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")

- 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.
- Use neural networks to forecast fortified wine sales, as follows:
- Partition the data using the period until December 1993 as the training period.
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))
- Run a neural network using R’s nnetar with 11 nonseasonal lags (i.e., p = 11). Leave all other arguments at their default.
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
- 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
- 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
- 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)
- 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")

- 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
- 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