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