From 34c491dabedf3ce4feb1db6d00df33e5573ec03c Mon Sep 17 00:00:00 2001 From: Kohei Yoshida Date: Fri, 26 Apr 2013 22:53:04 -0400 Subject: Handle invariant group with single references. Change-Id: Ifbbac2b11b1023a5cf3d21204c12b9740af09aaf --- sc/source/core/data/column2.cxx | 70 +++++++++++++++++--- sc/source/core/data/document.cxx | 16 ++++- sc/source/core/data/formulacell.cxx | 126 ++++++++++++++++++++++++------------ sc/source/core/data/table1.cxx | 28 ++++++-- 4 files changed, 181 insertions(+), 59 deletions(-) (limited to 'sc/source') diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx index e95dcf93a6c4..20483f84a4d2 100644 --- a/sc/source/core/data/column2.cxx +++ b/sc/source/core/data/column2.cxx @@ -1589,28 +1589,78 @@ ScFormulaVectorState ScColumn::GetFormulaVectorState( SCROW nRow ) const return pCell ? pCell->GetVectorState() : FormulaVectorUnknown; } -bool ScColumn::ResolveVectorReference( SCROW nRow1, SCROW nRow2 ) +formula::FormulaTokenRef ScColumn::ResolveStaticReference( SCROW nRow ) { std::vector::iterator itEnd = maItems.end(); // Find first cell whose position is equal or greater than nRow1. ColEntry aBound; - aBound.nRow = nRow1; + aBound.nRow = nRow; std::vector::iterator it = std::lower_bound(maItems.begin(), itEnd, aBound, ColEntry::Less()); - if (it == itEnd) + if (it == itEnd || it->nRow != nRow) + { + // Empty cell. + return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0)); + } + + ScBaseCell* pCell = it->pCell; + switch (pCell->GetCellType()) + { + case CELLTYPE_VALUE: + { + ScValueCell* pVC = static_cast(pCell); + return formula::FormulaTokenRef(new formula::FormulaDoubleToken(pVC->GetValue())); + } + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFC = static_cast(pCell); + if (pFC->GetDirty()) + // Dirty formula cell is not considered static. Return null token. + return formula::FormulaTokenRef(); + + return formula::FormulaTokenRef(new formula::FormulaDoubleToken(pFC->GetResultDouble())); + } + default: + ; + } + + return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0)); +} + +bool ScColumn::ResolveStaticReference( ScMatrix& rMat, SCCOL nMatCol, SCROW nRow1, SCROW nRow2 ) +{ + if (nRow1 > nRow2) return false; + std::vector::iterator itEnd = maItems.end(); + // Find first cell whose position is equal or greater than nRow1. + ColEntry aBound; + aBound.nRow = nRow1; + std::vector::iterator it = + std::lower_bound(maItems.begin(), itEnd, aBound, ColEntry::Less()); + for (; it != itEnd && it->nRow <= nRow2; ++it) { - if (it->pCell->GetCellType() != CELLTYPE_FORMULA) - // Non-formula cells are fine. - continue; + switch (it->pCell->GetCellType()) + { + case CELLTYPE_VALUE: + { + ScValueCell* pVC = static_cast(it->pCell); + rMat.PutDouble(pVC->GetValue(), nMatCol, it->nRow - nRow1); + } + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFC = static_cast(it->pCell); + if (pFC->GetDirty()) + // Dirty formula cell is not considered static. Return null token. + return false; - ScFormulaCell* pFC = static_cast(it->pCell); - if (pFC->GetDirty()) - // Dirty formula cells are not supported yet. - return false; + rMat.PutDouble(pFC->GetResultDouble(), nMatCol, it->nRow - nRow1); + } + default: + ; + } } return true; diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx index 6a00d36d0b6e..9a3dcf7b1668 100644 --- a/sc/source/core/data/document.cxx +++ b/sc/source/core/data/document.cxx @@ -1574,13 +1574,23 @@ ScFormulaVectorState ScDocument::GetFormulaVectorState( const ScAddress& rPos ) return maTabs[nTab]->GetFormulaVectorState(rPos.Col(), rPos.Row()); } -bool ScDocument::ResolveVectorReference( const ScAddress& rPos, SCROW nEndRow ) +formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScAddress& rPos ) { SCTAB nTab = rPos.Tab(); if (!TableExists(nTab)) - return false; + return formula::FormulaTokenRef(); + + return maTabs[nTab]->ResolveStaticReference(rPos.Col(), rPos.Row()); +} + +formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScRange& rRange ) +{ + SCTAB nTab = rRange.aStart.Tab(); + if (nTab != rRange.aEnd.Tab() || !TableExists(nTab)) + return formula::FormulaTokenRef(); - return maTabs[nTab]->ResolveVectorReference(rPos.Col(), rPos.Row(), nEndRow); + return maTabs[nTab]->ResolveStaticReference( + rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()); } bool ScDocument::CanFitBlock( const ScRange& rOld, const ScRange& rNew ) diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx index e73641d606b0..43d80cf906df 100644 --- a/sc/source/core/data/formulacell.cxx +++ b/sc/source/core/data/formulacell.cxx @@ -2954,27 +2954,75 @@ bool ScFormulaCell::InterpretFormulaGroup() switch (pCode->GetVectorState()) { case FormulaVectorEnabled: + case FormulaVectorCheckReference: // Good. break; - case FormulaVectorCheckReference: - // To support this we would need a real range-based dependency - // tracking. We can't support this right now. - return false; case FormulaVectorDisabled: case FormulaVectorUnknown: default: + // Not good. return false; } -// fprintf( stderr, "Interpret cell %d, %d\n", (int)aPos.Col(), (int)aPos.Row() ); - if (xGroup->mbInvariant) { -// fprintf( stderr, "struck gold - completely invariant for %d items !\n", -// (int)xGroup->mnLength ); + if (pCode->GetVectorState() == FormulaVectorCheckReference) + { + // An invariant group should only have absolute references, and no + // external references are allowed. + + ScTokenArray aCode; + pCode->Reset(); + for (const formula::FormulaToken* p = pCode->First(); p; p = pCode->Next()) + { + const ScToken* pToken = static_cast(p); + switch (pToken->GetType()) + { + case svSingleRef: + { + const ScSingleRefData& rRef = pToken->GetSingleRef(); + ScAddress aRefPos(rRef.nCol, rRef.nRow, rRef.nTab); + formula::FormulaTokenRef pNewToken = pDocument->ResolveStaticReference(aRefPos); + if (!pNewToken) + return false; + + aCode.AddToken(*pNewToken); + } + break; + case svDoubleRef: + { + const ScComplexRefData& rRef = pToken->GetDoubleRef(); + ScRange aRefRange( + rRef.Ref1.nCol, rRef.Ref1.nRow, rRef.Ref1.nTab, + rRef.Ref2.nCol, rRef.Ref2.nRow, rRef.Ref2.nTab); + + formula::FormulaTokenRef pNewToken = pDocument->ResolveStaticReference(aRefRange); + if (!pNewToken) + return false; + + aCode.AddToken(*pNewToken); + } + break; + default: + aCode.AddToken(*pToken); + } + } + + ScCompiler aComp(pDocument, aPos, aCode); + aComp.SetGrammar(pDocument->GetGrammar()); + aComp.CompileTokenArray(); // Create RPN token array. + ScInterpreter aInterpreter(this, pDocument, aPos, aCode); + aInterpreter.Interpret(); + aResult.SetToken(aInterpreter.GetResultToken().get()); + } + else + { + // Formula contains no references. + ScInterpreter aInterpreter(this, pDocument, aPos, *pCode); + aInterpreter.Interpret(); + aResult.SetToken(aInterpreter.GetResultToken().get()); + } - // calculate ourselves: - InterpretTail( SCITP_NORMAL ); for ( sal_Int32 i = 0; i < xGroup->mnLength; i++ ) { ScAddress aTmpPos = aPos; @@ -2993,37 +3041,35 @@ bool ScFormulaCell::InterpretFormulaGroup() } return true; } - else - { - // scan the formula ... - // have a document method: "Get2DRangeAsDoublesArray" that does the - // column-based heavy lifting call it for each absolute range from the - // first cell pos in the formula group. - // - // Project single references to ranges by adding their vector * xGroup->mnLength - // - // TODO: - // elide multiple dimensional movement in vectors eg. =SUM(A1<1,1>) - // produces a diagonal 'column' that serves no useful purpose for us. - // these should be very rare. Should elide in GetDeltas anyway and - // assert here. - // - // Having built our input data ... - // Throw it, and the formula over to some 'OpenCLCalculage' hook - // - // on return - release references on these double buffers - // - // transfer the result to the formula cells (as above) - // store the doubles in the columns' maDoubles array for - // dependent formulae - // - // TODO: - // need to abort/fail when we get errors returned and fallback to - // stock interpreting [ I guess ], unless we can use NaN etc. to - // signal errors. - return false; - } + // scan the formula ... + // have a document method: "Get2DRangeAsDoublesArray" that does the + // column-based heavy lifting call it for each absolute range from the + // first cell pos in the formula group. + // + // Project single references to ranges by adding their vector * xGroup->mnLength + // + // TODO: + // elide multiple dimensional movement in vectors eg. =SUM(A1<1,1>) + // produces a diagonal 'column' that serves no useful purpose for us. + // these should be very rare. Should elide in GetDeltas anyway and + // assert here. + // + // Having built our input data ... + // Throw it, and the formula over to some 'OpenCLCalculage' hook + // + // on return - release references on these double buffers + // + // transfer the result to the formula cells (as above) + // store the doubles in the columns' maDoubles array for + // dependent formulae + // + // TODO: + // need to abort/fail when we get errors returned and fallback to + // stock interpreting [ I guess ], unless we can use NaN etc. to + // signal errors. + + return false; } void ScFormulaCell::StartListeningTo( ScDocument* pDoc ) diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx index 7ae3c66ea8d7..52e0bb98c8e2 100644 --- a/sc/source/core/data/table1.cxx +++ b/sc/source/core/data/table1.cxx @@ -2113,15 +2113,31 @@ ScFormulaVectorState ScTable::GetFormulaVectorState( SCCOL nCol, SCROW nRow ) co return aCol[nCol].GetFormulaVectorState(nRow); } -bool ScTable::ResolveVectorReference( SCCOL nCol, SCROW nRow1, SCROW nRow2 ) +formula::FormulaTokenRef ScTable::ResolveStaticReference( SCCOL nCol, SCROW nRow ) { - if (!ValidCol(nCol) || !ValidRow(nRow1) || !ValidRow(nRow2)) - return false; + if (!ValidCol(nCol) || !ValidRow(nRow)) + return formula::FormulaTokenRef(); - if (!aCol[nCol].ResolveVectorReference(nRow1, nRow2)) - return false; + return aCol[nCol].ResolveStaticReference(nRow); +} - return true; +formula::FormulaTokenRef ScTable::ResolveStaticReference( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) +{ + if (nCol2 < nCol1 || nRow2 < nRow1) + return formula::FormulaTokenRef(); + + if (!ValidCol(nCol1) || !ValidCol(nCol2) || !ValidRow(nRow1) || !ValidRow(nRow2)) + return formula::FormulaTokenRef(); + + ScMatrixRef pMat(new ScMatrix(nCol2-nCol1+1, nRow2-nRow1+1, 0.0)); + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + { + if (!aCol[nCol].ResolveStaticReference(*pMat, nCol2-nCol1, nRow1, nRow2)) + // Column contains non-static cell. Failed. + return formula::FormulaTokenRef(); + } + + return formula::FormulaTokenRef(new ScMatrixToken(pMat)); } ScRefCellValue ScTable::GetRefCellValue( SCCOL nCol, SCROW nRow ) -- cgit