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 .
20 #include <rtl/math.hxx>
24 #ifdef DEBUG_SC_LUP_DECOMPOSITION
28 #include <unotools/bootstrap.hxx>
29 #include <svl/zforlist.hxx>
30 #include <tools/duration.hxx>
32 #include <interpre.hxx>
34 #include <formulacell.hxx>
35 #include <document.hxx>
36 #include <dociter.hxx>
37 #include <scmatrix.hxx>
38 #include <globstr.hrc>
39 #include <scresid.hxx>
40 #include <cellkeytranslator.hxx>
41 #include <formulagroup.hxx>
42 #include <vcl/svapp.hxx> //Application::
47 using namespace formula
;
51 double MatrixAdd(const double& lhs
, const double& rhs
)
53 return ::rtl::math::approxAdd( lhs
,rhs
);
56 double MatrixSub(const double& lhs
, const double& rhs
)
58 return ::rtl::math::approxSub( lhs
,rhs
);
61 double MatrixMul(const double& lhs
, const double& rhs
)
66 double MatrixDiv(const double& lhs
, const double& rhs
)
68 return ScInterpreter::div( lhs
,rhs
);
71 double MatrixPow(const double& lhs
, const double& rhs
)
73 return ::pow( lhs
,rhs
);
76 // Multiply n x m Mat A with m x l Mat B to n x l Mat R
77 void lcl_MFastMult(const ScMatrixRef
& pA
, const ScMatrixRef
& pB
, const ScMatrixRef
& pR
,
78 SCSIZE n
, SCSIZE m
, SCSIZE l
)
80 for (SCSIZE row
= 0; row
< n
; row
++)
82 for (SCSIZE col
= 0; col
< l
; col
++)
83 { // result element(col, row) =sum[ (row of A) * (column of B)]
85 for (SCSIZE k
= 0; k
< m
; k
++)
86 fSum
+= pA
->GetDouble(k
,row
) * pB
->GetDouble(col
,k
);
87 pR
->PutDouble(fSum
.get(), col
, row
);
94 double ScInterpreter::ScGetGCD(double fx
, double fy
)
96 // By ODFF definition GCD(0,a) => a. This is also vital for the code in
97 // ScGCD() to work correctly with a preset fy=0.0
104 double fz
= fmod(fx
, fy
);
115 void ScInterpreter::ScGCD()
117 short nParamCount
= GetByte();
118 if ( !MustHaveParamCountMin( nParamCount
, 1 ) )
123 size_t nRefInList
= 0;
124 while (nGlobalError
== FormulaError::NONE
&& nParamCount
-- > 0)
126 switch (GetStackType())
132 fx
= ::rtl::math::approxFloor( GetDouble());
135 PushIllegalArgument();
138 fy
= ScGetGCD(fx
, fy
);
144 FormulaError nErr
= FormulaError::NONE
;
145 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
147 ScValueIterator
aValIter( mrContext
, aRange
, mnSubTotalFlags
);
148 if (aValIter
.GetFirst(nCellVal
, nErr
))
152 fx
= ::rtl::math::approxFloor( nCellVal
);
155 PushIllegalArgument();
158 fy
= ScGetGCD(fx
, fy
);
159 } while (nErr
== FormulaError::NONE
&& aValIter
.GetNext(nCellVal
, nErr
));
165 case svExternalSingleRef
:
166 case svExternalDoubleRef
:
168 ScMatrixRef pMat
= GetMatrix();
172 pMat
->GetDimensions(nC
, nR
);
173 if (nC
== 0 || nR
== 0)
174 SetError(FormulaError::IllegalArgument
);
177 double nVal
= pMat
->GetGcd();
178 fy
= ScGetGCD(nVal
,fy
);
183 default : SetError(FormulaError::IllegalParameter
); break;
189 void ScInterpreter:: ScLCM()
191 short nParamCount
= GetByte();
192 if ( !MustHaveParamCountMin( nParamCount
, 1 ) )
197 size_t nRefInList
= 0;
198 while (nGlobalError
== FormulaError::NONE
&& nParamCount
-- > 0)
200 switch (GetStackType())
206 fx
= ::rtl::math::approxFloor( GetDouble());
209 PushIllegalArgument();
212 if (fx
== 0.0 || fy
== 0.0)
215 fy
= fx
* fy
/ ScGetGCD(fx
, fy
);
221 FormulaError nErr
= FormulaError::NONE
;
222 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
224 ScValueIterator
aValIter( mrContext
, aRange
, mnSubTotalFlags
);
225 if (aValIter
.GetFirst(nCellVal
, nErr
))
229 fx
= ::rtl::math::approxFloor( nCellVal
);
232 PushIllegalArgument();
235 if (fx
== 0.0 || fy
== 0.0)
238 fy
= fx
* fy
/ ScGetGCD(fx
, fy
);
239 } while (nErr
== FormulaError::NONE
&& aValIter
.GetNext(nCellVal
, nErr
));
245 case svExternalSingleRef
:
246 case svExternalDoubleRef
:
248 ScMatrixRef pMat
= GetMatrix();
252 pMat
->GetDimensions(nC
, nR
);
253 if (nC
== 0 || nR
== 0)
254 SetError(FormulaError::IllegalArgument
);
257 double nVal
= pMat
->GetLcm();
258 fy
= (nVal
* fy
) / ScGetGCD(nVal
, fy
);
263 default : SetError(FormulaError::IllegalParameter
); break;
269 void ScInterpreter::MakeMatNew(ScMatrixRef
& rMat
, SCSIZE nC
, SCSIZE nR
)
271 rMat
->SetErrorInterpreter( this);
272 // A temporary matrix is mutable and ScMatrix::CloneIfConst() returns the
276 rMat
->GetDimensions( nCols
, nRows
);
277 if ( nCols
!= nC
|| nRows
!= nR
)
278 { // arbitrary limit of elements exceeded
279 SetError( FormulaError::MatrixSize
);
284 ScMatrixRef
ScInterpreter::GetNewMat(SCSIZE nC
, SCSIZE nR
, bool bEmpty
)
288 pMat
= new ScMatrix(nC
, nR
);
290 pMat
= new ScMatrix(nC
, nR
, 0.0);
291 MakeMatNew(pMat
, nC
, nR
);
295 ScMatrixRef
ScInterpreter::GetNewMat(SCSIZE nC
, SCSIZE nR
, const std::vector
<double>& rValues
)
297 ScMatrixRef
pMat(new ScMatrix(nC
, nR
, rValues
));
298 MakeMatNew(pMat
, nC
, nR
);
302 ScMatrixRef
ScInterpreter::CreateMatrixFromDoubleRef( const FormulaToken
* pToken
,
303 SCCOL nCol1
, SCROW nRow1
, SCTAB nTab1
,
304 SCCOL nCol2
, SCROW nRow2
, SCTAB nTab2
)
306 if (nTab1
!= nTab2
|| nGlobalError
!= FormulaError::NONE
)
309 SetError(FormulaError::IllegalParameter
);
313 if (nTab1
== nTab2
&& pToken
)
315 const ScComplexRefData
& rCRef
= *pToken
->GetDoubleRef();
316 if (rCRef
.IsTrimToData())
318 // Clamp the size of the matrix area to rows which actually contain data.
319 // For e.g. SUM(IF over an entire column, this can make a big difference,
320 // But let's not trim the empty space from the top or left as this matters
321 // at least in matrix formulas involving IF().
322 // Refer ScCompiler::AnnotateTrimOnDoubleRefs() where double-refs are
323 // flagged for trimming.
324 SCCOL nTempCol
= nCol1
;
325 SCROW nTempRow
= nRow1
;
326 mrDoc
.ShrinkToDataArea(nTab1
, nTempCol
, nTempRow
, nCol2
, nRow2
);
330 SCSIZE nMatCols
= static_cast<SCSIZE
>(nCol2
- nCol1
+ 1);
331 SCSIZE nMatRows
= static_cast<SCSIZE
>(nRow2
- nRow1
+ 1);
333 if (!ScMatrix::IsSizeAllocatable( nMatCols
, nMatRows
))
335 SetError(FormulaError::MatrixSize
);
339 ScTokenMatrixMap::const_iterator aIter
;
340 if (pToken
&& ((aIter
= maTokenMatrixMap
.find( pToken
)) != maTokenMatrixMap
.end()))
342 /* XXX casting const away here is ugly; ScMatrixToken (to which the
343 * result of this function usually is assigned) should not be forced to
344 * carry a ScConstMatrixRef though.
345 * TODO: a matrix already stored in pTokenMatrixMap should be
346 * read-only and have a copy-on-write mechanism. Previously all tokens
347 * were modifiable so we're already better than before ... */
348 return const_cast<FormulaToken
*>((*aIter
).second
.get())->GetMatrix();
351 ScMatrixRef pMat
= GetNewMat( nMatCols
, nMatRows
, true);
352 if (!pMat
|| nGlobalError
!= FormulaError::NONE
)
357 // Use fast array fill.
358 mrDoc
.FillMatrix(*pMat
, nTab1
, nCol1
, nRow1
, nCol2
, nRow2
);
362 // Use slower ScCellIterator to round values.
364 // TODO: this probably could use CellBucket for faster storage, see
365 // sc/source/core/data/column2.cxx and FillMatrixHandler, and then be
366 // moved to a function on its own, and/or squeeze the rounding into a
367 // similar FillMatrixHandler that would need to keep track of the cell
370 // Set position where the next entry is expected.
371 SCROW nNextRow
= nRow1
;
372 SCCOL nNextCol
= nCol1
;
373 // Set last position as if there was a previous entry.
374 SCROW nThisRow
= nRow2
;
375 SCCOL nThisCol
= nCol1
- 1;
377 ScCellIterator
aCellIter( mrDoc
, ScRange( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
));
378 for (bool bHas
= aCellIter
.first(); bHas
; bHas
= aCellIter
.next())
380 nThisCol
= aCellIter
.GetPos().Col();
381 nThisRow
= aCellIter
.GetPos().Row();
382 if (nThisCol
!= nNextCol
|| nThisRow
!= nNextRow
)
384 // Fill empty between iterator's positions.
385 for ( ; nNextCol
<= nThisCol
; ++nNextCol
)
387 const SCSIZE nC
= nNextCol
- nCol1
;
388 const SCSIZE nMatStopRow
= ((nNextCol
< nThisCol
) ? nMatRows
: nThisRow
- nRow1
);
389 for (SCSIZE nR
= nNextRow
- nRow1
; nR
< nMatStopRow
; ++nR
)
391 pMat
->PutEmpty( nC
, nR
);
396 if (nThisRow
== nRow2
)
398 nNextCol
= nThisCol
+ 1;
404 nNextRow
= nThisRow
+ 1;
407 const SCSIZE nMatCol
= static_cast<SCSIZE
>(nThisCol
- nCol1
);
408 const SCSIZE nMatRow
= static_cast<SCSIZE
>(nThisRow
- nRow1
);
409 ScRefCellValue
aCell( aCellIter
.getRefCellValue());
410 if (aCellIter
.isEmpty() || aCell
.hasEmptyValue())
412 pMat
->PutEmpty( nMatCol
, nMatRow
);
414 else if (aCell
.hasError())
416 pMat
->PutError( aCell
.getFormula()->GetErrCode(), nMatCol
, nMatRow
);
418 else if (aCell
.hasNumeric())
420 double fVal
= aCell
.getValue();
421 // CELLTYPE_FORMULA already stores the rounded value.
422 if (aCell
.getType() == CELLTYPE_VALUE
)
424 // TODO: this could be moved to ScCellIterator to take
425 // advantage of the faster ScAttrArray_IterGetNumberFormat.
426 const ScAddress
aAdr( nThisCol
, nThisRow
, nTab1
);
427 const sal_uInt32 nNumFormat
= mrDoc
.GetNumberFormat( mrContext
, aAdr
);
428 fVal
= mrDoc
.RoundValueAsShown( fVal
, nNumFormat
, &mrContext
);
430 pMat
->PutDouble( fVal
, nMatCol
, nMatRow
);
432 else if (aCell
.hasString())
434 pMat
->PutString( mrStrPool
.intern( aCell
.getString(&mrDoc
)), nMatCol
, nMatRow
);
438 assert(!"aCell.what?");
439 pMat
->PutEmpty( nMatCol
, nMatRow
);
443 // Fill empty if iterator's last position wasn't the end.
444 if (nThisCol
!= nCol2
|| nThisRow
!= nRow2
)
446 for ( ; nNextCol
<= nCol2
; ++nNextCol
)
448 SCSIZE nC
= nNextCol
- nCol1
;
449 for (SCSIZE nR
= nNextRow
- nRow1
; nR
< nMatRows
; ++nR
)
451 pMat
->PutEmpty( nC
, nR
);
459 maTokenMatrixMap
.emplace(pToken
, new ScMatrixToken( pMat
));
464 ScMatrixRef
ScInterpreter::GetMatrix()
466 ScMatrixRef pMat
= nullptr;
467 switch (GetRawStackType())
472 PopSingleRef( aAdr
);
473 pMat
= GetNewMat(1, 1);
476 ScRefCellValue
aCell(mrDoc
, aAdr
);
477 if (aCell
.hasEmptyValue())
478 pMat
->PutEmpty(0, 0);
479 else if (aCell
.hasNumeric())
480 pMat
->PutDouble(GetCellValue(aAdr
, aCell
), 0);
483 svl::SharedString aStr
;
484 GetCellString(aStr
, aCell
);
485 pMat
->PutString(aStr
, 0);
495 const formula::FormulaToken
* p
= sp
? pStack
[sp
-1] : nullptr;
496 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
497 pMat
= CreateMatrixFromDoubleRef( p
, nCol1
, nRow1
, nTab1
,
498 nCol2
, nRow2
, nTab2
);
508 double fVal
= GetDouble();
509 pMat
= GetNewMat( 1, 1);
512 if ( nGlobalError
!= FormulaError::NONE
)
514 fVal
= CreateDoubleError( nGlobalError
);
515 nGlobalError
= FormulaError::NONE
;
517 pMat
->PutDouble( fVal
, 0);
523 svl::SharedString aStr
= GetString();
524 pMat
= GetNewMat( 1, 1);
527 if ( nGlobalError
!= FormulaError::NONE
)
529 double fVal
= CreateDoubleError( nGlobalError
);
530 pMat
->PutDouble( fVal
, 0);
531 nGlobalError
= FormulaError::NONE
;
534 pMat
->PutString(aStr
, 0);
538 case svExternalSingleRef
:
540 ScExternalRefCache::TokenRef pToken
;
541 PopExternalSingleRef(pToken
);
542 pMat
= GetNewMat( 1, 1, true);
545 if (nGlobalError
!= FormulaError::NONE
)
547 pMat
->PutError( nGlobalError
, 0, 0);
548 nGlobalError
= FormulaError::NONE
;
551 switch (pToken
->GetType())
554 pMat
->PutError( pToken
->GetError(), 0, 0);
557 pMat
->PutDouble( pToken
->GetDouble(), 0, 0);
560 pMat
->PutString( pToken
->GetString(), 0, 0);
563 ; // nothing, empty element matrix
567 case svExternalDoubleRef
:
568 PopExternalDoubleRef(pMat
);
572 SetError( FormulaError::IllegalArgument
);
578 ScMatrixRef
ScInterpreter::GetMatrix( short & rParam
, size_t & rRefInList
)
580 switch (GetRawStackType())
584 ScRange
aRange( ScAddress::INITIALIZE_INVALID
);
585 PopDoubleRef( aRange
, rParam
, rRefInList
);
586 if (nGlobalError
!= FormulaError::NONE
)
592 aRange
.GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
593 return CreateMatrixFromDoubleRef( nullptr, nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
601 sc::RangeMatrix
ScInterpreter::GetRangeMatrix()
603 sc::RangeMatrix aRet
;
604 switch (GetRawStackType())
607 aRet
= PopRangeMatrix();
610 aRet
.mpMat
= GetMatrix();
615 void ScInterpreter::ScMatValue()
617 if ( !MustHaveParamCount( GetByte(), 3 ) )
621 // Theoretically we could have GetSize() instead of GetUInt32(), but
622 // really, practically ...
623 SCSIZE nR
= static_cast<SCSIZE
>(GetUInt32());
624 SCSIZE nC
= static_cast<SCSIZE
>(GetUInt32());
625 if (nGlobalError
!= FormulaError::NONE
)
627 PushError( nGlobalError
);
630 switch (GetStackType())
635 PopSingleRef( aAdr
);
636 ScRefCellValue
aCell(mrDoc
, aAdr
);
637 if (aCell
.getType() == CELLTYPE_FORMULA
)
639 FormulaError nErrCode
= aCell
.getFormula()->GetErrCode();
640 if (nErrCode
!= FormulaError::NONE
)
641 PushError( nErrCode
);
644 const ScMatrix
* pMat
= aCell
.getFormula()->GetMatrix();
645 CalculateMatrixValue(pMat
,nC
,nR
);
649 PushIllegalParameter();
660 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
661 if (nCol2
- nCol1
>= static_cast<SCCOL
>(nR
) &&
662 nRow2
- nRow1
>= static_cast<SCROW
>(nC
) &&
665 ScAddress
aAdr( sal::static_int_cast
<SCCOL
>( nCol1
+ nR
),
666 sal::static_int_cast
<SCROW
>( nRow1
+ nC
), nTab1
);
667 ScRefCellValue
aCell(mrDoc
, aAdr
);
668 if (aCell
.hasNumeric())
669 PushDouble(GetCellValue(aAdr
, aCell
));
672 svl::SharedString aStr
;
673 GetCellString(aStr
, aCell
);
683 ScMatrixRef pMat
= PopMatrix();
684 CalculateMatrixValue(pMat
.get(),nC
,nR
);
689 PushIllegalParameter();
693 void ScInterpreter::CalculateMatrixValue(const ScMatrix
* pMat
,SCSIZE nC
,SCSIZE nR
)
698 pMat
->GetDimensions(nCl
, nRw
);
699 if (nC
< nCl
&& nR
< nRw
)
701 const ScMatrixValue nMatVal
= pMat
->Get( nC
, nR
);
702 ScMatValType nMatValType
= nMatVal
.nType
;
703 if (ScMatrix::IsNonValueType( nMatValType
))
704 PushString( nMatVal
.GetString() );
706 PushDouble(nMatVal
.fVal
);
707 // also handles DoubleError
716 void ScInterpreter::ScEMat()
718 if ( !MustHaveParamCount( GetByte(), 1 ) )
721 SCSIZE nDim
= static_cast<SCSIZE
>(GetUInt32());
722 if (nGlobalError
!= FormulaError::NONE
|| nDim
== 0)
723 PushIllegalArgument();
724 else if (!ScMatrix::IsSizeAllocatable( nDim
, nDim
))
725 PushError( FormulaError::MatrixSize
);
728 ScMatrixRef pRMat
= GetNewMat(nDim
, nDim
, /*bEmpty*/true);
735 PushIllegalArgument();
739 void ScInterpreter::MEMat(const ScMatrixRef
& mM
, SCSIZE n
)
741 mM
->FillDouble(0.0, 0, 0, n
-1, n
-1);
742 for (SCSIZE i
= 0; i
< n
; i
++)
743 mM
->PutDouble(1.0, i
, i
);
746 /* Matrix LUP decomposition according to the pseudocode of "Introduction to
747 * Algorithms" by Cormen, Leiserson, Rivest, Stein.
749 * Added scaling for numeric stability.
751 * Given an n x n nonsingular matrix A, find a permutation matrix P, a unit
752 * lower-triangular matrix L, and an upper-triangular matrix U such that PA=LU.
753 * Compute L and U "in place" in the matrix A, the original content is
754 * destroyed. Note that the diagonal elements of the U triangular matrix
755 * replace the diagonal elements of the L-unit matrix (that are each ==1). The
756 * permutation matrix P is an array, where P[i]=j means that the i-th row of P
757 * contains a 1 in column j. Additionally keep track of the number of
758 * permutations (row exchanges).
760 * Returns 0 if a singular matrix is encountered, else +1 if an even number of
761 * permutations occurred, or -1 if odd, which is the sign of the determinant.
762 * This may be used to calculate the determinant by multiplying the sign with
763 * the product of the diagonal elements of the LU matrix.
765 static int lcl_LUP_decompose( ScMatrix
* mA
, const SCSIZE n
,
766 ::std::vector
< SCSIZE
> & P
)
769 // Find scale of each row.
770 ::std::vector
< double> aScale(n
);
771 for (SCSIZE i
=0; i
< n
; ++i
)
774 for (SCSIZE j
=0; j
< n
; ++j
)
776 double fTmp
= fabs( mA
->GetDouble( j
, i
));
781 return 0; // singular matrix
782 aScale
[i
] = 1.0 / fMax
;
784 // Represent identity permutation, P[i]=i
785 for (SCSIZE i
=0; i
< n
; ++i
)
787 // "Recursion" on the diagonal.
789 for (SCSIZE k
=0; k
< l
; ++k
)
791 // Implicit pivoting. With the scale found for a row, compare values of
792 // a column and pick largest.
794 double fScale
= aScale
[k
];
796 for (SCSIZE i
= k
; i
< n
; ++i
)
798 double fTmp
= fScale
* fabs( mA
->GetDouble( k
, i
));
806 return 0; // singular matrix
807 // Swap rows. The pivot element will be at mA[k,kp] (row,col notation)
816 double fTmp
= aScale
[k
];
817 aScale
[k
] = aScale
[kp
];
820 for (SCSIZE i
=0; i
< n
; ++i
)
822 double fMatTmp
= mA
->GetDouble( i
, k
);
823 mA
->PutDouble( mA
->GetDouble( i
, kp
), i
, k
);
824 mA
->PutDouble( fMatTmp
, i
, kp
);
827 // Compute Schur complement.
828 for (SCSIZE i
= k
+1; i
< n
; ++i
)
830 double fNum
= mA
->GetDouble( k
, i
);
831 double fDen
= mA
->GetDouble( k
, k
);
832 mA
->PutDouble( fNum
/fDen
, k
, i
);
833 for (SCSIZE j
= k
+1; j
< n
; ++j
)
834 mA
->PutDouble( ( mA
->GetDouble( j
, i
) * fDen
-
835 fNum
* mA
->GetDouble( j
, k
) ) / fDen
, j
, i
);
838 #ifdef DEBUG_SC_LUP_DECOMPOSITION
839 fprintf( stderr
, "\n%s\n", "lcl_LUP_decompose(): LU");
840 for (SCSIZE i
=0; i
< n
; ++i
)
842 for (SCSIZE j
=0; j
< n
; ++j
)
843 fprintf( stderr
, "%8.2g ", mA
->GetDouble( j
, i
));
844 fprintf( stderr
, "\n%s\n", "");
846 fprintf( stderr
, "\n%s\n", "lcl_LUP_decompose(): P");
847 for (SCSIZE j
=0; j
< n
; ++j
)
848 fprintf( stderr
, "%5u ", (unsigned)P
[j
]);
849 fprintf( stderr
, "\n%s\n", "");
852 bool bSingular
=false;
853 for (SCSIZE i
=0; i
<n
&& !bSingular
; i
++)
854 bSingular
= (mA
->GetDouble(i
,i
)) == 0.0;
861 /* Solve a LUP decomposed equation Ax=b. LU is a combined matrix of L and U
862 * triangulars and P the permutation vector as obtained from
863 * lcl_LUP_decompose(). B is the right-hand side input vector, X is used to
864 * return the solution vector.
866 static void lcl_LUP_solve( const ScMatrix
* mLU
, const SCSIZE n
,
867 const ::std::vector
< SCSIZE
> & P
, const ::std::vector
< double> & B
,
868 ::std::vector
< double> & X
)
870 SCSIZE nFirst
= SCSIZE_MAX
;
871 // Ax=b => PAx=Pb, with decomposition LUx=Pb.
872 // Define y=Ux and solve for y in Ly=Pb using forward substitution.
873 for (SCSIZE i
=0; i
< n
; ++i
)
875 KahanSum fSum
= B
[P
[i
]];
876 // Matrix inversion comes with a lot of zeros in the B vectors, we
877 // don't have to do all the computing with results multiplied by zero.
878 // Until then, simply lookout for the position of the first nonzero
880 if (nFirst
!= SCSIZE_MAX
)
882 for (SCSIZE j
= nFirst
; j
< i
; ++j
)
883 fSum
-= mLU
->GetDouble( j
, i
) * X
[j
]; // X[j] === y[j]
887 X
[i
] = fSum
.get(); // X[i] === y[i]
889 // Solve for x in Ux=y using back substitution.
890 for (SCSIZE i
= n
; i
--; )
892 KahanSum fSum
= X
[i
]; // X[i] === y[i]
893 for (SCSIZE j
= i
+1; j
< n
; ++j
)
894 fSum
-= mLU
->GetDouble( j
, i
) * X
[j
]; // X[j] === x[j]
895 X
[i
] = fSum
.get() / mLU
->GetDouble( i
, i
); // X[i] === x[i]
897 #ifdef DEBUG_SC_LUP_DECOMPOSITION
898 fprintf( stderr
, "\n%s\n", "lcl_LUP_solve():");
899 for (SCSIZE i
=0; i
< n
; ++i
)
900 fprintf( stderr
, "%8.2g ", X
[i
]);
901 fprintf( stderr
, "%s\n", "");
905 void ScInterpreter::ScMatDet()
907 if ( !MustHaveParamCount( GetByte(), 1 ) )
910 ScMatrixRef pMat
= GetMatrix();
913 PushIllegalParameter();
916 if ( !pMat
->IsNumeric() )
922 pMat
->GetDimensions(nC
, nR
);
923 if ( nC
!= nR
|| nC
== 0 )
924 PushIllegalArgument();
925 else if (!ScMatrix::IsSizeAllocatable( nC
, nR
))
926 PushError( FormulaError::MatrixSize
);
929 // LUP decomposition is done inplace, use copy.
930 ScMatrixRef xLU
= pMat
->Clone();
932 PushError( FormulaError::CodeOverflow
);
935 ::std::vector
< SCSIZE
> P(nR
);
936 int nDetSign
= lcl_LUP_decompose( xLU
.get(), nR
, P
);
938 PushInt(0); // singular matrix
941 // In an LU matrix the determinant is simply the product of
942 // all diagonal elements.
943 double fDet
= nDetSign
;
944 for (SCSIZE i
=0; i
< nR
; ++i
)
945 fDet
*= xLU
->GetDouble( i
, i
);
952 void ScInterpreter::ScMatInv()
954 if ( !MustHaveParamCount( GetByte(), 1 ) )
957 ScMatrixRef pMat
= GetMatrix();
960 PushIllegalParameter();
963 if ( !pMat
->IsNumeric() )
969 pMat
->GetDimensions(nC
, nR
);
971 if (ScCalcConfig::isOpenCLEnabled())
973 sc::FormulaGroupInterpreter
*pInterpreter
= sc::FormulaGroupInterpreter::getStatic();
974 if (pInterpreter
!= nullptr)
976 ScMatrixRef xResMat
= pInterpreter
->inverseMatrix(*pMat
);
985 if ( nC
!= nR
|| nC
== 0 )
986 PushIllegalArgument();
987 else if (!ScMatrix::IsSizeAllocatable( nC
, nR
))
988 PushError( FormulaError::MatrixSize
);
991 // LUP decomposition is done inplace, use copy.
992 ScMatrixRef xLU
= pMat
->Clone();
993 // The result matrix.
994 ScMatrixRef xY
= GetNewMat( nR
, nR
, /*bEmpty*/true );
996 PushError( FormulaError::CodeOverflow
);
999 ::std::vector
< SCSIZE
> P(nR
);
1000 int nDetSign
= lcl_LUP_decompose( xLU
.get(), nR
, P
);
1002 PushIllegalArgument();
1005 // Solve equation for each column.
1006 ::std::vector
< double> B(nR
);
1007 ::std::vector
< double> X(nR
);
1008 for (SCSIZE j
=0; j
< nR
; ++j
)
1010 for (SCSIZE i
=0; i
< nR
; ++i
)
1013 lcl_LUP_solve( xLU
.get(), nR
, P
, B
, X
);
1014 for (SCSIZE i
=0; i
< nR
; ++i
)
1015 xY
->PutDouble( X
[i
], j
, i
);
1017 #ifdef DEBUG_SC_LUP_DECOMPOSITION
1018 /* Possible checks for ill-condition:
1019 * 1. Scale matrix, invert scaled matrix. If there are
1020 * elements of the inverted matrix that are several
1021 * orders of magnitude greater than 1 =>
1023 * Just how much is "several orders"?
1024 * 2. Invert the inverted matrix and assess whether the
1025 * result is sufficiently close to the original matrix.
1026 * If not => ill-conditioned.
1027 * Just what is sufficient?
1028 * 3. Multiplying the inverse by the original matrix should
1029 * produce a result sufficiently close to the identity
1031 * Just what is sufficient?
1033 * The following is #3.
1035 const double fInvEpsilon
= 1.0E-7;
1036 ScMatrixRef xR
= GetNewMat( nR
, nR
);
1039 ScMatrix
* pR
= xR
.get();
1040 lcl_MFastMult( pMat
, xY
.get(), pR
, nR
, nR
, nR
);
1041 fprintf( stderr
, "\n%s\n", "ScMatInv(): mult-identity");
1042 for (SCSIZE i
=0; i
< nR
; ++i
)
1044 for (SCSIZE j
=0; j
< nR
; ++j
)
1046 double fTmp
= pR
->GetDouble( j
, i
);
1047 fprintf( stderr
, "%8.2g ", fTmp
);
1048 if (fabs( fTmp
- (i
== j
)) > fInvEpsilon
)
1049 SetError( FormulaError::IllegalArgument
);
1051 fprintf( stderr
, "\n%s\n", "");
1055 if (nGlobalError
!= FormulaError::NONE
)
1056 PushError( nGlobalError
);
1064 void ScInterpreter::ScMatMult()
1066 if ( !MustHaveParamCount( GetByte(), 2 ) )
1069 ScMatrixRef pMat2
= GetMatrix();
1070 ScMatrixRef pMat1
= GetMatrix();
1074 if ( pMat1
->IsNumeric() && pMat2
->IsNumeric() )
1078 pMat1
->GetDimensions(nC1
, nR1
);
1079 pMat2
->GetDimensions(nC2
, nR2
);
1081 PushIllegalArgument();
1084 pRMat
= GetNewMat(nC2
, nR1
, /*bEmpty*/true);
1088 for (SCSIZE i
= 0; i
< nR1
; i
++)
1090 for (SCSIZE j
= 0; j
< nC2
; j
++)
1093 for (SCSIZE k
= 0; k
< nC1
; k
++)
1095 fSum
+= pMat1
->GetDouble(k
,i
)*pMat2
->GetDouble(j
,k
);
1097 pRMat
->PutDouble(fSum
.get(), j
, i
);
1103 PushIllegalArgument();
1110 PushIllegalParameter();
1113 void ScInterpreter::ScMatSequence()
1115 sal_uInt8 nParamCount
= GetByte();
1116 if (!MustHaveParamCount(nParamCount
, 1, 4))
1119 // 4th argument is the step number (optional)
1120 double nSteps
= 1.0;
1121 if (nParamCount
== 4)
1122 nSteps
= GetDoubleWithDefault(nSteps
);
1124 // 3d argument is the start number (optional)
1125 double nStart
= 1.0;
1126 if (nParamCount
>= 3)
1127 nStart
= GetDoubleWithDefault(nStart
);
1129 // 2nd argument is the col number (optional)
1130 sal_Int32 nColumns
= 1;
1131 if (nParamCount
>= 2)
1133 nColumns
= GetInt32WithDefault(nColumns
);
1136 PushIllegalArgument();
1141 // 1st argument is the row number (required)
1142 sal_Int32 nRows
= GetInt32WithDefault(1);
1145 PushIllegalArgument();
1149 if (nGlobalError
!= FormulaError::NONE
)
1151 PushError(nGlobalError
);
1155 size_t nMatrixSize
= nColumns
* nRows
;
1156 ScMatrixRef pResMat
= GetNewMat(nColumns
, nRows
, /*bEmpty*/true);
1157 for (size_t iPos
= 0; iPos
< nMatrixSize
; iPos
++)
1159 pResMat
->PutDoubleTrans(nStart
, iPos
);
1160 nStart
= nStart
+ nSteps
;
1165 PushMatrix(pResMat
);
1169 PushIllegalParameter();
1174 void ScInterpreter::ScMatTrans()
1176 if ( !MustHaveParamCount( GetByte(), 1 ) )
1179 ScMatrixRef pMat
= GetMatrix();
1184 pMat
->GetDimensions(nC
, nR
);
1185 pRMat
= GetNewMat(nR
, nC
, /*bEmpty*/true);
1188 pMat
->MatTrans(*pRMat
);
1192 PushIllegalArgument();
1195 PushIllegalParameter();
1198 /** Minimum extent of one result matrix dimension.
1199 For a row or column vector to be replicated the larger matrix dimension is
1200 returned, else the smaller dimension.
1202 static SCSIZE
lcl_GetMinExtent( SCSIZE n1
, SCSIZE n2
)
1214 static ScMatrixRef
lcl_MatrixCalculation(
1215 const ScMatrix
& rMat1
, const ScMatrix
& rMat2
, ScInterpreter
* pInterpreter
, const ScMatrix::CalculateOpFunction
& Op
)
1217 SCSIZE nC1
, nC2
, nMinC
;
1218 SCSIZE nR1
, nR2
, nMinR
;
1219 rMat1
.GetDimensions(nC1
, nR1
);
1220 rMat2
.GetDimensions(nC2
, nR2
);
1221 nMinC
= lcl_GetMinExtent( nC1
, nC2
);
1222 nMinR
= lcl_GetMinExtent( nR1
, nR2
);
1223 ScMatrixRef xResMat
= pInterpreter
->GetNewMat(nMinC
, nMinR
, /*bEmpty*/true);
1225 xResMat
->ExecuteBinaryOp(nMinC
, nMinR
, rMat1
, rMat2
, pInterpreter
, Op
);
1229 ScMatrixRef
ScInterpreter::MatConcat(const ScMatrixRef
& pMat1
, const ScMatrixRef
& pMat2
)
1231 SCSIZE nC1
, nC2
, nMinC
;
1232 SCSIZE nR1
, nR2
, nMinR
;
1233 pMat1
->GetDimensions(nC1
, nR1
);
1234 pMat2
->GetDimensions(nC2
, nR2
);
1235 nMinC
= lcl_GetMinExtent( nC1
, nC2
);
1236 nMinR
= lcl_GetMinExtent( nR1
, nR2
);
1237 ScMatrixRef xResMat
= GetNewMat(nMinC
, nMinR
, /*bEmpty*/true);
1240 xResMat
->MatConcat(nMinC
, nMinR
, pMat1
, pMat2
, mrContext
, mrDoc
.GetSharedStringPool());
1245 // for DATE, TIME, DATETIME, DURATION
1246 static void lcl_GetDiffDateTimeFmtType( SvNumFormatType
& nFuncFmt
, SvNumFormatType nFmt1
, SvNumFormatType nFmt2
)
1248 if ( nFmt1
== SvNumFormatType::UNDEFINED
&& nFmt2
== SvNumFormatType::UNDEFINED
)
1251 if ( nFmt1
== nFmt2
)
1253 if ( nFmt1
== SvNumFormatType::TIME
|| nFmt1
== SvNumFormatType::DATETIME
1254 || nFmt1
== SvNumFormatType::DURATION
)
1255 nFuncFmt
= SvNumFormatType::DURATION
; // times result in time duration
1256 // else: nothing special, number (date - date := days)
1258 else if ( nFmt1
== SvNumFormatType::UNDEFINED
)
1259 nFuncFmt
= nFmt2
; // e.g. date + days := date
1260 else if ( nFmt2
== SvNumFormatType::UNDEFINED
)
1264 if ( nFmt1
== SvNumFormatType::DATE
|| nFmt2
== SvNumFormatType::DATE
||
1265 nFmt1
== SvNumFormatType::DATETIME
|| nFmt2
== SvNumFormatType::DATETIME
)
1267 if ( nFmt1
== SvNumFormatType::TIME
|| nFmt2
== SvNumFormatType::TIME
)
1268 nFuncFmt
= SvNumFormatType::DATETIME
; // date + time
1273 void ScInterpreter::ScAdd()
1275 CalculateAddSub(false);
1278 void ScInterpreter::CalculateAddSub(bool _bSub
)
1280 ScMatrixRef pMat1
= nullptr;
1281 ScMatrixRef pMat2
= nullptr;
1282 double fVal1
= 0.0, fVal2
= 0.0;
1283 SvNumFormatType nFmt1
, nFmt2
;
1284 nFmt1
= nFmt2
= SvNumFormatType::UNDEFINED
;
1285 bool bDuration
= false;
1286 SvNumFormatType nFmtCurrencyType
= nCurFmtType
;
1287 sal_uLong nFmtCurrencyIndex
= nCurFmtIndex
;
1288 SvNumFormatType nFmtPercentType
= nCurFmtType
;
1289 if ( GetStackType() == svMatrix
)
1290 pMat2
= GetMatrix();
1293 fVal2
= GetDouble();
1294 switch ( nCurFmtType
)
1296 case SvNumFormatType::DATE
:
1297 case SvNumFormatType::TIME
:
1298 case SvNumFormatType::DATETIME
:
1299 case SvNumFormatType::DURATION
:
1300 nFmt2
= nCurFmtType
;
1303 case SvNumFormatType::CURRENCY
:
1304 nFmtCurrencyType
= nCurFmtType
;
1305 nFmtCurrencyIndex
= nCurFmtIndex
;
1307 case SvNumFormatType::PERCENT
:
1308 nFmtPercentType
= SvNumFormatType::PERCENT
;
1313 if ( GetStackType() == svMatrix
)
1314 pMat1
= GetMatrix();
1317 fVal1
= GetDouble();
1318 switch ( nCurFmtType
)
1320 case SvNumFormatType::DATE
:
1321 case SvNumFormatType::TIME
:
1322 case SvNumFormatType::DATETIME
:
1323 case SvNumFormatType::DURATION
:
1324 nFmt1
= nCurFmtType
;
1327 case SvNumFormatType::CURRENCY
:
1328 nFmtCurrencyType
= nCurFmtType
;
1329 nFmtCurrencyIndex
= nCurFmtIndex
;
1331 case SvNumFormatType::PERCENT
:
1332 nFmtPercentType
= SvNumFormatType::PERCENT
;
1339 ScMatrixRef pResMat
;
1342 pResMat
= lcl_MatrixCalculation( *pMat1
, *pMat2
, this, MatrixSub
);
1346 pResMat
= lcl_MatrixCalculation( *pMat1
, *pMat2
, this, MatrixAdd
);
1352 PushMatrix(pResMat
);
1354 else if (pMat1
|| pMat2
)
1358 ScMatrixRef pMat
= std::move(pMat1
);
1362 pMat
= std::move(pMat2
);
1363 bFlag
= true; // double - Matrix
1368 bFlag
= false; // Matrix - double
1371 pMat
->GetDimensions(nC
, nR
);
1372 ScMatrixRef pResMat
= GetNewMat(nC
, nR
, true);
1377 pMat
->SubOp( bFlag
, fVal
, *pResMat
);
1381 pMat
->AddOp( fVal
, *pResMat
);
1383 PushMatrix(pResMat
);
1386 PushIllegalArgument();
1390 // Determine nFuncFmtType type before PushDouble().
1391 if ( nFmtCurrencyType
== SvNumFormatType::CURRENCY
)
1393 nFuncFmtType
= nFmtCurrencyType
;
1394 nFuncFmtIndex
= nFmtCurrencyIndex
;
1398 lcl_GetDiffDateTimeFmtType( nFuncFmtType
, nFmt1
, nFmt2
);
1399 if (nFmtPercentType
== SvNumFormatType::PERCENT
&& nFuncFmtType
== SvNumFormatType::NUMBER
)
1400 nFuncFmtType
= SvNumFormatType::PERCENT
;
1402 if ((nFuncFmtType
== SvNumFormatType::DURATION
|| bDuration
)
1403 && ((_bSub
&& std::fabs(fVal1
- fVal2
) <= SAL_MAX_INT32
)
1404 || (!_bSub
&& std::fabs(fVal1
+ fVal2
) <= SAL_MAX_INT32
)))
1406 // Limit to microseconds resolution on date inflicted or duration
1407 // values of 24 hours or more.
1408 const sal_uInt64 nEpsilon
= ((std::fabs(fVal1
) >= 1.0 || std::fabs(fVal2
) >= 1.0) ?
1409 ::tools::Duration::kAccuracyEpsilonNanosecondsMicroseconds
:
1410 ::tools::Duration::kAccuracyEpsilonNanoseconds
);
1412 PushDouble( ::tools::Duration( fVal1
- fVal2
, nEpsilon
).GetInDays());
1414 PushDouble( ::tools::Duration( fVal1
+ fVal2
, nEpsilon
).GetInDays());
1419 PushDouble( ::rtl::math::approxSub( fVal1
, fVal2
) );
1421 PushDouble( ::rtl::math::approxAdd( fVal1
, fVal2
) );
1426 void ScInterpreter::ScAmpersand()
1428 ScMatrixRef pMat1
= nullptr;
1429 ScMatrixRef pMat2
= nullptr;
1430 OUString sStr1
, sStr2
;
1431 if ( GetStackType() == svMatrix
)
1432 pMat2
= GetMatrix();
1434 sStr2
= GetString().getString();
1435 if ( GetStackType() == svMatrix
)
1436 pMat1
= GetMatrix();
1438 sStr1
= GetString().getString();
1441 ScMatrixRef pResMat
= MatConcat(pMat1
, pMat2
);
1445 PushMatrix(pResMat
);
1447 else if (pMat1
|| pMat2
)
1451 ScMatrixRef pMat
= std::move(pMat1
);
1455 pMat
= std::move(pMat2
);
1456 bFlag
= true; // double - Matrix
1461 bFlag
= false; // Matrix - double
1464 pMat
->GetDimensions(nC
, nR
);
1465 ScMatrixRef pResMat
= GetNewMat(nC
, nR
, /*bEmpty*/true);
1468 if (nGlobalError
!= FormulaError::NONE
)
1470 for (SCSIZE i
= 0; i
< nC
; ++i
)
1471 for (SCSIZE j
= 0; j
< nR
; ++j
)
1472 pResMat
->PutError( nGlobalError
, i
, j
);
1476 for (SCSIZE i
= 0; i
< nC
; ++i
)
1477 for (SCSIZE j
= 0; j
< nR
; ++j
)
1479 FormulaError nErr
= pMat
->GetErrorIfNotString( i
, j
);
1480 if (nErr
!= FormulaError::NONE
)
1481 pResMat
->PutError( nErr
, i
, j
);
1484 OUString aTmp
= sStr
+ pMat
->GetString(mrContext
, i
, j
).getString();
1485 pResMat
->PutString(mrStrPool
.intern(aTmp
), i
, j
);
1491 for (SCSIZE i
= 0; i
< nC
; ++i
)
1492 for (SCSIZE j
= 0; j
< nR
; ++j
)
1494 FormulaError nErr
= pMat
->GetErrorIfNotString( i
, j
);
1495 if (nErr
!= FormulaError::NONE
)
1496 pResMat
->PutError( nErr
, i
, j
);
1499 OUString aTmp
= pMat
->GetString(mrContext
, i
, j
).getString() + sStr
;
1500 pResMat
->PutString(mrStrPool
.intern(aTmp
), i
, j
);
1504 PushMatrix(pResMat
);
1507 PushIllegalArgument();
1511 if ( CheckStringResultLen( sStr1
, sStr2
.getLength() ) )
1517 void ScInterpreter::ScSub()
1519 CalculateAddSub(true);
1522 void ScInterpreter::ScMul()
1524 ScMatrixRef pMat1
= nullptr;
1525 ScMatrixRef pMat2
= nullptr;
1526 double fVal1
= 0.0, fVal2
= 0.0;
1527 SvNumFormatType nFmtCurrencyType
= nCurFmtType
;
1528 sal_uLong nFmtCurrencyIndex
= nCurFmtIndex
;
1529 if ( GetStackType() == svMatrix
)
1530 pMat2
= GetMatrix();
1533 fVal2
= GetDouble();
1534 switch ( nCurFmtType
)
1536 case SvNumFormatType::CURRENCY
:
1537 nFmtCurrencyType
= nCurFmtType
;
1538 nFmtCurrencyIndex
= nCurFmtIndex
;
1543 if ( GetStackType() == svMatrix
)
1544 pMat1
= GetMatrix();
1547 fVal1
= GetDouble();
1548 switch ( nCurFmtType
)
1550 case SvNumFormatType::CURRENCY
:
1551 nFmtCurrencyType
= nCurFmtType
;
1552 nFmtCurrencyIndex
= nCurFmtIndex
;
1559 ScMatrixRef pResMat
= lcl_MatrixCalculation( *pMat1
, *pMat2
, this, MatrixMul
);
1563 PushMatrix(pResMat
);
1565 else if (pMat1
|| pMat2
)
1568 ScMatrixRef pMat
= std::move(pMat1
);
1572 pMat
= std::move(pMat2
);
1577 pMat
->GetDimensions(nC
, nR
);
1578 ScMatrixRef pResMat
= GetNewMat(nC
, nR
, /*bEmpty*/true);
1581 pMat
->MulOp( fVal
, *pResMat
);
1582 PushMatrix(pResMat
);
1585 PushIllegalArgument();
1589 // Determine nFuncFmtType type before PushDouble().
1590 if ( nFmtCurrencyType
== SvNumFormatType::CURRENCY
)
1592 nFuncFmtType
= nFmtCurrencyType
;
1593 nFuncFmtIndex
= nFmtCurrencyIndex
;
1595 PushDouble(fVal1
* fVal2
);
1599 void ScInterpreter::ScDiv()
1601 ScMatrixRef pMat1
= nullptr;
1602 ScMatrixRef pMat2
= nullptr;
1603 double fVal1
= 0.0, fVal2
= 0.0;
1604 SvNumFormatType nFmtCurrencyType
= nCurFmtType
;
1605 sal_uLong nFmtCurrencyIndex
= nCurFmtIndex
;
1606 SvNumFormatType nFmtCurrencyType2
= SvNumFormatType::UNDEFINED
;
1607 if ( GetStackType() == svMatrix
)
1608 pMat2
= GetMatrix();
1611 fVal2
= GetDouble();
1612 // do not take over currency, 123kg/456USD is not USD
1613 nFmtCurrencyType2
= nCurFmtType
;
1615 if ( GetStackType() == svMatrix
)
1616 pMat1
= GetMatrix();
1619 fVal1
= GetDouble();
1620 switch ( nCurFmtType
)
1622 case SvNumFormatType::CURRENCY
:
1623 nFmtCurrencyType
= nCurFmtType
;
1624 nFmtCurrencyIndex
= nCurFmtIndex
;
1631 ScMatrixRef pResMat
= lcl_MatrixCalculation( *pMat1
, *pMat2
, this, MatrixDiv
);
1635 PushMatrix(pResMat
);
1637 else if (pMat1
|| pMat2
)
1641 ScMatrixRef pMat
= std::move(pMat1
);
1645 pMat
= std::move(pMat2
);
1646 bFlag
= true; // double - Matrix
1651 bFlag
= false; // Matrix - double
1654 pMat
->GetDimensions(nC
, nR
);
1655 ScMatrixRef pResMat
= GetNewMat(nC
, nR
, /*bEmpty*/true);
1658 pMat
->DivOp( bFlag
, fVal
, *pResMat
);
1659 PushMatrix(pResMat
);
1662 PushIllegalArgument();
1666 // Determine nFuncFmtType type before PushDouble().
1667 if ( nFmtCurrencyType
== SvNumFormatType::CURRENCY
&&
1668 nFmtCurrencyType2
!= SvNumFormatType::CURRENCY
)
1669 { // even USD/USD is not USD
1670 nFuncFmtType
= nFmtCurrencyType
;
1671 nFuncFmtIndex
= nFmtCurrencyIndex
;
1673 PushDouble( div( fVal1
, fVal2
) );
1677 void ScInterpreter::ScPower()
1679 if ( MustHaveParamCount( GetByte(), 2 ) )
1683 void ScInterpreter::ScPow()
1685 ScMatrixRef pMat1
= nullptr;
1686 ScMatrixRef pMat2
= nullptr;
1687 double fVal1
= 0.0, fVal2
= 0.0;
1688 if ( GetStackType() == svMatrix
)
1689 pMat2
= GetMatrix();
1691 fVal2
= GetDouble();
1692 if ( GetStackType() == svMatrix
)
1693 pMat1
= GetMatrix();
1695 fVal1
= GetDouble();
1698 ScMatrixRef pResMat
= lcl_MatrixCalculation( *pMat1
, *pMat2
, this, MatrixPow
);
1702 PushMatrix(pResMat
);
1704 else if (pMat1
|| pMat2
)
1708 ScMatrixRef pMat
= std::move(pMat1
);
1712 pMat
= std::move(pMat2
);
1713 bFlag
= true; // double - Matrix
1718 bFlag
= false; // Matrix - double
1721 pMat
->GetDimensions(nC
, nR
);
1722 ScMatrixRef pResMat
= GetNewMat(nC
, nR
, /*bEmpty*/true);
1725 pMat
->PowOp( bFlag
, fVal
, *pResMat
);
1726 PushMatrix(pResMat
);
1729 PushIllegalArgument();
1733 PushDouble( sc::power( fVal1
, fVal2
));
1737 void ScInterpreter::ScSumProduct()
1739 short nParamCount
= GetByte();
1740 if ( !MustHaveParamCountMin( nParamCount
, 1) )
1743 // XXX NOTE: Excel returns #VALUE! for reference list and 0 (why?) for
1744 // array of references. We calculate the proper individual arrays if sizes
1747 size_t nInRefList
= 0;
1748 ScMatrixRef pMatLast
;
1751 pMatLast
= GetMatrix( --nParamCount
, nInRefList
);
1754 PushIllegalParameter();
1758 SCSIZE nC
, nCLast
, nR
, nRLast
;
1759 pMatLast
->GetDimensions(nCLast
, nRLast
);
1760 std::vector
<double> aResArray
;
1761 pMatLast
->GetDoubleArray(aResArray
);
1763 while (nParamCount
--)
1765 pMat
= GetMatrix( nParamCount
, nInRefList
);
1768 PushIllegalParameter();
1771 pMat
->GetDimensions(nC
, nR
);
1772 if (nC
!= nCLast
|| nR
!= nRLast
)
1778 pMat
->MergeDoubleArrayMultiply(aResArray
);
1781 KahanSum fSum
= 0.0;
1782 for( double fPosArray
: aResArray
)
1784 FormulaError nErr
= GetDoubleErrorValue(fPosArray
);
1785 if (nErr
== FormulaError::NONE
)
1787 else if (nErr
!= FormulaError::ElementNaN
)
1789 // Propagate the first error encountered, ignore "this is not a number" elements.
1795 PushDouble(fSum
.get());
1798 void ScInterpreter::ScSumX2MY2()
1800 CalculateSumX2MY2SumX2DY2(false);
1802 void ScInterpreter::CalculateSumX2MY2SumX2DY2(bool _bSumX2DY2
)
1804 if ( !MustHaveParamCount( GetByte(), 2 ) )
1807 ScMatrixRef pMat1
= nullptr;
1808 ScMatrixRef pMat2
= nullptr;
1810 pMat2
= GetMatrix();
1811 pMat1
= GetMatrix();
1812 if (!pMat2
|| !pMat1
)
1814 PushIllegalParameter();
1819 pMat2
->GetDimensions(nC2
, nR2
);
1820 pMat1
->GetDimensions(nC1
, nR1
);
1821 if (nC1
!= nC2
|| nR1
!= nR2
)
1827 KahanSum fSum
= 0.0;
1828 for (i
= 0; i
< nC1
; i
++)
1829 for (j
= 0; j
< nR1
; j
++)
1830 if (!pMat1
->IsStringOrEmpty(i
,j
) && !pMat2
->IsStringOrEmpty(i
,j
))
1832 fVal
= pMat1
->GetDouble(i
,j
);
1833 fSum
+= fVal
* fVal
;
1834 fVal
= pMat2
->GetDouble(i
,j
);
1836 fSum
+= fVal
* fVal
;
1838 fSum
-= fVal
* fVal
;
1840 PushDouble(fSum
.get());
1843 void ScInterpreter::ScSumX2DY2()
1845 CalculateSumX2MY2SumX2DY2(true);
1848 void ScInterpreter::ScSumXMY2()
1850 if ( !MustHaveParamCount( GetByte(), 2 ) )
1853 ScMatrixRef pMat2
= GetMatrix();
1854 ScMatrixRef pMat1
= GetMatrix();
1855 if (!pMat2
|| !pMat1
)
1857 PushIllegalParameter();
1862 pMat2
->GetDimensions(nC2
, nR2
);
1863 pMat1
->GetDimensions(nC1
, nR1
);
1864 if (nC1
!= nC2
|| nR1
!= nR2
)
1868 } // if (nC1 != nC2 || nR1 != nR2)
1869 ScMatrixRef pResMat
= lcl_MatrixCalculation( *pMat1
, *pMat2
, this, MatrixSub
);
1876 PushDouble(pResMat
->SumSquare(false).maAccumulator
.get());
1880 void ScInterpreter::ScFrequency()
1882 if ( !MustHaveParamCount( GetByte(), 2 ) )
1885 vector
<double> aBinArray
;
1886 vector
<tools::Long
> aBinIndexOrder
;
1888 GetSortArray( 1, aBinArray
, &aBinIndexOrder
, false, false );
1889 SCSIZE nBinSize
= aBinArray
.size();
1890 if (nGlobalError
!= FormulaError::NONE
)
1896 vector
<double> aDataArray
;
1897 GetSortArray( 1, aDataArray
, nullptr, false, false );
1898 SCSIZE nDataSize
= aDataArray
.size();
1900 if (aDataArray
.empty() || nGlobalError
!= FormulaError::NONE
)
1905 ScMatrixRef pResMat
= GetNewMat(1, nBinSize
+1, /*bEmpty*/true);
1908 PushIllegalArgument();
1912 if (nBinSize
!= aBinIndexOrder
.size())
1914 PushIllegalArgument();
1920 for (j
= 0; j
< nBinSize
; ++j
)
1923 while (i
< nDataSize
&& aDataArray
[i
] <= aBinArray
[j
])
1928 pResMat
->PutDouble(static_cast<double>(nCount
), aBinIndexOrder
[j
]);
1930 pResMat
->PutDouble(static_cast<double>(nDataSize
-i
), j
);
1931 PushMatrix(pResMat
);
1936 // Helper methods for LINEST/LOGEST and TREND/GROWTH
1937 // All matrices must already exist and have the needed size, no control tests
1938 // done. Those methods, which names start with lcl_T, are adapted to case 3,
1939 // where Y (=observed values) is given as row.
1940 // Remember, ScMatrix matrices are zero based, index access (column,row).
1942 // <A;B> over all elements; uses the matrices as vectors of length M
1943 double lcl_GetSumProduct(const ScMatrixRef
& pMatA
, const ScMatrixRef
& pMatB
, SCSIZE nM
)
1945 KahanSum fSum
= 0.0;
1946 for (SCSIZE i
=0; i
<nM
; i
++)
1947 fSum
+= pMatA
->GetDouble(i
) * pMatB
->GetDouble(i
);
1951 // Special version for use within QR decomposition.
1952 // Euclidean norm of column index C starting in row index R;
1953 // matrix A has count N rows.
1954 double lcl_GetColumnEuclideanNorm(const ScMatrixRef
& pMatA
, SCSIZE nC
, SCSIZE nR
, SCSIZE nN
)
1956 KahanSum fNorm
= 0.0;
1957 for (SCSIZE row
=nR
; row
<nN
; row
++)
1958 fNorm
+= (pMatA
->GetDouble(nC
,row
)) * (pMatA
->GetDouble(nC
,row
));
1959 return sqrt(fNorm
.get());
1962 // Euclidean norm of row index R starting in column index C;
1963 // matrix A has count N columns.
1964 double lcl_TGetColumnEuclideanNorm(const ScMatrixRef
& pMatA
, SCSIZE nR
, SCSIZE nC
, SCSIZE nN
)
1966 KahanSum fNorm
= 0.0;
1967 for (SCSIZE col
=nC
; col
<nN
; col
++)
1968 fNorm
+= (pMatA
->GetDouble(col
,nR
)) * (pMatA
->GetDouble(col
,nR
));
1969 return sqrt(fNorm
.get());
1972 // Special version for use within QR decomposition.
1973 // Maximum norm of column index C starting in row index R;
1974 // matrix A has count N rows.
1975 double lcl_GetColumnMaximumNorm(const ScMatrixRef
& pMatA
, SCSIZE nC
, SCSIZE nR
, SCSIZE nN
)
1978 for (SCSIZE row
=nR
; row
<nN
; row
++)
1980 double fVal
= fabs(pMatA
->GetDouble(nC
,row
));
1987 // Maximum norm of row index R starting in col index C;
1988 // matrix A has count N columns.
1989 double lcl_TGetColumnMaximumNorm(const ScMatrixRef
& pMatA
, SCSIZE nR
, SCSIZE nC
, SCSIZE nN
)
1992 for (SCSIZE col
=nC
; col
<nN
; col
++)
1994 double fVal
= fabs(pMatA
->GetDouble(col
,nR
));
2001 // Special version for use within QR decomposition.
2002 // <A(Ca);B(Cb)> starting in row index R;
2003 // Ca and Cb are indices of columns, matrices A and B have count N rows.
2004 double lcl_GetColumnSumProduct(const ScMatrixRef
& pMatA
, SCSIZE nCa
,
2005 const ScMatrixRef
& pMatB
, SCSIZE nCb
, SCSIZE nR
, SCSIZE nN
)
2007 KahanSum fResult
= 0.0;
2008 for (SCSIZE row
=nR
; row
<nN
; row
++)
2009 fResult
+= pMatA
->GetDouble(nCa
,row
) * pMatB
->GetDouble(nCb
,row
);
2010 return fResult
.get();
2013 // <A(Ra);B(Rb)> starting in column index C;
2014 // Ra and Rb are indices of rows, matrices A and B have count N columns.
2015 double lcl_TGetColumnSumProduct(const ScMatrixRef
& pMatA
, SCSIZE nRa
,
2016 const ScMatrixRef
& pMatB
, SCSIZE nRb
, SCSIZE nC
, SCSIZE nN
)
2018 KahanSum fResult
= 0.0;
2019 for (SCSIZE col
=nC
; col
<nN
; col
++)
2020 fResult
+= pMatA
->GetDouble(col
,nRa
) * pMatB
->GetDouble(col
,nRb
);
2021 return fResult
.get();
2024 // no mathematical signum, but used to switch between adding and subtracting
2025 double lcl_GetSign(double fValue
)
2027 return (fValue
>= 0.0 ? 1.0 : -1.0 );
2030 /* Calculates a QR decomposition with Householder reflection.
2031 * For each NxK matrix A exists a decomposition A=Q*R with an orthogonal
2032 * NxN matrix Q and a NxK matrix R.
2033 * Q=H1*H2*...*Hk with Householder matrices H. Such a householder matrix can
2034 * be build from a vector u by H=I-(2/u'u)*(u u'). This vectors u are returned
2035 * in the columns of matrix A, overwriting the old content.
2036 * The matrix R has a quadric upper part KxK with values in the upper right
2037 * triangle and zeros in all other elements. Here the diagonal elements of R
2038 * are stored in the vector R and the other upper right elements in the upper
2039 * right of the matrix A.
2040 * The function returns false, if calculation breaks. But because of round-off
2041 * errors singularity is often not detected.
2043 bool lcl_CalculateQRdecomposition(const ScMatrixRef
& pMatA
,
2044 ::std::vector
< double>& pVecR
, SCSIZE nK
, SCSIZE nN
)
2046 // ScMatrix matrices are zero based, index access (column,row)
2047 for (SCSIZE col
= 0; col
<nK
; col
++)
2049 // calculate vector u of the householder transformation
2050 const double fScale
= lcl_GetColumnMaximumNorm(pMatA
, col
, col
, nN
);
2056 for (SCSIZE row
= col
; row
<nN
; row
++)
2057 pMatA
->PutDouble( pMatA
->GetDouble(col
,row
)/fScale
, col
, row
);
2059 const double fEuclid
= lcl_GetColumnEuclideanNorm(pMatA
, col
, col
, nN
);
2060 const double fFactor
= 1.0/fEuclid
/(fEuclid
+ fabs(pMatA
->GetDouble(col
,col
)));
2061 const double fSignum
= lcl_GetSign(pMatA
->GetDouble(col
,col
));
2062 pMatA
->PutDouble( pMatA
->GetDouble(col
,col
) + fSignum
*fEuclid
, col
,col
);
2063 pVecR
[col
] = -fSignum
* fScale
* fEuclid
;
2065 // apply Householder transformation to A
2066 for (SCSIZE c
=col
+1; c
<nK
; c
++)
2068 const double fSum
=lcl_GetColumnSumProduct(pMatA
, col
, pMatA
, c
, col
, nN
);
2069 for (SCSIZE row
= col
; row
<nN
; row
++)
2070 pMatA
->PutDouble( pMatA
->GetDouble(c
,row
) - fSum
* fFactor
* pMatA
->GetDouble(col
,row
), c
, row
);
2076 // same with transposed matrix A, N is count of columns, K count of rows
2077 bool lcl_TCalculateQRdecomposition(const ScMatrixRef
& pMatA
,
2078 ::std::vector
< double>& pVecR
, SCSIZE nK
, SCSIZE nN
)
2081 // ScMatrix matrices are zero based, index access (column,row)
2082 for (SCSIZE row
= 0; row
<nK
; row
++)
2084 // calculate vector u of the householder transformation
2085 const double fScale
= lcl_TGetColumnMaximumNorm(pMatA
, row
, row
, nN
);
2091 for (SCSIZE col
= row
; col
<nN
; col
++)
2092 pMatA
->PutDouble( pMatA
->GetDouble(col
,row
)/fScale
, col
, row
);
2094 const double fEuclid
= lcl_TGetColumnEuclideanNorm(pMatA
, row
, row
, nN
);
2095 const double fFactor
= 1.0/fEuclid
/(fEuclid
+ fabs(pMatA
->GetDouble(row
,row
)));
2096 const double fSignum
= lcl_GetSign(pMatA
->GetDouble(row
,row
));
2097 pMatA
->PutDouble( pMatA
->GetDouble(row
,row
) + fSignum
*fEuclid
, row
,row
);
2098 pVecR
[row
] = -fSignum
* fScale
* fEuclid
;
2100 // apply Householder transformation to A
2101 for (SCSIZE r
=row
+1; r
<nK
; r
++)
2103 fSum
=lcl_TGetColumnSumProduct(pMatA
, row
, pMatA
, r
, row
, nN
);
2104 for (SCSIZE col
= row
; col
<nN
; col
++)
2106 pMatA
->GetDouble(col
,r
) - fSum
* fFactor
* pMatA
->GetDouble(col
,row
), col
, r
);
2112 /* Applies a Householder transformation to a column vector Y with is given as
2113 * Nx1 Matrix. The vector u, from which the Householder transformation is built,
2114 * is the column part in matrix A, with column index C, starting with row
2115 * index C. A is the result of the QR decomposition as obtained from
2116 * lcl_CalculateQRdecomposition.
2118 void lcl_ApplyHouseholderTransformation(const ScMatrixRef
& pMatA
, SCSIZE nC
,
2119 const ScMatrixRef
& pMatY
, SCSIZE nN
)
2121 // ScMatrix matrices are zero based, index access (column,row)
2122 double fDenominator
= lcl_GetColumnSumProduct(pMatA
, nC
, pMatA
, nC
, nC
, nN
);
2123 double fNumerator
= lcl_GetColumnSumProduct(pMatA
, nC
, pMatY
, 0, nC
, nN
);
2124 double fFactor
= 2.0 * (fNumerator
/fDenominator
);
2125 for (SCSIZE row
= nC
; row
< nN
; row
++)
2127 pMatY
->GetDouble(row
) - fFactor
* pMatA
->GetDouble(nC
,row
), row
);
2130 // Same with transposed matrices A and Y.
2131 void lcl_TApplyHouseholderTransformation(const ScMatrixRef
& pMatA
, SCSIZE nR
,
2132 const ScMatrixRef
& pMatY
, SCSIZE nN
)
2134 // ScMatrix matrices are zero based, index access (column,row)
2135 double fDenominator
= lcl_TGetColumnSumProduct(pMatA
, nR
, pMatA
, nR
, nR
, nN
);
2136 double fNumerator
= lcl_TGetColumnSumProduct(pMatA
, nR
, pMatY
, 0, nR
, nN
);
2137 double fFactor
= 2.0 * (fNumerator
/fDenominator
);
2138 for (SCSIZE col
= nR
; col
< nN
; col
++)
2140 pMatY
->GetDouble(col
) - fFactor
* pMatA
->GetDouble(col
,nR
), col
);
2143 /* Solve for X in R*X=S using back substitution. The solution X overwrites S.
2144 * Uses R from the result of the QR decomposition of a NxK matrix A.
2145 * S is a column vector given as matrix, with at least elements on index
2146 * 0 to K-1; elements on index>=K are ignored. Vector R must not have zero
2147 * elements, no check is done.
2149 void lcl_SolveWithUpperRightTriangle(const ScMatrixRef
& pMatA
,
2150 ::std::vector
< double>& pVecR
, const ScMatrixRef
& pMatS
,
2151 SCSIZE nK
, bool bIsTransposed
)
2153 // ScMatrix matrices are zero based, index access (column,row)
2155 // SCSIZE is never negative, therefore test with rowp1=row+1
2156 for (SCSIZE rowp1
= nK
; rowp1
>0; rowp1
--)
2159 KahanSum fSum
= pMatS
->GetDouble(row
);
2160 for (SCSIZE col
= rowp1
; col
<nK
; col
++)
2162 fSum
-= pMatA
->GetDouble(row
,col
) * pMatS
->GetDouble(col
);
2164 fSum
-= pMatA
->GetDouble(col
,row
) * pMatS
->GetDouble(col
);
2165 pMatS
->PutDouble( fSum
.get() / pVecR
[row
] , row
);
2169 /* Solve for X in R' * X= T using forward substitution. The solution X
2170 * overwrites T. Uses R from the result of the QR decomposition of a NxK
2171 * matrix A. T is a column vectors given as matrix, with at least elements on
2172 * index 0 to K-1; elements on index>=K are ignored. Vector R must not have
2173 * zero elements, no check is done.
2175 void lcl_SolveWithLowerLeftTriangle(const ScMatrixRef
& pMatA
,
2176 ::std::vector
< double>& pVecR
, const ScMatrixRef
& pMatT
,
2177 SCSIZE nK
, bool bIsTransposed
)
2179 // ScMatrix matrices are zero based, index access (column,row)
2180 for (SCSIZE row
= 0; row
< nK
; row
++)
2182 KahanSum fSum
= pMatT
-> GetDouble(row
);
2183 for (SCSIZE col
=0; col
< row
; col
++)
2186 fSum
-= pMatA
->GetDouble(col
,row
) * pMatT
->GetDouble(col
);
2188 fSum
-= pMatA
->GetDouble(row
,col
) * pMatT
->GetDouble(col
);
2190 pMatT
->PutDouble( fSum
.get() / pVecR
[row
] , row
);
2194 /* Calculates Z = R * B
2195 * R is given in matrix A and vector VecR as obtained from the QR
2196 * decomposition in lcl_CalculateQRdecomposition. B and Z are column vectors
2197 * given as matrix with at least index 0 to K-1; elements on index>=K are
2200 void lcl_ApplyUpperRightTriangle(const ScMatrixRef
& pMatA
,
2201 ::std::vector
< double>& pVecR
, const ScMatrixRef
& pMatB
,
2202 const ScMatrixRef
& pMatZ
, SCSIZE nK
, bool bIsTransposed
)
2204 // ScMatrix matrices are zero based, index access (column,row)
2205 for (SCSIZE row
= 0; row
< nK
; row
++)
2207 KahanSum fSum
= pVecR
[row
] * pMatB
->GetDouble(row
);
2208 for (SCSIZE col
= row
+1; col
< nK
; col
++)
2210 fSum
+= pMatA
->GetDouble(row
,col
) * pMatB
->GetDouble(col
);
2212 fSum
+= pMatA
->GetDouble(col
,row
) * pMatB
->GetDouble(col
);
2213 pMatZ
->PutDouble( fSum
.get(), row
);
2217 double lcl_GetMeanOverAll(const ScMatrixRef
& pMat
, SCSIZE nN
)
2219 KahanSum fSum
= 0.0;
2220 for (SCSIZE i
=0 ; i
<nN
; i
++)
2221 fSum
+= pMat
->GetDouble(i
);
2222 return fSum
.get()/static_cast<double>(nN
);
2225 // Calculates means of the columns of matrix X. X is a RxC matrix;
2226 // ResMat is a 1xC matrix (=row).
2227 void lcl_CalculateColumnMeans(const ScMatrixRef
& pX
, const ScMatrixRef
& pResMat
,
2228 SCSIZE nC
, SCSIZE nR
)
2230 for (SCSIZE i
=0; i
< nC
; i
++)
2233 for (SCSIZE k
=0; k
< nR
; k
++)
2234 fSum
+= pX
->GetDouble(i
,k
); // GetDouble(Column,Row)
2235 pResMat
->PutDouble( fSum
.get()/static_cast<double>(nR
),i
);
2239 // Calculates means of the rows of matrix X. X is a RxC matrix;
2240 // ResMat is a Rx1 matrix (=column).
2241 void lcl_CalculateRowMeans(const ScMatrixRef
& pX
, const ScMatrixRef
& pResMat
,
2242 SCSIZE nC
, SCSIZE nR
)
2244 for (SCSIZE k
=0; k
< nR
; k
++)
2246 KahanSum fSum
= 0.0;
2247 for (SCSIZE i
=0; i
< nC
; i
++)
2248 fSum
+= pX
->GetDouble(i
,k
); // GetDouble(Column,Row)
2249 pResMat
->PutDouble( fSum
.get()/static_cast<double>(nC
),k
);
2253 void lcl_CalculateColumnsDelta(const ScMatrixRef
& pMat
, const ScMatrixRef
& pColumnMeans
,
2254 SCSIZE nC
, SCSIZE nR
)
2256 for (SCSIZE i
= 0; i
< nC
; i
++)
2257 for (SCSIZE k
= 0; k
< nR
; k
++)
2258 pMat
->PutDouble( ::rtl::math::approxSub
2259 (pMat
->GetDouble(i
,k
) , pColumnMeans
->GetDouble(i
) ) , i
, k
);
2262 void lcl_CalculateRowsDelta(const ScMatrixRef
& pMat
, const ScMatrixRef
& pRowMeans
,
2263 SCSIZE nC
, SCSIZE nR
)
2265 for (SCSIZE k
= 0; k
< nR
; k
++)
2266 for (SCSIZE i
= 0; i
< nC
; i
++)
2267 pMat
->PutDouble( ::rtl::math::approxSub
2268 ( pMat
->GetDouble(i
,k
) , pRowMeans
->GetDouble(k
) ) , i
, k
);
2271 // Case1 = simple regression
2272 // MatX = X - MeanX, MatY = Y - MeanY, y - haty = (y - MeanY) - (haty - MeanY)
2273 // = (y-MeanY)-((slope*x+a)-(slope*MeanX+a)) = (y-MeanY)-slope*(x-MeanX)
2274 double lcl_GetSSresid(const ScMatrixRef
& pMatX
, const ScMatrixRef
& pMatY
, double fSlope
,
2277 KahanSum fSum
= 0.0;
2278 for (SCSIZE i
=0; i
<nN
; i
++)
2280 const double fTemp
= pMatY
->GetDouble(i
) - fSlope
* pMatX
->GetDouble(i
);
2281 fSum
+= fTemp
* fTemp
;
2288 // Fill default values in matrix X, transform Y to log(Y) in case LOGEST|GROWTH,
2289 // determine sizes of matrices X and Y, determine kind of regression, clone
2290 // Y in case LOGEST|GROWTH, if constant.
2291 bool ScInterpreter::CheckMatrix(bool _bLOG
, sal_uInt8
& nCase
, SCSIZE
& nCX
,
2292 SCSIZE
& nCY
, SCSIZE
& nRX
, SCSIZE
& nRY
, SCSIZE
& M
,
2293 SCSIZE
& N
, ScMatrixRef
& pMatX
, ScMatrixRef
& pMatY
)
2302 pMatY
->GetDimensions(nCY
, nRY
);
2303 const SCSIZE nCountY
= nCY
* nRY
;
2304 for ( SCSIZE i
= 0; i
< nCountY
; i
++ )
2306 if (!pMatY
->IsValue(i
))
2308 PushIllegalArgument();
2315 ScMatrixRef pNewY
= pMatY
->CloneIfConst();
2316 for (SCSIZE nElem
= 0; nElem
< nCountY
; nElem
++)
2318 const double fVal
= pNewY
->GetDouble(nElem
);
2321 PushIllegalArgument();
2325 pNewY
->PutDouble(log(fVal
), nElem
);
2327 pMatY
= std::move(pNewY
);
2332 pMatX
->GetDimensions(nCX
, nRX
);
2333 const SCSIZE nCountX
= nCX
* nRX
;
2334 for ( SCSIZE i
= 0; i
< nCountX
; i
++ )
2335 if (!pMatX
->IsValue(i
))
2337 PushIllegalArgument();
2340 if (nCX
== nCY
&& nRX
== nRY
)
2342 nCase
= 1; // simple regression
2346 else if (nCY
!= 1 && nRY
!= 1)
2348 PushIllegalArgument();
2355 PushIllegalArgument();
2360 nCase
= 2; // Y is column
2365 else if (nCX
!= nCY
)
2367 PushIllegalArgument();
2372 nCase
= 3; // Y is row
2379 pMatX
= GetNewMat(nCY
, nRY
, /*bEmpty*/true);
2384 PushIllegalArgument();
2387 for ( SCSIZE i
= 1; i
<= nCountY
; i
++ )
2388 pMatX
->PutDouble(static_cast<double>(i
), i
-1);
2397 void ScInterpreter::ScLinest()
2399 CalculateRGPRKP(false);
2403 void ScInterpreter::ScLogest()
2405 CalculateRGPRKP(true);
2408 void ScInterpreter::CalculateRGPRKP(bool _bRKP
)
2410 sal_uInt8 nParamCount
= GetByte();
2411 if (!MustHaveParamCount( nParamCount
, 1, 4 ))
2413 bool bConstant
, bStats
;
2415 // optional forth parameter
2416 if (nParamCount
== 4)
2421 // The third parameter may not be missing in ODF, if the forth parameter
2422 // is present. But Excel allows it with default true, we too.
2423 if (nParamCount
>= 3)
2429 // PushIllegalParameter(); if ODF behavior is desired
2433 bConstant
= GetBool();
2439 if (nParamCount
>= 2)
2442 { //In ODF1.2 empty second parameter (which is two ;; ) is allowed
2448 pMatX
= GetMatrix();
2454 ScMatrixRef pMatY
= GetMatrix();
2457 PushIllegalParameter();
2461 // 1 = simple; 2 = multiple with Y as column; 3 = multiple with Y as row
2464 SCSIZE nCX
, nCY
; // number of columns
2465 SCSIZE nRX
, nRY
; //number of rows
2466 SCSIZE K
= 0, N
= 0; // K=number of variables X, N=number of data samples
2467 if (!CheckMatrix(_bRKP
,nCase
,nCX
,nCY
,nRX
,nRY
,K
,N
,pMatX
,pMatY
))
2469 PushIllegalParameter();
2473 // Enough data samples?
2474 if ((bConstant
&& (N
<K
+1)) || (!bConstant
&& (N
<K
)) || (N
<1) || (K
<1))
2476 PushIllegalParameter();
2480 ScMatrixRef pResMat
;
2482 pResMat
= GetNewMat(K
+1,5, /*bEmpty*/true);
2484 pResMat
= GetNewMat(K
+1,1, /*bEmpty*/true);
2487 PushError(FormulaError::CodeOverflow
);
2490 // Fill unused cells in pResMat; order (column,row)
2493 for (SCSIZE i
=2; i
<K
+1; i
++)
2495 pResMat
->PutError( FormulaError::NotAvailable
, i
, 2);
2496 pResMat
->PutError( FormulaError::NotAvailable
, i
, 3);
2497 pResMat
->PutError( FormulaError::NotAvailable
, i
, 4);
2501 // Uses sum(x-MeanX)^2 and not [sum x^2]-N * MeanX^2 in case bConstant.
2502 // Clone constant matrices, so that Mat = Mat - Mean is possible.
2503 double fMeanY
= 0.0;
2506 ScMatrixRef pNewX
= pMatX
->CloneIfConst();
2507 ScMatrixRef pNewY
= pMatY
->CloneIfConst();
2508 if (!pNewX
|| !pNewY
)
2510 PushError(FormulaError::CodeOverflow
);
2513 pMatX
= std::move(pNewX
);
2514 pMatY
= std::move(pNewY
);
2515 // DeltaY is possible here; DeltaX depends on nCase, so later
2516 fMeanY
= lcl_GetMeanOverAll(pMatY
, N
);
2517 for (SCSIZE i
=0; i
<N
; i
++)
2519 pMatY
->PutDouble( ::rtl::math::approxSub(pMatY
->GetDouble(i
),fMeanY
), i
);
2525 // calculate simple regression
2526 double fMeanX
= 0.0;
2528 { // Mat = Mat - Mean
2529 fMeanX
= lcl_GetMeanOverAll(pMatX
, N
);
2530 for (SCSIZE i
=0; i
<N
; i
++)
2532 pMatX
->PutDouble( ::rtl::math::approxSub(pMatX
->GetDouble(i
),fMeanX
), i
);
2535 double fSumXY
= lcl_GetSumProduct(pMatX
,pMatY
,N
);
2536 double fSumX2
= lcl_GetSumProduct(pMatX
,pMatX
,N
);
2539 PushNoValue(); // all x-values are identical
2542 double fSlope
= fSumXY
/ fSumX2
;
2543 double fIntercept
= 0.0;
2545 fIntercept
= fMeanY
- fSlope
* fMeanX
;
2546 pResMat
->PutDouble(_bRKP
? exp(fIntercept
) : fIntercept
, 1, 0); //order (column,row)
2547 pResMat
->PutDouble(_bRKP
? exp(fSlope
) : fSlope
, 0, 0);
2551 double fSSreg
= fSlope
* fSlope
* fSumX2
;
2552 pResMat
->PutDouble(fSSreg
, 0, 4);
2554 double fDegreesFreedom
=static_cast<double>( bConstant
? N
-2 : N
-1 );
2555 pResMat
->PutDouble(fDegreesFreedom
, 1, 3);
2557 double fSSresid
= lcl_GetSSresid(pMatX
,pMatY
,fSlope
,N
);
2558 pResMat
->PutDouble(fSSresid
, 1, 4);
2560 if (fDegreesFreedom
== 0.0 || fSSresid
== 0.0 || fSSreg
== 0.0)
2561 { // exact fit; test SSreg too, because SSresid might be
2562 // unequal zero due to round of errors
2563 pResMat
->PutDouble(0.0, 1, 4); // SSresid
2564 pResMat
->PutError( FormulaError::NotAvailable
, 0, 3); // F
2565 pResMat
->PutDouble(0.0, 1, 2); // RMSE
2566 pResMat
->PutDouble(0.0, 0, 1); // SigmaSlope
2568 pResMat
->PutDouble(0.0, 1, 1); //SigmaIntercept
2570 pResMat
->PutError( FormulaError::NotAvailable
, 1, 1);
2571 pResMat
->PutDouble(1.0, 0, 2); // R^2
2575 double fFstatistic
= (fSSreg
/ static_cast<double>(K
))
2576 / (fSSresid
/ fDegreesFreedom
);
2577 pResMat
->PutDouble(fFstatistic
, 0, 3);
2579 // standard error of estimate
2580 double fRMSE
= sqrt(fSSresid
/ fDegreesFreedom
);
2581 pResMat
->PutDouble(fRMSE
, 1, 2);
2583 double fSigmaSlope
= fRMSE
/ sqrt(fSumX2
);
2584 pResMat
->PutDouble(fSigmaSlope
, 0, 1);
2588 double fSigmaIntercept
= fRMSE
2589 * sqrt(fMeanX
*fMeanX
/fSumX2
+ 1.0/static_cast<double>(N
));
2590 pResMat
->PutDouble(fSigmaIntercept
, 1, 1);
2594 pResMat
->PutError( FormulaError::NotAvailable
, 1, 1);
2597 double fR2
= fSSreg
/ (fSSreg
+ fSSresid
);
2598 pResMat
->PutDouble(fR2
, 0, 2);
2601 PushMatrix(pResMat
);
2603 else // calculate multiple regression;
2605 // Uses a QR decomposition X = QR. The solution B = (X'X)^(-1) * X' * Y
2606 // becomes B = R^(-1) * Q' * Y
2607 if (nCase
==2) // Y is column
2609 ::std::vector
< double> aVecR(N
); // for QR decomposition
2610 // Enough memory for needed matrices?
2611 ScMatrixRef pMeans
= GetNewMat(K
, 1, /*bEmpty*/true); // mean of each column
2612 ScMatrixRef pMatZ
; // for Q' * Y , inter alia
2614 pMatZ
= pMatY
->Clone(); // Y is used in statistic, keep it
2616 pMatZ
= pMatY
; // Y can be overwritten
2617 ScMatrixRef pSlopes
= GetNewMat(1,K
, /*bEmpty*/true); // from b1 to bK
2618 if (!pMeans
|| !pMatZ
|| !pSlopes
)
2620 PushError(FormulaError::CodeOverflow
);
2625 lcl_CalculateColumnMeans(pMatX
, pMeans
, K
, N
);
2626 lcl_CalculateColumnsDelta(pMatX
, pMeans
, K
, N
);
2628 if (!lcl_CalculateQRdecomposition(pMatX
, aVecR
, K
, N
))
2633 // Later on we will divide by elements of aVecR, so make sure
2634 // that they aren't zero.
2635 bool bIsSingular
=false;
2636 for (SCSIZE row
=0; row
< K
&& !bIsSingular
; row
++)
2637 bIsSingular
= aVecR
[row
] == 0.0;
2644 for (SCSIZE col
= 0; col
< K
; col
++)
2646 lcl_ApplyHouseholderTransformation(pMatX
, col
, pMatZ
, N
);
2648 // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
2649 // result Z should have zeros for index>=K; if not, ignore values
2650 for (SCSIZE col
= 0; col
< K
; col
++)
2652 pSlopes
->PutDouble( pMatZ
->GetDouble(col
), col
);
2654 lcl_SolveWithUpperRightTriangle(pMatX
, aVecR
, pSlopes
, K
, false);
2655 double fIntercept
= 0.0;
2657 fIntercept
= fMeanY
- lcl_GetSumProduct(pMeans
,pSlopes
,K
);
2658 // Fill first line in result matrix
2659 pResMat
->PutDouble(_bRKP
? exp(fIntercept
) : fIntercept
, K
, 0 );
2660 for (SCSIZE i
= 0; i
< K
; i
++)
2661 pResMat
->PutDouble(_bRKP
? exp(pSlopes
->GetDouble(i
))
2662 : pSlopes
->GetDouble(i
) , K
-1-i
, 0);
2666 double fSSreg
= 0.0;
2667 double fSSresid
= 0.0;
2668 // re-use memory of Z;
2669 pMatZ
->FillDouble(0.0, 0, 0, 0, N
-1);
2671 lcl_ApplyUpperRightTriangle(pMatX
, aVecR
, pSlopes
, pMatZ
, K
, false);
2672 // Z = Q * Z, that is Q * R * Slopes = X * Slopes
2673 for (SCSIZE colp1
= K
; colp1
> 0; colp1
--)
2675 lcl_ApplyHouseholderTransformation(pMatX
, colp1
-1, pMatZ
,N
);
2677 fSSreg
=lcl_GetSumProduct(pMatZ
, pMatZ
, N
);
2678 // re-use Y for residuals, Y = Y-Z
2679 for (SCSIZE row
= 0; row
< N
; row
++)
2680 pMatY
->PutDouble(pMatY
->GetDouble(row
) - pMatZ
->GetDouble(row
), row
);
2681 fSSresid
= lcl_GetSumProduct(pMatY
, pMatY
, N
);
2682 pResMat
->PutDouble(fSSreg
, 0, 4);
2683 pResMat
->PutDouble(fSSresid
, 1, 4);
2685 double fDegreesFreedom
=static_cast<double>( bConstant
? N
-K
-1 : N
-K
);
2686 pResMat
->PutDouble(fDegreesFreedom
, 1, 3);
2688 if (fDegreesFreedom
== 0.0 || fSSresid
== 0.0 || fSSreg
== 0.0)
2689 { // exact fit; incl. observed values Y are identical
2690 pResMat
->PutDouble(0.0, 1, 4); // SSresid
2691 // F = (SSreg/K) / (SSresid/df) = #DIV/0!
2692 pResMat
->PutError( FormulaError::NotAvailable
, 0, 3); // F
2693 // RMSE = sqrt(SSresid / df) = sqrt(0 / df) = 0
2694 pResMat
->PutDouble(0.0, 1, 2); // RMSE
2695 // SigmaSlope[i] = RMSE * sqrt(matrix[i,i]) = 0 * sqrt(...) = 0
2696 for (SCSIZE i
=0; i
<K
; i
++)
2697 pResMat
->PutDouble(0.0, K
-1-i
, 1);
2699 // SigmaIntercept = RMSE * sqrt(...) = 0
2701 pResMat
->PutDouble(0.0, K
, 1); //SigmaIntercept
2703 pResMat
->PutError( FormulaError::NotAvailable
, K
, 1);
2705 // R^2 = SSreg / (SSreg + SSresid) = 1.0
2706 pResMat
->PutDouble(1.0, 0, 2); // R^2
2710 double fFstatistic
= (fSSreg
/ static_cast<double>(K
))
2711 / (fSSresid
/ fDegreesFreedom
);
2712 pResMat
->PutDouble(fFstatistic
, 0, 3);
2714 // standard error of estimate = root mean SSE
2715 double fRMSE
= sqrt(fSSresid
/ fDegreesFreedom
);
2716 pResMat
->PutDouble(fRMSE
, 1, 2);
2718 // standard error of slopes
2719 // = RMSE * sqrt(diagonal element of (R' R)^(-1) )
2720 // standard error of intercept
2721 // = RMSE * sqrt( Xmean * (R' R)^(-1) * Xmean' + 1/N)
2722 // (R' R)^(-1) = R^(-1) * (R')^(-1). Do not calculate it as
2723 // a whole matrix, but iterate over unit vectors.
2724 KahanSum aSigmaIntercept
= 0.0;
2725 double fPart
; // for Xmean * single column of (R' R)^(-1)
2726 for (SCSIZE col
= 0; col
< K
; col
++)
2728 //re-use memory of MatZ
2729 pMatZ
->FillDouble(0.0,0,0,0,K
-1); // Z = unit vector e
2730 pMatZ
->PutDouble(1.0, col
);
2732 lcl_SolveWithLowerLeftTriangle(pMatX
, aVecR
, pMatZ
, K
, false);
2733 // Solve R * Znew = Zold
2734 lcl_SolveWithUpperRightTriangle(pMatX
, aVecR
, pMatZ
, K
, false);
2735 // now Z is column col in (R' R)^(-1)
2736 double fSigmaSlope
= fRMSE
* sqrt(pMatZ
->GetDouble(col
));
2737 pResMat
->PutDouble(fSigmaSlope
, K
-1-col
, 1);
2738 // (R' R) ^(-1) is symmetric
2741 fPart
= lcl_GetSumProduct(pMeans
, pMatZ
, K
);
2742 aSigmaIntercept
+= fPart
* pMeans
->GetDouble(col
);
2747 double fSigmaIntercept
= fRMSE
2748 * sqrt( (aSigmaIntercept
+ 1.0 / static_cast<double>(N
) ).get() );
2749 pResMat
->PutDouble(fSigmaIntercept
, K
, 1);
2753 pResMat
->PutError( FormulaError::NotAvailable
, K
, 1);
2756 double fR2
= fSSreg
/ (fSSreg
+ fSSresid
);
2757 pResMat
->PutDouble(fR2
, 0, 2);
2760 PushMatrix(pResMat
);
2762 else // nCase == 3, Y is row, all matrices are transposed
2764 ::std::vector
< double> aVecR(N
); // for QR decomposition
2765 // Enough memory for needed matrices?
2766 ScMatrixRef pMeans
= GetNewMat(1, K
, /*bEmpty*/true); // mean of each row
2767 ScMatrixRef pMatZ
; // for Q' * Y , inter alia
2769 pMatZ
= pMatY
->Clone(); // Y is used in statistic, keep it
2771 pMatZ
= pMatY
; // Y can be overwritten
2772 ScMatrixRef pSlopes
= GetNewMat(K
,1, /*bEmpty*/true); // from b1 to bK
2773 if (!pMeans
|| !pMatZ
|| !pSlopes
)
2775 PushError(FormulaError::CodeOverflow
);
2780 lcl_CalculateRowMeans(pMatX
, pMeans
, N
, K
);
2781 lcl_CalculateRowsDelta(pMatX
, pMeans
, N
, K
);
2784 if (!lcl_TCalculateQRdecomposition(pMatX
, aVecR
, K
, N
))
2790 // Later on we will divide by elements of aVecR, so make sure
2791 // that they aren't zero.
2792 bool bIsSingular
=false;
2793 for (SCSIZE row
=0; row
< K
&& !bIsSingular
; row
++)
2794 bIsSingular
= aVecR
[row
] == 0.0;
2801 for (SCSIZE row
= 0; row
< K
; row
++)
2803 lcl_TApplyHouseholderTransformation(pMatX
, row
, pMatZ
, N
);
2805 // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
2806 // result Z should have zeros for index>=K; if not, ignore values
2807 for (SCSIZE col
= 0; col
< K
; col
++)
2809 pSlopes
->PutDouble( pMatZ
->GetDouble(col
), col
);
2811 lcl_SolveWithUpperRightTriangle(pMatX
, aVecR
, pSlopes
, K
, true);
2812 double fIntercept
= 0.0;
2814 fIntercept
= fMeanY
- lcl_GetSumProduct(pMeans
,pSlopes
,K
);
2815 // Fill first line in result matrix
2816 pResMat
->PutDouble(_bRKP
? exp(fIntercept
) : fIntercept
, K
, 0 );
2817 for (SCSIZE i
= 0; i
< K
; i
++)
2818 pResMat
->PutDouble(_bRKP
? exp(pSlopes
->GetDouble(i
))
2819 : pSlopes
->GetDouble(i
) , K
-1-i
, 0);
2823 double fSSreg
= 0.0;
2824 double fSSresid
= 0.0;
2825 // re-use memory of Z;
2826 pMatZ
->FillDouble(0.0, 0, 0, N
-1, 0);
2828 lcl_ApplyUpperRightTriangle(pMatX
, aVecR
, pSlopes
, pMatZ
, K
, true);
2829 // Z = Q * Z, that is Q * R * Slopes = X * Slopes
2830 for (SCSIZE rowp1
= K
; rowp1
> 0; rowp1
--)
2832 lcl_TApplyHouseholderTransformation(pMatX
, rowp1
-1, pMatZ
,N
);
2834 fSSreg
=lcl_GetSumProduct(pMatZ
, pMatZ
, N
);
2835 // re-use Y for residuals, Y = Y-Z
2836 for (SCSIZE col
= 0; col
< N
; col
++)
2837 pMatY
->PutDouble(pMatY
->GetDouble(col
) - pMatZ
->GetDouble(col
), col
);
2838 fSSresid
= lcl_GetSumProduct(pMatY
, pMatY
, N
);
2839 pResMat
->PutDouble(fSSreg
, 0, 4);
2840 pResMat
->PutDouble(fSSresid
, 1, 4);
2842 double fDegreesFreedom
=static_cast<double>( bConstant
? N
-K
-1 : N
-K
);
2843 pResMat
->PutDouble(fDegreesFreedom
, 1, 3);
2845 if (fDegreesFreedom
== 0.0 || fSSresid
== 0.0 || fSSreg
== 0.0)
2846 { // exact fit; incl. case observed values Y are identical
2847 pResMat
->PutDouble(0.0, 1, 4); // SSresid
2848 // F = (SSreg/K) / (SSresid/df) = #DIV/0!
2849 pResMat
->PutError( FormulaError::NotAvailable
, 0, 3); // F
2850 // RMSE = sqrt(SSresid / df) = sqrt(0 / df) = 0
2851 pResMat
->PutDouble(0.0, 1, 2); // RMSE
2852 // SigmaSlope[i] = RMSE * sqrt(matrix[i,i]) = 0 * sqrt(...) = 0
2853 for (SCSIZE i
=0; i
<K
; i
++)
2854 pResMat
->PutDouble(0.0, K
-1-i
, 1);
2856 // SigmaIntercept = RMSE * sqrt(...) = 0
2858 pResMat
->PutDouble(0.0, K
, 1); //SigmaIntercept
2860 pResMat
->PutError( FormulaError::NotAvailable
, K
, 1);
2862 // R^2 = SSreg / (SSreg + SSresid) = 1.0
2863 pResMat
->PutDouble(1.0, 0, 2); // R^2
2867 double fFstatistic
= (fSSreg
/ static_cast<double>(K
))
2868 / (fSSresid
/ fDegreesFreedom
);
2869 pResMat
->PutDouble(fFstatistic
, 0, 3);
2871 // standard error of estimate = root mean SSE
2872 double fRMSE
= sqrt(fSSresid
/ fDegreesFreedom
);
2873 pResMat
->PutDouble(fRMSE
, 1, 2);
2875 // standard error of slopes
2876 // = RMSE * sqrt(diagonal element of (R' R)^(-1) )
2877 // standard error of intercept
2878 // = RMSE * sqrt( Xmean * (R' R)^(-1) * Xmean' + 1/N)
2879 // (R' R)^(-1) = R^(-1) * (R')^(-1). Do not calculate it as
2880 // a whole matrix, but iterate over unit vectors.
2881 // (R' R) ^(-1) is symmetric
2882 KahanSum aSigmaIntercept
= 0.0;
2883 double fPart
; // for Xmean * single col of (R' R)^(-1)
2884 for (SCSIZE row
= 0; row
< K
; row
++)
2886 //re-use memory of MatZ
2887 pMatZ
->FillDouble(0.0,0,0,K
-1,0); // Z = unit vector e
2888 pMatZ
->PutDouble(1.0, row
);
2890 lcl_SolveWithLowerLeftTriangle(pMatX
, aVecR
, pMatZ
, K
, true);
2891 // Solve R * Znew = Zold
2892 lcl_SolveWithUpperRightTriangle(pMatX
, aVecR
, pMatZ
, K
, true);
2893 // now Z is column col in (R' R)^(-1)
2894 double fSigmaSlope
= fRMSE
* sqrt(pMatZ
->GetDouble(row
));
2895 pResMat
->PutDouble(fSigmaSlope
, K
-1-row
, 1);
2898 fPart
= lcl_GetSumProduct(pMeans
, pMatZ
, K
);
2899 aSigmaIntercept
+= fPart
* pMeans
->GetDouble(row
);
2904 double fSigmaIntercept
= fRMSE
2905 * sqrt( (aSigmaIntercept
+ 1.0 / static_cast<double>(N
) ).get() );
2906 pResMat
->PutDouble(fSigmaIntercept
, K
, 1);
2910 pResMat
->PutError( FormulaError::NotAvailable
, K
, 1);
2913 double fR2
= fSSreg
/ (fSSreg
+ fSSresid
);
2914 pResMat
->PutDouble(fR2
, 0, 2);
2917 PushMatrix(pResMat
);
2922 void ScInterpreter::ScTrend()
2924 CalculateTrendGrowth(false);
2927 void ScInterpreter::ScGrowth()
2929 CalculateTrendGrowth(true);
2932 void ScInterpreter::CalculateTrendGrowth(bool _bGrowth
)
2934 sal_uInt8 nParamCount
= GetByte();
2935 if (!MustHaveParamCount( nParamCount
, 1, 4 ))
2938 // optional forth parameter
2940 if (nParamCount
== 4)
2941 bConstant
= GetBool();
2945 // The third parameter may be missing in ODF, although the forth parameter
2946 // is present. Default values depend on data not yet read.
2947 ScMatrixRef pMatNewX
;
2948 if (nParamCount
>= 3)
2956 pMatNewX
= GetMatrix();
2961 //In ODF1.2 empty second parameter (which is two ;; ) is allowed
2962 //Defaults will be set in CheckMatrix
2964 if (nParamCount
>= 2)
2973 pMatX
= GetMatrix();
2979 ScMatrixRef pMatY
= GetMatrix();
2982 PushIllegalParameter();
2986 // 1 = simple; 2 = multiple with Y as column; 3 = multiple with Y as row
2989 SCSIZE nCX
, nCY
; // number of columns
2990 SCSIZE nRX
, nRY
; //number of rows
2991 SCSIZE K
= 0, N
= 0; // K=number of variables X, N=number of data samples
2992 if (!CheckMatrix(_bGrowth
,nCase
,nCX
,nCY
,nRX
,nRY
,K
,N
,pMatX
,pMatY
))
2994 PushIllegalParameter();
2998 // Enough data samples?
2999 if ((bConstant
&& (N
<K
+1)) || (!bConstant
&& (N
<K
)) || (N
<1) || (K
<1))
3001 PushIllegalParameter();
3005 // Set default pMatNewX if necessary
3012 nCountXN
= nCXN
* nRXN
;
3013 pMatNewX
= pMatX
->Clone(); // pMatX will be changed to X-meanX
3017 pMatNewX
->GetDimensions(nCXN
, nRXN
);
3018 if ((nCase
== 2 && K
!= nCXN
) || (nCase
== 3 && K
!= nRXN
))
3020 PushIllegalArgument();
3023 nCountXN
= nCXN
* nRXN
;
3024 for (SCSIZE i
= 0; i
< nCountXN
; i
++)
3025 if (!pMatNewX
->IsValue(i
))
3027 PushIllegalArgument();
3031 ScMatrixRef pResMat
; // size depends on nCase
3033 pResMat
= GetNewMat(nCXN
,nRXN
, /*bEmpty*/true);
3037 pResMat
= GetNewMat(1,nRXN
, /*bEmpty*/true);
3039 pResMat
= GetNewMat(nCXN
,1, /*bEmpty*/true);
3043 PushError(FormulaError::CodeOverflow
);
3046 // Uses sum(x-MeanX)^2 and not [sum x^2]-N * MeanX^2 in case bConstant.
3047 // Clone constant matrices, so that Mat = Mat - Mean is possible.
3048 double fMeanY
= 0.0;
3051 ScMatrixRef pCopyX
= pMatX
->CloneIfConst();
3052 ScMatrixRef pCopyY
= pMatY
->CloneIfConst();
3053 if (!pCopyX
|| !pCopyY
)
3055 PushError(FormulaError::MatrixSize
);
3058 pMatX
= std::move(pCopyX
);
3059 pMatY
= std::move(pCopyY
);
3060 // DeltaY is possible here; DeltaX depends on nCase, so later
3061 fMeanY
= lcl_GetMeanOverAll(pMatY
, N
);
3062 for (SCSIZE i
=0; i
<N
; i
++)
3064 pMatY
->PutDouble( ::rtl::math::approxSub(pMatY
->GetDouble(i
),fMeanY
), i
);
3070 // calculate simple regression
3071 double fMeanX
= 0.0;
3073 { // Mat = Mat - Mean
3074 fMeanX
= lcl_GetMeanOverAll(pMatX
, N
);
3075 for (SCSIZE i
=0; i
<N
; i
++)
3077 pMatX
->PutDouble( ::rtl::math::approxSub(pMatX
->GetDouble(i
),fMeanX
), i
);
3080 double fSumXY
= lcl_GetSumProduct(pMatX
,pMatY
,N
);
3081 double fSumX2
= lcl_GetSumProduct(pMatX
,pMatX
,N
);
3084 PushNoValue(); // all x-values are identical
3087 double fSlope
= fSumXY
/ fSumX2
;
3091 double fIntercept
= fMeanY
- fSlope
* fMeanX
;
3092 for (SCSIZE i
= 0; i
< nCountXN
; i
++)
3094 fHelp
= pMatNewX
->GetDouble(i
)*fSlope
+ fIntercept
;
3095 pResMat
->PutDouble(_bGrowth
? exp(fHelp
) : fHelp
, i
);
3100 for (SCSIZE i
= 0; i
< nCountXN
; i
++)
3102 fHelp
= pMatNewX
->GetDouble(i
)*fSlope
;
3103 pResMat
->PutDouble(_bGrowth
? exp(fHelp
) : fHelp
, i
);
3107 else // calculate multiple regression;
3109 if (nCase
==2) // Y is column
3111 ::std::vector
< double> aVecR(N
); // for QR decomposition
3112 // Enough memory for needed matrices?
3113 ScMatrixRef pMeans
= GetNewMat(K
, 1, /*bEmpty*/true); // mean of each column
3114 ScMatrixRef pSlopes
= GetNewMat(1,K
, /*bEmpty*/true); // from b1 to bK
3115 if (!pMeans
|| !pSlopes
)
3117 PushError(FormulaError::CodeOverflow
);
3122 lcl_CalculateColumnMeans(pMatX
, pMeans
, K
, N
);
3123 lcl_CalculateColumnsDelta(pMatX
, pMeans
, K
, N
);
3125 if (!lcl_CalculateQRdecomposition(pMatX
, aVecR
, K
, N
))
3130 // Later on we will divide by elements of aVecR, so make sure
3131 // that they aren't zero.
3132 bool bIsSingular
=false;
3133 for (SCSIZE row
=0; row
< K
&& !bIsSingular
; row
++)
3134 bIsSingular
= aVecR
[row
] == 0.0;
3140 // Z := Q' Y; Y is overwritten with result Z
3141 for (SCSIZE col
= 0; col
< K
; col
++)
3143 lcl_ApplyHouseholderTransformation(pMatX
, col
, pMatY
, N
);
3145 // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
3146 // result Z should have zeros for index>=K; if not, ignore values
3147 for (SCSIZE col
= 0; col
< K
; col
++)
3149 pSlopes
->PutDouble( pMatY
->GetDouble(col
), col
);
3151 lcl_SolveWithUpperRightTriangle(pMatX
, aVecR
, pSlopes
, K
, false);
3153 // Fill result matrix
3154 lcl_MFastMult(pMatNewX
,pSlopes
,pResMat
,nRXN
,K
,1);
3157 double fIntercept
= fMeanY
- lcl_GetSumProduct(pMeans
,pSlopes
,K
);
3158 for (SCSIZE row
= 0; row
< nRXN
; row
++)
3159 pResMat
->PutDouble(pResMat
->GetDouble(row
)+fIntercept
, row
);
3163 for (SCSIZE i
= 0; i
< nRXN
; i
++)
3164 pResMat
->PutDouble(exp(pResMat
->GetDouble(i
)), i
);
3168 { // nCase == 3, Y is row, all matrices are transposed
3170 ::std::vector
< double> aVecR(N
); // for QR decomposition
3171 // Enough memory for needed matrices?
3172 ScMatrixRef pMeans
= GetNewMat(1, K
, /*bEmpty*/true); // mean of each row
3173 ScMatrixRef pSlopes
= GetNewMat(K
,1, /*bEmpty*/true); // row from b1 to bK
3174 if (!pMeans
|| !pSlopes
)
3176 PushError(FormulaError::CodeOverflow
);
3181 lcl_CalculateRowMeans(pMatX
, pMeans
, N
, K
);
3182 lcl_CalculateRowsDelta(pMatX
, pMeans
, N
, K
);
3184 if (!lcl_TCalculateQRdecomposition(pMatX
, aVecR
, K
, N
))
3189 // Later on we will divide by elements of aVecR, so make sure
3190 // that they aren't zero.
3191 bool bIsSingular
=false;
3192 for (SCSIZE row
=0; row
< K
&& !bIsSingular
; row
++)
3193 bIsSingular
= aVecR
[row
] == 0.0;
3199 // Z := Q' Y; Y is overwritten with result Z
3200 for (SCSIZE row
= 0; row
< K
; row
++)
3202 lcl_TApplyHouseholderTransformation(pMatX
, row
, pMatY
, N
);
3204 // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
3205 // result Z should have zeros for index>=K; if not, ignore values
3206 for (SCSIZE col
= 0; col
< K
; col
++)
3208 pSlopes
->PutDouble( pMatY
->GetDouble(col
), col
);
3210 lcl_SolveWithUpperRightTriangle(pMatX
, aVecR
, pSlopes
, K
, true);
3212 // Fill result matrix
3213 lcl_MFastMult(pSlopes
,pMatNewX
,pResMat
,1,K
,nCXN
);
3216 double fIntercept
= fMeanY
- lcl_GetSumProduct(pMeans
,pSlopes
,K
);
3217 for (SCSIZE col
= 0; col
< nCXN
; col
++)
3218 pResMat
->PutDouble(pResMat
->GetDouble(col
)+fIntercept
, col
);
3222 for (SCSIZE i
= 0; i
< nCXN
; i
++)
3223 pResMat
->PutDouble(exp(pResMat
->GetDouble(i
)), i
);
3227 PushMatrix(pResMat
);
3230 void ScInterpreter::ScMatRef()
3232 // In case it contains relative references resolve them as usual.
3235 PopSingleRef( aAdr
);
3237 ScRefCellValue
aCell(mrDoc
, aAdr
);
3239 if (aCell
.getType() != CELLTYPE_FORMULA
)
3241 PushError( FormulaError::NoRef
);
3245 if (aCell
.getFormula()->IsRunning())
3247 // Twisted odd corner case where an array element's cell tries to
3248 // access the top left matrix while it is still running, see tdf#88737
3249 // This is a hackish workaround, not a general solution, the matrix
3250 // isn't available anyway and FormulaError::CircularReference would be set.
3251 PushError( FormulaError::RetryCircular
);
3255 const ScMatrix
* pMat
= aCell
.getFormula()->GetMatrix();
3258 SCSIZE nCols
, nRows
;
3259 pMat
->GetDimensions( nCols
, nRows
);
3260 SCSIZE nC
= static_cast<SCSIZE
>(aPos
.Col() - aAdr
.Col());
3261 SCSIZE nR
= static_cast<SCSIZE
>(aPos
.Row() - aAdr
.Row());
3263 // XXX: this could be an additional change for tdf#145085 to not
3264 // display the URL in a voluntary entered 2-rows array context.
3265 // However, that might as well be used on purpose to implement a check
3266 // on the URL, which existing documents may have done, the more that
3267 // before the accompanying change of
3268 // ScFormulaCell::GetResultDimensions() the cell array was forced to
3269 // two rows. Do not change without compelling reason. Note that this
3270 // repeating top cell is what Excel implements, but it has no
3271 // additional value so probably isn't used there. Exporting to and
3272 // using in Excel though will lose this capability.
3273 if (aCell
.mpFormula
->GetCode()->IsHyperLink())
3275 // Row 2 element is the URL that is not to be displayed, fake a
3276 // 1-row cell-text-only matrix that is repeated.
3281 if ((nCols
<= nC
&& nCols
!= 1) || (nRows
<= nR
&& nRows
!= 1))
3285 const ScMatrixValue nMatVal
= pMat
->Get( nC
, nR
);
3286 ScMatValType nMatValType
= nMatVal
.nType
;
3288 if (ScMatrix::IsNonValueType( nMatValType
))
3290 if (ScMatrix::IsEmptyPathType( nMatValType
))
3291 { // result of empty false jump path
3292 nFuncFmtType
= SvNumFormatType::LOGICAL
;
3295 else if (ScMatrix::IsEmptyType( nMatValType
))
3297 // Not inherited (really?) and display as empty string, not 0.
3298 PushTempToken( new ScEmptyCellToken( false, true));
3301 PushString( nMatVal
.GetString() );
3305 // Determine nFuncFmtType type before PushDouble().
3306 mrDoc
.GetNumberFormatInfo(mrContext
, nCurFmtType
, nCurFmtIndex
, aAdr
);
3307 nFuncFmtType
= nCurFmtType
;
3308 nFuncFmtIndex
= nCurFmtIndex
;
3309 PushDouble(nMatVal
.fVal
); // handles DoubleError
3315 // Determine nFuncFmtType type before PushDouble().
3316 mrDoc
.GetNumberFormatInfo(mrContext
, nCurFmtType
, nCurFmtIndex
, aAdr
);
3317 nFuncFmtType
= nCurFmtType
;
3318 nFuncFmtIndex
= nCurFmtIndex
;
3319 // If not a result matrix, obtain the cell value.
3320 FormulaError nErr
= aCell
.getFormula()->GetErrCode();
3321 if (nErr
!= FormulaError::NONE
)
3323 else if (aCell
.getFormula()->IsValue())
3324 PushDouble(aCell
.getFormula()->GetValue());
3327 svl::SharedString aVal
= aCell
.getFormula()->GetString();
3333 void ScInterpreter::ScInfo()
3335 if( !MustHaveParamCount( GetByte(), 1 ) )
3338 OUString aStr
= GetString().getString();
3339 ScCellKeywordTranslator::transKeyword(aStr
, &ScGlobal::GetLocale(), ocInfo
);
3340 if( aStr
== "SYSTEM" )
3341 PushString( u
"" SC_INFO_OSVERSION
""_ustr
);
3342 else if( aStr
== "OSVERSION" )
3343 #if (defined LINUX || defined __FreeBSD__)
3344 PushString(Application::GetOSVersion());
3345 #elif defined MACOSX
3346 // TODO tdf#140286 handle MACOSX version to get result compatible to Excel
3347 PushString("Windows (32-bit) NT 5.01");
3348 #else // handle Windows (WNT, WIN_NT, WIN32, _WIN32)
3349 // TODO tdf#140286 handle Windows version to get a result compatible to Excel
3350 PushString( "Windows (32-bit) NT 5.01" );
3352 else if( aStr
== "RELEASE" )
3353 PushString( ::utl::Bootstrap::getBuildIdData( OUString() ) );
3354 else if( aStr
== "NUMFILE" )
3356 else if( aStr
== "RECALC" )
3357 PushString( ScResId( mrDoc
.GetAutoCalc() ? STR_RECALC_AUTO
: STR_RECALC_MANUAL
) );
3358 else if (aStr
== "DIRECTORY" || aStr
== "MEMAVAIL" || aStr
== "MEMUSED" || aStr
== "ORIGIN" || aStr
== "TOTMEM")
3361 PushIllegalArgument();
3364 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */