1 Introduction

1.1 Description

This dataset is the monthly weather report for Houston Texas from January 1947 - December 2012. For this specific analysis we will be focusing on the 150 most recent observations which is from 2002 - 2014.

Variables: Month (of Year) Year LowTemp HighTemp WarmestMin ColdestHigh AveMin AveMax meanTemp TotPrecip TotSnow Max24hrPrecip

2 Data Analysis

Weather <- read.csv("https://raw.githubusercontent.com/TylerBattaglini/STA-321/refs/heads/main/houston_weather.csv", header = TRUE)
Weather$Date <- as.Date(paste(Weather$Year, Weather$Month, "01", sep = "-"), format = "%Y-%m-%d")

Weather1 <- Weather[order(Weather$Date), ]

most_recent_150 <- tail(Weather1, 150)

head(most_recent_150)
    Month Year LowTemp HighTemp WarmestMin ColdestHigh AveMin AveMax meanTemp
667     7 2002      71       95         79          82   75.5   90.9     83.2
668     8 2002      70       97         79          76   75.4   91.3     83.3
669     9 2002      64       96         77          79   72.2   87.9     80.1
670    10 2002      50       93         75          67   65.5   79.0     72.3
671    11 2002      38       84         69          51   49.8   69.5     59.7
672    12 2002      35       77         66          43   46.7   64.8     55.7
    TotPrecip Max24hrPrecip       Date
667      8.55          2.29 2002-07-01
668      8.39          5.52 2002-08-01
669      3.53          0.80 2002-09-01
670      9.99          2.84 2002-10-01
671      2.89          1.22 2002-11-01
672      4.84          1.33 2002-12-01

2.1 Time Series Object

Since our data set is monthly our frequency will be set to 12 and is used to define the time series object.

houstonweather.ts <- ts(most_recent_150$meanTemp, frequency = 12, start = c(2002, 1))

par(mar = c(4, 4, 2, 2))
plot(houstonweather.ts, 
     main = "Houston Average Monthly Temperatures (2008-2019)", 
     ylab = "Mean Temperature (°F)", 
     xlab = "Year")

2.2 Decomposing

We use this method to get a better visual understanding of our dataset by using different methods to show our data.

cls.decomp = decompose(houstonweather.ts)
par(mar=c(2,2,2,2))
plot(cls.decomp, xlab="")

stl.decomp=stl(houstonweather.ts, s.window = 12)
par(mar=c(2,2,2,2))
plot(stl.decomp)

We can see above our second decomposing model did a better job at visualizing trends. We see that in the second graph it is clearly outlined that there is a minimum at around 2009 and also a maximum at 2011. While the previous graph is very rough this decomposing model gives us a more smooth looking graph which is easier to read.

2.3 Training and Testing Data

To show the effect of different sizes in training the time series, we use different training data sets with different sizes. the three sizes that were used are 144, 109, 73, and 48. To calculate the prediction error we will use size 7.

ini.data = most_recent_150$meanTemp

n0 = length(ini.data)

train.data01 = ini.data[1:(n0-7)]
train.data02 = ini.data[37:(n0-7)]
train.data03 = ini.data[73:(n0-7)]
train.data04 = ini.data[97:(n0-7)]

test.data = ini.data[(n0-6):n0]

train01.ts = ts(train.data01, frequency = 12, start = c(2002, 1))
train02.ts = ts(train.data02, frequency = 12, start = c(2006, 1))
train03.ts = ts(train.data03, frequency = 12, start = c(2010, 1))
train04.ts = ts(train.data04, frequency = 12, start = c(2014, 1))

stl01 = stl(train01.ts, s.window = 12)
stl02 = stl(train02.ts, s.window = 12)
stl03 = stl(train03.ts, s.window = 12)
stl04 = stl(train04.ts, s.window = 12)

fcst01 = forecast(stl01, h = 7, method = "naive")
fcst02 = forecast(stl02, h = 7, method = "naive")
fcst03 = forecast(stl03, h = 7, method = "naive")
fcst04 = forecast(stl04, h = 7, method = "naive")
PE01 = (test.data - fcst01$mean) / fcst01$mean
PE02 = (test.data - fcst02$mean) / fcst02$mean
PE03 = (test.data - fcst03$mean) / fcst03$mean
PE04 = (test.data - fcst04$mean) / fcst04$mean

MAPE1 = mean(abs(PE01))
MAPE2 = mean(abs(PE02))
MAPE3 = mean(abs(PE03))
MAPE4 = mean(abs(PE04))

E1 = test.data - fcst01$mean
E2 = test.data - fcst02$mean
E3 = test.data - fcst03$mean
E4 = test.data - fcst04$mean

MSE1 = mean(E1^2)
MSE2 = mean(E2^2)
MSE3 = mean(E3^2)
MSE4 = mean(E4^2)

MSE = c(MSE1, MSE2, MSE3, MSE4)
MAPE = c(MAPE1, MAPE2, MAPE3, MAPE4)

accuracy = cbind(MSE = MSE, MAPE = MAPE)
row.names(accuracy) = c("n.144", "n.109", "n.73", "n.48")

kable(accuracy, caption = "Error comparison between forecast results with different sample sizes")
Error comparison between forecast results with different sample sizes
MSE MAPE
n.144 13.212112 0.0477541
n.109 13.274332 0.0473071
n.73 12.067922 0.0440394
n.48 8.515213 0.0365059
par(mfrow = c(1, 2))

plot(1:4, MSE, type = "b", col = "darkred", ylab = "Error", xlab = "",
     main = "MSE", axes = FALSE)
labs = c("n=144", "n=109", "n=73", "n=48")  
axis(1, at = 1:4, labels = labs)  
axis(2)  

text(1:4, MSE - 0.03, as.character(round(MSE, 4)), col = "darkred", cex = 0.7)

plot(1:4, MAPE, type = "b", col = "blue", ylab = "Error", xlab = "",
     main = "MAPE", axes = FALSE)
axis(1, at = 1:4, labels = labs)
axis(2)  

text(1:4, MAPE + 0.03, as.character(round(MAPE, 4)), col = "blue", cex = 0.7)

legend("topright", legend = c("MSE", "MAPE"), col = c("darkred", "blue"), 
       lty = 1, bty = "n", cex = 0.8)

Sample size of 48 gives the lowest MSE (8.515) and lowest MAPE (0.0365) and gives us the the best overall forecast accuracy. This suggests that the model built on the smallest sample size outperforms all the other sizes. However, smaller sample sizes can sometimes lead to overfitting, which might limit the forecast. Sample size of 73 is also a good sample size because compared to the sizes other than 48 it is low in MSE and MAPE. If we are confident that a smaller sample is a good representative of the data then a sample size of 48 would be the best choice. If we are concerned about generalizability or overfitting a sample size of 73 might be a better option, as it also provides solid performance without too much risk from overfitting.

LS0tDQp0aXRsZTogIlRpbWUgU2VyaWVzIEhvdXN0b24gV2VhdGhlciINCmF1dGhvcjogJ1R5bGVyIEJhdHRhZ2xpbmknDQpkYXRlOiAiMjAyNC0xMS0yNCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGZpZ193aWR0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGZpZ193aWR0aDogMw0KICAgIGZpZ19oZWlnaHQ6IDMNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KYWx3YXlzX2FsbG93X2h0bWw6IHRydWUNCi0tLQ0KDQpgYGB7PWh0bWx9DQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLw0KDQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXNpemU6IDI0cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovDQogIGZvbnQtc2l6ZTogMjBweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMjJweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQ0KDQpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQo8L3N0eWxlPg0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBEZXRlY3QsIGluc3RhbGwgYW5kIGxvYWQgcGFja2FnZXMgaWYgbmVlZGVkLg0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJNQVNTIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIk1BU1MiKQ0KICAgbGlicmFyeShNQVNTKQ0KfQ0KaWYgKCFyZXF1aXJlKCJubGVxc2x2IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm5sZXFzbHYiKQ0KICAgbGlicmFyeShubGVxc2x2KQ0KfQ0KIw0KaWYgKCFyZXF1aXJlKCJwYW5kZXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGFuZGVyIikNCiAgIGxpYnJhcnkocGFuZGVyKQ0KfQ0KDQppZiAoIXJlcXVpcmUoInBzeWNoIikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygicHN5Y2giKQ0KICAgbGlicmFyeShwc3ljaCkNCn0NCmlmICghcmVxdWlyZSgiTUFTUyIpKSB7ICAgDQogIGluc3RhbGwucGFja2FnZXMoIk1BU1MiKQ0KICAgbGlicmFyeShNQVNTKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQogICBsaWJyYXJ5KGdncGxvdDIpDQp9DQppZiAoIXJlcXVpcmUoIkdHYWxseSIpKSB7ICAgDQogIGluc3RhbGwucGFja2FnZXMoIkdHYWxseSIpDQogICBsaWJyYXJ5KEdHYWxseSkNCn0NCmlmICghcmVxdWlyZSgiY2FyIikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygiY2FyIikNCiAgIGxpYnJhcnkoY2FyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJkcGx5ciIpKSB7ICAgDQogIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikNCiAgIGxpYnJhcnkoZHBseXIpDQp9DQppZiAoIXJlcXVpcmUoImNhcmV0IikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KICAgbGlicmFyeShjYXJldCkNCn0NCmlmICghcmVxdWlyZSgicmVhZHhsIikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikNCiAgIGxpYnJhcnkocmVhZHhsKQ0KfQ0KaWYgKCFyZXF1aXJlKCJvcGVueGxzeCIpKSB7ICAgDQogIGluc3RhbGwucGFja2FnZXMoIm9wZW54bHN4IikNCiAgIGxpYnJhcnkob3Blbnhsc3gpDQp9DQppZiAoIXJlcXVpcmUoImZvcmVjYXN0IikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygiZm9yZWNhc3QiKQ0KICAgbGlicmFyeShmb3JlY2FzdCkNCn0NCiMgc3BlY2lmaWNhdGlvbnMgb2Ygb3V0cHV0cyBvZiBjb2RlIGluIGNvZGUgY2h1bmtzDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZ3MgPSBGQUxTRSwgICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHlvdSBjYW4gY2hvb3NlIHRvIGluY2x1ZGUgdGhlIHdhcm5pbmcgbWVzc2FnZXMgaW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2VzID0gRkFMU0UsICAjDQogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BICAgICAgICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluIHRoZSBvdXRwdXQgZmlsZS4NCiAgICAgICAgICAgICAgICAgICAgICApICAgDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24NCg0KIyMgRGVzY3JpcHRpb24NCg0KVGhpcyBkYXRhc2V0IGlzIHRoZSBtb250aGx5IHdlYXRoZXIgcmVwb3J0IGZvciBIb3VzdG9uIFRleGFzIGZyb20gSmFudWFyeSAxOTQ3IC0gRGVjZW1iZXIgMjAxMi4gRm9yIHRoaXMgc3BlY2lmaWMgYW5hbHlzaXMgd2Ugd2lsbCBiZSBmb2N1c2luZyBvbiB0aGUgMTUwIG1vc3QgcmVjZW50IG9ic2VydmF0aW9ucyB3aGljaCBpcyBmcm9tIDIwMDIgLSAyMDE0LiANCg0KVmFyaWFibGVzOg0KTW9udGggKG9mIFllYXIpDQpZZWFyDQpMb3dUZW1wDQpIaWdoVGVtcA0KV2FybWVzdE1pbg0KQ29sZGVzdEhpZ2gNCkF2ZU1pbg0KQXZlTWF4DQptZWFuVGVtcA0KVG90UHJlY2lwDQpUb3RTbm93DQpNYXgyNGhyUHJlY2lwDQoNCiMgRGF0YSBBbmFseXNpcw0KDQpgYGB7cn0NCldlYXRoZXIgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9UeWxlckJhdHRhZ2xpbmkvU1RBLTMyMS9yZWZzL2hlYWRzL21haW4vaG91c3Rvbl93ZWF0aGVyLmNzdiIsIGhlYWRlciA9IFRSVUUpDQpXZWF0aGVyJERhdGUgPC0gYXMuRGF0ZShwYXN0ZShXZWF0aGVyJFllYXIsIFdlYXRoZXIkTW9udGgsICIwMSIsIHNlcCA9ICItIiksIGZvcm1hdCA9ICIlWS0lbS0lZCIpDQoNCldlYXRoZXIxIDwtIFdlYXRoZXJbb3JkZXIoV2VhdGhlciREYXRlKSwgXQ0KDQptb3N0X3JlY2VudF8xNTAgPC0gdGFpbChXZWF0aGVyMSwgMTUwKQ0KDQpoZWFkKG1vc3RfcmVjZW50XzE1MCkNCmBgYA0KDQojIyBUaW1lIFNlcmllcyBPYmplY3QNClNpbmNlIG91ciBkYXRhIHNldCBpcyBtb250aGx5IG91ciBmcmVxdWVuY3kgd2lsbCBiZSBzZXQgdG8gMTIgYW5kIGlzIHVzZWQgdG8gZGVmaW5lIHRoZSB0aW1lIHNlcmllcyBvYmplY3QuDQoNCmBgYHtyfQ0KaG91c3RvbndlYXRoZXIudHMgPC0gdHMobW9zdF9yZWNlbnRfMTUwJG1lYW5UZW1wLCBmcmVxdWVuY3kgPSAxMiwgc3RhcnQgPSBjKDIwMDIsIDEpKQ0KDQpwYXIobWFyID0gYyg0LCA0LCAyLCAyKSkNCnBsb3QoaG91c3RvbndlYXRoZXIudHMsIA0KICAgICBtYWluID0gIkhvdXN0b24gQXZlcmFnZSBNb250aGx5IFRlbXBlcmF0dXJlcyAoMjAwOC0yMDE5KSIsIA0KICAgICB5bGFiID0gIk1lYW4gVGVtcGVyYXR1cmUgKMKwRikiLCANCiAgICAgeGxhYiA9ICJZZWFyIikNCg0KYGBgDQoNCiMjIERlY29tcG9zaW5nDQoNCldlIHVzZSB0aGlzIG1ldGhvZCB0byBnZXQgYSBiZXR0ZXIgdmlzdWFsIHVuZGVyc3RhbmRpbmcgb2Ygb3VyIGRhdGFzZXQgYnkgdXNpbmcgZGlmZmVyZW50IG1ldGhvZHMgdG8gc2hvdyBvdXIgZGF0YS4NCg0KYGBge3J9DQpjbHMuZGVjb21wID0gZGVjb21wb3NlKGhvdXN0b253ZWF0aGVyLnRzKQ0KcGFyKG1hcj1jKDIsMiwyLDIpKQ0KcGxvdChjbHMuZGVjb21wLCB4bGFiPSIiKQ0KDQpgYGANCg0KDQpgYGB7cn0NCnN0bC5kZWNvbXA9c3RsKGhvdXN0b253ZWF0aGVyLnRzLCBzLndpbmRvdyA9IDEyKQ0KcGFyKG1hcj1jKDIsMiwyLDIpKQ0KcGxvdChzdGwuZGVjb21wKQ0KDQpgYGANCg0KV2UgY2FuIHNlZSBhYm92ZSBvdXIgc2Vjb25kIGRlY29tcG9zaW5nIG1vZGVsIGRpZCBhIGJldHRlciBqb2IgYXQgdmlzdWFsaXppbmcgdHJlbmRzLiBXZSBzZWUgdGhhdCBpbiB0aGUgc2Vjb25kIGdyYXBoIGl0IGlzIGNsZWFybHkgb3V0bGluZWQgdGhhdCB0aGVyZSBpcyBhIG1pbmltdW0gYXQgYXJvdW5kIDIwMDkgYW5kIGFsc28gYSBtYXhpbXVtIGF0IDIwMTEuIFdoaWxlIHRoZSBwcmV2aW91cyBncmFwaCBpcyB2ZXJ5IHJvdWdoIHRoaXMgZGVjb21wb3NpbmcgbW9kZWwgZ2l2ZXMgdXMgYSBtb3JlIHNtb290aCBsb29raW5nIGdyYXBoIHdoaWNoIGlzIGVhc2llciB0byByZWFkLiANCg0KIyMgVHJhaW5pbmcgYW5kIFRlc3RpbmcgRGF0YQ0KDQpUbyBzaG93IHRoZSBlZmZlY3Qgb2YgZGlmZmVyZW50IHNpemVzIGluIHRyYWluaW5nIHRoZSB0aW1lIHNlcmllcywgd2UgdXNlIGRpZmZlcmVudCB0cmFpbmluZyBkYXRhIHNldHMgd2l0aCBkaWZmZXJlbnQgc2l6ZXMuIHRoZSB0aHJlZSBzaXplcyB0aGF0IHdlcmUgdXNlZCBhcmUgMTQ0LCAxMDksIDczLCBhbmQgNDguIFRvIGNhbGN1bGF0ZSB0aGUgcHJlZGljdGlvbiBlcnJvciB3ZSB3aWxsIHVzZSBzaXplIDcuDQoNCmBgYHtyfQ0KaW5pLmRhdGEgPSBtb3N0X3JlY2VudF8xNTAkbWVhblRlbXANCg0KbjAgPSBsZW5ndGgoaW5pLmRhdGEpDQoNCnRyYWluLmRhdGEwMSA9IGluaS5kYXRhWzE6KG4wLTcpXQ0KdHJhaW4uZGF0YTAyID0gaW5pLmRhdGFbMzc6KG4wLTcpXQ0KdHJhaW4uZGF0YTAzID0gaW5pLmRhdGFbNzM6KG4wLTcpXQ0KdHJhaW4uZGF0YTA0ID0gaW5pLmRhdGFbOTc6KG4wLTcpXQ0KDQp0ZXN0LmRhdGEgPSBpbmkuZGF0YVsobjAtNik6bjBdDQoNCnRyYWluMDEudHMgPSB0cyh0cmFpbi5kYXRhMDEsIGZyZXF1ZW5jeSA9IDEyLCBzdGFydCA9IGMoMjAwMiwgMSkpDQp0cmFpbjAyLnRzID0gdHModHJhaW4uZGF0YTAyLCBmcmVxdWVuY3kgPSAxMiwgc3RhcnQgPSBjKDIwMDYsIDEpKQ0KdHJhaW4wMy50cyA9IHRzKHRyYWluLmRhdGEwMywgZnJlcXVlbmN5ID0gMTIsIHN0YXJ0ID0gYygyMDEwLCAxKSkNCnRyYWluMDQudHMgPSB0cyh0cmFpbi5kYXRhMDQsIGZyZXF1ZW5jeSA9IDEyLCBzdGFydCA9IGMoMjAxNCwgMSkpDQoNCnN0bDAxID0gc3RsKHRyYWluMDEudHMsIHMud2luZG93ID0gMTIpDQpzdGwwMiA9IHN0bCh0cmFpbjAyLnRzLCBzLndpbmRvdyA9IDEyKQ0Kc3RsMDMgPSBzdGwodHJhaW4wMy50cywgcy53aW5kb3cgPSAxMikNCnN0bDA0ID0gc3RsKHRyYWluMDQudHMsIHMud2luZG93ID0gMTIpDQoNCmZjc3QwMSA9IGZvcmVjYXN0KHN0bDAxLCBoID0gNywgbWV0aG9kID0gIm5haXZlIikNCmZjc3QwMiA9IGZvcmVjYXN0KHN0bDAyLCBoID0gNywgbWV0aG9kID0gIm5haXZlIikNCmZjc3QwMyA9IGZvcmVjYXN0KHN0bDAzLCBoID0gNywgbWV0aG9kID0gIm5haXZlIikNCmZjc3QwNCA9IGZvcmVjYXN0KHN0bDA0LCBoID0gNywgbWV0aG9kID0gIm5haXZlIikNCg0KYGBgDQoNCg0KYGBge3J9DQpQRTAxID0gKHRlc3QuZGF0YSAtIGZjc3QwMSRtZWFuKSAvIGZjc3QwMSRtZWFuDQpQRTAyID0gKHRlc3QuZGF0YSAtIGZjc3QwMiRtZWFuKSAvIGZjc3QwMiRtZWFuDQpQRTAzID0gKHRlc3QuZGF0YSAtIGZjc3QwMyRtZWFuKSAvIGZjc3QwMyRtZWFuDQpQRTA0ID0gKHRlc3QuZGF0YSAtIGZjc3QwNCRtZWFuKSAvIGZjc3QwNCRtZWFuDQoNCk1BUEUxID0gbWVhbihhYnMoUEUwMSkpDQpNQVBFMiA9IG1lYW4oYWJzKFBFMDIpKQ0KTUFQRTMgPSBtZWFuKGFicyhQRTAzKSkNCk1BUEU0ID0gbWVhbihhYnMoUEUwNCkpDQoNCkUxID0gdGVzdC5kYXRhIC0gZmNzdDAxJG1lYW4NCkUyID0gdGVzdC5kYXRhIC0gZmNzdDAyJG1lYW4NCkUzID0gdGVzdC5kYXRhIC0gZmNzdDAzJG1lYW4NCkU0ID0gdGVzdC5kYXRhIC0gZmNzdDA0JG1lYW4NCg0KTVNFMSA9IG1lYW4oRTFeMikNCk1TRTIgPSBtZWFuKEUyXjIpDQpNU0UzID0gbWVhbihFM14yKQ0KTVNFNCA9IG1lYW4oRTReMikNCg0KTVNFID0gYyhNU0UxLCBNU0UyLCBNU0UzLCBNU0U0KQ0KTUFQRSA9IGMoTUFQRTEsIE1BUEUyLCBNQVBFMywgTUFQRTQpDQoNCmFjY3VyYWN5ID0gY2JpbmQoTVNFID0gTVNFLCBNQVBFID0gTUFQRSkNCnJvdy5uYW1lcyhhY2N1cmFjeSkgPSBjKCJuLjE0NCIsICJuLjEwOSIsICJuLjczIiwgIm4uNDgiKQ0KDQprYWJsZShhY2N1cmFjeSwgY2FwdGlvbiA9ICJFcnJvciBjb21wYXJpc29uIGJldHdlZW4gZm9yZWNhc3QgcmVzdWx0cyB3aXRoIGRpZmZlcmVudCBzYW1wbGUgc2l6ZXMiKQ0KDQpgYGANCg0KYGBge3J9DQpwYXIobWZyb3cgPSBjKDEsIDIpKQ0KDQpwbG90KDE6NCwgTVNFLCB0eXBlID0gImIiLCBjb2wgPSAiZGFya3JlZCIsIHlsYWIgPSAiRXJyb3IiLCB4bGFiID0gIiIsDQogICAgIG1haW4gPSAiTVNFIiwgYXhlcyA9IEZBTFNFKQ0KbGFicyA9IGMoIm49MTQ0IiwgIm49MTA5IiwgIm49NzMiLCAibj00OCIpICANCmF4aXMoMSwgYXQgPSAxOjQsIGxhYmVscyA9IGxhYnMpICANCmF4aXMoMikgIA0KDQp0ZXh0KDE6NCwgTVNFIC0gMC4wMywgYXMuY2hhcmFjdGVyKHJvdW5kKE1TRSwgNCkpLCBjb2wgPSAiZGFya3JlZCIsIGNleCA9IDAuNykNCg0KcGxvdCgxOjQsIE1BUEUsIHR5cGUgPSAiYiIsIGNvbCA9ICJibHVlIiwgeWxhYiA9ICJFcnJvciIsIHhsYWIgPSAiIiwNCiAgICAgbWFpbiA9ICJNQVBFIiwgYXhlcyA9IEZBTFNFKQ0KYXhpcygxLCBhdCA9IDE6NCwgbGFiZWxzID0gbGFicykNCmF4aXMoMikgIA0KDQp0ZXh0KDE6NCwgTUFQRSArIDAuMDMsIGFzLmNoYXJhY3Rlcihyb3VuZChNQVBFLCA0KSksIGNvbCA9ICJibHVlIiwgY2V4ID0gMC43KQ0KDQpsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gYygiTVNFIiwgIk1BUEUiKSwgY29sID0gYygiZGFya3JlZCIsICJibHVlIiksIA0KICAgICAgIGx0eSA9IDEsIGJ0eSA9ICJuIiwgY2V4ID0gMC44KQ0KDQpgYGANCg0KU2FtcGxlIHNpemUgb2YgNDggZ2l2ZXMgdGhlIGxvd2VzdCBNU0UgKDguNTE1KSBhbmQgbG93ZXN0IE1BUEUgKDAuMDM2NSkgYW5kIGdpdmVzIHVzIHRoZSB0aGUgYmVzdCBvdmVyYWxsIGZvcmVjYXN0IGFjY3VyYWN5LiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlIG1vZGVsIGJ1aWx0IG9uIHRoZSBzbWFsbGVzdCBzYW1wbGUgc2l6ZSBvdXRwZXJmb3JtcyBhbGwgdGhlIG90aGVyIHNpemVzLiBIb3dldmVyLCBzbWFsbGVyIHNhbXBsZSBzaXplcyBjYW4gc29tZXRpbWVzIGxlYWQgdG8gb3ZlcmZpdHRpbmcsIHdoaWNoIG1pZ2h0IGxpbWl0IHRoZSBmb3JlY2FzdC4gU2FtcGxlIHNpemUgb2YgNzMgaXMgYWxzbyBhIGdvb2Qgc2FtcGxlIHNpemUgYmVjYXVzZSBjb21wYXJlZCB0byB0aGUgc2l6ZXMgb3RoZXIgdGhhbiA0OCBpdCBpcyBsb3cgaW4gTVNFIGFuZCBNQVBFLiBJZiB3ZSBhcmUgY29uZmlkZW50IHRoYXQgYSBzbWFsbGVyIHNhbXBsZSBpcyBhIGdvb2QgcmVwcmVzZW50YXRpdmUgb2YgdGhlIGRhdGEgdGhlbiBhIHNhbXBsZSBzaXplIG9mIDQ4IHdvdWxkIGJlIHRoZSBiZXN0IGNob2ljZS4gSWYgd2UgYXJlIGNvbmNlcm5lZCBhYm91dCBnZW5lcmFsaXphYmlsaXR5IG9yIG92ZXJmaXR0aW5nIGEgc2FtcGxlIHNpemUgb2YgNzMgbWlnaHQgYmUgYSBiZXR0ZXIgb3B0aW9uLCBhcyBpdCBhbHNvIHByb3ZpZGVzIHNvbGlkIHBlcmZvcm1hbmNlIHdpdGhvdXQgdG9vIG11Y2ggcmlzayBmcm9tIG92ZXJmaXR0aW5nLg0KDQo=