/src/libreoffice/sc/inc/scmatrix.hxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | #pragma once |
21 | | |
22 | | #include "address.hxx" |
23 | | #include "matrixoperators.hxx" |
24 | | #include "types.hxx" |
25 | | #include <formula/errorcodes.hxx> |
26 | | #include "scdllapi.h" |
27 | | #include <svl/sharedstring.hxx> |
28 | | #include <svl/sharedstringpool.hxx> |
29 | | |
30 | | #include <memory> |
31 | | #include <utility> |
32 | | #include <vector> |
33 | | |
34 | | #define DEBUG_MATRIX 0 |
35 | | |
36 | | class ScInterpreter; |
37 | | struct ScInterpreterContext; |
38 | | class ScMatrixImpl; |
39 | | enum class FormulaError : sal_uInt16; |
40 | | class ScJumpMatrix; |
41 | | |
42 | | namespace sc { |
43 | | |
44 | | struct Compare; |
45 | | struct CompareOptions; |
46 | | |
47 | | } |
48 | | |
49 | | /** |
50 | | * Try NOT to use this struct. This struct should go away in a hopefully |
51 | | * not so distant future. |
52 | | */ |
53 | | struct ScMatrixValue |
54 | | { |
55 | | double fVal; |
56 | | svl::SharedString aStr; |
57 | | ScMatValType nType; |
58 | | |
59 | | /// Only valid if ScMatrix methods indicate so! |
60 | 147 | const svl::SharedString& GetString() const { return aStr; } |
61 | | |
62 | | /// Only valid if ScMatrix methods indicate that this is no string! |
63 | 224 | FormulaError GetError() const { return GetDoubleErrorValue(fVal); } |
64 | | |
65 | | /// Only valid if ScMatrix methods indicate that this is a boolean |
66 | 0 | bool GetBoolean() const { return fVal != 0.0; } |
67 | | |
68 | 23.3k | ScMatrixValue() : fVal(0.0), nType(ScMatValType::Empty) {} |
69 | | ScMatrixValue(const ScMatrixValue& r) = default; |
70 | 224 | ScMatrixValue& operator= (const ScMatrixValue& r) = default; |
71 | | |
72 | | bool operator== (const ScMatrixValue& r) const |
73 | 0 | { |
74 | 0 | if (nType != r.nType) |
75 | 0 | return false; |
76 | | |
77 | 0 | switch (nType) |
78 | 0 | { |
79 | 0 | case ScMatValType::Value: |
80 | 0 | case ScMatValType::Boolean: |
81 | 0 | return fVal == r.fVal; |
82 | 0 | break; |
83 | 0 | default: |
84 | 0 | ; |
85 | 0 | } |
86 | | |
87 | 0 | return aStr == r.aStr; |
88 | 0 | } |
89 | | |
90 | | bool operator!= (const ScMatrixValue& r) const |
91 | 0 | { |
92 | 0 | return !operator==(r); |
93 | 0 | } |
94 | | }; |
95 | | |
96 | | /** |
97 | | * Matrix data type that can store values of mixed types. Each element can |
98 | | * be one of the following types: numeric, string, boolean, empty, and empty |
99 | | * path. |
100 | | */ |
101 | | class ScMatrix final |
102 | | { |
103 | | friend class ScMatrixImpl; |
104 | | |
105 | | mutable size_t nRefCnt; // reference count |
106 | | mutable bool mbCloneIfConst; // Whether the matrix is cloned with a CloneIfConst() call. |
107 | | std::unique_ptr<ScMatrixImpl> pImpl; |
108 | | |
109 | | ScMatrix( const ScMatrix& ) = delete; |
110 | | ScMatrix& operator=( const ScMatrix&) = delete; |
111 | | |
112 | | public: |
113 | | SC_DLLPUBLIC ScMatrix(SCSIZE nC, SCSIZE nR); |
114 | | SC_DLLPUBLIC ScMatrix(SCSIZE nC, SCSIZE nR, double fInitVal); |
115 | | ScMatrix( size_t nC, size_t nR, const std::vector<double>& rInitVals ); |
116 | | ~ScMatrix(); |
117 | | |
118 | | typedef std::function<void(size_t, size_t, double)> DoubleOpFunction; |
119 | | typedef std::function<void(size_t, size_t, bool)> BoolOpFunction; |
120 | | typedef std::function<void(size_t, size_t, svl::SharedString)> StringOpFunction; |
121 | | typedef std::function<void(size_t, size_t)> EmptyOpFunction; |
122 | | typedef std::function<double(double, double)> CalculateOpFunction; |
123 | | |
124 | | /** |
125 | | * When adding all numerical matrix elements for a scalar result such as |
126 | | * summation, the interpreter wants to separate the first non-zero value |
127 | | * with the rest of the summed values. This is necessary for better |
128 | | * numerical stability, unless we sort all by absolute values before |
129 | | * summing (not really an option) or use another algorithm, e.g. Kahan's |
130 | | * summation algorithm, |
131 | | * https://en.wikipedia.org/wiki/Kahan_summation_algorithm |
132 | | */ |
133 | | template<typename tRes> |
134 | | struct IterateResultMultiple |
135 | | { |
136 | | std::vector<tRes> maAccumulator; |
137 | | size_t mnCount; |
138 | | |
139 | | IterateResultMultiple(size_t nCount) : |
140 | 306 | maAccumulator(0), mnCount(nCount) {} |
141 | | }; |
142 | | typedef IterateResultMultiple<KahanSum> KahanIterateResultMultiple; |
143 | | typedef IterateResultMultiple<double> DoubleIterateResultMultiple; |
144 | | |
145 | | /** |
146 | | * Iterator for executing one operation with the matrix data. |
147 | | */ |
148 | | template<typename tRes> |
149 | | struct IterateResult |
150 | | { |
151 | | tRes maAccumulator; |
152 | | size_t mnCount; |
153 | | |
154 | | IterateResult(tRes fAccumulator, size_t nCount) |
155 | 0 | : maAccumulator(fAccumulator), mnCount(nCount) {} |
156 | | }; |
157 | | typedef IterateResult<KahanSum> KahanIterateResult; |
158 | | typedef IterateResult<double> DoubleIterateResult; |
159 | | |
160 | | |
161 | | /** Checks nC or nR for zero and uses GetElementsMax() whether a matrix of |
162 | | the size of nC*nR could be allocated. A zero size (both nC and nR zero) |
163 | | matrix is allowed for later resize. |
164 | | */ |
165 | | bool static IsSizeAllocatable( SCSIZE nC, SCSIZE nR ); |
166 | | |
167 | | /// Value or boolean. |
168 | | static bool IsValueType( ScMatValType nType ) |
169 | 587 | { |
170 | 587 | return nType <= ScMatValType::Boolean; |
171 | 587 | } |
172 | | |
173 | | /// Boolean. |
174 | | static bool IsBooleanType( ScMatValType nType ) |
175 | 0 | { |
176 | 0 | return nType == ScMatValType::Boolean; |
177 | 0 | } |
178 | | |
179 | | /// String, empty or empty path, but not value nor boolean. |
180 | | static bool IsNonValueType( ScMatValType nType ) |
181 | 228 | { |
182 | 228 | return bool(nType & ScMatValType::NonvalueMask); |
183 | 228 | } |
184 | | |
185 | | /** String, but not empty or empty path or any other type. |
186 | | Not named IsStringType to prevent confusion because previously |
187 | | IsNonValueType was named IsStringType. */ |
188 | | static bool IsRealStringType( ScMatValType nType ) |
189 | 139 | { |
190 | 139 | return (nType & ScMatValType::NonvalueMask) == ScMatValType::String; |
191 | 139 | } |
192 | | |
193 | | /// Empty, but not empty path or any other type. |
194 | | static bool IsEmptyType( ScMatValType nType ) |
195 | 0 | { |
196 | 0 | return (nType & ScMatValType::NonvalueMask) == ScMatValType::Empty; |
197 | 0 | } |
198 | | |
199 | | /// Empty path, but not empty or any other type. |
200 | | static bool IsEmptyPathType( ScMatValType nType ) |
201 | 0 | { |
202 | 0 | return (nType & ScMatValType::NonvalueMask) == ScMatValType::EmptyPath; |
203 | 0 | } |
204 | | |
205 | | /** Clone the matrix. */ |
206 | | ScMatrix* Clone() const; |
207 | | |
208 | | /** Clone the matrix if mbCloneIfConst (immutable) is set, otherwise |
209 | | return _this_ matrix, to be assigned to a ScMatrixRef. */ |
210 | | ScMatrix* CloneIfConst(); |
211 | | |
212 | | /** Set the matrix to mutable for CloneIfConst(), only the interpreter |
213 | | should do this and know the consequences. */ |
214 | | void SetMutable(); |
215 | | |
216 | | /** Set the matrix to immutable for CloneIfConst(), only the interpreter |
217 | | should do this and know the consequences. */ |
218 | | void SetImmutable() const; |
219 | | |
220 | | /** |
221 | | * Resize the matrix to specified new dimension. |
222 | | */ |
223 | | SC_DLLPUBLIC void Resize(SCSIZE nC, SCSIZE nR); |
224 | | |
225 | | void Resize(SCSIZE nC, SCSIZE nR, double fVal); |
226 | | |
227 | | /** Clone the matrix and extend it to the new size. nNewCols and nNewRows |
228 | | MUST be at least of the size of the original matrix. */ |
229 | | ScMatrix* CloneAndExtend(SCSIZE nNewCols, SCSIZE nNewRows) const; |
230 | | |
231 | | SC_DLLPUBLIC void IncRef() const; |
232 | | SC_DLLPUBLIC void DecRef() const; |
233 | | |
234 | | void SetErrorInterpreter( ScInterpreter* p); |
235 | | SC_DLLPUBLIC void GetDimensions( SCSIZE& rC, SCSIZE& rR) const; |
236 | | SCSIZE GetElementCount() const; |
237 | | bool ValidColRow( SCSIZE nC, SCSIZE nR) const; |
238 | | |
239 | | /** For a row vector or column vector, if the position does not point into |
240 | | the vector but is a valid column or row offset it is adapted such that |
241 | | it points to an element to be replicated, same column row 0 for a row |
242 | | vector, same row column 0 for a column vector. Else, for a 2D matrix, |
243 | | returns false. |
244 | | */ |
245 | | bool ValidColRowReplicated( SCSIZE & rC, SCSIZE & rR ) const; |
246 | | |
247 | | /** Checks if the matrix position is within the matrix. If it is not, for a |
248 | | row vector or column vector the position is adapted such that it points |
249 | | to an element to be replicated, same column row 0 for a row vector, |
250 | | same row column 0 for a column vector. Else, for a 2D matrix and |
251 | | position not within matrix, returns false. |
252 | | */ |
253 | | bool ValidColRowOrReplicated( SCSIZE & rC, SCSIZE & rR ) const; |
254 | | |
255 | | SC_DLLPUBLIC void PutDouble( double fVal, SCSIZE nC, SCSIZE nR); |
256 | | void PutDouble( double fVal, SCSIZE nIndex); |
257 | | void PutDoubleTrans( double fVal, SCSIZE nIndex); |
258 | | void PutDouble(const double* pArray, size_t nLen, SCSIZE nC, SCSIZE nR); |
259 | | |
260 | | SC_DLLPUBLIC void PutString( const svl::SharedString& rStr, SCSIZE nC, SCSIZE nR) ; |
261 | | void PutString( const svl::SharedString& rStr, SCSIZE nIndex) ; |
262 | | void PutStringTrans( const svl::SharedString& rStr, SCSIZE nIndex) ; |
263 | | void PutString( const svl::SharedString* pArray, size_t nLen, SCSIZE nC, SCSIZE nR) ; |
264 | | |
265 | | SC_DLLPUBLIC void PutEmpty( SCSIZE nC, SCSIZE nR); |
266 | | void PutEmpty(SCSIZE nIndex); |
267 | | void PutEmptyTrans( SCSIZE nIndex ); |
268 | | |
269 | | /// Jump sal_False without path |
270 | | void PutEmptyPath( SCSIZE nC, SCSIZE nR) ; |
271 | | SC_DLLPUBLIC void PutError( FormulaError nErrorCode, SCSIZE nC, SCSIZE nR ) ; |
272 | | SC_DLLPUBLIC void PutBoolean( bool bVal, SCSIZE nC, SCSIZE nR) ; |
273 | | |
274 | | void FillDouble( double fVal, |
275 | | SCSIZE nC1, SCSIZE nR1, SCSIZE nC2, SCSIZE nR2 ) ; |
276 | | |
277 | | /** Put a column vector of doubles, starting at row nR, must fit into dimensions. */ |
278 | | void PutDoubleVector( const ::std::vector< double > & rVec, SCSIZE nC, SCSIZE nR ) ; |
279 | | |
280 | | /** Put a column vector of strings, starting at row nR, must fit into dimensions. */ |
281 | | void PutStringVector( const ::std::vector< svl::SharedString > & rVec, SCSIZE nC, SCSIZE nR ) ; |
282 | | |
283 | | /** Put a column vector of empties, starting at row nR, must fit into dimensions. */ |
284 | | void PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR ) ; |
285 | | |
286 | | /** Put a column vector of empty results, starting at row nR, must fit into dimensions. */ |
287 | | void PutEmptyResultVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR ) ; |
288 | | |
289 | | /** Put a column vector of empty paths, starting at row nR, must fit into dimensions. */ |
290 | | void PutEmptyPathVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR ) ; |
291 | | |
292 | | /** May be used before obtaining the double value of an element to avoid |
293 | | passing its NAN around. |
294 | | @ATTENTION: MUST NOT be used if the element is a string! |
295 | | Use GetErrorIfNotString() instead if not sure. |
296 | | @returns 0 if no error, else one of err... constants */ |
297 | | FormulaError GetError( SCSIZE nC, SCSIZE nR) const ; |
298 | | |
299 | | /** Use in ScInterpreter to obtain the error code, if any. |
300 | | @returns 0 if no error or string element, else one of err... constants */ |
301 | | FormulaError GetErrorIfNotString( SCSIZE nC, SCSIZE nR) const |
302 | 30 | { return IsValue( nC, nR) ? GetError( nC, nR) : FormulaError::NONE; } |
303 | | |
304 | | /// @return 0.0 if empty or empty path, else value or DoubleError. |
305 | | double GetDouble( SCSIZE nC, SCSIZE nR) const ; |
306 | | /// @return 0.0 if empty or empty path, else value or DoubleError. |
307 | | double GetDouble( SCSIZE nIndex) const ; |
308 | | /// @return value or DoubleError or string converted to value. |
309 | | double GetDoubleWithStringConversion( SCSIZE nC, SCSIZE nR ) const ; |
310 | | |
311 | | /// @return empty string if empty or empty path, else string content. |
312 | | svl::SharedString GetString( SCSIZE nC, SCSIZE nR) const ; |
313 | | /// @return empty string if empty or empty path, else string content. |
314 | | svl::SharedString GetString( SCSIZE nIndex) const ; |
315 | | |
316 | | /** @returns the matrix element's string if one is present, otherwise the |
317 | | numerical value formatted as string, or in case of an error the error |
318 | | string is returned; an empty string for empty, a "FALSE" string for |
319 | | empty path. */ |
320 | | svl::SharedString GetString( ScInterpreterContext& rContext, SCSIZE nC, SCSIZE nR) const ; |
321 | | |
322 | | /// @ATTENTION: If bString the ScMatrixValue->pS may still be NULL to indicate |
323 | | /// an empty string! |
324 | | SC_DLLPUBLIC ScMatrixValue Get( SCSIZE nC, SCSIZE nR) const ; |
325 | | |
326 | | /** @return <TRUE/> if string or any empty, empty cell, empty result, empty |
327 | | path, in fact non-value. */ |
328 | | bool IsStringOrEmpty( SCSIZE nIndex ) const ; |
329 | | |
330 | | /** @return <TRUE/> if string or any empty, empty cell, empty result, empty |
331 | | path, in fact non-value. */ |
332 | | bool IsStringOrEmpty( SCSIZE nC, SCSIZE nR ) const ; |
333 | | |
334 | | /// @return <TRUE/> if empty or empty cell or empty result, not empty path. |
335 | | bool IsEmpty( SCSIZE nC, SCSIZE nR ) const ; |
336 | | |
337 | | /// @return <TRUE/> if empty cell, not empty or empty result or empty path. |
338 | | bool IsEmptyCell( SCSIZE nC, SCSIZE nR ) const ; |
339 | | |
340 | | /// @return <TRUE/> if empty result, not empty or empty cell or empty path. |
341 | | bool IsEmptyResult( SCSIZE nC, SCSIZE nR ) const ; |
342 | | |
343 | | /// @return <TRUE/> if empty path, not empty or empty cell or empty result. |
344 | | bool IsEmptyPath( SCSIZE nC, SCSIZE nR ) const ; |
345 | | |
346 | | /// @return <TRUE/> if value or boolean. |
347 | | bool IsValue( SCSIZE nIndex ) const ; |
348 | | |
349 | | /// @return <TRUE/> if value or boolean. |
350 | | bool IsValue( SCSIZE nC, SCSIZE nR ) const ; |
351 | | |
352 | | /// @return <TRUE/> if value or boolean or empty or empty path. |
353 | | bool IsValueOrEmpty( SCSIZE nC, SCSIZE nR ) const ; |
354 | | |
355 | | /// @return <TRUE/> if boolean. |
356 | | bool IsBoolean( SCSIZE nC, SCSIZE nR ) const ; |
357 | | |
358 | | /// @return <TRUE/> if entire matrix is numeric, including booleans, with no strings or empties |
359 | | bool IsNumeric() const ; |
360 | | |
361 | | void MatTrans( const ScMatrix& mRes) const ; |
362 | | void MatCopy ( const ScMatrix& mRes) const ; |
363 | | |
364 | | // Convert ScInterpreter::CompareMat values (-1,0,1) to boolean values |
365 | | void CompareEqual() ; |
366 | | void CompareNotEqual() ; |
367 | | void CompareLess() ; |
368 | | void CompareGreater() ; |
369 | | void CompareLessEqual() ; |
370 | | void CompareGreaterEqual() ; |
371 | | |
372 | | double And() const ; // logical AND of all matrix values, or NAN |
373 | | double Or() const ; // logical OR of all matrix values, or NAN |
374 | | double Xor() const ; // logical XOR of all matrix values, or NAN |
375 | | |
376 | | KahanIterateResult Sum( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ; |
377 | | KahanIterateResult SumSquare( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ; |
378 | | DoubleIterateResult Product( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ; |
379 | | size_t Count(bool bCountStrings, bool bCountErrors, bool bIgnoreEmptyStrings = false) const ; |
380 | | size_t MatchDoubleInColumns(double fValue, size_t nCol1, size_t nCol2) const ; |
381 | | size_t MatchStringInColumns(const svl::SharedString& rStr, size_t nCol1, size_t nCol2) const ; |
382 | | void IfJump( ScJumpMatrix& rJumpMatrix, const short* pJump, short nJumpCount ) const ; |
383 | | |
384 | | double GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ; |
385 | | double GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues = false ) const ; |
386 | | double GetGcd() const ; |
387 | | double GetLcm() const ; |
388 | | |
389 | | ScMatrixRef CompareMatrix( |
390 | | sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) const ; |
391 | | |
392 | | /** |
393 | | * Convert the content of matrix into a linear array of numeric values. |
394 | | * String elements are mapped to NaN's and empty elements are mapped to |
395 | | * either NaN or zero values. |
396 | | * |
397 | | * @param bEmptyAsZero if true empty elements are mapped to zero values, |
398 | | * otherwise they become NaN values. |
399 | | */ |
400 | | void GetDoubleArray( std::vector<double>& rArray, bool bEmptyAsZero = true ) const ; |
401 | | void MergeDoubleArrayMultiply( std::vector<double>& rArray ) const ; |
402 | | |
403 | | void NotOp(const ScMatrix& rMat) ; |
404 | | void NegOp(const ScMatrix& rMat) ; |
405 | | void AddOp(double fVal, const ScMatrix& rMat) ; |
406 | | void SubOp(bool bFlag, double fVal, const ScMatrix& rMat) ; |
407 | | void MulOp(double fVal, const ScMatrix& rMat) ; |
408 | | void DivOp(bool bFlag, double fVal, const ScMatrix& rMat) ; |
409 | | void PowOp(bool bFlag, double fVal, const ScMatrix& rMat) ; |
410 | | |
411 | | KahanIterateResultMultiple CollectKahan(const std::vector<sc::op::kOp>& aOp) ; |
412 | | |
413 | | void ExecuteOperation(const std::pair<size_t, size_t>& rStartPos, const std::pair<size_t, size_t>& rEndPos, |
414 | | DoubleOpFunction aDoubleFunc, BoolOpFunction aBoolFunc, StringOpFunction aStringFunc, |
415 | | EmptyOpFunction aEmptyFunc) const ; |
416 | | |
417 | | void MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrixRef& xMat1, const ScMatrixRef& xMat2, |
418 | | ScInterpreterContext& rContext, svl::SharedStringPool& rPool) ; |
419 | | |
420 | | /** Apply binary operation to values from two input matrices, storing result into this matrix. */ |
421 | | void ExecuteBinaryOp(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrix& rInputMat1, const ScMatrix& rInputMat2, |
422 | | ScInterpreter* pInterpreter, const CalculateOpFunction& op); |
423 | | |
424 | | #if DEBUG_MATRIX |
425 | | void Dump() const; |
426 | | #endif |
427 | | }; |
428 | | |
429 | | inline void intrusive_ptr_add_ref(const ScMatrix* p) |
430 | 106k | { |
431 | 106k | p->IncRef(); |
432 | 106k | } |
433 | | |
434 | | inline void intrusive_ptr_release(const ScMatrix* p) |
435 | 106k | { |
436 | 106k | p->DecRef(); |
437 | 106k | } |
438 | | |
439 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |