#1. Define a binary outcome of your choosing
self rated health status
2) Fit a predictive logistic regression model using as many predictor variables as you think you need
data$age2<- data$age^2
library(dplyr)
model.data<- data %>%
select(serial, badhealth, opportunity_youth_cat, urban_rural, male, educ, age2)
knitr::kable(head(model.data))
22 |
0 |
Not opportunity youth |
urban |
Male |
1Less than HS |
529 |
73 |
0 |
Not opportunity youth |
urban |
Female |
3More than HS |
400 |
81 |
0 |
Not opportunity youth |
urban |
Female |
2hsgrad |
484 |
82 |
0 |
Not opportunity youth |
urban |
Female |
3More than HS |
441 |
88 |
0 |
Not opportunity youth |
urban |
Male |
3More than HS |
484 |
136 |
0 |
Not opportunity youth |
urban |
Female |
3More than HS |
361 |
3) Use a 80% training/20% test split for your data
set.seed(1115)
train<- createDataPartition(y = model.data$badhealth,
p = .80,
list=F)
model.dat2train<-model.data[train,]
model.dat2test<-model.data[-train,]
table(model.dat2train$badhealth)
##
## 0 1
## 2848 162
prop.table(table(model.dat2train$badhealth))
##
## 0 1
## 0.9461794 0.0538206
summary(model.dat2train)
## serial badhealth opportunity_youth_cat urban_rural
## Min. : 22 Min. :0.00000 Opportunity youth : 312 rural: 374
## 1st Qu.: 8486 1st Qu.:0.00000 Not opportunity youth:2698 urban:2636
## Median :16876 Median :0.00000
## Mean :16646 Mean :0.05382
## 3rd Qu.:24905 3rd Qu.:0.00000
## Max. :33099 Max. :1.00000
## male educ age2
## Female:1514 1Less than HS: 295 Min. :324.0
## Male :1496 2hsgrad :1000 1st Qu.:400.0
## 3More than HS:1715 Median :441.0
## Mean :458.7
## 3rd Qu.:529.0
## Max. :576.0
Logistic regression for classification
glm1<-glm(badhealth~factor(opportunity_youth_cat)+factor(urban_rural)+scale(age2)+factor(male)+factor(educ),
data=model.dat2train[,-1],
family = binomial)
summary(glm1)
##
## Call:
## glm(formula = badhealth ~ factor(opportunity_youth_cat) + factor(urban_rural) +
## scale(age2) + factor(male) + factor(educ), family = binomial,
## data = model.dat2train[, -1])
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -0.8531 -0.3388 -0.2971 -0.2622 2.7703
##
## Coefficients:
## Estimate Std. Error z value
## (Intercept) -0.98218 0.29381 -3.343
## factor(opportunity_youth_cat)Not opportunity youth -0.93572 0.20295 -4.611
## factor(urban_rural)urban -0.35696 0.21904 -1.630
## scale(age2) 0.18906 0.08664 2.182
## factor(male)Male -0.41848 0.16714 -2.504
## factor(educ)2hsgrad -0.54191 0.24907 -2.176
## factor(educ)3More than HS -0.81846 0.25567 -3.201
## Pr(>|z|)
## (Intercept) 0.000829 ***
## factor(opportunity_youth_cat)Not opportunity youth 4.01e-06 ***
## factor(urban_rural)urban 0.103168
## scale(age2) 0.029107 *
## factor(male)Male 0.012289 *
## factor(educ)2hsgrad 0.029576 *
## factor(educ)3More than HS 0.001369 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 1261.9 on 3009 degrees of freedom
## Residual deviance: 1212.1 on 3003 degrees of freedom
## AIC: 1226.1
##
## Number of Fisher Scoring iterations: 6
tr_pred<- predict(glm1,
newdata = model.dat2train,
type = "response")
head(tr_pred)
## 1 2 3 4 5 6
## 0.07345859 0.05954407 0.03062989 0.02547889 0.07228006 0.02547889
Using 50% as the predictor
tr_predcl<-factor(ifelse(tr_pred>.5, 1, 0))
library(ggplot2)
pred1<-data.frame(pr=tr_pred,
gr=tr_predcl,
badht=model.dat2train$badhealth)
pred1%>%
ggplot()+
geom_histogram(aes(x=pr, color=gr, fill=gr))+
ggtitle(label = "Probability of Bad health",
subtitle = "Threshold = .5")+
geom_vline(xintercept=.5)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

pred1%>%
ggplot()+
geom_histogram(aes(x=pr, color=badht, fill=badht))+
ggtitle(label = "Probability of Bad health",
subtitle = "Truth")+
geom_vline(xintercept=.5)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

table( tr_predcl,
model.dat2train$badhealth)
##
## tr_predcl 0 1
## 0 2848 162
model.dat2train$badhealth<- as.factor(model.dat2train$badhealth)
cm1<-confusionMatrix(data = tr_predcl,
reference = model.dat2train$badhealth)
## Warning in confusionMatrix.default(data = tr_predcl, reference =
## model.dat2train$badhealth): Levels are not in the same order for reference and
## data. Refactoring data to match.
cm1
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 2848 162
## 1 0 0
##
## Accuracy : 0.9462
## 95% CI : (0.9375, 0.954)
## No Information Rate : 0.9462
## P-Value [Acc > NIR] : 0.5209
##
## Kappa : 0
##
## Mcnemar's Test P-Value : <2e-16
##
## Sensitivity : 1.0000
## Specificity : 0.0000
## Pos Pred Value : 0.9462
## Neg Pred Value : NaN
## Prevalence : 0.9462
## Detection Rate : 0.9462
## Detection Prevalence : 1.0000
## Balanced Accuracy : 0.5000
##
## 'Positive' Class : 0
##
3) Report the % correct classification from the training data using the .5 decision rule
Overall the model has a 94.62% accuracy.
Using mean as a predictor
tr_predcl<-factor(ifelse(tr_pred>mean(I(model.dat2train$badhealth==1)), 1, 0)) #mean of response
pred2<-data.frame(pr=tr_pred,
gr=tr_predcl,
badht=model.dat2train$badhealth)
pred2%>%
ggplot(aes(x=pr, fill=gr))+
geom_histogram(position="identity",
alpha=.2)+
ggtitle(label = "Probability of Bad health",
subtitle = "Threshold = Mean")+
geom_vline(xintercept=mean(I(model.dat2train$badhealth==1)))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

pred2%>%
ggplot(aes(x=pr, fill=badht))+
geom_histogram(position="identity",
alpha=.2)+
ggtitle(label = "Probability of Bad health",
subtitle = "Truth")+
geom_vline(xintercept=mean(I(model.dat2train$badhealth==1)))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

table( tr_predcl,
model.dat2train$badhealth)
##
## tr_predcl 0 1
## 0 1995 81
## 1 853 81
model.dat2train$badhealth<- as.factor(model.dat2train$badhealth)
confusionMatrix(data = tr_predcl,
model.dat2train$badhealth,
positive = "1" )
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 1995 81
## 1 853 81
##
## Accuracy : 0.6897
## 95% CI : (0.6728, 0.7062)
## No Information Rate : 0.9462
## P-Value [Acc > NIR] : 1
##
## Kappa : 0.0617
##
## Mcnemar's Test P-Value : <2e-16
##
## Sensitivity : 0.50000
## Specificity : 0.70049
## Pos Pred Value : 0.08672
## Neg Pred Value : 0.96098
## Prevalence : 0.05382
## Detection Rate : 0.02691
## Detection Prevalence : 0.31030
## Balanced Accuracy : 0.60025
##
## 'Positive' Class : 1
##
3) Report the % correct classification from the training data using the mean as the decision rule
answer: Overral, from the training data, the model has a 68.97% accuracy
3a) Does changing the decision rule threshold affect your classification accuracy?
Answer: Yes, using the .5 the classification accuracy is 94.62%, while using the mean the accuracy reduced to 68.97%. Even though the mean accuracy is lesser, the .5 classification did not account for false positive and true negative.
Testing data using mean
pred_test<-predict(glm1,
newdata=model.dat2test,
type="response")
pred_cl<-factor(ifelse(pred_test > mean( I(model.dat2test$badhealth==1)), 1, 0))
table(model.dat2test$badhealth,pred_cl)
## pred_cl
## 0 1
## 0 532 178
## 1 25 17
model.dat2test$badhealth<- as.factor(model.dat2test$badhealth)
confusionMatrix(data = pred_cl,model.dat2test$badhealth)
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 532 25
## 1 178 17
##
## Accuracy : 0.7301
## 95% CI : (0.6968, 0.7615)
## No Information Rate : 0.9441
## P-Value [Acc > NIR] : 1
##
## Kappa : 0.0568
##
## Mcnemar's Test P-Value : <2e-16
##
## Sensitivity : 0.74930
## Specificity : 0.40476
## Pos Pred Value : 0.95512
## Neg Pred Value : 0.08718
## Prevalence : 0.94415
## Detection Rate : 0.70745
## Detection Prevalence : 0.74069
## Balanced Accuracy : 0.57703
##
## 'Positive' Class : 0
##
4. Report the % correct classification from the test data using the mean as the decision rule
The Overral, from the testing data, the model has a 73.01% accuracy
Testing data using 0.5
pred_test2<-predict(glm1,
newdata=model.dat2test,
type="response")
pred_cl<-factor(ifelse(pred_test >.5, 1, 0))
table(model.dat2test$badhealth,pred_cl)
## pred_cl
## 0
## 0 710
## 1 42
model.dat2test$badhealth<- as.factor(model.dat2test$badhealth)
confusionMatrix(data = pred_cl,model.dat2test$badhealth)
## Warning in confusionMatrix.default(data = pred_cl, model.dat2test$badhealth):
## Levels are not in the same order for reference and data. Refactoring data to
## match.
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 710 42
## 1 0 0
##
## Accuracy : 0.9441
## 95% CI : (0.9253, 0.9595)
## No Information Rate : 0.9441
## P-Value [Acc > NIR] : 0.5409
##
## Kappa : 0
##
## Mcnemar's Test P-Value : 2.509e-10
##
## Sensitivity : 1.0000
## Specificity : 0.0000
## Pos Pred Value : 0.9441
## Neg Pred Value : NaN
## Prevalence : 0.9441
## Detection Rate : 0.9441
## Detection Prevalence : 1.0000
## Balanced Accuracy : 0.5000
##
## 'Positive' Class : 0
##
4 Report the % correct classification from the test data using the .5 decision rule
answer: The percentage correct classification is 94.41 % accurate. Still did not account for the false positive and true negative
LS0tCnRpdGxlOiAiQXNzaWdubWVudCA0IgphdXRob3I6ICJKb3NlcGggSmFpeWVvbGEiCmRhdGU6ICAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDoKICAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgojMS4gRGVmaW5lIGEgYmluYXJ5IG91dGNvbWUgb2YgeW91ciBjaG9vc2luZwoKc2VsZiByYXRlZCBoZWFsdGggc3RhdHVzCgoKCgoKCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KHN0YXJnYXplciwgcXVpZXRseSA9IFQpCmxpYnJhcnkoc3VydmV5LCBxdWlldGx5ID0gVCkKbGlicmFyeShjYXIsIHF1aWV0bHkgPSBUKQpsaWJyYXJ5KHF1ZXN0aW9uciwgcXVpZXRseSA9IFQpCmxpYnJhcnkoZHBseXIsIHF1aWV0bHkgPSBUKQpsaWJyYXJ5KGZvcmNhdHMsIHF1aWV0bHkgPSBUKQpsaWJyYXJ5KHRpZHl2ZXJzZSwgcXVpZXRseSA9IFQpCmxpYnJhcnkoc3J2eXIsIHF1aWV0bHkgPSBUKQpsaWJyYXJ5KCBndHN1bW1hcnksIHF1aWV0bHkgPSBUKQpsaWJyYXJ5KGNhcmV0LCBxdWlldGx5ID0gVCkKCmBgYAoKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoaXB1bXNyKQpgYGAKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmRkaSA8LSByZWFkX2lwdW1zX2RkaSgibmhpc18wMDAwMi54bWwiKQpkYXRhIDwtIHJlYWRfaXB1bXNfbWljcm8oZGRpKQpkYXRhPC0gaGF2ZW46OnphcF9sYWJlbHMoZGF0YSkKYGBgCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpuYW1lcyhkYXRhKSA8LSB0b2xvd2VyKGdzdWIocGF0dGVybiA9ICJfIixyZXBsYWNlbWVudCA9ICAiIix4ID0gIG5hbWVzKGRhdGEpKSkKYGBgCgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KI3NleApkYXRhJG1hbGU8LWFzLmZhY3RvcihpZmVsc2UoZGF0YSRzZXg9PTEsICJNYWxlIiwgIkZlbWFsZSIpKQoKCiNyYWNlL2V0aG5pY2l0eQpkYXRhJHdoaXRlbWFqb3JpdHk8LSBjYXI6OlJlY29kZShkYXRhJGhpc3ByYWNlLAogICAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9IjAyPTE7IDk5PU5BOyBlbHNlPTAiKQoKZGF0YSRvdGhlcm1pbm9yaXR5PC0gY2FyOjpSZWNvZGUoZGF0YSRoaXNwcmFjZSwKICAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9ImMoMSwzLDQsNSw2LDcpPTE7IDk5PU5BOyBlbHNlPTAiKQoKZGF0YSRyYWNlX2V0aDwtY2FyOjpSZWNvZGUoZGF0YSRoaXNwcmFjZSwKcmVjb2Rlcz0iMDI9J3doaXRlbWFqb3JpdHknOyBjKDEsMyw0LDUsNiw3KT0nb3RoZXJtaW5vcml0eSc7ZWxzZT1OQSIsCmFzLmZhY3RvciA9IFQpCmRhdGEkcmFjZV9ldGg8LXJlbGV2ZWwoZGF0YSRyYWNlX2V0aCwKICAgICAgICAgICAgICAgICAgICAgICAgICByZWYgPSAid2hpdGVtYWpvcml0eSIpCgoKI2VkdWNhdGlvbiBsZXZlbAoKCgpkYXRhJGVkdWM8LSBjYXI6OlJlY29kZShkYXRhJGVkdWMsCiAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9IjEwMjoxMTY9JzFMZXNzIHRoYW4gSFMnOyAyMDE6MjAyPScyaHNncmFkJzsgMzAxOjUwMz0nM01vcmUgdGhhbiBIUyc7OTk3Ojk5OT1OQTswMDA9TkEiLAogICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3I9VCkKZGF0YSRlZHVjPC1mY3RfcmVsZXZlbChkYXRhJGVkdWMsJzFMZXNzIHRoYW4gSFMnLCcyaHNncmFkJywnM01vcmUgdGhhbiBIUycpIAoKCiNVcmJhbi1ydXJhbCBjbGFzc2lmaWNhdGlvbgoKCmRhdGEkdXJiYW5fcnVyYWw8LSBjYXI6OlJlY29kZShkYXRhJHVyYnJybCwKICAgICAgICAgICAgICAgICAgICAgcmVjb2Rlcz0iMTozPSd1cmJhbic7IDQ9J3J1cmFsJzswMDA9TkEiLAogICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3I9VCkKZGF0YSR1cmJhbl9ydXJhbDwtcmVsZXZlbChkYXRhJHVyYmFuX3J1cmFsLCByZWY9J3J1cmFsJykgCgoKZGF0YSRydXJhbDwtIGNhcjo6UmVjb2RlKGRhdGEkaGlzcHJhY2UsCiAgICAgICAgICAgICAgICAgICAgICAgcmVjb2Rlcz0iND0xOyA5OT1OQTsgZWxzZT0wIikKCmRhdGEkdXJiYW48LSBjYXI6OlJlY29kZShkYXRhJGhpc3ByYWNlLAogICAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9IjE6Mz0xOyA5OT1OQTsgZWxzZT0wIikKCgoKCiNlbXBsb3ltZW50IHN0YXR1cwoKCmRhdGEkdW5lbXBsb3k8LSBjYXI6OlJlY29kZShkYXRhJGVtcHN0YXQsCiAgICAgICAgICAgICAgICAgICAgICAgcmVjb2Rlcz0iMjAwPTE7IDAwPU5BOyA5OTk9TkE7IGVsc2U9MCIpCgoKZGF0YSRlbXBsb3lfc3RhdHVzPC0gY2FyOjpSZWNvZGUoZGF0YSRlbXBzdGF0LAogICAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9IjEwMD0nRW1wbG95ZWQnOyAyMDA9J3VuZW1wbG95ZWQnO2Vsc2U9TkEiLAogICAgICAgICAgICAgICAgICAgICAgIGFzLmZhY3Rvcj1UKQpkYXRhJGVtcGxveV9zdGF0dXM8LXJlbGV2ZWwoZGF0YSRlbXBsb3lfc3RhdHVzLCByZWY9J0VtcGxveWVkJykKCgojIGN1cnJlbnRseSBpbiBzY2hvb2wKCmRhdGEkbm9uX3NjaG9vbGluZzwtIGNhcjo6UmVjb2RlKGRhdGEkc2Nob29sbm93LAogICAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9IjE9MTsgMD1OQTsgNzo5PU5BOyBlbHNlPTAiKQoKZGF0YSRzY2hvb2xzdGF0dXM8LSBjYXI6OlJlY29kZShkYXRhJHNjaG9vbG5vdywKICAgICAgICAgICAgICAgICAgICAgICByZWNvZGVzPSIxPSdubyc7IDI9J3llcyc7ZWxzZT1OQSIsCiAgICAgICAgICAgICAgICAgICAgICAgYXMuZmFjdG9yPVQpCmRhdGEkc2Nob29sc3RhdHVzPC1yZWxldmVsKGRhdGEkc2Nob29sc3RhdHVzLCByZWY9J25vJykKCmRhdGE8LWRhdGElPiUKICBmaWx0ZXIoY29tcGxldGUuY2FzZXModW5lbXBsb3ksbm9uX3NjaG9vbGluZykpCgoKIyBtZXJnaW5nIHNjaG9vbGluZyBhbmQgd29ya2luZwoKZGF0YSRvcHBvcnR1bml0eV95b3V0aCA8LSBwYXN0ZSggZGF0YSR1bmVtcGxveSwgZGF0YSRub25fc2Nob29saW5nLCBzZXAgPSIiKQoKCmRhdGEkb3Bwb3J0dW5pdHlfeW91dGhfY2F0X251bTwtIGNhcjo6UmVjb2RlKGRhdGEkb3Bwb3J0dW5pdHlfeW91dGgsCiAgICAgICAgICAgICAgICAgICAgICAgcmVjb2Rlcz0iMTE9MTsgMDA6MTA9MDtlbHNlPU5BIiwKICAgICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3I9RikKCgpkYXRhJG9wcG9ydHVuaXR5X3lvdXRoX2NhdDwtIGNhcjo6UmVjb2RlKGRhdGEkb3Bwb3J0dW5pdHlfeW91dGgsCiAgICAgICAgICAgICAgICAgICAgICAgcmVjb2Rlcz0iMTE9J09wcG9ydHVuaXR5IHlvdXRoJzswMDoxMD0nTm90IG9wcG9ydHVuaXR5IHlvdXRoJztlbHNlPU5BIiwKICAgICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3I9VCkKZGF0YSRvcHBvcnR1bml0eV95b3V0aF9jYXQ8LXJlbGV2ZWwoZGF0YSRvcHBvcnR1bml0eV95b3V0aF9jYXQsIHJlZj0nT3Bwb3J0dW5pdHkgeW91dGgnKQoKZGF0YSRub25fb3Bwb3J0dW5pdHlfeW91dGhfY2F0X251bTwtIGNhcjo6UmVjb2RlKGRhdGEkb3Bwb3J0dW5pdHlfeW91dGhfY2F0X251bSwKICAgICAgICAgICAgICAgICAgICAgICByZWNvZGVzPSIwPTE7IDk5PU5BOyBlbHNlPTAiKQoKI2luY29tZSBncm91cGluZwoKZGF0YSRmYW1pbHlpbmNvbWUgPC0gZGF0YSRpbmNmYW0wN29uCgojIGJvcm4gaW4gdGhlIFVTCgpkYXRhJHVzYm9ybjwtIGNhcjo6UmVjb2RlKGRhdGEkdXNib3JuLAogICAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9IjIwPTE7IDk3Ojk4PU5BOyBlbHNlPTAiKQoKIyBVUyBDaXRpemVuCmRhdGEkY2l0aXplbjwtIGNhcjo6UmVjb2RlKGRhdGEkY2l0aXplbiwKICAgICAgICAgICAgICAgICAgICAgICByZWNvZGVzPSIyPTE7IDg6OT1OQTsgZWxzZT0wIikKCiNsYXN0IGVtcGxveWVkCgoKCmRhdGEkZW1wbG95ZWRsYXN0PC0gY2FyOjpSZWNvZGUoZGF0YSRlbXBsYXN0LAogICAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9IjE9J1dpdGhpbiBwYXN0IDEybW9udGhzJzsgMj0nMS01eWVhcnMgYWdvJzsgMz0nb3ZlciA1eWVhcnMgYWdvJzsgND0nbmV2ZXIgd29ya2VkJztlbHNlPU5BIiwKICAgICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3I9VCkKZGF0YSRlbXBsb3llZGxhc3Q8LXJlbGV2ZWwoZGF0YSRlbXBsb3llZGxhc3QsIHJlZj0nMS01eWVhcnMgYWdvJykKCgojSEVBTFRIIFZBUklBQkxFUwoKI1Bvb3Igb3IgZmFpciBzZWxmIHJhdGVkIGhlYWx0aApkYXRhJGJhZGhlYWx0aDwtY2FyOjpSZWNvZGUoZGF0YSRoZWFsdGgsIHJlY29kZXM9IjQ6NT0xOyAxOjM9MDsgZWxzZT1OQSIpCgoKI3BsYWNlIGZvciBtZWRpY2FsIGNhcmUKCgpkYXRhJG1lZGljYWxwbGFjZTwtIGNhcjo6UmVjb2RlKGRhdGEkdXN1YWxwbCwKICAgICAgICAgICAgICAgICAgICAgICByZWNvZGVzPSIxPSdubyc7IDI6Mzo9J3llcyc7ZWxzZT1OQSIsCiAgICAgICAgICAgICAgICAgICAgICAgYXMuZmFjdG9yPVQpCmRhdGEkbWVkaWNhbHBsYWNlPC1yZWxldmVsKGRhdGEkbWVkaWNhbHBsYWNlLCByZWY9J25vJykKCgojIGRlbGF5ZWQgbWVkaWNhbCBjYXJlIGR1ZSB0byBjb3N0CgoKZGF0YSRtZWRpY2FsX2NhcmVfY29zdDwtIGNhcjo6UmVjb2RlKGRhdGEkZGVsYXljb3N0LAogICAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9IjI9J3llcyc7IDE9J25vJztlbHNlPU5BIiwKICAgICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3I9VCkKZGF0YSRtZWRpY2FsX2NhcmVfY29zdDwtcmVsZXZlbChkYXRhJG1lZGljYWxfY2FyZV9jb3N0LCByZWY9J3llcycpCgojIHdvcnJpZWQgYWJvdXQgcGF5aW5nIG1lZGljYWwgYmlsbHMKCgpkYXRhJG1lZGljYWxfYmlsbF93b3JyaWVkPC0gY2FyOjpSZWNvZGUoZGF0YSR3b3JtZWRiaWxsLAogICAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9IjE9J3Zlcnkgd29ycmllZCc7IDI9J3NvbWV3aGF0IHdvcnJpZWQnOyAzPSdub3QgYXQgYWxsJztlbHNlPU5BIiwKICAgICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3I9VCkKZGF0YSRtZWRpY2FsX2JpbGxfd29ycmllZDwtcmVsZXZlbChkYXRhJG1lZGljYWxfYmlsbF93b3JyaWVkLCByZWY9J3Zlcnkgd29ycmllZCcpCgojIHVuYWJsZSB0byBwYXkgbWVkaWNhbCBiaWxscwoKCmRhdGEkbWVkaWNhbF9iaWxsX3VuYWJsZXRvcGF5PC0gY2FyOjpSZWNvZGUoZGF0YSRoaXVuYWJsZXBheSwKICAgICAgICAgICAgICAgICAgICAgICByZWNvZGVzPSIxPSdubyc7IDI9J3llcyc7ZWxzZT1OQSIsCiAgICAgICAgICAgICAgICAgICAgICAgYXMuZmFjdG9yPVQpCmRhdGEkbWVkaWNhbF9iaWxsX3VuYWJsZXRvcGF5PC1yZWxldmVsKGRhdGEkbWVkaWNhbF9iaWxsX3VuYWJsZXRvcGF5LCByZWY9J3llcycpCgoKIyBoZWFsdGggaW5zdXJhbmNlIGNvdmVyYWdlCgoKZGF0YSRoZWFsdGhpbnN1cmFjZV9jb3ZlcmFnZTwtIGNhcjo6UmVjb2RlKGRhdGEkaGlub3Rjb3ZlLAogICAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9IjE9J25vLCBoYXMgY292ZXJhZ2UnOyAyPSd5ZXMsIG5vIGNvdmVyYWdlJztlbHNlPU5BIiwKICAgICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3I9VCkKZGF0YSRoZWFsdGhpbnN1cmFjZV9jb3ZlcmFnZTwtcmVsZXZlbChkYXRhJGhlYWx0aGluc3VyYWNlX2NvdmVyYWdlLCByZWY9J3llcywgbm8gY292ZXJhZ2UnKQoKIyBkb250IGhhdmUgaGVhbHRoIGluc3VyYW5jZSBjdXogb2YgY29zdAoKCmRhdGEkbm9oZWFsdGhpbnN1cmFjZV9jb3N0PC0gY2FyOjpSZWNvZGUoZGF0YSRoaW5vY29zdHIsCiAgICAgICAgICAgICAgICAgICAgICAgcmVjb2Rlcz0iMT0nbm8nOyAyPSd5ZXMnO2Vsc2U9TkEiLAogICAgICAgICAgICAgICAgICAgICAgIGFzLmZhY3Rvcj1UKQpkYXRhJG5vaGVhbHRoaW5zdXJhY2VfY29zdDwtcmVsZXZlbChkYXRhJG5vaGVhbHRoaW5zdXJhY2VfY29zdCwgcmVmPSd5ZXMnKQoKIyB1c2VkIG1lZGljYXRpb24gaW4gdGhlIHBhc3QgeWVhcgoKCmRhdGEkdXNlZG1lZGljYXRpb25zPC0gY2FyOjpSZWNvZGUoZGF0YSRwcmVtZWR5ciwKICAgICAgICAgICAgICAgICAgICAgICByZWNvZGVzPSIxPSdubyc7IDI9J3llcyc7ZWxzZT1OQSIsCiAgICAgICAgICAgICAgICAgICAgICAgYXMuZmFjdG9yPVQpCmRhdGEkdXNlZG1lZGljYXRpb25zPC1yZWxldmVsKGRhdGEkdXNlZG1lZGljYXRpb25zLCByZWY9J3llcycpCgoKIyBpZiB0aGV5IHNtb2tlZAoKCmRhdGEkc21va2VfZnJlcXVlbnRseTwtIGNhcjo6UmVjb2RlKGRhdGEkc21va2ZyZXFub3csCiAgICAgICAgICAgICAgICAgICAgICAgcmVjb2Rlcz0iMT0nbm8nOyAyOjM9J3NvbWVkYXlzL2V2ZXJ5ZGF5JztlbHNlPU5BIiwKICAgICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3I9VCkKZGF0YSRzbW9rZV9mcmVxdWVudGx5PC1yZWxldmVsKGRhdGEkc21va2VfZnJlcXVlbnRseSwgcmVmPSdzb21lZGF5cy9ldmVyeWRheScpCgojIHNtb2tlZCB1cCB0byAxMDAgY2lnYXJyZXRoIGluIGxpZmUgdGltZQoKCmRhdGEkc21va2VfMTAwY2lnPC0gY2FyOjpSZWNvZGUoZGF0YSRzbW9rZXYsCiAgICAgICAgICAgICAgICAgICAgICAgcmVjb2Rlcz0iMT0nbm8nOyAyPSd5ZXMnO2Vsc2U9TkEiLAogICAgICAgICAgICAgICAgICAgICAgIGFzLmZhY3Rvcj1UKQpkYXRhJHNtb2tlXzEwMGNpZzwtcmVsZXZlbChkYXRhJHNtb2tlXzEwMGNpZywgcmVmPSd5ZXMnKQoKCiNNZW50YWwgaGVhbHRoCgojIGV2ZXIgaGFkIGFueGlldHkgZGlzb3JlZGVyCgoKZGF0YSRhbnhpZXR5X2Rpc29yZWRlcjwtIGNhcjo6UmVjb2RlKGRhdGEkYW54aWV0eWV2LAogICAgICAgICAgICAgICAgICAgICAgIHJlY29kZXM9IjE9J25vJzsgMj0neWVzJztlbHNlPU5BIiwKICAgICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3I9VCkKZGF0YSRhbnhpZXR5X2Rpc29yZWRlcjwtcmVsZXZlbChkYXRhJGFueGlldHlfZGlzb3JlZGVyLCByZWY9J3llcycpCgojIG1lZGljYXRpb24gZm9yIHdvcnJ5aW5nCgoKZGF0YSRtZWRpY2F0aW9uX2Zvcl93b3JyeTwtIGNhcjo6UmVjb2RlKGRhdGEkd29ycngsCiAgICAgICAgICAgICAgICAgICAgICAgcmVjb2Rlcz0iMT0nbm8nOyAyPSd5ZXMnO2Vsc2U9TkEiLAogICAgICAgICAgICAgICAgICAgICAgIGFzLmZhY3Rvcj1UKQpkYXRhJG1lZGljYXRpb25fZm9yX3dvcnJ5PC1yZWxldmVsKGRhdGEkbWVkaWNhdGlvbl9mb3Jfd29ycnksIHJlZj0neWVzJykKCgojIG1lZGljYXRpb24gZm9yIGRlcHJlc3Npb24KCgoKZGF0YSRtZWRpY2F0aW9uX2Zvcl9kZXByZXNzaW9uPC0gY2FyOjpSZWNvZGUoZGF0YSRkZXByeCwKICAgICAgICAgICAgICAgICAgICAgICByZWNvZGVzPSIxPSdubyc7IDI9J3llcyc7ZWxzZT1OQSIsCiAgICAgICAgICAgICAgICAgICAgICAgYXMuZmFjdG9yPVQpCmRhdGEkbWVkaWNhdGlvbl9mb3JfZGVwcmVzc2lvbjwtcmVsZXZlbChkYXRhJG1lZGljYXRpb25fZm9yX2RlcHJlc3Npb24sIHJlZj0neWVzJykKCiMgbGV2ZWwgb2Ygd29ycnkKCgoKZGF0YSRsZXZlbF9vZl93b3JyeTwtIGNhcjo6UmVjb2RlKGRhdGEkd29yZmVlbGV2bCwKICAgICAgICAgICAgICAgICAgICAgICByZWNvZGVzPSIxPSdhbG90JzsgMj0nYSBsaXR0bGUnOyAzPSdidHcgbGl0dGxlIGFuZCBhbG90JztlbHNlPU5BIiwKICAgICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3I9VCkKZGF0YSRsZXZlbF9vZl93b3JyeTwtcmVsZXZlbChkYXRhJGxldmVsX29mX3dvcnJ5LCByZWY9J2Fsb3QnKQoKCiMgbGV2ZWwgb2YgZGVwcmVzc2lvbgoKCgpkYXRhJGxldmVsX29mX2RlcHJlc3Npb248LSBjYXI6OlJlY29kZShkYXRhJGRlcGZlZWxldmwsCiAgICAgICAgICAgICAgICAgICAgICAgcmVjb2Rlcz0iMT0nYWxvdCc7IDI9J2EgbGl0dGxlJzsgMz0nYnR3IGxpdHRsZSBhbmQgYWxvdCc7ZWxzZT1OQSIsCiAgICAgICAgICAgICAgICAgICAgICAgYXMuZmFjdG9yPVQpCmRhdGEkbGV2ZWxfb2ZfZGVwcmVzc2lvbjwtcmVsZXZlbChkYXRhJGxldmVsX29mX2RlcHJlc3Npb24sIHJlZj0nYWxvdCcpCgoKCgpgYGAKCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpkYXRhIDwtIGRhdGElPiUKZmlsdGVyKGFnZSA+PTE2ICYgYWdlPD0yNCkKCmRhdGE8LWRhdGElPiUKICBmaWx0ZXIoaXMubmEoYmFkaGVhbHRoKT09RikKZGF0YTwtZGF0YSU+JQogIGZpbHRlcihpcy5uYShlZHVjKT09RikKYGBgCgoKCmBgYHtyIGluY2x1ZGU9RkFMU0V9Cm9wdGlvbnMoc3VydmV5LmxvbmVseS5wc3UgPSAiYWRqdXN0IikKCmRlczwtc3Z5ZGVzaWduKGlkcz1+MSwgc3RyYXRhPX5zdHJhdGEsIHdlaWdodHM9fnNhbXB3ZWlnaHQsIGRhdGEgPSBkYXRhICkKZGVzCmBgYAoKCgoKCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpzdi50YWJsZTwtc3Z5YnkoZm9ybXVsYSA9IH5iYWRoZWFsdGgsCiAgICAgICAgICAgICAgICBieSA9IH5vcHBvcnR1bml0eV95b3V0aF9jYXQsCiAgICAgICAgICAgICAgICBkZXNpZ24gPSBkZXMsCiAgICAgICAgICAgICAgICBGVU4gPSBzdnltZWFuLAogICAgICAgICAgICAgICAgbmEucm09VCkKCgprbml0cjo6a2FibGUoc3YudGFibGUsCiAgICAgIGNhcHRpb24gPSAiU3VydmV5IEVzdGltYXRlcyBvZiBQb29yIFNSSCBieSBPcHBvcnR1bml0eSBZb3V0aHMiLAogICAgICBhbGlnbiA9ICdjJywgIAogICAgICBmb3JtYXQgPSAiaHRtbCIpCmBgYAoKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CiNNYWtlIGEgc3VydmV5IGRlc2lnbiB0aGF0IGlzIHJhbmRvbSBzYW1wbGluZyAtIG5vIHN1cnZleSBpbmZvcm1hdGlvbgpub2Rlczwtc3Z5ZGVzaWduKGlkcyA9IH4xLCAgd2VpZ2h0cyA9IH4xLCBkYXRhID0gZGF0YSkKCnN2LnRhYmxlPC1zdnlieShmb3JtdWxhID0gfmZhY3RvcihiYWRoZWFsdGgpLAogICAgICAgICAgICAgICAgYnkgPSB+b3Bwb3J0dW5pdHlfeW91dGhfY2F0LAogICAgICAgICAgICAgICAgZGVzaWduID0gbm9kZXMsCiAgICAgICAgICAgICAgICBGVU4gPSBzdnltZWFuLAogICAgICAgICAgICAgICAgbmEucm09VCkKYGBgCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQprbml0cjo6a2FibGUoc3YudGFibGUsCiAgICAgIGNhcHRpb24gPSAiRXN0aW1hdGVzIG9mIFBvb3IgU1JIIGJ5IE9wcG9ydHVuaXR5IHlvdXRoIC0gTm8gc3VydmV5IGRlc2lnbiIsCiAgICAgIGFsaWduID0gJ2MnLCAgCiAgICAgIGZvcm1hdCA9ICJodG1sIikKYGBgCgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShzcnZ5cikKCmRhdGElPiUKICBhc19zdXJ2ZXlfZGVzaWduKHN0cmF0YSA9IHN0cmF0YSwKICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSBzYW1wd2VpZ2h0KSU+JQogIGdyb3VwX2J5KG9wcG9ydHVuaXR5X3lvdXRoX2NhdCklPiUKICBzdW1tYXJpc2UobWVhbl9iaCA9IHN1cnZleV9tZWFuKGJhZGhlYWx0aCwgbmEucm09VCkpJT4lCiAgdW5ncm91cCgpCmBgYAoKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoZ3RzdW1tYXJ5KQoKZGF0YSU+JQogIGFzX3N1cnZleV9kZXNpZ24oIHN0cmF0YSA9c3RyYXRhLAogICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSBzYW1wd2VpZ2h0KSU+JQogIHNlbGVjdChiYWRoZWFsdGgsIG9wcG9ydHVuaXR5X3lvdXRoX2NhdCklPiUKICB0Ymxfc3Z5c3VtbWFyeShieSA9IG9wcG9ydHVuaXR5X3lvdXRoX2NhdCwgCiAgICAgICAgICAgICAgbGFiZWwgPSBsaXN0KGJhZGhlYWx0aCA9ICJGYWlyL1Bvb3IgSGVhbHRoIikpJT4lCiAgYWRkX3AoKSU+JQogIGFkZF9uKCkKYGBgCgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShkcGx5cikKc3ViPC1kYXRhJT4lCiAgc2VsZWN0KGJhZGhlYWx0aCwgb3Bwb3J0dW5pdHlfeW91dGhfY2F0X251bSwgb3Bwb3J0dW5pdHlfeW91dGhfY2F0LCByYWNlX2V0aCxlZHVjLHdoaXRlbWFqb3JpdHksIG90aGVybWlub3JpdHksdXJiYW5fcnVyYWwsIG1hbGUsc2FtcHdlaWdodCxtYWxlLHNtb2tlX2ZyZXF1ZW50bHksIHN0cmF0YSkKI0ZpcnN0IHdlIHRlbGwgUiBvdXIgc3VydmV5IGRlc2lnbgpvcHRpb25zKHN1cnZleS5sb25lbHkucHN1ID0gImFkanVzdCIpCmRlczwtc3Z5ZGVzaWduKGlkcz0gfjEsCiAgICAgICAgICAgICAgIHN0cmF0YT0gfnN0cmF0YSwKICAgICAgICAgICAgICAgd2VpZ2h0cz0gfnNhbXB3ZWlnaHQsCiAgICAgICAgICAgICAgIGRhdGEgPSBzdWIgKQpgYGAKCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpmaXQubG9naXQxPC1zdnlnbG0oYmFkaGVhbHRofm9wcG9ydHVuaXR5X3lvdXRoX2NhdCxkZXNpZ249IGRlcywgZmFtaWx5PWJpbm9taWFsKSAjIE9wcG9ydHVuaXR5IHlvdXRoIGNhdApgYGAKCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpmaXQubG9naXQyPC1zdnlnbG0oYmFkaGVhbHRofm9wcG9ydHVuaXR5X3lvdXRoX2NhdCtlZHVjLGRlc2lnbj0gZGVzLCBmYW1pbHk9Ymlub21pYWwpICNPcHBvcnR1bml0eSB5b3V0aCBjYXQrZWR1Y2F0aW9uCmBgYAoKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmZpdC5sb2dpdDM8LXN2eWdsbShiYWRoZWFsdGh+b3Bwb3J0dW5pdHlfeW91dGhfY2F0K2VkdWMrbWFsZSt1cmJhbl9ydXJhbCxkZXNpZ249IGRlcywgZmFtaWx5PWJpbm9taWFsKSNPcHBvcnR1bml0eSB5b3V0aCBjYXQrZWR1Y2F0aW9uICwgZ2VuZGVyLCB1cmJhbi1ydXJhbApgYGAKCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpzdW1tYXJ5KGZpdC5sb2dpdDIpCmBgYAoKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CnJlZ1Rlcm1UZXN0KGZpdC5sb2dpdDIsCiAgICAgICAgICAgIHRlc3QudGVybXMgPSB+ZWR1YywKICAgICAgICAgICAgbWV0aG9kPSJXYWxkIiwKICAgICAgICAgICAgZGYgPSBOVUxMKQpgYGAKCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpzdW1tYXJ5KGZpdC5sb2dpdDMpCmBgYAoKCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpyZWdUZXJtVGVzdChmaXQubG9naXQzLAogICAgICAgICAgICB0ZXN0LnRlcm1zPX5tYWxlK3VyYmFuX3J1cmFsLAogICAgICAgICAgICBtZXRob2Q9IldhbGQiLAogICAgICAgICAgICBkZiA9IE5VTEwpCmBgYAoKCmBgYHtyIGluY2x1ZGU9RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQpmMTwtIGZpdC5sb2dpdDElPiUKICB0YmxfcmVncmVzc2lvbihleHBvbmVudGlhdGUgPVQpCgpmMjwtIGZpdC5sb2dpdDIlPiUKICB0YmxfcmVncmVzc2lvbihleHBvbmVudGlhdGUgPVQpCgpmMzwtIGZpdC5sb2dpdDMlPiUKICB0YmxfcmVncmVzc2lvbihleHBvbmVudGlhdGUgPVQpCgoKCmZfYWxsIDwtIHRibF9tZXJnZSh0YmxzID1saXN0KGYxLCBmMiwgZjMpLAogICAgICAgICAgICAgICAgICAgIHRhYl9zcGFubmVyID0gYygiKipNb2RlbCAxKioiLCAiKipNb2RlbCAyKioiLCAiKipNb2RlbCAzKioiKSkKCmZfYWxsCmBgYAoKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CkFJQyhmaXQubG9naXQxLCBmaXQubG9naXQyLCBmaXQubG9naXQzKQpgYGAKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmFub3ZhKGZpdC5sb2dpdDEsIGZpdC5sb2dpdDIpCmBgYAoKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmFub3ZhKGZpdC5sb2dpdDIsIGZpdC5sb2dpdDMpCmBgYAoKCgojIDIpIEZpdCBhIHByZWRpY3RpdmUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBhcyBtYW55IHByZWRpY3RvciB2YXJpYWJsZXMgYXMgeW91IHRoaW5rIHlvdSBuZWVkCgpgYGB7cn0KZGF0YSRhZ2UyPC0gZGF0YSRhZ2VeMgpgYGAKCgpgYGB7cn0KbGlicmFyeShkcGx5cikKbW9kZWwuZGF0YTwtIGRhdGEgJT4lCiAgc2VsZWN0KHNlcmlhbCwgYmFkaGVhbHRoLCBvcHBvcnR1bml0eV95b3V0aF9jYXQsIHVyYmFuX3J1cmFsLCBtYWxlLCBlZHVjLCBhZ2UyKQpgYGAKCgpgYGB7ciwgcmVzdWx0cz0nYXNpcyd9CmtuaXRyOjprYWJsZShoZWFkKG1vZGVsLmRhdGEpKQpgYGAKCgojIDMpIFVzZSBhIDgwJSB0cmFpbmluZy8yMCUgdGVzdCBzcGxpdCBmb3IgeW91ciBkYXRhCgpgYGB7cn0Kc2V0LnNlZWQoMTExNSkKdHJhaW48LSBjcmVhdGVEYXRhUGFydGl0aW9uKHkgPSBtb2RlbC5kYXRhJGJhZGhlYWx0aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHAgPSAuODAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0PUYpCgptb2RlbC5kYXQydHJhaW48LW1vZGVsLmRhdGFbdHJhaW4sXQptb2RlbC5kYXQydGVzdDwtbW9kZWwuZGF0YVstdHJhaW4sXQoKdGFibGUobW9kZWwuZGF0MnRyYWluJGJhZGhlYWx0aCkKYGBgCgoKYGBge3J9CnByb3AudGFibGUodGFibGUobW9kZWwuZGF0MnRyYWluJGJhZGhlYWx0aCkpCmBgYAoKCmBgYHtyfQpzdW1tYXJ5KG1vZGVsLmRhdDJ0cmFpbikKYGBgCgoKIyBMb2dpc3RpYyByZWdyZXNzaW9uIGZvciBjbGFzc2lmaWNhdGlvbgoKYGBge3J9CmdsbTE8LWdsbShiYWRoZWFsdGh+ZmFjdG9yKG9wcG9ydHVuaXR5X3lvdXRoX2NhdCkrZmFjdG9yKHVyYmFuX3J1cmFsKStzY2FsZShhZ2UyKStmYWN0b3IobWFsZSkrZmFjdG9yKGVkdWMpLAogICAgICAgICAgZGF0YT1tb2RlbC5kYXQydHJhaW5bLC0xXSwKICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKQpzdW1tYXJ5KGdsbTEpCmBgYAoKYGBge3J9CnRyX3ByZWQ8LSBwcmVkaWN0KGdsbTEsCiAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBtb2RlbC5kYXQydHJhaW4sCiAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQoKaGVhZCh0cl9wcmVkKQpgYGAKCgpVc2luZyA1MCUgYXMgdGhlIHByZWRpY3RvcgoKYGBge3J9CnRyX3ByZWRjbDwtZmFjdG9yKGlmZWxzZSh0cl9wcmVkPi41LCAxLCAwKSkKCmxpYnJhcnkoZ2dwbG90MikKCnByZWQxPC1kYXRhLmZyYW1lKHByPXRyX3ByZWQsCiAgICAgICAgICAgICAgICAgIGdyPXRyX3ByZWRjbCwKICAgICAgICAgICAgICAgICAgYmFkaHQ9bW9kZWwuZGF0MnRyYWluJGJhZGhlYWx0aCkKCnByZWQxJT4lCiAgZ2dwbG90KCkrCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHg9cHIsIGNvbG9yPWdyLCBmaWxsPWdyKSkrCiAgZ2d0aXRsZShsYWJlbCA9ICJQcm9iYWJpbGl0eSBvZiBCYWQgaGVhbHRoIiwKICAgICAgICAgIHN1YnRpdGxlID0gIlRocmVzaG9sZCA9IC41IikrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PS41KQpgYGAKCmBgYHtyfQpwcmVkMSU+JQogIGdncGxvdCgpKwogIGdlb21faGlzdG9ncmFtKGFlcyh4PXByLCBjb2xvcj1iYWRodCwgZmlsbD1iYWRodCkpKwogIGdndGl0bGUobGFiZWwgPSAiUHJvYmFiaWxpdHkgb2YgQmFkIGhlYWx0aCIsCiAgICAgICAgICBzdWJ0aXRsZSA9ICJUcnV0aCIpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0uNSkKYGBgCgoKYGBge3J9CnRhYmxlKCB0cl9wcmVkY2wsCiAgICAgICBtb2RlbC5kYXQydHJhaW4kYmFkaGVhbHRoKQpgYGAKCmBgYHtyfQptb2RlbC5kYXQydHJhaW4kYmFkaGVhbHRoPC0gYXMuZmFjdG9yKG1vZGVsLmRhdDJ0cmFpbiRiYWRoZWFsdGgpCmBgYAoKCmBgYHtyfQpjbTE8LWNvbmZ1c2lvbk1hdHJpeChkYXRhID0gdHJfcHJlZGNsLAogICAgICAgICAgICAgICAgICAgICByZWZlcmVuY2UgPSBtb2RlbC5kYXQydHJhaW4kYmFkaGVhbHRoKQpjbTEKYGBgCgoKCgojIDMpIFJlcG9ydCB0aGUgJSBjb3JyZWN0IGNsYXNzaWZpY2F0aW9uIGZyb20gdGhlIHRyYWluaW5nIGRhdGEgdXNpbmcgdGhlIC41IGRlY2lzaW9uIHJ1bGUgCgpPdmVyYWxsIHRoZSBtb2RlbCBoYXMgYSA5NC42MiUgYWNjdXJhY3kuIAoKCiMjIFVzaW5nIG1lYW4gYXMgYSBwcmVkaWN0b3IKCmBgYHtyfQp0cl9wcmVkY2w8LWZhY3RvcihpZmVsc2UodHJfcHJlZD5tZWFuKEkobW9kZWwuZGF0MnRyYWluJGJhZGhlYWx0aD09MSkpLCAxLCAwKSkgI21lYW4gb2YgcmVzcG9uc2UKCnByZWQyPC1kYXRhLmZyYW1lKHByPXRyX3ByZWQsCiAgICAgICAgICAgICAgICAgIGdyPXRyX3ByZWRjbCwKICAgICAgICAgICAgICAgICAgYmFkaHQ9bW9kZWwuZGF0MnRyYWluJGJhZGhlYWx0aCkKCnByZWQyJT4lCiAgZ2dwbG90KGFlcyh4PXByLCBmaWxsPWdyKSkrCiAgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb249ImlkZW50aXR5IiwKICAgICAgICAgICAgICAgICBhbHBoYT0uMikrCiAgZ2d0aXRsZShsYWJlbCA9ICJQcm9iYWJpbGl0eSBvZiBCYWQgaGVhbHRoIiwKICAgICAgICAgIHN1YnRpdGxlID0gIlRocmVzaG9sZCA9IE1lYW4iKSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9bWVhbihJKG1vZGVsLmRhdDJ0cmFpbiRiYWRoZWFsdGg9PTEpKSkKYGBgCgoKYGBge3J9CnByZWQyJT4lCiAgZ2dwbG90KGFlcyh4PXByLCBmaWxsPWJhZGh0KSkrCiAgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb249ImlkZW50aXR5IiwKICAgICAgICAgICAgICAgICBhbHBoYT0uMikrCiAgZ2d0aXRsZShsYWJlbCA9ICJQcm9iYWJpbGl0eSBvZiBCYWQgaGVhbHRoIiwKICAgICAgICAgIHN1YnRpdGxlID0gIlRydXRoIikrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PW1lYW4oSShtb2RlbC5kYXQydHJhaW4kYmFkaGVhbHRoPT0xKSkpCmBgYAoKYGBge3J9CnRhYmxlKCB0cl9wcmVkY2wsCiAgICAgICBtb2RlbC5kYXQydHJhaW4kYmFkaGVhbHRoKQpgYGAKCmBgYHtyfQptb2RlbC5kYXQydHJhaW4kYmFkaGVhbHRoPC0gYXMuZmFjdG9yKG1vZGVsLmRhdDJ0cmFpbiRiYWRoZWFsdGgpCmBgYAoKCmBgYHtyfQpjb25mdXNpb25NYXRyaXgoZGF0YSA9IHRyX3ByZWRjbCwKICAgICAgICAgICAgICAgIG1vZGVsLmRhdDJ0cmFpbiRiYWRoZWFsdGgsCiAgICAgICAgICAgICAgICBwb3NpdGl2ZSA9ICIxIiApCmBgYAoKIyAzKSBSZXBvcnQgdGhlICUgY29ycmVjdCBjbGFzc2lmaWNhdGlvbiBmcm9tIHRoZSB0cmFpbmluZyBkYXRhIHVzaW5nIHRoZSAgbWVhbiBhcyB0aGUgZGVjaXNpb24gcnVsZSAKCmFuc3dlcjogT3ZlcnJhbCwgZnJvbSB0aGUgdHJhaW5pbmcgZGF0YSwgdGhlIG1vZGVsIGhhcyBhIDY4Ljk3JSBhY2N1cmFjeQoKIyAzYSkgRG9lcyBjaGFuZ2luZyB0aGUgZGVjaXNpb24gcnVsZSB0aHJlc2hvbGQgYWZmZWN0IHlvdXIgY2xhc3NpZmljYXRpb24gYWNjdXJhY3k/CgpBbnN3ZXI6IFllcywgdXNpbmcgdGhlIC41IHRoZSBjbGFzc2lmaWNhdGlvbiBhY2N1cmFjeSBpcyA5NC42MiUsIHdoaWxlIHVzaW5nIHRoZSBtZWFuIHRoZSBhY2N1cmFjeSByZWR1Y2VkIHRvIDY4Ljk3JS4gRXZlbiB0aG91Z2ggdGhlIG1lYW4gYWNjdXJhY3kgaXMgbGVzc2VyLCB0aGUgLjUgY2xhc3NpZmljYXRpb24gZGlkIG5vdCBhY2NvdW50IGZvciBmYWxzZSBwb3NpdGl2ZSBhbmQgdHJ1ZSBuZWdhdGl2ZS4KCgoKCiMjIFRlc3RpbmcgZGF0YSB1c2luZyBtZWFuCgpgYGB7cn0KcHJlZF90ZXN0PC1wcmVkaWN0KGdsbTEsCiAgICAgICAgICAgICAgICAgICBuZXdkYXRhPW1vZGVsLmRhdDJ0ZXN0LAogICAgICAgICAgICAgICAgICAgdHlwZT0icmVzcG9uc2UiKQoKcHJlZF9jbDwtZmFjdG9yKGlmZWxzZShwcmVkX3Rlc3QgPiBtZWFuKCBJKG1vZGVsLmRhdDJ0ZXN0JGJhZGhlYWx0aD09MSkpLCAxLCAwKSkKCnRhYmxlKG1vZGVsLmRhdDJ0ZXN0JGJhZGhlYWx0aCxwcmVkX2NsKQpgYGAKCmBgYHtyfQptb2RlbC5kYXQydGVzdCRiYWRoZWFsdGg8LSBhcy5mYWN0b3IobW9kZWwuZGF0MnRlc3QkYmFkaGVhbHRoKQpgYGAKCgpgYGB7cn0KY29uZnVzaW9uTWF0cml4KGRhdGEgPSBwcmVkX2NsLG1vZGVsLmRhdDJ0ZXN0JGJhZGhlYWx0aCkKYGBgCgojIDQuIFJlcG9ydCB0aGUgJSBjb3JyZWN0IGNsYXNzaWZpY2F0aW9uIGZyb20gdGhlIHRlc3QgZGF0YSB1c2luZyB0aGUgbWVhbiBhcyB0aGUgZGVjaXNpb24gcnVsZSAKClRoZSBPdmVycmFsLCBmcm9tIHRoZSB0ZXN0aW5nIGRhdGEsIHRoZSBtb2RlbCBoYXMgYSA3My4wMSUgYWNjdXJhY3kKCgojIyBUZXN0aW5nIGRhdGEgdXNpbmcgMC41CgpgYGB7cn0KcHJlZF90ZXN0MjwtcHJlZGljdChnbG0xLAogICAgICAgICAgICAgICAgICAgbmV3ZGF0YT1tb2RlbC5kYXQydGVzdCwKICAgICAgICAgICAgICAgICAgIHR5cGU9InJlc3BvbnNlIikKCnByZWRfY2w8LWZhY3RvcihpZmVsc2UocHJlZF90ZXN0ID4uNSwgMSwgMCkpCgp0YWJsZShtb2RlbC5kYXQydGVzdCRiYWRoZWFsdGgscHJlZF9jbCkKYGBgCgoKYGBge3J9Cm1vZGVsLmRhdDJ0ZXN0JGJhZGhlYWx0aDwtIGFzLmZhY3Rvcihtb2RlbC5kYXQydGVzdCRiYWRoZWFsdGgpCmBgYAoKYGBge3J9CmNvbmZ1c2lvbk1hdHJpeChkYXRhID0gcHJlZF9jbCxtb2RlbC5kYXQydGVzdCRiYWRoZWFsdGgpCmBgYAoKCiMgNCBSZXBvcnQgdGhlICUgY29ycmVjdCBjbGFzc2lmaWNhdGlvbiBmcm9tIHRoZSB0ZXN0IGRhdGEgdXNpbmcgdGhlIC41IGRlY2lzaW9uIHJ1bGUKYW5zd2VyOiBUaGUgcGVyY2VudGFnZSBjb3JyZWN0IGNsYXNzaWZpY2F0aW9uIGlzIDk0LjQxICUgYWNjdXJhdGUuIFN0aWxsIGRpZCBub3QgYWNjb3VudCBmb3IgdGhlIGZhbHNlIHBvc2l0aXZlIGFuZCB0cnVlIG5lZ2F0aXZlCgo=