fdo#85258 differentiate between empty cell and empty result in matrix

Change-Id: I79259224e411f9c1a6e852623d9d49cf89a03c27
diff --git a/sc/inc/scmatrix.hxx b/sc/inc/scmatrix.hxx
index a5117be..6bcd8857 100644
--- a/sc/inc/scmatrix.hxx
+++ b/sc/inc/scmatrix.hxx
@@ -278,6 +278,9 @@ public:
    /** Put a column vector of empties, starting at row nR, must fit into dimensions. */
    void PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );

    /** Put a column vector of empty results, starting at row nR, must fit into dimensions. */
    void PutEmptyResultVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );

    /** Put a column vector of empty paths, starting at row nR, must fit into dimensions. */
    void PutEmptyPathVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );

@@ -322,6 +325,9 @@ public:
    /// @return <TRUE/> if empty or empty path.
    bool IsEmpty( SCSIZE nC, SCSIZE nR ) const;

    /// @return <TRUE/> if empty, not empty result or empty path.
    bool IsEmptyCell( SCSIZE nC, SCSIZE nR ) const;

    /// @return <TRUE/> if empty path.
    bool IsEmptyPath( SCSIZE nC, SCSIZE nR ) const;

diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index c206867..9b0b5ef 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -2066,16 +2066,23 @@ namespace {

struct CellBucket
{
    SCSIZE mnEmpValStart;
    SCSIZE mnNumValStart;
    SCSIZE mnStrValStart;
    SCSIZE mnEmpValCount;
    std::vector<double> maNumVals;
    std::vector<svl::SharedString> maStrVals;

    CellBucket() : mnNumValStart(0), mnStrValStart(0) {}
    CellBucket() : mnEmpValStart(0), mnNumValStart(0), mnStrValStart(0), mnEmpValCount(0) {}

    void flush(ScMatrix& rMat, SCSIZE nCol)
    {
        if (!maNumVals.empty())
        if (mnEmpValCount)
        {
            rMat.PutEmptyResultVector(mnEmpValCount, nCol, mnEmpValStart);
            reset();
        }
        else if (!maNumVals.empty())
        {
            const double* p = &maNumVals[0];
            rMat.PutDouble(p, maNumVals.size(), nCol, mnNumValStart);
@@ -2091,7 +2098,8 @@ struct CellBucket

    void reset()
    {
        mnNumValStart = mnStrValStart = 0;
        mnEmpValStart = mnNumValStart = mnStrValStart = 0;
        mnEmpValCount = 0;
        maNumVals.clear();
        maStrVals.clear();
    }
@@ -2163,7 +2171,18 @@ public:

                    if (rCell.IsEmpty())
                    {
                        aBucket.flush(mrMat, mnMatCol);
                        if (aBucket.mnEmpValCount && nThisRow == nPrevRow + 1)
                        {
                            // Secondary empty results.
                            ++aBucket.mnEmpValCount;
                        }
                        else
                        {
                            // First empty result.
                            aBucket.flush(mrMat, mnMatCol);
                            aBucket.mnEmpValStart = nThisRow - mnTopRow;
                            ++aBucket.mnEmpValCount;
                        }
                        continue;
                    }

diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index 252d38d..326987e 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -1849,14 +1849,14 @@ void ScInterpreter::ScIsEmpty()
            if ( !pMat )
                ;   // nothing
            else if ( !pJumpMatrix )
                nRes = pMat->IsEmpty( 0, 0) ? 1 : 0;
                nRes = pMat->IsEmptyCell( 0, 0) ? 1 : 0;
            else
            {
                SCSIZE nCols, nRows, nC, nR;
                pMat->GetDimensions( nCols, nRows);
                pJumpMatrix->GetPos( nC, nR);
                if ( nC < nCols && nR < nRows )
                    nRes = pMat->IsEmpty( nC, nR) ? 1 : 0;
                    nRes = pMat->IsEmptyCell( nC, nR) ? 1 : 0;
                // else: false, not empty (which is what Xcl does)
            }
        }
diff --git a/sc/source/core/tool/scmatrix.cxx b/sc/source/core/tool/scmatrix.cxx
index c352ecb..af57384 100644
--- a/sc/source/core/tool/scmatrix.cxx
+++ b/sc/source/core/tool/scmatrix.cxx
@@ -194,6 +194,13 @@ _Comp CompareMatrixElemFunc<_Comp>::maComp;

}

/* TODO: it would be good if mdds had get/set<sal_uInt8> additionally to
 * get/set<bool>, we're abusing double here. */
typedef double TMatFlag;
const TMatFlag SC_MATFLAG_EMPTYCELL   = 0.0;
const TMatFlag SC_MATFLAG_EMPTYRESULT = 1.0;
const TMatFlag SC_MATFLAG_EMPTYPATH   = 2.0;

class ScMatrixImpl: private boost::noncopyable
{
    MatrixImplType maMat;
@@ -246,6 +253,7 @@ public:
    bool IsString( SCSIZE nIndex ) const;
    bool IsString( SCSIZE nC, SCSIZE nR ) const;
    bool IsEmpty( SCSIZE nC, SCSIZE nR ) const;
    bool IsEmptyCell( SCSIZE nC, SCSIZE nR ) const;
    bool IsEmptyPath( SCSIZE nC, SCSIZE nR ) const;
    bool IsValue( SCSIZE nIndex ) const;
    bool IsValue( SCSIZE nC, SCSIZE nR ) const;
@@ -259,6 +267,7 @@ public:
    void PutDoubleVector( const ::std::vector< double > & rVec, SCSIZE nC, SCSIZE nR );
    void PutStringVector( const ::std::vector< svl::SharedString > & rVec, SCSIZE nC, SCSIZE nR );
    void PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );
    void PutEmptyResultVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );
    void PutEmptyPathVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );
    void CompareEqual();
    void CompareNotEqual();
@@ -298,7 +307,7 @@ private:
};

ScMatrixImpl::ScMatrixImpl(SCSIZE nC, SCSIZE nR) :
    maMat(nR, nC), maMatFlag(nR, nC), pErrorInterpreter(NULL), mbCloneIfConst(true) {}
    maMat(nR, nC), maMatFlag(nR, nC, SC_MATFLAG_EMPTYCELL), pErrorInterpreter(NULL), mbCloneIfConst(true) {}

ScMatrixImpl::ScMatrixImpl(SCSIZE nC, SCSIZE nR, double fInitVal) :
    maMat(nR, nC, fInitVal), maMatFlag(nR, nC), pErrorInterpreter(NULL), mbCloneIfConst(true) {}
@@ -452,7 +461,7 @@ void ScMatrixImpl::PutEmpty(SCSIZE nC, SCSIZE nR)
    if (ValidColRow( nC, nR))
    {
        maMat.set_empty(nR, nC);
        maMatFlag.set(nR, nC, false); // zero flag to indicate that this is 'empty', not 'empty path'.
        maMatFlag.set(nR, nC, SC_MATFLAG_EMPTYCELL);
    }
    else
    {
@@ -465,7 +474,7 @@ void ScMatrixImpl::PutEmptyPath(SCSIZE nC, SCSIZE nR)
    if (ValidColRow( nC, nR))
    {
        maMat.set_empty(nR, nC);
        maMatFlag.set(nR, nC, true); // non-zero flag to indicate empty 'path'.
        maMatFlag.set(nR, nC, SC_MATFLAG_EMPTYPATH);
    }
    else
    {
@@ -580,7 +589,7 @@ svl::SharedString ScMatrixImpl::GetString( SvNumberFormatter& rFormatter, SCSIZE
            return maMat.get_string(aPos).getString();
        case mdds::mtm::element_empty:
        {
            if (!maMatFlag.get<bool>(nR, nC))
            if (maMatFlag.get<TMatFlag>(nR, nC) != SC_MATFLAG_EMPTYPATH)
                // not an empty path.
                return svl::SharedString::getEmptyString();

@@ -636,8 +645,10 @@ ScMatrixValue ScMatrixImpl::Get(SCSIZE nC, SCSIZE nR) const
                aVal.aStr = maMat.get_string(aPos);
            break;
            case mdds::mtm::element_empty:
                // Empty path equals empty plus flag.
                aVal.nType = maMatFlag.get<bool>(nR, nC) ? SC_MATVAL_EMPTYPATH : SC_MATVAL_EMPTY;
                /* TODO: do we need to pass the differentiation of 'empty' and
                 * 'empty result' to the outer world anywhere? */
                aVal.nType = maMatFlag.get<TMatFlag>(nR, nC) == SC_MATFLAG_EMPTYPATH ? SC_MATVAL_EMPTYPATH :
                    SC_MATVAL_EMPTY;
                aVal.fVal = 0.0;
            default:
                ;
@@ -673,17 +684,28 @@ bool ScMatrixImpl::IsString( SCSIZE nC, SCSIZE nR ) const

bool ScMatrixImpl::IsEmpty( SCSIZE nC, SCSIZE nR ) const
{
    // Flag must be zero for this to be an empty element, instead of being an
    // empty path element.
    // Flag must indicate an empty element instead of an
    // 'empty path' element.
    ValidColRowReplicated( nC, nR );
    return maMat.get_type(nR, nC) == mdds::mtm::element_empty && !maMatFlag.get<bool>(nR, nC);
    return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
        maMatFlag.get<TMatFlag>(nR, nC) != SC_MATFLAG_EMPTYPATH;
}

bool ScMatrixImpl::IsEmptyCell( SCSIZE nC, SCSIZE nR ) const
{
    // Flag must indicate an 'empty' element instead of an
    // 'empty result' or 'empty path' element.
    ValidColRowReplicated( nC, nR );
    return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
        maMatFlag.get<TMatFlag>(nR, nC) == SC_MATFLAG_EMPTYCELL;
}

bool ScMatrixImpl::IsEmptyPath( SCSIZE nC, SCSIZE nR ) const
{
    // 'Empty path' is empty plus non-zero flag.
    // Flag must indicate an 'empty path' element.
    if (ValidColRowOrReplicated( nC, nR ))
        return maMat.get_type(nR, nC) == mdds::mtm::element_empty && maMatFlag.get<bool>(nR, nC);
        return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
            maMatFlag.get<TMatFlag>(nR, nC) == SC_MATFLAG_EMPTYPATH;
    else
        return true;
}
@@ -799,8 +821,8 @@ void ScMatrixImpl::PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
    if (nCount && ValidColRow( nC, nR) && ValidColRow( nC, nR + nCount - 1))
    {
        maMat.set_empty(nR, nC, nCount);
        // zero flag to indicate that this is 'empty', not 'empty path'.
        std::vector<bool> aVals(nCount, false);
        // Flag to indicate that this is 'empty', not 'empty result' or 'empty path'.
        std::vector<TMatFlag> aVals(nCount, SC_MATFLAG_EMPTYCELL);
        maMatFlag.set(nR, nC, aVals.begin(), aVals.end());
    }
    else
@@ -809,13 +831,28 @@ void ScMatrixImpl::PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
    }
}

void ScMatrixImpl::PutEmptyResultVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
{
    if (nCount && ValidColRow( nC, nR) && ValidColRow( nC, nR + nCount - 1))
    {
        maMat.set_empty(nR, nC, nCount);
        // Flag to indicate that this is 'empty result', not 'empty' or 'empty path'.
        std::vector<TMatFlag> aVals(nCount, SC_MATFLAG_EMPTYRESULT);
        maMatFlag.set(nR, nC, aVals.begin(), aVals.end());
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::PutEmptyResultVector: dimension error");
    }
}

void ScMatrixImpl::PutEmptyPathVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
{
    if (nCount && ValidColRow( nC, nR) && ValidColRow( nC, nR + nCount - 1))
    {
        maMat.set_empty(nR, nC, nCount);
        // non-zero flag to indicate empty 'path'.
        std::vector<bool> aVals(nCount, true);
        // Flag to indicate 'empty path'.
        std::vector<TMatFlag> aVals(nCount, SC_MATFLAG_EMPTYPATH);
        maMatFlag.set(nR, nC, aVals.begin(), aVals.end());
    }
    else
@@ -2281,6 +2318,11 @@ bool ScMatrix::IsEmpty( SCSIZE nC, SCSIZE nR ) const
    return pImpl->IsEmpty(nC, nR);
}

bool ScMatrix::IsEmptyCell( SCSIZE nC, SCSIZE nR ) const
{
    return pImpl->IsEmptyCell(nC, nR);
}

bool ScMatrix::IsEmptyPath( SCSIZE nC, SCSIZE nR ) const
{
    return pImpl->IsEmptyPath(nC, nR);
@@ -2341,6 +2383,11 @@ void ScMatrix::PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
    pImpl->PutEmptyVector(nCount, nC, nR);
}

void ScMatrix::PutEmptyResultVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
{
    pImpl->PutEmptyResultVector(nCount, nC, nR);
}

void ScMatrix::PutEmptyPathVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
{
    pImpl->PutEmptyPathVector(nCount, nC, nR);