96f162d02a
Move the AddSub calculation inside ScMatrix so we can use an iterator to walk the matrix and avoid lookup cost for each element. Shaves 50% off the time spent here in my test sheet. Change-Id: I171d7dd4ae86419a563342a4120d14106e8d71db Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144826 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
433 lines
16 KiB
C++
433 lines
16 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "address.hxx"
|
|
#include "matrixoperators.hxx"
|
|
#include "types.hxx"
|
|
#include <formula/errorcodes.hxx>
|
|
#include "scdllapi.h"
|
|
#include <svl/sharedstring.hxx>
|
|
#include <svl/sharedstringpool.hxx>
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#define DEBUG_MATRIX 0
|
|
|
|
class ScInterpreter;
|
|
class SvNumberFormatter;
|
|
class ScMatrixImpl;
|
|
enum class FormulaError : sal_uInt16;
|
|
|
|
namespace sc {
|
|
|
|
struct Compare;
|
|
struct CompareOptions;
|
|
|
|
}
|
|
|
|
/**
|
|
* Try NOT to use this struct. This struct should go away in a hopefully
|
|
* not so distant future.
|
|
*/
|
|
struct ScMatrixValue
|
|
{
|
|
double fVal;
|
|
svl::SharedString aStr;
|
|
ScMatValType nType;
|
|
|
|
/// Only valid if ScMatrix methods indicate so!
|
|
const svl::SharedString& GetString() const { return aStr; }
|
|
|
|
/// Only valid if ScMatrix methods indicate that this is no string!
|
|
FormulaError GetError() const { return GetDoubleErrorValue(fVal); }
|
|
|
|
/// Only valid if ScMatrix methods indicate that this is a boolean
|
|
bool GetBoolean() const { return fVal != 0.0; }
|
|
|
|
ScMatrixValue() : fVal(0.0), nType(ScMatValType::Empty) {}
|
|
ScMatrixValue(const ScMatrixValue& r) = default;
|
|
ScMatrixValue& operator= (const ScMatrixValue& r) = default;
|
|
|
|
bool operator== (const ScMatrixValue& r) const
|
|
{
|
|
if (nType != r.nType)
|
|
return false;
|
|
|
|
switch (nType)
|
|
{
|
|
case ScMatValType::Value:
|
|
case ScMatValType::Boolean:
|
|
return fVal == r.fVal;
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
return aStr == r.aStr;
|
|
}
|
|
|
|
bool operator!= (const ScMatrixValue& r) const
|
|
{
|
|
return !operator==(r);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Matrix data type that can store values of mixed types. Each element can
|
|
* be one of the following types: numeric, string, boolean, empty, and empty
|
|
* path.
|
|
*/
|
|
class SC_DLLPUBLIC ScMatrix final
|
|
{
|
|
friend class ScMatrixImpl;
|
|
|
|
mutable size_t nRefCnt; // reference count
|
|
mutable bool mbCloneIfConst; // Whether the matrix is cloned with a CloneIfConst() call.
|
|
std::unique_ptr<ScMatrixImpl> pImpl;
|
|
|
|
ScMatrix( const ScMatrix& ) = delete;
|
|
ScMatrix& operator=( const ScMatrix&) = delete;
|
|
|
|
public:
|
|
ScMatrix(SCSIZE nC, SCSIZE nR);
|
|
ScMatrix(SCSIZE nC, SCSIZE nR, double fInitVal);
|
|
ScMatrix( size_t nC, size_t nR, const std::vector<double>& rInitVals );
|
|
~ScMatrix();
|
|
|
|
typedef std::function<void(size_t, size_t, double)> DoubleOpFunction;
|
|
typedef std::function<void(size_t, size_t, bool)> BoolOpFunction;
|
|
typedef std::function<void(size_t, size_t, svl::SharedString)> StringOpFunction;
|
|
typedef std::function<void(size_t, size_t)> EmptyOpFunction;
|
|
typedef std::function<double(double, double)> CalculateOpFunction;
|
|
|
|
/**
|
|
* When adding all numerical matrix elements for a scalar result such as
|
|
* summation, the interpreter wants to separate the first non-zero value
|
|
* with the rest of the summed values. This is necessary for better
|
|
* numerical stability, unless we sort all by absolute values before
|
|
* summing (not really an option) or use another algorithm, e.g. Kahan's
|
|
* summation algorithm,
|
|
* https://en.wikipedia.org/wiki/Kahan_summation_algorithm
|
|
*/
|
|
template<typename tRes>
|
|
struct IterateResultMultiple
|
|
{
|
|
std::vector<tRes> maAccumulator;
|
|
size_t mnCount;
|
|
|
|
IterateResultMultiple(size_t nCount) :
|
|
maAccumulator(0), mnCount(nCount) {}
|
|
};
|
|
typedef IterateResultMultiple<KahanSum> KahanIterateResultMultiple;
|
|
typedef IterateResultMultiple<double> DoubleIterateResultMultiple;
|
|
|
|
/**
|
|
* Iterator for executing one operation with the matrix data.
|
|
*/
|
|
template<typename tRes>
|
|
struct IterateResult
|
|
{
|
|
tRes maAccumulator;
|
|
size_t mnCount;
|
|
|
|
IterateResult(tRes fAccumulator, size_t nCount)
|
|
: maAccumulator(fAccumulator), mnCount(nCount) {}
|
|
};
|
|
typedef IterateResult<KahanSum> KahanIterateResult;
|
|
typedef IterateResult<double> DoubleIterateResult;
|
|
|
|
|
|
/** Checks nC or nR for zero and uses GetElementsMax() whether a matrix of
|
|
the size of nC*nR could be allocated. A zero size (both nC and nR zero)
|
|
matrix is allowed for later resize.
|
|
*/
|
|
bool static IsSizeAllocatable( SCSIZE nC, SCSIZE nR );
|
|
|
|
/// Value or boolean.
|
|
static bool IsValueType( ScMatValType nType )
|
|
{
|
|
return nType <= ScMatValType::Boolean;
|
|
}
|
|
|
|
/// Boolean.
|
|
static bool IsBooleanType( ScMatValType nType )
|
|
{
|
|
return nType == ScMatValType::Boolean;
|
|
}
|
|
|
|
/// String, empty or empty path, but not value nor boolean.
|
|
static bool IsNonValueType( ScMatValType nType )
|
|
{
|
|
return bool(nType & ScMatValType::NonvalueMask);
|
|
}
|
|
|
|
/** String, but not empty or empty path or any other type.
|
|
Not named IsStringType to prevent confusion because previously
|
|
IsNonValueType was named IsStringType. */
|
|
static bool IsRealStringType( ScMatValType nType )
|
|
{
|
|
return (nType & ScMatValType::NonvalueMask) == ScMatValType::String;
|
|
}
|
|
|
|
/// Empty, but not empty path or any other type.
|
|
static bool IsEmptyType( ScMatValType nType )
|
|
{
|
|
return (nType & ScMatValType::NonvalueMask) == ScMatValType::Empty;
|
|
}
|
|
|
|
/// Empty path, but not empty or any other type.
|
|
static bool IsEmptyPathType( ScMatValType nType )
|
|
{
|
|
return (nType & ScMatValType::NonvalueMask) == ScMatValType::EmptyPath;
|
|
}
|
|
|
|
/** Clone the matrix. */
|
|
ScMatrix* Clone() const;
|
|
|
|
/** Clone the matrix if mbCloneIfConst (immutable) is set, otherwise
|
|
return _this_ matrix, to be assigned to a ScMatrixRef. */
|
|
ScMatrix* CloneIfConst();
|
|
|
|
/** Set the matrix to mutable for CloneIfConst(), only the interpreter
|
|
should do this and know the consequences. */
|
|
void SetMutable();
|
|
|
|
/** Set the matrix to immutable for CloneIfConst(), only the interpreter
|
|
should do this and know the consequences. */
|
|
void SetImmutable() const;
|
|
|
|
/**
|
|
* Resize the matrix to specified new dimension.
|
|
*/
|
|
void Resize(SCSIZE nC, SCSIZE nR);
|
|
|
|
void Resize(SCSIZE nC, SCSIZE nR, double fVal);
|
|
|
|
/** Clone the matrix and extend it to the new size. nNewCols and nNewRows
|
|
MUST be at least of the size of the original matrix. */
|
|
ScMatrix* CloneAndExtend(SCSIZE nNewCols, SCSIZE nNewRows) const;
|
|
|
|
void IncRef() const;
|
|
void DecRef() const;
|
|
|
|
void SetErrorInterpreter( ScInterpreter* p);
|
|
void GetDimensions( SCSIZE& rC, SCSIZE& rR) const;
|
|
SCSIZE GetElementCount() const;
|
|
bool ValidColRow( SCSIZE nC, SCSIZE nR) const;
|
|
|
|
/** For a row vector or column vector, if the position does not point into
|
|
the vector but is a valid column or row offset it is adapted such that
|
|
it points to an element to be replicated, same column row 0 for a row
|
|
vector, same row column 0 for a column vector. Else, for a 2D matrix,
|
|
returns false.
|
|
*/
|
|
bool ValidColRowReplicated( SCSIZE & rC, SCSIZE & rR ) const;
|
|
|
|
/** Checks if the matrix position is within the matrix. If it is not, for a
|
|
row vector or column vector the position is adapted such that it points
|
|
to an element to be replicated, same column row 0 for a row vector,
|
|
same row column 0 for a column vector. Else, for a 2D matrix and
|
|
position not within matrix, returns false.
|
|
*/
|
|
bool ValidColRowOrReplicated( SCSIZE & rC, SCSIZE & rR ) 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);
|
|
|
|
/// Jump sal_False without path
|
|
void PutEmptyPath( SCSIZE nC, SCSIZE nR) ;
|
|
void PutError( FormulaError nErrorCode, SCSIZE nC, SCSIZE nR ) ;
|
|
void PutBoolean( bool bVal, SCSIZE nC, SCSIZE nR) ;
|
|
|
|
void FillDouble( double fVal,
|
|
SCSIZE nC1, SCSIZE nR1, SCSIZE nC2, SCSIZE nR2 ) ;
|
|
|
|
/** Put a column vector of doubles, starting at row nR, must fit into dimensions. */
|
|
void PutDoubleVector( const ::std::vector< double > & rVec, SCSIZE nC, SCSIZE nR ) ;
|
|
|
|
/** Put a column vector of strings, starting at row nR, must fit into dimensions. */
|
|
void PutStringVector( const ::std::vector< svl::SharedString > & rVec, SCSIZE nC, SCSIZE nR ) ;
|
|
|
|
/** Put a column vector of empties, starting at row nR, must fit into dimensions. */
|
|
void PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR ) ;
|
|
|
|
/** Put a column vector of empty results, starting at row nR, must fit into dimensions. */
|
|
void PutEmptyResultVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR ) ;
|
|
|
|
/** Put a column vector of empty paths, starting at row nR, must fit into dimensions. */
|
|
void PutEmptyPathVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR ) ;
|
|
|
|
/** May be used before obtaining the double value of an element to avoid
|
|
passing its NAN around.
|
|
@ATTENTION: MUST NOT be used if the element is a string!
|
|
Use GetErrorIfNotString() instead if not sure.
|
|
@returns 0 if no error, else one of err... constants */
|
|
FormulaError GetError( SCSIZE nC, SCSIZE nR) const ;
|
|
|
|
/** Use in ScInterpreter to obtain the error code, if any.
|
|
@returns 0 if no error or string element, else one of err... constants */
|
|
FormulaError GetErrorIfNotString( SCSIZE nC, SCSIZE nR) const
|
|
{ return IsValue( nC, nR) ? GetError( nC, nR) : FormulaError::NONE; }
|
|
|
|
/// @return 0.0 if empty or empty path, else value or DoubleError.
|
|
double GetDouble( SCSIZE nC, SCSIZE nR) const ;
|
|
/// @return 0.0 if empty or empty path, else value or DoubleError.
|
|
double GetDouble( SCSIZE nIndex) const ;
|
|
/// @return value or DoubleError or string converted to value.
|
|
double GetDoubleWithStringConversion( SCSIZE nC, SCSIZE nR ) const ;
|
|
|
|
/// @return empty string if empty or empty path, else string content.
|
|
svl::SharedString GetString( SCSIZE nC, SCSIZE nR) const ;
|
|
/// @return empty string if empty or empty path, else string content.
|
|
svl::SharedString GetString( SCSIZE nIndex) const ;
|
|
|
|
/** @returns the matrix element's string if one is present, otherwise the
|
|
numerical value formatted as string, or in case of an error the error
|
|
string is returned; an empty string for empty, a "FALSE" string for
|
|
empty path. */
|
|
svl::SharedString GetString( SvNumberFormatter& rFormatter, SCSIZE nC, SCSIZE nR) const ;
|
|
|
|
/// @ATTENTION: If bString the ScMatrixValue->pS may still be NULL to indicate
|
|
/// an empty string!
|
|
ScMatrixValue Get( SCSIZE nC, SCSIZE nR) const ;
|
|
|
|
/** @return <TRUE/> if string or any empty, empty cell, empty result, empty
|
|
path, in fact non-value. */
|
|
bool IsStringOrEmpty( SCSIZE nIndex ) const ;
|
|
|
|
/** @return <TRUE/> if string or any empty, empty cell, empty result, empty
|
|
path, in fact non-value. */
|
|
bool IsStringOrEmpty( SCSIZE nC, SCSIZE nR ) const ;
|
|
|
|
/// @return <TRUE/> if empty or empty cell or empty result, not empty path.
|
|
bool IsEmpty( SCSIZE nC, SCSIZE nR ) const ;
|
|
|
|
/// @return <TRUE/> if empty cell, not empty or empty result or empty path.
|
|
bool IsEmptyCell( SCSIZE nC, SCSIZE nR ) const ;
|
|
|
|
/// @return <TRUE/> if empty result, not empty or empty cell or empty path.
|
|
bool IsEmptyResult( SCSIZE nC, SCSIZE nR ) const ;
|
|
|
|
/// @return <TRUE/> if empty path, not empty or empty cell or empty result.
|
|
bool IsEmptyPath( SCSIZE nC, SCSIZE nR ) const ;
|
|
|
|
/// @return <TRUE/> if value or boolean.
|
|
bool IsValue( SCSIZE nIndex ) const ;
|
|
|
|
/// @return <TRUE/> if value or boolean.
|
|
bool IsValue( SCSIZE nC, SCSIZE nR ) const ;
|
|
|
|
/// @return <TRUE/> if value or boolean or empty or empty path.
|
|
bool IsValueOrEmpty( SCSIZE nC, SCSIZE nR ) const ;
|
|
|
|
/// @return <TRUE/> if boolean.
|
|
bool IsBoolean( SCSIZE nC, SCSIZE nR ) const ;
|
|
|
|
/// @return <TRUE/> if entire matrix is numeric, including booleans, with no strings or empties
|
|
bool IsNumeric() const ;
|
|
|
|
void MatTrans( const ScMatrix& mRes) const ;
|
|
void MatCopy ( const ScMatrix& mRes) const ;
|
|
|
|
// Convert ScInterpreter::CompareMat values (-1,0,1) to boolean values
|
|
void CompareEqual() ;
|
|
void CompareNotEqual() ;
|
|
void CompareLess() ;
|
|
void CompareGreater() ;
|
|
void CompareLessEqual() ;
|
|
void CompareGreaterEqual() ;
|
|
|
|
double And() const ; // logical AND of all matrix values, or NAN
|
|
double Or() const ; // logical OR of all matrix values, or NAN
|
|
double Xor() const ; // logical XOR of all matrix values, or NAN
|
|
|
|
KahanIterateResult Sum( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ;
|
|
KahanIterateResult SumSquare( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ;
|
|
DoubleIterateResult Product( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ;
|
|
size_t Count(bool bCountStrings, bool bCountErrors, bool bIgnoreEmptyStrings = false) 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 = false ) const ;
|
|
double GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ;
|
|
double GetGcd() const ;
|
|
double GetLcm() const ;
|
|
|
|
ScMatrixRef CompareMatrix(
|
|
sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) const ;
|
|
|
|
/**
|
|
* Convert the content of matrix into a linear array of numeric values.
|
|
* String elements are mapped to NaN's and empty elements are mapped to
|
|
* either NaN or zero values.
|
|
*
|
|
* @param bEmptyAsZero if true empty elements are mapped to zero values,
|
|
* otherwise they become NaN values.
|
|
*/
|
|
void GetDoubleArray( std::vector<double>& rArray, bool bEmptyAsZero = true ) const ;
|
|
void MergeDoubleArrayMultiply( std::vector<double>& rArray ) const ;
|
|
|
|
void NotOp(const ScMatrix& rMat) ;
|
|
void NegOp(const ScMatrix& rMat) ;
|
|
void AddOp(double fVal, const ScMatrix& rMat) ;
|
|
void SubOp(bool bFlag, double fVal, const ScMatrix& rMat) ;
|
|
void MulOp(double fVal, const ScMatrix& rMat) ;
|
|
void DivOp(bool bFlag, double fVal, const ScMatrix& rMat) ;
|
|
void PowOp(bool bFlag, double fVal, const ScMatrix& rMat) ;
|
|
|
|
KahanIterateResultMultiple CollectKahan(const std::vector<sc::op::kOp>& aOp) ;
|
|
|
|
void ExecuteOperation(const std::pair<size_t, size_t>& rStartPos, const std::pair<size_t, size_t>& rEndPos,
|
|
DoubleOpFunction aDoubleFunc, BoolOpFunction aBoolFunc, StringOpFunction aStringFunc,
|
|
EmptyOpFunction aEmptyFunc) const ;
|
|
|
|
void MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrixRef& xMat1, const ScMatrixRef& xMat2,
|
|
SvNumberFormatter& rFormatter, svl::SharedStringPool& rPool) ;
|
|
|
|
/** Apply binary operation to values from two input matrices, storing result into this matrix. */
|
|
void ExecuteBinaryOp(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrix& rInputMat1, const ScMatrix& rInputMat2,
|
|
ScInterpreter* pInterpreter, CalculateOpFunction op);
|
|
|
|
#if DEBUG_MATRIX
|
|
void Dump() const;
|
|
#endif
|
|
};
|
|
|
|
inline void intrusive_ptr_add_ref(const ScMatrix* p)
|
|
{
|
|
p->IncRef();
|
|
}
|
|
|
|
inline void intrusive_ptr_release(const ScMatrix* p)
|
|
{
|
|
p->DecRef();
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|