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