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

Winfried Donkers winfrieddonkers at libreoffice.org
Thu Mar 3 16:29:24 UTC 2016


 formula/source/core/resource/core_resource.src |   56 +
 include/formula/compiler.hrc                   |   10 
 include/formula/opcode.hxx                     |    8 
 sc/Library_sc.mk                               |    1 
 sc/inc/helpids.h                               |    8 
 sc/qa/unit/ucalc.cxx                           |    8 
 sc/source/core/inc/interpre.hxx                |   14 
 sc/source/core/tool/interpr3.cxx               |   18 
 sc/source/core/tool/interpr4.cxx               |   10 
 sc/source/core/tool/interpr8.cxx               | 1386 +++++++++++++++++++++++++
 sc/source/core/tool/parclass.cxx               |    7 
 sc/source/filter/excel/xlformula.cxx           |   21 
 sc/source/filter/oox/formulabase.cxx           |   23 
 sc/source/ui/src/scfuncs.src                   |  488 ++++++++
 14 files changed, 2046 insertions(+), 12 deletions(-)

New commits:
commit f336f63da900d76c2bf6e5690f1c8a7bd15a0aa2
Author: Winfried Donkers <winfrieddonkers at libreoffice.org>
Date:   Sat Jan 30 10:14:05 2016 +0100

    tdf#94635 Add FORECAST.ETS functions to Calc
    
    Change-Id: Ifbfff1c27fb3960a06f467630da0fa39665f0ce4
    Reviewed-on: https://gerrit.libreoffice.org/20073
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Eike Rathke <erack at redhat.com>
    Tested-by: Eike Rathke <erack at redhat.com>

diff --git a/formula/source/core/resource/core_resource.src b/formula/source/core/resource/core_resource.src
index dcb0b46..1d6c15d 100644
--- a/formula/source/core/resource/core_resource.src
+++ b/formula/source/core/resource/core_resource.src
@@ -368,6 +368,14 @@ Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF
     String SC_OPCODE_LINEST { Text = "LINEST" ; };
     String SC_OPCODE_LOGEST { Text = "LOGEST" ; };
     String SC_OPCODE_FORECAST { Text = "FORECAST" ; };
+    String SC_OPCODE_FORECAST_ETS_ADD { Text = "FORECAST.ETS" ; };
+    String SC_OPCODE_FORECAST_ETS_SEA { Text = "FORECAST.ETS.SEASONALITY" ; };
+    String SC_OPCODE_FORECAST_ETS_MUL { Text = "FORECAST.ETS.MULT" ; };
+    String SC_OPCODE_FORECAST_ETS_PIA { Text = "FORECAST.ETS.CONFINT" ; };
+    String SC_OPCODE_FORECAST_ETS_PIM { Text = "FORECAST.ETS.PI.MULT" ; };
+    String SC_OPCODE_FORECAST_ETS_STA { Text = "FORECAST.ETS.STAT" ; };
+    String SC_OPCODE_FORECAST_ETS_STM { Text = "FORECAST.ETS.STAT.MULT" ; };
+    String SC_OPCODE_FORECAST_LIN { Text = "FORECAST.LINEAR" ; };
     String SC_OPCODE_CHI_INV { Text = "LEGACY.CHIINV" ; };
     String SC_OPCODE_CHI_INV_MS { Text = "COM.MICROSOFT.CHISQ.INV.RT" ; };
     String SC_OPCODE_GAMMA_DIST { Text = "GAMMADIST" ; };
@@ -794,6 +802,14 @@ Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML
     String SC_OPCODE_LINEST { Text = "LINEST" ; };
     String SC_OPCODE_LOGEST { Text = "LOGEST" ; };
     String SC_OPCODE_FORECAST { Text = "FORECAST" ; };
+    String SC_OPCODE_FORECAST_ETS_ADD { Text = "_xlfn.FORECAST.ETS" ; };
+    String SC_OPCODE_FORECAST_ETS_SEA { Text = "_xlfn.FORECAST.ETS.SEASONALITY" ; };
+    String SC_OPCODE_FORECAST_ETS_MUL { Text = "_xlfn.ORG.LIBREOFFICE.FORECAST.ETS.MULT" ; };
+    String SC_OPCODE_FORECAST_ETS_PIA { Text = "_xlfn.FORECAST.ETS.CONFINT" ; };
+    String SC_OPCODE_FORECAST_ETS_PIM { Text = "_xlfn.ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT" ; };
+    String SC_OPCODE_FORECAST_ETS_STA { Text = "_xlfn.FORECAST.ETS.STAT" ; };
+    String SC_OPCODE_FORECAST_ETS_STM { Text = "_xlfn.ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT" ; };
+    String SC_OPCODE_FORECAST_LIN { Text = "_xlfn.FORECAST.LINEAR" ; };
     String SC_OPCODE_CHI_INV { Text = "CHIINV" ; };
     String SC_OPCODE_CHI_INV_MS { Text = "_xlfn.CHISQ.INV.RT" ; };
     String SC_OPCODE_GAMMA_DIST { Text = "GAMMADIST" ; };
@@ -1220,6 +1236,14 @@ Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH
     String SC_OPCODE_LINEST { Text = "LINEST" ; };
     String SC_OPCODE_LOGEST { Text = "LOGEST" ; };
     String SC_OPCODE_FORECAST { Text = "FORECAST" ; };
+    String SC_OPCODE_FORECAST_ETS_ADD { Text = "FORECAST.ETS.ADD" ; };
+    String SC_OPCODE_FORECAST_ETS_SEA { Text = "FORECAST.ETS.SEASONALITY" ; };
+    String SC_OPCODE_FORECAST_ETS_MUL { Text = "FORECAST.ETS.MULT" ; };
+    String SC_OPCODE_FORECAST_ETS_PIA { Text = "FORECAST.ETS.PI.ADD" ; };
+    String SC_OPCODE_FORECAST_ETS_PIM { Text = "FORECAST.ETS.PI.MULT" ; };
+    String SC_OPCODE_FORECAST_ETS_STA { Text = "FORECAST.ETS.STAT.ADD" ; };
+    String SC_OPCODE_FORECAST_ETS_STM { Text = "FORECAST.ETS.STAT.MULT" ; };
+    String SC_OPCODE_FORECAST_LIN { Text = "FORECAST.LINEAR" ; };
     String SC_OPCODE_CHI_INV { Text = "CHIINV" ; };
     String SC_OPCODE_CHI_INV_MS { Text = "CHISQ.INV.RT" ; };
     String SC_OPCODE_GAMMA_DIST { Text = "GAMMADIST" ; };
@@ -2621,6 +2645,38 @@ Resource RID_STRLIST_FUNCTION_NAMES
     {
         Text [ en-US ] = "FORECAST" ;
     };
+    String SC_OPCODE_FORECAST_ETS_ADD
+    {
+        Text [ en-US ] = "FORECAST.ETS.ADD" ;
+    };
+    String SC_OPCODE_FORECAST_ETS_SEA
+    {
+        Text [ en-US ] = "FORECAST.ETS.SEASONALITY" ;
+    };
+    String SC_OPCODE_FORECAST_ETS_MUL
+    {
+        Text [ en-US ] = "FORECAST.ETS.MULT" ;
+    };
+    String SC_OPCODE_FORECAST_ETS_PIA
+    {
+        Text [ en-US ] = "FORECAST.ETS.PI.ADD" ;
+    };
+    String SC_OPCODE_FORECAST_ETS_PIM
+    {
+        Text [ en-US ] = "FORECAST.ETS.PI.MULT" ;
+    };
+    String SC_OPCODE_FORECAST_ETS_STA
+    {
+        Text [ en-US ] = "FORECAST.ETS.STAT.ADD" ;
+    };
+    String SC_OPCODE_FORECAST_ETS_STM
+    {
+        Text [ en-US ] = "FORECAST.ETS.STAT.MULT" ;
+    };
+    String SC_OPCODE_FORECAST_LIN
+    {
+        Text [ en-US ] = "FORECAST.LINEAR" ;
+    };
     String SC_OPCODE_CHI_INV
     {
         Text [ en-US ] = "CHIINV" ;
diff --git a/include/formula/compiler.hrc b/include/formula/compiler.hrc
index a6a5491..7eadfe5 100644
--- a/include/formula/compiler.hrc
+++ b/include/formula/compiler.hrc
@@ -487,7 +487,15 @@
 #define SC_OPCODE_FLOOR_PRECISE     476
 #define SC_OPCODE_RAWSUBTRACT       477
 #define SC_OPCODE_WEEKNUM_OOO       478
-#define SC_OPCODE_STOP_2_PAR        479     /* last function with two or more parameters' OpCode + 1 */
+#define SC_OPCODE_FORECAST_ETS_ADD  479
+#define SC_OPCODE_FORECAST_ETS_SEA  480
+#define SC_OPCODE_FORECAST_ETS_MUL  481
+#define SC_OPCODE_FORECAST_ETS_PIA  482
+#define SC_OPCODE_FORECAST_ETS_PIM  483
+#define SC_OPCODE_FORECAST_ETS_STA  484
+#define SC_OPCODE_FORECAST_ETS_STM  485
+#define SC_OPCODE_FORECAST_LIN      486
+#define SC_OPCODE_STOP_2_PAR        487     /* 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 6123733..d2ebdfc 100644
--- a/include/formula/opcode.hxx
+++ b/include/formula/opcode.hxx
@@ -421,6 +421,14 @@ enum OpCode : sal_uInt16
         ocLinest            = SC_OPCODE_LINEST,
         ocLogest            = SC_OPCODE_LOGEST,
         ocForecast          = SC_OPCODE_FORECAST,
+        ocForecast_ETS_ADD  = SC_OPCODE_FORECAST_ETS_ADD,
+        ocForecast_ETS_SEA  = SC_OPCODE_FORECAST_ETS_SEA,
+        ocForecast_ETS_MUL  = SC_OPCODE_FORECAST_ETS_MUL,
+        ocForecast_ETS_PIA  = SC_OPCODE_FORECAST_ETS_PIA,
+        ocForecast_ETS_PIM  = SC_OPCODE_FORECAST_ETS_PIM,
+        ocForecast_ETS_STA  = SC_OPCODE_FORECAST_ETS_STA,
+        ocForecast_ETS_STM  = SC_OPCODE_FORECAST_ETS_STM,
+        ocForecast_LIN      = SC_OPCODE_FORECAST_LIN,
         ocChiInv            = SC_OPCODE_CHI_INV,
         ocChiInv_MS         = SC_OPCODE_CHI_INV_MS,
         ocGammaDist         = SC_OPCODE_GAMMA_DIST,
diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk
index 4d6df7a..ff91a9f 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -243,6 +243,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\
     sc/source/core/tool/interpr5 \
     sc/source/core/tool/interpr6 \
     sc/source/core/tool/interpr7 \
+    sc/source/core/tool/interpr8 \
     sc/source/core/tool/jumpmatrix \
     sc/source/core/tool/listenerquery \
     sc/source/core/tool/lookupcache \
diff --git a/sc/inc/helpids.h b/sc/inc/helpids.h
index 252940f..e07f710 100644
--- a/sc/inc/helpids.h
+++ b/sc/inc/helpids.h
@@ -628,5 +628,13 @@
 #define HID_FUNC_FLOOR_PRECISE                                  "SC_HID_FUNC_FLOOR_PRECISE"
 #define HID_FUNC_RAWSUBTRACT                                    "SC_HID_FUNC_RAWSUBTRACT"
 #define HID_FUNC_WEEKNUM_OOO                                    "SC_HID_FUNC_WEEKNUM_OOO"
+#define HID_FUNC_FORECAST_ETS_ADD                               "SC_HID_FUNC_FORECAST_ETS_ADD"
+#define HID_FUNC_FORECAST_ETS_MUL                               "SC_HID_FUNC_FORECAST_ETS_MUL"
+#define HID_FUNC_FORECAST_ETS_PIA                               "SC_HID_FUNC_FORECAST_ETS_PIA"
+#define HID_FUNC_FORECAST_ETS_PIM                               "SC_HID_FUNC_FORECAST_ETS_PIM"
+#define HID_FUNC_FORECAST_ETS_SEA                               "SC_HID_FUNC_FORECAST_ETS_SEA"
+#define HID_FUNC_FORECAST_ETS_STA                               "SC_HID_FUNC_FORECAST_ETS_STA"
+#define HID_FUNC_FORECAST_ETS_STM                               "SC_HID_FUNC_FORECAST_ETS_STM"
+#define HID_FUNC_FORECAST_LIN                                   "SC_HID_FUNC_FORECAST_LIN"
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 121977a..7f155947 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -2619,6 +2619,14 @@ void Test::testFunctionLists()
         "FISHER",
         "FISHERINV",
         "FORECAST",
+        "FORECAST.ETS.ADD",
+        "FORECAST.ETS.MULT",
+        "FORECAST.ETS.PI.ADD",
+        "FORECAST.ETS.PI.MULT",
+        "FORECAST.ETS.SEASONALITY",
+        "FORECAST.ETS.STAT.ADD",
+        "FORECAST.ETS.STAT.MULT",
+        "FORECAST.LINEAR",
         "FTEST",
         "GAMMA",
         "GAMMA.DIST",
diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx
index 8de331d..6d5ceaa 100644
--- a/sc/source/core/inc/interpre.hxx
+++ b/sc/source/core/inc/interpre.hxx
@@ -102,6 +102,17 @@ enum ScIterFuncIfs
     ifCOUNTIFS    // Multi-Conditional count
 };
 
+enum ScETSType
+{
+    etsAdd,
+    etsMult,
+    etsSeason,
+    etsPIAdd,
+    etsPIMult,
+    etsStatAdd,
+    etsStatMult
+};
+
 struct FormulaTokenRef_less
 {
     bool operator () ( const formula::FormulaConstTokenRef& r1, const formula::FormulaConstTokenRef& r2 ) const
@@ -762,6 +773,7 @@ bool CheckMatrix(bool _bLOG,sal_uInt8& nCase,SCSIZE& nCX,SCSIZE& nCY,SCSIZE& nRX
 void ScLinest();
 void ScLogest();
 void ScForecast();
+void ScForecast_Ets( ScETSType eETSType );
 void ScNoName();
 void ScBadName();
 // Statistics:
@@ -772,6 +784,7 @@ public:
 static SC_DLLPUBLIC double phi(double x);
 static SC_DLLPUBLIC double integralPhi(double x);
 static SC_DLLPUBLIC double gaussinv(double x);
+static SC_DLLPUBLIC double GetPercentile( ::std::vector<double> & rArray, double fPercentile );
 
 private:
 double GetBetaDist(double x, double alpha, double beta);  //cumulative distribution function
@@ -841,7 +854,6 @@ void ScSkew();
 void ScSkewp();
 void ScMedian();
 double GetMedian( ::std::vector<double> & rArray );
-double GetPercentile( ::std::vector<double> & rArray, double fPercentile );
 double GetPercentileExclusive( ::std::vector<double> & rArray, double fPercentile );
 void GetNumberSequenceArray( sal_uInt8 nParamCount, ::std::vector<double>& rArray, bool bConvertTextInArray );
 void GetSortArray( sal_uInt8 nParamCount, ::std::vector<double>& rSortArray, ::std::vector<long>* pIndexOrder, bool bConvertTextInArray, bool bAllowEmptyArray );
diff --git a/sc/source/core/tool/interpr3.cxx b/sc/source/core/tool/interpr3.cxx
index a2c9de0..db7ee19 100644
--- a/sc/source/core/tool/interpr3.cxx
+++ b/sc/source/core/tool/interpr3.cxx
@@ -154,7 +154,7 @@ static double lcl_IterateInverse( const ScDistFunc& rFunction, double fAx, doubl
         {
             fAx = fRx; fAy = fRy;
         }
-        // if last iteration brought to small advance, then do bisection next
+        // if last interation brought to small advance, then do bisection next
         // time, for safety
         bHasToInterpolate = bHasToInterpolate && (fabs(fRy) * 2.0 <= fabs(fQy));
         ++nCount;
@@ -3349,12 +3349,6 @@ void ScInterpreter::ScMedian()
 double ScInterpreter::GetPercentile( vector<double> & rArray, double fPercentile )
 {
     size_t nSize = rArray.size();
-    if (rArray.empty() || nSize == 0 || nGlobalError)
-    {
-        SetError( errNoValue);
-        return 0.0;
-    }
-
     if (nSize == 1)
         return rArray[0];
     else
@@ -3420,6 +3414,11 @@ void ScInterpreter::ScPercentile( bool bInclusive )
     }
     vector<double> aArray;
     GetNumberSequenceArray( 1, aArray, false );
+    if ( aArray.empty() || aArray.size() == 0 || nGlobalError )
+    {
+        SetError( errNoValue );
+        return;
+    }
     if ( bInclusive )
         PushDouble( GetPercentile( aArray, alpha ));
     else
@@ -3438,6 +3437,11 @@ void ScInterpreter::ScQuartile( bool bInclusive )
     }
     vector<double> aArray;
     GetNumberSequenceArray( 1, aArray, false );
+    if ( aArray.empty() || aArray.size() == 0 || nGlobalError )
+    {
+        SetError( errNoValue );
+        return;
+    }
     if ( bInclusive )
         PushDouble( fFlag == 2.0 ? GetMedian( aArray ) : GetPercentile( aArray, 0.25 * fFlag ) );
     else
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index d67a026..f316862 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -3993,7 +3993,15 @@ StackVar ScInterpreter::Interpret()
                 case ocGrowth           : ScGrowth();                   break;
                 case ocLinest           : ScLinest();                   break;
                 case ocLogest           : ScLogest();                   break;
-                case ocForecast         : ScForecast();                 break;
+                case ocForecast_LIN     :
+                case ocForecast         : ScForecast();                   break;
+                case ocForecast_ETS_ADD : ScForecast_Ets( etsAdd );       break;
+                case ocForecast_ETS_SEA : ScForecast_Ets( etsSeason );    break;
+                case ocForecast_ETS_MUL : ScForecast_Ets( etsMult );      break;
+                case ocForecast_ETS_PIA : ScForecast_Ets( etsPIAdd );     break;
+                case ocForecast_ETS_PIM : ScForecast_Ets( etsPIMult );    break;
+                case ocForecast_ETS_STA : ScForecast_Ets( etsStatAdd );   break;
+                case ocForecast_ETS_STM : ScForecast_Ets( etsStatMult );  break;
                 case ocGammaLn          :
                 case ocGammaLn_MS       : ScLogGamma();                 break;
                 case ocGamma            : ScGamma();                    break;
diff --git a/sc/source/core/tool/interpr8.cxx b/sc/source/core/tool/interpr8.cxx
new file mode 100644
index 0000000..f88d3c2
--- /dev/null
+++ b/sc/source/core/tool/interpr8.cxx
@@ -0,0 +1,1386 @@
+/* -*- 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/.
+ *
+ */
+
+#include <interpre.hxx>
+#include <global.hxx>
+#include <scmatrix.hxx>
+#include <comphelper/random.hxx>
+#include <formula/token.hxx>
+
+#include <cmath>
+#include <vector>
+
+
+using ::std::vector;
+using namespace formula;
+
+struct DataPoint
+{
+    double X, Y;
+
+    DataPoint() : X( 0.0 ), Y( 0.0 ) {};
+    DataPoint( double rX, double rY ) : X( rX ), Y( rY ) {};
+};
+
+static bool lcl_SortByX( const DataPoint &lhs, const DataPoint &rhs ) { return lhs.X < rhs.X; }
+
+/*
+ * ScETSForecastCalculation
+ *
+ * Class is set up to be used with Calc's FORECAST.ETS
+ * functions and with chart extrapolations (not yet implemented).
+ *
+ * Triple Exponential Smoothing (Holt-Winters method)
+ *
+ * Forecasting of a linear change in data over time (y=a+b*x) with
+ * superimposed absolute or relative seasonal deviations, using additive
+ * respectively multiplicative Holt-Winters method.
+ *
+ * Initialisation and forecasting calculations are based on
+ * Engineering Statistics Handbook, 6.4.3.5 Triple Exponential Smoothing
+ * see "http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc435.htm"
+ * Further to the above is that initial calculation of Seasonal effect
+ * is corrected for trend.
+ *
+ * Prediction Interval calculations are based on
+ * Yar & Chatfield, Prediction Intervals for the Holt-Winters forecasting
+ * procedure, International Journal of Forecasting, 1990, Vol.6, pp127-137
+ * The calculation here is a simplified numerical approximation of the above,
+ * using random distributions.
+ *
+ * Double Exponential Smoothing (Holt-Winters method)
+ *
+ * Forecasting of a linear change in data over time (y=a+b*x), using
+ * the Holt-Winters method.
+ *
+ * Initialisation and forecasting calculations are based on
+ * Engineering Statistics Handbook, 6.4.3.3 Double Exponential Smoothing
+ * see "http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc433.htm"
+ *
+ * Prediction Interval calculations are based on
+ * Statistical Methods for Forecasting, Bovas & Ledolter, 2009, 3.8 Prediction
+ * Intervals for Future Values
+ *
+ */
+class ScETSForecastCalculation
+{
+private:
+    SvNumberFormatter* mpFormatter;
+    vector< DataPoint > maRange;        // data (X, Y)
+    double* mpBase;                     // calculated base value array
+    double* mpTrend;                    // calculated trend factor array
+    double* mpPerIdx;                   // calculated periodical deviation array, not used with eds
+    double* mpForecast;                 // forecasted value array
+    SCSIZE mnSmplInPrd;                 // samples per period
+    double mfStepSize;                  // increment of X in maRange
+    double mfAlpha, mfBeta, mfGamma;    // constants to minimise the RMSE in the ES-equations
+    SCSIZE mnCount;                     // No of data points
+    bool mbInitialised;
+    int mnMonthDay;                     // n-month X-interval, value is day of month
+    // accuracy indicators
+    double mfMAE;                       // mean absolute error
+    double mfMASE;                      // mean absolute scaled error
+    double mfMSE;                       // mean squared error (variation)
+    double mfRMSE;                      // root mean squared error (standard deviation)
+    double mfSMAPE;                     // symmetric mean absolute error
+    sal_uInt16 mnErrorValue;
+    bool bAdditive;                     // true: additive method, false: mulitplicative method
+    bool bEDS;                          // true: EDS, false: ETS
+
+    // constants used in determining best fit for alpha, beta, gamma
+    const double cfMinABCResolution = 0.001;  // minimum change of alpha, beta, gamma
+    const SCSIZE cnScenarios = 1000;          // No. of scenarios to calculate for PI calculations
+
+    bool initData();
+    bool prefillBaseData();
+    bool prefillTrendData();
+    bool prefillPerIdx();
+    bool initCalc();
+    void refill();
+    SCSIZE CalcPeriodLen();
+    void CalcAlphaBetaGamma();
+    void CalcBetaGamma();
+    void CalcGamma();
+    void calcAccuracyIndicators();
+    bool GetForecast( double fTarget, double& rForecast );
+    double RandDev();
+    double convertXtoMonths( double x );
+
+public:
+    ScETSForecastCalculation( SCSIZE nSize, SvNumberFormatter* pFormatter );
+    ~ScETSForecastCalculation();
+
+    bool PreprocessDataRange( ScMatrixRef rMatX, ScMatrixRef rMatY, int& rSmplInPrd,
+                              bool bDataCompletion, int nAggregation, ScMatrixRef rTMat,
+                              ScETSType eETSType );
+    sal_uInt16 GetError() { return mnErrorValue; };
+    bool GetForecastRange( ScMatrixRef rTMat, ScMatrixRef rFcMat );
+    bool GetStatisticValue( ScMatrixRef rTypeMat, ScMatrixRef rStatMat );
+    bool GetSamplesInPeriod( double& rVal );
+    bool GetEDSPredictionIntervals( ScMatrixRef rTMat, ScMatrixRef rPIMat, double fPILevel );
+    bool GetETSPredictionIntervals( ScMatrixRef rTMat, ScMatrixRef rPIMat, double fPILevel );
+};
+
+ScETSForecastCalculation::ScETSForecastCalculation( SCSIZE nSize, SvNumberFormatter* pFormatter )
+{
+    mpFormatter = pFormatter;
+    mnCount = nSize;
+    maRange.reserve( mnCount );
+    mbInitialised = false;
+    mnMonthDay = 0;
+    mpBase     = 0;
+    mpTrend    = 0;
+    mpPerIdx   = 0;
+    mpForecast = 0;
+}
+
+ScETSForecastCalculation::~ScETSForecastCalculation()
+{
+    if ( mpBase )
+        delete mpBase;
+    if ( mpTrend )
+        delete mpTrend;
+    if ( mpPerIdx )
+        delete mpPerIdx;
+    if ( mpForecast )
+        delete mpForecast;
+}
+
+bool ScETSForecastCalculation::PreprocessDataRange( ScMatrixRef rMatX, ScMatrixRef rMatY, int& rSmplInPrd,
+                                                    bool bDataCompletion, int nAggregation, ScMatrixRef rTMat,
+                                                    ScETSType eETSType )
+{
+    bEDS = ( rSmplInPrd == 0 );
+    bAdditive = ( eETSType == etsAdd || eETSType == etsPIAdd || eETSType == etsStatAdd );
+
+    // maRange needs to be sorted by X
+    for ( SCSIZE i = 0; i < mnCount; i++ )
+        maRange.push_back( DataPoint( rMatX->GetDouble( i ), rMatY->GetDouble( i ) ) );
+    sort( maRange.begin(), maRange.end(), lcl_SortByX );
+
+    if ( rTMat )
+    {
+        if ( eETSType != etsPIAdd && eETSType != etsPIMult )
+        {
+            if ( rTMat->GetDouble( 0 ) < maRange[ 0 ].X )
+            {
+                // target cannot be less than start of X-range
+                mnErrorValue = errIllegalFPOperation;
+                return false;
+            }
+        }
+        else
+        {
+            if ( rTMat->GetDouble( 0 ) < maRange[ mnCount - 1 ].X )
+            {
+                // target cannot be before end of X-range
+                mnErrorValue = errIllegalFPOperation;
+                return false;
+            }
+        }
+    }
+
+    if ( rSmplInPrd != 1 )
+        mnSmplInPrd = rSmplInPrd;
+    else
+        mnSmplInPrd = CalcPeriodLen();
+
+    // Month intervals don't have exact stepsize, so first
+    // detect if month interval is used.
+    // Method: assume there is an month interval and verify.
+    // If month interval is used, replace maRange.X with month values
+    // for ease of calculations.
+    Date aNullDate = *( mpFormatter->GetNullDate() );
+    Date aDate = aNullDate + static_cast< long >( maRange[ 0 ].X );
+    mnMonthDay = aDate.GetDay();
+    for ( SCSIZE i = 1; i < mnCount && mnMonthDay; i++ )
+    {
+        Date aDate1 = aNullDate + static_cast< long >( maRange[ i ].X );
+        if ( aDate != aDate1 )
+        {
+            if ( aDate1.GetDay() != mnMonthDay )
+                mnMonthDay = 0;
+        }
+    }
+
+    mfStepSize = ::std::numeric_limits<double>::max();
+    if ( mnMonthDay )
+    {
+        aDate = aNullDate + static_cast< long >( maRange[ 0 ].X );
+        maRange[ 0 ].X = aDate.GetYear() * 12 + aDate.GetMonth();
+    }
+    for ( SCSIZE i = 1; i < mnCount; i++ )
+    {
+        if ( mnMonthDay )
+        {
+            aDate = aNullDate + static_cast< long >( maRange[ i ].X );
+            maRange[ i ].X = aDate.GetYear() * 12 + aDate.GetMonth();
+        }
+        double fStep = maRange[ i ].X - maRange[ i - 1 ].X;
+        if ( fStep == 0.0 )
+        {
+            if ( nAggregation == 0 )
+            {
+                // identical X-values are not allowed
+                mnErrorValue = errNoValue;
+                return false;
+            }
+            double fTmp = maRange[ i - 1 ].Y;
+            SCSIZE nCounter = 1;
+            switch ( nAggregation )
+            {
+                case 1 : // AVERAGE (default)
+                case 7 : // SUM
+                         while ( maRange[ i ].X == maRange[ i - 1 ].X  && i < mnCount )
+                         {
+                             fTmp += maRange[ i ].Y;
+                             nCounter++;
+                             maRange.erase( maRange.begin() + i );
+                             --mnCount;
+                         }
+                         maRange[ i - 1 ].Y = ( nAggregation == 1 ? fTmp / nCounter : fTmp );
+                         break;
+
+                case 2 : // COUNT
+                case 3 : // COUNTA (same as COUNT as there are no non-numeric Y-values)
+                         while ( maRange[ i ].X == maRange[ i - 1 ].X  && i < mnCount )
+                         {
+                             nCounter++;
+                             maRange.erase( maRange.begin() + i );
+                             --mnCount;
+                         }
+                         maRange[ i - 1 ].Y = nCounter;
+                         break;
+
+                case 4 : // MAX
+                         while ( maRange[ i ].X == maRange[ i - 1 ].X  && i < mnCount )
+                         {
+                             if ( maRange[ i ].Y > fTmp )
+                                 fTmp = maRange[ i ].Y;
+                             maRange.erase( maRange.begin() + i );
+                             --mnCount;
+                         }
+                         maRange[ i - 1 ].Y = fTmp;
+                         break;
+
+                case 5 : // MEDIAN
+                         {
+                             vector< double > aTmp;
+                             aTmp.push_back( maRange[ i - 1 ].Y );
+                             while ( maRange[ i ].X == maRange[ i - 1 ].X  && i < mnCount )
+                             {
+                                 aTmp.push_back( maRange[ i ].Y );
+                                 nCounter++;
+                                 maRange.erase( maRange.begin() + i );
+                                 --mnCount;
+                             }
+                             sort( aTmp.begin(), aTmp.end() );
+
+                             if ( nCounter % 2 )
+                                 maRange[ i - 1 ].Y = aTmp[ nCounter / 2 ];
+                             else
+                                 maRange[ i - 1 ].Y = ( aTmp[ nCounter / 2 ] + aTmp[ nCounter / 2 - 1 ] ) / 2.0;
+                         }
+                             break;
+
+                case 6 : // MIN
+                         while ( maRange[ i ].X == maRange[ i - 1 ].X  && i < mnCount )
+                         {
+                             if ( maRange[ i ].Y < fTmp )
+                                 fTmp = maRange[ i ].Y;
+                             maRange.erase( maRange.begin() + i );
+                             --mnCount;
+                         }
+                         maRange[ i - 1 ].Y = fTmp;
+                         break;
+            }
+            if ( i < mnCount - 1 )
+            {
+                i++;
+                if ( mnMonthDay )
+                {
+                    Date aDate1 = aNullDate + static_cast< long >( maRange[ i ].X );
+                    fStep = 12 * ( aDate1.GetYear() - aDate.GetYear() ) +
+                            ( aDate1.GetMonth() - aDate.GetMonth() );
+                    aDate = aDate1;
+                }
+                else
+                    fStep = maRange[ i ].X - maRange[ i - 1 ].X;
+            }
+            else
+               fStep = mfStepSize;
+        }
+        if ( fStep < mfStepSize )
+            mfStepSize = fStep;
+    }
+
+    // step must be constant (or gap multiple of step)
+    bool bHasGap = false;
+    for ( SCSIZE i = 1; i < mnCount && !bHasGap; i++ )
+    {
+        double fStep = maRange[ i ].X - maRange[ i - 1 ].X;
+
+        if ( fStep != mfStepSize )
+        {
+            if ( fmod( fStep, mfStepSize ) != 0.0 )
+            {
+                // step not constant nor multiple of mfStepSize in case of gaps
+                mnErrorValue = errNoValue;
+                return false;
+            }
+            bHasGap = true;
+        }
+    }
+
+    // fill gaps with values depending on bDataCompletion
+    if ( bHasGap )
+    {
+        SCSIZE nMissingXCount = 0;
+        double fOriginalCount = static_cast< double >( mnCount );
+        if ( mnMonthDay )
+            aDate = aNullDate + static_cast< long >( maRange[ 0 ].X );
+        for ( SCSIZE i = 1; i < mnCount; i++ )
+        {
+            double fDist;
+            if ( mnMonthDay )
+            {
+                Date aDate1 = aNullDate + static_cast< long >( maRange[ i ].X );
+                fDist = 12 * ( aDate1.GetYear() - aDate.GetYear() ) +
+                         ( aDate1.GetMonth() - aDate.GetMonth() );
+                aDate = aDate1;
+            }
+            else
+                fDist = maRange[ i ].X - maRange[ i - 1 ].X;
+            if ( fDist > mfStepSize )
+            {
+                // gap, insert missing data points
+                double fYGap = ( maRange[ i ].Y + maRange[ i - 1 ].Y ) / 2.0;
+                for ( double fXGap = maRange[ i - 1].X + mfStepSize;  fXGap < maRange[ i ].X; fXGap += mfStepSize )
+                {
+                    maRange.insert( maRange.begin() + i, DataPoint( fXGap, ( bDataCompletion ? fYGap : 0.0 ) ) );
+                    i++;
+                    mnCount++;
+                    nMissingXCount++;
+                    if ( static_cast< double >( nMissingXCount ) / fOriginalCount > 0.3 )
+                    {
+                        // maximum of 30% missing points exceeded
+                        mnErrorValue = errNoValue;
+                        return false;
+                    }
+                }
+            }
+        }
+    }
+    if ( !initData() )
+        return false;  // note: mnErrorValue is set in called function(s)
+
+    return true;
+}
+
+bool ScETSForecastCalculation::initData( )
+{
+    // give variuous vectors size and initial value
+    mpBase     = new double[ mnCount ];
+    mpTrend    = new double[ mnCount ];
+    if ( !bEDS )
+        mpPerIdx   = new double[ mnCount ];
+    mpForecast = new double[ mnCount ];
+    mpForecast[ 0 ] = maRange[ 0 ].Y;
+
+    if ( prefillTrendData() )
+    {
+        if ( prefillPerIdx() )
+        {
+            if ( prefillBaseData() )
+                return true;
+        }
+    }
+    return false;
+}
+
+bool ScETSForecastCalculation::prefillTrendData()
+{
+    if ( bEDS )
+        mpTrend[ 0 ] = ( maRange[ mnCount - 1 ].Y - maRange[ 0 ].Y ) / static_cast< double >( mnCount - 1 );
+    else
+    {
+        // we need at least 2 periods in the data range
+        if ( mnCount < 2 * mnSmplInPrd )
+        {
+            mnErrorValue = errNoValue;
+            return false;
+        }
+
+        double fSum = 0.0;
+        for ( SCSIZE i = 0; i < mnSmplInPrd; i++ )
+            fSum += maRange[ i + mnSmplInPrd ].Y - maRange[ i ].Y;
+        double fTrend = fSum / static_cast< double >( mnSmplInPrd * mnSmplInPrd );
+
+        mpTrend[ 0 ] = fTrend;
+    }
+
+    return true;
+}
+
+bool ScETSForecastCalculation::prefillPerIdx()
+{
+    if ( !bEDS )
+    {
+        // use as many complete periods as available
+        if ( mnSmplInPrd == 0 )
+        {
+            // should never happen; if mnSmplInPrd equals 0, bEDS is true
+            mnErrorValue = errUnknownState;
+            return false;
+        }
+        SCSIZE nPeriods = mnCount / mnSmplInPrd;
+        vector< double > aPeriodAverage( nPeriods, 0.0 );
+        for ( SCSIZE i = 0; i < nPeriods ; i++ )
+        {
+            for ( SCSIZE j = 0; j < mnSmplInPrd; j++ )
+                aPeriodAverage[ i ] += maRange[ i * mnSmplInPrd + j ].Y;
+            aPeriodAverage[ i ] /= static_cast< double >( mnSmplInPrd );
+            if ( aPeriodAverage[ i ] == 0.0 )
+            {
+                SAL_WARN( "sc.core", "prefillPerIdx(), average of 0 will cause divide by zero error, quitting calculation" );
+                mnErrorValue = errDivisionByZero;
+                return false;
+            }
+        }
+
+        for ( SCSIZE j = 0; j < mnSmplInPrd; j++ )
+        {
+            double fI = 0.0;
+            for ( SCSIZE i = 0; i < nPeriods ; i++ )
+            {
+                // adjust average value for position within period
+                if ( bAdditive )
+                    fI += ( maRange[ i * mnSmplInPrd + j ].Y -
+                            ( aPeriodAverage[ i ] + ( static_cast< double >( j ) - 0.5 * ( mnSmplInPrd - 1 ) ) *
+                              mpTrend[ 0 ] ) );
+                else
+                    fI += ( maRange[ i * mnSmplInPrd + j ].Y /
+                            ( aPeriodAverage[ i ] + ( static_cast< double >( j ) - 0.5 * ( mnSmplInPrd - 1 ) ) *
+                              mpTrend[ 0 ] ) );
+            }
+            mpPerIdx[ j ] = fI / nPeriods;
+        }
+    }
+    return true;
+}
+
+bool ScETSForecastCalculation::prefillBaseData()
+{
+    if ( bEDS )
+        mpBase[ 0 ] = maRange[ 0 ].Y;
+    else
+        mpBase[ 0 ] = maRange[ 0 ].Y / mpPerIdx[ 0 ];
+    return true;
+}
+
+bool ScETSForecastCalculation::initCalc()
+{
+    if ( !mbInitialised )
+    {
+        CalcAlphaBetaGamma();
+
+        mbInitialised = true;
+        calcAccuracyIndicators();
+    }
+    return true;
+}
+
+void ScETSForecastCalculation::calcAccuracyIndicators()
+{
+    double fSumAbsErr     = 0.0;
+    double fSumDivisor    = 0.0;
+    double fSumErrSq      = 0.0;
+    double fSumAbsPercErr = 0.0;
+
+    for ( SCSIZE i = 1; i < mnCount; i++ )
+    {
+        double fError = mpForecast[ i ] - maRange[ i ].Y;
+        fSumAbsErr     += fabs( fError );
+        fSumErrSq      += fError * fError;
+        fSumAbsPercErr += fabs( fError ) / ( fabs( mpForecast[ i ] ) + fabs( maRange[ i ].Y ) );
+    }
+
+    for ( SCSIZE i = 2; i < mnCount; i++ )
+        fSumDivisor += fabs( maRange[ i ].Y - maRange[ i - 1 ].Y );
+
+    int nCalcCount = mnCount - 1;
+    mfMAE   = fSumAbsErr / nCalcCount;
+    mfMASE  = fSumAbsErr / ( nCalcCount * fSumDivisor / ( nCalcCount - 1 ) );
+    mfMSE   = fSumErrSq / nCalcCount;
+    mfRMSE  = sqrt( mfMSE );
+    mfSMAPE = fSumAbsPercErr * 2.0 / nCalcCount;
+}
+
+/*
+ * CalcPeriodLen() calculates the most likely length of a period.
+ *
+ * Method used: for all possible values (between mnCount/2 and 2) compare for
+ * each (sample-previous sample) with next period and calculate mean error.
+ * Use as much samples as possible for each period length and the most recent samples
+ * Return the period length with the lowest mean error.
+ */
+SCSIZE ScETSForecastCalculation::CalcPeriodLen()
+{
+    SCSIZE nBestVal = mnCount;
+    double fBestME = ::std::numeric_limits<double>::max();
+
+    for ( SCSIZE nPeriodLen = mnCount / 2; nPeriodLen > 1; nPeriodLen-- )
+    {
+        double fMeanError = 0.0;
+        SCSIZE nPeriods = mnCount / nPeriodLen;
+        SCSIZE nStart = mnCount - ( nPeriods * nPeriodLen ) + 1;
+        for ( SCSIZE i = nStart; i < ( mnCount - nPeriodLen ); i++ )
+        {
+            fMeanError += fabs( ( maRange[ i ].Y - maRange[ i - 1 ].Y ) -
+                                ( maRange[ nPeriodLen + i ].Y - maRange[ nPeriodLen + i - 1 ].Y ) );
+        }
+        fMeanError /= static_cast< double >( ( nPeriods - 1 ) * nPeriodLen - 1 );
+
+        if ( fMeanError < fBestME || fMeanError == 0.0 )
+        {
+            nBestVal = nPeriodLen;
+            fBestME = fMeanError;
+        }
+    }
+    return nBestVal;
+}
+
+void ScETSForecastCalculation::CalcAlphaBetaGamma()
+{
+    double f0 = 0.0;
+    mfAlpha = f0;
+    if ( bEDS )
+    {
+        mfBeta = 0.0; // beta is not used with EDS
+        CalcGamma();
+    }
+    else
+        CalcBetaGamma();
+    refill();
+    double fE0 = mfMSE;
+
+    double f2 = 1.0;
+    mfAlpha = f2;
+    if ( bEDS )
+        CalcGamma();
+    else
+        CalcBetaGamma();
+    refill();
+    double fE2 = mfMSE;
+
+    double f1 = 0.5;
+    mfAlpha = f1;
+    if ( bEDS )
+        CalcGamma();
+    else
+        CalcBetaGamma();
+    refill();
+
+    if ( fE0 == mfMSE && mfMSE == fE2 )
+    {
+        mfAlpha = 0;
+        if ( bEDS )
+            CalcGamma();
+        else
+            CalcBetaGamma();
+        refill();
+        return;
+    }
+    while ( ( f2 - f1 ) > cfMinABCResolution )
+    {
+        if ( fE2 > fE0 )
+        {
+            f2 = f1;
+            fE2 = mfMSE;
+            f1 = ( f0 + f1 ) / 2;
+        }
+        else
+        {
+            f0 = f1;
+            fE0 = mfMSE;
+            f1 = ( f1 + f2 ) / 2;
+        }
+        mfAlpha = f1;
+        if ( bEDS )
+            CalcGamma();
+        else
+            CalcBetaGamma();
+        refill();
+    }
+    if ( fE2 > fE0 )
+    {
+        if ( fE0 < mfMSE )
+        {
+            mfAlpha = f0;
+            if ( bEDS )
+                CalcGamma();
+            else
+                CalcBetaGamma();
+            refill();
+        }
+    }
+    else
+    {
+        if ( fE2 < mfMSE )
+        {
+            mfAlpha = f2;
+            if ( bEDS )
+                CalcGamma();
+            else
+                CalcBetaGamma();
+            refill();
+        }
+    }
+    calcAccuracyIndicators();
+
+    return;
+}
+
+void ScETSForecastCalculation::CalcBetaGamma()
+{
+    double f0 = 0.0;
+    mfBeta = f0;
+    CalcGamma();
+    refill();
+    double fE0 = mfMSE;
+
+    double f2 = 1.0;
+    mfBeta = f2;
+    CalcGamma();
+    refill();
+    double fE2 = mfMSE;
+
+    double f1 = 0.5;
+    mfBeta = f1;
+    CalcGamma();
+    refill();
+
+    if ( fE0 == mfMSE && mfMSE == fE2 )
+    {
+        mfBeta = 0;
+        CalcGamma();
+        refill();
+        return;
+    }
+    while ( ( f2 - f1 ) > cfMinABCResolution )
+    {
+        if ( fE2 > fE0 )
+        {
+            f2 = f1;
+            fE2 = mfMSE;
+            f1 = ( f0 + f1 ) / 2;
+        }
+        else
+        {
+            f0 = f1;
+            fE0 = mfMSE;
+            f1 = ( f1 + f2 ) / 2;
+        }
+        mfBeta = f1;
+        CalcGamma();
+        refill();
+    }
+    if ( fE2 > fE0 )
+    {
+        if ( fE0 < mfMSE )
+        {
+            mfBeta = f0;
+            CalcGamma();
+            refill();
+        }
+    }
+    else
+    {
+        if ( fE2 < mfMSE )
+        {
+            mfBeta = f2;
+            CalcGamma();
+            refill();
+        }
+    }
+}
+
+void ScETSForecastCalculation::CalcGamma()
+{
+    double f0 = 0.0;
+    mfGamma = f0;
+    refill();
+    double fE0 = mfMSE;
+
+    double f2 = 1.0;
+    mfGamma = f2;
+    refill();
+    double fE2 = mfMSE;
+
+    double f1 = 0.5;
+    mfGamma = f1;
+    refill();
+
+    if ( fE0 == mfMSE && mfMSE == fE2 )
+    {
+        mfGamma = 0;
+        refill();
+        return;
+    }
+    while ( ( f2 - f1 ) > cfMinABCResolution )
+    {
+        if ( fE2 > fE0 )
+        {
+            f2 = f1;
+            fE2 = mfMSE;
+            f1 = ( f0 + f1 ) / 2;
+        }
+        else
+        {
+            f0 = f1;
+            fE0 = mfMSE;
+            f1 = ( f1 + f2 ) / 2;
+        }
+        mfGamma = f1;
+        refill();
+    }
+    if ( fE2 > fE0 )
+    {
+        if ( fE0 < mfMSE )
+        {
+            mfGamma = f0;
+            refill();
+        }
+    }
+    else
+    {
+        if ( fE2 < mfMSE )
+        {
+            mfGamma = f2;
+            refill();
+        }
+    }
+}
+
+void ScETSForecastCalculation::refill()
+{
+    // refill mpBase, mpTrend, mpPerIdx and mpForecast with values
+    // using the calculated mfAlpha, (mfBeta), mfGamma
+    // forecast 1 step ahead
+    for ( SCSIZE i = 1; i < mnCount; i++ )
+    {
+        if ( bEDS )
+        {
+            mpBase[ i ] = mfAlpha * maRange[ i ].Y +
+                          ( 1 - mfAlpha ) * ( mpBase[ i - 1 ] + mpTrend[ i - 1 ] );
+            mpTrend[ i ] = mfGamma * ( mpBase[ i ] - mpBase[ i - 1 ] ) +
+                          ( 1 - mfGamma ) * mpTrend[ i - 1 ];
+            mpForecast[ i ] = mpBase[ i - 1 ] + mpTrend[ i - 1 ];
+        }
+        else
+        {
+            SCSIZE nIdx;
+            if ( bAdditive )
+            {
+                nIdx = ( i > mnSmplInPrd ? i - mnSmplInPrd : i );
+                mpBase[ i ] = mfAlpha * ( maRange[ i ].Y - mpPerIdx[ nIdx ] ) +
+                              ( 1 - mfAlpha ) * ( mpBase[ i - 1 ] + mpTrend[ i - 1 ] );
+                mpPerIdx[ i ] = mfBeta * ( maRange[ i ].Y - mpBase[ i ] ) +
+                                      ( 1 - mfBeta ) * mpPerIdx[ nIdx ];
+            }
+            else
+            {
+                nIdx = ( i >= mnSmplInPrd ? i - mnSmplInPrd : i );
+                mpBase[ i ] = mfAlpha * ( maRange[ i ].Y / mpPerIdx[ nIdx ] ) +
+                              ( 1 - mfAlpha ) * ( mpBase[ i - 1 ] + mpTrend[ i - 1 ] );
+                mpPerIdx[ i ] = mfBeta * ( maRange[ i ].Y / mpBase[ i ] ) +
+                                      ( 1 - mfBeta ) * mpPerIdx[ nIdx ];
+            }
+            mpTrend[ i ] = mfGamma * ( mpBase[ i ] - mpBase[ i - 1 ] ) +
+                          ( 1 - mfGamma ) * mpTrend[ i - 1 ];
+
+            if ( bAdditive )
+                mpForecast[ i ] = mpBase[ i - 1 ] + mpTrend[ i - 1 ] + mpPerIdx[ nIdx ];
+            else
+                mpForecast[ i ] = ( mpBase[ i - 1 ] + mpTrend[ i - 1 ] ) * mpPerIdx[ nIdx ];
+        }
+    }
+    calcAccuracyIndicators();
+}
+
+double ScETSForecastCalculation::convertXtoMonths( double x )
+{
+    Date aNullDate = *( mpFormatter->GetNullDate() );
+    Date aDate = aNullDate + static_cast< long >( x );
+    int nYear = aDate.GetYear();
+    int nMonth = aDate.GetMonth();
+    double fMonthLength;
+    switch ( nMonth )
+    {
+        case  1 :
+        case  3 :
+        case  5 :
+        case  7 :
+        case  8 :
+        case 10 :
+        case 12 :
+            fMonthLength = 31.0;
+            break;
+        case  2 :
+            fMonthLength = ( aDate.IsLeapYear() ? 29.0 : 28.0 );
+            break;
+        default :
+            fMonthLength = 30.0;
+    }
+    return ( 12.0 * nYear + nMonth + ( aDate.GetDay() - mnMonthDay ) / fMonthLength );
+}
+
+bool ScETSForecastCalculation::GetForecast( double fTarget, double& rForecast )
+{
+    if ( !initCalc() )
+        return false;
+
+    if ( fTarget <= maRange[ mnCount - 1 ].X )
+    {
+        SCSIZE n = ( fTarget - maRange[ 0 ].X ) / mfStepSize;
+        double fInterpolate = fmod( fTarget - maRange[ 0 ].X, mfStepSize );
+        rForecast = maRange[ n ].Y;
+
+        if ( fInterpolate >= cfMinABCResolution )
+        {
+            double fInterpolateFactor = fInterpolate / mfStepSize;
+            double fFc_1 = mpForecast[ n + 1 ];
+            rForecast = rForecast + fInterpolateFactor * ( fFc_1 - rForecast );
+        }
+    }
+    else
+    {
+        SCSIZE n = ( fTarget - maRange[ mnCount - 1 ].X ) / mfStepSize;
+        double fInterpolate = fmod( fTarget - maRange[ mnCount - 1 ].X, mfStepSize );
+
+        if ( bEDS )
+            rForecast = mpBase[ mnCount - 1 ] + n * mpTrend[ mnCount - 1 ];
+        else if ( bAdditive )
+            rForecast = mpBase[ mnCount - 1 ] + n * mpTrend[ mnCount - 1 ] +
+                        mpPerIdx[ mnCount - 1 - mnSmplInPrd + ( n % mnSmplInPrd ) ];
+        else
+            rForecast = ( mpBase[ mnCount - 1 ] + n * mpTrend[ mnCount - 1 ] ) *
+                        mpPerIdx[ mnCount - 1 - mnSmplInPrd + ( n % mnSmplInPrd ) ];
+
+        if ( fInterpolate >= cfMinABCResolution )
+        {
+            double fInterpolateFactor = fInterpolate / mfStepSize;
+            double fFc_1;
+            if ( bEDS )
+                fFc_1 = mpBase[ mnCount - 1 ] + ( n + 1 ) * mpTrend[ mnCount - 1 ];
+            else if ( bAdditive )
+                fFc_1 = mpBase[ mnCount - 1 ] + ( n + 1 ) * mpTrend[ mnCount - 1 ] +
+                        mpPerIdx[ mnCount - 1 - mnSmplInPrd + ( ( n + 1 ) % mnSmplInPrd ) ];
+            else
+                fFc_1 = ( mpBase[ mnCount - 1 ] + ( n + 1 ) * mpTrend[ mnCount - 1 ] ) *
+                        mpPerIdx[ mnCount - 1 - mnSmplInPrd + ( ( n + 1 ) % mnSmplInPrd ) ];
+            rForecast = rForecast + fInterpolateFactor * ( fFc_1 - rForecast );
+        }
+    }
+    return true;
+}
+
+bool ScETSForecastCalculation::GetForecastRange( ScMatrixRef rTMat, ScMatrixRef rFcMat )
+{
+    SCSIZE nC, nR;
+    rTMat->GetDimensions( nC, nR );
+
+    for ( SCSIZE i = 0; i < nR; i++ )
+    {
+        for ( SCSIZE j = 0; j < nC; j++ )
+        {
+            double fTarget;
+            if ( mnMonthDay )
+                fTarget = convertXtoMonths( rTMat->GetDouble( j, i ) );
+            else
+                fTarget = rTMat->GetDouble( j, i );
+            double fForecast;
+            if ( GetForecast( fTarget, fForecast ) )
+                rFcMat->PutDouble( fForecast, j, i );
+            else
+                return false;
+        }
+    }
+    return true;
+}
+
+bool ScETSForecastCalculation::GetStatisticValue( ScMatrixRef rTypeMat, ScMatrixRef rStatMat )
+{
+    if ( !initCalc() )
+        return false;
+
+    SCSIZE nC, nR;
+    rTypeMat->GetDimensions( nC, nR );
+    for ( SCSIZE i = 0; i < nR; i++ )
+    {
+        for ( SCSIZE j = 0; j < nC; j++ )
+        {
+            switch ( static_cast< int >( rTypeMat->GetDouble( j, i ) ) )
+            {
+                case 1 : // alpha
+                    rStatMat->PutDouble( mfAlpha, j, i );
+                    break;
+                case 2 : // gamma
+                    rStatMat->PutDouble( mfGamma, j, i );
+                    break;
+                case 3 : // beta
+                    rStatMat->PutDouble( mfBeta, j, i );
+                    break;
+                case 4 : // MASE
+                    rStatMat->PutDouble( mfMASE, j, i );
+                    break;
+                case 5 : // SMAPE
+                    rStatMat->PutDouble( mfSMAPE, j, i );
+                    break;
+                case 6 : // MAE
+                    rStatMat->PutDouble( mfMAE, j, i );
+                    break;
+                case 7 : // RMSE
+                    rStatMat->PutDouble( mfRMSE, j, i );
+                    break;
+                case 8 : // step size
+                    rStatMat->PutDouble( mfStepSize, j, i );
+                    break;
+                case 9 : // samples in period
+                    rStatMat->PutDouble( mnSmplInPrd, j, i );
+                    break;
+            }
+        }
+    }
+    return true;
+}
+
+bool ScETSForecastCalculation::GetSamplesInPeriod( double& rVal )
+{
+    if ( !initCalc() )
+        return false;
+
+    rVal = mnSmplInPrd;
+    return true;
+}
+
+double ScETSForecastCalculation::RandDev()
+{
+    // return a random deviation given the standard deviation
+    return ( mfRMSE * ScInterpreter::gaussinv(
+             ::comphelper::rng::uniform_real_distribution( 0.5, 1.0 ) ) );
+}
+
+bool ScETSForecastCalculation::GetETSPredictionIntervals( ScMatrixRef rTMat, ScMatrixRef rPIMat, double fPILevel )
+{
+    if ( !initCalc() )
+        return false;
+
+    SCSIZE nC, nR;
+    rTMat->GetDimensions( nC, nR );
+
+    // find maximum target value and calculate size of scenario-arrays
+    double fMaxTarget = rTMat->GetDouble( 0, 0 );
+    for ( SCSIZE i = 0; i < nR; i++ )
+    {
+        for ( SCSIZE j = 0; j < nC; j++ )
+        {
+            if ( fMaxTarget < rTMat->GetDouble( j, i ) )
+                fMaxTarget = rTMat->GetDouble( j, i );
+        }
+    }
+    if ( mnMonthDay )
+        fMaxTarget = convertXtoMonths( fMaxTarget ) - maRange[ mnCount - 1 ].X;
+    else
+        fMaxTarget -= maRange[ mnCount - 1 ].X;
+    SCSIZE nSize = ( fMaxTarget / mfStepSize );
+    if ( fmod( fMaxTarget, mfStepSize ) != 0.0 )
+        nSize++;
+
+    std::unique_ptr< double[] > xScenRange( new double[nSize]);
+    std::unique_ptr< double[] > xScenBase( new double[nSize]);
+    std::unique_ptr< double[] > xScenTrend( new double[nSize]);
+    std::unique_ptr< double[] > xScenPerIdx( new double[nSize]);
+    vector< vector< double > >  aPredictions( nSize, vector< double >( cnScenarios ) );
+
+    // fill scenarios
+    for ( SCSIZE k = 0; k < cnScenarios; k++ )
+    {
+        // fill array with forecasts, with RandDev() added to xScenRange
+        if ( bAdditive )
+        {
+            // calculation based on additive model
+            xScenRange[ 0 ] = mpBase[ mnCount - 1 ] + mpTrend[ mnCount - 1 ] +
+                              mpPerIdx[ mnCount - mnSmplInPrd ] +
+                              RandDev();
+            aPredictions[ 0 ][ k ] = xScenRange[ 0 ];
+            xScenBase[ 0 ] = mfAlpha * ( xScenRange[ 0 ] - mpPerIdx[ mnCount - mnSmplInPrd ] ) +
+                             ( 1 - mfAlpha ) * ( mpBase[ mnCount - 1 ] + mpTrend[ mnCount - 1 ] );
+            xScenTrend[ 0 ] = mfGamma * ( xScenBase[ 0 ] - mpBase[ mnCount - 1 ] ) +
+                              ( 1 - mfGamma ) * mpTrend[ mnCount - 1 ];
+            xScenPerIdx[ 0 ] = mfBeta * ( xScenRange[ 0 ] - xScenBase[ 0 ] ) +
+                               ( 1 - mfBeta ) * mpPerIdx[ mnCount - mnSmplInPrd ];
+            for ( SCSIZE i = 1; i < nSize; i++ )
+            {
+                double fPerIdx;
+                if ( i < mnSmplInPrd )
+                    fPerIdx = mpPerIdx[ mnCount + i - mnSmplInPrd ];
+                else
+                    fPerIdx = xScenPerIdx[ i - mnSmplInPrd ];
+                xScenRange[ i ] = xScenBase[ i - 1 ] + xScenTrend[ i - 1 ] + fPerIdx + RandDev();
+                aPredictions[ i ][ k ] = xScenRange[ i ];
+                xScenBase[ i ] = mfAlpha * ( xScenRange[ i ] - fPerIdx ) +
+                                 ( 1 - mfAlpha ) * ( xScenBase[ i - 1 ] + xScenTrend[ i - 1 ] );
+                xScenTrend[ i ] = mfGamma * ( xScenBase[ i ] - xScenBase[ i - 1 ] ) +
+                                  ( 1 - mfGamma ) * xScenTrend[ i - 1 ];
+                xScenPerIdx[ i ] = mfBeta * ( xScenRange[ i ] - xScenBase[ i ] ) +
+                                   ( 1 - mfBeta ) * fPerIdx;
+            }
+        }
+        else
+        {
+            // calculation based on multiplicative model
+            xScenRange[ 0 ] = ( mpBase[ mnCount - 1 ] + mpTrend[ mnCount - 1 ] ) *
+                              mpPerIdx[ mnCount - mnSmplInPrd ] +
+                              RandDev();
+            aPredictions[ 0 ][ k ] = xScenRange[ 0 ];
+            xScenBase[ 0 ] = mfAlpha * ( xScenRange[ 0 ] / mpPerIdx[ mnCount - mnSmplInPrd ] ) +
+                             ( 1 - mfAlpha ) * ( mpBase[ mnCount - 1 ] + mpTrend[ mnCount - 1 ] );
+            xScenTrend[ 0 ] = mfGamma * ( xScenBase[ 0 ] - mpBase[ mnCount - 1 ] ) +
+                              ( 1 - mfGamma ) * mpTrend[ mnCount - 1 ];
+            xScenPerIdx[ 0 ] = mfBeta * ( xScenRange[ 0 ] / xScenBase[ 0 ] ) +
+                               ( 1 - mfBeta ) * mpPerIdx[ mnCount - mnSmplInPrd ];
+            for ( SCSIZE i = 1; i < nSize; i++ )
+            {
+                double fPerIdx;
+                if ( i < mnSmplInPrd )
+                    fPerIdx = mpPerIdx[ mnCount + i - mnSmplInPrd ];
+                else
+                    fPerIdx = xScenPerIdx[ i - mnSmplInPrd ];
+                xScenRange[ i ] = ( xScenBase[ i - 1 ] + xScenTrend[ i - 1 ] ) * fPerIdx + RandDev();
+                aPredictions[ i ][ k ] = xScenRange[ i ];
+                xScenBase[ i ] = mfAlpha * ( xScenRange[ i ] / fPerIdx ) +
+                                 ( 1 - mfAlpha ) * ( xScenBase[ i - 1 ] + xScenTrend[ i - 1 ] );
+                xScenTrend[ i ] = mfGamma * ( xScenBase[ i ] - xScenBase[ i - 1 ] ) +
+                                  ( 1 - mfGamma ) * xScenTrend[ i - 1 ];
+                xScenPerIdx[ i ] = mfBeta * ( xScenRange[ i ] / xScenBase[ i ] ) +
+                                   ( 1 - mfBeta ) * fPerIdx;
+            }
+        }
+    }
+
+    // create array of Percentile values;
+    std::unique_ptr< double[] > xPercentile( new double[nSize]);
+    for ( SCSIZE i = 0; i < nSize; i++ )
+    {
+        xPercentile[ i ] = ScInterpreter::GetPercentile( aPredictions[ i ], ( 1 + fPILevel ) / 2 ) -
+                           ScInterpreter::GetPercentile( aPredictions[ i ], 0.5 );
+    }
+
+    for ( SCSIZE i = 0; i < nR; i++ )
+    {
+        for ( SCSIZE j = 0; j < nC; j++ )
+        {
+            double fTarget;
+            if ( mnMonthDay )
+                fTarget = convertXtoMonths( rTMat->GetDouble( j, i ) ) - maRange[ mnCount - 1 ].X;
+            else
+                fTarget = rTMat->GetDouble( j, i ) - maRange[ mnCount - 1 ].X;
+            SCSIZE nSteps = ( fTarget / mfStepSize ) - 1;
+            double fFactor = fmod( fTarget, mfStepSize );
+            double fPI = xPercentile[ nSteps ];
+            if ( fFactor != 0.0 )
+            {
+                // interpolate
+                double fPI1 = xPercentile[ nSteps + 1 ];
+                fPI = fPI + fFactor * ( fPI1 - fPI );
+            }
+            rPIMat->PutDouble( fPI, j, i );
+        }
+    }
+    return true;
+}
+
+
+bool ScETSForecastCalculation::GetEDSPredictionIntervals( ScMatrixRef rTMat, ScMatrixRef rPIMat, double fPILevel )
+{
+    if ( !initCalc() )
+        return false;
+
+    SCSIZE nC, nR;
+    rTMat->GetDimensions( nC, nR );
+
+    // find maximum target value and calculate size of coefficient- array c
+    double fMaxTarget = rTMat->GetDouble( 0, 0 );
+    for ( SCSIZE i = 0; i < nR; i++ )
+    {
+        for ( SCSIZE j = 0; j < nC; j++ )
+        {
+            if ( fMaxTarget < rTMat->GetDouble( j, i ) )
+                fMaxTarget = rTMat->GetDouble( j, i );
+        }
+    }
+    if ( mnMonthDay )
+        fMaxTarget = convertXtoMonths( fMaxTarget ) - maRange[ mnCount - 1 ].X;
+    else
+        fMaxTarget -= maRange[ mnCount - 1 ].X;
+    SCSIZE nSize = ( fMaxTarget / mfStepSize );
+    if ( fmod( fMaxTarget, mfStepSize ) != 0.0 )
+        nSize++;
+
+    double z = ScInterpreter::gaussinv( ( 1.0 + fPILevel ) / 2.0 );
+    double o = 1 - fPILevel;
+    vector< double > c( nSize );
+    for ( SCSIZE i = 0; i < nSize; i++ )
+    {
+        c[ i ] = sqrt( 1 + ( fPILevel / pow( 1 + o, 3.0 ) ) *
+                 ( ( 1 + 4 * o + 5 * o * o ) +
+                   2 * static_cast< double >( i ) * fPILevel * ( 1 + 3 * o ) +
+                   2 * static_cast< double >( i * i ) * fPILevel * fPILevel ) );
+    }
+
+
+    for ( SCSIZE i = 0; i < nR; i++ )
+    {
+        for ( SCSIZE j = 0; j < nC; j++ )
+        {
+            double fTarget;
+            if ( mnMonthDay )
+                fTarget = convertXtoMonths( rTMat->GetDouble( j, i ) ) - maRange[ mnCount - 1 ].X;
+            else
+                fTarget = rTMat->GetDouble( j, i ) - maRange[ mnCount - 1 ].X;
+            SCSIZE nSteps = ( fTarget / mfStepSize ) - 1;
+            double fFactor = fmod( fTarget, mfStepSize );
+            double fPI = z * mfRMSE * c[ nSteps ] / c[ 0 ];
+            if ( fFactor != 0.0 )
+            {
+                // interpolate
+                double fPI1 = z * mfRMSE * c[ nSteps + 1 ] / c[ 0 ];
+                fPI = fPI + fFactor * ( fPI1 - fPI );
+            }
+            rPIMat->PutDouble( fPI, j, i );
+        }
+    }
+    return true;
+}
+
+
+void ScInterpreter::ScForecast_Ets( ScETSType eETSType )
+{
+    sal_uInt8 nParamCount = GetByte();
+    switch ( eETSType )
+    {
+        case etsAdd :
+        case etsMult :
+        case etsStatAdd :
+        case etsStatMult :
+            if ( !MustHaveParamCount( nParamCount, 3, 6 ) )
+                return;
+            break;
+        case etsPIAdd :
+        case etsPIMult :
+            if ( !MustHaveParamCount( nParamCount, 3, 7 ) )
+            {
+                return;
+            }
+            break;
+        case etsSeason :
+            if ( !MustHaveParamCount( nParamCount, 2, 4 ) )
+                return;
+            break;
+    }
+
+    int nAggregation;
+    if ( ( nParamCount == 6 && eETSType != etsPIAdd && eETSType != etsPIMult ) ||
+         ( nParamCount == 4 && eETSType == etsSeason ) ||
+         nParamCount == 7 )
+        nAggregation = static_cast< int >( GetDoubleWithDefault( 1.0 ) );
+    else
+        nAggregation = 1;
+    if ( nAggregation < 1 || nAggregation > 7 )
+    {
+        PushIllegalParameter();
+        return;
+    }
+
+    bool bDataCompletion;
+    if ( ( nParamCount >= 5 && eETSType != etsPIAdd && eETSType != etsPIMult ) ||
+         ( nParamCount >= 3 && eETSType == etsSeason ) ||
+         ( nParamCount >= 6  && ( eETSType == etsPIAdd || eETSType == etsPIMult ) ) )
+    {
+        int nTemp = static_cast< int >( GetDoubleWithDefault( 1.0 ) );
+        if ( nTemp == 0 || nTemp == 1 )
+            bDataCompletion = nTemp;
+        else
+        {
+            PushIllegalParameter();
+            return;
+        }
+    }
+    else
+        bDataCompletion = true;
+
+    int nSmplInPrd;
+    if ( ( ( nParamCount >= 4 && eETSType != etsPIAdd && eETSType != etsPIMult ) ||
+           ( nParamCount >= 5  && ( eETSType == etsPIAdd || eETSType == etsPIMult ) ) ) &&
+         eETSType != etsSeason )
+    {
+        double fVal = GetDoubleWithDefault( 1.0 );
+        if ( fmod( fVal, 1.0 ) != 0 || fVal < 0.0 )
+        {
+            PushError( errIllegalFPOperation );
+            return;
+        }
+        nSmplInPrd = static_cast< int >( fVal );
+    }
+    else
+        nSmplInPrd = 1;
+
+    // required arguments
+    double fPILevel = 0.0;
+    if ( nParamCount < 3 && !( nParamCount == 2 && eETSType == etsSeason ) )
+    {
+        PushIllegalArgument();
+        return;
+    }
+
+    if ( eETSType == etsPIAdd || eETSType == etsPIMult )
+    {
+        fPILevel = GetDoubleWithDefault( 0.95 );
+        if ( fPILevel < 0 || fPILevel > 1 )
+        {
+            PushIllegalParameter();
+            return;
+        }
+    }
+
+    ScMatrixRef pTypeMat;
+    if ( eETSType == etsStatAdd || eETSType == etsStatMult )
+    {
+        pTypeMat = GetMatrix();
+        SCSIZE nC, nR;
+        pTypeMat->GetDimensions( nC, nR );
+        for ( SCSIZE i = 0; i < nR; i++ )
+        {
+            for ( SCSIZE j = 0; j < nC; j++ )
+            {
+                if ( static_cast< int >( pTypeMat->GetDouble( j, i ) ) < 1 ||
+                     static_cast< int >( pTypeMat->GetDouble( j, i ) ) > 9 )
+                {
+                    PushIllegalParameter();
+                    return;
+                }
+            }
+        }
+    }
+
+    ScMatrixRef pMatX = GetMatrix();
+    ScMatrixRef pMatY = GetMatrix();
+    if ( !pMatX || !pMatY )
+    {
+        PushIllegalParameter();
+        return;
+    }
+    SCSIZE nCX, nCY;
+    SCSIZE nRX, nRY;
+    pMatX->GetDimensions( nCX, nRX );
+    pMatY->GetDimensions( nCY, nRY );
+    if ( nRX != nRY || nCX != nCY ||
+         !pMatX->IsNumeric() || !pMatY->IsNumeric() )
+    {
+        PushIllegalArgument();
+        return;
+    }
+
+    ScMatrixRef pTMat;
+    if ( eETSType != etsStatAdd && eETSType != etsStatMult && eETSType != etsSeason )
+    {
+        pTMat = GetMatrix();
+        if ( !pTMat )
+        {
+            PushIllegalArgument();
+            return;
+        }
+    }
+
+    ScETSForecastCalculation aETSCalc( pMatX->GetElementCount(), pFormatter );
+    if ( !aETSCalc.PreprocessDataRange( pMatX, pMatY, nSmplInPrd, bDataCompletion,
+                                       nAggregation,
+                                       ( eETSType != etsStatAdd && eETSType != etsStatMult ? pTMat : 0 ),
+                                       eETSType ) )
+    {
+        PushError( aETSCalc.GetError() );
+        return;
+    }
+
+    switch ( eETSType )
+    {
+        case etsAdd    :
+        case etsMult   :
+            {
+                SCSIZE nC, nR;
+                pTMat->GetDimensions( nC, nR );
+                ScMatrixRef pFcMat = GetNewMat( nC, nR );
+                if ( aETSCalc.GetForecastRange( pTMat, pFcMat ) )
+                    PushMatrix( pFcMat );
+                else
+                    PushError( aETSCalc.GetError() );
+            }
+            break;
+        case etsPIAdd :
+        case etsPIMult :
+            {
+                SCSIZE nC, nR;
+                pTMat->GetDimensions( nC, nR );
+                ScMatrixRef pPIMat = GetNewMat( nC, nR );
+                if ( nSmplInPrd == 0 )
+                {
+                    if ( aETSCalc.GetEDSPredictionIntervals( pTMat, pPIMat, fPILevel ) )
+                        PushMatrix( pPIMat );
+                    else
+                        PushError( aETSCalc.GetError() );
+                }
+                else
+                {
+                    if ( aETSCalc.GetETSPredictionIntervals( pTMat, pPIMat, fPILevel ) )
+                        PushMatrix( pPIMat );
+                    else
+                        PushError( aETSCalc.GetError() );
+                }
+            }
+            break;
+            break;
+        case etsStatAdd  :
+        case etsStatMult :
+            {
+                SCSIZE nC, nR;
+                pTypeMat->GetDimensions( nC, nR );
+                ScMatrixRef pStatMat = GetNewMat( nC, nR );
+                if ( aETSCalc.GetStatisticValue( pTypeMat, pStatMat ) )
+                    PushMatrix( pStatMat );
+                else
+                    PushError( aETSCalc.GetError() );
+            }
+            break;
+        case etsSeason :
+            {
+                double rVal;
+                if ( aETSCalc.GetSamplesInPeriod( rVal ) )
+                    PushDouble( rVal );
+                else
+                    PushError( aETSCalc.GetError() );
+            }
+            break;
+    }
+
+    return;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/parclass.cxx b/sc/source/core/tool/parclass.cxx
index b676bdf..bad5c65 100644
--- a/sc/source/core/tool/parclass.cxx
+++ b/sc/source/core/tool/parclass.cxx
@@ -212,6 +212,13 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
     { ocNetWorkdays_MS,  {{ Value, Value, Value, Reference                       }, 0 }},
     { ocWorkday_MS,      {{ Value, Value, Value, Reference                       }, 0 }},
     { ocAggregate,       {{ Value, Value, Reference                              }, 1 }},
+    { ocForecast_ETS_ADD, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value        }, 0 }},
+    { ocForecast_ETS_MUL, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value        }, 0 }},
+    { ocForecast_ETS_PIA, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value, Value }, 0 }},
+    { ocForecast_ETS_PIM, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value, Value }, 0 }},
+    { ocForecast_ETS_SEA, {{ ForceArray, ForceArray, Value, Value                           }, 0 }},
+    { ocForecast_ETS_STA, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value        }, 0 }},
+    { ocForecast_ETS_STM, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value        }, 0 }},
     // Excel doubts:
     // ocN, ocT: Excel says (and handles) Reference, error? This means no
     // position dependent SingleRef if DoubleRef, and no array calculation,
diff --git a/sc/source/filter/excel/xlformula.cxx b/sc/source/filter/excel/xlformula.cxx
index 998634f..3a7952d 100644
--- a/sc/source/filter/excel/xlformula.cxx
+++ b/sc/source/filter/excel/xlformula.cxx
@@ -566,6 +566,21 @@ static const XclFunctionInfo saFuncTable_2013[] =
     EXC_FUNCENTRY_V_VR(         ocErrorType_ODF, 1,  1,  0,  "ERROR.TYPE" )
 };
 
+/** Functions new in Excel 2016.
+
+    See https://support.office.com/en-us/article/Forecasting-functions-897a2fe9-6595-4680-a0b0-93e0308d5f6e?ui=en-US&rs=en-US&ad=US#_forecast.ets
+
+    @See sc/source/filter/oox/formulabase.cxx saFuncTable2016 for V,VR,RO,...
+ */
+static const XclFunctionInfo saFuncTable_2016[] =
+{
+    EXC_FUNCENTRY_V_VR(  ocForecast_ETS_ADD,    3,  6,  0,  "FORECAST.ETS" ),
+    EXC_FUNCENTRY_V_VR(  ocForecast_ETS_PIA,    3,  7,  0,  "FORECAST.ETS.CONFINT" ),
+    EXC_FUNCENTRY_V_VR(  ocForecast_ETS_SEA,    2,  4,  0,  "FORECAST.ETS.SEASONALITY" ),
+    EXC_FUNCENTRY_V_VR(  ocForecast_ETS_STA,    3,  6,  0,  "FORECAST.ETS.STAT" ),
+    EXC_FUNCENTRY_V_VR(  ocForecast_LIN,        3,  3,  0,  "FORECAST.LINEAR" )
+};
+
 #define EXC_FUNCENTRY_ODF( opcode, minparam, maxparam, flags, asciiname ) \
     { opcode, NOID, minparam,     maxparam,     V, { VR },       EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) }, \
     { opcode,  255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) }
@@ -589,7 +604,10 @@ static const XclFunctionInfo saFuncTable_OOoLO[] =
     EXC_FUNCENTRY_OOO( ocConvert,       3,  3,  0,  "ORG.OPENOFFICE.CONVERT" ),
     EXC_FUNCENTRY_OOO( ocColor,         3,  4,  0,  "ORG.LIBREOFFICE.COLOR" ),
     EXC_FUNCENTRY_OOO( ocRawSubtract,   2, MX,  0,  "ORG.LIBREOFFICE.RAWSUBTRACT" ),
-    EXC_FUNCENTRY_OOO( ocWeeknumOOo,    2,  2,  0,  "ORG.LIBREOFFICE.WEEKNUM_OOO" )
+    EXC_FUNCENTRY_OOO( ocWeeknumOOo,    2,  2,  0,  "ORG.LIBREOFFICE.WEEKNUM_OOO" ),
+    EXC_FUNCENTRY_OOO( ocForecast_ETS_MUL, 3,  6,  0,  "ORG.LIBREOFFICE.FORECAST.ETS.MULT" ),
+    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" )
 };
 
 #undef EXC_FUNCENTRY_OOO
@@ -616,6 +634,7 @@ XclFunctionProvider::XclFunctionProvider( const XclRoot& rRoot )
     (this->*pFillFunc)( saFuncTable_Oox, STATIC_ARRAY_END( saFuncTable_Oox ) );
     (this->*pFillFunc)( saFuncTable_2010, STATIC_ARRAY_END( saFuncTable_2010 ) );
     (this->*pFillFunc)( saFuncTable_2013, STATIC_ARRAY_END( saFuncTable_2013 ) );
+    (this->*pFillFunc)( saFuncTable_2016, STATIC_ARRAY_END( saFuncTable_2016 ) );
     (this->*pFillFunc)( saFuncTable_Odf, STATIC_ARRAY_END( saFuncTable_Odf ) );
     (this->*pFillFunc)( saFuncTable_OOoLO, STATIC_ARRAY_END( saFuncTable_OOoLO ) );
 }
diff --git a/sc/source/filter/oox/formulabase.cxx b/sc/source/filter/oox/formulabase.cxx
index 7d34721..1bf125e 100644
--- a/sc/source/filter/oox/formulabase.cxx
+++ b/sc/source/filter/oox/formulabase.cxx
@@ -895,6 +895,23 @@ static const FunctionData saFuncTable2013[] =
     { "ERROR.TYPE",             "ERROR.TYPE",           NOID,   NOID,   1,  1,  V, { VR }, FUNCFLAG_MACROCALL_NEW }
 };
 
+/** Functions new in Excel 2016.
+
+    See https://support.office.com/en-us/article/Forecasting-functions-897a2fe9-6595-4680-a0b0-93e0308d5f6e?ui=en-US&rs=en-US&ad=US#_forecast.ets
+
+    @See sc/source/filter/excel/xlformula.cxx saFuncTable_2016
+ */
+/* FIXME: BIFF12 function identifiers available? Where to obtain? */
+static const FunctionData saFuncTable2016[] =
+{
+    { "FORECAST.ETS",             "FORECAST.ETS",             NOID,   NOID,   3,  6,  V, { VR, VA, VR }, FUNCFLAG_MACROCALL_NEW },
+    { "FORECAST.ETS.CONFINT",     "FORECAST.ETS.CONFINT",     NOID,   NOID,   4,  7,  V, { VR, VA, VR }, FUNCFLAG_MACROCALL_NEW },
+    { "FORECAST.ETS.SEASONALITY", "FORECAST.ETS.SEASONALITY", NOID,   NOID,   2,  4,  V, { VR, VA, VR }, FUNCFLAG_MACROCALL_NEW },
+    { "FORECAST.ETS.STAT",        "FORECAST.ETS.STAT",        NOID,   NOID,   3,  6,  V, { VR, VA, VR }, FUNCFLAG_MACROCALL_NEW },
+    { "FORECAST.LINEAR",          "FORECAST.LINEAR",          NOID,   NOID,   3,  3,  V, { VR, VA }, FUNCFLAG_MACROCALL_NEW }
+};
+
+
 /** Functions defined by OpenFormula, but not supported by Calc or by Excel. */
 static const FunctionData saFuncTableOdf[] =
 {
@@ -926,7 +943,10 @@ static const FunctionData saFuncTableOOoLO[] =
     // Other functions.
     { "ORG.OPENOFFICE.CONVERT",     "ORG.OPENOFFICE.CONVERT",   NOID,   NOID,   3,  3,  V, { VR }, FUNCFLAG_MACROCALL_NEW },
     { "ORG.LIBREOFFICE.COLOR",      "ORG.LIBREOFFICE.COLOR",    NOID,   NOID,   3,  4,  V, { VR }, FUNCFLAG_MACROCALL_NEW },
-    { "ORG.LIBREOFFICE.RAWSUBTRACT","ORG.LIBREOFFICE.RAWSUBTRACT",NOID, NOID,   1, MX,  V, { RX }, FUNCFLAG_MACROCALL_NEW }
+    { "ORG.LIBREOFFICE.RAWSUBTRACT","ORG.LIBREOFFICE.RAWSUBTRACT",NOID, NOID,   1, MX,  V, { RX }, FUNCFLAG_MACROCALL_NEW },
+    { "ORG.LIBREOFFICE.FORECAST.ETS.MULT",      "FORECAST.ETS.MULT",      NOID,   NOID,   3,  6,  V, { VR, VA, VR }, FUNCFLAG_MACROCALL_NEW },
+    { "ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT",   "FORECAST.ETS.PI.MULT",   NOID,   NOID,   4,  7,  V, { VR, VA, VR }, FUNCFLAG_MACROCALL_NEW },
+    { "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT", "FORECAST.ETS.STAT.MULT", NOID,   NOID,   3,  6,  V, { VR, VA, VR }, FUNCFLAG_MACROCALL_NEW }
 };
 
 const sal_Unicode API_TOKEN_OPEN            = '(';
@@ -1045,6 +1065,7 @@ FunctionProviderImpl::FunctionProviderImpl( FilterType eFilter, BiffType eBiff,
     initFuncs( saFuncTableOox, STATIC_ARRAY_END( saFuncTableOox ), nMaxParam, bImportFilter, eFilter );
     initFuncs( saFuncTable2010, STATIC_ARRAY_END( saFuncTable2010 ), nMaxParam, bImportFilter, eFilter );
     initFuncs( saFuncTable2013, STATIC_ARRAY_END( saFuncTable2013 ), nMaxParam, bImportFilter, eFilter );
+    initFuncs( saFuncTable2016, STATIC_ARRAY_END( saFuncTable2016 ), nMaxParam, bImportFilter, eFilter );
     initFuncs( saFuncTableOdf, STATIC_ARRAY_END( saFuncTableOdf ), nMaxParam, bImportFilter, eFilter );
     initFuncs( saFuncTableOOoLO, STATIC_ARRAY_END( saFuncTableOOoLO ), nMaxParam, bImportFilter, eFilter );
 }
diff --git a/sc/source/ui/src/scfuncs.src b/sc/source/ui/src/scfuncs.src
index 70508b3..33475bd 100644
--- a/sc/source/ui/src/scfuncs.src
+++ b/sc/source/ui/src/scfuncs.src
@@ -10061,6 +10061,494 @@ Resource RID_SC_FUNCTION_DESCRIPTIONS2
             Text [ en-US ] = "The X data array." ;
         };
     };
+     // -=*# Resource for function FORECAST.ETS #*=-
+    Resource SC_OPCODE_FORECAST_ETS_ADD
+    {
+        String 1 // Description
+        {
+            Text [ en-US ] = "Calculates future value(s) using additive Exponential Smoothing algorithm." ;
+        };
+        ExtraData =
+        {
+            0;
+            ID_FUNCTION_GRP_STATISTIC;
+            HID_FUNC_FORECAST_ETS_ADD;
+            6;  0;  0;  0;  1;  1;  1;
+            0;
+        };
+        String 2 // Name of Parameter 1
+        {
+            Text [ en-US ] = "date" ;
+        };
+        String 3 // Description of Parameter 1
+        {
+            Text [ en-US ] = "The date (array) for which you want to predict a value." ;
+        };
+        String 4 // Name of Parameter 2
+        {
+            Text [ en-US ] = "data_Y" ;
+        };
+        String 5 // Description of Parameter 2
+        {
+            Text [ en-US ] = "The data array from which you want to forecast." ;
+        };
+        String 6 // Name of Parameter 3
+        {
+            Text [ en-US ] = "data_X" ;
+        };
+        String 7 // Description of Parameter 3
+        {
+            Text [ en-US ] = "The date or numeric array; a consistent step between values is needed." ;
+        };
+        String 8 // Name of Parameter 4
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 9 // Description of Parameter 4
+        {
+            Text [ en-US ] = "Number of Samples in Period (default 1); length of the seasonal pattern." ;
+        };
+        String 10 // Name of Parameter 5
+        {
+            Text [ en-US ] = "boolean" ;
+        };
+        String 11 // Description of Parameter 5
+        {
+            Text [ en-US ] = "Data completion (default 1); 0 treats missing points as zero, 1 interpolates." ;
+        };
+        String 12 // Name of Parameter 6
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 13 // Description of Parameter 6
+        {
+            Text [ en-US ] = "Aggegration (default 0 = AVERAGE); method to be used to aggegrate identical (time) values." ;
+        };
+    };
+     // -=*# Resource for function FORECAST.ETS.MULT #*=-
+    Resource SC_OPCODE_FORECAST_ETS_MUL
+    {
+        String 1 // Description
+        {
+            Text [ en-US ] = "Calculates future value(s) using multiplicative Exponential Smoothing algorithm." ;
+        };
+        ExtraData =
+        {
+            0;
+            ID_FUNCTION_GRP_STATISTIC;
+            HID_FUNC_FORECAST_ETS_MUL;
+            6;  0;  0;  0;  1;  1;  1;
+            0;
+        };
+        String 2 // Name of Parameter 1
+        {
+            Text [ en-US ] = "date" ;
+        };
+        String 3 // Description of Parameter 1
+        {
+            Text [ en-US ] = "The date (array) for which you want to predict a value." ;
+        };
+        String 4 // Name of Parameter 2
+        {
+            Text [ en-US ] = "data_Y" ;
+        };
+        String 5 // Description of Parameter 2
+        {
+            Text [ en-US ] = "The data array from which you want to forecast." ;
+        };
+        String 6 // Name of Parameter 3
+        {
+            Text [ en-US ] = "data_X" ;
+        };
+        String 7 // Description of Parameter 3
+        {
+            Text [ en-US ] = "The date or numeric array; a consistent step between values is needed." ;
+        };
+        String 8 // Name of Parameter 4
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 9 // Description of Parameter 4
+        {
+            Text [ en-US ] = "Number of Samples in Period (default 1); length of the seasonal pattern." ;
+        };
+        String 10 // Name of Parameter 5
+        {
+            Text [ en-US ] = "boolean" ;
+        };
+        String 11 // Description of Parameter 5
+        {
+            Text [ en-US ] = "Data completion (default 1); 0 treats missing points as zero, 1 interpolates." ;
+        };
+        String 12 // Name of Parameter 6
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 13 // Description of Parameter 6
+        {
+            Text [ en-US ] = "Aggegration (default 0 = AVERAGE); method to be used to aggegrate identical (time) values." ;
+        };
+    };
+     // -=*# Resource for function FORECAST.ETS.CONFINT #*=-
+    Resource SC_OPCODE_FORECAST_ETS_PIA
+    {
+        String 1 // Description
+        {
+            Text [ en-US ] = "Returns a prediction interval at the specified target value(s) for additive Exponential Smoothing method" ;
+        };
+        ExtraData =
+        {
+            0;
+            ID_FUNCTION_GRP_STATISTIC;
+            HID_FUNC_FORECAST_ETS_PIA;
+            7;  0;  0;  0;  1;  1;  1;  1;
+            0;
+        };
+        String 2 // Name of Parameter 1
+        {
+            Text [ en-US ] = "date" ;
+        };
+        String 3 // Description of Parameter 1
+        {
+            Text [ en-US ] = "The date (array) for which you want to predict a value." ;
+        };
+        String 4 // Name of Parameter 2
+        {
+            Text [ en-US ] = "data_Y" ;
+        };
+        String 5 // Description of Parameter 2
+        {
+            Text [ en-US ] = "The data array from which you want to forecast." ;
+        };
+        String 6 // Name of Parameter 3
+        {
+            Text [ en-US ] = "data_X" ;
+        };
+        String 7 // Description of Parameter 3
+        {
+            Text [ en-US ] = "The date or numeric array; a consistent step between values is needed." ;
+        };
+        String 8 // Name of Parameter 4
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 9 // Description of Parameter 4
+        {
+            Text [ en-US ] = "Confidence level (default 0.95); value 0 to 1 (exclusive) for 0 to 100% calculated prediction interval." ;
+        };
+        String 10 // Name of Parameter 5
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 11 // Description of Parameter 5
+        {
+            Text [ en-US ] = "Number of Samples in Period (default 1); length of the seasonal pattern." ;
+        };
+        String 12 // Name of Parameter 6
+        {
+            Text [ en-US ] = "boolean" ;
+        };
+        String 13 // Description of Parameter 6
+        {
+            Text [ en-US ] = "Data completion (default 1); 0 treats missing points as zero, 1 interpolates." ;
+        };
+        String 14 // Name of Parameter 7
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 15 // Description of Parameter 7
+        {
+            Text [ en-US ] = "Aggegration (default 1 = AVERAGE); method to be used to aggegrate identical (time) values." ;
+        };
+    };
+     // -=*# Resource for function FORECAST.ETS.PI.MULT #*=-
+    Resource SC_OPCODE_FORECAST_ETS_PIM
+    {
+        String 1 // Description
+        {
+            Text [ en-US ] = "Returns a prediction interval at the specified target value(s) for multiplicative Exponential Smoothing method" ;
+        };
+        ExtraData =
+        {
+            0;
+            ID_FUNCTION_GRP_STATISTIC;
+            HID_FUNC_FORECAST_ETS_PIM;
+            7;  0;  0;  0;  1;  1;  1;  1;
+            0;
+        };
+        String 2 // Name of Parameter 1
+        {
+            Text [ en-US ] = "date" ;
+        };
+        String 3 // Description of Parameter 1
+        {
+            Text [ en-US ] = "The date (array) for which you want to predict a value." ;
+        };
+        String 4 // Name of Parameter 2
+        {
+            Text [ en-US ] = "data_Y" ;
+        };
+        String 5 // Description of Parameter 2
+        {
+            Text [ en-US ] = "The data array from which you want to forecast." ;
+        };
+        String 6 // Name of Parameter 3
+        {
+            Text [ en-US ] = "data_X" ;
+        };
+        String 7 // Description of Parameter 3
+        {
+            Text [ en-US ] = "The date or numeric array; a consistent step between values is needed." ;
+        };
+        String 8 // Name of Parameter 4
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 9 // Description of Parameter 4
+        {
+            Text [ en-US ] = "Confidence level (default 0.95); value 0 to 1 (exclusive) for 0 to 100% calculated prediction interval." ;
+        };
+        String 10 // Name of Parameter 5
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 11 // Description of Parameter 5
+        {
+            Text [ en-US ] = "Number of Samples in Period (default 1); length of the seasonal pattern." ;
+        };
+        String 12 // Name of Parameter 6
+        {
+            Text [ en-US ] = "boolean" ;
+        };
+        String 13 // Description of Parameter 6
+        {
+            Text [ en-US ] = "Data completion (default 1); 0 treats missing points as zero, 1 interpolates." ;
+        };
+        String 14 // Name of Parameter 7
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 15 // Description of Parameter 7
+        {
+            Text [ en-US ] = "Aggegration (default 1 = AVERAGE); method to be used to aggegrate identical (time) values." ;
+        };
+    };
+     // -=*# Resource for function FORECAST.ETS.SEASONALITY #*=-
+    Resource SC_OPCODE_FORECAST_ETS_SEA
+    {
+        String 1 // Description
+        {
+            Text [ en-US ] = "Calculates the number of samples in period (season) using additive Exponential Triple Smoothing algorithm." ;
+        };
+        ExtraData =
+        {
+            0;
+            ID_FUNCTION_GRP_STATISTIC;
+            HID_FUNC_FORECAST_ETS_SEA;
+            4;  0;  0;  1;  1;
+            0;
+        };
+        String 2 // Name of Parameter 1
+        {
+            Text [ en-US ] = "data_Y" ;
+        };
+        String 3 // Description of Parameter 1
+        {
+            Text [ en-US ] = "The data array from which you want to forecast." ;
+        };
+        String 4 // Name of Parameter 2
+        {
+            Text [ en-US ] = "data_X" ;
+        };
+        String 5 // Description of Parameter 2
+        {
+            Text [ en-US ] = "The date or numeric array; a consistent step between values is needed." ;
+        };
+        String 6 // Name of Parameter 3
+        {
+            Text [ en-US ] = "boolean" ;
+        };
+        String 7 // Description of Parameter 3
+        {
+            Text [ en-US ] = "Data completion (default 1); 0 treats missing points as zero, 1 interpolates." ;
+        };
+        String 8 // Name of Parameter 4
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 9 // Description of Parameter 4
+        {
+            Text [ en-US ] = "Aggegration (default 1 = AVERAGE); method to be used to aggegrate identical (time) values." ;
+        };
+    };
+     // -=*# Resource for function FORECAST.ETS.STAT #*=-
+    Resource SC_OPCODE_FORECAST_ETS_STA
+    {
+        String 1 // Description
+        {
+            Text [ en-US ] = "Returns statistical value(s) using additive Exponential Smoothing algorithm." ;
+        };
+        ExtraData =
+        {
+            0;
+            ID_FUNCTION_GRP_STATISTIC;
+            HID_FUNC_FORECAST_ETS_STA;
+            6;  0;  0;  0;  1;  1;  1;
+            0;
+        };
+        String 2 // Name of Parameter 1
+        {
+            Text [ en-US ] = "data_Y" ;
+        };
+        String 3 // Description of Parameter 1
+        {
+            Text [ en-US ] = "The data array from which you want to forecast." ;
+        };
+        String 4 // Name of Parameter 2
+        {
+            Text [ en-US ] = "data_X" ;
+        };
+        String 5 // Description of Parameter 2
+        {
+            Text [ en-US ] = "The date or numeric array; a consistent step between values is needed." ;
+        };
+        String 6 // Name of Parameter 3
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 7 // Description of Parameter 3
+        {
+            Text [ en-US ] = "Value (1-9) or array of values, indicating which statistic will be returned for the calculated forecast" ;
+        };
+        String 8 // Name of Parameter 4
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 9 // Description of Parameter 4
+        {
+            Text [ en-US ] = "Number of Samples in Period (default 1); length of the seasonal pattern." ;
+        };
+        String 10 // Name of Parameter 5
+        {
+            Text [ en-US ] = "boolean" ;
+        };
+        String 11 // Description of Parameter 5
+        {
+            Text [ en-US ] = "Data completion (default 1); 0 treats missing points as zero, 1 interpolates." ;
+        };
+        String 12 // Name of Parameter 6
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 13 // Description of Parameter 6
+        {
+            Text [ en-US ] = "Aggegration (default 1 = AVERAGE); method to be used to aggegrate identical (time) values." ;
+        };
+    };
+     // -=*# Resource for function FORECAST.ETS.STAT.MULT #*=-
+    Resource SC_OPCODE_FORECAST_ETS_STM
+    {
+        String 1 // Description
+        {
+            Text [ en-US ] = "Returns statistical value(s) using multiplicative Exponential Smoothing algorithm." ;
+        };
+        ExtraData =
+        {
+            0;
+            ID_FUNCTION_GRP_STATISTIC;
+            HID_FUNC_FORECAST_ETS_STM;
+            6;  0;  0;  0;  1;  1;  1;
+            0;
+        };
+        String 2 // Name of Parameter 1
+        {
+            Text [ en-US ] = "data_Y" ;
+        };
+        String 3 // Description of Parameter 1
+        {
+            Text [ en-US ] = "The data array from which you want to forecast." ;
+        };
+        String 4 // Name of Parameter 2
+        {
+            Text [ en-US ] = "data_X" ;
+        };
+        String 5 // Description of Parameter 2
+        {
+            Text [ en-US ] = "The date or numeric array; a consistent step between values is needed." ;
+        };
+        String 6 // Name of Parameter 3
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 7 // Description of Parameter 3
+        {
+            Text [ en-US ] = "Value (1-9) or array of values, indicating which statistic will be returned for the calculated forecast" ;
+        };
+        String 8 // Name of Parameter 4
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 9 // Description of Parameter 4
+        {
+            Text [ en-US ] = "Number Of Samples in Period (default 1); length of the seasonal pattern." ;
+        };
+        String 10 // Name of Parameter 5
+        {
+            Text [ en-US ] = "boolean" ;
+        };
+        String 11 // Description of Parameter 5
+        {
+            Text [ en-US ] = "Data completion (default 1); 0 treats missing points as zero, 1 interpolates." ;
+        };
+        String 12 // Name of Parameter 6
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 13 // Description of Parameter 6
+        {
+            Text [ en-US ] = "Aggegration (default 1 = AVERAGE); method to be used to aggegrate identical (time) values." ;
+        };
+    };
+     // -=*# Resource for function FORECAST.LINEAR #*=-
+    Resource SC_OPCODE_FORECAST_LIN
+    {
+        String 1 // Description
+        {
+            Text [ en-US ] = "Returns a value along a linear regression" ;
+        };
+        ExtraData =
+        {
+            0;
+            ID_FUNCTION_GRP_STATISTIC;
+            HID_FUNC_FORECAST_LIN;
+            3;  0;  0;  0;
+            0;
+        };
+        String 2 // Name of Parameter 1
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 3 // Description of Parameter 1
+        {
+            Text [ en-US ] = "The X value for which the Y value on the regression linear is to be calculated." ;
+        };
+        String 4 // Name of Parameter 2
+        {
+            Text [ en-US ] = "data_Y" ;
+        };
+        String 5 // Description of Parameter 2
+        {
+            Text [ en-US ] = "The Y data array." ;
+        };
+        String 6 // Name of Parameter 3
+        {
+            Text [ en-US ] = "data_X" ;
+        };
+        String 7 // Description of Parameter 3
+        {
+            Text [ en-US ] = "The X data array." ;
+        };
+    };
      // -=*# Resource for function ADDRESS #*=-
     Resource SC_OPCODE_ADDRESS
     {


More information about the Libreoffice-commits mailing list