summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEike Rathke <erack@redhat.com>2017-06-10 00:42:57 +0200
committerEike Rathke <erack@redhat.com>2017-06-10 00:46:41 +0200
commitc47fc935a135b4728b452d6f94a856040552a90c (patch)
tree38dacd5d29ebb1990a2cec41d07296c8bb555bbf
parent2d2af57bc0406cd3afd376dd3c92be112b8c9603 (diff)
COUNTIFS, SUMIFS, AVERAGEIFS, MINIFS, MAXIFS with reference arrays, tdf#58874
Change-Id: I3959d67bd206f68ba1d20499d919838773b2e7df
-rw-r--r--sc/source/core/tool/interpr1.cxx259
1 files changed, 209 insertions, 50 deletions
diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index 180d1400127f..df787af469e3 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -5637,11 +5637,12 @@ void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIf
sal_uInt8 nParamCount = GetByte();
sal_uInt8 nQueryCount = nParamCount / 2;
- sc::ParamIfsResult aRes;
std::vector<sal_uInt32> vConditions;
double fVal = 0.0;
SCCOL nDimensionCols = 0;
SCROW nDimensionRows = 0;
+ const SCSIZE nRefArrayRows = GetRefListArrayMaxSize( nParamCount);
+ std::vector<std::vector<sal_uInt32>> vRefArrayConditions;
while (nParamCount > 1 && nGlobalError == FormulaError::NONE)
{
@@ -5729,6 +5730,7 @@ void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIf
// take range
short nParam = nParamCount;
size_t nRefInList = 0;
+ size_t nRefArrayPos = std::numeric_limits<size_t>::max();
SCCOL nCol1 = 0;
SCROW nRow1 = 0;
SCTAB nTab1 = 0;
@@ -5742,6 +5744,38 @@ void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIf
{
case svRefList :
{
+ const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
+ if (p && p->IsArrayResult())
+ {
+ if (nRefInList == 0)
+ {
+ if (vRefArrayConditions.empty())
+ vRefArrayConditions.resize( nRefArrayRows);
+ if (!vConditions.empty())
+ {
+ // Similar to other reference list array
+ // handling, add/op the current value to
+ // all array positions.
+ for (auto & rVec : vRefArrayConditions)
+ {
+ if (rVec.empty())
+ rVec = vConditions;
+ else
+ {
+ assert(rVec.size() == vConditions.size()); // see dimensions below
+ for (size_t i=0, n = rVec.size(); i < n; ++i)
+ {
+ rVec[i] += vConditions[i];
+ }
+ }
+ }
+ // Reset condition results.
+ std::for_each( vConditions.begin(), vConditions.end(),
+ [](sal_uInt32 & r){ r = 0.0; } );
+ }
+ }
+ nRefArrayPos = nRefInList;
+ }
ScRange aRange;
PopDoubleRef( aRange, nParam, nRefInList);
aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
@@ -5874,21 +5908,66 @@ void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIf
} while ( aCellIter.GetNext() );
}
}
+ if (nRefArrayPos != std::numeric_limits<size_t>::max())
+ {
+ // Apply condition result to reference list array result position.
+ std::vector<sal_uInt32>& rVec = vRefArrayConditions[nRefArrayPos];
+ if (rVec.empty())
+ rVec = vConditions;
+ else
+ {
+ assert(rVec.size() == vConditions.size()); // see dimensions above
+ for (size_t i=0, n = rVec.size(); i < n; ++i)
+ {
+ rVec[i] += vConditions[i];
+ }
+ }
+ // Reset conditions vector.
+ // When leaving an svRefList this has to be emptied not set to
+ // 0.0 because it's checked when entering an svRefList.
+ if (nRefInList == 0)
+ std::vector<sal_uInt32>().swap( vConditions);
+ else
+ std::for_each( vConditions.begin(), vConditions.end(), [](sal_uInt32 & r){ r = 0.0; } );
+ }
}
nParamCount -= 2;
}
+ if (!vRefArrayConditions.empty() && !vConditions.empty())
+ {
+ // Add/op the last current value to all array positions.
+ for (auto & rVec : vRefArrayConditions)
+ {
+ if (rVec.empty())
+ rVec = vConditions;
+ else
+ {
+ assert(rVec.size() == vConditions.size()); // see dimensions above
+ for (size_t i=0, n = rVec.size(); i < n; ++i)
+ {
+ rVec[i] += vConditions[i];
+ }
+ }
+ }
+ }
+
if (nGlobalError != FormulaError::NONE)
{
PushError( nGlobalError);
return; // bail out
}
+ sc::ParamIfsResult aRes;
+ ScMatrixRef xResMat;
+
// main range - only for AVERAGEIFS, SUMIFS, MINIFS and MAXIFS
if (nParamCount == 1)
{
short nParam = nParamCount;
size_t nRefInList = 0;
+ size_t nRefArrayPos = std::numeric_limits<size_t>::max();
+ bool bRefArrayMain = false;
while (nParam-- == nParamCount)
{
bool bNull = true;
@@ -5903,6 +5982,24 @@ void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIf
{
case svRefList :
{
+ const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
+ if (p && p->IsArrayResult())
+ {
+ if (vRefArrayConditions.empty())
+ {
+ // Replicate conditions if there wasn't a
+ // reference list array for criteria
+ // evaluation.
+ vRefArrayConditions.resize( nRefArrayRows);
+ for (auto & rVec : vRefArrayConditions)
+ {
+ rVec = vConditions;
+ }
+ }
+
+ bRefArrayMain = true;
+ nRefArrayPos = nRefInList;
+ }
ScRange aRange;
PopDoubleRef( aRange, nParam, nRefInList);
aRange.GetVars( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2);
@@ -5961,85 +6058,147 @@ void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIf
}
// end-result calculation
- ScAddress aAdr;
- aAdr.SetTab( nMainTab1 );
+
+ // This gets weird.. if conditions were calculated using a
+ // reference list array but the main calculation range is not a
+ // reference list array, then the conditions of the array are
+ // applied to the main range each in turn to form the array result.
+
+ size_t nRefArrayMainPos = (bRefArrayMain ? nRefArrayPos :
+ (vRefArrayConditions.empty() ? std::numeric_limits<size_t>::max() : 0));
+ const bool bAppliedArray = (!bRefArrayMain && nRefArrayMainPos == 0);
+
+ if (nRefArrayMainPos == 0)
+ xResMat = GetNewMat( 1, nRefArrayRows);
+
if (pMainMatrix)
{
std::vector<double> aMainValues;
pMainMatrix->GetDoubleArray(aMainValues, false); // Map empty values to NaN's.
- if (vConditions.size() != aMainValues.size())
- {
- PushError( FormulaError::IllegalArgument);
- return;
- }
- std::vector<sal_uInt32>::const_iterator itRes = vConditions.begin(), itResEnd = vConditions.end();
- std::vector<double>::const_iterator itMain = aMainValues.begin();
- for (; itRes != itResEnd; ++itRes, ++itMain)
+ do
{
- if (*itRes != nQueryCount)
- continue;
+ if (nRefArrayMainPos < vRefArrayConditions.size())
+ vConditions = vRefArrayConditions[nRefArrayMainPos];
+
+ if (vConditions.size() != aMainValues.size())
+ {
+ PushError( FormulaError::IllegalArgument);
+ return;
+ }
- fVal = *itMain;
- if (GetDoubleErrorValue(fVal) == FormulaError::ElementNaN)
- continue;
+ std::vector<sal_uInt32>::const_iterator itRes = vConditions.begin(), itResEnd = vConditions.end();
+ std::vector<double>::const_iterator itMain = aMainValues.begin();
+ for (; itRes != itResEnd; ++itRes, ++itMain)
+ {
+ if (*itRes != nQueryCount)
+ continue;
- ++aRes.mfCount;
- if (bNull && fVal != 0.0)
+ fVal = *itMain;
+ if (GetDoubleErrorValue(fVal) == FormulaError::ElementNaN)
+ continue;
+
+ ++aRes.mfCount;
+ if (bNull && fVal != 0.0)
+ {
+ bNull = false;
+ aRes.mfMem = fVal;
+ }
+ else
+ aRes.mfSum += fVal;
+ if ( aRes.mfMin > fVal )
+ aRes.mfMin = fVal;
+ if ( aRes.mfMax < fVal )
+ aRes.mfMax = fVal;
+ }
+ if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
{
- bNull = false;
- aRes.mfMem = fVal;
+ xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
+ aRes = sc::ParamIfsResult();
}
- else
- aRes.mfSum += fVal;
- if ( aRes.mfMin > fVal )
- aRes.mfMin = fVal;
- if ( aRes.mfMax < fVal )
- aRes.mfMax = fVal;
}
+ while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
}
else
{
- std::vector<sal_uInt32>::const_iterator itRes = vConditions.begin();
- for (SCCOL nCol = 0; nCol < nDimensionCols; ++nCol)
+ ScAddress aAdr;
+ aAdr.SetTab( nMainTab1 );
+ do
{
- for (SCROW nRow = 0; nRow < nDimensionRows; ++nRow, ++itRes)
+ if (nRefArrayMainPos < vRefArrayConditions.size())
+ vConditions = vRefArrayConditions[nRefArrayMainPos];
+
+ std::vector<sal_uInt32>::const_iterator itRes = vConditions.begin();
+ for (SCCOL nCol = 0; nCol < nDimensionCols; ++nCol)
{
- if (*itRes == nQueryCount)
+ for (SCROW nRow = 0; nRow < nDimensionRows; ++nRow, ++itRes)
{
- aAdr.SetCol( nCol + nMainCol1);
- aAdr.SetRow( nRow + nMainRow1);
- ScRefCellValue aCell(*pDok, aAdr);
- if (aCell.hasNumeric())
+ if (*itRes == nQueryCount)
{
- fVal = GetCellValue(aAdr, aCell);
- ++aRes.mfCount;
- if ( bNull && fVal != 0.0 )
+ aAdr.SetCol( nCol + nMainCol1);
+ aAdr.SetRow( nRow + nMainRow1);
+ ScRefCellValue aCell(*pDok, aAdr);
+ if (aCell.hasNumeric())
{
- bNull = false;
- aRes.mfMem = fVal;
+ fVal = GetCellValue(aAdr, aCell);
+ ++aRes.mfCount;
+ if ( bNull && fVal != 0.0 )
+ {
+ bNull = false;
+ aRes.mfMem = fVal;
+ }
+ else
+ aRes.mfSum += fVal;
+ if ( aRes.mfMin > fVal )
+ aRes.mfMin = fVal;
+ if ( aRes.mfMax < fVal )
+ aRes.mfMax = fVal;
}
- else
- aRes.mfSum += fVal;
- if ( aRes.mfMin > fVal )
- aRes.mfMin = fVal;
- if ( aRes.mfMax < fVal )
- aRes.mfMax = fVal;
}
}
}
+ if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
+ {
+ xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
+ aRes = sc::ParamIfsResult();
+ }
}
+ while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
}
}
}
else
{
- std::vector<sal_uInt32>::const_iterator itRes = vConditions.begin(), itResEnd = vConditions.end();
- for (; itRes != itResEnd; ++itRes)
- if (*itRes == nQueryCount)
- ++aRes.mfCount;
+ // COUNTIFS only.
+ if (vRefArrayConditions.empty())
+ {
+ for (auto const & rCond : vConditions)
+ {
+ if (rCond == nQueryCount)
+ ++aRes.mfCount;
+ }
+ }
+ else
+ {
+ xResMat = GetNewMat( 1, nRefArrayRows);
+ for (size_t i=0, n = vRefArrayConditions.size(); i < n; ++i)
+ {
+ double fCount = 0.0;
+ for (auto const & rCond : vRefArrayConditions[i])
+ {
+ if (rCond == nQueryCount)
+ ++fCount;
+ }
+ if (fCount)
+ xResMat->PutDouble( fCount, 0, i);
+ }
+ }
}
- PushDouble( ResultFunc( aRes));
+
+ if (xResMat)
+ PushMatrix( xResMat);
+ else
+ PushDouble( ResultFunc( aRes));
}
void ScInterpreter::ScSumIfs()