summaryrefslogtreecommitdiffstats
path: root/sc/source/core/tool/interpr3.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/tool/interpr3.cxx')
-rw-r--r--sc/source/core/tool/interpr3.cxx297
1 files changed, 297 insertions, 0 deletions
diff --git a/sc/source/core/tool/interpr3.cxx b/sc/source/core/tool/interpr3.cxx
index 1015d8cfc78a..e568e4f96367 100644
--- a/sc/source/core/tool/interpr3.cxx
+++ b/sc/source/core/tool/interpr3.cxx
@@ -4721,4 +4721,301 @@ void ScInterpreter::ScForecast()
}
}
+static void lcl_roundUpNearestPow2(SCSIZE& nNum, SCSIZE& nNumBits)
+{
+ // Find the least power of 2 that is less than or equal to nNum.
+ SCSIZE nPow2(1);
+ nNumBits = std::numeric_limits<SCSIZE>::digits;
+ nPow2 <<= (nNumBits - 1);
+ while (nPow2 >= 1)
+ {
+ if (nNum & nPow2)
+ break;
+
+ --nNumBits;
+ nPow2 >>= 1;
+ }
+
+ if (nPow2 != nNum)
+ nNum = nPow2 ? (nPow2 << 1) : 1;
+ else
+ --nNumBits;
+}
+
+static SCSIZE lcl_bitReverse(SCSIZE nIn, SCSIZE nBound)
+{
+ SCSIZE nOut = 0;
+ for (SCSIZE nMask = 1; nMask < nBound; nMask <<= 1)
+ {
+ nOut <<= 1;
+
+ if (nIn & nMask)
+ nOut |= 1;
+ }
+
+ return nOut;
+}
+
+class ScFFT2
+{
+public:
+ ScFFT2(ScMatrixRef& pMat, SCSIZE nPoints, bool bRealInput, bool bInverse, bool bPolar) :
+ mpMat(pMat),
+ mnPoints(nPoints),
+ mnStages(0),
+ mbRealInput(bRealInput),
+ mbInverse(bInverse),
+ mbPolar(bPolar)
+ {}
+
+ void Compute();
+private:
+
+ void prepare();
+ void computeTFactors();
+ void normalize();
+ void convertToPolar();
+
+ double getReal(SCSIZE nIdx)
+ {
+ return mpMat->GetDouble(0, nIdx);
+ }
+
+ void setReal(double fVal, SCSIZE nIdx)
+ {
+ mpMat->PutDouble(fVal, 0, nIdx);
+ }
+
+ double getImag(SCSIZE nIdx)
+ {
+ return mpMat->GetDouble(1, nIdx);
+ }
+
+ void setImag(double fVal, SCSIZE nIdx)
+ {
+ mpMat->PutDouble(fVal, 1, nIdx);
+ }
+
+ SCSIZE getTFactorIndex(SCSIZE nPtIndex, SCSIZE nTfIdxScale)
+ {
+ return ( ( nPtIndex * nTfIdxScale ) & ( mnPoints - 1 ) ); // (x & (N-1)) is same as (x % N) but faster.
+ }
+
+ void computeFly(SCSIZE nTopIdx, SCSIZE nBottomIdx, SCSIZE nWIdx1, SCSIZE nWIdx2, SCSIZE nStage)
+ {
+ const double x1r = getReal(nTopIdx);
+
+ const double& w1r = mfWReal[nWIdx1];
+ const double& w1i = mfWImag[nWIdx1];
+
+ const double x2r = getReal(nBottomIdx);
+
+ const double& w2r = mfWReal[nWIdx2];
+ const double& w2i = mfWImag[nWIdx2];
+
+ // Optimization for real input in stage #0
+ if (nStage == 0 && mbRealInput)
+ {
+ setReal(x1r + x2r*w1r, nTopIdx);
+ setImag(x2r*w1i, nTopIdx);
+
+ setReal(x1r + x2r*w2r, nBottomIdx);
+ setImag(x2r*w2i, nBottomIdx);
+
+ return;
+ }
+
+ const double x1i = getImag(nTopIdx);
+ const double x2i = getImag(nBottomIdx);
+
+ setReal(x1r + x2r*w1r - x2i*w1i, nTopIdx);
+ setImag(x1i + x2i*w1r + x2r*w1i, nTopIdx);
+
+ setReal(x1r + x2r*w2r - x2i*w2i, nBottomIdx);
+ setImag(x1i + x2i*w2r + x2r*w2i, nBottomIdx);
+ }
+
+ ScMatrixRef& mpMat;
+ std::vector<double> mfWReal;
+ std::vector<double> mfWImag;
+ SCSIZE mnPoints;
+ SCSIZE mnStages;
+ bool mbRealInput:1;
+ bool mbInverse:1;
+ bool mbPolar:1;
+};
+
+void ScFFT2::prepare()
+{
+ lcl_roundUpNearestPow2(mnPoints, mnStages);
+
+ mpMat->Resize(2, mnPoints, 0.0);
+
+ // Reorder array by bit-reversed indices.
+ for (SCSIZE nIdx = 0; nIdx < mnPoints; ++nIdx)
+ {
+ SCSIZE nRevIdx = lcl_bitReverse(nIdx, mnPoints);
+ if (nIdx < nRevIdx)
+ {
+ double fTmp = getReal(nIdx);
+ setReal(getReal(nRevIdx), nIdx);
+ setReal(fTmp, nRevIdx);
+
+ if (!mbRealInput)
+ {
+ fTmp = getImag(nIdx);
+ setImag(getImag(nRevIdx), nIdx);
+ setImag(fTmp, nRevIdx);
+ }
+ }
+ }
+}
+
+void ScFFT2::computeTFactors()
+{
+ mfWReal.resize(mnPoints);
+ mfWImag.resize(mnPoints);
+
+ double nW = -2.0*F_PI/static_cast<double>(mnPoints);
+ if (mbInverse)
+ nW = -nW;
+
+ for (SCSIZE nIdx = 0; nIdx < mnPoints; ++nIdx)
+ {
+ mfWReal[nIdx] = cos(nW*static_cast<double>(nIdx));
+ mfWImag[nIdx] = sin(nW*static_cast<double>(nIdx));
+ }
+}
+
+void ScFFT2::normalize()
+{
+ const double fScale = 1.0/static_cast<double>(mnPoints);
+
+ for (SCSIZE nIdx = 0; nIdx < mnPoints; ++nIdx)
+ setReal(getReal(nIdx)*fScale, nIdx);
+
+ // If already in polar coordinates, we should only scale the magnitude part
+ // which is stored where "real" part was stored before.
+ if (!mbPolar)
+ for (SCSIZE nIdx = 0; nIdx < mnPoints; ++nIdx)
+ setImag(getImag(nIdx)*fScale, nIdx);
+}
+
+void ScFFT2::convertToPolar()
+{
+ double fMag, fPhase, fR, fI;
+ for (SCSIZE nIdx = 0; nIdx < mnPoints; ++nIdx)
+ {
+ fR = getReal(nIdx);
+ fI = getImag(nIdx);
+ fPhase = atan(fI/fR);
+ fMag = sqrt(fR*fR + fI*fI);
+
+ setReal(fMag, nIdx);
+ setImag(fPhase, nIdx);
+ }
+}
+
+void ScFFT2::Compute()
+{
+ prepare();
+ computeTFactors();
+
+ const SCSIZE nFliesInStage = mnPoints/2;
+ for (SCSIZE nStage = 0; nStage < mnStages; ++nStage)
+ {
+ const SCSIZE nTFIdxScale = (1 << (mnStages-nStage-1)); // Twiddle factor index's scale factor.
+ const SCSIZE nFliesInGroup = 1<<nStage;
+ const SCSIZE nGroups = nFliesInStage/nFliesInGroup;
+ const SCSIZE nFlyWidth = nFliesInGroup;
+ for (SCSIZE nGroup = 0, nFlyTopIdx = 0; nGroup < nGroups; ++nGroup)
+ {
+ for (SCSIZE nFly = 0; nFly < nFliesInGroup; ++nFly, ++nFlyTopIdx)
+ {
+ SCSIZE nFlyBottomIdx = nFlyTopIdx + nFlyWidth;
+ SCSIZE nWIdx1 = getTFactorIndex(nFlyTopIdx, nTFIdxScale);
+ SCSIZE nWIdx2 = getTFactorIndex(nFlyBottomIdx, nTFIdxScale);
+
+ computeFly(nFlyTopIdx, nFlyBottomIdx, nWIdx1, nWIdx2, nStage);
+ }
+
+ nFlyTopIdx += nFlyWidth;
+ }
+ }
+
+ if (mbPolar)
+ convertToPolar();
+
+ // Normalize after converting to polar, so we have a chance to
+ // save O(mnPoints) flops.
+ if (mbInverse)
+ normalize();
+}
+
+void ScInterpreter::ScFourier()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 4 ) )
+ return;
+
+ bool bInverse = false;
+ bool bPolar = false;
+
+ if (nParamCount == 4)
+ bPolar = GetBool();
+
+ if (nParamCount > 2)
+ {
+ if (IsMissing())
+ Pop();
+ else
+ bInverse = GetBool();
+ }
+
+ bool bGroupedByColumn = GetBool();
+
+ ScMatrixRef pInputMat = GetMatrix();
+ if (!pInputMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ SCSIZE nC, nR;
+ pInputMat->GetDimensions(nC, nR);
+
+ if ((bGroupedByColumn && nC > 2) || (!bGroupedByColumn && nR > 2))
+ {
+ // There can be no more than 2 columns (real, imaginary) if data grouped by columns.
+ // and no more than 2 rows if data is grouped by rows.
+ PushIllegalArgument();
+ return;
+ }
+
+ if (!pInputMat->IsNumeric())
+ {
+ PushNoValue();
+ return;
+ }
+
+ SCSIZE nNumPoints;
+ bool bRealInput = true;
+ if (!bGroupedByColumn)
+ {
+ pInputMat->MatTrans(*pInputMat);
+ nNumPoints = nC;
+ bRealInput = (nR == 1);
+ }
+ else
+ {
+ nNumPoints = nR;
+ bRealInput = (nC == 1);
+ }
+
+ ScFFT2 aFFT2(pInputMat, nNumPoints, bRealInput, bInverse, bPolar);
+ aFFT2.Compute();
+
+ PushMatrix(pInputMat);
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */