summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--formula/inc/core_resource.hrc6
-rw-r--r--formula/source/core/api/FormulaCompiler.cxx1
-rw-r--r--include/formula/compiler.hxx3
-rw-r--r--include/formula/opcode.hxx2
-rw-r--r--sc/inc/helpids.h1
-rw-r--r--sc/inc/scfuncs.hrc14
-rw-r--r--sc/qa/extras/scfunctionlistobj.cxx2
-rw-r--r--sc/qa/unit/ucalc.cxx1
-rw-r--r--sc/source/core/data/funcdesc.cxx3
-rw-r--r--sc/source/core/inc/interpre.hxx1
-rw-r--r--sc/source/core/tool/interpr3.cxx297
-rw-r--r--sc/source/core/tool/interpr4.cxx1
-rw-r--r--sc/source/core/tool/parclass.cxx1
-rw-r--r--sc/source/filter/excel/xlformula.cxx3
-rw-r--r--sc/source/filter/oox/formulabase.cxx3
15 files changed, 334 insertions, 5 deletions
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 }
};