Primeira Questão

Explique com suas palavras o dilema de balanço entre viés e variância.

Resposta :

O viés refere á diferença entre as previsões médias do modelo e os valores reais que estamos tentando prever.

Variância é a sensibilidade do modelo ás variações nos dados de treinamento.

Segunda Questão

Explique resumidamente o que é aprendizagem supervisionada e não-supervisionada. Cite um problema de aprendizagem supervisionada e um outro de aprendizagem não-supervisionada.

Resposta:

Aprendizagem supervisionada usamos o método de classificação, o objetivo é mapear as entradas e as saídas com base nos padrões identificados nos dados de treinamento.

Aprendizagem não-supervisionada usamos o método de classificação, o objetivo é encontrar padrões, estruturas ou relações intrínsecas nos dados sem que haja informações prévias sobre as saídas desejadas.

Terceira Questão

Explique qual o motivo que faz com que o Erro Quadrático Médio-EQM para avaliar o desempenho de um modelo é ruim quando não adotamos nenhuma estratégia de divisão do conjunto de dados em treinamento e teste.

Resposta:

Overfitting (Sobreajuste): Quando um modelo é treinado em todo o conjunto de dados, ele pode aprender detalhes e ruídos específicos dos dados de treinamento que não são generalizáveis para novos dados. Isso pode resultar em um modelo que se ajusta muito bem aos dados de treinamento, mas que performa mal em dados não vistos anteriormente. O EQM pode ser baixo nos dados de treinamento, mas alto nos dados de teste devido ao overfitting.

Avaliação inadequada da generalização: A principal razão para dividir o conjunto de dados em treinamento e teste é avaliar como o modelo generaliza para dados não vistos. Quando não há divisão e o modelo é treinado em todos os dados, não há uma avaliação realista da capacidade do modelo de fazer previsões precisas em novos dados, que é o objetivo final da maioria das aplicações.


load("C:/Users/elton/Downloads/dados_expectativa_renda (3).RData")
library(rsample)
library(dplyr)
library(ggplot2)
library(tidymodels)
library(tibble)
library(purrr)
library(patchwork)
tidymodels::tidymodels_prefer()

Quarta Questão

Considere o conjunto de dados de Expectativa de vida versus PIB per Capita. Considere a função g uma regressão polinomia. Utilizando o erro quadrático médio observado, sem fazer nenhuma estratégia de divisão dos dados, implemente um código em R para checar qual o melhor modelo. p = 1, …, 50

set.seed(2023)
p = 50
dados_exp <- dados_expectativa_renda[,-1]
colnames(dados_exp) <- c("y", "x")

for(i in 1:p){
  if(i > 1){
    temp <- cbind(temp, temp[,2]^i)
    colnames(temp) <- c("y", "x", paste0("x", 2:i))
    fit <- lm(y~. , data = temp)
    eqm[i] <- mean((temp$y - predict(fit, newdata = temp))^2)
    i = i + 1
  }
  else{
    fit <- lm(y~. , data = dados_exp)
    eqm <- mean((dados_exp$y - predict(fit, newdata = dados_exp))^2)
    temp <- dados_exp
  }
}
eqm1 <- data.frame("P" = 1:50, "EQM" = eqm)

g <- ggplot(eqm1, aes(P, EQM)) + 
  geom_line(linetype = 2, size = 0.4) + 
  geom_point(size = 2, aes(colour = -EQM)) +
  scale_fill_brewer(palette = 1) + 
  labs(x = "P", y = "EQM",
       title = "Avaliação do EQM",
       subtitle = "Sem repartir conjunto treino / teste")
g

Quinta Questão

Refaça o exercício anterior do polinômio, utilizando a estratégia de data splitting, em que divide-se o conjunto de dados em treinamento e teste. Utilize o conjunto de teste para calcular a estimativa do risco, usando o EQM

set.seed(2023)
p = 50
dados_exp <- dados_expectativa_renda[,-1]
colnames(dados_exp) <- c("y", "x")
dados_exp <- initial_split(dados_exp, prop = 3/4)
trainer <- training(dados_exp)
tester <- testing(dados_exp)

eqm2 = data.frame(P = 1:50, y = NA)
for(i in 1:p){
  if(i > 1){
    trainer <- cbind(trainer, trainer[,2]^i)
    colnames(trainer) <- c("y", "x", paste0("x", 2:i))
    fit <- lm(y~., trainer)
    
    ## Avaliando no conjunto de teste
    tester <- cbind(tester, tester[,2]^i)
    colnames(tester) <- c("y", "x", paste0("x", 2:i))
    eqm2[i,2] <- mean((tester[,1] - predict(fit, tester))^2)
  }
  else{
    fit <- lm(y~., trainer)
    eqm2[i,2] <- mean((tester[,1] - predict(fit, tester))^2)
  }
}

colnames(eqm2) <- c("P", "EQM")
g <- ggplot(eqm2, aes(P, EQM)) + 
  geom_line(linetype = 2, size = 0.4) + 
  geom_point(size = 2, aes(colour = -EQM)) +
  labs(x = "P", y = "EQM",
       title = "Avaliação do EQM",
       subtitle = "Repartindo conjunto treino/teste, sem validação cruzada") +
  xlim(c(0,18)) + ylim(c(10,130))

g

Sexta Questão

Ainda considerando o exercício do polinômio, implemente uma estratégia de leave-one-out cross-validation e selecione o melhor modelo minimizando a função de risco.

set.seed(2023)
p = 50
dados_exp <- dados_expectativa_renda[,-1]
colnames(dados_exp) <- c("y", "x")
dados_exp <- initial_split(dados_exp, prop = 3/4)
trainer <- training(dados_exp)
tester <- testing(dados_exp)

leave <- loo_cv(trainer)

results1 <- data.frame(P = NA, EQM = NA)
for(b in 1:dim(leave)[1]){
  eqm <- NULL
  
  for(i in 1:p){
    if(i > 1){
      temp_trainer <- leave$splits[[b]] %>% analysis()
      temp_tester <- leave$splits[[b]] %>% assessment()
      for(j in 2:i){
        temp_trainer <- cbind(temp_trainer, temp_trainer$x^j)
        temp_tester <- cbind(temp_tester, temp_tester$x^j)
        colnames(temp_trainer) <- c("y", "x", paste0("x", 2:j))
        colnames(temp_tester) <- c("y", "x", paste0("x", 2:j))
      }
      
      fit <- lm(y~., temp_trainer)
      eqm[i] <- as.numeric((temp_tester[,1] - predict(fit, temp_tester))^2)
    }
    else{
      temp_trainer <- leave$splits[[b]] %>% analysis()
      temp_tester <- leave$splits[[b]] %>% assessment()
      fit <- lm(y~., temp_trainer)
      eqm[i] <- as.numeric((temp_tester[,1] - predict(fit, newdata = temp_tester))^2)
    }
  }
  
  results1[b,1] <- which(eqm == min(eqm))[1]
  results1[b,2] <- min(eqm) 
}

Resposta:

Polinômio de Grau 7 apresentou o menor EQM, no valor de \(4.5994799^{-6}\).

Sétima Questão

Por fim, considerando o exercício do polinômio, rafaça-o utilizando um procedimento de k-fold cross-validation. Considere k=5. Dica: considere utilizar a biblioteca rsample.


set.seed(2023)
p = 50
k = 5
dados_exp <- dados_expectativa_renda[,-1]
colnames(dados_exp) <- c("y", "x")
dados_exp <- initial_split(dados_exp, prop = 3/4)
trainer <- training(dados_exp)
tester <- testing(dados_exp)

vfold <- vfold_cv(trainer, v = 5)

results2 <- data.frame(P = NA, EQM = NA)
for(b in 1:dim(vfold)[1]){
  eqm <- NULL
  
  for(i in 1:p){
    if(i > 1){
      temp_trainer <- vfold$splits[[b]] %>% analysis()
      temp_tester <- vfold$splits[[b]] %>% assessment()
      for(j in 2:i){
        temp_trainer <- cbind(temp_trainer, temp_trainer$x^j)
        temp_tester <- cbind(temp_tester, temp_tester$x^j)
        colnames(temp_trainer) <- c("y", "x", paste0("x", 2:j))
        colnames(temp_tester) <- c("y", "x", paste0("x", 2:j))
      }
      
      fit <- lm(y~., temp_trainer)
      eqm[i] <- mean((temp_tester[,1] - predict(fit, newdata = temp_tester))^2)
    }
    else{
      temp_trainer <- vfold$splits[[b]] %>% analysis()
      temp_tester <- vfold$splits[[b]] %>% assessment()
      fit <- lm(y~., temp_trainer)
      eqm[i] <- mean((temp_tester[,1] - predict(fit, newdata = temp_tester))^2)
    }
  }
  results2[b,1] <- which(eqm == min(eqm))[1]
  results2[b,2] <- min(eqm) 
}

Resposta :

Polinômio de Grau 10 apresentou o menor EQM, no valor de 19.8729033 para o método k-fold, k=5.

Oitava Questão

Utilizando os dados de vinho vermelho, faça uma pequena análise exploratória dos dados. No link do Kaggle você consegue uma explicação sobre o que significa cada uma das variáveis.

library(dplyr)
wine<-read.csv("C:/Users/elton/Desktop/winequality-red.csv")
wine<- wine%>%rename(c(acidez_fixa=fixed.acidity,acidez_volatil=volatile.acidity,acido_citrico=citric.acid,açucar_residual=residual.sugar,cloretos=chlorides,dioxido_enxofre_livre=free.sulfur.dioxide,dioxido_enxofre_total=total.sulfur.dioxide,densidade=density,sulfatos=sulphates,alcool=alcohol,qualidade=quality))
skimr::skim(wine)
── Data Summary ────────────────────────
                           Values
Name                       wine  
Number of rows             1599  
Number of columns          12    
_______________________          
Column type frequency:           
  numeric                  12    
________________________         
Group variables            None  

Nona Questão

obtenha o melhor modelo de regressão linear para modelar a qualidade do vinho, considerando: Método dos Mínimos Quadrados, Regressão Ridge, Regressão Lasso, Elastic Net. Selecione o melhor modelo para cada uma das classes e contrua uma tabela com o risco estimado. Ao fim construir quatro gráficos mostrando cada um dos ajustes.

library(rsample)
library(tidymodels)
library(workflows)
library(dplyr)
library(ggplot2)
library(tune)
library(tibble)
library(purrr)
library(patchwork)
library(glmnet)      
wine<-read.csv("C:/Users/elton/Desktop/winequality-red.csv")
colnames(wine) <- c(paste0("x", 1:11), "y")
wine <- initial_split(wine, prop = 3/4)

trainer <- training(wine)
tester <- testing(wine)

#Setting Engine
(modelo_mmo <- 
  linear_reg(penalty = 0, mixture = 0) %>% 
  set_mode("regression") %>% 
  set_engine("glmnet"))
Linear Regression Model Specification (regression)

Main Arguments:
  penalty = 0
  mixture = 0

Computational engine: glmnet 
(modelo_ridge <- 
  linear_reg(penalty = tune::tune(), mixture = 0) %>% 
  set_mode("regression") %>% 
  set_engine("glmnet"))
Linear Regression Model Specification (regression)

Main Arguments:
  penalty = tune::tune()
  mixture = 0

Computational engine: glmnet 
(modelo_lasso <- 
  parsnip::linear_reg(penalty = tune::tune(), mixture = 1) %>% 
  set_mode("regression") %>% 
  parsnip::set_engine("glmnet"))
Linear Regression Model Specification (regression)

Main Arguments:
  penalty = tune::tune()
  mixture = 1

Computational engine: glmnet 
(modelo_elastic <- 
  parsnip::linear_reg(penalty = tune::tune(), mixture = tune::tune()) %>% 
  set_mode("regression") %>% 
  parsnip::set_engine("glmnet"))
Linear Regression Model Specification (regression)

Main Arguments:
  penalty = tune::tune()
  mixture = tune::tune()

Computational engine: glmnet 
#workflow
(all_wf <- 
  workflow_set(
    preproc = list(y ~ . ),
    models = list(mmo = modelo_mmo, ridge = modelo_ridge, lasso = modelo_lasso, elastic = modelo_elastic), 
    cross = TRUE
  ))

#cross-validation
set.seed(2023)
(cv <-vfold_cv(trainer, v = 5L))
#  5-fold cross-validation 
#metrics
(metrica <- yardstick::metric_set(rmse))

#tunning
tunagem <- 
  all_wf %>%  
  workflow_map(
    seed = 2023, 
    verbose = TRUE,
    resamples = cv,
    grid = 50,
    metrics = metrica
  )
  
library(rsample)
library(purrr)
library(dplyr)
library(workflowsets)

modelos_rank <- tunagem %>% rank_results()

melhor_mmo <- 
  tunagem %>% 
  extract_workflow_set_result("formula_mmo") %>% 
  select_best("rmse")

melhor_ridge <- 
  tunagem %>% 
  extract_workflow_set_result("formula_ridge") %>% 
  select_best("rmse")

melhor_lasso <- 
  tunagem %>% 
  extract_workflow_set_result("formula_lasso") %>% 
  select_best("rmse")

melhor_elastic <- 
  tunagem %>% 
  extract_workflow_set_result("formula_elastic") %>% 
  select_best("rmse")

finalizando_mmo <- 
  tunagem %>% 
  extract_workflow("formula_mmo") %>% 
  finalize_workflow(melhor_mmo) %>% 
  last_fit(split = wine)

finalizando_ridge <- 
  tunagem %>% 
  extract_workflow("formula_ridge") %>% 
  finalize_workflow(melhor_ridge) %>% 
  last_fit(split = wine)

finalizando_lasso <- 
  tunagem %>% 
  extract_workflow("formula_lasso") %>% 
  finalize_workflow(melhor_lasso) %>% 
  last_fit(split = wine)

finalizando_elastic <- 
  tunagem %>% 
  extract_workflow("formula_elastic") %>% 
  finalize_workflow(melhor_elastic) %>% 
  last_fit(split = wine)
knitr::kable(data.frame(row.names = c("MMO", "Ridge", "Lasso", "Elastic"), 
                        "RMSE" = as.numeric(c((finalizando_mmo  %>%  collect_metrics())[1,3],
                                              (finalizando_ridge %>% collect_metrics())[1,3],
                                              (finalizando_lasso %>% collect_metrics())[1,3],
                                              (finalizando_elastic %>% collect_metrics())[1,3])))
             
             
)
RMSE
MMO 0.6456993
Ridge 0.6456993
Lasso 0.6465983
Elastic 0.6455892
data_temp <- data.frame(ychapeu_mmo = (finalizando_mmo %>% collect_predictions())[2],
                        ychapeu_ridge = (finalizando_ridge %>% collect_predictions())[2],
                        ychapeu_lasso = (finalizando_lasso %>% collect_predictions())[2],
                        ychapeu_elastic = (finalizando_elastic %>% collect_predictions())[2],
                        y = (finalizando_elastic %>% collect_predictions())[4]
)

colnames(data_temp) <- c("ychapeu_mmo", "ychapeu_ridge", "ychapeu_lasso", "ychapeu_elastic", "y")
data_temp <- as_tibble(data_temp)


graph <- ggplot(data_temp, aes(x = ychapeu_mmo, y = y)) +
  geom_point() + geom_line(aes(y = ychapeu_mmo), colour = "red", size = 1) +
  labs(title = "Reta ajustada", subtitle = "y_ajustado vs y_real do modelo MMO")

graph

wine <- read.csv("winequality-red.csv")

colnames(wine) <- c(paste0("x", 1:11), "y")
#Data split
wine <- initial_split(wine, prop = 3/4)

trainer <- training(wine)
tester <- testing(wine)

features <- trainer[,-12] %>% as.matrix()
ytrainer <- trainer[,12] %>% as.matrix()
testfeatures <- tester[,-12] %>% as.matrix()
ytest <- tester[,12] %>% as.matrix()

#MMO

fit <- glmnet(x = features, y = ytrainer, alpha = 0, lambda = 0)
preditos <- predict(fit, newx = testfeatures)
eqm_mmo <- mean((preditos - ytest)^2)


#Ridge
cvridge <- cv.glmnet(x = features, y = ytrainer, alpha = 0, nfolds = 5)
fit2 <- glmnet(x = features, y = ytrainer, alpha = 0)
preditos_ridge <- predict(fit2, s = cvridge$lambda.1se, newx = testfeatures)
eqm_ridge <- mean((preditos_ridge - ytest)^2)

#Lasso
cvlasso <- cv.glmnet(x = features, y = ytrainer, alpha = 1, nfolds = 5)
fit3 <- glmnet(x = features, y = ytrainer, alpha = 1)
preditos_lasso <- predict(fit3, s = cvlasso$lambda.1se, newx = testfeatures)
eqm_lass <- mean((preditos_lasso - ytest)^2)

#Elastic-net
#Precisamos estipular o melhor alpha através de uma validaçao cruzada.
#Utilizando 5-folds.
eqm_elastic <- NULL
alpha_values <- seq(0.01, 0.999, length.out = 500)
for(a in alpha_values){
  cvelastic <- cv.glmnet(x = features, y = ytrainer, alpha = a, nfolds = 5)
  tempfit <- glmnet(x = features, y = ytrainer, alpha = a)
  preditos_elastic <- predict(tempfit, s = cvelastic$lambda.1se, newx = testfeatures)
  eqm_elastic <- c(eqm_elastic, 
                   mean((preditos_elastic - ytest)^2)) 
}
min(eqm_elastic)
[1] 0.4219499
alpha_values[which(eqm_elastic == min(eqm_elastic))]
[1] 0.9633246
knitr::kable(data.frame(row.names = c("MMO", "Ridge", "Lasso", "Elastic"),
                        "EQM" = c(eqm_mmo, eqm_ridge, eqm_lass, min(eqm_elastic)),
                        "RMSE" = sqrt(c(eqm_mmo, eqm_ridge, eqm_lass, min(eqm_elastic)))
                        )
)
EQM RMSE
MMO 0.4166706 0.6455003
Ridge 0.4395210 0.6629638
Lasso 0.4278124 0.6540737
Elastic 0.4219499 0.6495767

Decima Questão

Considere agora o conjunto de dados de despesas médicas. Refaça o mesmo exercício dos dados de vinho vermelho, em que aqui, o objetivo é prever a variável charges. Perceba que algumas variáveis são qualitativas, e portanto, você deverá transformá-las em dummy. Indique os melhores cenários dos quatro modelos e informe qual modelo você utilizaria. Explique! Dica: usando a função step_dummy() da biblioteca recipes, você poderá facilmente transformar variáveis qualitativas em numéricas.

library(dplyr)
library(tidymodels)
insurance <- read.csv("C:/Users/elton/Desktop/insurance.csv")
df <- recipe(charges ~., data = insurance) %>% 
  step_dummy(all_nominal(), -all_outcomes()) %>% 
  prep() %>% juice() 

knitr::kable(df %>% head(10), format = "html")
age bmi children charges sex_male smoker_yes region_northwest region_southeast region_southwest
19 27.900 0 16884.924 0 1 0 0 1
18 33.770 1 1725.552 1 0 0 1 0
28 33.000 3 4449.462 1 0 0 1 0
33 22.705 0 21984.471 1 0 1 0 0
32 28.880 0 3866.855 1 0 1 0 0
31 25.740 0 3756.622 0 0 0 1 0
46 33.440 1 8240.590 0 0 0 1 0
37 27.740 3 7281.506 0 0 1 0 0
37 29.830 2 6406.411 1 0 0 0 0
60 25.840 0 28923.137 0 0 1 0 0
df <- initial_split(df, prop = 3/4)

trainer <- training(df)
tester <- testing(df)

#Setting Engine
modelo_mmo <- 
  linear_reg(penalty = 0, mixture = 0) %>% 
  set_mode("regression") %>% 
  set_engine("glmnet")

modelo_ridge <- 
  linear_reg(penalty = tune::tune(), mixture = 0) %>% 
  set_mode("regression") %>% 
  set_engine("glmnet")

modelo_lasso <- 
  parsnip::linear_reg(penalty = tune::tune(), mixture = 1) %>% 
  set_mode("regression") %>% 
  parsnip::set_engine("glmnet")

modelo_elastic <- 
  parsnip::linear_reg(penalty = tune::tune(), mixture = tune::tune()) %>% 
  set_mode("regression") %>% 
  parsnip::set_engine("glmnet")


#workflow
all_wf <- 
  workflow_set(
    preproc = list(charges ~ . ),
    models = list(mmo = modelo_mmo, ridge = modelo_ridge, lasso = modelo_lasso, elastic = modelo_elastic), 
    cross = TRUE
  )

#cross-validation
set.seed(2023)
cv <- rsample::vfold_cv(trainer, v = 5L)

#metrics
metrica <- yardstick::metric_set(rmse)

#tunning
tunagem <- 
  all_wf %>%  
  workflow_map(
    seed = 2023, 
    verbose = TRUE,
    resamples = cv,
    grid = 50,
    metrics = metrica
  )
library(rsample)
library(dplyr)
library(ggplot2)
library(tidymodels)
library(tibble)
library(purrr)
library(patchwork)
library(workflowsets)
library(tune)
library(workflows)
tidymodels::tidymodels_prefer()
modelos_rank <- tunagem %>% rank_results()

melhor_mmo <- 
  tunagem %>% 
  extract_workflow_set_result("formula_mmo") %>% 
  select_best("rmse")

melhor_ridge <- 
  tunagem %>% 
  extract_workflow_set_result("formula_ridge") %>% 
  select_best("rmse")

melhor_lasso <- 
  tunagem %>% 
  extract_workflow_set_result("formula_lasso") %>% 
  select_best("rmse")

melhor_elastic <- 
  tunagem %>% 
  extract_workflow_set_result("formula_elastic") %>% 
  select_best("rmse")

finalizando_mmo <- 
  tunagem %>% 
  extract_workflow("formula_mmo") %>% 
  finalize_workflow(melhor_mmo) %>% 
  last_fit(split = df)

finalizando_ridge <- 
  tunagem %>% 
  extract_workflow("formula_ridge") %>% 
  finalize_workflow(melhor_ridge) %>% 
  last_fit(split = df)

finalizando_lasso <- 
  tunagem %>% 
  extract_workflow("formula_lasso") %>% 
  finalize_workflow(melhor_lasso) %>% 
  last_fit(split = df)

finalizando_elastic <- 
  tunagem %>% 
  extract_workflow("formula_elastic") %>% 
  finalize_workflow(melhor_elastic) %>% 
  last_fit(split = df)


knitr::kable(data.frame(row.names = c("MMO", "Ridge", "Lasso", "Elastic"), 
                        "RMSE" = as.numeric(c((finalizando_mmo  %>%  collect_metrics())[1,3],
                                              (finalizando_ridge %>% collect_metrics())[1,3],
                                              (finalizando_lasso %>% collect_metrics())[1,3],
                                              (finalizando_elastic %>% collect_metrics())[1,3])))
)
RMSE
MMO 6032.497
Ridge 6032.497
Lasso 6046.101
Elastic 6046.310
data_temp <- data.frame(ychapeu_mmo = (finalizando_mmo %>% collect_predictions())[2],
                        ychapeu_ridge = (finalizando_ridge %>% collect_predictions())[2],
                        ychapeu_lasso = (finalizando_lasso %>% collect_predictions())[2],
                        ychapeu_elastic = (finalizando_elastic %>% collect_predictions())[2],
                        y = (finalizando_elastic %>% collect_predictions())[4]
)

colnames(data_temp) <- c("ychapeu_mmo", "ychapeu_ridge", "ychapeu_lasso", "ychapeu_elastic", "y")
data_temp <- as_tibble(data_temp)


g1 <- ggplot(data_temp, aes(x = ychapeu_mmo, y = y)) +
  geom_point() + geom_line(aes(y = ychapeu_mmo), colour = "#191970", size = 1) +
  labs(title = "Modelo MMO", subtitle = "y_ajustado vs y_real")

g2 <- ggplot(data_temp, aes(x = ychapeu_ridge, y = y)) +
  geom_point() + geom_line(aes(y = ychapeu_ridge), colour = "#4169E1", size = 1) +
  labs(title = "Modelo ridge", subtitle = "y_ajustado vs y_real")

g3 <- ggplot(data_temp, aes(x = ychapeu_lasso, y = y)) +
  geom_point() + geom_line(aes(y = ychapeu_lasso), colour = "orange", size = 1) +
  labs(title = "Modelo lasso", subtitle = "y_ajustado vs y_real")

g4 <- ggplot(data_temp, aes(x = ychapeu_elastic, y = y)) +
  geom_point() + geom_line(aes(y = ychapeu_elastic), colour = "red", size = 1) +
  labs(title = "Modelo elastic", subtitle = "y_ajustado vs y_real")


(g1 + g2) / (g3 + g4)

Resposta :

Escolheria o modelo de regressão MMO ou Modelo Ridge.

LS0tDQp0aXRsZTogIlByb3ZhIEFwcmVuZGl6YWdlbSBkYSBNw6FxdWluYXMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogRWx0b24gU29hcmVzDQotLS0NCg0KIyBQcmltZWlyYSBRdWVzdMOjbw0KDQo+DQoNCkV4cGxpcXVlIGNvbSBzdWFzIHBhbGF2cmFzIG8gZGlsZW1hIGRlIGJhbGFuw6dvIGVudHJlIHZpw6lzIGUgdmFyacOibmNpYS4NCg0KPg0KDQojIyMgUmVzcG9zdGEgOiANCg0KTyB2acOpcyByZWZlcmUgw6EgZGlmZXJlbsOnYSBlbnRyZSBhcyBwcmV2aXPDtWVzIG3DqWRpYXMgZG8gbW9kZWxvIGUgb3MgdmFsb3JlcyByZWFpcyBxdWUgZXN0YW1vcyB0ZW50YW5kbyBwcmV2ZXIuDQoNClZhcmnDom5jaWEgw6kgYSBzZW5zaWJpbGlkYWRlIGRvIG1vZGVsbyDDoXMgdmFyaWHDp8O1ZXMgbm9zIGRhZG9zIGRlIHRyZWluYW1lbnRvLg0KDQo+DQoNCiMgU2VndW5kYSBRdWVzdMOjbw0KDQo+DQoNCkV4cGxpcXVlIHJlc3VtaWRhbWVudGUgbyBxdWUgw6kgYXByZW5kaXphZ2VtIHN1cGVydmlzaW9uYWRhIGUgbsOjby1zdXBlcnZpc2lvbmFkYS4gQ2l0ZSB1bSBwcm9ibGVtYSBkZSBhcHJlbmRpemFnZW0gc3VwZXJ2aXNpb25hZGEgZSB1bSBvdXRybyBkZSBhcHJlbmRpemFnZW0gbsOjby1zdXBlcnZpc2lvbmFkYS4NCg0KPg0KDQojIyMgUmVzcG9zdGE6IA0KDQpBcHJlbmRpemFnZW0gc3VwZXJ2aXNpb25hZGEgdXNhbW9zIG8gbcOpdG9kbyBkZSBjbGFzc2lmaWNhw6fDo28sIG8gb2JqZXRpdm8gw6kgbWFwZWFyIGFzIGVudHJhZGFzIGUgYXMgc2HDrWRhcyBjb20gYmFzZSBub3MgcGFkcsO1ZXMgaWRlbnRpZmljYWRvcyBub3MgZGFkb3MgZGUgdHJlaW5hbWVudG8uDQoNCkFwcmVuZGl6YWdlbSBuw6NvLXN1cGVydmlzaW9uYWRhIHVzYW1vcyBvIG3DqXRvZG8gZGUgY2xhc3NpZmljYcOnw6NvLCBvIG9iamV0aXZvIMOpIGVuY29udHJhciBwYWRyw7VlcywgZXN0cnV0dXJhcyBvdSByZWxhw6fDtWVzIGludHLDrW5zZWNhcyBub3MgZGFkb3Mgc2VtIHF1ZSBoYWphIGluZm9ybWHDp8O1ZXMgcHLDqXZpYXMgc29icmUgYXMgc2HDrWRhcyBkZXNlamFkYXMuDQoNCj4NCg0KIyBUZXJjZWlyYSBRdWVzdMOjbw0KDQo+DQoNCkV4cGxpcXVlIHF1YWwgbyBtb3Rpdm8gcXVlIGZheiBjb20gcXVlIG8gRXJybyBRdWFkcsOhdGljbyBNw6lkaW8tRVFNIHBhcmEgYXZhbGlhciBvIGRlc2VtcGVuaG8gZGUgdW0gbW9kZWxvIMOpIHJ1aW0gcXVhbmRvIG7Do28gYWRvdGFtb3MgbmVuaHVtYSBlc3RyYXTDqWdpYSBkZSBkaXZpc8OjbyBkbyBjb25qdW50byBkZSBkYWRvcyBlbSB0cmVpbmFtZW50byBlIHRlc3RlLg0KDQo+DQoNCiMjIyBSZXNwb3N0YTogDQoNCipPdmVyZml0dGluZyogKFNvYnJlYWp1c3RlKTogUXVhbmRvIHVtIG1vZGVsbyDDqSB0cmVpbmFkbyBlbSB0b2RvIG8gY29uanVudG8gZGUgZGFkb3MsIGVsZSBwb2RlIGFwcmVuZGVyIGRldGFsaGVzIGUgcnXDrWRvcyBlc3BlY8OtZmljb3MgZG9zIGRhZG9zIGRlIHRyZWluYW1lbnRvIHF1ZSBuw6NvIHPDo28gZ2VuZXJhbGl6w6F2ZWlzIHBhcmEgbm92b3MgZGFkb3MuIElzc28gcG9kZSByZXN1bHRhciBlbSB1bSBtb2RlbG8gcXVlIHNlIGFqdXN0YSBtdWl0byBiZW0gYW9zIGRhZG9zIGRlIHRyZWluYW1lbnRvLCBtYXMgcXVlIHBlcmZvcm1hIG1hbCBlbSBkYWRvcyBuw6NvIHZpc3RvcyBhbnRlcmlvcm1lbnRlLiBPIEVRTSBwb2RlIHNlciBiYWl4byBub3MgZGFkb3MgZGUgdHJlaW5hbWVudG8sIG1hcyBhbHRvIG5vcyBkYWRvcyBkZSB0ZXN0ZSBkZXZpZG8gYW8gb3ZlcmZpdHRpbmcuDQoNCipBdmFsaWHDp8OjbyBpbmFkZXF1YWRhIGRhIGdlbmVyYWxpemHDp8Ojbyo6IEEgcHJpbmNpcGFsIHJhesOjbyBwYXJhIGRpdmlkaXIgbyBjb25qdW50byBkZSBkYWRvcyBlbSB0cmVpbmFtZW50byBlIHRlc3RlIMOpIGF2YWxpYXIgY29tbyBvIG1vZGVsbyBnZW5lcmFsaXphIHBhcmEgZGFkb3MgbsOjbyB2aXN0b3MuIFF1YW5kbyBuw6NvIGjDoSBkaXZpc8OjbyBlIG8gbW9kZWxvIMOpIHRyZWluYWRvIGVtIHRvZG9zIG9zIGRhZG9zLCBuw6NvIGjDoSB1bWEgYXZhbGlhw6fDo28gcmVhbGlzdGEgZGEgY2FwYWNpZGFkZSBkbyBtb2RlbG8gZGUgZmF6ZXIgcHJldmlzw7VlcyBwcmVjaXNhcyBlbSBub3ZvcyBkYWRvcywgcXVlIMOpIG8gb2JqZXRpdm8gZmluYWwgZGEgbWFpb3JpYSBkYXMgYXBsaWNhw6fDtWVzLg0KDQo+DQoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQpsb2FkKCJDOi9Vc2Vycy9lbHRvbi9Eb3dubG9hZHMvZGFkb3NfZXhwZWN0YXRpdmFfcmVuZGEgKDMpLlJEYXRhIikNCmxpYnJhcnkocnNhbXBsZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRpZHltb2RlbHMpDQpsaWJyYXJ5KHRpYmJsZSkNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KHBhdGNod29yaykNCnRpZHltb2RlbHM6OnRpZHltb2RlbHNfcHJlZmVyKCkNCmBgYA0KDQo+DQoNCiMgUXVhcnRhIFF1ZXN0w6NvDQoNCj4NCg0KQ29uc2lkZXJlIG8gY29uanVudG8gZGUgZGFkb3MgZGUgRXhwZWN0YXRpdmEgZGUgdmlkYSB2ZXJzdXMgUElCIHBlciBDYXBpdGEuIENvbnNpZGVyZSBhIGZ1bsOnw6NvIGcgdW1hIHJlZ3Jlc3PDo28gcG9saW5vbWlhLiBVdGlsaXphbmRvIG8gZXJybyBxdWFkcsOhdGljbyBtw6lkaW8gb2JzZXJ2YWRvLCBzZW0gZmF6ZXIgbmVuaHVtYSBlc3RyYXTDqWdpYSBkZSBkaXZpc8OjbyBkb3MgZGFkb3MsIGltcGxlbWVudGUgdW0gY8OzZGlnbyBlbSBSIHBhcmEgY2hlY2FyIHF1YWwgbyBtZWxob3IgbW9kZWxvLiBwID0gMSwg4oCmLCA1MA0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnNldC5zZWVkKDIwMjMpDQpwID0gNTANCmRhZG9zX2V4cCA8LSBkYWRvc19leHBlY3RhdGl2YV9yZW5kYVssLTFdDQpjb2xuYW1lcyhkYWRvc19leHApIDwtIGMoInkiLCAieCIpDQoNCmZvcihpIGluIDE6cCl7DQogIGlmKGkgPiAxKXsNCiAgICB0ZW1wIDwtIGNiaW5kKHRlbXAsIHRlbXBbLDJdXmkpDQogICAgY29sbmFtZXModGVtcCkgPC0gYygieSIsICJ4IiwgcGFzdGUwKCJ4IiwgMjppKSkNCiAgICBmaXQgPC0gbG0oeX4uICwgZGF0YSA9IHRlbXApDQogICAgZXFtW2ldIDwtIG1lYW4oKHRlbXAkeSAtIHByZWRpY3QoZml0LCBuZXdkYXRhID0gdGVtcCkpXjIpDQogICAgaSA9IGkgKyAxDQogIH0NCiAgZWxzZXsNCiAgICBmaXQgPC0gbG0oeX4uICwgZGF0YSA9IGRhZG9zX2V4cCkNCiAgICBlcW0gPC0gbWVhbigoZGFkb3NfZXhwJHkgLSBwcmVkaWN0KGZpdCwgbmV3ZGF0YSA9IGRhZG9zX2V4cCkpXjIpDQogICAgdGVtcCA8LSBkYWRvc19leHANCiAgfQ0KfQ0KZXFtMSA8LSBkYXRhLmZyYW1lKCJQIiA9IDE6NTAsICJFUU0iID0gZXFtKQ0KDQpnIDwtIGdncGxvdChlcW0xLCBhZXMoUCwgRVFNKSkgKyANCiAgZ2VvbV9saW5lKGxpbmV0eXBlID0gMiwgc2l6ZSA9IDAuNCkgKyANCiAgZ2VvbV9wb2ludChzaXplID0gMiwgYWVzKGNvbG91ciA9IC1FUU0pKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAxKSArIA0KICBsYWJzKHggPSAiUCIsIHkgPSAiRVFNIiwNCiAgICAgICB0aXRsZSA9ICJBdmFsaWHDp8OjbyBkbyBFUU0iLA0KICAgICAgIHN1YnRpdGxlID0gIlNlbSByZXBhcnRpciBjb25qdW50byB0cmVpbm8gLyB0ZXN0ZSIpDQpnDQpgYGANCj4NCg0KIyBRdWludGEgUXVlc3TDo28NCg0KPg0KDQpSZWZhw6dhIG8gZXhlcmPDrWNpbyBhbnRlcmlvciBkbyBwb2xpbsO0bWlvLCB1dGlsaXphbmRvIGEgZXN0cmF0w6lnaWEgZGUgZGF0YSBzcGxpdHRpbmcsIGVtIHF1ZSBkaXZpZGUtc2UgbyBjb25qdW50byBkZSBkYWRvcyBlbSB0cmVpbmFtZW50byBlIHRlc3RlLiBVdGlsaXplIG8gY29uanVudG8gZGUgdGVzdGUgcGFyYSBjYWxjdWxhciBhIGVzdGltYXRpdmEgZG8gcmlzY28sIHVzYW5kbyBvIEVRTQ0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnNldC5zZWVkKDIwMjMpDQpwID0gNTANCmRhZG9zX2V4cCA8LSBkYWRvc19leHBlY3RhdGl2YV9yZW5kYVssLTFdDQpjb2xuYW1lcyhkYWRvc19leHApIDwtIGMoInkiLCAieCIpDQpkYWRvc19leHAgPC0gaW5pdGlhbF9zcGxpdChkYWRvc19leHAsIHByb3AgPSAzLzQpDQp0cmFpbmVyIDwtIHRyYWluaW5nKGRhZG9zX2V4cCkNCnRlc3RlciA8LSB0ZXN0aW5nKGRhZG9zX2V4cCkNCg0KZXFtMiA9IGRhdGEuZnJhbWUoUCA9IDE6NTAsIHkgPSBOQSkNCmZvcihpIGluIDE6cCl7DQogIGlmKGkgPiAxKXsNCiAgICB0cmFpbmVyIDwtIGNiaW5kKHRyYWluZXIsIHRyYWluZXJbLDJdXmkpDQogICAgY29sbmFtZXModHJhaW5lcikgPC0gYygieSIsICJ4IiwgcGFzdGUwKCJ4IiwgMjppKSkNCiAgICBmaXQgPC0gbG0oeX4uLCB0cmFpbmVyKQ0KICAgIA0KICAgICMjIEF2YWxpYW5kbyBubyBjb25qdW50byBkZSB0ZXN0ZQ0KICAgIHRlc3RlciA8LSBjYmluZCh0ZXN0ZXIsIHRlc3RlclssMl1eaSkNCiAgICBjb2xuYW1lcyh0ZXN0ZXIpIDwtIGMoInkiLCAieCIsIHBhc3RlMCgieCIsIDI6aSkpDQogICAgZXFtMltpLDJdIDwtIG1lYW4oKHRlc3RlclssMV0gLSBwcmVkaWN0KGZpdCwgdGVzdGVyKSleMikNCiAgfQ0KICBlbHNlew0KICAgIGZpdCA8LSBsbSh5fi4sIHRyYWluZXIpDQogICAgZXFtMltpLDJdIDwtIG1lYW4oKHRlc3RlclssMV0gLSBwcmVkaWN0KGZpdCwgdGVzdGVyKSleMikNCiAgfQ0KfQ0KDQpjb2xuYW1lcyhlcW0yKSA8LSBjKCJQIiwgIkVRTSIpDQpnIDwtIGdncGxvdChlcW0yLCBhZXMoUCwgRVFNKSkgKyANCiAgZ2VvbV9saW5lKGxpbmV0eXBlID0gMiwgc2l6ZSA9IDAuNCkgKyANCiAgZ2VvbV9wb2ludChzaXplID0gMiwgYWVzKGNvbG91ciA9IC1FUU0pKSArDQogIGxhYnMoeCA9ICJQIiwgeSA9ICJFUU0iLA0KICAgICAgIHRpdGxlID0gIkF2YWxpYcOnw6NvIGRvIEVRTSIsDQogICAgICAgc3VidGl0bGUgPSAiUmVwYXJ0aW5kbyBjb25qdW50byB0cmVpbm8vdGVzdGUsIHNlbSB2YWxpZGHDp8OjbyBjcnV6YWRhIikgKw0KICB4bGltKGMoMCwxOCkpICsgeWxpbShjKDEwLDEzMCkpDQoNCmcNCg0KYGBgDQo+DQoNCiMgU2V4dGEgUXVlc3TDo28NCg0KPg0KDQpBaW5kYSBjb25zaWRlcmFuZG8gbyBleGVyY8OtY2lvIGRvIHBvbGluw7RtaW8sIGltcGxlbWVudGUgdW1hIGVzdHJhdMOpZ2lhIGRlIGxlYXZlLW9uZS1vdXQgY3Jvc3MtdmFsaWRhdGlvbiBlIHNlbGVjaW9uZSBvIG1lbGhvciBtb2RlbG8gbWluaW1pemFuZG8gYSBmdW7Dp8OjbyBkZSByaXNjby4NCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpzZXQuc2VlZCgyMDIzKQ0KcCA9IDUwDQpkYWRvc19leHAgPC0gZGFkb3NfZXhwZWN0YXRpdmFfcmVuZGFbLC0xXQ0KY29sbmFtZXMoZGFkb3NfZXhwKSA8LSBjKCJ5IiwgIngiKQ0KZGFkb3NfZXhwIDwtIGluaXRpYWxfc3BsaXQoZGFkb3NfZXhwLCBwcm9wID0gMy80KQ0KdHJhaW5lciA8LSB0cmFpbmluZyhkYWRvc19leHApDQp0ZXN0ZXIgPC0gdGVzdGluZyhkYWRvc19leHApDQoNCmxlYXZlIDwtIGxvb19jdih0cmFpbmVyKQ0KDQpyZXN1bHRzMSA8LSBkYXRhLmZyYW1lKFAgPSBOQSwgRVFNID0gTkEpDQpmb3IoYiBpbiAxOmRpbShsZWF2ZSlbMV0pew0KICBlcW0gPC0gTlVMTA0KICANCiAgZm9yKGkgaW4gMTpwKXsNCiAgICBpZihpID4gMSl7DQogICAgICB0ZW1wX3RyYWluZXIgPC0gbGVhdmUkc3BsaXRzW1tiXV0gJT4lIGFuYWx5c2lzKCkNCiAgICAgIHRlbXBfdGVzdGVyIDwtIGxlYXZlJHNwbGl0c1tbYl1dICU+JSBhc3Nlc3NtZW50KCkNCiAgICAgIGZvcihqIGluIDI6aSl7DQogICAgICAgIHRlbXBfdHJhaW5lciA8LSBjYmluZCh0ZW1wX3RyYWluZXIsIHRlbXBfdHJhaW5lciR4XmopDQogICAgICAgIHRlbXBfdGVzdGVyIDwtIGNiaW5kKHRlbXBfdGVzdGVyLCB0ZW1wX3Rlc3RlciR4XmopDQogICAgICAgIGNvbG5hbWVzKHRlbXBfdHJhaW5lcikgPC0gYygieSIsICJ4IiwgcGFzdGUwKCJ4IiwgMjpqKSkNCiAgICAgICAgY29sbmFtZXModGVtcF90ZXN0ZXIpIDwtIGMoInkiLCAieCIsIHBhc3RlMCgieCIsIDI6aikpDQogICAgICB9DQogICAgICANCiAgICAgIGZpdCA8LSBsbSh5fi4sIHRlbXBfdHJhaW5lcikNCiAgICAgIGVxbVtpXSA8LSBhcy5udW1lcmljKCh0ZW1wX3Rlc3RlclssMV0gLSBwcmVkaWN0KGZpdCwgdGVtcF90ZXN0ZXIpKV4yKQ0KICAgIH0NCiAgICBlbHNlew0KICAgICAgdGVtcF90cmFpbmVyIDwtIGxlYXZlJHNwbGl0c1tbYl1dICU+JSBhbmFseXNpcygpDQogICAgICB0ZW1wX3Rlc3RlciA8LSBsZWF2ZSRzcGxpdHNbW2JdXSAlPiUgYXNzZXNzbWVudCgpDQogICAgICBmaXQgPC0gbG0oeX4uLCB0ZW1wX3RyYWluZXIpDQogICAgICBlcW1baV0gPC0gYXMubnVtZXJpYygodGVtcF90ZXN0ZXJbLDFdIC0gcHJlZGljdChmaXQsIG5ld2RhdGEgPSB0ZW1wX3Rlc3RlcikpXjIpDQogICAgfQ0KICB9DQogIA0KICByZXN1bHRzMVtiLDFdIDwtIHdoaWNoKGVxbSA9PSBtaW4oZXFtKSlbMV0NCiAgcmVzdWx0czFbYiwyXSA8LSBtaW4oZXFtKSANCn0NCg0KYGBgDQoNCj4NCg0KIyMjIFJlc3Bvc3RhOiANCg0KUG9saW7DtG1pbyBkZSBHcmF1IDcgYXByZXNlbnRvdSBvIG1lbm9yIEVRTSwgbm8gdmFsb3IgZGUgJDQuNTk5NDc5OV57LTZ9JC4NCg0KPg0KDQojIFPDqXRpbWEgUXVlc3TDo28NCg0KPg0KDQpQb3IgZmltLCBjb25zaWRlcmFuZG8gbyBleGVyY8OtY2lvIGRvIHBvbGluw7RtaW8sIHJhZmHDp2EtbyB1dGlsaXphbmRvIHVtIHByb2NlZGltZW50byBkZSBrLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbi4gQ29uc2lkZXJlIGs9NS4gRGljYTogY29uc2lkZXJlIHV0aWxpemFyIGEgYmlibGlvdGVjYSByc2FtcGxlLg0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCg0Kc2V0LnNlZWQoMjAyMykNCnAgPSA1MA0KayA9IDUNCmRhZG9zX2V4cCA8LSBkYWRvc19leHBlY3RhdGl2YV9yZW5kYVssLTFdDQpjb2xuYW1lcyhkYWRvc19leHApIDwtIGMoInkiLCAieCIpDQpkYWRvc19leHAgPC0gaW5pdGlhbF9zcGxpdChkYWRvc19leHAsIHByb3AgPSAzLzQpDQp0cmFpbmVyIDwtIHRyYWluaW5nKGRhZG9zX2V4cCkNCnRlc3RlciA8LSB0ZXN0aW5nKGRhZG9zX2V4cCkNCg0KdmZvbGQgPC0gdmZvbGRfY3YodHJhaW5lciwgdiA9IDUpDQoNCnJlc3VsdHMyIDwtIGRhdGEuZnJhbWUoUCA9IE5BLCBFUU0gPSBOQSkNCmZvcihiIGluIDE6ZGltKHZmb2xkKVsxXSl7DQogIGVxbSA8LSBOVUxMDQogIA0KICBmb3IoaSBpbiAxOnApew0KICAgIGlmKGkgPiAxKXsNCiAgICAgIHRlbXBfdHJhaW5lciA8LSB2Zm9sZCRzcGxpdHNbW2JdXSAlPiUgYW5hbHlzaXMoKQ0KICAgICAgdGVtcF90ZXN0ZXIgPC0gdmZvbGQkc3BsaXRzW1tiXV0gJT4lIGFzc2Vzc21lbnQoKQ0KICAgICAgZm9yKGogaW4gMjppKXsNCiAgICAgICAgdGVtcF90cmFpbmVyIDwtIGNiaW5kKHRlbXBfdHJhaW5lciwgdGVtcF90cmFpbmVyJHheaikNCiAgICAgICAgdGVtcF90ZXN0ZXIgPC0gY2JpbmQodGVtcF90ZXN0ZXIsIHRlbXBfdGVzdGVyJHheaikNCiAgICAgICAgY29sbmFtZXModGVtcF90cmFpbmVyKSA8LSBjKCJ5IiwgIngiLCBwYXN0ZTAoIngiLCAyOmopKQ0KICAgICAgICBjb2xuYW1lcyh0ZW1wX3Rlc3RlcikgPC0gYygieSIsICJ4IiwgcGFzdGUwKCJ4IiwgMjpqKSkNCiAgICAgIH0NCiAgICAgIA0KICAgICAgZml0IDwtIGxtKHl+LiwgdGVtcF90cmFpbmVyKQ0KICAgICAgZXFtW2ldIDwtIG1lYW4oKHRlbXBfdGVzdGVyWywxXSAtIHByZWRpY3QoZml0LCBuZXdkYXRhID0gdGVtcF90ZXN0ZXIpKV4yKQ0KICAgIH0NCiAgICBlbHNlew0KICAgICAgdGVtcF90cmFpbmVyIDwtIHZmb2xkJHNwbGl0c1tbYl1dICU+JSBhbmFseXNpcygpDQogICAgICB0ZW1wX3Rlc3RlciA8LSB2Zm9sZCRzcGxpdHNbW2JdXSAlPiUgYXNzZXNzbWVudCgpDQogICAgICBmaXQgPC0gbG0oeX4uLCB0ZW1wX3RyYWluZXIpDQogICAgICBlcW1baV0gPC0gbWVhbigodGVtcF90ZXN0ZXJbLDFdIC0gcHJlZGljdChmaXQsIG5ld2RhdGEgPSB0ZW1wX3Rlc3RlcikpXjIpDQogICAgfQ0KICB9DQogIHJlc3VsdHMyW2IsMV0gPC0gd2hpY2goZXFtID09IG1pbihlcW0pKVsxXQ0KICByZXN1bHRzMltiLDJdIDwtIG1pbihlcW0pIA0KfQ0KYGBgDQoNCj4NCg0KIyMjIFJlc3Bvc3RhIDoNCg0KUG9saW7DtG1pbyBkZSBHcmF1IDEwIGFwcmVzZW50b3UgbyBtZW5vciBFUU0sIG5vIHZhbG9yIGRlIDE5Ljg3MjkwMzMgcGFyYSBvIG3DqXRvZG8gay1mb2xkLCBrPTUuDQoNCj4NCg0KIyBPaXRhdmEgUXVlc3TDo28NCg0KPg0KDQpVdGlsaXphbmRvIG9zIGRhZG9zIGRlIHZpbmhvIHZlcm1lbGhvLCBmYcOnYSB1bWEgcGVxdWVuYSBhbsOhbGlzZSBleHBsb3JhdMOzcmlhIGRvcyBkYWRvcy4gTm8gbGluayBkbyBLYWdnbGUgdm9jw6ogY29uc2VndWUgdW1hIGV4cGxpY2HDp8OjbyBzb2JyZSBvIHF1ZSBzaWduaWZpY2EgY2FkYSB1bWEgZGFzIHZhcmnDoXZlaXMuDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCndpbmU8LXJlYWQuY3N2KCJDOi9Vc2Vycy9lbHRvbi9EZXNrdG9wL3dpbmVxdWFsaXR5LXJlZC5jc3YiKQ0Kd2luZTwtIHdpbmUlPiVyZW5hbWUoYyhhY2lkZXpfZml4YT1maXhlZC5hY2lkaXR5LGFjaWRlel92b2xhdGlsPXZvbGF0aWxlLmFjaWRpdHksYWNpZG9fY2l0cmljbz1jaXRyaWMuYWNpZCxhw6d1Y2FyX3Jlc2lkdWFsPXJlc2lkdWFsLnN1Z2FyLGNsb3JldG9zPWNobG9yaWRlcyxkaW94aWRvX2VueG9mcmVfbGl2cmU9ZnJlZS5zdWxmdXIuZGlveGlkZSxkaW94aWRvX2VueG9mcmVfdG90YWw9dG90YWwuc3VsZnVyLmRpb3hpZGUsZGVuc2lkYWRlPWRlbnNpdHksc3VsZmF0b3M9c3VscGhhdGVzLGFsY29vbD1hbGNvaG9sLHF1YWxpZGFkZT1xdWFsaXR5KSkNCnNraW1yOjpza2ltKHdpbmUpDQpgYGANCg0KPg0KDQojIE5vbmEgUXVlc3TDo28NCg0KPg0KDQpvYnRlbmhhIG8gbWVsaG9yIG1vZGVsbyBkZSByZWdyZXNzw6NvIGxpbmVhciBwYXJhIG1vZGVsYXIgYSBxdWFsaWRhZGUgZG8gdmluaG8sIGNvbnNpZGVyYW5kbzogTcOpdG9kbyBkb3MgTcOtbmltb3MgUXVhZHJhZG9zLCBSZWdyZXNzw6NvIFJpZGdlLCBSZWdyZXNzw6NvIExhc3NvLCBFbGFzdGljIE5ldC4gU2VsZWNpb25lIG8gbWVsaG9yIG1vZGVsbyBwYXJhIGNhZGEgdW1hIGRhcyBjbGFzc2VzIGUgY29udHJ1YSB1bWEgdGFiZWxhIGNvbSBvIHJpc2NvIGVzdGltYWRvLiBBbyBmaW0gY29uc3RydWlyIHF1YXRybyBncsOhZmljb3MgbW9zdHJhbmRvIGNhZGEgdW0gZG9zIGFqdXN0ZXMuDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShyc2FtcGxlKQ0KbGlicmFyeSh0aWR5bW9kZWxzKQ0KbGlicmFyeSh3b3JrZmxvd3MpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh0dW5lKQ0KbGlicmFyeSh0aWJibGUpDQpsaWJyYXJ5KHB1cnJyKQ0KbGlicmFyeShwYXRjaHdvcmspDQpsaWJyYXJ5KGdsbW5ldCkgICAgICANCndpbmU8LXJlYWQuY3N2KCJDOi9Vc2Vycy9lbHRvbi9EZXNrdG9wL3dpbmVxdWFsaXR5LXJlZC5jc3YiKQ0KY29sbmFtZXMod2luZSkgPC0gYyhwYXN0ZTAoIngiLCAxOjExKSwgInkiKQ0Kd2luZSA8LSBpbml0aWFsX3NwbGl0KHdpbmUsIHByb3AgPSAzLzQpDQoNCnRyYWluZXIgPC0gdHJhaW5pbmcod2luZSkNCnRlc3RlciA8LSB0ZXN0aW5nKHdpbmUpDQoNCiNTZXR0aW5nIEVuZ2luZQ0KKG1vZGVsb19tbW8gPC0gDQogIGxpbmVhcl9yZWcocGVuYWx0eSA9IDAsIG1peHR1cmUgPSAwKSAlPiUgDQogIHNldF9tb2RlKCJyZWdyZXNzaW9uIikgJT4lIA0KICBzZXRfZW5naW5lKCJnbG1uZXQiKSkNCg0KKG1vZGVsb19yaWRnZSA8LSANCiAgbGluZWFyX3JlZyhwZW5hbHR5ID0gdHVuZTo6dHVuZSgpLCBtaXh0dXJlID0gMCkgJT4lIA0KICBzZXRfbW9kZSgicmVncmVzc2lvbiIpICU+JSANCiAgc2V0X2VuZ2luZSgiZ2xtbmV0IikpDQoNCihtb2RlbG9fbGFzc28gPC0gDQogIHBhcnNuaXA6OmxpbmVhcl9yZWcocGVuYWx0eSA9IHR1bmU6OnR1bmUoKSwgbWl4dHVyZSA9IDEpICU+JSANCiAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKSAlPiUgDQogIHBhcnNuaXA6OnNldF9lbmdpbmUoImdsbW5ldCIpKQ0KDQoobW9kZWxvX2VsYXN0aWMgPC0gDQogIHBhcnNuaXA6OmxpbmVhcl9yZWcocGVuYWx0eSA9IHR1bmU6OnR1bmUoKSwgbWl4dHVyZSA9IHR1bmU6OnR1bmUoKSkgJT4lIA0KICBzZXRfbW9kZSgicmVncmVzc2lvbiIpICU+JSANCiAgcGFyc25pcDo6c2V0X2VuZ2luZSgiZ2xtbmV0IikpDQoNCg0KI3dvcmtmbG93DQooYWxsX3dmIDwtIA0KICB3b3JrZmxvd19zZXQoDQogICAgcHJlcHJvYyA9IGxpc3QoeSB+IC4gKSwNCiAgICBtb2RlbHMgPSBsaXN0KG1tbyA9IG1vZGVsb19tbW8sIHJpZGdlID0gbW9kZWxvX3JpZGdlLCBsYXNzbyA9IG1vZGVsb19sYXNzbywgZWxhc3RpYyA9IG1vZGVsb19lbGFzdGljKSwgDQogICAgY3Jvc3MgPSBUUlVFDQogICkpDQoNCiNjcm9zcy12YWxpZGF0aW9uDQpzZXQuc2VlZCgyMDIzKQ0KKGN2IDwtdmZvbGRfY3YodHJhaW5lciwgdiA9IDVMKSkNCg0KI21ldHJpY3MNCihtZXRyaWNhIDwtIHlhcmRzdGljazo6bWV0cmljX3NldChybXNlKSkNCg0KI3R1bm5pbmcNCnR1bmFnZW0gPC0gDQogIGFsbF93ZiAlPiUgIA0KICB3b3JrZmxvd19tYXAoDQogICAgc2VlZCA9IDIwMjMsIA0KICAgIHZlcmJvc2UgPSBUUlVFLA0KICAgIHJlc2FtcGxlcyA9IGN2LA0KICAgIGdyaWQgPSA1MCwNCiAgICBtZXRyaWNzID0gbWV0cmljYQ0KICApDQogIA0KYGBgDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShyc2FtcGxlKQ0KbGlicmFyeShwdXJycikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHdvcmtmbG93c2V0cykNCg0KbW9kZWxvc19yYW5rIDwtIHR1bmFnZW0gJT4lIHJhbmtfcmVzdWx0cygpDQoNCm1lbGhvcl9tbW8gPC0gDQogIHR1bmFnZW0gJT4lIA0KICBleHRyYWN0X3dvcmtmbG93X3NldF9yZXN1bHQoImZvcm11bGFfbW1vIikgJT4lIA0KICBzZWxlY3RfYmVzdCgicm1zZSIpDQoNCm1lbGhvcl9yaWRnZSA8LSANCiAgdHVuYWdlbSAlPiUgDQogIGV4dHJhY3Rfd29ya2Zsb3dfc2V0X3Jlc3VsdCgiZm9ybXVsYV9yaWRnZSIpICU+JSANCiAgc2VsZWN0X2Jlc3QoInJtc2UiKQ0KDQptZWxob3JfbGFzc28gPC0gDQogIHR1bmFnZW0gJT4lIA0KICBleHRyYWN0X3dvcmtmbG93X3NldF9yZXN1bHQoImZvcm11bGFfbGFzc28iKSAlPiUgDQogIHNlbGVjdF9iZXN0KCJybXNlIikNCg0KbWVsaG9yX2VsYXN0aWMgPC0gDQogIHR1bmFnZW0gJT4lIA0KICBleHRyYWN0X3dvcmtmbG93X3NldF9yZXN1bHQoImZvcm11bGFfZWxhc3RpYyIpICU+JSANCiAgc2VsZWN0X2Jlc3QoInJtc2UiKQ0KDQpmaW5hbGl6YW5kb19tbW8gPC0gDQogIHR1bmFnZW0gJT4lIA0KICBleHRyYWN0X3dvcmtmbG93KCJmb3JtdWxhX21tbyIpICU+JSANCiAgZmluYWxpemVfd29ya2Zsb3cobWVsaG9yX21tbykgJT4lIA0KICBsYXN0X2ZpdChzcGxpdCA9IHdpbmUpDQoNCmZpbmFsaXphbmRvX3JpZGdlIDwtIA0KICB0dW5hZ2VtICU+JSANCiAgZXh0cmFjdF93b3JrZmxvdygiZm9ybXVsYV9yaWRnZSIpICU+JSANCiAgZmluYWxpemVfd29ya2Zsb3cobWVsaG9yX3JpZGdlKSAlPiUgDQogIGxhc3RfZml0KHNwbGl0ID0gd2luZSkNCg0KZmluYWxpemFuZG9fbGFzc28gPC0gDQogIHR1bmFnZW0gJT4lIA0KICBleHRyYWN0X3dvcmtmbG93KCJmb3JtdWxhX2xhc3NvIikgJT4lIA0KICBmaW5hbGl6ZV93b3JrZmxvdyhtZWxob3JfbGFzc28pICU+JSANCiAgbGFzdF9maXQoc3BsaXQgPSB3aW5lKQ0KDQpmaW5hbGl6YW5kb19lbGFzdGljIDwtIA0KICB0dW5hZ2VtICU+JSANCiAgZXh0cmFjdF93b3JrZmxvdygiZm9ybXVsYV9lbGFzdGljIikgJT4lIA0KICBmaW5hbGl6ZV93b3JrZmxvdyhtZWxob3JfZWxhc3RpYykgJT4lIA0KICBsYXN0X2ZpdChzcGxpdCA9IHdpbmUpDQpgYGANCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0Ka25pdHI6OmthYmxlKGRhdGEuZnJhbWUocm93Lm5hbWVzID0gYygiTU1PIiwgIlJpZGdlIiwgIkxhc3NvIiwgIkVsYXN0aWMiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAiUk1TRSIgPSBhcy5udW1lcmljKGMoKGZpbmFsaXphbmRvX21tbyAgJT4lICBjb2xsZWN0X21ldHJpY3MoKSlbMSwzXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoZmluYWxpemFuZG9fcmlkZ2UgJT4lIGNvbGxlY3RfbWV0cmljcygpKVsxLDNdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChmaW5hbGl6YW5kb19sYXNzbyAlPiUgY29sbGVjdF9tZXRyaWNzKCkpWzEsM10sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGZpbmFsaXphbmRvX2VsYXN0aWMgJT4lIGNvbGxlY3RfbWV0cmljcygpKVsxLDNdKSkpDQogICAgICAgICAgICAgDQogICAgICAgICAgICAgDQopDQpgYGANCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpkYXRhX3RlbXAgPC0gZGF0YS5mcmFtZSh5Y2hhcGV1X21tbyA9IChmaW5hbGl6YW5kb19tbW8gJT4lIGNvbGxlY3RfcHJlZGljdGlvbnMoKSlbMl0sDQogICAgICAgICAgICAgICAgICAgICAgICB5Y2hhcGV1X3JpZGdlID0gKGZpbmFsaXphbmRvX3JpZGdlICU+JSBjb2xsZWN0X3ByZWRpY3Rpb25zKCkpWzJdLA0KICAgICAgICAgICAgICAgICAgICAgICAgeWNoYXBldV9sYXNzbyA9IChmaW5hbGl6YW5kb19sYXNzbyAlPiUgY29sbGVjdF9wcmVkaWN0aW9ucygpKVsyXSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHljaGFwZXVfZWxhc3RpYyA9IChmaW5hbGl6YW5kb19lbGFzdGljICU+JSBjb2xsZWN0X3ByZWRpY3Rpb25zKCkpWzJdLA0KICAgICAgICAgICAgICAgICAgICAgICAgeSA9IChmaW5hbGl6YW5kb19lbGFzdGljICU+JSBjb2xsZWN0X3ByZWRpY3Rpb25zKCkpWzRdDQopDQoNCmNvbG5hbWVzKGRhdGFfdGVtcCkgPC0gYygieWNoYXBldV9tbW8iLCAieWNoYXBldV9yaWRnZSIsICJ5Y2hhcGV1X2xhc3NvIiwgInljaGFwZXVfZWxhc3RpYyIsICJ5IikNCmRhdGFfdGVtcCA8LSBhc190aWJibGUoZGF0YV90ZW1wKQ0KDQoNCmdyYXBoIDwtIGdncGxvdChkYXRhX3RlbXAsIGFlcyh4ID0geWNoYXBldV9tbW8sIHkgPSB5KSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoYWVzKHkgPSB5Y2hhcGV1X21tbyksIGNvbG91ciA9ICJyZWQiLCBzaXplID0gMSkgKw0KICBsYWJzKHRpdGxlID0gIlJldGEgYWp1c3RhZGEiLCBzdWJ0aXRsZSA9ICJ5X2FqdXN0YWRvIHZzIHlfcmVhbCBkbyBtb2RlbG8gTU1PIikNCg0KZ3JhcGgNCmBgYA0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCndpbmUgPC0gcmVhZC5jc3YoIndpbmVxdWFsaXR5LXJlZC5jc3YiKQ0KDQpjb2xuYW1lcyh3aW5lKSA8LSBjKHBhc3RlMCgieCIsIDE6MTEpLCAieSIpDQojRGF0YSBzcGxpdA0Kd2luZSA8LSBpbml0aWFsX3NwbGl0KHdpbmUsIHByb3AgPSAzLzQpDQoNCnRyYWluZXIgPC0gdHJhaW5pbmcod2luZSkNCnRlc3RlciA8LSB0ZXN0aW5nKHdpbmUpDQoNCmZlYXR1cmVzIDwtIHRyYWluZXJbLC0xMl0gJT4lIGFzLm1hdHJpeCgpDQp5dHJhaW5lciA8LSB0cmFpbmVyWywxMl0gJT4lIGFzLm1hdHJpeCgpDQp0ZXN0ZmVhdHVyZXMgPC0gdGVzdGVyWywtMTJdICU+JSBhcy5tYXRyaXgoKQ0KeXRlc3QgPC0gdGVzdGVyWywxMl0gJT4lIGFzLm1hdHJpeCgpDQoNCiNNTU8NCg0KZml0IDwtIGdsbW5ldCh4ID0gZmVhdHVyZXMsIHkgPSB5dHJhaW5lciwgYWxwaGEgPSAwLCBsYW1iZGEgPSAwKQ0KcHJlZGl0b3MgPC0gcHJlZGljdChmaXQsIG5ld3ggPSB0ZXN0ZmVhdHVyZXMpDQplcW1fbW1vIDwtIG1lYW4oKHByZWRpdG9zIC0geXRlc3QpXjIpDQoNCg0KI1JpZGdlDQpjdnJpZGdlIDwtIGN2LmdsbW5ldCh4ID0gZmVhdHVyZXMsIHkgPSB5dHJhaW5lciwgYWxwaGEgPSAwLCBuZm9sZHMgPSA1KQ0KZml0MiA8LSBnbG1uZXQoeCA9IGZlYXR1cmVzLCB5ID0geXRyYWluZXIsIGFscGhhID0gMCkNCnByZWRpdG9zX3JpZGdlIDwtIHByZWRpY3QoZml0MiwgcyA9IGN2cmlkZ2UkbGFtYmRhLjFzZSwgbmV3eCA9IHRlc3RmZWF0dXJlcykNCmVxbV9yaWRnZSA8LSBtZWFuKChwcmVkaXRvc19yaWRnZSAtIHl0ZXN0KV4yKQ0KDQojTGFzc28NCmN2bGFzc28gPC0gY3YuZ2xtbmV0KHggPSBmZWF0dXJlcywgeSA9IHl0cmFpbmVyLCBhbHBoYSA9IDEsIG5mb2xkcyA9IDUpDQpmaXQzIDwtIGdsbW5ldCh4ID0gZmVhdHVyZXMsIHkgPSB5dHJhaW5lciwgYWxwaGEgPSAxKQ0KcHJlZGl0b3NfbGFzc28gPC0gcHJlZGljdChmaXQzLCBzID0gY3ZsYXNzbyRsYW1iZGEuMXNlLCBuZXd4ID0gdGVzdGZlYXR1cmVzKQ0KZXFtX2xhc3MgPC0gbWVhbigocHJlZGl0b3NfbGFzc28gLSB5dGVzdCleMikNCg0KI0VsYXN0aWMtbmV0DQojUHJlY2lzYW1vcyBlc3RpcHVsYXIgbyBtZWxob3IgYWxwaGEgYXRyYXbDqXMgZGUgdW1hIHZhbGlkYcOnYW8gY3J1emFkYS4NCiNVdGlsaXphbmRvIDUtZm9sZHMuDQplcW1fZWxhc3RpYyA8LSBOVUxMDQphbHBoYV92YWx1ZXMgPC0gc2VxKDAuMDEsIDAuOTk5LCBsZW5ndGgub3V0ID0gNTAwKQ0KZm9yKGEgaW4gYWxwaGFfdmFsdWVzKXsNCiAgY3ZlbGFzdGljIDwtIGN2LmdsbW5ldCh4ID0gZmVhdHVyZXMsIHkgPSB5dHJhaW5lciwgYWxwaGEgPSBhLCBuZm9sZHMgPSA1KQ0KICB0ZW1wZml0IDwtIGdsbW5ldCh4ID0gZmVhdHVyZXMsIHkgPSB5dHJhaW5lciwgYWxwaGEgPSBhKQ0KICBwcmVkaXRvc19lbGFzdGljIDwtIHByZWRpY3QodGVtcGZpdCwgcyA9IGN2ZWxhc3RpYyRsYW1iZGEuMXNlLCBuZXd4ID0gdGVzdGZlYXR1cmVzKQ0KICBlcW1fZWxhc3RpYyA8LSBjKGVxbV9lbGFzdGljLCANCiAgICAgICAgICAgICAgICAgICBtZWFuKChwcmVkaXRvc19lbGFzdGljIC0geXRlc3QpXjIpKSANCn0NCm1pbihlcW1fZWxhc3RpYykNCmFscGhhX3ZhbHVlc1t3aGljaChlcW1fZWxhc3RpYyA9PSBtaW4oZXFtX2VsYXN0aWMpKV0NCmBgYA0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmtuaXRyOjprYWJsZShkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGMoIk1NTyIsICJSaWRnZSIsICJMYXNzbyIsICJFbGFzdGljIiksDQogICAgICAgICAgICAgICAgICAgICAgICAiRVFNIiA9IGMoZXFtX21tbywgZXFtX3JpZGdlLCBlcW1fbGFzcywgbWluKGVxbV9lbGFzdGljKSksDQogICAgICAgICAgICAgICAgICAgICAgICAiUk1TRSIgPSBzcXJ0KGMoZXFtX21tbywgZXFtX3JpZGdlLCBlcW1fbGFzcywgbWluKGVxbV9lbGFzdGljKSkpDQogICAgICAgICAgICAgICAgICAgICAgICApDQopDQpgYGANCg0KPg0KDQojIERlY2ltYSBRdWVzdMOjbw0KDQo+DQoNCkNvbnNpZGVyZSBhZ29yYSBvIGNvbmp1bnRvIGRlIGRhZG9zIGRlIGRlc3Blc2FzIG3DqWRpY2FzLiBSZWZhw6dhIG8gbWVzbW8gZXhlcmPDrWNpbyBkb3MgZGFkb3MgZGUgdmluaG8gdmVybWVsaG8sIGVtIHF1ZSBhcXVpLCBvIG9iamV0aXZvIMOpIHByZXZlciBhIHZhcmnDoXZlbCBjaGFyZ2VzLiBQZXJjZWJhIHF1ZSBhbGd1bWFzIHZhcmnDoXZlaXMgc8OjbyBxdWFsaXRhdGl2YXMsIGUgcG9ydGFudG8sIHZvY8OqIGRldmVyw6EgdHJhbnNmb3Jtw6EtbGFzIGVtIGR1bW15LiBJbmRpcXVlIG9zIG1lbGhvcmVzIGNlbsOhcmlvcyBkb3MgcXVhdHJvIG1vZGVsb3MgZSBpbmZvcm1lIHF1YWwgbW9kZWxvIHZvY8OqIHV0aWxpemFyaWEuIEV4cGxpcXVlISBEaWNhOiB1c2FuZG8gYSBmdW7Dp8OjbyBzdGVwX2R1bW15KCkgZGEgYmlibGlvdGVjYSByZWNpcGVzLCB2b2PDqiBwb2RlcsOhIGZhY2lsbWVudGUgdHJhbnNmb3JtYXIgdmFyacOhdmVpcyBxdWFsaXRhdGl2YXMgZW0gbnVtw6lyaWNhcy4NCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5bW9kZWxzKQ0KaW5zdXJhbmNlIDwtIHJlYWQuY3N2KCJDOi9Vc2Vycy9lbHRvbi9EZXNrdG9wL2luc3VyYW5jZS5jc3YiKQ0KZGYgPC0gcmVjaXBlKGNoYXJnZXMgfi4sIGRhdGEgPSBpbnN1cmFuY2UpICU+JSANCiAgc3RlcF9kdW1teShhbGxfbm9taW5hbCgpLCAtYWxsX291dGNvbWVzKCkpICU+JSANCiAgcHJlcCgpICU+JSBqdWljZSgpIA0KDQprbml0cjo6a2FibGUoZGYgJT4lIGhlYWQoMTApLCBmb3JtYXQgPSAiaHRtbCIpDQpgYGANCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpkZiA8LSBpbml0aWFsX3NwbGl0KGRmLCBwcm9wID0gMy80KQ0KDQp0cmFpbmVyIDwtIHRyYWluaW5nKGRmKQ0KdGVzdGVyIDwtIHRlc3RpbmcoZGYpDQoNCiNTZXR0aW5nIEVuZ2luZQ0KbW9kZWxvX21tbyA8LSANCiAgbGluZWFyX3JlZyhwZW5hbHR5ID0gMCwgbWl4dHVyZSA9IDApICU+JSANCiAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKSAlPiUgDQogIHNldF9lbmdpbmUoImdsbW5ldCIpDQoNCm1vZGVsb19yaWRnZSA8LSANCiAgbGluZWFyX3JlZyhwZW5hbHR5ID0gdHVuZTo6dHVuZSgpLCBtaXh0dXJlID0gMCkgJT4lIA0KICBzZXRfbW9kZSgicmVncmVzc2lvbiIpICU+JSANCiAgc2V0X2VuZ2luZSgiZ2xtbmV0IikNCg0KbW9kZWxvX2xhc3NvIDwtIA0KICBwYXJzbmlwOjpsaW5lYXJfcmVnKHBlbmFsdHkgPSB0dW5lOjp0dW5lKCksIG1peHR1cmUgPSAxKSAlPiUgDQogIHNldF9tb2RlKCJyZWdyZXNzaW9uIikgJT4lIA0KICBwYXJzbmlwOjpzZXRfZW5naW5lKCJnbG1uZXQiKQ0KDQptb2RlbG9fZWxhc3RpYyA8LSANCiAgcGFyc25pcDo6bGluZWFyX3JlZyhwZW5hbHR5ID0gdHVuZTo6dHVuZSgpLCBtaXh0dXJlID0gdHVuZTo6dHVuZSgpKSAlPiUgDQogIHNldF9tb2RlKCJyZWdyZXNzaW9uIikgJT4lIA0KICBwYXJzbmlwOjpzZXRfZW5naW5lKCJnbG1uZXQiKQ0KDQoNCiN3b3JrZmxvdw0KYWxsX3dmIDwtIA0KICB3b3JrZmxvd19zZXQoDQogICAgcHJlcHJvYyA9IGxpc3QoY2hhcmdlcyB+IC4gKSwNCiAgICBtb2RlbHMgPSBsaXN0KG1tbyA9IG1vZGVsb19tbW8sIHJpZGdlID0gbW9kZWxvX3JpZGdlLCBsYXNzbyA9IG1vZGVsb19sYXNzbywgZWxhc3RpYyA9IG1vZGVsb19lbGFzdGljKSwgDQogICAgY3Jvc3MgPSBUUlVFDQogICkNCg0KI2Nyb3NzLXZhbGlkYXRpb24NCnNldC5zZWVkKDIwMjMpDQpjdiA8LSByc2FtcGxlOjp2Zm9sZF9jdih0cmFpbmVyLCB2ID0gNUwpDQoNCiNtZXRyaWNzDQptZXRyaWNhIDwtIHlhcmRzdGljazo6bWV0cmljX3NldChybXNlKQ0KDQojdHVubmluZw0KdHVuYWdlbSA8LSANCiAgYWxsX3dmICU+JSAgDQogIHdvcmtmbG93X21hcCgNCiAgICBzZWVkID0gMjAyMywgDQogICAgdmVyYm9zZSA9IFRSVUUsDQogICAgcmVzYW1wbGVzID0gY3YsDQogICAgZ3JpZCA9IDUwLA0KICAgIG1ldHJpY3MgPSBtZXRyaWNhDQogICkNCmBgYA0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkocnNhbXBsZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRpZHltb2RlbHMpDQpsaWJyYXJ5KHRpYmJsZSkNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KHBhdGNod29yaykNCmxpYnJhcnkod29ya2Zsb3dzZXRzKQ0KbGlicmFyeSh0dW5lKQ0KbGlicmFyeSh3b3JrZmxvd3MpDQp0aWR5bW9kZWxzOjp0aWR5bW9kZWxzX3ByZWZlcigpDQptb2RlbG9zX3JhbmsgPC0gdHVuYWdlbSAlPiUgcmFua19yZXN1bHRzKCkNCg0KbWVsaG9yX21tbyA8LSANCiAgdHVuYWdlbSAlPiUgDQogIGV4dHJhY3Rfd29ya2Zsb3dfc2V0X3Jlc3VsdCgiZm9ybXVsYV9tbW8iKSAlPiUgDQogIHNlbGVjdF9iZXN0KCJybXNlIikNCg0KbWVsaG9yX3JpZGdlIDwtIA0KICB0dW5hZ2VtICU+JSANCiAgZXh0cmFjdF93b3JrZmxvd19zZXRfcmVzdWx0KCJmb3JtdWxhX3JpZGdlIikgJT4lIA0KICBzZWxlY3RfYmVzdCgicm1zZSIpDQoNCm1lbGhvcl9sYXNzbyA8LSANCiAgdHVuYWdlbSAlPiUgDQogIGV4dHJhY3Rfd29ya2Zsb3dfc2V0X3Jlc3VsdCgiZm9ybXVsYV9sYXNzbyIpICU+JSANCiAgc2VsZWN0X2Jlc3QoInJtc2UiKQ0KDQptZWxob3JfZWxhc3RpYyA8LSANCiAgdHVuYWdlbSAlPiUgDQogIGV4dHJhY3Rfd29ya2Zsb3dfc2V0X3Jlc3VsdCgiZm9ybXVsYV9lbGFzdGljIikgJT4lIA0KICBzZWxlY3RfYmVzdCgicm1zZSIpDQoNCmZpbmFsaXphbmRvX21tbyA8LSANCiAgdHVuYWdlbSAlPiUgDQogIGV4dHJhY3Rfd29ya2Zsb3coImZvcm11bGFfbW1vIikgJT4lIA0KICBmaW5hbGl6ZV93b3JrZmxvdyhtZWxob3JfbW1vKSAlPiUgDQogIGxhc3RfZml0KHNwbGl0ID0gZGYpDQoNCmZpbmFsaXphbmRvX3JpZGdlIDwtIA0KICB0dW5hZ2VtICU+JSANCiAgZXh0cmFjdF93b3JrZmxvdygiZm9ybXVsYV9yaWRnZSIpICU+JSANCiAgZmluYWxpemVfd29ya2Zsb3cobWVsaG9yX3JpZGdlKSAlPiUgDQogIGxhc3RfZml0KHNwbGl0ID0gZGYpDQoNCmZpbmFsaXphbmRvX2xhc3NvIDwtIA0KICB0dW5hZ2VtICU+JSANCiAgZXh0cmFjdF93b3JrZmxvdygiZm9ybXVsYV9sYXNzbyIpICU+JSANCiAgZmluYWxpemVfd29ya2Zsb3cobWVsaG9yX2xhc3NvKSAlPiUgDQogIGxhc3RfZml0KHNwbGl0ID0gZGYpDQoNCmZpbmFsaXphbmRvX2VsYXN0aWMgPC0gDQogIHR1bmFnZW0gJT4lIA0KICBleHRyYWN0X3dvcmtmbG93KCJmb3JtdWxhX2VsYXN0aWMiKSAlPiUgDQogIGZpbmFsaXplX3dvcmtmbG93KG1lbGhvcl9lbGFzdGljKSAlPiUgDQogIGxhc3RfZml0KHNwbGl0ID0gZGYpDQoNCg0Ka25pdHI6OmthYmxlKGRhdGEuZnJhbWUocm93Lm5hbWVzID0gYygiTU1PIiwgIlJpZGdlIiwgIkxhc3NvIiwgIkVsYXN0aWMiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAiUk1TRSIgPSBhcy5udW1lcmljKGMoKGZpbmFsaXphbmRvX21tbyAgJT4lICBjb2xsZWN0X21ldHJpY3MoKSlbMSwzXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoZmluYWxpemFuZG9fcmlkZ2UgJT4lIGNvbGxlY3RfbWV0cmljcygpKVsxLDNdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChmaW5hbGl6YW5kb19sYXNzbyAlPiUgY29sbGVjdF9tZXRyaWNzKCkpWzEsM10sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGZpbmFsaXphbmRvX2VsYXN0aWMgJT4lIGNvbGxlY3RfbWV0cmljcygpKVsxLDNdKSkpDQopDQpgYGANCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KZGF0YV90ZW1wIDwtIGRhdGEuZnJhbWUoeWNoYXBldV9tbW8gPSAoZmluYWxpemFuZG9fbW1vICU+JSBjb2xsZWN0X3ByZWRpY3Rpb25zKCkpWzJdLA0KICAgICAgICAgICAgICAgICAgICAgICAgeWNoYXBldV9yaWRnZSA9IChmaW5hbGl6YW5kb19yaWRnZSAlPiUgY29sbGVjdF9wcmVkaWN0aW9ucygpKVsyXSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHljaGFwZXVfbGFzc28gPSAoZmluYWxpemFuZG9fbGFzc28gJT4lIGNvbGxlY3RfcHJlZGljdGlvbnMoKSlbMl0sDQogICAgICAgICAgICAgICAgICAgICAgICB5Y2hhcGV1X2VsYXN0aWMgPSAoZmluYWxpemFuZG9fZWxhc3RpYyAlPiUgY29sbGVjdF9wcmVkaWN0aW9ucygpKVsyXSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAoZmluYWxpemFuZG9fZWxhc3RpYyAlPiUgY29sbGVjdF9wcmVkaWN0aW9ucygpKVs0XQ0KKQ0KDQpjb2xuYW1lcyhkYXRhX3RlbXApIDwtIGMoInljaGFwZXVfbW1vIiwgInljaGFwZXVfcmlkZ2UiLCAieWNoYXBldV9sYXNzbyIsICJ5Y2hhcGV1X2VsYXN0aWMiLCAieSIpDQpkYXRhX3RlbXAgPC0gYXNfdGliYmxlKGRhdGFfdGVtcCkNCg0KDQpnMSA8LSBnZ3Bsb3QoZGF0YV90ZW1wLCBhZXMoeCA9IHljaGFwZXVfbW1vLCB5ID0geSkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKGFlcyh5ID0geWNoYXBldV9tbW8pLCBjb2xvdXIgPSAiIzE5MTk3MCIsIHNpemUgPSAxKSArDQogIGxhYnModGl0bGUgPSAiTW9kZWxvIE1NTyIsIHN1YnRpdGxlID0gInlfYWp1c3RhZG8gdnMgeV9yZWFsIikNCg0KZzIgPC0gZ2dwbG90KGRhdGFfdGVtcCwgYWVzKHggPSB5Y2hhcGV1X3JpZGdlLCB5ID0geSkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKGFlcyh5ID0geWNoYXBldV9yaWRnZSksIGNvbG91ciA9ICIjNDE2OUUxIiwgc2l6ZSA9IDEpICsNCiAgbGFicyh0aXRsZSA9ICJNb2RlbG8gcmlkZ2UiLCBzdWJ0aXRsZSA9ICJ5X2FqdXN0YWRvIHZzIHlfcmVhbCIpDQoNCmczIDwtIGdncGxvdChkYXRhX3RlbXAsIGFlcyh4ID0geWNoYXBldV9sYXNzbywgeSA9IHkpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fbGluZShhZXMoeSA9IHljaGFwZXVfbGFzc28pLCBjb2xvdXIgPSAib3JhbmdlIiwgc2l6ZSA9IDEpICsNCiAgbGFicyh0aXRsZSA9ICJNb2RlbG8gbGFzc28iLCBzdWJ0aXRsZSA9ICJ5X2FqdXN0YWRvIHZzIHlfcmVhbCIpDQoNCmc0IDwtIGdncGxvdChkYXRhX3RlbXAsIGFlcyh4ID0geWNoYXBldV9lbGFzdGljLCB5ID0geSkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKGFlcyh5ID0geWNoYXBldV9lbGFzdGljKSwgY29sb3VyID0gInJlZCIsIHNpemUgPSAxKSArDQogIGxhYnModGl0bGUgPSAiTW9kZWxvIGVsYXN0aWMiLCBzdWJ0aXRsZSA9ICJ5X2FqdXN0YWRvIHZzIHlfcmVhbCIpDQoNCg0KKGcxICsgZzIpIC8gKGczICsgZzQpDQpgYGANCj4NCg0KIyMjIFJlc3Bvc3RhIDoNCg0KRXNjb2xoZXJpYSBvIG1vZGVsbyBkZSByZWdyZXNzw6NvIE1NTyBvdSBNb2RlbG8gUmlkZ2UuDQo=