xref: /core/sc/source/core/tool/interpr1.cxx (revision 2e1f9da8)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <interpre.hxx>
21 
22 #include <scitems.hxx>
23 #include <editeng/langitem.hxx>
24 #include <editeng/justifyitem.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <o3tl/temporary.hxx>
27 #include <osl/thread.h>
28 #include <unotools/textsearch.hxx>
29 #include <svl/numformat.hxx>
30 #include <svl/zforlist.hxx>
31 #include <svl/zformat.hxx>
32 #include <tools/urlobj.hxx>
33 #include <unotools/charclass.hxx>
34 #include <sfx2/docfile.hxx>
35 #include <sfx2/printer.hxx>
36 #include <unotools/collatorwrapper.hxx>
37 #include <unotools/transliterationwrapper.hxx>
38 #include <rtl/character.hxx>
39 #include <rtl/ustring.hxx>
40 #include <sal/log.hxx>
41 #include <osl/diagnose.h>
42 #include <unicode/uchar.h>
43 #include <unicode/regex.h>
44 #include <i18nlangtag/mslangid.hxx>
45 
46 #include <patattr.hxx>
47 #include <global.hxx>
48 #include <document.hxx>
49 #include <dociter.hxx>
50 #include <docsh.hxx>
51 #include <formulacell.hxx>
52 #include <scmatrix.hxx>
53 #include <docoptio.hxx>
54 #include <attrib.hxx>
55 #include <jumpmatrix.hxx>
56 #include <cellkeytranslator.hxx>
57 #include <lookupcache.hxx>
58 #include <rangenam.hxx>
59 #include <rangeutl.hxx>
60 #include <compiler.hxx>
61 #include <externalrefmgr.hxx>
62 #include <doubleref.hxx>
63 #include <queryparam.hxx>
64 #include <queryentry.hxx>
65 #include <queryiter.hxx>
66 #include <tokenarray.hxx>
67 #include <compare.hxx>
68 
69 #include <comphelper/processfactory.hxx>
70 #include <comphelper/random.hxx>
71 #include <comphelper/string.hxx>
72 #include <svl/sharedstringpool.hxx>
73 
74 #include <stdlib.h>
75 #include <vector>
76 #include <memory>
77 #include <limits>
78 #include <string_view>
79 #include <cmath>
80 
81 const sal_uInt64 n2power48 = SAL_CONST_UINT64( 281474976710656); // 2^48
82 
83 ScCalcConfig *ScInterpreter::mpGlobalConfig = nullptr;
84 
85 using namespace formula;
86 using ::std::unique_ptr;
87 
88 void ScInterpreter::ScIfJump()
89 {
90     const short* pJump = pCur->GetJump();
91     short nJumpCount = pJump[ 0 ];
92     MatrixJumpConditionToMatrix();
93     switch ( GetStackType() )
94     {
95         case svMatrix:
96         {
97             ScMatrixRef pMat = PopMatrix();
98             if ( !pMat )
99                 PushIllegalParameter();
100             else
101             {
102                 FormulaConstTokenRef xNew;
103                 ScTokenMatrixMap::const_iterator aMapIter;
104                 // DoubleError handled by JumpMatrix
105                 pMat->SetErrorInterpreter( nullptr);
106                 SCSIZE nCols, nRows;
107                 pMat->GetDimensions( nCols, nRows );
108                 if ( nCols == 0 || nRows == 0 )
109                 {
110                     PushIllegalArgument();
111                     return;
112                 }
113                 else if ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end())
114                     xNew = (*aMapIter).second;
115                 else
116                 {
117                     std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
118                                 pCur->GetOpCode(), nCols, nRows));
119                     for ( SCSIZE nC=0; nC < nCols; ++nC )
120                     {
121                         for ( SCSIZE nR=0; nR < nRows; ++nR )
122                         {
123                             double fVal;
124                             bool bTrue;
125                             bool bIsValue = pMat->IsValue(nC, nR);
126                             if (bIsValue)
127                             {
128                                 fVal = pMat->GetDouble(nC, nR);
129                                 bIsValue = std::isfinite(fVal);
130                                 bTrue = bIsValue && (fVal != 0.0);
131                                 if (bTrue)
132                                     fVal = 1.0;
133                             }
134                             else
135                             {
136                                 // Treat empty and empty path as 0, but string
137                                 // as error. ScMatrix::IsValueOrEmpty() returns
138                                 // true for any empty, empty path, empty cell,
139                                 // empty result.
140                                 bIsValue = pMat->IsValueOrEmpty(nC, nR);
141                                 bTrue = false;
142                                 fVal = (bIsValue ? 0.0 : CreateDoubleError( FormulaError::NoValue));
143                             }
144                             if ( bTrue )
145                             {   // TRUE
146                                 if( nJumpCount >= 2 )
147                                 {   // THEN path
148                                     pJumpMat->SetJump( nC, nR, fVal,
149                                             pJump[ 1 ],
150                                             pJump[ nJumpCount ]);
151                                 }
152                                 else
153                                 {   // no parameter given for THEN
154                                     pJumpMat->SetJump( nC, nR, fVal,
155                                             pJump[ nJumpCount ],
156                                             pJump[ nJumpCount ]);
157                                 }
158                             }
159                             else
160                             {   // FALSE
161                                 if( nJumpCount == 3 && bIsValue )
162                                 {   // ELSE path
163                                     pJumpMat->SetJump( nC, nR, fVal,
164                                             pJump[ 2 ],
165                                             pJump[ nJumpCount ]);
166                                 }
167                                 else
168                                 {   // no parameter given for ELSE,
169                                     // or DoubleError
170                                     pJumpMat->SetJump( nC, nR, fVal,
171                                             pJump[ nJumpCount ],
172                                             pJump[ nJumpCount ]);
173                                 }
174                             }
175                         }
176                     }
177                     xNew = new ScJumpMatrixToken( pJumpMat );
178                     GetTokenMatrixMap().emplace(pCur, xNew);
179                 }
180                 if (!xNew)
181                 {
182                     PushIllegalArgument();
183                     return;
184                 }
185                 PushTokenRef( xNew);
186                 // set endpoint of path for main code line
187                 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
188             }
189         }
190         break;
191         default:
192         {
193             const bool bCondition = GetBool();
194             if (nGlobalError != FormulaError::NONE)
195             {   // Propagate error, not THEN- or ELSE-path, jump behind.
196                 PushError(nGlobalError);
197                 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
198             }
199             else if ( bCondition )
200             {   // TRUE
201                 if( nJumpCount >= 2 )
202                 {   // THEN path
203                     aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
204                 }
205                 else
206                 {   // no parameter given for THEN
207                     nFuncFmtType = SvNumFormatType::LOGICAL;
208                     PushInt(1);
209                     aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
210                 }
211             }
212             else
213             {   // FALSE
214                 if( nJumpCount == 3 )
215                 {   // ELSE path
216                     aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] );
217                 }
218                 else
219                 {   // no parameter given for ELSE
220                     nFuncFmtType = SvNumFormatType::LOGICAL;
221                     PushInt(0);
222                     aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
223                 }
224             }
225         }
226     }
227 }
228 
229 /** Store a matrix value in another matrix in the context of that other matrix
230     is the result matrix of a jump matrix. All arguments must be valid and are
231     not checked. */
232 static void lcl_storeJumpMatResult(
233     const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR )
234 {
235     if ( pMat->IsValue( nC, nR ) )
236     {
237         double fVal = pMat->GetDouble( nC, nR );
238         pJumpMat->PutResultDouble( fVal, nC, nR );
239     }
240     else if ( pMat->IsEmpty( nC, nR ) )
241     {
242         pJumpMat->PutResultEmpty( nC, nR );
243     }
244     else
245     {
246         pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR);
247     }
248 }
249 
250 void ScInterpreter::ScIfError( bool bNAonly )
251 {
252     const short* pJump = pCur->GetJump();
253     short nJumpCount = pJump[ 0 ];
254     if (!sp || nJumpCount != 2)
255     {
256         // Reset nGlobalError here to not propagate the old error, if any.
257         nGlobalError = (sp ? FormulaError::ParameterExpected : FormulaError::UnknownStackVariable);
258         PushError( nGlobalError);
259         aCode.Jump( pJump[ nJumpCount  ], pJump[ nJumpCount ] );
260         return;
261     }
262 
263     FormulaConstTokenRef xToken( pStack[ sp - 1 ] );
264     bool bError = false;
265     FormulaError nOldGlobalError = nGlobalError;
266     nGlobalError = FormulaError::NONE;
267 
268     MatrixJumpConditionToMatrix();
269     switch (GetStackType())
270     {
271         default:
272             Pop();
273             // Act on implicitly propagated error, if any.
274             if (nOldGlobalError != FormulaError::NONE)
275                 nGlobalError = nOldGlobalError;
276             if (nGlobalError != FormulaError::NONE)
277                 bError = true;
278             break;
279         case svError:
280             PopError();
281             bError = true;
282             break;
283         case svDoubleRef:
284         case svSingleRef:
285             {
286                 ScAddress aAdr;
287                 if (!PopDoubleRefOrSingleRef( aAdr))
288                     bError = true;
289                 else
290                 {
291 
292                     ScRefCellValue aCell(mrDoc, aAdr);
293                     nGlobalError = GetCellErrCode(aCell);
294                     if (nGlobalError != FormulaError::NONE)
295                         bError = true;
296                 }
297             }
298             break;
299         case svExternalSingleRef:
300         case svExternalDoubleRef:
301         {
302             double fVal;
303             svl::SharedString aStr;
304             // Handles also existing jump matrix case and sets error on
305             // elements.
306             GetDoubleOrStringFromMatrix( fVal, aStr);
307             if (nGlobalError != FormulaError::NONE)
308                 bError = true;
309         }
310         break;
311         case svMatrix:
312             {
313                 const ScMatrixRef pMat = PopMatrix();
314                 if (!pMat || (nGlobalError != FormulaError::NONE && (!bNAonly || nGlobalError == FormulaError::NotAvailable)))
315                 {
316                     bError = true;
317                     break;  // switch
318                 }
319                 // If the matrix has no queried error at all we can simply use
320                 // it as result and don't need to bother with jump matrix.
321                 SCSIZE nErrorCol = ::std::numeric_limits<SCSIZE>::max(),
322                        nErrorRow = ::std::numeric_limits<SCSIZE>::max();
323                 SCSIZE nCols, nRows;
324                 pMat->GetDimensions( nCols, nRows );
325                 if (nCols == 0 || nRows == 0)
326                 {
327                     bError = true;
328                     break;  // switch
329                 }
330                 for (SCSIZE nC=0; nC < nCols && !bError; ++nC)
331                 {
332                     for (SCSIZE nR=0; nR < nRows && !bError; ++nR)
333                     {
334                         FormulaError nErr = pMat->GetError( nC, nR );
335                         if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
336                         {
337                             bError = true;
338                             nErrorCol = nC;
339                             nErrorRow = nR;
340                         }
341                     }
342                 }
343                 if (!bError)
344                     break;  // switch, we're done and have the result
345 
346                 FormulaConstTokenRef xNew;
347                 ScTokenMatrixMap::const_iterator aMapIter;
348                 if ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end())
349                 {
350                     xNew = (*aMapIter).second;
351                 }
352                 else
353                 {
354                     const ScMatrix* pMatPtr = pMat.get();
355                     std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
356                                 pCur->GetOpCode(), nCols, nRows));
357                     // Init all jumps to no error to save single calls. Error
358                     // is the exceptional condition.
359                     const double fFlagResult = CreateDoubleError( FormulaError::JumpMatHasResult);
360                     pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] );
361                     // Up to first error position simply store results, no need
362                     // to evaluate error conditions again.
363                     SCSIZE nC = 0, nR = 0;
364                     for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ )
365                     {
366                         for (nR = 0 ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR)
367                         {
368                             lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
369                         }
370                         if (nC != nErrorCol && nR != nErrorRow)
371                             ++nC;
372                     }
373                     // Now the mixed cases.
374                     for ( ; nC < nCols; ++nC)
375                     {
376                         for ( ; nR < nRows; ++nR)
377                         {
378                             FormulaError nErr = pMat->GetError( nC, nR );
379                             if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
380                             {   // TRUE, THEN path
381                                 pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] );
382                             }
383                             else
384                             {   // FALSE, EMPTY path, store result instead
385                                 lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
386                             }
387                         }
388                         nR = 0;
389                     }
390                     xNew = new ScJumpMatrixToken( pJumpMat );
391                     GetTokenMatrixMap().emplace( pCur, xNew );
392                 }
393                 nGlobalError = nOldGlobalError;
394                 PushTokenRef( xNew );
395                 // set endpoint of path for main code line
396                 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
397                 return;
398             }
399             break;
400     }
401 
402     if (bError && (!bNAonly || nGlobalError == FormulaError::NotAvailable))
403     {
404         // error, calculate 2nd argument
405         nGlobalError = FormulaError::NONE;
406         aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
407     }
408     else
409     {
410         // no error, push 1st argument and continue
411         nGlobalError = nOldGlobalError;
412         PushTokenRef( xToken);
413         aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
414     }
415 }
416 
417 void ScInterpreter::ScChooseJump()
418 {
419     // We have to set a jump, if there was none chosen because of an error set
420     // it to endpoint.
421     bool bHaveJump = false;
422     const short* pJump = pCur->GetJump();
423     short nJumpCount = pJump[ 0 ];
424     MatrixJumpConditionToMatrix();
425     switch ( GetStackType() )
426     {
427         case svMatrix:
428         {
429             ScMatrixRef pMat = PopMatrix();
430             if ( !pMat )
431                 PushIllegalParameter();
432             else
433             {
434                 FormulaConstTokenRef xNew;
435                 ScTokenMatrixMap::const_iterator aMapIter;
436                 // DoubleError handled by JumpMatrix
437                 pMat->SetErrorInterpreter( nullptr);
438                 SCSIZE nCols, nRows;
439                 pMat->GetDimensions( nCols, nRows );
440                 if ( nCols == 0 || nRows == 0 )
441                     PushIllegalParameter();
442                 else if ((aMapIter = maTokenMatrixMap.find(
443                                     pCur)) != maTokenMatrixMap.end())
444                     xNew = (*aMapIter).second;
445                 else
446                 {
447                     std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
448                                 pCur->GetOpCode(), nCols, nRows));
449                     for ( SCSIZE nC=0; nC < nCols; ++nC )
450                     {
451                         for ( SCSIZE nR=0; nR < nRows; ++nR )
452                         {
453                             double fVal;
454                             bool bIsValue = pMat->IsValue(nC, nR);
455                             if ( bIsValue )
456                             {
457                                 fVal = pMat->GetDouble(nC, nR);
458                                 bIsValue = std::isfinite( fVal );
459                                 if ( bIsValue )
460                                 {
461                                     fVal = ::rtl::math::approxFloor( fVal);
462                                     if ( (fVal < 1) || (fVal >= nJumpCount))
463                                     {
464                                         bIsValue = false;
465                                         fVal = CreateDoubleError(
466                                                 FormulaError::IllegalArgument);
467                                     }
468                                 }
469                             }
470                             else
471                             {
472                                 fVal = CreateDoubleError( FormulaError::NoValue);
473                             }
474                             if ( bIsValue )
475                             {
476                                 pJumpMat->SetJump( nC, nR, fVal,
477                                         pJump[ static_cast<short>(fVal) ],
478                                         pJump[ nJumpCount ]);
479                             }
480                             else
481                             {
482                                 pJumpMat->SetJump( nC, nR, fVal,
483                                         pJump[ nJumpCount ],
484                                         pJump[ nJumpCount ]);
485                             }
486                         }
487                     }
488                     xNew = new ScJumpMatrixToken( pJumpMat );
489                     GetTokenMatrixMap().emplace(pCur, xNew);
490                 }
491                 if (xNew)
492                 {
493                     PushTokenRef( xNew);
494                     // set endpoint of path for main code line
495                     aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
496                     bHaveJump = true;
497                 }
498             }
499         }
500         break;
501         default:
502         {
503             sal_Int16 nJumpIndex = GetInt16();
504             if (nGlobalError == FormulaError::NONE && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount))
505             {
506                 aCode.Jump( pJump[ static_cast<short>(nJumpIndex) ], pJump[ nJumpCount ] );
507                 bHaveJump = true;
508             }
509             else
510                 PushIllegalArgument();
511         }
512     }
513     if (!bHaveJump)
514         aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
515 }
516 
517 static void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, SCSIZE nParmCols, SCSIZE nParmRows )
518 {
519     SCSIZE nJumpCols, nJumpRows;
520     SCSIZE nResCols, nResRows;
521     SCSIZE nAdjustCols, nAdjustRows;
522     pJumpM->GetDimensions( nJumpCols, nJumpRows );
523     pJumpM->GetResMatDimensions( nResCols, nResRows );
524     if (!(( nJumpCols == 1 && nParmCols > nResCols ) ||
525         ( nJumpRows == 1 && nParmRows > nResRows )))
526         return;
527 
528     if ( nJumpCols == 1 && nJumpRows == 1 )
529     {
530         nAdjustCols = std::max(nParmCols, nResCols);
531         nAdjustRows = std::max(nParmRows, nResRows);
532     }
533     else if ( nJumpCols == 1 )
534     {
535         nAdjustCols = nParmCols;
536         nAdjustRows = nResRows;
537     }
538     else
539     {
540         nAdjustCols = nResCols;
541         nAdjustRows = nParmRows;
542     }
543     pJumpM->SetNewResMat( nAdjustCols, nAdjustRows );
544 }
545 
546 bool ScInterpreter::JumpMatrix( short nStackLevel )
547 {
548     pJumpMatrix = pStack[sp-nStackLevel]->GetJumpMatrix();
549     bool bHasResMat = pJumpMatrix->HasResultMatrix();
550     SCSIZE nC, nR;
551     if ( nStackLevel == 2 )
552     {
553         if ( aCode.HasStacked() )
554             aCode.Pop();    // pop what Jump() pushed
555         else
556         {
557             assert(!"pop goes the weasel");
558         }
559 
560         if ( !bHasResMat )
561         {
562             Pop();
563             SetError( FormulaError::UnknownStackVariable );
564         }
565         else
566         {
567             pJumpMatrix->GetPos( nC, nR );
568             switch ( GetStackType() )
569             {
570                 case svDouble:
571                 {
572                     double fVal = GetDouble();
573                     if ( nGlobalError != FormulaError::NONE )
574                     {
575                         fVal = CreateDoubleError( nGlobalError );
576                         nGlobalError = FormulaError::NONE;
577                     }
578                     pJumpMatrix->PutResultDouble( fVal, nC, nR );
579                 }
580                 break;
581                 case svString:
582                 {
583                     svl::SharedString aStr = GetString();
584                     if ( nGlobalError != FormulaError::NONE )
585                     {
586                         pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
587                                 nC, nR);
588                         nGlobalError = FormulaError::NONE;
589                     }
590                     else
591                         pJumpMatrix->PutResultString(aStr, nC, nR);
592                 }
593                 break;
594                 case svSingleRef:
595                 {
596                     FormulaConstTokenRef xRef = pStack[sp-1];
597                     ScAddress aAdr;
598                     PopSingleRef( aAdr );
599                     if ( nGlobalError != FormulaError::NONE )
600                     {
601                         pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
602                                 nC, nR);
603                         nGlobalError = FormulaError::NONE;
604                     }
605                     else
606                     {
607                         ScRefCellValue aCell(mrDoc, aAdr);
608                         if (aCell.hasEmptyValue())
609                             pJumpMatrix->PutResultEmpty( nC, nR );
610                         else if (aCell.hasNumeric())
611                         {
612                             double fVal = GetCellValue(aAdr, aCell);
613                             if ( nGlobalError != FormulaError::NONE )
614                             {
615                                 fVal = CreateDoubleError(
616                                         nGlobalError);
617                                 nGlobalError = FormulaError::NONE;
618                             }
619                             pJumpMatrix->PutResultDouble( fVal, nC, nR );
620                         }
621                         else
622                         {
623                             svl::SharedString aStr;
624                             GetCellString(aStr, aCell);
625                             if ( nGlobalError != FormulaError::NONE )
626                             {
627                                 pJumpMatrix->PutResultDouble( CreateDoubleError(
628                                             nGlobalError), nC, nR);
629                                 nGlobalError = FormulaError::NONE;
630                             }
631                             else
632                                 pJumpMatrix->PutResultString(aStr, nC, nR);
633                         }
634                     }
635 
636                     formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16);
637                     if (eReturnType == ParamClass::Reference)
638                     {
639                         /* TODO: What about error handling and do we actually
640                          * need the result matrix above at all in this case? */
641                         ScComplexRefData aRef;
642                         aRef.Ref1 = aRef.Ref2 = *(xRef->GetSingleRef());
643                         pJumpMatrix->GetRefList().push_back( aRef);
644                     }
645                 }
646                 break;
647                 case svDoubleRef:
648                 {   // upper left plus offset within matrix
649                     FormulaConstTokenRef xRef = pStack[sp-1];
650                     double fVal;
651                     ScRange aRange;
652                     PopDoubleRef( aRange );
653                     if ( nGlobalError != FormulaError::NONE )
654                     {
655                         fVal = CreateDoubleError( nGlobalError );
656                         nGlobalError = FormulaError::NONE;
657                         pJumpMatrix->PutResultDouble( fVal, nC, nR );
658                     }
659                     else
660                     {
661                         // Do not modify the original range because we use it
662                         // to adjust the size of the result matrix if necessary.
663                         ScAddress aAdr( aRange.aStart);
664                         sal_uLong nCol = static_cast<sal_uLong>(aAdr.Col()) + nC;
665                         sal_uLong nRow = static_cast<sal_uLong>(aAdr.Row()) + nR;
666                         if ((nCol > o3tl::make_unsigned(aRange.aEnd.Col()) &&
667                                     aRange.aEnd.Col() != aRange.aStart.Col())
668                                 || (nRow > o3tl::make_unsigned(aRange.aEnd.Row()) &&
669                                     aRange.aEnd.Row() != aRange.aStart.Row()))
670                         {
671                             fVal = CreateDoubleError( FormulaError::NotAvailable );
672                             pJumpMatrix->PutResultDouble( fVal, nC, nR );
673                         }
674                         else
675                         {
676                             // Replicate column and/or row of a vector if it is
677                             // one. Note that this could be a range reference
678                             // that in fact consists of only one cell, e.g. A1:A1
679                             if (aRange.aEnd.Col() == aRange.aStart.Col())
680                                 nCol = aRange.aStart.Col();
681                             if (aRange.aEnd.Row() == aRange.aStart.Row())
682                                 nRow = aRange.aStart.Row();
683                             aAdr.SetCol( static_cast<SCCOL>(nCol) );
684                             aAdr.SetRow( static_cast<SCROW>(nRow) );
685                             ScRefCellValue aCell(mrDoc, aAdr);
686                             if (aCell.hasEmptyValue())
687                                 pJumpMatrix->PutResultEmpty( nC, nR );
688                             else if (aCell.hasNumeric())
689                             {
690                                 double fCellVal = GetCellValue(aAdr, aCell);
691                                 if ( nGlobalError != FormulaError::NONE )
692                                 {
693                                     fCellVal = CreateDoubleError(
694                                             nGlobalError);
695                                     nGlobalError = FormulaError::NONE;
696                                 }
697                                 pJumpMatrix->PutResultDouble( fCellVal, nC, nR );
698                             }
699                             else
700                             {
701                                 svl::SharedString aStr;
702                                 GetCellString(aStr, aCell);
703                                 if ( nGlobalError != FormulaError::NONE )
704                                 {
705                                     pJumpMatrix->PutResultDouble( CreateDoubleError(
706                                                 nGlobalError), nC, nR);
707                                     nGlobalError = FormulaError::NONE;
708                                 }
709                                 else
710                                     pJumpMatrix->PutResultString(aStr, nC, nR);
711                             }
712                         }
713                         SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
714                         SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
715                         lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows );
716                     }
717 
718                     formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16);
719                     if (eReturnType == ParamClass::Reference)
720                     {
721                         /* TODO: What about error handling and do we actually
722                          * need the result matrix above at all in this case? */
723                         pJumpMatrix->GetRefList().push_back( *(xRef->GetDoubleRef()));
724                     }
725                 }
726                 break;
727                 case svExternalSingleRef:
728                 {
729                     ScExternalRefCache::TokenRef pToken;
730                     PopExternalSingleRef(pToken);
731                     if (nGlobalError != FormulaError::NONE)
732                     {
733                         pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), nC, nR );
734                         nGlobalError = FormulaError::NONE;
735                     }
736                     else
737                     {
738                         switch (pToken->GetType())
739                         {
740                             case svDouble:
741                                 pJumpMatrix->PutResultDouble( pToken->GetDouble(), nC, nR );
742                             break;
743                             case svString:
744                                 pJumpMatrix->PutResultString( pToken->GetString(), nC, nR );
745                             break;
746                             case svEmptyCell:
747                                 pJumpMatrix->PutResultEmpty( nC, nR );
748                             break;
749                             default:
750                                 // svError was already handled (set by
751                                 // PopExternalSingleRef()) with nGlobalError
752                                 // above.
753                                 assert(!"unhandled svExternalSingleRef case");
754                                 pJumpMatrix->PutResultDouble( CreateDoubleError(
755                                             FormulaError::UnknownStackVariable), nC, nR );
756                         }
757                     }
758                 }
759                 break;
760                 case svExternalDoubleRef:
761                 case svMatrix:
762                 {   // match matrix offsets
763                     double fVal;
764                     ScMatrixRef pMat = GetMatrix();
765                     if ( nGlobalError != FormulaError::NONE )
766                     {
767                         fVal = CreateDoubleError( nGlobalError );
768                         nGlobalError = FormulaError::NONE;
769                         pJumpMatrix->PutResultDouble( fVal, nC, nR );
770                     }
771                     else if ( !pMat )
772                     {
773                         fVal = CreateDoubleError( FormulaError::UnknownVariable );
774                         pJumpMatrix->PutResultDouble( fVal, nC, nR );
775                     }
776                     else
777                     {
778                         SCSIZE nCols, nRows;
779                         pMat->GetDimensions( nCols, nRows );
780                         if ((nCols <= nC && nCols != 1) ||
781                             (nRows <= nR && nRows != 1))
782                         {
783                             fVal = CreateDoubleError( FormulaError::NotAvailable );
784                             pJumpMatrix->PutResultDouble( fVal, nC, nR );
785                         }
786                         else
787                         {
788                             // GetMatrix() does SetErrorInterpreter() at the
789                             // matrix, do not propagate an error from
790                             // matrix->GetValue() as global error.
791                             pMat->SetErrorInterpreter(nullptr);
792                             lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR);
793                         }
794                         lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows );
795                     }
796                 }
797                 break;
798                 case svError:
799                 {
800                     PopError();
801                     double fVal = CreateDoubleError( nGlobalError);
802                     nGlobalError = FormulaError::NONE;
803                     pJumpMatrix->PutResultDouble( fVal, nC, nR );
804                 }
805                 break;
806                 default:
807                 {
808                     Pop();
809                     double fVal = CreateDoubleError( FormulaError::IllegalArgument);
810                     pJumpMatrix->PutResultDouble( fVal, nC, nR );
811                 }
812             }
813         }
814     }
815     bool bCont = pJumpMatrix->Next( nC, nR );
816     if ( bCont )
817     {
818         double fBool;
819         short nStart, nNext, nStop;
820         pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
821         while ( bCont && nStart == nNext )
822         {   // push all results that have no jump path
823             if ( bHasResMat && (GetDoubleErrorValue( fBool) != FormulaError::JumpMatHasResult) )
824             {
825                 // a false without path results in an empty path value
826                 if ( fBool == 0.0 )
827                     pJumpMatrix->PutResultEmptyPath( nC, nR );
828                 else
829                     pJumpMatrix->PutResultDouble( fBool, nC, nR );
830             }
831             bCont = pJumpMatrix->Next( nC, nR );
832             if ( bCont )
833                 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
834         }
835         if ( bCont && nStart != nNext )
836         {
837             const ScTokenVec & rParams = pJumpMatrix->GetJumpParameters();
838             for ( auto const & i : rParams )
839             {
840                 // This is not the current state of the interpreter, so
841                 // push without error, and elements' errors are coded into
842                 // double.
843                 PushWithoutError(*i);
844             }
845             aCode.Jump( nStart, nNext, nStop );
846         }
847     }
848     if ( !bCont )
849     {   // We're done with it, throw away jump matrix, keep result.
850         // For an intermediate result of Reference use the array of references
851         // if there are more than one reference and the current ForceArray
852         // context is ReferenceOrRefArray.
853         // Else (also for a final result of Reference) use the matrix.
854         // Treat the result of a jump command as final and use the matrix (see
855         // tdf#115493 for why).
856         if (pCur->GetInForceArray() == ParamClass::ReferenceOrRefArray &&
857                 pJumpMatrix->GetRefList().size() > 1 &&
858                 ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16) == ParamClass::Reference &&
859                 !FormulaCompiler::IsOpCodeJumpCommand( pJumpMatrix->GetOpCode()) &&
860                 aCode.PeekNextOperator())
861         {
862             FormulaTokenRef xRef = new ScRefListToken(true);
863             *(xRef->GetRefList()) = pJumpMatrix->GetRefList();
864             pJumpMatrix = nullptr;
865             Pop();
866             PushTokenRef( xRef);
867             maTokenMatrixMap.erase( pCur);
868             // There's no result matrix to remember in this case.
869         }
870         else
871         {
872             ScMatrix* pResMat = pJumpMatrix->GetResultMatrix();
873             pJumpMatrix = nullptr;
874             Pop();
875             PushMatrix( pResMat );
876             // Remove jump matrix from map and remember result matrix in case it
877             // could be reused in another path of the same condition.
878             maTokenMatrixMap.erase( pCur);
879             maTokenMatrixMap.emplace(pCur, pStack[sp-1]);
880         }
881         return true;
882     }
883     return false;
884 }
885 
886 double ScInterpreter::Compare( ScQueryOp eOp )
887 {
888     sc::Compare aComp;
889     aComp.meOp = eOp;
890     aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
891     for( short i = 1; i >= 0; i-- )
892     {
893         sc::Compare::Cell& rCell = aComp.maCells[i];
894 
895         switch ( GetRawStackType() )
896         {
897             case svEmptyCell:
898                 Pop();
899                 rCell.mbEmpty = true;
900                 break;
901             case svMissing:
902             case svDouble:
903                 rCell.mfValue = GetDouble();
904                 rCell.mbValue = true;
905                 break;
906             case svString:
907                 rCell.maStr = GetString();
908                 rCell.mbValue = false;
909                 break;
910             case svDoubleRef :
911             case svSingleRef :
912             {
913                 ScAddress aAdr;
914                 if ( !PopDoubleRefOrSingleRef( aAdr ) )
915                     break;
916                 ScRefCellValue aCell(mrDoc, aAdr);
917                 if (aCell.hasEmptyValue())
918                     rCell.mbEmpty = true;
919                 else if (aCell.hasString())
920                 {
921                     svl::SharedString aStr;
922                     GetCellString(aStr, aCell);
923                     rCell.maStr = aStr;
924                     rCell.mbValue = false;
925                 }
926                 else
927                 {
928                     rCell.mfValue = GetCellValue(aAdr, aCell);
929                     rCell.mbValue = true;
930                 }
931             }
932             break;
933             case svExternalSingleRef:
934             {
935                 ScMatrixRef pMat = GetMatrix();
936                 if (!pMat)
937                 {
938                     SetError( FormulaError::IllegalParameter);
939                     break;
940                 }
941 
942                 SCSIZE nC, nR;
943                 pMat->GetDimensions(nC, nR);
944                 if (!nC || !nR)
945                 {
946                     SetError( FormulaError::IllegalParameter);
947                     break;
948                 }
949                 if (pMat->IsEmpty(0, 0))
950                     rCell.mbEmpty = true;
951                 else if (pMat->IsStringOrEmpty(0, 0))
952                 {
953                     rCell.maStr = pMat->GetString(0, 0);
954                     rCell.mbValue = false;
955                 }
956                 else
957                 {
958                     rCell.mfValue = pMat->GetDouble(0, 0);
959                     rCell.mbValue = true;
960                 }
961             }
962             break;
963             case svExternalDoubleRef:
964                 // TODO: Find out how to handle this...
965                 // Xcl generates a position dependent intersection using
966                 // col/row, as it seems to do for all range references, not
967                 // only in compare context. We'd need a general implementation
968                 // for that behavior similar to svDoubleRef in scalar and array
969                 // mode. Which also means we'd have to change all places where
970                 // it currently is handled along with svMatrix.
971             default:
972                 PopError();
973                 SetError( FormulaError::IllegalParameter);
974             break;
975         }
976     }
977     if( nGlobalError != FormulaError::NONE )
978         return 0;
979     nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
980     return sc::CompareFunc(aComp);
981 }
982 
983 sc::RangeMatrix ScInterpreter::CompareMat( ScQueryOp eOp, sc::CompareOptions* pOptions )
984 {
985     sc::Compare aComp;
986     aComp.meOp = eOp;
987     aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
988     sc::RangeMatrix aMat[2];
989     ScAddress aAdr;
990     for( short i = 1; i >= 0; i-- )
991     {
992         sc::Compare::Cell& rCell = aComp.maCells[i];
993 
994         switch (GetRawStackType())
995         {
996             case svEmptyCell:
997                 Pop();
998                 rCell.mbEmpty = true;
999                 break;
1000             case svMissing:
1001             case svDouble:
1002                 rCell.mfValue = GetDouble();
1003                 rCell.mbValue = true;
1004                 break;
1005             case svString:
1006                 rCell.maStr = GetString();
1007                 rCell.mbValue = false;
1008                 break;
1009             case svSingleRef:
1010             {
1011                 PopSingleRef( aAdr );
1012                 ScRefCellValue aCell(mrDoc, aAdr);
1013                 if (aCell.hasEmptyValue())
1014                     rCell.mbEmpty = true;
1015                 else if (aCell.hasString())
1016                 {
1017                     svl::SharedString aStr;
1018                     GetCellString(aStr, aCell);
1019                     rCell.maStr = aStr;
1020                     rCell.mbValue = false;
1021                 }
1022                 else
1023                 {
1024                     rCell.mfValue = GetCellValue(aAdr, aCell);
1025                     rCell.mbValue = true;
1026                 }
1027             }
1028             break;
1029             case svExternalSingleRef:
1030             case svExternalDoubleRef:
1031             case svDoubleRef:
1032             case svMatrix:
1033                 aMat[i] = GetRangeMatrix();
1034                 if (!aMat[i].mpMat)
1035                     SetError( FormulaError::IllegalParameter);
1036                 else
1037                     aMat[i].mpMat->SetErrorInterpreter(nullptr);
1038                     // errors are transported as DoubleError inside matrix
1039                 break;
1040             default:
1041                 PopError();
1042                 SetError( FormulaError::IllegalParameter);
1043             break;
1044         }
1045     }
1046 
1047     sc::RangeMatrix aRes;
1048 
1049     if (nGlobalError != FormulaError::NONE)
1050     {
1051         nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
1052         return aRes;
1053     }
1054 
1055     if (aMat[0].mpMat && aMat[1].mpMat)
1056     {
1057         SCSIZE nC0, nC1;
1058         SCSIZE nR0, nR1;
1059         aMat[0].mpMat->GetDimensions(nC0, nR0);
1060         aMat[1].mpMat->GetDimensions(nC1, nR1);
1061         SCSIZE nC = std::max( nC0, nC1 );
1062         SCSIZE nR = std::max( nR0, nR1 );
1063         aRes.mpMat = GetNewMat( nC, nR, /*bEmpty*/true );
1064         if (!aRes.mpMat)
1065             return aRes;
1066         for ( SCSIZE j=0; j<nC; j++ )
1067         {
1068             for ( SCSIZE k=0; k<nR; k++ )
1069             {
1070                 SCSIZE nCol = j, nRow = k;
1071                 if (aMat[0].mpMat->ValidColRowOrReplicated(nCol, nRow) &&
1072                     aMat[1].mpMat->ValidColRowOrReplicated(nCol, nRow))
1073                 {
1074                     for ( short i=1; i>=0; i-- )
1075                     {
1076                         sc::Compare::Cell& rCell = aComp.maCells[i];
1077 
1078                         if (aMat[i].mpMat->IsStringOrEmpty(j, k))
1079                         {
1080                             rCell.mbValue = false;
1081                             rCell.maStr = aMat[i].mpMat->GetString(j, k);
1082                             rCell.mbEmpty = aMat[i].mpMat->IsEmpty(j, k);
1083                         }
1084                         else
1085                         {
1086                             rCell.mbValue = true;
1087                             rCell.mfValue = aMat[i].mpMat->GetDouble(j, k);
1088                             rCell.mbEmpty = false;
1089                         }
1090                     }
1091                     aRes.mpMat->PutDouble( sc::CompareFunc( aComp, pOptions), j, k);
1092                 }
1093                 else
1094                     aRes.mpMat->PutError( FormulaError::NoValue, j, k);
1095             }
1096         }
1097 
1098         switch (eOp)
1099         {
1100             case SC_EQUAL:
1101                 aRes.mpMat->CompareEqual();
1102                 break;
1103             case SC_LESS:
1104                 aRes.mpMat->CompareLess();
1105                 break;
1106             case SC_GREATER:
1107                 aRes.mpMat->CompareGreater();
1108                 break;
1109             case SC_LESS_EQUAL:
1110                 aRes.mpMat->CompareLessEqual();
1111                 break;
1112             case SC_GREATER_EQUAL:
1113                 aRes.mpMat->CompareGreaterEqual();
1114                 break;
1115             case SC_NOT_EQUAL:
1116                 aRes.mpMat->CompareNotEqual();
1117                 break;
1118             default:
1119                 SAL_WARN("sc",  "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp));
1120                 aRes.mpMat.reset();
1121                 return aRes;
1122         }
1123     }
1124     else if (aMat[0].mpMat || aMat[1].mpMat)
1125     {
1126         size_t i = ( aMat[0].mpMat ? 0 : 1);
1127 
1128         aRes.mnCol1 = aMat[i].mnCol1;
1129         aRes.mnRow1 = aMat[i].mnRow1;
1130         aRes.mnTab1 = aMat[i].mnTab1;
1131         aRes.mnCol2 = aMat[i].mnCol2;
1132         aRes.mnRow2 = aMat[i].mnRow2;
1133         aRes.mnTab2 = aMat[i].mnTab2;
1134 
1135         ScMatrix& rMat = *aMat[i].mpMat;
1136         aRes.mpMat = rMat.CompareMatrix(aComp, i, pOptions);
1137         if (!aRes.mpMat)
1138             return aRes;
1139     }
1140 
1141     nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
1142     return aRes;
1143 }
1144 
1145 ScMatrixRef ScInterpreter::QueryMat( const ScMatrixRef& pMat, sc::CompareOptions& rOptions )
1146 {
1147     SvNumFormatType nSaveCurFmtType = nCurFmtType;
1148     SvNumFormatType nSaveFuncFmtType = nFuncFmtType;
1149     PushMatrix( pMat);
1150     const ScQueryEntry::Item& rItem = rOptions.aQueryEntry.GetQueryItem();
1151     if (rItem.meType == ScQueryEntry::ByString)
1152         PushString(rItem.maString.getString());
1153     else
1154         PushDouble(rItem.mfVal);
1155     ScMatrixRef pResultMatrix = CompareMat(rOptions.aQueryEntry.eOp, &rOptions).mpMat;
1156     nCurFmtType = nSaveCurFmtType;
1157     nFuncFmtType = nSaveFuncFmtType;
1158     if (nGlobalError != FormulaError::NONE || !pResultMatrix)
1159     {
1160         SetError( FormulaError::IllegalParameter);
1161         return pResultMatrix;
1162     }
1163 
1164     return pResultMatrix;
1165 }
1166 
1167 void ScInterpreter::ScEqual()
1168 {
1169     if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1170     {
1171         sc::RangeMatrix aMat = CompareMat(SC_EQUAL);
1172         if (!aMat.mpMat)
1173         {
1174             PushIllegalParameter();
1175             return;
1176         }
1177 
1178         PushMatrix(aMat);
1179     }
1180     else
1181         PushInt( int(Compare( SC_EQUAL) == 0) );
1182 }
1183 
1184 void ScInterpreter::ScNotEqual()
1185 {
1186     if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1187     {
1188         sc::RangeMatrix aMat = CompareMat(SC_NOT_EQUAL);
1189         if (!aMat.mpMat)
1190         {
1191             PushIllegalParameter();
1192             return;
1193         }
1194 
1195         PushMatrix(aMat);
1196     }
1197     else
1198         PushInt( int(Compare( SC_NOT_EQUAL) != 0) );
1199 }
1200 
1201 void ScInterpreter::ScLess()
1202 {
1203     if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1204     {
1205         sc::RangeMatrix aMat = CompareMat(SC_LESS);
1206         if (!aMat.mpMat)
1207         {
1208             PushIllegalParameter();
1209             return;
1210         }
1211 
1212         PushMatrix(aMat);
1213     }
1214     else
1215         PushInt( int(Compare( SC_LESS) < 0) );
1216 }
1217 
1218 void ScInterpreter::ScGreater()
1219 {
1220     if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1221     {
1222         sc::RangeMatrix aMat = CompareMat(SC_GREATER);
1223         if (!aMat.mpMat)
1224         {
1225             PushIllegalParameter();
1226             return;
1227         }
1228 
1229         PushMatrix(aMat);
1230     }
1231     else
1232         PushInt( int(Compare( SC_GREATER) > 0) );
1233 }
1234 
1235 void ScInterpreter::ScLessEqual()
1236 {
1237     if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1238     {
1239         sc::RangeMatrix aMat = CompareMat(SC_LESS_EQUAL);
1240         if (!aMat.mpMat)
1241         {
1242             PushIllegalParameter();
1243             return;
1244         }
1245 
1246         PushMatrix(aMat);
1247     }
1248     else
1249         PushInt( int(Compare( SC_LESS_EQUAL) <= 0) );
1250 }
1251 
1252 void ScInterpreter::ScGreaterEqual()
1253 {
1254     if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1255     {
1256         sc::RangeMatrix aMat = CompareMat(SC_GREATER_EQUAL);
1257         if (!aMat.mpMat)
1258         {
1259             PushIllegalParameter();
1260             return;
1261         }
1262 
1263         PushMatrix(aMat);
1264     }
1265     else
1266         PushInt( int(Compare( SC_GREATER_EQUAL) >= 0) );
1267 }
1268 
1269 void ScInterpreter::ScAnd()
1270 {
1271     nFuncFmtType = SvNumFormatType::LOGICAL;
1272     short nParamCount = GetByte();
1273     if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1274         return;
1275 
1276     bool bHaveValue = false;
1277     bool bRes = true;
1278     size_t nRefInList = 0;
1279     while( nParamCount-- > 0)
1280     {
1281         if ( nGlobalError == FormulaError::NONE )
1282         {
1283             switch ( GetStackType() )
1284             {
1285                 case svDouble :
1286                     bHaveValue = true;
1287                     bRes &= ( PopDouble() != 0.0 );
1288                 break;
1289                 case svString :
1290                     Pop();
1291                     SetError( FormulaError::NoValue );
1292                 break;
1293                 case svSingleRef :
1294                 {
1295                     ScAddress aAdr;
1296                     PopSingleRef( aAdr );
1297                     if ( nGlobalError == FormulaError::NONE )
1298                     {
1299                         ScRefCellValue aCell(mrDoc, aAdr);
1300                         if (aCell.hasNumeric())
1301                         {
1302                             bHaveValue = true;
1303                             bRes &= ( GetCellValue(aAdr, aCell) != 0.0 );
1304                         }
1305                         // else: Xcl raises no error here
1306                     }
1307                 }
1308                 break;
1309                 case svDoubleRef:
1310                 case svRefList:
1311                 {
1312                     ScRange aRange;
1313                     PopDoubleRef( aRange, nParamCount, nRefInList);
1314                     if ( nGlobalError == FormulaError::NONE )
1315                     {
1316                         double fVal;
1317                         FormulaError nErr = FormulaError::NONE;
1318                         ScValueIterator aValIter( mrContext, aRange );
1319                         if ( aValIter.GetFirst( fVal, nErr ) && nErr == FormulaError::NONE )
1320                         {
1321                             bHaveValue = true;
1322                             do
1323                             {
1324                                 bRes &= ( fVal != 0.0 );
1325                             } while ( (nErr == FormulaError::NONE) &&
1326                                 aValIter.GetNext( fVal, nErr ) );
1327                         }
1328                         SetError( nErr );
1329                     }
1330                 }
1331                 break;
1332                 case svExternalSingleRef:
1333                 case svExternalDoubleRef:
1334                 case svMatrix:
1335                 {
1336                     ScMatrixRef pMat = GetMatrix();
1337                     if ( pMat )
1338                     {
1339                         bHaveValue = true;
1340                         double fVal = pMat->And();
1341                         FormulaError nErr = GetDoubleErrorValue( fVal );
1342                         if ( nErr != FormulaError::NONE )
1343                         {
1344                             SetError( nErr );
1345                             bRes = false;
1346                         }
1347                         else
1348                             bRes &= (fVal != 0.0);
1349                     }
1350                     // else: GetMatrix did set FormulaError::IllegalParameter
1351                 }
1352                 break;
1353                 default:
1354                     PopError();
1355                     SetError( FormulaError::IllegalParameter);
1356             }
1357         }
1358         else
1359             Pop();
1360     }
1361     if ( bHaveValue )
1362         PushInt( int(bRes) );
1363     else
1364         PushNoValue();
1365 }
1366 
1367 void ScInterpreter::ScOr()
1368 {
1369     nFuncFmtType = SvNumFormatType::LOGICAL;
1370     short nParamCount = GetByte();
1371     if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1372         return;
1373 
1374     bool bHaveValue = false;
1375     bool bRes = false;
1376     size_t nRefInList = 0;
1377     while( nParamCount-- > 0)
1378     {
1379         if ( nGlobalError == FormulaError::NONE )
1380         {
1381             switch ( GetStackType() )
1382             {
1383                 case svDouble :
1384                     bHaveValue = true;
1385                     bRes |= ( PopDouble() != 0.0 );
1386                 break;
1387                 case svString :
1388                     Pop();
1389                     SetError( FormulaError::NoValue );
1390                 break;
1391                 case svSingleRef :
1392                 {
1393                     ScAddress aAdr;
1394                     PopSingleRef( aAdr );
1395                     if ( nGlobalError == FormulaError::NONE )
1396                     {
1397                         ScRefCellValue aCell(mrDoc, aAdr);
1398                         if (aCell.hasNumeric())
1399                         {
1400                             bHaveValue = true;
1401                             bRes |= ( GetCellValue(aAdr, aCell) != 0.0 );
1402                         }
1403                         // else: Xcl raises no error here
1404                     }
1405                 }
1406                 break;
1407                 case svDoubleRef:
1408                 case svRefList:
1409                 {
1410                     ScRange aRange;
1411                     PopDoubleRef( aRange, nParamCount, nRefInList);
1412                     if ( nGlobalError == FormulaError::NONE )
1413                     {
1414                         double fVal;
1415                         FormulaError nErr = FormulaError::NONE;
1416                         ScValueIterator aValIter( mrContext, aRange );
1417                         if ( aValIter.GetFirst( fVal, nErr ) )
1418                         {
1419                             bHaveValue = true;
1420                             do
1421                             {
1422                                 bRes |= ( fVal != 0.0 );
1423                             } while ( (nErr == FormulaError::NONE) &&
1424                                 aValIter.GetNext( fVal, nErr ) );
1425                         }
1426                         SetError( nErr );
1427                     }
1428                 }
1429                 break;
1430                 case svExternalSingleRef:
1431                 case svExternalDoubleRef:
1432                 case svMatrix:
1433                 {
1434                     bHaveValue = true;
1435                     ScMatrixRef pMat = GetMatrix();
1436                     if ( pMat )
1437                     {
1438                         bHaveValue = true;
1439                         double fVal = pMat->Or();
1440                         FormulaError nErr = GetDoubleErrorValue( fVal );
1441                         if ( nErr != FormulaError::NONE )
1442                         {
1443                             SetError( nErr );
1444                             bRes = false;
1445                         }
1446                         else
1447                             bRes |= (fVal != 0.0);
1448                     }
1449                     // else: GetMatrix did set FormulaError::IllegalParameter
1450                 }
1451                 break;
1452                 default:
1453                     PopError();
1454                     SetError( FormulaError::IllegalParameter);
1455             }
1456         }
1457         else
1458             Pop();
1459     }
1460     if ( bHaveValue )
1461         PushInt( int(bRes) );
1462     else
1463         PushNoValue();
1464 }
1465 
1466 void ScInterpreter::ScXor()
1467 {
1468 
1469     nFuncFmtType = SvNumFormatType::LOGICAL;
1470     short nParamCount = GetByte();
1471     if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1472         return;
1473 
1474     bool bHaveValue = false;
1475     bool bRes = false;
1476     size_t nRefInList = 0;
1477     while( nParamCount-- > 0)
1478     {
1479         if ( nGlobalError == FormulaError::NONE )
1480         {
1481             switch ( GetStackType() )
1482             {
1483                 case svDouble :
1484                     bHaveValue = true;
1485                     bRes ^= ( PopDouble() != 0.0 );
1486                 break;
1487                 case svString :
1488                     Pop();
1489                     SetError( FormulaError::NoValue );
1490                 break;
1491                 case svSingleRef :
1492                 {
1493                     ScAddress aAdr;
1494                     PopSingleRef( aAdr );
1495                     if ( nGlobalError == FormulaError::NONE )
1496                     {
1497                         ScRefCellValue aCell(mrDoc, aAdr);
1498                         if (aCell.hasNumeric())
1499                         {
1500                             bHaveValue = true;
1501                             bRes ^= ( GetCellValue(aAdr, aCell) != 0.0 );
1502                         }
1503                         /* TODO: set error? Excel doesn't have XOR, but
1504                          * doesn't set an error in this case for AND and
1505                          * OR. */
1506                     }
1507                 }
1508                 break;
1509                 case svDoubleRef:
1510                 case svRefList:
1511                 {
1512                     ScRange aRange;
1513                     PopDoubleRef( aRange, nParamCount, nRefInList);
1514                     if ( nGlobalError == FormulaError::NONE )
1515                     {
1516                         double fVal;
1517                         FormulaError nErr = FormulaError::NONE;
1518                         ScValueIterator aValIter( mrContext, aRange );
1519                         if ( aValIter.GetFirst( fVal, nErr ) )
1520                         {
1521                             bHaveValue = true;
1522                             do
1523                             {
1524                                 bRes ^= ( fVal != 0.0 );
1525                             } while ( (nErr == FormulaError::NONE) &&
1526                                 aValIter.GetNext( fVal, nErr ) );
1527                         }
1528                         SetError( nErr );
1529                     }
1530                 }
1531                 break;
1532                 case svExternalSingleRef:
1533                 case svExternalDoubleRef:
1534                 case svMatrix:
1535                 {
1536                     bHaveValue = true;
1537                     ScMatrixRef pMat = GetMatrix();
1538                     if ( pMat )
1539                     {
1540                         bHaveValue = true;
1541                         double fVal = pMat->Xor();
1542                         FormulaError nErr = GetDoubleErrorValue( fVal );
1543                         if ( nErr != FormulaError::NONE )
1544                         {
1545                             SetError( nErr );
1546                             bRes = false;
1547                         }
1548                         else
1549                             bRes ^= ( fVal != 0.0 );
1550                     }
1551                     // else: GetMatrix did set FormulaError::IllegalParameter
1552                 }
1553                 break;
1554                 default:
1555                     PopError();
1556                     SetError( FormulaError::IllegalParameter);
1557             }
1558         }
1559         else
1560             Pop();
1561     }
1562     if ( bHaveValue )
1563         PushInt( int(bRes) );
1564     else
1565         PushNoValue();
1566 }
1567 
1568 void ScInterpreter::ScNeg()
1569 {
1570     // Simple negation doesn't change current format type to number, keep
1571     // current type.
1572     nFuncFmtType = nCurFmtType;
1573     switch ( GetStackType() )
1574     {
1575         case svMatrix :
1576         {
1577             ScMatrixRef pMat = GetMatrix();
1578             if ( !pMat )
1579                 PushIllegalParameter();
1580             else
1581             {
1582                 SCSIZE nC, nR;
1583                 pMat->GetDimensions( nC, nR );
1584                 ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true );
1585                 if ( !pResMat )
1586                     PushIllegalArgument();
1587                 else
1588                 {
1589                     pMat->NegOp( *pResMat);
1590                     PushMatrix( pResMat );
1591                 }
1592             }
1593         }
1594         break;
1595         default:
1596             PushDouble( -GetDouble() );
1597     }
1598 }
1599 
1600 void ScInterpreter::ScPercentSign()
1601 {
1602     nFuncFmtType = SvNumFormatType::PERCENT;
1603     const FormulaToken* pSaveCur = pCur;
1604     sal_uInt8 nSavePar = cPar;
1605     PushInt( 100 );
1606     cPar = 2;
1607     FormulaByteToken aDivOp( ocDiv, cPar );
1608     pCur = &aDivOp;
1609     ScDiv();
1610     pCur = pSaveCur;
1611     cPar = nSavePar;
1612 }
1613 
1614 void ScInterpreter::ScNot()
1615 {
1616     nFuncFmtType = SvNumFormatType::LOGICAL;
1617     switch ( GetStackType() )
1618     {
1619         case svMatrix :
1620         {
1621             ScMatrixRef pMat = GetMatrix();
1622             if ( !pMat )
1623                 PushIllegalParameter();
1624             else
1625             {
1626                 SCSIZE nC, nR;
1627                 pMat->GetDimensions( nC, nR );
1628                 ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true);
1629                 if ( !pResMat )
1630                     PushIllegalArgument();
1631                 else
1632                 {
1633                     pMat->NotOp( *pResMat);
1634                     PushMatrix( pResMat );
1635                 }
1636             }
1637         }
1638         break;
1639         default:
1640             PushInt( int(GetDouble() == 0.0) );
1641     }
1642 }
1643 
1644 void ScInterpreter::ScBitAnd()
1645 {
1646 
1647     if ( !MustHaveParamCount( GetByte(), 2 ) )
1648         return;
1649 
1650     double num1 = ::rtl::math::approxFloor( GetDouble());
1651     double num2 = ::rtl::math::approxFloor( GetDouble());
1652     if (    (num1 >= n2power48) || (num1 < 0) ||
1653             (num2 >= n2power48) || (num2 < 0))
1654         PushIllegalArgument();
1655     else
1656         PushDouble (static_cast<sal_uInt64>(num1) & static_cast<sal_uInt64>(num2));
1657 }
1658 
1659 void ScInterpreter::ScBitOr()
1660 {
1661 
1662     if ( !MustHaveParamCount( GetByte(), 2 ) )
1663         return;
1664 
1665     double num1 = ::rtl::math::approxFloor( GetDouble());
1666     double num2 = ::rtl::math::approxFloor( GetDouble());
1667     if (    (num1 >= n2power48) || (num1 < 0) ||
1668             (num2 >= n2power48) || (num2 < 0))
1669         PushIllegalArgument();
1670     else
1671         PushDouble (static_cast<sal_uInt64>(num1) | static_cast<sal_uInt64>(num2));
1672 }
1673 
1674 void ScInterpreter::ScBitXor()
1675 {
1676 
1677     if ( !MustHaveParamCount( GetByte(), 2 ) )
1678         return;
1679 
1680     double num1 = ::rtl::math::approxFloor( GetDouble());
1681     double num2 = ::rtl::math::approxFloor( GetDouble());
1682     if (    (num1 >= n2power48) || (num1 < 0) ||
1683             (num2 >= n2power48) || (num2 < 0))
1684         PushIllegalArgument();
1685     else
1686         PushDouble (static_cast<sal_uInt64>(num1) ^ static_cast<sal_uInt64>(num2));
1687 }
1688 
1689 void ScInterpreter::ScBitLshift()
1690 {
1691 
1692     if ( !MustHaveParamCount( GetByte(), 2 ) )
1693         return;
1694 
1695     double fShift = ::rtl::math::approxFloor( GetDouble());
1696     double num = ::rtl::math::approxFloor( GetDouble());
1697     if ((num >= n2power48) || (num < 0))
1698         PushIllegalArgument();
1699     else
1700     {
1701         double fRes;
1702         if (fShift < 0)
1703             fRes = ::rtl::math::approxFloor( num / pow( 2.0, -fShift));
1704         else if (fShift == 0)
1705             fRes = num;
1706         else
1707             fRes = num * pow( 2.0, fShift);
1708         PushDouble( fRes);
1709     }
1710 }
1711 
1712 void ScInterpreter::ScBitRshift()
1713 {
1714 
1715     if ( !MustHaveParamCount( GetByte(), 2 ) )
1716         return;
1717 
1718     double fShift = ::rtl::math::approxFloor( GetDouble());
1719     double num = ::rtl::math::approxFloor( GetDouble());
1720     if ((num >= n2power48) || (num < 0))
1721         PushIllegalArgument();
1722     else
1723     {
1724         double fRes;
1725         if (fShift < 0)
1726             fRes = num * pow( 2.0, -fShift);
1727         else if (fShift == 0)
1728             fRes = num;
1729         else
1730             fRes = ::rtl::math::approxFloor( num / pow( 2.0, fShift));
1731         PushDouble( fRes);
1732     }
1733 }
1734 
1735 void ScInterpreter::ScPi()
1736 {
1737     PushDouble(M_PI);
1738 }
1739 
1740 void ScInterpreter::ScRandomImpl( const std::function<double( double fFirst, double fLast )>& RandomFunc,
1741         double fFirst, double fLast )
1742 {
1743     if (bMatrixFormula)
1744     {
1745         SCCOL nCols = 0;
1746         SCROW nRows = 0;
1747         // In JumpMatrix context use its dimensions for the return matrix; the
1748         // formula cell range selected may differ, for example if the result is
1749         // to be transposed.
1750         if (GetStackType(1) == svJumpMatrix)
1751         {
1752             SCSIZE nC, nR;
1753             pStack[sp-1]->GetJumpMatrix()->GetDimensions( nC, nR);
1754             nCols = std::max<SCCOL>(0, static_cast<SCCOL>(nC));
1755             nRows = std::max<SCROW>(0, static_cast<SCROW>(nR));
1756         }
1757         else if (pMyFormulaCell)
1758             pMyFormulaCell->GetMatColsRows( nCols, nRows);
1759 
1760         if (nCols == 1 && nRows == 1)
1761         {
1762             // For compatibility with existing
1763             // com.sun.star.sheet.FunctionAccess.callFunction() calls that per
1764             // default are executed in array context unless
1765             // FA.setPropertyValue("IsArrayFunction",False) was set, return a
1766             // scalar double instead of a 1x1 matrix object. tdf#128218
1767             PushDouble( RandomFunc( fFirst, fLast));
1768             return;
1769         }
1770 
1771         // ScViewFunc::EnterMatrix() might be asking for
1772         // ScFormulaCell::GetResultDimensions(), which here are none so create
1773         // a 1x1 matrix at least which exactly is the case when EnterMatrix()
1774         // asks for a not selected range.
1775         if (nCols == 0)
1776             nCols = 1;
1777         if (nRows == 0)
1778             nRows = 1;
1779         ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows), /*bEmpty*/true );
1780         if (!pResMat)
1781             PushError( FormulaError::MatrixSize);
1782         else
1783         {
1784             for (SCCOL i=0; i < nCols; ++i)
1785             {
1786                 for (SCROW j=0; j < nRows; ++j)
1787                 {
1788                     pResMat->PutDouble( RandomFunc( fFirst, fLast),
1789                             static_cast<SCSIZE>(i), static_cast<SCSIZE>(j));
1790                 }
1791             }
1792             PushMatrix( pResMat);
1793         }
1794     }
1795     else
1796     {
1797         PushDouble( RandomFunc( fFirst, fLast));
1798     }
1799 }
1800 
1801 void ScInterpreter::ScRandom()
1802 {
1803     auto RandomFunc = []( double, double )
1804     {
1805         return comphelper::rng::uniform_real_distribution();
1806     };
1807     ScRandomImpl( RandomFunc, 0.0, 0.0);
1808 }
1809 
1810 void ScInterpreter::ScRandbetween()
1811 {
1812     if (!MustHaveParamCount( GetByte(), 2))
1813         return;
1814 
1815     // Same like scaddins/source/analysis/analysis.cxx
1816     // AnalysisAddIn::getRandbetween()
1817     double fMax = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
1818     double fMin = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
1819     if (nGlobalError != FormulaError::NONE || fMin > fMax)
1820     {
1821         PushIllegalArgument();
1822         return;
1823     }
1824     fMax = std::nextafter( fMax+1, -DBL_MAX);
1825     auto RandomFunc = []( double fFirst, double fLast )
1826     {
1827         return floor( comphelper::rng::uniform_real_distribution( fFirst, fLast));
1828     };
1829     ScRandomImpl( RandomFunc, fMin, fMax);
1830 }
1831 
1832 void ScInterpreter::ScTrue()
1833 {
1834     nFuncFmtType = SvNumFormatType::LOGICAL;
1835     PushInt(1);
1836 }
1837 
1838 void ScInterpreter::ScFalse()
1839 {
1840     nFuncFmtType = SvNumFormatType::LOGICAL;
1841     PushInt(0);
1842 }
1843 
1844 void ScInterpreter::ScDeg()
1845 {
1846     PushDouble(basegfx::rad2deg(GetDouble()));
1847 }
1848 
1849 void ScInterpreter::ScRad()
1850 {
1851     PushDouble(basegfx::deg2rad(GetDouble()));
1852 }
1853 
1854 void ScInterpreter::ScSin()
1855 {
1856     PushDouble(::rtl::math::sin(GetDouble()));
1857 }
1858 
1859 void ScInterpreter::ScCos()
1860 {
1861     PushDouble(::rtl::math::cos(GetDouble()));
1862 }
1863 
1864 void ScInterpreter::ScTan()
1865 {
1866     PushDouble(::rtl::math::tan(GetDouble()));
1867 }
1868 
1869 void ScInterpreter::ScCot()
1870 {
1871     PushDouble(1.0 / ::rtl::math::tan(GetDouble()));
1872 }
1873 
1874 void ScInterpreter::ScArcSin()
1875 {
1876     PushDouble(asin(GetDouble()));
1877 }
1878 
1879 void ScInterpreter::ScArcCos()
1880 {
1881     PushDouble(acos(GetDouble()));
1882 }
1883 
1884 void ScInterpreter::ScArcTan()
1885 {
1886     PushDouble(atan(GetDouble()));
1887 }
1888 
1889 void ScInterpreter::ScArcCot()
1890 {
1891     PushDouble((M_PI_2) - atan(GetDouble()));
1892 }
1893 
1894 void ScInterpreter::ScSinHyp()
1895 {
1896     PushDouble(sinh(GetDouble()));
1897 }
1898 
1899 void ScInterpreter::ScCosHyp()
1900 {
1901     PushDouble(cosh(GetDouble()));
1902 }
1903 
1904 void ScInterpreter::ScTanHyp()
1905 {
1906     PushDouble(tanh(GetDouble()));
1907 }
1908 
1909 void ScInterpreter::ScCotHyp()
1910 {
1911     PushDouble(1.0 / tanh(GetDouble()));
1912 }
1913 
1914 void ScInterpreter::ScArcSinHyp()
1915 {
1916     PushDouble( ::rtl::math::asinh( GetDouble()));
1917 }
1918 
1919 void ScInterpreter::ScArcCosHyp()
1920 {
1921     double fVal = GetDouble();
1922     if (fVal < 1.0)
1923         PushIllegalArgument();
1924     else
1925         PushDouble( ::rtl::math::acosh( fVal));
1926 }
1927 
1928 void ScInterpreter::ScArcTanHyp()
1929 {
1930     double fVal = GetDouble();
1931     if (fabs(fVal) >= 1.0)
1932         PushIllegalArgument();
1933     else
1934         PushDouble(::atanh(fVal));
1935 }
1936 
1937 void ScInterpreter::ScArcCotHyp()
1938 {
1939     double nVal = GetDouble();
1940     if (fabs(nVal) <= 1.0)
1941         PushIllegalArgument();
1942     else
1943         PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0)));
1944 }
1945 
1946 void ScInterpreter::ScCosecant()
1947 {
1948     PushDouble(1.0 / ::rtl::math::sin(GetDouble()));
1949 }
1950 
1951 void ScInterpreter::ScSecant()
1952 {
1953     PushDouble(1.0 / ::rtl::math::cos(GetDouble()));
1954 }
1955 
1956 void ScInterpreter::ScCosecantHyp()
1957 {
1958     PushDouble(1.0 / sinh(GetDouble()));
1959 }
1960 
1961 void ScInterpreter::ScSecantHyp()
1962 {
1963     PushDouble(1.0 / cosh(GetDouble()));
1964 }
1965 
1966 void ScInterpreter::ScExp()
1967 {
1968     PushDouble(exp(GetDouble()));
1969 }
1970 
1971 void ScInterpreter::ScSqrt()
1972 {
1973     double fVal = GetDouble();
1974     if (fVal >= 0.0)
1975         PushDouble(sqrt(fVal));
1976     else
1977         PushIllegalArgument();
1978 }
1979 
1980 void ScInterpreter::ScIsEmpty()
1981 {
1982     short nRes = 0;
1983     nFuncFmtType = SvNumFormatType::LOGICAL;
1984     switch ( GetRawStackType() )
1985     {
1986         case svEmptyCell:
1987         {
1988             FormulaConstTokenRef p = PopToken();
1989             if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited())
1990                 nRes = 1;
1991         }
1992         break;
1993         case svDoubleRef :
1994         case svSingleRef :
1995         {
1996             ScAddress aAdr;
1997             if ( !PopDoubleRefOrSingleRef( aAdr ) )
1998                 break;
1999             // NOTE: this differs from COUNTBLANK() ScCountEmptyCells() that
2000             // may treat ="" in the referenced cell as blank for Excel
2001             // interoperability.
2002             ScRefCellValue aCell(mrDoc, aAdr);
2003             if (aCell.getType() == CELLTYPE_NONE)
2004                 nRes = 1;
2005         }
2006         break;
2007         case svExternalSingleRef:
2008         case svExternalDoubleRef:
2009         case svMatrix:
2010         {
2011             ScMatrixRef pMat = GetMatrix();
2012             if ( !pMat )
2013                 ;   // nothing
2014             else if ( !pJumpMatrix )
2015                 nRes = pMat->IsEmptyCell( 0, 0) ? 1 : 0;
2016             else
2017             {
2018                 SCSIZE nCols, nRows, nC, nR;
2019                 pMat->GetDimensions( nCols, nRows);
2020                 pJumpMatrix->GetPos( nC, nR);
2021                 if ( nC < nCols && nR < nRows )
2022                     nRes = pMat->IsEmptyCell( nC, nR) ? 1 : 0;
2023                 // else: false, not empty (which is what Xcl does)
2024             }
2025         }
2026         break;
2027         default:
2028             Pop();
2029     }
2030     nGlobalError = FormulaError::NONE;
2031     PushInt( nRes );
2032 }
2033 
2034 bool ScInterpreter::IsString()
2035 {
2036     nFuncFmtType = SvNumFormatType::LOGICAL;
2037     bool bRes = false;
2038     switch ( GetRawStackType() )
2039     {
2040         case svString:
2041             Pop();
2042             bRes = true;
2043         break;
2044         case svDoubleRef :
2045         case svSingleRef :
2046         {
2047             ScAddress aAdr;
2048             if ( !PopDoubleRefOrSingleRef( aAdr ) )
2049                 break;
2050 
2051             ScRefCellValue aCell(mrDoc, aAdr);
2052             if (GetCellErrCode(aCell) == FormulaError::NONE)
2053             {
2054                 switch (aCell.getType())
2055                 {
2056                     case CELLTYPE_STRING :
2057                     case CELLTYPE_EDIT :
2058                         bRes = true;
2059                         break;
2060                     case CELLTYPE_FORMULA :
2061                         bRes = (!aCell.getFormula()->IsValue() && !aCell.getFormula()->IsEmpty());
2062                         break;
2063                     default:
2064                         ; // nothing
2065                 }
2066             }
2067         }
2068         break;
2069         case svExternalSingleRef:
2070         {
2071             ScExternalRefCache::TokenRef pToken;
2072             PopExternalSingleRef(pToken);
2073             if (nGlobalError == FormulaError::NONE && pToken->GetType() == svString)
2074                 bRes = true;
2075         }
2076         break;
2077         case svExternalDoubleRef:
2078         case svMatrix:
2079         {
2080             ScMatrixRef pMat = GetMatrix();
2081             if ( !pMat )
2082                 ;   // nothing
2083             else if ( !pJumpMatrix )
2084                 bRes = pMat->IsStringOrEmpty(0, 0) && !pMat->IsEmpty(0, 0);
2085             else
2086             {
2087                 SCSIZE nCols, nRows, nC, nR;
2088                 pMat->GetDimensions( nCols, nRows);
2089                 pJumpMatrix->GetPos( nC, nR);
2090                 if ( nC < nCols && nR < nRows )
2091                     bRes = pMat->IsStringOrEmpty( nC, nR) && !pMat->IsEmpty( nC, nR);
2092             }
2093         }
2094         break;
2095         default:
2096             Pop();
2097     }
2098     nGlobalError = FormulaError::NONE;
2099     return bRes;
2100 }
2101 
2102 void ScInterpreter::ScIsString()
2103 {
2104     PushInt( int(IsString()) );
2105 }
2106 
2107 void ScInterpreter::ScIsNonString()
2108 {
2109     PushInt( int(!IsString()) );
2110 }
2111 
2112 void ScInterpreter::ScIsLogical()
2113 {
2114     bool bRes = false;
2115     switch ( GetStackType() )
2116     {
2117         case svDoubleRef :
2118         case svSingleRef :
2119         {
2120             ScAddress aAdr;
2121             if ( !PopDoubleRefOrSingleRef( aAdr ) )
2122                 break;
2123 
2124             ScRefCellValue aCell(mrDoc, aAdr);
2125             if (GetCellErrCode(aCell) == FormulaError::NONE)
2126             {
2127                 if (aCell.hasNumeric())
2128                 {
2129                     sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
2130                     bRes = (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL);
2131                 }
2132             }
2133         }
2134         break;
2135         case svMatrix:
2136         {
2137             double fVal;
2138             svl::SharedString aStr;
2139             ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
2140             bRes = (nMatValType == ScMatValType::Boolean);
2141         }
2142         break;
2143         default:
2144             PopError();
2145             if ( nGlobalError == FormulaError::NONE )
2146                 bRes = ( nCurFmtType == SvNumFormatType::LOGICAL );
2147     }
2148     nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
2149     nGlobalError = FormulaError::NONE;
2150     PushInt( int(bRes) );
2151 }
2152 
2153 void ScInterpreter::ScType()
2154 {
2155     short nType = 0;
2156     switch ( GetStackType() )
2157     {
2158         case svDoubleRef :
2159         case svSingleRef :
2160         {
2161             ScAddress aAdr;
2162             if ( !PopDoubleRefOrSingleRef( aAdr ) )
2163                 break;
2164 
2165             ScRefCellValue aCell(mrDoc, aAdr);
2166             if (GetCellErrCode(aCell) == FormulaError::NONE)
2167             {
2168                 switch (aCell.getType())
2169                 {
2170                     // NOTE: this is Xcl nonsense!
2171                     case CELLTYPE_STRING :
2172                     case CELLTYPE_EDIT :
2173                         nType = 2;
2174                         break;
2175                     case CELLTYPE_VALUE :
2176                     {
2177                         sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
2178                         if (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL)
2179                             nType = 4;
2180                         else
2181                             nType = 1;
2182                     }
2183                     break;
2184                     case CELLTYPE_NONE:
2185                         // always 1, s. tdf#73078
2186                         nType = 1;
2187                         break;
2188                     case CELLTYPE_FORMULA :
2189                         nType = 8;
2190                         break;
2191                     default:
2192                         PushIllegalArgument();
2193                 }
2194             }
2195             else
2196                 nType = 16;
2197         }
2198         break;
2199         case svString:
2200             PopError();
2201             if ( nGlobalError != FormulaError::NONE )
2202             {
2203                 nType = 16;
2204                 nGlobalError = FormulaError::NONE;
2205             }
2206             else
2207                 nType = 2;
2208         break;
2209         case svMatrix:
2210             PopMatrix();
2211             if ( nGlobalError != FormulaError::NONE )
2212             {
2213                 nType = 16;
2214                 nGlobalError = FormulaError::NONE;
2215             }
2216             else
2217                 nType = 64;
2218                 // we could return the type of one element if in JumpMatrix or
2219                 // ForceArray mode, but Xcl doesn't ...
2220         break;
2221         default:
2222             PopError();
2223             if ( nGlobalError != FormulaError::NONE )
2224             {
2225                 nType = 16;
2226                 nGlobalError = FormulaError::NONE;
2227             }
2228             else
2229                 nType = 1;
2230     }
2231     PushInt( nType );
2232 }
2233 
2234 static bool lcl_FormatHasNegColor( const SvNumberformat* pFormat )
2235 {
2236     return pFormat && pFormat->GetColor( 1 );
2237 }
2238 
2239 static bool lcl_FormatHasOpenPar( const SvNumberformat* pFormat )
2240 {
2241     return pFormat && (pFormat->GetFormatstring().indexOf('(') != -1);
2242 }
2243 
2244 namespace {
2245 
2246 void getFormatString(const SvNumberFormatter* pFormatter, sal_uLong nFormat, OUString& rFmtStr)
2247 {
2248     rFmtStr = pFormatter->GetCalcCellReturn( nFormat);
2249 }
2250 
2251 }
2252 
2253 void ScInterpreter::ScCell()
2254 {   // ATTRIBUTE ; [REF]
2255     sal_uInt8 nParamCount = GetByte();
2256     if( !MustHaveParamCount( nParamCount, 1, 2 ) )
2257         return;
2258 
2259     ScAddress aCellPos( aPos );
2260     if( nParamCount == 2 )
2261     {
2262         switch (GetStackType())
2263         {
2264             case svExternalSingleRef:
2265             case svExternalDoubleRef:
2266             {
2267                 // Let's handle external reference separately...
2268                 ScCellExternal();
2269                 return;
2270             }
2271             case svDoubleRef:
2272             {
2273                 // Exceptionally not an intersecting position but top left.
2274                 // See ODF v1.3 part 4 OpenFormula 6.13.3 CELL
2275                 ScRange aRange;
2276                 PopDoubleRef( aRange);
2277                 aCellPos = aRange.aStart;
2278             }
2279             break;
2280             case svSingleRef:
2281                 PopSingleRef( aCellPos);
2282             break;
2283             default:
2284                 PopError();
2285                 SetError( FormulaError::NoRef);
2286         }
2287     }
2288     OUString aInfoType = GetString().getString();
2289     if (nGlobalError != FormulaError::NONE)
2290         PushIllegalParameter();
2291     else
2292     {
2293         ScRefCellValue aCell(mrDoc, aCellPos);
2294 
2295         ScCellKeywordTranslator::transKeyword(aInfoType, &ScGlobal::GetLocale(), ocCell);
2296 
2297 // *** ADDRESS INFO ***
2298         if( aInfoType == "COL" )
2299         {   // column number (1-based)
2300             PushInt( aCellPos.Col() + 1 );
2301         }
2302         else if( aInfoType == "ROW" )
2303         {   // row number (1-based)
2304             PushInt( aCellPos.Row() + 1 );
2305         }
2306         else if( aInfoType == "SHEET" )
2307         {   // table number (1-based)
2308             PushInt( aCellPos.Tab() + 1 );
2309         }
2310         else if( aInfoType == "ADDRESS" )
2311         {   // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW
2312 
2313             // Follow the configurable string reference address syntax as also
2314             // used by INDIRECT() (and ADDRESS() for the sheet separator).
2315             FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
2316             switch (eConv)
2317             {
2318                 default:
2319                     // Use the current address syntax if unspecified or says
2320                     // one or the other or one we don't explicitly handle.
2321                     eConv = mrDoc.GetAddressConvention();
2322                 break;
2323                 case FormulaGrammar::CONV_OOO:
2324                 case FormulaGrammar::CONV_XL_A1:
2325                 case FormulaGrammar::CONV_XL_R1C1:
2326                     // Use that.
2327                 break;
2328             }
2329 
2330             ScRefFlags nFlags = (aCellPos.Tab() == aPos.Tab()) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D;
2331             OUString aStr(aCellPos.Format(nFlags, &mrDoc, eConv));
2332             PushString(aStr);
2333         }
2334         else if( aInfoType == "FILENAME" )
2335         {
2336             SCTAB nTab = aCellPos.Tab();
2337             OUString aFuncResult;
2338             if( nTab < mrDoc.GetTableCount() )
2339             {
2340                 if( mrDoc.GetLinkMode( nTab ) == ScLinkMode::VALUE )
2341                     mrDoc.GetName( nTab, aFuncResult );
2342                 else
2343                 {
2344                     ScDocShell* pShell = mrDoc.GetDocumentShell();
2345                     if( pShell && pShell->GetMedium() )
2346                     {
2347                         const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject();
2348                         OUString aTabName;
2349                         mrDoc.GetName( nTab, aTabName );
2350 
2351                         FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
2352                         if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
2353                             eConv = mrDoc.GetAddressConvention();
2354 
2355                         if (eConv == FormulaGrammar::CONV_XL_A1 ||
2356                             eConv == FormulaGrammar::CONV_XL_R1C1 ||
2357                             eConv == FormulaGrammar::CONV_XL_OOX)
2358                         {
2359                             // file name and table name: FILEPATH/[FILENAME]TABLE
2360                             aFuncResult = rURLObj.GetPartBeforeLastName()
2361                                 + "[" + rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous)
2362                                 + "]" + aTabName;
2363                         }
2364                         else
2365                         {
2366                             // file name and table name: 'FILEPATH/FILENAME'#$TABLE
2367                             aFuncResult = "'"
2368                                 + rURLObj.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous)
2369                                 + "'#$" + aTabName;
2370                         }
2371                     }
2372                 }
2373             }
2374             PushString( aFuncResult );
2375         }
2376         else if( aInfoType == "COORD" )
2377         {   // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW
2378             // Yes, passing tab as col is intentional!
2379             OUString aCellStr1 =
2380                 ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format(
2381                     (ScRefFlags::COL_ABS|ScRefFlags::COL_VALID), nullptr, mrDoc.GetAddressConvention() );
2382             OUString aCellStr2 =
2383                 aCellPos.Format((ScRefFlags::COL_ABS|ScRefFlags::COL_VALID|ScRefFlags::ROW_ABS|ScRefFlags::ROW_VALID),
2384                              nullptr, mrDoc.GetAddressConvention());
2385             OUString aFuncResult = aCellStr1 + ":" + aCellStr2;
2386             PushString( aFuncResult );
2387         }
2388 
2389 // *** CELL PROPERTIES ***
2390         else if( aInfoType == "CONTENTS" )
2391         {   // contents of the cell, no formatting
2392             if (aCell.hasString())
2393             {
2394                 svl::SharedString aStr;
2395                 GetCellString(aStr, aCell);
2396                 PushString( aStr );
2397             }
2398             else
2399                 PushDouble(GetCellValue(aCellPos, aCell));
2400         }
2401         else if( aInfoType == "TYPE" )
2402         {   // b = blank; l = string (label); v = otherwise (value)
2403             sal_Unicode c;
2404             if (aCell.hasString())
2405                 c = 'l';
2406             else
2407                 c = aCell.hasNumeric() ? 'v' : 'b';
2408             PushString( OUString(c) );
2409         }
2410         else if( aInfoType == "WIDTH" )
2411         {   // column width (rounded off as count of zero characters in standard font and size)
2412             Printer*    pPrinter = mrDoc.GetPrinter();
2413             MapMode     aOldMode( pPrinter->GetMapMode() );
2414             vcl::Font   aOldFont( pPrinter->GetFont() );
2415             vcl::Font   aDefFont;
2416 
2417             pPrinter->SetMapMode(MapMode(MapUnit::MapTwip));
2418             // font color doesn't matter here
2419             mrDoc.getCellAttributeHelper().getDefaultCellAttribute().fillFontOnly(aDefFont, pPrinter);
2420             pPrinter->SetFont(aDefFont);
2421             tools::Long nZeroWidth = pPrinter->GetTextWidth( OUString( '0' ) );
2422             assert(nZeroWidth != 0);
2423             pPrinter->SetFont( aOldFont );
2424             pPrinter->SetMapMode( aOldMode );
2425             int nZeroCount = static_cast<int>(mrDoc.GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth);
2426             PushInt( nZeroCount );
2427         }
2428         else if( aInfoType == "PREFIX" )
2429         {   // ' = left; " = right; ^ = centered
2430             sal_Unicode c = 0;
2431             if (aCell.hasString())
2432             {
2433                 const SvxHorJustifyItem* pJustAttr = mrDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY );
2434                 switch( pJustAttr->GetValue() )
2435                 {
2436                     case SvxCellHorJustify::Standard:
2437                     case SvxCellHorJustify::Left:
2438                     case SvxCellHorJustify::Block:     c = '\''; break;
2439                     case SvxCellHorJustify::Center:    c = '^';  break;
2440                     case SvxCellHorJustify::Right:     c = '"';  break;
2441                     case SvxCellHorJustify::Repeat:    c = '\\'; break;
2442                 }
2443             }
2444             PushString( OUString(c) );
2445         }
2446         else if( aInfoType == "PROTECT" )
2447         {   // 1 = cell locked
2448             const ScProtectionAttr* pProtAttr = mrDoc.GetAttr( aCellPos, ATTR_PROTECTION );
2449             PushInt( pProtAttr->GetProtection() ? 1 : 0 );
2450         }
2451 
2452 // *** FORMATTING ***
2453         else if( aInfoType == "FORMAT" )
2454         {   // specific format code for standard formats
2455             OUString aFuncResult;
2456             sal_uInt32 nFormat = mrDoc.GetNumberFormat( aCellPos );
2457             getFormatString(pFormatter, nFormat, aFuncResult);
2458             PushString( aFuncResult );
2459         }
2460         else if( aInfoType == "COLOR" )
2461         {   // 1 = negative values are colored, otherwise 0
2462             const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
2463             PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 );
2464         }
2465         else if( aInfoType == "PARENTHESES" )
2466         {   // 1 = format string contains a '(' character, otherwise 0
2467             const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
2468             PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 );
2469         }
2470         else
2471             PushIllegalArgument();
2472     }
2473 }
2474 
2475 void ScInterpreter::ScCellExternal()
2476 {
2477     sal_uInt16 nFileId;
2478     OUString aTabName;
2479     ScSingleRefData aRef;
2480     ScExternalRefCache::TokenRef pToken;
2481     ScExternalRefCache::CellFormat aFmt;
2482     PopExternalSingleRef(nFileId, aTabName, aRef, pToken, &aFmt);
2483     if (nGlobalError != FormulaError::NONE)
2484     {
2485         PushError( nGlobalError);
2486         return;
2487     }
2488 
2489     OUString aInfoType = GetString().getString();
2490     if (nGlobalError != FormulaError::NONE)
2491     {
2492         PushError( nGlobalError);
2493         return;
2494     }
2495 
2496     SCCOL nCol;
2497     SCROW nRow;
2498     SCTAB nTab;
2499     aRef.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like.
2500     SingleRefToVars(aRef, nCol, nRow, nTab);
2501     if (nGlobalError != FormulaError::NONE)
2502     {
2503         PushIllegalParameter();
2504         return;
2505     }
2506     aRef.SetAbsTab(-1); // revert the value.
2507 
2508     ScCellKeywordTranslator::transKeyword(aInfoType, &ScGlobal::GetLocale(), ocCell);
2509     ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
2510 
2511     if ( aInfoType == "COL" )
2512         PushInt(nCol + 1);
2513     else if ( aInfoType == "ROW" )
2514         PushInt(nRow + 1);
2515     else if ( aInfoType == "SHEET" )
2516     {
2517         // For SHEET, No idea what number we should set, but let's always set
2518         // 1 if the external sheet exists, no matter what sheet.  Excel does
2519         // the same.
2520         if (pRefMgr->getCacheTable(nFileId, aTabName, false))
2521             PushInt(1);
2522         else
2523             SetError(FormulaError::NoName);
2524     }
2525     else if ( aInfoType == "ADDRESS" )
2526     {
2527         // ODF 1.2 says we need to always display address using the ODF A1 grammar.
2528         ScTokenArray aArray(mrDoc);
2529         aArray.AddExternalSingleReference(nFileId, svl::SharedString( aTabName), aRef); // string not interned
2530         ScCompiler aComp(mrDoc, aPos, aArray, formula::FormulaGrammar::GRAM_ODFF_A1);
2531         OUString aStr;
2532         aComp.CreateStringFromTokenArray(aStr);
2533         PushString(aStr);
2534     }
2535     else if ( aInfoType == "FILENAME" )
2536     {
2537         const OUString* p = pRefMgr->getExternalFileName(nFileId);
2538         if (!p)
2539         {
2540             // In theory this should never happen...
2541             SetError(FormulaError::NoName);
2542             return;
2543         }
2544 
2545         OUString aBuf;
2546         FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
2547         if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
2548             eConv = mrDoc.GetAddressConvention();
2549 
2550         if (eConv == FormulaGrammar::CONV_XL_A1 ||
2551             eConv == FormulaGrammar::CONV_XL_R1C1 ||
2552             eConv == FormulaGrammar::CONV_XL_OOX)
2553         {
2554             // 'file URI/[FileName]SheetName
2555             sal_Int32 nPos = p->lastIndexOf('/');
2556             aBuf = OUString::Concat(p->subView(0, nPos + 1))
2557                 + "[" + p->subView(nPos + 1) + "]"
2558                 + aTabName;
2559         }
2560         else
2561         {
2562             // 'file URI'#$SheetName
2563             aBuf = "'" + *p + "'#$" + aTabName;
2564         }
2565 
2566         PushString(aBuf);
2567     }
2568     else if ( aInfoType == "CONTENTS" )
2569     {
2570         switch (pToken->GetType())
2571         {
2572             case svString:
2573                 PushString(pToken->GetString());
2574             break;
2575             case svDouble:
2576                 PushString(OUString::number(pToken->GetDouble()));
2577             break;
2578             case svError:
2579                 PushString(ScGlobal::GetErrorString(pToken->GetError()));
2580             break;
2581             default:
2582                 PushString(OUString());
2583         }
2584     }
2585     else if ( aInfoType == "TYPE" )
2586     {
2587         sal_Unicode c = 'v';
2588         switch (pToken->GetType())
2589         {
2590             case svString:
2591                 c = 'l';
2592             break;
2593             case svEmptyCell:
2594                 c = 'b';
2595             break;
2596             default:
2597                 ;
2598         }
2599         PushString(OUString(c));
2600     }
2601     else if ( aInfoType == "FORMAT" )
2602     {
2603         OUString aFmtStr;
2604         sal_uLong nFmt = aFmt.mbIsSet ? aFmt.mnIndex : 0;
2605         getFormatString(pFormatter, nFmt, aFmtStr);
2606         PushString(aFmtStr);
2607     }
2608     else if ( aInfoType == "COLOR" )
2609     {
2610         // 1 = negative values are colored, otherwise 0
2611         int nVal = 0;
2612         if (aFmt.mbIsSet)
2613         {
2614             const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2615             nVal = lcl_FormatHasNegColor(pFormat) ? 1 : 0;
2616         }
2617         PushInt(nVal);
2618     }
2619     else if ( aInfoType == "PARENTHESES" )
2620     {
2621         // 1 = format string contains a '(' character, otherwise 0
2622         int nVal = 0;
2623         if (aFmt.mbIsSet)
2624         {
2625             const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2626             nVal = lcl_FormatHasOpenPar(pFormat) ? 1 : 0;
2627         }
2628         PushInt(nVal);
2629     }
2630     else
2631         PushIllegalParameter();
2632 }
2633 
2634 void ScInterpreter::ScIsRef()
2635 {
2636     nFuncFmtType = SvNumFormatType::LOGICAL;
2637     bool bRes = false;
2638     switch ( GetStackType() )
2639     {
2640         case svSingleRef :
2641         {
2642             ScAddress aAdr;
2643             PopSingleRef( aAdr );
2644             if ( nGlobalError == FormulaError::NONE )
2645                 bRes = true;
2646         }
2647         break;
2648         case svDoubleRef :
2649         {
2650             ScRange aRange;
2651             PopDoubleRef( aRange );
2652             if ( nGlobalError == FormulaError::NONE )
2653                 bRes = true;
2654         }
2655         break;
2656         case svRefList :
2657         {
2658             FormulaConstTokenRef x = PopToken();
2659             if ( nGlobalError == FormulaError::NONE )
2660                 bRes = !x->GetRefList()->empty();
2661         }
2662         break;
2663         case svExternalSingleRef:
2664         {
2665             ScExternalRefCache::TokenRef pToken;
2666             PopExternalSingleRef(pToken);
2667             if (nGlobalError == FormulaError::NONE)
2668                 bRes = true;
2669         }
2670         break;
2671         case svExternalDoubleRef:
2672         {
2673             ScExternalRefCache::TokenArrayRef pArray;
2674             PopExternalDoubleRef(pArray);
2675             if (nGlobalError == FormulaError::NONE)
2676                 bRes = true;
2677         }
2678         break;
2679         default:
2680             Pop();
2681     }
2682     nGlobalError = FormulaError::NONE;
2683     PushInt( int(bRes) );
2684 }
2685 
2686 void ScInterpreter::ScIsValue()
2687 {
2688     nFuncFmtType = SvNumFormatType::LOGICAL;
2689     bool bRes = false;
2690     switch ( GetRawStackType() )
2691     {
2692         case svDouble:
2693             Pop();
2694             bRes = true;
2695         break;
2696         case svDoubleRef :
2697         case svSingleRef :
2698         {
2699             ScAddress aAdr;
2700             if ( !PopDoubleRefOrSingleRef( aAdr ) )
2701                 break;
2702 
2703             ScRefCellValue aCell(mrDoc, aAdr);
2704             if (GetCellErrCode(aCell) == FormulaError::NONE)
2705             {
2706                 switch (aCell.getType())
2707                 {
2708                     case CELLTYPE_VALUE :
2709                         bRes = true;
2710                         break;
2711                     case CELLTYPE_FORMULA :
2712                         bRes = (aCell.getFormula()->IsValue() && !aCell.getFormula()->IsEmpty());
2713                         break;
2714                     default:
2715                         ; // nothing
2716                 }
2717             }
2718         }
2719         break;
2720         case svExternalSingleRef:
2721         {
2722             ScExternalRefCache::TokenRef pToken;
2723             PopExternalSingleRef(pToken);
2724             if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
2725                 bRes = true;
2726         }
2727         break;
2728         case svExternalDoubleRef:
2729         case svMatrix:
2730         {
2731             ScMatrixRef pMat = GetMatrix();
2732             if ( !pMat )
2733                 ;   // nothing
2734             else if ( !pJumpMatrix )
2735             {
2736                 if (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NONE)
2737                     bRes = pMat->IsValue( 0, 0);
2738             }
2739             else
2740             {
2741                 SCSIZE nCols, nRows, nC, nR;
2742                 pMat->GetDimensions( nCols, nRows);
2743                 pJumpMatrix->GetPos( nC, nR);
2744                 if ( nC < nCols && nR < nRows )
2745                     if (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NONE)
2746                         bRes = pMat->IsValue( nC, nR);
2747             }
2748         }
2749         break;
2750         default:
2751             Pop();
2752     }
2753     nGlobalError = FormulaError::NONE;
2754     PushInt( int(bRes) );
2755 }
2756 
2757 void ScInterpreter::ScIsFormula()
2758 {
2759     nFuncFmtType = SvNumFormatType::LOGICAL;
2760     bool bRes = false;
2761     switch ( GetStackType() )
2762     {
2763         case svDoubleRef :
2764             if (IsInArrayContext())
2765             {
2766                 SCCOL nCol1, nCol2;
2767                 SCROW nRow1, nRow2;
2768                 SCTAB nTab1, nTab2;
2769                 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2770                 if (nGlobalError != FormulaError::NONE)
2771                 {
2772                     PushError( nGlobalError);
2773                     return;
2774                 }
2775                 if (nTab1 != nTab2)
2776                 {
2777                     PushIllegalArgument();
2778                     return;
2779                 }
2780 
2781                 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCol2 - nCol1 + 1),
2782                         static_cast<SCSIZE>(nRow2 - nRow1 + 1), true);
2783                 if (!pResMat)
2784                 {
2785                     PushError( FormulaError::MatrixSize);
2786                     return;
2787                 }
2788 
2789                 /* TODO: we really should have a gap-aware cell iterator. */
2790                 SCSIZE i=0, j=0;
2791                 ScAddress aAdr( 0, 0, nTab1);
2792                 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2793                 {
2794                     aAdr.SetCol(nCol);
2795                     for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2796                     {
2797                         aAdr.SetRow(nRow);
2798                         ScRefCellValue aCell(mrDoc, aAdr);
2799                         pResMat->PutBoolean( (aCell.getType() == CELLTYPE_FORMULA), i,j);
2800                         ++j;
2801                     }
2802                     ++i;
2803                     j = 0;
2804                 }
2805 
2806                 PushMatrix( pResMat);
2807                 return;
2808             }
2809         [[fallthrough]];
2810         case svSingleRef :
2811         {
2812             ScAddress aAdr;
2813             if ( !PopDoubleRefOrSingleRef( aAdr ) )
2814                 break;
2815 
2816             bRes = (mrDoc.GetCellType(aAdr) == CELLTYPE_FORMULA);
2817         }
2818         break;
2819         default:
2820             Pop();
2821     }
2822     nGlobalError = FormulaError::NONE;
2823     PushInt( int(bRes) );
2824 }
2825 
2826 void ScInterpreter::ScFormula()
2827 {
2828     OUString aFormula;
2829     switch ( GetStackType() )
2830     {
2831         case svDoubleRef :
2832             if (IsInArrayContext())
2833             {
2834                 SCCOL nCol1, nCol2;
2835                 SCROW nRow1, nRow2;
2836                 SCTAB nTab1, nTab2;
2837                 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2838                 if (nGlobalError != FormulaError::NONE)
2839                     break;
2840 
2841                 if (nTab1 != nTab2)
2842                 {
2843                     SetError( FormulaError::IllegalArgument);
2844                     break;
2845                 }
2846 
2847                 ScMatrixRef pResMat = GetNewMat( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1, true);
2848                 if (!pResMat)
2849                     break;
2850 
2851                 /* TODO: use a column iterator instead? */
2852                 SCSIZE i=0, j=0;
2853                 ScAddress aAdr(0,0,nTab1);
2854                 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2855                 {
2856                     aAdr.SetCol(nCol);
2857                     for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2858                     {
2859                         aAdr.SetRow(nRow);
2860                         ScRefCellValue aCell(mrDoc, aAdr);
2861                         switch (aCell.getType())
2862                         {
2863                             case CELLTYPE_FORMULA :
2864                                 aFormula = aCell.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext);
2865                                 pResMat->PutString( mrStrPool.intern( aFormula), i,j);
2866                                 break;
2867                             default:
2868                                 pResMat->PutError( FormulaError::NotAvailable, i,j);
2869                         }
2870                         ++j;
2871                     }
2872                     ++i;
2873                     j = 0;
2874                 }
2875 
2876                 PushMatrix( pResMat);
2877                 return;
2878             }
2879             [[fallthrough]];
2880         case svSingleRef :
2881         {
2882             ScAddress aAdr;
2883             if ( !PopDoubleRefOrSingleRef( aAdr ) )
2884                 break;
2885 
2886             ScRefCellValue aCell(mrDoc, aAdr);
2887             switch (aCell.getType())
2888             {
2889                 case CELLTYPE_FORMULA :
2890                     aFormula = aCell.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext);
2891                 break;
2892                 default:
2893                     SetError( FormulaError::NotAvailable );
2894             }
2895         }
2896         break;
2897         default:
2898             PopError();
2899             SetError( FormulaError::NotAvailable );
2900     }
2901     PushString( aFormula );
2902 }
2903 
2904 void ScInterpreter::ScIsNV()
2905 {
2906     nFuncFmtType = SvNumFormatType::LOGICAL;
2907     bool bRes = false;
2908     switch ( GetStackType() )
2909     {
2910         case svDoubleRef :
2911         case svSingleRef :
2912         {
2913             ScAddress aAdr;
2914             bool bOk = PopDoubleRefOrSingleRef( aAdr );
2915             if ( nGlobalError == FormulaError::NotAvailable )
2916                 bRes = true;
2917             else if (bOk)
2918             {
2919                 ScRefCellValue aCell(mrDoc, aAdr);
2920                 FormulaError nErr = GetCellErrCode(aCell);
2921                 bRes = (nErr == FormulaError::NotAvailable);
2922             }
2923         }
2924         break;
2925         case svExternalSingleRef:
2926         {
2927             ScExternalRefCache::TokenRef pToken;
2928             PopExternalSingleRef(pToken);
2929             if (nGlobalError == FormulaError::NotAvailable ||
2930                     (pToken && pToken->GetType() == svError && pToken->GetError() == FormulaError::NotAvailable))
2931                 bRes = true;
2932         }
2933         break;
2934         case svExternalDoubleRef:
2935         case svMatrix:
2936         {
2937             ScMatrixRef pMat = GetMatrix();
2938             if ( !pMat )
2939                 ;   // nothing
2940             else if ( !pJumpMatrix )
2941                 bRes = (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NotAvailable);
2942             else
2943             {
2944                 SCSIZE nCols, nRows, nC, nR;
2945                 pMat->GetDimensions( nCols, nRows);
2946                 pJumpMatrix->GetPos( nC, nR);
2947                 if ( nC < nCols && nR < nRows )
2948                     bRes = (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NotAvailable);
2949             }
2950         }
2951         break;
2952         default:
2953             PopError();
2954             if ( nGlobalError == FormulaError::NotAvailable )
2955                 bRes = true;
2956     }
2957     nGlobalError = FormulaError::NONE;
2958     PushInt( int(bRes) );
2959 }
2960 
2961 void ScInterpreter::ScIsErr()
2962 {
2963     nFuncFmtType = SvNumFormatType::LOGICAL;
2964     bool bRes = false;
2965     switch ( GetStackType() )
2966     {
2967         case svDoubleRef :
2968         case svSingleRef :
2969         {
2970             ScAddress aAdr;
2971             bool bOk = PopDoubleRefOrSingleRef( aAdr );
2972             if ( !bOk || (nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) )
2973                 bRes = true;
2974             else
2975             {
2976                 ScRefCellValue aCell(mrDoc, aAdr);
2977                 FormulaError nErr = GetCellErrCode(aCell);
2978                 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2979             }
2980         }
2981         break;
2982         case svExternalSingleRef:
2983         {
2984             ScExternalRefCache::TokenRef pToken;
2985             PopExternalSingleRef(pToken);
2986             if ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pToken ||
2987                     (pToken->GetType() == svError && pToken->GetError() != FormulaError::NotAvailable))
2988                 bRes = true;
2989         }
2990         break;
2991         case svExternalDoubleRef:
2992         case svMatrix:
2993         {
2994             ScMatrixRef pMat = GetMatrix();
2995             if ( nGlobalError != FormulaError::NONE || !pMat )
2996                 bRes = ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pMat);
2997             else if ( !pJumpMatrix )
2998             {
2999                 FormulaError nErr = pMat->GetErrorIfNotString( 0, 0);
3000                 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
3001             }
3002             else
3003             {
3004                 SCSIZE nCols, nRows, nC, nR;
3005                 pMat->GetDimensions( nCols, nRows);
3006                 pJumpMatrix->GetPos( nC, nR);
3007                 if ( nC < nCols && nR < nRows )
3008                 {
3009                     FormulaError nErr = pMat->GetErrorIfNotString( nC, nR);
3010                     bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
3011                 }
3012             }
3013         }
3014         break;
3015         default:
3016             PopError();
3017             if ( nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable )
3018                 bRes = true;
3019     }
3020     nGlobalError = FormulaError::NONE;
3021     PushInt( int(bRes) );
3022 }
3023 
3024 void ScInterpreter::ScIsError()
3025 {
3026     nFuncFmtType = SvNumFormatType::LOGICAL;
3027     bool bRes = false;
3028     switch ( GetStackType() )
3029     {
3030         case svDoubleRef :
3031         case svSingleRef :
3032         {
3033             ScAddress aAdr;
3034             if ( !PopDoubleRefOrSingleRef( aAdr ) )
3035             {
3036                 bRes = true;
3037                 break;
3038             }
3039             if ( nGlobalError != FormulaError::NONE )
3040                 bRes = true;
3041             else
3042             {
3043                 ScRefCellValue aCell(mrDoc, aAdr);
3044                 bRes = (GetCellErrCode(aCell) != FormulaError::NONE);
3045             }
3046         }
3047         break;
3048         case svExternalSingleRef:
3049         {
3050             ScExternalRefCache::TokenRef pToken;
3051             PopExternalSingleRef(pToken);
3052             if (nGlobalError != FormulaError::NONE || pToken->GetType() == svError)
3053                 bRes = true;
3054         }
3055         break;
3056         case svExternalDoubleRef:
3057         case svMatrix:
3058         {
3059             ScMatrixRef pMat = GetMatrix();
3060             if ( nGlobalError != FormulaError::NONE || !pMat )
3061                 bRes = true;
3062             else if ( !pJumpMatrix )
3063                 bRes = (pMat->GetErrorIfNotString( 0, 0) != FormulaError::NONE);
3064             else
3065             {
3066                 SCSIZE nCols, nRows, nC, nR;
3067                 pMat->GetDimensions( nCols, nRows);
3068                 pJumpMatrix->GetPos( nC, nR);
3069                 if ( nC < nCols && nR < nRows )
3070                     bRes = (pMat->GetErrorIfNotString( nC, nR) != FormulaError::NONE);
3071             }
3072         }
3073         break;
3074         default:
3075             PopError();
3076             if ( nGlobalError != FormulaError::NONE )
3077                 bRes = true;
3078     }
3079     nGlobalError = FormulaError::NONE;
3080     PushInt( int(bRes) );
3081 }
3082 
3083 bool ScInterpreter::IsEven()
3084 {
3085     nFuncFmtType = SvNumFormatType::LOGICAL;
3086     bool bRes = false;
3087     double fVal = 0.0;
3088     switch ( GetStackType() )
3089     {
3090         case svDoubleRef :
3091         case svSingleRef :
3092         {
3093             ScAddress aAdr;
3094             if ( !PopDoubleRefOrSingleRef( aAdr ) )
3095                 break;
3096 
3097             ScRefCellValue aCell(mrDoc, aAdr);
3098             FormulaError nErr = GetCellErrCode(aCell);
3099             if (nErr != FormulaError::NONE)
3100                 SetError(nErr);
3101             else
3102             {
3103                 switch (aCell.getType())
3104                 {
3105                     case CELLTYPE_VALUE :
3106                         fVal = GetCellValue(aAdr, aCell);
3107                         bRes = true;
3108                     break;
3109                     case CELLTYPE_FORMULA :
3110                         if (aCell.getFormula()->IsValue())
3111                         {
3112                             fVal = GetCellValue(aAdr, aCell);
3113                             bRes = true;
3114                         }
3115                     break;
3116                     default:
3117                         ; // nothing
3118                 }
3119             }
3120         }
3121         break;
3122         case svDouble:
3123         {
3124             fVal = PopDouble();
3125             bRes = true;
3126         }
3127         break;
3128         case svExternalSingleRef:
3129         {
3130             ScExternalRefCache::TokenRef pToken;
3131             PopExternalSingleRef(pToken);
3132             if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
3133             {
3134                 fVal = pToken->GetDouble();
3135                 bRes = true;
3136             }
3137         }
3138         break;
3139         case svExternalDoubleRef:
3140         case svMatrix:
3141         {
3142             ScMatrixRef pMat = GetMatrix();
3143             if ( !pMat )
3144                 ;   // nothing
3145             else if ( !pJumpMatrix )
3146             {
3147                 bRes = pMat->IsValue( 0, 0);
3148                 if ( bRes )
3149                     fVal = pMat->GetDouble( 0, 0);
3150             }
3151             else
3152             {
3153                 SCSIZE nCols, nRows, nC, nR;
3154                 pMat->GetDimensions( nCols, nRows);
3155                 pJumpMatrix->GetPos( nC, nR);
3156                 if ( nC < nCols && nR < nRows )
3157                 {
3158                     bRes = pMat->IsValue( nC, nR);
3159                     if ( bRes )
3160                         fVal = pMat->GetDouble( nC, nR);
3161                 }
3162                 else
3163                     SetError( FormulaError::NoValue);
3164             }
3165         }
3166         break;
3167         default:
3168             ; // nothing
3169     }
3170     if ( !bRes )
3171         SetError( FormulaError::IllegalParameter);
3172     else
3173         bRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 );
3174     return bRes;
3175 }
3176 
3177 void ScInterpreter::ScIsEven()
3178 {
3179     PushInt( int(IsEven()) );
3180 }
3181 
3182 void ScInterpreter::ScIsOdd()
3183 {
3184     PushInt( int(!IsEven()) );
3185 }
3186 
3187 void ScInterpreter::ScN()
3188 {
3189     FormulaError nErr = nGlobalError;
3190     nGlobalError = FormulaError::NONE;
3191     // Temporarily override the ConvertStringToValue() error for
3192     // GetCellValue() / GetCellValueOrZero()
3193     FormulaError nSErr = mnStringNoValueError;
3194     mnStringNoValueError = FormulaError::CellNoValue;
3195     double fVal = GetDouble();
3196     mnStringNoValueError = nSErr;
3197     if (nErr != FormulaError::NONE)
3198         nGlobalError = nErr;    // preserve previous error if any
3199     else if (nGlobalError == FormulaError::CellNoValue)
3200         nGlobalError = FormulaError::NONE;       // reset temporary detection error
3201     PushDouble(fVal);
3202 }
3203 
3204 void ScInterpreter::ScTrim()
3205 {
3206     // Doesn't only trim but also removes duplicated blanks within!
3207     OUString aVal = comphelper::string::strip(GetString().getString(), ' ');
3208     OUStringBuffer aStr;
3209     const sal_Unicode* p = aVal.getStr();
3210     const sal_Unicode* const pEnd = p + aVal.getLength();
3211     while ( p < pEnd )
3212     {
3213         if ( *p != ' ' || p[-1] != ' ' )    // first can't be ' ', so -1 is fine
3214             aStr.append(*p);
3215         p++;
3216     }
3217     PushString(aStr.makeStringAndClear());
3218 }
3219 
3220 void ScInterpreter::ScUpper()
3221 {
3222     OUString aString = ScGlobal::getCharClass().uppercase(GetString().getString());
3223     PushString(aString);
3224 }
3225 
3226 void ScInterpreter::ScProper()
3227 {
3228 //2do: what to do with I18N-CJK ?!?
3229     OUStringBuffer aStr(GetString().getString());
3230     const sal_Int32 nLen = aStr.getLength();
3231     if ( nLen > 0 )
3232     {
3233         OUString aUpr(ScGlobal::getCharClass().uppercase(aStr.toString()));
3234         OUString aLwr(ScGlobal::getCharClass().lowercase(aStr.toString()));
3235         aStr[0] = aUpr[0];
3236         sal_Int32 nPos = 1;
3237         while( nPos < nLen )
3238         {
3239             OUString aTmpStr( aStr[nPos-1] );
3240             if ( !ScGlobal::getCharClass().isLetter( aTmpStr, 0 ) )
3241                 aStr[nPos] = aUpr[nPos];
3242             else
3243                 aStr[nPos] = aLwr[nPos];
3244             ++nPos;
3245         }
3246     }
3247     PushString(aStr.makeStringAndClear());
3248 }
3249 
3250 void ScInterpreter::ScLower()
3251 {
3252     OUString aString = ScGlobal::getCharClass().lowercase(GetString().getString());
3253     PushString(aString);
3254 }
3255 
3256 void ScInterpreter::ScLen()
3257 {
3258     OUString aStr = GetString().getString();
3259     sal_Int32 nIdx = 0;
3260     sal_Int32 nCnt = 0;
3261     while ( nIdx < aStr.getLength() )
3262     {
3263         aStr.iterateCodePoints( &nIdx );
3264         ++nCnt;
3265     }
3266     PushDouble( nCnt );
3267 }
3268 
3269 void ScInterpreter::ScT()
3270 {
3271     switch ( GetStackType() )
3272     {
3273         case svDoubleRef :
3274         case svSingleRef :
3275         {
3276             ScAddress aAdr;
3277             if ( !PopDoubleRefOrSingleRef( aAdr ) )
3278             {
3279                 PushInt(0);
3280                 return ;
3281             }
3282             bool bValue = false;
3283             ScRefCellValue aCell(mrDoc, aAdr);
3284             if (GetCellErrCode(aCell) == FormulaError::NONE)
3285             {
3286                 switch (aCell.getType())
3287                 {
3288                     case CELLTYPE_VALUE :
3289                         bValue = true;
3290                         break;
3291                     case CELLTYPE_FORMULA :
3292                         bValue = aCell.getFormula()->IsValue();
3293                         break;
3294                     default:
3295                         ; // nothing
3296                 }
3297             }
3298             if ( bValue )
3299                 PushString(OUString());
3300             else
3301             {
3302                 // like GetString()
3303                 svl::SharedString aStr;
3304                 GetCellString(aStr, aCell);
3305                 PushString(aStr);
3306             }
3307         }
3308         break;
3309         case svMatrix:
3310         case svExternalSingleRef:
3311         case svExternalDoubleRef:
3312         {
3313             double fVal;
3314             svl::SharedString aStr;
3315             ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
3316             if (ScMatrix::IsValueType( nMatValType))
3317                 PushString(svl::SharedString::getEmptyString());
3318             else
3319                 PushString( aStr);
3320         }
3321         break;
3322         case svDouble :
3323         {
3324             PopError();
3325             PushString( OUString() );
3326         }
3327         break;
3328         case svString :
3329             ;   // leave on stack
3330         break;
3331         default :
3332             PushError( FormulaError::UnknownOpCode);
3333     }
3334 }
3335 
3336 void ScInterpreter::ScValue()
3337 {
3338     OUString aInputString;
3339     double fVal;
3340 
3341     switch ( GetRawStackType() )
3342     {
3343         case svMissing:
3344         case svEmptyCell:
3345             Pop();
3346             PushInt(0);
3347             return;
3348         case svDouble:
3349             return;     // leave on stack
3350 
3351         case svSingleRef:
3352         case svDoubleRef:
3353         {
3354             ScAddress aAdr;
3355             if ( !PopDoubleRefOrSingleRef( aAdr ) )
3356             {
3357                 PushInt(0);
3358                 return;
3359             }
3360             ScRefCellValue aCell(mrDoc, aAdr);
3361             if (aCell.hasString())
3362             {
3363                 svl::SharedString aSS;
3364                 GetCellString(aSS, aCell);
3365                 aInputString = aSS.getString();
3366             }
3367             else if (aCell.hasNumeric())
3368             {
3369                 PushDouble( GetCellValue(aAdr, aCell) );
3370                 return;
3371             }
3372             else
3373             {
3374                 PushDouble(0.0);
3375                 return;
3376             }
3377         }
3378         break;
3379         case svMatrix:
3380             {
3381                 svl::SharedString aSS;
3382                 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal,
3383                         aSS);
3384                 aInputString = aSS.getString();
3385                 switch (nType)
3386                 {
3387                     case ScMatValType::Empty:
3388                         fVal = 0.0;
3389                         [[fallthrough]];
3390                     case ScMatValType::Value:
3391                     case ScMatValType::Boolean:
3392                         PushDouble( fVal);
3393                         return;
3394                     case ScMatValType::String:
3395                         // evaluated below
3396                         break;
3397                     default:
3398                         PushIllegalArgument();
3399                 }
3400             }
3401             break;
3402         default:
3403             aInputString = GetString().getString();
3404             break;
3405     }
3406 
3407     sal_uInt32 nFIndex = 0;     // 0 for default locale
3408     if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
3409         PushDouble(fVal);
3410     else
3411         PushIllegalArgument();
3412 }
3413 
3414 // fdo#57180
3415 void ScInterpreter::ScNumberValue()
3416 {
3417 
3418     sal_uInt8 nParamCount = GetByte();
3419     if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
3420         return;
3421 
3422     OUString aInputString;
3423     OUString aGroupSeparator;
3424     sal_Unicode cDecimalSeparator = 0;
3425 
3426     if ( nParamCount == 3 )
3427         aGroupSeparator = GetString().getString();
3428 
3429     if ( nParamCount >= 2 )
3430     {
3431         OUString aDecimalSeparator = GetString().getString();
3432         if ( aDecimalSeparator.getLength() == 1  )
3433             cDecimalSeparator = aDecimalSeparator[ 0 ];
3434         else
3435         {
3436             PushIllegalArgument();  //if given, separator length must be 1
3437             return;
3438         }
3439     }
3440 
3441     if ( cDecimalSeparator && aGroupSeparator.indexOf( cDecimalSeparator ) != -1 )
3442     {
3443         PushIllegalArgument(); //decimal separator cannot appear in group separator
3444         return;
3445     }
3446 
3447     switch (GetStackType())
3448     {
3449         case svDouble:
3450         return; // leave on stack
3451         default:
3452         aInputString = GetString().getString();
3453     }
3454     if ( nGlobalError != FormulaError::NONE )
3455     {
3456         PushError( nGlobalError );
3457         return;
3458     }
3459     if ( aInputString.isEmpty() )
3460     {
3461         if ( maCalcConfig.mbEmptyStringAsZero )
3462             PushDouble( 0.0 );
3463         else
3464             PushNoValue();
3465         return;
3466     }
3467 
3468     sal_Int32 nDecSep = aInputString.indexOf( cDecimalSeparator );
3469     if ( nDecSep != 0 )
3470     {
3471         OUString aTemporary( nDecSep >= 0 ? aInputString.copy( 0, nDecSep ) : aInputString );
3472         sal_Int32 nIndex = 0;
3473         while (nIndex < aGroupSeparator.getLength())
3474         {
3475             sal_uInt32 nChar = aGroupSeparator.iterateCodePoints( &nIndex );
3476             aTemporary = aTemporary.replaceAll( OUString( &nChar, 1 ), "" );
3477         }
3478         if ( nDecSep >= 0 )
3479             aInputString = aTemporary + aInputString.subView( nDecSep );
3480         else
3481             aInputString = aTemporary;
3482     }
3483 
3484     for ( sal_Int32 i = aInputString.getLength(); --i >= 0; )
3485     {
3486         sal_Unicode c = aInputString[ i ];
3487         if ( c == 0x0020 || c == 0x0009 || c == 0x000A || c == 0x000D )
3488             aInputString = aInputString.replaceAt( i, 1, u"" ); // remove spaces etc.
3489     }
3490     sal_Int32 nPercentCount = 0;
3491     for ( sal_Int32 i = aInputString.getLength() - 1; i >= 0 && aInputString[ i ] == 0x0025; i-- )
3492     {
3493         aInputString = aInputString.replaceAt( i, 1, u"" );  // remove and count trailing '%'
3494         nPercentCount++;
3495     }
3496 
3497     rtl_math_ConversionStatus eStatus;
3498     sal_Int32 nParseEnd;
3499     double fVal = ::rtl::math::stringToDouble( aInputString, cDecimalSeparator, 0, &eStatus, &nParseEnd );
3500     if ( eStatus == rtl_math_ConversionStatus_Ok && nParseEnd == aInputString.getLength() )
3501     {
3502         if (nPercentCount)
3503             fVal *= pow( 10.0, -(nPercentCount * 2));    // process '%' from input string
3504         PushDouble(fVal);
3505         return;
3506     }
3507     PushNoValue();
3508 }
3509 
3510 static bool lcl_ScInterpreter_IsPrintable( sal_uInt32 nCodePoint )
3511 {
3512     return ( !u_isISOControl(nCodePoint) /*not in Cc*/
3513              && u_isdefined(nCodePoint)  /*not in Cn*/ );
3514 }
3515 
3516 
3517 void ScInterpreter::ScClean()
3518 {
3519     OUString aStr = GetString().getString();
3520 
3521     OUStringBuffer aBuf( aStr.getLength() );
3522     sal_Int32 nIdx = 0;
3523     while ( nIdx <  aStr.getLength() )
3524     {
3525         sal_uInt32 c = aStr.iterateCodePoints( &nIdx );
3526         if ( lcl_ScInterpreter_IsPrintable( c ) )
3527             aBuf.appendUtf32( c );
3528     }
3529     PushString( aBuf.makeStringAndClear() );
3530 }
3531 
3532 
3533 void ScInterpreter::ScCode()
3534 {
3535 //2do: make it full range unicode?
3536     OUString aStr = GetString().getString();
3537     if (aStr.isEmpty())
3538         PushInt(0);
3539     else
3540     {
3541         //"classic" ByteString conversion flags
3542         const sal_uInt32 convertFlags =
3543             RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE |
3544             RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE |
3545             RTL_UNICODETOTEXT_FLAGS_FLUSH |
3546             RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT |
3547             RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT |
3548             RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE;
3549         PushInt( static_cast<unsigned char>(OUStringToOString(OUStringChar(aStr[0]), osl_getThreadTextEncoding(), convertFlags).toChar()) );
3550     }
3551 }
3552 
3553 void ScInterpreter::ScChar()
3554 {
3555 //2do: make it full range unicode?
3556     double fVal = GetDouble();
3557     if (fVal < 0.0 || fVal >= 256.0)
3558         PushIllegalArgument();
3559     else
3560     {
3561         //"classic" ByteString conversion flags
3562         const sal_uInt32 convertFlags =
3563             RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
3564             RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
3565             RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT;
3566 
3567         char cEncodedChar = static_cast<char>(fVal);
3568         OUString aStr(&cEncodedChar, 1,  osl_getThreadTextEncoding(), convertFlags);
3569         PushString(aStr);
3570     }
3571 }
3572 
3573 /* #i70213# fullwidth/halfwidth conversion provided by
3574  * Takashi Nakamoto <bluedwarf@ooo>
3575  * erAck: added Excel compatibility conversions as seen in issue's test case. */
3576 
3577 static OUString lcl_convertIntoHalfWidth( const OUString & rStr )
3578 {
3579     // Make the initialization thread-safe. Since another function needs to be called, move it all to another
3580     // function and thread-safely initialize a static reference in this function.
3581     auto init = []() -> utl::TransliterationWrapper&
3582         {
3583         static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3584         trans.loadModuleByImplName( "FULLWIDTH_HALFWIDTH_LIKE_ASC", LANGUAGE_SYSTEM );
3585         return trans;
3586         };
3587     static utl::TransliterationWrapper& aTrans( init());
3588     return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3589 }
3590 
3591 static OUString lcl_convertIntoFullWidth( const OUString & rStr )
3592 {
3593     auto init = []() -> utl::TransliterationWrapper&
3594         {
3595         static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3596         trans.loadModuleByImplName( "HALFWIDTH_FULLWIDTH_LIKE_JIS", LANGUAGE_SYSTEM );
3597         return trans;
3598         };
3599     static utl::TransliterationWrapper& aTrans( init());
3600     return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3601 }
3602 
3603 /* ODFF:
3604  * Summary: Converts half-width to full-width ASCII and katakana characters.
3605  * Semantics: Conversion is done for half-width ASCII and katakana characters,
3606  * other characters are simply copied from T to the result. This is the
3607  * complementary function to ASC.
3608  * For references regarding halfwidth and fullwidth characters see
3609  * http://www.unicode.org/reports/tr11/
3610  * http://www.unicode.org/charts/charindex2.html#H
3611  * http://www.unicode.org/charts/charindex2.html#F
3612  */
3613 void ScInterpreter::ScJis()
3614 {
3615     if (MustHaveParamCount( GetByte(), 1))
3616         PushString( lcl_convertIntoFullWidth( GetString().getString()));
3617 }
3618 
3619 /* ODFF:
3620  * Summary: Converts full-width to half-width ASCII and katakana characters.
3621  * Semantics: Conversion is done for full-width ASCII and katakana characters,
3622  * other characters are simply copied from T to the result. This is the
3623  * complementary function to JIS.
3624  */
3625 void ScInterpreter::ScAsc()
3626 {
3627     if (MustHaveParamCount( GetByte(), 1))
3628         PushString( lcl_convertIntoHalfWidth( GetString().getString()));
3629 }
3630 
3631 void ScInterpreter::ScUnicode()
3632 {
3633     if ( MustHaveParamCount( GetByte(), 1 ) )
3634     {
3635         OUString aStr = GetString().getString();
3636         if (aStr.isEmpty())
3637             PushIllegalParameter();
3638         else
3639         {
3640             PushDouble(aStr.iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
3641         }
3642     }
3643 }
3644 
3645 void ScInterpreter::ScUnichar()
3646 {
3647     if ( MustHaveParamCount( GetByte(), 1 ) )
3648     {
3649         sal_uInt32 nCodePoint = GetUInt32();
3650         if (nGlobalError != FormulaError::NONE || !rtl::isUnicodeCodePoint(nCodePoint))
3651             PushIllegalArgument();
3652         else
3653         {
3654             OUString aStr( &nCodePoint, 1 );
3655             PushString( aStr );
3656         }
3657     }
3658 }
3659 
3660 bool ScInterpreter::SwitchToArrayRefList( ScMatrixRef& xResMat, SCSIZE nMatRows, double fCurrent,
3661         const std::function<void( SCSIZE i, double fCurrent )>& MatOpFunc, bool bDoMatOp )
3662 {
3663     const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
3664     if (!p || !p->IsArrayResult())
3665         return false;
3666 
3667     if (!xResMat)
3668     {
3669         // Create and init all elements with current value.
3670         assert(nMatRows > 0);
3671         xResMat = GetNewMat( 1, nMatRows, true);
3672         xResMat->FillDouble( fCurrent, 0,0, 0,nMatRows-1);
3673     }
3674     else if (bDoMatOp)
3675     {
3676         // Current value and values from vector are operands
3677         // for each vector position.
3678         for (SCSIZE i=0; i < nMatRows; ++i)
3679         {
3680             MatOpFunc( i, fCurrent);
3681         }
3682     }
3683     return true;
3684 }
3685 
3686 void ScInterpreter::ScMin( bool bTextAsZero )
3687 {
3688     short nParamCount = GetByte();
3689     if (!MustHaveParamCountMin( nParamCount, 1))
3690         return;
3691 
3692     ScMatrixRef xResMat;
3693     double nMin = ::std::numeric_limits<double>::max();
3694     auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMin )
3695     {
3696         double fVecRes = xResMat->GetDouble(0,i);
3697         if (fVecRes > fCurMin)
3698             xResMat->PutDouble( fCurMin, 0,i);
3699     };
3700     const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3701     size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3702 
3703     double nVal = 0.0;
3704     ScAddress aAdr;
3705     ScRange aRange;
3706     size_t nRefInList = 0;
3707     while (nParamCount-- > 0)
3708     {
3709         switch (GetStackType())
3710         {
3711             case svDouble :
3712             {
3713                 nVal = GetDouble();
3714                 if (nMin > nVal) nMin = nVal;
3715                 nFuncFmtType = SvNumFormatType::NUMBER;
3716             }
3717             break;
3718             case svSingleRef :
3719             {
3720                 PopSingleRef( aAdr );
3721                 ScRefCellValue aCell(mrDoc, aAdr);
3722                 if (aCell.hasNumeric())
3723                 {
3724                     nVal = GetCellValue(aAdr, aCell);
3725                     CurFmtToFuncFmt();
3726                     if (nMin > nVal) nMin = nVal;
3727                 }
3728                 else if (bTextAsZero && aCell.hasString())
3729                 {
3730                     if ( nMin > 0.0 )
3731                         nMin = 0.0;
3732                 }
3733             }
3734             break;
3735             case svRefList :
3736             {
3737                 // bDoMatOp only for non-array value when switching to
3738                 // ArrayRefList.
3739                 if (SwitchToArrayRefList( xResMat, nMatRows, nMin, MatOpFunc,
3740                             nRefArrayPos == std::numeric_limits<size_t>::max()))
3741                 {
3742                     nRefArrayPos = nRefInList;
3743                 }
3744             }
3745             [[fallthrough]];
3746             case svDoubleRef :
3747             {
3748                 FormulaError nErr = FormulaError::NONE;
3749                 PopDoubleRef( aRange, nParamCount, nRefInList);
3750                 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
3751                 if (aValIter.GetFirst(nVal, nErr))
3752                 {
3753                     if (nMin > nVal)
3754                         nMin = nVal;
3755                     aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
3756                     while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3757                     {
3758                         if (nMin > nVal)
3759                             nMin = nVal;
3760                     }
3761                     SetError(nErr);
3762                 }
3763                 if (nRefArrayPos != std::numeric_limits<size_t>::max())
3764                 {
3765                     // Update vector element with current value.
3766                     MatOpFunc( nRefArrayPos, nMin);
3767 
3768                     // Reset.
3769                     nMin = std::numeric_limits<double>::max();
3770                     nVal = 0.0;
3771                     nRefArrayPos = std::numeric_limits<size_t>::max();
3772                 }
3773             }
3774             break;
3775             case svMatrix :
3776             case svExternalSingleRef:
3777             case svExternalDoubleRef:
3778             {
3779                 ScMatrixRef pMat = GetMatrix();
3780                 if (pMat)
3781                 {
3782                     nFuncFmtType = SvNumFormatType::NUMBER;
3783                     nVal = pMat->GetMinValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
3784                     if (nMin > nVal)
3785                         nMin = nVal;
3786                 }
3787             }
3788             break;
3789             case svString :
3790             {
3791                 Pop();
3792                 if ( bTextAsZero )
3793                 {
3794                     if ( nMin > 0.0 )
3795                         nMin = 0.0;
3796                 }
3797                 else
3798                     SetError(FormulaError::IllegalParameter);
3799             }
3800             break;
3801             default :
3802                 PopError();
3803                 SetError(FormulaError::IllegalParameter);
3804         }
3805     }
3806 
3807     if (xResMat)
3808     {
3809         // Include value of last non-references-array type and calculate final result.
3810         if (nMin < std::numeric_limits<double>::max())
3811         {
3812             for (SCSIZE i=0; i < nMatRows; ++i)
3813             {
3814                 MatOpFunc( i, nMin);
3815             }
3816         }
3817         else
3818         {
3819             /* TODO: the awkward "no value is minimum 0.0" is likely the case
3820              * if a value is numeric_limits::max. Still, that could be a valid
3821              * minimum value as well, but nVal and nMin had been reset after
3822              * the last svRefList... so we may lie here. */
3823             for (SCSIZE i=0; i < nMatRows; ++i)
3824             {
3825                 double fVecRes = xResMat->GetDouble(0,i);
3826                 if (fVecRes == std::numeric_limits<double>::max())
3827                     xResMat->PutDouble( 0.0, 0,i);
3828             }
3829         }
3830         PushMatrix( xResMat);
3831     }
3832     else
3833     {
3834         if (!std::isfinite(nVal))
3835             PushError( GetDoubleErrorValue( nVal));
3836         else if ( nVal < nMin  )
3837             PushDouble(0.0);    // zero or only empty arguments
3838         else
3839             PushDouble(nMin);
3840     }
3841 }
3842 
3843 void ScInterpreter::ScMax( bool bTextAsZero )
3844 {
3845     short nParamCount = GetByte();
3846     if (!MustHaveParamCountMin( nParamCount, 1))
3847         return;
3848 
3849     ScMatrixRef xResMat;
3850     double nMax = std::numeric_limits<double>::lowest();
3851     auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMax )
3852     {
3853         double fVecRes = xResMat->GetDouble(0,i);
3854         if (fVecRes < fCurMax)
3855             xResMat->PutDouble( fCurMax, 0,i);
3856     };
3857     const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3858     size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3859 
3860     double nVal = 0.0;
3861     ScAddress aAdr;
3862     ScRange aRange;
3863     size_t nRefInList = 0;
3864     while (nParamCount-- > 0)
3865     {
3866         switch (GetStackType())
3867         {
3868             case svDouble :
3869             {
3870                 nVal = GetDouble();
3871                 if (nMax < nVal) nMax = nVal;
3872                 nFuncFmtType = SvNumFormatType::NUMBER;
3873             }
3874             break;
3875             case svSingleRef :
3876             {
3877                 PopSingleRef( aAdr );
3878                 ScRefCellValue aCell(mrDoc, aAdr);
3879                 if (aCell.hasNumeric())
3880                 {
3881                     nVal = GetCellValue(aAdr, aCell);
3882                     CurFmtToFuncFmt();
3883                     if (nMax < nVal) nMax = nVal;
3884                 }
3885                 else if (bTextAsZero && aCell.hasString())
3886                 {
3887                     if ( nMax < 0.0 )
3888                         nMax = 0.0;
3889                 }
3890             }
3891             break;
3892             case svRefList :
3893             {
3894                 // bDoMatOp only for non-array value when switching to
3895                 // ArrayRefList.
3896                 if (SwitchToArrayRefList( xResMat, nMatRows, nMax, MatOpFunc,
3897                             nRefArrayPos == std::numeric_limits<size_t>::max()))
3898                 {
3899                     nRefArrayPos = nRefInList;
3900                 }
3901             }
3902             [[fallthrough]];
3903             case svDoubleRef :
3904             {
3905                 FormulaError nErr = FormulaError::NONE;
3906                 PopDoubleRef( aRange, nParamCount, nRefInList);
3907                 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
3908                 if (aValIter.GetFirst(nVal, nErr))
3909                 {
3910                     if (nMax < nVal)
3911                         nMax = nVal;
3912                     aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
3913                     while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3914                     {
3915                         if (nMax < nVal)
3916                             nMax = nVal;
3917                     }
3918                     SetError(nErr);
3919                 }
3920                 if (nRefArrayPos != std::numeric_limits<size_t>::max())
3921                 {
3922                     // Update vector element with current value.
3923                     MatOpFunc( nRefArrayPos, nMax);
3924 
3925                     // Reset.
3926                     nMax = std::numeric_limits<double>::lowest();
3927                     nVal = 0.0;
3928                     nRefArrayPos = std::numeric_limits<size_t>::max();
3929                 }
3930             }
3931             break;
3932             case svMatrix :
3933             case svExternalSingleRef:
3934             case svExternalDoubleRef:
3935             {
3936                 ScMatrixRef pMat = GetMatrix();
3937                 if (pMat)
3938                 {
3939                     nFuncFmtType = SvNumFormatType::NUMBER;
3940                     nVal = pMat->GetMaxValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
3941                     if (nMax < nVal)
3942                         nMax = nVal;
3943                 }
3944             }
3945             break;
3946             case svString :
3947             {
3948                 Pop();
3949                 if ( bTextAsZero )
3950                 {
3951                     if ( nMax < 0.0 )
3952                         nMax = 0.0;
3953                 }
3954                 else
3955                     SetError(FormulaError::IllegalParameter);
3956             }
3957             break;
3958             default :
3959                 PopError();
3960                 SetError(FormulaError::IllegalParameter);
3961         }
3962     }
3963 
3964     if (xResMat)
3965     {
3966         // Include value of last non-references-array type and calculate final result.
3967         if (nMax > std::numeric_limits<double>::lowest())
3968         {
3969             for (SCSIZE i=0; i < nMatRows; ++i)
3970             {
3971                 MatOpFunc( i, nMax);
3972             }
3973         }
3974         else
3975         {
3976             /* TODO: the awkward "no value is maximum 0.0" is likely the case
3977              * if a value is numeric_limits::lowest. Still, that could be a
3978              * valid maximum value as well, but nVal and nMax had been reset
3979              * after the last svRefList... so we may lie here. */
3980             for (SCSIZE i=0; i < nMatRows; ++i)
3981             {
3982                 double fVecRes = xResMat->GetDouble(0,i);
3983                 if (fVecRes == -std::numeric_limits<double>::max())
3984                     xResMat->PutDouble( 0.0, 0,i);
3985             }
3986         }
3987         PushMatrix( xResMat);
3988     }
3989     else
3990     {
3991         if (!std::isfinite(nVal))
3992             PushError( GetDoubleErrorValue( nVal));
3993         else if ( nVal > nMax  )
3994             PushDouble(0.0);    // zero or only empty arguments
3995         else
3996             PushDouble(nMax);
3997     }
3998 }
3999 
4000 void ScInterpreter::GetStVarParams( bool bTextAsZero, double(*VarResult)( double fVal, size_t nValCount ) )
4001 {
4002     short nParamCount = GetByte();
4003     const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
4004 
4005     struct ArrayRefListValue
4006     {
4007         std::vector<double> mvValues;
4008         KahanSum mfSum;
4009         ArrayRefListValue() = default;
4010         double get() const { return mfSum.get(); }
4011     };
4012     std::vector<ArrayRefListValue> vArrayValues;
4013 
4014     std::vector<double> values;
4015     KahanSum fSum    = 0.0;
4016     double fVal = 0.0;
4017     ScAddress aAdr;
4018     ScRange aRange;
4019     size_t nRefInList = 0;
4020     while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
4021     {
4022         switch (GetStackType())
4023         {
4024             case svDouble :
4025             {
4026                 fVal = GetDouble();
4027                 if (nGlobalError == FormulaError::NONE)
4028                 {
4029                     values.push_back(fVal);
4030                     fSum    += fVal;
4031                 }
4032             }
4033             break;
4034             case svSingleRef :
4035             {
4036                 PopSingleRef( aAdr );
4037                 ScRefCellValue aCell(mrDoc, aAdr);
4038                 if (aCell.hasNumeric())
4039                 {
4040                     fVal = GetCellValue(aAdr, aCell);
4041                     if (nGlobalError == FormulaError::NONE)
4042                     {
4043                         values.push_back(fVal);
4044                         fSum += fVal;
4045                     }
4046                 }
4047                 else if (bTextAsZero && aCell.hasString())
4048                 {
4049                     values.push_back(0.0);
4050                 }
4051             }
4052             break;
4053             case svRefList :
4054             {
4055                 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
4056                 if (p && p->IsArrayResult())
4057                 {
4058                     size_t nRefArrayPos = nRefInList;
4059                     if (vArrayValues.empty())
4060                     {
4061                         // Create and init all elements with current value.
4062                         assert(nMatRows > 0);
4063                         vArrayValues.resize(nMatRows);
4064                         for (ArrayRefListValue & it : vArrayValues)
4065                         {
4066                             it.mvValues = values;
4067                             it.mfSum = fSum;
4068                         }
4069                     }
4070                     else
4071                     {
4072                         // Current value and values from vector are operands
4073                         // for each vector position.
4074                         for (ArrayRefListValue & it : vArrayValues)
4075                         {
4076                             it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
4077                             it.mfSum += fSum;
4078                         }
4079                     }
4080                     ArrayRefListValue& rArrayValue = vArrayValues[nRefArrayPos];
4081                     FormulaError nErr = FormulaError::NONE;
4082                     PopDoubleRef( aRange, nParamCount, nRefInList);
4083                     ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
4084                     if (aValIter.GetFirst(fVal, nErr))
4085                     {
4086                         do
4087                         {
4088                             rArrayValue.mvValues.push_back(fVal);
4089                             rArrayValue.mfSum += fVal;
4090                         }
4091                         while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4092                     }
4093                     if ( nErr != FormulaError::NONE )
4094                     {
4095                         rArrayValue.mfSum = CreateDoubleError( nErr);
4096                     }
4097                     // Reset.
4098                     std::vector<double>().swap(values);
4099                     fSum = 0.0;
4100                     break;
4101                 }
4102             }
4103             [[fallthrough]];
4104             case svDoubleRef :
4105             {
4106                 FormulaError nErr = FormulaError::NONE;
4107                 PopDoubleRef( aRange, nParamCount, nRefInList);
4108                 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
4109                 if (aValIter.GetFirst(fVal, nErr))
4110                 {
4111                     do
4112                     {
4113                         values.push_back(fVal);
4114                         fSum += fVal;
4115                     }
4116                     while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4117                 }
4118                 if ( nErr != FormulaError::NONE )
4119                 {
4120                     SetError(nErr);
4121                 }
4122             }
4123             break;
4124             case svExternalSingleRef :
4125             case svExternalDoubleRef :
4126             case svMatrix :
4127             {
4128                 ScMatrixRef pMat = GetMatrix();
4129                 if (pMat)
4130                 {
4131                     const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal);
4132                     SCSIZE nC, nR;
4133                     pMat->GetDimensions(nC, nR);
4134                     for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
4135                     {
4136                         for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
4137                         {
4138                             if (!pMat->IsStringOrEmpty(nMatCol,nMatRow))
4139                             {
4140                                 fVal= pMat->GetDouble(nMatCol,nMatRow);
4141                                 if (nGlobalError == FormulaError::NONE)
4142                                 {
4143                                     values.push_back(fVal);
4144                                     fSum += fVal;
4145                                 }
4146                                 else if (bIgnoreErrVal)
4147                                     nGlobalError = FormulaError::NONE;
4148                             }
4149                             else if ( bTextAsZero )
4150                             {
4151                                 values.push_back(0.0);
4152                             }
4153                         }
4154                     }
4155                 }
4156             }
4157             break;
4158             case svString :
4159             {
4160                 Pop();
4161                 if ( bTextAsZero )
4162                 {
4163                     values.push_back(0.0);
4164                 }
4165                 else
4166                     SetError(FormulaError::IllegalParameter);
4167             }
4168             break;
4169             default :
4170                 PopError();
4171                 SetError(FormulaError::IllegalParameter);
4172         }
4173     }
4174 
4175     if (!vArrayValues.empty())
4176     {
4177         // Include value of last non-references-array type and calculate final result.
4178         if (!values.empty())
4179         {
4180             for (auto & it : vArrayValues)
4181             {
4182                 it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
4183                 it.mfSum += fSum;
4184             }
4185         }
4186         ScMatrixRef xResMat = GetNewMat( 1, nMatRows, true);
4187         for (SCSIZE r=0; r < nMatRows; ++r)
4188         {
4189             ::std::vector<double>::size_type n = vArrayValues[r].mvValues.size();
4190             if (!n)
4191                 xResMat->PutError( FormulaError::DivisionByZero, 0, r);
4192             else
4193             {
4194                 ArrayRefListValue& rArrayValue = vArrayValues[r];
4195                 double vSum = 0.0;
4196                 const double vMean = rArrayValue.get() / n;
4197                 for (::std::vector<double>::size_type i = 0; i < n; i++)
4198                     vSum += ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean) *
4199                         ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean);
4200                 xResMat->PutDouble( VarResult( vSum, n), 0, r);
4201             }
4202         }
4203         PushMatrix( xResMat);
4204     }
4205     else
4206     {
4207         ::std::vector<double>::size_type n = values.size();
4208         if (!n)
4209             SetError( FormulaError::DivisionByZero);
4210         double vSum = 0.0;
4211         if (nGlobalError == FormulaError::NONE)
4212         {
4213             const double vMean = fSum.get() / n;
4214             for (::std::vector<double>::size_type i = 0; i < n; i++)
4215                 vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean);
4216         }
4217         PushDouble( VarResult( vSum, n));
4218     }
4219 }
4220 
4221 void ScInterpreter::ScVar( bool bTextAsZero )
4222 {
4223     auto VarResult = []( double fVal, size_t nValCount )
4224     {
4225         if (nValCount <= 1)
4226             return CreateDoubleError( FormulaError::DivisionByZero );
4227         else
4228             return fVal / (nValCount - 1);
4229     };
4230     GetStVarParams( bTextAsZero, VarResult );
4231 }
4232 
4233 void ScInterpreter::ScVarP( bool bTextAsZero )
4234 {
4235     auto VarResult = []( double fVal, size_t nValCount )
4236     {
4237         return sc::div( fVal, nValCount);
4238     };
4239     GetStVarParams( bTextAsZero, VarResult );
4240 
4241 }
4242 
4243 void ScInterpreter::ScStDev( bool bTextAsZero )
4244 {
4245     auto VarResult = []( double fVal, size_t nValCount )
4246     {
4247         if (nValCount <= 1)
4248             return CreateDoubleError( FormulaError::DivisionByZero );
4249         else
4250             return sqrt( fVal / (nValCount - 1));
4251     };
4252     GetStVarParams( bTextAsZero, VarResult );
4253 }
4254 
4255 void ScInterpreter::ScStDevP( bool bTextAsZero )
4256 {
4257     auto VarResult = []( double fVal, size_t nValCount )
4258     {
4259         if (nValCount == 0)
4260             return CreateDoubleError( FormulaError::DivisionByZero );
4261         else
4262             return sqrt( fVal / nValCount);
4263     };
4264     GetStVarParams( bTextAsZero, VarResult );
4265 
4266     /* this was: PushDouble( sqrt( div( nVal, nValCount)));
4267      *
4268      * Besides that the special NAN gets lost in the call through sqrt(),
4269      * unxlngi6.pro then looped back and forth somewhere between div() and
4270      * ::rtl::math::setNan(). Tests showed that
4271      *
4272      *      sqrt( div( 1, 0));
4273      *
4274      * produced a loop, but
4275      *
4276      *      double f1 = div( 1, 0);
4277      *      sqrt( f1 );
4278      *
4279      * was fine. There seems to be some compiler optimization problem. It does
4280      * not occur when compiled with debug=t.
4281      */
4282 }
4283 
4284 void ScInterpreter::ScColumns()
4285 {
4286     sal_uInt8 nParamCount = GetByte();
4287     sal_uLong nVal = 0;
4288     SCCOL nCol1;
4289     SCROW nRow1;
4290     SCTAB nTab1;
4291     SCCOL nCol2;
4292     SCROW nRow2;
4293     SCTAB nTab2;
4294     while (nParamCount-- > 0)
4295     {
4296         switch ( GetStackType() )
4297         {
4298             case svSingleRef:
4299                 PopError();
4300                 nVal++;
4301                 break;
4302             case svDoubleRef:
4303                 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4304                 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4305                     static_cast<sal_uLong>(nCol2 - nCol1 + 1);
4306                 break;
4307             case svMatrix:
4308             {
4309                 ScMatrixRef pMat = PopMatrix();
4310                 if (pMat)
4311                 {
4312                     SCSIZE nC, nR;
4313                     pMat->GetDimensions(nC, nR);
4314                     nVal += nC;
4315                 }
4316             }
4317             break;
4318             case svExternalSingleRef:
4319                 PopError();
4320                 nVal++;
4321             break;
4322             case svExternalDoubleRef:
4323             {
4324                 sal_uInt16 nFileId;
4325                 OUString aTabName;
4326                 ScComplexRefData aRef;
4327                 PopExternalDoubleRef( nFileId, aTabName, aRef);
4328                 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4329                 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4330                     static_cast<sal_uLong>(aAbs.aEnd.Col() - aAbs.aStart.Col() + 1);
4331             }
4332             break;
4333             default:
4334                 PopError();
4335                 SetError(FormulaError::IllegalParameter);
4336         }
4337     }
4338     PushDouble(static_cast<double>(nVal));
4339 }
4340 
4341 void ScInterpreter::ScRows()
4342 {
4343     sal_uInt8 nParamCount = GetByte();
4344     sal_uLong nVal = 0;
4345     SCCOL nCol1;
4346     SCROW nRow1;
4347     SCTAB nTab1;
4348     SCCOL nCol2;
4349     SCROW nRow2;
4350     SCTAB nTab2;
4351     while (nParamCount-- > 0)
4352     {
4353         switch ( GetStackType() )
4354         {
4355             case svSingleRef:
4356                 PopError();
4357                 nVal++;
4358                 break;
4359             case svDoubleRef:
4360                 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4361                 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4362                     static_cast<sal_uLong>(nRow2 - nRow1 + 1);
4363                 break;
4364             case svMatrix:
4365             {
4366                 ScMatrixRef pMat = PopMatrix();
4367                 if (pMat)
4368                 {
4369                     SCSIZE nC, nR;
4370                     pMat->GetDimensions(nC, nR);
4371                     nVal += nR;
4372                 }
4373             }
4374             break;
4375             case svExternalSingleRef:
4376                 PopError();
4377                 nVal++;
4378             break;
4379             case svExternalDoubleRef:
4380             {
4381                 sal_uInt16 nFileId;
4382                 OUString aTabName;
4383                 ScComplexRefData aRef;
4384                 PopExternalDoubleRef( nFileId, aTabName, aRef);
4385                 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4386                 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4387                     static_cast<sal_uLong>(aAbs.aEnd.Row() - aAbs.aStart.Row() + 1);
4388             }
4389             break;
4390             default:
4391                 PopError();
4392                 SetError(FormulaError::IllegalParameter);
4393         }
4394     }
4395     PushDouble(static_cast<double>(nVal));
4396 }
4397 
4398 void ScInterpreter::ScSheets()
4399 {
4400     sal_uInt8 nParamCount = GetByte();
4401     sal_uLong nVal;
4402     if ( nParamCount == 0 )
4403         nVal = mrDoc.GetTableCount();
4404     else
4405     {
4406         nVal = 0;
4407         SCCOL nCol1;
4408         SCROW nRow1;
4409         SCTAB nTab1;
4410         SCCOL nCol2;
4411         SCROW nRow2;
4412         SCTAB nTab2;
4413         while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
4414         {
4415             switch ( GetStackType() )
4416             {
4417                 case svSingleRef:
4418                 case svExternalSingleRef:
4419                     PopError();
4420                     nVal++;
4421                 break;
4422                 case svDoubleRef:
4423                     PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4424                     nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1);
4425                 break;
4426                 case svExternalDoubleRef:
4427                 {
4428                     sal_uInt16 nFileId;
4429                     OUString aTabName;
4430                     ScComplexRefData aRef;
4431                     PopExternalDoubleRef( nFileId, aTabName, aRef);
4432                     ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4433                     nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1);
4434                 }
4435                 break;
4436                 default:
4437                     PopError();
4438                     SetError( FormulaError::IllegalParameter );
4439             }
4440         }
4441     }
4442     PushDouble( static_cast<double>(nVal) );
4443 }
4444 
4445 void ScInterpreter::ScColumn()
4446 {
4447     sal_uInt8 nParamCount = GetByte();
4448     if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4449         return;
4450 
4451     double nVal = 0.0;
4452     if (nParamCount == 0)
4453     {
4454         nVal = aPos.Col() + 1;
4455         if (bMatrixFormula)
4456         {
4457             SCCOL nCols = 0;
4458             SCROW nRows = 0;
4459             if (pMyFormulaCell)
4460                 pMyFormulaCell->GetMatColsRows( nCols, nRows);
4461             bool bMayBeScalar;
4462             if (nCols == 0)
4463             {
4464                 // Happens if called via ScViewFunc::EnterMatrix()
4465                 // ScFormulaCell::GetResultDimensions() as of course a
4466                 // matrix result is not available yet.
4467                 nCols = 1;
4468                 bMayBeScalar = false;
4469             }
4470             else
4471             {
4472                 bMayBeScalar = true;
4473             }
4474             if (!bMayBeScalar || nCols != 1 || nRows != 1)
4475             {
4476                 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1, /*bEmpty*/true );
4477                 if (pResMat)
4478                 {
4479                     for (SCCOL i=0; i < nCols; ++i)
4480                         pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0);
4481                     PushMatrix( pResMat);
4482                     return;
4483                 }
4484             }
4485         }
4486     }
4487     else
4488     {
4489         switch ( GetStackType() )
4490         {
4491             case svSingleRef :
4492             {
4493                 SCCOL nCol1(0);
4494                 SCROW nRow1(0);
4495                 SCTAB nTab1(0);
4496                 PopSingleRef( nCol1, nRow1, nTab1 );
4497                 nVal = static_cast<double>(nCol1 + 1);
4498             }
4499             break;
4500             case svExternalSingleRef :
4501             {
4502                 sal_uInt16 nFileId;
4503                 OUString aTabName;
4504                 ScSingleRefData aRef;
4505                 PopExternalSingleRef( nFileId, aTabName, aRef );
4506                 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4507                 nVal = static_cast<double>( aAbsRef.Col() + 1 );
4508             }
4509             break;
4510 
4511             case svDoubleRef :
4512             case svExternalDoubleRef :
4513             {
4514                 SCCOL nCol1;
4515                 SCCOL nCol2;
4516                 if ( GetStackType() == svDoubleRef )
4517                 {
4518                     SCROW nRow1;
4519                     SCTAB nTab1;
4520                     SCROW nRow2;
4521                     SCTAB nTab2;
4522                     PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4523                 }
4524                 else
4525                 {
4526                     sal_uInt16 nFileId;
4527                     OUString aTabName;
4528                     ScComplexRefData aRef;
4529                     PopExternalDoubleRef( nFileId, aTabName, aRef );
4530                     ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4531                     nCol1 = aAbs.aStart.Col();
4532                     nCol2 = aAbs.aEnd.Col();
4533                 }
4534                 if (nCol2 > nCol1)
4535                 {
4536                     ScMatrixRef pResMat = GetNewMat(
4537                             static_cast<SCSIZE>(nCol2-nCol1+1), 1, /*bEmpty*/true);
4538                     if (pResMat)
4539                     {
4540                         for (SCCOL i = nCol1; i <= nCol2; i++)
4541                             pResMat->PutDouble(static_cast<double>(i+1),
4542                                     static_cast<SCSIZE>(i-nCol1), 0);
4543                         PushMatrix(pResMat);
4544                         return;
4545                     }
4546                 }
4547                 else
4548                     nVal = static_cast<double>(nCol1 + 1);
4549             }
4550             break;
4551             default:
4552                 SetError( FormulaError::IllegalParameter );
4553         }
4554     }
4555     PushDouble( nVal );
4556 }
4557 
4558 void ScInterpreter::ScRow()
4559 {
4560     sal_uInt8 nParamCount = GetByte();
4561     if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4562         return;
4563 
4564     double nVal = 0.0;
4565     if (nParamCount == 0)
4566     {
4567         nVal = aPos.Row() + 1;
4568         if (bMatrixFormula)
4569         {
4570             SCCOL nCols = 0;
4571             SCROW nRows = 0;
4572             if (pMyFormulaCell)
4573                 pMyFormulaCell->GetMatColsRows( nCols, nRows);
4574             bool bMayBeScalar;
4575             if (nRows == 0)
4576             {
4577                 // Happens if called via ScViewFunc::EnterMatrix()
4578                 // ScFormulaCell::GetResultDimensions() as of course a
4579                 // matrix result is not available yet.
4580                 nRows = 1;
4581                 bMayBeScalar = false;
4582             }
4583             else
4584             {
4585                 bMayBeScalar = true;
4586             }
4587             if (!bMayBeScalar || nCols != 1 || nRows != 1)
4588             {
4589                 ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows), /*bEmpty*/true);
4590                 if (pResMat)
4591                 {
4592                     for (SCROW i=0; i < nRows; i++)
4593                         pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i));
4594                     PushMatrix( pResMat);
4595                     return;
4596                 }
4597             }
4598         }
4599     }
4600     else
4601     {
4602         switch ( GetStackType() )
4603         {
4604             case svSingleRef :
4605             {
4606                 SCCOL nCol1(0);
4607                 SCROW nRow1(0);
4608                 SCTAB nTab1(0);
4609                 PopSingleRef( nCol1, nRow1, nTab1 );
4610                 nVal = static_cast<double>(nRow1 + 1);
4611             }
4612             break;
4613             case svExternalSingleRef :
4614             {
4615                 sal_uInt16 nFileId;
4616                 OUString aTabName;
4617                 ScSingleRefData aRef;
4618                 PopExternalSingleRef( nFileId, aTabName, aRef );
4619                 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4620                 nVal = static_cast<double>( aAbsRef.Row() + 1 );
4621             }
4622             break;
4623             case svDoubleRef :
4624             case svExternalDoubleRef :
4625             {
4626                 SCROW nRow1;
4627                 SCROW nRow2;
4628                 if ( GetStackType() == svDoubleRef )
4629                 {
4630                     SCCOL nCol1;
4631                     SCTAB nTab1;
4632                     SCCOL nCol2;
4633                     SCTAB nTab2;
4634                     PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4635                 }
4636                 else
4637                 {
4638                     sal_uInt16 nFileId;
4639                     OUString aTabName;
4640                     ScComplexRefData aRef;
4641                     PopExternalDoubleRef( nFileId, aTabName, aRef );
4642                     ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4643                     nRow1 = aAbs.aStart.Row();
4644                     nRow2 = aAbs.aEnd.Row();
4645                 }
4646                 if (nRow2 > nRow1)
4647                 {
4648                     ScMatrixRef pResMat = GetNewMat( 1,
4649                             static_cast<SCSIZE>(nRow2-nRow1+1), /*bEmpty*/true);
4650                     if (pResMat)
4651                     {
4652                         for (SCROW i = nRow1; i <= nRow2; i++)
4653                             pResMat->PutDouble(static_cast<double>(i+1), 0,
4654                                     static_cast<SCSIZE>(i-nRow1));
4655                         PushMatrix(pResMat);
4656                         return;
4657                     }
4658                 }
4659                 else
4660                     nVal = static_cast<double>(nRow1 + 1);
4661             }
4662             break;
4663             default:
4664                 SetError( FormulaError::IllegalParameter );
4665         }
4666     }
4667     PushDouble( nVal );
4668 }
4669 
4670 void ScInterpreter::ScSheet()
4671 {
4672     sal_uInt8 nParamCount = GetByte();
4673     if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4674         return;
4675 
4676     SCTAB nVal = 0;
4677     if ( nParamCount == 0 )
4678         nVal = aPos.Tab() + 1;
4679     else
4680     {
4681         switch ( GetStackType() )
4682         {
4683             case svString :
4684             {
4685                 svl::SharedString aStr = PopString();
4686                 if ( mrDoc.GetTable(aStr.getString(), nVal))
4687                     ++nVal;
4688                 else
4689                     SetError( FormulaError::IllegalArgument );
4690             }
4691             break;
4692             case svSingleRef :
4693             {
4694                 SCCOL nCol1(0);
4695                 SCROW nRow1(0);
4696                 SCTAB nTab1(0);
4697                 PopSingleRef(nCol1, nRow1, nTab1);
4698                 nVal = nTab1 + 1;
4699             }
4700             break;
4701             case svDoubleRef :
4702             {
4703                 SCCOL nCol1;
4704                 SCROW nRow1;
4705                 SCTAB nTab1;
4706                 SCCOL nCol2;
4707                 SCROW nRow2;
4708                 SCTAB nTab2;
4709                 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4710                 nVal = nTab1 + 1;
4711             }
4712             break;
4713             default:
4714                 SetError( FormulaError::IllegalParameter );
4715         }
4716         if ( nGlobalError != FormulaError::NONE )
4717             nVal = 0;
4718     }
4719     PushDouble( static_cast<double>(nVal) );
4720 }
4721 
4722 namespace {
4723 
4724 class VectorMatrixAccessor
4725 {
4726 public:
4727     VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) :
4728         mrMat(rMat), mbColVec(bColVec) {}
4729 
4730     bool IsEmpty(SCSIZE i) const
4731     {
4732         return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0);
4733     }
4734 
4735     bool IsEmptyPath(SCSIZE i) const
4736     {
4737         return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0);
4738     }
4739 
4740     bool IsValue(SCSIZE i) const
4741     {
4742         return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0);
4743     }
4744 
4745     bool IsStringOrEmpty(SCSIZE i) const
4746     {
4747         return mbColVec ? mrMat.IsStringOrEmpty(0, i) : mrMat.IsStringOrEmpty(i, 0);
4748     }
4749 
4750     double GetDouble(SCSIZE i) const
4751     {
4752         return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0);
4753     }
4754 
4755     OUString GetString(SCSIZE i) const
4756     {
4757         return mbColVec ? mrMat.GetString(0, i).getString() : mrMat.GetString(i, 0).getString();
4758     }
4759 
4760     SCSIZE GetElementCount() const
4761     {
4762         SCSIZE nC, nR;
4763         mrMat.GetDimensions(nC, nR);
4764         return mbColVec ? nR : nC;
4765     }
4766 
4767 private:
4768     const ScMatrix& mrMat;
4769     bool mbColVec;
4770 };
4771 
4772 /** returns -1 when the matrix value is smaller than the query value, 0 when
4773     they are equal, and 1 when the matrix value is larger than the query
4774     value. */
4775 sal_Int32 lcl_CompareMatrix2Query(
4776     SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryEntry& rEntry)
4777 {
4778     if (rMat.IsEmpty(i))
4779     {
4780         /* TODO: in case we introduced query for real empty this would have to
4781          * be changed! */
4782         return -1;      // empty always less than anything else
4783     }
4784 
4785     /* FIXME: what is an empty path (result of IF(false;true_path) in
4786      * comparisons? */
4787 
4788     bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
4789     if (rMat.IsValue(i))
4790     {
4791         const double nVal1 = rMat.GetDouble(i);
4792         if (!std::isfinite(nVal1))
4793         {
4794             // XXX Querying for error values is not required, otherwise we'd
4795             // need to check here.
4796             return 1;   // error always greater than numeric or string
4797         }
4798 
4799         if (bByString)
4800             return -1;  // numeric always less than string
4801 
4802         const double nVal2 = rEntry.GetQueryItem().mfVal;
4803         // XXX Querying for error values is not required, otherwise we'd need
4804         // to check here and move that check before the bByString check.
4805         if (nVal1 == nVal2)
4806             return 0;
4807 
4808         return nVal1 < nVal2 ? -1 : 1;
4809     }
4810 
4811     if (!bByString)
4812         return 1;       // string always greater than numeric
4813 
4814     OUString aStr1 = rMat.GetString(i);
4815     OUString aStr2 = rEntry.GetQueryItem().maString.getString();
4816 
4817     return ScGlobal::GetCollator().compareString(aStr1, aStr2); // case-insensitive
4818 }
4819 
4820 /** returns the last item with the identical value as the original item
4821     value. */
4822 void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat,
4823         SCSIZE nMatCount)
4824 {
4825     if (rMat.IsValue(rIndex))
4826     {
4827         double nVal = rMat.GetDouble(rIndex);
4828         while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) &&
4829                 nVal == rMat.GetDouble(rIndex+1))
4830             ++rIndex;
4831     }
4832     // Order of IsEmptyPath, IsEmpty, IsStringOrEmpty is significant!
4833     else if (rMat.IsEmptyPath(rIndex))
4834     {
4835         while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1))
4836             ++rIndex;
4837     }
4838     else if (rMat.IsEmpty(rIndex))
4839     {
4840         while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1))
4841             ++rIndex;
4842     }
4843     else if (rMat.IsStringOrEmpty(rIndex))
4844     {
4845         OUString aStr( rMat.GetString(rIndex));
4846         while (rIndex < nMatCount-1 && rMat.IsStringOrEmpty(rIndex+1) &&
4847                 aStr == rMat.GetString(rIndex+1))
4848             ++rIndex;
4849     }
4850     else
4851     {
4852         OSL_FAIL("lcl_GetLastMatch: unhandled matrix type");
4853     }
4854 }
4855 
4856 }
4857 
4858 void ScInterpreter::ScMatch()
4859 {
4860 
4861     sal_uInt8 nParamCount = GetByte();
4862     if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
4863         return;
4864 
4865     double fTyp;
4866     if (nParamCount == 3)
4867         fTyp = GetDouble();
4868     else
4869         fTyp = 1.0;
4870     SCCOL nCol1 = 0;
4871     SCROW nRow1 = 0;
4872     SCTAB nTab1 = 0;
4873     SCCOL nCol2 = 0;
4874     SCROW nRow2 = 0;
4875     ScMatrixRef pMatSrc = nullptr;
4876 
4877     switch (GetStackType())
4878     {
4879         case svSingleRef:
4880             PopSingleRef( nCol1, nRow1, nTab1);
4881             nCol2 = nCol1;
4882             nRow2 = nRow1;
4883         break;
4884         case svDoubleRef:
4885         {
4886             SCTAB nTab2 = 0;
4887             PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4888             if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2))
4889             {
4890                 PushIllegalParameter();
4891                 return;
4892             }
4893         }
4894         break;
4895         case svMatrix:
4896         case svExternalDoubleRef:
4897         {
4898             if (GetStackType() == svMatrix)
4899                 pMatSrc = PopMatrix();
4900             else
4901                 PopExternalDoubleRef(pMatSrc);
4902 
4903             if (!pMatSrc)
4904             {
4905                 PushIllegalParameter();
4906                 return;
4907             }
4908         }
4909         break;
4910         default:
4911             PushIllegalParameter();
4912             return;
4913     }
4914 
4915     if (nGlobalError == FormulaError::NONE)
4916     {
4917         double fVal;
4918         ScQueryParam rParam;
4919         rParam.nCol1       = nCol1;
4920         rParam.nRow1       = nRow1;
4921         rParam.nCol2       = nCol2;
4922         rParam.nTab        = nTab1;
4923         const ScComplexRefData* refData = nullptr;
4924 
4925         ScQueryEntry& rEntry = rParam.GetEntry(0);
4926         ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
4927         rEntry.bDoQuery = true;
4928         if (fTyp < 0.0)
4929             rEntry.eOp = SC_GREATER_EQUAL;
4930         else if (fTyp > 0.0)
4931             rEntry.eOp = SC_LESS_EQUAL;
4932         switch ( GetStackType() )
4933         {
4934             case svDouble:
4935             {
4936                 fVal = GetDouble();
4937                 rItem.mfVal = fVal;
4938                 rItem.meType = ScQueryEntry::ByValue;
4939             }
4940             break;
4941             case svString:
4942             {
4943                 rItem.meType = ScQueryEntry::ByString;
4944                 rItem.maString = GetString();
4945             }
4946             break;
4947             case svDoubleRef :
4948                 refData = GetStackDoubleRef();
4949                 [[fallthrough]];
4950             case svSingleRef :
4951             {
4952                 ScAddress aAdr;
4953                 if ( !PopDoubleRefOrSingleRef( aAdr ) )
4954                 {
4955                     PushInt(0);
4956                     return ;
4957                 }
4958                 ScRefCellValue aCell(mrDoc, aAdr);
4959                 if (aCell.hasNumeric())
4960                 {
4961                     fVal = GetCellValue(aAdr, aCell);
4962                     rItem.meType = ScQueryEntry::ByValue;
4963                     rItem.mfVal = fVal;
4964                 }
4965                 else
4966                 {
4967                     GetCellString(rItem.maString, aCell);
4968                     rItem.meType = ScQueryEntry::ByString;
4969                 }
4970             }
4971             break;
4972             case svExternalSingleRef:
4973             {
4974                 ScExternalRefCache::TokenRef pToken;
4975                 PopExternalSingleRef(pToken);
4976                 if (nGlobalError != FormulaError::NONE)
4977                 {
4978                     PushError( nGlobalError);
4979                     return;
4980                 }
4981                 if (pToken->GetType() == svDouble)
4982                 {
4983                     rItem.meType = ScQueryEntry::ByValue;
4984                     rItem.mfVal = pToken->GetDouble();
4985                 }
4986                 else
4987                 {
4988                     rItem.meType = ScQueryEntry::ByString;
4989                     rItem.maString = pToken->GetString();
4990                 }
4991             }
4992             break;
4993             case svExternalDoubleRef:
4994             case svMatrix :
4995             {
4996                 svl::SharedString aStr;
4997                 ScMatValType nType = GetDoubleOrStringFromMatrix(
4998                         rItem.mfVal, aStr);
4999                 rItem.maString = aStr;
5000                 rItem.meType = ScMatrix::IsNonValueType(nType) ?
5001                     ScQueryEntry::ByString : ScQueryEntry::ByValue;
5002             }
5003             break;
5004             default:
5005             {
5006                 PushIllegalParameter();
5007                 return;
5008             }
5009         }
5010         if (rItem.meType == ScQueryEntry::ByString)
5011         {
5012             bool bIsVBAMode = mrDoc.IsInVBAMode();
5013 
5014             if ( bIsVBAMode )
5015                 rParam.eSearchType = utl::SearchParam::SearchType::Wildcard;
5016             else
5017                 rParam.eSearchType = DetectSearchType(rEntry.GetQueryItem().maString.getString(), mrDoc);
5018         }
5019 
5020         if (pMatSrc) // The source data is matrix array.
5021         {
5022             SCSIZE nC, nR;
5023             pMatSrc->GetDimensions( nC, nR);
5024             if (nC > 1 && nR > 1)
5025             {
5026                 // The source matrix must be a vector.
5027                 PushIllegalParameter();
5028                 return;
5029             }
5030 
5031             // Do not propagate errors from matrix while searching.
5032             pMatSrc->SetErrorInterpreter( nullptr);
5033 
5034             SCSIZE nMatCount = (nC == 1) ? nR : nC;
5035             VectorMatrixAccessor aMatAcc(*pMatSrc, nC == 1);
5036 
5037             // simple serial search for equality mode (source data doesn't
5038             // need to be sorted).
5039 
5040             if (rEntry.eOp == SC_EQUAL)
5041             {
5042                 for (SCSIZE i = 0; i < nMatCount; ++i)
5043                 {
5044                     if (lcl_CompareMatrix2Query( i, aMatAcc, rEntry) == 0)
5045                     {
5046                         PushDouble(i+1); // found !
5047                         return;
5048                     }
5049                 }
5050                 PushNA(); // not found
5051                 return;
5052             }
5053 
5054             // binary search for non-equality mode (the source data is
5055             // assumed to be sorted).
5056 
5057             bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL);
5058             SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0;
5059             for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
5060             {
5061                 SCSIZE nMid = nFirst + nLen/2;
5062                 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rEntry);
5063                 if (nCmp == 0)
5064                 {
5065                     // exact match.  find the last item with the same value.
5066                     lcl_GetLastMatch( nMid, aMatAcc, nMatCount);
5067                     PushDouble( nMid+1);
5068                     return;
5069                 }
5070 
5071                 if (nLen == 1) // first and last items are next to each other.
5072                 {
5073                     if (nCmp < 0)
5074                         nHitIndex = bAscOrder ? nLast : nFirst;
5075                     else
5076                         nHitIndex = bAscOrder ? nFirst : nLast;
5077                     break;
5078                 }
5079 
5080                 if (nCmp < 0)
5081                 {
5082                     if (bAscOrder)
5083                         nFirst = nMid;
5084                     else
5085                         nLast = nMid;
5086                 }
5087                 else
5088                 {
5089                     if (bAscOrder)
5090                         nLast = nMid;
5091                     else
5092                         nFirst = nMid;
5093                 }
5094             }
5095 
5096             if (nHitIndex == nMatCount-1) // last item
5097             {
5098                 sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, aMatAcc, rEntry);
5099                 if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0))
5100                 {
5101                     // either the last item is an exact match or the real
5102                     // hit is beyond the last item.
5103                     PushDouble( nHitIndex+1);
5104                     return;
5105                 }
5106             }
5107 
5108             if (nHitIndex > 0) // valid hit must be 2nd item or higher
5109             {
5110                 if ( ! ( rItem.meType == ScQueryEntry::ByString &&  aMatAcc.IsValue( nHitIndex-1 ) ) &&
5111                      ! ( rItem.meType == ScQueryEntry::ByValue  && !aMatAcc.IsValue( nHitIndex-1 ) ) )
5112                     PushDouble( nHitIndex); // non-exact match
5113                 else
5114                     PushNA();
5115                 return;
5116             }
5117 
5118             PushNA();
5119             return;
5120         }
5121 
5122         // The source data is cell range.
5123         SCCOLROW nDelta = 0;
5124         if (nCol1 == nCol2)
5125         {                                           // search row in column
5126             rParam.nRow2 = nRow2;
5127             rEntry.nField = nCol1;
5128             ScAddress aResultPos( nCol1, nRow1, nTab1);
5129             if (!LookupQueryWithCache( aResultPos, rParam, refData))
5130             {
5131                 PushNA();
5132                 return;
5133             }
5134             nDelta = aResultPos.Row() - nRow1;
5135         }
5136         else
5137         {                                           // search column in row
5138             SCCOL nC;
5139             rParam.bByRow = false;
5140             rParam.nRow2 = nRow1;
5141             rEntry.nField = nCol1;
5142             ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false);
5143             // Advance Entry.nField in Iterator if column changed
5144             aCellIter.SetAdvanceQueryParamEntryField( true );
5145             if (fTyp == 0.0)
5146             {                                       // EQUAL
5147                 if ( aCellIter.GetFirst() )
5148                     nC = aCellIter.GetCol();
5149                 else
5150                 {
5151                     PushNA();
5152                     return;
5153                 }
5154             }
5155             else
5156             {                                       // <= or >=
5157                 SCROW nR;
5158                 if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) )
5159                 {
5160                     PushNA();
5161                     return;
5162                 }
5163             }
5164             nDelta = nC - nCol1;
5165         }
5166         PushDouble(static_cast<double>(nDelta + 1));
5167     }
5168     else
5169         PushIllegalParameter();
5170 }
5171 
5172 namespace {
5173 
5174 bool isCellContentEmpty( const ScRefCellValue& rCell )
5175 {
5176     switch (rCell.getType())
5177     {
5178         case CELLTYPE_VALUE:
5179         case CELLTYPE_STRING:
5180         case CELLTYPE_EDIT:
5181             return false;
5182         case CELLTYPE_FORMULA:
5183         {
5184             // NOTE: Excel treats ="" in a referenced cell as blank in
5185             // COUNTBLANK() but not in ISBLANK(), which is inconsistent.
5186             // COUNTBLANK() tests the (display) result whereas ISBLANK() tests
5187             // the cell content.
5188             // ODFF allows both for COUNTBLANK().
5189             // OOo and LibreOffice prior to 4.4 did not treat ="" as blank in
5190             // COUNTBLANK(), we now do for Excel interoperability.
5191             /* TODO: introduce yet another compatibility option? */
5192             sc::FormulaResultValue aRes = rCell.getFormula()->GetResult();
5193             if (aRes.meType != sc::FormulaResultValue::String)
5194                 return false;
5195             if (!aRes.maString.isEmpty())
5196                 return false;
5197         }
5198         break;
5199         default:
5200             ;
5201     }
5202 
5203     return true;
5204 }
5205 
5206 }
5207 
5208 void ScInterpreter::ScCountEmptyCells()
5209 {
5210     if ( !MustHaveParamCount( GetByte(), 1 ) )
5211         return;
5212 
5213     const SCSIZE nMatRows = GetRefListArrayMaxSize(1);
5214     // There's either one RefList and nothing else, or none.
5215     ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
5216     sal_uLong nMaxCount = 0, nCount = 0;
5217     switch (GetStackType())
5218     {
5219         case svSingleRef :
5220         {
5221             nMaxCount = 1;
5222             ScAddress aAdr;
5223             PopSingleRef( aAdr );
5224             ScRefCellValue aCell(mrDoc, aAdr);
5225             if (!isCellContentEmpty(aCell))
5226                 nCount = 1;
5227         }
5228         break;
5229         case svRefList :
5230         case svDoubleRef :
5231         {
5232             ScRange aRange;
5233             short nParam = 1;
5234             SCSIZE nRefListArrayPos = 0;
5235             size_t nRefInList = 0;
5236             while (nParam-- > 0)
5237             {
5238                 nRefListArrayPos = nRefInList;
5239                 PopDoubleRef( aRange, nParam, nRefInList);
5240                 nMaxCount +=
5241                     static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) *
5242                     static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) *
5243                     static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1);
5244 
5245                 ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags);
5246                 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
5247                 {
5248                     const ScRefCellValue& rCell = aIter.getRefCellValue();
5249                     if (!isCellContentEmpty(rCell))
5250                         ++nCount;
5251                 }
5252                 if (xResMat)
5253                 {
5254                     xResMat->PutDouble( nMaxCount - nCount, 0, nRefListArrayPos);
5255                     nMaxCount = nCount = 0;
5256                 }
5257             }
5258         }
5259         break;
5260         case svMatrix:
5261         case svExternalSingleRef:
5262         case svExternalDoubleRef:
5263         {
5264             ScMatrixRef xMat = GetMatrix();
5265             if (!xMat)
5266                 SetError( FormulaError::IllegalParameter);
5267             else
5268             {
5269                 SCSIZE nC, nR;
5270                 xMat->GetDimensions( nC, nR);
5271                 nMaxCount = nC * nR;
5272                 // Numbers (implicit), strings and error values, ignore empty
5273                 // strings as those if not entered in an inline array are the
5274                 // result of a formula, to be par with a reference to formula
5275                 // cell as *visual* blank, see isCellContentEmpty() above.
5276                 nCount = xMat->Count( true, true, true);
5277             }
5278         }
5279         break;
5280         default : SetError(FormulaError::IllegalParameter); break;
5281     }
5282     if (xResMat)
5283         PushMatrix( xResMat);
5284     else
5285         PushDouble(nMaxCount - nCount);
5286 }
5287 
5288 void ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc )
5289 {
5290     sal_uInt8 nParamCount = GetByte();
5291     if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
5292         return;
5293 
5294     SCCOL nCol3 = 0;
5295     SCROW nRow3 = 0;
5296     SCTAB nTab3 = 0;
5297 
5298     ScMatrixRef pSumExtraMatrix;
5299     bool bSumExtraRange = (nParamCount == 3);
5300     if (bSumExtraRange)
5301     {
5302         // Save only the upperleft cell in case of cell range.  The geometry
5303         // of the 3rd parameter is taken from the 1st parameter.
5304 
5305         switch ( GetStackType() )
5306         {
5307             case svDoubleRef :
5308                 {
5309                     SCCOL nColJunk = 0;
5310                     SCROW nRowJunk = 0;
5311                     SCTAB nTabJunk = 0;
5312                     PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk );
5313                     if ( nTabJunk != nTab3 )
5314                     {
5315                         PushError( FormulaError::IllegalParameter);
5316                         return;
5317                     }
5318                 }
5319                 break;
5320             case svSingleRef :
5321                 PopSingleRef( nCol3, nRow3, nTab3 );
5322                 break;
5323             case svMatrix:
5324                 pSumExtraMatrix = PopMatrix();
5325                 // nCol3, nRow3, nTab3 remain 0
5326                 break;
5327             case svExternalSingleRef:
5328                 {
5329                     pSumExtraMatrix = GetNewMat(1,1);
5330                     ScExternalRefCache::TokenRef pToken;
5331                     PopExternalSingleRef(pToken);
5332                     if (nGlobalError != FormulaError::NONE)
5333                     {
5334                         PushError( nGlobalError);
5335                         return;
5336                     }
5337 
5338                     if (pToken->GetType() == svDouble)
5339                         pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0);
5340                     else
5341                         pSumExtraMatrix->PutString(pToken->GetString(), 0, 0);
5342                 }
5343                 break;
5344             case svExternalDoubleRef:
5345                 PopExternalDoubleRef(pSumExtraMatrix);
5346                 break;
5347             default:
5348                 PushError( FormulaError::IllegalParameter);
5349                 return;
5350         }
5351     }
5352 
5353     svl::SharedString aString;
5354     double fVal = 0.0;
5355     bool bIsString = true;
5356     switch ( GetStackType() )
5357     {
5358         case svDoubleRef :
5359         case svSingleRef :
5360             {
5361                 ScAddress aAdr;
5362                 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5363                 {
5364                     PushError( nGlobalError);
5365                     return;
5366                 }
5367 
5368                 ScRefCellValue aCell(mrDoc, aAdr);
5369                 switch (aCell.getType())
5370                 {
5371                     case CELLTYPE_VALUE :
5372                         fVal = GetCellValue(aAdr, aCell);
5373                         bIsString = false;
5374                         break;
5375                     case CELLTYPE_FORMULA :
5376                         if (aCell.getFormula()->IsValue())
5377                         {
5378                             fVal = GetCellValue(aAdr, aCell);
5379                             bIsString = false;
5380                         }
5381                         else
5382                             GetCellString(aString, aCell);
5383                         break;
5384                     case CELLTYPE_STRING :
5385                     case CELLTYPE_EDIT :
5386                         GetCellString(aString, aCell);
5387                         break;
5388                     default:
5389                         fVal = 0.0;
5390                         bIsString = false;
5391                 }
5392             }
5393             break;
5394         case svString:
5395             aString = GetString();
5396             break;
5397         case svMatrix :
5398         case svExternalDoubleRef:
5399             {
5400                 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
5401                 bIsString = ScMatrix::IsRealStringType( nType);
5402             }
5403             break;
5404         case svExternalSingleRef:
5405             {
5406                 ScExternalRefCache::TokenRef pToken;
5407                 PopExternalSingleRef(pToken);
5408                 if (nGlobalError == FormulaError::NONE)
5409                 {
5410                     if (pToken->GetType() == svDouble)
5411                     {
5412                         fVal = pToken->GetDouble();
5413                         bIsString = false;
5414                     }
5415                     else
5416                         aString = pToken->GetString();
5417                 }
5418             }
5419             break;
5420         default:
5421             {
5422                 fVal = GetDouble();
5423                 bIsString = false;
5424             }
5425     }
5426 
5427     KahanSum fSum = 0.0;
5428     double fRes = 0.0;
5429     double fCount = 0.0;
5430     short nParam = 1;
5431     const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
5432     // There's either one RefList and nothing else, or none.
5433     ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
5434     SCSIZE nRefListArrayPos = 0;
5435     size_t nRefInList = 0;
5436     while (nParam-- > 0)
5437     {
5438         SCCOL nCol1 = 0;
5439         SCROW nRow1 = 0;
5440         SCTAB nTab1 = 0;
5441         SCCOL nCol2 = 0;
5442         SCROW nRow2 = 0;
5443         SCTAB nTab2 = 0;
5444         ScMatrixRef pQueryMatrix;
5445         switch ( GetStackType() )
5446         {
5447             case svRefList :
5448                 if (bSumExtraRange)
5449                 {
5450                     /* TODO: this could resolve if all refs are of the same size */
5451                     SetError( FormulaError::IllegalParameter);
5452                 }
5453                 else
5454                 {
5455                     nRefListArrayPos = nRefInList;
5456                     ScRange aRange;
5457                     PopDoubleRef( aRange, nParam, nRefInList);
5458                     aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5459                 }
5460                 break;
5461             case svDoubleRef :
5462                 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
5463                 break;
5464             case svSingleRef :
5465                 PopSingleRef( nCol1, nRow1, nTab1 );
5466                 nCol2 = nCol1;
5467                 nRow2 = nRow1;
5468                 nTab2 = nTab1;
5469                 break;
5470             case svMatrix:
5471             case svExternalSingleRef:
5472             case svExternalDoubleRef:
5473                 {
5474                     pQueryMatrix = GetMatrix();
5475                     if (!pQueryMatrix)
5476                     {
5477                         PushError( FormulaError::IllegalParameter);
5478                         return;
5479                     }
5480                     nCol1 = 0;
5481                     nRow1 = 0;
5482                     nTab1 = 0;
5483                     SCSIZE nC, nR;
5484                     pQueryMatrix->GetDimensions( nC, nR);
5485                     nCol2 = static_cast<SCCOL>(nC - 1);
5486                     nRow2 = static_cast<SCROW>(nR - 1);
5487                     nTab2 = 0;
5488                 }
5489                 break;
5490             default:
5491                 SetError( FormulaError::IllegalParameter);
5492         }
5493         if ( nTab1 != nTab2 )
5494         {
5495             SetError( FormulaError::IllegalParameter);
5496         }
5497 
5498         if (bSumExtraRange)
5499         {
5500             // Take the range geometry of the 1st parameter and apply it to
5501             // the 3rd. If parts of the resulting range would point outside
5502             // the sheet, don't complain but silently ignore and simply cut
5503             // them away, this is what Xcl does :-/
5504 
5505             // For the cut-away part we also don't need to determine the
5506             // criteria match, so shrink the source range accordingly,
5507             // instead of the result range.
5508             SCCOL nColDelta = nCol2 - nCol1;
5509             SCROW nRowDelta = nRow2 - nRow1;
5510             SCCOL nMaxCol;
5511             SCROW nMaxRow;
5512             if (pSumExtraMatrix)
5513             {
5514                 SCSIZE nC, nR;
5515                 pSumExtraMatrix->GetDimensions( nC, nR);
5516                 nMaxCol = static_cast<SCCOL>(nC - 1);
5517                 nMaxRow = static_cast<SCROW>(nR - 1);
5518             }
5519             else
5520             {
5521                 nMaxCol = mrDoc.MaxCol();
5522                 nMaxRow = mrDoc.MaxRow();
5523             }
5524             if (nCol3 + nColDelta > nMaxCol)
5525             {
5526                 SCCOL nNewDelta = nMaxCol - nCol3;
5527                 nCol2 = nCol1 + nNewDelta;
5528             }
5529 
5530             if (nRow3 + nRowDelta > nMaxRow)
5531             {
5532                 SCROW nNewDelta = nMaxRow - nRow3;
5533                 nRow2 = nRow1 + nNewDelta;
5534             }
5535         }
5536         else
5537         {
5538             nCol3 = nCol1;
5539             nRow3 = nRow1;
5540             nTab3 = nTab1;
5541         }
5542 
5543         if (nGlobalError == FormulaError::NONE)
5544         {
5545             ScQueryParam rParam;
5546             rParam.nRow1       = nRow1;
5547             rParam.nRow2       = nRow2;
5548 
5549             ScQueryEntry& rEntry = rParam.GetEntry(0);
5550             ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5551             rEntry.bDoQuery = true;
5552             if (!bIsString)
5553             {
5554                 rItem.meType = ScQueryEntry::ByValue;
5555                 rItem.mfVal = fVal;
5556                 rEntry.eOp = SC_EQUAL;
5557             }
5558             else
5559             {
5560                 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
5561                 if (rItem.meType == ScQueryEntry::ByString)
5562                     rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
5563             }
5564             ScAddress aAdr;
5565             aAdr.SetTab( nTab3 );
5566             rParam.nCol1  = nCol1;
5567             rParam.nCol2  = nCol2;
5568             rEntry.nField = nCol1;
5569             SCCOL nColDiff = nCol3 - nCol1;
5570             SCROW nRowDiff = nRow3 - nRow1;
5571             if (pQueryMatrix)
5572             {
5573                 // Never case-sensitive.
5574                 sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
5575                 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5576                 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
5577                 {
5578                     SetError( FormulaError::IllegalParameter);
5579                 }
5580 
5581                 if (pSumExtraMatrix)
5582                 {
5583                     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5584                     {
5585                         for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5586                         {
5587                             if (pResultMatrix->IsValue( nCol, nRow) &&
5588                                     pResultMatrix->GetDouble( nCol, nRow))
5589                             {
5590                                 SCSIZE nC = nCol + nColDiff;
5591                                 SCSIZE nR = nRow + nRowDiff;
5592                                 if (pSumExtraMatrix->IsValue( nC, nR))
5593                                 {
5594                                     fVal = pSumExtraMatrix->GetDouble( nC, nR);
5595                                     ++fCount;
5596                                     fSum += fVal;
5597                                 }
5598                             }
5599                         }
5600                     }
5601                 }
5602                 else
5603                 {
5604                     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5605                     {
5606                         for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5607                         {
5608                             if (pResultMatrix->GetDouble( nCol, nRow))
5609                             {
5610                                 aAdr.SetCol( nCol + nColDiff);
5611                                 aAdr.SetRow( nRow + nRowDiff);
5612                                 ScRefCellValue aCell(mrDoc, aAdr);
5613                                 if (aCell.hasNumeric())
5614                                 {
5615                                     fVal = GetCellValue(aAdr, aCell);
5616                                     ++fCount;
5617                                     fSum += fVal;
5618                                 }
5619                             }
5620                         }
5621                     }
5622                 }
5623             }
5624             else
5625             {
5626                 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false);
5627                 // Increment Entry.nField in iterator when switching to next column.
5628                 aCellIter.SetAdvanceQueryParamEntryField( true );
5629                 if ( aCellIter.GetFirst() )
5630                 {
5631                     if (pSumExtraMatrix)
5632                     {
5633                         do
5634                         {
5635                             SCSIZE nC = aCellIter.GetCol() + nColDiff;
5636                             SCSIZE nR = aCellIter.GetRow() + nRowDiff;
5637                             if (pSumExtraMatrix->IsValue( nC, nR))
5638                             {
5639                                 fVal = pSumExtraMatrix->GetDouble( nC, nR);
5640                                 ++fCount;
5641                                 fSum += fVal;
5642                             }
5643                         } while ( aCellIter.GetNext() );
5644                     }
5645                     else
5646                     {
5647                         do
5648                         {
5649                             aAdr.SetCol( aCellIter.GetCol() + nColDiff);
5650                             aAdr.SetRow( aCellIter.GetRow() + nRowDiff);
5651                             ScRefCellValue aCell(mrDoc, aAdr);
5652                             if (aCell.hasNumeric())
5653                             {
5654                                 fVal = GetCellValue(aAdr, aCell);
5655                                 ++fCount;
5656                                 fSum += fVal;
5657                             }
5658                         } while ( aCellIter.GetNext() );
5659                     }
5660                 }
5661             }
5662         }
5663         else
5664         {
5665             PushError( FormulaError::IllegalParameter);
5666             return;
5667         }
5668 
5669         switch( eFunc )
5670         {
5671             case ifSUMIF:     fRes = fSum.get(); break;
5672             case ifAVERAGEIF: fRes = div( fSum.get(), fCount ); break;
5673         }
5674         if (xResMat)
5675         {
5676             if (nGlobalError == FormulaError::NONE)
5677                 xResMat->PutDouble( fRes, 0, nRefListArrayPos);
5678             else
5679             {
5680                 xResMat->PutError( nGlobalError, 0, nRefListArrayPos);
5681                 nGlobalError = FormulaError::NONE;
5682             }
5683             fRes = fCount = 0.0;
5684             fSum = 0;
5685         }
5686     }
5687     if (xResMat)
5688         PushMatrix( xResMat);
5689     else
5690         PushDouble( fRes);
5691 }
5692 
5693 void ScInterpreter::ScSumIf()
5694 {
5695     IterateParametersIf( ifSUMIF);
5696 }
5697 
5698 void ScInterpreter::ScAverageIf()
5699 {
5700     IterateParametersIf( ifAVERAGEIF);
5701 }
5702 
5703 void ScInterpreter::ScCountIf()
5704 {
5705     if ( !MustHaveParamCount( GetByte(), 2 ) )
5706         return;
5707 
5708     svl::SharedString aString;
5709     double fVal = 0.0;
5710     bool bIsString = true;
5711     switch ( GetStackType() )
5712     {
5713         case svDoubleRef :
5714         case svSingleRef :
5715         {
5716             ScAddress aAdr;
5717             if ( !PopDoubleRefOrSingleRef( aAdr ) )
5718             {
5719                 PushInt(0);
5720                 return ;
5721             }
5722             ScRefCellValue aCell(mrDoc, aAdr);
5723             switch (aCell.getType())
5724             {
5725                 case CELLTYPE_VALUE :
5726                     fVal = GetCellValue(aAdr, aCell);
5727                     bIsString = false;
5728                     break;
5729                 case CELLTYPE_FORMULA :
5730                     if (aCell.getFormula()->IsValue())
5731                     {
5732                         fVal = GetCellValue(aAdr, aCell);
5733                         bIsString = false;
5734                     }
5735                     else
5736                         GetCellString(aString, aCell);
5737                     break;
5738                 case CELLTYPE_STRING :
5739                 case CELLTYPE_EDIT :
5740                     GetCellString(aString, aCell);
5741                     break;
5742                 default:
5743                     fVal = 0.0;
5744                     bIsString = false;
5745             }
5746         }
5747         break;
5748         case svMatrix:
5749         case svExternalSingleRef:
5750         case svExternalDoubleRef:
5751         {
5752             ScMatValType nType = GetDoubleOrStringFromMatrix(fVal, aString);
5753             bIsString = ScMatrix::IsRealStringType( nType);
5754         }
5755         break;
5756         case svString:
5757             aString = GetString();
5758         break;
5759         default:
5760         {
5761             fVal = GetDouble();
5762             bIsString = false;
5763         }
5764     }
5765     double fCount = 0.0;
5766     short nParam = 1;
5767     const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
5768     // There's either one RefList and nothing else, or none.
5769     ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
5770     SCSIZE nRefListArrayPos = 0;
5771     size_t nRefInList = 0;
5772     while (nParam-- > 0)
5773     {
5774         SCCOL nCol1 = 0;
5775         SCROW nRow1 = 0;
5776         SCTAB nTab1 = 0;
5777         SCCOL nCol2 = 0;
5778         SCROW nRow2 = 0;
5779         SCTAB nTab2 = 0;
5780         ScMatrixRef pQueryMatrix;
5781         const ScComplexRefData* refData = nullptr;
5782         switch ( GetStackType() )
5783         {
5784             case svRefList :
5785                 nRefListArrayPos = nRefInList;
5786                 [[fallthrough]];
5787             case svDoubleRef :
5788                 {
5789                     refData = GetStackDoubleRef(nRefInList);
5790                     ScRange aRange;
5791                     PopDoubleRef( aRange, nParam, nRefInList);
5792                     aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5793                 }
5794                 break;
5795             case svSingleRef :
5796                 PopSingleRef( nCol1, nRow1, nTab1 );
5797                 nCol2 = nCol1;
5798                 nRow2 = nRow1;
5799                 nTab2 = nTab1;
5800                 break;
5801             case svMatrix:
5802             case svExternalSingleRef:
5803             case svExternalDoubleRef:
5804             {
5805                 pQueryMatrix = GetMatrix();
5806                 if (!pQueryMatrix)
5807                 {
5808                     PushIllegalParameter();
5809                     return;
5810                 }
5811                 nCol1 = 0;
5812                 nRow1 = 0;
5813                 nTab1 = 0;
5814                 SCSIZE nC, nR;
5815                 pQueryMatrix->GetDimensions( nC, nR);
5816                 nCol2 = static_cast<SCCOL>(nC - 1);
5817                 nRow2 = static_cast<SCROW>(nR - 1);
5818                 nTab2 = 0;
5819             }
5820             break;
5821             default:
5822                 PopError(); // Propagate it further
5823                 PushIllegalParameter();
5824                 return ;
5825         }
5826         if ( nTab1 != nTab2 )
5827         {
5828             PushIllegalParameter();
5829             return;
5830         }
5831         if (nCol1 > nCol2)
5832         {
5833             PushIllegalParameter();
5834             return;
5835         }
5836         if (nGlobalError == FormulaError::NONE)
5837         {
5838             ScQueryParam rParam;
5839             rParam.nRow1       = nRow1;
5840             rParam.nRow2       = nRow2;
5841             rParam.nTab        = nTab1;
5842 
5843             ScQueryEntry& rEntry = rParam.GetEntry(0);
5844             ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5845             rEntry.bDoQuery = true;
5846             if (!bIsString)
5847             {
5848                 rItem.meType = ScQueryEntry::ByValue;
5849                 rItem.mfVal = fVal;
5850                 rEntry.eOp = SC_EQUAL;
5851             }
5852             else
5853             {
5854                 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
5855                 if (rItem.meType == ScQueryEntry::ByString)
5856                     rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
5857             }
5858             rParam.nCol1  = nCol1;
5859             rParam.nCol2  = nCol2;
5860             rEntry.nField = nCol1;
5861             if (pQueryMatrix)
5862             {
5863                 // Never case-sensitive.
5864                 sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
5865                 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5866                 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
5867                 {
5868                     PushIllegalParameter();
5869                     return;
5870                 }
5871 
5872                 SCSIZE nSize = pResultMatrix->GetElementCount();
5873                 for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex)
5874                 {
5875                     if (pResultMatrix->IsValue( nIndex) &&
5876                             pResultMatrix->GetDouble( nIndex))
5877                         ++fCount;
5878                 }
5879             }
5880             else
5881             {
5882                 if(ScCountIfCellIteratorSortedCache::CanBeUsed(mrDoc, rParam, nTab1, pMyFormulaCell,
5883                         refData, mrContext))
5884                 {
5885                     ScCountIfCellIteratorSortedCache aCellIter(mrDoc, mrContext, nTab1, rParam, false);
5886                     fCount += aCellIter.GetCount();
5887                 }
5888                 else
5889                 {
5890                     ScCountIfCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false);
5891                     fCount += aCellIter.GetCount();
5892                 }
5893             }
5894         }
5895         else
5896         {
5897             PushIllegalParameter();
5898             return;
5899         }
5900         if (xResMat)
5901         {
5902             xResMat->PutDouble( fCount, 0, nRefListArrayPos);
5903             fCount = 0.0;
5904         }
5905     }
5906     if (xResMat)
5907         PushMatrix( xResMat);
5908     else
5909         PushDouble(fCount);
5910 }
5911 
5912 void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIfsResult& rRes ) )
5913 {
5914     sal_uInt8 nParamCount = GetByte();
5915     sal_uInt8 nQueryCount = nParamCount / 2;
5916 
5917     std::vector<sal_uInt8>& vConditions = mrContext.maConditions;
5918     // vConditions is cached, although it is clear'ed after every cell is interpreted,
5919     // if the SUMIFS/COUNTIFS are part of a matrix formula, then that is not enough because
5920     // with a single InterpretTail() call it results in evaluation of all the cells in the
5921     // matrix formula.
5922     vConditions.clear();
5923 
5924     // Range-reduce optimization
5925     SCCOL nStartColDiff = 0;
5926     SCCOL nEndColDiff = 0;
5927     SCROW nStartRowDiff = 0;
5928     SCROW nEndRowDiff = 0;
5929     bool bRangeReduce = false;
5930     ScRange aMainRange;
5931 
5932     bool bHasDoubleRefCriteriaRanges = true;
5933     // Do not attempt main-range reduce if any of the criteria-ranges are not double-refs.
5934     // For COUNTIFS queries it's possible to range-reduce too, if the query is not supposed
5935     // to match empty cells (will be checked and undone later if needed), so simply treat
5936     // the first criteria range as the main range for purposes of detecting if this can be done.
5937     for (sal_uInt16 nParamIdx = 2; nParamIdx < nParamCount; nParamIdx += 2 )
5938     {
5939         const formula::FormulaToken* pCriteriaRangeToken = pStack[ sp-nParamIdx ];
5940         if (pCriteriaRangeToken->GetType() != svDoubleRef )
5941         {
5942             bHasDoubleRefCriteriaRanges = false;
5943             break;
5944         }
5945     }
5946 
5947     // Probe the main range token, and try if we can shrink the range without altering results.
5948     const formula::FormulaToken* pMainRangeToken = pStack[ sp-nParamCount ];
5949     if (pMainRangeToken->GetType() == svDoubleRef && bHasDoubleRefCriteriaRanges)
5950     {
5951         const ScComplexRefData* pRefData = pMainRangeToken->GetDoubleRef();
5952         if (!pRefData->IsDeleted())
5953         {
5954             DoubleRefToRange( *pRefData, aMainRange);
5955             if (aMainRange.aStart.Tab() == aMainRange.aEnd.Tab())
5956             {
5957                 // Shrink the range to actual data content.
5958                 ScRange aSubRange = aMainRange;
5959                 mrDoc.GetDataAreaSubrange(aSubRange);
5960                 nStartColDiff = aSubRange.aStart.Col() - aMainRange.aStart.Col();
5961                 nStartRowDiff = aSubRange.aStart.Row() - aMainRange.aStart.Row();
5962                 nEndColDiff = aSubRange.aEnd.Col() - aMainRange.aEnd.Col();
5963                 nEndRowDiff = aSubRange.aEnd.Row() - aMainRange.aEnd.Row();
5964                 bRangeReduce = nStartColDiff || nStartRowDiff || nEndColDiff || nEndRowDiff;
5965             }
5966         }
5967     }
5968 
5969     double fVal = 0.0;
5970     SCCOL nDimensionCols = 0;
5971     SCROW nDimensionRows = 0;
5972     const SCSIZE nRefArrayRows = GetRefListArrayMaxSize( nParamCount);
5973     std::vector<std::vector<sal_uInt8>> vRefArrayConditions;
5974 
5975     while (nParamCount > 1 && nGlobalError == FormulaError::NONE)
5976     {
5977         // take criteria
5978         svl::SharedString aString;
5979         fVal = 0.0;
5980         bool bIsString = true;
5981         switch ( GetStackType() )
5982         {
5983             case svDoubleRef :
5984             case svSingleRef :
5985                 {
5986                     ScAddress aAdr;
5987                     if ( !PopDoubleRefOrSingleRef( aAdr ) )
5988                     {
5989                         PushError( nGlobalError);
5990                         return;
5991                     }
5992 
5993                     ScRefCellValue aCell(mrDoc, aAdr);
5994                     switch (aCell.getType())
5995                     {
5996                         case CELLTYPE_VALUE :
5997                             fVal = GetCellValue(aAdr, aCell);
5998                             bIsString = false;
5999                             break;
6000                         case CELLTYPE_FORMULA :
6001                             if (aCell.getFormula()->IsValue())
6002                             {
6003                                 fVal = GetCellValue(aAdr, aCell);
6004                                 bIsString = false;
6005                             }
6006                             else
6007                                 GetCellString(aString, aCell);
6008                             break;
6009                         case CELLTYPE_STRING :
6010                         case CELLTYPE_EDIT :
6011                             GetCellString(aString, aCell);
6012                             break;
6013                         default:
6014                             fVal = 0.0;
6015                             bIsString = false;
6016                     }
6017                 }
6018                 break;
6019             case svString:
6020                 aString = GetString();
6021                 break;
6022             case svMatrix :
6023             case svExternalDoubleRef:
6024                 {
6025                     ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
6026                     bIsString = ScMatrix::IsRealStringType( nType);
6027                 }
6028                 break;
6029             case svExternalSingleRef:
6030                 {
6031                     ScExternalRefCache::TokenRef pToken;
6032                     PopExternalSingleRef(pToken);
6033                     if (nGlobalError == FormulaError::NONE)
6034                     {
6035                         if (pToken->GetType() == svDouble)
6036                         {
6037                             fVal = pToken->GetDouble();
6038                             bIsString = false;
6039                         }
6040                         else
6041                             aString = pToken->GetString();
6042                     }
6043                 }
6044                 break;
6045             default:
6046                 {
6047                     fVal = GetDouble();
6048                     bIsString = false;
6049                 }
6050         }
6051 
6052         if (nGlobalError != FormulaError::NONE)
6053         {
6054             PushError( nGlobalError);
6055             return;   // and bail out, no need to evaluate other arguments
6056         }
6057 
6058         // take range
6059         short nParam = nParamCount;
6060         size_t nRefInList = 0;
6061         size_t nRefArrayPos = std::numeric_limits<size_t>::max();
6062         SCCOL nCol1 = 0;
6063         SCROW nRow1 = 0;
6064         SCTAB nTab1 = 0;
6065         SCCOL nCol2 = 0;
6066         SCROW nRow2 = 0;
6067         SCTAB nTab2 = 0;
6068         ScMatrixRef pQueryMatrix;
6069         while (nParam-- == nParamCount)
6070         {
6071             const ScComplexRefData* refData = nullptr;
6072             switch ( GetStackType() )
6073             {
6074                 case svRefList :
6075                     {
6076                         const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
6077                         if (p && p->IsArrayResult())
6078                         {
6079                             if (nRefInList == 0)
6080                             {
6081                                 if (vRefArrayConditions.empty())
6082                                     vRefArrayConditions.resize( nRefArrayRows);
6083                                 if (!vConditions.empty())
6084                                 {
6085                                     // Similar to other reference list array
6086                                     // handling, add/op the current value to
6087                                     // all array positions.
6088                                     for (auto & rVec : vRefArrayConditions)
6089                                     {
6090                                         if (rVec.empty())
6091                                             rVec = vConditions;
6092                                         else
6093                                         {
6094                                             assert(rVec.size() == vConditions.size());  // see dimensions below
6095                                             for (size_t i=0, n = rVec.size(); i < n; ++i)
6096                                             {
6097                                                 rVec[i] += vConditions[i];
6098                                             }
6099                                         }
6100                                     }
6101                                     // Reset condition results.
6102                                     std::for_each( vConditions.begin(), vConditions.end(),
6103                                             [](sal_uInt8 & r){ r = 0.0; } );
6104                                 }
6105                             }
6106                             nRefArrayPos = nRefInList;
6107                         }
6108                         refData = GetStackDoubleRef(nRefInList);
6109                         ScRange aRange;
6110                         PopDoubleRef( aRange, nParam, nRefInList);
6111                         aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
6112                     }
6113                 break;
6114                 case svDoubleRef :
6115                     refData = GetStackDoubleRef();
6116                     PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
6117                 break;
6118                 case svSingleRef :
6119                     PopSingleRef( nCol1, nRow1, nTab1 );
6120                     nCol2 = nCol1;
6121                     nRow2 = nRow1;
6122                     nTab2 = nTab1;
6123                 break;
6124                 case svMatrix:
6125                 case svExternalSingleRef:
6126                 case svExternalDoubleRef:
6127                     {
6128                         pQueryMatrix = GetMatrix();
6129                         if (!pQueryMatrix)
6130                         {
6131                             PushError( FormulaError::IllegalParameter);
6132                             return;
6133                         }
6134                         nCol1 = 0;
6135                         nRow1 = 0;
6136                         nTab1 = 0;
6137                         SCSIZE nC, nR;
6138                         pQueryMatrix->GetDimensions( nC, nR);
6139                         nCol2 = static_cast<SCCOL>(nC - 1);
6140                         nRow2 = static_cast<SCROW>(nR - 1);
6141                         nTab2 = 0;
6142                     }
6143                 break;
6144                 default:
6145                     PushError( FormulaError::IllegalParameter);
6146                     return;
6147             }
6148             if ( nTab1 != nTab2 )
6149             {
6150                 PushError( FormulaError::IllegalArgument);
6151                 return;
6152             }
6153 
6154             ScQueryParam rParam;
6155             ScQueryEntry& rEntry = rParam.GetEntry(0);
6156             ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6157             rEntry.bDoQuery = true;
6158             if (!bIsString)
6159             {
6160                 rItem.meType = ScQueryEntry::ByValue;
6161                 rItem.mfVal = fVal;
6162                 rEntry.eOp = SC_EQUAL;
6163             }
6164             else
6165             {
6166                 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
6167                 if (rItem.meType == ScQueryEntry::ByString)
6168                     rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
6169             }
6170 
6171             // Undo bRangeReduce if asked to match empty cells for COUNTIFS (which should be rare).
6172             assert(rEntry.GetQueryItems().size() == 1);
6173             const bool isCountIfs = (nParamCount % 2) == 0;
6174             if(isCountIfs && (rEntry.IsQueryByEmpty() || rItem.mbMatchEmpty) && bRangeReduce)
6175             {
6176                 bRangeReduce = false;
6177                 // All criteria ranges are svDoubleRef's, so only vConditions needs adjusting.
6178                 assert(vRefArrayConditions.empty());
6179                 if(!vConditions.empty())
6180                 {
6181                     std::vector<sal_uInt8> newConditions;
6182                     SCCOL newDimensionCols = nCol2 - nCol1 + 1;
6183                     SCROW newDimensionRows = nRow2 - nRow1 + 1;
6184                     newConditions.reserve( newDimensionCols * newDimensionRows );
6185                     SCCOL col = nCol1;
6186                     for(; col < nCol1 + nStartColDiff; ++col)
6187                         newConditions.insert( newConditions.end(), newDimensionRows, 0 );
6188                     for(; col <= nCol2 - nStartColDiff; ++col)
6189                     {
6190                         newConditions.insert( newConditions.end(), nStartRowDiff, 0 );
6191                         SCCOL oldCol = col - ( nCol1 + nStartColDiff );
6192                         size_t nIndex = oldCol * nDimensionRows;
6193                         if (nIndex < vConditions.size())
6194                         {
6195                             auto it = vConditions.begin() + nIndex;
6196                             newConditions.insert( newConditions.end(), it, it + nDimensionRows );
6197                         }
6198                         else
6199                             newConditions.insert( newConditions.end(), nDimensionRows, 0 );
6200                         newConditions.insert( newConditions.end(), -nEndRowDiff, 0 );
6201                     }
6202                     for(; col <= nCol2; ++col)
6203                         newConditions.insert( newConditions.end(), newDimensionRows, 0 );
6204                     assert( newConditions.size() == o3tl::make_unsigned( newDimensionCols * newDimensionRows ));
6205                     vConditions = std::move( newConditions );
6206                     nDimensionCols = newDimensionCols;
6207                     nDimensionRows = newDimensionRows;
6208                 }
6209             }
6210 
6211             if (bRangeReduce)
6212             {
6213                 // All reference ranges must be of the same size as the main range.
6214                 if( aMainRange.aEnd.Col() - aMainRange.aStart.Col() != nCol2 - nCol1
6215                     || aMainRange.aEnd.Row() - aMainRange.aStart.Row() != nRow2 - nRow1)
6216                 {
6217                     PushError ( FormulaError::IllegalArgument);
6218                     return;
6219                 }
6220                 nCol1 += nStartColDiff;
6221                 nRow1 += nStartRowDiff;
6222 
6223                 nCol2 += nEndColDiff;
6224                 nRow2 += nEndRowDiff;
6225             }
6226 
6227             // All reference ranges must be of same dimension and size.
6228             if (!nDimensionCols)
6229                 nDimensionCols = nCol2 - nCol1 + 1;
6230             if (!nDimensionRows)
6231                 nDimensionRows = nRow2 - nRow1 + 1;
6232             if ((nDimensionCols != (nCol2 - nCol1 + 1)) || (nDimensionRows != (nRow2 - nRow1 + 1)))
6233             {
6234                 PushError ( FormulaError::IllegalArgument);
6235                 return;
6236             }
6237 
6238             // recalculate matrix values
6239             if (nGlobalError != FormulaError::NONE)
6240             {
6241                 PushError( nGlobalError);
6242                 return;
6243             }
6244 
6245             // initialize temporary result matrix
6246             if (vConditions.empty())
6247                 vConditions.resize( nDimensionCols * nDimensionRows, 0);
6248 
6249             rParam.nRow1  = nRow1;
6250             rParam.nRow2  = nRow2;
6251             rParam.nCol1  = nCol1;
6252             rParam.nCol2  = nCol2;
6253             rEntry.nField = nCol1;
6254             SCCOL nColDiff = -nCol1;
6255             SCROW nRowDiff = -nRow1;
6256             if (pQueryMatrix)
6257             {
6258                 // Never case-sensitive.
6259                 sc::CompareOptions aOptions(mrDoc, rEntry, rParam.eSearchType);
6260                 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
6261                 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
6262                 {
6263                     PushError( FormulaError::IllegalParameter);
6264                     return;
6265                 }
6266 
6267                 // result matrix is filled with boolean values.
6268                 std::vector<double> aResValues;
6269                 pResultMatrix->GetDoubleArray(aResValues);
6270                 if (vConditions.size() != aResValues.size())
6271                 {
6272                     PushError( FormulaError::IllegalParameter);
6273                     return;
6274                 }
6275 
6276                 std::vector<double>::const_iterator itThisRes = aResValues.begin();
6277                 for (auto& rCondition : vConditions)
6278                 {
6279                     rCondition += *itThisRes;
6280                     ++itThisRes;
6281                 }
6282             }
6283             else
6284             {
6285                 if( ScQueryCellIteratorSortedCache::CanBeUsed( mrDoc, rParam, nTab1, pMyFormulaCell,
6286                         refData, mrContext ))
6287                 {
6288                     ScQueryCellIteratorSortedCache aCellIter(mrDoc, mrContext, nTab1, rParam, false);
6289                     // Increment Entry.nField in iterator when switching to next column.
6290                     aCellIter.SetAdvanceQueryParamEntryField( true );
6291                     if ( aCellIter.GetFirst() )
6292                     {
6293                         do
6294                         {
6295                             size_t nC = aCellIter.GetCol() + nColDiff;
6296                             size_t nR = aCellIter.GetRow() + nRowDiff;
6297                             ++vConditions[nC * nDimensionRows + nR];
6298                         } while ( aCellIter.GetNext() );
6299                     }
6300                 }
6301                 else
6302                 {
6303                     ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false);
6304                     // Increment Entry.nField in iterator when switching to next column.
6305                     aCellIter.SetAdvanceQueryParamEntryField( true );
6306                     if ( aCellIter.GetFirst() )
6307                     {
6308                         do
6309                         {
6310                             size_t nC = aCellIter.GetCol() + nColDiff;
6311                             size_t nR = aCellIter.GetRow() + nRowDiff;
6312                             ++vConditions[nC * nDimensionRows + nR];
6313                         } while ( aCellIter.GetNext() );
6314                     }
6315                 }
6316             }
6317             if (nRefArrayPos != std::numeric_limits<size_t>::max())
6318             {
6319                 // Apply condition result to reference list array result position.
6320                 std::vector<sal_uInt8>& rVec = vRefArrayConditions[nRefArrayPos];
6321                 if (rVec.empty())
6322                     rVec = vConditions;
6323                 else
6324                 {
6325                     assert(rVec.size() == vConditions.size());  // see dimensions above
6326                     for (size_t i=0, n = rVec.size(); i < n; ++i)
6327                     {
6328                         rVec[i] += vConditions[i];
6329                     }
6330                 }
6331                 // Reset conditions vector.
6332                 // When leaving an svRefList this has to be emptied not set to
6333                 // 0.0 because it's checked when entering an svRefList.
6334                 if (nRefInList == 0)
6335                     std::vector<sal_uInt8>().swap( vConditions);
6336                 else
6337                     std::for_each( vConditions.begin(), vConditions.end(), [](sal_uInt8 & r){ r = 0; } );
6338             }
6339         }
6340         nParamCount -= 2;
6341     }
6342 
6343     if (!vRefArrayConditions.empty() && !vConditions.empty())
6344     {
6345         // Add/op the last current value to all array positions.
6346         for (auto & rVec : vRefArrayConditions)
6347         {
6348             if (rVec.empty())
6349                 rVec = vConditions;
6350             else
6351             {
6352                 assert(rVec.size() == vConditions.size());  // see dimensions above
6353                 for (size_t i=0, n = rVec.size(); i < n; ++i)
6354                 {
6355                     rVec[i] += vConditions[i];
6356                 }
6357             }
6358         }
6359     }
6360 
6361     if (nGlobalError != FormulaError::NONE)
6362     {
6363         PushError( nGlobalError);
6364         return;   // bail out
6365     }
6366 
6367     sc::ParamIfsResult aRes;
6368     ScMatrixRef xResMat;
6369 
6370     // main range - only for AVERAGEIFS, SUMIFS, MINIFS and MAXIFS
6371     if (nParamCount == 1)
6372     {
6373         short nParam = nParamCount;
6374         size_t nRefInList = 0;
6375         size_t nRefArrayPos = std::numeric_limits<size_t>::max();
6376         bool bRefArrayMain = false;
6377         while (nParam-- == nParamCount)
6378         {
6379             SCCOL nMainCol1 = 0;
6380             SCROW nMainRow1 = 0;
6381             SCTAB nMainTab1 = 0;
6382             SCCOL nMainCol2 = 0;
6383             SCROW nMainRow2 = 0;
6384             SCTAB nMainTab2 = 0;
6385             ScMatrixRef pMainMatrix;
6386             switch ( GetStackType() )
6387             {
6388                 case svRefList :
6389                     {
6390                         const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
6391                         if (p && p->IsArrayResult())
6392                         {
6393                             if (vRefArrayConditions.empty())
6394                             {
6395                                 // Replicate conditions if there wasn't a
6396                                 // reference list array for criteria
6397                                 // evaluation.
6398                                 vRefArrayConditions.resize( nRefArrayRows);
6399                                 for (auto & rVec : vRefArrayConditions)
6400                                 {
6401                                     rVec = vConditions;
6402                                 }
6403                             }
6404 
6405                             bRefArrayMain = true;
6406                             nRefArrayPos = nRefInList;
6407                         }
6408                         ScRange aRange;
6409                         PopDoubleRef( aRange, nParam, nRefInList);
6410                         aRange.GetVars( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2);
6411                     }
6412                 break;
6413                 case svDoubleRef :
6414                     PopDoubleRef( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2 );
6415                 break;
6416                 case svSingleRef :
6417                     PopSingleRef( nMainCol1, nMainRow1, nMainTab1 );
6418                     nMainCol2 = nMainCol1;
6419                     nMainRow2 = nMainRow1;
6420                     nMainTab2 = nMainTab1;
6421                 break;
6422                 case svMatrix:
6423                 case svExternalSingleRef:
6424                 case svExternalDoubleRef:
6425                     {
6426                         pMainMatrix = GetMatrix();
6427                         if (!pMainMatrix)
6428                         {
6429                             PushError( FormulaError::IllegalParameter);
6430                             return;
6431                         }
6432                         nMainCol1 = 0;
6433                         nMainRow1 = 0;
6434                         nMainTab1 = 0;
6435                         SCSIZE nC, nR;
6436                         pMainMatrix->GetDimensions( nC, nR);
6437                         nMainCol2 = static_cast<SCCOL>(nC - 1);
6438                         nMainRow2 = static_cast<SCROW>(nR - 1);
6439                         nMainTab2 = 0;
6440                     }
6441                 break;
6442                 // Treat a scalar value as 1x1 matrix.
6443                 case svDouble:
6444                     pMainMatrix = GetNewMat(1,1);
6445                     nMainCol1 = nMainCol2 = 0;
6446                     nMainRow1 = nMainRow2 = 0;
6447                     nMainTab1 = nMainTab2 = 0;
6448                     pMainMatrix->PutDouble( GetDouble(), 0, 0);
6449                 break;
6450                 case svString:
6451                     pMainMatrix = GetNewMat(1,1);
6452                     nMainCol1 = nMainCol2 = 0;
6453                     nMainRow1 = nMainRow2 = 0;
6454                     nMainTab1 = nMainTab2 = 0;
6455                     pMainMatrix->PutString( GetString(), 0, 0);
6456                 break;
6457                 default:
6458                     PopError();
6459                     PushError( FormulaError::IllegalParameter);
6460                     return;
6461             }
6462             if ( nMainTab1 != nMainTab2 )
6463             {
6464                 PushError( FormulaError::IllegalArgument);
6465                 return;
6466             }
6467 
6468             if (bRangeReduce)
6469             {
6470                 nMainCol1 += nStartColDiff;
6471                 nMainRow1 += nStartRowDiff;
6472 
6473                 nMainCol2 += nEndColDiff;
6474                 nMainRow2 += nEndRowDiff;
6475             }
6476 
6477             // All reference ranges must be of same dimension and size.
6478             if ((nDimensionCols != (nMainCol2 - nMainCol1 + 1)) || (nDimensionRows != (nMainRow2 - nMainRow1 + 1)))
6479             {
6480                 PushError ( FormulaError::IllegalArgument);
6481                 return;
6482             }
6483 
6484             if (nGlobalError != FormulaError::NONE)
6485             {
6486                 PushError( nGlobalError);
6487                 return;   // bail out
6488             }
6489 
6490             // end-result calculation
6491 
6492             // This gets weird... if conditions were calculated using a
6493             // reference list array but the main calculation range is not a
6494             // reference list array, then the conditions of the array are
6495             // applied to the main range each in turn to form the array result.
6496 
6497             size_t nRefArrayMainPos = (bRefArrayMain ? nRefArrayPos :
6498                     (vRefArrayConditions.empty() ? std::numeric_limits<size_t>::max() : 0));
6499             const bool bAppliedArray = (!bRefArrayMain && nRefArrayMainPos == 0);
6500 
6501             if (nRefArrayMainPos == 0)
6502                 xResMat = GetNewMat( 1, nRefArrayRows, /*bEmpty*/true );
6503 
6504             if (pMainMatrix)
6505             {
6506                 std::vector<double> aMainValues;
6507                 pMainMatrix->GetDoubleArray(aMainValues, false); // Map empty values to NaN's.
6508 
6509                 do
6510                 {
6511                     if (nRefArrayMainPos < vRefArrayConditions.size())
6512                         vConditions = vRefArrayConditions[nRefArrayMainPos];
6513 
6514                     if (vConditions.size() != aMainValues.size())
6515                     {
6516                         PushError( FormulaError::IllegalArgument);
6517                         return;
6518                     }
6519 
6520                     std::vector<sal_uInt8>::const_iterator itRes = vConditions.begin(), itResEnd = vConditions.end();
6521                     std::vector<double>::const_iterator itMain = aMainValues.begin();
6522                     for (; itRes != itResEnd; ++itRes, ++itMain)
6523                     {
6524                         if (*itRes != nQueryCount)
6525                             continue;
6526 
6527                         fVal = *itMain;
6528                         if (GetDoubleErrorValue(fVal) == FormulaError::ElementNaN)
6529                             continue;
6530 
6531                         ++aRes.mfCount;
6532                         aRes.mfSum += fVal;
6533                         if ( aRes.mfMin > fVal )
6534                             aRes.mfMin = fVal;
6535                         if ( aRes.mfMax < fVal )
6536                             aRes.mfMax = fVal;
6537                     }
6538                     if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
6539                     {
6540                         xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
6541                         aRes = sc::ParamIfsResult();
6542                     }
6543                 }
6544                 while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
6545             }
6546             else
6547             {
6548                 ScAddress aAdr;
6549                 aAdr.SetTab( nMainTab1 );
6550                 do
6551                 {
6552                     if (nRefArrayMainPos < vRefArrayConditions.size())
6553                         vConditions = vRefArrayConditions[nRefArrayMainPos];
6554 
6555                     SAL_WARN_IF(nDimensionCols && nDimensionRows && vConditions.empty(), "sc",  "ScInterpreter::IterateParametersIfs vConditions is empty");
6556                     if (!vConditions.empty())
6557                     {
6558                         std::vector<sal_uInt8>::const_iterator itRes = vConditions.begin();
6559                         for (SCCOL nCol = 0; nCol < nDimensionCols; ++nCol)
6560                         {
6561                             for (SCROW nRow = 0; nRow < nDimensionRows; ++nRow, ++itRes)
6562                             {
6563                                 if (*itRes == nQueryCount)
6564                                 {
6565                                     aAdr.SetCol( nCol + nMainCol1);
6566                                     aAdr.SetRow( nRow + nMainRow1);
6567                                     ScRefCellValue aCell(mrDoc, aAdr);
6568                                     if (aCell.hasNumeric())
6569                                     {
6570                                         fVal = GetCellValue(aAdr, aCell);
6571                                         ++aRes.mfCount;
6572                                         aRes.mfSum += fVal;
6573                                         if ( aRes.mfMin > fVal )
6574                                             aRes.mfMin = fVal;
6575                                         if ( aRes.mfMax < fVal )
6576                                             aRes.mfMax = fVal;
6577                                     }
6578                                 }
6579                             }
6580                         }
6581                     }
6582                     if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
6583                     {
6584                         xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
6585                         aRes = sc::ParamIfsResult();
6586                     }
6587                 }
6588                 while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
6589             }
6590         }
6591     }
6592     else
6593     {
6594         // COUNTIFS only.
6595         if (vRefArrayConditions.empty())
6596         {
6597             // The code below is this but optimized for most elements not matching.
6598             // for (auto const & rCond : vConditions)
6599             //     if (rCond == nQueryCount)
6600             //         ++aRes.mfCount;
6601             static_assert(sizeof(vConditions[0]) == 1);
6602             const sal_uInt8* pos = vConditions.data();
6603             const sal_uInt8* end = pos + vConditions.size();
6604             for(;;)
6605             {
6606                 pos = static_cast< const sal_uInt8* >( memchr( pos, nQueryCount, end - pos ));
6607                 if( pos == nullptr )
6608                     break;
6609                 ++aRes.mfCount;
6610                 ++pos;
6611             }
6612         }
6613         else
6614         {
6615             xResMat = GetNewMat( 1, nRefArrayRows, /*bEmpty*/true );
6616             for (size_t i=0, n = vRefArrayConditions.size(); i < n; ++i)
6617             {
6618                 double fCount = 0.0;
6619                 for (auto const & rCond : vRefArrayConditions[i])
6620                 {
6621                     if (rCond == nQueryCount)
6622                         ++fCount;
6623                 }
6624                 xResMat->PutDouble( fCount, 0, i);
6625             }
6626         }
6627     }
6628 
6629     if (xResMat)
6630         PushMatrix( xResMat);
6631     else
6632         PushDouble( ResultFunc( aRes));
6633 }
6634 
6635 void ScInterpreter::ScSumIfs()
6636 {
6637     // ScMutationGuard aShouldFail(pDok, ScMutationGuardFlags::CORE);
6638     sal_uInt8 nParamCount = GetByte();
6639 
6640     if (nParamCount < 3 || (nParamCount % 2 != 1))
6641     {
6642         PushError( FormulaError::ParameterExpected);
6643         return;
6644     }
6645 
6646     auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6647     {
6648         return rRes.mfSum.get();
6649     };
6650     IterateParametersIfs(ResultFunc);
6651 }
6652 
6653 void ScInterpreter::ScAverageIfs()
6654 {
6655     sal_uInt8 nParamCount = GetByte();
6656 
6657     if (nParamCount < 3 || (nParamCount % 2 != 1))
6658     {
6659         PushError( FormulaError::ParameterExpected);
6660         return;
6661     }
6662 
6663     auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6664     {
6665         return sc::div( rRes.mfSum.get(), rRes.mfCount);
6666     };
6667     IterateParametersIfs(ResultFunc);
6668 }
6669 
6670 void ScInterpreter::ScCountIfs()
6671 {
6672     sal_uInt8 nParamCount = GetByte();
6673 
6674     if (nParamCount < 2 || (nParamCount % 2 != 0))
6675     {
6676         PushError( FormulaError::ParameterExpected);
6677         return;
6678     }
6679 
6680     auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6681     {
6682         return rRes.mfCount;
6683     };
6684     IterateParametersIfs(ResultFunc);
6685 }
6686 
6687 void ScInterpreter::ScMinIfs_MS()
6688 {
6689     sal_uInt8 nParamCount = GetByte();
6690 
6691     if (nParamCount < 3 || (nParamCount % 2 != 1))
6692     {
6693         PushError( FormulaError::ParameterExpected);
6694         return;
6695     }
6696 
6697     auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6698     {
6699         return (rRes.mfMin < std::numeric_limits<double>::max()) ? rRes.mfMin : 0.0;
6700     };
6701     IterateParametersIfs(ResultFunc);
6702 }
6703 
6704 
6705 void ScInterpreter::ScMaxIfs_MS()
6706 {
6707     sal_uInt8 nParamCount = GetByte();
6708 
6709     if (nParamCount < 3 || (nParamCount % 2 != 1))
6710     {
6711         PushError( FormulaError::ParameterExpected);
6712         return;
6713     }
6714 
6715     auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6716     {
6717         return (rRes.mfMax > std::numeric_limits<double>::lowest()) ? rRes.mfMax : 0.0;
6718     };
6719     IterateParametersIfs(ResultFunc);
6720 }
6721 
6722 void ScInterpreter::ScLookup()
6723 {
6724     sal_uInt8 nParamCount = GetByte();
6725     if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
6726         return ;
6727 
6728     ScMatrixRef pDataMat = nullptr, pResMat = nullptr;
6729     SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0;
6730     SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0;
6731     SCTAB nTab1 = 0, nResTab = 0;
6732     SCSIZE nLenMajor = 0;   // length of major direction
6733     bool bVertical = true;  // whether to lookup vertically or horizontally
6734 
6735     // The third parameter, result array, double, string and reference.
6736     double fResVal = 0.0;
6737     svl::SharedString aResStr;
6738     StackVar eResArrayType = svUnknown;
6739 
6740     if (nParamCount == 3)
6741     {
6742         eResArrayType = GetStackType();
6743         switch (eResArrayType)
6744         {
6745             case svDoubleRef:
6746             {
6747                 SCTAB nTabJunk;
6748                 PopDoubleRef(nResCol1, nResRow1, nResTab,
6749                              nResCol2, nResRow2, nTabJunk);
6750                 if (nResTab != nTabJunk ||
6751                     ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0))
6752                 {
6753                     // The result array must be a vector.
6754                     PushIllegalParameter();
6755                     return;
6756                 }
6757             }
6758             break;
6759             case svSingleRef:
6760                 PopSingleRef( nResCol1, nResRow1, nResTab);
6761                 nResCol2 = nResCol1;
6762                 nResRow2 = nResRow1;
6763             break;
6764             case svMatrix:
6765             case svExternalSingleRef:
6766             case svExternalDoubleRef:
6767             {
6768                 pResMat = GetMatrix();
6769                 if (!pResMat)
6770                 {
6771                     PushIllegalParameter();
6772                     return;
6773                 }
6774                 SCSIZE nC, nR;
6775                 pResMat->GetDimensions(nC, nR);
6776                 if (nC != 1 && nR != 1)
6777                 {
6778                     // Result matrix must be a vector.
6779                     PushIllegalParameter();
6780                     return;
6781                 }
6782             }
6783             break;
6784             case svDouble:
6785                 fResVal = GetDouble();
6786             break;
6787             case svString:
6788                 aResStr = GetString();
6789             break;
6790             default:
6791                 PushIllegalParameter();
6792                 return;
6793         }
6794     }
6795 
6796     // For double, string and single reference.
6797     double fDataVal = 0.0;
6798     svl::SharedString aDataStr;
6799     ScAddress aDataAdr;
6800     bool bValueData = false;
6801 
6802     // Get the data-result range and also determine whether this is vertical
6803     // lookup or horizontal lookup.
6804 
6805     StackVar eDataArrayType = GetStackType();
6806     switch (eDataArrayType)
6807     {
6808         case svDoubleRef:
6809         {
6810             SCTAB nTabJunk;
6811             PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk);
6812             if (nTab1 != nTabJunk)
6813             {
6814                 PushIllegalParameter();
6815                 return;
6816             }
6817             bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1);
6818             nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1;
6819         }
6820         break;
6821         case svMatrix:
6822         case svExternalSingleRef:
6823         case svExternalDoubleRef:
6824         {
6825             pDataMat = GetMatrix();
6826             if (!pDataMat)
6827             {
6828                 PushIllegalParameter();
6829                 return;
6830             }
6831 
6832             SCSIZE nC, nR;
6833             pDataMat->GetDimensions(nC, nR);
6834             bVertical = (nR >= nC);
6835             nLenMajor = bVertical ? nR : nC;
6836         }
6837         break;
6838         case svDouble:
6839         {
6840             fDataVal = GetDouble();
6841             bValueData = true;
6842         }
6843         break;
6844         case svString:
6845         {
6846             aDataStr = GetString();
6847         }
6848         break;
6849         case svSingleRef:
6850         {
6851             PopSingleRef( aDataAdr );
6852             ScRefCellValue aCell(mrDoc, aDataAdr);
6853             if (aCell.hasEmptyValue())
6854             {
6855                 // Empty cells aren't found anywhere, bail out early.
6856                 SetError( FormulaError::NotAvailable);
6857             }
6858             else if (aCell.hasNumeric())
6859             {
6860                 fDataVal = GetCellValue(aDataAdr, aCell);
6861                 bValueData = true;
6862             }
6863             else
6864                 GetCellString(aDataStr, aCell);
6865         }
6866         break;
6867         default:
6868             SetError( FormulaError::IllegalParameter);
6869     }
6870 
6871     if (nGlobalError != FormulaError::NONE)
6872     {
6873         PushError( nGlobalError);
6874         return;
6875     }
6876 
6877     // Get the lookup value.
6878 
6879     ScQueryParam aParam;
6880     ScQueryEntry& rEntry = aParam.GetEntry(0);
6881     if ( !FillEntry(rEntry) )
6882         return;
6883 
6884     if ( eDataArrayType == svDouble || eDataArrayType == svString ||
6885             eDataArrayType == svSingleRef )
6886     {
6887         // Delta position for a single value is always 0.
6888 
6889         // Found if data <= query, but not if query is string and found data is
6890         // numeric or vice versa. This is how Excel does it but doesn't
6891         // document it.
6892 
6893         bool bFound = false;
6894         ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6895 
6896         if ( bValueData )
6897         {
6898             if (rItem.meType == ScQueryEntry::ByString)
6899                 bFound = false;
6900             else
6901                 bFound = (fDataVal <= rItem.mfVal);
6902         }
6903         else
6904         {
6905             if (rItem.meType != ScQueryEntry::ByString)
6906                 bFound = false;
6907             else
6908                 bFound = (ScGlobal::GetCollator().compareString(aDataStr.getString(), rItem.maString.getString()) <= 0);
6909         }
6910 
6911         if (!bFound)
6912         {
6913             PushNA();
6914             return;
6915         }
6916 
6917         if (pResMat)
6918         {
6919             if (pResMat->IsValue( 0, 0 ))
6920                 PushDouble(pResMat->GetDouble( 0, 0 ));
6921             else
6922                 PushString(pResMat->GetString(0, 0));
6923         }
6924         else if (nParamCount == 3)
6925         {
6926             switch (eResArrayType)
6927             {
6928                 case svDouble:
6929                     PushDouble( fResVal );
6930                     break;
6931                 case svString:
6932                     PushString( aResStr );
6933                     break;
6934                 case svDoubleRef:
6935                 case svSingleRef:
6936                     PushCellResultToken( true, ScAddress( nResCol1, nResRow1, nResTab), nullptr, nullptr);
6937                     break;
6938                 default:
6939                     assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, single value data");
6940                     PushIllegalParameter();
6941             }
6942         }
6943         else
6944         {
6945             switch (eDataArrayType)
6946             {
6947                 case svDouble:
6948                     PushDouble( fDataVal );
6949                     break;
6950                 case svString:
6951                     PushString( aDataStr );
6952                     break;
6953                 case svSingleRef:
6954                     PushCellResultToken( true, aDataAdr, nullptr, nullptr);
6955                     break;
6956                 default:
6957                     assert(!"ScInterpreter::ScLookup: unhandled eDataArrayType, single value data");
6958                     PushIllegalParameter();
6959             }
6960         }
6961         return;
6962     }
6963 
6964     // Now, perform the search to compute the delta position (nDelta).
6965 
6966     if (pDataMat)
6967     {
6968         // Data array is given as a matrix.
6969         rEntry.bDoQuery = true;
6970         rEntry.eOp = SC_LESS_EQUAL;
6971         bool bFound = false;
6972 
6973         SCSIZE nC, nR;
6974         pDataMat->GetDimensions(nC, nR);
6975 
6976         // Do not propagate errors from matrix while copying to vector.
6977         pDataMat->SetErrorInterpreter( nullptr);
6978 
6979         // Excel has an undocumented behaviour in that it seems to internally
6980         // sort an interim array (i.e. error values specifically #DIV/0! are
6981         // sorted to the end) or ignore error values that makes these "get last
6982         // non-empty" searches work, e.g. =LOOKUP(2,1/NOT(ISBLANK(A:A)),A:A)
6983         // see tdf#117016
6984         // Instead of sorting a million entries of which mostly only a bunch of
6985         // rows are filled and moving error values to the end which most are
6986         // already anyway, assume the matrix to be sorted except error values
6987         // and omit the coded DoubleError values.
6988         // Do this only for a numeric matrix (that includes errors coded as
6989         // doubles), which covers the case in question.
6990         /* TODO: it's unclear whether this really matches Excel behaviour in
6991          * all constellations or if there are cases that include unsorted error
6992          * values and thus yield arbitrary binary search results or something
6993          * different or whether there are cases where error values are also
6994          * omitted from mixed numeric/string arrays or if it's not an interim
6995          * matrix but a cell range reference instead. */
6996         const bool bOmitErrorValues = (eDataArrayType == svMatrix && pDataMat->IsNumeric());
6997 
6998         // In case of non-vector matrix, only search the first row or column.
6999         ScMatrixRef pDataMat2;
7000         std::vector<SCCOLROW> vIndex;
7001         if (bOmitErrorValues)
7002         {
7003             std::vector<double> vArray;
7004             VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
7005             const SCSIZE nElements = aMatAcc.GetElementCount();
7006             for (SCSIZE i=0; i < nElements; ++i)
7007             {
7008                 const double fVal = aMatAcc.GetDouble(i);
7009                 if (std::isfinite(fVal))
7010                 {
7011                     vArray.push_back(fVal);
7012                     vIndex.push_back(i);
7013                 }
7014             }
7015             if (vArray.empty())
7016             {
7017                 PushNA();
7018                 return;
7019             }
7020             const size_t nElems = vArray.size();
7021             if (nElems == nElements)
7022             {
7023                 // No error value omitted, use as is.
7024                 pDataMat2 = pDataMat;
7025                 std::vector<SCCOLROW>().swap( vIndex);
7026             }
7027             else
7028             {
7029                 nLenMajor = nElems;
7030                 if (bVertical)
7031                 {
7032                     ScMatrixRef pTempMat = GetNewMat( 1, nElems, /*bEmpty*/true );
7033                     pTempMat->PutDoubleVector( vArray, 0, 0);
7034                     pDataMat2 = pTempMat;
7035                 }
7036                 else
7037                 {
7038                     ScMatrixRef pTempMat = GetNewMat( nElems, 1, /*bEmpty*/true );
7039                     for (size_t i=0; i < nElems; ++i)
7040                         pTempMat->PutDouble( vArray[i], i, 0);
7041                     pDataMat2 = pTempMat;
7042                 }
7043             }
7044         }
7045         else
7046         {
7047             // Just use as is with the VectorMatrixAccessor.
7048             pDataMat2 = pDataMat;
7049         }
7050 
7051         // Do not propagate errors from matrix while searching.
7052         pDataMat2->SetErrorInterpreter( nullptr);
7053 
7054         VectorMatrixAccessor aMatAcc2(*pDataMat2, bVertical);
7055 
7056         // binary search for non-equality mode (the source data is
7057         // assumed to be sorted in ascending order).
7058 
7059         SCCOLROW nDelta = -1;
7060 
7061         SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0;
7062         for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
7063         {
7064             SCSIZE nMid = nFirst + nLen/2;
7065             sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc2, rEntry);
7066             if (nCmp == 0)
7067             {
7068                 // exact match.  find the last item with the same value.
7069                 lcl_GetLastMatch( nMid, aMatAcc2, nLenMajor);
7070                 nDelta = nMid;
7071                 bFound = true;
7072                 break;
7073             }
7074 
7075             if (nLen == 1) // first and last items are next to each other.
7076             {
7077                 nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1;
7078                 // If already the 1st item is greater there's nothing found.
7079                 bFound = (nDelta >= 0);
7080                 break;
7081             }
7082 
7083             if (nCmp < 0)
7084                 nFirst = nMid;
7085             else
7086                 nLast = nMid;
7087         }
7088 
7089         if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item
7090         {
7091             sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, aMatAcc2, rEntry);
7092             if (nCmp <= 0)
7093             {
7094                 // either the last item is an exact match or the real
7095                 // hit is beyond the last item.
7096                 nDelta += 1;
7097                 bFound = true;
7098             }
7099         }
7100         else if (nDelta > 0) // valid hit must be 2nd item or higher
7101         {
7102             // non-exact match
7103             bFound = true;
7104         }
7105 
7106         // With 0-9 < A-Z, if query is numeric and data found is string, or
7107         // vice versa, the (yet another undocumented) Excel behavior is to
7108         // return #N/A instead.
7109 
7110         if (bFound)
7111         {
7112             if (!vIndex.empty())
7113                 nDelta = vIndex[nDelta];
7114 
7115             VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
7116             SCCOLROW i = nDelta;
7117             SCSIZE n = aMatAcc.GetElementCount();
7118             if (o3tl::make_unsigned(i) >= n)
7119                 i = static_cast<SCCOLROW>(n);
7120             bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
7121             if (bByString == aMatAcc.IsValue(i))
7122                 bFound = false;
7123         }
7124 
7125         if (!bFound)
7126         {
7127             PushNA();
7128             return;
7129         }
7130 
7131         // Now that we've found the delta, push the result back to the cell.
7132 
7133         if (pResMat)
7134         {
7135             VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
7136             // Result array is matrix.
7137             // Note this does not replicate the other dimension.
7138             if (o3tl::make_unsigned(nDelta) >= aResMatAcc.GetElementCount())
7139             {
7140                 PushNA();
7141                 return;
7142             }
7143             if (aResMatAcc.IsValue(nDelta))
7144                 PushDouble(aResMatAcc.GetDouble(nDelta));
7145             else
7146                 PushString(aResMatAcc.GetString(nDelta));
7147         }
7148         else if (nParamCount == 3)
7149         {
7150             /* TODO: the entire switch is a copy of the cell range search
7151              * result, factor out. */
7152             switch (eResArrayType)
7153             {
7154                 case svDoubleRef:
7155                 case svSingleRef:
7156                 {
7157                     // Use the result array vector.  Note that the result array is assumed
7158                     // to be a vector (i.e. 1-dimensional array).
7159 
7160                     ScAddress aAdr;
7161                     aAdr.SetTab(nResTab);
7162                     bool bResVertical = (nResRow2 - nResRow1) > 0;
7163                     if (bResVertical)
7164                     {
7165                         SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
7166                         if (nTempRow > mrDoc.MaxRow())
7167                         {
7168                             PushDouble(0);
7169                             return;
7170                         }
7171                         aAdr.SetCol(nResCol1);
7172                         aAdr.SetRow(nTempRow);
7173                     }
7174                     else
7175                     {
7176                         SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
7177                         if (nTempCol > mrDoc.MaxCol())
7178                         {
7179                             PushDouble(0);
7180                             return;
7181                         }
7182                         aAdr.SetCol(nTempCol);
7183                         aAdr.SetRow(nResRow1);
7184                     }
7185                     PushCellResultToken( true, aAdr, nullptr, nullptr);
7186                 }
7187                 break;
7188                 case svDouble:
7189                 case svString:
7190                 {
7191                     if (nDelta != 0)
7192                         PushNA();
7193                     else
7194                     {
7195                         switch (eResArrayType)
7196                         {
7197                             case svDouble:
7198                                 PushDouble( fResVal );
7199                             break;
7200                             case svString:
7201                                 PushString( aResStr );
7202                             break;
7203                             default:
7204                                 ;   // nothing
7205                         }
7206                     }
7207                 }
7208                 break;
7209                 default:
7210                     assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, array search");
7211                     PushIllegalParameter();
7212             }
7213         }
7214         else
7215         {
7216             // No result array. Use the data array to get the final value from.
7217             // Propagate errors from matrix again.
7218             pDataMat->SetErrorInterpreter( this);
7219             if (bVertical)
7220             {
7221                 if (pDataMat->IsValue(nC-1, nDelta))
7222                     PushDouble(pDataMat->GetDouble(nC-1, nDelta));
7223                 else
7224                     PushString(pDataMat->GetString(nC-1, nDelta));
7225             }
7226             else
7227             {
7228                 if (pDataMat->IsValue(nDelta, nR-1))
7229                     PushDouble(pDataMat->GetDouble(nDelta, nR-1));
7230                 else
7231                     PushString(pDataMat->GetString(nDelta, nR-1));
7232             }
7233         }
7234 
7235         return;
7236     }
7237 
7238     // Perform cell range search.
7239 
7240     aParam.nCol1            = nCol1;
7241     aParam.nRow1            = nRow1;
7242     aParam.nCol2            = bVertical ? nCol1 : nCol2;
7243     aParam.nRow2            = bVertical ? nRow2 : nRow1;
7244     aParam.bByRow           = bVertical;
7245 
7246     rEntry.bDoQuery = true;
7247     rEntry.eOp = SC_LESS_EQUAL;
7248     rEntry.nField = nCol1;
7249     ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7250     if (rItem.meType == ScQueryEntry::ByString)
7251         aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
7252 
7253     ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, aParam, false);
7254     SCCOL nC;
7255     SCROW nR;
7256     // Advance Entry.nField in iterator upon switching columns if
7257     // lookup in row.
7258     aCellIter.SetAdvanceQueryParamEntryField(!bVertical);
7259     if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) )
7260     {
7261         PushNA();
7262         return;
7263     }
7264 
7265     SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1);
7266 
7267     if (pResMat)
7268     {
7269         VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
7270         // Use the matrix result array.
7271         // Note this does not replicate the other dimension.
7272         if (o3tl::make_unsigned(nDelta) >= aResMatAcc.GetElementCount())
7273         {
7274             PushNA();
7275             return;
7276         }
7277         if (aResMatAcc.IsValue(nDelta))
7278             PushDouble(aResMatAcc.GetDouble(nDelta));
7279         else
7280             PushString(aResMatAcc.GetString(nDelta));
7281     }
7282     else if (nParamCount == 3)
7283     {
7284         /* TODO: the entire switch is a copy of the array search result, factor
7285          * out. */
7286         switch (eResArrayType)
7287         {
7288             case svDoubleRef:
7289             case svSingleRef:
7290             {
7291                 // Use the result array vector.  Note that the result array is assumed
7292                 // to be a vector (i.e. 1-dimensional array).
7293 
7294                 ScAddress aAdr;
7295                 aAdr.SetTab(nResTab);
7296                 bool bResVertical = (nResRow2 - nResRow1) > 0;
7297                 if (bResVertical)
7298                 {
7299                     SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
7300                     if (nTempRow > mrDoc.MaxRow())
7301                     {
7302                         PushDouble(0);
7303                         return;
7304                     }
7305                     aAdr.SetCol(nResCol1);
7306                     aAdr.SetRow(nTempRow);
7307                 }
7308                 else
7309                 {
7310                     SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
7311                     if (nTempCol > mrDoc.MaxCol())
7312                     {
7313                         PushDouble(0);
7314                         return;
7315                     }
7316                     aAdr.SetCol(nTempCol);
7317                     aAdr.SetRow(nResRow1);
7318                 }
7319                 PushCellResultToken( true, aAdr, nullptr, nullptr);
7320             }
7321             break;
7322             case svDouble:
7323             case svString:
7324             {
7325                 if (nDelta != 0)
7326                     PushNA();
7327                 else
7328                 {
7329                     switch (eResArrayType)
7330                     {
7331                         case svDouble:
7332                             PushDouble( fResVal );
7333                             break;
7334                         case svString:
7335                             PushString( aResStr );
7336                             break;
7337                         default:
7338                             ;   // nothing
7339                     }
7340                 }
7341             }
7342             break;
7343             default:
7344                 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, range search");
7345                 PushIllegalParameter();
7346         }
7347     }
7348     else
7349     {
7350         // Regardless of whether or not the result array exists, the last
7351         // array is always used as the "result" array.
7352 
7353         ScAddress aAdr;
7354         aAdr.SetTab(nTab1);
7355         if (bVertical)
7356         {
7357             SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta);
7358             if (nTempRow > mrDoc.MaxRow())
7359             {
7360                 PushDouble(0);
7361                 return;
7362             }
7363             aAdr.SetCol(nCol2);
7364             aAdr.SetRow(nTempRow);
7365         }
7366         else
7367         {
7368             SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta);
7369             if (nTempCol > mrDoc.MaxCol())
7370             {
7371                 PushDouble(0);
7372                 return;
7373             }
7374             aAdr.SetCol(nTempCol);
7375             aAdr.SetRow(nRow2);
7376         }
7377         PushCellResultToken(true, aAdr, nullptr, nullptr);
7378     }
7379 }
7380 
7381 void ScInterpreter::ScHLookup()
7382 {
7383     CalculateLookup(true);
7384 }
7385 
7386 void ScInterpreter::CalculateLookup(bool bHLookup)
7387 {
7388     sal_uInt8 nParamCount = GetByte();
7389     if (!MustHaveParamCount(nParamCount, 3, 4))
7390         return;
7391 
7392     // Optional 4th argument to declare whether or not the range is sorted.
7393     bool bSorted = true;
7394     if (nParamCount == 4)
7395         bSorted = GetBool();
7396 
7397     // Index of column to search.
7398     double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0;
7399 
7400     ScMatrixRef pMat = nullptr;
7401     SCSIZE nC = 0, nR = 0;
7402     SCCOL nCol1 = 0;
7403     SCROW nRow1 = 0;
7404     SCTAB nTab1 = 0;
7405     SCCOL nCol2 = 0;
7406     SCROW nRow2 = 0;
7407     const ScComplexRefData* refData = nullptr;
7408     StackVar eType = GetStackType();
7409     if (eType == svDoubleRef)
7410     {
7411         refData = GetStackDoubleRef(0);
7412         SCTAB nTab2;
7413         PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7414         if (nTab1 != nTab2)
7415         {
7416             PushIllegalParameter();
7417             return;
7418         }
7419     }
7420     else if (eType == svSingleRef)
7421     {
7422         PopSingleRef(nCol1, nRow1, nTab1);
7423         nCol2 = nCol1;
7424         nRow2 = nRow1;
7425     }
7426     else if (eType == svMatrix || eType == svExternalDoubleRef || eType == svExternalSingleRef)
7427     {
7428         pMat = GetMatrix();
7429 
7430         if (pMat)
7431             pMat->GetDimensions(nC, nR);
7432         else
7433         {
7434             PushIllegalParameter();
7435             return;
7436         }
7437     }
7438     else
7439     {
7440         PushIllegalParameter();
7441         return;
7442     }
7443 
7444     if ( fIndex < 0.0 || (bHLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) )
7445     {
7446         PushIllegalArgument();
7447         return;
7448     }
7449 
7450     SCROW nZIndex = static_cast<SCROW>(fIndex);
7451     SCCOL nSpIndex = static_cast<SCCOL>(fIndex);
7452 
7453     if (!pMat)
7454     {
7455         nZIndex += nRow1;                       // value row
7456         nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 );     // value column
7457     }
7458 
7459     if (nGlobalError != FormulaError::NONE)
7460     {
7461         PushIllegalParameter();
7462         return;
7463     }
7464 
7465     ScQueryParam aParam;
7466     aParam.nCol1 = nCol1;
7467     aParam.nRow1 = nRow1;
7468     if ( bHLookup )
7469     {
7470         aParam.nCol2 = nCol2;
7471         aParam.nRow2 = nRow1;     // search only in the first row
7472         aParam.bByRow = false;
7473     }
7474     else
7475     {
7476         aParam.nCol2 = nCol1;     // search only in the first column
7477         aParam.nRow2 = nRow2;
7478         aParam.nTab  = nTab1;
7479     }
7480 
7481     ScQueryEntry& rEntry = aParam.GetEntry(0);
7482     rEntry.bDoQuery = true;
7483     if ( bSorted )
7484         rEntry.eOp = SC_LESS_EQUAL;
7485     if ( !FillEntry(rEntry) )
7486         return;
7487 
7488     ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7489     if (rItem.meType == ScQueryEntry::ByString)
7490         aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
7491     if (pMat)
7492     {
7493         SCSIZE nMatCount = bHLookup ? nC : nR;
7494         SCSIZE nDelta = SCSIZE_MAX;
7495         if (rItem.meType == ScQueryEntry::ByString)
7496         {
7497 //!!!!!!!
7498 //TODO: enable regex on matrix strings
7499 //!!!!!!!
7500             svl::SharedString aParamStr = rItem.maString;
7501             if ( bSorted )
7502             {
7503                 CollatorWrapper& rCollator = ScGlobal::GetCollator();
7504                 for (SCSIZE i = 0; i < nMatCount; i++)
7505                 {
7506                     if (bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i))
7507                     {
7508                         sal_Int32 nRes =
7509                             rCollator.compareString(
7510                                 bHLookup ? pMat->GetString(i,0).getString() : pMat->GetString(0,i).getString(), aParamStr.getString());
7511                         if (nRes <= 0)
7512                             nDelta = i;
7513                         else if (i>0)   // #i2168# ignore first mismatch
7514                             i = nMatCount+1;
7515                     }
7516                     else
7517                         nDelta = i;
7518                 }
7519             }
7520             else
7521             {
7522                 if (bHLookup)
7523                 {
7524                     for (SCSIZE i = 0; i < nMatCount; i++)
7525                     {
7526                         if (pMat->IsStringOrEmpty(i, 0))
7527                         {
7528                             if (pMat->GetString(i,0).getDataIgnoreCase() == aParamStr.getDataIgnoreCase())
7529                             {
7530                                 nDelta = i;
7531                                 i = nMatCount + 1;
7532                             }
7533                         }
7534                     }
7535                 }
7536                 else
7537                 {
7538                     nDelta = pMat->MatchStringInColumns(aParamStr, 0, 0);
7539                 }
7540             }
7541         }
7542         else
7543         {
7544             if ( bSorted )
7545             {
7546                 // #i2168# ignore strings
7547                 for (SCSIZE i = 0; i < nMatCount; i++)
7548                 {
7549                     if (!(bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i)))
7550                     {
7551                         if ((bHLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rItem.mfVal)
7552                             nDelta = i;
7553                         else
7554                             i = nMatCount+1;
7555                     }
7556                 }
7557             }
7558             else
7559             {
7560                 if (bHLookup)
7561                 {
7562                     for (SCSIZE i = 0; i < nMatCount; i++)
7563                     {
7564                         if (! pMat->IsStringOrEmpty(i, 0) )
7565                         {
7566                             if ( pMat->GetDouble(i,0) == rItem.mfVal)
7567                             {
7568                                 nDelta = i;
7569                                 i = nMatCount + 1;
7570                             }
7571                         }
7572                     }
7573                 }
7574                 else
7575                 {
7576                     nDelta = pMat->MatchDoubleInColumns(rItem.mfVal, 0, 0);
7577                 }
7578             }
7579         }
7580         if ( nDelta != SCSIZE_MAX )
7581         {
7582             SCSIZE nX = static_cast<SCSIZE>(nSpIndex);
7583             SCSIZE nY = nDelta;
7584             SCSIZE nXs = 0;
7585             SCSIZE nYs = nY;
7586             if ( bHLookup )
7587             {
7588                 nX = nDelta;
7589                 nY = static_cast<SCSIZE>(nZIndex);
7590                 nXs = nX;
7591                 nYs = 0;
7592             }
7593             assert( nX < nC && nY < nR );
7594             if (!(rItem.meType == ScQueryEntry::ByString && pMat->IsValue( nXs, nYs)))
7595             {
7596                 if (pMat->IsStringOrEmpty( nX, nY))
7597                     PushString(pMat->GetString( nX, nY).getString());
7598                 else
7599                     PushDouble(pMat->GetDouble( nX, nY));
7600             }
7601             else
7602                 PushNA();
7603             return;
7604         }
7605         else
7606             PushNA();
7607     }
7608     else
7609     {
7610         rEntry.nField = nCol1;
7611         bool bFound = false;
7612         SCCOL nCol = 0;
7613         SCROW nRow = 0;
7614         if ( bSorted )
7615             rEntry.eOp = SC_LESS_EQUAL;
7616         if ( bHLookup )
7617         {
7618             ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, aParam, false);
7619             // advance Entry.nField in Iterator upon switching columns
7620             aCellIter.SetAdvanceQueryParamEntryField( true );
7621             if ( bSorted )
7622             {
7623                 SCROW nRow1_temp;
7624                 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp );
7625             }
7626             else if ( aCellIter.GetFirst() )
7627             {
7628                 bFound = true;
7629                 nCol = aCellIter.GetCol();
7630             }
7631             nRow = nZIndex;
7632         }
7633         else
7634         {
7635             ScAddress aResultPos( nCol1, nRow1, nTab1);
7636             bFound = LookupQueryWithCache( aResultPos, aParam, refData);
7637             nRow = aResultPos.Row();
7638             nCol = nSpIndex;
7639         }
7640 
7641         if ( bFound )
7642         {
7643             ScAddress aAdr( nCol, nRow, nTab1 );
7644             PushCellResultToken( true, aAdr, nullptr, nullptr);
7645         }
7646         else
7647             PushNA();
7648     }
7649 }
7650 
7651 bool ScInterpreter::FillEntry(ScQueryEntry& rEntry)
7652 {
7653     ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7654     switch ( GetStackType() )
7655     {
7656         case svDouble:
7657         {
7658             rItem.meType = ScQueryEntry::ByValue;
7659             rItem.mfVal = GetDouble();
7660         }
7661         break;
7662         case svString:
7663         {
7664             rItem.meType = ScQueryEntry::ByString;
7665             rItem.maString = GetString();
7666         }
7667         break;
7668         case svDoubleRef :
7669         case svSingleRef :
7670         {
7671             ScAddress aAdr;
7672             if ( !PopDoubleRefOrSingleRef( aAdr ) )
7673             {
7674                 PushInt(0);
7675                 return false;
7676             }
7677             ScRefCellValue aCell(mrDoc, aAdr);
7678             if (aCell.hasNumeric())
7679             {
7680                 rItem.meType = ScQueryEntry::ByValue;
7681                 rItem.mfVal = GetCellValue(aAdr, aCell);
7682             }
7683             else
7684             {
7685                 GetCellString(rItem.maString, aCell);
7686                 rItem.meType = ScQueryEntry::ByString;
7687             }
7688         }
7689         break;
7690         case svExternalDoubleRef:
7691         case svExternalSingleRef:
7692         case svMatrix:
7693         {
7694             svl::SharedString aStr;
7695             const ScMatValType nType = GetDoubleOrStringFromMatrix(rItem.mfVal, aStr);
7696             rItem.maString = aStr;
7697             rItem.meType = ScMatrix::IsNonValueType(nType) ?
7698                 ScQueryEntry::ByString : ScQueryEntry::ByValue;
7699         }
7700         break;
7701         default:
7702         {
7703             PushIllegalParameter();
7704             return false;
7705         }
7706     } // switch ( GetStackType() )
7707     return true;
7708 }
7709 
7710 void ScInterpreter::ScVLookup()
7711 {
7712     CalculateLookup(false);
7713 }
7714 
7715 void ScInterpreter::ScSubTotal()
7716 {
7717     sal_uInt8 nParamCount = GetByte();
7718     if ( !MustHaveParamCountMinWithStackCheck( nParamCount, 2 ) )
7719         return;
7720 
7721     // We must fish the 1st parameter deep from the stack! And push it on top.
7722     const FormulaToken* p = pStack[ sp - nParamCount ];
7723     PushWithoutError( *p );
7724     sal_Int32 nFunc = GetInt32();
7725     mnSubTotalFlags |= SubtotalFlags::IgnoreNestedStAg | SubtotalFlags::IgnoreFiltered;
7726     if (nFunc > 100)
7727     {
7728         // For opcodes 101 through 111, we need to skip hidden cells.
7729         // Other than that these opcodes are identical to 1 through 11.
7730         mnSubTotalFlags |= SubtotalFlags::IgnoreHidden;
7731         nFunc -= 100;
7732     }
7733 
7734     if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 11 )
7735         PushIllegalArgument();  // simulate return on stack, not SetError(...)
7736     else
7737     {
7738         cPar = nParamCount - 1;
7739         switch( nFunc )
7740         {
7741             case SUBTOTAL_FUNC_AVE  : ScAverage(); break;
7742             case SUBTOTAL_FUNC_CNT  : ScCount();   break;
7743             case SUBTOTAL_FUNC_CNT2 : ScCount2();  break;
7744             case SUBTOTAL_FUNC_MAX  : ScMax();     break;
7745             case SUBTOTAL_FUNC_MIN  : ScMin();     break;
7746             case SUBTOTAL_FUNC_PROD : ScProduct(); break;
7747             case SUBTOTAL_FUNC_STD  : ScStDev();   break;
7748             case SUBTOTAL_FUNC_STDP : ScStDevP();  break;
7749             case SUBTOTAL_FUNC_SUM  : ScSum();     break;
7750             case SUBTOTAL_FUNC_VAR  : ScVar();     break;
7751             case SUBTOTAL_FUNC_VARP : ScVarP();    break;
7752             default : PushIllegalArgument();       break;
7753         }
7754     }
7755     mnSubTotalFlags = SubtotalFlags::NONE;
7756     // Get rid of the 1st (fished) parameter.
7757     FormulaConstTokenRef xRef( PopToken());
7758     Pop();
7759     PushTokenRef( xRef);
7760 }
7761 
7762 void ScInterpreter::ScAggregate()
7763 {
7764     sal_uInt8 nParamCount = GetByte();
7765     if ( !MustHaveParamCountMinWithStackCheck( nParamCount, 3 ) )
7766         return;
7767 
7768     const FormulaError nErr = nGlobalError;
7769     nGlobalError = FormulaError::NONE;
7770 
7771     // fish the 1st parameter from the stack and push it on top.
7772     const FormulaToken* p = pStack[ sp - nParamCount ];
7773     PushWithoutError( *p );
7774     sal_Int32 nFunc = GetInt32();
7775     // fish the 2nd parameter from the stack and push it on top.
7776     const FormulaToken* p2 = pStack[ sp - ( nParamCount - 1 ) ];
7777     PushWithoutError( *p2 );
7778     sal_Int32 nOption = GetInt32();
7779 
7780     if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 19 )
7781     {
7782         nGlobalError = nErr;
7783         PushIllegalArgument();
7784     }
7785     else
7786     {
7787         switch ( nOption)
7788         {
7789             case 0 : // ignore nested SUBTOTAL and AGGREGATE functions
7790                 mnSubTotalFlags = SubtotalFlags::IgnoreNestedStAg;
7791                 break;
7792             case 1 : // ignore hidden rows, nested SUBTOTAL and AGGREGATE functions
7793                 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreNestedStAg;
7794                 break;
7795             case 2 : // ignore error values, nested SUBTOTAL and AGGREGATE functions
7796                 mnSubTotalFlags = SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg;
7797                 break;
7798             case 3 : // ignore hidden rows, error values, nested SUBTOTAL and AGGREGATE functions
7799                 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg;
7800                 break;
7801             case 4 : // ignore nothing
7802                 mnSubTotalFlags = SubtotalFlags::NONE;
7803                 break;
7804             case 5 : // ignore hidden rows
7805                 mnSubTotalFlags = SubtotalFlags::IgnoreHidden ;
7806                 break;
7807             case 6 : // ignore error values
7808                 mnSubTotalFlags = SubtotalFlags::IgnoreErrVal ;
7809                 break;
7810             case 7 : // ignore hidden rows and error values
7811                 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal ;
7812                 break;
7813             default :
7814                 nGlobalError = nErr;
7815                 PushIllegalArgument();
7816                 return;
7817         }
7818 
7819         if ((mnSubTotalFlags & SubtotalFlags::IgnoreErrVal) == SubtotalFlags::NONE)
7820             nGlobalError = nErr;
7821 
7822         cPar = nParamCount - 2;
7823         switch ( nFunc )
7824         {
7825             case AGGREGATE_FUNC_AVE     : ScAverage(); break;
7826             case AGGREGATE_FUNC_CNT     : ScCount();   break;
7827             case AGGREGATE_FUNC_CNT2    : ScCount2();  break;
7828             case AGGREGATE_FUNC_MAX     : ScMax();     break;
7829             case AGGREGATE_FUNC_MIN     : ScMin();     break;
7830             case AGGREGATE_FUNC_PROD    : ScProduct(); break;
7831             case AGGREGATE_FUNC_STD     : ScStDev();   break;
7832             case AGGREGATE_FUNC_STDP    : ScStDevP();  break;
7833             case AGGREGATE_FUNC_SUM     : ScSum();     break;
7834             case AGGREGATE_FUNC_VAR     : ScVar();     break;
7835             case AGGREGATE_FUNC_VARP    : ScVarP();    break;
7836             case AGGREGATE_FUNC_MEDIAN  : ScMedian();            break;
7837             case AGGREGATE_FUNC_MODSNGL : ScModalValue();        break;
7838             case AGGREGATE_FUNC_LARGE   : ScLarge();             break;
7839             case AGGREGATE_FUNC_SMALL   : ScSmall();             break;
7840             case AGGREGATE_FUNC_PERCINC : ScPercentile( true );  break;
7841             case AGGREGATE_FUNC_QRTINC  : ScQuartile( true );    break;
7842             case AGGREGATE_FUNC_PERCEXC : ScPercentile( false ); break;
7843             case AGGREGATE_FUNC_QRTEXC  : ScQuartile( false );   break;
7844             default:
7845                 nGlobalError = nErr;
7846                 PushIllegalArgument();
7847             break;
7848         }
7849         mnSubTotalFlags = SubtotalFlags::NONE;
7850     }
7851     FormulaConstTokenRef xRef( PopToken());
7852     // Get rid of the 1st and 2nd (fished) parameters.
7853     Pop();
7854     Pop();
7855     PushTokenRef( xRef);
7856 }
7857 
7858 std::unique_ptr<ScDBQueryParamBase> ScInterpreter::GetDBParams( bool& rMissingField )
7859 {
7860     bool bAllowMissingField = false;
7861     if ( rMissingField )
7862     {
7863         bAllowMissingField = true;
7864         rMissingField = false;
7865     }
7866     if ( GetByte() == 3 )
7867     {
7868         // First, get the query criteria range.
7869         ::std::unique_ptr<ScDBRangeBase> pQueryRef( PopDBDoubleRef() );
7870         if (!pQueryRef)
7871             return nullptr;
7872 
7873         bool    bByVal = true;
7874         double  nVal = 0.0;
7875         svl::SharedString  aStr;
7876         ScRange aMissingRange;
7877         bool bRangeFake = false;
7878         switch (GetStackType())
7879         {
7880             case svDouble :
7881                 nVal = ::rtl::math::approxFloor( GetDouble() );
7882                 if ( bAllowMissingField && nVal == 0.0 )
7883                     rMissingField = true;   // fake missing parameter
7884                 break;
7885             case svString :
7886                 bByVal = false;
7887                 aStr = GetString();
7888                 break;
7889             case svSingleRef :
7890                 {
7891                     ScAddress aAdr;
7892                     PopSingleRef( aAdr );
7893                     ScRefCellValue aCell(mrDoc, aAdr);
7894                     if (aCell.hasNumeric())
7895                         nVal = GetCellValue(aAdr, aCell);
7896                     else
7897                     {
7898                         bByVal = false;
7899                         GetCellString(aStr, aCell);
7900                     }
7901                 }
7902                 break;
7903             case svDoubleRef :
7904                 if ( bAllowMissingField )
7905                 {   // fake missing parameter for old SO compatibility
7906                     bRangeFake = true;
7907                     PopDoubleRef( aMissingRange );
7908                 }
7909                 else
7910                 {
7911                     PopError();
7912                     SetError( FormulaError::IllegalParameter );
7913                 }
7914                 break;
7915             case svMissing :
7916                 PopError();
7917                 if ( bAllowMissingField )
7918                     rMissingField = true;
7919                 else
7920                     SetError( FormulaError::IllegalParameter );
7921                 break;
7922             default:
7923                 PopError();
7924                 SetError( FormulaError::IllegalParameter );
7925         }
7926 
7927         if (nGlobalError != FormulaError::NONE)
7928             return nullptr;
7929 
7930         unique_ptr<ScDBRangeBase> pDBRef( PopDBDoubleRef() );
7931 
7932         if (nGlobalError != FormulaError::NONE || !pDBRef)
7933             return nullptr;
7934 
7935         if ( bRangeFake )
7936         {
7937             // range parameter must match entire database range
7938             if (pDBRef->isRangeEqual(aMissingRange))
7939                 rMissingField = true;
7940             else
7941                 SetError( FormulaError::IllegalParameter );
7942         }
7943 
7944         if (nGlobalError != FormulaError::NONE)
7945             return nullptr;
7946 
7947         SCCOL nField = pDBRef->getFirstFieldColumn();
7948         if (rMissingField)
7949             ; // special case
7950         else if (bByVal)
7951             nField = pDBRef->findFieldColumn(static_cast<SCCOL>(nVal));
7952         else
7953         {
7954             FormulaError nErr = FormulaError::NONE;
7955             nField = pDBRef->findFieldColumn(aStr.getString(), &nErr);
7956             SetError(nErr);
7957         }
7958 
7959         if (!mrDoc.ValidCol(nField))
7960             return nullptr;
7961 
7962         unique_ptr<ScDBQueryParamBase> pParam( pDBRef->createQueryParam(pQueryRef.get()) );
7963 
7964         if (pParam)
7965         {
7966             // An allowed missing field parameter sets the result field
7967             // to any of the query fields, just to be able to return
7968             // some cell from the iterator.
7969             if ( rMissingField )
7970                 nField = static_cast<SCCOL>(pParam->GetEntry(0).nField);
7971             pParam->mnField = nField;
7972 
7973             SCSIZE nCount = pParam->GetEntryCount();
7974             for ( SCSIZE i=0; i < nCount; i++ )
7975             {
7976                 ScQueryEntry& rEntry = pParam->GetEntry(i);
7977                 if (!rEntry.bDoQuery)
7978                     break;
7979 
7980                 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7981                 sal_uInt32 nIndex = 0;
7982                 OUString aQueryStr = rItem.maString.getString();
7983                 bool bNumber = pFormatter->IsNumberFormat(
7984                     aQueryStr, nIndex, rItem.mfVal);
7985                 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
7986 
7987                 if (!bNumber && pParam->eSearchType == utl::SearchParam::SearchType::Normal)
7988                     pParam->eSearchType = DetectSearchType(aQueryStr, mrDoc);
7989             }
7990             return pParam;
7991         }
7992     }
7993     return nullptr;
7994 }
7995 
7996 void ScInterpreter::DBIterator( ScIterFunc eFunc )
7997 {
7998     double fRes = 0;
7999     KahanSum fErg = 0;
8000     sal_uLong nCount = 0;
8001     bool bMissingField = false;
8002     unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
8003     if (pQueryParam)
8004     {
8005         if (!pQueryParam->IsValidFieldIndex())
8006         {
8007             SetError(FormulaError::NoValue);
8008             return;
8009         }
8010         ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
8011         ScDBQueryDataIterator::Value aValue;
8012         if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
8013         {
8014             switch( eFunc )
8015             {
8016                 case ifPRODUCT: fRes = 1; break;
8017                 case ifMAX:     fRes = -MAXDOUBLE; break;
8018                 case ifMIN:     fRes = MAXDOUBLE; break;
8019                 default: ; // nothing
8020             }
8021 
8022             do
8023             {
8024                 nCount++;
8025                 switch( eFunc )
8026                 {
8027                     case ifAVERAGE:
8028                     case ifSUM:
8029                         fErg += aValue.mfValue;
8030                         break;
8031                     case ifSUMSQ:
8032                         fErg += aValue.mfValue * aValue.mfValue;
8033                         break;
8034                     case ifPRODUCT:
8035                         fRes *= aValue.mfValue;
8036                         break;
8037                     case ifMAX:
8038                         if( aValue.mfValue > fRes ) fRes = aValue.mfValue;
8039                         break;
8040                     case ifMIN:
8041                         if( aValue.mfValue < fRes ) fRes = aValue.mfValue;
8042                         break;
8043                     default: ; // nothing
8044                 }
8045             }
8046             while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
8047         }
8048         SetError(aValue.mnError);
8049     }
8050     else
8051         SetError( FormulaError::IllegalParameter);
8052     switch( eFunc )
8053     {
8054         case ifCOUNT:   fRes = nCount; break;
8055         case ifSUM:     fRes = fErg.get(); break;
8056         case ifSUMSQ:   fRes = fErg.get(); break;
8057         case ifAVERAGE: fRes = div(fErg.get(), nCount); break;
8058         default: ; // nothing
8059     }
8060     PushDouble( fRes );
8061 }
8062 
8063 void ScInterpreter::ScDBSum()
8064 {
8065     DBIterator( ifSUM );
8066 }
8067 
8068 void ScInterpreter::ScDBCount()
8069 {
8070     bool bMissingField = true;
8071     unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
8072     if (pQueryParam)
8073     {
8074         sal_uLong nCount = 0;
8075         if ( bMissingField && pQueryParam->GetType() == ScDBQueryParamBase::INTERNAL )
8076         {   // count all matching records
8077             // TODO: currently the QueryIterators only return cell pointers of
8078             // existing cells, so if a query matches an empty cell there's
8079             // nothing returned, and therefore not counted!
8080             // Since this has ever been the case and this code here only came
8081             // into existence to fix #i6899 and it never worked before we'll
8082             // have to live with it until we reimplement the iterators to also
8083             // return empty cells, which would mean to adapt all callers of
8084             // iterators.
8085             ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pQueryParam.get());
8086             p->nCol2 = p->nCol1; // Don't forget to select only one column.
8087             SCTAB nTab = p->nTab;
8088             // ScQueryCellIteratorDirect doesn't make use of ScDBQueryParamBase::mnField,
8089             // so the source range has to be restricted, like before the introduction
8090             // of ScDBQueryParamBase.
8091             p->nCol1 = p->nCol2 = p->mnField;
8092             ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab, *p, true);
8093             if ( aCellIter.GetFirst() )
8094             {
8095                 do
8096                 {
8097                     nCount++;
8098                 } while ( aCellIter.GetNext() );
8099             }
8100         }
8101         else
8102         {   // count only matching records with a value in the "result" field
8103             if (!pQueryParam->IsValidFieldIndex())
8104             {
8105                 SetError(FormulaError::NoValue);
8106                 return;
8107             }
8108             ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
8109             ScDBQueryDataIterator::Value aValue;
8110             if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
8111             {
8112                 do
8113                 {
8114                     nCount++;
8115                 }
8116                 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
8117             }
8118             SetError(aValue.mnError);
8119         }
8120         PushDouble( nCount );
8121     }
8122     else
8123         PushIllegalParameter();
8124 }
8125 
8126 void ScInterpreter::ScDBCount2()
8127 {
8128     bool bMissingField = true;
8129     unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
8130     if (pQueryParam)
8131     {
8132         if (!pQueryParam->IsValidFieldIndex())
8133         {
8134             SetError(FormulaError::NoValue);
8135             return;
8136         }
8137         sal_uLong nCount = 0;
8138         pQueryParam->mbSkipString = false;
8139         ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
8140         ScDBQueryDataIterator::Value aValue;
8141         if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
8142         {
8143             do
8144             {
8145                 nCount++;
8146             }
8147             while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
8148         }
8149         SetError(aValue.mnError);
8150         PushDouble( nCount );
8151     }
8152     else
8153         PushIllegalParameter();
8154 }
8155 
8156 void ScInterpreter::ScDBAverage()
8157 {
8158     DBIterator( ifAVERAGE );
8159 }
8160 
8161 void ScInterpreter::ScDBMax()
8162 {
8163     DBIterator( ifMAX );
8164 }
8165 
8166 void ScInterpreter::ScDBMin()
8167 {
8168     DBIterator( ifMIN );
8169 }
8170 
8171 void ScInterpreter::ScDBProduct()
8172 {
8173     DBIterator( ifPRODUCT );
8174 }
8175 
8176 void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount )
8177 {
8178     std::vector<double> values;
8179     KahanSum vSum    = 0.0;
8180     KahanSum fSum    = 0.0;
8181 
8182     rValCount = 0.0;
8183     bool bMissingField = false;
8184     unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
8185     if (pQueryParam)
8186     {
8187         if (!pQueryParam->IsValidFieldIndex())
8188         {
8189             SetError(FormulaError::NoValue);
8190             return;
8191         }
8192         ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
8193         ScDBQueryDataIterator::Value aValue;
8194         if (aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE)
8195         {
8196             do
8197             {
8198                 rValCount++;
8199                 values.push_back(aValue.mfValue);
8200                 fSum += aValue.mfValue;
8201             }
8202             while ((aValue.mnError == FormulaError::NONE) && aValIter.GetNext(aValue));
8203         }
8204         SetError(aValue.mnError);
8205     }
8206     else
8207         SetError( FormulaError::IllegalParameter);
8208 
8209     double vMean = fSum.get() / values.size();
8210 
8211     for (double v : values)
8212         vSum += (v - vMean) * (v - vMean);
8213 
8214     rVal = vSum.get();
8215 }
8216 
8217 void ScInterpreter::ScDBStdDev()
8218 {
8219     double fVal, fCount;
8220     GetDBStVarParams( fVal, fCount );
8221     PushDouble( sqrt(fVal/(fCount-1)));
8222 }
8223 
8224 void ScInterpreter::ScDBStdDevP()
8225 {
8226     double fVal, fCount;
8227     GetDBStVarParams( fVal, fCount );
8228     PushDouble( sqrt(fVal/fCount));
8229 }
8230 
8231 void ScInterpreter::ScDBVar()
8232 {
8233     double fVal, fCount;
8234     GetDBStVarParams( fVal, fCount );
8235     PushDouble(fVal/(fCount-1));
8236 }
8237 
8238 void ScInterpreter::ScDBVarP()
8239 {
8240     double fVal, fCount;
8241     GetDBStVarParams( fVal, fCount );
8242     PushDouble(fVal/fCount);
8243 }
8244 
8245 static bool lcl_IsTableStructuredRef(const OUString& sRefStr, sal_Int32& nIndex)
8246 {
8247     nIndex = ScGlobal::FindUnquoted(sRefStr, '[');
8248     return (nIndex > 0 && ScGlobal::FindUnquoted(sRefStr, ']', nIndex + 1) > nIndex);
8249 }
8250 
8251 void ScInterpreter::ScIndirect()
8252 {
8253     sal_uInt8 nParamCount = GetByte();
8254     if ( !MustHaveParamCount( nParamCount, 1, 2 )  )
8255         return;
8256 
8257     // Reference address syntax for INDIRECT is configurable.
8258     FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
8259     if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
8260         // Use the current address syntax if unspecified.
8261         eConv = mrDoc.GetAddressConvention();
8262 
8263     // either CONV_A1_XL_A1 was explicitly configured, or it wasn't possible
8264     // to determine which syntax to use during doc import
8265     bool bTryXlA1 = (eConv == FormulaGrammar::CONV_A1_XL_A1);
8266 
8267     if (nParamCount == 2 && 0.0 == GetDouble() )
8268     {
8269         // Overwrite the config and try Excel R1C1.
8270         eConv = FormulaGrammar::CONV_XL_R1C1;
8271         bTryXlA1 = false;
8272     }
8273 
8274     svl::SharedString sSharedRefStr = GetString();
8275     const OUString & sRefStr = sSharedRefStr.getString();
8276     if (sRefStr.isEmpty())
8277     {
8278         // Bail out early for empty cells, rely on "we do have a string" below.
8279         PushError( FormulaError::NoRef);
8280         return;
8281     }
8282 
8283     const ScAddress::Details aDetails( bTryXlA1 ? FormulaGrammar::CONV_OOO : eConv, aPos );
8284     const ScAddress::Details aDetailsXlA1( FormulaGrammar::CONV_XL_A1, aPos );
8285     SCTAB nTab = aPos.Tab();
8286 
8287     bool bTableRefNamed = false;
8288     sal_Int32 nTableRefNamedIndex = -1;
8289     OUString sTabRefStr;
8290 
8291     // Named expressions and DB range names need to be tried first, as older 1K
8292     // columns allowed names that would now match a 16k columns cell address.
8293     do
8294     {
8295         ScRangeData* pData = ScRangeStringConverter::GetRangeDataFromString( sRefStr, nTab, mrDoc, eConv);
8296         if (!pData)
8297             break;
8298 
8299         // We need this in order to obtain a good range.
8300         pData->ValidateTabRefs();
8301 
8302         ScRange aRange;
8303 
8304         // This is the usual way to treat named ranges containing
8305         // relative references.
8306         if (!pData->IsReference(aRange, aPos))
8307         {
8308             sTabRefStr = pData->GetSymbol();
8309             bTableRefNamed = lcl_IsTableStructuredRef(sTabRefStr, nTableRefNamedIndex);
8310             // if bTableRefNamed is true, we have a name that maps to a table structured reference.
8311             // Such a case is handled below.
8312             break;
8313         }
8314 
8315         if (aRange.aStart == aRange.aEnd)
8316             PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8317                     aRange.aStart.Tab());
8318         else
8319             PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8320                     aRange.aStart.Tab(), aRange.aEnd.Col(),
8321                     aRange.aEnd.Row(), aRange.aEnd.Tab());
8322 
8323         // success!
8324         return;
8325     }
8326     while (false);
8327 
8328     do
8329     {
8330         if (bTableRefNamed)
8331             break;
8332 
8333         const OUString & aName( sSharedRefStr.getIgnoreCaseString() );
8334         ScDBCollection::NamedDBs& rDBs = mrDoc.GetDBCollection()->getNamedDBs();
8335         const ScDBData* pData = rDBs.findByUpperName( aName);
8336         if (!pData)
8337             break;
8338 
8339         ScRange aRange;
8340         pData->GetArea( aRange);
8341 
8342         // In Excel, specifying a table name without [] resolves to the
8343         // same as with [], a range that excludes header and totals
8344         // rows and contains only data rows. Do the same.
8345         if (pData->HasHeader())
8346             aRange.aStart.IncRow();
8347         if (pData->HasTotals())
8348             aRange.aEnd.IncRow(-1);
8349 
8350         if (aRange.aStart.Row() > aRange.aEnd.Row())
8351             break;
8352 
8353         if (aRange.aStart == aRange.aEnd)
8354             PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8355                     aRange.aStart.Tab());
8356         else
8357             PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8358                     aRange.aStart.Tab(), aRange.aEnd.Col(),
8359                     aRange.aEnd.Row(), aRange.aEnd.Tab());
8360 
8361         // success!
8362         return;
8363     }
8364     while (false);
8365 
8366     ScRefAddress aRefAd, aRefAd2;
8367     ScAddress::ExternalInfo aExtInfo;
8368     if ( !bTableRefNamed &&
8369          (ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo) ||
8370             ( bTryXlA1 && ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd,
8371                                            aRefAd2, aDetailsXlA1, &aExtInfo) ) ) )
8372     {
8373         if (aExtInfo.mbExternal)
8374         {
8375             PushExternalDoubleRef(
8376                 aExtInfo.mnFileId, aExtInfo.maTabName,
8377                 aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(),
8378                 aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab());
8379         }
8380         else
8381             PushDoubleRef( aRefAd, aRefAd2);
8382     }
8383     else if ( !bTableRefNamed &&
8384               (ConvertSingleRef(mrDoc, sRefStr, nTab, aRefAd, aDetails, &aExtInfo) ||
8385                 ( bTryXlA1 && ConvertSingleRef (mrDoc, sRefStr, nTab, aRefAd,
8386                                                 aDetailsXlA1, &aExtInfo) ) ) )
8387     {
8388         if (aExtInfo.mbExternal)
8389         {
8390             PushExternalSingleRef(
8391                 aExtInfo.mnFileId, aExtInfo.maTabName, aRefAd.Col(), aRefAd.Row(), aRefAd.Tab());
8392         }
8393         else
8394             PushSingleRef( aRefAd);
8395     }
8396     else
8397     {
8398         // It may be even a TableRef or an external name.
8399         // Anything else that resolves to one reference could be added
8400         // here, but we don't want to compile every arbitrary string. This
8401         // is already nasty enough...
8402         sal_Int32 nIndex = bTableRefNamed ? nTableRefNamedIndex : -1;
8403         bool bTableRef = bTableRefNamed;
8404         if (!bTableRefNamed)
8405             bTableRef = lcl_IsTableStructuredRef(sRefStr, nIndex);
8406         bool bExternalName = false;     // External references would had been consumed above already.
8407         if (!bTableRef)
8408         {
8409             // This is our own file name reference representation centric.. but
8410             // would work also for XL '[doc]'!name and also for
8411             // '[doc]Sheet1'!name ... sickos.
8412             if (sRefStr[0] == '\'')
8413             {
8414                 // Minimum 'a'#name or 'a'!name
8415                 // bTryXlA1 means try both, first our own.
8416                 if (bTryXlA1 || eConv == FormulaGrammar::CONV_OOO)
8417                 {
8418                     nIndex = ScGlobal::FindUnquoted( sRefStr, '#');
8419                     if (nIndex >= 3 && sRefStr[nIndex-1] == '\'')
8420                     {
8421                         bExternalName = true;
8422                         eConv = FormulaGrammar::CONV_OOO;
8423                     }
8424                 }
8425                 if (!bExternalName && (bTryXlA1 || eConv != FormulaGrammar::CONV_OOO))
8426                 {
8427                     nIndex = ScGlobal::FindUnquoted( sRefStr, '!');
8428                     if (nIndex >= 3 && sRefStr[nIndex-1] == '\'')
8429                     {
8430                         bExternalName = true;
8431                     }
8432                 }
8433             }
8434 
8435         }
8436         if (bExternalName || bTableRef)
8437         {
8438             do
8439             {
8440                 ScCompiler aComp( mrDoc, aPos, mrDoc.GetGrammar());
8441                 aComp.SetRefConvention( eConv);     // must be after grammar
8442                 std::unique_ptr<ScTokenArray> pTokArr( aComp.CompileString(bTableRefNamed ? sTabRefStr : sRefStr));
8443 
8444                 if (pTokArr->GetCodeError() != FormulaError::NONE || !pTokArr->GetLen())
8445                     break;
8446 
8447                 // Whatever... use only the specific case.
8448                 if (bExternalName)
8449                 {
8450                     const formula::FormulaToken* pTok = pTokArr->FirstToken();
8451                     if (!pTok || pTok->GetType() != svExternalName)
8452                         break;
8453                 }
8454                 else if (!pTokArr->HasOpCode( ocTableRef))
8455                     break;
8456 
8457                 aComp.CompileTokenArray();
8458 
8459                 // A syntactically valid reference will generate exactly
8460                 // one RPN token, a reference or error. Discard everything
8461                 // else as error.
8462                 if (pTokArr->GetCodeLen() != 1)
8463                     break;
8464 
8465                 ScTokenRef xTok( pTokArr->FirstRPNToken());
8466                 if (!xTok)
8467                     break;
8468 
8469                 switch (xTok->GetType())
8470                 {
8471                     case svSingleRef:
8472                     case svDoubleRef:
8473                     case svExternalSingleRef:
8474                     case svExternalDoubleRef:
8475                     case svError:
8476                         PushTokenRef( xTok);
8477                         // success!
8478                         return;
8479                     default:
8480                         ;   // nothing
8481                 }
8482             }
8483             while (false);
8484         }
8485 
8486         PushError( FormulaError::NoRef);
8487     }
8488 }
8489 
8490 void ScInterpreter::ScAddressFunc()
8491 {
8492     OUString  sTabStr;
8493 
8494     sal_uInt8    nParamCount = GetByte();
8495     if( !MustHaveParamCount( nParamCount, 2, 5 ) )
8496         return;
8497 
8498     if( nParamCount >= 5 )
8499         sTabStr = GetString().getString();
8500 
8501     FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO;      // default
8502     if (nParamCount >= 4 && 0.0 == GetDoubleWithDefault( 1.0))
8503         eConv = FormulaGrammar::CONV_XL_R1C1;
8504     else
8505     {
8506         // If A1 syntax is requested then the actual sheet separator and format
8507         // convention depends on the syntax configured for INDIRECT to match
8508         // that, and if it is unspecified then the document's address syntax.
8509         FormulaGrammar::AddressConvention eForceConv = maCalcConfig.meStringRefAddressSyntax;
8510         if (eForceConv == FormulaGrammar::CONV_UNSPECIFIED)
8511             eForceConv = mrDoc.GetAddressConvention();
8512         if (eForceConv == FormulaGrammar::CONV_XL_A1 || eForceConv == FormulaGrammar::CONV_XL_R1C1)
8513             eConv = FormulaGrammar::CONV_XL_A1;     // for anything Excel use Excel A1
8514     }
8515 
8516     ScRefFlags  nFlags = ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS;   // default
8517     if( nParamCount >= 3 )
8518     {
8519         sal_Int32 n = GetInt32WithDefault(1);
8520         switch ( n )
8521         {
8522             default :
8523                 PushNoValue();
8524                 return;
8525 
8526             case 5:
8527             case 1 : break; // default
8528             case 6:
8529             case 2 : nFlags = ScRefFlags::ROW_ABS; break;
8530             case 7:
8531             case 3 : nFlags = ScRefFlags::COL_ABS; break;
8532             case 8:
8533             case 4 : nFlags = ScRefFlags::ZERO; break; // both relative
8534         }
8535     }
8536     nFlags |= ScRefFlags::VALID | ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID;
8537 
8538     SCCOL nCol = static_cast<SCCOL>(GetInt16());
8539     SCROW nRow = static_cast<SCROW>(GetInt32());
8540     if( eConv == FormulaGrammar::CONV_XL_R1C1 )
8541     {
8542         // YUCK!  The XL interface actually treats rel R1C1 refs differently
8543         // than A1
8544         if( !(nFlags & ScRefFlags::COL_ABS) )
8545             nCol += aPos.Col() + 1;
8546         if( !(nFlags & ScRefFlags::ROW_ABS) )
8547             nRow += aPos.Row() + 1;
8548     }
8549 
8550     --nCol;
8551     --nRow;
8552     if (nGlobalError != FormulaError::NONE || !mrDoc.ValidCol( nCol) || !mrDoc.ValidRow( nRow))
8553     {
8554         PushIllegalArgument();
8555         return;
8556     }
8557 
8558     const ScAddress::Details aDetails( eConv, aPos );
8559     const ScAddress aAdr( nCol, nRow, 0);
8560     OUString aRefStr(aAdr.Format(nFlags, &mrDoc, aDetails));
8561 
8562     if( nParamCount >= 5 && !sTabStr.isEmpty() )
8563     {
8564         OUString aDoc;
8565         if (eConv == FormulaGrammar::CONV_OOO)
8566         {
8567             // Isolate Tab from 'Doc'#Tab
8568             sal_Int32 nPos = ScCompiler::GetDocTabPos( sTabStr);
8569             if (nPos != -1)
8570             {
8571                 if (sTabStr[nPos+1] == '$')
8572                     ++nPos;     // also split 'Doc'#$Tab
8573                 aDoc = sTabStr.copy( 0, nPos+1);
8574                 sTabStr = sTabStr.copy( nPos+1);
8575             }
8576         }
8577         /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may
8578          * need some extra handling to isolate Tab from Doc. */
8579         if (sTabStr[0] != '\'' || !sTabStr.endsWith("'"))
8580             ScCompiler::CheckTabQuotes( sTabStr, eConv);
8581         if (!aDoc.isEmpty())
8582             sTabStr = aDoc + sTabStr;
8583         sTabStr += (eConv == FormulaGrammar::CONV_XL_R1C1 || eConv == FormulaGrammar::CONV_XL_A1) ?
8584             std::u16string_view(u"!") : std::u16string_view(u".");
8585         sTabStr += aRefStr;
8586         PushString( sTabStr );
8587     }
8588     else
8589         PushString( aRefStr );
8590 }
8591 
8592 void ScInterpreter::ScOffset()
8593 {
8594     sal_uInt8 nParamCount = GetByte();
8595     if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
8596         return;
8597 
8598     bool bNewWidth = false;
8599     bool bNewHeight = false;
8600     sal_Int32 nColNew = 1, nRowNew = 1;
8601     if (nParamCount == 5)
8602     {
8603         if (IsMissing())
8604             PopError();
8605         else
8606         {
8607             nColNew = GetInt32();
8608             bNewWidth = true;
8609         }
8610     }
8611     if (nParamCount >= 4)
8612     {
8613         if (IsMissing())
8614             PopError();
8615         else
8616         {
8617             nRowNew = GetInt32();
8618             bNewHeight = true;
8619         }
8620     }
8621     sal_Int32 nColPlus = GetInt32();
8622     sal_Int32 nRowPlus = GetInt32();
8623     if (nGlobalError != FormulaError::NONE)
8624     {
8625         PushError( nGlobalError);
8626         return;
8627     }
8628     if (nColNew <= 0 || nRowNew <= 0)
8629     {
8630         PushIllegalArgument();
8631         return;
8632     }
8633     SCCOL nCol1(0);
8634     SCROW nRow1(0);
8635     SCTAB nTab1(0);
8636     SCCOL nCol2(0);
8637     SCROW nRow2(0);
8638     SCTAB nTab2(0);
8639     switch (GetStackType())
8640     {
8641     case svSingleRef:
8642     {
8643         PopSingleRef(nCol1, nRow1, nTab1);
8644         if (!bNewWidth && !bNewHeight)
8645         {
8646             nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1) + nColPlus);
8647             nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1) + nRowPlus);
8648             if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1))
8649                 PushIllegalArgument();
8650             else
8651                 PushSingleRef(nCol1, nRow1, nTab1);
8652         }
8653         else
8654         {
8655             nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
8656             nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
8657             nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
8658             nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
8659             if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8660                 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2))
8661                 PushIllegalArgument();
8662             else
8663                 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
8664         }
8665         break;
8666     }
8667     case svExternalSingleRef:
8668     {
8669         sal_uInt16 nFileId;
8670         OUString aTabName;
8671         ScSingleRefData aRef;
8672         PopExternalSingleRef(nFileId, aTabName, aRef);
8673         ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
8674         nCol1 = aAbsRef.Col();
8675         nRow1 = aAbsRef.Row();
8676         nTab1 = aAbsRef.Tab();
8677 
8678         if (!bNewWidth && !bNewHeight)
8679         {
8680             nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1) + nColPlus);
8681             nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1) + nRowPlus);
8682             if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1))
8683                 PushIllegalArgument();
8684             else
8685                 PushExternalSingleRef(nFileId, aTabName, nCol1, nRow1, nTab1);
8686         }
8687         else
8688         {
8689             nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
8690             nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
8691             nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
8692             nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
8693             nTab2 = nTab1;
8694             if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8695                 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2))
8696                 PushIllegalArgument();
8697             else
8698                 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8699         }
8700         break;
8701     }
8702     case svDoubleRef:
8703     {
8704         PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8705         if (!bNewWidth)
8706             nColNew = nCol2 - nCol1 + 1;
8707         if (!bNewHeight)
8708             nRowNew = nRow2 - nRow1 + 1;
8709         nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
8710         nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
8711         nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
8712         nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
8713         if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8714             !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2)
8715             PushIllegalArgument();
8716         else
8717             PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
8718         break;
8719     }
8720     case svExternalDoubleRef:
8721     {
8722         sal_uInt16 nFileId;
8723         OUString aTabName;
8724         ScComplexRefData aRef;
8725         PopExternalDoubleRef(nFileId, aTabName, aRef);
8726         ScRange aAbs = aRef.toAbs(mrDoc, aPos);
8727         nCol1 = aAbs.aStart.Col();
8728         nRow1 = aAbs.aStart.Row();
8729         nTab1 = aAbs.aStart.Tab();
8730         nCol2 = aAbs.aEnd.Col();
8731         nRow2 = aAbs.aEnd.Row();
8732         nTab2 = aAbs.aEnd.Tab();
8733         if (!bNewWidth)
8734             nColNew = nCol2 - nCol1 + 1;
8735         if (!bNewHeight)
8736             nRowNew = nRow2 - nRow1 + 1;
8737         nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
8738         nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
8739         nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
8740         nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
8741         if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8742             !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2)
8743             PushIllegalArgument();
8744         else
8745             PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8746         break;
8747     }
8748     default:
8749         PushIllegalParameter();
8750         break;
8751     } // end switch
8752 }
8753 
8754 void ScInterpreter::ScIndex()
8755 {
8756     sal_uInt8 nParamCount = GetByte();
8757     if ( !MustHaveParamCount( nParamCount, 1, 4 ) )
8758         return;
8759 
8760     sal_uInt32 nArea;
8761     size_t nAreaCount;
8762     SCCOL nCol;
8763     SCROW nRow;
8764     if (nParamCount == 4)
8765         nArea = GetUInt32();
8766     else
8767         nArea = 1;
8768     bool bColMissing;
8769     if (nParamCount >= 3)
8770     {
8771         bColMissing = IsMissing();
8772         nCol = static_cast<SCCOL>(GetInt16());
8773     }
8774     else
8775     {
8776         bColMissing = false;
8777         nCol = 0;
8778     }
8779     if (nParamCount >= 2)
8780         nRow = static_cast<SCROW>(GetInt32());
8781     else
8782         nRow = 0;
8783     if (GetStackType() == svRefList)
8784         nAreaCount = (sp ? pStack[sp-1]->GetRefList()->size() : 0);
8785     else
8786         nAreaCount = 1;     // one reference or array or whatever
8787     if (nGlobalError != FormulaError::NONE || nAreaCount == 0 || static_cast<size_t>(nArea) > nAreaCount)
8788     {
8789         PushError( FormulaError::NoRef);
8790         return;
8791     }
8792     else if (nArea < 1 || nCol < 0 || nRow < 0)
8793     {
8794         PushIllegalArgument();
8795         return;
8796     }
8797     switch (GetStackType())
8798     {
8799         case svMatrix:
8800         case svExternalSingleRef:
8801         case svExternalDoubleRef:
8802             {
8803                 if (nArea != 1)
8804                     SetError(FormulaError::IllegalArgument);
8805                 sal_uInt16 nOldSp = sp;
8806                 ScMatrixRef pMat = GetMatrix();
8807                 if (pMat)
8808                 {
8809                     SCSIZE nC, nR;
8810                     pMat->GetDimensions(nC, nR);
8811 
8812                     // Access one element of a vector independent of col/row
8813                     // orientation. Excel documentation does not mention, but
8814                     // i62850 had a .xls example of a row vector accessed by
8815                     // row number returning one element. This
8816                     // INDEX(row_vector;element) behaves the same as
8817                     // INDEX(row_vector;0;element) and thus contradicts Excel
8818                     // documentation where the second parameter is always
8819                     // row_num.
8820                     //
8821                     // ODFF v1.3 in 6.14.6 INDEX states "If DataSource is a
8822                     // one-dimensional row vector, Row is optional, which
8823                     // effectively makes Row act as the column offset into the
8824                     // vector". Guess the first Row is a typo and should read
8825                     // Column instead.
8826 
8827                     const bool bRowVectorSpecial = (nParamCount == 2 || bColMissing);
8828                     const bool bRowVectorElement = (nR == 1 && (nCol != 0 || (bRowVectorSpecial && nRow != 0)));
8829                     const bool bVectorElement = (bRowVectorElement || (nC == 1 && nRow != 0));
8830 
8831                     if (nC == 0 || nR == 0 ||
8832                             (!bVectorElement && (o3tl::make_unsigned(nCol) > nC ||
8833                                                  o3tl::make_unsigned(nRow) > nR)))
8834                         PushError( FormulaError::NoRef);
8835                     else if (nCol == 0 && nRow == 0)
8836                         sp = nOldSp;
8837                     else if (bVectorElement)
8838                     {
8839                         // Vectors here don't replicate to the other dimension.
8840                         SCSIZE nElement, nOtherDimension;
8841                         if (bRowVectorElement && !bRowVectorSpecial)
8842                         {
8843                             nElement = o3tl::make_unsigned(nCol);
8844                             nOtherDimension = o3tl::make_unsigned(nRow);
8845                         }
8846                         else
8847                         {
8848                             nElement = o3tl::make_unsigned(nRow);
8849                             nOtherDimension = o3tl::make_unsigned(nCol);
8850                         }
8851 
8852                         if (nElement == 0 || nElement > nC * nR || nOtherDimension > 1)
8853                             PushError( FormulaError::NoRef);
8854                         else
8855                         {
8856                             --nElement;
8857                             if (pMat->IsStringOrEmpty( nElement))
8858                                 PushString( pMat->GetString(nElement).getString());
8859                             else
8860                                 PushDouble( pMat->GetDouble( nElement));
8861                         }
8862                     }
8863                     else if (nCol == 0)
8864                     {
8865                         ScMatrixRef pResMat = GetNewMat(nC, 1, /*bEmpty*/true);
8866                         if (pResMat)
8867                         {
8868                             SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1);
8869                             for (SCSIZE i = 0; i < nC; i++)
8870                                 if (!pMat->IsStringOrEmpty(i, nRowMinus1))
8871                                     pResMat->PutDouble(pMat->GetDouble(i,
8872                                                 nRowMinus1), i, 0);
8873                                 else
8874                                     pResMat->PutString(pMat->GetString(i, nRowMinus1), i, 0);
8875 
8876                             PushMatrix(pResMat);
8877                         }
8878                         else
8879                             PushError( FormulaError::NoRef);
8880                     }
8881                     else if (nRow == 0)
8882                     {
8883                         ScMatrixRef pResMat = GetNewMat(1, nR, /*bEmpty*/true);
8884                         if (pResMat)
8885                         {
8886                             SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1);
8887                             for (SCSIZE i = 0; i < nR; i++)
8888                                 if (!pMat->IsStringOrEmpty(nColMinus1, i))
8889                                     pResMat->PutDouble(pMat->GetDouble(nColMinus1,
8890                                                 i), i);
8891                                 else
8892                                     pResMat->PutString(pMat->GetString(nColMinus1, i), i);
8893                             PushMatrix(pResMat);
8894                         }
8895                         else
8896                             PushError( FormulaError::NoRef);
8897                     }
8898                     else
8899                     {
8900                         if (!pMat->IsStringOrEmpty( static_cast<SCSIZE>(nCol-1),
8901                                     static_cast<SCSIZE>(nRow-1)))
8902                             PushDouble( pMat->GetDouble(
8903                                         static_cast<SCSIZE>(nCol-1),
8904                                         static_cast<SCSIZE>(nRow-1)));
8905                         else
8906                             PushString( pMat->GetString(
8907                                         static_cast<SCSIZE>(nCol-1),
8908                                         static_cast<SCSIZE>(nRow-1)).getString());
8909                     }
8910                 }
8911             }
8912             break;
8913         case svSingleRef:
8914             {
8915                 SCCOL nCol1 = 0;
8916                 SCROW nRow1 = 0;
8917                 SCTAB nTab1 = 0;
8918                 PopSingleRef( nCol1, nRow1, nTab1);
8919                 if (nCol > 1 || nRow > 1)
8920                     PushError( FormulaError::NoRef);
8921                 else
8922                     PushSingleRef( nCol1, nRow1, nTab1);
8923             }
8924             break;
8925         case svDoubleRef:
8926         case svRefList:
8927             {
8928                 SCCOL nCol1 = 0;
8929                 SCROW nRow1 = 0;
8930                 SCTAB nTab1 = 0;
8931                 SCCOL nCol2 = 0;
8932                 SCROW nRow2 = 0;
8933                 SCTAB nTab2 = 0;
8934                 bool bRowArray = false;
8935                 if (GetStackType() == svRefList)
8936                 {
8937                     FormulaConstTokenRef xRef = PopToken();
8938                     if (nGlobalError != FormulaError::NONE || !xRef)
8939                     {
8940                         PushError( FormulaError::NoRef);
8941                         return;
8942                     }
8943                     ScRange aRange( ScAddress::UNINITIALIZED);
8944                     DoubleRefToRange( (*(xRef->GetRefList()))[nArea-1], aRange);
8945                     aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8946                     if ( nParamCount == 2 && nRow1 == nRow2 )
8947                         bRowArray = true;
8948                 }
8949                 else
8950                 {
8951                     PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8952                     if ( nParamCount == 2 && nRow1 == nRow2 )
8953                         bRowArray = true;
8954                 }
8955                 if ( nTab1 != nTab2 ||
8956                         (nCol > 0 && nCol1+nCol-1 > nCol2) ||
8957                         (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) ||
8958                         ( nRow > nCol2 - nCol1 + 1 && bRowArray ))
8959                     PushError( FormulaError::NoRef);
8960                 else if (nCol == 0 && nRow == 0)
8961                 {
8962                     if ( nCol1 == nCol2 && nRow1 == nRow2 )
8963                         PushSingleRef( nCol1, nRow1, nTab1 );
8964                     else
8965                         PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 );
8966                 }
8967                 else if (nRow == 0)
8968                 {
8969                     if ( nRow1 == nRow2 )
8970                         PushSingleRef( nCol1+nCol-1, nRow1, nTab1 );
8971                     else
8972                         PushDoubleRef( nCol1+nCol-1, nRow1, nTab1,
8973                                 nCol1+nCol-1, nRow2, nTab1 );
8974                 }
8975                 else if (nCol == 0)
8976                 {
8977                     if ( nCol1 == nCol2 )
8978                         PushSingleRef( nCol1, nRow1+nRow-1, nTab1 );
8979                     else if ( bRowArray )
8980                     {
8981                         nCol =static_cast<SCCOL>(nRow);
8982                         nRow = 1;
8983                         PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
8984                     }
8985                     else
8986                         PushDoubleRef( nCol1, nRow1+nRow-1, nTab1,
8987                                 nCol2, nRow1+nRow-1, nTab1);
8988                 }
8989                 else
8990                     PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
8991             }
8992             break;
8993         default:
8994             PopError();
8995             PushError( FormulaError::NoRef);
8996     }
8997 }
8998 
8999 void ScInterpreter::ScMultiArea()
9000 {
9001     // Legacy support, convert to RefList
9002     sal_uInt8 nParamCount = GetByte();
9003     if (MustHaveParamCountMin( nParamCount, 1))
9004     {
9005         while (nGlobalError == FormulaError::NONE && nParamCount-- > 1)
9006         {
9007             ScUnionFunc();
9008         }
9009     }
9010 }
9011 
9012 void ScInterpreter::ScAreas()
9013 {
9014     sal_uInt8 nParamCount = GetByte();
9015     if (!MustHaveParamCount( nParamCount, 1))
9016         return;
9017 
9018     size_t nCount = 0;
9019     switch (GetStackType())
9020     {
9021         case svSingleRef:
9022             {
9023                 FormulaConstTokenRef xT = PopToken();
9024                 ValidateRef( *xT->GetSingleRef());
9025                 ++nCount;
9026             }
9027             break;
9028         case svDoubleRef:
9029             {
9030                 FormulaConstTokenRef xT = PopToken();
9031                 ValidateRef( *xT->GetDoubleRef());
9032                 ++nCount;
9033             }
9034             break;
9035         case svRefList:
9036             {
9037                 FormulaConstTokenRef xT = PopToken();
9038                 ValidateRef( *(xT->GetRefList()));
9039                 nCount += xT->GetRefList()->size();
9040             }
9041             break;
9042         default:
9043             SetError( FormulaError::IllegalParameter);
9044     }
9045     PushDouble( double(nCount));
9046 }
9047 
9048 void ScInterpreter::ScCurrency()
9049 {
9050     sal_uInt8 nParamCount = GetByte();
9051     if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9052         return;
9053 
9054     OUString aStr;
9055     double fDec;
9056     if (nParamCount == 2)
9057     {
9058         fDec = ::rtl::math::approxFloor(GetDouble());
9059         if (fDec < -15.0 || fDec > 15.0)
9060         {
9061             PushIllegalArgument();
9062             return;
9063         }
9064     }
9065     else
9066         fDec = 2.0;
9067     double fVal = GetDouble();
9068     double fFac;
9069     if ( fDec != 0.0 )
9070         fFac = pow( double(10), fDec );
9071     else
9072         fFac = 1.0;
9073     if (fVal < 0.0)
9074         fVal = ceil(fVal*fFac-0.5)/fFac;
9075     else
9076         fVal = floor(fVal*fFac+0.5)/fFac;
9077     const Color* pColor = nullptr;
9078     if ( fDec < 0.0 )
9079         fDec = 0.0;
9080     sal_uLong nIndex = pFormatter->GetStandardFormat(
9081                                     SvNumFormatType::CURRENCY,
9082                                     ScGlobal::eLnge);
9083     if ( static_cast<sal_uInt16>(fDec) != pFormatter->GetFormatPrecision( nIndex ) )
9084     {
9085         OUString sFormatString = pFormatter->GenerateFormat(
9086                                                nIndex,
9087                                                ScGlobal::eLnge,
9088                                                true,        // with thousands separator
9089                                                false,       // not red
9090                                               static_cast<sal_uInt16>(fDec));// decimal places
9091         if (!pFormatter->GetPreviewString(sFormatString,
9092                                           fVal,
9093                                           aStr,
9094                                           &pColor,
9095                                           ScGlobal::eLnge))
9096             SetError(FormulaError::IllegalArgument);
9097     }
9098     else
9099     {
9100         pFormatter->GetOutputString(fVal, nIndex, aStr, &pColor);
9101     }
9102     PushString(aStr);
9103 }
9104 
9105 void ScInterpreter::ScReplace()
9106 {
9107     if ( !MustHaveParamCount( GetByte(), 4 ) )
9108         return;
9109 
9110     OUString aNewStr = GetString().getString();
9111     sal_Int32 nCount = GetStringPositionArgument();
9112     sal_Int32 nPos   = GetStringPositionArgument();
9113     OUString aOldStr = GetString().getString();
9114     if (nPos < 1 || nCount < 0)
9115         PushIllegalArgument();
9116     else
9117     {
9118         sal_Int32 nLen   = aOldStr.getLength();
9119         if (nPos > nLen + 1)
9120             nPos = nLen + 1;
9121         if (nCount > nLen - nPos + 1)
9122             nCount = nLen - nPos + 1;
9123         sal_Int32 nIdx = 0;
9124         sal_Int32 nCnt = 0;
9125         while ( nIdx < nLen && nPos > nCnt + 1 )
9126         {
9127             aOldStr.iterateCodePoints( &nIdx );
9128             ++nCnt;
9129         }
9130         sal_Int32 nStart = nIdx;
9131         while ( nIdx < nLen && nPos + nCount - 1 > nCnt )
9132         {
9133             aOldStr.iterateCodePoints( &nIdx );
9134             ++nCnt;
9135         }
9136         if ( CheckStringResultLen( aOldStr, aNewStr.getLength() - (nIdx - nStart) ) )
9137             aOldStr = aOldStr.replaceAt( nStart, nIdx - nStart, aNewStr );
9138         PushString( aOldStr );
9139     }
9140 }
9141 
9142 void ScInterpreter::ScFixed()
9143 {
9144     sal_uInt8 nParamCount = GetByte();
9145     if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
9146         return;
9147 
9148     OUString aStr;
9149     double fDec;
9150     bool bThousand;
9151     if (nParamCount == 3)
9152         bThousand = !GetBool();     // Param true: no thousands separator
9153     else
9154         bThousand = true;
9155     if (nParamCount >= 2)
9156     {
9157         fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 ));
9158         if (fDec < -15.0 || fDec > 15.0)
9159         {
9160             PushIllegalArgument();
9161             return;
9162         }
9163     }
9164     else
9165         fDec = 2.0;
9166     double fVal = GetDouble();
9167     double fFac;
9168     if ( fDec != 0.0 )
9169         fFac = pow( double(10), fDec );
9170     else
9171         fFac = 1.0;
9172     if (fVal < 0.0)
9173         fVal = ceil(fVal*fFac-0.5)/fFac;
9174     else
9175         fVal = floor(fVal*fFac+0.5)/fFac;
9176     const Color* pColor = nullptr;
9177     if (fDec < 0.0)
9178         fDec = 0.0;
9179     sal_uLong nIndex = pFormatter->GetStandardFormat(
9180                                         SvNumFormatType::NUMBER,
9181                                         ScGlobal::eLnge);
9182     OUString sFormatString = pFormatter->GenerateFormat(
9183                                            nIndex,
9184                                            ScGlobal::eLnge,
9185                                            bThousand,   // with thousands separator
9186                                            false,       // not red
9187                                            static_cast<sal_uInt16>(fDec));// decimal places
9188     if (!pFormatter->GetPreviewString(sFormatString,
9189                                               fVal,
9190                                               aStr,
9191                                               &pColor,
9192                                               ScGlobal::eLnge))
9193         PushIllegalArgument();
9194     else
9195         PushString(aStr);
9196 }
9197 
9198 void ScInterpreter::ScFind()
9199 {
9200     sal_uInt8 nParamCount = GetByte();
9201     if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
9202         return;
9203 
9204     sal_Int32 nCnt;
9205     if (nParamCount == 3)
9206         nCnt = GetDouble();
9207     else
9208         nCnt = 1;
9209     OUString sStr = GetString().getString();
9210     if (nCnt < 1 || nCnt > sStr.getLength())
9211         PushNoValue();
9212     else
9213     {
9214         sal_Int32 nPos = sStr.indexOf(GetString().getString(), nCnt - 1);
9215         if (nPos == -1)
9216             PushNoValue();
9217         else
9218         {
9219             sal_Int32 nIdx = 0;
9220             nCnt = 0;
9221             while ( nIdx < nPos )
9222             {
9223                 sStr.iterateCodePoints( &nIdx );
9224                 ++nCnt;
9225             }
9226             PushDouble( static_cast<double>(nCnt + 1) );
9227         }
9228     }
9229 }
9230 
9231 void ScInterpreter::ScExact()
9232 {
9233     nFuncFmtType = SvNumFormatType::LOGICAL;
9234     if ( MustHaveParamCount( GetByte(), 2 ) )
9235     {
9236         svl::SharedString s1 = GetString();
9237         svl::SharedString s2 = GetString();
9238         PushInt( int(s1.getData() == s2.getData()) );
9239     }
9240 }
9241 
9242 void ScInterpreter::ScLeft()
9243 {
9244     sal_uInt8 nParamCount = GetByte();
9245     if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9246         return;
9247 
9248     sal_Int32 n;
9249     if (nParamCount == 2)
9250     {
9251         n = GetStringPositionArgument();
9252         if (n < 0)
9253         {
9254             PushIllegalArgument();
9255             return ;
9256         }
9257     }
9258     else
9259         n = 1;
9260     OUString aStr = GetString().getString();
9261     sal_Int32 nIdx = 0;
9262     sal_Int32 nCnt = 0;
9263     while ( nIdx < aStr.getLength() && n > nCnt++ )
9264         aStr.iterateCodePoints( &nIdx );
9265     aStr = aStr.copy( 0, nIdx );
9266     PushString( aStr );
9267 }
9268 
9269 namespace {
9270 
9271 struct UBlockScript {
9272     UBlockCode from;
9273     UBlockCode to;
9274 };
9275 
9276 }
9277 
9278 const UBlockScript scriptList[] = {
9279     {UBLOCK_HANGUL_JAMO, UBLOCK_HANGUL_JAMO},
9280     {UBLOCK_CJK_RADICALS_SUPPLEMENT, UBLOCK_HANGUL_SYLLABLES},
9281     {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS,UBLOCK_CJK_RADICALS_SUPPLEMENT },
9282     {UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS,UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS},
9283     {UBLOCK_CJK_COMPATIBILITY_FORMS, UBLOCK_CJK_COMPATIBILITY_FORMS},
9284     {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS},
9285     {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT},
9286     {UBLOCK_CJK_STROKES, UBLOCK_CJK_STROKES}
9287 };
9288 static bool IsDBCS(sal_Unicode currentChar)
9289 {
9290     // for the locale of ja-JP, character U+0x005c and U+0x20ac should be ScriptType::Asian
9291     if( (currentChar == 0x005c || currentChar == 0x20ac) &&
9292           (MsLangId::getConfiguredSystemLanguage() == LANGUAGE_JAPANESE) )
9293         return true;
9294     sal_uInt16 i;
9295     bool bRet = false;
9296     UBlockCode block = ublock_getCode(currentChar);
9297     for ( i = 0; i < SAL_N_ELEMENTS(scriptList); i++) {
9298         if (block <= scriptList[i].to) break;
9299     }
9300     bRet = (i < SAL_N_ELEMENTS(scriptList) && block >= scriptList[i].from);
9301     return bRet;
9302 }
9303 static sal_Int32 lcl_getLengthB( std::u16string_view str, sal_Int32 nPos )
9304 {
9305     sal_Int32 index = 0;
9306     sal_Int32 length = 0;
9307     while ( index < nPos )
9308     {
9309         if (IsDBCS(str[index]))
9310             length += 2;
9311         else
9312             length++;
9313         index++;
9314     }
9315     return length;
9316 }
9317 static sal_Int32 getLengthB(std::u16string_view str)
9318 {
9319     if(str.empty())
9320         return 0;
9321     else
9322         return lcl_getLengthB( str, str.size() );
9323 }
9324 void ScInterpreter::ScLenB()
9325 {
9326     PushDouble( getLengthB(GetString().getString()) );
9327 }
9328 static OUString lcl_RightB(const OUString &rStr, sal_Int32 n)
9329 {
9330     if( n < getLengthB(rStr) )
9331     {
9332         OUStringBuffer aBuf(rStr);
9333         sal_Int32 index = aBuf.getLength();
9334         while(index-- >= 0)
9335         {
9336             if(0 == n)
9337             {
9338                 aBuf.remove( 0, index + 1);
9339                 break;
9340             }
9341             if(-1 == n)
9342             {
9343                 aBuf.remove( 0, index + 2 );
9344                 aBuf.insert( 0, " ");
9345                 break;
9346             }
9347             if(IsDBCS(aBuf[index]))
9348                 n -= 2;
9349             else
9350                 n--;
9351         }
9352         return aBuf.makeStringAndClear();
9353     }
9354     return rStr;
9355 }
9356 void ScInterpreter::ScRightB()
9357 {
9358     sal_uInt8 nParamCount = GetByte();
9359     if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9360         return;
9361 
9362     sal_Int32 n;
9363     if (nParamCount == 2)
9364     {
9365         n = GetStringPositionArgument();
9366         if (n < 0)
9367         {
9368             PushIllegalArgument();
9369             return ;
9370         }
9371     }
9372     else
9373         n = 1;
9374     OUString aStr(lcl_RightB(GetString().getString(), n));
9375     PushString( aStr );
9376 }
9377 static OUString lcl_LeftB(const OUString &rStr, sal_Int32 n)
9378 {
9379     if( n < getLengthB(rStr) )
9380     {
9381         OUStringBuffer aBuf(rStr);
9382         sal_Int32 index = -1;
9383         while(index++ < aBuf.getLength())
9384         {
9385             if(0 == n)
9386             {
9387                 aBuf.truncate(index);
9388                 break;
9389             }
9390             if(-1 == n)
9391             {
9392                 aBuf.truncate( index - 1 );
9393                 aBuf.append(" ");
9394                 break;
9395             }
9396             if(IsDBCS(aBuf[index]))
9397                 n -= 2;
9398             else
9399                 n--;
9400         }
9401         return aBuf.makeStringAndClear();
9402     }
9403     return rStr;
9404 }
9405 void ScInterpreter::ScLeftB()
9406 {
9407     sal_uInt8 nParamCount = GetByte();
9408     if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9409         return;
9410 
9411     sal_Int32 n;
9412     if (nParamCount == 2)
9413     {
9414         n = GetStringPositionArgument();
9415         if (n < 0)
9416         {
9417             PushIllegalArgument();
9418             return ;
9419         }
9420     }
9421     else
9422         n = 1;
9423     OUString aStr(lcl_LeftB(GetString().getString(), n));
9424     PushString( aStr );
9425 }
9426 void ScInterpreter::ScMidB()
9427 {
9428     if ( !MustHaveParamCount( GetByte(), 3 ) )
9429         return;
9430 
9431     const sal_Int32 nCount = GetStringPositionArgument();
9432     const sal_Int32 nStart = GetStringPositionArgument();
9433     OUString aStr = GetString().getString();
9434     if (nStart < 1 || nCount < 0)
9435         PushIllegalArgument();
9436     else
9437     {
9438 
9439         aStr = lcl_LeftB(aStr, nStart + nCount - 1);
9440         sal_Int32 nCnt = getLengthB(aStr) - nStart + 1;
9441         aStr = lcl_RightB(aStr, std::max<sal_Int32>(nCnt,0));
9442         PushString(aStr);
9443     }
9444 }
9445 
9446 void ScInterpreter::ScReplaceB()
9447 {
9448     if ( !MustHaveParamCount( GetByte(), 4 ) )
9449         return;
9450 
9451     OUString aNewStr       = GetString().getString();
9452     const sal_Int32 nCount = GetStringPositionArgument();
9453     const sal_Int32 nPos   = GetStringPositionArgument();
9454     OUString aOldStr       = GetString().getString();
9455     int nLen               = getLengthB( aOldStr );
9456     if (nPos < 1.0 || nPos > nLen || nCount < 0.0 || nPos + nCount -1 > nLen)
9457         PushIllegalArgument();
9458     else
9459     {
9460         // REPLACEB(aOldStr;nPos;nCount;aNewStr) is the same as
9461         // LEFTB(aOldStr;nPos-1) & aNewStr & RIGHT(aOldStr;LENB(aOldStr)-(nPos - 1)-nCount)
9462         OUString aStr1 = lcl_LeftB( aOldStr, nPos - 1 );
9463         OUString aStr3 = lcl_RightB( aOldStr, nLen - nPos - nCount + 1);
9464 
9465         PushString( aStr1 + aNewStr + aStr3 );
9466     }
9467 }
9468 
9469 void ScInterpreter::ScFindB()
9470 {
9471     sal_uInt8 nParamCount = GetByte();
9472     if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
9473         return;
9474 
9475     sal_Int32 nStart;
9476     if ( nParamCount == 3 )
9477         nStart = GetStringPositionArgument();
9478     else
9479         nStart = 1;
9480     OUString aStr  = GetString().getString();
9481     int nLen       = getLengthB( aStr );
9482     OUString asStr = GetString().getString();
9483     int nsLen      = getLengthB( asStr );
9484     if ( nStart < 1 || nStart > nLen - nsLen + 1 )
9485         PushIllegalArgument();
9486     else
9487     {
9488         // create a string from sStr starting at nStart
9489         OUString aBuf = lcl_RightB( aStr, nLen - nStart + 1 );
9490         // search aBuf for asStr
9491         sal_Int32 nPos = aBuf.indexOf( asStr, 0 );
9492         if ( nPos == -1 )
9493             PushNoValue();
9494         else
9495         {
9496             // obtain byte value of nPos
9497             int nBytePos = lcl_getLengthB( aBuf, nPos );
9498             PushDouble( nBytePos + nStart );
9499         }
9500     }
9501 }
9502 
9503 void ScInterpreter::ScSearchB()
9504 {
9505     sal_uInt8 nParamCount = GetByte();
9506     if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
9507         return;
9508 
9509     sal_Int32 nStart;
9510     if ( nParamCount == 3 )
9511     {
9512         nStart = GetStringPositionArgument();
9513         if( nStart < 1 )
9514         {
9515             PushIllegalArgument();
9516             return;
9517         }
9518     }
9519     else
9520         nStart = 1;
9521     OUString aStr = GetString().getString();
9522     sal_Int32 nLen = getLengthB( aStr );
9523     OUString asStr = GetString().getString();
9524     sal_Int32 nsLen = nStart - 1;
9525     if( nsLen >= nLen )
9526         PushNoValue();
9527     else
9528     {
9529         // create a string from sStr starting at nStart
9530         OUString aSubStr( lcl_RightB( aStr, nLen - nStart + 1 ) );
9531         // search aSubStr for asStr
9532         sal_Int32 nPos = 0;
9533         sal_Int32 nEndPos = aSubStr.getLength();
9534         utl::SearchParam::SearchType eSearchType = DetectSearchType( asStr, mrDoc );
9535         utl::SearchParam sPar( asStr, eSearchType, false, '~', false );
9536         utl::TextSearch sT( sPar, ScGlobal::getCharClass() );
9537         if ( !sT.SearchForward( aSubStr, &nPos, &nEndPos ) )
9538             PushNoValue();
9539         else
9540         {
9541             // obtain byte value of nPos
9542             int nBytePos = lcl_getLengthB( aSubStr, nPos );
9543             PushDouble( nBytePos + nStart );
9544         }
9545     }
9546 }
9547 
9548 void ScInterpreter::ScRight()
9549 {
9550     sal_uInt8 nParamCount = GetByte();
9551     if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9552         return;
9553 
9554     sal_Int32 n;
9555     if (nParamCount == 2)
9556     {
9557         n = GetStringPositionArgument();
9558         if (n < 0)
9559         {
9560             PushIllegalArgument();
9561             return ;
9562         }
9563     }
9564     else
9565         n = 1;
9566     OUString aStr = GetString().getString();
9567     sal_Int32 nLen = aStr.getLength();
9568     if ( nLen <= n )
9569         PushString( aStr );
9570     else
9571     {
9572         sal_Int32 nIdx = nLen;
9573         sal_Int32 nCnt = 0;
9574         while ( nIdx > 0 && n > nCnt )
9575         {
9576             aStr.iterateCodePoints( &nIdx, -1 );
9577             ++nCnt;
9578         }
9579         aStr = aStr.copy( nIdx, nLen - nIdx );
9580         PushString( aStr );
9581     }
9582 }
9583 
9584 void ScInterpreter::ScSearch()
9585 {
9586     sal_uInt8 nParamCount = GetByte();
9587     if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
9588         return;
9589 
9590     sal_Int32 nStart;
9591     if (nParamCount == 3)
9592     {
9593         nStart = GetStringPositionArgument();
9594         if( nStart < 1 )
9595         {
9596             PushIllegalArgument();
9597             return;
9598         }
9599     }
9600     else
9601         nStart = 1;
9602     OUString sStr = GetString().getString();
9603     OUString SearchStr = GetString().getString();
9604     sal_Int32 nPos = nStart - 1;
9605     sal_Int32 nEndPos = sStr.getLength();
9606     if( nPos >= nEndPos )
9607         PushNoValue();
9608     else
9609     {
9610         utl::SearchParam::SearchType eSearchType = DetectSearchType( SearchStr, mrDoc );
9611         utl::SearchParam sPar(SearchStr, eSearchType, false, '~', false);
9612         utl::TextSearch sT( sPar, ScGlobal::getCharClass() );
9613         bool bBool = sT.SearchForward(sStr, &nPos, &nEndPos);
9614         if (!bBool)
9615             PushNoValue();
9616         else
9617         {
9618             sal_Int32 nIdx = 0;
9619             sal_Int32 nCnt = 0;
9620             while ( nIdx < nPos )
9621             {
9622                 sStr.iterateCodePoints( &nIdx );
9623                 ++nCnt;
9624             }
9625             PushDouble( static_cast<double>(nCnt + 1) );
9626         }
9627     }
9628 }
9629 
9630 void ScInterpreter::ScRegex()
9631 {
9632     const sal_uInt8 nParamCount = GetByte();
9633     if (!MustHaveParamCount( nParamCount, 2, 4))
9634         return;
9635 
9636     // Flags are supported only for replacement, search match flags can be
9637     // individually and much more flexible set in the regular expression
9638     // pattern using (?ismwx-ismwx)
9639     bool bGlobalReplacement = false;
9640     sal_Int32 nOccurrence = 1;  // default first occurrence, if any
9641     if (nParamCount == 4)
9642     {
9643         // Argument can be either string or double.
9644         double fOccurrence;
9645         svl::SharedString aFlagsString;
9646         bool bDouble;
9647         if (!IsMissing())
9648             bDouble = GetDoubleOrString( fOccurrence, aFlagsString);
9649         else
9650         {
9651             // For an omitted argument keep the default.
9652             PopError();
9653             bDouble = true;
9654             fOccurrence = nOccurrence;
9655         }
9656         if (nGlobalError != FormulaError::NONE)
9657         {
9658             PushError( nGlobalError);
9659             return;
9660         }
9661         if (bDouble)
9662         {
9663             if (!CheckStringPositionArgument( fOccurrence))
9664             {
9665                 PushError( FormulaError::IllegalArgument);
9666                 return;
9667             }
9668             nOccurrence = static_cast<sal_Int32>(fOccurrence);
9669         }
9670         else
9671         {
9672             const OUString aFlags( aFlagsString.getString());
9673             // Empty flags string is valid => no flag set.
9674             if (aFlags.getLength() > 1)
9675             {
9676                 // Only one flag supported.
9677                 PushIllegalArgument();
9678                 return;
9679             }
9680             if (aFlags.getLength() == 1)
9681             {
9682                 if (aFlags.indexOf('g') >= 0)
9683                     bGlobalReplacement = true;
9684                 else
9685                 {
9686                     // Unsupported flag.
9687                     PushIllegalArgument();
9688                     return;
9689                 }
9690             }
9691         }
9692     }
9693 
9694     bool bReplacement = false;
9695     OUString aReplacement;
9696     if (nParamCount >= 3)
9697     {
9698         // A missing argument is not an empty string to replace the match.
9699         // nOccurrence==0 forces no replacement, so simply discard the
9700         // argument.
9701         if (IsMissing() || nOccurrence == 0)
9702             PopError();
9703         else
9704         {
9705             aReplacement = GetString().getString();
9706             bReplacement = true;
9707         }
9708     }
9709     // If bGlobalReplacement==true and bReplacement==false then
9710     // bGlobalReplacement is silently ignored.
9711 
9712     const OUString aExpression = GetString().getString();
9713     const OUString aText = GetString().getString();
9714 
9715     if (nGlobalError != FormulaError::NONE)
9716     {
9717         PushError( nGlobalError);
9718         return;
9719     }
9720 
9721     // 0-th match or replacement is none, return original string early.
9722     if (nOccurrence == 0)
9723     {
9724         PushString( aText);
9725         return;
9726     }
9727 
9728     const icu::UnicodeString aIcuExpression(
9729         false, reinterpret_cast<const UChar*>(aExpression.getStr()), aExpression.getLength());
9730     UErrorCode status = U_ZERO_ERROR;
9731     icu::RegexMatcher aRegexMatcher( aIcuExpression, 0, status);
9732     if (U_FAILURE(status))
9733     {
9734         // Invalid regex.
9735         PushIllegalArgument();
9736         return;
9737     }
9738     // Guard against pathological patterns, limit steps of engine, see
9739     // https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1RegexMatcher.html#a6ebcfcab4fe6a38678c0291643a03a00
9740     aRegexMatcher.setTimeLimit( 23*1000, status);
9741 
9742     const icu::UnicodeString aIcuText(false, reinterpret_cast<const UChar*>(aText.getStr()), aText.getLength());
9743     aRegexMatcher.reset( aIcuText);
9744 
9745     if (!bReplacement)
9746     {
9747         // Find n-th occurrence.
9748         sal_Int32 nCount = 0;
9749         while (aRegexMatcher.find(status) && U_SUCCESS(status) && ++nCount < nOccurrence)
9750             ;
9751         if (U_FAILURE(status))
9752         {
9753             // Some error.
9754             PushIllegalArgument();
9755             return;
9756         }
9757         // n-th match found?
9758         if (nCount != nOccurrence)
9759         {
9760             PushError( FormulaError::NotAvailable);
9761             return;
9762         }
9763         // Extract matched text.
9764         icu::UnicodeString aMatch( aRegexMatcher.group( status));
9765         if (U_FAILURE(status))
9766         {
9767             // Some error.
9768             PushIllegalArgument();
9769             return;
9770         }
9771         OUString aResult( reinterpret_cast<const sal_Unicode*>(aMatch.getBuffer()), aMatch.length());
9772         PushString( aResult);
9773         return;
9774     }
9775 
9776     const icu::UnicodeString aIcuReplacement(
9777         false, reinterpret_cast<const UChar*>(aReplacement.getStr()), aReplacement.getLength());
9778     icu::UnicodeString aReplaced;
9779     if (bGlobalReplacement)
9780         // Replace all occurrences of match with replacement.
9781         aReplaced = aRegexMatcher.replaceAll( aIcuReplacement, status);
9782     else if (nOccurrence == 1)
9783         // Replace first occurrence of match with replacement.
9784         aReplaced = aRegexMatcher.replaceFirst( aIcuReplacement, status);
9785     else
9786     {
9787         // Replace n-th occurrence of match with replacement.
9788         sal_Int32 nCount = 0;
9789         while (aRegexMatcher.find(status) && U_SUCCESS(status))
9790         {
9791             // XXX NOTE: After several RegexMatcher::find() the
9792             // RegexMatcher::appendReplacement() still starts at the
9793             // beginning (or after the last appendReplacement() position
9794             // which is none here) and copies the original text up to the
9795             // current found match and then replaces the found match.
9796             if (++nCount == nOccurrence)
9797             {
9798                 aRegexMatcher.appendReplacement( aReplaced, aIcuReplacement, status);
9799                 break;
9800             }
9801         }
9802         aRegexMatcher.appendTail( aReplaced);
9803     }
9804     if (U_FAILURE(status))
9805     {
9806         // Some error, e.g. extraneous $1 without group.
9807         PushIllegalArgument();
9808         return;
9809     }
9810     OUString aResult( reinterpret_cast<const sal_Unicode*>(aReplaced.getBuffer()), aReplaced.length());
9811     PushString( aResult);
9812 }
9813 
9814 void ScInterpreter::ScMid()
9815 {
9816     if ( !MustHaveParamCount( GetByte(), 3 ) )
9817         return;
9818 
9819     const sal_Int32 nSubLen = GetStringPositionArgument();
9820     const sal_Int32 nStart  = GetStringPositionArgument();
9821     OUString aStr = GetString().getString();
9822     if ( nStart < 1 || nSubLen < 0 )
9823         PushIllegalArgument();
9824     else if (nStart > kScInterpreterMaxStrLen || nSubLen > kScInterpreterMaxStrLen)
9825         PushError(FormulaError::StringOverflow);
9826     else
9827     {
9828         sal_Int32 nLen = aStr.getLength();
9829         sal_Int32 nIdx = 0;
9830         sal_Int32 nCnt = 0;
9831         while ( nIdx < nLen && nStart - 1 > nCnt )
9832         {
9833             aStr.iterateCodePoints( &nIdx );
9834             ++nCnt;
9835         }
9836         sal_Int32 nIdx0 = nIdx;  //start position
9837 
9838         while ( nIdx < nLen && nStart + nSubLen - 1 > nCnt )
9839         {
9840             aStr.iterateCodePoints( &nIdx );
9841             ++nCnt;
9842         }
9843         aStr = aStr.copy( nIdx0, nIdx - nIdx0 );
9844         PushString( aStr );
9845     }
9846 }
9847 
9848 void ScInterpreter::ScText()
9849 {
9850     if ( !MustHaveParamCount( GetByte(), 2 ) )
9851         return;
9852 
9853     OUString sFormatString = GetString().getString();
9854     svl::SharedString aStr;
9855     bool bString = false;
9856     double fVal = 0.0;
9857     switch (GetStackType())
9858     {
9859         case svError:
9860             PopError();
9861             break;
9862         case svDouble:
9863             fVal = PopDouble();
9864             break;
9865         default:
9866             {
9867                 FormulaConstTokenRef xTok( PopToken());
9868                 if (nGlobalError == FormulaError::NONE)
9869                 {
9870                     PushTokenRef( xTok);
9871                     // Temporarily override the ConvertStringToValue()
9872                     // error for GetCellValue() / GetCellValueOrZero()
9873                     FormulaError nSErr = mnStringNoValueError;
9874                     mnStringNoValueError = FormulaError::NotNumericString;
9875                     fVal = GetDouble();
9876                     mnStringNoValueError = nSErr;
9877                     if (nGlobalError == FormulaError::NotNumericString)
9878                     {
9879                         // Not numeric.
9880                         nGlobalError = FormulaError::NONE;
9881                         PushTokenRef( xTok);
9882                         aStr = GetString();
9883                         bString = true;
9884                     }
9885                 }
9886             }
9887     }
9888     if (nGlobalError != FormulaError::NONE)
9889         PushError( nGlobalError);
9890     else if (sFormatString.isEmpty())
9891     {
9892         // Mimic the Excel behaviour that
9893         // * anything numeric returns an empty string
9894         // * text convertible to numeric returns an empty string
9895         // * any other text returns that text
9896         // Conversion was detected above.
9897         if (bString)
9898             PushString( aStr);
9899         else
9900             PushString( OUString());
9901     }
9902     else
9903     {
9904         OUString aResult;
9905         const Color* pColor = nullptr;
9906         LanguageType eCellLang;
9907         const ScPatternAttr* pPattern = mrDoc.GetPattern(
9908                 aPos.Col(), aPos.Row(), aPos.Tab() );
9909         if ( pPattern )
9910             eCellLang = pPattern->GetItem( ATTR_LANGUAGE_FORMAT ).GetValue();
9911         else
9912             eCellLang = ScGlobal::eLnge;
9913         if (bString)
9914         {
9915             if (!pFormatter->GetPreviewString( sFormatString, aStr.getString(),
9916                         aResult, &pColor, eCellLang))
9917                 PushIllegalArgument();
9918             else
9919                 PushString( aResult);
9920         }
9921         else
9922         {
9923             if (!pFormatter->GetPreviewStringGuess( sFormatString, fVal,
9924                         aResult, &pColor, eCellLang))
9925                 PushIllegalArgument();
9926             else
9927                 PushString( aResult);
9928         }
9929     }
9930 }
9931 
9932 void ScInterpreter::ScSubstitute()
9933 {
9934     sal_uInt8 nParamCount = GetByte();
9935     if ( !MustHaveParamCount( nParamCount, 3, 4 ) )
9936         return;
9937 
9938     sal_Int32 nCnt;
9939     if (nParamCount == 4)
9940     {
9941         nCnt = GetStringPositionArgument();
9942         if (nCnt < 1)
9943         {
9944             PushIllegalArgument();
9945             return;
9946         }
9947     }
9948     else
9949         nCnt = 0;
9950     OUString sNewStr = GetString().getString();
9951     OUString sOldStr = GetString().getString();
9952     OUString sStr    = GetString().getString();
9953     sal_Int32 nPos = 0;
9954     sal_Int32 nCount = 0;
9955     std::optional<OUStringBuffer> oResult;
9956     for (sal_Int32 nEnd = sStr.indexOf(sOldStr); nEnd >= 0; nEnd = sStr.indexOf(sOldStr, nEnd))
9957     {
9958         if (nCnt == 0 || ++nCount == nCnt) // Found a replacement cite
9959         {
9960             if (!oResult) // Only allocate buffer when needed
9961                 oResult.emplace(sStr.getLength() + sNewStr.getLength() - sOldStr.getLength());
9962 
9963             oResult->append(sStr.subView(nPos, nEnd - nPos)); // Copy leading unchanged text
9964             if (!CheckStringResultLen(*oResult, sNewStr.getLength()))
9965                 return PushError(GetError());
9966             oResult->append(sNewStr); // Copy  the replacement
9967             nPos = nEnd + sOldStr.getLength();
9968             if (nCnt > 0) // Found the single replacement site - end the loop
9969                 break;
9970         }
9971         nEnd += sOldStr.getLength();
9972     }
9973     if (oResult) // If there were prior replacements, copy the rest, otherwise use original
9974         oResult->append(sStr.subView(nPos, sStr.getLength() - nPos));
9975     PushString(oResult ? oResult->makeStringAndClear() : sStr);
9976 }
9977 
9978 void ScInterpreter::ScRept()
9979 {
9980     if ( !MustHaveParamCount( GetByte(), 2 ) )
9981         return;
9982 
9983     sal_Int32 nCnt = GetStringPositionArgument();
9984     OUString aStr = GetString().getString();
9985     if (nCnt < 0)
9986         PushIllegalArgument();
9987     else if (static_cast<double>(nCnt) * aStr.getLength() > kScInterpreterMaxStrLen)
9988     {
9989         PushError( FormulaError::StringOverflow );
9990     }
9991     else if (nCnt == 0)
9992         PushString( OUString() );
9993     else
9994     {
9995         const sal_Int32 nLen = aStr.getLength();
9996         OUStringBuffer aRes(nCnt*nLen);
9997         while( nCnt-- )
9998             aRes.append(aStr);
9999         PushString( aRes.makeStringAndClear() );
10000     }
10001 }
10002 
10003 void ScInterpreter::ScConcat()
10004 {
10005     sal_uInt8 nParamCount = GetByte();
10006 
10007     //reverse order of parameter stack to simplify processing
10008     ReverseStack(nParamCount);
10009 
10010     OUStringBuffer aRes;
10011     while( nParamCount-- > 0)
10012     {
10013         OUString aStr = GetString().getString();
10014         if (CheckStringResultLen(aRes, aStr.getLength()))
10015             aRes.append(aStr);
10016         else
10017             break;
10018     }
10019     PushString( aRes.makeStringAndClear() );
10020 }
10021 
10022 FormulaError ScInterpreter::GetErrorType()
10023 {
10024     FormulaError nErr;
10025     FormulaError nOldError = nGlobalError;
10026     nGlobalError = FormulaError::NONE;
10027     switch ( GetStackType() )
10028     {
10029         case svRefList :
10030         {
10031             FormulaConstTokenRef x = PopToken();
10032             if (nGlobalError != FormulaError::NONE)
10033                 nErr = nGlobalError;
10034             else
10035             {
10036                 const ScRefList* pRefList = x->GetRefList();
10037                 size_t n = pRefList->size();
10038                 if (!n)
10039                     nErr = FormulaError::NoRef;
10040                 else if (n > 1)
10041                     nErr = FormulaError::NoValue;
10042                 else
10043                 {
10044                     ScRange aRange;
10045                     DoubleRefToRange( (*pRefList)[0], aRange);
10046                     if (nGlobalError != FormulaError::NONE)
10047                         nErr = nGlobalError;
10048                     else
10049                     {
10050                         ScAddress aAdr;
10051                         if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
10052                             nErr = mrDoc.GetErrCode( aAdr );
10053                         else
10054                             nErr = nGlobalError;
10055                     }
10056                 }
10057             }
10058         }
10059         break;
10060         case svDoubleRef :
10061         {
10062             ScRange aRange;
10063             PopDoubleRef( aRange );
10064             if ( nGlobalError != FormulaError::NONE )
10065                 nErr = nGlobalError;
10066             else
10067             {
10068                 ScAddress aAdr;
10069                 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
10070                     nErr = mrDoc.GetErrCode( aAdr );
10071                 else
10072                     nErr = nGlobalError;
10073             }
10074         }
10075         break;
10076         case svSingleRef :
10077         {
10078             ScAddress aAdr;
10079             PopSingleRef( aAdr );
10080             if ( nGlobalError != FormulaError::NONE )
10081                 nErr = nGlobalError;
10082             else
10083                 nErr = mrDoc.GetErrCode( aAdr );
10084         }
10085         break;
10086         default:
10087             PopError();
10088             nErr = nGlobalError;
10089     }
10090     nGlobalError = nOldError;
10091     return nErr;
10092 }
10093 
10094 void ScInterpreter::ScErrorType()
10095 {
10096     FormulaError nErr = GetErrorType();
10097     if ( nErr != FormulaError::NONE )
10098     {
10099         nGlobalError = FormulaError::NONE;
10100         PushDouble( static_cast<double>(nErr) );
10101     }
10102     else
10103     {
10104         PushNA();
10105     }
10106 }
10107 
10108 void ScInterpreter::ScErrorType_ODF()
10109 {
10110     FormulaError nErr = GetErrorType();
10111     sal_uInt16 nErrType;
10112 
10113     switch ( nErr )
10114     {
10115         case FormulaError::NoCode :             // #NULL!
10116             nErrType = 1;
10117             break;
10118         case FormulaError::DivisionByZero :     // #DIV/0!
10119             nErrType = 2;
10120             break;
10121         case FormulaError::NoValue :            // #VALUE!
10122             nErrType = 3;
10123             break;
10124         case FormulaError::NoRef :              // #REF!
10125             nErrType = 4;
10126             break;
10127         case FormulaError::NoName :             // #NAME?
10128             nErrType = 5;
10129             break;
10130         case FormulaError::IllegalFPOperation : // #NUM!
10131             nErrType = 6;
10132             break;
10133         case FormulaError::NotAvailable :       // #N/A
10134             nErrType = 7;
10135             break;
10136         /*
10137         #GETTING_DATA is a message that can appear in Excel when a large or
10138         complex worksheet is being calculated. In Excel 2007 and newer,
10139         operations are grouped so more complicated cells may finish after
10140         earlier ones do. While the calculations are still processing, the
10141         unfinished cells may display #GETTING_DATA.
10142         Because the message is temporary and disappears when the calculations
10143         complete, this isn’t a true error.
10144         No calc error code known (yet).
10145 
10146         case :                       // GETTING_DATA
10147             nErrType = 8;
10148             break;
10149         */
10150         default :
10151             nErrType = 0;
10152             break;
10153     }
10154 
10155     if ( nErrType )
10156     {
10157         nGlobalError =FormulaError::NONE;
10158         PushDouble( nErrType );
10159     }
10160     else
10161         PushNA();
10162 }
10163 
10164 static bool MayBeRegExp( std::u16string_view rStr )
10165 {
10166     if ( rStr.empty() || (rStr.size() == 1 && rStr[0] != '.') )
10167         return false;   // single meta characters can not be a regexp
10168     // First two characters are wildcard '?' and '*' characters.
10169     std::u16string_view cre(u"?*+.[]^$\\<>()|");
10170     return rStr.find_first_of(cre) != std::u16string_view::npos;
10171 }
10172 
10173 static bool MayBeWildcard( std::u16string_view rStr )
10174 {
10175     // Wildcards with '~' escape, if there are no wildcards then an escaped
10176     // character does not make sense, but it modifies the search pattern in an
10177     // Excel compatible wildcard search...
10178     std::u16string_view cw(u"*?~");
10179     return rStr.find_first_of(cw) != std::u16string_view::npos;
10180 }
10181 
10182 utl::SearchParam::SearchType ScInterpreter::DetectSearchType( std::u16string_view rStr, const ScDocument& rDoc )
10183 {
10184     const auto eType = rDoc.GetDocOptions().GetFormulaSearchType();
10185     if ((eType == utl::SearchParam::SearchType::Wildcard && MayBeWildcard(rStr))
10186         || (eType == utl::SearchParam::SearchType::Regexp && MayBeRegExp(rStr)))
10187         return eType;
10188     return utl::SearchParam::SearchType::Normal;
10189 }
10190 
10191 static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument& rDoc, ScInterpreterContext& rContext,
10192         const ScQueryParam & rParam, const ScQueryEntry & rEntry, const ScFormulaCell* cell,
10193         const ScComplexRefData* refData )
10194 {
10195     if (rEntry.eOp != SC_EQUAL)
10196     {
10197         // range lookup <= or >=
10198         SCCOL nCol;
10199         SCROW nRow;
10200         ScQueryCellIteratorDirect aCellIter( rDoc, rContext, rParam.nTab, rParam, false);
10201         if( aCellIter.FindEqualOrSortedLastInRange( nCol, nRow ))
10202         {
10203             o_rResultPos.SetCol( nCol);
10204             o_rResultPos.SetRow( nRow);
10205             return true;
10206         }
10207     }
10208     else // EQUAL
10209     {
10210         if( ScQueryCellIteratorSortedCache::CanBeUsed( rDoc, rParam, rParam.nTab, cell, refData, rContext ))
10211         {
10212             ScQueryCellIteratorSortedCache aCellIter( rDoc, rContext, rParam.nTab, rParam, false);
10213             if (aCellIter.GetFirst())
10214             {
10215                 o_rResultPos.SetCol( aCellIter.GetCol());
10216                 o_rResultPos.SetRow( aCellIter.GetRow());
10217                 return true;
10218             }
10219         }
10220         else
10221         {
10222             ScQueryCellIteratorDirect aCellIter( rDoc, rContext, rParam.nTab, rParam, false);
10223             if (aCellIter.GetFirst())
10224             {
10225                 o_rResultPos.SetCol( aCellIter.GetCol());
10226                 o_rResultPos.SetRow( aCellIter.GetRow());
10227                 return true;
10228             }
10229         }
10230     }
10231     return false;
10232 }
10233 
10234 // tdf#121052:
10235 // =VLOOKUP(SearchCriterion; RangeArray; Index; Sorted)
10236 //  [SearchCriterion] is the value searched for in the first column of the array.
10237 //  [RangeArray] is the reference, which is to comprise at least two columns.
10238 //  [Index] is the number of the column in the array that contains the value to be returned. The first column has the number 1.
10239 //
10240 // Prerequisite of lcl_getPrevRowWithEmptyValueLookup():
10241 //      Value referenced by [SearchCriterion] is empty.
10242 // lcl_getPrevRowWithEmptyValueLookup() performs following checks:
10243 // - if we run query with "exact match" mode (i.e. VLOOKUP)
10244 // - and if we already have the same lookup done before but for another row
10245 //   which is also had empty [SearchCriterion]
10246 //
10247 // then
10248 //   we could say, that for current row we could reuse results of the cached call which was done for the row2
10249 //   In this case we return row index, which is >= 0.
10250 //
10251 // Elsewhere
10252 //   -1 is returned, which will lead to default behavior =>
10253 //   complete lookup will be done in RangeArray inside lcl_LookupQuery() method.
10254 //
10255 // This method was added only for speed up to avoid several useless complete
10256 // lookups inside [RangeArray] for searching empty strings.
10257 //
10258 static SCROW lcl_getPrevRowWithEmptyValueLookup( const ScLookupCache& rCache,
10259         const ScLookupCache::QueryCriteria& rCriteria, const ScQueryParam & rParam)
10260 {
10261     // is lookup value empty?
10262     const ScQueryEntry& rEntry = rParam.GetEntry(0);
10263     const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
10264     if (! rItem.maString.getString().isEmpty())
10265         return -1; // not found
10266 
10267     // try to find the row index for which we have already performed lookup
10268     // and have some result of it inside cache
10269     return rCache.lookup( rCriteria );
10270 }
10271 
10272 bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos,
10273         const ScQueryParam & rParam, const ScComplexRefData* refData ) const
10274 {
10275     bool bFound = false;
10276     const ScQueryEntry& rEntry = rParam.GetEntry(0);
10277     bool bColumnsMatch = (rParam.nCol1 == rEntry.nField);
10278     OSL_ENSURE( bColumnsMatch, "ScInterpreter::LookupQueryWithCache: columns don't match");
10279     // At least all volatile functions that generate indirect references have
10280     // to force non-cached lookup.
10281     /* TODO: We could further classify volatile functions into reference
10282      * generating and not reference generating functions to have to force less
10283      * direct lookups here. We could even further attribute volatility per
10284      * parameter so it would affect only the lookup range parameter. */
10285     if (!bColumnsMatch || GetVolatileType() != NOT_VOLATILE)
10286         bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry, pMyFormulaCell, refData);
10287     else
10288     {
10289         ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab,
10290                 rParam.nCol2, rParam.nRow2, rParam.nTab);
10291         ScLookupCache& rCache = mrDoc.GetLookupCache( aLookupRange, &mrContext );
10292         ScLookupCache::QueryCriteria aCriteria( rEntry);
10293         ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos,
10294                 aCriteria, aPos);
10295 
10296         // tdf#121052: Slow load of cells with VLOOKUP with references to empty cells
10297         // This check was added only for speed up to avoid several useless complete
10298         // lookups inside [RangeArray] for searching empty strings.
10299         if (eCacheResult == ScLookupCache::NOT_CACHED && aCriteria.isEmptyStringQuery())
10300         {
10301             const SCROW nPrevRowWithEmptyValueLookup = lcl_getPrevRowWithEmptyValueLookup(rCache, aCriteria, rParam);
10302             if (nPrevRowWithEmptyValueLookup >= 0)
10303             {
10304                 // make the same lookup using cache with different row index
10305                 // (this lookup was already cached)
10306                 ScAddress aPosPrev(aPos);
10307                 aPosPrev.SetRow(nPrevRowWithEmptyValueLookup);
10308 
10309                 eCacheResult = rCache.lookup( o_rResultPos, aCriteria, aPosPrev );
10310             }
10311         }
10312 
10313         switch (eCacheResult)
10314         {
10315             case ScLookupCache::NOT_CACHED :
10316             case ScLookupCache::CRITERIA_DIFFERENT :
10317                 bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry, pMyFormulaCell, refData);
10318                 if (eCacheResult == ScLookupCache::NOT_CACHED)
10319                     rCache.insert( o_rResultPos, aCriteria, aPos, bFound);
10320                 break;
10321             case ScLookupCache::FOUND :
10322                 bFound = true;
10323                 break;
10324             case ScLookupCache::NOT_AVAILABLE :
10325                 ;   // nothing, bFound remains FALSE
10326                 break;
10327         }
10328     }
10329     return bFound;
10330 }
10331 
10332 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
10333