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