Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  scmatrix.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <scmatrix.hxx>
#include <global.hxx>
#include <address.hxx>
#include <formula/errorcodes.hxx>
#include <interpre.hxx>
#include <mtvelements.hxx>
#include <compare.hxx>
#include <matrixoperators.hxx>
#include <math.hxx>
#include <jumpmatrix.hxx>

#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <svl/sharedstring.hxx>
#include <rtl/math.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>

#include <memory>
#include <mutex>
#include <utility>
#include <vector>
#include <limits>

#include <mdds/multi_type_matrix.hpp>
#include <mdds/multi_type_vector/types.hpp>

#if DEBUG_MATRIX
#include <iostream>
using std::cout;
using std::endl;
#endif

using ::std::pair;
using ::std::advance;

namespace {

/**
 * Custom string trait struct to tell mdds::multi_type_matrix about the
 * custom string type and how to handle blocks storing them.
 */

struct matrix_traits
{
    typedef sc::string_block string_element_block;
    typedef sc::uint16_block integer_element_block;
};

struct matrix_flag_traits
{
    typedef sc::string_block string_element_block;
    typedef mdds::mtv::uint8_element_block integer_element_block;
};

}

typedef mdds::multi_type_matrix<matrix_traits> MatrixImplType;
typedef mdds::multi_type_matrix<matrix_flag_traits> MatrixFlagImplType;

namespace {

double convertStringToValue( ScInterpreter* pErrorInterpreter, const OUString& rStr )
{
    if (pErrorInterpreter)
    {
        FormulaError nError = FormulaError::NONE;
        SvNumFormatType nCurFmtType = SvNumFormatType::ALL;
        double fValue = pErrorInterpreter->ConvertStringToValue( rStr, nError, nCurFmtType);
        if (nError != FormulaError::NONE)
        {
            pErrorInterpreter->SetError( nError);
            return CreateDoubleError( nError);
        }
        return fValue;
    }
    return CreateDoubleError( FormulaError::NoValue);
}

struct ElemEqualZero
{
    double operator() (double val) const
    {
        if (!std::isfinite(val))
            return val;
        return val == 0.0 ? 1.0 : 0.0;
    }
};

struct ElemNotEqualZero
{
    double operator() (double val) const
    {
        if (!std::isfinite(val))
            return val;
        return val != 0.0 ? 1.0 : 0.0;
    }
};

struct ElemGreaterZero
{
    double operator() (double val) const
    {
        if (!std::isfinite(val))
            return val;
        return val > 0.0 ? 1.0 : 0.0;
    }
};

struct ElemLessZero
{
    double operator() (double val) const
    {
        if (!std::isfinite(val))
            return val;
        return val < 0.0 ? 1.0 : 0.0;
    }
};

struct ElemGreaterEqualZero
{
    double operator() (double val) const
    {
        if (!std::isfinite(val))
            return val;
        return val >= 0.0 ? 1.0 : 0.0;
    }
};

struct ElemLessEqualZero
{
    double operator() (double val) const
    {
        if (!std::isfinite(val))
            return val;
        return val <= 0.0 ? 1.0 : 0.0;
    }
};

template<typename Comp>
class CompareMatrixElemFunc
{
    static Comp maComp;

    std::vector<double> maNewMatValues;     // double instead of bool to transport error values
    size_t mnRow;
    size_t mnCol;
public:
    CompareMatrixElemFunc( size_t nRow, size_t nCol ) : mnRow(nRow), mnCol(nCol)
    {
        maNewMatValues.reserve(nRow*nCol);
    }

    CompareMatrixElemFunc( const CompareMatrixElemFunc& ) = delete;
    CompareMatrixElemFunc& operator= ( const CompareMatrixElemFunc& ) = delete;

    CompareMatrixElemFunc( CompareMatrixElemFunc&& ) = default;
    CompareMatrixElemFunc& operator= ( CompareMatrixElemFunc&& ) = default;

    void operator() (const MatrixImplType::element_block_node_type& node)
    {
        switch (node.type)
        {
            case mdds::mtm::element_numeric:
            {
                typedef MatrixImplType::numeric_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                {
                    double fVal = *it;
                    maNewMatValues.push_back(maComp(fVal));
                }
            }
            break;
            case mdds::mtm::element_boolean:
            {
                typedef MatrixImplType::boolean_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                {
                    double fVal = *it ? 1.0 : 0.0;
                    maNewMatValues.push_back(maComp(fVal));
                }
            }
            break;
            case mdds::mtm::element_string:
            case mdds::mtm::element_empty:
            default:
                // Fill it with false.
                maNewMatValues.resize(maNewMatValues.size() + node.size, 0.0);
        }
    }

    void swap( MatrixImplType& rMat )
    {
        MatrixImplType aNewMat(mnRow, mnCol, maNewMatValues.begin(), maNewMatValues.end());
        rMat.swap(aNewMat);
    }
};

template<typename Comp>
Comp CompareMatrixElemFunc<Comp>::maComp;

}

typedef uint8_t TMatFlag;
const TMatFlag SC_MATFLAG_EMPTYRESULT = 1;
const TMatFlag SC_MATFLAG_EMPTYPATH   = 2;

class ScMatrixImpl
{
    MatrixImplType maMat;
    MatrixFlagImplType maMatFlag;
    ScInterpreter* pErrorInterpreter;

public:
    ScMatrixImpl(const ScMatrixImpl&) = delete;
    const ScMatrixImpl& operator=(const ScMatrixImpl&) = delete;

    ScMatrixImpl(SCSIZE nC, SCSIZE nR);
    ScMatrixImpl(SCSIZE nC, SCSIZE nR, double fInitVal);

    ScMatrixImpl( size_t nC, size_t nR, const std::vector<double>& rInitVals );

    ~ScMatrixImpl();

    void Clear();
    void Resize(SCSIZE nC, SCSIZE nR);
    void Resize(SCSIZE nC, SCSIZE nR, double fVal);
    void SetErrorInterpreter( ScInterpreter* p);
    ScInterpreter* GetErrorInterpreter() const { return pErrorInterpreter; }

    void GetDimensions( SCSIZE& rC, SCSIZE& rR) const;
    SCSIZE GetElementCount() const;
    bool ValidColRow( SCSIZE nC, SCSIZE nR) const;
    bool ValidColRowReplicated( SCSIZE & rC, SCSIZE & rR ) const;
    bool ValidColRowOrReplicated( SCSIZE & rC, SCSIZE & rR ) const;
    void SetErrorAtInterpreter( FormulaError nError ) const;

    void PutDouble(double fVal, SCSIZE nC, SCSIZE nR);
    void PutDouble( double fVal, SCSIZE nIndex);
    void PutDoubleTrans( double fVal, SCSIZE nIndex);
    void PutDouble(const double* pArray, size_t nLen, SCSIZE nC, SCSIZE nR);

    void PutString(const svl::SharedString& rStr, SCSIZE nC, SCSIZE nR);
    void PutString(const svl::SharedString& rStr, SCSIZE nIndex);
    void PutStringTrans(const svl::SharedString& rStr, SCSIZE nIndex);
    void PutString(const svl::SharedString* pArray, size_t nLen, SCSIZE nC, SCSIZE nR);

    void PutEmpty(SCSIZE nC, SCSIZE nR);
    void PutEmpty(SCSIZE nIndex);
    void PutEmptyTrans(SCSIZE nIndex);
    void PutEmptyPath(SCSIZE nC, SCSIZE nR);
    void PutError( FormulaError nErrorCode, SCSIZE nC, SCSIZE nR );
    void PutBoolean(bool bVal, SCSIZE nC, SCSIZE nR);
    FormulaError GetError( SCSIZE nC, SCSIZE nR) const;
    double GetDouble(SCSIZE nC, SCSIZE nR) const;
    double GetDouble( SCSIZE nIndex) const;
    double GetDoubleWithStringConversion(SCSIZE nC, SCSIZE nR) const;
    svl::SharedString GetString(SCSIZE nC, SCSIZE nR) const;
    svl::SharedString GetString( SCSIZE nIndex) const;
    svl::SharedString GetString( ScInterpreterContext& rContext, SCSIZE nC, SCSIZE nR) const;
    ScMatrixValue Get(SCSIZE nC, SCSIZE nR) const;
    bool IsStringOrEmpty( SCSIZE nIndex ) const;
    bool IsStringOrEmpty( SCSIZE nC, SCSIZE nR ) const;
    bool IsEmpty( SCSIZE nC, SCSIZE nR ) const;
    bool IsEmptyCell( SCSIZE nC, SCSIZE nR ) const;
    bool IsEmptyResult( SCSIZE nC, SCSIZE nR ) const;
    bool IsEmptyPath( SCSIZE nC, SCSIZE nR ) const;
    bool IsValue( SCSIZE nIndex ) const;
    bool IsValue( SCSIZE nC, SCSIZE nR ) const;
    bool IsValueOrEmpty( SCSIZE nC, SCSIZE nR ) const;
    bool IsBoolean( SCSIZE nC, SCSIZE nR ) const;
    bool IsNumeric() const;

    void MatCopy(ScMatrixImpl& mRes) const;
    void MatTrans(ScMatrixImpl& mRes) const;
    void FillDouble( double fVal, SCSIZE nC1, SCSIZE nR1, SCSIZE nC2, SCSIZE nR2 );
    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();
    void CompareLess();
    void CompareGreater();
    void CompareLessEqual();
    void CompareGreaterEqual();
    double And() const;
    double Or() const;
    double Xor() const;

    ScMatrix::KahanIterateResult Sum( bool bTextAsZero, bool bIgnoreErrorValues ) const;
    ScMatrix::KahanIterateResult SumSquare( bool bTextAsZero, bool bIgnoreErrorValues ) const;
    ScMatrix::DoubleIterateResult Product( bool bTextAsZero, bool bIgnoreErrorValues ) const;
    size_t Count(bool bCountStrings, bool bCountErrors, bool bIgnoreEmptyStrings) 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;
    void IfJump( ScJumpMatrix& rJumpMatrix, const short* pJump, short nJumpCount ) const ;

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

    ScMatrixRef CompareMatrix( sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) const;

    void GetDoubleArray( std::vector<double>& rArray, bool bEmptyAsZero ) const;
    void MergeDoubleArrayMultiply( std::vector<double>& rArray ) const;

    template<typename T>
    void ApplyOperation(T aOp, ScMatrixImpl& rMat);

    void ExecuteOperation(const std::pair<size_t, size_t>& rStartPos,
            const std::pair<size_t, size_t>& rEndPos, const ScMatrix::DoubleOpFunction& aDoubleFunc,
            const ScMatrix::BoolOpFunction& aBoolFunc, const ScMatrix::StringOpFunction& aStringFunc,
            const ScMatrix::EmptyOpFunction& aEmptyFunc) const;

    template<typename T, typename tRes>
    ScMatrix::IterateResultMultiple<tRes> ApplyCollectOperation(const std::vector<T>& aOp);

    void MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrixRef& xMat1, const ScMatrixRef& xMat2,
            ScInterpreterContext& rContext, svl::SharedStringPool& rPool);

    void ExecuteBinaryOp(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrix& rInputMat1, const ScMatrix& rInputMat2,
            ScInterpreter* pInterpreter, const ScMatrix::CalculateOpFunction& op);
    bool IsValueOrEmpty( const MatrixImplType::const_position_type & rPos ) const;
    double GetDouble( const MatrixImplType::const_position_type & rPos) const;
    FormulaError GetErrorIfNotString( const MatrixImplType::const_position_type & rPos ) const;
    bool IsValue( const MatrixImplType::const_position_type & rPos ) const;
    FormulaError GetError(const MatrixImplType::const_position_type & rPos) const;
    bool IsStringOrEmpty(const MatrixImplType::const_position_type & rPos) const;
    svl::SharedString GetString(const MatrixImplType::const_position_type& rPos) const;

#if DEBUG_MATRIX
    void Dump() const;
#endif

private:
    void CalcPosition(SCSIZE nIndex, SCSIZE& rC, SCSIZE& rR) const;
    void CalcTransPosition(SCSIZE nIndex, SCSIZE& rC, SCSIZE& rR) const;
};

static std::once_flag bElementsMaxFetched;
static std::atomic<size_t> nElementsMax;

/** The maximum number of elements a matrix or the pool may have at runtime.

    @param  nMemory
            If 0, the arbitrary limit of one matrix is returned.
            If >0, the given memory pool divided by the average size of a
            matrix element is returned, which is used to initialize
            nElementsMax.
 */

static size_t GetElementsMax( size_t nMemory )
{
    // Arbitrarily assuming 12 bytes per element, 8 bytes double plus
    // overhead. Stored as an array in an mdds container it's less, but for
    // strings or mixed matrix it can be much more...
    constexpr size_t nPerElem = 12;
    if (nMemory)
        return nMemory / nPerElem;

    // Arbitrarily assuming 1GB memory. Could be dynamic at some point.
    constexpr size_t nMemMax = 0x40000000;
    // With 1GB that's ~85M elements, or 85 whole columns.
    constexpr size_t nElemMax = nMemMax / nPerElem;
    // With MAXROWCOUNT==1048576 and 128 columns => 128M elements, 1.5GB
    constexpr size_t nArbitraryLimit = size_t(MAXROWCOUNT) * 128;
    // With the constant 1GB from above that's the actual value.
    return std::min(nElemMax, nArbitraryLimit);
}

ScMatrixImpl::ScMatrixImpl(SCSIZE nC, SCSIZE nR) :
    maMat(nR, nC), maMatFlag(nR, nC), pErrorInterpreter(nullptr)
{
    nElementsMax -= GetElementCount();
}

ScMatrixImpl::ScMatrixImpl(SCSIZE nC, SCSIZE nR, double fInitVal) :
    maMat(nR, nC, fInitVal), maMatFlag(nR, nC), pErrorInterpreter(nullptr)
{
    nElementsMax -= GetElementCount();
}

ScMatrixImpl::ScMatrixImpl( size_t nC, size_t nR, const std::vector<double>& rInitVals ) :
    maMat(nR, nC, rInitVals.begin(), rInitVals.end()), maMatFlag(nR, nC), pErrorInterpreter(nullptr)
{
    nElementsMax -= GetElementCount();
}

ScMatrixImpl::~ScMatrixImpl()
{
    nElementsMax += GetElementCount();
    suppress_fun_call_w_exception(Clear());
}

void ScMatrixImpl::Clear()
{
    suppress_fun_call_w_exception(maMat.clear());
    maMatFlag.clear();
}

void ScMatrixImpl::Resize(SCSIZE nC, SCSIZE nR)
{
    nElementsMax += GetElementCount();
    if (ScMatrix::IsSizeAllocatable( nC, nR))
    {
        maMat.resize(nR, nC);
        maMatFlag.resize(nR, nC);
    }
    else
    {
        // Invalid matrix size, allocate 1x1 matrix with error value.
        maMat.resize(1, 1, CreateDoubleError( FormulaError::MatrixSize));
        maMatFlag.resize(1, 1);
    }
    nElementsMax -= GetElementCount();
}

void ScMatrixImpl::Resize(SCSIZE nC, SCSIZE nR, double fVal)
{
    nElementsMax += GetElementCount();
    if (ScMatrix::IsSizeAllocatable( nC, nR))
    {
        maMat.resize(nR, nC, fVal);
        maMatFlag.resize(nR, nC);
    }
    else
    {
        // Invalid matrix size, allocate 1x1 matrix with error value.
        maMat.resize(1, 1, CreateDoubleError( FormulaError::StackOverflow));
        maMatFlag.resize(1, 1);
    }
    nElementsMax -= GetElementCount();
}

void ScMatrixImpl::SetErrorInterpreter( ScInterpreter* p)
{
    pErrorInterpreter = p;
}

void ScMatrixImpl::GetDimensions( SCSIZE& rC, SCSIZE& rR) const
{
    MatrixImplType::size_pair_type aSize = maMat.size();
    rR = aSize.row;
    rC = aSize.column;
}

SCSIZE ScMatrixImpl::GetElementCount() const
{
    MatrixImplType::size_pair_type aSize = maMat.size();
    return aSize.row * aSize.column;
}

bool ScMatrixImpl::ValidColRow( SCSIZE nC, SCSIZE nR) const
{
    MatrixImplType::size_pair_type aSize = maMat.size();
    return nR < aSize.row && nC < aSize.column;
}

bool ScMatrixImpl::ValidColRowReplicated( SCSIZE & rC, SCSIZE & rR ) const
{
    MatrixImplType::size_pair_type aSize = maMat.size();
    if (aSize.column == 1 && aSize.row == 1)
    {
        rC = 0;
        rR = 0;
        return true;
    }
    else if (aSize.column == 1 && rR < aSize.row)
    {
        // single column matrix.
        rC = 0;
        return true;
    }
    else if (aSize.row == 1 && rC < aSize.column)
    {
        // single row matrix.
        rR = 0;
        return true;
    }
    return false;
}

bool ScMatrixImpl::ValidColRowOrReplicated( SCSIZE & rC, SCSIZE & rR ) const
{
    return ValidColRow( rC, rR) || ValidColRowReplicated( rC, rR);
}

void ScMatrixImpl::SetErrorAtInterpreter( FormulaError nError ) const
{
    if ( pErrorInterpreter )
        pErrorInterpreter->SetError( nError);
}

void ScMatrixImpl::PutDouble(double fVal, SCSIZE nC, SCSIZE nR)
{
    if (ValidColRow( nC, nR))
        maMat.set(nR, nC, fVal);
    else
    {
        OSL_FAIL("ScMatrixImpl::PutDouble: dimension error");
    }
}

void ScMatrixImpl::PutDouble(const double* pArray, size_t nLen, SCSIZE nC, SCSIZE nR)
{
    if (ValidColRow( nC, nR))
        maMat.set(nR, nC, pArray, pArray + nLen);
    else
    {
        OSL_FAIL("ScMatrixImpl::PutDouble: dimension error");
    }
}

void ScMatrixImpl::PutDouble( double fVal, SCSIZE nIndex)
{
    SCSIZE nC, nR;
    CalcPosition(nIndex, nC, nR);
    PutDouble(fVal, nC, nR);
}

void ScMatrixImpl::PutDoubleTrans(double fVal, SCSIZE nIndex)
{
    SCSIZE nC, nR;
    CalcTransPosition(nIndex, nC, nR);
    PutDouble(fVal, nC, nR);
}

void ScMatrixImpl::PutString(const svl::SharedString& rStr, SCSIZE nC, SCSIZE nR)
{
    if (ValidColRow( nC, nR))
        maMat.set(nR, nC, rStr);
    else
    {
        OSL_FAIL("ScMatrixImpl::PutString: dimension error");
    }
}

void ScMatrixImpl::PutString(const svl::SharedString* pArray, size_t nLen, SCSIZE nC, SCSIZE nR)
{
    if (ValidColRow( nC, nR))
        maMat.set(nR, nC, pArray, pArray + nLen);
    else
    {
        OSL_FAIL("ScMatrixImpl::PutString: dimension error");
    }
}

void ScMatrixImpl::PutString(const svl::SharedString& rStr, SCSIZE nIndex)
{
    SCSIZE nC, nR;
    CalcPosition(nIndex, nC, nR);
    PutString(rStr, nC, nR);
}

void ScMatrixImpl::PutStringTrans(const svl::SharedString& rStr, SCSIZE nIndex)
{
    SCSIZE nC, nR;
    CalcTransPosition(nIndex, nC, nR);
    PutString(rStr, nC, nR);
}

void ScMatrixImpl::PutEmpty(SCSIZE nC, SCSIZE nR)
{
    if (ValidColRow( nC, nR))
    {
        maMat.set_empty(nR, nC);
        maMatFlag.set_empty(nR, nC);
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::PutEmpty: dimension error");
    }
}

void ScMatrixImpl::PutEmpty(SCSIZE nIndex)
{
    SCSIZE nC, nR;
    CalcPosition(nIndex, nC, nR);
    PutEmpty(nC, nR);
}

void ScMatrixImpl::PutEmptyTrans(SCSIZE nIndex)
{
    SCSIZE nC, nR;
    CalcTransPosition(nIndex, nC, nR);
    PutEmpty(nC, nR);
}

void ScMatrixImpl::PutEmptyPath(SCSIZE nC, SCSIZE nR)
{
    if (ValidColRow( nC, nR))
    {
        maMat.set_empty(nR, nC);
#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 && __cplusplus == 202002L
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#endif
        maMatFlag.set(nR, nC, SC_MATFLAG_EMPTYPATH);
#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 && __cplusplus == 202002L
#pragma GCC diagnostic pop
#endif
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::PutEmptyPath: dimension error");
    }
}

void ScMatrixImpl::PutError( FormulaError nErrorCode, SCSIZE nC, SCSIZE nR )
{
    maMat.set(nR, nC, CreateDoubleError(nErrorCode));
}

void ScMatrixImpl::PutBoolean(bool bVal, SCSIZE nC, SCSIZE nR)
{
    if (ValidColRow( nC, nR))
        maMat.set(nR, nC, bVal);
    else
    {
        OSL_FAIL("ScMatrixImpl::PutBoolean: dimension error");
    }
}

FormulaError ScMatrixImpl::GetError( SCSIZE nC, SCSIZE nR) const
{
    if (ValidColRowOrReplicated( nC, nR ))
    {
        double fVal = maMat.get_numeric(nR, nC);
        return GetDoubleErrorValue(fVal);
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::GetError: dimension error");
        return FormulaError::NoValue;
    }
}

double ScMatrixImpl::GetDouble(SCSIZE nC, SCSIZE nR) const
{
    if (ValidColRowOrReplicated( nC, nR ))
    {
        double fVal = maMat.get_numeric(nR, nC);
        if ( pErrorInterpreter )
        {
            FormulaError nError = GetDoubleErrorValue(fVal);
            if ( nError != FormulaError::NONE )
                SetErrorAtInterpreter( nError);
        }
        return fVal;
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::GetDouble: dimension error");
        return CreateDoubleError( FormulaError::NoValue);
    }
}

double ScMatrixImpl::GetDouble( SCSIZE nIndex) const
{
    SCSIZE nC, nR;
    CalcPosition(nIndex, nC, nR);
    return GetDouble(nC, nR);
}

double ScMatrixImpl::GetDoubleWithStringConversion(SCSIZE nC, SCSIZE nR) const
{
    ScMatrixValue aMatVal = Get(nC, nR);
    if (aMatVal.nType == ScMatValType::String)
        return convertStringToValue( pErrorInterpreter, aMatVal.aStr.getString());
    return aMatVal.fVal;
}

svl::SharedString ScMatrixImpl::GetString(SCSIZE nC, SCSIZE nR) const
{
    if (ValidColRowOrReplicated( nC, nR ))
    {
        return GetString(maMat.position(nR, nC));
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::GetString: dimension error");
    }
    return svl::SharedString::getEmptyString();
}

svl::SharedString ScMatrixImpl::GetString(const MatrixImplType::const_position_type& rPos) const
{
    double fErr = 0.0;
    switch (maMat.get_type(rPos))
    {
        case mdds::mtm::element_string:
            return maMat.get_string(rPos);
        case mdds::mtm::element_empty:
            return svl::SharedString::getEmptyString();
        case mdds::mtm::element_numeric:
        case mdds::mtm::element_boolean:
            fErr = maMat.get_numeric(rPos);
            [[fallthrough]];
        default:
            OSL_FAIL("ScMatrixImpl::GetString: access error, no string");
    }
    SetErrorAtInterpreter(GetDoubleErrorValue(fErr));
    return svl::SharedString::getEmptyString();
}

svl::SharedString ScMatrixImpl::GetString( SCSIZE nIndex) const
{
    SCSIZE nC, nR;
    CalcPosition(nIndex, nC, nR);
    return GetString(nC, nR);
}

svl::SharedString ScMatrixImpl::GetString( ScInterpreterContext& rContext, SCSIZE nC, SCSIZE nR) const
{
    if (!ValidColRowOrReplicated( nC, nR ))
    {
        OSL_FAIL("ScMatrixImpl::GetString: dimension error");
        return svl::SharedString::getEmptyString();
    }

    double fVal = 0.0;
    MatrixImplType::const_position_type aPos = maMat.position(nR, nC);
    switch (maMat.get_type(aPos))
    {
        case mdds::mtm::element_string:
            return maMat.get_string(aPos);
        case mdds::mtm::element_empty:
        {
            if (maMatFlag.get<uint8_t>(nR, nC) != SC_MATFLAG_EMPTYPATH)
                // not an empty path.
                return svl::SharedString::getEmptyString();

            // result of empty FALSE jump path
            sal_uInt32 nKey = rContext.NFGetStandardFormat( SvNumFormatType::LOGICAL,
                    ScGlobal::eLnge);
            OUString aStr;
            const Color* pColor = nullptr;
            rContext.NFGetOutputString( 0.0, nKey, aStr, &pColor);
            return svl::SharedString( aStr);    // string not interned
        }
        case mdds::mtm::element_numeric:
        case mdds::mtm::element_boolean:
            fVal = maMat.get_numeric(aPos);
        break;
        default:
            ;
    }

    FormulaError nError = GetDoubleErrorValue(fVal);
    if (nError != FormulaError::NONE)
    {
        SetErrorAtInterpreter( nError);
        return svl::SharedString( ScGlobal::GetErrorString( nError));   // string not interned
    }

    sal_uInt32 nKey = rContext.NFGetStandardFormat( SvNumFormatType::NUMBER,
            ScGlobal::eLnge);
    return svl::SharedString(rContext.NFGetInputLineString( fVal, nKey )); // string not interned
}

ScMatrixValue ScMatrixImpl::Get(SCSIZE nC, SCSIZE nR) const
{
    ScMatrixValue aVal;
    if (ValidColRowOrReplicated(nC, nR))
    {
        MatrixImplType::const_position_type aPos = maMat.position(nR, nC);
        mdds::mtm::element_t eType = maMat.get_type(aPos);
        switch (eType)
        {
            case mdds::mtm::element_boolean:
                aVal.nType = ScMatValType::Boolean;
                aVal.fVal = double(maMat.get_boolean(aPos));
            break;
            case mdds::mtm::element_numeric:
                aVal.nType = ScMatValType::Value;
                aVal.fVal = maMat.get_numeric(aPos);
            break;
            case mdds::mtm::element_string:
                aVal.nType = ScMatValType::String;
                aVal.aStr = maMat.get_string(aPos);
            break;
            case mdds::mtm::element_empty:
                /* TODO: do we need to pass the differentiation of 'empty' and
                 * 'empty result' to the outer world anywhere? */

                switch (maMatFlag.get_type(nR, nC))
                {
                    case mdds::mtm::element_empty:
                        aVal.nType = ScMatValType::Empty;
                    break;
                    case mdds::mtm::element_integer:
                        aVal.nType = maMatFlag.get<uint8_t>(nR, nC)
                            == SC_MATFLAG_EMPTYPATH ? ScMatValType::EmptyPath : ScMatValType::Empty;
                    break;
                    default:
                        assert(false);
                }
                aVal.fVal = 0.0;
            break;
            default:
                ;
        }
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::Get: dimension error");
    }
    return aVal;
}

bool ScMatrixImpl::IsStringOrEmpty( SCSIZE nIndex ) const
{
    SCSIZE nC, nR;
    CalcPosition(nIndex, nC, nR);
    return IsStringOrEmpty(nC, nR);
}

bool ScMatrixImpl::IsStringOrEmpty( SCSIZE nC, SCSIZE nR ) const
{
    if (!ValidColRowOrReplicated( nC, nR ))
        return false;

    switch (maMat.get_type(nR, nC))
    {
        case mdds::mtm::element_empty:
        case mdds::mtm::element_string:
            return true;
        default:
            ;
    }
    return false;
}

bool ScMatrixImpl::IsEmpty( SCSIZE nC, SCSIZE nR ) const
{
    if (!ValidColRowOrReplicated( nC, nR ))
        return false;

    // Flag must indicate an 'empty' or 'empty cell' or 'empty result' element,
    // but not an 'empty path' element.
    return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
        maMatFlag.get_integer(nR, nC) != SC_MATFLAG_EMPTYPATH;
}

bool ScMatrixImpl::IsEmptyCell( SCSIZE nC, SCSIZE nR ) const
{
    if (!ValidColRowOrReplicated( nC, nR ))
        return false;

    // Flag must indicate an 'empty cell' element instead of an
    // 'empty' or 'empty result' or 'empty path' element.
    return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
        maMatFlag.get_type(nR, nC) == mdds::mtm::element_empty;
}

bool ScMatrixImpl::IsEmptyResult( SCSIZE nC, SCSIZE nR ) const
{
    if (!ValidColRowOrReplicated( nC, nR ))
        return false;

    // Flag must indicate an 'empty result' element instead of an
    // 'empty' or 'empty cell' or 'empty path' element.
    return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
        maMatFlag.get_integer(nR, nC) == SC_MATFLAG_EMPTYRESULT;
}

bool ScMatrixImpl::IsEmptyPath( SCSIZE nC, SCSIZE nR ) const
{
    // Flag must indicate an 'empty path' element.
    if (ValidColRowOrReplicated( nC, nR ))
        return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
            maMatFlag.get_integer(nR, nC) == SC_MATFLAG_EMPTYPATH;
    else
        return true;
}

bool ScMatrixImpl::IsValue( SCSIZE nIndex ) const
{
    SCSIZE nC, nR;
    CalcPosition(nIndex, nC, nR);
    return IsValue(nC, nR);
}

bool ScMatrixImpl::IsValue( SCSIZE nC, SCSIZE nR ) const
{
    if (!ValidColRowOrReplicated( nC, nR ))
        return false;

    switch (maMat.get_type(nR, nC))
    {
        case mdds::mtm::element_boolean:
        case mdds::mtm::element_numeric:
            return true;
        default:
            ;
    }
    return false;
}

bool ScMatrixImpl::IsValueOrEmpty( SCSIZE nC, SCSIZE nR ) const
{
    if (!ValidColRowOrReplicated( nC, nR ))
        return false;

    switch (maMat.get_type(nR, nC))
    {
        case mdds::mtm::element_boolean:
        case mdds::mtm::element_numeric:
        case mdds::mtm::element_empty:
            return true;
        default:
            ;
    }
    return false;
}

bool ScMatrixImpl::IsBoolean( SCSIZE nC, SCSIZE nR ) const
{
    if (!ValidColRowOrReplicated( nC, nR ))
        return false;

    return maMat.get_type(nR, nC) == mdds::mtm::element_boolean;
}

bool ScMatrixImpl::IsNumeric() const
{
    return maMat.numeric();
}

void ScMatrixImpl::MatCopy(ScMatrixImpl& mRes) const
{
    if (maMat.size().row > mRes.maMat.size().row || maMat.size().column > mRes.maMat.size().column)
    {
        // destination matrix is not large enough.
        OSL_FAIL("ScMatrixImpl::MatCopy: dimension error");
        return;
    }

    mRes.maMat.copy(maMat);
}

void ScMatrixImpl::MatTrans(ScMatrixImpl& mRes) const
{
    mRes.maMat = maMat;
    mRes.maMat.transpose();
}

void ScMatrixImpl::FillDouble( double fVal, SCSIZE nC1, SCSIZE nR1, SCSIZE nC2, SCSIZE nR2 )
{
    if (ValidColRow( nC1, nR1) && ValidColRow( nC2, nR2))
    {
        for (SCSIZE j = nC1; j <= nC2; ++j)
        {
            // Passing value array is much faster.
            std::vector<double> aVals(nR2-nR1+1, fVal);
            maMat.set(nR1, j, aVals.begin(), aVals.end());
        }
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::FillDouble: dimension error");
    }
}

void ScMatrixImpl::PutDoubleVector( const ::std::vector< double > & rVec, SCSIZE nC, SCSIZE nR )
{
    if (!rVec.empty() && ValidColRow( nC, nR) && ValidColRow( nC, nR + rVec.size() - 1))
    {
        maMat.set(nR, nC, rVec.begin(), rVec.end());
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::PutDoubleVector: dimension error");
    }
}

void ScMatrixImpl::PutStringVector( const ::std::vector< svl::SharedString > & rVec, SCSIZE nC, SCSIZE nR )
{
    if (!rVec.empty() && ValidColRow( nC, nR) && ValidColRow( nC, nR + rVec.size() - 1))
    {
        maMat.set(nR, nC, rVec.begin(), rVec.end());
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::PutStringVector: dimension error");
    }
}

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);
        // Flag to indicate that this is 'empty', not 'empty result' or 'empty path'.
        maMatFlag.set_empty(nR, nC, nCount);
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::PutEmptyVector: dimension error");
    }
}

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<uint8_t> 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);
        // Flag to indicate 'empty path'.
        std::vector<uint8_t> aVals(nCount, SC_MATFLAG_EMPTYPATH);
        maMatFlag.set(nR, nC, aVals.begin(), aVals.end());
    }
    else
    {
        OSL_FAIL("ScMatrixImpl::PutEmptyPathVector: dimension error");
    }
}

void ScMatrixImpl::CompareEqual()
{
    MatrixImplType::size_pair_type aSize = maMat.size();
    CompareMatrixElemFunc<ElemEqualZero> aFunc(aSize.row, aSize.column);
    aFunc = maMat.walk(std::move(aFunc));
    aFunc.swap(maMat);
}

void ScMatrixImpl::CompareNotEqual()
{
    MatrixImplType::size_pair_type aSize = maMat.size();
    CompareMatrixElemFunc<ElemNotEqualZero> aFunc(aSize.row, aSize.column);
    aFunc = maMat.walk(std::move(aFunc));
    aFunc.swap(maMat);
}

void ScMatrixImpl::CompareLess()
{
    MatrixImplType::size_pair_type aSize = maMat.size();
    CompareMatrixElemFunc<ElemLessZero> aFunc(aSize.row, aSize.column);
    aFunc = maMat.walk(std::move(aFunc));
    aFunc.swap(maMat);
}

void ScMatrixImpl::CompareGreater()
{
    MatrixImplType::size_pair_type aSize = maMat.size();
    CompareMatrixElemFunc<ElemGreaterZero> aFunc(aSize.row, aSize.column);
    aFunc = maMat.walk(std::move(aFunc));
    aFunc.swap(maMat);
}

void ScMatrixImpl::CompareLessEqual()
{
    MatrixImplType::size_pair_type aSize = maMat.size();
    CompareMatrixElemFunc<ElemLessEqualZero> aFunc(aSize.row, aSize.column);
    aFunc = maMat.walk(std::move(aFunc));
    aFunc.swap(maMat);
}

void ScMatrixImpl::CompareGreaterEqual()
{
    MatrixImplType::size_pair_type aSize = maMat.size();
    CompareMatrixElemFunc<ElemGreaterEqualZero> aFunc(aSize.row, aSize.column);
    aFunc = maMat.walk(std::move(aFunc));
    aFunc.swap(maMat);
}

namespace {

struct AndEvaluator
{
    bool mbResult;
    void operate(double fVal) { mbResult &= (fVal != 0.0); }
    bool result() const { return mbResult; }
    AndEvaluator() : mbResult(true) {}
};

struct OrEvaluator
{
    bool mbResult;
    void operate(double fVal) { mbResult |= (fVal != 0.0); }
    bool result() const { return mbResult; }
    OrEvaluator() : mbResult(false) {}
};

struct XorEvaluator
{
    bool mbResult;
    void operate(double fVal) { mbResult ^= (fVal != 0.0); }
    bool result() const { return mbResult; }
    XorEvaluator() : mbResult(false) {}
};

// Do not short circuit logical operations, in case there are error values
// these need to be propagated even if the result was determined earlier.
template <typename Evaluator>
double EvalMatrix(const MatrixImplType& rMat)
{
    Evaluator aEval;
    size_t nRows = rMat.size().row, nCols = rMat.size().column;

    MatrixImplType::const_position_type aPos = rMat.position(0, 0);
    for (size_t nC = 0; nC < nCols; ++nC)
    {
        for (size_t nR = 0; nR < nRows; ++nR)
        {
            mdds::mtm::element_t eType = rMat.get_type(aPos);
            if (eType != mdds::mtm::element_numeric && eType != mdds::mtm::element_boolean)
                // assuming a CompareMat this is an error
                return CreateDoubleError(FormulaError::IllegalArgument);

            double fVal = rMat.get_numeric(aPos);
            if (!std::isfinite(fVal))
                // DoubleError
                return fVal;

            aEval.operate(fVal);

            aPos = MatrixImplType::next_position(aPos);
        }
    }

    return aEval.result();
}

}

double ScMatrixImpl::And() const
{
    // All elements must be of value type.
    // True only if all the elements have non-zero values.
    return EvalMatrix<AndEvaluator>(maMat);
}

double ScMatrixImpl::Or() const
{
    // All elements must be of value type.
    // True if at least one element has a non-zero value.
    return EvalMatrix<OrEvaluator>(maMat);
}

double ScMatrixImpl::Xor() const
{
    // All elements must be of value type.
    // True if an odd number of elements have a non-zero value.
    return EvalMatrix<XorEvaluator>(maMat);
}

namespace {

template<typename Op, typename tRes>
class WalkElementBlocks
{
    Op maOp;
    ScMatrix::IterateResult<tRes> maRes;
    bool mbTextAsZero:1;
    bool mbIgnoreErrorValues:1;
public:
    WalkElementBlocks(bool bTextAsZero, bool bIgnoreErrorValues) :
        maRes(Op::InitVal, 0),
        mbTextAsZero(bTextAsZero), mbIgnoreErrorValues(bIgnoreErrorValues)
    {}

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

    void operator() (const MatrixImplType::element_block_node_type& node)
    {
        switch (node.type)
        {
            case mdds::mtm::element_numeric:
            {
                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 && !std::isfinite(*it))
                    {
                        ++nIgnored;
                        continue;
                    }
                    maOp(maRes.maAccumulator, *it);
                }
                maRes.mnCount += node.size - nIgnored;
            }
            break;
            case mdds::mtm::element_boolean:
            {
                typedef MatrixImplType::boolean_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                {
                    maOp(maRes.maAccumulator, *it);
                }
                maRes.mnCount += node.size;
            }
            break;
            case mdds::mtm::element_string:
                if (mbTextAsZero)
                    maRes.mnCount += node.size;
            break;
            case mdds::mtm::element_empty:
            default:
                ;
        }
    }
};

template<typename Op, typename tRes>
class WalkElementBlocksMultipleValues
{
    const std::vector<Op>* mpOp;
    ScMatrix::IterateResultMultiple<tRes> maRes;
public:
    WalkElementBlocksMultipleValues(const std::vector<Op>& aOp) :
        mpOp(&aOp), maRes(0)
    {
        for (const auto& rpOp : *mpOp)
            maRes.maAccumulator.emplace_back(rpOp.mInitVal);
    }

    WalkElementBlocksMultipleValues( const WalkElementBlocksMultipleValues& ) = delete;
    WalkElementBlocksMultipleValues& operator= ( const WalkElementBlocksMultipleValues& ) = delete;

    WalkElementBlocksMultipleValues(WalkElementBlocksMultipleValues&& r) noexcept
    : mpOp(r.mpOp), maRes(r.maRes.mnCount)
    {
        maRes.maAccumulator = std::move(r.maRes.maAccumulator);
    }

    WalkElementBlocksMultipleValues& operator=(WalkElementBlocksMultipleValues&& r) noexcept
    {
        mpOp = r.mpOp;
        maRes.maAccumulator = std::move(r.maRes.maAccumulator);
        maRes.mnCount = r.maRes.mnCount;
        return *this;
    }

    const ScMatrix::IterateResultMultiple<tRes>& getResult() const { return maRes; }

    void operator() (const MatrixImplType::element_block_node_type& node)
    {
        switch (node.type)
        {
            case mdds::mtm::element_numeric:
            {
                typedef MatrixImplType::numeric_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                {
                    for (size_t i = 0u; i < mpOp->size(); ++i)
                        (*mpOp)[i](maRes.maAccumulator[i], *it);
                }
                maRes.mnCount += node.size;
            }
            break;
            case mdds::mtm::element_boolean:
            {
                typedef MatrixImplType::boolean_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                {
                    for (size_t i = 0u; i < mpOp->size(); ++i)
                        (*mpOp)[i](maRes.maAccumulator[i], *it);
                }
                maRes.mnCount += node.size;
            }
            break;
            case mdds::mtm::element_string:
            case mdds::mtm::element_empty:
            default:
                ;
        }
    }
};

class CountElements
{
    size_t mnCount;
    bool mbCountString;
    bool mbCountErrors;
    bool mbIgnoreEmptyStrings;
public:
    explicit CountElements(bool bCountString, bool bCountErrors, bool bIgnoreEmptyStrings) :
        mnCount(0), mbCountString(bCountString), mbCountErrors(bCountErrors),
        mbIgnoreEmptyStrings(bIgnoreEmptyStrings) {}

    size_t getCount() const { return mnCount; }

    void operator() (const MatrixImplType::element_block_node_type& node)
    {
        switch (node.type)
        {
            case mdds::mtm::element_numeric:
                mnCount += node.size;
                if (!mbCountErrors)
                {
                    typedef MatrixImplType::numeric_block_type block_type;

                    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 (!std::isfinite(*it))
                            --mnCount;
                    }
                }
            break;
            case mdds::mtm::element_boolean:
                mnCount += node.size;
            break;
            case mdds::mtm::element_string:
                if (mbCountString)
                {
                    mnCount += node.size;
                    if (mbIgnoreEmptyStrings)
                    {
                        typedef MatrixImplType::string_block_type block_type;

                        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 (it->isEmpty())
                                --mnCount;
                        }
                    }
                }
            break;
            case mdds::mtm::element_empty:
            default:
                ;
        }
    }
};

const size_t ResultNotSet = std::numeric_limits<size_t>::max();

template<typename Type>
class WalkAndMatchElements
{
    Type maMatchValue;
    size_t mnStartIndex;
    size_t mnStopIndex;
    size_t mnResult;
    size_t mnIndex;

public:
    WalkAndMatchElements(Type aMatchValue, const MatrixImplType::size_pair_type& aSize, size_t nCol1, size_t nCol2) :
        maMatchValue(std::move(aMatchValue)),
        mnStartIndex( nCol1 * aSize.row ),
        mnStopIndex( (nCol2 + 1) * aSize.row ),
        mnResult(ResultNotSet),
        mnIndex(0)
    {
        assert( nCol1 < aSize.column && nCol2 < aSize.column);
    }

    size_t getMatching() const { return mnResult; }

    size_t getRemainingCount() const
    {
        return mnIndex < mnStopIndex ? mnStopIndex - mnIndex : 0;
    }

    size_t compare(const MatrixImplType::element_block_node_type& node) const;

    void operator() (const MatrixImplType::element_block_node_type& node)
    {
        // early exit if match already found
        if (mnResult != ResultNotSet)
            return;

        // limit lookup to the requested columns
        if (mnStartIndex <= mnIndex && getRemainingCount() > 0)
        {
            mnResult = compare(node);
        }

        mnIndex += node.size;
    }
};

template<>
size_t WalkAndMatchElements<double>::compare(const MatrixImplType::element_block_node_type& node) const
{
    size_t nCount = 0;
    switch (node.type)
    {
        case mdds::mtm::element_numeric:
        {
            typedef MatrixImplType::numeric_block_type block_type;

            block_type::const_iterator it = block_type::begin(*node.data);
            block_type::const_iterator itEnd = block_type::end(*node.data);
            const size_t nRemaining = getRemainingCount();
            for (; it != itEnd && nCount < nRemaining; ++it, ++nCount)
            {
                if (*it == maMatchValue)
                {
                    return mnIndex + nCount;
                }
            }
            break;
        }
        case mdds::mtm::element_boolean:
        {
            typedef MatrixImplType::boolean_block_type block_type;

            block_type::const_iterator it = block_type::begin(*node.data);
            block_type::const_iterator itEnd = block_type::end(*node.data);
            const size_t nRemaining = getRemainingCount();
            for (; it != itEnd && nCount < nRemaining; ++it, ++nCount)
            {
                if (int(*it) == maMatchValue)
                {
                    return mnIndex + nCount;
                }
            }
            break;
        }
        break;
        case mdds::mtm::element_string:
        case mdds::mtm::element_empty:
        default:
            ;
    }
    return ResultNotSet;
}

template<>
size_t WalkAndMatchElements<svl::SharedString>::compare(const MatrixImplType::element_block_node_type& node) const
{
    switch (node.type)
    {
        case mdds::mtm::element_string:
        {
            size_t nCount = 0;
            typedef MatrixImplType::string_block_type block_type;

            block_type::const_iterator it = block_type::begin(*node.data);
            block_type::const_iterator itEnd = block_type::end(*node.data);
            const size_t nRemaining = getRemainingCount();
            for (; it != itEnd && nCount < nRemaining; ++it, ++nCount)
            {
                if (it->getDataIgnoreCase() == maMatchValue.getDataIgnoreCase())
                {
                    return mnIndex + nCount;
                }
            }
            break;
        }
        case mdds::mtm::element_boolean:
        case mdds::mtm::element_numeric:
        case mdds::mtm::element_empty:
        default:
            ;
    }
    return ResultNotSet;
}

struct MaxOp
{
    static double init() { return -std::numeric_limits<double>::max(); }
    static double compare(double left, double right)
    {
        if (!std::isfinite(left))
            return left;
        if (!std::isfinite(right))
            return right;
        return std::max(left, right);
    }

    static double boolValue(
        MatrixImplType::boolean_block_type::const_iterator it,
        const MatrixImplType::boolean_block_type::const_iterator& itEnd)
    {
        // If the array has at least one true value, the maximum value is 1.
        it = std::find(it, itEnd, true);
        return it == itEnd ? 0.0 : 1.0;
    }
};

struct MinOp
{
    static double init() { return std::numeric_limits<double>::max(); }
    static double compare(double left, double right)
    {
        if (!std::isfinite(left))
            return left;
        if (!std::isfinite(right))
            return right;
        return std::min(left, right);
    }

    static double boolValue(
        MatrixImplType::boolean_block_type::const_iterator it,
        const MatrixImplType::boolean_block_type::const_iterator& itEnd)
    {
        // If the array has at least one false value, the minimum value is 0.
        it = std::find(it, itEnd, false);
        return it == itEnd ? 1.0 : 0.0;
    }
};

struct Lcm
{
    static double init() { return 1.0; }
    static double calculate(double fx,double fy)
    {
        return (fx*fy)/ScInterpreter::ScGetGCD(fx,fy);
    }

    static double boolValue(
            MatrixImplType::boolean_block_type::const_iterator it,
            const MatrixImplType::boolean_block_type::const_iterator& itEnd)
    {
        // If the array has at least one false value, the minimum value is 0.
        it = std::find(it, itEnd, false);
        return it == itEnd ? 1.0 : 0.0;
    }
};

struct Gcd
{
    static double init() { return 0.0; }
    static double calculate(double fx,double fy)
    {
        return ScInterpreter::ScGetGCD(fx,fy);
    }

    static double boolValue(
            MatrixImplType::boolean_block_type::const_iterator it,
            const MatrixImplType::boolean_block_type::const_iterator& itEnd)
    {
        // If the array has at least one true value, the gcdResult is 1.
        it = std::find(it, itEnd, true);
        return it == itEnd ? 0.0 : 1.0;
    }
};

template<typename Op>
class CalcMaxMinValue
{
    double mfVal;
    bool mbTextAsZero;
    bool mbIgnoreErrorValues;
    bool mbHasValue;
public:
    CalcMaxMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) :
        mfVal(Op::init()),
        mbTextAsZero(bTextAsZero),
        mbIgnoreErrorValues(bIgnoreErrorValues),
        mbHasValue(false) {}

    double getValue() const { return mbHasValue ? mfVal : 0.0; }

    void operator() (const MatrixImplType::element_block_node_type& node)
    {

        switch (node.type)
        {
            case mdds::mtm::element_numeric:
            {
                typedef MatrixImplType::numeric_block_type block_type;

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

                mbHasValue = true;
            }
            break;
            case mdds::mtm::element_boolean:
            {
                typedef MatrixImplType::boolean_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                double fVal = Op::boolValue(it, itEnd);
                mfVal = Op::compare(mfVal, fVal);
                mbHasValue = true;
            }
            break;
            case mdds::mtm::element_string:
            case mdds::mtm::element_empty:
            {
                // empty elements are treated as empty strings.
                if (mbTextAsZero)
                {
                    mfVal = Op::compare(mfVal, 0.0);
                    mbHasValue = true;
                }
            }
            break;
            default:
                ;
        }
    }
};

template<typename Op>
class CalcGcdLcm
{
    double mfval;

public:
    CalcGcdLcm() : mfval(Op::init()) {}

    double getResult() const { return mfval; }

    void operator() ( const MatrixImplType::element_block_node_type& node )
    {
        switch (node.type)
        {
            case mdds::mtm::element_numeric:
                {
                    typedef MatrixImplType::numeric_block_type block_type;
                    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 (*it < 0.0)
                            mfval = CreateDoubleError(FormulaError::IllegalArgument);
                        else
                            mfval = ::rtl::math::approxFloor( Op::calculate(*it,mfval));
                    }
                }
            break;
            case mdds::mtm::element_boolean:
                {
                    typedef MatrixImplType::boolean_block_type block_type;
                    block_type::const_iterator it = block_type::begin(*node.data);
                    block_type::const_iterator itEnd = block_type::end(*node.data);

                    mfval = Op::boolValue(it, itEnd);
                }
            break;
            case mdds::mtm::element_empty:
            case mdds::mtm::element_string:
                {
                    mfval = CreateDoubleError(FormulaError::IllegalArgument);
                }
            break;
            default:
                ;
        }
    }
};

double evaluate( double fVal, ScQueryOp eOp )
{
    if (!std::isfinite(fVal))
        return fVal;

    switch (eOp)
    {
        case SC_EQUAL:
            return fVal == 0.0 ? 1.0 : 0.0;
        case SC_LESS:
            return fVal < 0.0 ? 1.0 : 0.0;
        case SC_GREATER:
            return fVal > 0.0 ? 1.0 : 0.0;
        case SC_LESS_EQUAL:
            return fVal <= 0.0 ? 1.0 : 0.0;
        case SC_GREATER_EQUAL:
            return fVal >= 0.0 ? 1.0 : 0.0;
        case SC_NOT_EQUAL:
            return fVal != 0.0 ? 1.0 : 0.0;
        default:
            ;
    }

    SAL_WARN("sc.core",  "evaluate: unhandled comparison operator: " << static_cast<int>(eOp));
    return CreateDoubleError( FormulaError::UnknownState);
}

class CompareMatrixFunc
{
    sc::Compare& mrComp;
    size_t mnMatPos;
    sc::CompareOptions* mpOptions;
    std::vector<double> maResValues;    // double instead of bool to transport error values

    void compare()
    {
        double fVal = sc::CompareFunc( mrComp, mpOptions);
        maResValues.push_back(evaluate(fVal, mrComp.meOp));
    }

public:
    CompareMatrixFunc( size_t nResSize, sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) :
        mrComp(rComp), mnMatPos(nMatPos), mpOptions(pOptions)
    {
        maResValues.reserve(nResSize);
    }

    CompareMatrixFunc( const CompareMatrixFunc& ) = delete;
    CompareMatrixFunc& operator= ( const CompareMatrixFunc& ) = delete;

    CompareMatrixFunc(CompareMatrixFunc&& r) noexcept :
        mrComp(r.mrComp),
        mnMatPos(r.mnMatPos),
        mpOptions(r.mpOptions),
        maResValues(std::move(r.maResValues)) {}

    CompareMatrixFunc& operator=(CompareMatrixFunc&& r) noexcept
    {
        mrComp = r.mrComp;
        mnMatPos = r.mnMatPos;
        mpOptions = r.mpOptions;
        maResValues = std::move(r.maResValues);
        return *this;
    }

    void operator() (const MatrixImplType::element_block_node_type& node)
    {
        sc::Compare::Cell& rCell = mrComp.maCells[mnMatPos];

        switch (node.type)
        {
            case mdds::mtm::element_numeric:
            {
                typedef MatrixImplType::numeric_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                {
                    rCell.mbValue = true;
                    rCell.mbEmpty = false;
                    rCell.mfValue = *it;
                    compare();
                }
            }
            break;
            case mdds::mtm::element_boolean:
            {
                typedef MatrixImplType::boolean_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                {
                    rCell.mbValue = true;
                    rCell.mbEmpty = false;
                    rCell.mfValue = double(*it);
                    compare();
                }
            }
            break;
            case mdds::mtm::element_string:
            {
                typedef MatrixImplType::string_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                {
                    const svl::SharedString& rStr = *it;
                    rCell.mbValue = false;
                    rCell.mbEmpty = false;
                    rCell.maStr = rStr;
                    compare();
                }
            }
            break;
            case mdds::mtm::element_empty:
            {
                rCell.mbValue = false;
                rCell.mbEmpty = true;
                rCell.maStr = svl::SharedString::getEmptyString();
                for (size_t i = 0; i < node.size; ++i)
                    compare();
            }
            break;
            default:
                ;
        }
    }

    const std::vector<double>& getValues() const
    {
        return maResValues;
    }
};

/**
 * Left-hand side is a matrix while the right-hand side is a numeric value.
 */

class CompareMatrixToNumericFunc
{
    sc::Compare& mrComp;
    double mfRightValue;
    sc::CompareOptions* mpOptions;
    std::vector<double> maResValues;    // double instead of bool to transport error values

    void compare()
    {
        double fVal = sc::CompareFunc(mrComp.maCells[0], mfRightValue, mpOptions);
        maResValues.push_back(evaluate(fVal, mrComp.meOp));
    }

    void compareLeftNumeric( double fLeftVal )
    {
        double fVal = sc::CompareFunc(fLeftVal, mfRightValue);
        maResValues.push_back(evaluate(fVal, mrComp.meOp));
    }

    void compareLeftEmpty( size_t nSize )
    {
        double fVal = sc::CompareEmptyToNumericFunc(mfRightValue);
        bool bRes = evaluate(fVal, mrComp.meOp);
        maResValues.resize(maResValues.size() + nSize, bRes ? 1.0 : 0.0);
    }

public:
    CompareMatrixToNumericFunc( size_t nResSize, sc::Compare& rComp, double fRightValue, sc::CompareOptions* pOptions ) :
        mrComp(rComp), mfRightValue(fRightValue), mpOptions(pOptions)
    {
        maResValues.reserve(nResSize);
    }

    CompareMatrixToNumericFunc( const CompareMatrixToNumericFunc& ) = delete;
    CompareMatrixToNumericFunc& operator= ( const CompareMatrixToNumericFunc& ) = delete;

    CompareMatrixToNumericFunc(CompareMatrixToNumericFunc&& r) noexcept :
        mrComp(r.mrComp),
        mfRightValue(r.mfRightValue),
        mpOptions(r.mpOptions),
        maResValues(std::move(r.maResValues)) {}

    CompareMatrixToNumericFunc& operator=(CompareMatrixToNumericFunc&& r) noexcept
    {
        mrComp = r.mrComp;
        mfRightValue = r.mfRightValue;
        mpOptions = r.mpOptions;
        maResValues = std::move(r.maResValues);
        return *this;
    }

    void operator() (const MatrixImplType::element_block_node_type& node)
    {
        switch (node.type)
        {
            case mdds::mtm::element_numeric:
            {
                typedef MatrixImplType::numeric_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                    compareLeftNumeric(*it);
            }
            break;
            case mdds::mtm::element_boolean:
            {
                typedef MatrixImplType::boolean_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                    compareLeftNumeric(double(*it));
            }
            break;
            case mdds::mtm::element_string:
            {
                typedef MatrixImplType::string_block_type block_type;

                block_type::const_iterator it = block_type::begin(*node.data);
                block_type::const_iterator itEnd = block_type::end(*node.data);
                for (; it != itEnd; ++it)
                {
                    const svl::SharedString& rStr = *it;
                    sc::Compare::Cell& rCell = mrComp.maCells[0];
                    rCell.mbValue = false;
                    rCell.mbEmpty = false;
                    rCell.maStr = rStr;
                    compare();
                }
            }
            break;
            case mdds::mtm::element_empty:
                compareLeftEmpty(node.size);
            break;
            default:
                ;
        }
    }

    const std::vector<double>& getValues() const
    {
        return maResValues;
    }
};

class ToDoubleArray
{
    std::vector<double> maArray;
    std::vector<double>::iterator miPos;
    double mfNaN;
    bool mbEmptyAsZero;

    void moveArray( ToDoubleArray& r )
    {
        // Re-create the iterator from the new array after the array has been
        // moved, to ensure that the iterator points to a valid array
        // position.
        size_t n = std::distance(r.maArray.begin(), r.miPos);
        maArray = std::move(r.maArray);
        miPos = maArray.begin();
        std::advance(miPos, n);
    }

public:
    ToDoubleArray( size_t nSize, bool bEmptyAsZero ) :
        maArray(nSize, 0.0), miPos(maArray.begin()), mbEmptyAsZero(bEmptyAsZero)
    {
        mfNaN = CreateDoubleError( FormulaError::ElementNaN);
    }

    ToDoubleArray( const ToDoubleArray& ) = delete;
    ToDoubleArray& operator= ( const ToDoubleArray& ) = delete;

    ToDoubleArray(ToDoubleArray&& r) noexcept :
        mfNaN(r.mfNaN), mbEmptyAsZero(r.mbEmptyAsZero)
    {
        moveArray(r);
    }

    ToDoubleArray& operator=(ToDoubleArray&& r) noexcept
    {
        mfNaN = r.mfNaN;
        mbEmptyAsZero = r.mbEmptyAsZero;
        moveArray(r);
        return *this;
    }

    void operator() (const MatrixImplType::element_block_node_type& node)
    {
        using namespace mdds::mtv;

        switch (node.type)
        {
            case mdds::mtm::element_numeric:
            {
                double_element_block::const_iterator it = double_element_block::begin(*node.data);
                double_element_block::const_iterator itEnd = double_element_block::end(*node.data);
                for (; it != itEnd; ++it, ++miPos)
                    *miPos = *it;
            }
            break;
            case mdds::mtm::element_boolean:
            {
                boolean_element_block::const_iterator it = boolean_element_block::begin(*node.data);
                boolean_element_block::const_iterator itEnd = boolean_element_block::end(*node.data);
                for (; it != itEnd; ++it, ++miPos)
                    *miPos = *it ? 1.0 : 0.0;
            }
            break;
            case mdds::mtm::element_string:
            {
                for (size_t i = 0; i < node.size; ++i, ++miPos)
                    *miPos = mfNaN;
            }
            break;
            case mdds::mtm::element_empty:
            {
                if (mbEmptyAsZero)
                {
                    std::advance(miPos, node.size);
                    return;
                }

                for (size_t i = 0; i < node.size; ++i, ++miPos)
                    *miPos = mfNaN;
            }
            break;
            default:
                ;
        }
    }

    void swap(std::vector<double>& rOther)
    {
        maArray.swap(rOther);
    }
};

struct ArrayMul
{
    double operator() (const double& lhs, const double& rhs) const
    {
        return lhs * rhs;
    }
};

template<typename Op>
class MergeDoubleArrayFunc
{
    std::vector<double>::iterator miPos;
    double mfNaN;
public:
    MergeDoubleArrayFunc(std::vector<double>& rArray) : miPos(rArray.begin())
    {
        mfNaN = CreateDoubleError( FormulaError::ElementNaN);
    }

    MergeDoubleArrayFunc( const MergeDoubleArrayFunc& ) = delete;
    MergeDoubleArrayFunc& operator= ( const MergeDoubleArrayFunc& ) = delete;

    MergeDoubleArrayFunc( MergeDoubleArrayFunc&& ) = default;
    MergeDoubleArrayFunc& operator= ( MergeDoubleArrayFunc&& ) = default;

    void operator() (const MatrixImplType::element_block_node_type& node)
    {
        using namespace mdds::mtv;
        static const Op op;

        switch (node.type)
        {
            case mdds::mtm::element_numeric:
            {
                double_element_block::const_iterator it = double_element_block::begin(*node.data);
                double_element_block::const_iterator itEnd = double_element_block::end(*node.data);
                for (; it != itEnd; ++it, ++miPos)
                {
                    if (GetDoubleErrorValue(*miPos) == FormulaError::ElementNaN)
                        continue;

                    *miPos = op(*miPos, *it);
                }
            }
            break;
            case mdds::mtm::element_boolean:
            {
                boolean_element_block::const_iterator it = boolean_element_block::begin(*node.data);
                boolean_element_block::const_iterator itEnd = boolean_element_block::end(*node.data);
                for (; it != itEnd; ++it, ++miPos)
                {
                    if (GetDoubleErrorValue(*miPos) == FormulaError::ElementNaN)
                        continue;

                    *miPos = op(*miPos, *it ? 1.0 : 0.0);
                }
            }
            break;
            case mdds::mtm::element_string:
            {
                for (size_t i = 0; i < node.size; ++i, ++miPos)
                    *miPos = mfNaN;
            }
            break;
            case mdds::mtm::element_empty:
            {
                // Empty element is equivalent of having a numeric value of 0.0.
                for (size_t i = 0; i < node.size; ++i, ++miPos)
                {
                    if (GetDoubleErrorValue(*miPos) == FormulaError::ElementNaN)
                        continue;

                    *miPos = op(*miPos, 0.0);
                }
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=96 G=94

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge