#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))
serial badhealth opportunity_youth_cat urban_rural male educ age2
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=