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])))
)
| 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)))
)
)
| 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])))
)
| 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=