Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */