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