/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if DEBUG_MATRIX #include 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 element_block_func; }; } typedef mdds::multi_type_matrix 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 class CompareMatrixElemFunc { static Comp maComp; std::vector 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 Comp CompareMatrixElemFunc::maComp; } /* TODO: it would be good if mdds had get/set additionally to * get/set, 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& 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::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; 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& rArray, bool bEmptyAsZero ) const; void MergeDoubleArrayMultiply( std::vector& rArray ) const; template void ApplyOperation(T aOp, ScMatrixImpl& rMat); void ExecuteOperation(const std::pair& rStartPos, const std::pair& rEndPos, const ScMatrix::DoubleOpFunction& aDoubleFunc, const ScMatrix::BoolOpFunction& aBoolFunc, const ScMatrix::StringOpFunction& aStringFunc, const ScMatrix::EmptyOpFunction& aEmptyFunc) const; template ScMatrix::IterateResultMultiple ApplyCollectOperation(const std::vector& 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& 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; const 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(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 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 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 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 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 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 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 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 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 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 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(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(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(maMat); } namespace { template class WalkElementBlocks { Op maOp; ScMatrix::IterateResult maRes; bool mbTextAsZero:1; bool mbIgnoreErrorValues:1; public: WalkElementBlocks(bool bTextAsZero, bool bIgnoreErrorValues) : maRes(Op::InitVal, 0), 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; } 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 class WalkElementBlocksMultipleValues { const std::vector* mpOp; ScMatrix::IterateResultMultiple maRes; public: WalkElementBlocksMultipleValues(const std::vector& 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& 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::max(); template 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::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::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::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::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 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 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(eOp)); return CreateDoubleError( FormulaError::UnknownState); } class CompareMatrixFunc { sc::Compare& mrComp; size_t mnMatPos; sc::CompareOptions* mpOptions; std::vector 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& 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 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& getValues() const { return maResValues; } }; class ToDoubleArray { std::vector maArray; std::vector::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& rOther) { maArray.swap(rOther); } }; struct ArrayMul { double operator() (const double& lhs, const double& rhs) const { return lhs * rhs; } }; template class MergeDoubleArrayFunc { std::vector::iterator miPos; double mfNaN; public: MergeDoubleArrayFunc(std::vector& 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 ScMatrix::IterateResult GetValueWithCount(bool bTextAsZero, bool bIgnoreErrorValues, const MatrixImplType& maMat) { WalkElementBlocks aFunc(bTextAsZero, bIgnoreErrorValues); aFunc = maMat.walk(aFunc); return aFunc.getResult(); } } ScMatrix::KahanIterateResult ScMatrixImpl::Sum(bool bTextAsZero, bool bIgnoreErrorValues) const { return GetValueWithCount(bTextAsZero, bIgnoreErrorValues, maMat); } ScMatrix::KahanIterateResult ScMatrixImpl::SumSquare(bool bTextAsZero, bool bIgnoreErrorValues) const { return GetValueWithCount(bTextAsZero, bIgnoreErrorValues, maMat); } ScMatrix::DoubleIterateResult ScMatrixImpl::Product(bool bTextAsZero, bool bIgnoreErrorValues) const { return GetValueWithCount(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 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 aFunc(rStr, maMat.size(), nCol1, nCol2); aFunc = maMat.walk(aFunc); return aFunc.getMatching(); } double ScMatrixImpl::GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues ) const { CalcMaxMinValue aFunc(bTextAsZero, bIgnoreErrorValues); aFunc = maMat.walk(aFunc); return aFunc.getValue(); } double ScMatrixImpl::GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) const { CalcMaxMinValue aFunc(bTextAsZero, bIgnoreErrorValues); aFunc = maMat.walk(aFunc); return aFunc.getValue(); } double ScMatrixImpl::GetGcd() const { CalcGcdLcm aFunc; aFunc = maMat.walk(aFunc); return aFunc.getResult(); } double ScMatrixImpl::GetLcm() const { CalcGcdLcm 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& rResVal = aFunc.getValues(); assert (nSize == rResVal.size()); if (nSize != rResVal.size()) return 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& rResVal = aFunc.getValues(); assert (nSize == rResVal.size()); if (nSize != rResVal.size()) return ScMatrixRef(); return ScMatrixRef(new ScMatrix(aSize.column, aSize.row, rResVal)); } void ScMatrixImpl::GetDoubleArray( std::vector& 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& rArray ) const { MatrixImplType::size_pair_type aSize = maMat.size(); size_t nSize = aSize.row*aSize.column; if (nSize != rArray.size()) return; MergeDoubleArrayFunc aFunc(rArray); maMat.walk(std::move(aFunc)); } namespace { template 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 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 begin() { return wrapped_iterator(m_itBegin, maOp); } wrapped_iterator end() { return wrapped_iterator(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 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 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 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 aFunc(it, itEnd, *mpOp); pos = mrMat.set(pos, aFunc.begin(), aFunc.end()); } break; case mdds::mtm::element_empty: { if (mpOp->useFunctionForEmpty()) { std::vector aVec(node.size); MatrixIteratorWrapper, 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 void ScMatrixImpl::ApplyOperation(T aOp, ScMatrixImpl& rMat) { MatrixOpWrapper aFunc(rMat.maMat, aOp); maMat.walk(aFunc); } template ScMatrix::IterateResultMultiple ScMatrixImpl::ApplyCollectOperation(const std::vector& aOp) { WalkElementBlocksMultipleValues 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& rStartPos, const std::pair& 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 aString(nMaxCol * nMaxRow); std::vector aValid(nMaxCol * nMaxRow, true); std::vector nErrors(nMaxCol * nMaxRow,FormulaError::NONE); size_t nRowOffset = 0; size_t nColOffset = 0; std::function 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 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 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 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(0, 0), std::pair(std::min(nR1, nMaxRow) - 1, std::min(nC1, nMaxCol) - 1), aDoubleFunc, aBoolFunc, aStringFunc, aEmptyFunc); } } } else xMat1->ExecuteOperation( std::pair(0, 0), std::pair(nMaxRow - 1, nMaxCol - 1), aDoubleFunc, aBoolFunc, aStringFunc, aEmptyFunc); std::vector aSharedString(nMaxCol*nMaxRow); std::function 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 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 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 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(0, 0), std::pair(std::min(nR2, nMaxRow) - 1, std::min(nC2, nMaxCol) - 1), aDoubleFunc2, aBoolFunc2, aStringFunc2, aEmptyFunc2); } } } else xMat2->ExecuteOperation( std::pair(0, 0), std::pair(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& 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::KahanIterateResult ScMatrix::Sum(bool bTextAsZero, bool bIgnoreErrorValues) const { return pImpl->Sum(bTextAsZero, bIgnoreErrorValues); } ScMatrix::KahanIterateResult ScMatrix::SumSquare(bool bTextAsZero, bool bIgnoreErrorValues) const { return pImpl->SumSquare(bTextAsZero, bIgnoreErrorValues); } ScMatrix::DoubleIterateResult 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& rArray, bool bEmptyAsZero ) const { pImpl->GetDoubleArray(rArray, bEmptyAsZero); } void ScMatrix::MergeDoubleArrayMultiply( std::vector& rArray ) const { pImpl->MergeDoubleArrayMultiply(rArray); } namespace matop { 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. */ template struct MatOp { private: TOp maOp; ScInterpreter* mpErrorInterpreter; double mfVal; public: typedef double number_value_type; MatOp( TOp aOp, ScInterpreter* pErrorInterpreter, double fVal = 0.0 ): maOp(aOp), mpErrorInterpreter(pErrorInterpreter), mfVal(fVal) { if (mpErrorInterpreter) { FormulaError nErr = mpErrorInterpreter->GetError(); if (nErr != FormulaError::NONE) mfVal = CreateDoubleError( nErr); } } double operator()(double fVal) const { return maOp(fVal, mfVal); } double operator()(bool bVal) const { return maOp(static_cast(bVal), mfVal); } double operator()(const svl::SharedString& rStr) const { return maOp( convertStringToValue( mpErrorInterpreter, rStr.getString()), mfVal); } /// the action for empty entries in a matrix double operator()(char) const { return maOp(0, mfVal); } static bool useFunctionForEmpty() { return true; } }; } } void ScMatrix::NotOp( const ScMatrix& rMat) { auto not_ = [](double a, double){return double(a == 0.0);}; matop::MatOp aOp(not_, pImpl->GetErrorInterpreter()); pImpl->ApplyOperation(aOp, *rMat.pImpl); } void ScMatrix::NegOp( const ScMatrix& rMat) { auto neg_ = [](double a, double){return -a;}; matop::MatOp 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 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 aOp(sub_, pImpl->GetErrorInterpreter(), fVal); pImpl->ApplyOperation(aOp, *rMat.pImpl); } else { auto sub_ = [](double a, double b){return a - b;}; matop::MatOp 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 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 aOp(div_, pImpl->GetErrorInterpreter(), fVal); pImpl->ApplyOperation(aOp, *rMat.pImpl); } else { auto div_ = [](double a, double b){return sc::div(a, b);}; matop::MatOp 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 aOp(pow_, pImpl->GetErrorInterpreter(), fVal); pImpl->ApplyOperation(aOp, *rMat.pImpl); } else { auto pow_ = [](double a, double b){return sc::power(a, b);}; matop::MatOp aOp(pow_, pImpl->GetErrorInterpreter(), fVal); pImpl->ApplyOperation(aOp, *rMat.pImpl); } } void ScMatrix::ExecuteOperation(const std::pair& rStartPos, const std::pair& rEndPos, DoubleOpFunction aDoubleFunc, BoolOpFunction aBoolFunc, StringOpFunction aStringFunc, EmptyOpFunction aEmptyFunc) const { pImpl->ExecuteOperation(rStartPos, rEndPos, aDoubleFunc, aBoolFunc, aStringFunc, aEmptyFunc); } ScMatrix::KahanIterateResultMultiple ScMatrix::CollectKahan(const std::vector& 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: */