1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
48 #include <document.hxx>
49 #include <dociter.hxx>
51 #include <sfx2/linkmgr.hxx>
52 #include <formulacell.hxx>
53 #include <scmatrix.hxx>
54 #include <docoptio.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>
77 #include <string_view>
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() )
96 ScMatrixRef pMat
= PopMatrix();
98 PushIllegalParameter();
101 FormulaConstTokenRef xNew
;
102 ScTokenMatrixMap::const_iterator aMapIter
;
103 // DoubleError handled by JumpMatrix
104 pMat
->SetErrorInterpreter( nullptr);
106 pMat
->GetDimensions( nCols
, nRows
);
107 if ( nCols
== 0 || nRows
== 0 )
109 PushIllegalArgument();
112 else if ((aMapIter
= maTokenMatrixMap
.find( pCur
)) != maTokenMatrixMap
.end())
113 xNew
= (*aMapIter
).second
;
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
)
124 bool bIsValue
= pMat
->IsValue(nC
, nR
);
127 fVal
= pMat
->GetDouble(nC
, nR
);
128 bIsValue
= std::isfinite(fVal
);
129 bTrue
= bIsValue
&& (fVal
!= 0.0);
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,
139 bIsValue
= pMat
->IsValueOrEmpty(nC
, nR
);
141 fVal
= (bIsValue
? 0.0 : CreateDoubleError( FormulaError::NoValue
));
145 if( nJumpCount
>= 2 )
147 pJumpMat
->SetJump( nC
, nR
, fVal
,
149 pJump
[ nJumpCount
]);
152 { // no parameter given for THEN
153 pJumpMat
->SetJump( nC
, nR
, fVal
,
155 pJump
[ nJumpCount
]);
160 if( nJumpCount
== 3 && bIsValue
)
162 pJumpMat
->SetJump( nC
, nR
, fVal
,
164 pJump
[ nJumpCount
]);
167 { // no parameter given for ELSE,
169 pJumpMat
->SetJump( nC
, nR
, fVal
,
171 pJump
[ nJumpCount
]);
176 xNew
= new ScJumpMatrixToken(std::move(pJumpMat
));
177 GetTokenMatrixMap().emplace(pCur
, xNew
);
181 PushIllegalArgument();
185 // set endpoint of path for main code line
186 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
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
)
200 if( nJumpCount
>= 2 )
202 aCode
.Jump( pJump
[ 1 ], pJump
[ nJumpCount
] );
205 { // no parameter given for THEN
206 nFuncFmtType
= SvNumFormatType::LOGICAL
;
208 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
213 if( nJumpCount
== 3 )
215 aCode
.Jump( pJump
[ 2 ], pJump
[ nJumpCount
] );
218 { // no parameter given for ELSE
219 nFuncFmtType
= SvNumFormatType::LOGICAL
;
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
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
);
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
] );
262 FormulaConstTokenRef
xToken( pStack
[ sp
- 1 ] );
264 FormulaError nOldGlobalError
= nGlobalError
;
265 nGlobalError
= FormulaError::NONE
;
267 MatrixJumpConditionToMatrix();
268 switch (GetStackType())
272 // Act on implicitly propagated error, if any.
273 if (nOldGlobalError
!= FormulaError::NONE
)
274 nGlobalError
= nOldGlobalError
;
275 if (nGlobalError
!= FormulaError::NONE
)
286 if (!PopDoubleRefOrSingleRef( aAdr
))
291 ScRefCellValue
aCell(mrDoc
, aAdr
);
292 nGlobalError
= GetCellErrCode(aCell
);
293 if (nGlobalError
!= FormulaError::NONE
)
298 case svExternalSingleRef
:
299 case svExternalDoubleRef
:
302 svl::SharedString aStr
;
303 // Handles also existing jump matrix case and sets error on
305 GetDoubleOrStringFromMatrix( fVal
, aStr
);
306 if (nGlobalError
!= FormulaError::NONE
)
312 const ScMatrixRef pMat
= PopMatrix();
313 if (!pMat
|| (nGlobalError
!= FormulaError::NONE
&& (!bNAonly
|| nGlobalError
== FormulaError::NotAvailable
)))
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();
323 pMat
->GetDimensions( nCols
, nRows
);
324 if (nCols
== 0 || nRows
== 0)
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
))
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
;
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
)
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
))
380 pJumpMat
->SetJump( nC
, nR
, 1.0, pJump
[ 1 ], pJump
[ nJumpCount
] );
383 { // FALSE, EMPTY path, store result instead
384 lcl_storeJumpMatResult(pMatPtr
, pJumpMat
.get(), nC
, nR
);
389 xNew
= new ScJumpMatrixToken(std::move(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
] );
401 if (bError
&& (!bNAonly
|| nGlobalError
== FormulaError::NotAvailable
))
403 // error, calculate 2nd argument
404 nGlobalError
= FormulaError::NONE
;
405 aCode
.Jump( pJump
[ 1 ], pJump
[ nJumpCount
] );
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
420 bool bHaveJump
= false;
421 const short* pJump
= pCur
->GetJump();
422 short nJumpCount
= pJump
[ 0 ];
423 MatrixJumpConditionToMatrix();
424 switch ( GetStackType() )
428 ScMatrixRef pMat
= PopMatrix();
430 PushIllegalParameter();
433 FormulaConstTokenRef xNew
;
434 ScTokenMatrixMap::const_iterator aMapIter
;
435 // DoubleError handled by JumpMatrix
436 pMat
->SetErrorInterpreter( nullptr);
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
;
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
)
453 bool bIsValue
= pMat
->IsValue(nC
, nR
);
456 fVal
= pMat
->GetDouble(nC
, nR
);
457 bIsValue
= std::isfinite( fVal
);
460 fVal
= ::rtl::math::approxFloor( fVal
);
461 if ( (fVal
< 1) || (fVal
>= nJumpCount
))
464 fVal
= CreateDoubleError(
465 FormulaError::IllegalArgument
);
471 fVal
= CreateDoubleError( FormulaError::NoValue
);
475 pJumpMat
->SetJump( nC
, nR
, fVal
,
476 pJump
[ static_cast<short>(fVal
) ],
477 pJump
[ nJumpCount
]);
481 pJumpMat
->SetJump( nC
, nR
, fVal
,
483 pJump
[ nJumpCount
]);
487 xNew
= new ScJumpMatrixToken(std::move(pJumpMat
));
488 GetTokenMatrixMap().emplace(pCur
, xNew
);
493 // set endpoint of path for main code line
494 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
502 sal_Int16 nJumpIndex
= GetInt16();
503 if (nGlobalError
== FormulaError::NONE
&& (nJumpIndex
>= 1) && (nJumpIndex
< nJumpCount
))
505 aCode
.Jump( pJump
[ static_cast<short>(nJumpIndex
) ], pJump
[ nJumpCount
] );
509 PushIllegalArgument();
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
)))
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
;
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();
550 if ( nStackLevel
== 2 )
552 if ( aCode
.HasStacked() )
553 aCode
.Pop(); // pop what Jump() pushed
556 assert(!"pop goes the weasel");
562 SetError( FormulaError::UnknownStackVariable
);
566 pJumpMatrix
->GetPos( nC
, nR
);
567 switch ( GetStackType() )
571 double fVal
= GetDouble();
572 if ( nGlobalError
!= FormulaError::NONE
)
574 fVal
= CreateDoubleError( nGlobalError
);
575 nGlobalError
= FormulaError::NONE
;
577 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
582 svl::SharedString aStr
= GetString();
583 if ( nGlobalError
!= FormulaError::NONE
)
585 pJumpMatrix
->PutResultDouble( CreateDoubleError( nGlobalError
),
587 nGlobalError
= FormulaError::NONE
;
590 pJumpMatrix
->PutResultString(aStr
, nC
, nR
);
595 FormulaConstTokenRef xRef
= pStack
[sp
-1];
597 PopSingleRef( aAdr
);
598 if ( nGlobalError
!= FormulaError::NONE
)
600 pJumpMatrix
->PutResultDouble( CreateDoubleError( nGlobalError
),
602 nGlobalError
= FormulaError::NONE
;
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(
616 nGlobalError
= FormulaError::NONE
;
618 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
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
;
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
);
647 { // upper left plus offset within matrix
648 FormulaConstTokenRef xRef
= pStack
[sp
-1];
651 PopDoubleRef( aRange
);
652 if ( nGlobalError
!= FormulaError::NONE
)
654 fVal
= CreateDoubleError( nGlobalError
);
655 nGlobalError
= FormulaError::NONE
;
656 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
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
);
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(
694 nGlobalError
= FormulaError::NONE
;
696 pJumpMatrix
->PutResultDouble( fCellVal
, nC
, nR
);
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
;
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()));
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
;
737 switch (pToken
->GetType())
740 pJumpMatrix
->PutResultDouble( pToken
->GetDouble(), nC
, nR
);
743 pJumpMatrix
->PutResultString( pToken
->GetString(), nC
, nR
);
746 pJumpMatrix
->PutResultEmpty( nC
, nR
);
749 // svError was already handled (set by
750 // PopExternalSingleRef()) with nGlobalError
752 assert(!"unhandled svExternalSingleRef case");
753 pJumpMatrix
->PutResultDouble( CreateDoubleError(
754 FormulaError::UnknownStackVariable
), nC
, nR
);
759 case svExternalDoubleRef
:
761 { // match matrix offsets
763 ScMatrixRef pMat
= GetMatrix();
764 if ( nGlobalError
!= FormulaError::NONE
)
766 fVal
= CreateDoubleError( nGlobalError
);
767 nGlobalError
= FormulaError::NONE
;
768 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
772 fVal
= CreateDoubleError( FormulaError::UnknownVariable
);
773 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
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
);
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
);
800 double fVal
= CreateDoubleError( nGlobalError
);
801 nGlobalError
= FormulaError::NONE
;
802 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
808 double fVal
= CreateDoubleError( FormulaError::IllegalArgument
);
809 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
814 bool bCont
= pJumpMatrix
->Next( nC
, nR
);
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
826 pJumpMatrix
->PutResultEmptyPath( nC
, nR
);
828 pJumpMatrix
->PutResultDouble( fBool
, nC
, nR
);
830 bCont
= pJumpMatrix
->Next( nC
, nR
);
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
842 PushWithoutError(*i
);
844 aCode
.Jump( nStart
, nNext
, nStop
);
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;
866 maTokenMatrixMap
.erase( pCur
);
867 // There's no result matrix to remember in this case.
871 ScMatrix
* pResMat
= pJumpMatrix
->GetResultMatrix();
872 pJumpMatrix
= nullptr;
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]);
885 double ScInterpreter::Compare( ScQueryOp 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() )
898 rCell
.mbEmpty
= true;
902 rCell
.mfValue
= GetDouble();
903 rCell
.mbValue
= true;
906 rCell
.maStr
= GetString();
907 rCell
.mbValue
= false;
913 if ( !PopDoubleRefOrSingleRef( aAdr
) )
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
= std::move(aStr
);
923 rCell
.mbValue
= false;
927 rCell
.mfValue
= GetCellValue(aAdr
, aCell
);
928 rCell
.mbValue
= true;
932 case svExternalSingleRef
:
934 ScMatrixRef pMat
= GetMatrix();
937 SetError( FormulaError::IllegalParameter
);
942 pMat
->GetDimensions(nC
, nR
);
945 SetError( FormulaError::IllegalParameter
);
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;
957 rCell
.mfValue
= pMat
->GetDouble(0, 0);
958 rCell
.mbValue
= true;
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.
972 SetError( FormulaError::IllegalParameter
);
976 if( nGlobalError
!= FormulaError::NONE
)
978 nCurFmtType
= nFuncFmtType
= SvNumFormatType::LOGICAL
;
979 return sc::CompareFunc(aComp
);
982 sc::RangeMatrix
ScInterpreter::CompareMat( ScQueryOp eOp
, sc::CompareOptions
* pOptions
)
986 aComp
.mbIgnoreCase
= mrDoc
.GetDocOptions().IsIgnoreCase();
987 sc::RangeMatrix aMat
[2];
989 for( short i
= 1; i
>= 0; i
-- )
991 sc::Compare::Cell
& rCell
= aComp
.maCells
[i
];
993 switch (GetRawStackType())
997 rCell
.mbEmpty
= true;
1001 rCell
.mfValue
= GetDouble();
1002 rCell
.mbValue
= true;
1005 rCell
.maStr
= GetString();
1006 rCell
.mbValue
= false;
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
= std::move(aStr
);
1019 rCell
.mbValue
= false;
1023 rCell
.mfValue
= GetCellValue(aAdr
, aCell
);
1024 rCell
.mbValue
= true;
1028 case svExternalSingleRef
:
1029 case svExternalDoubleRef
:
1032 aMat
[i
] = GetRangeMatrix();
1034 SetError( FormulaError::IllegalParameter
);
1036 aMat
[i
].mpMat
->SetErrorInterpreter(nullptr);
1037 // errors are transported as DoubleError inside matrix
1041 SetError( FormulaError::IllegalParameter
);
1046 sc::RangeMatrix aRes
;
1048 if (nGlobalError
!= FormulaError::NONE
)
1050 nCurFmtType
= nFuncFmtType
= SvNumFormatType::LOGICAL
;
1054 if (aMat
[0].mpMat
&& aMat
[1].mpMat
)
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 );
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
);
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
);
1093 aRes
.mpMat
->PutError( FormulaError::NoValue
, j
, k
);
1100 aRes
.mpMat
->CompareEqual();
1103 aRes
.mpMat
->CompareLess();
1106 aRes
.mpMat
->CompareGreater();
1109 aRes
.mpMat
->CompareLessEqual();
1111 case SC_GREATER_EQUAL
:
1112 aRes
.mpMat
->CompareGreaterEqual();
1115 aRes
.mpMat
->CompareNotEqual();
1118 SAL_WARN("sc", "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp
));
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
);
1140 nCurFmtType
= nFuncFmtType
= SvNumFormatType::LOGICAL
;
1144 ScMatrixRef
ScInterpreter::QueryMat( const ScMatrixRef
& pMat
, sc::CompareOptions
& rOptions
)
1146 SvNumFormatType nSaveCurFmtType
= nCurFmtType
;
1147 SvNumFormatType nSaveFuncFmtType
= nFuncFmtType
;
1149 const ScQueryEntry::Item
& rItem
= rOptions
.aQueryEntry
.GetQueryItem();
1150 if (rItem
.meType
== ScQueryEntry::ByString
)
1151 PushString(rItem
.maString
.getString());
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
);
1173 PushIllegalParameter();
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
);
1190 PushIllegalParameter();
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
);
1207 PushIllegalParameter();
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
);
1224 PushIllegalParameter();
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
);
1241 PushIllegalParameter();
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
);
1258 PushIllegalParameter();
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 ) )
1275 bool bHaveValue
= false;
1277 size_t nRefInList
= 0;
1278 while( nParamCount
-- > 0)
1280 if ( nGlobalError
== FormulaError::NONE
)
1282 switch ( GetStackType() )
1286 bRes
&= ( PopDouble() != 0.0 );
1290 SetError( FormulaError::NoValue
);
1295 PopSingleRef( aAdr
);
1296 if ( nGlobalError
== FormulaError::NONE
)
1298 ScRefCellValue
aCell(mrDoc
, aAdr
);
1299 if (aCell
.hasNumeric())
1302 bRes
&= ( GetCellValue(aAdr
, aCell
) != 0.0 );
1304 // else: Xcl raises no error here
1312 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
1313 if ( nGlobalError
== FormulaError::NONE
)
1316 FormulaError nErr
= FormulaError::NONE
;
1317 ScValueIterator
aValIter( mrContext
, aRange
);
1318 if ( aValIter
.GetFirst( fVal
, nErr
) && nErr
== FormulaError::NONE
)
1323 bRes
&= ( fVal
!= 0.0 );
1324 } while ( (nErr
== FormulaError::NONE
) &&
1325 aValIter
.GetNext( fVal
, nErr
) );
1331 case svExternalSingleRef
:
1332 case svExternalDoubleRef
:
1335 ScMatrixRef pMat
= GetMatrix();
1339 double fVal
= pMat
->And();
1340 FormulaError nErr
= GetDoubleErrorValue( fVal
);
1341 if ( nErr
!= FormulaError::NONE
)
1347 bRes
&= (fVal
!= 0.0);
1349 // else: GetMatrix did set FormulaError::IllegalParameter
1354 SetError( FormulaError::IllegalParameter
);
1361 PushInt( int(bRes
) );
1366 void ScInterpreter::ScOr()
1368 nFuncFmtType
= SvNumFormatType::LOGICAL
;
1369 short nParamCount
= GetByte();
1370 if ( !MustHaveParamCountMin( nParamCount
, 1 ) )
1373 bool bHaveValue
= false;
1375 size_t nRefInList
= 0;
1376 while( nParamCount
-- > 0)
1378 if ( nGlobalError
== FormulaError::NONE
)
1380 switch ( GetStackType() )
1384 bRes
|= ( PopDouble() != 0.0 );
1388 SetError( FormulaError::NoValue
);
1393 PopSingleRef( aAdr
);
1394 if ( nGlobalError
== FormulaError::NONE
)
1396 ScRefCellValue
aCell(mrDoc
, aAdr
);
1397 if (aCell
.hasNumeric())
1400 bRes
|= ( GetCellValue(aAdr
, aCell
) != 0.0 );
1402 // else: Xcl raises no error here
1410 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
1411 if ( nGlobalError
== FormulaError::NONE
)
1414 FormulaError nErr
= FormulaError::NONE
;
1415 ScValueIterator
aValIter( mrContext
, aRange
);
1416 if ( aValIter
.GetFirst( fVal
, nErr
) )
1421 bRes
|= ( fVal
!= 0.0 );
1422 } while ( (nErr
== FormulaError::NONE
) &&
1423 aValIter
.GetNext( fVal
, nErr
) );
1429 case svExternalSingleRef
:
1430 case svExternalDoubleRef
:
1434 ScMatrixRef pMat
= GetMatrix();
1438 double fVal
= pMat
->Or();
1439 FormulaError nErr
= GetDoubleErrorValue( fVal
);
1440 if ( nErr
!= FormulaError::NONE
)
1446 bRes
|= (fVal
!= 0.0);
1448 // else: GetMatrix did set FormulaError::IllegalParameter
1453 SetError( FormulaError::IllegalParameter
);
1460 PushInt( int(bRes
) );
1465 void ScInterpreter::ScXor()
1468 nFuncFmtType
= SvNumFormatType::LOGICAL
;
1469 short nParamCount
= GetByte();
1470 if ( !MustHaveParamCountMin( nParamCount
, 1 ) )
1473 bool bHaveValue
= false;
1475 size_t nRefInList
= 0;
1476 while( nParamCount
-- > 0)
1478 if ( nGlobalError
== FormulaError::NONE
)
1480 switch ( GetStackType() )
1484 bRes
^= ( PopDouble() != 0.0 );
1488 SetError( FormulaError::NoValue
);
1493 PopSingleRef( aAdr
);
1494 if ( nGlobalError
== FormulaError::NONE
)
1496 ScRefCellValue
aCell(mrDoc
, aAdr
);
1497 if (aCell
.hasNumeric())
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
1512 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
1513 if ( nGlobalError
== FormulaError::NONE
)
1516 FormulaError nErr
= FormulaError::NONE
;
1517 ScValueIterator
aValIter( mrContext
, aRange
);
1518 if ( aValIter
.GetFirst( fVal
, nErr
) )
1523 bRes
^= ( fVal
!= 0.0 );
1524 } while ( (nErr
== FormulaError::NONE
) &&
1525 aValIter
.GetNext( fVal
, nErr
) );
1531 case svExternalSingleRef
:
1532 case svExternalDoubleRef
:
1536 ScMatrixRef pMat
= GetMatrix();
1540 double fVal
= pMat
->Xor();
1541 FormulaError nErr
= GetDoubleErrorValue( fVal
);
1542 if ( nErr
!= FormulaError::NONE
)
1548 bRes
^= ( fVal
!= 0.0 );
1550 // else: GetMatrix did set FormulaError::IllegalParameter
1555 SetError( FormulaError::IllegalParameter
);
1562 PushInt( int(bRes
) );
1567 void ScInterpreter::ScNeg()
1569 // Simple negation doesn't change current format type to number, keep
1571 nFuncFmtType
= nCurFmtType
;
1572 switch ( GetStackType() )
1576 ScMatrixRef pMat
= GetMatrix();
1578 PushIllegalParameter();
1582 pMat
->GetDimensions( nC
, nR
);
1583 ScMatrixRef pResMat
= GetNewMat( nC
, nR
, /*bEmpty*/true );
1585 PushIllegalArgument();
1588 pMat
->NegOp( *pResMat
);
1589 PushMatrix( pResMat
);
1595 PushDouble( -GetDouble() );
1599 void ScInterpreter::ScPercentSign()
1601 nFuncFmtType
= SvNumFormatType::PERCENT
;
1602 const FormulaToken
* pSaveCur
= pCur
;
1603 sal_uInt8 nSavePar
= cPar
;
1606 FormulaByteToken
aDivOp( ocDiv
, cPar
);
1613 void ScInterpreter::ScNot()
1615 nFuncFmtType
= SvNumFormatType::LOGICAL
;
1616 switch ( GetStackType() )
1620 ScMatrixRef pMat
= GetMatrix();
1622 PushIllegalParameter();
1626 pMat
->GetDimensions( nC
, nR
);
1627 ScMatrixRef pResMat
= GetNewMat( nC
, nR
, /*bEmpty*/true);
1629 PushIllegalArgument();
1632 pMat
->NotOp( *pResMat
);
1633 PushMatrix( pResMat
);
1639 PushInt( int(GetDouble() == 0.0) );
1643 void ScInterpreter::ScBitAnd()
1646 if ( !MustHaveParamCount( GetByte(), 2 ) )
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();
1655 PushDouble (static_cast<sal_uInt64
>(num1
) & static_cast<sal_uInt64
>(num2
));
1658 void ScInterpreter::ScBitOr()
1661 if ( !MustHaveParamCount( GetByte(), 2 ) )
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();
1670 PushDouble (static_cast<sal_uInt64
>(num1
) | static_cast<sal_uInt64
>(num2
));
1673 void ScInterpreter::ScBitXor()
1676 if ( !MustHaveParamCount( GetByte(), 2 ) )
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();
1685 PushDouble (static_cast<sal_uInt64
>(num1
) ^ static_cast<sal_uInt64
>(num2
));
1688 void ScInterpreter::ScBitLshift()
1691 if ( !MustHaveParamCount( GetByte(), 2 ) )
1694 double fShift
= ::rtl::math::approxFloor( GetDouble());
1695 double num
= ::rtl::math::approxFloor( GetDouble());
1696 if ((num
>= n2power48
) || (num
< 0))
1697 PushIllegalArgument();
1702 fRes
= ::rtl::math::approxFloor( num
/ pow( 2.0, -fShift
));
1703 else if (fShift
== 0)
1706 fRes
= num
* pow( 2.0, fShift
);
1711 void ScInterpreter::ScBitRshift()
1714 if ( !MustHaveParamCount( GetByte(), 2 ) )
1717 double fShift
= ::rtl::math::approxFloor( GetDouble());
1718 double num
= ::rtl::math::approxFloor( GetDouble());
1719 if ((num
>= n2power48
) || (num
< 0))
1720 PushIllegalArgument();
1725 fRes
= num
* pow( 2.0, -fShift
);
1726 else if (fShift
== 0)
1729 fRes
= ::rtl::math::approxFloor( num
/ pow( 2.0, fShift
));
1734 void ScInterpreter::ScPi()
1739 void ScInterpreter::ScRandomImpl( const std::function
<double( double fFirst
, double fLast
)>& RandomFunc
,
1740 double fFirst
, double fLast
)
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
)
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
));
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.
1778 ScMatrixRef pResMat
= GetNewMat( static_cast<SCSIZE
>(nCols
), static_cast<SCSIZE
>(nRows
), /*bEmpty*/true );
1780 PushError( FormulaError::MatrixSize
);
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
);
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
1822 if (nParamCount
>= 4)
1823 fMax
= GetDoubleWithDefault(1.0);
1825 // optional 3rd para: The minimum value of the random numbers
1827 if (nParamCount
>= 3)
1828 fMin
= GetDoubleWithDefault(0.0);
1830 // optional 2nd para: The number of columns of the return array
1832 if (nParamCount
>= 2)
1833 nCols
= static_cast<SCCOL
>(GetInt32WithDefault(1));
1835 // optional 1st para: The number of rows of the return array
1837 if (nParamCount
>= 1)
1838 nRows
= static_cast<SCROW
>(GetInt32WithDefault(1));
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();
1853 fMax
= std::nextafter(fMax
+ 1, -DBL_MAX
);
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
);
1861 return floor(dist(mrContext
.aRNG
));
1863 return dist(mrContext
.aRNG
);
1866 if (nCols
== 1 && nRows
== 1)
1868 PushDouble(RandomFunc(fMin
, fMax
, bWholeNumber
));
1872 ScMatrixRef pResMat
= GetNewMat(static_cast<SCSIZE
>(nCols
), static_cast<SCSIZE
>(nRows
), /*bEmpty*/true);
1874 PushError(FormulaError::MatrixSize
);
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))
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();
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
;
1918 void ScInterpreter::ScFalse()
1920 nFuncFmtType
= SvNumFormatType::LOGICAL
;
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();
2003 PushIllegalArgument();
2005 PushDouble( ::rtl::math::acosh( fVal
));
2008 void ScInterpreter::ScArcTanHyp()
2010 double fVal
= GetDouble();
2011 if (fabs(fVal
) >= 1.0)
2012 PushIllegalArgument();
2014 PushDouble(::atanh(fVal
));
2017 void ScInterpreter::ScArcCotHyp()
2019 double nVal
= GetDouble();
2020 if (fabs(nVal
) <= 1.0)
2021 PushIllegalArgument();
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();
2055 PushDouble(sqrt(fVal
));
2057 PushIllegalArgument();
2060 void ScInterpreter::ScIsEmpty()
2063 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2064 switch ( GetRawStackType() )
2068 FormulaConstTokenRef p
= PopToken();
2069 if (!static_cast<const ScEmptyCellToken
*>(p
.get())->IsInherited())
2077 if ( !PopDoubleRefOrSingleRef( aAdr
) )
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
)
2087 case svExternalSingleRef
:
2088 case svExternalDoubleRef
:
2091 ScMatrixRef pMat
= GetMatrix();
2094 else if ( !pJumpMatrix
)
2095 nRes
= pMat
->IsEmptyCell( 0, 0) ? 1 : 0;
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)
2110 nGlobalError
= FormulaError::NONE
;
2114 bool ScInterpreter::IsString()
2116 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2118 switch ( GetRawStackType() )
2128 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2131 ScRefCellValue
aCell(mrDoc
, aAdr
);
2132 if (GetCellErrCode(aCell
) == FormulaError::NONE
)
2134 switch (aCell
.getType())
2136 case CELLTYPE_STRING
:
2137 case CELLTYPE_EDIT
:
2140 case CELLTYPE_FORMULA
:
2141 bRes
= (!aCell
.getFormula()->IsValue() && !aCell
.getFormula()->IsEmpty());
2149 case svExternalSingleRef
:
2151 ScExternalRefCache::TokenRef pToken
;
2152 PopExternalSingleRef(pToken
);
2153 if (nGlobalError
== FormulaError::NONE
&& pToken
->GetType() == svString
)
2157 case svExternalDoubleRef
:
2160 ScMatrixRef pMat
= GetMatrix();
2163 else if ( !pJumpMatrix
)
2164 bRes
= pMat
->IsStringOrEmpty(0, 0) && !pMat
->IsEmpty(0, 0);
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
);
2178 nGlobalError
= FormulaError::NONE
;
2182 void ScInterpreter::ScIsString()
2184 PushInt( int(IsString()) );
2187 void ScInterpreter::ScIsNonString()
2189 PushInt( int(!IsString()) );
2192 void ScInterpreter::ScIsLogical()
2195 switch ( GetStackType() )
2201 if ( !PopDoubleRefOrSingleRef( aAdr
) )
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
);
2218 svl::SharedString aStr
;
2219 ScMatValType nMatValType
= GetDoubleOrStringFromMatrix( fVal
, aStr
);
2220 bRes
= (nMatValType
== ScMatValType::Boolean
);
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()
2236 switch ( GetStackType() )
2242 if ( !PopDoubleRefOrSingleRef( aAdr
) )
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
:
2255 case CELLTYPE_VALUE
:
2257 sal_uInt32 nFormat
= GetCellNumberFormat(aAdr
, aCell
);
2258 if (mrContext
.NFGetType(nFormat
) == SvNumFormatType::LOGICAL
)
2265 // always 1, s. tdf#73078
2268 case CELLTYPE_FORMULA
:
2272 PushIllegalArgument();
2281 if ( nGlobalError
!= FormulaError::NONE
)
2284 nGlobalError
= FormulaError::NONE
;
2291 if ( nGlobalError
!= FormulaError::NONE
)
2294 nGlobalError
= FormulaError::NONE
;
2298 // we could return the type of one element if in JumpMatrix or
2299 // ForceArray mode, but Xcl doesn't ...
2303 if ( nGlobalError
!= FormulaError::NONE
)
2306 nGlobalError
= FormulaError::NONE
;
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);
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 ) )
2339 ScAddress
aCellPos( aPos
);
2340 if( nParamCount
== 2 )
2342 switch (GetStackType())
2344 case svExternalSingleRef
:
2345 case svExternalDoubleRef
:
2347 // Let's handle external reference separately...
2353 // Exceptionally not an intersecting position but top left.
2354 // See ODF v1.3 part 4 OpenFormula 6.13.3 CELL
2356 PopDoubleRef( aRange
);
2357 aCellPos
= aRange
.aStart
;
2361 PopSingleRef( aCellPos
);
2365 SetError( FormulaError::NoRef
);
2368 OUString aInfoType
= GetString().getString();
2369 if (nGlobalError
!= FormulaError::NONE
)
2370 PushIllegalParameter();
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
;
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();
2403 case FormulaGrammar::CONV_OOO
:
2404 case FormulaGrammar::CONV_XL_A1
:
2405 case FormulaGrammar::CONV_XL_R1C1
:
2410 ScRefFlags nFlags
= (aCellPos
.Tab() == aPos
.Tab()) ? ScRefFlags::ADDR_ABS
: ScRefFlags::ADDR_ABS_3D
;
2411 OUString
aStr(aCellPos
.Format(nFlags
, &mrDoc
, eConv
));
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
);
2424 ScDocShell
* pShell
= mrDoc
.GetDocumentShell();
2425 if( pShell
&& pShell
->GetMedium() )
2427 const INetURLObject
& rURLObj
= pShell
->GetMedium()->GetURLObject();
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
) +
2447 // file name and table name: 'FILEPATH/FILENAME'#$TABLE
2449 if (!comphelper::LibreOfficeKit::isActive())
2450 aFuncResult
+= rURLObj
.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous
);
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
);
2483 PushDouble(GetCellValue(aCellPos
, aCell
));
2485 else if( aInfoType
== "TYPE" )
2486 { // b = blank; l = string (label); v = otherwise (value)
2488 if (aCell
.hasString())
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() );
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
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( ScRange(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( ScRange(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( ScRange(aCellPos
) ) );
2552 PushInt( lcl_FormatHasOpenPar( pFormat
) ? 1 : 0 );
2555 PushIllegalArgument();
2559 void ScInterpreter::ScCellExternal()
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
);
2573 OUString aInfoType
= GetString().getString();
2574 if (nGlobalError
!= FormulaError::NONE
)
2576 PushError( nGlobalError
);
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();
2590 aRef
.SetAbsTab(-1); // revert the value.
2592 ScCellKeywordTranslator::transKeyword(aInfoType
, &ScGlobal::GetLocale(), ocCell
);
2593 ScExternalRefManager
* pRefMgr
= mrDoc
.GetExternalRefManager();
2595 if ( aInfoType
== "COL" )
2597 else if ( aInfoType
== "ROW" )
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
2604 if (pRefMgr
->getCacheTable(nFileId
, aTabName
, false))
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
);
2616 aComp
.CreateStringFromTokenArray(aStr
);
2619 else if ( aInfoType
== "FILENAME" )
2621 const OUString
* p
= pRefMgr
->getExternalFileName(nFileId
);
2624 // In theory this should never happen...
2625 SetError(FormulaError::NoName
);
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) + "]"
2646 // 'file URI'#$SheetName
2647 aBuf
= "'" + *p
+ "'#$" + aTabName
;
2652 else if ( aInfoType
== "CONTENTS" )
2654 switch (pToken
->GetType())
2657 PushString(pToken
->GetString());
2660 PushString(OUString::number(pToken
->GetDouble()));
2663 PushString(ScGlobal::GetErrorString(pToken
->GetError()));
2666 PushString(OUString());
2669 else if ( aInfoType
== "TYPE" )
2671 sal_Unicode c
= 'v';
2672 switch (pToken
->GetType())
2683 PushString(OUString(c
));
2685 else if ( aInfoType
== "FORMAT" )
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
2698 const SvNumberformat
* pFormat
= mrContext
.NFGetFormatEntry(aFmt
.mnIndex
);
2699 nVal
= lcl_FormatHasNegColor(pFormat
) ? 1 : 0;
2703 else if ( aInfoType
== "PARENTHESES" )
2705 // 1 = format string contains a '(' character, otherwise 0
2709 const SvNumberformat
* pFormat
= mrContext
.NFGetFormatEntry(aFmt
.mnIndex
);
2710 nVal
= lcl_FormatHasOpenPar(pFormat
) ? 1 : 0;
2715 PushIllegalParameter();
2718 void ScInterpreter::ScIsRef()
2720 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2722 switch ( GetStackType() )
2727 PopSingleRef( aAdr
);
2728 if ( nGlobalError
== FormulaError::NONE
)
2735 PopDoubleRef( aRange
);
2736 if ( nGlobalError
== FormulaError::NONE
)
2742 FormulaConstTokenRef x
= PopToken();
2743 if ( nGlobalError
== FormulaError::NONE
)
2744 bRes
= !x
->GetRefList()->empty();
2747 case svExternalSingleRef
:
2749 ScExternalRefCache::TokenRef pToken
;
2750 PopExternalSingleRef(pToken
);
2751 if (nGlobalError
== FormulaError::NONE
)
2755 case svExternalDoubleRef
:
2757 ScExternalRefCache::TokenArrayRef pArray
;
2758 PopExternalDoubleRef(pArray
);
2759 if (nGlobalError
== FormulaError::NONE
)
2766 nGlobalError
= FormulaError::NONE
;
2767 PushInt( int(bRes
) );
2770 void ScInterpreter::ScIsValue()
2772 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2774 switch ( GetRawStackType() )
2784 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2787 ScRefCellValue
aCell(mrDoc
, aAdr
);
2788 if (GetCellErrCode(aCell
) == FormulaError::NONE
)
2790 switch (aCell
.getType())
2792 case CELLTYPE_VALUE
:
2795 case CELLTYPE_FORMULA
:
2796 bRes
= (aCell
.getFormula()->IsValue() && !aCell
.getFormula()->IsEmpty());
2804 case svExternalSingleRef
:
2806 ScExternalRefCache::TokenRef pToken
;
2807 PopExternalSingleRef(pToken
);
2808 if (nGlobalError
== FormulaError::NONE
&& pToken
->GetType() == svDouble
)
2812 case svExternalDoubleRef
:
2815 ScMatrixRef pMat
= GetMatrix();
2818 else if ( !pJumpMatrix
)
2820 if (pMat
->GetErrorIfNotString( 0, 0) == FormulaError::NONE
)
2821 bRes
= pMat
->IsValue( 0, 0);
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
);
2837 nGlobalError
= FormulaError::NONE
;
2838 PushInt( int(bRes
) );
2841 void ScInterpreter::ScIsFormula()
2843 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2845 switch ( GetStackType() )
2848 if (IsInArrayContext())
2853 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
2854 if (nGlobalError
!= FormulaError::NONE
)
2856 PushError( nGlobalError
);
2861 PushIllegalArgument();
2865 ScMatrixRef pResMat
= GetNewMat( static_cast<SCSIZE
>(nCol2
- nCol1
+ 1),
2866 static_cast<SCSIZE
>(nRow2
- nRow1
+ 1), true);
2869 PushError( FormulaError::MatrixSize
);
2873 /* TODO: we really should have a gap-aware cell iterator. */
2875 ScAddress
aAdr( 0, 0, nTab1
);
2876 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
2879 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
2882 ScRefCellValue
aCell(mrDoc
, aAdr
);
2883 pResMat
->PutBoolean( (aCell
.getType() == CELLTYPE_FORMULA
), i
,j
);
2890 PushMatrix( pResMat
);
2897 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2900 bRes
= (mrDoc
.GetCellType(aAdr
) == CELLTYPE_FORMULA
);
2906 nGlobalError
= FormulaError::NONE
;
2907 PushInt( int(bRes
) );
2910 void ScInterpreter::ScFormula()
2913 switch ( GetStackType() )
2916 if (IsInArrayContext())
2921 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
2922 if (nGlobalError
!= FormulaError::NONE
)
2927 SetError( FormulaError::IllegalArgument
);
2931 ScMatrixRef pResMat
= GetNewMat( nCol2
- nCol1
+ 1, nRow2
- nRow1
+ 1, true);
2935 /* TODO: use a column iterator instead? */
2937 ScAddress
aAdr(0,0,nTab1
);
2938 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
2941 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++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
);
2952 pResMat
->PutError( FormulaError::NotAvailable
, i
,j
);
2960 PushMatrix( pResMat
);
2967 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2970 ScRefCellValue
aCell(mrDoc
, aAdr
);
2971 switch (aCell
.getType())
2973 case CELLTYPE_FORMULA
:
2974 aFormula
= aCell
.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_UNSPECIFIED
, &mrContext
);
2977 SetError( FormulaError::NotAvailable
);
2983 SetError( FormulaError::NotAvailable
);
2985 PushString( aFormula
);
2988 void ScInterpreter::ScIsNV()
2990 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2992 switch ( GetStackType() )
2998 bool bOk
= PopDoubleRefOrSingleRef( aAdr
);
2999 if ( nGlobalError
== FormulaError::NotAvailable
)
3003 ScRefCellValue
aCell(mrDoc
, aAdr
);
3004 FormulaError nErr
= GetCellErrCode(aCell
);
3005 bRes
= (nErr
== FormulaError::NotAvailable
);
3009 case svExternalSingleRef
:
3011 ScExternalRefCache::TokenRef pToken
;
3012 PopExternalSingleRef(pToken
);
3013 if (nGlobalError
== FormulaError::NotAvailable
||
3014 (pToken
&& pToken
->GetType() == svError
&& pToken
->GetError() == FormulaError::NotAvailable
))
3018 case svExternalDoubleRef
:
3021 ScMatrixRef pMat
= GetMatrix();
3024 else if ( !pJumpMatrix
)
3025 bRes
= (pMat
->GetErrorIfNotString( 0, 0) == FormulaError::NotAvailable
);
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
);
3038 if ( nGlobalError
== FormulaError::NotAvailable
)
3041 nGlobalError
= FormulaError::NONE
;
3042 PushInt( int(bRes
) );
3045 void ScInterpreter::ScIsErr()
3047 nFuncFmtType
= SvNumFormatType::LOGICAL
;
3049 switch ( GetStackType() )
3055 bool bOk
= PopDoubleRefOrSingleRef( aAdr
);
3056 if ( !bOk
|| (nGlobalError
!= FormulaError::NONE
&& nGlobalError
!= FormulaError::NotAvailable
) )
3060 ScRefCellValue
aCell(mrDoc
, aAdr
);
3061 FormulaError nErr
= GetCellErrCode(aCell
);
3062 bRes
= (nErr
!= FormulaError::NONE
&& nErr
!= FormulaError::NotAvailable
);
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
))
3075 case svExternalDoubleRef
:
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
);
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
);
3101 if ( nGlobalError
!= FormulaError::NONE
&& nGlobalError
!= FormulaError::NotAvailable
)
3104 nGlobalError
= FormulaError::NONE
;
3105 PushInt( int(bRes
) );
3108 void ScInterpreter::ScIsError()
3110 nFuncFmtType
= SvNumFormatType::LOGICAL
;
3112 switch ( GetStackType() )
3118 if ( !PopDoubleRefOrSingleRef( aAdr
) )
3123 if ( nGlobalError
!= FormulaError::NONE
)
3127 ScRefCellValue
aCell(mrDoc
, aAdr
);
3128 bRes
= (GetCellErrCode(aCell
) != FormulaError::NONE
);
3132 case svExternalSingleRef
:
3134 ScExternalRefCache::TokenRef pToken
;
3135 PopExternalSingleRef(pToken
);
3136 if (nGlobalError
!= FormulaError::NONE
|| pToken
->GetType() == svError
)
3140 case svExternalDoubleRef
:
3143 ScMatrixRef pMat
= GetMatrix();
3144 if ( nGlobalError
!= FormulaError::NONE
|| !pMat
)
3146 else if ( !pJumpMatrix
)
3147 bRes
= (pMat
->GetErrorIfNotString( 0, 0) != FormulaError::NONE
);
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
);
3160 if ( nGlobalError
!= FormulaError::NONE
)
3163 nGlobalError
= FormulaError::NONE
;
3164 PushInt( int(bRes
) );
3167 bool ScInterpreter::IsEven()
3169 nFuncFmtType
= SvNumFormatType::LOGICAL
;
3172 switch ( GetStackType() )
3178 if ( !PopDoubleRefOrSingleRef( aAdr
) )
3181 ScRefCellValue
aCell(mrDoc
, aAdr
);
3182 FormulaError nErr
= GetCellErrCode(aCell
);
3183 if (nErr
!= FormulaError::NONE
)
3187 switch (aCell
.getType())
3189 case CELLTYPE_VALUE
:
3190 fVal
= GetCellValue(aAdr
, aCell
);
3193 case CELLTYPE_FORMULA
:
3194 if (aCell
.getFormula()->IsValue())
3196 fVal
= GetCellValue(aAdr
, aCell
);
3212 case svExternalSingleRef
:
3214 ScExternalRefCache::TokenRef pToken
;
3215 PopExternalSingleRef(pToken
);
3216 if (nGlobalError
== FormulaError::NONE
&& pToken
->GetType() == svDouble
)
3218 fVal
= pToken
->GetDouble();
3223 case svExternalDoubleRef
:
3226 ScMatrixRef pMat
= GetMatrix();
3229 else if ( !pJumpMatrix
)
3231 bRes
= pMat
->IsValue( 0, 0);
3233 fVal
= pMat
->GetDouble( 0, 0);
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
);
3244 fVal
= pMat
->GetDouble( nC
, nR
);
3247 SetError( FormulaError::NoValue
);
3255 SetError( FormulaError::IllegalParameter
);
3257 bRes
= ( fmod( ::rtl::math::approxFloor( fabs( fVal
) ), 2.0 ) < 0.5 );
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
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();
3297 if ( *p
!= ' ' || p
[-1] != ' ' ) // first can't be ' ', so -1 is fine
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();
3317 OUString
aUpr(ScGlobal::getCharClass().uppercase(aStr
.toString()));
3318 OUString
aLwr(ScGlobal::getCharClass().lowercase(aStr
.toString()));
3321 while( nPos
< nLen
)
3323 OUString
aTmpStr( aStr
[nPos
-1] );
3324 if ( !ScGlobal::getCharClass().isLetter( aTmpStr
, 0 ) )
3325 aStr
[nPos
] = aUpr
[nPos
];
3327 aStr
[nPos
] = aLwr
[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();
3345 while ( nIdx
< aStr
.getLength() )
3347 aStr
.iterateCodePoints( &nIdx
);
3353 void ScInterpreter::ScT()
3355 switch ( GetStackType() )
3361 if ( !PopDoubleRefOrSingleRef( aAdr
) )
3366 bool bValue
= false;
3367 ScRefCellValue
aCell(mrDoc
, aAdr
);
3368 if (GetCellErrCode(aCell
) == FormulaError::NONE
)
3370 switch (aCell
.getType())
3372 case CELLTYPE_VALUE
:
3375 case CELLTYPE_FORMULA
:
3376 bValue
= aCell
.getFormula()->IsValue();
3383 PushString(OUString());
3387 svl::SharedString aStr
;
3388 GetCellString(aStr
, aCell
);
3394 case svExternalSingleRef
:
3395 case svExternalDoubleRef
:
3398 svl::SharedString aStr
;
3399 ScMatValType nMatValType
= GetDoubleOrStringFromMatrix( fVal
, aStr
);
3400 if (ScMatrix::IsValueType( nMatValType
))
3401 PushString(svl::SharedString::getEmptyString());
3409 PushString( OUString() );
3416 PushError( FormulaError::UnknownOpCode
);
3420 void ScInterpreter::ScValue()
3422 OUString aInputString
;
3425 switch ( GetRawStackType() )
3433 return; // leave on stack
3439 if ( !PopDoubleRefOrSingleRef( aAdr
) )
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
) );
3465 svl::SharedString aSS
;
3466 ScMatValType nType
= GetDoubleOrStringFromMatrix( fVal
,
3468 aInputString
= aSS
.getString();
3471 case ScMatValType::Empty
:
3474 case ScMatValType::Value
:
3475 case ScMatValType::Boolean
:
3478 case ScMatValType::String
:
3482 PushIllegalArgument();
3487 aInputString
= GetString().getString();
3491 sal_uInt32 nFIndex
= 0; // 0 for default locale
3492 if (mrContext
.NFIsNumberFormat(aInputString
, nFIndex
, fVal
))
3495 PushIllegalArgument();
3499 void ScInterpreter::ScNumberValue()
3502 sal_uInt8 nParamCount
= GetByte();
3503 if ( !MustHaveParamCount( nParamCount
, 1, 3 ) )
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 ];
3520 PushIllegalArgument(); //if given, separator length must be 1
3525 if ( cDecimalSeparator
&& aGroupSeparator
.indexOf( cDecimalSeparator
) != -1 )
3527 PushIllegalArgument(); //decimal separator cannot appear in group separator
3531 switch (GetStackType())
3534 return; // leave on stack
3536 aInputString
= GetString().getString();
3538 if ( nGlobalError
!= FormulaError::NONE
)
3540 PushError( nGlobalError
);
3543 if ( aInputString
.isEmpty() )
3545 if ( maCalcConfig
.mbEmptyStringAsZero
)
3552 sal_Int32 nDecSep
= aInputString
.indexOf( cDecimalSeparator
);
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 ), "" );
3563 aInputString
= aTemporary
+ aInputString
.subView( nDecSep
);
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 '%'
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() )
3587 fVal
*= pow( 10.0, -(nPercentCount
* 2)); // process '%' from input string
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() );
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();
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();
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
);
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
);
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
);
3683 static utl::TransliterationWrapper
& aTrans( init());
3684 return aTrans
.transliterate( rStr
, 0, sal_uInt16( rStr
.getLength() ) );
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()));
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();
3721 PushIllegalParameter();
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();
3738 OUString
aStr( &nCodePoint
, 1 );
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())
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);
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
);
3770 void ScInterpreter::ScMin( bool bTextAsZero
)
3772 short nParamCount
= GetByte();
3773 if (!MustHaveParamCountMin( nParamCount
, 1))
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();
3790 size_t nRefInList
= 0;
3791 while (nParamCount
-- > 0)
3793 switch (GetStackType())
3798 if (nMin
> nVal
) nMin
= nVal
;
3799 nFuncFmtType
= SvNumFormatType::NUMBER
;
3804 PopSingleRef( aAdr
);
3805 ScRefCellValue
aCell(mrDoc
, aAdr
);
3806 if (aCell
.hasNumeric())
3808 nVal
= GetCellValue(aAdr
, aCell
);
3810 if (nMin
> nVal
) nMin
= nVal
;
3812 else if (bTextAsZero
&& aCell
.hasString())
3821 // bDoMatOp only for non-array value when switching to
3823 if (SwitchToArrayRefList( xResMat
, nMatRows
, nMin
, MatOpFunc
,
3824 nRefArrayPos
== std::numeric_limits
<size_t>::max()))
3826 nRefArrayPos
= nRefInList
;
3832 FormulaError nErr
= FormulaError::NONE
;
3833 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
3834 ScValueIterator
aValIter( mrContext
, aRange
, mnSubTotalFlags
, bTextAsZero
);
3835 if (aValIter
.GetFirst(nVal
, nErr
))
3839 aValIter
.GetCurNumFmtInfo( nFuncFmtType
, nFuncFmtIndex
);
3840 while ((nErr
== FormulaError::NONE
) && aValIter
.GetNext(nVal
, nErr
))
3847 if (nRefArrayPos
!= std::numeric_limits
<size_t>::max())
3849 // Update vector element with current value.
3850 MatOpFunc( nRefArrayPos
, nMin
);
3853 nMin
= std::numeric_limits
<double>::max();
3855 nRefArrayPos
= std::numeric_limits
<size_t>::max();
3860 case svExternalSingleRef
:
3861 case svExternalDoubleRef
:
3863 ScMatrixRef pMat
= GetMatrix();
3866 nFuncFmtType
= SvNumFormatType::NUMBER
;
3867 nVal
= pMat
->GetMinValue(bTextAsZero
, bool(mnSubTotalFlags
& SubtotalFlags::IgnoreErrVal
));
3882 SetError(FormulaError::IllegalParameter
);
3887 SetError(FormulaError::IllegalParameter
);
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
);
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
);
3918 if (!std::isfinite(nVal
))
3919 PushError( GetDoubleErrorValue( nVal
));
3920 else if ( nVal
< nMin
)
3921 PushDouble(0.0); // zero or only empty arguments
3927 void ScInterpreter::ScMax( bool bTextAsZero
)
3929 short nParamCount
= GetByte();
3930 if (!MustHaveParamCountMin( nParamCount
, 1))
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();
3947 size_t nRefInList
= 0;
3948 while (nParamCount
-- > 0)
3950 switch (GetStackType())
3955 if (nMax
< nVal
) nMax
= nVal
;
3956 nFuncFmtType
= SvNumFormatType::NUMBER
;
3961 PopSingleRef( aAdr
);
3962 ScRefCellValue
aCell(mrDoc
, aAdr
);
3963 if (aCell
.hasNumeric())
3965 nVal
= GetCellValue(aAdr
, aCell
);
3967 if (nMax
< nVal
) nMax
= nVal
;
3969 else if (bTextAsZero
&& aCell
.hasString())
3978 // bDoMatOp only for non-array value when switching to
3980 if (SwitchToArrayRefList( xResMat
, nMatRows
, nMax
, MatOpFunc
,
3981 nRefArrayPos
== std::numeric_limits
<size_t>::max()))
3983 nRefArrayPos
= nRefInList
;
3989 FormulaError nErr
= FormulaError::NONE
;
3990 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
3991 ScValueIterator
aValIter( mrContext
, aRange
, mnSubTotalFlags
, bTextAsZero
);
3992 if (aValIter
.GetFirst(nVal
, nErr
))
3996 aValIter
.GetCurNumFmtInfo( nFuncFmtType
, nFuncFmtIndex
);
3997 while ((nErr
== FormulaError::NONE
) && aValIter
.GetNext(nVal
, nErr
))
4004 if (nRefArrayPos
!= std::numeric_limits
<size_t>::max())
4006 // Update vector element with current value.
4007 MatOpFunc( nRefArrayPos
, nMax
);
4010 nMax
= std::numeric_limits
<double>::lowest();
4012 nRefArrayPos
= std::numeric_limits
<size_t>::max();
4017 case svExternalSingleRef
:
4018 case svExternalDoubleRef
:
4020 ScMatrixRef pMat
= GetMatrix();
4023 nFuncFmtType
= SvNumFormatType::NUMBER
;
4024 nVal
= pMat
->GetMaxValue(bTextAsZero
, bool(mnSubTotalFlags
& SubtotalFlags::IgnoreErrVal
));
4039 SetError(FormulaError::IllegalParameter
);
4044 SetError(FormulaError::IllegalParameter
);
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
);
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
);
4075 if (!std::isfinite(nVal
))
4076 PushError( GetDoubleErrorValue( nVal
));
4077 else if ( nVal
> nMax
)
4078 PushDouble(0.0); // zero or only empty arguments
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
;
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;
4103 size_t nRefInList
= 0;
4104 while (nGlobalError
== FormulaError::NONE
&& nParamCount
-- > 0)
4106 switch (GetStackType())
4111 if (nGlobalError
== FormulaError::NONE
)
4113 values
.push_back(fVal
);
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
);
4131 else if (bTextAsZero
&& aCell
.hasString())
4133 values
.push_back(0.0);
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
;
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());
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
);
4182 std::vector
<double>().swap(values
);
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
);
4200 while ((nErr
== FormulaError::NONE
) && aValIter
.GetNext(fVal
, nErr
));
4202 if ( nErr
!= FormulaError::NONE
)
4208 case svExternalSingleRef
:
4209 case svExternalDoubleRef
:
4212 ScMatrixRef pMat
= GetMatrix();
4215 const bool bIgnoreErrVal
= bool(mnSubTotalFlags
& SubtotalFlags::IgnoreErrVal
);
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
);
4230 else if (bIgnoreErrVal
)
4231 nGlobalError
= FormulaError::NONE
;
4233 else if ( bTextAsZero
)
4235 values
.push_back(0.0);
4247 values
.push_back(0.0);
4250 SetError(FormulaError::IllegalParameter
);
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());
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();
4275 xResMat
->PutError( FormulaError::DivisionByZero
, 0, r
);
4278 ArrayRefListValue
& rArrayValue
= vArrayValues
[r
];
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
);
4291 ::std::vector
<double>::size_type n
= values
.size();
4293 SetError( FormulaError::DivisionByZero
);
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
)
4310 return CreateDoubleError( FormulaError::DivisionByZero
);
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
)
4332 return CreateDoubleError( FormulaError::DivisionByZero
);
4334 return sqrt( fVal
/ (nValCount
- 1));
4336 GetStVarParams( bTextAsZero
, VarResult
);
4339 void ScInterpreter::ScStDevP( bool bTextAsZero
)
4341 auto VarResult
= []( double fVal
, size_t nValCount
)
4344 return CreateDoubleError( FormulaError::DivisionByZero
);
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);
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();
4378 while (nParamCount
-- > 0)
4380 switch ( GetStackType() )
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);
4393 ScMatrixRef pMat
= PopMatrix();
4397 pMat
->GetDimensions(nC
, nR
);
4402 case svExternalSingleRef
:
4406 case svExternalDoubleRef
:
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);
4419 SetError(FormulaError::IllegalParameter
);
4422 PushDouble(static_cast<double>(nVal
));
4425 void ScInterpreter::ScRows()
4427 sal_uInt8 nParamCount
= GetByte();
4435 while (nParamCount
-- > 0)
4437 switch ( GetStackType() )
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);
4450 ScMatrixRef pMat
= PopMatrix();
4454 pMat
->GetDimensions(nC
, nR
);
4459 case svExternalSingleRef
:
4463 case svExternalDoubleRef
:
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);
4476 SetError(FormulaError::IllegalParameter
);
4479 PushDouble(static_cast<double>(nVal
));
4482 void ScInterpreter::ScSheets()
4484 sal_uInt8 nParamCount
= GetByte();
4486 if ( nParamCount
== 0 )
4487 nVal
= mrDoc
.GetTableCount();
4497 while (nGlobalError
== FormulaError::NONE
&& nParamCount
-- > 0)
4499 switch ( GetStackType() )
4502 case svExternalSingleRef
:
4507 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
4508 nVal
+= static_cast<sal_uLong
>(nTab2
- nTab1
+ 1);
4510 case svExternalDoubleRef
:
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);
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 ) )
4536 if (nParamCount
== 0)
4538 nVal
= aPos
.Col() + 1;
4544 pMyFormulaCell
->GetMatColsRows( nCols
, nRows
);
4548 // Happens if called via ScViewFunc::EnterMatrix()
4549 // ScFormulaCell::GetResultDimensions() as of course a
4550 // matrix result is not available yet.
4552 bMayBeScalar
= false;
4556 bMayBeScalar
= true;
4558 if (!bMayBeScalar
|| nCols
!= 1 || nRows
!= 1)
4560 ScMatrixRef pResMat
= GetNewMat( static_cast<SCSIZE
>(nCols
), 1, /*bEmpty*/true );
4563 for (SCCOL i
=0; i
< nCols
; ++i
)
4564 pResMat
->PutDouble( nVal
+ i
, static_cast<SCSIZE
>(i
), 0);
4565 PushMatrix( pResMat
);
4573 switch ( GetStackType() )
4580 PopSingleRef( nCol1
, nRow1
, nTab1
);
4581 nVal
= static_cast<double>(nCol1
+ 1);
4584 case svExternalSingleRef
:
4588 ScSingleRefData aRef
;
4589 PopExternalSingleRef( nFileId
, aTabName
, aRef
);
4590 ScAddress aAbsRef
= aRef
.toAbs(mrDoc
, aPos
);
4591 nVal
= static_cast<double>( aAbsRef
.Col() + 1 );
4596 case svExternalDoubleRef
:
4600 if ( GetStackType() == svDoubleRef
)
4606 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
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();
4620 ScMatrixRef pResMat
= GetNewMat(
4621 static_cast<SCSIZE
>(nCol2
-nCol1
+1), 1, /*bEmpty*/true);
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
);
4632 nVal
= static_cast<double>(nCol1
+ 1);
4636 SetError( FormulaError::IllegalParameter
);
4642 void ScInterpreter::ScRow()
4644 sal_uInt8 nParamCount
= GetByte();
4645 if ( !MustHaveParamCount( nParamCount
, 0, 1 ) )
4649 if (nParamCount
== 0)
4651 nVal
= aPos
.Row() + 1;
4657 pMyFormulaCell
->GetMatColsRows( nCols
, nRows
);
4661 // Happens if called via ScViewFunc::EnterMatrix()
4662 // ScFormulaCell::GetResultDimensions() as of course a
4663 // matrix result is not available yet.
4665 bMayBeScalar
= false;
4669 bMayBeScalar
= true;
4671 if (!bMayBeScalar
|| nCols
!= 1 || nRows
!= 1)
4673 ScMatrixRef pResMat
= GetNewMat( 1, static_cast<SCSIZE
>(nRows
), /*bEmpty*/true);
4676 for (SCROW i
=0; i
< nRows
; i
++)
4677 pResMat
->PutDouble( nVal
+ i
, 0, static_cast<SCSIZE
>(i
));
4678 PushMatrix( pResMat
);
4686 switch ( GetStackType() )
4693 PopSingleRef( nCol1
, nRow1
, nTab1
);
4694 nVal
= static_cast<double>(nRow1
+ 1);
4697 case svExternalSingleRef
:
4701 ScSingleRefData aRef
;
4702 PopExternalSingleRef( nFileId
, aTabName
, aRef
);
4703 ScAddress aAbsRef
= aRef
.toAbs(mrDoc
, aPos
);
4704 nVal
= static_cast<double>( aAbsRef
.Row() + 1 );
4708 case svExternalDoubleRef
:
4712 if ( GetStackType() == svDoubleRef
)
4718 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
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();
4732 ScMatrixRef pResMat
= GetNewMat( 1,
4733 static_cast<SCSIZE
>(nRow2
-nRow1
+1), /*bEmpty*/true);
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
);
4744 nVal
= static_cast<double>(nRow1
+ 1);
4748 SetError( FormulaError::IllegalParameter
);
4754 void ScInterpreter::ScSheet()
4756 sal_uInt8 nParamCount
= GetByte();
4757 if ( !MustHaveParamCount( nParamCount
, 0, 1 ) )
4761 if ( nParamCount
== 0 )
4762 nVal
= aPos
.Tab() + 1;
4765 switch ( GetStackType() )
4769 svl::SharedString aStr
= PopString();
4770 if ( mrDoc
.GetTable(aStr
.getString(), nVal
))
4773 SetError( FormulaError::IllegalArgument
);
4781 PopSingleRef(nCol1
, nRow1
, nTab1
);
4793 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
4798 SetError( FormulaError::IllegalParameter
);
4800 if ( nGlobalError
!= FormulaError::NONE
)
4803 PushDouble( static_cast<double>(nVal
) );
4808 class VectorMatrixAccessor
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
4847 mrMat
.GetDimensions(nC
, nR
);
4848 return mbColVec
? nR
: nC
;
4852 const ScMatrix
& mrMat
;
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
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
4866 return -1; // empty always less than anything else
4869 /* FIXME: what is an empty path (result of IF(false;true_path) in
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
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.
4892 return nVal1
< nVal2
? -1 : 1;
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
);
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
))
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
4943 return -1; // numeric always less than string
4945 const double nVal2
= rMat
.GetDouble(j
);
4949 return ( nVal1
< nVal2
? -1 : 1 );
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
4960 void lcl_GetLastMatch( SCSIZE
& rIndex
, const VectorMatrixAccessor
& rMat
,
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))
4970 // Order of IsEmptyPath, IsEmpty, IsStringOrEmpty is significant!
4971 else if (rMat
.IsEmptyPath(rIndex
))
4973 while (rIndex
< nMatCount
-1 && rMat
.IsEmptyPath(rIndex
+1))
4976 else if (rMat
.IsEmpty(rIndex
))
4978 while (rIndex
< nMatCount
-1 && rMat
.IsEmpty(rIndex
+1))
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))
4990 OSL_FAIL("lcl_GetLastMatch: unhandled matrix type");
4996 void ScInterpreter::ScMatch()
4998 sal_uInt8 nParamCount
= GetByte();
4999 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
5002 VectorSearchArguments vsa
;
5003 vsa
.nSearchOpCode
= SC_OPCODE_MATCH
;
5006 double fType
= ( nParamCount
== 3 ? GetDouble() : 1.0 );
5007 switch ( static_cast<int>(fType
) )
5010 vsa
.eMatchMode
= exactorG
;
5011 vsa
.eSearchMode
= searchbdesc
;
5014 vsa
.eMatchMode
= exactorNA
;
5015 vsa
.eSearchMode
= searchfwd
;
5019 vsa
.eMatchMode
= exactorS
;
5020 vsa
.eSearchMode
= searchbasc
;
5023 PushIllegalParameter();
5027 // get vector to be searched
5028 switch (GetStackType())
5031 PopSingleRef( vsa
.nCol1
, vsa
.nRow1
, vsa
.nTab1
);
5032 vsa
.nCol2
= vsa
.nCol1
;
5033 vsa
.nRow2
= vsa
.nRow1
;
5034 vsa
.pMatSrc
= nullptr;
5038 vsa
.pMatSrc
= nullptr;
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();
5049 case svExternalDoubleRef
:
5051 if (GetStackType() == svMatrix
)
5052 vsa
.pMatSrc
= PopMatrix();
5054 PopExternalDoubleRef(vsa
.pMatSrc
);
5058 PushIllegalParameter();
5064 PushIllegalParameter();
5069 if (nGlobalError
== FormulaError::NONE
)
5071 switch ( GetStackType() )
5075 vsa
.isStringSearch
= false;
5076 vsa
.fSearchVal
= GetDouble();
5081 vsa
.isStringSearch
= true;
5082 vsa
.sSearchStr
= GetString();
5089 if ( !PopDoubleRefOrSingleRef( aAdr
) )
5094 ScRefCellValue
aCell(mrDoc
, aAdr
);
5095 if (aCell
.hasNumeric())
5097 vsa
.isStringSearch
= false;
5098 vsa
.fSearchVal
= GetCellValue(aAdr
, aCell
);
5102 vsa
.isStringSearch
= true;
5103 GetCellString(vsa
.sSearchStr
, aCell
);
5107 case svExternalSingleRef
:
5109 ScExternalRefCache::TokenRef pToken
;
5110 PopExternalSingleRef(pToken
);
5111 if (nGlobalError
!= FormulaError::NONE
)
5113 PushError( nGlobalError
);
5116 if (pToken
->GetType() == svDouble
)
5118 vsa
.isStringSearch
= false;
5119 vsa
.fSearchVal
= pToken
->GetDouble();
5123 vsa
.isStringSearch
= true;
5124 vsa
.sSearchStr
= pToken
->GetString();
5128 case svExternalDoubleRef
:
5131 ScMatValType nType
= GetDoubleOrStringFromMatrix(
5132 vsa
.fSearchVal
, vsa
.sSearchStr
);
5133 vsa
.isStringSearch
= ScMatrix::IsNonValueType(nType
);
5138 PushIllegalParameter();
5144 if ( SearchVectorForValue( vsa
) )
5145 PushDouble( vsa
.nIndex
);
5148 if ( vsa
.isResultNA
)
5151 return; // error occurred and has already been pushed
5155 PushIllegalParameter();
5158 void ScInterpreter::ScXMatch()
5160 sal_uInt8 nParamCount
= GetByte();
5161 if (!MustHaveParamCount(nParamCount
, 2, 4))
5164 VectorSearchArguments vsa
;
5165 vsa
.nSearchOpCode
= SC_OPCODE_X_MATCH
;
5168 if (nParamCount
== 4)
5170 sal_Int16 k
= GetInt16();
5171 if (k
>= -2 && k
<= 2 && k
!= 0)
5172 vsa
.eSearchMode
= static_cast<SearchMode
>(k
);
5175 PushIllegalParameter();
5180 vsa
.eSearchMode
= searchfwd
;
5183 if (nParamCount
>= 3)
5185 sal_Int16 k
= GetInt16();
5186 if (k
>= -1 && k
<= 3)
5187 vsa
.eMatchMode
= static_cast<MatchMode
>(k
);
5190 PushIllegalParameter();
5195 vsa
.eMatchMode
= exactorNA
;
5197 // get vector to be searched
5198 switch (GetStackType())
5202 PopSingleRef(vsa
.nCol1
, vsa
.nRow1
, vsa
.nTab1
);
5203 vsa
.nCol2
= vsa
.nCol1
;
5204 vsa
.nRow2
= vsa
.nRow1
;
5205 vsa
.pMatSrc
= nullptr;
5210 vsa
.pMatSrc
= nullptr;
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();
5221 case svExternalDoubleRef
:
5223 if (GetStackType() == svMatrix
)
5224 vsa
.pMatSrc
= PopMatrix();
5226 PopExternalDoubleRef(vsa
.pMatSrc
);
5230 PushIllegalParameter();
5236 PushIllegalParameter();
5241 if (nGlobalError
== FormulaError::NONE
)
5243 switch (GetRawStackType())
5248 vsa
.isEmptySearch
= true;
5249 vsa
.isStringSearch
= false;
5250 vsa
.sSearchStr
= GetString();
5255 vsa
.isStringSearch
= false;
5256 vsa
.fSearchVal
= GetDouble();
5261 vsa
.isStringSearch
= true;
5262 vsa
.sSearchStr
= GetString();
5269 if (!PopDoubleRefOrSingleRef(aAdr
))
5274 ScRefCellValue
aCell(mrDoc
, aAdr
);
5275 if (aCell
.hasNumeric())
5277 vsa
.isStringSearch
= false;
5278 vsa
.fSearchVal
= GetCellValue(aAdr
, aCell
);
5282 vsa
.isStringSearch
= true;
5283 GetCellString(vsa
.sSearchStr
, aCell
);
5287 case svExternalSingleRef
:
5289 ScExternalRefCache::TokenRef pToken
;
5290 PopExternalSingleRef(pToken
);
5291 if (nGlobalError
!= FormulaError::NONE
)
5293 PushError(nGlobalError
);
5296 if (pToken
->GetType() == svDouble
)
5298 vsa
.isStringSearch
= false;
5299 vsa
.fSearchVal
= pToken
->GetDouble();
5303 vsa
.isStringSearch
= true;
5304 vsa
.sSearchStr
= pToken
->GetString();
5308 case svExternalDoubleRef
:
5311 ScMatValType nType
= GetDoubleOrStringFromMatrix(
5312 vsa
.fSearchVal
, vsa
.sSearchStr
);
5313 vsa
.isStringSearch
= ScMatrix::IsNonValueType(nType
);
5318 PushIllegalParameter();
5324 if (SearchVectorForValue(vsa
))
5325 PushDouble(vsa
.nIndex
);
5331 return; // error occurred and has already been pushed
5335 PushIllegalParameter();
5340 bool isCellContentEmpty( const ScRefCellValue
& rCell
)
5342 switch (rCell
.getType())
5344 case CELLTYPE_VALUE
:
5345 case CELLTYPE_STRING
:
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
)
5361 if (!aRes
.maString
.isEmpty())
5374 void ScInterpreter::ScCountEmptyCells()
5376 if ( !MustHaveParamCount( GetByte(), 1 ) )
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())
5389 PopSingleRef( aAdr
);
5390 ScRefCellValue
aCell(mrDoc
, aAdr
);
5391 if (!isCellContentEmpty(aCell
))
5400 SCSIZE nRefListArrayPos
= 0;
5401 size_t nRefInList
= 0;
5402 while (nParam
-- > 0)
5404 nRefListArrayPos
= nRefInList
;
5405 PopDoubleRef( aRange
, nParam
, nRefInList
);
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
))
5420 xResMat
->PutDouble( nMaxCount
- nCount
, 0, nRefListArrayPos
);
5421 nMaxCount
= nCount
= 0;
5427 case svExternalSingleRef
:
5428 case svExternalDoubleRef
:
5430 ScMatrixRef xMat
= GetMatrix();
5432 SetError( FormulaError::IllegalParameter
);
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);
5446 default : SetError(FormulaError::IllegalParameter
); break;
5449 PushMatrix( xResMat
);
5451 PushDouble(nMaxCount
- nCount
);
5454 void ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc
)
5456 sal_uInt8 nParamCount
= GetByte();
5457 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
5464 ScMatrixRef pSumExtraMatrix
;
5465 bool bSumExtraRange
= (nParamCount
== 3);
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() )
5478 PopDoubleRef( nCol3
, nRow3
, nTab3
, nColJunk
, nRowJunk
, nTabJunk
);
5479 if ( nTabJunk
!= nTab3
)
5481 PushError( FormulaError::IllegalParameter
);
5487 PopSingleRef( nCol3
, nRow3
, nTab3
);
5490 pSumExtraMatrix
= PopMatrix();
5491 // nCol3, nRow3, nTab3 remain 0
5493 case svExternalSingleRef
:
5495 pSumExtraMatrix
= GetNewMat(1,1);
5496 ScExternalRefCache::TokenRef pToken
;
5497 PopExternalSingleRef(pToken
);
5498 if (nGlobalError
!= FormulaError::NONE
)
5500 PushError( nGlobalError
);
5504 if (pToken
->GetType() == svDouble
)
5505 pSumExtraMatrix
->PutDouble(pToken
->GetDouble(), 0, 0);
5507 pSumExtraMatrix
->PutString(pToken
->GetString(), 0, 0);
5510 case svExternalDoubleRef
:
5511 PopExternalDoubleRef(pSumExtraMatrix
);
5514 PushError( FormulaError::IllegalParameter
);
5519 svl::SharedString aString
;
5521 bool bIsString
= true;
5522 switch ( GetStackType() )
5528 if ( !PopDoubleRefOrSingleRef( aAdr
) )
5530 PushError( nGlobalError
);
5534 ScRefCellValue
aCell(mrDoc
, aAdr
);
5535 switch (aCell
.getType())
5537 case CELLTYPE_VALUE
:
5538 fVal
= GetCellValue(aAdr
, aCell
);
5541 case CELLTYPE_FORMULA
:
5542 if (aCell
.getFormula()->IsValue())
5544 fVal
= GetCellValue(aAdr
, aCell
);
5548 GetCellString(aString
, aCell
);
5550 case CELLTYPE_STRING
:
5551 case CELLTYPE_EDIT
:
5552 GetCellString(aString
, aCell
);
5561 aString
= GetString();
5564 case svExternalDoubleRef
:
5566 ScMatValType nType
= GetDoubleOrStringFromMatrix( fVal
, aString
);
5567 bIsString
= ScMatrix::IsRealStringType( nType
);
5570 case svExternalSingleRef
:
5572 ScExternalRefCache::TokenRef pToken
;
5573 PopExternalSingleRef(pToken
);
5574 if (nGlobalError
== FormulaError::NONE
)
5576 if (pToken
->GetType() == svDouble
)
5578 fVal
= pToken
->GetDouble();
5582 aString
= pToken
->GetString();
5593 KahanSum fSum
= 0.0;
5595 double fCount
= 0.0;
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)
5610 ScMatrixRef pQueryMatrix
;
5611 switch ( GetStackType() )
5616 /* TODO: this could resolve if all refs are of the same size */
5617 SetError( FormulaError::IllegalParameter
);
5621 nRefListArrayPos
= nRefInList
;
5623 PopDoubleRef( aRange
, nParam
, nRefInList
);
5624 aRange
.GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
5628 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
5631 PopSingleRef( nCol1
, nRow1
, nTab1
);
5637 case svExternalSingleRef
:
5638 case svExternalDoubleRef
:
5640 pQueryMatrix
= GetMatrix();
5643 PushError( FormulaError::IllegalParameter
);
5650 pQueryMatrix
->GetDimensions( nC
, nR
);
5651 nCol2
= static_cast<SCCOL
>(nC
- 1);
5652 nRow2
= static_cast<SCROW
>(nR
- 1);
5657 SetError( FormulaError::IllegalParameter
);
5659 if ( nTab1
!= nTab2
)
5661 SetError( FormulaError::IllegalParameter
);
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
;
5678 if (pSumExtraMatrix
)
5681 pSumExtraMatrix
->GetDimensions( nC
, nR
);
5682 nMaxCol
= static_cast<SCCOL
>(nC
- 1);
5683 nMaxRow
= static_cast<SCROW
>(nR
- 1);
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
;
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;
5720 rItem
.meType
= ScQueryEntry::ByValue
;
5722 rEntry
.eOp
= SC_EQUAL
;
5726 rParam
.FillInExcelSyntax(mrDoc
.GetSharedStringPool(), aString
.getString(), 0, &mrContext
);
5727 if (rItem
.meType
== ScQueryEntry::ByString
)
5728 rParam
.eSearchType
= DetectSearchType(rItem
.maString
.getString(), mrDoc
);
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
;
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();
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
);
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
);
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
);
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
);
5829 } while ( aCellIter
.GetNext() );
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
);
5844 } while ( aCellIter
.GetNext() );
5851 PushError( FormulaError::IllegalParameter
);
5857 case ifSUMIF
: fRes
= fSum
.get(); break;
5858 case ifAVERAGEIF
: fRes
= div( fSum
.get(), fCount
); break;
5862 if (nGlobalError
== FormulaError::NONE
)
5863 xResMat
->PutDouble( fRes
, 0, nRefListArrayPos
);
5866 xResMat
->PutError( nGlobalError
, 0, nRefListArrayPos
);
5867 nGlobalError
= FormulaError::NONE
;
5869 fRes
= fCount
= 0.0;
5874 PushMatrix( xResMat
);
5879 void ScInterpreter::ScSumIf()
5881 IterateParametersIf( ifSUMIF
);
5884 void ScInterpreter::ScAverageIf()
5886 IterateParametersIf( ifAVERAGEIF
);
5889 void ScInterpreter::ScCountIf()
5891 if ( !MustHaveParamCount( GetByte(), 2 ) )
5894 svl::SharedString aString
;
5896 bool bIsString
= true;
5897 switch ( GetStackType() )
5903 if ( !PopDoubleRefOrSingleRef( aAdr
) )
5908 ScRefCellValue
aCell(mrDoc
, aAdr
);
5909 switch (aCell
.getType())
5911 case CELLTYPE_VALUE
:
5912 fVal
= GetCellValue(aAdr
, aCell
);
5915 case CELLTYPE_FORMULA
:
5916 if (aCell
.getFormula()->IsValue())
5918 fVal
= GetCellValue(aAdr
, aCell
);
5922 GetCellString(aString
, aCell
);
5924 case CELLTYPE_STRING
:
5925 case CELLTYPE_EDIT
:
5926 GetCellString(aString
, aCell
);
5935 case svExternalSingleRef
:
5936 case svExternalDoubleRef
:
5938 ScMatValType nType
= GetDoubleOrStringFromMatrix(fVal
, aString
);
5939 bIsString
= ScMatrix::IsRealStringType( nType
);
5943 aString
= GetString();
5951 double fCount
= 0.0;
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)
5966 ScMatrixRef pQueryMatrix
;
5967 const ScComplexRefData
* refData
= nullptr;
5968 switch ( GetStackType() )
5971 nRefListArrayPos
= nRefInList
;
5975 refData
= GetStackDoubleRef(nRefInList
);
5977 PopDoubleRef( aRange
, nParam
, nRefInList
);
5978 aRange
.GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
5982 PopSingleRef( nCol1
, nRow1
, nTab1
);
5988 case svExternalSingleRef
:
5989 case svExternalDoubleRef
:
5991 pQueryMatrix
= GetMatrix();
5994 PushIllegalParameter();
6001 pQueryMatrix
->GetDimensions( nC
, nR
);
6002 nCol2
= static_cast<SCCOL
>(nC
- 1);
6003 nRow2
= static_cast<SCROW
>(nR
- 1);
6008 PopError(); // Propagate it further
6009 PushIllegalParameter();
6012 if ( nTab1
!= nTab2
)
6014 PushIllegalParameter();
6019 PushIllegalParameter();
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;
6034 rItem
.meType
= ScQueryEntry::ByValue
;
6036 rEntry
.eOp
= SC_EQUAL
;
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
;
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();
6058 SCSIZE nSize
= pResultMatrix
->GetElementCount();
6059 for (SCSIZE nIndex
= 0; nIndex
< nSize
; ++nIndex
)
6061 if (pResultMatrix
->IsValue( nIndex
) &&
6062 pResultMatrix
->GetDouble( nIndex
))
6068 if(ScCountIfCellIteratorSortedCache::CanBeUsed(mrDoc
, rParam
, nTab1
, pMyFormulaCell
,
6069 refData
, mrContext
))
6071 ScCountIfCellIteratorSortedCache
aCellIter(mrDoc
, mrContext
, nTab1
, rParam
, false, false);
6072 fCount
+= aCellIter
.GetCount();
6076 ScCountIfCellIteratorDirect
aCellIter(mrDoc
, mrContext
, nTab1
, rParam
, false, false);
6077 fCount
+= aCellIter
.GetCount();
6083 PushIllegalParameter();
6088 xResMat
->PutDouble( fCount
, 0, nRefListArrayPos
);
6093 PushMatrix( xResMat
);
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
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;
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;
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
;
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
)
6164 svl::SharedString aString
;
6166 bool bIsString
= true;
6167 switch ( GetStackType() )
6173 if ( !PopDoubleRefOrSingleRef( aAdr
) )
6175 PushError( nGlobalError
);
6179 ScRefCellValue
aCell(mrDoc
, aAdr
);
6180 switch (aCell
.getType())
6182 case CELLTYPE_VALUE
:
6183 fVal
= GetCellValue(aAdr
, aCell
);
6186 case CELLTYPE_FORMULA
:
6187 if (aCell
.getFormula()->IsValue())
6189 fVal
= GetCellValue(aAdr
, aCell
);
6193 GetCellString(aString
, aCell
);
6195 case CELLTYPE_STRING
:
6196 case CELLTYPE_EDIT
:
6197 GetCellString(aString
, aCell
);
6206 aString
= GetString();
6209 case svExternalDoubleRef
:
6211 ScMatValType nType
= GetDoubleOrStringFromMatrix( fVal
, aString
);
6212 bIsString
= ScMatrix::IsRealStringType( nType
);
6215 case svExternalSingleRef
:
6217 ScExternalRefCache::TokenRef pToken
;
6218 PopExternalSingleRef(pToken
);
6219 if (nGlobalError
== FormulaError::NONE
)
6221 if (pToken
->GetType() == svDouble
)
6223 fVal
= pToken
->GetDouble();
6227 aString
= pToken
->GetString();
6238 if (nGlobalError
!= FormulaError::NONE
)
6240 PushError( nGlobalError
);
6241 return; // and bail out, no need to evaluate other arguments
6245 short nParam
= nParamCount
;
6246 size_t nRefInList
= 0;
6247 size_t nRefArrayPos
= std::numeric_limits
<size_t>::max();
6254 ScMatrixRef pQueryMatrix
;
6255 while (nParam
-- == nParamCount
)
6257 const ScComplexRefData
* refData
= nullptr;
6258 switch ( GetStackType() )
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
)
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
);
6296 PopDoubleRef( aRange
, nParam
, nRefInList
);
6297 aRange
.GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
6301 refData
= GetStackDoubleRef();
6302 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
6305 PopSingleRef( nCol1
, nRow1
, nTab1
);
6311 case svExternalSingleRef
:
6312 case svExternalDoubleRef
:
6314 pQueryMatrix
= GetMatrix();
6317 PushError( FormulaError::IllegalParameter
);
6324 pQueryMatrix
->GetDimensions( nC
, nR
);
6325 nCol2
= static_cast<SCCOL
>(nC
- 1);
6326 nRow2
= static_cast<SCROW
>(nR
- 1);
6331 PushError( FormulaError::IllegalParameter
);
6334 if ( nTab1
!= nTab2
)
6336 PushError( FormulaError::IllegalArgument
);
6340 ScQueryParam rParam
;
6341 ScQueryEntry
& rEntry
= rParam
.GetEntry(0);
6342 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
6343 rEntry
.bDoQuery
= true;
6346 rItem
.meType
= ScQueryEntry::ByValue
;
6348 rEntry
.eOp
= SC_EQUAL
;
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
);
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
);
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
;
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
);
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
);
6424 // recalculate matrix values
6425 if (nGlobalError
!= FormulaError::NONE
)
6427 PushError( nGlobalError
);
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
;
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
);
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
);
6462 std::vector
<double>::const_iterator itThisRes
= aResValues
.begin();
6463 for (auto& rCondition
: vConditions
)
6465 rCondition
+= *itThisRes
;
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() );
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
];
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
);
6523 std::for_each( vConditions
.begin(), vConditions
.end(), [](sal_uInt8
& r
){ r
= 0; } );
6529 if (!vRefArrayConditions
.empty() && !vConditions
.empty())
6531 // Add/op the last current value to all array positions.
6532 for (auto & rVec
: vRefArrayConditions
)
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
);
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() )
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
6584 vRefArrayConditions
.resize( nRefArrayRows
);
6585 for (auto & rVec
: vRefArrayConditions
)
6591 bRefArrayMain
= true;
6592 nRefArrayPos
= nRefInList
;
6595 PopDoubleRef( aRange
, nParam
, nRefInList
);
6596 aRange
.GetVars( nMainCol1
, nMainRow1
, nMainTab1
, nMainCol2
, nMainRow2
, nMainTab2
);
6600 PopDoubleRef( nMainCol1
, nMainRow1
, nMainTab1
, nMainCol2
, nMainRow2
, nMainTab2
);
6603 PopSingleRef( nMainCol1
, nMainRow1
, nMainTab1
);
6604 nMainCol2
= nMainCol1
;
6605 nMainRow2
= nMainRow1
;
6606 nMainTab2
= nMainTab1
;
6609 case svExternalSingleRef
:
6610 case svExternalDoubleRef
:
6612 pMainMatrix
= GetMatrix();
6615 PushError( FormulaError::IllegalParameter
);
6622 pMainMatrix
->GetDimensions( nC
, nR
);
6623 nMainCol2
= static_cast<SCCOL
>(nC
- 1);
6624 nMainRow2
= static_cast<SCROW
>(nR
- 1);
6628 // Treat a scalar value as 1x1 matrix.
6630 pMainMatrix
= GetNewMat(1,1);
6631 nMainCol1
= nMainCol2
= 0;
6632 nMainRow1
= nMainRow2
= 0;
6633 nMainTab1
= nMainTab2
= 0;
6634 pMainMatrix
->PutDouble( GetDouble(), 0, 0);
6637 pMainMatrix
= GetNewMat(1,1);
6638 nMainCol1
= nMainCol2
= 0;
6639 nMainRow1
= nMainRow2
= 0;
6640 nMainTab1
= nMainTab2
= 0;
6641 pMainMatrix
->PutString( GetString(), 0, 0);
6645 PushError( FormulaError::IllegalParameter
);
6648 if ( nMainTab1
!= nMainTab2
)
6650 PushError( FormulaError::IllegalArgument
);
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
);
6670 if (nGlobalError
!= FormulaError::NONE
)
6672 PushError( nGlobalError
);
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 );
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
);
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
)
6714 if (GetDoubleErrorValue(fVal
) == FormulaError::ElementNaN
)
6719 if ( aRes
.mfMin
> fVal
)
6721 if ( 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
);
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
);
6759 if ( aRes
.mfMin
> fVal
)
6761 if ( 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
);
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)
6787 static_assert(sizeof(vConditions
[0]) == 1);
6788 const sal_uInt8
* pos
= vConditions
.data();
6789 const sal_uInt8
* end
= pos
+ vConditions
.size();
6792 pos
= static_cast< const sal_uInt8
* >( memchr( pos
, nQueryCount
, end
- pos
));
6793 if( pos
== nullptr )
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
)
6810 xResMat
->PutDouble( fCount
, 0, i
);
6816 PushMatrix( xResMat
);
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
);
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
);
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
);
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
);
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
);
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 ) )
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
)
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();
6946 PopSingleRef( nResCol1
, nResRow1
, nResTab
);
6947 nResCol2
= nResCol1
;
6948 nResRow2
= nResRow1
;
6951 case svExternalSingleRef
:
6952 case svExternalDoubleRef
:
6954 pResMat
= GetMatrix();
6957 PushIllegalParameter();
6961 pResMat
->GetDimensions(nC
, nR
);
6962 if (nC
!= 1 && nR
!= 1)
6964 // Result matrix must be a vector.
6965 PushIllegalParameter();
6971 fResVal
= GetDouble();
6974 aResStr
= GetString();
6977 PushIllegalParameter();
6982 // For double, string and single reference.
6983 double fDataVal
= 0.0;
6984 svl::SharedString aDataStr
;
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
)
6997 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTabJunk
);
6998 if (nTab1
!= nTabJunk
)
7000 PushIllegalParameter();
7003 bVertical
= (nRow2
- nRow1
) >= (nCol2
- nCol1
);
7004 nLenMajor
= bVertical
? nRow2
- nRow1
+ 1 : nCol2
- nCol1
+ 1;
7008 case svExternalSingleRef
:
7009 case svExternalDoubleRef
:
7011 pDataMat
= GetMatrix();
7014 PushIllegalParameter();
7019 pDataMat
->GetDimensions(nC
, nR
);
7020 bVertical
= (nR
>= nC
);
7021 nLenMajor
= bVertical
? nR
: nC
;
7026 fDataVal
= GetDouble();
7032 aDataStr
= GetString();
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
);
7050 GetCellString(aDataStr
, aCell
);
7054 SetError( FormulaError::IllegalParameter
);
7057 if (nGlobalError
!= FormulaError::NONE
)
7059 PushError( nGlobalError
);
7063 // Get the lookup value.
7065 ScQueryParam aParam
;
7066 ScQueryEntry
& rEntry
= aParam
.GetEntry(0);
7067 if ( !FillEntry(rEntry
) )
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
7079 bool bFound
= false;
7080 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
7084 if (rItem
.meType
== ScQueryEntry::ByString
)
7087 bFound
= (fDataVal
<= rItem
.mfVal
);
7091 if (rItem
.meType
!= ScQueryEntry::ByString
)
7094 bFound
= (ScGlobal::GetCollator().compareString(aDataStr
.getString(), rItem
.maString
.getString()) <= 0);
7105 if (pResMat
->IsValue( 0, 0 ))
7106 PushDouble(pResMat
->GetDouble( 0, 0 ));
7108 PushString(pResMat
->GetString(0, 0));
7110 else if (nParamCount
== 3)
7112 switch (eResArrayType
)
7115 PushDouble( fResVal
);
7118 PushString( aResStr
);
7122 PushCellResultToken( true, ScAddress( nResCol1
, nResRow1
, nResTab
), nullptr, nullptr);
7125 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, single value data");
7126 PushIllegalParameter();
7131 switch (eDataArrayType
)
7134 PushDouble( fDataVal
);
7137 PushString( aDataStr
);
7140 PushCellResultToken( true, aDataAdr
, nullptr, nullptr);
7143 assert(!"ScInterpreter::ScLookup: unhandled eDataArrayType, single value data");
7144 PushIllegalParameter();
7150 // Now, perform the search to compute the delta position (nDelta).
7154 // Data array is given as a matrix.
7155 rEntry
.bDoQuery
= true;
7156 rEntry
.eOp
= SC_LESS_EQUAL
;
7157 bool bFound
= false;
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)
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
);
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
);
7218 ScMatrixRef pTempMat
= GetNewMat( 1, nElems
, /*bEmpty*/true );
7219 pTempMat
->PutDoubleVector( vArray
, 0, 0);
7220 pDataMat2
= std::move(pTempMat
);
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
= std::move(pTempMat
);
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
);
7255 // exact match. find the last item with the same value.
7256 lcl_GetLastMatch( nMid
, aMatAcc2
, nLenMajor
);
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);
7276 if (nDelta
== static_cast<SCCOLROW
>(nLenMajor
-2)) // last item
7278 sal_Int32 nCmp
= lcl_CompareMatrix2Query(nDelta
+1, aMatAcc2
, aParam
, rEntry
, bMatchWholeCell
);
7281 // either the last item is an exact match or the real
7282 // hit is beyond the last item.
7287 else if (nDelta
> 0) // valid hit must be 2nd item or higher
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.
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
))
7318 // Now that we've found the delta, push the result back to the cell.
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())
7330 if (aResMatAcc
.IsValue(nDelta
))
7331 PushDouble(aResMatAcc
.GetDouble(nDelta
));
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
)
7344 // Use the result array vector. Note that the result array is assumed
7345 // to be a vector (i.e. 1-dimensional array).
7348 aAdr
.SetTab(nResTab
);
7349 bool bResVertical
= (nResRow2
- nResRow1
) > 0;
7352 SCROW nTempRow
= static_cast<SCROW
>(nResRow1
+ nDelta
);
7353 if (nTempRow
> mrDoc
.MaxRow())
7358 aAdr
.SetCol(nResCol1
);
7359 aAdr
.SetRow(nTempRow
);
7363 SCCOL nTempCol
= static_cast<SCCOL
>(nResCol1
+ nDelta
);
7364 if (nTempCol
> mrDoc
.MaxCol())
7369 aAdr
.SetCol(nTempCol
);
7370 aAdr
.SetRow(nResRow1
);
7372 PushCellResultToken( true, aAdr
, nullptr, nullptr);
7382 switch (eResArrayType
)
7385 PushDouble( fResVal
);
7388 PushString( aResStr
);
7397 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, array search");
7398 PushIllegalParameter();
7403 // No result array. Use the data array to get the final value from.
7404 // Propagate errors from matrix again.
7405 pDataMat
->SetErrorInterpreter( this);
7408 if (pDataMat
->IsValue(nC
-1, nDelta
))
7409 PushDouble(pDataMat
->GetDouble(nC
-1, nDelta
));
7411 PushString(pDataMat
->GetString(nC
-1, nDelta
));
7415 if (pDataMat
->IsValue(nDelta
, nR
-1))
7416 PushDouble(pDataMat
->GetDouble(nDelta
, nR
-1));
7418 PushString(pDataMat
->GetString(nDelta
, nR
-1));
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);
7443 // Advance Entry.nField in iterator upon switching columns if
7445 aCellIter
.SetAdvanceQueryParamEntryField(!bVertical
);
7446 if ( !aCellIter
.FindEqualOrSortedLastInRange(nC
, nR
) )
7452 SCCOLROW nDelta
= bVertical
? static_cast<SCSIZE
>(nR
-nRow1
) : static_cast<SCSIZE
>(nC
-nCol1
);
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())
7464 if (aResMatAcc
.IsValue(nDelta
))
7465 PushDouble(aResMatAcc
.GetDouble(nDelta
));
7467 PushString(aResMatAcc
.GetString(nDelta
));
7469 else if (nParamCount
== 3)
7471 /* TODO: the entire switch is a copy of the array search result, factor
7473 switch (eResArrayType
)
7478 // Use the result array vector. Note that the result array is assumed
7479 // to be a vector (i.e. 1-dimensional array).
7482 aAdr
.SetTab(nResTab
);
7483 bool bResVertical
= (nResRow2
- nResRow1
) > 0;
7486 SCROW nTempRow
= static_cast<SCROW
>(nResRow1
+ nDelta
);
7487 if (nTempRow
> mrDoc
.MaxRow())
7492 aAdr
.SetCol(nResCol1
);
7493 aAdr
.SetRow(nTempRow
);
7497 SCCOL nTempCol
= static_cast<SCCOL
>(nResCol1
+ nDelta
);
7498 if (nTempCol
> mrDoc
.MaxCol())
7503 aAdr
.SetCol(nTempCol
);
7504 aAdr
.SetRow(nResRow1
);
7506 PushCellResultToken( true, aAdr
, nullptr, nullptr);
7516 switch (eResArrayType
)
7519 PushDouble( fResVal
);
7522 PushString( aResStr
);
7531 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, range search");
7532 PushIllegalParameter();
7537 // Regardless of whether or not the result array exists, the last
7538 // array is always used as the "result" array.
7544 SCROW nTempRow
= static_cast<SCROW
>(nRow1
+ nDelta
);
7545 if (nTempRow
> mrDoc
.MaxRow())
7551 aAdr
.SetRow(nTempRow
);
7555 SCCOL nTempCol
= static_cast<SCCOL
>(nCol1
+ nDelta
);
7556 if (nTempCol
> mrDoc
.MaxCol())
7561 aAdr
.SetCol(nTempCol
);
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))
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;
7594 const ScComplexRefData
* refData
= nullptr;
7595 StackVar eType
= GetStackType();
7596 if (eType
== svDoubleRef
)
7598 refData
= GetStackDoubleRef(0);
7600 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
7603 PushIllegalParameter();
7607 else if (eType
== svSingleRef
)
7609 PopSingleRef(nCol1
, nRow1
, nTab1
);
7613 else if (eType
== svMatrix
|| eType
== svExternalDoubleRef
|| eType
== svExternalSingleRef
)
7618 pMat
->GetDimensions(nC
, nR
);
7621 PushIllegalParameter();
7627 PushIllegalParameter();
7631 if ( fIndex
< 0.0 || (bHLookup
? (pMat
? (fIndex
>= nR
) : (fIndex
+nRow1
> nRow2
)) : (pMat
? (fIndex
>= nC
) : (fIndex
+nCol1
> nCol2
)) ) )
7633 PushIllegalArgument();
7637 SCROW nZIndex
= static_cast<SCROW
>(fIndex
);
7638 SCCOL nSpIndex
= static_cast<SCCOL
>(fIndex
);
7642 nZIndex
+= nRow1
; // value row
7643 nSpIndex
= sal::static_int_cast
<SCCOL
>( nSpIndex
+ nCol1
); // value column
7646 if (nGlobalError
!= FormulaError::NONE
)
7648 PushIllegalParameter();
7652 ScQueryParam aParam
;
7653 aParam
.nCol1
= nCol1
;
7654 aParam
.nRow1
= nRow1
;
7657 aParam
.nCol2
= nCol2
;
7658 aParam
.nRow2
= nRow1
; // search only in the first row
7659 aParam
.bByRow
= false;
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;
7671 rEntry
.eOp
= SC_LESS_EQUAL
;
7672 if ( !FillEntry(rEntry
) )
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
;
7685 SCSIZE nMatCount
= bHLookup
? nC
: nR
;
7686 SCSIZE nDelta
= SCSIZE_MAX
;
7687 if (rItem
.meType
== ScQueryEntry::ByString
)
7690 //TODO: enable regex on matrix strings
7694 CollatorWrapper
& rCollator
= ScGlobal::GetCollator();
7695 for (SCSIZE i
= 0; i
< nMatCount
; i
++)
7697 if (bHLookup
? pMat
->IsStringOrEmpty(i
, 0) : pMat
->IsStringOrEmpty(0, i
))
7700 rCollator
.compareString(
7701 bHLookup
? pMat
->GetString(i
,0).getString() : pMat
->GetString(0,i
).getString(), aParamStr
.getString());
7704 else if (i
>0) // #i2168# ignore first mismatch
7715 for (SCSIZE i
= 0; i
< nMatCount
; i
++)
7717 if (pMat
->IsStringOrEmpty(i
, 0))
7719 if (pMat
->GetString(i
,0).getDataIgnoreCase() == aParamStr
.getDataIgnoreCase())
7729 nDelta
= pMat
->MatchStringInColumns(aParamStr
, 0, 0);
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
)
7753 for (SCSIZE i
= 0; i
< nMatCount
; i
++)
7755 if (! pMat
->IsStringOrEmpty(i
, 0) )
7757 if ( pMat
->GetDouble(i
,0) == rItem
.mfVal
)
7767 nDelta
= pMat
->MatchDoubleInColumns(rItem
.mfVal
, 0, 0);
7771 if ( nDelta
!= SCSIZE_MAX
)
7773 SCSIZE nX
= static_cast<SCSIZE
>(nSpIndex
);
7780 nY
= static_cast<SCSIZE
>(nZIndex
);
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());
7790 PushDouble(pMat
->GetDouble( nX
, nY
));
7802 rEntry
.nField
= nCol1
;
7803 bool bFound
= false;
7807 rEntry
.eOp
= SC_LESS_EQUAL
;
7810 ScQueryCellIteratorDirect
aCellIter(mrDoc
, mrContext
, nTab1
, aParam
, false, false);
7811 // advance Entry.nField in Iterator upon switching columns
7812 aCellIter
.SetAdvanceQueryParamEntryField( true );
7816 bFound
= aCellIter
.FindEqualOrSortedLastInRange( nCol
, nRow1_temp
);
7818 else if ( aCellIter
.GetFirst() )
7821 nCol
= aCellIter
.GetCol();
7827 ScAddress
aResultPos( nCol1
, nRow1
, nTab1
);
7828 bFound
= LookupQueryWithCache( aResultPos
, aParam
, refData
, 1 /*searchfwd*/, SC_OPCODE_V_LOOKUP
);
7829 nRow
= aResultPos
.Row();
7835 ScAddress
aAdr( nCol
, nRow
, nTab1
);
7836 PushCellResultToken( true, aAdr
, nullptr, nullptr);
7843 bool ScInterpreter::FillEntry(ScQueryEntry
& rEntry
)
7845 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
7846 switch ( GetStackType() )
7850 rItem
.meType
= ScQueryEntry::ByValue
;
7851 rItem
.mfVal
= GetDouble();
7856 rItem
.meType
= ScQueryEntry::ByString
;
7857 rItem
.maString
= GetString();
7864 if ( !PopDoubleRefOrSingleRef( aAdr
) )
7869 ScRefCellValue
aCell(mrDoc
, aAdr
);
7870 if (aCell
.hasNumeric())
7872 rItem
.meType
= ScQueryEntry::ByValue
;
7873 rItem
.mfVal
= GetCellValue(aAdr
, aCell
);
7877 GetCellString(rItem
.maString
, aCell
);
7878 rItem
.meType
= ScQueryEntry::ByString
;
7882 case svExternalDoubleRef
:
7883 case svExternalSingleRef
:
7886 svl::SharedString aStr
;
7887 const ScMatValType nType
= GetDoubleOrStringFromMatrix(rItem
.mfVal
, aStr
);
7888 rItem
.maString
= std::move(aStr
);
7889 rItem
.meType
= ScMatrix::IsNonValueType(nType
) ?
7890 ScQueryEntry::ByString
: ScQueryEntry::ByValue
;
7895 PushIllegalParameter();
7898 } // switch ( GetStackType() )
7902 void ScInterpreter::ScVLookup()
7904 CalculateLookup(false);
7907 void ScInterpreter::ScXLookup()
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
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 ) )
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
);
7930 PushIllegalParameter();
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
);
7944 PushIllegalParameter();
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() )
7974 PopSingleRef(nSearchCol1
, nSearchRow1
, nSearchTab1
);
7975 nSearchCol2
= nSearchCol1
;
7976 nSearchRow2
= nSearchRow1
;
7977 nrC
= nSearchCol2
- nSearchCol1
+ 1;
7978 nrR
= nSearchRow2
- nSearchRow1
+ 1;
7982 PopDoubleRef(nSearchCol1
, nSearchRow1
, nSearchTab1
, nSearchCol2
, nSearchRow2
, nSearchTab2
);
7983 if (nSearchTab1
!= nSearchTab2
)
7985 PushIllegalParameter();
7988 nrC
= nSearchCol2
- nSearchCol1
+ 1;
7989 nrR
= nSearchRow2
- nSearchRow1
+ 1;
7993 case svExternalDoubleRef
:
7995 if (GetStackType() == svMatrix
)
7996 prMat
= PopMatrix();
7998 PopExternalDoubleRef(prMat
);
8002 PushIllegalParameter();
8005 prMat
->GetDimensions(nrC
, nrR
);
8010 PushIllegalParameter();
8014 // 2nd argument is vector to be searched
8015 SCSIZE nsC
= 0, nsR
= 0;
8016 switch ( GetStackType() )
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;
8028 vsa
.pMatSrc
= nullptr;
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();
8036 nsC
= vsa
.nCol2
- vsa
.nCol1
+ 1;
8037 nsR
= vsa
.nRow2
- vsa
.nRow1
+ 1;
8041 case svExternalDoubleRef
:
8043 if (GetStackType() == svMatrix
)
8044 vsa
.pMatSrc
= PopMatrix();
8046 PopExternalDoubleRef(vsa
.pMatSrc
);
8050 PushIllegalParameter();
8053 vsa
.pMatSrc
->GetDimensions( nsC
, nsR
);
8058 PushIllegalParameter();
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();
8068 // 1st argument is search value
8069 if (nGlobalError
== FormulaError::NONE
)
8071 switch ( GetRawStackType() )
8076 vsa
.isEmptySearch
= true;
8077 vsa
.isStringSearch
= false;
8078 vsa
.sSearchStr
= GetString();
8084 vsa
.isStringSearch
= false;
8085 vsa
.fSearchVal
= GetDouble();
8091 vsa
.isStringSearch
= true;
8092 vsa
.sSearchStr
= GetString();
8100 if ( !PopDoubleRefOrSingleRef( aAdr
) )
8105 ScRefCellValue
aCell(mrDoc
, aAdr
);
8106 if (aCell
.hasNumeric())
8108 vsa
.isStringSearch
= false;
8109 vsa
.fSearchVal
= GetCellValue(aAdr
, aCell
);
8113 vsa
.isStringSearch
= true;
8114 GetCellString(vsa
.sSearchStr
, aCell
);
8119 case svExternalSingleRef
:
8121 ScExternalRefCache::TokenRef pToken
;
8122 PopExternalSingleRef(pToken
);
8123 if (nGlobalError
!= FormulaError::NONE
)
8125 PushError( nGlobalError
);
8128 if (pToken
->GetType() == svDouble
)
8130 vsa
.isStringSearch
= false;
8131 vsa
.fSearchVal
= pToken
->GetDouble();
8135 vsa
.isStringSearch
= true;
8136 vsa
.sSearchStr
= pToken
->GetString();
8141 case svExternalDoubleRef
:
8144 ScMatValType nType
= GetDoubleOrStringFromMatrix(
8145 vsa
.fSearchVal
, vsa
.sSearchStr
);
8146 vsa
.isStringSearch
= ScMatrix::IsNonValueType(nType
);
8152 PushIllegalParameter();
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
) );
8170 nX
= static_cast<SCSIZE
>(0);
8178 nY
= static_cast<SCSIZE
>(0);
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 )
8187 // result is matrix, make / fill matrix with output and push that
8188 ScMatrixRef pResMat
= GetNewMat(nResCols
, nResRows
, /*bEmpty*/true);
8191 for (SCSIZE i
= 0; i
< nResCols
; i
++)
8193 for (SCSIZE j
= 0; j
< nResRows
; 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
);
8212 pResMat
->PutDouble(prMat
->GetDouble(ri
, rj
), i
, j
);
8215 PushMatrix(pResMat
);
8219 PushIllegalParameter();
8225 // result is a double ref
8226 PushDoubleRef(nSearchCol1
+ nX
, nSearchRow1
+ nY
, nSearchTab1
,
8227 nSearchCol1
+ (nResCols
- 1) + nX
, nSearchRow1
+ (nResRows
- 1) + nY
, nSearchTab1
);
8232 // result is a single ref
8233 PushSingleRef(nSearchCol1
+ nX
, nSearchRow1
+ nY
, nSearchTab1
);
8238 if ( vsa
.isResultNA
)
8240 if ( xNotFound
&& ( xNotFound
->GetType() != svMissing
) )
8242 nGlobalError
= nFirstMatchError
;
8243 PushTokenRef(xNotFound
);
8251 void ScInterpreter::ScFilter()
8253 sal_uInt8 nParamCount
= GetByte();
8254 if (!MustHaveParamCount(nParamCount
, 2, 3))
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
;
8268 // take 2nd argument criteria bool array
8269 switch ( GetStackType() )
8272 case svExternalDoubleRef
:
8273 case svExternalSingleRef
:
8277 pCondResultMatrix
= GetMatrix();
8278 if (!pCondResultMatrix
)
8280 PushError(FormulaError::IllegalParameter
);
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
);
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
)
8313 if (auto it
= std::find_if(aResValues
.begin(), aResValues
.end(), matchNum
); it
!= aResValues
.end())
8323 PushIllegalParameter();
8328 // bail out, no need to evaluate other arguments
8329 if (nGlobalError
!= FormulaError::NONE
)
8331 PushError(nGlobalError
);
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() )
8346 case svExternalSingleRef
:
8347 case svExternalDoubleRef
:
8349 pQueryMatrix
= GetMatrix();
8352 PushError( FormulaError::IllegalParameter
);
8356 pQueryMatrix
->GetDimensions( nC
, nR
);
8357 nQueryCol2
= static_cast<SCCOL
>(nC
- 1);
8358 nQueryRow2
= static_cast<SCROW
>(nR
- 1);
8362 PushError( FormulaError::IllegalParameter
);
8366 // bail out, no need to set a matrix if we have no result
8369 if (xNotFound
&& (xNotFound
->GetType() != svMissing
))
8370 PushTokenRef(xNotFound
);
8372 PushError(FormulaError::NestedArray
);
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
++);
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
++);
8411 pResMat
->PutDouble(pQueryMatrix
->GetDouble(iC
, iR
), nResPos
++);
8418 PushError(FormulaError::IllegalParameter
);
8423 PushMatrix(pResMat
);
8425 PushError(FormulaError::NestedArray
);
8428 void ScInterpreter::ScSort()
8430 sal_uInt8 nParamCount
= GetByte();
8431 if (!MustHaveParamCount(nParamCount
, 1, 4))
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();
8450 aSortOrderValues
.clear();
8451 pSortOrder
->GetDoubleArray(aSortOrderValues
);
8452 for (const double& sortOrder
: aSortOrderValues
)
8454 if (sortOrder
!= 1.0 && sortOrder
!= -1.0)
8456 PushIllegalParameter();
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();
8471 aSortIndexValues
.clear();
8472 pSortIndex
->GetDoubleArray(aSortIndexValues
);
8473 for (double& sortIndex
: aSortIndexValues
)
8477 PushIllegalParameter();
8486 if (aSortIndexValues
.size() != aSortOrderValues
.size() && aSortOrderValues
.size() > 1)
8488 PushIllegalParameter();
8492 // 1st argument is vector to be sorted
8493 SCSIZE nsC
= 0, nsR
= 0;
8494 ScMatrixRef pMatSrc
= nullptr;
8495 switch ( GetStackType() )
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;
8507 PopDoubleRef(aSortData
.nCol1
, aSortData
.nRow1
, aSortData
.nSourceTab
, aSortData
.nCol2
, aSortData
.nRow2
, nTab2
);
8508 if (aSortData
.nSourceTab
!= nTab2
)
8510 PushIllegalParameter();
8513 nsC
= aSortData
.nCol2
- aSortData
.nCol1
+ 1;
8514 nsR
= aSortData
.nRow2
- aSortData
.nRow1
+ 1;
8518 case svExternalSingleRef
:
8519 case svExternalDoubleRef
:
8521 pMatSrc
= GetMatrix();
8524 PushIllegalParameter();
8527 pMatSrc
->GetDimensions(nsC
, nsR
);
8528 if (nsC
== 0 || nsR
== 0)
8530 PushIllegalArgument();
8533 aSortData
.nCol2
= nsC
- 1; // aSortData.nCol1 = 0
8534 aSortData
.nRow2
= nsR
- 1; // aSortData.nRow1 = 0
8539 PushIllegalParameter();
8543 aSortData
.maKeyState
.resize(aSortIndexValues
.size());
8544 for (size_t i
= 0; i
< aSortIndexValues
.size(); i
++)
8546 SCSIZE fIndex
= static_cast<SCSIZE
>(double_to_int32(aSortIndexValues
[i
]));
8547 if ((aSortData
.bByRow
&& fIndex
+ 1 > nsC
) || (!aSortData
.bByRow
&& fIndex
+ 1 > nsR
))
8549 PushIllegalParameter();
8553 aSortData
.maKeyState
[i
].bDoSort
= true;
8554 aSortData
.maKeyState
[i
].nField
= (aSortData
.bByRow
? aSortData
.nCol1
: aSortData
.nRow1
) + fIndex
;
8556 if (aSortIndexValues
.size() == aSortOrderValues
.size())
8557 aSortData
.maKeyState
[i
].bAscending
= (aSortOrderValues
[i
] == 1.0);
8559 aSortData
.maKeyState
[i
].bAscending
= (aSortOrderValues
[0] == 1.0);
8563 std::vector
<SCCOLROW
> aOrderIndices
= GetSortOrder(aSortData
, pMatSrc
);
8564 // create sorted matrix
8565 ScMatrixRef pResMat
= CreateSortedMatrix(aSortData
, pMatSrc
,
8566 ScRange(aSortData
.nCol1
, aSortData
.nRow1
, aSortData
.nSourceTab
,
8567 aSortData
.nCol2
, aSortData
.nRow2
, aSortData
.nSourceTab
),
8568 aOrderIndices
, nsC
, nsR
);
8571 PushMatrix(pResMat
);
8573 PushIllegalParameter();
8576 void ScInterpreter::ScSortBy()
8578 sal_uInt8 nParamCount
= GetByte();
8580 if (nParamCount
< 2/*|| (nParamCount % 2 != 1)*/)
8582 PushError(FormulaError::ParameterExpected
);
8586 sal_uInt8 nSortCount
= nParamCount
/ 2;
8588 ScSortParam aSortData
;
8589 aSortData
.maKeyState
.resize(nSortCount
);
8591 // 127th, ..., 3rd and 2nd argument: sort by range/array and sort orders pair
8592 sal_uInt8 nSortBy
= nSortCount
;
8593 ScMatrixRef pFullMatSortBy
= nullptr;
8594 while (nSortBy
-- > 0 && nGlobalError
== FormulaError::NONE
)
8596 // 3rd argument sort_order optional: default ascending
8597 if (nParamCount
>= 3 && (nParamCount
% 2 == 1))
8599 sal_Int8 nSortOrder
= static_cast<sal_Int8
>(GetInt32WithDefault(1));
8600 if (nSortOrder
!= 1 && nSortOrder
!= -1)
8602 PushIllegalParameter();
8605 aSortData
.maKeyState
[nSortBy
].bAscending
= (nSortOrder
== 1);
8609 // 2nd argument: take sort by ranges
8610 ScMatrixRef pMatSortBy
= nullptr;
8611 SCSIZE nbyC
= 0, nbyR
= 0;
8612 switch (GetStackType())
8617 case svExternalSingleRef
:
8618 case svExternalDoubleRef
:
8620 if (nSortCount
== 1)
8622 pFullMatSortBy
= GetMatrix();
8623 if (!pFullMatSortBy
)
8625 PushIllegalParameter();
8628 pFullMatSortBy
->GetDimensions(nbyC
, nbyR
);
8632 pMatSortBy
= GetMatrix();
8635 PushIllegalParameter();
8638 pMatSortBy
->GetDimensions(nbyC
, nbyR
);
8641 // last->first (backward) sortby array
8642 if (nSortBy
== nSortCount
- 1)
8644 if (nbyC
== 1 && nbyR
> 1)
8645 aSortData
.bByRow
= true;
8646 else if (nbyR
== 1 && nbyC
> 1)
8647 aSortData
.bByRow
= false;
8650 PushIllegalParameter();
8656 pFullMatSortBy
= GetNewMat(aSortData
.bByRow
? (nbyC
* nSortCount
) : nbyC
,
8657 aSortData
.bByRow
? nbyR
: (nbyR
* nSortCount
), /*bEmpty*/true);
8664 PushIllegalParameter();
8668 // ..., penultimate sortby arrays
8669 if (nSortCount
> 1 && nSortBy
<= nSortCount
- 1)
8671 SCSIZE nCheckCol
= 0, nCheckRow
= 0;
8672 pFullMatSortBy
->GetDimensions(nCheckCol
, nCheckRow
);
8673 if ((aSortData
.bByRow
&& nbyR
== nCheckRow
&& nbyC
== 1) ||
8674 (!aSortData
.bByRow
&& nbyC
== nCheckCol
&& nbyR
== 1))
8676 for (SCSIZE ci
= 0; ci
< nbyC
; ci
++)//col
8678 for (SCSIZE rj
= 0; rj
< nbyR
; rj
++)//row
8680 if (pMatSortBy
->IsEmptyCell(ci
, rj
))
8682 if (aSortData
.bByRow
)
8683 pFullMatSortBy
->PutEmpty(ci
+ nSortBy
, rj
);
8685 pFullMatSortBy
->PutEmpty(ci
, rj
+ nSortBy
);
8687 else if (pMatSortBy
->IsStringOrEmpty(ci
, rj
))
8689 if (aSortData
.bByRow
)
8690 pFullMatSortBy
->PutString(pMatSortBy
->GetString(ci
, rj
), ci
+ nSortBy
, rj
);
8692 pFullMatSortBy
->PutString(pMatSortBy
->GetString(ci
, rj
), ci
, rj
+ nSortBy
);
8696 if (aSortData
.bByRow
)
8697 pFullMatSortBy
->PutDouble(pMatSortBy
->GetDouble(ci
, rj
), ci
+ nSortBy
, rj
);
8699 pFullMatSortBy
->PutDouble(pMatSortBy
->GetDouble(ci
, rj
), ci
, rj
+ nSortBy
);
8706 PushIllegalParameter();
8711 aSortData
.maKeyState
[nSortBy
].bDoSort
= true;
8712 aSortData
.maKeyState
[nSortBy
].nField
= nSortBy
;
8717 // 1st argument is the range/array to be sorted
8718 SCSIZE nsC
= 0, nsR
= 0;
8719 SCCOL nSortCol1
= 0, nSortCol2
= 0;
8720 SCROW nSortRow1
= 0, nSortRow2
= 0;
8721 SCTAB nSortTab1
= 0, nSortTab2
= 0;
8722 ScMatrixRef pMatSrc
= nullptr;
8723 switch ( GetStackType() )
8726 PopSingleRef(nSortCol1
, nSortRow1
, nSortTab1
);
8727 nSortCol2
= nSortCol1
;
8728 nSortRow2
= nSortRow1
;
8729 nsC
= nSortCol2
- nSortCol1
+ 1;
8730 nsR
= nSortRow2
- nSortRow1
+ 1;
8734 PopDoubleRef(nSortCol1
, nSortRow1
, nSortTab1
, nSortCol2
, nSortRow2
, nSortTab2
);
8735 if (nSortTab1
!= nSortTab2
)
8737 PushIllegalParameter();
8740 nsC
= nSortCol2
- nSortCol1
+ 1;
8741 nsR
= nSortRow2
- nSortRow1
+ 1;
8745 case svExternalSingleRef
:
8746 case svExternalDoubleRef
:
8748 pMatSrc
= GetMatrix();
8751 PushIllegalParameter();
8754 pMatSrc
->GetDimensions(nsC
, nsR
);
8755 if (nsC
== 0 || nsR
== 0)
8757 PushIllegalArgument();
8760 nSortCol2
= nsC
- 1; // nSortCol1 = 0
8761 nSortRow2
= nsR
- 1; // nSortRow1 = 0
8766 PushIllegalParameter();
8770 SCSIZE nCheckMatrixCol
= 0, nCheckMatrixRow
= 0;
8771 pFullMatSortBy
->GetDimensions(nCheckMatrixCol
, nCheckMatrixRow
);
8772 if (nGlobalError
!= FormulaError::NONE
)
8774 PushError(nGlobalError
);
8777 else if ((aSortData
.bByRow
&& nsR
!= nCheckMatrixRow
) ||
8778 (!aSortData
.bByRow
&& nsC
!= nCheckMatrixCol
))
8780 PushIllegalParameter();
8785 aSortData
.nCol2
= nCheckMatrixCol
- 1;
8786 aSortData
.nRow2
= nCheckMatrixRow
- 1;
8790 std::vector
<SCCOLROW
> aOrderIndices
= GetSortOrder(aSortData
, pFullMatSortBy
);
8791 // create sorted matrix
8792 ScMatrixRef pResMat
= CreateSortedMatrix(aSortData
, pMatSrc
,
8793 ScRange(nSortCol1
, nSortRow1
, nSortTab1
, nSortCol2
, nSortRow2
, nSortTab2
),
8794 aOrderIndices
, nsC
, nsR
);
8797 PushMatrix(pResMat
);
8799 PushIllegalParameter();
8802 void ScInterpreter::ScUnique()
8804 sal_uInt8 nParamCount
= GetByte();
8805 if (!MustHaveParamCount(nParamCount
, 1, 3))
8808 // 3rd argument optional - Exactly_once: default FALSE
8809 bool bExactly_once
= false;
8810 if (nParamCount
== 3)
8811 bExactly_once
= GetBoolWithDefault(false);
8813 // 2nd argument optional - default: By_Col = false --> bByRow = true
8815 if (nParamCount
>= 2)
8816 bByRow
= !GetBoolWithDefault(false);
8818 // 1st argument: take unique search range
8819 ScMatrixRef pMatSource
= nullptr;
8820 SCSIZE nsC
= 0, nsR
= 0;
8821 switch (GetStackType())
8826 case svExternalSingleRef
:
8827 case svExternalDoubleRef
:
8829 pMatSource
= GetMatrix();
8832 PushIllegalParameter();
8836 pMatSource
->GetDimensions(nsC
, nsR
);
8841 PushIllegalParameter();
8845 if (nGlobalError
!= FormulaError::NONE
|| nsC
< 1 || nsR
< 1)
8847 PushIllegalArgument();
8851 // Create unique dataset
8852 std::unordered_set
<OUString
> aStrSet
;
8853 std::vector
<std::pair
<SCSIZE
, OUString
>> aResPos
;
8854 SCSIZE nOut
= bByRow
? nsR
: nsC
;
8855 SCSIZE nIn
= bByRow
? nsC
: nsR
;
8857 for (SCSIZE i
= 0; i
< nOut
; i
++)
8860 for (SCSIZE j
= 0; j
< nIn
; j
++)
8862 OUString aCellStr
= bByRow
? pMatSource
->GetString(mrContext
, j
, i
).getString() :
8863 pMatSource
->GetString(mrContext
, i
, j
).getString();
8864 aStr
+= aCellStr
+ u
"\x0001";
8867 if (aStrSet
.insert(aStr
).second
) // unique if inserted
8869 aResPos
.emplace_back(std::make_pair(i
, aStr
));
8875 auto it
= std::find_if(aResPos
.begin(), aResPos
.end(),
8876 [&aStr
](const std::pair
<SCSIZE
, OUString
>& aRes
)
8878 return aRes
.second
.equals(aStr
);
8881 if (it
!= aResPos
.end())
8887 if (aResPos
.size() == 0)
8889 if (nGlobalError
!= FormulaError::NONE
)
8891 PushIllegalArgument();
8899 // fill result matrix with unique values
8900 ScMatrixRef pResMat
= bByRow
? GetNewMat(nsC
, aResPos
.size(), /*bEmpty*/true) :
8901 GetNewMat(aResPos
.size(), nsR
, /*bEmpty*/true);
8902 for (SCSIZE iPos
= 0; iPos
< aResPos
.size(); iPos
++)
8906 for (SCSIZE col
= 0; col
< nsC
; col
++)
8908 if (pMatSource
->IsEmptyCell(col
, aResPos
[iPos
].first
))
8910 pResMat
->PutEmpty(col
, iPos
);
8912 else if (!pMatSource
->IsStringOrEmpty(col
, aResPos
[iPos
].first
))
8914 pResMat
->PutDouble(pMatSource
->GetDouble(col
, aResPos
[iPos
].first
), col
, iPos
);
8918 pResMat
->PutString(pMatSource
->GetString(col
, aResPos
[iPos
].first
), col
, iPos
);
8924 for (SCSIZE row
= 0; row
< nsR
; row
++)
8926 if (pMatSource
->IsEmptyCell(aResPos
[iPos
].first
, row
))
8928 pResMat
->PutEmpty(iPos
, row
);
8930 else if (!pMatSource
->IsStringOrEmpty(aResPos
[iPos
].first
, row
))
8932 pResMat
->PutDouble(pMatSource
->GetDouble(aResPos
[iPos
].first
, row
), iPos
, row
);
8936 pResMat
->PutString(pMatSource
->GetString(aResPos
[iPos
].first
, row
), iPos
, row
);
8944 PushIllegalArgument();
8948 PushMatrix(pResMat
);
8952 void ScInterpreter::getTokensAtParameter( std::unique_ptr
<ScTokenArray
>& pTokens
, short nPos
)
8954 sal_uInt16 nOpen
= 0;
8955 sal_uInt16 nSepCount
= 0;
8956 formula::FormulaTokenArrayPlainIterator
aIter(*pArr
);
8957 formula::FormulaToken
* t
= aIter
.First();
8958 for (t
= aIter
.NextNoSpaces(); t
; t
= aIter
.NextNoSpaces())
8960 OpCode aOpCode
= t
->GetOpCode();
8961 formula::StackVar aIntType
= t
->GetType();
8962 if ((aOpCode
== ocOpen
|| aOpCode
== ocArrayOpen
|| aOpCode
== ocTableRefOpen
) && aIntType
== formula::StackVar::svSep
)
8964 else if ((aOpCode
== ocClose
|| aOpCode
== ocArrayClose
|| aOpCode
== ocTableRefClose
) && aIntType
== formula::StackVar::svSep
)
8966 else if (aOpCode
== ocSep
&& aIntType
== formula::StackVar::svSep
&& nOpen
== 1)
8972 if (nSepCount
== nPos
&& nOpen
> 0)
8974 pTokens
->AddToken(*t
->Clone());
8979 void ScInterpreter::replaceNamesToResult(const std::unordered_map
<OUString
, formula::FormulaToken
*>& rResultIndexes
,
8980 std::unique_ptr
<ScTokenArray
>& pTokens
)
8982 formula::FormulaTokenArrayPlainIterator
aIterResult(*pTokens
);
8983 for (FormulaToken
* t
= aIterResult
.GetNextStringName(); t
; t
= aIterResult
.GetNextStringName())
8985 auto iRes
= rResultIndexes
.find(t
->GetString().getString());
8986 if (iRes
!= rResultIndexes
.end())
8987 pTokens
->ReplaceToken(aIterResult
.GetIndex() - 1, iRes
->second
->Clone(),
8988 FormulaTokenArray::ReplaceMode::CODE_ONLY
);
8992 void ScInterpreter::ScLet()
8994 const short* pJump
= pCur
->GetJump();
8995 short nJumpCount
= pJump
[0];
8996 short nOrgJumpCount
= nJumpCount
;
8998 if (nJumpCount
< 3 || (nJumpCount
% 2 != 1))
9000 PushError(FormulaError::ParameterExpected
);
9001 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
9006 std::unordered_map
<OUString
, formula::FormulaToken
*> nResultIndexes
;
9007 formula::FormulaTokenArrayPlainIterator
aIter(*pArr
);
9008 unique_ptr
<ScTokenArray
> pValueTokens(new ScTokenArray(mrDoc
));
9010 // name and function pairs parameter
9011 while (nJumpCount
> 1)
9013 if (nJumpCount
== nOrgJumpCount
)
9015 aStrName
= GetString().getString();
9017 else if ((nOrgJumpCount
- nJumpCount
+ 1) % 2 == 1)
9019 aIter
.Jump(pJump
[static_cast<short>(nOrgJumpCount
- nJumpCount
+ 1)] - 1);
9020 FormulaToken
* t
= aIter
.NextRPN();
9021 aStrName
= t
->GetString().getString();
9025 PushError(FormulaError::ParameterExpected
);
9026 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
9032 getTokensAtParameter(pValueTokens
, nOrgJumpCount
- nJumpCount
);
9035 // replace names with result tokens
9036 replaceNamesToResult(nResultIndexes
, pValueTokens
);
9038 // calculate the inner results unless we already have a push result token
9039 if (pValueTokens
->GetLen() == 1 && pValueTokens
->GetArray()[0]->GetOpCode() == ocPush
)
9041 if (!nResultIndexes
.insert(std::make_pair(aStrName
, pValueTokens
->GetArray()[0]->Clone())).second
)
9043 PushIllegalParameter();
9044 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
9050 ScCompiler
aComp(mrDoc
, aPos
, *pValueTokens
, mrDoc
.GetGrammar(), false, false, &mrContext
);
9051 aComp
.CompileTokenArray();
9052 ScInterpreter
aInt(mrDoc
.GetFormulaCell(aPos
), mrDoc
, mrContext
, aPos
, *pValueTokens
);
9053 sfx2::LinkManager
aNewLinkMgr(mrDoc
.GetDocumentShell());
9054 aInt
.SetLinkManager(&aNewLinkMgr
);
9055 formula::StackVar aIntType
= aInt
.Interpret();
9057 if (aIntType
== formula::svMatrixCell
)
9059 ScConstMatrixRef
xMat(aInt
.GetResultToken()->GetMatrix());
9060 if (!nResultIndexes
.insert(std::make_pair(aStrName
, new ScMatrixToken(xMat
->Clone()))).second
)
9062 PushIllegalParameter();
9063 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
9069 const FormulaConstTokenRef
& xTok(aInt
.GetResultToken());
9070 if (!nResultIndexes
.insert(std::make_pair(aStrName
, xTok
->Clone())).second
)
9072 PushIllegalParameter();
9073 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
9078 pValueTokens
->Clear();
9081 // last parameter: calculation
9082 getTokensAtParameter(pValueTokens
, nOrgJumpCount
- nJumpCount
);
9085 // replace names with result tokens
9086 replaceNamesToResult(nResultIndexes
, pValueTokens
);
9088 // calculate the final result
9089 ScCompiler
aComp(mrDoc
, aPos
, *pValueTokens
, mrDoc
.GetGrammar(), false, false, &mrContext
);
9090 aComp
.CompileTokenArray();
9091 ScInterpreter
aInt(mrDoc
.GetFormulaCell(aPos
), mrDoc
, mrContext
, aPos
, *pValueTokens
);
9092 sfx2::LinkManager
aNewLinkMgr(mrDoc
.GetDocumentShell());
9093 aInt
.SetLinkManager(&aNewLinkMgr
);
9094 formula::StackVar aIntType
= aInt
.Interpret();
9096 if (aIntType
== formula::svMatrixCell
)
9098 ScConstMatrixRef
xMat(aInt
.GetResultToken()->GetMatrix());
9099 PushTokenRef(new ScMatrixToken(xMat
->Clone()));
9103 const formula::FormulaConstTokenRef
& xLambdaResult(aInt
.GetResultToken());
9106 nGlobalError
= xLambdaResult
->GetError();
9107 if (nGlobalError
== FormulaError::NONE
)
9108 PushTokenRef(xLambdaResult
);
9110 PushError(nGlobalError
);
9114 pValueTokens
.reset();
9115 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
9118 void ScInterpreter::ScSubTotal()
9120 sal_uInt8 nParamCount
= GetByte();
9121 if ( !MustHaveParamCountMinWithStackCheck( nParamCount
, 2 ) )
9124 // We must fish the 1st parameter deep from the stack! And push it on top.
9125 const FormulaToken
* p
= pStack
[ sp
- nParamCount
];
9126 PushWithoutError( *p
);
9127 sal_Int32 nFunc
= GetInt32();
9128 mnSubTotalFlags
|= SubtotalFlags::IgnoreNestedStAg
| SubtotalFlags::IgnoreFiltered
;
9131 // For opcodes 101 through 111, we need to skip hidden cells.
9132 // Other than that these opcodes are identical to 1 through 11.
9133 mnSubTotalFlags
|= SubtotalFlags::IgnoreHidden
;
9137 if ( nGlobalError
!= FormulaError::NONE
|| nFunc
< 1 || nFunc
> 11 )
9138 PushIllegalArgument(); // simulate return on stack, not SetError(...)
9141 cPar
= nParamCount
- 1;
9144 case SUBTOTAL_FUNC_AVE
: ScAverage(); break;
9145 case SUBTOTAL_FUNC_CNT
: ScCount(); break;
9146 case SUBTOTAL_FUNC_CNT2
: ScCount2(); break;
9147 case SUBTOTAL_FUNC_MAX
: ScMax(); break;
9148 case SUBTOTAL_FUNC_MIN
: ScMin(); break;
9149 case SUBTOTAL_FUNC_PROD
: ScProduct(); break;
9150 case SUBTOTAL_FUNC_STD
: ScStDev(); break;
9151 case SUBTOTAL_FUNC_STDP
: ScStDevP(); break;
9152 case SUBTOTAL_FUNC_SUM
: ScSum(); break;
9153 case SUBTOTAL_FUNC_VAR
: ScVar(); break;
9154 case SUBTOTAL_FUNC_VARP
: ScVarP(); break;
9155 default : PushIllegalArgument(); break;
9158 mnSubTotalFlags
= SubtotalFlags::NONE
;
9159 // Get rid of the 1st (fished) parameter.
9160 FormulaConstTokenRef
xRef( PopToken());
9162 PushTokenRef( xRef
);
9165 void ScInterpreter::ScAggregate()
9167 sal_uInt8 nParamCount
= GetByte();
9168 if ( !MustHaveParamCountMinWithStackCheck( nParamCount
, 3 ) )
9171 const FormulaError nErr
= nGlobalError
;
9172 nGlobalError
= FormulaError::NONE
;
9174 // fish the 1st parameter from the stack and push it on top.
9175 const FormulaToken
* p
= pStack
[ sp
- nParamCount
];
9176 PushWithoutError( *p
);
9177 sal_Int32 nFunc
= GetInt32();
9178 // fish the 2nd parameter from the stack and push it on top.
9179 const FormulaToken
* p2
= pStack
[ sp
- ( nParamCount
- 1 ) ];
9180 PushWithoutError( *p2
);
9181 sal_Int32 nOption
= GetInt32();
9183 if ( nGlobalError
!= FormulaError::NONE
|| nFunc
< 1 || nFunc
> 19 )
9185 nGlobalError
= nErr
;
9186 PushIllegalArgument();
9192 case 0 : // ignore nested SUBTOTAL and AGGREGATE functions
9193 mnSubTotalFlags
= SubtotalFlags::IgnoreNestedStAg
;
9195 case 1 : // ignore hidden rows, nested SUBTOTAL and AGGREGATE functions
9196 mnSubTotalFlags
= SubtotalFlags::IgnoreHidden
| SubtotalFlags::IgnoreNestedStAg
;
9198 case 2 : // ignore error values, nested SUBTOTAL and AGGREGATE functions
9199 mnSubTotalFlags
= SubtotalFlags::IgnoreErrVal
| SubtotalFlags::IgnoreNestedStAg
;
9201 case 3 : // ignore hidden rows, error values, nested SUBTOTAL and AGGREGATE functions
9202 mnSubTotalFlags
= SubtotalFlags::IgnoreHidden
| SubtotalFlags::IgnoreErrVal
| SubtotalFlags::IgnoreNestedStAg
;
9204 case 4 : // ignore nothing
9205 mnSubTotalFlags
= SubtotalFlags::NONE
;
9207 case 5 : // ignore hidden rows
9208 mnSubTotalFlags
= SubtotalFlags::IgnoreHidden
;
9210 case 6 : // ignore error values
9211 mnSubTotalFlags
= SubtotalFlags::IgnoreErrVal
;
9213 case 7 : // ignore hidden rows and error values
9214 mnSubTotalFlags
= SubtotalFlags::IgnoreHidden
| SubtotalFlags::IgnoreErrVal
;
9217 nGlobalError
= nErr
;
9218 PushIllegalArgument();
9222 if ((mnSubTotalFlags
& SubtotalFlags::IgnoreErrVal
) == SubtotalFlags::NONE
)
9223 nGlobalError
= nErr
;
9225 cPar
= nParamCount
- 2;
9228 case AGGREGATE_FUNC_AVE
: ScAverage(); break;
9229 case AGGREGATE_FUNC_CNT
: ScCount(); break;
9230 case AGGREGATE_FUNC_CNT2
: ScCount2(); break;
9231 case AGGREGATE_FUNC_MAX
: ScMax(); break;
9232 case AGGREGATE_FUNC_MIN
: ScMin(); break;
9233 case AGGREGATE_FUNC_PROD
: ScProduct(); break;
9234 case AGGREGATE_FUNC_STD
: ScStDev(); break;
9235 case AGGREGATE_FUNC_STDP
: ScStDevP(); break;
9236 case AGGREGATE_FUNC_SUM
: ScSum(); break;
9237 case AGGREGATE_FUNC_VAR
: ScVar(); break;
9238 case AGGREGATE_FUNC_VARP
: ScVarP(); break;
9239 case AGGREGATE_FUNC_MEDIAN
: ScMedian(); break;
9240 case AGGREGATE_FUNC_MODSNGL
: ScModalValue(); break;
9241 case AGGREGATE_FUNC_LARGE
: ScLarge(); break;
9242 case AGGREGATE_FUNC_SMALL
: ScSmall(); break;
9243 case AGGREGATE_FUNC_PERCINC
: ScPercentile( true ); break;
9244 case AGGREGATE_FUNC_QRTINC
: ScQuartile( true ); break;
9245 case AGGREGATE_FUNC_PERCEXC
: ScPercentile( false ); break;
9246 case AGGREGATE_FUNC_QRTEXC
: ScQuartile( false ); break;
9248 nGlobalError
= nErr
;
9249 PushIllegalArgument();
9252 mnSubTotalFlags
= SubtotalFlags::NONE
;
9254 FormulaConstTokenRef
xRef( PopToken());
9255 // Get rid of the 1st and 2nd (fished) parameters.
9258 PushTokenRef( xRef
);
9261 std::unique_ptr
<ScDBQueryParamBase
> ScInterpreter::GetDBParams( bool& rMissingField
)
9263 bool bAllowMissingField
= false;
9264 if ( rMissingField
)
9266 bAllowMissingField
= true;
9267 rMissingField
= false;
9269 if ( GetByte() == 3 )
9271 // First, get the query criteria range.
9272 ::std::unique_ptr
<ScDBRangeBase
> pQueryRef( PopDBDoubleRef() );
9278 svl::SharedString aStr
;
9279 ScRange aMissingRange
;
9280 bool bRangeFake
= false;
9281 switch (GetStackType())
9284 nVal
= ::rtl::math::approxFloor( GetDouble() );
9285 if ( bAllowMissingField
&& nVal
== 0.0 )
9286 rMissingField
= true; // fake missing parameter
9295 PopSingleRef( aAdr
);
9296 ScRefCellValue
aCell(mrDoc
, aAdr
);
9297 if (aCell
.hasNumeric())
9298 nVal
= GetCellValue(aAdr
, aCell
);
9302 GetCellString(aStr
, aCell
);
9307 if ( bAllowMissingField
)
9308 { // fake missing parameter for old SO compatibility
9310 PopDoubleRef( aMissingRange
);
9315 SetError( FormulaError::IllegalParameter
);
9320 if ( bAllowMissingField
)
9321 rMissingField
= true;
9323 SetError( FormulaError::IllegalParameter
);
9327 SetError( FormulaError::IllegalParameter
);
9330 if (nGlobalError
!= FormulaError::NONE
)
9333 unique_ptr
<ScDBRangeBase
> pDBRef( PopDBDoubleRef() );
9335 if (nGlobalError
!= FormulaError::NONE
|| !pDBRef
)
9340 // range parameter must match entire database range
9341 if (pDBRef
->isRangeEqual(aMissingRange
))
9342 rMissingField
= true;
9344 SetError( FormulaError::IllegalParameter
);
9347 if (nGlobalError
!= FormulaError::NONE
)
9350 SCCOL nField
= pDBRef
->getFirstFieldColumn();
9354 nField
= pDBRef
->findFieldColumn(static_cast<SCCOL
>(nVal
));
9357 FormulaError nErr
= FormulaError::NONE
;
9358 nField
= pDBRef
->findFieldColumn(aStr
.getString(), &nErr
);
9362 if (!mrDoc
.ValidCol(nField
))
9365 unique_ptr
<ScDBQueryParamBase
> pParam( pDBRef
->createQueryParam(pQueryRef
.get()) );
9369 // An allowed missing field parameter sets the result field
9370 // to any of the query fields, just to be able to return
9371 // some cell from the iterator.
9372 if ( rMissingField
)
9373 nField
= static_cast<SCCOL
>(pParam
->GetEntry(0).nField
);
9374 pParam
->mnField
= nField
;
9376 SCSIZE nCount
= pParam
->GetEntryCount();
9377 for ( SCSIZE i
=0; i
< nCount
; i
++ )
9379 ScQueryEntry
& rEntry
= pParam
->GetEntry(i
);
9380 if (!rEntry
.bDoQuery
)
9383 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
9384 sal_uInt32 nIndex
= 0;
9385 OUString aQueryStr
= rItem
.maString
.getString();
9386 bool bNumber
= mrContext
.NFIsNumberFormat(
9387 aQueryStr
, nIndex
, rItem
.mfVal
);
9388 rItem
.meType
= bNumber
? ScQueryEntry::ByValue
: ScQueryEntry::ByString
;
9390 if (!bNumber
&& pParam
->eSearchType
== utl::SearchParam::SearchType::Normal
)
9391 pParam
->eSearchType
= DetectSearchType(aQueryStr
, mrDoc
);
9399 void ScInterpreter::DBIterator( ScIterFunc eFunc
)
9403 sal_uLong nCount
= 0;
9404 bool bMissingField
= false;
9405 unique_ptr
<ScDBQueryParamBase
> pQueryParam( GetDBParams(bMissingField
) );
9408 if (!pQueryParam
->IsValidFieldIndex())
9410 SetError(FormulaError::NoValue
);
9413 ScDBQueryDataIterator
aValIter(mrDoc
, mrContext
, std::move(pQueryParam
));
9414 ScDBQueryDataIterator::Value aValue
;
9415 if ( aValIter
.GetFirst(aValue
) && aValue
.mnError
== FormulaError::NONE
)
9419 case ifPRODUCT
: fRes
= 1; break;
9420 case ifMAX
: fRes
= -MAXDOUBLE
; break;
9421 case ifMIN
: fRes
= MAXDOUBLE
; break;
9422 default: ; // nothing
9432 fErg
+= aValue
.mfValue
;
9435 fErg
+= aValue
.mfValue
* aValue
.mfValue
;
9438 fRes
*= aValue
.mfValue
;
9441 if( aValue
.mfValue
> fRes
) fRes
= aValue
.mfValue
;
9444 if( aValue
.mfValue
< fRes
) fRes
= aValue
.mfValue
;
9446 default: ; // nothing
9449 while ( aValIter
.GetNext(aValue
) && aValue
.mnError
== FormulaError::NONE
);
9451 SetError(aValue
.mnError
);
9454 SetError( FormulaError::IllegalParameter
);
9457 case ifCOUNT
: fRes
= nCount
; break;
9458 case ifSUM
: fRes
= fErg
.get(); break;
9459 case ifSUMSQ
: fRes
= fErg
.get(); break;
9460 case ifAVERAGE
: fRes
= div(fErg
.get(), nCount
); break;
9461 default: ; // nothing
9466 void ScInterpreter::ScDBSum()
9468 DBIterator( ifSUM
);
9471 void ScInterpreter::ScDBCount()
9473 bool bMissingField
= true;
9474 unique_ptr
<ScDBQueryParamBase
> pQueryParam( GetDBParams(bMissingField
) );
9477 sal_uLong nCount
= 0;
9478 if ( bMissingField
&& pQueryParam
->GetType() == ScDBQueryParamBase::INTERNAL
)
9479 { // count all matching records
9480 // TODO: currently the QueryIterators only return cell pointers of
9481 // existing cells, so if a query matches an empty cell there's
9482 // nothing returned, and therefore not counted!
9483 // Since this has ever been the case and this code here only came
9484 // into existence to fix #i6899 and it never worked before we'll
9485 // have to live with it until we reimplement the iterators to also
9486 // return empty cells, which would mean to adapt all callers of
9488 ScDBQueryParamInternal
* p
= static_cast<ScDBQueryParamInternal
*>(pQueryParam
.get());
9489 p
->nCol2
= p
->nCol1
; // Don't forget to select only one column.
9490 SCTAB nTab
= p
->nTab
;
9491 // ScQueryCellIteratorDirect doesn't make use of ScDBQueryParamBase::mnField,
9492 // so the source range has to be restricted, like before the introduction
9493 // of ScDBQueryParamBase.
9494 p
->nCol1
= p
->nCol2
= p
->mnField
;
9495 ScQueryCellIteratorDirect
aCellIter(mrDoc
, mrContext
, nTab
, *p
, true, false);
9496 if ( aCellIter
.GetFirst() )
9501 } while ( aCellIter
.GetNext() );
9505 { // count only matching records with a value in the "result" field
9506 if (!pQueryParam
->IsValidFieldIndex())
9508 SetError(FormulaError::NoValue
);
9511 ScDBQueryDataIterator
aValIter(mrDoc
, mrContext
, std::move(pQueryParam
));
9512 ScDBQueryDataIterator::Value aValue
;
9513 if ( aValIter
.GetFirst(aValue
) && aValue
.mnError
== FormulaError::NONE
)
9519 while ( aValIter
.GetNext(aValue
) && aValue
.mnError
== FormulaError::NONE
);
9521 SetError(aValue
.mnError
);
9523 PushDouble( nCount
);
9526 PushIllegalParameter();
9529 void ScInterpreter::ScDBCount2()
9531 bool bMissingField
= true;
9532 unique_ptr
<ScDBQueryParamBase
> pQueryParam( GetDBParams(bMissingField
) );
9535 if (!pQueryParam
->IsValidFieldIndex())
9537 SetError(FormulaError::NoValue
);
9540 sal_uLong nCount
= 0;
9541 pQueryParam
->mbSkipString
= false;
9542 ScDBQueryDataIterator
aValIter(mrDoc
, mrContext
, std::move(pQueryParam
));
9543 ScDBQueryDataIterator::Value aValue
;
9544 if ( aValIter
.GetFirst(aValue
) && aValue
.mnError
== FormulaError::NONE
)
9550 while ( aValIter
.GetNext(aValue
) && aValue
.mnError
== FormulaError::NONE
);
9552 SetError(aValue
.mnError
);
9553 PushDouble( nCount
);
9556 PushIllegalParameter();
9559 void ScInterpreter::ScDBAverage()
9561 DBIterator( ifAVERAGE
);
9564 void ScInterpreter::ScDBMax()
9566 DBIterator( ifMAX
);
9569 void ScInterpreter::ScDBMin()
9571 DBIterator( ifMIN
);
9574 void ScInterpreter::ScDBProduct()
9576 DBIterator( ifPRODUCT
);
9579 void ScInterpreter::GetDBStVarParams( double& rVal
, double& rValCount
)
9581 std::vector
<double> values
;
9582 KahanSum vSum
= 0.0;
9583 KahanSum fSum
= 0.0;
9586 bool bMissingField
= false;
9587 unique_ptr
<ScDBQueryParamBase
> pQueryParam( GetDBParams(bMissingField
) );
9590 if (!pQueryParam
->IsValidFieldIndex())
9592 SetError(FormulaError::NoValue
);
9595 ScDBQueryDataIterator
aValIter(mrDoc
, mrContext
, std::move(pQueryParam
));
9596 ScDBQueryDataIterator::Value aValue
;
9597 if (aValIter
.GetFirst(aValue
) && aValue
.mnError
== FormulaError::NONE
)
9602 values
.push_back(aValue
.mfValue
);
9603 fSum
+= aValue
.mfValue
;
9605 while ((aValue
.mnError
== FormulaError::NONE
) && aValIter
.GetNext(aValue
));
9607 SetError(aValue
.mnError
);
9610 SetError( FormulaError::IllegalParameter
);
9612 double vMean
= fSum
.get() / values
.size();
9614 for (double v
: values
)
9615 vSum
+= (v
- vMean
) * (v
- vMean
);
9620 void ScInterpreter::ScDBStdDev()
9622 double fVal
, fCount
;
9623 GetDBStVarParams( fVal
, fCount
);
9624 PushDouble( sqrt(fVal
/(fCount
-1)));
9627 void ScInterpreter::ScDBStdDevP()
9629 double fVal
, fCount
;
9630 GetDBStVarParams( fVal
, fCount
);
9631 PushDouble( sqrt(fVal
/fCount
));
9634 void ScInterpreter::ScDBVar()
9636 double fVal
, fCount
;
9637 GetDBStVarParams( fVal
, fCount
);
9638 PushDouble(fVal
/(fCount
-1));
9641 void ScInterpreter::ScDBVarP()
9643 double fVal
, fCount
;
9644 GetDBStVarParams( fVal
, fCount
);
9645 PushDouble(fVal
/fCount
);
9648 static bool lcl_IsTableStructuredRef(const OUString
& sRefStr
, sal_Int32
& nIndex
)
9650 nIndex
= ScGlobal::FindUnquoted(sRefStr
, '[');
9651 return (nIndex
> 0 && ScGlobal::FindUnquoted(sRefStr
, ']', nIndex
+ 1) > nIndex
);
9654 void ScInterpreter::ScIndirect()
9656 sal_uInt8 nParamCount
= GetByte();
9657 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
9660 // Reference address syntax for INDIRECT is configurable.
9661 FormulaGrammar::AddressConvention eConv
= maCalcConfig
.meStringRefAddressSyntax
;
9662 if (eConv
== FormulaGrammar::CONV_UNSPECIFIED
)
9663 // Use the current address syntax if unspecified.
9664 eConv
= mrDoc
.GetAddressConvention();
9666 // either CONV_A1_XL_A1 was explicitly configured, or it wasn't possible
9667 // to determine which syntax to use during doc import
9668 bool bTryXlA1
= (eConv
== FormulaGrammar::CONV_A1_XL_A1
);
9670 if (nParamCount
== 2 && 0.0 == GetDouble() )
9672 // Overwrite the config and try Excel R1C1.
9673 eConv
= FormulaGrammar::CONV_XL_R1C1
;
9677 svl::SharedString sSharedRefStr
= GetString();
9678 const OUString
& sRefStr
= sSharedRefStr
.getString();
9679 if (sRefStr
.isEmpty())
9681 // Bail out early for empty cells, rely on "we do have a string" below.
9682 PushError( FormulaError::NoRef
);
9686 const ScAddress::Details
aDetails( bTryXlA1
? FormulaGrammar::CONV_OOO
: eConv
, aPos
);
9687 const ScAddress::Details
aDetailsXlA1( FormulaGrammar::CONV_XL_A1
, aPos
);
9688 SCTAB nTab
= aPos
.Tab();
9690 bool bTableRefNamed
= false;
9691 sal_Int32 nTableRefNamedIndex
= -1;
9692 OUString sTabRefStr
;
9694 // Named expressions and DB range names need to be tried first, as older 1K
9695 // columns allowed names that would now match a 16k columns cell address.
9698 ScRangeData
* pData
= ScRangeStringConverter::GetRangeDataFromString( sRefStr
, nTab
, mrDoc
, eConv
);
9702 // We need this in order to obtain a good range.
9703 pData
->ValidateTabRefs();
9707 // This is the usual way to treat named ranges containing
9708 // relative references.
9709 if (!pData
->IsReference(aRange
, aPos
))
9711 sTabRefStr
= pData
->GetSymbol();
9712 bTableRefNamed
= lcl_IsTableStructuredRef(sTabRefStr
, nTableRefNamedIndex
);
9713 // if bTableRefNamed is true, we have a name that maps to a table structured reference.
9714 // Such a case is handled below.
9718 if (aRange
.aStart
== aRange
.aEnd
)
9719 PushSingleRef( aRange
.aStart
.Col(), aRange
.aStart
.Row(),
9720 aRange
.aStart
.Tab());
9722 PushDoubleRef( aRange
.aStart
.Col(), aRange
.aStart
.Row(),
9723 aRange
.aStart
.Tab(), aRange
.aEnd
.Col(),
9724 aRange
.aEnd
.Row(), aRange
.aEnd
.Tab());
9736 const OUString
& aName( sSharedRefStr
.getIgnoreCaseString() );
9737 ScDBCollection::NamedDBs
& rDBs
= mrDoc
.GetDBCollection()->getNamedDBs();
9738 const ScDBData
* pData
= rDBs
.findByUpperName( aName
);
9743 pData
->GetArea( aRange
);
9745 // In Excel, specifying a table name without [] resolves to the
9746 // same as with [], a range that excludes header and totals
9747 // rows and contains only data rows. Do the same.
9748 if (pData
->HasHeader())
9749 aRange
.aStart
.IncRow();
9750 if (pData
->HasTotals())
9751 aRange
.aEnd
.IncRow(-1);
9753 if (aRange
.aStart
.Row() > aRange
.aEnd
.Row())
9756 if (aRange
.aStart
== aRange
.aEnd
)
9757 PushSingleRef( aRange
.aStart
.Col(), aRange
.aStart
.Row(),
9758 aRange
.aStart
.Tab());
9760 PushDoubleRef( aRange
.aStart
.Col(), aRange
.aStart
.Row(),
9761 aRange
.aStart
.Tab(), aRange
.aEnd
.Col(),
9762 aRange
.aEnd
.Row(), aRange
.aEnd
.Tab());
9769 ScRefAddress aRefAd
, aRefAd2
;
9770 ScAddress::ExternalInfo aExtInfo
;
9771 if ( !bTableRefNamed
&&
9772 (ConvertDoubleRef(mrDoc
, sRefStr
, nTab
, aRefAd
, aRefAd2
, aDetails
, &aExtInfo
) ||
9773 ( bTryXlA1
&& ConvertDoubleRef(mrDoc
, sRefStr
, nTab
, aRefAd
,
9774 aRefAd2
, aDetailsXlA1
, &aExtInfo
) ) ) )
9776 if (aExtInfo
.mbExternal
)
9778 PushExternalDoubleRef(
9779 aExtInfo
.mnFileId
, aExtInfo
.maTabName
,
9780 aRefAd
.Col(), aRefAd
.Row(), aRefAd
.Tab(),
9781 aRefAd2
.Col(), aRefAd2
.Row(), aRefAd2
.Tab());
9784 PushDoubleRef( aRefAd
, aRefAd2
);
9786 else if ( !bTableRefNamed
&&
9787 (ConvertSingleRef(mrDoc
, sRefStr
, nTab
, aRefAd
, aDetails
, &aExtInfo
) ||
9788 ( bTryXlA1
&& ConvertSingleRef (mrDoc
, sRefStr
, nTab
, aRefAd
,
9789 aDetailsXlA1
, &aExtInfo
) ) ) )
9791 if (aExtInfo
.mbExternal
)
9793 PushExternalSingleRef(
9794 aExtInfo
.mnFileId
, aExtInfo
.maTabName
, aRefAd
.Col(), aRefAd
.Row(), aRefAd
.Tab());
9797 PushSingleRef( aRefAd
);
9801 // It may be even a TableRef or an external name.
9802 // Anything else that resolves to one reference could be added
9803 // here, but we don't want to compile every arbitrary string. This
9804 // is already nasty enough...
9805 sal_Int32 nIndex
= bTableRefNamed
? nTableRefNamedIndex
: -1;
9806 bool bTableRef
= bTableRefNamed
;
9807 if (!bTableRefNamed
)
9808 bTableRef
= lcl_IsTableStructuredRef(sRefStr
, nIndex
);
9809 bool bExternalName
= false; // External references would had been consumed above already.
9812 // This is our own file name reference representation centric.. but
9813 // would work also for XL '[doc]'!name and also for
9814 // '[doc]Sheet1'!name ... sickos.
9815 if (sRefStr
[0] == '\'')
9817 // Minimum 'a'#name or 'a'!name
9818 // bTryXlA1 means try both, first our own.
9819 if (bTryXlA1
|| eConv
== FormulaGrammar::CONV_OOO
)
9821 nIndex
= ScGlobal::FindUnquoted( sRefStr
, '#');
9822 if (nIndex
>= 3 && sRefStr
[nIndex
-1] == '\'')
9824 bExternalName
= true;
9825 eConv
= FormulaGrammar::CONV_OOO
;
9828 if (!bExternalName
&& (bTryXlA1
|| eConv
!= FormulaGrammar::CONV_OOO
))
9830 nIndex
= ScGlobal::FindUnquoted( sRefStr
, '!');
9831 if (nIndex
>= 3 && sRefStr
[nIndex
-1] == '\'')
9833 bExternalName
= true;
9839 if (bExternalName
|| bTableRef
)
9843 ScCompiler
aComp( mrDoc
, aPos
, mrDoc
.GetGrammar());
9844 aComp
.SetRefConvention( eConv
); // must be after grammar
9845 std::unique_ptr
<ScTokenArray
> pTokArr( aComp
.CompileString(bTableRefNamed
? sTabRefStr
: sRefStr
));
9847 if (pTokArr
->GetCodeError() != FormulaError::NONE
|| !pTokArr
->GetLen())
9850 // Whatever... use only the specific case.
9853 const formula::FormulaToken
* pTok
= pTokArr
->FirstToken();
9854 if (!pTok
|| pTok
->GetType() != svExternalName
)
9857 else if (!pTokArr
->HasOpCode( ocTableRef
))
9860 aComp
.CompileTokenArray();
9862 // A syntactically valid reference will generate exactly
9863 // one RPN token, a reference or error. Discard everything
9865 if (pTokArr
->GetCodeLen() != 1)
9868 ScTokenRef
xTok( pTokArr
->FirstRPNToken());
9872 switch (xTok
->GetType())
9876 case svExternalSingleRef
:
9877 case svExternalDoubleRef
:
9879 PushTokenRef( xTok
);
9889 PushError( FormulaError::NoRef
);
9893 void ScInterpreter::ScAddressFunc()
9897 sal_uInt8 nParamCount
= GetByte();
9898 if( !MustHaveParamCount( nParamCount
, 2, 5 ) )
9901 if( nParamCount
>= 5 )
9902 sTabStr
= GetString().getString();
9904 FormulaGrammar::AddressConvention eConv
= FormulaGrammar::CONV_OOO
; // default
9905 if (nParamCount
>= 4 && 0.0 == GetDoubleWithDefault( 1.0))
9906 eConv
= FormulaGrammar::CONV_XL_R1C1
;
9909 // If A1 syntax is requested then the actual sheet separator and format
9910 // convention depends on the syntax configured for INDIRECT to match
9911 // that, and if it is unspecified then the document's address syntax.
9912 FormulaGrammar::AddressConvention eForceConv
= maCalcConfig
.meStringRefAddressSyntax
;
9913 if (eForceConv
== FormulaGrammar::CONV_UNSPECIFIED
)
9914 eForceConv
= mrDoc
.GetAddressConvention();
9915 if (eForceConv
== FormulaGrammar::CONV_XL_A1
|| eForceConv
== FormulaGrammar::CONV_XL_R1C1
)
9916 eConv
= FormulaGrammar::CONV_XL_A1
; // for anything Excel use Excel A1
9919 ScRefFlags nFlags
= ScRefFlags::COL_ABS
| ScRefFlags::ROW_ABS
; // default
9920 if( nParamCount
>= 3 )
9922 sal_Int32 n
= GetInt32WithDefault(1);
9930 case 1 : break; // default
9932 case 2 : nFlags
= ScRefFlags::ROW_ABS
; break;
9934 case 3 : nFlags
= ScRefFlags::COL_ABS
; break;
9936 case 4 : nFlags
= ScRefFlags::ZERO
; break; // both relative
9939 nFlags
|= ScRefFlags::VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::COL_VALID
;
9941 SCCOL nCol
= static_cast<SCCOL
>(GetInt16());
9942 SCROW nRow
= static_cast<SCROW
>(GetInt32());
9943 if( eConv
== FormulaGrammar::CONV_XL_R1C1
)
9945 // YUCK! The XL interface actually treats rel R1C1 refs differently
9947 if( !(nFlags
& ScRefFlags::COL_ABS
) )
9948 nCol
+= aPos
.Col() + 1;
9949 if( !(nFlags
& ScRefFlags::ROW_ABS
) )
9950 nRow
+= aPos
.Row() + 1;
9955 if (nGlobalError
!= FormulaError::NONE
|| !mrDoc
.ValidCol( nCol
) || !mrDoc
.ValidRow( nRow
))
9957 PushIllegalArgument();
9961 const ScAddress::Details
aDetails( eConv
, aPos
);
9962 const ScAddress
aAdr( nCol
, nRow
, 0);
9963 OUString
aRefStr(aAdr
.Format(nFlags
, &mrDoc
, aDetails
));
9965 if( nParamCount
>= 5 && !sTabStr
.isEmpty() )
9968 if (eConv
== FormulaGrammar::CONV_OOO
)
9970 // Isolate Tab from 'Doc'#Tab
9971 sal_Int32 nPos
= ScCompiler::GetDocTabPos( sTabStr
);
9974 if (sTabStr
[nPos
+1] == '$')
9975 ++nPos
; // also split 'Doc'#$Tab
9976 aDoc
= sTabStr
.copy( 0, nPos
+1);
9977 sTabStr
= sTabStr
.copy( nPos
+1);
9980 /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may
9981 * need some extra handling to isolate Tab from Doc. */
9982 if (sTabStr
[0] != '\'' || !sTabStr
.endsWith("'"))
9983 ScCompiler::CheckTabQuotes( sTabStr
, eConv
);
9984 if (!aDoc
.isEmpty())
9985 sTabStr
= aDoc
+ sTabStr
;
9986 sTabStr
+= (eConv
== FormulaGrammar::CONV_XL_R1C1
|| eConv
== FormulaGrammar::CONV_XL_A1
) ?
9987 std::u16string_view(u
"!") : std::u16string_view(u
".");
9989 PushString( sTabStr
);
9992 PushString( aRefStr
);
9995 void ScInterpreter::ScOffset()
9997 sal_uInt8 nParamCount
= GetByte();
9998 if ( !MustHaveParamCount( nParamCount
, 3, 5 ) )
10001 bool bNewWidth
= false;
10002 bool bNewHeight
= false;
10003 sal_Int32 nColNew
= 1, nRowNew
= 1;
10004 if (nParamCount
== 5)
10010 nColNew
= GetInt32();
10014 if (nParamCount
>= 4)
10020 nRowNew
= GetInt32();
10024 sal_Int32 nColPlus
= GetInt32();
10025 sal_Int32 nRowPlus
= GetInt32();
10026 if (nGlobalError
!= FormulaError::NONE
)
10028 PushError( nGlobalError
);
10031 if (nColNew
<= 0 || nRowNew
<= 0)
10033 PushIllegalArgument();
10042 switch (GetStackType())
10046 PopSingleRef(nCol1
, nRow1
, nTab1
);
10047 if (!bNewWidth
&& !bNewHeight
)
10049 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
) + nColPlus
);
10050 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
) + nRowPlus
);
10051 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
))
10052 PushIllegalArgument();
10054 PushSingleRef(nCol1
, nRow1
, nTab1
);
10058 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColPlus
);
10059 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowPlus
);
10060 nCol2
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColNew
-1);
10061 nRow2
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowNew
-1);
10062 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
) ||
10063 !mrDoc
.ValidCol(nCol2
) || !mrDoc
.ValidRow(nRow2
))
10064 PushIllegalArgument();
10066 PushDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab1
);
10070 case svExternalSingleRef
:
10072 sal_uInt16 nFileId
;
10074 ScSingleRefData aRef
;
10075 PopExternalSingleRef(nFileId
, aTabName
, aRef
);
10076 ScAddress aAbsRef
= aRef
.toAbs(mrDoc
, aPos
);
10077 nCol1
= aAbsRef
.Col();
10078 nRow1
= aAbsRef
.Row();
10079 nTab1
= aAbsRef
.Tab();
10081 if (!bNewWidth
&& !bNewHeight
)
10083 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
) + nColPlus
);
10084 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
) + nRowPlus
);
10085 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
))
10086 PushIllegalArgument();
10088 PushExternalSingleRef(nFileId
, aTabName
, nCol1
, nRow1
, nTab1
);
10092 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColPlus
);
10093 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowPlus
);
10094 nCol2
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColNew
-1);
10095 nRow2
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowNew
-1);
10097 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
) ||
10098 !mrDoc
.ValidCol(nCol2
) || !mrDoc
.ValidRow(nRow2
))
10099 PushIllegalArgument();
10101 PushExternalDoubleRef(nFileId
, aTabName
, nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
10107 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
10109 nColNew
= nCol2
- nCol1
+ 1;
10111 nRowNew
= nRow2
- nRow1
+ 1;
10112 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColPlus
);
10113 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowPlus
);
10114 nCol2
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColNew
-1);
10115 nRow2
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowNew
-1);
10116 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
) ||
10117 !mrDoc
.ValidCol(nCol2
) || !mrDoc
.ValidRow(nRow2
) || nTab1
!= nTab2
)
10118 PushIllegalArgument();
10120 PushDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab1
);
10123 case svExternalDoubleRef
:
10125 sal_uInt16 nFileId
;
10127 ScComplexRefData aRef
;
10128 PopExternalDoubleRef(nFileId
, aTabName
, aRef
);
10129 ScRange aAbs
= aRef
.toAbs(mrDoc
, aPos
);
10130 nCol1
= aAbs
.aStart
.Col();
10131 nRow1
= aAbs
.aStart
.Row();
10132 nTab1
= aAbs
.aStart
.Tab();
10133 nCol2
= aAbs
.aEnd
.Col();
10134 nRow2
= aAbs
.aEnd
.Row();
10135 nTab2
= aAbs
.aEnd
.Tab();
10137 nColNew
= nCol2
- nCol1
+ 1;
10139 nRowNew
= nRow2
- nRow1
+ 1;
10140 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColPlus
);
10141 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowPlus
);
10142 nCol2
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColNew
-1);
10143 nRow2
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowNew
-1);
10144 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
) ||
10145 !mrDoc
.ValidCol(nCol2
) || !mrDoc
.ValidRow(nRow2
) || nTab1
!= nTab2
)
10146 PushIllegalArgument();
10148 PushExternalDoubleRef(nFileId
, aTabName
, nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
10152 PushIllegalParameter();
10157 void ScInterpreter::ScIndex()
10159 sal_uInt8 nParamCount
= GetByte();
10160 if ( !MustHaveParamCount( nParamCount
, 1, 4 ) )
10167 if (nParamCount
== 4)
10168 nArea
= GetUInt32();
10172 if (nParamCount
>= 3)
10174 bColMissing
= IsMissing();
10175 nCol
= static_cast<SCCOL
>(GetInt16());
10179 bColMissing
= false;
10182 if (nParamCount
>= 2)
10183 nRow
= static_cast<SCROW
>(GetInt32());
10186 if (GetStackType() == svRefList
)
10187 nAreaCount
= (sp
? pStack
[sp
-1]->GetRefList()->size() : 0);
10189 nAreaCount
= 1; // one reference or array or whatever
10190 if (nGlobalError
!= FormulaError::NONE
|| nAreaCount
== 0 || static_cast<size_t>(nArea
) > nAreaCount
)
10192 PushError( FormulaError::NoRef
);
10195 else if (nArea
< 1 || nCol
< 0 || nRow
< 0)
10197 PushIllegalArgument();
10200 switch (GetStackType())
10203 case svExternalSingleRef
:
10204 case svExternalDoubleRef
:
10207 SetError(FormulaError::IllegalArgument
);
10208 sal_uInt16 nOldSp
= sp
;
10209 ScMatrixRef pMat
= GetMatrix();
10213 pMat
->GetDimensions(nC
, nR
);
10215 // Access one element of a vector independent of col/row
10216 // orientation. Excel documentation does not mention, but
10217 // i62850 had a .xls example of a row vector accessed by
10218 // row number returning one element. This
10219 // INDEX(row_vector;element) behaves the same as
10220 // INDEX(row_vector;0;element) and thus contradicts Excel
10221 // documentation where the second parameter is always
10224 // ODFF v1.3 in 6.14.6 INDEX states "If DataSource is a
10225 // one-dimensional row vector, Row is optional, which
10226 // effectively makes Row act as the column offset into the
10227 // vector". Guess the first Row is a typo and should read
10230 const bool bRowVectorSpecial
= (nParamCount
== 2 || bColMissing
);
10231 const bool bRowVectorElement
= (nR
== 1 && (nCol
!= 0 || (bRowVectorSpecial
&& nRow
!= 0)));
10232 const bool bVectorElement
= (bRowVectorElement
|| (nC
== 1 && nRow
!= 0));
10234 if (nC
== 0 || nR
== 0 ||
10235 (!bVectorElement
&& (o3tl::make_unsigned(nCol
) > nC
||
10236 o3tl::make_unsigned(nRow
) > nR
)))
10237 PushError( FormulaError::NoRef
);
10238 else if (nCol
== 0 && nRow
== 0)
10240 else if (bVectorElement
)
10242 // Vectors here don't replicate to the other dimension.
10243 SCSIZE nElement
, nOtherDimension
;
10244 if (bRowVectorElement
&& !bRowVectorSpecial
)
10246 nElement
= o3tl::make_unsigned(nCol
);
10247 nOtherDimension
= o3tl::make_unsigned(nRow
);
10251 nElement
= o3tl::make_unsigned(nRow
);
10252 nOtherDimension
= o3tl::make_unsigned(nCol
);
10255 if (nElement
== 0 || nElement
> nC
* nR
|| nOtherDimension
> 1)
10256 PushError( FormulaError::NoRef
);
10260 if (pMat
->IsStringOrEmpty( nElement
))
10261 PushString( pMat
->GetString(nElement
).getString());
10263 PushDouble( pMat
->GetDouble( nElement
));
10266 else if (nCol
== 0)
10268 ScMatrixRef pResMat
= GetNewMat(nC
, 1, /*bEmpty*/true);
10271 SCSIZE nRowMinus1
= static_cast<SCSIZE
>(nRow
- 1);
10272 for (SCSIZE i
= 0; i
< nC
; i
++)
10273 if (!pMat
->IsStringOrEmpty(i
, nRowMinus1
))
10274 pResMat
->PutDouble(pMat
->GetDouble(i
,
10275 nRowMinus1
), i
, 0);
10277 pResMat
->PutString(pMat
->GetString(i
, nRowMinus1
), i
, 0);
10279 PushMatrix(pResMat
);
10282 PushError( FormulaError::NoRef
);
10284 else if (nRow
== 0)
10286 ScMatrixRef pResMat
= GetNewMat(1, nR
, /*bEmpty*/true);
10289 SCSIZE nColMinus1
= static_cast<SCSIZE
>(nCol
- 1);
10290 for (SCSIZE i
= 0; i
< nR
; i
++)
10291 if (!pMat
->IsStringOrEmpty(nColMinus1
, i
))
10292 pResMat
->PutDouble(pMat
->GetDouble(nColMinus1
,
10295 pResMat
->PutString(pMat
->GetString(nColMinus1
, i
), i
);
10296 PushMatrix(pResMat
);
10299 PushError( FormulaError::NoRef
);
10303 if (!pMat
->IsStringOrEmpty( static_cast<SCSIZE
>(nCol
-1),
10304 static_cast<SCSIZE
>(nRow
-1)))
10305 PushDouble( pMat
->GetDouble(
10306 static_cast<SCSIZE
>(nCol
-1),
10307 static_cast<SCSIZE
>(nRow
-1)));
10309 PushString( pMat
->GetString(
10310 static_cast<SCSIZE
>(nCol
-1),
10311 static_cast<SCSIZE
>(nRow
-1)).getString());
10321 PopSingleRef( nCol1
, nRow1
, nTab1
);
10322 if (nCol
> 1 || nRow
> 1)
10323 PushError( FormulaError::NoRef
);
10325 PushSingleRef( nCol1
, nRow1
, nTab1
);
10337 bool bRowArray
= false;
10338 if (GetStackType() == svRefList
)
10340 FormulaConstTokenRef xRef
= PopToken();
10341 if (nGlobalError
!= FormulaError::NONE
|| !xRef
)
10343 PushError( FormulaError::NoRef
);
10346 ScRange
aRange( ScAddress::UNINITIALIZED
);
10347 DoubleRefToRange( (*(xRef
->GetRefList()))[nArea
-1], aRange
);
10348 aRange
.GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
10349 if ( nParamCount
== 2 && nRow1
== nRow2
)
10354 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
10355 if ( nParamCount
== 2 && nRow1
== nRow2
)
10358 if ( nTab1
!= nTab2
||
10359 (nCol
> 0 && nCol1
+nCol
-1 > nCol2
) ||
10360 (nRow
> 0 && nRow1
+nRow
-1 > nRow2
&& !bRowArray
) ||
10361 ( nRow
> nCol2
- nCol1
+ 1 && bRowArray
))
10362 PushError( FormulaError::NoRef
);
10363 else if (nCol
== 0 && nRow
== 0)
10365 if ( nCol1
== nCol2
&& nRow1
== nRow2
)
10366 PushSingleRef( nCol1
, nRow1
, nTab1
);
10368 PushDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab1
);
10370 else if (nRow
== 0)
10372 if ( nRow1
== nRow2
)
10373 PushSingleRef( nCol1
+nCol
-1, nRow1
, nTab1
);
10375 PushDoubleRef( nCol1
+nCol
-1, nRow1
, nTab1
,
10376 nCol1
+nCol
-1, nRow2
, nTab1
);
10378 else if (nCol
== 0)
10380 if ( nCol1
== nCol2
)
10381 PushSingleRef( nCol1
, nRow1
+nRow
-1, nTab1
);
10382 else if ( bRowArray
)
10384 nCol
=static_cast<SCCOL
>(nRow
);
10386 PushSingleRef( nCol1
+nCol
-1, nRow1
+nRow
-1, nTab1
);
10389 PushDoubleRef( nCol1
, nRow1
+nRow
-1, nTab1
,
10390 nCol2
, nRow1
+nRow
-1, nTab1
);
10393 PushSingleRef( nCol1
+nCol
-1, nRow1
+nRow
-1, nTab1
);
10398 PushError( FormulaError::NoRef
);
10402 void ScInterpreter::ScMultiArea()
10404 // Legacy support, convert to RefList
10405 sal_uInt8 nParamCount
= GetByte();
10406 if (MustHaveParamCountMin( nParamCount
, 1))
10408 while (nGlobalError
== FormulaError::NONE
&& nParamCount
-- > 1)
10415 void ScInterpreter::ScAreas()
10417 sal_uInt8 nParamCount
= GetByte();
10418 if (!MustHaveParamCount( nParamCount
, 1))
10422 switch (GetStackType())
10426 FormulaConstTokenRef xT
= PopToken();
10427 ValidateRef( *xT
->GetSingleRef());
10433 FormulaConstTokenRef xT
= PopToken();
10434 ValidateRef( *xT
->GetDoubleRef());
10440 FormulaConstTokenRef xT
= PopToken();
10441 ValidateRef( *(xT
->GetRefList()));
10442 nCount
+= xT
->GetRefList()->size();
10446 SetError( FormulaError::IllegalParameter
);
10448 PushDouble( double(nCount
));
10451 void ScInterpreter::ScCurrency()
10453 sal_uInt8 nParamCount
= GetByte();
10454 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
10459 if (nParamCount
== 2)
10461 fDec
= ::rtl::math::approxFloor(GetDouble());
10462 if (fDec
< -15.0 || fDec
> 15.0)
10464 PushIllegalArgument();
10470 double fVal
= GetDouble();
10473 fFac
= pow( double(10), fDec
);
10477 fVal
= ceil(fVal
*fFac
-0.5)/fFac
;
10479 fVal
= floor(fVal
*fFac
+0.5)/fFac
;
10480 const Color
* pColor
= nullptr;
10483 sal_uLong nIndex
= mrContext
.NFGetStandardFormat(
10484 SvNumFormatType::CURRENCY
,
10486 if ( static_cast<sal_uInt16
>(fDec
) != mrContext
.NFGetFormatPrecision( nIndex
) )
10488 OUString sFormatString
= mrContext
.NFGenerateFormat(
10491 true, // with thousands separator
10493 static_cast<sal_uInt16
>(fDec
));// decimal places
10494 if (!mrContext
.NFGetPreviewString(sFormatString
,
10499 SetError(FormulaError::IllegalArgument
);
10503 mrContext
.NFGetOutputString(fVal
, nIndex
, aStr
, &pColor
);
10508 void ScInterpreter::ScReplace()
10510 if ( !MustHaveParamCount( GetByte(), 4 ) )
10513 OUString aNewStr
= GetString().getString();
10514 sal_Int32 nCount
= GetStringPositionArgument();
10515 sal_Int32 nPos
= GetStringPositionArgument();
10516 OUString aOldStr
= GetString().getString();
10517 if (nPos
< 1 || nCount
< 0)
10518 PushIllegalArgument();
10521 sal_Int32 nLen
= aOldStr
.getLength();
10522 if (nPos
> nLen
+ 1)
10524 if (nCount
> nLen
- nPos
+ 1)
10525 nCount
= nLen
- nPos
+ 1;
10526 sal_Int32 nIdx
= 0;
10527 sal_Int32 nCnt
= 0;
10528 while ( nIdx
< nLen
&& nPos
> nCnt
+ 1 )
10530 aOldStr
.iterateCodePoints( &nIdx
);
10533 sal_Int32 nStart
= nIdx
;
10534 while ( nIdx
< nLen
&& nPos
+ nCount
- 1 > nCnt
)
10536 aOldStr
.iterateCodePoints( &nIdx
);
10539 if ( CheckStringResultLen( aOldStr
, aNewStr
.getLength() - (nIdx
- nStart
) ) )
10540 aOldStr
= aOldStr
.replaceAt( nStart
, nIdx
- nStart
, aNewStr
);
10541 PushString( aOldStr
);
10545 void ScInterpreter::ScFixed()
10547 sal_uInt8 nParamCount
= GetByte();
10548 if ( !MustHaveParamCount( nParamCount
, 1, 3 ) )
10554 if (nParamCount
== 3)
10555 bThousand
= !GetBool(); // Param true: no thousands separator
10558 if (nParamCount
>= 2)
10560 fDec
= ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 ));
10561 if (fDec
< -15.0 || fDec
> 15.0)
10563 PushIllegalArgument();
10569 double fVal
= GetDouble();
10572 fFac
= pow( double(10), fDec
);
10576 fVal
= ceil(fVal
*fFac
-0.5)/fFac
;
10578 fVal
= floor(fVal
*fFac
+0.5)/fFac
;
10579 const Color
* pColor
= nullptr;
10582 sal_uLong nIndex
= mrContext
.NFGetStandardFormat(
10583 SvNumFormatType::NUMBER
,
10585 OUString sFormatString
= mrContext
.NFGenerateFormat(
10588 bThousand
, // with thousands separator
10590 static_cast<sal_uInt16
>(fDec
));// decimal places
10591 if (!mrContext
.NFGetPreviewString(sFormatString
,
10596 PushIllegalArgument();
10601 void ScInterpreter::ScFind()
10603 sal_uInt8 nParamCount
= GetByte();
10604 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
10608 if (nParamCount
== 3)
10609 nCnt
= GetDouble();
10612 OUString sStr
= GetString().getString();
10613 if (nCnt
< 1 || nCnt
> sStr
.getLength())
10617 sal_Int32 nPos
= sStr
.indexOf(GetString().getString(), nCnt
- 1);
10622 sal_Int32 nIdx
= 0;
10624 while ( nIdx
< nPos
)
10626 sStr
.iterateCodePoints( &nIdx
);
10629 PushDouble( static_cast<double>(nCnt
+ 1) );
10634 void ScInterpreter::ScExact()
10636 nFuncFmtType
= SvNumFormatType::LOGICAL
;
10637 if ( MustHaveParamCount( GetByte(), 2 ) )
10639 svl::SharedString s1
= GetString();
10640 svl::SharedString s2
= GetString();
10641 PushInt(int(s1
== s2
));
10645 void ScInterpreter::ScLeft()
10647 sal_uInt8 nParamCount
= GetByte();
10648 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
10652 if (nParamCount
== 2)
10654 n
= GetStringPositionArgument();
10657 PushIllegalArgument();
10663 OUString aStr
= GetString().getString();
10664 sal_Int32 nIdx
= 0;
10665 sal_Int32 nCnt
= 0;
10666 while ( nIdx
< aStr
.getLength() && n
> nCnt
++ )
10667 aStr
.iterateCodePoints( &nIdx
);
10668 aStr
= aStr
.copy( 0, nIdx
);
10669 PushString( aStr
);
10674 struct UBlockScript
{
10681 const UBlockScript scriptList
[] = {
10682 {UBLOCK_HANGUL_JAMO
, UBLOCK_HANGUL_JAMO
},
10683 {UBLOCK_CJK_RADICALS_SUPPLEMENT
, UBLOCK_HANGUL_SYLLABLES
},
10684 {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS
,UBLOCK_CJK_RADICALS_SUPPLEMENT
},
10685 {UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS
,UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS
},
10686 {UBLOCK_CJK_COMPATIBILITY_FORMS
, UBLOCK_CJK_COMPATIBILITY_FORMS
},
10687 {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS
, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS
},
10688 {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT
},
10689 {UBLOCK_CJK_STROKES
, UBLOCK_CJK_STROKES
}
10691 static bool IsDBCS(sal_Unicode currentChar
)
10693 // for the locale of ja-JP, character U+0x005c and U+0x20ac should be ScriptType::Asian
10694 if( (currentChar
== 0x005c || currentChar
== 0x20ac) &&
10695 (MsLangId::getConfiguredSystemLanguage() == LANGUAGE_JAPANESE
) )
10699 UBlockCode block
= ublock_getCode(currentChar
);
10700 for ( i
= 0; i
< SAL_N_ELEMENTS(scriptList
); i
++) {
10701 if (block
<= scriptList
[i
].to
) break;
10703 bRet
= (i
< SAL_N_ELEMENTS(scriptList
) && block
>= scriptList
[i
].from
);
10706 static sal_Int32
lcl_getLengthB( std::u16string_view str
, sal_Int32 nPos
)
10708 sal_Int32 index
= 0;
10709 sal_Int32 length
= 0;
10710 while ( index
< nPos
)
10712 if (IsDBCS(str
[index
]))
10720 static sal_Int32
getLengthB(std::u16string_view str
)
10725 return lcl_getLengthB( str
, str
.size() );
10727 void ScInterpreter::ScLenB()
10729 PushDouble( getLengthB(GetString().getString()) );
10731 static OUString
lcl_RightB(const OUString
&rStr
, sal_Int32 n
)
10733 if( n
< getLengthB(rStr
) )
10735 OUStringBuffer
aBuf(rStr
);
10736 sal_Int32 index
= aBuf
.getLength();
10737 while(index
-- >= 0)
10741 aBuf
.remove( 0, index
+ 1);
10746 aBuf
.remove( 0, index
+ 2 );
10747 aBuf
.insert( 0, " ");
10750 if(IsDBCS(aBuf
[index
]))
10755 return aBuf
.makeStringAndClear();
10759 void ScInterpreter::ScRightB()
10761 sal_uInt8 nParamCount
= GetByte();
10762 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
10766 if (nParamCount
== 2)
10768 n
= GetStringPositionArgument();
10771 PushIllegalArgument();
10777 OUString
aStr(lcl_RightB(GetString().getString(), n
));
10778 PushString( aStr
);
10780 static OUString
lcl_LeftB(const OUString
&rStr
, sal_Int32 n
)
10782 if( n
< getLengthB(rStr
) )
10784 OUStringBuffer
aBuf(rStr
);
10785 sal_Int32 index
= -1;
10786 while(index
++ < aBuf
.getLength())
10790 aBuf
.truncate(index
);
10795 aBuf
.truncate( index
- 1 );
10799 if(IsDBCS(aBuf
[index
]))
10804 return aBuf
.makeStringAndClear();
10808 void ScInterpreter::ScLeftB()
10810 sal_uInt8 nParamCount
= GetByte();
10811 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
10815 if (nParamCount
== 2)
10817 n
= GetStringPositionArgument();
10820 PushIllegalArgument();
10826 OUString
aStr(lcl_LeftB(GetString().getString(), n
));
10827 PushString( aStr
);
10829 void ScInterpreter::ScMidB()
10831 if ( !MustHaveParamCount( GetByte(), 3 ) )
10834 const sal_Int32 nCount
= GetStringPositionArgument();
10835 const sal_Int32 nStart
= GetStringPositionArgument();
10836 OUString aStr
= GetString().getString();
10837 if (nStart
< 1 || nCount
< 0)
10838 PushIllegalArgument();
10842 aStr
= lcl_LeftB(aStr
, nStart
+ nCount
- 1);
10843 sal_Int32 nCnt
= getLengthB(aStr
) - nStart
+ 1;
10844 aStr
= lcl_RightB(aStr
, std::max
<sal_Int32
>(nCnt
,0));
10849 void ScInterpreter::ScReplaceB()
10851 if ( !MustHaveParamCount( GetByte(), 4 ) )
10854 OUString aNewStr
= GetString().getString();
10855 const sal_Int32 nCount
= GetStringPositionArgument();
10856 const sal_Int32 nPos
= GetStringPositionArgument();
10857 OUString aOldStr
= GetString().getString();
10858 int nLen
= getLengthB( aOldStr
);
10859 if (nPos
< 1.0 || nPos
> nLen
|| nCount
< 0.0 || nPos
+ nCount
-1 > nLen
)
10860 PushIllegalArgument();
10863 // REPLACEB(aOldStr;nPos;nCount;aNewStr) is the same as
10864 // LEFTB(aOldStr;nPos-1) & aNewStr & RIGHT(aOldStr;LENB(aOldStr)-(nPos - 1)-nCount)
10865 OUString aStr1
= lcl_LeftB( aOldStr
, nPos
- 1 );
10866 OUString aStr3
= lcl_RightB( aOldStr
, nLen
- nPos
- nCount
+ 1);
10868 PushString( aStr1
+ aNewStr
+ aStr3
);
10872 void ScInterpreter::ScFindB()
10874 sal_uInt8 nParamCount
= GetByte();
10875 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
10879 if ( nParamCount
== 3 )
10880 nStart
= GetStringPositionArgument();
10883 OUString aStr
= GetString().getString();
10884 int nLen
= getLengthB( aStr
);
10885 OUString asStr
= GetString().getString();
10886 int nsLen
= getLengthB( asStr
);
10887 if ( nStart
< 1 || nStart
> nLen
- nsLen
+ 1 )
10888 PushIllegalArgument();
10891 // create a string from sStr starting at nStart
10892 OUString aBuf
= lcl_RightB( aStr
, nLen
- nStart
+ 1 );
10893 // search aBuf for asStr
10894 sal_Int32 nPos
= aBuf
.indexOf( asStr
, 0 );
10899 // obtain byte value of nPos
10900 int nBytePos
= lcl_getLengthB( aBuf
, nPos
);
10901 PushDouble( nBytePos
+ nStart
);
10906 void ScInterpreter::ScSearchB()
10908 sal_uInt8 nParamCount
= GetByte();
10909 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
10913 if ( nParamCount
== 3 )
10915 nStart
= GetStringPositionArgument();
10918 PushIllegalArgument();
10924 OUString aStr
= GetString().getString();
10925 sal_Int32 nLen
= getLengthB( aStr
);
10926 OUString asStr
= GetString().getString();
10927 sal_Int32 nsLen
= nStart
- 1;
10928 if( nsLen
>= nLen
)
10932 // create a string from sStr starting at nStart
10933 OUString
aSubStr( lcl_RightB( aStr
, nLen
- nStart
+ 1 ) );
10934 // search aSubStr for asStr
10935 sal_Int32 nPos
= 0;
10936 sal_Int32 nEndPos
= aSubStr
.getLength();
10937 utl::SearchParam::SearchType eSearchType
= DetectSearchType( asStr
, mrDoc
);
10938 utl::SearchParam
sPar( asStr
, eSearchType
, false, '~', false );
10939 utl::TextSearch
sT( sPar
, ScGlobal::getCharClass() );
10940 if ( !sT
.SearchForward( aSubStr
, &nPos
, &nEndPos
) )
10944 // obtain byte value of nPos
10945 int nBytePos
= lcl_getLengthB( aSubStr
, nPos
);
10946 PushDouble( nBytePos
+ nStart
);
10951 void ScInterpreter::ScRight()
10953 sal_uInt8 nParamCount
= GetByte();
10954 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
10958 if (nParamCount
== 2)
10960 n
= GetStringPositionArgument();
10963 PushIllegalArgument();
10969 OUString aStr
= GetString().getString();
10970 sal_Int32 nLen
= aStr
.getLength();
10972 PushString( aStr
);
10975 sal_Int32 nIdx
= nLen
;
10976 sal_Int32 nCnt
= 0;
10977 while ( nIdx
> 0 && n
> nCnt
)
10979 aStr
.iterateCodePoints( &nIdx
, -1 );
10982 aStr
= aStr
.copy( nIdx
, nLen
- nIdx
);
10983 PushString( aStr
);
10987 void ScInterpreter::ScSearch()
10989 sal_uInt8 nParamCount
= GetByte();
10990 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
10994 if (nParamCount
== 3)
10996 nStart
= GetStringPositionArgument();
10999 PushIllegalArgument();
11005 OUString sStr
= GetString().getString();
11006 OUString SearchStr
= GetString().getString();
11007 sal_Int32 nPos
= nStart
- 1;
11008 sal_Int32 nEndPos
= sStr
.getLength();
11009 if( nPos
>= nEndPos
)
11013 utl::SearchParam::SearchType eSearchType
= DetectSearchType( SearchStr
, mrDoc
);
11014 utl::SearchParam
sPar(SearchStr
, eSearchType
, false, '~', false);
11015 utl::TextSearch
sT( sPar
, ScGlobal::getCharClass() );
11016 bool bBool
= sT
.SearchForward(sStr
, &nPos
, &nEndPos
);
11021 sal_Int32 nIdx
= 0;
11022 sal_Int32 nCnt
= 0;
11023 while ( nIdx
< nPos
)
11025 sStr
.iterateCodePoints( &nIdx
);
11028 PushDouble( static_cast<double>(nCnt
+ 1) );
11033 void ScInterpreter::ScRegex()
11035 const sal_uInt8 nParamCount
= GetByte();
11036 if (!MustHaveParamCount( nParamCount
, 2, 4))
11039 // Flags are supported only for replacement, search match flags can be
11040 // individually and much more flexible set in the regular expression
11041 // pattern using (?ismwx-ismwx)
11042 bool bGlobalReplacement
= false;
11043 sal_Int32 nOccurrence
= 1; // default first occurrence, if any
11044 if (nParamCount
== 4)
11046 // Argument can be either string or double.
11047 double fOccurrence
;
11048 svl::SharedString aFlagsString
;
11051 bDouble
= GetDoubleOrString( fOccurrence
, aFlagsString
);
11054 // For an omitted argument keep the default.
11057 fOccurrence
= nOccurrence
;
11059 if (nGlobalError
!= FormulaError::NONE
)
11061 PushError( nGlobalError
);
11066 if (!CheckStringPositionArgument( fOccurrence
))
11068 PushError( FormulaError::IllegalArgument
);
11071 nOccurrence
= static_cast<sal_Int32
>(fOccurrence
);
11075 const OUString
& aFlags( aFlagsString
.getString());
11076 // Empty flags string is valid => no flag set.
11077 if (aFlags
.getLength() > 1)
11079 // Only one flag supported.
11080 PushIllegalArgument();
11083 if (aFlags
.getLength() == 1)
11085 if (aFlags
.indexOf('g') >= 0)
11086 bGlobalReplacement
= true;
11089 // Unsupported flag.
11090 PushIllegalArgument();
11097 bool bReplacement
= false;
11098 OUString aReplacement
;
11099 if (nParamCount
>= 3)
11101 // A missing argument is not an empty string to replace the match.
11102 // nOccurrence==0 forces no replacement, so simply discard the
11104 if (IsMissing() || nOccurrence
== 0)
11108 aReplacement
= GetString().getString();
11109 bReplacement
= true;
11112 // If bGlobalReplacement==true and bReplacement==false then
11113 // bGlobalReplacement is silently ignored.
11115 const OUString aExpression
= GetString().getString();
11116 const OUString aText
= GetString().getString();
11118 if (nGlobalError
!= FormulaError::NONE
)
11120 PushError( nGlobalError
);
11124 // 0-th match or replacement is none, return original string early.
11125 if (nOccurrence
== 0)
11127 PushString( aText
);
11131 const icu::UnicodeString
aIcuExpression(
11132 false, reinterpret_cast<const UChar
*>(aExpression
.getStr()), aExpression
.getLength());
11133 UErrorCode status
= U_ZERO_ERROR
;
11134 icu::RegexMatcher
aRegexMatcher( aIcuExpression
, 0, status
);
11135 if (U_FAILURE(status
))
11138 PushIllegalArgument();
11141 // Guard against pathological patterns, limit steps of engine, see
11142 // https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1RegexMatcher.html#a6ebcfcab4fe6a38678c0291643a03a00
11143 aRegexMatcher
.setTimeLimit( 23*1000, status
);
11145 const icu::UnicodeString
aIcuText(false, reinterpret_cast<const UChar
*>(aText
.getStr()), aText
.getLength());
11146 aRegexMatcher
.reset( aIcuText
);
11150 // Find n-th occurrence.
11151 sal_Int32 nCount
= 0;
11152 while (aRegexMatcher
.find(status
) && U_SUCCESS(status
) && ++nCount
< nOccurrence
)
11154 if (U_FAILURE(status
))
11157 PushIllegalArgument();
11160 // n-th match found?
11161 if (nCount
!= nOccurrence
)
11163 PushError( FormulaError::NotAvailable
);
11166 // Extract matched text.
11167 icu::UnicodeString
aMatch( aRegexMatcher
.group( status
));
11168 if (U_FAILURE(status
))
11171 PushIllegalArgument();
11174 OUString
aResult( reinterpret_cast<const sal_Unicode
*>(aMatch
.getBuffer()), aMatch
.length());
11175 PushString( aResult
);
11179 const icu::UnicodeString
aIcuReplacement(
11180 false, reinterpret_cast<const UChar
*>(aReplacement
.getStr()), aReplacement
.getLength());
11181 icu::UnicodeString aReplaced
;
11182 if (bGlobalReplacement
)
11183 // Replace all occurrences of match with replacement.
11184 aReplaced
= aRegexMatcher
.replaceAll( aIcuReplacement
, status
);
11185 else if (nOccurrence
== 1)
11186 // Replace first occurrence of match with replacement.
11187 aReplaced
= aRegexMatcher
.replaceFirst( aIcuReplacement
, status
);
11190 // Replace n-th occurrence of match with replacement.
11191 sal_Int32 nCount
= 0;
11192 while (aRegexMatcher
.find(status
) && U_SUCCESS(status
))
11194 // XXX NOTE: After several RegexMatcher::find() the
11195 // RegexMatcher::appendReplacement() still starts at the
11196 // beginning (or after the last appendReplacement() position
11197 // which is none here) and copies the original text up to the
11198 // current found match and then replaces the found match.
11199 if (++nCount
== nOccurrence
)
11201 aRegexMatcher
.appendReplacement( aReplaced
, aIcuReplacement
, status
);
11205 aRegexMatcher
.appendTail( aReplaced
);
11207 if (U_FAILURE(status
))
11209 // Some error, e.g. extraneous $1 without group.
11210 PushIllegalArgument();
11213 OUString
aResult( reinterpret_cast<const sal_Unicode
*>(aReplaced
.getBuffer()), aReplaced
.length());
11214 PushString( aResult
);
11217 void ScInterpreter::ScMid()
11219 if ( !MustHaveParamCount( GetByte(), 3 ) )
11222 const sal_Int32 nSubLen
= GetStringPositionArgument();
11223 const sal_Int32 nStart
= GetStringPositionArgument();
11224 OUString aStr
= GetString().getString();
11225 if ( nStart
< 1 || nSubLen
< 0 )
11226 PushIllegalArgument();
11227 else if (nStart
> kScInterpreterMaxStrLen
|| nSubLen
> kScInterpreterMaxStrLen
)
11228 PushError(FormulaError::StringOverflow
);
11231 sal_Int32 nLen
= aStr
.getLength();
11232 sal_Int32 nIdx
= 0;
11233 sal_Int32 nCnt
= 0;
11234 while ( nIdx
< nLen
&& nStart
- 1 > nCnt
)
11236 aStr
.iterateCodePoints( &nIdx
);
11239 sal_Int32 nIdx0
= nIdx
; //start position
11241 while ( nIdx
< nLen
&& nStart
+ nSubLen
- 1 > nCnt
)
11243 aStr
.iterateCodePoints( &nIdx
);
11246 aStr
= aStr
.copy( nIdx0
, nIdx
- nIdx0
);
11247 PushString( aStr
);
11251 void ScInterpreter::ScText()
11253 if ( !MustHaveParamCount( GetByte(), 2 ) )
11256 OUString sFormatString
= GetString().getString();
11257 svl::SharedString aStr
;
11258 bool bString
= false;
11260 switch (GetStackType())
11266 fVal
= PopDouble();
11270 FormulaConstTokenRef
xTok( PopToken());
11271 if (nGlobalError
== FormulaError::NONE
)
11273 PushTokenRef( xTok
);
11274 // Temporarily override the ConvertStringToValue()
11275 // error for GetCellValue() / GetCellValueOrZero()
11276 FormulaError nSErr
= mnStringNoValueError
;
11277 mnStringNoValueError
= FormulaError::NotNumericString
;
11278 fVal
= GetDouble();
11279 mnStringNoValueError
= nSErr
;
11280 if (nGlobalError
== FormulaError::NotNumericString
)
11283 nGlobalError
= FormulaError::NONE
;
11284 PushTokenRef( xTok
);
11285 aStr
= GetString();
11291 if (nGlobalError
!= FormulaError::NONE
)
11292 PushError( nGlobalError
);
11293 else if (sFormatString
.isEmpty())
11295 // Mimic the Excel behaviour that
11296 // * anything numeric returns an empty string
11297 // * text convertible to numeric returns an empty string
11298 // * any other text returns that text
11299 // Conversion was detected above.
11303 PushString( OUString());
11308 const Color
* pColor
= nullptr;
11309 LanguageType eCellLang
;
11310 const ScPatternAttr
* pPattern
= mrDoc
.GetPattern(
11311 aPos
.Col(), aPos
.Row(), aPos
.Tab() );
11313 eCellLang
= pPattern
->GetItem( ATTR_LANGUAGE_FORMAT
).GetValue();
11315 eCellLang
= ScGlobal::eLnge
;
11318 if (!mrContext
.NFGetPreviewString( sFormatString
, aStr
.getString(),
11319 aResult
, &pColor
, eCellLang
))
11320 PushIllegalArgument();
11322 PushString( aResult
);
11326 if (!mrContext
.NFGetPreviewStringGuess( sFormatString
, fVal
,
11327 aResult
, &pColor
, eCellLang
))
11328 PushIllegalArgument();
11330 PushString( aResult
);
11335 void ScInterpreter::ScSubstitute()
11337 sal_uInt8 nParamCount
= GetByte();
11338 if ( !MustHaveParamCount( nParamCount
, 3, 4 ) )
11342 if (nParamCount
== 4)
11344 nCnt
= GetStringPositionArgument();
11347 PushIllegalArgument();
11353 OUString sNewStr
= GetString().getString();
11354 OUString sOldStr
= GetString().getString();
11355 OUString sStr
= GetString().getString();
11356 sal_Int32 nPos
= 0;
11357 sal_Int32 nCount
= 0;
11358 std::optional
<OUStringBuffer
> oResult
;
11359 for (sal_Int32 nEnd
= sStr
.indexOf(sOldStr
); nEnd
>= 0; nEnd
= sStr
.indexOf(sOldStr
, nEnd
))
11361 if (nCnt
== 0 || ++nCount
== nCnt
) // Found a replacement cite
11363 if (!oResult
) // Only allocate buffer when needed
11364 oResult
.emplace(sStr
.getLength() + sNewStr
.getLength() - sOldStr
.getLength());
11366 oResult
->append(sStr
.subView(nPos
, nEnd
- nPos
)); // Copy leading unchanged text
11367 if (!CheckStringResultLen(*oResult
, sNewStr
.getLength()))
11368 return PushError(GetError());
11369 oResult
->append(sNewStr
); // Copy the replacement
11370 nPos
= nEnd
+ sOldStr
.getLength();
11371 if (nCnt
> 0) // Found the single replacement site - end the loop
11374 nEnd
+= sOldStr
.getLength();
11376 if (oResult
) // If there were prior replacements, copy the rest, otherwise use original
11377 oResult
->append(sStr
.subView(nPos
, sStr
.getLength() - nPos
));
11378 PushString(oResult
? oResult
->makeStringAndClear() : sStr
);
11381 void ScInterpreter::ScRept()
11383 if ( !MustHaveParamCount( GetByte(), 2 ) )
11386 sal_Int32 nCnt
= GetStringPositionArgument();
11387 OUString aStr
= GetString().getString();
11389 PushIllegalArgument();
11390 else if (static_cast<double>(nCnt
) * aStr
.getLength() > kScInterpreterMaxStrLen
)
11392 PushError( FormulaError::StringOverflow
);
11394 else if (nCnt
== 0)
11395 PushString( OUString() );
11398 const sal_Int32 nLen
= aStr
.getLength();
11399 OUStringBuffer
aRes(nCnt
*nLen
);
11402 PushString( aRes
.makeStringAndClear() );
11406 void ScInterpreter::ScConcat()
11408 sal_uInt8 nParamCount
= GetByte();
11410 //reverse order of parameter stack to simplify processing
11411 ReverseStack(nParamCount
);
11413 OUStringBuffer aRes
;
11414 while( nParamCount
-- > 0)
11416 OUString aStr
= GetString().getString();
11417 if (CheckStringResultLen(aRes
, aStr
.getLength()))
11422 PushString( aRes
.makeStringAndClear() );
11425 FormulaError
ScInterpreter::GetErrorType()
11428 FormulaError nOldError
= nGlobalError
;
11429 nGlobalError
= FormulaError::NONE
;
11430 switch ( GetStackType() )
11434 FormulaConstTokenRef x
= PopToken();
11435 if (nGlobalError
!= FormulaError::NONE
)
11436 nErr
= nGlobalError
;
11439 const ScRefList
* pRefList
= x
->GetRefList();
11440 size_t n
= pRefList
->size();
11442 nErr
= FormulaError::NoRef
;
11444 nErr
= FormulaError::NoValue
;
11448 DoubleRefToRange( (*pRefList
)[0], aRange
);
11449 if (nGlobalError
!= FormulaError::NONE
)
11450 nErr
= nGlobalError
;
11454 if ( DoubleRefToPosSingleRef( aRange
, aAdr
) )
11455 nErr
= mrDoc
.GetErrCode( aAdr
);
11457 nErr
= nGlobalError
;
11466 PopDoubleRef( aRange
);
11467 if ( nGlobalError
!= FormulaError::NONE
)
11468 nErr
= nGlobalError
;
11472 if ( DoubleRefToPosSingleRef( aRange
, aAdr
) )
11473 nErr
= mrDoc
.GetErrCode( aAdr
);
11475 nErr
= nGlobalError
;
11482 PopSingleRef( aAdr
);
11483 if ( nGlobalError
!= FormulaError::NONE
)
11484 nErr
= nGlobalError
;
11486 nErr
= mrDoc
.GetErrCode( aAdr
);
11491 nErr
= nGlobalError
;
11493 nGlobalError
= nOldError
;
11497 void ScInterpreter::ScErrorType()
11499 FormulaError nErr
= GetErrorType();
11500 if ( nErr
!= FormulaError::NONE
)
11502 nGlobalError
= FormulaError::NONE
;
11503 PushDouble( static_cast<double>(nErr
) );
11511 void ScInterpreter::ScErrorType_ODF()
11513 FormulaError nErr
= GetErrorType();
11514 sal_uInt16 nErrType
;
11518 case FormulaError::NoCode
: // #NULL!
11521 case FormulaError::DivisionByZero
: // #DIV/0!
11524 case FormulaError::NoValue
: // #VALUE!
11527 case FormulaError::NoRef
: // #REF!
11530 case FormulaError::NoName
: // #NAME?
11533 case FormulaError::IllegalFPOperation
: // #NUM!
11536 case FormulaError::NotAvailable
: // #N/A
11540 #GETTING_DATA is a message that can appear in Excel when a large or
11541 complex worksheet is being calculated. In Excel 2007 and newer,
11542 operations are grouped so more complicated cells may finish after
11543 earlier ones do. While the calculations are still processing, the
11544 unfinished cells may display #GETTING_DATA.
11545 Because the message is temporary and disappears when the calculations
11546 complete, this isn’t a true error.
11547 No calc error code known (yet).
11549 case : // GETTING_DATA
11560 nGlobalError
=FormulaError::NONE
;
11561 PushDouble( nErrType
);
11567 static bool MayBeRegExp( std::u16string_view rStr
)
11569 if ( rStr
.empty() || (rStr
.size() == 1 && rStr
[0] != '.') )
11570 return false; // single meta characters can not be a regexp
11571 // First two characters are wildcard '?' and '*' characters.
11572 std::u16string_view
cre(u
"?*+.[]^$\\<>()|");
11573 return rStr
.find_first_of(cre
) != std::u16string_view::npos
;
11576 static bool MayBeWildcard( std::u16string_view rStr
)
11578 // Wildcards with '~' escape, if there are no wildcards then an escaped
11579 // character does not make sense, but it modifies the search pattern in an
11580 // Excel compatible wildcard search...
11581 std::u16string_view
cw(u
"*?~");
11582 return rStr
.find_first_of(cw
) != std::u16string_view::npos
;
11585 utl::SearchParam::SearchType
ScInterpreter::DetectSearchType( std::u16string_view rStr
, const ScDocument
& rDoc
)
11587 const auto eType
= rDoc
.GetDocOptions().GetFormulaSearchType();
11588 if ((eType
== utl::SearchParam::SearchType::Wildcard
&& MayBeWildcard(rStr
))
11589 || (eType
== utl::SearchParam::SearchType::Regexp
&& MayBeRegExp(rStr
)))
11591 return utl::SearchParam::SearchType::Normal
;
11594 bool ScInterpreter::SearchMatrixForValue( VectorSearchArguments
& vsa
, ScQueryParam
& rParam
, ScQueryEntry
& rEntry
, ScQueryEntry::Item
& rItem
)
11597 vsa
.pMatSrc
->GetDimensions( nC
, nR
);
11598 if (nC
> 1 && nR
> 1)
11600 // The source matrix must be a vector.
11601 PushIllegalParameter();
11604 vsa
.bVLookup
= ( nC
== 1 );
11606 // Do not propagate errors from matrix while searching.
11607 vsa
.pMatSrc
->SetErrorInterpreter( nullptr );
11609 SCSIZE nMatCount
= (vsa
.bVLookup
? nR
: nC
);
11610 VectorMatrixAccessor
aMatAcc(*(vsa
.pMatSrc
), vsa
.bVLookup
);
11611 bool bMatchWholeCell
= mrDoc
.GetDocOptions().IsMatchWholeCell();
11613 switch ( vsa
.eSearchMode
)
11617 switch ( vsa
.eMatchMode
)
11621 // simple serial search for equality mode (source data doesn't
11622 // need to be sorted).
11623 for (SCSIZE i
= 0; i
< nMatCount
; ++i
)
11625 if (lcl_CompareMatrix2Query( i
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
) == 0)
11627 vsa
.nHitIndex
= i
+1; // found !
11635 for (SCSIZE i
= 0; i
< nMatCount
; ++i
)
11637 sal_Int32 result
= lcl_CompareMatrix2Query( i
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
);
11640 vsa
.nHitIndex
= i
+1; // found !
11643 else if (vsa
.eMatchMode
== exactorS
&& result
== -1)
11645 if ( vsa
.nBestFit
== SCSIZE_MAX
)
11649 // replace value of vsa.nBestFit if value(i) > value(vsa.nBestFit)
11650 if ( lcl_Compare2MatrixCells( i
, aMatAcc
, vsa
.nBestFit
) == 1 )
11654 else if (vsa
.eMatchMode
== exactorG
&& result
== 1)
11656 if ( vsa
.nBestFit
== SCSIZE_MAX
)
11660 // replace value of vsa.nBestFit if value(i) < value(vsa.nBestFit)
11661 if ( lcl_Compare2MatrixCells( i
, aMatAcc
, vsa
.nBestFit
) == -1 )
11670 PushIllegalParameter();
11678 switch ( vsa
.eMatchMode
)
11682 // simple serial search for equality mode (source data doesn't
11683 // need to be sorted).
11684 for ( SCSIZE i
= nMatCount
- 1; i
> 0; i
-- )
11686 if (lcl_CompareMatrix2Query(i
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
) == 0)
11688 vsa
.nHitIndex
= i
+ 1; // found !
11696 for (SCSIZE i
= nMatCount
- 1; i
-- > 0; )
11698 sal_Int32 result
= lcl_CompareMatrix2Query( i
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
);
11701 vsa
.nHitIndex
= i
+ 1; // found !
11704 else if (vsa
.eMatchMode
== exactorS
&& result
== -1)
11706 if ( vsa
.nBestFit
== SCSIZE_MAX
)
11710 // replace value of vsa.nBestFit if value(i) > value(vsa.nBestFit)
11711 if ( lcl_Compare2MatrixCells( i
, aMatAcc
, vsa
.nBestFit
) == 1 )
11715 else if (vsa
.eMatchMode
== exactorG
&& result
== 1)
11717 if ( vsa
.nBestFit
== SCSIZE_MAX
)
11721 // replace value of vsa.nBestFit if value(i) < value(vsa.nBestFit)
11722 if ( lcl_Compare2MatrixCells( i
, aMatAcc
, vsa
.nBestFit
) == -1 )
11731 PushIllegalParameter();
11740 // binary search for non-equality mode (the source data is sorted)
11741 bool bAscOrder
= ( vsa
.eSearchMode
== searchbasc
);
11743 SCSIZE nLast
= nMatCount
- 1;
11744 for ( SCSIZE nLen
= nLast
- nFirst
; nLen
> 0; nLen
= nLast
- nFirst
)
11746 SCSIZE nMid
= nFirst
+ nLen
/ 2;
11747 sal_Int32 nCmp
= lcl_CompareMatrix2Query( nMid
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
);
11750 // exact match. find the last item with the same value.
11751 lcl_GetLastMatch( nMid
, aMatAcc
, nMatCount
);
11752 vsa
.nHitIndex
= nMid
+ 1;
11756 if ( nLen
== 1 ) // first and last items are next to each other.
11758 if ( bAscOrder
&& vsa
.eMatchMode
== exactorS
)
11759 vsa
.nHitIndex
= ( nCmp
> 0 ? nFirst
: nLast
);
11760 else if ( !bAscOrder
&& vsa
.eMatchMode
== exactorG
)
11761 vsa
.nHitIndex
= ( nCmp
< 0 ? nFirst
: nLast
);
11781 if ( vsa
.nHitIndex
== nMatCount
- 1 ) // last item
11783 nCmp
= lcl_CompareMatrix2Query( vsa
.nHitIndex
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
);
11784 if ( ( vsa
.eMatchMode
== exactorS
&& nCmp
<= 0 ) ||
11785 ( vsa
.eMatchMode
== exactorG
&& nCmp
>= 0 ) )
11787 // either the last item is an exact match or the real
11788 // hit is beyond the last item.
11800 PushIllegalParameter();
11804 if ((vsa
.nHitIndex
> 0) && ((rItem
.meType
== ScQueryEntry::ByString
&& aMatAcc
.IsValue(vsa
.nHitIndex
- 1)) ||
11805 (rItem
.meType
== ScQueryEntry::ByValue
&& !aMatAcc
.IsValue(vsa
.nHitIndex
- 1))))
11808 vsa
.isResultNA
= true;
11814 bool ScInterpreter::SearchRangeForValue( VectorSearchArguments
& vsa
, ScQueryParam
& rParam
, ScQueryEntry
& rEntry
)
11816 vsa
.bVLookup
= ( vsa
.nCol1
== vsa
.nCol2
);
11817 switch ( vsa
.eSearchMode
)
11826 // search of rows in column
11827 rParam
.bByRow
= true;
11828 ScAddress
aResultPos( vsa
.nCol1
, vsa
.nRow1
, vsa
.nTab1
);
11829 const ScComplexRefData
* refData
= nullptr;
11830 if ( LookupQueryWithCache( aResultPos
, rParam
, refData
, vsa
.eSearchMode
, vsa
.nSearchOpCode
) )
11831 vsa
.nHitIndex
= aResultPos
.Row() - vsa
.nRow1
+ 1;
11835 rParam
.bByRow
= false;
11836 bool bBinarySearch
= vsa
.eSearchMode
== searchbasc
|| vsa
.eSearchMode
== searchbdesc
;
11837 if (bBinarySearch
&& (vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
|| vsa
.nSearchOpCode
== SC_OPCODE_X_MATCH
))
11839 ScQueryCellIteratorSortedCache
aCellIter(mrDoc
, mrContext
, rParam
.nTab
, rParam
, false, false);
11840 // Advance Entry.nField in Iterator if column changed
11841 aCellIter
.SetAdvanceQueryParamEntryField(true);
11842 aCellIter
.SetSortedBinarySearchMode(vsa
.eSearchMode
);
11843 aCellIter
.SetLookupMode(vsa
.nSearchOpCode
);
11844 if (aCellIter
.GetFirst())
11846 vsa
.nHitIndex
= aCellIter
.GetCol() - vsa
.nCol1
+ 1;
11851 // search of columns in row
11852 bool bReverseSearch
= (vsa
.eSearchMode
== searchrev
);
11853 ScQueryCellIteratorDirect
aCellIter(mrDoc
, mrContext
, vsa
.nTab1
, rParam
, false, bReverseSearch
);
11854 // Advance Entry.nField in Iterator if column changed
11855 aCellIter
.SetAdvanceQueryParamEntryField(true);
11856 aCellIter
.SetLookupMode(vsa
.nSearchOpCode
);
11857 aCellIter
.SetSortedBinarySearchMode(vsa
.eSearchMode
);
11858 if (rEntry
.eOp
== SC_EQUAL
)
11860 if (aCellIter
.GetFirst())
11861 vsa
.nHitIndex
= aCellIter
.GetCol() - vsa
.nCol1
+ 1;
11867 if (aCellIter
.FindEqualOrSortedLastInRange(nC
, nR
))
11868 vsa
.nHitIndex
= nC
- vsa
.nCol1
+ 1;
11876 PushIllegalParameter();
11883 /** When search value is found, the index is stored in struct VectorSearchArguments.nIndex
11884 and SearchVectorForValue() returns true. When search value is not found or an error
11885 occurs, SearchVectorForValue() pushes the relevant (error)message and returns false,
11886 expect when SearchVectorForValue() is called by ScXLookup and the search value is not
11888 This difference in behaviour is because MATCH returns the found index and XLOOKUP
11889 uses the found index to determine the result(s) to be pushed and may return a custom
11890 value when the search value is not found.
11892 bool ScInterpreter::SearchVectorForValue( VectorSearchArguments
& vsa
)
11895 ScQueryParam rParam
;
11896 rParam
.nCol1
= vsa
.nCol1
;
11897 rParam
.nRow1
= vsa
.nRow1
;
11898 rParam
.nCol2
= vsa
.nCol2
;
11899 rParam
.nRow2
= vsa
.nRow2
;
11900 rParam
.nTab
= vsa
.nTab1
;
11902 ScQueryEntry
& rEntry
= rParam
.GetEntry(0);
11903 rEntry
.nField
= vsa
.eSearchMode
!= searchrev
? vsa
.nCol1
: vsa
.nCol2
;
11904 rEntry
.bDoQuery
= true;
11905 switch ( vsa
.eMatchMode
)
11908 rEntry
.eOp
= SC_EQUAL
;
11912 rEntry
.eOp
= SC_LESS_EQUAL
;
11916 rEntry
.eOp
= SC_GREATER_EQUAL
;
11921 // this mode can only used with XLOOKUP/XMATCH
11922 if ( vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
|| vsa
.nSearchOpCode
== SC_OPCODE_X_MATCH
)
11924 // Wildcard/Regex search mode with binary search is not allowed
11925 if (vsa
.eSearchMode
== searchbasc
|| vsa
.eSearchMode
== searchbdesc
)
11931 rEntry
.eOp
= SC_EQUAL
;
11932 if ( vsa
.isStringSearch
)
11934 if (vsa
.eMatchMode
== wildcard
&& MayBeWildcard(vsa
.sSearchStr
.getString()))
11935 rParam
.eSearchType
= utl::SearchParam::SearchType::Wildcard
;
11936 else if (vsa
.eMatchMode
== regex
&& MayBeRegExp(vsa
.sSearchStr
.getString()))
11937 rParam
.eSearchType
= utl::SearchParam::SearchType::Regexp
;
11939 rParam
.eSearchType
= utl::SearchParam::SearchType::Normal
;
11944 PushIllegalParameter();
11950 PushIllegalParameter();
11954 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
11955 // allow to match empty cells as result if we are looking for the next smaller
11956 // or larger values in case of the new lookup functions
11957 if (rEntry
.eOp
!= SC_EQUAL
&& (vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
||
11958 vsa
.nSearchOpCode
== SC_OPCODE_X_MATCH
))
11959 rItem
.mbMatchEmpty
= true;
11961 if ( vsa
.isStringSearch
)
11963 rItem
.meType
= ScQueryEntry::ByString
;
11964 rItem
.maString
= vsa
.sSearchStr
;
11965 if ( vsa
.nSearchOpCode
== SC_OPCODE_MATCH
)
11967 if ( mrDoc
.IsInVBAMode() )
11968 rParam
.eSearchType
= utl::SearchParam::SearchType::Wildcard
;
11970 rParam
.eSearchType
= DetectSearchType(rEntry
.GetQueryItem().maString
.getString(), mrDoc
);
11973 else if ( vsa
.isEmptySearch
&& (vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
||
11974 vsa
.nSearchOpCode
== SC_OPCODE_X_MATCH
) )
11976 rEntry
.SetQueryByEmpty();
11977 rItem
.mbMatchEmpty
= true;
11981 rItem
.mfVal
= vsa
.fSearchVal
;
11982 rItem
.meType
= ScQueryEntry::ByValue
;
11986 if (vsa
.pMatSrc
) // The source data is matrix array.
11989 if ( !SearchMatrixForValue( vsa
, rParam
, rEntry
, rItem
) )
11995 if ( !SearchRangeForValue( vsa
, rParam
, rEntry
) )
11999 // MATCH expects index starting with 1, XLOOKUP expects index starting with 0
12000 if ( vsa
.nHitIndex
> 0 )
12002 vsa
.nIndex
= ( vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
? --vsa
.nHitIndex
: vsa
.nHitIndex
);
12005 else if ( vsa
.nHitIndex
== 0 && vsa
.nBestFit
!= SCSIZE_MAX
)
12007 if ( vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
)
12009 vsa
.nIndex
= vsa
.nBestFit
;
12010 if ( !vsa
.pMatSrc
)
12012 vsa
.nIndex
-= ( vsa
.bVLookup
? vsa
.nRow1
: vsa
.nCol1
);
12017 vsa
.nIndex
= ++vsa
.nBestFit
;
12023 vsa
.isResultNA
= true;
12027 static bool lcl_LookupQuery( ScAddress
& o_rResultPos
, ScDocument
& rDoc
, ScInterpreterContext
& rContext
,
12028 const ScQueryParam
& rParam
, const ScQueryEntry
& rEntry
, const ScFormulaCell
* cell
,
12029 const ScComplexRefData
* refData
, sal_Int8 nSearchMode
, sal_uInt16 nOpCode
)
12031 if (rEntry
.eOp
!= SC_EQUAL
)
12033 // range lookup <= or >=
12036 bool bBinarySearch
= static_cast<SearchMode
>(nSearchMode
) == searchbasc
|| static_cast<SearchMode
>(nSearchMode
) == searchbdesc
;
12037 if ((bBinarySearch
&& (nOpCode
== SC_OPCODE_X_LOOKUP
|| nOpCode
== SC_OPCODE_X_MATCH
)) ||
12038 ScQueryCellIteratorSortedCache::CanBeUsed(rDoc
, rParam
, rParam
.nTab
, cell
, refData
, rContext
))
12040 ScQueryCellIteratorSortedCache
aCellIter(rDoc
, rContext
, rParam
.nTab
, rParam
, false, false);
12041 aCellIter
.SetSortedBinarySearchMode(nSearchMode
);
12042 aCellIter
.SetLookupMode(nOpCode
);
12043 if (aCellIter
.GetFirst())
12045 o_rResultPos
.SetCol(aCellIter
.GetCol());
12046 o_rResultPos
.SetRow(aCellIter
.GetRow());
12052 bool bReverse
= (static_cast<SearchMode
>(nSearchMode
) == searchrev
);
12053 ScQueryCellIteratorDirect
aCellIter(rDoc
, rContext
, rParam
.nTab
, rParam
, false, bReverse
);
12055 aCellIter
.SetSortedBinarySearchMode(nSearchMode
);
12056 aCellIter
.SetLookupMode(nOpCode
);
12057 if (aCellIter
.FindEqualOrSortedLastInRange(nCol
, nRow
))
12059 o_rResultPos
.SetCol(nCol
);
12060 o_rResultPos
.SetRow(nRow
);
12067 // we can use binary search for rows if the SearchMode is searchbasc or searchbdesc
12068 bool bLiteral
= rParam
.eSearchType
== utl::SearchParam::SearchType::Normal
&&
12069 rEntry
.GetQueryItem().meType
== ScQueryEntry::ByString
;
12070 bool bBinary
= rParam
.bByRow
&&
12071 (bLiteral
|| rEntry
.GetQueryItem().meType
== ScQueryEntry::ByValue
);
12073 if( bBinary
&& (static_cast<SearchMode
>(nSearchMode
) == searchbasc
|| static_cast<SearchMode
>(nSearchMode
) == searchbdesc
||
12074 ScQueryCellIteratorSortedCache::CanBeUsed(rDoc
, rParam
, rParam
.nTab
, cell
, refData
, rContext
)))
12076 ScQueryCellIteratorSortedCache
aCellIter( rDoc
, rContext
, rParam
.nTab
, rParam
, false, false );
12077 aCellIter
.SetSortedBinarySearchMode(nSearchMode
);
12078 aCellIter
.SetLookupMode(nOpCode
);
12079 if (aCellIter
.GetFirst())
12081 o_rResultPos
.SetCol( aCellIter
.GetCol());
12082 o_rResultPos
.SetRow( aCellIter
.GetRow());
12088 ScQueryCellIteratorDirect
aCellIter( rDoc
, rContext
, rParam
.nTab
, rParam
, false,
12089 static_cast<SearchMode
>(nSearchMode
) == searchrev
);
12090 aCellIter
.SetSortedBinarySearchMode(nSearchMode
);
12091 aCellIter
.SetLookupMode(nOpCode
);
12092 if (aCellIter
.GetFirst())
12094 o_rResultPos
.SetCol( aCellIter
.GetCol());
12095 o_rResultPos
.SetRow( aCellIter
.GetRow());
12104 // =VLOOKUP(SearchCriterion; RangeArray; Index; Sorted)
12105 // [SearchCriterion] is the value searched for in the first column of the array.
12106 // [RangeArray] is the reference, which is to comprise at least two columns.
12107 // [Index] is the number of the column in the array that contains the value to be returned. The first column has the number 1.
12109 // Prerequisite of lcl_getPrevRowWithEmptyValueLookup():
12110 // Value referenced by [SearchCriterion] is empty.
12111 // lcl_getPrevRowWithEmptyValueLookup() performs following checks:
12112 // - if we run query with "exact match" mode (i.e. VLOOKUP)
12113 // - and if we already have the same lookup done before but for another row
12114 // which is also had empty [SearchCriterion]
12117 // we could say, that for current row we could reuse results of the cached call which was done for the row2
12118 // In this case we return row index, which is >= 0.
12121 // -1 is returned, which will lead to default behavior =>
12122 // complete lookup will be done in RangeArray inside lcl_LookupQuery() method.
12124 // This method was added only for speed up to avoid several useless complete
12125 // lookups inside [RangeArray] for searching empty strings.
12127 static SCROW
lcl_getPrevRowWithEmptyValueLookup( const ScLookupCache
& rCache
,
12128 const ScLookupCache::QueryCriteria
& rCriteria
, const ScQueryParam
& rParam
)
12130 // is lookup value empty?
12131 const ScQueryEntry
& rEntry
= rParam
.GetEntry(0);
12132 const ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
12133 if (! rItem
.maString
.getString().isEmpty())
12134 return -1; // not found
12136 // try to find the row index for which we have already performed lookup
12137 // and have some result of it inside cache
12138 return rCache
.lookup( rCriteria
);
12141 bool ScInterpreter::LookupQueryWithCache( ScAddress
& o_rResultPos
,
12142 const ScQueryParam
& rParam
, const ScComplexRefData
* refData
,
12143 sal_Int8 nSearchMode
, sal_uInt16 nOpCode
) const
12145 bool bFound
= false;
12146 const ScQueryEntry
& rEntry
= rParam
.GetEntry(0);
12147 bool bColumnsMatch
= (rParam
.nCol1
== rEntry
.nField
);
12148 // At least all volatile functions that generate indirect references have
12149 // to force non-cached lookup.
12150 /* TODO: We could further classify volatile functions into reference
12151 * generating and not reference generating functions to have to force less
12152 * direct lookups here. We could even further attribute volatility per
12153 * parameter so it would affect only the lookup range parameter. */
12154 if (!bColumnsMatch
|| GetVolatileType() != NOT_VOLATILE
)
12155 bFound
= lcl_LookupQuery( o_rResultPos
, mrDoc
, mrContext
, rParam
, rEntry
, pMyFormulaCell
,
12156 refData
, nSearchMode
, nOpCode
);
12159 ScRange
aLookupRange( rParam
.nCol1
, rParam
.nRow1
, rParam
.nTab
,
12160 rParam
.nCol2
, rParam
.nRow2
, rParam
.nTab
);
12161 ScLookupCache
& rCache
= mrDoc
.GetLookupCache( aLookupRange
, &mrContext
);
12162 ScLookupCache::QueryCriteria
aCriteria( rEntry
, nSearchMode
);
12163 ScLookupCache::Result eCacheResult
= rCache
.lookup( o_rResultPos
,
12166 // tdf#121052: Slow load of cells with VLOOKUP with references to empty cells
12167 // This check was added only for speed up to avoid several useless complete
12168 // lookups inside [RangeArray] for searching empty strings.
12169 if (eCacheResult
== ScLookupCache::NOT_CACHED
&& aCriteria
.isEmptyStringQuery())
12171 const SCROW nPrevRowWithEmptyValueLookup
= lcl_getPrevRowWithEmptyValueLookup(rCache
, aCriteria
, rParam
);
12172 if (nPrevRowWithEmptyValueLookup
>= 0)
12174 // make the same lookup using cache with different row index
12175 // (this lookup was already cached)
12176 ScAddress
aPosPrev(aPos
);
12177 aPosPrev
.SetRow(nPrevRowWithEmptyValueLookup
);
12179 eCacheResult
= rCache
.lookup( o_rResultPos
, aCriteria
, aPosPrev
);
12183 switch (eCacheResult
)
12185 case ScLookupCache::NOT_CACHED
:
12186 case ScLookupCache::CRITERIA_DIFFERENT
:
12187 bFound
= lcl_LookupQuery( o_rResultPos
, mrDoc
, mrContext
, rParam
, rEntry
,
12188 pMyFormulaCell
, refData
, nSearchMode
, nOpCode
);
12189 if (eCacheResult
== ScLookupCache::NOT_CACHED
)
12190 rCache
.insert( o_rResultPos
, aCriteria
, aPos
, bFound
);
12192 case ScLookupCache::FOUND
:
12195 case ScLookupCache::NOT_AVAILABLE
:
12196 ; // nothing, bFound remains FALSE
12203 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */