#load needed packages
library(tidyverse)
library(MASS)
library(forecast)
library(fpp)
library(corrplot)
library(magrittr)
library(zoo)
library(RColorBrewer)
library(gridExtra)
library(Amelia)
#bring in data
train_features = read.csv('dengue_features_train.csv')
train_labels = read.csv('dengue_labels_train.csv')
densubformat = read.csv('submission_format.csv')
#separate data by city
sj_train_features = train_features %>% filter(city == 'sj')
sj_train_labels = train_labels %>% filter(city == 'sj')
iq_train_features = train_features %>% filter(city == 'iq')
iq_train_labels = train_labels %>% filter(city == 'iq')
#data shape for each city
cat('\nSan Juan\n',
'\t features: ', sj_train_features %>% ncol,
'\t entries: ' , sj_train_features %>% nrow,
'\t labels: ' , sj_train_labels %>% nrow)
San Juan
features: 24 entries: 936 labels: 936
cat('\nIquitos\n',
'\t features: ', iq_train_features %>% ncol,
'\t entries: ' , iq_train_features %>% nrow,
'\t labels: ' , iq_train_labels %>% nrow)
Iquitos
features: 24 entries: 520 labels: 520
#Look at Data
head(sj_train_features[1:7])
tail(sj_train_features)
head(iq_train_features[1:7])
tail(iq_train_features)
#Remove "week_start_date"
sj_train_features %<>% dplyr::select(-week_start_date)
iq_train_features %<>% dplyr::select(-week_start_date)
apply(sj_train_features, 2, function(x)
round(100 * (length(which(is.na(x))))/length(x) , digits = 1)) %>%
as.data.frame() %>%
`names<-`('Percent of Missing Values')
#missing values
missmap(sj_train_features)

#example plot of variable with out the NA fix
sj_train_features %>%
mutate(index = as.numeric(row.names(.))) %>%
ggplot(aes(index, ndvi_ne)) +
geom_line(colour = 'dodgerblue') +
ggtitle("Vegetation Index over Time")

#fill in the NA
sj_train_features$ndvi_ne %<>% na.locf(fromLast = TRUE)
iq_train_features$ndvi_ne %<>% na.locf(fromLast = TRUE)
#distrubution of labels SJ
cat('\nSan Juan\n',
'\t total cases mean: ', sj_train_labels$total_cases %>% mean(),
'\t total cases variance: ' , sj_train_labels$total_cases %>% var() )
San Juan
total cases mean: 34.18056 total cases variance: 2640.045
#distrubution of labels IQ
cat('\nIquitos\n',
'\t total cases mean: ', iq_train_labels$total_cases %>% mean(),
'\t total cases variance: ' , iq_train_labels$total_cases %>% var() )
Iquitos
total cases mean: 7.565385 total cases variance: 115.8955
#Total cases of disease
rbind(iq_train_labels, sj_train_labels) %>%
ggplot(aes(x = total_cases,fill = ..count..)) +
geom_histogram(bins = 12, colour = 'black') + ggtitle('Total Cases of Dengue') +
scale_y_continuous(breaks = seq(0,700,100)) + facet_wrap(~city)

#correlations ?
sj_train_features %<>% mutate('total_cases' = sj_train_labels$total_cases)
iq_train_features %<>% mutate('total_cases' = iq_train_labels$total_cases)
#san juan corr
sj_train_features %>%
dplyr::select(-city, -year, -weekofyear) %>%
cor(use = 'pairwise.complete.obs') -> M1
corrplot(M1, type="lower", method="color",
col=brewer.pal(n=8, name="RdBu"),diag=FALSE)

#iquitos corr
iq_train_features %>%
dplyr::select(-city, -year, -weekofyear) %>%
cor(use = 'pairwise.complete.obs') -> M2
corrplot(M2, type="lower", method="color",
col=brewer.pal(n=8, name="RdBu"),diag=FALSE)

#correlation bar
sort(M1[21,-21]) %>%
as.data.frame %>%
`names<-`('correlation') %>%
ggplot(aes(x = reorder(row.names(.), -correlation), y = correlation, fill = correlation)) +
geom_bar(stat='identity', colour = 'black') + scale_fill_continuous(guide = FALSE) + scale_y_continuous(limits = c(-.15,.25)) +
labs(title = 'San Juan\n Correlations', x = NULL, y = NULL) + coord_flip() -> cor1
# can use ncol(M1) instead of 21 to generalize the code
sort(M2[21,-21]) %>%
as.data.frame %>%
`names<-`('correlation') %>%
ggplot(aes(x = reorder(row.names(.), -correlation), y = correlation, fill = correlation)) +
geom_bar(stat='identity', colour = 'black') + scale_fill_continuous(guide = FALSE) + scale_y_continuous(limits = c(-.15,.25)) +
labs(title = 'Iquitos\n Correlations', x = NULL, y = NULL) + coord_flip() -> cor2
grid.arrange(cor1, cor2, nrow = 1)

preprocessData <- function(data_path, labels_path = NULL)
{
# load data
df <- read.csv(data_path)
# features we want
features = c("reanalysis_specific_humidity_g_per_kg", "reanalysis_dew_point_temp_k",
"station_avg_temp_c", "station_min_temp_c")
# fill missing values
df[features] %<>% na.locf(fromLast = TRUE)
# add city if labels data aren't provided
if (is.null(labels_path)) features %<>% c("city", "year", "weekofyear")
# select features we want
df <- df[features]
# add labels to dataframe
if (!is.null(labels_path)) df %<>% cbind(read.csv(labels_path))
# filter by city
df_sj <- filter(df, city == 'sj')
df_iq <- filter(df, city == 'iq')
# return a list with the 2 data frames
return(list(df_sj, df_iq))
}
#preprocess files
preprocessData(data_path = 'dengue_features_train.csv', labels_path = 'dengue_labels_train.csv') -> trains
sj_train <- trains[[1]]; iq_train <- as.data.frame(trains[2])
summary(sj_train)
reanalysis_specific_humidity_g_per_kg reanalysis_dew_point_temp_k
Min. :11.72 Min. :289.6
1st Qu.:15.23 1st Qu.:293.8
Median :16.83 Median :295.4
Mean :16.54 Mean :295.1
3rd Qu.:17.85 3rd Qu.:296.4
Max. :19.44 Max. :297.8
station_avg_temp_c station_min_temp_c city year weekofyear
Min. :22.84 Min. :17.80 iq: 0 Min. :1990 Min. : 1.00
1st Qu.:25.81 1st Qu.:21.70 sj:936 1st Qu.:1994 1st Qu.:13.75
Median :27.21 Median :22.80 Median :1999 Median :26.50
Mean :27.00 Mean :22.59 Mean :1999 Mean :26.50
3rd Qu.:28.18 3rd Qu.:23.90 3rd Qu.:2003 3rd Qu.:39.25
Max. :30.07 Max. :25.60 Max. :2008 Max. :53.00
total_cases
Min. : 0.00
1st Qu.: 9.00
Median : 19.00
Mean : 34.18
3rd Qu.: 37.00
Max. :461.00
summary(iq_train)
reanalysis_specific_humidity_g_per_kg reanalysis_dew_point_temp_k
Min. :12.11 Min. :290.1
1st Qu.:16.12 1st Qu.:294.6
Median :17.43 Median :295.9
Mean :17.10 Mean :295.5
3rd Qu.:18.19 3rd Qu.:296.6
Max. :20.46 Max. :298.4
station_avg_temp_c station_min_temp_c city year weekofyear
Min. :21.40 Min. :14.70 iq:520 Min. :2000 Min. : 1.00
1st Qu.:27.00 1st Qu.:20.60 sj: 0 1st Qu.:2003 1st Qu.:13.75
Median :27.57 Median :21.35 Median :2005 Median :26.50
Mean :27.52 Mean :21.20 Mean :2005 Mean :26.50
3rd Qu.:28.09 3rd Qu.:22.00 3rd Qu.:2007 3rd Qu.:39.25
Max. :30.80 Max. :24.20 Max. :2010 Max. :53.00
total_cases
Min. : 0.000
1st Qu.: 1.000
Median : 5.000
Mean : 7.565
3rd Qu.: 9.000
Max. :116.000
#split into train and test
sj_train_subtrain <- head(sj_train, 800)
sj_train_subtest <- tail(sj_train, nrow(sj_train) - 800)
iq_train_subtrain <- head(iq_train, 400)
iq_train_subtest <- tail(iq_train, nrow(sj_train) - 400)
#negative binomial model replication
mae <- function(error) return(mean(abs(error)) )
get_bst_model <- function(train, test)
{
# Step 1: specify the form of the model
form <- "total_cases ~ 1 +
reanalysis_specific_humidity_g_per_kg +
reanalysis_dew_point_temp_k +
station_avg_temp_c +
station_min_temp_c"
grid = 10 ^(seq(-8, -3,1))
best_alpha = c()
best_score = 1000
# Step 2: Find the best hyper parameter, alpha
for (i in grid)
{
model = glm.nb(formula = form,
data = train,
init.theta = i)
results <- predict(model, test)
score <- mae(test$total_cases - results)
if (score < best_score) {
best_alpha <- i
best_score <- score
cat('\nbest score = ', best_score, '\twith alpha = ', best_alpha)
}
}
# Step 3: refit on entire dataset
combined <- rbind(train, test)
combined_model = glm.nb(formula=form,
data = combined,
init.theta = best_alpha)
return (combined_model)
}
sj_model <- get_bst_model(sj_train_subtrain, sj_train_subtest)
best score = 21.01167 with alpha = 1e-08
iq_model <- get_bst_model(iq_train_subtrain, iq_train_subtest)
best score = 6.421811 with alpha = 1e-08
best score = 6.421811 with alpha = 1e-06
best score = 6.421811 with alpha = 1e-05
#plot san juan
sj_train$fitted = predict(sj_model, sj_train, type = 'response')
sj_train %>%
mutate(index = as.numeric(row.names(.))) %>%
ggplot(aes(x = index)) + ggtitle("San Juan") +
geom_line(aes(y = total_cases, colour = "total_cases")) +
geom_line(aes(y = fitted, colour = "fitted"))

#plot Iquitos
iq_train$fitted = predict(iq_model, iq_train, type = 'response')
iq_train %>%
mutate(index = as.numeric(row.names(.))) %>%
ggplot(aes(x = index)) + ggtitle("Iquitos") +
geom_line(aes(y = total_cases, colour = "total_cases")) +
geom_line(aes(y = fitted, colour = "fitted"))

#submit predictions
tests <- preprocessData('dengue_features_test.csv')
sj_test <- tests[[1]]; iq_test <- tests[[2]]
sj_test$predicted = predict(sj_model , sj_test, type = 'response')
iq_test$predicted = predict(iq_model , iq_test, type = 'response')
submissions = read.csv('submission_format.csv')
inner_join(submissions, rbind(sj_test,iq_test)) %>%
dplyr::select(city, year, weekofyear, total_cases = predicted) ->
predictions
Joining, by = c("city", "year", "weekofyear")
predictions$total_cases %<>% round()
write.csv(predictions, 'negbinomialpredictions.csv', row.names = FALSE)
#time series plot
sj.ts<-ts(sj_train_features$total_cases, frequency=52, start=c(1990, 18))
plot(sj.ts)

iq.ts<-ts(iq_train_features$total_cases, frequency=52, start=c(2000, 26))
plot(iq.ts)

#train and test sets
sj.train.ts<-window(sj.ts, end=c(2004,37))
sj.test.ts<-window(sj.ts,start=c(2004,38))
iq.train.ts<-window(iq.ts, end=c(2008,27))
iq.test.ts<-window(iq.ts,start=c(2008,28))
#neural network models
sj.fit.nnet<-nnetar(sj.train.ts)
sj.fit.nnet
Series: sj.train.ts
Model: NNAR(14,1,8)[52]
Call: nnetar(y = sj.train.ts)
Average of 20 networks, each of which is
a 15-8-1 network with 137 weights
options were - linear output units
sigma^2 estimated as 44.45
iq.fit.nnet<-nnetar(iq.train.ts)
iq.fit.nnet
Series: iq.train.ts
Model: NNAR(3,1,2)[52]
Call: nnetar(y = iq.train.ts)
Average of 20 networks, each of which is
a 4-2-1 network with 13 weights
options were - linear output units
sigma^2 estimated as 34.12
#neural net forecast
sj.fcast.nnet<-forecast(sj.fit.nnet,h=260)
plot(sj.fcast.nnet)
lines(sj.test.ts, col="red")
legend("topright",lty=1,col=c("red","blue"),c("actual values","forecast"))

iq.fcast.nnet<-forecast(iq.fit.nnet,h=156)
plot(iq.fcast.nnet)
lines(iq.test.ts, col="red")
legend("topright",lty=1,col=c("red","blue"),c("actual values","forecast"))

#accuracy
acc.sj.nnet <- accuracy(sj.fcast.nnet, sj.test.ts)
acc.sj.nnet
ME RMSE MAE MPE MAPE MASE ACF1
Training set 0.006646099 6.667078 4.885579 -Inf Inf 0.1225886 -0.01376727
Test set 7.338365616 32.377485 18.987895 -Inf Inf 0.4764430 0.93389023
Theil's U
Training set NA
Test set 0
tsdisplay(residuals(sj.fcast.nnet))

acc.iq.nnet <- accuracy(iq.fcast.nnet,iq.test.ts)
acc.iq.nnet
ME RMSE MAE MPE MAPE MASE ACF1
Training set -0.01527385 5.840966 3.543559 -Inf Inf 0.3871471 -0.001184902
Test set 5.28822909 12.685205 7.300727 -Inf Inf 0.7976317 0.844531021
Theil's U
Training set NA
Test set 0
tsdisplay(residuals(iq.fcast.nnet))

#solutions for neural net model
NNET_sj_sol=data.frame(densubformat[1:260, -4],total_cases=round(sj.fcast.nnet$mean))
NNET_iq_sol=data.frame(densubformat[261:416,-4],total_cases=round(iq.fcast.nnet$mean))
Dengue_NNET_solution=bind_rows(NNET_sj_sol,NNET_iq_sol)
Vectorizing 'ts' elements may not preserve their attributesVectorizing 'ts' elements may not preserve their attributes
write.csv(Dengue_NNET_solution,file="Dengue_NNET_predictions.csv",row.names=F)
#arima model SJ
sj.arima=Arima(sj.train.ts,order=c(1,1,2),seasonal=c(0,0,0))
sj.arima
Series: sj.train.ts
ARIMA(1,1,2)
Coefficients:
ar1 ma1 ma2
-0.7820 0.9503 0.1979
s.e. 0.2153 0.2147 0.0347
sigma^2 estimated as 192.5: log likelihood=-3023.21
AIC=6054.43 AICc=6054.48 BIC=6072.89
sj.arima.fc=forecast(sj.arima,h=260)
acc.sj.arima = accuracy(sj.arima.fc,sj.test.ts)
acc.sj.arima
ME RMSE MAE MPE MAPE MASE ACF1
Training set 0.006577048 13.83894 8.379156 NaN Inf 0.2102492 0.007353076
Test set 14.740977080 34.48529 18.234742 -Inf Inf 0.4575449 0.932173942
Theil's U
Training set NA
Test set 0
plot(sj.arima.fc)

tsdisplay(residuals(sj.arima.fc))

#arima modle IQ
iq.arima=Arima(iq.train.ts,order=c(0,1,2),seasonal=c(0,0,1))
iq.arima
Series: iq.train.ts
ARIMA(0,1,2)(0,0,1)[52]
Coefficients:
ma1 ma2 sma1
-0.2731 -0.3054 0.0385
s.e. 0.0469 0.0479 0.0437
sigma^2 estimated as 53.12: log likelihood=-1418.7
AIC=2845.39 AICc=2845.49 BIC=2861.52
iq.arima.fc=forecast(iq.arima,h=156)
acc.iq.arima = accuracy(iq.arima.fc,iq.test.ts)
acc.iq.arima
ME RMSE MAE MPE MAPE MASE ACF1
Training set 0.008711802 7.253488 3.808308 NaN Inf 0.4160719 -0.00938541
Test set 7.897047615 13.832998 8.248390 -Inf Inf 0.9011674 0.84182636
Theil's U
Training set NA
Test set 0
plot(iq.arima.fc)

tsdisplay(residuals(iq.arima.fc))

#solution for arima model
Arima_sj_sol=data.frame(densubformat[1:260,-4],total_cases=round(sj.arima.fc$mean))
Arima_iq_sol=data.frame(densubformat[261:416,-4],total_cases=round(iq.arima.fc$mean))
Dengue_Arima_solution=bind_rows(Arima_sj_sol,Arima_iq_sol)
Vectorizing 'ts' elements may not preserve their attributesVectorizing 'ts' elements may not preserve their attributes
write.csv(Dengue_Arima_solution,file="Dengue_Arima_predictions.csv",row.names=F)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpgYGB7cn0KI2xvYWQgbmVlZGVkIHBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkoZm9yZWNhc3QpCmxpYnJhcnkoZnBwKQpsaWJyYXJ5KGNvcnJwbG90KQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KHpvbykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KEFtZWxpYSkKYGBgCgoKYGBge3J9CiNicmluZyBpbiBkYXRhCgp0cmFpbl9mZWF0dXJlcyA9IHJlYWQuY3N2KCdkZW5ndWVfZmVhdHVyZXNfdHJhaW4uY3N2JykKdHJhaW5fbGFiZWxzICAgPSByZWFkLmNzdignZGVuZ3VlX2xhYmVsc190cmFpbi5jc3YnKQpkZW5zdWJmb3JtYXQgPSByZWFkLmNzdignc3VibWlzc2lvbl9mb3JtYXQuY3N2JykKYGBgCgoKYGBge3J9CiNzZXBhcmF0ZSBkYXRhIGJ5IGNpdHkKc2pfdHJhaW5fZmVhdHVyZXMgPSB0cmFpbl9mZWF0dXJlcyAlPiUgZmlsdGVyKGNpdHkgPT0gJ3NqJykKc2pfdHJhaW5fbGFiZWxzICAgPSB0cmFpbl9sYWJlbHMgICAlPiUgZmlsdGVyKGNpdHkgPT0gJ3NqJykKCmlxX3RyYWluX2ZlYXR1cmVzID0gdHJhaW5fZmVhdHVyZXMgJT4lIGZpbHRlcihjaXR5ID09ICdpcScpCmlxX3RyYWluX2xhYmVscyAgID0gdHJhaW5fbGFiZWxzICAgJT4lIGZpbHRlcihjaXR5ID09ICdpcScpCmBgYAoKCmBgYHtyfQojZGF0YSBzaGFwZSBmb3IgZWFjaCBjaXR5CmNhdCgnXG5TYW4gSnVhblxuJywKICAgICdcdCBmZWF0dXJlczogJywgc2pfdHJhaW5fZmVhdHVyZXMgJT4lIG5jb2wsIAogICAgJ1x0IGVudHJpZXM6ICcgLCBzal90cmFpbl9mZWF0dXJlcyAlPiUgbnJvdywKICAgICdcdCBsYWJlbHM6ICcgICwgc2pfdHJhaW5fbGFiZWxzICU+JSBucm93KQpjYXQoJ1xuSXF1aXRvc1xuJywKICAgICdcdCBmZWF0dXJlczogJywgaXFfdHJhaW5fZmVhdHVyZXMgJT4lIG5jb2wsIAogICAgJ1x0IGVudHJpZXM6ICcgLCBpcV90cmFpbl9mZWF0dXJlcyAlPiUgbnJvdywKICAgICdcdCBsYWJlbHM6ICcgICwgaXFfdHJhaW5fbGFiZWxzICU+JSBucm93KQpgYGAKCgpgYGB7cn0KI0xvb2sgYXQgRGF0YSAKaGVhZChzal90cmFpbl9mZWF0dXJlc1sxOjddKQp0YWlsKHNqX3RyYWluX2ZlYXR1cmVzKQpoZWFkKGlxX3RyYWluX2ZlYXR1cmVzWzE6N10pCnRhaWwoaXFfdHJhaW5fZmVhdHVyZXMpCmBgYAoKCmBgYHtyfQojUmVtb3ZlICJ3ZWVrX3N0YXJ0X2RhdGUiCnNqX3RyYWluX2ZlYXR1cmVzICU8PiUgZHBseXI6OnNlbGVjdCgtd2Vla19zdGFydF9kYXRlKQppcV90cmFpbl9mZWF0dXJlcyAlPD4lIGRwbHlyOjpzZWxlY3QoLXdlZWtfc3RhcnRfZGF0ZSkKCmFwcGx5KHNqX3RyYWluX2ZlYXR1cmVzLCAyLCBmdW5jdGlvbih4KSAKICByb3VuZCgxMDAgKiAobGVuZ3RoKHdoaWNoKGlzLm5hKHgpKSkpL2xlbmd0aCh4KSAsIGRpZ2l0cyA9IDEpKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYG5hbWVzPC1gKCdQZXJjZW50IG9mIE1pc3NpbmcgVmFsdWVzJykKCiNtaXNzaW5nIHZhbHVlcwoKbWlzc21hcChzal90cmFpbl9mZWF0dXJlcykKCgojZXhhbXBsZSBwbG90IG9mIHZhcmlhYmxlIHdpdGggb3V0IHRoZSBOQSBmaXgKCnNqX3RyYWluX2ZlYXR1cmVzICU+JQogIG11dGF0ZShpbmRleCA9IGFzLm51bWVyaWMocm93Lm5hbWVzKC4pKSkgJT4lCiAgZ2dwbG90KGFlcyhpbmRleCwgbmR2aV9uZSkpICsgCiAgZ2VvbV9saW5lKGNvbG91ciA9ICdkb2RnZXJibHVlJykgKwogIGdndGl0bGUoIlZlZ2V0YXRpb24gSW5kZXggb3ZlciBUaW1lIikKCgojZmlsbCBpbiB0aGUgTkEgCnNqX3RyYWluX2ZlYXR1cmVzJG5kdmlfbmUgJTw+JSBuYS5sb2NmKGZyb21MYXN0ID0gVFJVRSkKaXFfdHJhaW5fZmVhdHVyZXMkbmR2aV9uZSAlPD4lIG5hLmxvY2YoZnJvbUxhc3QgPSBUUlVFKQpgYGAKCgpgYGB7cn0KI2Rpc3RydWJ1dGlvbiBvZiBsYWJlbHMgU0oKY2F0KCdcblNhbiBKdWFuXG4nLAogICAgJ1x0IHRvdGFsIGNhc2VzIG1lYW46ICcsICAgICAgc2pfdHJhaW5fbGFiZWxzJHRvdGFsX2Nhc2VzICU+JSBtZWFuKCksIAogICAgJ1x0IHRvdGFsIGNhc2VzIHZhcmlhbmNlOiAnICwgc2pfdHJhaW5fbGFiZWxzJHRvdGFsX2Nhc2VzICU+JSB2YXIoKSApCiNkaXN0cnVidXRpb24gb2YgbGFiZWxzIElRCmNhdCgnXG5JcXVpdG9zXG4nLAogICAgJ1x0IHRvdGFsIGNhc2VzIG1lYW46ICcsICAgICAgaXFfdHJhaW5fbGFiZWxzJHRvdGFsX2Nhc2VzICU+JSBtZWFuKCksIAogICAgJ1x0IHRvdGFsIGNhc2VzIHZhcmlhbmNlOiAnICwgaXFfdHJhaW5fbGFiZWxzJHRvdGFsX2Nhc2VzICU+JSB2YXIoKSApCmBgYAoKCmBgYHtyfQojVG90YWwgY2FzZXMgb2YgZGlzZWFzZQpyYmluZChpcV90cmFpbl9sYWJlbHMsIHNqX3RyYWluX2xhYmVscykgJT4lIAogIGdncGxvdChhZXMoeCA9IHRvdGFsX2Nhc2VzLGZpbGwgPSAuLmNvdW50Li4pKSArIAogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMiwgY29sb3VyID0gJ2JsYWNrJykgKyBnZ3RpdGxlKCdUb3RhbCBDYXNlcyBvZiBEZW5ndWUnKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDcwMCwxMDApKSArIGZhY2V0X3dyYXAofmNpdHkpCmBgYAoKCmBgYHtyfQojY29ycmVsYXRpb25zID8Kc2pfdHJhaW5fZmVhdHVyZXMgJTw+JSBtdXRhdGUoJ3RvdGFsX2Nhc2VzJyA9IHNqX3RyYWluX2xhYmVscyR0b3RhbF9jYXNlcykKaXFfdHJhaW5fZmVhdHVyZXMgJTw+JSBtdXRhdGUoJ3RvdGFsX2Nhc2VzJyA9IGlxX3RyYWluX2xhYmVscyR0b3RhbF9jYXNlcykKCiNzYW4ganVhbiBjb3JyCnNqX3RyYWluX2ZlYXR1cmVzICU+JSAKICBkcGx5cjo6c2VsZWN0KC1jaXR5LCAteWVhciwgLXdlZWtvZnllYXIpICU+JQogIGNvcih1c2UgPSAncGFpcndpc2UuY29tcGxldGUub2JzJykgLT4gTTEKCmNvcnJwbG90KE0xLCB0eXBlPSJsb3dlciIsIG1ldGhvZD0iY29sb3IiLAogICAgICAgICBjb2w9YnJld2VyLnBhbChuPTgsIG5hbWU9IlJkQnUiKSxkaWFnPUZBTFNFKQoKCiNpcXVpdG9zIGNvcnIKaXFfdHJhaW5fZmVhdHVyZXMgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWNpdHksIC15ZWFyLCAtd2Vla29meWVhcikgJT4lCiAgY29yKHVzZSA9ICdwYWlyd2lzZS5jb21wbGV0ZS5vYnMnKSAtPiBNMgoKY29ycnBsb3QoTTIsIHR5cGU9Imxvd2VyIiwgbWV0aG9kPSJjb2xvciIsCiAgICAgICAgIGNvbD1icmV3ZXIucGFsKG49OCwgbmFtZT0iUmRCdSIpLGRpYWc9RkFMU0UpCgoKCiNjb3JyZWxhdGlvbiBiYXIKc29ydChNMVsyMSwtMjFdKSAlPiUgIAogIGFzLmRhdGEuZnJhbWUgJT4lIAogIGBuYW1lczwtYCgnY29ycmVsYXRpb24nKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKHJvdy5uYW1lcyguKSwgLWNvcnJlbGF0aW9uKSwgeSA9IGNvcnJlbGF0aW9uLCBmaWxsID0gY29ycmVsYXRpb24pKSArIAogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgY29sb3VyID0gJ2JsYWNrJykgKyBzY2FsZV9maWxsX2NvbnRpbnVvdXMoZ3VpZGUgPSBGQUxTRSkgKyBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gIGMoLS4xNSwuMjUpKSArCiAgbGFicyh0aXRsZSA9ICdTYW4gSnVhblxuIENvcnJlbGF0aW9ucycsIHggPSBOVUxMLCB5ID0gTlVMTCkgKyBjb29yZF9mbGlwKCkgLT4gY29yMQoKIyBjYW4gdXNlIG5jb2woTTEpIGluc3RlYWQgb2YgMjEgdG8gZ2VuZXJhbGl6ZSB0aGUgY29kZQpzb3J0KE0yWzIxLC0yMV0pICU+JSAgCiAgYXMuZGF0YS5mcmFtZSAlPiUgCiAgYG5hbWVzPC1gKCdjb3JyZWxhdGlvbicpICU+JQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIocm93Lm5hbWVzKC4pLCAtY29ycmVsYXRpb24pLCB5ID0gY29ycmVsYXRpb24sIGZpbGwgPSBjb3JyZWxhdGlvbikpICsgCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLCBjb2xvdXIgPSAnYmxhY2snKSArIHNjYWxlX2ZpbGxfY29udGludW91cyhndWlkZSA9IEZBTFNFKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSAgYygtLjE1LC4yNSkpICsKICBsYWJzKHRpdGxlID0gJ0lxdWl0b3NcbiBDb3JyZWxhdGlvbnMnLCB4ID0gTlVMTCwgeSA9IE5VTEwpICsgY29vcmRfZmxpcCgpIC0+IGNvcjIKCmdyaWQuYXJyYW5nZShjb3IxLCBjb3IyLCBucm93ID0gMSkKYGBgCgoKYGBge3J9CnByZXByb2Nlc3NEYXRhIDwtIGZ1bmN0aW9uKGRhdGFfcGF0aCwgbGFiZWxzX3BhdGggPSBOVUxMKQp7CiAgIyBsb2FkIGRhdGEgCiAgZGYgPC0gcmVhZC5jc3YoZGF0YV9wYXRoKQogIAogICMgZmVhdHVyZXMgd2Ugd2FudAogIGZlYXR1cmVzID0gYygicmVhbmFseXNpc19zcGVjaWZpY19odW1pZGl0eV9nX3Blcl9rZyIsICJyZWFuYWx5c2lzX2Rld19wb2ludF90ZW1wX2siLAogICAgICAgICAgICAgICAic3RhdGlvbl9hdmdfdGVtcF9jIiwgInN0YXRpb25fbWluX3RlbXBfYyIpCiAgCiAgIyBmaWxsIG1pc3NpbmcgdmFsdWVzCiAgZGZbZmVhdHVyZXNdICU8PiUgbmEubG9jZihmcm9tTGFzdCA9IFRSVUUpIAogIAogICMgYWRkIGNpdHkgaWYgbGFiZWxzIGRhdGEgYXJlbid0IHByb3ZpZGVkCiAgaWYgKGlzLm51bGwobGFiZWxzX3BhdGgpKSBmZWF0dXJlcyAlPD4lIGMoImNpdHkiLCAieWVhciIsICJ3ZWVrb2Z5ZWFyIikKICAKICAjIHNlbGVjdCBmZWF0dXJlcyB3ZSB3YW50CiAgZGYgPC0gZGZbZmVhdHVyZXNdCiAgCiAgIyBhZGQgbGFiZWxzIHRvIGRhdGFmcmFtZQogIGlmICghaXMubnVsbChsYWJlbHNfcGF0aCkpIGRmICU8PiUgY2JpbmQocmVhZC5jc3YobGFiZWxzX3BhdGgpKQogIAogICMgZmlsdGVyIGJ5IGNpdHkKICBkZl9zaiA8LSBmaWx0ZXIoZGYsIGNpdHkgPT0gJ3NqJykKICBkZl9pcSA8LSBmaWx0ZXIoZGYsIGNpdHkgPT0gJ2lxJykKICAKICAjIHJldHVybiBhIGxpc3Qgd2l0aCB0aGUgMiBkYXRhIGZyYW1lcyAKICByZXR1cm4obGlzdChkZl9zaiwgZGZfaXEpKQp9CgoKI3ByZXByb2Nlc3MgZmlsZXMKcHJlcHJvY2Vzc0RhdGEoZGF0YV9wYXRoID0gJ2Rlbmd1ZV9mZWF0dXJlc190cmFpbi5jc3YnLCBsYWJlbHNfcGF0aCA9ICdkZW5ndWVfbGFiZWxzX3RyYWluLmNzdicpIC0+IHRyYWlucwpzal90cmFpbiA8LSB0cmFpbnNbWzFdXTsgaXFfdHJhaW4gPC0gYXMuZGF0YS5mcmFtZSh0cmFpbnNbMl0pCgpzdW1tYXJ5KHNqX3RyYWluKQpzdW1tYXJ5KGlxX3RyYWluKQpgYGAKCgpgYGB7cn0KI3NwbGl0IGludG8gdHJhaW4gYW5kIHRlc3QKc2pfdHJhaW5fc3VidHJhaW4gPC0gaGVhZChzal90cmFpbiwgODAwKQpzal90cmFpbl9zdWJ0ZXN0ICA8LSB0YWlsKHNqX3RyYWluLCBucm93KHNqX3RyYWluKSAtIDgwMCkKCmlxX3RyYWluX3N1YnRyYWluIDwtIGhlYWQoaXFfdHJhaW4sIDQwMCkKaXFfdHJhaW5fc3VidGVzdCAgPC0gdGFpbChpcV90cmFpbiwgbnJvdyhzal90cmFpbikgLSA0MDApCmBgYAoKCmBgYHtyfQojbmVnYXRpdmUgYmlub21pYWwgbW9kZWwgcmVwbGljYXRpb24KCgptYWUgPC0gZnVuY3Rpb24oZXJyb3IpIHJldHVybihtZWFuKGFicyhlcnJvcikpICkKCmdldF9ic3RfbW9kZWwgPC0gZnVuY3Rpb24odHJhaW4sIHRlc3QpCnsKICAKICAjIFN0ZXAgMTogc3BlY2lmeSB0aGUgZm9ybSBvZiB0aGUgbW9kZWwKICBmb3JtIDwtICJ0b3RhbF9jYXNlcyB+IDEgKwogICAgICAgICAgICByZWFuYWx5c2lzX3NwZWNpZmljX2h1bWlkaXR5X2dfcGVyX2tnICsKICAgICAgICAgICAgcmVhbmFseXNpc19kZXdfcG9pbnRfdGVtcF9rICsgCiAgICAgICAgICAgIHN0YXRpb25fYXZnX3RlbXBfYyArCiAgICAgICAgICAgIHN0YXRpb25fbWluX3RlbXBfYyIKICAKICBncmlkID0gMTAgXihzZXEoLTgsIC0zLDEpKQogIAogIGJlc3RfYWxwaGEgPSBjKCkKICBiZXN0X3Njb3JlID0gMTAwMAogIAogICMgU3RlcCAyOiBGaW5kIHRoZSBiZXN0IGh5cGVyIHBhcmFtZXRlciwgYWxwaGEKICBmb3IgKGkgaW4gZ3JpZCkKICB7CiAgICBtb2RlbCA9IGdsbS5uYihmb3JtdWxhID0gZm9ybSwKICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbiwKICAgICAgICAgICAgICAgICAgIGluaXQudGhldGEgPSBpKQogICAgCiAgICByZXN1bHRzIDwtICBwcmVkaWN0KG1vZGVsLCB0ZXN0KQogICAgc2NvcmUgICA8LSAgbWFlKHRlc3QkdG90YWxfY2FzZXMgLSByZXN1bHRzKQogICAgCiAgICBpZiAoc2NvcmUgPCBiZXN0X3Njb3JlKSB7CiAgICAgIGJlc3RfYWxwaGEgPC0gaQogICAgICBiZXN0X3Njb3JlIDwtIHNjb3JlCiAgICAgIGNhdCgnXG5iZXN0IHNjb3JlID0gJywgYmVzdF9zY29yZSwgJ1x0d2l0aCBhbHBoYSA9ICcsIGJlc3RfYWxwaGEpCiAgICB9CiAgfQogIAogICMgU3RlcCAzOiByZWZpdCBvbiBlbnRpcmUgZGF0YXNldAogIGNvbWJpbmVkIDwtIHJiaW5kKHRyYWluLCB0ZXN0KQogIGNvbWJpbmVkX21vZGVsID0gZ2xtLm5iKGZvcm11bGE9Zm9ybSwKICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gY29tYmluZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgaW5pdC50aGV0YSA9IGJlc3RfYWxwaGEpCiAgCiAgcmV0dXJuIChjb21iaW5lZF9tb2RlbCkKfQpzal9tb2RlbCA8LSBnZXRfYnN0X21vZGVsKHNqX3RyYWluX3N1YnRyYWluLCBzal90cmFpbl9zdWJ0ZXN0KQppcV9tb2RlbCA8LSBnZXRfYnN0X21vZGVsKGlxX3RyYWluX3N1YnRyYWluLCBpcV90cmFpbl9zdWJ0ZXN0KQpgYGAKCgpgYGB7cn0KI3Bsb3Qgc2FuIGp1YW4KCnNqX3RyYWluJGZpdHRlZCA9IHByZWRpY3Qoc2pfbW9kZWwsIHNqX3RyYWluLCB0eXBlID0gJ3Jlc3BvbnNlJykKc2pfdHJhaW4gJT4lIAogIG11dGF0ZShpbmRleCA9IGFzLm51bWVyaWMocm93Lm5hbWVzKC4pKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gaW5kZXgpKSArIGdndGl0bGUoIlNhbiBKdWFuIikgKwogIGdlb21fbGluZShhZXMoeSA9IHRvdGFsX2Nhc2VzLCBjb2xvdXIgPSAidG90YWxfY2FzZXMiKSkgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBmaXR0ZWQsIGNvbG91ciA9ICJmaXR0ZWQiKSkKYGBgCgoKYGBge3J9CiNwbG90IElxdWl0b3MKaXFfdHJhaW4kZml0dGVkID0gcHJlZGljdChpcV9tb2RlbCwgaXFfdHJhaW4sIHR5cGUgPSAncmVzcG9uc2UnKQppcV90cmFpbiAlPiUgCiAgbXV0YXRlKGluZGV4ID0gYXMubnVtZXJpYyhyb3cubmFtZXMoLikpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBpbmRleCkpICsgZ2d0aXRsZSgiSXF1aXRvcyIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gdG90YWxfY2FzZXMsIGNvbG91ciA9ICJ0b3RhbF9jYXNlcyIpKSArIAogIGdlb21fbGluZShhZXMoeSA9IGZpdHRlZCwgY29sb3VyID0gImZpdHRlZCIpKQpgYGAKCgpgYGB7cn0KI3N1Ym1pdCBwcmVkaWN0aW9ucwp0ZXN0cyA8LSBwcmVwcm9jZXNzRGF0YSgnZGVuZ3VlX2ZlYXR1cmVzX3Rlc3QuY3N2JykKc2pfdGVzdCA8LSB0ZXN0c1tbMV1dOyBpcV90ZXN0IDwtIHRlc3RzW1syXV0KCnNqX3Rlc3QkcHJlZGljdGVkID0gcHJlZGljdChzal9tb2RlbCAsIHNqX3Rlc3QsIHR5cGUgPSAncmVzcG9uc2UnKQppcV90ZXN0JHByZWRpY3RlZCA9IHByZWRpY3QoaXFfbW9kZWwgLCBpcV90ZXN0LCB0eXBlID0gJ3Jlc3BvbnNlJykKCnN1Ym1pc3Npb25zID0gcmVhZC5jc3YoJ3N1Ym1pc3Npb25fZm9ybWF0LmNzdicpCmlubmVyX2pvaW4oc3VibWlzc2lvbnMsIHJiaW5kKHNqX3Rlc3QsaXFfdGVzdCkpICU+JQogIGRwbHlyOjpzZWxlY3QoY2l0eSwgeWVhciwgd2Vla29meWVhciwgdG90YWxfY2FzZXMgPSBwcmVkaWN0ZWQpIC0+CiAgcHJlZGljdGlvbnMKCnByZWRpY3Rpb25zJHRvdGFsX2Nhc2VzICU8PiUgcm91bmQoKQp3cml0ZS5jc3YocHJlZGljdGlvbnMsICduZWdiaW5vbWlhbHByZWRpY3Rpb25zLmNzdicsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCgpgYGB7cn0KI3RpbWUgc2VyaWVzIHBsb3QgCgpzai50czwtdHMoc2pfdHJhaW5fZmVhdHVyZXMkdG90YWxfY2FzZXMsIGZyZXF1ZW5jeT01Miwgc3RhcnQ9YygxOTkwLCAxOCkpCnBsb3Qoc2oudHMpCgppcS50czwtdHMoaXFfdHJhaW5fZmVhdHVyZXMkdG90YWxfY2FzZXMsIGZyZXF1ZW5jeT01Miwgc3RhcnQ9YygyMDAwLCAyNikpCnBsb3QoaXEudHMpCgoKI3RyYWluIGFuZCB0ZXN0IHNldHMKc2oudHJhaW4udHM8LXdpbmRvdyhzai50cywgZW5kPWMoMjAwNCwzNykpCnNqLnRlc3QudHM8LXdpbmRvdyhzai50cyxzdGFydD1jKDIwMDQsMzgpKQoKaXEudHJhaW4udHM8LXdpbmRvdyhpcS50cywgZW5kPWMoMjAwOCwyNykpCmlxLnRlc3QudHM8LXdpbmRvdyhpcS50cyxzdGFydD1jKDIwMDgsMjgpKQpgYGAKCgpgYGB7cn0KI25ldXJhbCBuZXR3b3JrIG1vZGVscwoKc2ouZml0Lm5uZXQ8LW5uZXRhcihzai50cmFpbi50cykKc2ouZml0Lm5uZXQKCgppcS5maXQubm5ldDwtbm5ldGFyKGlxLnRyYWluLnRzKQppcS5maXQubm5ldAoKI25ldXJhbCBuZXQgZm9yZWNhc3QKCnNqLmZjYXN0Lm5uZXQ8LWZvcmVjYXN0KHNqLmZpdC5ubmV0LGg9MjYwKQpwbG90KHNqLmZjYXN0Lm5uZXQpIApsaW5lcyhzai50ZXN0LnRzLCBjb2w9InJlZCIpICAKbGVnZW5kKCJ0b3ByaWdodCIsbHR5PTEsY29sPWMoInJlZCIsImJsdWUiKSxjKCJhY3R1YWwgdmFsdWVzIiwiZm9yZWNhc3QiKSkKCgppcS5mY2FzdC5ubmV0PC1mb3JlY2FzdChpcS5maXQubm5ldCxoPTE1NikKcGxvdChpcS5mY2FzdC5ubmV0KSAKbGluZXMoaXEudGVzdC50cywgY29sPSJyZWQiKSAgCmxlZ2VuZCgidG9wcmlnaHQiLGx0eT0xLGNvbD1jKCJyZWQiLCJibHVlIiksYygiYWN0dWFsIHZhbHVlcyIsImZvcmVjYXN0IikpCgojYWNjdXJhY3kKYWNjLnNqLm5uZXQgPC0gYWNjdXJhY3koc2ouZmNhc3Qubm5ldCwgc2oudGVzdC50cykKYWNjLnNqLm5uZXQKCgp0c2Rpc3BsYXkocmVzaWR1YWxzKHNqLmZjYXN0Lm5uZXQpKQoKCmFjYy5pcS5ubmV0IDwtIGFjY3VyYWN5KGlxLmZjYXN0Lm5uZXQsaXEudGVzdC50cykKYWNjLmlxLm5uZXQKCnRzZGlzcGxheShyZXNpZHVhbHMoaXEuZmNhc3Qubm5ldCkpCgojc29sdXRpb25zIGZvciBuZXVyYWwgbmV0IG1vZGVsCk5ORVRfc2pfc29sPWRhdGEuZnJhbWUoZGVuc3ViZm9ybWF0WzE6MjYwLCAtNF0sdG90YWxfY2FzZXM9cm91bmQoc2ouZmNhc3Qubm5ldCRtZWFuKSkKTk5FVF9pcV9zb2w9ZGF0YS5mcmFtZShkZW5zdWJmb3JtYXRbMjYxOjQxNiwtNF0sdG90YWxfY2FzZXM9cm91bmQoaXEuZmNhc3Qubm5ldCRtZWFuKSkKCkRlbmd1ZV9OTkVUX3NvbHV0aW9uPWJpbmRfcm93cyhOTkVUX3NqX3NvbCxOTkVUX2lxX3NvbCkKd3JpdGUuY3N2KERlbmd1ZV9OTkVUX3NvbHV0aW9uLGZpbGU9IkRlbmd1ZV9OTkVUX3ByZWRpY3Rpb25zLmNzdiIscm93Lm5hbWVzPUYpCmBgYAoKCmBgYHtyfQojYXJpbWEgbW9kZWwgU0oKCnNqLmFyaW1hPUFyaW1hKHNqLnRyYWluLnRzLG9yZGVyPWMoMSwxLDIpLHNlYXNvbmFsPWMoMCwwLDApKQpzai5hcmltYQpzai5hcmltYS5mYz1mb3JlY2FzdChzai5hcmltYSxoPTI2MCkKYWNjLnNqLmFyaW1hID0gYWNjdXJhY3koc2ouYXJpbWEuZmMsc2oudGVzdC50cykKYWNjLnNqLmFyaW1hCnBsb3Qoc2ouYXJpbWEuZmMpCnRzZGlzcGxheShyZXNpZHVhbHMoc2ouYXJpbWEuZmMpKQoKCiNhcmltYSBtb2RsZSBJUQppcS5hcmltYT1BcmltYShpcS50cmFpbi50cyxvcmRlcj1jKDAsMSwyKSxzZWFzb25hbD1jKDAsMCwxKSkKaXEuYXJpbWEKaXEuYXJpbWEuZmM9Zm9yZWNhc3QoaXEuYXJpbWEsaD0xNTYpCmFjYy5pcS5hcmltYSA9IGFjY3VyYWN5KGlxLmFyaW1hLmZjLGlxLnRlc3QudHMpCmFjYy5pcS5hcmltYQpwbG90KGlxLmFyaW1hLmZjKQp0c2Rpc3BsYXkocmVzaWR1YWxzKGlxLmFyaW1hLmZjKSkKCiNzb2x1dGlvbiBmb3IgYXJpbWEgbW9kZWwKQXJpbWFfc2pfc29sPWRhdGEuZnJhbWUoZGVuc3ViZm9ybWF0WzE6MjYwLC00XSx0b3RhbF9jYXNlcz1yb3VuZChzai5hcmltYS5mYyRtZWFuKSkKQXJpbWFfaXFfc29sPWRhdGEuZnJhbWUoZGVuc3ViZm9ybWF0WzI2MTo0MTYsLTRdLHRvdGFsX2Nhc2VzPXJvdW5kKGlxLmFyaW1hLmZjJG1lYW4pKQoKRGVuZ3VlX0FyaW1hX3NvbHV0aW9uPWJpbmRfcm93cyhBcmltYV9zal9zb2wsQXJpbWFfaXFfc29sKQoKd3JpdGUuY3N2KERlbmd1ZV9BcmltYV9zb2x1dGlvbixmaWxlPSJEZW5ndWVfQXJpbWFfcHJlZGljdGlvbnMuY3N2Iixyb3cubmFtZXM9RikKCmBgYAoK