1010 lines
35 KiB
C++
1010 lines
35 KiB
C++
/* -*- 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.getType();
|
|
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.NFGetType(nFuncFmtIndex);
|
|
}
|
|
else
|
|
{
|
|
ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
|
|
FormulaError nErr = FormulaError::NONE;
|
|
if (aValIter.GetFirst(fVal, nErr))
|
|
{
|
|
// placed the loop on the inside for performance reasons:
|
|
aValIter.GetCurNumFmtInfo( 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: */
|