[Libreoffice-commits] core.git: formula/inc formula/source include/formula sc/inc sc/qa sc/source

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Tue Feb 19 11:35:50 UTC 2019


 formula/inc/core_resource.hrc               |    6 
 formula/source/core/api/FormulaCompiler.cxx |    1 
 include/formula/compiler.hxx                |    3 
 include/formula/opcode.hxx                  |    2 
 sc/inc/helpids.h                            |    1 
 sc/inc/scfuncs.hrc                          |   14 +
 sc/qa/extras/scfunctionlistobj.cxx          |    2 
 sc/qa/unit/ucalc.cxx                        |    1 
 sc/source/core/data/funcdesc.cxx            |    3 
 sc/source/core/inc/interpre.hxx             |    1 
 sc/source/core/tool/interpr3.cxx            |  297 ++++++++++++++++++++++++++++
 sc/source/core/tool/interpr4.cxx            |    1 
 sc/source/core/tool/parclass.cxx            |    1 
 sc/source/filter/excel/xlformula.cxx        |    3 
 sc/source/filter/oox/formulabase.cxx        |    3 
 15 files changed, 334 insertions(+), 5 deletions(-)

New commits:
commit 32d9f7a632e1df1b6d5ac32a88b2b88c042e91ff
Author:     Dennis Francis <dennis.francis at collabora.com>
AuthorDate: Sat Feb 16 14:36:01 2019 +0530
Commit:     Eike Rathke <erack at redhat.com>
CommitDate: Tue Feb 19 12:35:25 2019 +0100

    tdf#74664 : Adds FOURIER() formula
    
    FOURIER(Array, GroupedByColumns, Inverse, Polar)
    is a matrix formula that computes discrete Fourier transform[DFT]
    of input array(first argument) via a radix-2, decimation-in-time
    fast Fourier transform algorithm.
    
    Unit test for this is coming up in a new commit.
    
    The data in input array(first argument) can be :-
    
    1) grouped by columns (needs to be indicated by flag GroupedByColumns = TRUE)
       In this case the array can contain 1 or 2 columns, where the first column
       contains the real part of input series and second column if present contains
       the imaginary part of the input series. If there is only 1 column, the input
       series is treated as purely real. If the number of rows is not a power of 2,
       zeroes are appended to the input series internally to make the series length
       equal to the next nearest power of 2.
    
    2) grouped by rows (needs to be indicated by flag GroupedByColumns = FALSE)
       In this case the array can contain 1 or 2 rows, where the first row
       contains the real part of input series and second row if present contains
       the imaginary part of the input series. If there is only 1 row, the input
       series is treated as purely real. If the number of columns is not a power of 2,
       zeroes are appended to the input series internally to make the series length
       equal to the next nearest power of 2.
    
    The third argument "Inverse" is a boolean flag to indicate whether an inverse
    DFT needs to be computed. This argument is optional and the default value is
    FALSE.
    
    The fourth argument Polar is a boolean flag to indicate whether the final output
    needs to be in polar coordinates. This argument is optional and the default value
    is FALSE.
    
    The result of DFT consists of two columns - first column contains the real parts (or
    the magnitudes if Polar=TRUE) and second column contains the imaginary parts (or
    the phases if Polar=TRUE).
    
    Implementation:
    
    A fairly straighforward non-recursive implementation of radix-2 FFT algorithm is
    written from scratch.
    
    Reference:
    
    Heckbert, P., 1995. Fourier transforms and the fast Fourier transform (FFT) algorithm.
    Computer Graphics, 2, pp.15-463.
    
    The normalization factor used in DFT / and inverse DFT in this implementation matches that
    of fft() and ifft() functions of Matlab/Octave. It also matches the one used in Wikipedia
    article on DFT: https://en.wikipedia.org/wiki/Discrete_Fourier_transform.
    
    Change-Id: If4a40a6ef62bce1f03c589ae5357b2049f66fe64
    Reviewed-on: https://gerrit.libreoffice.org/67938
    Tested-by: Jenkins
    Reviewed-by: Eike Rathke <erack at redhat.com>

diff --git a/formula/inc/core_resource.hrc b/formula/inc/core_resource.hrc
index 9d97fd771d9e..cd2baa334b52 100644
--- a/formula/inc/core_resource.hrc
+++ b/formula/inc/core_resource.hrc
@@ -466,6 +466,7 @@ const std::pair<const char*, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF[] =
     { "ORG.LIBREOFFICE.RAWSUBTRACT" , SC_OPCODE_RAWSUBTRACT },
     { "ORG.LIBREOFFICE.ROUNDSIG" , SC_OPCODE_ROUNDSIG },
     { "ORG.LIBREOFFICE.REGEX" , SC_OPCODE_REGEX },
+    { "ORG.LIBREOFFICE.FOURIER", SC_OPCODE_FOURIER },
     { nullptr,  -1 }
 };
 
@@ -909,6 +910,7 @@ const std::pair<const char*, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML[] =
     { "_xlfn.ORG.LIBREOFFICE.RAWSUBTRACT" , SC_OPCODE_RAWSUBTRACT },
     { "_xlfn.ORG.LIBREOFFICE.ROUNDSIG" , SC_OPCODE_ROUNDSIG },
     { "_xlfn.ORG.LIBREOFFICE.REGEX" , SC_OPCODE_REGEX },
+    { "_xlfn.ORG.LIBREOFFICE.FOURIER", SC_OPCODE_FOURIER },
     { nullptr,  -1 }
 };
 
@@ -1357,6 +1359,7 @@ const std::pair<const char*, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF[] =
     { "RAWSUBTRACT" , SC_OPCODE_RAWSUBTRACT },
     { "ROUNDSIG" , SC_OPCODE_ROUNDSIG },
     { "REGEX" , SC_OPCODE_REGEX },
+    { "FOURIER", SC_OPCODE_FOURIER },
     { nullptr, -1 }
 };
 
@@ -1804,6 +1807,7 @@ const std::pair<const char*, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH_API[] =
     { "RAWSUBTRACT" , SC_OPCODE_RAWSUBTRACT },
     { "ROUNDSIG" , SC_OPCODE_ROUNDSIG },
     { "REGEX" , SC_OPCODE_REGEX },
+    { "FOURIER", SC_OPCODE_FOURIER },
     { nullptr, -1 }
 };
 
@@ -2250,6 +2254,7 @@ const std::pair<const char*, int> RID_STRLIST_FUNCTION_NAMES_ENGLISH[] =
     { "RAWSUBTRACT" , SC_OPCODE_RAWSUBTRACT },
     { "ROUNDSIG" , SC_OPCODE_ROUNDSIG },
     { "REGEX" , SC_OPCODE_REGEX },
+    { "FOURIER", SC_OPCODE_FOURIER },
     { nullptr, -1 }
 };
 
@@ -2680,6 +2685,7 @@ const std::pair<const char*, int> RID_STRLIST_FUNCTION_NAMES[] =
     { NC_("RID_STRLIST_FUNCTION_NAMES", "FINDB") , SC_OPCODE_FINDB },
     { NC_("RID_STRLIST_FUNCTION_NAMES", "SEARCHB") , SC_OPCODE_SEARCHB },
     { NC_("RID_STRLIST_FUNCTION_NAMES", "REGEX") , SC_OPCODE_REGEX },
+    { NC_("RID_STRLIST_FUNCTION_NAMES", "FOURIER"), SC_OPCODE_FOURIER },
 
     { nullptr, -1 }
 };
diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx
index f243aab357f7..930774023ace 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -1051,6 +1051,7 @@ bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode )
         case ocMatInv :
         case ocMatrixUnit :
         case ocModalValue_Multi :
+        case ocFourier :
             return true;
         default:
         {
diff --git a/include/formula/compiler.hxx b/include/formula/compiler.hxx
index 09a507fc3d68..c5e5cfdf0da2 100644
--- a/include/formula/compiler.hxx
+++ b/include/formula/compiler.hxx
@@ -506,7 +506,8 @@
 #define SC_OPCODE_FINDB             495
 #define SC_OPCODE_SEARCHB           496
 #define SC_OPCODE_REGEX             497
-#define SC_OPCODE_STOP_2_PAR        498     /* last function with two or more parameters' OpCode + 1 */
+#define SC_OPCODE_FOURIER           498
+#define SC_OPCODE_STOP_2_PAR        499     /* last function with two or more parameters' OpCode + 1 */
 
 #define SC_OPCODE_STOP_FUNCTION     SC_OPCODE_STOP_2_PAR            /* last function's OpCode + 1 */
 #define SC_OPCODE_LAST_OPCODE_ID    (SC_OPCODE_STOP_FUNCTION - 1)   /* last OpCode */
diff --git a/include/formula/opcode.hxx b/include/formula/opcode.hxx
index d2c6548e286f..b969a0047007 100644
--- a/include/formula/opcode.hxx
+++ b/include/formula/opcode.hxx
@@ -500,6 +500,7 @@ enum OpCode : sal_uInt16
         ocErf_MS            = SC_OPCODE_ERF_MS,
         ocErfc_MS           = SC_OPCODE_ERFC_MS,
         ocEncodeURL         = SC_OPCODE_ENCODEURL,
+        ocFourier           = SC_OPCODE_FOURIER,
     // internal stuff
         ocInternalBegin     = SC_OPCODE_INTERNAL_BEGIN,
         ocTTT               = SC_OPCODE_TTT,
@@ -972,6 +973,7 @@ inline std::string OpCodeEnumToString(OpCode eCode)
     case ocErf_MS: return "Erf_MS";
     case ocErfc_MS: return "Erfc_MS";
     case ocEncodeURL: return "EncodeURL";
+    case ocFourier: return "Fourier";
     case ocTTT: return "TTT";
     case ocDebugVar: return "DebugVar";
     case ocDataToken1: return "DataToken1";
diff --git a/sc/inc/helpids.h b/sc/inc/helpids.h
index 283057e3ccce..d6616f106784 100644
--- a/sc/inc/helpids.h
+++ b/sc/inc/helpids.h
@@ -578,6 +578,7 @@
 #define HID_FUNC_FINDB                                          "SC_HID_FUNC_FINDB"
 #define HID_FUNC_SEARCHB                                        "SC_HID_FUNC_SEARCHB"
 #define HID_FUNC_REGEX                                          "SC_HID_FUNC_REGEX"
+#define HID_FUNC_FOURIER                                        "SC_HID_FUNC_FOURIER"
 
 #endif
 
diff --git a/sc/inc/scfuncs.hrc b/sc/inc/scfuncs.hrc
index c08df5adc072..c4a050a4c334 100644
--- a/sc/inc/scfuncs.hrc
+++ b/sc/inc/scfuncs.hrc
@@ -4118,6 +4118,20 @@ const char* SC_OPCODE_SEARCHB_ARY[] =
     NC_("SC_OPCODE_SEARCHB", "The position in the text from which the search starts.")
 };
 
+// -=*# Resource for function FOURIER #*=-
+const char* SC_OPCODE_FOURIER_ARY[] =
+{
+    NC_("SC_OPCODE_FOURIER", "Computes the Discrete Fourier Transform (DFT) of an array"),
+    NC_("SC_OPCODE_FOURIER", "Array"),
+    NC_("SC_OPCODE_FOURIER", "The array whose DFT needs to be computed. The dimensions of this array can be Nx1 or Nx2 or 1xN or 2xN."),
+    NC_("SC_OPCODE_FOURIER", "GroupedByColumns"),
+    NC_("SC_OPCODE_FOURIER", "Flag to indicate whether the array is grouped by columns or not (default TRUE)."),
+    NC_("SC_OPCODE_FOURIER", "Inverse"),
+    NC_("SC_OPCODE_FOURIER", "Flag to indicate whether an inverse DFT is to be computed (default FALSE)."),
+    NC_("SC_OPCODE_FOURIER", "Polar"),
+    NC_("SC_OPCODE_FOURIER", "Flag to indicate whether to return the results in polar form (default FALSE).")
+};
+
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/extras/scfunctionlistobj.cxx b/sc/qa/extras/scfunctionlistobj.cxx
index 0ce8953ecc21..cf8c962bfb06 100644
--- a/sc/qa/extras/scfunctionlistobj.cxx
+++ b/sc/qa/extras/scfunctionlistobj.cxx
@@ -82,7 +82,7 @@ private:
 ScFunctionListObj::ScFunctionListObj()
     : CalcUnoApiTest("/sc/qa/extras/testdocuments")
     , XElementAccess(cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get())
-    , XIndexAccess(392)
+    , XIndexAccess(393)
     , XNameAccess("IF")
     , XServiceInfo("stardiv.StarCalc.ScFunctionListObj", "com.sun.star.sheet.FunctionDescriptions")
 {
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index bfb780a8e172..f1a594912eec 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -2423,6 +2423,7 @@ void Test::testFunctionLists()
     };
 
     const char* aArray[] = {
+        "FOURIER",
         "FREQUENCY",
         "GROWTH",
         "LINEST",
diff --git a/sc/source/core/data/funcdesc.cxx b/sc/source/core/data/funcdesc.cxx
index 15a2e22d8811..a5cc499e44df 100644
--- a/sc/source/core/data/funcdesc.cxx
+++ b/sc/source/core/data/funcdesc.cxx
@@ -787,7 +787,8 @@ ScFunctionList::ScFunctionList()
         { SC_OPCODE_REPLACEB, ENTRY(SC_OPCODE_REPLACEB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_REPLACEB, 4, { 0, 0, 0, 0 } },
         { SC_OPCODE_FINDB, ENTRY(SC_OPCODE_FINDB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_FINDB, 3, { 0, 0, 1 } },
         { SC_OPCODE_SEARCHB, ENTRY(SC_OPCODE_SEARCHB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_SEARCHB, 3, { 0, 0, 1 } },
-        { SC_OPCODE_REGEX, ENTRY(SC_OPCODE_REGEX_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_REGEX, 4, { 0, 0, 1, 1 } }
+        { SC_OPCODE_REGEX, ENTRY(SC_OPCODE_REGEX_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_REGEX, 4, { 0, 0, 1, 1 } },
+        { SC_OPCODE_FOURIER, ENTRY(SC_OPCODE_FOURIER_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_FOURIER, 4, { 0, 0, 1, 1 } }
     };
 
     ScFuncDesc* pDesc = nullptr;
diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx
index b6c68dcdb264..4f76af346d63 100644
--- a/sc/source/core/inc/interpre.hxx
+++ b/sc/source/core/inc/interpre.hxx
@@ -857,6 +857,7 @@ private:
     void ScLogest();
     void ScForecast();
     void ScForecast_Ets( ScETSType eETSType );
+    void ScFourier();
     void ScNoName();
     void ScBadName();
     // Statistics:
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: */
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index 5c2cd6e97cb7..238edf4eba9b 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -4357,6 +4357,7 @@ StackVar ScInterpreter::Interpret()
                 case ocBetaDist_MS      : ScBetaDist_MS();              break;
                 case ocBetaInv          :
                 case ocBetaInv_MS       : ScBetaInv();                  break;
+                case ocFourier          : ScFourier();                  break;
                 case ocExternal         : ScExternal();                 break;
                 case ocTableOp          : ScTableOp();                  break;
                 case ocStop :                                           break;
diff --git a/sc/source/core/tool/parclass.cxx b/sc/source/core/tool/parclass.cxx
index ce6c9094cc71..7e2cd6eacd9f 100644
--- a/sc/source/core/tool/parclass.cxx
+++ b/sc/source/core/tool/parclass.cxx
@@ -146,6 +146,7 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
     { ocForecast_ETS_STA, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value        }, 0, Value }},
     { ocForecast_ETS_STM, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value        }, 0, Value }},
     { ocFormula,         {{ Reference                                            }, 0, Value }},
+    { ocFourier,         {{ ForceArray, Value, Value, Value                      }, 0, Value }},
     { ocFrequency,       {{ ReferenceOrForceArray, ReferenceOrForceArray         }, 0, ForceArrayReturn }},
     { ocGCD,             {{ Reference                                            }, 1, Value }},
     { ocGeoMean,         {{ Reference                                            }, 1, Value }},
diff --git a/sc/source/filter/excel/xlformula.cxx b/sc/source/filter/excel/xlformula.cxx
index 3df57661f009..8cdf9806896f 100644
--- a/sc/source/filter/excel/xlformula.cxx
+++ b/sc/source/filter/excel/xlformula.cxx
@@ -637,7 +637,8 @@ static const XclFunctionInfo saFuncTable_OOoLO[] =
     EXC_FUNCENTRY_OOO( ocForecast_ETS_PIM, 3,  7,  0,  "ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT" ),
     EXC_FUNCENTRY_OOO( ocForecast_ETS_STM, 3,  6,  0,  "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT" ),
     EXC_FUNCENTRY_OOO( ocRoundSig,      2,  2,  0,  "ORG.LIBREOFFICE.ROUNDSIG" ),
-    EXC_FUNCENTRY_OOO( ocRegex,         2,  4,  0,  "ORG.LIBREOFFICE.REGEX" )
+    EXC_FUNCENTRY_OOO( ocRegex,         2,  4,  0,  "ORG.LIBREOFFICE.REGEX" ),
+    EXC_FUNCENTRY_OOO( ocFourier,       2,  4,  0,  "ORG.LIBREOFFICE.FOURIER" )
 };
 
 #undef EXC_FUNCENTRY_OOO_IBR
diff --git a/sc/source/filter/oox/formulabase.cxx b/sc/source/filter/oox/formulabase.cxx
index 62633437c1cb..707db93ed22f 100644
--- a/sc/source/filter/oox/formulabase.cxx
+++ b/sc/source/filter/oox/formulabase.cxx
@@ -910,7 +910,8 @@ static const FunctionData saFuncTableOOoLO[] =
     { "ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT",   "ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT",   NOID,   NOID,   4,  7,  V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
     { "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT", "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT", NOID,   NOID,   3,  6,  V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
     { "ORG.LIBREOFFICE.ROUNDSIG",   "ORG.LIBREOFFICE.ROUNDSIG", NOID, NOID,  2,  2,  V, { RX }, FuncFlags::MACROCALL_NEW },
-    { "ORG.LIBREOFFICE.REGEX",      "ORG.LIBREOFFICE.REGEX", NOID, NOID,  2,  4,  V, { RX }, FuncFlags::MACROCALL_NEW }
+    { "ORG.LIBREOFFICE.REGEX",      "ORG.LIBREOFFICE.REGEX", NOID, NOID,  2,  4,  V, { RX }, FuncFlags::MACROCALL_NEW },
+    { "ORG.LIBREOFFICE.FOURIER",    "ORG.LIBREOFFICE.FOURIER", NOID, NOID,  2,  4,  V, { RX }, FuncFlags::MACROCALL_NEW }
 
 };
 


More information about the Libreoffice-commits mailing list