Exercicio 1

Considere o problema em que o objetivo é prever a pontuação perfomance index do aluno com base em algumas variáveis. Avalie o poder preditivo do modelo selecionado. Sua análise deve conter uma análise exploratória dos dados, utilizar um esquema de k-folds cross-validation, com k=20 e considerar um esquema de data splitting na proporção 90% para treino e 1 0% para teste.

library(tidymodels)
library(rpart)
library(rpart.plot)
library(skimr)
library(corrplot)
library(visdat)
library(dplyr)
tidymodels::tidymodels_prefer()

Análise de Dados

dados<- read.csv("C:/Users/elton/Desktop/Student_Performance.csv")
dados$Extracurricular.Activities<- ifelse(dados$Extracurricular.Activities=="Yes",1,0)
skim(dados)
── Data Summary ────────────────────────
                           Values
Name                       dados 
Number of rows             10000 
Number of columns          6     
_______________________          
Column type frequency:           
  numeric                  6     
________________________         
Group variables            None  
vis_dat(dados)

attach(dados)
rho_hat <- cor(dados)
corrplot(rho_hat, method = "number")

vis_cor(dados)

dados |> dplyr::glimpse()
Rows: 10,000
Columns: 6
$ Hours.Studied                    <int> 7, 4, 8, 5, 7, 3, 7, 8, 5, 4, 8, 8, 3, 6…
$ Previous.Scores                  <int> 99, 82, 51, 52, 75, 78, 73, 45, 77, 89, …
$ Extracurricular.Activities       <dbl> 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0…
$ Sleep.Hours                      <int> 9, 4, 7, 5, 8, 9, 5, 4, 8, 4, 4, 6, 9, 4…
$ Sample.Question.Papers.Practiced <int> 1, 2, 2, 2, 5, 6, 6, 6, 2, 0, 5, 2, 2, 2…
$ Performance.Index                <dbl> 91, 65, 45, 36, 66, 61, 63, 42, 61, 69, …

Tratamento dos dados e preparação das receitas

set.seed(0) # Fixando uma semente
divisao_inicial <- rsample::initial_split(dados, prop = 0.9, strata ="Performance.Index")
treinamento <- rsample::training(divisao_inicial) # Conjunto de treinamento
teste <- rsample::testing(divisao_inicial) # Conjunto de teste
teste
NA
receita_1 <- 
  treinamento |> 
  recipe(formula = Performance.Index ~ .) |>
  step_YeoJohnson(all_predictors()) |>
  step_normalize(all_predictors()) |>
  step_zv(all_predictors()) |>
  step_corr(all_predictors())  
  
receita_2 <- 
  treinamento |> 
  recipe(formula = Performance.Index ~ .) |>
  step_YeoJohnson(all_predictors()) |>
  step_normalize(all_predictors()) 
  

Configurando os Modelos


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

modelo_knn <-
  parsnip::nearest_neighbor(
    neighbors = tune::tune(),
    dist_power = tune::tune(), 
    weight_func = "gaussian" 
  ) |> 
  parsnip::set_mode("regression") |> 
  parsnip::set_engine("kknn")

modelo_svm <- 
  parsnip::svm_rbf(
    cost = tune::tune(),
    rbf_sigma = tune::tune(),
    margin = tune::tune()
  ) |> 
  parsnip::set_mode("regression") |> 
  parsnip::set_engine("kernlab")

modelo_arvore <- decision_tree(cost_complexity = tune(), tree_depth = tune(),
                         min_n = tune()) |> 
  set_engine(engine = "rpart") |> 
  set_mode("regression")

modelo_floresta <- 
  rand_forest(mtry = tune(), min_n = tune(), trees = tune()) %>%  # random forest
  set_engine(engine = "ranger", importance = "impurity") %>% 
  set_mode("regression")

Validação Cruzada

validacao_cruzada <- 
  treinamento |> 
  rsample::vfold_cv(v = 5L, strata = Performance.Index)

Workflow

wf_todos <-
  workflow_set(
    preproc = list(receita_1, receita_2),
    models = list(
      knn_fit = modelo_knn,
      elastic_fit = modelo_elastic,
      svm_fit = modelo_svm,
      arvore_fit<- modelo_arvore,
      floresta_fit<- modelo_floresta
    ),
    cross = TRUE
  )


controle_grid <- control_grid(
  save_pred = TRUE,
  save_workflow = TRUE,
  parallel_over = "resamples"
)

Treino

treino <-
  wf_todos |> 
  workflow_map(
    resamples = validacao_cruzada,
    grid = 20L,
    control = controle_grid
  )



autoplot(treino, metric = "rmse") + 
  labs(
    title = "Avaliação dos modelos de regressão",
    subtitle = "Utilizando a métrica do EQM"
  ) + 
  xlab("Rank dos Workflows") +
  ylab("Erro Quadrático Médio - EQM")

Melhores

melhores <- 
  treino |> 
  rank_results(select_best = TRUE, rank_metric = "rmse")
melhores

autoplot(treino, select_best = TRUE)

NA
NA

Melhor Modelo

treino |> 
  rank_results()

melhor_modelo <- 
  treino |> 
  extract_workflow_set_result(id = "recipe_1_svm_fit") |> 
  select_best(metric = "rmse")

melhor_modelo
NA
wf_final <- 
  treino |> 
  extract_workflow(id = "recipe_1_svm_fit") |> 
  finalize_workflow(melhor_modelo)

teste <- 
  wf_final |> 
  last_fit(split = divisao_inicial)

teste$.metrics
[[1]]
NA

Modelo Final

modelo_final
══ Workflow [trained] ═════════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: svm_rbf()

── Preprocessor ───────────────────────────────────────────────────────────────────
4 Recipe Steps

• step_YeoJohnson()
• step_normalize()
• step_zv()
• step_corr()

── Model ──────────────────────────────────────────────────────────────────────────
Support Vector Machine object of class "ksvm" 

SV type: eps-svr  (regression) 
 parameter : epsilon = 0.0874433389003389  cost C = 8.70624801835044 

Gaussian Radial Basis kernel function. 
 Hyperparameter : sigma =  0.0544428691125384 

Number of Support Vectors : 4097 

Objective Function Value : -2117.35 
Training error : 0.01114 

Salvando o Modelo


saveRDS(modelo_final, file = "modelo_final.rds")

# Lendo o arquivo serializado com o modelo final
load(file = "modelo_final.rds")

# Fazendo novas previsões
predict(modelo_final, new_data = novos_dados)

Exercicio 2

Considere o problema em que o objetivo é prever o custo, charge, com base em algumas variáveis. Sua análise deve conter uma análise exploratória dos dados, deverá utilizar um esquema de k-folds cross-validation, com k=20 e considerar um esquema de data splitting na proporção 90% para treino e 10% para teste.

dados1<-read.csv("C:/Users/elton/Desktop/insurance.csv")
dados1<- dados1 |> select(-region)
dados1$sex<- ifelse(dados1$sex=="male",1,0)
dados1$smoker<- ifelse(dados1$smoker=="yes",1,0)

Descritiva dos dados

skim(dados1)
── Data Summary ────────────────────────
                           Values
Name                       dados1
Number of rows             1338  
Number of columns          6     
_______________________          
Column type frequency:           
  numeric                  6     
________________________         
Group variables            None  
vis_dat(dados1)

rho_hat1 <- cor(dados1)
corrplot(rho_hat1, method = "number")

vis_cor(dados1)

Tratamento dos dados e preparação das receitas

attach(dados1)
set.seed(0) 
divisao_inicial1 <- rsample::initial_split(dados1, prop = 0.9, strata ="charges")
treinamento1 <- rsample::training(divisao_inicial1) 
teste1 <- rsample::testing(divisao_inicial1)
receita_11 <- 
  treinamento1 |> 
  recipe(formula = charges ~ .) |>
  step_YeoJohnson(all_predictors()) |>
  step_normalize(all_predictors()) |>
  step_zv(all_predictors()) |>
  step_corr(all_predictors())  
  
receita_22 <- 
  treinamento1 |> 
  recipe(formula = charges ~ .) |>
  step_YeoJohnson(all_predictors()) |>
  step_normalize(all_predictors()) 
  

Configurando os Modelos


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

modelo_knn1 <-
  parsnip::nearest_neighbor(
    neighbors = tune::tune(),
    dist_power = tune::tune(), 
    weight_func = "gaussian" 
  ) |> 
  parsnip::set_mode("regression") |> 
  parsnip::set_engine("kknn")

modelo_svm1 <- 
  parsnip::svm_rbf(
    cost = tune::tune(),
    rbf_sigma = tune::tune(),
    margin = tune::tune()
  ) |> 
  parsnip::set_mode("regression") |> 
  parsnip::set_engine("kernlab")

modelo_arvore1 <- decision_tree(cost_complexity = tune(), tree_depth = tune(),
                         min_n = tune()) |> 
  set_engine(engine = "rpart") |> 
  set_mode("regression")

modelo_floresta1 <- 
  rand_forest(mtry = tune(), min_n = tune(), trees = tune()) |>   
  set_engine(engine = "ranger", importance = "impurity") |>  
  set_mode("regression")

Validação Cruzada

validacao_cruzada1 <- 
  treinamento1 |> 
  rsample::vfold_cv(v = 5L, strata = charges)

Workflow

wf_todos1 <-
  workflow_set(
    preproc = list(receita_11, receita_22),
    models = list(
      knn_fit1 = modelo_knn1,
      elastic_fit1 = modelo_elastic1,
      svm_fit1 = modelo_svm1,
      arvore_fit1<- modelo_arvore1,
      floresta_fit1<- modelo_floresta1
    ),
    cross = TRUE
  )


controle_grid1 <- control_grid(
  save_pred = TRUE,
  save_workflow = TRUE,
  parallel_over = "resamples"
)

Treino

treino1 <-
  wf_todos1 |> 
  workflow_map(
    resamples = validacao_cruzada1,
    grid = 20L,
    control = controle_grid1
  )



autoplot(treino1, metric = "rmse") + 
  labs(
    title = "Avaliação dos modelos de regressão",
    subtitle = "Utilizando a métrica do EQM"
  ) + 
  xlab("Rank dos Workflows") +
  ylab("Erro Quadrático Médio - EQM")

Melhores

melhores1 <- 
  treino1 |> 
  rank_results(select_best = TRUE, rank_metric = "rmse")
melhores1

autoplot(treino1, select_best = TRUE)

NA
NA

Melhor Modelo

treino1 |> 
  rank_results()

melhor_modelo1 <- 
  treino1 |> 
  extract_workflow_set_result(id = "recipe_2_rand_forest") |> 
  select_best(metric = "rmse")

melhor_modelo1
wf_final1 <- 
  treino1 |> 
  extract_workflow(id = "recipe_2_rand_forest") |> 
  finalize_workflow(melhor_modelo1)

teste1 <- 
  wf_final1 |> 
  last_fit(split = divisao_inicial1)

teste1$.metrics

Modelo Final


modelo_final1 <- 
  wf_final1 |> 
  fit(dados1)
modelo_final1

Salvando o Modelo


saveRDS(modelo_final1, file = "modelo_final.rds1")

# Lendo o arquivo serializado com o modelo final
load(file = "modelo_final.rds1")

# Fazendo novas previsões
predict(modelo_final1, new_data = novos_dados1)

Exercício 3

Considere o problema em que o objetivo é prever a qualidade, quality, com base em algumas variáveis. Sua análise deve conter uma análise exploratória dos dados, utilizar um esquema de k-folds cross-validation, com k=20 e considerar um esquema de data splitting na proporção 90% para treino e 10% para teste.

dados2<- read.csv("C:/Users/elton/Desktop/winequality-red.csv")
dados2<- dados2 |> rename(c(acidez_fixa=fixed.acidity,votalidade_acidez=volatile.acidity,
                            acidez_citrica=citric.acid,açucar_residual=residual.sugar,
                            cloretos=chlorides,dioxido_enxofre_livre=free.sulfur.dioxide,
                            total_dioxido_enxofre=total.sulfur.dioxide,densidade=density,
                            sulfatos=sulphates,alcool=alcohol,qualidade=quality))

Descritiva dos dados

skim(dados2)
── Data Summary ────────────────────────
                           Values
Name                       dados2
Number of rows             1599  
Number of columns          12    
_______________________          
Column type frequency:           
  numeric                  12    
________________________         
Group variables            None  
vis_dat(dados2)

vis_cor(dados2)

Tratamento dos dados e preparação das receitas

attach(dados2)
set.seed(0) 
divisao_inicial2 <- rsample::initial_split(dados2, prop = 0.9, strata ="qualidade")
treinamento2 <- rsample::training(divisao_inicial2) 
teste2 <- rsample::testing(divisao_inicial2)
receita_111 <- 
  treinamento2 |> 
  recipe(formula = qualidade ~ .) |>
  step_YeoJohnson(all_predictors()) |>
  step_normalize(all_predictors()) |>
  step_zv(all_predictors()) |>
  step_corr(all_predictors())  
  
receita_222 <- 
  treinamento2 |> 
  recipe(formula = qualidade ~ .) |>
  step_YeoJohnson(all_predictors()) |>
  step_normalize(all_predictors()) 
  

Configurando os Modelos


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

modelo_knn2 <-
  parsnip::nearest_neighbor(
    neighbors = tune::tune(),
    dist_power = tune::tune(), 
    weight_func = "gaussian" 
  ) |> 
  parsnip::set_mode("regression") |> 
  parsnip::set_engine("kknn")

modelo_svm2 <- 
  parsnip::svm_rbf(
    cost = tune::tune(),
    rbf_sigma = tune::tune(),
    margin = tune::tune()
  ) |> 
  parsnip::set_mode("regression") |> 
  parsnip::set_engine("kernlab")

modelo_arvore2 <- decision_tree(cost_complexity = tune(), tree_depth = tune(),
                         min_n = tune()) |> 
  set_engine(engine = "rpart") |> 
  set_mode("regression")

modelo_floresta2 <- 
  rand_forest(mtry = tune(), min_n = tune(), trees = tune()) |>   
  set_engine(engine = "ranger", importance = "impurity") |>  
  set_mode("regression")

Validação Cruzada

validacao_cruzada2 <- 
  treinamento2 |> 
  rsample::vfold_cv(v = 5L, strata = qualidade)

Workflow

wf_todos2 <-
  workflow_set(
    preproc = list(receita_111, receita_222),
    models = list(
      knn_fit2 = modelo_knn2,
      elastic_fit2 = modelo_elastic2,
      svm_fit2 = modelo_svm2,
      arvore_fit2<- modelo_arvore2,
      floresta_fit2<- modelo_floresta2
    ),
    cross = TRUE
  )


controle_grid2 <- control_grid(
  save_pred = TRUE,
  save_workflow = TRUE,
  parallel_over = "resamples"
)

Treino

treino2 <-
  wf_todos2 |> 
  workflow_map(
    resamples = validacao_cruzada2,
    grid = 20L,
    control = controle_grid2
  )



autoplot(treino2, metric = "rmse") + 
  labs(
    title = "Avaliação dos modelos de regressão",
    subtitle = "Utilizando a métrica do EQM"
  ) + 
  xlab("Rank dos Workflows") +
  ylab("Erro Quadrático Médio - EQM")

Melhores

melhores2 <- 
  treino2 |> 
  rank_results(select_best = TRUE, rank_metric = "rmse")
melhores2

autoplot(treino2, select_best = TRUE)

NA
NA
treino2 |> 
  rank_results()

melhor_modelo2 <- 
  treino2 |> 
  extract_workflow_set_result(id = "recipe_2_rand_forest") |> 
  select_best(metric = "rmse")

melhor_modelo2
NA
wf_final2 <- 
  treino2 |> 
  extract_workflow(id = "recipe_2_rand_forest") |> 
  finalize_workflow(melhor_modelo2)

teste2 <- 
  wf_final2 |> 
  last_fit(split = divisao_inicial2)

teste2$.metrics
[[1]]
NA

Modelo Final


modelo_final2 <- 
  wf_final2 |> 
  fit(dados2)
modelo_final2
══ Workflow [trained] ══════════════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: rand_forest()

── Preprocessor ────────────────────────────────────────────────────────────────────────
2 Recipe Steps

• step_YeoJohnson()
• step_normalize()

── Model ───────────────────────────────────────────────────────────────────────────────
Ranger result

Call:
 ranger::ranger(x = maybe_data_frame(x), y = y, mtry = min_cols(~4L,      x), num.trees = ~1767L, min.node.size = min_rows(~3L, x),      importance = ~"impurity", num.threads = 1, verbose = FALSE,      seed = sample.int(10^5, 1)) 

Type:                             Regression 
Number of trees:                  1767 
Sample size:                      1599 
Number of independent variables:  11 
Mtry:                             4 
Target node size:                 3 
Variable importance mode:         impurity 
Splitrule:                        variance 
OOB prediction error (MSE):       0.3174914 
R squared (OOB):                  0.5131757 

Salvando o Modelo


saveRDS(modelo_final2, file = "modelo_final.rds2")

# Lendo o arquivo serializado com o modelo final
load(file = "modelo_final.rds2")

# Fazendo novas previsões
predict(modelo_final2, new_data = novos_dados2)

Exercício 4

Considere o problema em que o objetivo é prever a variável cooling_load com base em algumas variáveis. Sua análise deve conter uma análise exploratória dos dados, utilizar um esquema de k-folds cross-validation, com k=20 e considerar um esquema de data splitting na proporção 90% para treino e 10% para teste.

dados3<-read.csv("C:/Users/elton/Downloads/energy_efficiency_data.csv")

Descritiva dos dados

skim(dados3)
── Data Summary ────────────────────────
                           Values
Name                       dados3
Number of rows             768   
Number of columns          10    
_______________________          
Column type frequency:           
  numeric                  10    
________________________         
Group variables            None  
vis_dat(dados3)

vis_cor(dados3)

Tratamento dos dados e preparação das receitas

attach(dados3)
set.seed(0) 
divisao_inicial3 <- rsample::initial_split(dados3, prop = 0.9, strata ="Cooling_Load")
treinamento3 <- rsample::training(divisao_inicial3) 
teste3 <- rsample::testing(divisao_inicial3)
receita_1111 <- 
  treinamento3 |> 
  recipe(formula = Cooling_Load ~ .) |>
  step_YeoJohnson(all_predictors()) |>
  step_normalize(all_predictors()) |>
  step_zv(all_predictors()) |>
  step_corr(all_predictors())  
  
receita_2222 <- 
  treinamento3 |> 
  recipe(formula = Cooling_Load ~ .) |>
  step_YeoJohnson(all_predictors()) |>
  step_normalize(all_predictors()) 
  

Configurando os Modelos


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

modelo_knn3 <-
  parsnip::nearest_neighbor(
    neighbors = tune::tune(),
    dist_power = tune::tune(), 
    weight_func = "gaussian" 
  ) |> 
  parsnip::set_mode("regression") |> 
  parsnip::set_engine("kknn")

modelo_svm3 <- 
  parsnip::svm_rbf(
    cost = tune::tune(),
    rbf_sigma = tune::tune(),
    margin = tune::tune()
  ) |> 
  parsnip::set_mode("regression") |> 
  parsnip::set_engine("kernlab")

modelo_arvore3 <- decision_tree(cost_complexity = tune(), tree_depth = tune(),
                         min_n = tune()) |> 
  set_engine(engine = "rpart") |> 
  set_mode("regression")

modelo_floresta3 <- 
  rand_forest(mtry = tune(), min_n = tune(), trees = tune()) |>   
  set_engine(engine = "ranger", importance = "impurity") |>  
  set_mode("regression")

Validação Cruzada

validacao_cruzada3 <- 
  treinamento3 |> 
  rsample::vfold_cv(v = 5L, strata = Cooling_Load)

Workflow

wf_todos3 <-
  workflow_set(
    preproc = list(receita_1111, receita_2222),
    models = list(
      knn_fit3 = modelo_knn3,
      elastic_fit3 = modelo_elastic3,
      svm_fit3 = modelo_svm3,
      arvore_fit3<- modelo_arvore3,
      floresta_fit3<- modelo_floresta3
    ),
    cross = TRUE
  )


controle_grid3 <- control_grid(
  save_pred = TRUE,
  save_workflow = TRUE,
  parallel_over = "resamples"
)

Treino

treino3 <-
  wf_todos3 |> 
  workflow_map(
    resamples = validacao_cruzada3,
    grid = 20L,
    control = controle_grid3
  )



autoplot(treino3, metric = "rmse") + 
  labs(
    title = "Avaliação dos modelos de regressão",
    subtitle = "Utilizando a métrica do EQM"
  ) + 
  xlab("Rank dos Workflows") +
  ylab("Erro Quadrático Médio - EQM")

Melhores

melhores3 <- 
  treino3 |> 
  rank_results(select_best = TRUE, rank_metric = "rmse")
melhores3

autoplot(treino3, select_best = TRUE)

NA
NA
treino3 |> 
  rank_results()

melhor_modelo3 <- 
  treino3 |> 
  extract_workflow_set_result(id = "recipe_2_rand_forest") |> 
  select_best(metric = "rmse")

melhor_modelo3
NA
NA
wf_final3 <- 
  treino3 |> 
  extract_workflow(id = "recipe_2_rand_forest") |> 
  finalize_workflow(melhor_modelo2)

teste3 <- 
  wf_final3 |> 
  last_fit(split = divisao_inicial3)

teste3$.metrics
[[1]]
NA

Modelo Final


modelo_final3 <- 
  wf_final3 |> 
  fit(dados3)
modelo_final3
══ Workflow [trained] ═════════════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: rand_forest()

── Preprocessor ───────────────────────────────────────────────────────────────────────
2 Recipe Steps

• step_YeoJohnson()
• step_normalize()

── Model ──────────────────────────────────────────────────────────────────────────────
Ranger result

Call:
 ranger::ranger(x = maybe_data_frame(x), y = y, mtry = min_cols(~4L,      x), num.trees = ~1767L, min.node.size = min_rows(~3L, x),      importance = ~"impurity", num.threads = 1, verbose = FALSE,      seed = sample.int(10^5, 1)) 

Type:                             Regression 
Number of trees:                  1767 
Sample size:                      768 
Number of independent variables:  9 
Mtry:                             4 
Target node size:                 3 
Variable importance mode:         impurity 
Splitrule:                        variance 
OOB prediction error (MSE):       1.780618 
R squared (OOB):                  0.9803253 

Salvando o Modelo


saveRDS(modelo_final3, file = "modelo_final.rds3")

# Lendo o arquivo serializado com o modelo final
load(file = "modelo_final.rds3")

# Fazendo novas previsões
predict(modelo_final3, new_data = novos_dados3)
LS0tDQp0aXRsZTogIlByb3ZhIDIiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIEV4ZXJjaWNpbyAxDQoNCj4NCg0KQ29uc2lkZXJlIG8gcHJvYmxlbWEgZW0gcXVlIG8gb2JqZXRpdm8gw6kgcHJldmVyIGEgcG9udHVhw6fDo28gcGVyZm9tYW5jZSBpbmRleCBkbyBhbHVubyBjb20gYmFzZSBlbSBhbGd1bWFzIHZhcmnDoXZlaXMuIEF2YWxpZSBvIHBvZGVyIHByZWRpdGl2byBkbyBtb2RlbG8gc2VsZWNpb25hZG8uIFN1YSBhbsOhbGlzZSBkZXZlIGNvbnRlciB1bWEgYW7DoWxpc2UgZXhwbG9yYXTDs3JpYSBkb3MgZGFkb3MsIHV0aWxpemFyIHVtIGVzcXVlbWEgZGUgay1mb2xkcyBjcm9zcy12YWxpZGF0aW9uLCBjb20gaz0yMCBlIGNvbnNpZGVyYXIgdW0gZXNxdWVtYSBkZSBkYXRhIHNwbGl0dGluZyBuYSBwcm9wb3LDp8OjbyA5MCUgcGFyYSB0cmVpbm8gZSAxDQowJSBwYXJhIHRlc3RlLg0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeW1vZGVscykNCmxpYnJhcnkocnBhcnQpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpsaWJyYXJ5KHNraW1yKQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkodmlzZGF0KQ0KbGlicmFyeShkcGx5cikNCnRpZHltb2RlbHM6OnRpZHltb2RlbHNfcHJlZmVyKCkNCg0KYGBgDQoNCj4NCg0KIyBBbsOhbGlzZSBkZSBEYWRvcw0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0V9DQpkYWRvczwtIHJlYWQuY3N2KCJDOi9Vc2Vycy9lbHRvbi9EZXNrdG9wL1N0dWRlbnRfUGVyZm9ybWFuY2UuY3N2IikNCmRhZG9zJEV4dHJhY3VycmljdWxhci5BY3Rpdml0aWVzPC0gaWZlbHNlKGRhZG9zJEV4dHJhY3VycmljdWxhci5BY3Rpdml0aWVzPT0iWWVzIiwxLDApDQpza2ltKGRhZG9zKQ0KdmlzX2RhdChkYWRvcykNCmF0dGFjaChkYWRvcykNCmBgYA0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRSxmaWcuaGVpZ2h0PTgsZmlnLndpZHRoPTEwfQ0KcmhvX2hhdCA8LSBjb3IoZGFkb3MpDQpjb3JycGxvdChyaG9faGF0LCBtZXRob2QgPSAibnVtYmVyIikNCg0KYGBgDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdmlzX2NvcihkYWRvcykNCg0KYGBgDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KZGFkb3MgfD4gZHBseXI6OmdsaW1wc2UoKQ0KDQpgYGANCj4NCg0KIyBUcmF0YW1lbnRvIGRvcyBkYWRvcyBlIHByZXBhcmHDp8OjbyBkYXMgcmVjZWl0YXMNCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpzZXQuc2VlZCgwKSAjIEZpeGFuZG8gdW1hIHNlbWVudGUNCmRpdmlzYW9faW5pY2lhbCA8LSByc2FtcGxlOjppbml0aWFsX3NwbGl0KGRhZG9zLCBwcm9wID0gMC45LCBzdHJhdGEgPSJQZXJmb3JtYW5jZS5JbmRleCIpDQp0cmVpbmFtZW50byA8LSByc2FtcGxlOjp0cmFpbmluZyhkaXZpc2FvX2luaWNpYWwpICMgQ29uanVudG8gZGUgdHJlaW5hbWVudG8NCnRlc3RlIDwtIHJzYW1wbGU6OnRlc3RpbmcoZGl2aXNhb19pbmljaWFsKSAjIENvbmp1bnRvIGRlIHRlc3RlDQp0ZXN0ZQ0KDQpgYGANCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpyZWNlaXRhXzEgPC0gDQogIHRyZWluYW1lbnRvIHw+IA0KICByZWNpcGUoZm9ybXVsYSA9IFBlcmZvcm1hbmNlLkluZGV4IH4gLikgfD4NCiAgc3RlcF9ZZW9Kb2huc29uKGFsbF9wcmVkaWN0b3JzKCkpIHw+DQogIHN0ZXBfbm9ybWFsaXplKGFsbF9wcmVkaWN0b3JzKCkpIHw+DQogIHN0ZXBfenYoYWxsX3ByZWRpY3RvcnMoKSkgfD4NCiAgc3RlcF9jb3JyKGFsbF9wcmVkaWN0b3JzKCkpICANCiAgDQpgYGANCg0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnJlY2VpdGFfMiA8LSANCiAgdHJlaW5hbWVudG8gfD4gDQogIHJlY2lwZShmb3JtdWxhID0gUGVyZm9ybWFuY2UuSW5kZXggfiAuKSB8Pg0KICBzdGVwX1llb0pvaG5zb24oYWxsX3ByZWRpY3RvcnMoKSkgfD4NCiAgc3RlcF9ub3JtYWxpemUoYWxsX3ByZWRpY3RvcnMoKSkgDQogIA0KDQpgYGANCg0KPg0KDQoNCiMgQ29uZmlndXJhbmRvIG9zIE1vZGVsb3MNCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCm1vZGVsb19lbGFzdGljIDwtIA0KICBwYXJzbmlwOjpsaW5lYXJfcmVnKHBlbmFsdHkgPSB0dW5lOjp0dW5lKCksIG1peHR1cmUgPSB0dW5lOjp0dW5lKCkpIHw+IA0KICBwYXJzbmlwOjpzZXRfbW9kZSgicmVncmVzc2lvbiIpIHw+IA0KICBwYXJzbmlwOjpzZXRfZW5naW5lKCJnbG1uZXQiKQ0KDQptb2RlbG9fa25uIDwtDQogIHBhcnNuaXA6Om5lYXJlc3RfbmVpZ2hib3IoDQogICAgbmVpZ2hib3JzID0gdHVuZTo6dHVuZSgpLA0KICAgIGRpc3RfcG93ZXIgPSB0dW5lOjp0dW5lKCksIA0KICAgIHdlaWdodF9mdW5jID0gImdhdXNzaWFuIiANCiAgKSB8PiANCiAgcGFyc25pcDo6c2V0X21vZGUoInJlZ3Jlc3Npb24iKSB8PiANCiAgcGFyc25pcDo6c2V0X2VuZ2luZSgia2tubiIpDQoNCm1vZGVsb19zdm0gPC0gDQogIHBhcnNuaXA6OnN2bV9yYmYoDQogICAgY29zdCA9IHR1bmU6OnR1bmUoKSwNCiAgICByYmZfc2lnbWEgPSB0dW5lOjp0dW5lKCksDQogICAgbWFyZ2luID0gdHVuZTo6dHVuZSgpDQogICkgfD4gDQogIHBhcnNuaXA6OnNldF9tb2RlKCJyZWdyZXNzaW9uIikgfD4gDQogIHBhcnNuaXA6OnNldF9lbmdpbmUoImtlcm5sYWIiKQ0KDQptb2RlbG9fYXJ2b3JlIDwtIGRlY2lzaW9uX3RyZWUoY29zdF9jb21wbGV4aXR5ID0gdHVuZSgpLCB0cmVlX2RlcHRoID0gdHVuZSgpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1pbl9uID0gdHVuZSgpKSB8PiANCiAgc2V0X2VuZ2luZShlbmdpbmUgPSAicnBhcnQiKSB8PiANCiAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKQ0KDQptb2RlbG9fZmxvcmVzdGEgPC0gDQogIHJhbmRfZm9yZXN0KG10cnkgPSB0dW5lKCksIG1pbl9uID0gdHVuZSgpLCB0cmVlcyA9IHR1bmUoKSkgfD4gICANCiAgc2V0X2VuZ2luZShlbmdpbmUgPSAicmFuZ2VyIiwgaW1wb3J0YW5jZSA9ICJpbXB1cml0eSIpIHw+ICANCiAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKQ0KDQpgYGANCg0KPg0KDQojIFZhbGlkYcOnw6NvIENydXphZGENCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQp2YWxpZGFjYW9fY3J1emFkYSA8LSANCiAgdHJlaW5hbWVudG8gfD4gDQogIHJzYW1wbGU6OnZmb2xkX2N2KHYgPSA1TCwgc3RyYXRhID0gUGVyZm9ybWFuY2UuSW5kZXgpDQoNCmBgYA0KDQo+DQoNCiMgV29ya2Zsb3cNCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQp3Zl90b2RvcyA8LQ0KICB3b3JrZmxvd19zZXQoDQogICAgcHJlcHJvYyA9IGxpc3QocmVjZWl0YV8xLCByZWNlaXRhXzIpLA0KICAgIG1vZGVscyA9IGxpc3QoDQogICAgICBrbm5fZml0ID0gbW9kZWxvX2tubiwNCiAgICAgIGVsYXN0aWNfZml0ID0gbW9kZWxvX2VsYXN0aWMsDQogICAgICBzdm1fZml0ID0gbW9kZWxvX3N2bSwNCiAgICAgIGFydm9yZV9maXQ8LSBtb2RlbG9fYXJ2b3JlLA0KICAgICAgZmxvcmVzdGFfZml0PC0gbW9kZWxvX2Zsb3Jlc3RhDQogICAgKSwNCiAgICBjcm9zcyA9IFRSVUUNCiAgKQ0KDQoNCmNvbnRyb2xlX2dyaWQgPC0gY29udHJvbF9ncmlkKA0KICBzYXZlX3ByZWQgPSBUUlVFLA0KICBzYXZlX3dvcmtmbG93ID0gVFJVRSwNCiAgcGFyYWxsZWxfb3ZlciA9ICJyZXNhbXBsZXMiDQopDQoNCg0KYGBgDQoNCj4NCg0KIyBUcmVpbm8NCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQp0cmVpbm8gPC0NCiAgd2ZfdG9kb3MgfD4gDQogIHdvcmtmbG93X21hcCgNCiAgICByZXNhbXBsZXMgPSB2YWxpZGFjYW9fY3J1emFkYSwNCiAgICBncmlkID0gMjBMLA0KICAgIGNvbnRyb2wgPSBjb250cm9sZV9ncmlkDQogICkNCg0KDQoNCmF1dG9wbG90KHRyZWlubywgbWV0cmljID0gInJtc2UiKSArIA0KICBsYWJzKA0KICAgIHRpdGxlID0gIkF2YWxpYcOnw6NvIGRvcyBtb2RlbG9zIGRlIHJlZ3Jlc3PDo28iLA0KICAgIHN1YnRpdGxlID0gIlV0aWxpemFuZG8gYSBtw6l0cmljYSBkbyBFUU0iDQogICkgKyANCiAgeGxhYigiUmFuayBkb3MgV29ya2Zsb3dzIikgKw0KICB5bGFiKCJFcnJvIFF1YWRyw6F0aWNvIE3DqWRpbyAtIEVRTSIpDQoNCmBgYA0KDQo+DQoNCiMgTWVsaG9yZXMgDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbWVsaG9yZXMgPC0gDQogIHRyZWlubyB8PiANCiAgcmFua19yZXN1bHRzKHNlbGVjdF9iZXN0ID0gVFJVRSwgcmFua19tZXRyaWMgPSAicm1zZSIpDQptZWxob3Jlcw0KDQphdXRvcGxvdCh0cmVpbm8sIHNlbGVjdF9iZXN0ID0gVFJVRSkNCg0KDQpgYGANCg0KPg0KDQojIE1lbGhvciBNb2RlbG8NCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQp0cmVpbm8gfD4gDQogIHJhbmtfcmVzdWx0cygpDQoNCm1lbGhvcl9tb2RlbG8gPC0gDQogIHRyZWlubyB8PiANCiAgZXh0cmFjdF93b3JrZmxvd19zZXRfcmVzdWx0KGlkID0gInJlY2lwZV8xX3N2bV9maXQiKSB8PiANCiAgc2VsZWN0X2Jlc3QobWV0cmljID0gInJtc2UiKQ0KDQptZWxob3JfbW9kZWxvDQoNCmBgYA0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQp3Zl9maW5hbCA8LSANCiAgdHJlaW5vIHw+IA0KICBleHRyYWN0X3dvcmtmbG93KGlkID0gInJlY2lwZV8xX3N2bV9maXQiKSB8PiANCiAgZmluYWxpemVfd29ya2Zsb3cobWVsaG9yX21vZGVsbykNCg0KdGVzdGUgPC0gDQogIHdmX2ZpbmFsIHw+IA0KICBsYXN0X2ZpdChzcGxpdCA9IGRpdmlzYW9faW5pY2lhbCkNCg0KdGVzdGUkLm1ldHJpY3MNCg0KYGBgDQoNCj4NCg0KIyBNb2RlbG8gRmluYWwNCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQptb2RlbG9fZmluYWwgPC0gDQogIHdmX2ZpbmFsIHw+IA0KICBmaXQoZGFkb3MpDQptb2RlbG9fZmluYWwNCmBgYA0KPg0KDQojIFNhbHZhbmRvIG8gTW9kZWxvDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLGVycm9yPUZBTFNFfQ0KDQpzYXZlUkRTKG1vZGVsb19maW5hbCwgZmlsZSA9ICJtb2RlbG9fZmluYWwucmRzIikNCg0KIyBMZW5kbyBvIGFycXVpdm8gc2VyaWFsaXphZG8gY29tIG8gbW9kZWxvIGZpbmFsDQpsb2FkKGZpbGUgPSAibW9kZWxvX2ZpbmFsLnJkcyIpDQoNCiMgRmF6ZW5kbyBub3ZhcyBwcmV2aXPDtWVzDQpwcmVkaWN0KG1vZGVsb19maW5hbCwgbmV3X2RhdGEgPSBub3Zvc19kYWRvcykNCg0KYGBgDQoNCj4NCg0KIyBFeGVyY2ljaW8gMg0KDQo+DQoNCkNvbnNpZGVyZSBvIHByb2JsZW1hIGVtIHF1ZSBvIG9iamV0aXZvIMOpIHByZXZlciBvIGN1c3RvLCBjaGFyZ2UsIGNvbSBiYXNlIGVtIGFsZ3VtYXMgdmFyacOhdmVpcy4gU3VhIGFuw6FsaXNlIGRldmUgY29udGVyIHVtYSBhbsOhbGlzZSBleHBsb3JhdMOzcmlhIGRvcyBkYWRvcywgZGV2ZXLDoSB1dGlsaXphciB1bSBlc3F1ZW1hIGRlIGstZm9sZHMgY3Jvc3MtdmFsaWRhdGlvbiwgY29tIGs9MjAgZSBjb25zaWRlcmFyIHVtIGVzcXVlbWEgZGUgZGF0YSBzcGxpdHRpbmcgbmEgcHJvcG9yw6fDo28gOTAlIHBhcmEgdHJlaW5vIGUgMTAlIHBhcmEgdGVzdGUuDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KZGFkb3MxPC1yZWFkLmNzdigiQzovVXNlcnMvZWx0b24vRGVza3RvcC9pbnN1cmFuY2UuY3N2IikNCmRhZG9zMTwtIGRhZG9zMSB8PiBzZWxlY3QoLXJlZ2lvbikNCmRhZG9zMSRzZXg8LSBpZmVsc2UoZGFkb3MxJHNleD09Im1hbGUiLDEsMCkNCmRhZG9zMSRzbW9rZXI8LSBpZmVsc2UoZGFkb3MxJHNtb2tlcj09InllcyIsMSwwKQ0KDQoNCmBgYA0KDQoNCj4NCg0KIyBEZXNjcml0aXZhIGRvcyBkYWRvcw0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnNraW0oZGFkb3MxKQ0KdmlzX2RhdChkYWRvczEpDQpyaG9faGF0MSA8LSBjb3IoZGFkb3MxKQ0KY29ycnBsb3QocmhvX2hhdDEsIG1ldGhvZCA9ICJudW1iZXIiKQ0KdmlzX2NvcihkYWRvczEpDQpgYGANCg0KPg0KDQojIFRyYXRhbWVudG8gZG9zIGRhZG9zIGUgcHJlcGFyYcOnw6NvIGRhcyByZWNlaXRhcw0KDQo+DQoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KYXR0YWNoKGRhZG9zMSkNCnNldC5zZWVkKDApIA0KZGl2aXNhb19pbmljaWFsMSA8LSByc2FtcGxlOjppbml0aWFsX3NwbGl0KGRhZG9zMSwgcHJvcCA9IDAuOSwgc3RyYXRhID0iY2hhcmdlcyIpDQp0cmVpbmFtZW50bzEgPC0gcnNhbXBsZTo6dHJhaW5pbmcoZGl2aXNhb19pbmljaWFsMSkgDQp0ZXN0ZTEgPC0gcnNhbXBsZTo6dGVzdGluZyhkaXZpc2FvX2luaWNpYWwxKQ0KDQpgYGANCg0KPg0KDQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnJlY2VpdGFfMTEgPC0gDQogIHRyZWluYW1lbnRvMSB8PiANCiAgcmVjaXBlKGZvcm11bGEgPSBjaGFyZ2VzIH4gLikgfD4NCiAgc3RlcF9ZZW9Kb2huc29uKGFsbF9wcmVkaWN0b3JzKCkpIHw+DQogIHN0ZXBfbm9ybWFsaXplKGFsbF9wcmVkaWN0b3JzKCkpIHw+DQogIHN0ZXBfenYoYWxsX3ByZWRpY3RvcnMoKSkgfD4NCiAgc3RlcF9jb3JyKGFsbF9wcmVkaWN0b3JzKCkpICANCiAgDQpgYGANCg0KPg0KDQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnJlY2VpdGFfMjIgPC0gDQogIHRyZWluYW1lbnRvMSB8PiANCiAgcmVjaXBlKGZvcm11bGEgPSBjaGFyZ2VzIH4gLikgfD4NCiAgc3RlcF9ZZW9Kb2huc29uKGFsbF9wcmVkaWN0b3JzKCkpIHw+DQogIHN0ZXBfbm9ybWFsaXplKGFsbF9wcmVkaWN0b3JzKCkpIA0KICANCg0KYGBgDQoNCg0KPg0KDQojIENvbmZpZ3VyYW5kbyBvcyBNb2RlbG9zDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQptb2RlbG9fZWxhc3RpYzEgPC0gDQogIHBhcnNuaXA6OmxpbmVhcl9yZWcocGVuYWx0eSA9IHR1bmU6OnR1bmUoKSwgbWl4dHVyZSA9IHR1bmU6OnR1bmUoKSkgfD4gDQogIHBhcnNuaXA6OnNldF9tb2RlKCJyZWdyZXNzaW9uIikgfD4gDQogIHBhcnNuaXA6OnNldF9lbmdpbmUoImdsbW5ldCIpDQoNCm1vZGVsb19rbm4xIDwtDQogIHBhcnNuaXA6Om5lYXJlc3RfbmVpZ2hib3IoDQogICAgbmVpZ2hib3JzID0gdHVuZTo6dHVuZSgpLA0KICAgIGRpc3RfcG93ZXIgPSB0dW5lOjp0dW5lKCksIA0KICAgIHdlaWdodF9mdW5jID0gImdhdXNzaWFuIiANCiAgKSB8PiANCiAgcGFyc25pcDo6c2V0X21vZGUoInJlZ3Jlc3Npb24iKSB8PiANCiAgcGFyc25pcDo6c2V0X2VuZ2luZSgia2tubiIpDQoNCm1vZGVsb19zdm0xIDwtIA0KICBwYXJzbmlwOjpzdm1fcmJmKA0KICAgIGNvc3QgPSB0dW5lOjp0dW5lKCksDQogICAgcmJmX3NpZ21hID0gdHVuZTo6dHVuZSgpLA0KICAgIG1hcmdpbiA9IHR1bmU6OnR1bmUoKQ0KICApIHw+IA0KICBwYXJzbmlwOjpzZXRfbW9kZSgicmVncmVzc2lvbiIpIHw+IA0KICBwYXJzbmlwOjpzZXRfZW5naW5lKCJrZXJubGFiIikNCg0KbW9kZWxvX2Fydm9yZTEgPC0gZGVjaXNpb25fdHJlZShjb3N0X2NvbXBsZXhpdHkgPSB0dW5lKCksIHRyZWVfZGVwdGggPSB0dW5lKCksDQogICAgICAgICAgICAgICAgICAgICAgICAgbWluX24gPSB0dW5lKCkpIHw+IA0KICBzZXRfZW5naW5lKGVuZ2luZSA9ICJycGFydCIpIHw+IA0KICBzZXRfbW9kZSgicmVncmVzc2lvbiIpDQoNCm1vZGVsb19mbG9yZXN0YTEgPC0gDQogIHJhbmRfZm9yZXN0KG10cnkgPSB0dW5lKCksIG1pbl9uID0gdHVuZSgpLCB0cmVlcyA9IHR1bmUoKSkgfD4gICANCiAgc2V0X2VuZ2luZShlbmdpbmUgPSAicmFuZ2VyIiwgaW1wb3J0YW5jZSA9ICJpbXB1cml0eSIpIHw+ICANCiAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKQ0KDQpgYGANCg0KDQo+DQoNCiMgVmFsaWRhw6fDo28gQ3J1emFkYQ0KDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdmFsaWRhY2FvX2NydXphZGExIDwtIA0KICB0cmVpbmFtZW50bzEgfD4gDQogIHJzYW1wbGU6OnZmb2xkX2N2KHYgPSA1TCwgc3RyYXRhID0gY2hhcmdlcykNCg0KYGBgDQoNCj4NCg0KIyBXb3JrZmxvdw0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCndmX3RvZG9zMSA8LQ0KICB3b3JrZmxvd19zZXQoDQogICAgcHJlcHJvYyA9IGxpc3QocmVjZWl0YV8xMSwgcmVjZWl0YV8yMiksDQogICAgbW9kZWxzID0gbGlzdCgNCiAgICAgIGtubl9maXQxID0gbW9kZWxvX2tubjEsDQogICAgICBlbGFzdGljX2ZpdDEgPSBtb2RlbG9fZWxhc3RpYzEsDQogICAgICBzdm1fZml0MSA9IG1vZGVsb19zdm0xLA0KICAgICAgYXJ2b3JlX2ZpdDE8LSBtb2RlbG9fYXJ2b3JlMSwNCiAgICAgIGZsb3Jlc3RhX2ZpdDE8LSBtb2RlbG9fZmxvcmVzdGExDQogICAgKSwNCiAgICBjcm9zcyA9IFRSVUUNCiAgKQ0KDQoNCmNvbnRyb2xlX2dyaWQxIDwtIGNvbnRyb2xfZ3JpZCgNCiAgc2F2ZV9wcmVkID0gVFJVRSwNCiAgc2F2ZV93b3JrZmxvdyA9IFRSVUUsDQogIHBhcmFsbGVsX292ZXIgPSAicmVzYW1wbGVzIg0KKQ0KDQoNCmBgYA0KDQo+DQoNCiMgVHJlaW5vDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdHJlaW5vMSA8LQ0KICB3Zl90b2RvczEgfD4gDQogIHdvcmtmbG93X21hcCgNCiAgICByZXNhbXBsZXMgPSB2YWxpZGFjYW9fY3J1emFkYTEsDQogICAgZ3JpZCA9IDIwTCwNCiAgICBjb250cm9sID0gY29udHJvbGVfZ3JpZDENCiAgKQ0KDQoNCg0KYXV0b3Bsb3QodHJlaW5vMSwgbWV0cmljID0gInJtc2UiKSArIA0KICBsYWJzKA0KICAgIHRpdGxlID0gIkF2YWxpYcOnw6NvIGRvcyBtb2RlbG9zIGRlIHJlZ3Jlc3PDo28iLA0KICAgIHN1YnRpdGxlID0gIlV0aWxpemFuZG8gYSBtw6l0cmljYSBkbyBFUU0iDQogICkgKyANCiAgeGxhYigiUmFuayBkb3MgV29ya2Zsb3dzIikgKw0KICB5bGFiKCJFcnJvIFF1YWRyw6F0aWNvIE3DqWRpbyAtIEVRTSIpDQoNCmBgYA0KDQo+DQoNCg0KIyBNZWxob3JlcyANCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQptZWxob3JlczEgPC0gDQogIHRyZWlubzEgfD4gDQogIHJhbmtfcmVzdWx0cyhzZWxlY3RfYmVzdCA9IFRSVUUsIHJhbmtfbWV0cmljID0gInJtc2UiKQ0KbWVsaG9yZXMxDQoNCmF1dG9wbG90KHRyZWlubzEsIHNlbGVjdF9iZXN0ID0gVFJVRSkNCg0KDQpgYGANCg0KPg0KDQojIE1lbGhvciBNb2RlbG8NCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQp0cmVpbm8xIHw+IA0KICByYW5rX3Jlc3VsdHMoKQ0KDQptZWxob3JfbW9kZWxvMSA8LSANCiAgdHJlaW5vMSB8PiANCiAgZXh0cmFjdF93b3JrZmxvd19zZXRfcmVzdWx0KGlkID0gInJlY2lwZV8yX3JhbmRfZm9yZXN0IikgfD4gDQogIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJybXNlIikNCg0KbWVsaG9yX21vZGVsbzENCg0KYGBgDQoNCg0KPg0KDQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCndmX2ZpbmFsMSA8LSANCiAgdHJlaW5vMSB8PiANCiAgZXh0cmFjdF93b3JrZmxvdyhpZCA9ICJyZWNpcGVfMl9yYW5kX2ZvcmVzdCIpIHw+IA0KICBmaW5hbGl6ZV93b3JrZmxvdyhtZWxob3JfbW9kZWxvMSkNCg0KdGVzdGUxIDwtIA0KICB3Zl9maW5hbDEgfD4gDQogIGxhc3RfZml0KHNwbGl0ID0gZGl2aXNhb19pbmljaWFsMSkNCg0KdGVzdGUxJC5tZXRyaWNzDQoNCmBgYA0KDQo+DQoNCg0KIyBNb2RlbG8gRmluYWwNCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCm1vZGVsb19maW5hbDEgPC0gDQogIHdmX2ZpbmFsMSB8PiANCiAgZml0KGRhZG9zMSkNCm1vZGVsb19maW5hbDENCmBgYA0KDQo+DQoNCiMgU2FsdmFuZG8gbyBNb2RlbG8NCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZXJyb3I9RkFMU0V9DQoNCnNhdmVSRFMobW9kZWxvX2ZpbmFsMSwgZmlsZSA9ICJtb2RlbG9fZmluYWwucmRzMSIpDQoNCiMgTGVuZG8gbyBhcnF1aXZvIHNlcmlhbGl6YWRvIGNvbSBvIG1vZGVsbyBmaW5hbA0KbG9hZChmaWxlID0gIm1vZGVsb19maW5hbC5yZHMxIikNCg0KIyBGYXplbmRvIG5vdmFzIHByZXZpc8O1ZXMNCnByZWRpY3QobW9kZWxvX2ZpbmFsMSwgbmV3X2RhdGEgPSBub3Zvc19kYWRvczEpDQoNCmBgYA0KDQo+DQoNCiMgRXhlcmPDrWNpbyAzDQoNCj4NCg0KQ29uc2lkZXJlIG8gcHJvYmxlbWEgZW0gcXVlIG8gb2JqZXRpdm8gw6kgcHJldmVyIGEgcXVhbGlkYWRlLCBxdWFsaXR5LCBjb20gYmFzZSBlbSBhbGd1bWFzIHZhcmnDoXZlaXMuIFN1YSBhbsOhbGlzZSBkZXZlIGNvbnRlciB1bWEgYW7DoWxpc2UgZXhwbG9yYXTDs3JpYSBkb3MgZGFkb3MsIHV0aWxpemFyIHVtIGVzcXVlbWEgZGUgay1mb2xkcyBjcm9zcy12YWxpZGF0aW9uLCBjb20gaz0yMCBlIGNvbnNpZGVyYXIgdW0gZXNxdWVtYSBkZSBkYXRhIHNwbGl0dGluZyBuYSBwcm9wb3LDp8OjbyA5MCUgcGFyYSB0cmVpbm8gZSAxMCUgcGFyYSB0ZXN0ZS4NCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpkYWRvczI8LSByZWFkLmNzdigiQzovVXNlcnMvZWx0b24vRGVza3RvcC93aW5lcXVhbGl0eS1yZWQuY3N2IikNCmRhZG9zMjwtIGRhZG9zMiB8PiByZW5hbWUoYyhhY2lkZXpfZml4YT1maXhlZC5hY2lkaXR5LHZvdGFsaWRhZGVfYWNpZGV6PXZvbGF0aWxlLmFjaWRpdHksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNpZGV6X2NpdHJpY2E9Y2l0cmljLmFjaWQsYcOndWNhcl9yZXNpZHVhbD1yZXNpZHVhbC5zdWdhciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbG9yZXRvcz1jaGxvcmlkZXMsZGlveGlkb19lbnhvZnJlX2xpdnJlPWZyZWUuc3VsZnVyLmRpb3hpZGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdG90YWxfZGlveGlkb19lbnhvZnJlPXRvdGFsLnN1bGZ1ci5kaW94aWRlLGRlbnNpZGFkZT1kZW5zaXR5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bGZhdG9zPXN1bHBoYXRlcyxhbGNvb2w9YWxjb2hvbCxxdWFsaWRhZGU9cXVhbGl0eSkpDQoNCmBgYA0KDQo+DQoNCiMgRGVzY3JpdGl2YSBkb3MgZGFkb3MNCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpza2ltKGRhZG9zMikNCnZpc19kYXQoZGFkb3MyKQ0KdmlzX2NvcihkYWRvczIpDQoNCmBgYA0KDQo+DQoNCiMgVHJhdGFtZW50byBkb3MgZGFkb3MgZSBwcmVwYXJhw6fDo28gZGFzIHJlY2VpdGFzDQoNCj4NCg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQphdHRhY2goZGFkb3MyKQ0Kc2V0LnNlZWQoMCkgDQpkaXZpc2FvX2luaWNpYWwyIDwtIHJzYW1wbGU6OmluaXRpYWxfc3BsaXQoZGFkb3MyLCBwcm9wID0gMC45LCBzdHJhdGEgPSJxdWFsaWRhZGUiKQ0KdHJlaW5hbWVudG8yIDwtIHJzYW1wbGU6OnRyYWluaW5nKGRpdmlzYW9faW5pY2lhbDIpIA0KdGVzdGUyIDwtIHJzYW1wbGU6OnRlc3RpbmcoZGl2aXNhb19pbmljaWFsMikNCg0KYGBgDQoNCj4NCg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpyZWNlaXRhXzExMSA8LSANCiAgdHJlaW5hbWVudG8yIHw+IA0KICByZWNpcGUoZm9ybXVsYSA9IHF1YWxpZGFkZSB+IC4pIHw+DQogIHN0ZXBfWWVvSm9obnNvbihhbGxfcHJlZGljdG9ycygpKSB8Pg0KICBzdGVwX25vcm1hbGl6ZShhbGxfcHJlZGljdG9ycygpKSB8Pg0KICBzdGVwX3p2KGFsbF9wcmVkaWN0b3JzKCkpIHw+DQogIHN0ZXBfY29ycihhbGxfcHJlZGljdG9ycygpKSAgDQogIA0KYGBgDQoNCj4NCg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpyZWNlaXRhXzIyMiA8LSANCiAgdHJlaW5hbWVudG8yIHw+IA0KICByZWNpcGUoZm9ybXVsYSA9IHF1YWxpZGFkZSB+IC4pIHw+DQogIHN0ZXBfWWVvSm9obnNvbihhbGxfcHJlZGljdG9ycygpKSB8Pg0KICBzdGVwX25vcm1hbGl6ZShhbGxfcHJlZGljdG9ycygpKSANCiAgDQoNCmBgYA0KDQoNCj4NCg0KIyBDb25maWd1cmFuZG8gb3MgTW9kZWxvcw0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCg0KbW9kZWxvX2VsYXN0aWMyIDwtIA0KICBwYXJzbmlwOjpsaW5lYXJfcmVnKHBlbmFsdHkgPSB0dW5lOjp0dW5lKCksIG1peHR1cmUgPSB0dW5lOjp0dW5lKCkpIHw+IA0KICBwYXJzbmlwOjpzZXRfbW9kZSgicmVncmVzc2lvbiIpIHw+IA0KICBwYXJzbmlwOjpzZXRfZW5naW5lKCJnbG1uZXQiKQ0KDQptb2RlbG9fa25uMiA8LQ0KICBwYXJzbmlwOjpuZWFyZXN0X25laWdoYm9yKA0KICAgIG5laWdoYm9ycyA9IHR1bmU6OnR1bmUoKSwNCiAgICBkaXN0X3Bvd2VyID0gdHVuZTo6dHVuZSgpLCANCiAgICB3ZWlnaHRfZnVuYyA9ICJnYXVzc2lhbiIgDQogICkgfD4gDQogIHBhcnNuaXA6OnNldF9tb2RlKCJyZWdyZXNzaW9uIikgfD4gDQogIHBhcnNuaXA6OnNldF9lbmdpbmUoImtrbm4iKQ0KDQptb2RlbG9fc3ZtMiA8LSANCiAgcGFyc25pcDo6c3ZtX3JiZigNCiAgICBjb3N0ID0gdHVuZTo6dHVuZSgpLA0KICAgIHJiZl9zaWdtYSA9IHR1bmU6OnR1bmUoKSwNCiAgICBtYXJnaW4gPSB0dW5lOjp0dW5lKCkNCiAgKSB8PiANCiAgcGFyc25pcDo6c2V0X21vZGUoInJlZ3Jlc3Npb24iKSB8PiANCiAgcGFyc25pcDo6c2V0X2VuZ2luZSgia2VybmxhYiIpDQoNCm1vZGVsb19hcnZvcmUyIDwtIGRlY2lzaW9uX3RyZWUoY29zdF9jb21wbGV4aXR5ID0gdHVuZSgpLCB0cmVlX2RlcHRoID0gdHVuZSgpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1pbl9uID0gdHVuZSgpKSB8PiANCiAgc2V0X2VuZ2luZShlbmdpbmUgPSAicnBhcnQiKSB8PiANCiAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKQ0KDQptb2RlbG9fZmxvcmVzdGEyIDwtIA0KICByYW5kX2ZvcmVzdChtdHJ5ID0gdHVuZSgpLCBtaW5fbiA9IHR1bmUoKSwgdHJlZXMgPSB0dW5lKCkpIHw+ICAgDQogIHNldF9lbmdpbmUoZW5naW5lID0gInJhbmdlciIsIGltcG9ydGFuY2UgPSAiaW1wdXJpdHkiKSB8PiAgDQogIHNldF9tb2RlKCJyZWdyZXNzaW9uIikNCg0KYGBgDQoNCg0KPg0KDQojIFZhbGlkYcOnw6NvIENydXphZGENCg0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnZhbGlkYWNhb19jcnV6YWRhMiA8LSANCiAgdHJlaW5hbWVudG8yIHw+IA0KICByc2FtcGxlOjp2Zm9sZF9jdih2ID0gNUwsIHN0cmF0YSA9IHF1YWxpZGFkZSkNCg0KYGBgDQoNCj4NCg0KIyBXb3JrZmxvdw0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCndmX3RvZG9zMiA8LQ0KICB3b3JrZmxvd19zZXQoDQogICAgcHJlcHJvYyA9IGxpc3QocmVjZWl0YV8xMTEsIHJlY2VpdGFfMjIyKSwNCiAgICBtb2RlbHMgPSBsaXN0KA0KICAgICAga25uX2ZpdDIgPSBtb2RlbG9fa25uMiwNCiAgICAgIGVsYXN0aWNfZml0MiA9IG1vZGVsb19lbGFzdGljMiwNCiAgICAgIHN2bV9maXQyID0gbW9kZWxvX3N2bTIsDQogICAgICBhcnZvcmVfZml0MjwtIG1vZGVsb19hcnZvcmUyLA0KICAgICAgZmxvcmVzdGFfZml0MjwtIG1vZGVsb19mbG9yZXN0YTINCiAgICApLA0KICAgIGNyb3NzID0gVFJVRQ0KICApDQoNCg0KY29udHJvbGVfZ3JpZDIgPC0gY29udHJvbF9ncmlkKA0KICBzYXZlX3ByZWQgPSBUUlVFLA0KICBzYXZlX3dvcmtmbG93ID0gVFJVRSwNCiAgcGFyYWxsZWxfb3ZlciA9ICJyZXNhbXBsZXMiDQopDQoNCg0KYGBgDQoNCj4NCg0KIyBUcmVpbm8NCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQp0cmVpbm8yIDwtDQogIHdmX3RvZG9zMiB8PiANCiAgd29ya2Zsb3dfbWFwKA0KICAgIHJlc2FtcGxlcyA9IHZhbGlkYWNhb19jcnV6YWRhMiwNCiAgICBncmlkID0gMjBMLA0KICAgIGNvbnRyb2wgPSBjb250cm9sZV9ncmlkMg0KICApDQoNCg0KDQphdXRvcGxvdCh0cmVpbm8yLCBtZXRyaWMgPSAicm1zZSIpICsgDQogIGxhYnMoDQogICAgdGl0bGUgPSAiQXZhbGlhw6fDo28gZG9zIG1vZGVsb3MgZGUgcmVncmVzc8OjbyIsDQogICAgc3VidGl0bGUgPSAiVXRpbGl6YW5kbyBhIG3DqXRyaWNhIGRvIEVRTSINCiAgKSArIA0KICB4bGFiKCJSYW5rIGRvcyBXb3JrZmxvd3MiKSArDQogIHlsYWIoIkVycm8gUXVhZHLDoXRpY28gTcOpZGlvIC0gRVFNIikNCg0KYGBgDQo+DQoNCg0KIyBNZWxob3JlcyANCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQptZWxob3JlczIgPC0gDQogIHRyZWlubzIgfD4gDQogIHJhbmtfcmVzdWx0cyhzZWxlY3RfYmVzdCA9IFRSVUUsIHJhbmtfbWV0cmljID0gInJtc2UiKQ0KbWVsaG9yZXMyDQoNCmF1dG9wbG90KHRyZWlubzIsIHNlbGVjdF9iZXN0ID0gVFJVRSkNCg0KDQpgYGANCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQp0cmVpbm8yIHw+IA0KICByYW5rX3Jlc3VsdHMoKQ0KDQptZWxob3JfbW9kZWxvMiA8LSANCiAgdHJlaW5vMiB8PiANCiAgZXh0cmFjdF93b3JrZmxvd19zZXRfcmVzdWx0KGlkID0gInJlY2lwZV8yX3JhbmRfZm9yZXN0IikgfD4gDQogIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJybXNlIikNCg0KbWVsaG9yX21vZGVsbzINCg0KYGBgDQoNCg0KPg0KDQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCndmX2ZpbmFsMiA8LSANCiAgdHJlaW5vMiB8PiANCiAgZXh0cmFjdF93b3JrZmxvdyhpZCA9ICJyZWNpcGVfMl9yYW5kX2ZvcmVzdCIpIHw+IA0KICBmaW5hbGl6ZV93b3JrZmxvdyhtZWxob3JfbW9kZWxvMikNCg0KdGVzdGUyIDwtIA0KICB3Zl9maW5hbDIgfD4gDQogIGxhc3RfZml0KHNwbGl0ID0gZGl2aXNhb19pbmljaWFsMikNCg0KdGVzdGUyJC5tZXRyaWNzDQoNCmBgYA0KDQo+DQoNCg0KIyBNb2RlbG8gRmluYWwNCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCm1vZGVsb19maW5hbDIgPC0gDQogIHdmX2ZpbmFsMiB8PiANCiAgZml0KGRhZG9zMikNCm1vZGVsb19maW5hbDINCmBgYA0KDQo+DQoNCiMgU2FsdmFuZG8gbyBNb2RlbG8NCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UsZXJyb3I9RkFMU0V9DQoNCnNhdmVSRFMobW9kZWxvX2ZpbmFsMiwgZmlsZSA9ICJtb2RlbG9fZmluYWwucmRzMiIpDQoNCiMgTGVuZG8gbyBhcnF1aXZvIHNlcmlhbGl6YWRvIGNvbSBvIG1vZGVsbyBmaW5hbA0KbG9hZChmaWxlID0gIm1vZGVsb19maW5hbC5yZHMyIikNCg0KIyBGYXplbmRvIG5vdmFzIHByZXZpc8O1ZXMNCnByZWRpY3QobW9kZWxvX2ZpbmFsMiwgbmV3X2RhdGEgPSBub3Zvc19kYWRvczIpDQoNCmBgYA0KDQoNCj4NCg0KIyBFeGVyY8OtY2lvIDQNCg0KPg0KDQpDb25zaWRlcmUgbyBwcm9ibGVtYSBlbSBxdWUgbyBvYmpldGl2byDDqSBwcmV2ZXIgYSB2YXJpw6F2ZWwgY29vbGluZ19sb2FkIGNvbSBiYXNlIGVtIGFsZ3VtYXMgdmFyacOhdmVpcy4gU3VhIGFuw6FsaXNlIGRldmUgY29udGVyIHVtYSBhbsOhbGlzZSBleHBsb3JhdMOzcmlhIGRvcyBkYWRvcywgdXRpbGl6YXIgdW0gZXNxdWVtYSBkZSBrLWZvbGRzIGNyb3NzLXZhbGlkYXRpb24sIGNvbSBrPTIwIGUgY29uc2lkZXJhciB1bSBlc3F1ZW1hIGRlIGRhdGEgc3BsaXR0aW5nIG5hIHByb3BvcsOnw6NvIDkwJSBwYXJhIHRyZWlubyBlIDEwJSBwYXJhIHRlc3RlLg0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmRhZG9zMzwtcmVhZC5jc3YoIkM6L1VzZXJzL2VsdG9uL0Rvd25sb2Fkcy9lbmVyZ3lfZWZmaWNpZW5jeV9kYXRhLmNzdiIpDQoNCmBgYA0KDQoNCj4NCg0KIyBEZXNjcml0aXZhIGRvcyBkYWRvcw0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnNraW0oZGFkb3MzKQ0KdmlzX2RhdChkYWRvczMpDQp2aXNfY29yKGRhZG9zMykNCg0KYGBgDQoNCg0KDQo+DQoNCiMgVHJhdGFtZW50byBkb3MgZGFkb3MgZSBwcmVwYXJhw6fDo28gZGFzIHJlY2VpdGFzDQoNCj4NCg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQphdHRhY2goZGFkb3MzKQ0Kc2V0LnNlZWQoMCkgDQpkaXZpc2FvX2luaWNpYWwzIDwtIHJzYW1wbGU6OmluaXRpYWxfc3BsaXQoZGFkb3MzLCBwcm9wID0gMC45LCBzdHJhdGEgPSJDb29saW5nX0xvYWQiKQ0KdHJlaW5hbWVudG8zIDwtIHJzYW1wbGU6OnRyYWluaW5nKGRpdmlzYW9faW5pY2lhbDMpIA0KdGVzdGUzIDwtIHJzYW1wbGU6OnRlc3RpbmcoZGl2aXNhb19pbmljaWFsMykNCg0KYGBgDQoNCj4NCg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpyZWNlaXRhXzExMTEgPC0gDQogIHRyZWluYW1lbnRvMyB8PiANCiAgcmVjaXBlKGZvcm11bGEgPSBDb29saW5nX0xvYWQgfiAuKSB8Pg0KICBzdGVwX1llb0pvaG5zb24oYWxsX3ByZWRpY3RvcnMoKSkgfD4NCiAgc3RlcF9ub3JtYWxpemUoYWxsX3ByZWRpY3RvcnMoKSkgfD4NCiAgc3RlcF96dihhbGxfcHJlZGljdG9ycygpKSB8Pg0KICBzdGVwX2NvcnIoYWxsX3ByZWRpY3RvcnMoKSkgIA0KICANCmBgYA0KDQo+DQoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KcmVjZWl0YV8yMjIyIDwtIA0KICB0cmVpbmFtZW50bzMgfD4gDQogIHJlY2lwZShmb3JtdWxhID0gQ29vbGluZ19Mb2FkIH4gLikgfD4NCiAgc3RlcF9ZZW9Kb2huc29uKGFsbF9wcmVkaWN0b3JzKCkpIHw+DQogIHN0ZXBfbm9ybWFsaXplKGFsbF9wcmVkaWN0b3JzKCkpIA0KICANCg0KYGBgDQoNCg0KPg0KDQojIENvbmZpZ3VyYW5kbyBvcyBNb2RlbG9zDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQptb2RlbG9fZWxhc3RpYzMgPC0gDQogIHBhcnNuaXA6OmxpbmVhcl9yZWcocGVuYWx0eSA9IHR1bmU6OnR1bmUoKSwgbWl4dHVyZSA9IHR1bmU6OnR1bmUoKSkgfD4gDQogIHBhcnNuaXA6OnNldF9tb2RlKCJyZWdyZXNzaW9uIikgfD4gDQogIHBhcnNuaXA6OnNldF9lbmdpbmUoImdsbW5ldCIpDQoNCm1vZGVsb19rbm4zIDwtDQogIHBhcnNuaXA6Om5lYXJlc3RfbmVpZ2hib3IoDQogICAgbmVpZ2hib3JzID0gdHVuZTo6dHVuZSgpLA0KICAgIGRpc3RfcG93ZXIgPSB0dW5lOjp0dW5lKCksIA0KICAgIHdlaWdodF9mdW5jID0gImdhdXNzaWFuIiANCiAgKSB8PiANCiAgcGFyc25pcDo6c2V0X21vZGUoInJlZ3Jlc3Npb24iKSB8PiANCiAgcGFyc25pcDo6c2V0X2VuZ2luZSgia2tubiIpDQoNCm1vZGVsb19zdm0zIDwtIA0KICBwYXJzbmlwOjpzdm1fcmJmKA0KICAgIGNvc3QgPSB0dW5lOjp0dW5lKCksDQogICAgcmJmX3NpZ21hID0gdHVuZTo6dHVuZSgpLA0KICAgIG1hcmdpbiA9IHR1bmU6OnR1bmUoKQ0KICApIHw+IA0KICBwYXJzbmlwOjpzZXRfbW9kZSgicmVncmVzc2lvbiIpIHw+IA0KICBwYXJzbmlwOjpzZXRfZW5naW5lKCJrZXJubGFiIikNCg0KbW9kZWxvX2Fydm9yZTMgPC0gZGVjaXNpb25fdHJlZShjb3N0X2NvbXBsZXhpdHkgPSB0dW5lKCksIHRyZWVfZGVwdGggPSB0dW5lKCksDQogICAgICAgICAgICAgICAgICAgICAgICAgbWluX24gPSB0dW5lKCkpIHw+IA0KICBzZXRfZW5naW5lKGVuZ2luZSA9ICJycGFydCIpIHw+IA0KICBzZXRfbW9kZSgicmVncmVzc2lvbiIpDQoNCm1vZGVsb19mbG9yZXN0YTMgPC0gDQogIHJhbmRfZm9yZXN0KG10cnkgPSB0dW5lKCksIG1pbl9uID0gdHVuZSgpLCB0cmVlcyA9IHR1bmUoKSkgfD4gICANCiAgc2V0X2VuZ2luZShlbmdpbmUgPSAicmFuZ2VyIiwgaW1wb3J0YW5jZSA9ICJpbXB1cml0eSIpIHw+ICANCiAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKQ0KDQpgYGANCg0KDQo+DQoNCiMgVmFsaWRhw6fDo28gQ3J1emFkYQ0KDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdmFsaWRhY2FvX2NydXphZGEzIDwtIA0KICB0cmVpbmFtZW50bzMgfD4gDQogIHJzYW1wbGU6OnZmb2xkX2N2KHYgPSA1TCwgc3RyYXRhID0gQ29vbGluZ19Mb2FkKQ0KDQpgYGANCg0KPg0KDQojIFdvcmtmbG93DQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0Kd2ZfdG9kb3MzIDwtDQogIHdvcmtmbG93X3NldCgNCiAgICBwcmVwcm9jID0gbGlzdChyZWNlaXRhXzExMTEsIHJlY2VpdGFfMjIyMiksDQogICAgbW9kZWxzID0gbGlzdCgNCiAgICAgIGtubl9maXQzID0gbW9kZWxvX2tubjMsDQogICAgICBlbGFzdGljX2ZpdDMgPSBtb2RlbG9fZWxhc3RpYzMsDQogICAgICBzdm1fZml0MyA9IG1vZGVsb19zdm0zLA0KICAgICAgYXJ2b3JlX2ZpdDM8LSBtb2RlbG9fYXJ2b3JlMywNCiAgICAgIGZsb3Jlc3RhX2ZpdDM8LSBtb2RlbG9fZmxvcmVzdGEzDQogICAgKSwNCiAgICBjcm9zcyA9IFRSVUUNCiAgKQ0KDQoNCmNvbnRyb2xlX2dyaWQzIDwtIGNvbnRyb2xfZ3JpZCgNCiAgc2F2ZV9wcmVkID0gVFJVRSwNCiAgc2F2ZV93b3JrZmxvdyA9IFRSVUUsDQogIHBhcmFsbGVsX292ZXIgPSAicmVzYW1wbGVzIg0KKQ0KDQoNCmBgYA0KDQo+DQoNCiMgVHJlaW5vDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdHJlaW5vMyA8LQ0KICB3Zl90b2RvczMgfD4gDQogIHdvcmtmbG93X21hcCgNCiAgICByZXNhbXBsZXMgPSB2YWxpZGFjYW9fY3J1emFkYTMsDQogICAgZ3JpZCA9IDIwTCwNCiAgICBjb250cm9sID0gY29udHJvbGVfZ3JpZDMNCiAgKQ0KDQoNCg0KYXV0b3Bsb3QodHJlaW5vMywgbWV0cmljID0gInJtc2UiKSArIA0KICBsYWJzKA0KICAgIHRpdGxlID0gIkF2YWxpYcOnw6NvIGRvcyBtb2RlbG9zIGRlIHJlZ3Jlc3PDo28iLA0KICAgIHN1YnRpdGxlID0gIlV0aWxpemFuZG8gYSBtw6l0cmljYSBkbyBFUU0iDQogICkgKyANCiAgeGxhYigiUmFuayBkb3MgV29ya2Zsb3dzIikgKw0KICB5bGFiKCJFcnJvIFF1YWRyw6F0aWNvIE3DqWRpbyAtIEVRTSIpDQoNCmBgYA0KDQoNCg0KPg0KDQojIE1lbGhvcmVzIA0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCm1lbGhvcmVzMyA8LSANCiAgdHJlaW5vMyB8PiANCiAgcmFua19yZXN1bHRzKHNlbGVjdF9iZXN0ID0gVFJVRSwgcmFua19tZXRyaWMgPSAicm1zZSIpDQptZWxob3JlczMNCg0KYXV0b3Bsb3QodHJlaW5vMywgc2VsZWN0X2Jlc3QgPSBUUlVFKQ0KDQoNCmBgYA0KDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdHJlaW5vMyB8PiANCiAgcmFua19yZXN1bHRzKCkNCg0KbWVsaG9yX21vZGVsbzMgPC0gDQogIHRyZWlubzMgfD4gDQogIGV4dHJhY3Rfd29ya2Zsb3dfc2V0X3Jlc3VsdChpZCA9ICJyZWNpcGVfMl9yYW5kX2ZvcmVzdCIpIHw+IA0KICBzZWxlY3RfYmVzdChtZXRyaWMgPSAicm1zZSIpDQoNCm1lbGhvcl9tb2RlbG8zDQoNCg0KYGBgDQoNCj4NCg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQp3Zl9maW5hbDMgPC0gDQogIHRyZWlubzMgfD4gDQogIGV4dHJhY3Rfd29ya2Zsb3coaWQgPSAicmVjaXBlXzJfcmFuZF9mb3Jlc3QiKSB8PiANCiAgZmluYWxpemVfd29ya2Zsb3cobWVsaG9yX21vZGVsbzIpDQoNCnRlc3RlMyA8LSANCiAgd2ZfZmluYWwzIHw+IA0KICBsYXN0X2ZpdChzcGxpdCA9IGRpdmlzYW9faW5pY2lhbDMpDQoNCnRlc3RlMyQubWV0cmljcw0KDQpgYGANCg0KDQo+DQoNCiMgTW9kZWxvIEZpbmFsDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQptb2RlbG9fZmluYWwzIDwtIA0KICB3Zl9maW5hbDMgfD4gDQogIGZpdChkYWRvczMpDQptb2RlbG9fZmluYWwzDQpgYGANCg0KPg0KDQojIFNhbHZhbmRvIG8gTW9kZWxvDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLGVycm9yPUZBTFNFfQ0KDQpzYXZlUkRTKG1vZGVsb19maW5hbDMsIGZpbGUgPSAibW9kZWxvX2ZpbmFsLnJkczMiKQ0KDQojIExlbmRvIG8gYXJxdWl2byBzZXJpYWxpemFkbyBjb20gbyBtb2RlbG8gZmluYWwNCmxvYWQoZmlsZSA9ICJtb2RlbG9fZmluYWwucmRzMyIpDQoNCiMgRmF6ZW5kbyBub3ZhcyBwcmV2aXPDtWVzDQpwcmVkaWN0KG1vZGVsb19maW5hbDMsIG5ld19kYXRhID0gbm92b3NfZGFkb3MzKQ0KDQpgYGA=