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