diff options
Diffstat (limited to 'sc/source/core/tool/interpr6.cxx')
-rw-r--r-- | sc/source/core/tool/interpr6.cxx | 1010 |
1 files changed, 1010 insertions, 0 deletions
diff --git a/sc/source/core/tool/interpr6.cxx b/sc/source/core/tool/interpr6.cxx new file mode 100644 index 000000000..545bbd58c --- /dev/null +++ b/sc/source/core/tool/interpr6.cxx @@ -0,0 +1,1010 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <interpre.hxx> +#include <columnspanset.hxx> +#include <column.hxx> +#include <document.hxx> +#include <cellvalue.hxx> +#include <dociter.hxx> +#include <mtvfunctions.hxx> +#include <scmatrix.hxx> + +#include <arraysumfunctor.hxx> + +#include <formula/token.hxx> + +using namespace formula; + +double const fHalfMachEps = 0.5 * ::std::numeric_limits<double>::epsilon(); + +// The idea how this group of gamma functions is calculated, is +// based on the Cephes library +// online http://www.moshier.net/#Cephes [called 2008-02] + +/** You must ensure fA>0.0 && fX>0.0 + valid results only if fX > fA+1.0 + uses continued fraction with odd items */ +double ScInterpreter::GetGammaContFraction( double fA, double fX ) +{ + + double const fBigInv = ::std::numeric_limits<double>::epsilon(); + double const fBig = 1.0/fBigInv; + double fCount = 0.0; + double fY = 1.0 - fA; + double fDenom = fX + 2.0-fA; + double fPkm1 = fX + 1.0; + double fPkm2 = 1.0; + double fQkm1 = fDenom * fX; + double fQkm2 = fX; + double fApprox = fPkm1/fQkm1; + bool bFinished = false; + do + { + fCount = fCount +1.0; + fY = fY+ 1.0; + const double fNum = fY * fCount; + fDenom = fDenom +2.0; + double fPk = fPkm1 * fDenom - fPkm2 * fNum; + const double fQk = fQkm1 * fDenom - fQkm2 * fNum; + if (fQk != 0.0) + { + const double fR = fPk/fQk; + bFinished = (fabs( (fApprox - fR)/fR ) <= fHalfMachEps); + fApprox = fR; + } + fPkm2 = fPkm1; + fPkm1 = fPk; + fQkm2 = fQkm1; + fQkm1 = fQk; + if (fabs(fPk) > fBig) + { + // reduce a fraction does not change the value + fPkm2 = fPkm2 * fBigInv; + fPkm1 = fPkm1 * fBigInv; + fQkm2 = fQkm2 * fBigInv; + fQkm1 = fQkm1 * fBigInv; + } + } while (!bFinished && fCount<10000); + // most iterations, if fX==fAlpha+1.0; approx sqrt(fAlpha) iterations then + if (!bFinished) + { + SetError(FormulaError::NoConvergence); + } + return fApprox; +} + +/** You must ensure fA>0.0 && fX>0.0 + valid results only if fX <= fA+1.0 + uses power series */ +double ScInterpreter::GetGammaSeries( double fA, double fX ) +{ + double fDenomfactor = fA; + double fSummand = 1.0/fA; + double fSum = fSummand; + int nCount=1; + do + { + fDenomfactor = fDenomfactor + 1.0; + fSummand = fSummand * fX/fDenomfactor; + fSum = fSum + fSummand; + nCount = nCount+1; + } while ( fSummand/fSum > fHalfMachEps && nCount<=10000); + // large amount of iterations will be carried out for huge fAlpha, even + // if fX <= fAlpha+1.0 + if (nCount>10000) + { + SetError(FormulaError::NoConvergence); + } + return fSum; +} + +/** You must ensure fA>0.0 && fX>0.0) */ +double ScInterpreter::GetLowRegIGamma( double fA, double fX ) +{ + double fLnFactor = fA * log(fX) - fX - GetLogGamma(fA); + double fFactor = exp(fLnFactor); // Do we need more accuracy than exp(ln()) has? + if (fX>fA+1.0) // includes fX>1.0; 1-GetUpRegIGamma, continued fraction + return 1.0 - fFactor * GetGammaContFraction(fA,fX); + else // fX<=1.0 || fX<=fA+1.0, series + return fFactor * GetGammaSeries(fA,fX); +} + +/** You must ensure fA>0.0 && fX>0.0) */ +double ScInterpreter::GetUpRegIGamma( double fA, double fX ) +{ + + double fLnFactor= fA*log(fX)-fX-GetLogGamma(fA); + double fFactor = exp(fLnFactor); //Do I need more accuracy than exp(ln()) has?; + if (fX>fA+1.0) // includes fX>1.0 + return fFactor * GetGammaContFraction(fA,fX); + else //fX<=1 || fX<=fA+1, 1-GetLowRegIGamma, series + return 1.0 -fFactor * GetGammaSeries(fA,fX); +} + +/** Gamma distribution, probability density function. + fLambda is "scale" parameter + You must ensure fAlpha>0.0 and fLambda>0.0 */ +double ScInterpreter::GetGammaDistPDF( double fX, double fAlpha, double fLambda ) +{ + if (fX < 0.0) + return 0.0; // see ODFF + else if (fX == 0) + // in this case 0^0 isn't zero + { + if (fAlpha < 1.0) + { + SetError(FormulaError::DivisionByZero); // should be #DIV/0 + return HUGE_VAL; + } + else if (fAlpha == 1) + { + return (1.0 / fLambda); + } + else + { + return 0.0; + } + } + else + { + double fXr = fX / fLambda; + // use exp(ln()) only for large arguments because of less accuracy + if (fXr > 1.0) + { + const double fLogDblMax = log( ::std::numeric_limits<double>::max()); + if (log(fXr) * (fAlpha-1.0) < fLogDblMax && fAlpha < fMaxGammaArgument) + { + return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / GetGamma(fAlpha); + } + else + { + return exp( (fAlpha-1.0) * log(fXr) - fXr - log(fLambda) - GetLogGamma(fAlpha)); + } + } + else // fXr near to zero + { + if (fAlpha<fMaxGammaArgument) + { + return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / GetGamma(fAlpha); + } + else + { + return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / exp( GetLogGamma(fAlpha)); + } + } + } +} + +/** Gamma distribution, cumulative distribution function. + fLambda is "scale" parameter + You must ensure fAlpha>0.0 and fLambda>0.0 */ +double ScInterpreter::GetGammaDist( double fX, double fAlpha, double fLambda ) +{ + if (fX <= 0.0) + return 0.0; + else + return GetLowRegIGamma( fAlpha, fX / fLambda); +} + +namespace { + +class NumericCellAccumulator +{ + KahanSum maSum; + FormulaError mnError; + +public: + NumericCellAccumulator() : maSum(0.0), mnError(FormulaError::NONE) {} + + void operator() (const sc::CellStoreType::value_type& rNode, size_t nOffset, size_t nDataSize) + { + switch (rNode.type) + { + case sc::element_type_numeric: + { + if (nDataSize == 0) + return; + + const double *p = &sc::numeric_block::at(*rNode.data, nOffset); + maSum += sc::op::sumArray(p, nDataSize); + break; + } + + case sc::element_type_formula: + { + sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data); + std::advance(it, nOffset); + sc::formula_block::const_iterator itEnd = it; + std::advance(itEnd, nDataSize); + for (; it != itEnd; ++it) + { + double fVal = 0.0; + FormulaError nErr = FormulaError::NONE; + ScFormulaCell& rCell = *(*it); + if (!rCell.GetErrorOrValue(nErr, fVal)) + // The cell has neither error nor value. Perhaps string result. + continue; + + if (nErr != FormulaError::NONE) + { + // Cell has error - skip all the rest + mnError = nErr; + return; + } + + maSum += fVal; + } + } + break; + default: + ; + } + } + + FormulaError getError() const { return mnError; } + const KahanSum& getResult() const { return maSum; } +}; + +class NumericCellCounter +{ + size_t mnCount; +public: + NumericCellCounter() : mnCount(0) {} + + void operator() (const sc::CellStoreType::value_type& rNode, size_t nOffset, size_t nDataSize) + { + switch (rNode.type) + { + case sc::element_type_numeric: + mnCount += nDataSize; + break; + case sc::element_type_formula: + { + sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data); + std::advance(it, nOffset); + sc::formula_block::const_iterator itEnd = it; + std::advance(itEnd, nDataSize); + for (; it != itEnd; ++it) + { + ScFormulaCell& rCell = **it; + if (rCell.IsValueNoError()) + ++mnCount; + } + } + break; + default: + ; + } + } + + size_t getCount() const { return mnCount; } +}; + +class FuncCount : public sc::ColumnSpanSet::ColumnAction +{ + const ScInterpreterContext& mrContext; + sc::ColumnBlockConstPosition maPos; + ScColumn* mpCol; + size_t mnCount; + sal_uInt32 mnNumFmt; + +public: + FuncCount(const ScInterpreterContext& rContext) : mrContext(rContext), mpCol(nullptr), mnCount(0), mnNumFmt(0) {} + + virtual void startColumn(ScColumn* pCol) override + { + mpCol = pCol; + mpCol->InitBlockPosition(maPos); + } + + virtual void execute(SCROW nRow1, SCROW nRow2, bool bVal) override + { + if (!bVal) + return; + + NumericCellCounter aFunc; + maPos.miCellPos = sc::ParseBlock(maPos.miCellPos, mpCol->GetCellStore(), aFunc, nRow1, nRow2); + mnCount += aFunc.getCount(); + mnNumFmt = mpCol->GetNumberFormat(mrContext, nRow2); + }; + + size_t getCount() const { return mnCount; } + sal_uInt32 getNumberFormat() const { return mnNumFmt; } +}; + +class FuncSum : public sc::ColumnSpanSet::ColumnAction +{ + const ScInterpreterContext& mrContext; + sc::ColumnBlockConstPosition maPos; + ScColumn* mpCol; + KahanSum mfSum; + FormulaError mnError; + sal_uInt32 mnNumFmt; + +public: + FuncSum(const ScInterpreterContext& rContext) : mrContext(rContext), mpCol(nullptr), mfSum(0.0), mnError(FormulaError::NONE), mnNumFmt(0) {} + + virtual void startColumn(ScColumn* pCol) override + { + mpCol = pCol; + mpCol->InitBlockPosition(maPos); + } + + virtual void execute(SCROW nRow1, SCROW nRow2, bool bVal) override + { + if (!bVal) + return; + + if (mnError != FormulaError::NONE) + return; + + NumericCellAccumulator aFunc; + maPos.miCellPos = sc::ParseBlock(maPos.miCellPos, mpCol->GetCellStore(), aFunc, nRow1, nRow2); + mnError = aFunc.getError(); + if (mnError != FormulaError::NONE) + return; + + + mfSum += aFunc.getResult(); + mnNumFmt = mpCol->GetNumberFormat(mrContext, nRow2); + }; + + FormulaError getError() const { return mnError; } + const KahanSum& getSum() const { return mfSum; } + sal_uInt32 getNumberFormat() const { return mnNumFmt; } +}; + +} + +static void IterateMatrix( + const ScMatrixRef& pMat, ScIterFunc eFunc, bool bTextAsZero, SubtotalFlags nSubTotalFlags, + sal_uLong& rCount, SvNumFormatType& rFuncFmtType, KahanSum& fRes ) +{ + if (!pMat) + return; + + const bool bIgnoreErrVal = bool(nSubTotalFlags & SubtotalFlags::IgnoreErrVal); + rFuncFmtType = SvNumFormatType::NUMBER; + switch (eFunc) + { + case ifAVERAGE: + case ifSUM: + { + ScMatrix::KahanIterateResult aRes = pMat->Sum(bTextAsZero, bIgnoreErrVal); + fRes += aRes.maAccumulator; + rCount += aRes.mnCount; + } + break; + case ifCOUNT: + rCount += pMat->Count(bTextAsZero, false); // do not count error values + break; + case ifCOUNT2: + /* TODO: what is this supposed to be with bIgnoreErrVal? */ + rCount += pMat->Count(true, true); // do count error values + break; + case ifPRODUCT: + { + ScMatrix::DoubleIterateResult aRes = pMat->Product(bTextAsZero, bIgnoreErrVal); + fRes *= aRes.maAccumulator; + rCount += aRes.mnCount; + } + break; + case ifSUMSQ: + { + ScMatrix::KahanIterateResult aRes = pMat->SumSquare(bTextAsZero, bIgnoreErrVal); + fRes += aRes.maAccumulator; + rCount += aRes.mnCount; + } + break; + default: + ; + } +} + +size_t ScInterpreter::GetRefListArrayMaxSize( short nParamCount ) +{ + size_t nSize = 0; + if (IsInArrayContext()) + { + for (short i=1; i <= nParamCount; ++i) + { + if (GetStackType(i) == svRefList) + { + const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp - i]); + if (p && p->IsArrayResult() && p->GetRefList()->size() > nSize) + nSize = p->GetRefList()->size(); + } + } + } + return nSize; +} + +static double lcl_IterResult( ScIterFunc eFunc, double fRes, sal_uLong nCount ) +{ + switch( eFunc ) + { + case ifAVERAGE: + fRes = sc::div( fRes, nCount); + break; + case ifCOUNT2: + case ifCOUNT: + fRes = nCount; + break; + case ifPRODUCT: + if ( !nCount ) + fRes = 0.0; + break; + default: + ; // nothing + } + return fRes; +} + +void ScInterpreter::IterateParameters( ScIterFunc eFunc, bool bTextAsZero ) +{ + short nParamCount = GetByte(); + const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount); + ScMatrixRef xResMat, xResCount; + const double ResInitVal = (eFunc == ifPRODUCT) ? 1.0 : 0.0; + KahanSum fRes = ResInitVal; + double fVal = 0.0; + sal_uLong nCount = 0; + ScAddress aAdr; + ScRange aRange; + size_t nRefInList = 0; + size_t nRefArrayPos = std::numeric_limits<size_t>::max(); + if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT || + ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ) + nGlobalError = FormulaError::NONE; + while (nParamCount-- > 0) + { + switch (GetStackType()) + { + case svString: + { + if( eFunc == ifCOUNT ) + { + OUString aStr = PopString().getString(); + if ( bTextAsZero ) + nCount++; + else + { + // Only check if string can be converted to number, no + // error propagation. + FormulaError nErr = nGlobalError; + nGlobalError = FormulaError::NONE; + ConvertStringToValue( aStr ); + if (nGlobalError == FormulaError::NONE) + ++nCount; + nGlobalError = nErr; + } + } + else + { + Pop(); + switch ( eFunc ) + { + case ifAVERAGE: + case ifSUM: + case ifSUMSQ: + case ifPRODUCT: + { + if ( bTextAsZero ) + { + nCount++; + if ( eFunc == ifPRODUCT ) + fRes = 0.0; + } + else + { + while (nParamCount-- > 0) + PopError(); + SetError( FormulaError::NoValue ); + } + } + break; + default: + nCount++; + } + } + } + break; + case svDouble : + fVal = GetDouble(); + nCount++; + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: fRes += fVal; break; + case ifSUMSQ: fRes += fVal * fVal; break; + case ifPRODUCT: fRes *= fVal; break; + default: ; // nothing + } + nFuncFmtType = SvNumFormatType::NUMBER; + break; + case svExternalSingleRef: + { + ScExternalRefCache::TokenRef pToken; + ScExternalRefCache::CellFormat aFmt; + PopExternalSingleRef(pToken, &aFmt); + if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT || + ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ) + { + nGlobalError = FormulaError::NONE; + if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) + ++nCount; + break; + } + + if (!pToken) + break; + + StackVar eType = pToken->GetType(); + if (eFunc == ifCOUNT2) + { + if ( eType != svEmptyCell && + ( ( pToken->GetOpCode() != ocSubTotal && + pToken->GetOpCode() != ocAggregate ) || + ( mnSubTotalFlags & SubtotalFlags::IgnoreNestedStAg ) ) ) + nCount++; + if (nGlobalError != FormulaError::NONE) + nGlobalError = FormulaError::NONE; + } + else if (eType == svDouble) + { + nCount++; + fVal = pToken->GetDouble(); + if (aFmt.mbIsSet) + { + nFuncFmtType = aFmt.mnType; + nFuncFmtIndex = aFmt.mnIndex; + } + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: fRes += fVal; break; + case ifSUMSQ: fRes += fVal * fVal; break; + case ifPRODUCT: fRes *= fVal; break; + case ifCOUNT: + if ( nGlobalError != FormulaError::NONE ) + { + nGlobalError = FormulaError::NONE; + nCount--; + } + break; + default: ; // nothing + } + } + else if (bTextAsZero && eType == svString) + { + nCount++; + if ( eFunc == ifPRODUCT ) + fRes = 0.0; + } + } + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + if (nGlobalError == FormulaError::NoRef) + { + PushError( FormulaError::NoRef); + return; + } + + if ( ( ( mnSubTotalFlags & SubtotalFlags::IgnoreFiltered ) && + mrDoc.RowFiltered( aAdr.Row(), aAdr.Tab() ) ) || + ( ( mnSubTotalFlags & SubtotalFlags::IgnoreHidden ) && + mrDoc.RowHidden( aAdr.Row(), aAdr.Tab() ) ) ) + { + break; + } + if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT || + ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ) + { + nGlobalError = FormulaError::NONE; + if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) + ++nCount; + break; + } + ScRefCellValue aCell(mrDoc, aAdr); + if (!aCell.isEmpty()) + { + if( eFunc == ifCOUNT2 ) + { + CellType eCellType = aCell.meType; + if ( eCellType != CELLTYPE_NONE ) + nCount++; + if ( nGlobalError != FormulaError::NONE ) + nGlobalError = FormulaError::NONE; + } + else if (aCell.hasNumeric()) + { + fVal = GetCellValue(aAdr, aCell); + if (nGlobalError != FormulaError::NONE) + { + if (eFunc == ifCOUNT || (mnSubTotalFlags & SubtotalFlags::IgnoreErrVal)) + nGlobalError = FormulaError::NONE; + break; + } + nCount++; + CurFmtToFuncFmt(); + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: fRes += fVal; break; + case ifSUMSQ: fRes += fVal * fVal; break; + case ifPRODUCT: fRes *= fVal; break; + default: ; // nothing + } + } + else if (bTextAsZero && aCell.hasString()) + { + nCount++; + if ( eFunc == ifPRODUCT ) + fRes = 0.0; + } + } + } + break; + case svRefList : + { + const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]); + if (p && p->IsArrayResult()) + { + nRefArrayPos = nRefInList; + // The "one value to all references of an array" seems to + // be what Excel does if there are other types than just + // arrays of references. + if (!xResMat) + { + // Create and init all elements with current value. + assert(nMatRows > 0); + xResMat = GetNewMat( 1, nMatRows, true); + xResMat->FillDouble( fRes.get(), 0,0, 0,nMatRows-1); + if (eFunc != ifSUM) + { + xResCount = GetNewMat( 1, nMatRows, true); + xResCount->FillDouble( nCount, 0,0, 0,nMatRows-1); + } + } + else + { + // Current value and values from vector are operands + // for each vector position. + if (nCount && xResCount) + { + for (SCSIZE i=0; i < nMatRows; ++i) + { + xResCount->PutDouble( xResCount->GetDouble(0,i) + nCount, 0,i); + } + } + if (fRes != ResInitVal) + { + for (SCSIZE i=0; i < nMatRows; ++i) + { + double fVecRes = xResMat->GetDouble(0,i); + if (eFunc == ifPRODUCT) + fVecRes *= fRes.get(); + else + fVecRes += fRes.get(); + xResMat->PutDouble( fVecRes, 0,i); + } + } + } + fRes = ResInitVal; + nCount = 0; + } + } + [[fallthrough]]; + case svDoubleRef : + { + PopDoubleRef( aRange, nParamCount, nRefInList); + if (nGlobalError == FormulaError::NoRef) + { + PushError( FormulaError::NoRef); + return; + } + + if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT || + ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ) + { + nGlobalError = FormulaError::NONE; + if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) + ++nCount; + if ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ) + break; + } + if( eFunc == ifCOUNT2 ) + { + ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags ); + for (bool bHas = aIter.first(); bHas; bHas = aIter.next()) + { + if ( !aIter.isEmpty() ) + { + ++nCount; + } + } + + if ( nGlobalError != FormulaError::NONE ) + nGlobalError = FormulaError::NONE; + } + else if (((eFunc == ifSUM && !bCalcAsShown) || eFunc == ifCOUNT ) + && mnSubTotalFlags == SubtotalFlags::NONE) + { + // Use fast span set array method. + // ifSUM with bCalcAsShown has to use the slow bells and + // whistles ScValueIterator below. + sc::RangeColumnSpanSet aSet( aRange ); + + if ( eFunc == ifSUM ) + { + FuncSum aAction(mrContext); + aSet.executeColumnAction( mrDoc, aAction ); + FormulaError nErr = aAction.getError(); + if ( nErr != FormulaError::NONE ) + { + PushError( nErr ); + return; + } + fRes += aAction.getSum(); + + // Get the number format of the last iterated cell. + nFuncFmtIndex = aAction.getNumberFormat(); + } + else + { + FuncCount aAction(mrContext); + aSet.executeColumnAction(mrDoc, aAction); + nCount += aAction.getCount(); + + // Get the number format of the last iterated cell. + nFuncFmtIndex = aAction.getNumberFormat(); + } + + nFuncFmtType = mrContext.GetNumberFormatType( nFuncFmtIndex ); + } + else + { + ScValueIterator aValIter( mrContext, mrDoc, aRange, mnSubTotalFlags, bTextAsZero ); + FormulaError nErr = FormulaError::NONE; + if (aValIter.GetFirst(fVal, nErr)) + { + // placed the loop on the inside for performance reasons: + aValIter.GetCurNumFmtInfo( mrContext, nFuncFmtType, nFuncFmtIndex ); + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: + if ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) + { + do + { + if ( nErr == FormulaError::NONE ) + { + SetError(nErr); + fRes += fVal; + nCount++; + } + } + while (aValIter.GetNext(fVal, nErr)); + } + else + { + do + { + SetError(nErr); + fRes += fVal; + nCount++; + } + while (aValIter.GetNext(fVal, nErr)); + } + break; + case ifSUMSQ: + if ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) + { + do + { + if ( nErr == FormulaError::NONE ) + { + SetError(nErr); + fRes += fVal * fVal; + nCount++; + } + } + while (aValIter.GetNext(fVal, nErr)); + } + else + { + do + { + SetError(nErr); + fRes += fVal * fVal; + nCount++; + } + while (aValIter.GetNext(fVal, nErr)); + } + break; + case ifPRODUCT: + do + { + if ( !( nErr != FormulaError::NONE && ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ) + { + SetError(nErr); + fRes *= fVal; + nCount++; + } + } + while (aValIter.GetNext(fVal, nErr)); + break; + case ifCOUNT: + do + { + if ( nErr == FormulaError::NONE ) + nCount++; + } + while (aValIter.GetNext(fVal, nErr)); + break; + default: ; // nothing + } + SetError( nErr ); + } + } + if (nRefArrayPos != std::numeric_limits<size_t>::max()) + { + // Update vector element with current value. + if (xResCount) + xResCount->PutDouble( xResCount->GetDouble(0,nRefArrayPos) + nCount, 0,nRefArrayPos); + double fVecRes = xResMat->GetDouble(0,nRefArrayPos); + if (eFunc == ifPRODUCT) + fVecRes *= fRes.get(); + else + fVecRes += fRes.get(); + xResMat->PutDouble( fVecRes, 0,nRefArrayPos); + // Reset. + fRes = ResInitVal; + nCount = 0; + nRefArrayPos = std::numeric_limits<size_t>::max(); + } + } + break; + case svExternalDoubleRef: + { + ScMatrixRef pMat; + PopExternalDoubleRef(pMat); + if ( nGlobalError != FormulaError::NONE && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) + break; + + IterateMatrix( pMat, eFunc, bTextAsZero, mnSubTotalFlags, nCount, nFuncFmtType, fRes ); + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + + IterateMatrix( pMat, eFunc, bTextAsZero, mnSubTotalFlags, nCount, nFuncFmtType, fRes ); + } + break; + case svError: + { + PopError(); + if ( eFunc == ifCOUNT || ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) + { + nGlobalError = FormulaError::NONE; + } + else if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) + { + nCount++; + nGlobalError = FormulaError::NONE; + } + } + break; + default : + while (nParamCount-- > 0) + PopError(); + SetError(FormulaError::IllegalParameter); + } + } + + // A boolean return type makes no sense on sums et al. + // Counts are always numbers. + if( nFuncFmtType == SvNumFormatType::LOGICAL || eFunc == ifCOUNT || eFunc == ifCOUNT2 ) + nFuncFmtType = SvNumFormatType::NUMBER; + + if (xResMat) + { + // Include value of last non-references-array type and calculate final result. + for (SCSIZE i=0; i < nMatRows; ++i) + { + sal_uLong nVecCount = (xResCount ? nCount + xResCount->GetDouble(0,i) : nCount); + double fVecRes = xResMat->GetDouble(0,i); + if (eFunc == ifPRODUCT) + fVecRes *= fRes.get(); + else + fVecRes += fRes.get(); + fVecRes = lcl_IterResult( eFunc, fVecRes, nVecCount); + xResMat->PutDouble( fVecRes, 0,i); + } + PushMatrix( xResMat); + } + else + { + PushDouble( lcl_IterResult( eFunc, fRes.get(), nCount)); + } +} + +void ScInterpreter::ScSumSQ() +{ + IterateParameters( ifSUMSQ ); +} + +void ScInterpreter::ScSum() +{ + IterateParameters( ifSUM ); +} + +void ScInterpreter::ScProduct() +{ + IterateParameters( ifPRODUCT ); +} + +void ScInterpreter::ScAverage( bool bTextAsZero ) +{ + IterateParameters( ifAVERAGE, bTextAsZero ); +} + +void ScInterpreter::ScCount() +{ + IterateParameters( ifCOUNT ); +} + +void ScInterpreter::ScCount2() +{ + IterateParameters( ifCOUNT2 ); +} + +/** + * The purpose of RAWSUBTRACT() is exactly to not apply any error correction, approximation etc. + * But use the "raw" IEEE 754 double subtraction. + * So no Kahan summation + */ +void ScInterpreter::ScRawSubtract() +{ + short nParamCount = GetByte(); + if (!MustHaveParamCountMin( nParamCount, 2)) + return; + + // Reverse stack to process arguments from left to right. + ReverseStack( nParamCount); + // Obtain the minuend. + double fRes = GetDouble(); + + while (nGlobalError == FormulaError::NONE && --nParamCount > 0) + { + // Simple single values without matrix support. + fRes -= GetDouble(); + } + while (nParamCount-- > 0) + PopError(); + + PushDouble( fRes); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |