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