Resolves: tdf#162093 TableRef item specifier may occur standalone
[LibreOffice.git] / sc / source / core / tool / interpr1.cxx
blob73b25d541fa48722c8f002d9a6500b5157657f03
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <interpre.hxx>
22 #include <scitems.hxx>
23 #include <editeng/langitem.hxx>
24 #include <editeng/justifyitem.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <o3tl/temporary.hxx>
27 #include <osl/thread.h>
28 #include <unotools/textsearch.hxx>
29 #include <svl/numformat.hxx>
30 #include <svl/zforlist.hxx>
31 #include <svl/zformat.hxx>
32 #include <tools/urlobj.hxx>
33 #include <unotools/charclass.hxx>
34 #include <sfx2/docfile.hxx>
35 #include <sfx2/printer.hxx>
36 #include <unotools/collatorwrapper.hxx>
37 #include <unotools/transliterationwrapper.hxx>
38 #include <rtl/character.hxx>
39 #include <rtl/ustring.hxx>
40 #include <sal/log.hxx>
41 #include <osl/diagnose.h>
42 #include <unicode/uchar.h>
43 #include <unicode/regex.h>
44 #include <i18nlangtag/mslangid.hxx>
46 #include <patattr.hxx>
47 #include <global.hxx>
48 #include <document.hxx>
49 #include <dociter.hxx>
50 #include <docsh.hxx>
51 #include <sfx2/linkmgr.hxx>
52 #include <formulacell.hxx>
53 #include <scmatrix.hxx>
54 #include <docoptio.hxx>
55 #include <attrib.hxx>
56 #include <jumpmatrix.hxx>
57 #include <cellkeytranslator.hxx>
58 #include <lookupcache.hxx>
59 #include <rangenam.hxx>
60 #include <rangeutl.hxx>
61 #include <compiler.hxx>
62 #include <externalrefmgr.hxx>
63 #include <doubleref.hxx>
64 #include <queryparam.hxx>
65 #include <queryiter.hxx>
66 #include <tokenarray.hxx>
67 #include <compare.hxx>
68 #include <comphelper/lok.hxx>
69 #include <comphelper/processfactory.hxx>
70 #include <comphelper/string.hxx>
71 #include <svl/sharedstringpool.hxx>
73 #include <stdlib.h>
74 #include <memory>
75 #include <vector>
76 #include <limits>
77 #include <string_view>
78 #include <cmath>
80 const sal_uInt64 n2power48 = SAL_CONST_UINT64( 281474976710656); // 2^48
82 ScCalcConfig *ScInterpreter::mpGlobalConfig = nullptr;
84 using namespace formula;
85 using ::std::unique_ptr;
87 void ScInterpreter::ScIfJump()
89 const short* pJump = pCur->GetJump();
90 short nJumpCount = pJump[ 0 ];
91 MatrixJumpConditionToMatrix();
92 switch ( GetStackType() )
94 case svMatrix:
96 ScMatrixRef pMat = PopMatrix();
97 if ( !pMat )
98 PushIllegalParameter();
99 else
101 FormulaConstTokenRef xNew;
102 ScTokenMatrixMap::const_iterator aMapIter;
103 // DoubleError handled by JumpMatrix
104 pMat->SetErrorInterpreter( nullptr);
105 SCSIZE nCols, nRows;
106 pMat->GetDimensions( nCols, nRows );
107 if ( nCols == 0 || nRows == 0 )
109 PushIllegalArgument();
110 return;
112 else if ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end())
113 xNew = (*aMapIter).second;
114 else
116 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
117 pCur->GetOpCode(), nCols, nRows));
118 for ( SCSIZE nC=0; nC < nCols; ++nC )
120 for ( SCSIZE nR=0; nR < nRows; ++nR )
122 double fVal;
123 bool bTrue;
124 bool bIsValue = pMat->IsValue(nC, nR);
125 if (bIsValue)
127 fVal = pMat->GetDouble(nC, nR);
128 bIsValue = std::isfinite(fVal);
129 bTrue = bIsValue && (fVal != 0.0);
130 if (bTrue)
131 fVal = 1.0;
133 else
135 // Treat empty and empty path as 0, but string
136 // as error. ScMatrix::IsValueOrEmpty() returns
137 // true for any empty, empty path, empty cell,
138 // empty result.
139 bIsValue = pMat->IsValueOrEmpty(nC, nR);
140 bTrue = false;
141 fVal = (bIsValue ? 0.0 : CreateDoubleError( FormulaError::NoValue));
143 if ( bTrue )
144 { // TRUE
145 if( nJumpCount >= 2 )
146 { // THEN path
147 pJumpMat->SetJump( nC, nR, fVal,
148 pJump[ 1 ],
149 pJump[ nJumpCount ]);
151 else
152 { // no parameter given for THEN
153 pJumpMat->SetJump( nC, nR, fVal,
154 pJump[ nJumpCount ],
155 pJump[ nJumpCount ]);
158 else
159 { // FALSE
160 if( nJumpCount == 3 && bIsValue )
161 { // ELSE path
162 pJumpMat->SetJump( nC, nR, fVal,
163 pJump[ 2 ],
164 pJump[ nJumpCount ]);
166 else
167 { // no parameter given for ELSE,
168 // or DoubleError
169 pJumpMat->SetJump( nC, nR, fVal,
170 pJump[ nJumpCount ],
171 pJump[ nJumpCount ]);
176 xNew = new ScJumpMatrixToken( pJumpMat );
177 GetTokenMatrixMap().emplace(pCur, xNew);
179 if (!xNew)
181 PushIllegalArgument();
182 return;
184 PushTokenRef( xNew);
185 // set endpoint of path for main code line
186 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
189 break;
190 default:
192 const bool bCondition = GetBool();
193 if (nGlobalError != FormulaError::NONE)
194 { // Propagate error, not THEN- or ELSE-path, jump behind.
195 PushError(nGlobalError);
196 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
198 else if ( bCondition )
199 { // TRUE
200 if( nJumpCount >= 2 )
201 { // THEN path
202 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
204 else
205 { // no parameter given for THEN
206 nFuncFmtType = SvNumFormatType::LOGICAL;
207 PushInt(1);
208 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
211 else
212 { // FALSE
213 if( nJumpCount == 3 )
214 { // ELSE path
215 aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] );
217 else
218 { // no parameter given for ELSE
219 nFuncFmtType = SvNumFormatType::LOGICAL;
220 PushInt(0);
221 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
228 /** Store a matrix value in another matrix in the context of that other matrix
229 is the result matrix of a jump matrix. All arguments must be valid and are
230 not checked. */
231 static void lcl_storeJumpMatResult(
232 const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR )
234 if ( pMat->IsValue( nC, nR ) )
236 double fVal = pMat->GetDouble( nC, nR );
237 pJumpMat->PutResultDouble( fVal, nC, nR );
239 else if ( pMat->IsEmpty( nC, nR ) )
241 pJumpMat->PutResultEmpty( nC, nR );
243 else
245 pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR);
249 void ScInterpreter::ScIfError( bool bNAonly )
251 const short* pJump = pCur->GetJump();
252 short nJumpCount = pJump[ 0 ];
253 if (!sp || nJumpCount != 2)
255 // Reset nGlobalError here to not propagate the old error, if any.
256 nGlobalError = (sp ? FormulaError::ParameterExpected : FormulaError::UnknownStackVariable);
257 PushError( nGlobalError);
258 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
259 return;
262 FormulaConstTokenRef xToken( pStack[ sp - 1 ] );
263 bool bError = false;
264 FormulaError nOldGlobalError = nGlobalError;
265 nGlobalError = FormulaError::NONE;
267 MatrixJumpConditionToMatrix();
268 switch (GetStackType())
270 default:
271 Pop();
272 // Act on implicitly propagated error, if any.
273 if (nOldGlobalError != FormulaError::NONE)
274 nGlobalError = nOldGlobalError;
275 if (nGlobalError != FormulaError::NONE)
276 bError = true;
277 break;
278 case svError:
279 PopError();
280 bError = true;
281 break;
282 case svDoubleRef:
283 case svSingleRef:
285 ScAddress aAdr;
286 if (!PopDoubleRefOrSingleRef( aAdr))
287 bError = true;
288 else
291 ScRefCellValue aCell(mrDoc, aAdr);
292 nGlobalError = GetCellErrCode(aCell);
293 if (nGlobalError != FormulaError::NONE)
294 bError = true;
297 break;
298 case svExternalSingleRef:
299 case svExternalDoubleRef:
301 double fVal;
302 svl::SharedString aStr;
303 // Handles also existing jump matrix case and sets error on
304 // elements.
305 GetDoubleOrStringFromMatrix( fVal, aStr);
306 if (nGlobalError != FormulaError::NONE)
307 bError = true;
309 break;
310 case svMatrix:
312 const ScMatrixRef pMat = PopMatrix();
313 if (!pMat || (nGlobalError != FormulaError::NONE && (!bNAonly || nGlobalError == FormulaError::NotAvailable)))
315 bError = true;
316 break; // switch
318 // If the matrix has no queried error at all we can simply use
319 // it as result and don't need to bother with jump matrix.
320 SCSIZE nErrorCol = ::std::numeric_limits<SCSIZE>::max(),
321 nErrorRow = ::std::numeric_limits<SCSIZE>::max();
322 SCSIZE nCols, nRows;
323 pMat->GetDimensions( nCols, nRows );
324 if (nCols == 0 || nRows == 0)
326 bError = true;
327 break; // switch
329 for (SCSIZE nC=0; nC < nCols && !bError; ++nC)
331 for (SCSIZE nR=0; nR < nRows && !bError; ++nR)
333 FormulaError nErr = pMat->GetError( nC, nR );
334 if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
336 bError = true;
337 nErrorCol = nC;
338 nErrorRow = nR;
342 if (!bError)
343 break; // switch, we're done and have the result
345 FormulaConstTokenRef xNew;
346 ScTokenMatrixMap::const_iterator aMapIter;
347 if ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end())
349 xNew = (*aMapIter).second;
351 else
353 const ScMatrix* pMatPtr = pMat.get();
354 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
355 pCur->GetOpCode(), nCols, nRows));
356 // Init all jumps to no error to save single calls. Error
357 // is the exceptional condition.
358 const double fFlagResult = CreateDoubleError( FormulaError::JumpMatHasResult);
359 pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] );
360 // Up to first error position simply store results, no need
361 // to evaluate error conditions again.
362 SCSIZE nC = 0, nR = 0;
363 for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ )
365 for (nR = 0 ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR)
367 lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
369 if (nC != nErrorCol && nR != nErrorRow)
370 ++nC;
372 // Now the mixed cases.
373 for ( ; nC < nCols; ++nC)
375 for ( ; nR < nRows; ++nR)
377 FormulaError nErr = pMat->GetError( nC, nR );
378 if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
379 { // TRUE, THEN path
380 pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] );
382 else
383 { // FALSE, EMPTY path, store result instead
384 lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
387 nR = 0;
389 xNew = new ScJumpMatrixToken( pJumpMat );
390 GetTokenMatrixMap().emplace( pCur, xNew );
392 nGlobalError = nOldGlobalError;
393 PushTokenRef( xNew );
394 // set endpoint of path for main code line
395 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
396 return;
398 break;
401 if (bError && (!bNAonly || nGlobalError == FormulaError::NotAvailable))
403 // error, calculate 2nd argument
404 nGlobalError = FormulaError::NONE;
405 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
407 else
409 // no error, push 1st argument and continue
410 nGlobalError = nOldGlobalError;
411 PushTokenRef( xToken);
412 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
416 void ScInterpreter::ScChooseJump()
418 // We have to set a jump, if there was none chosen because of an error set
419 // it to endpoint.
420 bool bHaveJump = false;
421 const short* pJump = pCur->GetJump();
422 short nJumpCount = pJump[ 0 ];
423 MatrixJumpConditionToMatrix();
424 switch ( GetStackType() )
426 case svMatrix:
428 ScMatrixRef pMat = PopMatrix();
429 if ( !pMat )
430 PushIllegalParameter();
431 else
433 FormulaConstTokenRef xNew;
434 ScTokenMatrixMap::const_iterator aMapIter;
435 // DoubleError handled by JumpMatrix
436 pMat->SetErrorInterpreter( nullptr);
437 SCSIZE nCols, nRows;
438 pMat->GetDimensions( nCols, nRows );
439 if ( nCols == 0 || nRows == 0 )
440 PushIllegalParameter();
441 else if ((aMapIter = maTokenMatrixMap.find(
442 pCur)) != maTokenMatrixMap.end())
443 xNew = (*aMapIter).second;
444 else
446 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
447 pCur->GetOpCode(), nCols, nRows));
448 for ( SCSIZE nC=0; nC < nCols; ++nC )
450 for ( SCSIZE nR=0; nR < nRows; ++nR )
452 double fVal;
453 bool bIsValue = pMat->IsValue(nC, nR);
454 if ( bIsValue )
456 fVal = pMat->GetDouble(nC, nR);
457 bIsValue = std::isfinite( fVal );
458 if ( bIsValue )
460 fVal = ::rtl::math::approxFloor( fVal);
461 if ( (fVal < 1) || (fVal >= nJumpCount))
463 bIsValue = false;
464 fVal = CreateDoubleError(
465 FormulaError::IllegalArgument);
469 else
471 fVal = CreateDoubleError( FormulaError::NoValue);
473 if ( bIsValue )
475 pJumpMat->SetJump( nC, nR, fVal,
476 pJump[ static_cast<short>(fVal) ],
477 pJump[ nJumpCount ]);
479 else
481 pJumpMat->SetJump( nC, nR, fVal,
482 pJump[ nJumpCount ],
483 pJump[ nJumpCount ]);
487 xNew = new ScJumpMatrixToken( pJumpMat );
488 GetTokenMatrixMap().emplace(pCur, xNew);
490 if (xNew)
492 PushTokenRef( xNew);
493 // set endpoint of path for main code line
494 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
495 bHaveJump = true;
499 break;
500 default:
502 sal_Int16 nJumpIndex = GetInt16();
503 if (nGlobalError == FormulaError::NONE && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount))
505 aCode.Jump( pJump[ static_cast<short>(nJumpIndex) ], pJump[ nJumpCount ] );
506 bHaveJump = true;
508 else
509 PushIllegalArgument();
512 if (!bHaveJump)
513 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
516 static void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, SCSIZE nParmCols, SCSIZE nParmRows )
518 SCSIZE nJumpCols, nJumpRows;
519 SCSIZE nResCols, nResRows;
520 SCSIZE nAdjustCols, nAdjustRows;
521 pJumpM->GetDimensions( nJumpCols, nJumpRows );
522 pJumpM->GetResMatDimensions( nResCols, nResRows );
523 if (!(( nJumpCols == 1 && nParmCols > nResCols ) ||
524 ( nJumpRows == 1 && nParmRows > nResRows )))
525 return;
527 if ( nJumpCols == 1 && nJumpRows == 1 )
529 nAdjustCols = std::max(nParmCols, nResCols);
530 nAdjustRows = std::max(nParmRows, nResRows);
532 else if ( nJumpCols == 1 )
534 nAdjustCols = nParmCols;
535 nAdjustRows = nResRows;
537 else
539 nAdjustCols = nResCols;
540 nAdjustRows = nParmRows;
542 pJumpM->SetNewResMat( nAdjustCols, nAdjustRows );
545 bool ScInterpreter::JumpMatrix( short nStackLevel )
547 pJumpMatrix = pStack[sp-nStackLevel]->GetJumpMatrix();
548 bool bHasResMat = pJumpMatrix->HasResultMatrix();
549 SCSIZE nC, nR;
550 if ( nStackLevel == 2 )
552 if ( aCode.HasStacked() )
553 aCode.Pop(); // pop what Jump() pushed
554 else
556 assert(!"pop goes the weasel");
559 if ( !bHasResMat )
561 Pop();
562 SetError( FormulaError::UnknownStackVariable );
564 else
566 pJumpMatrix->GetPos( nC, nR );
567 switch ( GetStackType() )
569 case svDouble:
571 double fVal = GetDouble();
572 if ( nGlobalError != FormulaError::NONE )
574 fVal = CreateDoubleError( nGlobalError );
575 nGlobalError = FormulaError::NONE;
577 pJumpMatrix->PutResultDouble( fVal, nC, nR );
579 break;
580 case svString:
582 svl::SharedString aStr = GetString();
583 if ( nGlobalError != FormulaError::NONE )
585 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
586 nC, nR);
587 nGlobalError = FormulaError::NONE;
589 else
590 pJumpMatrix->PutResultString(aStr, nC, nR);
592 break;
593 case svSingleRef:
595 FormulaConstTokenRef xRef = pStack[sp-1];
596 ScAddress aAdr;
597 PopSingleRef( aAdr );
598 if ( nGlobalError != FormulaError::NONE )
600 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
601 nC, nR);
602 nGlobalError = FormulaError::NONE;
604 else
606 ScRefCellValue aCell(mrDoc, aAdr);
607 if (aCell.hasEmptyValue())
608 pJumpMatrix->PutResultEmpty( nC, nR );
609 else if (aCell.hasNumeric())
611 double fVal = GetCellValue(aAdr, aCell);
612 if ( nGlobalError != FormulaError::NONE )
614 fVal = CreateDoubleError(
615 nGlobalError);
616 nGlobalError = FormulaError::NONE;
618 pJumpMatrix->PutResultDouble( fVal, nC, nR );
620 else
622 svl::SharedString aStr;
623 GetCellString(aStr, aCell);
624 if ( nGlobalError != FormulaError::NONE )
626 pJumpMatrix->PutResultDouble( CreateDoubleError(
627 nGlobalError), nC, nR);
628 nGlobalError = FormulaError::NONE;
630 else
631 pJumpMatrix->PutResultString(aStr, nC, nR);
635 formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16);
636 if (eReturnType == ParamClass::Reference)
638 /* TODO: What about error handling and do we actually
639 * need the result matrix above at all in this case? */
640 ScComplexRefData aRef;
641 aRef.Ref1 = aRef.Ref2 = *(xRef->GetSingleRef());
642 pJumpMatrix->GetRefList().push_back( aRef);
645 break;
646 case svDoubleRef:
647 { // upper left plus offset within matrix
648 FormulaConstTokenRef xRef = pStack[sp-1];
649 double fVal;
650 ScRange aRange;
651 PopDoubleRef( aRange );
652 if ( nGlobalError != FormulaError::NONE )
654 fVal = CreateDoubleError( nGlobalError );
655 nGlobalError = FormulaError::NONE;
656 pJumpMatrix->PutResultDouble( fVal, nC, nR );
658 else
660 // Do not modify the original range because we use it
661 // to adjust the size of the result matrix if necessary.
662 ScAddress aAdr( aRange.aStart);
663 sal_uLong nCol = static_cast<sal_uLong>(aAdr.Col()) + nC;
664 sal_uLong nRow = static_cast<sal_uLong>(aAdr.Row()) + nR;
665 if ((nCol > o3tl::make_unsigned(aRange.aEnd.Col()) &&
666 aRange.aEnd.Col() != aRange.aStart.Col())
667 || (nRow > o3tl::make_unsigned(aRange.aEnd.Row()) &&
668 aRange.aEnd.Row() != aRange.aStart.Row()))
670 fVal = CreateDoubleError( FormulaError::NotAvailable );
671 pJumpMatrix->PutResultDouble( fVal, nC, nR );
673 else
675 // Replicate column and/or row of a vector if it is
676 // one. Note that this could be a range reference
677 // that in fact consists of only one cell, e.g. A1:A1
678 if (aRange.aEnd.Col() == aRange.aStart.Col())
679 nCol = aRange.aStart.Col();
680 if (aRange.aEnd.Row() == aRange.aStart.Row())
681 nRow = aRange.aStart.Row();
682 aAdr.SetCol( static_cast<SCCOL>(nCol) );
683 aAdr.SetRow( static_cast<SCROW>(nRow) );
684 ScRefCellValue aCell(mrDoc, aAdr);
685 if (aCell.hasEmptyValue())
686 pJumpMatrix->PutResultEmpty( nC, nR );
687 else if (aCell.hasNumeric())
689 double fCellVal = GetCellValue(aAdr, aCell);
690 if ( nGlobalError != FormulaError::NONE )
692 fCellVal = CreateDoubleError(
693 nGlobalError);
694 nGlobalError = FormulaError::NONE;
696 pJumpMatrix->PutResultDouble( fCellVal, nC, nR );
698 else
700 svl::SharedString aStr;
701 GetCellString(aStr, aCell);
702 if ( nGlobalError != FormulaError::NONE )
704 pJumpMatrix->PutResultDouble( CreateDoubleError(
705 nGlobalError), nC, nR);
706 nGlobalError = FormulaError::NONE;
708 else
709 pJumpMatrix->PutResultString(aStr, nC, nR);
712 SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
713 SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
714 lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows );
717 formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16);
718 if (eReturnType == ParamClass::Reference)
720 /* TODO: What about error handling and do we actually
721 * need the result matrix above at all in this case? */
722 pJumpMatrix->GetRefList().push_back( *(xRef->GetDoubleRef()));
725 break;
726 case svExternalSingleRef:
728 ScExternalRefCache::TokenRef pToken;
729 PopExternalSingleRef(pToken);
730 if (nGlobalError != FormulaError::NONE)
732 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), nC, nR );
733 nGlobalError = FormulaError::NONE;
735 else
737 switch (pToken->GetType())
739 case svDouble:
740 pJumpMatrix->PutResultDouble( pToken->GetDouble(), nC, nR );
741 break;
742 case svString:
743 pJumpMatrix->PutResultString( pToken->GetString(), nC, nR );
744 break;
745 case svEmptyCell:
746 pJumpMatrix->PutResultEmpty( nC, nR );
747 break;
748 default:
749 // svError was already handled (set by
750 // PopExternalSingleRef()) with nGlobalError
751 // above.
752 assert(!"unhandled svExternalSingleRef case");
753 pJumpMatrix->PutResultDouble( CreateDoubleError(
754 FormulaError::UnknownStackVariable), nC, nR );
758 break;
759 case svExternalDoubleRef:
760 case svMatrix:
761 { // match matrix offsets
762 double fVal;
763 ScMatrixRef pMat = GetMatrix();
764 if ( nGlobalError != FormulaError::NONE )
766 fVal = CreateDoubleError( nGlobalError );
767 nGlobalError = FormulaError::NONE;
768 pJumpMatrix->PutResultDouble( fVal, nC, nR );
770 else if ( !pMat )
772 fVal = CreateDoubleError( FormulaError::UnknownVariable );
773 pJumpMatrix->PutResultDouble( fVal, nC, nR );
775 else
777 SCSIZE nCols, nRows;
778 pMat->GetDimensions( nCols, nRows );
779 if ((nCols <= nC && nCols != 1) ||
780 (nRows <= nR && nRows != 1))
782 fVal = CreateDoubleError( FormulaError::NotAvailable );
783 pJumpMatrix->PutResultDouble( fVal, nC, nR );
785 else
787 // GetMatrix() does SetErrorInterpreter() at the
788 // matrix, do not propagate an error from
789 // matrix->GetValue() as global error.
790 pMat->SetErrorInterpreter(nullptr);
791 lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR);
793 lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows );
796 break;
797 case svError:
799 PopError();
800 double fVal = CreateDoubleError( nGlobalError);
801 nGlobalError = FormulaError::NONE;
802 pJumpMatrix->PutResultDouble( fVal, nC, nR );
804 break;
805 default:
807 Pop();
808 double fVal = CreateDoubleError( FormulaError::IllegalArgument);
809 pJumpMatrix->PutResultDouble( fVal, nC, nR );
814 bool bCont = pJumpMatrix->Next( nC, nR );
815 if ( bCont )
817 double fBool;
818 short nStart, nNext, nStop;
819 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
820 while ( bCont && nStart == nNext )
821 { // push all results that have no jump path
822 if ( bHasResMat && (GetDoubleErrorValue( fBool) != FormulaError::JumpMatHasResult) )
824 // a false without path results in an empty path value
825 if ( fBool == 0.0 )
826 pJumpMatrix->PutResultEmptyPath( nC, nR );
827 else
828 pJumpMatrix->PutResultDouble( fBool, nC, nR );
830 bCont = pJumpMatrix->Next( nC, nR );
831 if ( bCont )
832 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
834 if ( bCont && nStart != nNext )
836 const ScTokenVec & rParams = pJumpMatrix->GetJumpParameters();
837 for ( auto const & i : rParams )
839 // This is not the current state of the interpreter, so
840 // push without error, and elements' errors are coded into
841 // double.
842 PushWithoutError(*i);
844 aCode.Jump( nStart, nNext, nStop );
847 if ( !bCont )
848 { // We're done with it, throw away jump matrix, keep result.
849 // For an intermediate result of Reference use the array of references
850 // if there are more than one reference and the current ForceArray
851 // context is ReferenceOrRefArray.
852 // Else (also for a final result of Reference) use the matrix.
853 // Treat the result of a jump command as final and use the matrix (see
854 // tdf#115493 for why).
855 if (pCur->GetInForceArray() == ParamClass::ReferenceOrRefArray &&
856 pJumpMatrix->GetRefList().size() > 1 &&
857 ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16) == ParamClass::Reference &&
858 !FormulaCompiler::IsOpCodeJumpCommand( pJumpMatrix->GetOpCode()) &&
859 aCode.PeekNextOperator())
861 FormulaTokenRef xRef = new ScRefListToken(true);
862 *(xRef->GetRefList()) = pJumpMatrix->GetRefList();
863 pJumpMatrix = nullptr;
864 Pop();
865 PushTokenRef( xRef);
866 maTokenMatrixMap.erase( pCur);
867 // There's no result matrix to remember in this case.
869 else
871 ScMatrix* pResMat = pJumpMatrix->GetResultMatrix();
872 pJumpMatrix = nullptr;
873 Pop();
874 PushMatrix( pResMat );
875 // Remove jump matrix from map and remember result matrix in case it
876 // could be reused in another path of the same condition.
877 maTokenMatrixMap.erase( pCur);
878 maTokenMatrixMap.emplace(pCur, pStack[sp-1]);
880 return true;
882 return false;
885 double ScInterpreter::Compare( ScQueryOp eOp )
887 sc::Compare aComp;
888 aComp.meOp = eOp;
889 aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
890 for( short i = 1; i >= 0; i-- )
892 sc::Compare::Cell& rCell = aComp.maCells[i];
894 switch ( GetRawStackType() )
896 case svEmptyCell:
897 Pop();
898 rCell.mbEmpty = true;
899 break;
900 case svMissing:
901 case svDouble:
902 rCell.mfValue = GetDouble();
903 rCell.mbValue = true;
904 break;
905 case svString:
906 rCell.maStr = GetString();
907 rCell.mbValue = false;
908 break;
909 case svDoubleRef :
910 case svSingleRef :
912 ScAddress aAdr;
913 if ( !PopDoubleRefOrSingleRef( aAdr ) )
914 break;
915 ScRefCellValue aCell(mrDoc, aAdr);
916 if (aCell.hasEmptyValue())
917 rCell.mbEmpty = true;
918 else if (aCell.hasString())
920 svl::SharedString aStr;
921 GetCellString(aStr, aCell);
922 rCell.maStr = aStr;
923 rCell.mbValue = false;
925 else
927 rCell.mfValue = GetCellValue(aAdr, aCell);
928 rCell.mbValue = true;
931 break;
932 case svExternalSingleRef:
934 ScMatrixRef pMat = GetMatrix();
935 if (!pMat)
937 SetError( FormulaError::IllegalParameter);
938 break;
941 SCSIZE nC, nR;
942 pMat->GetDimensions(nC, nR);
943 if (!nC || !nR)
945 SetError( FormulaError::IllegalParameter);
946 break;
948 if (pMat->IsEmpty(0, 0))
949 rCell.mbEmpty = true;
950 else if (pMat->IsStringOrEmpty(0, 0))
952 rCell.maStr = pMat->GetString(0, 0);
953 rCell.mbValue = false;
955 else
957 rCell.mfValue = pMat->GetDouble(0, 0);
958 rCell.mbValue = true;
961 break;
962 case svExternalDoubleRef:
963 // TODO: Find out how to handle this...
964 // Xcl generates a position dependent intersection using
965 // col/row, as it seems to do for all range references, not
966 // only in compare context. We'd need a general implementation
967 // for that behavior similar to svDoubleRef in scalar and array
968 // mode. Which also means we'd have to change all places where
969 // it currently is handled along with svMatrix.
970 default:
971 PopError();
972 SetError( FormulaError::IllegalParameter);
973 break;
976 if( nGlobalError != FormulaError::NONE )
977 return 0;
978 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
979 return sc::CompareFunc(aComp);
982 sc::RangeMatrix ScInterpreter::CompareMat( ScQueryOp eOp, sc::CompareOptions* pOptions )
984 sc::Compare aComp;
985 aComp.meOp = eOp;
986 aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
987 sc::RangeMatrix aMat[2];
988 ScAddress aAdr;
989 for( short i = 1; i >= 0; i-- )
991 sc::Compare::Cell& rCell = aComp.maCells[i];
993 switch (GetRawStackType())
995 case svEmptyCell:
996 Pop();
997 rCell.mbEmpty = true;
998 break;
999 case svMissing:
1000 case svDouble:
1001 rCell.mfValue = GetDouble();
1002 rCell.mbValue = true;
1003 break;
1004 case svString:
1005 rCell.maStr = GetString();
1006 rCell.mbValue = false;
1007 break;
1008 case svSingleRef:
1010 PopSingleRef( aAdr );
1011 ScRefCellValue aCell(mrDoc, aAdr);
1012 if (aCell.hasEmptyValue())
1013 rCell.mbEmpty = true;
1014 else if (aCell.hasString())
1016 svl::SharedString aStr;
1017 GetCellString(aStr, aCell);
1018 rCell.maStr = aStr;
1019 rCell.mbValue = false;
1021 else
1023 rCell.mfValue = GetCellValue(aAdr, aCell);
1024 rCell.mbValue = true;
1027 break;
1028 case svExternalSingleRef:
1029 case svExternalDoubleRef:
1030 case svDoubleRef:
1031 case svMatrix:
1032 aMat[i] = GetRangeMatrix();
1033 if (!aMat[i].mpMat)
1034 SetError( FormulaError::IllegalParameter);
1035 else
1036 aMat[i].mpMat->SetErrorInterpreter(nullptr);
1037 // errors are transported as DoubleError inside matrix
1038 break;
1039 default:
1040 PopError();
1041 SetError( FormulaError::IllegalParameter);
1042 break;
1046 sc::RangeMatrix aRes;
1048 if (nGlobalError != FormulaError::NONE)
1050 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
1051 return aRes;
1054 if (aMat[0].mpMat && aMat[1].mpMat)
1056 SCSIZE nC0, nC1;
1057 SCSIZE nR0, nR1;
1058 aMat[0].mpMat->GetDimensions(nC0, nR0);
1059 aMat[1].mpMat->GetDimensions(nC1, nR1);
1060 SCSIZE nC = std::max( nC0, nC1 );
1061 SCSIZE nR = std::max( nR0, nR1 );
1062 aRes.mpMat = GetNewMat( nC, nR, /*bEmpty*/true );
1063 if (!aRes.mpMat)
1064 return aRes;
1065 for ( SCSIZE j=0; j<nC; j++ )
1067 for ( SCSIZE k=0; k<nR; k++ )
1069 SCSIZE nCol = j, nRow = k;
1070 if (aMat[0].mpMat->ValidColRowOrReplicated(nCol, nRow) &&
1071 aMat[1].mpMat->ValidColRowOrReplicated(nCol, nRow))
1073 for ( short i=1; i>=0; i-- )
1075 sc::Compare::Cell& rCell = aComp.maCells[i];
1077 if (aMat[i].mpMat->IsStringOrEmpty(j, k))
1079 rCell.mbValue = false;
1080 rCell.maStr = aMat[i].mpMat->GetString(j, k);
1081 rCell.mbEmpty = aMat[i].mpMat->IsEmpty(j, k);
1083 else
1085 rCell.mbValue = true;
1086 rCell.mfValue = aMat[i].mpMat->GetDouble(j, k);
1087 rCell.mbEmpty = false;
1090 aRes.mpMat->PutDouble( sc::CompareFunc( aComp, pOptions), j, k);
1092 else
1093 aRes.mpMat->PutError( FormulaError::NoValue, j, k);
1097 switch (eOp)
1099 case SC_EQUAL:
1100 aRes.mpMat->CompareEqual();
1101 break;
1102 case SC_LESS:
1103 aRes.mpMat->CompareLess();
1104 break;
1105 case SC_GREATER:
1106 aRes.mpMat->CompareGreater();
1107 break;
1108 case SC_LESS_EQUAL:
1109 aRes.mpMat->CompareLessEqual();
1110 break;
1111 case SC_GREATER_EQUAL:
1112 aRes.mpMat->CompareGreaterEqual();
1113 break;
1114 case SC_NOT_EQUAL:
1115 aRes.mpMat->CompareNotEqual();
1116 break;
1117 default:
1118 SAL_WARN("sc", "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp));
1119 aRes.mpMat.reset();
1120 return aRes;
1123 else if (aMat[0].mpMat || aMat[1].mpMat)
1125 size_t i = ( aMat[0].mpMat ? 0 : 1);
1127 aRes.mnCol1 = aMat[i].mnCol1;
1128 aRes.mnRow1 = aMat[i].mnRow1;
1129 aRes.mnTab1 = aMat[i].mnTab1;
1130 aRes.mnCol2 = aMat[i].mnCol2;
1131 aRes.mnRow2 = aMat[i].mnRow2;
1132 aRes.mnTab2 = aMat[i].mnTab2;
1134 ScMatrix& rMat = *aMat[i].mpMat;
1135 aRes.mpMat = rMat.CompareMatrix(aComp, i, pOptions);
1136 if (!aRes.mpMat)
1137 return aRes;
1140 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
1141 return aRes;
1144 ScMatrixRef ScInterpreter::QueryMat( const ScMatrixRef& pMat, sc::CompareOptions& rOptions )
1146 SvNumFormatType nSaveCurFmtType = nCurFmtType;
1147 SvNumFormatType nSaveFuncFmtType = nFuncFmtType;
1148 PushMatrix( pMat);
1149 const ScQueryEntry::Item& rItem = rOptions.aQueryEntry.GetQueryItem();
1150 if (rItem.meType == ScQueryEntry::ByString)
1151 PushString(rItem.maString.getString());
1152 else
1153 PushDouble(rItem.mfVal);
1154 ScMatrixRef pResultMatrix = CompareMat(rOptions.aQueryEntry.eOp, &rOptions).mpMat;
1155 nCurFmtType = nSaveCurFmtType;
1156 nFuncFmtType = nSaveFuncFmtType;
1157 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
1159 SetError( FormulaError::IllegalParameter);
1160 return pResultMatrix;
1163 return pResultMatrix;
1166 void ScInterpreter::ScEqual()
1168 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1170 sc::RangeMatrix aMat = CompareMat(SC_EQUAL);
1171 if (!aMat.mpMat)
1173 PushIllegalParameter();
1174 return;
1177 PushMatrix(aMat);
1179 else
1180 PushInt( int(Compare( SC_EQUAL) == 0) );
1183 void ScInterpreter::ScNotEqual()
1185 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1187 sc::RangeMatrix aMat = CompareMat(SC_NOT_EQUAL);
1188 if (!aMat.mpMat)
1190 PushIllegalParameter();
1191 return;
1194 PushMatrix(aMat);
1196 else
1197 PushInt( int(Compare( SC_NOT_EQUAL) != 0) );
1200 void ScInterpreter::ScLess()
1202 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1204 sc::RangeMatrix aMat = CompareMat(SC_LESS);
1205 if (!aMat.mpMat)
1207 PushIllegalParameter();
1208 return;
1211 PushMatrix(aMat);
1213 else
1214 PushInt( int(Compare( SC_LESS) < 0) );
1217 void ScInterpreter::ScGreater()
1219 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1221 sc::RangeMatrix aMat = CompareMat(SC_GREATER);
1222 if (!aMat.mpMat)
1224 PushIllegalParameter();
1225 return;
1228 PushMatrix(aMat);
1230 else
1231 PushInt( int(Compare( SC_GREATER) > 0) );
1234 void ScInterpreter::ScLessEqual()
1236 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1238 sc::RangeMatrix aMat = CompareMat(SC_LESS_EQUAL);
1239 if (!aMat.mpMat)
1241 PushIllegalParameter();
1242 return;
1245 PushMatrix(aMat);
1247 else
1248 PushInt( int(Compare( SC_LESS_EQUAL) <= 0) );
1251 void ScInterpreter::ScGreaterEqual()
1253 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1255 sc::RangeMatrix aMat = CompareMat(SC_GREATER_EQUAL);
1256 if (!aMat.mpMat)
1258 PushIllegalParameter();
1259 return;
1262 PushMatrix(aMat);
1264 else
1265 PushInt( int(Compare( SC_GREATER_EQUAL) >= 0) );
1268 void ScInterpreter::ScAnd()
1270 nFuncFmtType = SvNumFormatType::LOGICAL;
1271 short nParamCount = GetByte();
1272 if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1273 return;
1275 bool bHaveValue = false;
1276 bool bRes = true;
1277 size_t nRefInList = 0;
1278 while( nParamCount-- > 0)
1280 if ( nGlobalError == FormulaError::NONE )
1282 switch ( GetStackType() )
1284 case svDouble :
1285 bHaveValue = true;
1286 bRes &= ( PopDouble() != 0.0 );
1287 break;
1288 case svString :
1289 Pop();
1290 SetError( FormulaError::NoValue );
1291 break;
1292 case svSingleRef :
1294 ScAddress aAdr;
1295 PopSingleRef( aAdr );
1296 if ( nGlobalError == FormulaError::NONE )
1298 ScRefCellValue aCell(mrDoc, aAdr);
1299 if (aCell.hasNumeric())
1301 bHaveValue = true;
1302 bRes &= ( GetCellValue(aAdr, aCell) != 0.0 );
1304 // else: Xcl raises no error here
1307 break;
1308 case svDoubleRef:
1309 case svRefList:
1311 ScRange aRange;
1312 PopDoubleRef( aRange, nParamCount, nRefInList);
1313 if ( nGlobalError == FormulaError::NONE )
1315 double fVal;
1316 FormulaError nErr = FormulaError::NONE;
1317 ScValueIterator aValIter( mrContext, aRange );
1318 if ( aValIter.GetFirst( fVal, nErr ) && nErr == FormulaError::NONE )
1320 bHaveValue = true;
1323 bRes &= ( fVal != 0.0 );
1324 } while ( (nErr == FormulaError::NONE) &&
1325 aValIter.GetNext( fVal, nErr ) );
1327 SetError( nErr );
1330 break;
1331 case svExternalSingleRef:
1332 case svExternalDoubleRef:
1333 case svMatrix:
1335 ScMatrixRef pMat = GetMatrix();
1336 if ( pMat )
1338 bHaveValue = true;
1339 double fVal = pMat->And();
1340 FormulaError nErr = GetDoubleErrorValue( fVal );
1341 if ( nErr != FormulaError::NONE )
1343 SetError( nErr );
1344 bRes = false;
1346 else
1347 bRes &= (fVal != 0.0);
1349 // else: GetMatrix did set FormulaError::IllegalParameter
1351 break;
1352 default:
1353 PopError();
1354 SetError( FormulaError::IllegalParameter);
1357 else
1358 Pop();
1360 if ( bHaveValue )
1361 PushInt( int(bRes) );
1362 else
1363 PushNoValue();
1366 void ScInterpreter::ScOr()
1368 nFuncFmtType = SvNumFormatType::LOGICAL;
1369 short nParamCount = GetByte();
1370 if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1371 return;
1373 bool bHaveValue = false;
1374 bool bRes = false;
1375 size_t nRefInList = 0;
1376 while( nParamCount-- > 0)
1378 if ( nGlobalError == FormulaError::NONE )
1380 switch ( GetStackType() )
1382 case svDouble :
1383 bHaveValue = true;
1384 bRes |= ( PopDouble() != 0.0 );
1385 break;
1386 case svString :
1387 Pop();
1388 SetError( FormulaError::NoValue );
1389 break;
1390 case svSingleRef :
1392 ScAddress aAdr;
1393 PopSingleRef( aAdr );
1394 if ( nGlobalError == FormulaError::NONE )
1396 ScRefCellValue aCell(mrDoc, aAdr);
1397 if (aCell.hasNumeric())
1399 bHaveValue = true;
1400 bRes |= ( GetCellValue(aAdr, aCell) != 0.0 );
1402 // else: Xcl raises no error here
1405 break;
1406 case svDoubleRef:
1407 case svRefList:
1409 ScRange aRange;
1410 PopDoubleRef( aRange, nParamCount, nRefInList);
1411 if ( nGlobalError == FormulaError::NONE )
1413 double fVal;
1414 FormulaError nErr = FormulaError::NONE;
1415 ScValueIterator aValIter( mrContext, aRange );
1416 if ( aValIter.GetFirst( fVal, nErr ) )
1418 bHaveValue = true;
1421 bRes |= ( fVal != 0.0 );
1422 } while ( (nErr == FormulaError::NONE) &&
1423 aValIter.GetNext( fVal, nErr ) );
1425 SetError( nErr );
1428 break;
1429 case svExternalSingleRef:
1430 case svExternalDoubleRef:
1431 case svMatrix:
1433 bHaveValue = true;
1434 ScMatrixRef pMat = GetMatrix();
1435 if ( pMat )
1437 bHaveValue = true;
1438 double fVal = pMat->Or();
1439 FormulaError nErr = GetDoubleErrorValue( fVal );
1440 if ( nErr != FormulaError::NONE )
1442 SetError( nErr );
1443 bRes = false;
1445 else
1446 bRes |= (fVal != 0.0);
1448 // else: GetMatrix did set FormulaError::IllegalParameter
1450 break;
1451 default:
1452 PopError();
1453 SetError( FormulaError::IllegalParameter);
1456 else
1457 Pop();
1459 if ( bHaveValue )
1460 PushInt( int(bRes) );
1461 else
1462 PushNoValue();
1465 void ScInterpreter::ScXor()
1468 nFuncFmtType = SvNumFormatType::LOGICAL;
1469 short nParamCount = GetByte();
1470 if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1471 return;
1473 bool bHaveValue = false;
1474 bool bRes = false;
1475 size_t nRefInList = 0;
1476 while( nParamCount-- > 0)
1478 if ( nGlobalError == FormulaError::NONE )
1480 switch ( GetStackType() )
1482 case svDouble :
1483 bHaveValue = true;
1484 bRes ^= ( PopDouble() != 0.0 );
1485 break;
1486 case svString :
1487 Pop();
1488 SetError( FormulaError::NoValue );
1489 break;
1490 case svSingleRef :
1492 ScAddress aAdr;
1493 PopSingleRef( aAdr );
1494 if ( nGlobalError == FormulaError::NONE )
1496 ScRefCellValue aCell(mrDoc, aAdr);
1497 if (aCell.hasNumeric())
1499 bHaveValue = true;
1500 bRes ^= ( GetCellValue(aAdr, aCell) != 0.0 );
1502 /* TODO: set error? Excel doesn't have XOR, but
1503 * doesn't set an error in this case for AND and
1504 * OR. */
1507 break;
1508 case svDoubleRef:
1509 case svRefList:
1511 ScRange aRange;
1512 PopDoubleRef( aRange, nParamCount, nRefInList);
1513 if ( nGlobalError == FormulaError::NONE )
1515 double fVal;
1516 FormulaError nErr = FormulaError::NONE;
1517 ScValueIterator aValIter( mrContext, aRange );
1518 if ( aValIter.GetFirst( fVal, nErr ) )
1520 bHaveValue = true;
1523 bRes ^= ( fVal != 0.0 );
1524 } while ( (nErr == FormulaError::NONE) &&
1525 aValIter.GetNext( fVal, nErr ) );
1527 SetError( nErr );
1530 break;
1531 case svExternalSingleRef:
1532 case svExternalDoubleRef:
1533 case svMatrix:
1535 bHaveValue = true;
1536 ScMatrixRef pMat = GetMatrix();
1537 if ( pMat )
1539 bHaveValue = true;
1540 double fVal = pMat->Xor();
1541 FormulaError nErr = GetDoubleErrorValue( fVal );
1542 if ( nErr != FormulaError::NONE )
1544 SetError( nErr );
1545 bRes = false;
1547 else
1548 bRes ^= ( fVal != 0.0 );
1550 // else: GetMatrix did set FormulaError::IllegalParameter
1552 break;
1553 default:
1554 PopError();
1555 SetError( FormulaError::IllegalParameter);
1558 else
1559 Pop();
1561 if ( bHaveValue )
1562 PushInt( int(bRes) );
1563 else
1564 PushNoValue();
1567 void ScInterpreter::ScNeg()
1569 // Simple negation doesn't change current format type to number, keep
1570 // current type.
1571 nFuncFmtType = nCurFmtType;
1572 switch ( GetStackType() )
1574 case svMatrix :
1576 ScMatrixRef pMat = GetMatrix();
1577 if ( !pMat )
1578 PushIllegalParameter();
1579 else
1581 SCSIZE nC, nR;
1582 pMat->GetDimensions( nC, nR );
1583 ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true );
1584 if ( !pResMat )
1585 PushIllegalArgument();
1586 else
1588 pMat->NegOp( *pResMat);
1589 PushMatrix( pResMat );
1593 break;
1594 default:
1595 PushDouble( -GetDouble() );
1599 void ScInterpreter::ScPercentSign()
1601 nFuncFmtType = SvNumFormatType::PERCENT;
1602 const FormulaToken* pSaveCur = pCur;
1603 sal_uInt8 nSavePar = cPar;
1604 PushInt( 100 );
1605 cPar = 2;
1606 FormulaByteToken aDivOp( ocDiv, cPar );
1607 pCur = &aDivOp;
1608 ScDiv();
1609 pCur = pSaveCur;
1610 cPar = nSavePar;
1613 void ScInterpreter::ScNot()
1615 nFuncFmtType = SvNumFormatType::LOGICAL;
1616 switch ( GetStackType() )
1618 case svMatrix :
1620 ScMatrixRef pMat = GetMatrix();
1621 if ( !pMat )
1622 PushIllegalParameter();
1623 else
1625 SCSIZE nC, nR;
1626 pMat->GetDimensions( nC, nR );
1627 ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true);
1628 if ( !pResMat )
1629 PushIllegalArgument();
1630 else
1632 pMat->NotOp( *pResMat);
1633 PushMatrix( pResMat );
1637 break;
1638 default:
1639 PushInt( int(GetDouble() == 0.0) );
1643 void ScInterpreter::ScBitAnd()
1646 if ( !MustHaveParamCount( GetByte(), 2 ) )
1647 return;
1649 double num1 = ::rtl::math::approxFloor( GetDouble());
1650 double num2 = ::rtl::math::approxFloor( GetDouble());
1651 if ( (num1 >= n2power48) || (num1 < 0) ||
1652 (num2 >= n2power48) || (num2 < 0))
1653 PushIllegalArgument();
1654 else
1655 PushDouble (static_cast<sal_uInt64>(num1) & static_cast<sal_uInt64>(num2));
1658 void ScInterpreter::ScBitOr()
1661 if ( !MustHaveParamCount( GetByte(), 2 ) )
1662 return;
1664 double num1 = ::rtl::math::approxFloor( GetDouble());
1665 double num2 = ::rtl::math::approxFloor( GetDouble());
1666 if ( (num1 >= n2power48) || (num1 < 0) ||
1667 (num2 >= n2power48) || (num2 < 0))
1668 PushIllegalArgument();
1669 else
1670 PushDouble (static_cast<sal_uInt64>(num1) | static_cast<sal_uInt64>(num2));
1673 void ScInterpreter::ScBitXor()
1676 if ( !MustHaveParamCount( GetByte(), 2 ) )
1677 return;
1679 double num1 = ::rtl::math::approxFloor( GetDouble());
1680 double num2 = ::rtl::math::approxFloor( GetDouble());
1681 if ( (num1 >= n2power48) || (num1 < 0) ||
1682 (num2 >= n2power48) || (num2 < 0))
1683 PushIllegalArgument();
1684 else
1685 PushDouble (static_cast<sal_uInt64>(num1) ^ static_cast<sal_uInt64>(num2));
1688 void ScInterpreter::ScBitLshift()
1691 if ( !MustHaveParamCount( GetByte(), 2 ) )
1692 return;
1694 double fShift = ::rtl::math::approxFloor( GetDouble());
1695 double num = ::rtl::math::approxFloor( GetDouble());
1696 if ((num >= n2power48) || (num < 0))
1697 PushIllegalArgument();
1698 else
1700 double fRes;
1701 if (fShift < 0)
1702 fRes = ::rtl::math::approxFloor( num / pow( 2.0, -fShift));
1703 else if (fShift == 0)
1704 fRes = num;
1705 else
1706 fRes = num * pow( 2.0, fShift);
1707 PushDouble( fRes);
1711 void ScInterpreter::ScBitRshift()
1714 if ( !MustHaveParamCount( GetByte(), 2 ) )
1715 return;
1717 double fShift = ::rtl::math::approxFloor( GetDouble());
1718 double num = ::rtl::math::approxFloor( GetDouble());
1719 if ((num >= n2power48) || (num < 0))
1720 PushIllegalArgument();
1721 else
1723 double fRes;
1724 if (fShift < 0)
1725 fRes = num * pow( 2.0, -fShift);
1726 else if (fShift == 0)
1727 fRes = num;
1728 else
1729 fRes = ::rtl::math::approxFloor( num / pow( 2.0, fShift));
1730 PushDouble( fRes);
1734 void ScInterpreter::ScPi()
1736 PushDouble(M_PI);
1739 void ScInterpreter::ScRandomImpl( const std::function<double( double fFirst, double fLast )>& RandomFunc,
1740 double fFirst, double fLast )
1742 if (bMatrixFormula)
1744 SCCOL nCols = 0;
1745 SCROW nRows = 0;
1746 // In JumpMatrix context use its dimensions for the return matrix; the
1747 // formula cell range selected may differ, for example if the result is
1748 // to be transposed.
1749 if (GetStackType(1) == svJumpMatrix)
1751 SCSIZE nC, nR;
1752 pStack[sp-1]->GetJumpMatrix()->GetDimensions( nC, nR);
1753 nCols = std::max<SCCOL>(0, static_cast<SCCOL>(nC));
1754 nRows = std::max<SCROW>(0, static_cast<SCROW>(nR));
1756 else if (pMyFormulaCell)
1757 pMyFormulaCell->GetMatColsRows( nCols, nRows);
1759 if (nCols == 1 && nRows == 1)
1761 // For compatibility with existing
1762 // com.sun.star.sheet.FunctionAccess.callFunction() calls that per
1763 // default are executed in array context unless
1764 // FA.setPropertyValue("IsArrayFunction",False) was set, return a
1765 // scalar double instead of a 1x1 matrix object. tdf#128218
1766 PushDouble( RandomFunc( fFirst, fLast));
1767 return;
1770 // ScViewFunc::EnterMatrix() might be asking for
1771 // ScFormulaCell::GetResultDimensions(), which here are none so create
1772 // a 1x1 matrix at least which exactly is the case when EnterMatrix()
1773 // asks for a not selected range.
1774 if (nCols == 0)
1775 nCols = 1;
1776 if (nRows == 0)
1777 nRows = 1;
1778 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows), /*bEmpty*/true );
1779 if (!pResMat)
1780 PushError( FormulaError::MatrixSize);
1781 else
1783 for (SCCOL i=0; i < nCols; ++i)
1785 for (SCROW j=0; j < nRows; ++j)
1787 pResMat->PutDouble( RandomFunc( fFirst, fLast),
1788 static_cast<SCSIZE>(i), static_cast<SCSIZE>(j));
1791 PushMatrix( pResMat);
1794 else
1796 PushDouble( RandomFunc( fFirst, fLast));
1800 void ScInterpreter::ScRandom()
1802 auto RandomFunc = [this]( double, double )
1804 std::uniform_real_distribution<double> dist(0.0, 1.0);
1805 return dist(mrContext.aRNG);
1807 ScRandomImpl( RandomFunc, 0.0, 0.0 );
1810 void ScInterpreter::ScRandArray()
1812 sal_uInt8 nParamCount = GetByte();
1813 // optional 5th para:
1814 // TRUE for a whole number
1815 // FALSE for a decimal number - default.
1816 bool bWholeNumber = false;
1817 if (nParamCount == 5)
1818 bWholeNumber = GetBoolWithDefault(false);
1820 // optional 4th para: The maximum value of the random numbers
1821 double fMax = 1.0;
1822 if (nParamCount >= 4)
1823 fMax = GetDoubleWithDefault(1.0);
1825 // optional 3rd para: The minimum value of the random numbers
1826 double fMin = 0.0;
1827 if (nParamCount >= 3)
1828 fMin = GetDoubleWithDefault(0.0);
1830 // optional 2nd para: The number of columns of the return array
1831 SCCOL nCols = 1;
1832 if (nParamCount >= 2)
1833 nCols = static_cast<SCCOL>(GetInt32WithDefault(1));
1835 // optional 1st para: The number of rows of the return array
1836 SCROW nRows = 1;
1837 if (nParamCount >= 1)
1838 nRows = static_cast<SCROW>(GetInt32WithDefault(1));
1840 if (bWholeNumber)
1842 fMax = rtl::math::round(fMax, 0, rtl_math_RoundingMode_Up);
1843 fMin = rtl::math::round(fMin, 0, rtl_math_RoundingMode_Up);
1846 if (nGlobalError != FormulaError::NONE || fMin > fMax || nCols <= 0 || nRows <= 0)
1848 PushIllegalArgument();
1849 return;
1852 if (bWholeNumber)
1853 fMax = std::nextafter(fMax + 1, -DBL_MAX);
1854 else
1855 fMax = std::nextafter(fMax, DBL_MAX);
1857 auto RandomFunc = [this](double fFirst, double fLast, bool bWholeNum)
1859 std::uniform_real_distribution<double> dist(fFirst, fLast);
1860 if (bWholeNum)
1861 return floor(dist(mrContext.aRNG));
1862 else
1863 return dist(mrContext.aRNG);
1866 if (nCols == 1 && nRows == 1)
1868 PushDouble(RandomFunc(fMin, fMax, bWholeNumber));
1869 return;
1872 ScMatrixRef pResMat = GetNewMat(static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows), /*bEmpty*/true);
1873 if (!pResMat)
1874 PushError(FormulaError::MatrixSize);
1875 else
1877 for (SCCOL i = 0; i < nCols; ++i)
1879 for (SCROW j = 0; j < nRows; ++j)
1881 pResMat->PutDouble(RandomFunc(fMin, fMax, bWholeNumber),
1882 static_cast<SCSIZE>(i), static_cast<SCSIZE>(j));
1885 PushMatrix(pResMat);
1889 void ScInterpreter::ScRandbetween()
1891 if (!MustHaveParamCount( GetByte(), 2))
1892 return;
1894 // Same like scaddins/source/analysis/analysis.cxx
1895 // AnalysisAddIn::getRandbetween()
1896 double fMax = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
1897 double fMin = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
1898 if (nGlobalError != FormulaError::NONE || fMin > fMax)
1900 PushIllegalArgument();
1901 return;
1903 fMax = std::nextafter( fMax+1, -DBL_MAX);
1904 auto RandomFunc = [this]( double fFirst, double fLast )
1906 std::uniform_real_distribution<double> dist(fFirst, fLast);
1907 return floor(dist(mrContext.aRNG));
1909 ScRandomImpl( RandomFunc, fMin, fMax);
1912 void ScInterpreter::ScTrue()
1914 nFuncFmtType = SvNumFormatType::LOGICAL;
1915 PushInt(1);
1918 void ScInterpreter::ScFalse()
1920 nFuncFmtType = SvNumFormatType::LOGICAL;
1921 PushInt(0);
1924 void ScInterpreter::ScDeg()
1926 PushDouble(basegfx::rad2deg(GetDouble()));
1929 void ScInterpreter::ScRad()
1931 PushDouble(basegfx::deg2rad(GetDouble()));
1934 void ScInterpreter::ScSin()
1936 PushDouble(::rtl::math::sin(GetDouble()));
1939 void ScInterpreter::ScCos()
1941 PushDouble(::rtl::math::cos(GetDouble()));
1944 void ScInterpreter::ScTan()
1946 PushDouble(::rtl::math::tan(GetDouble()));
1949 void ScInterpreter::ScCot()
1951 PushDouble(1.0 / ::rtl::math::tan(GetDouble()));
1954 void ScInterpreter::ScArcSin()
1956 PushDouble(asin(GetDouble()));
1959 void ScInterpreter::ScArcCos()
1961 PushDouble(acos(GetDouble()));
1964 void ScInterpreter::ScArcTan()
1966 PushDouble(atan(GetDouble()));
1969 void ScInterpreter::ScArcCot()
1971 PushDouble((M_PI_2) - atan(GetDouble()));
1974 void ScInterpreter::ScSinHyp()
1976 PushDouble(sinh(GetDouble()));
1979 void ScInterpreter::ScCosHyp()
1981 PushDouble(cosh(GetDouble()));
1984 void ScInterpreter::ScTanHyp()
1986 PushDouble(tanh(GetDouble()));
1989 void ScInterpreter::ScCotHyp()
1991 PushDouble(1.0 / tanh(GetDouble()));
1994 void ScInterpreter::ScArcSinHyp()
1996 PushDouble( ::rtl::math::asinh( GetDouble()));
1999 void ScInterpreter::ScArcCosHyp()
2001 double fVal = GetDouble();
2002 if (fVal < 1.0)
2003 PushIllegalArgument();
2004 else
2005 PushDouble( ::rtl::math::acosh( fVal));
2008 void ScInterpreter::ScArcTanHyp()
2010 double fVal = GetDouble();
2011 if (fabs(fVal) >= 1.0)
2012 PushIllegalArgument();
2013 else
2014 PushDouble(::atanh(fVal));
2017 void ScInterpreter::ScArcCotHyp()
2019 double nVal = GetDouble();
2020 if (fabs(nVal) <= 1.0)
2021 PushIllegalArgument();
2022 else
2023 PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0)));
2026 void ScInterpreter::ScCosecant()
2028 PushDouble(1.0 / ::rtl::math::sin(GetDouble()));
2031 void ScInterpreter::ScSecant()
2033 PushDouble(1.0 / ::rtl::math::cos(GetDouble()));
2036 void ScInterpreter::ScCosecantHyp()
2038 PushDouble(1.0 / sinh(GetDouble()));
2041 void ScInterpreter::ScSecantHyp()
2043 PushDouble(1.0 / cosh(GetDouble()));
2046 void ScInterpreter::ScExp()
2048 PushDouble(exp(GetDouble()));
2051 void ScInterpreter::ScSqrt()
2053 double fVal = GetDouble();
2054 if (fVal >= 0.0)
2055 PushDouble(sqrt(fVal));
2056 else
2057 PushIllegalArgument();
2060 void ScInterpreter::ScIsEmpty()
2062 short nRes = 0;
2063 nFuncFmtType = SvNumFormatType::LOGICAL;
2064 switch ( GetRawStackType() )
2066 case svEmptyCell:
2068 FormulaConstTokenRef p = PopToken();
2069 if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited())
2070 nRes = 1;
2072 break;
2073 case svDoubleRef :
2074 case svSingleRef :
2076 ScAddress aAdr;
2077 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2078 break;
2079 // NOTE: this differs from COUNTBLANK() ScCountEmptyCells() that
2080 // may treat ="" in the referenced cell as blank for Excel
2081 // interoperability.
2082 ScRefCellValue aCell(mrDoc, aAdr);
2083 if (aCell.getType() == CELLTYPE_NONE)
2084 nRes = 1;
2086 break;
2087 case svExternalSingleRef:
2088 case svExternalDoubleRef:
2089 case svMatrix:
2091 ScMatrixRef pMat = GetMatrix();
2092 if ( !pMat )
2093 ; // nothing
2094 else if ( !pJumpMatrix )
2095 nRes = pMat->IsEmptyCell( 0, 0) ? 1 : 0;
2096 else
2098 SCSIZE nCols, nRows, nC, nR;
2099 pMat->GetDimensions( nCols, nRows);
2100 pJumpMatrix->GetPos( nC, nR);
2101 if ( nC < nCols && nR < nRows )
2102 nRes = pMat->IsEmptyCell( nC, nR) ? 1 : 0;
2103 // else: false, not empty (which is what Xcl does)
2106 break;
2107 default:
2108 Pop();
2110 nGlobalError = FormulaError::NONE;
2111 PushInt( nRes );
2114 bool ScInterpreter::IsString()
2116 nFuncFmtType = SvNumFormatType::LOGICAL;
2117 bool bRes = false;
2118 switch ( GetRawStackType() )
2120 case svString:
2121 Pop();
2122 bRes = true;
2123 break;
2124 case svDoubleRef :
2125 case svSingleRef :
2127 ScAddress aAdr;
2128 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2129 break;
2131 ScRefCellValue aCell(mrDoc, aAdr);
2132 if (GetCellErrCode(aCell) == FormulaError::NONE)
2134 switch (aCell.getType())
2136 case CELLTYPE_STRING :
2137 case CELLTYPE_EDIT :
2138 bRes = true;
2139 break;
2140 case CELLTYPE_FORMULA :
2141 bRes = (!aCell.getFormula()->IsValue() && !aCell.getFormula()->IsEmpty());
2142 break;
2143 default:
2144 ; // nothing
2148 break;
2149 case svExternalSingleRef:
2151 ScExternalRefCache::TokenRef pToken;
2152 PopExternalSingleRef(pToken);
2153 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svString)
2154 bRes = true;
2156 break;
2157 case svExternalDoubleRef:
2158 case svMatrix:
2160 ScMatrixRef pMat = GetMatrix();
2161 if ( !pMat )
2162 ; // nothing
2163 else if ( !pJumpMatrix )
2164 bRes = pMat->IsStringOrEmpty(0, 0) && !pMat->IsEmpty(0, 0);
2165 else
2167 SCSIZE nCols, nRows, nC, nR;
2168 pMat->GetDimensions( nCols, nRows);
2169 pJumpMatrix->GetPos( nC, nR);
2170 if ( nC < nCols && nR < nRows )
2171 bRes = pMat->IsStringOrEmpty( nC, nR) && !pMat->IsEmpty( nC, nR);
2174 break;
2175 default:
2176 Pop();
2178 nGlobalError = FormulaError::NONE;
2179 return bRes;
2182 void ScInterpreter::ScIsString()
2184 PushInt( int(IsString()) );
2187 void ScInterpreter::ScIsNonString()
2189 PushInt( int(!IsString()) );
2192 void ScInterpreter::ScIsLogical()
2194 bool bRes = false;
2195 switch ( GetStackType() )
2197 case svDoubleRef :
2198 case svSingleRef :
2200 ScAddress aAdr;
2201 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2202 break;
2204 ScRefCellValue aCell(mrDoc, aAdr);
2205 if (GetCellErrCode(aCell) == FormulaError::NONE)
2207 if (aCell.hasNumeric())
2209 sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
2210 bRes = (mrContext.NFGetType(nFormat) == SvNumFormatType::LOGICAL);
2214 break;
2215 case svMatrix:
2217 double fVal;
2218 svl::SharedString aStr;
2219 ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
2220 bRes = (nMatValType == ScMatValType::Boolean);
2222 break;
2223 default:
2224 PopError();
2225 if ( nGlobalError == FormulaError::NONE )
2226 bRes = ( nCurFmtType == SvNumFormatType::LOGICAL );
2228 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
2229 nGlobalError = FormulaError::NONE;
2230 PushInt( int(bRes) );
2233 void ScInterpreter::ScType()
2235 short nType = 0;
2236 switch ( GetStackType() )
2238 case svDoubleRef :
2239 case svSingleRef :
2241 ScAddress aAdr;
2242 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2243 break;
2245 ScRefCellValue aCell(mrDoc, aAdr);
2246 if (GetCellErrCode(aCell) == FormulaError::NONE)
2248 switch (aCell.getType())
2250 // NOTE: this is Xcl nonsense!
2251 case CELLTYPE_STRING :
2252 case CELLTYPE_EDIT :
2253 nType = 2;
2254 break;
2255 case CELLTYPE_VALUE :
2257 sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
2258 if (mrContext.NFGetType(nFormat) == SvNumFormatType::LOGICAL)
2259 nType = 4;
2260 else
2261 nType = 1;
2263 break;
2264 case CELLTYPE_NONE:
2265 // always 1, s. tdf#73078
2266 nType = 1;
2267 break;
2268 case CELLTYPE_FORMULA :
2269 nType = 8;
2270 break;
2271 default:
2272 PushIllegalArgument();
2275 else
2276 nType = 16;
2278 break;
2279 case svString:
2280 PopError();
2281 if ( nGlobalError != FormulaError::NONE )
2283 nType = 16;
2284 nGlobalError = FormulaError::NONE;
2286 else
2287 nType = 2;
2288 break;
2289 case svMatrix:
2290 PopMatrix();
2291 if ( nGlobalError != FormulaError::NONE )
2293 nType = 16;
2294 nGlobalError = FormulaError::NONE;
2296 else
2297 nType = 64;
2298 // we could return the type of one element if in JumpMatrix or
2299 // ForceArray mode, but Xcl doesn't ...
2300 break;
2301 default:
2302 PopError();
2303 if ( nGlobalError != FormulaError::NONE )
2305 nType = 16;
2306 nGlobalError = FormulaError::NONE;
2308 else
2309 nType = 1;
2311 PushInt( nType );
2314 static bool lcl_FormatHasNegColor( const SvNumberformat* pFormat )
2316 return pFormat && pFormat->GetColor( 1 );
2319 static bool lcl_FormatHasOpenPar( const SvNumberformat* pFormat )
2321 return pFormat && (pFormat->GetFormatstring().indexOf('(') != -1);
2324 namespace {
2326 void getFormatString(const ScInterpreterContext& rContext, sal_uLong nFormat, OUString& rFmtStr)
2328 rFmtStr = rContext.NFGetCalcCellReturn(nFormat);
2333 void ScInterpreter::ScCell()
2334 { // ATTRIBUTE ; [REF]
2335 sal_uInt8 nParamCount = GetByte();
2336 if( !MustHaveParamCount( nParamCount, 1, 2 ) )
2337 return;
2339 ScAddress aCellPos( aPos );
2340 if( nParamCount == 2 )
2342 switch (GetStackType())
2344 case svExternalSingleRef:
2345 case svExternalDoubleRef:
2347 // Let's handle external reference separately...
2348 ScCellExternal();
2349 return;
2351 case svDoubleRef:
2353 // Exceptionally not an intersecting position but top left.
2354 // See ODF v1.3 part 4 OpenFormula 6.13.3 CELL
2355 ScRange aRange;
2356 PopDoubleRef( aRange);
2357 aCellPos = aRange.aStart;
2359 break;
2360 case svSingleRef:
2361 PopSingleRef( aCellPos);
2362 break;
2363 default:
2364 PopError();
2365 SetError( FormulaError::NoRef);
2368 OUString aInfoType = GetString().getString();
2369 if (nGlobalError != FormulaError::NONE)
2370 PushIllegalParameter();
2371 else
2373 ScRefCellValue aCell(mrDoc, aCellPos);
2375 ScCellKeywordTranslator::transKeyword(aInfoType, &ScGlobal::GetLocale(), ocCell);
2377 // *** ADDRESS INFO ***
2378 if( aInfoType == "COL" )
2379 { // column number (1-based)
2380 PushInt( aCellPos.Col() + 1 );
2382 else if( aInfoType == "ROW" )
2383 { // row number (1-based)
2384 PushInt( aCellPos.Row() + 1 );
2386 else if( aInfoType == "SHEET" )
2387 { // table number (1-based)
2388 PushInt( aCellPos.Tab() + 1 );
2390 else if( aInfoType == "ADDRESS" )
2391 { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW
2393 // Follow the configurable string reference address syntax as also
2394 // used by INDIRECT() (and ADDRESS() for the sheet separator).
2395 FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
2396 switch (eConv)
2398 default:
2399 // Use the current address syntax if unspecified or says
2400 // one or the other or one we don't explicitly handle.
2401 eConv = mrDoc.GetAddressConvention();
2402 break;
2403 case FormulaGrammar::CONV_OOO:
2404 case FormulaGrammar::CONV_XL_A1:
2405 case FormulaGrammar::CONV_XL_R1C1:
2406 // Use that.
2407 break;
2410 ScRefFlags nFlags = (aCellPos.Tab() == aPos.Tab()) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D;
2411 OUString aStr(aCellPos.Format(nFlags, &mrDoc, eConv));
2412 PushString(aStr);
2414 else if( aInfoType == "FILENAME" )
2416 SCTAB nTab = aCellPos.Tab();
2417 OUString aFuncResult;
2418 if( nTab < mrDoc.GetTableCount() )
2420 if( mrDoc.GetLinkMode( nTab ) == ScLinkMode::VALUE )
2421 mrDoc.GetName( nTab, aFuncResult );
2422 else
2424 ScDocShell* pShell = mrDoc.GetDocumentShell();
2425 if( pShell && pShell->GetMedium() )
2427 const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject();
2428 OUString aTabName;
2429 mrDoc.GetName( nTab, aTabName );
2431 FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
2432 if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
2433 eConv = mrDoc.GetAddressConvention();
2435 if (eConv == FormulaGrammar::CONV_XL_A1 ||
2436 eConv == FormulaGrammar::CONV_XL_R1C1 ||
2437 eConv == FormulaGrammar::CONV_XL_OOX)
2439 // file name and table name: FILEPATH/[FILENAME]TABLE
2440 if (!comphelper::LibreOfficeKit::isActive())
2441 aFuncResult = rURLObj.GetPartBeforeLastName();
2442 aFuncResult += "[" + rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous) +
2443 "]" + aTabName;
2445 else
2447 // file name and table name: 'FILEPATH/FILENAME'#$TABLE
2448 aFuncResult = "'";
2449 if (!comphelper::LibreOfficeKit::isActive())
2450 aFuncResult += rURLObj.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous);
2451 else
2452 aFuncResult += rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous);
2453 aFuncResult += "'#$" + aTabName;
2458 PushString( aFuncResult );
2460 else if( aInfoType == "COORD" )
2461 { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW
2462 // Yes, passing tab as col is intentional!
2463 OUString aCellStr1 =
2464 ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format(
2465 (ScRefFlags::COL_ABS|ScRefFlags::COL_VALID), nullptr, mrDoc.GetAddressConvention() );
2466 OUString aCellStr2 =
2467 aCellPos.Format((ScRefFlags::COL_ABS|ScRefFlags::COL_VALID|ScRefFlags::ROW_ABS|ScRefFlags::ROW_VALID),
2468 nullptr, mrDoc.GetAddressConvention());
2469 OUString aFuncResult = aCellStr1 + ":" + aCellStr2;
2470 PushString( aFuncResult );
2473 // *** CELL PROPERTIES ***
2474 else if( aInfoType == "CONTENTS" )
2475 { // contents of the cell, no formatting
2476 if (aCell.hasString())
2478 svl::SharedString aStr;
2479 GetCellString(aStr, aCell);
2480 PushString( aStr );
2482 else
2483 PushDouble(GetCellValue(aCellPos, aCell));
2485 else if( aInfoType == "TYPE" )
2486 { // b = blank; l = string (label); v = otherwise (value)
2487 sal_Unicode c;
2488 if (aCell.hasString())
2489 c = 'l';
2490 else
2491 c = aCell.hasNumeric() ? 'v' : 'b';
2492 PushString( OUString(c) );
2494 else if( aInfoType == "WIDTH" )
2495 { // column width (rounded off as count of zero characters in standard font and size)
2496 Printer* pPrinter = mrDoc.GetPrinter();
2497 MapMode aOldMode( pPrinter->GetMapMode() );
2498 vcl::Font aOldFont( pPrinter->GetFont() );
2499 vcl::Font aDefFont;
2501 pPrinter->SetMapMode(MapMode(MapUnit::MapTwip));
2502 // font color doesn't matter here
2503 mrDoc.getCellAttributeHelper().getDefaultCellAttribute().fillFontOnly(aDefFont, pPrinter);
2504 pPrinter->SetFont(aDefFont);
2505 tools::Long nZeroWidth = pPrinter->GetTextWidth( OUString( '0' ) );
2506 assert(nZeroWidth != 0);
2507 pPrinter->SetFont( aOldFont );
2508 pPrinter->SetMapMode( aOldMode );
2509 int nZeroCount = static_cast<int>(mrDoc.GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth);
2510 PushInt( nZeroCount );
2512 else if( aInfoType == "PREFIX" )
2513 { // ' = left; " = right; ^ = centered
2514 sal_Unicode c = 0;
2515 if (aCell.hasString())
2517 const SvxHorJustifyItem* pJustAttr = mrDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY );
2518 switch( pJustAttr->GetValue() )
2520 case SvxCellHorJustify::Standard:
2521 case SvxCellHorJustify::Left:
2522 case SvxCellHorJustify::Block: c = '\''; break;
2523 case SvxCellHorJustify::Center: c = '^'; break;
2524 case SvxCellHorJustify::Right: c = '"'; break;
2525 case SvxCellHorJustify::Repeat: c = '\\'; break;
2528 PushString( OUString(c) );
2530 else if( aInfoType == "PROTECT" )
2531 { // 1 = cell locked
2532 const ScProtectionAttr* pProtAttr = mrDoc.GetAttr( aCellPos, ATTR_PROTECTION );
2533 PushInt( pProtAttr->GetProtection() ? 1 : 0 );
2536 // *** FORMATTING ***
2537 else if( aInfoType == "FORMAT" )
2538 { // specific format code for standard formats
2539 OUString aFuncResult;
2540 sal_uInt32 nFormat = mrDoc.GetNumberFormat( aCellPos );
2541 getFormatString(mrContext, nFormat, aFuncResult);
2542 PushString( aFuncResult );
2544 else if( aInfoType == "COLOR" )
2545 { // 1 = negative values are colored, otherwise 0
2546 const SvNumberformat* pFormat = mrContext.NFGetFormatEntry( mrDoc.GetNumberFormat( aCellPos ) );
2547 PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 );
2549 else if( aInfoType == "PARENTHESES" )
2550 { // 1 = format string contains a '(' character, otherwise 0
2551 const SvNumberformat* pFormat = mrContext.NFGetFormatEntry( mrDoc.GetNumberFormat( aCellPos ) );
2552 PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 );
2554 else
2555 PushIllegalArgument();
2559 void ScInterpreter::ScCellExternal()
2561 sal_uInt16 nFileId;
2562 OUString aTabName;
2563 ScSingleRefData aRef;
2564 ScExternalRefCache::TokenRef pToken;
2565 ScExternalRefCache::CellFormat aFmt;
2566 PopExternalSingleRef(nFileId, aTabName, aRef, pToken, &aFmt);
2567 if (nGlobalError != FormulaError::NONE)
2569 PushError( nGlobalError);
2570 return;
2573 OUString aInfoType = GetString().getString();
2574 if (nGlobalError != FormulaError::NONE)
2576 PushError( nGlobalError);
2577 return;
2580 SCCOL nCol;
2581 SCROW nRow;
2582 SCTAB nTab;
2583 aRef.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like.
2584 SingleRefToVars(aRef, nCol, nRow, nTab);
2585 if (nGlobalError != FormulaError::NONE)
2587 PushIllegalParameter();
2588 return;
2590 aRef.SetAbsTab(-1); // revert the value.
2592 ScCellKeywordTranslator::transKeyword(aInfoType, &ScGlobal::GetLocale(), ocCell);
2593 ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
2595 if ( aInfoType == "COL" )
2596 PushInt(nCol + 1);
2597 else if ( aInfoType == "ROW" )
2598 PushInt(nRow + 1);
2599 else if ( aInfoType == "SHEET" )
2601 // For SHEET, No idea what number we should set, but let's always set
2602 // 1 if the external sheet exists, no matter what sheet. Excel does
2603 // the same.
2604 if (pRefMgr->getCacheTable(nFileId, aTabName, false))
2605 PushInt(1);
2606 else
2607 SetError(FormulaError::NoName);
2609 else if ( aInfoType == "ADDRESS" )
2611 // ODF 1.2 says we need to always display address using the ODF A1 grammar.
2612 ScTokenArray aArray(mrDoc);
2613 aArray.AddExternalSingleReference(nFileId, svl::SharedString( aTabName), aRef); // string not interned
2614 ScCompiler aComp(mrDoc, aPos, aArray, formula::FormulaGrammar::GRAM_ODFF_A1);
2615 OUString aStr;
2616 aComp.CreateStringFromTokenArray(aStr);
2617 PushString(aStr);
2619 else if ( aInfoType == "FILENAME" )
2621 const OUString* p = pRefMgr->getExternalFileName(nFileId);
2622 if (!p)
2624 // In theory this should never happen...
2625 SetError(FormulaError::NoName);
2626 return;
2629 OUString aBuf;
2630 FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
2631 if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
2632 eConv = mrDoc.GetAddressConvention();
2634 if (eConv == FormulaGrammar::CONV_XL_A1 ||
2635 eConv == FormulaGrammar::CONV_XL_R1C1 ||
2636 eConv == FormulaGrammar::CONV_XL_OOX)
2638 // 'file URI/[FileName]SheetName
2639 sal_Int32 nPos = p->lastIndexOf('/');
2640 aBuf = OUString::Concat(p->subView(0, nPos + 1))
2641 + "[" + p->subView(nPos + 1) + "]"
2642 + aTabName;
2644 else
2646 // 'file URI'#$SheetName
2647 aBuf = "'" + *p + "'#$" + aTabName;
2650 PushString(aBuf);
2652 else if ( aInfoType == "CONTENTS" )
2654 switch (pToken->GetType())
2656 case svString:
2657 PushString(pToken->GetString());
2658 break;
2659 case svDouble:
2660 PushString(OUString::number(pToken->GetDouble()));
2661 break;
2662 case svError:
2663 PushString(ScGlobal::GetErrorString(pToken->GetError()));
2664 break;
2665 default:
2666 PushString(OUString());
2669 else if ( aInfoType == "TYPE" )
2671 sal_Unicode c = 'v';
2672 switch (pToken->GetType())
2674 case svString:
2675 c = 'l';
2676 break;
2677 case svEmptyCell:
2678 c = 'b';
2679 break;
2680 default:
2683 PushString(OUString(c));
2685 else if ( aInfoType == "FORMAT" )
2687 OUString aFmtStr;
2688 sal_uLong nFmt = aFmt.mbIsSet ? aFmt.mnIndex : 0;
2689 getFormatString(mrContext, nFmt, aFmtStr);
2690 PushString(aFmtStr);
2692 else if ( aInfoType == "COLOR" )
2694 // 1 = negative values are colored, otherwise 0
2695 int nVal = 0;
2696 if (aFmt.mbIsSet)
2698 const SvNumberformat* pFormat = mrContext.NFGetFormatEntry(aFmt.mnIndex);
2699 nVal = lcl_FormatHasNegColor(pFormat) ? 1 : 0;
2701 PushInt(nVal);
2703 else if ( aInfoType == "PARENTHESES" )
2705 // 1 = format string contains a '(' character, otherwise 0
2706 int nVal = 0;
2707 if (aFmt.mbIsSet)
2709 const SvNumberformat* pFormat = mrContext.NFGetFormatEntry(aFmt.mnIndex);
2710 nVal = lcl_FormatHasOpenPar(pFormat) ? 1 : 0;
2712 PushInt(nVal);
2714 else
2715 PushIllegalParameter();
2718 void ScInterpreter::ScIsRef()
2720 nFuncFmtType = SvNumFormatType::LOGICAL;
2721 bool bRes = false;
2722 switch ( GetStackType() )
2724 case svSingleRef :
2726 ScAddress aAdr;
2727 PopSingleRef( aAdr );
2728 if ( nGlobalError == FormulaError::NONE )
2729 bRes = true;
2731 break;
2732 case svDoubleRef :
2734 ScRange aRange;
2735 PopDoubleRef( aRange );
2736 if ( nGlobalError == FormulaError::NONE )
2737 bRes = true;
2739 break;
2740 case svRefList :
2742 FormulaConstTokenRef x = PopToken();
2743 if ( nGlobalError == FormulaError::NONE )
2744 bRes = !x->GetRefList()->empty();
2746 break;
2747 case svExternalSingleRef:
2749 ScExternalRefCache::TokenRef pToken;
2750 PopExternalSingleRef(pToken);
2751 if (nGlobalError == FormulaError::NONE)
2752 bRes = true;
2754 break;
2755 case svExternalDoubleRef:
2757 ScExternalRefCache::TokenArrayRef pArray;
2758 PopExternalDoubleRef(pArray);
2759 if (nGlobalError == FormulaError::NONE)
2760 bRes = true;
2762 break;
2763 default:
2764 Pop();
2766 nGlobalError = FormulaError::NONE;
2767 PushInt( int(bRes) );
2770 void ScInterpreter::ScIsValue()
2772 nFuncFmtType = SvNumFormatType::LOGICAL;
2773 bool bRes = false;
2774 switch ( GetRawStackType() )
2776 case svDouble:
2777 Pop();
2778 bRes = true;
2779 break;
2780 case svDoubleRef :
2781 case svSingleRef :
2783 ScAddress aAdr;
2784 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2785 break;
2787 ScRefCellValue aCell(mrDoc, aAdr);
2788 if (GetCellErrCode(aCell) == FormulaError::NONE)
2790 switch (aCell.getType())
2792 case CELLTYPE_VALUE :
2793 bRes = true;
2794 break;
2795 case CELLTYPE_FORMULA :
2796 bRes = (aCell.getFormula()->IsValue() && !aCell.getFormula()->IsEmpty());
2797 break;
2798 default:
2799 ; // nothing
2803 break;
2804 case svExternalSingleRef:
2806 ScExternalRefCache::TokenRef pToken;
2807 PopExternalSingleRef(pToken);
2808 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
2809 bRes = true;
2811 break;
2812 case svExternalDoubleRef:
2813 case svMatrix:
2815 ScMatrixRef pMat = GetMatrix();
2816 if ( !pMat )
2817 ; // nothing
2818 else if ( !pJumpMatrix )
2820 if (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NONE)
2821 bRes = pMat->IsValue( 0, 0);
2823 else
2825 SCSIZE nCols, nRows, nC, nR;
2826 pMat->GetDimensions( nCols, nRows);
2827 pJumpMatrix->GetPos( nC, nR);
2828 if ( nC < nCols && nR < nRows )
2829 if (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NONE)
2830 bRes = pMat->IsValue( nC, nR);
2833 break;
2834 default:
2835 Pop();
2837 nGlobalError = FormulaError::NONE;
2838 PushInt( int(bRes) );
2841 void ScInterpreter::ScIsFormula()
2843 nFuncFmtType = SvNumFormatType::LOGICAL;
2844 bool bRes = false;
2845 switch ( GetStackType() )
2847 case svDoubleRef :
2848 if (IsInArrayContext())
2850 SCCOL nCol1, nCol2;
2851 SCROW nRow1, nRow2;
2852 SCTAB nTab1, nTab2;
2853 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2854 if (nGlobalError != FormulaError::NONE)
2856 PushError( nGlobalError);
2857 return;
2859 if (nTab1 != nTab2)
2861 PushIllegalArgument();
2862 return;
2865 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCol2 - nCol1 + 1),
2866 static_cast<SCSIZE>(nRow2 - nRow1 + 1), true);
2867 if (!pResMat)
2869 PushError( FormulaError::MatrixSize);
2870 return;
2873 /* TODO: we really should have a gap-aware cell iterator. */
2874 SCSIZE i=0, j=0;
2875 ScAddress aAdr( 0, 0, nTab1);
2876 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2878 aAdr.SetCol(nCol);
2879 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2881 aAdr.SetRow(nRow);
2882 ScRefCellValue aCell(mrDoc, aAdr);
2883 pResMat->PutBoolean( (aCell.getType() == CELLTYPE_FORMULA), i,j);
2884 ++j;
2886 ++i;
2887 j = 0;
2890 PushMatrix( pResMat);
2891 return;
2893 [[fallthrough]];
2894 case svSingleRef :
2896 ScAddress aAdr;
2897 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2898 break;
2900 bRes = (mrDoc.GetCellType(aAdr) == CELLTYPE_FORMULA);
2902 break;
2903 default:
2904 Pop();
2906 nGlobalError = FormulaError::NONE;
2907 PushInt( int(bRes) );
2910 void ScInterpreter::ScFormula()
2912 OUString aFormula;
2913 switch ( GetStackType() )
2915 case svDoubleRef :
2916 if (IsInArrayContext())
2918 SCCOL nCol1, nCol2;
2919 SCROW nRow1, nRow2;
2920 SCTAB nTab1, nTab2;
2921 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2922 if (nGlobalError != FormulaError::NONE)
2923 break;
2925 if (nTab1 != nTab2)
2927 SetError( FormulaError::IllegalArgument);
2928 break;
2931 ScMatrixRef pResMat = GetNewMat( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1, true);
2932 if (!pResMat)
2933 break;
2935 /* TODO: use a column iterator instead? */
2936 SCSIZE i=0, j=0;
2937 ScAddress aAdr(0,0,nTab1);
2938 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2940 aAdr.SetCol(nCol);
2941 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2943 aAdr.SetRow(nRow);
2944 ScRefCellValue aCell(mrDoc, aAdr);
2945 switch (aCell.getType())
2947 case CELLTYPE_FORMULA :
2948 aFormula = aCell.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext);
2949 pResMat->PutString( mrStrPool.intern( aFormula), i,j);
2950 break;
2951 default:
2952 pResMat->PutError( FormulaError::NotAvailable, i,j);
2954 ++j;
2956 ++i;
2957 j = 0;
2960 PushMatrix( pResMat);
2961 return;
2963 [[fallthrough]];
2964 case svSingleRef :
2966 ScAddress aAdr;
2967 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2968 break;
2970 ScRefCellValue aCell(mrDoc, aAdr);
2971 switch (aCell.getType())
2973 case CELLTYPE_FORMULA :
2974 aFormula = aCell.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext);
2975 break;
2976 default:
2977 SetError( FormulaError::NotAvailable );
2980 break;
2981 default:
2982 PopError();
2983 SetError( FormulaError::NotAvailable );
2985 PushString( aFormula );
2988 void ScInterpreter::ScIsNV()
2990 nFuncFmtType = SvNumFormatType::LOGICAL;
2991 bool bRes = false;
2992 switch ( GetStackType() )
2994 case svDoubleRef :
2995 case svSingleRef :
2997 ScAddress aAdr;
2998 bool bOk = PopDoubleRefOrSingleRef( aAdr );
2999 if ( nGlobalError == FormulaError::NotAvailable )
3000 bRes = true;
3001 else if (bOk)
3003 ScRefCellValue aCell(mrDoc, aAdr);
3004 FormulaError nErr = GetCellErrCode(aCell);
3005 bRes = (nErr == FormulaError::NotAvailable);
3008 break;
3009 case svExternalSingleRef:
3011 ScExternalRefCache::TokenRef pToken;
3012 PopExternalSingleRef(pToken);
3013 if (nGlobalError == FormulaError::NotAvailable ||
3014 (pToken && pToken->GetType() == svError && pToken->GetError() == FormulaError::NotAvailable))
3015 bRes = true;
3017 break;
3018 case svExternalDoubleRef:
3019 case svMatrix:
3021 ScMatrixRef pMat = GetMatrix();
3022 if ( !pMat )
3023 ; // nothing
3024 else if ( !pJumpMatrix )
3025 bRes = (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NotAvailable);
3026 else
3028 SCSIZE nCols, nRows, nC, nR;
3029 pMat->GetDimensions( nCols, nRows);
3030 pJumpMatrix->GetPos( nC, nR);
3031 if ( nC < nCols && nR < nRows )
3032 bRes = (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NotAvailable);
3035 break;
3036 default:
3037 PopError();
3038 if ( nGlobalError == FormulaError::NotAvailable )
3039 bRes = true;
3041 nGlobalError = FormulaError::NONE;
3042 PushInt( int(bRes) );
3045 void ScInterpreter::ScIsErr()
3047 nFuncFmtType = SvNumFormatType::LOGICAL;
3048 bool bRes = false;
3049 switch ( GetStackType() )
3051 case svDoubleRef :
3052 case svSingleRef :
3054 ScAddress aAdr;
3055 bool bOk = PopDoubleRefOrSingleRef( aAdr );
3056 if ( !bOk || (nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) )
3057 bRes = true;
3058 else
3060 ScRefCellValue aCell(mrDoc, aAdr);
3061 FormulaError nErr = GetCellErrCode(aCell);
3062 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
3065 break;
3066 case svExternalSingleRef:
3068 ScExternalRefCache::TokenRef pToken;
3069 PopExternalSingleRef(pToken);
3070 if ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pToken ||
3071 (pToken->GetType() == svError && pToken->GetError() != FormulaError::NotAvailable))
3072 bRes = true;
3074 break;
3075 case svExternalDoubleRef:
3076 case svMatrix:
3078 ScMatrixRef pMat = GetMatrix();
3079 if ( nGlobalError != FormulaError::NONE || !pMat )
3080 bRes = ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pMat);
3081 else if ( !pJumpMatrix )
3083 FormulaError nErr = pMat->GetErrorIfNotString( 0, 0);
3084 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
3086 else
3088 SCSIZE nCols, nRows, nC, nR;
3089 pMat->GetDimensions( nCols, nRows);
3090 pJumpMatrix->GetPos( nC, nR);
3091 if ( nC < nCols && nR < nRows )
3093 FormulaError nErr = pMat->GetErrorIfNotString( nC, nR);
3094 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
3098 break;
3099 default:
3100 PopError();
3101 if ( nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable )
3102 bRes = true;
3104 nGlobalError = FormulaError::NONE;
3105 PushInt( int(bRes) );
3108 void ScInterpreter::ScIsError()
3110 nFuncFmtType = SvNumFormatType::LOGICAL;
3111 bool bRes = false;
3112 switch ( GetStackType() )
3114 case svDoubleRef :
3115 case svSingleRef :
3117 ScAddress aAdr;
3118 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3120 bRes = true;
3121 break;
3123 if ( nGlobalError != FormulaError::NONE )
3124 bRes = true;
3125 else
3127 ScRefCellValue aCell(mrDoc, aAdr);
3128 bRes = (GetCellErrCode(aCell) != FormulaError::NONE);
3131 break;
3132 case svExternalSingleRef:
3134 ScExternalRefCache::TokenRef pToken;
3135 PopExternalSingleRef(pToken);
3136 if (nGlobalError != FormulaError::NONE || pToken->GetType() == svError)
3137 bRes = true;
3139 break;
3140 case svExternalDoubleRef:
3141 case svMatrix:
3143 ScMatrixRef pMat = GetMatrix();
3144 if ( nGlobalError != FormulaError::NONE || !pMat )
3145 bRes = true;
3146 else if ( !pJumpMatrix )
3147 bRes = (pMat->GetErrorIfNotString( 0, 0) != FormulaError::NONE);
3148 else
3150 SCSIZE nCols, nRows, nC, nR;
3151 pMat->GetDimensions( nCols, nRows);
3152 pJumpMatrix->GetPos( nC, nR);
3153 if ( nC < nCols && nR < nRows )
3154 bRes = (pMat->GetErrorIfNotString( nC, nR) != FormulaError::NONE);
3157 break;
3158 default:
3159 PopError();
3160 if ( nGlobalError != FormulaError::NONE )
3161 bRes = true;
3163 nGlobalError = FormulaError::NONE;
3164 PushInt( int(bRes) );
3167 bool ScInterpreter::IsEven()
3169 nFuncFmtType = SvNumFormatType::LOGICAL;
3170 bool bRes = false;
3171 double fVal = 0.0;
3172 switch ( GetStackType() )
3174 case svDoubleRef :
3175 case svSingleRef :
3177 ScAddress aAdr;
3178 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3179 break;
3181 ScRefCellValue aCell(mrDoc, aAdr);
3182 FormulaError nErr = GetCellErrCode(aCell);
3183 if (nErr != FormulaError::NONE)
3184 SetError(nErr);
3185 else
3187 switch (aCell.getType())
3189 case CELLTYPE_VALUE :
3190 fVal = GetCellValue(aAdr, aCell);
3191 bRes = true;
3192 break;
3193 case CELLTYPE_FORMULA :
3194 if (aCell.getFormula()->IsValue())
3196 fVal = GetCellValue(aAdr, aCell);
3197 bRes = true;
3199 break;
3200 default:
3201 ; // nothing
3205 break;
3206 case svDouble:
3208 fVal = PopDouble();
3209 bRes = true;
3211 break;
3212 case svExternalSingleRef:
3214 ScExternalRefCache::TokenRef pToken;
3215 PopExternalSingleRef(pToken);
3216 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
3218 fVal = pToken->GetDouble();
3219 bRes = true;
3222 break;
3223 case svExternalDoubleRef:
3224 case svMatrix:
3226 ScMatrixRef pMat = GetMatrix();
3227 if ( !pMat )
3228 ; // nothing
3229 else if ( !pJumpMatrix )
3231 bRes = pMat->IsValue( 0, 0);
3232 if ( bRes )
3233 fVal = pMat->GetDouble( 0, 0);
3235 else
3237 SCSIZE nCols, nRows, nC, nR;
3238 pMat->GetDimensions( nCols, nRows);
3239 pJumpMatrix->GetPos( nC, nR);
3240 if ( nC < nCols && nR < nRows )
3242 bRes = pMat->IsValue( nC, nR);
3243 if ( bRes )
3244 fVal = pMat->GetDouble( nC, nR);
3246 else
3247 SetError( FormulaError::NoValue);
3250 break;
3251 default:
3252 ; // nothing
3254 if ( !bRes )
3255 SetError( FormulaError::IllegalParameter);
3256 else
3257 bRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 );
3258 return bRes;
3261 void ScInterpreter::ScIsEven()
3263 PushInt( int(IsEven()) );
3266 void ScInterpreter::ScIsOdd()
3268 PushInt( int(!IsEven()) );
3271 void ScInterpreter::ScN()
3273 FormulaError nErr = nGlobalError;
3274 nGlobalError = FormulaError::NONE;
3275 // Temporarily override the ConvertStringToValue() error for
3276 // GetCellValue() / GetCellValueOrZero()
3277 FormulaError nSErr = mnStringNoValueError;
3278 mnStringNoValueError = FormulaError::CellNoValue;
3279 double fVal = GetDouble();
3280 mnStringNoValueError = nSErr;
3281 if (nErr != FormulaError::NONE)
3282 nGlobalError = nErr; // preserve previous error if any
3283 else if (nGlobalError == FormulaError::CellNoValue)
3284 nGlobalError = FormulaError::NONE; // reset temporary detection error
3285 PushDouble(fVal);
3288 void ScInterpreter::ScTrim()
3290 // Doesn't only trim but also removes duplicated blanks within!
3291 OUString aVal = comphelper::string::strip(GetString().getString(), ' ');
3292 OUStringBuffer aStr;
3293 const sal_Unicode* p = aVal.getStr();
3294 const sal_Unicode* const pEnd = p + aVal.getLength();
3295 while ( p < pEnd )
3297 if ( *p != ' ' || p[-1] != ' ' ) // first can't be ' ', so -1 is fine
3298 aStr.append(*p);
3299 p++;
3301 PushString(aStr.makeStringAndClear());
3304 void ScInterpreter::ScUpper()
3306 OUString aString = ScGlobal::getCharClass().uppercase(GetString().getString());
3307 PushString(aString);
3310 void ScInterpreter::ScProper()
3312 //2do: what to do with I18N-CJK ?!?
3313 OUStringBuffer aStr(GetString().getString());
3314 const sal_Int32 nLen = aStr.getLength();
3315 if ( nLen > 0 )
3317 OUString aUpr(ScGlobal::getCharClass().uppercase(aStr.toString()));
3318 OUString aLwr(ScGlobal::getCharClass().lowercase(aStr.toString()));
3319 aStr[0] = aUpr[0];
3320 sal_Int32 nPos = 1;
3321 while( nPos < nLen )
3323 OUString aTmpStr( aStr[nPos-1] );
3324 if ( !ScGlobal::getCharClass().isLetter( aTmpStr, 0 ) )
3325 aStr[nPos] = aUpr[nPos];
3326 else
3327 aStr[nPos] = aLwr[nPos];
3328 ++nPos;
3331 PushString(aStr.makeStringAndClear());
3334 void ScInterpreter::ScLower()
3336 OUString aString = ScGlobal::getCharClass().lowercase(GetString().getString());
3337 PushString(aString);
3340 void ScInterpreter::ScLen()
3342 OUString aStr = GetString().getString();
3343 sal_Int32 nIdx = 0;
3344 sal_Int32 nCnt = 0;
3345 while ( nIdx < aStr.getLength() )
3347 aStr.iterateCodePoints( &nIdx );
3348 ++nCnt;
3350 PushDouble( nCnt );
3353 void ScInterpreter::ScT()
3355 switch ( GetStackType() )
3357 case svDoubleRef :
3358 case svSingleRef :
3360 ScAddress aAdr;
3361 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3363 PushInt(0);
3364 return ;
3366 bool bValue = false;
3367 ScRefCellValue aCell(mrDoc, aAdr);
3368 if (GetCellErrCode(aCell) == FormulaError::NONE)
3370 switch (aCell.getType())
3372 case CELLTYPE_VALUE :
3373 bValue = true;
3374 break;
3375 case CELLTYPE_FORMULA :
3376 bValue = aCell.getFormula()->IsValue();
3377 break;
3378 default:
3379 ; // nothing
3382 if ( bValue )
3383 PushString(OUString());
3384 else
3386 // like GetString()
3387 svl::SharedString aStr;
3388 GetCellString(aStr, aCell);
3389 PushString(aStr);
3392 break;
3393 case svMatrix:
3394 case svExternalSingleRef:
3395 case svExternalDoubleRef:
3397 double fVal;
3398 svl::SharedString aStr;
3399 ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
3400 if (ScMatrix::IsValueType( nMatValType))
3401 PushString(svl::SharedString::getEmptyString());
3402 else
3403 PushString( aStr);
3405 break;
3406 case svDouble :
3408 PopError();
3409 PushString( OUString() );
3411 break;
3412 case svString :
3413 ; // leave on stack
3414 break;
3415 default :
3416 PushError( FormulaError::UnknownOpCode);
3420 void ScInterpreter::ScValue()
3422 OUString aInputString;
3423 double fVal;
3425 switch ( GetRawStackType() )
3427 case svMissing:
3428 case svEmptyCell:
3429 Pop();
3430 PushInt(0);
3431 return;
3432 case svDouble:
3433 return; // leave on stack
3435 case svSingleRef:
3436 case svDoubleRef:
3438 ScAddress aAdr;
3439 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3441 PushInt(0);
3442 return;
3444 ScRefCellValue aCell(mrDoc, aAdr);
3445 if (aCell.hasString())
3447 svl::SharedString aSS;
3448 GetCellString(aSS, aCell);
3449 aInputString = aSS.getString();
3451 else if (aCell.hasNumeric())
3453 PushDouble( GetCellValue(aAdr, aCell) );
3454 return;
3456 else
3458 PushDouble(0.0);
3459 return;
3462 break;
3463 case svMatrix:
3465 svl::SharedString aSS;
3466 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal,
3467 aSS);
3468 aInputString = aSS.getString();
3469 switch (nType)
3471 case ScMatValType::Empty:
3472 fVal = 0.0;
3473 [[fallthrough]];
3474 case ScMatValType::Value:
3475 case ScMatValType::Boolean:
3476 PushDouble( fVal);
3477 return;
3478 case ScMatValType::String:
3479 // evaluated below
3480 break;
3481 default:
3482 PushIllegalArgument();
3485 break;
3486 default:
3487 aInputString = GetString().getString();
3488 break;
3491 sal_uInt32 nFIndex = 0; // 0 for default locale
3492 if (mrContext.NFIsNumberFormat(aInputString, nFIndex, fVal))
3493 PushDouble(fVal);
3494 else
3495 PushIllegalArgument();
3498 // fdo#57180
3499 void ScInterpreter::ScNumberValue()
3502 sal_uInt8 nParamCount = GetByte();
3503 if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
3504 return;
3506 OUString aInputString;
3507 OUString aGroupSeparator;
3508 sal_Unicode cDecimalSeparator = 0;
3510 if ( nParamCount == 3 )
3511 aGroupSeparator = GetString().getString();
3513 if ( nParamCount >= 2 )
3515 OUString aDecimalSeparator = GetString().getString();
3516 if ( aDecimalSeparator.getLength() == 1 )
3517 cDecimalSeparator = aDecimalSeparator[ 0 ];
3518 else
3520 PushIllegalArgument(); //if given, separator length must be 1
3521 return;
3525 if ( cDecimalSeparator && aGroupSeparator.indexOf( cDecimalSeparator ) != -1 )
3527 PushIllegalArgument(); //decimal separator cannot appear in group separator
3528 return;
3531 switch (GetStackType())
3533 case svDouble:
3534 return; // leave on stack
3535 default:
3536 aInputString = GetString().getString();
3538 if ( nGlobalError != FormulaError::NONE )
3540 PushError( nGlobalError );
3541 return;
3543 if ( aInputString.isEmpty() )
3545 if ( maCalcConfig.mbEmptyStringAsZero )
3546 PushDouble( 0.0 );
3547 else
3548 PushNoValue();
3549 return;
3552 sal_Int32 nDecSep = aInputString.indexOf( cDecimalSeparator );
3553 if ( nDecSep != 0 )
3555 OUString aTemporary( nDecSep >= 0 ? aInputString.copy( 0, nDecSep ) : aInputString );
3556 sal_Int32 nIndex = 0;
3557 while (nIndex < aGroupSeparator.getLength())
3559 sal_uInt32 nChar = aGroupSeparator.iterateCodePoints( &nIndex );
3560 aTemporary = aTemporary.replaceAll( OUString( &nChar, 1 ), "" );
3562 if ( nDecSep >= 0 )
3563 aInputString = aTemporary + aInputString.subView( nDecSep );
3564 else
3565 aInputString = aTemporary;
3568 for ( sal_Int32 i = aInputString.getLength(); --i >= 0; )
3570 sal_Unicode c = aInputString[ i ];
3571 if ( c == 0x0020 || c == 0x0009 || c == 0x000A || c == 0x000D )
3572 aInputString = aInputString.replaceAt( i, 1, u"" ); // remove spaces etc.
3574 sal_Int32 nPercentCount = 0;
3575 for ( sal_Int32 i = aInputString.getLength() - 1; i >= 0 && aInputString[ i ] == 0x0025; i-- )
3577 aInputString = aInputString.replaceAt( i, 1, u"" ); // remove and count trailing '%'
3578 nPercentCount++;
3581 rtl_math_ConversionStatus eStatus;
3582 sal_Int32 nParseEnd;
3583 double fVal = ::rtl::math::stringToDouble( aInputString, cDecimalSeparator, 0, &eStatus, &nParseEnd );
3584 if ( eStatus == rtl_math_ConversionStatus_Ok && nParseEnd == aInputString.getLength() )
3586 if (nPercentCount)
3587 fVal *= pow( 10.0, -(nPercentCount * 2)); // process '%' from input string
3588 PushDouble(fVal);
3589 return;
3591 PushNoValue();
3594 static bool lcl_ScInterpreter_IsPrintable( sal_uInt32 nCodePoint )
3596 return ( !u_isISOControl(nCodePoint) /*not in Cc*/
3597 && u_isdefined(nCodePoint) /*not in Cn*/ );
3601 void ScInterpreter::ScClean()
3603 OUString aStr = GetString().getString();
3605 OUStringBuffer aBuf( aStr.getLength() );
3606 sal_Int32 nIdx = 0;
3607 while ( nIdx < aStr.getLength() )
3609 sal_uInt32 c = aStr.iterateCodePoints( &nIdx );
3610 if ( lcl_ScInterpreter_IsPrintable( c ) )
3611 aBuf.appendUtf32( c );
3613 PushString( aBuf.makeStringAndClear() );
3617 void ScInterpreter::ScCode()
3619 //2do: make it full range unicode?
3620 OUString aStr = GetString().getString();
3621 if (aStr.isEmpty())
3622 PushInt(0);
3623 else
3625 //"classic" ByteString conversion flags
3626 const sal_uInt32 convertFlags =
3627 RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE |
3628 RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE |
3629 RTL_UNICODETOTEXT_FLAGS_FLUSH |
3630 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT |
3631 RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT |
3632 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE;
3633 PushInt( static_cast<unsigned char>(OUStringToOString(OUStringChar(aStr[0]), osl_getThreadTextEncoding(), convertFlags).toChar()) );
3637 void ScInterpreter::ScChar()
3639 //2do: make it full range unicode?
3640 double fVal = GetDouble();
3641 if (fVal < 0.0 || fVal >= 256.0)
3642 PushIllegalArgument();
3643 else
3645 //"classic" ByteString conversion flags
3646 const sal_uInt32 convertFlags =
3647 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
3648 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
3649 RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT;
3651 char cEncodedChar = static_cast<char>(fVal);
3652 OUString aStr(&cEncodedChar, 1, osl_getThreadTextEncoding(), convertFlags);
3653 PushString(aStr);
3657 /* #i70213# fullwidth/halfwidth conversion provided by
3658 * Takashi Nakamoto <bluedwarf@ooo>
3659 * erAck: added Excel compatibility conversions as seen in issue's test case. */
3661 static OUString lcl_convertIntoHalfWidth( const OUString & rStr )
3663 // Make the initialization thread-safe. Since another function needs to be called, move it all to another
3664 // function and thread-safely initialize a static reference in this function.
3665 auto init = []() -> utl::TransliterationWrapper&
3667 static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3668 trans.loadModuleByImplName( u"FULLWIDTH_HALFWIDTH_LIKE_ASC"_ustr, LANGUAGE_SYSTEM );
3669 return trans;
3671 static utl::TransliterationWrapper& aTrans( init());
3672 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3675 static OUString lcl_convertIntoFullWidth( const OUString & rStr )
3677 auto init = []() -> utl::TransliterationWrapper&
3679 static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3680 trans.loadModuleByImplName( u"HALFWIDTH_FULLWIDTH_LIKE_JIS"_ustr, LANGUAGE_SYSTEM );
3681 return trans;
3683 static utl::TransliterationWrapper& aTrans( init());
3684 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3687 /* ODFF:
3688 * Summary: Converts half-width to full-width ASCII and katakana characters.
3689 * Semantics: Conversion is done for half-width ASCII and katakana characters,
3690 * other characters are simply copied from T to the result. This is the
3691 * complementary function to ASC.
3692 * For references regarding halfwidth and fullwidth characters see
3693 * http://www.unicode.org/reports/tr11/
3694 * http://www.unicode.org/charts/charindex2.html#H
3695 * http://www.unicode.org/charts/charindex2.html#F
3697 void ScInterpreter::ScJis()
3699 if (MustHaveParamCount( GetByte(), 1))
3700 PushString( lcl_convertIntoFullWidth( GetString().getString()));
3703 /* ODFF:
3704 * Summary: Converts full-width to half-width ASCII and katakana characters.
3705 * Semantics: Conversion is done for full-width ASCII and katakana characters,
3706 * other characters are simply copied from T to the result. This is the
3707 * complementary function to JIS.
3709 void ScInterpreter::ScAsc()
3711 if (MustHaveParamCount( GetByte(), 1))
3712 PushString( lcl_convertIntoHalfWidth( GetString().getString()));
3715 void ScInterpreter::ScUnicode()
3717 if ( MustHaveParamCount( GetByte(), 1 ) )
3719 OUString aStr = GetString().getString();
3720 if (aStr.isEmpty())
3721 PushIllegalParameter();
3722 else
3724 PushDouble(aStr.iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
3729 void ScInterpreter::ScUnichar()
3731 if ( MustHaveParamCount( GetByte(), 1 ) )
3733 sal_uInt32 nCodePoint = GetUInt32();
3734 if (nGlobalError != FormulaError::NONE || !rtl::isUnicodeCodePoint(nCodePoint))
3735 PushIllegalArgument();
3736 else
3738 OUString aStr( &nCodePoint, 1 );
3739 PushString( aStr );
3744 bool ScInterpreter::SwitchToArrayRefList( ScMatrixRef& xResMat, SCSIZE nMatRows, double fCurrent,
3745 const std::function<void( SCSIZE i, double fCurrent )>& MatOpFunc, bool bDoMatOp )
3747 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
3748 if (!p || !p->IsArrayResult())
3749 return false;
3751 if (!xResMat)
3753 // Create and init all elements with current value.
3754 assert(nMatRows > 0);
3755 xResMat = GetNewMat( 1, nMatRows, true);
3756 xResMat->FillDouble( fCurrent, 0,0, 0,nMatRows-1);
3758 else if (bDoMatOp)
3760 // Current value and values from vector are operands
3761 // for each vector position.
3762 for (SCSIZE i=0; i < nMatRows; ++i)
3764 MatOpFunc( i, fCurrent);
3767 return true;
3770 void ScInterpreter::ScMin( bool bTextAsZero )
3772 short nParamCount = GetByte();
3773 if (!MustHaveParamCountMin( nParamCount, 1))
3774 return;
3776 ScMatrixRef xResMat;
3777 double nMin = ::std::numeric_limits<double>::max();
3778 auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMin )
3780 double fVecRes = xResMat->GetDouble(0,i);
3781 if (fVecRes > fCurMin)
3782 xResMat->PutDouble( fCurMin, 0,i);
3784 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3785 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3787 double nVal = 0.0;
3788 ScAddress aAdr;
3789 ScRange aRange;
3790 size_t nRefInList = 0;
3791 while (nParamCount-- > 0)
3793 switch (GetStackType())
3795 case svDouble :
3797 nVal = GetDouble();
3798 if (nMin > nVal) nMin = nVal;
3799 nFuncFmtType = SvNumFormatType::NUMBER;
3801 break;
3802 case svSingleRef :
3804 PopSingleRef( aAdr );
3805 ScRefCellValue aCell(mrDoc, aAdr);
3806 if (aCell.hasNumeric())
3808 nVal = GetCellValue(aAdr, aCell);
3809 CurFmtToFuncFmt();
3810 if (nMin > nVal) nMin = nVal;
3812 else if (bTextAsZero && aCell.hasString())
3814 if ( nMin > 0.0 )
3815 nMin = 0.0;
3818 break;
3819 case svRefList :
3821 // bDoMatOp only for non-array value when switching to
3822 // ArrayRefList.
3823 if (SwitchToArrayRefList( xResMat, nMatRows, nMin, MatOpFunc,
3824 nRefArrayPos == std::numeric_limits<size_t>::max()))
3826 nRefArrayPos = nRefInList;
3829 [[fallthrough]];
3830 case svDoubleRef :
3832 FormulaError nErr = FormulaError::NONE;
3833 PopDoubleRef( aRange, nParamCount, nRefInList);
3834 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
3835 if (aValIter.GetFirst(nVal, nErr))
3837 if (nMin > nVal)
3838 nMin = nVal;
3839 aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
3840 while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3842 if (nMin > nVal)
3843 nMin = nVal;
3845 SetError(nErr);
3847 if (nRefArrayPos != std::numeric_limits<size_t>::max())
3849 // Update vector element with current value.
3850 MatOpFunc( nRefArrayPos, nMin);
3852 // Reset.
3853 nMin = std::numeric_limits<double>::max();
3854 nVal = 0.0;
3855 nRefArrayPos = std::numeric_limits<size_t>::max();
3858 break;
3859 case svMatrix :
3860 case svExternalSingleRef:
3861 case svExternalDoubleRef:
3863 ScMatrixRef pMat = GetMatrix();
3864 if (pMat)
3866 nFuncFmtType = SvNumFormatType::NUMBER;
3867 nVal = pMat->GetMinValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
3868 if (nMin > nVal)
3869 nMin = nVal;
3872 break;
3873 case svString :
3875 Pop();
3876 if ( bTextAsZero )
3878 if ( nMin > 0.0 )
3879 nMin = 0.0;
3881 else
3882 SetError(FormulaError::IllegalParameter);
3884 break;
3885 default :
3886 PopError();
3887 SetError(FormulaError::IllegalParameter);
3891 if (xResMat)
3893 // Include value of last non-references-array type and calculate final result.
3894 if (nMin < std::numeric_limits<double>::max())
3896 for (SCSIZE i=0; i < nMatRows; ++i)
3898 MatOpFunc( i, nMin);
3901 else
3903 /* TODO: the awkward "no value is minimum 0.0" is likely the case
3904 * if a value is numeric_limits::max. Still, that could be a valid
3905 * minimum value as well, but nVal and nMin had been reset after
3906 * the last svRefList... so we may lie here. */
3907 for (SCSIZE i=0; i < nMatRows; ++i)
3909 double fVecRes = xResMat->GetDouble(0,i);
3910 if (fVecRes == std::numeric_limits<double>::max())
3911 xResMat->PutDouble( 0.0, 0,i);
3914 PushMatrix( xResMat);
3916 else
3918 if (!std::isfinite(nVal))
3919 PushError( GetDoubleErrorValue( nVal));
3920 else if ( nVal < nMin )
3921 PushDouble(0.0); // zero or only empty arguments
3922 else
3923 PushDouble(nMin);
3927 void ScInterpreter::ScMax( bool bTextAsZero )
3929 short nParamCount = GetByte();
3930 if (!MustHaveParamCountMin( nParamCount, 1))
3931 return;
3933 ScMatrixRef xResMat;
3934 double nMax = std::numeric_limits<double>::lowest();
3935 auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMax )
3937 double fVecRes = xResMat->GetDouble(0,i);
3938 if (fVecRes < fCurMax)
3939 xResMat->PutDouble( fCurMax, 0,i);
3941 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3942 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3944 double nVal = 0.0;
3945 ScAddress aAdr;
3946 ScRange aRange;
3947 size_t nRefInList = 0;
3948 while (nParamCount-- > 0)
3950 switch (GetStackType())
3952 case svDouble :
3954 nVal = GetDouble();
3955 if (nMax < nVal) nMax = nVal;
3956 nFuncFmtType = SvNumFormatType::NUMBER;
3958 break;
3959 case svSingleRef :
3961 PopSingleRef( aAdr );
3962 ScRefCellValue aCell(mrDoc, aAdr);
3963 if (aCell.hasNumeric())
3965 nVal = GetCellValue(aAdr, aCell);
3966 CurFmtToFuncFmt();
3967 if (nMax < nVal) nMax = nVal;
3969 else if (bTextAsZero && aCell.hasString())
3971 if ( nMax < 0.0 )
3972 nMax = 0.0;
3975 break;
3976 case svRefList :
3978 // bDoMatOp only for non-array value when switching to
3979 // ArrayRefList.
3980 if (SwitchToArrayRefList( xResMat, nMatRows, nMax, MatOpFunc,
3981 nRefArrayPos == std::numeric_limits<size_t>::max()))
3983 nRefArrayPos = nRefInList;
3986 [[fallthrough]];
3987 case svDoubleRef :
3989 FormulaError nErr = FormulaError::NONE;
3990 PopDoubleRef( aRange, nParamCount, nRefInList);
3991 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
3992 if (aValIter.GetFirst(nVal, nErr))
3994 if (nMax < nVal)
3995 nMax = nVal;
3996 aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
3997 while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3999 if (nMax < nVal)
4000 nMax = nVal;
4002 SetError(nErr);
4004 if (nRefArrayPos != std::numeric_limits<size_t>::max())
4006 // Update vector element with current value.
4007 MatOpFunc( nRefArrayPos, nMax);
4009 // Reset.
4010 nMax = std::numeric_limits<double>::lowest();
4011 nVal = 0.0;
4012 nRefArrayPos = std::numeric_limits<size_t>::max();
4015 break;
4016 case svMatrix :
4017 case svExternalSingleRef:
4018 case svExternalDoubleRef:
4020 ScMatrixRef pMat = GetMatrix();
4021 if (pMat)
4023 nFuncFmtType = SvNumFormatType::NUMBER;
4024 nVal = pMat->GetMaxValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
4025 if (nMax < nVal)
4026 nMax = nVal;
4029 break;
4030 case svString :
4032 Pop();
4033 if ( bTextAsZero )
4035 if ( nMax < 0.0 )
4036 nMax = 0.0;
4038 else
4039 SetError(FormulaError::IllegalParameter);
4041 break;
4042 default :
4043 PopError();
4044 SetError(FormulaError::IllegalParameter);
4048 if (xResMat)
4050 // Include value of last non-references-array type and calculate final result.
4051 if (nMax > std::numeric_limits<double>::lowest())
4053 for (SCSIZE i=0; i < nMatRows; ++i)
4055 MatOpFunc( i, nMax);
4058 else
4060 /* TODO: the awkward "no value is maximum 0.0" is likely the case
4061 * if a value is numeric_limits::lowest. Still, that could be a
4062 * valid maximum value as well, but nVal and nMax had been reset
4063 * after the last svRefList... so we may lie here. */
4064 for (SCSIZE i=0; i < nMatRows; ++i)
4066 double fVecRes = xResMat->GetDouble(0,i);
4067 if (fVecRes == -std::numeric_limits<double>::max())
4068 xResMat->PutDouble( 0.0, 0,i);
4071 PushMatrix( xResMat);
4073 else
4075 if (!std::isfinite(nVal))
4076 PushError( GetDoubleErrorValue( nVal));
4077 else if ( nVal > nMax )
4078 PushDouble(0.0); // zero or only empty arguments
4079 else
4080 PushDouble(nMax);
4084 void ScInterpreter::GetStVarParams( bool bTextAsZero, double(*VarResult)( double fVal, size_t nValCount ) )
4086 short nParamCount = GetByte();
4087 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
4089 struct ArrayRefListValue
4091 std::vector<double> mvValues;
4092 KahanSum mfSum;
4093 ArrayRefListValue() = default;
4094 double get() const { return mfSum.get(); }
4096 std::vector<ArrayRefListValue> vArrayValues;
4098 std::vector<double> values;
4099 KahanSum fSum = 0.0;
4100 double fVal = 0.0;
4101 ScAddress aAdr;
4102 ScRange aRange;
4103 size_t nRefInList = 0;
4104 while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
4106 switch (GetStackType())
4108 case svDouble :
4110 fVal = GetDouble();
4111 if (nGlobalError == FormulaError::NONE)
4113 values.push_back(fVal);
4114 fSum += fVal;
4117 break;
4118 case svSingleRef :
4120 PopSingleRef( aAdr );
4121 ScRefCellValue aCell(mrDoc, aAdr);
4122 if (aCell.hasNumeric())
4124 fVal = GetCellValue(aAdr, aCell);
4125 if (nGlobalError == FormulaError::NONE)
4127 values.push_back(fVal);
4128 fSum += fVal;
4131 else if (bTextAsZero && aCell.hasString())
4133 values.push_back(0.0);
4136 break;
4137 case svRefList :
4139 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
4140 if (p && p->IsArrayResult())
4142 size_t nRefArrayPos = nRefInList;
4143 if (vArrayValues.empty())
4145 // Create and init all elements with current value.
4146 assert(nMatRows > 0);
4147 vArrayValues.resize(nMatRows);
4148 for (ArrayRefListValue & it : vArrayValues)
4150 it.mvValues = values;
4151 it.mfSum = fSum;
4154 else
4156 // Current value and values from vector are operands
4157 // for each vector position.
4158 for (ArrayRefListValue & it : vArrayValues)
4160 it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
4161 it.mfSum += fSum;
4164 ArrayRefListValue& rArrayValue = vArrayValues[nRefArrayPos];
4165 FormulaError nErr = FormulaError::NONE;
4166 PopDoubleRef( aRange, nParamCount, nRefInList);
4167 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
4168 if (aValIter.GetFirst(fVal, nErr))
4172 rArrayValue.mvValues.push_back(fVal);
4173 rArrayValue.mfSum += fVal;
4175 while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4177 if ( nErr != FormulaError::NONE )
4179 rArrayValue.mfSum = CreateDoubleError( nErr);
4181 // Reset.
4182 std::vector<double>().swap(values);
4183 fSum = 0.0;
4184 break;
4187 [[fallthrough]];
4188 case svDoubleRef :
4190 FormulaError nErr = FormulaError::NONE;
4191 PopDoubleRef( aRange, nParamCount, nRefInList);
4192 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
4193 if (aValIter.GetFirst(fVal, nErr))
4197 values.push_back(fVal);
4198 fSum += fVal;
4200 while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4202 if ( nErr != FormulaError::NONE )
4204 SetError(nErr);
4207 break;
4208 case svExternalSingleRef :
4209 case svExternalDoubleRef :
4210 case svMatrix :
4212 ScMatrixRef pMat = GetMatrix();
4213 if (pMat)
4215 const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal);
4216 SCSIZE nC, nR;
4217 pMat->GetDimensions(nC, nR);
4218 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
4220 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
4222 if (!pMat->IsStringOrEmpty(nMatCol,nMatRow))
4224 fVal= pMat->GetDouble(nMatCol,nMatRow);
4225 if (nGlobalError == FormulaError::NONE)
4227 values.push_back(fVal);
4228 fSum += fVal;
4230 else if (bIgnoreErrVal)
4231 nGlobalError = FormulaError::NONE;
4233 else if ( bTextAsZero )
4235 values.push_back(0.0);
4241 break;
4242 case svString :
4244 Pop();
4245 if ( bTextAsZero )
4247 values.push_back(0.0);
4249 else
4250 SetError(FormulaError::IllegalParameter);
4252 break;
4253 default :
4254 PopError();
4255 SetError(FormulaError::IllegalParameter);
4259 if (!vArrayValues.empty())
4261 // Include value of last non-references-array type and calculate final result.
4262 if (!values.empty())
4264 for (auto & it : vArrayValues)
4266 it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
4267 it.mfSum += fSum;
4270 ScMatrixRef xResMat = GetNewMat( 1, nMatRows, true);
4271 for (SCSIZE r=0; r < nMatRows; ++r)
4273 ::std::vector<double>::size_type n = vArrayValues[r].mvValues.size();
4274 if (!n)
4275 xResMat->PutError( FormulaError::DivisionByZero, 0, r);
4276 else
4278 ArrayRefListValue& rArrayValue = vArrayValues[r];
4279 double vSum = 0.0;
4280 const double vMean = rArrayValue.get() / n;
4281 for (::std::vector<double>::size_type i = 0; i < n; i++)
4282 vSum += ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean) *
4283 ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean);
4284 xResMat->PutDouble( VarResult( vSum, n), 0, r);
4287 PushMatrix( xResMat);
4289 else
4291 ::std::vector<double>::size_type n = values.size();
4292 if (!n)
4293 SetError( FormulaError::DivisionByZero);
4294 double vSum = 0.0;
4295 if (nGlobalError == FormulaError::NONE)
4297 const double vMean = fSum.get() / n;
4298 for (::std::vector<double>::size_type i = 0; i < n; i++)
4299 vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean);
4301 PushDouble( VarResult( vSum, n));
4305 void ScInterpreter::ScVar( bool bTextAsZero )
4307 auto VarResult = []( double fVal, size_t nValCount )
4309 if (nValCount <= 1)
4310 return CreateDoubleError( FormulaError::DivisionByZero );
4311 else
4312 return fVal / (nValCount - 1);
4314 GetStVarParams( bTextAsZero, VarResult );
4317 void ScInterpreter::ScVarP( bool bTextAsZero )
4319 auto VarResult = []( double fVal, size_t nValCount )
4321 return sc::div( fVal, nValCount);
4323 GetStVarParams( bTextAsZero, VarResult );
4327 void ScInterpreter::ScStDev( bool bTextAsZero )
4329 auto VarResult = []( double fVal, size_t nValCount )
4331 if (nValCount <= 1)
4332 return CreateDoubleError( FormulaError::DivisionByZero );
4333 else
4334 return sqrt( fVal / (nValCount - 1));
4336 GetStVarParams( bTextAsZero, VarResult );
4339 void ScInterpreter::ScStDevP( bool bTextAsZero )
4341 auto VarResult = []( double fVal, size_t nValCount )
4343 if (nValCount == 0)
4344 return CreateDoubleError( FormulaError::DivisionByZero );
4345 else
4346 return sqrt( fVal / nValCount);
4348 GetStVarParams( bTextAsZero, VarResult );
4350 /* this was: PushDouble( sqrt( div( nVal, nValCount)));
4352 * Besides that the special NAN gets lost in the call through sqrt(),
4353 * unxlngi6.pro then looped back and forth somewhere between div() and
4354 * ::rtl::math::setNan(). Tests showed that
4356 * sqrt( div( 1, 0));
4358 * produced a loop, but
4360 * double f1 = div( 1, 0);
4361 * sqrt( f1 );
4363 * was fine. There seems to be some compiler optimization problem. It does
4364 * not occur when compiled with debug=t.
4368 void ScInterpreter::ScColumns()
4370 sal_uInt8 nParamCount = GetByte();
4371 sal_uLong nVal = 0;
4372 SCCOL nCol1;
4373 SCROW nRow1;
4374 SCTAB nTab1;
4375 SCCOL nCol2;
4376 SCROW nRow2;
4377 SCTAB nTab2;
4378 while (nParamCount-- > 0)
4380 switch ( GetStackType() )
4382 case svSingleRef:
4383 PopError();
4384 nVal++;
4385 break;
4386 case svDoubleRef:
4387 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4388 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4389 static_cast<sal_uLong>(nCol2 - nCol1 + 1);
4390 break;
4391 case svMatrix:
4393 ScMatrixRef pMat = PopMatrix();
4394 if (pMat)
4396 SCSIZE nC, nR;
4397 pMat->GetDimensions(nC, nR);
4398 nVal += nC;
4401 break;
4402 case svExternalSingleRef:
4403 PopError();
4404 nVal++;
4405 break;
4406 case svExternalDoubleRef:
4408 sal_uInt16 nFileId;
4409 OUString aTabName;
4410 ScComplexRefData aRef;
4411 PopExternalDoubleRef( nFileId, aTabName, aRef);
4412 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4413 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4414 static_cast<sal_uLong>(aAbs.aEnd.Col() - aAbs.aStart.Col() + 1);
4416 break;
4417 default:
4418 PopError();
4419 SetError(FormulaError::IllegalParameter);
4422 PushDouble(static_cast<double>(nVal));
4425 void ScInterpreter::ScRows()
4427 sal_uInt8 nParamCount = GetByte();
4428 sal_uLong nVal = 0;
4429 SCCOL nCol1;
4430 SCROW nRow1;
4431 SCTAB nTab1;
4432 SCCOL nCol2;
4433 SCROW nRow2;
4434 SCTAB nTab2;
4435 while (nParamCount-- > 0)
4437 switch ( GetStackType() )
4439 case svSingleRef:
4440 PopError();
4441 nVal++;
4442 break;
4443 case svDoubleRef:
4444 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4445 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4446 static_cast<sal_uLong>(nRow2 - nRow1 + 1);
4447 break;
4448 case svMatrix:
4450 ScMatrixRef pMat = PopMatrix();
4451 if (pMat)
4453 SCSIZE nC, nR;
4454 pMat->GetDimensions(nC, nR);
4455 nVal += nR;
4458 break;
4459 case svExternalSingleRef:
4460 PopError();
4461 nVal++;
4462 break;
4463 case svExternalDoubleRef:
4465 sal_uInt16 nFileId;
4466 OUString aTabName;
4467 ScComplexRefData aRef;
4468 PopExternalDoubleRef( nFileId, aTabName, aRef);
4469 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4470 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4471 static_cast<sal_uLong>(aAbs.aEnd.Row() - aAbs.aStart.Row() + 1);
4473 break;
4474 default:
4475 PopError();
4476 SetError(FormulaError::IllegalParameter);
4479 PushDouble(static_cast<double>(nVal));
4482 void ScInterpreter::ScSheets()
4484 sal_uInt8 nParamCount = GetByte();
4485 sal_uLong nVal;
4486 if ( nParamCount == 0 )
4487 nVal = mrDoc.GetTableCount();
4488 else
4490 nVal = 0;
4491 SCCOL nCol1;
4492 SCROW nRow1;
4493 SCTAB nTab1;
4494 SCCOL nCol2;
4495 SCROW nRow2;
4496 SCTAB nTab2;
4497 while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
4499 switch ( GetStackType() )
4501 case svSingleRef:
4502 case svExternalSingleRef:
4503 PopError();
4504 nVal++;
4505 break;
4506 case svDoubleRef:
4507 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4508 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1);
4509 break;
4510 case svExternalDoubleRef:
4512 sal_uInt16 nFileId;
4513 OUString aTabName;
4514 ScComplexRefData aRef;
4515 PopExternalDoubleRef( nFileId, aTabName, aRef);
4516 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4517 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1);
4519 break;
4520 default:
4521 PopError();
4522 SetError( FormulaError::IllegalParameter );
4526 PushDouble( static_cast<double>(nVal) );
4529 void ScInterpreter::ScColumn()
4531 sal_uInt8 nParamCount = GetByte();
4532 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4533 return;
4535 double nVal = 0.0;
4536 if (nParamCount == 0)
4538 nVal = aPos.Col() + 1;
4539 if (bMatrixFormula)
4541 SCCOL nCols = 0;
4542 SCROW nRows = 0;
4543 if (pMyFormulaCell)
4544 pMyFormulaCell->GetMatColsRows( nCols, nRows);
4545 bool bMayBeScalar;
4546 if (nCols == 0)
4548 // Happens if called via ScViewFunc::EnterMatrix()
4549 // ScFormulaCell::GetResultDimensions() as of course a
4550 // matrix result is not available yet.
4551 nCols = 1;
4552 bMayBeScalar = false;
4554 else
4556 bMayBeScalar = true;
4558 if (!bMayBeScalar || nCols != 1 || nRows != 1)
4560 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1, /*bEmpty*/true );
4561 if (pResMat)
4563 for (SCCOL i=0; i < nCols; ++i)
4564 pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0);
4565 PushMatrix( pResMat);
4566 return;
4571 else
4573 switch ( GetStackType() )
4575 case svSingleRef :
4577 SCCOL nCol1(0);
4578 SCROW nRow1(0);
4579 SCTAB nTab1(0);
4580 PopSingleRef( nCol1, nRow1, nTab1 );
4581 nVal = static_cast<double>(nCol1 + 1);
4583 break;
4584 case svExternalSingleRef :
4586 sal_uInt16 nFileId;
4587 OUString aTabName;
4588 ScSingleRefData aRef;
4589 PopExternalSingleRef( nFileId, aTabName, aRef );
4590 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4591 nVal = static_cast<double>( aAbsRef.Col() + 1 );
4593 break;
4595 case svDoubleRef :
4596 case svExternalDoubleRef :
4598 SCCOL nCol1;
4599 SCCOL nCol2;
4600 if ( GetStackType() == svDoubleRef )
4602 SCROW nRow1;
4603 SCTAB nTab1;
4604 SCROW nRow2;
4605 SCTAB nTab2;
4606 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4608 else
4610 sal_uInt16 nFileId;
4611 OUString aTabName;
4612 ScComplexRefData aRef;
4613 PopExternalDoubleRef( nFileId, aTabName, aRef );
4614 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4615 nCol1 = aAbs.aStart.Col();
4616 nCol2 = aAbs.aEnd.Col();
4618 if (nCol2 > nCol1)
4620 ScMatrixRef pResMat = GetNewMat(
4621 static_cast<SCSIZE>(nCol2-nCol1+1), 1, /*bEmpty*/true);
4622 if (pResMat)
4624 for (SCCOL i = nCol1; i <= nCol2; i++)
4625 pResMat->PutDouble(static_cast<double>(i+1),
4626 static_cast<SCSIZE>(i-nCol1), 0);
4627 PushMatrix(pResMat);
4628 return;
4631 else
4632 nVal = static_cast<double>(nCol1 + 1);
4634 break;
4635 default:
4636 SetError( FormulaError::IllegalParameter );
4639 PushDouble( nVal );
4642 void ScInterpreter::ScRow()
4644 sal_uInt8 nParamCount = GetByte();
4645 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4646 return;
4648 double nVal = 0.0;
4649 if (nParamCount == 0)
4651 nVal = aPos.Row() + 1;
4652 if (bMatrixFormula)
4654 SCCOL nCols = 0;
4655 SCROW nRows = 0;
4656 if (pMyFormulaCell)
4657 pMyFormulaCell->GetMatColsRows( nCols, nRows);
4658 bool bMayBeScalar;
4659 if (nRows == 0)
4661 // Happens if called via ScViewFunc::EnterMatrix()
4662 // ScFormulaCell::GetResultDimensions() as of course a
4663 // matrix result is not available yet.
4664 nRows = 1;
4665 bMayBeScalar = false;
4667 else
4669 bMayBeScalar = true;
4671 if (!bMayBeScalar || nCols != 1 || nRows != 1)
4673 ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows), /*bEmpty*/true);
4674 if (pResMat)
4676 for (SCROW i=0; i < nRows; i++)
4677 pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i));
4678 PushMatrix( pResMat);
4679 return;
4684 else
4686 switch ( GetStackType() )
4688 case svSingleRef :
4690 SCCOL nCol1(0);
4691 SCROW nRow1(0);
4692 SCTAB nTab1(0);
4693 PopSingleRef( nCol1, nRow1, nTab1 );
4694 nVal = static_cast<double>(nRow1 + 1);
4696 break;
4697 case svExternalSingleRef :
4699 sal_uInt16 nFileId;
4700 OUString aTabName;
4701 ScSingleRefData aRef;
4702 PopExternalSingleRef( nFileId, aTabName, aRef );
4703 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4704 nVal = static_cast<double>( aAbsRef.Row() + 1 );
4706 break;
4707 case svDoubleRef :
4708 case svExternalDoubleRef :
4710 SCROW nRow1;
4711 SCROW nRow2;
4712 if ( GetStackType() == svDoubleRef )
4714 SCCOL nCol1;
4715 SCTAB nTab1;
4716 SCCOL nCol2;
4717 SCTAB nTab2;
4718 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4720 else
4722 sal_uInt16 nFileId;
4723 OUString aTabName;
4724 ScComplexRefData aRef;
4725 PopExternalDoubleRef( nFileId, aTabName, aRef );
4726 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4727 nRow1 = aAbs.aStart.Row();
4728 nRow2 = aAbs.aEnd.Row();
4730 if (nRow2 > nRow1)
4732 ScMatrixRef pResMat = GetNewMat( 1,
4733 static_cast<SCSIZE>(nRow2-nRow1+1), /*bEmpty*/true);
4734 if (pResMat)
4736 for (SCROW i = nRow1; i <= nRow2; i++)
4737 pResMat->PutDouble(static_cast<double>(i+1), 0,
4738 static_cast<SCSIZE>(i-nRow1));
4739 PushMatrix(pResMat);
4740 return;
4743 else
4744 nVal = static_cast<double>(nRow1 + 1);
4746 break;
4747 default:
4748 SetError( FormulaError::IllegalParameter );
4751 PushDouble( nVal );
4754 void ScInterpreter::ScSheet()
4756 sal_uInt8 nParamCount = GetByte();
4757 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4758 return;
4760 SCTAB nVal = 0;
4761 if ( nParamCount == 0 )
4762 nVal = aPos.Tab() + 1;
4763 else
4765 switch ( GetStackType() )
4767 case svString :
4769 svl::SharedString aStr = PopString();
4770 if ( mrDoc.GetTable(aStr.getString(), nVal))
4771 ++nVal;
4772 else
4773 SetError( FormulaError::IllegalArgument );
4775 break;
4776 case svSingleRef :
4778 SCCOL nCol1(0);
4779 SCROW nRow1(0);
4780 SCTAB nTab1(0);
4781 PopSingleRef(nCol1, nRow1, nTab1);
4782 nVal = nTab1 + 1;
4784 break;
4785 case svDoubleRef :
4787 SCCOL nCol1;
4788 SCROW nRow1;
4789 SCTAB nTab1;
4790 SCCOL nCol2;
4791 SCROW nRow2;
4792 SCTAB nTab2;
4793 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4794 nVal = nTab1 + 1;
4796 break;
4797 default:
4798 SetError( FormulaError::IllegalParameter );
4800 if ( nGlobalError != FormulaError::NONE )
4801 nVal = 0;
4803 PushDouble( static_cast<double>(nVal) );
4806 namespace {
4808 class VectorMatrixAccessor
4810 public:
4811 VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) :
4812 mrMat(rMat), mbColVec(bColVec) {}
4814 bool IsEmpty(SCSIZE i) const
4816 return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0);
4819 bool IsEmptyPath(SCSIZE i) const
4821 return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0);
4824 bool IsValue(SCSIZE i) const
4826 return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0);
4829 bool IsStringOrEmpty(SCSIZE i) const
4831 return mbColVec ? mrMat.IsStringOrEmpty(0, i) : mrMat.IsStringOrEmpty(i, 0);
4834 double GetDouble(SCSIZE i) const
4836 return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0);
4839 OUString GetString(SCSIZE i) const
4841 return mbColVec ? mrMat.GetString(0, i).getString() : mrMat.GetString(i, 0).getString();
4844 SCSIZE GetElementCount() const
4846 SCSIZE nC, nR;
4847 mrMat.GetDimensions(nC, nR);
4848 return mbColVec ? nR : nC;
4851 private:
4852 const ScMatrix& mrMat;
4853 bool mbColVec;
4856 /** returns -1 when the matrix value is smaller than the query value, 0 when
4857 they are equal, and 1 when the matrix value is larger than the query
4858 value. */
4859 sal_Int32 lcl_CompareMatrix2Query(
4860 SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryParam& rParam, const ScQueryEntry& rEntry, bool bMatchWholeCell )
4862 if (rMat.IsEmpty(i))
4864 /* TODO: in case we introduced query for real empty this would have to
4865 * be changed! */
4866 return -1; // empty always less than anything else
4869 /* FIXME: what is an empty path (result of IF(false;true_path) in
4870 * comparisons? */
4872 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
4873 if (rMat.IsValue(i))
4875 const double nVal1 = rMat.GetDouble(i);
4876 if (!std::isfinite(nVal1))
4878 // XXX Querying for error values is not required, otherwise we'd
4879 // need to check here.
4880 return 1; // error always greater than numeric or string
4883 if (bByString)
4884 return -1; // numeric always less than string
4886 const double nVal2 = rEntry.GetQueryItem().mfVal;
4887 // XXX Querying for error values is not required, otherwise we'd need
4888 // to check here and move that check before the bByString check.
4889 if (nVal1 == nVal2)
4890 return 0;
4892 return nVal1 < nVal2 ? -1 : 1;
4895 if (!bByString)
4896 return 1; // string always greater than numeric
4898 OUString aStr1 = rMat.GetString(i);
4899 OUString aStr2 = rEntry.GetQueryItem().maString.getString();
4901 // bRealWildOrRegExp
4902 if (rParam.eSearchType != utl::SearchParam::SearchType::Normal &&
4903 ((rEntry.eOp == SC_EQUAL) || (rEntry.eOp == SC_NOT_EQUAL)))
4905 sal_Int32 nStart = 0;
4906 sal_Int32 nEnd = aStr1.getLength();
4908 bool bMatch = rEntry.GetSearchTextPtr(rParam.eSearchType, rParam.bCaseSens, bMatchWholeCell)
4909 ->SearchForward(aStr1, &nStart, &nEnd);
4910 // from 614 on, nEnd is behind the found text
4911 if (bMatch && bMatchWholeCell
4912 && (nStart != 0 || nEnd != aStr1.getLength()))
4913 bMatch = false; // RegExp must match entire cell string
4915 bool bOk = ((rEntry.eOp == SC_NOT_EQUAL) ? !bMatch : bMatch);
4917 if (bOk)
4918 return 0; // we have a WildOrRegExp match
4921 CollatorWrapper& rCollator = ScGlobal::GetCollator(rParam.bCaseSens);
4922 return rCollator.compareString(aStr1, aStr2);
4925 /** returns -1 when matrix(i) value is smaller than matrix(j) value, 0 when
4926 they are equal, and 1 when larger */
4927 sal_Int32 lcl_Compare2MatrixCells( SCSIZE i, const VectorMatrixAccessor& rMat, SCSIZE j )
4929 // empty always less than anything else
4930 if (rMat.IsEmpty(i))
4931 return ( rMat.IsEmpty(j) ? 0 : -1 );
4932 else if (rMat.IsEmpty(j))
4933 return 1;
4935 bool bByString = rMat.IsStringOrEmpty(j); // string, empty has already been handled
4936 if (rMat.IsValue(i))
4938 const double nVal1 = rMat.GetDouble(i);
4939 if (!std::isfinite(nVal1))
4940 return 1; // error always greater than numeric or string
4942 if (bByString)
4943 return -1; // numeric always less than string
4945 const double nVal2 = rMat.GetDouble(j);
4946 if (nVal1 == nVal2)
4947 return 0;
4949 return ( nVal1 < nVal2 ? -1 : 1 );
4952 if (!bByString)
4953 return 1; // string always greater than numeric
4955 return ScGlobal::GetCollator().compareString(rMat.GetString(i), rMat.GetString(j)); // case-insensitive
4958 /** returns the last item with the identical value as the original item
4959 value. */
4960 void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat,
4961 SCSIZE nMatCount)
4963 if (rMat.IsValue(rIndex))
4965 double nVal = rMat.GetDouble(rIndex);
4966 while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) &&
4967 nVal == rMat.GetDouble(rIndex+1))
4968 ++rIndex;
4970 // Order of IsEmptyPath, IsEmpty, IsStringOrEmpty is significant!
4971 else if (rMat.IsEmptyPath(rIndex))
4973 while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1))
4974 ++rIndex;
4976 else if (rMat.IsEmpty(rIndex))
4978 while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1))
4979 ++rIndex;
4981 else if (rMat.IsStringOrEmpty(rIndex))
4983 OUString aStr( rMat.GetString(rIndex));
4984 while (rIndex < nMatCount-1 && rMat.IsStringOrEmpty(rIndex+1) &&
4985 aStr == rMat.GetString(rIndex+1))
4986 ++rIndex;
4988 else
4990 OSL_FAIL("lcl_GetLastMatch: unhandled matrix type");
4996 void ScInterpreter::ScMatch()
4998 sal_uInt8 nParamCount = GetByte();
4999 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
5000 return;
5002 VectorSearchArguments vsa;
5003 vsa.nSearchOpCode = SC_OPCODE_MATCH;
5005 // get match mode
5006 double fType = ( nParamCount == 3 ? GetDouble() : 1.0 );
5007 switch ( static_cast<int>(fType) )
5009 case -1 :
5010 vsa.eMatchMode = exactorG;
5011 vsa.eSearchMode = searchbdesc;
5012 break;
5013 case 0 :
5014 vsa.eMatchMode = exactorNA;
5015 vsa.eSearchMode = searchfwd;
5016 break;
5017 case 1 :
5018 // default value
5019 vsa.eMatchMode = exactorS;
5020 vsa.eSearchMode = searchbasc;
5021 break;
5022 default :
5023 PushIllegalParameter();
5024 return;
5027 // get vector to be searched
5028 switch (GetStackType())
5030 case svSingleRef:
5031 PopSingleRef( vsa.nCol1, vsa.nRow1, vsa.nTab1);
5032 vsa.nCol2 = vsa.nCol1;
5033 vsa.nRow2 = vsa.nRow1;
5034 vsa.pMatSrc = nullptr;
5035 break;
5036 case svDoubleRef:
5038 vsa.pMatSrc = nullptr;
5039 SCTAB nTab2 = 0;
5040 PopDoubleRef(vsa.nCol1, vsa.nRow1, vsa.nTab1, vsa.nCol2, vsa.nRow2, nTab2);
5041 if (vsa.nTab1 != nTab2 || (vsa.nCol1 != vsa.nCol2 && vsa.nRow1 != vsa.nRow2))
5043 PushIllegalParameter();
5044 return;
5047 break;
5048 case svMatrix:
5049 case svExternalDoubleRef:
5051 if (GetStackType() == svMatrix)
5052 vsa.pMatSrc = PopMatrix();
5053 else
5054 PopExternalDoubleRef(vsa.pMatSrc);
5056 if (!vsa.pMatSrc)
5058 PushIllegalParameter();
5059 return;
5062 break;
5063 default:
5064 PushIllegalParameter();
5065 return;
5068 // get search value
5069 if (nGlobalError == FormulaError::NONE)
5071 switch ( GetStackType() )
5073 case svDouble:
5075 vsa.isStringSearch = false;
5076 vsa.fSearchVal = GetDouble();
5078 break;
5079 case svString:
5081 vsa.isStringSearch = true;
5082 vsa.sSearchStr = GetString();
5084 break;
5085 case svDoubleRef :
5086 case svSingleRef :
5088 ScAddress aAdr;
5089 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5091 PushInt(0);
5092 return ;
5094 ScRefCellValue aCell(mrDoc, aAdr);
5095 if (aCell.hasNumeric())
5097 vsa.isStringSearch = false;
5098 vsa.fSearchVal = GetCellValue(aAdr, aCell);
5100 else
5102 vsa.isStringSearch = true;
5103 GetCellString(vsa.sSearchStr, aCell);
5106 break;
5107 case svExternalSingleRef:
5109 ScExternalRefCache::TokenRef pToken;
5110 PopExternalSingleRef(pToken);
5111 if (nGlobalError != FormulaError::NONE)
5113 PushError( nGlobalError);
5114 return;
5116 if (pToken->GetType() == svDouble)
5118 vsa.isStringSearch = false;
5119 vsa.fSearchVal = pToken->GetDouble();
5121 else
5123 vsa.isStringSearch = true;
5124 vsa.sSearchStr = pToken->GetString();
5127 break;
5128 case svExternalDoubleRef:
5129 case svMatrix :
5131 ScMatValType nType = GetDoubleOrStringFromMatrix(
5132 vsa.fSearchVal, vsa.sSearchStr);
5133 vsa.isStringSearch = ScMatrix::IsNonValueType(nType);
5135 break;
5136 default:
5138 PushIllegalParameter();
5139 return;
5143 // execute search
5144 if ( SearchVectorForValue( vsa ) )
5145 PushDouble( vsa.nIndex );
5146 else
5148 if ( vsa.isResultNA )
5149 PushNA();
5150 else
5151 return; // error occurred and has already been pushed
5154 else
5155 PushIllegalParameter();
5158 void ScInterpreter::ScXMatch()
5160 sal_uInt8 nParamCount = GetByte();
5161 if (!MustHaveParamCount(nParamCount, 2, 4))
5162 return;
5164 VectorSearchArguments vsa;
5165 vsa.nSearchOpCode = SC_OPCODE_X_MATCH;
5167 // get search mode
5168 if (nParamCount == 4)
5170 sal_Int16 k = GetInt16();
5171 if (k >= -2 && k <= 2 && k != 0)
5172 vsa.eSearchMode = static_cast<SearchMode>(k);
5173 else
5175 PushIllegalParameter();
5176 return;
5179 else
5180 vsa.eSearchMode = searchfwd;
5182 // get match mode
5183 if (nParamCount >= 3)
5185 sal_Int16 k = GetInt16();
5186 if (k >= -1 && k <= 3)
5187 vsa.eMatchMode = static_cast<MatchMode>(k);
5188 else
5190 PushIllegalParameter();
5191 return;
5194 else
5195 vsa.eMatchMode = exactorNA;
5197 // get vector to be searched
5198 switch (GetStackType())
5200 case svSingleRef:
5202 PopSingleRef(vsa.nCol1, vsa.nRow1, vsa.nTab1);
5203 vsa.nCol2 = vsa.nCol1;
5204 vsa.nRow2 = vsa.nRow1;
5205 vsa.pMatSrc = nullptr;
5207 break;
5208 case svDoubleRef:
5210 vsa.pMatSrc = nullptr;
5211 SCTAB nTab2 = 0;
5212 PopDoubleRef(vsa.nCol1, vsa.nRow1, vsa.nTab1, vsa.nCol2, vsa.nRow2, nTab2);
5213 if (vsa.nTab1 != nTab2 || (vsa.nCol1 != vsa.nCol2 && vsa.nRow1 != vsa.nRow2))
5215 PushIllegalParameter();
5216 return;
5219 break;
5220 case svMatrix:
5221 case svExternalDoubleRef:
5223 if (GetStackType() == svMatrix)
5224 vsa.pMatSrc = PopMatrix();
5225 else
5226 PopExternalDoubleRef(vsa.pMatSrc);
5228 if (!vsa.pMatSrc)
5230 PushIllegalParameter();
5231 return;
5234 break;
5235 default:
5236 PushIllegalParameter();
5237 return;
5240 // get search value
5241 if (nGlobalError == FormulaError::NONE)
5243 switch (GetRawStackType())
5245 case svMissing:
5246 case svEmptyCell:
5248 vsa.isEmptySearch = true;
5249 vsa.isStringSearch = false;
5250 vsa.sSearchStr = GetString();
5252 break;
5253 case svDouble:
5255 vsa.isStringSearch = false;
5256 vsa.fSearchVal = GetDouble();
5258 break;
5259 case svString:
5261 vsa.isStringSearch = true;
5262 vsa.sSearchStr = GetString();
5264 break;
5265 case svDoubleRef:
5266 case svSingleRef:
5268 ScAddress aAdr;
5269 if (!PopDoubleRefOrSingleRef(aAdr))
5271 PushInt(0);
5272 return;
5274 ScRefCellValue aCell(mrDoc, aAdr);
5275 if (aCell.hasNumeric())
5277 vsa.isStringSearch = false;
5278 vsa.fSearchVal = GetCellValue(aAdr, aCell);
5280 else
5282 vsa.isStringSearch = true;
5283 GetCellString(vsa.sSearchStr, aCell);
5286 break;
5287 case svExternalSingleRef:
5289 ScExternalRefCache::TokenRef pToken;
5290 PopExternalSingleRef(pToken);
5291 if (nGlobalError != FormulaError::NONE)
5293 PushError(nGlobalError);
5294 return;
5296 if (pToken->GetType() == svDouble)
5298 vsa.isStringSearch = false;
5299 vsa.fSearchVal = pToken->GetDouble();
5301 else
5303 vsa.isStringSearch = true;
5304 vsa.sSearchStr = pToken->GetString();
5307 break;
5308 case svExternalDoubleRef:
5309 case svMatrix:
5311 ScMatValType nType = GetDoubleOrStringFromMatrix(
5312 vsa.fSearchVal, vsa.sSearchStr);
5313 vsa.isStringSearch = ScMatrix::IsNonValueType(nType);
5315 break;
5316 default:
5318 PushIllegalParameter();
5319 return;
5323 // execute search
5324 if (SearchVectorForValue(vsa))
5325 PushDouble(vsa.nIndex);
5326 else
5328 if (vsa.isResultNA)
5329 PushNA();
5330 else
5331 return; // error occurred and has already been pushed
5334 else
5335 PushIllegalParameter();
5338 namespace {
5340 bool isCellContentEmpty( const ScRefCellValue& rCell )
5342 switch (rCell.getType())
5344 case CELLTYPE_VALUE:
5345 case CELLTYPE_STRING:
5346 case CELLTYPE_EDIT:
5347 return false;
5348 case CELLTYPE_FORMULA:
5350 // NOTE: Excel treats ="" in a referenced cell as blank in
5351 // COUNTBLANK() but not in ISBLANK(), which is inconsistent.
5352 // COUNTBLANK() tests the (display) result whereas ISBLANK() tests
5353 // the cell content.
5354 // ODFF allows both for COUNTBLANK().
5355 // OOo and LibreOffice prior to 4.4 did not treat ="" as blank in
5356 // COUNTBLANK(), we now do for Excel interoperability.
5357 /* TODO: introduce yet another compatibility option? */
5358 sc::FormulaResultValue aRes = rCell.getFormula()->GetResult();
5359 if (aRes.meType != sc::FormulaResultValue::String)
5360 return false;
5361 if (!aRes.maString.isEmpty())
5362 return false;
5364 break;
5365 default:
5369 return true;
5374 void ScInterpreter::ScCountEmptyCells()
5376 if ( !MustHaveParamCount( GetByte(), 1 ) )
5377 return;
5379 const SCSIZE nMatRows = GetRefListArrayMaxSize(1);
5380 // There's either one RefList and nothing else, or none.
5381 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
5382 sal_uLong nMaxCount = 0, nCount = 0;
5383 switch (GetStackType())
5385 case svSingleRef :
5387 nMaxCount = 1;
5388 ScAddress aAdr;
5389 PopSingleRef( aAdr );
5390 ScRefCellValue aCell(mrDoc, aAdr);
5391 if (!isCellContentEmpty(aCell))
5392 nCount = 1;
5394 break;
5395 case svRefList :
5396 case svDoubleRef :
5398 ScRange aRange;
5399 short nParam = 1;
5400 SCSIZE nRefListArrayPos = 0;
5401 size_t nRefInList = 0;
5402 while (nParam-- > 0)
5404 nRefListArrayPos = nRefInList;
5405 PopDoubleRef( aRange, nParam, nRefInList);
5406 nMaxCount +=
5407 static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) *
5408 static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) *
5409 static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1);
5411 ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags);
5412 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
5414 const ScRefCellValue& rCell = aIter.getRefCellValue();
5415 if (!isCellContentEmpty(rCell))
5416 ++nCount;
5418 if (xResMat)
5420 xResMat->PutDouble( nMaxCount - nCount, 0, nRefListArrayPos);
5421 nMaxCount = nCount = 0;
5425 break;
5426 case svMatrix:
5427 case svExternalSingleRef:
5428 case svExternalDoubleRef:
5430 ScMatrixRef xMat = GetMatrix();
5431 if (!xMat)
5432 SetError( FormulaError::IllegalParameter);
5433 else
5435 SCSIZE nC, nR;
5436 xMat->GetDimensions( nC, nR);
5437 nMaxCount = nC * nR;
5438 // Numbers (implicit), strings and error values, ignore empty
5439 // strings as those if not entered in an inline array are the
5440 // result of a formula, to be par with a reference to formula
5441 // cell as *visual* blank, see isCellContentEmpty() above.
5442 nCount = xMat->Count( true, true, true);
5445 break;
5446 default : SetError(FormulaError::IllegalParameter); break;
5448 if (xResMat)
5449 PushMatrix( xResMat);
5450 else
5451 PushDouble(nMaxCount - nCount);
5454 void ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc )
5456 sal_uInt8 nParamCount = GetByte();
5457 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
5458 return;
5460 SCCOL nCol3 = 0;
5461 SCROW nRow3 = 0;
5462 SCTAB nTab3 = 0;
5464 ScMatrixRef pSumExtraMatrix;
5465 bool bSumExtraRange = (nParamCount == 3);
5466 if (bSumExtraRange)
5468 // Save only the upperleft cell in case of cell range. The geometry
5469 // of the 3rd parameter is taken from the 1st parameter.
5471 switch ( GetStackType() )
5473 case svDoubleRef :
5475 SCCOL nColJunk = 0;
5476 SCROW nRowJunk = 0;
5477 SCTAB nTabJunk = 0;
5478 PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk );
5479 if ( nTabJunk != nTab3 )
5481 PushError( FormulaError::IllegalParameter);
5482 return;
5485 break;
5486 case svSingleRef :
5487 PopSingleRef( nCol3, nRow3, nTab3 );
5488 break;
5489 case svMatrix:
5490 pSumExtraMatrix = PopMatrix();
5491 // nCol3, nRow3, nTab3 remain 0
5492 break;
5493 case svExternalSingleRef:
5495 pSumExtraMatrix = GetNewMat(1,1);
5496 ScExternalRefCache::TokenRef pToken;
5497 PopExternalSingleRef(pToken);
5498 if (nGlobalError != FormulaError::NONE)
5500 PushError( nGlobalError);
5501 return;
5504 if (pToken->GetType() == svDouble)
5505 pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0);
5506 else
5507 pSumExtraMatrix->PutString(pToken->GetString(), 0, 0);
5509 break;
5510 case svExternalDoubleRef:
5511 PopExternalDoubleRef(pSumExtraMatrix);
5512 break;
5513 default:
5514 PushError( FormulaError::IllegalParameter);
5515 return;
5519 svl::SharedString aString;
5520 double fVal = 0.0;
5521 bool bIsString = true;
5522 switch ( GetStackType() )
5524 case svDoubleRef :
5525 case svSingleRef :
5527 ScAddress aAdr;
5528 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5530 PushError( nGlobalError);
5531 return;
5534 ScRefCellValue aCell(mrDoc, aAdr);
5535 switch (aCell.getType())
5537 case CELLTYPE_VALUE :
5538 fVal = GetCellValue(aAdr, aCell);
5539 bIsString = false;
5540 break;
5541 case CELLTYPE_FORMULA :
5542 if (aCell.getFormula()->IsValue())
5544 fVal = GetCellValue(aAdr, aCell);
5545 bIsString = false;
5547 else
5548 GetCellString(aString, aCell);
5549 break;
5550 case CELLTYPE_STRING :
5551 case CELLTYPE_EDIT :
5552 GetCellString(aString, aCell);
5553 break;
5554 default:
5555 fVal = 0.0;
5556 bIsString = false;
5559 break;
5560 case svString:
5561 aString = GetString();
5562 break;
5563 case svMatrix :
5564 case svExternalDoubleRef:
5566 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
5567 bIsString = ScMatrix::IsRealStringType( nType);
5569 break;
5570 case svExternalSingleRef:
5572 ScExternalRefCache::TokenRef pToken;
5573 PopExternalSingleRef(pToken);
5574 if (nGlobalError == FormulaError::NONE)
5576 if (pToken->GetType() == svDouble)
5578 fVal = pToken->GetDouble();
5579 bIsString = false;
5581 else
5582 aString = pToken->GetString();
5585 break;
5586 default:
5588 fVal = GetDouble();
5589 bIsString = false;
5593 KahanSum fSum = 0.0;
5594 double fRes = 0.0;
5595 double fCount = 0.0;
5596 short nParam = 1;
5597 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
5598 // There's either one RefList and nothing else, or none.
5599 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
5600 SCSIZE nRefListArrayPos = 0;
5601 size_t nRefInList = 0;
5602 while (nParam-- > 0)
5604 SCCOL nCol1 = 0;
5605 SCROW nRow1 = 0;
5606 SCTAB nTab1 = 0;
5607 SCCOL nCol2 = 0;
5608 SCROW nRow2 = 0;
5609 SCTAB nTab2 = 0;
5610 ScMatrixRef pQueryMatrix;
5611 switch ( GetStackType() )
5613 case svRefList :
5614 if (bSumExtraRange)
5616 /* TODO: this could resolve if all refs are of the same size */
5617 SetError( FormulaError::IllegalParameter);
5619 else
5621 nRefListArrayPos = nRefInList;
5622 ScRange aRange;
5623 PopDoubleRef( aRange, nParam, nRefInList);
5624 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5626 break;
5627 case svDoubleRef :
5628 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
5629 break;
5630 case svSingleRef :
5631 PopSingleRef( nCol1, nRow1, nTab1 );
5632 nCol2 = nCol1;
5633 nRow2 = nRow1;
5634 nTab2 = nTab1;
5635 break;
5636 case svMatrix:
5637 case svExternalSingleRef:
5638 case svExternalDoubleRef:
5640 pQueryMatrix = GetMatrix();
5641 if (!pQueryMatrix)
5643 PushError( FormulaError::IllegalParameter);
5644 return;
5646 nCol1 = 0;
5647 nRow1 = 0;
5648 nTab1 = 0;
5649 SCSIZE nC, nR;
5650 pQueryMatrix->GetDimensions( nC, nR);
5651 nCol2 = static_cast<SCCOL>(nC - 1);
5652 nRow2 = static_cast<SCROW>(nR - 1);
5653 nTab2 = 0;
5655 break;
5656 default:
5657 SetError( FormulaError::IllegalParameter);
5659 if ( nTab1 != nTab2 )
5661 SetError( FormulaError::IllegalParameter);
5664 if (bSumExtraRange)
5666 // Take the range geometry of the 1st parameter and apply it to
5667 // the 3rd. If parts of the resulting range would point outside
5668 // the sheet, don't complain but silently ignore and simply cut
5669 // them away, this is what Xcl does :-/
5671 // For the cut-away part we also don't need to determine the
5672 // criteria match, so shrink the source range accordingly,
5673 // instead of the result range.
5674 SCCOL nColDelta = nCol2 - nCol1;
5675 SCROW nRowDelta = nRow2 - nRow1;
5676 SCCOL nMaxCol;
5677 SCROW nMaxRow;
5678 if (pSumExtraMatrix)
5680 SCSIZE nC, nR;
5681 pSumExtraMatrix->GetDimensions( nC, nR);
5682 nMaxCol = static_cast<SCCOL>(nC - 1);
5683 nMaxRow = static_cast<SCROW>(nR - 1);
5685 else
5687 nMaxCol = mrDoc.MaxCol();
5688 nMaxRow = mrDoc.MaxRow();
5690 if (nCol3 + nColDelta > nMaxCol)
5692 SCCOL nNewDelta = nMaxCol - nCol3;
5693 nCol2 = nCol1 + nNewDelta;
5696 if (nRow3 + nRowDelta > nMaxRow)
5698 SCROW nNewDelta = nMaxRow - nRow3;
5699 nRow2 = nRow1 + nNewDelta;
5702 else
5704 nCol3 = nCol1;
5705 nRow3 = nRow1;
5706 nTab3 = nTab1;
5709 if (nGlobalError == FormulaError::NONE)
5711 ScQueryParam rParam;
5712 rParam.nRow1 = nRow1;
5713 rParam.nRow2 = nRow2;
5715 ScQueryEntry& rEntry = rParam.GetEntry(0);
5716 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5717 rEntry.bDoQuery = true;
5718 if (!bIsString)
5720 rItem.meType = ScQueryEntry::ByValue;
5721 rItem.mfVal = fVal;
5722 rEntry.eOp = SC_EQUAL;
5724 else
5726 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, &mrContext);
5727 if (rItem.meType == ScQueryEntry::ByString)
5728 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
5730 ScAddress aAdr;
5731 aAdr.SetTab( nTab3 );
5732 rParam.nCol1 = nCol1;
5733 rParam.nCol2 = nCol2;
5734 rEntry.nField = nCol1;
5735 SCCOL nColDiff = nCol3 - nCol1;
5736 SCROW nRowDiff = nRow3 - nRow1;
5737 if (pQueryMatrix)
5739 // Never case-sensitive.
5740 sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
5741 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5742 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
5744 PushIllegalParameter();
5745 return;
5748 if (pSumExtraMatrix)
5750 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5752 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5754 if (pResultMatrix->IsValue( nCol, nRow) &&
5755 pResultMatrix->GetDouble( nCol, nRow))
5757 SCSIZE nC = nCol + nColDiff;
5758 SCSIZE nR = nRow + nRowDiff;
5759 if (pSumExtraMatrix->IsValue( nC, nR))
5761 fVal = pSumExtraMatrix->GetDouble( nC, nR);
5762 ++fCount;
5763 fSum += fVal;
5769 else if (!bSumExtraRange)
5771 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5773 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5775 if (pResultMatrix->IsValue( nCol, nRow) &&
5776 pResultMatrix->GetDouble( nCol, nRow))
5778 if (pQueryMatrix->IsValue( nCol, nRow))
5780 fVal = pQueryMatrix->GetDouble( nCol, nRow);
5781 ++fCount;
5782 fSum += fVal;
5788 else
5790 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5792 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5794 if (pResultMatrix->GetDouble( nCol, nRow))
5796 aAdr.SetCol( nCol + nColDiff);
5797 aAdr.SetRow( nRow + nRowDiff);
5798 ScRefCellValue aCell(mrDoc, aAdr);
5799 if (aCell.hasNumeric())
5801 fVal = GetCellValue(aAdr, aCell);
5802 ++fCount;
5803 fSum += fVal;
5810 else
5812 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false, false);
5813 // Increment Entry.nField in iterator when switching to next column.
5814 aCellIter.SetAdvanceQueryParamEntryField( true );
5815 if ( aCellIter.GetFirst() )
5817 if (pSumExtraMatrix)
5821 SCSIZE nC = aCellIter.GetCol() + nColDiff;
5822 SCSIZE nR = aCellIter.GetRow() + nRowDiff;
5823 if (pSumExtraMatrix->IsValue( nC, nR))
5825 fVal = pSumExtraMatrix->GetDouble( nC, nR);
5826 ++fCount;
5827 fSum += fVal;
5829 } while ( aCellIter.GetNext() );
5831 else
5835 aAdr.SetCol( aCellIter.GetCol() + nColDiff);
5836 aAdr.SetRow( aCellIter.GetRow() + nRowDiff);
5837 ScRefCellValue aCell(mrDoc, aAdr);
5838 if (aCell.hasNumeric())
5840 fVal = GetCellValue(aAdr, aCell);
5841 ++fCount;
5842 fSum += fVal;
5844 } while ( aCellIter.GetNext() );
5849 else
5851 PushError( FormulaError::IllegalParameter);
5852 return;
5855 switch( eFunc )
5857 case ifSUMIF: fRes = fSum.get(); break;
5858 case ifAVERAGEIF: fRes = div( fSum.get(), fCount ); break;
5860 if (xResMat)
5862 if (nGlobalError == FormulaError::NONE)
5863 xResMat->PutDouble( fRes, 0, nRefListArrayPos);
5864 else
5866 xResMat->PutError( nGlobalError, 0, nRefListArrayPos);
5867 nGlobalError = FormulaError::NONE;
5869 fRes = fCount = 0.0;
5870 fSum = 0;
5873 if (xResMat)
5874 PushMatrix( xResMat);
5875 else
5876 PushDouble( fRes);
5879 void ScInterpreter::ScSumIf()
5881 IterateParametersIf( ifSUMIF);
5884 void ScInterpreter::ScAverageIf()
5886 IterateParametersIf( ifAVERAGEIF);
5889 void ScInterpreter::ScCountIf()
5891 if ( !MustHaveParamCount( GetByte(), 2 ) )
5892 return;
5894 svl::SharedString aString;
5895 double fVal = 0.0;
5896 bool bIsString = true;
5897 switch ( GetStackType() )
5899 case svDoubleRef :
5900 case svSingleRef :
5902 ScAddress aAdr;
5903 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5905 PushInt(0);
5906 return ;
5908 ScRefCellValue aCell(mrDoc, aAdr);
5909 switch (aCell.getType())
5911 case CELLTYPE_VALUE :
5912 fVal = GetCellValue(aAdr, aCell);
5913 bIsString = false;
5914 break;
5915 case CELLTYPE_FORMULA :
5916 if (aCell.getFormula()->IsValue())
5918 fVal = GetCellValue(aAdr, aCell);
5919 bIsString = false;
5921 else
5922 GetCellString(aString, aCell);
5923 break;
5924 case CELLTYPE_STRING :
5925 case CELLTYPE_EDIT :
5926 GetCellString(aString, aCell);
5927 break;
5928 default:
5929 fVal = 0.0;
5930 bIsString = false;
5933 break;
5934 case svMatrix:
5935 case svExternalSingleRef:
5936 case svExternalDoubleRef:
5938 ScMatValType nType = GetDoubleOrStringFromMatrix(fVal, aString);
5939 bIsString = ScMatrix::IsRealStringType( nType);
5941 break;
5942 case svString:
5943 aString = GetString();
5944 break;
5945 default:
5947 fVal = GetDouble();
5948 bIsString = false;
5951 double fCount = 0.0;
5952 short nParam = 1;
5953 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
5954 // There's either one RefList and nothing else, or none.
5955 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
5956 SCSIZE nRefListArrayPos = 0;
5957 size_t nRefInList = 0;
5958 while (nParam-- > 0)
5960 SCCOL nCol1 = 0;
5961 SCROW nRow1 = 0;
5962 SCTAB nTab1 = 0;
5963 SCCOL nCol2 = 0;
5964 SCROW nRow2 = 0;
5965 SCTAB nTab2 = 0;
5966 ScMatrixRef pQueryMatrix;
5967 const ScComplexRefData* refData = nullptr;
5968 switch ( GetStackType() )
5970 case svRefList :
5971 nRefListArrayPos = nRefInList;
5972 [[fallthrough]];
5973 case svDoubleRef :
5975 refData = GetStackDoubleRef(nRefInList);
5976 ScRange aRange;
5977 PopDoubleRef( aRange, nParam, nRefInList);
5978 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5980 break;
5981 case svSingleRef :
5982 PopSingleRef( nCol1, nRow1, nTab1 );
5983 nCol2 = nCol1;
5984 nRow2 = nRow1;
5985 nTab2 = nTab1;
5986 break;
5987 case svMatrix:
5988 case svExternalSingleRef:
5989 case svExternalDoubleRef:
5991 pQueryMatrix = GetMatrix();
5992 if (!pQueryMatrix)
5994 PushIllegalParameter();
5995 return;
5997 nCol1 = 0;
5998 nRow1 = 0;
5999 nTab1 = 0;
6000 SCSIZE nC, nR;
6001 pQueryMatrix->GetDimensions( nC, nR);
6002 nCol2 = static_cast<SCCOL>(nC - 1);
6003 nRow2 = static_cast<SCROW>(nR - 1);
6004 nTab2 = 0;
6006 break;
6007 default:
6008 PopError(); // Propagate it further
6009 PushIllegalParameter();
6010 return ;
6012 if ( nTab1 != nTab2 )
6014 PushIllegalParameter();
6015 return;
6017 if (nCol1 > nCol2)
6019 PushIllegalParameter();
6020 return;
6022 if (nGlobalError == FormulaError::NONE)
6024 ScQueryParam rParam;
6025 rParam.nRow1 = nRow1;
6026 rParam.nRow2 = nRow2;
6027 rParam.nTab = nTab1;
6029 ScQueryEntry& rEntry = rParam.GetEntry(0);
6030 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6031 rEntry.bDoQuery = true;
6032 if (!bIsString)
6034 rItem.meType = ScQueryEntry::ByValue;
6035 rItem.mfVal = fVal;
6036 rEntry.eOp = SC_EQUAL;
6038 else
6040 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, &mrContext);
6041 if (rItem.meType == ScQueryEntry::ByString)
6042 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
6044 rParam.nCol1 = nCol1;
6045 rParam.nCol2 = nCol2;
6046 rEntry.nField = nCol1;
6047 if (pQueryMatrix)
6049 // Never case-sensitive.
6050 sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
6051 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
6052 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
6054 PushIllegalParameter();
6055 return;
6058 SCSIZE nSize = pResultMatrix->GetElementCount();
6059 for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex)
6061 if (pResultMatrix->IsValue( nIndex) &&
6062 pResultMatrix->GetDouble( nIndex))
6063 ++fCount;
6066 else
6068 if(ScCountIfCellIteratorSortedCache::CanBeUsed(mrDoc, rParam, nTab1, pMyFormulaCell,
6069 refData, mrContext))
6071 ScCountIfCellIteratorSortedCache aCellIter(mrDoc, mrContext, nTab1, rParam, false, false);
6072 fCount += aCellIter.GetCount();
6074 else
6076 ScCountIfCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false, false);
6077 fCount += aCellIter.GetCount();
6081 else
6083 PushIllegalParameter();
6084 return;
6086 if (xResMat)
6088 xResMat->PutDouble( fCount, 0, nRefListArrayPos);
6089 fCount = 0.0;
6092 if (xResMat)
6093 PushMatrix( xResMat);
6094 else
6095 PushDouble(fCount);
6098 void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIfsResult& rRes ) )
6100 sal_uInt8 nParamCount = GetByte();
6101 sal_uInt8 nQueryCount = nParamCount / 2;
6103 std::vector<sal_uInt8>& vConditions = mrContext.maConditions;
6104 // vConditions is cached, although it is clear'ed after every cell is interpreted,
6105 // if the SUMIFS/COUNTIFS are part of a matrix formula, then that is not enough because
6106 // with a single InterpretTail() call it results in evaluation of all the cells in the
6107 // matrix formula.
6108 vConditions.clear();
6110 // Range-reduce optimization
6111 SCCOL nStartColDiff = 0;
6112 SCCOL nEndColDiff = 0;
6113 SCROW nStartRowDiff = 0;
6114 SCROW nEndRowDiff = 0;
6115 bool bRangeReduce = false;
6116 ScRange aMainRange;
6118 bool bHasDoubleRefCriteriaRanges = true;
6119 // Do not attempt main-range reduce if any of the criteria-ranges are not double-refs.
6120 // For COUNTIFS queries it's possible to range-reduce too, if the query is not supposed
6121 // to match empty cells (will be checked and undone later if needed), so simply treat
6122 // the first criteria range as the main range for purposes of detecting if this can be done.
6123 for (sal_uInt16 nParamIdx = 2; nParamIdx < nParamCount; nParamIdx += 2 )
6125 const formula::FormulaToken* pCriteriaRangeToken = pStack[ sp-nParamIdx ];
6126 if (pCriteriaRangeToken->GetType() != svDoubleRef )
6128 bHasDoubleRefCriteriaRanges = false;
6129 break;
6133 // Probe the main range token, and try if we can shrink the range without altering results.
6134 const formula::FormulaToken* pMainRangeToken = pStack[ sp-nParamCount ];
6135 if (pMainRangeToken->GetType() == svDoubleRef && bHasDoubleRefCriteriaRanges)
6137 const ScComplexRefData* pRefData = pMainRangeToken->GetDoubleRef();
6138 if (!pRefData->IsDeleted())
6140 DoubleRefToRange( *pRefData, aMainRange);
6141 if (aMainRange.aStart.Tab() == aMainRange.aEnd.Tab())
6143 // Shrink the range to actual data content.
6144 ScRange aSubRange = aMainRange;
6145 mrDoc.GetDataAreaSubrange(aSubRange);
6146 nStartColDiff = aSubRange.aStart.Col() - aMainRange.aStart.Col();
6147 nStartRowDiff = aSubRange.aStart.Row() - aMainRange.aStart.Row();
6148 nEndColDiff = aSubRange.aEnd.Col() - aMainRange.aEnd.Col();
6149 nEndRowDiff = aSubRange.aEnd.Row() - aMainRange.aEnd.Row();
6150 bRangeReduce = nStartColDiff || nStartRowDiff || nEndColDiff || nEndRowDiff;
6155 double fVal = 0.0;
6156 SCCOL nDimensionCols = 0;
6157 SCROW nDimensionRows = 0;
6158 const SCSIZE nRefArrayRows = GetRefListArrayMaxSize( nParamCount);
6159 std::vector<std::vector<sal_uInt8>> vRefArrayConditions;
6161 while (nParamCount > 1 && nGlobalError == FormulaError::NONE)
6163 // take criteria
6164 svl::SharedString aString;
6165 fVal = 0.0;
6166 bool bIsString = true;
6167 switch ( GetStackType() )
6169 case svDoubleRef :
6170 case svSingleRef :
6172 ScAddress aAdr;
6173 if ( !PopDoubleRefOrSingleRef( aAdr ) )
6175 PushError( nGlobalError);
6176 return;
6179 ScRefCellValue aCell(mrDoc, aAdr);
6180 switch (aCell.getType())
6182 case CELLTYPE_VALUE :
6183 fVal = GetCellValue(aAdr, aCell);
6184 bIsString = false;
6185 break;
6186 case CELLTYPE_FORMULA :
6187 if (aCell.getFormula()->IsValue())
6189 fVal = GetCellValue(aAdr, aCell);
6190 bIsString = false;
6192 else
6193 GetCellString(aString, aCell);
6194 break;
6195 case CELLTYPE_STRING :
6196 case CELLTYPE_EDIT :
6197 GetCellString(aString, aCell);
6198 break;
6199 default:
6200 fVal = 0.0;
6201 bIsString = false;
6204 break;
6205 case svString:
6206 aString = GetString();
6207 break;
6208 case svMatrix :
6209 case svExternalDoubleRef:
6211 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
6212 bIsString = ScMatrix::IsRealStringType( nType);
6214 break;
6215 case svExternalSingleRef:
6217 ScExternalRefCache::TokenRef pToken;
6218 PopExternalSingleRef(pToken);
6219 if (nGlobalError == FormulaError::NONE)
6221 if (pToken->GetType() == svDouble)
6223 fVal = pToken->GetDouble();
6224 bIsString = false;
6226 else
6227 aString = pToken->GetString();
6230 break;
6231 default:
6233 fVal = GetDouble();
6234 bIsString = false;
6238 if (nGlobalError != FormulaError::NONE)
6240 PushError( nGlobalError);
6241 return; // and bail out, no need to evaluate other arguments
6244 // take range
6245 short nParam = nParamCount;
6246 size_t nRefInList = 0;
6247 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
6248 SCCOL nCol1 = 0;
6249 SCROW nRow1 = 0;
6250 SCTAB nTab1 = 0;
6251 SCCOL nCol2 = 0;
6252 SCROW nRow2 = 0;
6253 SCTAB nTab2 = 0;
6254 ScMatrixRef pQueryMatrix;
6255 while (nParam-- == nParamCount)
6257 const ScComplexRefData* refData = nullptr;
6258 switch ( GetStackType() )
6260 case svRefList :
6262 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
6263 if (p && p->IsArrayResult())
6265 if (nRefInList == 0)
6267 if (vRefArrayConditions.empty())
6268 vRefArrayConditions.resize( nRefArrayRows);
6269 if (!vConditions.empty())
6271 // Similar to other reference list array
6272 // handling, add/op the current value to
6273 // all array positions.
6274 for (auto & rVec : vRefArrayConditions)
6276 if (rVec.empty())
6277 rVec = vConditions;
6278 else
6280 assert(rVec.size() == vConditions.size()); // see dimensions below
6281 for (size_t i=0, n = rVec.size(); i < n; ++i)
6283 rVec[i] += vConditions[i];
6287 // Reset condition results.
6288 std::for_each( vConditions.begin(), vConditions.end(),
6289 [](sal_uInt8 & r){ r = 0.0; } );
6292 nRefArrayPos = nRefInList;
6294 refData = GetStackDoubleRef(nRefInList);
6295 ScRange aRange;
6296 PopDoubleRef( aRange, nParam, nRefInList);
6297 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
6299 break;
6300 case svDoubleRef :
6301 refData = GetStackDoubleRef();
6302 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
6303 break;
6304 case svSingleRef :
6305 PopSingleRef( nCol1, nRow1, nTab1 );
6306 nCol2 = nCol1;
6307 nRow2 = nRow1;
6308 nTab2 = nTab1;
6309 break;
6310 case svMatrix:
6311 case svExternalSingleRef:
6312 case svExternalDoubleRef:
6314 pQueryMatrix = GetMatrix();
6315 if (!pQueryMatrix)
6317 PushError( FormulaError::IllegalParameter);
6318 return;
6320 nCol1 = 0;
6321 nRow1 = 0;
6322 nTab1 = 0;
6323 SCSIZE nC, nR;
6324 pQueryMatrix->GetDimensions( nC, nR);
6325 nCol2 = static_cast<SCCOL>(nC - 1);
6326 nRow2 = static_cast<SCROW>(nR - 1);
6327 nTab2 = 0;
6329 break;
6330 default:
6331 PushError( FormulaError::IllegalParameter);
6332 return;
6334 if ( nTab1 != nTab2 )
6336 PushError( FormulaError::IllegalArgument);
6337 return;
6340 ScQueryParam rParam;
6341 ScQueryEntry& rEntry = rParam.GetEntry(0);
6342 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6343 rEntry.bDoQuery = true;
6344 if (!bIsString)
6346 rItem.meType = ScQueryEntry::ByValue;
6347 rItem.mfVal = fVal;
6348 rEntry.eOp = SC_EQUAL;
6350 else
6352 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, &mrContext);
6353 if (rItem.meType == ScQueryEntry::ByString)
6354 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
6357 // Undo bRangeReduce if asked to match empty cells for COUNTIFS (which should be rare).
6358 assert(rEntry.GetQueryItems().size() == 1);
6359 const bool isCountIfs = (nParamCount % 2) == 0;
6360 if(isCountIfs && (rEntry.IsQueryByEmpty() || rItem.mbMatchEmpty) && bRangeReduce)
6362 bRangeReduce = false;
6363 // All criteria ranges are svDoubleRef's, so only vConditions needs adjusting.
6364 assert(vRefArrayConditions.empty());
6365 if(!vConditions.empty())
6367 std::vector<sal_uInt8> newConditions;
6368 SCCOL newDimensionCols = nCol2 - nCol1 + 1;
6369 SCROW newDimensionRows = nRow2 - nRow1 + 1;
6370 newConditions.reserve( newDimensionCols * newDimensionRows );
6371 SCCOL col = nCol1;
6372 for(; col < nCol1 + nStartColDiff; ++col)
6373 newConditions.insert( newConditions.end(), newDimensionRows, 0 );
6374 for(; col <= nCol2 - nStartColDiff; ++col)
6376 newConditions.insert( newConditions.end(), nStartRowDiff, 0 );
6377 SCCOL oldCol = col - ( nCol1 + nStartColDiff );
6378 size_t nIndex = oldCol * nDimensionRows;
6379 if (nIndex < vConditions.size())
6381 auto it = vConditions.begin() + nIndex;
6382 newConditions.insert( newConditions.end(), it, it + nDimensionRows );
6384 else
6385 newConditions.insert( newConditions.end(), nDimensionRows, 0 );
6386 newConditions.insert( newConditions.end(), -nEndRowDiff, 0 );
6388 for(; col <= nCol2; ++col)
6389 newConditions.insert( newConditions.end(), newDimensionRows, 0 );
6390 assert( newConditions.size() == o3tl::make_unsigned( newDimensionCols * newDimensionRows ));
6391 vConditions = std::move( newConditions );
6392 nDimensionCols = newDimensionCols;
6393 nDimensionRows = newDimensionRows;
6397 if (bRangeReduce)
6399 // All reference ranges must be of the same size as the main range.
6400 if( aMainRange.aEnd.Col() - aMainRange.aStart.Col() != nCol2 - nCol1
6401 || aMainRange.aEnd.Row() - aMainRange.aStart.Row() != nRow2 - nRow1)
6403 PushError ( FormulaError::IllegalArgument);
6404 return;
6406 nCol1 += nStartColDiff;
6407 nRow1 += nStartRowDiff;
6409 nCol2 += nEndColDiff;
6410 nRow2 += nEndRowDiff;
6413 // All reference ranges must be of same dimension and size.
6414 if (!nDimensionCols)
6415 nDimensionCols = nCol2 - nCol1 + 1;
6416 if (!nDimensionRows)
6417 nDimensionRows = nRow2 - nRow1 + 1;
6418 if ((nDimensionCols != (nCol2 - nCol1 + 1)) || (nDimensionRows != (nRow2 - nRow1 + 1)))
6420 PushError ( FormulaError::IllegalArgument);
6421 return;
6424 // recalculate matrix values
6425 if (nGlobalError != FormulaError::NONE)
6427 PushError( nGlobalError);
6428 return;
6431 // initialize temporary result matrix
6432 if (vConditions.empty())
6433 vConditions.resize( nDimensionCols * nDimensionRows, 0);
6435 rParam.nRow1 = nRow1;
6436 rParam.nRow2 = nRow2;
6437 rParam.nCol1 = nCol1;
6438 rParam.nCol2 = nCol2;
6439 rEntry.nField = nCol1;
6440 SCCOL nColDiff = -nCol1;
6441 SCROW nRowDiff = -nRow1;
6442 if (pQueryMatrix)
6444 // Never case-sensitive.
6445 sc::CompareOptions aOptions(mrDoc, rEntry, rParam.eSearchType);
6446 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
6447 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
6449 PushError( FormulaError::IllegalParameter);
6450 return;
6453 // result matrix is filled with boolean values.
6454 std::vector<double> aResValues;
6455 pResultMatrix->GetDoubleArray(aResValues);
6456 if (vConditions.size() != aResValues.size())
6458 PushError( FormulaError::IllegalParameter);
6459 return;
6462 std::vector<double>::const_iterator itThisRes = aResValues.begin();
6463 for (auto& rCondition : vConditions)
6465 rCondition += *itThisRes;
6466 ++itThisRes;
6469 else
6471 if( ScQueryCellIteratorSortedCache::CanBeUsed( mrDoc, rParam, nTab1, pMyFormulaCell,
6472 refData, mrContext ))
6474 ScQueryCellIteratorSortedCache aCellIter(mrDoc, mrContext, nTab1, rParam, false, false);
6475 // Increment Entry.nField in iterator when switching to next column.
6476 aCellIter.SetAdvanceQueryParamEntryField( true );
6477 if ( aCellIter.GetFirst() )
6481 size_t nC = aCellIter.GetCol() + nColDiff;
6482 size_t nR = aCellIter.GetRow() + nRowDiff;
6483 ++vConditions[nC * nDimensionRows + nR];
6484 } while ( aCellIter.GetNext() );
6487 else
6489 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false, false);
6490 // Increment Entry.nField in iterator when switching to next column.
6491 aCellIter.SetAdvanceQueryParamEntryField( true );
6492 if ( aCellIter.GetFirst() )
6496 size_t nC = aCellIter.GetCol() + nColDiff;
6497 size_t nR = aCellIter.GetRow() + nRowDiff;
6498 ++vConditions[nC * nDimensionRows + nR];
6499 } while ( aCellIter.GetNext() );
6503 if (nRefArrayPos != std::numeric_limits<size_t>::max())
6505 // Apply condition result to reference list array result position.
6506 std::vector<sal_uInt8>& rVec = vRefArrayConditions[nRefArrayPos];
6507 if (rVec.empty())
6508 rVec = vConditions;
6509 else
6511 assert(rVec.size() == vConditions.size()); // see dimensions above
6512 for (size_t i=0, n = rVec.size(); i < n; ++i)
6514 rVec[i] += vConditions[i];
6517 // Reset conditions vector.
6518 // When leaving an svRefList this has to be emptied not set to
6519 // 0.0 because it's checked when entering an svRefList.
6520 if (nRefInList == 0)
6521 std::vector<sal_uInt8>().swap( vConditions);
6522 else
6523 std::for_each( vConditions.begin(), vConditions.end(), [](sal_uInt8 & r){ r = 0; } );
6526 nParamCount -= 2;
6529 if (!vRefArrayConditions.empty() && !vConditions.empty())
6531 // Add/op the last current value to all array positions.
6532 for (auto & rVec : vRefArrayConditions)
6534 if (rVec.empty())
6535 rVec = vConditions;
6536 else
6538 assert(rVec.size() == vConditions.size()); // see dimensions above
6539 for (size_t i=0, n = rVec.size(); i < n; ++i)
6541 rVec[i] += vConditions[i];
6547 if (nGlobalError != FormulaError::NONE)
6549 PushError( nGlobalError);
6550 return; // bail out
6553 sc::ParamIfsResult aRes;
6554 ScMatrixRef xResMat;
6556 // main range - only for AVERAGEIFS, SUMIFS, MINIFS and MAXIFS
6557 if (nParamCount == 1)
6559 short nParam = nParamCount;
6560 size_t nRefInList = 0;
6561 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
6562 bool bRefArrayMain = false;
6563 while (nParam-- == nParamCount)
6565 SCCOL nMainCol1 = 0;
6566 SCROW nMainRow1 = 0;
6567 SCTAB nMainTab1 = 0;
6568 SCCOL nMainCol2 = 0;
6569 SCROW nMainRow2 = 0;
6570 SCTAB nMainTab2 = 0;
6571 ScMatrixRef pMainMatrix;
6572 switch ( GetStackType() )
6574 case svRefList :
6576 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
6577 if (p && p->IsArrayResult())
6579 if (vRefArrayConditions.empty())
6581 // Replicate conditions if there wasn't a
6582 // reference list array for criteria
6583 // evaluation.
6584 vRefArrayConditions.resize( nRefArrayRows);
6585 for (auto & rVec : vRefArrayConditions)
6587 rVec = vConditions;
6591 bRefArrayMain = true;
6592 nRefArrayPos = nRefInList;
6594 ScRange aRange;
6595 PopDoubleRef( aRange, nParam, nRefInList);
6596 aRange.GetVars( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2);
6598 break;
6599 case svDoubleRef :
6600 PopDoubleRef( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2 );
6601 break;
6602 case svSingleRef :
6603 PopSingleRef( nMainCol1, nMainRow1, nMainTab1 );
6604 nMainCol2 = nMainCol1;
6605 nMainRow2 = nMainRow1;
6606 nMainTab2 = nMainTab1;
6607 break;
6608 case svMatrix:
6609 case svExternalSingleRef:
6610 case svExternalDoubleRef:
6612 pMainMatrix = GetMatrix();
6613 if (!pMainMatrix)
6615 PushError( FormulaError::IllegalParameter);
6616 return;
6618 nMainCol1 = 0;
6619 nMainRow1 = 0;
6620 nMainTab1 = 0;
6621 SCSIZE nC, nR;
6622 pMainMatrix->GetDimensions( nC, nR);
6623 nMainCol2 = static_cast<SCCOL>(nC - 1);
6624 nMainRow2 = static_cast<SCROW>(nR - 1);
6625 nMainTab2 = 0;
6627 break;
6628 // Treat a scalar value as 1x1 matrix.
6629 case svDouble:
6630 pMainMatrix = GetNewMat(1,1);
6631 nMainCol1 = nMainCol2 = 0;
6632 nMainRow1 = nMainRow2 = 0;
6633 nMainTab1 = nMainTab2 = 0;
6634 pMainMatrix->PutDouble( GetDouble(), 0, 0);
6635 break;
6636 case svString:
6637 pMainMatrix = GetNewMat(1,1);
6638 nMainCol1 = nMainCol2 = 0;
6639 nMainRow1 = nMainRow2 = 0;
6640 nMainTab1 = nMainTab2 = 0;
6641 pMainMatrix->PutString( GetString(), 0, 0);
6642 break;
6643 default:
6644 PopError();
6645 PushError( FormulaError::IllegalParameter);
6646 return;
6648 if ( nMainTab1 != nMainTab2 )
6650 PushError( FormulaError::IllegalArgument);
6651 return;
6654 if (bRangeReduce)
6656 nMainCol1 += nStartColDiff;
6657 nMainRow1 += nStartRowDiff;
6659 nMainCol2 += nEndColDiff;
6660 nMainRow2 += nEndRowDiff;
6663 // All reference ranges must be of same dimension and size.
6664 if ((nDimensionCols != (nMainCol2 - nMainCol1 + 1)) || (nDimensionRows != (nMainRow2 - nMainRow1 + 1)))
6666 PushError ( FormulaError::IllegalArgument);
6667 return;
6670 if (nGlobalError != FormulaError::NONE)
6672 PushError( nGlobalError);
6673 return; // bail out
6676 // end-result calculation
6678 // This gets weird... if conditions were calculated using a
6679 // reference list array but the main calculation range is not a
6680 // reference list array, then the conditions of the array are
6681 // applied to the main range each in turn to form the array result.
6683 size_t nRefArrayMainPos = (bRefArrayMain ? nRefArrayPos :
6684 (vRefArrayConditions.empty() ? std::numeric_limits<size_t>::max() : 0));
6685 const bool bAppliedArray = (!bRefArrayMain && nRefArrayMainPos == 0);
6687 if (nRefArrayMainPos == 0)
6688 xResMat = GetNewMat( 1, nRefArrayRows, /*bEmpty*/true );
6690 if (pMainMatrix)
6692 std::vector<double> aMainValues;
6693 pMainMatrix->GetDoubleArray(aMainValues, false); // Map empty values to NaN's.
6697 if (nRefArrayMainPos < vRefArrayConditions.size())
6698 vConditions = vRefArrayConditions[nRefArrayMainPos];
6700 if (vConditions.size() != aMainValues.size())
6702 PushError( FormulaError::IllegalArgument);
6703 return;
6706 std::vector<sal_uInt8>::const_iterator itRes = vConditions.begin(), itResEnd = vConditions.end();
6707 std::vector<double>::const_iterator itMain = aMainValues.begin();
6708 for (; itRes != itResEnd; ++itRes, ++itMain)
6710 if (*itRes != nQueryCount)
6711 continue;
6713 fVal = *itMain;
6714 if (GetDoubleErrorValue(fVal) == FormulaError::ElementNaN)
6715 continue;
6717 ++aRes.mfCount;
6718 aRes.mfSum += fVal;
6719 if ( aRes.mfMin > fVal )
6720 aRes.mfMin = fVal;
6721 if ( aRes.mfMax < fVal )
6722 aRes.mfMax = fVal;
6724 if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
6726 xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
6727 aRes = sc::ParamIfsResult();
6730 while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
6732 else
6734 ScAddress aAdr;
6735 aAdr.SetTab( nMainTab1 );
6738 if (nRefArrayMainPos < vRefArrayConditions.size())
6739 vConditions = vRefArrayConditions[nRefArrayMainPos];
6741 SAL_WARN_IF(nDimensionCols && nDimensionRows && vConditions.empty(), "sc", "ScInterpreter::IterateParametersIfs vConditions is empty");
6742 if (!vConditions.empty())
6744 std::vector<sal_uInt8>::const_iterator itRes = vConditions.begin();
6745 for (SCCOL nCol = 0; nCol < nDimensionCols; ++nCol)
6747 for (SCROW nRow = 0; nRow < nDimensionRows; ++nRow, ++itRes)
6749 if (*itRes == nQueryCount)
6751 aAdr.SetCol( nCol + nMainCol1);
6752 aAdr.SetRow( nRow + nMainRow1);
6753 ScRefCellValue aCell(mrDoc, aAdr);
6754 if (aCell.hasNumeric())
6756 fVal = GetCellValue(aAdr, aCell);
6757 ++aRes.mfCount;
6758 aRes.mfSum += fVal;
6759 if ( aRes.mfMin > fVal )
6760 aRes.mfMin = fVal;
6761 if ( aRes.mfMax < fVal )
6762 aRes.mfMax = fVal;
6768 if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
6770 xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
6771 aRes = sc::ParamIfsResult();
6774 while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
6778 else
6780 // COUNTIFS only.
6781 if (vRefArrayConditions.empty())
6783 // The code below is this but optimized for most elements not matching.
6784 // for (auto const & rCond : vConditions)
6785 // if (rCond == nQueryCount)
6786 // ++aRes.mfCount;
6787 static_assert(sizeof(vConditions[0]) == 1);
6788 const sal_uInt8* pos = vConditions.data();
6789 const sal_uInt8* end = pos + vConditions.size();
6790 for(;;)
6792 pos = static_cast< const sal_uInt8* >( memchr( pos, nQueryCount, end - pos ));
6793 if( pos == nullptr )
6794 break;
6795 ++aRes.mfCount;
6796 ++pos;
6799 else
6801 xResMat = GetNewMat( 1, nRefArrayRows, /*bEmpty*/true );
6802 for (size_t i=0, n = vRefArrayConditions.size(); i < n; ++i)
6804 double fCount = 0.0;
6805 for (auto const & rCond : vRefArrayConditions[i])
6807 if (rCond == nQueryCount)
6808 ++fCount;
6810 xResMat->PutDouble( fCount, 0, i);
6815 if (xResMat)
6816 PushMatrix( xResMat);
6817 else
6818 PushDouble( ResultFunc( aRes));
6821 void ScInterpreter::ScSumIfs()
6823 // ScMutationGuard aShouldFail(pDok, ScMutationGuardFlags::CORE);
6824 sal_uInt8 nParamCount = GetByte();
6826 if (nParamCount < 3 || (nParamCount % 2 != 1))
6828 PushError( FormulaError::ParameterExpected);
6829 return;
6832 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6834 return rRes.mfSum.get();
6836 IterateParametersIfs(ResultFunc);
6839 void ScInterpreter::ScAverageIfs()
6841 sal_uInt8 nParamCount = GetByte();
6843 if (nParamCount < 3 || (nParamCount % 2 != 1))
6845 PushError( FormulaError::ParameterExpected);
6846 return;
6849 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6851 return sc::div( rRes.mfSum.get(), rRes.mfCount);
6853 IterateParametersIfs(ResultFunc);
6856 void ScInterpreter::ScCountIfs()
6858 sal_uInt8 nParamCount = GetByte();
6860 if (nParamCount < 2 || (nParamCount % 2 != 0))
6862 PushError( FormulaError::ParameterExpected);
6863 return;
6866 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6868 return rRes.mfCount;
6870 IterateParametersIfs(ResultFunc);
6873 void ScInterpreter::ScMinIfs_MS()
6875 sal_uInt8 nParamCount = GetByte();
6877 if (nParamCount < 3 || (nParamCount % 2 != 1))
6879 PushError( FormulaError::ParameterExpected);
6880 return;
6883 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6885 return (rRes.mfMin < std::numeric_limits<double>::max()) ? rRes.mfMin : 0.0;
6887 IterateParametersIfs(ResultFunc);
6891 void ScInterpreter::ScMaxIfs_MS()
6893 sal_uInt8 nParamCount = GetByte();
6895 if (nParamCount < 3 || (nParamCount % 2 != 1))
6897 PushError( FormulaError::ParameterExpected);
6898 return;
6901 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6903 return (rRes.mfMax > std::numeric_limits<double>::lowest()) ? rRes.mfMax : 0.0;
6905 IterateParametersIfs(ResultFunc);
6908 void ScInterpreter::ScLookup()
6910 sal_uInt8 nParamCount = GetByte();
6911 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
6912 return ;
6914 ScMatrixRef pDataMat = nullptr, pResMat = nullptr;
6915 SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0;
6916 SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0;
6917 SCTAB nTab1 = 0, nResTab = 0;
6918 SCSIZE nLenMajor = 0; // length of major direction
6919 bool bVertical = true; // whether to lookup vertically or horizontally
6921 // The third parameter, result array, double, string and reference.
6922 double fResVal = 0.0;
6923 svl::SharedString aResStr;
6924 StackVar eResArrayType = svUnknown;
6926 if (nParamCount == 3)
6928 eResArrayType = GetStackType();
6929 switch (eResArrayType)
6931 case svDoubleRef:
6933 SCTAB nTabJunk;
6934 PopDoubleRef(nResCol1, nResRow1, nResTab,
6935 nResCol2, nResRow2, nTabJunk);
6936 if (nResTab != nTabJunk ||
6937 ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0))
6939 // The result array must be a vector.
6940 PushIllegalParameter();
6941 return;
6944 break;
6945 case svSingleRef:
6946 PopSingleRef( nResCol1, nResRow1, nResTab);
6947 nResCol2 = nResCol1;
6948 nResRow2 = nResRow1;
6949 break;
6950 case svMatrix:
6951 case svExternalSingleRef:
6952 case svExternalDoubleRef:
6954 pResMat = GetMatrix();
6955 if (!pResMat)
6957 PushIllegalParameter();
6958 return;
6960 SCSIZE nC, nR;
6961 pResMat->GetDimensions(nC, nR);
6962 if (nC != 1 && nR != 1)
6964 // Result matrix must be a vector.
6965 PushIllegalParameter();
6966 return;
6969 break;
6970 case svDouble:
6971 fResVal = GetDouble();
6972 break;
6973 case svString:
6974 aResStr = GetString();
6975 break;
6976 default:
6977 PushIllegalParameter();
6978 return;
6982 // For double, string and single reference.
6983 double fDataVal = 0.0;
6984 svl::SharedString aDataStr;
6985 ScAddress aDataAdr;
6986 bool bValueData = false;
6988 // Get the data-result range and also determine whether this is vertical
6989 // lookup or horizontal lookup.
6991 StackVar eDataArrayType = GetStackType();
6992 switch (eDataArrayType)
6994 case svDoubleRef:
6996 SCTAB nTabJunk;
6997 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk);
6998 if (nTab1 != nTabJunk)
7000 PushIllegalParameter();
7001 return;
7003 bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1);
7004 nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1;
7006 break;
7007 case svMatrix:
7008 case svExternalSingleRef:
7009 case svExternalDoubleRef:
7011 pDataMat = GetMatrix();
7012 if (!pDataMat)
7014 PushIllegalParameter();
7015 return;
7018 SCSIZE nC, nR;
7019 pDataMat->GetDimensions(nC, nR);
7020 bVertical = (nR >= nC);
7021 nLenMajor = bVertical ? nR : nC;
7023 break;
7024 case svDouble:
7026 fDataVal = GetDouble();
7027 bValueData = true;
7029 break;
7030 case svString:
7032 aDataStr = GetString();
7034 break;
7035 case svSingleRef:
7037 PopSingleRef( aDataAdr );
7038 ScRefCellValue aCell(mrDoc, aDataAdr);
7039 if (aCell.hasEmptyValue())
7041 // Empty cells aren't found anywhere, bail out early.
7042 SetError( FormulaError::NotAvailable);
7044 else if (aCell.hasNumeric())
7046 fDataVal = GetCellValue(aDataAdr, aCell);
7047 bValueData = true;
7049 else
7050 GetCellString(aDataStr, aCell);
7052 break;
7053 default:
7054 SetError( FormulaError::IllegalParameter);
7057 if (nGlobalError != FormulaError::NONE)
7059 PushError( nGlobalError);
7060 return;
7063 // Get the lookup value.
7065 ScQueryParam aParam;
7066 ScQueryEntry& rEntry = aParam.GetEntry(0);
7067 if ( !FillEntry(rEntry) )
7068 return;
7070 if ( eDataArrayType == svDouble || eDataArrayType == svString ||
7071 eDataArrayType == svSingleRef )
7073 // Delta position for a single value is always 0.
7075 // Found if data <= query, but not if query is string and found data is
7076 // numeric or vice versa. This is how Excel does it but doesn't
7077 // document it.
7079 bool bFound = false;
7080 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7082 if ( bValueData )
7084 if (rItem.meType == ScQueryEntry::ByString)
7085 bFound = false;
7086 else
7087 bFound = (fDataVal <= rItem.mfVal);
7089 else
7091 if (rItem.meType != ScQueryEntry::ByString)
7092 bFound = false;
7093 else
7094 bFound = (ScGlobal::GetCollator().compareString(aDataStr.getString(), rItem.maString.getString()) <= 0);
7097 if (!bFound)
7099 PushNA();
7100 return;
7103 if (pResMat)
7105 if (pResMat->IsValue( 0, 0 ))
7106 PushDouble(pResMat->GetDouble( 0, 0 ));
7107 else
7108 PushString(pResMat->GetString(0, 0));
7110 else if (nParamCount == 3)
7112 switch (eResArrayType)
7114 case svDouble:
7115 PushDouble( fResVal );
7116 break;
7117 case svString:
7118 PushString( aResStr );
7119 break;
7120 case svDoubleRef:
7121 case svSingleRef:
7122 PushCellResultToken( true, ScAddress( nResCol1, nResRow1, nResTab), nullptr, nullptr);
7123 break;
7124 default:
7125 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, single value data");
7126 PushIllegalParameter();
7129 else
7131 switch (eDataArrayType)
7133 case svDouble:
7134 PushDouble( fDataVal );
7135 break;
7136 case svString:
7137 PushString( aDataStr );
7138 break;
7139 case svSingleRef:
7140 PushCellResultToken( true, aDataAdr, nullptr, nullptr);
7141 break;
7142 default:
7143 assert(!"ScInterpreter::ScLookup: unhandled eDataArrayType, single value data");
7144 PushIllegalParameter();
7147 return;
7150 // Now, perform the search to compute the delta position (nDelta).
7152 if (pDataMat)
7154 // Data array is given as a matrix.
7155 rEntry.bDoQuery = true;
7156 rEntry.eOp = SC_LESS_EQUAL;
7157 bool bFound = false;
7159 SCSIZE nC, nR;
7160 pDataMat->GetDimensions(nC, nR);
7162 // Do not propagate errors from matrix while copying to vector.
7163 pDataMat->SetErrorInterpreter( nullptr);
7165 // Excel has an undocumented behaviour in that it seems to internally
7166 // sort an interim array (i.e. error values specifically #DIV/0! are
7167 // sorted to the end) or ignore error values that makes these "get last
7168 // non-empty" searches work, e.g. =LOOKUP(2,1/NOT(ISBLANK(A:A)),A:A)
7169 // see tdf#117016
7170 // Instead of sorting a million entries of which mostly only a bunch of
7171 // rows are filled and moving error values to the end which most are
7172 // already anyway, assume the matrix to be sorted except error values
7173 // and omit the coded DoubleError values.
7174 // Do this only for a numeric matrix (that includes errors coded as
7175 // doubles), which covers the case in question.
7176 /* TODO: it's unclear whether this really matches Excel behaviour in
7177 * all constellations or if there are cases that include unsorted error
7178 * values and thus yield arbitrary binary search results or something
7179 * different or whether there are cases where error values are also
7180 * omitted from mixed numeric/string arrays or if it's not an interim
7181 * matrix but a cell range reference instead. */
7182 const bool bOmitErrorValues = (eDataArrayType == svMatrix && pDataMat->IsNumeric());
7184 // In case of non-vector matrix, only search the first row or column.
7185 ScMatrixRef pDataMat2;
7186 std::vector<SCCOLROW> vIndex;
7187 if (bOmitErrorValues)
7189 std::vector<double> vArray;
7190 VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
7191 const SCSIZE nElements = aMatAcc.GetElementCount();
7192 for (SCSIZE i=0; i < nElements; ++i)
7194 const double fVal = aMatAcc.GetDouble(i);
7195 if (std::isfinite(fVal))
7197 vArray.push_back(fVal);
7198 vIndex.push_back(i);
7201 if (vArray.empty())
7203 PushNA();
7204 return;
7206 const size_t nElems = vArray.size();
7207 if (nElems == nElements)
7209 // No error value omitted, use as is.
7210 pDataMat2 = pDataMat;
7211 std::vector<SCCOLROW>().swap( vIndex);
7213 else
7215 nLenMajor = nElems;
7216 if (bVertical)
7218 ScMatrixRef pTempMat = GetNewMat( 1, nElems, /*bEmpty*/true );
7219 pTempMat->PutDoubleVector( vArray, 0, 0);
7220 pDataMat2 = pTempMat;
7222 else
7224 ScMatrixRef pTempMat = GetNewMat( nElems, 1, /*bEmpty*/true );
7225 for (size_t i=0; i < nElems; ++i)
7226 pTempMat->PutDouble( vArray[i], i, 0);
7227 pDataMat2 = pTempMat;
7231 else
7233 // Just use as is with the VectorMatrixAccessor.
7234 pDataMat2 = pDataMat;
7237 // Do not propagate errors from matrix while searching.
7238 pDataMat2->SetErrorInterpreter( nullptr);
7240 VectorMatrixAccessor aMatAcc2(*pDataMat2, bVertical);
7242 // binary search for non-equality mode (the source data is
7243 // assumed to be sorted in ascending order).
7245 SCCOLROW nDelta = -1;
7246 bool bMatchWholeCell = mrDoc.GetDocOptions().IsMatchWholeCell();
7248 SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0;
7249 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
7251 SCSIZE nMid = nFirst + nLen/2;
7252 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc2, aParam, rEntry, bMatchWholeCell );
7253 if (nCmp == 0)
7255 // exact match. find the last item with the same value.
7256 lcl_GetLastMatch( nMid, aMatAcc2, nLenMajor);
7257 nDelta = nMid;
7258 bFound = true;
7259 break;
7262 if (nLen == 1) // first and last items are next to each other.
7264 nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1;
7265 // If already the 1st item is greater there's nothing found.
7266 bFound = (nDelta >= 0);
7267 break;
7270 if (nCmp < 0)
7271 nFirst = nMid;
7272 else
7273 nLast = nMid;
7276 if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item
7278 sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, aMatAcc2, aParam, rEntry, bMatchWholeCell );
7279 if (nCmp <= 0)
7281 // either the last item is an exact match or the real
7282 // hit is beyond the last item.
7283 nDelta += 1;
7284 bFound = true;
7287 else if (nDelta > 0) // valid hit must be 2nd item or higher
7289 // non-exact match
7290 bFound = true;
7293 // With 0-9 < A-Z, if query is numeric and data found is string, or
7294 // vice versa, the (yet another undocumented) Excel behavior is to
7295 // return #N/A instead.
7297 if (bFound)
7299 if (!vIndex.empty())
7300 nDelta = vIndex[nDelta];
7302 VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
7303 SCCOLROW i = nDelta;
7304 SCSIZE n = aMatAcc.GetElementCount();
7305 if (o3tl::make_unsigned(i) >= n)
7306 i = static_cast<SCCOLROW>(n);
7307 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
7308 if (bByString == aMatAcc.IsValue(i))
7309 bFound = false;
7312 if (!bFound)
7314 PushNA();
7315 return;
7318 // Now that we've found the delta, push the result back to the cell.
7320 if (pResMat)
7322 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
7323 // Result array is matrix.
7324 // Note this does not replicate the other dimension.
7325 if (o3tl::make_unsigned(nDelta) >= aResMatAcc.GetElementCount())
7327 PushNA();
7328 return;
7330 if (aResMatAcc.IsValue(nDelta))
7331 PushDouble(aResMatAcc.GetDouble(nDelta));
7332 else
7333 PushString(aResMatAcc.GetString(nDelta));
7335 else if (nParamCount == 3)
7337 /* TODO: the entire switch is a copy of the cell range search
7338 * result, factor out. */
7339 switch (eResArrayType)
7341 case svDoubleRef:
7342 case svSingleRef:
7344 // Use the result array vector. Note that the result array is assumed
7345 // to be a vector (i.e. 1-dimensional array).
7347 ScAddress aAdr;
7348 aAdr.SetTab(nResTab);
7349 bool bResVertical = (nResRow2 - nResRow1) > 0;
7350 if (bResVertical)
7352 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
7353 if (nTempRow > mrDoc.MaxRow())
7355 PushDouble(0);
7356 return;
7358 aAdr.SetCol(nResCol1);
7359 aAdr.SetRow(nTempRow);
7361 else
7363 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
7364 if (nTempCol > mrDoc.MaxCol())
7366 PushDouble(0);
7367 return;
7369 aAdr.SetCol(nTempCol);
7370 aAdr.SetRow(nResRow1);
7372 PushCellResultToken( true, aAdr, nullptr, nullptr);
7374 break;
7375 case svDouble:
7376 case svString:
7378 if (nDelta != 0)
7379 PushNA();
7380 else
7382 switch (eResArrayType)
7384 case svDouble:
7385 PushDouble( fResVal );
7386 break;
7387 case svString:
7388 PushString( aResStr );
7389 break;
7390 default:
7391 ; // nothing
7395 break;
7396 default:
7397 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, array search");
7398 PushIllegalParameter();
7401 else
7403 // No result array. Use the data array to get the final value from.
7404 // Propagate errors from matrix again.
7405 pDataMat->SetErrorInterpreter( this);
7406 if (bVertical)
7408 if (pDataMat->IsValue(nC-1, nDelta))
7409 PushDouble(pDataMat->GetDouble(nC-1, nDelta));
7410 else
7411 PushString(pDataMat->GetString(nC-1, nDelta));
7413 else
7415 if (pDataMat->IsValue(nDelta, nR-1))
7416 PushDouble(pDataMat->GetDouble(nDelta, nR-1));
7417 else
7418 PushString(pDataMat->GetString(nDelta, nR-1));
7422 return;
7425 // Perform cell range search.
7427 aParam.nCol1 = nCol1;
7428 aParam.nRow1 = nRow1;
7429 aParam.nCol2 = bVertical ? nCol1 : nCol2;
7430 aParam.nRow2 = bVertical ? nRow2 : nRow1;
7431 aParam.bByRow = bVertical;
7433 rEntry.bDoQuery = true;
7434 rEntry.eOp = SC_LESS_EQUAL;
7435 rEntry.nField = nCol1;
7436 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7437 if (rItem.meType == ScQueryEntry::ByString)
7438 aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
7440 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, aParam, false, false);
7441 SCCOL nC;
7442 SCROW nR;
7443 // Advance Entry.nField in iterator upon switching columns if
7444 // lookup in row.
7445 aCellIter.SetAdvanceQueryParamEntryField(!bVertical);
7446 if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) )
7448 PushNA();
7449 return;
7452 SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1);
7454 if (pResMat)
7456 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
7457 // Use the matrix result array.
7458 // Note this does not replicate the other dimension.
7459 if (o3tl::make_unsigned(nDelta) >= aResMatAcc.GetElementCount())
7461 PushNA();
7462 return;
7464 if (aResMatAcc.IsValue(nDelta))
7465 PushDouble(aResMatAcc.GetDouble(nDelta));
7466 else
7467 PushString(aResMatAcc.GetString(nDelta));
7469 else if (nParamCount == 3)
7471 /* TODO: the entire switch is a copy of the array search result, factor
7472 * out. */
7473 switch (eResArrayType)
7475 case svDoubleRef:
7476 case svSingleRef:
7478 // Use the result array vector. Note that the result array is assumed
7479 // to be a vector (i.e. 1-dimensional array).
7481 ScAddress aAdr;
7482 aAdr.SetTab(nResTab);
7483 bool bResVertical = (nResRow2 - nResRow1) > 0;
7484 if (bResVertical)
7486 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
7487 if (nTempRow > mrDoc.MaxRow())
7489 PushDouble(0);
7490 return;
7492 aAdr.SetCol(nResCol1);
7493 aAdr.SetRow(nTempRow);
7495 else
7497 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
7498 if (nTempCol > mrDoc.MaxCol())
7500 PushDouble(0);
7501 return;
7503 aAdr.SetCol(nTempCol);
7504 aAdr.SetRow(nResRow1);
7506 PushCellResultToken( true, aAdr, nullptr, nullptr);
7508 break;
7509 case svDouble:
7510 case svString:
7512 if (nDelta != 0)
7513 PushNA();
7514 else
7516 switch (eResArrayType)
7518 case svDouble:
7519 PushDouble( fResVal );
7520 break;
7521 case svString:
7522 PushString( aResStr );
7523 break;
7524 default:
7525 ; // nothing
7529 break;
7530 default:
7531 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, range search");
7532 PushIllegalParameter();
7535 else
7537 // Regardless of whether or not the result array exists, the last
7538 // array is always used as the "result" array.
7540 ScAddress aAdr;
7541 aAdr.SetTab(nTab1);
7542 if (bVertical)
7544 SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta);
7545 if (nTempRow > mrDoc.MaxRow())
7547 PushDouble(0);
7548 return;
7550 aAdr.SetCol(nCol2);
7551 aAdr.SetRow(nTempRow);
7553 else
7555 SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta);
7556 if (nTempCol > mrDoc.MaxCol())
7558 PushDouble(0);
7559 return;
7561 aAdr.SetCol(nTempCol);
7562 aAdr.SetRow(nRow2);
7564 PushCellResultToken(true, aAdr, nullptr, nullptr);
7568 void ScInterpreter::ScHLookup()
7570 CalculateLookup(true);
7573 void ScInterpreter::CalculateLookup(bool bHLookup)
7575 sal_uInt8 nParamCount = GetByte();
7576 if (!MustHaveParamCount(nParamCount, 3, 4))
7577 return;
7579 // Optional 4th argument to declare whether or not the range is sorted.
7580 bool bSorted = true;
7581 if (nParamCount == 4)
7582 bSorted = GetBool();
7584 // Index of column to search.
7585 double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0;
7587 ScMatrixRef pMat = nullptr;
7588 SCSIZE nC = 0, nR = 0;
7589 SCCOL nCol1 = 0;
7590 SCROW nRow1 = 0;
7591 SCTAB nTab1 = 0;
7592 SCCOL nCol2 = 0;
7593 SCROW nRow2 = 0;
7594 const ScComplexRefData* refData = nullptr;
7595 StackVar eType = GetStackType();
7596 if (eType == svDoubleRef)
7598 refData = GetStackDoubleRef(0);
7599 SCTAB nTab2;
7600 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7601 if (nTab1 != nTab2)
7603 PushIllegalParameter();
7604 return;
7607 else if (eType == svSingleRef)
7609 PopSingleRef(nCol1, nRow1, nTab1);
7610 nCol2 = nCol1;
7611 nRow2 = nRow1;
7613 else if (eType == svMatrix || eType == svExternalDoubleRef || eType == svExternalSingleRef)
7615 pMat = GetMatrix();
7617 if (pMat)
7618 pMat->GetDimensions(nC, nR);
7619 else
7621 PushIllegalParameter();
7622 return;
7625 else
7627 PushIllegalParameter();
7628 return;
7631 if ( fIndex < 0.0 || (bHLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) )
7633 PushIllegalArgument();
7634 return;
7637 SCROW nZIndex = static_cast<SCROW>(fIndex);
7638 SCCOL nSpIndex = static_cast<SCCOL>(fIndex);
7640 if (!pMat)
7642 nZIndex += nRow1; // value row
7643 nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 ); // value column
7646 if (nGlobalError != FormulaError::NONE)
7648 PushIllegalParameter();
7649 return;
7652 ScQueryParam aParam;
7653 aParam.nCol1 = nCol1;
7654 aParam.nRow1 = nRow1;
7655 if ( bHLookup )
7657 aParam.nCol2 = nCol2;
7658 aParam.nRow2 = nRow1; // search only in the first row
7659 aParam.bByRow = false;
7661 else
7663 aParam.nCol2 = nCol1; // search only in the first column
7664 aParam.nRow2 = nRow2;
7665 aParam.nTab = nTab1;
7668 ScQueryEntry& rEntry = aParam.GetEntry(0);
7669 rEntry.bDoQuery = true;
7670 if ( bSorted )
7671 rEntry.eOp = SC_LESS_EQUAL;
7672 if ( !FillEntry(rEntry) )
7673 return;
7675 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7676 svl::SharedString aParamStr;
7677 if (rItem.meType == ScQueryEntry::ByString)
7679 aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
7680 aParamStr = rItem.maString;
7683 if (pMat)
7685 SCSIZE nMatCount = bHLookup ? nC : nR;
7686 SCSIZE nDelta = SCSIZE_MAX;
7687 if (rItem.meType == ScQueryEntry::ByString)
7689 //!!!!!!!
7690 //TODO: enable regex on matrix strings
7691 //!!!!!!!
7692 if ( bSorted )
7694 CollatorWrapper& rCollator = ScGlobal::GetCollator();
7695 for (SCSIZE i = 0; i < nMatCount; i++)
7697 if (bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i))
7699 sal_Int32 nRes =
7700 rCollator.compareString(
7701 bHLookup ? pMat->GetString(i,0).getString() : pMat->GetString(0,i).getString(), aParamStr.getString());
7702 if (nRes <= 0)
7703 nDelta = i;
7704 else if (i>0) // #i2168# ignore first mismatch
7705 i = nMatCount+1;
7707 else
7708 nDelta = i;
7711 else
7713 if (bHLookup)
7715 for (SCSIZE i = 0; i < nMatCount; i++)
7717 if (pMat->IsStringOrEmpty(i, 0))
7719 if (pMat->GetString(i,0).getDataIgnoreCase() == aParamStr.getDataIgnoreCase())
7721 nDelta = i;
7722 i = nMatCount + 1;
7727 else
7729 nDelta = pMat->MatchStringInColumns(aParamStr, 0, 0);
7733 else
7735 if ( bSorted )
7737 // #i2168# ignore strings
7738 for (SCSIZE i = 0; i < nMatCount; i++)
7740 if (!(bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i)))
7742 if ((bHLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rItem.mfVal)
7743 nDelta = i;
7744 else
7745 i = nMatCount+1;
7749 else
7751 if (bHLookup)
7753 for (SCSIZE i = 0; i < nMatCount; i++)
7755 if (! pMat->IsStringOrEmpty(i, 0) )
7757 if ( pMat->GetDouble(i,0) == rItem.mfVal)
7759 nDelta = i;
7760 i = nMatCount + 1;
7765 else
7767 nDelta = pMat->MatchDoubleInColumns(rItem.mfVal, 0, 0);
7771 if ( nDelta != SCSIZE_MAX )
7773 SCSIZE nX = static_cast<SCSIZE>(nSpIndex);
7774 SCSIZE nY = nDelta;
7775 SCSIZE nXs = 0;
7776 SCSIZE nYs = nY;
7777 if ( bHLookup )
7779 nX = nDelta;
7780 nY = static_cast<SCSIZE>(nZIndex);
7781 nXs = nX;
7782 nYs = 0;
7784 assert( nX < nC && nY < nR );
7785 if (!(rItem.meType == ScQueryEntry::ByString && pMat->IsValue( nXs, nYs)))
7787 if (pMat->IsStringOrEmpty( nX, nY))
7788 PushString(pMat->GetString( nX, nY).getString());
7789 else
7790 PushDouble(pMat->GetDouble( nX, nY));
7792 else
7793 PushNA();
7794 return;
7796 else
7797 PushNA();
7799 else
7801 // not a matrix
7802 rEntry.nField = nCol1;
7803 bool bFound = false;
7804 SCCOL nCol = 0;
7805 SCROW nRow = 0;
7806 if ( bSorted )
7807 rEntry.eOp = SC_LESS_EQUAL;
7808 if ( bHLookup )
7810 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, aParam, false, false);
7811 // advance Entry.nField in Iterator upon switching columns
7812 aCellIter.SetAdvanceQueryParamEntryField( true );
7813 if ( bSorted )
7815 SCROW nRow1_temp;
7816 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp );
7818 else if ( aCellIter.GetFirst() )
7820 bFound = true;
7821 nCol = aCellIter.GetCol();
7823 nRow = nZIndex;
7825 else
7827 ScAddress aResultPos( nCol1, nRow1, nTab1);
7828 bFound = LookupQueryWithCache( aResultPos, aParam, refData, 0, SC_OPCODE_V_LOOKUP );
7829 nRow = aResultPos.Row();
7830 nCol = nSpIndex;
7833 if ( bFound )
7835 ScAddress aAdr( nCol, nRow, nTab1 );
7836 PushCellResultToken( true, aAdr, nullptr, nullptr);
7838 else
7839 PushNA();
7843 bool ScInterpreter::FillEntry(ScQueryEntry& rEntry)
7845 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7846 switch ( GetStackType() )
7848 case svDouble:
7850 rItem.meType = ScQueryEntry::ByValue;
7851 rItem.mfVal = GetDouble();
7853 break;
7854 case svString:
7856 rItem.meType = ScQueryEntry::ByString;
7857 rItem.maString = GetString();
7859 break;
7860 case svDoubleRef :
7861 case svSingleRef :
7863 ScAddress aAdr;
7864 if ( !PopDoubleRefOrSingleRef( aAdr ) )
7866 PushInt(0);
7867 return false;
7869 ScRefCellValue aCell(mrDoc, aAdr);
7870 if (aCell.hasNumeric())
7872 rItem.meType = ScQueryEntry::ByValue;
7873 rItem.mfVal = GetCellValue(aAdr, aCell);
7875 else
7877 GetCellString(rItem.maString, aCell);
7878 rItem.meType = ScQueryEntry::ByString;
7881 break;
7882 case svExternalDoubleRef:
7883 case svExternalSingleRef:
7884 case svMatrix:
7886 svl::SharedString aStr;
7887 const ScMatValType nType = GetDoubleOrStringFromMatrix(rItem.mfVal, aStr);
7888 rItem.maString = aStr;
7889 rItem.meType = ScMatrix::IsNonValueType(nType) ?
7890 ScQueryEntry::ByString : ScQueryEntry::ByValue;
7892 break;
7893 default:
7895 PushIllegalParameter();
7896 return false;
7898 } // switch ( GetStackType() )
7899 return true;
7902 void ScInterpreter::ScVLookup()
7904 CalculateLookup(false);
7907 void ScInterpreter::ScXLookup()
7909 /* TODO
7910 -use VectorSearchArguments and SearchVectorForValue() with ScLookup, ScHLookup and ScVLookup
7911 as well to reduce redundant code, can de done later with lots of other MATCH/LOOKUP related code
7912 that can be unified
7913 -BinarySearch not supported for columns (horizontal search), now just use linear mode in this case
7914 -improve efficiency of code
7916 sal_uInt8 nParamCount = GetByte();
7917 if ( !MustHaveParamCount( nParamCount, 3, 6 ) )
7918 return;
7920 VectorSearchArguments vsa;
7921 vsa.nSearchOpCode = SC_OPCODE_X_LOOKUP;
7923 if ( nParamCount == 6 )
7925 sal_Int16 k = GetInt16();
7926 if ( k >= -2 && k <= 2 && k != 0 )
7927 vsa.eSearchMode = static_cast<SearchMode>(k);
7928 else
7930 PushIllegalParameter();
7931 return;
7934 else
7935 vsa.eSearchMode = searchfwd;
7937 if ( nParamCount >= 5 )
7939 sal_Int16 k = GetInt16();
7940 if ( k >= -1 && k <= 3 )
7941 vsa.eMatchMode = static_cast<MatchMode>(k);
7942 else
7944 PushIllegalParameter();
7945 return;
7948 else
7949 vsa.eMatchMode = exactorNA;
7951 // Optional 4th argument to set return values if not found (default is #N/A)
7952 formula::FormulaConstTokenRef xNotFound;
7953 FormulaError nFirstMatchError = FormulaError::NONE;
7954 if ( nParamCount >= 4 && GetStackType() != svEmptyCell )
7956 xNotFound = PopToken();
7957 nFirstMatchError = xNotFound->GetError();
7958 nGlobalError = FormulaError::NONE; // propagate only for match or active result path
7961 // 3rd argument is return value array
7962 ScMatrixRef prMat = nullptr;
7963 SCCOL nSearchCol1 = 0;
7964 SCROW nSearchRow1 = 0;
7965 SCTAB nSearchTab1 = 0;
7966 SCCOL nSearchCol2 = 0;
7967 SCROW nSearchRow2 = 0;
7968 SCTAB nSearchTab2 = 0;
7969 SCSIZE nrC = 0, nrR = 0;
7971 switch ( GetStackType() )
7973 case svSingleRef :
7974 PopSingleRef(nSearchCol1, nSearchRow1, nSearchTab1);
7975 nSearchCol2 = nSearchCol1;
7976 nSearchRow2 = nSearchRow1;
7977 nrC = nSearchCol2 - nSearchCol1 + 1;
7978 nrR = nSearchRow2 - nSearchRow1 + 1;
7979 break;
7980 case svDoubleRef:
7982 PopDoubleRef(nSearchCol1, nSearchRow1, nSearchTab1, nSearchCol2, nSearchRow2, nSearchTab2);
7983 if (nSearchTab1 != nSearchTab2)
7985 PushIllegalParameter();
7986 return;
7988 nrC = nSearchCol2 - nSearchCol1 + 1;
7989 nrR = nSearchRow2 - nSearchRow1 + 1;
7991 break;
7992 case svMatrix :
7993 case svExternalDoubleRef :
7995 if (GetStackType() == svMatrix)
7996 prMat = PopMatrix();
7997 else
7998 PopExternalDoubleRef(prMat);
8000 if (!prMat)
8002 PushIllegalParameter();
8003 return;
8005 prMat->GetDimensions(nrC, nrR);
8007 break;
8009 default :
8010 PushIllegalParameter();
8011 return;
8014 // 2nd argument is vector to be searched
8015 SCSIZE nsC = 0, nsR = 0;
8016 switch ( GetStackType() )
8018 case svSingleRef:
8019 vsa.pMatSrc = nullptr;
8020 PopSingleRef( vsa.nCol1, vsa.nRow1, vsa.nTab1);
8021 vsa.nCol2 = vsa.nCol1;
8022 vsa.nRow2 = vsa.nRow1;
8023 nsC = vsa.nCol2 - vsa.nCol1 + 1;
8024 nsR = vsa.nRow2 - vsa.nRow1 + 1;
8025 break;
8026 case svDoubleRef:
8028 vsa.pMatSrc = nullptr;
8029 SCTAB nTab2 = 0;
8030 PopDoubleRef(vsa.nCol1, vsa.nRow1, vsa.nTab1, vsa.nCol2, vsa.nRow2, nTab2);
8031 if (vsa.nTab1 != nTab2 || (vsa.nCol1 != vsa.nCol2 && vsa.nRow1 != vsa.nRow2))
8033 PushIllegalParameter();
8034 return;
8036 nsC = vsa.nCol2 - vsa.nCol1 + 1;
8037 nsR = vsa.nRow2 - vsa.nRow1 + 1;
8039 break;
8040 case svMatrix:
8041 case svExternalDoubleRef:
8043 if (GetStackType() == svMatrix)
8044 vsa.pMatSrc = PopMatrix();
8045 else
8046 PopExternalDoubleRef(vsa.pMatSrc);
8048 if (!vsa.pMatSrc)
8050 PushIllegalParameter();
8051 return;
8053 vsa.pMatSrc->GetDimensions( nsC, nsR);
8055 break;
8057 default:
8058 PushIllegalParameter();
8059 return;
8061 if ( ( nsR >= nsC && nsR != nrR ) || ( nsR < nsC && nsC != nrC ) )
8063 // search matrix must have same number of elements as result matrix in search direction
8064 PushIllegalParameter();
8065 return;
8068 // 1st argument is search value
8069 if (nGlobalError == FormulaError::NONE)
8071 switch ( GetRawStackType() )
8073 case svMissing:
8074 case svEmptyCell:
8076 vsa.isEmptySearch = true;
8077 vsa.isStringSearch = false;
8078 vsa.sSearchStr = GetString();
8080 break;
8082 case svDouble:
8084 vsa.isStringSearch = false;
8085 vsa.fSearchVal = GetDouble();
8087 break;
8089 case svString:
8091 vsa.isStringSearch = true;
8092 vsa.sSearchStr = GetString();
8094 break;
8096 case svDoubleRef :
8097 case svSingleRef :
8099 ScAddress aAdr;
8100 if ( !PopDoubleRefOrSingleRef( aAdr ) )
8102 PushInt(0);
8103 return ;
8105 ScRefCellValue aCell(mrDoc, aAdr);
8106 if (aCell.hasNumeric())
8108 vsa.isStringSearch = false;
8109 vsa.fSearchVal = GetCellValue(aAdr, aCell);
8111 else
8113 vsa.isStringSearch = true;
8114 GetCellString(vsa.sSearchStr, aCell);
8117 break;
8119 case svExternalSingleRef:
8121 ScExternalRefCache::TokenRef pToken;
8122 PopExternalSingleRef(pToken);
8123 if (nGlobalError != FormulaError::NONE)
8125 PushError( nGlobalError);
8126 return;
8128 if (pToken->GetType() == svDouble)
8130 vsa.isStringSearch = false;
8131 vsa.fSearchVal = pToken->GetDouble();
8133 else
8135 vsa.isStringSearch = true;
8136 vsa.sSearchStr = pToken->GetString();
8139 break;
8141 case svExternalDoubleRef:
8142 case svMatrix :
8144 ScMatValType nType = GetDoubleOrStringFromMatrix(
8145 vsa.fSearchVal, vsa.sSearchStr);
8146 vsa.isStringSearch = ScMatrix::IsNonValueType(nType);
8148 break;
8150 default:
8152 PushIllegalParameter();
8153 return;
8158 // start search
8159 if ( SearchVectorForValue( vsa ) )
8161 // found, output result
8162 assert( vsa.bVLookup ? ( o3tl::make_unsigned(vsa.nIndex) < nrR ) :
8163 ( o3tl::make_unsigned(vsa.nIndex) < nrC ) );
8164 SCSIZE nX;
8165 SCSIZE nY;
8166 SCSIZE nResCols;
8167 SCSIZE nResRows;
8168 if ( vsa.bVLookup )
8170 nX = static_cast<SCSIZE>(0);
8171 nY = vsa.nIndex;
8172 nResCols = nrC;
8173 nResRows = 1;
8175 else
8177 nX = vsa.nIndex;
8178 nY = static_cast<SCSIZE>(0);
8179 nResCols = 1;
8180 nResRows = nrR;
8182 // if result has more than one row or column push double ref or matrix, else push single ref
8183 if ( nResCols > 1 || nResRows > 1 )
8185 if (prMat)
8187 // result is matrix, make / fill matrix with output and push that
8188 ScMatrixRef pResMat = GetNewMat(nResCols, nResRows, /*bEmpty*/true);
8189 if (pResMat)
8191 for (SCSIZE i = 0; i < nResCols; i++)
8193 for (SCSIZE j = 0; j < nResRows; j++)
8195 SCSIZE ri;
8196 SCSIZE rj;
8197 if (vsa.bVLookup)
8199 ri = nX + i;
8200 rj = nY;
8202 else
8204 ri = nX;
8205 rj = nY + j;
8207 if (prMat->IsEmptyCell(ri, rj))
8208 pResMat->PutEmpty(i, j);
8209 else if (prMat->IsStringOrEmpty(ri, rj))
8210 pResMat->PutString(prMat->GetString(ri, rj), i, j);
8211 else
8212 pResMat->PutDouble(prMat->GetDouble(ri, rj), i, j);
8215 PushMatrix(pResMat);
8217 else
8219 PushIllegalParameter();
8220 return;
8223 else
8225 // result is a double ref
8226 PushDoubleRef(nSearchCol1 + nX, nSearchRow1 + nY, nSearchTab1,
8227 nSearchCol1 + (nResCols - 1) + nX, nSearchRow1 + (nResRows - 1) + nY, nSearchTab1);
8230 else
8232 // result is a single ref
8233 PushSingleRef(nSearchCol1 + nX, nSearchRow1 + nY, nSearchTab1);
8236 else
8238 if ( vsa.isResultNA )
8240 if ( xNotFound && ( xNotFound->GetType() != svMissing ) )
8242 nGlobalError = nFirstMatchError;
8243 PushTokenRef(xNotFound);
8245 else
8246 PushNA();
8251 void ScInterpreter::ScFilter()
8253 sal_uInt8 nParamCount = GetByte();
8254 if (!MustHaveParamCount(nParamCount, 2, 3))
8255 return;
8257 // Optional 3th argument to set the value to return if all values
8258 // in the included array are empty (filter returns nothing)
8259 formula::FormulaConstTokenRef xNotFound;
8260 if (nParamCount == 3 && GetStackType() != svEmptyCell)
8261 xNotFound = PopToken();
8263 SCCOL nCondResultColEnd = 0;
8264 SCROW nCondResultRowEnd = 0;
8265 ScMatrixRef pCondResultMatrix = nullptr;
8266 std::vector<double> aResValues;
8267 size_t nMatch = 0;
8268 // take 2nd argument criteria bool array
8269 switch ( GetStackType() )
8271 case svMatrix :
8272 case svExternalDoubleRef:
8273 case svExternalSingleRef:
8274 case svDoubleRef:
8275 case svSingleRef:
8277 pCondResultMatrix = GetMatrix();
8278 if (!pCondResultMatrix)
8280 PushError(FormulaError::IllegalParameter);
8281 return;
8283 SCSIZE nC, nR;
8284 pCondResultMatrix->GetDimensions(nC, nR);
8285 nCondResultColEnd = static_cast<SCCOL>(nC - 1);
8286 nCondResultRowEnd = static_cast<SCROW>(nR - 1);
8288 // only 1 dimension of filtering allowed (also in excel)
8289 if (nCondResultColEnd > 0 && nCondResultRowEnd > 0)
8291 PushError(FormulaError::NoValue);
8292 return;
8295 // result matrix is filled with boolean values.
8296 pCondResultMatrix->GetDoubleArray(aResValues);
8298 FormulaError nError = FormulaError::NONE;
8299 auto matchNum = [&nMatch, &nError](double i) {
8300 nError = GetDoubleErrorValue(i);
8301 if (nError != FormulaError::NONE)
8303 return true;
8305 else
8307 if (i > 0)
8308 nMatch++;
8309 return false;
8313 if (auto it = std::find_if(aResValues.begin(), aResValues.end(), matchNum); it != aResValues.end())
8315 PushError(nError);
8316 return;
8319 break;
8321 default:
8323 PushIllegalParameter();
8324 return;
8328 // bail out, no need to evaluate other arguments
8329 if (nGlobalError != FormulaError::NONE)
8331 PushError(nGlobalError);
8332 return;
8335 SCCOL nQueryCol1 = 0;
8336 SCROW nQueryRow1 = 0;
8337 SCCOL nQueryCol2 = 0;
8338 SCROW nQueryRow2 = 0;
8339 ScMatrixRef pQueryMatrix = nullptr;
8340 // take 1st argument range
8341 switch ( GetStackType() )
8343 case svSingleRef:
8344 case svDoubleRef:
8345 case svMatrix:
8346 case svExternalSingleRef:
8347 case svExternalDoubleRef:
8349 pQueryMatrix = GetMatrix();
8350 if (!pQueryMatrix)
8352 PushError( FormulaError::IllegalParameter);
8353 return;
8355 SCSIZE nC, nR;
8356 pQueryMatrix->GetDimensions( nC, nR);
8357 nQueryCol2 = static_cast<SCCOL>(nC - 1);
8358 nQueryRow2 = static_cast<SCROW>(nR - 1);
8360 break;
8361 default:
8362 PushError( FormulaError::IllegalParameter);
8363 return;
8366 // bail out, no need to set a matrix if we have no result
8367 if (!nMatch)
8369 if (xNotFound && (xNotFound->GetType() != svMissing))
8370 PushTokenRef(xNotFound);
8371 else
8372 PushError(FormulaError::NestedArray);
8373 return;
8376 SCSIZE nResPos = 0;
8377 ScMatrixRef pResMat = nullptr;
8378 if (nQueryCol2 == nCondResultColEnd && nCondResultColEnd > 0)
8380 pResMat = GetNewMat(nMatch, nQueryRow2 + 1 , /*bEmpty*/true);
8381 for (SCROW iR = nQueryRow1; iR <= nQueryRow2; iR++)
8383 for (size_t iC = 0; iC < aResValues.size(); iC++)
8385 if (aResValues[iC] > 0)
8387 if (pQueryMatrix->IsEmptyCell(iC, iR))
8388 pResMat->PutEmptyTrans(nResPos++);
8389 else if (pQueryMatrix->IsStringOrEmpty(iC, iR))
8390 pResMat->PutStringTrans(pQueryMatrix->GetString(iC, iR), nResPos++);
8391 else
8392 pResMat->PutDoubleTrans(pQueryMatrix->GetDouble(iC, iR), nResPos++);
8397 else if (nQueryRow2 == nCondResultRowEnd && nCondResultRowEnd > 0)
8399 pResMat = GetNewMat(nQueryCol2 + 1, nMatch, /*bEmpty*/true);
8400 for (SCCOL iC = nQueryCol1; iC <= nQueryCol2; iC++)
8402 for (size_t iR = 0; iR < aResValues.size(); iR++)
8404 if (aResValues[iR] > 0)
8406 if (pQueryMatrix->IsEmptyCell(iC, iR))
8407 pResMat->PutEmpty(nResPos++);
8408 else if (pQueryMatrix->IsStringOrEmpty(iC, iR))
8409 pResMat->PutString(pQueryMatrix->GetString(iC, iR), nResPos++);
8410 else
8411 pResMat->PutDouble(pQueryMatrix->GetDouble(iC, iR), nResPos++);
8416 else
8418 PushError(FormulaError::IllegalParameter);
8419 return;
8422 if (pResMat)
8423 PushMatrix(pResMat);
8424 else
8425 PushError(FormulaError::NestedArray);
8428 void ScInterpreter::ScSort()
8430 sal_uInt8 nParamCount = GetByte();
8431 if (!MustHaveParamCount(nParamCount, 1, 4))
8432 return;
8434 // Create sort data
8435 ScSortParam aSortData;
8437 // 4th argument optional
8438 aSortData.bByRow = true; // default: By_Col = false --> bByRow = true
8439 if (nParamCount == 4)
8440 aSortData.bByRow = !GetBool();
8442 // 3rd argument optional
8443 std::vector<double> aSortOrderValues{ 1.0 }; // default: 1 = asc, -1 = desc
8444 if (nParamCount >= 3)
8446 bool bMissing = IsMissing();
8447 ScMatrixRef pSortOrder = GetMatrix();
8448 if (!bMissing)
8450 aSortOrderValues.clear();
8451 pSortOrder->GetDoubleArray(aSortOrderValues);
8452 for (const double& sortOrder : aSortOrderValues)
8454 if (sortOrder != 1.0 && sortOrder != -1.0)
8456 PushIllegalParameter();
8457 return;
8463 // 2nd argument optional
8464 std::vector<double> aSortIndexValues{ 0.0 }; // default: first column or row
8465 if (nParamCount >= 2)
8467 bool bMissing = IsMissing();
8468 ScMatrixRef pSortIndex = GetMatrix();
8469 if (!bMissing)
8471 aSortIndexValues.clear();
8472 pSortIndex->GetDoubleArray(aSortIndexValues);
8473 for (double& sortIndex : aSortIndexValues)
8475 if (sortIndex < 1)
8477 PushIllegalParameter();
8478 return;
8480 else
8481 sortIndex--;
8486 if (aSortIndexValues.size() != aSortOrderValues.size() && aSortOrderValues.size() > 1)
8488 PushIllegalParameter();
8489 return;
8492 // 1st argument is vector to be sorted
8493 SCSIZE nsC = 0, nsR = 0;
8494 ScMatrixRef pMatSrc = nullptr;
8495 switch ( GetStackType() )
8497 case svSingleRef:
8498 PopSingleRef(aSortData.nCol1, aSortData.nRow1, aSortData.nSourceTab);
8499 aSortData.nCol2 = aSortData.nCol1;
8500 aSortData.nRow2 = aSortData.nRow1;
8501 nsC = aSortData.nCol2 - aSortData.nCol1 + 1;
8502 nsR = aSortData.nRow2 - aSortData.nRow1 + 1;
8503 break;
8504 case svDoubleRef:
8506 SCTAB nTab2 = 0;
8507 PopDoubleRef(aSortData.nCol1, aSortData.nRow1, aSortData.nSourceTab, aSortData.nCol2, aSortData.nRow2, nTab2);
8508 if (aSortData.nSourceTab != nTab2)
8510 PushIllegalParameter();
8511 return;
8513 nsC = aSortData.nCol2 - aSortData.nCol1 + 1;
8514 nsR = aSortData.nRow2 - aSortData.nRow1 + 1;
8516 break;
8517 case svMatrix:
8518 case svExternalSingleRef:
8519 case svExternalDoubleRef:
8521 pMatSrc = GetMatrix();
8522 if (!pMatSrc)
8524 PushIllegalParameter();
8525 return;
8527 pMatSrc->GetDimensions(nsC, nsR);
8528 aSortData.nCol2 = nsC - 1; // aSortData.nCol1 = 0
8529 aSortData.nRow2 = nsR - 1; // aSortData.nRow1 = 0
8531 break;
8533 default:
8534 PushIllegalParameter();
8535 return;
8538 aSortData.maKeyState.resize(aSortIndexValues.size());
8539 for (size_t i = 0; i < aSortIndexValues.size(); i++)
8541 SCSIZE fIndex = static_cast<SCSIZE>(double_to_int32(aSortIndexValues[i]));
8542 if ((aSortData.bByRow && fIndex + 1 > nsC) || (!aSortData.bByRow && fIndex + 1 > nsR))
8544 PushIllegalParameter();
8545 return;
8548 aSortData.maKeyState[i].bDoSort = true;
8549 aSortData.maKeyState[i].nField = (aSortData.bByRow ? aSortData.nCol1 : aSortData.nRow1) + fIndex;
8551 if (aSortIndexValues.size() == aSortOrderValues.size())
8552 aSortData.maKeyState[i].bAscending = (aSortOrderValues[i] == 1.0);
8553 else
8554 aSortData.maKeyState[i].bAscending = (aSortOrderValues[0] == 1.0);
8557 // sorting...
8558 std::vector<SCCOLROW> aOrderIndices = GetSortOrder(aSortData, pMatSrc);
8559 // create sorted matrix
8560 ScMatrixRef pResMat = CreateSortedMatrix(aSortData, pMatSrc,
8561 ScRange(aSortData.nCol1, aSortData.nRow1, aSortData.nSourceTab,
8562 aSortData.nCol2, aSortData.nRow2, aSortData.nSourceTab),
8563 aOrderIndices, nsC, nsR);
8565 if (pResMat)
8566 PushMatrix(pResMat);
8567 else
8568 PushIllegalParameter();
8571 void ScInterpreter::ScSortBy()
8573 sal_uInt8 nParamCount = GetByte();
8575 if (nParamCount < 2/*|| (nParamCount % 2 != 1)*/)
8577 PushError(FormulaError::ParameterExpected);
8578 return;
8581 sal_uInt8 nSortCount = nParamCount / 2;
8583 ScSortParam aSortData;
8584 aSortData.maKeyState.resize(nSortCount);
8586 // 127th, ..., 3rd and 2nd argument: sort by range/array and sort orders pair
8587 sal_uInt8 nSortBy = nSortCount;
8588 ScMatrixRef pFullMatSortBy = nullptr;
8589 while (nSortBy-- > 0 && nGlobalError == FormulaError::NONE)
8591 // 3rd argument sort_order optional: default ascending
8592 if (nParamCount >= 3 && (nParamCount % 2 == 1))
8594 sal_Int8 nSortOrder = static_cast<sal_Int8>(GetInt32WithDefault(1));
8595 if (nSortOrder != 1 && nSortOrder != -1)
8597 PushIllegalParameter();
8598 return;
8600 aSortData.maKeyState[nSortBy].bAscending = (nSortOrder == 1);
8601 nParamCount--;
8604 // 2nd argument: take sort by ranges
8605 ScMatrixRef pMatSortBy = nullptr;
8606 SCSIZE nbyC = 0, nbyR = 0;
8607 switch (GetStackType())
8609 case svSingleRef:
8610 case svDoubleRef:
8611 case svMatrix:
8612 case svExternalSingleRef:
8613 case svExternalDoubleRef:
8615 if (nSortCount == 1)
8617 pFullMatSortBy = GetMatrix();
8618 if (!pFullMatSortBy)
8620 PushIllegalParameter();
8621 return;
8623 pFullMatSortBy->GetDimensions(nbyC, nbyR);
8625 else
8627 pMatSortBy = GetMatrix();
8628 if (!pMatSortBy)
8630 PushIllegalParameter();
8631 return;
8633 pMatSortBy->GetDimensions(nbyC, nbyR);
8636 // last->first (backward) sortby array
8637 if (nSortBy == nSortCount - 1)
8639 if (nbyC == 1 && nbyR > 1)
8640 aSortData.bByRow = true;
8641 else if (nbyR == 1 && nbyC > 1)
8642 aSortData.bByRow = false;
8643 else
8645 PushIllegalParameter();
8646 return;
8649 if (nSortCount > 1)
8651 pFullMatSortBy = GetNewMat(aSortData.bByRow ? (nbyC * nSortCount) : nbyC,
8652 aSortData.bByRow ? nbyR : (nbyR * nSortCount), /*bEmpty*/true);
8656 break;
8658 default:
8659 PushIllegalParameter();
8660 return;
8663 // ..., penultimate sortby arrays
8664 if (nSortCount > 1 && nSortBy <= nSortCount - 1)
8666 SCSIZE nCheckCol = 0, nCheckRow = 0;
8667 pFullMatSortBy->GetDimensions(nCheckCol, nCheckRow);
8668 if ((aSortData.bByRow && nbyR == nCheckRow && nbyC == 1) ||
8669 (!aSortData.bByRow && nbyC == nCheckCol && nbyR == 1))
8671 for (SCSIZE ci = 0; ci < nbyC; ci++)//col
8673 for (SCSIZE rj = 0; rj < nbyR; rj++)//row
8675 if (pMatSortBy->IsEmptyCell(ci, rj))
8677 if (aSortData.bByRow)
8678 pFullMatSortBy->PutEmpty(ci + nSortBy, rj);
8679 else
8680 pFullMatSortBy->PutEmpty(ci, rj + nSortBy);
8682 else if (pMatSortBy->IsStringOrEmpty(ci, rj))
8684 if (aSortData.bByRow)
8685 pFullMatSortBy->PutString(pMatSortBy->GetString(ci, rj), ci + nSortBy, rj);
8686 else
8687 pFullMatSortBy->PutString(pMatSortBy->GetString(ci, rj), ci, rj + nSortBy);
8689 else
8691 if (aSortData.bByRow)
8692 pFullMatSortBy->PutDouble(pMatSortBy->GetDouble(ci, rj), ci + nSortBy, rj);
8693 else
8694 pFullMatSortBy->PutDouble(pMatSortBy->GetDouble(ci, rj), ci, rj + nSortBy);
8699 else
8701 PushIllegalParameter();
8702 return;
8706 aSortData.maKeyState[nSortBy].bDoSort = true;
8707 aSortData.maKeyState[nSortBy].nField = nSortBy;
8709 nParamCount--;
8712 // 1st argument is the range/array to be sorted
8713 SCSIZE nsC = 0, nsR = 0;
8714 SCCOL nSortCol1 = 0, nSortCol2 = 0;
8715 SCROW nSortRow1 = 0, nSortRow2 = 0;
8716 SCTAB nSortTab1 = 0, nSortTab2 = 0;
8717 ScMatrixRef pMatSrc = nullptr;
8718 switch ( GetStackType() )
8720 case svSingleRef:
8721 PopSingleRef(nSortCol1, nSortRow1, nSortTab1);
8722 nSortCol2 = nSortCol1;
8723 nSortRow2 = nSortRow1;
8724 nsC = nSortCol2 - nSortCol1 + 1;
8725 nsR = nSortRow2 - nSortRow1 + 1;
8726 break;
8727 case svDoubleRef:
8729 PopDoubleRef(nSortCol1, nSortRow1, nSortTab1, nSortCol2, nSortRow2, nSortTab2);
8730 if (nSortTab1 != nSortTab2)
8732 PushIllegalParameter();
8733 return;
8735 nsC = nSortCol2 - nSortCol1 + 1;
8736 nsR = nSortRow2 - nSortRow1 + 1;
8738 break;
8739 case svMatrix:
8740 case svExternalSingleRef:
8741 case svExternalDoubleRef:
8743 pMatSrc = GetMatrix();
8744 if (!pMatSrc)
8746 PushIllegalParameter();
8747 return;
8749 pMatSrc->GetDimensions(nsC, nsR);
8750 nSortCol2 = nsC - 1; // nSortCol1 = 0
8751 nSortRow2 = nsR - 1; // nSortRow1 = 0
8753 break;
8755 default:
8756 PushIllegalParameter();
8757 return;
8760 SCSIZE nCheckMatrixCol = 0, nCheckMatrixRow = 0;
8761 pFullMatSortBy->GetDimensions(nCheckMatrixCol, nCheckMatrixRow);
8762 if (nGlobalError != FormulaError::NONE)
8764 PushError(nGlobalError);
8765 return;
8767 else if ((aSortData.bByRow && nsR != nCheckMatrixRow) ||
8768 (!aSortData.bByRow && nsC != nCheckMatrixCol))
8770 PushIllegalParameter();
8771 return;
8773 else
8775 aSortData.nCol2 = nCheckMatrixCol - 1;
8776 aSortData.nRow2 = nCheckMatrixRow - 1;
8779 // sorting...
8780 std::vector<SCCOLROW> aOrderIndices = GetSortOrder(aSortData, pFullMatSortBy);
8781 // create sorted matrix
8782 ScMatrixRef pResMat = CreateSortedMatrix(aSortData, pMatSrc,
8783 ScRange(nSortCol1, nSortRow1, nSortTab1, nSortCol2, nSortRow2, nSortTab2),
8784 aOrderIndices, nsC, nsR);
8786 if (pResMat)
8787 PushMatrix(pResMat);
8788 else
8789 PushIllegalParameter();
8792 void ScInterpreter::ScUnique()
8794 sal_uInt8 nParamCount = GetByte();
8795 if (!MustHaveParamCount(nParamCount, 1, 3))
8796 return;
8798 // 3rd argument optional - Exactly_once: default FALSE
8799 bool bExactly_once = false;
8800 if (nParamCount == 3)
8801 bExactly_once = GetBoolWithDefault(false);
8803 // 2nd argument optional - default: By_Col = false --> bByRow = true
8804 bool bByRow = true;
8805 if (nParamCount >= 2)
8806 bByRow = !GetBoolWithDefault(false);
8808 // 1st argument: take unique search range
8809 ScMatrixRef pMatSource = nullptr;
8810 SCSIZE nsC = 0, nsR = 0;
8811 switch (GetStackType())
8813 case svSingleRef:
8814 case svDoubleRef:
8815 case svMatrix:
8816 case svExternalSingleRef:
8817 case svExternalDoubleRef:
8819 pMatSource = GetMatrix();
8820 if (!pMatSource)
8822 PushIllegalParameter();
8823 return;
8826 pMatSource->GetDimensions(nsC, nsR);
8828 break;
8830 default:
8831 PushIllegalParameter();
8832 return;
8835 if (nGlobalError != FormulaError::NONE || nsC < 1 || nsR < 1)
8837 PushIllegalArgument();
8838 return;
8841 // Create unique dataset
8842 std::unordered_set<OUString> aStrSet;
8843 std::vector<std::pair<SCSIZE, OUString>> aResPos;
8844 SCSIZE nOut = bByRow ? nsR : nsC;
8845 SCSIZE nIn = bByRow ? nsC : nsR;
8847 for (SCSIZE i = 0; i < nOut; i++)
8849 OUString aStr;
8850 for (SCSIZE j = 0; j < nIn; j++)
8852 OUString aCellStr = bByRow ? pMatSource->GetString(mrContext, j, i).getString() :
8853 pMatSource->GetString(mrContext, i, j).getString();
8854 aStr += aCellStr + u"\x0001";
8857 if (aStrSet.insert(aStr).second) // unique if inserted
8859 aResPos.emplace_back(std::make_pair(i, aStr));
8861 else
8863 if (bExactly_once)
8865 auto it = std::find_if(aResPos.begin(), aResPos.end(),
8866 [&aStr](const std::pair<SCSIZE, OUString>& aRes)
8868 return aRes.second.equals(aStr);
8871 if (it != aResPos.end())
8872 aResPos.erase(it);
8876 // No result
8877 if (aResPos.size() == 0)
8879 if (nGlobalError != FormulaError::NONE)
8881 PushIllegalArgument();
8883 else
8885 PushNA();
8887 return;
8889 // fill result matrix with unique values
8890 ScMatrixRef pResMat = bByRow ? GetNewMat(nsC, aResPos.size(), /*bEmpty*/true) :
8891 GetNewMat(aResPos.size(), nsR, /*bEmpty*/true);
8892 for (SCSIZE iPos = 0; iPos < aResPos.size(); iPos++)
8894 if (bByRow)
8896 for (SCSIZE col = 0; col < nsC; col++)
8898 if (pMatSource->IsEmptyCell(col, aResPos[iPos].first))
8900 pResMat->PutEmpty(col, iPos);
8902 else if (!pMatSource->IsStringOrEmpty(col, aResPos[iPos].first))
8904 pResMat->PutDouble(pMatSource->GetDouble(col, aResPos[iPos].first), col, iPos);
8906 else
8908 pResMat->PutString(pMatSource->GetString(col, aResPos[iPos].first), col, iPos);
8912 else
8914 for (SCSIZE row = 0; row < nsR; row++)
8916 if (pMatSource->IsEmptyCell(aResPos[iPos].first, row))
8918 pResMat->PutEmpty(iPos, row);
8920 else if (!pMatSource->IsStringOrEmpty(aResPos[iPos].first, row))
8922 pResMat->PutDouble(pMatSource->GetDouble(aResPos[iPos].first, row), iPos, row);
8924 else
8926 pResMat->PutString(pMatSource->GetString(aResPos[iPos].first, row), iPos, row);
8932 if (!pResMat)
8934 PushIllegalArgument();
8936 else
8938 PushMatrix(pResMat);
8942 void ScInterpreter::getTokensAtParameter( std::unique_ptr<ScTokenArray>& pTokens, short nPos )
8944 sal_uInt16 nOpen = 0;
8945 sal_uInt16 nSepCount = 0;
8946 formula::FormulaTokenArrayPlainIterator aIter(*pArr);
8947 formula::FormulaToken* t = aIter.First();
8948 for (t = aIter.NextNoSpaces(); t; t = aIter.NextNoSpaces())
8950 OpCode aOpCode = t->GetOpCode();
8951 formula::StackVar aIntType = t->GetType();
8952 if ((aOpCode == ocOpen || aOpCode == ocArrayOpen || aOpCode == ocTableRefOpen) && aIntType == formula::StackVar::svSep)
8953 nOpen++;
8954 else if ((aOpCode == ocClose || aOpCode == ocArrayClose || aOpCode == ocTableRefClose) && aIntType == formula::StackVar::svSep)
8955 nOpen--;
8956 else if (aOpCode == ocSep && aIntType == formula::StackVar::svSep && nOpen == 1)
8958 nSepCount++;
8959 continue;
8962 if (nSepCount == nPos && nOpen > 0)
8964 pTokens->AddToken(*t->Clone());
8969 void ScInterpreter::replaceNamesToResult( const std::unordered_map<OUString, formula::FormulaToken*> nResultIndexes,
8970 std::unique_ptr<ScTokenArray>& pTokens )
8972 formula::FormulaTokenArrayPlainIterator aIterResult(*pTokens);
8973 for (FormulaToken* t = aIterResult.GetNextStringName(); t; t = aIterResult.GetNextStringName())
8975 auto iRes = nResultIndexes.find(t->GetString().getString());
8976 if (iRes != nResultIndexes.end())
8977 pTokens->ReplaceToken(aIterResult.GetIndex() - 1, iRes->second->Clone(),
8978 FormulaTokenArray::ReplaceMode::CODE_ONLY);
8982 void ScInterpreter::ScLet()
8984 const short* pJump = pCur->GetJump();
8985 short nJumpCount = pJump[0];
8986 short nOrgJumpCount = nJumpCount;
8988 if (nJumpCount < 3 || (nJumpCount % 2 != 1))
8990 PushError(FormulaError::ParameterExpected);
8991 aCode.Jump(pJump[nOrgJumpCount], pJump[nOrgJumpCount]);
8992 return;
8995 OUString aStrName;
8996 std::unordered_map<OUString, formula::FormulaToken*> nResultIndexes;
8997 formula::FormulaTokenArrayPlainIterator aIter(*pArr);
8998 unique_ptr<ScTokenArray> pValueTokens(new ScTokenArray(mrDoc));
9000 // name and function pairs parameter
9001 while (nJumpCount > 1)
9003 if (nJumpCount == nOrgJumpCount)
9005 aStrName = GetString().getString();
9007 else if ((nOrgJumpCount - nJumpCount + 1) % 2 == 1)
9009 aIter.Jump(pJump[static_cast<short>(nOrgJumpCount - nJumpCount + 1)] - 1);
9010 FormulaToken* t = aIter.NextRPN();
9011 aStrName = t->GetString().getString();
9013 else
9015 PushError(FormulaError::ParameterExpected);
9016 aCode.Jump(pJump[nOrgJumpCount], pJump[nOrgJumpCount]);
9017 return;
9019 nJumpCount--;
9021 // get value tokens
9022 getTokensAtParameter(pValueTokens, nOrgJumpCount - nJumpCount);
9023 nJumpCount--;
9025 // replace names with result tokens
9026 replaceNamesToResult(nResultIndexes, pValueTokens);
9028 // calculate the inner results unless we already have a push result token
9029 if (pValueTokens->GetLen() == 1 && pValueTokens->GetArray()[0]->GetOpCode() == ocPush)
9031 if (!nResultIndexes.insert(std::make_pair(aStrName, pValueTokens->GetArray()[0]->Clone())).second)
9033 PushIllegalParameter();
9034 aCode.Jump(pJump[nOrgJumpCount], pJump[nOrgJumpCount]);
9035 return;
9038 else
9040 ScCompiler aComp(mrDoc, aPos, *pValueTokens, mrDoc.GetGrammar(), false, false, &mrContext);
9041 aComp.CompileTokenArray();
9042 ScInterpreter aInt(mrDoc.GetFormulaCell(aPos), mrDoc, mrContext, aPos, *pValueTokens);
9043 sfx2::LinkManager aNewLinkMgr(mrDoc.GetDocumentShell());
9044 aInt.SetLinkManager(&aNewLinkMgr);
9045 formula::StackVar aIntType = aInt.Interpret();
9047 if (aIntType == formula::svMatrixCell)
9049 ScConstMatrixRef xMat(aInt.GetResultToken()->GetMatrix());
9050 if (!nResultIndexes.insert(std::make_pair(aStrName, new ScMatrixToken(xMat->Clone()))).second)
9052 PushIllegalParameter();
9053 aCode.Jump(pJump[nOrgJumpCount], pJump[nOrgJumpCount]);
9054 return;
9057 else
9059 FormulaConstTokenRef xTok(aInt.GetResultToken());
9060 if (!nResultIndexes.insert(std::make_pair(aStrName, xTok->Clone())).second)
9062 PushIllegalParameter();
9063 aCode.Jump(pJump[nOrgJumpCount], pJump[nOrgJumpCount]);
9064 return;
9068 pValueTokens->Clear();
9071 // last parameter: calculation
9072 getTokensAtParameter(pValueTokens, nOrgJumpCount - nJumpCount);
9073 nJumpCount--;
9075 // replace names with result tokens
9076 replaceNamesToResult(nResultIndexes, pValueTokens);
9078 // calculate the final result
9079 ScCompiler aComp(mrDoc, aPos, *pValueTokens, mrDoc.GetGrammar(), false, false, &mrContext);
9080 aComp.CompileTokenArray();
9081 ScInterpreter aInt(mrDoc.GetFormulaCell(aPos), mrDoc, mrContext, aPos, *pValueTokens);
9082 sfx2::LinkManager aNewLinkMgr(mrDoc.GetDocumentShell());
9083 aInt.SetLinkManager(&aNewLinkMgr);
9084 formula::StackVar aIntType = aInt.Interpret();
9086 if (aIntType == formula::svMatrixCell)
9088 ScConstMatrixRef xMat(aInt.GetResultToken()->GetMatrix());
9089 PushTokenRef(new ScMatrixToken(xMat->Clone()));
9091 else
9093 formula::FormulaConstTokenRef xLambdaResult(aInt.GetResultToken());
9094 if (xLambdaResult)
9096 nGlobalError = xLambdaResult->GetError();
9097 if (nGlobalError == FormulaError::NONE)
9098 PushTokenRef(xLambdaResult);
9099 else
9100 PushError(nGlobalError);
9104 pValueTokens.reset();
9105 aCode.Jump(pJump[nOrgJumpCount], pJump[nOrgJumpCount]);
9108 void ScInterpreter::ScSubTotal()
9110 sal_uInt8 nParamCount = GetByte();
9111 if ( !MustHaveParamCountMinWithStackCheck( nParamCount, 2 ) )
9112 return;
9114 // We must fish the 1st parameter deep from the stack! And push it on top.
9115 const FormulaToken* p = pStack[ sp - nParamCount ];
9116 PushWithoutError( *p );
9117 sal_Int32 nFunc = GetInt32();
9118 mnSubTotalFlags |= SubtotalFlags::IgnoreNestedStAg | SubtotalFlags::IgnoreFiltered;
9119 if (nFunc > 100)
9121 // For opcodes 101 through 111, we need to skip hidden cells.
9122 // Other than that these opcodes are identical to 1 through 11.
9123 mnSubTotalFlags |= SubtotalFlags::IgnoreHidden;
9124 nFunc -= 100;
9127 if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 11 )
9128 PushIllegalArgument(); // simulate return on stack, not SetError(...)
9129 else
9131 cPar = nParamCount - 1;
9132 switch( nFunc )
9134 case SUBTOTAL_FUNC_AVE : ScAverage(); break;
9135 case SUBTOTAL_FUNC_CNT : ScCount(); break;
9136 case SUBTOTAL_FUNC_CNT2 : ScCount2(); break;
9137 case SUBTOTAL_FUNC_MAX : ScMax(); break;
9138 case SUBTOTAL_FUNC_MIN : ScMin(); break;
9139 case SUBTOTAL_FUNC_PROD : ScProduct(); break;
9140 case SUBTOTAL_FUNC_STD : ScStDev(); break;
9141 case SUBTOTAL_FUNC_STDP : ScStDevP(); break;
9142 case SUBTOTAL_FUNC_SUM : ScSum(); break;
9143 case SUBTOTAL_FUNC_VAR : ScVar(); break;
9144 case SUBTOTAL_FUNC_VARP : ScVarP(); break;
9145 default : PushIllegalArgument(); break;
9148 mnSubTotalFlags = SubtotalFlags::NONE;
9149 // Get rid of the 1st (fished) parameter.
9150 FormulaConstTokenRef xRef( PopToken());
9151 Pop();
9152 PushTokenRef( xRef);
9155 void ScInterpreter::ScAggregate()
9157 sal_uInt8 nParamCount = GetByte();
9158 if ( !MustHaveParamCountMinWithStackCheck( nParamCount, 3 ) )
9159 return;
9161 const FormulaError nErr = nGlobalError;
9162 nGlobalError = FormulaError::NONE;
9164 // fish the 1st parameter from the stack and push it on top.
9165 const FormulaToken* p = pStack[ sp - nParamCount ];
9166 PushWithoutError( *p );
9167 sal_Int32 nFunc = GetInt32();
9168 // fish the 2nd parameter from the stack and push it on top.
9169 const FormulaToken* p2 = pStack[ sp - ( nParamCount - 1 ) ];
9170 PushWithoutError( *p2 );
9171 sal_Int32 nOption = GetInt32();
9173 if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 19 )
9175 nGlobalError = nErr;
9176 PushIllegalArgument();
9178 else
9180 switch ( nOption)
9182 case 0 : // ignore nested SUBTOTAL and AGGREGATE functions
9183 mnSubTotalFlags = SubtotalFlags::IgnoreNestedStAg;
9184 break;
9185 case 1 : // ignore hidden rows, nested SUBTOTAL and AGGREGATE functions
9186 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreNestedStAg;
9187 break;
9188 case 2 : // ignore error values, nested SUBTOTAL and AGGREGATE functions
9189 mnSubTotalFlags = SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg;
9190 break;
9191 case 3 : // ignore hidden rows, error values, nested SUBTOTAL and AGGREGATE functions
9192 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg;
9193 break;
9194 case 4 : // ignore nothing
9195 mnSubTotalFlags = SubtotalFlags::NONE;
9196 break;
9197 case 5 : // ignore hidden rows
9198 mnSubTotalFlags = SubtotalFlags::IgnoreHidden ;
9199 break;
9200 case 6 : // ignore error values
9201 mnSubTotalFlags = SubtotalFlags::IgnoreErrVal ;
9202 break;
9203 case 7 : // ignore hidden rows and error values
9204 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal ;
9205 break;
9206 default :
9207 nGlobalError = nErr;
9208 PushIllegalArgument();
9209 return;
9212 if ((mnSubTotalFlags & SubtotalFlags::IgnoreErrVal) == SubtotalFlags::NONE)
9213 nGlobalError = nErr;
9215 cPar = nParamCount - 2;
9216 switch ( nFunc )
9218 case AGGREGATE_FUNC_AVE : ScAverage(); break;
9219 case AGGREGATE_FUNC_CNT : ScCount(); break;
9220 case AGGREGATE_FUNC_CNT2 : ScCount2(); break;
9221 case AGGREGATE_FUNC_MAX : ScMax(); break;
9222 case AGGREGATE_FUNC_MIN : ScMin(); break;
9223 case AGGREGATE_FUNC_PROD : ScProduct(); break;
9224 case AGGREGATE_FUNC_STD : ScStDev(); break;
9225 case AGGREGATE_FUNC_STDP : ScStDevP(); break;
9226 case AGGREGATE_FUNC_SUM : ScSum(); break;
9227 case AGGREGATE_FUNC_VAR : ScVar(); break;
9228 case AGGREGATE_FUNC_VARP : ScVarP(); break;
9229 case AGGREGATE_FUNC_MEDIAN : ScMedian(); break;
9230 case AGGREGATE_FUNC_MODSNGL : ScModalValue(); break;
9231 case AGGREGATE_FUNC_LARGE : ScLarge(); break;
9232 case AGGREGATE_FUNC_SMALL : ScSmall(); break;
9233 case AGGREGATE_FUNC_PERCINC : ScPercentile( true ); break;
9234 case AGGREGATE_FUNC_QRTINC : ScQuartile( true ); break;
9235 case AGGREGATE_FUNC_PERCEXC : ScPercentile( false ); break;
9236 case AGGREGATE_FUNC_QRTEXC : ScQuartile( false ); break;
9237 default:
9238 nGlobalError = nErr;
9239 PushIllegalArgument();
9240 break;
9242 mnSubTotalFlags = SubtotalFlags::NONE;
9244 FormulaConstTokenRef xRef( PopToken());
9245 // Get rid of the 1st and 2nd (fished) parameters.
9246 Pop();
9247 Pop();
9248 PushTokenRef( xRef);
9251 std::unique_ptr<ScDBQueryParamBase> ScInterpreter::GetDBParams( bool& rMissingField )
9253 bool bAllowMissingField = false;
9254 if ( rMissingField )
9256 bAllowMissingField = true;
9257 rMissingField = false;
9259 if ( GetByte() == 3 )
9261 // First, get the query criteria range.
9262 ::std::unique_ptr<ScDBRangeBase> pQueryRef( PopDBDoubleRef() );
9263 if (!pQueryRef)
9264 return nullptr;
9266 bool bByVal = true;
9267 double nVal = 0.0;
9268 svl::SharedString aStr;
9269 ScRange aMissingRange;
9270 bool bRangeFake = false;
9271 switch (GetStackType())
9273 case svDouble :
9274 nVal = ::rtl::math::approxFloor( GetDouble() );
9275 if ( bAllowMissingField && nVal == 0.0 )
9276 rMissingField = true; // fake missing parameter
9277 break;
9278 case svString :
9279 bByVal = false;
9280 aStr = GetString();
9281 break;
9282 case svSingleRef :
9284 ScAddress aAdr;
9285 PopSingleRef( aAdr );
9286 ScRefCellValue aCell(mrDoc, aAdr);
9287 if (aCell.hasNumeric())
9288 nVal = GetCellValue(aAdr, aCell);
9289 else
9291 bByVal = false;
9292 GetCellString(aStr, aCell);
9295 break;
9296 case svDoubleRef :
9297 if ( bAllowMissingField )
9298 { // fake missing parameter for old SO compatibility
9299 bRangeFake = true;
9300 PopDoubleRef( aMissingRange );
9302 else
9304 PopError();
9305 SetError( FormulaError::IllegalParameter );
9307 break;
9308 case svMissing :
9309 PopError();
9310 if ( bAllowMissingField )
9311 rMissingField = true;
9312 else
9313 SetError( FormulaError::IllegalParameter );
9314 break;
9315 default:
9316 PopError();
9317 SetError( FormulaError::IllegalParameter );
9320 if (nGlobalError != FormulaError::NONE)
9321 return nullptr;
9323 unique_ptr<ScDBRangeBase> pDBRef( PopDBDoubleRef() );
9325 if (nGlobalError != FormulaError::NONE || !pDBRef)
9326 return nullptr;
9328 if ( bRangeFake )
9330 // range parameter must match entire database range
9331 if (pDBRef->isRangeEqual(aMissingRange))
9332 rMissingField = true;
9333 else
9334 SetError( FormulaError::IllegalParameter );
9337 if (nGlobalError != FormulaError::NONE)
9338 return nullptr;
9340 SCCOL nField = pDBRef->getFirstFieldColumn();
9341 if (rMissingField)
9342 ; // special case
9343 else if (bByVal)
9344 nField = pDBRef->findFieldColumn(static_cast<SCCOL>(nVal));
9345 else
9347 FormulaError nErr = FormulaError::NONE;
9348 nField = pDBRef->findFieldColumn(aStr.getString(), &nErr);
9349 SetError(nErr);
9352 if (!mrDoc.ValidCol(nField))
9353 return nullptr;
9355 unique_ptr<ScDBQueryParamBase> pParam( pDBRef->createQueryParam(pQueryRef.get()) );
9357 if (pParam)
9359 // An allowed missing field parameter sets the result field
9360 // to any of the query fields, just to be able to return
9361 // some cell from the iterator.
9362 if ( rMissingField )
9363 nField = static_cast<SCCOL>(pParam->GetEntry(0).nField);
9364 pParam->mnField = nField;
9366 SCSIZE nCount = pParam->GetEntryCount();
9367 for ( SCSIZE i=0; i < nCount; i++ )
9369 ScQueryEntry& rEntry = pParam->GetEntry(i);
9370 if (!rEntry.bDoQuery)
9371 break;
9373 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
9374 sal_uInt32 nIndex = 0;
9375 OUString aQueryStr = rItem.maString.getString();
9376 bool bNumber = mrContext.NFIsNumberFormat(
9377 aQueryStr, nIndex, rItem.mfVal);
9378 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
9380 if (!bNumber && pParam->eSearchType == utl::SearchParam::SearchType::Normal)
9381 pParam->eSearchType = DetectSearchType(aQueryStr, mrDoc);
9383 return pParam;
9386 return nullptr;
9389 void ScInterpreter::DBIterator( ScIterFunc eFunc )
9391 double fRes = 0;
9392 KahanSum fErg = 0;
9393 sal_uLong nCount = 0;
9394 bool bMissingField = false;
9395 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
9396 if (pQueryParam)
9398 if (!pQueryParam->IsValidFieldIndex())
9400 SetError(FormulaError::NoValue);
9401 return;
9403 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
9404 ScDBQueryDataIterator::Value aValue;
9405 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
9407 switch( eFunc )
9409 case ifPRODUCT: fRes = 1; break;
9410 case ifMAX: fRes = -MAXDOUBLE; break;
9411 case ifMIN: fRes = MAXDOUBLE; break;
9412 default: ; // nothing
9417 nCount++;
9418 switch( eFunc )
9420 case ifAVERAGE:
9421 case ifSUM:
9422 fErg += aValue.mfValue;
9423 break;
9424 case ifSUMSQ:
9425 fErg += aValue.mfValue * aValue.mfValue;
9426 break;
9427 case ifPRODUCT:
9428 fRes *= aValue.mfValue;
9429 break;
9430 case ifMAX:
9431 if( aValue.mfValue > fRes ) fRes = aValue.mfValue;
9432 break;
9433 case ifMIN:
9434 if( aValue.mfValue < fRes ) fRes = aValue.mfValue;
9435 break;
9436 default: ; // nothing
9439 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
9441 SetError(aValue.mnError);
9443 else
9444 SetError( FormulaError::IllegalParameter);
9445 switch( eFunc )
9447 case ifCOUNT: fRes = nCount; break;
9448 case ifSUM: fRes = fErg.get(); break;
9449 case ifSUMSQ: fRes = fErg.get(); break;
9450 case ifAVERAGE: fRes = div(fErg.get(), nCount); break;
9451 default: ; // nothing
9453 PushDouble( fRes );
9456 void ScInterpreter::ScDBSum()
9458 DBIterator( ifSUM );
9461 void ScInterpreter::ScDBCount()
9463 bool bMissingField = true;
9464 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
9465 if (pQueryParam)
9467 sal_uLong nCount = 0;
9468 if ( bMissingField && pQueryParam->GetType() == ScDBQueryParamBase::INTERNAL )
9469 { // count all matching records
9470 // TODO: currently the QueryIterators only return cell pointers of
9471 // existing cells, so if a query matches an empty cell there's
9472 // nothing returned, and therefore not counted!
9473 // Since this has ever been the case and this code here only came
9474 // into existence to fix #i6899 and it never worked before we'll
9475 // have to live with it until we reimplement the iterators to also
9476 // return empty cells, which would mean to adapt all callers of
9477 // iterators.
9478 ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pQueryParam.get());
9479 p->nCol2 = p->nCol1; // Don't forget to select only one column.
9480 SCTAB nTab = p->nTab;
9481 // ScQueryCellIteratorDirect doesn't make use of ScDBQueryParamBase::mnField,
9482 // so the source range has to be restricted, like before the introduction
9483 // of ScDBQueryParamBase.
9484 p->nCol1 = p->nCol2 = p->mnField;
9485 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab, *p, true, false);
9486 if ( aCellIter.GetFirst() )
9490 nCount++;
9491 } while ( aCellIter.GetNext() );
9494 else
9495 { // count only matching records with a value in the "result" field
9496 if (!pQueryParam->IsValidFieldIndex())
9498 SetError(FormulaError::NoValue);
9499 return;
9501 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
9502 ScDBQueryDataIterator::Value aValue;
9503 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
9507 nCount++;
9509 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
9511 SetError(aValue.mnError);
9513 PushDouble( nCount );
9515 else
9516 PushIllegalParameter();
9519 void ScInterpreter::ScDBCount2()
9521 bool bMissingField = true;
9522 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
9523 if (pQueryParam)
9525 if (!pQueryParam->IsValidFieldIndex())
9527 SetError(FormulaError::NoValue);
9528 return;
9530 sal_uLong nCount = 0;
9531 pQueryParam->mbSkipString = false;
9532 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
9533 ScDBQueryDataIterator::Value aValue;
9534 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
9538 nCount++;
9540 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
9542 SetError(aValue.mnError);
9543 PushDouble( nCount );
9545 else
9546 PushIllegalParameter();
9549 void ScInterpreter::ScDBAverage()
9551 DBIterator( ifAVERAGE );
9554 void ScInterpreter::ScDBMax()
9556 DBIterator( ifMAX );
9559 void ScInterpreter::ScDBMin()
9561 DBIterator( ifMIN );
9564 void ScInterpreter::ScDBProduct()
9566 DBIterator( ifPRODUCT );
9569 void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount )
9571 std::vector<double> values;
9572 KahanSum vSum = 0.0;
9573 KahanSum fSum = 0.0;
9575 rValCount = 0.0;
9576 bool bMissingField = false;
9577 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
9578 if (pQueryParam)
9580 if (!pQueryParam->IsValidFieldIndex())
9582 SetError(FormulaError::NoValue);
9583 return;
9585 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
9586 ScDBQueryDataIterator::Value aValue;
9587 if (aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE)
9591 rValCount++;
9592 values.push_back(aValue.mfValue);
9593 fSum += aValue.mfValue;
9595 while ((aValue.mnError == FormulaError::NONE) && aValIter.GetNext(aValue));
9597 SetError(aValue.mnError);
9599 else
9600 SetError( FormulaError::IllegalParameter);
9602 double vMean = fSum.get() / values.size();
9604 for (double v : values)
9605 vSum += (v - vMean) * (v - vMean);
9607 rVal = vSum.get();
9610 void ScInterpreter::ScDBStdDev()
9612 double fVal, fCount;
9613 GetDBStVarParams( fVal, fCount );
9614 PushDouble( sqrt(fVal/(fCount-1)));
9617 void ScInterpreter::ScDBStdDevP()
9619 double fVal, fCount;
9620 GetDBStVarParams( fVal, fCount );
9621 PushDouble( sqrt(fVal/fCount));
9624 void ScInterpreter::ScDBVar()
9626 double fVal, fCount;
9627 GetDBStVarParams( fVal, fCount );
9628 PushDouble(fVal/(fCount-1));
9631 void ScInterpreter::ScDBVarP()
9633 double fVal, fCount;
9634 GetDBStVarParams( fVal, fCount );
9635 PushDouble(fVal/fCount);
9638 static bool lcl_IsTableStructuredRef(const OUString& sRefStr, sal_Int32& nIndex)
9640 nIndex = ScGlobal::FindUnquoted(sRefStr, '[');
9641 return (nIndex > 0 && ScGlobal::FindUnquoted(sRefStr, ']', nIndex + 1) > nIndex);
9644 void ScInterpreter::ScIndirect()
9646 sal_uInt8 nParamCount = GetByte();
9647 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9648 return;
9650 // Reference address syntax for INDIRECT is configurable.
9651 FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
9652 if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
9653 // Use the current address syntax if unspecified.
9654 eConv = mrDoc.GetAddressConvention();
9656 // either CONV_A1_XL_A1 was explicitly configured, or it wasn't possible
9657 // to determine which syntax to use during doc import
9658 bool bTryXlA1 = (eConv == FormulaGrammar::CONV_A1_XL_A1);
9660 if (nParamCount == 2 && 0.0 == GetDouble() )
9662 // Overwrite the config and try Excel R1C1.
9663 eConv = FormulaGrammar::CONV_XL_R1C1;
9664 bTryXlA1 = false;
9667 svl::SharedString sSharedRefStr = GetString();
9668 const OUString & sRefStr = sSharedRefStr.getString();
9669 if (sRefStr.isEmpty())
9671 // Bail out early for empty cells, rely on "we do have a string" below.
9672 PushError( FormulaError::NoRef);
9673 return;
9676 const ScAddress::Details aDetails( bTryXlA1 ? FormulaGrammar::CONV_OOO : eConv, aPos );
9677 const ScAddress::Details aDetailsXlA1( FormulaGrammar::CONV_XL_A1, aPos );
9678 SCTAB nTab = aPos.Tab();
9680 bool bTableRefNamed = false;
9681 sal_Int32 nTableRefNamedIndex = -1;
9682 OUString sTabRefStr;
9684 // Named expressions and DB range names need to be tried first, as older 1K
9685 // columns allowed names that would now match a 16k columns cell address.
9688 ScRangeData* pData = ScRangeStringConverter::GetRangeDataFromString( sRefStr, nTab, mrDoc, eConv);
9689 if (!pData)
9690 break;
9692 // We need this in order to obtain a good range.
9693 pData->ValidateTabRefs();
9695 ScRange aRange;
9697 // This is the usual way to treat named ranges containing
9698 // relative references.
9699 if (!pData->IsReference(aRange, aPos))
9701 sTabRefStr = pData->GetSymbol();
9702 bTableRefNamed = lcl_IsTableStructuredRef(sTabRefStr, nTableRefNamedIndex);
9703 // if bTableRefNamed is true, we have a name that maps to a table structured reference.
9704 // Such a case is handled below.
9705 break;
9708 if (aRange.aStart == aRange.aEnd)
9709 PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
9710 aRange.aStart.Tab());
9711 else
9712 PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
9713 aRange.aStart.Tab(), aRange.aEnd.Col(),
9714 aRange.aEnd.Row(), aRange.aEnd.Tab());
9716 // success!
9717 return;
9719 while (false);
9723 if (bTableRefNamed)
9724 break;
9726 const OUString & aName( sSharedRefStr.getIgnoreCaseString() );
9727 ScDBCollection::NamedDBs& rDBs = mrDoc.GetDBCollection()->getNamedDBs();
9728 const ScDBData* pData = rDBs.findByUpperName( aName);
9729 if (!pData)
9730 break;
9732 ScRange aRange;
9733 pData->GetArea( aRange);
9735 // In Excel, specifying a table name without [] resolves to the
9736 // same as with [], a range that excludes header and totals
9737 // rows and contains only data rows. Do the same.
9738 if (pData->HasHeader())
9739 aRange.aStart.IncRow();
9740 if (pData->HasTotals())
9741 aRange.aEnd.IncRow(-1);
9743 if (aRange.aStart.Row() > aRange.aEnd.Row())
9744 break;
9746 if (aRange.aStart == aRange.aEnd)
9747 PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
9748 aRange.aStart.Tab());
9749 else
9750 PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
9751 aRange.aStart.Tab(), aRange.aEnd.Col(),
9752 aRange.aEnd.Row(), aRange.aEnd.Tab());
9754 // success!
9755 return;
9757 while (false);
9759 ScRefAddress aRefAd, aRefAd2;
9760 ScAddress::ExternalInfo aExtInfo;
9761 if ( !bTableRefNamed &&
9762 (ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo) ||
9763 ( bTryXlA1 && ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd,
9764 aRefAd2, aDetailsXlA1, &aExtInfo) ) ) )
9766 if (aExtInfo.mbExternal)
9768 PushExternalDoubleRef(
9769 aExtInfo.mnFileId, aExtInfo.maTabName,
9770 aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(),
9771 aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab());
9773 else
9774 PushDoubleRef( aRefAd, aRefAd2);
9776 else if ( !bTableRefNamed &&
9777 (ConvertSingleRef(mrDoc, sRefStr, nTab, aRefAd, aDetails, &aExtInfo) ||
9778 ( bTryXlA1 && ConvertSingleRef (mrDoc, sRefStr, nTab, aRefAd,
9779 aDetailsXlA1, &aExtInfo) ) ) )
9781 if (aExtInfo.mbExternal)
9783 PushExternalSingleRef(
9784 aExtInfo.mnFileId, aExtInfo.maTabName, aRefAd.Col(), aRefAd.Row(), aRefAd.Tab());
9786 else
9787 PushSingleRef( aRefAd);
9789 else
9791 // It may be even a TableRef or an external name.
9792 // Anything else that resolves to one reference could be added
9793 // here, but we don't want to compile every arbitrary string. This
9794 // is already nasty enough...
9795 sal_Int32 nIndex = bTableRefNamed ? nTableRefNamedIndex : -1;
9796 bool bTableRef = bTableRefNamed;
9797 if (!bTableRefNamed)
9798 bTableRef = lcl_IsTableStructuredRef(sRefStr, nIndex);
9799 bool bExternalName = false; // External references would had been consumed above already.
9800 if (!bTableRef)
9802 // This is our own file name reference representation centric.. but
9803 // would work also for XL '[doc]'!name and also for
9804 // '[doc]Sheet1'!name ... sickos.
9805 if (sRefStr[0] == '\'')
9807 // Minimum 'a'#name or 'a'!name
9808 // bTryXlA1 means try both, first our own.
9809 if (bTryXlA1 || eConv == FormulaGrammar::CONV_OOO)
9811 nIndex = ScGlobal::FindUnquoted( sRefStr, '#');
9812 if (nIndex >= 3 && sRefStr[nIndex-1] == '\'')
9814 bExternalName = true;
9815 eConv = FormulaGrammar::CONV_OOO;
9818 if (!bExternalName && (bTryXlA1 || eConv != FormulaGrammar::CONV_OOO))
9820 nIndex = ScGlobal::FindUnquoted( sRefStr, '!');
9821 if (nIndex >= 3 && sRefStr[nIndex-1] == '\'')
9823 bExternalName = true;
9829 if (bExternalName || bTableRef)
9833 ScCompiler aComp( mrDoc, aPos, mrDoc.GetGrammar());
9834 aComp.SetRefConvention( eConv); // must be after grammar
9835 std::unique_ptr<ScTokenArray> pTokArr( aComp.CompileString(bTableRefNamed ? sTabRefStr : sRefStr));
9837 if (pTokArr->GetCodeError() != FormulaError::NONE || !pTokArr->GetLen())
9838 break;
9840 // Whatever... use only the specific case.
9841 if (bExternalName)
9843 const formula::FormulaToken* pTok = pTokArr->FirstToken();
9844 if (!pTok || pTok->GetType() != svExternalName)
9845 break;
9847 else if (!pTokArr->HasOpCode( ocTableRef))
9848 break;
9850 aComp.CompileTokenArray();
9852 // A syntactically valid reference will generate exactly
9853 // one RPN token, a reference or error. Discard everything
9854 // else as error.
9855 if (pTokArr->GetCodeLen() != 1)
9856 break;
9858 ScTokenRef xTok( pTokArr->FirstRPNToken());
9859 if (!xTok)
9860 break;
9862 switch (xTok->GetType())
9864 case svSingleRef:
9865 case svDoubleRef:
9866 case svExternalSingleRef:
9867 case svExternalDoubleRef:
9868 case svError:
9869 PushTokenRef( xTok);
9870 // success!
9871 return;
9872 default:
9873 ; // nothing
9876 while (false);
9879 PushError( FormulaError::NoRef);
9883 void ScInterpreter::ScAddressFunc()
9885 OUString sTabStr;
9887 sal_uInt8 nParamCount = GetByte();
9888 if( !MustHaveParamCount( nParamCount, 2, 5 ) )
9889 return;
9891 if( nParamCount >= 5 )
9892 sTabStr = GetString().getString();
9894 FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; // default
9895 if (nParamCount >= 4 && 0.0 == GetDoubleWithDefault( 1.0))
9896 eConv = FormulaGrammar::CONV_XL_R1C1;
9897 else
9899 // If A1 syntax is requested then the actual sheet separator and format
9900 // convention depends on the syntax configured for INDIRECT to match
9901 // that, and if it is unspecified then the document's address syntax.
9902 FormulaGrammar::AddressConvention eForceConv = maCalcConfig.meStringRefAddressSyntax;
9903 if (eForceConv == FormulaGrammar::CONV_UNSPECIFIED)
9904 eForceConv = mrDoc.GetAddressConvention();
9905 if (eForceConv == FormulaGrammar::CONV_XL_A1 || eForceConv == FormulaGrammar::CONV_XL_R1C1)
9906 eConv = FormulaGrammar::CONV_XL_A1; // for anything Excel use Excel A1
9909 ScRefFlags nFlags = ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS; // default
9910 if( nParamCount >= 3 )
9912 sal_Int32 n = GetInt32WithDefault(1);
9913 switch ( n )
9915 default :
9916 PushNoValue();
9917 return;
9919 case 5:
9920 case 1 : break; // default
9921 case 6:
9922 case 2 : nFlags = ScRefFlags::ROW_ABS; break;
9923 case 7:
9924 case 3 : nFlags = ScRefFlags::COL_ABS; break;
9925 case 8:
9926 case 4 : nFlags = ScRefFlags::ZERO; break; // both relative
9929 nFlags |= ScRefFlags::VALID | ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID;
9931 SCCOL nCol = static_cast<SCCOL>(GetInt16());
9932 SCROW nRow = static_cast<SCROW>(GetInt32());
9933 if( eConv == FormulaGrammar::CONV_XL_R1C1 )
9935 // YUCK! The XL interface actually treats rel R1C1 refs differently
9936 // than A1
9937 if( !(nFlags & ScRefFlags::COL_ABS) )
9938 nCol += aPos.Col() + 1;
9939 if( !(nFlags & ScRefFlags::ROW_ABS) )
9940 nRow += aPos.Row() + 1;
9943 --nCol;
9944 --nRow;
9945 if (nGlobalError != FormulaError::NONE || !mrDoc.ValidCol( nCol) || !mrDoc.ValidRow( nRow))
9947 PushIllegalArgument();
9948 return;
9951 const ScAddress::Details aDetails( eConv, aPos );
9952 const ScAddress aAdr( nCol, nRow, 0);
9953 OUString aRefStr(aAdr.Format(nFlags, &mrDoc, aDetails));
9955 if( nParamCount >= 5 && !sTabStr.isEmpty() )
9957 OUString aDoc;
9958 if (eConv == FormulaGrammar::CONV_OOO)
9960 // Isolate Tab from 'Doc'#Tab
9961 sal_Int32 nPos = ScCompiler::GetDocTabPos( sTabStr);
9962 if (nPos != -1)
9964 if (sTabStr[nPos+1] == '$')
9965 ++nPos; // also split 'Doc'#$Tab
9966 aDoc = sTabStr.copy( 0, nPos+1);
9967 sTabStr = sTabStr.copy( nPos+1);
9970 /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may
9971 * need some extra handling to isolate Tab from Doc. */
9972 if (sTabStr[0] != '\'' || !sTabStr.endsWith("'"))
9973 ScCompiler::CheckTabQuotes( sTabStr, eConv);
9974 if (!aDoc.isEmpty())
9975 sTabStr = aDoc + sTabStr;
9976 sTabStr += (eConv == FormulaGrammar::CONV_XL_R1C1 || eConv == FormulaGrammar::CONV_XL_A1) ?
9977 std::u16string_view(u"!") : std::u16string_view(u".");
9978 sTabStr += aRefStr;
9979 PushString( sTabStr );
9981 else
9982 PushString( aRefStr );
9985 void ScInterpreter::ScOffset()
9987 sal_uInt8 nParamCount = GetByte();
9988 if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
9989 return;
9991 bool bNewWidth = false;
9992 bool bNewHeight = false;
9993 sal_Int32 nColNew = 1, nRowNew = 1;
9994 if (nParamCount == 5)
9996 if (IsMissing())
9997 PopError();
9998 else
10000 nColNew = GetInt32();
10001 bNewWidth = true;
10004 if (nParamCount >= 4)
10006 if (IsMissing())
10007 PopError();
10008 else
10010 nRowNew = GetInt32();
10011 bNewHeight = true;
10014 sal_Int32 nColPlus = GetInt32();
10015 sal_Int32 nRowPlus = GetInt32();
10016 if (nGlobalError != FormulaError::NONE)
10018 PushError( nGlobalError);
10019 return;
10021 if (nColNew <= 0 || nRowNew <= 0)
10023 PushIllegalArgument();
10024 return;
10026 SCCOL nCol1(0);
10027 SCROW nRow1(0);
10028 SCTAB nTab1(0);
10029 SCCOL nCol2(0);
10030 SCROW nRow2(0);
10031 SCTAB nTab2(0);
10032 switch (GetStackType())
10034 case svSingleRef:
10036 PopSingleRef(nCol1, nRow1, nTab1);
10037 if (!bNewWidth && !bNewHeight)
10039 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1) + nColPlus);
10040 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1) + nRowPlus);
10041 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1))
10042 PushIllegalArgument();
10043 else
10044 PushSingleRef(nCol1, nRow1, nTab1);
10046 else
10048 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
10049 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
10050 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
10051 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
10052 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
10053 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2))
10054 PushIllegalArgument();
10055 else
10056 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
10058 break;
10060 case svExternalSingleRef:
10062 sal_uInt16 nFileId;
10063 OUString aTabName;
10064 ScSingleRefData aRef;
10065 PopExternalSingleRef(nFileId, aTabName, aRef);
10066 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
10067 nCol1 = aAbsRef.Col();
10068 nRow1 = aAbsRef.Row();
10069 nTab1 = aAbsRef.Tab();
10071 if (!bNewWidth && !bNewHeight)
10073 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1) + nColPlus);
10074 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1) + nRowPlus);
10075 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1))
10076 PushIllegalArgument();
10077 else
10078 PushExternalSingleRef(nFileId, aTabName, nCol1, nRow1, nTab1);
10080 else
10082 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
10083 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
10084 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
10085 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
10086 nTab2 = nTab1;
10087 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
10088 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2))
10089 PushIllegalArgument();
10090 else
10091 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
10093 break;
10095 case svDoubleRef:
10097 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
10098 if (!bNewWidth)
10099 nColNew = nCol2 - nCol1 + 1;
10100 if (!bNewHeight)
10101 nRowNew = nRow2 - nRow1 + 1;
10102 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
10103 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
10104 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
10105 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
10106 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
10107 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2)
10108 PushIllegalArgument();
10109 else
10110 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
10111 break;
10113 case svExternalDoubleRef:
10115 sal_uInt16 nFileId;
10116 OUString aTabName;
10117 ScComplexRefData aRef;
10118 PopExternalDoubleRef(nFileId, aTabName, aRef);
10119 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
10120 nCol1 = aAbs.aStart.Col();
10121 nRow1 = aAbs.aStart.Row();
10122 nTab1 = aAbs.aStart.Tab();
10123 nCol2 = aAbs.aEnd.Col();
10124 nRow2 = aAbs.aEnd.Row();
10125 nTab2 = aAbs.aEnd.Tab();
10126 if (!bNewWidth)
10127 nColNew = nCol2 - nCol1 + 1;
10128 if (!bNewHeight)
10129 nRowNew = nRow2 - nRow1 + 1;
10130 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
10131 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
10132 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
10133 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
10134 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
10135 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2)
10136 PushIllegalArgument();
10137 else
10138 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
10139 break;
10141 default:
10142 PushIllegalParameter();
10143 break;
10144 } // end switch
10147 void ScInterpreter::ScIndex()
10149 sal_uInt8 nParamCount = GetByte();
10150 if ( !MustHaveParamCount( nParamCount, 1, 4 ) )
10151 return;
10153 sal_uInt32 nArea;
10154 size_t nAreaCount;
10155 SCCOL nCol;
10156 SCROW nRow;
10157 if (nParamCount == 4)
10158 nArea = GetUInt32();
10159 else
10160 nArea = 1;
10161 bool bColMissing;
10162 if (nParamCount >= 3)
10164 bColMissing = IsMissing();
10165 nCol = static_cast<SCCOL>(GetInt16());
10167 else
10169 bColMissing = false;
10170 nCol = 0;
10172 if (nParamCount >= 2)
10173 nRow = static_cast<SCROW>(GetInt32());
10174 else
10175 nRow = 0;
10176 if (GetStackType() == svRefList)
10177 nAreaCount = (sp ? pStack[sp-1]->GetRefList()->size() : 0);
10178 else
10179 nAreaCount = 1; // one reference or array or whatever
10180 if (nGlobalError != FormulaError::NONE || nAreaCount == 0 || static_cast<size_t>(nArea) > nAreaCount)
10182 PushError( FormulaError::NoRef);
10183 return;
10185 else if (nArea < 1 || nCol < 0 || nRow < 0)
10187 PushIllegalArgument();
10188 return;
10190 switch (GetStackType())
10192 case svMatrix:
10193 case svExternalSingleRef:
10194 case svExternalDoubleRef:
10196 if (nArea != 1)
10197 SetError(FormulaError::IllegalArgument);
10198 sal_uInt16 nOldSp = sp;
10199 ScMatrixRef pMat = GetMatrix();
10200 if (pMat)
10202 SCSIZE nC, nR;
10203 pMat->GetDimensions(nC, nR);
10205 // Access one element of a vector independent of col/row
10206 // orientation. Excel documentation does not mention, but
10207 // i62850 had a .xls example of a row vector accessed by
10208 // row number returning one element. This
10209 // INDEX(row_vector;element) behaves the same as
10210 // INDEX(row_vector;0;element) and thus contradicts Excel
10211 // documentation where the second parameter is always
10212 // row_num.
10214 // ODFF v1.3 in 6.14.6 INDEX states "If DataSource is a
10215 // one-dimensional row vector, Row is optional, which
10216 // effectively makes Row act as the column offset into the
10217 // vector". Guess the first Row is a typo and should read
10218 // Column instead.
10220 const bool bRowVectorSpecial = (nParamCount == 2 || bColMissing);
10221 const bool bRowVectorElement = (nR == 1 && (nCol != 0 || (bRowVectorSpecial && nRow != 0)));
10222 const bool bVectorElement = (bRowVectorElement || (nC == 1 && nRow != 0));
10224 if (nC == 0 || nR == 0 ||
10225 (!bVectorElement && (o3tl::make_unsigned(nCol) > nC ||
10226 o3tl::make_unsigned(nRow) > nR)))
10227 PushError( FormulaError::NoRef);
10228 else if (nCol == 0 && nRow == 0)
10229 sp = nOldSp;
10230 else if (bVectorElement)
10232 // Vectors here don't replicate to the other dimension.
10233 SCSIZE nElement, nOtherDimension;
10234 if (bRowVectorElement && !bRowVectorSpecial)
10236 nElement = o3tl::make_unsigned(nCol);
10237 nOtherDimension = o3tl::make_unsigned(nRow);
10239 else
10241 nElement = o3tl::make_unsigned(nRow);
10242 nOtherDimension = o3tl::make_unsigned(nCol);
10245 if (nElement == 0 || nElement > nC * nR || nOtherDimension > 1)
10246 PushError( FormulaError::NoRef);
10247 else
10249 --nElement;
10250 if (pMat->IsStringOrEmpty( nElement))
10251 PushString( pMat->GetString(nElement).getString());
10252 else
10253 PushDouble( pMat->GetDouble( nElement));
10256 else if (nCol == 0)
10258 ScMatrixRef pResMat = GetNewMat(nC, 1, /*bEmpty*/true);
10259 if (pResMat)
10261 SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1);
10262 for (SCSIZE i = 0; i < nC; i++)
10263 if (!pMat->IsStringOrEmpty(i, nRowMinus1))
10264 pResMat->PutDouble(pMat->GetDouble(i,
10265 nRowMinus1), i, 0);
10266 else
10267 pResMat->PutString(pMat->GetString(i, nRowMinus1), i, 0);
10269 PushMatrix(pResMat);
10271 else
10272 PushError( FormulaError::NoRef);
10274 else if (nRow == 0)
10276 ScMatrixRef pResMat = GetNewMat(1, nR, /*bEmpty*/true);
10277 if (pResMat)
10279 SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1);
10280 for (SCSIZE i = 0; i < nR; i++)
10281 if (!pMat->IsStringOrEmpty(nColMinus1, i))
10282 pResMat->PutDouble(pMat->GetDouble(nColMinus1,
10283 i), i);
10284 else
10285 pResMat->PutString(pMat->GetString(nColMinus1, i), i);
10286 PushMatrix(pResMat);
10288 else
10289 PushError( FormulaError::NoRef);
10291 else
10293 if (!pMat->IsStringOrEmpty( static_cast<SCSIZE>(nCol-1),
10294 static_cast<SCSIZE>(nRow-1)))
10295 PushDouble( pMat->GetDouble(
10296 static_cast<SCSIZE>(nCol-1),
10297 static_cast<SCSIZE>(nRow-1)));
10298 else
10299 PushString( pMat->GetString(
10300 static_cast<SCSIZE>(nCol-1),
10301 static_cast<SCSIZE>(nRow-1)).getString());
10305 break;
10306 case svSingleRef:
10308 SCCOL nCol1 = 0;
10309 SCROW nRow1 = 0;
10310 SCTAB nTab1 = 0;
10311 PopSingleRef( nCol1, nRow1, nTab1);
10312 if (nCol > 1 || nRow > 1)
10313 PushError( FormulaError::NoRef);
10314 else
10315 PushSingleRef( nCol1, nRow1, nTab1);
10317 break;
10318 case svDoubleRef:
10319 case svRefList:
10321 SCCOL nCol1 = 0;
10322 SCROW nRow1 = 0;
10323 SCTAB nTab1 = 0;
10324 SCCOL nCol2 = 0;
10325 SCROW nRow2 = 0;
10326 SCTAB nTab2 = 0;
10327 bool bRowArray = false;
10328 if (GetStackType() == svRefList)
10330 FormulaConstTokenRef xRef = PopToken();
10331 if (nGlobalError != FormulaError::NONE || !xRef)
10333 PushError( FormulaError::NoRef);
10334 return;
10336 ScRange aRange( ScAddress::UNINITIALIZED);
10337 DoubleRefToRange( (*(xRef->GetRefList()))[nArea-1], aRange);
10338 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
10339 if ( nParamCount == 2 && nRow1 == nRow2 )
10340 bRowArray = true;
10342 else
10344 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
10345 if ( nParamCount == 2 && nRow1 == nRow2 )
10346 bRowArray = true;
10348 if ( nTab1 != nTab2 ||
10349 (nCol > 0 && nCol1+nCol-1 > nCol2) ||
10350 (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) ||
10351 ( nRow > nCol2 - nCol1 + 1 && bRowArray ))
10352 PushError( FormulaError::NoRef);
10353 else if (nCol == 0 && nRow == 0)
10355 if ( nCol1 == nCol2 && nRow1 == nRow2 )
10356 PushSingleRef( nCol1, nRow1, nTab1 );
10357 else
10358 PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 );
10360 else if (nRow == 0)
10362 if ( nRow1 == nRow2 )
10363 PushSingleRef( nCol1+nCol-1, nRow1, nTab1 );
10364 else
10365 PushDoubleRef( nCol1+nCol-1, nRow1, nTab1,
10366 nCol1+nCol-1, nRow2, nTab1 );
10368 else if (nCol == 0)
10370 if ( nCol1 == nCol2 )
10371 PushSingleRef( nCol1, nRow1+nRow-1, nTab1 );
10372 else if ( bRowArray )
10374 nCol =static_cast<SCCOL>(nRow);
10375 nRow = 1;
10376 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
10378 else
10379 PushDoubleRef( nCol1, nRow1+nRow-1, nTab1,
10380 nCol2, nRow1+nRow-1, nTab1);
10382 else
10383 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
10385 break;
10386 default:
10387 PopError();
10388 PushError( FormulaError::NoRef);
10392 void ScInterpreter::ScMultiArea()
10394 // Legacy support, convert to RefList
10395 sal_uInt8 nParamCount = GetByte();
10396 if (MustHaveParamCountMin( nParamCount, 1))
10398 while (nGlobalError == FormulaError::NONE && nParamCount-- > 1)
10400 ScUnionFunc();
10405 void ScInterpreter::ScAreas()
10407 sal_uInt8 nParamCount = GetByte();
10408 if (!MustHaveParamCount( nParamCount, 1))
10409 return;
10411 size_t nCount = 0;
10412 switch (GetStackType())
10414 case svSingleRef:
10416 FormulaConstTokenRef xT = PopToken();
10417 ValidateRef( *xT->GetSingleRef());
10418 ++nCount;
10420 break;
10421 case svDoubleRef:
10423 FormulaConstTokenRef xT = PopToken();
10424 ValidateRef( *xT->GetDoubleRef());
10425 ++nCount;
10427 break;
10428 case svRefList:
10430 FormulaConstTokenRef xT = PopToken();
10431 ValidateRef( *(xT->GetRefList()));
10432 nCount += xT->GetRefList()->size();
10434 break;
10435 default:
10436 SetError( FormulaError::IllegalParameter);
10438 PushDouble( double(nCount));
10441 void ScInterpreter::ScCurrency()
10443 sal_uInt8 nParamCount = GetByte();
10444 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
10445 return;
10447 OUString aStr;
10448 double fDec;
10449 if (nParamCount == 2)
10451 fDec = ::rtl::math::approxFloor(GetDouble());
10452 if (fDec < -15.0 || fDec > 15.0)
10454 PushIllegalArgument();
10455 return;
10458 else
10459 fDec = 2.0;
10460 double fVal = GetDouble();
10461 double fFac;
10462 if ( fDec != 0.0 )
10463 fFac = pow( double(10), fDec );
10464 else
10465 fFac = 1.0;
10466 if (fVal < 0.0)
10467 fVal = ceil(fVal*fFac-0.5)/fFac;
10468 else
10469 fVal = floor(fVal*fFac+0.5)/fFac;
10470 const Color* pColor = nullptr;
10471 if ( fDec < 0.0 )
10472 fDec = 0.0;
10473 sal_uLong nIndex = mrContext.NFGetStandardFormat(
10474 SvNumFormatType::CURRENCY,
10475 ScGlobal::eLnge);
10476 if ( static_cast<sal_uInt16>(fDec) != mrContext.NFGetFormatPrecision( nIndex ) )
10478 OUString sFormatString = mrContext.NFGenerateFormat(
10479 nIndex,
10480 ScGlobal::eLnge,
10481 true, // with thousands separator
10482 false, // not red
10483 static_cast<sal_uInt16>(fDec));// decimal places
10484 if (!mrContext.NFGetPreviewString(sFormatString,
10485 fVal,
10486 aStr,
10487 &pColor,
10488 ScGlobal::eLnge))
10489 SetError(FormulaError::IllegalArgument);
10491 else
10493 mrContext.NFGetOutputString(fVal, nIndex, aStr, &pColor);
10495 PushString(aStr);
10498 void ScInterpreter::ScReplace()
10500 if ( !MustHaveParamCount( GetByte(), 4 ) )
10501 return;
10503 OUString aNewStr = GetString().getString();
10504 sal_Int32 nCount = GetStringPositionArgument();
10505 sal_Int32 nPos = GetStringPositionArgument();
10506 OUString aOldStr = GetString().getString();
10507 if (nPos < 1 || nCount < 0)
10508 PushIllegalArgument();
10509 else
10511 sal_Int32 nLen = aOldStr.getLength();
10512 if (nPos > nLen + 1)
10513 nPos = nLen + 1;
10514 if (nCount > nLen - nPos + 1)
10515 nCount = nLen - nPos + 1;
10516 sal_Int32 nIdx = 0;
10517 sal_Int32 nCnt = 0;
10518 while ( nIdx < nLen && nPos > nCnt + 1 )
10520 aOldStr.iterateCodePoints( &nIdx );
10521 ++nCnt;
10523 sal_Int32 nStart = nIdx;
10524 while ( nIdx < nLen && nPos + nCount - 1 > nCnt )
10526 aOldStr.iterateCodePoints( &nIdx );
10527 ++nCnt;
10529 if ( CheckStringResultLen( aOldStr, aNewStr.getLength() - (nIdx - nStart) ) )
10530 aOldStr = aOldStr.replaceAt( nStart, nIdx - nStart, aNewStr );
10531 PushString( aOldStr );
10535 void ScInterpreter::ScFixed()
10537 sal_uInt8 nParamCount = GetByte();
10538 if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
10539 return;
10541 OUString aStr;
10542 double fDec;
10543 bool bThousand;
10544 if (nParamCount == 3)
10545 bThousand = !GetBool(); // Param true: no thousands separator
10546 else
10547 bThousand = true;
10548 if (nParamCount >= 2)
10550 fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 ));
10551 if (fDec < -15.0 || fDec > 15.0)
10553 PushIllegalArgument();
10554 return;
10557 else
10558 fDec = 2.0;
10559 double fVal = GetDouble();
10560 double fFac;
10561 if ( fDec != 0.0 )
10562 fFac = pow( double(10), fDec );
10563 else
10564 fFac = 1.0;
10565 if (fVal < 0.0)
10566 fVal = ceil(fVal*fFac-0.5)/fFac;
10567 else
10568 fVal = floor(fVal*fFac+0.5)/fFac;
10569 const Color* pColor = nullptr;
10570 if (fDec < 0.0)
10571 fDec = 0.0;
10572 sal_uLong nIndex = mrContext.NFGetStandardFormat(
10573 SvNumFormatType::NUMBER,
10574 ScGlobal::eLnge);
10575 OUString sFormatString = mrContext.NFGenerateFormat(
10576 nIndex,
10577 ScGlobal::eLnge,
10578 bThousand, // with thousands separator
10579 false, // not red
10580 static_cast<sal_uInt16>(fDec));// decimal places
10581 if (!mrContext.NFGetPreviewString(sFormatString,
10582 fVal,
10583 aStr,
10584 &pColor,
10585 ScGlobal::eLnge))
10586 PushIllegalArgument();
10587 else
10588 PushString(aStr);
10591 void ScInterpreter::ScFind()
10593 sal_uInt8 nParamCount = GetByte();
10594 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
10595 return;
10597 sal_Int32 nCnt;
10598 if (nParamCount == 3)
10599 nCnt = GetDouble();
10600 else
10601 nCnt = 1;
10602 OUString sStr = GetString().getString();
10603 if (nCnt < 1 || nCnt > sStr.getLength())
10604 PushNoValue();
10605 else
10607 sal_Int32 nPos = sStr.indexOf(GetString().getString(), nCnt - 1);
10608 if (nPos == -1)
10609 PushNoValue();
10610 else
10612 sal_Int32 nIdx = 0;
10613 nCnt = 0;
10614 while ( nIdx < nPos )
10616 sStr.iterateCodePoints( &nIdx );
10617 ++nCnt;
10619 PushDouble( static_cast<double>(nCnt + 1) );
10624 void ScInterpreter::ScExact()
10626 nFuncFmtType = SvNumFormatType::LOGICAL;
10627 if ( MustHaveParamCount( GetByte(), 2 ) )
10629 svl::SharedString s1 = GetString();
10630 svl::SharedString s2 = GetString();
10631 PushInt( int(s1.getData() == s2.getData()) );
10635 void ScInterpreter::ScLeft()
10637 sal_uInt8 nParamCount = GetByte();
10638 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
10639 return;
10641 sal_Int32 n;
10642 if (nParamCount == 2)
10644 n = GetStringPositionArgument();
10645 if (n < 0)
10647 PushIllegalArgument();
10648 return ;
10651 else
10652 n = 1;
10653 OUString aStr = GetString().getString();
10654 sal_Int32 nIdx = 0;
10655 sal_Int32 nCnt = 0;
10656 while ( nIdx < aStr.getLength() && n > nCnt++ )
10657 aStr.iterateCodePoints( &nIdx );
10658 aStr = aStr.copy( 0, nIdx );
10659 PushString( aStr );
10662 namespace {
10664 struct UBlockScript {
10665 UBlockCode from;
10666 UBlockCode to;
10671 const UBlockScript scriptList[] = {
10672 {UBLOCK_HANGUL_JAMO, UBLOCK_HANGUL_JAMO},
10673 {UBLOCK_CJK_RADICALS_SUPPLEMENT, UBLOCK_HANGUL_SYLLABLES},
10674 {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS,UBLOCK_CJK_RADICALS_SUPPLEMENT },
10675 {UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS,UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS},
10676 {UBLOCK_CJK_COMPATIBILITY_FORMS, UBLOCK_CJK_COMPATIBILITY_FORMS},
10677 {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS},
10678 {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT},
10679 {UBLOCK_CJK_STROKES, UBLOCK_CJK_STROKES}
10681 static bool IsDBCS(sal_Unicode currentChar)
10683 // for the locale of ja-JP, character U+0x005c and U+0x20ac should be ScriptType::Asian
10684 if( (currentChar == 0x005c || currentChar == 0x20ac) &&
10685 (MsLangId::getConfiguredSystemLanguage() == LANGUAGE_JAPANESE) )
10686 return true;
10687 sal_uInt16 i;
10688 bool bRet = false;
10689 UBlockCode block = ublock_getCode(currentChar);
10690 for ( i = 0; i < SAL_N_ELEMENTS(scriptList); i++) {
10691 if (block <= scriptList[i].to) break;
10693 bRet = (i < SAL_N_ELEMENTS(scriptList) && block >= scriptList[i].from);
10694 return bRet;
10696 static sal_Int32 lcl_getLengthB( std::u16string_view str, sal_Int32 nPos )
10698 sal_Int32 index = 0;
10699 sal_Int32 length = 0;
10700 while ( index < nPos )
10702 if (IsDBCS(str[index]))
10703 length += 2;
10704 else
10705 length++;
10706 index++;
10708 return length;
10710 static sal_Int32 getLengthB(std::u16string_view str)
10712 if(str.empty())
10713 return 0;
10714 else
10715 return lcl_getLengthB( str, str.size() );
10717 void ScInterpreter::ScLenB()
10719 PushDouble( getLengthB(GetString().getString()) );
10721 static OUString lcl_RightB(const OUString &rStr, sal_Int32 n)
10723 if( n < getLengthB(rStr) )
10725 OUStringBuffer aBuf(rStr);
10726 sal_Int32 index = aBuf.getLength();
10727 while(index-- >= 0)
10729 if(0 == n)
10731 aBuf.remove( 0, index + 1);
10732 break;
10734 if(-1 == n)
10736 aBuf.remove( 0, index + 2 );
10737 aBuf.insert( 0, " ");
10738 break;
10740 if(IsDBCS(aBuf[index]))
10741 n -= 2;
10742 else
10743 n--;
10745 return aBuf.makeStringAndClear();
10747 return rStr;
10749 void ScInterpreter::ScRightB()
10751 sal_uInt8 nParamCount = GetByte();
10752 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
10753 return;
10755 sal_Int32 n;
10756 if (nParamCount == 2)
10758 n = GetStringPositionArgument();
10759 if (n < 0)
10761 PushIllegalArgument();
10762 return ;
10765 else
10766 n = 1;
10767 OUString aStr(lcl_RightB(GetString().getString(), n));
10768 PushString( aStr );
10770 static OUString lcl_LeftB(const OUString &rStr, sal_Int32 n)
10772 if( n < getLengthB(rStr) )
10774 OUStringBuffer aBuf(rStr);
10775 sal_Int32 index = -1;
10776 while(index++ < aBuf.getLength())
10778 if(0 == n)
10780 aBuf.truncate(index);
10781 break;
10783 if(-1 == n)
10785 aBuf.truncate( index - 1 );
10786 aBuf.append(" ");
10787 break;
10789 if(IsDBCS(aBuf[index]))
10790 n -= 2;
10791 else
10792 n--;
10794 return aBuf.makeStringAndClear();
10796 return rStr;
10798 void ScInterpreter::ScLeftB()
10800 sal_uInt8 nParamCount = GetByte();
10801 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
10802 return;
10804 sal_Int32 n;
10805 if (nParamCount == 2)
10807 n = GetStringPositionArgument();
10808 if (n < 0)
10810 PushIllegalArgument();
10811 return ;
10814 else
10815 n = 1;
10816 OUString aStr(lcl_LeftB(GetString().getString(), n));
10817 PushString( aStr );
10819 void ScInterpreter::ScMidB()
10821 if ( !MustHaveParamCount( GetByte(), 3 ) )
10822 return;
10824 const sal_Int32 nCount = GetStringPositionArgument();
10825 const sal_Int32 nStart = GetStringPositionArgument();
10826 OUString aStr = GetString().getString();
10827 if (nStart < 1 || nCount < 0)
10828 PushIllegalArgument();
10829 else
10832 aStr = lcl_LeftB(aStr, nStart + nCount - 1);
10833 sal_Int32 nCnt = getLengthB(aStr) - nStart + 1;
10834 aStr = lcl_RightB(aStr, std::max<sal_Int32>(nCnt,0));
10835 PushString(aStr);
10839 void ScInterpreter::ScReplaceB()
10841 if ( !MustHaveParamCount( GetByte(), 4 ) )
10842 return;
10844 OUString aNewStr = GetString().getString();
10845 const sal_Int32 nCount = GetStringPositionArgument();
10846 const sal_Int32 nPos = GetStringPositionArgument();
10847 OUString aOldStr = GetString().getString();
10848 int nLen = getLengthB( aOldStr );
10849 if (nPos < 1.0 || nPos > nLen || nCount < 0.0 || nPos + nCount -1 > nLen)
10850 PushIllegalArgument();
10851 else
10853 // REPLACEB(aOldStr;nPos;nCount;aNewStr) is the same as
10854 // LEFTB(aOldStr;nPos-1) & aNewStr & RIGHT(aOldStr;LENB(aOldStr)-(nPos - 1)-nCount)
10855 OUString aStr1 = lcl_LeftB( aOldStr, nPos - 1 );
10856 OUString aStr3 = lcl_RightB( aOldStr, nLen - nPos - nCount + 1);
10858 PushString( aStr1 + aNewStr + aStr3 );
10862 void ScInterpreter::ScFindB()
10864 sal_uInt8 nParamCount = GetByte();
10865 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
10866 return;
10868 sal_Int32 nStart;
10869 if ( nParamCount == 3 )
10870 nStart = GetStringPositionArgument();
10871 else
10872 nStart = 1;
10873 OUString aStr = GetString().getString();
10874 int nLen = getLengthB( aStr );
10875 OUString asStr = GetString().getString();
10876 int nsLen = getLengthB( asStr );
10877 if ( nStart < 1 || nStart > nLen - nsLen + 1 )
10878 PushIllegalArgument();
10879 else
10881 // create a string from sStr starting at nStart
10882 OUString aBuf = lcl_RightB( aStr, nLen - nStart + 1 );
10883 // search aBuf for asStr
10884 sal_Int32 nPos = aBuf.indexOf( asStr, 0 );
10885 if ( nPos == -1 )
10886 PushNoValue();
10887 else
10889 // obtain byte value of nPos
10890 int nBytePos = lcl_getLengthB( aBuf, nPos );
10891 PushDouble( nBytePos + nStart );
10896 void ScInterpreter::ScSearchB()
10898 sal_uInt8 nParamCount = GetByte();
10899 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
10900 return;
10902 sal_Int32 nStart;
10903 if ( nParamCount == 3 )
10905 nStart = GetStringPositionArgument();
10906 if( nStart < 1 )
10908 PushIllegalArgument();
10909 return;
10912 else
10913 nStart = 1;
10914 OUString aStr = GetString().getString();
10915 sal_Int32 nLen = getLengthB( aStr );
10916 OUString asStr = GetString().getString();
10917 sal_Int32 nsLen = nStart - 1;
10918 if( nsLen >= nLen )
10919 PushNoValue();
10920 else
10922 // create a string from sStr starting at nStart
10923 OUString aSubStr( lcl_RightB( aStr, nLen - nStart + 1 ) );
10924 // search aSubStr for asStr
10925 sal_Int32 nPos = 0;
10926 sal_Int32 nEndPos = aSubStr.getLength();
10927 utl::SearchParam::SearchType eSearchType = DetectSearchType( asStr, mrDoc );
10928 utl::SearchParam sPar( asStr, eSearchType, false, '~', false );
10929 utl::TextSearch sT( sPar, ScGlobal::getCharClass() );
10930 if ( !sT.SearchForward( aSubStr, &nPos, &nEndPos ) )
10931 PushNoValue();
10932 else
10934 // obtain byte value of nPos
10935 int nBytePos = lcl_getLengthB( aSubStr, nPos );
10936 PushDouble( nBytePos + nStart );
10941 void ScInterpreter::ScRight()
10943 sal_uInt8 nParamCount = GetByte();
10944 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
10945 return;
10947 sal_Int32 n;
10948 if (nParamCount == 2)
10950 n = GetStringPositionArgument();
10951 if (n < 0)
10953 PushIllegalArgument();
10954 return ;
10957 else
10958 n = 1;
10959 OUString aStr = GetString().getString();
10960 sal_Int32 nLen = aStr.getLength();
10961 if ( nLen <= n )
10962 PushString( aStr );
10963 else
10965 sal_Int32 nIdx = nLen;
10966 sal_Int32 nCnt = 0;
10967 while ( nIdx > 0 && n > nCnt )
10969 aStr.iterateCodePoints( &nIdx, -1 );
10970 ++nCnt;
10972 aStr = aStr.copy( nIdx, nLen - nIdx );
10973 PushString( aStr );
10977 void ScInterpreter::ScSearch()
10979 sal_uInt8 nParamCount = GetByte();
10980 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
10981 return;
10983 sal_Int32 nStart;
10984 if (nParamCount == 3)
10986 nStart = GetStringPositionArgument();
10987 if( nStart < 1 )
10989 PushIllegalArgument();
10990 return;
10993 else
10994 nStart = 1;
10995 OUString sStr = GetString().getString();
10996 OUString SearchStr = GetString().getString();
10997 sal_Int32 nPos = nStart - 1;
10998 sal_Int32 nEndPos = sStr.getLength();
10999 if( nPos >= nEndPos )
11000 PushNoValue();
11001 else
11003 utl::SearchParam::SearchType eSearchType = DetectSearchType( SearchStr, mrDoc );
11004 utl::SearchParam sPar(SearchStr, eSearchType, false, '~', false);
11005 utl::TextSearch sT( sPar, ScGlobal::getCharClass() );
11006 bool bBool = sT.SearchForward(sStr, &nPos, &nEndPos);
11007 if (!bBool)
11008 PushNoValue();
11009 else
11011 sal_Int32 nIdx = 0;
11012 sal_Int32 nCnt = 0;
11013 while ( nIdx < nPos )
11015 sStr.iterateCodePoints( &nIdx );
11016 ++nCnt;
11018 PushDouble( static_cast<double>(nCnt + 1) );
11023 void ScInterpreter::ScRegex()
11025 const sal_uInt8 nParamCount = GetByte();
11026 if (!MustHaveParamCount( nParamCount, 2, 4))
11027 return;
11029 // Flags are supported only for replacement, search match flags can be
11030 // individually and much more flexible set in the regular expression
11031 // pattern using (?ismwx-ismwx)
11032 bool bGlobalReplacement = false;
11033 sal_Int32 nOccurrence = 1; // default first occurrence, if any
11034 if (nParamCount == 4)
11036 // Argument can be either string or double.
11037 double fOccurrence;
11038 svl::SharedString aFlagsString;
11039 bool bDouble;
11040 if (!IsMissing())
11041 bDouble = GetDoubleOrString( fOccurrence, aFlagsString);
11042 else
11044 // For an omitted argument keep the default.
11045 PopError();
11046 bDouble = true;
11047 fOccurrence = nOccurrence;
11049 if (nGlobalError != FormulaError::NONE)
11051 PushError( nGlobalError);
11052 return;
11054 if (bDouble)
11056 if (!CheckStringPositionArgument( fOccurrence))
11058 PushError( FormulaError::IllegalArgument);
11059 return;
11061 nOccurrence = static_cast<sal_Int32>(fOccurrence);
11063 else
11065 const OUString aFlags( aFlagsString.getString());
11066 // Empty flags string is valid => no flag set.
11067 if (aFlags.getLength() > 1)
11069 // Only one flag supported.
11070 PushIllegalArgument();
11071 return;
11073 if (aFlags.getLength() == 1)
11075 if (aFlags.indexOf('g') >= 0)
11076 bGlobalReplacement = true;
11077 else
11079 // Unsupported flag.
11080 PushIllegalArgument();
11081 return;
11087 bool bReplacement = false;
11088 OUString aReplacement;
11089 if (nParamCount >= 3)
11091 // A missing argument is not an empty string to replace the match.
11092 // nOccurrence==0 forces no replacement, so simply discard the
11093 // argument.
11094 if (IsMissing() || nOccurrence == 0)
11095 PopError();
11096 else
11098 aReplacement = GetString().getString();
11099 bReplacement = true;
11102 // If bGlobalReplacement==true and bReplacement==false then
11103 // bGlobalReplacement is silently ignored.
11105 const OUString aExpression = GetString().getString();
11106 const OUString aText = GetString().getString();
11108 if (nGlobalError != FormulaError::NONE)
11110 PushError( nGlobalError);
11111 return;
11114 // 0-th match or replacement is none, return original string early.
11115 if (nOccurrence == 0)
11117 PushString( aText);
11118 return;
11121 const icu::UnicodeString aIcuExpression(
11122 false, reinterpret_cast<const UChar*>(aExpression.getStr()), aExpression.getLength());
11123 UErrorCode status = U_ZERO_ERROR;
11124 icu::RegexMatcher aRegexMatcher( aIcuExpression, 0, status);
11125 if (U_FAILURE(status))
11127 // Invalid regex.
11128 PushIllegalArgument();
11129 return;
11131 // Guard against pathological patterns, limit steps of engine, see
11132 // https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1RegexMatcher.html#a6ebcfcab4fe6a38678c0291643a03a00
11133 aRegexMatcher.setTimeLimit( 23*1000, status);
11135 const icu::UnicodeString aIcuText(false, reinterpret_cast<const UChar*>(aText.getStr()), aText.getLength());
11136 aRegexMatcher.reset( aIcuText);
11138 if (!bReplacement)
11140 // Find n-th occurrence.
11141 sal_Int32 nCount = 0;
11142 while (aRegexMatcher.find(status) && U_SUCCESS(status) && ++nCount < nOccurrence)
11144 if (U_FAILURE(status))
11146 // Some error.
11147 PushIllegalArgument();
11148 return;
11150 // n-th match found?
11151 if (nCount != nOccurrence)
11153 PushError( FormulaError::NotAvailable);
11154 return;
11156 // Extract matched text.
11157 icu::UnicodeString aMatch( aRegexMatcher.group( status));
11158 if (U_FAILURE(status))
11160 // Some error.
11161 PushIllegalArgument();
11162 return;
11164 OUString aResult( reinterpret_cast<const sal_Unicode*>(aMatch.getBuffer()), aMatch.length());
11165 PushString( aResult);
11166 return;
11169 const icu::UnicodeString aIcuReplacement(
11170 false, reinterpret_cast<const UChar*>(aReplacement.getStr()), aReplacement.getLength());
11171 icu::UnicodeString aReplaced;
11172 if (bGlobalReplacement)
11173 // Replace all occurrences of match with replacement.
11174 aReplaced = aRegexMatcher.replaceAll( aIcuReplacement, status);
11175 else if (nOccurrence == 1)
11176 // Replace first occurrence of match with replacement.
11177 aReplaced = aRegexMatcher.replaceFirst( aIcuReplacement, status);
11178 else
11180 // Replace n-th occurrence of match with replacement.
11181 sal_Int32 nCount = 0;
11182 while (aRegexMatcher.find(status) && U_SUCCESS(status))
11184 // XXX NOTE: After several RegexMatcher::find() the
11185 // RegexMatcher::appendReplacement() still starts at the
11186 // beginning (or after the last appendReplacement() position
11187 // which is none here) and copies the original text up to the
11188 // current found match and then replaces the found match.
11189 if (++nCount == nOccurrence)
11191 aRegexMatcher.appendReplacement( aReplaced, aIcuReplacement, status);
11192 break;
11195 aRegexMatcher.appendTail( aReplaced);
11197 if (U_FAILURE(status))
11199 // Some error, e.g. extraneous $1 without group.
11200 PushIllegalArgument();
11201 return;
11203 OUString aResult( reinterpret_cast<const sal_Unicode*>(aReplaced.getBuffer()), aReplaced.length());
11204 PushString( aResult);
11207 void ScInterpreter::ScMid()
11209 if ( !MustHaveParamCount( GetByte(), 3 ) )
11210 return;
11212 const sal_Int32 nSubLen = GetStringPositionArgument();
11213 const sal_Int32 nStart = GetStringPositionArgument();
11214 OUString aStr = GetString().getString();
11215 if ( nStart < 1 || nSubLen < 0 )
11216 PushIllegalArgument();
11217 else if (nStart > kScInterpreterMaxStrLen || nSubLen > kScInterpreterMaxStrLen)
11218 PushError(FormulaError::StringOverflow);
11219 else
11221 sal_Int32 nLen = aStr.getLength();
11222 sal_Int32 nIdx = 0;
11223 sal_Int32 nCnt = 0;
11224 while ( nIdx < nLen && nStart - 1 > nCnt )
11226 aStr.iterateCodePoints( &nIdx );
11227 ++nCnt;
11229 sal_Int32 nIdx0 = nIdx; //start position
11231 while ( nIdx < nLen && nStart + nSubLen - 1 > nCnt )
11233 aStr.iterateCodePoints( &nIdx );
11234 ++nCnt;
11236 aStr = aStr.copy( nIdx0, nIdx - nIdx0 );
11237 PushString( aStr );
11241 void ScInterpreter::ScText()
11243 if ( !MustHaveParamCount( GetByte(), 2 ) )
11244 return;
11246 OUString sFormatString = GetString().getString();
11247 svl::SharedString aStr;
11248 bool bString = false;
11249 double fVal = 0.0;
11250 switch (GetStackType())
11252 case svError:
11253 PopError();
11254 break;
11255 case svDouble:
11256 fVal = PopDouble();
11257 break;
11258 default:
11260 FormulaConstTokenRef xTok( PopToken());
11261 if (nGlobalError == FormulaError::NONE)
11263 PushTokenRef( xTok);
11264 // Temporarily override the ConvertStringToValue()
11265 // error for GetCellValue() / GetCellValueOrZero()
11266 FormulaError nSErr = mnStringNoValueError;
11267 mnStringNoValueError = FormulaError::NotNumericString;
11268 fVal = GetDouble();
11269 mnStringNoValueError = nSErr;
11270 if (nGlobalError == FormulaError::NotNumericString)
11272 // Not numeric.
11273 nGlobalError = FormulaError::NONE;
11274 PushTokenRef( xTok);
11275 aStr = GetString();
11276 bString = true;
11281 if (nGlobalError != FormulaError::NONE)
11282 PushError( nGlobalError);
11283 else if (sFormatString.isEmpty())
11285 // Mimic the Excel behaviour that
11286 // * anything numeric returns an empty string
11287 // * text convertible to numeric returns an empty string
11288 // * any other text returns that text
11289 // Conversion was detected above.
11290 if (bString)
11291 PushString( aStr);
11292 else
11293 PushString( OUString());
11295 else
11297 OUString aResult;
11298 const Color* pColor = nullptr;
11299 LanguageType eCellLang;
11300 const ScPatternAttr* pPattern = mrDoc.GetPattern(
11301 aPos.Col(), aPos.Row(), aPos.Tab() );
11302 if ( pPattern )
11303 eCellLang = pPattern->GetItem( ATTR_LANGUAGE_FORMAT ).GetValue();
11304 else
11305 eCellLang = ScGlobal::eLnge;
11306 if (bString)
11308 if (!mrContext.NFGetPreviewString( sFormatString, aStr.getString(),
11309 aResult, &pColor, eCellLang))
11310 PushIllegalArgument();
11311 else
11312 PushString( aResult);
11314 else
11316 if (!mrContext.NFGetPreviewStringGuess( sFormatString, fVal,
11317 aResult, &pColor, eCellLang))
11318 PushIllegalArgument();
11319 else
11320 PushString( aResult);
11325 void ScInterpreter::ScSubstitute()
11327 sal_uInt8 nParamCount = GetByte();
11328 if ( !MustHaveParamCount( nParamCount, 3, 4 ) )
11329 return;
11331 sal_Int32 nCnt;
11332 if (nParamCount == 4)
11334 nCnt = GetStringPositionArgument();
11335 if (nCnt < 1)
11337 PushIllegalArgument();
11338 return;
11341 else
11342 nCnt = 0;
11343 OUString sNewStr = GetString().getString();
11344 OUString sOldStr = GetString().getString();
11345 OUString sStr = GetString().getString();
11346 sal_Int32 nPos = 0;
11347 sal_Int32 nCount = 0;
11348 std::optional<OUStringBuffer> oResult;
11349 for (sal_Int32 nEnd = sStr.indexOf(sOldStr); nEnd >= 0; nEnd = sStr.indexOf(sOldStr, nEnd))
11351 if (nCnt == 0 || ++nCount == nCnt) // Found a replacement cite
11353 if (!oResult) // Only allocate buffer when needed
11354 oResult.emplace(sStr.getLength() + sNewStr.getLength() - sOldStr.getLength());
11356 oResult->append(sStr.subView(nPos, nEnd - nPos)); // Copy leading unchanged text
11357 if (!CheckStringResultLen(*oResult, sNewStr.getLength()))
11358 return PushError(GetError());
11359 oResult->append(sNewStr); // Copy the replacement
11360 nPos = nEnd + sOldStr.getLength();
11361 if (nCnt > 0) // Found the single replacement site - end the loop
11362 break;
11364 nEnd += sOldStr.getLength();
11366 if (oResult) // If there were prior replacements, copy the rest, otherwise use original
11367 oResult->append(sStr.subView(nPos, sStr.getLength() - nPos));
11368 PushString(oResult ? oResult->makeStringAndClear() : sStr);
11371 void ScInterpreter::ScRept()
11373 if ( !MustHaveParamCount( GetByte(), 2 ) )
11374 return;
11376 sal_Int32 nCnt = GetStringPositionArgument();
11377 OUString aStr = GetString().getString();
11378 if (nCnt < 0)
11379 PushIllegalArgument();
11380 else if (static_cast<double>(nCnt) * aStr.getLength() > kScInterpreterMaxStrLen)
11382 PushError( FormulaError::StringOverflow );
11384 else if (nCnt == 0)
11385 PushString( OUString() );
11386 else
11388 const sal_Int32 nLen = aStr.getLength();
11389 OUStringBuffer aRes(nCnt*nLen);
11390 while( nCnt-- )
11391 aRes.append(aStr);
11392 PushString( aRes.makeStringAndClear() );
11396 void ScInterpreter::ScConcat()
11398 sal_uInt8 nParamCount = GetByte();
11400 //reverse order of parameter stack to simplify processing
11401 ReverseStack(nParamCount);
11403 OUStringBuffer aRes;
11404 while( nParamCount-- > 0)
11406 OUString aStr = GetString().getString();
11407 if (CheckStringResultLen(aRes, aStr.getLength()))
11408 aRes.append(aStr);
11409 else
11410 break;
11412 PushString( aRes.makeStringAndClear() );
11415 FormulaError ScInterpreter::GetErrorType()
11417 FormulaError nErr;
11418 FormulaError nOldError = nGlobalError;
11419 nGlobalError = FormulaError::NONE;
11420 switch ( GetStackType() )
11422 case svRefList :
11424 FormulaConstTokenRef x = PopToken();
11425 if (nGlobalError != FormulaError::NONE)
11426 nErr = nGlobalError;
11427 else
11429 const ScRefList* pRefList = x->GetRefList();
11430 size_t n = pRefList->size();
11431 if (!n)
11432 nErr = FormulaError::NoRef;
11433 else if (n > 1)
11434 nErr = FormulaError::NoValue;
11435 else
11437 ScRange aRange;
11438 DoubleRefToRange( (*pRefList)[0], aRange);
11439 if (nGlobalError != FormulaError::NONE)
11440 nErr = nGlobalError;
11441 else
11443 ScAddress aAdr;
11444 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
11445 nErr = mrDoc.GetErrCode( aAdr );
11446 else
11447 nErr = nGlobalError;
11452 break;
11453 case svDoubleRef :
11455 ScRange aRange;
11456 PopDoubleRef( aRange );
11457 if ( nGlobalError != FormulaError::NONE )
11458 nErr = nGlobalError;
11459 else
11461 ScAddress aAdr;
11462 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
11463 nErr = mrDoc.GetErrCode( aAdr );
11464 else
11465 nErr = nGlobalError;
11468 break;
11469 case svSingleRef :
11471 ScAddress aAdr;
11472 PopSingleRef( aAdr );
11473 if ( nGlobalError != FormulaError::NONE )
11474 nErr = nGlobalError;
11475 else
11476 nErr = mrDoc.GetErrCode( aAdr );
11478 break;
11479 default:
11480 PopError();
11481 nErr = nGlobalError;
11483 nGlobalError = nOldError;
11484 return nErr;
11487 void ScInterpreter::ScErrorType()
11489 FormulaError nErr = GetErrorType();
11490 if ( nErr != FormulaError::NONE )
11492 nGlobalError = FormulaError::NONE;
11493 PushDouble( static_cast<double>(nErr) );
11495 else
11497 PushNA();
11501 void ScInterpreter::ScErrorType_ODF()
11503 FormulaError nErr = GetErrorType();
11504 sal_uInt16 nErrType;
11506 switch ( nErr )
11508 case FormulaError::NoCode : // #NULL!
11509 nErrType = 1;
11510 break;
11511 case FormulaError::DivisionByZero : // #DIV/0!
11512 nErrType = 2;
11513 break;
11514 case FormulaError::NoValue : // #VALUE!
11515 nErrType = 3;
11516 break;
11517 case FormulaError::NoRef : // #REF!
11518 nErrType = 4;
11519 break;
11520 case FormulaError::NoName : // #NAME?
11521 nErrType = 5;
11522 break;
11523 case FormulaError::IllegalFPOperation : // #NUM!
11524 nErrType = 6;
11525 break;
11526 case FormulaError::NotAvailable : // #N/A
11527 nErrType = 7;
11528 break;
11530 #GETTING_DATA is a message that can appear in Excel when a large or
11531 complex worksheet is being calculated. In Excel 2007 and newer,
11532 operations are grouped so more complicated cells may finish after
11533 earlier ones do. While the calculations are still processing, the
11534 unfinished cells may display #GETTING_DATA.
11535 Because the message is temporary and disappears when the calculations
11536 complete, this isn’t a true error.
11537 No calc error code known (yet).
11539 case : // GETTING_DATA
11540 nErrType = 8;
11541 break;
11543 default :
11544 nErrType = 0;
11545 break;
11548 if ( nErrType )
11550 nGlobalError =FormulaError::NONE;
11551 PushDouble( nErrType );
11553 else
11554 PushNA();
11557 static bool MayBeRegExp( std::u16string_view rStr )
11559 if ( rStr.empty() || (rStr.size() == 1 && rStr[0] != '.') )
11560 return false; // single meta characters can not be a regexp
11561 // First two characters are wildcard '?' and '*' characters.
11562 std::u16string_view cre(u"?*+.[]^$\\<>()|");
11563 return rStr.find_first_of(cre) != std::u16string_view::npos;
11566 static bool MayBeWildcard( std::u16string_view rStr )
11568 // Wildcards with '~' escape, if there are no wildcards then an escaped
11569 // character does not make sense, but it modifies the search pattern in an
11570 // Excel compatible wildcard search...
11571 std::u16string_view cw(u"*?~");
11572 return rStr.find_first_of(cw) != std::u16string_view::npos;
11575 utl::SearchParam::SearchType ScInterpreter::DetectSearchType( std::u16string_view rStr, const ScDocument& rDoc )
11577 const auto eType = rDoc.GetDocOptions().GetFormulaSearchType();
11578 if ((eType == utl::SearchParam::SearchType::Wildcard && MayBeWildcard(rStr))
11579 || (eType == utl::SearchParam::SearchType::Regexp && MayBeRegExp(rStr)))
11580 return eType;
11581 return utl::SearchParam::SearchType::Normal;
11584 bool ScInterpreter::SearchMatrixForValue( VectorSearchArguments& vsa, ScQueryParam& rParam, ScQueryEntry& rEntry, ScQueryEntry::Item& rItem )
11586 SCSIZE nC, nR;
11587 vsa.pMatSrc->GetDimensions( nC, nR);
11588 if (nC > 1 && nR > 1)
11590 // The source matrix must be a vector.
11591 PushIllegalParameter();
11592 return false;
11594 vsa.bVLookup = ( nC == 1 );
11596 // Do not propagate errors from matrix while searching.
11597 vsa.pMatSrc->SetErrorInterpreter( nullptr );
11599 SCSIZE nMatCount = (vsa.bVLookup ? nR : nC);
11600 VectorMatrixAccessor aMatAcc(*(vsa.pMatSrc), vsa.bVLookup);
11601 bool bMatchWholeCell = mrDoc.GetDocOptions().IsMatchWholeCell();
11603 switch ( vsa.eSearchMode )
11605 case searchfwd :
11607 switch ( vsa.eMatchMode )
11609 case exactorNA :
11610 case wildcard :
11611 // simple serial search for equality mode (source data doesn't
11612 // need to be sorted).
11613 for (SCSIZE i = 0; i < nMatCount; ++i)
11615 if (lcl_CompareMatrix2Query( i, aMatAcc, rParam, rEntry, bMatchWholeCell ) == 0)
11617 vsa.nHitIndex = i+1; // found !
11618 break;
11621 break;
11623 case exactorS :
11624 case exactorG :
11625 for (SCSIZE i = 0; i < nMatCount; ++i)
11627 sal_Int32 result = lcl_CompareMatrix2Query( i, aMatAcc, rParam, rEntry, bMatchWholeCell );
11628 if (result == 0)
11630 vsa.nHitIndex = i+1; // found !
11631 break;
11633 else if (vsa.eMatchMode == exactorS && result == -1)
11635 if ( vsa.nBestFit == SCSIZE_MAX )
11636 vsa.nBestFit = i;
11637 else
11639 // replace value of vsa.nBestFit if value(i) > value(vsa.nBestFit)
11640 if ( lcl_Compare2MatrixCells( i, aMatAcc, vsa.nBestFit) == 1 )
11641 vsa.nBestFit = i;
11644 else if (vsa.eMatchMode == exactorG && result == 1)
11646 if ( vsa.nBestFit == SCSIZE_MAX )
11647 vsa.nBestFit = i;
11648 else
11650 // replace value of vsa.nBestFit if value(i) < value(vsa.nBestFit)
11651 if ( lcl_Compare2MatrixCells( i, aMatAcc, vsa.nBestFit) == -1 )
11652 vsa.nBestFit = i;
11655 // else do nothing
11657 break;
11659 default :
11660 PushIllegalParameter();
11661 return false;
11664 break;
11666 case searchrev:
11668 switch ( vsa.eMatchMode )
11670 case exactorNA :
11671 case wildcard :
11672 // simple serial search for equality mode (source data doesn't
11673 // need to be sorted).
11674 for ( SCSIZE i = nMatCount - 1; i > 0; i-- )
11676 if (lcl_CompareMatrix2Query(i, aMatAcc, rParam, rEntry, bMatchWholeCell) == 0)
11678 vsa.nHitIndex = i + 1; // found !
11679 break;
11682 break;
11684 case exactorS :
11685 case exactorG :
11686 for (SCSIZE i = nMatCount - 1; i-- > 0; )
11688 sal_Int32 result = lcl_CompareMatrix2Query( i, aMatAcc, rParam, rEntry, bMatchWholeCell );
11689 if (result == 0)
11691 vsa.nHitIndex = i + 1; // found !
11692 break;
11694 else if (vsa.eMatchMode == exactorS && result == -1)
11696 if ( vsa.nBestFit == SCSIZE_MAX )
11697 vsa.nBestFit = i;
11698 else
11700 // replace value of vsa.nBestFit if value(i) > value(vsa.nBestFit)
11701 if ( lcl_Compare2MatrixCells( i, aMatAcc, vsa.nBestFit) == 1 )
11702 vsa.nBestFit = i;
11705 else if (vsa.eMatchMode == exactorG && result == 1)
11707 if ( vsa.nBestFit == SCSIZE_MAX )
11708 vsa.nBestFit = i;
11709 else
11711 // replace value of vsa.nBestFit if value(i) < value(vsa.nBestFit)
11712 if ( lcl_Compare2MatrixCells( i, aMatAcc, vsa.nBestFit) == -1 )
11713 vsa.nBestFit = i;
11716 // else do nothing
11718 break;
11720 default :
11721 PushIllegalParameter();
11722 return false;
11725 break;
11727 case searchbasc:
11728 case searchbdesc:
11730 // binary search for non-equality mode (the source data is sorted)
11731 bool bAscOrder = ( vsa.eSearchMode == searchbasc );
11732 SCSIZE nFirst = 0;
11733 SCSIZE nLast = nMatCount - 1;
11734 for ( SCSIZE nLen = nLast - nFirst; nLen > 0; nLen = nLast - nFirst )
11736 SCSIZE nMid = nFirst + nLen / 2;
11737 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rParam, rEntry, bMatchWholeCell );
11738 if ( nCmp == 0 )
11740 // exact match. find the last item with the same value.
11741 lcl_GetLastMatch( nMid, aMatAcc, nMatCount);
11742 vsa.nHitIndex = nMid + 1;
11743 break;
11746 if ( nLen == 1 ) // first and last items are next to each other.
11748 if ( bAscOrder && vsa.eMatchMode == exactorS )
11749 vsa.nHitIndex = ( nCmp > 0 ? nFirst : nLast );
11750 else if ( !bAscOrder && vsa.eMatchMode == exactorG )
11751 vsa.nHitIndex = ( nCmp < 0 ? nFirst : nLast );
11752 break;
11754 else
11756 if ( nCmp < 0 )
11758 if ( bAscOrder )
11759 nFirst = nMid;
11760 else
11761 nLast = nMid;
11763 else
11765 if ( bAscOrder )
11766 nLast = nMid;
11767 else
11768 nFirst = nMid;
11771 if ( vsa.nHitIndex == nMatCount - 1 ) // last item
11773 nCmp = lcl_CompareMatrix2Query( vsa.nHitIndex, aMatAcc, rParam, rEntry, bMatchWholeCell );
11774 if ( ( vsa.eMatchMode == exactorS && nCmp <= 0 ) ||
11775 ( vsa.eMatchMode == exactorG && nCmp >= 0 ) )
11777 // either the last item is an exact match or the real
11778 // hit is beyond the last item.
11779 vsa.nHitIndex++;
11781 else
11782 vsa.nHitIndex = 0;
11787 break;
11789 default:
11790 PushIllegalParameter();
11791 return false;
11794 if ((vsa.nHitIndex > 0) && ((rItem.meType == ScQueryEntry::ByString && aMatAcc.IsValue(vsa.nHitIndex - 1)) ||
11795 (rItem.meType == ScQueryEntry::ByValue && !aMatAcc.IsValue(vsa.nHitIndex - 1))))
11797 vsa.nHitIndex = 0;
11798 vsa.isResultNA = true;
11799 return false;
11801 return true;
11804 bool ScInterpreter::SearchRangeForValue( VectorSearchArguments& vsa, ScQueryParam& rParam, ScQueryEntry& rEntry )
11806 vsa.bVLookup = ( vsa.nCol1 == vsa.nCol2 );
11807 switch ( vsa.eSearchMode )
11809 case searchfwd:
11810 case searchrev:
11811 case searchbasc:
11812 case searchbdesc:
11814 if (vsa.bVLookup)
11816 // search of rows in column
11817 rParam.bByRow = true;
11818 ScAddress aResultPos( vsa.nCol1, vsa.nRow1, vsa.nTab1 );
11819 const ScComplexRefData* refData = nullptr;
11820 if ( LookupQueryWithCache( aResultPos, rParam, refData, vsa.eSearchMode, vsa.nSearchOpCode ) )
11821 vsa.nHitIndex = aResultPos.Row() - vsa.nRow1 + 1;
11823 else
11825 rParam.bByRow = false;
11826 bool bBinarySearch = vsa.eSearchMode == searchbasc || vsa.eSearchMode == searchbdesc;
11827 if (bBinarySearch && (vsa.nSearchOpCode == SC_OPCODE_X_LOOKUP || vsa.nSearchOpCode == SC_OPCODE_X_MATCH))
11829 ScQueryCellIteratorSortedCache aCellIter(mrDoc, mrContext, rParam.nTab, rParam, false, false);
11830 // Advance Entry.nField in Iterator if column changed
11831 aCellIter.SetAdvanceQueryParamEntryField(true);
11832 aCellIter.SetSortedBinarySearchMode(vsa.eSearchMode);
11833 aCellIter.SetLookupMode(vsa.nSearchOpCode);
11834 if (aCellIter.GetFirst())
11836 vsa.nHitIndex = aCellIter.GetCol() - vsa.nCol1 + 1;
11839 else
11841 // search of columns in row
11842 bool bReverseSearch = (vsa.eSearchMode == searchrev);
11843 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, vsa.nTab1, rParam, false, bReverseSearch);
11844 // Advance Entry.nField in Iterator if column changed
11845 aCellIter.SetAdvanceQueryParamEntryField(true);
11846 aCellIter.SetLookupMode(vsa.nSearchOpCode);
11847 aCellIter.SetSortedBinarySearchMode(vsa.eSearchMode);
11848 if (rEntry.eOp == SC_EQUAL)
11850 if (aCellIter.GetFirst())
11851 vsa.nHitIndex = aCellIter.GetCol() - vsa.nCol1 + 1;
11853 else
11855 SCCOL nC;
11856 SCROW nR;
11857 if (aCellIter.FindEqualOrSortedLastInRange(nC, nR))
11858 vsa.nHitIndex = nC - vsa.nCol1 + 1;
11863 break;
11865 default :
11866 PushIllegalParameter();
11867 return false;
11869 return true;
11873 /** When search value is found, the index is stored in struct VectorSearchArguments.nIndex
11874 and SearchVectorForValue() returns true. When search value is not found or an error
11875 occurs, SearchVectorForValue() pushes the relevant (error)message and returns false,
11876 expect when SearchVectorForValue() is called by ScXLookup and the search value is not
11877 found.
11878 This difference in behaviour is because MATCH returns the found index and XLOOKUP
11879 uses the found index to determine the result(s) to be pushed and may return a custom
11880 value when the search value is not found.
11882 bool ScInterpreter::SearchVectorForValue( VectorSearchArguments& vsa )
11884 // preparations
11885 ScQueryParam rParam;
11886 rParam.nCol1 = vsa.nCol1;
11887 rParam.nRow1 = vsa.nRow1;
11888 rParam.nCol2 = vsa.nCol2;
11889 rParam.nRow2 = vsa.nRow2;
11890 rParam.nTab = vsa.nTab1;
11892 ScQueryEntry& rEntry = rParam.GetEntry(0);
11893 rEntry.nField = vsa.eSearchMode != searchrev ? vsa.nCol1 : vsa.nCol2;
11894 rEntry.bDoQuery = true;
11895 switch ( vsa.eMatchMode )
11897 case exactorNA :
11898 rEntry.eOp = SC_EQUAL;
11899 break;
11901 case exactorS :
11902 rEntry.eOp = SC_LESS_EQUAL;
11903 break;
11905 case exactorG :
11906 rEntry.eOp = SC_GREATER_EQUAL;
11907 break;
11909 case wildcard :
11910 case regex :
11911 // this mode can only used with XLOOKUP/XMATCH
11912 if ( vsa.nSearchOpCode == SC_OPCODE_X_LOOKUP || vsa.nSearchOpCode == SC_OPCODE_X_MATCH )
11914 // Wildcard/Regex search mode with binary search is not allowed
11915 if (vsa.eSearchMode == searchbasc || vsa.eSearchMode == searchbdesc)
11917 PushNoValue();
11918 return false;
11921 rEntry.eOp = SC_EQUAL;
11922 if ( vsa.isStringSearch )
11924 if (vsa.eMatchMode == wildcard && MayBeWildcard(vsa.sSearchStr.getString()))
11925 rParam.eSearchType = utl::SearchParam::SearchType::Wildcard;
11926 else if (vsa.eMatchMode == regex && MayBeRegExp(vsa.sSearchStr.getString()))
11927 rParam.eSearchType = utl::SearchParam::SearchType::Regexp;
11928 else
11929 rParam.eSearchType = utl::SearchParam::SearchType::Normal;
11932 else
11934 PushIllegalParameter();
11935 return false;
11937 break;
11939 default :
11940 PushIllegalParameter();
11941 return false;
11944 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
11945 // allow to match empty cells as result if we are looking for the next smaller
11946 // or larger values in case of the new lookup functions
11947 if (rEntry.eOp != SC_EQUAL && (vsa.nSearchOpCode == SC_OPCODE_X_LOOKUP ||
11948 vsa.nSearchOpCode == SC_OPCODE_X_MATCH))
11949 rItem.mbMatchEmpty = true;
11951 if ( vsa.isStringSearch )
11953 rItem.meType = ScQueryEntry::ByString;
11954 rItem.maString = vsa.sSearchStr;
11955 if ( vsa.nSearchOpCode == SC_OPCODE_MATCH )
11957 if ( mrDoc.IsInVBAMode() )
11958 rParam.eSearchType = utl::SearchParam::SearchType::Wildcard;
11959 else
11960 rParam.eSearchType = DetectSearchType(rEntry.GetQueryItem().maString.getString(), mrDoc);
11963 else if ( vsa.isEmptySearch && (vsa.nSearchOpCode == SC_OPCODE_X_LOOKUP ||
11964 vsa.nSearchOpCode == SC_OPCODE_X_MATCH) )
11966 rEntry.SetQueryByEmpty();
11967 rItem.mbMatchEmpty = true;
11969 else
11971 rItem.mfVal = vsa.fSearchVal;
11972 rItem.meType = ScQueryEntry::ByValue;
11975 // execute search
11976 if (vsa.pMatSrc) // The source data is matrix array.
11978 // matrix
11979 if ( !SearchMatrixForValue( vsa, rParam, rEntry, rItem ) )
11980 return false;
11982 else
11984 // not a matrix
11985 if ( !SearchRangeForValue( vsa, rParam, rEntry ) )
11986 return false;
11989 // MATCH expects index starting with 1, XLOOKUP expects index starting with 0
11990 if ( vsa.nHitIndex > 0 )
11992 vsa.nIndex = ( vsa.nSearchOpCode == SC_OPCODE_X_LOOKUP ? --vsa.nHitIndex : vsa.nHitIndex );
11993 return true;
11995 else if ( vsa.nHitIndex == 0 && vsa.nBestFit != SCSIZE_MAX )
11997 if ( vsa.nSearchOpCode == SC_OPCODE_X_LOOKUP )
11999 vsa.nIndex = vsa.nBestFit;
12000 if ( !vsa.pMatSrc )
12002 vsa.nIndex -= ( vsa.bVLookup ? vsa.nRow1 : vsa.nCol1 );
12005 else
12007 vsa.nIndex = ++vsa.nBestFit;
12009 return true;
12012 // nomatch
12013 vsa.isResultNA = true;
12014 return false;
12017 static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument& rDoc, ScInterpreterContext& rContext,
12018 const ScQueryParam & rParam, const ScQueryEntry & rEntry, const ScFormulaCell* cell,
12019 const ScComplexRefData* refData, sal_Int8 nSearchMode, sal_uInt16 nOpCode )
12021 if (rEntry.eOp != SC_EQUAL)
12023 // range lookup <= or >=
12024 SCCOL nCol;
12025 SCROW nRow;
12026 bool bBinarySearch = static_cast<SearchMode>(nSearchMode) == searchbasc || static_cast<SearchMode>(nSearchMode) == searchbdesc;
12027 if ((bBinarySearch && (nOpCode == SC_OPCODE_X_LOOKUP || nOpCode == SC_OPCODE_X_MATCH)) ||
12028 ScQueryCellIteratorSortedCache::CanBeUsed(rDoc, rParam, rParam.nTab, cell, refData, rContext))
12030 ScQueryCellIteratorSortedCache aCellIter(rDoc, rContext, rParam.nTab, rParam, false, false);
12031 aCellIter.SetSortedBinarySearchMode(nSearchMode);
12032 aCellIter.SetLookupMode(nOpCode);
12033 if (aCellIter.GetFirst())
12035 o_rResultPos.SetCol(aCellIter.GetCol());
12036 o_rResultPos.SetRow(aCellIter.GetRow());
12037 return true;
12040 else
12042 bool bReverse = (static_cast<SearchMode>(nSearchMode) == searchrev);
12043 ScQueryCellIteratorDirect aCellIter(rDoc, rContext, rParam.nTab, rParam, false, bReverse);
12045 aCellIter.SetSortedBinarySearchMode(nSearchMode);
12046 aCellIter.SetLookupMode(nOpCode);
12047 if (aCellIter.FindEqualOrSortedLastInRange(nCol, nRow))
12049 o_rResultPos.SetCol(nCol);
12050 o_rResultPos.SetRow(nRow);
12051 return true;
12055 else // EQUAL
12057 // we can use binary search for rows if the SearchMode is searchbasc or searchbdesc
12058 bool bLiteral = rParam.eSearchType == utl::SearchParam::SearchType::Normal &&
12059 rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
12060 bool bBinary = rParam.bByRow &&
12061 (bLiteral || rEntry.GetQueryItem().meType == ScQueryEntry::ByValue);
12063 if( bBinary && (static_cast<SearchMode>(nSearchMode) == searchbasc || static_cast<SearchMode>(nSearchMode) == searchbdesc ||
12064 ScQueryCellIteratorSortedCache::CanBeUsed(rDoc, rParam, rParam.nTab, cell, refData, rContext)))
12066 ScQueryCellIteratorSortedCache aCellIter( rDoc, rContext, rParam.nTab, rParam, false, false );
12067 aCellIter.SetSortedBinarySearchMode(nSearchMode);
12068 aCellIter.SetLookupMode(nOpCode);
12069 if (aCellIter.GetFirst())
12071 o_rResultPos.SetCol( aCellIter.GetCol());
12072 o_rResultPos.SetRow( aCellIter.GetRow());
12073 return true;
12076 else
12078 ScQueryCellIteratorDirect aCellIter( rDoc, rContext, rParam.nTab, rParam, false,
12079 static_cast<SearchMode>(nSearchMode) == searchrev);
12080 aCellIter.SetSortedBinarySearchMode(nSearchMode);
12081 aCellIter.SetLookupMode(nOpCode);
12082 if (aCellIter.GetFirst())
12084 o_rResultPos.SetCol( aCellIter.GetCol());
12085 o_rResultPos.SetRow( aCellIter.GetRow());
12086 return true;
12090 return false;
12093 // tdf#121052:
12094 // =VLOOKUP(SearchCriterion; RangeArray; Index; Sorted)
12095 // [SearchCriterion] is the value searched for in the first column of the array.
12096 // [RangeArray] is the reference, which is to comprise at least two columns.
12097 // [Index] is the number of the column in the array that contains the value to be returned. The first column has the number 1.
12099 // Prerequisite of lcl_getPrevRowWithEmptyValueLookup():
12100 // Value referenced by [SearchCriterion] is empty.
12101 // lcl_getPrevRowWithEmptyValueLookup() performs following checks:
12102 // - if we run query with "exact match" mode (i.e. VLOOKUP)
12103 // - and if we already have the same lookup done before but for another row
12104 // which is also had empty [SearchCriterion]
12106 // then
12107 // we could say, that for current row we could reuse results of the cached call which was done for the row2
12108 // In this case we return row index, which is >= 0.
12110 // Elsewhere
12111 // -1 is returned, which will lead to default behavior =>
12112 // complete lookup will be done in RangeArray inside lcl_LookupQuery() method.
12114 // This method was added only for speed up to avoid several useless complete
12115 // lookups inside [RangeArray] for searching empty strings.
12117 static SCROW lcl_getPrevRowWithEmptyValueLookup( const ScLookupCache& rCache,
12118 const ScLookupCache::QueryCriteria& rCriteria, const ScQueryParam & rParam)
12120 // is lookup value empty?
12121 const ScQueryEntry& rEntry = rParam.GetEntry(0);
12122 const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
12123 if (! rItem.maString.getString().isEmpty())
12124 return -1; // not found
12126 // try to find the row index for which we have already performed lookup
12127 // and have some result of it inside cache
12128 return rCache.lookup( rCriteria );
12131 bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos,
12132 const ScQueryParam & rParam, const ScComplexRefData* refData,
12133 sal_Int8 nSearchMode, sal_uInt16 nOpCode ) const
12135 bool bFound = false;
12136 const ScQueryEntry& rEntry = rParam.GetEntry(0);
12137 bool bColumnsMatch = (rParam.nCol1 == rEntry.nField);
12138 // At least all volatile functions that generate indirect references have
12139 // to force non-cached lookup.
12140 /* TODO: We could further classify volatile functions into reference
12141 * generating and not reference generating functions to have to force less
12142 * direct lookups here. We could even further attribute volatility per
12143 * parameter so it would affect only the lookup range parameter. */
12144 if (!bColumnsMatch || GetVolatileType() != NOT_VOLATILE)
12145 bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry, pMyFormulaCell,
12146 refData, nSearchMode, nOpCode );
12147 else
12149 ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab,
12150 rParam.nCol2, rParam.nRow2, rParam.nTab);
12151 ScLookupCache& rCache = mrDoc.GetLookupCache( aLookupRange, &mrContext );
12152 ScLookupCache::QueryCriteria aCriteria( rEntry );
12153 ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos,
12154 aCriteria, aPos);
12156 // tdf#121052: Slow load of cells with VLOOKUP with references to empty cells
12157 // This check was added only for speed up to avoid several useless complete
12158 // lookups inside [RangeArray] for searching empty strings.
12159 if (eCacheResult == ScLookupCache::NOT_CACHED && aCriteria.isEmptyStringQuery())
12161 const SCROW nPrevRowWithEmptyValueLookup = lcl_getPrevRowWithEmptyValueLookup(rCache, aCriteria, rParam);
12162 if (nPrevRowWithEmptyValueLookup >= 0)
12164 // make the same lookup using cache with different row index
12165 // (this lookup was already cached)
12166 ScAddress aPosPrev(aPos);
12167 aPosPrev.SetRow(nPrevRowWithEmptyValueLookup);
12169 eCacheResult = rCache.lookup( o_rResultPos, aCriteria, aPosPrev );
12173 switch (eCacheResult)
12175 case ScLookupCache::NOT_CACHED :
12176 case ScLookupCache::CRITERIA_DIFFERENT :
12177 bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry,
12178 pMyFormulaCell, refData, nSearchMode, nOpCode );
12179 if (eCacheResult == ScLookupCache::NOT_CACHED)
12180 rCache.insert( o_rResultPos, aCriteria, aPos, bFound);
12181 break;
12182 case ScLookupCache::FOUND :
12183 bFound = true;
12184 break;
12185 case ScLookupCache::NOT_AVAILABLE :
12186 ; // nothing, bFound remains FALSE
12187 break;
12190 return bFound;
12193 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */