library(tidyverse)
library(caret)
library(ROSE)
library(missForest)
options(scipen = 4) #escala dos valores exibidos
data <- read.csv("../dados/train_lab4.csv", encoding = "UTF-8")

# Separa os dados em treino e teste
dataPartition <- createDataPartition(y = data$situacao_final, p=0.75, list=FALSE)
# Se o y for um vector de factor a divis?o ? feita tentando balancear a distribui??o de classes de # y dentro das parti??es.

train <- data[ dataPartition, ]
validation <- data[ -dataPartition, ]

Os dados de treino apresentam muitos valores faltantes representados por 0, para resolver este problema Utilizei o m?todo missForest, do pacote missForest. Este m?todo constro? um modelo de floresta aleat?ria para cada vari?vel e ent?o utliza para prever os valores das vari?veis faltantes por meio dos valores observados.

Algumas vari?veis irrelevantes foram removidas como ID e nome.

#removendo vari?veis irrelevantes
train <- train %>% select(-ID, -nome, -numero_cadidato, -setor_economico_receita, -setor_economico_despesa) 
validation <- validation %>% select(-ID, -nome, -numero_cadidato, -setor_economico_receita, -setor_economico_despesa)
#o m?todo missForest n?o executa para vari?veis com mais de 53 categorias, neste caso optei por remover a vari?vel descricao_ocupacao
train <- train %>% select(-descricao_ocupacao)
validation <- validation %>% select(-descricao_ocupacao)
#transformando valores faltantes em NA
train[train == 0] <- NA
validation[validation == 0] <- NA
#tratando missing data 
train <- missForest(train)$ximp
  missForest iteration 1 in progress...done!
  missForest iteration 2 in progress...done!
  missForest iteration 3 in progress...done!
validation <- missForest(validation)$ximp
  missForest iteration 1 in progress...done!
  missForest iteration 2 in progress...done!
  missForest iteration 3 in progress...done!
  missForest iteration 4 in progress...done!

H? desbalanceamento das classes (isto ?, uma classe tem muito mais inst?ncias que outra)? Em que propor??o? Quais efeitos colaterais o desbalanceamento de classes pode causar no classificador?.

Classes desbalanceadas ocorrem quando existe uma grande despropor??o entre o n?mero de exemplos de cada classe. Essa situa??o frequentemente faz com que os exemplos da classe minorit?ria sejam classificados incorretamente.

prop.table(table(train$situacao_final))

    eleito nao_eleito 
 0.1005803  0.8994197 

Analisando os dados de treino ? f?cil perceber o grande desbalanceamento entre classes, neste caso a classe nao_eleitos compreende aproximadamente 90% dos exemplos de treino.

Para resolver esse problema utilizei o m?todo de balanceamento ROSE, do pacote ROSE, este m?todo gera dados de forma sint?tica buscando preservar as caracter?sticas dos dados originais.

train <- ROSE(situacao_final ~ ., data = train, seed = 1)$data
prop.table(table(train$situacao_final))

nao_eleito     eleito 
 0.5206319  0.4793681 

Agora as classes apresentam uma quantidade proporcional de exemplos, nao_eleitos 52% e eleitos 48%.

Treine: um modelo de regress?o log?stica, uma ?rvore de decis?o e um modelo de adaboost. Tune esses modelos usando valida??o cruzada e controle overfitting se necess?rio, considerando as particularidades de cada modelo.

Regress?o Log?stica

O modelo de regress?o log?stica foi tunado conforme a fun??o definida em fitcontrol, o hiperparametro do modelo ? otimizado por meio de testes em n valores aleat?rios. Todos os modelos consideram 5-fold para valida??o cruzada.

fitControl <- trainControl(method = "cv",
                    number = 5,
                    search= "random")

formula <- as.formula(situacao_final ~.)

# Regress?o log?stica glm (generalized linear model)
reg_log <- train(formula,
                 data = train,
                 method="glm",
                 trControl = fitControl,
                 family="binomial",
                 na.action = na.omit)

?rvore de decis?o

A ?rvore de decis?o foi tumada definindo 10 valores distintos para seu hiperpar?metro de complexidade no intervalo [0, 0.01].

#intervalo do hiperpar?metro
values_cp <- expand.grid(fraction = seq(0, 0.01, length = 10))
arvore <- train(formula,
                 data=train,
                 method = "rpart",
                 trControl = fitControl,
                 cp = values_cp,  # hiperpar?metro de complexidade
                 maxdepth = 20,
                 na.action = na.omit)
plot(arvore)

O melhor valor observado foi para um cp de aproximadamente 0.0012.

Modelo Adaboost

O modelo adaboost foi tunado por meio do teste de n valores para seu hiperpar?metro n?mero de ?rvores.

adaboost <- train(formula,
                  data=train,
                  method = "adaboost",
                  trControl = fitControl,
                  na.action = na.omit)
plot(adaboost)

O melhor valor observado foi para 900 ?rvores.

Reporte acur?cia, precision, recall e f-measure no treino e valida??o. Como voc? avalia os resultados? Justifique sua resposta.

Regress?o log?stica

train$predict <- predict(reg_log, train)
confusionMatrix(train$predict, train$situacao_final)
Confusion Matrix and Statistics

            Reference
Prediction   nao_eleito eleito
  nao_eleito       1471    172
  eleito            144   1315
                                          
               Accuracy : 0.8981          
                 95% CI : (0.8869, 0.9086)
    No Information Rate : 0.5206          
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.7958          
 Mcnemar's Test P-Value : 0.1288          
                                          
            Sensitivity : 0.9108          
            Specificity : 0.8843          
         Pos Pred Value : 0.8953          
         Neg Pred Value : 0.9013          
             Prevalence : 0.5206          
         Detection Rate : 0.4742          
   Detection Prevalence : 0.5297          
      Balanced Accuracy : 0.8976          
                                          
       'Positive' Class : nao_eleito      
                                          
validation$predict <- predict(reg_log, validation)
confusionMatrix(validation$predict, validation$situacao_final)
Levels are not in the same order for reference and data. Refactoring data to match.
Confusion Matrix and Statistics

            Reference
Prediction   eleito nao_eleito
  eleito         92         70
  nao_eleito     12        859
                                          
               Accuracy : 0.9206          
                 95% CI : (0.9024, 0.9364)
    No Information Rate : 0.8993          
    P-Value [Acc > NIR] : 0.0112          
                                          
                  Kappa : 0.6486          
 Mcnemar's Test P-Value : 3.082e-10       
                                          
            Sensitivity : 0.88462         
            Specificity : 0.92465         
         Pos Pred Value : 0.56790         
         Neg Pred Value : 0.98622         
             Prevalence : 0.10068         
         Detection Rate : 0.08906         
   Detection Prevalence : 0.15682         
      Balanced Accuracy : 0.90463         
                                          
       'Positive' Class : eleito          
                                          

Tanto com os dados de treino como com os dados de teste o modelo preservou sua acur?cia em torno de 90%. A precis?o ou sensibilidade do modelo ficou em torno de 92,15%, indicando a propor??o de acerto do modelo ao classificar um deputado que ser? eleito. O recall ou especificidade ficou em torno de 89% sendo respons?vel pela propor??o de acertos do modelo ao classificar um deputado que n?o ser? eleito.

?rvore de decis?o

train$predict <- predict(arvore, train)
confusionMatrix(train$predict, train$situacao_final)
Confusion Matrix and Statistics

            Reference
Prediction   nao_eleito eleito
  nao_eleito       1542     12
  eleito             73   1475
                                          
               Accuracy : 0.9726          
                 95% CI : (0.9662, 0.9781)
    No Information Rate : 0.5206          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.9452          
 Mcnemar's Test P-Value : 7.62e-11        
                                          
            Sensitivity : 0.9548          
            Specificity : 0.9919          
         Pos Pred Value : 0.9923          
         Neg Pred Value : 0.9528          
             Prevalence : 0.5206          
         Detection Rate : 0.4971          
   Detection Prevalence : 0.5010          
      Balanced Accuracy : 0.9734          
                                          
       'Positive' Class : nao_eleito      
                                          
validation$predict <- predict(arvore, validation)
confusionMatrix(validation$predict, validation$situacao_final)
Levels are not in the same order for reference and data. Refactoring data to match.
Confusion Matrix and Statistics

            Reference
Prediction   eleito nao_eleito
  eleito         77         28
  nao_eleito     27        901
                                          
               Accuracy : 0.9468          
                 95% CI : (0.9313, 0.9596)
    No Information Rate : 0.8993          
    P-Value [Acc > NIR] : 0.0000000264    
                                          
                  Kappa : 0.7072          
 Mcnemar's Test P-Value : 1               
                                          
            Sensitivity : 0.74038         
            Specificity : 0.96986         
         Pos Pred Value : 0.73333         
         Neg Pred Value : 0.97091         
             Prevalence : 0.10068         
         Detection Rate : 0.07454         
   Detection Prevalence : 0.10165         
      Balanced Accuracy : 0.85512         
                                          
       'Positive' Class : eleito          
                                          

A acur?cia da ?rvore de decis?o ficou em torno de 94%. Em torno de 87% das observa??es s?o classificadas corretamente como deputado eleito e 97% s?o classificadas corretamente como deputado n?o eleitos.

Adaboost

train$predict <- predict(adaboost, train)
confusionMatrix(train$predict, train$situacao_final)
validation$predict <- predict(adaboost, validation)
confusionMatrix(validation$predict, validation$situacao_final)

Adaboost apresentou acur?cia em torno de 100%. 90% das observa??es s?o classificadas corretamente como deputado eleito e 97% s?o classificadas corretamente como deputado n?o eleitos.

Interprete as sa?das dos modelos. Quais atributos parecem ser mais importantes de acordo com cada modelo? Crie pelo menos um novo atributo que n?o est? nos dados originais e estude o impacto desse atributo.

Os gr?ficos exibem as 10 vari?veis mais importantes por ordem de import?ncia para cada modelo.

ggplot(varImp(reg_log), top = 10) +
theme(axis.title.y=element_blank()) +
ggtitle("Regress?o log?stica - Vari?vel X Import?ncia")

ggplot(varImp(arvore), top = 10) +
theme(axis.title.y=element_blank()) +
ggtitle("?rvore de decis?o - Vari?vel X Import?ncia")

ggplot(varImp(adaboost), top = 10) +
theme(axis.title.y=element_blank()) +
ggtitle("Adaboost - Vari?vel X Import?ncia")

Vari?veis relacionadas a despesa ou receita aparecem dentre as mais importantes para os 3 modelos, como exemplo total_despesa e media_receita.

Envie seus melhores modelos ? competi??o do Kaggle.

test <- read.csv("../dados/test_lab4.csv", encoding = "UTF-8")
submission <- read.csv("../dados/sample_submission_lab4.csv")

submission_predict <- predict(adaboost, test)

for(i in 1:length(submission_predict)){
  print(submission_predict[i])
  submission$prediction[i] = submission_predict[i]
}

write.csv(submission, file = "../dados/submission.csv", row.names = FALSE)
LS0tCnRpdGxlOiAiUHJlZGk/P28gZGUgRGVwdXRhZG9zIEVsZWl0b3MiCmF1dGhvcjogIkRpb2dvIEZsb3I/bmNpbyIKZGF0ZTogIjEgZGUgbWFyP28gZGUgMjAxOCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBmaWdfaGVpZ2h0OiA0CiAgICBmaWdfd2lkdGg6IDUKICAgIHRoZW1lOiByZWFkYWJsZQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KFJPU0UpCmxpYnJhcnkobWlzc0ZvcmVzdCkKb3B0aW9ucyhzY2lwZW4gPSA0KSAjZXNjYWxhIGRvcyB2YWxvcmVzIGV4aWJpZG9zCmRhdGEgPC0gcmVhZC5jc3YoIi4uL2RhZG9zL3RyYWluX2xhYjQuY3N2IiwgZW5jb2RpbmcgPSAiVVRGLTgiKQoKIyBTZXBhcmEgb3MgZGFkb3MgZW0gdHJlaW5vIGUgdGVzdGUKZGF0YVBhcnRpdGlvbiA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHkgPSBkYXRhJHNpdHVhY2FvX2ZpbmFsLCBwPTAuNzUsIGxpc3Q9RkFMU0UpCiMgU2UgbyB5IGZvciB1bSB2ZWN0b3IgZGUgZmFjdG9yIGEgZGl2aXM/byA/IGZlaXRhIHRlbnRhbmRvIGJhbGFuY2VhciBhIGRpc3RyaWJ1aT8/byBkZSBjbGFzc2VzIGRlICMgeSBkZW50cm8gZGFzIHBhcnRpPz9lcy4KCnRyYWluIDwtIGRhdGFbIGRhdGFQYXJ0aXRpb24sIF0KdmFsaWRhdGlvbiA8LSBkYXRhWyAtZGF0YVBhcnRpdGlvbiwgXQpgYGAKCk9zIGRhZG9zIGRlIHRyZWlubyBhcHJlc2VudGFtIG11aXRvcyB2YWxvcmVzIGZhbHRhbnRlcyByZXByZXNlbnRhZG9zIHBvciBgMGAsIHBhcmEgcmVzb2x2ZXIgZXN0ZSBwcm9ibGVtYSBVdGlsaXplaSBvIG0/dG9kbyBtaXNzRm9yZXN0LCBkbyBwYWNvdGUgbWlzc0ZvcmVzdC4gRXN0ZSBtP3RvZG8gY29uc3Rybz8gdW0gbW9kZWxvIGRlIGBmbG9yZXN0YSBhbGVhdD9yaWFgIHBhcmEgY2FkYSB2YXJpP3ZlbCBlIGVudD9vIHV0bGl6YSBwYXJhIHByZXZlciBvcyB2YWxvcmVzIGRhcyB2YXJpP3ZlaXMgZmFsdGFudGVzIHBvciBtZWlvIGRvcyB2YWxvcmVzIG9ic2VydmFkb3MuCgpBbGd1bWFzIHZhcmk/dmVpcyBpcnJlbGV2YW50ZXMgZm9yYW0gcmVtb3ZpZGFzIGNvbW8gYElEYCBlIGBub21lYC4KCmBgYHtyfQojcmVtb3ZlbmRvIHZhcmk/dmVpcyBpcnJlbGV2YW50ZXMKdHJhaW4gPC0gdHJhaW4gJT4lIHNlbGVjdCgtSUQsIC1ub21lLCAtbnVtZXJvX2NhZGlkYXRvLCAtc2V0b3JfZWNvbm9taWNvX3JlY2VpdGEsIC1zZXRvcl9lY29ub21pY29fZGVzcGVzYSkgCnZhbGlkYXRpb24gPC0gdmFsaWRhdGlvbiAlPiUgc2VsZWN0KC1JRCwgLW5vbWUsIC1udW1lcm9fY2FkaWRhdG8sIC1zZXRvcl9lY29ub21pY29fcmVjZWl0YSwgLXNldG9yX2Vjb25vbWljb19kZXNwZXNhKQoKI28gbT90b2RvIG1pc3NGb3Jlc3Qgbj9vIGV4ZWN1dGEgcGFyYSB2YXJpP3ZlaXMgY29tIG1haXMgZGUgNTMgY2F0ZWdvcmlhcywgbmVzdGUgY2FzbyBvcHRlaSBwb3IgcmVtb3ZlciBhIHZhcmk/dmVsIGRlc2NyaWNhb19vY3VwYWNhbwp0cmFpbiA8LSB0cmFpbiAlPiUgc2VsZWN0KC1kZXNjcmljYW9fb2N1cGFjYW8pCnZhbGlkYXRpb24gPC0gdmFsaWRhdGlvbiAlPiUgc2VsZWN0KC1kZXNjcmljYW9fb2N1cGFjYW8pCgojdHJhbnNmb3JtYW5kbyB2YWxvcmVzIGZhbHRhbnRlcyBlbSBOQQp0cmFpblt0cmFpbiA9PSAwXSA8LSBOQQp2YWxpZGF0aW9uW3ZhbGlkYXRpb24gPT0gMF0gPC0gTkEKCiN0cmF0YW5kbyBtaXNzaW5nIGRhdGEgCnRyYWluIDwtIG1pc3NGb3Jlc3QodHJhaW4pJHhpbXAKdmFsaWRhdGlvbiA8LSBtaXNzRm9yZXN0KHZhbGlkYXRpb24pJHhpbXAKYGBgCgojI0g/IGRlc2JhbGFuY2VhbWVudG8gZGFzIGNsYXNzZXMgKGlzdG8gPywgdW1hIGNsYXNzZSB0ZW0gbXVpdG8gbWFpcyBpbnN0P25jaWFzIHF1ZSBvdXRyYSk/IEVtIHF1ZSBwcm9wb3I/P28/IFF1YWlzIGVmZWl0b3MgY29sYXRlcmFpcyBvIGRlc2JhbGFuY2VhbWVudG8gZGUgY2xhc3NlcyBwb2RlIGNhdXNhciBubyBjbGFzc2lmaWNhZG9yPy4KCkNsYXNzZXMgZGVzYmFsYW5jZWFkYXMgb2NvcnJlbSBxdWFuZG8gZXhpc3RlIHVtYSBncmFuZGUgZGVzcHJvcG9yPz9vIGVudHJlIG8gbj9tZXJvIGRlIGV4ZW1wbG9zIGRlIGNhZGEgY2xhc3NlLiBFc3NhIHNpdHVhPz9vIGZyZXF1ZW50ZW1lbnRlIGZheiBjb20gcXVlIG9zIGV4ZW1wbG9zIGRhIGNsYXNzZSBtaW5vcml0P3JpYSBzZWphbSBjbGFzc2lmaWNhZG9zIGluY29ycmV0YW1lbnRlLgoKYGBge3J9CnByb3AudGFibGUodGFibGUodHJhaW4kc2l0dWFjYW9fZmluYWwpKQpgYGAKCkFuYWxpc2FuZG8gb3MgZGFkb3MgZGUgdHJlaW5vID8gZj9jaWwgcGVyY2ViZXIgbyBncmFuZGUgZGVzYmFsYW5jZWFtZW50byBlbnRyZSBjbGFzc2VzLCBuZXN0ZSBjYXNvIGEgY2xhc3NlIGBuYW9fZWxlaXRvc2AgY29tcHJlZW5kZSBhcHJveGltYWRhbWVudGUgOTAlIGRvcyBleGVtcGxvcyBkZSB0cmVpbm8uCgpQYXJhIHJlc29sdmVyIGVzc2UgcHJvYmxlbWEgdXRpbGl6ZWkgbyBtP3RvZG8gZGUgYmFsYW5jZWFtZW50byBST1NFLCBkbyBwYWNvdGUgUk9TRSwgZXN0ZSBtP3RvZG8gZ2VyYSBkYWRvcyBkZSBmb3JtYSBzaW50P3RpY2EgYnVzY2FuZG8gcHJlc2VydmFyIGFzIGNhcmFjdGVyP3N0aWNhcyBkb3MgZGFkb3Mgb3JpZ2luYWlzLgoKYGBge3J9CnRyYWluIDwtIFJPU0Uoc2l0dWFjYW9fZmluYWwgfiAuLCBkYXRhID0gdHJhaW4sIHNlZWQgPSAxKSRkYXRhCnByb3AudGFibGUodGFibGUodHJhaW4kc2l0dWFjYW9fZmluYWwpKQpgYGAKCkFnb3JhIGFzIGNsYXNzZXMgYXByZXNlbnRhbSB1bWEgcXVhbnRpZGFkZSBwcm9wb3JjaW9uYWwgZGUgZXhlbXBsb3MsIGBuYW9fZWxlaXRvc2AgNTIlIGUgYGVsZWl0b3NgIDQ4JS4KCiMjVHJlaW5lOiB1bSBtb2RlbG8gZGUgcmVncmVzcz9vIGxvZz9zdGljYSwgdW1hID9ydm9yZSBkZSBkZWNpcz9vIGUgdW0gbW9kZWxvIGRlIGFkYWJvb3N0LiBUdW5lIGVzc2VzIG1vZGVsb3MgdXNhbmRvIHZhbGlkYT8/byBjcnV6YWRhIGUgY29udHJvbGUgb3ZlcmZpdHRpbmcgc2UgbmVjZXNzP3JpbywgY29uc2lkZXJhbmRvIGFzIHBhcnRpY3VsYXJpZGFkZXMgZGUgY2FkYSBtb2RlbG8uCgojIyNSZWdyZXNzP28gTG9nP3N0aWNhCgpPIG1vZGVsbyBkZSByZWdyZXNzP28gbG9nP3N0aWNhIGZvaSB0dW5hZG8gY29uZm9ybWUgYSBmdW4/P28gZGVmaW5pZGEgZW0gYGZpdGNvbnRyb2xgLCBvIGhpcGVycGFyYW1ldHJvIGRvIG1vZGVsbyA/IG90aW1pemFkbyBwb3IgbWVpbyBkZSB0ZXN0ZXMgZW0gYG5gIHZhbG9yZXMgYWxlYXQ/cmlvcy4gVG9kb3Mgb3MgbW9kZWxvcyBjb25zaWRlcmFtIDUtZm9sZCBwYXJhIHZhbGlkYT8/byBjcnV6YWRhLgoKYGBge3J9CmZpdENvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsCiAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gNSwKICAgICAgICAgICAgICAgICAgICBzZWFyY2g9ICJyYW5kb20iKQoKZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHNpdHVhY2FvX2ZpbmFsIH4uKQoKIyBSZWdyZXNzP28gbG9nP3N0aWNhIGdsbSAoZ2VuZXJhbGl6ZWQgbGluZWFyIG1vZGVsKQpyZWdfbG9nIDwtIHRyYWluKGZvcm11bGEsCiAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluLAogICAgICAgICAgICAgICAgIG1ldGhvZD0iZ2xtIiwKICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBmaXRDb250cm9sLAogICAgICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiLAogICAgICAgICAgICAgICAgIG5hLmFjdGlvbiA9IG5hLm9taXQpCmBgYAoKIyMjP3J2b3JlIGRlIGRlY2lzP28KCkEgP3J2b3JlIGRlIGRlY2lzP28gZm9pIHR1bWFkYSBkZWZpbmluZG8gMTAgdmFsb3JlcyBkaXN0aW50b3MgcGFyYSBzZXUgaGlwZXJwYXI/bWV0cm8gZGUgY29tcGxleGlkYWRlIG5vIGludGVydmFsbyBbMCwgMC4wMV0uIAoKYGBge3J9CiNpbnRlcnZhbG8gZG8gaGlwZXJwYXI/bWV0cm8KdmFsdWVzX2NwIDwtIGV4cGFuZC5ncmlkKGZyYWN0aW9uID0gc2VxKDAsIDAuMDEsIGxlbmd0aCA9IDEwKSkKCmFydm9yZSA8LSB0cmFpbihmb3JtdWxhLAogICAgICAgICAgICAgICAgIGRhdGE9dHJhaW4sCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJwYXJ0IiwKICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBmaXRDb250cm9sLAogICAgICAgICAgICAgICAgIGNwID0gdmFsdWVzX2NwLCAgIyBoaXBlcnBhcj9tZXRybyBkZSBjb21wbGV4aWRhZGUKICAgICAgICAgICAgICAgICBtYXhkZXB0aCA9IDIwLAogICAgICAgICAgICAgICAgIG5hLmFjdGlvbiA9IG5hLm9taXQpCnBsb3QoYXJ2b3JlKQpgYGAKCk8gbWVsaG9yIHZhbG9yIG9ic2VydmFkbyBmb2kgcGFyYSB1bSBgY3BgIGRlIGFwcm94aW1hZGFtZW50ZSAwLjAwMTIuCgojIyNNb2RlbG8gQWRhYm9vc3QKCk8gbW9kZWxvIGFkYWJvb3N0IGZvaSB0dW5hZG8gcG9yIG1laW8gZG8gdGVzdGUgZGUgYG5gIHZhbG9yZXMgcGFyYSBzZXUgaGlwZXJwYXI/bWV0cm8gYG4/bWVybyBkZSA/cnZvcmVzYC4KCmBgYHtyfQphZGFib29zdCA8LSB0cmFpbihmb3JtdWxhLAogICAgICAgICAgICAgICAgICBkYXRhPXRyYWluLAogICAgICAgICAgICAgICAgICBtZXRob2QgPSAiYWRhYm9vc3QiLAogICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBmaXRDb250cm9sLAogICAgICAgICAgICAgICAgICBuYS5hY3Rpb24gPSBuYS5vbWl0KQpwbG90KGFkYWJvb3N0KQpgYGAKCk8gbWVsaG9yIHZhbG9yIG9ic2VydmFkbyBmb2kgcGFyYSA5MDAgP3J2b3Jlcy4KCiMjUmVwb3J0ZSBhY3VyP2NpYSwgcHJlY2lzaW9uLCByZWNhbGwgZSBmLW1lYXN1cmUgbm8gdHJlaW5vIGUgdmFsaWRhPz9vLiBDb21vIHZvYz8gYXZhbGlhIG9zIHJlc3VsdGFkb3M/IEp1c3RpZmlxdWUgc3VhIHJlc3Bvc3RhLgoKIyMjUmVncmVzcz9vIGxvZz9zdGljYQoKYGBge3J9CnRyYWluJHByZWRpY3QgPC0gcHJlZGljdChyZWdfbG9nLCB0cmFpbikKY29uZnVzaW9uTWF0cml4KHRyYWluJHByZWRpY3QsIHRyYWluJHNpdHVhY2FvX2ZpbmFsKQpgYGAKCmBgYHtyfQp2YWxpZGF0aW9uJHByZWRpY3QgPC0gcHJlZGljdChyZWdfbG9nLCB2YWxpZGF0aW9uKQpjb25mdXNpb25NYXRyaXgodmFsaWRhdGlvbiRwcmVkaWN0LCB2YWxpZGF0aW9uJHNpdHVhY2FvX2ZpbmFsKQpgYGAKVGFudG8gY29tIG9zIGRhZG9zIGRlIHRyZWlubyBjb21vIGNvbSBvcyBkYWRvcyBkZSB0ZXN0ZSBvIG1vZGVsbyBwcmVzZXJ2b3Ugc3VhIGFjdXI/Y2lhIGVtIHRvcm5vIGRlIDkwJS4gQSBwcmVjaXM/byBvdSBzZW5zaWJpbGlkYWRlIGRvIG1vZGVsbyBmaWNvdSBlbSB0b3JubyBkZSA5MiwxNSUsIGluZGljYW5kbyBhIHByb3Bvcj8/byBkZSBhY2VydG8gZG8gbW9kZWxvIGFvIGNsYXNzaWZpY2FyIHVtIGRlcHV0YWRvIHF1ZSBzZXI/IGVsZWl0by4gTyByZWNhbGwgb3UgZXNwZWNpZmljaWRhZGUgZmljb3UgZW0gdG9ybm8gZGUgODklIHNlbmRvIHJlc3BvbnM/dmVsIHBlbGEgcHJvcG9yPz9vIGRlIGFjZXJ0b3MgZG8gbW9kZWxvIGFvIGNsYXNzaWZpY2FyIHVtIGRlcHV0YWRvIHF1ZSBuP28gc2VyPyBlbGVpdG8uICAgCgoKIyMjP3J2b3JlIGRlIGRlY2lzP28KCmBgYHtyfQp0cmFpbiRwcmVkaWN0IDwtIHByZWRpY3QoYXJ2b3JlLCB0cmFpbikKY29uZnVzaW9uTWF0cml4KHRyYWluJHByZWRpY3QsIHRyYWluJHNpdHVhY2FvX2ZpbmFsKQpgYGAKCmBgYHtyfQp2YWxpZGF0aW9uJHByZWRpY3QgPC0gcHJlZGljdChhcnZvcmUsIHZhbGlkYXRpb24pCmNvbmZ1c2lvbk1hdHJpeCh2YWxpZGF0aW9uJHByZWRpY3QsIHZhbGlkYXRpb24kc2l0dWFjYW9fZmluYWwpCmBgYApBIGFjdXI/Y2lhIGRhID9ydm9yZSBkZSBkZWNpcz9vIGZpY291IGVtIHRvcm5vIGRlIDk0JS4gRW0gdG9ybm8gZGUgODclIGRhcyBvYnNlcnZhPz9lcyBzP28gY2xhc3NpZmljYWRhcyBjb3JyZXRhbWVudGUgY29tbyBkZXB1dGFkbyBlbGVpdG8gZSA5NyUgcz9vIGNsYXNzaWZpY2FkYXMgY29ycmV0YW1lbnRlIGNvbW8gZGVwdXRhZG8gbj9vIGVsZWl0b3MuCgojIyNBZGFib29zdAoKYGBge3J9CnRyYWluJHByZWRpY3QgPC0gcHJlZGljdChhZGFib29zdCwgdHJhaW4pCmNvbmZ1c2lvbk1hdHJpeCh0cmFpbiRwcmVkaWN0LCB0cmFpbiRzaXR1YWNhb19maW5hbCkKYGBgCgpgYGB7cn0KdmFsaWRhdGlvbiRwcmVkaWN0IDwtIHByZWRpY3QoYWRhYm9vc3QsIHZhbGlkYXRpb24pCmNvbmZ1c2lvbk1hdHJpeCh2YWxpZGF0aW9uJHByZWRpY3QsIHZhbGlkYXRpb24kc2l0dWFjYW9fZmluYWwpCmBgYAoKQWRhYm9vc3QgYXByZXNlbnRvdSBhY3VyP2NpYSBlbSB0b3JubyBkZSAxMDAlLiA5MCUgZGFzIG9ic2VydmE/P2VzIHM/byBjbGFzc2lmaWNhZGFzIGNvcnJldGFtZW50ZSBjb21vIGRlcHV0YWRvIGVsZWl0byBlIDk3JSBzP28gY2xhc3NpZmljYWRhcyBjb3JyZXRhbWVudGUgY29tbyBkZXB1dGFkbyBuP28gZWxlaXRvcy4KCiMjSW50ZXJwcmV0ZSBhcyBzYT9kYXMgZG9zIG1vZGVsb3MuIFF1YWlzIGF0cmlidXRvcyBwYXJlY2VtIHNlciBtYWlzIGltcG9ydGFudGVzIGRlIGFjb3JkbyBjb20gY2FkYSBtb2RlbG8/IENyaWUgcGVsbyBtZW5vcyB1bSBub3ZvIGF0cmlidXRvIHF1ZSBuP28gZXN0PyBub3MgZGFkb3Mgb3JpZ2luYWlzIGUgZXN0dWRlIG8gaW1wYWN0byBkZXNzZSBhdHJpYnV0by4KCk9zIGdyP2ZpY29zIGV4aWJlbSBhcyAxMCB2YXJpP3ZlaXMgbWFpcyBpbXBvcnRhbnRlcyBwb3Igb3JkZW0gZGUgaW1wb3J0P25jaWEgcGFyYSBjYWRhIG1vZGVsby4KCmBgYHtyfQpnZ3Bsb3QodmFySW1wKHJlZ19sb2cpLCB0b3AgPSAxMCkgKwp0aGVtZShheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpKSArCmdndGl0bGUoIlJlZ3Jlc3M/byBsb2c/c3RpY2EgLSBWYXJpP3ZlbCBYIEltcG9ydD9uY2lhIikKYGBgCgpgYGB7cn0KZ2dwbG90KHZhckltcChhcnZvcmUpLCB0b3AgPSAxMCkgKwp0aGVtZShheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpKSArCmdndGl0bGUoIj9ydm9yZSBkZSBkZWNpcz9vIC0gVmFyaT92ZWwgWCBJbXBvcnQ/bmNpYSIpCmBgYAoKYGBge3J9CmdncGxvdCh2YXJJbXAoYWRhYm9vc3QpLCB0b3AgPSAxMCkgKwp0aGVtZShheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpKSArCmdndGl0bGUoIkFkYWJvb3N0IC0gVmFyaT92ZWwgWCBJbXBvcnQ/bmNpYSIpCmBgYAoKVmFyaT92ZWlzIHJlbGFjaW9uYWRhcyBhIGRlc3Blc2Egb3UgcmVjZWl0YSBhcGFyZWNlbSBkZW50cmUgYXMgbWFpcyBpbXBvcnRhbnRlcyBwYXJhIG9zIDMgbW9kZWxvcywgY29tbyBleGVtcGxvIGB0b3RhbF9kZXNwZXNhYCBlIGBtZWRpYV9yZWNlaXRhYC4KCiMjRW52aWUgc2V1cyBtZWxob3JlcyBtb2RlbG9zID8gY29tcGV0aT8/byBkbyBLYWdnbGUuCgpgYGB7cn0KdGVzdCA8LSByZWFkLmNzdigiLi4vZGFkb3MvdGVzdF9sYWI0LmNzdiIsIGVuY29kaW5nID0gIlVURi04IikKc3VibWlzc2lvbiA8LSByZWFkLmNzdigiLi4vZGFkb3Mvc2FtcGxlX3N1Ym1pc3Npb25fbGFiNC5jc3YiKQoKc3VibWlzc2lvbl9wcmVkaWN0IDwtIHByZWRpY3QoYWRhYm9vc3QsIHRlc3QpCgpmb3IoaSBpbiAxOmxlbmd0aChzdWJtaXNzaW9uX3ByZWRpY3QpKXsKICBwcmludChzdWJtaXNzaW9uX3ByZWRpY3RbaV0pCiAgc3VibWlzc2lvbiRwcmVkaWN0aW9uW2ldID0gc3VibWlzc2lvbl9wcmVkaWN0W2ldCn0KCndyaXRlLmNzdihzdWJtaXNzaW9uLCBmaWxlID0gIi4uL2RhZG9zL3N1Ym1pc3Npb24uY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoK