1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include "address.hxx"
23 #include "matrixoperators.hxx"
25 #include <formula/errorcodes.hxx>
27 #include <svl/sharedstring.hxx>
28 #include <svl/sharedstringpool.hxx>
34 #define DEBUG_MATRIX 0
37 class SvNumberFormatter
;
39 enum class FormulaError
: sal_uInt16
;
44 struct CompareOptions
;
49 * Try NOT to use this struct. This struct should go away in a hopefully
50 * not so distant future.
55 svl::SharedString aStr
;
58 /// Only valid if ScMatrix methods indicate so!
59 const svl::SharedString
& GetString() const { return aStr
; }
61 /// Only valid if ScMatrix methods indicate that this is no string!
62 FormulaError
GetError() const { return GetDoubleErrorValue(fVal
); }
64 /// Only valid if ScMatrix methods indicate that this is a boolean
65 bool GetBoolean() const { return fVal
!= 0.0; }
67 ScMatrixValue() : fVal(0.0), nType(ScMatValType::Empty
) {}
68 ScMatrixValue(const ScMatrixValue
& r
) = default;
69 ScMatrixValue
& operator= (const ScMatrixValue
& r
) = default;
71 bool operator== (const ScMatrixValue
& r
) const
78 case ScMatValType::Value
:
79 case ScMatValType::Boolean
:
80 return fVal
== r
.fVal
;
86 return aStr
== r
.aStr
;
89 bool operator!= (const ScMatrixValue
& r
) const
91 return !operator==(r
);
96 * Matrix data type that can store values of mixed types. Each element can
97 * be one of the following types: numeric, string, boolean, empty, and empty
100 class SC_DLLPUBLIC ScMatrix final
102 friend class ScMatrixImpl
;
104 mutable size_t nRefCnt
; // reference count
105 mutable bool mbCloneIfConst
; // Whether the matrix is cloned with a CloneIfConst() call.
106 std::unique_ptr
<ScMatrixImpl
> pImpl
;
108 ScMatrix( const ScMatrix
& ) = delete;
109 ScMatrix
& operator=( const ScMatrix
&) = delete;
112 ScMatrix(SCSIZE nC
, SCSIZE nR
);
113 ScMatrix(SCSIZE nC
, SCSIZE nR
, double fInitVal
);
114 ScMatrix( size_t nC
, size_t nR
, const std::vector
<double>& rInitVals
);
117 typedef std::function
<void(size_t, size_t, double)> DoubleOpFunction
;
118 typedef std::function
<void(size_t, size_t, bool)> BoolOpFunction
;
119 typedef std::function
<void(size_t, size_t, svl::SharedString
)> StringOpFunction
;
120 typedef std::function
<void(size_t, size_t)> EmptyOpFunction
;
123 * When adding all numerical matrix elements for a scalar result such as
124 * summation, the interpreter wants to separate the first non-zero value
125 * with the rest of the summed values. This is necessary for better
126 * numerical stability, unless we sort all by absolute values before
127 * summing (not really an option) or use another algorithm, e.g. Kahan's
128 * summation algorithm,
129 * https://en.wikipedia.org/wiki/Kahan_summation_algorithm
131 template<typename tRes
>
132 struct IterateResultMultiple
134 std::vector
<tRes
> maAccumulator
;
137 IterateResultMultiple(size_t nCount
) :
138 maAccumulator(0), mnCount(nCount
) {}
140 typedef IterateResultMultiple
<KahanSum
> KahanIterateResultMultiple
;
141 typedef IterateResultMultiple
<double> DoubleIterateResultMultiple
;
144 * Iterator for executing one operation with the matrix data.
146 template<typename tRes
>
152 IterateResult(tRes fAccumulator
, size_t nCount
)
153 : maAccumulator(fAccumulator
), mnCount(nCount
) {}
155 typedef IterateResult
<KahanSum
> KahanIterateResult
;
156 typedef IterateResult
<double> DoubleIterateResult
;
159 /** Checks nC or nR for zero and uses GetElementsMax() whether a matrix of
160 the size of nC*nR could be allocated. A zero size (both nC and nR zero)
161 matrix is allowed for later resize.
163 bool static IsSizeAllocatable( SCSIZE nC
, SCSIZE nR
);
165 /// Value or boolean.
166 static bool IsValueType( ScMatValType nType
)
168 return nType
<= ScMatValType::Boolean
;
172 static bool IsBooleanType( ScMatValType nType
)
174 return nType
== ScMatValType::Boolean
;
177 /// String, empty or empty path, but not value nor boolean.
178 static bool IsNonValueType( ScMatValType nType
)
180 return bool(nType
& ScMatValType::NonvalueMask
);
183 /** String, but not empty or empty path or any other type.
184 Not named IsStringType to prevent confusion because previously
185 IsNonValueType was named IsStringType. */
186 static bool IsRealStringType( ScMatValType nType
)
188 return (nType
& ScMatValType::NonvalueMask
) == ScMatValType::String
;
191 /// Empty, but not empty path or any other type.
192 static bool IsEmptyType( ScMatValType nType
)
194 return (nType
& ScMatValType::NonvalueMask
) == ScMatValType::Empty
;
197 /// Empty path, but not empty or any other type.
198 static bool IsEmptyPathType( ScMatValType nType
)
200 return (nType
& ScMatValType::NonvalueMask
) == ScMatValType::EmptyPath
;
203 /** Clone the matrix. */
204 ScMatrix
* Clone() const;
206 /** Clone the matrix if mbCloneIfConst (immutable) is set, otherwise
207 return _this_ matrix, to be assigned to a ScMatrixRef. */
208 ScMatrix
* CloneIfConst();
210 /** Set the matrix to mutable for CloneIfConst(), only the interpreter
211 should do this and know the consequences. */
214 /** Set the matrix to immutable for CloneIfConst(), only the interpreter
215 should do this and know the consequences. */
216 void SetImmutable() const;
219 * Resize the matrix to specified new dimension.
221 void Resize(SCSIZE nC
, SCSIZE nR
);
223 void Resize(SCSIZE nC
, SCSIZE nR
, double fVal
);
225 /** Clone the matrix and extend it to the new size. nNewCols and nNewRows
226 MUST be at least of the size of the original matrix. */
227 ScMatrix
* CloneAndExtend(SCSIZE nNewCols
, SCSIZE nNewRows
) const;
232 void SetErrorInterpreter( ScInterpreter
* p
);
233 void GetDimensions( SCSIZE
& rC
, SCSIZE
& rR
) const;
234 SCSIZE
GetElementCount() const;
235 bool ValidColRow( SCSIZE nC
, SCSIZE nR
) const;
237 /** For a row vector or column vector, if the position does not point into
238 the vector but is a valid column or row offset it is adapted such that
239 it points to an element to be replicated, same column row 0 for a row
240 vector, same row column 0 for a column vector. Else, for a 2D matrix,
243 bool ValidColRowReplicated( SCSIZE
& rC
, SCSIZE
& rR
) const;
245 /** Checks if the matrix position is within the matrix. If it is not, for a
246 row vector or column vector the position is adapted such that it points
247 to an element to be replicated, same column row 0 for a row vector,
248 same row column 0 for a column vector. Else, for a 2D matrix and
249 position not within matrix, returns false.
251 bool ValidColRowOrReplicated( SCSIZE
& rC
, SCSIZE
& rR
) const;
253 void PutDouble( double fVal
, SCSIZE nC
, SCSIZE nR
);
254 void PutDouble( double fVal
, SCSIZE nIndex
);
255 void PutDouble(const double* pArray
, size_t nLen
, SCSIZE nC
, SCSIZE nR
);
257 void PutString( const svl::SharedString
& rStr
, SCSIZE nC
, SCSIZE nR
) ;
258 void PutString( const svl::SharedString
& rStr
, SCSIZE nIndex
) ;
259 void PutString( const svl::SharedString
* pArray
, size_t nLen
, SCSIZE nC
, SCSIZE nR
) ;
261 void PutEmpty( SCSIZE nC
, SCSIZE nR
);
263 /// Jump sal_False without path
264 void PutEmptyPath( SCSIZE nC
, SCSIZE nR
) ;
265 void PutError( FormulaError nErrorCode
, SCSIZE nC
, SCSIZE nR
) ;
266 void PutBoolean( bool bVal
, SCSIZE nC
, SCSIZE nR
) ;
268 void FillDouble( double fVal
,
269 SCSIZE nC1
, SCSIZE nR1
, SCSIZE nC2
, SCSIZE nR2
) ;
271 /** Put a column vector of doubles, starting at row nR, must fit into dimensions. */
272 void PutDoubleVector( const ::std::vector
< double > & rVec
, SCSIZE nC
, SCSIZE nR
) ;
274 /** Put a column vector of strings, starting at row nR, must fit into dimensions. */
275 void PutStringVector( const ::std::vector
< svl::SharedString
> & rVec
, SCSIZE nC
, SCSIZE nR
) ;
277 /** Put a column vector of empties, starting at row nR, must fit into dimensions. */
278 void PutEmptyVector( SCSIZE nCount
, SCSIZE nC
, SCSIZE nR
) ;
280 /** Put a column vector of empty results, starting at row nR, must fit into dimensions. */
281 void PutEmptyResultVector( SCSIZE nCount
, SCSIZE nC
, SCSIZE nR
) ;
283 /** Put a column vector of empty paths, starting at row nR, must fit into dimensions. */
284 void PutEmptyPathVector( SCSIZE nCount
, SCSIZE nC
, SCSIZE nR
) ;
286 /** May be used before obtaining the double value of an element to avoid
287 passing its NAN around.
288 @ATTENTION: MUST NOT be used if the element is a string!
289 Use GetErrorIfNotString() instead if not sure.
290 @returns 0 if no error, else one of err... constants */
291 FormulaError
GetError( SCSIZE nC
, SCSIZE nR
) const ;
293 /** Use in ScInterpreter to obtain the error code, if any.
294 @returns 0 if no error or string element, else one of err... constants */
295 FormulaError
GetErrorIfNotString( SCSIZE nC
, SCSIZE nR
) const
296 { return IsValue( nC
, nR
) ? GetError( nC
, nR
) : FormulaError::NONE
; }
298 /// @return 0.0 if empty or empty path, else value or DoubleError.
299 double GetDouble( SCSIZE nC
, SCSIZE nR
) const ;
300 /// @return 0.0 if empty or empty path, else value or DoubleError.
301 double GetDouble( SCSIZE nIndex
) const ;
302 /// @return value or DoubleError or string converted to value.
303 double GetDoubleWithStringConversion( SCSIZE nC
, SCSIZE nR
) const ;
305 /// @return empty string if empty or empty path, else string content.
306 svl::SharedString
GetString( SCSIZE nC
, SCSIZE nR
) const ;
307 /// @return empty string if empty or empty path, else string content.
308 svl::SharedString
GetString( SCSIZE nIndex
) const ;
310 /** @returns the matrix element's string if one is present, otherwise the
311 numerical value formatted as string, or in case of an error the error
312 string is returned; an empty string for empty, a "FALSE" string for
314 svl::SharedString
GetString( SvNumberFormatter
& rFormatter
, SCSIZE nC
, SCSIZE nR
) const ;
316 /// @ATTENTION: If bString the ScMatrixValue->pS may still be NULL to indicate
318 ScMatrixValue
Get( SCSIZE nC
, SCSIZE nR
) const ;
320 /** @return <TRUE/> if string or any empty, empty cell, empty result, empty
321 path, in fact non-value. */
322 bool IsStringOrEmpty( SCSIZE nIndex
) const ;
324 /** @return <TRUE/> if string or any empty, empty cell, empty result, empty
325 path, in fact non-value. */
326 bool IsStringOrEmpty( SCSIZE nC
, SCSIZE nR
) const ;
328 /// @return <TRUE/> if empty or empty cell or empty result, not empty path.
329 bool IsEmpty( SCSIZE nC
, SCSIZE nR
) const ;
331 /// @return <TRUE/> if empty cell, not empty or empty result or empty path.
332 bool IsEmptyCell( SCSIZE nC
, SCSIZE nR
) const ;
334 /// @return <TRUE/> if empty result, not empty or empty cell or empty path.
335 bool IsEmptyResult( SCSIZE nC
, SCSIZE nR
) const ;
337 /// @return <TRUE/> if empty path, not empty or empty cell or empty result.
338 bool IsEmptyPath( SCSIZE nC
, SCSIZE nR
) const ;
340 /// @return <TRUE/> if value or boolean.
341 bool IsValue( SCSIZE nIndex
) const ;
343 /// @return <TRUE/> if value or boolean.
344 bool IsValue( SCSIZE nC
, SCSIZE nR
) const ;
346 /// @return <TRUE/> if value or boolean or empty or empty path.
347 bool IsValueOrEmpty( SCSIZE nC
, SCSIZE nR
) const ;
349 /// @return <TRUE/> if boolean.
350 bool IsBoolean( SCSIZE nC
, SCSIZE nR
) const ;
352 /// @return <TRUE/> if entire matrix is numeric, including booleans, with no strings or empties
353 bool IsNumeric() const ;
355 void MatTrans( const ScMatrix
& mRes
) const ;
356 void MatCopy ( const ScMatrix
& mRes
) const ;
358 // Convert ScInterpreter::CompareMat values (-1,0,1) to boolean values
359 void CompareEqual() ;
360 void CompareNotEqual() ;
362 void CompareGreater() ;
363 void CompareLessEqual() ;
364 void CompareGreaterEqual() ;
366 double And() const ; // logical AND of all matrix values, or NAN
367 double Or() const ; // logical OR of all matrix values, or NAN
368 double Xor() const ; // logical XOR of all matrix values, or NAN
370 KahanIterateResult
Sum( bool bTextAsZero
, bool bIgnoreErrorValues
= false ) const ;
371 KahanIterateResult
SumSquare( bool bTextAsZero
, bool bIgnoreErrorValues
= false ) const ;
372 DoubleIterateResult
Product( bool bTextAsZero
, bool bIgnoreErrorValues
= false ) const ;
373 size_t Count(bool bCountStrings
, bool bCountErrors
, bool bIgnoreEmptyStrings
= false) const ;
374 size_t MatchDoubleInColumns(double fValue
, size_t nCol1
, size_t nCol2
) const ;
375 size_t MatchStringInColumns(const svl::SharedString
& rStr
, size_t nCol1
, size_t nCol2
) const ;
377 double GetMaxValue( bool bTextAsZero
, bool bIgnoreErrorValues
= false ) const ;
378 double GetMinValue( bool bTextAsZero
, bool bIgnoreErrorValues
= false ) const ;
379 double GetGcd() const ;
380 double GetLcm() const ;
382 ScMatrixRef
CompareMatrix(
383 sc::Compare
& rComp
, size_t nMatPos
, sc::CompareOptions
* pOptions
) const ;
386 * Convert the content of matrix into a linear array of numeric values.
387 * String elements are mapped to NaN's and empty elements are mapped to
388 * either NaN or zero values.
390 * @param bEmptyAsZero if true empty elements are mapped to zero values,
391 * otherwise they become NaN values.
393 void GetDoubleArray( std::vector
<double>& rArray
, bool bEmptyAsZero
= true ) const ;
394 void MergeDoubleArrayMultiply( std::vector
<double>& rArray
) const ;
396 void NotOp(const ScMatrix
& rMat
) ;
397 void NegOp(const ScMatrix
& rMat
) ;
398 void AddOp(double fVal
, const ScMatrix
& rMat
) ;
399 void SubOp(bool bFlag
, double fVal
, const ScMatrix
& rMat
) ;
400 void MulOp(double fVal
, const ScMatrix
& rMat
) ;
401 void DivOp(bool bFlag
, double fVal
, const ScMatrix
& rMat
) ;
402 void PowOp(bool bFlag
, double fVal
, const ScMatrix
& rMat
) ;
404 KahanIterateResultMultiple
CollectKahan(const std::vector
<sc::op::kOp
>& aOp
) ;
406 void ExecuteOperation(const std::pair
<size_t, size_t>& rStartPos
, const std::pair
<size_t, size_t>& rEndPos
,
407 DoubleOpFunction aDoubleFunc
, BoolOpFunction aBoolFunc
, StringOpFunction aStringFunc
,
408 EmptyOpFunction aEmptyFunc
) const ;
410 void MatConcat(SCSIZE nMaxCol
, SCSIZE nMaxRow
, const ScMatrixRef
& xMat1
, const ScMatrixRef
& xMat2
,
411 SvNumberFormatter
& rFormatter
, svl::SharedStringPool
& rPool
) ;
418 inline void intrusive_ptr_add_ref(const ScMatrix
* p
)
423 inline void intrusive_ptr_release(const ScMatrix
* p
)
428 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */