Resolves: tdf#129681 Handle array/matrix in AGGREGATE() with ignore errors

This also changes AGGREGATE() 3rd and subsequent parameters'
classification from ReferenceOrRefArray to ReferenceOrForceArray
to force the expected array mode on inline calculations.

Change-Id: I53a5591e46bfbabbfa6a273f5b9590a69fad87a0
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/86388
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
diff --git a/sc/inc/scmatrix.hxx b/sc/inc/scmatrix.hxx
index ca9d349..2efa73c 100644
--- a/sc/inc/scmatrix.hxx
+++ b/sc/inc/scmatrix.hxx
@@ -362,15 +362,15 @@ public:
    double Or() const ;        // logical OR of all matrix values, or NAN
    double Xor() const ;       // logical XOR of all matrix values, or NAN

    IterateResult Sum(bool bTextAsZero) const ;
    IterateResult SumSquare(bool bTextAsZero) const ;
    IterateResult Product(bool bTextAsZero) const ;
    IterateResult Sum( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ;
    IterateResult SumSquare( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ;
    IterateResult Product( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ;
    size_t Count(bool bCountStrings, bool bCountErrors) const ;
    size_t MatchDoubleInColumns(double fValue, size_t nCol1, size_t nCol2) const ;
    size_t MatchStringInColumns(const svl::SharedString& rStr, size_t nCol1, size_t nCol2) const ;

    double GetMaxValue( bool bTextAsZero ) const ;
    double GetMinValue( bool bTextAsZero ) const ;
    double GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ;
    double GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ;
    double GetGcd() const ;
    double GetLcm() const ;

diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index 175228c..b6fa53b 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -3721,7 +3721,7 @@ void ScInterpreter::ScMin( bool bTextAsZero )
                if (pMat)
                {
                    nFuncFmtType = SvNumFormatType::NUMBER;
                    nVal = pMat->GetMinValue(bTextAsZero);
                    nVal = pMat->GetMinValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
                    if (nMin > nVal)
                        nMin = nVal;
                }
@@ -3879,7 +3879,7 @@ void ScInterpreter::ScMax( bool bTextAsZero )
                if (pMat)
                {
                    nFuncFmtType = SvNumFormatType::NUMBER;
                    nVal = pMat->GetMaxValue(bTextAsZero);
                    nVal = pMat->GetMaxValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
                    if (nMax < nVal)
                        nMax = nVal;
                }
@@ -4069,6 +4069,7 @@ void ScInterpreter::GetStVarParams( bool bTextAsZero, double(*VarResult)( double
                ScMatrixRef pMat = GetMatrix();
                if (pMat)
                {
                    const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal);
                    SCSIZE nC, nR;
                    pMat->GetDimensions(nC, nR);
                    for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
@@ -4083,6 +4084,8 @@ void ScInterpreter::GetStVarParams( bool bTextAsZero, double(*VarResult)( double
                                    values.push_back(fVal);
                                    fSum += fVal;
                                }
                                else if (bIgnoreErrVal)
                                    nGlobalError = FormulaError::NONE;
                            }
                            else if ( bTextAsZero )
                            {
@@ -7565,6 +7568,9 @@ void ScInterpreter::ScAggregate()
    sal_uInt8 nParamCount = GetByte();
    if ( MustHaveParamCountMin( nParamCount, 3 ) )
    {
        const FormulaError nErr = nGlobalError;
        nGlobalError = FormulaError::NONE;

        // fish the 1st parameter from the stack and push it on top.
        const FormulaToken* p = pStack[ sp - nParamCount ];
        PushWithoutError( *p );
@@ -7575,7 +7581,10 @@ void ScInterpreter::ScAggregate()
        sal_Int32 nOption = GetInt32();

        if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 19 )
        {
            nGlobalError = nErr;
            PushIllegalArgument();
        }
        else
        {
            switch ( nOption)
@@ -7605,10 +7614,14 @@ void ScInterpreter::ScAggregate()
                    mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal ;
                    break;
                default :
                    nGlobalError = nErr;
                    PushIllegalArgument();
                    return;
            }

            if ((mnSubTotalFlags & SubtotalFlags::IgnoreErrVal) == SubtotalFlags::NONE)
                nGlobalError = nErr;

            cPar = nParamCount - 2;
            switch ( nFunc )
            {
@@ -7631,7 +7644,10 @@ void ScInterpreter::ScAggregate()
                case AGGREGATE_FUNC_QRTINC  : ScQuartile( true );    break;
                case AGGREGATE_FUNC_PERCEXC : ScPercentile( false ); break;
                case AGGREGATE_FUNC_QRTEXC  : ScQuartile( false );   break;
                default : PushIllegalArgument();       break;
                default:
                    nGlobalError = nErr;
                    PushIllegalArgument();
                break;
            }
            mnSubTotalFlags = SubtotalFlags::NONE;
        }
diff --git a/sc/source/core/tool/interpr3.cxx b/sc/source/core/tool/interpr3.cxx
index 31dee4c..ad9db97 100644
--- a/sc/source/core/tool/interpr3.cxx
+++ b/sc/source/core/tool/interpr3.cxx
@@ -3943,6 +3943,7 @@ void ScInterpreter::GetNumberSequenceArray( sal_uInt8 nParamCount, vector<double
{
    ScAddress aAdr;
    ScRange aRange;
    const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal);
    short nParam = nParamCount;
    size_t nRefInList = 0;
    ReverseStack( nParamCount );
@@ -3958,7 +3959,9 @@ void ScInterpreter::GetNumberSequenceArray( sal_uInt8 nParamCount, vector<double
            {
                PopSingleRef( aAdr );
                ScRefCellValue aCell(*pDok, aAdr);
                if (aCell.hasNumeric())
                if (bIgnoreErrVal && aCell.hasError())
                    ;   // nothing
                else if (aCell.hasNumeric())
                    rArray.push_back(GetCellValue(aAdr, aCell));
            }
            break;
@@ -3979,11 +3982,24 @@ void ScInterpreter::GetNumberSequenceArray( sal_uInt8 nParamCount, vector<double
                ScValueIterator aValIter( pDok, aRange, mnSubTotalFlags );
                if (aValIter.GetFirst( fCellVal, nErr))
                {
                    rArray.push_back( fCellVal);
                    SetError(nErr);
                    while ((nErr == FormulaError::NONE) && aValIter.GetNext( fCellVal, nErr))
                    if (bIgnoreErrVal)
                    {
                        if (nErr == FormulaError::NONE)
                            rArray.push_back( fCellVal);
                        while (aValIter.GetNext( fCellVal, nErr))
                        {
                            if (nErr == FormulaError::NONE)
                                rArray.push_back( fCellVal);
                        }
                    }
                    else
                    {
                        rArray.push_back( fCellVal);
                    SetError(nErr);
                        SetError(nErr);
                        while ((nErr == FormulaError::NONE) && aValIter.GetNext( fCellVal, nErr))
                            rArray.push_back( fCellVal);
                        SetError(nErr);
                    }
                }
            }
            break;
@@ -3999,15 +4015,40 @@ void ScInterpreter::GetNumberSequenceArray( sal_uInt8 nParamCount, vector<double
                rArray.reserve( rArray.size() + nCount);
                if (pMat->IsNumeric())
                {
                    for (SCSIZE i = 0; i < nCount; ++i)
                        rArray.push_back( pMat->GetDouble(i));
                    if (bIgnoreErrVal)
                    {
                        for (SCSIZE i = 0; i < nCount; ++i)
                        {
                            const double fVal = pMat->GetDouble(i);
                            if (nGlobalError == FormulaError::NONE)
                                rArray.push_back( fVal);
                            else
                                nGlobalError = FormulaError::NONE;
                        }
                    }
                    else
                    {
                        for (SCSIZE i = 0; i < nCount; ++i)
                            rArray.push_back( pMat->GetDouble(i));
                    }
                }
                else if (bConvertTextInArray && eStackType == svMatrix)
                {
                    for (SCSIZE i = 0; i < nCount; ++i)
                    {
                        if ( pMat->IsValue( i ) )
                            rArray.push_back( pMat->GetDouble(i));
                        {
                            if (bIgnoreErrVal)
                            {
                                const double fVal = pMat->GetDouble(i);
                                if (nGlobalError == FormulaError::NONE)
                                    rArray.push_back( fVal);
                                else
                                    nGlobalError = FormulaError::NONE;
                            }
                            else
                                rArray.push_back( pMat->GetDouble(i));
                        }
                        else
                        {
                            // tdf#88547 try to convert string to (date)value
@@ -4024,13 +4065,17 @@ void ScInterpreter::GetNumberSequenceArray( sal_uInt8 nParamCount, vector<double
                                }
                                else
                                {
                                    rArray.push_back( CreateDoubleError( FormulaError::NoValue));
                                    if (!bIgnoreErrVal)
                                        rArray.push_back( CreateDoubleError( FormulaError::NoValue));
                                    // Propagate previous error if any, else
                                    // the current #VALUE! error.
                                    // the current #VALUE! error, unless
                                    // ignoring error values.
                                    if (nErr != FormulaError::NONE)
                                        nGlobalError = nErr;
                                    else
                                    else if (!bIgnoreErrVal)
                                        nGlobalError = FormulaError::NoValue;
                                    else
                                        nGlobalError = FormulaError::NONE;
                                }
                            }
                        }
@@ -4038,10 +4083,27 @@ void ScInterpreter::GetNumberSequenceArray( sal_uInt8 nParamCount, vector<double
                }
                else
                {
                    for (SCSIZE i = 0; i < nCount; ++i)
                    if (bIgnoreErrVal)
                    {
                        if ( pMat->IsValue( i ) )
                            rArray.push_back( pMat->GetDouble(i));
                        for (SCSIZE i = 0; i < nCount; ++i)
                        {
                            if (pMat->IsValue(i))
                            {
                                const double fVal = pMat->GetDouble(i);
                                if (nGlobalError == FormulaError::NONE)
                                    rArray.push_back( fVal);
                                else
                                    nGlobalError = FormulaError::NONE;
                            }
                        }
                    }
                    else
                    {
                        for (SCSIZE i = 0; i < nCount; ++i)
                        {
                            if (pMat->IsValue(i))
                                rArray.push_back( pMat->GetDouble(i));
                        }
                    }
                }
            }
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index 78a2bf5..b1a3658 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -3926,6 +3926,7 @@ bool IsErrFunc(OpCode oc)
        case ocIfError :
        case ocIfNA :
        case ocErrorType_ODF :
        case ocAggregate:       // may ignore errors depending on option
        case ocIfs_MS:
        case ocSwitch_MS:
            return true;
diff --git a/sc/source/core/tool/interpr6.cxx b/sc/source/core/tool/interpr6.cxx
index 7038e4d..327fff5 100644
--- a/sc/source/core/tool/interpr6.cxx
+++ b/sc/source/core/tool/interpr6.cxx
@@ -404,20 +404,20 @@ public:
}

static void IterateMatrix(
    const ScMatrixRef& pMat, ScIterFunc eFunc, bool bTextAsZero,
    const ScMatrixRef& pMat, ScIterFunc eFunc, bool bTextAsZero, SubtotalFlags nSubTotalFlags,
    sal_uLong& rCount, SvNumFormatType& rFuncFmtType, double& fRes, double& fMem )
{
    if (!pMat)
        return;

    // TODO fdo73148 take mnSubTotalFlags into account
    const bool bIgnoreErrVal = bool(nSubTotalFlags & SubtotalFlags::IgnoreErrVal);
    rFuncFmtType = SvNumFormatType::NUMBER;
    switch (eFunc)
    {
        case ifAVERAGE:
        case ifSUM:
        {
            ScMatrix::IterateResult aRes = pMat->Sum(bTextAsZero);
            ScMatrix::IterateResult aRes = pMat->Sum(bTextAsZero, bIgnoreErrVal);
            // If the first value is a NaN, it probably means it was an empty cell,
            // and should be treated as zero.
            if ( !rtl::math::isFinite(aRes.mfFirst) )
@@ -442,11 +442,12 @@ static void IterateMatrix(
            rCount += pMat->Count(bTextAsZero, false);  // do not count error values
        break;
        case ifCOUNT2:
            /* TODO: what is this supposed to be with bIgnoreErrVal? */
            rCount += pMat->Count(true, true);          // do count error values
        break;
        case ifPRODUCT:
        {
            ScMatrix::IterateResult aRes = pMat->Product(bTextAsZero);
            ScMatrix::IterateResult aRes = pMat->Product(bTextAsZero, bIgnoreErrVal);
            fRes *= aRes.mfFirst;
            fRes *= aRes.mfRest;
            rCount += aRes.mnCount;
@@ -454,7 +455,7 @@ static void IterateMatrix(
        break;
        case ifSUMSQ:
        {
            ScMatrix::IterateResult aRes = pMat->SumSquare(bTextAsZero);
            ScMatrix::IterateResult aRes = pMat->SumSquare(bTextAsZero, bIgnoreErrVal);
            fRes += aRes.mfFirst;
            fRes += aRes.mfRest;
            rCount += aRes.mnCount;
@@ -979,14 +980,14 @@ void ScInterpreter::IterateParameters( ScIterFunc eFunc, bool bTextAsZero )
                if ( nGlobalError != FormulaError::NONE && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
                    break;

                IterateMatrix( pMat, eFunc, bTextAsZero, nCount, nFuncFmtType, fRes, fMem );
                IterateMatrix( pMat, eFunc, bTextAsZero, mnSubTotalFlags, nCount, nFuncFmtType, fRes, fMem );
            }
            break;
            case svMatrix :
            {
                ScMatrixRef pMat = PopMatrix();

                IterateMatrix( pMat, eFunc, bTextAsZero, nCount, nFuncFmtType, fRes, fMem );
                IterateMatrix( pMat, eFunc, bTextAsZero, mnSubTotalFlags, nCount, nFuncFmtType, fRes, fMem );
            }
            break;
            case svError:
diff --git a/sc/source/core/tool/parclass.cxx b/sc/source/core/tool/parclass.cxx
index e05b48e..f285df4 100644
--- a/sc/source/core/tool/parclass.cxx
+++ b/sc/source/core/tool/parclass.cxx
@@ -99,7 +99,7 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
    { ocTableOp,         {{ Value, Value, Value, Value, Value                    }, 0, Value }},
    // Operators and functions.
    { ocAdd,             {{ Array, Array                                         }, 0, Value }},
    { ocAggregate,       {{ Value, Value, ReferenceOrRefArray                    }, 1, Value }},
    { ocAggregate,       {{ Value, Value, ReferenceOrForceArray                  }, 1, Value }},
    { ocAmpersand,       {{ Array, Array                                         }, 0, Value }},
    { ocAnd,             {{ Reference                                            }, 1, Value }},
    { ocAreas,           {{ Reference                                            }, 0, Value }},
diff --git a/sc/source/core/tool/scmatrix.cxx b/sc/source/core/tool/scmatrix.cxx
index 934c14d..869a914 100644
--- a/sc/source/core/tool/scmatrix.cxx
+++ b/sc/source/core/tool/scmatrix.cxx
@@ -308,15 +308,15 @@ public:
    double Or() const;
    double Xor() const;

    ScMatrix::IterateResult Sum(bool bTextAsZero) const;
    ScMatrix::IterateResult SumSquare(bool bTextAsZero) const;
    ScMatrix::IterateResult Product(bool bTextAsZero) const;
    ScMatrix::IterateResult Sum( bool bTextAsZero, bool bIgnoreErrorValues ) const;
    ScMatrix::IterateResult SumSquare( bool bTextAsZero, bool bIgnoreErrorValues ) const;
    ScMatrix::IterateResult Product( bool bTextAsZero, bool bIgnoreErrorValues ) const;
    size_t Count(bool bCountStrings, bool bCountErrors) const;
    size_t MatchDoubleInColumns(double fValue, size_t nCol1, size_t nCol2) const;
    size_t MatchStringInColumns(const svl::SharedString& rStr, size_t nCol1, size_t nCol2) const;

    double GetMaxValue( bool bTextAsZero ) const;
    double GetMinValue( bool bTextAsZero ) const;
    double GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues ) const;
    double GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) const;
    double GetGcd() const;
    double GetLcm() const;

@@ -1120,8 +1120,12 @@ class WalkElementBlocks
    ScMatrix::IterateResult maRes;
    bool mbFirst:1;
    bool mbTextAsZero:1;
    bool mbIgnoreErrorValues:1;
public:
    WalkElementBlocks(bool bTextAsZero) : maRes(Op::InitVal, Op::InitVal, 0), mbFirst(true), mbTextAsZero(bTextAsZero) {}
    WalkElementBlocks(bool bTextAsZero, bool bIgnoreErrorValues) :
        maRes(Op::InitVal, Op::InitVal, 0), mbFirst(true),
        mbTextAsZero(bTextAsZero), mbIgnoreErrorValues(bIgnoreErrorValues)
    {}

    const ScMatrix::IterateResult& getResult() const { return maRes; }

@@ -1133,10 +1137,17 @@ public:
            {
                typedef MatrixImplType::numeric_block_type block_type;

                size_t nIgnored = 0;
                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                {
                    if (mbIgnoreErrorValues && !rtl::math::isFinite(*it))
                    {
                        ++nIgnored;
                        continue;
                    }

                    if (mbFirst)
                    {
                        maOp(maRes.mfFirst, *it);
@@ -1147,7 +1158,7 @@ public:
                        maOp(maRes.mfRest, *it);
                    }
                }
                maRes.mnCount += node.size;
                maRes.mnCount += node.size - nIgnored;
            }
            break;
            case mdds::mtm::element_boolean:
@@ -1535,11 +1546,13 @@ class CalcMaxMinValue
{
    double mfVal;
    bool mbTextAsZero;
    bool mbIgnoreErrorValues;
    bool mbHasValue;
public:
    CalcMaxMinValue( bool bTextAsZero ) :
    CalcMaxMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) :
        mfVal(Op::init()),
        mbTextAsZero(bTextAsZero),
        mbIgnoreErrorValues(bIgnoreErrorValues),
        mbHasValue(false) {}

    double getValue() const { return mbHasValue ? mfVal : 0.0; }
@@ -1555,8 +1568,19 @@ public:

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                    mfVal = Op::compare(mfVal, *it);
                if (mbIgnoreErrorValues)
                {
                    for (; it != itEnd; ++it)
                    {
                        if (rtl::math::isFinite(*it))
                            mfVal = Op::compare(mfVal, *it);
                    }
                }
                else
                {
                    for (; it != itEnd; ++it)
                        mfVal = Op::compare(mfVal, *it);
                }

                mbHasValue = true;
            }
@@ -2069,28 +2093,28 @@ public:
namespace {

template<typename TOp>
ScMatrix::IterateResult GetValueWithCount(bool bTextAsZero, const MatrixImplType& maMat)
ScMatrix::IterateResult GetValueWithCount(bool bTextAsZero, bool bIgnoreErrorValues, const MatrixImplType& maMat)
{
    WalkElementBlocks<TOp> aFunc(bTextAsZero);
    WalkElementBlocks<TOp> aFunc(bTextAsZero, bIgnoreErrorValues);
    aFunc = maMat.walk(aFunc);
    return aFunc.getResult();
}

}

ScMatrix::IterateResult ScMatrixImpl::Sum(bool bTextAsZero) const
ScMatrix::IterateResult ScMatrixImpl::Sum(bool bTextAsZero, bool bIgnoreErrorValues) const
{
    return GetValueWithCount<sc::op::Sum>(bTextAsZero, maMat);
    return GetValueWithCount<sc::op::Sum>(bTextAsZero, bIgnoreErrorValues, maMat);
}

ScMatrix::IterateResult ScMatrixImpl::SumSquare(bool bTextAsZero) const
ScMatrix::IterateResult ScMatrixImpl::SumSquare(bool bTextAsZero, bool bIgnoreErrorValues) const
{
    return GetValueWithCount<sc::op::SumSquare>(bTextAsZero, maMat);
    return GetValueWithCount<sc::op::SumSquare>(bTextAsZero, bIgnoreErrorValues, maMat);
}

ScMatrix::IterateResult ScMatrixImpl::Product(bool bTextAsZero) const
ScMatrix::IterateResult ScMatrixImpl::Product(bool bTextAsZero, bool bIgnoreErrorValues) const
{
    return GetValueWithCount<sc::op::Product>(bTextAsZero, maMat);
    return GetValueWithCount<sc::op::Product>(bTextAsZero, bIgnoreErrorValues, maMat);
}

size_t ScMatrixImpl::Count(bool bCountStrings, bool bCountErrors) const
@@ -2114,16 +2138,16 @@ size_t ScMatrixImpl::MatchStringInColumns(const svl::SharedString& rStr, size_t 
    return aFunc.getMatching();
}

double ScMatrixImpl::GetMaxValue( bool bTextAsZero ) const
double ScMatrixImpl::GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues ) const
{
    CalcMaxMinValue<MaxOp> aFunc(bTextAsZero);
    CalcMaxMinValue<MaxOp> aFunc(bTextAsZero, bIgnoreErrorValues);
    aFunc = maMat.walk(aFunc);
    return aFunc.getValue();
}

double ScMatrixImpl::GetMinValue( bool bTextAsZero ) const
double ScMatrixImpl::GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) const
{
    CalcMaxMinValue<MinOp> aFunc(bTextAsZero);
    CalcMaxMinValue<MinOp> aFunc(bTextAsZero, bIgnoreErrorValues);
    aFunc = maMat.walk(aFunc);
    return aFunc.getValue();
}
@@ -3194,19 +3218,19 @@ double ScMatrix::Xor() const
    return pImpl->Xor();
}

ScMatrix::IterateResult ScMatrix::Sum(bool bTextAsZero) const
ScMatrix::IterateResult ScMatrix::Sum(bool bTextAsZero, bool bIgnoreErrorValues) const
{
    return pImpl->Sum(bTextAsZero);
    return pImpl->Sum(bTextAsZero, bIgnoreErrorValues);
}

ScMatrix::IterateResult ScMatrix::SumSquare(bool bTextAsZero) const
ScMatrix::IterateResult ScMatrix::SumSquare(bool bTextAsZero, bool bIgnoreErrorValues) const
{
    return pImpl->SumSquare(bTextAsZero);
    return pImpl->SumSquare(bTextAsZero, bIgnoreErrorValues);
}

ScMatrix::IterateResult ScMatrix::Product(bool bTextAsZero) const
ScMatrix::IterateResult ScMatrix::Product(bool bTextAsZero, bool bIgnoreErrorValues) const
{
    return pImpl->Product(bTextAsZero);
    return pImpl->Product(bTextAsZero, bIgnoreErrorValues);
}

size_t ScMatrix::Count(bool bCountStrings, bool bCountErrors) const
@@ -3224,14 +3248,14 @@ size_t ScMatrix::MatchStringInColumns(const svl::SharedString& rStr, size_t nCol
    return pImpl->MatchStringInColumns(rStr, nCol1, nCol2);
}

double ScMatrix::GetMaxValue( bool bTextAsZero ) const
double ScMatrix::GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues ) const
{
    return pImpl->GetMaxValue(bTextAsZero);
    return pImpl->GetMaxValue(bTextAsZero, bIgnoreErrorValues);
}

double ScMatrix::GetMinValue( bool bTextAsZero ) const
double ScMatrix::GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) const
{
    return pImpl->GetMinValue(bTextAsZero);
    return pImpl->GetMinValue(bTextAsZero, bIgnoreErrorValues);
}

double ScMatrix::GetGcd() const