forked from amazingfate/loongoffice
matrix-ranges not supported Change-Id: I902c78da6d9c7a4a5f8c5e4a7e1678b69d49a6fe Reviewed-on: https://gerrit.libreoffice.org/9449 Reviewed-by: Eike Rathke <erack@redhat.com> Tested-by: Eike Rathke <erack@redhat.com>
1107 lines
36 KiB
C++
1107 lines
36 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 "mtvcellfunc.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 fNum = 0.0; // dummy value
|
|
double fY = 1.0 - fA;
|
|
double fDenom = fX + 2.0-fA;
|
|
double fPk = 0.0; // dummy value
|
|
double fPkm1 = fX + 1.0;
|
|
double fPkm2 = 1.0;
|
|
double fQk = 1.0; // dummy value
|
|
double fQkm1 = fDenom * fX;
|
|
double fQkm2 = fX;
|
|
double fApprox = fPkm1/fQkm1;
|
|
bool bFinished = false;
|
|
double fR = 0.0; // dummy value
|
|
do
|
|
{
|
|
fCount = fCount +1.0;
|
|
fY = fY+ 1.0;
|
|
fNum = fY * fCount;
|
|
fDenom = fDenom +2.0;
|
|
fPk = fPkm1 * fDenom - fPkm2 * fNum;
|
|
fQk = fQkm1 * fDenom - fQkm2 * fNum;
|
|
if (fQk != 0.0)
|
|
{
|
|
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(errNoConvergence);
|
|
}
|
|
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(errNoConvergence);
|
|
}
|
|
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(errDivisionByZero); // 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
|
|
{
|
|
double mfSum;
|
|
sal_uInt16 mnError;
|
|
|
|
public:
|
|
NumericCellAccumulator() : mfSum(0.0), mnError(0) {}
|
|
|
|
void operator() (size_t, double fVal)
|
|
{
|
|
mfSum += fVal;
|
|
}
|
|
|
|
void operator() (size_t, const ScFormulaCell* pCell)
|
|
{
|
|
if (mnError)
|
|
// Skip all the rest if we have an error.
|
|
return;
|
|
|
|
double fVal = 0.0;
|
|
sal_uInt16 nErr = 0;
|
|
ScFormulaCell& rCell = const_cast<ScFormulaCell&>(*pCell);
|
|
if (!rCell.GetErrorOrValue(nErr, fVal))
|
|
// The cell has neither error nor value. Perhaps string result.
|
|
return;
|
|
|
|
if (nErr)
|
|
{
|
|
// Cell has error.
|
|
mnError = nErr;
|
|
return;
|
|
}
|
|
|
|
mfSum += fVal;
|
|
}
|
|
|
|
sal_uInt16 getError() const { return mnError; }
|
|
double getSum() const { return mfSum; }
|
|
};
|
|
|
|
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 = const_cast<ScFormulaCell&>(**it);
|
|
if (rCell.IsValueNoError())
|
|
++mnCount;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
size_t getCount() const { return mnCount; }
|
|
};
|
|
|
|
class FuncCount : public sc::ColumnSpanSet::ColumnAction
|
|
{
|
|
sc::ColumnBlockConstPosition maPos;
|
|
ScColumn* mpCol;
|
|
size_t mnCount;
|
|
sal_uInt32 mnNumFmt;
|
|
|
|
public:
|
|
FuncCount() : mpCol(0), mnCount(0), mnNumFmt(0) {}
|
|
|
|
virtual void startColumn(ScColumn* pCol) SAL_OVERRIDE
|
|
{
|
|
mpCol = pCol;
|
|
mpCol->InitBlockPosition(maPos);
|
|
}
|
|
|
|
virtual void execute(SCROW nRow1, SCROW nRow2, bool bVal) SAL_OVERRIDE
|
|
{
|
|
if (!bVal)
|
|
return;
|
|
|
|
NumericCellCounter aFunc;
|
|
maPos.miCellPos = sc::ParseBlock(maPos.miCellPos, mpCol->GetCellStore(), aFunc, nRow1, nRow2);
|
|
mnCount += aFunc.getCount();
|
|
mnNumFmt = mpCol->GetNumberFormat(nRow2);
|
|
};
|
|
|
|
size_t getCount() const { return mnCount; }
|
|
sal_uInt32 getNumberFormat() const { return mnNumFmt; }
|
|
};
|
|
|
|
class FuncSum : public sc::ColumnSpanSet::ColumnAction
|
|
{
|
|
sc::ColumnBlockConstPosition maPos;
|
|
ScColumn* mpCol;
|
|
double mfSum;
|
|
sal_uInt16 mnError;
|
|
sal_uInt32 mnNumFmt;
|
|
|
|
public:
|
|
FuncSum() : mpCol(0), mfSum(0.0), mnError(0), mnNumFmt(0) {}
|
|
|
|
virtual void startColumn(ScColumn* pCol) SAL_OVERRIDE
|
|
{
|
|
mpCol = pCol;
|
|
mpCol->InitBlockPosition(maPos);
|
|
}
|
|
|
|
virtual void execute(SCROW nRow1, SCROW nRow2, bool bVal) SAL_OVERRIDE
|
|
{
|
|
if (!bVal)
|
|
return;
|
|
|
|
if (mnError)
|
|
return;
|
|
|
|
NumericCellAccumulator aFunc;
|
|
maPos.miCellPos = sc::ParseFormulaNumeric(maPos.miCellPos, mpCol->GetCellStore(), nRow1, nRow2, aFunc);
|
|
mnError = aFunc.getError();
|
|
if (mnError)
|
|
return;
|
|
|
|
mfSum += aFunc.getSum();
|
|
mnNumFmt = mpCol->GetNumberFormat(nRow2);
|
|
};
|
|
|
|
sal_uInt16 getError() const { return mnError; }
|
|
double getSum() const { return mfSum; }
|
|
sal_uInt32 getNumberFormat() const { return mnNumFmt; }
|
|
};
|
|
|
|
void IterateMatrix(
|
|
const ScMatrixRef& pMat, ScIterFunc eFunc, bool bTextAsZero,
|
|
sal_uLong& rCount, short& rFuncFmtType, double& fRes, double& fMem, bool& bNull)
|
|
{
|
|
if (!pMat)
|
|
return;
|
|
|
|
// TODO fdo73148 take mnSubTotalFlags into account
|
|
rFuncFmtType = NUMBERFORMAT_NUMBER;
|
|
switch (eFunc)
|
|
{
|
|
case ifAVERAGE:
|
|
case ifSUM:
|
|
{
|
|
ScMatrix::IterateResult aRes = pMat->Sum(bTextAsZero);
|
|
if (bNull)
|
|
{
|
|
bNull = false;
|
|
fMem = aRes.mfFirst;
|
|
fRes += aRes.mfRest;
|
|
}
|
|
else
|
|
fRes += aRes.mfFirst + aRes.mfRest;
|
|
rCount += aRes.mnCount;
|
|
}
|
|
break;
|
|
case ifCOUNT:
|
|
rCount += pMat->Count(bTextAsZero);
|
|
break;
|
|
case ifCOUNT2:
|
|
rCount += pMat->Count(true);
|
|
break;
|
|
case ifPRODUCT:
|
|
{
|
|
ScMatrix::IterateResult aRes = pMat->Product(bTextAsZero);
|
|
fRes = aRes.mfFirst;
|
|
fRes *= aRes.mfRest;
|
|
rCount += aRes.mnCount;
|
|
}
|
|
break;
|
|
case ifSUMSQ:
|
|
{
|
|
ScMatrix::IterateResult aRes = pMat->SumSquare(bTextAsZero);
|
|
fRes += aRes.mfRest;
|
|
rCount += aRes.mnCount;
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
double ScInterpreter::IterateParameters( ScIterFunc eFunc, bool bTextAsZero )
|
|
{
|
|
short nParamCount = GetByte();
|
|
double fRes = ( eFunc == ifPRODUCT ) ? 1.0 : 0.0;
|
|
double fVal = 0.0;
|
|
double fMem = 0.0; // first numeric value.
|
|
bool bNull = true;
|
|
sal_uLong nCount = 0;
|
|
ScAddress aAdr;
|
|
ScRange aRange;
|
|
size_t nRefInList = 0;
|
|
if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
|
|
( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) ) )
|
|
nGlobalError = 0;
|
|
while (nParamCount-- > 0)
|
|
{
|
|
switch (GetStackType())
|
|
{
|
|
case svString:
|
|
{
|
|
if( eFunc == ifCOUNT )
|
|
{
|
|
OUString aStr = PopString().getString();
|
|
sal_uInt32 nFIndex = 0; // damit default Land/Spr.
|
|
if ( bTextAsZero || pFormatter->IsNumberFormat(aStr, nFIndex, fVal))
|
|
nCount++;
|
|
}
|
|
else
|
|
{
|
|
switch ( eFunc )
|
|
{
|
|
case ifAVERAGE:
|
|
case ifSUM:
|
|
case ifSUMSQ:
|
|
case ifPRODUCT:
|
|
{
|
|
if ( bTextAsZero )
|
|
{
|
|
Pop();
|
|
nCount++;
|
|
if ( eFunc == ifPRODUCT )
|
|
fRes = 0.0;
|
|
}
|
|
else
|
|
{
|
|
while (nParamCount-- > 0)
|
|
Pop();
|
|
SetError( errNoValue );
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
Pop();
|
|
nCount++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svDouble :
|
|
fVal = GetDouble();
|
|
nCount++;
|
|
switch( eFunc )
|
|
{
|
|
case ifAVERAGE:
|
|
case ifSUM:
|
|
if ( bNull && fVal != 0.0 )
|
|
{
|
|
bNull = false;
|
|
fMem = fVal;
|
|
}
|
|
else
|
|
fRes += fVal;
|
|
break;
|
|
case ifSUMSQ: fRes += fVal * fVal; break;
|
|
case ifPRODUCT: fRes *= fVal; break;
|
|
default: ; // nothing
|
|
}
|
|
nFuncFmtType = NUMBERFORMAT_NUMBER;
|
|
break;
|
|
case svExternalSingleRef:
|
|
{
|
|
ScExternalRefCache::TokenRef pToken;
|
|
ScExternalRefCache::CellFormat aFmt;
|
|
PopExternalSingleRef(pToken, &aFmt);
|
|
if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
|
|
( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) ) )
|
|
{
|
|
nGlobalError = 0;
|
|
if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) )
|
|
++nCount;
|
|
break;
|
|
}
|
|
|
|
if (!pToken)
|
|
break;
|
|
|
|
StackVar eType = pToken->GetType();
|
|
if (eFunc == ifCOUNT2)
|
|
{
|
|
if ( eType != formula::svEmptyCell &&
|
|
( ( pToken->GetOpCode() != ocSubTotal &&
|
|
pToken->GetOpCode() != ocAggregate ) ||
|
|
( mnSubTotalFlags & SUBTOTAL_IGN_NESTED_ST_AG ) ) )
|
|
nCount++;
|
|
if (nGlobalError)
|
|
nGlobalError = 0;
|
|
}
|
|
else if (eType == formula::svDouble)
|
|
{
|
|
nCount++;
|
|
fVal = pToken->GetDouble();
|
|
if (aFmt.mbIsSet)
|
|
{
|
|
nFuncFmtType = aFmt.mnType;
|
|
nFuncFmtIndex = aFmt.mnIndex;
|
|
}
|
|
switch( eFunc )
|
|
{
|
|
case ifAVERAGE:
|
|
case ifSUM:
|
|
if ( bNull && fVal != 0.0 )
|
|
{
|
|
bNull = false;
|
|
fMem = fVal;
|
|
}
|
|
else
|
|
fRes += fVal;
|
|
break;
|
|
case ifSUMSQ: fRes += fVal * fVal; break;
|
|
case ifPRODUCT: fRes *= fVal; break;
|
|
case ifCOUNT:
|
|
if ( nGlobalError )
|
|
{
|
|
nGlobalError = 0;
|
|
nCount--;
|
|
}
|
|
break;
|
|
default: ; // nothing
|
|
}
|
|
}
|
|
else if (bTextAsZero && eType == formula::svString)
|
|
{
|
|
nCount++;
|
|
if ( eFunc == ifPRODUCT )
|
|
fRes = 0.0;
|
|
}
|
|
}
|
|
break;
|
|
case svSingleRef :
|
|
{
|
|
PopSingleRef( aAdr );
|
|
if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
|
|
( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) ) )
|
|
{
|
|
nGlobalError = 0;
|
|
if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) )
|
|
++nCount;
|
|
break;
|
|
}
|
|
if ( ( mnSubTotalFlags & SUBTOTAL_IGN_FILTERED ) &&
|
|
pDok->RowFiltered( aAdr.Row(), aAdr.Tab() ) )
|
|
{
|
|
break;
|
|
}
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, aAdr);
|
|
if (!aCell.isEmpty())
|
|
{
|
|
if( eFunc == ifCOUNT2 )
|
|
{
|
|
CellType eCellType = aCell.meType;
|
|
if ( eCellType != CELLTYPE_NONE )
|
|
nCount++;
|
|
if ( nGlobalError )
|
|
nGlobalError = 0;
|
|
}
|
|
else if (aCell.hasNumeric())
|
|
{
|
|
nCount++;
|
|
fVal = GetCellValue(aAdr, aCell);
|
|
CurFmtToFuncFmt();
|
|
switch( eFunc )
|
|
{
|
|
case ifAVERAGE:
|
|
case ifSUM:
|
|
if ( bNull && fVal != 0.0 )
|
|
{
|
|
bNull = false;
|
|
fMem = fVal;
|
|
}
|
|
else
|
|
fRes += fVal;
|
|
break;
|
|
case ifSUMSQ: fRes += fVal * fVal; break;
|
|
case ifPRODUCT: fRes *= fVal; break;
|
|
case ifCOUNT:
|
|
if ( nGlobalError )
|
|
{
|
|
nGlobalError = 0;
|
|
nCount--;
|
|
}
|
|
break;
|
|
default: ; // nothing
|
|
}
|
|
}
|
|
else if (bTextAsZero && aCell.hasString())
|
|
{
|
|
nCount++;
|
|
if ( eFunc == ifPRODUCT )
|
|
fRes = 0.0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svDoubleRef :
|
|
case svRefList :
|
|
{
|
|
PopDoubleRef( aRange, nParamCount, nRefInList);
|
|
if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
|
|
( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) ) )
|
|
{
|
|
nGlobalError = 0;
|
|
if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) )
|
|
++nCount;
|
|
if ( eFunc == ifCOUNT2 || eFunc == ifCOUNT )
|
|
break;
|
|
}
|
|
if( eFunc == ifCOUNT2 )
|
|
{
|
|
ScCellIterator aIter( pDok, aRange, mnSubTotalFlags );
|
|
for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
|
|
{
|
|
if ( !aIter.hasEmptyData() )
|
|
{
|
|
++nCount;
|
|
}
|
|
}
|
|
|
|
if ( nGlobalError )
|
|
nGlobalError = 0;
|
|
}
|
|
else
|
|
{
|
|
ScValueIterator aValIter( pDok, aRange, mnSubTotalFlags, bTextAsZero );
|
|
sal_uInt16 nErr = 0;
|
|
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 & SUBTOTAL_IGN_ERR_VAL )
|
|
{
|
|
do
|
|
{
|
|
if ( !nErr )
|
|
{
|
|
SetError(nErr);
|
|
if ( bNull && fVal != 0.0 )
|
|
{
|
|
bNull = false;
|
|
fMem = fVal;
|
|
}
|
|
else
|
|
fRes += fVal;
|
|
nCount++;
|
|
}
|
|
}
|
|
while (aValIter.GetNext(fVal, nErr));
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
SetError(nErr);
|
|
if ( bNull && fVal != 0.0 )
|
|
{
|
|
bNull = false;
|
|
fMem = fVal;
|
|
}
|
|
else
|
|
fRes += fVal;
|
|
nCount++;
|
|
}
|
|
while (aValIter.GetNext(fVal, nErr));
|
|
}
|
|
break;
|
|
case ifSUMSQ:
|
|
if ( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL )
|
|
{
|
|
do
|
|
{
|
|
if ( !nErr )
|
|
{
|
|
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 && ( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) ) )
|
|
{
|
|
SetError(nErr);
|
|
fRes *= fVal;
|
|
nCount++;
|
|
}
|
|
}
|
|
while (aValIter.GetNext(fVal, nErr));
|
|
break;
|
|
case ifCOUNT:
|
|
do
|
|
{
|
|
if ( !nErr )
|
|
nCount++;
|
|
}
|
|
while (aValIter.GetNext(fVal, nErr));
|
|
break;
|
|
default: ; // nothing
|
|
}
|
|
SetError( nErr );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svExternalDoubleRef:
|
|
{
|
|
ScMatrixRef pMat;
|
|
PopExternalDoubleRef(pMat);
|
|
if ( nGlobalError && !( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) )
|
|
break;
|
|
|
|
IterateMatrix(pMat, eFunc, bTextAsZero, nCount, nFuncFmtType, fRes, fMem, bNull);
|
|
}
|
|
break;
|
|
case svMatrix :
|
|
{
|
|
ScMatrixRef pMat = PopMatrix();
|
|
IterateMatrix(pMat, eFunc, bTextAsZero, nCount, nFuncFmtType, fRes, fMem, bNull);
|
|
}
|
|
break;
|
|
case svError:
|
|
{
|
|
PopError();
|
|
if ( eFunc == ifCOUNT || ( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) )
|
|
{
|
|
nGlobalError = 0;
|
|
}
|
|
else if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SUBTOTAL_IGN_ERR_VAL ) )
|
|
{
|
|
nCount++;
|
|
nGlobalError = 0;
|
|
}
|
|
}
|
|
break;
|
|
default :
|
|
while (nParamCount-- > 0)
|
|
PopError();
|
|
SetError(errIllegalParameter);
|
|
}
|
|
}
|
|
switch( eFunc )
|
|
{
|
|
case ifSUM: fRes = ::rtl::math::approxAdd( fRes, fMem ); break;
|
|
case ifAVERAGE: fRes = div(::rtl::math::approxAdd( fRes, fMem ), nCount); break;
|
|
case ifCOUNT2:
|
|
case ifCOUNT: fRes = nCount; break;
|
|
case ifPRODUCT: if ( !nCount ) fRes = 0.0; break;
|
|
default: ; // nothing
|
|
}
|
|
// Bei Summen etc. macht ein bool-Ergebnis keinen Sinn
|
|
// und Anzahl ist immer Number (#38345#)
|
|
if( eFunc == ifCOUNT || nFuncFmtType == NUMBERFORMAT_LOGICAL )
|
|
nFuncFmtType = NUMBERFORMAT_NUMBER;
|
|
return fRes;
|
|
}
|
|
|
|
void ScInterpreter::ScSumSQ()
|
|
{
|
|
PushDouble( IterateParameters( ifSUMSQ ) );
|
|
}
|
|
|
|
void ScInterpreter::ScSum()
|
|
{
|
|
if ( mnSubTotalFlags )
|
|
PushDouble( IterateParameters( ifSUM ) );
|
|
else
|
|
{
|
|
short nParamCount = GetByte();
|
|
double fRes = 0.0;
|
|
double fVal = 0.0;
|
|
ScAddress aAdr;
|
|
ScRange aRange;
|
|
size_t nRefInList = 0;
|
|
while (nParamCount-- > 0)
|
|
{
|
|
switch (GetStackType())
|
|
{
|
|
case svString:
|
|
{
|
|
while (nParamCount-- > 0)
|
|
Pop();
|
|
SetError( errNoValue );
|
|
}
|
|
break;
|
|
case svDouble :
|
|
fVal = GetDouble();
|
|
fRes += fVal;
|
|
nFuncFmtType = NUMBERFORMAT_NUMBER;
|
|
break;
|
|
case svExternalSingleRef:
|
|
{
|
|
ScExternalRefCache::TokenRef pToken;
|
|
ScExternalRefCache::CellFormat aFmt;
|
|
PopExternalSingleRef(pToken, &aFmt);
|
|
|
|
if (!pToken)
|
|
break;
|
|
|
|
StackVar eType = pToken->GetType();
|
|
if (eType == formula::svDouble)
|
|
{
|
|
fVal = pToken->GetDouble();
|
|
if (aFmt.mbIsSet)
|
|
{
|
|
nFuncFmtType = aFmt.mnType;
|
|
nFuncFmtIndex = aFmt.mnIndex;
|
|
}
|
|
|
|
fRes += fVal;
|
|
}
|
|
}
|
|
break;
|
|
case svSingleRef :
|
|
{
|
|
PopSingleRef( aAdr );
|
|
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, aAdr);
|
|
if (!aCell.isEmpty())
|
|
{
|
|
if (aCell.hasNumeric())
|
|
{
|
|
fVal = GetCellValue(aAdr, aCell);
|
|
CurFmtToFuncFmt();
|
|
fRes += fVal;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svDoubleRef :
|
|
case svRefList :
|
|
{
|
|
PopDoubleRef( aRange, nParamCount, nRefInList);
|
|
|
|
sc::ColumnSpanSet aSet(false);
|
|
aSet.set(aRange, true);
|
|
|
|
FuncSum aAction;
|
|
aSet.executeColumnAction(*pDok, aAction);
|
|
sal_uInt16 nErr = aAction.getError();
|
|
if (nErr)
|
|
{
|
|
SetError(nErr);
|
|
return;
|
|
}
|
|
fRes += aAction.getSum();
|
|
|
|
// Get the number format of the last iterated cell.
|
|
nFuncFmtIndex = aAction.getNumberFormat();
|
|
nFuncFmtType = pDok->GetFormatTable()->GetType(nFuncFmtIndex);
|
|
}
|
|
break;
|
|
case svExternalDoubleRef:
|
|
{
|
|
ScMatrixRef pMat;
|
|
PopExternalDoubleRef(pMat);
|
|
if (nGlobalError)
|
|
break;
|
|
|
|
sal_uLong nCount = 0;
|
|
double fMem = 0.0;
|
|
bool bNull = true;
|
|
IterateMatrix(pMat, ifSUM, false, nCount, nFuncFmtType, fRes, fMem, bNull);
|
|
fRes += fMem;
|
|
}
|
|
break;
|
|
case svMatrix :
|
|
{
|
|
ScMatrixRef pMat = PopMatrix();
|
|
sal_uLong nCount = 0;
|
|
double fMem = 0.0;
|
|
bool bNull = true;
|
|
IterateMatrix(pMat, ifSUM, false, nCount, nFuncFmtType, fRes, fMem, bNull);
|
|
fRes += fMem;
|
|
}
|
|
break;
|
|
case svError:
|
|
{
|
|
PopError();
|
|
}
|
|
break;
|
|
default :
|
|
while (nParamCount-- > 0)
|
|
PopError();
|
|
SetError(errIllegalParameter);
|
|
}
|
|
}
|
|
|
|
if (nFuncFmtType == NUMBERFORMAT_LOGICAL)
|
|
nFuncFmtType = NUMBERFORMAT_NUMBER;
|
|
|
|
PushDouble(fRes);
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::ScProduct()
|
|
{
|
|
PushDouble( IterateParameters( ifPRODUCT ) );
|
|
}
|
|
|
|
void ScInterpreter::ScAverage( bool bTextAsZero )
|
|
{
|
|
PushDouble( IterateParameters( ifAVERAGE, bTextAsZero ) );
|
|
}
|
|
|
|
void ScInterpreter::ScCount()
|
|
{
|
|
if ( mnSubTotalFlags )
|
|
PushDouble( IterateParameters( ifCOUNT ) );
|
|
else
|
|
{
|
|
short nParamCount = GetByte();
|
|
double fVal = 0.0;
|
|
sal_uLong nCount = 0;
|
|
ScAddress aAdr;
|
|
ScRange aRange;
|
|
size_t nRefInList = 0;
|
|
if (nGlobalError)
|
|
nGlobalError = 0;
|
|
|
|
while (nParamCount-- > 0)
|
|
{
|
|
switch (GetRawStackType())
|
|
{
|
|
case svString:
|
|
{
|
|
OUString aStr = PopString().getString();
|
|
sal_uInt32 nFIndex = 0; // damit default Land/Spr.
|
|
if (pFormatter->IsNumberFormat(aStr, nFIndex, fVal))
|
|
nCount++;
|
|
}
|
|
break;
|
|
case svDouble :
|
|
GetDouble();
|
|
nCount++;
|
|
nFuncFmtType = NUMBERFORMAT_NUMBER;
|
|
break;
|
|
case svExternalSingleRef:
|
|
{
|
|
ScExternalRefCache::TokenRef pToken;
|
|
ScExternalRefCache::CellFormat aFmt;
|
|
PopExternalSingleRef(pToken, &aFmt);
|
|
if (nGlobalError)
|
|
{
|
|
nGlobalError = 0;
|
|
break;
|
|
}
|
|
|
|
if (!pToken)
|
|
break;
|
|
|
|
StackVar eType = pToken->GetType();
|
|
if (eType == formula::svDouble)
|
|
{
|
|
nCount++;
|
|
if (aFmt.mbIsSet)
|
|
{
|
|
nFuncFmtType = aFmt.mnType;
|
|
nFuncFmtIndex = aFmt.mnIndex;
|
|
}
|
|
|
|
if (nGlobalError)
|
|
{
|
|
nGlobalError = 0;
|
|
nCount--;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svSingleRef :
|
|
{
|
|
PopSingleRef( aAdr );
|
|
if (nGlobalError)
|
|
{
|
|
nGlobalError = 0;
|
|
break;
|
|
}
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, aAdr);
|
|
if (!aCell.isEmpty())
|
|
{
|
|
if (aCell.hasNumeric())
|
|
{
|
|
nCount++;
|
|
CurFmtToFuncFmt();
|
|
if (nGlobalError)
|
|
{
|
|
nGlobalError = 0;
|
|
nCount--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svDoubleRef :
|
|
case svRefList :
|
|
{
|
|
PopDoubleRef( aRange, nParamCount, nRefInList);
|
|
if (nGlobalError)
|
|
{
|
|
nGlobalError = 0;
|
|
break;
|
|
}
|
|
|
|
sc::ColumnSpanSet aSet(false);
|
|
aSet.set(aRange, true);
|
|
|
|
FuncCount aAction;
|
|
aSet.executeColumnAction(*pDok, aAction);
|
|
nCount += aAction.getCount();
|
|
|
|
// Get the number format of the last iterated cell.
|
|
nFuncFmtIndex = aAction.getNumberFormat();
|
|
nFuncFmtType = pDok->GetFormatTable()->GetType(nFuncFmtIndex);
|
|
}
|
|
break;
|
|
case svExternalDoubleRef:
|
|
{
|
|
ScMatrixRef pMat;
|
|
PopExternalDoubleRef(pMat);
|
|
if (nGlobalError)
|
|
break;
|
|
|
|
double fMem = 0.0, fRes = 0.0;
|
|
bool bNull = true;
|
|
IterateMatrix(pMat, ifCOUNT, false, nCount, nFuncFmtType, fRes, fMem, bNull);
|
|
}
|
|
break;
|
|
case svMatrix :
|
|
{
|
|
ScMatrixRef pMat = PopMatrix();
|
|
double fMem = 0.0, fRes = 0.0;
|
|
bool bNull = true;
|
|
IterateMatrix(pMat, ifCOUNT, false, nCount, nFuncFmtType, fRes, fMem, bNull);
|
|
}
|
|
break;
|
|
case svError:
|
|
{
|
|
PopError();
|
|
nGlobalError = 0;
|
|
}
|
|
break;
|
|
default :
|
|
while (nParamCount-- > 0)
|
|
PopError();
|
|
SetError(errIllegalParameter);
|
|
}
|
|
}
|
|
|
|
nFuncFmtType = NUMBERFORMAT_NUMBER;
|
|
|
|
PushDouble(nCount);
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::ScCount2()
|
|
{
|
|
PushDouble( IterateParameters( ifCOUNT2 ) );
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|