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{