From d9aaa26a4efbe7c69254ecc5f00b997f237686e9 Mon Sep 17 00:00:00 2001 From: Eike Rathke Date: Thu, 13 Dec 2018 19:26:22 +0100 Subject: Encapsulate ScFunctionData members and abuse WelfordRunner members ... to squeeze some memory and later use this as a mass object during consolidation. Change-Id: I3f0aa03ec0bbbb4c64a4854b55a451dd3cacfa90 Reviewed-on: https://gerrit.libreoffice.org/65124 Tested-by: Jenkins Reviewed-by: Eike Rathke --- sc/inc/column.hxx | 2 +- sc/inc/subtotal.hxx | 52 ++++++++++++++--------- sc/inc/table.hxx | 2 +- sc/source/core/data/column2.cxx | 12 +++--- sc/source/core/data/documen4.cxx | 6 +-- sc/source/core/data/table3.cxx | 2 +- sc/source/core/tool/subtotal.cxx | 90 ++++++++++++++++++++-------------------- 7 files changed, 89 insertions(+), 77 deletions(-) diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx index b1e890a6347c..7e12cf4eda46 100644 --- a/sc/inc/column.hxx +++ b/sc/inc/column.hxx @@ -85,7 +85,7 @@ class ScStyleSheet; class SvtBroadcaster; class ScTypedStrData; class ScProgress; -struct ScFunctionData; +class ScFunctionData; class ScFlatBoolRowSegments; struct ScSetStringParam; struct ScColWidthParam; diff --git a/sc/inc/subtotal.hxx b/sc/inc/subtotal.hxx index 2c1db3ca2264..2e2811e54815 100644 --- a/sc/inc/subtotal.hxx +++ b/sc/inc/subtotal.hxx @@ -30,6 +30,8 @@ public: static bool SafeDiv( double& fVal1, double fVal2); }; +class ScFunctionData; + /** Implements the Welford Online one-pass algorithm. See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_Online_algorithm and Donald E. Knuth, TAoCP vol.2, 3rd edn., p. 232 @@ -37,34 +39,44 @@ public: class WelfordRunner { public: - WelfordRunner() : fMean(0.0), fM2(0.0), nCount(0) {} + WelfordRunner() : mfMean(0.0), mfM2(0.0), mnCount(0) {} void update( double fVal ); - sal_uInt64 getCount() const { return nCount; } - double getMean() const { return fMean; } - double getVarianceSample() const { return nCount > 1 ? fM2 / (nCount-1) : 0.0; } - double getVariancePopulation() const { return nCount > 0 ? fM2 / nCount : 0.0; } + sal_uInt64 getCount() const { return mnCount; } + double getMean() const { return mfMean; } + double getVarianceSample() const { return mnCount > 1 ? mfM2 / (mnCount-1) : 0.0; } + double getVariancePopulation() const { return mnCount > 0 ? mfM2 / mnCount : 0.0; } + // The private variables can be abused by ScFunctionData as general + // sum/min/max/ave/count/... variables to reduce memory footprint for that + // ScFunctionData may be a mass object during consolidation. + // ScFunctionData::update() and getResult() take care that purposes are not + // mixed. + friend class ScFunctionData; private: - double fMean; - double fM2; - sal_uInt64 nCount; + double mfMean; + double mfM2; + sal_uInt64 mnCount; }; -struct ScFunctionData // to calculate single functions +/** To calculate a single subtotal function. */ +class ScFunctionData { -private: - WelfordRunner maWelford; - double nVal; - sal_uInt64 nCount; public: - ScSubTotalFunc const eFunc; - bool bError; + ScFunctionData( ScSubTotalFunc eFn ) : meFunc(eFn), mbError(false) {} + void update( double fNewVal ); + /// Check getError() after (!) obtaining the result. + double getResult(); + bool getError() const { return mbError; } + ScSubTotalFunc getFunc() const { return meFunc; } + void setError() { mbError = true; } + +private: + WelfordRunner maWelford; + ScSubTotalFunc const meFunc; + bool mbError; - ScFunctionData( ScSubTotalFunc eFn ) : - nVal(0.0), nCount(0), eFunc(eFn), bError(false) {} - void update( double fNewVal ); - /// Check bError after (!) obtaining the result. - double getResult(); + double& getValueRef() { return maWelford.mfMean; } + sal_uInt64& getCountRef() { return maWelford.mnCount; } }; #endif diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx index 64bf2bd95b2c..e26ae0ae7a40 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -99,7 +99,7 @@ class ScStyleSheet; class ScTableProtection; class ScUserListData; struct RowInfo; -struct ScFunctionData; +class ScFunctionData; class CollatorWrapper; class ScFlatUInt16RowSegments; class ScFlatBoolRowSegments; diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx index 3aabfec3f611..f9b4569d5c4e 100644 --- a/sc/source/core/data/column2.cxx +++ b/sc/source/core/data/column2.cxx @@ -3378,10 +3378,10 @@ class UpdateSubTotalHandler void update(double fVal, bool bVal) { - if (mrData.bError) + if (mrData.getError()) return; - switch (mrData.eFunc) + switch (mrData.getFunc()) { case SUBTOTAL_FUNC_CNT2: // everything mrData.update( fVal); @@ -3414,13 +3414,13 @@ public: { double fVal = 0.0; bool bVal = false; - if (mrData.eFunc != SUBTOTAL_FUNC_CNT2) // it doesn't interest us + if (mrData.getFunc() != SUBTOTAL_FUNC_CNT2) // it doesn't interest us { if (pCell->GetErrCode() != FormulaError::NONE) { - if (mrData.eFunc != SUBTOTAL_FUNC_CNT) // simply remove from count - mrData.bError = true; + if (mrData.getFunc() != SUBTOTAL_FUNC_CNT) // simply remove from count + mrData.setError(); } else if (pCell->IsValue()) { @@ -3466,7 +3466,7 @@ void ScColumn::UpdateSelectionFunction( sc::SingleColumnSpanSet::SpansType::const_iterator it = aSpans.begin(), itEnd = aSpans.end(); - switch (rData.eFunc) + switch (rData.getFunc()) { case SUBTOTAL_FUNC_SELECTION_COUNT: { diff --git a/sc/source/core/data/documen4.cxx b/sc/source/core/data/documen4.cxx index 040f5e6b5dc0..f02e43f0bffc 100644 --- a/sc/source/core/data/documen4.cxx +++ b/sc/source/core/data/documen4.cxx @@ -610,15 +610,15 @@ bool ScDocument::GetSelectionFunction( ScSubTotalFunc eFunc, SCTAB nMax = static_cast(maTabs.size()); ScMarkData::const_iterator itr = aMark.begin(), itrEnd = aMark.end(); - for (; itr != itrEnd && *itr < nMax && !aData.bError; ++itr) + for (; itr != itrEnd && *itr < nMax && !aData.getError(); ++itr) if (maTabs[*itr]) maTabs[*itr]->UpdateSelectionFunction(aData, aMark); rResult = aData.getResult(); - if (aData.bError) + if (aData.getError()) rResult = 0.0; - return !aData.bError; + return !aData.getError(); } double ScDocument::RoundValueAsShown( double fVal, sal_uInt32 nFormat, const ScInterpreterContext* pContext ) const diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx index 7ffd5dfec926..5a2550ce0ead 100644 --- a/sc/source/core/data/table3.cxx +++ b/sc/source/core/data/table3.cxx @@ -3595,7 +3595,7 @@ void ScTable::UpdateSelectionFunction( ScFunctionData& rData, const ScMarkData& } const SCCOL nStartCol = aMarkArea.aStart.Col(); const SCCOL nEndCol = aMarkArea.aEnd.Col(); - for (SCCOL nCol = nStartCol; nCol <= nEndCol && !rData.bError; ++nCol) + for (SCCOL nCol = nStartCol; nCol <= nEndCol && !rData.getError(); ++nCol) { if (mpColFlags && ColHidden(nCol)) continue; diff --git a/sc/source/core/tool/subtotal.cxx b/sc/source/core/tool/subtotal.cxx index 44bec5477756..8fdb2d2342fb 100644 --- a/sc/source/core/tool/subtotal.cxx +++ b/sc/source/core/tool/subtotal.cxx @@ -64,54 +64,54 @@ bool SubTotal::SafeDiv(double& fVal1, double fVal2) void ScFunctionData::update( double fNewVal ) { - if (bError) + if (mbError) return; - switch (eFunc) + switch (meFunc) { case SUBTOTAL_FUNC_SUM: - if (!SubTotal::SafePlus(nVal, fNewVal)) - bError = true; + if (!SubTotal::SafePlus(getValueRef(), fNewVal)) + mbError = true; break; case SUBTOTAL_FUNC_PROD: - if (nCount == 0) // copy first value (nVal is initialized to 0) + if (getCountRef() == 0) // copy first value (nVal is initialized to 0) { - nVal = fNewVal; - nCount = 1; // don't care about further count + getValueRef() = fNewVal; + getCountRef() = 1; // don't care about further count } - else if (!SubTotal::SafeMult(nVal, fNewVal)) - bError = true; + else if (!SubTotal::SafeMult(getValueRef(), fNewVal)) + mbError = true; break; case SUBTOTAL_FUNC_CNT: case SUBTOTAL_FUNC_CNT2: - ++nCount; + ++getCountRef(); break; case SUBTOTAL_FUNC_SELECTION_COUNT: - nCount += fNewVal; + getCountRef() += fNewVal; break; case SUBTOTAL_FUNC_AVE: - if (!SubTotal::SafePlus(nVal, fNewVal)) - bError = true; + if (!SubTotal::SafePlus(getValueRef(), fNewVal)) + mbError = true; else - ++nCount; + ++getCountRef(); break; case SUBTOTAL_FUNC_MAX: - if (nCount == 0) // copy first value (nVal is initialized to 0) + if (getCountRef() == 0) // copy first value (nVal is initialized to 0) { - nVal = fNewVal; - nCount = 1; // don't care about further count + getValueRef() = fNewVal; + getCountRef() = 1; // don't care about further count } - else if (fNewVal > nVal) - nVal = fNewVal; + else if (fNewVal > getValueRef()) + getValueRef() = fNewVal; break; case SUBTOTAL_FUNC_MIN: - if (nCount == 0) // copy first value (nVal is initialized to 0) + if (getCountRef() == 0) // copy first value (nVal is initialized to 0) { - nVal = fNewVal; - nCount = 1; // don't care about further count + getValueRef() = fNewVal; + getCountRef() = 1; // don't care about further count } - else if (fNewVal < nVal) - nVal = fNewVal; + else if (fNewVal < getValueRef()) + getValueRef() = fNewVal; break; case SUBTOTAL_FUNC_VAR: case SUBTOTAL_FUNC_STD: @@ -121,83 +121,83 @@ void ScFunctionData::update( double fNewVal ) break; default: // unhandled unknown - bError = true; + mbError = true; } } double ScFunctionData::getResult() { - if (bError) + if (mbError) return 0.0; double fRet = 0.0; - switch (eFunc) + switch (meFunc) { case SUBTOTAL_FUNC_CNT: case SUBTOTAL_FUNC_CNT2: case SUBTOTAL_FUNC_SELECTION_COUNT: - fRet = nCount; + fRet = getCountRef(); break; case SUBTOTAL_FUNC_SUM: case SUBTOTAL_FUNC_MAX: case SUBTOTAL_FUNC_MIN: // Note that nVal is 0.0 for MAX and MIN if nCount==0, that's also // how it is defined in ODFF. - fRet = nVal; + fRet = getValueRef(); break; case SUBTOTAL_FUNC_PROD: - fRet = (nCount > 0) ? nVal : 0.0; + fRet = (getCountRef() > 0) ? getValueRef() : 0.0; break; case SUBTOTAL_FUNC_AVE: - if (nCount == 0) - bError = true; + if (getCountRef() == 0) + mbError = true; else - fRet = nVal / nCount; + fRet = getValueRef() / getCountRef(); break; case SUBTOTAL_FUNC_VAR: case SUBTOTAL_FUNC_STD: if (maWelford.getCount() < 2) - bError = true; + mbError = true; else { fRet = maWelford.getVarianceSample(); if (fRet < 0.0) - bError = true; - else if (eFunc == SUBTOTAL_FUNC_STD) + mbError = true; + else if (meFunc == SUBTOTAL_FUNC_STD) fRet = sqrt( fRet); } break; case SUBTOTAL_FUNC_VARP: case SUBTOTAL_FUNC_STDP: if (maWelford.getCount() < 1) - bError = true; + mbError = true; else if (maWelford.getCount() == 1) fRet = 0.0; else { fRet = maWelford.getVariancePopulation(); if (fRet < 0.0) - bError = true; - else if (eFunc == SUBTOTAL_FUNC_STDP) + mbError = true; + else if (meFunc == SUBTOTAL_FUNC_STDP) fRet = sqrt( fRet); } break; default: assert(!"unhandled unknown"); - bError = true; + mbError = true; break; } - if (bError) + if (mbError) fRet = 0.0; return fRet; } void WelfordRunner::update( double fVal ) { - ++nCount; - const double fDelta = fVal - fMean; - fMean += fDelta / nCount; - fM2 += fDelta * (fVal - fMean); + ++mnCount; + const double fDelta = fVal - mfMean; + mfMean += fDelta / mnCount; + mfM2 += fDelta * (fVal - mfMean); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3