Forecasting Australian Wine Sales: You are hired to obtain short-term forecasts (2-3 months ahead) for each of the six series, and this task will be repeated every month.

library(forecast)
library(pander)
library(knitr)
wine <- read.csv("AustralianWines.csv", header = T, stringsAsFactors = F)
wine <- wine[1:180, ]
head(wine)
tail(wine)
#read in each as a time series and partition
#fortified
Fortified.ts <- ts(wine$Fortified, start = c(1980, 1), frequency = 12)
FortifiedvalidLength <- 12
FortifiedtrainLength <- length(Fortified.ts) - FortifiedvalidLength
FortifiedsalesTrain <- window(Fortified.ts, end = c(1980, FortifiedtrainLength))
FortifiedsalesValid <- window(Fortified.ts, start = c(1980, FortifiedtrainLength + 1))
#Red
Red.ts <- ts(wine$Red, start = c(1980, 1), frequency = 12)
RedvalidLength <- 12
RedtrainLength <- length(Red.ts) - RedvalidLength
RedsalesTrain <- window(Red.ts, end = c(1980, RedtrainLength))
RedsalesValid <- window(Red.ts, start = c(1980, RedtrainLength + 1))
#Rose
Rose.ts <- ts(wine$Rose, start = c(1980, 1), frequency = 12)
RosevalidLength <- 12
RosetrainLength <- length(Rose.ts) - RosevalidLength
RosesalesTrain <- window(Rose.ts, end = c(1980, RosetrainLength))
RosesalesValid <- window(Rose.ts, start = c(1980, RosetrainLength + 1))
#parkling
Sparkling.ts <- ts(wine$sparkling, start = c(1980, 1), frequency = 12)
SparklingvalidLength <- 12
SparklingtrainLength <- length(Sparkling.ts) - SparklingvalidLength
SparklingsalesTrain <- window(Sparkling.ts, end = c(1980, SparklingtrainLength))
SparklingsalesValid <- window(Sparkling.ts, start = c(1980, SparklingtrainLength + 1))
#Sweet
Sweet.white.ts <- ts(wine$Sweet.white, start = c(1980, 1), frequency = 12)
SweetvalidLength <- 12
SweettrainLength <- length(Sweet.white.ts) - SweetvalidLength
SweetsalesTrain <- window(Sweet.white.ts, end = c(1980, SweettrainLength))
SweetsalesValid <- window(Sweet.white.ts, start = c(1980, SweettrainLength + 1))
#Dry
Dry.white.ts <- ts(wine$Dry.white, start = c(1980, 1), frequency = 12)
DryvalidLength <- 12
DrytrainLength <- length(Dry.white.ts) - DryvalidLength
DrysalesTrain <- window(Dry.white.ts, end = c(1980, DrytrainLength))
DrysalesValid <- window(Dry.white.ts, start = c(1980, DrytrainLength + 1))
#plots
par(mfrow = c(2,3))
plot(Fortified.ts, xlab = "Year", ylab = "Sales (thousands)", col = "deeppink4", main = "Fortified Wine Sales")
plot(Red.ts, xlab = "Year", ylab = "Sales (thousands)", col = "deeppink4", main = "Red Wine Sales")
plot(Rose.ts, xlab = "Year", ylab = "Sales (thousands)", col = "deeppink4", main = "Rose Sales")
plot(Sparkling.ts, xlab = "Year", ylab = "Sales (thousands)", col = "deeppink4", main = "Sparkling Sales")
plot(Sweet.white.ts, xlab = "Year", ylab = "Sales (thousands)", col = "deeppink4", main = "Sweet Wine Sales")
plot(Dry.white.ts, xlab = "Year", ylab = "Sales (thousands)", col = "deeppink4", main = "Dry Wine Sales")

  1. Would you consider neural networks for this task? Explain why.

I think it would be appropriate to consider neural networks for forecasting each of the six series plotted above, even for the short-term. Each type of wine displays differently nuanced components such as seasonality, trend, and error which can be captured by neural networks. Although neural networks can be computationally challenging, if the company were looking for a one-size fits all forecasting model, this method might be appropriate because it is capable of learning the subtleties of each series. However, it is important that other forecasting methods that can capture the various components in each series be considered as well.

  1. Use neural networks to forecast fortified wine sales, as follows:
Fortified.ts <- ts(wine$Fortified, start = c(1980, 1), frequency = 12)
FortifiedvalidLength <- 12
FortifiedtrainLength <- length(Fortified.ts) - FortifiedvalidLength
FortifiedsalesTrain <- window(Fortified.ts, end = c(1980, FortifiedtrainLength))
FortifiedsalesValid <- window(Fortified.ts, start = c(1980, FortifiedtrainLength + 1))
set.seed(201)
fortified.nn <- nnetar(FortifiedsalesTrain, p = 11)
fortified.nn
Series: FortifiedsalesTrain 
Model:  NNAR(11,1,6)[12] 
Call:   nnetar(y = FortifiedsalesTrain, p = 11)

Average of 20 networks, each of which is
a 12-6-1 network with 85 weights
options were - linear output units 

sigma^2 estimated as 6772
summary(fortified.nn$model[[1]])
a 12-6-1 network with 85 weights
options were - linear output units 
  b->h1  i1->h1  i2->h1  i3->h1  i4->h1  i5->h1  i6->h1  i7->h1 
   1.34    0.66   -3.88   -2.79    3.02   -0.22   -0.63   -1.13 
 i8->h1  i9->h1 i10->h1 i11->h1 i12->h1 
   2.69    0.20   -2.05    1.52    3.10 
  b->h2  i1->h2  i2->h2  i3->h2  i4->h2  i5->h2  i6->h2  i7->h2 
   3.70    3.03   -1.79   -0.85    3.09   -7.69   -1.40    6.90 
 i8->h2  i9->h2 i10->h2 i11->h2 i12->h2 
   2.13   -2.09    3.73   -2.95   -3.37 
  b->h3  i1->h3  i2->h3  i3->h3  i4->h3  i5->h3  i6->h3  i7->h3 
   2.09   -1.77    1.45   -1.70    1.28   -2.66    1.39    0.38 
 i8->h3  i9->h3 i10->h3 i11->h3 i12->h3 
   0.53    0.93    1.33   -0.53   -0.31 
  b->h4  i1->h4  i2->h4  i3->h4  i4->h4  i5->h4  i6->h4  i7->h4 
  -3.86   -0.61   -9.73   -1.68    4.01    7.70   -5.67   -4.29 
 i8->h4  i9->h4 i10->h4 i11->h4 i12->h4 
   4.78    3.11   -5.60    1.35    0.25 
  b->h5  i1->h5  i2->h5  i3->h5  i4->h5  i5->h5  i6->h5  i7->h5 
   1.15    1.41   -1.39    1.01   -1.73   -0.41    0.69   -0.52 
 i8->h5  i9->h5 i10->h5 i11->h5 i12->h5 
  -2.16    0.32    0.40   -0.05   -1.30 
  b->h6  i1->h6  i2->h6  i3->h6  i4->h6  i5->h6  i6->h6  i7->h6 
   2.47   -0.45   -1.09   -0.89    2.13   -0.52   -0.57    1.50 
 i8->h6  i9->h6 i10->h6 i11->h6 i12->h6 
   1.72   -2.42   -1.51   -0.50    1.02 
 b->o h1->o h2->o h3->o h4->o h5->o h6->o 
 2.10  1.98  0.64 -1.66 -1.25 -1.33 -1.86 
  1. Create a time plot for the actual and forecasted series over the training period. Interpret what you see in the plots.
fortifiedForecast <- forecast(fortified.nn, h = FortifiedvalidLength)
plot(c(1980, 1993), c(1000, 6000), type = "n", xlab = "Year", ylab = "Sales", bty = "l", xaxt = "n", yaxt = "n", main = "Fortified Wine Historical Sales \nNeural Network Model Fit")
lines(FortifiedsalesTrain, col = "deeppink4", lwd = 2)
axis(1, at = seq(1980, 1994, 1))
axis(2, at = seq(1000, 6000, 500), labels = format(seq(1000, 6000, 500)))
lines(fortified.nn$fitted, lty = 1, col = "blue", lwd = 1)
legend(1980, 2000, c("Actual Sales", "NN Model MAPE = 2.29%"), lty = c(1, 1), lwd = c(2,1), col = c("deeppink4", "blue"), bty = "n")

Create also a time plot of the forecast errors for the training period.

plot(fortified.nn$residuals, col = "deeppink4", main = "Residuals from Neural Net Model", xlab = "Year", ylab = "Residuals", bty = "l")
abline(h = 0)

Interpretation

  1. Use the neural network to forecast sales for each month in the validation period (January 1994 to December 1994).
fortifiedForecast
          Jan      Feb      Mar      Apr      May      Jun      Jul
1994 1378.183 1382.149 1766.535 2060.303 2276.977 2320.602 2731.557
          Aug      Sep      Oct      Nov      Dec
1994 2599.842 2116.505 1841.124 2322.511 2689.677
  1. Compare your neural network to an exponential smoothing model used to forecast fortified wine sales.
fortified.ets <- ets(FortifiedsalesTrain, model = "ZZZ", restrict = FALSE)
fortified.ets.forecast <- forecast(fortified.ets, h = FortifiedvalidLength)
  1. Use R’s ets function to automatically select and fit an exponential smoothing model to the training period until December 1993. Which model did ets fit?
fortified.ets
ETS(M,A,M) 

Call:
 ets(y = FortifiedsalesTrain, model = "ZZZ", restrict = FALSE) 

  Smoothing parameters:
    alpha = 0.0555 
    beta  = 9e-04 
    gamma = 1e-04 

  Initial states:
    l = 4040.0811 
    b = -6.7983 
    s=1.1316 1.0399 0.8877 0.9505 1.2722 1.3862
           1.1463 1.1097 0.9345 0.8513 0.6996 0.5903

  sigma:  0.0859

     AIC     AICc      BIC 
2755.038 2759.118 2808.145 

The ets() function chooses a smoothing model with multiplicative error, additive trend, multiplicative seasonality, an alpha of .05 and a much smaller beta value.

plot(c(1980, 1993), c(1000, 6000), type = "n", xlab = "Year", ylab = "Sales", bty = "l", xaxt = "n", yaxt = "n",  main = "Fortified Wine Historical Sales \nFitted Exponential Smoothing Model")
lines(FortifiedsalesTrain, col = "deeppink4", lwd = 2)
axis(1, at = seq(1980, 1994, 1))
axis(2, at = seq(1000, 6000, 500), labels = format(seq(1000, 6000, 500)))
lines(fortified.ets$fitted, lty = 1, lwd = 1, col = "blue")
legend(1987, 5900, c("Actual Sales", "ETS Model MAPE = 7.23%"), lty = c(1, 1), lwd = c(2,1), col = c("deeppink4", "blue"), bty = "n")

plot(c(1994, 1995), c(1000, 6000), type = "n", xlab = "Month", ylab = "Sales", bty = "l", xaxt = "n", yaxt = "n", main = "1994 Fortified Wine Sales \nActual and Forecasted Values")
lines(FortifiedsalesValid, col = "deeppink4", lwd = 2)
axis(1, at=seq(1994,1995,1/11),labels=c("Jan","Feb","Mar","Apr","May","Jun","Jul", "Aug","Sep","Oct","Nov","Dec"), xaxs = "r")
axis(2, at = seq(1000, 6000, 500), labels = format(seq(1000, 6000, 500)))
lines(fortifiedForecast$mean, lty = 2, col = "blue", lwd = 2)
lines(fortified.ets.forecast$mean, lty = 3, lwd = 3)
legend(1994+1/11, 5900, c("Actual Sales", "NN Model Forecast", "Smoothing Forecast"), lty = c(2, 2, 3), lwd = c(1,2, 3), col = c("deeppink4", "blue", "black"), bty = "n")

  1. Use this exponential smoothing model to forecast sales for each month in 1994.
fortified.ets.forecast
         Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
Jan 1994       1289.829 1147.913 1431.745 1072.788 1506.871
Feb 1994       1521.475 1353.802 1689.148 1265.041 1777.909
Mar 1994       1842.645 1639.237 2046.054 1531.559 2153.732
Apr 1994       2013.011 1790.409 2235.614 1672.571 2353.452
May 1994       2379.117 2115.554 2642.679 1976.033 2782.201
Jun 1994       2445.906 2174.435 2717.376 2030.728 2861.083
Jul 1994       2943.532 2616.195 3270.870 2442.913 3444.151
Aug 1994       2688.471 2388.895 2988.047 2230.309 3146.633
Sep 1994       1998.782 1775.592 2221.971 1657.443 2340.120
Oct 1994       1857.773 1649.880 2065.666 1539.829 2175.717
Nov 1994       2165.635 1922.749 2408.521 1794.173 2537.097
Dec 1994       2344.995 2081.384 2608.606 1941.836 2748.153
  1. How does the neural network compare to the exponential smoothing model in terms of predictive performance in the training period? In the validation period?
accuracy(fortifiedForecast, FortifiedsalesValid)
                       ME      RMSE       MAE       MPE      MAPE
Training set 8.231706e-03  82.29415  64.69893 -0.235581  2.283963
Test set     1.260029e+02 296.22162 254.37924  4.489456 11.561448
                  MASE        ACF1 Theil's U
Training set 0.2325155  0.08597936        NA
Test set     0.9141900 -0.16585493   0.66165
accuracy(fortified.ets.forecast, FortifiedsalesValid)
                    ME     RMSE      MAE       MPE      MAPE      MASE        ACF1
Training set -25.32466 287.8687 224.6507 -1.317643  7.229271 0.8073515  0.05168201
Test set     125.56906 328.9246 256.3940  4.443793 10.858860 0.9214307 -0.01105575
             Theil's U
Training set        NA
Test set     0.7140459

Although the neural network fit the training set more closely than the optimal smoothing model chosen by the ETS() function, the Exponential Smoothing forecast slightly outperforms the NN forecast. This is an example where a NN overfits the data in the training set, therefore warranting caution in forecasting.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpGb3JlY2FzdGluZyBBdXN0cmFsaWFuIFdpbmUgU2FsZXM6IFlvdSBhcmUgaGlyZWQgdG8gb2J0YWluIHNob3J0LXRlcm0gZm9yZWNhc3RzICgyLTMgbW9udGhzIGFoZWFkKSBmb3IgZWFjaCBvZiB0aGUgc2l4IHNlcmllcywgYW5kIHRoaXMgdGFzayB3aWxsIGJlIHJlcGVhdGVkIGV2ZXJ5IG1vbnRoLiANCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeShwYW5kZXIpDQpsaWJyYXJ5KGtuaXRyKQ0KYGBgDQoNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp3aW5lIDwtIHJlYWQuY3N2KCJBdXN0cmFsaWFuV2luZXMuY3N2IiwgaGVhZGVyID0gVCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpDQp3aW5lIDwtIHdpbmVbMToxODAsIF0NCmhlYWQod2luZSkNCnRhaWwod2luZSkNCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNyZWFkIGluIGVhY2ggYXMgYSB0aW1lIHNlcmllcyBhbmQgcGFydGl0aW9uDQojZm9ydGlmaWVkDQpGb3J0aWZpZWQudHMgPC0gdHMod2luZSRGb3J0aWZpZWQsIHN0YXJ0ID0gYygxOTgwLCAxKSwgZnJlcXVlbmN5ID0gMTIpDQpGb3J0aWZpZWR2YWxpZExlbmd0aCA8LSAxMg0KRm9ydGlmaWVkdHJhaW5MZW5ndGggPC0gbGVuZ3RoKEZvcnRpZmllZC50cykgLSBGb3J0aWZpZWR2YWxpZExlbmd0aA0KRm9ydGlmaWVkc2FsZXNUcmFpbiA8LSB3aW5kb3coRm9ydGlmaWVkLnRzLCBlbmQgPSBjKDE5ODAsIEZvcnRpZmllZHRyYWluTGVuZ3RoKSkNCkZvcnRpZmllZHNhbGVzVmFsaWQgPC0gd2luZG93KEZvcnRpZmllZC50cywgc3RhcnQgPSBjKDE5ODAsIEZvcnRpZmllZHRyYWluTGVuZ3RoICsgMSkpDQojUmVkDQpSZWQudHMgPC0gdHMod2luZSRSZWQsIHN0YXJ0ID0gYygxOTgwLCAxKSwgZnJlcXVlbmN5ID0gMTIpDQpSZWR2YWxpZExlbmd0aCA8LSAxMg0KUmVkdHJhaW5MZW5ndGggPC0gbGVuZ3RoKFJlZC50cykgLSBSZWR2YWxpZExlbmd0aA0KUmVkc2FsZXNUcmFpbiA8LSB3aW5kb3coUmVkLnRzLCBlbmQgPSBjKDE5ODAsIFJlZHRyYWluTGVuZ3RoKSkNClJlZHNhbGVzVmFsaWQgPC0gd2luZG93KFJlZC50cywgc3RhcnQgPSBjKDE5ODAsIFJlZHRyYWluTGVuZ3RoICsgMSkpDQojUm9zZQ0KUm9zZS50cyA8LSB0cyh3aW5lJFJvc2UsIHN0YXJ0ID0gYygxOTgwLCAxKSwgZnJlcXVlbmN5ID0gMTIpDQpSb3NldmFsaWRMZW5ndGggPC0gMTINClJvc2V0cmFpbkxlbmd0aCA8LSBsZW5ndGgoUm9zZS50cykgLSBSb3NldmFsaWRMZW5ndGgNClJvc2VzYWxlc1RyYWluIDwtIHdpbmRvdyhSb3NlLnRzLCBlbmQgPSBjKDE5ODAsIFJvc2V0cmFpbkxlbmd0aCkpDQpSb3Nlc2FsZXNWYWxpZCA8LSB3aW5kb3coUm9zZS50cywgc3RhcnQgPSBjKDE5ODAsIFJvc2V0cmFpbkxlbmd0aCArIDEpKQ0KI3BhcmtsaW5nDQpTcGFya2xpbmcudHMgPC0gdHMod2luZSRzcGFya2xpbmcsIHN0YXJ0ID0gYygxOTgwLCAxKSwgZnJlcXVlbmN5ID0gMTIpDQpTcGFya2xpbmd2YWxpZExlbmd0aCA8LSAxMg0KU3BhcmtsaW5ndHJhaW5MZW5ndGggPC0gbGVuZ3RoKFNwYXJrbGluZy50cykgLSBTcGFya2xpbmd2YWxpZExlbmd0aA0KU3BhcmtsaW5nc2FsZXNUcmFpbiA8LSB3aW5kb3coU3BhcmtsaW5nLnRzLCBlbmQgPSBjKDE5ODAsIFNwYXJrbGluZ3RyYWluTGVuZ3RoKSkNClNwYXJrbGluZ3NhbGVzVmFsaWQgPC0gd2luZG93KFNwYXJrbGluZy50cywgc3RhcnQgPSBjKDE5ODAsIFNwYXJrbGluZ3RyYWluTGVuZ3RoICsgMSkpDQojU3dlZXQNClN3ZWV0LndoaXRlLnRzIDwtIHRzKHdpbmUkU3dlZXQud2hpdGUsIHN0YXJ0ID0gYygxOTgwLCAxKSwgZnJlcXVlbmN5ID0gMTIpDQpTd2VldHZhbGlkTGVuZ3RoIDwtIDEyDQpTd2VldHRyYWluTGVuZ3RoIDwtIGxlbmd0aChTd2VldC53aGl0ZS50cykgLSBTd2VldHZhbGlkTGVuZ3RoDQpTd2VldHNhbGVzVHJhaW4gPC0gd2luZG93KFN3ZWV0LndoaXRlLnRzLCBlbmQgPSBjKDE5ODAsIFN3ZWV0dHJhaW5MZW5ndGgpKQ0KU3dlZXRzYWxlc1ZhbGlkIDwtIHdpbmRvdyhTd2VldC53aGl0ZS50cywgc3RhcnQgPSBjKDE5ODAsIFN3ZWV0dHJhaW5MZW5ndGggKyAxKSkNCiNEcnkNCkRyeS53aGl0ZS50cyA8LSB0cyh3aW5lJERyeS53aGl0ZSwgc3RhcnQgPSBjKDE5ODAsIDEpLCBmcmVxdWVuY3kgPSAxMikNCkRyeXZhbGlkTGVuZ3RoIDwtIDEyDQpEcnl0cmFpbkxlbmd0aCA8LSBsZW5ndGgoRHJ5LndoaXRlLnRzKSAtIERyeXZhbGlkTGVuZ3RoDQpEcnlzYWxlc1RyYWluIDwtIHdpbmRvdyhEcnkud2hpdGUudHMsIGVuZCA9IGMoMTk4MCwgRHJ5dHJhaW5MZW5ndGgpKQ0KRHJ5c2FsZXNWYWxpZCA8LSB3aW5kb3coRHJ5LndoaXRlLnRzLCBzdGFydCA9IGMoMTk4MCwgRHJ5dHJhaW5MZW5ndGggKyAxKSkNCg0KI3Bsb3RzDQpwYXIobWZyb3cgPSBjKDIsMykpDQpwbG90KEZvcnRpZmllZC50cywgeGxhYiA9ICJZZWFyIiwgeWxhYiA9ICJTYWxlcyAodGhvdXNhbmRzKSIsIGNvbCA9ICJkZWVwcGluazQiLCBtYWluID0gIkZvcnRpZmllZCBXaW5lIFNhbGVzIikNCnBsb3QoUmVkLnRzLCB4bGFiID0gIlllYXIiLCB5bGFiID0gIlNhbGVzICh0aG91c2FuZHMpIiwgY29sID0gImRlZXBwaW5rNCIsIG1haW4gPSAiUmVkIFdpbmUgU2FsZXMiKQ0KcGxvdChSb3NlLnRzLCB4bGFiID0gIlllYXIiLCB5bGFiID0gIlNhbGVzICh0aG91c2FuZHMpIiwgY29sID0gImRlZXBwaW5rNCIsIG1haW4gPSAiUm9zZSBTYWxlcyIpDQpwbG90KFNwYXJrbGluZy50cywgeGxhYiA9ICJZZWFyIiwgeWxhYiA9ICJTYWxlcyAodGhvdXNhbmRzKSIsIGNvbCA9ICJkZWVwcGluazQiLCBtYWluID0gIlNwYXJrbGluZyBTYWxlcyIpDQpwbG90KFN3ZWV0LndoaXRlLnRzLCB4bGFiID0gIlllYXIiLCB5bGFiID0gIlNhbGVzICh0aG91c2FuZHMpIiwgY29sID0gImRlZXBwaW5rNCIsIG1haW4gPSAiU3dlZXQgV2luZSBTYWxlcyIpDQpwbG90KERyeS53aGl0ZS50cywgeGxhYiA9ICJZZWFyIiwgeWxhYiA9ICJTYWxlcyAodGhvdXNhbmRzKSIsIGNvbCA9ICJkZWVwcGluazQiLCBtYWluID0gIkRyeSBXaW5lIFNhbGVzIikNCg0KDQpgYGANCg0KDQoxLiBXb3VsZCB5b3UgY29uc2lkZXIgbmV1cmFsIG5ldHdvcmtzIGZvciB0aGlzIHRhc2s/IEV4cGxhaW4gd2h5LiANCg0KSSB0aGluayBpdCB3b3VsZCBiZSBhcHByb3ByaWF0ZSB0byBjb25zaWRlciBuZXVyYWwgbmV0d29ya3MgZm9yIGZvcmVjYXN0aW5nIGVhY2ggb2YgdGhlIHNpeCBzZXJpZXMgcGxvdHRlZCBhYm92ZSwgZXZlbiBmb3IgdGhlIHNob3J0LXRlcm0uIEVhY2ggdHlwZSBvZiB3aW5lIGRpc3BsYXlzIGRpZmZlcmVudGx5IG51YW5jZWQgY29tcG9uZW50cyBzdWNoIGFzIHNlYXNvbmFsaXR5LCB0cmVuZCwgYW5kIGVycm9yIHdoaWNoIGNhbiBiZSBjYXB0dXJlZCBieSBuZXVyYWwgbmV0d29ya3MuIEFsdGhvdWdoIG5ldXJhbCBuZXR3b3JrcyBjYW4gYmUgY29tcHV0YXRpb25hbGx5IGNoYWxsZW5naW5nLCBpZiB0aGUgY29tcGFueSB3ZXJlIGxvb2tpbmcgZm9yIGEgb25lLXNpemUgZml0cyBhbGwgZm9yZWNhc3RpbmcgbW9kZWwsIHRoaXMgbWV0aG9kIG1pZ2h0IGJlIGFwcHJvcHJpYXRlIGJlY2F1c2UgaXQgaXMgY2FwYWJsZSBvZiBsZWFybmluZyB0aGUgc3VidGxldGllcyBvZiBlYWNoIHNlcmllcy4gSG93ZXZlciwgaXQgaXMgaW1wb3J0YW50IHRoYXQgb3RoZXIgZm9yZWNhc3RpbmcgbWV0aG9kcyB0aGF0IGNhbiBjYXB0dXJlIHRoZSB2YXJpb3VzIGNvbXBvbmVudHMgaW4gZWFjaCBzZXJpZXMgYmUgY29uc2lkZXJlZCBhcyB3ZWxsLiANCg0KMi4gVXNlIG5ldXJhbCBuZXR3b3JrcyB0byBmb3JlY2FzdCBmb3J0aWZpZWQgd2luZSBzYWxlcywgYXMgZm9sbG93czogDQoNCiogUGFydGl0aW9uIHRoZSBkYXRhIHVzaW5nIHRoZSBwZXJpb2QgdW50aWwgRGVjZW1iZXIgMTk5MyBhcyB0aGUgdHJhaW5pbmcgcGVyaW9kLg0KDQpgYGB7cn0NCkZvcnRpZmllZC50cyA8LSB0cyh3aW5lJEZvcnRpZmllZCwgc3RhcnQgPSBjKDE5ODAsIDEpLCBmcmVxdWVuY3kgPSAxMikNCkZvcnRpZmllZHZhbGlkTGVuZ3RoIDwtIDEyDQpGb3J0aWZpZWR0cmFpbkxlbmd0aCA8LSBsZW5ndGgoRm9ydGlmaWVkLnRzKSAtIEZvcnRpZmllZHZhbGlkTGVuZ3RoDQpGb3J0aWZpZWRzYWxlc1RyYWluIDwtIHdpbmRvdyhGb3J0aWZpZWQudHMsIGVuZCA9IGMoMTk4MCwgRm9ydGlmaWVkdHJhaW5MZW5ndGgpKQ0KRm9ydGlmaWVkc2FsZXNWYWxpZCA8LSB3aW5kb3coRm9ydGlmaWVkLnRzLCBzdGFydCA9IGMoMTk4MCwgRm9ydGlmaWVkdHJhaW5MZW5ndGggKyAxKSkNCmBgYA0KDQoNCiogUnVuIGEgbmV1cmFsIG5ldHdvcmsgdXNpbmcgUuKAmXMgbm5ldGFyIHdpdGggMTEgbm9uc2Vhc29uYWwgbGFncyAoaS5lLiwgcCA9IDExKS4gTGVhdmUgYWxsIG90aGVyIGFyZ3VtZW50cyBhdCB0aGVpciBkZWZhdWx0LiANCg0KYGBge3J9DQpzZXQuc2VlZCgyMDEpDQpmb3J0aWZpZWQubm4gPC0gbm5ldGFyKEZvcnRpZmllZHNhbGVzVHJhaW4sIHAgPSAxMSkNCmZvcnRpZmllZC5ubg0Kc3VtbWFyeShmb3J0aWZpZWQubm4kbW9kZWxbWzFdXSkNCmBgYA0KDQoNCiAgKGEpIENyZWF0ZSBhIHRpbWUgcGxvdCBmb3IgdGhlIGFjdHVhbCBhbmQgZm9yZWNhc3RlZCBzZXJpZXMgb3ZlciB0aGUgdHJhaW5pbmcgcGVyaW9kLiAgSW50ZXJwcmV0IHdoYXQgeW91IHNlZSBpbiB0aGUgcGxvdHMuIA0KDQpgYGB7ciwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9N30NCmZvcnRpZmllZEZvcmVjYXN0IDwtIGZvcmVjYXN0KGZvcnRpZmllZC5ubiwgaCA9IEZvcnRpZmllZHZhbGlkTGVuZ3RoKQ0KcGxvdChjKDE5ODAsIDE5OTMpLCBjKDEwMDAsIDYwMDApLCB0eXBlID0gIm4iLCB4bGFiID0gIlllYXIiLCB5bGFiID0gIlNhbGVzIiwgYnR5ID0gImwiLCB4YXh0ID0gIm4iLCB5YXh0ID0gIm4iLCBtYWluID0gIkZvcnRpZmllZCBXaW5lIEhpc3RvcmljYWwgU2FsZXMgXG5OZXVyYWwgTmV0d29yayBNb2RlbCBGaXQiKQ0KbGluZXMoRm9ydGlmaWVkc2FsZXNUcmFpbiwgY29sID0gImRlZXBwaW5rNCIsIGx3ZCA9IDIpDQpheGlzKDEsIGF0ID0gc2VxKDE5ODAsIDE5OTQsIDEpKQ0KYXhpcygyLCBhdCA9IHNlcSgxMDAwLCA2MDAwLCA1MDApLCBsYWJlbHMgPSBmb3JtYXQoc2VxKDEwMDAsIDYwMDAsIDUwMCkpKQ0KbGluZXMoZm9ydGlmaWVkLm5uJGZpdHRlZCwgbHR5ID0gMSwgY29sID0gImJsdWUiLCBsd2QgPSAxKQ0KbGVnZW5kKDE5ODAsIDIwMDAsIGMoIkFjdHVhbCBTYWxlcyIsICJOTiBNb2RlbCBNQVBFID0gMi4yOSUiKSwgbHR5ID0gYygxLCAxKSwgbHdkID0gYygyLDEpLCBjb2wgPSBjKCJkZWVwcGluazQiLCAiYmx1ZSIpLCBidHkgPSAibiIpDQpgYGANCg0KICANCkNyZWF0ZSBhbHNvIGEgdGltZSBwbG90IG9mIHRoZSBmb3JlY2FzdCBlcnJvcnMgZm9yIHRoZSB0cmFpbmluZyBwZXJpb2QuICANCiAgDQpgYGB7ciwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9N30NCnBsb3QoZm9ydGlmaWVkLm5uJHJlc2lkdWFscywgY29sID0gImRlZXBwaW5rNCIsIG1haW4gPSAiUmVzaWR1YWxzIGZyb20gTmV1cmFsIE5ldCBNb2RlbCIsIHhsYWIgPSAiWWVhciIsIHlsYWIgPSAiUmVzaWR1YWxzIiwgYnR5ID0gImwiKQ0KYWJsaW5lKGggPSAwKQ0KYGBgDQoqKkludGVycHJldGF0aW9uKioNCg0KICAoYikgVXNlIHRoZSBuZXVyYWwgbmV0d29yayB0byBmb3JlY2FzdCBzYWxlcyBmb3IgZWFjaCBtb250aCBpbiB0aGUgdmFsaWRhdGlvbiBwZXJpb2QgKEphbnVhcnkgMTk5NCB0byBEZWNlbWJlciAxOTk0KS4gDQogIA0KYGBge3J9DQpmb3J0aWZpZWRGb3JlY2FzdA0KYGBgICANCiAgDQozLiBDb21wYXJlIHlvdXIgbmV1cmFsIG5ldHdvcmsgdG8gYW4gZXhwb25lbnRpYWwgc21vb3RoaW5nIG1vZGVsIHVzZWQgdG8gZm9yZWNhc3QgZm9ydGlmaWVkIHdpbmUgc2FsZXMuIA0KDQpgYGB7cn0NCmZvcnRpZmllZC5ldHMgPC0gZXRzKEZvcnRpZmllZHNhbGVzVHJhaW4sIG1vZGVsID0gIlpaWiIsIHJlc3RyaWN0ID0gRkFMU0UpDQpmb3J0aWZpZWQuZXRzLmZvcmVjYXN0IDwtIGZvcmVjYXN0KGZvcnRpZmllZC5ldHMsIGggPSBGb3J0aWZpZWR2YWxpZExlbmd0aCkNCmBgYA0KDQogIChhKSBVc2UgUuKAmXMgZXRzIGZ1bmN0aW9uIHRvIGF1dG9tYXRpY2FsbHkgc2VsZWN0IGFuZCBmaXQgYW4gZXhwb25lbnRpYWwgc21vb3RoaW5nIG1vZGVsIHRvIHRoZSB0cmFpbmluZyBwZXJpb2QgdW50aWwgRGVjZW1iZXIgMTk5My4gV2hpY2ggbW9kZWwgZGlkIGV0cyBmaXQ/DQogIA0KYGBge3J9DQpmb3J0aWZpZWQuZXRzDQpgYGAgIA0KICANCg0KVGhlIGV0cygpIGZ1bmN0aW9uIGNob29zZXMgYSBzbW9vdGhpbmcgbW9kZWwgd2l0aCBtdWx0aXBsaWNhdGl2ZSBlcnJvciwgYWRkaXRpdmUgdHJlbmQsIG11bHRpcGxpY2F0aXZlIHNlYXNvbmFsaXR5LCBhbiBhbHBoYSBvZiAuMDUgYW5kIGEgbXVjaCBzbWFsbGVyIGJldGEgdmFsdWUuIA0KDQpgYGB7ciwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9N30NCnBsb3QoYygxOTgwLCAxOTkzKSwgYygxMDAwLCA2MDAwKSwgdHlwZSA9ICJuIiwgeGxhYiA9ICJZZWFyIiwgeWxhYiA9ICJTYWxlcyIsIGJ0eSA9ICJsIiwgeGF4dCA9ICJuIiwgeWF4dCA9ICJuIiwgIG1haW4gPSAiRm9ydGlmaWVkIFdpbmUgSGlzdG9yaWNhbCBTYWxlcyBcbkZpdHRlZCBFeHBvbmVudGlhbCBTbW9vdGhpbmcgTW9kZWwiKQ0KbGluZXMoRm9ydGlmaWVkc2FsZXNUcmFpbiwgY29sID0gImRlZXBwaW5rNCIsIGx3ZCA9IDIpDQpheGlzKDEsIGF0ID0gc2VxKDE5ODAsIDE5OTQsIDEpKQ0KYXhpcygyLCBhdCA9IHNlcSgxMDAwLCA2MDAwLCA1MDApLCBsYWJlbHMgPSBmb3JtYXQoc2VxKDEwMDAsIDYwMDAsIDUwMCkpKQ0KbGluZXMoZm9ydGlmaWVkLmV0cyRmaXR0ZWQsIGx0eSA9IDEsIGx3ZCA9IDEsIGNvbCA9ICJibHVlIikNCmxlZ2VuZCgxOTg3LCA1OTAwLCBjKCJBY3R1YWwgU2FsZXMiLCAiRVRTIE1vZGVsIE1BUEUgPSA3LjIzJSIpLCBsdHkgPSBjKDEsIDEpLCBsd2QgPSBjKDIsMSksIGNvbCA9IGMoImRlZXBwaW5rNCIsICJibHVlIiksIGJ0eSA9ICJuIikNCmBgYA0KDQogIA0KYGBge3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTd9DQpwbG90KGMoMTk5NCwgMTk5NSksIGMoMTAwMCwgNjAwMCksIHR5cGUgPSAibiIsIHhsYWIgPSAiTW9udGgiLCB5bGFiID0gIlNhbGVzIiwgYnR5ID0gImwiLCB4YXh0ID0gIm4iLCB5YXh0ID0gIm4iLCBtYWluID0gIjE5OTQgRm9ydGlmaWVkIFdpbmUgU2FsZXMgXG5BY3R1YWwgYW5kIEZvcmVjYXN0ZWQgVmFsdWVzIikNCmxpbmVzKEZvcnRpZmllZHNhbGVzVmFsaWQsIGNvbCA9ICJkZWVwcGluazQiLCBsd2QgPSAyKQ0KYXhpcygxLCBhdD1zZXEoMTk5NCwxOTk1LDEvMTEpLGxhYmVscz1jKCJKYW4iLCJGZWIiLCJNYXIiLCJBcHIiLCJNYXkiLCJKdW4iLCJKdWwiLCAiQXVnIiwiU2VwIiwiT2N0IiwiTm92IiwiRGVjIiksIHhheHMgPSAiciIpDQpheGlzKDIsIGF0ID0gc2VxKDEwMDAsIDYwMDAsIDUwMCksIGxhYmVscyA9IGZvcm1hdChzZXEoMTAwMCwgNjAwMCwgNTAwKSkpDQpsaW5lcyhmb3J0aWZpZWRGb3JlY2FzdCRtZWFuLCBsdHkgPSAyLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpDQpsaW5lcyhmb3J0aWZpZWQuZXRzLmZvcmVjYXN0JG1lYW4sIGx0eSA9IDMsIGx3ZCA9IDMpDQpsZWdlbmQoMTk5NCsxLzExLCA1OTAwLCBjKCJBY3R1YWwgU2FsZXMiLCAiTk4gTW9kZWwgRm9yZWNhc3QiLCAiU21vb3RoaW5nIEZvcmVjYXN0IiksIGx0eSA9IGMoMiwgMiwgMyksIGx3ZCA9IGMoMSwyLCAzKSwgY29sID0gYygiZGVlcHBpbms0IiwgImJsdWUiLCAiYmxhY2siKSwgYnR5ID0gIm4iKQ0KYGBgDQoNCg0KICANCiAgDQogIChiKSBVc2UgdGhpcyBleHBvbmVudGlhbCBzbW9vdGhpbmcgbW9kZWwgdG8gZm9yZWNhc3Qgc2FsZXMgZm9yIGVhY2ggbW9udGggaW4gMTk5NC4gDQogIA0KYGBge3J9DQpmb3J0aWZpZWQuZXRzLmZvcmVjYXN0DQpgYGANCiAgDQogIChjKSBIb3cgZG9lcyB0aGUgbmV1cmFsIG5ldHdvcmsgY29tcGFyZSB0byB0aGUgZXhwb25lbnRpYWwgc21vb3RoaW5nIG1vZGVsIGluIHRlcm1zIG9mIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UgaW4gdGhlIHRyYWluaW5nIHBlcmlvZD8gSW4gdGhlIHZhbGlkYXRpb24gcGVyaW9kPw0KICANCiAgKiAqKk5ldXJhbCBOZXR3b3JrKioNCiAgDQpgYGB7cn0NCmFjY3VyYWN5KGZvcnRpZmllZEZvcmVjYXN0LCBGb3J0aWZpZWRzYWxlc1ZhbGlkKQ0KYGBgDQoNCiAgKiAqKkVUUyBNb2RlbCoqDQogIA0KYGBge3J9DQphY2N1cmFjeShmb3J0aWZpZWQuZXRzLmZvcmVjYXN0LCBGb3J0aWZpZWRzYWxlc1ZhbGlkKQ0KYGBgDQoNCkFsdGhvdWdoIHRoZSBuZXVyYWwgbmV0d29yayBmaXQgdGhlIHRyYWluaW5nIHNldCBtb3JlIGNsb3NlbHkgdGhhbiB0aGUgb3B0aW1hbCBzbW9vdGhpbmcgbW9kZWwgY2hvc2VuIGJ5IHRoZSBFVFMoKSBmdW5jdGlvbiwgdGhlIEV4cG9uZW50aWFsIFNtb290aGluZyBmb3JlY2FzdCBzbGlnaHRseSBvdXRwZXJmb3JtcyB0aGUgTk4gZm9yZWNhc3QuIFRoaXMgaXMgYW4gZXhhbXBsZSB3aGVyZSBhIE5OIG92ZXJmaXRzIHRoZSBkYXRhIGluIHRoZSB0cmFpbmluZyBzZXQsIHRoZXJlZm9yZSB3YXJyYW50aW5nIGNhdXRpb24gaW4gZm9yZWNhc3RpbmcuIA0K