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