Questão 1
O banco de dados apresentado, trata de restaurante que são ou não
Michelin. O banco contém 6 variáveis, ao todo são apenas 164
observações.
Descritiva dos dados
vis_dat(MichelinNY)

skim(MichelinNY)
── Data Summary ────────────────────────
Values
Name MichelinNY
Number of rows 164
Number of columns 6
_______________________
Column type frequency:
factor 2
numeric 4
________________________
Group variables None
Divisão Inicial e Receita
set.seed(0)
divisao_inicial <- rsample::initial_split(MichelinNY, prop = 0.8, strata ="InMichelin")
treinamento <- rsample::training(divisao_inicial)
teste <- rsample::testing(divisao_inicial)
receita <-
treinamento |> recipe(formula = InMichelin ~ .) |>
recipes::step_dummy(all_nominal(), -all_outcomes()) |>
recipes::step_zv(all_numeric(), -all_outcomes()) |>
recipes::step_corr(all_predictors(), threshold = 0.7, method = "spearman")
Configurando o Modelo
log_reg <-
logistic_reg(penalty = tune(), mixture = tune()) |>
set_engine(engine = "glmnet") |>
set_mode("classification")
Validação Cruzada
validacao_cruzada <-
treinamento |>
rsample::vfold_cv(v = 5L, strata = InMichelin)
Workflow
wf_todos <- workflow_set(
preproc = list(receita),
models = list(modelo_log = log_reg)
) |>
mutate(wflow_id = gsub("(recipe_)", "", wflow_id))
controle_grid <- control_grid(
save_pred = TRUE,
save_workflow = TRUE,
parallel_over = "resamples"
)
metrica <- metric_set(accuracy, roc_auc)
Treinando o Modelo
treino <-
wf_todos |>
workflow_map(
resamples = validacao_cruzada,
grid = 20L,
control = controle_grid
)
Verificando os melhores Hiperparametro
best = treino |>
extract_workflow_set_result("modelo_log") |>
select_best(metric = "roc_auc")
best |>
knitr::kable()
| 0.0185454 |
0.997221 |
Preprocessor1_Model20 |
test <- treino |>
extract_workflow("modelo_log") |>
finalize_workflow(best) |>
last_fit(split = divisao_inicial,
metrics = metric_set(accuracy, roc_auc))
Testando em uma matriz de confusão
predictions <- test |>
collect_predictions()
matriz_confusão <- predictions |>
conf_mat(InMichelin, .pred_class)
round(prop.table(matriz_confusão$table),2) |>
knitr::kable()
| naoMichelin |
0.48 |
0.12 |
| Michelin |
0.06 |
0.33 |
NA
NA
melhores <-
treino |>
rank_results(select_best = TRUE, rank_metric = c( "roc_auc"))
stargazer::stargazer(melhores,summary = F,type = "text")
=================================================================================================================
wflow_id .config .metric mean std_err n preprocessor model rank
-----------------------------------------------------------------------------------------------------------------
1 modelo_log Preprocessor1_Model20 accuracy 0.777356125356125 0.0322521598740875 5 recipe logistic_reg 1
2 modelo_log Preprocessor1_Model20 roc_auc 0.858391053391053 0.0248715845505386 5 recipe logistic_reg 1
-----------------------------------------------------------------------------------------------------------------
curva_roc <- roc_curve(predictions,InMichelin, .pred_naoMichelin)
roc_auc(predictions, InMichelin, .pred_naoMichelin)
autoplot(curva_roc)

O modelo apresentado tem desempenho superior a qualquer modelo feito
de forma aleatório, mostrando com isso, a eficiência dos modelos de
machine learning.
Salvando o Modelo
#Salvando o modelo em um arquivo serializado
#saveRDS(test, file = "modelo_final_questao1.rds")
# Lendo o arquivo serializado com o modelo final
#load(file = "modelo_final_questao1.rds")
# Fazendo novas previsões
#predict(modelo_final_questao1, new_data = data_novo)
Questão 2
A-Explique resumidamente o que é aprendizagem supervisionada e
não-supervisionada. Cite um problema de aprendizagem supervisionada e um
outro de aprendizagem não-supervisionada.
Supervisionada utiliza conjuntos de dados rotulados, sem a
necessidade de uma pessoa externa para compreender o assunto.
Não supervisionada é quando não temos a resposta correta do conteudo,
ou seja, temos a necessidade de pessoas externas para entendimento do
assunto.
B- 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.
Considere a variável aleatória \(Y_i \sim N
(sin (X_i),\sigma^2)\) , com \(X_i\) \(\epsilon\) \(U
(0,10)\) , \(\forall_i\) .
Utilizando a biblioteca \(rpart\),
implemente a função arvore(n=250L,complexidade,sigma =0.1) que devolva o
gráfico scatterplot com os pontos \(X_i\) e \(Y_i\) e por cima deles o gráfico de linha
com os valores preditos. A função deverá ter três argumentos ,
n,complexidade e sigma, que são tamanho da amostra, o grau de
complexidade do modelo, e o desvio padrão, respectivamente. Aqui não se
preocupe com divisão entre treino e teste nem de validação cruzada. A
solução desse exercício não tem como objetivo encontrar o melhor
hiperparâmetro , \(i.e\), não é
necessário tunar o parâmetro de complexidade.
library(rpart)
library(ggplot2)
library(tidymodels)
library(tibble)
library(dplyr)
library(workflows)
tidymodels::tidymodels_prefer()
arvore <- function(n = 250, complexidade, sigma = 0.1) {
X <- runif(n, 0, 10)
Y <- sin(X) + rnorm(n, mean = 0, sd = sigma)
df <- data.frame(X = X, Y = Y)
modelo_arvore <- rpart(Y ~ X, data = df, control = rpart.control(cp = complexidade))
plot(X, Y, main = "Gráfico de Dispersão e Árvore de Regressão", xlab = "X", ylab = "Y", pch = 19)
curve(predict(modelo_arvore, data.frame(X = x)), from = 0, to = 10, add = TRUE, col = "red")
}
arvore(n = 250, complexidade = 0.01, sigma = 0.1)

NA
NA
Sem utilizar a biblioteca tidymodels, apenas as bibliotecas rsample e
rpart, treine um modelo com 10 mil observações geradas. No procedimento
\(k-folds\) \(cross-validation\), para \(k=20\) , encontre um bom valor para o grau
de complexidade considerando um grid de possíveis valores. Dica:
experimente testar um parâmetro dentro do conjunto de treino no
procedimento de cross-validation. Por exemplo, experimente criar um grid
com valores entre \(0.001\) \(0.001\) e \(0.4\) 0.4. Aprensente o gráfico com a
estimativa do melhor modelo. Não esqueça de fixar um valor de semente,
para que os resultados possam ser reproduzidos.
estimar_e_plotar_arvores <- function(n = 250, num_arvores = 10, complexidade, sigma = 0.1) {
resultados <- data.frame(X = rep(0, n), Y_pred = rep(0, n))
for (i in 1:num_arvores) {
X <- runif(n, 0, 10)
Y <- sin(X) + rnorm(n, mean = 0, sd = sigma)
df <- data.frame(X = X, Y = Y)
modelo_arvore <- rpart(Y ~ X, data = df, control = rpart.control(cp = complexidade))
X_pred <- seq(0, 10, length.out = n)
Y_pred <- predict(modelo_arvore, data.frame(X = X_pred))
resultados <- data.frame(X = X_pred, Y_pred = resultados$Y_pred + Y_pred / num_arvores)
}
ggplot(resultados, aes(x = X, y = Y_pred)) +
geom_line(color = "blue") +
ggtitle("Estimativas de Múltiplas Árvores de Regressão") +
xlab("X") + ylab("Y Predito")
}
estimar_e_plotar_arvores(n = 250, num_arvores = 10, complexidade = 0.01, sigma = 0.1)

NA
NA
NA
Questão 3
Construa um modelo para previsão do valor do imóvel. Considere apenas
casas e apartamentos.
imoveissalvador<- read.csv("C:/Users/elton/Downloads/salvador djl.csv", header=FALSE)
imoveissalvador<- imoveissalvador |> select(c(-V1,-V2,-V11,-V26,-V27,-V10))
imoveissalvador<-
imoveissalvador |> rename(c(valor=V3,area=V4,
iptu=V5,condominio=V6,quarto=V7,
vaga=V8,banheiro=V9,
tipo=V12,area_servico=V13,espaco_gourmet=V14,piscina=V15,elevador=V16,salao_de_festa=V17,academia=V18,playground=V19,quadra_esporte=V20,portaria_24h=V21,varanda_gourmet=V22,sauna=V23,spa=V24,comercio=V25,))
imoveissalvador<- imoveissalvador[-1,]
imoveissalvador <- imoveissalvador |>
filter(tipo == "casas" | tipo == "apartamentos") |> select(-comercio)
Descritiva dos dados
dados <- dados_expectativa_renda[,-1]
Error: object 'dados_expectativa_renda' not found
skim(imoveissalvador)
── Data Summary ────────────────────────
Values
Name imoveissalvador
Number of rows 16944
Number of columns 22
_______________________
Column type frequency:
character 22
________________________
Group variables None
Divisão Inicial e Receita
set.seed(0)
divisao_inicial1 <- rsample::initial_split(imoveissalvador, prop = 0.8, strata ="valor")
treinamento1 <- rsample::training(divisao_inicial1)
teste1 <- rsample::testing(divisao_inicial1)
receita1 <-
treinamento1 |>
recipe(formula = valor ~ . , imoveissalvador) |>
step_impute_knn(all_predictors(),
impute_with = c("iptu","condominio","vaga","quarto","banheiro"),
neighbors = 15L) |>
step_bin2factor(all_logical_predictors()) |>
step_dummy(all_nominal_predictors()) |>
step_YeoJohnson(all_numeric_predictors()) |>
step_normalize(all_numeric_predictors()) |>
step_zv(all_numeric_predictors()) |>
step_corr(all_numeric_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()) |>
set_engine(engine = "ranger", importance = "impurity") |>
set_mode("regression")
modelo_bootstrep <- xgb <- boost_tree(tree_depth = tune(), learn_rate = tune(),
loss_reduction = tune(), min_n = tune(),
sample_size = tune(), trees = tune()) |>
set_engine(engine = "xgboost") |>
set_mode("regression")
Validação Cruzada
validacao_cruzada1 <-
treinamento1 |>
rsample::vfold_cv(v = 5L, strata = valor)
Worflow
wf_todos1 <-
workflow_set(
preproc = list(receita1),
models = list(
knn_fit = modelo_knn,
elastic_fit = modelo_elastic,
svm_fit = modelo_svm,
arvore_fit<- modelo_arvore,
floresta_fit<- modelo_floresta,
boot_fit<- modelo_bootstrep
),
cross = TRUE
)
controle_grid1 <- control_grid(
save_pred = TRUE,
save_workflow = TRUE,
parallel_over = "resamples"
)
Treinando o Modelo
treino1 <-
wf_todos1 |>
workflow_map(
resamples = validacao_cruzada1,
grid = 20L,
control = controle_grid1
)
Melhores
melhores3 <-
treino3 |>
rank_results(select_best = TRUE, rank_metric = "rmse")
autoplot(treino1, select_best = TRUE)
treino3 |>
rank_results()
melhor_modelo <-
treino1 |>
extract_workflow_set_result(id = "recipe_1_rand_forest") |>
select_best(metric = "rmse")
wf_final3 <-
treino1 |>
extract_workflow(id = "recipe_1_rand_forest") |>
finalize_workflow(melhor_modelo)
teste2 <-
wf_final3 |>
last_fit(split = divisao_inicial1)
teste2$.metrics
Modelo final
modelo_final <-
wf_final3 |>
fit(imoveissalvador)
modelo_final
LS0tDQp0aXRsZTogIkFwcmVuZGl6YWdlbSBkYXMgTcOhcXVpbmFzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBRdWVzdMOjbyAxDQoNCj4NCg0KTyBiYW5jbyBkZSBkYWRvcyBhcHJlc2VudGFkbywgdHJhdGEgZGUgcmVzdGF1cmFudGUgcXVlIHPDo28gb3UgbsOjbyBNaWNoZWxpbi4gTyBiYW5jbyBjb250w6ltIDYgdmFyacOhdmVpcywgYW8gdG9kbyBzw6NvIGFwZW5hcyAxNjQgb2JzZXJ2YcOnw7Vlcy4NCg0KPg0KDQojIyBUcmFuc2Zvcm1hbmRvIGFzIHZhcmnDoXZlaXMNCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpZHltb2RlbHMpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh2aXNkYXQpDQpsaWJyYXJ5KHNraW1yKQ0KbGlicmFyeShyZWNpcGVzKQ0KbGlicmFyeShwUk9DKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShzdGFyZ2F6ZXIpDQpsaWJyYXJ5KHlhcmRzdGljaykNCmBgYA0KDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KTWljaGVsaW5OWSA8LSByZWFkLmNzdigiQzovVXNlcnMvZWx0b24vRG93bmxvYWRzL01pY2hlbGluTlkudHh0IikNCnRpZHltb2RlbHM6OnRpZHltb2RlbHNfcHJlZmVyKCkNCmBgYA0KDQo+DQoNCiMjIFRyYW5zZm9ybWFuZG8gYXMgdmFyacOhdmVpcw0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCk1pY2hlbGluTlk8LSBNaWNoZWxpbk5ZIHw+DQogIHJlbmFtZShjKGVuZGVyZcOnbz1BZHJlc3MsY29taWRhPUZvb2QsZGVjb3Jhw6dhbz1EZWNvciwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXJ2acOnbz1TZXJ2aWNlLHByZcOnbz1QcmljZSkpDQoNCmBgYA0KDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KTWljaGVsaW5OWSRlbmRlcmXDp288LSBhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKE1pY2hlbGluTlkkZW5kZXJlw6dvKSkNCg0KTWljaGVsaW5OWSRjb21pZGE8LSBhcy5udW1lcmljKGFzLmludGVnZXIoTWljaGVsaW5OWSRjb21pZGEpKQ0KTWljaGVsaW5OWSRkZWNvcmHDp2FvPC0gYXMubnVtZXJpYyhhcy5pbnRlZ2VyKE1pY2hlbGluTlkkZGVjb3Jhw6dhbykpDQpNaWNoZWxpbk5ZJHNlcnZpw6dvPC0gYXMubnVtZXJpYyhhcy5pbnRlZ2VyKE1pY2hlbGluTlkkc2VydmnDp28pKQ0KTWljaGVsaW5OWSRwcmXDp288LSBhcy5udW1lcmljKGFzLmludGVnZXIoTWljaGVsaW5OWSRwcmXDp28pKQ0KTWljaGVsaW5OWSRJbk1pY2hlbGluIDwtIGZhY3RvcihNaWNoZWxpbk5ZJEluTWljaGVsaW4sIGxldmVscyA9IGMoMCwxKSxsYWJlbHMgPSBjKCJuYW9NaWNoZWxpbiIsICJNaWNoZWxpbiIpKQ0KDQpgYGANCg0KPg0KDQojIyBEZXNjcml0aXZhIGRvcyBkYWRvcw0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnZpc19kYXQoTWljaGVsaW5OWSkNCnNraW0oTWljaGVsaW5OWSkNCg0KYGBgDQoNCj4NCg0KIyMgRGl2aXPDo28gSW5pY2lhbCBlIFJlY2VpdGENCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpzZXQuc2VlZCgwKSANCmRpdmlzYW9faW5pY2lhbCA8LSByc2FtcGxlOjppbml0aWFsX3NwbGl0KE1pY2hlbGluTlksIHByb3AgPSAwLjgsIHN0cmF0YSA9IkluTWljaGVsaW4iKQ0KdHJlaW5hbWVudG8gPC0gcnNhbXBsZTo6dHJhaW5pbmcoZGl2aXNhb19pbmljaWFsKQ0KdGVzdGUgPC0gcnNhbXBsZTo6dGVzdGluZyhkaXZpc2FvX2luaWNpYWwpIA0KDQoNCmBgYA0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnJlY2VpdGEgPC0gDQogIHRyZWluYW1lbnRvIHw+IHJlY2lwZShmb3JtdWxhID0gSW5NaWNoZWxpbiB+IC4pIHw+IA0KICAgIA0KICByZWNpcGVzOjpzdGVwX2R1bW15KGFsbF9ub21pbmFsKCksIC1hbGxfb3V0Y29tZXMoKSkgfD4gIA0KICByZWNpcGVzOjpzdGVwX3p2KGFsbF9udW1lcmljKCksIC1hbGxfb3V0Y29tZXMoKSkgfD4gDQogIHJlY2lwZXM6OnN0ZXBfY29ycihhbGxfcHJlZGljdG9ycygpLCB0aHJlc2hvbGQgPSAwLjcsIG1ldGhvZCA9ICJzcGVhcm1hbiIpDQoNCmBgYA0KDQo+DQoNCiMjIENvbmZpZ3VyYW5kbyBvIE1vZGVsbw0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmxvZ19yZWcgPC0NCiAgbG9naXN0aWNfcmVnKHBlbmFsdHkgPSB0dW5lKCksIG1peHR1cmUgPSB0dW5lKCkpIHw+ICANCiAgc2V0X2VuZ2luZShlbmdpbmUgPSAiZ2xtbmV0IikgfD4gDQogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpDQoNCmBgYA0KDQo+DQoNCg0KIyMgVmFsaWRhw6fDo28gQ3J1emFkYQ0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCg0KdmFsaWRhY2FvX2NydXphZGEgPC0gDQogIHRyZWluYW1lbnRvIHw+IA0KICByc2FtcGxlOjp2Zm9sZF9jdih2ID0gNUwsIHN0cmF0YSA9IEluTWljaGVsaW4pDQoNCmBgYA0KDQo+DQoNCiMjIFdvcmtmbG93DQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0Kd2ZfdG9kb3MgPC0gd29ya2Zsb3dfc2V0KA0KICBwcmVwcm9jID0gbGlzdChyZWNlaXRhKSwNCiAgbW9kZWxzID0gbGlzdChtb2RlbG9fbG9nID0gbG9nX3JlZykNCikgfD4gDQogIG11dGF0ZSh3Zmxvd19pZCA9IGdzdWIoIihyZWNpcGVfKSIsICIiLCB3Zmxvd19pZCkpDQpjb250cm9sZV9ncmlkIDwtIGNvbnRyb2xfZ3JpZCgNCiAgc2F2ZV9wcmVkID0gVFJVRSwNCiAgc2F2ZV93b3JrZmxvdyA9IFRSVUUsDQogIHBhcmFsbGVsX292ZXIgPSAicmVzYW1wbGVzIg0KKQ0KbWV0cmljYSA8LSBtZXRyaWNfc2V0KGFjY3VyYWN5LCByb2NfYXVjKQ0KDQoNCmBgYA0KDQo+DQoNCiMjIFRyZWluYW5kbyBvIE1vZGVsbw0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnRyZWlubyA8LQ0KICB3Zl90b2RvcyB8PiANCiAgd29ya2Zsb3dfbWFwKA0KICAgIHJlc2FtcGxlcyA9IHZhbGlkYWNhb19jcnV6YWRhLA0KICAgIGdyaWQgPSAyMEwsDQogICAgY29udHJvbCA9IGNvbnRyb2xlX2dyaWQNCiAgKQ0KDQoNCmBgYA0KDQo+DQoNCiMjIFZlcmlmaWNhbmRvIG9zIG1lbGhvcmVzIEhpcGVycGFyYW1ldHJvDQoNCj4NCiANCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmJlc3QgPSB0cmVpbm8gfD4gDQogIGV4dHJhY3Rfd29ya2Zsb3dfc2V0X3Jlc3VsdCgibW9kZWxvX2xvZyIpIHw+ICANCiAgc2VsZWN0X2Jlc3QobWV0cmljID0gInJvY19hdWMiKQ0KDQpiZXN0IHw+IA0KICBrbml0cjo6a2FibGUoKQ0KDQp0ZXN0IDwtICB0cmVpbm8gfD4gDQogIGV4dHJhY3Rfd29ya2Zsb3coIm1vZGVsb19sb2ciKSB8PiAgDQogIGZpbmFsaXplX3dvcmtmbG93KGJlc3QpIHw+IA0KICBsYXN0X2ZpdChzcGxpdCA9IGRpdmlzYW9faW5pY2lhbCwNCiAgICAgICAgICAgbWV0cmljcyA9IG1ldHJpY19zZXQoYWNjdXJhY3ksIHJvY19hdWMpKQ0KDQpgYGANCg0KPg0KDQojIyBUZXN0YW5kbyBlbSB1bWEgbWF0cml6IGRlIGNvbmZ1c8Ojbw0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnByZWRpY3Rpb25zIDwtIHRlc3QgfD4gDQogIGNvbGxlY3RfcHJlZGljdGlvbnMoKQ0KDQptYXRyaXpfY29uZnVzw6NvIDwtIHByZWRpY3Rpb25zIHw+IA0KICBjb25mX21hdChJbk1pY2hlbGluLCAucHJlZF9jbGFzcykNCg0Kcm91bmQocHJvcC50YWJsZShtYXRyaXpfY29uZnVzw6NvJHRhYmxlKSwyKSB8PiANCiAga25pdHI6OmthYmxlKCkNCg0KDQpgYGANCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQptZWxob3JlcyA8LSANCiAgdHJlaW5vIHw+IA0KICByYW5rX3Jlc3VsdHMoc2VsZWN0X2Jlc3QgPSBUUlVFLCByYW5rX21ldHJpYyA9IGMoICJyb2NfYXVjIikpIA0Kc3RhcmdhemVyOjpzdGFyZ2F6ZXIobWVsaG9yZXMsc3VtbWFyeSA9IEYsdHlwZSA9ICJ0ZXh0IikNCg0KYGBgDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQpjdXJ2YV9yb2MgPC0gcm9jX2N1cnZlKHByZWRpY3Rpb25zLEluTWljaGVsaW4sIC5wcmVkX25hb01pY2hlbGluKSANCnJvY19hdWMocHJlZGljdGlvbnMsIEluTWljaGVsaW4sIC5wcmVkX25hb01pY2hlbGluKQ0KDQphdXRvcGxvdChjdXJ2YV9yb2MpDQoNCmBgYA0KDQo+DQoNCg0KTyBtb2RlbG8gYXByZXNlbnRhZG8gdGVtIGRlc2VtcGVuaG8gc3VwZXJpb3IgYSBxdWFscXVlciBtb2RlbG8gZmVpdG8gZGUgZm9ybWEgYWxlYXTDs3JpbywgbW9zdHJhbmRvIGNvbSBpc3NvLCBhIGVmaWNpw6puY2lhIGRvcyBtb2RlbG9zIGRlIG1hY2hpbmUgbGVhcm5pbmcuDQoNCg0KPg0KDQoNCiMjIFNhbHZhbmRvIG8gTW9kZWxvDQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCg0KI1NhbHZhbmRvIG8gbW9kZWxvIGVtIHVtIGFycXVpdm8gc2VyaWFsaXphZG8NCg0KI3NhdmVSRFModGVzdCwgZmlsZSA9ICJtb2RlbG9fZmluYWxfcXVlc3RhbzEucmRzIikNCg0KIyBMZW5kbyBvIGFycXVpdm8gc2VyaWFsaXphZG8gY29tIG8gbW9kZWxvIGZpbmFsDQojbG9hZChmaWxlID0gIm1vZGVsb19maW5hbF9xdWVzdGFvMS5yZHMiKQ0KDQojIEZhemVuZG8gbm92YXMgcHJldmlzw7Vlcw0KDQojcHJlZGljdChtb2RlbG9fZmluYWxfcXVlc3RhbzEsIG5ld19kYXRhID0gZGF0YV9ub3ZvKQ0KYGBgDQoNCg0KPg0KDQojIFF1ZXN0w6NvIDINCg0KPg0KDQpBLUV4cGxpcXVlIHJlc3VtaWRhbWVudGUgbyBxdWUgw6kgYXByZW5kaXphZ2VtIHN1cGVydmlzaW9uYWRhIGUgbsOjby1zdXBlcnZpc2lvbmFkYS4gQ2l0ZSB1bSBwcm9ibGVtYSBkZSBhcHJlbmRpemFnZW0gc3VwZXJ2aXNpb25hZGEgZSB1bSBvdXRybyBkZSBhcHJlbmRpemFnZW0gbsOjby1zdXBlcnZpc2lvbmFkYS4NCg0KDQpTdXBlcnZpc2lvbmFkYSB1dGlsaXphIGNvbmp1bnRvcyBkZSBkYWRvcyByb3R1bGFkb3MsIHNlbSBhIG5lY2Vzc2lkYWRlIGRlIHVtYSBwZXNzb2EgZXh0ZXJuYSBwYXJhIGNvbXByZWVuZGVyIG8gYXNzdW50by4NCg0KDQpOw6NvIHN1cGVydmlzaW9uYWRhIMOpIHF1YW5kbyBuw6NvIHRlbW9zIGEgcmVzcG9zdGEgY29ycmV0YSBkbyBjb250ZXVkbywgb3Ugc2VqYSwgdGVtb3MgYSBuZWNlc3NpZGFkZSBkZSBwZXNzb2FzIGV4dGVybmFzIHBhcmEgZW50ZW5kaW1lbnRvIGRvIGFzc3VudG8uIA0KDQoNCj4NCg0KQi0gRXhwbGlxdWUgcXVhbCBvIG1vdGl2byBxdWUgZmF6IGNvbSBxdWUgbyBFcnJvIFF1YWRyw6F0aWNvIE3DqWRpbyAtIEVRTSBwYXJhIGF2YWxpYXIgbyBkZXNlbXBlbmhvIGRlIHVtIG1vZGVsbyDDqSBydWltIHF1YW5kbyBuw6NvIGFkb3RhbW9zIG5lbmh1bWEgZXN0cmF0w6lnaWEgZGUgZGl2aXPDo28gZG8gY29uanVudG8gZGUgZGFkb3MgZW0gdHJlaW5hbWVudG8gZSB0ZXN0ZS4NCg0KPg0KDQpDb25zaWRlcmUgYSB2YXJpw6F2ZWwgYWxlYXTDs3JpYSAkWV9pIFxzaW0gTiAoc2luIChYX2kpLFxzaWdtYV4yKSQgLCBjb20gJFhfaSQgJFxlcHNpbG9uJCAkVSAoMCwxMCkkICwgJFxmb3JhbGxfaSQgLiBVdGlsaXphbmRvIGEgYmlibGlvdGVjYSAkcnBhcnQkLCBpbXBsZW1lbnRlIGEgZnVuw6fDo28gYXJ2b3JlKG49MjUwTCxjb21wbGV4aWRhZGUsc2lnbWEgPTAuMSkgcXVlIGRldm9sdmEgbyBncsOhZmljbyBzY2F0dGVycGxvdCBjb20gb3MgcG9udG9zICRYX2kkIGUgJFlfaSQgZSBwb3IgY2ltYSBkZWxlcyBvIGdyw6FmaWNvIGRlIGxpbmhhIGNvbSBvcyB2YWxvcmVzIHByZWRpdG9zLiBBIGZ1bsOnw6NvIGRldmVyw6EgdGVyIHRyw6pzIGFyZ3VtZW50b3MgLCBuLGNvbXBsZXhpZGFkZSBlIHNpZ21hLCBxdWUgc8OjbyB0YW1hbmhvIGRhIGFtb3N0cmEsIG8gZ3JhdSBkZSBjb21wbGV4aWRhZGUgZG8gbW9kZWxvLCBlIG8gZGVzdmlvIHBhZHLDo28sIHJlc3BlY3RpdmFtZW50ZS4gQXF1aSBuw6NvIHNlIHByZW9jdXBlIGNvbSBkaXZpc8OjbyBlbnRyZSB0cmVpbm8gZSB0ZXN0ZSBuZW0gZGUgdmFsaWRhw6fDo28gY3J1emFkYS4gQSBzb2x1w6fDo28gZGVzc2UgZXhlcmPDrWNpbyBuw6NvIHRlbSBjb21vIG9iamV0aXZvIGVuY29udHJhciBvIG1lbGhvciBoaXBlcnBhcsOibWV0cm8gLCAkaS5lJCwgbsOjbyDDqSBuZWNlc3PDoXJpbyB0dW5hciBvIHBhcsOibWV0cm8gZGUgY29tcGxleGlkYWRlLg0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkocnBhcnQpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRpZHltb2RlbHMpDQpsaWJyYXJ5KHRpYmJsZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHdvcmtmbG93cykNCnRpZHltb2RlbHM6OnRpZHltb2RlbHNfcHJlZmVyKCkNCmBgYA0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCg0KYXJ2b3JlIDwtIGZ1bmN0aW9uKG4gPSAyNTAsIGNvbXBsZXhpZGFkZSwgc2lnbWEgPSAwLjEpIHsNClggPC0gcnVuaWYobiwgMCwgMTApDQogIA0KWSA8LSBzaW4oWCkgKyBybm9ybShuLCBtZWFuID0gMCwgc2QgPSBzaWdtYSkNCiAgDQpkZiA8LSBkYXRhLmZyYW1lKFggPSBYLCBZID0gWSkNCiAgDQptb2RlbG9fYXJ2b3JlIDwtIHJwYXJ0KFkgfiBYLCBkYXRhID0gZGYsIGNvbnRyb2wgPSBycGFydC5jb250cm9sKGNwID0gY29tcGxleGlkYWRlKSkNCiAgDQpwbG90KFgsIFksIG1haW4gPSAiR3LDoWZpY28gZGUgRGlzcGVyc8OjbyBlIMOBcnZvcmUgZGUgUmVncmVzc8OjbyIsIHhsYWIgPSAiWCIsIHlsYWIgPSAiWSIsIHBjaCA9IDE5KQ0KICANCmN1cnZlKHByZWRpY3QobW9kZWxvX2Fydm9yZSwgZGF0YS5mcmFtZShYID0geCkpLCBmcm9tID0gMCwgdG8gPSAxMCwgYWRkID0gVFJVRSwgY29sID0gInJlZCIpDQp9DQphcnZvcmUobiA9IDI1MCwgY29tcGxleGlkYWRlID0gMC4wMSwgc2lnbWEgPSAwLjEpDQoNCg0KYGBgDQoNCg0KPg0KDQoNClNlbSB1dGlsaXphciBhIGJpYmxpb3RlY2EgdGlkeW1vZGVscywgYXBlbmFzIGFzIGJpYmxpb3RlY2FzIHJzYW1wbGUgZSBycGFydCwgdHJlaW5lIHVtIG1vZGVsbyBjb20gMTAgbWlsIG9ic2VydmHDp8O1ZXMgZ2VyYWRhcy4gTm8gcHJvY2VkaW1lbnRvICRrLWZvbGRzJCAkY3Jvc3MtdmFsaWRhdGlvbiQsIHBhcmEgDQokaz0yMCQgLCBlbmNvbnRyZSB1bSBib20gdmFsb3IgcGFyYSBvIGdyYXUgZGUgY29tcGxleGlkYWRlIGNvbnNpZGVyYW5kbyB1bSBncmlkIGRlIHBvc3PDrXZlaXMgdmFsb3Jlcy4gRGljYTogZXhwZXJpbWVudGUgdGVzdGFyIHVtIHBhcsOibWV0cm8gZGVudHJvIGRvIGNvbmp1bnRvIGRlIHRyZWlubyBubyBwcm9jZWRpbWVudG8gZGUgY3Jvc3MtdmFsaWRhdGlvbi4gUG9yIGV4ZW1wbG8sIGV4cGVyaW1lbnRlIGNyaWFyIHVtIGdyaWQgY29tIHZhbG9yZXMgZW50cmUgDQokMC4wMDEkICQwLjAwMSQgZSAkMC40JCAwLjQuIEFwcmVuc2VudGUgbyBncsOhZmljbyBjb20gYSBlc3RpbWF0aXZhIGRvIG1lbGhvciBtb2RlbG8uIE7Do28gZXNxdWXDp2EgZGUgZml4YXIgdW0gdmFsb3IgZGUgc2VtZW50ZSwgcGFyYSBxdWUgb3MgcmVzdWx0YWRvcyBwb3NzYW0gc2VyIHJlcHJvZHV6aWRvcy4gDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQplc3RpbWFyX2VfcGxvdGFyX2Fydm9yZXMgPC0gZnVuY3Rpb24obiA9IDI1MCwgbnVtX2Fydm9yZXMgPSAxMCwgY29tcGxleGlkYWRlLCBzaWdtYSA9IDAuMSkgew0KcmVzdWx0YWRvcyA8LSBkYXRhLmZyYW1lKFggPSByZXAoMCwgbiksIFlfcHJlZCA9IHJlcCgwLCBuKSkNCiAgDQpmb3IgKGkgaW4gMTpudW1fYXJ2b3Jlcykgew0KICAgDQpYIDwtIHJ1bmlmKG4sIDAsIDEwKQ0KICAgIA0KICAgIA0KWSA8LSBzaW4oWCkgKyBybm9ybShuLCBtZWFuID0gMCwgc2QgPSBzaWdtYSkNCiAgICANCiAgIA0KZGYgPC0gZGF0YS5mcmFtZShYID0gWCwgWSA9IFkpDQogICAgDQogICAgDQptb2RlbG9fYXJ2b3JlIDwtIHJwYXJ0KFkgfiBYLCBkYXRhID0gZGYsIGNvbnRyb2wgPSBycGFydC5jb250cm9sKGNwID0gY29tcGxleGlkYWRlKSkNCiAgICANCiAgICANClhfcHJlZCA8LSBzZXEoMCwgMTAsIGxlbmd0aC5vdXQgPSBuKQ0KICAgIA0KICAgIA0KWV9wcmVkIDwtIHByZWRpY3QobW9kZWxvX2Fydm9yZSwgZGF0YS5mcmFtZShYID0gWF9wcmVkKSkNCiAgICANCiAgIA0KcmVzdWx0YWRvcyA8LSBkYXRhLmZyYW1lKFggPSBYX3ByZWQsIFlfcHJlZCA9IHJlc3VsdGFkb3MkWV9wcmVkICsgWV9wcmVkIC8gbnVtX2Fydm9yZXMpDQogIH0NCiAgDQogDQpnZ3Bsb3QocmVzdWx0YWRvcywgYWVzKHggPSBYLCB5ID0gWV9wcmVkKSkgKw0KICAgIGdlb21fbGluZShjb2xvciA9ICJibHVlIikgKw0KICAgIGdndGl0bGUoIkVzdGltYXRpdmFzIGRlIE3Dumx0aXBsYXMgw4Fydm9yZXMgZGUgUmVncmVzc8OjbyIpICsNCiAgICB4bGFiKCJYIikgKyB5bGFiKCJZIFByZWRpdG8iKQ0KfQ0KDQoNCmVzdGltYXJfZV9wbG90YXJfYXJ2b3JlcyhuID0gMjUwLCBudW1fYXJ2b3JlcyA9IDEwLCBjb21wbGV4aWRhZGUgPSAwLjAxLCBzaWdtYSA9IDAuMSkNCg0KDQoNCmBgYA0KDQoNCg0KPg0KDQojIFF1ZXN0w6NvIDMNCg0KPg0KDQpDb25zdHJ1YSB1bSBtb2RlbG8gcGFyYSBwcmV2aXPDo28gZG8gdmFsb3IgZG8gaW3Ds3ZlbC4gQ29uc2lkZXJlIGFwZW5hcyBjYXNhcyBlIGFwYXJ0YW1lbnRvcy4NCg0KPg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQppbW92ZWlzc2FsdmFkb3I8LSByZWFkLmNzdigiQzovVXNlcnMvZWx0b24vRG93bmxvYWRzL3NhbHZhZG9yIGRqbC5jc3YiLCBoZWFkZXI9RkFMU0UpDQoNCmBgYA0KDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KaW1vdmVpc3NhbHZhZG9yPC0gaW1vdmVpc3NhbHZhZG9yIHw+IHNlbGVjdChjKC1WMSwtVjIsLVYxMSwtVjI2LC1WMjcsLVYxMCkpDQoNCmBgYA0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmltb3ZlaXNzYWx2YWRvcjwtIA0KICBpbW92ZWlzc2FsdmFkb3IgfD4gcmVuYW1lKGModmFsb3I9VjMsYXJlYT1WNCwNCiAgICAgICAgICAgICAgICAgICAgaXB0dT1WNSxjb25kb21pbmlvPVY2LHF1YXJ0bz1WNywNCiAgICAgICAgICAgICAgICAgICAgdmFnYT1WOCxiYW5oZWlybz1WOSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpcG89VjEyLGFyZWFfc2Vydmljbz1WMTMsZXNwYWNvX2dvdXJtZXQ9VjE0LHBpc2NpbmE9VjE1LGVsZXZhZG9yPVYxNixzYWxhb19kZV9mZXN0YT1WMTcsYWNhZGVtaWE9VjE4LHBsYXlncm91bmQ9VjE5LHF1YWRyYV9lc3BvcnRlPVYyMCxwb3J0YXJpYV8yNGg9VjIxLHZhcmFuZGFfZ291cm1ldD1WMjIsc2F1bmE9VjIzLHNwYT1WMjQsY29tZXJjaW89VjI1LCkpDQppbW92ZWlzc2FsdmFkb3I8LSBpbW92ZWlzc2FsdmFkb3JbLTEsXQ0KaW1vdmVpc3NhbHZhZG9yIDwtIGltb3ZlaXNzYWx2YWRvciB8PiAgDQogIGZpbHRlcih0aXBvID09ICJjYXNhcyIgfCB0aXBvID09ICJhcGFydGFtZW50b3MiKSB8PiBzZWxlY3QoLWNvbWVyY2lvKQ0KDQpgYGANCg0KDQo+DQoNCiMjIERlc2NyaXRpdmEgZG9zIGRhZG9zDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdmlzX2RhdChpbW92ZWlzc2FsdmFkb3IsbGFyZ2VfZGF0YV9zaXplID0gMTAwZSsxMDApDQp2aXNfbWlzcyhpbW92ZWlzc2FsdmFkb3IsIHNvcnRfbWlzcyA9IFRSVUUsbGFyZ2VfZGF0YV9zaXplID0gMTAwZSsxMDApDQoNCmBgYA0KDQo+DQoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0Kc2tpbShpbW92ZWlzc2FsdmFkb3IpDQoNCmBgYA0KDQo+DQoNCiMjIERpdmlzw6NvIEluaWNpYWwgZSBSZWNlaXRhDQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KDQpzZXQuc2VlZCgwKSANCmRpdmlzYW9faW5pY2lhbDEgPC0gcnNhbXBsZTo6aW5pdGlhbF9zcGxpdChpbW92ZWlzc2FsdmFkb3IsIHByb3AgPSAwLjgsIHN0cmF0YSA9InZhbG9yIikNCnRyZWluYW1lbnRvMSA8LSByc2FtcGxlOjp0cmFpbmluZyhkaXZpc2FvX2luaWNpYWwxKSANCnRlc3RlMSA8LSByc2FtcGxlOjp0ZXN0aW5nKGRpdmlzYW9faW5pY2lhbDEpDQoNCnJlY2VpdGExIDwtIA0KICANCiAgdHJlaW5hbWVudG8xIHw+IA0KICByZWNpcGUoZm9ybXVsYSA9IHZhbG9yIH4gLiAsIGltb3ZlaXNzYWx2YWRvcikgfD4gDQogIHN0ZXBfaW1wdXRlX2tubihhbGxfcHJlZGljdG9ycygpLA0KICAgICAgICAgICAgICAgICAgaW1wdXRlX3dpdGggPSBjKCJpcHR1IiwiY29uZG9taW5pbyIsInZhZ2EiLCJxdWFydG8iLCJiYW5oZWlybyIpLA0KICAgICAgICAgICAgICAgICAgbmVpZ2hib3JzID0gMTVMKSB8PiANCiAgc3RlcF9iaW4yZmFjdG9yKGFsbF9sb2dpY2FsX3ByZWRpY3RvcnMoKSkgfD4gDQogIHN0ZXBfZHVtbXkoYWxsX25vbWluYWxfcHJlZGljdG9ycygpKSB8PiANCiAgc3RlcF9ZZW9Kb2huc29uKGFsbF9udW1lcmljX3ByZWRpY3RvcnMoKSkgfD4gDQogIHN0ZXBfbm9ybWFsaXplKGFsbF9udW1lcmljX3ByZWRpY3RvcnMoKSkgfD4gDQogIHN0ZXBfenYoYWxsX251bWVyaWNfcHJlZGljdG9ycygpKSB8PiANCiAgc3RlcF9jb3JyKGFsbF9udW1lcmljX3ByZWRpY3RvcnMoKSkNCmBgYA0KDQo+DQoNCiMjIENvbmZpZ3VyYW5kbyBvcyBNb2RlbG9zDQoNCj4gIA0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQptb2RlbG9fZWxhc3RpYyA8LSANCiAgcGFyc25pcDo6bGluZWFyX3JlZyhwZW5hbHR5ID0gdHVuZTo6dHVuZSgpLCBtaXh0dXJlID0gdHVuZTo6dHVuZSgpKSB8PiANCiAgcGFyc25pcDo6c2V0X21vZGUoInJlZ3Jlc3Npb24iKSB8PiANCiAgcGFyc25pcDo6c2V0X2VuZ2luZSgiZ2xtbmV0IikNCg0KbW9kZWxvX2tubiA8LQ0KICBwYXJzbmlwOjpuZWFyZXN0X25laWdoYm9yKA0KICAgIG5laWdoYm9ycyA9IHR1bmU6OnR1bmUoKSwNCiAgICBkaXN0X3Bvd2VyID0gdHVuZTo6dHVuZSgpLCANCiAgICB3ZWlnaHRfZnVuYyA9ICJnYXVzc2lhbiIgDQogICkgfD4gDQogIHBhcnNuaXA6OnNldF9tb2RlKCJyZWdyZXNzaW9uIikgfD4gDQogIHBhcnNuaXA6OnNldF9lbmdpbmUoImtrbm4iKQ0KDQptb2RlbG9fc3ZtIDwtIA0KICBwYXJzbmlwOjpzdm1fcmJmKA0KICAgIGNvc3QgPSB0dW5lOjp0dW5lKCksDQogICAgcmJmX3NpZ21hID0gdHVuZTo6dHVuZSgpLA0KICAgIG1hcmdpbiA9IHR1bmU6OnR1bmUoKQ0KICApIHw+IA0KICBwYXJzbmlwOjpzZXRfbW9kZSgicmVncmVzc2lvbiIpIHw+IA0KICBwYXJzbmlwOjpzZXRfZW5naW5lKCJrZXJubGFiIikNCg0KbW9kZWxvX2Fydm9yZSA8LSBkZWNpc2lvbl90cmVlKGNvc3RfY29tcGxleGl0eSA9IHR1bmUoKSwgdHJlZV9kZXB0aCA9IHR1bmUoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fbiA9IHR1bmUoKSkgfD4gDQogIHNldF9lbmdpbmUoZW5naW5lID0gInJwYXJ0IikgfD4gDQogIHNldF9tb2RlKCJyZWdyZXNzaW9uIikNCg0KbW9kZWxvX2Zsb3Jlc3RhIDwtIA0KICByYW5kX2ZvcmVzdChtdHJ5ID0gdHVuZSgpLCBtaW5fbiA9IHR1bmUoKSwgdHJlZXMgPSB0dW5lKCkpIHw+ICAgDQogIHNldF9lbmdpbmUoZW5naW5lID0gInJhbmdlciIsIGltcG9ydGFuY2UgPSAiaW1wdXJpdHkiKSB8PiANCiAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKQ0KDQptb2RlbG9fYm9vdHN0cmVwIDwtIHhnYiA8LSBib29zdF90cmVlKHRyZWVfZGVwdGggPSB0dW5lKCksIGxlYXJuX3JhdGUgPSB0dW5lKCksDQogICAgICAgICAgICAgICAgICAgICAgICBsb3NzX3JlZHVjdGlvbiA9IHR1bmUoKSwgbWluX24gPSB0dW5lKCksDQogICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfc2l6ZSA9IHR1bmUoKSwgdHJlZXMgPSB0dW5lKCkpIHw+IA0KICAgc2V0X2VuZ2luZShlbmdpbmUgPSAieGdib29zdCIpIHw+IA0KICAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKQ0KYGBgDQoNCj4NCg0KIyMgVmFsaWRhw6fDo28gQ3J1emFkYQ0KDQo+DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnZhbGlkYWNhb19jcnV6YWRhMSA8LSANCiAgdHJlaW5hbWVudG8xIHw+IA0KICByc2FtcGxlOjp2Zm9sZF9jdih2ID0gNUwsIHN0cmF0YSA9IHZhbG9yKQ0KDQpgYGANCg0KPg0KDQojIyBXb3JmbG93DQoNCj4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0Kd2ZfdG9kb3MxIDwtDQogIHdvcmtmbG93X3NldCgNCiAgICBwcmVwcm9jID0gbGlzdChyZWNlaXRhMSksDQogICAgbW9kZWxzID0gbGlzdCgNCiAgICAgIGtubl9maXQgPSBtb2RlbG9fa25uLA0KICAgICAgZWxhc3RpY19maXQgPSBtb2RlbG9fZWxhc3RpYywNCiAgICAgIHN2bV9maXQgPSBtb2RlbG9fc3ZtLA0KICAgICAgYXJ2b3JlX2ZpdDwtIG1vZGVsb19hcnZvcmUsDQogICAgICBmbG9yZXN0YV9maXQ8LSBtb2RlbG9fZmxvcmVzdGEsDQogICAgICAgYm9vdF9maXQ8LSBtb2RlbG9fYm9vdHN0cmVwDQogICAgKSwNCiAgICBjcm9zcyA9IFRSVUUNCiAgKQ0KDQoNCmNvbnRyb2xlX2dyaWQxIDwtIGNvbnRyb2xfZ3JpZCgNCiAgc2F2ZV9wcmVkID0gVFJVRSwNCiAgc2F2ZV93b3JrZmxvdyA9IFRSVUUsDQogIHBhcmFsbGVsX292ZXIgPSAicmVzYW1wbGVzIg0KKQ0KDQpgYGANCg0KPg0KDQojIyBUcmVpbmFuZG8gbyBNb2RlbG8NCg0KPg0KDQpgYGB7cn0NCnRyZWlubzEgPC0NCiAgd2ZfdG9kb3MxIHw+IA0KICB3b3JrZmxvd19tYXAoDQogICAgcmVzYW1wbGVzID0gdmFsaWRhY2FvX2NydXphZGExLA0KICAgIGdyaWQgPSAyMEwsDQogICAgY29udHJvbCA9IGNvbnRyb2xlX2dyaWQxDQogICkNCg0KYXV0b3Bsb3QodHJlaW5vMSwgbWV0cmljID0gInJtc2UiKSArIA0KICBsYWJzKA0KICAgIHRpdGxlID0gIkF2YWxpYcOnw6NvIGRvcyBtb2RlbG9zIGRlIHJlZ3Jlc3PDo28iLA0KICAgIHN1YnRpdGxlID0gIlV0aWxpemFuZG8gYSBtw6l0cmljYSBkbyBFUU0iDQogICkgKyANCiAgeGxhYigiUmFuayBkb3MgV29ya2Zsb3dzIikgKw0KICB5bGFiKCJFcnJvIFF1YWRyw6F0aWNvIE3DqWRpbyAtIEVRTSIpDQoNCmBgYA0KDQoNCg0KPg0KDQojIyBNZWxob3Jlcw0KDQo+DQoNCmBgYHtyfQ0KbWVsaG9yZXMzIDwtIA0KICB0cmVpbm8zIHw+IA0KICByYW5rX3Jlc3VsdHMoc2VsZWN0X2Jlc3QgPSBUUlVFLCByYW5rX21ldHJpYyA9ICJybXNlIikNCmF1dG9wbG90KHRyZWlubzEsIHNlbGVjdF9iZXN0ID0gVFJVRSkNCnRyZWlubzMgfD4gDQogIHJhbmtfcmVzdWx0cygpDQpgYGANCg0KPg0KDQoNCmBgYHtyfQ0KbWVsaG9yX21vZGVsbyA8LSANCiAgdHJlaW5vMSB8PiANCiAgZXh0cmFjdF93b3JrZmxvd19zZXRfcmVzdWx0KGlkID0gInJlY2lwZV8xX3JhbmRfZm9yZXN0IikgfD4gDQogIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJybXNlIikNCmBgYA0KDQoNCg0KPg0KDQpgYGB7cn0NCndmX2ZpbmFsMyA8LSANCiAgdHJlaW5vMSB8PiANCiAgZXh0cmFjdF93b3JrZmxvdyhpZCA9ICJyZWNpcGVfMV9yYW5kX2ZvcmVzdCIpIHw+IA0KICBmaW5hbGl6ZV93b3JrZmxvdyhtZWxob3JfbW9kZWxvKQ0KDQp0ZXN0ZTIgPC0gDQogIHdmX2ZpbmFsMyB8PiANCiAgbGFzdF9maXQoc3BsaXQgPSBkaXZpc2FvX2luaWNpYWwxKQ0KDQp0ZXN0ZTIkLm1ldHJpY3MNCmBgYA0KDQoNCj4NCg0KIyMgTW9kZWxvIGZpbmFsDQoNCj4NCg0KYGBge3J9DQptb2RlbG9fZmluYWwgPC0gDQogIHdmX2ZpbmFsMyB8PiANCiAgZml0KGltb3ZlaXNzYWx2YWRvcikNCm1vZGVsb19maW5hbA0KYGBgDQoNCg0KDQoNCg0KDQo=