/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// Multiply n x m Mat A with m x l Mat B to n x l Mat R void lcl_MFastMult(const ScMatrixRef& pA, const ScMatrixRef& pB, const ScMatrixRef& pR,
SCSIZE n, SCSIZE m, SCSIZE l)
{ for (SCSIZE row = 0; row < n; row++)
{ for (SCSIZE col = 0; col < l; col++)
{ // result element(col, row) =sum[ (row of A) * (column of B)]
KahanSum fSum = 0.0; for (SCSIZE k = 0; k < m; k++)
fSum += pA->GetDouble(k,row) * pB->GetDouble(col,k);
pR->PutDouble(fSum.get(), col, row);
}
}
}
}
double ScInterpreter::ScGetGCD(double fx, double fy)
{ // By ODFF definition GCD(0,a) => a. This is also vital for the code in // ScGCD() to work correctly with a preset fy=0.0 if (fy == 0.0) return fx; elseif (fx == 0.0) return fy; else
{ double fz = fmod(fx, fy); while (fz > 0.0)
{
fx = fy;
fy = fz;
fz = fmod(fx, fy);
} return fy;
}
}
void ScInterpreter::ScGCD()
{ short nParamCount = GetByte(); if ( !MustHaveParamCountMin( nParamCount, 1 ) ) return;
double fx, fy = 0.0;
ScRange aRange;
size_t nRefInList = 0; while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
{ switch (GetStackType())
{ case svDouble : case svString: case svSingleRef:
{
fx = ::rtl::math::approxFloor( GetDouble()); if (fx < 0.0)
{
PushIllegalArgument(); return;
}
fy = ScGetGCD(fx, fy);
} break; case svDoubleRef : case svRefList :
{
FormulaError nErr = FormulaError::NONE;
PopDoubleRef( aRange, nParamCount, nRefInList); double nCellVal;
ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags ); if (aValIter.GetFirst(nCellVal, nErr))
{ do
{
fx = ::rtl::math::approxFloor( nCellVal); if (fx < 0.0)
{
PushIllegalArgument(); return;
}
fy = ScGetGCD(fx, fy);
} while (nErr == FormulaError::NONE && aValIter.GetNext(nCellVal, nErr));
}
SetError(nErr);
} break; case svMatrix : case svExternalSingleRef: case svExternalDoubleRef:
{
ScMatrixRef pMat = GetMatrix(); if (pMat)
{
SCSIZE nC, nR;
pMat->GetDimensions(nC, nR); if (nC == 0 || nR == 0)
SetError(FormulaError::IllegalArgument); else
{ double nVal = pMat->GetGcd();
fy = ScGetGCD(nVal,fy);
}
}
} break; default : SetError(FormulaError::IllegalParameter); break;
}
}
PushDouble(fy);
}
void ScInterpreter:: ScLCM()
{ short nParamCount = GetByte(); if ( !MustHaveParamCountMin( nParamCount, 1 ) ) return;
double fx, fy = 1.0;
ScRange aRange;
size_t nRefInList = 0; while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
{ switch (GetStackType())
{ case svDouble : case svString: case svSingleRef:
{
fx = ::rtl::math::approxFloor( GetDouble()); if (fx < 0.0)
{
PushIllegalArgument(); return;
} if (fx == 0.0 || fy == 0.0)
fy = 0.0; else
fy = fx * fy / ScGetGCD(fx, fy);
} break; case svDoubleRef : case svRefList :
{
FormulaError nErr = FormulaError::NONE;
PopDoubleRef( aRange, nParamCount, nRefInList); double nCellVal;
ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags ); if (aValIter.GetFirst(nCellVal, nErr))
{ do
{
fx = ::rtl::math::approxFloor( nCellVal); if (fx < 0.0)
{
PushIllegalArgument(); return;
} if (fx == 0.0 || fy == 0.0)
fy = 0.0; else
fy = fx * fy / ScGetGCD(fx, fy);
} while (nErr == FormulaError::NONE && aValIter.GetNext(nCellVal, nErr));
}
SetError(nErr);
} break; case svMatrix : case svExternalSingleRef: case svExternalDoubleRef:
{
ScMatrixRef pMat = GetMatrix(); if (pMat)
{
SCSIZE nC, nR;
pMat->GetDimensions(nC, nR); if (nC == 0 || nR == 0)
SetError(FormulaError::IllegalArgument); else
{ double nVal = pMat->GetLcm();
fy = (nVal * fy ) / ScGetGCD(nVal, fy);
}
}
} break; default : SetError(FormulaError::IllegalParameter); break;
}
}
PushDouble(fy);
}
void ScInterpreter::MakeMatNew(ScMatrixRef& rMat, SCSIZE nC, SCSIZE nR)
{
rMat->SetErrorInterpreter( this); // A temporary matrix is mutable and ScMatrix::CloneIfConst() returns the // very matrix.
rMat->SetMutable();
SCSIZE nCols, nRows;
rMat->GetDimensions( nCols, nRows); if ( nCols != nC || nRows != nR )
{ // arbitrary limit of elements exceeded
SetError( FormulaError::MatrixSize);
rMat.reset();
}
}
ScMatrixRef ScInterpreter::GetNewMat(SCSIZE nC, SCSIZE nR, bool bEmpty)
{
ScMatrixRef pMat; if (bEmpty)
pMat = new ScMatrix(nC, nR); else
pMat = new ScMatrix(nC, nR, 0.0);
MakeMatNew(pMat, nC, nR); return pMat;
}
if (nTab1 == nTab2 && pToken)
{ const ScComplexRefData& rCRef = *pToken->GetDoubleRef(); if (rCRef.IsTrimToData())
{ // Clamp the size of the matrix area to rows which actually contain data. // For e.g. SUM(IF over an entire column, this can make a big difference, // But let's not trim the empty space from the top or left as this matters // at least in matrix formulas involving IF(). // Refer ScCompiler::AnnotateTrimOnDoubleRefs() where double-refs are // flagged for trimming.
SCCOL nTempCol = nCol1;
SCROW nTempRow = nRow1;
mrDoc.ShrinkToDataArea(nTab1, nTempCol, nTempRow, nCol2, nRow2);
}
}
if (!ScMatrix::IsSizeAllocatable( nMatCols, nMatRows))
{
SetError(FormulaError::MatrixSize); return nullptr;
}
ScTokenMatrixMap::const_iterator aIter; if (pToken && ((aIter = maTokenMatrixMap.find( pToken)) != maTokenMatrixMap.end()))
{ /* XXX casting const away here is ugly; ScMatrixToken (to which the * result of this function usually is assigned) should not be forced to * carry a ScConstMatrixRef though. * TODO: a matrix already stored in pTokenMatrixMap should be * read-only and have a copy-on-write mechanism. Previously all tokens
* were modifiable so we're already better than before ... */ returnconst_cast<FormulaToken*>((*aIter).second.get())->GetMatrix();
}
if (!bCalcAsShown)
{ // Use fast array fill.
mrDoc.FillMatrix(*pMat, nTab1, nCol1, nRow1, nCol2, nRow2);
} else
{ // Use slower ScCellIterator to round values.
// TODO: this probably could use CellBucket for faster storage, see // sc/source/core/data/column2.cxx and FillMatrixHandler, and then be // moved to a function on its own, and/or squeeze the rounding into a // similar FillMatrixHandler that would need to keep track of the cell // position then.
// Set position where the next entry is expected.
SCROW nNextRow = nRow1;
SCCOL nNextCol = nCol1; // Set last position as if there was a previous entry.
SCROW nThisRow = nRow2;
SCCOL nThisCol = nCol1 - 1;
void ScInterpreter::MEMat(const ScMatrixRef& mM, SCSIZE n)
{
mM->FillDouble(0.0, 0, 0, n-1, n-1); for (SCSIZE i = 0; i < n; i++)
mM->PutDouble(1.0, i, i);
}
/* Matrix LUP decomposition according to the pseudocode of "Introduction to * Algorithms" by Cormen, Leiserson, Rivest, Stein. * * Added scaling for numeric stability. * * Given an n x n nonsingular matrix A, find a permutation matrix P, a unit * lower-triangular matrix L, and an upper-triangular matrix U such that PA=LU. * Compute L and U "in place" in the matrix A, the original content is * destroyed. Note that the diagonal elements of the U triangular matrix * replace the diagonal elements of the L-unit matrix (that are each ==1). The * permutation matrix P is an array, where P[i]=j means that the i-th row of P * contains a 1 in column j. Additionally keep track of the number of * permutations (row exchanges). * * Returns 0 if a singular matrix is encountered, else +1 if an even number of * permutations occurred, or -1 if odd, which is the sign of the determinant. * This may be used to calculate the determinant by multiplying the sign with * the product of the diagonal elements of the LU matrix.
*/ staticint lcl_LUP_decompose( ScMatrix* mA, const SCSIZE n,
::std::vector< SCSIZE> & P )
{ int nSign = 1; // Find scale of each row.
::std::vector< double> aScale(n); for (SCSIZE i=0; i < n; ++i)
{ double fMax = 0.0; for (SCSIZE j=0; j < n; ++j)
{ double fTmp = fabs( mA->GetDouble( j, i)); if (fMax < fTmp)
fMax = fTmp;
} if (fMax == 0.0) return 0; // singular matrix
aScale[i] = 1.0 / fMax;
} // Represent identity permutation, P[i]=i for (SCSIZE i=0; i < n; ++i)
P[i] = i; // "Recursion" on the diagonal.
SCSIZE l = n - 1; for (SCSIZE k=0; k < l; ++k)
{ // Implicit pivoting. With the scale found for a row, compare values of // a column and pick largest. double fMax = 0.0; double fScale = aScale[k];
SCSIZE kp = k; for (SCSIZE i = k; i < n; ++i)
{ double fTmp = fScale * fabs( mA->GetDouble( k, i)); if (fMax < fTmp)
{
fMax = fTmp;
kp = i;
}
} if (fMax == 0.0) return 0; // singular matrix // Swap rows. The pivot element will be at mA[k,kp] (row,col notation) if (k != kp)
{ // permutations
SCSIZE nTmp = P[k];
P[k] = P[kp];
P[kp] = nTmp;
nSign = -nSign; // scales double fTmp = aScale[k];
aScale[k] = aScale[kp];
aScale[kp] = fTmp; // elements for (SCSIZE i=0; i < n; ++i)
{ double fMatTmp = mA->GetDouble( i, k);
mA->PutDouble( mA->GetDouble( i, kp), i, k);
mA->PutDouble( fMatTmp, i, kp);
}
} // Compute Schur complement. for (SCSIZE i = k+1; i < n; ++i)
{ double fNum = mA->GetDouble( k, i); double fDen = mA->GetDouble( k, k);
mA->PutDouble( fNum/fDen, k, i); for (SCSIZE j = k+1; j < n; ++j)
mA->PutDouble( ( mA->GetDouble( j, i) * fDen -
fNum * mA->GetDouble( j, k) ) / fDen, j, i);
}
} #ifdef DEBUG_SC_LUP_DECOMPOSITION
fprintf( stderr, "\n%s\n", "lcl_LUP_decompose(): LU"); for (SCSIZE i=0; i < n; ++i)
{ for (SCSIZE j=0; j < n; ++j)
fprintf( stderr, "%8.2g ", mA->GetDouble( j, i));
fprintf( stderr, "\n%s\n", "");
}
fprintf( stderr, "\n%s\n", "lcl_LUP_decompose(): P"); for (SCSIZE j=0; j < n; ++j)
fprintf( stderr, "%5u ", (unsigned)P[j]);
fprintf( stderr, "\n%s\n", ""); #endif
bool bSingular=false; for (SCSIZE i=0; i<n && !bSingular; i++)
bSingular = (mA->GetDouble(i,i)) == 0.0; if (bSingular)
nSign = 0;
return nSign;
}
/* Solve a LUP decomposed equation Ax=b. LU is a combined matrix of L and U * triangulars and P the permutation vector as obtained from * lcl_LUP_decompose(). B is the right-hand side input vector, X is used to * return the solution vector.
*/ staticvoid lcl_LUP_solve( const ScMatrix* mLU, const SCSIZE n, const ::std::vector< SCSIZE> & P, const ::std::vector< double> & B,
::std::vector< double> & X )
{
SCSIZE nFirst = SCSIZE_MAX; // Ax=b => PAx=Pb, with decomposition LUx=Pb. // Define y=Ux and solve for y in Ly=Pb using forward substitution. for (SCSIZE i=0; i < n; ++i)
{
KahanSum fSum = B[P[i]]; // Matrix inversion comes with a lot of zeros in the B vectors, we // don't have to do all the computing with results multiplied by zero. // Until then, simply lookout for the position of the first nonzero // value. if (nFirst != SCSIZE_MAX)
{ for (SCSIZE j = nFirst; j < i; ++j)
fSum -= mLU->GetDouble( j, i) * X[j]; // X[j] === y[j]
} elseif (fSum != 0)
nFirst = i;
X[i] = fSum.get(); // X[i] === y[i]
} // Solve for x in Ux=y using back substitution. for (SCSIZE i = n; i--; )
{
KahanSum fSum = X[i]; // X[i] === y[i] for (SCSIZE j = i+1; j < n; ++j)
fSum -= mLU->GetDouble( j, i) * X[j]; // X[j] === x[j]
X[i] = fSum.get() / mLU->GetDouble( i, i); // X[i] === x[i]
} #ifdef DEBUG_SC_LUP_DECOMPOSITION
fprintf( stderr, "\n%s\n", "lcl_LUP_solve():"); for (SCSIZE i=0; i < n; ++i)
fprintf( stderr, "%8.2g ", X[i]);
fprintf( stderr, "%s\n", ""); #endif
}
if (ScCalcConfig::isOpenCLEnabled())
{
sc::FormulaGroupInterpreter *pInterpreter = sc::FormulaGroupInterpreter::getStatic(); if (pInterpreter != nullptr)
{
ScMatrixRef xResMat = pInterpreter->inverseMatrix(*pMat); if (xResMat)
{
PushMatrix(xResMat); return;
}
}
}
if ( nC != nR || nC == 0 )
PushIllegalArgument(); elseif (!ScMatrix::IsSizeAllocatable( nC, nR))
PushError( FormulaError::MatrixSize); else
{ // LUP decomposition is done inplace, use copy.
ScMatrixRef xLU = pMat->Clone(); // The result matrix.
ScMatrixRef xY = GetNewMat( nR, nR, /*bEmpty*/true ); if (!xLU || !xY)
PushError( FormulaError::CodeOverflow); else
{
::std::vector< SCSIZE> P(nR); int nDetSign = lcl_LUP_decompose( xLU.get(), nR, P); if (!nDetSign)
PushIllegalArgument(); else
{ // Solve equation for each column.
::std::vector< double> B(nR);
::std::vector< double> X(nR); for (SCSIZE j=0; j < nR; ++j)
{ for (SCSIZE i=0; i < nR; ++i)
B[i] = 0.0;
B[j] = 1.0;
lcl_LUP_solve( xLU.get(), nR, P, B, X); for (SCSIZE i=0; i < nR; ++i)
xY->PutDouble( X[i], j, i);
} #ifdef DEBUG_SC_LUP_DECOMPOSITION /* Possible checks for ill-condition: * 1. Scale matrix, invert scaled matrix. If there are * elements of the inverted matrix that are several * orders of magnitude greater than 1 => * ill-conditioned. * Just how much is "several orders"? * 2. Invert the inverted matrix and assess whether the * result is sufficiently close to the original matrix. * If not => ill-conditioned. * Just what is sufficient? * 3. Multiplying the inverse by the original matrix should * produce a result sufficiently close to the identity * matrix. * Just what is sufficient? * * The following is #3.
*/ constdouble fInvEpsilon = 1.0E-7;
ScMatrixRef xR = GetNewMat( nR, nR); if (xR)
{
ScMatrix* pR = xR.get();
lcl_MFastMult( pMat, xY.get(), pR, nR, nR, nR);
fprintf( stderr, "\n%s\n", "ScMatInv(): mult-identity"); for (SCSIZE i=0; i < nR; ++i)
{ for (SCSIZE j=0; j < nR; ++j)
{ double fTmp = pR->GetDouble( j, i);
fprintf( stderr, "%8.2g ", fTmp); if (fabs( fTmp - (i == j)) > fInvEpsilon)
SetError( FormulaError::IllegalArgument);
}
fprintf( stderr, "\n%s\n", "");
}
} #endif if (nGlobalError != FormulaError::NONE)
PushError( nGlobalError); else
PushMatrix( xY);
}
}
}
}
// 4th argument is the step number (optional) double nSteps = 1.0; if (nParamCount == 4)
nSteps = GetDoubleWithDefault(nSteps);
// 3d argument is the start number (optional) double nStart = 1.0; if (nParamCount >= 3)
nStart = GetDoubleWithDefault(nStart);
// 2nd argument is the col number (optional)
sal_Int32 nColumns = 1; if (nParamCount >= 2)
{
nColumns = GetInt32WithDefault(nColumns); if (nColumns < 1)
{
PushIllegalArgument(); return;
}
}
// 1st argument is the row number (required)
sal_Int32 nRows = GetInt32WithDefault(1); if (nRows < 1)
{
PushIllegalArgument(); return;
}
if (nGlobalError != FormulaError::NONE)
{
PushError(nGlobalError); return;
}
/** Minimum extent of one result matrix dimension. For a row or column vector to be replicated the larger matrix dimension is returned, else the smaller dimension.
*/ static SCSIZE lcl_GetMinExtent( SCSIZE n1, SCSIZE n2 )
{ if (n1 == 1) return n2; elseif (n2 == 1) return n1; elseif (n1 < n2) return n1; else return n2;
}
void ScInterpreter::ScSumProduct()
{ short nParamCount = GetByte(); if ( !MustHaveParamCountMin( nParamCount, 1) ) return;
// XXX NOTE: Excel returns #VALUE! for reference list and 0 (why?) for // array of references. We calculate the proper individual arrays if sizes // match.
if (nBinSize != aBinIndexOrder.size())
{
PushIllegalArgument(); return;
}
SCSIZE j;
SCSIZE i = 0; for (j = 0; j < nBinSize; ++j)
{
SCSIZE nCount = 0; while (i < nDataSize && aDataArray[i] <= aBinArray[j])
{
++nCount;
++i;
}
pResMat->PutDouble(static_cast<double>(nCount), aBinIndexOrder[j]);
}
pResMat->PutDouble(static_cast<double>(nDataSize-i), j);
PushMatrix(pResMat);
}
namespace {
// Helper methods for LINEST/LOGEST and TREND/GROWTH // All matrices must already exist and have the needed size, no control tests // done. Those methods, which names start with lcl_T, are adapted to case 3, // where Y (=observed values) is given as row. // Remember, ScMatrix matrices are zero based, index access (column,row).
// <A;B> over all elements; uses the matrices as vectors of length M double lcl_GetSumProduct(const ScMatrixRef& pMatA, const ScMatrixRef& pMatB, SCSIZE nM)
{
KahanSum fSum = 0.0; for (SCSIZE i=0; i<nM; i++)
fSum += pMatA->GetDouble(i) * pMatB->GetDouble(i); return fSum.get();
}
// Special version for use within QR decomposition. // Euclidean norm of column index C starting in row index R; // matrix A has count N rows. double lcl_GetColumnEuclideanNorm(const ScMatrixRef& pMatA, SCSIZE nC, SCSIZE nR, SCSIZE nN)
{
KahanSum fNorm = 0.0; for (SCSIZE row=nR; row<nN; row++)
fNorm += (pMatA->GetDouble(nC,row)) * (pMatA->GetDouble(nC,row)); return sqrt(fNorm.get());
}
// Euclidean norm of row index R starting in column index C; // matrix A has count N columns. double lcl_TGetColumnEuclideanNorm(const ScMatrixRef& pMatA, SCSIZE nR, SCSIZE nC, SCSIZE nN)
{
KahanSum fNorm = 0.0; for (SCSIZE col=nC; col<nN; col++)
fNorm += (pMatA->GetDouble(col,nR)) * (pMatA->GetDouble(col,nR)); return sqrt(fNorm.get());
}
// Special version for use within QR decomposition. // Maximum norm of column index C starting in row index R; // matrix A has count N rows. double lcl_GetColumnMaximumNorm(const ScMatrixRef& pMatA, SCSIZE nC, SCSIZE nR, SCSIZE nN)
{ double fNorm = 0.0; for (SCSIZE row=nR; row<nN; row++)
{ double fVal = fabs(pMatA->GetDouble(nC,row)); if (fNorm < fVal)
fNorm = fVal;
} return fNorm;
}
// Maximum norm of row index R starting in col index C; // matrix A has count N columns. double lcl_TGetColumnMaximumNorm(const ScMatrixRef& pMatA, SCSIZE nR, SCSIZE nC, SCSIZE nN)
{ double fNorm = 0.0; for (SCSIZE col=nC; col<nN; col++)
{ double fVal = fabs(pMatA->GetDouble(col,nR)); if (fNorm < fVal)
fNorm = fVal;
} return fNorm;
}
// Special version for use within QR decomposition. // <A(Ca);B(Cb)> starting in row index R; // Ca and Cb are indices of columns, matrices A and B have count N rows. double lcl_GetColumnSumProduct(const ScMatrixRef& pMatA, SCSIZE nCa, const ScMatrixRef& pMatB, SCSIZE nCb, SCSIZE nR, SCSIZE nN)
{
KahanSum fResult = 0.0; for (SCSIZE row=nR; row<nN; row++)
fResult += pMatA->GetDouble(nCa,row) * pMatB->GetDouble(nCb,row); return fResult.get();
}
// <A(Ra);B(Rb)> starting in column index C; // Ra and Rb are indices of rows, matrices A and B have count N columns. double lcl_TGetColumnSumProduct(const ScMatrixRef& pMatA, SCSIZE nRa, const ScMatrixRef& pMatB, SCSIZE nRb, SCSIZE nC, SCSIZE nN)
{
KahanSum fResult = 0.0; for (SCSIZE col=nC; col<nN; col++)
fResult += pMatA->GetDouble(col,nRa) * pMatB->GetDouble(col,nRb); return fResult.get();
}
// no mathematical signum, but used to switch between adding and subtracting double lcl_GetSign(double fValue)
{ return (fValue >= 0.0 ? 1.0 : -1.0 );
}
/* Calculates a QR decomposition with Householder reflection. * For each NxK matrix A exists a decomposition A=Q*R with an orthogonal * NxN matrix Q and a NxK matrix R. * Q=H1*H2*...*Hk with Householder matrices H. Such a householder matrix can * be build from a vector u by H=I-(2/u'u)*(u u'). This vectors u are returned * in the columns of matrix A, overwriting the old content. * The matrix R has a quadric upper part KxK with values in the upper right * triangle and zeros in all other elements. Here the diagonal elements of R * are stored in the vector R and the other upper right elements in the upper * right of the matrix A. * The function returns false, if calculation breaks. But because of round-off * errors singularity is often not detected.
*/ bool lcl_CalculateQRdecomposition(const ScMatrixRef& pMatA,
::std::vector< double>& pVecR, SCSIZE nK, SCSIZE nN)
{ // ScMatrix matrices are zero based, index access (column,row) for (SCSIZE col = 0; col <nK; col++)
{ // calculate vector u of the householder transformation constdouble fScale = lcl_GetColumnMaximumNorm(pMatA, col, col, nN); if (fScale == 0.0)
{ // A is singular returnfalse;
} for (SCSIZE row = col; row <nN; row++)
pMatA->PutDouble( pMatA->GetDouble(col,row)/fScale, col, row);
// same with transposed matrix A, N is count of columns, K count of rows bool lcl_TCalculateQRdecomposition(const ScMatrixRef& pMatA,
::std::vector< double>& pVecR, SCSIZE nK, SCSIZE nN)
{ double fSum ; // ScMatrix matrices are zero based, index access (column,row) for (SCSIZE row = 0; row <nK; row++)
{ // calculate vector u of the householder transformation constdouble fScale = lcl_TGetColumnMaximumNorm(pMatA, row, row, nN); if (fScale == 0.0)
{ // A is singular returnfalse;
} for (SCSIZE col = row; col <nN; col++)
pMatA->PutDouble( pMatA->GetDouble(col,row)/fScale, col, row);
// apply Householder transformation to A for (SCSIZE r=row+1; r<nK; r++)
{
fSum =lcl_TGetColumnSumProduct(pMatA, row, pMatA, r, row, nN); for (SCSIZE col = row; col <nN; col++)
pMatA->PutDouble(
pMatA->GetDouble(col,r) - fSum * fFactor * pMatA->GetDouble(col,row), col, r);
}
} returntrue;
}
/* Applies a Householder transformation to a column vector Y with is given as * Nx1 Matrix. The vector u, from which the Householder transformation is built, * is the column part in matrix A, with column index C, starting with row * index C. A is the result of the QR decomposition as obtained from * lcl_CalculateQRdecomposition.
*/ void lcl_ApplyHouseholderTransformation(const ScMatrixRef& pMatA, SCSIZE nC, const ScMatrixRef& pMatY, SCSIZE nN)
{ // ScMatrix matrices are zero based, index access (column,row) double fDenominator = lcl_GetColumnSumProduct(pMatA, nC, pMatA, nC, nC, nN); double fNumerator = lcl_GetColumnSumProduct(pMatA, nC, pMatY, 0, nC, nN); double fFactor = 2.0 * (fNumerator/fDenominator); for (SCSIZE row = nC; row < nN; row++)
pMatY->PutDouble(
pMatY->GetDouble(row) - fFactor * pMatA->GetDouble(nC,row), row);
}
// Same with transposed matrices A and Y. void lcl_TApplyHouseholderTransformation(const ScMatrixRef& pMatA, SCSIZE nR, const ScMatrixRef& pMatY, SCSIZE nN)
{ // ScMatrix matrices are zero based, index access (column,row) double fDenominator = lcl_TGetColumnSumProduct(pMatA, nR, pMatA, nR, nR, nN); double fNumerator = lcl_TGetColumnSumProduct(pMatA, nR, pMatY, 0, nR, nN); double fFactor = 2.0 * (fNumerator/fDenominator); for (SCSIZE col = nR; col < nN; col++)
pMatY->PutDouble(
pMatY->GetDouble(col) - fFactor * pMatA->GetDouble(col,nR), col);
}
/* Solve for X in R*X=S using back substitution. The solution X overwrites S. * Uses R from the result of the QR decomposition of a NxK matrix A. * S is a column vector given as matrix, with at least elements on index * 0 to K-1; elements on index>=K are ignored. Vector R must not have zero * elements, no check is done.
*/ void lcl_SolveWithUpperRightTriangle(const ScMatrixRef& pMatA,
::std::vector< double>& pVecR, const ScMatrixRef& pMatS,
SCSIZE nK, bool bIsTransposed)
{ // ScMatrix matrices are zero based, index access (column,row)
SCSIZE row; // SCSIZE is never negative, therefore test with rowp1=row+1 for (SCSIZE rowp1 = nK; rowp1>0; rowp1--)
{
row = rowp1-1;
KahanSum fSum = pMatS->GetDouble(row); for (SCSIZE col = rowp1; col<nK ; col++) if (bIsTransposed)
fSum -= pMatA->GetDouble(row,col) * pMatS->GetDouble(col); else
fSum -= pMatA->GetDouble(col,row) * pMatS->GetDouble(col);
pMatS->PutDouble( fSum.get() / pVecR[row] , row);
}
}
/* Solve for X in R' * X= T using forward substitution. The solution X * overwrites T. Uses R from the result of the QR decomposition of a NxK * matrix A. T is a column vectors given as matrix, with at least elements on * index 0 to K-1; elements on index>=K are ignored. Vector R must not have * zero elements, no check is done.
*/ void lcl_SolveWithLowerLeftTriangle(const ScMatrixRef& pMatA,
::std::vector< double>& pVecR, const ScMatrixRef& pMatT,
SCSIZE nK, bool bIsTransposed)
{ // ScMatrix matrices are zero based, index access (column,row) for (SCSIZE row = 0; row < nK; row++)
{
KahanSum fSum = pMatT -> GetDouble(row); for (SCSIZE col=0; col < row; col++)
{ if (bIsTransposed)
fSum -= pMatA->GetDouble(col,row) * pMatT->GetDouble(col); else
fSum -= pMatA->GetDouble(row,col) * pMatT->GetDouble(col);
}
pMatT->PutDouble( fSum.get() / pVecR[row] , row);
}
}
/* Calculates Z = R * B * R is given in matrix A and vector VecR as obtained from the QR * decomposition in lcl_CalculateQRdecomposition. B and Z are column vectors * given as matrix with at least index 0 to K-1; elements on index>=K are * not used.
*/ void lcl_ApplyUpperRightTriangle(const ScMatrixRef& pMatA,
::std::vector< double>& pVecR, const ScMatrixRef& pMatB, const ScMatrixRef& pMatZ, SCSIZE nK, bool bIsTransposed)
{ // ScMatrix matrices are zero based, index access (column,row) for (SCSIZE row = 0; row < nK; row++)
{
KahanSum fSum = pVecR[row] * pMatB->GetDouble(row); for (SCSIZE col = row+1; col < nK; col++) if (bIsTransposed)
fSum += pMatA->GetDouble(row,col) * pMatB->GetDouble(col); else
fSum += pMatA->GetDouble(col,row) * pMatB->GetDouble(col);
pMatZ->PutDouble( fSum.get(), row);
}
}
// Calculates means of the columns of matrix X. X is a RxC matrix; // ResMat is a 1xC matrix (=row). void lcl_CalculateColumnMeans(const ScMatrixRef& pX, const ScMatrixRef& pResMat,
SCSIZE nC, SCSIZE nR)
{ for (SCSIZE i=0; i < nC; i++)
{
KahanSum fSum =0.0; for (SCSIZE k=0; k < nR; k++)
fSum += pX->GetDouble(i,k); // GetDouble(Column,Row)
pResMat ->PutDouble( fSum.get()/static_cast<double>(nR),i);
}
}
// Calculates means of the rows of matrix X. X is a RxC matrix; // ResMat is a Rx1 matrix (=column). void lcl_CalculateRowMeans(const ScMatrixRef& pX, const ScMatrixRef& pResMat,
SCSIZE nC, SCSIZE nR)
{ for (SCSIZE k=0; k < nR; k++)
{
KahanSum fSum = 0.0; for (SCSIZE i=0; i < nC; i++)
fSum += pX->GetDouble(i,k); // GetDouble(Column,Row)
pResMat ->PutDouble( fSum.get()/static_cast<double>(nC),k);
}
}
void lcl_CalculateColumnsDelta(const ScMatrixRef& pMat, const ScMatrixRef& pColumnMeans,
SCSIZE nC, SCSIZE nR)
{ for (SCSIZE i = 0; i < nC; i++) for (SCSIZE k = 0; k < nR; k++)
pMat->PutDouble( ::rtl::math::approxSub
(pMat->GetDouble(i,k) , pColumnMeans->GetDouble(i) ) , i, k);
}
void lcl_CalculateRowsDelta(const ScMatrixRef& pMat, const ScMatrixRef& pRowMeans,
SCSIZE nC, SCSIZE nR)
{ for (SCSIZE k = 0; k < nR; k++) for (SCSIZE i = 0; i < nC; i++)
pMat->PutDouble( ::rtl::math::approxSub
( pMat->GetDouble(i,k) , pRowMeans->GetDouble(k) ) , i, k);
}
// Fill default values in matrix X, transform Y to log(Y) in case LOGEST|GROWTH, // determine sizes of matrices X and Y, determine kind of regression, clone // Y in case LOGEST|GROWTH, if constant. bool ScInterpreter::CheckMatrix(bool _bLOG, sal_uInt8& nCase, SCSIZE& nCX,
SCSIZE& nCY, SCSIZE& nRX, SCSIZE& nRY, SCSIZE& M,
SCSIZE& N, ScMatrixRef& pMatX, ScMatrixRef& pMatY)
{
nCX = 0;
nCY = 0;
nRX = 0;
nRY = 0;
M = 0;
N = 0;
pMatY->GetDimensions(nCY, nRY); const SCSIZE nCountY = nCY * nRY; for ( SCSIZE i = 0; i < nCountY; i++ )
{ if (!pMatY->IsValue(i))
{
PushIllegalArgument(); returnfalse;
}
}
// The third parameter may not be missing in ODF, if the forth parameter // is present. But Excel allows it with default true, we too. if (nParamCount >= 3)
{ if (IsMissing())
{
Pop();
bConstant = true; // PushIllegalParameter(); if ODF behavior is desired // return;
} else
bConstant = GetBool();
} else
bConstant = true;
ScMatrixRef pMatX; if (nParamCount >= 2)
{ if (IsMissing())
{ //In ODF1.2 empty second parameter (which is two ;; ) is allowed
Pop();
pMatX = nullptr;
} else
{
pMatX = GetMatrix();
}
} else
pMatX = nullptr;
ScMatrixRef pMatY = GetMatrix(); if (!pMatY)
{
PushIllegalParameter(); return;
}
// 1 = simple; 2 = multiple with Y as column; 3 = multiple with Y as row
sal_uInt8 nCase;
SCSIZE nCX, nCY; // number of columns
SCSIZE nRX, nRY; //number of rows
SCSIZE K = 0, N = 0; // K=number of variables X, N=number of data samples if (!CheckMatrix(_bRKP,nCase,nCX,nCY,nRX,nRY,K,N,pMatX,pMatY))
{
PushIllegalParameter(); return;
}
// Enough data samples? if ((bConstant && (N<K+1)) || (!bConstant && (N<K)) || (N<1) || (K<1))
{
PushIllegalParameter(); return;
}
ScMatrixRef pResMat; if (bStats)
pResMat = GetNewMat(K+1,5, /*bEmpty*/true); else
pResMat = GetNewMat(K+1,1, /*bEmpty*/true); if (!pResMat)
{
PushError(FormulaError::CodeOverflow); return;
} // Fill unused cells in pResMat; order (column,row) if (bStats)
{ for (SCSIZE i=2; i<K+1; i++)
{
pResMat->PutError( FormulaError::NotAvailable, i, 2);
pResMat->PutError( FormulaError::NotAvailable, i, 3);
pResMat->PutError( FormulaError::NotAvailable, i, 4);
}
}
// Uses sum(x-MeanX)^2 and not [sum x^2]-N * MeanX^2 in case bConstant. // Clone constant matrices, so that Mat = Mat - Mean is possible. double fMeanY = 0.0; if (bConstant)
{
ScMatrixRef pNewX = pMatX->CloneIfConst();
ScMatrixRef pNewY = pMatY->CloneIfConst(); if (!pNewX || !pNewY)
{
PushError(FormulaError::CodeOverflow); return;
}
pMatX = std::move(pNewX);
pMatY = std::move(pNewY); // DeltaY is possible here; DeltaX depends on nCase, so later
fMeanY = lcl_GetMeanOverAll(pMatY, N); for (SCSIZE i=0; i<N; i++)
{
pMatY->PutDouble( ::rtl::math::approxSub(pMatY->GetDouble(i),fMeanY), i );
}
}
if (nCase==1)
{ // calculate simple regression double fMeanX = 0.0; if (bConstant)
{ // Mat = Mat - Mean
fMeanX = lcl_GetMeanOverAll(pMatX, N); for (SCSIZE i=0; i<N; i++)
{
pMatX->PutDouble( ::rtl::math::approxSub(pMatX->GetDouble(i),fMeanX), i );
}
} double fSumXY = lcl_GetSumProduct(pMatX,pMatY,N); double fSumX2 = lcl_GetSumProduct(pMatX,pMatX,N); if (fSumX2==0.0)
{
PushNoValue(); // all x-values are identical return;
} double fSlope = fSumXY / fSumX2; double fIntercept = 0.0; if (bConstant)
fIntercept = fMeanY - fSlope * fMeanX;
pResMat->PutDouble(_bRKP ? exp(fIntercept) : fIntercept, 1, 0); //order (column,row)
pResMat->PutDouble(_bRKP ? exp(fSlope) : fSlope, 0, 0);
double fR2 = fSSreg / (fSSreg + fSSresid);
pResMat->PutDouble(fR2, 0, 2);
}
}
PushMatrix(pResMat);
} else// calculate multiple regression;
{ // Uses a QR decomposition X = QR. The solution B = (X'X)^(-1) * X' * Y // becomes B = R^(-1) * Q' * Y if (nCase ==2) // Y is column
{
::std::vector< double> aVecR(N); // for QR decomposition // Enough memory for needed matrices?
ScMatrixRef pMeans = GetNewMat(K, 1, /*bEmpty*/true); // mean of each column
ScMatrixRef pMatZ; // for Q' * Y , inter alia if (bStats)
pMatZ = pMatY->Clone(); // Y is used in statistic, keep it else
pMatZ = pMatY; // Y can be overwritten
ScMatrixRef pSlopes = GetNewMat(1,K, /*bEmpty*/true); // from b1 to bK if (!pMeans || !pMatZ || !pSlopes)
{
PushError(FormulaError::CodeOverflow); return;
} if (bConstant)
{
lcl_CalculateColumnMeans(pMatX, pMeans, K, N);
lcl_CalculateColumnsDelta(pMatX, pMeans, K, N);
} if (!lcl_CalculateQRdecomposition(pMatX, aVecR, K, N))
{
PushNoValue(); return;
} // Later on we will divide by elements of aVecR, so make sure // that they aren't zero. bool bIsSingular=false; for (SCSIZE row=0; row < K && !bIsSingular; row++)
bIsSingular = aVecR[row] == 0.0; if (bIsSingular)
{
PushNoValue(); return;
} // Z = Q' Y; for (SCSIZE col = 0; col < K; col++)
{
lcl_ApplyHouseholderTransformation(pMatX, col, pMatZ, N);
} // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z // result Z should have zeros for index>=K; if not, ignore values for (SCSIZE col = 0; col < K ; col++)
{
pSlopes->PutDouble( pMatZ->GetDouble(col), col);
}
lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, false); double fIntercept = 0.0; if (bConstant)
fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K); // Fill first line in result matrix
pResMat->PutDouble(_bRKP ? exp(fIntercept) : fIntercept, K, 0 ); for (SCSIZE i = 0; i < K; i++)
pResMat->PutDouble(_bRKP ? exp(pSlopes->GetDouble(i))
: pSlopes->GetDouble(i) , K-1-i, 0);
if (bStats)
{ double fSSreg = 0.0; double fSSresid = 0.0; // re-use memory of Z;
pMatZ->FillDouble(0.0, 0, 0, 0, N-1); // Z = R * Slopes
lcl_ApplyUpperRightTriangle(pMatX, aVecR, pSlopes, pMatZ, K, false); // Z = Q * Z, that is Q * R * Slopes = X * Slopes for (SCSIZE colp1 = K; colp1 > 0; colp1--)
{
lcl_ApplyHouseholderTransformation(pMatX, colp1-1, pMatZ,N);
}
fSSreg =lcl_GetSumProduct(pMatZ, pMatZ, N); // re-use Y for residuals, Y = Y-Z for (SCSIZE row = 0; row < N; row++)
pMatY->PutDouble(pMatY->GetDouble(row) - pMatZ->GetDouble(row), row);
fSSresid = lcl_GetSumProduct(pMatY, pMatY, N);
pResMat->PutDouble(fSSreg, 0, 4);
pResMat->PutDouble(fSSresid, 1, 4);
// standard error of estimate = root mean SSE double fRMSE = sqrt(fSSresid / fDegreesFreedom);
pResMat->PutDouble(fRMSE, 1, 2);
// standard error of slopes // = RMSE * sqrt(diagonal element of (R' R)^(-1) ) // standard error of intercept // = RMSE * sqrt( Xmean * (R' R)^(-1) * Xmean' + 1/N) // (R' R)^(-1) = R^(-1) * (R')^(-1). Do not calculate it as // a whole matrix, but iterate over unit vectors.
KahanSum aSigmaIntercept = 0.0; double fPart; // for Xmean * single column of (R' R)^(-1) for (SCSIZE col = 0; col < K; col++)
{ //re-use memory of MatZ
pMatZ->FillDouble(0.0,0,0,0,K-1); // Z = unit vector e
pMatZ->PutDouble(1.0, col); //Solve R' * Z = e
lcl_SolveWithLowerLeftTriangle(pMatX, aVecR, pMatZ, K, false); // Solve R * Znew = Zold
lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pMatZ, K, false); // now Z is column col in (R' R)^(-1) double fSigmaSlope = fRMSE * sqrt(pMatZ->GetDouble(col));
pResMat->PutDouble(fSigmaSlope, K-1-col, 1); // (R' R) ^(-1) is symmetric if (bConstant)
{
fPart = lcl_GetSumProduct(pMeans, pMatZ, K);
aSigmaIntercept += fPart * pMeans->GetDouble(col);
}
} if (bConstant)
{ double fSigmaIntercept = fRMSE
* sqrt( (aSigmaIntercept + 1.0 / static_cast<double>(N) ).get() );
pResMat->PutDouble(fSigmaIntercept, K, 1);
} else
{
pResMat->PutError( FormulaError::NotAvailable, K, 1);
}
double fR2 = fSSreg / (fSSreg + fSSresid);
pResMat->PutDouble(fR2, 0, 2);
}
}
PushMatrix(pResMat);
} else// nCase == 3, Y is row, all matrices are transposed
{
::std::vector< double> aVecR(N); // for QR decomposition // Enough memory for needed matrices?
ScMatrixRef pMeans = GetNewMat(1, K, /*bEmpty*/true); // mean of each row
ScMatrixRef pMatZ; // for Q' * Y , inter alia if (bStats)
pMatZ = pMatY->Clone(); // Y is used in statistic, keep it else
pMatZ = pMatY; // Y can be overwritten
ScMatrixRef pSlopes = GetNewMat(K,1, /*bEmpty*/true); // from b1 to bK if (!pMeans || !pMatZ || !pSlopes)
{
PushError(FormulaError::CodeOverflow); return;
} if (bConstant)
{
lcl_CalculateRowMeans(pMatX, pMeans, N, K);
lcl_CalculateRowsDelta(pMatX, pMeans, N, K);
}
if (!lcl_TCalculateQRdecomposition(pMatX, aVecR, K, N))
{
PushNoValue(); return;
}
// Later on we will divide by elements of aVecR, so make sure // that they aren't zero. bool bIsSingular=false; for (SCSIZE row=0; row < K && !bIsSingular; row++)
bIsSingular = aVecR[row] == 0.0; if (bIsSingular)
{
PushNoValue(); return;
} // Z = Q' Y for (SCSIZE row = 0; row < K; row++)
{
lcl_TApplyHouseholderTransformation(pMatX, row, pMatZ, N);
} // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z // result Z should have zeros for index>=K; if not, ignore values for (SCSIZE col = 0; col < K ; col++)
{
pSlopes->PutDouble( pMatZ->GetDouble(col), col);
}
lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, true); double fIntercept = 0.0; if (bConstant)
fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K); // Fill first line in result matrix
pResMat->PutDouble(_bRKP ? exp(fIntercept) : fIntercept, K, 0 ); for (SCSIZE i = 0; i < K; i++)
pResMat->PutDouble(_bRKP ? exp(pSlopes->GetDouble(i))
: pSlopes->GetDouble(i) , K-1-i, 0);
if (bStats)
{ double fSSreg = 0.0; double fSSresid = 0.0; // re-use memory of Z;
pMatZ->FillDouble(0.0, 0, 0, N-1, 0); // Z = R * Slopes
lcl_ApplyUpperRightTriangle(pMatX, aVecR, pSlopes, pMatZ, K, true); // Z = Q * Z, that is Q * R * Slopes = X * Slopes for (SCSIZE rowp1 = K; rowp1 > 0; rowp1--)
{
lcl_TApplyHouseholderTransformation(pMatX, rowp1-1, pMatZ,N);
}
fSSreg =lcl_GetSumProduct(pMatZ, pMatZ, N); // re-use Y for residuals, Y = Y-Z for (SCSIZE col = 0; col < N; col++)
pMatY->PutDouble(pMatY->GetDouble(col) - pMatZ->GetDouble(col), col);
fSSresid = lcl_GetSumProduct(pMatY, pMatY, N);
pResMat->PutDouble(fSSreg, 0, 4);
pResMat->PutDouble(fSSresid, 1, 4);
// The third parameter may be missing in ODF, although the forth parameter // is present. Default values depend on data not yet read.
ScMatrixRef pMatNewX; if (nParamCount >= 3)
{ if (IsMissing())
{
Pop();
pMatNewX = nullptr;
} else
pMatNewX = GetMatrix();
} else
pMatNewX = nullptr;
//In ODF1.2 empty second parameter (which is two ;; ) is allowed //Defaults will be set in CheckMatrix
ScMatrixRef pMatX; if (nParamCount >= 2)
{ if (IsMissing())
{
Pop();
pMatX = nullptr;
} else
{
pMatX = GetMatrix();
}
} else
pMatX = nullptr;
ScMatrixRef pMatY = GetMatrix(); if (!pMatY)
{
PushIllegalParameter(); return;
}
// 1 = simple; 2 = multiple with Y as column; 3 = multiple with Y as row
sal_uInt8 nCase;
SCSIZE nCX, nCY; // number of columns
SCSIZE nRX, nRY; //number of rows
SCSIZE K = 0, N = 0; // K=number of variables X, N=number of data samples if (!CheckMatrix(_bGrowth,nCase,nCX,nCY,nRX,nRY,K,N,pMatX,pMatY))
{
PushIllegalParameter(); return;
}
// Enough data samples? if ((bConstant && (N<K+1)) || (!bConstant && (N<K)) || (N<1) || (K<1))
{
PushIllegalParameter(); return;
}
// Set default pMatNewX if necessary
SCSIZE nCXN, nRXN;
SCSIZE nCountXN; if (!pMatNewX)
{
nCXN = nCX;
nRXN = nRX;
nCountXN = nCXN * nRXN;
pMatNewX = pMatX->Clone(); // pMatX will be changed to X-meanX
} else
{
pMatNewX->GetDimensions(nCXN, nRXN); if ((nCase == 2 && K != nCXN) || (nCase == 3 && K != nRXN))
{
PushIllegalArgument(); return;
}
nCountXN = nCXN * nRXN; for (SCSIZE i = 0; i < nCountXN; i++) if (!pMatNewX->IsValue(i))
{
PushIllegalArgument(); return;
}
}
ScMatrixRef pResMat; // size depends on nCase if (nCase == 1)
pResMat = GetNewMat(nCXN,nRXN, /*bEmpty*/true); else
{ if (nCase==2)
pResMat = GetNewMat(1,nRXN, /*bEmpty*/true); else
pResMat = GetNewMat(nCXN,1, /*bEmpty*/true);
} if (!pResMat)
{
PushError(FormulaError::CodeOverflow); return;
} // Uses sum(x-MeanX)^2 and not [sum x^2]-N * MeanX^2 in case bConstant. // Clone constant matrices, so that Mat = Mat - Mean is possible. double fMeanY = 0.0; if (bConstant)
{
ScMatrixRef pCopyX = pMatX->CloneIfConst();
ScMatrixRef pCopyY = pMatY->CloneIfConst(); if (!pCopyX || !pCopyY)
{
PushError(FormulaError::MatrixSize); return;
}
pMatX = std::move(pCopyX);
pMatY = std::move(pCopyY); // DeltaY is possible here; DeltaX depends on nCase, so later
fMeanY = lcl_GetMeanOverAll(pMatY, N); for (SCSIZE i=0; i<N; i++)
{
pMatY->PutDouble( ::rtl::math::approxSub(pMatY->GetDouble(i),fMeanY), i );
}
}
if (nCase==1)
{ // calculate simple regression double fMeanX = 0.0; if (bConstant)
{ // Mat = Mat - Mean
fMeanX = lcl_GetMeanOverAll(pMatX, N); for (SCSIZE i=0; i<N; i++)
{
pMatX->PutDouble( ::rtl::math::approxSub(pMatX->GetDouble(i),fMeanX), i );
}
} double fSumXY = lcl_GetSumProduct(pMatX,pMatY,N); double fSumX2 = lcl_GetSumProduct(pMatX,pMatX,N); if (fSumX2==0.0)
{
PushNoValue(); // all x-values are identical return;
} double fSlope = fSumXY / fSumX2; double fHelp; if (bConstant)
{ double fIntercept = fMeanY - fSlope * fMeanX; for (SCSIZE i = 0; i < nCountXN; i++)
{
fHelp = pMatNewX->GetDouble(i)*fSlope + fIntercept;
pResMat->PutDouble(_bGrowth ? exp(fHelp) : fHelp, i);
}
} else
{ for (SCSIZE i = 0; i < nCountXN; i++)
{
fHelp = pMatNewX->GetDouble(i)*fSlope;
pResMat->PutDouble(_bGrowth ? exp(fHelp) : fHelp, i);
}
}
} else// calculate multiple regression;
{ if (nCase ==2) // Y is column
{
::std::vector< double> aVecR(N); // for QR decomposition // Enough memory for needed matrices?
ScMatrixRef pMeans = GetNewMat(K, 1, /*bEmpty*/true); // mean of each column
ScMatrixRef pSlopes = GetNewMat(1,K, /*bEmpty*/true); // from b1 to bK if (!pMeans || !pSlopes)
{
PushError(FormulaError::CodeOverflow); return;
} if (bConstant)
{
lcl_CalculateColumnMeans(pMatX, pMeans, K, N);
lcl_CalculateColumnsDelta(pMatX, pMeans, K, N);
} if (!lcl_CalculateQRdecomposition(pMatX, aVecR, K, N))
{
PushNoValue(); return;
} // Later on we will divide by elements of aVecR, so make sure // that they aren't zero. bool bIsSingular=false; for (SCSIZE row=0; row < K && !bIsSingular; row++)
bIsSingular = aVecR[row] == 0.0; if (bIsSingular)
{
PushNoValue(); return;
} // Z := Q' Y; Y is overwritten with result Z for (SCSIZE col = 0; col < K; col++)
{
lcl_ApplyHouseholderTransformation(pMatX, col, pMatY, N);
} // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z // result Z should have zeros for index>=K; if not, ignore values for (SCSIZE col = 0; col < K ; col++)
{
pSlopes->PutDouble( pMatY->GetDouble(col), col);
}
lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, false);
// Fill result matrix
lcl_MFastMult(pMatNewX,pSlopes,pResMat,nRXN,K,1); if (bConstant)
{ double fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K); for (SCSIZE row = 0; row < nRXN; row++)
pResMat->PutDouble(pResMat->GetDouble(row)+fIntercept, row);
} if (_bGrowth)
{ for (SCSIZE i = 0; i < nRXN; i++)
pResMat->PutDouble(exp(pResMat->GetDouble(i)), i);
}
} else
{ // nCase == 3, Y is row, all matrices are transposed
::std::vector< double> aVecR(N); // for QR decomposition // Enough memory for needed matrices?
ScMatrixRef pMeans = GetNewMat(1, K, /*bEmpty*/true); // mean of each row
ScMatrixRef pSlopes = GetNewMat(K,1, /*bEmpty*/true); // row from b1 to bK if (!pMeans || !pSlopes)
{
PushError(FormulaError::CodeOverflow); return;
} if (bConstant)
{
lcl_CalculateRowMeans(pMatX, pMeans, N, K);
lcl_CalculateRowsDelta(pMatX, pMeans, N, K);
} if (!lcl_TCalculateQRdecomposition(pMatX, aVecR, K, N))
{
PushNoValue(); return;
} // Later on we will divide by elements of aVecR, so make sure // that they aren't zero. bool bIsSingular=false; for (SCSIZE row=0; row < K && !bIsSingular; row++)
bIsSingular = aVecR[row] == 0.0; if (bIsSingular)
{
PushNoValue(); return;
} // Z := Q' Y; Y is overwritten with result Z for (SCSIZE row = 0; row < K; row++)
{
lcl_TApplyHouseholderTransformation(pMatX, row, pMatY, N);
} // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z // result Z should have zeros for index>=K; if not, ignore values for (SCSIZE col = 0; col < K ; col++)
{
pSlopes->PutDouble( pMatY->GetDouble(col), col);
}
lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, true);
// Fill result matrix
lcl_MFastMult(pSlopes,pMatNewX,pResMat,1,K,nCXN); if (bConstant)
{ double fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K); for (SCSIZE col = 0; col < nCXN; col++)
pResMat->PutDouble(pResMat->GetDouble(col)+fIntercept, col);
} if (_bGrowth)
{ for (SCSIZE i = 0; i < nCXN; i++)
pResMat->PutDouble(exp(pResMat->GetDouble(i)), i);
}
}
}
PushMatrix(pResMat);
}
void ScInterpreter::ScMatRef()
{ // In case it contains relative references resolve them as usual.
Push( *pCur );
ScAddress aAdr;
PopSingleRef( aAdr );
ScRefCellValue aCell(mrDoc, aAdr);
if (aCell.getType() != CELLTYPE_FORMULA)
{
PushError( FormulaError::NoRef ); return;
}
if (aCell.getFormula()->IsRunning())
{ // Twisted odd corner case where an array element's cell tries to // access the top left matrix while it is still running, see tdf#88737 // This is a hackish workaround, not a general solution, the matrix // isn't available anyway and FormulaError::CircularReference would be set.
PushError( FormulaError::RetryCircular ); return;
}
const ScMatrix* pMat = aCell.getFormula()->GetMatrix(); if (pMat)
{
SCSIZE nCols, nRows;
pMat->GetDimensions( nCols, nRows );
SCSIZE nC = static_cast<SCSIZE>(aPos.Col() - aAdr.Col());
SCSIZE nR = static_cast<SCSIZE>(aPos.Row() - aAdr.Row()); #if 0 // XXX: this could be an additional change for tdf#145085 to not // display the URL in a voluntary entered 2-rows array context. // However, that might as well be used on purpose to implement a check // on the URL, which existing documents may have done, the more that // before the accompanying change of // ScFormulaCell::GetResultDimensions() the cell array was forced to // two rows. Do not change without compelling reason. Note that this // repeating top cell is what Excel implements, but it has no // additional value so probably isn't used there. Exporting to and // using in Excel though will lose this capability. if (aCell.mpFormula->GetCode()->IsHyperLink())
{ // Row 2 element is the URL that is not to be displayed, fake a // 1-row cell-text-only matrix that is repeated.
assert(nRows == 2);
nR = 0;
} #endif if ((nCols <= nC && nCols != 1) || (nRows <= nR && nRows != 1))
PushNA(); else
{ const ScMatrixValue nMatVal = pMat->Get( nC, nR);
ScMatValType nMatValType = nMatVal.nType;
if (ScMatrix::IsNonValueType( nMatValType))
{ if (ScMatrix::IsEmptyPathType( nMatValType))
{ // result of empty false jump path
nFuncFmtType = SvNumFormatType::LOGICAL;
PushInt(0);
} elseif (ScMatrix::IsEmptyType( nMatValType))
{ // Not inherited (really?) and display as empty string, not 0.
PushTempToken( new ScEmptyCellToken( false, true));
} else
PushString( nMatVal.GetString() );
} else
{ // Determine nFuncFmtType type before PushDouble().
mrDoc.GetNumberFormatInfo(mrContext, nCurFmtType, nCurFmtIndex, aAdr);
nFuncFmtType = nCurFmtType;
nFuncFmtIndex = nCurFmtIndex;
PushDouble(nMatVal.fVal); // handles DoubleError
}
}
} else
{ // Determine nFuncFmtType type before PushDouble().
mrDoc.GetNumberFormatInfo(mrContext, nCurFmtType, nCurFmtIndex, aAdr);
nFuncFmtType = nCurFmtType;
nFuncFmtIndex = nCurFmtIndex; // If not a result matrix, obtain the cell value.
FormulaError nErr = aCell.getFormula()->GetErrCode(); if (nErr != FormulaError::NONE)
PushError( nErr ); elseif (aCell.getFormula()->IsValue())
PushDouble(aCell.getFormula()->GetValue()); else
{
svl::SharedString aVal = aCell.getFormula()->GetString();
PushString( aVal );
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.