Bug Summary

File:home/maarten/src/libreoffice/core/sc/source/core/inc/arraysumfunctor.hxx
Warning:line 68, column 13
Assigned value is garbage or undefined

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 interpr6.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/interpr6.cxx

/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr6.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#include <columnspanset.hxx>
22#include <column.hxx>
23#include <document.hxx>
24#include <cellvalue.hxx>
25#include <dociter.hxx>
26#include <mtvcellfunc.hxx>
27#include <scmatrix.hxx>
28
29#include <arraysumfunctor.hxx>
30
31#include <formula/token.hxx>
32
33using namespace formula;
34
35double const fHalfMachEps = 0.5 * ::std::numeric_limits<double>::epsilon();
36
37// The idea how this group of gamma functions is calculated, is
38// based on the Cephes library
39// online http://www.moshier.net/#Cephes [called 2008-02]
40
41/** You must ensure fA>0.0 && fX>0.0
42 valid results only if fX > fA+1.0
43 uses continued fraction with odd items */
44double ScInterpreter::GetGammaContFraction( double fA, double fX )
45{
46
47 double const fBigInv = ::std::numeric_limits<double>::epsilon();
48 double const fBig = 1.0/fBigInv;
49 double fCount = 0.0;
50 double fY = 1.0 - fA;
51 double fDenom = fX + 2.0-fA;
52 double fPkm1 = fX + 1.0;
53 double fPkm2 = 1.0;
54 double fQkm1 = fDenom * fX;
55 double fQkm2 = fX;
56 double fApprox = fPkm1/fQkm1;
57 bool bFinished = false;
58 do
59 {
60 fCount = fCount +1.0;
61 fY = fY+ 1.0;
62 const double fNum = fY * fCount;
63 fDenom = fDenom +2.0;
64 double fPk = fPkm1 * fDenom - fPkm2 * fNum;
65 const double fQk = fQkm1 * fDenom - fQkm2 * fNum;
66 if (fQk != 0.0)
67 {
68 const double fR = fPk/fQk;
69 bFinished = (fabs( (fApprox - fR)/fR ) <= fHalfMachEps);
70 fApprox = fR;
71 }
72 fPkm2 = fPkm1;
73 fPkm1 = fPk;
74 fQkm2 = fQkm1;
75 fQkm1 = fQk;
76 if (fabs(fPk) > fBig)
77 {
78 // reduce a fraction does not change the value
79 fPkm2 = fPkm2 * fBigInv;
80 fPkm1 = fPkm1 * fBigInv;
81 fQkm2 = fQkm2 * fBigInv;
82 fQkm1 = fQkm1 * fBigInv;
83 }
84 } while (!bFinished && fCount<10000);
85 // most iterations, if fX==fAlpha+1.0; approx sqrt(fAlpha) iterations then
86 if (!bFinished)
87 {
88 SetError(FormulaError::NoConvergence);
89 }
90 return fApprox;
91}
92
93/** You must ensure fA>0.0 && fX>0.0
94 valid results only if fX <= fA+1.0
95 uses power series */
96double ScInterpreter::GetGammaSeries( double fA, double fX )
97{
98 double fDenomfactor = fA;
99 double fSummand = 1.0/fA;
100 double fSum = fSummand;
101 int nCount=1;
102 do
103 {
104 fDenomfactor = fDenomfactor + 1.0;
105 fSummand = fSummand * fX/fDenomfactor;
106 fSum = fSum + fSummand;
107 nCount = nCount+1;
108 } while ( fSummand/fSum > fHalfMachEps && nCount<=10000);
109 // large amount of iterations will be carried out for huge fAlpha, even
110 // if fX <= fAlpha+1.0
111 if (nCount>10000)
112 {
113 SetError(FormulaError::NoConvergence);
114 }
115 return fSum;
116}
117
118/** You must ensure fA>0.0 && fX>0.0) */
119double ScInterpreter::GetLowRegIGamma( double fA, double fX )
120{
121 double fLnFactor = fA * log(fX) - fX - GetLogGamma(fA);
122 double fFactor = exp(fLnFactor); // Do we need more accuracy than exp(ln()) has?
123 if (fX>fA+1.0) // includes fX>1.0; 1-GetUpRegIGamma, continued fraction
124 return 1.0 - fFactor * GetGammaContFraction(fA,fX);
125 else // fX<=1.0 || fX<=fA+1.0, series
126 return fFactor * GetGammaSeries(fA,fX);
127}
128
129/** You must ensure fA>0.0 && fX>0.0) */
130double ScInterpreter::GetUpRegIGamma( double fA, double fX )
131{
132
133 double fLnFactor= fA*log(fX)-fX-GetLogGamma(fA);
134 double fFactor = exp(fLnFactor); //Do I need more accuracy than exp(ln()) has?;
135 if (fX>fA+1.0) // includes fX>1.0
136 return fFactor * GetGammaContFraction(fA,fX);
137 else //fX<=1 || fX<=fA+1, 1-GetLowRegIGamma, series
138 return 1.0 -fFactor * GetGammaSeries(fA,fX);
139}
140
141/** Gamma distribution, probability density function.
142 fLambda is "scale" parameter
143 You must ensure fAlpha>0.0 and fLambda>0.0 */
144double ScInterpreter::GetGammaDistPDF( double fX, double fAlpha, double fLambda )
145{
146 if (fX < 0.0)
147 return 0.0; // see ODFF
148 else if (fX == 0)
149 // in this case 0^0 isn't zero
150 {
151 if (fAlpha < 1.0)
152 {
153 SetError(FormulaError::DivisionByZero); // should be #DIV/0
154 return HUGE_VAL(__builtin_huge_val ());
155 }
156 else if (fAlpha == 1)
157 {
158 return (1.0 / fLambda);
159 }
160 else
161 {
162 return 0.0;
163 }
164 }
165 else
166 {
167 double fXr = fX / fLambda;
168 // use exp(ln()) only for large arguments because of less accuracy
169 if (fXr > 1.0)
170 {
171 const double fLogDblMax = log( ::std::numeric_limits<double>::max());
172 if (log(fXr) * (fAlpha-1.0) < fLogDblMax && fAlpha < fMaxGammaArgument)
173 {
174 return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / GetGamma(fAlpha);
175 }
176 else
177 {
178 return exp( (fAlpha-1.0) * log(fXr) - fXr - log(fLambda) - GetLogGamma(fAlpha));
179 }
180 }
181 else // fXr near to zero
182 {
183 if (fAlpha<fMaxGammaArgument)
184 {
185 return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / GetGamma(fAlpha);
186 }
187 else
188 {
189 return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / exp( GetLogGamma(fAlpha));
190 }
191 }
192 }
193}
194
195/** Gamma distribution, cumulative distribution function.
196 fLambda is "scale" parameter
197 You must ensure fAlpha>0.0 and fLambda>0.0 */
198double ScInterpreter::GetGammaDist( double fX, double fAlpha, double fLambda )
199{
200 if (fX <= 0.0)
201 return 0.0;
202 else
203 return GetLowRegIGamma( fAlpha, fX / fLambda);
204}
205
206namespace {
207
208class NumericCellAccumulator
209{
210 double mfFirst;
211 double mfRest;
212 FormulaError mnError;
213
214public:
215 NumericCellAccumulator() : mfFirst(0.0), mfRest(0.0), mnError(FormulaError::NONE) {}
216
217 void operator() (const sc::CellStoreType::value_type& rNode, size_t nOffset, size_t nDataSize)
218 {
219 switch (rNode.type)
12
Control jumps to 'case 10:' at line 221
220 {
221 case sc::element_type_numeric:
222 {
223 const double *p = &sc::numeric_block::at(*rNode.data, nOffset);
224 size_t i = 0;
225
226 // Store the first non-zero value in mfFirst (for some reason).
227 if (!mfFirst)
13
Assuming the condition is false
14
Taking false branch
228 {
229 for (i = 0; i < nDataSize; ++i)
230 {
231 if (!mfFirst)
232 mfFirst = p[i];
233 else
234 break;
235 }
236 }
237 p += i;
238 nDataSize -= i;
239 if (nDataSize == 0)
15
Assuming 'nDataSize' is not equal to 0
16
Taking false branch
240 return;
241
242 sc::ArraySumFunctor functor(p, nDataSize);
243
244 mfRest += functor();
17
Calling 'ArraySumFunctor::operator()'
245 break;
246 }
247
248 case sc::element_type_formula:
249 {
250 sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data);
251 std::advance(it, nOffset);
252 sc::formula_block::const_iterator itEnd = it;
253 std::advance(itEnd, nDataSize);
254 for (; it != itEnd; ++it)
255 {
256 double fVal = 0.0;
257 FormulaError nErr = FormulaError::NONE;
258 ScFormulaCell& rCell = *(*it);
259 if (!rCell.GetErrorOrValue(nErr, fVal))
260 // The cell has neither error nor value. Perhaps string result.
261 continue;
262
263 if (nErr != FormulaError::NONE)
264 {
265 // Cell has error - skip all the rest
266 mnError = nErr;
267 return;
268 }
269
270 if ( !mfFirst )
271 mfFirst = fVal;
272 else
273 mfRest += fVal;
274 }
275 }
276 break;
277 default:
278 ;
279 }
280 }
281
282 FormulaError getError() const { return mnError; }
283 double getFirst() const { return mfFirst; }
284 double getRest() const { return mfRest; }
285};
286
287class NumericCellCounter
288{
289 size_t mnCount;
290public:
291 NumericCellCounter() : mnCount(0) {}
292
293 void operator() (const sc::CellStoreType::value_type& rNode, size_t nOffset, size_t nDataSize)
294 {
295 switch (rNode.type)
296 {
297 case sc::element_type_numeric:
298 mnCount += nDataSize;
299 break;
300 case sc::element_type_formula:
301 {
302 sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data);
303 std::advance(it, nOffset);
304 sc::formula_block::const_iterator itEnd = it;
305 std::advance(itEnd, nDataSize);
306 for (; it != itEnd; ++it)
307 {
308 ScFormulaCell& rCell = **it;
309 if (rCell.IsValueNoError())
310 ++mnCount;
311 }
312 }
313 break;
314 default:
315 ;
316 }
317 }
318
319 size_t getCount() const { return mnCount; }
320};
321
322class FuncCount : public sc::ColumnSpanSet::ColumnAction
323{
324 const ScInterpreterContext& mrContext;
325 sc::ColumnBlockConstPosition maPos;
326 ScColumn* mpCol;
327 size_t mnCount;
328 sal_uInt32 mnNumFmt;
329
330public:
331 FuncCount(const ScInterpreterContext& rContext) : mrContext(rContext), mpCol(nullptr), mnCount(0), mnNumFmt(0) {}
332
333 virtual void startColumn(ScColumn* pCol) override
334 {
335 mpCol = pCol;
336 mpCol->InitBlockPosition(maPos);
337 }
338
339 virtual void execute(SCROW nRow1, SCROW nRow2, bool bVal) override
340 {
341 if (!bVal)
342 return;
343
344 NumericCellCounter aFunc;
345 maPos.miCellPos = sc::ParseBlock(maPos.miCellPos, mpCol->GetCellStore(), aFunc, nRow1, nRow2);
346 mnCount += aFunc.getCount();
347 mnNumFmt = mpCol->GetNumberFormat(mrContext, nRow2);
348 };
349
350 size_t getCount() const { return mnCount; }
351 sal_uInt32 getNumberFormat() const { return mnNumFmt; }
352};
353
354class FuncSum : public sc::ColumnSpanSet::ColumnAction
355{
356 const ScInterpreterContext& mrContext;
357 sc::ColumnBlockConstPosition maPos;
358 ScColumn* mpCol;
359 double mfSum;
360 FormulaError mnError;
361 sal_uInt32 mnNumFmt;
362
363public:
364 FuncSum(const ScInterpreterContext& rContext) : mrContext(rContext), mpCol(nullptr), mfSum(0.0), mnError(FormulaError::NONE), mnNumFmt(0) {}
365
366 virtual void startColumn(ScColumn* pCol) override
367 {
368 mpCol = pCol;
369 mpCol->InitBlockPosition(maPos);
370 }
371
372 virtual void execute(SCROW, SCROW, bool) override {}
373
374 virtual void executeSum(SCROW nRow1, SCROW nRow2, bool bVal, double& fMem ) override
375 {
376 if (!bVal)
1
Assuming 'bVal' is true
2
Taking false branch
377 return;
378
379 if (mnError != FormulaError::NONE)
3
Assuming field 'mnError' is equal to NONE
4
Taking false branch
380 return;
381
382 NumericCellAccumulator aFunc;
383 maPos.miCellPos = sc::ParseBlock(maPos.miCellPos, mpCol->GetCellStore(), aFunc, nRow1, nRow2);
5
Calling 'ParseBlock<mdds::multi_type_vector<mdds::mtv::custom_block_func3<mdds::mtv::default_element_block<52, svl::SharedString>, mdds::mtv::noncopyable_managed_element_block<53, EditTextObject>, mdds::mtv::noncopyable_managed_element_block<54, ScFormulaCell>>, sc::CellStoreEvent>, (anonymous namespace)::NumericCellAccumulator>'
384 mnError = aFunc.getError();
385 if (mnError != FormulaError::NONE)
386 return;
387
388 if ( fMem )
389 mfSum += aFunc.getFirst() + aFunc.getRest();
390 else
391 {
392 fMem = aFunc.getFirst();
393 mfSum += aFunc.getRest();
394 }
395
396 mnNumFmt = mpCol->GetNumberFormat(mrContext, nRow2);
397 };
398
399 FormulaError getError() const { return mnError; }
400 double getSum() const { return mfSum; }
401 sal_uInt32 getNumberFormat() const { return mnNumFmt; }
402};
403
404}
405
406static void IterateMatrix(
407 const ScMatrixRef& pMat, ScIterFunc eFunc, bool bTextAsZero, SubtotalFlags nSubTotalFlags,
408 sal_uLong& rCount, SvNumFormatType& rFuncFmtType, double& fRes, double& fMem )
409{
410 if (!pMat)
411 return;
412
413 const bool bIgnoreErrVal = bool(nSubTotalFlags & SubtotalFlags::IgnoreErrVal);
414 rFuncFmtType = SvNumFormatType::NUMBER;
415 switch (eFunc)
416 {
417 case ifAVERAGE:
418 case ifSUM:
419 {
420 ScMatrix::IterateResult aRes = pMat->Sum(bTextAsZero, bIgnoreErrVal);
421 // If the first value is a NaN, it probably means it was an empty cell,
422 // and should be treated as zero.
423 if ( !std::isfinite(aRes.mfFirst) )
424 {
425 sal_uInt32 nErr = reinterpret_cast< sal_math_Double * >(&aRes.mfFirst)->nan_parts.fraction_lo;
426 if (nErr & 0xffff0000)
427 {
428 aRes.mfFirst = 0;
429 }
430 }
431 if ( fMem )
432 fRes += aRes.mfFirst + aRes.mfRest;
433 else
434 {
435 fMem = aRes.mfFirst;
436 fRes += aRes.mfRest;
437 }
438 rCount += aRes.mnCount;
439 }
440 break;
441 case ifCOUNT:
442 rCount += pMat->Count(bTextAsZero, false); // do not count error values
443 break;
444 case ifCOUNT2:
445 /* TODO: what is this supposed to be with bIgnoreErrVal? */
446 rCount += pMat->Count(true, true); // do count error values
447 break;
448 case ifPRODUCT:
449 {
450 ScMatrix::IterateResult aRes = pMat->Product(bTextAsZero, bIgnoreErrVal);
451 fRes *= aRes.mfFirst;
452 fRes *= aRes.mfRest;
453 rCount += aRes.mnCount;
454 }
455 break;
456 case ifSUMSQ:
457 {
458 ScMatrix::IterateResult aRes = pMat->SumSquare(bTextAsZero, bIgnoreErrVal);
459 fRes += aRes.mfFirst;
460 fRes += aRes.mfRest;
461 rCount += aRes.mnCount;
462 }
463 break;
464 default:
465 ;
466 }
467}
468
469size_t ScInterpreter::GetRefListArrayMaxSize( short nParamCount )
470{
471 size_t nSize = 0;
472 if (IsInArrayContext())
473 {
474 for (short i=1; i <= nParamCount; ++i)
475 {
476 if (GetStackType(i) == svRefList)
477 {
478 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp - i]);
479 if (p && p->IsArrayResult() && p->GetRefList()->size() > nSize)
480 nSize = p->GetRefList()->size();
481 }
482 }
483 }
484 return nSize;
485}
486
487static double lcl_IterResult( ScIterFunc eFunc, double fRes, double fMem, sal_uLong nCount )
488{
489 switch( eFunc )
490 {
491 case ifSUM:
492 fRes = ::rtl::math::approxAdd( fRes, fMem );
493 break;
494 case ifAVERAGE:
495 fRes = sc::div( ::rtl::math::approxAdd( fRes, fMem ), nCount);
496 break;
497 case ifCOUNT2:
498 case ifCOUNT:
499 fRes = nCount;
500 break;
501 case ifPRODUCT:
502 if ( !nCount )
503 fRes = 0.0;
504 break;
505 default:
506 ; // nothing
507 }
508 return fRes;
509}
510
511void ScInterpreter::IterateParameters( ScIterFunc eFunc, bool bTextAsZero )
512{
513 short nParamCount = GetByte();
514 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
515 ScMatrixRef xResMat, xResCount;
516 auto ResInitVal = [eFunc]()
517 {
518 return (eFunc == ifPRODUCT) ? 1.0 : 0.0;
519 };
520 double fRes = ResInitVal();
521 double fVal = 0.0;
522 double fMem = 0.0; // first numeric value != 0.0
523 sal_uLong nCount = 0;
524 ScAddress aAdr;
525 ScRange aRange;
526 size_t nRefInList = 0;
527 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
528 if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
529 ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) )
530 nGlobalError = FormulaError::NONE;
531 while (nParamCount-- > 0)
532 {
533 switch (GetStackType())
534 {
535 case svString:
536 {
537 if( eFunc == ifCOUNT )
538 {
539 OUString aStr = PopString().getString();
540 if ( bTextAsZero )
541 nCount++;
542 else
543 {
544 // Only check if string can be converted to number, no
545 // error propagation.
546 FormulaError nErr = nGlobalError;
547 nGlobalError = FormulaError::NONE;
548 ConvertStringToValue( aStr );
549 if (nGlobalError == FormulaError::NONE)
550 ++nCount;
551 nGlobalError = nErr;
552 }
553 }
554 else
555 {
556 Pop();
557 switch ( eFunc )
558 {
559 case ifAVERAGE:
560 case ifSUM:
561 case ifSUMSQ:
562 case ifPRODUCT:
563 {
564 if ( bTextAsZero )
565 {
566 nCount++;
567 if ( eFunc == ifPRODUCT )
568 fRes = 0.0;
569 }
570 else
571 {
572 while (nParamCount-- > 0)
573 Pop();
574 SetError( FormulaError::NoValue );
575 }
576 }
577 break;
578 default:
579 nCount++;
580 }
581 }
582 }
583 break;
584 case svDouble :
585 fVal = GetDouble();
586 nCount++;
587 switch( eFunc )
588 {
589 case ifAVERAGE:
590 case ifSUM:
591 if ( fMem )
592 fRes += fVal;
593 else
594 fMem = fVal;
595 break;
596 case ifSUMSQ: fRes += fVal * fVal; break;
597 case ifPRODUCT: fRes *= fVal; break;
598 default: ; // nothing
599 }
600 nFuncFmtType = SvNumFormatType::NUMBER;
601 break;
602 case svExternalSingleRef:
603 {
604 ScExternalRefCache::TokenRef pToken;
605 ScExternalRefCache::CellFormat aFmt;
606 PopExternalSingleRef(pToken, &aFmt);
607 if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
608 ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) )
609 {
610 nGlobalError = FormulaError::NONE;
611 if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
612 ++nCount;
613 break;
614 }
615
616 if (!pToken)
617 break;
618
619 StackVar eType = pToken->GetType();
620 if (eFunc == ifCOUNT2)
621 {
622 if ( eType != svEmptyCell &&
623 ( ( pToken->GetOpCode() != ocSubTotal &&
624 pToken->GetOpCode() != ocAggregate ) ||
625 ( mnSubTotalFlags & SubtotalFlags::IgnoreNestedStAg ) ) )
626 nCount++;
627 if (nGlobalError != FormulaError::NONE)
628 nGlobalError = FormulaError::NONE;
629 }
630 else if (eType == svDouble)
631 {
632 nCount++;
633 fVal = pToken->GetDouble();
634 if (aFmt.mbIsSet)
635 {
636 nFuncFmtType = aFmt.mnType;
637 nFuncFmtIndex = aFmt.mnIndex;
638 }
639 switch( eFunc )
640 {
641 case ifAVERAGE:
642 case ifSUM:
643 if ( fMem )
644 fRes += fVal;
645 else
646 fMem = fVal;
647 break;
648 case ifSUMSQ: fRes += fVal * fVal; break;
649 case ifPRODUCT: fRes *= fVal; break;
650 case ifCOUNT:
651 if ( nGlobalError != FormulaError::NONE )
652 {
653 nGlobalError = FormulaError::NONE;
654 nCount--;
655 }
656 break;
657 default: ; // nothing
658 }
659 }
660 else if (bTextAsZero && eType == svString)
661 {
662 nCount++;
663 if ( eFunc == ifPRODUCT )
664 fRes = 0.0;
665 }
666 }
667 break;
668 case svSingleRef :
669 {
670 PopSingleRef( aAdr );
671 if (nGlobalError == FormulaError::NoRef)
672 {
673 PushError( FormulaError::NoRef);
674 return;
675 }
676
677 if ( ( ( mnSubTotalFlags & SubtotalFlags::IgnoreFiltered ) &&
678 mrDoc.RowFiltered( aAdr.Row(), aAdr.Tab() ) ) ||
679 ( ( mnSubTotalFlags & SubtotalFlags::IgnoreHidden ) &&
680 mrDoc.RowHidden( aAdr.Row(), aAdr.Tab() ) ) )
681 {
682 break;
683 }
684 if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
685 ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) )
686 {
687 nGlobalError = FormulaError::NONE;
688 if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
689 ++nCount;
690 break;
691 }
692 ScRefCellValue aCell(mrDoc, aAdr);
693 if (!aCell.isEmpty())
694 {
695 if( eFunc == ifCOUNT2 )
696 {
697 CellType eCellType = aCell.meType;
698 if ( eCellType != CELLTYPE_NONE )
699 nCount++;
700 if ( nGlobalError != FormulaError::NONE )
701 nGlobalError = FormulaError::NONE;
702 }
703 else if (aCell.hasNumeric())
704 {
705 nCount++;
706 fVal = GetCellValue(aAdr, aCell);
707 CurFmtToFuncFmt();
708 switch( eFunc )
709 {
710 case ifAVERAGE:
711 case ifSUM:
712 if ( fMem )
713 fRes += fVal;
714 else
715 fMem = fVal;
716 break;
717 case ifSUMSQ: fRes += fVal * fVal; break;
718 case ifPRODUCT: fRes *= fVal; break;
719 case ifCOUNT:
720 if ( nGlobalError != FormulaError::NONE )
721 {
722 nGlobalError = FormulaError::NONE;
723 nCount--;
724 }
725 break;
726 default: ; // nothing
727 }
728 }
729 else if (bTextAsZero && aCell.hasString())
730 {
731 nCount++;
732 if ( eFunc == ifPRODUCT )
733 fRes = 0.0;
734 }
735 }
736 }
737 break;
738 case svRefList :
739 {
740 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
741 if (p && p->IsArrayResult())
742 {
743 nRefArrayPos = nRefInList;
744 if ((eFunc == ifSUM || eFunc == ifAVERAGE) && fMem != 0.0)
745 {
746 fRes = rtl::math::approxAdd( fRes, fMem);
747 fMem = 0.0;
748 }
749 // The "one value to all references of an array" seems to
750 // be what Excel does if there are other types than just
751 // arrays of references.
752 if (!xResMat)
753 {
754 // Create and init all elements with current value.
755 assert(nMatRows > 0)(static_cast <bool> (nMatRows > 0) ? void (0) : __assert_fail
("nMatRows > 0", "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr6.cxx"
, 755, __extension__ __PRETTY_FUNCTION__))
;
756 xResMat = GetNewMat( 1, nMatRows, true);
757 xResMat->FillDouble( fRes, 0,0, 0,nMatRows-1);
758 if (eFunc != ifSUM)
759 {
760 xResCount = GetNewMat( 1, nMatRows, true);
761 xResCount->FillDouble( nCount, 0,0, 0,nMatRows-1);
762 }
763 }
764 else
765 {
766 // Current value and values from vector are operands
767 // for each vector position.
768 if (nCount && xResCount)
769 {
770 for (SCSIZE i=0; i < nMatRows; ++i)
771 {
772 xResCount->PutDouble( xResCount->GetDouble(0,i) + nCount, 0,i);
773 }
774 }
775 if (fRes != ResInitVal())
776 {
777 for (SCSIZE i=0; i < nMatRows; ++i)
778 {
779 double fVecRes = xResMat->GetDouble(0,i);
780 if (eFunc == ifPRODUCT)
781 fVecRes *= fRes;
782 else
783 fVecRes += fRes;
784 xResMat->PutDouble( fVecRes, 0,i);
785 }
786 }
787 }
788 fRes = ResInitVal();
789 nCount = 0;
790 }
791 }
792 [[fallthrough]];
793 case svDoubleRef :
794 {
795 PopDoubleRef( aRange, nParamCount, nRefInList);
796 if (nGlobalError == FormulaError::NoRef)
797 {
798 PushError( FormulaError::NoRef);
799 return;
800 }
801
802 if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
803 ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) )
804 {
805 nGlobalError = FormulaError::NONE;
806 if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
807 ++nCount;
808 if ( eFunc == ifCOUNT2 || eFunc == ifCOUNT )
809 break;
810 }
811 if( eFunc == ifCOUNT2 )
812 {
813 ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags );
814 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
815 {
816 if ( !aIter.isEmpty() )
817 {
818 ++nCount;
819 }
820 }
821
822 if ( nGlobalError != FormulaError::NONE )
823 nGlobalError = FormulaError::NONE;
824 }
825 else if (((eFunc == ifSUM && !bCalcAsShown) || eFunc == ifCOUNT )
826 && mnSubTotalFlags == SubtotalFlags::NONE)
827 {
828 // Use fast span set array method.
829 // ifSUM with bCalcAsShown has to use the slow bells and
830 // whistles ScValueIterator below.
831 sc::RangeColumnSpanSet aSet( aRange );
832
833 if ( eFunc == ifSUM )
834 {
835 FuncSum aAction(mrContext);
836 aSet.executeColumnAction( mrDoc, aAction, fMem );
837 FormulaError nErr = aAction.getError();
838 if ( nErr != FormulaError::NONE )
839 {
840 PushError( nErr );
841 return;
842 }
843 fRes += aAction.getSum();
844
845 // Get the number format of the last iterated cell.
846 nFuncFmtIndex = aAction.getNumberFormat();
847 }
848 else
849 {
850 FuncCount aAction(mrContext);
851 aSet.executeColumnAction(mrDoc, aAction);
852 nCount += aAction.getCount();
853
854 // Get the number format of the last iterated cell.
855 nFuncFmtIndex = aAction.getNumberFormat();
856 }
857
858 nFuncFmtType = mrContext.GetNumberFormatType( nFuncFmtIndex );
859 }
860 else
861 {
862 ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
863 aValIter.SetInterpreterContext( &mrContext );
864 FormulaError nErr = FormulaError::NONE;
865 if (aValIter.GetFirst(fVal, nErr))
866 {
867 // placed the loop on the inside for performance reasons:
868 aValIter.GetCurNumFmtInfo( mrContext, nFuncFmtType, nFuncFmtIndex );
869 switch( eFunc )
870 {
871 case ifAVERAGE:
872 case ifSUM:
873 if ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal )
874 {
875 do
876 {
877 if ( nErr == FormulaError::NONE )
878 {
879 SetError(nErr);
880 if ( fMem )
881 fRes += fVal;
882 else
883 fMem = fVal;
884 nCount++;
885 }
886 }
887 while (aValIter.GetNext(fVal, nErr));
888 }
889 else
890 {
891 do
892 {
893 SetError(nErr);
894 if ( fMem )
895 fRes += fVal;
896 else
897 fMem = fVal;
898 nCount++;
899 }
900 while (aValIter.GetNext(fVal, nErr));
901 }
902 break;
903 case ifSUMSQ:
904 if ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal )
905 {
906 do
907 {
908 if ( nErr == FormulaError::NONE )
909 {
910 SetError(nErr);
911 fRes += fVal * fVal;
912 nCount++;
913 }
914 }
915 while (aValIter.GetNext(fVal, nErr));
916 }
917 else
918 {
919 do
920 {
921 SetError(nErr);
922 fRes += fVal * fVal;
923 nCount++;
924 }
925 while (aValIter.GetNext(fVal, nErr));
926 }
927 break;
928 case ifPRODUCT:
929 do
930 {
931 if ( !( nErr != FormulaError::NONE && ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) )
932 {
933 SetError(nErr);
934 fRes *= fVal;
935 nCount++;
936 }
937 }
938 while (aValIter.GetNext(fVal, nErr));
939 break;
940 case ifCOUNT:
941 do
942 {
943 if ( nErr == FormulaError::NONE )
944 nCount++;
945 }
946 while (aValIter.GetNext(fVal, nErr));
947 break;
948 default: ; // nothing
949 }
950 SetError( nErr );
951 }
952 }
953 if (nRefArrayPos != std::numeric_limits<size_t>::max())
954 {
955 // Update vector element with current value.
956 if ((eFunc == ifSUM || eFunc == ifAVERAGE) && fMem != 0.0)
957 {
958 fRes = rtl::math::approxAdd( fRes, fMem);
959 fMem = 0.0;
960 }
961 if (xResCount)
962 xResCount->PutDouble( xResCount->GetDouble(0,nRefArrayPos) + nCount, 0,nRefArrayPos);
963 double fVecRes = xResMat->GetDouble(0,nRefArrayPos);
964 if (eFunc == ifPRODUCT)
965 fVecRes *= fRes;
966 else
967 fVecRes += fRes;
968 xResMat->PutDouble( fVecRes, 0,nRefArrayPos);
969 // Reset.
970 fRes = ResInitVal();
971 nCount = 0;
972 nRefArrayPos = std::numeric_limits<size_t>::max();
973 }
974 }
975 break;
976 case svExternalDoubleRef:
977 {
978 ScMatrixRef pMat;
979 PopExternalDoubleRef(pMat);
980 if ( nGlobalError != FormulaError::NONE && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
981 break;
982
983 IterateMatrix( pMat, eFunc, bTextAsZero, mnSubTotalFlags, nCount, nFuncFmtType, fRes, fMem );
984 }
985 break;
986 case svMatrix :
987 {
988 ScMatrixRef pMat = PopMatrix();
989
990 IterateMatrix( pMat, eFunc, bTextAsZero, mnSubTotalFlags, nCount, nFuncFmtType, fRes, fMem );
991 }
992 break;
993 case svError:
994 {
995 PopError();
996 if ( eFunc == ifCOUNT || ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
997 {
998 nGlobalError = FormulaError::NONE;
999 }
1000 else if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
1001 {
1002 nCount++;
1003 nGlobalError = FormulaError::NONE;
1004 }
1005 }
1006 break;
1007 default :
1008 while (nParamCount-- > 0)
1009 PopError();
1010 SetError(FormulaError::IllegalParameter);
1011 }
1012 }
1013
1014 // A boolean return type makes no sense on sums et al.
1015 // Counts are always numbers.
1016 if( nFuncFmtType == SvNumFormatType::LOGICAL || eFunc == ifCOUNT || eFunc == ifCOUNT2 )
1017 nFuncFmtType = SvNumFormatType::NUMBER;
1018
1019 if (xResMat)
1020 {
1021 // Include value of last non-references-array type and calculate final result.
1022 for (SCSIZE i=0; i < nMatRows; ++i)
1023 {
1024 sal_uLong nVecCount = (xResCount ? nCount + xResCount->GetDouble(0,i) : nCount);
1025 double fVecRes = xResMat->GetDouble(0,i);
1026 if (eFunc == ifPRODUCT)
1027 fVecRes *= fRes;
1028 else
1029 fVecRes += fRes;
1030 fVecRes = lcl_IterResult( eFunc, fVecRes, fMem, nVecCount);
1031 xResMat->PutDouble( fVecRes, 0,i);
1032 }
1033 PushMatrix( xResMat);
1034 }
1035 else
1036 {
1037 PushDouble( lcl_IterResult( eFunc, fRes, fMem, nCount));
1038 }
1039}
1040
1041void ScInterpreter::ScSumSQ()
1042{
1043 IterateParameters( ifSUMSQ );
1044}
1045
1046void ScInterpreter::ScSum()
1047{
1048 IterateParameters( ifSUM );
1049}
1050
1051void ScInterpreter::ScProduct()
1052{
1053 IterateParameters( ifPRODUCT );
1054}
1055
1056void ScInterpreter::ScAverage( bool bTextAsZero )
1057{
1058 IterateParameters( ifAVERAGE, bTextAsZero );
1059}
1060
1061void ScInterpreter::ScCount()
1062{
1063 IterateParameters( ifCOUNT );
1064}
1065
1066void ScInterpreter::ScCount2()
1067{
1068 IterateParameters( ifCOUNT2 );
1069}
1070
1071void ScInterpreter::ScRawSubtract()
1072{
1073 short nParamCount = GetByte();
1074 if (!MustHaveParamCountMin( nParamCount, 2))
1075 return;
1076
1077 // Fish the 1st parameter from the stack and push it on top.
1078 const FormulaToken* p = pStack[ sp - nParamCount ];
1079 PushWithoutError( *p );
1080 // Obtain the minuend.
1081 double fRes = GetDouble();
1082
1083 while (nGlobalError == FormulaError::NONE && nParamCount > 1)
1084 {
1085 // Simple single values without matrix support.
1086 fRes -= GetDouble();
1087 --nParamCount;
1088 }
1089 while (nParamCount-- > 0)
1090 PopError();
1091
1092 PushDouble( fRes);
1093}
1094
1095/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/sc/inc/mtvfunctions.hxx

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
10#ifndef INCLUDED_SC_INC_MTVFUNCTIONS_HXX
11#define INCLUDED_SC_INC_MTVFUNCTIONS_HXX
12
13#include <cstdlib>
14#include <mdds/multi_type_vector_types.hpp>
15
16namespace sc {
17
18template<typename SizeT, typename Ret = bool>
19struct FuncElseNoOp
20{
21 Ret operator() (mdds::mtv::element_t, SizeT, SizeT) const
22 {
23 return Ret();
24 }
25};
26
27/**
28 * Generic algorithm to parse blocks of multi_type_vector either partially
29 * or fully.
30 */
31template<typename StoreT, typename Func>
32typename StoreT::const_iterator
33ParseBlock(
34 const typename StoreT::const_iterator& itPos, const StoreT& rStore, Func& rFunc,
35 typename StoreT::size_type nStart, typename StoreT::size_type nEnd)
36{
37 typedef std::pair<typename StoreT::const_iterator, typename StoreT::size_type> PositionType;
38
39 PositionType aPos = rStore.position(itPos, nStart);
40 typename StoreT::const_iterator it = aPos.first;
41 typename StoreT::size_type nOffset = aPos.second;
42 typename StoreT::size_type nDataSize = 0;
43 typename StoreT::size_type nTopRow = nStart;
44
45 for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize)
6
Assuming the condition is true
7
Assuming 'nTopRow' is <= 'nEnd'
8
Loop condition is true. Entering loop body
46 {
47 bool bLastBlock = false;
48 nDataSize = it->size - nOffset;
49 if (nTopRow + nDataSize - 1 > nEnd)
9
Assuming the condition is false
10
Taking false branch
50 {
51 // Truncate the block.
52 nDataSize = nEnd - nTopRow + 1;
53 bLastBlock = true;
54 }
55
56 rFunc(*it, nOffset, nDataSize);
11
Calling 'NumericCellAccumulator::operator()'
57
58 if (bLastBlock)
59 break;
60 }
61
62 return it;
63}
64
65/**
66 * Non-const variant of the above function. TODO: Find a way to merge these
67 * two in an elegant way.
68 */
69template<typename StoreT, typename Func>
70typename StoreT::iterator
71ProcessBlock(const typename StoreT::iterator& itPos, StoreT& rStore, Func& rFunc, typename StoreT::size_type nStart, typename StoreT::size_type nEnd)
72{
73 typedef std::pair<typename StoreT::iterator, typename StoreT::size_type> PositionType;
74
75 PositionType aPos = rStore.position(itPos, nStart);
76 typename StoreT::iterator it = aPos.first;
77 typename StoreT::size_type nOffset = aPos.second;
78 typename StoreT::size_type nDataSize = 0;
79 typename StoreT::size_type nCurRow = nStart;
80
81 for (; it != rStore.end() && nCurRow <= nEnd; ++it, nOffset = 0, nCurRow += nDataSize)
82 {
83 bool bLastBlock = false;
84 nDataSize = it->size - nOffset;
85 if (nCurRow + nDataSize - 1 > nEnd)
86 {
87 // Truncate the block.
88 nDataSize = nEnd - nCurRow + 1;
89 bLastBlock = true;
90 }
91
92 rFunc(*it, nOffset, nDataSize);
93
94 if (bLastBlock)
95 break;
96 }
97
98 return it;
99}
100
101template<typename BlkT, typename ItrT, typename NodeT, typename FuncElem>
102void EachElem(NodeT& rNode, size_t nOffset, size_t nDataSize, FuncElem& rFuncElem)
103{
104 ItrT it = BlkT::begin(*rNode.data);
105 std::advance(it, nOffset);
106 ItrT itEnd = it;
107 std::advance(itEnd, nDataSize);
108 size_t nRow = rNode.position + nOffset;
109 for (; it != itEnd; ++it, ++nRow)
110 rFuncElem(nRow, *it);
111}
112
113template<typename BlkT, typename ItrT, typename NodeT, typename FuncElem>
114void EachElem(NodeT& rNode, FuncElem& rFuncElem)
115{
116 auto it = BlkT::begin(*rNode.data);
117 auto itEnd = BlkT::end(*rNode.data);
118 size_t nRow = rNode.position;
119 for (; it != itEnd; ++it, ++nRow)
120 rFuncElem(nRow, *it);
121}
122
123template<typename BlkT, typename ItrT, typename NodeT, typename FuncElem>
124void EachElemReverse(NodeT& rNode, FuncElem& rFuncElem)
125{
126 auto it = BlkT::rbegin(*rNode.data);
127 auto itEnd = BlkT::rend(*rNode.data);
128 size_t nRow = rNode.position;
129 for (; it != itEnd; ++it, ++nRow)
130 rFuncElem(nRow, *it);
131}
132
133template<typename BlkT, typename StoreT, typename FuncElem>
134std::pair<typename StoreT::const_iterator, size_t>
135CheckElem(
136 const StoreT& rStore, const typename StoreT::const_iterator& it, size_t nOffset, size_t nDataSize,
137 FuncElem& rFuncElem)
138{
139 typedef std::pair<typename StoreT::const_iterator, size_t> PositionType;
140
141 typename BlkT::const_iterator itData = BlkT::begin(*it->data);
142 std::advance(itData, nOffset);
143 typename BlkT::const_iterator itDataEnd = itData;
144 std::advance(itDataEnd, nDataSize);
145 size_t nTopRow = it->position + nOffset;
146 size_t nRow = nTopRow;
147 for (; itData != itDataEnd; ++itData, ++nRow)
148 {
149 if (rFuncElem(nRow, *itData))
150 return PositionType(it, nRow - it->position);
151 }
152
153 return PositionType(rStore.end(), 0);
154}
155
156template<typename StoreT, typename BlkT, typename FuncElem, typename FuncElse>
157void ParseElements1(const StoreT& rStore, FuncElem& rFuncElem, FuncElse& rFuncElse)
158{
159 typename StoreT::size_type nTopRow = 0, nDataSize = 0;
160 typename StoreT::const_iterator it = rStore.begin(), itEnd = rStore.end();
161 for (; it != itEnd; ++it, nTopRow += nDataSize)
162 {
163 nDataSize = it->size;
164 if (it->type != BlkT::block_type)
165 {
166 rFuncElse(it->type, nTopRow, nDataSize);
167 continue;
168 }
169
170 EachElem<BlkT, typename BlkT::const_iterator>(*it, rFuncElem);
171 }
172}
173
174template<typename StoreT, typename BlkT, typename FuncElem, typename FuncElse>
175typename StoreT::const_iterator
176ParseElements1(
177 const typename StoreT::const_iterator& itPos, const StoreT& rStore,
178 typename StoreT::size_type nStart, typename StoreT::size_type nEnd,
179 FuncElem& rFuncElem, FuncElse& rFuncElse)
180{
181 typedef std::pair<typename StoreT::const_iterator, typename StoreT::size_type> PositionType;
182
183 PositionType aPos = rStore.position(itPos, nStart);
184 typename StoreT::const_iterator it = aPos.first;
185 typename StoreT::size_type nOffset = aPos.second;
186 typename StoreT::size_type nDataSize = 0;
187 typename StoreT::size_type nTopRow = nStart;
188
189 for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize)
190 {
191 bool bLastBlock = false;
192 nDataSize = it->size - nOffset;
193 if (nTopRow + nDataSize - 1 > nEnd)
194 {
195 // Truncate the block.
196 nDataSize = nEnd - nTopRow + 1;
197 bLastBlock = true;
198 }
199
200 if (it->type == BlkT::block_type)
201 EachElem<BlkT, typename BlkT::const_iterator>(*it, nOffset, nDataSize, rFuncElem);
202 else
203 rFuncElse(it->type, nTopRow, nDataSize);
204
205 if (bLastBlock)
206 break;
207 }
208
209 return it;
210};
211
212template<typename StoreT, typename Blk1, typename Blk2, typename FuncElem, typename FuncElse>
213typename StoreT::const_iterator
214ParseElements2(
215 const typename StoreT::const_iterator& itPos, const StoreT& rStore, typename StoreT::size_type nStart, typename StoreT::size_type nEnd,
216 FuncElem& rFuncElem, FuncElse& rFuncElse)
217{
218 typedef std::pair<typename StoreT::const_iterator, typename StoreT::size_type> PositionType;
219
220 PositionType aPos = rStore.position(itPos, nStart);
221 typename StoreT::const_iterator it = aPos.first;
222 typename StoreT::size_type nOffset = aPos.second;
223 typename StoreT::size_type nDataSize = 0;
224 typename StoreT::size_type nTopRow = nStart;
225
226 for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize)
227 {
228 bool bLastBlock = false;
229 nDataSize = it->size - nOffset;
230 if (nTopRow + nDataSize - 1 > nEnd)
231 {
232 // Truncate the block.
233 nDataSize = nEnd - nTopRow + 1;
234 bLastBlock = true;
235 }
236
237 switch (it->type)
238 {
239 case Blk1::block_type:
240 EachElem<Blk1, typename Blk1::const_iterator>(*it, nOffset, nDataSize, rFuncElem);
241 break;
242 case Blk2::block_type:
243 EachElem<Blk2, typename Blk2::const_iterator>(*it, nOffset, nDataSize, rFuncElem);
244 break;
245 default:
246 rFuncElse(it->type, nTopRow, nDataSize);
247 }
248
249 if (bLastBlock)
250 break;
251 }
252
253 return it;
254}
255
256template<typename StoreT, typename Blk1, typename Blk2, typename Blk3, typename Blk4, typename FuncElem, typename FuncElse>
257typename StoreT::const_iterator
258ParseElements4(
259 const typename StoreT::const_iterator& itPos, const StoreT& rStore, typename StoreT::size_type nStart, typename StoreT::size_type nEnd,
260 FuncElem& rFuncElem, FuncElse& rFuncElse)
261{
262 typedef std::pair<typename StoreT::const_iterator, typename StoreT::size_type> PositionType;
263
264 PositionType aPos = rStore.position(itPos, nStart);
265 typename StoreT::const_iterator it = aPos.first;
266 typename StoreT::size_type nOffset = aPos.second;
267 typename StoreT::size_type nDataSize = 0;
268 typename StoreT::size_type nTopRow = nStart;
269
270 for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize)
271 {
272 bool bLastBlock = false;
273 nDataSize = it->size - nOffset;
274 if (nTopRow + nDataSize - 1 > nEnd)
275 {
276 // Truncate the block.
277 nDataSize = nEnd - nTopRow + 1;
278 bLastBlock = true;
279 }
280
281 switch (it->type)
282 {
283 case Blk1::block_type:
284 EachElem<Blk1, typename Blk1::const_iterator>(*it, nOffset, nDataSize, rFuncElem);
285 break;
286 case Blk2::block_type:
287 EachElem<Blk2, typename Blk2::const_iterator>(*it, nOffset, nDataSize, rFuncElem);
288 break;
289 case Blk3::block_type:
290 EachElem<Blk3, typename Blk3::const_iterator>(*it, nOffset, nDataSize, rFuncElem);
291 break;
292 case Blk4::block_type:
293 EachElem<Blk4, typename Blk4::const_iterator>(*it, nOffset, nDataSize, rFuncElem);
294 break;
295 default:
296 rFuncElse(it->type, nTopRow, nDataSize);
297 }
298
299 if (bLastBlock)
300 break;
301 }
302
303 return it;
304}
305
306template<typename StoreT, typename BlkT, typename FuncElem, typename FuncElse>
307void ProcessElements1(StoreT& rStore, FuncElem& rFuncElem, FuncElse& rFuncElse)
308{
309 typename StoreT::size_type nTopRow = 0, nDataSize = 0;
310 typename StoreT::iterator it = rStore.begin(), itEnd = rStore.end();
311 for (; it != itEnd; ++it, nTopRow += nDataSize)
312 {
313 nDataSize = it->size;
314 if (it->type != BlkT::block_type)
315 {
316 rFuncElse(it->type, nTopRow, nDataSize);
317 continue;
318 }
319
320 EachElem<BlkT, typename BlkT::iterator>(*it, rFuncElem);
321 }
322}
323
324/**
325 * This variant specifies start and end positions.
326 */
327template<typename StoreT, typename BlkT, typename FuncElem, typename FuncElse>
328typename StoreT::iterator
329ProcessElements1(
330 const typename StoreT::iterator& itPos, StoreT& rStore,
331 typename StoreT::size_type nStart, typename StoreT::size_type nEnd,
332 FuncElem& rFuncElem, FuncElse& rFuncElse)
333{
334 typedef std::pair<typename StoreT::iterator, typename StoreT::size_type> PositionType;
335
336 PositionType aPos = rStore.position(itPos, nStart);
337 typename StoreT::iterator it = aPos.first;
338 typename StoreT::size_type nOffset = aPos.second;
339 typename StoreT::size_type nDataSize = 0;
340 typename StoreT::size_type nTopRow = nStart;
341
342 for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize)
343 {
344 bool bLastBlock = false;
345 nDataSize = it->size - nOffset;
346 if (nTopRow + nDataSize - 1 > nEnd)
347 {
348 // Truncate the block.
349 nDataSize = nEnd - nTopRow + 1;
350 bLastBlock = true;
351 }
352
353 if (it->type == BlkT::block_type)
354 EachElem<BlkT, typename BlkT::iterator>(*it, nOffset, nDataSize, rFuncElem);
355 else
356 rFuncElse(it->type, nTopRow, nDataSize);
357
358 if (bLastBlock)
359 break;
360 }
361
362 return it;
363};
364
365template<typename StoreT, typename Blk1, typename Blk2, typename FuncElem, typename FuncElse>
366void ProcessElements2(StoreT& rStore, FuncElem& rFuncElem, FuncElse& rFuncElse)
367{
368 typename StoreT::size_type nTopRow = 0, nDataSize = 0;
369 typename StoreT::iterator it = rStore.begin(), itEnd = rStore.end();
370 for (; it != itEnd; ++it, nTopRow += nDataSize)
371 {
372 nDataSize = it->size;
373 switch (it->type)
374 {
375 case Blk1::block_type:
376 EachElem<Blk1, typename Blk1::iterator>(*it, rFuncElem);
377 break;
378 case Blk2::block_type:
379 EachElem<Blk2, typename Blk2::iterator>(*it, rFuncElem);
380 break;
381 default:
382 rFuncElse(it->type, nTopRow, nDataSize);
383 }
384 }
385}
386
387template<typename StoreT, typename Blk1, typename Blk2, typename FuncElem, typename FuncElse>
388void ProcessElements2Reverse(StoreT& rStore, FuncElem& rFuncElem, FuncElse& rFuncElse)
389{
390 typename StoreT::size_type nTopRow = 0, nDataSize = 0;
391 typename StoreT::iterator it = rStore.begin(), itEnd = rStore.end();
392 for (; it != itEnd; ++it, nTopRow += nDataSize)
393 {
394 nDataSize = it->size;
395 switch (it->type)
396 {
397 case Blk1::block_type:
398 EachElemReverse<Blk1, typename Blk1::iterator>(*it, rFuncElem);
399 break;
400 case Blk2::block_type:
401 EachElemReverse<Blk2, typename Blk2::iterator>(*it, rFuncElem);
402 break;
403 default:
404 rFuncElse(it->type, nTopRow, nDataSize);
405 }
406 }
407}
408
409template<typename StoreT, typename Blk1, typename FuncElem, typename FuncElse>
410std::pair<typename StoreT::const_iterator, typename StoreT::size_type>
411FindElement1(
412 const StoreT& rStore, typename StoreT::size_type nStart, typename StoreT::size_type nEnd,
413 FuncElem& rFuncElem, FuncElse& rFuncElse)
414{
415 typedef std::pair<typename StoreT::const_iterator, typename StoreT::size_type> PositionType;
416 typedef std::pair<typename StoreT::size_type, bool> ElseRetType;
417
418 PositionType aPos = rStore.position(nStart);
419 typename StoreT::const_iterator it = aPos.first;
420 typename StoreT::size_type nOffset = aPos.second;
421 typename StoreT::size_type nDataSize = 0;
422 typename StoreT::size_type nTopRow = nStart;
423
424 for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize)
425 {
426 bool bLastBlock = false;
427 nDataSize = it->size - nOffset;
428 if (nTopRow + nDataSize - 1 > nEnd)
429 {
430 // Truncate the block.
431 nDataSize = nEnd - nTopRow + 1;
432 bLastBlock = true;
433 }
434
435 switch (it->type)
436 {
437 case Blk1::block_type:
438 {
439 PositionType aRet = CheckElem<Blk1>(rStore, it, nOffset, nDataSize, rFuncElem);
440 if (aRet.first != rStore.end())
441 return aRet;
442 }
443 break;
444 default:
445 {
446 ElseRetType aRet = rFuncElse(it->type, nTopRow, nDataSize);
447 if (aRet.second)
448 return PositionType(it, aRet.first);
449 }
450 }
451
452 if (bLastBlock)
453 break;
454 }
455
456 return PositionType(rStore.end(), 0);
457}
458
459template<typename StoreT, typename Blk1, typename Blk2, typename FuncElem, typename FuncElse>
460std::pair<typename StoreT::const_iterator, typename StoreT::size_type>
461FindElement2(
462 const StoreT& rStore, typename StoreT::size_type nStart, typename StoreT::size_type nEnd,
463 FuncElem& rFuncElem, FuncElse& rFuncElse)
464{
465 typedef std::pair<typename StoreT::const_iterator, typename StoreT::size_type> PositionType;
466 typedef std::pair<typename StoreT::size_type, bool> ElseRetType;
467
468 PositionType aPos = rStore.position(nStart);
469 typename StoreT::const_iterator it = aPos.first;
470 typename StoreT::size_type nOffset = aPos.second;
471 typename StoreT::size_type nDataSize = 0;
472 typename StoreT::size_type nTopRow = nStart;
473
474 for (; it != rStore.end() && nTopRow <= nEnd; ++it, nOffset = 0, nTopRow += nDataSize)
475 {
476 bool bLastBlock = false;
477 nDataSize = it->size - nOffset;
478 if (nTopRow + nDataSize - 1 > nEnd)
479 {
480 // Truncate the block.
481 nDataSize = nEnd - nTopRow + 1;
482 bLastBlock = true;
483 }
484
485 switch (it->type)
486 {
487 case Blk1::block_type:
488 {
489 PositionType aRet = CheckElem<Blk1>(rStore, it, nOffset, nDataSize, rFuncElem);
490 if (aRet.first != rStore.end())
491 return aRet;
492 }
493 break;
494 case Blk2::block_type:
495 {
496 PositionType aRet = CheckElem<Blk2>(rStore, it, nOffset, nDataSize, rFuncElem);
497 if (aRet.first != rStore.end())
498 return aRet;
499 }
500 break;
501 default:
502 {
503 ElseRetType aRet = rFuncElse(*it, nOffset, nDataSize);
504 if (aRet.second)
505 return PositionType(it, aRet.first);
506 }
507 }
508
509 if (bLastBlock)
510 break;
511 }
512
513 return PositionType(rStore.end(), 0);
514}
515
516}
517
518#endif
519
520/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/sc/source/core/inc/arraysumfunctor.hxx

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 */
10
11#ifndef INCLUDED_SC_SOURCE_CORE_INC_ARRAYSUMFUNCTOR_HXX
12#define INCLUDED_SC_SOURCE_CORE_INC_ARRAYSUMFUNCTOR_HXX
13
14#include <cstdint>
15#include <rtl/math.hxx>
16
17#include <tools/simd.hxx>
18#include <tools/cpuid.hxx>
19
20namespace sc
21{
22
23struct ArraySumFunctor
24{
25private:
26 const double* mpArray;
27 size_t mnSize;
28
29public:
30 ArraySumFunctor(const double* pArray, size_t nSize)
31 : mpArray(pArray)
32 , mnSize(nSize)
33 {
34 }
35
36 double operator() ()
37 {
38 const static bool hasSSE2 = cpuid::hasSSE2();
39
40 double fSum = 0.0;
41 size_t i = 0;
42 const double* pCurrent = mpArray;
43
44 if (hasSSE2)
18
Assuming 'hasSSE2' is false
19
Taking false branch
45 {
46 while ( i < mnSize && !simd::isAligned<double, 16>(pCurrent))
47 {
48 fSum += *pCurrent++;
49 i++;
50 }
51 if( i < mnSize )
52 {
53 fSum += executeSSE2(i, pCurrent);
54 }
55 }
56 else
57 fSum += executeUnrolled(i, pCurrent);
58
59 // sum rest of the array
60
61 for (; i < mnSize; ++i)
20
Assuming 'i' is >= field 'mnSize'
21
Loop condition is false. Execution continues on line 66
62 fSum += mpArray[i];
63
64 // If the sum is a NaN, some of the terms were empty cells, probably.
65 // Re-calculate, carefully
66 if (!std::isfinite(fSum))
22
Assuming the condition is true
23
Taking true branch
67 {
68 sal_uInt32 nErr = reinterpret_cast< sal_math_Double * >(&fSum)->nan_parts.fraction_lo;
24
Assigned value is garbage or undefined
69 if (nErr & 0xffff0000)
70 {
71 fSum = 0;
72 for (i = 0; i < mnSize; i++)
73 {
74 if (!std::isfinite(mpArray[i]))
75 {
76 nErr = reinterpret_cast< const sal_math_Double * >(&mpArray[i])->nan_parts.fraction_lo;
77 if (!(nErr & 0xffff0000))
78 fSum += mpArray[i]; // Let errors encoded as NaNs propagate ???
79 }
80 else
81 fSum += mpArray[i];
82 }
83 }
84 }
85 return fSum;
86 }
87
88private:
89
90 double executeSSE2(size_t& i, const double* pCurrent) const;
91 double executeUnrolled(size_t& i, const double* pCurrent) const
92 {
93 size_t nRealSize = mnSize - i;
94 size_t nUnrolledSize = nRealSize - (nRealSize % 4);
95
96 if (nUnrolledSize > 0)
97 {
98 double sum0 = 0.0;
99 double sum1 = 0.0;
100 double sum2 = 0.0;
101 double sum3 = 0.0;
102
103 for (; i < nUnrolledSize; i += 4)
104 {
105 sum0 += *pCurrent++;
106 sum1 += *pCurrent++;
107 sum2 += *pCurrent++;
108 sum3 += *pCurrent++;
109 }
110 return sum0 + sum1 + sum2 + sum3;
111 }
112 return 0.0;
113 }
114};
115
116} // end namespace sc
117
118#endif
119
120/* vim:set shiftwidth=4 softtabstop=4 expandtab: */