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