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