summaryrefslogtreecommitdiffstats
path: root/sc/inc/scmatrix.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/inc/scmatrix.hxx')
-rw-r--r--sc/inc/scmatrix.hxx433
1 files changed, 433 insertions, 0 deletions
diff --git a/sc/inc/scmatrix.hxx b/sc/inc/scmatrix.hxx
new file mode 100644
index 0000000000..8eb4ae7fde
--- /dev/null
+++ b/sc/inc/scmatrix.hxx
@@ -0,0 +1,433 @@
+/* -*- 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: */