xref: /core/sc/source/core/tool/interpr5.cxx (revision 6376fe01859a14a22b2601ff04691ceb894c33d4)
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 #include <rtl/math.hxx>
21 #include <string.h>
22 #include <math.h>
23 
24 #ifdef DEBUG_SC_LUP_DECOMPOSITION
25 #include <stdio.h>
26 #endif
27 
28 #include <unotools/bootstrap.hxx>
29 #include <svl/zforlist.hxx>
30 #include <tools/duration.hxx>
31 
32 #include <interpre.hxx>
33 #include <global.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::
43 
44 #include <vector>
45 
46 using namespace formula;
47 
48 namespace {
49 
MatrixAdd(const double & lhs,const double & rhs)50 double MatrixAdd(const double& lhs, const double& rhs)
51 {
52     return ::rtl::math::approxAdd( lhs,rhs);
53 }
54 
MatrixSub(const double & lhs,const double & rhs)55 double MatrixSub(const double& lhs, const double& rhs)
56 {
57     return ::rtl::math::approxSub( lhs,rhs);
58 }
59 
MatrixMul(const double & lhs,const double & rhs)60 double MatrixMul(const double& lhs, const double& rhs)
61 {
62     return lhs * rhs;
63 }
64 
MatrixDiv(const double & lhs,const double & rhs)65 double MatrixDiv(const double& lhs, const double& rhs)
66 {
67     return ScInterpreter::div( lhs,rhs);
68 }
69 
MatrixPow(const double & lhs,const double & rhs)70 double MatrixPow(const double& lhs, const double& rhs)
71 {
72     return ::pow( lhs,rhs);
73 }
74 
75 // Multiply n x m Mat A with m x l Mat B to n x l Mat R
lcl_MFastMult(const ScMatrixRef & pA,const ScMatrixRef & pB,const ScMatrixRef & pR,SCSIZE n,SCSIZE m,SCSIZE l)76 void lcl_MFastMult(const ScMatrixRef& pA, const ScMatrixRef& pB, const ScMatrixRef& pR,
77                    SCSIZE n, SCSIZE m, SCSIZE l)
78 {
79     for (SCSIZE row = 0; row < n; row++)
80     {
81         for (SCSIZE col = 0; col < l; col++)
82         {   // result element(col, row) =sum[ (row of A) * (column of B)]
83             KahanSum fSum = 0.0;
84             for (SCSIZE k = 0; k < m; k++)
85                 fSum += pA->GetDouble(k,row) * pB->GetDouble(col,k);
86             pR->PutDouble(fSum.get(), col, row);
87         }
88     }
89 }
90 
91 }
92 
ScGetGCD(double fx,double fy)93 double ScInterpreter::ScGetGCD(double fx, double fy)
94 {
95     // By ODFF definition GCD(0,a) => a. This is also vital for the code in
96     // ScGCD() to work correctly with a preset fy=0.0
97     if (fy == 0.0)
98         return fx;
99     else if (fx == 0.0)
100         return fy;
101     else
102     {
103         double fz = fmod(fx, fy);
104         while (fz > 0.0)
105         {
106             fx = fy;
107             fy = fz;
108             fz = fmod(fx, fy);
109         }
110         return fy;
111     }
112 }
113 
ScGCD()114 void ScInterpreter::ScGCD()
115 {
116     short nParamCount = GetByte();
117     if ( !MustHaveParamCountMin( nParamCount, 1 ) )
118         return;
119 
120     double fx, fy = 0.0;
121     ScRange aRange;
122     size_t nRefInList = 0;
123     while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
124     {
125         switch (GetStackType())
126         {
127             case svDouble :
128             case svString:
129             case svSingleRef:
130             {
131                 fx = ::rtl::math::approxFloor( GetDouble());
132                 if (fx < 0.0)
133                 {
134                     PushIllegalArgument();
135                     return;
136                 }
137                 fy = ScGetGCD(fx, fy);
138             }
139             break;
140             case svDoubleRef :
141             case svRefList :
142             {
143                 FormulaError nErr = FormulaError::NONE;
144                 PopDoubleRef( aRange, nParamCount, nRefInList);
145                 double nCellVal;
146                 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags );
147                 if (aValIter.GetFirst(nCellVal, nErr))
148                 {
149                     do
150                     {
151                         fx = ::rtl::math::approxFloor( nCellVal);
152                         if (fx < 0.0)
153                         {
154                             PushIllegalArgument();
155                             return;
156                         }
157                         fy = ScGetGCD(fx, fy);
158                     } while (nErr == FormulaError::NONE && aValIter.GetNext(nCellVal, nErr));
159                 }
160                 SetError(nErr);
161             }
162             break;
163             case svMatrix :
164             case svExternalSingleRef:
165             case svExternalDoubleRef:
166             {
167                 ScMatrixRef pMat = GetMatrix();
168                 if (pMat)
169                 {
170                     SCSIZE nC, nR;
171                     pMat->GetDimensions(nC, nR);
172                     if (nC == 0 || nR == 0)
173                         SetError(FormulaError::IllegalArgument);
174                     else
175                     {
176                      double nVal = pMat->GetGcd();
177                      fy = ScGetGCD(nVal,fy);
178                     }
179                 }
180             }
181             break;
182             default : SetError(FormulaError::IllegalParameter); break;
183         }
184     }
185     PushDouble(fy);
186 }
187 
ScLCM()188 void ScInterpreter:: ScLCM()
189 {
190     short nParamCount = GetByte();
191     if ( !MustHaveParamCountMin( nParamCount, 1 ) )
192         return;
193 
194     double fx, fy = 1.0;
195     ScRange aRange;
196     size_t nRefInList = 0;
197     while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
198     {
199         switch (GetStackType())
200         {
201             case svDouble :
202             case svString:
203             case svSingleRef:
204             {
205                 fx = ::rtl::math::approxFloor( GetDouble());
206                 if (fx < 0.0)
207                 {
208                     PushIllegalArgument();
209                     return;
210                 }
211                 if (fx == 0.0 || fy == 0.0)
212                     fy = 0.0;
213                 else
214                     fy = fx * fy / ScGetGCD(fx, fy);
215             }
216             break;
217             case svDoubleRef :
218             case svRefList :
219             {
220                 FormulaError nErr = FormulaError::NONE;
221                 PopDoubleRef( aRange, nParamCount, nRefInList);
222                 double nCellVal;
223                 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags );
224                 if (aValIter.GetFirst(nCellVal, nErr))
225                 {
226                     do
227                     {
228                         fx = ::rtl::math::approxFloor( nCellVal);
229                         if (fx < 0.0)
230                         {
231                             PushIllegalArgument();
232                             return;
233                         }
234                         if (fx == 0.0 || fy == 0.0)
235                             fy = 0.0;
236                         else
237                             fy = fx * fy / ScGetGCD(fx, fy);
238                     } while (nErr == FormulaError::NONE && aValIter.GetNext(nCellVal, nErr));
239                 }
240                 SetError(nErr);
241             }
242             break;
243             case svMatrix :
244             case svExternalSingleRef:
245             case svExternalDoubleRef:
246             {
247                 ScMatrixRef pMat = GetMatrix();
248                 if (pMat)
249                 {
250                     SCSIZE nC, nR;
251                     pMat->GetDimensions(nC, nR);
252                     if (nC == 0 || nR == 0)
253                         SetError(FormulaError::IllegalArgument);
254                     else
255                     {
256                      double nVal = pMat->GetLcm();
257                      fy = (nVal * fy ) / ScGetGCD(nVal, fy);
258                     }
259                 }
260             }
261             break;
262             default : SetError(FormulaError::IllegalParameter); break;
263         }
264     }
265     PushDouble(fy);
266 }
267 
MakeMatNew(ScMatrixRef & rMat,SCSIZE nC,SCSIZE nR)268 void ScInterpreter::MakeMatNew(ScMatrixRef& rMat, SCSIZE nC, SCSIZE nR)
269 {
270     rMat->SetErrorInterpreter( this);
271     // A temporary matrix is mutable and ScMatrix::CloneIfConst() returns the
272     // very matrix.
273     rMat->SetMutable();
274     SCSIZE nCols, nRows;
275     rMat->GetDimensions( nCols, nRows);
276     if ( nCols != nC || nRows != nR )
277     {   // arbitrary limit of elements exceeded
278         SetError( FormulaError::MatrixSize);
279         rMat.reset();
280     }
281 }
282 
GetNewMat(SCSIZE nC,SCSIZE nR,bool bEmpty)283 ScMatrixRef ScInterpreter::GetNewMat(SCSIZE nC, SCSIZE nR, bool bEmpty)
284 {
285     ScMatrixRef pMat;
286     if (bEmpty)
287         pMat = new ScMatrix(nC, nR);
288     else
289         pMat = new ScMatrix(nC, nR, 0.0);
290     MakeMatNew(pMat, nC, nR);
291     return pMat;
292 }
293 
GetNewMat(SCSIZE nC,SCSIZE nR,const std::vector<double> & rValues)294 ScMatrixRef ScInterpreter::GetNewMat(SCSIZE nC, SCSIZE nR, const std::vector<double>& rValues)
295 {
296     ScMatrixRef pMat(new ScMatrix(nC, nR, rValues));
297     MakeMatNew(pMat, nC, nR);
298     return pMat;
299 }
300 
CreateMatrixFromDoubleRef(const FormulaToken * pToken,SCCOL nCol1,SCROW nRow1,SCTAB nTab1,SCCOL nCol2,SCROW nRow2,SCTAB nTab2)301 ScMatrixRef ScInterpreter::CreateMatrixFromDoubleRef( const FormulaToken* pToken,
302         SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
303         SCCOL nCol2, SCROW nRow2, SCTAB nTab2 )
304 {
305     if (nTab1 != nTab2 || nGlobalError != FormulaError::NONE)
306     {
307         // Not a 2D matrix.
308         SetError(FormulaError::IllegalParameter);
309         return nullptr;
310     }
311 
312     if (nTab1 == nTab2 && pToken)
313     {
314         const ScComplexRefData& rCRef = *pToken->GetDoubleRef();
315         if (rCRef.IsTrimToData())
316         {
317             // Clamp the size of the matrix area to rows which actually contain data.
318             // For e.g. SUM(IF over an entire column, this can make a big difference,
319             // But let's not trim the empty space from the top or left as this matters
320             // at least in matrix formulas involving IF().
321             // Refer ScCompiler::AnnotateTrimOnDoubleRefs() where double-refs are
322             // flagged for trimming.
323             SCCOL nTempCol = nCol1;
324             SCROW nTempRow = nRow1;
325             mrDoc.ShrinkToDataArea(nTab1, nTempCol, nTempRow, nCol2, nRow2);
326         }
327     }
328 
329     SCSIZE nMatCols = static_cast<SCSIZE>(nCol2) - nCol1 + 1;
330     SCSIZE nMatRows = static_cast<SCSIZE>(nRow2) - nRow1 + 1;
331 
332     if (!ScMatrix::IsSizeAllocatable( nMatCols, nMatRows))
333     {
334         SetError(FormulaError::MatrixSize);
335         return nullptr;
336     }
337 
338     ScTokenMatrixMap::const_iterator aIter;
339     if (pToken && ((aIter = maTokenMatrixMap.find( pToken)) != maTokenMatrixMap.end()))
340     {
341         /* XXX casting const away here is ugly; ScMatrixToken (to which the
342          * result of this function usually is assigned) should not be forced to
343          * carry a ScConstMatrixRef though.
344          * TODO: a matrix already stored in pTokenMatrixMap should be
345          * read-only and have a copy-on-write mechanism. Previously all tokens
346          * were modifiable so we're already better than before ... */
347         return const_cast<FormulaToken*>((*aIter).second.get())->GetMatrix();
348     }
349 
350     ScMatrixRef pMat = GetNewMat( nMatCols, nMatRows, true);
351     if (!pMat || nGlobalError != FormulaError::NONE)
352         return nullptr;
353 
354     if (!bCalcAsShown)
355     {
356         // Use fast array fill.
357         mrDoc.FillMatrix(*pMat, nTab1, nCol1, nRow1, nCol2, nRow2);
358     }
359     else
360     {
361         // Use slower ScCellIterator to round values.
362 
363         // TODO: this probably could use CellBucket for faster storage, see
364         // sc/source/core/data/column2.cxx and FillMatrixHandler, and then be
365         // moved to a function on its own, and/or squeeze the rounding into a
366         // similar FillMatrixHandler that would need to keep track of the cell
367         // position then.
368 
369         // Set position where the next entry is expected.
370         SCROW nNextRow = nRow1;
371         SCCOL nNextCol = nCol1;
372         // Set last position as if there was a previous entry.
373         SCROW nThisRow = nRow2;
374         SCCOL nThisCol = nCol1 - 1;
375 
376         ScCellIterator aCellIter( mrDoc, ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2));
377         for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
378         {
379             nThisCol = aCellIter.GetPos().Col();
380             nThisRow = aCellIter.GetPos().Row();
381             if (nThisCol != nNextCol || nThisRow != nNextRow)
382             {
383                 // Fill empty between iterator's positions.
384                 for ( ; nNextCol <= nThisCol; ++nNextCol)
385                 {
386                     const SCSIZE nC = nNextCol - nCol1;
387                     const SCSIZE nMatStopRow = ((nNextCol < nThisCol) ? nMatRows : nThisRow - nRow1);
388                     for (SCSIZE nR = nNextRow - nRow1; nR < nMatStopRow; ++nR)
389                     {
390                         pMat->PutEmpty( nC, nR);
391                     }
392                     nNextRow = nRow1;
393                 }
394             }
395             if (nThisRow == nRow2)
396             {
397                 nNextCol = nThisCol + 1;
398                 nNextRow = nRow1;
399             }
400             else
401             {
402                 nNextCol = nThisCol;
403                 nNextRow = nThisRow + 1;
404             }
405 
406             const SCSIZE nMatCol = static_cast<SCSIZE>(nThisCol - nCol1);
407             const SCSIZE nMatRow = static_cast<SCSIZE>(nThisRow - nRow1);
408             ScRefCellValue aCell( aCellIter.getRefCellValue());
409             if (aCellIter.isEmpty() || aCell.hasEmptyValue())
410             {
411                 pMat->PutEmpty( nMatCol, nMatRow);
412             }
413             else if (aCell.hasError())
414             {
415                 pMat->PutError( aCell.getFormula()->GetErrCode(), nMatCol, nMatRow);
416             }
417             else if (aCell.hasNumeric())
418             {
419                 double fVal = aCell.getValue();
420                 // CELLTYPE_FORMULA already stores the rounded value.
421                 if (aCell.getType() == CELLTYPE_VALUE)
422                 {
423                     // TODO: this could be moved to ScCellIterator to take
424                     // advantage of the faster ScAttrArray_IterGetNumberFormat.
425                     const ScAddress aAdr( nThisCol, nThisRow, nTab1);
426                     const sal_uInt32 nNumFormat = mrDoc.GetNumberFormat( mrContext, aAdr);
427                     fVal = mrDoc.RoundValueAsShown( fVal, nNumFormat, &mrContext);
428                 }
429                 pMat->PutDouble( fVal, nMatCol, nMatRow);
430             }
431             else if (aCell.hasString())
432             {
433                 pMat->PutString( mrStrPool.intern( aCell.getString(mrDoc)), nMatCol, nMatRow);
434             }
435             else
436             {
437                 assert(!"aCell.what?");
438                 pMat->PutEmpty( nMatCol, nMatRow);
439             }
440         }
441 
442         // Fill empty if iterator's last position wasn't the end.
443         if (nThisCol != nCol2 || nThisRow != nRow2)
444         {
445             for ( ; nNextCol <= nCol2; ++nNextCol)
446             {
447                 SCSIZE nC = nNextCol - nCol1;
448                 for (SCSIZE nR = nNextRow - nRow1; nR < nMatRows; ++nR)
449                 {
450                     pMat->PutEmpty( nC, nR);
451                 }
452                 nNextRow = nRow1;
453             }
454         }
455     }
456 
457     if (pToken)
458         maTokenMatrixMap.emplace(pToken, new ScMatrixToken( pMat));
459 
460     return pMat;
461 }
462 
GetMatrix()463 ScMatrixRef ScInterpreter::GetMatrix()
464 {
465     ScMatrixRef pMat = nullptr;
466     switch (GetRawStackType())
467     {
468         case svSingleRef :
469         {
470             ScAddress aAdr;
471             PopSingleRef( aAdr );
472             pMat = GetNewMat(1, 1);
473             if (pMat)
474             {
475                 ScRefCellValue aCell(mrDoc, aAdr);
476                 if (aCell.hasEmptyValue())
477                     pMat->PutEmpty(0, 0);
478                 else if (aCell.hasNumeric())
479                     pMat->PutDouble(GetCellValue(aAdr, aCell), 0);
480                 else
481                 {
482                     svl::SharedString aStr;
483                     GetCellString(aStr, aCell);
484                     pMat->PutString(aStr, 0);
485                 }
486             }
487         }
488         break;
489         case svDoubleRef:
490         {
491             SCCOL nCol1, nCol2;
492             SCROW nRow1, nRow2;
493             SCTAB nTab1, nTab2;
494             const formula::FormulaToken* p = sp ? pStack[sp-1] : nullptr;
495             PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
496             pMat = CreateMatrixFromDoubleRef( p, nCol1, nRow1, nTab1,
497                     nCol2, nRow2, nTab2);
498         }
499         break;
500         case svMatrix:
501             pMat = PopMatrix();
502         break;
503         case svError :
504         case svMissing :
505         case svDouble :
506         {
507             double fVal = GetDouble();
508             pMat = GetNewMat( 1, 1);
509             if ( pMat )
510             {
511                 if ( nGlobalError != FormulaError::NONE )
512                 {
513                     fVal = CreateDoubleError( nGlobalError);
514                     nGlobalError = FormulaError::NONE;
515                 }
516                 pMat->PutDouble( fVal, 0);
517             }
518         }
519         break;
520         case svString :
521         {
522             svl::SharedString aStr = GetString();
523             pMat = GetNewMat( 1, 1);
524             if ( pMat )
525             {
526                 if ( nGlobalError != FormulaError::NONE )
527                 {
528                     double fVal = CreateDoubleError( nGlobalError);
529                     pMat->PutDouble( fVal, 0);
530                     nGlobalError = FormulaError::NONE;
531                 }
532                 else
533                     pMat->PutString(aStr, 0);
534             }
535         }
536         break;
537         case svExternalSingleRef:
538         {
539             ScExternalRefCache::TokenRef pToken;
540             PopExternalSingleRef(pToken);
541             pMat = GetNewMat( 1, 1, true);
542             if (!pMat)
543                 break;
544             if (nGlobalError != FormulaError::NONE)
545             {
546                 pMat->PutError( nGlobalError, 0, 0);
547                 nGlobalError = FormulaError::NONE;
548                 break;
549             }
550             switch (pToken->GetType())
551             {
552                 case svError:
553                     pMat->PutError( pToken->GetError(), 0, 0);
554                 break;
555                 case svDouble:
556                     pMat->PutDouble( pToken->GetDouble(), 0, 0);
557                 break;
558                 case svString:
559                     pMat->PutString( pToken->GetString(), 0, 0);
560                 break;
561                 default:
562                     ;   // nothing, empty element matrix
563             }
564         }
565         break;
566         case svExternalDoubleRef:
567             PopExternalDoubleRef(pMat);
568         break;
569         default:
570             PopError();
571             SetError( FormulaError::IllegalArgument);
572         break;
573     }
574     return pMat;
575 }
576 
GetMatrix(short & rParam,size_t & rRefInList)577 ScMatrixRef ScInterpreter::GetMatrix( short & rParam, size_t & rRefInList )
578 {
579     switch (GetRawStackType())
580     {
581         case svRefList:
582             {
583                 ScRange aRange( ScAddress::INITIALIZE_INVALID );
584                 PopDoubleRef( aRange, rParam, rRefInList);
585                 if (nGlobalError != FormulaError::NONE)
586                     return nullptr;
587 
588                 SCCOL nCol1, nCol2;
589                 SCROW nRow1, nRow2;
590                 SCTAB nTab1, nTab2;
591                 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
592                 return CreateMatrixFromDoubleRef( nullptr, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
593             }
594         break;
595         default:
596             return GetMatrix();
597     }
598 }
599 
GetRangeMatrix()600 sc::RangeMatrix ScInterpreter::GetRangeMatrix()
601 {
602     sc::RangeMatrix aRet;
603     switch (GetRawStackType())
604     {
605         case svMatrix:
606             aRet = PopRangeMatrix();
607         break;
608         default:
609             aRet.mpMat = GetMatrix();
610     }
611     return aRet;
612 }
613 
ScMatValue()614 void ScInterpreter::ScMatValue()
615 {
616     if ( !MustHaveParamCount( GetByte(), 3 ) )
617         return;
618 
619     // 0 to count-1
620     // Theoretically we could have GetSize() instead of GetUInt32(), but
621     // really, practically ...
622     SCSIZE nR = static_cast<SCSIZE>(GetUInt32());
623     SCSIZE nC = static_cast<SCSIZE>(GetUInt32());
624     if (nGlobalError != FormulaError::NONE)
625     {
626         PushError( nGlobalError);
627         return;
628     }
629     switch (GetStackType())
630     {
631         case svSingleRef :
632         {
633             ScAddress aAdr;
634             PopSingleRef( aAdr );
635             ScRefCellValue aCell(mrDoc, aAdr);
636             if (aCell.getType() == CELLTYPE_FORMULA)
637             {
638                 FormulaError nErrCode = aCell.getFormula()->GetErrCode();
639                 if (nErrCode != FormulaError::NONE)
640                     PushError( nErrCode);
641                 else
642                 {
643                     const ScMatrix* pMat = aCell.getFormula()->GetMatrix();
644                     CalculateMatrixValue(pMat,nC,nR);
645                 }
646             }
647             else
648                 PushIllegalParameter();
649         }
650         break;
651         case svDoubleRef :
652         {
653             SCCOL nCol1;
654             SCROW nRow1;
655             SCTAB nTab1;
656             SCCOL nCol2;
657             SCROW nRow2;
658             SCTAB nTab2;
659             PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
660             if (nCol2 - nCol1 >= static_cast<SCCOL>(nR) &&
661                     nRow2 - nRow1 >= static_cast<SCROW>(nC) &&
662                     nTab1 == nTab2)
663             {
664                 ScAddress aAdr( sal::static_int_cast<SCCOL>( nCol1 + nR ),
665                                 sal::static_int_cast<SCROW>( nRow1 + nC ), nTab1 );
666                 ScRefCellValue aCell(mrDoc, aAdr);
667                 if (aCell.hasNumeric())
668                     PushDouble(GetCellValue(aAdr, aCell));
669                 else
670                 {
671                     svl::SharedString aStr;
672                     GetCellString(aStr, aCell);
673                     PushString(aStr);
674                 }
675             }
676             else
677                 PushNoValue();
678         }
679         break;
680         case svMatrix:
681         {
682             ScMatrixRef pMat = PopMatrix();
683             CalculateMatrixValue(pMat.get(),nC,nR);
684         }
685         break;
686         default:
687             PopError();
688             PushIllegalParameter();
689         break;
690     }
691 }
CalculateMatrixValue(const ScMatrix * pMat,SCSIZE nC,SCSIZE nR)692 void ScInterpreter::CalculateMatrixValue(const ScMatrix* pMat,SCSIZE nC,SCSIZE nR)
693 {
694     if (pMat)
695     {
696         SCSIZE nCl, nRw;
697         pMat->GetDimensions(nCl, nRw);
698         if (nC < nCl && nR < nRw)
699         {
700             const ScMatrixValue nMatVal = pMat->Get( nC, nR);
701             ScMatValType nMatValType = nMatVal.nType;
702             if (ScMatrix::IsNonValueType( nMatValType))
703                 PushString( nMatVal.GetString() );
704             else
705                 PushDouble(nMatVal.fVal);
706                 // also handles DoubleError
707         }
708         else
709             PushNoValue();
710     }
711     else
712         PushNoValue();
713 }
714 
ScEMat()715 void ScInterpreter::ScEMat()
716 {
717     if ( !MustHaveParamCount( GetByte(), 1 ) )
718         return;
719 
720     SCSIZE nDim = static_cast<SCSIZE>(GetUInt32());
721     if (nGlobalError != FormulaError::NONE || nDim == 0)
722         PushIllegalArgument();
723     else if (!ScMatrix::IsSizeAllocatable( nDim, nDim))
724         PushError( FormulaError::MatrixSize);
725     else
726     {
727         ScMatrixRef pRMat = GetNewMat(nDim, nDim, /*bEmpty*/true);
728         if (pRMat)
729         {
730             MEMat(pRMat, nDim);
731             PushMatrix(pRMat);
732         }
733         else
734             PushIllegalArgument();
735     }
736 }
737 
MEMat(const ScMatrixRef & mM,SCSIZE n)738 void ScInterpreter::MEMat(const ScMatrixRef& mM, SCSIZE n)
739 {
740     mM->FillDouble(0.0, 0, 0, n-1, n-1);
741     for (SCSIZE i = 0; i < n; i++)
742         mM->PutDouble(1.0, i, i);
743 }
744 
745 /* Matrix LUP decomposition according to the pseudocode of "Introduction to
746  * Algorithms" by Cormen, Leiserson, Rivest, Stein.
747  *
748  * Added scaling for numeric stability.
749  *
750  * Given an n x n nonsingular matrix A, find a permutation matrix P, a unit
751  * lower-triangular matrix L, and an upper-triangular matrix U such that PA=LU.
752  * Compute L and U "in place" in the matrix A, the original content is
753  * destroyed. Note that the diagonal elements of the U triangular matrix
754  * replace the diagonal elements of the L-unit matrix (that are each ==1). The
755  * permutation matrix P is an array, where P[i]=j means that the i-th row of P
756  * contains a 1 in column j. Additionally keep track of the number of
757  * permutations (row exchanges).
758  *
759  * Returns 0 if a singular matrix is encountered, else +1 if an even number of
760  * permutations occurred, or -1 if odd, which is the sign of the determinant.
761  * This may be used to calculate the determinant by multiplying the sign with
762  * the product of the diagonal elements of the LU matrix.
763  */
lcl_LUP_decompose(ScMatrix * mA,const SCSIZE n,std::vector<SCSIZE> & P)764 static int lcl_LUP_decompose( ScMatrix* mA, const SCSIZE n,
765         std::vector< SCSIZE> & P )
766 {
767     int nSign = 1;
768     // Find scale of each row.
769     std::vector< double> aScale(n);
770     for (SCSIZE i=0; i < n; ++i)
771     {
772         double fMax = 0.0;
773         for (SCSIZE j=0; j < n; ++j)
774         {
775             double fTmp = fabs( mA->GetDouble( j, i));
776             if (fMax < fTmp)
777                 fMax = fTmp;
778         }
779         if (fMax == 0.0)
780             return 0;       // singular matrix
781         aScale[i] = 1.0 / fMax;
782     }
783     // Represent identity permutation, P[i]=i
784     for (SCSIZE i=0; i < n; ++i)
785         P[i] = i;
786     // "Recursion" on the diagonal.
787     SCSIZE l = n - 1;
788     for (SCSIZE k=0; k < l; ++k)
789     {
790         // Implicit pivoting. With the scale found for a row, compare values of
791         // a column and pick largest.
792         double fMax = 0.0;
793         double fScale = aScale[k];
794         SCSIZE kp = k;
795         for (SCSIZE i = k; i < n; ++i)
796         {
797             double fTmp = fScale * fabs( mA->GetDouble( k, i));
798             if (fMax < fTmp)
799             {
800                 fMax = fTmp;
801                 kp = i;
802             }
803         }
804         if (fMax == 0.0)
805             return 0;       // singular matrix
806         // Swap rows. The pivot element will be at mA[k,kp] (row,col notation)
807         if (k != kp)
808         {
809             // permutations
810             SCSIZE nTmp = P[k];
811             P[k]        = P[kp];
812             P[kp]       = nTmp;
813             nSign       = -nSign;
814             // scales
815             double fTmp = aScale[k];
816             aScale[k]   = aScale[kp];
817             aScale[kp]  = fTmp;
818             // elements
819             for (SCSIZE i=0; i < n; ++i)
820             {
821                 double fMatTmp = mA->GetDouble( i, k);
822                 mA->PutDouble( mA->GetDouble( i, kp), i, k);
823                 mA->PutDouble( fMatTmp, i, kp);
824             }
825         }
826         // Compute Schur complement.
827         for (SCSIZE i = k+1; i < n; ++i)
828         {
829             double fNum = mA->GetDouble( k, i);
830             double fDen = mA->GetDouble( k, k);
831             mA->PutDouble( fNum/fDen, k, i);
832             for (SCSIZE j = k+1; j < n; ++j)
833                 mA->PutDouble( ( mA->GetDouble( j, i) * fDen  -
834                             fNum * mA->GetDouble( j, k) ) / fDen, j, i);
835         }
836     }
837 #ifdef DEBUG_SC_LUP_DECOMPOSITION
838     fprintf( stderr, "\n%s\n", "lcl_LUP_decompose(): LU");
839     for (SCSIZE i=0; i < n; ++i)
840     {
841         for (SCSIZE j=0; j < n; ++j)
842             fprintf( stderr, "%8.2g  ", mA->GetDouble( j, i));
843         fprintf( stderr, "\n%s\n", "");
844     }
845     fprintf( stderr, "\n%s\n", "lcl_LUP_decompose(): P");
846     for (SCSIZE j=0; j < n; ++j)
847         fprintf( stderr, "%5u ", (unsigned)P[j]);
848     fprintf( stderr, "\n%s\n", "");
849 #endif
850 
851     bool bSingular=false;
852     for (SCSIZE i=0; i<n && !bSingular; i++)
853         bSingular = (mA->GetDouble(i,i)) == 0.0;
854     if (bSingular)
855         nSign = 0;
856 
857     return nSign;
858 }
859 
860 /* Solve a LUP decomposed equation Ax=b. LU is a combined matrix of L and U
861  * triangulars and P the permutation vector as obtained from
862  * lcl_LUP_decompose(). B is the right-hand side input vector, X is used to
863  * return the solution vector.
864  */
lcl_LUP_solve(const ScMatrix * mLU,const SCSIZE n,const std::vector<SCSIZE> & P,const std::vector<double> & B,std::vector<double> & X)865 static void lcl_LUP_solve( const ScMatrix* mLU, const SCSIZE n,
866         const std::vector< SCSIZE> & P, const std::vector< double> & B,
867         std::vector< double> & X )
868 {
869     SCSIZE nFirst = SCSIZE_MAX;
870     // Ax=b => PAx=Pb, with decomposition LUx=Pb.
871     // Define y=Ux and solve for y in Ly=Pb using forward substitution.
872     for (SCSIZE i=0; i < n; ++i)
873     {
874         KahanSum fSum = B[P[i]];
875         // Matrix inversion comes with a lot of zeros in the B vectors, we
876         // don't have to do all the computing with results multiplied by zero.
877         // Until then, simply lookout for the position of the first nonzero
878         // value.
879         if (nFirst != SCSIZE_MAX)
880         {
881             for (SCSIZE j = nFirst; j < i; ++j)
882                 fSum -= mLU->GetDouble( j, i) * X[j];         // X[j] === y[j]
883         }
884         else if (fSum != 0)
885             nFirst = i;
886         X[i] = fSum.get();                                    // X[i] === y[i]
887     }
888     // Solve for x in Ux=y using back substitution.
889     for (SCSIZE i = n; i--; )
890     {
891         KahanSum fSum = X[i];                                 // X[i] === y[i]
892         for (SCSIZE j = i+1; j < n; ++j)
893             fSum -= mLU->GetDouble( j, i) * X[j];             // X[j] === x[j]
894         X[i] = fSum.get() / mLU->GetDouble( i, i);            // X[i] === x[i]
895     }
896 #ifdef DEBUG_SC_LUP_DECOMPOSITION
897     fprintf( stderr, "\n%s\n", "lcl_LUP_solve():");
898     for (SCSIZE i=0; i < n; ++i)
899         fprintf( stderr, "%8.2g  ", X[i]);
900     fprintf( stderr, "%s\n", "");
901 #endif
902 }
903 
ScMatDet()904 void ScInterpreter::ScMatDet()
905 {
906     if ( !MustHaveParamCount( GetByte(), 1 ) )
907         return;
908 
909     ScMatrixRef pMat = GetMatrix();
910     if (!pMat)
911     {
912         PushIllegalParameter();
913         return;
914     }
915     if ( !pMat->IsNumeric() )
916     {
917         PushNoValue();
918         return;
919     }
920     SCSIZE nC, nR;
921     pMat->GetDimensions(nC, nR);
922     if ( nC != nR || nC == 0 )
923         PushIllegalArgument();
924     else if (!ScMatrix::IsSizeAllocatable( nC, nR))
925         PushError( FormulaError::MatrixSize);
926     else
927     {
928         // LUP decomposition is done inplace, use copy.
929         ScMatrixRef xLU = pMat->Clone();
930         if (!xLU)
931             PushError( FormulaError::CodeOverflow);
932         else
933         {
934             std::vector< SCSIZE> P(nR);
935             int nDetSign = lcl_LUP_decompose( xLU.get(), nR, P);
936             if (!nDetSign)
937                 PushInt(0);     // singular matrix
938             else
939             {
940                 // In an LU matrix the determinant is simply the product of
941                 // all diagonal elements.
942                 double fDet = nDetSign;
943                 for (SCSIZE i=0; i < nR; ++i)
944                     fDet *= xLU->GetDouble( i, i);
945                 PushDouble( fDet);
946             }
947         }
948     }
949 }
950 
ScMatInv()951 void ScInterpreter::ScMatInv()
952 {
953     if ( !MustHaveParamCount( GetByte(), 1 ) )
954         return;
955 
956     ScMatrixRef pMat = GetMatrix();
957     if (!pMat)
958     {
959         PushIllegalParameter();
960         return;
961     }
962     if ( !pMat->IsNumeric() )
963     {
964         PushNoValue();
965         return;
966     }
967     SCSIZE nC, nR;
968     pMat->GetDimensions(nC, nR);
969 
970     if (ScCalcConfig::isOpenCLEnabled())
971     {
972         sc::FormulaGroupInterpreter *pInterpreter = sc::FormulaGroupInterpreter::getStatic();
973         if (pInterpreter != nullptr)
974         {
975             ScMatrixRef xResMat = pInterpreter->inverseMatrix(*pMat);
976             if (xResMat)
977             {
978                 PushMatrix(xResMat);
979                 return;
980             }
981         }
982     }
983 
984     if ( nC != nR || nC == 0 )
985         PushIllegalArgument();
986     else if (!ScMatrix::IsSizeAllocatable( nC, nR))
987         PushError( FormulaError::MatrixSize);
988     else
989     {
990         // LUP decomposition is done inplace, use copy.
991         ScMatrixRef xLU = pMat->Clone();
992         // The result matrix.
993         ScMatrixRef xY = GetNewMat( nR, nR, /*bEmpty*/true );
994         if (!xLU || !xY)
995             PushError( FormulaError::CodeOverflow);
996         else
997         {
998             std::vector< SCSIZE> P(nR);
999             int nDetSign = lcl_LUP_decompose( xLU.get(), nR, P);
1000             if (!nDetSign)
1001                 PushIllegalArgument();
1002             else
1003             {
1004                 // Solve equation for each column.
1005                 std::vector< double> B(nR);
1006                 std::vector< double> X(nR);
1007                 for (SCSIZE j=0; j < nR; ++j)
1008                 {
1009                     for (SCSIZE i=0; i < nR; ++i)
1010                         B[i] = 0.0;
1011                     B[j] = 1.0;
1012                     lcl_LUP_solve( xLU.get(), nR, P, B, X);
1013                     for (SCSIZE i=0; i < nR; ++i)
1014                         xY->PutDouble( X[i], j, i);
1015                 }
1016 #ifdef DEBUG_SC_LUP_DECOMPOSITION
1017                 /* Possible checks for ill-condition:
1018                  * 1. Scale matrix, invert scaled matrix. If there are
1019                  *    elements of the inverted matrix that are several
1020                  *    orders of magnitude greater than 1 =>
1021                  *    ill-conditioned.
1022                  *    Just how much is "several orders"?
1023                  * 2. Invert the inverted matrix and assess whether the
1024                  *    result is sufficiently close to the original matrix.
1025                  *    If not => ill-conditioned.
1026                  *    Just what is sufficient?
1027                  * 3. Multiplying the inverse by the original matrix should
1028                  *    produce a result sufficiently close to the identity
1029                  *    matrix.
1030                  *    Just what is sufficient?
1031                  *
1032                  * The following is #3.
1033                  */
1034                 const double fInvEpsilon = 1.0E-7;
1035                 ScMatrixRef xR = GetNewMat( nR, nR);
1036                 if (xR)
1037                 {
1038                     ScMatrix* pR = xR.get();
1039                     lcl_MFastMult( pMat, xY.get(), pR, nR, nR, nR);
1040                     fprintf( stderr, "\n%s\n", "ScMatInv(): mult-identity");
1041                     for (SCSIZE i=0; i < nR; ++i)
1042                     {
1043                         for (SCSIZE j=0; j < nR; ++j)
1044                         {
1045                             double fTmp = pR->GetDouble( j, i);
1046                             fprintf( stderr, "%8.2g  ", fTmp);
1047                             if (fabs( fTmp - (i == j)) > fInvEpsilon)
1048                                 SetError( FormulaError::IllegalArgument);
1049                         }
1050                     fprintf( stderr, "\n%s\n", "");
1051                     }
1052                 }
1053 #endif
1054                 if (nGlobalError != FormulaError::NONE)
1055                     PushError( nGlobalError);
1056                 else
1057                     PushMatrix( xY);
1058             }
1059         }
1060     }
1061 }
1062 
ScMatMult()1063 void ScInterpreter::ScMatMult()
1064 {
1065     if ( !MustHaveParamCount( GetByte(), 2 ) )
1066         return;
1067 
1068     ScMatrixRef pMat2 = GetMatrix();
1069     ScMatrixRef pMat1 = GetMatrix();
1070     ScMatrixRef pRMat;
1071     if (pMat1 && pMat2)
1072     {
1073         if ( pMat1->IsNumeric() && pMat2->IsNumeric() )
1074         {
1075             SCSIZE nC1, nC2;
1076             SCSIZE nR1, nR2;
1077             pMat1->GetDimensions(nC1, nR1);
1078             pMat2->GetDimensions(nC2, nR2);
1079             if (nC1 != nR2)
1080                 PushIllegalArgument();
1081             else
1082             {
1083                 pRMat = GetNewMat(nC2, nR1, /*bEmpty*/true);
1084                 if (pRMat)
1085                 {
1086                     KahanSum fSum;
1087                     for (SCSIZE i = 0; i < nR1; i++)
1088                     {
1089                         for (SCSIZE j = 0; j < nC2; j++)
1090                         {
1091                             fSum = 0.0;
1092                             for (SCSIZE k = 0; k < nC1; k++)
1093                             {
1094                                 fSum += pMat1->GetDouble(k,i)*pMat2->GetDouble(j,k);
1095                             }
1096                             pRMat->PutDouble(fSum.get(), j, i);
1097                         }
1098                     }
1099                     PushMatrix(pRMat);
1100                 }
1101                 else
1102                     PushIllegalArgument();
1103             }
1104         }
1105         else
1106             PushNoValue();
1107     }
1108     else
1109         PushIllegalParameter();
1110 }
1111 
ScMatSequence()1112 void ScInterpreter::ScMatSequence()
1113 {
1114     sal_uInt8 nParamCount = GetByte();
1115     if (!MustHaveParamCount(nParamCount, 1, 4))
1116         return;
1117 
1118     // 4th argument is the step number (optional)
1119     double nSteps = 1.0;
1120     if (nParamCount == 4)
1121         nSteps = GetDoubleWithDefault(nSteps);
1122 
1123     // 3d argument is the start number (optional)
1124     double nStart = 1.0;
1125     if (nParamCount >= 3)
1126         nStart = GetDoubleWithDefault(nStart);
1127 
1128     // 2nd argument is the col number (optional)
1129     sal_Int32 nColumns = 1;
1130     if (nParamCount >= 2)
1131     {
1132         nColumns = GetInt32WithDefault(nColumns);
1133         if (nColumns < 1)
1134         {
1135             PushIllegalArgument();
1136             return;
1137         }
1138     }
1139 
1140     // 1st argument is the row number (required)
1141     sal_Int32 nRows = GetInt32WithDefault(1);
1142     if (nRows < 1)
1143     {
1144         PushIllegalArgument();
1145         return;
1146     }
1147 
1148     if (nGlobalError != FormulaError::NONE)
1149     {
1150         PushError(nGlobalError);
1151         return;
1152     }
1153 
1154     size_t nMatrixSize = static_cast<size_t>(nColumns) * nRows;
1155     ScMatrixRef pResMat = GetNewMat(nColumns, nRows, /*bEmpty*/true);
1156     for (size_t iPos = 0; iPos < nMatrixSize; iPos++)
1157     {
1158         pResMat->PutDoubleTrans(nStart, iPos);
1159         nStart = nStart + nSteps;
1160     }
1161 
1162     if (pResMat)
1163     {
1164         PushMatrix(pResMat);
1165     }
1166     else
1167     {
1168         PushIllegalParameter();
1169         return;
1170     }
1171 }
1172 
ScMatTrans()1173 void ScInterpreter::ScMatTrans()
1174 {
1175     if ( !MustHaveParamCount( GetByte(), 1 ) )
1176         return;
1177 
1178     ScMatrixRef pMat = GetMatrix();
1179     ScMatrixRef pRMat;
1180     if (pMat)
1181     {
1182         SCSIZE nC, nR;
1183         pMat->GetDimensions(nC, nR);
1184         pRMat = GetNewMat(nR, nC, /*bEmpty*/true);
1185         if ( pRMat )
1186         {
1187             pMat->MatTrans(*pRMat);
1188             PushMatrix(pRMat);
1189         }
1190         else
1191             PushIllegalArgument();
1192     }
1193     else
1194         PushIllegalParameter();
1195 }
1196 
1197 /** Minimum extent of one result matrix dimension.
1198     For a row or column vector to be replicated the larger matrix dimension is
1199     returned, else the smaller dimension.
1200  */
lcl_GetMinExtent(SCSIZE n1,SCSIZE n2)1201 static SCSIZE lcl_GetMinExtent( SCSIZE n1, SCSIZE n2 )
1202 {
1203     if (n1 == 1)
1204         return n2;
1205     else if (n2 == 1)
1206         return n1;
1207     else if (n1 < n2)
1208         return n1;
1209     else
1210         return n2;
1211 }
1212 
lcl_MatrixCalculation(const ScMatrix & rMat1,const ScMatrix & rMat2,ScInterpreter * pInterpreter,const ScMatrix::CalculateOpFunction & Op)1213 static ScMatrixRef lcl_MatrixCalculation(
1214     const ScMatrix& rMat1, const ScMatrix& rMat2, ScInterpreter* pInterpreter, const ScMatrix::CalculateOpFunction& Op)
1215 {
1216     SCSIZE nC1, nC2, nMinC;
1217     SCSIZE nR1, nR2, nMinR;
1218     rMat1.GetDimensions(nC1, nR1);
1219     rMat2.GetDimensions(nC2, nR2);
1220     nMinC = lcl_GetMinExtent( nC1, nC2);
1221     nMinR = lcl_GetMinExtent( nR1, nR2);
1222     ScMatrixRef xResMat = pInterpreter->GetNewMat(nMinC, nMinR, /*bEmpty*/true);
1223     if (xResMat)
1224         xResMat->ExecuteBinaryOp(nMinC, nMinR, rMat1, rMat2, pInterpreter, Op);
1225     return xResMat;
1226 }
1227 
MatConcat(const ScMatrixRef & pMat1,const ScMatrixRef & pMat2)1228 ScMatrixRef ScInterpreter::MatConcat(const ScMatrixRef& pMat1, const ScMatrixRef& pMat2)
1229 {
1230     SCSIZE nC1, nC2, nMinC;
1231     SCSIZE nR1, nR2, nMinR;
1232     pMat1->GetDimensions(nC1, nR1);
1233     pMat2->GetDimensions(nC2, nR2);
1234     nMinC = lcl_GetMinExtent( nC1, nC2);
1235     nMinR = lcl_GetMinExtent( nR1, nR2);
1236     ScMatrixRef xResMat = GetNewMat(nMinC, nMinR, /*bEmpty*/true);
1237     if (xResMat)
1238     {
1239         xResMat->MatConcat(nMinC, nMinR, pMat1, pMat2, mrContext, mrDoc.GetSharedStringPool());
1240     }
1241     return xResMat;
1242 }
1243 
1244 // for DATE, TIME, DATETIME, DURATION
lcl_GetDiffDateTimeFmtType(SvNumFormatType & nFuncFmt,SvNumFormatType nFmt1,SvNumFormatType nFmt2)1245 static void lcl_GetDiffDateTimeFmtType( SvNumFormatType& nFuncFmt, SvNumFormatType nFmt1, SvNumFormatType nFmt2 )
1246 {
1247     if ( nFmt1 == SvNumFormatType::UNDEFINED && nFmt2 == SvNumFormatType::UNDEFINED )
1248         return;
1249 
1250     if ( nFmt1 == nFmt2 )
1251     {
1252         if ( nFmt1 == SvNumFormatType::TIME || nFmt1 == SvNumFormatType::DATETIME
1253                 || nFmt1 == SvNumFormatType::DURATION )
1254             nFuncFmt = SvNumFormatType::DURATION;   // times result in time duration
1255         // else: nothing special, number (date - date := days)
1256     }
1257     else if ( nFmt1 == SvNumFormatType::UNDEFINED )
1258         nFuncFmt = nFmt2;   // e.g. date + days := date
1259     else if ( nFmt2 == SvNumFormatType::UNDEFINED )
1260         nFuncFmt = nFmt1;
1261     else
1262     {
1263         if ( nFmt1 == SvNumFormatType::DATE || nFmt2 == SvNumFormatType::DATE ||
1264             nFmt1 == SvNumFormatType::DATETIME || nFmt2 == SvNumFormatType::DATETIME )
1265         {
1266             if ( nFmt1 == SvNumFormatType::TIME || nFmt2 == SvNumFormatType::TIME )
1267                 nFuncFmt = SvNumFormatType::DATETIME;   // date + time
1268         }
1269     }
1270 }
1271 
ScAdd()1272 void ScInterpreter::ScAdd()
1273 {
1274     CalculateAddSub(false);
1275 }
1276 
CalculateAddSub(bool _bSub)1277 void ScInterpreter::CalculateAddSub(bool _bSub)
1278 {
1279     ScMatrixRef pMat1 = nullptr;
1280     ScMatrixRef pMat2 = nullptr;
1281     double fVal1 = 0.0, fVal2 = 0.0;
1282     SvNumFormatType nFmt1, nFmt2;
1283     nFmt1 = nFmt2 = SvNumFormatType::UNDEFINED;
1284     bool bDuration = false;
1285     SvNumFormatType nFmtCurrencyType = nCurFmtType;
1286     sal_uLong nFmtCurrencyIndex = nCurFmtIndex;
1287     SvNumFormatType nFmtPercentType = nCurFmtType;
1288     if ( GetStackType() == svMatrix )
1289         pMat2 = GetMatrix();
1290     else
1291     {
1292         fVal2 = GetDouble();
1293         switch ( nCurFmtType )
1294         {
1295             case SvNumFormatType::DATE :
1296             case SvNumFormatType::TIME :
1297             case SvNumFormatType::DATETIME :
1298             case SvNumFormatType::DURATION :
1299                 nFmt2 = nCurFmtType;
1300                 bDuration = true;
1301             break;
1302             case SvNumFormatType::CURRENCY :
1303                 nFmtCurrencyType = nCurFmtType;
1304                 nFmtCurrencyIndex = nCurFmtIndex;
1305             break;
1306             case SvNumFormatType::PERCENT :
1307                 nFmtPercentType = SvNumFormatType::PERCENT;
1308             break;
1309             default: break;
1310         }
1311     }
1312     if ( GetStackType() == svMatrix )
1313         pMat1 = GetMatrix();
1314     else
1315     {
1316         fVal1 = GetDouble();
1317         switch ( nCurFmtType )
1318         {
1319             case SvNumFormatType::DATE :
1320             case SvNumFormatType::TIME :
1321             case SvNumFormatType::DATETIME :
1322             case SvNumFormatType::DURATION :
1323                 nFmt1 = nCurFmtType;
1324                 bDuration = true;
1325             break;
1326             case SvNumFormatType::CURRENCY :
1327                 nFmtCurrencyType = nCurFmtType;
1328                 nFmtCurrencyIndex = nCurFmtIndex;
1329             break;
1330             case SvNumFormatType::PERCENT :
1331                 nFmtPercentType = SvNumFormatType::PERCENT;
1332             break;
1333             default: break;
1334         }
1335     }
1336     if (pMat1 && pMat2)
1337     {
1338         ScMatrixRef pResMat;
1339         if ( _bSub )
1340         {
1341             pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixSub);
1342         }
1343         else
1344         {
1345             pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixAdd);
1346         }
1347 
1348         if (!pResMat)
1349             PushNoValue();
1350         else
1351             PushMatrix(pResMat);
1352     }
1353     else if (pMat1 || pMat2)
1354     {
1355         double fVal;
1356         bool bFlag;
1357         ScMatrixRef pMat = std::move(pMat1);
1358         if (!pMat)
1359         {
1360             fVal = fVal1;
1361             pMat = std::move(pMat2);
1362             bFlag = true;           // double - Matrix
1363         }
1364         else
1365         {
1366             fVal = fVal2;
1367             bFlag = false;          // Matrix - double
1368         }
1369         SCSIZE nC, nR;
1370         pMat->GetDimensions(nC, nR);
1371         ScMatrixRef pResMat = GetNewMat(nC, nR, true);
1372         if (pResMat)
1373         {
1374             if (_bSub)
1375             {
1376                 pMat->SubOp( bFlag, fVal, *pResMat);
1377             }
1378             else
1379             {
1380                 pMat->AddOp( fVal, *pResMat);
1381             }
1382             PushMatrix(pResMat);
1383         }
1384         else
1385             PushIllegalArgument();
1386     }
1387     else
1388     {
1389         // Determine nFuncFmtType type before PushDouble().
1390         if ( nFmtCurrencyType == SvNumFormatType::CURRENCY )
1391         {
1392             nFuncFmtType = nFmtCurrencyType;
1393             nFuncFmtIndex = nFmtCurrencyIndex;
1394         }
1395         else
1396         {
1397             lcl_GetDiffDateTimeFmtType( nFuncFmtType, nFmt1, nFmt2 );
1398             if (nFmtPercentType == SvNumFormatType::PERCENT && nFuncFmtType == SvNumFormatType::NUMBER)
1399                 nFuncFmtType = SvNumFormatType::PERCENT;
1400         }
1401         if ((nFuncFmtType == SvNumFormatType::DURATION || bDuration)
1402                 && ((_bSub && std::fabs(fVal1 - fVal2) <= SAL_MAX_INT32)
1403                     || (!_bSub && std::fabs(fVal1 + fVal2) <= SAL_MAX_INT32)))
1404         {
1405             // Limit to microseconds resolution on date inflicted or duration
1406             // values of 24 hours or more.
1407             const sal_uInt64 nEpsilon = ((std::fabs(fVal1) >= 1.0 || std::fabs(fVal2) >= 1.0) ?
1408                     ::tools::Duration::kAccuracyEpsilonNanosecondsMicroseconds :
1409                     ::tools::Duration::kAccuracyEpsilonNanoseconds);
1410             if (_bSub)
1411                 PushDouble( ::tools::Duration( fVal1 - fVal2, nEpsilon).GetInDays());
1412             else
1413                 PushDouble( ::tools::Duration( fVal1 + fVal2, nEpsilon).GetInDays());
1414         }
1415         else
1416         {
1417             if (_bSub)
1418                 PushDouble( ::rtl::math::approxSub( fVal1, fVal2 ) );
1419             else
1420                 PushDouble( ::rtl::math::approxAdd( fVal1, fVal2 ) );
1421         }
1422     }
1423 }
1424 
ScAmpersand()1425 void ScInterpreter::ScAmpersand()
1426 {
1427     ScMatrixRef pMat1 = nullptr;
1428     ScMatrixRef pMat2 = nullptr;
1429     OUString sStr1, sStr2;
1430     if ( GetStackType() == svMatrix )
1431         pMat2 = GetMatrix();
1432     else
1433         sStr2 = GetString().getString();
1434     if ( GetStackType() == svMatrix )
1435         pMat1 = GetMatrix();
1436     else
1437         sStr1 = GetString().getString();
1438     if (pMat1 && pMat2)
1439     {
1440         ScMatrixRef pResMat = MatConcat(pMat1, pMat2);
1441         if (!pResMat)
1442             PushNoValue();
1443         else
1444             PushMatrix(pResMat);
1445     }
1446     else if (pMat1 || pMat2)
1447     {
1448         OUString sStr;
1449         bool bFlag;
1450         ScMatrixRef pMat = std::move(pMat1);
1451         if (!pMat)
1452         {
1453             sStr = sStr1;
1454             pMat = std::move(pMat2);
1455             bFlag = true;           // double - Matrix
1456         }
1457         else
1458         {
1459             sStr = sStr2;
1460             bFlag = false;          // Matrix - double
1461         }
1462         SCSIZE nC, nR;
1463         pMat->GetDimensions(nC, nR);
1464         ScMatrixRef pResMat = GetNewMat(nC, nR, /*bEmpty*/true);
1465         if (pResMat)
1466         {
1467             if (nGlobalError != FormulaError::NONE)
1468             {
1469                 for (SCSIZE i = 0; i < nC; ++i)
1470                     for (SCSIZE j = 0; j < nR; ++j)
1471                         pResMat->PutError( nGlobalError, i, j);
1472             }
1473             else if (bFlag)
1474             {
1475                 for (SCSIZE i = 0; i < nC; ++i)
1476                     for (SCSIZE j = 0; j < nR; ++j)
1477                     {
1478                         FormulaError nErr = pMat->GetErrorIfNotString( i, j);
1479                         if (nErr != FormulaError::NONE)
1480                             pResMat->PutError( nErr, i, j);
1481                         else
1482                         {
1483                             OUString aTmp = sStr + pMat->GetString(mrContext, i, j).getString();
1484                             pResMat->PutString(mrStrPool.intern(aTmp), i, j);
1485                         }
1486                     }
1487             }
1488             else
1489             {
1490                 for (SCSIZE i = 0; i < nC; ++i)
1491                     for (SCSIZE j = 0; j < nR; ++j)
1492                     {
1493                         FormulaError nErr = pMat->GetErrorIfNotString( i, j);
1494                         if (nErr != FormulaError::NONE)
1495                             pResMat->PutError( nErr, i, j);
1496                         else
1497                         {
1498                             OUString aTmp = pMat->GetString(mrContext, i, j).getString() + sStr;
1499                             pResMat->PutString(mrStrPool.intern(aTmp), i, j);
1500                         }
1501                     }
1502             }
1503             PushMatrix(pResMat);
1504         }
1505         else
1506             PushIllegalArgument();
1507     }
1508     else
1509     {
1510         if ( CheckStringResultLen( sStr1, sStr2.getLength() ) )
1511             sStr1 += sStr2;
1512         PushString(sStr1);
1513     }
1514 }
1515 
ScSub()1516 void ScInterpreter::ScSub()
1517 {
1518     CalculateAddSub(true);
1519 }
1520 
ScMul()1521 void ScInterpreter::ScMul()
1522 {
1523     ScMatrixRef pMat1 = nullptr;
1524     ScMatrixRef pMat2 = nullptr;
1525     double fVal1 = 0.0, fVal2 = 0.0;
1526     SvNumFormatType nFmtCurrencyType = nCurFmtType;
1527     sal_uLong nFmtCurrencyIndex = nCurFmtIndex;
1528     if ( GetStackType() == svMatrix )
1529         pMat2 = GetMatrix();
1530     else
1531     {
1532         fVal2 = GetDouble();
1533         switch ( nCurFmtType )
1534         {
1535             case SvNumFormatType::CURRENCY :
1536                 nFmtCurrencyType = nCurFmtType;
1537                 nFmtCurrencyIndex = nCurFmtIndex;
1538             break;
1539             default: break;
1540         }
1541     }
1542     if ( GetStackType() == svMatrix )
1543         pMat1 = GetMatrix();
1544     else
1545     {
1546         fVal1 = GetDouble();
1547         switch ( nCurFmtType )
1548         {
1549             case SvNumFormatType::CURRENCY :
1550                 nFmtCurrencyType = nCurFmtType;
1551                 nFmtCurrencyIndex = nCurFmtIndex;
1552             break;
1553             default: break;
1554         }
1555     }
1556     if (pMat1 && pMat2)
1557     {
1558         ScMatrixRef pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixMul);
1559         if (!pResMat)
1560             PushNoValue();
1561         else
1562             PushMatrix(pResMat);
1563     }
1564     else if (pMat1 || pMat2)
1565     {
1566         double fVal;
1567         ScMatrixRef pMat = std::move(pMat1);
1568         if (!pMat)
1569         {
1570             fVal = fVal1;
1571             pMat = std::move(pMat2);
1572         }
1573         else
1574             fVal = fVal2;
1575         SCSIZE nC, nR;
1576         pMat->GetDimensions(nC, nR);
1577         ScMatrixRef pResMat = GetNewMat(nC, nR, /*bEmpty*/true);
1578         if (pResMat)
1579         {
1580             pMat->MulOp( fVal, *pResMat);
1581             PushMatrix(pResMat);
1582         }
1583         else
1584             PushIllegalArgument();
1585     }
1586     else
1587     {
1588         // Determine nFuncFmtType type before PushDouble().
1589         if ( nFmtCurrencyType == SvNumFormatType::CURRENCY )
1590         {
1591             nFuncFmtType = nFmtCurrencyType;
1592             nFuncFmtIndex = nFmtCurrencyIndex;
1593         }
1594         PushDouble(fVal1 * fVal2);
1595     }
1596 }
1597 
ScDiv()1598 void ScInterpreter::ScDiv()
1599 {
1600     ScMatrixRef pMat1 = nullptr;
1601     ScMatrixRef pMat2 = nullptr;
1602     double fVal1 = 0.0, fVal2 = 0.0;
1603     SvNumFormatType nFmtCurrencyType = nCurFmtType;
1604     sal_uLong nFmtCurrencyIndex = nCurFmtIndex;
1605     SvNumFormatType nFmtCurrencyType2 = SvNumFormatType::UNDEFINED;
1606     if ( GetStackType() == svMatrix )
1607         pMat2 = GetMatrix();
1608     else
1609     {
1610         fVal2 = GetDouble();
1611         // do not take over currency, 123kg/456USD is not USD
1612         nFmtCurrencyType2 = nCurFmtType;
1613     }
1614     if ( GetStackType() == svMatrix )
1615         pMat1 = GetMatrix();
1616     else
1617     {
1618         fVal1 = GetDouble();
1619         switch ( nCurFmtType )
1620         {
1621             case SvNumFormatType::CURRENCY :
1622                 nFmtCurrencyType = nCurFmtType;
1623                 nFmtCurrencyIndex = nCurFmtIndex;
1624             break;
1625             default: break;
1626         }
1627     }
1628     if (pMat1 && pMat2)
1629     {
1630         ScMatrixRef pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixDiv);
1631         if (!pResMat)
1632             PushNoValue();
1633         else
1634             PushMatrix(pResMat);
1635     }
1636     else if (pMat1 || pMat2)
1637     {
1638         double fVal;
1639         bool bFlag;
1640         ScMatrixRef pMat = std::move(pMat1);
1641         if (!pMat)
1642         {
1643             fVal = fVal1;
1644             pMat = std::move(pMat2);
1645             bFlag = true;           // double - Matrix
1646         }
1647         else
1648         {
1649             fVal = fVal2;
1650             bFlag = false;          // Matrix - double
1651         }
1652         SCSIZE nC, nR;
1653         pMat->GetDimensions(nC, nR);
1654         ScMatrixRef pResMat = GetNewMat(nC, nR, /*bEmpty*/true);
1655         if (pResMat)
1656         {
1657             pMat->DivOp( bFlag, fVal, *pResMat);
1658             PushMatrix(pResMat);
1659         }
1660         else
1661             PushIllegalArgument();
1662     }
1663     else
1664     {
1665         // Determine nFuncFmtType type before PushDouble().
1666         if (    nFmtCurrencyType  == SvNumFormatType::CURRENCY &&
1667                 nFmtCurrencyType2 != SvNumFormatType::CURRENCY)
1668         {   // even USD/USD is not USD
1669             nFuncFmtType = nFmtCurrencyType;
1670             nFuncFmtIndex = nFmtCurrencyIndex;
1671         }
1672         PushDouble( div( fVal1, fVal2) );
1673     }
1674 }
1675 
ScPower()1676 void ScInterpreter::ScPower()
1677 {
1678     if ( MustHaveParamCount( GetByte(), 2 ) )
1679         ScPow();
1680 }
1681 
ScPow()1682 void ScInterpreter::ScPow()
1683 {
1684     ScMatrixRef pMat1 = nullptr;
1685     ScMatrixRef pMat2 = nullptr;
1686     double fVal1 = 0.0, fVal2 = 0.0;
1687     if ( GetStackType() == svMatrix )
1688         pMat2 = GetMatrix();
1689     else
1690         fVal2 = GetDouble();
1691     if ( GetStackType() == svMatrix )
1692         pMat1 = GetMatrix();
1693     else
1694         fVal1 = GetDouble();
1695     if (pMat1 && pMat2)
1696     {
1697         ScMatrixRef pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixPow);
1698         if (!pResMat)
1699             PushNoValue();
1700         else
1701             PushMatrix(pResMat);
1702     }
1703     else if (pMat1 || pMat2)
1704     {
1705         double fVal;
1706         bool bFlag;
1707         ScMatrixRef pMat = std::move(pMat1);
1708         if (!pMat)
1709         {
1710             fVal = fVal1;
1711             pMat = std::move(pMat2);
1712             bFlag = true;           // double - Matrix
1713         }
1714         else
1715         {
1716             fVal = fVal2;
1717             bFlag = false;          // Matrix - double
1718         }
1719         SCSIZE nC, nR;
1720         pMat->GetDimensions(nC, nR);
1721         ScMatrixRef pResMat = GetNewMat(nC, nR, /*bEmpty*/true);
1722         if (pResMat)
1723         {
1724             pMat->PowOp( bFlag, fVal, *pResMat);
1725             PushMatrix(pResMat);
1726         }
1727         else
1728             PushIllegalArgument();
1729     }
1730     else
1731     {
1732         PushDouble( sc::power( fVal1, fVal2));
1733     }
1734 }
1735 
ScSumProduct()1736 void ScInterpreter::ScSumProduct()
1737 {
1738     short nParamCount = GetByte();
1739     if ( !MustHaveParamCountMin( nParamCount, 1) )
1740         return;
1741 
1742     // XXX NOTE: Excel returns #VALUE! for reference list and 0 (why?) for
1743     // array of references. We calculate the proper individual arrays if sizes
1744     // match.
1745 
1746     size_t nInRefList = 0;
1747     ScMatrixRef pMatLast;
1748     ScMatrixRef pMat;
1749 
1750     pMatLast = GetMatrix( --nParamCount, nInRefList);
1751     if (!pMatLast)
1752     {
1753         PushIllegalParameter();
1754         return;
1755     }
1756 
1757     SCSIZE nC, nCLast, nR, nRLast;
1758     pMatLast->GetDimensions(nCLast, nRLast);
1759     std::vector<double> aResArray;
1760     pMatLast->GetDoubleArray(aResArray);
1761 
1762     while (nParamCount--)
1763     {
1764         pMat = GetMatrix( nParamCount, nInRefList);
1765         if (!pMat)
1766         {
1767             PushIllegalParameter();
1768             return;
1769         }
1770         pMat->GetDimensions(nC, nR);
1771         if (nC != nCLast || nR != nRLast)
1772         {
1773             PushNoValue();
1774             return;
1775         }
1776 
1777         pMat->MergeDoubleArrayMultiply(aResArray);
1778     }
1779 
1780     KahanSum fSum = 0.0;
1781     for( double fPosArray : aResArray )
1782     {
1783         FormulaError nErr = GetDoubleErrorValue(fPosArray);
1784         if (nErr == FormulaError::NONE)
1785             fSum += fPosArray;
1786         else if (nErr != FormulaError::ElementNaN)
1787         {
1788             // Propagate the first error encountered, ignore "this is not a number" elements.
1789             PushError(nErr);
1790             return;
1791         }
1792     }
1793 
1794     PushDouble(fSum.get());
1795 }
1796 
ScSumX2MY2()1797 void ScInterpreter::ScSumX2MY2()
1798 {
1799     CalculateSumX2MY2SumX2DY2(false);
1800 }
CalculateSumX2MY2SumX2DY2(bool _bSumX2DY2)1801 void ScInterpreter::CalculateSumX2MY2SumX2DY2(bool _bSumX2DY2)
1802 {
1803     if ( !MustHaveParamCount( GetByte(), 2 ) )
1804         return;
1805 
1806     ScMatrixRef pMat1 = nullptr;
1807     ScMatrixRef pMat2 = nullptr;
1808     SCSIZE i, j;
1809     pMat2 = GetMatrix();
1810     pMat1 = GetMatrix();
1811     if (!pMat2 || !pMat1)
1812     {
1813         PushIllegalParameter();
1814         return;
1815     }
1816     SCSIZE nC1, nC2;
1817     SCSIZE nR1, nR2;
1818     pMat2->GetDimensions(nC2, nR2);
1819     pMat1->GetDimensions(nC1, nR1);
1820     if (nC1 != nC2 || nR1 != nR2)
1821     {
1822         PushNoValue();
1823         return;
1824     }
1825     double fVal;
1826     KahanSum fSum = 0.0;
1827     for (i = 0; i < nC1; i++)
1828         for (j = 0; j < nR1; j++)
1829             if (!pMat1->IsStringOrEmpty(i,j) && !pMat2->IsStringOrEmpty(i,j))
1830             {
1831                 fVal = pMat1->GetDouble(i,j);
1832                 fSum += fVal * fVal;
1833                 fVal = pMat2->GetDouble(i,j);
1834                 if ( _bSumX2DY2 )
1835                     fSum += fVal * fVal;
1836                 else
1837                     fSum -= fVal * fVal;
1838             }
1839     PushDouble(fSum.get());
1840 }
1841 
ScSumX2DY2()1842 void ScInterpreter::ScSumX2DY2()
1843 {
1844     CalculateSumX2MY2SumX2DY2(true);
1845 }
1846 
ScSumXMY2()1847 void ScInterpreter::ScSumXMY2()
1848 {
1849     if ( !MustHaveParamCount( GetByte(), 2 ) )
1850         return;
1851 
1852     ScMatrixRef pMat2 = GetMatrix();
1853     ScMatrixRef pMat1 = GetMatrix();
1854     if (!pMat2 || !pMat1)
1855     {
1856         PushIllegalParameter();
1857         return;
1858     }
1859     SCSIZE nC1, nC2;
1860     SCSIZE nR1, nR2;
1861     pMat2->GetDimensions(nC2, nR2);
1862     pMat1->GetDimensions(nC1, nR1);
1863     if (nC1 != nC2 || nR1 != nR2)
1864     {
1865         PushNoValue();
1866         return;
1867     } // if (nC1 != nC2 || nR1 != nR2)
1868     ScMatrixRef pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixSub);
1869     if (!pResMat)
1870     {
1871         PushNoValue();
1872     }
1873     else
1874     {
1875         PushDouble(pResMat->SumSquare(false).maAccumulator.get());
1876     }
1877 }
1878 
ScFrequency()1879 void ScInterpreter::ScFrequency()
1880 {
1881     if ( !MustHaveParamCount( GetByte(), 2 ) )
1882         return;
1883 
1884     std::vector<double>  aBinArray;
1885     std::vector<tools::Long>    aBinIndexOrder;
1886 
1887     GetSortArray( 1, aBinArray, &aBinIndexOrder, false, false );
1888     SCSIZE nBinSize = aBinArray.size();
1889     if (nGlobalError != FormulaError::NONE)
1890     {
1891         PushNoValue();
1892         return;
1893     }
1894 
1895     std::vector<double>  aDataArray;
1896     GetSortArray( 1, aDataArray, nullptr, false, false );
1897     SCSIZE nDataSize = aDataArray.size();
1898 
1899     if (aDataArray.empty() || nGlobalError != FormulaError::NONE)
1900     {
1901         PushNoValue();
1902         return;
1903     }
1904     ScMatrixRef pResMat = GetNewMat(1, nBinSize+1, /*bEmpty*/true);
1905     if (!pResMat)
1906     {
1907         PushIllegalArgument();
1908         return;
1909     }
1910 
1911     if (nBinSize != aBinIndexOrder.size())
1912     {
1913         PushIllegalArgument();
1914         return;
1915     }
1916 
1917     SCSIZE j;
1918     SCSIZE i = 0;
1919     for (j = 0; j < nBinSize; ++j)
1920     {
1921         SCSIZE nCount = 0;
1922         while (i < nDataSize && aDataArray[i] <= aBinArray[j])
1923         {
1924             ++nCount;
1925             ++i;
1926         }
1927         pResMat->PutDouble(static_cast<double>(nCount), aBinIndexOrder[j]);
1928     }
1929     pResMat->PutDouble(static_cast<double>(nDataSize-i), j);
1930     PushMatrix(pResMat);
1931 }
1932 
1933 namespace {
1934 
1935 // Helper methods for LINEST/LOGEST and TREND/GROWTH
1936 // All matrices must already exist and have the needed size, no control tests
1937 // done. Those methods, which names start with lcl_T, are adapted to case 3,
1938 // where Y (=observed values) is given as row.
1939 // Remember, ScMatrix matrices are zero based, index access (column,row).
1940 
1941 // <A;B> over all elements; uses the matrices as vectors of length M
lcl_GetSumProduct(const ScMatrixRef & pMatA,const ScMatrixRef & pMatB,SCSIZE nM)1942 double lcl_GetSumProduct(const ScMatrixRef& pMatA, const ScMatrixRef& pMatB, SCSIZE nM)
1943 {
1944     KahanSum fSum = 0.0;
1945     for (SCSIZE i=0; i<nM; i++)
1946         fSum += pMatA->GetDouble(i) * pMatB->GetDouble(i);
1947     return fSum.get();
1948 }
1949 
1950 // Special version for use within QR decomposition.
1951 // Euclidean norm of column index C starting in row index R;
1952 // matrix A has count N rows.
lcl_GetColumnEuclideanNorm(const ScMatrixRef & pMatA,SCSIZE nC,SCSIZE nR,SCSIZE nN)1953 double lcl_GetColumnEuclideanNorm(const ScMatrixRef& pMatA, SCSIZE nC, SCSIZE nR, SCSIZE nN)
1954 {
1955     KahanSum fNorm = 0.0;
1956     for (SCSIZE row=nR; row<nN; row++)
1957         fNorm  += (pMatA->GetDouble(nC,row)) * (pMatA->GetDouble(nC,row));
1958     return sqrt(fNorm.get());
1959 }
1960 
1961 // Euclidean norm of row index R starting in column index C;
1962 // matrix A has count N columns.
lcl_TGetColumnEuclideanNorm(const ScMatrixRef & pMatA,SCSIZE nR,SCSIZE nC,SCSIZE nN)1963 double lcl_TGetColumnEuclideanNorm(const ScMatrixRef& pMatA, SCSIZE nR, SCSIZE nC, SCSIZE nN)
1964 {
1965     KahanSum fNorm = 0.0;
1966     for (SCSIZE col=nC; col<nN; col++)
1967         fNorm  += (pMatA->GetDouble(col,nR)) * (pMatA->GetDouble(col,nR));
1968     return sqrt(fNorm.get());
1969 }
1970 
1971 // Special version for use within QR decomposition.
1972 // Maximum norm of column index C starting in row index R;
1973 // matrix A has count N rows.
lcl_GetColumnMaximumNorm(const ScMatrixRef & pMatA,SCSIZE nC,SCSIZE nR,SCSIZE nN)1974 double lcl_GetColumnMaximumNorm(const ScMatrixRef& pMatA, SCSIZE nC, SCSIZE nR, SCSIZE nN)
1975 {
1976     double fNorm = 0.0;
1977     for (SCSIZE row=nR; row<nN; row++)
1978     {
1979         double fVal = fabs(pMatA->GetDouble(nC,row));
1980         if (fNorm < fVal)
1981             fNorm = fVal;
1982     }
1983     return fNorm;
1984 }
1985 
1986 // Maximum norm of row index R starting in col index C;
1987 // matrix A has count N columns.
lcl_TGetColumnMaximumNorm(const ScMatrixRef & pMatA,SCSIZE nR,SCSIZE nC,SCSIZE nN)1988 double lcl_TGetColumnMaximumNorm(const ScMatrixRef& pMatA, SCSIZE nR, SCSIZE nC, SCSIZE nN)
1989 {
1990     double fNorm = 0.0;
1991     for (SCSIZE col=nC; col<nN; col++)
1992     {
1993         double fVal = fabs(pMatA->GetDouble(col,nR));
1994         if (fNorm < fVal)
1995             fNorm = fVal;
1996     }
1997     return fNorm;
1998 }
1999 
2000 // Special version for use within QR decomposition.
2001 // <A(Ca);B(Cb)> starting in row index R;
2002 // Ca and Cb are indices of columns, matrices A and B have count N rows.
lcl_GetColumnSumProduct(const ScMatrixRef & pMatA,SCSIZE nCa,const ScMatrixRef & pMatB,SCSIZE nCb,SCSIZE nR,SCSIZE nN)2003 double lcl_GetColumnSumProduct(const ScMatrixRef& pMatA, SCSIZE nCa,
2004                                const ScMatrixRef& pMatB, SCSIZE nCb, SCSIZE nR, SCSIZE nN)
2005 {
2006     KahanSum fResult = 0.0;
2007     for (SCSIZE row=nR; row<nN; row++)
2008         fResult += pMatA->GetDouble(nCa,row) * pMatB->GetDouble(nCb,row);
2009     return fResult.get();
2010 }
2011 
2012 // <A(Ra);B(Rb)> starting in column index C;
2013 // Ra and Rb are indices of rows, matrices A and B have count N columns.
lcl_TGetColumnSumProduct(const ScMatrixRef & pMatA,SCSIZE nRa,const ScMatrixRef & pMatB,SCSIZE nRb,SCSIZE nC,SCSIZE nN)2014 double lcl_TGetColumnSumProduct(const ScMatrixRef& pMatA, SCSIZE nRa,
2015                                 const ScMatrixRef& pMatB, SCSIZE nRb, SCSIZE nC, SCSIZE nN)
2016 {
2017     KahanSum fResult = 0.0;
2018     for (SCSIZE col=nC; col<nN; col++)
2019         fResult += pMatA->GetDouble(col,nRa) * pMatB->GetDouble(col,nRb);
2020     return fResult.get();
2021 }
2022 
2023 // no mathematical signum, but used to switch between adding and subtracting
lcl_GetSign(double fValue)2024 double lcl_GetSign(double fValue)
2025 {
2026     return (fValue >= 0.0 ? 1.0 : -1.0 );
2027 }
2028 
2029 /* Calculates a QR decomposition with Householder reflection.
2030  * For each NxK matrix A exists a decomposition A=Q*R with an orthogonal
2031  * NxN matrix Q and a NxK matrix R.
2032  * Q=H1*H2*...*Hk with Householder matrices H. Such a householder matrix can
2033  * be build from a vector u by H=I-(2/u'u)*(u u'). This vectors u are returned
2034  * in the columns of matrix A, overwriting the old content.
2035  * The matrix R has a quadric upper part KxK with values in the upper right
2036  * triangle and zeros in all other elements. Here the diagonal elements of R
2037  * are stored in the vector R and the other upper right elements in the upper
2038  * right of the matrix A.
2039  * The function returns false, if calculation breaks. But because of round-off
2040  * errors singularity is often not detected.
2041  */
lcl_CalculateQRdecomposition(const ScMatrixRef & pMatA,std::vector<double> & pVecR,SCSIZE nK,SCSIZE nN)2042 bool lcl_CalculateQRdecomposition(const ScMatrixRef& pMatA,
2043                                   std::vector< double>& pVecR, SCSIZE nK, SCSIZE nN)
2044 {
2045     // ScMatrix matrices are zero based, index access (column,row)
2046     for (SCSIZE col = 0; col <nK; col++)
2047     {
2048         // calculate vector u of the householder transformation
2049         const double fScale = lcl_GetColumnMaximumNorm(pMatA, col, col, nN);
2050         if (fScale == 0.0)
2051         {
2052             // A is singular
2053             return false;
2054         }
2055         for (SCSIZE row = col; row <nN; row++)
2056             pMatA->PutDouble( pMatA->GetDouble(col,row)/fScale, col, row);
2057 
2058         const double fEuclid = lcl_GetColumnEuclideanNorm(pMatA, col, col, nN);
2059         const double fFactor = 1.0/fEuclid/(fEuclid + fabs(pMatA->GetDouble(col,col)));
2060         const double fSignum = lcl_GetSign(pMatA->GetDouble(col,col));
2061         pMatA->PutDouble( pMatA->GetDouble(col,col) + fSignum*fEuclid, col,col);
2062         pVecR[col] = -fSignum * fScale * fEuclid;
2063 
2064         // apply Householder transformation to A
2065         for (SCSIZE c=col+1; c<nK; c++)
2066         {
2067             const double fSum =lcl_GetColumnSumProduct(pMatA, col, pMatA, c, col, nN);
2068             for (SCSIZE row = col; row <nN; row++)
2069                 pMatA->PutDouble( pMatA->GetDouble(c,row) - fSum * fFactor * pMatA->GetDouble(col,row), c, row);
2070         }
2071     }
2072     return true;
2073 }
2074 
2075 // same with transposed matrix A, N is count of columns, K count of rows
lcl_TCalculateQRdecomposition(const ScMatrixRef & pMatA,std::vector<double> & pVecR,SCSIZE nK,SCSIZE nN)2076 bool lcl_TCalculateQRdecomposition(const ScMatrixRef& pMatA,
2077                                    std::vector< double>& pVecR, SCSIZE nK, SCSIZE nN)
2078 {
2079     double fSum ;
2080     // ScMatrix matrices are zero based, index access (column,row)
2081     for (SCSIZE row = 0; row <nK; row++)
2082     {
2083         // calculate vector u of the householder transformation
2084         const double fScale = lcl_TGetColumnMaximumNorm(pMatA, row, row, nN);
2085         if (fScale == 0.0)
2086         {
2087             // A is singular
2088             return false;
2089         }
2090         for (SCSIZE col = row; col <nN; col++)
2091             pMatA->PutDouble( pMatA->GetDouble(col,row)/fScale, col, row);
2092 
2093         const double fEuclid = lcl_TGetColumnEuclideanNorm(pMatA, row, row, nN);
2094         const double fFactor = 1.0/fEuclid/(fEuclid + fabs(pMatA->GetDouble(row,row)));
2095         const double fSignum = lcl_GetSign(pMatA->GetDouble(row,row));
2096         pMatA->PutDouble( pMatA->GetDouble(row,row) + fSignum*fEuclid, row,row);
2097         pVecR[row] = -fSignum * fScale * fEuclid;
2098 
2099         // apply Householder transformation to A
2100         for (SCSIZE r=row+1; r<nK; r++)
2101         {
2102             fSum =lcl_TGetColumnSumProduct(pMatA, row, pMatA, r, row, nN);
2103             for (SCSIZE col = row; col <nN; col++)
2104                 pMatA->PutDouble(
2105                     pMatA->GetDouble(col,r) - fSum * fFactor * pMatA->GetDouble(col,row), col, r);
2106         }
2107     }
2108     return true;
2109 }
2110 
2111 /* Applies a Householder transformation to a column vector Y with is given as
2112  * Nx1 Matrix. The vector u, from which the Householder transformation is built,
2113  * is the column part in matrix A, with column index C, starting with row
2114  * index C. A is the result of the QR decomposition as obtained from
2115  * lcl_CalculateQRdecomposition.
2116  */
lcl_ApplyHouseholderTransformation(const ScMatrixRef & pMatA,SCSIZE nC,const ScMatrixRef & pMatY,SCSIZE nN)2117 void lcl_ApplyHouseholderTransformation(const ScMatrixRef& pMatA, SCSIZE nC,
2118                                         const ScMatrixRef& pMatY, SCSIZE nN)
2119 {
2120     // ScMatrix matrices are zero based, index access (column,row)
2121     double fDenominator = lcl_GetColumnSumProduct(pMatA, nC, pMatA, nC, nC, nN);
2122     double fNumerator = lcl_GetColumnSumProduct(pMatA, nC, pMatY, 0, nC, nN);
2123     double fFactor = 2.0 * o3tl::div_allow_zero(fNumerator, fDenominator);
2124     for (SCSIZE row = nC; row < nN; row++)
2125         pMatY->PutDouble(
2126             pMatY->GetDouble(row) - fFactor * pMatA->GetDouble(nC,row), row);
2127 }
2128 
2129 // Same with transposed matrices A and Y.
lcl_TApplyHouseholderTransformation(const ScMatrixRef & pMatA,SCSIZE nR,const ScMatrixRef & pMatY,SCSIZE nN)2130 void lcl_TApplyHouseholderTransformation(const ScMatrixRef& pMatA, SCSIZE nR,
2131                                           const ScMatrixRef& pMatY, SCSIZE nN)
2132 {
2133     // ScMatrix matrices are zero based, index access (column,row)
2134     double fDenominator = lcl_TGetColumnSumProduct(pMatA, nR, pMatA, nR, nR, nN);
2135     double fNumerator = lcl_TGetColumnSumProduct(pMatA, nR, pMatY, 0, nR, nN);
2136     double fFactor = 2.0 * o3tl::div_allow_zero(fNumerator, fDenominator);
2137     for (SCSIZE col = nR; col < nN; col++)
2138         pMatY->PutDouble(
2139           pMatY->GetDouble(col) - fFactor * pMatA->GetDouble(col,nR), col);
2140 }
2141 
2142 /* Solve for X in R*X=S using back substitution. The solution X overwrites S.
2143  * Uses R from the result of the QR decomposition of a NxK matrix A.
2144  * S is a column vector given as matrix, with at least elements on index
2145  * 0 to K-1; elements on index>=K are ignored. Vector R must not have zero
2146  * elements, no check is done.
2147  */
lcl_SolveWithUpperRightTriangle(const ScMatrixRef & pMatA,std::vector<double> & pVecR,const ScMatrixRef & pMatS,SCSIZE nK,bool bIsTransposed)2148 void lcl_SolveWithUpperRightTriangle(const ScMatrixRef& pMatA,
2149                         std::vector< double>& pVecR, const ScMatrixRef& pMatS,
2150                         SCSIZE nK, bool bIsTransposed)
2151 {
2152     // ScMatrix matrices are zero based, index access (column,row)
2153     SCSIZE row;
2154     // SCSIZE is never negative, therefore test with rowp1=row+1
2155     for (SCSIZE rowp1 = nK; rowp1>0; rowp1--)
2156     {
2157         row = rowp1-1;
2158         KahanSum fSum = pMatS->GetDouble(row);
2159         for (SCSIZE col = rowp1; col<nK ; col++)
2160             if (bIsTransposed)
2161                 fSum -= pMatA->GetDouble(row,col) * pMatS->GetDouble(col);
2162             else
2163                 fSum -= pMatA->GetDouble(col,row) * pMatS->GetDouble(col);
2164         pMatS->PutDouble( fSum.get() / pVecR[row] , row);
2165     }
2166 }
2167 
2168 /* Solve for X in R' * X= T using forward substitution. The solution X
2169  * overwrites T. Uses R from the result of the QR decomposition of a NxK
2170  * matrix A. T is a column vectors given as matrix, with at least elements on
2171  * index 0 to K-1; elements on index>=K are ignored. Vector R must not have
2172  * zero elements, no check is done.
2173  */
lcl_SolveWithLowerLeftTriangle(const ScMatrixRef & pMatA,std::vector<double> & pVecR,const ScMatrixRef & pMatT,SCSIZE nK,bool bIsTransposed)2174 void lcl_SolveWithLowerLeftTriangle(const ScMatrixRef& pMatA,
2175                                     std::vector< double>& pVecR, const ScMatrixRef& pMatT,
2176                                     SCSIZE nK, bool bIsTransposed)
2177 {
2178     // ScMatrix matrices are zero based, index access (column,row)
2179     for (SCSIZE row = 0; row < nK; row++)
2180     {
2181         KahanSum fSum = pMatT -> GetDouble(row);
2182         for (SCSIZE col=0; col < row; col++)
2183         {
2184             if (bIsTransposed)
2185                 fSum -= pMatA->GetDouble(col,row) * pMatT->GetDouble(col);
2186             else
2187                 fSum -= pMatA->GetDouble(row,col) * pMatT->GetDouble(col);
2188         }
2189         pMatT->PutDouble( fSum.get() / pVecR[row] , row);
2190     }
2191 }
2192 
2193 /* Calculates Z = R * B
2194  * R is given in matrix A and vector VecR as obtained from the QR
2195  * decomposition in lcl_CalculateQRdecomposition. B and Z are column vectors
2196  * given as matrix with at least index 0 to K-1; elements on index>=K are
2197  * not used.
2198  */
lcl_ApplyUpperRightTriangle(const ScMatrixRef & pMatA,std::vector<double> & pVecR,const ScMatrixRef & pMatB,const ScMatrixRef & pMatZ,SCSIZE nK,bool bIsTransposed)2199 void lcl_ApplyUpperRightTriangle(const ScMatrixRef& pMatA,
2200                                  std::vector< double>& pVecR, const ScMatrixRef& pMatB,
2201                                  const ScMatrixRef& pMatZ, SCSIZE nK, bool bIsTransposed)
2202 {
2203     // ScMatrix matrices are zero based, index access (column,row)
2204     for (SCSIZE row = 0; row < nK; row++)
2205     {
2206         KahanSum fSum = pVecR[row] * pMatB->GetDouble(row);
2207         for (SCSIZE col = row+1; col < nK; col++)
2208             if (bIsTransposed)
2209                 fSum += pMatA->GetDouble(row,col) * pMatB->GetDouble(col);
2210             else
2211                 fSum += pMatA->GetDouble(col,row) * pMatB->GetDouble(col);
2212         pMatZ->PutDouble( fSum.get(), row);
2213     }
2214 }
2215 
lcl_GetMeanOverAll(const ScMatrixRef & pMat,SCSIZE nN)2216 double lcl_GetMeanOverAll(const ScMatrixRef& pMat, SCSIZE nN)
2217 {
2218     KahanSum fSum = 0.0;
2219     for (SCSIZE i=0 ; i<nN; i++)
2220         fSum += pMat->GetDouble(i);
2221     return fSum.get()/static_cast<double>(nN);
2222 }
2223 
2224 // Calculates means of the columns of matrix X. X is a RxC matrix;
2225 // ResMat is a 1xC matrix (=row).
lcl_CalculateColumnMeans(const ScMatrixRef & pX,const ScMatrixRef & pResMat,SCSIZE nC,SCSIZE nR)2226 void lcl_CalculateColumnMeans(const ScMatrixRef& pX, const ScMatrixRef& pResMat,
2227                               SCSIZE nC, SCSIZE nR)
2228 {
2229     for (SCSIZE i=0; i < nC; i++)
2230     {
2231         KahanSum fSum =0.0;
2232         for (SCSIZE k=0; k < nR; k++)
2233             fSum += pX->GetDouble(i,k);   // GetDouble(Column,Row)
2234         pResMat ->PutDouble( fSum.get()/static_cast<double>(nR),i);
2235     }
2236 }
2237 
2238 // Calculates means of the rows of matrix X. X is a RxC matrix;
2239 // ResMat is a Rx1 matrix (=column).
lcl_CalculateRowMeans(const ScMatrixRef & pX,const ScMatrixRef & pResMat,SCSIZE nC,SCSIZE nR)2240 void lcl_CalculateRowMeans(const ScMatrixRef& pX, const ScMatrixRef& pResMat,
2241                            SCSIZE nC, SCSIZE nR)
2242 {
2243     for (SCSIZE k=0; k < nR; k++)
2244     {
2245         KahanSum fSum = 0.0;
2246         for (SCSIZE i=0; i < nC; i++)
2247             fSum += pX->GetDouble(i,k);   // GetDouble(Column,Row)
2248         pResMat ->PutDouble( fSum.get()/static_cast<double>(nC),k);
2249     }
2250 }
2251 
lcl_CalculateColumnsDelta(const ScMatrixRef & pMat,const ScMatrixRef & pColumnMeans,SCSIZE nC,SCSIZE nR)2252 void lcl_CalculateColumnsDelta(const ScMatrixRef& pMat, const ScMatrixRef& pColumnMeans,
2253                                SCSIZE nC, SCSIZE nR)
2254 {
2255     for (SCSIZE i = 0; i < nC; i++)
2256         for (SCSIZE k = 0; k < nR; k++)
2257             pMat->PutDouble( ::rtl::math::approxSub
2258                              (pMat->GetDouble(i,k) , pColumnMeans->GetDouble(i) ) , i, k);
2259 }
2260 
lcl_CalculateRowsDelta(const ScMatrixRef & pMat,const ScMatrixRef & pRowMeans,SCSIZE nC,SCSIZE nR)2261 void lcl_CalculateRowsDelta(const ScMatrixRef& pMat, const ScMatrixRef& pRowMeans,
2262                             SCSIZE nC, SCSIZE nR)
2263 {
2264     for (SCSIZE k = 0; k < nR; k++)
2265         for (SCSIZE i = 0; i < nC; i++)
2266             pMat->PutDouble( ::rtl::math::approxSub
2267                              ( pMat->GetDouble(i,k) , pRowMeans->GetDouble(k) ) , i, k);
2268 }
2269 
2270 // Case1 = simple regression
2271 // MatX = X - MeanX, MatY = Y - MeanY, y - haty = (y - MeanY) - (haty - MeanY)
2272 // = (y-MeanY)-((slope*x+a)-(slope*MeanX+a)) = (y-MeanY)-slope*(x-MeanX)
lcl_GetSSresid(const ScMatrixRef & pMatX,const ScMatrixRef & pMatY,double fSlope,SCSIZE nN)2273 double lcl_GetSSresid(const ScMatrixRef& pMatX, const ScMatrixRef& pMatY, double fSlope,
2274                       SCSIZE nN)
2275 {
2276     KahanSum fSum = 0.0;
2277     for (SCSIZE i=0; i<nN; i++)
2278     {
2279         const double fTemp = pMatY->GetDouble(i) - fSlope * pMatX->GetDouble(i);
2280         fSum += fTemp * fTemp;
2281     }
2282     return fSum.get();
2283 }
2284 
2285 }
2286 
2287 // Fill default values in matrix X, transform Y to log(Y) in case LOGEST|GROWTH,
2288 // determine sizes of matrices X and Y, determine kind of regression, clone
2289 // Y in case LOGEST|GROWTH, if constant.
CheckMatrix(bool _bLOG,sal_uInt8 & nCase,SCSIZE & nCX,SCSIZE & nCY,SCSIZE & nRX,SCSIZE & nRY,SCSIZE & M,SCSIZE & N,ScMatrixRef & pMatX,ScMatrixRef & pMatY)2290 bool ScInterpreter::CheckMatrix(bool _bLOG, sal_uInt8& nCase, SCSIZE& nCX,
2291                         SCSIZE& nCY, SCSIZE& nRX, SCSIZE& nRY, SCSIZE& M,
2292                         SCSIZE& N, ScMatrixRef& pMatX, ScMatrixRef& pMatY)
2293 {
2294 
2295     nCX = 0;
2296     nCY = 0;
2297     nRX = 0;
2298     nRY = 0;
2299     M = 0;
2300     N = 0;
2301     pMatY->GetDimensions(nCY, nRY);
2302     const SCSIZE nCountY = nCY * nRY;
2303     for ( SCSIZE i = 0; i < nCountY; i++ )
2304     {
2305         if (!pMatY->IsValue(i))
2306         {
2307             PushIllegalArgument();
2308             return false;
2309         }
2310     }
2311 
2312     if ( _bLOG )
2313     {
2314         ScMatrixRef pNewY = pMatY->CloneIfConst();
2315         for (SCSIZE nElem = 0; nElem < nCountY; nElem++)
2316         {
2317             const double fVal = pNewY->GetDouble(nElem);
2318             if (fVal <= 0.0)
2319             {
2320                 PushIllegalArgument();
2321                 return false;
2322             }
2323             else
2324                 pNewY->PutDouble(log(fVal), nElem);
2325         }
2326         pMatY = std::move(pNewY);
2327     }
2328 
2329     if (pMatX)
2330     {
2331         pMatX->GetDimensions(nCX, nRX);
2332         const SCSIZE nCountX = nCX * nRX;
2333         for ( SCSIZE i = 0; i < nCountX; i++ )
2334             if (!pMatX->IsValue(i))
2335             {
2336                 PushIllegalArgument();
2337                 return false;
2338             }
2339         if (nCX == nCY && nRX == nRY)
2340         {
2341             nCase = 1;                  // simple regression
2342             M = 1;
2343             N = nCountY;
2344         }
2345         else if (nCY != 1 && nRY != 1)
2346         {
2347             PushIllegalArgument();
2348             return false;
2349         }
2350         else if (nCY == 1)
2351         {
2352             if (nRX != nRY)
2353             {
2354                 PushIllegalArgument();
2355                 return false;
2356             }
2357             else
2358             {
2359                 nCase = 2;              // Y is column
2360                 N = nRY;
2361                 M = nCX;
2362             }
2363         }
2364         else if (nCX != nCY)
2365         {
2366             PushIllegalArgument();
2367             return false;
2368         }
2369         else
2370         {
2371             nCase = 3;                  // Y is row
2372             N = nCY;
2373             M = nRX;
2374         }
2375     }
2376     else
2377     {
2378         pMatX = GetNewMat(nCY, nRY, /*bEmpty*/true);
2379         nCX = nCY;
2380         nRX = nRY;
2381         if (!pMatX)
2382         {
2383             PushIllegalArgument();
2384             return false;
2385         }
2386         for ( SCSIZE i = 1; i <= nCountY; i++ )
2387             pMatX->PutDouble(static_cast<double>(i), i-1);
2388         nCase = 1;
2389         N = nCountY;
2390         M = 1;
2391     }
2392     return true;
2393 }
2394 
2395 // LINEST
ScLinest()2396 void ScInterpreter::ScLinest()
2397 {
2398     CalculateRGPRKP(false);
2399 }
2400 
2401 // LOGEST
ScLogest()2402 void ScInterpreter::ScLogest()
2403 {
2404     CalculateRGPRKP(true);
2405 }
2406 
CalculateRGPRKP(bool _bRKP)2407 void ScInterpreter::CalculateRGPRKP(bool _bRKP)
2408 {
2409     sal_uInt8 nParamCount = GetByte();
2410     if (!MustHaveParamCount( nParamCount, 1, 4 ))
2411         return;
2412     bool bConstant, bStats;
2413 
2414     // optional forth parameter
2415     if (nParamCount == 4)
2416         bStats = GetBool();
2417     else
2418         bStats = false;
2419 
2420     // The third parameter may not be missing in ODF, if the forth parameter
2421     // is present. But Excel allows it with default true, we too.
2422     if (nParamCount >= 3)
2423     {
2424         if (IsMissing())
2425         {
2426             Pop();
2427             bConstant = true;
2428 //            PushIllegalParameter(); if ODF behavior is desired
2429 //            return;
2430         }
2431         else
2432             bConstant = GetBool();
2433     }
2434     else
2435         bConstant = true;
2436 
2437     ScMatrixRef pMatX;
2438     if (nParamCount >= 2)
2439     {
2440         if (IsMissing())
2441         { //In ODF1.2 empty second parameter (which is two ;; ) is allowed
2442             Pop();
2443             pMatX = nullptr;
2444         }
2445         else
2446         {
2447             pMatX = GetMatrix();
2448         }
2449     }
2450     else
2451         pMatX = nullptr;
2452 
2453     ScMatrixRef pMatY = GetMatrix();
2454     if (!pMatY)
2455     {
2456         PushIllegalParameter();
2457         return;
2458     }
2459 
2460     // 1 = simple; 2 = multiple with Y as column; 3 = multiple with Y as row
2461     sal_uInt8 nCase;
2462 
2463     SCSIZE nCX, nCY; // number of columns
2464     SCSIZE nRX, nRY;    //number of rows
2465     SCSIZE K = 0, N = 0; // K=number of variables X, N=number of data samples
2466     if (!CheckMatrix(_bRKP,nCase,nCX,nCY,nRX,nRY,K,N,pMatX,pMatY))
2467     {
2468         PushIllegalParameter();
2469         return;
2470     }
2471 
2472     // Enough data samples?
2473     if ((bConstant && (N<K+1)) || (!bConstant && (N<K)) || (N<1) || (K<1))
2474     {
2475         PushIllegalParameter();
2476         return;
2477     }
2478 
2479     ScMatrixRef pResMat;
2480     if (bStats)
2481         pResMat = GetNewMat(K+1,5, /*bEmpty*/true);
2482     else
2483         pResMat = GetNewMat(K+1,1, /*bEmpty*/true);
2484     if (!pResMat)
2485     {
2486         PushError(FormulaError::CodeOverflow);
2487         return;
2488     }
2489     // Fill unused cells in pResMat; order (column,row)
2490     if (bStats)
2491     {
2492         for (SCSIZE i=2; i<K+1; i++)
2493         {
2494             pResMat->PutError( FormulaError::NotAvailable, i, 2);
2495             pResMat->PutError( FormulaError::NotAvailable, i, 3);
2496             pResMat->PutError( FormulaError::NotAvailable, i, 4);
2497         }
2498     }
2499 
2500     // Uses sum(x-MeanX)^2 and not [sum x^2]-N * MeanX^2 in case bConstant.
2501     // Clone constant matrices, so that Mat = Mat - Mean is possible.
2502     double fMeanY = 0.0;
2503     if (bConstant)
2504     {
2505         ScMatrixRef pNewX = pMatX->CloneIfConst();
2506         ScMatrixRef pNewY = pMatY->CloneIfConst();
2507         if (!pNewX || !pNewY)
2508         {
2509             PushError(FormulaError::CodeOverflow);
2510             return;
2511         }
2512         pMatX = std::move(pNewX);
2513         pMatY = std::move(pNewY);
2514         // DeltaY is possible here; DeltaX depends on nCase, so later
2515         fMeanY = lcl_GetMeanOverAll(pMatY, N);
2516         for (SCSIZE i=0; i<N; i++)
2517         {
2518             pMatY->PutDouble( ::rtl::math::approxSub(pMatY->GetDouble(i),fMeanY), i );
2519         }
2520     }
2521 
2522     if (nCase==1)
2523     {
2524         // calculate simple regression
2525         double fMeanX = 0.0;
2526         if (bConstant)
2527         {   // Mat = Mat - Mean
2528             fMeanX = lcl_GetMeanOverAll(pMatX, N);
2529             for (SCSIZE i=0; i<N; i++)
2530             {
2531                 pMatX->PutDouble( ::rtl::math::approxSub(pMatX->GetDouble(i),fMeanX), i );
2532             }
2533         }
2534         double fSumXY = lcl_GetSumProduct(pMatX,pMatY,N);
2535         double fSumX2 = lcl_GetSumProduct(pMatX,pMatX,N);
2536         if (fSumX2==0.0)
2537         {
2538             PushNoValue(); // all x-values are identical
2539             return;
2540         }
2541         double fSlope = fSumXY / fSumX2;
2542         double fIntercept = 0.0;
2543         if (bConstant)
2544             fIntercept = fMeanY - fSlope * fMeanX;
2545         pResMat->PutDouble(_bRKP ? exp(fIntercept) : fIntercept, 1, 0); //order (column,row)
2546         pResMat->PutDouble(_bRKP ? exp(fSlope) : fSlope, 0, 0);
2547 
2548         if (bStats)
2549         {
2550             double fSSreg = fSlope * fSlope * fSumX2;
2551             pResMat->PutDouble(fSSreg, 0, 4);
2552 
2553             double fDegreesFreedom =static_cast<double>( bConstant ? N-2 : N-1 );
2554             pResMat->PutDouble(fDegreesFreedom, 1, 3);
2555 
2556             double fSSresid = lcl_GetSSresid(pMatX,pMatY,fSlope,N);
2557             pResMat->PutDouble(fSSresid, 1, 4);
2558 
2559             if (fDegreesFreedom == 0.0 || fSSresid == 0.0 || fSSreg == 0.0)
2560             {   // exact fit; test SSreg too, because SSresid might be
2561                 // unequal zero due to round of errors
2562                 pResMat->PutDouble(0.0, 1, 4); // SSresid
2563                 pResMat->PutError( FormulaError::NotAvailable, 0, 3); // F
2564                 pResMat->PutDouble(0.0, 1, 2); // RMSE
2565                 pResMat->PutDouble(0.0, 0, 1); // SigmaSlope
2566                 if (bConstant)
2567                     pResMat->PutDouble(0.0, 1, 1); //SigmaIntercept
2568                 else
2569                     pResMat->PutError( FormulaError::NotAvailable, 1, 1);
2570                 pResMat->PutDouble(1.0, 0, 2); // R^2
2571             }
2572             else
2573             {
2574                 double fFstatistic = (fSSreg / static_cast<double>(K))
2575                                      / (fSSresid / fDegreesFreedom);
2576                 pResMat->PutDouble(fFstatistic, 0, 3);
2577 
2578                 // standard error of estimate
2579                 double fRMSE = sqrt(fSSresid / fDegreesFreedom);
2580                 pResMat->PutDouble(fRMSE, 1, 2);
2581 
2582                 double fSigmaSlope = fRMSE / sqrt(fSumX2);
2583                 pResMat->PutDouble(fSigmaSlope, 0, 1);
2584 
2585                 if (bConstant)
2586                 {
2587                     double fSigmaIntercept = fRMSE
2588                                              * sqrt(fMeanX*fMeanX/fSumX2 + 1.0/static_cast<double>(N));
2589                     pResMat->PutDouble(fSigmaIntercept, 1, 1);
2590                 }
2591                 else
2592                 {
2593                     pResMat->PutError( FormulaError::NotAvailable, 1, 1);
2594                 }
2595 
2596                 double fR2 = fSSreg / (fSSreg + fSSresid);
2597                 pResMat->PutDouble(fR2, 0, 2);
2598             }
2599         }
2600         PushMatrix(pResMat);
2601     }
2602     else // calculate multiple regression;
2603     {
2604         // Uses a QR decomposition X = QR. The solution B = (X'X)^(-1) * X' * Y
2605         // becomes B = R^(-1) * Q' * Y
2606         if (nCase ==2) // Y is column
2607         {
2608             std::vector< double> aVecR(N); // for QR decomposition
2609             // Enough memory for needed matrices?
2610             ScMatrixRef pMeans = GetNewMat(K, 1, /*bEmpty*/true); // mean of each column
2611             ScMatrixRef pMatZ; // for Q' * Y , inter alia
2612             if (bStats)
2613                 pMatZ = pMatY->Clone(); // Y is used in statistic, keep it
2614             else
2615                 pMatZ = pMatY; // Y can be overwritten
2616             ScMatrixRef pSlopes = GetNewMat(1,K, /*bEmpty*/true); // from b1 to bK
2617             if (!pMeans || !pMatZ || !pSlopes)
2618             {
2619                 PushError(FormulaError::CodeOverflow);
2620                 return;
2621             }
2622             if (bConstant)
2623             {
2624                 lcl_CalculateColumnMeans(pMatX, pMeans, K, N);
2625                 lcl_CalculateColumnsDelta(pMatX, pMeans, K, N);
2626             }
2627             if (!lcl_CalculateQRdecomposition(pMatX, aVecR, K, N))
2628             {
2629                 PushNoValue();
2630                 return;
2631             }
2632             // Later on we will divide by elements of aVecR, so make sure
2633             // that they aren't zero.
2634             bool bIsSingular=false;
2635             for (SCSIZE row=0; row < K && !bIsSingular; row++)
2636                 bIsSingular = aVecR[row] == 0.0;
2637             if (bIsSingular)
2638             {
2639                 PushNoValue();
2640                 return;
2641             }
2642             // Z = Q' Y;
2643             for (SCSIZE col = 0; col < K; col++)
2644             {
2645                 lcl_ApplyHouseholderTransformation(pMatX, col, pMatZ, N);
2646             }
2647             // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
2648             // result Z should have zeros for index>=K; if not, ignore values
2649             for (SCSIZE col = 0; col < K ; col++)
2650             {
2651                 pSlopes->PutDouble( pMatZ->GetDouble(col), col);
2652             }
2653             lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, false);
2654             double fIntercept = 0.0;
2655             if (bConstant)
2656                 fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K);
2657             // Fill first line in result matrix
2658             pResMat->PutDouble(_bRKP ? exp(fIntercept) : fIntercept, K, 0 );
2659             for (SCSIZE i = 0; i < K; i++)
2660                 pResMat->PutDouble(_bRKP ? exp(pSlopes->GetDouble(i))
2661                                    : pSlopes->GetDouble(i) , K-1-i, 0);
2662 
2663             if (bStats)
2664             {
2665                 double fSSreg = 0.0;
2666                 double fSSresid = 0.0;
2667                 // re-use memory of Z;
2668                 pMatZ->FillDouble(0.0, 0, 0, 0, N-1);
2669                 // Z = R * Slopes
2670                 lcl_ApplyUpperRightTriangle(pMatX, aVecR, pSlopes, pMatZ, K, false);
2671                 // Z = Q * Z, that is Q * R * Slopes = X * Slopes
2672                 for (SCSIZE colp1 = K; colp1 > 0; colp1--)
2673                 {
2674                     lcl_ApplyHouseholderTransformation(pMatX, colp1-1, pMatZ,N);
2675                 }
2676                 fSSreg =lcl_GetSumProduct(pMatZ, pMatZ, N);
2677                 // re-use Y for residuals, Y = Y-Z
2678                 for (SCSIZE row = 0; row < N; row++)
2679                     pMatY->PutDouble(pMatY->GetDouble(row) - pMatZ->GetDouble(row), row);
2680                 fSSresid = lcl_GetSumProduct(pMatY, pMatY, N);
2681                 pResMat->PutDouble(fSSreg, 0, 4);
2682                 pResMat->PutDouble(fSSresid, 1, 4);
2683 
2684                 double fDegreesFreedom =static_cast<double>( bConstant ? N-K-1 : N-K );
2685                 pResMat->PutDouble(fDegreesFreedom, 1, 3);
2686 
2687                 if (fDegreesFreedom == 0.0 || fSSresid == 0.0 || fSSreg == 0.0)
2688                 {   // exact fit; incl. observed values Y are identical
2689                     pResMat->PutDouble(0.0, 1, 4); // SSresid
2690                     // F = (SSreg/K) / (SSresid/df) = #DIV/0!
2691                     pResMat->PutError( FormulaError::NotAvailable, 0, 3); // F
2692                     // RMSE = sqrt(SSresid / df) = sqrt(0 / df) = 0
2693                     pResMat->PutDouble(0.0, 1, 2); // RMSE
2694                     // SigmaSlope[i] = RMSE * sqrt(matrix[i,i]) = 0 * sqrt(...) = 0
2695                     for (SCSIZE i=0; i<K; i++)
2696                         pResMat->PutDouble(0.0, K-1-i, 1);
2697 
2698                     // SigmaIntercept = RMSE * sqrt(...) = 0
2699                     if (bConstant)
2700                         pResMat->PutDouble(0.0, K, 1); //SigmaIntercept
2701                     else
2702                         pResMat->PutError( FormulaError::NotAvailable, K, 1);
2703 
2704                     //  R^2 = SSreg / (SSreg + SSresid) = 1.0
2705                     pResMat->PutDouble(1.0, 0, 2); // R^2
2706                 }
2707                 else
2708                 {
2709                     double fFstatistic = (fSSreg / static_cast<double>(K))
2710                                          / (fSSresid / fDegreesFreedom);
2711                     pResMat->PutDouble(fFstatistic, 0, 3);
2712 
2713                     // standard error of estimate = root mean SSE
2714                     double fRMSE = sqrt(fSSresid / fDegreesFreedom);
2715                     pResMat->PutDouble(fRMSE, 1, 2);
2716 
2717                     // standard error of slopes
2718                     // = RMSE * sqrt(diagonal element of (R' R)^(-1) )
2719                     // standard error of intercept
2720                     // = RMSE * sqrt( Xmean * (R' R)^(-1) * Xmean' + 1/N)
2721                     // (R' R)^(-1) = R^(-1) * (R')^(-1). Do not calculate it as
2722                     // a whole matrix, but iterate over unit vectors.
2723                     KahanSum aSigmaIntercept = 0.0;
2724                     double fPart; // for Xmean * single column of (R' R)^(-1)
2725                     for (SCSIZE col = 0; col < K; col++)
2726                     {
2727                         //re-use memory of MatZ
2728                         pMatZ->FillDouble(0.0,0,0,0,K-1); // Z = unit vector e
2729                         pMatZ->PutDouble(1.0, col);
2730                         //Solve R' * Z = e
2731                         lcl_SolveWithLowerLeftTriangle(pMatX, aVecR, pMatZ, K, false);
2732                         // Solve R * Znew = Zold
2733                         lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pMatZ, K, false);
2734                         // now Z is column col in (R' R)^(-1)
2735                         double fSigmaSlope = fRMSE * sqrt(pMatZ->GetDouble(col));
2736                         pResMat->PutDouble(fSigmaSlope, K-1-col, 1);
2737                         // (R' R) ^(-1) is symmetric
2738                         if (bConstant)
2739                         {
2740                             fPart = lcl_GetSumProduct(pMeans, pMatZ, K);
2741                             aSigmaIntercept += fPart * pMeans->GetDouble(col);
2742                         }
2743                     }
2744                     if (bConstant)
2745                     {
2746                         double fSigmaIntercept = fRMSE
2747                                           * sqrt( (aSigmaIntercept + 1.0 / static_cast<double>(N) ).get() );
2748                         pResMat->PutDouble(fSigmaIntercept, K, 1);
2749                     }
2750                     else
2751                     {
2752                         pResMat->PutError( FormulaError::NotAvailable, K, 1);
2753                     }
2754 
2755                     double fR2 = fSSreg / (fSSreg + fSSresid);
2756                     pResMat->PutDouble(fR2, 0, 2);
2757                 }
2758             }
2759             PushMatrix(pResMat);
2760         }
2761         else  // nCase == 3, Y is row, all matrices are transposed
2762         {
2763             std::vector< double> aVecR(N); // for QR decomposition
2764             // Enough memory for needed matrices?
2765             ScMatrixRef pMeans = GetNewMat(1, K, /*bEmpty*/true); // mean of each row
2766             ScMatrixRef pMatZ; // for Q' * Y , inter alia
2767             if (bStats)
2768                 pMatZ = pMatY->Clone(); // Y is used in statistic, keep it
2769             else
2770                 pMatZ = pMatY; // Y can be overwritten
2771             ScMatrixRef pSlopes = GetNewMat(K,1, /*bEmpty*/true); // from b1 to bK
2772             if (!pMeans || !pMatZ || !pSlopes)
2773             {
2774                 PushError(FormulaError::CodeOverflow);
2775                 return;
2776             }
2777             if (bConstant)
2778             {
2779                 lcl_CalculateRowMeans(pMatX, pMeans, N, K);
2780                 lcl_CalculateRowsDelta(pMatX, pMeans, N, K);
2781             }
2782 
2783             if (!lcl_TCalculateQRdecomposition(pMatX, aVecR, K, N))
2784             {
2785                 PushNoValue();
2786                 return;
2787             }
2788 
2789             // Later on we will divide by elements of aVecR, so make sure
2790             // that they aren't zero.
2791             bool bIsSingular=false;
2792             for (SCSIZE row=0; row < K && !bIsSingular; row++)
2793                 bIsSingular = aVecR[row] == 0.0;
2794             if (bIsSingular)
2795             {
2796                 PushNoValue();
2797                 return;
2798             }
2799             // Z = Q' Y
2800             for (SCSIZE row = 0; row < K; row++)
2801             {
2802                 lcl_TApplyHouseholderTransformation(pMatX, row, pMatZ, N);
2803             }
2804             // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
2805             // result Z should have zeros for index>=K; if not, ignore values
2806             for (SCSIZE col = 0; col < K ; col++)
2807             {
2808                 pSlopes->PutDouble( pMatZ->GetDouble(col), col);
2809             }
2810             lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, true);
2811             double fIntercept = 0.0;
2812             if (bConstant)
2813                 fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K);
2814             // Fill first line in result matrix
2815             pResMat->PutDouble(_bRKP ? exp(fIntercept) : fIntercept, K, 0 );
2816             for (SCSIZE i = 0; i < K; i++)
2817                 pResMat->PutDouble(_bRKP ? exp(pSlopes->GetDouble(i))
2818                                    : pSlopes->GetDouble(i) , K-1-i, 0);
2819 
2820             if (bStats)
2821             {
2822                 double fSSreg = 0.0;
2823                 double fSSresid = 0.0;
2824                 // re-use memory of Z;
2825                 pMatZ->FillDouble(0.0, 0, 0, N-1, 0);
2826                 // Z = R * Slopes
2827                 lcl_ApplyUpperRightTriangle(pMatX, aVecR, pSlopes, pMatZ, K, true);
2828                 // Z = Q * Z, that is Q * R * Slopes = X * Slopes
2829                 for (SCSIZE rowp1 = K; rowp1 > 0; rowp1--)
2830                 {
2831                     lcl_TApplyHouseholderTransformation(pMatX, rowp1-1, pMatZ,N);
2832                 }
2833                 fSSreg =lcl_GetSumProduct(pMatZ, pMatZ, N);
2834                 // re-use Y for residuals, Y = Y-Z
2835                 for (SCSIZE col = 0; col < N; col++)
2836                     pMatY->PutDouble(pMatY->GetDouble(col) - pMatZ->GetDouble(col), col);
2837                 fSSresid = lcl_GetSumProduct(pMatY, pMatY, N);
2838                 pResMat->PutDouble(fSSreg, 0, 4);
2839                 pResMat->PutDouble(fSSresid, 1, 4);
2840 
2841                 double fDegreesFreedom =static_cast<double>( bConstant ? N-K-1 : N-K );
2842                 pResMat->PutDouble(fDegreesFreedom, 1, 3);
2843 
2844                 if (fDegreesFreedom == 0.0 || fSSresid == 0.0 || fSSreg == 0.0)
2845                 {   // exact fit; incl. case observed values Y are identical
2846                     pResMat->PutDouble(0.0, 1, 4); // SSresid
2847                     // F = (SSreg/K) / (SSresid/df) = #DIV/0!
2848                     pResMat->PutError( FormulaError::NotAvailable, 0, 3); // F
2849                     // RMSE = sqrt(SSresid / df) = sqrt(0 / df) = 0
2850                     pResMat->PutDouble(0.0, 1, 2); // RMSE
2851                     // SigmaSlope[i] = RMSE * sqrt(matrix[i,i]) = 0 * sqrt(...) = 0
2852                     for (SCSIZE i=0; i<K; i++)
2853                         pResMat->PutDouble(0.0, K-1-i, 1);
2854 
2855                     // SigmaIntercept = RMSE * sqrt(...) = 0
2856                     if (bConstant)
2857                         pResMat->PutDouble(0.0, K, 1); //SigmaIntercept
2858                     else
2859                         pResMat->PutError( FormulaError::NotAvailable, K, 1);
2860 
2861                     //  R^2 = SSreg / (SSreg + SSresid) = 1.0
2862                     pResMat->PutDouble(1.0, 0, 2); // R^2
2863                 }
2864                 else
2865                 {
2866                     double fFstatistic = (fSSreg / static_cast<double>(K))
2867                                          / (fSSresid / fDegreesFreedom);
2868                     pResMat->PutDouble(fFstatistic, 0, 3);
2869 
2870                     // standard error of estimate = root mean SSE
2871                     double fRMSE = sqrt(fSSresid / fDegreesFreedom);
2872                     pResMat->PutDouble(fRMSE, 1, 2);
2873 
2874                     // standard error of slopes
2875                     // = RMSE * sqrt(diagonal element of (R' R)^(-1) )
2876                     // standard error of intercept
2877                     // = RMSE * sqrt( Xmean * (R' R)^(-1) * Xmean' + 1/N)
2878                     // (R' R)^(-1) = R^(-1) * (R')^(-1). Do not calculate it as
2879                     // a whole matrix, but iterate over unit vectors.
2880                     // (R' R) ^(-1) is symmetric
2881                     KahanSum aSigmaIntercept = 0.0;
2882                     double fPart; // for Xmean * single col of (R' R)^(-1)
2883                     for (SCSIZE row = 0; row < K; row++)
2884                     {
2885                         //re-use memory of MatZ
2886                         pMatZ->FillDouble(0.0,0,0,K-1,0); // Z = unit vector e
2887                         pMatZ->PutDouble(1.0, row);
2888                         //Solve R' * Z = e
2889                         lcl_SolveWithLowerLeftTriangle(pMatX, aVecR, pMatZ, K, true);
2890                         // Solve R * Znew = Zold
2891                         lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pMatZ, K, true);
2892                         // now Z is column col in (R' R)^(-1)
2893                         double fSigmaSlope = fRMSE * sqrt(pMatZ->GetDouble(row));
2894                         pResMat->PutDouble(fSigmaSlope, K-1-row, 1);
2895                         if (bConstant)
2896                         {
2897                             fPart = lcl_GetSumProduct(pMeans, pMatZ, K);
2898                             aSigmaIntercept += fPart * pMeans->GetDouble(row);
2899                         }
2900                     }
2901                     if (bConstant)
2902                     {
2903                         double fSigmaIntercept = fRMSE
2904                                           * sqrt( (aSigmaIntercept + 1.0 / static_cast<double>(N) ).get() );
2905                         pResMat->PutDouble(fSigmaIntercept, K, 1);
2906                     }
2907                     else
2908                     {
2909                         pResMat->PutError( FormulaError::NotAvailable, K, 1);
2910                     }
2911 
2912                     double fR2 = fSSreg / (fSSreg + fSSresid);
2913                     pResMat->PutDouble(fR2, 0, 2);
2914                 }
2915             }
2916             PushMatrix(pResMat);
2917         }
2918     }
2919 }
2920 
ScTrend()2921 void ScInterpreter::ScTrend()
2922 {
2923     CalculateTrendGrowth(false);
2924 }
2925 
ScGrowth()2926 void ScInterpreter::ScGrowth()
2927 {
2928     CalculateTrendGrowth(true);
2929 }
2930 
CalculateTrendGrowth(bool _bGrowth)2931 void ScInterpreter::CalculateTrendGrowth(bool _bGrowth)
2932 {
2933     sal_uInt8 nParamCount = GetByte();
2934     if (!MustHaveParamCount( nParamCount, 1, 4 ))
2935         return;
2936 
2937     // optional forth parameter
2938     bool bConstant;
2939     if (nParamCount == 4)
2940         bConstant = GetBool();
2941     else
2942         bConstant = true;
2943 
2944     // The third parameter may be missing in ODF, although the forth parameter
2945     // is present. Default values depend on data not yet read.
2946     ScMatrixRef pMatNewX;
2947     if (nParamCount >= 3)
2948     {
2949         if (IsMissing())
2950         {
2951             Pop();
2952             pMatNewX = nullptr;
2953         }
2954         else
2955             pMatNewX = GetMatrix();
2956     }
2957     else
2958         pMatNewX = nullptr;
2959 
2960     //In ODF1.2 empty second parameter (which is two ;; ) is allowed
2961     //Defaults will be set in CheckMatrix
2962     ScMatrixRef pMatX;
2963     if (nParamCount >= 2)
2964     {
2965         if (IsMissing())
2966         {
2967             Pop();
2968             pMatX = nullptr;
2969         }
2970         else
2971         {
2972             pMatX = GetMatrix();
2973         }
2974     }
2975     else
2976         pMatX = nullptr;
2977 
2978     ScMatrixRef pMatY = GetMatrix();
2979     if (!pMatY)
2980     {
2981         PushIllegalParameter();
2982         return;
2983     }
2984 
2985     // 1 = simple; 2 = multiple with Y as column; 3 = multiple with Y as row
2986     sal_uInt8 nCase;
2987 
2988     SCSIZE nCX, nCY; // number of columns
2989     SCSIZE nRX, nRY; //number of rows
2990     SCSIZE K = 0, N = 0; // K=number of variables X, N=number of data samples
2991     if (!CheckMatrix(_bGrowth,nCase,nCX,nCY,nRX,nRY,K,N,pMatX,pMatY))
2992     {
2993         PushIllegalParameter();
2994         return;
2995     }
2996 
2997     // Enough data samples?
2998     if ((bConstant && (N<K+1)) || (!bConstant && (N<K)) || (N<1) || (K<1))
2999     {
3000         PushIllegalParameter();
3001         return;
3002     }
3003 
3004     // Set default pMatNewX if necessary
3005     SCSIZE nCXN, nRXN;
3006     SCSIZE nCountXN;
3007     if (!pMatNewX)
3008     {
3009         nCXN = nCX;
3010         nRXN = nRX;
3011         nCountXN = nCXN * nRXN;
3012         pMatNewX = pMatX->Clone(); // pMatX will be changed to X-meanX
3013     }
3014     else
3015     {
3016         pMatNewX->GetDimensions(nCXN, nRXN);
3017         if ((nCase == 2 && K != nCXN) || (nCase == 3 && K != nRXN))
3018         {
3019             PushIllegalArgument();
3020             return;
3021         }
3022         nCountXN = nCXN * nRXN;
3023         for (SCSIZE i = 0; i < nCountXN; i++)
3024             if (!pMatNewX->IsValue(i))
3025             {
3026                 PushIllegalArgument();
3027                 return;
3028             }
3029     }
3030     ScMatrixRef pResMat; // size depends on nCase
3031     if (nCase == 1)
3032         pResMat = GetNewMat(nCXN,nRXN, /*bEmpty*/true);
3033     else
3034     {
3035         if (nCase==2)
3036             pResMat = GetNewMat(1,nRXN, /*bEmpty*/true);
3037         else
3038             pResMat = GetNewMat(nCXN,1, /*bEmpty*/true);
3039     }
3040     if (!pResMat)
3041     {
3042         PushError(FormulaError::CodeOverflow);
3043         return;
3044     }
3045     // Uses sum(x-MeanX)^2 and not [sum x^2]-N * MeanX^2 in case bConstant.
3046     // Clone constant matrices, so that Mat = Mat - Mean is possible.
3047     double fMeanY = 0.0;
3048     if (bConstant)
3049     {
3050         ScMatrixRef pCopyX = pMatX->CloneIfConst();
3051         ScMatrixRef pCopyY = pMatY->CloneIfConst();
3052         if (!pCopyX || !pCopyY)
3053         {
3054             PushError(FormulaError::MatrixSize);
3055             return;
3056         }
3057         pMatX = std::move(pCopyX);
3058         pMatY = std::move(pCopyY);
3059         // DeltaY is possible here; DeltaX depends on nCase, so later
3060         fMeanY = lcl_GetMeanOverAll(pMatY, N);
3061         for (SCSIZE i=0; i<N; i++)
3062         {
3063             pMatY->PutDouble( ::rtl::math::approxSub(pMatY->GetDouble(i),fMeanY), i );
3064         }
3065     }
3066 
3067     if (nCase==1)
3068     {
3069         // calculate simple regression
3070         double fMeanX = 0.0;
3071         if (bConstant)
3072         {   // Mat = Mat - Mean
3073             fMeanX = lcl_GetMeanOverAll(pMatX, N);
3074             for (SCSIZE i=0; i<N; i++)
3075             {
3076                 pMatX->PutDouble( ::rtl::math::approxSub(pMatX->GetDouble(i),fMeanX), i );
3077             }
3078         }
3079         double fSumXY = lcl_GetSumProduct(pMatX,pMatY,N);
3080         double fSumX2 = lcl_GetSumProduct(pMatX,pMatX,N);
3081         if (fSumX2==0.0)
3082         {
3083             PushNoValue(); // all x-values are identical
3084             return;
3085         }
3086         double fSlope = fSumXY / fSumX2;
3087         double fHelp;
3088         if (bConstant)
3089         {
3090             double fIntercept = fMeanY - fSlope * fMeanX;
3091             for (SCSIZE i = 0; i < nCountXN; i++)
3092             {
3093                 fHelp = pMatNewX->GetDouble(i)*fSlope + fIntercept;
3094                 pResMat->PutDouble(_bGrowth ? exp(fHelp) : fHelp, i);
3095             }
3096         }
3097         else
3098         {
3099             for (SCSIZE i = 0; i < nCountXN; i++)
3100             {
3101                 fHelp = pMatNewX->GetDouble(i)*fSlope;
3102                 pResMat->PutDouble(_bGrowth ? exp(fHelp) : fHelp, i);
3103             }
3104         }
3105     }
3106     else // calculate multiple regression;
3107     {
3108         if (nCase ==2) // Y is column
3109         {
3110             std::vector< double> aVecR(N); // for QR decomposition
3111             // Enough memory for needed matrices?
3112             ScMatrixRef pMeans = GetNewMat(K, 1, /*bEmpty*/true); // mean of each column
3113             ScMatrixRef pSlopes = GetNewMat(1,K, /*bEmpty*/true); // from b1 to bK
3114             if (!pMeans || !pSlopes)
3115             {
3116                 PushError(FormulaError::CodeOverflow);
3117                 return;
3118             }
3119             if (bConstant)
3120             {
3121                 lcl_CalculateColumnMeans(pMatX, pMeans, K, N);
3122                 lcl_CalculateColumnsDelta(pMatX, pMeans, K, N);
3123             }
3124             if (!lcl_CalculateQRdecomposition(pMatX, aVecR, K, N))
3125             {
3126                 PushNoValue();
3127                 return;
3128             }
3129             // Later on we will divide by elements of aVecR, so make sure
3130             // that they aren't zero.
3131             bool bIsSingular=false;
3132             for (SCSIZE row=0; row < K && !bIsSingular; row++)
3133                 bIsSingular = aVecR[row] == 0.0;
3134             if (bIsSingular)
3135             {
3136                 PushNoValue();
3137                 return;
3138             }
3139             // Z := Q' Y; Y is overwritten with result Z
3140             for (SCSIZE col = 0; col < K; col++)
3141             {
3142                 lcl_ApplyHouseholderTransformation(pMatX, col, pMatY, N);
3143             }
3144             // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
3145             // result Z should have zeros for index>=K; if not, ignore values
3146             for (SCSIZE col = 0; col < K ; col++)
3147             {
3148                 pSlopes->PutDouble( pMatY->GetDouble(col), col);
3149             }
3150             lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, false);
3151 
3152             // Fill result matrix
3153             lcl_MFastMult(pMatNewX,pSlopes,pResMat,nRXN,K,1);
3154             if (bConstant)
3155             {
3156                 double fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K);
3157                 for (SCSIZE row = 0; row < nRXN; row++)
3158                     pResMat->PutDouble(pResMat->GetDouble(row)+fIntercept, row);
3159             }
3160             if (_bGrowth)
3161             {
3162                 for (SCSIZE i = 0; i < nRXN; i++)
3163                     pResMat->PutDouble(exp(pResMat->GetDouble(i)), i);
3164             }
3165         }
3166         else
3167         { // nCase == 3, Y is row, all matrices are transposed
3168 
3169             std::vector< double> aVecR(N); // for QR decomposition
3170             // Enough memory for needed matrices?
3171             ScMatrixRef pMeans = GetNewMat(1, K, /*bEmpty*/true); // mean of each row
3172             ScMatrixRef pSlopes = GetNewMat(K,1, /*bEmpty*/true); // row from b1 to bK
3173             if (!pMeans || !pSlopes)
3174             {
3175                 PushError(FormulaError::CodeOverflow);
3176                 return;
3177             }
3178             if (bConstant)
3179             {
3180                 lcl_CalculateRowMeans(pMatX, pMeans, N, K);
3181                 lcl_CalculateRowsDelta(pMatX, pMeans, N, K);
3182             }
3183             if (!lcl_TCalculateQRdecomposition(pMatX, aVecR, K, N))
3184             {
3185                 PushNoValue();
3186                 return;
3187             }
3188             // Later on we will divide by elements of aVecR, so make sure
3189             // that they aren't zero.
3190             bool bIsSingular=false;
3191             for (SCSIZE row=0; row < K && !bIsSingular; row++)
3192                 bIsSingular = aVecR[row] == 0.0;
3193             if (bIsSingular)
3194             {
3195                 PushNoValue();
3196                 return;
3197             }
3198             // Z := Q' Y; Y is overwritten with result Z
3199             for (SCSIZE row = 0; row < K; row++)
3200             {
3201                 lcl_TApplyHouseholderTransformation(pMatX, row, pMatY, N);
3202             }
3203             // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
3204             // result Z should have zeros for index>=K; if not, ignore values
3205             for (SCSIZE col = 0; col < K ; col++)
3206             {
3207                 pSlopes->PutDouble( pMatY->GetDouble(col), col);
3208             }
3209             lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, true);
3210 
3211             // Fill result matrix
3212             lcl_MFastMult(pSlopes,pMatNewX,pResMat,1,K,nCXN);
3213             if (bConstant)
3214             {
3215                 double fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K);
3216                 for (SCSIZE col = 0; col < nCXN; col++)
3217                     pResMat->PutDouble(pResMat->GetDouble(col)+fIntercept, col);
3218             }
3219             if (_bGrowth)
3220             {
3221                 for (SCSIZE i = 0; i < nCXN; i++)
3222                     pResMat->PutDouble(exp(pResMat->GetDouble(i)), i);
3223             }
3224         }
3225     }
3226     PushMatrix(pResMat);
3227 }
3228 
ScMatRef()3229 void ScInterpreter::ScMatRef()
3230 {
3231     // In case it contains relative references resolve them as usual.
3232     Push( *pCur );
3233     ScAddress aAdr;
3234     PopSingleRef( aAdr );
3235 
3236     ScRefCellValue aCell(mrDoc, aAdr);
3237 
3238     if (aCell.getType() != CELLTYPE_FORMULA)
3239     {
3240         PushError( FormulaError::NoRef );
3241         return;
3242     }
3243 
3244     if (aCell.getFormula()->IsRunning())
3245     {
3246         // Twisted odd corner case where an array element's cell tries to
3247         // access the top left matrix while it is still running, see tdf#88737
3248         // This is a hackish workaround, not a general solution, the matrix
3249         // isn't available anyway and FormulaError::CircularReference would be set.
3250         PushError( FormulaError::RetryCircular );
3251         return;
3252     }
3253 
3254     const ScMatrix* pMat = aCell.getFormula()->GetMatrix();
3255     if (pMat)
3256     {
3257         SCSIZE nCols, nRows;
3258         pMat->GetDimensions( nCols, nRows );
3259         SCSIZE nC = static_cast<SCSIZE>(aPos.Col() - aAdr.Col());
3260         SCSIZE nR = static_cast<SCSIZE>(aPos.Row() - aAdr.Row());
3261 #if 0
3262         // XXX: this could be an additional change for tdf#145085 to not
3263         // display the URL in a voluntary entered 2-rows array context.
3264         // However, that might as well be used on purpose to implement a check
3265         // on the URL, which existing documents may have done, the more that
3266         // before the accompanying change of
3267         // ScFormulaCell::GetResultDimensions() the cell array was forced to
3268         // two rows. Do not change without compelling reason. Note that this
3269         // repeating top cell is what Excel implements, but it has no
3270         // additional value so probably isn't used there. Exporting to and
3271         // using in Excel though will lose this capability.
3272         if (aCell.mpFormula->GetCode()->IsHyperLink())
3273         {
3274             // Row 2 element is the URL that is not to be displayed, fake a
3275             // 1-row cell-text-only matrix that is repeated.
3276             assert(nRows == 2);
3277             nR = 0;
3278         }
3279 #endif
3280         if ((nCols <= nC && nCols != 1) || (nRows <= nR && nRows != 1))
3281             PushNA();
3282         else
3283         {
3284             const ScMatrixValue nMatVal = pMat->Get( nC, nR);
3285             ScMatValType nMatValType = nMatVal.nType;
3286 
3287             if (ScMatrix::IsNonValueType( nMatValType))
3288             {
3289                 if (ScMatrix::IsEmptyPathType( nMatValType))
3290                 {   // result of empty false jump path
3291                     nFuncFmtType = SvNumFormatType::LOGICAL;
3292                     PushInt(0);
3293                 }
3294                 else if (ScMatrix::IsEmptyType( nMatValType))
3295                 {
3296                     // Not inherited (really?) and display as empty string, not 0.
3297                     PushTempToken( new ScEmptyCellToken( false, true));
3298                 }
3299                 else
3300                     PushString( nMatVal.GetString() );
3301             }
3302             else
3303             {
3304                 // Determine nFuncFmtType type before PushDouble().
3305                 mrDoc.GetNumberFormatInfo(mrContext, nCurFmtType, nCurFmtIndex, aAdr);
3306                 nFuncFmtType = nCurFmtType;
3307                 nFuncFmtIndex = nCurFmtIndex;
3308                 PushDouble(nMatVal.fVal);  // handles DoubleError
3309             }
3310         }
3311     }
3312     else
3313     {
3314         // Determine nFuncFmtType type before PushDouble().
3315         mrDoc.GetNumberFormatInfo(mrContext, nCurFmtType, nCurFmtIndex, aAdr);
3316         nFuncFmtType = nCurFmtType;
3317         nFuncFmtIndex = nCurFmtIndex;
3318         // If not a result matrix, obtain the cell value.
3319         FormulaError nErr = aCell.getFormula()->GetErrCode();
3320         if (nErr != FormulaError::NONE)
3321             PushError( nErr );
3322         else if (aCell.getFormula()->IsValue())
3323             PushDouble(aCell.getFormula()->GetValue());
3324         else
3325         {
3326             svl::SharedString aVal = aCell.getFormula()->GetString();
3327             PushString( aVal );
3328         }
3329     }
3330 }
3331 
ScInfo()3332 void ScInterpreter::ScInfo()
3333 {
3334     if( !MustHaveParamCount( GetByte(), 1 ) )
3335         return;
3336 
3337     OUString aStr = GetString().getString();
3338     ScCellKeywordTranslator::transKeyword(aStr, ScGlobal::GetLocale(), ocInfo);
3339     if( aStr == "SYSTEM" )
3340         PushString( u"" SC_INFO_OSVERSION ""_ustr );
3341     else if( aStr == "OSVERSION" )
3342 #if (defined LINUX || defined __FreeBSD__)
3343         PushString(Application::GetOSVersion());
3344 #elif defined MACOSX
3345         // TODO tdf#140286 handle MACOSX version to get result compatible to Excel
3346         PushString("Windows (32-bit) NT 5.01");
3347 #else // handle Windows (WNT, WIN_NT, WIN32, _WIN32)
3348         // TODO tdf#140286 handle Windows version to get a result compatible to Excel
3349         PushString( "Windows (32-bit) NT 5.01" );
3350 #endif
3351     else if( aStr == "RELEASE" )
3352         PushString( ::utl::Bootstrap::getBuildIdData( OUString() ) );
3353     else if( aStr == "NUMFILE" )
3354         PushDouble( 1 );
3355     else if( aStr == "RECALC" )
3356         PushString( ScResId( mrDoc.GetAutoCalc() ? STR_RECALC_AUTO : STR_RECALC_MANUAL ) );
3357     else if (aStr == "DIRECTORY" || aStr == "MEMAVAIL" || aStr == "MEMUSED" || aStr == "ORIGIN" || aStr == "TOTMEM")
3358         PushNA();
3359     else
3360         PushIllegalArgument();
3361 }
3362 
3363 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3364