# Authors: Rahul Bamotra, Maximilian Kantsler, Ryan Ansett, Anson Abraham, Maxim Skliarov
# For proper execution of this code the following files need to be located in the Working Directory
#
# BMI Index.csv
# hapind2020.csv
# HIV-AIDS.csv
# Malaria.csv
# Tuberculosis.csv
# Packages used: countrycode; readxl; wpp2019; wbstats; dplyr; rvest; ggplot2; ggfortify; car; pedometrics; plotly
library(countrycode)
library(readxl)
library(wpp2019)
library(wbstats)
library(dplyr)
library(rvest)
library(ggplot2)
library(ggfortify)
library(car)
library(pedometrics)
library(plotly)
rm(list = ls())
# Scraping from Global Residence Website
# Scraping code taken from: https://beanumber.github.io/sds192/lab-import.html
src <- "https://globalresidenceindex.com/wp-content/uploads/2017/10/STC-Climate-18.xlsx"
lcl <- basename(src)
download.file(url = src, destfile = lcl,mode='wb')
weather <- as.data.frame(read_excel(lcl))
c.code<-countrycode(weather[,2],"country.name","iso3c")
weather<-cbind(weather,c.code)
src <- "https://globalresidenceindex.com/wp-content/uploads/2017/10/STC-Health-Index-2018.xlsx"
lcl <- basename(src)
download.file(url = src, destfile = lcl,mode='wb')
health<-as.data.frame(read_excel(lcl))
c.code<-countrycode(health[,2],"country.name","iso3c")
health<-cbind(health,c.code)
# Getting Life Expectancy from World Bank
inf <- wb_search("Life Expectancy")
life_exp<-wb_data(country="countries_only",indicator = c("SP.DYN.LE00.FE.IN","SP.DYN.LE00.IN","SP.DYN.LE00.MA.IN"),start_date = 2016,end_date = 2016)
life_exp<-na.exclude(life_exp)
colnames(life_exp)[2] <- "c.code"
happiness<-read.csv(file='hapind2020.csv')
hapdata<-happiness[c(1:3,7)]
c.code<-countrycode(hapdata[,1],"country.name","iso3c")
hapdata<-cbind(hapdata,c.code)
rm(c.code,inf,lcl,src,happiness)
data(pop) #Call for data of population
#remove data that is not required
rm(popF)
rm(popM)
rm(popFT)
rm(popMT)
#Get a column of country code data in the population data frame
pop$country.code <- countrycode(
pop[,2],
"country.name",
"iso3c",
)
#Data cleanup - only get the columns required for the code
pop <- pop[, c(2,16,18)]
pop <- pop[complete.cases(pop),]
pop <- pop[-1,]
colnames(pop) <- c("country.name", "population","c.code")
variables <- weather[,c(16,4,6,10,11,13,15)]
cor(variables[-1])
# Check for correlations and patterns within the variables (same for every regression set)
plot(variables, pch = 15, col = "blue")
# Merging the variables with the response variables
data.lif <- right_join(life_exp[,c(2,6)], variables, by ="c.code")
data.lif <- data.lif[complete.cases(data.lif),]
attr(data.lif$SP.DYN.LE00.IN, "label") <- NULL # Removing attribute from column name
colnames(data.lif) <- c("c.code", "Response", "Index", "Days32",
"Precipitation.Annual", "Precipitation.Days",
"Heating.Days", "Sunshine.Hours")
data.hap <- right_join(hapdata[,c(5,3)], variables, by ="c.code")
data.hap <- data.hap[complete.cases(data.hap),]
colnames(data.hap) <- c("c.code", "Response", "Index", "Days32",
"Precipitation.Annual", "Precipitation.Days",
"Heating.Days", "Sunshine.Hours")
lif <- lm(Response ~ . -c.code, data = data.lif) # Problem with country codes
summary(lif)
# Check for relationships of variables to response variable (normalized value)
plot(data.lif, pch = 15, col = "blue")
# After checking for model accuracy we decided to include higher order terms of variables to improve the model
lif <- lm(Response ~ Precipitation.Days + poly(Heating.Days, 4) + poly(Index, 4) + poly(Precipitation.Annual, 4), data = data.lif)
summary(lif)
predict_data.lif <- data.frame(data.lif[,-c(2)])
predict_data.lif$predict_respone <- predict.lm(lif,predict_data.lif)
t1 <- list(family = "Arial, sans-serif", size = 13, color = "black")
t2 <- list(family = "Arial, sans-serif", size = 12, color = "black")
line_45 <- data.frame("Prediction"= c(70,82),"Actual"=c(70,82))
compare_df <- data.frame(predict_data.lif$predict_respone,data.lif$Response)
colnames(compare_df) <- c("Prediction","Actual")
fig <- plot_ly(compare_df)
fig <- fig %>% add_trace(x=~Actual,y=~Prediction,type="scatter",name="Life Expectancy Prediction",marker = list(color = 'cornflowerblue'))
fig <- fig %>% add_trace(x=line_45$Prediction,y=line_45$Actual,name="1:1 45°Line",type="scatter",mode="line",line=list(width=3,dash='dash',color='red'))
fig <- fig %>% layout(title="Life Expectancy Comparison",
yaxis = list(title = 'Predicted Life Expectancy',titlefont=t1,tickfont=t2,showgrid=F,showline = T,range=c(65,85)),
xaxis=list(autotick=FALSE, dtick=5,title="Actual Life Expectancy",titlefont=t1,tickfont=t2,showgrid=F,showline = T,range=c(65,85)),
legend = list(orientation = 'v',x=0.1,y=0.8),
showlegend=T)
fig
# Check normality of errors - Histogram for Life Expectancy
fig1 <- plot_ly(x=~lif$residuals, type = "histogram",marker=list(color="cornflowerblue"))
fig1 <- fig1 %>% layout(title="Histogram of Residuals - Life Expectancy",
yaxis=list(title="Frequency",titlefont=t1,tickfont=t2,showgrid=F,showline=T),
xaxis=list(title="Residuals - Life Expectancy",titlefont=t1,tickfont=t2,showgrid=F,showline=T))
fig1
# Check normality of errors - Q-Q Plot for Life Expectancy
autoplot(lif, which = 2, ncol = 1,colour="cornflowerblue") + theme_bw() + labs(title="Q-Q Plot for Life Expectancy")
# Check for Heteroskedasticity
for (i in 2:length(data.lif)){
col.names <- colnames(data.lif)
p <- qplot(data.lif[[i]], lif$residuals,color =I("cornflowerblue")) +
xlab(col.names[i]) + ylab('Residuals') +
labs(title="Life Expectancy Residual vs. Predictor") + theme_bw()
print(p)
Sys.sleep(2)
}
# Checking for Multicollinearity
vif(lif)
lif <- stepVIF(lif, threshold = 5, verbose = TRUE)
summary(lif)
# Checking for outliers
p <- lif$rank - 1 # Calculate number of predictors in the model
n <- nrow(data.lif) # Number of observations in the dataset
autoplot(lif, which = 4, ncol = 1,colour="cornflowerblue") + theme_bw() + labs(title="Cook's Distance - Life Expectancy")
lif.cooks <- as.vector(cooks.distance(lif))
lif.outl1 <- which(lif.cooks > 1)
lif.outl1
lif.outl2 <- which(lif.cooks > qf(0.50, p + 1, n - p - 1))
lif.outl2
hap <- lm(Response ~ . -c.code, data = data.hap)
summary(hap)
# Check for relationships of variables to response variable (normalized value)
plot(data.hap, pch = 15, col = "blue")
# After checking for model accuracy we have decided to include higher order terms
# in order to generate a better fit of the model
hap <- lm(Response ~ Precipitation.Days + poly(Heating.Days, 4) + poly(Index, 4) + poly(Precipitation.Annual, 4), data = data.hap)
summary(hap)
# Check normality of errors
fig7 <- plot_ly(x=~hap$residuals, type = "histogram",marker=list(color="aquamarine"))
fig7 <- fig7 %>% layout(title="Histogram of Residuals - Happiness Scores",
yaxis=list(title="Frequency",titlefont=t1,tickfont=t2,showgrid=F,showline=T),
xaxis=list(title="Residuals - Happiness Scores",titlefont=t1,tickfont=t2,showgrid=F,showline=T))
fig7
autoplot(hap, which = 2, ncol = 1,colour="aquamarine") + theme_bw() + labs(title="Q-Q Plot for Happiness Scores")
# Check for Heteroskedasticity
for (i in 2:length(data.hap)){
col.names <- colnames(data.hap)
p <- qplot(data.hap[[i]], hap$residuals,color=I("aquamarine")) +
xlab(col.names[i]) + ylab('Residuals') +
labs(title="Happiness Scores Residual vs. Predictor") + theme_bw()
print(p)
Sys.sleep(2)
}
# Checking for Multicollinearity
vif(hap)
hap <- stepVIF(hap, threshold = 5, verbose = TRUE)
summary(hap)
# Checking for outliers
p <- hap$rank - 1 # Calculate number of predictors in the model
n <- nrow(data.hap) # Number of observations in the dataset
autoplot(hap, which = 4, ncol = 1,colour="aquamarine") + theme_bw() + labs(title="Cook's Distance - Happiness")
hap.cooks <- as.vector(cooks.distance(hap))
hap.outl1 <- which(hap.cooks > 1)
hap.outl1 # No such outliers exist
hap.outl2 <- which(hap.cooks > qf(0.50, p + 1, n - p - 1))
hap.outl2 # No such outliers exist
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3J9DQojIEF1dGhvcnM6IFJhaHVsIEJhbW90cmEsIE1heGltaWxpYW4gS2FudHNsZXIsIFJ5YW4gQW5zZXR0LCBBbnNvbiBBYnJhaGFtLCBNYXhpbSBTa2xpYXJvdg0KDQojIEZvciBwcm9wZXIgZXhlY3V0aW9uIG9mIHRoaXMgY29kZSB0aGUgZm9sbG93aW5nIGZpbGVzIG5lZWQgdG8gYmUgbG9jYXRlZCBpbiB0aGUgV29ya2luZyBEaXJlY3RvcnkNCiMNCiMgQk1JIEluZGV4LmNzdg0KIyBoYXBpbmQyMDIwLmNzdg0KIyBISVYtQUlEUy5jc3YNCiMgTWFsYXJpYS5jc3YNCiMgVHViZXJjdWxvc2lzLmNzdg0KDQojIFBhY2thZ2VzIHVzZWQ6IGNvdW50cnljb2RlOyByZWFkeGw7IHdwcDIwMTk7IHdic3RhdHM7IGRwbHlyOyBydmVzdDsgZ2dwbG90MjsgZ2dmb3J0aWZ5OyBjYXI7IHBlZG9tZXRyaWNzOyBwbG90bHkNCg0KbGlicmFyeShjb3VudHJ5Y29kZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeSh3cHAyMDE5KQ0KbGlicmFyeSh3YnN0YXRzKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocnZlc3QpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdnZm9ydGlmeSkNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShwZWRvbWV0cmljcykNCmxpYnJhcnkocGxvdGx5KQ0KDQoNCnJtKGxpc3QgPSBscygpKQ0KDQojIFNjcmFwaW5nIGZyb20gR2xvYmFsIFJlc2lkZW5jZSBXZWJzaXRlDQojIFNjcmFwaW5nIGNvZGUgdGFrZW4gZnJvbTogaHR0cHM6Ly9iZWFudW1iZXIuZ2l0aHViLmlvL3NkczE5Mi9sYWItaW1wb3J0Lmh0bWwNCg0Kc3JjIDwtICJodHRwczovL2dsb2JhbHJlc2lkZW5jZWluZGV4LmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNy8xMC9TVEMtQ2xpbWF0ZS0xOC54bHN4Ig0KbGNsIDwtIGJhc2VuYW1lKHNyYykNCmRvd25sb2FkLmZpbGUodXJsID0gc3JjLCBkZXN0ZmlsZSA9IGxjbCxtb2RlPSd3YicpDQp3ZWF0aGVyIDwtIGFzLmRhdGEuZnJhbWUocmVhZF9leGNlbChsY2wpKQ0KYy5jb2RlPC1jb3VudHJ5Y29kZSh3ZWF0aGVyWywyXSwiY291bnRyeS5uYW1lIiwiaXNvM2MiKQ0Kd2VhdGhlcjwtY2JpbmQod2VhdGhlcixjLmNvZGUpDQoNCnNyYyA8LSAiaHR0cHM6Ly9nbG9iYWxyZXNpZGVuY2VpbmRleC5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTcvMTAvU1RDLUhlYWx0aC1JbmRleC0yMDE4Lnhsc3giDQpsY2wgPC0gYmFzZW5hbWUoc3JjKQ0KZG93bmxvYWQuZmlsZSh1cmwgPSBzcmMsIGRlc3RmaWxlID0gbGNsLG1vZGU9J3diJykNCmhlYWx0aDwtYXMuZGF0YS5mcmFtZShyZWFkX2V4Y2VsKGxjbCkpDQpjLmNvZGU8LWNvdW50cnljb2RlKGhlYWx0aFssMl0sImNvdW50cnkubmFtZSIsImlzbzNjIikNCmhlYWx0aDwtY2JpbmQoaGVhbHRoLGMuY29kZSkNCg0KIyBHZXR0aW5nIExpZmUgRXhwZWN0YW5jeSBmcm9tIFdvcmxkIEJhbmsNCmluZiA8LSB3Yl9zZWFyY2goIkxpZmUgRXhwZWN0YW5jeSIpDQpsaWZlX2V4cDwtd2JfZGF0YShjb3VudHJ5PSJjb3VudHJpZXNfb25seSIsaW5kaWNhdG9yID0gYygiU1AuRFlOLkxFMDAuRkUuSU4iLCJTUC5EWU4uTEUwMC5JTiIsIlNQLkRZTi5MRTAwLk1BLklOIiksc3RhcnRfZGF0ZSA9IDIwMTYsZW5kX2RhdGUgPSAyMDE2KQ0KbGlmZV9leHA8LW5hLmV4Y2x1ZGUobGlmZV9leHApDQpjb2xuYW1lcyhsaWZlX2V4cClbMl0gPC0gImMuY29kZSINCg0KaGFwcGluZXNzPC1yZWFkLmNzdihmaWxlPSdoYXBpbmQyMDIwLmNzdicpDQpoYXBkYXRhPC1oYXBwaW5lc3NbYygxOjMsNyldDQpjLmNvZGU8LWNvdW50cnljb2RlKGhhcGRhdGFbLDFdLCJjb3VudHJ5Lm5hbWUiLCJpc28zYyIpDQpoYXBkYXRhPC1jYmluZChoYXBkYXRhLGMuY29kZSkNCg0Kcm0oYy5jb2RlLGluZixsY2wsc3JjLGhhcHBpbmVzcykNCg0KZGF0YShwb3ApICNDYWxsIGZvciBkYXRhIG9mIHBvcHVsYXRpb24NCiNyZW1vdmUgZGF0YSB0aGF0IGlzIG5vdCByZXF1aXJlZA0Kcm0ocG9wRikNCnJtKHBvcE0pDQpybShwb3BGVCkNCnJtKHBvcE1UKQ0KDQojR2V0IGEgY29sdW1uIG9mIGNvdW50cnkgY29kZSBkYXRhIGluIHRoZSBwb3B1bGF0aW9uIGRhdGEgZnJhbWUNCg0KcG9wJGNvdW50cnkuY29kZSA8LSBjb3VudHJ5Y29kZSgNCiAgcG9wWywyXSwNCiAgImNvdW50cnkubmFtZSIsDQogICJpc28zYyIsDQogIA0KKQ0KDQojRGF0YSBjbGVhbnVwIC0gb25seSBnZXQgdGhlIGNvbHVtbnMgcmVxdWlyZWQgZm9yIHRoZSBjb2RlDQpwb3AgPC0gcG9wWywgYygyLDE2LDE4KV0NCnBvcCA8LSBwb3BbY29tcGxldGUuY2FzZXMocG9wKSxdDQpwb3AgPC0gcG9wWy0xLF0NCmNvbG5hbWVzKHBvcCkgPC0gYygiY291bnRyeS5uYW1lIiwgInBvcHVsYXRpb24iLCJjLmNvZGUiKQ0KDQp2YXJpYWJsZXMgPC0gd2VhdGhlclssYygxNiw0LDYsMTAsMTEsMTMsMTUpXQ0KY29yKHZhcmlhYmxlc1stMV0pDQoNCiMgQ2hlY2sgZm9yIGNvcnJlbGF0aW9ucyBhbmQgcGF0dGVybnMgd2l0aGluIHRoZSB2YXJpYWJsZXMgKHNhbWUgZm9yIGV2ZXJ5IHJlZ3Jlc3Npb24gc2V0KQ0KcGxvdCh2YXJpYWJsZXMsIHBjaCA9IDE1LCBjb2wgPSAiYmx1ZSIpDQoNCiMgTWVyZ2luZyB0aGUgdmFyaWFibGVzIHdpdGggdGhlIHJlc3BvbnNlIHZhcmlhYmxlcw0KZGF0YS5saWYgPC0gcmlnaHRfam9pbihsaWZlX2V4cFssYygyLDYpXSwgdmFyaWFibGVzLCBieSA9ImMuY29kZSIpDQpkYXRhLmxpZiA8LSBkYXRhLmxpZltjb21wbGV0ZS5jYXNlcyhkYXRhLmxpZiksXQ0KYXR0cihkYXRhLmxpZiRTUC5EWU4uTEUwMC5JTiwgImxhYmVsIikgPC0gTlVMTCAjIFJlbW92aW5nIGF0dHJpYnV0ZSBmcm9tIGNvbHVtbiBuYW1lDQpjb2xuYW1lcyhkYXRhLmxpZikgPC0gYygiYy5jb2RlIiwgIlJlc3BvbnNlIiwgIkluZGV4IiwgIkRheXMzMiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIlByZWNpcGl0YXRpb24uQW5udWFsIiwgIlByZWNpcGl0YXRpb24uRGF5cyIsDQogICAgICAgICAgICAgICAgICAgICAgICAiSGVhdGluZy5EYXlzIiwgIlN1bnNoaW5lLkhvdXJzIikNCmRhdGEuaGFwIDwtIHJpZ2h0X2pvaW4oaGFwZGF0YVssYyg1LDMpXSwgdmFyaWFibGVzLCBieSA9ImMuY29kZSIpDQpkYXRhLmhhcCA8LSBkYXRhLmhhcFtjb21wbGV0ZS5jYXNlcyhkYXRhLmhhcCksXQ0KY29sbmFtZXMoZGF0YS5oYXApIDwtIGMoImMuY29kZSIsICJSZXNwb25zZSIsICJJbmRleCIsICJEYXlzMzIiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICJQcmVjaXBpdGF0aW9uLkFubnVhbCIsICJQcmVjaXBpdGF0aW9uLkRheXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIkhlYXRpbmcuRGF5cyIsICJTdW5zaGluZS5Ib3VycyIpDQpsaWYgPC0gbG0oUmVzcG9uc2UgfiAuIC1jLmNvZGUsIGRhdGEgPSBkYXRhLmxpZikgIyBQcm9ibGVtIHdpdGggY291bnRyeSBjb2Rlcw0Kc3VtbWFyeShsaWYpDQoNCiMgQ2hlY2sgZm9yIHJlbGF0aW9uc2hpcHMgb2YgdmFyaWFibGVzIHRvIHJlc3BvbnNlIHZhcmlhYmxlIChub3JtYWxpemVkIHZhbHVlKQ0KcGxvdChkYXRhLmxpZiwgcGNoID0gMTUsIGNvbCA9ICJibHVlIikNCg0KIyBBZnRlciBjaGVja2luZyBmb3IgbW9kZWwgYWNjdXJhY3kgd2UgZGVjaWRlZCB0byBpbmNsdWRlIGhpZ2hlciBvcmRlciB0ZXJtcyBvZiB2YXJpYWJsZXMgdG8gaW1wcm92ZSB0aGUgbW9kZWwNCmxpZiA8LSBsbShSZXNwb25zZSB+IFByZWNpcGl0YXRpb24uRGF5cyArIHBvbHkoSGVhdGluZy5EYXlzLCA0KSArIHBvbHkoSW5kZXgsIDQpICsgcG9seShQcmVjaXBpdGF0aW9uLkFubnVhbCwgNCksIGRhdGEgPSBkYXRhLmxpZikNCnN1bW1hcnkobGlmKQ0KDQpwcmVkaWN0X2RhdGEubGlmIDwtIGRhdGEuZnJhbWUoZGF0YS5saWZbLC1jKDIpXSkNCnByZWRpY3RfZGF0YS5saWYkcHJlZGljdF9yZXNwb25lIDwtIHByZWRpY3QubG0obGlmLHByZWRpY3RfZGF0YS5saWYpDQoNCg0KDQp0MSA8LSBsaXN0KGZhbWlseSA9ICJBcmlhbCwgc2Fucy1zZXJpZiIsIHNpemUgPSAxMywgY29sb3IgPSAiYmxhY2siKQ0KdDIgPC0gbGlzdChmYW1pbHkgPSAiQXJpYWwsIHNhbnMtc2VyaWYiLCBzaXplID0gMTIsIGNvbG9yID0gImJsYWNrIikNCg0KbGluZV80NSA8LSBkYXRhLmZyYW1lKCJQcmVkaWN0aW9uIj0gYyg3MCw4MiksIkFjdHVhbCI9Yyg3MCw4MikpDQoNCg0KY29tcGFyZV9kZiA8LSBkYXRhLmZyYW1lKHByZWRpY3RfZGF0YS5saWYkcHJlZGljdF9yZXNwb25lLGRhdGEubGlmJFJlc3BvbnNlKQ0KY29sbmFtZXMoY29tcGFyZV9kZikgPC0gYygiUHJlZGljdGlvbiIsIkFjdHVhbCIpDQpmaWcgPC0gcGxvdF9seShjb21wYXJlX2RmKQ0KZmlnIDwtIGZpZyAlPiUgYWRkX3RyYWNlKHg9fkFjdHVhbCx5PX5QcmVkaWN0aW9uLHR5cGU9InNjYXR0ZXIiLG5hbWU9IkxpZmUgRXhwZWN0YW5jeSBQcmVkaWN0aW9uIixtYXJrZXIgPSBsaXN0KGNvbG9yID0gJ2Nvcm5mbG93ZXJibHVlJykpDQpmaWcgPC0gZmlnICU+JSBhZGRfdHJhY2UoeD1saW5lXzQ1JFByZWRpY3Rpb24seT1saW5lXzQ1JEFjdHVhbCxuYW1lPSIxOjEgNDXCsExpbmUiLHR5cGU9InNjYXR0ZXIiLG1vZGU9ImxpbmUiLGxpbmU9bGlzdCh3aWR0aD0zLGRhc2g9J2Rhc2gnLGNvbG9yPSdyZWQnKSkNCmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZT0iTGlmZSBFeHBlY3RhbmN5IENvbXBhcmlzb24iLA0KICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdQcmVkaWN0ZWQgTGlmZSBFeHBlY3RhbmN5Jyx0aXRsZWZvbnQ9dDEsdGlja2ZvbnQ9dDIsc2hvd2dyaWQ9RixzaG93bGluZSA9IFQscmFuZ2U9Yyg2NSw4NSkpLCANCiAgICAgICAgICAgICAgICAgICAgICB4YXhpcz1saXN0KGF1dG90aWNrPUZBTFNFLCBkdGljaz01LHRpdGxlPSJBY3R1YWwgTGlmZSBFeHBlY3RhbmN5Iix0aXRsZWZvbnQ9dDEsdGlja2ZvbnQ9dDIsc2hvd2dyaWQ9RixzaG93bGluZSA9IFQscmFuZ2U9Yyg2NSw4NSkpLA0KICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZCA9IGxpc3Qob3JpZW50YXRpb24gPSAndicseD0wLjEseT0wLjgpLA0KICAgICAgICAgICAgICAgICAgICAgIHNob3dsZWdlbmQ9VCkNCmZpZw0KDQojIENoZWNrIG5vcm1hbGl0eSBvZiBlcnJvcnMgLSBIaXN0b2dyYW0gZm9yIExpZmUgRXhwZWN0YW5jeQ0KDQpmaWcxIDwtIHBsb3RfbHkoeD1+bGlmJHJlc2lkdWFscywgdHlwZSA9ICJoaXN0b2dyYW0iLG1hcmtlcj1saXN0KGNvbG9yPSJjb3JuZmxvd2VyYmx1ZSIpKQ0KZmlnMSA8LSBmaWcxICU+JSBsYXlvdXQodGl0bGU9Ikhpc3RvZ3JhbSBvZiBSZXNpZHVhbHMgLSBMaWZlIEV4cGVjdGFuY3kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXM9bGlzdCh0aXRsZT0iRnJlcXVlbmN5Iix0aXRsZWZvbnQ9dDEsdGlja2ZvbnQ9dDIsc2hvd2dyaWQ9RixzaG93bGluZT1UKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzPWxpc3QodGl0bGU9IlJlc2lkdWFscyAtIExpZmUgRXhwZWN0YW5jeSIsdGl0bGVmb250PXQxLHRpY2tmb250PXQyLHNob3dncmlkPUYsc2hvd2xpbmU9VCkpDQpmaWcxDQojIENoZWNrIG5vcm1hbGl0eSBvZiBlcnJvcnMgLSBRLVEgUGxvdCBmb3IgTGlmZSBFeHBlY3RhbmN5DQphdXRvcGxvdChsaWYsIHdoaWNoID0gMiwgbmNvbCA9IDEsY29sb3VyPSJjb3JuZmxvd2VyYmx1ZSIpICsgdGhlbWVfYncoKSArIGxhYnModGl0bGU9IlEtUSBQbG90IGZvciBMaWZlIEV4cGVjdGFuY3kiKQ0KDQojIENoZWNrIGZvciBIZXRlcm9za2VkYXN0aWNpdHkNCmZvciAoaSBpbiAyOmxlbmd0aChkYXRhLmxpZikpew0KICBjb2wubmFtZXMgPC0gY29sbmFtZXMoZGF0YS5saWYpDQogIHAgPC0gcXBsb3QoZGF0YS5saWZbW2ldXSwgbGlmJHJlc2lkdWFscyxjb2xvciA9SSgiY29ybmZsb3dlcmJsdWUiKSkgKw0KICAgIHhsYWIoY29sLm5hbWVzW2ldKSArIHlsYWIoJ1Jlc2lkdWFscycpICsgDQogICAgbGFicyh0aXRsZT0iTGlmZSBFeHBlY3RhbmN5IFJlc2lkdWFsIHZzLiBQcmVkaWN0b3IiKSArIHRoZW1lX2J3KCkgDQogIHByaW50KHApDQogIFN5cy5zbGVlcCgyKQ0KfQ0KDQojIENoZWNraW5nIGZvciBNdWx0aWNvbGxpbmVhcml0eQ0KdmlmKGxpZikNCmxpZiA8LSBzdGVwVklGKGxpZiwgdGhyZXNob2xkID0gNSwgdmVyYm9zZSAgPSBUUlVFKQ0Kc3VtbWFyeShsaWYpDQoNCiMgQ2hlY2tpbmcgZm9yIG91dGxpZXJzDQpwIDwtIGxpZiRyYW5rIC0gMSAjIENhbGN1bGF0ZSBudW1iZXIgb2YgcHJlZGljdG9ycyBpbiB0aGUgbW9kZWwNCm4gPC0gbnJvdyhkYXRhLmxpZikgIyBOdW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhc2V0DQphdXRvcGxvdChsaWYsIHdoaWNoID0gNCwgbmNvbCA9IDEsY29sb3VyPSJjb3JuZmxvd2VyYmx1ZSIpICsgdGhlbWVfYncoKSArIGxhYnModGl0bGU9IkNvb2sncyBEaXN0YW5jZSAtIExpZmUgRXhwZWN0YW5jeSIpIA0KbGlmLmNvb2tzIDwtICBhcy52ZWN0b3IoY29va3MuZGlzdGFuY2UobGlmKSkNCmxpZi5vdXRsMSA8LSB3aGljaChsaWYuY29va3MgPiAxKQ0KbGlmLm91dGwxDQpsaWYub3V0bDIgPC0gd2hpY2gobGlmLmNvb2tzID4gcWYoMC41MCwgcCArIDEsIG4gLSBwIC0gMSkpDQpsaWYub3V0bDINCg0KaGFwIDwtIGxtKFJlc3BvbnNlIH4gLiAtYy5jb2RlLCBkYXRhID0gZGF0YS5oYXApDQpzdW1tYXJ5KGhhcCkNCg0KIyBDaGVjayBmb3IgcmVsYXRpb25zaGlwcyBvZiB2YXJpYWJsZXMgdG8gcmVzcG9uc2UgdmFyaWFibGUgKG5vcm1hbGl6ZWQgdmFsdWUpDQpwbG90KGRhdGEuaGFwLCBwY2ggPSAxNSwgY29sID0gImJsdWUiKQ0KDQojIEFmdGVyIGNoZWNraW5nIGZvciBtb2RlbCBhY2N1cmFjeSB3ZSBoYXZlIGRlY2lkZWQgdG8gaW5jbHVkZSBoaWdoZXIgb3JkZXIgdGVybXMNCiMgaW4gb3JkZXIgdG8gZ2VuZXJhdGUgYSBiZXR0ZXIgZml0IG9mIHRoZSBtb2RlbA0KaGFwIDwtIGxtKFJlc3BvbnNlIH4gUHJlY2lwaXRhdGlvbi5EYXlzICsgcG9seShIZWF0aW5nLkRheXMsIDQpICsgcG9seShJbmRleCwgNCkgKyBwb2x5KFByZWNpcGl0YXRpb24uQW5udWFsLCA0KSwgZGF0YSA9IGRhdGEuaGFwKQ0Kc3VtbWFyeShoYXApDQoNCg0KIyBDaGVjayBub3JtYWxpdHkgb2YgZXJyb3JzDQpmaWc3IDwtIHBsb3RfbHkoeD1+aGFwJHJlc2lkdWFscywgdHlwZSA9ICJoaXN0b2dyYW0iLG1hcmtlcj1saXN0KGNvbG9yPSJhcXVhbWFyaW5lIikpDQpmaWc3IDwtIGZpZzcgJT4lIGxheW91dCh0aXRsZT0iSGlzdG9ncmFtIG9mIFJlc2lkdWFscyAtIEhhcHBpbmVzcyBTY29yZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXM9bGlzdCh0aXRsZT0iRnJlcXVlbmN5Iix0aXRsZWZvbnQ9dDEsdGlja2ZvbnQ9dDIsc2hvd2dyaWQ9RixzaG93bGluZT1UKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzPWxpc3QodGl0bGU9IlJlc2lkdWFscyAtIEhhcHBpbmVzcyBTY29yZXMiLHRpdGxlZm9udD10MSx0aWNrZm9udD10MixzaG93Z3JpZD1GLHNob3dsaW5lPVQpKQ0KZmlnNw0KDQphdXRvcGxvdChoYXAsIHdoaWNoID0gMiwgbmNvbCA9IDEsY29sb3VyPSJhcXVhbWFyaW5lIikgKyB0aGVtZV9idygpICsgbGFicyh0aXRsZT0iUS1RIFBsb3QgZm9yIEhhcHBpbmVzcyBTY29yZXMiKQ0KDQojIENoZWNrIGZvciBIZXRlcm9za2VkYXN0aWNpdHkNCmZvciAoaSBpbiAyOmxlbmd0aChkYXRhLmhhcCkpew0KICBjb2wubmFtZXMgPC0gY29sbmFtZXMoZGF0YS5oYXApDQogIHAgPC0gcXBsb3QoZGF0YS5oYXBbW2ldXSwgaGFwJHJlc2lkdWFscyxjb2xvcj1JKCJhcXVhbWFyaW5lIikpICsNCiAgICB4bGFiKGNvbC5uYW1lc1tpXSkgKyB5bGFiKCdSZXNpZHVhbHMnKSArDQogICAgbGFicyh0aXRsZT0iSGFwcGluZXNzIFNjb3JlcyBSZXNpZHVhbCB2cy4gUHJlZGljdG9yIikgKyB0aGVtZV9idygpDQogIHByaW50KHApDQogIFN5cy5zbGVlcCgyKQ0KfQ0KDQojIENoZWNraW5nIGZvciBNdWx0aWNvbGxpbmVhcml0eQ0KdmlmKGhhcCkNCmhhcCA8LSBzdGVwVklGKGhhcCwgdGhyZXNob2xkID0gNSwgdmVyYm9zZSAgPSBUUlVFKQ0Kc3VtbWFyeShoYXApDQoNCiMgQ2hlY2tpbmcgZm9yIG91dGxpZXJzDQpwIDwtIGhhcCRyYW5rIC0gMSAjIENhbGN1bGF0ZSBudW1iZXIgb2YgcHJlZGljdG9ycyBpbiB0aGUgbW9kZWwNCm4gPC0gbnJvdyhkYXRhLmhhcCkgIyBOdW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhc2V0DQphdXRvcGxvdChoYXAsIHdoaWNoID0gNCwgbmNvbCA9IDEsY29sb3VyPSJhcXVhbWFyaW5lIikgKyB0aGVtZV9idygpICsgbGFicyh0aXRsZT0iQ29vaydzIERpc3RhbmNlIC0gSGFwcGluZXNzIikNCmhhcC5jb29rcyA8LSAgYXMudmVjdG9yKGNvb2tzLmRpc3RhbmNlKGhhcCkpDQpoYXAub3V0bDEgPC0gd2hpY2goaGFwLmNvb2tzID4gMSkNCmhhcC5vdXRsMSAjIE5vIHN1Y2ggb3V0bGllcnMgZXhpc3QNCmhhcC5vdXRsMiA8LSB3aGljaChoYXAuY29va3MgPiBxZigwLjUwLCBwICsgMSwgbiAtIHAgLSAxKSkNCmhhcC5vdXRsMiAjIE5vIHN1Y2ggb3V0bGllcnMgZXhpc3QNCg0KDQoNCmBgYA0KDQo=