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