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