/* -*- 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,
--> --------------------
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.