1 Introduction

1.1 Description

In this dataset we have weather observations from Houston, Texas from 1947-2014. Some variables included in this dataset include mean temperature, total precipitation, and the highest temperature. In this specific time series analysis we will look at the most recent 150 observations which comes from around 2002-2014. We will be looking at only one variable (meantemp) to see the trends around this given time.

1.2 Question

The objective of this analysis is to use time series modeling to draw conclusions on the mean temperature for the next year of this data set

1.3 Data Cleaning

weather <- read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/houston_weather.csv")

weather_subset <- tail(weather, 150)
train.weath = weather_subset$meanTemp[1:138]
test.weath = weather_subset$meanTemp[139:150]

weath = ts(train.weath, start=c(2000, 1), frequency=12)

fit1 = ses(weath, h=12)
fit2 = holt(weath, initial="optimal", h=12)
fit3 = holt(weath, damped=TRUE, h=12)
fit4 = holt(weath, exponential=TRUE, damped=TRUE, h=12)
fit5 = hw(weath, h=12, seasonal="additive")
fit6 = hw(weath, h=12, seasonal="multiplicative")
fit7 = hw(weath, h=12, seasonal="additive", damped=TRUE)
fit8 = hw(weath, h=12, seasonal="multiplicative", damped=TRUE)

We focus on the last 150 observations of the data set to reduce the chance of overfitting and to get the most recent observations of this dataset. We then split our data into a test and a training set. 12 observations in the test set and the remaining 138 in the training set. The training data is then converted into a time series object with monthly frequency of 12 because we are looking at the years. We then fit 8 different models to see which best fits our data.

2 Analysis

2.1 Accuracy Measures for Training Data

We get many accuracy values to see which model is best for our data.

accuracy.table = round(rbind(accuracy(fit1), accuracy(fit2), accuracy(fit3), accuracy(fit4),
                             accuracy(fit5), accuracy(fit6), accuracy(fit7), accuracy(fit8)),4)
row.names(accuracy.table)=c("SES","Holt Linear","Holt Add. Damped", "Holt Exp. Damped",
                            "HW Add.","HW Exp.","HW Add. Damp", "HW Exp. Damp")
kable(accuracy.table, caption = "The accuracy measures of various exponential smoothing models 
      based on the training data")
The accuracy measures of various exponential smoothing models based on the training data
ME RMSE MAE MPE MAPE MASE ACF1
SES -0.2210 6.3659 5.3461 -0.7799 7.8880 2.0777 0.6116
Holt Linear -0.0587 5.3786 4.2739 0.5114 6.3078 1.6610 0.0460
Holt Add. Damped -0.0907 5.0365 4.0451 0.2195 5.9348 1.5721 0.0545
Holt Exp. Damped -0.5602 5.1376 4.1367 -0.4293 6.0188 1.6077 0.0352
HW Add. -0.0498 2.2572 1.8184 -0.2023 2.7012 0.7067 0.1684
HW Exp. -0.1411 2.3253 1.7499 -0.3550 2.6720 0.6801 0.3866
HW Add. Damp -0.0747 2.2400 1.8105 -0.2256 2.6920 0.7037 0.1563
HW Exp. Damp -0.0829 2.3408 1.8635 -0.2395 2.7574 0.7242 0.1470

We see from our output above that HW Add. is the best fit for our data. We see that HW Add. has one of the lowest values for ME, RSME, MAE, MPE, MAPE, and MASE. For ACF1 we see that it is in the middle compared to the other values but it is still a relatively low value meaning that residual autocrrelation is not a concern.

2.2 Visualization

We are visualizing the original time series data and forecast results from different exponential smoothing models. We create two models one for non-seasonal smoothing models and another for seasonal smoothing models.

par(mfrow=c(2,1), mar=c(3,4,3,1))
###### plot the original data
pred.id = 139:150
plot(1:138, train.weath, lwd=2,type="o", ylab="MeanTemp", xlab="", 
     xlim=c(1,160), ylim=c(30, 90), cex=0.3,
     main="Non-seasonal Smoothing Models")
lines(pred.id, fit1$mean, col="red")
lines(pred.id, fit2$mean, col="blue")
lines(pred.id, fit3$mean, col="purple")
lines(pred.id, fit4$mean, col="navy")
##
points(pred.id, fit1$mean, pch=16, col="red", cex = 0.5)
points(pred.id, fit2$mean, pch=17, col="blue", cex = 0.5)
points(pred.id, fit3$mean, pch=19, col="purple", cex = 0.5)
points(pred.id, fit4$mean, pch=21, col="navy", cex = 0.5)
#points(fit0, col="black", pch=1)
legend("bottomright", lty=1, col=c("red","blue","purple", "navy"),pch=c(16,17,19,21),
   c("SES","Holt Linear","Holt Linear Damped", "Holt Multiplicative Damped"), 
   cex = 0.7, bty="n")

plot(1:138, train.weath, lwd=2,type="o", ylab="Mean Temp", xlab="", 
     xlim=c(1,160), ylim=c(30, 90), cex=0.3,
     main="Holt-Winterd Trend and Seasonal Smoothing Models")
lines(pred.id, fit5$mean, col="red")
lines(pred.id, fit6$mean, col="blue")
lines(pred.id, fit7$mean, col="purple")
lines(pred.id, fit8$mean, col="navy")
##
points(pred.id, fit5$mean, pch=16, col="red", cex = 0.5)
points(pred.id, fit6$mean, pch=17, col="blue", cex = 0.5)
points(pred.id, fit7$mean, pch=19, col="purple", cex = 0.5)
points(pred.id, fit8$mean, pch=21, col="navy", cex = 0.5)
###
legend("bottomright", lty=1, col=c("red","blue","purple", "navy"),pch=c(16,17,19,21),
   c("HW Additive","HW Multiplicative","HW Additive Damped", "HW Multiplicative Damped"), 
   cex = 0.7, bty="n")
Case study: Comparing various exponential smoothing models.

Case study: Comparing various exponential smoothing models.

We see from the above accuracy table that HW’s linear trend with an additive seasonal model is the best fit our of eight smoothing models. This is also matches with our previous conclusion that HW Add is the best model for our data.

2.3 Accuracy Measures for Test Data

We just visualized our training and test data and they showed us the same result in that HW Add is the appropriate model for these two data sets. To make a real life forecast of our data we need to use the whole dataset with all 150 observations. We refit our data to update the smoothing parameters in our final model.

acc.fun = function(test.data, mod.obj){
  PE=100*(test.data-mod.obj$mean)/mod.obj$mean
  MAPE = mean(abs(PE))
  ###
  E=test.data-mod.obj$mean
  MSE=mean(E^2)
  ###
  accuracy.metric=c(MSE=MSE, MAPE=MAPE)
  accuracy.metric
}
pred.accuracy = rbind(SES =acc.fun(test.data=test.weath, mod.obj=fit1),
                      Holt.Add =acc.fun(test.data=test.weath, mod.obj=fit2),
                      Holt.Add.Damp =acc.fun(test.data=test.weath, mod.obj=fit3),
                      Holt.Exp =acc.fun(test.data=test.weath, mod.obj=fit4),
                      HW.Add =acc.fun(test.data=test.weath, mod.obj=fit5),
                      HW.Exp =acc.fun(test.data=test.weath, mod.obj=fit6),
                      HW.Add.Damp =acc.fun(test.data=test.weath, mod.obj=fit7),
                      HW.Exp.Damp =acc.fun(test.data=test.weath, mod.obj=fit8))
kable(pred.accuracy, caption="The accuracy measures of various exponential smoothing models 
      based on the testing data")
The accuracy measures of various exponential smoothing models based on the testing data
MSE MAPE
SES 420.027941 32.430511
Holt.Add 5921.842465 607.491932
Holt.Add.Damp 1759.165800 137.916182
Holt.Exp 1768.349891 139.113295
HW.Add 5.111103 2.972308
HW.Exp 8.833668 3.593340
HW.Add.Damp 4.973422 2.981870
HW.Exp.Damp 7.161286 3.597887

We see from the above output that HW Add is again the best model which is consistent to our conclusions made previously in the training and test data. HW Add is the best model because MSE and MAPE are both the lowest values out of the models with MSE = 5.111 and MAPE = 2.972.

2.4 Final Model

Here we use the whole data set and refit it using smoothing parameters.

weather <- read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/houston_weather.csv")
weath=ts(weather$meanTemp[1:150], start=2000, frequency = 12)
final.model = hw(weath,h=12, seasonal="additive") 
smoothing.parameter = final.model$model$par[1:3]
kable(smoothing.parameter, caption="Estimated values of the smoothing parameters in
      Holt-Winters linear trend with additive seasonality")
Estimated values of the smoothing parameters in Holt-Winters linear trend with additive seasonality
x
alpha 0.1305680
beta 0.0001000
gamma 0.0001006

We see from the output above that a value of 0.131 for our alpha indicates that the model places moderate weight on recent observations when updating the level. For Beta, .00001, this value close to zero doesn’t give importance to updating the trend based on recent observations. For Gamma, .0001, with a value relatively close to zero it shows that seasonality patterns are stable and will likely not change.

3 Conlcusion

For this Houston Weather data set from 2002-2014 representing mean temperature the best model we can use is HW’s linear trend using with an additive seasonal model. We determined this by splitting our data into two sets, a training and test data set and from these two sets we gathered accuracy measures and plots and they all came to the same conclusion, that HW ADD is the best model. We then used the additive model for our full dataset and got alpha, beta, and gamma values. Our alpha value of .13 shows that the model moderately adjusts the level based on recent data. The Beta value of nearly zero shows us there is little to no trend detected in the series. This means that the mean temperature does not have a significant long-term upward or downward trend which we would expect of temperature. Our Gamma value of nearly zero demonstrates that the seasonality trend does not change over this interval.

LS0tDQp0aXRsZTogIlRpbWUgU2VyaWVzIFdlYXRoZXIgUHJvamVjdCINCmF1dGhvcjogJ1R5bGVyIEJhdHRhZ2xpbmknDQpkYXRlOiAiMjAyNC0xMi04Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIHRvY19mbG9hdDogeWVzDQogICAgZmlnX3dpZHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogIHdvcmRfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGtlZXBfbWQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQphbHdheXNfYWxsb3dfaHRtbDogdHJ1ZQ0KLS0tDQoNCmBgYHs9aHRtbH0NCg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KLyogQ2FzY2FkaW5nIFN0eWxlIFNoZWV0cyAoQ1NTKSBpcyBhIHN0eWxlc2hlZXQgbGFuZ3VhZ2UgdXNlZCB0byBkZXNjcmliZSB0aGUgcHJlc2VudGF0aW9uIG9mIGEgZG9jdW1lbnQgd3JpdHRlbiBpbiBIVE1MIG9yIFhNTC4gaXQgaXMgYSBzaW1wbGUgbWVjaGFuaXNtIGZvciBhZGRpbmcgc3R5bGUgKGUuZy4sIGZvbnRzLCBjb2xvcnMsIHNwYWNpbmcpIHRvIFdlYiBkb2N1bWVudHMuICovDQoNCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovDQogIGZvbnQtc2l6ZTogMjRweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8NCiAgZm9udC1zaXplOiAyMHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmRhdGUgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIHRoZSBkYXRlICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAyMnB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8NCiAgICBmb250LXNpemU6IDIwcHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCA0IHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCjwvc3R5bGU+DQpgYGANCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIERldGVjdCwgaW5zdGFsbCBhbmQgbG9hZCBwYWNrYWdlcyBpZiBuZWVkZWQuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoIk1BU1MiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpDQogICBsaWJyYXJ5KE1BU1MpDQp9DQppZiAoIXJlcXVpcmUoIm5sZXFzbHYiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygibmxlcXNsdiIpDQogICBsaWJyYXJ5KG5sZXFzbHYpDQp9DQojDQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQ0KICAgbGlicmFyeShwYW5kZXIpDQp9DQoNCmlmICghcmVxdWlyZSgicHN5Y2giKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJwc3ljaCIpDQogICBsaWJyYXJ5KHBzeWNoKQ0KfQ0KaWYgKCFyZXF1aXJlKCJNQVNTIikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpDQogICBsaWJyYXJ5KE1BU1MpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCiAgIGxpYnJhcnkoZ2dwbG90MikNCn0NCmlmICghcmVxdWlyZSgiR0dhbGx5IikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygiR0dhbGx5IikNCiAgIGxpYnJhcnkoR0dhbGx5KQ0KfQ0KaWYgKCFyZXF1aXJlKCJjYXIiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJjYXIiKQ0KICAgbGlicmFyeShjYXIpDQp9DQppZiAoIXJlcXVpcmUoImRwbHlyIikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KICAgbGlicmFyeShkcGx5cikNCn0NCmlmICghcmVxdWlyZSgiY2FyZXQiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQogICBsaWJyYXJ5KGNhcmV0KQ0KfQ0KaWYgKCFyZXF1aXJlKCJyZWFkeGwiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKQ0KICAgbGlicmFyeShyZWFkeGwpDQp9DQppZiAoIXJlcXVpcmUoIm9wZW54bHN4IikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygib3Blbnhsc3giKQ0KICAgbGlicmFyeShvcGVueGxzeCkNCn0NCmlmICghcmVxdWlyZSgiZm9yZWNhc3QiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJmb3JlY2FzdCIpDQogICBsaWJyYXJ5KGZvcmVjYXN0KQ0KfQ0KIyBzcGVjaWZpY2F0aW9ucyBvZiBvdXRwdXRzIG9mIGNvZGUgaW4gY29kZSBjaHVua3MNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAjIGluY2x1ZGUgY29kZSBjaHVuayBpbiB0aGUgb3V0cHV0IGZpbGUNCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5ncyA9IEZBTFNFLCAgIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgeW91IGNhbiBjaG9vc2UgdG8gaW5jbHVkZSB0aGUgd2FybmluZyBtZXNzYWdlcyBpbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRoZSBvdXRwdXQgZmlsZS4gDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZXMgPSBGQUxTRSwgICMNCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEgICAgICAgIyB5b3UgY2FuIGFsc28gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSB0aGUgb3V0cHV0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLg0KICAgICAgICAgICAgICAgICAgICAgICkgICANCmBgYA0KDQojIEludHJvZHVjdGlvbg0KDQojIyBEZXNjcmlwdGlvbg0KDQpJbiB0aGlzIGRhdGFzZXQgd2UgaGF2ZSB3ZWF0aGVyIG9ic2VydmF0aW9ucyBmcm9tIEhvdXN0b24sIFRleGFzIGZyb20gMTk0Ny0yMDE0LiBTb21lIHZhcmlhYmxlcyBpbmNsdWRlZCBpbiB0aGlzIGRhdGFzZXQgaW5jbHVkZSBtZWFuIHRlbXBlcmF0dXJlLCB0b3RhbCBwcmVjaXBpdGF0aW9uLCBhbmQgdGhlIGhpZ2hlc3QgdGVtcGVyYXR1cmUuIEluIHRoaXMgc3BlY2lmaWMgdGltZSBzZXJpZXMgYW5hbHlzaXMgd2Ugd2lsbCBsb29rIGF0IHRoZSBtb3N0IHJlY2VudCAxNTAgb2JzZXJ2YXRpb25zIHdoaWNoIGNvbWVzIGZyb20gYXJvdW5kIDIwMDItMjAxNC4gV2Ugd2lsbCBiZSBsb29raW5nIGF0IG9ubHkgb25lIHZhcmlhYmxlIChtZWFudGVtcCkgdG8gc2VlIHRoZSB0cmVuZHMgYXJvdW5kIHRoaXMgZ2l2ZW4gdGltZS4NCg0KIyMgUXVlc3Rpb24NCg0KVGhlIG9iamVjdGl2ZSBvZiB0aGlzIGFuYWx5c2lzIGlzIHRvIHVzZSB0aW1lIHNlcmllcyBtb2RlbGluZyB0byBkcmF3IGNvbmNsdXNpb25zIG9uIHRoZSBtZWFuIHRlbXBlcmF0dXJlIGZvciB0aGUgbmV4dCB5ZWFyIG9mIHRoaXMgZGF0YSBzZXQNCg0KIyMgRGF0YSBDbGVhbmluZw0KDQpgYGB7cn0NCndlYXRoZXIgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9UeWxlckJhdHRhZ2xpbmkvU1RBLTMyMS9yZWZzL2hlYWRzL21haW4vaG91c3Rvbl93ZWF0aGVyLmNzdiIpDQoNCndlYXRoZXJfc3Vic2V0IDwtIHRhaWwod2VhdGhlciwgMTUwKQ0KdHJhaW4ud2VhdGggPSB3ZWF0aGVyX3N1YnNldCRtZWFuVGVtcFsxOjEzOF0NCnRlc3Qud2VhdGggPSB3ZWF0aGVyX3N1YnNldCRtZWFuVGVtcFsxMzk6MTUwXQ0KDQp3ZWF0aCA9IHRzKHRyYWluLndlYXRoLCBzdGFydD1jKDIwMDAsIDEpLCBmcmVxdWVuY3k9MTIpDQoNCmZpdDEgPSBzZXMod2VhdGgsIGg9MTIpDQpmaXQyID0gaG9sdCh3ZWF0aCwgaW5pdGlhbD0ib3B0aW1hbCIsIGg9MTIpDQpmaXQzID0gaG9sdCh3ZWF0aCwgZGFtcGVkPVRSVUUsIGg9MTIpDQpmaXQ0ID0gaG9sdCh3ZWF0aCwgZXhwb25lbnRpYWw9VFJVRSwgZGFtcGVkPVRSVUUsIGg9MTIpDQpmaXQ1ID0gaHcod2VhdGgsIGg9MTIsIHNlYXNvbmFsPSJhZGRpdGl2ZSIpDQpmaXQ2ID0gaHcod2VhdGgsIGg9MTIsIHNlYXNvbmFsPSJtdWx0aXBsaWNhdGl2ZSIpDQpmaXQ3ID0gaHcod2VhdGgsIGg9MTIsIHNlYXNvbmFsPSJhZGRpdGl2ZSIsIGRhbXBlZD1UUlVFKQ0KZml0OCA9IGh3KHdlYXRoLCBoPTEyLCBzZWFzb25hbD0ibXVsdGlwbGljYXRpdmUiLCBkYW1wZWQ9VFJVRSkNCg0KYGBgDQoNCldlIGZvY3VzIG9uIHRoZSBsYXN0IDE1MCBvYnNlcnZhdGlvbnMgb2YgdGhlIGRhdGEgc2V0IHRvIHJlZHVjZSB0aGUgY2hhbmNlIG9mIG92ZXJmaXR0aW5nIGFuZCB0byBnZXQgdGhlIG1vc3QgcmVjZW50IG9ic2VydmF0aW9ucyBvZiB0aGlzIGRhdGFzZXQuIFdlIHRoZW4gc3BsaXQgb3VyIGRhdGEgaW50byBhIHRlc3QgYW5kIGEgdHJhaW5pbmcgc2V0LiAxMiBvYnNlcnZhdGlvbnMgaW4gdGhlIHRlc3Qgc2V0IGFuZCB0aGUgcmVtYWluaW5nIDEzOCBpbiB0aGUgdHJhaW5pbmcgc2V0LiBUaGUgdHJhaW5pbmcgZGF0YSBpcyB0aGVuIGNvbnZlcnRlZCBpbnRvIGEgdGltZSBzZXJpZXMgb2JqZWN0IHdpdGggbW9udGhseSBmcmVxdWVuY3kgb2YgMTIgYmVjYXVzZSB3ZSBhcmUgbG9va2luZyBhdCB0aGUgeWVhcnMuIFdlIHRoZW4gZml0IDggZGlmZmVyZW50IG1vZGVscyB0byBzZWUgd2hpY2ggYmVzdCBmaXRzIG91ciBkYXRhLg0KDQojIEFuYWx5c2lzDQoNCiMjIEFjY3VyYWN5IE1lYXN1cmVzIGZvciBUcmFpbmluZyBEYXRhDQoNCldlIGdldCBtYW55IGFjY3VyYWN5IHZhbHVlcyB0byBzZWUgd2hpY2ggbW9kZWwgaXMgYmVzdCBmb3Igb3VyIGRhdGEuIA0KDQpgYGB7cn0NCmFjY3VyYWN5LnRhYmxlID0gcm91bmQocmJpbmQoYWNjdXJhY3koZml0MSksIGFjY3VyYWN5KGZpdDIpLCBhY2N1cmFjeShmaXQzKSwgYWNjdXJhY3koZml0NCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjY3VyYWN5KGZpdDUpLCBhY2N1cmFjeShmaXQ2KSwgYWNjdXJhY3koZml0NyksIGFjY3VyYWN5KGZpdDgpKSw0KQ0Kcm93Lm5hbWVzKGFjY3VyYWN5LnRhYmxlKT1jKCJTRVMiLCJIb2x0IExpbmVhciIsIkhvbHQgQWRkLiBEYW1wZWQiLCAiSG9sdCBFeHAuIERhbXBlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhXIEFkZC4iLCJIVyBFeHAuIiwiSFcgQWRkLiBEYW1wIiwgIkhXIEV4cC4gRGFtcCIpDQprYWJsZShhY2N1cmFjeS50YWJsZSwgY2FwdGlvbiA9ICJUaGUgYWNjdXJhY3kgbWVhc3VyZXMgb2YgdmFyaW91cyBleHBvbmVudGlhbCBzbW9vdGhpbmcgbW9kZWxzIA0KICAgICAgYmFzZWQgb24gdGhlIHRyYWluaW5nIGRhdGEiKQ0KDQpgYGANCg0KV2Ugc2VlIGZyb20gb3VyIG91dHB1dCBhYm92ZSB0aGF0IEhXIEFkZC4gaXMgdGhlIGJlc3QgZml0IGZvciBvdXIgZGF0YS4gV2Ugc2VlIHRoYXQgSFcgQWRkLiBoYXMgb25lIG9mIHRoZSBsb3dlc3QgdmFsdWVzIGZvciBNRSwgUlNNRSwgTUFFLCBNUEUsIE1BUEUsIGFuZCBNQVNFLiBGb3IgQUNGMSB3ZSBzZWUgdGhhdCBpdCBpcyBpbiB0aGUgbWlkZGxlIGNvbXBhcmVkIHRvIHRoZSBvdGhlciB2YWx1ZXMgYnV0IGl0IGlzIHN0aWxsIGEgcmVsYXRpdmVseSBsb3cgdmFsdWUgbWVhbmluZyB0aGF0IHJlc2lkdWFsIGF1dG9jcnJlbGF0aW9uIGlzIG5vdCBhIGNvbmNlcm4uDQoNCiMjIFZpc3VhbGl6YXRpb24NCg0KV2UgYXJlIHZpc3VhbGl6aW5nIHRoZSBvcmlnaW5hbCB0aW1lIHNlcmllcyBkYXRhIGFuZCBmb3JlY2FzdCByZXN1bHRzIGZyb20gZGlmZmVyZW50IGV4cG9uZW50aWFsIHNtb290aGluZyBtb2RlbHMuIFdlIGNyZWF0ZSB0d28gbW9kZWxzIG9uZSBmb3Igbm9uLXNlYXNvbmFsIHNtb290aGluZyBtb2RlbHMgYW5kIGFub3RoZXIgZm9yIHNlYXNvbmFsIHNtb290aGluZyBtb2RlbHMuDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJyxmaWcud2lkdGg9NiwgZmlnLmhlaWdodD02LjUsIGZpZy5jYXA9IkNhc2Ugc3R1ZHk6IENvbXBhcmluZyB2YXJpb3VzIGV4cG9uZW50aWFsIHNtb290aGluZyBtb2RlbHMuIn0NCnBhcihtZnJvdz1jKDIsMSksIG1hcj1jKDMsNCwzLDEpKQ0KIyMjIyMjIHBsb3QgdGhlIG9yaWdpbmFsIGRhdGENCnByZWQuaWQgPSAxMzk6MTUwDQpwbG90KDE6MTM4LCB0cmFpbi53ZWF0aCwgbHdkPTIsdHlwZT0ibyIsIHlsYWI9Ik1lYW5UZW1wIiwgeGxhYj0iIiwgDQogICAgIHhsaW09YygxLDE2MCksIHlsaW09YygzMCwgOTApLCBjZXg9MC4zLA0KICAgICBtYWluPSJOb24tc2Vhc29uYWwgU21vb3RoaW5nIE1vZGVscyIpDQpsaW5lcyhwcmVkLmlkLCBmaXQxJG1lYW4sIGNvbD0icmVkIikNCmxpbmVzKHByZWQuaWQsIGZpdDIkbWVhbiwgY29sPSJibHVlIikNCmxpbmVzKHByZWQuaWQsIGZpdDMkbWVhbiwgY29sPSJwdXJwbGUiKQ0KbGluZXMocHJlZC5pZCwgZml0NCRtZWFuLCBjb2w9Im5hdnkiKQ0KIyMNCnBvaW50cyhwcmVkLmlkLCBmaXQxJG1lYW4sIHBjaD0xNiwgY29sPSJyZWQiLCBjZXggPSAwLjUpDQpwb2ludHMocHJlZC5pZCwgZml0MiRtZWFuLCBwY2g9MTcsIGNvbD0iYmx1ZSIsIGNleCA9IDAuNSkNCnBvaW50cyhwcmVkLmlkLCBmaXQzJG1lYW4sIHBjaD0xOSwgY29sPSJwdXJwbGUiLCBjZXggPSAwLjUpDQpwb2ludHMocHJlZC5pZCwgZml0NCRtZWFuLCBwY2g9MjEsIGNvbD0ibmF2eSIsIGNleCA9IDAuNSkNCiNwb2ludHMoZml0MCwgY29sPSJibGFjayIsIHBjaD0xKQ0KbGVnZW5kKCJib3R0b21yaWdodCIsIGx0eT0xLCBjb2w9YygicmVkIiwiYmx1ZSIsInB1cnBsZSIsICJuYXZ5IikscGNoPWMoMTYsMTcsMTksMjEpLA0KICAgYygiU0VTIiwiSG9sdCBMaW5lYXIiLCJIb2x0IExpbmVhciBEYW1wZWQiLCAiSG9sdCBNdWx0aXBsaWNhdGl2ZSBEYW1wZWQiKSwgDQogICBjZXggPSAwLjcsIGJ0eT0ibiIpDQoNCnBsb3QoMToxMzgsIHRyYWluLndlYXRoLCBsd2Q9Mix0eXBlPSJvIiwgeWxhYj0iTWVhbiBUZW1wIiwgeGxhYj0iIiwgDQogICAgIHhsaW09YygxLDE2MCksIHlsaW09YygzMCwgOTApLCBjZXg9MC4zLA0KICAgICBtYWluPSJIb2x0LVdpbnRlcmQgVHJlbmQgYW5kIFNlYXNvbmFsIFNtb290aGluZyBNb2RlbHMiKQ0KbGluZXMocHJlZC5pZCwgZml0NSRtZWFuLCBjb2w9InJlZCIpDQpsaW5lcyhwcmVkLmlkLCBmaXQ2JG1lYW4sIGNvbD0iYmx1ZSIpDQpsaW5lcyhwcmVkLmlkLCBmaXQ3JG1lYW4sIGNvbD0icHVycGxlIikNCmxpbmVzKHByZWQuaWQsIGZpdDgkbWVhbiwgY29sPSJuYXZ5IikNCiMjDQpwb2ludHMocHJlZC5pZCwgZml0NSRtZWFuLCBwY2g9MTYsIGNvbD0icmVkIiwgY2V4ID0gMC41KQ0KcG9pbnRzKHByZWQuaWQsIGZpdDYkbWVhbiwgcGNoPTE3LCBjb2w9ImJsdWUiLCBjZXggPSAwLjUpDQpwb2ludHMocHJlZC5pZCwgZml0NyRtZWFuLCBwY2g9MTksIGNvbD0icHVycGxlIiwgY2V4ID0gMC41KQ0KcG9pbnRzKHByZWQuaWQsIGZpdDgkbWVhbiwgcGNoPTIxLCBjb2w9Im5hdnkiLCBjZXggPSAwLjUpDQojIyMNCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCBsdHk9MSwgY29sPWMoInJlZCIsImJsdWUiLCJwdXJwbGUiLCAibmF2eSIpLHBjaD1jKDE2LDE3LDE5LDIxKSwNCiAgIGMoIkhXIEFkZGl0aXZlIiwiSFcgTXVsdGlwbGljYXRpdmUiLCJIVyBBZGRpdGl2ZSBEYW1wZWQiLCAiSFcgTXVsdGlwbGljYXRpdmUgRGFtcGVkIiksIA0KICAgY2V4ID0gMC43LCBidHk9Im4iKQ0KDQpgYGANCg0KV2Ugc2VlIGZyb20gdGhlIGFib3ZlIGFjY3VyYWN5IHRhYmxlIHRoYXQgSFfigJlzIGxpbmVhciB0cmVuZCB3aXRoIGFuIGFkZGl0aXZlIHNlYXNvbmFsIG1vZGVsIGlzIHRoZSBiZXN0IGZpdCBvdXIgb2YgZWlnaHQgc21vb3RoaW5nIG1vZGVscy4gVGhpcyBpcyBhbHNvIG1hdGNoZXMgd2l0aCBvdXIgcHJldmlvdXMgY29uY2x1c2lvbiB0aGF0IEhXIEFkZCBpcyB0aGUgYmVzdCBtb2RlbCBmb3Igb3VyIGRhdGEuDQoNCiMjIEFjY3VyYWN5IE1lYXN1cmVzIGZvciBUZXN0IERhdGENCg0KV2UganVzdCB2aXN1YWxpemVkIG91ciB0cmFpbmluZyBhbmQgdGVzdCBkYXRhIGFuZCB0aGV5IHNob3dlZCB1cyB0aGUgc2FtZSByZXN1bHQgaW4gdGhhdCBIVyBBZGQgaXMgdGhlIGFwcHJvcHJpYXRlIG1vZGVsIGZvciB0aGVzZSB0d28gZGF0YSBzZXRzLiBUbyBtYWtlIGEgcmVhbCBsaWZlIGZvcmVjYXN0IG9mIG91ciBkYXRhIHdlIG5lZWQgdG8gdXNlIHRoZSB3aG9sZSBkYXRhc2V0IHdpdGggYWxsIDE1MCBvYnNlcnZhdGlvbnMuIFdlIHJlZml0IG91ciBkYXRhIHRvIHVwZGF0ZSB0aGUgc21vb3RoaW5nIHBhcmFtZXRlcnMgaW4gb3VyIGZpbmFsIG1vZGVsLiANCg0KYGBge3J9DQoNCmFjYy5mdW4gPSBmdW5jdGlvbih0ZXN0LmRhdGEsIG1vZC5vYmopew0KICBQRT0xMDAqKHRlc3QuZGF0YS1tb2Qub2JqJG1lYW4pL21vZC5vYmokbWVhbg0KICBNQVBFID0gbWVhbihhYnMoUEUpKQ0KICAjIyMNCiAgRT10ZXN0LmRhdGEtbW9kLm9iaiRtZWFuDQogIE1TRT1tZWFuKEVeMikNCiAgIyMjDQogIGFjY3VyYWN5Lm1ldHJpYz1jKE1TRT1NU0UsIE1BUEU9TUFQRSkNCiAgYWNjdXJhY3kubWV0cmljDQp9DQoNCg0KDQpgYGANCg0KYGBge3J9DQpwcmVkLmFjY3VyYWN5ID0gcmJpbmQoU0VTID1hY2MuZnVuKHRlc3QuZGF0YT10ZXN0LndlYXRoLCBtb2Qub2JqPWZpdDEpLA0KICAgICAgICAgICAgICAgICAgICAgIEhvbHQuQWRkID1hY2MuZnVuKHRlc3QuZGF0YT10ZXN0LndlYXRoLCBtb2Qub2JqPWZpdDIpLA0KICAgICAgICAgICAgICAgICAgICAgIEhvbHQuQWRkLkRhbXAgPWFjYy5mdW4odGVzdC5kYXRhPXRlc3Qud2VhdGgsIG1vZC5vYmo9Zml0MyksDQogICAgICAgICAgICAgICAgICAgICAgSG9sdC5FeHAgPWFjYy5mdW4odGVzdC5kYXRhPXRlc3Qud2VhdGgsIG1vZC5vYmo9Zml0NCksDQogICAgICAgICAgICAgICAgICAgICAgSFcuQWRkID1hY2MuZnVuKHRlc3QuZGF0YT10ZXN0LndlYXRoLCBtb2Qub2JqPWZpdDUpLA0KICAgICAgICAgICAgICAgICAgICAgIEhXLkV4cCA9YWNjLmZ1bih0ZXN0LmRhdGE9dGVzdC53ZWF0aCwgbW9kLm9iaj1maXQ2KSwNCiAgICAgICAgICAgICAgICAgICAgICBIVy5BZGQuRGFtcCA9YWNjLmZ1bih0ZXN0LmRhdGE9dGVzdC53ZWF0aCwgbW9kLm9iaj1maXQ3KSwNCiAgICAgICAgICAgICAgICAgICAgICBIVy5FeHAuRGFtcCA9YWNjLmZ1bih0ZXN0LmRhdGE9dGVzdC53ZWF0aCwgbW9kLm9iaj1maXQ4KSkNCmthYmxlKHByZWQuYWNjdXJhY3ksIGNhcHRpb249IlRoZSBhY2N1cmFjeSBtZWFzdXJlcyBvZiB2YXJpb3VzIGV4cG9uZW50aWFsIHNtb290aGluZyBtb2RlbHMgDQogICAgICBiYXNlZCBvbiB0aGUgdGVzdGluZyBkYXRhIikNCg0KYGBgDQoNCldlIHNlZSBmcm9tIHRoZSBhYm92ZSBvdXRwdXQgdGhhdCBIVyBBZGQgaXMgYWdhaW4gdGhlIGJlc3QgbW9kZWwgd2hpY2ggaXMgY29uc2lzdGVudCB0byBvdXIgY29uY2x1c2lvbnMgbWFkZSBwcmV2aW91c2x5IGluIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBkYXRhLiBIVyBBZGQgaXMgdGhlIGJlc3QgbW9kZWwgYmVjYXVzZSBNU0UgYW5kIE1BUEUgYXJlIGJvdGggdGhlIGxvd2VzdCB2YWx1ZXMgb3V0IG9mIHRoZSBtb2RlbHMgd2l0aCBNU0UgPSA1LjExMSBhbmQgTUFQRSA9IDIuOTcyLiANCg0KIyMgRmluYWwgTW9kZWwNCg0KSGVyZSB3ZSB1c2UgdGhlIHdob2xlIGRhdGEgc2V0IGFuZCByZWZpdCBpdCB1c2luZyBzbW9vdGhpbmcgcGFyYW1ldGVycy4NCg0KYGBge3J9DQp3ZWF0aGVyIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vVHlsZXJCYXR0YWdsaW5pL1NUQS0zMjEvcmVmcy9oZWFkcy9tYWluL2hvdXN0b25fd2VhdGhlci5jc3YiKQ0Kd2VhdGg9dHMod2VhdGhlciRtZWFuVGVtcFsxOjE1MF0sIHN0YXJ0PTIwMDAsIGZyZXF1ZW5jeSA9IDEyKQ0KZmluYWwubW9kZWwgPSBodyh3ZWF0aCxoPTEyLCBzZWFzb25hbD0iYWRkaXRpdmUiKSANCnNtb290aGluZy5wYXJhbWV0ZXIgPSBmaW5hbC5tb2RlbCRtb2RlbCRwYXJbMTozXQ0Ka2FibGUoc21vb3RoaW5nLnBhcmFtZXRlciwgY2FwdGlvbj0iRXN0aW1hdGVkIHZhbHVlcyBvZiB0aGUgc21vb3RoaW5nIHBhcmFtZXRlcnMgaW4NCiAgICAgIEhvbHQtV2ludGVycyBsaW5lYXIgdHJlbmQgd2l0aCBhZGRpdGl2ZSBzZWFzb25hbGl0eSIpDQoNCmBgYA0KDQpXZSBzZWUgZnJvbSB0aGUgb3V0cHV0IGFib3ZlIHRoYXQgYSB2YWx1ZSBvZiAwLjEzMSBmb3Igb3VyIGFscGhhICBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9kZWwgcGxhY2VzIG1vZGVyYXRlIHdlaWdodCBvbiByZWNlbnQgb2JzZXJ2YXRpb25zIHdoZW4gdXBkYXRpbmcgdGhlIGxldmVsLiBGb3IgQmV0YSwgLjAwMDAxLCB0aGlzIHZhbHVlIGNsb3NlIHRvIHplcm8gZG9lc24ndCBnaXZlIGltcG9ydGFuY2UgdG8gdXBkYXRpbmcgdGhlIHRyZW5kIGJhc2VkIG9uIHJlY2VudCBvYnNlcnZhdGlvbnMuIEZvciBHYW1tYSwgLjAwMDEsIHdpdGggYSB2YWx1ZSByZWxhdGl2ZWx5IGNsb3NlIHRvIHplcm8gaXQgc2hvd3MgdGhhdCBzZWFzb25hbGl0eSBwYXR0ZXJucyBhcmUgc3RhYmxlIGFuZCB3aWxsIGxpa2VseSBub3QgY2hhbmdlLg0KDQojIENvbmxjdXNpb24NCg0KRm9yIHRoaXMgSG91c3RvbiBXZWF0aGVyIGRhdGEgc2V0IGZyb20gMjAwMi0yMDE0IHJlcHJlc2VudGluZyBtZWFuIHRlbXBlcmF0dXJlIHRoZSBiZXN0IG1vZGVsIHdlIGNhbiB1c2UgaXMgSFcncyBsaW5lYXIgdHJlbmQgdXNpbmcgd2l0aCBhbiBhZGRpdGl2ZSBzZWFzb25hbCBtb2RlbC4gV2UgZGV0ZXJtaW5lZCB0aGlzIGJ5IHNwbGl0dGluZyBvdXIgZGF0YSBpbnRvIHR3byBzZXRzLCBhIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGEgc2V0IGFuZCBmcm9tIHRoZXNlIHR3byBzZXRzIHdlIGdhdGhlcmVkIGFjY3VyYWN5IG1lYXN1cmVzIGFuZCBwbG90cyBhbmQgdGhleSBhbGwgY2FtZSB0byB0aGUgc2FtZSBjb25jbHVzaW9uLCB0aGF0IEhXIEFERCBpcyB0aGUgYmVzdCBtb2RlbC4gV2UgdGhlbiB1c2VkIHRoZSBhZGRpdGl2ZSBtb2RlbCBmb3Igb3VyIGZ1bGwgZGF0YXNldCBhbmQgZ290IGFscGhhLCBiZXRhLCBhbmQgZ2FtbWEgdmFsdWVzLiBPdXIgYWxwaGEgdmFsdWUgb2YgLjEzIHNob3dzIHRoYXQgdGhlIG1vZGVsIG1vZGVyYXRlbHkgYWRqdXN0cyB0aGUgbGV2ZWwgYmFzZWQgb24gcmVjZW50IGRhdGEuIFRoZSBCZXRhIHZhbHVlIG9mIG5lYXJseSB6ZXJvIHNob3dzIHVzIHRoZXJlIGlzIGxpdHRsZSB0byBubyB0cmVuZCBkZXRlY3RlZCBpbiB0aGUgc2VyaWVzLiAgVGhpcyBtZWFucyB0aGF0IHRoZSBtZWFuIHRlbXBlcmF0dXJlIGRvZXMgbm90IGhhdmUgYSBzaWduaWZpY2FudCBsb25nLXRlcm0gdXB3YXJkIG9yIGRvd253YXJkIHRyZW5kIHdoaWNoIHdlIHdvdWxkIGV4cGVjdCBvZiB0ZW1wZXJhdHVyZS4gT3VyIEdhbW1hIHZhbHVlIG9mIG5lYXJseSB6ZXJvIGRlbW9uc3RyYXRlcyB0aGF0IHRoZSBzZWFzb25hbGl0eSB0cmVuZCBkb2VzIG5vdCBjaGFuZ2Ugb3ZlciB0aGlzIGludGVydmFsLg0K