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