Bug Summary

File:home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx
Warning:line 8061, column 20
The left operand of '/' is a garbage value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name interpr1.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /usr/include/libxml2 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D SC_DLLIMPLEMENTATION -D SC_INFO_OSVERSION="LINUX" -D SYSTEM_LIBXML -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/liborcus/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/mdds/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/external/clew/source/include -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/sc/source/core/inc -I /home/maarten/src/libreoffice/core/sc/source/filter/inc -I /home/maarten/src/libreoffice/core/sc/source/ui/inc -I /home/maarten/src/libreoffice/core/sc/inc -I /home/maarten/src/libreoffice/core/workdir/SdiTarget/sc/sdi -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/oovbaapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <interpre.hxx>
21
22#include <scitems.hxx>
23#include <editeng/langitem.hxx>
24#include <editeng/justifyitem.hxx>
25#include <o3tl/safeint.hxx>
26#include <osl/thread.h>
27#include <svx/algitem.hxx>
28#include <unotools/textsearch.hxx>
29#include <svl/zforlist.hxx>
30#include <svl/zformat.hxx>
31#include <tools/urlobj.hxx>
32#include <unotools/charclass.hxx>
33#include <sfx2/docfile.hxx>
34#include <sfx2/printer.hxx>
35#include <unotools/collatorwrapper.hxx>
36#include <unotools/transliterationwrapper.hxx>
37#include <rtl/character.hxx>
38#include <rtl/ustring.hxx>
39#include <sal/log.hxx>
40#include <osl/diagnose.h>
41#include <unicode/uchar.h>
42#include <unicode/regex.h>
43#include <i18nlangtag/mslangid.hxx>
44
45#include <patattr.hxx>
46#include <global.hxx>
47#include <document.hxx>
48#include <dociter.hxx>
49#include <formulacell.hxx>
50#include <scmatrix.hxx>
51#include <docoptio.hxx>
52#include <attrib.hxx>
53#include <jumpmatrix.hxx>
54#include <cellkeytranslator.hxx>
55#include <lookupcache.hxx>
56#include <rangenam.hxx>
57#include <rangeutl.hxx>
58#include <compiler.hxx>
59#include <externalrefmgr.hxx>
60#include <basic/sbstar.hxx>
61#include <doubleref.hxx>
62#include <queryparam.hxx>
63#include <queryentry.hxx>
64#include <tokenarray.hxx>
65#include <compare.hxx>
66
67#include <com/sun/star/util/SearchResult.hpp>
68#include <comphelper/processfactory.hxx>
69#include <comphelper/random.hxx>
70#include <comphelper/string.hxx>
71#include <svl/sharedstringpool.hxx>
72
73#include <stdlib.h>
74#include <string.h>
75#include <math.h>
76#include <vector>
77#include <memory>
78#include <limits>
79
80const sal_uInt64 n2power48 = SAL_CONST_UINT64( 281474976710656)281474976710656ul; // 2^48
81
82ScCalcConfig *ScInterpreter::mpGlobalConfig = nullptr;
83
84using namespace formula;
85using ::std::unique_ptr;
86
87void ScInterpreter::ScIfJump()
88{
89 const short* pJump = pCur->GetJump();
90 short nJumpCount = pJump[ 0 ];
91 MatrixJumpConditionToMatrix();
92 switch ( GetStackType() )
93 {
94 case svMatrix:
95 {
96 ScMatrixRef pMat = PopMatrix();
97 if ( !pMat )
98 PushIllegalParameter();
99 else
100 {
101 FormulaConstTokenRef xNew;
102 ScTokenMatrixMap::const_iterator aMapIter;
103 // DoubleError handled by JumpMatrix
104 pMat->SetErrorInterpreter( nullptr);
105 SCSIZE nCols, nRows;
106 pMat->GetDimensions( nCols, nRows );
107 if ( nCols == 0 || nRows == 0 )
108 {
109 PushIllegalArgument();
110 return;
111 }
112 else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end()))
113 xNew = (*aMapIter).second;
114 else
115 {
116 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
117 pCur->GetOpCode(), nCols, nRows));
118 for ( SCSIZE nC=0; nC < nCols; ++nC )
119 {
120 for ( SCSIZE nR=0; nR < nRows; ++nR )
121 {
122 double fVal;
123 bool bTrue;
124 bool bIsValue = pMat->IsValue(nC, nR);
125 if (bIsValue)
126 {
127 fVal = pMat->GetDouble(nC, nR);
128 bIsValue = std::isfinite(fVal);
129 bTrue = bIsValue && (fVal != 0.0);
130 if (bTrue)
131 fVal = 1.0;
132 }
133 else
134 {
135 // Treat empty and empty path as 0, but string
136 // as error. ScMatrix::IsValueOrEmpty() returns
137 // true for any empty, empty path, empty cell,
138 // empty result.
139 bIsValue = pMat->IsValueOrEmpty(nC, nR);
140 bTrue = false;
141 fVal = (bIsValue ? 0.0 : CreateDoubleError( FormulaError::NoValue));
142 }
143 if ( bTrue )
144 { // TRUE
145 if( nJumpCount >= 2 )
146 { // THEN path
147 pJumpMat->SetJump( nC, nR, fVal,
148 pJump[ 1 ],
149 pJump[ nJumpCount ]);
150 }
151 else
152 { // no parameter given for THEN
153 pJumpMat->SetJump( nC, nR, fVal,
154 pJump[ nJumpCount ],
155 pJump[ nJumpCount ]);
156 }
157 }
158 else
159 { // FALSE
160 if( nJumpCount == 3 && bIsValue )
161 { // ELSE path
162 pJumpMat->SetJump( nC, nR, fVal,
163 pJump[ 2 ],
164 pJump[ nJumpCount ]);
165 }
166 else
167 { // no parameter given for ELSE,
168 // or DoubleError
169 pJumpMat->SetJump( nC, nR, fVal,
170 pJump[ nJumpCount ],
171 pJump[ nJumpCount ]);
172 }
173 }
174 }
175 }
176 xNew = new ScJumpMatrixToken( pJumpMat );
177 GetTokenMatrixMap().emplace(pCur, xNew);
178 }
179 if (!xNew)
180 {
181 PushIllegalArgument();
182 return;
183 }
184 PushTokenRef( xNew);
185 // set endpoint of path for main code line
186 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
187 }
188 }
189 break;
190 default:
191 {
192 if ( GetBool() )
193 { // TRUE
194 if( nJumpCount >= 2 )
195 { // THEN path
196 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
197 }
198 else
199 { // no parameter given for THEN
200 nFuncFmtType = SvNumFormatType::LOGICAL;
201 PushInt(1);
202 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
203 }
204 }
205 else
206 { // FALSE
207 if( nJumpCount == 3 )
208 { // ELSE path
209 aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] );
210 }
211 else
212 { // no parameter given for ELSE
213 nFuncFmtType = SvNumFormatType::LOGICAL;
214 PushInt(0);
215 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
216 }
217 }
218 }
219 }
220}
221
222/** Store a matrix value in another matrix in the context of that other matrix
223 is the result matrix of a jump matrix. All arguments must be valid and are
224 not checked. */
225static void lcl_storeJumpMatResult(
226 const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR )
227{
228 if ( pMat->IsValue( nC, nR ) )
229 {
230 double fVal = pMat->GetDouble( nC, nR );
231 pJumpMat->PutResultDouble( fVal, nC, nR );
232 }
233 else if ( pMat->IsEmpty( nC, nR ) )
234 {
235 pJumpMat->PutResultEmpty( nC, nR );
236 }
237 else
238 {
239 pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR);
240 }
241}
242
243void ScInterpreter::ScIfError( bool bNAonly )
244{
245 const short* pJump = pCur->GetJump();
246 short nJumpCount = pJump[ 0 ];
247 if (!sp || nJumpCount != 2)
248 {
249 // Reset nGlobalError here to not propagate the old error, if any.
250 nGlobalError = (sp ? FormulaError::ParameterExpected : FormulaError::UnknownStackVariable);
251 PushError( nGlobalError);
252 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
253 return;
254 }
255
256 FormulaConstTokenRef xToken( pStack[ sp - 1 ] );
257 bool bError = false;
258 FormulaError nOldGlobalError = nGlobalError;
259 nGlobalError = FormulaError::NONE;
260
261 MatrixJumpConditionToMatrix();
262 switch (GetStackType())
263 {
264 default:
265 Pop();
266 // Act on implicitly propagated error, if any.
267 if (nOldGlobalError != FormulaError::NONE)
268 nGlobalError = nOldGlobalError;
269 if (nGlobalError != FormulaError::NONE)
270 bError = true;
271 break;
272 case svError:
273 PopError();
274 bError = true;
275 break;
276 case svDoubleRef:
277 case svSingleRef:
278 {
279 ScAddress aAdr;
280 if (!PopDoubleRefOrSingleRef( aAdr))
281 bError = true;
282 else
283 {
284
285 ScRefCellValue aCell(mrDoc, aAdr);
286 nGlobalError = GetCellErrCode(aCell);
287 if (nGlobalError != FormulaError::NONE)
288 bError = true;
289 }
290 }
291 break;
292 case svExternalSingleRef:
293 case svExternalDoubleRef:
294 {
295 double fVal;
296 svl::SharedString aStr;
297 // Handles also existing jump matrix case and sets error on
298 // elements.
299 GetDoubleOrStringFromMatrix( fVal, aStr);
300 if (nGlobalError != FormulaError::NONE)
301 bError = true;
302 }
303 break;
304 case svMatrix:
305 {
306 const ScMatrixRef pMat = PopMatrix();
307 if (!pMat || (nGlobalError != FormulaError::NONE && (!bNAonly || nGlobalError == FormulaError::NotAvailable)))
308 {
309 bError = true;
310 break; // switch
311 }
312 // If the matrix has no queried error at all we can simply use
313 // it as result and don't need to bother with jump matrix.
314 SCSIZE nErrorCol = ::std::numeric_limits<SCSIZE>::max(),
315 nErrorRow = ::std::numeric_limits<SCSIZE>::max();
316 SCSIZE nCols, nRows;
317 pMat->GetDimensions( nCols, nRows );
318 if (nCols == 0 || nRows == 0)
319 {
320 bError = true;
321 break; // switch
322 }
323 for (SCSIZE nC=0; nC < nCols && !bError; ++nC)
324 {
325 for (SCSIZE nR=0; nR < nRows && !bError; ++nR)
326 {
327 FormulaError nErr = pMat->GetError( nC, nR );
328 if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
329 {
330 bError = true;
331 nErrorCol = nC;
332 nErrorRow = nR;
333 }
334 }
335 }
336 if (!bError)
337 break; // switch, we're done and have the result
338
339 FormulaConstTokenRef xNew;
340 ScTokenMatrixMap::const_iterator aMapIter;
341 if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end()))
342 {
343 xNew = (*aMapIter).second;
344 }
345 else
346 {
347 const ScMatrix* pMatPtr = pMat.get();
348 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
349 pCur->GetOpCode(), nCols, nRows));
350 // Init all jumps to no error to save single calls. Error
351 // is the exceptional condition.
352 const double fFlagResult = CreateDoubleError( FormulaError::JumpMatHasResult);
353 pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] );
354 // Up to first error position simply store results, no need
355 // to evaluate error conditions again.
356 SCSIZE nC = 0, nR = 0;
357 for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ )
358 {
359 for (nR = 0 ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR)
360 {
361 lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
362 }
363 if (nC != nErrorCol && nR != nErrorRow)
364 ++nC;
365 }
366 // Now the mixed cases.
367 for ( ; nC < nCols; ++nC)
368 {
369 for ( ; nR < nRows; ++nR)
370 {
371 FormulaError nErr = pMat->GetError( nC, nR );
372 if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
373 { // TRUE, THEN path
374 pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] );
375 }
376 else
377 { // FALSE, EMPTY path, store result instead
378 lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
379 }
380 }
381 nR = 0;
382 }
383 xNew = new ScJumpMatrixToken( pJumpMat );
384 GetTokenMatrixMap().emplace( pCur, xNew );
385 }
386 nGlobalError = nOldGlobalError;
387 PushTokenRef( xNew );
388 // set endpoint of path for main code line
389 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
390 return;
391 }
392 break;
393 }
394
395 if (bError && (!bNAonly || nGlobalError == FormulaError::NotAvailable))
396 {
397 // error, calculate 2nd argument
398 nGlobalError = FormulaError::NONE;
399 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
400 }
401 else
402 {
403 // no error, push 1st argument and continue
404 nGlobalError = nOldGlobalError;
405 PushTokenRef( xToken);
406 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
407 }
408}
409
410void ScInterpreter::ScChooseJump()
411{
412 // We have to set a jump, if there was none chosen because of an error set
413 // it to endpoint.
414 bool bHaveJump = false;
415 const short* pJump = pCur->GetJump();
416 short nJumpCount = pJump[ 0 ];
417 MatrixJumpConditionToMatrix();
418 switch ( GetStackType() )
419 {
420 case svMatrix:
421 {
422 ScMatrixRef pMat = PopMatrix();
423 if ( !pMat )
424 PushIllegalParameter();
425 else
426 {
427 FormulaConstTokenRef xNew;
428 ScTokenMatrixMap::const_iterator aMapIter;
429 // DoubleError handled by JumpMatrix
430 pMat->SetErrorInterpreter( nullptr);
431 SCSIZE nCols, nRows;
432 pMat->GetDimensions( nCols, nRows );
433 if ( nCols == 0 || nRows == 0 )
434 PushIllegalParameter();
435 else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find(
436 pCur)) != pTokenMatrixMap->end()))
437 xNew = (*aMapIter).second;
438 else
439 {
440 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
441 pCur->GetOpCode(), nCols, nRows));
442 for ( SCSIZE nC=0; nC < nCols; ++nC )
443 {
444 for ( SCSIZE nR=0; nR < nRows; ++nR )
445 {
446 double fVal;
447 bool bIsValue = pMat->IsValue(nC, nR);
448 if ( bIsValue )
449 {
450 fVal = pMat->GetDouble(nC, nR);
451 bIsValue = std::isfinite( fVal );
452 if ( bIsValue )
453 {
454 fVal = ::rtl::math::approxFloor( fVal);
455 if ( (fVal < 1) || (fVal >= nJumpCount))
456 {
457 bIsValue = false;
458 fVal = CreateDoubleError(
459 FormulaError::IllegalArgument);
460 }
461 }
462 }
463 else
464 {
465 fVal = CreateDoubleError( FormulaError::NoValue);
466 }
467 if ( bIsValue )
468 {
469 pJumpMat->SetJump( nC, nR, fVal,
470 pJump[ static_cast<short>(fVal) ],
471 pJump[ nJumpCount ]);
472 }
473 else
474 {
475 pJumpMat->SetJump( nC, nR, fVal,
476 pJump[ nJumpCount ],
477 pJump[ nJumpCount ]);
478 }
479 }
480 }
481 xNew = new ScJumpMatrixToken( pJumpMat );
482 GetTokenMatrixMap().emplace(pCur, xNew);
483 }
484 if (xNew)
485 {
486 PushTokenRef( xNew);
487 // set endpoint of path for main code line
488 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
489 bHaveJump = true;
490 }
491 }
492 }
493 break;
494 default:
495 {
496 sal_Int16 nJumpIndex = GetInt16();
497 if (nGlobalError == FormulaError::NONE && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount))
498 {
499 aCode.Jump( pJump[ static_cast<short>(nJumpIndex) ], pJump[ nJumpCount ] );
500 bHaveJump = true;
501 }
502 else
503 PushIllegalArgument();
504 }
505 }
506 if (!bHaveJump)
507 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
508}
509
510static void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, SCSIZE nParmCols, SCSIZE nParmRows )
511{
512 SCSIZE nJumpCols, nJumpRows;
513 SCSIZE nResCols, nResRows;
514 SCSIZE nAdjustCols, nAdjustRows;
515 pJumpM->GetDimensions( nJumpCols, nJumpRows );
516 pJumpM->GetResMatDimensions( nResCols, nResRows );
517 if (!(( nJumpCols == 1 && nParmCols > nResCols ) ||
518 ( nJumpRows == 1 && nParmRows > nResRows )))
519 return;
520
521 if ( nJumpCols == 1 && nJumpRows == 1 )
522 {
523 nAdjustCols = std::max(nParmCols, nResCols);
524 nAdjustRows = std::max(nParmRows, nResRows);
525 }
526 else if ( nJumpCols == 1 )
527 {
528 nAdjustCols = nParmCols;
529 nAdjustRows = nResRows;
530 }
531 else
532 {
533 nAdjustCols = nResCols;
534 nAdjustRows = nParmRows;
535 }
536 pJumpM->SetNewResMat( nAdjustCols, nAdjustRows );
537}
538
539bool ScInterpreter::JumpMatrix( short nStackLevel )
540{
541 pJumpMatrix = pStack[sp-nStackLevel]->GetJumpMatrix();
542 bool bHasResMat = pJumpMatrix->HasResultMatrix();
543 SCSIZE nC, nR;
544 if ( nStackLevel == 2 )
545 {
546 if ( aCode.HasStacked() )
547 aCode.Pop(); // pop what Jump() pushed
548 else
549 {
550 assert(!"pop goes the weasel")(static_cast <bool> (!"pop goes the weasel") ? void (0)
: __assert_fail ("!\"pop goes the weasel\"", "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
, 550, __extension__ __PRETTY_FUNCTION__))
;
551 }
552
553 if ( !bHasResMat )
554 {
555 Pop();
556 SetError( FormulaError::UnknownStackVariable );
557 }
558 else
559 {
560 pJumpMatrix->GetPos( nC, nR );
561 switch ( GetStackType() )
562 {
563 case svDouble:
564 {
565 double fVal = GetDouble();
566 if ( nGlobalError != FormulaError::NONE )
567 {
568 fVal = CreateDoubleError( nGlobalError );
569 nGlobalError = FormulaError::NONE;
570 }
571 pJumpMatrix->PutResultDouble( fVal, nC, nR );
572 }
573 break;
574 case svString:
575 {
576 svl::SharedString aStr = GetString();
577 if ( nGlobalError != FormulaError::NONE )
578 {
579 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
580 nC, nR);
581 nGlobalError = FormulaError::NONE;
582 }
583 else
584 pJumpMatrix->PutResultString(aStr, nC, nR);
585 }
586 break;
587 case svSingleRef:
588 {
589 FormulaConstTokenRef xRef = pStack[sp-1];
590 ScAddress aAdr;
591 PopSingleRef( aAdr );
592 if ( nGlobalError != FormulaError::NONE )
593 {
594 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
595 nC, nR);
596 nGlobalError = FormulaError::NONE;
597 }
598 else
599 {
600 ScRefCellValue aCell(mrDoc, aAdr);
601 if (aCell.hasEmptyValue())
602 pJumpMatrix->PutResultEmpty( nC, nR );
603 else if (aCell.hasNumeric())
604 {
605 double fVal = GetCellValue(aAdr, aCell);
606 if ( nGlobalError != FormulaError::NONE )
607 {
608 fVal = CreateDoubleError(
609 nGlobalError);
610 nGlobalError = FormulaError::NONE;
611 }
612 pJumpMatrix->PutResultDouble( fVal, nC, nR );
613 }
614 else
615 {
616 svl::SharedString aStr;
617 GetCellString(aStr, aCell);
618 if ( nGlobalError != FormulaError::NONE )
619 {
620 pJumpMatrix->PutResultDouble( CreateDoubleError(
621 nGlobalError), nC, nR);
622 nGlobalError = FormulaError::NONE;
623 }
624 else
625 pJumpMatrix->PutResultString(aStr, nC, nR);
626 }
627 }
628
629 formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16((sal_uInt16) 0xFFFF));
630 if (eReturnType == ParamClass::Reference)
631 {
632 /* TODO: What about error handling and do we actually
633 * need the result matrix above at all in this case? */
634 ScComplexRefData aRef;
635 aRef.Ref1 = aRef.Ref2 = *(xRef->GetSingleRef());
636 pJumpMatrix->GetRefList().push_back( aRef);
637 }
638 }
639 break;
640 case svDoubleRef:
641 { // upper left plus offset within matrix
642 FormulaConstTokenRef xRef = pStack[sp-1];
643 double fVal;
644 ScRange aRange;
645 PopDoubleRef( aRange );
646 if ( nGlobalError != FormulaError::NONE )
647 {
648 fVal = CreateDoubleError( nGlobalError );
649 nGlobalError = FormulaError::NONE;
650 pJumpMatrix->PutResultDouble( fVal, nC, nR );
651 }
652 else
653 {
654 // Do not modify the original range because we use it
655 // to adjust the size of the result matrix if necessary.
656 ScAddress aAdr( aRange.aStart);
657 sal_uLong nCol = static_cast<sal_uLong>(aAdr.Col()) + nC;
658 sal_uLong nRow = static_cast<sal_uLong>(aAdr.Row()) + nR;
659 if ((nCol > o3tl::make_unsigned(aRange.aEnd.Col()) &&
660 aRange.aEnd.Col() != aRange.aStart.Col())
661 || (nRow > o3tl::make_unsigned(aRange.aEnd.Row()) &&
662 aRange.aEnd.Row() != aRange.aStart.Row()))
663 {
664 fVal = CreateDoubleError( FormulaError::NotAvailable );
665 pJumpMatrix->PutResultDouble( fVal, nC, nR );
666 }
667 else
668 {
669 // Replicate column and/or row of a vector if it is
670 // one. Note that this could be a range reference
671 // that in fact consists of only one cell, e.g. A1:A1
672 if (aRange.aEnd.Col() == aRange.aStart.Col())
673 nCol = aRange.aStart.Col();
674 if (aRange.aEnd.Row() == aRange.aStart.Row())
675 nRow = aRange.aStart.Row();
676 aAdr.SetCol( static_cast<SCCOL>(nCol) );
677 aAdr.SetRow( static_cast<SCROW>(nRow) );
678 ScRefCellValue aCell(mrDoc, aAdr);
679 if (aCell.hasEmptyValue())
680 pJumpMatrix->PutResultEmpty( nC, nR );
681 else if (aCell.hasNumeric())
682 {
683 double fCellVal = GetCellValue(aAdr, aCell);
684 if ( nGlobalError != FormulaError::NONE )
685 {
686 fCellVal = CreateDoubleError(
687 nGlobalError);
688 nGlobalError = FormulaError::NONE;
689 }
690 pJumpMatrix->PutResultDouble( fCellVal, nC, nR );
691 }
692 else
693 {
694 svl::SharedString aStr;
695 GetCellString(aStr, aCell);
696 if ( nGlobalError != FormulaError::NONE )
697 {
698 pJumpMatrix->PutResultDouble( CreateDoubleError(
699 nGlobalError), nC, nR);
700 nGlobalError = FormulaError::NONE;
701 }
702 else
703 pJumpMatrix->PutResultString(aStr, nC, nR);
704 }
705 }
706 SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
707 SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
708 lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows );
709 }
710
711 formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16((sal_uInt16) 0xFFFF));
712 if (eReturnType == ParamClass::Reference)
713 {
714 /* TODO: What about error handling and do we actually
715 * need the result matrix above at all in this case? */
716 pJumpMatrix->GetRefList().push_back( *(xRef->GetDoubleRef()));
717 }
718 }
719 break;
720 case svExternalSingleRef:
721 {
722 ScExternalRefCache::TokenRef pToken;
723 PopExternalSingleRef(pToken);
724 if (nGlobalError != FormulaError::NONE)
725 {
726 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), nC, nR );
727 nGlobalError = FormulaError::NONE;
728 }
729 else
730 {
731 switch (pToken->GetType())
732 {
733 case svDouble:
734 pJumpMatrix->PutResultDouble( pToken->GetDouble(), nC, nR );
735 break;
736 case svString:
737 pJumpMatrix->PutResultString( pToken->GetString(), nC, nR );
738 break;
739 case svEmptyCell:
740 pJumpMatrix->PutResultEmpty( nC, nR );
741 break;
742 default:
743 // svError was already handled (set by
744 // PopExternalSingleRef()) with nGlobalError
745 // above.
746 assert(!"unhandled svExternalSingleRef case")(static_cast <bool> (!"unhandled svExternalSingleRef case"
) ? void (0) : __assert_fail ("!\"unhandled svExternalSingleRef case\""
, "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
, 746, __extension__ __PRETTY_FUNCTION__))
;
747 pJumpMatrix->PutResultDouble( CreateDoubleError(
748 FormulaError::UnknownStackVariable), nC, nR );
749 }
750 }
751 }
752 break;
753 case svExternalDoubleRef:
754 case svMatrix:
755 { // match matrix offsets
756 double fVal;
757 ScMatrixRef pMat = GetMatrix();
758 if ( nGlobalError != FormulaError::NONE )
759 {
760 fVal = CreateDoubleError( nGlobalError );
761 nGlobalError = FormulaError::NONE;
762 pJumpMatrix->PutResultDouble( fVal, nC, nR );
763 }
764 else if ( !pMat )
765 {
766 fVal = CreateDoubleError( FormulaError::UnknownVariable );
767 pJumpMatrix->PutResultDouble( fVal, nC, nR );
768 }
769 else
770 {
771 SCSIZE nCols, nRows;
772 pMat->GetDimensions( nCols, nRows );
773 if ((nCols <= nC && nCols != 1) ||
774 (nRows <= nR && nRows != 1))
775 {
776 fVal = CreateDoubleError( FormulaError::NotAvailable );
777 pJumpMatrix->PutResultDouble( fVal, nC, nR );
778 }
779 else
780 {
781 // GetMatrix() does SetErrorInterpreter() at the
782 // matrix, do not propagate an error from
783 // matrix->GetValue() as global error.
784 pMat->SetErrorInterpreter(nullptr);
785 lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR);
786 }
787 lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows );
788 }
789 }
790 break;
791 case svError:
792 {
793 PopError();
794 double fVal = CreateDoubleError( nGlobalError);
795 nGlobalError = FormulaError::NONE;
796 pJumpMatrix->PutResultDouble( fVal, nC, nR );
797 }
798 break;
799 default:
800 {
801 Pop();
802 double fVal = CreateDoubleError( FormulaError::IllegalArgument);
803 pJumpMatrix->PutResultDouble( fVal, nC, nR );
804 }
805 }
806 }
807 }
808 bool bCont = pJumpMatrix->Next( nC, nR );
809 if ( bCont )
810 {
811 double fBool;
812 short nStart, nNext, nStop;
813 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
814 while ( bCont && nStart == nNext )
815 { // push all results that have no jump path
816 if ( bHasResMat && (GetDoubleErrorValue( fBool) != FormulaError::JumpMatHasResult) )
817 {
818 // a false without path results in an empty path value
819 if ( fBool == 0.0 )
820 pJumpMatrix->PutResultEmptyPath( nC, nR );
821 else
822 pJumpMatrix->PutResultDouble( fBool, nC, nR );
823 }
824 bCont = pJumpMatrix->Next( nC, nR );
825 if ( bCont )
826 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
827 }
828 if ( bCont && nStart != nNext )
829 {
830 const ScTokenVec & rParams = pJumpMatrix->GetJumpParameters();
831 for ( auto const & i : rParams )
832 {
833 // This is not the current state of the interpreter, so
834 // push without error, and elements' errors are coded into
835 // double.
836 PushWithoutError(*i);
837 }
838 aCode.Jump( nStart, nNext, nStop );
839 }
840 }
841 if ( !bCont )
842 { // We're done with it, throw away jump matrix, keep result.
843 // For an intermediate result of Reference use the array of references
844 // if there are more than one reference and the current ForceArray
845 // context is ReferenceOrRefArray.
846 // Else (also for a final result of Reference) use the matrix.
847 // Treat the result of a jump command as final and use the matrix (see
848 // tdf#115493 for why).
849 if (pCur->GetInForceArray() == ParamClass::ReferenceOrRefArray &&
850 pJumpMatrix->GetRefList().size() > 1 &&
851 ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16((sal_uInt16) 0xFFFF)) == ParamClass::Reference &&
852 !FormulaCompiler::IsOpCodeJumpCommand( pJumpMatrix->GetOpCode()) &&
853 aCode.PeekNextOperator())
854 {
855 FormulaTokenRef xRef = new ScRefListToken(true);
856 *(xRef->GetRefList()) = pJumpMatrix->GetRefList();
857 pJumpMatrix = nullptr;
858 Pop();
859 PushTokenRef( xRef);
860 if (pTokenMatrixMap)
861 {
862 pTokenMatrixMap->erase( pCur);
863 // There's no result matrix to remember in this case.
864 }
865 }
866 else
867 {
868 ScMatrix* pResMat = pJumpMatrix->GetResultMatrix();
869 pJumpMatrix = nullptr;
870 Pop();
871 PushMatrix( pResMat );
872 // Remove jump matrix from map and remember result matrix in case it
873 // could be reused in another path of the same condition.
874 if (pTokenMatrixMap)
875 {
876 pTokenMatrixMap->erase( pCur);
877 pTokenMatrixMap->emplace(pCur, pStack[sp-1]);
878 }
879 }
880 return true;
881 }
882 return false;
883}
884
885double ScInterpreter::Compare( ScQueryOp eOp )
886{
887 sc::Compare aComp;
888 aComp.meOp = eOp;
889 aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
890 for( short i = 1; i >= 0; i-- )
891 {
892 sc::Compare::Cell& rCell = aComp.maCells[i];
893
894 switch ( GetRawStackType() )
895 {
896 case svEmptyCell:
897 Pop();
898 rCell.mbEmpty = true;
899 break;
900 case svMissing:
901 case svDouble:
902 rCell.mfValue = GetDouble();
903 rCell.mbValue = true;
904 break;
905 case svString:
906 rCell.maStr = GetString();
907 rCell.mbValue = false;
908 break;
909 case svDoubleRef :
910 case svSingleRef :
911 {
912 ScAddress aAdr;
913 if ( !PopDoubleRefOrSingleRef( aAdr ) )
914 break;
915 ScRefCellValue aCell(mrDoc, aAdr);
916 if (aCell.hasEmptyValue())
917 rCell.mbEmpty = true;
918 else if (aCell.hasString())
919 {
920 svl::SharedString aStr;
921 GetCellString(aStr, aCell);
922 rCell.maStr = aStr;
923 rCell.mbValue = false;
924 }
925 else
926 {
927 rCell.mfValue = GetCellValue(aAdr, aCell);
928 rCell.mbValue = true;
929 }
930 }
931 break;
932 case svExternalSingleRef:
933 {
934 ScMatrixRef pMat = GetMatrix();
935 if (!pMat)
936 {
937 SetError( FormulaError::IllegalParameter);
938 break;
939 }
940
941 SCSIZE nC, nR;
942 pMat->GetDimensions(nC, nR);
943 if (!nC || !nR)
944 {
945 SetError( FormulaError::IllegalParameter);
946 break;
947 }
948 if (pMat->IsEmpty(0, 0))
949 rCell.mbEmpty = true;
950 else if (pMat->IsStringOrEmpty(0, 0))
951 {
952 rCell.maStr = pMat->GetString(0, 0);
953 rCell.mbValue = false;
954 }
955 else
956 {
957 rCell.mfValue = pMat->GetDouble(0, 0);
958 rCell.mbValue = true;
959 }
960 }
961 break;
962 case svExternalDoubleRef:
963 // TODO: Find out how to handle this...
964 // Xcl generates a position dependent intersection using
965 // col/row, as it seems to do for all range references, not
966 // only in compare context. We'd need a general implementation
967 // for that behavior similar to svDoubleRef in scalar and array
968 // mode. Which also means we'd have to change all places where
969 // it currently is handled along with svMatrix.
970 default:
971 PopError();
972 SetError( FormulaError::IllegalParameter);
973 break;
974 }
975 }
976 if( nGlobalError != FormulaError::NONE )
977 return 0;
978 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
979 return sc::CompareFunc(aComp);
980}
981
982sc::RangeMatrix ScInterpreter::CompareMat( ScQueryOp eOp, sc::CompareOptions* pOptions )
983{
984 sc::Compare aComp;
985 aComp.meOp = eOp;
986 aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
987 sc::RangeMatrix aMat[2];
988 ScAddress aAdr;
989 for( short i = 1; i >= 0; i-- )
990 {
991 sc::Compare::Cell& rCell = aComp.maCells[i];
992
993 switch (GetRawStackType())
994 {
995 case svEmptyCell:
996 Pop();
997 rCell.mbEmpty = true;
998 break;
999 case svMissing:
1000 case svDouble:
1001 rCell.mfValue = GetDouble();
1002 rCell.mbValue = true;
1003 break;
1004 case svString:
1005 rCell.maStr = GetString();
1006 rCell.mbValue = false;
1007 break;
1008 case svSingleRef:
1009 {
1010 PopSingleRef( aAdr );
1011 ScRefCellValue aCell(mrDoc, aAdr);
1012 if (aCell.hasEmptyValue())
1013 rCell.mbEmpty = true;
1014 else if (aCell.hasString())
1015 {
1016 svl::SharedString aStr;
1017 GetCellString(aStr, aCell);
1018 rCell.maStr = aStr;
1019 rCell.mbValue = false;
1020 }
1021 else
1022 {
1023 rCell.mfValue = GetCellValue(aAdr, aCell);
1024 rCell.mbValue = true;
1025 }
1026 }
1027 break;
1028 case svExternalSingleRef:
1029 case svExternalDoubleRef:
1030 case svDoubleRef:
1031 case svMatrix:
1032 aMat[i] = GetRangeMatrix();
1033 if (!aMat[i].mpMat)
1034 SetError( FormulaError::IllegalParameter);
1035 else
1036 aMat[i].mpMat->SetErrorInterpreter(nullptr);
1037 // errors are transported as DoubleError inside matrix
1038 break;
1039 default:
1040 PopError();
1041 SetError( FormulaError::IllegalParameter);
1042 break;
1043 }
1044 }
1045
1046 sc::RangeMatrix aRes;
1047
1048 if (nGlobalError != FormulaError::NONE)
1049 {
1050 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
1051 return aRes;
1052 }
1053
1054 if (aMat[0].mpMat && aMat[1].mpMat)
1055 {
1056 SCSIZE nC0, nC1;
1057 SCSIZE nR0, nR1;
1058 aMat[0].mpMat->GetDimensions(nC0, nR0);
1059 aMat[1].mpMat->GetDimensions(nC1, nR1);
1060 SCSIZE nC = std::max( nC0, nC1 );
1061 SCSIZE nR = std::max( nR0, nR1 );
1062 aRes.mpMat = GetNewMat( nC, nR);
1063 if (!aRes.mpMat)
1064 return aRes;
1065 for ( SCSIZE j=0; j<nC; j++ )
1066 {
1067 for ( SCSIZE k=0; k<nR; k++ )
1068 {
1069 SCSIZE nCol = j, nRow = k;
1070 if (aMat[0].mpMat->ValidColRowOrReplicated(nCol, nRow) &&
1071 aMat[1].mpMat->ValidColRowOrReplicated(nCol, nRow))
1072 {
1073 for ( short i=1; i>=0; i-- )
1074 {
1075 sc::Compare::Cell& rCell = aComp.maCells[i];
1076
1077 if (aMat[i].mpMat->IsStringOrEmpty(j, k))
1078 {
1079 rCell.mbValue = false;
1080 rCell.maStr = aMat[i].mpMat->GetString(j, k);
1081 rCell.mbEmpty = aMat[i].mpMat->IsEmpty(j, k);
1082 }
1083 else
1084 {
1085 rCell.mbValue = true;
1086 rCell.mfValue = aMat[i].mpMat->GetDouble(j, k);
1087 rCell.mbEmpty = false;
1088 }
1089 }
1090 aRes.mpMat->PutDouble( sc::CompareFunc( aComp, pOptions), j, k);
1091 }
1092 else
1093 aRes.mpMat->PutError( FormulaError::NoValue, j, k);
1094 }
1095 }
1096
1097 switch (eOp)
1098 {
1099 case SC_EQUAL:
1100 aRes.mpMat->CompareEqual();
1101 break;
1102 case SC_LESS:
1103 aRes.mpMat->CompareLess();
1104 break;
1105 case SC_GREATER:
1106 aRes.mpMat->CompareGreater();
1107 break;
1108 case SC_LESS_EQUAL:
1109 aRes.mpMat->CompareLessEqual();
1110 break;
1111 case SC_GREATER_EQUAL:
1112 aRes.mpMat->CompareGreaterEqual();
1113 break;
1114 case SC_NOT_EQUAL:
1115 aRes.mpMat->CompareNotEqual();
1116 break;
1117 default:
1118 SAL_WARN("sc", "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp))do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sc")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScInterpreter::QueryMat: unhandled comparison operator: "
<< static_cast<int>(eOp)) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
":" "1118" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "ScInterpreter::QueryMat: unhandled comparison operator: "
<< static_cast<int>(eOp)), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "ScInterpreter::QueryMat: unhandled comparison operator: "
<< static_cast<int>(eOp); ::sal::detail::log( (::
SAL_DETAIL_LOG_LEVEL_WARN), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
":" "1118" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScInterpreter::QueryMat: unhandled comparison operator: "
<< static_cast<int>(eOp)) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
":" "1118" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "ScInterpreter::QueryMat: unhandled comparison operator: "
<< static_cast<int>(eOp)), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "ScInterpreter::QueryMat: unhandled comparison operator: "
<< static_cast<int>(eOp); ::sal::detail::log( (::
SAL_DETAIL_LOG_LEVEL_WARN), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
":" "1118" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1119 aRes.mpMat.reset();
1120 return aRes;
1121 }
1122 }
1123 else if (aMat[0].mpMat || aMat[1].mpMat)
1124 {
1125 size_t i = ( aMat[0].mpMat ? 0 : 1);
1126
1127 aRes.mnCol1 = aMat[i].mnCol1;
1128 aRes.mnRow1 = aMat[i].mnRow1;
1129 aRes.mnTab1 = aMat[i].mnTab1;
1130 aRes.mnCol2 = aMat[i].mnCol2;
1131 aRes.mnRow2 = aMat[i].mnRow2;
1132 aRes.mnTab2 = aMat[i].mnTab2;
1133
1134 ScMatrix& rMat = *aMat[i].mpMat;
1135 aRes.mpMat = rMat.CompareMatrix(aComp, i, pOptions);
1136 if (!aRes.mpMat)
1137 return aRes;
1138 }
1139
1140 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
1141 return aRes;
1142}
1143
1144ScMatrixRef ScInterpreter::QueryMat( const ScMatrixRef& pMat, sc::CompareOptions& rOptions )
1145{
1146 SvNumFormatType nSaveCurFmtType = nCurFmtType;
1147 SvNumFormatType nSaveFuncFmtType = nFuncFmtType;
1148 PushMatrix( pMat);
1149 const ScQueryEntry::Item& rItem = rOptions.aQueryEntry.GetQueryItem();
1150 if (rItem.meType == ScQueryEntry::ByString)
1151 PushString(rItem.maString.getString());
1152 else
1153 PushDouble(rItem.mfVal);
1154 ScMatrixRef pResultMatrix = CompareMat(rOptions.aQueryEntry.eOp, &rOptions).mpMat;
1155 nCurFmtType = nSaveCurFmtType;
1156 nFuncFmtType = nSaveFuncFmtType;
1157 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
1158 {
1159 SetError( FormulaError::IllegalParameter);
1160 return pResultMatrix;
1161 }
1162
1163 return pResultMatrix;
1164}
1165
1166void ScInterpreter::ScEqual()
1167{
1168 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1169 {
1170 sc::RangeMatrix aMat = CompareMat(SC_EQUAL);
1171 if (!aMat.mpMat)
1172 {
1173 PushIllegalParameter();
1174 return;
1175 }
1176
1177 PushMatrix(aMat);
1178 }
1179 else
1180 PushInt( int(Compare( SC_EQUAL) == 0) );
1181}
1182
1183void ScInterpreter::ScNotEqual()
1184{
1185 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1186 {
1187 sc::RangeMatrix aMat = CompareMat(SC_NOT_EQUAL);
1188 if (!aMat.mpMat)
1189 {
1190 PushIllegalParameter();
1191 return;
1192 }
1193
1194 PushMatrix(aMat);
1195 }
1196 else
1197 PushInt( int(Compare( SC_NOT_EQUAL) != 0) );
1198}
1199
1200void ScInterpreter::ScLess()
1201{
1202 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1203 {
1204 sc::RangeMatrix aMat = CompareMat(SC_LESS);
1205 if (!aMat.mpMat)
1206 {
1207 PushIllegalParameter();
1208 return;
1209 }
1210
1211 PushMatrix(aMat);
1212 }
1213 else
1214 PushInt( int(Compare( SC_LESS) < 0) );
1215}
1216
1217void ScInterpreter::ScGreater()
1218{
1219 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1220 {
1221 sc::RangeMatrix aMat = CompareMat(SC_GREATER);
1222 if (!aMat.mpMat)
1223 {
1224 PushIllegalParameter();
1225 return;
1226 }
1227
1228 PushMatrix(aMat);
1229 }
1230 else
1231 PushInt( int(Compare( SC_GREATER) > 0) );
1232}
1233
1234void ScInterpreter::ScLessEqual()
1235{
1236 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1237 {
1238 sc::RangeMatrix aMat = CompareMat(SC_LESS_EQUAL);
1239 if (!aMat.mpMat)
1240 {
1241 PushIllegalParameter();
1242 return;
1243 }
1244
1245 PushMatrix(aMat);
1246 }
1247 else
1248 PushInt( int(Compare( SC_LESS_EQUAL) <= 0) );
1249}
1250
1251void ScInterpreter::ScGreaterEqual()
1252{
1253 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1254 {
1255 sc::RangeMatrix aMat = CompareMat(SC_GREATER_EQUAL);
1256 if (!aMat.mpMat)
1257 {
1258 PushIllegalParameter();
1259 return;
1260 }
1261
1262 PushMatrix(aMat);
1263 }
1264 else
1265 PushInt( int(Compare( SC_GREATER_EQUAL) >= 0) );
1266}
1267
1268void ScInterpreter::ScAnd()
1269{
1270 nFuncFmtType = SvNumFormatType::LOGICAL;
1271 short nParamCount = GetByte();
1272 if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1273 return;
1274
1275 bool bHaveValue = false;
1276 bool bRes = true;
1277 size_t nRefInList = 0;
1278 while( nParamCount-- > 0)
1279 {
1280 if ( nGlobalError == FormulaError::NONE )
1281 {
1282 switch ( GetStackType() )
1283 {
1284 case svDouble :
1285 bHaveValue = true;
1286 bRes &= ( PopDouble() != 0.0 );
1287 break;
1288 case svString :
1289 Pop();
1290 SetError( FormulaError::NoValue );
1291 break;
1292 case svSingleRef :
1293 {
1294 ScAddress aAdr;
1295 PopSingleRef( aAdr );
1296 if ( nGlobalError == FormulaError::NONE )
1297 {
1298 ScRefCellValue aCell(mrDoc, aAdr);
1299 if (aCell.hasNumeric())
1300 {
1301 bHaveValue = true;
1302 bRes &= ( GetCellValue(aAdr, aCell) != 0.0 );
1303 }
1304 // else: Xcl raises no error here
1305 }
1306 }
1307 break;
1308 case svDoubleRef:
1309 case svRefList:
1310 {
1311 ScRange aRange;
1312 PopDoubleRef( aRange, nParamCount, nRefInList);
1313 if ( nGlobalError == FormulaError::NONE )
1314 {
1315 double fVal;
1316 FormulaError nErr = FormulaError::NONE;
1317 ScValueIterator aValIter( mrDoc, aRange );
1318 if ( aValIter.GetFirst( fVal, nErr ) && nErr == FormulaError::NONE )
1319 {
1320 bHaveValue = true;
1321 do
1322 {
1323 bRes &= ( fVal != 0.0 );
1324 } while ( (nErr == FormulaError::NONE) &&
1325 aValIter.GetNext( fVal, nErr ) );
1326 }
1327 SetError( nErr );
1328 }
1329 }
1330 break;
1331 case svExternalSingleRef:
1332 case svExternalDoubleRef:
1333 case svMatrix:
1334 {
1335 ScMatrixRef pMat = GetMatrix();
1336 if ( pMat )
1337 {
1338 bHaveValue = true;
1339 double fVal = pMat->And();
1340 FormulaError nErr = GetDoubleErrorValue( fVal );
1341 if ( nErr != FormulaError::NONE )
1342 {
1343 SetError( nErr );
1344 bRes = false;
1345 }
1346 else
1347 bRes &= (fVal != 0.0);
1348 }
1349 // else: GetMatrix did set FormulaError::IllegalParameter
1350 }
1351 break;
1352 default:
1353 Pop();
1354 SetError( FormulaError::IllegalParameter);
1355 }
1356 }
1357 else
1358 Pop();
1359 }
1360 if ( bHaveValue )
1361 PushInt( int(bRes) );
1362 else
1363 PushNoValue();
1364}
1365
1366void ScInterpreter::ScOr()
1367{
1368 nFuncFmtType = SvNumFormatType::LOGICAL;
1369 short nParamCount = GetByte();
1370 if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1371 return;
1372
1373 bool bHaveValue = false;
1374 bool bRes = false;
1375 size_t nRefInList = 0;
1376 while( nParamCount-- > 0)
1377 {
1378 if ( nGlobalError == FormulaError::NONE )
1379 {
1380 switch ( GetStackType() )
1381 {
1382 case svDouble :
1383 bHaveValue = true;
1384 bRes |= ( PopDouble() != 0.0 );
1385 break;
1386 case svString :
1387 Pop();
1388 SetError( FormulaError::NoValue );
1389 break;
1390 case svSingleRef :
1391 {
1392 ScAddress aAdr;
1393 PopSingleRef( aAdr );
1394 if ( nGlobalError == FormulaError::NONE )
1395 {
1396 ScRefCellValue aCell(mrDoc, aAdr);
1397 if (aCell.hasNumeric())
1398 {
1399 bHaveValue = true;
1400 bRes |= ( GetCellValue(aAdr, aCell) != 0.0 );
1401 }
1402 // else: Xcl raises no error here
1403 }
1404 }
1405 break;
1406 case svDoubleRef:
1407 case svRefList:
1408 {
1409 ScRange aRange;
1410 PopDoubleRef( aRange, nParamCount, nRefInList);
1411 if ( nGlobalError == FormulaError::NONE )
1412 {
1413 double fVal;
1414 FormulaError nErr = FormulaError::NONE;
1415 ScValueIterator aValIter( mrDoc, aRange );
1416 if ( aValIter.GetFirst( fVal, nErr ) )
1417 {
1418 bHaveValue = true;
1419 do
1420 {
1421 bRes |= ( fVal != 0.0 );
1422 } while ( (nErr == FormulaError::NONE) &&
1423 aValIter.GetNext( fVal, nErr ) );
1424 }
1425 SetError( nErr );
1426 }
1427 }
1428 break;
1429 case svExternalSingleRef:
1430 case svExternalDoubleRef:
1431 case svMatrix:
1432 {
1433 bHaveValue = true;
1434 ScMatrixRef pMat = GetMatrix();
1435 if ( pMat )
1436 {
1437 bHaveValue = true;
1438 double fVal = pMat->Or();
1439 FormulaError nErr = GetDoubleErrorValue( fVal );
1440 if ( nErr != FormulaError::NONE )
1441 {
1442 SetError( nErr );
1443 bRes = false;
1444 }
1445 else
1446 bRes |= (fVal != 0.0);
1447 }
1448 // else: GetMatrix did set FormulaError::IllegalParameter
1449 }
1450 break;
1451 default:
1452 Pop();
1453 SetError( FormulaError::IllegalParameter);
1454 }
1455 }
1456 else
1457 Pop();
1458 }
1459 if ( bHaveValue )
1460 PushInt( int(bRes) );
1461 else
1462 PushNoValue();
1463}
1464
1465void ScInterpreter::ScXor()
1466{
1467
1468 nFuncFmtType = SvNumFormatType::LOGICAL;
1469 short nParamCount = GetByte();
1470 if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1471 return;
1472
1473 bool bHaveValue = false;
1474 bool bRes = false;
1475 size_t nRefInList = 0;
1476 while( nParamCount-- > 0)
1477 {
1478 if ( nGlobalError == FormulaError::NONE )
1479 {
1480 switch ( GetStackType() )
1481 {
1482 case svDouble :
1483 bHaveValue = true;
1484 bRes ^= ( PopDouble() != 0.0 );
1485 break;
1486 case svString :
1487 Pop();
1488 SetError( FormulaError::NoValue );
1489 break;
1490 case svSingleRef :
1491 {
1492 ScAddress aAdr;
1493 PopSingleRef( aAdr );
1494 if ( nGlobalError == FormulaError::NONE )
1495 {
1496 ScRefCellValue aCell(mrDoc, aAdr);
1497 if (aCell.hasNumeric())
1498 {
1499 bHaveValue = true;
1500 bRes ^= ( GetCellValue(aAdr, aCell) != 0.0 );
1501 }
1502 /* TODO: set error? Excel doesn't have XOR, but
1503 * doesn't set an error in this case for AND and
1504 * OR. */
1505 }
1506 }
1507 break;
1508 case svDoubleRef:
1509 case svRefList:
1510 {
1511 ScRange aRange;
1512 PopDoubleRef( aRange, nParamCount, nRefInList);
1513 if ( nGlobalError == FormulaError::NONE )
1514 {
1515 double fVal;
1516 FormulaError nErr = FormulaError::NONE;
1517 ScValueIterator aValIter( mrDoc, aRange );
1518 if ( aValIter.GetFirst( fVal, nErr ) )
1519 {
1520 bHaveValue = true;
1521 do
1522 {
1523 bRes ^= ( fVal != 0.0 );
1524 } while ( (nErr == FormulaError::NONE) &&
1525 aValIter.GetNext( fVal, nErr ) );
1526 }
1527 SetError( nErr );
1528 }
1529 }
1530 break;
1531 case svExternalSingleRef:
1532 case svExternalDoubleRef:
1533 case svMatrix:
1534 {
1535 bHaveValue = true;
1536 ScMatrixRef pMat = GetMatrix();
1537 if ( pMat )
1538 {
1539 bHaveValue = true;
1540 double fVal = pMat->Xor();
1541 FormulaError nErr = GetDoubleErrorValue( fVal );
1542 if ( nErr != FormulaError::NONE )
1543 {
1544 SetError( nErr );
1545 bRes = false;
1546 }
1547 else
1548 bRes ^= ( fVal != 0.0 );
1549 }
1550 // else: GetMatrix did set FormulaError::IllegalParameter
1551 }
1552 break;
1553 default:
1554 Pop();
1555 SetError( FormulaError::IllegalParameter);
1556 }
1557 }
1558 else
1559 Pop();
1560 }
1561 if ( bHaveValue )
1562 PushInt( int(bRes) );
1563 else
1564 PushNoValue();
1565}
1566
1567void ScInterpreter::ScNeg()
1568{
1569 // Simple negation doesn't change current format type to number, keep
1570 // current type.
1571 nFuncFmtType = nCurFmtType;
1572 switch ( GetStackType() )
1573 {
1574 case svMatrix :
1575 {
1576 ScMatrixRef pMat = GetMatrix();
1577 if ( !pMat )
1578 PushIllegalParameter();
1579 else
1580 {
1581 SCSIZE nC, nR;
1582 pMat->GetDimensions( nC, nR );
1583 ScMatrixRef pResMat = GetNewMat( nC, nR);
1584 if ( !pResMat )
1585 PushIllegalArgument();
1586 else
1587 {
1588 pMat->NegOp( *pResMat);
1589 PushMatrix( pResMat );
1590 }
1591 }
1592 }
1593 break;
1594 default:
1595 PushDouble( -GetDouble() );
1596 }
1597}
1598
1599void ScInterpreter::ScPercentSign()
1600{
1601 nFuncFmtType = SvNumFormatType::PERCENT;
1602 const FormulaToken* pSaveCur = pCur;
1603 sal_uInt8 nSavePar = cPar;
1604 PushInt( 100 );
1605 cPar = 2;
1606 FormulaByteToken aDivOp( ocDiv, cPar );
1607 pCur = &aDivOp;
1608 ScDiv();
1609 pCur = pSaveCur;
1610 cPar = nSavePar;
1611}
1612
1613void ScInterpreter::ScNot()
1614{
1615 nFuncFmtType = SvNumFormatType::LOGICAL;
1616 switch ( GetStackType() )
1617 {
1618 case svMatrix :
1619 {
1620 ScMatrixRef pMat = GetMatrix();
1621 if ( !pMat )
1622 PushIllegalParameter();
1623 else
1624 {
1625 SCSIZE nC, nR;
1626 pMat->GetDimensions( nC, nR );
1627 ScMatrixRef pResMat = GetNewMat( nC, nR);
1628 if ( !pResMat )
1629 PushIllegalArgument();
1630 else
1631 {
1632 pMat->NotOp( *pResMat);
1633 PushMatrix( pResMat );
1634 }
1635 }
1636 }
1637 break;
1638 default:
1639 PushInt( int(GetDouble() == 0.0) );
1640 }
1641}
1642
1643void ScInterpreter::ScBitAnd()
1644{
1645
1646 if ( !MustHaveParamCount( GetByte(), 2 ) )
1647 return;
1648
1649 double num1 = ::rtl::math::approxFloor( GetDouble());
1650 double num2 = ::rtl::math::approxFloor( GetDouble());
1651 if ( (num1 >= n2power48) || (num1 < 0) ||
1652 (num2 >= n2power48) || (num2 < 0))
1653 PushIllegalArgument();
1654 else
1655 PushDouble (static_cast<sal_uInt64>(num1) & static_cast<sal_uInt64>(num2));
1656}
1657
1658void ScInterpreter::ScBitOr()
1659{
1660
1661 if ( !MustHaveParamCount( GetByte(), 2 ) )
1662 return;
1663
1664 double num1 = ::rtl::math::approxFloor( GetDouble());
1665 double num2 = ::rtl::math::approxFloor( GetDouble());
1666 if ( (num1 >= n2power48) || (num1 < 0) ||
1667 (num2 >= n2power48) || (num2 < 0))
1668 PushIllegalArgument();
1669 else
1670 PushDouble (static_cast<sal_uInt64>(num1) | static_cast<sal_uInt64>(num2));
1671}
1672
1673void ScInterpreter::ScBitXor()
1674{
1675
1676 if ( !MustHaveParamCount( GetByte(), 2 ) )
1677 return;
1678
1679 double num1 = ::rtl::math::approxFloor( GetDouble());
1680 double num2 = ::rtl::math::approxFloor( GetDouble());
1681 if ( (num1 >= n2power48) || (num1 < 0) ||
1682 (num2 >= n2power48) || (num2 < 0))
1683 PushIllegalArgument();
1684 else
1685 PushDouble (static_cast<sal_uInt64>(num1) ^ static_cast<sal_uInt64>(num2));
1686}
1687
1688void ScInterpreter::ScBitLshift()
1689{
1690
1691 if ( !MustHaveParamCount( GetByte(), 2 ) )
1692 return;
1693
1694 double fShift = ::rtl::math::approxFloor( GetDouble());
1695 double num = ::rtl::math::approxFloor( GetDouble());
1696 if ((num >= n2power48) || (num < 0))
1697 PushIllegalArgument();
1698 else
1699 {
1700 double fRes;
1701 if (fShift < 0)
1702 fRes = ::rtl::math::approxFloor( num / pow( 2.0, -fShift));
1703 else if (fShift == 0)
1704 fRes = num;
1705 else
1706 fRes = num * pow( 2.0, fShift);
1707 PushDouble( fRes);
1708 }
1709}
1710
1711void ScInterpreter::ScBitRshift()
1712{
1713
1714 if ( !MustHaveParamCount( GetByte(), 2 ) )
1715 return;
1716
1717 double fShift = ::rtl::math::approxFloor( GetDouble());
1718 double num = ::rtl::math::approxFloor( GetDouble());
1719 if ((num >= n2power48) || (num < 0))
1720 PushIllegalArgument();
1721 else
1722 {
1723 double fRes;
1724 if (fShift < 0)
1725 fRes = num * pow( 2.0, -fShift);
1726 else if (fShift == 0)
1727 fRes = num;
1728 else
1729 fRes = ::rtl::math::approxFloor( num / pow( 2.0, fShift));
1730 PushDouble( fRes);
1731 }
1732}
1733
1734void ScInterpreter::ScPi()
1735{
1736 PushDouble(F_PI3.14159265358979323846);
1737}
1738
1739void ScInterpreter::ScRandomImpl( const std::function<double( double fFirst, double fLast )>& RandomFunc,
1740 double fFirst, double fLast )
1741{
1742 if (bMatrixFormula)
1743 {
1744 SCCOL nCols = 0;
1745 SCROW nRows = 0;
1746 if (pMyFormulaCell)
1747 pMyFormulaCell->GetMatColsRows( nCols, nRows);
1748
1749 if (nCols == 1 && nRows == 1)
1750 {
1751 // For compatibility with existing
1752 // com.sun.star.sheet.FunctionAccess.callFunction() calls that per
1753 // default are executed in array context unless
1754 // FA.setPropertyValue("IsArrayFunction",False) was set, return a
1755 // scalar double instead of a 1x1 matrix object. tdf#128218
1756 PushDouble( RandomFunc( fFirst, fLast));
1757 return;
1758 }
1759
1760 // ScViewFunc::EnterMatrix() might be asking for
1761 // ScFormulaCell::GetResultDimensions(), which here are none so create
1762 // a 1x1 matrix at least which exactly is the case when EnterMatrix()
1763 // asks for a not selected range.
1764 if (nCols == 0)
1765 nCols = 1;
1766 if (nRows == 0)
1767 nRows = 1;
1768 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows));
1769 if (!pResMat)
1770 PushError( FormulaError::MatrixSize);
1771 else
1772 {
1773 for (SCCOL i=0; i < nCols; ++i)
1774 {
1775 for (SCROW j=0; j < nRows; ++j)
1776 {
1777 pResMat->PutDouble( RandomFunc( fFirst, fLast),
1778 static_cast<SCSIZE>(i), static_cast<SCSIZE>(j));
1779 }
1780 }
1781 PushMatrix( pResMat);
1782 }
1783 }
1784 else
1785 {
1786 PushDouble( RandomFunc( fFirst, fLast));
1787 }
1788}
1789
1790void ScInterpreter::ScRandom()
1791{
1792 auto RandomFunc = []( double, double )
1793 {
1794 return comphelper::rng::uniform_real_distribution();
1795 };
1796 ScRandomImpl( RandomFunc, 0.0, 0.0);
1797}
1798
1799void ScInterpreter::ScRandbetween()
1800{
1801 if (!MustHaveParamCount( GetByte(), 2))
1802 return;
1803
1804 // Same like scaddins/source/analysis/analysis.cxx
1805 // AnalysisAddIn::getRandbetween()
1806 double fMax = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
1807 double fMin = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
1808 if (nGlobalError != FormulaError::NONE || fMin > fMax)
1809 {
1810 PushIllegalArgument();
1811 return;
1812 }
1813 fMax = std::nextafter( fMax+1, -DBL_MAX1.7976931348623157e+308);
1814 auto RandomFunc = []( double fFirst, double fLast )
1815 {
1816 return floor( comphelper::rng::uniform_real_distribution( fFirst, fLast));
1817 };
1818 ScRandomImpl( RandomFunc, fMin, fMax);
1819}
1820
1821void ScInterpreter::ScTrue()
1822{
1823 nFuncFmtType = SvNumFormatType::LOGICAL;
1824 PushInt(1);
1825}
1826
1827void ScInterpreter::ScFalse()
1828{
1829 nFuncFmtType = SvNumFormatType::LOGICAL;
1830 PushInt(0);
1831}
1832
1833void ScInterpreter::ScDeg()
1834{
1835 PushDouble(basegfx::rad2deg(GetDouble()));
1836}
1837
1838void ScInterpreter::ScRad()
1839{
1840 PushDouble(basegfx::deg2rad(GetDouble()));
1841}
1842
1843void ScInterpreter::ScSin()
1844{
1845 PushDouble(::rtl::math::sin(GetDouble()));
1846}
1847
1848void ScInterpreter::ScCos()
1849{
1850 PushDouble(::rtl::math::cos(GetDouble()));
1851}
1852
1853void ScInterpreter::ScTan()
1854{
1855 PushDouble(::rtl::math::tan(GetDouble()));
1856}
1857
1858void ScInterpreter::ScCot()
1859{
1860 PushDouble(1.0 / ::rtl::math::tan(GetDouble()));
1861}
1862
1863void ScInterpreter::ScArcSin()
1864{
1865 PushDouble(asin(GetDouble()));
1866}
1867
1868void ScInterpreter::ScArcCos()
1869{
1870 PushDouble(acos(GetDouble()));
1871}
1872
1873void ScInterpreter::ScArcTan()
1874{
1875 PushDouble(atan(GetDouble()));
1876}
1877
1878void ScInterpreter::ScArcCot()
1879{
1880 PushDouble((F_PI21.57079632679489661923) - atan(GetDouble()));
1881}
1882
1883void ScInterpreter::ScSinHyp()
1884{
1885 PushDouble(sinh(GetDouble()));
1886}
1887
1888void ScInterpreter::ScCosHyp()
1889{
1890 PushDouble(cosh(GetDouble()));
1891}
1892
1893void ScInterpreter::ScTanHyp()
1894{
1895 PushDouble(tanh(GetDouble()));
1896}
1897
1898void ScInterpreter::ScCotHyp()
1899{
1900 PushDouble(1.0 / tanh(GetDouble()));
1901}
1902
1903void ScInterpreter::ScArcSinHyp()
1904{
1905 PushDouble( ::rtl::math::asinh( GetDouble()));
1906}
1907
1908void ScInterpreter::ScArcCosHyp()
1909{
1910 double fVal = GetDouble();
1911 if (fVal < 1.0)
1912 PushIllegalArgument();
1913 else
1914 PushDouble( ::rtl::math::acosh( fVal));
1915}
1916
1917void ScInterpreter::ScArcTanHyp()
1918{
1919 double fVal = GetDouble();
1920 if (fabs(fVal) >= 1.0)
1921 PushIllegalArgument();
1922 else
1923 PushDouble( ::rtl::math::atanh( fVal));
1924}
1925
1926void ScInterpreter::ScArcCotHyp()
1927{
1928 double nVal = GetDouble();
1929 if (fabs(nVal) <= 1.0)
1930 PushIllegalArgument();
1931 else
1932 PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0)));
1933}
1934
1935void ScInterpreter::ScCosecant()
1936{
1937 PushDouble(1.0 / ::rtl::math::sin(GetDouble()));
1938}
1939
1940void ScInterpreter::ScSecant()
1941{
1942 PushDouble(1.0 / ::rtl::math::cos(GetDouble()));
1943}
1944
1945void ScInterpreter::ScCosecantHyp()
1946{
1947 PushDouble(1.0 / sinh(GetDouble()));
1948}
1949
1950void ScInterpreter::ScSecantHyp()
1951{
1952 PushDouble(1.0 / cosh(GetDouble()));
1953}
1954
1955void ScInterpreter::ScExp()
1956{
1957 PushDouble(exp(GetDouble()));
1958}
1959
1960void ScInterpreter::ScSqrt()
1961{
1962 double fVal = GetDouble();
1963 if (fVal >= 0.0)
1964 PushDouble(sqrt(fVal));
1965 else
1966 PushIllegalArgument();
1967}
1968
1969void ScInterpreter::ScIsEmpty()
1970{
1971 short nRes = 0;
1972 nFuncFmtType = SvNumFormatType::LOGICAL;
1973 switch ( GetRawStackType() )
1974 {
1975 case svEmptyCell:
1976 {
1977 FormulaConstTokenRef p = PopToken();
1978 if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited())
1979 nRes = 1;
1980 }
1981 break;
1982 case svDoubleRef :
1983 case svSingleRef :
1984 {
1985 ScAddress aAdr;
1986 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1987 break;
1988 // NOTE: this differs from COUNTBLANK() ScCountEmptyCells() that
1989 // may treat ="" in the referenced cell as blank for Excel
1990 // interoperability.
1991 ScRefCellValue aCell(mrDoc, aAdr);
1992 if (aCell.meType == CELLTYPE_NONE)
1993 nRes = 1;
1994 }
1995 break;
1996 case svExternalSingleRef:
1997 case svExternalDoubleRef:
1998 case svMatrix:
1999 {
2000 ScMatrixRef pMat = GetMatrix();
2001 if ( !pMat )
2002 ; // nothing
2003 else if ( !pJumpMatrix )
2004 nRes = pMat->IsEmptyCell( 0, 0) ? 1 : 0;
2005 else
2006 {
2007 SCSIZE nCols, nRows, nC, nR;
2008 pMat->GetDimensions( nCols, nRows);
2009 pJumpMatrix->GetPos( nC, nR);
2010 if ( nC < nCols && nR < nRows )
2011 nRes = pMat->IsEmptyCell( nC, nR) ? 1 : 0;
2012 // else: false, not empty (which is what Xcl does)
2013 }
2014 }
2015 break;
2016 default:
2017 Pop();
2018 }
2019 nGlobalError = FormulaError::NONE;
2020 PushInt( nRes );
2021}
2022
2023bool ScInterpreter::IsString()
2024{
2025 nFuncFmtType = SvNumFormatType::LOGICAL;
2026 bool bRes = false;
2027 switch ( GetRawStackType() )
2028 {
2029 case svString:
2030 Pop();
2031 bRes = true;
2032 break;
2033 case svDoubleRef :
2034 case svSingleRef :
2035 {
2036 ScAddress aAdr;
2037 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2038 break;
2039
2040 ScRefCellValue aCell(mrDoc, aAdr);
2041 if (GetCellErrCode(aCell) == FormulaError::NONE)
2042 {
2043 switch (aCell.meType)
2044 {
2045 case CELLTYPE_STRING :
2046 case CELLTYPE_EDIT :
2047 bRes = true;
2048 break;
2049 case CELLTYPE_FORMULA :
2050 bRes = (!aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty());
2051 break;
2052 default:
2053 ; // nothing
2054 }
2055 }
2056 }
2057 break;
2058 case svExternalSingleRef:
2059 {
2060 ScExternalRefCache::TokenRef pToken;
2061 PopExternalSingleRef(pToken);
2062 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svString)
2063 bRes = true;
2064 }
2065 break;
2066 case svExternalDoubleRef:
2067 case svMatrix:
2068 {
2069 ScMatrixRef pMat = GetMatrix();
2070 if ( !pMat )
2071 ; // nothing
2072 else if ( !pJumpMatrix )
2073 bRes = pMat->IsStringOrEmpty(0, 0) && !pMat->IsEmpty(0, 0);
2074 else
2075 {
2076 SCSIZE nCols, nRows, nC, nR;
2077 pMat->GetDimensions( nCols, nRows);
2078 pJumpMatrix->GetPos( nC, nR);
2079 if ( nC < nCols && nR < nRows )
2080 bRes = pMat->IsStringOrEmpty( nC, nR) && !pMat->IsEmpty( nC, nR);
2081 }
2082 }
2083 break;
2084 default:
2085 Pop();
2086 }
2087 nGlobalError = FormulaError::NONE;
2088 return bRes;
2089}
2090
2091void ScInterpreter::ScIsString()
2092{
2093 PushInt( int(IsString()) );
2094}
2095
2096void ScInterpreter::ScIsNonString()
2097{
2098 PushInt( int(!IsString()) );
2099}
2100
2101void ScInterpreter::ScIsLogical()
2102{
2103 bool bRes = false;
2104 switch ( GetStackType() )
2105 {
2106 case svDoubleRef :
2107 case svSingleRef :
2108 {
2109 ScAddress aAdr;
2110 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2111 break;
2112
2113 ScRefCellValue aCell(mrDoc, aAdr);
2114 if (GetCellErrCode(aCell) == FormulaError::NONE)
2115 {
2116 if (aCell.hasNumeric())
2117 {
2118 sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
2119 bRes = (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL);
2120 }
2121 }
2122 }
2123 break;
2124 case svMatrix:
2125 {
2126 double fVal;
2127 svl::SharedString aStr;
2128 ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
2129 bRes = (nMatValType == ScMatValType::Boolean);
2130 }
2131 break;
2132 default:
2133 PopError();
2134 if ( nGlobalError == FormulaError::NONE )
2135 bRes = ( nCurFmtType == SvNumFormatType::LOGICAL );
2136 }
2137 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
2138 nGlobalError = FormulaError::NONE;
2139 PushInt( int(bRes) );
2140}
2141
2142void ScInterpreter::ScType()
2143{
2144 short nType = 0;
2145 switch ( GetStackType() )
2146 {
2147 case svDoubleRef :
2148 case svSingleRef :
2149 {
2150 ScAddress aAdr;
2151 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2152 break;
2153
2154 ScRefCellValue aCell(mrDoc, aAdr);
2155 if (GetCellErrCode(aCell) == FormulaError::NONE)
2156 {
2157 switch (aCell.meType)
2158 {
2159 // NOTE: this is Xcl nonsense!
2160 case CELLTYPE_STRING :
2161 case CELLTYPE_EDIT :
2162 nType = 2;
2163 break;
2164 case CELLTYPE_VALUE :
2165 {
2166 sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
2167 if (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL)
2168 nType = 4;
2169 else
2170 nType = 1;
2171 }
2172 break;
2173 case CELLTYPE_NONE:
2174 // always 1, s. tdf#73078
2175 nType = 1;
2176 break;
2177 case CELLTYPE_FORMULA :
2178 nType = 8;
2179 break;
2180 default:
2181 PushIllegalArgument();
2182 }
2183 }
2184 else
2185 nType = 16;
2186 }
2187 break;
2188 case svString:
2189 PopError();
2190 if ( nGlobalError != FormulaError::NONE )
2191 {
2192 nType = 16;
2193 nGlobalError = FormulaError::NONE;
2194 }
2195 else
2196 nType = 2;
2197 break;
2198 case svMatrix:
2199 PopMatrix();
2200 if ( nGlobalError != FormulaError::NONE )
2201 {
2202 nType = 16;
2203 nGlobalError = FormulaError::NONE;
2204 }
2205 else
2206 nType = 64;
2207 // we could return the type of one element if in JumpMatrix or
2208 // ForceArray mode, but Xcl doesn't ...
2209 break;
2210 default:
2211 PopError();
2212 if ( nGlobalError != FormulaError::NONE )
2213 {
2214 nType = 16;
2215 nGlobalError = FormulaError::NONE;
2216 }
2217 else
2218 nType = 1;
2219 }
2220 PushInt( nType );
2221}
2222
2223static bool lcl_FormatHasNegColor( const SvNumberformat* pFormat )
2224{
2225 return pFormat && pFormat->GetColor( 1 );
2226}
2227
2228static bool lcl_FormatHasOpenPar( const SvNumberformat* pFormat )
2229{
2230 return pFormat && (pFormat->GetFormatstring().indexOf('(') != -1);
2231}
2232
2233namespace {
2234
2235void getFormatString(const SvNumberFormatter* pFormatter, sal_uLong nFormat, OUString& rFmtStr)
2236{
2237 rFmtStr = pFormatter->GetCalcCellReturn( nFormat);
2238}
2239
2240}
2241
2242void ScInterpreter::ScCell()
2243{ // ATTRIBUTE ; [REF]
2244 sal_uInt8 nParamCount = GetByte();
2245 if( !MustHaveParamCount( nParamCount, 1, 2 ) )
2246 return;
2247
2248 ScAddress aCellPos( aPos );
2249 bool bError = false;
2250 if( nParamCount == 2 )
2251 {
2252 switch (GetStackType())
2253 {
2254 case svExternalSingleRef:
2255 case svExternalDoubleRef:
2256 {
2257 // Let's handle external reference separately...
2258 ScCellExternal();
2259 return;
2260 }
2261 default:
2262 ;
2263 }
2264 bError = !PopDoubleRefOrSingleRef( aCellPos );
2265 }
2266 OUString aInfoType = GetString().getString();
2267 if( bError || nGlobalError != FormulaError::NONE )
2268 PushIllegalParameter();
2269 else
2270 {
2271 ScRefCellValue aCell(mrDoc, aCellPos);
2272
2273 ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell);
2274
2275// *** ADDRESS INFO ***
2276 if( aInfoType == "COL" )
2277 { // column number (1-based)
2278 PushInt( aCellPos.Col() + 1 );
2279 }
2280 else if( aInfoType == "ROW" )
2281 { // row number (1-based)
2282 PushInt( aCellPos.Row() + 1 );
2283 }
2284 else if( aInfoType == "SHEET" )
2285 { // table number (1-based)
2286 PushInt( aCellPos.Tab() + 1 );
2287 }
2288 else if( aInfoType == "ADDRESS" )
2289 { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW
2290 ScRefFlags nFlags = (aCellPos.Tab() == aPos.Tab()) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D;
2291 OUString aStr(aCellPos.Format(nFlags, &mrDoc, mrDoc.GetAddressConvention()));
2292 PushString(aStr);
2293 }
2294 else if( aInfoType == "FILENAME" )
2295 { // file name and table name: 'FILENAME'#$TABLE
2296 SCTAB nTab = aCellPos.Tab();
2297 OUString aFuncResult;
2298 if( nTab < mrDoc.GetTableCount() )
2299 {
2300 if( mrDoc.GetLinkMode( nTab ) == ScLinkMode::VALUE )
2301 mrDoc.GetName( nTab, aFuncResult );
2302 else
2303 {
2304 SfxObjectShell* pShell = mrDoc.GetDocumentShell();
2305 if( pShell && pShell->GetMedium() )
2306 {
2307 OUStringBuffer aBuf;
2308 aBuf.append('\'');
2309 const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject();
2310 aBuf.append(rURLObj.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous));
2311 aBuf.append("'#$");
2312 OUString aTabName;
2313 mrDoc.GetName( nTab, aTabName );
2314 aBuf.append(aTabName);
2315 aFuncResult = aBuf.makeStringAndClear();
2316 }
2317 }
2318 }
2319 PushString( aFuncResult );
2320 }
2321 else if( aInfoType == "COORD" )
2322 { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW
2323 // Yes, passing tab as col is intentional!
2324 OUString aCellStr1 =
2325 ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format(
2326 (ScRefFlags::COL_ABS|ScRefFlags::COL_VALID), nullptr, mrDoc.GetAddressConvention() );
2327 OUString aCellStr2 =
2328 aCellPos.Format((ScRefFlags::COL_ABS|ScRefFlags::COL_VALID|ScRefFlags::ROW_ABS|ScRefFlags::ROW_VALID),
2329 nullptr, mrDoc.GetAddressConvention());
2330 OUString aFuncResult = aCellStr1 + ":" + aCellStr2;
2331 PushString( aFuncResult );
2332 }
2333
2334// *** CELL PROPERTIES ***
2335 else if( aInfoType == "CONTENTS" )
2336 { // contents of the cell, no formatting
2337 if (aCell.hasString())
2338 {
2339 svl::SharedString aStr;
2340 GetCellString(aStr, aCell);
2341 PushString( aStr );
2342 }
2343 else
2344 PushDouble(GetCellValue(aCellPos, aCell));
2345 }
2346 else if( aInfoType == "TYPE" )
2347 { // b = blank; l = string (label); v = otherwise (value)
2348 sal_Unicode c;
2349 if (aCell.hasString())
2350 c = 'l';
2351 else
2352 c = aCell.hasNumeric() ? 'v' : 'b';
2353 PushString( OUString(c) );
2354 }
2355 else if( aInfoType == "WIDTH" )
2356 { // column width (rounded off as count of zero characters in standard font and size)
2357 Printer* pPrinter = mrDoc.GetPrinter();
2358 MapMode aOldMode( pPrinter->GetMapMode() );
2359 vcl::Font aOldFont( pPrinter->GetFont() );
2360 vcl::Font aDefFont;
2361
2362 pPrinter->SetMapMode(MapMode(MapUnit::MapTwip));
2363 // font color doesn't matter here
2364 mrDoc.GetDefPattern()->GetFont( aDefFont, SC_AUTOCOL_BLACK, pPrinter );
2365 pPrinter->SetFont( aDefFont );
2366 long nZeroWidth = pPrinter->GetTextWidth( OUString( '0' ) );
2367 pPrinter->SetFont( aOldFont );
2368 pPrinter->SetMapMode( aOldMode );
2369 int nZeroCount = static_cast<int>(mrDoc.GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth);
2370 PushInt( nZeroCount );
2371 }
2372 else if( aInfoType == "PREFIX" )
2373 { // ' = left; " = right; ^ = centered
2374 sal_Unicode c = 0;
2375 if (aCell.hasString())
2376 {
2377 const SvxHorJustifyItem* pJustAttr = mrDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY );
2378 switch( pJustAttr->GetValue() )
2379 {
2380 case SvxCellHorJustify::Standard:
2381 case SvxCellHorJustify::Left:
2382 case SvxCellHorJustify::Block: c = '\''; break;
2383 case SvxCellHorJustify::Center: c = '^'; break;
2384 case SvxCellHorJustify::Right: c = '"'; break;
2385 case SvxCellHorJustify::Repeat: c = '\\'; break;
2386 }
2387 }
2388 PushString( OUString(c) );
2389 }
2390 else if( aInfoType == "PROTECT" )
2391 { // 1 = cell locked
2392 const ScProtectionAttr* pProtAttr = mrDoc.GetAttr( aCellPos, ATTR_PROTECTION );
2393 PushInt( pProtAttr->GetProtection() ? 1 : 0 );
2394 }
2395
2396// *** FORMATTING ***
2397 else if( aInfoType == "FORMAT" )
2398 { // specific format code for standard formats
2399 OUString aFuncResult;
2400 sal_uInt32 nFormat = mrDoc.GetNumberFormat( aCellPos );
2401 getFormatString(pFormatter, nFormat, aFuncResult);
2402 PushString( aFuncResult );
2403 }
2404 else if( aInfoType == "COLOR" )
2405 { // 1 = negative values are colored, otherwise 0
2406 const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
2407 PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 );
2408 }
2409 else if( aInfoType == "PARENTHESES" )
2410 { // 1 = format string contains a '(' character, otherwise 0
2411 const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
2412 PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 );
2413 }
2414 else
2415 PushIllegalArgument();
2416 }
2417}
2418
2419void ScInterpreter::ScCellExternal()
2420{
2421 sal_uInt16 nFileId;
2422 OUString aTabName;
2423 ScSingleRefData aRef;
2424 ScExternalRefCache::TokenRef pToken;
2425 ScExternalRefCache::CellFormat aFmt;
2426 PopExternalSingleRef(nFileId, aTabName, aRef, pToken, &aFmt);
2427 if (nGlobalError != FormulaError::NONE)
2428 {
2429 PushError( nGlobalError);
2430 return;
2431 }
2432
2433 OUString aInfoType = GetString().getString();
2434 if (nGlobalError != FormulaError::NONE)
2435 {
2436 PushError( nGlobalError);
2437 return;
2438 }
2439
2440 SCCOL nCol;
2441 SCROW nRow;
2442 SCTAB nTab;
2443 aRef.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like.
2444 SingleRefToVars(aRef, nCol, nRow, nTab);
2445 if (nGlobalError != FormulaError::NONE)
2446 {
2447 PushIllegalParameter();
2448 return;
2449 }
2450 aRef.SetAbsTab(-1); // revert the value.
2451
2452 ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell);
2453 ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
2454
2455 if ( aInfoType == "COL" )
2456 PushInt(nCol + 1);
2457 else if ( aInfoType == "ROW" )
2458 PushInt(nRow + 1);
2459 else if ( aInfoType == "SHEET" )
2460 {
2461 // For SHEET, No idea what number we should set, but let's always set
2462 // 1 if the external sheet exists, no matter what sheet. Excel does
2463 // the same.
2464 if (pRefMgr->getCacheTable(nFileId, aTabName, false))
2465 PushInt(1);
2466 else
2467 SetError(FormulaError::NoName);
2468 }
2469 else if ( aInfoType == "ADDRESS" )
2470 {
2471 // ODF 1.2 says we need to always display address using the ODF A1 grammar.
2472 ScTokenArray aArray(mrDoc);
2473 aArray.AddExternalSingleReference(nFileId, svl::SharedString( aTabName), aRef); // string not interned
2474 ScCompiler aComp(mrDoc, aPos, aArray, formula::FormulaGrammar::GRAM_ODFF_A1);
2475 OUString aStr;
2476 aComp.CreateStringFromTokenArray(aStr);
2477 PushString(aStr);
2478 }
2479 else if ( aInfoType == "FILENAME" )
2480 {
2481 // 'file URI'#$SheetName
2482
2483 const OUString* p = pRefMgr->getExternalFileName(nFileId);
2484 if (!p)
2485 {
2486 // In theory this should never happen...
2487 SetError(FormulaError::NoName);
2488 return;
2489 }
2490
2491 OUString aBuf = "'" + *p + "'#$" + aTabName;
2492 PushString(aBuf);
2493 }
2494 else if ( aInfoType == "CONTENTS" )
2495 {
2496 switch (pToken->GetType())
2497 {
2498 case svString:
2499 PushString(pToken->GetString());
2500 break;
2501 case svDouble:
2502 PushString(OUString::number(pToken->GetDouble()));
2503 break;
2504 case svError:
2505 PushString(ScGlobal::GetErrorString(pToken->GetError()));
2506 break;
2507 default:
2508 PushString(ScGlobal::GetEmptyOUString());
2509 }
2510 }
2511 else if ( aInfoType == "TYPE" )
2512 {
2513 sal_Unicode c = 'v';
2514 switch (pToken->GetType())
2515 {
2516 case svString:
2517 c = 'l';
2518 break;
2519 case svEmptyCell:
2520 c = 'b';
2521 break;
2522 default:
2523 ;
2524 }
2525 PushString(OUString(c));
2526 }
2527 else if ( aInfoType == "FORMAT" )
2528 {
2529 OUString aFmtStr;
2530 sal_uLong nFmt = aFmt.mbIsSet ? aFmt.mnIndex : 0;
2531 getFormatString(pFormatter, nFmt, aFmtStr);
2532 PushString(aFmtStr);
2533 }
2534 else if ( aInfoType == "COLOR" )
2535 {
2536 // 1 = negative values are colored, otherwise 0
2537 int nVal = 0;
2538 if (aFmt.mbIsSet)
2539 {
2540 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2541 nVal = lcl_FormatHasNegColor(pFormat) ? 1 : 0;
2542 }
2543 PushInt(nVal);
2544 }
2545 else if ( aInfoType == "PARENTHESES" )
2546 {
2547 // 1 = format string contains a '(' character, otherwise 0
2548 int nVal = 0;
2549 if (aFmt.mbIsSet)
2550 {
2551 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2552 nVal = lcl_FormatHasOpenPar(pFormat) ? 1 : 0;
2553 }
2554 PushInt(nVal);
2555 }
2556 else
2557 PushIllegalParameter();
2558}
2559
2560void ScInterpreter::ScIsRef()
2561{
2562 nFuncFmtType = SvNumFormatType::LOGICAL;
2563 bool bRes = false;
2564 switch ( GetStackType() )
2565 {
2566 case svSingleRef :
2567 {
2568 ScAddress aAdr;
2569 PopSingleRef( aAdr );
2570 if ( nGlobalError == FormulaError::NONE )
2571 bRes = true;
2572 }
2573 break;
2574 case svDoubleRef :
2575 {
2576 ScRange aRange;
2577 PopDoubleRef( aRange );
2578 if ( nGlobalError == FormulaError::NONE )
2579 bRes = true;
2580 }
2581 break;
2582 case svRefList :
2583 {
2584 FormulaConstTokenRef x = PopToken();
2585 if ( nGlobalError == FormulaError::NONE )
2586 bRes = !x->GetRefList()->empty();
2587 }
2588 break;
2589 case svExternalSingleRef:
2590 {
2591 ScExternalRefCache::TokenRef pToken;
2592 PopExternalSingleRef(pToken);
2593 if (nGlobalError == FormulaError::NONE)
2594 bRes = true;
2595 }
2596 break;
2597 case svExternalDoubleRef:
2598 {
2599 ScExternalRefCache::TokenArrayRef pArray;
2600 PopExternalDoubleRef(pArray);
2601 if (nGlobalError == FormulaError::NONE)
2602 bRes = true;
2603 }
2604 break;
2605 default:
2606 Pop();
2607 }
2608 nGlobalError = FormulaError::NONE;
2609 PushInt( int(bRes) );
2610}
2611
2612void ScInterpreter::ScIsValue()
2613{
2614 nFuncFmtType = SvNumFormatType::LOGICAL;
2615 bool bRes = false;
2616 switch ( GetRawStackType() )
2617 {
2618 case svDouble:
2619 Pop();
2620 bRes = true;
2621 break;
2622 case svDoubleRef :
2623 case svSingleRef :
2624 {
2625 ScAddress aAdr;
2626 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2627 break;
2628
2629 ScRefCellValue aCell(mrDoc, aAdr);
2630 if (GetCellErrCode(aCell) == FormulaError::NONE)
2631 {
2632 switch (aCell.meType)
2633 {
2634 case CELLTYPE_VALUE :
2635 bRes = true;
2636 break;
2637 case CELLTYPE_FORMULA :
2638 bRes = (aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty());
2639 break;
2640 default:
2641 ; // nothing
2642 }
2643 }
2644 }
2645 break;
2646 case svExternalSingleRef:
2647 {
2648 ScExternalRefCache::TokenRef pToken;
2649 PopExternalSingleRef(pToken);
2650 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
2651 bRes = true;
2652 }
2653 break;
2654 case svExternalDoubleRef:
2655 case svMatrix:
2656 {
2657 ScMatrixRef pMat = GetMatrix();
2658 if ( !pMat )
2659 ; // nothing
2660 else if ( !pJumpMatrix )
2661 {
2662 if (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NONE)
2663 bRes = pMat->IsValue( 0, 0);
2664 }
2665 else
2666 {
2667 SCSIZE nCols, nRows, nC, nR;
2668 pMat->GetDimensions( nCols, nRows);
2669 pJumpMatrix->GetPos( nC, nR);
2670 if ( nC < nCols && nR < nRows )
2671 if (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NONE)
2672 bRes = pMat->IsValue( nC, nR);
2673 }
2674 }
2675 break;
2676 default:
2677 Pop();
2678 }
2679 nGlobalError = FormulaError::NONE;
2680 PushInt( int(bRes) );
2681}
2682
2683void ScInterpreter::ScIsFormula()
2684{
2685 nFuncFmtType = SvNumFormatType::LOGICAL;
2686 bool bRes = false;
2687 switch ( GetStackType() )
2688 {
2689 case svDoubleRef :
2690 if (IsInArrayContext())
2691 {
2692 SCCOL nCol1, nCol2;
2693 SCROW nRow1, nRow2;
2694 SCTAB nTab1, nTab2;
2695 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2696 if (nGlobalError != FormulaError::NONE)
2697 {
2698 PushError( nGlobalError);
2699 return;
2700 }
2701 if (nTab1 != nTab2)
2702 {
2703 PushIllegalArgument();
2704 return;
2705 }
2706
2707 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCol2 - nCol1 + 1),
2708 static_cast<SCSIZE>(nRow2 - nRow1 + 1), true);
2709 if (!pResMat)
2710 {
2711 PushError( FormulaError::MatrixSize);
2712 return;
2713 }
2714
2715 /* TODO: we really should have a gap-aware cell iterator. */
2716 SCSIZE i=0, j=0;
2717 ScAddress aAdr( 0, 0, nTab1);
2718 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2719 {
2720 aAdr.SetCol(nCol);
2721 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2722 {
2723 aAdr.SetRow(nRow);
2724 ScRefCellValue aCell(mrDoc, aAdr);
2725 pResMat->PutBoolean( (aCell.meType == CELLTYPE_FORMULA), i,j);
2726 ++j;
2727 }
2728 ++i;
2729 j = 0;
2730 }
2731
2732 PushMatrix( pResMat);
2733 return;
2734 }
2735 [[fallthrough]];
2736 case svSingleRef :
2737 {
2738 ScAddress aAdr;
2739 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2740 break;
2741
2742 bRes = (mrDoc.GetCellType(aAdr) == CELLTYPE_FORMULA);
2743 }
2744 break;
2745 default:
2746 Pop();
2747 }
2748 nGlobalError = FormulaError::NONE;
2749 PushInt( int(bRes) );
2750}
2751
2752void ScInterpreter::ScFormula()
2753{
2754 OUString aFormula;
2755 switch ( GetStackType() )
2756 {
2757 case svDoubleRef :
2758 if (IsInArrayContext())
2759 {
2760 SCCOL nCol1, nCol2;
2761 SCROW nRow1, nRow2;
2762 SCTAB nTab1, nTab2;
2763 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2764 if (nGlobalError != FormulaError::NONE)
2765 break;
2766
2767 if (nTab1 != nTab2)
2768 {
2769 SetError( FormulaError::IllegalArgument);
2770 break;
2771 }
2772
2773 ScMatrixRef pResMat = GetNewMat( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1, true);
2774 if (!pResMat)
2775 break;
2776
2777 /* TODO: use a column iterator instead? */
2778 SCSIZE i=0, j=0;
2779 ScAddress aAdr(0,0,nTab1);
2780 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2781 {
2782 aAdr.SetCol(nCol);
2783 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2784 {
2785 aAdr.SetRow(nRow);
2786 ScRefCellValue aCell(mrDoc, aAdr);
2787 switch (aCell.meType)
2788 {
2789 case CELLTYPE_FORMULA :
2790 aCell.mpFormula->GetFormula(aFormula, formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext);
2791 pResMat->PutString( mrStrPool.intern( aFormula), i,j);
2792 break;
2793 default:
2794 pResMat->PutError( FormulaError::NotAvailable, i,j);
2795 }
2796 ++j;
2797 }
2798 ++i;
2799 j = 0;
2800 }
2801
2802 PushMatrix( pResMat);
2803 return;
2804 }
2805 [[fallthrough]];
2806 case svSingleRef :
2807 {
2808 ScAddress aAdr;
2809 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2810 break;
2811
2812 ScRefCellValue aCell(mrDoc, aAdr);
2813 switch (aCell.meType)
2814 {
2815 case CELLTYPE_FORMULA :
2816 aCell.mpFormula->GetFormula(aFormula, formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext);
2817 break;
2818 default:
2819 SetError( FormulaError::NotAvailable );
2820 }
2821 }
2822 break;
2823 default:
2824 Pop();
2825 SetError( FormulaError::NotAvailable );
2826 }
2827 PushString( aFormula );
2828}
2829
2830void ScInterpreter::ScIsNV()
2831{
2832 nFuncFmtType = SvNumFormatType::LOGICAL;
2833 bool bRes = false;
2834 switch ( GetStackType() )
2835 {
2836 case svDoubleRef :
2837 case svSingleRef :
2838 {
2839 ScAddress aAdr;
2840 bool bOk = PopDoubleRefOrSingleRef( aAdr );
2841 if ( nGlobalError == FormulaError::NotAvailable )
2842 bRes = true;
2843 else if (bOk)
2844 {
2845 ScRefCellValue aCell(mrDoc, aAdr);
2846 FormulaError nErr = GetCellErrCode(aCell);
2847 bRes = (nErr == FormulaError::NotAvailable);
2848 }
2849 }
2850 break;
2851 case svExternalSingleRef:
2852 {
2853 ScExternalRefCache::TokenRef pToken;
2854 PopExternalSingleRef(pToken);
2855 if (nGlobalError == FormulaError::NotAvailable ||
2856 (pToken && pToken->GetType() == svError && pToken->GetError() == FormulaError::NotAvailable))
2857 bRes = true;
2858 }
2859 break;
2860 case svExternalDoubleRef:
2861 case svMatrix:
2862 {
2863 ScMatrixRef pMat = GetMatrix();
2864 if ( !pMat )
2865 ; // nothing
2866 else if ( !pJumpMatrix )
2867 bRes = (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NotAvailable);
2868 else
2869 {
2870 SCSIZE nCols, nRows, nC, nR;
2871 pMat->GetDimensions( nCols, nRows);
2872 pJumpMatrix->GetPos( nC, nR);
2873 if ( nC < nCols && nR < nRows )
2874 bRes = (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NotAvailable);
2875 }
2876 }
2877 break;
2878 default:
2879 PopError();
2880 if ( nGlobalError == FormulaError::NotAvailable )
2881 bRes = true;
2882 }
2883 nGlobalError = FormulaError::NONE;
2884 PushInt( int(bRes) );
2885}
2886
2887void ScInterpreter::ScIsErr()
2888{
2889 nFuncFmtType = SvNumFormatType::LOGICAL;
2890 bool bRes = false;
2891 switch ( GetStackType() )
2892 {
2893 case svDoubleRef :
2894 case svSingleRef :
2895 {
2896 ScAddress aAdr;
2897 bool bOk = PopDoubleRefOrSingleRef( aAdr );
2898 if ( !bOk || (nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) )
2899 bRes = true;
2900 else
2901 {
2902 ScRefCellValue aCell(mrDoc, aAdr);
2903 FormulaError nErr = GetCellErrCode(aCell);
2904 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2905 }
2906 }
2907 break;
2908 case svExternalSingleRef:
2909 {
2910 ScExternalRefCache::TokenRef pToken;
2911 PopExternalSingleRef(pToken);
2912 if ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pToken ||
2913 (pToken->GetType() == svError && pToken->GetError() != FormulaError::NotAvailable))
2914 bRes = true;
2915 }
2916 break;
2917 case svExternalDoubleRef:
2918 case svMatrix:
2919 {
2920 ScMatrixRef pMat = GetMatrix();
2921 if ( nGlobalError != FormulaError::NONE || !pMat )
2922 bRes = ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pMat);
2923 else if ( !pJumpMatrix )
2924 {
2925 FormulaError nErr = pMat->GetErrorIfNotString( 0, 0);
2926 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2927 }
2928 else
2929 {
2930 SCSIZE nCols, nRows, nC, nR;
2931 pMat->GetDimensions( nCols, nRows);
2932 pJumpMatrix->GetPos( nC, nR);
2933 if ( nC < nCols && nR < nRows )
2934 {
2935 FormulaError nErr = pMat->GetErrorIfNotString( nC, nR);
2936 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2937 }
2938 }
2939 }
2940 break;
2941 default:
2942 PopError();
2943 if ( nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable )
2944 bRes = true;
2945 }
2946 nGlobalError = FormulaError::NONE;
2947 PushInt( int(bRes) );
2948}
2949
2950void ScInterpreter::ScIsError()
2951{
2952 nFuncFmtType = SvNumFormatType::LOGICAL;
2953 bool bRes = false;
2954 switch ( GetStackType() )
2955 {
2956 case svDoubleRef :
2957 case svSingleRef :
2958 {
2959 ScAddress aAdr;
2960 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2961 {
2962 bRes = true;
2963 break;
2964 }
2965 if ( nGlobalError != FormulaError::NONE )
2966 bRes = true;
2967 else
2968 {
2969 ScRefCellValue aCell(mrDoc, aAdr);
2970 bRes = (GetCellErrCode(aCell) != FormulaError::NONE);
2971 }
2972 }
2973 break;
2974 case svExternalSingleRef:
2975 {
2976 ScExternalRefCache::TokenRef pToken;
2977 PopExternalSingleRef(pToken);
2978 if (nGlobalError != FormulaError::NONE || pToken->GetType() == svError)
2979 bRes = true;
2980 }
2981 break;
2982 case svExternalDoubleRef:
2983 case svMatrix:
2984 {
2985 ScMatrixRef pMat = GetMatrix();
2986 if ( nGlobalError != FormulaError::NONE || !pMat )
2987 bRes = true;
2988 else if ( !pJumpMatrix )
2989 bRes = (pMat->GetErrorIfNotString( 0, 0) != FormulaError::NONE);
2990 else
2991 {
2992 SCSIZE nCols, nRows, nC, nR;
2993 pMat->GetDimensions( nCols, nRows);
2994 pJumpMatrix->GetPos( nC, nR);
2995 if ( nC < nCols && nR < nRows )
2996 bRes = (pMat->GetErrorIfNotString( nC, nR) != FormulaError::NONE);
2997 }
2998 }
2999 break;
3000 default:
3001 PopError();
3002 if ( nGlobalError != FormulaError::NONE )
3003 bRes = true;
3004 }
3005 nGlobalError = FormulaError::NONE;
3006 PushInt( int(bRes) );
3007}
3008
3009bool ScInterpreter::IsEven()
3010{
3011 nFuncFmtType = SvNumFormatType::LOGICAL;
3012 bool bRes = false;
3013 double fVal = 0.0;
3014 switch ( GetStackType() )
3015 {
3016 case svDoubleRef :
3017 case svSingleRef :
3018 {
3019 ScAddress aAdr;
3020 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3021 break;
3022
3023 ScRefCellValue aCell(mrDoc, aAdr);
3024 FormulaError nErr = GetCellErrCode(aCell);
3025 if (nErr != FormulaError::NONE)
3026 SetError(nErr);
3027 else
3028 {
3029 switch (aCell.meType)
3030 {
3031 case CELLTYPE_VALUE :
3032 fVal = GetCellValue(aAdr, aCell);
3033 bRes = true;
3034 break;
3035 case CELLTYPE_FORMULA :
3036 if (aCell.mpFormula->IsValue())
3037 {
3038 fVal = GetCellValue(aAdr, aCell);
3039 bRes = true;
3040 }
3041 break;
3042 default:
3043 ; // nothing
3044 }
3045 }
3046 }
3047 break;
3048 case svDouble:
3049 {
3050 fVal = PopDouble();
3051 bRes = true;
3052 }
3053 break;
3054 case svExternalSingleRef:
3055 {
3056 ScExternalRefCache::TokenRef pToken;
3057 PopExternalSingleRef(pToken);
3058 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
3059 {
3060 fVal = pToken->GetDouble();
3061 bRes = true;
3062 }
3063 }
3064 break;
3065 case svExternalDoubleRef:
3066 case svMatrix:
3067 {
3068 ScMatrixRef pMat = GetMatrix();
3069 if ( !pMat )
3070 ; // nothing
3071 else if ( !pJumpMatrix )
3072 {
3073 bRes = pMat->IsValue( 0, 0);
3074 if ( bRes )
3075 fVal = pMat->GetDouble( 0, 0);
3076 }
3077 else
3078 {
3079 SCSIZE nCols, nRows, nC, nR;
3080 pMat->GetDimensions( nCols, nRows);
3081 pJumpMatrix->GetPos( nC, nR);
3082 if ( nC < nCols && nR < nRows )
3083 {
3084 bRes = pMat->IsValue( nC, nR);
3085 if ( bRes )
3086 fVal = pMat->GetDouble( nC, nR);
3087 }
3088 else
3089 SetError( FormulaError::NoValue);
3090 }
3091 }
3092 break;
3093 default:
3094 ; // nothing
3095 }
3096 if ( !bRes )
3097 SetError( FormulaError::IllegalParameter);
3098 else
3099 bRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 );
3100 return bRes;
3101}
3102
3103void ScInterpreter::ScIsEven()
3104{
3105 PushInt( int(IsEven()) );
3106}
3107
3108void ScInterpreter::ScIsOdd()
3109{
3110 PushInt( int(!IsEven()) );
3111}
3112
3113void ScInterpreter::ScN()
3114{
3115 FormulaError nErr = nGlobalError;
3116 nGlobalError = FormulaError::NONE;
3117 // Temporarily override the ConvertStringToValue() error for
3118 // GetCellValue() / GetCellValueOrZero()
3119 FormulaError nSErr = mnStringNoValueError;
3120 mnStringNoValueError = FormulaError::CellNoValue;
3121 double fVal = GetDouble();
3122 mnStringNoValueError = nSErr;
3123 if (nErr != FormulaError::NONE)
3124 nGlobalError = nErr; // preserve previous error if any
3125 else if (nGlobalError == FormulaError::CellNoValue)
3126 nGlobalError = FormulaError::NONE; // reset temporary detection error
3127 PushDouble(fVal);
3128}
3129
3130void ScInterpreter::ScTrim()
3131{
3132 // Doesn't only trim but also removes duplicated blanks within!
3133 OUString aVal = comphelper::string::strip(GetString().getString(), ' ');
3134 OUStringBuffer aStr;
3135 const sal_Unicode* p = aVal.getStr();
3136 const sal_Unicode* const pEnd = p + aVal.getLength();
3137 while ( p < pEnd )
3138 {
3139 if ( *p != ' ' || p[-1] != ' ' ) // first can't be ' ', so -1 is fine
3140 aStr.append(*p);
3141 p++;
3142 }
3143 PushString(aStr.makeStringAndClear());
3144}
3145
3146void ScInterpreter::ScUpper()
3147{
3148 OUString aString = ScGlobal::getCharClassPtr()->uppercase(GetString().getString());
3149 PushString(aString);
3150}
3151
3152void ScInterpreter::ScProper()
3153{
3154//2do: what to do with I18N-CJK ?!?
3155 OUStringBuffer aStr(GetString().getString());
3156 const sal_Int32 nLen = aStr.getLength();
3157 if ( nLen > 0 )
3158 {
3159 OUString aUpr(ScGlobal::getCharClassPtr()->uppercase(aStr.toString()));
3160 OUString aLwr(ScGlobal::getCharClassPtr()->lowercase(aStr.toString()));
3161 aStr[0] = aUpr[0];
3162 sal_Int32 nPos = 1;
3163 while( nPos < nLen )
3164 {
3165 OUString aTmpStr( aStr[nPos-1] );
3166 if ( !ScGlobal::getCharClassPtr()->isLetter( aTmpStr, 0 ) )
3167 aStr[nPos] = aUpr[nPos];
3168 else
3169 aStr[nPos] = aLwr[nPos];
3170 ++nPos;
3171 }
3172 }
3173 PushString(aStr.makeStringAndClear());
3174}
3175
3176void ScInterpreter::ScLower()
3177{
3178 OUString aString = ScGlobal::getCharClassPtr()->lowercase(GetString().getString());
3179 PushString(aString);
3180}
3181
3182void ScInterpreter::ScLen()
3183{
3184 OUString aStr = GetString().getString();
3185 sal_Int32 nIdx = 0;
3186 sal_Int32 nCnt = 0;
3187 while ( nIdx < aStr.getLength() )
3188 {
3189 aStr.iterateCodePoints( &nIdx );
3190 ++nCnt;
3191 }
3192 PushDouble( nCnt );
3193}
3194
3195void ScInterpreter::ScT()
3196{
3197 switch ( GetStackType() )
3198 {
3199 case svDoubleRef :
3200 case svSingleRef :
3201 {
3202 ScAddress aAdr;
3203 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3204 {
3205 PushInt(0);
3206 return ;
3207 }
3208 bool bValue = false;
3209 ScRefCellValue aCell(mrDoc, aAdr);
3210 if (GetCellErrCode(aCell) == FormulaError::NONE)
3211 {
3212 switch (aCell.meType)
3213 {
3214 case CELLTYPE_VALUE :
3215 bValue = true;
3216 break;
3217 case CELLTYPE_FORMULA :
3218 bValue = aCell.mpFormula->IsValue();
3219 break;
3220 default:
3221 ; // nothing
3222 }
3223 }
3224 if ( bValue )
3225 PushString(EMPTY_OUSTRINGScGlobal::GetEmptyOUString());
3226 else
3227 {
3228 // like GetString()
3229 svl::SharedString aStr;
3230 GetCellString(aStr, aCell);
3231 PushString(aStr);
3232 }
3233 }
3234 break;
3235 case svMatrix:
3236 case svExternalSingleRef:
3237 case svExternalDoubleRef:
3238 {
3239 double fVal;
3240 svl::SharedString aStr;
3241 ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
3242 if (ScMatrix::IsValueType( nMatValType))
3243 PushString(svl::SharedString::getEmptyString());
3244 else
3245 PushString( aStr);
3246 }
3247 break;
3248 case svDouble :
3249 {
3250 PopError();
3251 PushString( EMPTY_OUSTRINGScGlobal::GetEmptyOUString() );
3252 }
3253 break;
3254 case svString :
3255 ; // leave on stack
3256 break;
3257 default :
3258 PushError( FormulaError::UnknownOpCode);
3259 }
3260}
3261
3262void ScInterpreter::ScValue()
3263{
3264 OUString aInputString;
3265 double fVal;
3266
3267 switch ( GetRawStackType() )
3268 {
3269 case svMissing:
3270 case svEmptyCell:
3271 Pop();
3272 PushInt(0);
3273 return;
3274 case svDouble:
3275 return; // leave on stack
3276
3277 case svSingleRef:
3278 case svDoubleRef:
3279 {
3280 ScAddress aAdr;
3281 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3282 {
3283 PushInt(0);
3284 return;
3285 }
3286 ScRefCellValue aCell(mrDoc, aAdr);
3287 if (aCell.hasString())
3288 {
3289 svl::SharedString aSS;
3290 GetCellString(aSS, aCell);
3291 aInputString = aSS.getString();
3292 }
3293 else if (aCell.hasNumeric())
3294 {
3295 PushDouble( GetCellValue(aAdr, aCell) );
3296 return;
3297 }
3298 else
3299 {
3300 PushDouble(0.0);
3301 return;
3302 }
3303 }
3304 break;
3305 case svMatrix:
3306 {
3307 svl::SharedString aSS;
3308 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal,
3309 aSS);
3310 aInputString = aSS.getString();
3311 switch (nType)
3312 {
3313 case ScMatValType::Empty:
3314 fVal = 0.0;
3315 [[fallthrough]];
3316 case ScMatValType::Value:
3317 case ScMatValType::Boolean:
3318 PushDouble( fVal);
3319 return;
3320 case ScMatValType::String:
3321 // evaluated below
3322 break;
3323 default:
3324 PushIllegalArgument();
3325 }
3326 }
3327 break;
3328 default:
3329 aInputString = GetString().getString();
3330 break;
3331 }
3332
3333 sal_uInt32 nFIndex = 0; // 0 for default locale
3334 if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
3335 PushDouble(fVal);
3336 else
3337 PushIllegalArgument();
3338}
3339
3340// fdo#57180
3341void ScInterpreter::ScNumberValue()
3342{
3343
3344 sal_uInt8 nParamCount = GetByte();
3345 if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
3346 return;
3347
3348 OUString aInputString;
3349 OUString aGroupSeparator;
3350 sal_Unicode cDecimalSeparator = 0;
3351
3352 if ( nParamCount == 3 )
3353 aGroupSeparator = GetString().getString();
3354
3355 if ( nParamCount >= 2 )
3356 {
3357 OUString aDecimalSeparator = GetString().getString();
3358 if ( aDecimalSeparator.getLength() == 1 )
3359 cDecimalSeparator = aDecimalSeparator[ 0 ];
3360 else
3361 {
3362 PushIllegalArgument(); //if given, separator length must be 1
3363 return;
3364 }
3365 }
3366
3367 if ( cDecimalSeparator && aGroupSeparator.indexOf( cDecimalSeparator ) != -1 )
3368 {
3369 PushIllegalArgument(); //decimal separator cannot appear in group separator
3370 return;
3371 }
3372
3373 switch (GetStackType())
3374 {
3375 case svDouble:
3376 return; // leave on stack
3377 default:
3378 aInputString = GetString().getString();
3379 }
3380 if ( nGlobalError != FormulaError::NONE )
3381 {
3382 PushError( nGlobalError );
3383 return;
3384 }
3385 if ( aInputString.isEmpty() )
3386 {
3387 if ( maCalcConfig.mbEmptyStringAsZero )
3388 PushDouble( 0.0 );
3389 else
3390 PushNoValue();
3391 return;
3392 }
3393
3394 sal_Int32 nDecSep = aInputString.indexOf( cDecimalSeparator );
3395 if ( nDecSep != 0 )
3396 {
3397 OUString aTemporary( nDecSep >= 0 ? aInputString.copy( 0, nDecSep ) : aInputString );
3398 sal_Int32 nIndex = 0;
3399 while (nIndex < aGroupSeparator.getLength())
3400 {
3401 sal_uInt32 nChar = aGroupSeparator.iterateCodePoints( &nIndex );
3402 aTemporary = aTemporary.replaceAll( OUString( &nChar, 1 ), "" );
3403 }
3404 if ( nDecSep >= 0 )
3405 aInputString = aTemporary + aInputString.copy( nDecSep );
3406 else
3407 aInputString = aTemporary;
3408 }
3409
3410 for ( sal_Int32 i = aInputString.getLength(); --i >= 0; )
3411 {
3412 sal_Unicode c = aInputString[ i ];
3413 if ( c == 0x0020 || c == 0x0009 || c == 0x000A || c == 0x000D )
3414 aInputString = aInputString.replaceAt( i, 1, "" ); // remove spaces etc.
3415 }
3416 sal_Int32 nPercentCount = 0;
3417 for ( sal_Int32 i = aInputString.getLength() - 1; i >= 0 && aInputString[ i ] == 0x0025; i-- )
3418 {
3419 aInputString = aInputString.replaceAt( i, 1, "" ); // remove and count trailing '%'
3420 nPercentCount++;
3421 }
3422
3423 rtl_math_ConversionStatus eStatus;
3424 sal_Int32 nParseEnd;
3425 double fVal = ::rtl::math::stringToDouble( aInputString, cDecimalSeparator, 0, &eStatus, &nParseEnd );
3426 if ( eStatus == rtl_math_ConversionStatus_Ok && nParseEnd == aInputString.getLength() )
3427 {
3428 if (nPercentCount)
3429 fVal *= pow( 10.0, -(nPercentCount * 2)); // process '%' from input string
3430 PushDouble(fVal);
3431 return;
3432 }
3433 PushNoValue();
3434}
3435
3436//2do: this should be a proper unicode string method
3437static bool lcl_ScInterpreter_IsPrintable( sal_Unicode c )
3438{
3439 return 0x20 <= c && c != 0x7f;
3440}
3441
3442void ScInterpreter::ScClean()
3443{
3444 OUString aStr = GetString().getString();
3445 for ( sal_Int32 i = 0; i < aStr.getLength(); i++ )
3446 {
3447 if ( !lcl_ScInterpreter_IsPrintable( aStr[i] ) )
3448 aStr = aStr.replaceAt(i,1,"");
3449 }
3450 PushString(aStr);
3451}
3452
3453void ScInterpreter::ScCode()
3454{
3455//2do: make it full range unicode?
3456 OUString aStr = GetString().getString();
3457 if (aStr.isEmpty())
3458 PushInt(0);
3459 else
3460 {
3461 //"classic" ByteString conversion flags
3462 const sal_uInt32 convertFlags =
3463 RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE((sal_uInt32)0x0800) |
3464 RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE((sal_uInt32)0x1000) |
3465 RTL_UNICODETOTEXT_FLAGS_FLUSH((sal_uInt32)0x8000) |
3466 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT((sal_uInt32)0x0006) |
3467 RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT((sal_uInt32)0x0060) |
3468 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE((sal_uInt32)0x0100);
3469 PushInt( static_cast<unsigned char>(OUStringToOString(OUString(aStr[0]), osl_getThreadTextEncoding(), convertFlags).toChar()) );
3470 }
3471}
3472
3473void ScInterpreter::ScChar()
3474{
3475//2do: make it full range unicode?
3476 double fVal = GetDouble();
3477 if (fVal < 0.0 || fVal >= 256.0)
3478 PushIllegalArgument();
3479 else
3480 {
3481 //"classic" ByteString conversion flags
3482 const sal_uInt32 convertFlags =
3483 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT((sal_uInt32)0x0004) |
3484 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT((sal_uInt32)0x0030) |
3485 RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT((sal_uInt32)0x0300);
3486
3487 char cEncodedChar = static_cast<char>(fVal);
3488 OUString aStr(&cEncodedChar, 1, osl_getThreadTextEncoding(), convertFlags);
3489 PushString(aStr);
3490 }
3491}
3492
3493/* #i70213# fullwidth/halfwidth conversion provided by
3494 * Takashi Nakamoto <bluedwarf@ooo>
3495 * erAck: added Excel compatibility conversions as seen in issue's test case. */
3496
3497static OUString lcl_convertIntoHalfWidth( const OUString & rStr )
3498{
3499 // Make the initialization thread-safe. Since another function needs to be called, move it all to another
3500 // function and thread-safely initialize a static reference in this function.
3501 auto init = []() -> utl::TransliterationWrapper&
3502 {
3503 static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3504 trans.loadModuleByImplName( "FULLWIDTH_HALFWIDTH_LIKE_ASC", LANGUAGE_SYSTEMLanguageType(0x0000) );
3505 return trans;
3506 };
3507 static utl::TransliterationWrapper& aTrans( init());
3508 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3509}
3510
3511static OUString lcl_convertIntoFullWidth( const OUString & rStr )
3512{
3513 auto init = []() -> utl::TransliterationWrapper&
3514 {
3515 static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3516 trans.loadModuleByImplName( "HALFWIDTH_FULLWIDTH_LIKE_JIS", LANGUAGE_SYSTEMLanguageType(0x0000) );
3517 return trans;
3518 };
3519 static utl::TransliterationWrapper& aTrans( init());
3520 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3521}
3522
3523/* ODFF:
3524 * Summary: Converts half-width to full-width ASCII and katakana characters.
3525 * Semantics: Conversion is done for half-width ASCII and katakana characters,
3526 * other characters are simply copied from T to the result. This is the
3527 * complementary function to ASC.
3528 * For references regarding halfwidth and fullwidth characters see
3529 * http://www.unicode.org/reports/tr11/
3530 * http://www.unicode.org/charts/charindex2.html#H
3531 * http://www.unicode.org/charts/charindex2.html#F
3532 */
3533void ScInterpreter::ScJis()
3534{
3535 if (MustHaveParamCount( GetByte(), 1))
3536 PushString( lcl_convertIntoFullWidth( GetString().getString()));
3537}
3538
3539/* ODFF:
3540 * Summary: Converts full-width to half-width ASCII and katakana characters.
3541 * Semantics: Conversion is done for full-width ASCII and katakana characters,
3542 * other characters are simply copied from T to the result. This is the
3543 * complementary function to JIS.
3544 */
3545void ScInterpreter::ScAsc()
3546{
3547 if (MustHaveParamCount( GetByte(), 1))
3548 PushString( lcl_convertIntoHalfWidth( GetString().getString()));
3549}
3550
3551void ScInterpreter::ScUnicode()
3552{
3553 if ( MustHaveParamCount( GetByte(), 1 ) )
3554 {
3555 OUString aStr = GetString().getString();
3556 if (aStr.isEmpty())
3557 PushIllegalParameter();
3558 else
3559 {
3560 sal_Int32 i = 0;
3561 PushDouble(aStr.iterateCodePoints(&i));
3562 }
3563 }
3564}
3565
3566void ScInterpreter::ScUnichar()
3567{
3568 if ( MustHaveParamCount( GetByte(), 1 ) )
3569 {
3570 sal_uInt32 nCodePoint = GetUInt32();
3571 if (nGlobalError != FormulaError::NONE || !rtl::isUnicodeCodePoint(nCodePoint))
3572 PushIllegalArgument();
3573 else
3574 {
3575 OUString aStr( &nCodePoint, 1 );
3576 PushString( aStr );
3577 }
3578 }
3579}
3580
3581bool ScInterpreter::SwitchToArrayRefList( ScMatrixRef& xResMat, SCSIZE nMatRows, double fCurrent,
3582 const std::function<void( SCSIZE i, double fCurrent )>& MatOpFunc, bool bDoMatOp )
3583{
3584 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
3585 if (!p || !p->IsArrayResult())
3586 return false;
3587
3588 if (!xResMat)
3589 {
3590 // Create and init all elements with current value.
3591 assert(nMatRows > 0)(static_cast <bool> (nMatRows > 0) ? void (0) : __assert_fail
("nMatRows > 0", "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
, 3591, __extension__ __PRETTY_FUNCTION__))
;
3592 xResMat = GetNewMat( 1, nMatRows, true);
3593 xResMat->FillDouble( fCurrent, 0,0, 0,nMatRows-1);
3594 }
3595 else if (bDoMatOp)
3596 {
3597 // Current value and values from vector are operands
3598 // for each vector position.
3599 for (SCSIZE i=0; i < nMatRows; ++i)
3600 {
3601 MatOpFunc( i, fCurrent);
3602 }
3603 }
3604 return true;
3605}
3606
3607void ScInterpreter::ScMin( bool bTextAsZero )
3608{
3609 short nParamCount = GetByte();
3610 if (!MustHaveParamCountMin( nParamCount, 1))
3611 return;
3612
3613 ScMatrixRef xResMat;
3614 double nMin = ::std::numeric_limits<double>::max();
3615 auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMin )
3616 {
3617 double fVecRes = xResMat->GetDouble(0,i);
3618 if (fVecRes > fCurMin)
3619 xResMat->PutDouble( fCurMin, 0,i);
3620 };
3621 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3622 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3623
3624 double nVal = 0.0;
3625 ScAddress aAdr;
3626 ScRange aRange;
3627 size_t nRefInList = 0;
3628 while (nParamCount-- > 0)
3629 {
3630 switch (GetStackType())
3631 {
3632 case svDouble :
3633 {
3634 nVal = GetDouble();
3635 if (nMin > nVal) nMin = nVal;
3636 nFuncFmtType = SvNumFormatType::NUMBER;
3637 }
3638 break;
3639 case svSingleRef :
3640 {
3641 PopSingleRef( aAdr );
3642 ScRefCellValue aCell(mrDoc, aAdr);
3643 if (aCell.hasNumeric())
3644 {
3645 nVal = GetCellValue(aAdr, aCell);
3646 CurFmtToFuncFmt();
3647 if (nMin > nVal) nMin = nVal;
3648 }
3649 else if (bTextAsZero && aCell.hasString())
3650 {
3651 if ( nMin > 0.0 )
3652 nMin = 0.0;
3653 }
3654 }
3655 break;
3656 case svRefList :
3657 {
3658 // bDoMatOp only for non-array value when switching to
3659 // ArrayRefList.
3660 if (SwitchToArrayRefList( xResMat, nMatRows, nMin, MatOpFunc,
3661 nRefArrayPos == std::numeric_limits<size_t>::max()))
3662 {
3663 nRefArrayPos = nRefInList;
3664 }
3665 }
3666 [[fallthrough]];
3667 case svDoubleRef :
3668 {
3669 FormulaError nErr = FormulaError::NONE;
3670 PopDoubleRef( aRange, nParamCount, nRefInList);
3671 ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
3672 aValIter.SetInterpreterContext( &mrContext );
3673 if (aValIter.GetFirst(nVal, nErr))
3674 {
3675 if (nMin > nVal)
3676 nMin = nVal;
3677 aValIter.GetCurNumFmtInfo( mrContext, nFuncFmtType, nFuncFmtIndex );
3678 while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3679 {
3680 if (nMin > nVal)
3681 nMin = nVal;
3682 }
3683 SetError(nErr);
3684 }
3685 if (nRefArrayPos != std::numeric_limits<size_t>::max())
3686 {
3687 // Update vector element with current value.
3688 MatOpFunc( nRefArrayPos, nMin);
3689
3690 // Reset.
3691 nMin = std::numeric_limits<double>::max();
3692 nVal = 0.0;
3693 nRefArrayPos = std::numeric_limits<size_t>::max();
3694 }
3695 }
3696 break;
3697 case svMatrix :
3698 case svExternalSingleRef:
3699 case svExternalDoubleRef:
3700 {
3701 ScMatrixRef pMat = GetMatrix();
3702 if (pMat)
3703 {
3704 nFuncFmtType = SvNumFormatType::NUMBER;
3705 nVal = pMat->GetMinValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
3706 if (nMin > nVal)
3707 nMin = nVal;
3708 }
3709 }
3710 break;
3711 case svString :
3712 {
3713 Pop();
3714 if ( bTextAsZero )
3715 {
3716 if ( nMin > 0.0 )
3717 nMin = 0.0;
3718 }
3719 else
3720 SetError(FormulaError::IllegalParameter);
3721 }
3722 break;
3723 default :
3724 Pop();
3725 SetError(FormulaError::IllegalParameter);
3726 }
3727 }
3728
3729 if (xResMat)
3730 {
3731 // Include value of last non-references-array type and calculate final result.
3732 if (nMin < std::numeric_limits<double>::max())
3733 {
3734 for (SCSIZE i=0; i < nMatRows; ++i)
3735 {
3736 MatOpFunc( i, nMin);
3737 }
3738 }
3739 else
3740 {
3741 /* TODO: the awkward "no value is minimum 0.0" is likely the case
3742 * if a value is numeric_limits::max. Still, that could be a valid
3743 * minimum value as well, but nVal and nMin had been reset after
3744 * the last svRefList... so we may lie here. */
3745 for (SCSIZE i=0; i < nMatRows; ++i)
3746 {
3747 double fVecRes = xResMat->GetDouble(0,i);
3748 if (fVecRes == std::numeric_limits<double>::max())
3749 xResMat->PutDouble( 0.0, 0,i);
3750 }
3751 }
3752 PushMatrix( xResMat);
3753 }
3754 else
3755 {
3756 if (!std::isfinite(nVal))
3757 PushError( GetDoubleErrorValue( nVal));
3758 else if ( nVal < nMin )
3759 PushDouble(0.0); // zero or only empty arguments
3760 else
3761 PushDouble(nMin);
3762 }
3763}
3764
3765void ScInterpreter::ScMax( bool bTextAsZero )
3766{
3767 short nParamCount = GetByte();
3768 if (!MustHaveParamCountMin( nParamCount, 1))
3769 return;
3770
3771 ScMatrixRef xResMat;
3772 double nMax = std::numeric_limits<double>::lowest();
3773 auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMax )
3774 {
3775 double fVecRes = xResMat->GetDouble(0,i);
3776 if (fVecRes < fCurMax)
3777 xResMat->PutDouble( fCurMax, 0,i);
3778 };
3779 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3780 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3781
3782 double nVal = 0.0;
3783 ScAddress aAdr;
3784 ScRange aRange;
3785 size_t nRefInList = 0;
3786 while (nParamCount-- > 0)
3787 {
3788 switch (GetStackType())
3789 {
3790 case svDouble :
3791 {
3792 nVal = GetDouble();
3793 if (nMax < nVal) nMax = nVal;
3794 nFuncFmtType = SvNumFormatType::NUMBER;
3795 }
3796 break;
3797 case svSingleRef :
3798 {
3799 PopSingleRef( aAdr );
3800 ScRefCellValue aCell(mrDoc, aAdr);
3801 if (aCell.hasNumeric())
3802 {
3803 nVal = GetCellValue(aAdr, aCell);
3804 CurFmtToFuncFmt();
3805 if (nMax < nVal) nMax = nVal;
3806 }
3807 else if (bTextAsZero && aCell.hasString())
3808 {
3809 if ( nMax < 0.0 )
3810 nMax = 0.0;
3811 }
3812 }
3813 break;
3814 case svRefList :
3815 {
3816 // bDoMatOp only for non-array value when switching to
3817 // ArrayRefList.
3818 if (SwitchToArrayRefList( xResMat, nMatRows, nMax, MatOpFunc,
3819 nRefArrayPos == std::numeric_limits<size_t>::max()))
3820 {
3821 nRefArrayPos = nRefInList;
3822 }
3823 }
3824 [[fallthrough]];
3825 case svDoubleRef :
3826 {
3827 FormulaError nErr = FormulaError::NONE;
3828 PopDoubleRef( aRange, nParamCount, nRefInList);
3829 ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
3830 aValIter.SetInterpreterContext( &mrContext );
3831 if (aValIter.GetFirst(nVal, nErr))
3832 {
3833 if (nMax < nVal)
3834 nMax = nVal;
3835 aValIter.GetCurNumFmtInfo( mrContext, nFuncFmtType, nFuncFmtIndex );
3836 while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3837 {
3838 if (nMax < nVal)
3839 nMax = nVal;
3840 }
3841 SetError(nErr);
3842 }
3843 if (nRefArrayPos != std::numeric_limits<size_t>::max())
3844 {
3845 // Update vector element with current value.
3846 MatOpFunc( nRefArrayPos, nMax);
3847
3848 // Reset.
3849 nMax = std::numeric_limits<double>::lowest();
3850 nVal = 0.0;
3851 nRefArrayPos = std::numeric_limits<size_t>::max();
3852 }
3853 }
3854 break;
3855 case svMatrix :
3856 case svExternalSingleRef:
3857 case svExternalDoubleRef:
3858 {
3859 ScMatrixRef pMat = GetMatrix();
3860 if (pMat)
3861 {
3862 nFuncFmtType = SvNumFormatType::NUMBER;
3863 nVal = pMat->GetMaxValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
3864 if (nMax < nVal)
3865 nMax = nVal;
3866 }
3867 }
3868 break;
3869 case svString :
3870 {
3871 Pop();
3872 if ( bTextAsZero )
3873 {
3874 if ( nMax < 0.0 )
3875 nMax = 0.0;
3876 }
3877 else
3878 SetError(FormulaError::IllegalParameter);
3879 }
3880 break;
3881 default :
3882 Pop();
3883 SetError(FormulaError::IllegalParameter);
3884 }
3885 }
3886
3887 if (xResMat)
3888 {
3889 // Include value of last non-references-array type and calculate final result.
3890 if (nMax > std::numeric_limits<double>::lowest())
3891 {
3892 for (SCSIZE i=0; i < nMatRows; ++i)
3893 {
3894 MatOpFunc( i, nMax);
3895 }
3896 }
3897 else
3898 {
3899 /* TODO: the awkward "no value is maximum 0.0" is likely the case
3900 * if a value is numeric_limits::lowest. Still, that could be a
3901 * valid maximum value as well, but nVal and nMax had been reset
3902 * after the last svRefList... so we may lie here. */
3903 for (SCSIZE i=0; i < nMatRows; ++i)
3904 {
3905 double fVecRes = xResMat->GetDouble(0,i);
3906 if (fVecRes == -std::numeric_limits<double>::max())
3907 xResMat->PutDouble( 0.0, 0,i);
3908 }
3909 }
3910 PushMatrix( xResMat);
3911 }
3912 else
3913 {
3914 if (!std::isfinite(nVal))
3915 PushError( GetDoubleErrorValue( nVal));
3916 else if ( nVal > nMax )
3917 PushDouble(0.0); // zero or only empty arguments
3918 else
3919 PushDouble(nMax);
3920 }
3921}
3922
3923void ScInterpreter::GetStVarParams( bool bTextAsZero, double(*VarResult)( double fVal, size_t nValCount ) )
3924{
3925 short nParamCount = GetByte();
3926 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3927
3928 struct ArrayRefListValue
3929 {
3930 std::vector<double> mvValues;
3931 double mfSum;
3932 ArrayRefListValue() : mfSum(0.0) {}
3933 };
3934 std::vector<ArrayRefListValue> vArrayValues;
3935
3936 std::vector<double> values;
3937 double fSum = 0.0;
3938 double fVal = 0.0;
3939 ScAddress aAdr;
3940 ScRange aRange;
3941 size_t nRefInList = 0;
3942 while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
3943 {
3944 switch (GetStackType())
3945 {
3946 case svDouble :
3947 {
3948 fVal = GetDouble();
3949 if (nGlobalError == FormulaError::NONE)
3950 {
3951 values.push_back(fVal);
3952 fSum += fVal;
3953 }
3954 }
3955 break;
3956 case svSingleRef :
3957 {
3958 PopSingleRef( aAdr );
3959 ScRefCellValue aCell(mrDoc, aAdr);
3960 if (aCell.hasNumeric())
3961 {
3962 fVal = GetCellValue(aAdr, aCell);
3963 if (nGlobalError == FormulaError::NONE)
3964 {
3965 values.push_back(fVal);
3966 fSum += fVal;
3967 }
3968 }
3969 else if (bTextAsZero && aCell.hasString())
3970 {
3971 values.push_back(0.0);
3972 }
3973 }
3974 break;
3975 case svRefList :
3976 {
3977 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
3978 if (p && p->IsArrayResult())
3979 {
3980 size_t nRefArrayPos = nRefInList;
3981 if (vArrayValues.empty())
3982 {
3983 // Create and init all elements with current value.
3984 assert(nMatRows > 0)(static_cast <bool> (nMatRows > 0) ? void (0) : __assert_fail
("nMatRows > 0", "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
, 3984, __extension__ __PRETTY_FUNCTION__))
;
3985 vArrayValues.resize(nMatRows);
3986 for (auto & it : vArrayValues)
3987 {
3988 it.mvValues = values;
3989 it.mfSum = fSum;
3990 }
3991 }
3992 else
3993 {
3994 // Current value and values from vector are operands
3995 // for each vector position.
3996 for (auto & it : vArrayValues)
3997 {
3998 it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
3999 it.mfSum += fSum;
4000 }
4001 }
4002 ArrayRefListValue& rArrayValue = vArrayValues[nRefArrayPos];
4003 FormulaError nErr = FormulaError::NONE;
4004 PopDoubleRef( aRange, nParamCount, nRefInList);
4005 ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
4006 if (aValIter.GetFirst(fVal, nErr))
4007 {
4008 do
4009 {
4010 rArrayValue.mvValues.push_back(fVal);
4011 rArrayValue.mfSum += fVal;
4012 }
4013 while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4014 }
4015 if ( nErr != FormulaError::NONE )
4016 {
4017 rArrayValue.mfSum = CreateDoubleError( nErr);
4018 }
4019 // Reset.
4020 std::vector<double>().swap(values);
4021 fSum = 0.0;
4022 break;
4023 }
4024 }
4025 [[fallthrough]];
4026 case svDoubleRef :
4027 {
4028 FormulaError nErr = FormulaError::NONE;
4029 PopDoubleRef( aRange, nParamCount, nRefInList);
4030 ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
4031 if (aValIter.GetFirst(fVal, nErr))
4032 {
4033 do
4034 {
4035 values.push_back(fVal);
4036 fSum += fVal;
4037 }
4038 while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4039 }
4040 if ( nErr != FormulaError::NONE )
4041 {
4042 SetError(nErr);
4043 }
4044 }
4045 break;
4046 case svExternalSingleRef :
4047 case svExternalDoubleRef :
4048 case svMatrix :
4049 {
4050 ScMatrixRef pMat = GetMatrix();
4051 if (pMat)
4052 {
4053 const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal);
4054 SCSIZE nC, nR;
4055 pMat->GetDimensions(nC, nR);
4056 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
4057 {
4058 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
4059 {
4060 if (!pMat->IsStringOrEmpty(nMatCol,nMatRow))
4061 {
4062 fVal= pMat->GetDouble(nMatCol,nMatRow);
4063 if (nGlobalError == FormulaError::NONE)
4064 {
4065 values.push_back(fVal);
4066 fSum += fVal;
4067 }
4068 else if (bIgnoreErrVal)
4069 nGlobalError = FormulaError::NONE;
4070 }
4071 else if ( bTextAsZero )
4072 {
4073 values.push_back(0.0);
4074 }
4075 }
4076 }
4077 }
4078 }
4079 break;
4080 case svString :
4081 {
4082 Pop();
4083 if ( bTextAsZero )
4084 {
4085 values.push_back(0.0);
4086 }
4087 else
4088 SetError(FormulaError::IllegalParameter);
4089 }
4090 break;
4091 default :
4092 Pop();
4093 SetError(FormulaError::IllegalParameter);
4094 }
4095 }
4096
4097 if (!vArrayValues.empty())
4098 {
4099 // Include value of last non-references-array type and calculate final result.
4100 if (!values.empty())
4101 {
4102 for (auto & it : vArrayValues)
4103 {
4104 it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
4105 it.mfSum += fSum;
4106 }
4107 }
4108 ScMatrixRef xResMat = GetNewMat( 1, nMatRows, true);
4109 for (SCSIZE r=0; r < nMatRows; ++r)
4110 {
4111 ::std::vector<double>::size_type n = vArrayValues[r].mvValues.size();
4112 if (!n)
4113 xResMat->PutError( FormulaError::DivisionByZero, 0, r);
4114 else
4115 {
4116 ArrayRefListValue& rArrayValue = vArrayValues[r];
4117 double vSum = 0.0;
4118 const double vMean = rArrayValue.mfSum / n;
4119 for (::std::vector<double>::size_type i = 0; i < n; i++)
4120 vSum += ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean) *
4121 ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean);
4122 xResMat->PutDouble( VarResult( vSum, n), 0, r);
4123 }
4124 }
4125 PushMatrix( xResMat);
4126 }
4127 else
4128 {
4129 ::std::vector<double>::size_type n = values.size();
4130 if (!n)
4131 SetError( FormulaError::DivisionByZero);
4132 double vSum = 0.0;
4133 if (nGlobalError == FormulaError::NONE)
4134 {
4135 const double vMean = fSum / n;
4136 for (::std::vector<double>::size_type i = 0; i < n; i++)
4137 vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean);
4138 }
4139 PushDouble( VarResult( vSum, n));
4140 }
4141}
4142
4143void ScInterpreter::ScVar( bool bTextAsZero )
4144{
4145 auto VarResult = []( double fVal, size_t nValCount )
4146 {
4147 if (nValCount <= 1)
4148 return CreateDoubleError( FormulaError::DivisionByZero );
4149 else
4150 return fVal / (nValCount - 1);
4151 };
4152 GetStVarParams( bTextAsZero, VarResult );
4153}
4154
4155void ScInterpreter::ScVarP( bool bTextAsZero )
4156{
4157 auto VarResult = []( double fVal, size_t nValCount )
4158 {
4159 return sc::div( fVal, nValCount);
4160 };
4161 GetStVarParams( bTextAsZero, VarResult );
4162
4163}
4164
4165void ScInterpreter::ScStDev( bool bTextAsZero )
4166{
4167 auto VarResult = []( double fVal, size_t nValCount )
4168 {
4169 if (nValCount <= 1)
4170 return CreateDoubleError( FormulaError::DivisionByZero );
4171 else
4172 return sqrt( fVal / (nValCount - 1));
4173 };
4174 GetStVarParams( bTextAsZero, VarResult );
4175}
4176
4177void ScInterpreter::ScStDevP( bool bTextAsZero )
4178{
4179 auto VarResult = []( double fVal, size_t nValCount )
4180 {
4181 if (nValCount == 0)
4182 return CreateDoubleError( FormulaError::DivisionByZero );
4183 else
4184 return sqrt( fVal / nValCount);
4185 };
4186 GetStVarParams( bTextAsZero, VarResult );
4187
4188 /* this was: PushDouble( sqrt( div( nVal, nValCount)));
4189 *
4190 * Besides that the special NAN gets lost in the call through sqrt(),
4191 * unxlngi6.pro then looped back and forth somewhere between div() and
4192 * ::rtl::math::setNan(). Tests showed that
4193 *
4194 * sqrt( div( 1, 0));
4195 *
4196 * produced a loop, but
4197 *
4198 * double f1 = div( 1, 0);
4199 * sqrt( f1 );
4200 *
4201 * was fine. There seems to be some compiler optimization problem. It does
4202 * not occur when compiled with debug=t.
4203 */
4204}
4205
4206void ScInterpreter::ScColumns()
4207{
4208 sal_uInt8 nParamCount = GetByte();
4209 sal_uLong nVal = 0;
4210 SCCOL nCol1;
4211 SCROW nRow1;
4212 SCTAB nTab1;
4213 SCCOL nCol2;
4214 SCROW nRow2;
4215 SCTAB nTab2;
4216 while (nParamCount-- > 0)
4217 {
4218 switch ( GetStackType() )
4219 {
4220 case svSingleRef:
4221 PopError();
4222 nVal++;
4223 break;
4224 case svDoubleRef:
4225 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4226 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4227 static_cast<sal_uLong>(nCol2 - nCol1 + 1);
4228 break;
4229 case svMatrix:
4230 {
4231 ScMatrixRef pMat = PopMatrix();
4232 if (pMat)
4233 {
4234 SCSIZE nC, nR;
4235 pMat->GetDimensions(nC, nR);
4236 nVal += nC;
4237 }
4238 }
4239 break;
4240 case svExternalSingleRef:
4241 PopError();
4242 nVal++;
4243 break;
4244 case svExternalDoubleRef:
4245 {
4246 sal_uInt16 nFileId;
4247 OUString aTabName;
4248 ScComplexRefData aRef;
4249 PopExternalDoubleRef( nFileId, aTabName, aRef);
4250 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4251 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4252 static_cast<sal_uLong>(aAbs.aEnd.Col() - aAbs.aStart.Col() + 1);
4253 }
4254 break;
4255 default:
4256 PopError();
4257 SetError(FormulaError::IllegalParameter);
4258 }
4259 }
4260 PushDouble(static_cast<double>(nVal));
4261}
4262
4263void ScInterpreter::ScRows()
4264{
4265 sal_uInt8 nParamCount = GetByte();
4266 sal_uLong nVal = 0;
4267 SCCOL nCol1;
4268 SCROW nRow1;
4269 SCTAB nTab1;
4270 SCCOL nCol2;
4271 SCROW nRow2;
4272 SCTAB nTab2;
4273 while (nParamCount-- > 0)
4274 {
4275 switch ( GetStackType() )
4276 {
4277 case svSingleRef:
4278 PopError();
4279 nVal++;
4280 break;
4281 case svDoubleRef:
4282 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4283 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4284 static_cast<sal_uLong>(nRow2 - nRow1 + 1);
4285 break;
4286 case svMatrix:
4287 {
4288 ScMatrixRef pMat = PopMatrix();
4289 if (pMat)
4290 {
4291 SCSIZE nC, nR;
4292 pMat->GetDimensions(nC, nR);
4293 nVal += nR;
4294 }
4295 }
4296 break;
4297 case svExternalSingleRef:
4298 PopError();
4299 nVal++;
4300 break;
4301 case svExternalDoubleRef:
4302 {
4303 sal_uInt16 nFileId;
4304 OUString aTabName;
4305 ScComplexRefData aRef;
4306 PopExternalDoubleRef( nFileId, aTabName, aRef);
4307 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4308 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4309 static_cast<sal_uLong>(aAbs.aEnd.Row() - aAbs.aStart.Row() + 1);
4310 }
4311 break;
4312 default:
4313 PopError();
4314 SetError(FormulaError::IllegalParameter);
4315 }
4316 }
4317 PushDouble(static_cast<double>(nVal));
4318}
4319
4320void ScInterpreter::ScSheets()
4321{
4322 sal_uInt8 nParamCount = GetByte();
4323 sal_uLong nVal;
4324 if ( nParamCount == 0 )
4325 nVal = mrDoc.GetTableCount();
4326 else
4327 {
4328 nVal = 0;
4329 SCCOL nCol1;
4330 SCROW nRow1;
4331 SCTAB nTab1;
4332 SCCOL nCol2;
4333 SCROW nRow2;
4334 SCTAB nTab2;
4335 while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
4336 {
4337 switch ( GetStackType() )
4338 {
4339 case svSingleRef:
4340 case svExternalSingleRef:
4341 PopError();
4342 nVal++;
4343 break;
4344 case svDoubleRef:
4345 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4346 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1);
4347 break;
4348 case svExternalDoubleRef:
4349 {
4350 sal_uInt16 nFileId;
4351 OUString aTabName;
4352 ScComplexRefData aRef;
4353 PopExternalDoubleRef( nFileId, aTabName, aRef);
4354 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4355 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1);
4356 }
4357 break;
4358 default:
4359 PopError();
4360 SetError( FormulaError::IllegalParameter );
4361 }
4362 }
4363 }
4364 PushDouble( static_cast<double>(nVal) );
4365}
4366
4367void ScInterpreter::ScColumn()
4368{
4369 sal_uInt8 nParamCount = GetByte();
4370 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4371 return;
4372
4373 double nVal = 0.0;
4374 if (nParamCount == 0)
4375 {
4376 nVal = aPos.Col() + 1;
4377 if (bMatrixFormula)
4378 {
4379 SCCOL nCols = 0;
4380 SCROW nRows = 0;
4381 if (pMyFormulaCell)
4382 pMyFormulaCell->GetMatColsRows( nCols, nRows);
4383 if (nCols == 0)
4384 {
4385 // Happens if called via ScViewFunc::EnterMatrix()
4386 // ScFormulaCell::GetResultDimensions() as of course a
4387 // matrix result is not available yet.
4388 nCols = 1;
4389 }
4390 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1);
4391 if (pResMat)
4392 {
4393 for (SCCOL i=0; i < nCols; ++i)
4394 pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0);
4395 PushMatrix( pResMat);
4396 return;
4397 }
4398 }
4399 }
4400 else
4401 {
4402 switch ( GetStackType() )
4403 {
4404 case svSingleRef :
4405 {
4406 SCCOL nCol1(0);
4407 SCROW nRow1(0);
4408 SCTAB nTab1(0);
4409 PopSingleRef( nCol1, nRow1, nTab1 );
4410 nVal = static_cast<double>(nCol1 + 1);
4411 }
4412 break;
4413 case svExternalSingleRef :
4414 {
4415 sal_uInt16 nFileId;
4416 OUString aTabName;
4417 ScSingleRefData aRef;
4418 PopExternalSingleRef( nFileId, aTabName, aRef );
4419 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4420 nVal = static_cast<double>( aAbsRef.Col() + 1 );
4421 }
4422 break;
4423
4424 case svDoubleRef :
4425 case svExternalDoubleRef :
4426 {
4427 SCCOL nCol1;
4428 SCCOL nCol2;
4429 if ( GetStackType() == svDoubleRef )
4430 {
4431 SCROW nRow1;
4432 SCTAB nTab1;
4433 SCROW nRow2;
4434 SCTAB nTab2;
4435 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4436 }
4437 else
4438 {
4439 sal_uInt16 nFileId;
4440 OUString aTabName;
4441 ScComplexRefData aRef;
4442 PopExternalDoubleRef( nFileId, aTabName, aRef );
4443 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4444 nCol1 = aAbs.aStart.Col();
4445 nCol2 = aAbs.aEnd.Col();
4446 }
4447 if (nCol2 > nCol1)
4448 {
4449 ScMatrixRef pResMat = GetNewMat(
4450 static_cast<SCSIZE>(nCol2-nCol1+1), 1);
4451 if (pResMat)
4452 {
4453 for (SCCOL i = nCol1; i <= nCol2; i++)
4454 pResMat->PutDouble(static_cast<double>(i+1),
4455 static_cast<SCSIZE>(i-nCol1), 0);
4456 PushMatrix(pResMat);
4457 return;
4458 }
4459 }
4460 else
4461 nVal = static_cast<double>(nCol1 + 1);
4462 }
4463 break;
4464 default:
4465 SetError( FormulaError::IllegalParameter );
4466 }
4467 }
4468 PushDouble( nVal );
4469}
4470
4471void ScInterpreter::ScRow()
4472{
4473 sal_uInt8 nParamCount = GetByte();
4474 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4475 return;
4476
4477 double nVal = 0.0;
4478 if (nParamCount == 0)
4479 {
4480 nVal = aPos.Row() + 1;
4481 if (bMatrixFormula)
4482 {
4483 SCCOL nCols = 0;
4484 SCROW nRows = 0;
4485 if (pMyFormulaCell)
4486 pMyFormulaCell->GetMatColsRows( nCols, nRows);
4487 if (nRows == 0)
4488 {
4489 // Happens if called via ScViewFunc::EnterMatrix()
4490 // ScFormulaCell::GetResultDimensions() as of course a
4491 // matrix result is not available yet.
4492 nRows = 1;
4493 }
4494 ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows));
4495 if (pResMat)
4496 {
4497 for (SCROW i=0; i < nRows; i++)
4498 pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i));
4499 PushMatrix( pResMat);
4500 return;
4501 }
4502 }
4503 }
4504 else
4505 {
4506 switch ( GetStackType() )
4507 {
4508 case svSingleRef :
4509 {
4510 SCCOL nCol1(0);
4511 SCROW nRow1(0);
4512 SCTAB nTab1(0);
4513 PopSingleRef( nCol1, nRow1, nTab1 );
4514 nVal = static_cast<double>(nRow1 + 1);
4515 }
4516 break;
4517 case svExternalSingleRef :
4518 {
4519 sal_uInt16 nFileId;
4520 OUString aTabName;
4521 ScSingleRefData aRef;
4522 PopExternalSingleRef( nFileId, aTabName, aRef );
4523 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4524 nVal = static_cast<double>( aAbsRef.Row() + 1 );
4525 }
4526 break;
4527 case svDoubleRef :
4528 case svExternalDoubleRef :
4529 {
4530 SCROW nRow1;
4531 SCROW nRow2;
4532 if ( GetStackType() == svDoubleRef )
4533 {
4534 SCCOL nCol1;
4535 SCTAB nTab1;
4536 SCCOL nCol2;
4537 SCTAB nTab2;
4538 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4539 }
4540 else
4541 {
4542 sal_uInt16 nFileId;
4543 OUString aTabName;
4544 ScComplexRefData aRef;
4545 PopExternalDoubleRef( nFileId, aTabName, aRef );
4546 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4547 nRow1 = aAbs.aStart.Row();
4548 nRow2 = aAbs.aEnd.Row();
4549 }
4550 if (nRow2 > nRow1)
4551 {
4552 ScMatrixRef pResMat = GetNewMat( 1,
4553 static_cast<SCSIZE>(nRow2-nRow1+1));
4554 if (pResMat)
4555 {
4556 for (SCROW i = nRow1; i <= nRow2; i++)
4557 pResMat->PutDouble(static_cast<double>(i+1), 0,
4558 static_cast<SCSIZE>(i-nRow1));
4559 PushMatrix(pResMat);
4560 return;
4561 }
4562 }
4563 else
4564 nVal = static_cast<double>(nRow1 + 1);
4565 }
4566 break;
4567 default:
4568 SetError( FormulaError::IllegalParameter );
4569 }
4570 }
4571 PushDouble( nVal );
4572}
4573
4574void ScInterpreter::ScSheet()
4575{
4576 sal_uInt8 nParamCount = GetByte();
4577 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4578 return;
4579
4580 SCTAB nVal = 0;
4581 if ( nParamCount == 0 )
4582 nVal = aPos.Tab() + 1;
4583 else
4584 {
4585 switch ( GetStackType() )
4586 {
4587 case svString :
4588 {
4589 svl::SharedString aStr = PopString();
4590 if ( mrDoc.GetTable(aStr.getString(), nVal))
4591 ++nVal;
4592 else
4593 SetError( FormulaError::IllegalArgument );
4594 }
4595 break;
4596 case svSingleRef :
4597 {
4598 SCCOL nCol1(0);
4599 SCROW nRow1(0);
4600 SCTAB nTab1(0);
4601 PopSingleRef(nCol1, nRow1, nTab1);
4602 nVal = nTab1 + 1;
4603 }
4604 break;
4605 case svDoubleRef :
4606 {
4607 SCCOL nCol1;
4608 SCROW nRow1;
4609 SCTAB nTab1;
4610 SCCOL nCol2;
4611 SCROW nRow2;
4612 SCTAB nTab2;
4613 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4614 nVal = nTab1 + 1;
4615 }
4616 break;
4617 default:
4618 SetError( FormulaError::IllegalParameter );
4619 }
4620 if ( nGlobalError != FormulaError::NONE )
4621 nVal = 0;
4622 }
4623 PushDouble( static_cast<double>(nVal) );
4624}
4625
4626namespace {
4627
4628class VectorMatrixAccessor
4629{
4630public:
4631 VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) :
4632 mrMat(rMat), mbColVec(bColVec) {}
4633
4634 bool IsEmpty(SCSIZE i) const
4635 {
4636 return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0);
4637 }
4638
4639 bool IsEmptyPath(SCSIZE i) const
4640 {
4641 return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0);
4642 }
4643
4644 bool IsValue(SCSIZE i) const
4645 {
4646 return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0);
4647 }
4648
4649 bool IsStringOrEmpty(SCSIZE i) const
4650 {
4651 return mbColVec ? mrMat.IsStringOrEmpty(0, i) : mrMat.IsStringOrEmpty(i, 0);
4652 }
4653
4654 double GetDouble(SCSIZE i) const
4655 {
4656 return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0);
4657 }
4658
4659 OUString GetString(SCSIZE i) const
4660 {
4661 return mbColVec ? mrMat.GetString(0, i).getString() : mrMat.GetString(i, 0).getString();
4662 }
4663
4664 SCSIZE GetElementCount() const
4665 {
4666 SCSIZE nC, nR;
4667 mrMat.GetDimensions(nC, nR);
4668 return mbColVec ? nR : nC;
4669 }
4670
4671private:
4672 const ScMatrix& mrMat;
4673 bool mbColVec;
4674};
4675
4676/** returns -1 when the matrix value is smaller than the query value, 0 when
4677 they are equal, and 1 when the matrix value is larger than the query
4678 value. */
4679sal_Int32 lcl_CompareMatrix2Query(
4680 SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryEntry& rEntry)
4681{
4682 if (rMat.IsEmpty(i))
4683 {
4684 /* TODO: in case we introduced query for real empty this would have to
4685 * be changed! */
4686 return -1; // empty always less than anything else
4687 }
4688
4689 /* FIXME: what is an empty path (result of IF(false;true_path) in
4690 * comparisons? */
4691
4692 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
4693 if (rMat.IsValue(i))
4694 {
4695 const double nVal1 = rMat.GetDouble(i);
4696 if (!std::isfinite(nVal1))
4697 {
4698 // XXX Querying for error values is not required, otherwise we'd
4699 // need to check here.
4700 return 1; // error always greater than numeric or string
4701 }
4702
4703 if (bByString)
4704 return -1; // numeric always less than string
4705
4706 const double nVal2 = rEntry.GetQueryItem().mfVal;
4707 // XXX Querying for error values is not required, otherwise we'd need
4708 // to check here and move that check before the bByString check.
4709 if (nVal1 == nVal2)
4710 return 0;
4711
4712 return nVal1 < nVal2 ? -1 : 1;
4713 }
4714
4715 if (!bByString)
4716 return 1; // string always greater than numeric
4717
4718 OUString aStr1 = rMat.GetString(i);
4719 OUString aStr2 = rEntry.GetQueryItem().maString.getString();
4720
4721 return ScGlobal::GetCollator()->compareString(aStr1, aStr2); // case-insensitive
4722}
4723
4724/** returns the last item with the identical value as the original item
4725 value. */
4726void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat,
4727 SCSIZE nMatCount, bool bReverse)
4728{
4729 if (rMat.IsValue(rIndex))
4730 {
4731 double nVal = rMat.GetDouble(rIndex);
4732 if (bReverse)
4733 while (rIndex > 0 && rMat.IsValue(rIndex-1) &&
4734 nVal == rMat.GetDouble(rIndex-1))
4735 --rIndex;
4736 else
4737 while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) &&
4738 nVal == rMat.GetDouble(rIndex+1))
4739 ++rIndex;
4740 }
4741 // Order of IsEmptyPath, IsEmpty, IsStringOrEmpty is significant!
4742 else if (rMat.IsEmptyPath(rIndex))
4743 {
4744 if (bReverse)
4745 while (rIndex > 0 && rMat.IsEmptyPath(rIndex-1))
4746 --rIndex;
4747 else
4748 while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1))
4749 ++rIndex;
4750 }
4751 else if (rMat.IsEmpty(rIndex))
4752 {
4753 if (bReverse)
4754 while (rIndex > 0 && rMat.IsEmpty(rIndex-1))
4755 --rIndex;
4756 else
4757 while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1))
4758 ++rIndex;
4759 }
4760 else if (rMat.IsStringOrEmpty(rIndex))
4761 {
4762 OUString aStr( rMat.GetString(rIndex));
4763 if (bReverse)
4764 while (rIndex > 0 && rMat.IsStringOrEmpty(rIndex-1) &&
4765 aStr == rMat.GetString(rIndex-1))
4766 --rIndex;
4767 else
4768 while (rIndex < nMatCount-1 && rMat.IsStringOrEmpty(rIndex+1) &&
4769 aStr == rMat.GetString(rIndex+1))
4770 ++rIndex;
4771 }
4772 else
4773 {
4774 OSL_FAIL("lcl_GetLastMatch: unhandled matrix type")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
":" "4774" ": "), "%s", "lcl_GetLastMatch: unhandled matrix type"
); } } while (false)
;
4775 }
4776}
4777
4778}
4779
4780void ScInterpreter::ScMatch()
4781{
4782
4783 sal_uInt8 nParamCount = GetByte();
4784 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
4785 return;
4786
4787 double fTyp;
4788 if (nParamCount == 3)
4789 fTyp = GetDouble();
4790 else
4791 fTyp = 1.0;
4792 SCCOL nCol1 = 0;
4793 SCROW nRow1 = 0;
4794 SCTAB nTab1 = 0;
4795 SCCOL nCol2 = 0;
4796 SCROW nRow2 = 0;
4797 ScMatrixRef pMatSrc = nullptr;
4798
4799 switch (GetStackType())
4800 {
4801 case svSingleRef:
4802 PopSingleRef( nCol1, nRow1, nTab1);
4803 nCol2 = nCol1;
4804 nRow2 = nRow1;
4805 break;
4806 case svDoubleRef:
4807 {
4808 SCTAB nTab2 = 0;
4809 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4810 if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2))
4811 {
4812 PushIllegalParameter();
4813 return;
4814 }
4815 }
4816 break;
4817 case svMatrix:
4818 case svExternalDoubleRef:
4819 {
4820 if (GetStackType() == svMatrix)
4821 pMatSrc = PopMatrix();
4822 else
4823 PopExternalDoubleRef(pMatSrc);
4824
4825 if (!pMatSrc)
4826 {
4827 PushIllegalParameter();
4828 return;
4829 }
4830 }
4831 break;
4832 default:
4833 PushIllegalParameter();
4834 return;
4835 }
4836
4837 if (nGlobalError == FormulaError::NONE)
4838 {
4839 double fVal;
4840 ScQueryParam rParam;
4841 rParam.nCol1 = nCol1;
4842 rParam.nRow1 = nRow1;
4843 rParam.nCol2 = nCol2;
4844 rParam.nTab = nTab1;
4845
4846 ScQueryEntry& rEntry = rParam.GetEntry(0);
4847 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
4848 rEntry.bDoQuery = true;
4849 if (fTyp < 0.0)
4850 rEntry.eOp = SC_GREATER_EQUAL;
4851 else if (fTyp > 0.0)
4852 rEntry.eOp = SC_LESS_EQUAL;
4853 switch ( GetStackType() )
4854 {
4855 case svDouble:
4856 {
4857 fVal = GetDouble();
4858 rItem.mfVal = fVal;
4859 rItem.meType = ScQueryEntry::ByValue;
4860 }
4861 break;
4862 case svString:
4863 {
4864 rItem.meType = ScQueryEntry::ByString;
4865 rItem.maString = GetString();
4866 }
4867 break;
4868 case svDoubleRef :
4869 case svSingleRef :
4870 {
4871 ScAddress aAdr;
4872 if ( !PopDoubleRefOrSingleRef( aAdr ) )
4873 {
4874 PushInt(0);
4875 return ;
4876 }
4877 ScRefCellValue aCell(mrDoc, aAdr);
4878 if (aCell.hasNumeric())
4879 {
4880 fVal = GetCellValue(aAdr, aCell);
4881 rItem.meType = ScQueryEntry::ByValue;
4882 rItem.mfVal = fVal;
4883 }
4884 else
4885 {
4886 GetCellString(rItem.maString, aCell);
4887 rItem.meType = ScQueryEntry::ByString;
4888 }
4889 }
4890 break;
4891 case svExternalSingleRef:
4892 {
4893 ScExternalRefCache::TokenRef pToken;
4894 PopExternalSingleRef(pToken);
4895 if (nGlobalError != FormulaError::NONE)
4896 {
4897 PushError( nGlobalError);
4898 return;
4899 }
4900 if (pToken->GetType() == svDouble)
4901 {
4902 rItem.meType = ScQueryEntry::ByValue;
4903 rItem.mfVal = pToken->GetDouble();
4904 }
4905 else
4906 {
4907 rItem.meType = ScQueryEntry::ByString;
4908 rItem.maString = pToken->GetString();
4909 }
4910 }
4911 break;
4912 case svExternalDoubleRef:
4913 case svMatrix :
4914 {
4915 svl::SharedString aStr;
4916 ScMatValType nType = GetDoubleOrStringFromMatrix(
4917 rItem.mfVal, aStr);
4918 rItem.maString = aStr;
4919 rItem.meType = ScMatrix::IsNonValueType(nType) ?
4920 ScQueryEntry::ByString : ScQueryEntry::ByValue;
4921 }
4922 break;
4923 default:
4924 {
4925 PushIllegalParameter();
4926 return;
4927 }
4928 }
4929 if (rItem.meType == ScQueryEntry::ByString)
4930 {
4931 bool bIsVBAMode = mrDoc.IsInVBAMode();
4932
4933 if ( bIsVBAMode )
4934 rParam.eSearchType = utl::SearchParam::SearchType::Wildcard;
4935 else
4936 rParam.eSearchType = DetectSearchType(rEntry.GetQueryItem().maString.getString(), mrDoc);
4937 }
4938
4939 if (pMatSrc) // The source data is matrix array.
4940 {
4941 SCSIZE nC, nR;
4942 pMatSrc->GetDimensions( nC, nR);
4943 if (nC > 1 && nR > 1)
4944 {
4945 // The source matrix must be a vector.
4946 PushIllegalParameter();
4947 return;
4948 }
4949
4950 // Do not propagate errors from matrix while searching.
4951 pMatSrc->SetErrorInterpreter( nullptr);
4952
4953 SCSIZE nMatCount = (nC == 1) ? nR : nC;
4954 VectorMatrixAccessor aMatAcc(*pMatSrc, nC == 1);
4955
4956 // simple serial search for equality mode (source data doesn't
4957 // need to be sorted).
4958
4959 if (rEntry.eOp == SC_EQUAL)
4960 {
4961 for (SCSIZE i = 0; i < nMatCount; ++i)
4962 {
4963 if (lcl_CompareMatrix2Query( i, aMatAcc, rEntry) == 0)
4964 {
4965 PushDouble(i+1); // found !
4966 return;
4967 }
4968 }
4969 PushNA(); // not found
4970 return;
4971 }
4972
4973 // binary search for non-equality mode (the source data is
4974 // assumed to be sorted).
4975
4976 bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL);
4977 SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0;
4978 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
4979 {
4980 SCSIZE nMid = nFirst + nLen/2;
4981 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rEntry);
4982 if (nCmp == 0)
4983 {
4984 // exact match. find the last item with the same value.
4985 lcl_GetLastMatch( nMid, aMatAcc, nMatCount, false);
4986 PushDouble( nMid+1);
4987 return;
4988 }
4989
4990 if (nLen == 1) // first and last items are next to each other.
4991 {
4992 if (nCmp < 0)
4993 nHitIndex = bAscOrder ? nLast : nFirst;
4994 else
4995 nHitIndex = bAscOrder ? nFirst : nLast;
4996 break;
4997 }
4998
4999 if (nCmp < 0)
5000 {
5001 if (bAscOrder)
5002 nFirst = nMid;
5003 else
5004 nLast = nMid;
5005 }
5006 else
5007 {
5008 if (bAscOrder)
5009 nLast = nMid;
5010 else
5011 nFirst = nMid;
5012 }
5013 }
5014
5015 if (nHitIndex == nMatCount-1) // last item
5016 {
5017 sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, aMatAcc, rEntry);
5018 if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0))
5019 {
5020 // either the last item is an exact match or the real
5021 // hit is beyond the last item.
5022 PushDouble( nHitIndex+1);
5023 return;
5024 }
5025 }
5026
5027 if (nHitIndex > 0) // valid hit must be 2nd item or higher
5028 {
5029 PushDouble( nHitIndex); // non-exact match
5030 return;
5031 }
5032
5033 PushNA();
5034 return;
5035 }
5036
5037 SCCOLROW nDelta = 0;
5038 if (nCol1 == nCol2)
5039 { // search row in column
5040 rParam.nRow2 = nRow2;
5041 rEntry.nField = nCol1;
5042 ScAddress aResultPos( nCol1, nRow1, nTab1);
5043 if (!LookupQueryWithCache( aResultPos, rParam))
5044 {
5045 PushNA();
5046 return;
5047 }
5048 nDelta = aResultPos.Row() - nRow1;
5049 }
5050 else
5051 { // search column in row
5052 SCCOL nC;
5053 rParam.bByRow = false;
5054 rParam.nRow2 = nRow1;
5055 rEntry.nField = nCol1;
5056 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, false);
5057 // Advance Entry.nField in Iterator if column changed
5058 aCellIter.SetAdvanceQueryParamEntryField( true );
5059 if (fTyp == 0.0)
5060 { // EQUAL
5061 if ( aCellIter.GetFirst() )
5062 nC = aCellIter.GetCol();
5063 else
5064 {
5065 PushNA();
5066 return;
5067 }
5068 }
5069 else
5070 { // <= or >=
5071 SCROW nR;
5072 if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) )
5073 {
5074 PushNA();
5075 return;
5076 }
5077 }
5078 nDelta = nC - nCol1;
5079 }
5080 PushDouble(static_cast<double>(nDelta + 1));
5081 }
5082 else
5083 PushIllegalParameter();
5084}
5085
5086namespace {
5087
5088bool isCellContentEmpty( const ScRefCellValue& rCell )
5089{
5090 switch (rCell.meType)
5091 {
5092 case CELLTYPE_VALUE:
5093 case CELLTYPE_STRING:
5094 case CELLTYPE_EDIT:
5095 return false;
5096 case CELLTYPE_FORMULA:
5097 {
5098 // NOTE: Excel treats ="" in a referenced cell as blank in
5099 // COUNTBLANK() but not in ISBLANK(), which is inconsistent.
5100 // COUNTBLANK() tests the (display) result whereas ISBLANK() tests
5101 // the cell content.
5102 // ODFF allows both for COUNTBLANK().
5103 // OOo and LibreOffice prior to 4.4 did not treat ="" as blank in
5104 // COUNTBLANK(), we now do for Excel interoperability.
5105 /* TODO: introduce yet another compatibility option? */
5106 sc::FormulaResultValue aRes = rCell.mpFormula->GetResult();
5107 if (aRes.meType != sc::FormulaResultValue::String)
5108 return false;
5109 if (!aRes.maString.isEmpty())
5110 return false;
5111 }
5112 break;
5113 default:
5114 ;
5115 }
5116
5117 return true;
5118}
5119
5120}
5121
5122void ScInterpreter::ScCountEmptyCells()
5123{
5124 if ( !MustHaveParamCount( GetByte(), 1 ) )
5125 return;
5126
5127 const SCSIZE nMatRows = GetRefListArrayMaxSize(1);
5128 // There's either one RefList and nothing else, or none.
5129 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows) : nullptr);
5130 sal_uLong nMaxCount = 0, nCount = 0;
5131 switch (GetStackType())
5132 {
5133 case svSingleRef :
5134 {
5135 nMaxCount = 1;
5136 ScAddress aAdr;
5137 PopSingleRef( aAdr );
5138 ScRefCellValue aCell(mrDoc, aAdr);
5139 if (!isCellContentEmpty(aCell))
5140 nCount = 1;
5141 }
5142 break;
5143 case svRefList :
5144 case svDoubleRef :
5145 {
5146 ScRange aRange;
5147 short nParam = 1;
5148 SCSIZE nRefListArrayPos = 0;
5149 size_t nRefInList = 0;
5150 while (nParam-- > 0)
5151 {
5152 nRefListArrayPos = nRefInList;
5153 PopDoubleRef( aRange, nParam, nRefInList);
5154 nMaxCount +=
5155 static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) *
5156 static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) *
5157 static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1);
5158
5159 ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags);
5160 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
5161 {
5162 const ScRefCellValue& rCell = aIter.getRefCellValue();
5163 if (!isCellContentEmpty(rCell))
5164 ++nCount;
5165 }
5166 if (xResMat)
5167 {
5168 xResMat->PutDouble( nMaxCount - nCount, 0, nRefListArrayPos);
5169 nMaxCount = nCount = 0;
5170 }
5171 }
5172 }
5173 break;
5174 case svMatrix:
5175 case svExternalSingleRef:
5176 case svExternalDoubleRef:
5177 {
5178 ScMatrixRef xMat = GetMatrix();
5179 if (!xMat)
5180 SetError( FormulaError::IllegalParameter);
5181 else
5182 {
5183 SCSIZE nC, nR;
5184 xMat->GetDimensions( nC, nR);
5185 nMaxCount = nC * nR;
5186 // Numbers (implicit), strings and error values, ignore empty
5187 // strings as those if not entered in an inline array are the
5188 // result of a formula, to be par with a reference to formula
5189 // cell as *visual* blank, see isCellContentEmpty() above.
5190 nCount = xMat->Count( true, true, true);
5191 }
5192 }
5193 break;
5194 default : SetError(FormulaError::IllegalParameter); break;
5195 }
5196 if (xResMat)
5197 PushMatrix( xResMat);
5198 else
5199 PushDouble(nMaxCount - nCount);
5200}
5201
5202void ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc )
5203{
5204 sal_uInt8 nParamCount = GetByte();
5205 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
5206 return;
5207
5208 SCCOL nCol3 = 0;
5209 SCROW nRow3 = 0;
5210 SCTAB nTab3 = 0;
5211
5212 ScMatrixRef pSumExtraMatrix;
5213 bool bSumExtraRange = (nParamCount == 3);
5214 if (bSumExtraRange)
5215 {
5216 // Save only the upperleft cell in case of cell range. The geometry
5217 // of the 3rd parameter is taken from the 1st parameter.
5218
5219 switch ( GetStackType() )
5220 {
5221 case svDoubleRef :
5222 {
5223 SCCOL nColJunk = 0;
5224 SCROW nRowJunk = 0;
5225 SCTAB nTabJunk = 0;
5226 PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk );
5227 if ( nTabJunk != nTab3 )
5228 {
5229 PushError( FormulaError::IllegalParameter);
5230 return;
5231 }
5232 }
5233 break;
5234 case svSingleRef :
5235 PopSingleRef( nCol3, nRow3, nTab3 );
5236 break;
5237 case svMatrix:
5238 pSumExtraMatrix = PopMatrix();
5239 // nCol3, nRow3, nTab3 remain 0
5240 break;
5241 case svExternalSingleRef:
5242 {
5243 pSumExtraMatrix = GetNewMat(1,1);
5244 ScExternalRefCache::TokenRef pToken;
5245 PopExternalSingleRef(pToken);
5246 if (nGlobalError != FormulaError::NONE)
5247 {
5248 PushError( nGlobalError);
5249 return;
5250 }
5251
5252 if (pToken->GetType() == svDouble)
5253 pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0);
5254 else
5255 pSumExtraMatrix->PutString(pToken->GetString(), 0, 0);
5256 }
5257 break;
5258 case svExternalDoubleRef:
5259 PopExternalDoubleRef(pSumExtraMatrix);
5260 break;
5261 default:
5262 PushError( FormulaError::IllegalParameter);
5263 return;
5264 }
5265 }
5266
5267 svl::SharedString aString;
5268 double fVal = 0.0;
5269 bool bIsString = true;
5270 switch ( GetStackType() )
5271 {
5272 case svDoubleRef :
5273 case svSingleRef :
5274 {
5275 ScAddress aAdr;
5276 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5277 {
5278 PushError( nGlobalError);
5279 return;
5280 }
5281
5282 ScRefCellValue aCell(mrDoc, aAdr);
5283 switch (aCell.meType)
5284 {
5285 case CELLTYPE_VALUE :
5286 fVal = GetCellValue(aAdr, aCell);
5287 bIsString = false;
5288 break;
5289 case CELLTYPE_FORMULA :
5290 if (aCell.mpFormula->IsValue())
5291 {
5292 fVal = GetCellValue(aAdr, aCell);
5293 bIsString = false;
5294 }
5295 else
5296 GetCellString(aString, aCell);
5297 break;
5298 case CELLTYPE_STRING :
5299 case CELLTYPE_EDIT :
5300 GetCellString(aString, aCell);
5301 break;
5302 default:
5303 fVal = 0.0;
5304 bIsString = false;
5305 }
5306 }
5307 break;
5308 case svString:
5309 aString = GetString();
5310 break;
5311 case svMatrix :
5312 case svExternalDoubleRef:
5313 {
5314 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
5315 bIsString = ScMatrix::IsRealStringType( nType);
5316 }
5317 break;
5318 case svExternalSingleRef:
5319 {
5320 ScExternalRefCache::TokenRef pToken;
5321 PopExternalSingleRef(pToken);
5322 if (nGlobalError == FormulaError::NONE)
5323 {
5324 if (pToken->GetType() == svDouble)
5325 {
5326 fVal = pToken->GetDouble();
5327 bIsString = false;
5328 }
5329 else
5330 aString = pToken->GetString();
5331 }
5332 }
5333 break;
5334 default:
5335 {
5336 fVal = GetDouble();
5337 bIsString = false;
5338 }
5339 }
5340
5341 double fSum = 0.0;
5342 double fMem = 0.0;
5343 double fRes = 0.0;
5344 double fCount = 0.0;
5345 bool bNull = true;
5346 short nParam = 1;
5347 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
5348 // There's either one RefList and nothing else, or none.
5349 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows) : nullptr);
5350 SCSIZE nRefListArrayPos = 0;
5351 size_t nRefInList = 0;
5352 while (nParam-- > 0)
5353 {
5354 SCCOL nCol1 = 0;
5355 SCROW nRow1 = 0;
5356 SCTAB nTab1 = 0;
5357 SCCOL nCol2 = 0;
5358 SCROW nRow2 = 0;
5359 SCTAB nTab2 = 0;
5360 ScMatrixRef pQueryMatrix;
5361 switch ( GetStackType() )
5362 {
5363 case svRefList :
5364 if (bSumExtraRange)
5365 {
5366 /* TODO: this could resolve if all refs are of the same size */
5367 SetError( FormulaError::IllegalParameter);
5368 }
5369 else
5370 {
5371 nRefListArrayPos = nRefInList;
5372 ScRange aRange;
5373 PopDoubleRef( aRange, nParam, nRefInList);
5374 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5375 }
5376 break;
5377 case svDoubleRef :
5378 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
5379 break;
5380 case svSingleRef :
5381 PopSingleRef( nCol1, nRow1, nTab1 );
5382 nCol2 = nCol1;
5383 nRow2 = nRow1;
5384 nTab2 = nTab1;
5385 break;
5386 case svMatrix:
5387 case svExternalSingleRef:
5388 case svExternalDoubleRef:
5389 {
5390 pQueryMatrix = GetMatrix();
5391 if (!pQueryMatrix)
5392 {
5393 PushError( FormulaError::IllegalParameter);
5394 return;
5395 }
5396 nCol1 = 0;
5397 nRow1 = 0;
5398 nTab1 = 0;
5399 SCSIZE nC, nR;
5400 pQueryMatrix->GetDimensions( nC, nR);
5401 nCol2 = static_cast<SCCOL>(nC - 1);
5402 nRow2 = static_cast<SCROW>(nR - 1);
5403 nTab2 = 0;
5404 }
5405 break;
5406 default:
5407 SetError( FormulaError::IllegalParameter);
5408 }
5409 if ( nTab1 != nTab2 )
5410 {
5411 SetError( FormulaError::IllegalParameter);
5412 }
5413
5414 if (bSumExtraRange)
5415 {
5416 // Take the range geometry of the 1st parameter and apply it to
5417 // the 3rd. If parts of the resulting range would point outside
5418 // the sheet, don't complain but silently ignore and simply cut
5419 // them away, this is what Xcl does :-/
5420
5421 // For the cut-away part we also don't need to determine the
5422 // criteria match, so shrink the source range accordingly,
5423 // instead of the result range.
5424 SCCOL nColDelta = nCol2 - nCol1;
5425 SCROW nRowDelta = nRow2 - nRow1;
5426 SCCOL nMaxCol;
5427 SCROW nMaxRow;
5428 if (pSumExtraMatrix)
5429 {
5430 SCSIZE nC, nR;
5431 pSumExtraMatrix->GetDimensions( nC, nR);
5432 nMaxCol = static_cast<SCCOL>(nC - 1);
5433 nMaxRow = static_cast<SCROW>(nR - 1);
5434 }
5435 else
5436 {
5437 nMaxCol = mrDoc.MaxCol();
5438 nMaxRow = mrDoc.MaxRow();
5439 }
5440 if (nCol3 + nColDelta > nMaxCol)
5441 {
5442 SCCOL nNewDelta = nMaxCol - nCol3;
5443 nCol2 = nCol1 + nNewDelta;
5444 }
5445
5446 if (nRow3 + nRowDelta > nMaxRow)
5447 {
5448 SCROW nNewDelta = nMaxRow - nRow3;
5449 nRow2 = nRow1 + nNewDelta;
5450 }
5451 }
5452 else
5453 {
5454 nCol3 = nCol1;
5455 nRow3 = nRow1;
5456 nTab3 = nTab1;
5457 }
5458
5459 if (nGlobalError == FormulaError::NONE)
5460 {
5461 ScQueryParam rParam;
5462 rParam.nRow1 = nRow1;
5463 rParam.nRow2 = nRow2;
5464
5465 ScQueryEntry& rEntry = rParam.GetEntry(0);
5466 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5467 rEntry.bDoQuery = true;
5468 if (!bIsString)
5469 {
5470 rItem.meType = ScQueryEntry::ByValue;
5471 rItem.mfVal = fVal;
5472 rEntry.eOp = SC_EQUAL;
5473 }
5474 else
5475 {
5476 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
5477 if (rItem.meType == ScQueryEntry::ByString)
5478 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
5479 }
5480 ScAddress aAdr;
5481 aAdr.SetTab( nTab3 );
5482 rParam.nCol1 = nCol1;
5483 rParam.nCol2 = nCol2;
5484 rEntry.nField = nCol1;
5485 SCCOL nColDiff = nCol3 - nCol1;
5486 SCROW nRowDiff = nRow3 - nRow1;
5487 if (pQueryMatrix)
5488 {
5489 // Never case-sensitive.
5490 sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
5491 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5492 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
5493 {
5494 SetError( FormulaError::IllegalParameter);
5495 }
5496
5497 if (pSumExtraMatrix)
5498 {
5499 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5500 {
5501 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5502 {
5503 if (pResultMatrix->IsValue( nCol, nRow) &&
5504 pResultMatrix->GetDouble( nCol, nRow))
5505 {
5506 SCSIZE nC = nCol + nColDiff;
5507 SCSIZE nR = nRow + nRowDiff;
5508 if (pSumExtraMatrix->IsValue( nC, nR))
5509 {
5510 fVal = pSumExtraMatrix->GetDouble( nC, nR);
5511 ++fCount;
5512 if ( bNull && fVal != 0.0 )
5513 {
5514 bNull = false;
5515 fMem = fVal;
5516 }
5517 else
5518 fSum += fVal;
5519 }
5520 }
5521 }
5522 }
5523 }
5524 else
5525 {
5526 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5527 {
5528 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5529 {
5530 if (pResultMatrix->GetDouble( nCol, nRow))
5531 {
5532 aAdr.SetCol( nCol + nColDiff);
5533 aAdr.SetRow( nRow + nRowDiff);
5534 ScRefCellValue aCell(mrDoc, aAdr);
5535 if (aCell.hasNumeric())
5536 {
5537 fVal = GetCellValue(aAdr, aCell);
5538 ++fCount;
5539 if ( bNull && fVal != 0.0 )
5540 {
5541 bNull = false;
5542 fMem = fVal;
5543 }
5544 else
5545 fSum += fVal;
5546 }
5547 }
5548 }
5549 }
5550 }
5551 }
5552 else
5553 {
5554 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, false);
5555 // Increment Entry.nField in iterator when switching to next column.
5556 aCellIter.SetAdvanceQueryParamEntryField( true );
5557 if ( aCellIter.GetFirst() )
5558 {
5559 if (pSumExtraMatrix)
5560 {
5561 do
5562 {
5563 SCSIZE nC = aCellIter.GetCol() + nColDiff;
5564 SCSIZE nR = aCellIter.GetRow() + nRowDiff;
5565 if (pSumExtraMatrix->IsValue( nC, nR))
5566 {
5567 fVal = pSumExtraMatrix->GetDouble( nC, nR);
5568 ++fCount;
5569 if ( bNull && fVal != 0.0 )
5570 {
5571 bNull = false;
5572 fMem = fVal;
5573 }
5574 else
5575 fSum += fVal;
5576 }
5577 } while ( aCellIter.GetNext() );
5578 }
5579 else
5580 {
5581 do
5582 {
5583 aAdr.SetCol( aCellIter.GetCol() + nColDiff);
5584 aAdr.SetRow( aCellIter.GetRow() + nRowDiff);
5585 ScRefCellValue aCell(mrDoc, aAdr);
5586 if (aCell.hasNumeric())
5587 {
5588 fVal = GetCellValue(aAdr, aCell);
5589 ++fCount;
5590 if ( bNull && fVal != 0.0 )
5591 {
5592 bNull = false;
5593 fMem = fVal;
5594 }
5595 else
5596 fSum += fVal;
5597 }
5598 } while ( aCellIter.GetNext() );
5599 }
5600 }
5601 }
5602 }
5603 else
5604 {
5605 PushError( FormulaError::IllegalParameter);
5606 return;
5607 }
5608
5609 switch( eFunc )
5610 {
5611 case ifSUMIF: fRes = ::rtl::math::approxAdd( fSum, fMem ); break;
5612 case ifAVERAGEIF: fRes = div( ::rtl::math::approxAdd( fSum, fMem ), fCount); break;
5613 }
5614 if (xResMat)
5615 {
5616 if (nGlobalError == FormulaError::NONE)
5617 xResMat->PutDouble( fRes, 0, nRefListArrayPos);
5618 else
5619 {
5620 xResMat->PutError( nGlobalError, 0, nRefListArrayPos);
5621 nGlobalError = FormulaError::NONE;
5622 }
5623 fRes = fSum = fMem = fCount = 0.0;
5624 }
5625 }
5626 if (xResMat)
5627 PushMatrix( xResMat);
5628 else
5629 PushDouble( fRes);
5630}
5631
5632void ScInterpreter::ScSumIf()
5633{
5634 IterateParametersIf( ifSUMIF);
5635}
5636
5637void ScInterpreter::ScAverageIf()
5638{
5639 IterateParametersIf( ifAVERAGEIF);
5640}
5641
5642void ScInterpreter::ScCountIf()
5643{
5644 if ( !MustHaveParamCount( GetByte(), 2 ) )
5645 return;
5646
5647 svl::SharedString aString;
5648 double fVal = 0.0;
5649 bool bIsString = true;
5650 switch ( GetStackType() )
5651 {
5652 case svDoubleRef :
5653 case svSingleRef :
5654 {
5655 ScAddress aAdr;
5656 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5657 {
5658 PushInt(0);
5659 return ;
5660 }
5661 ScRefCellValue aCell(mrDoc, aAdr);
5662 switch (aCell.meType)
5663 {
5664 case CELLTYPE_VALUE :
5665 fVal = GetCellValue(aAdr, aCell);
5666 bIsString = false;
5667 break;
5668 case CELLTYPE_FORMULA :
5669 if (aCell.mpFormula->IsValue())
5670 {
5671 fVal = GetCellValue(aAdr, aCell);
5672 bIsString = false;
5673 }
5674 else
5675 GetCellString(aString, aCell);
5676 break;
5677 case CELLTYPE_STRING :
5678 case CELLTYPE_EDIT :
5679 GetCellString(aString, aCell);
5680 break;
5681 default:
5682 fVal = 0.0;
5683 bIsString = false;
5684 }
5685 }
5686 break;
5687 case svMatrix:
5688 case svExternalSingleRef:
5689 case svExternalDoubleRef:
5690 {
5691 ScMatValType nType = GetDoubleOrStringFromMatrix(fVal, aString);
5692 bIsString = ScMatrix::IsRealStringType( nType);
5693 }
5694 break;
5695 case svString:
5696 aString = GetString();
5697 break;
5698 default:
5699 {
5700 fVal = GetDouble();
5701 bIsString = false;
5702 }
5703 }
5704 double fCount = 0.0;
5705 short nParam = 1;
5706 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
5707 // There's either one RefList and nothing else, or none.
5708 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows) : nullptr);
5709 SCSIZE nRefListArrayPos = 0;
5710 size_t nRefInList = 0;
5711 while (nParam-- > 0)
5712 {
5713 SCCOL nCol1 = 0;
5714 SCROW nRow1 = 0;
5715 SCTAB nTab1 = 0;
5716 SCCOL nCol2 = 0;
5717 SCROW nRow2 = 0;
5718 SCTAB nTab2 = 0;
5719 ScMatrixRef pQueryMatrix;
5720 switch ( GetStackType() )
5721 {
5722 case svRefList :
5723 nRefListArrayPos = nRefInList;
5724 [[fallthrough]];
5725 case svDoubleRef :
5726 {
5727 ScRange aRange;
5728 PopDoubleRef( aRange, nParam, nRefInList);
5729 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5730 }
5731 break;
5732 case svSingleRef :
5733 PopSingleRef( nCol1, nRow1, nTab1 );
5734 nCol2 = nCol1;
5735 nRow2 = nRow1;
5736 nTab2 = nTab1;
5737 break;
5738 case svMatrix:
5739 case svExternalSingleRef:
5740 case svExternalDoubleRef:
5741 {
5742 pQueryMatrix = GetMatrix();
5743 if (!pQueryMatrix)
5744 {
5745 PushIllegalParameter();
5746 return;
5747 }
5748 nCol1 = 0;
5749 nRow1 = 0;
5750 nTab1 = 0;
5751 SCSIZE nC, nR;
5752 pQueryMatrix->GetDimensions( nC, nR);
5753 nCol2 = static_cast<SCCOL>(nC - 1);
5754 nRow2 = static_cast<SCROW>(nR - 1);
5755 nTab2 = 0;
5756 }
5757 break;
5758 default:
5759 PopError(); // Propagate it further
5760 PushIllegalParameter();
5761 return ;
5762 }
5763 if ( nTab1 != nTab2 )
5764 {
5765 PushIllegalParameter();
5766 return;
5767 }
5768 if (nCol1 > nCol2)
5769 {
5770 PushIllegalParameter();
5771 return;
5772 }
5773 if (nGlobalError == FormulaError::NONE)
5774 {
5775 ScQueryParam rParam;
5776 rParam.nRow1 = nRow1;
5777 rParam.nRow2 = nRow2;
5778
5779 ScQueryEntry& rEntry = rParam.GetEntry(0);
5780 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5781 rEntry.bDoQuery = true;
5782 if (!bIsString)
5783 {
5784 rItem.meType = ScQueryEntry::ByValue;
5785 rItem.mfVal = fVal;
5786 rEntry.eOp = SC_EQUAL;
5787 }
5788 else
5789 {
5790 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
5791 if (rItem.meType == ScQueryEntry::ByString)
5792 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
5793 }
5794 rParam.nCol1 = nCol1;
5795 rParam.nCol2 = nCol2;
5796 rEntry.nField = nCol1;
5797 if (pQueryMatrix)
5798 {
5799 // Never case-sensitive.
5800 sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
5801 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5802 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
5803 {
5804 PushIllegalParameter();
5805 return;
5806 }
5807
5808 SCSIZE nSize = pResultMatrix->GetElementCount();
5809 for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex)
5810 {
5811 if (pResultMatrix->IsValue( nIndex) &&
5812 pResultMatrix->GetDouble( nIndex))
5813 ++fCount;
5814 }
5815 }
5816 else
5817 {
5818 ScCountIfCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam);
5819 fCount += aCellIter.GetCount();
5820 }
5821 }
5822 else
5823 {
5824 PushIllegalParameter();
5825 return;
5826 }
5827 if (xResMat)
5828 {
5829 xResMat->PutDouble( fCount, 0, nRefListArrayPos);
5830 fCount = 0.0;
5831 }
5832 }
5833 if (xResMat)
5834 PushMatrix( xResMat);
5835 else
5836 PushDouble(fCount);
5837}
5838
5839void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIfsResult& rRes ) )
5840{
5841 sal_uInt8 nParamCount = GetByte();
5842 sal_uInt8 nQueryCount = nParamCount / 2;
5843
5844 std::vector<sal_uInt32>& vConditions = mrContext.maConditions;
5845 // vConditions is cached, although it is clear'ed after every cell is interpreted,
5846 // if the SUMIFS/COUNTIFS are part of a matrix formula, then that is not enough because
5847 // with a single InterpretTail() call it results in evaluation of all the cells in the
5848 // matrix formula.
5849 vConditions.clear();
5850
5851 SCCOL nStartColDiff = 0;
5852 SCCOL nEndColDiff = 0;
5853 SCROW nStartRowDiff = 0;
5854 SCROW nEndRowDiff = 0;
5855 bool bRangeReduce = false;
5856 ScRange aMainRange;
5857
5858 // Range-reduce optimization
5859 if (nParamCount % 2) // Not COUNTIFS
5860 {
5861 bool bHasDoubleRefCriteriaRanges = true;
5862 // Do not attempt main-range reduce if any of the criteria-ranges are not double-refs.
5863 for (sal_uInt16 nParamIdx = 2; nParamIdx < nParamCount; nParamIdx += 2 )
5864 {
5865 const formula::FormulaToken* pCriteriaRangeToken = pStack[ sp-nParamIdx ];
5866 if (pCriteriaRangeToken->GetType() != svDoubleRef )
5867 {
5868 bHasDoubleRefCriteriaRanges = false;
5869 break;
5870 }
5871 }
5872
5873 // Probe the main range token, and try if we can shrink the range without altering results.
5874 const formula::FormulaToken* pMainRangeToken = pStack[ sp-nParamCount ];
5875 if (pMainRangeToken->GetType() == svDoubleRef && bHasDoubleRefCriteriaRanges)
5876 {
5877 const ScComplexRefData* pRefData = pMainRangeToken->GetDoubleRef();
5878 if (!pRefData->IsDeleted())
5879 {
5880 DoubleRefToRange( *pRefData, aMainRange);
5881
5882 if (aMainRange.aStart.Tab() == aMainRange.aEnd.Tab())
5883 {
5884 // Shrink the range to actual data content.
5885 ScRange aSubRange = aMainRange;
5886 mrDoc.GetDataAreaSubrange(aSubRange);
5887
5888 nStartColDiff = aSubRange.aStart.Col() - aMainRange.aStart.Col();
5889 nStartRowDiff = aSubRange.aStart.Row() - aMainRange.aStart.Row();
5890
5891 nEndColDiff = aSubRange.aEnd.Col() - aMainRange.aEnd.Col();
5892 nEndRowDiff = aSubRange.aEnd.Row() - aMainRange.aEnd.Row();
5893 bRangeReduce = nStartColDiff || nStartRowDiff || nEndColDiff || nEndRowDiff;
5894 }
5895 }
5896 }
5897 }
5898
5899 double fVal = 0.0;
5900 SCCOL nDimensionCols = 0;
5901 SCROW nDimensionRows = 0;
5902 const SCSIZE nRefArrayRows = GetRefListArrayMaxSize( nParamCount);
5903 std::vector<std::vector<sal_uInt32>> vRefArrayConditions;
5904
5905 while (nParamCount > 1 && nGlobalError == FormulaError::NONE)
5906 {
5907 // take criteria
5908 svl::SharedString aString;
5909 fVal = 0.0;
5910 bool bIsString = true;
5911 switch ( GetStackType() )
5912 {
5913 case svDoubleRef :
5914 case svSingleRef :
5915 {
5916 ScAddress aAdr;
5917 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5918 {
5919 PushError( nGlobalError);
5920 return;
5921 }
5922
5923 ScRefCellValue aCell(mrDoc, aAdr);
5924 switch (aCell.meType)
5925 {
5926 case CELLTYPE_VALUE :
5927 fVal = GetCellValue(aAdr, aCell);
5928 bIsString = false;
5929 break;
5930 case CELLTYPE_FORMULA :
5931 if (aCell.mpFormula->IsValue())
5932 {
5933 fVal = GetCellValue(aAdr, aCell);
5934 bIsString = false;
5935 }
5936 else
5937 GetCellString(aString, aCell);
5938 break;
5939 case CELLTYPE_STRING :
5940 case CELLTYPE_EDIT :
5941 GetCellString(aString, aCell);
5942 break;
5943 default:
5944 fVal = 0.0;
5945 bIsString = false;
5946 }
5947 }
5948 break;
5949 case svString:
5950 aString = GetString();
5951 break;
5952 case svMatrix :
5953 case svExternalDoubleRef:
5954 {
5955 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
5956 bIsString = ScMatrix::IsRealStringType( nType);
5957 }
5958 break;
5959 case svExternalSingleRef:
5960 {
5961 ScExternalRefCache::TokenRef pToken;
5962 PopExternalSingleRef(pToken);
5963 if (nGlobalError == FormulaError::NONE)
5964 {
5965 if (pToken->GetType() == svDouble)
5966 {
5967 fVal = pToken->GetDouble();
5968 bIsString = false;
5969 }
5970 else
5971 aString = pToken->GetString();
5972 }
5973 }
5974 break;
5975 default:
5976 {
5977 fVal = GetDouble();
5978 bIsString = false;
5979 }
5980 }
5981
5982 if (nGlobalError != FormulaError::NONE)
5983 {
5984 PushError( nGlobalError);
5985 return; // and bail out, no need to evaluate other arguments
5986 }
5987
5988 // take range
5989 short nParam = nParamCount;
5990 size_t nRefInList = 0;
5991 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
5992 SCCOL nCol1 = 0;
5993 SCROW nRow1 = 0;
5994 SCTAB nTab1 = 0;
5995 SCCOL nCol2 = 0;
5996 SCROW nRow2 = 0;
5997 SCTAB nTab2 = 0;
5998 ScMatrixRef pQueryMatrix;
5999 while (nParam-- == nParamCount)
6000 {
6001 switch ( GetStackType() )
6002 {
6003 case svRefList :
6004 {
6005 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
6006 if (p && p->IsArrayResult())
6007 {
6008 if (nRefInList == 0)
6009 {
6010 if (vRefArrayConditions.empty())
6011 vRefArrayConditions.resize( nRefArrayRows);
6012 if (!vConditions.empty())
6013 {
6014 // Similar to other reference list array
6015 // handling, add/op the current value to
6016 // all array positions.
6017 for (auto & rVec : vRefArrayConditions)
6018 {
6019 if (rVec.empty())
6020 rVec = vConditions;
6021 else
6022 {
6023 assert(rVec.size() == vConditions.size())(static_cast <bool> (rVec.size() == vConditions.size())
? void (0) : __assert_fail ("rVec.size() == vConditions.size()"
, "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
, 6023, __extension__ __PRETTY_FUNCTION__))
; // see dimensions below
6024 for (size_t i=0, n = rVec.size(); i < n; ++i)
6025 {
6026 rVec[i] += vConditions[i];
6027 }
6028 }
6029 }
6030 // Reset condition results.
6031 std::for_each( vConditions.begin(), vConditions.end(),
6032 [](sal_uInt32 & r){ r = 0.0; } );
6033 }
6034 }
6035 nRefArrayPos = nRefInList;
6036 }
6037 ScRange aRange;
6038 PopDoubleRef( aRange, nParam, nRefInList);
6039 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
6040 }
6041 break;
6042 case svDoubleRef :
6043 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
6044 break;
6045 case svSingleRef :
6046 PopSingleRef( nCol1, nRow1, nTab1 );
6047 nCol2 = nCol1;
6048 nRow2 = nRow1;
6049 nTab2 = nTab1;
6050 break;
6051 case svMatrix:
6052 case svExternalSingleRef:
6053 case svExternalDoubleRef:
6054 {
6055 pQueryMatrix = GetMatrix();
6056 if (!pQueryMatrix)
6057 {
6058 PushError( FormulaError::IllegalParameter);
6059 return;
6060 }
6061 nCol1 = 0;
6062 nRow1 = 0;
6063 nTab1 = 0;
6064 SCSIZE nC, nR;
6065 pQueryMatrix->GetDimensions( nC, nR);
6066 nCol2 = static_cast<SCCOL>(nC - 1);
6067 nRow2 = static_cast<SCROW>(nR - 1);
6068 nTab2 = 0;
6069 }
6070 break;
6071 default:
6072 PushError( FormulaError::IllegalParameter);
6073 return;
6074 }
6075 if ( nTab1 != nTab2 )
6076 {
6077 PushError( FormulaError::IllegalArgument);
6078 return;
6079 }
6080
6081 if (bRangeReduce)
6082 {
6083 // All reference ranges must be of the same size as the main range.
6084 if( aMainRange.aEnd.Col() - aMainRange.aStart.Col() != nCol2 - nCol1
6085 || aMainRange.aEnd.Row() - aMainRange.aStart.Row() != nRow2 - nRow1)
6086 {
6087 PushError ( FormulaError::IllegalArgument);
6088 return;
6089 }
6090 nCol1 += nStartColDiff;
6091 nRow1 += nStartRowDiff;
6092
6093 nCol2 += nEndColDiff;
6094 nRow2 += nEndRowDiff;
6095 }
6096
6097 // All reference ranges must be of same dimension and size.
6098 if (!nDimensionCols)
6099 nDimensionCols = nCol2 - nCol1 + 1;
6100 if (!nDimensionRows)
6101 nDimensionRows = nRow2 - nRow1 + 1;
6102 if ((nDimensionCols != (nCol2 - nCol1 + 1)) || (nDimensionRows != (nRow2 - nRow1 + 1)))
6103 {
6104 PushError ( FormulaError::IllegalArgument);
6105 return;
6106 }
6107
6108 // recalculate matrix values
6109 if (nGlobalError != FormulaError::NONE)
6110 {
6111 PushError( nGlobalError);
6112 return;
6113 }
6114
6115 // initialize temporary result matrix
6116 if (vConditions.empty())
6117 vConditions.resize( nDimensionCols * nDimensionRows, 0);
6118
6119 ScQueryParam rParam;
6120 rParam.nRow1 = nRow1;
6121 rParam.nRow2 = nRow2;
6122
6123 ScQueryEntry& rEntry = rParam.GetEntry(0);
6124 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6125 rEntry.bDoQuery = true;
6126 if (!bIsString)
6127 {
6128 rItem.meType = ScQueryEntry::ByValue;
6129 rItem.mfVal = fVal;
6130 rEntry.eOp = SC_EQUAL;
6131 }
6132 else
6133 {
6134 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
6135 if (rItem.meType == ScQueryEntry::ByString)
6136 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
6137 }
6138 rParam.nCol1 = nCol1;
6139 rParam.nCol2 = nCol2;
6140 rEntry.nField = nCol1;
6141 SCCOL nColDiff = -nCol1;
6142 SCROW nRowDiff = -nRow1;
6143 if (pQueryMatrix)
6144 {
6145 // Never case-sensitive.
6146 sc::CompareOptions aOptions(mrDoc, rEntry, rParam.eSearchType);
6147 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
6148 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
6149 {
6150 PushError( FormulaError::IllegalParameter);
6151 return;
6152 }
6153
6154 // result matrix is filled with boolean values.
6155 std::vector<double> aResValues;
6156 pResultMatrix->GetDoubleArray(aResValues);
6157 if (vConditions.size() != aResValues.size())
6158 {
6159 PushError( FormulaError::IllegalParameter);
6160 return;
6161 }
6162
6163 std::vector<double>::const_iterator itThisRes = aResValues.begin();
6164 for (auto& rCondition : vConditions)
6165 {
6166 rCondition += *itThisRes;
6167 ++itThisRes;
6168 }
6169 }
6170 else
6171 {
6172 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, false);
6173 // Increment Entry.nField in iterator when switching to next column.
6174 aCellIter.SetAdvanceQueryParamEntryField( true );
6175 if ( aCellIter.GetFirst() )
6176 {
6177 do
6178 {
6179 size_t nC = aCellIter.GetCol() + nColDiff;
6180 size_t nR = aCellIter.GetRow() + nRowDiff;
6181 ++vConditions[nC * nDimensionRows + nR];
6182 } while ( aCellIter.GetNext() );
6183 }
6184 }
6185 if (nRefArrayPos != std::numeric_limits<size_t>::max())
6186 {
6187 // Apply condition result to reference list array result position.
6188 std::vector<sal_uInt32>& rVec = vRefArrayConditions[nRefArrayPos];
6189 if (rVec.empty())
6190 rVec = vConditions;
6191 else
6192 {
6193 assert(rVec.size() == vConditions.size())(static_cast <bool> (rVec.size() == vConditions.size())
? void (0) : __assert_fail ("rVec.size() == vConditions.size()"
, "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
, 6193, __extension__ __PRETTY_FUNCTION__))
; // see dimensions above
6194 for (size_t i=0, n = rVec.size(); i < n; ++i)
6195 {
6196 rVec[i] += vConditions[i];
6197 }
6198 }
6199 // Reset conditions vector.
6200 // When leaving an svRefList this has to be emptied not set to
6201 // 0.0 because it's checked when entering an svRefList.
6202 if (nRefInList == 0)
6203 std::vector<sal_uInt32>().swap( vConditions);
6204 else
6205 std::for_each( vConditions.begin(), vConditions.end(), [](sal_uInt32 & r){ r = 0.0; } );
6206 }
6207 }
6208 nParamCount -= 2;
6209 }
6210
6211 if (!vRefArrayConditions.empty() && !vConditions.empty())
6212 {
6213 // Add/op the last current value to all array positions.
6214 for (auto & rVec : vRefArrayConditions)
6215 {
6216 if (rVec.empty())
6217 rVec = vConditions;
6218 else
6219 {
6220 assert(rVec.size() == vConditions.size())(static_cast <bool> (rVec.size() == vConditions.size())
? void (0) : __assert_fail ("rVec.size() == vConditions.size()"
, "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
, 6220, __extension__ __PRETTY_FUNCTION__))
; // see dimensions above
6221 for (size_t i=0, n = rVec.size(); i < n; ++i)
6222 {
6223 rVec[i] += vConditions[i];
6224 }
6225 }
6226 }
6227 }
6228
6229 if (nGlobalError != FormulaError::NONE)
6230 {
6231 PushError( nGlobalError);
6232 return; // bail out
6233 }
6234
6235 sc::ParamIfsResult aRes;
6236 ScMatrixRef xResMat;
6237
6238 // main range - only for AVERAGEIFS, SUMIFS, MINIFS and MAXIFS
6239 if (nParamCount == 1)
6240 {
6241 short nParam = nParamCount;
6242 size_t nRefInList = 0;
6243 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
6244 bool bRefArrayMain = false;
6245 while (nParam-- == nParamCount)
6246 {
6247 bool bNull = true;
6248 SCCOL nMainCol1 = 0;
6249 SCROW nMainRow1 = 0;
6250 SCTAB nMainTab1 = 0;
6251 SCCOL nMainCol2 = 0;
6252 SCROW nMainRow2 = 0;
6253 SCTAB nMainTab2 = 0;
6254 ScMatrixRef pMainMatrix;
6255 switch ( GetStackType() )
6256 {
6257 case svRefList :
6258 {
6259 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
6260 if (p && p->IsArrayResult())
6261 {
6262 if (vRefArrayConditions.empty())
6263 {
6264 // Replicate conditions if there wasn't a
6265 // reference list array for criteria
6266 // evaluation.
6267 vRefArrayConditions.resize( nRefArrayRows);
6268 for (auto & rVec : vRefArrayConditions)
6269 {
6270 rVec = vConditions;
6271 }
6272 }
6273
6274 bRefArrayMain = true;
6275 nRefArrayPos = nRefInList;
6276 }
6277 ScRange aRange;
6278 PopDoubleRef( aRange, nParam, nRefInList);
6279 aRange.GetVars( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2);
6280 }
6281 break;
6282 case svDoubleRef :
6283 PopDoubleRef( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2 );
6284 break;
6285 case svSingleRef :
6286 PopSingleRef( nMainCol1, nMainRow1, nMainTab1 );
6287 nMainCol2 = nMainCol1;
6288 nMainRow2 = nMainRow1;
6289 nMainTab2 = nMainTab1;
6290 break;
6291 case svMatrix:
6292 case svExternalSingleRef:
6293 case svExternalDoubleRef:
6294 {
6295 pMainMatrix = GetMatrix();
6296 if (!pMainMatrix)
6297 {
6298 PushError( FormulaError::IllegalParameter);
6299 return;
6300 }
6301 nMainCol1 = 0;
6302 nMainRow1 = 0;
6303 nMainTab1 = 0;
6304 SCSIZE nC, nR;
6305 pMainMatrix->GetDimensions( nC, nR);
6306 nMainCol2 = static_cast<SCCOL>(nC - 1);
6307 nMainRow2 = static_cast<SCROW>(nR - 1);
6308 nMainTab2 = 0;
6309 }
6310 break;
6311 // Treat a scalar value as 1x1 matrix.
6312 case svDouble:
6313 pMainMatrix = GetNewMat(1,1);
6314 nMainCol1 = nMainCol2 = 0;
6315 nMainRow1 = nMainRow2 = 0;
6316 nMainTab1 = nMainTab2 = 0;
6317 pMainMatrix->PutDouble( GetDouble(), 0, 0);
6318 break;
6319 case svString:
6320 pMainMatrix = GetNewMat(1,1);
6321 nMainCol1 = nMainCol2 = 0;
6322 nMainRow1 = nMainRow2 = 0;
6323 nMainTab1 = nMainTab2 = 0;
6324 pMainMatrix->PutString( GetString(), 0, 0);
6325 break;
6326 default:
6327 PopError();
6328 PushError( FormulaError::IllegalParameter);
6329 return;
6330 }
6331 if ( nMainTab1 != nMainTab2 )
6332 {
6333 PushError( FormulaError::IllegalArgument);
6334 return;
6335 }
6336
6337 if (bRangeReduce)
6338 {
6339 nMainCol1 += nStartColDiff;
6340 nMainRow1 += nStartRowDiff;
6341
6342 nMainCol2 += nEndColDiff;
6343 nMainRow2 += nEndRowDiff;
6344 }
6345
6346 // All reference ranges must be of same dimension and size.
6347 if ((nDimensionCols != (nMainCol2 - nMainCol1 + 1)) || (nDimensionRows != (nMainRow2 - nMainRow1 + 1)))
6348 {
6349 PushError ( FormulaError::IllegalArgument);
6350 return;
6351 }
6352
6353 if (nGlobalError != FormulaError::NONE)
6354 {
6355 PushError( nGlobalError);
6356 return; // bail out
6357 }
6358
6359 // end-result calculation
6360
6361 // This gets weird... if conditions were calculated using a
6362 // reference list array but the main calculation range is not a
6363 // reference list array, then the conditions of the array are
6364 // applied to the main range each in turn to form the array result.
6365
6366 size_t nRefArrayMainPos = (bRefArrayMain ? nRefArrayPos :
6367 (vRefArrayConditions.empty() ? std::numeric_limits<size_t>::max() : 0));
6368 const bool bAppliedArray = (!bRefArrayMain && nRefArrayMainPos == 0);
6369
6370 if (nRefArrayMainPos == 0)
6371 xResMat = GetNewMat( 1, nRefArrayRows);
6372
6373 if (pMainMatrix)
6374 {
6375 std::vector<double> aMainValues;
6376 pMainMatrix->GetDoubleArray(aMainValues, false); // Map empty values to NaN's.
6377
6378 do
6379 {
6380 if (nRefArrayMainPos < vRefArrayConditions.size())
6381 vConditions = vRefArrayConditions[nRefArrayMainPos];
6382
6383 if (vConditions.size() != aMainValues.size())
6384 {
6385 PushError( FormulaError::IllegalArgument);
6386 return;
6387 }
6388
6389 std::vector<sal_uInt32>::const_iterator itRes = vConditions.begin(), itResEnd = vConditions.end();
6390 std::vector<double>::const_iterator itMain = aMainValues.begin();
6391 for (; itRes != itResEnd; ++itRes, ++itMain)
6392 {
6393 if (*itRes != nQueryCount)
6394 continue;
6395
6396 fVal = *itMain;
6397 if (GetDoubleErrorValue(fVal) == FormulaError::ElementNaN)
6398 continue;
6399
6400 ++aRes.mfCount;
6401 if (bNull && fVal != 0.0)
6402 {
6403 bNull = false;
6404 aRes.mfMem = fVal;
6405 }
6406 else
6407 aRes.mfSum += fVal;
6408 if ( aRes.mfMin > fVal )
6409 aRes.mfMin = fVal;
6410 if ( aRes.mfMax < fVal )
6411 aRes.mfMax = fVal;
6412 }
6413 if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
6414 {
6415 xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
6416 aRes = sc::ParamIfsResult();
6417 }
6418 }
6419 while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
6420 }
6421 else
6422 {
6423 ScAddress aAdr;
6424 aAdr.SetTab( nMainTab1 );
6425 do
6426 {
6427 if (nRefArrayMainPos < vRefArrayConditions.size())
6428 vConditions = vRefArrayConditions[nRefArrayMainPos];
6429
6430 std::vector<sal_uInt32>::const_iterator itRes = vConditions.begin();
6431 for (SCCOL nCol = 0; nCol < nDimensionCols; ++nCol)
6432 {
6433 for (SCROW nRow = 0; nRow < nDimensionRows; ++nRow, ++itRes)
6434 {
6435 if (*itRes == nQueryCount)
6436 {
6437 aAdr.SetCol( nCol + nMainCol1);
6438 aAdr.SetRow( nRow + nMainRow1);
6439 ScRefCellValue aCell(mrDoc, aAdr);
6440 if (aCell.hasNumeric())
6441 {
6442 fVal = GetCellValue(aAdr, aCell);
6443 ++aRes.mfCount;
6444 if ( bNull && fVal != 0.0 )
6445 {
6446 bNull = false;
6447 aRes.mfMem = fVal;
6448 }
6449 else
6450 aRes.mfSum += fVal;
6451 if ( aRes.mfMin > fVal )
6452 aRes.mfMin = fVal;
6453 if ( aRes.mfMax < fVal )
6454 aRes.mfMax = fVal;
6455 }
6456 }
6457 }
6458 }
6459 if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
6460 {
6461 xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
6462 aRes = sc::ParamIfsResult();
6463 }
6464 }
6465 while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
6466 }
6467 }
6468 }
6469 else
6470 {
6471 // COUNTIFS only.
6472 if (vRefArrayConditions.empty())
6473 {
6474 for (auto const & rCond : vConditions)
6475 {
6476 if (rCond == nQueryCount)
6477 ++aRes.mfCount;
6478 }
6479 }
6480 else
6481 {
6482 xResMat = GetNewMat( 1, nRefArrayRows);
6483 for (size_t i=0, n = vRefArrayConditions.size(); i < n; ++i)
6484 {
6485 double fCount = 0.0;
6486 for (auto const & rCond : vRefArrayConditions[i])
6487 {
6488 if (rCond == nQueryCount)
6489 ++fCount;
6490 }
6491 if (fCount)
6492 xResMat->PutDouble( fCount, 0, i);
6493 }
6494 }
6495 }
6496
6497 if (xResMat)
6498 PushMatrix( xResMat);
6499 else
6500 PushDouble( ResultFunc( aRes));
6501}
6502
6503void ScInterpreter::ScSumIfs()
6504{
6505 // ScMutationGuard aShouldFail(pDok, ScMutationGuardFlags::CORE);
6506 sal_uInt8 nParamCount = GetByte();
6507
6508 if (nParamCount < 3 || (nParamCount % 2 != 1))
6509 {
6510 PushError( FormulaError::ParameterExpected);
6511 return;
6512 }
6513
6514 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6515 {
6516 return rtl::math::approxAdd(rRes.mfSum, rRes.mfMem);
6517 };
6518 IterateParametersIfs(ResultFunc);
6519}
6520
6521void ScInterpreter::ScAverageIfs()
6522{
6523 sal_uInt8 nParamCount = GetByte();
6524
6525 if (nParamCount < 3 || (nParamCount % 2 != 1))
6526 {
6527 PushError( FormulaError::ParameterExpected);
6528 return;
6529 }
6530
6531 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6532 {
6533 return sc::div( rtl::math::approxAdd( rRes.mfSum, rRes.mfMem), rRes.mfCount);
6534 };
6535 IterateParametersIfs(ResultFunc);
6536}
6537
6538void ScInterpreter::ScCountIfs()
6539{
6540 sal_uInt8 nParamCount = GetByte();
6541
6542 if (nParamCount < 2 || (nParamCount % 2 != 0))
6543 {
6544 PushError( FormulaError::ParameterExpected);
6545 return;
6546 }
6547
6548 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6549 {
6550 return rRes.mfCount;
6551 };
6552 IterateParametersIfs(ResultFunc);
6553}
6554
6555void ScInterpreter::ScMinIfs_MS()
6556{
6557 sal_uInt8 nParamCount = GetByte();
6558
6559 if (nParamCount < 3 || (nParamCount % 2 != 1))
6560 {
6561 PushError( FormulaError::ParameterExpected);
6562 return;
6563 }
6564
6565 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6566 {
6567 return (rRes.mfMin < std::numeric_limits<double>::max()) ? rRes.mfMin : 0.0;
6568 };
6569 IterateParametersIfs(ResultFunc);
6570}
6571
6572
6573void ScInterpreter::ScMaxIfs_MS()
6574{
6575 sal_uInt8 nParamCount = GetByte();
6576
6577 if (nParamCount < 3 || (nParamCount % 2 != 1))
6578 {
6579 PushError( FormulaError::ParameterExpected);
6580 return;
6581 }
6582
6583 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6584 {
6585 return (rRes.mfMax > std::numeric_limits<double>::lowest()) ? rRes.mfMax : 0.0;
6586 };
6587 IterateParametersIfs(ResultFunc);
6588}
6589
6590void ScInterpreter::ScLookup()
6591{
6592 sal_uInt8 nParamCount = GetByte();
6593 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
6594 return ;
6595
6596 ScMatrixRef pDataMat = nullptr, pResMat = nullptr;
6597 SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0;
6598 SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0;
6599 SCTAB nTab1 = 0, nResTab = 0;
6600 SCSIZE nLenMajor = 0; // length of major direction
6601 bool bVertical = true; // whether to lookup vertically or horizontally
6602
6603 // The third parameter, result array, for double, string and single reference.
6604 double fResVal = 0.0;
6605 svl::SharedString aResStr;
6606 ScAddress aResAdr;
6607 StackVar eResArrayType = svUnknown;
6608
6609 if (nParamCount == 3)
6610 {
6611 eResArrayType = GetStackType();
6612 switch (eResArrayType)
6613 {
6614 case svDoubleRef:
6615 {
6616 SCTAB nTabJunk;
6617 PopDoubleRef(nResCol1, nResRow1, nResTab,
6618 nResCol2, nResRow2, nTabJunk);
6619 if (nResTab != nTabJunk ||
6620 ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0))
6621 {
6622 // The result array must be a vector.
6623 PushIllegalParameter();
6624 return;
6625 }
6626 }
6627 break;
6628 case svMatrix:
6629 case svExternalSingleRef:
6630 case svExternalDoubleRef:
6631 {
6632 pResMat = GetMatrix();
6633 if (!pResMat)
6634 {
6635 PushIllegalParameter();
6636 return;
6637 }
6638 SCSIZE nC, nR;
6639 pResMat->GetDimensions(nC, nR);
6640 if (nC != 1 && nR != 1)
6641 {
6642 // Result matrix must be a vector.
6643 PushIllegalParameter();
6644 return;
6645 }
6646 }
6647 break;
6648 case svDouble:
6649 fResVal = GetDouble();
6650 break;
6651 case svString:
6652 aResStr = GetString();
6653 break;
6654 case svSingleRef:
6655 PopSingleRef( aResAdr );
6656 break;
6657 default:
6658 PushIllegalParameter();
6659 return;
6660 }
6661 }
6662
6663 // For double, string and single reference.
6664 double fDataVal = 0.0;
6665 svl::SharedString aDataStr;
6666 ScAddress aDataAdr;
6667 bool bValueData = false;
6668
6669 // Get the data-result range and also determine whether this is vertical
6670 // lookup or horizontal lookup.
6671
6672 StackVar eDataArrayType = GetStackType();
6673 switch (eDataArrayType)
6674 {
6675 case svDoubleRef:
6676 {
6677 SCTAB nTabJunk;
6678 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk);
6679 if (nTab1 != nTabJunk)
6680 {
6681 PushIllegalParameter();
6682 return;
6683 }
6684 bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1);
6685 nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1;
6686 }
6687 break;
6688 case svMatrix:
6689 case svExternalSingleRef:
6690 case svExternalDoubleRef:
6691 {
6692 pDataMat = GetMatrix();
6693 if (!pDataMat)
6694 {
6695 PushIllegalParameter();
6696 return;
6697 }
6698
6699 SCSIZE nC, nR;
6700 pDataMat->GetDimensions(nC, nR);
6701 bVertical = (nR >= nC);
6702 nLenMajor = bVertical ? nR : nC;
6703 }
6704 break;
6705 case svDouble:
6706 {
6707 fDataVal = GetDouble();
6708 bValueData = true;
6709 }
6710 break;
6711 case svString:
6712 {
6713 aDataStr = GetString();
6714 }
6715 break;
6716 case svSingleRef:
6717 {
6718 PopSingleRef( aDataAdr );
6719 ScRefCellValue aCell(mrDoc, aDataAdr);
6720 if (aCell.hasEmptyValue())
6721 {
6722 // Empty cells aren't found anywhere, bail out early.
6723 SetError( FormulaError::NotAvailable);
6724 }
6725 else if (aCell.hasNumeric())
6726 {
6727 fDataVal = GetCellValue(aDataAdr, aCell);
6728 bValueData = true;
6729 }
6730 else
6731 GetCellString(aDataStr, aCell);
6732 }
6733 break;
6734 default:
6735 SetError( FormulaError::IllegalParameter);
6736 }
6737
6738 if (nGlobalError != FormulaError::NONE)
6739 {
6740 PushError( nGlobalError);
6741 return;
6742 }
6743
6744 // Get the lookup value.
6745
6746 ScQueryParam aParam;
6747 ScQueryEntry& rEntry = aParam.GetEntry(0);
6748 if ( !FillEntry(rEntry) )
6749 return;
6750
6751 if ( eDataArrayType == svDouble || eDataArrayType == svString ||
6752 eDataArrayType == svSingleRef )
6753 {
6754 // Delta position for a single value is always 0.
6755
6756 // Found if data <= query, but not if query is string and found data is
6757 // numeric or vice versa. This is how Excel does it but doesn't
6758 // document it.
6759
6760 bool bFound = false;
6761 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6762
6763 if ( bValueData )
6764 {
6765 if (rItem.meType == ScQueryEntry::ByString)
6766 bFound = false;
6767 else
6768 bFound = (fDataVal <= rItem.mfVal);
6769 }
6770 else
6771 {
6772 if (rItem.meType != ScQueryEntry::ByString)
6773 bFound = false;
6774 else
6775 bFound = (ScGlobal::GetCollator()->compareString(aDataStr.getString(), rItem.maString.getString()) <= 0);
6776 }
6777
6778 if (!bFound)
6779 {
6780 PushNA();
6781 return;
6782 }
6783
6784 if (pResMat)
6785 {
6786 if (pResMat->IsValue( 0, 0 ))
6787 PushDouble(pResMat->GetDouble( 0, 0 ));
6788 else
6789 PushString(pResMat->GetString(0, 0));
6790 }
6791 else if (nParamCount == 3)
6792 {
6793 switch (eResArrayType)
6794 {
6795 case svDouble:
6796 PushDouble( fResVal );
6797 break;
6798 case svString:
6799 PushString( aResStr );
6800 break;
6801 case svDoubleRef:
6802 aResAdr.Set( nResCol1, nResRow1, nResTab);
6803 [[fallthrough]];
6804 case svSingleRef:
6805 PushCellResultToken( true, aResAdr, nullptr, nullptr);
6806 break;
6807 default:
6808 OSL_FAIL( "ScInterpreter::ScLookup: unhandled eResArrayType, single value data")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
":" "6808" ": "), "%s", "ScInterpreter::ScLookup: unhandled eResArrayType, single value data"
); } } while (false)
;
6809 }
6810 }
6811 else
6812 {
6813 switch (eDataArrayType)
6814 {
6815 case svDouble:
6816 PushDouble( fDataVal );
6817 break;
6818 case svString:
6819 PushString( aDataStr );
6820 break;
6821 case svSingleRef:
6822 PushCellResultToken( true, aDataAdr, nullptr, nullptr);
6823 break;
6824 default:
6825 OSL_FAIL( "ScInterpreter::ScLookup: unhandled eDataArrayType, single value data")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
":" "6825" ": "), "%s", "ScInterpreter::ScLookup: unhandled eDataArrayType, single value data"
); } } while (false)
;
6826 }
6827 }
6828 return;
6829 }
6830
6831 // Now, perform the search to compute the delta position (nDelta).
6832
6833 if (pDataMat)
6834 {
6835 // Data array is given as a matrix.
6836 rEntry.bDoQuery = true;
6837 rEntry.eOp = SC_LESS_EQUAL;
6838 bool bFound = false;
6839
6840 SCSIZE nC, nR;
6841 pDataMat->GetDimensions(nC, nR);
6842
6843 // Do not propagate errors from matrix while copying to vector.
6844 pDataMat->SetErrorInterpreter( nullptr);
6845
6846 // Excel has an undocumented behaviour in that it seems to internally
6847 // sort an interim array (i.e. error values specifically #DIV/0! are
6848 // sorted to the end) or ignore error values that makes these "get last
6849 // non-empty" searches work, e.g. =LOOKUP(2,1/NOT(ISBLANK(A:A)),A:A)
6850 // see tdf#117016
6851 // Instead of sorting a million entries of which mostly only a bunch of
6852 // rows are filled and moving error values to the end which most are
6853 // already anyway, assume the matrix to be sorted except error values
6854 // and omit the coded DoubleError values.
6855 // Do this only for a numeric matrix (that includes errors coded as
6856 // doubles), which covers the case in question.
6857 /* TODO: it's unclear whether this really matches Excel behaviour in
6858 * all constellations or if there are cases that include unsorted error
6859 * values and thus yield arbitrary binary search results or something
6860 * different or whether there are cases where error values are also
6861 * omitted from mixed numeric/string arrays or if it's not an interim
6862 * matrix but a cell range reference instead. */
6863 const bool bOmitErrorValues = (eDataArrayType == svMatrix && pDataMat->IsNumeric());
6864
6865 // In case of non-vector matrix, only search the first row or column.
6866 ScMatrixRef pDataMat2;
6867 std::vector<SCCOLROW> vIndex;
6868 if (bOmitErrorValues)
6869 {
6870 std::vector<double> vArray;
6871 VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
6872 const SCSIZE nElements = aMatAcc.GetElementCount();
6873 for (SCSIZE i=0; i < nElements; ++i)
6874 {
6875 const double fVal = aMatAcc.GetDouble(i);
6876 if (std::isfinite(fVal))
6877 {
6878 vArray.push_back(fVal);
6879 vIndex.push_back(i);
6880 }
6881 }
6882 if (vArray.empty())
6883 {
6884 PushNA();
6885 return;
6886 }
6887 const size_t nElems = vArray.size();
6888 if (nElems == nElements)
6889 {
6890 // No error value omitted, use as is.
6891 pDataMat2 = pDataMat;
6892 std::vector<SCCOLROW>().swap( vIndex);
6893 }
6894 else
6895 {
6896 nLenMajor = nElems;
6897 if (bVertical)
6898 {
6899 ScMatrixRef pTempMat = GetNewMat( 1, nElems);
6900 pTempMat->PutDoubleVector( vArray, 0, 0);
6901 pDataMat2 = pTempMat;
6902 }
6903 else
6904 {
6905 ScMatrixRef pTempMat = GetNewMat( nElems, 1);
6906 for (size_t i=0; i < nElems; ++i)
6907 pTempMat->PutDouble( vArray[i], i, 0);
6908 pDataMat2 = pTempMat;
6909 }
6910 }
6911 }
6912 else
6913 {
6914 // Just use as is with the VectorMatrixAccessor.
6915 pDataMat2 = pDataMat;
6916 }
6917
6918 // Do not propagate errors from matrix while searching.
6919 pDataMat2->SetErrorInterpreter( nullptr);
6920
6921 VectorMatrixAccessor aMatAcc2(*pDataMat2, bVertical);
6922
6923 // binary search for non-equality mode (the source data is
6924 // assumed to be sorted in ascending order).
6925
6926 SCCOLROW nDelta = -1;
6927
6928 SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0;
6929 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
6930 {
6931 SCSIZE nMid = nFirst + nLen/2;
6932 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc2, rEntry);
6933 if (nCmp == 0)
6934 {
6935 // exact match. find the last item with the same value.
6936 lcl_GetLastMatch( nMid, aMatAcc2, nLenMajor, false);
6937 nDelta = nMid;
6938 bFound = true;
6939 break;
6940 }
6941
6942 if (nLen == 1) // first and last items are next to each other.
6943 {
6944 nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1;
6945 // If already the 1st item is greater there's nothing found.
6946 bFound = (nDelta >= 0);
6947 break;
6948 }
6949
6950 if (nCmp < 0)
6951 nFirst = nMid;
6952 else
6953 nLast = nMid;
6954 }
6955
6956 if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item
6957 {
6958 sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, aMatAcc2, rEntry);
6959 if (nCmp <= 0)
6960 {
6961 // either the last item is an exact match or the real
6962 // hit is beyond the last item.
6963 nDelta += 1;
6964 bFound = true;
6965 }
6966 }
6967 else if (nDelta > 0) // valid hit must be 2nd item or higher
6968 {
6969 // non-exact match
6970 bFound = true;
6971 }
6972
6973 // With 0-9 < A-Z, if query is numeric and data found is string, or
6974 // vice versa, the (yet another undocumented) Excel behavior is to
6975 // return #N/A instead.
6976
6977 if (bFound)
6978 {
6979 if (!vIndex.empty())
6980 nDelta = vIndex[nDelta];
6981
6982 VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
6983 SCCOLROW i = nDelta;
6984 SCSIZE n = aMatAcc.GetElementCount();
6985 if (o3tl::make_unsigned(i) >= n)
6986 i = static_cast<SCCOLROW>(n);
6987 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
6988 if (bByString == aMatAcc.IsValue(i))
6989 bFound = false;
6990 }
6991
6992 if (!bFound)
6993 {
6994 PushNA();
6995 return;
6996 }
6997
6998 // Now that we've found the delta, push the result back to the cell.
6999
7000 if (pResMat)
7001 {
7002 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
7003 // result array is matrix.
7004 if (o3tl::make_unsigned(nDelta) >= aResMatAcc.GetElementCount())
7005 {
7006 PushNA();
7007 return;
7008 }
7009 if (aResMatAcc.IsValue(nDelta))
7010 PushDouble(aResMatAcc.GetDouble(nDelta));
7011 else
7012 PushString(aResMatAcc.GetString(nDelta));
7013 }
7014 else if (nParamCount == 3)
7015 {
7016 // result array is cell range.
7017 ScAddress aAdr;
7018 aAdr.SetTab(nResTab);
7019 bool bResVertical = (nResRow2 - nResRow1) > 0;
7020 if (bResVertical)
7021 {
7022 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
7023 if (nTempRow > mrDoc.MaxRow())
7024 {
7025 PushDouble(0);
7026 return;
7027 }
7028 aAdr.SetCol(nResCol1);
7029 aAdr.SetRow(nTempRow);
7030 }
7031 else
7032 {
7033 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
7034 if (nTempCol > mrDoc.MaxCol())
7035 {
7036 PushDouble(0);
7037 return;
7038 }
7039 aAdr.SetCol(nTempCol);
7040 aAdr.SetRow(nResRow1);
7041 }
7042 PushCellResultToken(true, aAdr, nullptr, nullptr);
7043 }
7044 else
7045 {
7046 // No result array. Use the data array to get the final value from.
7047 // Propagate errors from matrix again.
7048 pDataMat->SetErrorInterpreter( this);
7049 if (bVertical)
7050 {
7051 if (pDataMat->IsValue(nC-1, nDelta))
7052 PushDouble(pDataMat->GetDouble(nC-1, nDelta));
7053 else
7054 PushString(pDataMat->GetString(nC-1, nDelta));
7055 }
7056 else
7057 {
7058 if (pDataMat->IsValue(nDelta, nR-1))
7059 PushDouble(pDataMat->GetDouble(nDelta, nR-1));
7060 else
7061 PushString(pDataMat->GetString(nDelta, nR-1));
7062 }
7063 }
7064
7065 return;
7066 }
7067
7068 // Perform cell range search.
7069
7070 aParam.nCol1 = nCol1;
7071 aParam.nRow1 = nRow1;
7072 aParam.nCol2 = bVertical ? nCol1 : nCol2;
7073 aParam.nRow2 = bVertical ? nRow2 : nRow1;
7074 aParam.bByRow = bVertical;
7075
7076 rEntry.bDoQuery = true;
7077 rEntry.eOp = SC_LESS_EQUAL;
7078 rEntry.nField = nCol1;
7079 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7080 if (rItem.meType == ScQueryEntry::ByString)
7081 aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
7082
7083 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, aParam, false);
7084 SCCOL nC;
7085 SCROW nR;
7086 // Advance Entry.nField in iterator upon switching columns if
7087 // lookup in row.
7088 aCellIter.SetAdvanceQueryParamEntryField(!bVertical);
7089 if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) )
7090 {
7091 PushNA();
7092 return;
7093 }
7094
7095 SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1);
7096
7097 if (pResMat)
7098 {
7099 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
7100 // Use the matrix result array.
7101 if (aResMatAcc.IsValue(nDelta))
7102 PushDouble(aResMatAcc.GetDouble(nDelta));
7103 else
7104 PushString(aResMatAcc.GetString(nDelta));
7105 }
7106 else if (nParamCount == 3)
7107 {
7108 switch (eResArrayType)
7109 {
7110 case svDoubleRef:
7111 {
7112 // Use the result array vector. Note that the result array is assumed
7113 // to be a vector (i.e. 1-dimensional array).
7114
7115 ScAddress aAdr;
7116 aAdr.SetTab(nResTab);
7117 bool bResVertical = (nResRow2 - nResRow1) > 0;
7118 if (bResVertical)
7119 {
7120 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
7121 if (nTempRow > mrDoc.MaxRow())
7122 {
7123 PushDouble(0);
7124 return;
7125 }
7126 aAdr.SetCol(nResCol1);
7127 aAdr.SetRow(nTempRow);
7128 }
7129 else
7130 {
7131 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
7132 if (nTempCol > mrDoc.MaxCol())
7133 {
7134 PushDouble(0);
7135 return;
7136 }
7137 aAdr.SetCol(nTempCol);
7138 aAdr.SetRow(nResRow1);
7139 }
7140 PushCellResultToken( true, aAdr, nullptr, nullptr);
7141 }
7142 break;
7143 case svDouble:
7144 case svString:
7145 case svSingleRef:
7146 {
7147 if (nDelta != 0)
7148 PushNA();
7149 else
7150 {
7151 switch (eResArrayType)
7152 {
7153 case svDouble:
7154 PushDouble( fResVal );
7155 break;
7156 case svString:
7157 PushString( aResStr );
7158 break;
7159 case svSingleRef:
7160 PushCellResultToken( true, aResAdr, nullptr, nullptr);
7161 break;
7162 default:
7163 ; // nothing
7164 }
7165 }
7166 }
7167 break;
7168 default:
7169 OSL_FAIL( "ScInterpreter::ScLookup: unhandled eResArrayType, range search")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
":" "7169" ": "), "%s", "ScInterpreter::ScLookup: unhandled eResArrayType, range search"
); } } while (false)
;
7170 }
7171 }
7172 else
7173 {
7174 // Regardless of whether or not the result array exists, the last
7175 // array is always used as the "result" array.
7176
7177 ScAddress aAdr;
7178 aAdr.SetTab(nTab1);
7179 if (bVertical)
7180 {
7181 SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta);
7182 if (nTempRow > mrDoc.MaxRow())
7183 {
7184 PushDouble(0);
7185 return;
7186 }
7187 aAdr.SetCol(nCol2);
7188 aAdr.SetRow(nTempRow);
7189 }
7190 else
7191 {
7192 SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta);
7193 if (nTempCol > mrDoc.MaxCol())
7194 {
7195 PushDouble(0);
7196 return;
7197 }
7198 aAdr.SetCol(nTempCol);
7199 aAdr.SetRow(nRow2);
7200 }
7201 PushCellResultToken(true, aAdr, nullptr, nullptr);
7202 }
7203}
7204
7205void ScInterpreter::ScHLookup()
7206{
7207 CalculateLookup(true);
7208}
7209
7210void ScInterpreter::CalculateLookup(bool bHLookup)
7211{
7212 sal_uInt8 nParamCount = GetByte();
7213 if (!MustHaveParamCount(nParamCount, 3, 4))
7214 return;
7215
7216 // Optional 4th argument to declare whether or not the range is sorted.
7217 bool bSorted = true;
7218 if (nParamCount == 4)
7219 bSorted = GetBool();
7220
7221 // Index of column to search.
7222 double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0;
7223
7224 ScMatrixRef pMat = nullptr;
7225 SCSIZE nC = 0, nR = 0;
7226 SCCOL nCol1 = 0;
7227 SCROW nRow1 = 0;
7228 SCTAB nTab1 = 0;
7229 SCCOL nCol2 = 0;
7230 SCROW nRow2 = 0;
7231 StackVar eType = GetStackType();
7232 if (eType == svDoubleRef)
7233 {
7234 SCTAB nTab2;
7235 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7236 if (nTab1 != nTab2)
7237 {
7238 PushIllegalParameter();
7239 return;
7240 }
7241 }
7242 else if (eType == svSingleRef)
7243 {
7244 PopSingleRef(nCol1, nRow1, nTab1);
7245 nCol2 = nCol1;
7246 nRow2 = nRow1;
7247 }
7248 else if (eType == svMatrix || eType == svExternalDoubleRef || eType == svExternalSingleRef)
7249 {
7250 pMat = GetMatrix();
7251
7252 if (pMat)
7253 pMat->GetDimensions(nC, nR);
7254 else
7255 {
7256 PushIllegalParameter();
7257 return;
7258 }
7259 }
7260 else
7261 {
7262 PushIllegalParameter();
7263 return;
7264 }
7265
7266 if ( fIndex < 0.0 || (bHLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) )
7267 {
7268 PushIllegalArgument();
7269 return;
7270 }
7271
7272 SCROW nZIndex = static_cast<SCROW>(fIndex);
7273 SCCOL nSpIndex = static_cast<SCCOL>(fIndex);
7274
7275 if (!pMat)
7276 {
7277 nZIndex += nRow1; // value row
7278 nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 ); // value column
7279 }
7280
7281 if (nGlobalError != FormulaError::NONE)
7282 {
7283 PushIllegalParameter();
7284 return;
7285 }
7286
7287 ScQueryParam aParam;
7288 aParam.nCol1 = nCol1;
7289 aParam.nRow1 = nRow1;
7290 if ( bHLookup )
7291 {
7292 aParam.nCol2 = nCol2;
7293 aParam.nRow2 = nRow1; // search only in the first row
7294 aParam.bByRow = false;
7295 }
7296 else
7297 {
7298 aParam.nCol2 = nCol1; // search only in the first column
7299 aParam.nRow2 = nRow2;
7300 aParam.nTab = nTab1;
7301 }
7302
7303 ScQueryEntry& rEntry = aParam.GetEntry(0);
7304 rEntry.bDoQuery = true;
7305 if ( bSorted )
7306 rEntry.eOp = SC_LESS_EQUAL;
7307 if ( !FillEntry(rEntry) )
7308 return;
7309
7310 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7311 if (rItem.meType == ScQueryEntry::ByString)
7312 aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
7313 if (pMat)
7314 {
7315 SCSIZE nMatCount = bHLookup ? nC : nR;
7316 SCSIZE nDelta = SCSIZE_MAX;
7317 if (rItem.meType == ScQueryEntry::ByString)
7318 {
7319//!!!!!!!
7320//TODO: enable regex on matrix strings
7321//!!!!!!!
7322 svl::SharedString aParamStr = rItem.maString;
7323 if ( bSorted )
7324 {
7325 CollatorWrapper* pCollator = ScGlobal::GetCollator();
7326 for (SCSIZE i = 0; i < nMatCount; i++)
7327 {
7328 if (bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i))
7329 {
7330 sal_Int32 nRes =
7331 pCollator->compareString(
7332 bHLookup ? pMat->GetString(i,0).getString() : pMat->GetString(0,i).getString(), aParamStr.getString());
7333 if (nRes <= 0)
7334 nDelta = i;
7335 else if (i>0) // #i2168# ignore first mismatch
7336 i = nMatCount+1;
7337 }
7338 else
7339 nDelta = i;
7340 }
7341 }
7342 else
7343 {
7344 if (bHLookup)
7345 {
7346 for (SCSIZE i = 0; i < nMatCount; i++)
7347 {
7348 if (pMat->IsStringOrEmpty(i, 0))
7349 {
7350 if (pMat->GetString(i,0).getDataIgnoreCase() == aParamStr.getDataIgnoreCase())
7351 {
7352 nDelta = i;
7353 i = nMatCount + 1;
7354 }
7355 }
7356 }
7357 }
7358 else
7359 {
7360 nDelta = pMat->MatchStringInColumns(aParamStr, 0, 0);
7361 }
7362 }
7363 }
7364 else
7365 {
7366 if ( bSorted )
7367 {
7368 // #i2168# ignore strings
7369 for (SCSIZE i = 0; i < nMatCount; i++)
7370 {
7371 if (!(bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i)))
7372 {
7373 if ((bHLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rItem.mfVal)
7374 nDelta = i;
7375 else
7376 i = nMatCount+1;
7377 }
7378 }
7379 }
7380 else
7381 {
7382 if (bHLookup)
7383 {
7384 for (SCSIZE i = 0; i < nMatCount; i++)
7385 {
7386 if (! pMat->IsStringOrEmpty(i, 0) )
7387 {
7388 if ( pMat->GetDouble(i,0) == rItem.mfVal)
7389 {
7390 nDelta = i;
7391 i = nMatCount + 1;
7392 }
7393 }
7394 }
7395 }
7396 else
7397 {
7398 nDelta = pMat->MatchDoubleInColumns(rItem.mfVal, 0, 0);
7399 }
7400 }
7401 }
7402 if ( nDelta != SCSIZE_MAX )
7403 {
7404 SCSIZE nX = static_cast<SCSIZE>(nSpIndex);
7405 SCSIZE nY = nDelta;
7406 if ( bHLookup )
7407 {
7408 nX = nDelta;
7409 nY = static_cast<SCSIZE>(nZIndex);
7410 }
7411 assert( nX < nC && nY < nR )(static_cast <bool> (nX < nC && nY < nR) ?
void (0) : __assert_fail ("nX < nC && nY < nR"
, "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
, 7411, __extension__ __PRETTY_FUNCTION__))
;
7412 if ( pMat->IsStringOrEmpty( nX, nY) )
7413 PushString(pMat->GetString( nX,nY).getString());
7414 else
7415 PushDouble(pMat->GetDouble( nX,nY));
7416 }
7417 else
7418 PushNA();
7419 }
7420 else
7421 {
7422 rEntry.nField = nCol1;
7423 bool bFound = false;
7424 SCCOL nCol = 0;
7425 SCROW nRow = 0;
7426 if ( bSorted )
7427 rEntry.eOp = SC_LESS_EQUAL;
7428 if ( bHLookup )
7429 {
7430 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, aParam, false);
7431 // advance Entry.nField in Iterator upon switching columns
7432 aCellIter.SetAdvanceQueryParamEntryField( true );
7433 if ( bSorted )
7434 {
7435 SCROW nRow1_temp;
7436 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp );
7437 }
7438 else if ( aCellIter.GetFirst() )
7439 {
7440 bFound = true;
7441 nCol = aCellIter.GetCol();
7442 }
7443 nRow = nZIndex;
7444 }
7445 else
7446 {
7447 ScAddress aResultPos( nCol1, nRow1, nTab1);
7448 bFound = LookupQueryWithCache( aResultPos, aParam);
7449 nRow = aResultPos.Row();
7450 nCol = nSpIndex;
7451 }
7452
7453 if ( bFound )
7454 {
7455 ScAddress aAdr( nCol, nRow, nTab1 );
7456 PushCellResultToken( true, aAdr, nullptr, nullptr);
7457 }
7458 else
7459 PushNA();
7460 }
7461}
7462
7463bool ScInterpreter::FillEntry(ScQueryEntry& rEntry)
7464{
7465 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7466 switch ( GetStackType() )
7467 {
7468 case svDouble:
7469 {
7470 rItem.meType = ScQueryEntry::ByValue;
7471 rItem.mfVal = GetDouble();
7472 }
7473 break;
7474 case svString:
7475 {
7476 rItem.meType = ScQueryEntry::ByString;
7477 rItem.maString = GetString();
7478 }
7479 break;
7480 case svDoubleRef :
7481 case svSingleRef :
7482 {
7483 ScAddress aAdr;
7484 if ( !PopDoubleRefOrSingleRef( aAdr ) )
7485 {
7486 PushInt(0);
7487 return false;
7488 }
7489 ScRefCellValue aCell(mrDoc, aAdr);
7490 if (aCell.hasNumeric())
7491 {
7492 rItem.meType = ScQueryEntry::ByValue;
7493 rItem.mfVal = GetCellValue(aAdr, aCell);
7494 }
7495 else
7496 {
7497 GetCellString(rItem.maString, aCell);
7498 rItem.meType = ScQueryEntry::ByString;
7499 }
7500 }
7501 break;
7502 case svExternalDoubleRef:
7503 case svExternalSingleRef:
7504 case svMatrix:
7505 {
7506 svl::SharedString aStr;
7507 const ScMatValType nType = GetDoubleOrStringFromMatrix(rItem.mfVal, aStr);
7508 rItem.maString = aStr;
7509 rItem.meType = ScMatrix::IsNonValueType(nType) ?
7510 ScQueryEntry::ByString : ScQueryEntry::ByValue;
7511 }
7512 break;
7513 default:
7514 {
7515 PushIllegalParameter();
7516 return false;
7517 }
7518 } // switch ( GetStackType() )
7519 return true;
7520}
7521
7522void ScInterpreter::ScVLookup()
7523{
7524 CalculateLookup(false);
7525}
7526
7527void ScInterpreter::ScSubTotal()
7528{
7529 sal_uInt8 nParamCount = GetByte();
7530 if ( !MustHaveParamCountMin( nParamCount, 2 ) )
7531 return;
7532
7533 // We must fish the 1st parameter deep from the stack! And push it on top.
7534 const FormulaToken* p = pStack[ sp - nParamCount ];
7535 PushWithoutError( *p );
7536 sal_Int32 nFunc = GetInt32();
7537 mnSubTotalFlags |= SubtotalFlags::IgnoreNestedStAg | SubtotalFlags::IgnoreFiltered;
7538 if (nFunc > 100)
7539 {
7540 // For opcodes 101 through 111, we need to skip hidden cells.
7541 // Other than that these opcodes are identical to 1 through 11.
7542 mnSubTotalFlags |= SubtotalFlags::IgnoreHidden;
7543 nFunc -= 100;
7544 }
7545
7546 if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 11 )
7547 PushIllegalArgument(); // simulate return on stack, not SetError(...)
7548 else
7549 {
7550 cPar = nParamCount - 1;
7551 switch( nFunc )
7552 {
7553 case SUBTOTAL_FUNC_AVE : ScAverage(); break;
7554 case SUBTOTAL_FUNC_CNT : ScCount(); break;
7555 case SUBTOTAL_FUNC_CNT2 : ScCount2(); break;
7556 case SUBTOTAL_FUNC_MAX : ScMax(); break;
7557 case SUBTOTAL_FUNC_MIN : ScMin(); break;
7558 case SUBTOTAL_FUNC_PROD : ScProduct(); break;
7559 case SUBTOTAL_FUNC_STD : ScStDev(); break;
7560 case SUBTOTAL_FUNC_STDP : ScStDevP(); break;
7561 case SUBTOTAL_FUNC_SUM : ScSum(); break;
7562 case SUBTOTAL_FUNC_VAR : ScVar(); break;
7563 case SUBTOTAL_FUNC_VARP : ScVarP(); break;
7564 default : PushIllegalArgument(); break;
7565 }
7566 }
7567 mnSubTotalFlags = SubtotalFlags::NONE;
7568 // Get rid of the 1st (fished) parameter.
7569 FormulaConstTokenRef xRef( PopToken());
7570 Pop();
7571 PushTokenRef( xRef);
7572}
7573
7574void ScInterpreter::ScAggregate()
7575{
7576 sal_uInt8 nParamCount = GetByte();
7577 if ( !MustHaveParamCountMin( nParamCount, 3 ) )
7578 return;
7579
7580 const FormulaError nErr = nGlobalError;
7581 nGlobalError = FormulaError::NONE;
7582
7583 // fish the 1st parameter from the stack and push it on top.
7584 const FormulaToken* p = pStack[ sp - nParamCount ];
7585 PushWithoutError( *p );
7586 sal_Int32 nFunc = GetInt32();
7587 // fish the 2nd parameter from the stack and push it on top.
7588 const FormulaToken* p2 = pStack[ sp - ( nParamCount - 1 ) ];
7589 PushWithoutError( *p2 );
7590 sal_Int32 nOption = GetInt32();
7591
7592 if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 19 )
7593 {
7594 nGlobalError = nErr;
7595 PushIllegalArgument();
7596 }
7597 else
7598 {
7599 switch ( nOption)
7600 {
7601 case 0 : // ignore nested SUBTOTAL and AGGREGATE functions
7602 mnSubTotalFlags = SubtotalFlags::IgnoreNestedStAg;
7603 break;
7604 case 1 : // ignore hidden rows, nested SUBTOTAL and AGGREGATE functions
7605 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreNestedStAg;
7606 break;
7607 case 2 : // ignore error values, nested SUBTOTAL and AGGREGATE functions
7608 mnSubTotalFlags = SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg;
7609 break;
7610 case 3 : // ignore hidden rows, error values, nested SUBTOTAL and AGGREGATE functions
7611 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg;
7612 break;
7613 case 4 : // ignore nothing
7614 mnSubTotalFlags = SubtotalFlags::NONE;
7615 break;
7616 case 5 : // ignore hidden rows
7617 mnSubTotalFlags = SubtotalFlags::IgnoreHidden ;
7618 break;
7619 case 6 : // ignore error values
7620 mnSubTotalFlags = SubtotalFlags::IgnoreErrVal ;
7621 break;
7622 case 7 : // ignore hidden rows and error values
7623 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal ;
7624 break;
7625 default :
7626 nGlobalError = nErr;
7627 PushIllegalArgument();
7628 return;
7629 }
7630
7631 if ((mnSubTotalFlags & SubtotalFlags::IgnoreErrVal) == SubtotalFlags::NONE)
7632 nGlobalError = nErr;
7633
7634 cPar = nParamCount - 2;
7635 switch ( nFunc )
7636 {
7637 case AGGREGATE_FUNC_AVE : ScAverage(); break;
7638 case AGGREGATE_FUNC_CNT : ScCount(); break;
7639 case AGGREGATE_FUNC_CNT2 : ScCount2(); break;
7640 case AGGREGATE_FUNC_MAX : ScMax(); break;
7641 case AGGREGATE_FUNC_MIN : ScMin(); break;
7642 case AGGREGATE_FUNC_PROD : ScProduct(); break;
7643 case AGGREGATE_FUNC_STD : ScStDev(); break;
7644 case AGGREGATE_FUNC_STDP : ScStDevP(); break;
7645 case AGGREGATE_FUNC_SUM : ScSum(); break;
7646 case AGGREGATE_FUNC_VAR : ScVar(); break;
7647 case AGGREGATE_FUNC_VARP : ScVarP(); break;
7648 case AGGREGATE_FUNC_MEDIAN : ScMedian(); break;
7649 case AGGREGATE_FUNC_MODSNGL : ScModalValue(); break;
7650 case AGGREGATE_FUNC_LARGE : ScLarge(); break;
7651 case AGGREGATE_FUNC_SMALL : ScSmall(); break;
7652 case AGGREGATE_FUNC_PERCINC : ScPercentile( true ); break;
7653 case AGGREGATE_FUNC_QRTINC : ScQuartile( true ); break;
7654 case AGGREGATE_FUNC_PERCEXC : ScPercentile( false ); break;
7655 case AGGREGATE_FUNC_QRTEXC : ScQuartile( false ); break;
7656 default:
7657 nGlobalError = nErr;
7658 PushIllegalArgument();
7659 break;
7660 }
7661 mnSubTotalFlags = SubtotalFlags::NONE;
7662 }
7663 FormulaConstTokenRef xRef( PopToken());
7664 // Get rid of the 1st and 2nd (fished) parameters.
7665 Pop();
7666 Pop();
7667 PushTokenRef( xRef);
7668}
7669
7670std::unique_ptr<ScDBQueryParamBase> ScInterpreter::GetDBParams( bool& rMissingField )
7671{
7672 bool bAllowMissingField = false;
7673 if ( rMissingField )
7674 {
7675 bAllowMissingField = true;
7676 rMissingField = false;
7677 }
7678 if ( GetByte() == 3 )
7679 {
7680 // First, get the query criteria range.
7681 ::std::unique_ptr<ScDBRangeBase> pQueryRef( PopDBDoubleRef() );
7682 if (!pQueryRef)
7683 return nullptr;
7684
7685 bool bByVal = true;
7686 double nVal = 0.0;
7687 svl::SharedString aStr;
7688 ScRange aMissingRange;
7689 bool bRangeFake = false;
7690 switch (GetStackType())
7691 {
7692 case svDouble :
7693 nVal = ::rtl::math::approxFloor( GetDouble() );
7694 if ( bAllowMissingField && nVal == 0.0 )
7695 rMissingField = true; // fake missing parameter
7696 break;
7697 case svString :
7698 bByVal = false;
7699 aStr = GetString();
7700 break;
7701 case svSingleRef :
7702 {
7703 ScAddress aAdr;
7704 PopSingleRef( aAdr );
7705 ScRefCellValue aCell(mrDoc, aAdr);
7706 if (aCell.hasNumeric())
7707 nVal = GetCellValue(aAdr, aCell);
7708 else
7709 {
7710 bByVal = false;
7711 GetCellString(aStr, aCell);
7712 }
7713 }
7714 break;
7715 case svDoubleRef :
7716 if ( bAllowMissingField )
7717 { // fake missing parameter for old SO compatibility
7718 bRangeFake = true;
7719 PopDoubleRef( aMissingRange );
7720 }
7721 else
7722 {
7723 PopError();
7724 SetError( FormulaError::IllegalParameter );
7725 }
7726 break;
7727 case svMissing :
7728 PopError();
7729 if ( bAllowMissingField )
7730 rMissingField = true;
7731 else
7732 SetError( FormulaError::IllegalParameter );
7733 break;
7734 default:
7735 PopError();
7736 SetError( FormulaError::IllegalParameter );
7737 }
7738
7739 if (nGlobalError != FormulaError::NONE)
7740 return nullptr;
7741
7742 unique_ptr<ScDBRangeBase> pDBRef( PopDBDoubleRef() );
7743
7744 if (nGlobalError != FormulaError::NONE || !pDBRef)
7745 return nullptr;
7746
7747 if ( bRangeFake )
7748 {
7749 // range parameter must match entire database range
7750 if (pDBRef->isRangeEqual(aMissingRange))
7751 rMissingField = true;
7752 else
7753 SetError( FormulaError::IllegalParameter );
7754 }
7755
7756 if (nGlobalError != FormulaError::NONE)
7757 return nullptr;
7758
7759 SCCOL nField = pDBRef->getFirstFieldColumn();
7760 if (rMissingField)
7761 ; // special case
7762 else if (bByVal)
7763 nField = pDBRef->findFieldColumn(static_cast<SCCOL>(nVal));
7764 else
7765 {
7766 FormulaError nErr = FormulaError::NONE;
7767 nField = pDBRef->findFieldColumn(aStr.getString(), &nErr);
7768 SetError(nErr);
7769 }
7770
7771 if (!mrDoc.ValidCol(nField))
7772 return nullptr;
7773
7774 unique_ptr<ScDBQueryParamBase> pParam( pDBRef->createQueryParam(pQueryRef.get()) );
7775
7776 if (pParam)
7777 {
7778 // An allowed missing field parameter sets the result field
7779 // to any of the query fields, just to be able to return
7780 // some cell from the iterator.
7781 if ( rMissingField )
7782 nField = static_cast<SCCOL>(pParam->GetEntry(0).nField);
7783 pParam->mnField = nField;
7784
7785 SCSIZE nCount = pParam->GetEntryCount();
7786 for ( SCSIZE i=0; i < nCount; i++ )
7787 {
7788 ScQueryEntry& rEntry = pParam->GetEntry(i);
7789 if (!rEntry.bDoQuery)
7790 break;
7791
7792 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7793 sal_uInt32 nIndex = 0;
7794 OUString aQueryStr = rItem.maString.getString();
7795 bool bNumber = pFormatter->IsNumberFormat(
7796 aQueryStr, nIndex, rItem.mfVal);
7797 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
7798
7799 if (!bNumber && pParam->eSearchType == utl::SearchParam::SearchType::Normal)
7800 pParam->eSearchType = DetectSearchType(aQueryStr, mrDoc);
7801 }
7802 return pParam;
7803 }
7804 }
7805 return nullptr;
7806}
7807
7808void ScInterpreter::DBIterator( ScIterFunc eFunc )
7809{
7810 double nErg = 0.0;
7811 double fMem = 0.0;
7812 sal_uLong nCount = 0;
7813 bool bMissingField = false;
7814 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
7815 if (pQueryParam)
7816 {
7817 if (!pQueryParam->IsValidFieldIndex())
7818 {
7819 SetError(FormulaError::NoValue);
7820 return;
7821 }
7822 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
7823 ScDBQueryDataIterator::Value aValue;
7824 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
7825 {
7826 switch( eFunc )
7827 {
7828 case ifPRODUCT: nErg = 1; break;
7829 case ifMAX: nErg = -MAXDOUBLE1.7e307; break;
7830 case ifMIN: nErg = MAXDOUBLE1.7e307; break;
7831 default: ; // nothing
7832 }
7833
7834 bool bNull = true;
7835 do
7836 {
7837 nCount++;
7838 switch( eFunc )
7839 {
7840 case ifAVERAGE:
7841 case ifSUM:
7842 if ( bNull && aValue.mfValue != 0.0 )
7843 {
7844 bNull = false;
7845 fMem = aValue.mfValue;
7846 }
7847 else
7848 nErg += aValue.mfValue;
7849 break;
7850 case ifSUMSQ:
7851 nErg += aValue.mfValue * aValue.mfValue;
7852 break;
7853 case ifPRODUCT:
7854 nErg *= aValue.mfValue;
7855 break;
7856 case ifMAX:
7857 if( aValue.mfValue > nErg ) nErg = aValue.mfValue;
7858 break;
7859 case ifMIN:
7860 if( aValue.mfValue < nErg ) nErg = aValue.mfValue;
7861 break;
7862 default: ; // nothing
7863 }
7864 }
7865 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
7866 }
7867 SetError(aValue.mnError);
7868 }
7869 else
7870 SetError( FormulaError::IllegalParameter);
7871 switch( eFunc )
7872 {
7873 case ifCOUNT: nErg = nCount; break;
7874 case ifSUM: nErg = ::rtl::math::approxAdd( nErg, fMem ); break;
7875 case ifAVERAGE: nErg = div(::rtl::math::approxAdd(nErg, fMem), nCount); break;
7876 default: ; // nothing
7877 }
7878 PushDouble( nErg );
7879}
7880
7881void ScInterpreter::ScDBSum()
7882{
7883 DBIterator( ifSUM );
7884}
7885
7886void ScInterpreter::ScDBCount()
7887{
7888 bool bMissingField = true;
7889 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
7890 if (pQueryParam)
7891 {
7892 sal_uLong nCount = 0;
7893 if ( bMissingField && pQueryParam->GetType() == ScDBQueryParamBase::INTERNAL )
7894 { // count all matching records
7895 // TODO: currently the QueryIterators only return cell pointers of
7896 // existing cells, so if a query matches an empty cell there's
7897 // nothing returned, and therefore not counted!
7898 // Since this has ever been the case and this code here only came
7899 // into existence to fix #i6899 and it never worked before we'll
7900 // have to live with it until we reimplement the iterators to also
7901 // return empty cells, which would mean to adapt all callers of
7902 // iterators.
7903 ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pQueryParam.get());
7904 p->nCol2 = p->nCol1; // Don't forget to select only one column.
7905 SCTAB nTab = p->nTab;
7906 // ScQueryCellIterator doesn't make use of ScDBQueryParamBase::mnField,
7907 // so the source range has to be restricted, like before the introduction
7908 // of ScDBQueryParamBase.
7909 p->nCol1 = p->nCol2 = p->mnField;
7910 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab, *p, true);
7911 if ( aCellIter.GetFirst() )
7912 {
7913 do
7914 {
7915 nCount++;
7916 } while ( aCellIter.GetNext() );
7917 }
7918 }
7919 else
7920 { // count only matching records with a value in the "result" field
7921 if (!pQueryParam->IsValidFieldIndex())
7922 {
7923 SetError(FormulaError::NoValue);
7924 return;
7925 }
7926 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
7927 ScDBQueryDataIterator::Value aValue;
7928 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
7929 {
7930 do
7931 {
7932 nCount++;
7933 }
7934 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
7935 }
7936 SetError(aValue.mnError);
7937 }
7938 PushDouble( nCount );
7939 }
7940 else
7941 PushIllegalParameter();
7942}
7943
7944void ScInterpreter::ScDBCount2()
7945{
7946 bool bMissingField = true;
7947 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
7948 if (pQueryParam)
7949 {
7950 if (!pQueryParam->IsValidFieldIndex())
7951 {
7952 SetError(FormulaError::NoValue);
7953 return;
7954 }
7955 sal_uLong nCount = 0;
7956 pQueryParam->mbSkipString = false;
7957 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
7958 ScDBQueryDataIterator::Value aValue;
7959 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
7960 {
7961 do
7962 {
7963 nCount++;
7964 }
7965 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
7966 }
7967 SetError(aValue.mnError);
7968 PushDouble( nCount );
7969 }
7970 else
7971 PushIllegalParameter();
7972}
7973
7974void ScInterpreter::ScDBAverage()
7975{
7976 DBIterator( ifAVERAGE );
7977}
7978
7979void ScInterpreter::ScDBMax()
7980{
7981 DBIterator( ifMAX );
7982}
7983
7984void ScInterpreter::ScDBMin()
7985{
7986 DBIterator( ifMIN );
7987}
7988
7989void ScInterpreter::ScDBProduct()
7990{
7991 DBIterator( ifPRODUCT );
7992}
7993
7994void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount )
7995{
7996 std::vector<double> values;
7997 double vSum = 0.0;
7998 double vMean = 0.0;
7999
8000 rValCount = 0.0;
8001 double fSum = 0.0;
8002 bool bMissingField = false;
8003 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
8004 if (pQueryParam)
3
Taking true branch
8005 {
8006 if (!pQueryParam->IsValidFieldIndex())
4
Assuming the condition is true
5
Taking true branch
8007 {
8008 SetError(FormulaError::NoValue);
8009 return;
6
Returning without writing to 'rVal'
8010 }
8011 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
8012 ScDBQueryDataIterator::Value aValue;
8013 if (aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE)
8014 {
8015 do
8016 {
8017 rValCount++;
8018 values.push_back(aValue.mfValue);
8019 fSum += aValue.mfValue;
8020 }
8021 while ((aValue.mnError == FormulaError::NONE) && aValIter.GetNext(aValue));
8022 }
8023 SetError(aValue.mnError);
8024 }
8025 else
8026 SetError( FormulaError::IllegalParameter);
8027
8028 vMean = fSum / values.size();
8029
8030 for (double v : values)
8031 vSum += (v - vMean) * (v - vMean);
8032
8033 rVal = vSum;
8034}
8035
8036void ScInterpreter::ScDBStdDev()
8037{
8038 double fVal, fCount;
8039 GetDBStVarParams( fVal, fCount );
8040 PushDouble( sqrt(fVal/(fCount-1)));
8041}
8042
8043void ScInterpreter::ScDBStdDevP()
8044{
8045 double fVal, fCount;
8046 GetDBStVarParams( fVal, fCount );
8047 PushDouble( sqrt(fVal/fCount));
8048}
8049
8050void ScInterpreter::ScDBVar()
8051{
8052 double fVal, fCount;
8053 GetDBStVarParams( fVal, fCount );
8054 PushDouble(fVal/(fCount-1));
8055}
8056
8057void ScInterpreter::ScDBVarP()
8058{
8059 double fVal, fCount;
1
'fVal' declared without an initial value
8060 GetDBStVarParams( fVal, fCount );
2
Calling 'ScInterpreter::GetDBStVarParams'
7
Returning from 'ScInterpreter::GetDBStVarParams'
8061 PushDouble(fVal/fCount);
8
The left operand of '/' is a garbage value
8062}
8063
8064void ScInterpreter::ScIndirect()
8065{
8066 sal_uInt8 nParamCount = GetByte();
8067 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
8068 return;
8069
8070 // Reference address syntax for INDIRECT is configurable.
8071 FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
8072 if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
8073 // Use the current address syntax if unspecified.
8074 eConv = mrDoc.GetAddressConvention();
8075
8076 // either CONV_A1_XL_A1 was explicitly configured, or it wasn't possible
8077 // to determine which syntax to use during doc import
8078 bool bTryXlA1 = (eConv == FormulaGrammar::CONV_A1_XL_A1);
8079
8080 if (nParamCount == 2 && 0.0 == GetDouble() )
8081 {
8082 // Overwrite the config and try Excel R1C1.
8083 eConv = FormulaGrammar::CONV_XL_R1C1;
8084 bTryXlA1 = false;
8085 }
8086
8087
8088 const ScAddress::Details aDetails( bTryXlA1 ? FormulaGrammar::CONV_OOO : eConv, aPos );
8089 const ScAddress::Details aDetailsXlA1( FormulaGrammar::CONV_XL_A1, aPos );
8090 SCTAB nTab = aPos.Tab();
8091 OUString sRefStr = GetString().getString();
8092 ScRefAddress aRefAd, aRefAd2;
8093 ScAddress::ExternalInfo aExtInfo;
8094 if ( ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo) ||
8095 ( bTryXlA1 && ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd,
8096 aRefAd2, aDetailsXlA1, &aExtInfo) ) )
8097 {
8098 if (aExtInfo.mbExternal)
8099 {
8100 PushExternalDoubleRef(
8101 aExtInfo.mnFileId, aExtInfo.maTabName,
8102 aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(),
8103 aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab());
8104 }
8105 else
8106 PushDoubleRef( aRefAd, aRefAd2);
8107 }
8108 else if ( ConvertSingleRef(mrDoc, sRefStr, nTab, aRefAd, aDetails, &aExtInfo) ||
8109 ( bTryXlA1 && ConvertSingleRef (mrDoc, sRefStr, nTab, aRefAd,
8110 aDetailsXlA1, &aExtInfo) ) )
8111 {
8112 if (aExtInfo.mbExternal)
8113 {
8114 PushExternalSingleRef(
8115 aExtInfo.mnFileId, aExtInfo.maTabName, aRefAd.Col(), aRefAd.Row(), aRefAd.Tab());
8116 }
8117 else
8118 PushSingleRef( aRefAd);
8119 }
8120 else
8121 {
8122 do
8123 {
8124 ScRangeData* pData = ScRangeStringConverter::GetRangeDataFromString(sRefStr, nTab, mrDoc);
8125 if (!pData)
8126 break;
8127
8128 // We need this in order to obtain a good range.
8129 pData->ValidateTabRefs();
8130
8131 ScRange aRange;
8132
8133 // This is the usual way to treat named ranges containing
8134 // relative references.
8135 if (!pData->IsReference( aRange, aPos))
8136 break;
8137
8138 if (aRange.aStart == aRange.aEnd)
8139 PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8140 aRange.aStart.Tab());
8141 else
8142 PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8143 aRange.aStart.Tab(), aRange.aEnd.Col(),
8144 aRange.aEnd.Row(), aRange.aEnd.Tab());
8145
8146 // success!
8147 return;
8148 }
8149 while (false);
8150
8151 do
8152 {
8153 OUString aName( ScGlobal::getCharClassPtr()->uppercase( sRefStr));
8154 ScDBCollection::NamedDBs& rDBs = mrDoc.GetDBCollection()->getNamedDBs();
8155 const ScDBData* pData = rDBs.findByUpperName( aName);
8156 if (!pData)
8157 break;
8158
8159 ScRange aRange;
8160 pData->GetArea( aRange);
8161
8162 // In Excel, specifying a table name without [] resolves to the
8163 // same as with [], a range that excludes header and totals
8164 // rows and contains only data rows. Do the same.
8165 if (pData->HasHeader())
8166 aRange.aStart.IncRow();
8167 if (pData->HasTotals())
8168 aRange.aEnd.IncRow(-1);
8169
8170 if (aRange.aStart.Row() > aRange.aEnd.Row())
8171 break;
8172
8173 if (aRange.aStart == aRange.aEnd)
8174 PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8175 aRange.aStart.Tab());
8176 else
8177 PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8178 aRange.aStart.Tab(), aRange.aEnd.Col(),
8179 aRange.aEnd.Row(), aRange.aEnd.Tab());
8180
8181 // success!
8182 return;
8183 }
8184 while (false);
8185
8186 // It may be even a TableRef.
8187 // Anything else that resolves to one reference could be added
8188 // here, but we don't want to compile every arbitrary string. This
8189 // is already nasty enough...
8190 sal_Int32 nIndex = sRefStr.indexOf('[');
8191 if (nIndex >= 0 && sRefStr.indexOf(']',nIndex+1) > nIndex)
8192 {
8193 do
8194 {
8195 ScCompiler aComp( mrDoc, aPos, mrDoc.GetGrammar());
8196 aComp.SetRefConvention( eConv); // must be after grammar
8197 std::unique_ptr<ScTokenArray> pTokArr( aComp.CompileString( sRefStr));
8198
8199 // Whatever... use only the specific case.
8200 if (!pTokArr->HasOpCode( ocTableRef))
8201 break;
8202
8203 aComp.CompileTokenArray();
8204
8205 // A syntactically valid reference will generate exactly
8206 // one RPN token, a reference or error. Discard everything
8207 // else as error.
8208 if (pTokArr->GetCodeLen() != 1)
8209 break;
8210
8211 ScTokenRef xTok( pTokArr->FirstRPNToken());
8212 if (!xTok)
8213 break;
8214
8215 switch (xTok->GetType())
8216 {
8217 case svSingleRef:
8218 case svDoubleRef:
8219 case svError:
8220 PushTokenRef( xTok);
8221 // success!
8222 return;
8223 default:
8224 ; // nothing
8225 }
8226 }
8227 while (false);
8228 }
8229
8230 PushError( FormulaError::NoRef);
8231 }
8232}
8233
8234void ScInterpreter::ScAddressFunc()
8235{
8236 OUString sTabStr;
8237
8238 sal_uInt8 nParamCount = GetByte();
8239 if( !MustHaveParamCount( nParamCount, 2, 5 ) )
8240 return;
8241
8242 if( nParamCount >= 5 )
8243 sTabStr = GetString().getString();
8244
8245 FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; // default
8246 if (nParamCount >= 4 && 0.0 == GetDoubleWithDefault( 1.0))
8247 eConv = FormulaGrammar::CONV_XL_R1C1;
8248 else
8249 {
8250 // If A1 syntax is requested then the actual sheet separator and format
8251 // convention depends on the syntax configured for INDIRECT to match
8252 // that, and if it is unspecified then the document's address syntax.
8253 FormulaGrammar::AddressConvention eForceConv = maCalcConfig.meStringRefAddressSyntax;
8254 if (eForceConv == FormulaGrammar::CONV_UNSPECIFIED)
8255 eForceConv = mrDoc.GetAddressConvention();
8256 if (eForceConv == FormulaGrammar::CONV_XL_A1 || eForceConv == FormulaGrammar::CONV_XL_R1C1)
8257 eConv = FormulaGrammar::CONV_XL_A1; // for anything Excel use Excel A1
8258 }
8259
8260 ScRefFlags nFlags = ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS; // default
8261 if( nParamCount >= 3 )
8262 {
8263 sal_Int32 n = GetInt32WithDefault(1);
8264 switch ( n )
8265 {
8266 default :
8267 PushNoValue();
8268 return;
8269
8270 case 5:
8271 case 1 : break; // default
8272 case 6:
8273 case 2 : nFlags = ScRefFlags::ROW_ABS; break;
8274 case 7:
8275 case 3 : nFlags = ScRefFlags::COL_ABS; break;
8276 case 8:
8277 case 4 : nFlags = ScRefFlags::ZERO; break; // both relative
8278 }
8279 }
8280 nFlags |= ScRefFlags::VALID | ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID;
8281
8282 SCCOL nCol = static_cast<SCCOL>(GetInt16());
8283 SCROW nRow = static_cast<SCROW>(GetInt32());
8284 if( eConv == FormulaGrammar::CONV_XL_R1C1 )
8285 {
8286 // YUCK! The XL interface actually treats rel R1C1 refs differently
8287 // than A1
8288 if( !(nFlags & ScRefFlags::COL_ABS) )
8289 nCol += aPos.Col() + 1;
8290 if( !(nFlags & ScRefFlags::ROW_ABS) )
8291 nRow += aPos.Row() + 1;
8292 }
8293
8294 --nCol;
8295 --nRow;
8296 if (nGlobalError != FormulaError::NONE || !mrDoc.ValidCol( nCol) || !mrDoc.ValidRow( nRow))
8297 {
8298 PushIllegalArgument();
8299 return;
8300 }
8301
8302 const ScAddress::Details aDetails( eConv, aPos );
8303 const ScAddress aAdr( nCol, nRow, 0);
8304 OUString aRefStr(aAdr.Format(nFlags, &mrDoc, aDetails));
8305
8306 if( nParamCount >= 5 && !sTabStr.isEmpty() )
8307 {
8308 OUString aDoc;
8309 if (eConv == FormulaGrammar::CONV_OOO)
8310 {
8311 // Isolate Tab from 'Doc'#Tab
8312 sal_Int32 nPos = ScCompiler::GetDocTabPos( sTabStr);
8313 if (nPos != -1)
8314 {
8315 if (sTabStr[nPos+1] == '$')
8316 ++nPos; // also split 'Doc'#$Tab
8317 aDoc = sTabStr.copy( 0, nPos+1);
8318 sTabStr = sTabStr.copy( nPos+1);
8319 }
8320 }
8321 /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may
8322 * need some extra handling to isolate Tab from Doc. */
8323 if (sTabStr[0] != '\'' || !sTabStr.endsWith("'"))
8324 ScCompiler::CheckTabQuotes( sTabStr, eConv);
8325 if (!aDoc.isEmpty())
8326 sTabStr = aDoc + sTabStr;
8327 sTabStr += (eConv == FormulaGrammar::CONV_XL_R1C1 || eConv == FormulaGrammar::CONV_XL_A1) ?
8328 OUStringLiteral(u"!") : OUStringLiteral(u".");
8329 sTabStr += aRefStr;
8330 PushString( sTabStr );
8331 }
8332 else
8333 PushString( aRefStr );
8334}
8335
8336void ScInterpreter::ScOffset()
8337{
8338 sal_uInt8 nParamCount = GetByte();
8339 if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
8340 return;
8341
8342 bool bNewWidth = false;
8343 bool bNewHeight = false;
8344 sal_Int32 nColNew = 1, nRowNew = 1;
8345 if (nParamCount == 5)
8346 {
8347 if (IsMissing())
8348 PopError();
8349 else
8350 {
8351 nColNew = GetInt32();
8352 bNewWidth = true;
8353 }
8354 }
8355 if (nParamCount >= 4)
8356 {
8357 if (IsMissing())
8358 PopError();
8359 else
8360 {
8361 nRowNew = GetInt32();
8362 bNewHeight = true;
8363 }
8364 }
8365 sal_Int32 nColPlus = GetInt32();
8366 sal_Int32 nRowPlus = GetInt32();
8367 if (nGlobalError != FormulaError::NONE)
8368 {
8369 PushError( nGlobalError);
8370 return;
8371 }
8372 if (nColNew <= 0 || nRowNew <= 0)
8373 {
8374 PushIllegalArgument();
8375 return;
8376 }
8377 SCCOL nCol1(0);
8378 SCROW nRow1(0);
8379 SCTAB nTab1(0);
8380 SCCOL nCol2(0);
8381 SCROW nRow2(0);
8382 SCTAB nTab2(0);
8383 switch (GetStackType())
8384 {
8385 case svSingleRef:
8386 {
8387 PopSingleRef(nCol1, nRow1, nTab1);
8388 if (!bNewWidth && !bNewHeight)
8389 {
8390 nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1) + nColPlus);
8391 nRow1 = static_cast<SCROW>(static_cast<long>(nRow1) + nRowPlus);
8392 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1))
8393 PushIllegalArgument();
8394 else
8395 PushSingleRef(nCol1, nRow1, nTab1);
8396 }
8397 else
8398 {
8399 nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColPlus);
8400 nRow1 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowPlus);
8401 nCol2 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColNew-1);
8402 nRow2 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowNew-1);
8403 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8404 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2))
8405 PushIllegalArgument();
8406 else
8407 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
8408 }
8409 break;
8410 }
8411 case svExternalSingleRef:
8412 {
8413 sal_uInt16 nFileId;
8414 OUString aTabName;
8415 ScSingleRefData aRef;
8416 PopExternalSingleRef(nFileId, aTabName, aRef);
8417 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
8418 nCol1 = aAbsRef.Col();
8419 nRow1 = aAbsRef.Row();
8420 nTab1 = aAbsRef.Tab();
8421
8422 if (!bNewWidth && !bNewHeight)
8423 {
8424 nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1) + nColPlus);
8425 nRow1 = static_cast<SCROW>(static_cast<long>(nRow1) + nRowPlus);
8426 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1))
8427 PushIllegalArgument();
8428 else
8429 PushExternalSingleRef(nFileId, aTabName, nCol1, nRow1, nTab1);
8430 }
8431 else
8432 {
8433 nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColPlus);
8434 nRow1 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowPlus);
8435 nCol2 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColNew-1);
8436 nRow2 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowNew-1);
8437 nTab2 = nTab1;
8438 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8439 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2))
8440 PushIllegalArgument();
8441 else
8442 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8443 }
8444 break;
8445 }
8446 case svDoubleRef:
8447 {
8448 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8449 if (!bNewWidth)
8450 nColNew = nCol2 - nCol1 + 1;
8451 if (!bNewHeight)
8452 nRowNew = nRow2 - nRow1 + 1;
8453 nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColPlus);
8454 nRow1 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowPlus);
8455 nCol2 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColNew-1);
8456 nRow2 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowNew-1);
8457 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8458 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2)
8459 PushIllegalArgument();
8460 else
8461 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
8462 break;
8463 }
8464 case svExternalDoubleRef:
8465 {
8466 sal_uInt16 nFileId;
8467 OUString aTabName;
8468 ScComplexRefData aRef;
8469 PopExternalDoubleRef(nFileId, aTabName, aRef);
8470 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
8471 nCol1 = aAbs.aStart.Col();
8472 nRow1 = aAbs.aStart.Row();
8473 nTab1 = aAbs.aStart.Tab();
8474 nCol2 = aAbs.aEnd.Col();
8475 nRow2 = aAbs.aEnd.Row();
8476 nTab2 = aAbs.aEnd.Tab();
8477 if (!bNewWidth)
8478 nColNew = nCol2 - nCol1 + 1;
8479 if (!bNewHeight)
8480 nRowNew = nRow2 - nRow1 + 1;
8481 nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColPlus);
8482 nRow1 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowPlus);
8483 nCol2 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColNew-1);
8484 nRow2 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowNew-1);
8485 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8486 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2)
8487 PushIllegalArgument();
8488 else
8489 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8490 break;
8491 }
8492 default:
8493 PushIllegalParameter();
8494 break;
8495 } // end switch
8496}
8497
8498void ScInterpreter::ScIndex()
8499{
8500 sal_uInt8 nParamCount = GetByte();
8501 if ( !MustHaveParamCount( nParamCount, 1, 4 ) )
8502 return;
8503
8504 sal_uInt32 nArea;
8505 size_t nAreaCount;
8506 SCCOL nCol;
8507 SCROW nRow;
8508 if (nParamCount == 4)
8509 nArea = GetUInt32();
8510 else
8511 nArea = 1;
8512 if (nParamCount >= 3)
8513 nCol = static_cast<SCCOL>(GetInt16());
8514 else
8515 nCol = 0;
8516 if (nParamCount >= 2)
8517 nRow = static_cast<SCROW>(GetInt32());
8518 else
8519 nRow = 0;
8520 if (GetStackType() == svRefList)
8521 nAreaCount = (sp ? pStack[sp-1]->GetRefList()->size() : 0);
8522 else
8523 nAreaCount = 1; // one reference or array or whatever
8524 if (nGlobalError != FormulaError::NONE || nAreaCount == 0 || static_cast<size_t>(nArea) > nAreaCount)
8525 {
8526 PushError( FormulaError::NoRef);
8527 return;
8528 }
8529 else if (nArea < 1 || nCol < 0 || nRow < 0)
8530 {
8531 PushIllegalArgument();
8532 return;
8533 }
8534 switch (GetStackType())
8535 {
8536 case svMatrix:
8537 case svExternalSingleRef:
8538 case svExternalDoubleRef:
8539 {
8540 if (nArea != 1)
8541 SetError(FormulaError::IllegalArgument);
8542 sal_uInt16 nOldSp = sp;
8543 ScMatrixRef pMat = GetMatrix();
8544 if (pMat)
8545 {
8546 SCSIZE nC, nR;
8547 pMat->GetDimensions(nC, nR);
8548 // Access one element of a vector independent of col/row
8549 // orientation?
8550 bool bVector = ((nCol == 0 || nRow == 0) && (nC == 1 || nR == 1));
8551 SCSIZE nElement = ::std::max( static_cast<SCSIZE>(nCol),
8552 static_cast<SCSIZE>(nRow));
8553 if (nC == 0 || nR == 0 ||
8554 (!bVector && (o3tl::make_unsigned(nCol) > nC ||
8555 o3tl::make_unsigned(nRow) > nR)) ||
8556 (bVector && nElement > nC * nR))
8557 PushIllegalArgument();
8558 else if (nCol == 0 && nRow == 0)
8559 sp = nOldSp;
8560 else if (bVector)
8561 {
8562 --nElement;
8563 if (pMat->IsStringOrEmpty( nElement))
8564 PushString( pMat->GetString(nElement).getString());
8565 else
8566 PushDouble( pMat->GetDouble( nElement));
8567 }
8568 else if (nCol == 0)
8569 {
8570 ScMatrixRef pResMat = GetNewMat(nC, 1);
8571 if (pResMat)
8572 {
8573 SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1);
8574 for (SCSIZE i = 0; i < nC; i++)
8575 if (!pMat->IsStringOrEmpty(i, nRowMinus1))
8576 pResMat->PutDouble(pMat->GetDouble(i,
8577 nRowMinus1), i, 0);
8578 else
8579 pResMat->PutString(pMat->GetString(i, nRowMinus1), i, 0);
8580
8581 PushMatrix(pResMat);
8582 }
8583 else
8584 PushIllegalArgument();
8585 }
8586 else if (nRow == 0)
8587 {
8588 ScMatrixRef pResMat = GetNewMat(1, nR);
8589 if (pResMat)
8590 {
8591 SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1);
8592 for (SCSIZE i = 0; i < nR; i++)
8593 if (!pMat->IsStringOrEmpty(nColMinus1, i))
8594 pResMat->PutDouble(pMat->GetDouble(nColMinus1,
8595 i), i);
8596 else
8597 pResMat->PutString(pMat->GetString(nColMinus1, i), i);
8598 PushMatrix(pResMat);
8599 }
8600 else
8601 PushIllegalArgument();
8602 }
8603 else
8604 {
8605 if (!pMat->IsStringOrEmpty( static_cast<SCSIZE>(nCol-1),
8606 static_cast<SCSIZE>(nRow-1)))
8607 PushDouble( pMat->GetDouble(
8608 static_cast<SCSIZE>(nCol-1),
8609 static_cast<SCSIZE>(nRow-1)));
8610 else
8611 PushString( pMat->GetString(
8612 static_cast<SCSIZE>(nCol-1),
8613 static_cast<SCSIZE>(nRow-1)).getString());
8614 }
8615 }
8616 }
8617 break;
8618 case svSingleRef:
8619 {
8620 SCCOL nCol1 = 0;
8621 SCROW nRow1 = 0;
8622 SCTAB nTab1 = 0;
8623 PopSingleRef( nCol1, nRow1, nTab1);
8624 if (nCol > 1 || nRow > 1)
8625 PushIllegalArgument();
8626 else
8627 PushSingleRef( nCol1, nRow1, nTab1);
8628 }
8629 break;
8630 case svDoubleRef:
8631 case svRefList:
8632 {
8633 SCCOL nCol1 = 0;
8634 SCROW nRow1 = 0;
8635 SCTAB nTab1 = 0;
8636 SCCOL nCol2 = 0;
8637 SCROW nRow2 = 0;
8638 SCTAB nTab2 = 0;
8639 bool bRowArray = false;
8640 if (GetStackType() == svRefList)
8641 {
8642 FormulaConstTokenRef xRef = PopToken();
8643 if (nGlobalError != FormulaError::NONE || !xRef)
8644 {
8645 PushIllegalParameter();
8646 return;
8647 }
8648 ScRange aRange( ScAddress::UNINITIALIZED);
8649 DoubleRefToRange( (*(xRef->GetRefList()))[nArea-1], aRange);
8650 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8651 if ( nParamCount == 2 && nRow1 == nRow2 )
8652 bRowArray = true;
8653 }
8654 else
8655 {
8656 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8657 if ( nParamCount == 2 && nRow1 == nRow2 )
8658 bRowArray = true;
8659 }
8660 if ( nTab1 != nTab2 ||
8661 (nCol > 0 && nCol1+nCol-1 > nCol2) ||
8662 (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) ||
8663 ( nRow > nCol2 - nCol1 + 1 && bRowArray ))
8664 PushIllegalArgument();
8665 else if (nCol == 0 && nRow == 0)
8666 {
8667 if ( nCol1 == nCol2 && nRow1 == nRow2 )
8668 PushSingleRef( nCol1, nRow1, nTab1 );
8669 else
8670 PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 );
8671 }
8672 else if (nRow == 0)
8673 {
8674 if ( nRow1 == nRow2 )
8675 PushSingleRef( nCol1+nCol-1, nRow1, nTab1 );
8676 else
8677 PushDoubleRef( nCol1+nCol-1, nRow1, nTab1,
8678 nCol1+nCol-1, nRow2, nTab1 );
8679 }
8680 else if (nCol == 0)
8681 {
8682 if ( nCol1 == nCol2 )
8683 PushSingleRef( nCol1, nRow1+nRow-1, nTab1 );
8684 else if ( bRowArray )
8685 {
8686 nCol =static_cast<SCCOL>(nRow);
8687 nRow = 1;
8688 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
8689 }
8690 else
8691 PushDoubleRef( nCol1, nRow1+nRow-1, nTab1,
8692 nCol2, nRow1+nRow-1, nTab1);
8693 }
8694 else
8695 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
8696 }
8697 break;
8698 default:
8699 PushIllegalParameter();
8700 }
8701}
8702
8703void ScInterpreter::ScMultiArea()
8704{
8705 // Legacy support, convert to RefList
8706 sal_uInt8 nParamCount = GetByte();
8707 if (MustHaveParamCountMin( nParamCount, 1))
8708 {
8709 while (nGlobalError == FormulaError::NONE && nParamCount-- > 1)
8710 {
8711 ScUnionFunc();
8712 }
8713 }
8714}
8715
8716void ScInterpreter::ScAreas()
8717{
8718 sal_uInt8 nParamCount = GetByte();
8719 if (!MustHaveParamCount( nParamCount, 1))
8720 return;
8721
8722 size_t nCount = 0;
8723 switch (GetStackType())
8724 {
8725 case svSingleRef:
8726 {
8727 FormulaConstTokenRef xT = PopToken();
8728 ValidateRef( *xT->GetSingleRef());
8729 ++nCount;
8730 }
8731 break;
8732 case svDoubleRef:
8733 {
8734 FormulaConstTokenRef xT = PopToken();
8735 ValidateRef( *xT->GetDoubleRef());
8736 ++nCount;
8737 }
8738 break;
8739 case svRefList:
8740 {
8741 FormulaConstTokenRef xT = PopToken();
8742 ValidateRef( *(xT->GetRefList()));
8743 nCount += xT->GetRefList()->size();
8744 }
8745 break;
8746 default:
8747 SetError( FormulaError::IllegalParameter);
8748 }
8749 PushDouble( double(nCount));
8750}
8751
8752void ScInterpreter::ScCurrency()
8753{
8754 sal_uInt8 nParamCount = GetByte();
8755 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
8756 return;
8757
8758 OUString aStr;
8759 double fDec;
8760 if (nParamCount == 2)
8761 {
8762 fDec = ::rtl::math::approxFloor(GetDouble());
8763 if (fDec < -15.0 || fDec > 15.0)
8764 {
8765 PushIllegalArgument();
8766 return;
8767 }
8768 }
8769 else
8770 fDec = 2.0;
8771 double fVal = GetDouble();
8772 double fFac;
8773 if ( fDec != 0.0 )
8774 fFac = pow( double(10), fDec );
8775 else
8776 fFac = 1.0;
8777 if (fVal < 0.0)
8778 fVal = ceil(fVal*fFac-0.5)/fFac;
8779 else
8780 fVal = floor(fVal*fFac+0.5)/fFac;
8781 const Color* pColor = nullptr;
8782 if ( fDec < 0.0 )
8783 fDec = 0.0;
8784 sal_uLong nIndex = pFormatter->GetStandardFormat(
8785 SvNumFormatType::CURRENCY,
8786 ScGlobal::eLnge);
8787 if ( static_cast<sal_uInt16>(fDec) != pFormatter->GetFormatPrecision( nIndex ) )
8788 {
8789 OUString sFormatString = pFormatter->GenerateFormat(
8790 nIndex,
8791 ScGlobal::eLnge,
8792 true, // with thousands separator
8793 false, // not red
8794 static_cast<sal_uInt16>(fDec));// decimal places
8795 if (!pFormatter->GetPreviewString(sFormatString,
8796 fVal,
8797 aStr,
8798 &pColor,
8799 ScGlobal::eLnge))
8800 SetError(FormulaError::IllegalArgument);
8801 }
8802 else
8803 {
8804 pFormatter->GetOutputString(fVal, nIndex, aStr, &pColor);
8805 }
8806 PushString(aStr);
8807}
8808
8809void ScInterpreter::ScReplace()
8810{
8811 if ( !MustHaveParamCount( GetByte(), 4 ) )
8812 return;
8813
8814 OUString aNewStr = GetString().getString();
8815 sal_Int32 nCount = GetStringPositionArgument();
8816 sal_Int32 nPos = GetStringPositionArgument();
8817 OUString aOldStr = GetString().getString();
8818 if (nPos < 1 || nCount < 0)
8819 PushIllegalArgument();
8820 else
8821 {
8822 sal_Int32 nLen = aOldStr.getLength();
8823 if (nPos > nLen + 1)
8824 nPos = nLen + 1;
8825 if (nCount > nLen - nPos + 1)
8826 nCount = nLen - nPos + 1;
8827 sal_Int32 nIdx = 0;
8828 sal_Int32 nCnt = 0;
8829 while ( nIdx < nLen && nPos > nCnt + 1 )
8830 {
8831 aOldStr.iterateCodePoints( &nIdx );
8832 ++nCnt;
8833 }
8834 sal_Int32 nStart = nIdx;
8835 while ( nIdx < nLen && nPos + nCount - 1 > nCnt )
8836 {
8837 aOldStr.iterateCodePoints( &nIdx );
8838 ++nCnt;
8839 }
8840 aOldStr = aOldStr.replaceAt( nStart, nIdx - nStart, "" );
8841 if ( CheckStringResultLen( aOldStr, aNewStr ) )
8842 aOldStr = aOldStr.replaceAt( nStart, 0, aNewStr );
8843 PushString( aOldStr );
8844 }
8845}
8846
8847void ScInterpreter::ScFixed()
8848{
8849 sal_uInt8 nParamCount = GetByte();
8850 if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
8851 return;
8852
8853 OUString aStr;
8854 double fDec;
8855 bool bThousand;
8856 if (nParamCount == 3)
8857 bThousand = !GetBool(); // Param true: no thousands separator
8858 else
8859 bThousand = true;
8860 if (nParamCount >= 2)
8861 {
8862 fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 ));
8863 if (fDec < -15.0 || fDec > 15.0)
8864 {
8865 PushIllegalArgument();
8866 return;
8867 }
8868 }
8869 else
8870 fDec = 2.0;
8871 double fVal = GetDouble();
8872 double fFac;
8873 if ( fDec != 0.0 )
8874 fFac = pow( double(10), fDec );
8875 else
8876 fFac = 1.0;
8877 if (fVal < 0.0)
8878 fVal = ceil(fVal*fFac-0.5)/fFac;
8879 else
8880 fVal = floor(fVal*fFac+0.5)/fFac;
8881 const Color* pColor = nullptr;
8882 if (fDec < 0.0)
8883 fDec = 0.0;
8884 sal_uLong nIndex = pFormatter->GetStandardFormat(
8885 SvNumFormatType::NUMBER,
8886 ScGlobal::eLnge);
8887 OUString sFormatString = pFormatter->GenerateFormat(
8888 nIndex,
8889 ScGlobal::eLnge,
8890 bThousand, // with thousands separator
8891 false, // not red
8892 static_cast<sal_uInt16>(fDec));// decimal places
8893 if (!pFormatter->GetPreviewString(sFormatString,
8894 fVal,
8895 aStr,
8896 &pColor,
8897 ScGlobal::eLnge))
8898 PushIllegalArgument();
8899 else
8900 PushString(aStr);
8901}
8902
8903void ScInterpreter::ScFind()
8904{
8905 sal_uInt8 nParamCount = GetByte();
8906 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
8907 return;
8908
8909 sal_Int32 nCnt;
8910 if (nParamCount == 3)
8911 nCnt = GetDouble();
8912 else
8913 nCnt = 1;
8914 OUString sStr = GetString().getString();
8915 if (nCnt < 1 || nCnt > sStr.getLength())
8916 PushNoValue();
8917 else
8918 {
8919 sal_Int32 nPos = sStr.indexOf(GetString().getString(), nCnt - 1);
8920 if (nPos == -1)
8921 PushNoValue();
8922 else
8923 {
8924 sal_Int32 nIdx = 0;
8925 nCnt = 0;
8926 while ( nIdx <= nPos )
8927 {
8928 sStr.iterateCodePoints( &nIdx );
8929 ++nCnt;
8930 }
8931 PushDouble( static_cast<double>(nCnt) );
8932 }
8933 }
8934}
8935
8936void ScInterpreter::ScExact()
8937{
8938 nFuncFmtType = SvNumFormatType::LOGICAL;
8939 if ( MustHaveParamCount( GetByte(), 2 ) )
8940 {
8941 svl::SharedString s1 = GetString();
8942 svl::SharedString s2 = GetString();
8943 PushInt( int(s1.getData() == s2.getData()) );
8944 }
8945}
8946
8947void ScInterpreter::ScLeft()
8948{
8949 sal_uInt8 nParamCount = GetByte();
8950 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
8951 return;
8952
8953 sal_Int32 n;
8954 if (nParamCount == 2)
8955 {
8956 n = GetStringPositionArgument();
8957 if (n < 0)
8958 {
8959 PushIllegalArgument();
8960 return ;
8961 }
8962 }
8963 else
8964 n = 1;
8965 OUString aStr = GetString().getString();
8966 sal_Int32 nIdx = 0;
8967 sal_Int32 nCnt = 0;
8968 while ( nIdx < aStr.getLength() && n > nCnt++ )
8969 aStr.iterateCodePoints( &nIdx );
8970 aStr = aStr.copy( 0, nIdx );
8971 PushString( aStr );
8972}
8973
8974namespace {
8975
8976struct UBlockScript {
8977 UBlockCode from;
8978 UBlockCode to;
8979};
8980
8981}
8982
8983const UBlockScript scriptList[] = {
8984 {UBLOCK_HANGUL_JAMO, UBLOCK_HANGUL_JAMO},
8985 {UBLOCK_CJK_RADICALS_SUPPLEMENT, UBLOCK_HANGUL_SYLLABLES},
8986 {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS,UBLOCK_CJK_RADICALS_SUPPLEMENT },
8987 {UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS,UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS},
8988 {UBLOCK_CJK_COMPATIBILITY_FORMS, UBLOCK_CJK_COMPATIBILITY_FORMS},
8989 {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS},
8990 {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT},
8991 {UBLOCK_CJK_STROKES, UBLOCK_CJK_STROKES}
8992};
8993static bool IsDBCS(sal_Unicode currentChar)
8994{
8995 // for the locale of ja-JP, character U+0x005c and U+0x20ac should be ScriptType::Asian
8996 if( (currentChar == 0x005c || currentChar == 0x20ac) &&
8997 (MsLangId::getSystemLanguage() == LANGUAGE_JAPANESELanguageType(0x0411)) )
8998 return true;
8999 sal_uInt16 i;
9000 bool bRet = false;
9001 UBlockCode block = ublock_getCodeublock_getCode_67(currentChar);
9002 for ( i = 0; i < SAL_N_ELEMENTS(scriptList)(sizeof(sal_n_array_size(scriptList))); i++) {
9003 if (block <= scriptList[i].to) break;
9004 }
9005 bRet = (i < SAL_N_ELEMENTS(scriptList)(sizeof(sal_n_array_size(scriptList))) && block >= scriptList[i].from);
9006 return bRet;
9007}
9008static sal_Int32 lcl_getLengthB( const OUString &str, sal_Int32 nPos )
9009{
9010 sal_Int32 index = 0;
9011 sal_Int32 length = 0;
9012 while ( index < nPos )
9013 {
9014 if (IsDBCS(str[index]))
9015 length += 2;
9016 else
9017 length++;
9018 index++;
9019 }
9020 return length;
9021}
9022static sal_Int32 getLengthB(const OUString &str)
9023{
9024 if(str.isEmpty())
9025 return 0;
9026 else
9027 return lcl_getLengthB( str, str.getLength() );
9028}
9029void ScInterpreter::ScLenB()
9030{
9031 PushDouble( getLengthB(GetString().getString()) );
9032}
9033static OUString lcl_RightB(const OUString &rStr, sal_Int32 n)
9034{
9035 if( n < getLengthB(rStr) )
9036 {
9037 OUStringBuffer aBuf(rStr);
9038 sal_Int32 index = aBuf.getLength();
9039 while(index-- >= 0)
9040 {
9041 if(0 == n)
9042 {
9043 aBuf.remove( 0, index + 1);
9044 break;
9045 }
9046 if(-1 == n)
9047 {
9048 aBuf.remove( 0, index + 2 );
9049 aBuf.insert( 0, " ");
9050 break;
9051 }
9052 if(IsDBCS(aBuf[index]))
9053 n -= 2;
9054 else
9055 n--;
9056 }
9057 return aBuf.makeStringAndClear();
9058 }
9059 return rStr;
9060}
9061void ScInterpreter::ScRightB()
9062{
9063 sal_uInt8 nParamCount = GetByte();
9064 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9065 return;
9066
9067 sal_Int32 n;
9068 if (nParamCount == 2)
9069 {
9070 n = GetStringPositionArgument();
9071 if (n < 0)
9072 {
9073 PushIllegalArgument();
9074 return ;
9075 }
9076 }
9077 else
9078 n = 1;
9079 OUString aStr(lcl_RightB(GetString().getString(), n));
9080 PushString( aStr );
9081}
9082static OUString lcl_LeftB(const OUString &rStr, sal_Int32 n)
9083{
9084 if( n < getLengthB(rStr) )
9085 {
9086 OUStringBuffer aBuf(rStr);
9087 sal_Int32 index = -1;
9088 while(index++ < aBuf.getLength())
9089 {
9090 if(0 == n)
9091 {
9092 aBuf.truncate(index);
9093 break;
9094 }
9095 if(-1 == n)
9096 {
9097 aBuf.truncate( index - 1 );
9098 aBuf.append(" ");
9099 break;
9100 }
9101 if(IsDBCS(aBuf[index]))
9102 n -= 2;
9103 else
9104 n--;
9105 }
9106 return aBuf.makeStringAndClear();
9107 }
9108 return rStr;
9109}
9110void ScInterpreter::ScLeftB()
9111{
9112 sal_uInt8 nParamCount = GetByte();
9113 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9114 return;
9115
9116 sal_Int32 n;
9117 if (nParamCount == 2)
9118 {
9119 n = GetStringPositionArgument();
9120 if (n < 0)
9121 {
9122 PushIllegalArgument();
9123 return ;
9124 }
9125 }
9126 else
9127 n = 1;
9128 OUString aStr(lcl_LeftB(GetString().getString(), n));
9129 PushString( aStr );
9130}
9131void ScInterpreter::ScMidB()
9132{
9133 if ( !MustHaveParamCount( GetByte(), 3 ) )
9134 return;
9135
9136 const sal_Int32 nCount = GetStringPositionArgument();
9137 const sal_Int32 nStart = GetStringPositionArgument();
9138 OUString aStr = GetString().getString();
9139 if (nStart < 1 || nCount < 0)
9140 PushIllegalArgument();
9141 else
9142 {
9143
9144 aStr = lcl_LeftB(aStr, nStart + nCount - 1);
9145 sal_Int32 nCnt = getLengthB(aStr) - nStart + 1;
9146 aStr = lcl_RightB(aStr, std::max<sal_Int32>(nCnt,0));
9147 PushString(aStr);
9148 }
9149}
9150
9151void ScInterpreter::ScReplaceB()
9152{
9153 if ( !MustHaveParamCount( GetByte(), 4 ) )
9154 return;
9155
9156 OUString aNewStr = GetString().getString();
9157 const sal_Int32 nCount = GetStringPositionArgument();
9158 const sal_Int32 nPos = GetStringPositionArgument();
9159 OUString aOldStr = GetString().getString();
9160 int nLen = getLengthB( aOldStr );
9161 if (nPos < 1.0 || nPos > nLen || nCount < 0.0 || nPos + nCount -1 > nLen)
9162 PushIllegalArgument();
9163 else
9164 {
9165 // REPLACEB(aOldStr;nPos;nCount;aNewStr) is the same as
9166 // LEFTB(aOldStr;nPos-1) & aNewStr & RIGHT(aOldStr;LENB(aOldStr)-(nPos - 1)-nCount)
9167 OUString aStr1 = lcl_LeftB( aOldStr, nPos - 1 );
9168 OUString aStr3 = lcl_RightB( aOldStr, nLen - nPos - nCount + 1);
9169
9170 PushString( aStr1 + aNewStr + aStr3 );
9171 }
9172}
9173
9174void ScInterpreter::ScFindB()
9175{
9176 sal_uInt8 nParamCount = GetByte();
9177 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
9178 return;
9179
9180 sal_Int32 nStart;
9181 if ( nParamCount == 3 )
9182 nStart = GetStringPositionArgument();
9183 else
9184 nStart = 1;
9185 OUString aStr = GetString().getString();
9186 int nLen = getLengthB( aStr );
9187 OUString asStr = GetString().getString();
9188 int nsLen = getLengthB( asStr );
9189 if ( nStart < 1 || nStart > nLen - nsLen + 1 )
9190 PushIllegalArgument();
9191 else
9192 {
9193 // create a string from sStr starting at nStart
9194 OUString aBuf = lcl_RightB( aStr, nLen - nStart + 1 );
9195 // search aBuf for asStr
9196 sal_Int32 nPos = aBuf.indexOf( asStr, 0 );
9197 if ( nPos == -1 )
9198 PushNoValue();
9199 else
9200 {
9201 // obtain byte value of nPos
9202 int nBytePos = lcl_getLengthB( aBuf, nPos );
9203 PushDouble( nBytePos + nStart );
9204 }
9205 }
9206}
9207
9208void ScInterpreter::ScSearchB()
9209{
9210 sal_uInt8 nParamCount = GetByte();
9211 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
9212 return;
9213
9214 sal_Int32 nStart;
9215 if ( nParamCount == 3 )
9216 {
9217 nStart = GetStringPositionArgument();
9218 if( nStart < 1 )
9219 {
9220 PushIllegalArgument();
9221 return;
9222 }
9223 }
9224 else
9225 nStart = 1;
9226 OUString aStr = GetString().getString();
9227 sal_Int32 nLen = getLengthB( aStr );
9228 OUString asStr = GetString().getString();
9229 sal_Int32 nsLen = nStart - 1;
9230 if( nsLen >= nLen )
9231 PushNoValue();
9232 else
9233 {
9234 // create a string from sStr starting at nStart
9235 OUString aSubStr( lcl_RightB( aStr, nLen - nStart + 1 ) );
9236 // search aSubStr for asStr
9237 sal_Int32 nPos = 0;
9238 sal_Int32 nEndPos = aSubStr.getLength();
9239 utl::SearchParam::SearchType eSearchType = DetectSearchType( asStr, mrDoc );
9240 utl::SearchParam sPar( asStr, eSearchType, false, '~', false );
9241 utl::TextSearch sT( sPar, *ScGlobal::getCharClassPtr() );
9242 if ( !sT.SearchForward( aSubStr, &nPos, &nEndPos ) )
9243 PushNoValue();
9244 else
9245 {
9246 // obtain byte value of nPos
9247 int nBytePos = lcl_getLengthB( aSubStr, nPos );
9248 PushDouble( nBytePos + nStart );
9249 }
9250 }
9251}
9252
9253void ScInterpreter::ScRight()
9254{
9255 sal_uInt8 nParamCount = GetByte();
9256 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9257 return;
9258
9259 sal_Int32 n;
9260 if (nParamCount == 2)
9261 {
9262 n = GetStringPositionArgument();
9263 if (n < 0)
9264 {
9265 PushIllegalArgument();
9266 return ;
9267 }
9268 }
9269 else
9270 n = 1;
9271 OUString aStr = GetString().getString();
9272 sal_Int32 nLen = aStr.getLength();
9273 if ( nLen <= n )
9274 PushString( aStr );
9275 else
9276 {
9277 sal_Int32 nIdx = nLen;
9278 sal_Int32 nCnt = 0;
9279 while ( nIdx > 0 && n > nCnt )
9280 {
9281 aStr.iterateCodePoints( &nIdx, -1 );
9282 ++nCnt;
9283 }
9284 aStr = aStr.copy( nIdx, nLen - nIdx );
9285 PushString( aStr );
9286 }
9287}
9288
9289void ScInterpreter::ScSearch()
9290{
9291 sal_uInt8 nParamCount = GetByte();
9292 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
9293 return;
9294
9295 sal_Int32 nStart;
9296 if (nParamCount == 3)
9297 {
9298 nStart = GetStringPositionArgument();
9299 if( nStart < 1 )
9300 {
9301 PushIllegalArgument();
9302 return;
9303 }
9304 }
9305 else
9306 nStart = 1;
9307 OUString sStr = GetString().getString();
9308 OUString SearchStr = GetString().getString();
9309 sal_Int32 nPos = nStart - 1;
9310 sal_Int32 nEndPos = sStr.getLength();
9311 if( nPos >= nEndPos )
9312 PushNoValue();
9313 else
9314 {
9315 utl::SearchParam::SearchType eSearchType = DetectSearchType( SearchStr, mrDoc );
9316 utl::SearchParam sPar(SearchStr, eSearchType, false, '~', false);
9317 utl::TextSearch sT( sPar, *ScGlobal::getCharClassPtr() );
9318 bool bBool = sT.SearchForward(sStr, &nPos, &nEndPos);
9319 if (!bBool)
9320 PushNoValue();
9321 else
9322 {
9323 sal_Int32 nIdx = 0;
9324 sal_Int32 nCnt = 0;
9325 while ( nIdx <= nPos )
9326 {
9327 sStr.iterateCodePoints( &nIdx );
9328 ++nCnt;
9329 }
9330 PushDouble( static_cast<double>(nCnt) );
9331 }
9332 }
9333}
9334
9335void ScInterpreter::ScRegex()
9336{
9337 const sal_uInt8 nParamCount = GetByte();
9338 if (!MustHaveParamCount( nParamCount, 2, 4))
9339 return;
9340
9341 // Flags are supported only for replacement, search match flags can be
9342 // individually and much more flexible set in the regular expression
9343 // pattern using (?ismwx-ismwx)
9344 bool bGlobalReplacement = false;
9345 sal_Int32 nOccurrence = 1; // default first occurrence, if any
9346 if (nParamCount == 4)
9347 {
9348 // Argument can be either string or double.
9349 double fOccurrence;
9350 svl::SharedString aFlagsString;
9351 bool bDouble;
9352 if (!IsMissing())
9353 bDouble = GetDoubleOrString( fOccurrence, aFlagsString);
9354 else
9355 {
9356 // For an omitted argument keep the default.
9357 PopError();
9358 bDouble = true;
9359 fOccurrence = nOccurrence;
9360 }
9361 if (nGlobalError != FormulaError::NONE)
9362 {
9363 PushError( nGlobalError);
9364 return;
9365 }
9366 if (bDouble)
9367 {
9368 if (!CheckStringPositionArgument( fOccurrence))
9369 {
9370 PushError( FormulaError::IllegalArgument);
9371 return;
9372 }
9373 nOccurrence = static_cast<sal_Int32>(fOccurrence);
9374 }
9375 else
9376 {
9377 const OUString aFlags( aFlagsString.getString());
9378 // Empty flags string is valid => no flag set.
9379 if (aFlags.getLength() > 1)
9380 {
9381 // Only one flag supported.
9382 PushIllegalArgument();
9383 return;
9384 }
9385 if (aFlags.getLength() == 1)
9386 {
9387 if (aFlags.indexOf('g') >= 0)
9388 bGlobalReplacement = true;
9389 else
9390 {
9391 // Unsupported flag.
9392 PushIllegalArgument();
9393 return;
9394 }
9395 }
9396 }
9397 }
9398
9399 bool bReplacement = false;
9400 OUString aReplacement;
9401 if (nParamCount >= 3)
9402 {
9403 // A missing argument is not an empty string to replace the match.
9404 // nOccurrence==0 forces no replacement, so simply discard the
9405 // argument.
9406 if (IsMissing() || nOccurrence == 0)
9407 PopError();
9408 else
9409 {
9410 aReplacement = GetString().getString();
9411 bReplacement = true;
9412 }
9413 }
9414 // If bGlobalReplacement==true and bReplacement==false then
9415 // bGlobalReplacement is silently ignored.
9416
9417 OUString aExpression = GetString().getString();
9418 OUString aText = GetString().getString();
9419
9420 if (nGlobalError != FormulaError::NONE)
9421 {
9422 PushError( nGlobalError);
9423 return;
9424 }
9425
9426 // 0-th match or replacement is none, return original string early.
9427 if (nOccurrence == 0)
9428 {
9429 PushString( aText);
9430 return;
9431 }
9432
9433 const icu::UnicodeString aIcuExpression(
9434 reinterpret_cast<const UChar*>(aExpression.getStr()), aExpression.getLength());
9435 UErrorCode status = U_ZERO_ERROR;
9436 icu::RegexMatcher aRegexMatcher( aIcuExpression, 0, status);
9437 if (U_FAILURE(status))
9438 {
9439 // Invalid regex.
9440 PushIllegalArgument();
9441 return;
9442 }
9443 // Guard against pathological patterns, limit steps of engine, see
9444 // https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1RegexMatcher.html#a6ebcfcab4fe6a38678c0291643a03a00
9445 aRegexMatcher.setTimeLimit( 23*1000, status);
9446
9447 const icu::UnicodeString aIcuText( reinterpret_cast<const UChar*>(aText.getStr()), aText.getLength());
9448 aRegexMatcher.reset( aIcuText);
9449
9450 if (!bReplacement)
9451 {
9452 // Find n-th occurrence.
9453 sal_Int32 nCount = 0;
9454#if (U_ICU_VERSION_MAJOR_NUM67 < 55)
9455 int32_t nStartPos = 0;
9456 while (aRegexMatcher.find(nStartPos, status) && U_SUCCESS(status) && ++nCount < nOccurrence)
9457#else
9458 while (aRegexMatcher.find(status) && U_SUCCESS(status) && ++nCount < nOccurrence)
9459#endif
9460 ;
9461 if (U_FAILURE(status))
9462 {
9463 // Some error.
9464 PushIllegalArgument();
9465 return;
9466 }
9467 // n-th match found?
9468 if (nCount != nOccurrence)
9469 {
9470 PushError( FormulaError::NotAvailable);
9471 return;
9472 }
9473 // Extract matched text.
9474 icu::UnicodeString aMatch( aRegexMatcher.group( status));
9475 if (U_FAILURE(status))
9476 {
9477 // Some error.
9478 PushIllegalArgument();
9479 return;
9480 }
9481 OUString aResult( reinterpret_cast<const sal_Unicode*>(aMatch.getBuffer()), aMatch.length());
9482 PushString( aResult);
9483 return;
9484 }
9485
9486 const icu::UnicodeString aIcuReplacement(
9487 reinterpret_cast<const UChar*>(aReplacement.getStr()), aReplacement.getLength());
9488 icu::UnicodeString aReplaced;
9489 if (bGlobalReplacement)
9490 // Replace all occurrences of match with replacement.
9491 aReplaced = aRegexMatcher.replaceAll( aIcuReplacement, status);
9492 else if (nOccurrence == 1)
9493 // Replace first occurrence of match with replacement.
9494 aReplaced = aRegexMatcher.replaceFirst( aIcuReplacement, status);
9495 else
9496 {
9497 // Replace n-th occurrence of match with replacement.
9498 sal_Int32 nCount = 0;
9499#if (U_ICU_VERSION_MAJOR_NUM67 < 55)
9500 int32_t nStartPos = 0;
9501 while (aRegexMatcher.find(nStartPos, status) && U_SUCCESS(status))
9502#else
9503 while (aRegexMatcher.find(status) && U_SUCCESS(status))
9504#endif
9505 {
9506 // XXX NOTE: After several RegexMatcher::find() the
9507 // RegexMatcher::appendReplacement() still starts at the
9508 // beginning (or after the last appendReplacement() position
9509 // which is none here) and copies the original text up to the
9510 // current found match and then replaces the found match.
9511 if (++nCount == nOccurrence)
9512 {
9513 aRegexMatcher.appendReplacement( aReplaced, aIcuReplacement, status);
9514 break;
9515 }
9516 }
9517 aRegexMatcher.appendTail( aReplaced);
9518 }
9519 if (U_FAILURE(status))
9520 {
9521 // Some error, e.g. extraneous $1 without group.
9522 PushIllegalArgument();
9523 return;
9524 }
9525 OUString aResult( reinterpret_cast<const sal_Unicode*>(aReplaced.getBuffer()), aReplaced.length());
9526 PushString( aResult);
9527}
9528
9529void ScInterpreter::ScMid()
9530{
9531 if ( !MustHaveParamCount( GetByte(), 3 ) )
9532 return;
9533
9534 const sal_Int32 nSubLen = GetStringPositionArgument();
9535 const sal_Int32 nStart = GetStringPositionArgument();
9536 OUString aStr = GetString().getString();
9537 if ( nStart < 1 || nSubLen < 0 )
9538 PushIllegalArgument();
9539 else
9540 {
9541 sal_Int32 nLen = aStr.getLength();
9542 sal_Int32 nIdx = 0;
9543 sal_Int32 nCnt = 0;
9544 while ( nIdx < nLen && nStart - 1 > nCnt )
9545 {
9546 aStr.iterateCodePoints( &nIdx );
9547 ++nCnt;
9548 }
9549 sal_Int32 nIdx0 = nIdx; //start position
9550
9551 while ( nIdx < nLen && nStart + nSubLen - 1 > nCnt )
9552 {
9553 aStr.iterateCodePoints( &nIdx );
9554 ++nCnt;
9555 }
9556 aStr = aStr.copy( nIdx0, nIdx - nIdx0 );
9557 PushString( aStr );
9558 }
9559}
9560
9561void ScInterpreter::ScText()
9562{
9563 if ( !MustHaveParamCount( GetByte(), 2 ) )
9564 return;
9565
9566 OUString sFormatString = GetString().getString();
9567 svl::SharedString aStr;
9568 bool bString = false;
9569 double fVal = 0.0;
9570 switch (GetStackType())
9571 {
9572 case svError:
9573 PopError();
9574 break;
9575 case svDouble:
9576 fVal = PopDouble();
9577 break;
9578 default:
9579 {
9580 FormulaConstTokenRef xTok( PopToken());
9581 if (nGlobalError == FormulaError::NONE)
9582 {
9583 PushTokenRef( xTok);
9584 // Temporarily override the ConvertStringToValue()
9585 // error for GetCellValue() / GetCellValueOrZero()
9586 FormulaError nSErr = mnStringNoValueError;
9587 mnStringNoValueError = FormulaError::NotNumericString;
9588 fVal = GetDouble();
9589 mnStringNoValueError = nSErr;
9590 if (nGlobalError == FormulaError::NotNumericString)
9591 {
9592 // Not numeric.
9593 nGlobalError = FormulaError::NONE;
9594 PushTokenRef( xTok);
9595 aStr = GetString();
9596 bString = true;
9597 }
9598 }
9599 }
9600 }
9601 if (nGlobalError != FormulaError::NONE)
9602 PushError( nGlobalError);
9603 else if (sFormatString.isEmpty())
9604 {
9605 // Mimic the Excel behaviour that
9606 // * anything numeric returns an empty string
9607 // * text convertible to numeric returns an empty string
9608 // * any other text returns that text
9609 // Conversion was detected above.
9610 if (bString)
9611 PushString( aStr);
9612 else
9613 PushString( OUString());
9614 }
9615 else
9616 {
9617 OUString aResult;
9618 const Color* pColor = nullptr;
9619 LanguageType eCellLang;
9620 const ScPatternAttr* pPattern = mrDoc.GetPattern(
9621 aPos.Col(), aPos.Row(), aPos.Tab() );
9622 if ( pPattern )
9623 eCellLang = pPattern->GetItem( ATTR_LANGUAGE_FORMAT ).GetValue();
9624 else
9625 eCellLang = ScGlobal::eLnge;
9626 if (bString)
9627 {
9628 if (!pFormatter->GetPreviewString( sFormatString, aStr.getString(),
9629 aResult, &pColor, eCellLang))
9630 PushIllegalArgument();
9631 else
9632 PushString( aResult);
9633 }
9634 else
9635 {
9636 if (!pFormatter->GetPreviewStringGuess( sFormatString, fVal,
9637 aResult, &pColor, eCellLang))
9638 PushIllegalArgument();
9639 else
9640 PushString( aResult);
9641 }
9642 }
9643}
9644
9645void ScInterpreter::ScSubstitute()
9646{
9647 sal_uInt8 nParamCount = GetByte();
9648 if ( !MustHaveParamCount( nParamCount, 3, 4 ) )
9649 return;
9650
9651 sal_Int32 nCnt;
9652 if (nParamCount == 4)
9653 {
9654 nCnt = GetStringPositionArgument();
9655 if (nCnt < 1)
9656 {
9657 PushIllegalArgument();
9658 return;
9659 }
9660 }
9661 else
9662 nCnt = 0;
9663 OUString sNewStr = GetString().getString();
9664 OUString sOldStr = GetString().getString();
9665 OUString sStr = GetString().getString();
9666 sal_Int32 nPos = 0;
9667 sal_Int32 nCount = 0;
9668 sal_Int32 nNewLen = sNewStr.getLength();
9669 sal_Int32 nOldLen = sOldStr.getLength();
9670 while( true )
9671 {
9672 nPos = sStr.indexOf( sOldStr, nPos );
9673 if (nPos != -1)
9674 {
9675 nCount++;
9676 if( !nCnt || nCount == nCnt )
9677 {
9678 sStr = sStr.replaceAt(nPos,nOldLen, "");
9679 if ( CheckStringResultLen( sStr, sNewStr ) )
9680 {
9681 sStr = sStr.replaceAt(nPos, 0, sNewStr);
9682 nPos = sal::static_int_cast<sal_Int32>( nPos + nNewLen );
9683 }
9684 else
9685 break;
9686 }
9687 else
9688 nPos++;
9689 }
9690 else
9691 break;
9692 }
9693 PushString( sStr );
9694}
9695
9696void ScInterpreter::ScRept()
9697{
9698 if ( !MustHaveParamCount( GetByte(), 2 ) )
9699 return;
9700
9701 sal_Int32 nCnt = GetStringPositionArgument();
9702 OUString aStr = GetString().getString();
9703 if (nCnt < 0)
9704 PushIllegalArgument();
9705 else if (static_cast<double>(nCnt) * aStr.getLength() > kScInterpreterMaxStrLen)
9706 {
9707 PushError( FormulaError::StringOverflow );
9708 }
9709 else if (nCnt == 0)
9710 PushString( EMPTY_OUSTRINGScGlobal::GetEmptyOUString() );
9711 else
9712 {
9713 const sal_Int32 nLen = aStr.getLength();
9714 OUStringBuffer aRes(nCnt*nLen);
9715 while( nCnt-- )
9716 aRes.append(aStr);
9717 PushString( aRes.makeStringAndClear() );
9718 }
9719}
9720
9721void ScInterpreter::ScConcat()
9722{
9723 sal_uInt8 nParamCount = GetByte();
9724 OUString aRes;
9725 while( nParamCount-- > 0)
9726 {
9727 OUString aStr = GetString().getString();
9728 if (CheckStringResultLen( aRes, aStr))
9729 aRes = aStr + aRes;
9730 else
9731 break;
9732 }
9733 PushString( aRes );
9734}
9735
9736FormulaError ScInterpreter::GetErrorType()
9737{
9738 FormulaError nErr;
9739 FormulaError nOldError = nGlobalError;
9740 nGlobalError = FormulaError::NONE;
9741 switch ( GetStackType() )
9742 {
9743 case svRefList :
9744 {
9745 FormulaConstTokenRef x = PopToken();
9746 if (nGlobalError != FormulaError::NONE)
9747 nErr = nGlobalError;
9748 else
9749 {
9750 const ScRefList* pRefList = x->GetRefList();
9751 size_t n = pRefList->size();
9752 if (!n)
9753 nErr = FormulaError::NoRef;
9754 else if (n > 1)
9755 nErr = FormulaError::NoValue;
9756 else
9757 {
9758 ScRange aRange;
9759 DoubleRefToRange( (*pRefList)[0], aRange);
9760 if (nGlobalError != FormulaError::NONE)
9761 nErr = nGlobalError;
9762 else
9763 {
9764 ScAddress aAdr;
9765 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
9766 nErr = mrDoc.GetErrCode( aAdr );
9767 else
9768 nErr = nGlobalError;
9769 }
9770 }
9771 }
9772 }
9773 break;
9774 case svDoubleRef :
9775 {
9776 ScRange aRange;
9777 PopDoubleRef( aRange );
9778 if ( nGlobalError != FormulaError::NONE )
9779 nErr = nGlobalError;
9780 else
9781 {
9782 ScAddress aAdr;
9783 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
9784 nErr = mrDoc.GetErrCode( aAdr );
9785 else
9786 nErr = nGlobalError;
9787 }
9788 }
9789 break;
9790 case svSingleRef :
9791 {
9792 ScAddress aAdr;
9793 PopSingleRef( aAdr );
9794 if ( nGlobalError != FormulaError::NONE )
9795 nErr = nGlobalError;
9796 else
9797 nErr = mrDoc.GetErrCode( aAdr );
9798 }
9799 break;
9800 default:
9801 PopError();
9802 nErr = nGlobalError;
9803 }
9804 nGlobalError = nOldError;
9805 return nErr;
9806}
9807
9808void ScInterpreter::ScErrorType()
9809{
9810 FormulaError nErr = GetErrorType();
9811 if ( nErr != FormulaError::NONE )
9812 {
9813 nGlobalError = FormulaError::NONE;
9814 PushDouble( static_cast<double>(nErr) );
9815 }
9816 else
9817 {
9818 PushNA();
9819 }
9820}
9821
9822void ScInterpreter::ScErrorType_ODF()
9823{
9824 FormulaError nErr = GetErrorType();
9825 sal_uInt16 nErrType;
9826
9827 switch ( nErr )
9828 {
9829 case FormulaError::ParameterExpected : // #NULL!
9830 nErrType = 1;
9831 break;
9832 case FormulaError::DivisionByZero : // #DIV/0!
9833 nErrType = 2;
9834 break;
9835 case FormulaError::NoValue : // #VALUE!
9836 nErrType = 3;
9837 break;
9838 case FormulaError::NoRef : // #REF!
9839 nErrType = 4;
9840 break;
9841 case FormulaError::NoName : // #NAME?
9842 nErrType = 5;
9843 break;
9844 case FormulaError::IllegalFPOperation : // #NUM!
9845 nErrType = 6;
9846 break;
9847 case FormulaError::NotAvailable : // #N/A
9848 nErrType = 7;
9849 break;
9850 /*
9851 #GETTING_DATA is a message that can appear in Excel when a large or
9852 complex worksheet is being calculated. In Excel 2007 and newer,
9853 operations are grouped so more complicated cells may finish after
9854 earlier ones do. While the calculations are still processing, the
9855 unfinished cells may display #GETTING_DATA.
9856 Because the message is temporary and disappears when the calculations
9857 complete, this isn’t a true error.
9858 No calc error code known (yet).
9859
9860 case : // GETTING_DATA
9861 nErrType = 8;
9862 break;
9863 */
9864 default :
9865 nErrType = 0;
9866 break;
9867 }
9868
9869 if ( nErrType )
9870 {
9871 nGlobalError =FormulaError::NONE;
9872 PushDouble( nErrType );
9873 }
9874 else
9875 PushNA();
9876}
9877
9878bool ScInterpreter::MayBeRegExp( const OUString& rStr, bool bIgnoreWildcards )
9879{
9880 if ( rStr.isEmpty() || (rStr.getLength() == 1 && !rStr.startsWith(".")) )
9881 return false; // single meta characters can not be a regexp
9882 // First two characters are wildcard '?' and '*' characters.
9883 static const sal_Unicode cre[] = { '?','*','+','.','[',']','^','$','\\','<','>','(',')','|', 0 };
9884 const sal_Unicode* const pre = bIgnoreWildcards ? cre + 2 : cre;
9885 const sal_Unicode* p1 = rStr.getStr();
9886 sal_Unicode c1;
9887 while ( ( c1 = *p1++ ) != 0 )
9888 {
9889 const sal_Unicode* p2 = pre;
9890 while ( *p2 )
9891 {
9892 if ( c1 == *p2++ )
9893 return true;
9894 }
9895 }
9896 return false;
9897}
9898
9899bool ScInterpreter::MayBeWildcard( const OUString& rStr )
9900{
9901 // Wildcards with '~' escape, if there are no wildcards then an escaped
9902 // character does not make sense, but it modifies the search pattern in an
9903 // Excel compatible wildcard search...
9904 static const sal_Unicode cw[] = { '*','?','~', 0 };
9905 const sal_Unicode* p1 = rStr.getStr();
9906 sal_Unicode c1;
9907 while ( ( c1 = *p1++ ) != 0 )
9908 {
9909 const sal_Unicode* p2 = cw;
9910 while ( *p2 )
9911 {
9912 if ( c1 == *p2++ )
9913 return true;
9914 }
9915 }
9916 return false;
9917}
9918
9919utl::SearchParam::SearchType ScInterpreter::DetectSearchType( const OUString& rStr, const ScDocument& rDoc )
9920{
9921 if (rDoc.GetDocOptions().IsFormulaWildcardsEnabled())
9922 return MayBeWildcard( rStr ) ? utl::SearchParam::SearchType::Wildcard : utl::SearchParam::SearchType::Normal;
9923 if (rDoc.GetDocOptions().IsFormulaRegexEnabled())
9924 return MayBeRegExp( rStr ) ? utl::SearchParam::SearchType::Regexp : utl::SearchParam::SearchType::Normal;
9925 return utl::SearchParam::SearchType::Normal;
9926}
9927
9928static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument& rDoc, const ScInterpreterContext& rContext,
9929 const ScQueryParam & rParam, const ScQueryEntry & rEntry )
9930{
9931 bool bFound = false;
9932 ScQueryCellIterator aCellIter( rDoc, rContext, rParam.nTab, rParam, false);
9933 if (rEntry.eOp != SC_EQUAL)
9934 {
9935 // range lookup <= or >=
9936 SCCOL nCol;
9937 SCROW nRow;
9938 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow);
9939 if (bFound)
9940 {
9941 o_rResultPos.SetCol( nCol);
9942 o_rResultPos.SetRow( nRow);
9943 }
9944 }
9945 else if (aCellIter.GetFirst())
9946 {
9947 // EQUAL
9948 bFound = true;
9949 o_rResultPos.SetCol( aCellIter.GetCol());
9950 o_rResultPos.SetRow( aCellIter.GetRow());
9951 }
9952 return bFound;
9953}
9954
9955// tdf#121052:
9956// =VLOOKUP(SearchCriterion; RangeArray; Index; Sorted)
9957// [SearchCriterion] is the value searched for in the first column of the array.
9958// [RangeArray] is the reference, which is to comprise at least two columns.
9959// [Index] is the number of the column in the array that contains the value to be returned. The first column has the number 1.
9960//
9961// Prerequisite of lcl_getPrevRowWithEmptyValueLookup():
9962// Value referenced by [SearchCriterion] is empty.
9963// lcl_getPrevRowWithEmptyValueLookup() performs following checks:
9964// - if we run query with "exact match" mode (i.e. VLOOKUP)
9965// - and if we already have the same lookup done before but for another row
9966// which is also had empty [SearchCriterion]
9967//
9968// then
9969// we could say, that for current row we could reuse results of the cached call which was done for the row2
9970// In this case we return row index, which is >= 0.
9971//
9972// Elsewhere
9973// -1 is returned, which will lead to default behavior =>
9974// complete lookup will be done in RangeArray inside lcl_LookupQuery() method.
9975//
9976// This method was added only for speed up to avoid several useless complete
9977// lookups inside [RangeArray] for searching empty strings.
9978//
9979static SCROW lcl_getPrevRowWithEmptyValueLookup( const ScLookupCache& rCache,
9980 const ScLookupCache::QueryCriteria& rCriteria, const ScQueryParam & rParam)
9981{
9982 // is lookup value empty?
9983 const ScQueryEntry& rEntry = rParam.GetEntry(0);
9984 const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
9985 if (! rItem.maString.getString().isEmpty())
9986 return -1; // not found
9987
9988 // try to find the row index for which we have already performed lookup
9989 // and have some result of it inside cache
9990 return rCache.lookup( rCriteria );
9991}
9992
9993bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos,
9994 const ScQueryParam & rParam ) const
9995{
9996 bool bFound = false;
9997 const ScQueryEntry& rEntry = rParam.GetEntry(0);
9998 bool bColumnsMatch = (rParam.nCol1 == rEntry.nField);
9999 OSL_ENSURE( bColumnsMatch, "ScInterpreter::LookupQueryWithCache: columns don't match")do { if (true && (!(bColumnsMatch))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx"
":" "9999" ": "), "%s", "ScInterpreter::LookupQueryWithCache: columns don't match"
); } } while (false)
;
10000 // At least all volatile functions that generate indirect references have
10001 // to force non-cached lookup.
10002 /* TODO: We could further classify volatile functions into reference
10003 * generating and not reference generating functions to have to force less
10004 * direct lookups here. We could even further attribute volatility per
10005 * parameter so it would affect only the lookup range parameter. */
10006 if (!bColumnsMatch || GetVolatileType() != NOT_VOLATILE)
10007 bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry);
10008 else
10009 {
10010 ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab,
10011 rParam.nCol2, rParam.nRow2, rParam.nTab);
10012 ScLookupCache& rCache = mrDoc.GetLookupCache( aLookupRange, &mrContext );
10013 ScLookupCache::QueryCriteria aCriteria( rEntry);
10014 ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos,
10015 aCriteria, aPos);
10016
10017 // tdf#121052: Slow load of cells with VLOOKUP with references to empty cells
10018 // This check was added only for speed up to avoid several useless complete
10019 // lookups inside [RangeArray] for searching empty strings.
10020 if (eCacheResult == ScLookupCache::NOT_CACHED && aCriteria.isEmptyStringQuery())
10021 {
10022 const SCROW nPrevRowWithEmptyValueLookup = lcl_getPrevRowWithEmptyValueLookup(rCache, aCriteria, rParam);
10023 if (nPrevRowWithEmptyValueLookup >= 0)
10024 {
10025 // make the same lookup using cache with different row index
10026 // (this lookup was already cached)
10027 ScAddress aPosPrev(aPos);
10028 aPosPrev.SetRow(nPrevRowWithEmptyValueLookup);
10029
10030 eCacheResult = rCache.lookup( o_rResultPos, aCriteria, aPosPrev );
10031 }
10032 }
10033
10034 switch (eCacheResult)
10035 {
10036 case ScLookupCache::NOT_CACHED :
10037 case ScLookupCache::CRITERIA_DIFFERENT :
10038 bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry);
10039 if (eCacheResult == ScLookupCache::NOT_CACHED)
10040 rCache.insert( o_rResultPos, aCriteria, aPos, bFound);
10041 break;
10042 case ScLookupCache::FOUND :
10043 bFound = true;
10044 break;
10045 case ScLookupCache::NOT_AVAILABLE :
10046 ; // nothing, bFound remains FALSE
10047 break;
10048 }
10049 }
10050 return bFound;
10051}
10052
10053/* vim:set shiftwidth=4 softtabstop=4 expandtab: */