diff options
author | Winfried Donkers <winfrieddonkers@libreoffice.org> | 2016-01-30 10:14:05 +0100 |
---|---|---|
committer | Eike Rathke <erack@redhat.com> | 2016-03-03 16:28:59 +0000 |
commit | f336f63da900d76c2bf6e5690f1c8a7bd15a0aa2 (patch) | |
tree | 9b989555d5b9dc2c8ea01a8a3598b1c222863af2 | |
parent | cc75888c9e4cd09476287a8489c99fbf073feddb (diff) |
tdf#94635 Add FORECAST.ETS functions to Calc
Change-Id: Ifbfff1c27fb3960a06f467630da0fa39665f0ce4
Reviewed-on: https://gerrit.libreoffice.org/20073
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Eike Rathke <erack@redhat.com>
-rw-r--r-- | formula/source/core/resource/core_resource.src | 56 | ||||
-rw-r--r-- | include/formula/compiler.hrc | 10 | ||||
-rw-r--r-- | include/formula/opcode.hxx | 8 | ||||
-rw-r--r-- | sc/Library_sc.mk | 1 | ||||
-rw-r--r-- | sc/inc/helpids.h | 8 | ||||
-rw-r--r-- | sc/qa/unit/ucalc.cxx | 8 | ||||
-rw-r--r-- | sc/source/core/inc/interpre.hxx | 14 | ||||
-rw-r--r-- | sc/source/core/tool/interpr3.cxx | 18 | ||||
-rw-r--r-- | sc/source/core/tool/interpr4.cxx | 10 | ||||
-rw-r--r-- | sc/source/core/tool/interpr8.cxx | 1386 | ||||
-rw-r--r-- | sc/source/core/tool/parclass.cxx | 7 | ||||
-rw-r--r-- | sc/source/filter/excel/xlformula.cxx | 21 | ||||
-rw-r--r-- | sc/source/filter/oox/formulabase.cxx | 23 | ||||
-rw-r--r-- | sc/source/ui/src/scfuncs.src | 488 |
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 { |