summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--formula/source/core/resource/core_resource.src56
-rw-r--r--include/formula/compiler.hrc10
-rw-r--r--include/formula/opcode.hxx8
-rw-r--r--sc/Library_sc.mk1
-rw-r--r--sc/inc/helpids.h8
-rw-r--r--sc/qa/unit/ucalc.cxx8
-rw-r--r--sc/source/core/inc/interpre.hxx14
-rw-r--r--sc/source/core/tool/interpr3.cxx18
-rw-r--r--sc/source/core/tool/interpr4.cxx10
-rw-r--r--sc/source/core/tool/interpr8.cxx1386
-rw-r--r--sc/source/core/tool/parclass.cxx7
-rw-r--r--sc/source/filter/excel/xlformula.cxx21
-rw-r--r--sc/source/filter/oox/formulabase.cxx23
-rw-r--r--sc/source/ui/src/scfuncs.src488
14 files changed, 2046 insertions, 12 deletions
diff --git a/formula/source/core/resource/core_resource.src b/formula/source/core/resource/core_resource.src
index dcb0b46ccf95..1d6c15d0458a 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 a6a54913daa7..7eadfe5423b2 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 61237332fc85..d2ebdfc288e7 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 4d6df7a8fd7c..ff91a9f822ed 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 252940fc1dea..e07f7109ed38 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 121977aff76d..7f1559475939 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 8de331d65ea2..6d5ceaa1332b 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 a2c9de0809aa..db7ee1961d67 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 d67a026d1586..f316862d1993 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 000000000000..f88d3c2683e9
--- /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 b676bdff28cd..bad5c65003d0 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 998634fd7929..3a7952d0e5be 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 7d34721ad0ba..1bf125e654ff 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 70508b371ca4..33475bd1c181 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
{