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 { // file name and table name: 'FILENAME'#$TABLE
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 aFuncResult = "'"
2351 + "'#$" + aTabName;
2352 }
2353 }
2354 }
2355 PushString( aFuncResult );
2356 }
2357 else if( aInfoType == "COORD" )
2358 { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW
2359 // Yes, passing tab as col is intentional!
2360 OUString aCellStr1 =
2361 ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format(
2363 OUString aCellStr2 =
2365 nullptr, mrDoc.GetAddressConvention());
2366 OUString aFuncResult = aCellStr1 + ":" + aCellStr2;
2367 PushString( aFuncResult );
2368 }
2369
2370// *** CELL PROPERTIES ***
2371 else if( aInfoType == "CONTENTS" )
2372 { // contents of the cell, no formatting
2373 if (aCell.hasString())
2374 {
2376 GetCellString(aStr, aCell);
2377 PushString( aStr );
2378 }
2379 else
2380 PushDouble(GetCellValue(aCellPos, aCell));
2381 }
2382 else if( aInfoType == "TYPE" )
2383 { // b = blank; l = string (label); v = otherwise (value)
2384 sal_Unicode c;
2385 if (aCell.hasString())
2386 c = 'l';
2387 else
2388 c = aCell.hasNumeric() ? 'v' : 'b';
2389 PushString( OUString(c) );
2390 }
2391 else if( aInfoType == "WIDTH" )
2392 { // column width (rounded off as count of zero characters in standard font and size)
2393 Printer* pPrinter = mrDoc.GetPrinter();
2394 MapMode aOldMode( pPrinter->GetMapMode() );
2395 vcl::Font aOldFont( pPrinter->GetFont() );
2396 vcl::Font aDefFont;
2397
2398 pPrinter->SetMapMode(MapMode(MapUnit::MapTwip));
2399 // font color doesn't matter here
2400 mrDoc.GetDefPattern()->GetFont( aDefFont, SC_AUTOCOL_BLACK, pPrinter );
2401 pPrinter->SetFont( aDefFont );
2402 tools::Long nZeroWidth = pPrinter->GetTextWidth( OUString( '0' ) );
2403 assert(nZeroWidth != 0);
2404 pPrinter->SetFont( aOldFont );
2405 pPrinter->SetMapMode( aOldMode );
2406 int nZeroCount = static_cast<int>(mrDoc.GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth);
2407 PushInt( nZeroCount );
2408 }
2409 else if( aInfoType == "PREFIX" )
2410 { // ' = left; " = right; ^ = centered
2411 sal_Unicode c = 0;
2412 if (aCell.hasString())
2413 {
2414 const SvxHorJustifyItem* pJustAttr = mrDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY );
2415 switch( pJustAttr->GetValue() )
2416 {
2417 case SvxCellHorJustify::Standard:
2418 case SvxCellHorJustify::Left:
2419 case SvxCellHorJustify::Block: c = '\''; break;
2420 case SvxCellHorJustify::Center: c = '^'; break;
2421 case SvxCellHorJustify::Right: c = '"'; break;
2422 case SvxCellHorJustify::Repeat: c = '\\'; break;
2423 }
2424 }
2425 PushString( OUString(c) );
2426 }
2427 else if( aInfoType == "PROTECT" )
2428 { // 1 = cell locked
2429 const ScProtectionAttr* pProtAttr = mrDoc.GetAttr( aCellPos, ATTR_PROTECTION );
2430 PushInt( pProtAttr->GetProtection() ? 1 : 0 );
2431 }
2432
2433// *** FORMATTING ***
2434 else if( aInfoType == "FORMAT" )
2435 { // specific format code for standard formats
2436 OUString aFuncResult;
2437 sal_uInt32 nFormat = mrDoc.GetNumberFormat( aCellPos );
2438 getFormatString(pFormatter, nFormat, aFuncResult);
2439 PushString( aFuncResult );
2440 }
2441 else if( aInfoType == "COLOR" )
2442 { // 1 = negative values are colored, otherwise 0
2443 const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
2444 PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 );
2445 }
2446 else if( aInfoType == "PARENTHESES" )
2447 { // 1 = format string contains a '(' character, otherwise 0
2448 const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
2449 PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 );
2450 }
2451 else
2453 }
2454}
2455
2457{
2458 sal_uInt16 nFileId;
2459 OUString aTabName;
2460 ScSingleRefData aRef;
2463 PopExternalSingleRef(nFileId, aTabName, aRef, pToken, &aFmt);
2464 if (nGlobalError != FormulaError::NONE)
2465 {
2467 return;
2468 }
2469
2470 OUString aInfoType = GetString().getString();
2471 if (nGlobalError != FormulaError::NONE)
2472 {
2474 return;
2475 }
2476
2477 SCCOL nCol;
2478 SCROW nRow;
2479 SCTAB nTab;
2480 aRef.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like.
2481 SingleRefToVars(aRef, nCol, nRow, nTab);
2482 if (nGlobalError != FormulaError::NONE)
2483 {
2485 return;
2486 }
2487 aRef.SetAbsTab(-1); // revert the value.
2488
2491
2492 if ( aInfoType == "COL" )
2493 PushInt(nCol + 1);
2494 else if ( aInfoType == "ROW" )
2495 PushInt(nRow + 1);
2496 else if ( aInfoType == "SHEET" )
2497 {
2498 // For SHEET, No idea what number we should set, but let's always set
2499 // 1 if the external sheet exists, no matter what sheet. Excel does
2500 // the same.
2501 if (pRefMgr->getCacheTable(nFileId, aTabName, false))
2502 PushInt(1);
2503 else
2504 SetError(FormulaError::NoName);
2505 }
2506 else if ( aInfoType == "ADDRESS" )
2507 {
2508 // ODF 1.2 says we need to always display address using the ODF A1 grammar.
2509 ScTokenArray aArray(mrDoc);
2510 aArray.AddExternalSingleReference(nFileId, svl::SharedString( aTabName), aRef); // string not interned
2512 OUString aStr;
2515 }
2516 else if ( aInfoType == "FILENAME" )
2517 {
2518 // 'file URI'#$SheetName
2519
2520 const OUString* p = pRefMgr->getExternalFileName(nFileId);
2521 if (!p)
2522 {
2523 // In theory this should never happen...
2524 SetError(FormulaError::NoName);
2525 return;
2526 }
2527
2528 OUString aBuf = "'" + *p + "'#$" + aTabName;
2530 }
2531 else if ( aInfoType == "CONTENTS" )
2532 {
2533 switch (pToken->GetType())
2534 {
2535 case svString:
2536 PushString(pToken->GetString());
2537 break;
2538 case svDouble:
2539 PushString(OUString::number(pToken->GetDouble()));
2540 break;
2541 case svError:
2542 PushString(ScGlobal::GetErrorString(pToken->GetError()));
2543 break;
2544 default:
2545 PushString(OUString());
2546 }
2547 }
2548 else if ( aInfoType == "TYPE" )
2549 {
2550 sal_Unicode c = 'v';
2551 switch (pToken->GetType())
2552 {
2553 case svString:
2554 c = 'l';
2555 break;
2556 case svEmptyCell:
2557 c = 'b';
2558 break;
2559 default:
2560 ;
2561 }
2562 PushString(OUString(c));
2563 }
2564 else if ( aInfoType == "FORMAT" )
2565 {
2566 OUString aFmtStr;
2567 sal_uLong nFmt = aFmt.mbIsSet ? aFmt.mnIndex : 0;
2568 getFormatString(pFormatter, nFmt, aFmtStr);
2569 PushString(aFmtStr);
2570 }
2571 else if ( aInfoType == "COLOR" )
2572 {
2573 // 1 = negative values are colored, otherwise 0
2574 int nVal = 0;
2575 if (aFmt.mbIsSet)
2576 {
2577 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2578 nVal = lcl_FormatHasNegColor(pFormat) ? 1 : 0;
2579 }
2580 PushInt(nVal);
2581 }
2582 else if ( aInfoType == "PARENTHESES" )
2583 {
2584 // 1 = format string contains a '(' character, otherwise 0
2585 int nVal = 0;
2586 if (aFmt.mbIsSet)
2587 {
2588 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2589 nVal = lcl_FormatHasOpenPar(pFormat) ? 1 : 0;
2590 }
2591 PushInt(nVal);
2592 }
2593 else
2595}
2596
2598{
2599 nFuncFmtType = SvNumFormatType::LOGICAL;
2600 bool bRes = false;
2601 switch ( GetStackType() )
2602 {
2603 case svSingleRef :
2604 {
2605 ScAddress aAdr;
2606 PopSingleRef( aAdr );
2607 if ( nGlobalError == FormulaError::NONE )
2608 bRes = true;
2609 }
2610 break;
2611 case svDoubleRef :
2612 {
2613 ScRange aRange;
2614 PopDoubleRef( aRange );
2615 if ( nGlobalError == FormulaError::NONE )
2616 bRes = true;
2617 }
2618 break;
2619 case svRefList :
2620 {
2622 if ( nGlobalError == FormulaError::NONE )
2623 bRes = !x->GetRefList()->empty();
2624 }
2625 break;
2627 {
2629 PopExternalSingleRef(pToken);
2630 if (nGlobalError == FormulaError::NONE)
2631 bRes = true;
2632 }
2633 break;
2635 {
2637 PopExternalDoubleRef(pArray);
2638 if (nGlobalError == FormulaError::NONE)
2639 bRes = true;
2640 }
2641 break;
2642 default:
2643 Pop();
2644 }
2645 nGlobalError = FormulaError::NONE;
2646 PushInt( int(bRes) );
2647}
2648
2650{
2651 nFuncFmtType = SvNumFormatType::LOGICAL;
2652 bool bRes = false;
2653 switch ( GetRawStackType() )
2654 {
2655 case svDouble:
2656 Pop();
2657 bRes = true;
2658 break;
2659 case svDoubleRef :
2660 case svSingleRef :
2661 {
2662 ScAddress aAdr;
2663 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2664 break;
2665
2666 ScRefCellValue aCell(mrDoc, aAdr);
2667 if (GetCellErrCode(aCell) == FormulaError::NONE)
2668 {
2669 switch (aCell.getType())
2670 {
2671 case CELLTYPE_VALUE :
2672 bRes = true;
2673 break;
2674 case CELLTYPE_FORMULA :
2675 bRes = (aCell.getFormula()->IsValue() && !aCell.getFormula()->IsEmpty());
2676 break;
2677 default:
2678 ; // nothing
2679 }
2680 }
2681 }
2682 break;
2684 {
2686 PopExternalSingleRef(pToken);
2687 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
2688 bRes = true;
2689 }
2690 break;
2692 case svMatrix:
2693 {
2694 ScMatrixRef pMat = GetMatrix();
2695 if ( !pMat )
2696 ; // nothing
2697 else if ( !pJumpMatrix )
2698 {
2699 if (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NONE)
2700 bRes = pMat->IsValue( 0, 0);
2701 }
2702 else
2703 {
2704 SCSIZE nCols, nRows, nC, nR;
2705 pMat->GetDimensions( nCols, nRows);
2706 pJumpMatrix->GetPos( nC, nR);
2707 if ( nC < nCols && nR < nRows )
2708 if (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NONE)
2709 bRes = pMat->IsValue( nC, nR);
2710 }
2711 }
2712 break;
2713 default:
2714 Pop();
2715 }
2716 nGlobalError = FormulaError::NONE;
2717 PushInt( int(bRes) );
2718}
2719
2721{
2722 nFuncFmtType = SvNumFormatType::LOGICAL;
2723 bool bRes = false;
2724 switch ( GetStackType() )
2725 {
2726 case svDoubleRef :
2727 if (IsInArrayContext())
2728 {
2729 SCCOL nCol1, nCol2;
2730 SCROW nRow1, nRow2;
2731 SCTAB nTab1, nTab2;
2732 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2733 if (nGlobalError != FormulaError::NONE)
2734 {
2736 return;
2737 }
2738 if (nTab1 != nTab2)
2739 {
2741 return;
2742 }
2743
2744 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCol2 - nCol1 + 1),
2745 static_cast<SCSIZE>(nRow2 - nRow1 + 1), true);
2746 if (!pResMat)
2747 {
2748 PushError( FormulaError::MatrixSize);
2749 return;
2750 }
2751
2752 /* TODO: we really should have a gap-aware cell iterator. */
2753 SCSIZE i=0, j=0;
2754 ScAddress aAdr( 0, 0, nTab1);
2755 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2756 {
2757 aAdr.SetCol(nCol);
2758 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2759 {
2760 aAdr.SetRow(nRow);
2761 ScRefCellValue aCell(mrDoc, aAdr);
2762 pResMat->PutBoolean( (aCell.getType() == CELLTYPE_FORMULA), i,j);
2763 ++j;
2764 }
2765 ++i;
2766 j = 0;
2767 }
2768
2769 PushMatrix( pResMat);
2770 return;
2771 }
2772 [[fallthrough]];
2773 case svSingleRef :
2774 {
2775 ScAddress aAdr;
2776 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2777 break;
2778
2779 bRes = (mrDoc.GetCellType(aAdr) == CELLTYPE_FORMULA);
2780 }
2781 break;
2782 default:
2783 Pop();
2784 }
2785 nGlobalError = FormulaError::NONE;
2786 PushInt( int(bRes) );
2787}
2788
2790{
2791 OUString aFormula;
2792 switch ( GetStackType() )
2793 {
2794 case svDoubleRef :
2795 if (IsInArrayContext())
2796 {
2797 SCCOL nCol1, nCol2;
2798 SCROW nRow1, nRow2;
2799 SCTAB nTab1, nTab2;
2800 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2801 if (nGlobalError != FormulaError::NONE)
2802 break;
2803
2804 if (nTab1 != nTab2)
2805 {
2806 SetError( FormulaError::IllegalArgument);
2807 break;
2808 }
2809
2810 ScMatrixRef pResMat = GetNewMat( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1, true);
2811 if (!pResMat)
2812 break;
2813
2814 /* TODO: use a column iterator instead? */
2815 SCSIZE i=0, j=0;
2816 ScAddress aAdr(0,0,nTab1);
2817 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2818 {
2819 aAdr.SetCol(nCol);
2820 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2821 {
2822 aAdr.SetRow(nRow);
2823 ScRefCellValue aCell(mrDoc, aAdr);
2824 switch (aCell.getType())
2825 {
2826 case CELLTYPE_FORMULA :
2828 pResMat->PutString( mrStrPool.intern( aFormula), i,j);
2829 break;
2830 default:
2831 pResMat->PutError( FormulaError::NotAvailable, i,j);
2832 }
2833 ++j;
2834 }
2835 ++i;
2836 j = 0;
2837 }
2838
2839 PushMatrix( pResMat);
2840 return;
2841 }
2842 [[fallthrough]];
2843 case svSingleRef :
2844 {
2845 ScAddress aAdr;
2846 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2847 break;
2848
2849 ScRefCellValue aCell(mrDoc, aAdr);
2850 switch (aCell.getType())
2851 {
2852 case CELLTYPE_FORMULA :
2854 break;
2855 default:
2856 SetError( FormulaError::NotAvailable );
2857 }
2858 }
2859 break;
2860 default:
2861 PopError();
2862 SetError( FormulaError::NotAvailable );
2863 }
2864 PushString( aFormula );
2865}
2866
2868{
2869 nFuncFmtType = SvNumFormatType::LOGICAL;
2870 bool bRes = false;
2871 switch ( GetStackType() )
2872 {
2873 case svDoubleRef :
2874 case svSingleRef :
2875 {
2876 ScAddress aAdr;
2877 bool bOk = PopDoubleRefOrSingleRef( aAdr );
2878 if ( nGlobalError == FormulaError::NotAvailable )
2879 bRes = true;
2880 else if (bOk)
2881 {
2882 ScRefCellValue aCell(mrDoc, aAdr);
2883 FormulaError nErr = GetCellErrCode(aCell);
2884 bRes = (nErr == FormulaError::NotAvailable);
2885 }
2886 }
2887 break;
2889 {
2891 PopExternalSingleRef(pToken);
2892 if (nGlobalError == FormulaError::NotAvailable ||
2893 (pToken && pToken->GetType() == svError && pToken->GetError() == FormulaError::NotAvailable))
2894 bRes = true;
2895 }
2896 break;
2898 case svMatrix:
2899 {
2900 ScMatrixRef pMat = GetMatrix();
2901 if ( !pMat )
2902 ; // nothing
2903 else if ( !pJumpMatrix )
2904 bRes = (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NotAvailable);
2905 else
2906 {
2907 SCSIZE nCols, nRows, nC, nR;
2908 pMat->GetDimensions( nCols, nRows);
2909 pJumpMatrix->GetPos( nC, nR);
2910 if ( nC < nCols && nR < nRows )
2911 bRes = (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NotAvailable);
2912 }
2913 }
2914 break;
2915 default:
2916 PopError();
2917 if ( nGlobalError == FormulaError::NotAvailable )
2918 bRes = true;
2919 }
2920 nGlobalError = FormulaError::NONE;
2921 PushInt( int(bRes) );
2922}
2923
2925{
2926 nFuncFmtType = SvNumFormatType::LOGICAL;
2927 bool bRes = false;
2928 switch ( GetStackType() )
2929 {
2930 case svDoubleRef :
2931 case svSingleRef :
2932 {
2933 ScAddress aAdr;
2934 bool bOk = PopDoubleRefOrSingleRef( aAdr );
2935 if ( !bOk || (nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) )
2936 bRes = true;
2937 else
2938 {
2939 ScRefCellValue aCell(mrDoc, aAdr);
2940 FormulaError nErr = GetCellErrCode(aCell);
2941 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2942 }
2943 }
2944 break;
2946 {
2948 PopExternalSingleRef(pToken);
2949 if ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pToken ||
2950 (pToken->GetType() == svError && pToken->GetError() != FormulaError::NotAvailable))
2951 bRes = true;
2952 }
2953 break;
2955 case svMatrix:
2956 {
2957 ScMatrixRef pMat = GetMatrix();
2958 if ( nGlobalError != FormulaError::NONE || !pMat )
2959 bRes = ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pMat);
2960 else if ( !pJumpMatrix )
2961 {
2962 FormulaError nErr = pMat->GetErrorIfNotString( 0, 0);
2963 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2964 }
2965 else
2966 {
2967 SCSIZE nCols, nRows, nC, nR;
2968 pMat->GetDimensions( nCols, nRows);
2969 pJumpMatrix->GetPos( nC, nR);
2970 if ( nC < nCols && nR < nRows )
2971 {
2972 FormulaError nErr = pMat->GetErrorIfNotString( nC, nR);
2973 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2974 }
2975 }
2976 }
2977 break;
2978 default:
2979 PopError();
2980 if ( nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable )
2981 bRes = true;
2982 }
2983 nGlobalError = FormulaError::NONE;
2984 PushInt( int(bRes) );
2985}
2986
2988{
2989 nFuncFmtType = SvNumFormatType::LOGICAL;
2990 bool bRes = false;
2991 switch ( GetStackType() )
2992 {
2993 case svDoubleRef :
2994 case svSingleRef :
2995 {
2996 ScAddress aAdr;
2997 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2998 {
2999 bRes = true;
3000 break;
3001 }
3002 if ( nGlobalError != FormulaError::NONE )
3003 bRes = true;
3004 else
3005 {
3006 ScRefCellValue aCell(mrDoc, aAdr);
3007 bRes = (GetCellErrCode(aCell) != FormulaError::NONE);
3008 }
3009 }
3010 break;
3012 {
3014 PopExternalSingleRef(pToken);
3015 if (nGlobalError != FormulaError::NONE || pToken->GetType() == svError)
3016 bRes = true;
3017 }
3018 break;
3020 case svMatrix:
3021 {
3022 ScMatrixRef pMat = GetMatrix();
3023 if ( nGlobalError != FormulaError::NONE || !pMat )
3024 bRes = true;
3025 else if ( !pJumpMatrix )
3026 bRes = (pMat->GetErrorIfNotString( 0, 0) != FormulaError::NONE);
3027 else
3028 {
3029 SCSIZE nCols, nRows, nC, nR;
3030 pMat->GetDimensions( nCols, nRows);
3031 pJumpMatrix->GetPos( nC, nR);
3032 if ( nC < nCols && nR < nRows )
3033 bRes = (pMat->GetErrorIfNotString( nC, nR) != FormulaError::NONE);
3034 }
3035 }
3036 break;
3037 default:
3038 PopError();
3039 if ( nGlobalError != FormulaError::NONE )
3040 bRes = true;
3041 }
3042 nGlobalError = FormulaError::NONE;
3043 PushInt( int(bRes) );
3044}
3045
3047{
3048 nFuncFmtType = SvNumFormatType::LOGICAL;
3049 bool bRes = false;
3050 double fVal = 0.0;
3051 switch ( GetStackType() )
3052 {
3053 case svDoubleRef :
3054 case svSingleRef :
3055 {
3056 ScAddress aAdr;
3057 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3058 break;
3059
3060 ScRefCellValue aCell(mrDoc, aAdr);
3061 FormulaError nErr = GetCellErrCode(aCell);
3062 if (nErr != FormulaError::NONE)
3063 SetError(nErr);
3064 else
3065 {
3066 switch (aCell.getType())
3067 {
3068 case CELLTYPE_VALUE :
3069 fVal = GetCellValue(aAdr, aCell);
3070 bRes = true;
3071 break;
3072 case CELLTYPE_FORMULA :
3073 if (aCell.getFormula()->IsValue())
3074 {
3075 fVal = GetCellValue(aAdr, aCell);
3076 bRes = true;
3077 }
3078 break;
3079 default:
3080 ; // nothing
3081 }
3082 }
3083 }
3084 break;
3085 case svDouble:
3086 {
3087 fVal = PopDouble();
3088 bRes = true;
3089 }
3090 break;
3092 {
3094 PopExternalSingleRef(pToken);
3095 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
3096 {
3097 fVal = pToken->GetDouble();
3098 bRes = true;
3099 }
3100 }
3101 break;
3103 case svMatrix:
3104 {
3105 ScMatrixRef pMat = GetMatrix();
3106 if ( !pMat )
3107 ; // nothing
3108 else if ( !pJumpMatrix )
3109 {
3110 bRes = pMat->IsValue( 0, 0);
3111 if ( bRes )
3112 fVal = pMat->GetDouble( 0, 0);
3113 }
3114 else
3115 {
3116 SCSIZE nCols, nRows, nC, nR;
3117 pMat->GetDimensions( nCols, nRows);
3118 pJumpMatrix->GetPos( nC, nR);
3119 if ( nC < nCols && nR < nRows )
3120 {
3121 bRes = pMat->IsValue( nC, nR);
3122 if ( bRes )
3123 fVal = pMat->GetDouble( nC, nR);
3124 }
3125 else
3126 SetError( FormulaError::NoValue);
3127 }
3128 }
3129 break;
3130 default:
3131 ; // nothing
3132 }
3133 if ( !bRes )
3134 SetError( FormulaError::IllegalParameter);
3135 else
3136 bRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 );
3137 return bRes;
3138}
3139
3141{
3142 PushInt( int(IsEven()) );
3143}
3144
3146{
3147 PushInt( int(!IsEven()) );
3148}
3149
3151{
3153 nGlobalError = FormulaError::NONE;
3154 // Temporarily override the ConvertStringToValue() error for
3155 // GetCellValue() / GetCellValueOrZero()
3157 mnStringNoValueError = FormulaError::CellNoValue;
3158 double fVal = GetDouble();
3159 mnStringNoValueError = nSErr;
3160 if (nErr != FormulaError::NONE)
3161 nGlobalError = nErr; // preserve previous error if any
3162 else if (nGlobalError == FormulaError::CellNoValue)
3163 nGlobalError = FormulaError::NONE; // reset temporary detection error
3164 PushDouble(fVal);
3165}
3166
3168{
3169 // Doesn't only trim but also removes duplicated blanks within!
3170 OUString aVal = comphelper::string::strip(GetString().getString(), ' ');
3171 OUStringBuffer aStr;
3172 const sal_Unicode* p = aVal.getStr();
3173 const sal_Unicode* const pEnd = p + aVal.getLength();
3174 while ( p < pEnd )
3175 {
3176 if ( *p != ' ' || p[-1] != ' ' ) // first can't be ' ', so -1 is fine
3177 aStr.append(*p);
3178 p++;
3179 }
3180 PushString(aStr.makeStringAndClear());
3181}
3182
3184{
3185 OUString aString = ScGlobal::getCharClass().uppercase(GetString().getString());
3186 PushString(aString);
3187}
3188
3190{
3191//2do: what to do with I18N-CJK ?!?
3192 OUStringBuffer aStr(GetString().getString());
3193 const sal_Int32 nLen = aStr.getLength();
3194 if ( nLen > 0 )
3195 {
3196 OUString aUpr(ScGlobal::getCharClass().uppercase(aStr.toString()));
3197 OUString aLwr(ScGlobal::getCharClass().lowercase(aStr.toString()));
3198 aStr[0] = aUpr[0];
3199 sal_Int32 nPos = 1;
3200 while( nPos < nLen )
3201 {
3202 OUString aTmpStr( aStr[nPos-1] );
3203 if ( !ScGlobal::getCharClass().isLetter( aTmpStr, 0 ) )
3204 aStr[nPos] = aUpr[nPos];
3205 else
3206 aStr[nPos] = aLwr[nPos];
3207 ++nPos;
3208 }
3209 }
3210 PushString(aStr.makeStringAndClear());
3211}
3212
3214{
3215 OUString aString = ScGlobal::getCharClass().lowercase(GetString().getString());
3216 PushString(aString);
3217}
3218
3220{
3221 OUString aStr = GetString().getString();
3222 sal_Int32 nIdx = 0;
3223 sal_Int32 nCnt = 0;
3224 while ( nIdx < aStr.getLength() )
3225 {
3226 aStr.iterateCodePoints( &nIdx );
3227 ++nCnt;
3228 }
3229 PushDouble( nCnt );
3230}
3231
3233{
3234 switch ( GetStackType() )
3235 {
3236 case svDoubleRef :
3237 case svSingleRef :
3238 {
3239 ScAddress aAdr;
3240 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3241 {
3242 PushInt(0);
3243 return ;
3244 }
3245 bool bValue = false;
3246 ScRefCellValue aCell(mrDoc, aAdr);
3247 if (GetCellErrCode(aCell) == FormulaError::NONE)
3248 {
3249 switch (aCell.getType())
3250 {
3251 case CELLTYPE_VALUE :
3252 bValue = true;
3253 break;
3254 case CELLTYPE_FORMULA :
3255 bValue = aCell.getFormula()->IsValue();
3256 break;
3257 default:
3258 ; // nothing
3259 }
3260 }
3261 if ( bValue )
3262 PushString(OUString());
3263 else
3264 {
3265 // like GetString()
3267 GetCellString(aStr, aCell);
3269 }
3270 }
3271 break;
3272 case svMatrix:
3275 {
3276 double fVal;
3278 ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
3279 if (ScMatrix::IsValueType( nMatValType))
3281 else
3282 PushString( aStr);
3283 }
3284 break;
3285 case svDouble :
3286 {
3287 PopError();
3288 PushString( OUString() );
3289 }
3290 break;
3291 case svString :
3292 ; // leave on stack
3293 break;
3294 default :
3295 PushError( FormulaError::UnknownOpCode);
3296 }
3297}
3298
3300{
3301 OUString aInputString;
3302 double fVal;
3303
3304 switch ( GetRawStackType() )
3305 {
3306 case svMissing:
3307 case svEmptyCell:
3308 Pop();
3309 PushInt(0);
3310 return;
3311 case svDouble:
3312 return; // leave on stack
3313
3314 case svSingleRef:
3315 case svDoubleRef:
3316 {
3317 ScAddress aAdr;
3318 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3319 {
3320 PushInt(0);
3321 return;
3322 }
3323 ScRefCellValue aCell(mrDoc, aAdr);
3324 if (aCell.hasString())
3325 {
3327 GetCellString(aSS, aCell);
3328 aInputString = aSS.getString();
3329 }
3330 else if (aCell.hasNumeric())
3331 {
3332 PushDouble( GetCellValue(aAdr, aCell) );
3333 return;
3334 }
3335 else
3336 {
3337 PushDouble(0.0);
3338 return;
3339 }
3340 }
3341 break;
3342 case svMatrix:
3343 {
3346 aSS);
3347 aInputString = aSS.getString();
3348 switch (nType)
3349 {
3351 fVal = 0.0;
3352 [[fallthrough]];
3355 PushDouble( fVal);
3356 return;
3358 // evaluated below
3359 break;
3360 default:
3362 }
3363 }
3364 break;
3365 default:
3366 aInputString = GetString().getString();
3367 break;
3368 }
3369
3370 sal_uInt32 nFIndex = 0; // 0 for default locale
3371 if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
3372 PushDouble(fVal);
3373 else
3375}
3376
3377// fdo#57180
3379{
3380
3381 sal_uInt8 nParamCount = GetByte();
3382 if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
3383 return;
3384
3385 OUString aInputString;
3386 OUString aGroupSeparator;
3387 sal_Unicode cDecimalSeparator = 0;
3388
3389 if ( nParamCount == 3 )
3390 aGroupSeparator = GetString().getString();
3391
3392 if ( nParamCount >= 2 )
3393 {
3394 OUString aDecimalSeparator = GetString().getString();
3395 if ( aDecimalSeparator.getLength() == 1 )
3396 cDecimalSeparator = aDecimalSeparator[ 0 ];
3397 else
3398 {
3399 PushIllegalArgument(); //if given, separator length must be 1
3400 return;
3401 }
3402 }
3403
3404 if ( cDecimalSeparator && aGroupSeparator.indexOf( cDecimalSeparator ) != -1 )
3405 {
3406 PushIllegalArgument(); //decimal separator cannot appear in group separator
3407 return;
3408 }
3409
3410 switch (GetStackType())
3411 {
3412 case svDouble:
3413 return; // leave on stack
3414 default:
3415 aInputString = GetString().getString();
3416 }
3417 if ( nGlobalError != FormulaError::NONE )
3418 {
3420 return;
3421 }
3422 if ( aInputString.isEmpty() )
3423 {
3425 PushDouble( 0.0 );
3426 else
3427 PushNoValue();
3428 return;
3429 }
3430
3431 sal_Int32 nDecSep = aInputString.indexOf( cDecimalSeparator );
3432 if ( nDecSep != 0 )
3433 {
3434 OUString aTemporary( nDecSep >= 0 ? aInputString.copy( 0, nDecSep ) : aInputString );
3435 sal_Int32 nIndex = 0;
3436 while (nIndex < aGroupSeparator.getLength())
3437 {
3438 sal_uInt32 nChar = aGroupSeparator.iterateCodePoints( &nIndex );
3439 aTemporary = aTemporary.replaceAll( OUString( &nChar, 1 ), "" );
3440 }
3441 if ( nDecSep >= 0 )
3442 aInputString = aTemporary + aInputString.subView( nDecSep );
3443 else
3444 aInputString = aTemporary;
3445 }
3446
3447 for ( sal_Int32 i = aInputString.getLength(); --i >= 0; )
3448 {
3449 sal_Unicode c = aInputString[ i ];
3450 if ( c == 0x0020 || c == 0x0009 || c == 0x000A || c == 0x000D )
3451 aInputString = aInputString.replaceAt( i, 1, u"" ); // remove spaces etc.
3452 }
3453 sal_Int32 nPercentCount = 0;
3454 for ( sal_Int32 i = aInputString.getLength() - 1; i >= 0 && aInputString[ i ] == 0x0025; i-- )
3455 {
3456 aInputString = aInputString.replaceAt( i, 1, u"" ); // remove and count trailing '%'
3457 nPercentCount++;
3458 }
3459
3460 rtl_math_ConversionStatus eStatus;
3461 sal_Int32 nParseEnd;
3462 double fVal = ::rtl::math::stringToDouble( aInputString, cDecimalSeparator, 0, &eStatus, &nParseEnd );
3463 if ( eStatus == rtl_math_ConversionStatus_Ok && nParseEnd == aInputString.getLength() )
3464 {
3465 if (nPercentCount)
3466 fVal *= pow( 10.0, -(nPercentCount * 2)); // process '%' from input string
3467 PushDouble(fVal);
3468 return;
3469 }
3470 PushNoValue();
3471}
3472
3473static bool lcl_ScInterpreter_IsPrintable( sal_uInt32 nCodePoint )
3474{
3475 return ( !u_isISOControl(nCodePoint) /*not in Cc*/
3476 && u_isdefined(nCodePoint) /*not in Cn*/ );
3477}
3478
3479
3481{
3482 OUString aStr = GetString().getString();
3483
3484 OUStringBuffer aBuf( aStr.getLength() );
3485 sal_Int32 nIdx = 0;
3486 while ( nIdx < aStr.getLength() )
3487 {
3488 sal_uInt32 c = aStr.iterateCodePoints( &nIdx );
3490 aBuf.appendUtf32( c );
3491 }
3492 PushString( aBuf.makeStringAndClear() );
3493}
3494
3495
3497{
3498//2do: make it full range unicode?
3499 OUString aStr = GetString().getString();
3500 if (aStr.isEmpty())
3501 PushInt(0);
3502 else
3503 {
3504 //"classic" ByteString conversion flags
3505 const sal_uInt32 convertFlags =
3506 RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE |
3507 RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE |
3508 RTL_UNICODETOTEXT_FLAGS_FLUSH |
3509 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT |
3510 RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT |
3511 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE;
3512 PushInt( static_cast<unsigned char>(OUStringToOString(OUStringChar(aStr[0]), osl_getThreadTextEncoding(), convertFlags).toChar()) );
3513 }
3514}
3515
3517{
3518//2do: make it full range unicode?
3519 double fVal = GetDouble();
3520 if (fVal < 0.0 || fVal >= 256.0)
3522 else
3523 {
3524 //"classic" ByteString conversion flags
3525 const sal_uInt32 convertFlags =
3526 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
3527 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
3528 RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT;
3529
3530 char cEncodedChar = static_cast<char>(fVal);
3531 OUString aStr(&cEncodedChar, 1, osl_getThreadTextEncoding(), convertFlags);
3533 }
3534}
3535
3536/* #i70213# fullwidth/halfwidth conversion provided by
3537 * Takashi Nakamoto <bluedwarf@ooo>
3538 * erAck: added Excel compatibility conversions as seen in issue's test case. */
3539
3540static OUString lcl_convertIntoHalfWidth( const OUString & rStr )
3541{
3542 // Make the initialization thread-safe. Since another function needs to be called, move it all to another
3543 // function and thread-safely initialize a static reference in this function.
3544 auto init = []() -> utl::TransliterationWrapper&
3545 {
3546 static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3547 trans.loadModuleByImplName( "FULLWIDTH_HALFWIDTH_LIKE_ASC", LANGUAGE_SYSTEM );
3548 return trans;
3549 };
3550 static utl::TransliterationWrapper& aTrans( init());
3551 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3552}
3553
3554static OUString lcl_convertIntoFullWidth( const OUString & rStr )
3555{
3556 auto init = []() -> utl::TransliterationWrapper&
3557 {
3558 static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3559 trans.loadModuleByImplName( "HALFWIDTH_FULLWIDTH_LIKE_JIS", LANGUAGE_SYSTEM );
3560 return trans;
3561 };
3562 static utl::TransliterationWrapper& aTrans( init());
3563 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3564}
3565
3566/* ODFF:
3567 * Summary: Converts half-width to full-width ASCII and katakana characters.
3568 * Semantics: Conversion is done for half-width ASCII and katakana characters,
3569 * other characters are simply copied from T to the result. This is the
3570 * complementary function to ASC.
3571 * For references regarding halfwidth and fullwidth characters see
3572 * http://www.unicode.org/reports/tr11/
3573 * http://www.unicode.org/charts/charindex2.html#H
3574 * http://www.unicode.org/charts/charindex2.html#F
3575 */
3577{
3578 if (MustHaveParamCount( GetByte(), 1))
3580}
3581
3582/* ODFF:
3583 * Summary: Converts full-width to half-width ASCII and katakana characters.
3584 * Semantics: Conversion is done for full-width ASCII and katakana characters,
3585 * other characters are simply copied from T to the result. This is the
3586 * complementary function to JIS.
3587 */
3589{
3590 if (MustHaveParamCount( GetByte(), 1))
3592}
3593
3595{
3596 if ( MustHaveParamCount( GetByte(), 1 ) )
3597 {
3598 OUString aStr = GetString().getString();
3599 if (aStr.isEmpty())
3601 else
3602 {
3603 PushDouble(aStr.iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
3604 }
3605 }
3606}
3607
3609{
3610 if ( MustHaveParamCount( GetByte(), 1 ) )
3611 {
3612 sal_uInt32 nCodePoint = GetUInt32();
3613 if (nGlobalError != FormulaError::NONE || !rtl::isUnicodeCodePoint(nCodePoint))
3615 else
3616 {
3617 OUString aStr( &nCodePoint, 1 );
3618 PushString( aStr );
3619 }
3620 }
3621}
3622
3623bool ScInterpreter::SwitchToArrayRefList( ScMatrixRef& xResMat, SCSIZE nMatRows, double fCurrent,
3624 const std::function<void( SCSIZE i, double fCurrent )>& MatOpFunc, bool bDoMatOp )
3625{
3626 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
3627 if (!p || !p->IsArrayResult())
3628 return false;
3629
3630 if (!xResMat)
3631 {
3632 // Create and init all elements with current value.
3633 assert(nMatRows > 0);
3634 xResMat = GetNewMat( 1, nMatRows, true);
3635 xResMat->FillDouble( fCurrent, 0,0, 0,nMatRows-1);
3636 }
3637 else if (bDoMatOp)
3638 {
3639 // Current value and values from vector are operands
3640 // for each vector position.
3641 for (SCSIZE i=0; i < nMatRows; ++i)
3642 {
3643 MatOpFunc( i, fCurrent);
3644 }
3645 }
3646 return true;
3647}
3648
3649void ScInterpreter::ScMin( bool bTextAsZero )
3650{
3651 short nParamCount = GetByte();
3652 if (!MustHaveParamCountMin( nParamCount, 1))
3653 return;
3654
3655 ScMatrixRef xResMat;
3656 double nMin = ::std::numeric_limits<double>::max();
3657 auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMin )
3658 {
3659 double fVecRes = xResMat->GetDouble(0,i);
3660 if (fVecRes > fCurMin)
3661 xResMat->PutDouble( fCurMin, 0,i);
3662 };
3663 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3664 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3665
3666 double nVal = 0.0;
3667 ScAddress aAdr;
3668 ScRange aRange;
3669 size_t nRefInList = 0;
3670 while (nParamCount-- > 0)
3671 {
3672 switch (GetStackType())
3673 {
3674 case svDouble :
3675 {
3676 nVal = GetDouble();
3677 if (nMin > nVal) nMin = nVal;
3678 nFuncFmtType = SvNumFormatType::NUMBER;
3679 }
3680 break;
3681 case svSingleRef :
3682 {
3683 PopSingleRef( aAdr );
3684 ScRefCellValue aCell(mrDoc, aAdr);
3685 if (aCell.hasNumeric())
3686 {
3687 nVal = GetCellValue(aAdr, aCell);
3689 if (nMin > nVal) nMin = nVal;
3690 }
3691 else if (bTextAsZero && aCell.hasString())
3692 {
3693 if ( nMin > 0.0 )
3694 nMin = 0.0;
3695 }
3696 }
3697 break;
3698 case svRefList :
3699 {
3700 // bDoMatOp only for non-array value when switching to
3701 // ArrayRefList.
3702 if (SwitchToArrayRefList( xResMat, nMatRows, nMin, MatOpFunc,
3703 nRefArrayPos == std::numeric_limits<size_t>::max()))
3704 {
3705 nRefArrayPos = nRefInList;
3706 }
3707 }
3708 [[fallthrough]];
3709 case svDoubleRef :
3710 {
3711 FormulaError nErr = FormulaError::NONE;
3712 PopDoubleRef( aRange, nParamCount, nRefInList);
3713 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
3714 if (aValIter.GetFirst(nVal, nErr))
3715 {
3716 if (nMin > nVal)
3717 nMin = nVal;
3719 while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3720 {
3721 if (nMin > nVal)
3722 nMin = nVal;
3723 }
3724 SetError(nErr);
3725 }
3726 if (nRefArrayPos != std::numeric_limits<size_t>::max())
3727 {
3728 // Update vector element with current value.
3729 MatOpFunc( nRefArrayPos, nMin);
3730
3731 // Reset.
3732 nMin = std::numeric_limits<double>::max();
3733 nVal = 0.0;
3734 nRefArrayPos = std::numeric_limits<size_t>::max();
3735 }
3736 }
3737 break;
3738 case svMatrix :
3741 {
3742 ScMatrixRef pMat = GetMatrix();
3743 if (pMat)
3744 {
3745 nFuncFmtType = SvNumFormatType::NUMBER;
3746 nVal = pMat->GetMinValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
3747 if (nMin > nVal)
3748 nMin = nVal;
3749 }
3750 }
3751 break;
3752 case svString :
3753 {
3754 Pop();
3755 if ( bTextAsZero )
3756 {
3757 if ( nMin > 0.0 )
3758 nMin = 0.0;
3759 }
3760 else
3761 SetError(FormulaError::IllegalParameter);
3762 }
3763 break;
3764 default :
3765 PopError();
3766 SetError(FormulaError::IllegalParameter);
3767 }
3768 }
3769
3770 if (xResMat)
3771 {
3772 // Include value of last non-references-array type and calculate final result.
3773 if (nMin < std::numeric_limits<double>::max())
3774 {
3775 for (SCSIZE i=0; i < nMatRows; ++i)
3776 {
3777 MatOpFunc( i, nMin);
3778 }
3779 }
3780 else
3781 {
3782 /* TODO: the awkward "no value is minimum 0.0" is likely the case
3783 * if a value is numeric_limits::max. Still, that could be a valid
3784 * minimum value as well, but nVal and nMin had been reset after
3785 * the last svRefList... so we may lie here. */
3786 for (SCSIZE i=0; i < nMatRows; ++i)
3787 {
3788 double fVecRes = xResMat->GetDouble(0,i);
3789 if (fVecRes == std::numeric_limits<double>::max())
3790 xResMat->PutDouble( 0.0, 0,i);
3791 }
3792 }
3793 PushMatrix( xResMat);
3794 }
3795 else
3796 {
3797 if (!std::isfinite(nVal))
3799 else if ( nVal < nMin )
3800 PushDouble(0.0); // zero or only empty arguments
3801 else
3802 PushDouble(nMin);
3803 }
3804}
3805
3806void ScInterpreter::ScMax( bool bTextAsZero )
3807{
3808 short nParamCount = GetByte();
3809 if (!MustHaveParamCountMin( nParamCount, 1))
3810 return;
3811
3812 ScMatrixRef xResMat;
3813 double nMax = std::numeric_limits<double>::lowest();
3814 auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMax )
3815 {
3816 double fVecRes = xResMat->GetDouble(0,i);
3817 if (fVecRes < fCurMax)
3818 xResMat->PutDouble( fCurMax, 0,i);
3819 };
3820 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3821 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3822
3823 double nVal = 0.0;
3824 ScAddress aAdr;
3825 ScRange aRange;
3826 size_t nRefInList = 0;
3827 while (nParamCount-- > 0)
3828 {
3829 switch (GetStackType())
3830 {
3831 case svDouble :
3832 {
3833 nVal = GetDouble();
3834 if (nMax < nVal) nMax = nVal;
3835 nFuncFmtType = SvNumFormatType::NUMBER;
3836 }
3837 break;
3838 case svSingleRef :
3839 {
3840 PopSingleRef( aAdr );
3841 ScRefCellValue aCell(mrDoc, aAdr);
3842 if (aCell.hasNumeric())
3843 {
3844 nVal = GetCellValue(aAdr, aCell);
3846 if (nMax < nVal) nMax = nVal;
3847 }
3848 else if (bTextAsZero && aCell.hasString())
3849 {
3850 if ( nMax < 0.0 )
3851 nMax = 0.0;
3852 }
3853 }
3854 break;
3855 case svRefList :
3856 {
3857 // bDoMatOp only for non-array value when switching to
3858 // ArrayRefList.
3859 if (SwitchToArrayRefList( xResMat, nMatRows, nMax, MatOpFunc,
3860 nRefArrayPos == std::numeric_limits<size_t>::max()))
3861 {
3862 nRefArrayPos = nRefInList;
3863 }
3864 }
3865 [[fallthrough]];
3866 case svDoubleRef :
3867 {
3868 FormulaError nErr = FormulaError::NONE;
3869 PopDoubleRef( aRange, nParamCount, nRefInList);
3870 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
3871 if (aValIter.GetFirst(nVal, nErr))
3872 {
3873 if (nMax < nVal)
3874 nMax = nVal;
3876 while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3877 {
3878 if (nMax < nVal)
3879 nMax = nVal;
3880 }
3881 SetError(nErr);
3882 }
3883 if (nRefArrayPos != std::numeric_limits<size_t>::max())
3884 {
3885 // Update vector element with current value.
3886 MatOpFunc( nRefArrayPos, nMax);
3887
3888 // Reset.
3889 nMax = std::numeric_limits<double>::lowest();
3890 nVal = 0.0;
3891 nRefArrayPos = std::numeric_limits<size_t>::max();
3892 }
3893 }
3894 break;
3895 case svMatrix :
3898 {
3899 ScMatrixRef pMat = GetMatrix();
3900 if (pMat)
3901 {
3902 nFuncFmtType = SvNumFormatType::NUMBER;
3903 nVal = pMat->GetMaxValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
3904 if (nMax < nVal)
3905 nMax = nVal;
3906 }
3907 }
3908 break;
3909 case svString :
3910 {
3911 Pop();
3912 if ( bTextAsZero )
3913 {
3914 if ( nMax < 0.0 )
3915 nMax = 0.0;
3916 }
3917 else
3918 SetError(FormulaError::IllegalParameter);
3919 }
3920 break;
3921 default :
3922 PopError();
3923 SetError(FormulaError::IllegalParameter);
3924 }
3925 }
3926
3927 if (xResMat)
3928 {
3929 // Include value of last non-references-array type and calculate final result.
3930 if (nMax > std::numeric_limits<double>::lowest())
3931 {
3932 for (SCSIZE i=0; i < nMatRows; ++i)
3933 {
3934 MatOpFunc( i, nMax);
3935 }
3936 }
3937 else
3938 {
3939 /* TODO: the awkward "no value is maximum 0.0" is likely the case
3940 * if a value is numeric_limits::lowest. Still, that could be a
3941 * valid maximum value as well, but nVal and nMax had been reset
3942 * after the last svRefList... so we may lie here. */
3943 for (SCSIZE i=0; i < nMatRows; ++i)
3944 {
3945 double fVecRes = xResMat->GetDouble(0,i);
3946 if (fVecRes == -std::numeric_limits<double>::max())
3947 xResMat->PutDouble( 0.0, 0,i);
3948 }
3949 }
3950 PushMatrix( xResMat);
3951 }
3952 else
3953 {
3954 if (!std::isfinite(nVal))
3956 else if ( nVal > nMax )
3957 PushDouble(0.0); // zero or only empty arguments
3958 else
3959 PushDouble(nMax);
3960 }
3961}
3962
3963void ScInterpreter::GetStVarParams( bool bTextAsZero, double(*VarResult)( double fVal, size_t nValCount ) )
3964{
3965 short nParamCount = GetByte();
3966 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3967
3968 struct ArrayRefListValue
3969 {
3970 std::vector<double> mvValues;
3971 KahanSum mfSum;
3972 ArrayRefListValue() = default;
3973 double get() const { return mfSum.get(); }
3974 };
3975 std::vector<ArrayRefListValue> vArrayValues;
3976
3977 std::vector<double> values;
3978 KahanSum fSum = 0.0;
3979 double fVal = 0.0;
3980 ScAddress aAdr;
3981 ScRange aRange;
3982 size_t nRefInList = 0;
3983 while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
3984 {
3985 switch (GetStackType())
3986 {
3987 case svDouble :
3988 {
3989 fVal = GetDouble();
3990 if (nGlobalError == FormulaError::NONE)
3991 {
3992 values.push_back(fVal);
3993 fSum += fVal;
3994 }
3995 }
3996 break;
3997 case svSingleRef :
3998 {
3999 PopSingleRef( aAdr );
4000 ScRefCellValue aCell(mrDoc, aAdr);
4001 if (aCell.hasNumeric())
4002 {
4003 fVal = GetCellValue(aAdr, aCell);
4004 if (nGlobalError == FormulaError::NONE)
4005 {
4006 values.push_back(fVal);
4007 fSum += fVal;
4008 }
4009 }
4010 else if (bTextAsZero && aCell.hasString())
4011 {
4012 values.push_back(0.0);
4013 }
4014 }
4015 break;
4016 case svRefList :
4017 {
4018 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
4019 if (p && p->IsArrayResult())
4020 {
4021 size_t nRefArrayPos = nRefInList;
4022 if (vArrayValues.empty())
4023 {
4024 // Create and init all elements with current value.
4025 assert(nMatRows > 0);
4026 vArrayValues.resize(nMatRows);
4027 for (ArrayRefListValue & it : vArrayValues)
4028 {
4029 it.mvValues = values;
4030 it.mfSum = fSum;
4031 }
4032 }
4033 else
4034 {
4035 // Current value and values from vector are operands
4036 // for each vector position.
4037 for (ArrayRefListValue & it : vArrayValues)
4038 {
4039 it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
4040 it.mfSum += fSum;
4041 }
4042 }
4043 ArrayRefListValue& rArrayValue = vArrayValues[nRefArrayPos];
4044 FormulaError nErr = FormulaError::NONE;
4045 PopDoubleRef( aRange, nParamCount, nRefInList);
4046 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
4047 if (aValIter.GetFirst(fVal, nErr))
4048 {
4049 do
4050 {
4051 rArrayValue.mvValues.push_back(fVal);
4052 rArrayValue.mfSum += fVal;
4053 }
4054 while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4055 }
4056 if ( nErr != FormulaError::NONE )
4057 {
4058 rArrayValue.mfSum = CreateDoubleError( nErr);
4059 }
4060 // Reset.
4061 std::vector<double>().swap(values);
4062 fSum = 0.0;
4063 break;
4064 }
4065 }
4066 [[fallthrough]];
4067 case svDoubleRef :
4068 {
4069 FormulaError nErr = FormulaError::NONE;
4070 PopDoubleRef( aRange, nParamCount, nRefInList);
4071 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
4072 if (aValIter.GetFirst(fVal, nErr))
4073 {
4074 do
4075 {
4076 values.push_back(fVal);
4077 fSum += fVal;
4078 }
4079 while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4080 }
4081 if ( nErr != FormulaError::NONE )
4082 {
4083 SetError(nErr);
4084 }
4085 }
4086 break;
4087 case svExternalSingleRef :
4088 case svExternalDoubleRef :
4089 case svMatrix :
4090 {
4091 ScMatrixRef pMat = GetMatrix();
4092 if (pMat)
4093 {
4094 const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal);
4095 SCSIZE nC, nR;
4096 pMat->GetDimensions(nC, nR);
4097 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
4098 {
4099 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
4100 {
4101 if (!pMat->IsStringOrEmpty(nMatCol,nMatRow))
4102 {
4103 fVal= pMat->GetDouble(nMatCol,nMatRow);
4104 if (nGlobalError == FormulaError::NONE)
4105 {
4106 values.push_back(fVal);
4107 fSum += fVal;
4108 }
4109 else if (bIgnoreErrVal)
4110 nGlobalError = FormulaError::NONE;
4111 }
4112 else if ( bTextAsZero )
4113 {
4114 values.push_back(0.0);
4115 }
4116 }
4117 }
4118 }
4119 }
4120 break;
4121 case svString :
4122 {
4123 Pop();
4124 if ( bTextAsZero )
4125 {
4126 values.push_back(0.0);
4127 }
4128 else
4129 SetError(FormulaError::IllegalParameter);
4130 }
4131 break;
4132 default :
4133 PopError();
4134 SetError(FormulaError::IllegalParameter);
4135 }
4136 }
4137
4138 if (!vArrayValues.empty())
4139 {
4140 // Include value of last non-references-array type and calculate final result.
4141 if (!values.empty())
4142 {
4143 for (auto & it : vArrayValues)
4144 {
4145 it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
4146 it.mfSum += fSum;
4147 }
4148 }
4149 ScMatrixRef xResMat = GetNewMat( 1, nMatRows, true);
4150 for (SCSIZE r=0; r < nMatRows; ++r)
4151 {
4152 ::std::vector<double>::size_type n = vArrayValues[r].mvValues.size();
4153 if (!n)
4154 xResMat->PutError( FormulaError::DivisionByZero, 0, r);
4155 else
4156 {
4157 ArrayRefListValue& rArrayValue = vArrayValues[r];
4158 double vSum = 0.0;
4159 const double vMean = rArrayValue.get() / n;
4160 for (::std::vector<double>::size_type i = 0; i < n; i++)
4161 vSum += ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean) *
4162 ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean);
4163 xResMat->PutDouble( VarResult( vSum, n), 0, r);
4164 }
4165 }
4166 PushMatrix( xResMat);
4167 }
4168 else
4169 {
4170 ::std::vector<double>::size_type n = values.size();
4171 if (!n)
4172 SetError( FormulaError::DivisionByZero);
4173 double vSum = 0.0;
4174 if (nGlobalError == FormulaError::NONE)
4175 {
4176 const double vMean = fSum.get() / n;
4177 for (::std::vector<double>::size_type i = 0; i < n; i++)
4178 vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean);
4179 }
4180 PushDouble( VarResult( vSum, n));
4181 }
4182}
4183
4184void ScInterpreter::ScVar( bool bTextAsZero )
4185{
4186 auto VarResult = []( double fVal, size_t nValCount )
4187 {
4188 if (nValCount <= 1)
4189 return CreateDoubleError( FormulaError::DivisionByZero );
4190 else
4191 return fVal / (nValCount - 1);
4192 };
4193 GetStVarParams( bTextAsZero, VarResult );
4194}
4195
4196void ScInterpreter::ScVarP( bool bTextAsZero )
4197{
4198 auto VarResult = []( double fVal, size_t nValCount )
4199 {
4200 return sc::div( fVal, nValCount);
4201 };
4202 GetStVarParams( bTextAsZero, VarResult );
4203
4204}
4205
4206void ScInterpreter::ScStDev( bool bTextAsZero )
4207{
4208 auto VarResult = []( double fVal, size_t nValCount )
4209 {
4210 if (nValCount <= 1)
4211 return CreateDoubleError( FormulaError::DivisionByZero );
4212 else
4213 return sqrt( fVal / (nValCount - 1));
4214 };
4215 GetStVarParams( bTextAsZero, VarResult );
4216}
4217
4218void ScInterpreter::ScStDevP( bool bTextAsZero )
4219{
4220 auto VarResult = []( double fVal, size_t nValCount )
4221 {
4222 if (nValCount == 0)
4223 return CreateDoubleError( FormulaError::DivisionByZero );
4224 else
4225 return sqrt( fVal / nValCount);
4226 };
4227 GetStVarParams( bTextAsZero, VarResult );
4228
4229 /* this was: PushDouble( sqrt( div( nVal, nValCount)));
4230 *
4231 * Besides that the special NAN gets lost in the call through sqrt(),
4232 * unxlngi6.pro then looped back and forth somewhere between div() and
4233 * ::rtl::math::setNan(). Tests showed that
4234 *
4235 * sqrt( div( 1, 0));
4236 *
4237 * produced a loop, but
4238 *
4239 * double f1 = div( 1, 0);
4240 * sqrt( f1 );
4241 *
4242 * was fine. There seems to be some compiler optimization problem. It does
4243 * not occur when compiled with debug=t.
4244 */
4245}
4246
4248{
4249 sal_uInt8 nParamCount = GetByte();
4250 sal_uLong nVal = 0;
4251 SCCOL nCol1;
4252 SCROW nRow1;
4253 SCTAB nTab1;
4254 SCCOL nCol2;
4255 SCROW nRow2;
4256 SCTAB nTab2;
4257 while (nParamCount-- > 0)
4258 {
4259 switch ( GetStackType() )
4260 {
4261 case svSingleRef:
4262 PopError();
4263 nVal++;
4264 break;
4265 case svDoubleRef:
4266 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4267 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4268 static_cast<sal_uLong>(nCol2 - nCol1 + 1);
4269 break;
4270 case svMatrix:
4271 {
4272 ScMatrixRef pMat = PopMatrix();
4273 if (pMat)
4274 {
4275 SCSIZE nC, nR;
4276 pMat->GetDimensions(nC, nR);
4277 nVal += nC;
4278 }
4279 }
4280 break;
4282 PopError();
4283 nVal++;
4284 break;
4286 {
4287 sal_uInt16 nFileId;
4288 OUString aTabName;
4289 ScComplexRefData aRef;
4290 PopExternalDoubleRef( nFileId, aTabName, aRef);
4291 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4292 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4293 static_cast<sal_uLong>(aAbs.aEnd.Col() - aAbs.aStart.Col() + 1);
4294 }
4295 break;
4296 default:
4297 PopError();
4298 SetError(FormulaError::IllegalParameter);
4299 }
4300 }
4301 PushDouble(static_cast<double>(nVal));
4302}
4303
4305{
4306 sal_uInt8 nParamCount = GetByte();
4307 sal_uLong nVal = 0;
4308 SCCOL nCol1;
4309 SCROW nRow1;
4310 SCTAB nTab1;
4311 SCCOL nCol2;
4312 SCROW nRow2;
4313 SCTAB nTab2;
4314 while (nParamCount-- > 0)
4315 {
4316 switch ( GetStackType() )
4317 {
4318 case svSingleRef:
4319 PopError();
4320 nVal++;
4321 break;
4322 case svDoubleRef:
4323 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4324 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4325 static_cast<sal_uLong>(nRow2 - nRow1 + 1);
4326 break;
4327 case svMatrix:
4328 {
4329 ScMatrixRef pMat = PopMatrix();
4330 if (pMat)
4331 {
4332 SCSIZE nC, nR;
4333 pMat->GetDimensions(nC, nR);
4334 nVal += nR;
4335 }
4336 }
4337 break;
4339 PopError();
4340 nVal++;
4341 break;
4343 {
4344 sal_uInt16 nFileId;
4345 OUString aTabName;
4346 ScComplexRefData aRef;
4347 PopExternalDoubleRef( nFileId, aTabName, aRef);
4348 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4349 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4350 static_cast<sal_uLong>(aAbs.aEnd.Row() - aAbs.aStart.Row() + 1);
4351 }
4352 break;
4353 default:
4354 PopError();
4355 SetError(FormulaError::IllegalParameter);
4356 }
4357 }
4358 PushDouble(static_cast<double>(nVal));
4359}
4360
4362{
4363 sal_uInt8 nParamCount = GetByte();
4364 sal_uLong nVal;
4365 if ( nParamCount == 0 )
4366 nVal = mrDoc.GetTableCount();
4367 else
4368 {
4369 nVal = 0;
4370 SCCOL nCol1;
4371 SCROW nRow1;
4372 SCTAB nTab1;
4373 SCCOL nCol2;
4374 SCROW nRow2;
4375 SCTAB nTab2;
4376 while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
4377 {
4378 switch ( GetStackType() )
4379 {
4380 case svSingleRef:
4382 PopError();
4383 nVal++;
4384 break;
4385 case svDoubleRef:
4386 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4387 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1);
4388 break;
4390 {
4391 sal_uInt16 nFileId;
4392 OUString aTabName;
4393 ScComplexRefData aRef;
4394 PopExternalDoubleRef( nFileId, aTabName, aRef);
4395 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4396 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1);
4397 }
4398 break;
4399 default:
4400 PopError();
4401 SetError( FormulaError::IllegalParameter );
4402 }
4403 }
4404 }
4405 PushDouble( static_cast<double>(nVal) );
4406}
4407
4409{
4410 sal_uInt8 nParamCount = GetByte();
4411 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4412 return;
4413
4414 double nVal = 0.0;
4415 if (nParamCount == 0)
4416 {
4417 nVal = aPos.Col() + 1;
4418 if (bMatrixFormula)
4419 {
4420 SCCOL nCols = 0;
4421 SCROW nRows = 0;
4422 if (pMyFormulaCell)
4423 pMyFormulaCell->GetMatColsRows( nCols, nRows);
4424 if (nCols == 0)
4425 {
4426 // Happens if called via ScViewFunc::EnterMatrix()
4427 // ScFormulaCell::GetResultDimensions() as of course a
4428 // matrix result is not available yet.
4429 nCols = 1;
4430 }
4431 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1, /*bEmpty*/true );
4432 if (pResMat)
4433 {
4434 for (SCCOL i=0; i < nCols; ++i)
4435 pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0);
4436 PushMatrix( pResMat);
4437 return;
4438 }
4439 }
4440 }
4441 else
4442 {
4443 switch ( GetStackType() )
4444 {
4445 case svSingleRef :
4446 {
4447 SCCOL nCol1(0);
4448 SCROW nRow1(0);
4449 SCTAB nTab1(0);
4450 PopSingleRef( nCol1, nRow1, nTab1 );
4451 nVal = static_cast<double>(nCol1 + 1);
4452 }
4453 break;
4454 case svExternalSingleRef :
4455 {
4456 sal_uInt16 nFileId;
4457 OUString aTabName;
4458 ScSingleRefData aRef;
4459 PopExternalSingleRef( nFileId, aTabName, aRef );
4460 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4461 nVal = static_cast<double>( aAbsRef.Col() + 1 );
4462 }
4463 break;
4464
4465 case svDoubleRef :
4466 case svExternalDoubleRef :
4467 {
4468 SCCOL nCol1;
4469 SCCOL nCol2;
4470 if ( GetStackType() == svDoubleRef )
4471 {
4472 SCROW nRow1;
4473 SCTAB nTab1;
4474 SCROW nRow2;
4475 SCTAB nTab2;
4476 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4477 }
4478 else
4479 {
4480 sal_uInt16 nFileId;
4481 OUString aTabName;
4482 ScComplexRefData aRef;
4483 PopExternalDoubleRef( nFileId, aTabName, aRef );
4484 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4485 nCol1 = aAbs.aStart.Col();
4486 nCol2 = aAbs.aEnd.Col();
4487 }
4488 if (nCol2 > nCol1)
4489 {
4490 ScMatrixRef pResMat = GetNewMat(
4491 static_cast<SCSIZE>(nCol2-nCol1+1), 1, /*bEmpty*/true);
4492 if (pResMat)
4493 {
4494 for (SCCOL i = nCol1; i <= nCol2; i++)
4495 pResMat->PutDouble(static_cast<double>(i+1),
4496 static_cast<SCSIZE>(i-nCol1), 0);
4497 PushMatrix(pResMat);
4498 return;
4499 }
4500 }
4501 else
4502 nVal = static_cast<double>(nCol1 + 1);
4503 }
4504 break;
4505 default:
4506 SetError( FormulaError::IllegalParameter );
4507 }
4508 }
4509 PushDouble( nVal );
4510}
4511
4513{
4514 sal_uInt8 nParamCount = GetByte();
4515 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4516 return;
4517
4518 double nVal = 0.0;
4519 if (nParamCount == 0)
4520 {
4521 nVal = aPos.Row() + 1;
4522 if (bMatrixFormula)
4523 {
4524 SCCOL nCols = 0;
4525 SCROW nRows = 0;
4526 if (pMyFormulaCell)
4527 pMyFormulaCell->GetMatColsRows( nCols, nRows);
4528 if (nRows == 0)
4529 {
4530 // Happens if called via ScViewFunc::EnterMatrix()
4531 // ScFormulaCell::GetResultDimensions() as of course a
4532 // matrix result is not available yet.
4533 nRows = 1;
4534 }
4535 ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows), /*bEmpty*/true);
4536 if (pResMat)
4537 {
4538 for (SCROW i=0; i < nRows; i++)
4539 pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i));
4540 PushMatrix( pResMat);
4541 return;
4542 }
4543 }
4544 }
4545 else
4546 {
4547 switch ( GetStackType() )
4548 {
4549 case svSingleRef :
4550 {
4551 SCCOL nCol1(0);
4552 SCROW nRow1(0);
4553 SCTAB nTab1(0);
4554 PopSingleRef( nCol1, nRow1, nTab1 );
4555 nVal = static_cast<double>(nRow1 + 1);
4556 }
4557 break;
4558 case svExternalSingleRef :
4559 {
4560 sal_uInt16 nFileId;
4561 OUString aTabName;
4562 ScSingleRefData aRef;
4563 PopExternalSingleRef( nFileId, aTabName, aRef );
4564 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4565 nVal = static_cast<double>( aAbsRef.Row() + 1 );
4566 }
4567 break;
4568 case svDoubleRef :
4569 case svExternalDoubleRef :
4570 {
4571 SCROW nRow1;
4572 SCROW nRow2;
4573 if ( GetStackType() == svDoubleRef )
4574 {
4575 SCCOL nCol1;
4576 SCTAB nTab1;
4577 SCCOL nCol2;
4578 SCTAB nTab2;
4579 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4580 }
4581 else
4582 {
4583 sal_uInt16 nFileId;
4584 OUString aTabName;
4585 ScComplexRefData aRef;
4586 PopExternalDoubleRef( nFileId, aTabName, aRef );
4587 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4588 nRow1 = aAbs.aStart.Row();
4589 nRow2 = aAbs.aEnd.Row();
4590 }
4591 if (nRow2 > nRow1)
4592 {
4593 ScMatrixRef pResMat = GetNewMat( 1,
4594 static_cast<SCSIZE>(nRow2-nRow1+1), /*bEmpty*/true);
4595 if (pResMat)
4596 {
4597 for (SCROW i = nRow1; i <= nRow2; i++)
4598 pResMat->PutDouble(static_cast<double>(i+1), 0,
4599 static_cast<SCSIZE>(i-nRow1));
4600 PushMatrix(pResMat);
4601 return;
4602 }
4603 }
4604 else
4605 nVal = static_cast<double>(nRow1 + 1);
4606 }
4607 break;
4608 default:
4609 SetError( FormulaError::IllegalParameter );
4610 }
4611 }
4612 PushDouble( nVal );
4613}
4614
4616{
4617 sal_uInt8 nParamCount = GetByte();
4618 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4619 return;
4620
4621 SCTAB nVal = 0;
4622 if ( nParamCount == 0 )
4623 nVal = aPos.Tab() + 1;
4624 else
4625 {
4626 switch ( GetStackType() )
4627 {
4628 case svString :
4629 {
4631 if ( mrDoc.GetTable(aStr.getString(), nVal))
4632 ++nVal;
4633 else
4634 SetError( FormulaError::IllegalArgument );
4635 }
4636 break;
4637 case svSingleRef :
4638 {
4639 SCCOL nCol1(0);
4640 SCROW nRow1(0);
4641 SCTAB nTab1(0);
4642 PopSingleRef(nCol1, nRow1, nTab1);
4643 nVal = nTab1 + 1;
4644 }
4645 break;
4646 case svDoubleRef :
4647 {
4648 SCCOL nCol1;
4649 SCROW nRow1;
4650 SCTAB nTab1;
4651 SCCOL nCol2;
4652 SCROW nRow2;
4653 SCTAB nTab2;
4654 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4655 nVal = nTab1 + 1;
4656 }
4657 break;
4658 default:
4659 SetError( FormulaError::IllegalParameter );
4660 }
4661 if ( nGlobalError != FormulaError::NONE )
4662 nVal = 0;
4663 }
4664 PushDouble( static_cast<double>(nVal) );
4665}
4666
4667namespace {
4668
4669class VectorMatrixAccessor
4670{
4671public:
4672 VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) :
4673 mrMat(rMat), mbColVec(bColVec) {}
4674
4675 bool IsEmpty(SCSIZE i) const
4676 {
4677 return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0);
4678 }
4679
4680 bool IsEmptyPath(SCSIZE i) const
4681 {
4682 return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0);
4683 }
4684
4685 bool IsValue(SCSIZE i) const
4686 {
4687 return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0);
4688 }
4689
4690 bool IsStringOrEmpty(SCSIZE i) const
4691 {
4692 return mbColVec ? mrMat.IsStringOrEmpty(0, i) : mrMat.IsStringOrEmpty(i, 0);
4693 }
4694
4695 double GetDouble(SCSIZE i) const
4696 {
4697 return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0);
4698 }
4699
4700 OUString GetString(SCSIZE i) const
4701 {
4702 return mbColVec ? mrMat.GetString(0, i).getString() : mrMat.GetString(i, 0).getString();
4703 }
4704
4705 SCSIZE GetElementCount() const
4706 {
4707 SCSIZE nC, nR;
4708 mrMat.GetDimensions(nC, nR);
4709 return mbColVec ? nR : nC;
4710 }
4711
4712private:
4713 const ScMatrix& mrMat;
4714 bool mbColVec;
4715};
4716
4720sal_Int32 lcl_CompareMatrix2Query(
4721 SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryEntry& rEntry)
4722{
4723 if (rMat.IsEmpty(i))
4724 {
4725 /* TODO: in case we introduced query for real empty this would have to
4726 * be changed! */
4727 return -1; // empty always less than anything else
4728 }
4729
4730 /* FIXME: what is an empty path (result of IF(false;true_path) in
4731 * comparisons? */
4732
4733 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
4734 if (rMat.IsValue(i))
4735 {
4736 const double nVal1 = rMat.GetDouble(i);
4737 if (!std::isfinite(nVal1))
4738 {
4739 // XXX Querying for error values is not required, otherwise we'd
4740 // need to check here.
4741 return 1; // error always greater than numeric or string
4742 }
4743
4744 if (bByString)
4745 return -1; // numeric always less than string
4746
4747 const double nVal2 = rEntry.GetQueryItem().mfVal;
4748 // XXX Querying for error values is not required, otherwise we'd need
4749 // to check here and move that check before the bByString check.
4750 if (nVal1 == nVal2)
4751 return 0;
4752
4753 return nVal1 < nVal2 ? -1 : 1;
4754 }
4755
4756 if (!bByString)
4757 return 1; // string always greater than numeric
4758
4759 OUString aStr1 = rMat.GetString(i);
4760 OUString aStr2 = rEntry.GetQueryItem().maString.getString();
4761
4762 return ScGlobal::GetCollator().compareString(aStr1, aStr2); // case-insensitive
4763}
4764
4767void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat,
4768 SCSIZE nMatCount)
4769{
4770 if (rMat.IsValue(rIndex))
4771 {
4772 double nVal = rMat.GetDouble(rIndex);
4773 while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) &&
4774 nVal == rMat.GetDouble(rIndex+1))
4775 ++rIndex;
4776 }
4777 // Order of IsEmptyPath, IsEmpty, IsStringOrEmpty is significant!
4778 else if (rMat.IsEmptyPath(rIndex))
4779 {
4780 while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1))
4781 ++rIndex;
4782 }
4783 else if (rMat.IsEmpty(rIndex))
4784 {
4785 while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1))
4786 ++rIndex;
4787 }
4788 else if (rMat.IsStringOrEmpty(rIndex))
4789 {
4790 OUString aStr( rMat.GetString(rIndex));
4791 while (rIndex < nMatCount-1 && rMat.IsStringOrEmpty(rIndex+1) &&
4792 aStr == rMat.GetString(rIndex+1))
4793 ++rIndex;
4794 }
4795 else
4796 {
4797 OSL_FAIL("lcl_GetLastMatch: unhandled matrix type");
4798 }
4799}
4800
4801}
4802
4804{
4805
4806 sal_uInt8 nParamCount = GetByte();
4807 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
4808 return;
4809
4810 double fTyp;
4811 if (nParamCount == 3)
4812 fTyp = GetDouble();
4813 else
4814 fTyp = 1.0;
4815 SCCOL nCol1 = 0;
4816 SCROW nRow1 = 0;
4817 SCTAB nTab1 = 0;
4818 SCCOL nCol2 = 0;
4819 SCROW nRow2 = 0;
4820 ScMatrixRef pMatSrc = nullptr;
4821
4822 switch (GetStackType())
4823 {
4824 case svSingleRef:
4825 PopSingleRef( nCol1, nRow1, nTab1);
4826 nCol2 = nCol1;
4827 nRow2 = nRow1;
4828 break;
4829 case svDoubleRef:
4830 {
4831 SCTAB nTab2 = 0;
4832 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4833 if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2))
4834 {
4836 return;
4837 }
4838 }
4839 break;
4840 case svMatrix:
4842 {
4843 if (GetStackType() == svMatrix)
4844 pMatSrc = PopMatrix();
4845 else
4846 PopExternalDoubleRef(pMatSrc);
4847
4848 if (!pMatSrc)
4849 {
4851 return;
4852 }
4853 }
4854 break;
4855 default:
4857 return;
4858 }
4859
4860 if (nGlobalError == FormulaError::NONE)
4861 {
4862 double fVal;
4863 ScQueryParam rParam;
4864 rParam.nCol1 = nCol1;
4865 rParam.nRow1 = nRow1;
4866 rParam.nCol2 = nCol2;
4867 rParam.nTab = nTab1;
4868 const ScComplexRefData* refData = nullptr;
4869
4870 ScQueryEntry& rEntry = rParam.GetEntry(0);
4871 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
4872 rEntry.bDoQuery = true;
4873 if (fTyp < 0.0)
4874 rEntry.eOp = SC_GREATER_EQUAL;
4875 else if (fTyp > 0.0)
4876 rEntry.eOp = SC_LESS_EQUAL;
4877 switch ( GetStackType() )
4878 {
4879 case svDouble:
4880 {
4881 fVal = GetDouble();
4882 rItem.mfVal = fVal;
4884 }
4885 break;
4886 case svString:
4887 {
4889 rItem.maString = GetString();
4890 }
4891 break;
4892 case svDoubleRef :
4893 refData = GetStackDoubleRef();
4894 [[fallthrough]];
4895 case svSingleRef :
4896 {
4897 ScAddress aAdr;
4898 if ( !PopDoubleRefOrSingleRef( aAdr ) )
4899 {
4900 PushInt(0);
4901 return ;
4902 }
4903 ScRefCellValue aCell(mrDoc, aAdr);
4904 if (aCell.hasNumeric())
4905 {
4906 fVal = GetCellValue(aAdr, aCell);
4908 rItem.mfVal = fVal;
4909 }
4910 else
4911 {
4912 GetCellString(rItem.maString, aCell);
4914 }
4915 }
4916 break;
4918 {
4920 PopExternalSingleRef(pToken);
4921 if (nGlobalError != FormulaError::NONE)
4922 {
4924 return;
4925 }
4926 if (pToken->GetType() == svDouble)
4927 {
4929 rItem.mfVal = pToken->GetDouble();
4930 }
4931 else
4932 {
4934 rItem.maString = pToken->GetString();
4935 }
4936 }
4937 break;
4939 case svMatrix :
4940 {
4943 rItem.mfVal, aStr);
4944 rItem.maString = aStr;
4947 }
4948 break;
4949 default:
4950 {
4952 return;
4953 }
4954 }
4955 if (rItem.meType == ScQueryEntry::ByString)
4956 {
4957 bool bIsVBAMode = mrDoc.IsInVBAMode();
4958
4959 if ( bIsVBAMode )
4961 else
4963 }
4964
4965 if (pMatSrc) // The source data is matrix array.
4966 {
4967 SCSIZE nC, nR;
4968 pMatSrc->GetDimensions( nC, nR);
4969 if (nC > 1 && nR > 1)
4970 {
4971 // The source matrix must be a vector.
4973 return;
4974 }
4975
4976 // Do not propagate errors from matrix while searching.
4977 pMatSrc->SetErrorInterpreter( nullptr);
4978
4979 SCSIZE nMatCount = (nC == 1) ? nR : nC;
4980 VectorMatrixAccessor aMatAcc(*pMatSrc, nC == 1);
4981
4982 // simple serial search for equality mode (source data doesn't
4983 // need to be sorted).
4984
4985 if (rEntry.eOp == SC_EQUAL)
4986 {
4987 for (SCSIZE i = 0; i < nMatCount; ++i)
4988 {
4989 if (lcl_CompareMatrix2Query( i, aMatAcc, rEntry) == 0)
4990 {
4991 PushDouble(i+1); // found !
4992 return;
4993 }
4994 }
4995 PushNA(); // not found
4996 return;
4997 }
4998
4999 // binary search for non-equality mode (the source data is
5000 // assumed to be sorted).
5001
5002 bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL);
5003 SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0;
5004 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
5005 {
5006 SCSIZE nMid = nFirst + nLen/2;
5007 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rEntry);
5008 if (nCmp == 0)
5009 {
5010 // exact match. find the last item with the same value.
5011 lcl_GetLastMatch( nMid, aMatAcc, nMatCount);
5012 PushDouble( nMid+1);
5013 return;
5014 }
5015
5016 if (nLen == 1) // first and last items are next to each other.
5017 {
5018 if (nCmp < 0)
5019 nHitIndex = bAscOrder ? nLast : nFirst;
5020 else
5021 nHitIndex = bAscOrder ? nFirst : nLast;
5022 break;
5023 }
5024
5025 if (nCmp < 0)
5026 {
5027 if (bAscOrder)
5028 nFirst = nMid;
5029 else
5030 nLast = nMid;
5031 }
5032 else
5033 {
5034 if (bAscOrder)
5035 nLast = nMid;
5036 else
5037 nFirst = nMid;
5038 }
5039 }
5040
5041 if (nHitIndex == nMatCount-1) // last item
5042 {
5043 sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, aMatAcc, rEntry);
5044 if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0))
5045 {
5046 // either the last item is an exact match or the real
5047 // hit is beyond the last item.
5048 PushDouble( nHitIndex+1);
5049 return;
5050 }
5051 }
5052
5053 if (nHitIndex > 0) // valid hit must be 2nd item or higher
5054 {
5055 if ( ! ( rItem.meType == ScQueryEntry::ByString && aMatAcc.IsValue( nHitIndex-1 ) ) &&
5056 ! ( rItem.meType == ScQueryEntry::ByValue && !aMatAcc.IsValue( nHitIndex-1 ) ) )
5057 PushDouble( nHitIndex); // non-exact match
5058 else
5059 PushNA();
5060 return;
5061 }
5062
5063 PushNA();
5064 return;
5065 }
5066
5067 // The source data is cell range.
5068 SCCOLROW nDelta = 0;
5069 if (nCol1 == nCol2)
5070 { // search row in column
5071 rParam.nRow2 = nRow2;
5072 rEntry.nField = nCol1;
5073 ScAddress aResultPos( nCol1, nRow1, nTab1);
5074 if (!LookupQueryWithCache( aResultPos, rParam, refData))
5075 {
5076 PushNA();
5077 return;
5078 }
5079 nDelta = aResultPos.Row() - nRow1;
5080 }
5081 else
5082 { // search column in row
5083 SCCOL nC;
5084 rParam.bByRow = false;
5085 rParam.nRow2 = nRow1;
5086 rEntry.nField = nCol1;
5087 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false);
5088 // Advance Entry.nField in Iterator if column changed
5089 aCellIter.SetAdvanceQueryParamEntryField( true );
5090 if (fTyp == 0.0)
5091 { // EQUAL
5092 if ( aCellIter.GetFirst() )
5093 nC = aCellIter.GetCol();
5094 else
5095 {
5096 PushNA();
5097 return;
5098 }
5099 }
5100 else
5101 { // <= or >=
5102 SCROW nR;
5103 if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) )
5104 {
5105 PushNA();
5106 return;
5107 }
5108 }
5109 nDelta = nC - nCol1;
5110 }
5111 PushDouble(static_cast<double>(nDelta + 1));
5112 }
5113 else
5115}
5116
5117namespace {
5118
5119bool isCellContentEmpty( const ScRefCellValue& rCell )
5120{
5121 switch (rCell.getType())
5122 {
5123 case CELLTYPE_VALUE:
5124 case CELLTYPE_STRING:
5125 case CELLTYPE_EDIT:
5126 return false;
5127 case CELLTYPE_FORMULA:
5128 {
5129 // NOTE: Excel treats ="" in a referenced cell as blank in
5130 // COUNTBLANK() but not in ISBLANK(), which is inconsistent.
5131 // COUNTBLANK() tests the (display) result whereas ISBLANK() tests
5132 // the cell content.
5133 // ODFF allows both for COUNTBLANK().
5134 // OOo and LibreOffice prior to 4.4 did not treat ="" as blank in
5135 // COUNTBLANK(), we now do for Excel interoperability.
5136 /* TODO: introduce yet another compatibility option? */
5139 return false;
5140 if (!aRes.maString.isEmpty())
5141 return false;
5142 }
5143 break;
5144 default:
5145 ;
5146 }
5147
5148 return true;
5149}
5150
5151}
5152
5154{
5155 if ( !MustHaveParamCount( GetByte(), 1 ) )
5156 return;
5157
5158 const SCSIZE nMatRows = GetRefListArrayMaxSize(1);
5159 // There's either one RefList and nothing else, or none.
5160 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
5161 sal_uLong nMaxCount = 0, nCount = 0;
5162 switch (GetStackType())
5163 {
5164 case svSingleRef :
5165 {
5166 nMaxCount = 1;
5167 ScAddress aAdr;
5168 PopSingleRef( aAdr );
5169 ScRefCellValue aCell(mrDoc, aAdr);
5170 if (!isCellContentEmpty(aCell))
5171 nCount = 1;
5172 }
5173 break;
5174 case svRefList :
5175 case svDoubleRef :
5176 {
5177 ScRange aRange;
5178 short nParam = 1;
5179 SCSIZE nRefListArrayPos = 0;
5180 size_t nRefInList = 0;
5181 while (nParam-- > 0)
5182 {
5183 nRefListArrayPos = nRefInList;
5184 PopDoubleRef( aRange, nParam, nRefInList);
5185 nMaxCount +=
5186 static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) *
5187 static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) *
5188 static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1);
5189
5190 ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags);
5191 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
5192 {
5193 const ScRefCellValue& rCell = aIter.getRefCellValue();
5194 if (!isCellContentEmpty(rCell))
5195 ++nCount;
5196 }
5197 if (xResMat)
5198 {
5199 xResMat->PutDouble( nMaxCount - nCount, 0, nRefListArrayPos);
5200 nMaxCount = nCount = 0;
5201 }
5202 }
5203 }
5204 break;
5205 case svMatrix:
5208 {
5209 ScMatrixRef xMat = GetMatrix();
5210 if (!xMat)
5211 SetError( FormulaError::IllegalParameter);
5212 else
5213 {
5214 SCSIZE nC, nR;
5215 xMat->GetDimensions( nC, nR);
5216 nMaxCount = nC * nR;
5217 // Numbers (implicit), strings and error values, ignore empty
5218 // strings as those if not entered in an inline array are the
5219 // result of a formula, to be par with a reference to formula
5220 // cell as *visual* blank, see isCellContentEmpty() above.
5221 nCount = xMat->Count( true, true, true);
5222 }
5223 }
5224 break;
5225 default : SetError(FormulaError::IllegalParameter); break;
5226 }
5227 if (xResMat)
5228 PushMatrix( xResMat);
5229 else
5230 PushDouble(nMaxCount - nCount);
5231}
5232
5234{
5235 sal_uInt8 nParamCount = GetByte();
5236 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
5237 return;
5238
5239 SCCOL nCol3 = 0;
5240 SCROW nRow3 = 0;
5241 SCTAB nTab3 = 0;
5242
5243 ScMatrixRef pSumExtraMatrix;
5244 bool bSumExtraRange = (nParamCount == 3);
5245 if (bSumExtraRange)
5246 {
5247 // Save only the upperleft cell in case of cell range. The geometry
5248 // of the 3rd parameter is taken from the 1st parameter.
5249
5250 switch ( GetStackType() )
5251 {
5252 case svDoubleRef :
5253 {
5254 SCCOL nColJunk = 0;
5255 SCROW nRowJunk = 0;
5256 SCTAB nTabJunk = 0;
5257 PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk );
5258 if ( nTabJunk != nTab3 )
5259 {
5260 PushError( FormulaError::IllegalParameter);
5261 return;
5262 }
5263 }
5264 break;
5265 case svSingleRef :
5266 PopSingleRef( nCol3, nRow3, nTab3 );
5267 break;
5268 case svMatrix:
5269 pSumExtraMatrix = PopMatrix();
5270 // nCol3, nRow3, nTab3 remain 0
5271 break;
5273 {
5274 pSumExtraMatrix = GetNewMat(1,1);
5276 PopExternalSingleRef(pToken);
5277 if (nGlobalError != FormulaError::NONE)
5278 {
5280 return;
5281 }
5282
5283 if (pToken->GetType() == svDouble)
5284 pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0);
5285 else
5286 pSumExtraMatrix->PutString(pToken->GetString(), 0, 0);