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