diff options
Diffstat (limited to 'sc/source/core/tool/scmatrix.cxx')
-rw-r--r-- | sc/source/core/tool/scmatrix.cxx | 3505 |
1 files changed, 3505 insertions, 0 deletions
diff --git a/sc/source/core/tool/scmatrix.cxx b/sc/source/core/tool/scmatrix.cxx new file mode 100644 index 000000000..d23c3b698 --- /dev/null +++ b/sc/source/core/tool/scmatrix.cxx @@ -0,0 +1,3505 @@ +/* -*- 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 <arraysumfunctor.hxx> +#include <scmatrix.hxx> +#include <global.hxx> +#include <address.hxx> +#include <formula/errorcodes.hxx> +#include <formula/vectortoken.hxx> +#include <interpre.hxx> +#include <mtvelements.hxx> +#include <compare.hxx> +#include <matrixoperators.hxx> +#include <math.hxx> + +#include <svl/zforlist.hxx> +#include <svl/sharedstring.hxx> +#include <tools/stream.hxx> +#include <rtl/math.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include <math.h> + +#include <memory> +#include <vector> +#include <limits> + +#include <mdds/multi_type_matrix.hpp> +#include <mdds/multi_type_vector_types.hpp> +#include <mdds/multi_type_vector_trait.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_trait +{ + typedef sc::string_block string_element_block; + typedef sc::uint16_block integer_element_block; + + typedef mdds::mtv::custom_block_func1<sc::string_block> element_block_func; +}; + +} + +typedef mdds::multi_type_matrix<matrix_trait> MatrixImplType; + +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; + +} + +/* 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_EMPTYRESULT = 1.0; +const TMatFlag SC_MATFLAG_EMPTYPATH = 2.0; + +class ScMatrixImpl +{ + MatrixImplType maMat; + MatrixImplType 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() COVERITY_NOEXCEPT_FALSE; + + 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 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 PutString(const svl::SharedString* pArray, size_t nLen, SCSIZE nC, SCSIZE nR); + + void PutEmpty(SCSIZE nC, SCSIZE nR); + 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( SvNumberFormatter& rFormatter, 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::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, 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; + + 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> + std::vector<ScMatrix::IterateResult> ApplyCollectOperation(const std::vector<T>& aOp); + + void MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrixRef& xMat1, const ScMatrixRef& xMat2, + SvNumberFormatter& rFormatter, svl::SharedStringPool& rPool); + +#if DEBUG_MATRIX + void Dump() const; +#endif + +private: + void CalcPosition(SCSIZE nIndex, SCSIZE& rC, SCSIZE& rR) const; +}; + +static bool bElementsMaxFetched; +static 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() COVERITY_NOEXCEPT_FALSE +{ + nElementsMax += GetElementCount(); + Clear(); +} + +void ScMatrixImpl::Clear() +{ + 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::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::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::PutEmptyPath(SCSIZE nC, SCSIZE nR) +{ + if (ValidColRow( nC, nR)) + { + maMat.set_empty(nR, nC); + maMatFlag.set(nR, nC, SC_MATFLAG_EMPTYPATH); + } + 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 )) + { + double fErr = 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: + return svl::SharedString::getEmptyString(); + case mdds::mtm::element_numeric: + case mdds::mtm::element_boolean: + fErr = maMat.get_numeric(aPos); + [[fallthrough]]; + default: + OSL_FAIL("ScMatrixImpl::GetString: access error, no string"); + } + SetErrorAtInterpreter(GetDoubleErrorValue(fErr)); + } + else + { + OSL_FAIL("ScMatrixImpl::GetString: dimension error"); + } + 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( SvNumberFormatter& rFormatter, 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_numeric(nR, nC) != SC_MATFLAG_EMPTYPATH) + // not an empty path. + return svl::SharedString::getEmptyString(); + + // result of empty FALSE jump path + sal_uInt32 nKey = rFormatter.GetStandardFormat( SvNumFormatType::LOGICAL, + ScGlobal::eLnge); + OUString aStr; + Color* pColor = nullptr; + rFormatter.GetOutputString( 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 = rFormatter.GetStandardFormat( SvNumFormatType::NUMBER, + ScGlobal::eLnge); + OUString aStr; + rFormatter.GetInputLineString( fVal, nKey, aStr); + return svl::SharedString( aStr); // 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_numeric: + aVal.nType = maMatFlag.get<TMatFlag>(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 +{ + ValidColRowReplicated( nC, nR ); + 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 +{ + // Flag must indicate an 'empty' or 'empty cell' or 'empty result' element, + // but not an 'empty path' element. + ValidColRowReplicated( nC, nR ); + return maMat.get_type(nR, nC) == mdds::mtm::element_empty && + maMatFlag.get_numeric(nR, nC) != SC_MATFLAG_EMPTYPATH; +} + +bool ScMatrixImpl::IsEmptyCell( SCSIZE nC, SCSIZE nR ) const +{ + // Flag must indicate an 'empty cell' element instead of an + // 'empty' or 'empty result' or 'empty path' element. + ValidColRowReplicated( nC, nR ); + 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 +{ + // Flag must indicate an 'empty result' element instead of an + // 'empty' or 'empty cell' or 'empty path' element. + ValidColRowReplicated( nC, nR ); + return maMat.get_type(nR, nC) == mdds::mtm::element_empty && + maMatFlag.get_numeric(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_numeric(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 +{ + ValidColRowReplicated(nC, nR); + 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 +{ + ValidColRowReplicated(nC, nR); + 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 +{ + ValidColRowReplicated( nC, nR ); + 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<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); + // Flag to indicate 'empty path'. + std::vector<TMatFlag> 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; + for (size_t i = 0; i < nRows; ++i) + { + for (size_t j = 0; j < nCols; ++j) + { + MatrixImplType::const_position_type aPos = rMat.position(i, j); + 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); + } + } + 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> +class WalkElementBlocks +{ + Op maOp; + ScMatrix::IterateResult maRes; + bool mbFirst:1; + bool mbTextAsZero:1; + bool mbIgnoreErrorValues:1; +public: + WalkElementBlocks(bool bTextAsZero, bool bIgnoreErrorValues) : + maRes(Op::InitVal, Op::InitVal, 0), mbFirst(true), + mbTextAsZero(bTextAsZero), mbIgnoreErrorValues(bIgnoreErrorValues) + {} + + const ScMatrix::IterateResult& 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; + } + + if (mbFirst) + { + maOp(maRes.mfFirst, *it); + mbFirst = false; + } + else + { + maOp(maRes.mfRest, *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) + { + if (mbFirst) + { + maOp(maRes.mfFirst, *it); + mbFirst = false; + } + else + { + maOp(maRes.mfRest, *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> +class WalkElementBlocksMultipleValues +{ + const std::vector<Op>* mpOp; + std::vector<ScMatrix::IterateResult> maRes; + bool mbFirst:1; +public: + WalkElementBlocksMultipleValues(const std::vector<Op>& aOp) : + mpOp(&aOp), mbFirst(true) + { + for (const auto& rpOp : *mpOp) + { + maRes.emplace_back(rpOp.mInitVal, rpOp.mInitVal, 0); + } + maRes.emplace_back(0.0, 0.0, 0); // count + } + + WalkElementBlocksMultipleValues( const WalkElementBlocksMultipleValues& ) = delete; + WalkElementBlocksMultipleValues& operator= ( const WalkElementBlocksMultipleValues& ) = delete; + + WalkElementBlocksMultipleValues(WalkElementBlocksMultipleValues&& r) noexcept : + mpOp(r.mpOp), maRes(std::move(r.maRes)), mbFirst(r.mbFirst) {} + + WalkElementBlocksMultipleValues& operator=(WalkElementBlocksMultipleValues&& r) noexcept + { + mpOp = r.mpOp; + maRes = std::move(r.maRes); + mbFirst = r.mbFirst; + return *this; + } + + const std::vector<ScMatrix::IterateResult>& 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) + { + if (mbFirst) + { + for (size_t i = 0u; i < mpOp->size(); ++i) + { + (*mpOp)[i](maRes[i].mfFirst, *it); + } + mbFirst = false; + } + else + { + for (size_t i = 0u; i < mpOp->size(); ++i) + { + (*mpOp)[i](maRes[i].mfRest, *it); + } + } + } + maRes.back().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) + { + if (mbFirst) + { + for (size_t i = 0u; i < mpOp->size(); ++i) + { + (*mpOp)[i](maRes[i].mfFirst, *it); + } + mbFirst = false; + } + else + { + for (size_t i = 0u; i < mpOp->size(); ++i) + { + (*mpOp)[i](maRes[i].mfRest, *it); + } + } + } + maRes.back().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(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; + break; + case SC_LESS_EQUAL: + return fVal <= 0.0 ? 1.0 : 0.0; + break; + case SC_GREATER_EQUAL: + return fVal >= 0.0 ? 1.0 : 0.0; + break; + case SC_NOT_EQUAL: + return fVal != 0.0 ? 1.0 : 0.0; + break; + 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); + } + } + break; + default: + ; + } + } +}; + +} + +namespace { + +template<typename TOp> +ScMatrix::IterateResult GetValueWithCount(bool bTextAsZero, bool bIgnoreErrorValues, const MatrixImplType& maMat) +{ + WalkElementBlocks<TOp> aFunc(bTextAsZero, bIgnoreErrorValues); + aFunc = maMat.walk(aFunc); + return aFunc.getResult(); +} + +} + +ScMatrix::IterateResult ScMatrixImpl::Sum(bool bTextAsZero, bool bIgnoreErrorValues) const +{ + return GetValueWithCount<sc::op::Sum>(bTextAsZero, bIgnoreErrorValues, maMat); +} + +ScMatrix::IterateResult ScMatrixImpl::SumSquare(bool bTextAsZero, bool bIgnoreErrorValues) const +{ + return GetValueWithCount<sc::op::SumSquare>(bTextAsZero, bIgnoreErrorValues, maMat); +} + +ScMatrix::IterateResult ScMatrixImpl::Product(bool bTextAsZero, bool bIgnoreErrorValues) const +{ + return GetValueWithCount<sc::op::Product>(bTextAsZero, bIgnoreErrorValues, maMat); +} + +size_t ScMatrixImpl::Count(bool bCountStrings, bool bCountErrors, bool bIgnoreEmptyStrings) const +{ + CountElements aFunc(bCountStrings, bCountErrors, bIgnoreEmptyStrings); + aFunc = maMat.walk(aFunc); + return aFunc.getCount(); +} + +size_t ScMatrixImpl::MatchDoubleInColumns(double fValue, size_t nCol1, size_t nCol2) const +{ + WalkAndMatchElements<double> aFunc(fValue, maMat.size(), nCol1, nCol2); + aFunc = maMat.walk(aFunc); + return aFunc.getMatching(); +} + +size_t ScMatrixImpl::MatchStringInColumns(const svl::SharedString& rStr, size_t nCol1, size_t nCol2) const +{ + WalkAndMatchElements<svl::SharedString> aFunc(rStr, maMat.size(), nCol1, nCol2); + aFunc = maMat.walk(aFunc); + return aFunc.getMatching(); +} + +double ScMatrixImpl::GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues ) const +{ + CalcMaxMinValue<MaxOp> aFunc(bTextAsZero, bIgnoreErrorValues); + aFunc = maMat.walk(aFunc); + return aFunc.getValue(); +} + +double ScMatrixImpl::GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) const +{ + CalcMaxMinValue<MinOp> aFunc(bTextAsZero, bIgnoreErrorValues); + aFunc = maMat.walk(aFunc); + return aFunc.getValue(); +} + +double ScMatrixImpl::GetGcd() const +{ + CalcGcdLcm<Gcd> aFunc; + aFunc = maMat.walk(aFunc); + return aFunc.getResult(); +} + +double ScMatrixImpl::GetLcm() const +{ + CalcGcdLcm<Lcm> aFunc; + aFunc = maMat.walk(aFunc); + return aFunc.getResult(); +} + +ScMatrixRef ScMatrixImpl::CompareMatrix( + sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) const +{ + MatrixImplType::size_pair_type aSize = maMat.size(); + size_t nSize = aSize.column * aSize.row; + if (nMatPos == 0) + { + if (rComp.maCells[1].mbValue && !rComp.maCells[1].mbEmpty) + { + // Matrix on the left, and a numeric value on the right. Use a + // function object that has much less branching for much better + // performance. + CompareMatrixToNumericFunc aFunc(nSize, rComp, rComp.maCells[1].mfValue, pOptions); + aFunc = maMat.walk(std::move(aFunc)); + + // We assume the result matrix has the same dimension as this matrix. + const std::vector<double>& rResVal = aFunc.getValues(); + if (nSize != rResVal.size()) + ScMatrixRef(); + + return ScMatrixRef(new ScMatrix(aSize.column, aSize.row, rResVal)); + } + } + + CompareMatrixFunc aFunc(nSize, rComp, nMatPos, pOptions); + aFunc = maMat.walk(std::move(aFunc)); + + // We assume the result matrix has the same dimension as this matrix. + const std::vector<double>& rResVal = aFunc.getValues(); + if (nSize != rResVal.size()) + ScMatrixRef(); + + return ScMatrixRef(new ScMatrix(aSize.column, aSize.row, rResVal)); +} + +void ScMatrixImpl::GetDoubleArray( std::vector<double>& rArray, bool bEmptyAsZero ) const +{ + MatrixImplType::size_pair_type aSize = maMat.size(); + ToDoubleArray aFunc(aSize.row*aSize.column, bEmptyAsZero); + aFunc = maMat.walk(std::move(aFunc)); + aFunc.swap(rArray); +} + +void ScMatrixImpl::MergeDoubleArrayMultiply( std::vector<double>& rArray ) const +{ + MatrixImplType::size_pair_type aSize = maMat.size(); + size_t nSize = aSize.row*aSize.column; + if (nSize != rArray.size()) + return; + + MergeDoubleArrayFunc<ArrayMul> aFunc(rArray); + maMat.walk(std::move(aFunc)); +} + +namespace { + +template<typename T, typename U, typename return_type> +struct wrapped_iterator +{ + typedef ::std::bidirectional_iterator_tag iterator_category; + typedef typename T::const_iterator::value_type old_value_type; + typedef return_type value_type; + typedef value_type* pointer; + typedef value_type& reference; + typedef typename T::const_iterator::difference_type difference_type; + + typename T::const_iterator it; + mutable value_type val; + U maOp; + +private: + + value_type calcVal() const + { + return maOp(*it); + } + +public: + + wrapped_iterator(typename T::const_iterator const & it_, U const & aOp): + it(it_), + val(value_type()), + maOp(aOp) + { + } + + wrapped_iterator(const wrapped_iterator& r): + it(r.it), + val(r.val), + maOp(r.maOp) + { + } + + wrapped_iterator& operator=(const wrapped_iterator& r) + { + it = r.it; + return *this; + } + + bool operator==(const wrapped_iterator& r) const + { + return it == r.it; + } + + bool operator!=(const wrapped_iterator& r) const + { + return !operator==(r); + } + + wrapped_iterator& operator++() + { + ++it; + + return *this; + } + + wrapped_iterator& operator--() + { + --it; + + return *this; + } + + value_type& operator*() const + { + val = calcVal(); + return val; + } + + pointer operator->() const + { + val = calcVal(); + return &val; + } +}; + +template<typename T, typename U, typename return_type> +struct MatrixIteratorWrapper +{ +private: + typename T::const_iterator m_itBegin; + typename T::const_iterator m_itEnd; + U maOp; +public: + MatrixIteratorWrapper(typename T::const_iterator const & itBegin, typename T::const_iterator const & itEnd, U const & aOp): + m_itBegin(itBegin), + m_itEnd(itEnd), + maOp(aOp) + { + } + + wrapped_iterator<T, U, return_type> begin() + { + return wrapped_iterator<T, U, return_type>(m_itBegin, maOp); + } + + wrapped_iterator<T, U, return_type> end() + { + return wrapped_iterator<T, U, return_type>(m_itEnd, maOp); + } +}; + +MatrixImplType::position_type increment_position(const MatrixImplType::position_type& pos, size_t n) +{ + MatrixImplType::position_type ret = pos; + do + { + if (ret.second + n < ret.first->size) + { + ret.second += n; + break; + } + else + { + n -= (ret.first->size - ret.second); + ++ret.first; + ret.second = 0; + } + } + while (n > 0); + return ret; +} + +template<typename T> +struct MatrixOpWrapper +{ +private: + MatrixImplType& mrMat; + MatrixImplType::position_type pos; + const T* mpOp; + +public: + MatrixOpWrapper(MatrixImplType& rMat, const T& aOp): + mrMat(rMat), + pos(rMat.position(0,0)), + mpOp(&aOp) + { + } + + MatrixOpWrapper( const MatrixOpWrapper& r ) : mrMat(r.mrMat), pos(r.pos), mpOp(r.mpOp) {} + + MatrixOpWrapper& operator= ( const MatrixOpWrapper& r ) = 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); + MatrixIteratorWrapper<block_type, T, typename T::number_value_type> aFunc(it, itEnd, *mpOp); + pos = mrMat.set(pos,aFunc.begin(), aFunc.end()); + } + 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); + + MatrixIteratorWrapper<block_type, T, typename T::number_value_type> aFunc(it, itEnd, *mpOp); + pos = mrMat.set(pos, aFunc.begin(), aFunc.end()); + } + 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); + + MatrixIteratorWrapper<block_type, T, typename T::number_value_type> aFunc(it, itEnd, *mpOp); + pos = mrMat.set(pos, aFunc.begin(), aFunc.end()); + } + break; + case mdds::mtm::element_empty: + { + if (mpOp->useFunctionForEmpty()) + { + std::vector<char> aVec(node.size); + MatrixIteratorWrapper<std::vector<char>, T, typename T::number_value_type> + aFunc(aVec.begin(), aVec.end(), *mpOp); + pos = mrMat.set(pos, aFunc.begin(), aFunc.end()); + } + } + break; + default: + ; + } + pos = increment_position(pos, node.size); + } +}; + +} + +template<typename T> +void ScMatrixImpl::ApplyOperation(T aOp, ScMatrixImpl& rMat) +{ + MatrixOpWrapper<T> aFunc(rMat.maMat, aOp); + maMat.walk(aFunc); +} + +template<typename T> +std::vector<ScMatrix::IterateResult> ScMatrixImpl::ApplyCollectOperation(const std::vector<T>& aOp) +{ + WalkElementBlocksMultipleValues<T> aFunc(aOp); + aFunc = maMat.walk(std::move(aFunc)); + return aFunc.getResult(); +} + +namespace { + +struct ElementBlock +{ + ElementBlock(size_t nRowSize, + ScMatrix::DoubleOpFunction const & aDoubleFunc, + ScMatrix::BoolOpFunction const & aBoolFunc, + ScMatrix::StringOpFunction const & aStringFunc, + ScMatrix::EmptyOpFunction const & aEmptyFunc): + mnRowSize(nRowSize), + mnRowPos(0), + mnColPos(0), + maDoubleFunc(aDoubleFunc), + maBoolFunc(aBoolFunc), + maStringFunc(aStringFunc), + maEmptyFunc(aEmptyFunc) + { + } + + size_t mnRowSize; + size_t mnRowPos; + size_t mnColPos; + + ScMatrix::DoubleOpFunction maDoubleFunc; + ScMatrix::BoolOpFunction maBoolFunc; + ScMatrix::StringOpFunction maStringFunc; + ScMatrix::EmptyOpFunction maEmptyFunc; +}; + +class WalkElementBlockOperation +{ +public: + + WalkElementBlockOperation(ElementBlock& rElementBlock) + : mrElementBlock(rElementBlock) + { + } + + 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); + std::advance(it, node.offset); + block_type::const_iterator itEnd = it; + std::advance(itEnd, node.size); + for (auto itr = it; itr != itEnd; ++itr) + { + mrElementBlock.maDoubleFunc(mrElementBlock.mnRowPos, mrElementBlock.mnColPos, *itr); + ++mrElementBlock.mnRowPos; + if (mrElementBlock.mnRowPos >= mrElementBlock.mnRowSize) + { + mrElementBlock.mnRowPos = 0; + ++mrElementBlock.mnColPos; + } + } + } + break; + case mdds::mtm::element_string: + { + typedef MatrixImplType::string_block_type block_type; + + block_type::const_iterator it = block_type::begin(*node.data); + std::advance(it, node.offset); + block_type::const_iterator itEnd = it; + std::advance(itEnd, node.size); + for (auto itr = it; itr != itEnd; ++itr) + { + mrElementBlock.maStringFunc(mrElementBlock.mnRowPos, mrElementBlock.mnColPos, *itr); + ++mrElementBlock.mnRowPos; + if (mrElementBlock.mnRowPos >= mrElementBlock.mnRowSize) + { + mrElementBlock.mnRowPos = 0; + ++mrElementBlock.mnColPos; + } + } + } + break; + case mdds::mtm::element_boolean: + { + typedef MatrixImplType::boolean_block_type block_type; + + block_type::const_iterator it = block_type::begin(*node.data); + std::advance(it, node.offset); + block_type::const_iterator itEnd = it; + std::advance(itEnd, node.size); + for (auto itr = it; itr != itEnd; ++itr) + { + mrElementBlock.maBoolFunc(mrElementBlock.mnRowPos, mrElementBlock.mnColPos, *itr); + ++mrElementBlock.mnRowPos; + if (mrElementBlock.mnRowPos >= mrElementBlock.mnRowSize) + { + mrElementBlock.mnRowPos = 0; + ++mrElementBlock.mnColPos; + } + } + } + break; + case mdds::mtm::element_empty: + { + for (size_t i=0; i < node.size; ++i) + { + mrElementBlock.maEmptyFunc(mrElementBlock.mnRowPos, mrElementBlock.mnColPos); + ++mrElementBlock.mnRowPos; + if (mrElementBlock.mnRowPos >= mrElementBlock.mnRowSize) + { + mrElementBlock.mnRowPos = 0; + ++mrElementBlock.mnColPos; + } + } + } + break; + case mdds::mtm::element_integer: + { + SAL_WARN("sc.core","WalkElementBlockOperation - unhandled element_integer"); + // No function (yet?), but advance row and column count. + mrElementBlock.mnColPos += node.size / mrElementBlock.mnRowSize; + mrElementBlock.mnRowPos += node.size % mrElementBlock.mnRowSize; + if (mrElementBlock.mnRowPos >= mrElementBlock.mnRowSize) + { + mrElementBlock.mnRowPos = 0; + ++mrElementBlock.mnColPos; + } + } + break; + } + } + +private: + + ElementBlock& mrElementBlock; +}; + +} + +void ScMatrixImpl::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 +{ + ElementBlock aPayload(maMat.size().row, aDoubleFunc, aBoolFunc, aStringFunc, aEmptyFunc); + WalkElementBlockOperation aFunc(aPayload); + maMat.walk( + aFunc, + MatrixImplType::size_pair_type(rStartPos.first, rStartPos.second), + MatrixImplType::size_pair_type(rEndPos.first, rEndPos.second)); +} + +#if DEBUG_MATRIX + +void ScMatrixImpl::Dump() const +{ + cout << "-- matrix content" << endl; + SCSIZE nCols, nRows; + GetDimensions(nCols, nRows); + for (SCSIZE nRow = 0; nRow < nRows; ++nRow) + { + for (SCSIZE nCol = 0; nCol < nCols; ++nCol) + { + cout << " row=" << nRow << ", col=" << nCol << " : "; + switch (maMat.get_type(nRow, nCol)) + { + case mdds::mtm::element_string: + cout << "string (" << maMat.get_string(nRow, nCol).getString() << ")"; + break; + case mdds::mtm::element_numeric: + cout << "numeric (" << maMat.get_numeric(nRow, nCol) << ")"; + break; + case mdds::mtm::element_boolean: + cout << "boolean (" << maMat.get_boolean(nRow, nCol) << ")"; + break; + case mdds::mtm::element_empty: + cout << "empty"; + break; + default: + ; + } + + cout << endl; + } + } +} +#endif + +void ScMatrixImpl::CalcPosition(SCSIZE nIndex, SCSIZE& rC, SCSIZE& rR) const +{ + SCSIZE nRowSize = maMat.size().row; + SAL_WARN_IF( !nRowSize, "sc.core", "ScMatrixImpl::CalcPosition: 0 rows!"); + rC = nRowSize > 1 ? nIndex / nRowSize : nIndex; + rR = nIndex - rC*nRowSize; +} + +namespace { + +size_t get_index(SCSIZE nMaxRow, size_t nRow, size_t nCol, size_t nRowOffset, size_t nColOffset) +{ + return nMaxRow * (nCol + nColOffset) + nRow + nRowOffset; +} + +} + +void ScMatrixImpl::MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrixRef& xMat1, const ScMatrixRef& xMat2, + SvNumberFormatter& rFormatter, svl::SharedStringPool& rStringPool) +{ + SCSIZE nC1, nC2; + SCSIZE nR1, nR2; + xMat1->GetDimensions(nC1, nR1); + xMat2->GetDimensions(nC2, nR2); + + sal_uInt32 nKey = rFormatter.GetStandardFormat( SvNumFormatType::NUMBER, + ScGlobal::eLnge); + + std::vector<OUString> aString(nMaxCol * nMaxRow); + std::vector<bool> aValid(nMaxCol * nMaxRow, true); + std::vector<FormulaError> nErrors(nMaxCol * nMaxRow,FormulaError::NONE); + + size_t nRowOffset = 0; + size_t nColOffset = 0; + std::function<void(size_t, size_t, double)> aDoubleFunc = + [&](size_t nRow, size_t nCol, double nVal) + { + FormulaError nErr = GetDoubleErrorValue(nVal); + if (nErr != FormulaError::NONE) + { + aValid[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = false; + nErrors[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = nErr; + return; + } + OUString aStr; + rFormatter.GetInputLineString( nVal, nKey, aStr); + aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr; + }; + + std::function<void(size_t, size_t, bool)> aBoolFunc = + [&](size_t nRow, size_t nCol, bool nVal) + { + OUString aStr; + rFormatter.GetInputLineString( nVal ? 1.0 : 0.0, nKey, aStr); + aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr; + }; + + std::function<void(size_t, size_t, const svl::SharedString&)> aStringFunc = + [&](size_t nRow, size_t nCol, const svl::SharedString& aStr) + { + aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr.getString(); + }; + + std::function<void(size_t, size_t)> aEmptyFunc = + [&](size_t /*nRow*/, size_t /*nCol*/) + { + // Nothing. Concatenating an empty string to an existing string. + }; + + + if (nC1 == 1 || nR1 == 1) + { + size_t nRowRep = nR1 == 1 ? nMaxRow : 1; + size_t nColRep = nC1 == 1 ? nMaxCol : 1; + + for (size_t i = 0; i < nRowRep; ++i) + { + nRowOffset = i; + for (size_t j = 0; j < nColRep; ++j) + { + nColOffset = j; + xMat1->ExecuteOperation( + std::pair<size_t, size_t>(0, 0), + std::pair<size_t, size_t>(std::min(nR1, nMaxRow) - 1, std::min(nC1, nMaxCol) - 1), + aDoubleFunc, aBoolFunc, aStringFunc, aEmptyFunc); + } + } + } + else + xMat1->ExecuteOperation( + std::pair<size_t, size_t>(0, 0), + std::pair<size_t, size_t>(nMaxRow - 1, nMaxCol - 1), + aDoubleFunc, aBoolFunc, aStringFunc, aEmptyFunc); + + std::vector<svl::SharedString> aSharedString(nMaxCol*nMaxRow); + + std::function<void(size_t, size_t, double)> aDoubleFunc2 = + [&](size_t nRow, size_t nCol, double nVal) + { + FormulaError nErr = GetDoubleErrorValue(nVal); + if (nErr != FormulaError::NONE) + { + aValid[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = false; + nErrors[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = nErr; + return; + } + OUString aStr; + rFormatter.GetInputLineString( nVal, nKey, aStr); + aSharedString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = rStringPool.intern(aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr); + }; + + std::function<void(size_t, size_t, bool)> aBoolFunc2 = + [&](size_t nRow, size_t nCol, bool nVal) + { + OUString aStr; + rFormatter.GetInputLineString( nVal ? 1.0 : 0.0, nKey, aStr); + aSharedString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = rStringPool.intern(aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr); + }; + + std::function<void(size_t, size_t, const svl::SharedString&)> aStringFunc2 = + [&](size_t nRow, size_t nCol, const svl::SharedString& aStr) + { + aSharedString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = + rStringPool.intern(aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr.getString()); + }; + + std::function<void(size_t, size_t)> aEmptyFunc2 = + [&](size_t nRow, size_t nCol) + { + aSharedString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = + rStringPool.intern(aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)]); + }; + + nRowOffset = 0; + nColOffset = 0; + if (nC2 == 1 || nR2 == 1) + { + size_t nRowRep = nR2 == 1 ? nMaxRow : 1; + size_t nColRep = nC2 == 1 ? nMaxCol : 1; + + for (size_t i = 0; i < nRowRep; ++i) + { + nRowOffset = i; + for (size_t j = 0; j < nColRep; ++j) + { + nColOffset = j; + xMat2->ExecuteOperation( + std::pair<size_t, size_t>(0, 0), + std::pair<size_t, size_t>(std::min(nR2, nMaxRow) - 1, std::min(nC2, nMaxCol) - 1), + aDoubleFunc2, aBoolFunc2, aStringFunc2, aEmptyFunc2); + } + } + } + else + xMat2->ExecuteOperation( + std::pair<size_t, size_t>(0, 0), + std::pair<size_t, size_t>(nMaxRow - 1, nMaxCol - 1), + aDoubleFunc2, aBoolFunc2, aStringFunc2, aEmptyFunc2); + + aString.clear(); + + MatrixImplType::position_type pos = maMat.position(0, 0); + for (SCSIZE i = 0; i < nMaxCol; ++i) + { + for (SCSIZE j = 0; j < nMaxRow && i < nMaxCol; ++j) + { + if (aValid[nMaxRow * i + j]) + { + auto itr = aValid.begin(); + std::advance(itr, nMaxRow * i + j); + auto itrEnd = std::find(itr, aValid.end(), false); + size_t nSteps = std::distance(itr, itrEnd); + auto itrStr = aSharedString.begin(); + std::advance(itrStr, nMaxRow * i + j); + auto itrEndStr = itrStr; + std::advance(itrEndStr, nSteps); + pos = maMat.set(pos, itrStr, itrEndStr); + size_t nColSteps = nSteps / nMaxRow; + i += nColSteps; + j += nSteps % nMaxRow; + if (j >= nMaxRow) + { + j -= nMaxRow; + ++i; + } + } + else + { + pos = maMat.set(pos, CreateDoubleError(nErrors[nMaxRow * i + j])); + } + pos = MatrixImplType::next_position(pos); + } + } +} + +void ScMatrix::IncRef() const +{ + ++nRefCnt; +} + +void ScMatrix::DecRef() const +{ + --nRefCnt; + if (nRefCnt == 0) + delete this; +} + +bool ScMatrix::IsSizeAllocatable( SCSIZE nC, SCSIZE nR ) +{ + SAL_WARN_IF( !nC, "sc.core", "ScMatrix with 0 columns!"); + SAL_WARN_IF( !nR, "sc.core", "ScMatrix with 0 rows!"); + // 0-size matrix is valid, it could be resized later. + if ((nC && !nR) || (!nC && nR)) + { + SAL_WARN( "sc.core", "ScMatrix one-dimensional zero: " << nC << " columns * " << nR << " rows"); + return false; + } + if (!nC || !nR) + return true; + + if (!bElementsMaxFetched) + { + const char* pEnv = std::getenv("SC_MAX_MATRIX_ELEMENTS"); + if (pEnv) + { + // Environment specifies the overall elements pool. + nElementsMax = std::atoi(pEnv); + } + else + { + // GetElementsMax() uses an (~arbitrary) elements limit. + // The actual allocation depends on the types of individual matrix + // elements and is averaged for type double. +#if SAL_TYPES_SIZEOFPOINTER < 8 + // Assume 1GB memory could be consumed by matrices. + constexpr size_t nMemMax = 0x40000000; +#else + // Assume 6GB memory could be consumed by matrices. + constexpr size_t nMemMax = 0x180000000; +#endif + nElementsMax = GetElementsMax( nMemMax); + } + bElementsMaxFetched = true; + } + + if (nC > (nElementsMax / nR)) + { + SAL_WARN( "sc.core", "ScMatrix overflow: " << nC << " columns * " << nR << " rows"); + return false; + } + return true; +} + +ScMatrix::ScMatrix( SCSIZE nC, SCSIZE nR) : + nRefCnt(0), mbCloneIfConst(true) +{ + if (ScMatrix::IsSizeAllocatable( nC, nR)) + pImpl.reset( new ScMatrixImpl( nC, nR)); + else + // Invalid matrix size, allocate 1x1 matrix with error value. + pImpl.reset( new ScMatrixImpl( 1,1, CreateDoubleError( FormulaError::MatrixSize))); +} + +ScMatrix::ScMatrix(SCSIZE nC, SCSIZE nR, double fInitVal) : + nRefCnt(0), mbCloneIfConst(true) +{ + if (ScMatrix::IsSizeAllocatable( nC, nR)) + pImpl.reset( new ScMatrixImpl( nC, nR, fInitVal)); + else + // Invalid matrix size, allocate 1x1 matrix with error value. + pImpl.reset( new ScMatrixImpl( 1,1, CreateDoubleError( FormulaError::MatrixSize))); +} + +ScMatrix::ScMatrix( size_t nC, size_t nR, const std::vector<double>& rInitVals ) : + nRefCnt(0), mbCloneIfConst(true) +{ + if (ScMatrix::IsSizeAllocatable( nC, nR)) + pImpl.reset( new ScMatrixImpl( nC, nR, rInitVals)); + else + // Invalid matrix size, allocate 1x1 matrix with error value. + pImpl.reset( new ScMatrixImpl( 1,1, CreateDoubleError( FormulaError::MatrixSize))); +} + +ScMatrix::~ScMatrix() +{ +} + +ScMatrix* ScMatrix::Clone() const +{ + SCSIZE nC, nR; + pImpl->GetDimensions(nC, nR); + ScMatrix* pScMat = new ScMatrix(nC, nR); + MatCopy(*pScMat); + pScMat->SetErrorInterpreter(pImpl->GetErrorInterpreter()); // TODO: really? + return pScMat; +} + +ScMatrix* ScMatrix::CloneIfConst() +{ + return mbCloneIfConst ? Clone() : this; +} + +void ScMatrix::SetMutable() +{ + mbCloneIfConst = false; +} + +void ScMatrix::SetImmutable() const +{ + mbCloneIfConst = true; +} + +void ScMatrix::Resize( SCSIZE nC, SCSIZE nR) +{ + pImpl->Resize(nC, nR); +} + +void ScMatrix::Resize(SCSIZE nC, SCSIZE nR, double fVal) +{ + pImpl->Resize(nC, nR, fVal); +} + +ScMatrix* ScMatrix::CloneAndExtend(SCSIZE nNewCols, SCSIZE nNewRows) const +{ + ScMatrix* pScMat = new ScMatrix(nNewCols, nNewRows); + MatCopy(*pScMat); + pScMat->SetErrorInterpreter(pImpl->GetErrorInterpreter()); + return pScMat; +} + +void ScMatrix::SetErrorInterpreter( ScInterpreter* p) +{ + pImpl->SetErrorInterpreter(p); +} + +void ScMatrix::GetDimensions( SCSIZE& rC, SCSIZE& rR) const +{ + pImpl->GetDimensions(rC, rR); +} + +SCSIZE ScMatrix::GetElementCount() const +{ + return pImpl->GetElementCount(); +} + +bool ScMatrix::ValidColRow( SCSIZE nC, SCSIZE nR) const +{ + return pImpl->ValidColRow(nC, nR); +} + +bool ScMatrix::ValidColRowReplicated( SCSIZE & rC, SCSIZE & rR ) const +{ + return pImpl->ValidColRowReplicated(rC, rR); +} + +bool ScMatrix::ValidColRowOrReplicated( SCSIZE & rC, SCSIZE & rR ) const +{ + return ValidColRow( rC, rR) || ValidColRowReplicated( rC, rR); +} + +void ScMatrix::PutDouble(double fVal, SCSIZE nC, SCSIZE nR) +{ + pImpl->PutDouble(fVal, nC, nR); +} + +void ScMatrix::PutDouble( double fVal, SCSIZE nIndex) +{ + pImpl->PutDouble(fVal, nIndex); +} + +void ScMatrix::PutDouble(const double* pArray, size_t nLen, SCSIZE nC, SCSIZE nR) +{ + pImpl->PutDouble(pArray, nLen, nC, nR); +} + +void ScMatrix::PutString(const svl::SharedString& rStr, SCSIZE nC, SCSIZE nR) +{ + pImpl->PutString(rStr, nC, nR); +} + +void ScMatrix::PutString(const svl::SharedString& rStr, SCSIZE nIndex) +{ + pImpl->PutString(rStr, nIndex); +} + +void ScMatrix::PutString(const svl::SharedString* pArray, size_t nLen, SCSIZE nC, SCSIZE nR) +{ + pImpl->PutString(pArray, nLen, nC, nR); +} + +void ScMatrix::PutEmpty(SCSIZE nC, SCSIZE nR) +{ + pImpl->PutEmpty(nC, nR); +} + +void ScMatrix::PutEmptyPath(SCSIZE nC, SCSIZE nR) +{ + pImpl->PutEmptyPath(nC, nR); +} + +void ScMatrix::PutError( FormulaError nErrorCode, SCSIZE nC, SCSIZE nR ) +{ + pImpl->PutError(nErrorCode, nC, nR); +} + +void ScMatrix::PutBoolean(bool bVal, SCSIZE nC, SCSIZE nR) +{ + pImpl->PutBoolean(bVal, nC, nR); +} + +FormulaError ScMatrix::GetError( SCSIZE nC, SCSIZE nR) const +{ + return pImpl->GetError(nC, nR); +} + +double ScMatrix::GetDouble(SCSIZE nC, SCSIZE nR) const +{ + return pImpl->GetDouble(nC, nR); +} + +double ScMatrix::GetDouble( SCSIZE nIndex) const +{ + return pImpl->GetDouble(nIndex); +} + +double ScMatrix::GetDoubleWithStringConversion(SCSIZE nC, SCSIZE nR) const +{ + return pImpl->GetDoubleWithStringConversion(nC, nR); +} + +svl::SharedString ScMatrix::GetString(SCSIZE nC, SCSIZE nR) const +{ + return pImpl->GetString(nC, nR); +} + +svl::SharedString ScMatrix::GetString( SCSIZE nIndex) const +{ + return pImpl->GetString(nIndex); +} + +svl::SharedString ScMatrix::GetString( SvNumberFormatter& rFormatter, SCSIZE nC, SCSIZE nR) const +{ + return pImpl->GetString(rFormatter, nC, nR); +} + +ScMatrixValue ScMatrix::Get(SCSIZE nC, SCSIZE nR) const +{ + return pImpl->Get(nC, nR); +} + +bool ScMatrix::IsStringOrEmpty( SCSIZE nIndex ) const +{ + return pImpl->IsStringOrEmpty(nIndex); +} + +bool ScMatrix::IsStringOrEmpty( SCSIZE nC, SCSIZE nR ) const +{ + return pImpl->IsStringOrEmpty(nC, nR); +} + +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::IsEmptyResult( SCSIZE nC, SCSIZE nR ) const +{ + return pImpl->IsEmptyResult(nC, nR); +} + +bool ScMatrix::IsEmptyPath( SCSIZE nC, SCSIZE nR ) const +{ + return pImpl->IsEmptyPath(nC, nR); +} + +bool ScMatrix::IsValue( SCSIZE nIndex ) const +{ + return pImpl->IsValue(nIndex); +} + +bool ScMatrix::IsValue( SCSIZE nC, SCSIZE nR ) const +{ + return pImpl->IsValue(nC, nR); +} + +bool ScMatrix::IsValueOrEmpty( SCSIZE nC, SCSIZE nR ) const +{ + return pImpl->IsValueOrEmpty(nC, nR); +} + +bool ScMatrix::IsBoolean( SCSIZE nC, SCSIZE nR ) const +{ + return pImpl->IsBoolean(nC, nR); +} + +bool ScMatrix::IsNumeric() const +{ + return pImpl->IsNumeric(); +} + +void ScMatrix::MatCopy(const ScMatrix& mRes) const +{ + pImpl->MatCopy(*mRes.pImpl); +} + +void ScMatrix::MatTrans(const ScMatrix& mRes) const +{ + pImpl->MatTrans(*mRes.pImpl); +} + +void ScMatrix::FillDouble( double fVal, SCSIZE nC1, SCSIZE nR1, SCSIZE nC2, SCSIZE nR2 ) +{ + pImpl->FillDouble(fVal, nC1, nR1, nC2, nR2); +} + +void ScMatrix::PutDoubleVector( const ::std::vector< double > & rVec, SCSIZE nC, SCSIZE nR ) +{ + pImpl->PutDoubleVector(rVec, nC, nR); +} + +void ScMatrix::PutStringVector( const ::std::vector< svl::SharedString > & rVec, SCSIZE nC, SCSIZE nR ) +{ + pImpl->PutStringVector(rVec, nC, nR); +} + +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); +} + +void ScMatrix::CompareEqual() +{ + pImpl->CompareEqual(); +} + +void ScMatrix::CompareNotEqual() +{ + pImpl->CompareNotEqual(); +} + +void ScMatrix::CompareLess() +{ + pImpl->CompareLess(); +} + +void ScMatrix::CompareGreater() +{ + pImpl->CompareGreater(); +} + +void ScMatrix::CompareLessEqual() +{ + pImpl->CompareLessEqual(); +} + +void ScMatrix::CompareGreaterEqual() +{ + pImpl->CompareGreaterEqual(); +} + +double ScMatrix::And() const +{ + return pImpl->And(); +} + +double ScMatrix::Or() const +{ + return pImpl->Or(); +} + +double ScMatrix::Xor() const +{ + return pImpl->Xor(); +} + +ScMatrix::IterateResult ScMatrix::Sum(bool bTextAsZero, bool bIgnoreErrorValues) const +{ + return pImpl->Sum(bTextAsZero, bIgnoreErrorValues); +} + +ScMatrix::IterateResult ScMatrix::SumSquare(bool bTextAsZero, bool bIgnoreErrorValues) const +{ + return pImpl->SumSquare(bTextAsZero, bIgnoreErrorValues); +} + +ScMatrix::IterateResult ScMatrix::Product(bool bTextAsZero, bool bIgnoreErrorValues) const +{ + return pImpl->Product(bTextAsZero, bIgnoreErrorValues); +} + +size_t ScMatrix::Count(bool bCountStrings, bool bCountErrors, bool bIgnoreEmptyStrings) const +{ + return pImpl->Count(bCountStrings, bCountErrors, bIgnoreEmptyStrings); +} + +size_t ScMatrix::MatchDoubleInColumns(double fValue, size_t nCol1, size_t nCol2) const +{ + return pImpl->MatchDoubleInColumns(fValue, nCol1, nCol2); +} + +size_t ScMatrix::MatchStringInColumns(const svl::SharedString& rStr, size_t nCol1, size_t nCol2) const +{ + return pImpl->MatchStringInColumns(rStr, nCol1, nCol2); +} + +double ScMatrix::GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues ) const +{ + return pImpl->GetMaxValue(bTextAsZero, bIgnoreErrorValues); +} + +double ScMatrix::GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) const +{ + return pImpl->GetMinValue(bTextAsZero, bIgnoreErrorValues); +} + +double ScMatrix::GetGcd() const +{ + return pImpl->GetGcd(); +} + +double ScMatrix::GetLcm() const +{ + return pImpl->GetLcm(); +} + + +ScMatrixRef ScMatrix::CompareMatrix( + sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) const +{ + return pImpl->CompareMatrix(rComp, nMatPos, pOptions); +} + +void ScMatrix::GetDoubleArray( std::vector<double>& rArray, bool bEmptyAsZero ) const +{ + pImpl->GetDoubleArray(rArray, bEmptyAsZero); +} + +void ScMatrix::MergeDoubleArrayMultiply( std::vector<double>& rArray ) const +{ + pImpl->MergeDoubleArrayMultiply(rArray); +} + +namespace matop { + +namespace { + +/** + * COp struct is used in MatOp class to provide (through template specialization) + * different actions for empty entries in a matrix. + */ +template <typename T, typename S> +struct COp {}; + +} + +template <typename T> +struct COp<T, svl::SharedString> +{ + const svl::SharedString& operator()(char, T /*aOp*/, double /*a*/, double /*b*/, const svl::SharedString& rString) const + { + return rString; + } +}; + +template <typename T> +struct COp<T, double> +{ + double operator()(char, T aOp, double a, double b, const svl::SharedString& /*rString*/) const + { + return aOp( a, b); + } +}; + +namespace { + +/** A template for operations where operands are supposed to be numeric. + A non-numeric (string) operand leads to the configured conversion to number + method being called if in interpreter context and a FormulaError::NoValue DoubleError + if conversion was not possible, else to an unconditional FormulaError::NoValue + DoubleError. + An empty operand evaluates to 0. + XXX: semantically TEmptyRes and types other than number_value_type are + unused, but this template could serve as a basis for future enhancements. + */ +template<typename TOp, typename TEmptyRes=double, typename TRet=double> +struct MatOp +{ +private: + TOp maOp; + ScInterpreter* mpErrorInterpreter; + svl::SharedString maString; + double mfVal; + COp<TOp, TEmptyRes> maCOp; + +public: + typedef TRet number_value_type; + + MatOp( TOp aOp, ScInterpreter* pErrorInterpreter, + double fVal = 0.0, const svl::SharedString& rString = svl::SharedString() ): + maOp(aOp), + mpErrorInterpreter(pErrorInterpreter), + maString(rString), + mfVal(fVal) + { + if (mpErrorInterpreter) + { + FormulaError nErr = mpErrorInterpreter->GetError(); + if (nErr != FormulaError::NONE) + mfVal = CreateDoubleError( nErr); + } + } + + TRet operator()(double fVal) const + { + return maOp(fVal, mfVal); + } + + TRet operator()(bool bVal) const + { + return maOp(static_cast<double>(bVal), mfVal); + } + + double operator()(const svl::SharedString& rStr) const + { + return maOp( convertStringToValue( mpErrorInterpreter, rStr.getString()), mfVal); + } + + TEmptyRes operator()(char) const + { + return maCOp({}, maOp, 0, mfVal, maString); + } + + static bool useFunctionForEmpty() + { + return true; + } +}; + +} + +} + +void ScMatrix::NotOp( const ScMatrix& rMat) +{ + auto not_ = [](double a, double){return double(a == 0.0);}; + matop::MatOp<decltype(not_), double> aOp(not_, pImpl->GetErrorInterpreter()); + pImpl->ApplyOperation(aOp, *rMat.pImpl); +} + +void ScMatrix::NegOp( const ScMatrix& rMat) +{ + auto neg_ = [](double a, double){return -a;}; + matop::MatOp<decltype(neg_), double> aOp(neg_, pImpl->GetErrorInterpreter()); + pImpl->ApplyOperation(aOp, *rMat.pImpl); +} + +void ScMatrix::AddOp( double fVal, const ScMatrix& rMat) +{ + auto add_ = [](double a, double b){return a + b;}; + matop::MatOp<decltype(add_)> aOp(add_, pImpl->GetErrorInterpreter(), fVal); + pImpl->ApplyOperation(aOp, *rMat.pImpl); +} + +void ScMatrix::SubOp( bool bFlag, double fVal, const ScMatrix& rMat) +{ + if (bFlag) + { + auto sub_ = [](double a, double b){return b - a;}; + matop::MatOp<decltype(sub_)> aOp(sub_, pImpl->GetErrorInterpreter(), fVal); + pImpl->ApplyOperation(aOp, *rMat.pImpl); + } + else + { + auto sub_ = [](double a, double b){return a - b;}; + matop::MatOp<decltype(sub_)> aOp(sub_, pImpl->GetErrorInterpreter(), fVal); + pImpl->ApplyOperation(aOp, *rMat.pImpl); + } +} + +void ScMatrix::MulOp( double fVal, const ScMatrix& rMat) +{ + auto mul_ = [](double a, double b){return a * b;}; + matop::MatOp<decltype(mul_)> aOp(mul_, pImpl->GetErrorInterpreter(), fVal); + pImpl->ApplyOperation(aOp, *rMat.pImpl); +} + +void ScMatrix::DivOp( bool bFlag, double fVal, const ScMatrix& rMat) +{ + if (bFlag) + { + auto div_ = [](double a, double b){return sc::div(b, a);}; + matop::MatOp<decltype(div_)> aOp(div_, pImpl->GetErrorInterpreter(), fVal); + pImpl->ApplyOperation(aOp, *rMat.pImpl); + } + else + { + auto div_ = [](double a, double b){return sc::div(a, b);}; + matop::MatOp<decltype(div_)> aOp(div_, pImpl->GetErrorInterpreter(), fVal); + pImpl->ApplyOperation(aOp, *rMat.pImpl); + } +} + +void ScMatrix::PowOp( bool bFlag, double fVal, const ScMatrix& rMat) +{ + if (bFlag) + { + auto pow_ = [](double a, double b){return sc::power(b, a);}; + matop::MatOp<decltype(pow_)> aOp(pow_, pImpl->GetErrorInterpreter(), fVal); + pImpl->ApplyOperation(aOp, *rMat.pImpl); + } + else + { + auto pow_ = [](double a, double b){return sc::power(a, b);}; + matop::MatOp<decltype(pow_)> aOp(pow_, pImpl->GetErrorInterpreter(), fVal); + pImpl->ApplyOperation(aOp, *rMat.pImpl); + } +} + +void ScMatrix::ExecuteOperation(const std::pair<size_t, size_t>& rStartPos, + const std::pair<size_t, size_t>& rEndPos, DoubleOpFunction aDoubleFunc, + BoolOpFunction aBoolFunc, StringOpFunction aStringFunc, EmptyOpFunction aEmptyFunc) const +{ + pImpl->ExecuteOperation(rStartPos, rEndPos, aDoubleFunc, aBoolFunc, aStringFunc, aEmptyFunc); +} + +std::vector<ScMatrix::IterateResult> ScMatrix::Collect(const std::vector<sc::op::Op>& aOp) +{ + return pImpl->ApplyCollectOperation(aOp); +} + +#if DEBUG_MATRIX +void ScMatrix::Dump() const +{ + pImpl->Dump(); +} +#endif + +void ScMatrix::MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow, + const ScMatrixRef& xMat1, const ScMatrixRef& xMat2, SvNumberFormatter& rFormatter, svl::SharedStringPool& rPool) +{ + pImpl->MatConcat(nMaxCol, nMaxRow, xMat1, xMat2, rFormatter, rPool); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |