/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <scmatrix.hxx>
#include <global.hxx>
#include <address.hxx>
#include <formula/errorcodes.hxx>
#include <interpre.hxx>
#include <mtvelements.hxx>
#include <compare.hxx>
#include <matrixoperators.hxx>
#include <math.hxx>
#include <jumpmatrix.hxx>
#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <svl/sharedstring.hxx>
#include <rtl/math.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <memory>
#include <mutex>
#include <utility>
#include <vector>
#include <limits>
#include <mdds/multi_type_matrix.hpp>
#include <mdds/multi_type_vector/types.hpp>
#if DEBUG_MATRIX
#include <iostream>
using std::cout;
using std::endl;
#endif
using ::std::pair;
using ::std::advance;
namespace {
/**
* Custom string trait struct to tell mdds::multi_type_matrix about the
* custom string type and how to handle blocks storing them.
*/
struct matrix_traits
{
typedef sc::string_block string_element_block;
typedef sc::uint16_block integer_element_block;
};
struct matrix_flag_traits
{
typedef sc::string_block string_element_block;
typedef mdds::mtv::uint8_element_block integer_element_block;
};
}
typedef mdds::multi_type_matrix<matrix_traits> MatrixImplType;
typedef mdds::multi_type_matrix<matrix_flag_traits> MatrixFlagImplType;
namespace {
double convertStringToValue( ScInterpreter* pErrorInterpreter,
const OUString& rSt
r )
{
if (pErrorInterpreter)
{
FormulaError nError = FormulaError::NONE;
SvNumFormatType nCurFmtType = SvNumFormatType::ALL;
double fValue = pErrorInterpreter->ConvertStringToValue( rStr, nError, nCurFmtType);
if (nError != FormulaError::NONE)
{
pErrorInterpreter->SetError( nError);
return CreateDoubleError( nError);
}
return fValue;
}
return CreateDoubleError( FormulaError::NoValue);
}
struct ElemEqualZero
{
double operator () (double val) const
{
if (!std::isfinite(val))
return val;
return val == 0.0 ? 1.0 : 0.0;
}
};
struct ElemNotEqualZero
{
double operator () (double val) const
{
if (!std::isfinite(val))
return val;
return val != 0.0 ? 1.0 : 0.0;
}
};
struct ElemGreaterZero
{
double operator () (double val) const
{
if (!std::isfinite(val))
return val;
return val > 0.0 ? 1.0 : 0.0;
}
};
struct ElemLessZero
{
double operator () (double val) const
{
if (!std::isfinite(val))
return val;
return val < 0.0 ? 1.0 : 0.0;
}
};
struct ElemGreaterEqualZero
{
double operator () (double val) const
{
if (!std::isfinite(val))
return val;
return val >= 0.0 ? 1.0 : 0.0;
}
};
struct ElemLessEqualZero
{
double operator () (double val) const
{
if (!std::isfinite(val))
return val;
return val <= 0.0 ? 1.0 : 0.0;
}
};
template <typename Comp>
class CompareMatrixElemFunc
{
static Comp maComp;
std::vector<double > maNewMatValues; // double instead of bool to transport error values
size_t mnRow;
size_t mnCol;
public :
CompareMatrixElemFunc( size_t nRow, size_t nCol ) : mnRow(nRow), mnCol(nCol)
{
maNewMatValues.reserve(nRow*nCol);
}
CompareMatrixElemFunc( const CompareMatrixElemFunc& ) = delete ;
CompareMatrixElemFunc& operator = ( const CompareMatrixElemFunc& ) = delete ;
CompareMatrixElemFunc( CompareMatrixElemFunc&& ) = default ;
CompareMatrixElemFunc& operator = ( CompareMatrixElemFunc&& ) = default ;
void operator () (const MatrixImplType::element_block_node_type& node)
{
switch (node.type)
{
case mdds::mtm::element_numeric:
{
typedef MatrixImplType::numeric_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
double fVal = *it;
maNewMatValues.push_back(maComp(fVal));
}
}
break ;
case mdds::mtm::element_boolean:
{
typedef MatrixImplType::boolean_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
double fVal = *it ? 1.0 : 0.0;
maNewMatValues.push_back(maComp(fVal));
}
}
break ;
case mdds::mtm::element_string:
case mdds::mtm::element_empty:
default :
// Fill it with false.
maNewMatValues.resize(maNewMatValues.size() + node.size, 0.0);
}
}
void swap( MatrixImplType& rMat )
{
MatrixImplType aNewMat(mnRow, mnCol, maNewMatValues.begin(), maNewMatValues.end());
rMat.swap(aNewMat);
}
};
template <typename Comp>
Comp CompareMatrixElemFunc<Comp>::maComp;
}
typedef uint8_t TMatFlag;
const TMatFlag SC_MATFLAG_EMPTYRESULT = 1;
const TMatFlag SC_MATFLAG_EMPTYPATH = 2;
class ScMatrixImpl
{
MatrixImplType maMat;
MatrixFlagImplType maMatFlag;
ScInterpreter* pErrorInterpreter;
public :
ScMatrixImpl(const ScMatrixImpl&) = delete ;
const ScMatrixImpl& operator =(const ScMatrixImpl&) = delete ;
ScMatrixImpl(SCSIZE nC, SCSIZE nR);
ScMatrixImpl(SCSIZE nC, SCSIZE nR, double fInitVal);
ScMatrixImpl( size_t nC, size_t nR, const std::vector<double >& rInitVals );
~ScMatrixImpl();
void Clear();
void Resize(SCSIZE nC, SCSIZE nR);
void Resize(SCSIZE nC, SCSIZE nR, double fVal);
void SetErrorInterpreter( ScInterpreter* p);
ScInterpreter* GetErrorInterpreter() const { return pErrorInterpreter; }
void GetDimensions( SCSIZE& rC, SCSIZE& rR) const ;
SCSIZE GetElementCount() const ;
bool ValidColRow( SCSIZE nC, SCSIZE nR) const ;
bool ValidColRowReplicated( SCSIZE & rC, SCSIZE & rR ) const ;
bool ValidColRowOrReplicated( SCSIZE & rC, SCSIZE & rR ) const ;
void SetErrorAtInterpreter( FormulaError nError ) const ;
void PutDouble(double fVal, SCSIZE nC, SCSIZE nR);
void PutDouble( double fVal, SCSIZE nIndex);
void PutDoubleTrans( double fVal, SCSIZE nIndex);
void PutDouble(const double * pArray, size_t nLen, SCSIZE nC, SCSIZE nR);
void PutString(const svl::SharedString& rStr, SCSIZE nC, SCSIZE nR);
void PutString(const svl::SharedString& rStr, SCSIZE nIndex);
void PutStringTrans(const svl::SharedString& rStr, SCSIZE nIndex);
void PutString(const svl::SharedString* pArray, size_t nLen, SCSIZE nC, SCSIZE nR);
void PutEmpty(SCSIZE nC, SCSIZE nR);
void PutEmpty(SCSIZE nIndex);
void PutEmptyTrans(SCSIZE nIndex);
void PutEmptyPath(SCSIZE nC, SCSIZE nR);
void PutError( FormulaError nErrorCode, SCSIZE nC, SCSIZE nR );
void PutBoolean(bool bVal, SCSIZE nC, SCSIZE nR);
FormulaError GetError( SCSIZE nC, SCSIZE nR) const ;
double GetDouble(SCSIZE nC, SCSIZE nR) const ;
double GetDouble( SCSIZE nIndex) const ;
double GetDoubleWithStringConversion(SCSIZE nC, SCSIZE nR) const ;
svl::SharedString GetString(SCSIZE nC, SCSIZE nR) const ;
svl::SharedString GetString( SCSIZE nIndex) const ;
svl::SharedString GetString( ScInterpreterContext& rContext, SCSIZE nC, SCSIZE nR) const ;
ScMatrixValue Get(SCSIZE nC, SCSIZE nR) const ;
bool IsStringOrEmpty( SCSIZE nIndex ) const ;
bool IsStringOrEmpty( SCSIZE nC, SCSIZE nR ) const ;
bool IsEmpty( SCSIZE nC, SCSIZE nR ) const ;
bool IsEmptyCell( SCSIZE nC, SCSIZE nR ) const ;
bool IsEmptyResult( SCSIZE nC, SCSIZE nR ) const ;
bool IsEmptyPath( SCSIZE nC, SCSIZE nR ) const ;
bool IsValue( SCSIZE nIndex ) const ;
bool IsValue( SCSIZE nC, SCSIZE nR ) const ;
bool IsValueOrEmpty( SCSIZE nC, SCSIZE nR ) const ;
bool IsBoolean( SCSIZE nC, SCSIZE nR ) const ;
bool IsNumeric() const ;
void MatCopy(ScMatrixImpl& mRes) const ;
void MatTrans(ScMatrixImpl& mRes) const ;
void FillDouble( double fVal, SCSIZE nC1, SCSIZE nR1, SCSIZE nC2, SCSIZE nR2 );
void PutDoubleVector( const ::std::vector< double > & rVec, SCSIZE nC, SCSIZE nR );
void PutStringVector( const ::std::vector< svl::SharedString > & rVec, SCSIZE nC, SCSIZE nR );
void PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );
void PutEmptyResultVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );
void PutEmptyPathVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );
void CompareEqual();
void CompareNotEqual();
void CompareLess();
void CompareGreater();
void CompareLessEqual();
void CompareGreaterEqual();
double And () const ;
double Or () const ;
double Xor () const ;
ScMatrix::KahanIterateResult Sum( bool bTextAsZero, bool bIgnoreErrorValues ) const ;
ScMatrix::KahanIterateResult SumSquare( bool bTextAsZero, bool bIgnoreErrorValues ) const ;
ScMatrix::DoubleIterateResult Product( bool bTextAsZero, bool bIgnoreErrorValues ) const ;
size_t Count(bool bCountStrings, bool bCountErrors, bool bIgnoreEmptyStrings) const ;
size_t MatchDoubleInColumns(double fValue, size_t nCol1, size_t nCol2) const ;
size_t MatchStringInColumns(const svl::SharedString& rStr, size_t nCol1, size_t nCol2) const ;
void IfJump( ScJumpMatrix& rJumpMatrix, const short * pJump, short nJumpCount ) const ;
double GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues ) const ;
double GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) const ;
double GetGcd() const ;
double GetLcm() const ;
ScMatrixRef CompareMatrix( sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) const ;
void GetDoubleArray( std::vector<double >& rArray, bool bEmptyAsZero ) const ;
void MergeDoubleArrayMultiply( std::vector<double >& rArray ) const ;
template <typename T>
void ApplyOperation(T aOp, ScMatrixImpl& rMat);
void ExecuteOperation(const std::pair<size_t, size_t>& rStartPos,
const std::pair<size_t, size_t>& rEndPos, const ScMatrix::DoubleOpFunction& aDoubleFunc,
const ScMatrix::BoolOpFunction& aBoolFunc, const ScMatrix::StringOpFunction& aStringFunc,
const ScMatrix::EmptyOpFunction& aEmptyFunc) const ;
template <typename T, typename tRes>
ScMatrix::IterateResultMultiple<tRes> ApplyCollectOperation(const std::vector<T>& aOp);
void MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrixRef& xMat1, const ScMatrixRef& xMat2,
ScInterpreterContext& rContext, svl::SharedStringPool& rPool);
void ExecuteBinaryOp(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrix& rInputMat1, const ScMatrix& rInputMat2,
ScInterpreter* pInterpreter, const ScMatrix::CalculateOpFunction& op);
bool IsValueOrEmpty( const MatrixImplType::const_position_type & rPos ) const ;
double GetDouble( const MatrixImplType::const_position_type & rPos) const ;
FormulaError GetErrorIfNotString( const MatrixImplType::const_position_type & rPos ) const ;
bool IsValue( const MatrixImplType::const_position_type & rPos ) const ;
FormulaError GetError(const MatrixImplType::const_position_type & rPos) const ;
bool IsStringOrEmpty(const MatrixImplType::const_position_type & rPos) const ;
svl::SharedString GetString(const MatrixImplType::const_position_type& rPos) const ;
#if DEBUG_MATRIX
void Dump() const ;
#endif
private :
void CalcPosition(SCSIZE nIndex, SCSIZE& rC, SCSIZE& rR) const ;
void CalcTransPosition(SCSIZE nIndex, SCSIZE& rC, SCSIZE& rR) const ;
};
static std::once_flag bElementsMaxFetched;
static std::atomic<size_t> nElementsMax;
/** The maximum number of elements a matrix or the pool may have at runtime.
@param nMemory
If 0, the arbitrary limit of one matrix is returned.
If >0, the given memory pool divided by the average size of a
matrix element is returned, which is used to initialize
nElementsMax.
*/
static size_t GetElementsMax( size_t nMemory )
{
// Arbitrarily assuming 12 bytes per element, 8 bytes double plus
// overhead. Stored as an array in an mdds container it's less, but for
// strings or mixed matrix it can be much more...
constexpr size_t nPerElem = 12;
if (nMemory)
return nMemory / nPerElem;
// Arbitrarily assuming 1GB memory. Could be dynamic at some point.
constexpr size_t nMemMax = 0x40000000;
// With 1GB that's ~85M elements, or 85 whole columns.
constexpr size_t nElemMax = nMemMax / nPerElem;
// With MAXROWCOUNT==1048576 and 128 columns => 128M elements, 1.5GB
constexpr size_t nArbitraryLimit = size_t(MAXROWCOUNT) * 128;
// With the constant 1GB from above that's the actual value.
return std::min(nElemMax, nArbitraryLimit);
}
ScMatrixImpl::ScMatrixImpl(SCSIZE nC, SCSIZE nR) :
maMat(nR, nC), maMatFlag(nR, nC), pErrorInterpreter(nullptr)
{
nElementsMax -= GetElementCount();
}
ScMatrixImpl::ScMatrixImpl(SCSIZE nC, SCSIZE nR, double fInitVal) :
maMat(nR, nC, fInitVal), maMatFlag(nR, nC), pErrorInterpreter(nullptr)
{
nElementsMax -= GetElementCount();
}
ScMatrixImpl::ScMatrixImpl( size_t nC, size_t nR, const std::vector<double >& rInitVals ) :
maMat(nR, nC, rInitVals.begin(), rInitVals.end()), maMatFlag(nR, nC), pErrorInterpreter(nullptr)
{
nElementsMax -= GetElementCount();
}
ScMatrixImpl::~ScMatrixImpl()
{
nElementsMax += GetElementCount();
suppress_fun_call_w_exception(Clear());
}
void ScMatrixImpl::Clear()
{
suppress_fun_call_w_exception(maMat.clear());
maMatFlag.clear();
}
void ScMatrixImpl::Resize(SCSIZE nC, SCSIZE nR)
{
nElementsMax += GetElementCount();
if (ScMatrix::IsSizeAllocatable( nC, nR))
{
maMat.resize(nR, nC);
maMatFlag.resize(nR, nC);
}
else
{
// Invalid matrix size, allocate 1x1 matrix with error value.
maMat.resize(1, 1, CreateDoubleError( FormulaError::MatrixSize));
maMatFlag.resize(1, 1);
}
nElementsMax -= GetElementCount();
}
void ScMatrixImpl::Resize(SCSIZE nC, SCSIZE nR, double fVal)
{
nElementsMax += GetElementCount();
if (ScMatrix::IsSizeAllocatable( nC, nR))
{
maMat.resize(nR, nC, fVal);
maMatFlag.resize(nR, nC);
}
else
{
// Invalid matrix size, allocate 1x1 matrix with error value.
maMat.resize(1, 1, CreateDoubleError( FormulaError::StackOverflow));
maMatFlag.resize(1, 1);
}
nElementsMax -= GetElementCount();
}
void ScMatrixImpl::SetErrorInterpreter( ScInterpreter* p)
{
pErrorInterpreter = p;
}
void ScMatrixImpl::GetDimensions( SCSIZE& rC, SCSIZE& rR) const
{
MatrixImplType::size_pair_type aSize = maMat.size();
rR = aSize.row;
rC = aSize.column;
}
SCSIZE ScMatrixImpl::GetElementCount() const
{
MatrixImplType::size_pair_type aSize = maMat.size();
return aSize.row * aSize.column;
}
bool ScMatrixImpl::ValidColRow( SCSIZE nC, SCSIZE nR) const
{
MatrixImplType::size_pair_type aSize = maMat.size();
return nR < aSize.row && nC < aSize.column;
}
bool ScMatrixImpl::ValidColRowReplicated( SCSIZE & rC, SCSIZE & rR ) const
{
MatrixImplType::size_pair_type aSize = maMat.size();
if (aSize.column == 1 && aSize.row == 1)
{
rC = 0;
rR = 0;
return true ;
}
else if (aSize.column == 1 && rR < aSize.row)
{
// single column matrix.
rC = 0;
return true ;
}
else if (aSize.row == 1 && rC < aSize.column)
{
// single row matrix.
rR = 0;
return true ;
}
return false ;
}
bool ScMatrixImpl::ValidColRowOrReplicated( SCSIZE & rC, SCSIZE & rR ) const
{
return ValidColRow( rC, rR) || ValidColRowReplicated( rC, rR);
}
void ScMatrixImpl::SetErrorAtInterpreter( FormulaError nError ) const
{
if ( pErrorInterpreter )
pErrorInterpreter->SetError( nError);
}
void ScMatrixImpl::PutDouble(double fVal, SCSIZE nC, SCSIZE nR)
{
if (ValidColRow( nC, nR))
maMat.set(nR, nC, fVal);
else
{
OSL_FAIL("ScMatrixImpl::PutDouble: dimension error" );
}
}
void ScMatrixImpl::PutDouble(const double * pArray, size_t nLen, SCSIZE nC, SCSIZE nR)
{
if (ValidColRow( nC, nR))
maMat.set(nR, nC, pArray, pArray + nLen);
else
{
OSL_FAIL("ScMatrixImpl::PutDouble: dimension error" );
}
}
void ScMatrixImpl::PutDouble( double fVal, SCSIZE nIndex)
{
SCSIZE nC, nR;
CalcPosition(nIndex, nC, nR);
PutDouble(fVal, nC, nR);
}
void ScMatrixImpl::PutDoubleTrans(double fVal, SCSIZE nIndex)
{
SCSIZE nC, nR;
CalcTransPosition(nIndex, nC, nR);
PutDouble(fVal, nC, nR);
}
void ScMatrixImpl::PutString(const svl::SharedString& rStr, SCSIZE nC, SCSIZE nR)
{
if (ValidColRow( nC, nR))
maMat.set(nR, nC, rStr);
else
{
OSL_FAIL("ScMatrixImpl::PutString: dimension error" );
}
}
void ScMatrixImpl::PutString(const svl::SharedString* pArray, size_t nLen, SCSIZE nC, SCSIZE nR)
{
if (ValidColRow( nC, nR))
maMat.set(nR, nC, pArray, pArray + nLen);
else
{
OSL_FAIL("ScMatrixImpl::PutString: dimension error" );
}
}
void ScMatrixImpl::PutString(const svl::SharedString& rStr, SCSIZE nIndex)
{
SCSIZE nC, nR;
CalcPosition(nIndex, nC, nR);
PutString(rStr, nC, nR);
}
void ScMatrixImpl::PutStringTrans(const svl::SharedString& rStr, SCSIZE nIndex)
{
SCSIZE nC, nR;
CalcTransPosition(nIndex, nC, nR);
PutString(rStr, nC, nR);
}
void ScMatrixImpl::PutEmpty(SCSIZE nC, SCSIZE nR)
{
if (ValidColRow( nC, nR))
{
maMat.set_empty(nR, nC);
maMatFlag.set_empty(nR, nC);
}
else
{
OSL_FAIL("ScMatrixImpl::PutEmpty: dimension error" );
}
}
void ScMatrixImpl::PutEmpty(SCSIZE nIndex)
{
SCSIZE nC, nR;
CalcPosition(nIndex, nC, nR);
PutEmpty(nC, nR);
}
void ScMatrixImpl::PutEmptyTrans(SCSIZE nIndex)
{
SCSIZE nC, nR;
CalcTransPosition(nIndex, nC, nR);
PutEmpty(nC, nR);
}
void ScMatrixImpl::PutEmptyPath(SCSIZE nC, SCSIZE nR)
{
if (ValidColRow( nC, nR))
{
maMat.set_empty(nR, nC);
#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 && __cplusplus == 202002L
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#endif
maMatFlag.set(nR, nC, SC_MATFLAG_EMPTYPATH);
#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 && __cplusplus == 202002L
#pragma GCC diagnostic pop
#endif
}
else
{
OSL_FAIL("ScMatrixImpl::PutEmptyPath: dimension error" );
}
}
void ScMatrixImpl::PutError( FormulaError nErrorCode, SCSIZE nC, SCSIZE nR )
{
maMat.set(nR, nC, CreateDoubleError(nErrorCode));
}
void ScMatrixImpl::PutBoolean(bool bVal, SCSIZE nC, SCSIZE nR)
{
if (ValidColRow( nC, nR))
maMat.set(nR, nC, bVal);
else
{
OSL_FAIL("ScMatrixImpl::PutBoolean: dimension error" );
}
}
FormulaError ScMatrixImpl::GetError( SCSIZE nC, SCSIZE nR) const
{
if (ValidColRowOrReplicated( nC, nR ))
{
double fVal = maMat.get_numeric(nR, nC);
return GetDoubleErrorValue(fVal);
}
else
{
OSL_FAIL("ScMatrixImpl::GetError: dimension error" );
return FormulaError::NoValue;
}
}
double ScMatrixImpl::GetDouble(SCSIZE nC, SCSIZE nR) const
{
if (ValidColRowOrReplicated( nC, nR ))
{
double fVal = maMat.get_numeric(nR, nC);
if ( pErrorInterpreter )
{
FormulaError nError = GetDoubleErrorValue(fVal);
if ( nError != FormulaError::NONE )
SetErrorAtInterpreter( nError);
}
return fVal;
}
else
{
OSL_FAIL("ScMatrixImpl::GetDouble: dimension error" );
return CreateDoubleError( FormulaError::NoValue);
}
}
double ScMatrixImpl::GetDouble( SCSIZE nIndex) const
{
SCSIZE nC, nR;
CalcPosition(nIndex, nC, nR);
return GetDouble(nC, nR);
}
double ScMatrixImpl::GetDoubleWithStringConversion(SCSIZE nC, SCSIZE nR) const
{
ScMatrixValue aMatVal = Get(nC, nR);
if (aMatVal.nType == ScMatValType::String)
return convertStringToValue( pErrorInterpreter, aMatVal.aStr.getString());
return aMatVal.fVal;
}
svl::SharedString ScMatrixImpl::GetString(SCSIZE nC, SCSIZE nR) const
{
if (ValidColRowOrReplicated( nC, nR ))
{
return GetString(maMat.position(nR, nC));
}
else
{
OSL_FAIL("ScMatrixImpl::GetString: dimension error" );
}
return svl::SharedString::getEmptyString();
}
svl::SharedString ScMatrixImpl::GetString(const MatrixImplType::const_position_type& rPos) const
{
double fErr = 0.0;
switch (maMat.get_type(rPos))
{
case mdds::mtm::element_string:
return maMat.get_string(rPos);
case mdds::mtm::element_empty:
return svl::SharedString::getEmptyString();
case mdds::mtm::element_numeric:
case mdds::mtm::element_boolean:
fErr = maMat.get_numeric(rPos);
[[fallthrough]];
default :
OSL_FAIL("ScMatrixImpl::GetString: access error, no string" );
}
SetErrorAtInterpreter(GetDoubleErrorValue(fErr));
return svl::SharedString::getEmptyString();
}
svl::SharedString ScMatrixImpl::GetString( SCSIZE nIndex) const
{
SCSIZE nC, nR;
CalcPosition(nIndex, nC, nR);
return GetString(nC, nR);
}
svl::SharedString ScMatrixImpl::GetString( ScInterpreterContext& rContext, SCSIZE nC, SCSIZE nR) const
{
if (!ValidColRowOrReplicated( nC, nR ))
{
OSL_FAIL("ScMatrixImpl::GetString: dimension error" );
return svl::SharedString::getEmptyString();
}
double fVal = 0.0;
MatrixImplType::const_position_type aPos = maMat.position(nR, nC);
switch (maMat.get_type(aPos))
{
case mdds::mtm::element_string:
return maMat.get_string(aPos);
case mdds::mtm::element_empty:
{
if (maMatFlag.get<uint8_t>(nR, nC) != SC_MATFLAG_EMPTYPATH)
// not an empty path.
return svl::SharedString::getEmptyString();
// result of empty FALSE jump path
sal_uInt32 nKey = rContext.NFGetStandardFormat( SvNumFormatType::LOGICAL,
ScGlobal::eLnge);
OUString aStr;
const Color* pColor = nullptr;
rContext.NFGetOutputString( 0.0, nKey, aStr, &pColor);
return svl::SharedString( aStr); // string not interned
}
case mdds::mtm::element_numeric:
case mdds::mtm::element_boolean:
fVal = maMat.get_numeric(aPos);
break ;
default :
;
}
FormulaError nError = GetDoubleErrorValue(fVal);
if (nError != FormulaError::NONE)
{
SetErrorAtInterpreter( nError);
return svl::SharedString( ScGlobal::GetErrorString( nError)); // string not interned
}
sal_uInt32 nKey = rContext.NFGetStandardFormat( SvNumFormatType::NUMBER,
ScGlobal::eLnge);
return svl::SharedString(rContext.NFGetInputLineString( fVal, nKey )); // string not interned
}
ScMatrixValue ScMatrixImpl::Get(SCSIZE nC, SCSIZE nR) const
{
ScMatrixValue aVal;
if (ValidColRowOrReplicated(nC, nR))
{
MatrixImplType::const_position_type aPos = maMat.position(nR, nC);
mdds::mtm::element_t eType = maMat.get_type(aPos);
switch (eType)
{
case mdds::mtm::element_boolean:
aVal.nType = ScMatValType::Boolean;
aVal.fVal = double (maMat.get_boolean(aPos));
break ;
case mdds::mtm::element_numeric:
aVal.nType = ScMatValType::Value;
aVal.fVal = maMat.get_numeric(aPos);
break ;
case mdds::mtm::element_string:
aVal.nType = ScMatValType::String;
aVal.aStr = maMat.get_string(aPos);
break ;
case mdds::mtm::element_empty:
/* TODO: do we need to pass the differentiation of 'empty' and
* 'empty result' to the outer world anywhere? */
switch (maMatFlag.get_type(nR, nC))
{
case mdds::mtm::element_empty:
aVal.nType = ScMatValType::Empty;
break ;
case mdds::mtm::element_integer:
aVal.nType = maMatFlag.get<uint8_t>(nR, nC)
== SC_MATFLAG_EMPTYPATH ? ScMatValType::EmptyPath : ScMatValType::Empty;
break ;
default :
assert(false );
}
aVal.fVal = 0.0;
break ;
default :
;
}
}
else
{
OSL_FAIL("ScMatrixImpl::Get: dimension error" );
}
return aVal;
}
bool ScMatrixImpl::IsStringOrEmpty( SCSIZE nIndex ) const
{
SCSIZE nC, nR;
CalcPosition(nIndex, nC, nR);
return IsStringOrEmpty(nC, nR);
}
bool ScMatrixImpl::IsStringOrEmpty( SCSIZE nC, SCSIZE nR ) const
{
if (!ValidColRowOrReplicated( nC, nR ))
return false ;
switch (maMat.get_type(nR, nC))
{
case mdds::mtm::element_empty:
case mdds::mtm::element_string:
return true ;
default :
;
}
return false ;
}
bool ScMatrixImpl::IsEmpty( SCSIZE nC, SCSIZE nR ) const
{
if (!ValidColRowOrReplicated( nC, nR ))
return false ;
// Flag must indicate an 'empty' or 'empty cell' or 'empty result' element,
// but not an 'empty path' element.
return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
maMatFlag.get_integer(nR, nC) != SC_MATFLAG_EMPTYPATH;
}
bool ScMatrixImpl::IsEmptyCell( SCSIZE nC, SCSIZE nR ) const
{
if (!ValidColRowOrReplicated( nC, nR ))
return false ;
// Flag must indicate an 'empty cell' element instead of an
// 'empty' or 'empty result' or 'empty path' element.
return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
maMatFlag.get_type(nR, nC) == mdds::mtm::element_empty;
}
bool ScMatrixImpl::IsEmptyResult( SCSIZE nC, SCSIZE nR ) const
{
if (!ValidColRowOrReplicated( nC, nR ))
return false ;
// Flag must indicate an 'empty result' element instead of an
// 'empty' or 'empty cell' or 'empty path' element.
return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
maMatFlag.get_integer(nR, nC) == SC_MATFLAG_EMPTYRESULT;
}
bool ScMatrixImpl::IsEmptyPath( SCSIZE nC, SCSIZE nR ) const
{
// Flag must indicate an 'empty path' element.
if (ValidColRowOrReplicated( nC, nR ))
return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
maMatFlag.get_integer(nR, nC) == SC_MATFLAG_EMPTYPATH;
else
return true ;
}
bool ScMatrixImpl::IsValue( SCSIZE nIndex ) const
{
SCSIZE nC, nR;
CalcPosition(nIndex, nC, nR);
return IsValue(nC, nR);
}
bool ScMatrixImpl::IsValue( SCSIZE nC, SCSIZE nR ) const
{
if (!ValidColRowOrReplicated( nC, nR ))
return false ;
switch (maMat.get_type(nR, nC))
{
case mdds::mtm::element_boolean:
case mdds::mtm::element_numeric:
return true ;
default :
;
}
return false ;
}
bool ScMatrixImpl::IsValueOrEmpty( SCSIZE nC, SCSIZE nR ) const
{
if (!ValidColRowOrReplicated( nC, nR ))
return false ;
switch (maMat.get_type(nR, nC))
{
case mdds::mtm::element_boolean:
case mdds::mtm::element_numeric:
case mdds::mtm::element_empty:
return true ;
default :
;
}
return false ;
}
bool ScMatrixImpl::IsBoolean( SCSIZE nC, SCSIZE nR ) const
{
if (!ValidColRowOrReplicated( nC, nR ))
return false ;
return maMat.get_type(nR, nC) == mdds::mtm::element_boolean;
}
bool ScMatrixImpl::IsNumeric() const
{
return maMat.numeric();
}
void ScMatrixImpl::MatCopy(ScMatrixImpl& mRes) const
{
if (maMat.size().row > mRes.maMat.size().row || maMat.size().column > mRes.maMat.size().column)
{
// destination matrix is not large enough.
OSL_FAIL("ScMatrixImpl::MatCopy: dimension error" );
return ;
}
mRes.maMat.copy(maMat);
}
void ScMatrixImpl::MatTrans(ScMatrixImpl& mRes) const
{
mRes.maMat = maMat;
mRes.maMat.transpose();
}
void ScMatrixImpl::FillDouble( double fVal, SCSIZE nC1, SCSIZE nR1, SCSIZE nC2, SCSIZE nR2 )
{
if (ValidColRow( nC1, nR1) && ValidColRow( nC2, nR2))
{
for (SCSIZE j = nC1; j <= nC2; ++j)
{
// Passing value array is much faster.
std::vector<double > aVals(nR2-nR1+1, fVal);
maMat.set(nR1, j, aVals.begin(), aVals.end());
}
}
else
{
OSL_FAIL("ScMatrixImpl::FillDouble: dimension error" );
}
}
void ScMatrixImpl::PutDoubleVector( const ::std::vector< double > & rVec, SCSIZE nC, SCSIZE nR )
{
if (!rVec.empty() && ValidColRow( nC, nR) && ValidColRow( nC, nR + rVec.size() - 1))
{
maMat.set(nR, nC, rVec.begin(), rVec.end());
}
else
{
OSL_FAIL("ScMatrixImpl::PutDoubleVector: dimension error" );
}
}
void ScMatrixImpl::PutStringVector( const ::std::vector< svl::SharedString > & rVec, SCSIZE nC, SCSIZE nR )
{
if (!rVec.empty() && ValidColRow( nC, nR) && ValidColRow( nC, nR + rVec.size() - 1))
{
maMat.set(nR, nC, rVec.begin(), rVec.end());
}
else
{
OSL_FAIL("ScMatrixImpl::PutStringVector: dimension error" );
}
}
void ScMatrixImpl::PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
{
if (nCount && ValidColRow( nC, nR) && ValidColRow( nC, nR + nCount - 1))
{
maMat.set_empty(nR, nC, nCount);
// Flag to indicate that this is 'empty', not 'empty result' or 'empty path'.
maMatFlag.set_empty(nR, nC, nCount);
}
else
{
OSL_FAIL("ScMatrixImpl::PutEmptyVector: dimension error" );
}
}
void ScMatrixImpl::PutEmptyResultVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
{
if (nCount && ValidColRow( nC, nR) && ValidColRow( nC, nR + nCount - 1))
{
maMat.set_empty(nR, nC, nCount);
// Flag to indicate that this is 'empty result', not 'empty' or 'empty path'.
std::vector<uint8_t> aVals(nCount, SC_MATFLAG_EMPTYRESULT);
maMatFlag.set(nR, nC, aVals.begin(), aVals.end());
}
else
{
OSL_FAIL("ScMatrixImpl::PutEmptyResultVector: dimension error" );
}
}
void ScMatrixImpl::PutEmptyPathVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
{
if (nCount && ValidColRow( nC, nR) && ValidColRow( nC, nR + nCount - 1))
{
maMat.set_empty(nR, nC, nCount);
// Flag to indicate 'empty path'.
std::vector<uint8_t> aVals(nCount, SC_MATFLAG_EMPTYPATH);
maMatFlag.set(nR, nC, aVals.begin(), aVals.end());
}
else
{
OSL_FAIL("ScMatrixImpl::PutEmptyPathVector: dimension error" );
}
}
void ScMatrixImpl::CompareEqual()
{
MatrixImplType::size_pair_type aSize = maMat.size();
CompareMatrixElemFunc<ElemEqualZero> aFunc(aSize.row, aSize.column);
aFunc = maMat.walk(std::move(aFunc));
aFunc.swap(maMat);
}
void ScMatrixImpl::CompareNotEqual()
{
MatrixImplType::size_pair_type aSize = maMat.size();
CompareMatrixElemFunc<ElemNotEqualZero> aFunc(aSize.row, aSize.column);
aFunc = maMat.walk(std::move(aFunc));
aFunc.swap(maMat);
}
void ScMatrixImpl::CompareLess()
{
MatrixImplType::size_pair_type aSize = maMat.size();
CompareMatrixElemFunc<ElemLessZero> aFunc(aSize.row, aSize.column);
aFunc = maMat.walk(std::move(aFunc));
aFunc.swap(maMat);
}
void ScMatrixImpl::CompareGreater()
{
MatrixImplType::size_pair_type aSize = maMat.size();
CompareMatrixElemFunc<ElemGreaterZero> aFunc(aSize.row, aSize.column);
aFunc = maMat.walk(std::move(aFunc));
aFunc.swap(maMat);
}
void ScMatrixImpl::CompareLessEqual()
{
MatrixImplType::size_pair_type aSize = maMat.size();
CompareMatrixElemFunc<ElemLessEqualZero> aFunc(aSize.row, aSize.column);
aFunc = maMat.walk(std::move(aFunc));
aFunc.swap(maMat);
}
void ScMatrixImpl::CompareGreaterEqual()
{
MatrixImplType::size_pair_type aSize = maMat.size();
CompareMatrixElemFunc<ElemGreaterEqualZero> aFunc(aSize.row, aSize.column);
aFunc = maMat.walk(std::move(aFunc));
aFunc.swap(maMat);
}
namespace {
struct AndEvaluator
{
bool mbResult;
void operate(double fVal) { mbResult &= (fVal != 0.0); }
bool result() const { return mbResult; }
AndEvaluator() : mbResult(true ) {}
};
struct OrEvaluator
{
bool mbResult;
void operate(double fVal) { mbResult |= (fVal != 0.0); }
bool result() const { return mbResult; }
OrEvaluator() : mbResult(false ) {}
};
struct XorEvaluator
{
bool mbResult;
void operate(double fVal) { mbResult ^= (fVal != 0.0); }
bool result() const { return mbResult; }
XorEvaluator() : mbResult(false ) {}
};
// Do not short circuit logical operations, in case there are error values
// these need to be propagated even if the result was determined earlier.
template <typename Evaluator>
double EvalMatrix(const MatrixImplType& rMat)
{
Evaluator aEval;
size_t nRows = rMat.size().row, nCols = rMat.size().column;
MatrixImplType::const_position_type aPos = rMat.position(0, 0);
for (size_t nC = 0; nC < nCols; ++nC)
{
for (size_t nR = 0; nR < nRows; ++nR)
{
mdds::mtm::element_t eType = rMat.get_type(aPos);
if (eType != mdds::mtm::element_numeric && eType != mdds::mtm::element_boolean)
// assuming a CompareMat this is an error
return CreateDoubleError(FormulaError::IllegalArgument);
double fVal = rMat.get_numeric(aPos);
if (!std::isfinite(fVal))
// DoubleError
return fVal;
aEval.operate(fVal);
aPos = MatrixImplType::next_position(aPos);
}
}
return aEval.result();
}
}
double ScMatrixImpl::And () const
{
// All elements must be of value type.
// True only if all the elements have non-zero values.
return EvalMatrix<AndEvaluator>(maMat);
}
double ScMatrixImpl::Or () const
{
// All elements must be of value type.
// True if at least one element has a non-zero value.
return EvalMatrix<OrEvaluator>(maMat);
}
double ScMatrixImpl::Xor () const
{
// All elements must be of value type.
// True if an odd number of elements have a non-zero value.
return EvalMatrix<XorEvaluator>(maMat);
}
namespace {
template <typename Op, typename tRes>
class WalkElementBlocks
{
Op maOp;
ScMatrix::IterateResult<tRes> maRes;
bool mbTextAsZero:1;
bool mbIgnoreErrorValues:1;
public :
WalkElementBlocks(bool bTextAsZero, bool bIgnoreErrorValues) :
maRes(Op::InitVal, 0),
mbTextAsZero(bTextAsZero), mbIgnoreErrorValues(bIgnoreErrorValues)
{}
const ScMatrix::IterateResult<tRes>& getResult() const { return maRes; }
void operator () (const MatrixImplType::element_block_node_type& node)
{
switch (node.type)
{
case mdds::mtm::element_numeric:
{
typedef MatrixImplType::numeric_block_type block_type;
size_t nIgnored = 0;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
if (mbIgnoreErrorValues && !std::isfinite(*it))
{
++nIgnored;
continue ;
}
maOp(maRes.maAccumulator, *it);
}
maRes.mnCount += node.size - nIgnored;
}
break ;
case mdds::mtm::element_boolean:
{
typedef MatrixImplType::boolean_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
maOp(maRes.maAccumulator, *it);
}
maRes.mnCount += node.size;
}
break ;
case mdds::mtm::element_string:
if (mbTextAsZero)
maRes.mnCount += node.size;
break ;
case mdds::mtm::element_empty:
default :
;
}
}
};
template <typename Op, typename tRes>
class WalkElementBlocksMultipleValues
{
const std::vector<Op>* mpOp;
ScMatrix::IterateResultMultiple<tRes> maRes;
public :
WalkElementBlocksMultipleValues(const std::vector<Op>& aOp) :
mpOp(&aOp), maRes(0)
{
for (const auto & rpOp : *mpOp)
maRes.maAccumulator.emplace_back(rpOp.mInitVal);
}
WalkElementBlocksMultipleValues( const WalkElementBlocksMultipleValues& ) = delete ;
WalkElementBlocksMultipleValues& operator = ( const WalkElementBlocksMultipleValues& ) = delete ;
WalkElementBlocksMultipleValues(WalkElementBlocksMultipleValues&& r) noexcept
: mpOp(r.mpOp), maRes(r.maRes.mnCount)
{
maRes.maAccumulator = std::move(r.maRes.maAccumulator);
}
WalkElementBlocksMultipleValues& operator =(WalkElementBlocksMultipleValues&& r) noexcept
{
mpOp = r.mpOp;
maRes.maAccumulator = std::move(r.maRes.maAccumulator);
maRes.mnCount = r.maRes.mnCount;
return *this ;
}
const ScMatrix::IterateResultMultiple<tRes>& getResult() const { return maRes; }
void operator () (const MatrixImplType::element_block_node_type& node)
{
switch (node.type)
{
case mdds::mtm::element_numeric:
{
typedef MatrixImplType::numeric_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
for (size_t i = 0u; i < mpOp->size(); ++i)
(*mpOp)[i](maRes.maAccumulator[i], *it);
}
maRes.mnCount += node.size;
}
break ;
case mdds::mtm::element_boolean:
{
typedef MatrixImplType::boolean_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
for (size_t i = 0u; i < mpOp->size(); ++i)
(*mpOp)[i](maRes.maAccumulator[i], *it);
}
maRes.mnCount += node.size;
}
break ;
case mdds::mtm::element_string:
case mdds::mtm::element_empty:
default :
;
}
}
};
class CountElements
{
size_t mnCount;
bool mbCountString;
bool mbCountErrors;
bool mbIgnoreEmptyStrings;
public :
explicit CountElements(bool bCountString, bool bCountErrors, bool bIgnoreEmptyStrings) :
mnCount(0), mbCountString(bCountString), mbCountErrors(bCountErrors),
mbIgnoreEmptyStrings(bIgnoreEmptyStrings) {}
size_t getCount() const { return mnCount; }
void operator () (const MatrixImplType::element_block_node_type& node)
{
switch (node.type)
{
case mdds::mtm::element_numeric:
mnCount += node.size;
if (!mbCountErrors)
{
typedef MatrixImplType::numeric_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
if (!std::isfinite(*it))
--mnCount;
}
}
break ;
case mdds::mtm::element_boolean:
mnCount += node.size;
break ;
case mdds::mtm::element_string:
if (mbCountString)
{
mnCount += node.size;
if (mbIgnoreEmptyStrings)
{
typedef MatrixImplType::string_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
if (it->isEmpty())
--mnCount;
}
}
}
break ;
case mdds::mtm::element_empty:
default :
;
}
}
};
const size_t ResultNotSet = std::numeric_limits<size_t>::max();
template <typename Type>
class WalkAndMatchElements
{
Type maMatchValue;
size_t mnStartIndex;
size_t mnStopIndex;
size_t mnResult;
size_t mnIndex;
public :
WalkAndMatchElements(Type aMatchValue, const MatrixImplType::size_pair_type& aSize, size_t nCol1, size_t nCol2) :
maMatchValue(std::move(aMatchValue)),
mnStartIndex( nCol1 * aSize.row ),
mnStopIndex( (nCol2 + 1) * aSize.row ),
mnResult(ResultNotSet),
mnIndex(0)
{
assert( nCol1 < aSize.column && nCol2 < aSize.column);
}
size_t getMatching() const { return mnResult; }
size_t getRemainingCount() const
{
return mnIndex < mnStopIndex ? mnStopIndex - mnIndex : 0;
}
size_t compare(const MatrixImplType::element_block_node_type& node) const ;
void operator () (const MatrixImplType::element_block_node_type& node)
{
// early exit if match already found
if (mnResult != ResultNotSet)
return ;
// limit lookup to the requested columns
if (mnStartIndex <= mnIndex && getRemainingCount() > 0)
{
mnResult = compare(node);
}
mnIndex += node.size;
}
};
template <>
size_t WalkAndMatchElements<double >::compare(const MatrixImplType::element_block_node_type& node) const
{
size_t nCount = 0;
switch (node.type)
{
case mdds::mtm::element_numeric:
{
typedef MatrixImplType::numeric_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
const size_t nRemaining = getRemainingCount();
for (; it != itEnd && nCount < nRemaining; ++it, ++nCount)
{
if (*it == maMatchValue)
{
return mnIndex + nCount;
}
}
break ;
}
case mdds::mtm::element_boolean:
{
typedef MatrixImplType::boolean_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
const size_t nRemaining = getRemainingCount();
for (; it != itEnd && nCount < nRemaining; ++it, ++nCount)
{
if (int (*it) == maMatchValue)
{
return mnIndex + nCount;
}
}
break ;
}
break ;
case mdds::mtm::element_string:
case mdds::mtm::element_empty:
default :
;
}
return ResultNotSet;
}
template <>
size_t WalkAndMatchElements<svl::SharedString>::compare(const MatrixImplType::element_block_node_type& node) const
{
switch (node.type)
{
case mdds::mtm::element_string:
{
size_t nCount = 0;
typedef MatrixImplType::string_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
const size_t nRemaining = getRemainingCount();
for (; it != itEnd && nCount < nRemaining; ++it, ++nCount)
{
if (it->getDataIgnoreCase() == maMatchValue.getDataIgnoreCase())
{
return mnIndex + nCount;
}
}
break ;
}
case mdds::mtm::element_boolean:
case mdds::mtm::element_numeric:
case mdds::mtm::element_empty:
default :
;
}
return ResultNotSet;
}
struct MaxOp
{
static double init() { return -std::numeric_limits<double >::max(); }
static double compare(double left, double right)
{
if (!std::isfinite(left))
return left;
if (!std::isfinite(right))
return right;
return std::max(left, right);
}
static double boolValue(
MatrixImplType::boolean_block_type::const_iterator it,
const MatrixImplType::boolean_block_type::const_iterator& itEnd)
{
// If the array has at least one true value, the maximum value is 1.
it = std::find(it, itEnd, true );
return it == itEnd ? 0.0 : 1.0;
}
};
struct MinOp
{
static double init() { return std::numeric_limits<double >::max(); }
static double compare(double left, double right)
{
if (!std::isfinite(left))
return left;
if (!std::isfinite(right))
return right;
return std::min(left, right);
}
static double boolValue(
MatrixImplType::boolean_block_type::const_iterator it,
const MatrixImplType::boolean_block_type::const_iterator& itEnd)
{
// If the array has at least one false value, the minimum value is 0.
it = std::find(it, itEnd, false );
return it == itEnd ? 1.0 : 0.0;
}
};
struct Lcm
{
static double init() { return 1.0; }
static double calculate(double fx,double fy)
{
return (fx*fy)/ScInterpreter::ScGetGCD(fx,fy);
}
static double boolValue(
MatrixImplType::boolean_block_type::const_iterator it,
const MatrixImplType::boolean_block_type::const_iterator& itEnd)
{
// If the array has at least one false value, the minimum value is 0.
it = std::find(it, itEnd, false );
return it == itEnd ? 1.0 : 0.0;
}
};
struct Gcd
{
static double init() { return 0.0; }
static double calculate(double fx,double fy)
{
return ScInterpreter::ScGetGCD(fx,fy);
}
static double boolValue(
MatrixImplType::boolean_block_type::const_iterator it,
const MatrixImplType::boolean_block_type::const_iterator& itEnd)
{
// If the array has at least one true value, the gcdResult is 1.
it = std::find(it, itEnd, true );
return it == itEnd ? 0.0 : 1.0;
}
};
template <typename Op>
class CalcMaxMinValue
{
double mfVal;
bool mbTextAsZero;
bool mbIgnoreErrorValues;
bool mbHasValue;
public :
CalcMaxMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) :
mfVal(Op::init()),
mbTextAsZero(bTextAsZero),
mbIgnoreErrorValues(bIgnoreErrorValues),
mbHasValue(false ) {}
double getValue() const { return mbHasValue ? mfVal : 0.0; }
void operator () (const MatrixImplType::element_block_node_type& node)
{
switch (node.type)
{
case mdds::mtm::element_numeric:
{
typedef MatrixImplType::numeric_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
if (mbIgnoreErrorValues)
{
for (; it != itEnd; ++it)
{
if (std::isfinite(*it))
mfVal = Op::compare(mfVal, *it);
}
}
else
{
for (; it != itEnd; ++it)
mfVal = Op::compare(mfVal, *it);
}
mbHasValue = true ;
}
break ;
case mdds::mtm::element_boolean:
{
typedef MatrixImplType::boolean_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
double fVal = Op::boolValue(it, itEnd);
mfVal = Op::compare(mfVal, fVal);
mbHasValue = true ;
}
break ;
case mdds::mtm::element_string:
case mdds::mtm::element_empty:
{
// empty elements are treated as empty strings.
if (mbTextAsZero)
{
mfVal = Op::compare(mfVal, 0.0);
mbHasValue = true ;
}
}
break ;
default :
;
}
}
};
template <typename Op>
class CalcGcdLcm
{
double mfval;
public :
CalcGcdLcm() : mfval(Op::init()) {}
double getResult() const { return mfval; }
void operator () ( const MatrixImplType::element_block_node_type& node )
{
switch (node.type)
{
case mdds::mtm::element_numeric:
{
typedef MatrixImplType::numeric_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for ( ; it != itEnd; ++it)
{
if (*it < 0.0)
mfval = CreateDoubleError(FormulaError::IllegalArgument);
else
mfval = ::rtl::math::approxFloor( Op::calculate(*it,mfval));
}
}
break ;
case mdds::mtm::element_boolean:
{
typedef MatrixImplType::boolean_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
mfval = Op::boolValue(it, itEnd);
}
break ;
case mdds::mtm::element_empty:
case mdds::mtm::element_string:
{
mfval = CreateDoubleError(FormulaError::IllegalArgument);
}
break ;
default :
;
}
}
};
double evaluate( double fVal, ScQueryOp eOp )
{
if (!std::isfinite(fVal))
return fVal;
switch (eOp)
{
case SC_EQUAL:
return fVal == 0.0 ? 1.0 : 0.0;
case SC_LESS:
return fVal < 0.0 ? 1.0 : 0.0;
case SC_GREATER:
return fVal > 0.0 ? 1.0 : 0.0;
case SC_LESS_EQUAL:
return fVal <= 0.0 ? 1.0 : 0.0;
case SC_GREATER_EQUAL:
return fVal >= 0.0 ? 1.0 : 0.0;
case SC_NOT_EQUAL:
return fVal != 0.0 ? 1.0 : 0.0;
default :
;
}
SAL_WARN("sc.core" , "evaluate: unhandled comparison operator: " << static_cast <int >(eOp));
return CreateDoubleError( FormulaError::UnknownState);
}
class CompareMatrixFunc
{
sc::Compare& mrComp;
size_t mnMatPos;
sc::CompareOptions* mpOptions;
std::vector<double > maResValues; // double instead of bool to transport error values
void compare()
{
double fVal = sc::CompareFunc( mrComp, mpOptions);
maResValues.push_back(evaluate(fVal, mrComp.meOp));
}
public :
CompareMatrixFunc( size_t nResSize, sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) :
mrComp(rComp), mnMatPos(nMatPos), mpOptions(pOptions)
{
maResValues.reserve(nResSize);
}
CompareMatrixFunc( const CompareMatrixFunc& ) = delete ;
CompareMatrixFunc& operator = ( const CompareMatrixFunc& ) = delete ;
CompareMatrixFunc(CompareMatrixFunc&& r) noexcept :
mrComp(r.mrComp),
mnMatPos(r.mnMatPos),
mpOptions(r.mpOptions),
maResValues(std::move(r.maResValues)) {}
CompareMatrixFunc& operator =(CompareMatrixFunc&& r) noexcept
{
mrComp = r.mrComp;
mnMatPos = r.mnMatPos;
mpOptions = r.mpOptions;
maResValues = std::move(r.maResValues);
return *this ;
}
void operator () (const MatrixImplType::element_block_node_type& node)
{
sc::Compare::Cell& rCell = mrComp.maCells[mnMatPos];
switch (node.type)
{
case mdds::mtm::element_numeric:
{
typedef MatrixImplType::numeric_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
rCell.mbValue = true ;
rCell.mbEmpty = false ;
rCell.mfValue = *it;
compare();
}
}
break ;
case mdds::mtm::element_boolean:
{
typedef MatrixImplType::boolean_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
rCell.mbValue = true ;
rCell.mbEmpty = false ;
rCell.mfValue = double (*it);
compare();
}
}
break ;
case mdds::mtm::element_string:
{
typedef MatrixImplType::string_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
const svl::SharedString& rStr = *it;
rCell.mbValue = false ;
rCell.mbEmpty = false ;
rCell.maStr = rStr;
compare();
}
}
break ;
case mdds::mtm::element_empty:
{
rCell.mbValue = false ;
rCell.mbEmpty = true ;
rCell.maStr = svl::SharedString::getEmptyString();
for (size_t i = 0; i < node.size; ++i)
compare();
}
break ;
default :
;
}
}
const std::vector<double >& getValues() const
{
return maResValues;
}
};
/**
* Left-hand side is a matrix while the right-hand side is a numeric value.
*/
class CompareMatrixToNumericFunc
{
sc::Compare& mrComp;
double mfRightValue;
sc::CompareOptions* mpOptions;
std::vector<double > maResValues; // double instead of bool to transport error values
void compare()
{
double fVal = sc::CompareFunc(mrComp.maCells[0], mfRightValue, mpOptions);
maResValues.push_back(evaluate(fVal, mrComp.meOp));
}
void compareLeftNumeric( double fLeftVal )
{
double fVal = sc::CompareFunc(fLeftVal, mfRightValue);
maResValues.push_back(evaluate(fVal, mrComp.meOp));
}
void compareLeftEmpty( size_t nSize )
{
double fVal = sc::CompareEmptyToNumericFunc(mfRightValue);
bool bRes = evaluate(fVal, mrComp.meOp);
maResValues.resize(maResValues.size() + nSize, bRes ? 1.0 : 0.0);
}
public :
CompareMatrixToNumericFunc( size_t nResSize, sc::Compare& rComp, double fRightValue, sc::CompareOptions* pOptions ) :
mrComp(rComp), mfRightValue(fRightValue), mpOptions(pOptions)
{
maResValues.reserve(nResSize);
}
CompareMatrixToNumericFunc( const CompareMatrixToNumericFunc& ) = delete ;
CompareMatrixToNumericFunc& operator = ( const CompareMatrixToNumericFunc& ) = delete ;
CompareMatrixToNumericFunc(CompareMatrixToNumericFunc&& r) noexcept :
mrComp(r.mrComp),
mfRightValue(r.mfRightValue),
mpOptions(r.mpOptions),
maResValues(std::move(r.maResValues)) {}
CompareMatrixToNumericFunc& operator =(CompareMatrixToNumericFunc&& r) noexcept
{
mrComp = r.mrComp;
mfRightValue = r.mfRightValue;
mpOptions = r.mpOptions;
maResValues = std::move(r.maResValues);
return *this ;
}
void operator () (const MatrixImplType::element_block_node_type& node)
{
switch (node.type)
{
case mdds::mtm::element_numeric:
{
typedef MatrixImplType::numeric_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
compareLeftNumeric(*it);
}
break ;
case mdds::mtm::element_boolean:
{
typedef MatrixImplType::boolean_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
compareLeftNumeric(double (*it));
}
break ;
case mdds::mtm::element_string:
{
typedef MatrixImplType::string_block_type block_type;
block_type::const_iterator it = block_type::begin(*node.data);
block_type::const_iterator itEnd = block_type::end(*node.data);
for (; it != itEnd; ++it)
{
const svl::SharedString& rStr = *it;
sc::Compare::Cell& rCell = mrComp.maCells[0];
rCell.mbValue = false ;
rCell.mbEmpty = false ;
rCell.maStr = rStr;
compare();
}
}
break ;
case mdds::mtm::element_empty:
compareLeftEmpty(node.size);
break ;
default :
;
}
}
const std::vector<double >& getValues() const
{
return maResValues;
}
};
class ToDoubleArray
{
std::vector<double > maArray;
std::vector<double >::iterator miPos;
double mfNaN;
bool mbEmptyAsZero;
void moveArray( ToDoubleArray& r )
{
// Re-create the iterator from the new array after the array has been
// moved, to ensure that the iterator points to a valid array
// position.
size_t n = std::distance(r.maArray.begin(), r.miPos);
maArray = std::move(r.maArray);
miPos = maArray.begin();
std::advance(miPos, n);
}
public :
ToDoubleArray( size_t nSize, bool bEmptyAsZero ) :
maArray(nSize, 0.0), miPos(maArray.begin()), mbEmptyAsZero(bEmptyAsZero)
{
mfNaN = CreateDoubleError( FormulaError::ElementNaN);
}
ToDoubleArray( const ToDoubleArray& ) = delete ;
ToDoubleArray& operator = ( const ToDoubleArray& ) = delete ;
ToDoubleArray(ToDoubleArray&& r) noexcept :
mfNaN(r.mfNaN), mbEmptyAsZero(r.mbEmptyAsZero)
{
moveArray(r);
}
ToDoubleArray& operator =(ToDoubleArray&& r) noexcept
{
mfNaN = r.mfNaN;
mbEmptyAsZero = r.mbEmptyAsZero;
moveArray(r);
return *this ;
}
void operator () (const MatrixImplType::element_block_node_type& node)
{
using namespace mdds::mtv;
switch (node.type)
{
case mdds::mtm::element_numeric:
{
double_element_block::const_iterator it = double_element_block::begin(*node.data);
double_element_block::const_iterator itEnd = double_element_block::end(*node.data);
for (; it != itEnd; ++it, ++miPos)
*miPos = *it;
}
break ;
case mdds::mtm::element_boolean:
{
boolean_element_block::const_iterator it = boolean_element_block::begin(*node.data);
boolean_element_block::const_iterator itEnd = boolean_element_block::end(*node.data);
for (; it != itEnd; ++it, ++miPos)
*miPos = *it ? 1.0 : 0.0;
}
break ;
case mdds::mtm::element_string:
{
for (size_t i = 0; i < node.size; ++i, ++miPos)
*miPos = mfNaN;
}
break ;
case mdds::mtm::element_empty:
{
if (mbEmptyAsZero)
{
std::advance(miPos, node.size);
return ;
}
for (size_t i = 0; i < node.size; ++i, ++miPos)
*miPos = mfNaN;
}
break ;
default :
;
}
}
void swap(std::vector<double >& rOther)
{
maArray.swap(rOther);
}
};
struct ArrayMul
{
double operator () (const double & lhs, const double & rhs) const
{
return lhs * rhs;
}
};
template <typename Op>
class MergeDoubleArrayFunc
{
std::vector<double >::iterator miPos;
double mfNaN;
public :
MergeDoubleArrayFunc(std::vector<double >& rArray) : miPos(rArray.begin())
{
mfNaN = CreateDoubleError( FormulaError::ElementNaN);
}
MergeDoubleArrayFunc( const MergeDoubleArrayFunc& ) = delete ;
MergeDoubleArrayFunc& operator = ( const MergeDoubleArrayFunc& ) = delete ;
MergeDoubleArrayFunc( MergeDoubleArrayFunc&& ) = default ;
MergeDoubleArrayFunc& operator = ( MergeDoubleArrayFunc&& ) = default ;
void operator () (const MatrixImplType::element_block_node_type& node)
{
using namespace mdds::mtv;
static const Op op;
switch (node.type)
{
case mdds::mtm::element_numeric:
{
double_element_block::const_iterator it = double_element_block::begin(*node.data);
double_element_block::const_iterator itEnd = double_element_block::end(*node.data);
for (; it != itEnd; ++it, ++miPos)
{
if (GetDoubleErrorValue(*miPos) == FormulaError::ElementNaN)
continue ;
*miPos = op(*miPos, *it);
}
}
break ;
case mdds::mtm::element_boolean:
{
boolean_element_block::const_iterator it = boolean_element_block::begin(*node.data);
boolean_element_block::const_iterator itEnd = boolean_element_block::end(*node.data);
for (; it != itEnd; ++it, ++miPos)
{
if (GetDoubleErrorValue(*miPos) == FormulaError::ElementNaN)
continue ;
*miPos = op(*miPos, *it ? 1.0 : 0.0);
}
}
break ;
case mdds::mtm::element_string:
{
for (size_t i = 0; i < node.size; ++i, ++miPos)
*miPos = mfNaN;
}
break ;
case mdds::mtm::element_empty:
{
// Empty element is equivalent of having a numeric value of 0.0.
for (size_t i = 0; i < node.size; ++i, ++miPos)
{
if (GetDoubleErrorValue(*miPos) == FormulaError::ElementNaN)
continue ;
*miPos = op(*miPos, 0.0);
}
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=94 H=96 G=94
¤ Dauer der Verarbeitung: 0.23 Sekunden
¤
*© Formatika GbR, Deutschland