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>
24 #include <editeng/justifyitem.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <o3tl/temporary.hxx>
27 #include <osl/thread.h>
28 #include <unotools/textsearch.hxx>
29 #include <svl/zforlist.hxx>
30 #include <svl/zformat.hxx>
31 #include <tools/urlobj.hxx>
32 #include <unotools/charclass.hxx>
33 #include <sfx2/docfile.hxx>
34 #include <sfx2/printer.hxx>
37 #include <rtl/character.hxx>
38 #include <rtl/ustring.hxx>
39 #include <sal/log.hxx>
40 #include <osl/diagnose.h>
41 #include <unicode/uchar.h>
42 #include <unicode/regex.h>
43 #include <i18nlangtag/mslangid.hxx>
44 
45 #include <patattr.hxx>
46 #include <global.hxx>
47 #include <document.hxx>
48 #include <dociter.hxx>
49 #include <formulacell.hxx>
50 #include <scmatrix.hxx>
51 #include <docoptio.hxx>
52 #include <attrib.hxx>
53 #include <jumpmatrix.hxx>
54 #include <cellkeytranslator.hxx>
55 #include <lookupcache.hxx>
56 #include <rangenam.hxx>
57 #include <rangeutl.hxx>
58 #include <compiler.hxx>
59 #include <externalrefmgr.hxx>
60 #include <doubleref.hxx>
61 #include <queryparam.hxx>
62 #include <queryentry.hxx>
63 #include <tokenarray.hxx>
64 #include <compare.hxx>
65 
67 #include <comphelper/random.hxx>
68 #include <comphelper/string.hxx>
69 #include <svl/sharedstringpool.hxx>
70 
71 #include <stdlib.h>
72 #include <vector>
73 #include <memory>
74 #include <limits>
75 #include <string_view>
76 
77 const sal_uInt64 n2power48 = SAL_CONST_UINT64( 281474976710656); // 2^48
78 
80 
81 using namespace formula;
82 using ::std::unique_ptr;
83 
85 {
86  const short* pJump = pCur->GetJump();
87  short nJumpCount = pJump[ 0 ];
88  MatrixJumpConditionToMatrix();
89  switch ( GetStackType() )
90  {
91  case svMatrix:
92  {
93  ScMatrixRef pMat = PopMatrix();
94  if ( !pMat )
95  PushIllegalParameter();
96  else
97  {
99  ScTokenMatrixMap::const_iterator aMapIter;
100  // DoubleError handled by JumpMatrix
101  pMat->SetErrorInterpreter( nullptr);
102  SCSIZE nCols, nRows;
103  pMat->GetDimensions( nCols, nRows );
104  if ( nCols == 0 || nRows == 0 )
105  {
106  PushIllegalArgument();
107  return;
108  }
109  else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end()))
110  xNew = (*aMapIter).second;
111  else
112  {
113  std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
114  pCur->GetOpCode(), nCols, nRows));
115  for ( SCSIZE nC=0; nC < nCols; ++nC )
116  {
117  for ( SCSIZE nR=0; nR < nRows; ++nR )
118  {
119  double fVal;
120  bool bTrue;
121  bool bIsValue = pMat->IsValue(nC, nR);
122  if (bIsValue)
123  {
124  fVal = pMat->GetDouble(nC, nR);
125  bIsValue = std::isfinite(fVal);
126  bTrue = bIsValue && (fVal != 0.0);
127  if (bTrue)
128  fVal = 1.0;
129  }
130  else
131  {
132  // Treat empty and empty path as 0, but string
133  // as error. ScMatrix::IsValueOrEmpty() returns
134  // true for any empty, empty path, empty cell,
135  // empty result.
136  bIsValue = pMat->IsValueOrEmpty(nC, nR);
137  bTrue = false;
138  fVal = (bIsValue ? 0.0 : CreateDoubleError( FormulaError::NoValue));
139  }
140  if ( bTrue )
141  { // TRUE
142  if( nJumpCount >= 2 )
143  { // THEN path
144  pJumpMat->SetJump( nC, nR, fVal,
145  pJump[ 1 ],
146  pJump[ nJumpCount ]);
147  }
148  else
149  { // no parameter given for THEN
150  pJumpMat->SetJump( nC, nR, fVal,
151  pJump[ nJumpCount ],
152  pJump[ nJumpCount ]);
153  }
154  }
155  else
156  { // FALSE
157  if( nJumpCount == 3 && bIsValue )
158  { // ELSE path
159  pJumpMat->SetJump( nC, nR, fVal,
160  pJump[ 2 ],
161  pJump[ nJumpCount ]);
162  }
163  else
164  { // no parameter given for ELSE,
165  // or DoubleError
166  pJumpMat->SetJump( nC, nR, fVal,
167  pJump[ nJumpCount ],
168  pJump[ nJumpCount ]);
169  }
170  }
171  }
172  }
173  xNew = new ScJumpMatrixToken( pJumpMat );
174  GetTokenMatrixMap().emplace(pCur, xNew);
175  }
176  if (!xNew)
177  {
178  PushIllegalArgument();
179  return;
180  }
181  PushTokenRef( xNew);
182  // set endpoint of path for main code line
183  aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
184  }
185  }
186  break;
187  default:
188  {
189  if ( GetBool() )
190  { // TRUE
191  if( nJumpCount >= 2 )
192  { // THEN path
193  aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
194  }
195  else
196  { // no parameter given for THEN
197  nFuncFmtType = SvNumFormatType::LOGICAL;
198  PushInt(1);
199  aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
200  }
201  }
202  else
203  { // FALSE
204  if( nJumpCount == 3 )
205  { // ELSE path
206  aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] );
207  }
208  else
209  { // no parameter given for ELSE
210  nFuncFmtType = SvNumFormatType::LOGICAL;
211  PushInt(0);
212  aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
213  }
214  }
215  }
216  }
217 }
218 
223  const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR )
224 {
225  if ( pMat->IsValue( nC, nR ) )
226  {
227  double fVal = pMat->GetDouble( nC, nR );
228  pJumpMat->PutResultDouble( fVal, nC, nR );
229  }
230  else if ( pMat->IsEmpty( nC, nR ) )
231  {
232  pJumpMat->PutResultEmpty( nC, nR );
233  }
234  else
235  {
236  pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR);
237  }
238 }
239 
240 void ScInterpreter::ScIfError( bool bNAonly )
241 {
242  const short* pJump = pCur->GetJump();
243  short nJumpCount = pJump[ 0 ];
244  if (!sp || nJumpCount != 2)
245  {
246  // Reset nGlobalError here to not propagate the old error, if any.
247  nGlobalError = (sp ? FormulaError::ParameterExpected : FormulaError::UnknownStackVariable);
248  PushError( nGlobalError);
249  aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
250  return;
251  }
252 
253  FormulaConstTokenRef xToken( pStack[ sp - 1 ] );
254  bool bError = false;
255  FormulaError nOldGlobalError = nGlobalError;
256  nGlobalError = FormulaError::NONE;
257 
258  MatrixJumpConditionToMatrix();
259  switch (GetStackType())
260  {
261  default:
262  Pop();
263  // Act on implicitly propagated error, if any.
264  if (nOldGlobalError != FormulaError::NONE)
265  nGlobalError = nOldGlobalError;
266  if (nGlobalError != FormulaError::NONE)
267  bError = true;
268  break;
269  case svError:
270  PopError();
271  bError = true;
272  break;
273  case svDoubleRef:
274  case svSingleRef:
275  {
276  ScAddress aAdr;
277  if (!PopDoubleRefOrSingleRef( aAdr))
278  bError = true;
279  else
280  {
281 
282  ScRefCellValue aCell(mrDoc, aAdr);
283  nGlobalError = GetCellErrCode(aCell);
284  if (nGlobalError != FormulaError::NONE)
285  bError = true;
286  }
287  }
288  break;
289  case svExternalSingleRef:
290  case svExternalDoubleRef:
291  {
292  double fVal;
294  // Handles also existing jump matrix case and sets error on
295  // elements.
296  GetDoubleOrStringFromMatrix( fVal, aStr);
297  if (nGlobalError != FormulaError::NONE)
298  bError = true;
299  }
300  break;
301  case svMatrix:
302  {
303  const ScMatrixRef pMat = PopMatrix();
304  if (!pMat || (nGlobalError != FormulaError::NONE && (!bNAonly || nGlobalError == FormulaError::NotAvailable)))
305  {
306  bError = true;
307  break; // switch
308  }
309  // If the matrix has no queried error at all we can simply use
310  // it as result and don't need to bother with jump matrix.
311  SCSIZE nErrorCol = ::std::numeric_limits<SCSIZE>::max(),
312  nErrorRow = ::std::numeric_limits<SCSIZE>::max();
313  SCSIZE nCols, nRows;
314  pMat->GetDimensions( nCols, nRows );
315  if (nCols == 0 || nRows == 0)
316  {
317  bError = true;
318  break; // switch
319  }
320  for (SCSIZE nC=0; nC < nCols && !bError; ++nC)
321  {
322  for (SCSIZE nR=0; nR < nRows && !bError; ++nR)
323  {
324  FormulaError nErr = pMat->GetError( nC, nR );
325  if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
326  {
327  bError = true;
328  nErrorCol = nC;
329  nErrorRow = nR;
330  }
331  }
332  }
333  if (!bError)
334  break; // switch, we're done and have the result
335 
337  ScTokenMatrixMap::const_iterator aMapIter;
338  if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end()))
339  {
340  xNew = (*aMapIter).second;
341  }
342  else
343  {
344  const ScMatrix* pMatPtr = pMat.get();
345  std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
346  pCur->GetOpCode(), nCols, nRows));
347  // Init all jumps to no error to save single calls. Error
348  // is the exceptional condition.
349  const double fFlagResult = CreateDoubleError( FormulaError::JumpMatHasResult);
350  pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] );
351  // Up to first error position simply store results, no need
352  // to evaluate error conditions again.
353  SCSIZE nC = 0, nR = 0;
354  for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ )
355  {
356  for (nR = 0 ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR)
357  {
358  lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
359  }
360  if (nC != nErrorCol && nR != nErrorRow)
361  ++nC;
362  }
363  // Now the mixed cases.
364  for ( ; nC < nCols; ++nC)
365  {
366  for ( ; nR < nRows; ++nR)
367  {
368  FormulaError nErr = pMat->GetError( nC, nR );
369  if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
370  { // TRUE, THEN path
371  pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] );
372  }
373  else
374  { // FALSE, EMPTY path, store result instead
375  lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
376  }
377  }
378  nR = 0;
379  }
380  xNew = new ScJumpMatrixToken( pJumpMat );
381  GetTokenMatrixMap().emplace( pCur, xNew );
382  }
383  nGlobalError = nOldGlobalError;
384  PushTokenRef( xNew );
385  // set endpoint of path for main code line
386  aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
387  return;
388  }
389  break;
390  }
391 
392  if (bError && (!bNAonly || nGlobalError == FormulaError::NotAvailable))
393  {
394  // error, calculate 2nd argument
395  nGlobalError = FormulaError::NONE;
396  aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
397  }
398  else
399  {
400  // no error, push 1st argument and continue
401  nGlobalError = nOldGlobalError;
402  PushTokenRef( xToken);
403  aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
404  }
405 }
406 
408 {
409  // We have to set a jump, if there was none chosen because of an error set
410  // it to endpoint.
411  bool bHaveJump = false;
412  const short* pJump = pCur->GetJump();
413  short nJumpCount = pJump[ 0 ];
414  MatrixJumpConditionToMatrix();
415  switch ( GetStackType() )
416  {
417  case svMatrix:
418  {
419  ScMatrixRef pMat = PopMatrix();
420  if ( !pMat )
421  PushIllegalParameter();
422  else
423  {
425  ScTokenMatrixMap::const_iterator aMapIter;
426  // DoubleError handled by JumpMatrix
427  pMat->SetErrorInterpreter( nullptr);
428  SCSIZE nCols, nRows;
429  pMat->GetDimensions( nCols, nRows );
430  if ( nCols == 0 || nRows == 0 )
431  PushIllegalParameter();
432  else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find(
433  pCur)) != pTokenMatrixMap->end()))
434  xNew = (*aMapIter).second;
435  else
436  {
437  std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
438  pCur->GetOpCode(), nCols, nRows));
439  for ( SCSIZE nC=0; nC < nCols; ++nC )
440  {
441  for ( SCSIZE nR=0; nR < nRows; ++nR )
442  {
443  double fVal;
444  bool bIsValue = pMat->IsValue(nC, nR);
445  if ( bIsValue )
446  {
447  fVal = pMat->GetDouble(nC, nR);
448  bIsValue = std::isfinite( fVal );
449  if ( bIsValue )
450  {
451  fVal = ::rtl::math::approxFloor( fVal);
452  if ( (fVal < 1) || (fVal >= nJumpCount))
453  {
454  bIsValue = false;
455  fVal = CreateDoubleError(
456  FormulaError::IllegalArgument);
457  }
458  }
459  }
460  else
461  {
462  fVal = CreateDoubleError( FormulaError::NoValue);
463  }
464  if ( bIsValue )
465  {
466  pJumpMat->SetJump( nC, nR, fVal,
467  pJump[ static_cast<short>(fVal) ],
468  pJump[ nJumpCount ]);
469  }
470  else
471  {
472  pJumpMat->SetJump( nC, nR, fVal,
473  pJump[ nJumpCount ],
474  pJump[ nJumpCount ]);
475  }
476  }
477  }
478  xNew = new ScJumpMatrixToken( pJumpMat );
479  GetTokenMatrixMap().emplace(pCur, xNew);
480  }
481  if (xNew)
482  {
483  PushTokenRef( xNew);
484  // set endpoint of path for main code line
485  aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
486  bHaveJump = true;
487  }
488  }
489  }
490  break;
491  default:
492  {
493  sal_Int16 nJumpIndex = GetInt16();
494  if (nGlobalError == FormulaError::NONE && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount))
495  {
496  aCode.Jump( pJump[ static_cast<short>(nJumpIndex) ], pJump[ nJumpCount ] );
497  bHaveJump = true;
498  }
499  else
500  PushIllegalArgument();
501  }
502  }
503  if (!bHaveJump)
504  aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
505 }
506 
507 static void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, SCSIZE nParmCols, SCSIZE nParmRows )
508 {
509  SCSIZE nJumpCols, nJumpRows;
510  SCSIZE nResCols, nResRows;
511  SCSIZE nAdjustCols, nAdjustRows;
512  pJumpM->GetDimensions( nJumpCols, nJumpRows );
513  pJumpM->GetResMatDimensions( nResCols, nResRows );
514  if (!(( nJumpCols == 1 && nParmCols > nResCols ) ||
515  ( nJumpRows == 1 && nParmRows > nResRows )))
516  return;
517 
518  if ( nJumpCols == 1 && nJumpRows == 1 )
519  {
520  nAdjustCols = std::max(nParmCols, nResCols);
521  nAdjustRows = std::max(nParmRows, nResRows);
522  }
523  else if ( nJumpCols == 1 )
524  {
525  nAdjustCols = nParmCols;
526  nAdjustRows = nResRows;
527  }
528  else
529  {
530  nAdjustCols = nResCols;
531  nAdjustRows = nParmRows;
532  }
533  pJumpM->SetNewResMat( nAdjustCols, nAdjustRows );
534 }
535 
536 bool ScInterpreter::JumpMatrix( short nStackLevel )
537 {
538  pJumpMatrix = pStack[sp-nStackLevel]->GetJumpMatrix();
539  bool bHasResMat = pJumpMatrix->HasResultMatrix();
540  SCSIZE nC, nR;
541  if ( nStackLevel == 2 )
542  {
543  if ( aCode.HasStacked() )
544  aCode.Pop(); // pop what Jump() pushed
545  else
546  {
547  assert(!"pop goes the weasel");
548  }
549 
550  if ( !bHasResMat )
551  {
552  Pop();
553  SetError( FormulaError::UnknownStackVariable );
554  }
555  else
556  {
557  pJumpMatrix->GetPos( nC, nR );
558  switch ( GetStackType() )
559  {
560  case svDouble:
561  {
562  double fVal = GetDouble();
563  if ( nGlobalError != FormulaError::NONE )
564  {
565  fVal = CreateDoubleError( nGlobalError );
566  nGlobalError = FormulaError::NONE;
567  }
568  pJumpMatrix->PutResultDouble( fVal, nC, nR );
569  }
570  break;
571  case svString:
572  {
574  if ( nGlobalError != FormulaError::NONE )
575  {
576  pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
577  nC, nR);
578  nGlobalError = FormulaError::NONE;
579  }
580  else
581  pJumpMatrix->PutResultString(aStr, nC, nR);
582  }
583  break;
584  case svSingleRef:
585  {
586  FormulaConstTokenRef xRef = pStack[sp-1];
587  ScAddress aAdr;
588  PopSingleRef( aAdr );
589  if ( nGlobalError != FormulaError::NONE )
590  {
591  pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
592  nC, nR);
593  nGlobalError = FormulaError::NONE;
594  }
595  else
596  {
597  ScRefCellValue aCell(mrDoc, aAdr);
598  if (aCell.hasEmptyValue())
599  pJumpMatrix->PutResultEmpty( nC, nR );
600  else if (aCell.hasNumeric())
601  {
602  double fVal = GetCellValue(aAdr, aCell);
603  if ( nGlobalError != FormulaError::NONE )
604  {
605  fVal = CreateDoubleError(
606  nGlobalError);
607  nGlobalError = FormulaError::NONE;
608  }
609  pJumpMatrix->PutResultDouble( fVal, nC, nR );
610  }
611  else
612  {
614  GetCellString(aStr, aCell);
615  if ( nGlobalError != FormulaError::NONE )
616  {
617  pJumpMatrix->PutResultDouble( CreateDoubleError(
618  nGlobalError), nC, nR);
619  nGlobalError = FormulaError::NONE;
620  }
621  else
622  pJumpMatrix->PutResultString(aStr, nC, nR);
623  }
624  }
625 
627  if (eReturnType == ParamClass::Reference)
628  {
629  /* TODO: What about error handling and do we actually
630  * need the result matrix above at all in this case? */
631  ScComplexRefData aRef;
632  aRef.Ref1 = aRef.Ref2 = *(xRef->GetSingleRef());
633  pJumpMatrix->GetRefList().push_back( aRef);
634  }
635  }
636  break;
637  case svDoubleRef:
638  { // upper left plus offset within matrix
639  FormulaConstTokenRef xRef = pStack[sp-1];
640  double fVal;
641  ScRange aRange;
642  PopDoubleRef( aRange );
643  if ( nGlobalError != FormulaError::NONE )
644  {
645  fVal = CreateDoubleError( nGlobalError );
646  nGlobalError = FormulaError::NONE;
647  pJumpMatrix->PutResultDouble( fVal, nC, nR );
648  }
649  else
650  {
651  // Do not modify the original range because we use it
652  // to adjust the size of the result matrix if necessary.
653  ScAddress aAdr( aRange.aStart);
654  sal_uLong nCol = static_cast<sal_uLong>(aAdr.Col()) + nC;
655  sal_uLong nRow = static_cast<sal_uLong>(aAdr.Row()) + nR;
656  if ((nCol > o3tl::make_unsigned(aRange.aEnd.Col()) &&
657  aRange.aEnd.Col() != aRange.aStart.Col())
658  || (nRow > o3tl::make_unsigned(aRange.aEnd.Row()) &&
659  aRange.aEnd.Row() != aRange.aStart.Row()))
660  {
661  fVal = CreateDoubleError( FormulaError::NotAvailable );
662  pJumpMatrix->PutResultDouble( fVal, nC, nR );
663  }
664  else
665  {
666  // Replicate column and/or row of a vector if it is
667  // one. Note that this could be a range reference
668  // that in fact consists of only one cell, e.g. A1:A1
669  if (aRange.aEnd.Col() == aRange.aStart.Col())
670  nCol = aRange.aStart.Col();
671  if (aRange.aEnd.Row() == aRange.aStart.Row())
672  nRow = aRange.aStart.Row();
673  aAdr.SetCol( static_cast<SCCOL>(nCol) );
674  aAdr.SetRow( static_cast<SCROW>(nRow) );
675  ScRefCellValue aCell(mrDoc, aAdr);
676  if (aCell.hasEmptyValue())
677  pJumpMatrix->PutResultEmpty( nC, nR );
678  else if (aCell.hasNumeric())
679  {
680  double fCellVal = GetCellValue(aAdr, aCell);
681  if ( nGlobalError != FormulaError::NONE )
682  {
683  fCellVal = CreateDoubleError(
684  nGlobalError);
685  nGlobalError = FormulaError::NONE;
686  }
687  pJumpMatrix->PutResultDouble( fCellVal, nC, nR );
688  }
689  else
690  {
692  GetCellString(aStr, aCell);
693  if ( nGlobalError != FormulaError::NONE )
694  {
695  pJumpMatrix->PutResultDouble( CreateDoubleError(
696  nGlobalError), nC, nR);
697  nGlobalError = FormulaError::NONE;
698  }
699  else
700  pJumpMatrix->PutResultString(aStr, nC, nR);
701  }
702  }
703  SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
704  SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
705  lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows );
706  }
707 
709  if (eReturnType == ParamClass::Reference)
710  {
711  /* TODO: What about error handling and do we actually
712  * need the result matrix above at all in this case? */
713  pJumpMatrix->GetRefList().push_back( *(xRef->GetDoubleRef()));
714  }
715  }
716  break;
717  case svExternalSingleRef:
718  {
720  PopExternalSingleRef(pToken);
721  if (nGlobalError != FormulaError::NONE)
722  {
723  pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), nC, nR );
724  nGlobalError = FormulaError::NONE;
725  }
726  else
727  {
728  switch (pToken->GetType())
729  {
730  case svDouble:
731  pJumpMatrix->PutResultDouble( pToken->GetDouble(), nC, nR );
732  break;
733  case svString:
734  pJumpMatrix->PutResultString( pToken->GetString(), nC, nR );
735  break;
736  case svEmptyCell:
737  pJumpMatrix->PutResultEmpty( nC, nR );
738  break;
739  default:
740  // svError was already handled (set by
741  // PopExternalSingleRef()) with nGlobalError
742  // above.
743  assert(!"unhandled svExternalSingleRef case");
744  pJumpMatrix->PutResultDouble( CreateDoubleError(
745  FormulaError::UnknownStackVariable), nC, nR );
746  }
747  }
748  }
749  break;
750  case svExternalDoubleRef:
751  case svMatrix:
752  { // match matrix offsets
753  double fVal;
754  ScMatrixRef pMat = GetMatrix();
755  if ( nGlobalError != FormulaError::NONE )
756  {
757  fVal = CreateDoubleError( nGlobalError );
758  nGlobalError = FormulaError::NONE;
759  pJumpMatrix->PutResultDouble( fVal, nC, nR );
760  }
761  else if ( !pMat )
762  {
763  fVal = CreateDoubleError( FormulaError::UnknownVariable );
764  pJumpMatrix->PutResultDouble( fVal, nC, nR );
765  }
766  else
767  {
768  SCSIZE nCols, nRows;
769  pMat->GetDimensions( nCols, nRows );
770  if ((nCols <= nC && nCols != 1) ||
771  (nRows <= nR && nRows != 1))
772  {
773  fVal = CreateDoubleError( FormulaError::NotAvailable );
774  pJumpMatrix->PutResultDouble( fVal, nC, nR );
775  }
776  else
777  {
778  // GetMatrix() does SetErrorInterpreter() at the
779  // matrix, do not propagate an error from
780  // matrix->GetValue() as global error.
781  pMat->SetErrorInterpreter(nullptr);
782  lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR);
783  }
784  lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows );
785  }
786  }
787  break;
788  case svError:
789  {
790  PopError();
791  double fVal = CreateDoubleError( nGlobalError);
792  nGlobalError = FormulaError::NONE;
793  pJumpMatrix->PutResultDouble( fVal, nC, nR );
794  }
795  break;
796  default:
797  {
798  Pop();
799  double fVal = CreateDoubleError( FormulaError::IllegalArgument);
800  pJumpMatrix->PutResultDouble( fVal, nC, nR );
801  }
802  }
803  }
804  }
805  bool bCont = pJumpMatrix->Next( nC, nR );
806  if ( bCont )
807  {
808  double fBool;
809  short nStart, nNext, nStop;
810  pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
811  while ( bCont && nStart == nNext )
812  { // push all results that have no jump path
813  if ( bHasResMat && (GetDoubleErrorValue( fBool) != FormulaError::JumpMatHasResult) )
814  {
815  // a false without path results in an empty path value
816  if ( fBool == 0.0 )
817  pJumpMatrix->PutResultEmptyPath( nC, nR );
818  else
819  pJumpMatrix->PutResultDouble( fBool, nC, nR );
820  }
821  bCont = pJumpMatrix->Next( nC, nR );
822  if ( bCont )
823  pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
824  }
825  if ( bCont && nStart != nNext )
826  {
827  const ScTokenVec & rParams = pJumpMatrix->GetJumpParameters();
828  for ( auto const & i : rParams )
829  {
830  // This is not the current state of the interpreter, so
831  // push without error, and elements' errors are coded into
832  // double.
833  PushWithoutError(*i);
834  }
835  aCode.Jump( nStart, nNext, nStop );
836  }
837  }
838  if ( !bCont )
839  { // We're done with it, throw away jump matrix, keep result.
840  // For an intermediate result of Reference use the array of references
841  // if there are more than one reference and the current ForceArray
842  // context is ReferenceOrRefArray.
843  // Else (also for a final result of Reference) use the matrix.
844  // Treat the result of a jump command as final and use the matrix (see
845  // tdf#115493 for why).
846  if (pCur->GetInForceArray() == ParamClass::ReferenceOrRefArray &&
847  pJumpMatrix->GetRefList().size() > 1 &&
848  ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16) == ParamClass::Reference &&
849  !FormulaCompiler::IsOpCodeJumpCommand( pJumpMatrix->GetOpCode()) &&
850  aCode.PeekNextOperator())
851  {
852  FormulaTokenRef xRef = new ScRefListToken(true);
853  *(xRef->GetRefList()) = pJumpMatrix->GetRefList();
854  pJumpMatrix = nullptr;
855  Pop();
856  PushTokenRef( xRef);
857  if (pTokenMatrixMap)
858  {
859  pTokenMatrixMap->erase( pCur);
860  // There's no result matrix to remember in this case.
861  }
862  }
863  else
864  {
865  ScMatrix* pResMat = pJumpMatrix->GetResultMatrix();
866  pJumpMatrix = nullptr;
867  Pop();
868  PushMatrix( pResMat );
869  // Remove jump matrix from map and remember result matrix in case it
870  // could be reused in another path of the same condition.
871  if (pTokenMatrixMap)
872  {
873  pTokenMatrixMap->erase( pCur);
874  pTokenMatrixMap->emplace(pCur, pStack[sp-1]);
875  }
876  }
877  return true;
878  }
879  return false;
880 }
881 
883 {
884  sc::Compare aComp;
885  aComp.meOp = eOp;
886  aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
887  for( short i = 1; i >= 0; i-- )
888  {
889  sc::Compare::Cell& rCell = aComp.maCells[i];
890 
891  switch ( GetRawStackType() )
892  {
893  case svEmptyCell:
894  Pop();
895  rCell.mbEmpty = true;
896  break;
897  case svMissing:
898  case svDouble:
899  rCell.mfValue = GetDouble();
900  rCell.mbValue = true;
901  break;
902  case svString:
903  rCell.maStr = GetString();
904  rCell.mbValue = false;
905  break;
906  case svDoubleRef :
907  case svSingleRef :
908  {
909  ScAddress aAdr;
910  if ( !PopDoubleRefOrSingleRef( aAdr ) )
911  break;
912  ScRefCellValue aCell(mrDoc, aAdr);
913  if (aCell.hasEmptyValue())
914  rCell.mbEmpty = true;
915  else if (aCell.hasString())
916  {
918  GetCellString(aStr, aCell);
919  rCell.maStr = aStr;
920  rCell.mbValue = false;
921  }
922  else
923  {
924  rCell.mfValue = GetCellValue(aAdr, aCell);
925  rCell.mbValue = true;
926  }
927  }
928  break;
929  case svExternalSingleRef:
930  {
931  ScMatrixRef pMat = GetMatrix();
932  if (!pMat)
933  {
934  SetError( FormulaError::IllegalParameter);
935  break;
936  }
937 
938  SCSIZE nC, nR;
939  pMat->GetDimensions(nC, nR);
940  if (!nC || !nR)
941  {
942  SetError( FormulaError::IllegalParameter);
943  break;
944  }
945  if (pMat->IsEmpty(0, 0))
946  rCell.mbEmpty = true;
947  else if (pMat->IsStringOrEmpty(0, 0))
948  {
949  rCell.maStr = pMat->GetString(0, 0);
950  rCell.mbValue = false;
951  }
952  else
953  {
954  rCell.mfValue = pMat->GetDouble(0, 0);
955  rCell.mbValue = true;
956  }
957  }
958  break;
959  case svExternalDoubleRef:
960  // TODO: Find out how to handle this...
961  // Xcl generates a position dependent intersection using
962  // col/row, as it seems to do for all range references, not
963  // only in compare context. We'd need a general implementation
964  // for that behavior similar to svDoubleRef in scalar and array
965  // mode. Which also means we'd have to change all places where
966  // it currently is handled along with svMatrix.
967  default:
968  PopError();
969  SetError( FormulaError::IllegalParameter);
970  break;
971  }
972  }
973  if( nGlobalError != FormulaError::NONE )
974  return 0;
975  nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
976  return sc::CompareFunc(aComp);
977 }
978 
980 {
981  sc::Compare aComp;
982  aComp.meOp = eOp;
983  aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
984  sc::RangeMatrix aMat[2];
985  ScAddress aAdr;
986  for( short i = 1; i >= 0; i-- )
987  {
988  sc::Compare::Cell& rCell = aComp.maCells[i];
989 
990  switch (GetRawStackType())
991  {
992  case svEmptyCell:
993  Pop();
994  rCell.mbEmpty = true;
995  break;
996  case svMissing:
997  case svDouble:
998  rCell.mfValue = GetDouble();
999  rCell.mbValue = true;
1000  break;
1001  case svString:
1002  rCell.maStr = GetString();
1003  rCell.mbValue = false;
1004  break;
1005  case svSingleRef:
1006  {
1007  PopSingleRef( aAdr );
1008  ScRefCellValue aCell(mrDoc, aAdr);
1009  if (aCell.hasEmptyValue())
1010  rCell.mbEmpty = true;
1011  else if (aCell.hasString())
1012  {
1014  GetCellString(aStr, aCell);
1015  rCell.maStr = aStr;
1016  rCell.mbValue = false;
1017  }
1018  else
1019  {
1020  rCell.mfValue = GetCellValue(aAdr, aCell);
1021  rCell.mbValue = true;
1022  }
1023  }
1024  break;
1025  case svExternalSingleRef:
1026  case svExternalDoubleRef:
1027  case svDoubleRef:
1028  case svMatrix:
1029  aMat[i] = GetRangeMatrix();
1030  if (!aMat[i].mpMat)
1031  SetError( FormulaError::IllegalParameter);
1032  else
1033  aMat[i].mpMat->SetErrorInterpreter(nullptr);
1034  // errors are transported as DoubleError inside matrix
1035  break;
1036  default:
1037  PopError();
1038  SetError( FormulaError::IllegalParameter);
1039  break;
1040  }
1041  }
1042 
1043  sc::RangeMatrix aRes;
1044 
1045  if (nGlobalError != FormulaError::NONE)
1046  {
1047  nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
1048  return aRes;
1049  }
1050 
1051  if (aMat[0].mpMat && aMat[1].mpMat)
1052  {
1053  SCSIZE nC0, nC1;
1054  SCSIZE nR0, nR1;
1055  aMat[0].mpMat->GetDimensions(nC0, nR0);
1056  aMat[1].mpMat->GetDimensions(nC1, nR1);
1057  SCSIZE nC = std::max( nC0, nC1 );
1058  SCSIZE nR = std::max( nR0, nR1 );
1059  aRes.mpMat = GetNewMat( nC, nR);
1060  if (!aRes.mpMat)
1061  return aRes;
1062  for ( SCSIZE j=0; j<nC; j++ )
1063  {
1064  for ( SCSIZE k=0; k<nR; k++ )
1065  {
1066  SCSIZE nCol = j, nRow = k;
1067  if (aMat[0].mpMat->ValidColRowOrReplicated(nCol, nRow) &&
1068  aMat[1].mpMat->ValidColRowOrReplicated(nCol, nRow))
1069  {
1070  for ( short i=1; i>=0; i-- )
1071  {
1072  sc::Compare::Cell& rCell = aComp.maCells[i];
1073 
1074  if (aMat[i].mpMat->IsStringOrEmpty(j, k))
1075  {
1076  rCell.mbValue = false;
1077  rCell.maStr = aMat[i].mpMat->GetString(j, k);
1078  rCell.mbEmpty = aMat[i].mpMat->IsEmpty(j, k);
1079  }
1080  else
1081  {
1082  rCell.mbValue = true;
1083  rCell.mfValue = aMat[i].mpMat->GetDouble(j, k);
1084  rCell.mbEmpty = false;
1085  }
1086  }
1087  aRes.mpMat->PutDouble( sc::CompareFunc( aComp, pOptions), j, k);
1088  }
1089  else
1090  aRes.mpMat->PutError( FormulaError::NoValue, j, k);
1091  }
1092  }
1093 
1094  switch (eOp)
1095  {
1096  case SC_EQUAL:
1097  aRes.mpMat->CompareEqual();
1098  break;
1099  case SC_LESS:
1100  aRes.mpMat->CompareLess();
1101  break;
1102  case SC_GREATER:
1103  aRes.mpMat->CompareGreater();
1104  break;
1105  case SC_LESS_EQUAL:
1106  aRes.mpMat->CompareLessEqual();
1107  break;
1108  case SC_GREATER_EQUAL:
1109  aRes.mpMat->CompareGreaterEqual();
1110  break;
1111  case SC_NOT_EQUAL:
1112  aRes.mpMat->CompareNotEqual();
1113  break;
1114  default:
1115  SAL_WARN("sc", "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp));
1116  aRes.mpMat.reset();
1117  return aRes;
1118  }
1119  }
1120  else if (aMat[0].mpMat || aMat[1].mpMat)
1121  {
1122  size_t i = ( aMat[0].mpMat ? 0 : 1);
1123 
1124  aRes.mnCol1 = aMat[i].mnCol1;
1125  aRes.mnRow1 = aMat[i].mnRow1;
1126  aRes.mnTab1 = aMat[i].mnTab1;
1127  aRes.mnCol2 = aMat[i].mnCol2;
1128  aRes.mnRow2 = aMat[i].mnRow2;
1129  aRes.mnTab2 = aMat[i].mnTab2;
1130 
1131  ScMatrix& rMat = *aMat[i].mpMat;
1132  aRes.mpMat = rMat.CompareMatrix(aComp, i, pOptions);
1133  if (!aRes.mpMat)
1134  return aRes;
1135  }
1136 
1137  nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
1138  return aRes;
1139 }
1140 
1142 {
1143  SvNumFormatType nSaveCurFmtType = nCurFmtType;
1144  SvNumFormatType nSaveFuncFmtType = nFuncFmtType;
1145  PushMatrix( pMat);
1146  const ScQueryEntry::Item& rItem = rOptions.aQueryEntry.GetQueryItem();
1147  if (rItem.meType == ScQueryEntry::ByString)
1148  PushString(rItem.maString.getString());
1149  else
1150  PushDouble(rItem.mfVal);
1151  ScMatrixRef pResultMatrix = CompareMat(rOptions.aQueryEntry.eOp, &rOptions).mpMat;
1152  nCurFmtType = nSaveCurFmtType;
1153  nFuncFmtType = nSaveFuncFmtType;
1154  if (nGlobalError != FormulaError::NONE || !pResultMatrix)
1155  {
1156  SetError( FormulaError::IllegalParameter);
1157  return pResultMatrix;
1158  }
1159 
1160  return pResultMatrix;
1161 }
1162 
1164 {
1165  if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1166  {
1167  sc::RangeMatrix aMat = CompareMat(SC_EQUAL);
1168  if (!aMat.mpMat)
1169  {
1170  PushIllegalParameter();
1171  return;
1172  }
1173 
1174  PushMatrix(aMat);
1175  }
1176  else
1177  PushInt( int(Compare( SC_EQUAL) == 0) );
1178 }
1179 
1181 {
1182  if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1183  {
1184  sc::RangeMatrix aMat = CompareMat(SC_NOT_EQUAL);
1185  if (!aMat.mpMat)
1186  {
1187  PushIllegalParameter();
1188  return;
1189  }
1190 
1191  PushMatrix(aMat);
1192  }
1193  else
1194  PushInt( int(Compare( SC_NOT_EQUAL) != 0) );
1195 }
1196 
1198 {
1199  if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1200  {
1201  sc::RangeMatrix aMat = CompareMat(SC_LESS);
1202  if (!aMat.mpMat)
1203  {
1204  PushIllegalParameter();
1205  return;
1206  }
1207 
1208  PushMatrix(aMat);
1209  }
1210  else
1211  PushInt( int(Compare( SC_LESS) < 0) );
1212 }
1213 
1215 {
1216  if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1217  {
1218  sc::RangeMatrix aMat = CompareMat(SC_GREATER);
1219  if (!aMat.mpMat)
1220  {
1221  PushIllegalParameter();
1222  return;
1223  }
1224 
1225  PushMatrix(aMat);
1226  }
1227  else
1228  PushInt( int(Compare( SC_GREATER) > 0) );
1229 }
1230 
1232 {
1233  if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1234  {
1235  sc::RangeMatrix aMat = CompareMat(SC_LESS_EQUAL);
1236  if (!aMat.mpMat)
1237  {
1238  PushIllegalParameter();
1239  return;
1240  }
1241 
1242  PushMatrix(aMat);
1243  }
1244  else
1245  PushInt( int(Compare( SC_LESS_EQUAL) <= 0) );
1246 }
1247 
1249 {
1250  if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1251  {
1252  sc::RangeMatrix aMat = CompareMat(SC_GREATER_EQUAL);
1253  if (!aMat.mpMat)
1254  {
1255  PushIllegalParameter();
1256  return;
1257  }
1258 
1259  PushMatrix(aMat);
1260  }
1261  else
1262  PushInt( int(Compare( SC_GREATER_EQUAL) >= 0) );
1263 }
1264 
1266 {
1267  nFuncFmtType = SvNumFormatType::LOGICAL;
1268  short nParamCount = GetByte();
1269  if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1270  return;
1271 
1272  bool bHaveValue = false;
1273  bool bRes = true;
1274  size_t nRefInList = 0;
1275  while( nParamCount-- > 0)
1276  {
1277  if ( nGlobalError == FormulaError::NONE )
1278  {
1279  switch ( GetStackType() )
1280  {
1281  case svDouble :
1282  bHaveValue = true;
1283  bRes &= ( PopDouble() != 0.0 );
1284  break;
1285  case svString :
1286  Pop();
1287  SetError( FormulaError::NoValue );
1288  break;
1289  case svSingleRef :
1290  {
1291  ScAddress aAdr;
1292  PopSingleRef( aAdr );
1293  if ( nGlobalError == FormulaError::NONE )
1294  {
1295  ScRefCellValue aCell(mrDoc, aAdr);
1296  if (aCell.hasNumeric())
1297  {
1298  bHaveValue = true;
1299  bRes &= ( GetCellValue(aAdr, aCell) != 0.0 );
1300  }
1301  // else: Xcl raises no error here
1302  }
1303  }
1304  break;
1305  case svDoubleRef:
1306  case svRefList:
1307  {
1308  ScRange aRange;
1309  PopDoubleRef( aRange, nParamCount, nRefInList);
1310  if ( nGlobalError == FormulaError::NONE )
1311  {
1312  double fVal;
1313  FormulaError nErr = FormulaError::NONE;
1314  ScValueIterator aValIter( mrDoc, aRange );
1315  if ( aValIter.GetFirst( fVal, nErr ) && nErr == FormulaError::NONE )
1316  {
1317  bHaveValue = true;
1318  do
1319  {
1320  bRes &= ( fVal != 0.0 );
1321  } while ( (nErr == FormulaError::NONE) &&
1322  aValIter.GetNext( fVal, nErr ) );
1323  }
1324  SetError( nErr );
1325  }
1326  }
1327  break;
1328  case svExternalSingleRef:
1329  case svExternalDoubleRef:
1330  case svMatrix:
1331  {
1332  ScMatrixRef pMat = GetMatrix();
1333  if ( pMat )
1334  {
1335  bHaveValue = true;
1336  double fVal = pMat->And();
1337  FormulaError nErr = GetDoubleErrorValue( fVal );
1338  if ( nErr != FormulaError::NONE )
1339  {
1340  SetError( nErr );
1341  bRes = false;
1342  }
1343  else
1344  bRes &= (fVal != 0.0);
1345  }
1346  // else: GetMatrix did set FormulaError::IllegalParameter
1347  }
1348  break;
1349  default:
1350  Pop();
1351  SetError( FormulaError::IllegalParameter);
1352  }
1353  }
1354  else
1355  Pop();
1356  }
1357  if ( bHaveValue )
1358  PushInt( int(bRes) );
1359  else
1360  PushNoValue();
1361 }
1362 
1364 {
1365  nFuncFmtType = SvNumFormatType::LOGICAL;
1366  short nParamCount = GetByte();
1367  if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1368  return;
1369 
1370  bool bHaveValue = false;
1371  bool bRes = false;
1372  size_t nRefInList = 0;
1373  while( nParamCount-- > 0)
1374  {
1375  if ( nGlobalError == FormulaError::NONE )
1376  {
1377  switch ( GetStackType() )
1378  {
1379  case svDouble :
1380  bHaveValue = true;
1381  bRes |= ( PopDouble() != 0.0 );
1382  break;
1383  case svString :
1384  Pop();
1385  SetError( FormulaError::NoValue );
1386  break;
1387  case svSingleRef :
1388  {
1389  ScAddress aAdr;
1390  PopSingleRef( aAdr );
1391  if ( nGlobalError == FormulaError::NONE )
1392  {
1393  ScRefCellValue aCell(mrDoc, aAdr);
1394  if (aCell.hasNumeric())
1395  {
1396  bHaveValue = true;
1397  bRes |= ( GetCellValue(aAdr, aCell) != 0.0 );
1398  }
1399  // else: Xcl raises no error here
1400  }
1401  }
1402  break;
1403  case svDoubleRef:
1404  case svRefList:
1405  {
1406  ScRange aRange;
1407  PopDoubleRef( aRange, nParamCount, nRefInList);
1408  if ( nGlobalError == FormulaError::NONE )
1409  {
1410  double fVal;
1411  FormulaError nErr = FormulaError::NONE;
1412  ScValueIterator aValIter( mrDoc, aRange );
1413  if ( aValIter.GetFirst( fVal, nErr ) )
1414  {
1415  bHaveValue = true;
1416  do
1417  {
1418  bRes |= ( fVal != 0.0 );
1419  } while ( (nErr == FormulaError::NONE) &&
1420  aValIter.GetNext( fVal, nErr ) );
1421  }
1422  SetError( nErr );
1423  }
1424  }
1425  break;
1426  case svExternalSingleRef:
1427  case svExternalDoubleRef:
1428  case svMatrix:
1429  {
1430  bHaveValue = true;
1431  ScMatrixRef pMat = GetMatrix();
1432  if ( pMat )
1433  {
1434  bHaveValue = true;
1435  double fVal = pMat->Or();
1436  FormulaError nErr = GetDoubleErrorValue( fVal );
1437  if ( nErr != FormulaError::NONE )
1438  {
1439  SetError( nErr );
1440  bRes = false;
1441  }
1442  else
1443  bRes |= (fVal != 0.0);
1444  }
1445  // else: GetMatrix did set FormulaError::IllegalParameter
1446  }
1447  break;
1448  default:
1449  Pop();
1450  SetError( FormulaError::IllegalParameter);
1451  }
1452  }
1453  else
1454  Pop();
1455  }
1456  if ( bHaveValue )
1457  PushInt( int(bRes) );
1458  else
1459  PushNoValue();
1460 }
1461 
1463 {
1464 
1465  nFuncFmtType = SvNumFormatType::LOGICAL;
1466  short nParamCount = GetByte();
1467  if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1468  return;
1469 
1470  bool bHaveValue = false;
1471  bool bRes = false;
1472  size_t nRefInList = 0;
1473  while( nParamCount-- > 0)
1474  {
1475  if ( nGlobalError == FormulaError::NONE )
1476  {
1477  switch ( GetStackType() )
1478  {
1479  case svDouble :
1480  bHaveValue = true;
1481  bRes ^= ( PopDouble() != 0.0 );
1482  break;
1483  case svString :
1484  Pop();
1485  SetError( FormulaError::NoValue );
1486  break;
1487  case svSingleRef :
1488  {
1489  ScAddress aAdr;
1490  PopSingleRef( aAdr );
1491  if ( nGlobalError == FormulaError::NONE )
1492  {
1493  ScRefCellValue aCell(mrDoc, aAdr);
1494  if (aCell.hasNumeric())
1495  {
1496  bHaveValue = true;
1497  bRes ^= ( GetCellValue(aAdr, aCell) != 0.0 );
1498  }
1499  /* TODO: set error? Excel doesn't have XOR, but
1500  * doesn't set an error in this case for AND and
1501  * OR. */
1502  }
1503  }
1504  break;
1505  case svDoubleRef:
1506  case svRefList:
1507  {
1508  ScRange aRange;
1509  PopDoubleRef( aRange, nParamCount, nRefInList);
1510  if ( nGlobalError == FormulaError::NONE )
1511  {
1512  double fVal;
1513  FormulaError nErr = FormulaError::NONE;
1514  ScValueIterator aValIter( mrDoc, aRange );
1515  if ( aValIter.GetFirst( fVal, nErr ) )
1516  {
1517  bHaveValue = true;
1518  do
1519  {
1520  bRes ^= ( fVal != 0.0 );
1521  } while ( (nErr == FormulaError::NONE) &&
1522  aValIter.GetNext( fVal, nErr ) );
1523  }
1524  SetError( nErr );
1525  }
1526  }
1527  break;
1528  case svExternalSingleRef:
1529  case svExternalDoubleRef:
1530  case svMatrix:
1531  {
1532  bHaveValue = true;
1533  ScMatrixRef pMat = GetMatrix();
1534  if ( pMat )
1535  {
1536  bHaveValue = true;
1537  double fVal = pMat->Xor();
1538  FormulaError nErr = GetDoubleErrorValue( fVal );
1539  if ( nErr != FormulaError::NONE )
1540  {
1541  SetError( nErr );
1542  bRes = false;
1543  }
1544  else
1545  bRes ^= ( fVal != 0.0 );
1546  }
1547  // else: GetMatrix did set FormulaError::IllegalParameter
1548  }
1549  break;
1550  default:
1551  Pop();
1552  SetError( FormulaError::IllegalParameter);
1553  }
1554  }
1555  else
1556  Pop();
1557  }
1558  if ( bHaveValue )
1559  PushInt( int(bRes) );
1560  else
1561  PushNoValue();
1562 }
1563 
1565 {
1566  // Simple negation doesn't change current format type to number, keep
1567  // current type.
1568  nFuncFmtType = nCurFmtType;
1569  switch ( GetStackType() )
1570  {
1571  case svMatrix :
1572  {
1573  ScMatrixRef pMat = GetMatrix();
1574  if ( !pMat )
1575  PushIllegalParameter();
1576  else
1577  {
1578  SCSIZE nC, nR;
1579  pMat->GetDimensions( nC, nR );
1580  ScMatrixRef pResMat = GetNewMat( nC, nR);
1581  if ( !pResMat )
1582  PushIllegalArgument();
1583  else
1584  {
1585  pMat->NegOp( *pResMat);
1586  PushMatrix( pResMat );
1587  }
1588  }
1589  }
1590  break;
1591  default:
1592  PushDouble( -GetDouble() );
1593  }
1594 }
1595 
1597 {
1598  nFuncFmtType = SvNumFormatType::PERCENT;
1599  const FormulaToken* pSaveCur = pCur;
1600  sal_uInt8 nSavePar = cPar;
1601  PushInt( 100 );
1602  cPar = 2;
1603  FormulaByteToken aDivOp( ocDiv, cPar );
1604  pCur = &aDivOp;
1605  ScDiv();
1606  pCur = pSaveCur;
1607  cPar = nSavePar;
1608 }
1609 
1611 {
1612  nFuncFmtType = SvNumFormatType::LOGICAL;
1613  switch ( GetStackType() )
1614  {
1615  case svMatrix :
1616  {
1617  ScMatrixRef pMat = GetMatrix();
1618  if ( !pMat )
1619  PushIllegalParameter();
1620  else
1621  {
1622  SCSIZE nC, nR;
1623  pMat->GetDimensions( nC, nR );
1624  ScMatrixRef pResMat = GetNewMat( nC, nR);
1625  if ( !pResMat )
1626  PushIllegalArgument();
1627  else
1628  {
1629  pMat->NotOp( *pResMat);
1630  PushMatrix( pResMat );
1631  }
1632  }
1633  }
1634  break;
1635  default:
1636  PushInt( int(GetDouble() == 0.0) );
1637  }
1638 }
1639 
1641 {
1642 
1643  if ( !MustHaveParamCount( GetByte(), 2 ) )
1644  return;
1645 
1646  double num1 = ::rtl::math::approxFloor( GetDouble());
1647  double num2 = ::rtl::math::approxFloor( GetDouble());
1648  if ( (num1 >= n2power48) || (num1 < 0) ||
1649  (num2 >= n2power48) || (num2 < 0))
1650  PushIllegalArgument();
1651  else
1652  PushDouble (static_cast<sal_uInt64>(num1) & static_cast<sal_uInt64>(num2));
1653 }
1654 
1656 {
1657 
1658  if ( !MustHaveParamCount( GetByte(), 2 ) )
1659  return;
1660 
1661  double num1 = ::rtl::math::approxFloor( GetDouble());
1662  double num2 = ::rtl::math::approxFloor( GetDouble());
1663  if ( (num1 >= n2power48) || (num1 < 0) ||
1664  (num2 >= n2power48) || (num2 < 0))
1665  PushIllegalArgument();
1666  else
1667  PushDouble (static_cast<sal_uInt64>(num1) | static_cast<sal_uInt64>(num2));
1668 }
1669 
1671 {
1672 
1673  if ( !MustHaveParamCount( GetByte(), 2 ) )
1674  return;
1675 
1676  double num1 = ::rtl::math::approxFloor( GetDouble());
1677  double num2 = ::rtl::math::approxFloor( GetDouble());
1678  if ( (num1 >= n2power48) || (num1 < 0) ||
1679  (num2 >= n2power48) || (num2 < 0))
1680  PushIllegalArgument();
1681  else
1682  PushDouble (static_cast<sal_uInt64>(num1) ^ static_cast<sal_uInt64>(num2));
1683 }
1684 
1686 {
1687 
1688  if ( !MustHaveParamCount( GetByte(), 2 ) )
1689  return;
1690 
1691  double fShift = ::rtl::math::approxFloor( GetDouble());
1692  double num = ::rtl::math::approxFloor( GetDouble());
1693  if ((num >= n2power48) || (num < 0))
1694  PushIllegalArgument();
1695  else
1696  {
1697  double fRes;
1698  if (fShift < 0)
1699  fRes = ::rtl::math::approxFloor( num / pow( 2.0, -fShift));
1700  else if (fShift == 0)
1701  fRes = num;
1702  else
1703  fRes = num * pow( 2.0, fShift);
1704  PushDouble( fRes);
1705  }
1706 }
1707 
1709 {
1710 
1711  if ( !MustHaveParamCount( GetByte(), 2 ) )
1712  return;
1713 
1714  double fShift = ::rtl::math::approxFloor( GetDouble());
1715  double num = ::rtl::math::approxFloor( GetDouble());
1716  if ((num >= n2power48) || (num < 0))
1717  PushIllegalArgument();
1718  else
1719  {
1720  double fRes;
1721  if (fShift < 0)
1722  fRes = num * pow( 2.0, -fShift);
1723  else if (fShift == 0)
1724  fRes = num;
1725  else
1726  fRes = ::rtl::math::approxFloor( num / pow( 2.0, fShift));
1727  PushDouble( fRes);
1728  }
1729 }
1730 
1732 {
1733  PushDouble(F_PI);
1734 }
1735 
1736 void ScInterpreter::ScRandomImpl( const std::function<double( double fFirst, double fLast )>& RandomFunc,
1737  double fFirst, double fLast )
1738 {
1739  if (bMatrixFormula)
1740  {
1741  SCCOL nCols = 0;
1742  SCROW nRows = 0;
1743  if (pMyFormulaCell)
1744  pMyFormulaCell->GetMatColsRows( nCols, nRows);
1745 
1746  if (nCols == 1 && nRows == 1)
1747  {
1748  // For compatibility with existing
1749  // com.sun.star.sheet.FunctionAccess.callFunction() calls that per
1750  // default are executed in array context unless
1751  // FA.setPropertyValue("IsArrayFunction",False) was set, return a
1752  // scalar double instead of a 1x1 matrix object. tdf#128218
1753  PushDouble( RandomFunc( fFirst, fLast));
1754  return;
1755  }
1756 
1757  // ScViewFunc::EnterMatrix() might be asking for
1758  // ScFormulaCell::GetResultDimensions(), which here are none so create
1759  // a 1x1 matrix at least which exactly is the case when EnterMatrix()
1760  // asks for a not selected range.
1761  if (nCols == 0)
1762  nCols = 1;
1763  if (nRows == 0)
1764  nRows = 1;
1765  ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows));
1766  if (!pResMat)
1767  PushError( FormulaError::MatrixSize);
1768  else
1769  {
1770  for (SCCOL i=0; i < nCols; ++i)
1771  {
1772  for (SCROW j=0; j < nRows; ++j)
1773  {
1774  pResMat->PutDouble( RandomFunc( fFirst, fLast),
1775  static_cast<SCSIZE>(i), static_cast<SCSIZE>(j));
1776  }
1777  }
1778  PushMatrix( pResMat);
1779  }
1780  }
1781  else
1782  {
1783  PushDouble( RandomFunc( fFirst, fLast));
1784  }
1785 }
1786 
1788 {
1789  auto RandomFunc = []( double, double )
1790  {
1792  };
1793  ScRandomImpl( RandomFunc, 0.0, 0.0);
1794 }
1795 
1797 {
1798  if (!MustHaveParamCount( GetByte(), 2))
1799  return;
1800 
1801  // Same like scaddins/source/analysis/analysis.cxx
1802  // AnalysisAddIn::getRandbetween()
1803  double fMax = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
1804  double fMin = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
1805  if (nGlobalError != FormulaError::NONE || fMin > fMax)
1806  {
1807  PushIllegalArgument();
1808  return;
1809  }
1810  fMax = std::nextafter( fMax+1, -DBL_MAX);
1811  auto RandomFunc = []( double fFirst, double fLast )
1812  {
1813  return floor( comphelper::rng::uniform_real_distribution( fFirst, fLast));
1814  };
1815  ScRandomImpl( RandomFunc, fMin, fMax);
1816 }
1817 
1819 {
1820  nFuncFmtType = SvNumFormatType::LOGICAL;
1821  PushInt(1);
1822 }
1823 
1825 {
1826  nFuncFmtType = SvNumFormatType::LOGICAL;
1827  PushInt(0);
1828 }
1829 
1831 {
1832  PushDouble(basegfx::rad2deg(GetDouble()));
1833 }
1834 
1836 {
1837  PushDouble(basegfx::deg2rad(GetDouble()));
1838 }
1839 
1841 {
1842  PushDouble(::rtl::math::sin(GetDouble()));
1843 }
1844 
1846 {
1847  PushDouble(::rtl::math::cos(GetDouble()));
1848 }
1849 
1851 {
1852  PushDouble(::rtl::math::tan(GetDouble()));
1853 }
1854 
1856 {
1857  PushDouble(1.0 / ::rtl::math::tan(GetDouble()));
1858 }
1859 
1861 {
1862  PushDouble(asin(GetDouble()));
1863 }
1864 
1866 {
1867  PushDouble(acos(GetDouble()));
1868 }
1869 
1871 {
1872  PushDouble(atan(GetDouble()));
1873 }
1874 
1876 {
1877  PushDouble((F_PI2) - atan(GetDouble()));
1878 }
1879 
1881 {
1882  PushDouble(sinh(GetDouble()));
1883 }
1884 
1886 {
1887  PushDouble(cosh(GetDouble()));
1888 }
1889 
1891 {
1892  PushDouble(tanh(GetDouble()));
1893 }
1894 
1896 {
1897  PushDouble(1.0 / tanh(GetDouble()));
1898 }
1899 
1901 {
1902  PushDouble( ::rtl::math::asinh( GetDouble()));
1903 }
1904 
1906 {
1907  double fVal = GetDouble();
1908  if (fVal < 1.0)
1909  PushIllegalArgument();
1910  else
1911  PushDouble( ::rtl::math::acosh( fVal));
1912 }
1913 
1915 {
1916  double fVal = GetDouble();
1917  if (fabs(fVal) >= 1.0)
1918  PushIllegalArgument();
1919  else
1920  PushDouble( ::rtl::math::atanh( fVal));
1921 }
1922 
1924 {
1925  double nVal = GetDouble();
1926  if (fabs(nVal) <= 1.0)
1927  PushIllegalArgument();
1928  else
1929  PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0)));
1930 }
1931 
1933 {
1934  PushDouble(1.0 / ::rtl::math::sin(GetDouble()));
1935 }
1936 
1938 {
1939  PushDouble(1.0 / ::rtl::math::cos(GetDouble()));
1940 }
1941 
1943 {
1944  PushDouble(1.0 / sinh(GetDouble()));
1945 }
1946 
1948 {
1949  PushDouble(1.0 / cosh(GetDouble()));
1950 }
1951 
1953 {
1954  PushDouble(exp(GetDouble()));
1955 }
1956 
1958 {
1959  double fVal = GetDouble();
1960  if (fVal >= 0.0)
1961  PushDouble(sqrt(fVal));
1962  else
1963  PushIllegalArgument();
1964 }
1965 
1967 {
1968  short nRes = 0;
1969  nFuncFmtType = SvNumFormatType::LOGICAL;
1970  switch ( GetRawStackType() )
1971  {
1972  case svEmptyCell:
1973  {
1974  FormulaConstTokenRef p = PopToken();
1975  if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited())
1976  nRes = 1;
1977  }
1978  break;
1979  case svDoubleRef :
1980  case svSingleRef :
1981  {
1982  ScAddress aAdr;
1983  if ( !PopDoubleRefOrSingleRef( aAdr ) )
1984  break;
1985  // NOTE: this differs from COUNTBLANK() ScCountEmptyCells() that
1986  // may treat ="" in the referenced cell as blank for Excel
1987  // interoperability.
1988  ScRefCellValue aCell(mrDoc, aAdr);
1989  if (aCell.meType == CELLTYPE_NONE)
1990  nRes = 1;
1991  }
1992  break;
1993  case svExternalSingleRef:
1994  case svExternalDoubleRef:
1995  case svMatrix:
1996  {
1997  ScMatrixRef pMat = GetMatrix();
1998  if ( !pMat )
1999  ; // nothing
2000  else if ( !pJumpMatrix )
2001  nRes = pMat->IsEmptyCell( 0, 0) ? 1 : 0;
2002  else
2003  {
2004  SCSIZE nCols, nRows, nC, nR;
2005  pMat->GetDimensions( nCols, nRows);
2006  pJumpMatrix->GetPos( nC, nR);
2007  if ( nC < nCols && nR < nRows )
2008  nRes = pMat->IsEmptyCell( nC, nR) ? 1 : 0;
2009  // else: false, not empty (which is what Xcl does)
2010  }
2011  }
2012  break;
2013  default:
2014  Pop();
2015  }
2016  nGlobalError = FormulaError::NONE;
2017  PushInt( nRes );
2018 }
2019 
2021 {
2022  nFuncFmtType = SvNumFormatType::LOGICAL;
2023  bool bRes = false;
2024  switch ( GetRawStackType() )
2025  {
2026  case svString:
2027  Pop();
2028  bRes = true;
2029  break;
2030  case svDoubleRef :
2031  case svSingleRef :
2032  {
2033  ScAddress aAdr;
2034  if ( !PopDoubleRefOrSingleRef( aAdr ) )
2035  break;
2036 
2037  ScRefCellValue aCell(mrDoc, aAdr);
2038  if (GetCellErrCode(aCell) == FormulaError::NONE)
2039  {
2040  switch (aCell.meType)
2041  {
2042  case CELLTYPE_STRING :
2043  case CELLTYPE_EDIT :
2044  bRes = true;
2045  break;
2046  case CELLTYPE_FORMULA :
2047  bRes = (!aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty());
2048  break;
2049  default:
2050  ; // nothing
2051  }
2052  }
2053  }
2054  break;
2055  case svExternalSingleRef:
2056  {
2058  PopExternalSingleRef(pToken);
2059  if (nGlobalError == FormulaError::NONE && pToken->GetType() == svString)
2060  bRes = true;
2061  }
2062  break;
2063  case svExternalDoubleRef:
2064  case svMatrix:
2065  {
2066  ScMatrixRef pMat = GetMatrix();
2067  if ( !pMat )
2068  ; // nothing
2069  else if ( !pJumpMatrix )
2070  bRes = pMat->IsStringOrEmpty(0, 0) && !pMat->IsEmpty(0, 0);
2071  else
2072  {
2073  SCSIZE nCols, nRows, nC, nR;
2074  pMat->GetDimensions( nCols, nRows);
2075  pJumpMatrix->GetPos( nC, nR);
2076  if ( nC < nCols && nR < nRows )
2077  bRes = pMat->IsStringOrEmpty( nC, nR) && !pMat->IsEmpty( nC, nR);
2078  }
2079  }
2080  break;
2081  default:
2082  Pop();
2083  }
2084  nGlobalError = FormulaError::NONE;
2085  return bRes;
2086 }
2087 
2089 {
2090  PushInt( int(IsString()) );
2091 }
2092 
2094 {
2095  PushInt( int(!IsString()) );
2096 }
2097 
2099 {
2100  bool bRes = false;
2101  switch ( GetStackType() )
2102  {
2103  case svDoubleRef :
2104  case svSingleRef :
2105  {
2106  ScAddress aAdr;
2107  if ( !PopDoubleRefOrSingleRef( aAdr ) )
2108  break;
2109 
2110  ScRefCellValue aCell(mrDoc, aAdr);
2111  if (GetCellErrCode(aCell) == FormulaError::NONE)
2112  {
2113  if (aCell.hasNumeric())
2114  {
2115  sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
2116  bRes = (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL);
2117  }
2118  }
2119  }
2120  break;
2121  case svMatrix:
2122  {
2123  double fVal;
2125  ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
2126  bRes = (nMatValType == ScMatValType::Boolean);
2127  }
2128  break;
2129  default:
2130  PopError();
2131  if ( nGlobalError == FormulaError::NONE )
2132  bRes = ( nCurFmtType == SvNumFormatType::LOGICAL );
2133  }
2134  nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
2135  nGlobalError = FormulaError::NONE;
2136  PushInt( int(bRes) );
2137 }
2138 
2140 {
2141  short nType = 0;
2142  switch ( GetStackType() )
2143  {
2144  case svDoubleRef :
2145  case svSingleRef :
2146  {
2147  ScAddress aAdr;
2148  if ( !PopDoubleRefOrSingleRef( aAdr ) )
2149  break;
2150 
2151  ScRefCellValue aCell(mrDoc, aAdr);
2152  if (GetCellErrCode(aCell) == FormulaError::NONE)
2153  {
2154  switch (aCell.meType)
2155  {
2156  // NOTE: this is Xcl nonsense!
2157  case CELLTYPE_STRING :
2158  case CELLTYPE_EDIT :
2159  nType = 2;
2160  break;
2161  case CELLTYPE_VALUE :
2162  {
2163  sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
2164  if (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL)
2165  nType = 4;
2166  else
2167  nType = 1;
2168  }
2169  break;
2170  case CELLTYPE_NONE:
2171  // always 1, s. tdf#73078
2172  nType = 1;
2173  break;
2174  case CELLTYPE_FORMULA :
2175  nType = 8;
2176  break;
2177  default:
2178  PushIllegalArgument();
2179  }
2180  }
2181  else
2182  nType = 16;
2183  }
2184  break;
2185  case svString:
2186  PopError();
2187  if ( nGlobalError != FormulaError::NONE )
2188  {
2189  nType = 16;
2190  nGlobalError = FormulaError::NONE;
2191  }
2192  else
2193  nType = 2;
2194  break;
2195  case svMatrix:
2196  PopMatrix();
2197  if ( nGlobalError != FormulaError::NONE )
2198  {
2199  nType = 16;
2200  nGlobalError = FormulaError::NONE;
2201  }
2202  else
2203  nType = 64;
2204  // we could return the type of one element if in JumpMatrix or
2205  // ForceArray mode, but Xcl doesn't ...
2206  break;
2207  default:
2208  PopError();
2209  if ( nGlobalError != FormulaError::NONE )
2210  {
2211  nType = 16;
2212  nGlobalError = FormulaError::NONE;
2213  }
2214  else
2215  nType = 1;
2216  }
2217  PushInt( nType );
2218 }
2219 
2220 static bool lcl_FormatHasNegColor( const SvNumberformat* pFormat )
2221 {
2222  return pFormat && pFormat->GetColor( 1 );
2223 }
2224 
2225 static bool lcl_FormatHasOpenPar( const SvNumberformat* pFormat )
2226 {
2227  return pFormat && (pFormat->GetFormatstring().indexOf('(') != -1);
2228 }
2229 
2230 namespace {
2231 
2232 void getFormatString(const SvNumberFormatter* pFormatter, sal_uLong nFormat, OUString& rFmtStr)
2233 {
2234  rFmtStr = pFormatter->GetCalcCellReturn( nFormat);
2235 }
2236 
2237 }
2238 
2240 { // ATTRIBUTE ; [REF]
2241  sal_uInt8 nParamCount = GetByte();
2242  if( !MustHaveParamCount( nParamCount, 1, 2 ) )
2243  return;
2244 
2245  ScAddress aCellPos( aPos );
2246  if( nParamCount == 2 )
2247  {
2248  switch (GetStackType())
2249  {
2250  case svExternalSingleRef:
2251  case svExternalDoubleRef:
2252  {
2253  // Let's handle external reference separately...
2254  ScCellExternal();
2255  return;
2256  }
2257  case svDoubleRef:
2258  {
2259  // Exceptionally not an intersecting position but top left.
2260  // See ODF v1.3 part 4 OpenFormula 6.13.3 CELL
2261  ScRange aRange;
2262  PopDoubleRef( aRange);
2263  aCellPos = aRange.aStart;
2264  }
2265  break;
2266  case svSingleRef:
2267  PopSingleRef( aCellPos);
2268  break;
2269  default:
2270  PopError();
2271  SetError( FormulaError::NoRef);
2272  }
2273  }
2274  OUString aInfoType = GetString().getString();
2275  if (nGlobalError != FormulaError::NONE)
2276  PushIllegalParameter();
2277  else
2278  {
2279  ScRefCellValue aCell(mrDoc, aCellPos);
2280 
2282 
2283 // *** ADDRESS INFO ***
2284  if( aInfoType == "COL" )
2285  { // column number (1-based)
2286  PushInt( aCellPos.Col() + 1 );
2287  }
2288  else if( aInfoType == "ROW" )
2289  { // row number (1-based)
2290  PushInt( aCellPos.Row() + 1 );
2291  }
2292  else if( aInfoType == "SHEET" )
2293  { // table number (1-based)
2294  PushInt( aCellPos.Tab() + 1 );
2295  }
2296  else if( aInfoType == "ADDRESS" )
2297  { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW
2298  ScRefFlags nFlags = (aCellPos.Tab() == aPos.Tab()) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D;
2299  OUString aStr(aCellPos.Format(nFlags, &mrDoc, mrDoc.GetAddressConvention()));
2300  PushString(aStr);
2301  }
2302  else if( aInfoType == "FILENAME" )
2303  { // file name and table name: 'FILENAME'#$TABLE
2304  SCTAB nTab = aCellPos.Tab();
2305  OUString aFuncResult;
2306  if( nTab < mrDoc.GetTableCount() )
2307  {
2308  if( mrDoc.GetLinkMode( nTab ) == ScLinkMode::VALUE )
2309  mrDoc.GetName( nTab, aFuncResult );
2310  else
2311  {
2312  SfxObjectShell* pShell = mrDoc.GetDocumentShell();
2313  if( pShell && pShell->GetMedium() )
2314  {
2315  OUStringBuffer aBuf;
2316  aBuf.append('\'');
2317  const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject();
2319  aBuf.append("'#$");
2320  OUString aTabName;
2321  mrDoc.GetName( nTab, aTabName );
2322  aBuf.append(aTabName);
2323  aFuncResult = aBuf.makeStringAndClear();
2324  }
2325  }
2326  }
2327  PushString( aFuncResult );
2328  }
2329  else if( aInfoType == "COORD" )
2330  { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW
2331  // Yes, passing tab as col is intentional!
2332  OUString aCellStr1 =
2333  ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format(
2334  (ScRefFlags::COL_ABS|ScRefFlags::COL_VALID), nullptr, mrDoc.GetAddressConvention() );
2335  OUString aCellStr2 =
2337  nullptr, mrDoc.GetAddressConvention());
2338  OUString aFuncResult = aCellStr1 + ":" + aCellStr2;
2339  PushString( aFuncResult );
2340  }
2341 
2342 // *** CELL PROPERTIES ***
2343  else if( aInfoType == "CONTENTS" )
2344  { // contents of the cell, no formatting
2345  if (aCell.hasString())
2346  {
2348  GetCellString(aStr, aCell);
2349  PushString( aStr );
2350  }
2351  else
2352  PushDouble(GetCellValue(aCellPos, aCell));
2353  }
2354  else if( aInfoType == "TYPE" )
2355  { // b = blank; l = string (label); v = otherwise (value)
2356  sal_Unicode c;
2357  if (aCell.hasString())
2358  c = 'l';
2359  else
2360  c = aCell.hasNumeric() ? 'v' : 'b';
2361  PushString( OUString(c) );
2362  }
2363  else if( aInfoType == "WIDTH" )
2364  { // column width (rounded off as count of zero characters in standard font and size)
2365  Printer* pPrinter = mrDoc.GetPrinter();
2366  MapMode aOldMode( pPrinter->GetMapMode() );
2367  vcl::Font aOldFont( pPrinter->GetFont() );
2368  vcl::Font aDefFont;
2369 
2370  pPrinter->SetMapMode(MapMode(MapUnit::MapTwip));
2371  // font color doesn't matter here
2372  mrDoc.GetDefPattern()->GetFont( aDefFont, SC_AUTOCOL_BLACK, pPrinter );
2373  pPrinter->SetFont( aDefFont );
2374  tools::Long nZeroWidth = pPrinter->GetTextWidth( OUString( '0' ) );
2375  pPrinter->SetFont( aOldFont );
2376  pPrinter->SetMapMode( aOldMode );
2377  int nZeroCount = static_cast<int>(mrDoc.GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth);
2378  PushInt( nZeroCount );
2379  }
2380  else if( aInfoType == "PREFIX" )
2381  { // ' = left; " = right; ^ = centered
2382  sal_Unicode c = 0;
2383  if (aCell.hasString())
2384  {
2385  const SvxHorJustifyItem* pJustAttr = mrDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY );
2386  switch( pJustAttr->GetValue() )
2387  {
2388  case SvxCellHorJustify::Standard:
2389  case SvxCellHorJustify::Left:
2390  case SvxCellHorJustify::Block: c = '\''; break;
2391  case SvxCellHorJustify::Center: c = '^'; break;
2392  case SvxCellHorJustify::Right: c = '"'; break;
2393  case SvxCellHorJustify::Repeat: c = '\\'; break;
2394  }
2395  }
2396  PushString( OUString(c) );
2397  }
2398  else if( aInfoType == "PROTECT" )
2399  { // 1 = cell locked
2400  const ScProtectionAttr* pProtAttr = mrDoc.GetAttr( aCellPos, ATTR_PROTECTION );
2401  PushInt( pProtAttr->GetProtection() ? 1 : 0 );
2402  }
2403 
2404 // *** FORMATTING ***
2405  else if( aInfoType == "FORMAT" )
2406  { // specific format code for standard formats
2407  OUString aFuncResult;
2408  sal_uInt32 nFormat = mrDoc.GetNumberFormat( aCellPos );
2409  getFormatString(pFormatter, nFormat, aFuncResult);
2410  PushString( aFuncResult );
2411  }
2412  else if( aInfoType == "COLOR" )
2413  { // 1 = negative values are colored, otherwise 0
2414  const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
2415  PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 );
2416  }
2417  else if( aInfoType == "PARENTHESES" )
2418  { // 1 = format string contains a '(' character, otherwise 0
2419  const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
2420  PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 );
2421  }
2422  else
2423  PushIllegalArgument();
2424  }
2425 }
2426 
2428 {
2429  sal_uInt16 nFileId;
2430  OUString aTabName;
2431  ScSingleRefData aRef;
2434  PopExternalSingleRef(nFileId, aTabName, aRef, pToken, &aFmt);
2435  if (nGlobalError != FormulaError::NONE)
2436  {
2437  PushError( nGlobalError);
2438  return;
2439  }
2440 
2441  OUString aInfoType = GetString().getString();
2442  if (nGlobalError != FormulaError::NONE)
2443  {
2444  PushError( nGlobalError);
2445  return;
2446  }
2447 
2448  SCCOL nCol;
2449  SCROW nRow;
2450  SCTAB nTab;
2451  aRef.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like.
2452  SingleRefToVars(aRef, nCol, nRow, nTab);
2453  if (nGlobalError != FormulaError::NONE)
2454  {
2455  PushIllegalParameter();
2456  return;
2457  }
2458  aRef.SetAbsTab(-1); // revert the value.
2459 
2461  ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
2462 
2463  if ( aInfoType == "COL" )
2464  PushInt(nCol + 1);
2465  else if ( aInfoType == "ROW" )
2466  PushInt(nRow + 1);
2467  else if ( aInfoType == "SHEET" )
2468  {
2469  // For SHEET, No idea what number we should set, but let's always set
2470  // 1 if the external sheet exists, no matter what sheet. Excel does
2471  // the same.
2472  if (pRefMgr->getCacheTable(nFileId, aTabName, false))
2473  PushInt(1);
2474  else
2475  SetError(FormulaError::NoName);
2476  }
2477  else if ( aInfoType == "ADDRESS" )
2478  {
2479  // ODF 1.2 says we need to always display address using the ODF A1 grammar.
2480  ScTokenArray aArray(mrDoc);
2481  aArray.AddExternalSingleReference(nFileId, svl::SharedString( aTabName), aRef); // string not interned
2482  ScCompiler aComp(mrDoc, aPos, aArray, formula::FormulaGrammar::GRAM_ODFF_A1);
2483  OUString aStr;
2484  aComp.CreateStringFromTokenArray(aStr);
2485  PushString(aStr);
2486  }
2487  else if ( aInfoType == "FILENAME" )
2488  {
2489  // 'file URI'#$SheetName
2490 
2491  const OUString* p = pRefMgr->getExternalFileName(nFileId);
2492  if (!p)
2493  {
2494  // In theory this should never happen...
2495  SetError(FormulaError::NoName);
2496  return;
2497  }
2498 
2499  OUString aBuf = "'" + *p + "'#$" + aTabName;
2500  PushString(aBuf);
2501  }
2502  else if ( aInfoType == "CONTENTS" )
2503  {
2504  switch (pToken->GetType())
2505  {
2506  case svString:
2507  PushString(pToken->GetString());
2508  break;
2509  case svDouble:
2510  PushString(OUString::number(pToken->GetDouble()));
2511  break;
2512  case svError:
2513  PushString(ScGlobal::GetErrorString(pToken->GetError()));
2514  break;
2515  default:
2516  PushString(ScGlobal::GetEmptyOUString());
2517  }
2518  }
2519  else if ( aInfoType == "TYPE" )
2520  {
2521  sal_Unicode c = 'v';
2522  switch (pToken->GetType())
2523  {
2524  case svString:
2525  c = 'l';
2526  break;
2527  case svEmptyCell:
2528  c = 'b';
2529  break;
2530  default:
2531  ;
2532  }
2533  PushString(OUString(c));
2534  }
2535  else if ( aInfoType == "FORMAT" )
2536  {
2537  OUString aFmtStr;
2538  sal_uLong nFmt = aFmt.mbIsSet ? aFmt.mnIndex : 0;
2539  getFormatString(pFormatter, nFmt, aFmtStr);
2540  PushString(aFmtStr);
2541  }
2542  else if ( aInfoType == "COLOR" )
2543  {
2544  // 1 = negative values are colored, otherwise 0
2545  int nVal = 0;
2546  if (aFmt.mbIsSet)
2547  {
2548  const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2549  nVal = lcl_FormatHasNegColor(pFormat) ? 1 : 0;
2550  }
2551  PushInt(nVal);
2552  }
2553  else if ( aInfoType == "PARENTHESES" )
2554  {
2555  // 1 = format string contains a '(' character, otherwise 0
2556  int nVal = 0;
2557  if (aFmt.mbIsSet)
2558  {
2559  const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2560  nVal = lcl_FormatHasOpenPar(pFormat) ? 1 : 0;
2561  }
2562  PushInt(nVal);
2563  }
2564  else
2565  PushIllegalParameter();
2566 }
2567 
2569 {
2570  nFuncFmtType = SvNumFormatType::LOGICAL;
2571  bool bRes = false;
2572  switch ( GetStackType() )
2573  {
2574  case svSingleRef :
2575  {
2576  ScAddress aAdr;
2577  PopSingleRef( aAdr );
2578  if ( nGlobalError == FormulaError::NONE )
2579  bRes = true;
2580  }
2581  break;
2582  case svDoubleRef :
2583  {
2584  ScRange aRange;
2585  PopDoubleRef( aRange );
2586  if ( nGlobalError == FormulaError::NONE )
2587  bRes = true;
2588  }
2589  break;
2590  case svRefList :
2591  {
2592  FormulaConstTokenRef x = PopToken();
2593  if ( nGlobalError == FormulaError::NONE )
2594  bRes = !x->GetRefList()->empty();
2595  }
2596  break;
2597  case svExternalSingleRef:
2598  {
2600  PopExternalSingleRef(pToken);
2601  if (nGlobalError == FormulaError::NONE)
2602  bRes = true;
2603  }
2604  break;
2605  case svExternalDoubleRef:
2606  {
2608  PopExternalDoubleRef(pArray);
2609  if (nGlobalError == FormulaError::NONE)
2610  bRes = true;
2611  }
2612  break;
2613  default:
2614  Pop();
2615  }
2616  nGlobalError = FormulaError::NONE;
2617  PushInt( int(bRes) );
2618 }
2619 
2621 {
2622  nFuncFmtType = SvNumFormatType::LOGICAL;
2623  bool bRes = false;
2624  switch ( GetRawStackType() )
2625  {
2626  case svDouble:
2627  Pop();
2628  bRes = true;
2629  break;
2630  case svDoubleRef :
2631  case svSingleRef :
2632  {
2633  ScAddress aAdr;
2634  if ( !PopDoubleRefOrSingleRef( aAdr ) )
2635  break;
2636 
2637  ScRefCellValue aCell(mrDoc, aAdr);
2638  if (GetCellErrCode(aCell) == FormulaError::NONE)
2639  {
2640  switch (aCell.meType)
2641  {
2642  case CELLTYPE_VALUE :
2643  bRes = true;
2644  break;
2645  case CELLTYPE_FORMULA :
2646  bRes = (aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty());
2647  break;
2648  default:
2649  ; // nothing
2650  }
2651  }
2652  }
2653  break;
2654  case svExternalSingleRef:
2655  {
2657  PopExternalSingleRef(pToken);
2658  if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
2659  bRes = true;
2660  }
2661  break;
2662  case svExternalDoubleRef:
2663  case svMatrix:
2664  {
2665  ScMatrixRef pMat = GetMatrix();
2666  if ( !pMat )
2667  ; // nothing
2668  else if ( !pJumpMatrix )
2669  {
2670  if (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NONE)
2671  bRes = pMat->IsValue( 0, 0);
2672  }
2673  else
2674  {
2675  SCSIZE nCols, nRows, nC, nR;
2676  pMat->GetDimensions( nCols, nRows);
2677  pJumpMatrix->GetPos( nC, nR);
2678  if ( nC < nCols && nR < nRows )
2679  if (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NONE)
2680  bRes = pMat->IsValue( nC, nR);
2681  }
2682  }
2683  break;
2684  default:
2685  Pop();
2686  }
2687  nGlobalError = FormulaError::NONE;
2688  PushInt( int(bRes) );
2689 }
2690 
2692 {
2693  nFuncFmtType = SvNumFormatType::LOGICAL;
2694  bool bRes = false;
2695  switch ( GetStackType() )
2696  {
2697  case svDoubleRef :
2698  if (IsInArrayContext())
2699  {
2700  SCCOL nCol1, nCol2;
2701  SCROW nRow1, nRow2;
2702  SCTAB nTab1, nTab2;
2703  PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2704  if (nGlobalError != FormulaError::NONE)
2705  {
2706  PushError( nGlobalError);
2707  return;
2708  }
2709  if (nTab1 != nTab2)
2710  {
2711  PushIllegalArgument();
2712  return;
2713  }
2714 
2715  ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCol2 - nCol1 + 1),
2716  static_cast<SCSIZE>(nRow2 - nRow1 + 1), true);
2717  if (!pResMat)
2718  {
2719  PushError( FormulaError::MatrixSize);
2720  return;
2721  }
2722 
2723  /* TODO: we really should have a gap-aware cell iterator. */
2724  SCSIZE i=0, j=0;
2725  ScAddress aAdr( 0, 0, nTab1);
2726  for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2727  {
2728  aAdr.SetCol(nCol);
2729  for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2730  {
2731  aAdr.SetRow(nRow);
2732  ScRefCellValue aCell(mrDoc, aAdr);
2733  pResMat->PutBoolean( (aCell.meType == CELLTYPE_FORMULA), i,j);
2734  ++j;
2735  }
2736  ++i;
2737  j = 0;
2738  }
2739 
2740  PushMatrix( pResMat);
2741  return;
2742  }
2743  [[fallthrough]];
2744  case svSingleRef :
2745  {
2746  ScAddress aAdr;
2747  if ( !PopDoubleRefOrSingleRef( aAdr ) )
2748  break;
2749 
2750  bRes = (mrDoc.GetCellType(aAdr) == CELLTYPE_FORMULA);
2751  }
2752  break;
2753  default:
2754  Pop();
2755  }
2756  nGlobalError = FormulaError::NONE;
2757  PushInt( int(bRes) );
2758 }
2759 
2761 {
2762  OUString aFormula;
2763  switch ( GetStackType() )
2764  {
2765  case svDoubleRef :
2766  if (IsInArrayContext())
2767  {
2768  SCCOL nCol1, nCol2;
2769  SCROW nRow1, nRow2;
2770  SCTAB nTab1, nTab2;
2771  PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2772  if (nGlobalError != FormulaError::NONE)
2773  break;
2774 
2775  if (nTab1 != nTab2)
2776  {
2777  SetError( FormulaError::IllegalArgument);
2778  break;
2779  }
2780 
2781  ScMatrixRef pResMat = GetNewMat( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1, true);
2782  if (!pResMat)
2783  break;
2784 
2785  /* TODO: use a column iterator instead? */
2786  SCSIZE i=0, j=0;
2787  ScAddress aAdr(0,0,nTab1);
2788  for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2789  {
2790  aAdr.SetCol(nCol);
2791  for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2792  {
2793  aAdr.SetRow(nRow);
2794  ScRefCellValue aCell(mrDoc, aAdr);
2795  switch (aCell.meType)
2796  {
2797  case CELLTYPE_FORMULA :
2799  pResMat->PutString( mrStrPool.intern( aFormula), i,j);
2800  break;
2801  default:
2802  pResMat->PutError( FormulaError::NotAvailable, i,j);
2803  }
2804  ++j;
2805  }
2806  ++i;
2807  j = 0;
2808  }
2809 
2810  PushMatrix( pResMat);
2811  return;
2812  }
2813  [[fallthrough]];
2814  case svSingleRef :
2815  {
2816  ScAddress aAdr;
2817  if ( !PopDoubleRefOrSingleRef( aAdr ) )
2818  break;
2819 
2820  ScRefCellValue aCell(mrDoc, aAdr);
2821  switch (aCell.meType)
2822  {
2823  case CELLTYPE_FORMULA :
2825  break;
2826  default:
2827  SetError( FormulaError::NotAvailable );
2828  }
2829  }
2830  break;
2831  default:
2832  Pop();
2833  SetError( FormulaError::NotAvailable );
2834  }
2835  PushString( aFormula );
2836 }
2837 
2839 {
2840  nFuncFmtType = SvNumFormatType::LOGICAL;
2841  bool bRes = false;
2842  switch ( GetStackType() )
2843  {
2844  case svDoubleRef :
2845  case svSingleRef :
2846  {
2847  ScAddress aAdr;
2848  bool bOk = PopDoubleRefOrSingleRef( aAdr );
2849  if ( nGlobalError == FormulaError::NotAvailable )
2850  bRes = true;
2851  else if (bOk)
2852  {
2853  ScRefCellValue aCell(mrDoc, aAdr);
2854  FormulaError nErr = GetCellErrCode(aCell);
2855  bRes = (nErr == FormulaError::NotAvailable);
2856  }
2857  }
2858  break;
2859  case svExternalSingleRef:
2860  {
2862  PopExternalSingleRef(pToken);
2863  if (nGlobalError == FormulaError::NotAvailable ||
2864  (pToken && pToken->GetType() == svError && pToken->GetError() == FormulaError::NotAvailable))
2865  bRes = true;
2866  }
2867  break;
2868  case svExternalDoubleRef:
2869  case svMatrix:
2870  {
2871  ScMatrixRef pMat = GetMatrix();
2872  if ( !pMat )
2873  ; // nothing
2874  else if ( !pJumpMatrix )
2875  bRes = (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NotAvailable);
2876  else
2877  {
2878  SCSIZE nCols, nRows, nC, nR;
2879  pMat->GetDimensions( nCols, nRows);
2880  pJumpMatrix->GetPos( nC, nR);
2881  if ( nC < nCols && nR < nRows )
2882  bRes = (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NotAvailable);
2883  }
2884  }
2885  break;
2886  default:
2887  PopError();
2888  if ( nGlobalError == FormulaError::NotAvailable )
2889  bRes = true;
2890  }
2891  nGlobalError = FormulaError::NONE;
2892  PushInt( int(bRes) );
2893 }
2894 
2896 {
2897  nFuncFmtType = SvNumFormatType::LOGICAL;
2898  bool bRes = false;
2899  switch ( GetStackType() )
2900  {
2901  case svDoubleRef :
2902  case svSingleRef :
2903  {
2904  ScAddress aAdr;
2905  bool bOk = PopDoubleRefOrSingleRef( aAdr );
2906  if ( !bOk || (nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) )
2907  bRes = true;
2908  else
2909  {
2910  ScRefCellValue aCell(mrDoc, aAdr);
2911  FormulaError nErr = GetCellErrCode(aCell);
2912  bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2913  }
2914  }
2915  break;
2916  case svExternalSingleRef:
2917  {
2919  PopExternalSingleRef(pToken);
2920  if ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pToken ||
2921  (pToken->GetType() == svError && pToken->GetError() != FormulaError::NotAvailable))
2922  bRes = true;
2923  }
2924  break;
2925  case svExternalDoubleRef:
2926  case svMatrix:
2927  {
2928  ScMatrixRef pMat = GetMatrix();
2929  if ( nGlobalError != FormulaError::NONE || !pMat )
2930  bRes = ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pMat);
2931  else if ( !pJumpMatrix )
2932  {
2933  FormulaError nErr = pMat->GetErrorIfNotString( 0, 0);
2934  bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2935  }
2936  else
2937  {
2938  SCSIZE nCols, nRows, nC, nR;
2939  pMat->GetDimensions( nCols, nRows);
2940  pJumpMatrix->GetPos( nC, nR);
2941  if ( nC < nCols && nR < nRows )
2942  {
2943  FormulaError nErr = pMat->GetErrorIfNotString( nC, nR);
2944  bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2945  }
2946  }
2947  }
2948  break;
2949  default:
2950  PopError();
2951  if ( nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable )
2952  bRes = true;
2953  }
2954  nGlobalError = FormulaError::NONE;
2955  PushInt( int(bRes) );
2956 }
2957 
2959 {
2960  nFuncFmtType = SvNumFormatType::LOGICAL;
2961  bool bRes = false;
2962  switch ( GetStackType() )
2963  {
2964  case svDoubleRef :
2965  case svSingleRef :
2966  {
2967  ScAddress aAdr;
2968  if ( !PopDoubleRefOrSingleRef( aAdr ) )
2969  {
2970  bRes = true;
2971  break;
2972  }
2973  if ( nGlobalError != FormulaError::NONE )
2974  bRes = true;
2975  else
2976  {
2977  ScRefCellValue aCell(mrDoc, aAdr);
2978  bRes = (GetCellErrCode(aCell) != FormulaError::NONE);
2979  }
2980  }
2981  break;
2982  case svExternalSingleRef:
2983  {
2985  PopExternalSingleRef(pToken);
2986  if (nGlobalError != FormulaError::NONE || pToken->GetType() == svError)
2987  bRes = true;
2988  }
2989  break;
2990  case svExternalDoubleRef:
2991  case svMatrix:
2992  {
2993  ScMatrixRef pMat = GetMatrix();
2994  if ( nGlobalError != FormulaError::NONE || !pMat )
2995  bRes = true;
2996  else if ( !pJumpMatrix )
2997  bRes = (pMat->GetErrorIfNotString( 0, 0) != FormulaError::NONE);
2998  else
2999  {
3000  SCSIZE nCols, nRows, nC, nR;
3001  pMat->GetDimensions( nCols, nRows);
3002  pJumpMatrix->GetPos( nC, nR);
3003  if ( nC < nCols && nR < nRows )
3004  bRes = (pMat->GetErrorIfNotString( nC, nR) != FormulaError::NONE);
3005  }
3006  }
3007  break;
3008  default:
3009  PopError();
3010  if ( nGlobalError != FormulaError::NONE )
3011  bRes = true;
3012  }
3013  nGlobalError = FormulaError::NONE;
3014  PushInt( int(bRes) );
3015 }
3016 
3018 {
3019  nFuncFmtType = SvNumFormatType::LOGICAL;
3020  bool bRes = false;
3021  double fVal = 0.0;
3022  switch ( GetStackType() )
3023  {
3024  case svDoubleRef :
3025  case svSingleRef :
3026  {
3027  ScAddress aAdr;
3028  if ( !PopDoubleRefOrSingleRef( aAdr ) )
3029  break;
3030 
3031  ScRefCellValue aCell(mrDoc, aAdr);
3032  FormulaError nErr = GetCellErrCode(aCell);
3033  if (nErr != FormulaError::NONE)
3034  SetError(nErr);
3035  else
3036  {
3037  switch (aCell.meType)
3038  {
3039  case CELLTYPE_VALUE :
3040  fVal = GetCellValue(aAdr, aCell);
3041  bRes = true;
3042  break;
3043  case CELLTYPE_FORMULA :
3044  if (aCell.mpFormula->IsValue())
3045  {
3046  fVal = GetCellValue(aAdr, aCell);
3047  bRes = true;
3048  }
3049  break;
3050  default:
3051  ; // nothing
3052  }
3053  }
3054  }
3055  break;
3056  case svDouble:
3057  {
3058  fVal = PopDouble();
3059  bRes = true;
3060  }
3061  break;
3062  case svExternalSingleRef:
3063  {
3065  PopExternalSingleRef(pToken);
3066  if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
3067  {
3068  fVal = pToken->GetDouble();
3069  bRes = true;
3070  }
3071  }
3072  break;
3073  case svExternalDoubleRef:
3074  case svMatrix:
3075  {
3076  ScMatrixRef pMat = GetMatrix();
3077  if ( !pMat )
3078  ; // nothing
3079  else if ( !pJumpMatrix )
3080  {
3081  bRes = pMat->IsValue( 0, 0);
3082  if ( bRes )
3083  fVal = pMat->GetDouble( 0, 0);
3084  }
3085  else
3086  {
3087  SCSIZE nCols, nRows, nC, nR;
3088  pMat->GetDimensions( nCols, nRows);
3089  pJumpMatrix->GetPos( nC, nR);
3090  if ( nC < nCols && nR < nRows )
3091  {
3092  bRes = pMat->IsValue( nC, nR);
3093  if ( bRes )
3094  fVal = pMat->GetDouble( nC, nR);
3095  }
3096  else
3097  SetError( FormulaError::NoValue);
3098  }
3099  }
3100  break;
3101  default:
3102  ; // nothing
3103  }
3104  if ( !bRes )
3105  SetError( FormulaError::IllegalParameter);
3106  else
3107  bRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 );
3108  return bRes;
3109 }
3110 
3112 {
3113  PushInt( int(IsEven()) );
3114 }
3115 
3117 {
3118  PushInt( int(!IsEven()) );
3119 }
3120 
3122 {
3123  FormulaError nErr = nGlobalError;
3124  nGlobalError = FormulaError::NONE;
3125  // Temporarily override the ConvertStringToValue() error for
3126  // GetCellValue() / GetCellValueOrZero()
3127  FormulaError nSErr = mnStringNoValueError;
3128  mnStringNoValueError = FormulaError::CellNoValue;
3129  double fVal = GetDouble();
3130  mnStringNoValueError = nSErr;
3131  if (nErr != FormulaError::NONE)
3132  nGlobalError = nErr; // preserve previous error if any
3133  else if (nGlobalError == FormulaError::CellNoValue)
3134  nGlobalError = FormulaError::NONE; // reset temporary detection error
3135  PushDouble(fVal);
3136 }
3137 
3139 {
3140  // Doesn't only trim but also removes duplicated blanks within!
3141  OUString aVal = comphelper::string::strip(GetString().getString(), ' ');
3142  OUStringBuffer aStr;
3143  const sal_Unicode* p = aVal.getStr();
3144  const sal_Unicode* const pEnd = p + aVal.getLength();
3145  while ( p < pEnd )
3146  {
3147  if ( *p != ' ' || p[-1] != ' ' ) // first can't be ' ', so -1 is fine
3148  aStr.append(*p);
3149  p++;
3150  }
3151  PushString(aStr.makeStringAndClear());
3152 }
3153 
3155 {
3156  OUString aString = ScGlobal::getCharClassPtr()->uppercase(GetString().getString());
3157  PushString(aString);
3158 }
3159 
3161 {
3162 //2do: what to do with I18N-CJK ?!?
3163  OUStringBuffer aStr(GetString().getString());
3164  const sal_Int32 nLen = aStr.getLength();
3165  if ( nLen > 0 )
3166  {
3167  OUString aUpr(ScGlobal::getCharClassPtr()->uppercase(aStr.toString()));
3168  OUString aLwr(ScGlobal::getCharClassPtr()->lowercase(aStr.toString()));
3169  aStr[0] = aUpr[0];
3170  sal_Int32 nPos = 1;
3171  while( nPos < nLen )
3172  {
3173  OUString aTmpStr( aStr[nPos-1] );
3174  if ( !ScGlobal::getCharClassPtr()->isLetter( aTmpStr, 0 ) )
3175  aStr[nPos] = aUpr[nPos];
3176  else
3177  aStr[nPos] = aLwr[nPos];
3178  ++nPos;
3179  }
3180  }
3181  PushString(aStr.makeStringAndClear());
3182 }
3183 
3185 {
3186  OUString aString = ScGlobal::getCharClassPtr()->lowercase(GetString().getString());
3187  PushString(aString);
3188 }
3189 
3191 {
3192  OUString aStr = GetString().getString();
3193  sal_Int32 nIdx = 0;
3194  sal_Int32 nCnt = 0;
3195  while ( nIdx < aStr.getLength() )
3196  {
3197  aStr.iterateCodePoints( &nIdx );
3198  ++nCnt;
3199  }
3200  PushDouble( nCnt );
3201 }
3202 
3204 {
3205  switch ( GetStackType() )
3206  {
3207  case svDoubleRef :
3208  case svSingleRef :
3209  {
3210  ScAddress aAdr;
3211  if ( !PopDoubleRefOrSingleRef( aAdr ) )
3212  {
3213  PushInt(0);
3214  return ;
3215  }
3216  bool bValue = false;
3217  ScRefCellValue aCell(mrDoc, aAdr);
3218  if (GetCellErrCode(aCell) == FormulaError::NONE)
3219  {
3220  switch (aCell.meType)
3221  {
3222  case CELLTYPE_VALUE :
3223  bValue = true;
3224  break;
3225  case CELLTYPE_FORMULA :
3226  bValue = aCell.mpFormula->IsValue();
3227  break;
3228  default:
3229  ; // nothing
3230  }
3231  }
3232  if ( bValue )
3233  PushString(EMPTY_OUSTRING);
3234  else
3235  {
3236  // like GetString()
3238  GetCellString(aStr, aCell);
3239  PushString(aStr);
3240  }
3241  }
3242  break;
3243  case svMatrix:
3244  case svExternalSingleRef:
3245  case svExternalDoubleRef:
3246  {
3247  double fVal;
3249  ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
3250  if (ScMatrix::IsValueType( nMatValType))
3251  PushString(svl::SharedString::getEmptyString());
3252  else
3253  PushString( aStr);
3254  }
3255  break;
3256  case svDouble :
3257  {
3258  PopError();
3259  PushString( EMPTY_OUSTRING );
3260  }
3261  break;
3262  case svString :
3263  ; // leave on stack
3264  break;
3265  default :
3266  PushError( FormulaError::UnknownOpCode);
3267  }
3268 }
3269 
3271 {
3272  OUString aInputString;
3273  double fVal;
3274 
3275  switch ( GetRawStackType() )
3276  {
3277  case svMissing:
3278  case svEmptyCell:
3279  Pop();
3280  PushInt(0);
3281  return;
3282  case svDouble:
3283  return; // leave on stack
3284 
3285  case svSingleRef:
3286  case svDoubleRef:
3287  {
3288  ScAddress aAdr;
3289  if ( !PopDoubleRefOrSingleRef( aAdr ) )
3290  {
3291  PushInt(0);
3292  return;
3293  }
3294  ScRefCellValue aCell(mrDoc, aAdr);
3295  if (aCell.hasString())
3296  {
3297  svl::SharedString aSS;
3298  GetCellString(aSS, aCell);
3299  aInputString = aSS.getString();
3300  }
3301  else if (aCell.hasNumeric())
3302  {
3303  PushDouble( GetCellValue(aAdr, aCell) );
3304  return;
3305  }
3306  else
3307  {
3308  PushDouble(0.0);
3309  return;
3310  }
3311  }
3312  break;
3313  case svMatrix:
3314  {
3315  svl::SharedString aSS;
3316  ScMatValType nType = GetDoubleOrStringFromMatrix( fVal,
3317  aSS);
3318  aInputString = aSS.getString();
3319  switch (nType)
3320  {
3321  case ScMatValType::Empty:
3322  fVal = 0.0;
3323  [[fallthrough]];
3324  case ScMatValType::Value:
3325  case ScMatValType::Boolean:
3326  PushDouble( fVal);
3327  return;
3328  case ScMatValType::String:
3329  // evaluated below
3330  break;
3331  default:
3332  PushIllegalArgument();
3333  }
3334  }
3335  break;
3336  default:
3337  aInputString = GetString().getString();
3338  break;
3339  }
3340 
3341  sal_uInt32 nFIndex = 0; // 0 for default locale
3342  if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
3343  PushDouble(fVal);
3344  else
3345  PushIllegalArgument();
3346 }
3347 
3348 // fdo#57180
3350 {
3351 
3352  sal_uInt8 nParamCount = GetByte();
3353  if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
3354  return;
3355 
3356  OUString aInputString;
3357  OUString aGroupSeparator;
3358  sal_Unicode cDecimalSeparator = 0;
3359 
3360  if ( nParamCount == 3 )
3361  aGroupSeparator = GetString().getString();
3362 
3363  if ( nParamCount >= 2 )
3364  {
3365  OUString aDecimalSeparator = GetString().getString();
3366  if ( aDecimalSeparator.getLength() == 1 )
3367  cDecimalSeparator = aDecimalSeparator[ 0 ];
3368  else
3369  {
3370  PushIllegalArgument(); //if given, separator length must be 1
3371  return;
3372  }
3373  }
3374 
3375  if ( cDecimalSeparator && aGroupSeparator.indexOf( cDecimalSeparator ) != -1 )
3376  {
3377  PushIllegalArgument(); //decimal separator cannot appear in group separator
3378  return;
3379  }
3380 
3381  switch (GetStackType())
3382  {
3383  case svDouble:
3384  return; // leave on stack
3385  default:
3386  aInputString = GetString().getString();
3387  }
3388  if ( nGlobalError != FormulaError::NONE )
3389  {
3390  PushError( nGlobalError );
3391  return;
3392  }
3393  if ( aInputString.isEmpty() )
3394  {
3395  if ( maCalcConfig.mbEmptyStringAsZero )
3396  PushDouble( 0.0 );
3397  else
3398  PushNoValue();
3399  return;
3400  }
3401 
3402  sal_Int32 nDecSep = aInputString.indexOf( cDecimalSeparator );
3403  if ( nDecSep != 0 )
3404  {
3405  OUString aTemporary( nDecSep >= 0 ? aInputString.copy( 0, nDecSep ) : aInputString );
3406  sal_Int32 nIndex = 0;
3407  while (nIndex < aGroupSeparator.getLength())
3408  {
3409  sal_uInt32 nChar = aGroupSeparator.iterateCodePoints( &nIndex );
3410  aTemporary = aTemporary.replaceAll( OUString( &nChar, 1 ), "" );
3411  }
3412  if ( nDecSep >= 0 )
3413  aInputString = aTemporary + aInputString.subView( nDecSep );
3414  else
3415  aInputString = aTemporary;
3416  }
3417 
3418  for ( sal_Int32 i = aInputString.getLength(); --i >= 0; )
3419  {
3420  sal_Unicode c = aInputString[ i ];
3421  if ( c == 0x0020 || c == 0x0009 || c == 0x000A || c == 0x000D )
3422  aInputString = aInputString.replaceAt( i, 1, "" ); // remove spaces etc.
3423  }
3424  sal_Int32 nPercentCount = 0;
3425  for ( sal_Int32 i = aInputString.getLength() - 1; i >= 0 && aInputString[ i ] == 0x0025; i-- )
3426  {
3427  aInputString = aInputString.replaceAt( i, 1, "" ); // remove and count trailing '%'
3428  nPercentCount++;
3429  }
3430 
3431  rtl_math_ConversionStatus eStatus;
3432  sal_Int32 nParseEnd;
3433  double fVal = ::rtl::math::stringToDouble( aInputString, cDecimalSeparator, 0, &eStatus, &nParseEnd );
3434  if ( eStatus == rtl_math_ConversionStatus_Ok && nParseEnd == aInputString.getLength() )
3435  {
3436  if (nPercentCount)
3437  fVal *= pow( 10.0, -(nPercentCount * 2)); // process '%' from input string
3438  PushDouble(fVal);
3439  return;
3440  }
3441  PushNoValue();
3442 }
3443 
3444 //2do: this should be a proper unicode string method
3446 {
3447  return 0x20 <= c && c != 0x7f;
3448 }
3449 
3451 {
3452  OUString aStr = GetString().getString();
3453  for ( sal_Int32 i = 0; i < aStr.getLength(); i++ )
3454  {
3455  if ( !lcl_ScInterpreter_IsPrintable( aStr[i] ) )
3456  aStr = aStr.replaceAt(i,1,"");
3457  }
3458  PushString(aStr);
3459 }
3460 
3462 {
3463 //2do: make it full range unicode?
3464  OUString aStr = GetString().getString();
3465  if (aStr.isEmpty())
3466  PushInt(0);
3467  else
3468  {
3469  //"classic" ByteString conversion flags
3470  const sal_uInt32 convertFlags =
3471  RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE |
3472  RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE |
3473  RTL_UNICODETOTEXT_FLAGS_FLUSH |
3474  RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT |
3475  RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT |
3476  RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE;
3477  PushInt( static_cast<unsigned char>(OUStringToOString(OUStringChar(aStr[0]), osl_getThreadTextEncoding(), convertFlags).toChar()) );
3478  }
3479 }
3480 
3482 {
3483 //2do: make it full range unicode?
3484  double fVal = GetDouble();
3485  if (fVal < 0.0 || fVal >= 256.0)
3486  PushIllegalArgument();
3487  else
3488  {
3489  //"classic" ByteString conversion flags
3490  const sal_uInt32 convertFlags =
3491  RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
3492  RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
3493  RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT;
3494 
3495  char cEncodedChar = static_cast<char>(fVal);
3496  OUString aStr(&cEncodedChar, 1, osl_getThreadTextEncoding(), convertFlags);
3497  PushString(aStr);
3498  }
3499 }
3500 
3501 /* #i70213# fullwidth/halfwidth conversion provided by
3502  * Takashi Nakamoto <bluedwarf@ooo>
3503  * erAck: added Excel compatibility conversions as seen in issue's test case. */
3504 
3505 static OUString lcl_convertIntoHalfWidth( const OUString & rStr )
3506 {
3507  // Make the initialization thread-safe. Since another function needs to be called, move it all to another
3508  // function and thread-safely initialize a static reference in this function.
3509  auto init = []() -> utl::TransliterationWrapper&
3510  {
3511  static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3512  trans.loadModuleByImplName( "FULLWIDTH_HALFWIDTH_LIKE_ASC", LANGUAGE_SYSTEM );
3513  return trans;
3514  };
3515  static utl::TransliterationWrapper& aTrans( init());
3516  return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3517 }
3518 
3519 static OUString lcl_convertIntoFullWidth( const OUString & rStr )
3520 {
3521  auto init = []() -> utl::TransliterationWrapper&
3522  {
3523  static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3524  trans.loadModuleByImplName( "HALFWIDTH_FULLWIDTH_LIKE_JIS", LANGUAGE_SYSTEM );
3525  return trans;
3526  };
3527  static utl::TransliterationWrapper& aTrans( init());
3528  return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3529 }
3530 
3531 /* ODFF:
3532  * Summary: Converts half-width to full-width ASCII and katakana characters.
3533  * Semantics: Conversion is done for half-width ASCII and katakana characters,
3534  * other characters are simply copied from T to the result. This is the
3535  * complementary function to ASC.
3536  * For references regarding halfwidth and fullwidth characters see
3537  * http://www.unicode.org/reports/tr11/
3538  * http://www.unicode.org/charts/charindex2.html#H
3539  * http://www.unicode.org/charts/charindex2.html#F
3540  */
3542 {
3543  if (MustHaveParamCount( GetByte(), 1))
3544  PushString( lcl_convertIntoFullWidth( GetString().getString()));
3545 }
3546 
3547 /* ODFF:
3548  * Summary: Converts full-width to half-width ASCII and katakana characters.
3549  * Semantics: Conversion is done for full-width ASCII and katakana characters,
3550  * other characters are simply copied from T to the result. This is the
3551  * complementary function to JIS.
3552  */
3554 {
3555  if (MustHaveParamCount( GetByte(), 1))
3556  PushString( lcl_convertIntoHalfWidth( GetString().getString()));
3557 }
3558 
3560 {
3561  if ( MustHaveParamCount( GetByte(), 1 ) )
3562  {
3563  OUString aStr = GetString().getString();
3564  if (aStr.isEmpty())
3565  PushIllegalParameter();
3566  else
3567  {
3568  PushDouble(aStr.iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
3569  }
3570  }
3571 }
3572 
3574 {
3575  if ( MustHaveParamCount( GetByte(), 1 ) )
3576  {
3577  sal_uInt32 nCodePoint = GetUInt32();
3578  if (nGlobalError != FormulaError::NONE || !rtl::isUnicodeCodePoint(nCodePoint))
3579  PushIllegalArgument();
3580  else
3581  {
3582  OUString aStr( &nCodePoint, 1 );
3583  PushString( aStr );
3584  }
3585  }
3586 }
3587 
3588 bool ScInterpreter::SwitchToArrayRefList( ScMatrixRef& xResMat, SCSIZE nMatRows, double fCurrent,
3589  const std::function<void( SCSIZE i, double fCurrent )>& MatOpFunc, bool bDoMatOp )
3590 {
3591  const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
3592  if (!p || !p->IsArrayResult())
3593  return false;
3594 
3595  if (!xResMat)
3596  {
3597  // Create and init all elements with current value.
3598  assert(nMatRows > 0);
3599  xResMat = GetNewMat( 1, nMatRows, true);
3600  xResMat->FillDouble( fCurrent, 0,0, 0,nMatRows-1);
3601  }
3602  else if (bDoMatOp)
3603  {
3604  // Current value and values from vector are operands
3605  // for each vector position.
3606  for (SCSIZE i=0; i < nMatRows; ++i)
3607  {
3608  MatOpFunc( i, fCurrent);
3609  }
3610  }
3611  return true;
3612 }
3613 
3614 void ScInterpreter::ScMin( bool bTextAsZero )
3615 {
3616  short nParamCount = GetByte();
3617  if (!MustHaveParamCountMin( nParamCount, 1))
3618  return;
3619 
3620  ScMatrixRef xResMat;
3621  double nMin = ::std::numeric_limits<double>::max();
3622  auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMin )
3623  {
3624  double fVecRes = xResMat->GetDouble(0,i);
3625  if (fVecRes > fCurMin)
3626  xResMat->PutDouble( fCurMin, 0,i);
3627  };
3628  const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3629  size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3630 
3631  double nVal = 0.0;
3632  ScAddress aAdr;
3633  ScRange aRange;
3634  size_t nRefInList = 0;
3635  while (nParamCount-- > 0)
3636  {
3637  switch (GetStackType())
3638  {
3639  case svDouble :
3640  {
3641  nVal = GetDouble();
3642  if (nMin > nVal) nMin = nVal;
3643  nFuncFmtType = SvNumFormatType::NUMBER;
3644  }
3645  break;
3646  case svSingleRef :
3647  {
3648  PopSingleRef( aAdr );
3649  ScRefCellValue aCell(mrDoc, aAdr);
3650  if (aCell.hasNumeric())
3651  {
3652  nVal = GetCellValue(aAdr, aCell);
3653  CurFmtToFuncFmt();
3654  if (nMin > nVal) nMin = nVal;
3655  }
3656  else if (bTextAsZero && aCell.hasString())
3657  {
3658  if ( nMin > 0.0 )
3659  nMin = 0.0;
3660  }
3661  }
3662  break;
3663  case svRefList :
3664  {
3665  // bDoMatOp only for non-array value when switching to
3666  // ArrayRefList.
3667  if (SwitchToArrayRefList( xResMat, nMatRows, nMin, MatOpFunc,
3668  nRefArrayPos == std::numeric_limits<size_t>::max()))
3669  {
3670  nRefArrayPos = nRefInList;
3671  }
3672  }
3673  [[fallthrough]];
3674  case svDoubleRef :
3675  {
3676  FormulaError nErr = FormulaError::NONE;
3677  PopDoubleRef( aRange, nParamCount, nRefInList);
3678  ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
3679  aValIter.SetInterpreterContext( &mrContext );
3680  if (aValIter.GetFirst(nVal, nErr))
3681  {
3682  if (nMin > nVal)
3683  nMin = nVal;
3684  aValIter.GetCurNumFmtInfo( mrContext, nFuncFmtType, nFuncFmtIndex );
3685  while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3686  {
3687  if (nMin > nVal)
3688  nMin = nVal;
3689  }
3690  SetError(nErr);
3691  }
3692  if (nRefArrayPos != std::numeric_limits<size_t>::max())
3693  {
3694  // Update vector element with current value.
3695  MatOpFunc( nRefArrayPos, nMin);
3696 
3697  // Reset.
3698  nMin = std::numeric_limits<double>::max();
3699  nVal = 0.0;
3700  nRefArrayPos = std::numeric_limits<size_t>::max();
3701  }
3702  }
3703  break;
3704  case svMatrix :
3705  case svExternalSingleRef:
3706  case svExternalDoubleRef:
3707  {
3708  ScMatrixRef pMat = GetMatrix();
3709  if (pMat)
3710  {
3711  nFuncFmtType = SvNumFormatType::NUMBER;
3712  nVal = pMat->GetMinValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
3713  if (nMin > nVal)
3714  nMin = nVal;
3715  }
3716  }
3717  break;
3718  case svString :
3719  {
3720  Pop();
3721  if ( bTextAsZero )
3722  {
3723  if ( nMin > 0.0 )
3724  nMin = 0.0;
3725  }
3726  else
3727  SetError(FormulaError::IllegalParameter);
3728  }
3729  break;
3730  default :
3731  Pop();
3732  SetError(FormulaError::IllegalParameter);
3733  }
3734  }
3735 
3736  if (xResMat)
3737  {
3738  // Include value of last non-references-array type and calculate final result.
3739  if (nMin < std::numeric_limits<double>::max())
3740  {
3741  for (SCSIZE i=0; i < nMatRows; ++i)
3742  {
3743  MatOpFunc( i, nMin);
3744  }
3745  }
3746  else
3747  {
3748  /* TODO: the awkward "no value is minimum 0.0" is likely the case
3749  * if a value is numeric_limits::max. Still, that could be a valid
3750  * minimum value as well, but nVal and nMin had been reset after
3751  * the last svRefList... so we may lie here. */
3752  for (SCSIZE i=0; i < nMatRows; ++i)
3753  {
3754  double fVecRes = xResMat->GetDouble(0,i);
3755  if (fVecRes == std::numeric_limits<double>::max())
3756  xResMat->PutDouble( 0.0, 0,i);
3757  }
3758  }
3759  PushMatrix( xResMat);
3760  }
3761  else
3762  {
3763  if (!std::isfinite(nVal))
3764  PushError( GetDoubleErrorValue( nVal));
3765  else if ( nVal < nMin )
3766  PushDouble(0.0); // zero or only empty arguments
3767  else
3768  PushDouble(nMin);
3769  }
3770 }
3771 
3772 void ScInterpreter::ScMax( bool bTextAsZero )
3773 {
3774  short nParamCount = GetByte();
3775  if (!MustHaveParamCountMin( nParamCount, 1))
3776  return;
3777 
3778  ScMatrixRef xResMat;
3779  double nMax = std::numeric_limits<double>::lowest();
3780  auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMax )
3781  {
3782  double fVecRes = xResMat->GetDouble(0,i);
3783  if (fVecRes < fCurMax)
3784  xResMat->PutDouble( fCurMax, 0,i);
3785  };
3786  const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3787  size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3788 
3789  double nVal = 0.0;
3790  ScAddress aAdr;
3791  ScRange aRange;
3792  size_t nRefInList = 0;
3793  while (nParamCount-- > 0)
3794  {
3795  switch (GetStackType())
3796  {
3797  case svDouble :
3798  {
3799  nVal = GetDouble();
3800  if (nMax < nVal) nMax = nVal;
3801  nFuncFmtType = SvNumFormatType::NUMBER;
3802  }
3803  break;
3804  case svSingleRef :
3805  {
3806  PopSingleRef( aAdr );
3807  ScRefCellValue aCell(mrDoc, aAdr);
3808  if (aCell.hasNumeric())
3809  {
3810  nVal = GetCellValue(aAdr, aCell);
3811  CurFmtToFuncFmt();
3812  if (nMax < nVal) nMax = nVal;
3813  }
3814  else if (bTextAsZero && aCell.hasString())
3815  {
3816  if ( nMax < 0.0 )
3817  nMax = 0.0;
3818  }
3819  }
3820  break;
3821  case svRefList :
3822  {
3823  // bDoMatOp only for non-array value when switching to
3824  // ArrayRefList.
3825  if (SwitchToArrayRefList( xResMat, nMatRows, nMax, MatOpFunc,
3826  nRefArrayPos == std::numeric_limits<size_t>::max()))
3827  {
3828  nRefArrayPos = nRefInList;
3829  }
3830  }
3831  [[fallthrough]];
3832  case svDoubleRef :
3833  {
3834  FormulaError nErr = FormulaError::NONE;
3835  PopDoubleRef( aRange, nParamCount, nRefInList);
3836  ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
3837  aValIter.SetInterpreterContext( &mrContext );
3838  if (aValIter.GetFirst(nVal, nErr))
3839  {
3840  if (nMax < nVal)
3841  nMax = nVal;
3842  aValIter.GetCurNumFmtInfo( mrContext, nFuncFmtType, nFuncFmtIndex );
3843  while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3844  {
3845  if (nMax < nVal)
3846  nMax = nVal;
3847  }
3848  SetError(nErr);
3849  }
3850  if (nRefArrayPos != std::numeric_limits<size_t>::max())
3851  {
3852  // Update vector element with current value.
3853  MatOpFunc( nRefArrayPos, nMax);
3854 
3855  // Reset.
3856  nMax = std::numeric_limits<double>::lowest();
3857  nVal = 0.0;
3858  nRefArrayPos = std::numeric_limits<size_t>::max();
3859  }
3860  }
3861  break;
3862  case svMatrix :
3863  case svExternalSingleRef:
3864  case svExternalDoubleRef:
3865  {
3866  ScMatrixRef pMat = GetMatrix();
3867  if (pMat)
3868  {
3869  nFuncFmtType = SvNumFormatType::NUMBER;
3870  nVal = pMat->GetMaxValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
3871  if (nMax < nVal)
3872  nMax = nVal;
3873  }
3874  }
3875  break;
3876  case svString :
3877  {
3878  Pop();
3879  if ( bTextAsZero )
3880  {
3881  if ( nMax < 0.0 )
3882  nMax = 0.0;
3883  }
3884  else
3885  SetError(FormulaError::IllegalParameter);
3886  }
3887  break;
3888  default :
3889  Pop();
3890  SetError(FormulaError::IllegalParameter);
3891  }
3892  }
3893 
3894  if (xResMat)
3895  {
3896  // Include value of last non-references-array type and calculate final result.
3897  if (nMax > std::numeric_limits<double>::lowest())
3898  {
3899  for (SCSIZE i=0; i < nMatRows; ++i)
3900  {
3901  MatOpFunc( i, nMax);
3902  }
3903  }
3904  else
3905  {
3906  /* TODO: the awkward "no value is maximum 0.0" is likely the case
3907  * if a value is numeric_limits::lowest. Still, that could be a
3908  * valid maximum value as well, but nVal and nMax had been reset
3909  * after the last svRefList... so we may lie here. */
3910  for (SCSIZE i=0; i < nMatRows; ++i)
3911  {
3912  double fVecRes = xResMat->GetDouble(0,i);
3913  if (fVecRes == -std::numeric_limits<double>::max())
3914  xResMat->PutDouble( 0.0, 0,i);
3915  }
3916  }
3917  PushMatrix( xResMat);
3918  }
3919  else
3920  {
3921  if (!std::isfinite(nVal))
3922  PushError( GetDoubleErrorValue( nVal));
3923  else if ( nVal > nMax )
3924  PushDouble(0.0); // zero or only empty arguments
3925  else
3926  PushDouble(nMax);
3927  }
3928 }
3929 
3930 void ScInterpreter::GetStVarParams( bool bTextAsZero, double(*VarResult)( double fVal, size_t nValCount ) )
3931 {
3932  short nParamCount = GetByte();
3933  const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3934 
3935  struct ArrayRefListValue
3936  {
3937  std::vector<double> mvValues;
3938  double mfSum;
3939  ArrayRefListValue() : mfSum(0.0) {}
3940  };
3941  std::vector<ArrayRefListValue> vArrayValues;
3942 
3943  std::vector<double> values;
3944  double fSum = 0.0;
3945  double fVal = 0.0;
3946  ScAddress aAdr;
3947  ScRange aRange;
3948  size_t nRefInList = 0;
3949  while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
3950  {
3951  switch (GetStackType())
3952  {
3953  case svDouble :
3954  {
3955  fVal = GetDouble();
3956  if (nGlobalError == FormulaError::NONE)
3957  {
3958  values.push_back(fVal);
3959  fSum += fVal;
3960  }
3961  }
3962  break;
3963  case svSingleRef :
3964  {
3965  PopSingleRef( aAdr );
3966  ScRefCellValue aCell(mrDoc, aAdr);
3967  if (aCell.hasNumeric())
3968  {
3969  fVal = GetCellValue(aAdr, aCell);
3970  if (nGlobalError == FormulaError::NONE)
3971  {
3972  values.push_back(fVal);
3973  fSum += fVal;
3974  }
3975  }
3976  else if (bTextAsZero && aCell.hasString())
3977  {
3978  values.push_back(0.0);
3979  }
3980  }
3981  break;
3982  case svRefList :
3983  {
3984  const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
3985  if (p && p->IsArrayResult())
3986  {
3987  size_t nRefArrayPos = nRefInList;
3988  if (vArrayValues.empty())
3989  {
3990  // Create and init all elements with current value.
3991  assert(nMatRows > 0);
3992  vArrayValues.resize(nMatRows);
3993  for (auto & it : vArrayValues)
3994  {
3995  it.mvValues = values;
3996  it.mfSum = fSum;
3997  }
3998  }
3999  else
4000  {
4001  // Current value and values from vector are operands
4002  // for each vector position.
4003  for (auto & it : vArrayValues)
4004  {
4005  it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
4006  it.mfSum += fSum;
4007  }
4008  }
4009  ArrayRefListValue& rArrayValue = vArrayValues[nRefArrayPos];
4010  FormulaError nErr = FormulaError::NONE;
4011  PopDoubleRef( aRange, nParamCount, nRefInList);
4012  ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
4013  if (aValIter.GetFirst(fVal, nErr))
4014  {
4015  do
4016  {
4017  rArrayValue.mvValues.push_back(fVal);
4018  rArrayValue.mfSum += fVal;
4019  }
4020  while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4021  }
4022  if ( nErr != FormulaError::NONE )
4023  {
4024  rArrayValue.mfSum = CreateDoubleError( nErr);
4025  }
4026  // Reset.
4027  std::vector<double>().swap(values);
4028  fSum = 0.0;
4029  break;
4030  }
4031  }
4032  [[fallthrough]];
4033  case svDoubleRef :
4034  {
4035  FormulaError nErr = FormulaError::NONE;
4036  PopDoubleRef( aRange, nParamCount, nRefInList);
4037  ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
4038  if (aValIter.GetFirst(fVal, nErr))
4039  {
4040  do
4041  {
4042  values.push_back(fVal);
4043  fSum += fVal;
4044  }
4045  while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4046  }
4047  if ( nErr != FormulaError::NONE )
4048  {
4049  SetError(nErr);
4050  }
4051  }
4052  break;
4053  case svExternalSingleRef :
4054  case svExternalDoubleRef :
4055  case svMatrix :
4056  {
4057  ScMatrixRef pMat = GetMatrix();
4058  if (pMat)
4059  {
4060  const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal);
4061  SCSIZE nC, nR;
4062  pMat->GetDimensions(nC, nR);
4063  for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
4064  {
4065  for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
4066  {
4067  if (!pMat->IsStringOrEmpty(nMatCol,nMatRow))
4068  {
4069  fVal= pMat->GetDouble(nMatCol,nMatRow);
4070  if (nGlobalError == FormulaError::NONE)
4071  {
4072  values.push_back(fVal);
4073  fSum += fVal;
4074  }
4075  else if (bIgnoreErrVal)
4076  nGlobalError = FormulaError::NONE;
4077  }
4078  else if ( bTextAsZero )
4079  {
4080  values.push_back(0.0);
4081  }
4082  }
4083  }
4084  }
4085  }
4086  break;
4087  case svString :
4088  {
4089  Pop();
4090  if ( bTextAsZero )
4091  {
4092  values.push_back(0.0);
4093  }
4094  else
4095  SetError(FormulaError::IllegalParameter);
4096  }
4097  break;
4098  default :
4099  Pop();
4100  SetError(FormulaError::IllegalParameter);
4101  }
4102  }
4103 
4104  if (!vArrayValues.empty())
4105  {
4106  // Include value of last non-references-array type and calculate final result.
4107  if (!values.empty())
4108  {
4109  for (auto & it : vArrayValues)
4110  {
4111  it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
4112  it.mfSum += fSum;
4113  }
4114  }
4115  ScMatrixRef xResMat = GetNewMat( 1, nMatRows, true);
4116  for (SCSIZE r=0; r < nMatRows; ++r)
4117  {
4118  ::std::vector<double>::size_type n = vArrayValues[r].mvValues.size();
4119  if (!n)
4120  xResMat->PutError( FormulaError::DivisionByZero, 0, r);
4121  else
4122  {
4123  ArrayRefListValue& rArrayValue = vArrayValues[r];
4124  double vSum = 0.0;
4125  const double vMean = rArrayValue.mfSum / n;
4126  for (::std::vector<double>::size_type i = 0; i < n; i++)
4127  vSum += ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean) *
4128  ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean);
4129  xResMat->PutDouble( VarResult( vSum, n), 0, r);
4130  }
4131  }
4132  PushMatrix( xResMat);
4133  }
4134  else
4135  {
4136  ::std::vector<double>::size_type n = values.size();
4137  if (!n)
4138  SetError( FormulaError::DivisionByZero);
4139  double vSum = 0.0;
4140  if (nGlobalError == FormulaError::NONE)
4141  {
4142  const double vMean = fSum / n;
4143  for (::std::vector<double>::size_type i = 0; i < n; i++)
4144  vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean);
4145  }
4146  PushDouble( VarResult( vSum, n));
4147  }
4148 }
4149 
4150 void ScInterpreter::ScVar( bool bTextAsZero )
4151 {
4152  auto VarResult = []( double fVal, size_t nValCount )
4153  {
4154  if (nValCount <= 1)
4155  return CreateDoubleError( FormulaError::DivisionByZero );
4156  else
4157  return fVal / (nValCount - 1);
4158  };
4159  GetStVarParams( bTextAsZero, VarResult );
4160 }
4161 
4162 void ScInterpreter::ScVarP( bool bTextAsZero )
4163 {
4164  auto VarResult = []( double fVal, size_t nValCount )
4165  {
4166  return sc::div( fVal, nValCount);
4167  };
4168  GetStVarParams( bTextAsZero, VarResult );
4169 
4170 }
4171 
4172 void ScInterpreter::ScStDev( bool bTextAsZero )
4173 {
4174  auto VarResult = []( double fVal, size_t nValCount )
4175  {
4176  if (nValCount <= 1)
4177  return CreateDoubleError( FormulaError::DivisionByZero );
4178  else
4179  return sqrt( fVal / (nValCount - 1));
4180  };
4181  GetStVarParams( bTextAsZero, VarResult );
4182 }
4183 
4184 void ScInterpreter::ScStDevP( bool bTextAsZero )
4185 {
4186  auto VarResult = []( double fVal, size_t nValCount )
4187  {
4188  if (nValCount == 0)
4189  return CreateDoubleError( FormulaError::DivisionByZero );
4190  else
4191  return sqrt( fVal / nValCount);
4192  };
4193  GetStVarParams( bTextAsZero, VarResult );
4194 
4195  /* this was: PushDouble( sqrt( div( nVal, nValCount)));
4196  *
4197  * Besides that the special NAN gets lost in the call through sqrt(),
4198  * unxlngi6.pro then looped back and forth somewhere between div() and
4199  * ::rtl::math::setNan(). Tests showed that
4200  *
4201  * sqrt( div( 1, 0));
4202  *
4203  * produced a loop, but
4204  *
4205  * double f1 = div( 1, 0);
4206  * sqrt( f1 );
4207  *
4208  * was fine. There seems to be some compiler optimization problem. It does
4209  * not occur when compiled with debug=t.
4210  */
4211 }
4212 
4214 {
4215  sal_uInt8 nParamCount = GetByte();
4216  sal_uLong nVal = 0;
4217  SCCOL nCol1;
4218  SCROW nRow1;
4219  SCTAB nTab1;
4220  SCCOL nCol2;
4221  SCROW nRow2;
4222  SCTAB nTab2;
4223  while (nParamCount-- > 0)
4224  {
4225  switch ( GetStackType() )
4226  {
4227  case svSingleRef:
4228  PopError();
4229  nVal++;
4230  break;
4231  case svDoubleRef:
4232  PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4233  nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4234  static_cast<sal_uLong>(nCol2 - nCol1 + 1);
4235  break;
4236  case svMatrix:
4237  {
4238  ScMatrixRef pMat = PopMatrix();
4239  if (pMat)
4240  {
4241  SCSIZE nC, nR;
4242  pMat->GetDimensions(nC, nR);
4243  nVal += nC;
4244  }
4245  }
4246  break;
4247  case svExternalSingleRef:
4248  PopError();
4249  nVal++;
4250  break;
4251  case svExternalDoubleRef:
4252  {
4253  sal_uInt16 nFileId;
4254  OUString aTabName;
4255  ScComplexRefData aRef;
4256  PopExternalDoubleRef( nFileId, aTabName, aRef);
4257  ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4258  nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4259  static_cast<sal_uLong>(aAbs.aEnd.Col() - aAbs.aStart.Col() + 1);
4260  }
4261  break;
4262  default:
4263  PopError();
4264  SetError(FormulaError::IllegalParameter);
4265  }
4266  }
4267  PushDouble(static_cast<double>(nVal));
4268 }
4269 
4271 {
4272  sal_uInt8 nParamCount = GetByte();
4273  sal_uLong nVal = 0;
4274  SCCOL nCol1;
4275  SCROW nRow1;
4276  SCTAB nTab1;
4277  SCCOL nCol2;
4278  SCROW nRow2;
4279  SCTAB nTab2;
4280  while (nParamCount-- > 0)
4281  {
4282  switch ( GetStackType() )
4283  {
4284  case svSingleRef:
4285  PopError();
4286  nVal++;
4287  break;
4288  case svDoubleRef:
4289  PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4290  nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4291  static_cast<sal_uLong>(nRow2 - nRow1 + 1);
4292  break;
4293  case svMatrix:
4294  {
4295  ScMatrixRef pMat = PopMatrix();
4296  if (pMat)
4297  {
4298  SCSIZE nC, nR;
4299  pMat->GetDimensions(nC, nR);
4300  nVal += nR;
4301  }
4302  }
4303  break;
4304  case svExternalSingleRef:
4305  PopError();
4306  nVal++;
4307  break;
4308  case svExternalDoubleRef:
4309  {
4310  sal_uInt16 nFileId;
4311  OUString aTabName;
4312  ScComplexRefData aRef;
4313  PopExternalDoubleRef( nFileId, aTabName, aRef);
4314  ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4315  nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4316  static_cast<sal_uLong>(aAbs.aEnd.Row() - aAbs.aStart.Row() + 1);
4317  }
4318  break;
4319  default:
4320  PopError();
4321  SetError(FormulaError::IllegalParameter);
4322  }
4323  }
4324  PushDouble(static_cast<double>(nVal));
4325 }
4326 
4328 {
4329  sal_uInt8 nParamCount = GetByte();
4330  sal_uLong nVal;
4331  if ( nParamCount == 0 )
4332  nVal = mrDoc.GetTableCount();
4333  else
4334  {
4335  nVal = 0;
4336  SCCOL nCol1;
4337  SCROW nRow1;
4338  SCTAB nTab1;
4339  SCCOL nCol2;
4340  SCROW nRow2;
4341  SCTAB nTab2;
4342  while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
4343  {
4344  switch ( GetStackType() )
4345  {
4346  case svSingleRef:
4347  case svExternalSingleRef:
4348  PopError();
4349  nVal++;
4350  break;
4351  case svDoubleRef:
4352  PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4353  nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1);
4354  break;
4355  case svExternalDoubleRef:
4356  {
4357  sal_uInt16 nFileId;
4358  OUString aTabName;
4359  ScComplexRefData aRef;
4360  PopExternalDoubleRef( nFileId, aTabName, aRef);
4361  ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4362  nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1);
4363  }
4364  break;
4365  default:
4366  PopError();
4367  SetError( FormulaError::IllegalParameter );
4368  }
4369  }
4370  }
4371  PushDouble( static_cast<double>(nVal) );
4372 }
4373 
4375 {
4376  sal_uInt8 nParamCount = GetByte();
4377  if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4378  return;
4379 
4380  double nVal = 0.0;
4381  if (nParamCount == 0)
4382  {
4383  nVal = aPos.Col() + 1;
4384  if (bMatrixFormula)
4385  {
4386  SCCOL nCols = 0;
4387  SCROW nRows = 0;
4388  if (pMyFormulaCell)
4389  pMyFormulaCell->GetMatColsRows( nCols, nRows);
4390  if (nCols == 0)
4391  {
4392  // Happens if called via ScViewFunc::EnterMatrix()
4393  // ScFormulaCell::GetResultDimensions() as of course a
4394  // matrix result is not available yet.
4395  nCols = 1;
4396  }
4397  ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1);
4398  if (pResMat)
4399  {
4400  for (SCCOL i=0; i < nCols; ++i)
4401  pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0);
4402  PushMatrix( pResMat);
4403  return;
4404  }
4405  }
4406  }
4407  else
4408  {
4409  switch ( GetStackType() )
4410  {
4411  case svSingleRef :
4412  {
4413  SCCOL nCol1(0);
4414  SCROW nRow1(0);
4415  SCTAB nTab1(0);
4416  PopSingleRef( nCol1, nRow1, nTab1 );
4417  nVal = static_cast<double>(nCol1 + 1);
4418  }
4419  break;
4420  case svExternalSingleRef :
4421  {
4422  sal_uInt16 nFileId;
4423  OUString aTabName;
4424  ScSingleRefData aRef;
4425  PopExternalSingleRef( nFileId, aTabName, aRef );
4426  ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4427  nVal = static_cast<double>( aAbsRef.Col() + 1 );
4428  }
4429  break;
4430 
4431  case svDoubleRef :
4432  case svExternalDoubleRef :
4433  {
4434  SCCOL nCol1;
4435  SCCOL nCol2;
4436  if ( GetStackType() == svDoubleRef )
4437  {
4438  SCROW nRow1;
4439  SCTAB nTab1;
4440  SCROW nRow2;
4441  SCTAB nTab2;
4442  PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4443  }
4444  else
4445  {
4446  sal_uInt16 nFileId;
4447  OUString aTabName;
4448  ScComplexRefData aRef;
4449  PopExternalDoubleRef( nFileId, aTabName, aRef );
4450  ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4451  nCol1 = aAbs.aStart.Col();
4452  nCol2 = aAbs.aEnd.Col();
4453  }
4454  if (nCol2 > nCol1)
4455  {
4456  ScMatrixRef pResMat = GetNewMat(
4457  static_cast<SCSIZE>(nCol2-nCol1+1), 1);
4458  if (pResMat)
4459  {
4460  for (SCCOL i = nCol1; i <= nCol2; i++)
4461  pResMat->PutDouble(static_cast<double>(i+1),
4462  static_cast<SCSIZE>(i-nCol1), 0);
4463  PushMatrix(pResMat);
4464  return;
4465  }
4466  }
4467  else
4468  nVal = static_cast<double>(nCol1 + 1);
4469  }
4470  break;
4471  default:
4472  SetError( FormulaError::IllegalParameter );
4473  }
4474  }
4475  PushDouble( nVal );
4476 }
4477 
4479 {
4480  sal_uInt8 nParamCount = GetByte();
4481  if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4482  return;
4483 
4484  double nVal = 0.0;
4485  if (nParamCount == 0)
4486  {
4487  nVal = aPos.Row() + 1;
4488  if (bMatrixFormula)
4489  {
4490  SCCOL nCols = 0;
4491  SCROW nRows = 0;
4492  if (pMyFormulaCell)
4493  pMyFormulaCell->GetMatColsRows( nCols, nRows);
4494  if (nRows == 0)
4495  {
4496  // Happens if called via ScViewFunc::EnterMatrix()
4497  // ScFormulaCell::GetResultDimensions() as of course a
4498  // matrix result is not available yet.
4499  nRows = 1;
4500  }
4501  ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows));
4502  if (pResMat)
4503  {
4504  for (SCROW i=0; i < nRows; i++)
4505  pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i));
4506  PushMatrix( pResMat);
4507  return;
4508  }
4509  }
4510  }
4511  else
4512  {
4513  switch ( GetStackType() )
4514  {
4515  case svSingleRef :
4516  {
4517  SCCOL nCol1(0);
4518  SCROW nRow1(0);
4519  SCTAB nTab1(0);
4520  PopSingleRef( nCol1, nRow1, nTab1 );
4521  nVal = static_cast<double>(nRow1 + 1);
4522  }
4523  break;
4524  case svExternalSingleRef :
4525  {
4526  sal_uInt16 nFileId;
4527  OUString aTabName;
4528  ScSingleRefData aRef;
4529  PopExternalSingleRef( nFileId, aTabName, aRef );
4530  ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4531  nVal = static_cast<double>( aAbsRef.Row() + 1 );
4532  }
4533  break;
4534  case svDoubleRef :
4535  case svExternalDoubleRef :
4536  {
4537  SCROW nRow1;
4538  SCROW nRow2;
4539  if ( GetStackType() == svDoubleRef )
4540  {
4541  SCCOL nCol1;
4542  SCTAB nTab1;
4543  SCCOL nCol2;
4544  SCTAB nTab2;
4545  PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4546  }
4547  else
4548  {
4549  sal_uInt16 nFileId;
4550  OUString aTabName;
4551  ScComplexRefData aRef;
4552  PopExternalDoubleRef( nFileId, aTabName, aRef );
4553  ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4554  nRow1 = aAbs.aStart.Row();
4555  nRow2 = aAbs.aEnd.Row();
4556  }
4557  if (nRow2 > nRow1)
4558  {
4559  ScMatrixRef pResMat = GetNewMat( 1,
4560  static_cast<SCSIZE>(nRow2-nRow1+1));
4561  if (pResMat)
4562  {
4563  for (SCROW i = nRow1; i <= nRow2; i++)
4564  pResMat->PutDouble(static_cast<double>(i+1), 0,
4565  static_cast<SCSIZE>(i-nRow1));
4566  PushMatrix(pResMat);
4567  return;
4568  }
4569  }
4570  else
4571  nVal = static_cast<double>(nRow1 + 1);
4572  }
4573  break;
4574  default:
4575  SetError( FormulaError::IllegalParameter );
4576  }
4577  }
4578  PushDouble( nVal );
4579 }
4580 
4582 {
4583  sal_uInt8 nParamCount = GetByte();
4584  if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4585  return;
4586 
4587  SCTAB nVal = 0;
4588  if ( nParamCount == 0 )
4589  nVal = aPos.Tab() + 1;
4590  else
4591  {
4592  switch ( GetStackType() )
4593  {
4594  case svString :
4595  {
4596  svl::SharedString aStr = PopString();
4597  if ( mrDoc.GetTable(aStr.getString(), nVal))
4598  ++nVal;
4599  else
4600  SetError( FormulaError::IllegalArgument );
4601  }
4602  break;
4603  case svSingleRef :
4604  {
4605  SCCOL nCol1(0);
4606  SCROW nRow1(0);
4607  SCTAB nTab1(0);
4608  PopSingleRef(nCol1, nRow1, nTab1);
4609  nVal = nTab1 + 1;
4610  }
4611  break;
4612  case svDoubleRef :
4613  {
4614  SCCOL nCol1;
4615  SCROW nRow1;
4616  SCTAB nTab1;
4617  SCCOL nCol2;
4618  SCROW nRow2;
4619  SCTAB nTab2;
4620  PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4621  nVal = nTab1 + 1;
4622  }
4623  break;
4624  default:
4625  SetError( FormulaError::IllegalParameter );
4626  }
4627  if ( nGlobalError != FormulaError::NONE )
4628  nVal = 0;
4629  }
4630  PushDouble( static_cast<double>(nVal) );
4631 }
4632 
4633 namespace {
4634 
4635 class VectorMatrixAccessor
4636 {
4637 public:
4638  VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) :
4639  mrMat(rMat), mbColVec(bColVec) {}
4640 
4641  bool IsEmpty(SCSIZE i) const
4642  {
4643  return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0);
4644  }
4645 
4646  bool IsEmptyPath(SCSIZE i) const
4647  {
4648  return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0);
4649  }
4650 
4651  bool IsValue(SCSIZE i) const
4652  {
4653  return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0);
4654  }
4655 
4656  bool IsStringOrEmpty(SCSIZE i) const
4657  {
4658  return mbColVec ? mrMat.IsStringOrEmpty(0, i) : mrMat.IsStringOrEmpty(i, 0);
4659  }
4660 
4661  double GetDouble(SCSIZE i) const
4662  {
4663  return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0);
4664  }
4665 
4666  OUString GetString(SCSIZE i) const
4667  {
4668  return mbColVec ? mrMat.GetString(0, i).getString() : mrMat.GetString(i, 0).getString();
4669  }
4670 
4671  SCSIZE GetElementCount() const
4672  {
4673  SCSIZE nC, nR;
4674  mrMat.GetDimensions(nC, nR);
4675  return mbColVec ? nR : nC;
4676  }
4677 
4678 private:
4679  const ScMatrix& mrMat;
4680  bool mbColVec;
4681 };
4682 
4686 sal_Int32 lcl_CompareMatrix2Query(
4687  SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryEntry& rEntry)
4688 {
4689  if (rMat.IsEmpty(i))
4690  {
4691  /* TODO: in case we introduced query for real empty this would have to
4692  * be changed! */
4693  return -1; // empty always less than anything else
4694  }
4695 
4696  /* FIXME: what is an empty path (result of IF(false;true_path) in
4697  * comparisons? */
4698 
4699  bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
4700  if (rMat.IsValue(i))
4701  {
4702  const double nVal1 = rMat.GetDouble(i);
4703  if (!std::isfinite(nVal1))
4704  {
4705  // XXX Querying for error values is not required, otherwise we'd
4706  // need to check here.
4707  return 1; // error always greater than numeric or string
4708  }
4709 
4710  if (bByString)
4711  return -1; // numeric always less than string
4712 
4713  const double nVal2 = rEntry.GetQueryItem().mfVal;
4714  // XXX Querying for error values is not required, otherwise we'd need
4715  // to check here and move that check before the bByString check.
4716  if (nVal1 == nVal2)
4717  return 0;
4718 
4719  return nVal1 < nVal2 ? -1 : 1;
4720  }
4721 
4722  if (!bByString)
4723  return 1; // string always greater than numeric
4724 
4725  OUString aStr1 = rMat.GetString(i);
4726  OUString aStr2 = rEntry.GetQueryItem().maString.getString();
4727 
4728  return ScGlobal::GetCollator()->compareString(aStr1, aStr2); // case-insensitive
4729 }
4730 
4733 void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat,
4734  SCSIZE nMatCount)
4735 {
4736  if (rMat.IsValue(rIndex))
4737  {
4738  double nVal = rMat.GetDouble(rIndex);
4739  while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) &&
4740  nVal == rMat.GetDouble(rIndex+1))
4741  ++rIndex;
4742  }
4743  // Order of IsEmptyPath, IsEmpty, IsStringOrEmpty is significant!
4744  else if (rMat.IsEmptyPath(rIndex))
4745  {
4746  while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1))
4747  ++rIndex;
4748  }
4749  else if (rMat.IsEmpty(rIndex))
4750  {
4751  while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1))
4752  ++rIndex;
4753  }
4754  else if (rMat.IsStringOrEmpty(rIndex))
4755  {
4756  OUString aStr( rMat.GetString(rIndex));
4757  while (rIndex < nMatCount-1 && rMat.IsStringOrEmpty(rIndex+1) &&
4758  aStr == rMat.GetString(rIndex+1))
4759  ++rIndex;
4760  }
4761  else
4762  {
4763  OSL_FAIL("lcl_GetLastMatch: unhandled matrix type");
4764  }
4765 }
4766 
4767 }
4768 
4770 {
4771 
4772  sal_uInt8 nParamCount = GetByte();
4773  if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
4774  return;
4775 
4776  double fTyp;
4777  if (nParamCount == 3)
4778  fTyp = GetDouble();
4779  else
4780  fTyp = 1.0;
4781  SCCOL nCol1 = 0;
4782  SCROW nRow1 = 0;
4783  SCTAB nTab1 = 0;
4784  SCCOL nCol2 = 0;
4785  SCROW nRow2 = 0;
4786  ScMatrixRef pMatSrc = nullptr;
4787 
4788  switch (GetStackType())
4789  {
4790  case svSingleRef:
4791  PopSingleRef( nCol1, nRow1, nTab1);
4792  nCol2 = nCol1;
4793  nRow2 = nRow1;
4794  break;
4795  case svDoubleRef:
4796  {
4797  SCTAB nTab2 = 0;
4798  PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4799  if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2))
4800  {
4801  PushIllegalParameter();
4802  return;
4803  }
4804  }
4805  break;
4806  case svMatrix:
4807  case svExternalDoubleRef:
4808  {
4809  if (GetStackType() == svMatrix)
4810  pMatSrc = PopMatrix();
4811  else
4812  PopExternalDoubleRef(pMatSrc);
4813 
4814  if (!pMatSrc)
4815  {
4816  PushIllegalParameter();
4817  return;
4818  }
4819  }
4820  break;
4821  default:
4822  PushIllegalParameter();
4823  return;
4824  }
4825 
4826  if (nGlobalError == FormulaError::NONE)
4827  {
4828  double fVal;
4829  ScQueryParam rParam;
4830  rParam.nCol1 = nCol1;
4831  rParam.nRow1 = nRow1;
4832  rParam.nCol2 = nCol2;
4833  rParam.nTab = nTab1;
4834 
4835  ScQueryEntry& rEntry = rParam.GetEntry(0);
4836  ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
4837  rEntry.bDoQuery = true;
4838  if (fTyp < 0.0)
4839  rEntry.eOp = SC_GREATER_EQUAL;
4840  else if (fTyp > 0.0)
4841  rEntry.eOp = SC_LESS_EQUAL;
4842  switch ( GetStackType() )
4843  {
4844  case svDouble:
4845  {
4846  fVal = GetDouble();
4847  rItem.mfVal = fVal;
4848  rItem.meType = ScQueryEntry::ByValue;
4849  }
4850  break;
4851  case svString:
4852  {
4854  rItem.maString = GetString();
4855  }
4856  break;
4857  case svDoubleRef :
4858  case svSingleRef :
4859  {
4860  ScAddress aAdr;
4861  if ( !PopDoubleRefOrSingleRef( aAdr ) )
4862  {
4863  PushInt(0);
4864  return ;
4865  }
4866  ScRefCellValue aCell(mrDoc, aAdr);
4867  if (aCell.hasNumeric())
4868  {
4869  fVal = GetCellValue(aAdr, aCell);
4870  rItem.meType = ScQueryEntry::ByValue;
4871  rItem.mfVal = fVal;
4872  }
4873  else
4874  {
4875  GetCellString(rItem.maString, aCell);
4877  }
4878  }
4879  break;
4880  case svExternalSingleRef:
4881  {
4883  PopExternalSingleRef(pToken);
4884  if (nGlobalError != FormulaError::NONE)
4885  {
4886  PushError( nGlobalError);
4887  return;
4888  }
4889  if (pToken->GetType() == svDouble)
4890  {
4891  rItem.meType = ScQueryEntry::ByValue;
4892  rItem.mfVal = pToken->GetDouble();
4893  }
4894  else
4895  {
4897  rItem.maString = pToken->GetString();
4898  }
4899  }
4900  break;
4901  case svExternalDoubleRef:
4902  case svMatrix :
4903  {
4905  ScMatValType nType = GetDoubleOrStringFromMatrix(
4906  rItem.mfVal, aStr);
4907  rItem.maString = aStr;
4908  rItem.meType = ScMatrix::IsNonValueType(nType) ?
4910  }
4911  break;
4912  default:
4913  {
4914  PushIllegalParameter();
4915  return;
4916  }
4917  }
4918  if (rItem.meType == ScQueryEntry::ByString)
4919  {
4920  bool bIsVBAMode = mrDoc.IsInVBAMode();
4921 
4922  if ( bIsVBAMode )
4924  else
4925  rParam.eSearchType = DetectSearchType(rEntry.GetQueryItem().maString.getString(), mrDoc);
4926  }
4927 
4928  if (pMatSrc) // The source data is matrix array.
4929  {
4930  SCSIZE nC, nR;
4931  pMatSrc->GetDimensions( nC, nR);
4932  if (nC > 1 && nR > 1)
4933  {
4934  // The source matrix must be a vector.
4935  PushIllegalParameter();
4936  return;
4937  }
4938 
4939  // Do not propagate errors from matrix while searching.
4940  pMatSrc->SetErrorInterpreter( nullptr);
4941 
4942  SCSIZE nMatCount = (nC == 1) ? nR : nC;
4943  VectorMatrixAccessor aMatAcc(*pMatSrc, nC == 1);
4944 
4945  // simple serial search for equality mode (source data doesn't
4946  // need to be sorted).
4947 
4948  if (rEntry.eOp == SC_EQUAL)
4949  {
4950  for (SCSIZE i = 0; i < nMatCount; ++i)
4951  {
4952  if (lcl_CompareMatrix2Query( i, aMatAcc, rEntry) == 0)
4953  {
4954  PushDouble(i+1); // found !
4955  return;
4956  }
4957  }
4958  PushNA(); // not found
4959  return;
4960  }
4961 
4962  // binary search for non-equality mode (the source data is
4963  // assumed to be sorted).
4964 
4965  bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL);
4966  SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0;
4967  for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
4968  {
4969  SCSIZE nMid = nFirst + nLen/2;
4970  sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rEntry);
4971  if (nCmp == 0)
4972  {
4973  // exact match. find the last item with the same value.
4974  lcl_GetLastMatch( nMid, aMatAcc, nMatCount);
4975  PushDouble( nMid+1);
4976  return;
4977  }
4978 
4979  if (nLen == 1) // first and last items are next to each other.
4980  {
4981  if (nCmp < 0)
4982  nHitIndex = bAscOrder ? nLast : nFirst;
4983  else
4984  nHitIndex = bAscOrder ? nFirst : nLast;
4985  break;
4986  }
4987 
4988  if (nCmp < 0)
4989  {
4990  if (bAscOrder)
4991  nFirst = nMid;
4992  else
4993  nLast = nMid;
4994  }
4995  else
4996  {
4997  if (bAscOrder)
4998  nLast = nMid;
4999  else
5000  nFirst = nMid;
5001  }
5002  }
5003 
5004  if (nHitIndex == nMatCount-1) // last item
5005  {
5006  sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, aMatAcc, rEntry);
5007  if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0))
5008  {
5009  // either the last item is an exact match or the real
5010  // hit is beyond the last item.
5011  PushDouble( nHitIndex+1);
5012  return;
5013  }
5014  }
5015 
5016  if (nHitIndex > 0) // valid hit must be 2nd item or higher
5017  {
5018  PushDouble( nHitIndex); // non-exact match
5019  return;
5020  }
5021 
5022  PushNA();
5023  return;
5024  }
5025 
5026  SCCOLROW nDelta = 0;
5027  if (nCol1 == nCol2)
5028  { // search row in column
5029  rParam.nRow2 = nRow2;
5030  rEntry.nField = nCol1;
5031  ScAddress aResultPos( nCol1, nRow1, nTab1);
5032  if (!LookupQueryWithCache( aResultPos, rParam))
5033  {
5034  PushNA();
5035  return;
5036  }
5037  nDelta = aResultPos.Row() - nRow1;
5038  }
5039  else
5040  { // search column in row
5041  SCCOL nC;
5042  rParam.bByRow = false;
5043  rParam.nRow2 = nRow1;
5044  rEntry.nField = nCol1;
5045  ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, false);
5046  // Advance Entry.nField in Iterator if column changed
5047  aCellIter.SetAdvanceQueryParamEntryField( true );
5048  if (fTyp == 0.0)
5049  { // EQUAL
5050  if ( aCellIter.GetFirst() )
5051  nC = aCellIter.GetCol();
5052  else
5053  {
5054  PushNA();
5055  return;
5056  }
5057  }
5058  else
5059  { // <= or >=
5060  SCROW nR;
5061  if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) )
5062  {
5063  PushNA();
5064  return;
5065  }
5066  }
5067  nDelta = nC - nCol1;
5068  }
5069  PushDouble(static_cast<double>(nDelta + 1));
5070  }
5071  else
5072  PushIllegalParameter();
5073 }
5074 
5075 namespace {
5076 
5077 bool isCellContentEmpty( const ScRefCellValue& rCell )
5078 {
5079  switch (rCell.meType)
5080  {
5081  case CELLTYPE_VALUE:
5082  case CELLTYPE_STRING:
5083  case CELLTYPE_EDIT:
5084  return false;
5085  case CELLTYPE_FORMULA:
5086  {
5087  // NOTE: Excel treats ="" in a referenced cell as blank in
5088  // COUNTBLANK() but not in ISBLANK(), which is inconsistent.
5089  // COUNTBLANK() tests the (display) result whereas ISBLANK() tests
5090  // the cell content.
5091  // ODFF allows both for COUNTBLANK().
5092  // OOo and LibreOffice prior to 4.4 did not treat ="" as blank in
5093  // COUNTBLANK(), we now do for Excel interoperability.
5094  /* TODO: introduce yet another compatibility option? */
5095  sc::FormulaResultValue aRes = rCell.mpFormula->GetResult();
5097  return false;
5098  if (!aRes.maString.isEmpty())
5099  return false;
5100  }
5101  break;
5102  default:
5103  ;
5104  }
5105 
5106  return true;
5107 }
5108 
5109 }
5110 
5112 {
5113  if ( !MustHaveParamCount( GetByte(), 1 ) )
5114  return;
5115 
5116  const SCSIZE nMatRows = GetRefListArrayMaxSize(1);
5117  // There's either one RefList and nothing else, or none.
5118  ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows) : nullptr);
5119  sal_uLong nMaxCount = 0, nCount = 0;
5120  switch (GetStackType())
5121  {
5122  case svSingleRef :
5123  {
5124  nMaxCount = 1;
5125  ScAddress aAdr;
5126  PopSingleRef( aAdr );
5127  ScRefCellValue aCell(mrDoc, aAdr);
5128  if (!isCellContentEmpty(aCell))
5129  nCount = 1;
5130  }
5131  break;
5132  case svRefList :
5133  case svDoubleRef :
5134  {
5135  ScRange aRange;
5136  short nParam = 1;
5137  SCSIZE nRefListArrayPos = 0;
5138  size_t nRefInList = 0;
5139  while (nParam-- > 0)
5140  {
5141  nRefListArrayPos = nRefInList;
5142  PopDoubleRef( aRange, nParam, nRefInList);
5143  nMaxCount +=
5144  static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) *
5145  static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) *
5146  static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1);
5147 
5148  ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags);
5149  for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
5150  {
5151  const ScRefCellValue& rCell = aIter.getRefCellValue();
5152  if (!isCellContentEmpty(rCell))
5153  ++nCount;
5154  }
5155  if (xResMat)
5156  {
5157  xResMat->PutDouble( nMaxCount - nCount, 0, nRefListArrayPos);
5158  nMaxCount = nCount = 0;
5159  }
5160  }
5161  }
5162  break;
5163  case svMatrix:
5164  case svExternalSingleRef:
5165  case svExternalDoubleRef:
5166  {
5167  ScMatrixRef xMat = GetMatrix();
5168  if (!xMat)
5169  SetError( FormulaError::IllegalParameter);
5170  else
5171  {
5172  SCSIZE nC, nR;
5173  xMat->GetDimensions( nC, nR);
5174  nMaxCount = nC * nR;
5175  // Numbers (implicit), strings and error values, ignore empty
5176  // strings as those if not entered in an inline array are the
5177  // result of a formula, to be par with a reference to formula
5178  // cell as *visual* blank, see isCellContentEmpty() above.
5179  nCount = xMat->Count( true, true, true);
5180  }
5181  }
5182  break;
5183  default : SetError(FormulaError::IllegalParameter); break;
5184  }
5185  if (xResMat)
5186  PushMatrix( xResMat);
5187  else
5188  PushDouble(nMaxCount - nCount);
5189 }
5190 
5192 {
5193  sal_uInt8 nParamCount = GetByte();
5194  if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
5195  return;
5196 
5197  SCCOL nCol3 = 0;
5198  SCROW nRow3 = 0;
5199  SCTAB nTab3 = 0;
5200 
5201  ScMatrixRef pSumExtraMatrix;
5202  bool bSumExtraRange = (nParamCount == 3);
5203  if (bSumExtraRange)
5204  {
5205  // Save only the upperleft cell in case of cell range. The geometry
5206  // of the 3rd parameter is taken from the 1st parameter.
5207 
5208  switch ( GetStackType() )
5209  {
5210  case svDoubleRef :
5211  {
5212  SCCOL nColJunk = 0;
5213  SCROW nRowJunk = 0;
5214  SCTAB nTabJunk = 0;
5215  PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk );
5216  if ( nTabJunk != nTab3 )
5217  {
5218  PushError( FormulaError::IllegalParameter);
5219  return;
5220  }
5221  }
5222  break;
5223  case svSingleRef :
5224  PopSingleRef( nCol3, nRow3, nTab3 );
5225  break;
5226  case svMatrix:
5227  pSumExtraMatrix = PopMatrix();
5228  // nCol3, nRow3, nTab3 remain 0
5229  break;
5230  case svExternalSingleRef:
5231  {
5232  pSumExtraMatrix = GetNewMat(1,1);
5234  PopExternalSingleRef(pToken);
5235  if (nGlobalError != FormulaError::NONE)
5236  {
5237  PushError( nGlobalError);
5238  return;
5239  }
5240 
5241  if (pToken->GetType() == svDouble)
5242  pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0);
5243  else
5244  pSumExtraMatrix->PutString(pToken->GetString(), 0, 0);
5245  }
5246  break;
5247  case svExternalDoubleRef:
5248  PopExternalDoubleRef(pSumExtraMatrix);
5249  break;
5250  default:
5251  PushError( FormulaError::IllegalParameter);
5252  return;
5253  }
5254  }
5255 
5256  svl::SharedString aString;
5257  double fVal = 0.0;
5258  bool bIsString = true;
5259  switch ( GetStackType() )
5260  {
5261  case svDoubleRef :
5262  case svSingleRef :
5263  {
5264  ScAddress aAdr;
5265  if ( !PopDoubleRefOrSingleRef( aAdr ) )
5266  {
5267  PushError( nGlobalError);
5268  return;
5269  }
5270 
5271  ScRefCellValue aCell(mrDoc, aAdr);
5272  switch (aCell.meType)
5273  {
5274  case CELLTYPE_VALUE :
5275  fVal = GetCellValue(aAdr, aCell);
5276  bIsString = false;
5277  break;
5278  case CELLTYPE_FORMULA :
5279  if (aCell.mpFormula->IsValue())
5280  {
5281  fVal = GetCellValue(aAdr, aCell);
5282  bIsString = false;
5283  }
5284  else
5285  GetCellString(aString, aCell);
5286  break;
5287  case CELLTYPE_STRING :
5288  case CELLTYPE_EDIT :
5289  GetCellString(aString, aCell);
5290  break;
5291  default:
5292  fVal = 0.0;
5293  bIsString = false;
5294  }
5295  }
5296  break;
5297  case svString:
5298  aString = GetString();
5299  break;
5300  case svMatrix :
5301  case svExternalDoubleRef:
5302  {
5303  ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
5304  bIsString = ScMatrix::IsRealStringType( nType);
5305  }
5306  break;
5307  case svExternalSingleRef:
5308  {
5310  PopExternalSingleRef(pToken);
5311  if (nGlobalError == FormulaError::NONE)
5312  {
5313  if (pToken->GetType() == svDouble)
5314  {
5315  fVal = pToken->GetDouble();
5316  bIsString = false;
5317  }
5318  else
5319  aString = pToken->GetString();
5320  }
5321  }
5322  break;
5323  default:
5324  {
5325  fVal = GetDouble();
5326  bIsString = false;
5327  }
5328  }
5329 
5330  double fSum = 0.0;
5331  double fMem = 0.0;
5332  double fRes = 0.0;
5333  double fCount = 0.0;
5334  bool bNull = true;
5335  short nParam = 1;
5336  const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
5337  // There's either one RefList and nothing else, or none.
5338  ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows) : nullptr);
5339  SCSIZE nRefListArrayPos = 0;
5340  size_t nRefInList = 0;
5341  while (nParam-- > 0)
5342  {
5343  SCCOL nCol1 = 0;
5344  SCROW nRow1 = 0;
5345  SCTAB nTab1 = 0;
5346  SCCOL nCol2 = 0;
5347  SCROW nRow2 = 0;
5348  SCTAB nTab2 = 0;
5349  ScMatrixRef pQueryMatrix;
5350  switch ( GetStackType() )
5351  {
5352  case svRefList :
5353  if (bSumExtraRange)
5354  {
5355  /* TODO: this could resolve if all refs are of the same size */
5356  SetError( FormulaError::IllegalParameter);
5357  }
5358  else
5359  {
5360  nRefListArrayPos = nRefInList;
5361  ScRange aRange;
5362  PopDoubleRef( aRange, nParam, nRefInList);
5363  aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5364  }
5365  break;
5366  case svDoubleRef :
5367  PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
5368  break;
5369  case svSingleRef :
5370  PopSingleRef( nCol1, nRow1, nTab1 );
5371  nCol2 = nCol1;
5372  nRow2 = nRow1;
5373  nTab2 = nTab1;
5374  break;
5375  case svMatrix:
5376  case svExternalSingleRef:
5377  case svExternalDoubleRef:
5378  {
5379  pQueryMatrix = GetMatrix();
5380  if (!pQueryMatrix)
5381  {
5382  PushError( FormulaError::IllegalParameter);
5383  return;
5384  }
5385  nCol1 = 0;
5386  nRow1 = 0;
5387  nTab1 = 0;
5388  SCSIZE nC, nR;
5389  pQueryMatrix->GetDimensions( nC, nR);
5390  nCol2 = static_cast<SCCOL>(nC - 1);
5391  nRow2 = static_cast<SCROW>(nR - 1);
5392  nTab2 = 0;
5393  }
5394  break;
5395  default:
5396  SetError( FormulaError::IllegalParameter);
5397  }
5398  if ( nTab1 != nTab2 )
5399  {
5400  SetError( FormulaError::IllegalParameter);
5401  }
5402 
5403  if (bSumExtraRange)
5404  {
5405  // Take the range geometry of the 1st parameter and apply it to
5406  // the 3rd. If parts of the resulting range would point outside
5407  // the sheet, don't complain but silently ignore and simply cut
5408  // them away, this is what Xcl does :-/
5409 
5410  // For the cut-away part we also don't need to determine the
5411  // criteria match, so shrink the source range accordingly,
5412  // instead of the result range.
5413  SCCOL nColDelta = nCol2 - nCol1;
5414  SCROW nRowDelta = nRow2 - nRow1;
5415  SCCOL nMaxCol;
5416  SCROW nMaxRow;
5417  if (pSumExtraMatrix)
5418  {
5419  SCSIZE nC, nR;
5420  pSumExtraMatrix->GetDimensions( nC, nR);
5421  nMaxCol = static_cast<SCCOL>(nC - 1);
5422  nMaxRow = static_cast<SCROW>(nR - 1);
5423  }
5424  else
5425  {
5426  nMaxCol = mrDoc.MaxCol();
5427  nMaxRow = mrDoc.MaxRow();
5428  }
5429  if (nCol3 + nColDelta > nMaxCol)
5430  {
5431  SCCOL nNewDelta = nMaxCol - nCol3;
5432  nCol2 = nCol1 + nNewDelta;
5433  }
5434 
5435  if (nRow3 + nRowDelta > nMaxRow)
5436  {
5437  SCROW nNewDelta = nMaxRow - nRow3;
5438  nRow2 = nRow1 + nNewDelta;
5439  }
5440  }
5441  else
5442  {
5443  nCol3 = nCol1;
5444  nRow3 = nRow1;
5445  nTab3 = nTab1;
5446  }
5447 
5448  if (nGlobalError == FormulaError::NONE)
5449  {
5450  ScQueryParam rParam;
5451  rParam.nRow1 = nRow1;
5452  rParam.nRow2 = nRow2;
5453 
5454  ScQueryEntry& rEntry = rParam.GetEntry(0);
5455  ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5456  rEntry.bDoQuery = true;
5457  if (!bIsString)
5458  {
5459  rItem.meType = ScQueryEntry::ByValue;
5460  rItem.mfVal = fVal;
5461  rEntry.eOp = SC_EQUAL;
5462  }
5463  else
5464  {
5465  rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
5466  if (rItem.meType == ScQueryEntry::ByString)
5467  rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
5468  }
5469  ScAddress aAdr;
5470  aAdr.SetTab( nTab3 );
5471  rParam.nCol1 = nCol1;
5472  rParam.nCol2 = nCol2;
5473  rEntry.nField = nCol1;
5474  SCCOL nColDiff = nCol3 - nCol1;
5475  SCROW nRowDiff = nRow3 - nRow1;
5476  if (pQueryMatrix)
5477  {
5478  // Never case-sensitive.
5479  sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
5480  ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5481  if (nGlobalError != FormulaError::NONE || !pResultMatrix)
5482  {
5483  SetError( FormulaError::IllegalParameter);
5484  }
5485 
5486  if (pSumExtraMatrix)
5487  {
5488  for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5489  {
5490  for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5491  {
5492  if (pResultMatrix->IsValue( nCol, nRow) &&
5493  pResultMatrix->GetDouble( nCol, nRow))
5494  {
5495  SCSIZE nC = nCol + nColDiff;
5496  SCSIZE nR = nRow + nRowDiff;
5497  if (pSumExtraMatrix->IsValue( nC, nR))
5498  {
5499  fVal = pSumExtraMatrix->GetDouble( nC, nR);
5500  ++fCount;
5501  if ( bNull && fVal != 0.0 )
5502  {
5503  bNull = false;
5504  fMem = fVal;
5505  }
5506  else
5507  fSum += fVal;
5508  }
5509  }
5510  }
5511  }
5512  }
5513  else
5514  {
5515  for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5516  {
5517  for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5518  {
5519  if (pResultMatrix->GetDouble( nCol, nRow))
5520  {
5521  aAdr.SetCol( nCol + nColDiff);
5522  aAdr.SetRow( nRow + nRowDiff);
5523  ScRefCellValue aCell(mrDoc, aAdr);
5524  if (aCell.hasNumeric())
5525  {
5526  fVal = GetCellValue(aAdr, aCell);
5527  ++fCount;
5528  if ( bNull && fVal != 0.0 )
5529  {
5530  bNull = false;
5531  fMem = fVal;
5532  }
5533  else
5534  fSum += fVal;
5535  }
5536  }
5537  }
5538  }
5539  }
5540  }
5541  else
5542  {
5543  ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, false);
5544  // Increment Entry.nField in iterator when switching to next column.
5545  aCellIter.SetAdvanceQueryParamEntryField( true );
5546  if ( aCellIter.GetFirst() )
5547  {
5548  if (pSumExtraMatrix)
5549  {
5550  do
5551  {
5552  SCSIZE nC = aCellIter.GetCol() + nColDiff;
5553  SCSIZE nR = aCellIter.GetRow() + nRowDiff;
5554  if (pSumExtraMatrix->IsValue( nC, nR))
5555  {
5556  fVal = pSumExtraMatrix->GetDouble( nC, nR);
5557  ++fCount;
5558  if ( bNull && fVal != 0.0 )
5559  {
5560  bNull = false;
5561  fMem = fVal;
5562  }
5563  else
5564  fSum += fVal;
5565  }
5566  } while ( aCellIter.GetNext() );
5567  }
5568  else
5569  {
5570  do
5571  {
5572  aAdr.SetCol( aCellIter.GetCol() + nColDiff);
5573  aAdr.SetRow( aCellIter.GetRow() + nRowDiff);
5574  ScRefCellValue aCell(mrDoc, aAdr);
5575  if (aCell.hasNumeric())
5576  {
5577  fVal = GetCellValue(aAdr, aCell);
5578  ++fCount;
5579  if ( bNull && fVal != 0.0 )
5580  {
5581  bNull = false;
5582  fMem = fVal;
5583  }
5584  else
5585  fSum += fVal;
5586  }
5587  } while ( aCellIter.GetNext() );
5588  }
5589  }
5590  }
5591  }
5592  else
5593  {
5594  PushError( FormulaError::IllegalParameter);
5595  return;
5596  }
5597 
5598  switch( eFunc )
5599  {
5600  case ifSUMIF: fRes = ::rtl::math::approxAdd( fSum, fMem ); break;
5601  case ifAVERAGEIF: fRes = div( ::rtl::math::approxAdd( fSum, fMem ), fCount); break;
5602  }
5603  if (xResMat)
5604  {
5605  if (nGlobalError == FormulaError::NONE)
5606  xResMat->PutDouble( fRes, 0, nRefListArrayPos);
5607  else
5608  {
5609  xResMat->PutError( nGlobalError, 0, nRefListArrayPos);
5610  nGlobalError = FormulaError::NONE;
5611  }
5612  fRes = fSum = fMem = fCount = 0.0;
5613  }
5614  }
5615  if (xResMat)
5616  PushMatrix( xResMat);
5617  else
5618  PushDouble( fRes);
5619 }
5620 
5622 {
5623  IterateParametersIf( ifSUMIF);
5624 }
5625 
5627 {
5628  IterateParametersIf( ifAVERAGEIF);
5629 }
5630 
5632 {
5633  if ( !MustHaveParamCount( GetByte(), 2 ) )
5634  return;
5635 
5636  svl::SharedString aString;
5637  double fVal = 0.0;
5638  bool bIsString = true;
5639  switch ( GetStackType() )
5640  {
5641  case svDoubleRef :
5642  case svSingleRef :
5643  {
5644  ScAddress aAdr;
5645  if ( !PopDoubleRefOrSingleRef( aAdr ) )
5646  {
5647  PushInt(0);
5648  return ;
5649  }
5650  ScRefCellValue aCell(mrDoc, aAdr);
5651  switch (aCell.meType)
5652  {
5653  case CELLTYPE_VALUE :
5654  fVal = GetCellValue(aAdr, aCell);
5655  bIsString = false;
5656  break;
5657  case CELLTYPE_FORMULA :
5658  if (aCell.mpFormula->IsValue())
5659  {
5660  fVal = GetCellValue(aAdr, aCell);
5661  bIsString = false;
5662  }
5663  else
5664  GetCellString(aString, aCell);
5665  break;
5666  case CELLTYPE_STRING :
5667  case CELLTYPE_EDIT :
5668  GetCellString(aString, aCell);
5669  break;
5670  default:
5671  fVal = 0.0;
5672  bIsString = false;
5673  }
5674  }
5675  break;
5676  case svMatrix:
5677  case svExternalSingleRef:
5678  case svExternalDoubleRef:
5679  {
5680  ScMatValType nType = GetDoubleOrStringFromMatrix(fVal, aString);
5681  bIsString = ScMatrix::IsRealStringType( nType);
5682  }
5683  break;
5684  case svString:
5685  aString = GetString();
5686  break;
5687  default:
5688  {
5689  fVal = GetDouble();
5690  bIsString = false;
5691  }
5692  }
5693  double fCount = 0.0;
5694  short nParam = 1;
5695  const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
5696  // There's either one RefList and nothing else, or none.
5697  ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows) : nullptr);
5698  SCSIZE nRefListArrayPos = 0;
5699  size_t nRefInList = 0;
5700  while (nParam-- > 0)
5701  {
5702  SCCOL nCol1 = 0;
5703  SCROW nRow1 = 0;
5704  SCTAB nTab1 = 0;
5705  SCCOL nCol2 = 0;
5706  SCROW nRow2 = 0;
5707  SCTAB nTab2 = 0;
5708  ScMatrixRef pQueryMatrix;
5709  switch ( GetStackType() )
5710  {
5711  case svRefList :
5712  nRefListArrayPos = nRefInList;
5713  [[fallthrough]];
5714  case svDoubleRef :
5715  {
5716  ScRange aRange;
5717  PopDoubleRef( aRange, nParam, nRefInList);
5718  aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5719  }
5720  break;
5721  case svSingleRef :
5722  PopSingleRef( nCol1, nRow1, nTab1 );
5723  nCol2 = nCol1;
5724  nRow2 = nRow1;
5725  nTab2 = nTab1;
5726  break;
5727  case svMatrix:
5728  case svExternalSingleRef:
5729  case svExternalDoubleRef:
5730  {
5731  pQueryMatrix = GetMatrix();
5732  if (!pQueryMatrix)
5733  {
5734  PushIllegalParameter();
5735  return;
5736  }
5737  nCol1 = 0;
5738  nRow1 = 0;
5739  nTab1 = 0;
5740  SCSIZE nC, nR;
5741  pQueryMatrix->GetDimensions( nC, nR);
5742  nCol2 = static_cast<SCCOL>(nC - 1);
5743  nRow2 = static_cast<SCROW>(nR - 1);
5744  nTab2 = 0;
5745  }
5746  break;
5747  default:
5748  PopError(); // Propagate it further
5749  PushIllegalParameter();
5750  return ;
5751  }
5752  if ( nTab1 != nTab2 )
5753  {
5754  PushIllegalParameter();
5755  return;
5756  }
5757  if (nCol1 > nCol2)
5758  {
5759  PushIllegalParameter();
5760  return;
5761  }
5762  if (nGlobalError == FormulaError::NONE)
5763  {
5764  ScQueryParam rParam;
5765  rParam.nRow1 = nRow1;
5766  rParam.nRow2 = nRow2;
5767 
5768  ScQueryEntry& rEntry = rParam.GetEntry(0);
5769  ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5770  rEntry.bDoQuery = true;
5771  if (!bIsString)
5772  {
5773  rItem.meType = ScQueryEntry::ByValue;
5774  rItem.mfVal = fVal;
5775  rEntry.eOp = SC_EQUAL;
5776  }
5777  else
5778  {
5779  rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
5780  if (rItem.meType == ScQueryEntry::ByString)
5781  rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
5782  }
5783  rParam.nCol1 = nCol1;
5784  rParam.nCol2 = nCol2;
5785  rEntry.nField = nCol1;
5786  if (pQueryMatrix)
5787  {
5788  // Never case-sensitive.
5789  sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
5790  ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5791  if (nGlobalError != FormulaError::NONE || !pResultMatrix)
5792  {
5793  PushIllegalParameter();
5794  return;
5795  }
5796 
5797  SCSIZE nSize = pResultMatrix->GetElementCount();
5798  for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex)
5799  {
5800  if (pResultMatrix->IsValue( nIndex) &&
5801  pResultMatrix->GetDouble( nIndex))
5802  ++fCount;
5803  }
5804  }
5805  else
5806  {
5807  ScCountIfCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam);
5808  fCount += aCellIter.GetCount();
5809  }
5810  }
5811  else
5812  {
5813  PushIllegalParameter();
5814  return;
5815  }
5816  if (xResMat)
5817  {
5818  xResMat->PutDouble( fCount, 0, nRefListArrayPos);
5819  fCount = 0.0;
5820  }
5821  }
5822  if (xResMat)
5823  PushMatrix( xResMat);
5824  else
5825  PushDouble(fCount);
5826 }
5827 
5828 void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIfsResult& rRes ) )
5829 {
5830  sal_uInt8 nParamCount = GetByte();
5831  sal_uInt8 nQueryCount = nParamCount / 2;
5832 
5833  std::vector<sal_uInt32>& vConditions = mrContext.maConditions;
5834  // vConditions is cached, although it is clear'ed after every cell is interpreted,
5835  // if the SUMIFS/COUNTIFS are part of a matrix formula, then that is not enough because
5836  // with a single InterpretTail() call it results in evaluation of all the cells in the
5837  // matrix formula.
5838  vConditions.clear();
5839 
5840  SCCOL nStartColDiff = 0;
5841  SCCOL nEndColDiff = 0;
5842  SCROW nStartRowDiff = 0;
5843  SCROW nEndRowDiff = 0;
5844  bool bRangeReduce = false;
5845  ScRange aMainRange;
5846 
5847  // Range-reduce optimization
5848  if (nParamCount % 2) // Not COUNTIFS
5849  {
5850  bool bHasDoubleRefCriteriaRanges = true;
5851  // Do not attempt main-range reduce if any of the criteria-ranges are not double-refs.
5852  for (sal_uInt16 nParamIdx = 2; nParamIdx < nParamCount; nParamIdx += 2 )
5853  {
5854  const formula::FormulaToken* pCriteriaRangeToken = pStack[ sp-nParamIdx ];
5855  if (pCriteriaRangeToken->GetType() != svDoubleRef )
5856  {
5857  bHasDoubleRefCriteriaRanges = false;
5858  break;
5859  }
5860  }
5861 
5862  // Probe the main range token, and try if we can shrink the range without altering results.
5863  const formula::FormulaToken* pMainRangeToken = pStack[ sp-nParamCount ];
5864  if (pMainRangeToken->GetType() == svDoubleRef && bHasDoubleRefCriteriaRanges)
5865  {
5866  const ScComplexRefData* pRefData = pMainRangeToken->GetDoubleRef();
5867  if (!pRefData->IsDeleted())
5868  {
5869  DoubleRefToRange( *pRefData, aMainRange);
5870 
5871  if (aMainRange.aStart.Tab() == aMainRange.aEnd.Tab())
5872  {
5873  // Shrink the range to actual data content.
5874  ScRange aSubRange = aMainRange;
5875  mrDoc.GetDataAreaSubrange(aSubRange);
5876 
5877  nStartColDiff = aSubRange.aStart.Col() - aMainRange.aStart.Col();
5878  nStartRowDiff = aSubRange.aStart.Row() - aMainRange.aStart.Row();
5879 
5880  nEndColDiff = aSubRange.aEnd.Col() - aMainRange.aEnd.Col();
5881  nEndRowDiff = aSubRange.aEnd.Row() - aMainRange.aEnd.Row();
5882  bRangeReduce = nStartColDiff || nStartRowDiff || nEndColDiff || nEndRowDiff;
5883  }
5884  }
5885  }
5886  }
5887 
5888  double fVal = 0.0;
5889  SCCOL nDimensionCols = 0;
5890  SCROW nDimensionRows = 0;
5891  const SCSIZE nRefArrayRows = GetRefListArrayMaxSize( nParamCount);
5892  std::vector<std::vector<sal_uInt32>> vRefArrayConditions;
5893 
5894  while (nParamCount > 1 && nGlobalError == FormulaError::NONE)
5895  {
5896  // take criteria
5897  svl::SharedString aString;
5898  fVal = 0.0;
5899  bool bIsString = true;
5900  switch ( GetStackType() )
5901  {
5902  case svDoubleRef :
5903  case svSingleRef :
5904  {
5905  ScAddress aAdr;
5906  if ( !PopDoubleRefOrSingleRef( aAdr ) )
5907  {
5908  PushError( nGlobalError);
5909  return;
5910  }
5911 
5912  ScRefCellValue aCell(mrDoc, aAdr);
5913  switch (aCell.meType)
5914  {
5915  case CELLTYPE_VALUE :
5916  fVal = GetCellValue(aAdr, aCell);
5917  bIsString = false;
5918  break;
5919  case CELLTYPE_FORMULA :
5920  if (aCell.mpFormula->IsValue())
5921  {
5922  fVal = GetCellValue(aAdr, aCell);
5923  bIsString = false;
5924  }
5925  else
5926  GetCellString(aString, aCell);
5927  break;
5928  case CELLTYPE_STRING :
5929  case CELLTYPE_EDIT :
5930  GetCellString(aString, aCell);
5931  break;
5932  default:
5933  fVal = 0.0;
5934  bIsString = false;
5935  }
5936  }