# Load data and required packages
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
This project was completed as part of a Statistical Machine Learning final project. The 50/50 Diabetes Health Indicators Dataset, created using information from the Behavioral Risk Factor Surveillance System (BRFSS) by the CDC, was imported from Kaggle. There were 70,692 cases and 21 variables, including the outcome variable Diabetes_binary (0 for no diabetes, 1 for diabetes). I used a variety of statistical machine learning methods that we learned throughout the course.
In the supervised learning section I asked what factors are most important for predicting diabetes. The models used for this section include LASSO logistic regression, KNN, decision tree, and bagging and random forest.
In the unsupervised learning section, I used k-means and hierarchical clustering to examine what types of diagnoses a doctor might come to when considering variables such as HighBP, HighChol, and BMI.
library(dplyr)
library(tidyverse)
library(readr)
library(ggplot2)
library(tidymodels)
library(probably)
library(rpart.plot)
library(ranger)
library(vip)
library(cluster)
library(purrr)
tidymodels_prefer()
diabetes <- read.csv("diabetes_binary_5050split_health_indicators_BRFSS2015.csv")
diabetes <- diabetes %>% mutate(Age = ifelse(Age == 1, "18-24",
ifelse(Age == 2, "25-29",
ifelse(Age == 3, "30-34",
ifelse(Age==4, "35-39",
ifelse(Age==5,"40-44",
ifelse(Age==6, "45-49",
ifelse(Age==7, "50-54",
ifelse(Age==8,"55-59",
ifelse(Age==9, "60-64",
ifelse(Age==10, "65-69",
ifelse(Age==11, "70-74",
ifelse(Age==12, "75-79", "80+")))))))))))))
diabetes <- diabetes %>%
mutate(Diabetes_binary = relevel(factor(Diabetes_binary), ref="0",),
HighBP = relevel(factor(HighBP), ref="0"),
HighChol = relevel(factor(HighChol), ref="0"),
CholCheck = relevel(factor(CholCheck), ref="0"),
Smoker = relevel(factor(Smoker), ref="0"),
Stroke = relevel(factor(Stroke), ref="0"),
HeartDiseaseorAttack= relevel(factor(HeartDiseaseorAttack), ref="0"),
PhysActivity = relevel(factor(PhysActivity), ref="0"),
Fruits = relevel(factor(Fruits), ref="0"),
Veggies = relevel(factor(Veggies), ref="0"),
HvyAlcoholConsump = relevel(factor(HvyAlcoholConsump), ref="0"),
AnyHealthcare = relevel(factor(AnyHealthcare), ref="0"),
NoDocbcCost = relevel(factor(NoDocbcCost), ref="0"),
GenHlth = relevel(factor(GenHlth), ref="1"),
DiffWalk = relevel(factor(DiffWalk), ref="0"),
Sex = relevel(factor(Sex), ref="0"),
Age = relevel(factor(Age), ref="18-24"),
Education = relevel(factor(Education), ref="1"),
Income = relevel(factor(Income), ref="1"))
Supervised Learning Question
What are the most important health factors for predicting diabetes?
Exploratory Analyses
diabetes %>% count(Diabetes_binary)
## Diabetes_binary n
## 1 0 35346
## 2 1 35346
- The data contains a 50-50 split of individuals with no diabetes (0) and diabetes (1)
Predictors vs Outcome Plots
ggplot(diabetes, aes(fill = Age, x = Diabetes_binary)) +
geom_bar()

ggplot(diabetes) +
geom_bar(aes(x = Diabetes_binary, fill = HighBP))

ggplot(diabetes) +
geom_bar(aes(x = Diabetes_binary, fill = HighChol))

ggplot(diabetes) +
geom_boxplot(aes(x = Diabetes_binary, y = BMI))

Individuals with diabetes (1) tend to be older than individuals with no diabetes (0).
More individuals with diabetes have a high BP compared to individuals with no diabetes
More individuals with diabetes/prediabetes have high cholesterol compared to individuals with no diabetes
Individuals with diabetes/prediabetes have a slightly higher BMI on average compared to individuals with no diabetes
\
LASSO logistic regression:
Fit LASSO Model
set.seed(123)
# Set up CV folds
data_cv <- vfold_cv(diabetes, v = 12)
# LASSO logistic regression model specification
logistic_lasso_spec <- logistic_reg() %>%
set_engine("glmnet") %>%
set_args(mixture = 1, penalty = tune()) %>% # go through each penalty
set_mode("classification")
# Recipe
logistic_lasso_rec <- recipe(Diabetes_binary ~ ., data = diabetes) %>%
step_normalize(all_numeric_predictors()) %>%
step_dummy(all_nominal_predictors())
# Workflow (Recipe + Model)
log_lasso_wf <- workflow() %>%
add_model(logistic_lasso_spec) %>%
add_recipe(logistic_lasso_rec)
# Tune model: specify grid of parameters and tune
penalty_grid <- grid_regular(
penalty(range = c(-5,3)), # log10 scale, lambda seq 10^-5 to 10^3
levels = 100
)
tune_output <- tune_grid(
log_lasso_wf,
resamples = data_cv,
metrics = metric_set(roc_auc, accuracy),
grid = penalty_grid
)
Select and Fit Best LASSO Model
autoplot(tune_output) + theme_classic()

# Select "best" penalty by one standard error
best_se_penalty <- select_by_one_std_err(tune_output, metric = "roc_auc", desc(penalty))
# Define workflow with "best" penalty value
final_wf <- finalize_workflow(log_lasso_wf, best_se_penalty)
# Use final_wf to fit final model with "best" penalty value
final_fit_se <- fit(final_wf, data = diabetes)
Variable Importance
glmnet_output <- final_fit_se %>% extract_fit_parsnip() %>% pluck("fit") # get the original glmnet output
plot(glmnet_output, xvar = "lambda", label = TRUE, col = rainbow(20))

Least important variables:
final_fit_se %>% tidy() %>% filter(estimate == 0) # Unimportant variables
## # A tibble: 10 × 3
## term estimate penalty
## <chr> <dbl> <dbl>
## 1 PhysHlth 0 0.00320
## 2 Smoker_X1 0 0.00320
## 3 Fruits_X1 0 0.00320
## 4 AnyHealthcare_X1 0 0.00320
## 5 NoDocbcCost_X1 0 0.00320
## 6 Age_X50.54 0 0.00320
## 7 Age_X55.59 0 0.00320
## 8 Education_X4 0 0.00320
## 9 Education_X5 0 0.00320
## 10 Income_X5 0 0.00320
Most important variables:
final_fit_se %>% tidy() %>% filter(estimate != 0) %>%
arrange(desc(abs(estimate))) # Important variables
## # A tibble: 36 × 3
## term estimate penalty
## <chr> <dbl> <dbl>
## 1 (Intercept) -2.84 0.00320
## 2 GenHlth_X5 1.52 0.00320
## 3 GenHlth_X4 1.46 0.00320
## 4 CholCheck_X1 1.14 0.00320
## 5 Age_X25.29 -1.13 0.00320
## 6 GenHlth_X3 1.04 0.00320
## 7 Age_X30.34 -0.881 0.00320
## 8 HighBP_X1 0.756 0.00320
## 9 HvyAlcoholConsump_X1 -0.678 0.00320
## 10 Age_X35.39 -0.619 0.00320
## # … with 26 more rows
Model Evaluation
Accuracy and ROC AUC measures
tune_output %>%
collect_metrics() %>%
filter(penalty == best_se_penalty %>% pull(penalty)) # get standard error of the mean (variability across multiple samples of pop'n)
## # A tibble: 2 × 7
## penalty .metric .estimator mean n std_err .config
## <dbl> <chr> <chr> <dbl> <int> <dbl> <chr>
## 1 0.00320 accuracy binary 0.749 12 0.00120 Preprocessor1_Model032
## 2 0.00320 roc_auc binary 0.825 12 0.00123 Preprocessor1_Model032
No information rate
35346/(35346+35346)
## [1] 0.5
Misclassification Plots
lasso_mod_output <- predict(final_fit_se, new_data = diabetes) %>%
bind_cols(diabetes)
pred_labels <- c("0" = "Actual 0", "1" = "Actual 1")
# Plots
ggplot(lasso_mod_output, aes(x = BMI, fill = .pred_class)) +
geom_histogram() +
facet_wrap(~Diabetes_binary) +
labs(fill = "Predictions from LASSO", title = "BMI Predictions") +
theme_classic()

ggplot(lasso_mod_output, aes(x = HighBP, fill = .pred_class)) +
geom_bar() +
facet_wrap(~Diabetes_binary, labeller = labeller(Diabetes_binary = pred_labels)) +
labs(y = "Count", fill = "Predictions from LASSO", title = "HighBP Predictions") +
theme_classic()

ggplot(lasso_mod_output, aes(x = HighChol, fill = .pred_class)) +
geom_bar() +
facet_wrap(~Diabetes_binary, labeller = labeller(Diabetes_binary = pred_labels)) +
labs(y = "Count", fill = "Predictions from LASSO", title = "HighChol Predictions") +
theme_classic()

ggplot(lasso_mod_output, aes(x = Age, fill = .pred_class)) +
geom_bar() +
facet_wrap(~Diabetes_binary, labeller = labeller(Diabetes_binary = pred_labels)) +
labs(y = "Count", fill = "Predictions from LASSO", title = "Age Predictions") +
theme_classic() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

ggplot(lasso_mod_output, aes(x = GenHlth, fill = .pred_class)) +
geom_bar() +
facet_wrap(~Diabetes_binary, labeller = labeller(Diabetes_binary = pred_labels)) +
labs(y = "Count", fill = "Predictions from LASSO", title = "GenHlth Predictions") +
theme_classic()

LASSO Summary
The best value of lambda, chosen by selecting the best penalty by one standard error, is 0.003199267. The coefficient signs and magnitudes for our LASSO model make sense, as the ones that most greatly impact the model are commonly known risk factors or medical complications of diabetes.
With the set of penalty values that I used, 5 variables had their coefficients set equal to 0. PhysHlth, Smoker, Fruits, AnyHealthcare, and NoDocbcCost were not particularly important for predicting whether or not an individual has diabetes, after accounting for the other features in the model. The 5 variables with the greatest importance were GenHlth, HighBP, BMI, and HighChol, which correlates with the biggest risk factors/complications of diabetes. Of the unimportant variables, we were most surprised by the smoking variable, since the CDC says smokers have a greater risk of developing type 2 diabetes than non smokers.
The model's accuracy (74.9%) and roc_auc (82.5%) is higher than the NIR of 50%, so our accuracy isn’t purely driven by an overwhelming majority in one particular class.
With the set of penalty values that I used, 5 variables had their coefficients set equal to 0. PhysHlth, Smoker, Fruits, AnyHealthcare, and NoDocbcCost were not that important in predicting whether or not an individual has diabetes, after accounting for the other features in the model. Those of highest importance included variables like GenHlth, HighBP, BMI, and HighChol, and Age. Of the unimportant variables, we were most surprised by the smoking variable, since the CDC says smokers have a greater risk of developing type 2 diabetes than non smokers.
This LASSO model is good at predicting variables for risk factors of the outcome, but not as good at predicting variable values that are less common with that outcome. For example, it was able to predict someone as having diabetes (1) for individuals with high blood pressure (HighBP 1), but had a harder time predicting no high blood pressure (HighBP 0).
\
K-nearest neighbors
Fit KNN Model
knn_spec <- nearest_neighbor() %>% # General model type
set_args(neighbors = tune()) %>% # tuning parameter
set_engine(engine = "kknn") %>% # Engine name
set_mode("classification")
diabetes_cv <- vfold_cv(diabetes, v = 6) # Supply dataset and # of folds
diabetes_rec <- recipe(Diabetes_binary ~ HighBP + BMI + HighChol + DiffWalk + GenHlth + HeartDiseaseorAttack + Income + CholCheck + HvyAlcoholConsump + Age + Education + Sex + Stroke + PhysActivity + Veggies + MentHlth, data = diabetes) %>%
step_dummy(all_nominal_predictors()) %>%
step_normalize(all_numeric_predictors())
diabetes_wf <- workflow() %>%
add_model(knn_spec) %>% # Model specification object
add_recipe(diabetes_rec) # Data preprocessing recipe object
tuning_param_grid <- grid_regular(
neighbors(range = c(1, 100)), # min and max of values for neighbors
levels = 5 # number of neighbors values
)
knn_tune_output <- tune_grid(
diabetes_wf,
resamples = diabetes_cv,
metrics = metric_set(roc_auc, accuracy),
grid = tuning_param_grid
)
Select and Fit Best Number of Neighbors
autoplot(knn_tune_output) + theme_classic()

## Choose neighbors value that leads to the highest neighbors within 1 std. err.
knn_tune_output %>%
select_by_one_std_err(metric = "roc_auc", desc(neighbors)) ## The desc(neighbors) sorts the data from highest to lowest # of neighbors (most simple -> most complex)
## # A tibble: 1 × 9
## neighbors .metric .estimator mean n std_err .config .best .bound
## <int> <chr> <chr> <dbl> <int> <dbl> <chr> <dbl> <dbl>
## 1 100 roc_auc binary 0.807 6 0.00106 Preprocessor1_M… 0.807 0.806
knn_tune_output %>%
select_by_one_std_err(metric = "accuracy", desc(neighbors))
## # A tibble: 1 × 9
## neighbors .metric .estimator mean n std_err .config .best .bound
## <int> <chr> <chr> <dbl> <int> <dbl> <chr> <dbl> <dbl>
## 1 100 accuracy binary 0.734 6 0.00110 Preprocessor1_… 0.734 0.733
best_se_neighbors <- select_by_one_std_err(knn_tune_output, metric = "roc_auc", desc(neighbors))
final_wf <- finalize_workflow(diabetes_wf, best_se_neighbors)
final_fit <- fit(final_wf, data = diabetes)
Model Evaluation
# Show evaluation metrics for different values of neighbors, ordered
knn_tune_output %>% show_best(metric = "roc_auc")
## # A tibble: 5 × 7
## neighbors .metric .estimator mean n std_err .config
## <int> <chr> <chr> <dbl> <int> <dbl> <chr>
## 1 100 roc_auc binary 0.807 6 0.00106 Preprocessor1_Model5
## 2 75 roc_auc binary 0.806 6 0.00108 Preprocessor1_Model4
## 3 50 roc_auc binary 0.803 6 0.00114 Preprocessor1_Model3
## 4 25 roc_auc binary 0.794 6 0.00143 Preprocessor1_Model2
## 5 1 roc_auc binary 0.662 6 0.00179 Preprocessor1_Model1
knn_tune_output %>% show_best(metric = "accuracy")
## # A tibble: 5 × 7
## neighbors .metric .estimator mean n std_err .config
## <int> <chr> <chr> <dbl> <int> <dbl> <chr>
## 1 100 accuracy binary 0.734 6 0.00110 Preprocessor1_Model5
## 2 75 accuracy binary 0.733 6 0.000959 Preprocessor1_Model4
## 3 50 accuracy binary 0.732 6 0.00120 Preprocessor1_Model3
## 4 25 accuracy binary 0.724 6 0.00153 Preprocessor1_Model2
## 5 1 accuracy binary 0.662 6 0.00178 Preprocessor1_Model1
Misclassification Plots
# Use the best model to make predictions
knn_mod_out <- predict(final_fit, new_data = diabetes) %>%
bind_cols(diabetes)
pred_labels <- c("0" = "Actual 0", "1" = "Actual 1")
# Plots
ggplot(knn_mod_out, aes(x = BMI, fill = .pred_class)) +
geom_histogram() +
labs(y = "BMI", fill = "Predictions from KNN") +
facet_wrap(~Diabetes_binary) +
theme_classic()

ggplot(knn_mod_out, aes(x = HighBP, fill = .pred_class)) +
geom_bar() +
facet_wrap(~Diabetes_binary, labeller = labeller(Diabetes_binary = pred_labels)) +
labs(y = "Count", fill = "Predictions from KNN") +
theme_classic()

ggplot(knn_mod_out, aes(x = HighChol, fill = .pred_class)) +
geom_bar() +
facet_wrap(~Diabetes_binary, labeller = labeller(Diabetes_binary = pred_labels)) +
labs(y = "Count", fill = "Predictions from KNN") +
theme_classic()

ggplot(knn_mod_out, aes(x = Age, fill = .pred_class)) +
geom_bar() +
facet_wrap(~Diabetes_binary, labeller = labeller(Diabetes_binary = pred_labels)) +
labs(y = "Count", fill = "Predictions from KNN") +
theme_classic()

ggplot(knn_mod_out, aes(x = GenHlth, fill = .pred_class)) +
geom_bar() +
facet_wrap(~Diabetes_binary, labeller = labeller(Diabetes_binary = pred_labels)) +
labs(y = "Count", fill = "Predictions from KNN") +
theme_classic()

conf_mat( # confusion matrix
data = knn_mod_out,
truth = Diabetes_binary,
estimate = .pred_class
)
## Truth
## Prediction 0 1
## 0 26055 7406
## 1 9291 27940
KNN Summary
The best value for the number of neighbors would be 100, chosen by selecting the best number of neighbors by one standard error so as to achieve the best model with the least chance of being overfit. Similarly to the LASSO model, my KNN model is good at predicting diabetes where the risk factor is in line with the outcome, yet struggled more when the risk factor contradicted the outcome variable. For example, when HighChol = 1 and diabetes was not present, the model tended to have around a 60% accuracy rate, contrasted with its higher accuracy when HighChol and diabetes were both present.
With 100 neighbors for our model we have an accuracy of 73.6% (standard error: 0.0025) and an roc_auc of 80.8% (standard error: 0.0018).
\
Decision Tree
Fit Decision Tree
ct_spec <- decision_tree() %>%
set_engine(engine = "rpart") %>%
set_args(
cost_complexity = tune(),
min_n = 2,
tree_depth = NULL
) %>%
set_mode("classification")
data_cv <- vfold_cv(diabetes, v = 6)
data_rec <- recipe(Diabetes_binary ~ ., data = diabetes)
data_wf <- workflow() %>%
add_model(ct_spec) %>%
add_recipe(data_rec)
param_grid <- grid_regular(
cost_complexity(range = c(-5, -1)),
levels = 10)
tune_res <- tune_grid(
data_wf,
resamples = data_cv,
grid = param_grid,
metrics = metric_set(accuracy, roc_auc)
)
Select and Fit Best Tree
autoplot(tune_res) + theme_classic()

best_complexity <- select_by_one_std_err(tune_res, metric = 'roc_auc', desc(cost_complexity))
data_wf_final <- finalize_workflow(data_wf, best_complexity)
final_fit <- fit(data_wf_final, data = diabetes)
Visualize Tree
# Plot the tree (rpart.plot package)
final_fit %>%
extract_fit_engine() %>%
rpart.plot()

Variable Importance
# Variable importance metrics
# Sum of the goodness of split measures (impurity reduction) for each split for which it was the primary variable.
final_fit %>%
extract_fit_engine() %>%
pluck('variable.importance')
## HighBP GenHlth Age
## 5144.7557155 4165.5466942 2153.8845464
## BMI HighChol Income
## 1929.6252743 1529.5689982 868.5379667
## PhysHlth DiffWalk PhysActivity
## 544.7010297 470.9318388 232.6852643
## MentHlth HeartDiseaseorAttack HvyAlcoholConsump
## 79.7369107 49.3457654 48.9001103
## Sex Education CholCheck
## 23.4850427 19.3695753 18.5153717
## NoDocbcCost Stroke AnyHealthcare
## 17.2777173 10.5364755 9.7694860
## Smoker Fruits Veggies
## 9.4967505 1.6387786 0.1102831
# Predictions and Exploring Error
diabetes_preds <- diabetes %>%
mutate(
pred_prob = predict(final_fit, new_data = diabetes, type = "prob"),
pred_class = predict(final_fit, new_data = diabetes, type = "class")
)
diabetes_preds1 <- diabetes_preds %>% mutate(misclassified = Diabetes_binary!=pred_class$.pred_class)
Model Evaluation
tune_res %>%
select_by_one_std_err(metric = "roc_auc", desc(cost_complexity))
## # A tibble: 1 × 9
## cost_complexity .metric .estimator mean n std_err .config .best .bound
## <dbl> <chr> <chr> <dbl> <int> <dbl> <chr> <dbl> <dbl>
## 1 0.000215 roc_auc binary 0.802 6 0.00132 Preproces… 0.802 0.800
tune_res %>%
select_by_one_std_err(metric = "accuracy", desc(cost_complexity))
## # A tibble: 1 × 9
## cost_complexity .metric .estimator mean n std_err .config .best .bound
## <dbl> <chr> <chr> <dbl> <int> <dbl> <chr> <dbl> <dbl>
## 1 0.000599 accuracy binary 0.740 6 0.00305 Preproce… 0.742 0.740
Misclassification Plots
ggplot(diabetes_preds1, aes(x = misclassified, fill = HighBP)) +
geom_bar() +
labs(title = "High BP vs Predictions")

ggplot(diabetes_preds1, aes(x = misclassified, fill = GenHlth)) +
geom_bar(position = "fill") +
labs(title = "GenHlth vs Predictions", y = "%")

ggplot(diabetes_preds1, aes(x = misclassified, fill = Age)) +
geom_bar(position = "fill") +
labs(title = "Age vs Predictions", y = "%")

ggplot(diabetes_preds1, aes(y = BMI, x = misclassified)) +
geom_boxplot() +
labs(title = "BMI vs Predictions")

ggplot(diabetes_preds1, aes(x = misclassified, fill = HighChol)) +
geom_bar() +
labs(title = "HighChol vs Predictions")

Decision Tree Summary
The decision tree model results in a slightly different variable importance order than LASSO, with the top 5 also containing HighBP, GenHlth, and BMI, but also including Age and HighChol. The least important variables were very different, with the top 5 least important predictors including Veggies, Fruits, AnyHealthcare, Stroke, and CholCheck.
For most of the predictors, the rate of misclassification seems to be equal, illustrating the strength of the decision tree model. For HighBP and HighChol, the proportion of having either was relatively equal in cases where the model was accurate and where it wasn’t. Thus, there doesn’t seem to be a bias present where the model attaches a predictor with having diabetes. The exception to this is where there appears to be a higher rate of misclassification among individuals with a GenHlth rating of “Average” (3) and older individuals within the Age variable.
The accuracy of this decision tree model is around 80% (standard error: 0.001), and the ROC AUC is around 74% (standard error: 0.001).
Bagging and Random Forests
Fit Random Forest Model
# Model Specification
rf_spec <- rand_forest() %>%
set_engine(engine = "ranger") %>%
set_args(
mtry = NULL, # size of random subset of variables
trees = 1000, # Number of trees
min_n = 2,
probability = FALSE, # FALSE: get hard predictions
importance = "impurity"
) %>%
set_mode("classification")
# Recipe
data_rec <- recipe(Diabetes_binary ~ ., data = diabetes)
# Workflows
data_wf <- workflow() %>%
add_model(rf_spec) %>%
add_recipe(data_rec)
# No tune_grid() or vfold_cv()
rf_fit <- fit(data_wf, data = diabetes)
Variable Importance
# Plot of the variable importance information
rf_fit %>%
extract_fit_engine() %>%
vip(num_features = 30) + theme_classic()

# Extract the numerical information on variable importance and display the most and least important predictors
rf_var_imp <- rf_fit %>%
extract_fit_engine() %>%
vip::vi()
head(rf_var_imp)
## # A tibble: 6 × 2
## Variable Importance
## <chr> <dbl>
## 1 BMI 4185.
## 2 GenHlth 3548.
## 3 Age 3136.
## 4 HighBP 2599.
## 5 Income 2006.
## 6 PhysHlth 1809.
tail(rf_var_imp)
## # A tibble: 6 × 2
## Variable Importance
## <chr> <dbl>
## 1 Veggies 547.
## 2 NoDocbcCost 346.
## 3 Stroke 292.
## 4 HvyAlcoholConsump 282.
## 5 AnyHealthcare 223.
## 6 CholCheck 196.
Model Evaluation
rf_fit
## ══ Workflow [trained] ══════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: rand_forest()
##
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 0 Recipe Steps
##
## ── Model ───────────────────────────────────────────────────────────────────────
## Ranger result
##
## Call:
## ranger::ranger(x = maybe_data_frame(x), y = y, num.trees = ~1000, min.node.size = min_rows(~2, x), probability = ~FALSE, importance = ~"impurity", num.threads = 1, verbose = FALSE, seed = sample.int(10^5, 1))
##
## Type: Classification
## Number of trees: 1000
## Sample size: 70692
## Number of independent variables: 21
## Mtry: 4
## Target node size: 2
## Variable importance mode: impurity
## Splitrule: gini
## OOB prediction error: 25.26 %
Misclassification Plots
# OOB confusion matrix
rf_output <- diabetes %>%
mutate(OOB_pred_diabetes = rf_fit %>% extract_fit_engine() %>% pluck("predictions")) # extracts OOB predictions
conf_mat( # confusion matrix
data = rf_output,
truth = Diabetes_binary,
estimate = OOB_pred_diabetes
)
## Truth
## Prediction 0 1
## 0 24899 7410
## 1 10447 27936
rf_output <- rf_output %>%
mutate(is_misclass = Diabetes_binary!=OOB_pred_diabetes)
# Plots
ggplot(rf_output, aes(x = is_misclass, y = BMI)) +
geom_boxplot() +
labs(title = "BMI vs Prediction Misclassification", x = "misclassified")

ggplot(rf_output, aes(x = is_misclass, fill = HighBP)) +
geom_bar() +
labs(title = "HighBP vs Prediction Misclassification", x = "misclassified")

ggplot(rf_output, aes(x = is_misclass, fill = HighChol)) +
geom_bar() +
labs(title = "HighChol vs Prediction Misclassification", x = "misclassified")

ggplot(rf_output, aes(x = is_misclass, fill = GenHlth)) +
geom_bar(position = "fill") +
labs(title = "GenHlth vs Prediction Misclassification", y = "%", x = "misclassified")

ggplot(rf_output, aes(x = is_misclass, fill = Age)) +
geom_bar(position = "fill") +
labs(title = "Age vs Prediction Misclassification", y = "%", x = "misclassified")

Bagging and Random Forest Summary
Variable importance for bagging and random forest is very similar to that of the decision tree model, except that the Income variable is in the top 5 more important variables instead of HighChol. The least important variables also remained mostly the same, except HvyAlcoholConsumption replaced the Fruits variable.
Similarly to Decision trees, for most of the predictors the rate of misclassification seems to be equal, illustrating the strength of the bagging and random forest model. The exception is again with the GenHlth category, where there appears to be a higher rate of misclassification among individuals with a GenHlth rating of “Average” (3), and the older age categories within the Age variable.
The out-of-bag prediction error rate for this model is 25.26%, which makes the accuracy rate around 75%. Using the confusion matrix, I was able to caluclate the specificity and sensitivity of the model, which were 79% and 75% respectively.
Supervised Learning Answer
The most common important variables for the supervised learning models included HighBP, BMI, GenHlth, and Age, and the most common least important variables included Stroke, Veggies, CholCheck, AnyHealthcare, Fruits. These results come from supervised learning models that were between 74% and 83% accurate.
Unsupervised Learning Question
What types of diagnoses might a doctor come to when considering variables such as HighBP, HighChol, and BMI?
K-means Clustering
diabetes_clust <- diabetes %>%
select(HighBP, HighChol, BMI, Diabetes_binary) %>%
slice_sample(n = 100)
# Choosing an appropriate number of clusters
# Create storage vector for total within-cluster sum of squares
tot_wc_ss <- rep(0, 20)
# Loop
for (k in 1:20) {
# Perform clustering
pam_out <- pam(daisy(diabetes_clust), k = k)
# Store the total within-cluster sum of squares
tot_wc_ss[k-1] <- sum(pam_out$clusinfo[,"av_diss"]*pam_out$clusinfo[,"size"])
}
plot(1:20, tot_wc_ss, xlab = "Number of clusters", ylab = "Total within-cluster sum of squares")

Hierarchical Clustering
# Random subsample of 50 penguins
set.seed(253)
# Compute a distance matrix on the scaled data
dist_mat_scaled <- dist(daisy(diabetes_clust %>% select(BMI, HighBP, HighChol)))
# The (scaled) distance matrix is the input to hclust()
# The method argument indicates the linkage type
hc_complete <- hclust(dist_mat_scaled, method = "complete")
hc_single <- hclust(dist_mat_scaled, method = "single")
hc_average <- hclust(dist_mat_scaled, method = "average")
hc_centroid <- hclust(dist_mat_scaled, method = "centroid")
# Plot dendrograms
plot(hc_complete, labels = diabetes_clust$Diabetes_binary)

plot(hc_single, labels = diabetes_clust$Diabetes_binary)

plot(hc_average, labels = diabetes_clust$Diabetes_binary)

plot(hc_centroid, labels = diabetes_clust$Diabetes_binary)

diabetes4 <- diabetes_clust %>%
mutate(
hclust_height1.5 = factor(cutree(hc_complete, h = 1.5)), # Cut at height (h) 3
hclust_num3 = factor(cutree(hc_complete, k = 3)) # Cut into 6 clusters (k)
)
Cluster Plots
ggplot(diabetes4, aes(x = hclust_num3, y = BMI)) +
geom_boxplot() +
labs(x = "Cluster", title = "BMI vs Clusters")

ggplot(diabetes4, aes(x = hclust_num3, fill = HighBP)) +
geom_bar() +
labs(x = "Cluster", title = "HighBP vs Clusters")

ggplot(diabetes4, aes(x = hclust_num3, fill = HighChol)) +
geom_bar() +
labs(x = "Cluster", title = "HighChol vs Clusters")

Clustering Summary
The point at which there are no longer meaningful decreases in heterogeneity occurs somewhere between 3 and 6 clusters. Remembering my goal of finding diagnosis types, I want to favor a fewer number of clusters and chose to create plots considering 3 clusters. These consisted of cluster 1, with individuals who generally have a High BP, mixed cholesterol levels, and a high BMI, cluster 2, with individuals who generally have non-high BP, high cholesterol levels, and a medium BMI, and cluster 3, with individuals who generally have non-high BP, low cholesterol levels, and low BMI. To answer the unsupervised learning question, I would hypothesize that cluster 1 could be considered a diagnosis of diabetes, cluster 2 could be considered a diagnosis of prediabetes, and cluster 3 could be considered a diagnosis of no diabetes.
Cautions and Limitations
Looking back at these models, I would use them as diagnostic tools very cautiously, considering the lower accuracy and roc auc model evaluation metrics (between 74% and 83%). It is important to note that predictors are not the same as causes and should not be treated as so when considering our model. For limitations, I must consider that the data collection method was through phone surveys and therefore may be prone to biases consistant with such method. Another limitation was the time constraints for this project, including the fact that this dataset was so large that it takes significant computational time to run many of the models.
LS0tCnRpdGxlOiAiTWFjaGluZSBMZWFybmluZyBGaW5hbCBQcm9qZWN0OiBQcmVkaWN0aW5nIERpYWJldGVzIgphdXRob3I6ICJOaWNvbGUgQnJhbmNoIgpvdXRwdXQ6CiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgc3BsaXRfYnk6IG5vbmUKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQotLS0KCmBgYHtyIHNldHVwfQojIExvYWQgZGF0YSBhbmQgcmVxdWlyZWQgcGFja2FnZXMKa25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQpgYGAKClRoaXMgcHJvamVjdCB3YXMgY29tcGxldGVkIGFzIHBhcnQgb2YgYSBTdGF0aXN0aWNhbCBNYWNoaW5lIExlYXJuaW5nIGZpbmFsIHByb2plY3QuIFRoZSBbNTAvNTAgRGlhYmV0ZXMgSGVhbHRoIEluZGljYXRvcnMgRGF0YXNldF0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9hbGV4dGVib3VsL2RpYWJldGVzLWhlYWx0aC1pbmRpY2F0b3JzLWRhdGFzZXQ/cmVzb3VyY2U9ZG93bmxvYWQmc2VsZWN0PWRpYWJldGVzX2JpbmFyeV81MDUwc3BsaXRfaGVhbHRoX2luZGljYXRvcnNfQlJGU1MyMDE1LmNzdiksIGNyZWF0ZWQgdXNpbmcgaW5mb3JtYXRpb24gZnJvbSB0aGUgQmVoYXZpb3JhbCBSaXNrIEZhY3RvciBTdXJ2ZWlsbGFuY2UgU3lzdGVtIChCUkZTUykgYnkgdGhlIENEQywgd2FzIGltcG9ydGVkIGZyb20gS2FnZ2xlLiBUaGVyZSB3ZXJlIDcwLDY5MiBjYXNlcyBhbmQgMjEgdmFyaWFibGVzLCBpbmNsdWRpbmcgdGhlIG91dGNvbWUgdmFyaWFibGUgRGlhYmV0ZXNfYmluYXJ5ICgwIGZvciBubyBkaWFiZXRlcywgMSBmb3IgZGlhYmV0ZXMpLiBJIHVzZWQgYSB2YXJpZXR5IG9mIHN0YXRpc3RpY2FsIG1hY2hpbmUgbGVhcm5pbmcgbWV0aG9kcyB0aGF0IHdlIGxlYXJuZWQgdGhyb3VnaG91dCB0aGUgY291cnNlLgoKSW4gdGhlIHN1cGVydmlzZWQgbGVhcm5pbmcgc2VjdGlvbiBJIGFza2VkIHdoYXQgZmFjdG9ycyBhcmUgbW9zdCBpbXBvcnRhbnQgZm9yIHByZWRpY3RpbmcgZGlhYmV0ZXMuIFRoZSBtb2RlbHMgdXNlZCBmb3IgdGhpcyBzZWN0aW9uIGluY2x1ZGUgTEFTU08gbG9naXN0aWMgcmVncmVzc2lvbiwgS05OLCBkZWNpc2lvbiB0cmVlLCBhbmQgYmFnZ2luZyBhbmQgcmFuZG9tIGZvcmVzdC4KCkluIHRoZSB1bnN1cGVydmlzZWQgbGVhcm5pbmcgc2VjdGlvbiwgSSB1c2VkIGstbWVhbnMgYW5kIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHRvIGV4YW1pbmUgd2hhdCB0eXBlcyBvZiBkaWFnbm9zZXMgYSBkb2N0b3IgbWlnaHQgY29tZSB0byB3aGVuIGNvbnNpZGVyaW5nIHZhcmlhYmxlcyBzdWNoIGFzIEhpZ2hCUCwgSGlnaENob2wsIGFuZCBCTUkuCgotLS0KCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyZWFkcikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpZHltb2RlbHMpCmxpYnJhcnkocHJvYmFibHkpCmxpYnJhcnkocnBhcnQucGxvdCkKbGlicmFyeShyYW5nZXIpCmxpYnJhcnkodmlwKQpsaWJyYXJ5KGNsdXN0ZXIpCmxpYnJhcnkocHVycnIpCnRpZHltb2RlbHNfcHJlZmVyKCkKYGBgCgoKYGBge3J9CmRpYWJldGVzIDwtIHJlYWQuY3N2KCJkaWFiZXRlc19iaW5hcnlfNTA1MHNwbGl0X2hlYWx0aF9pbmRpY2F0b3JzX0JSRlNTMjAxNS5jc3YiKQpgYGAKCmBgYHtyfQpkaWFiZXRlcyA8LSBkaWFiZXRlcyAlPiUgbXV0YXRlKEFnZSA9IGlmZWxzZShBZ2UgPT0gMSwgIjE4LTI0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEFnZSA9PSAyLCAiMjUtMjkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShBZ2UgPT0gMywgIjMwLTM0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoQWdlPT00LCAiMzUtMzkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoQWdlPT01LCI0MC00NCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShBZ2U9PTYsICI0NS00OSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShBZ2U9PTcsICI1MC01NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEFnZT09OCwiNTUtNTkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShBZ2U9PTksICI2MC02NCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShBZ2U9PTEwLCAiNjUtNjkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShBZ2U9PTExLCAiNzAtNzQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoQWdlPT0xMiwgIjc1LTc5IiwgIjgwKyIpKSkpKSkpKSkpKSkpCgoKZGlhYmV0ZXMgPC0gZGlhYmV0ZXMgJT4lCiAgICBtdXRhdGUoRGlhYmV0ZXNfYmluYXJ5ID0gcmVsZXZlbChmYWN0b3IoRGlhYmV0ZXNfYmluYXJ5KSwgcmVmPSIwIiwpLAogICAgICAgICAgIEhpZ2hCUCA9IHJlbGV2ZWwoZmFjdG9yKEhpZ2hCUCksIHJlZj0iMCIpLAogICAgICAgICAgIEhpZ2hDaG9sID0gcmVsZXZlbChmYWN0b3IoSGlnaENob2wpLCByZWY9IjAiKSwKICAgICAgICAgICBDaG9sQ2hlY2sgPSByZWxldmVsKGZhY3RvcihDaG9sQ2hlY2spLCByZWY9IjAiKSwKICAgICAgICAgICBTbW9rZXIgPSByZWxldmVsKGZhY3RvcihTbW9rZXIpLCByZWY9IjAiKSwKICAgICAgICAgICBTdHJva2UgPSByZWxldmVsKGZhY3RvcihTdHJva2UpLCByZWY9IjAiKSwKICAgICAgICAgICBIZWFydERpc2Vhc2VvckF0dGFjaz0gcmVsZXZlbChmYWN0b3IoSGVhcnREaXNlYXNlb3JBdHRhY2spLCByZWY9IjAiKSwKICAgICAgICAgICBQaHlzQWN0aXZpdHkgPSByZWxldmVsKGZhY3RvcihQaHlzQWN0aXZpdHkpLCByZWY9IjAiKSwKICAgICAgICAgICBGcnVpdHMgPSByZWxldmVsKGZhY3RvcihGcnVpdHMpLCByZWY9IjAiKSwKICAgICAgICAgICBWZWdnaWVzID0gcmVsZXZlbChmYWN0b3IoVmVnZ2llcyksIHJlZj0iMCIpLAogICAgICAgICAgIEh2eUFsY29ob2xDb25zdW1wID0gcmVsZXZlbChmYWN0b3IoSHZ5QWxjb2hvbENvbnN1bXApLCByZWY9IjAiKSwKICAgICAgICAgICBBbnlIZWFsdGhjYXJlID0gcmVsZXZlbChmYWN0b3IoQW55SGVhbHRoY2FyZSksIHJlZj0iMCIpLAogICAgICAgICAgIE5vRG9jYmNDb3N0ID0gcmVsZXZlbChmYWN0b3IoTm9Eb2NiY0Nvc3QpLCByZWY9IjAiKSwKICAgICAgICAgICBHZW5IbHRoID0gcmVsZXZlbChmYWN0b3IoR2VuSGx0aCksIHJlZj0iMSIpLAogICAgICAgICAgIERpZmZXYWxrID0gcmVsZXZlbChmYWN0b3IoRGlmZldhbGspLCByZWY9IjAiKSwKICAgICAgICAgICBTZXggPSByZWxldmVsKGZhY3RvcihTZXgpLCByZWY9IjAiKSwKICAgICAgICAgICBBZ2UgPSByZWxldmVsKGZhY3RvcihBZ2UpLCByZWY9IjE4LTI0IiksCiAgICAgICAgICAgRWR1Y2F0aW9uID0gcmVsZXZlbChmYWN0b3IoRWR1Y2F0aW9uKSwgcmVmPSIxIiksCiAgICAgICAgICAgSW5jb21lID0gcmVsZXZlbChmYWN0b3IoSW5jb21lKSwgcmVmPSIxIikpCmBgYAoKIyBTdXBlcnZpc2VkIExlYXJuaW5nIFF1ZXN0aW9uCgo+V2hhdCBhcmUgdGhlIG1vc3QgaW1wb3J0YW50IGhlYWx0aCBmYWN0b3JzIGZvciBwcmVkaWN0aW5nIGRpYWJldGVzPwoKCiMjIEV4cGxvcmF0b3J5IEFuYWx5c2VzCgoKYGBge3J9CmRpYWJldGVzICU+JSBjb3VudChEaWFiZXRlc19iaW5hcnkpCmBgYAoKKyBUaGUgZGF0YSBjb250YWlucyBhIDUwLTUwIHNwbGl0IG9mIGluZGl2aWR1YWxzIHdpdGggbm8gZGlhYmV0ZXMgKDApIGFuZCBkaWFiZXRlcyAoMSkKCgojIyMgUHJlZGljdG9ycyB2cyBPdXRjb21lIFBsb3RzCgpgYGB7cn0KZ2dwbG90KGRpYWJldGVzLCBhZXMoZmlsbCA9IEFnZSwgeCA9IERpYWJldGVzX2JpbmFyeSkpICsKICBnZW9tX2JhcigpCgpnZ3Bsb3QoZGlhYmV0ZXMpICsKICBnZW9tX2JhcihhZXMoeCA9IERpYWJldGVzX2JpbmFyeSwgZmlsbCA9IEhpZ2hCUCkpCgpnZ3Bsb3QoZGlhYmV0ZXMpICsKICBnZW9tX2JhcihhZXMoeCA9IERpYWJldGVzX2JpbmFyeSwgZmlsbCA9IEhpZ2hDaG9sKSkKCmdncGxvdChkaWFiZXRlcykgKwogIGdlb21fYm94cGxvdChhZXMoeCA9IERpYWJldGVzX2JpbmFyeSwgeSA9IEJNSSkpCmBgYAoKKyBJbmRpdmlkdWFscyB3aXRoIGRpYWJldGVzICgxKSB0ZW5kIHRvIGJlIG9sZGVyIHRoYW4gaW5kaXZpZHVhbHMgd2l0aCBubyBkaWFiZXRlcyAoMCkuCgorIE1vcmUgaW5kaXZpZHVhbHMgd2l0aCBkaWFiZXRlcyBoYXZlIGEgaGlnaCBCUCBjb21wYXJlZCB0byBpbmRpdmlkdWFscyB3aXRoIG5vIGRpYWJldGVzCgorIE1vcmUgaW5kaXZpZHVhbHMgd2l0aCBkaWFiZXRlcy9wcmVkaWFiZXRlcyBoYXZlIGhpZ2ggY2hvbGVzdGVyb2wgY29tcGFyZWQgdG8gaW5kaXZpZHVhbHMgd2l0aCBubyBkaWFiZXRlcwoKKyBJbmRpdmlkdWFscyB3aXRoIGRpYWJldGVzL3ByZWRpYWJldGVzIGhhdmUgYSBzbGlnaHRseSBoaWdoZXIgQk1JIG9uIGF2ZXJhZ2UgY29tcGFyZWQgdG8gaW5kaXZpZHVhbHMgd2l0aCBubyBkaWFiZXRlcwoKXFwKCgoKIyMgTEFTU08gbG9naXN0aWMgcmVncmVzc2lvbjoKCiMjIyBGaXQgTEFTU08gTW9kZWwKCmBgYHtyfQpzZXQuc2VlZCgxMjMpCgoKIyBTZXQgdXAgQ1YgZm9sZHMKZGF0YV9jdiA8LSB2Zm9sZF9jdihkaWFiZXRlcywgdiA9IDEyKQoKIyBMQVNTTyBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHNwZWNpZmljYXRpb24KbG9naXN0aWNfbGFzc29fc3BlYyA8LSBsb2dpc3RpY19yZWcoKSAlPiUKICAgIHNldF9lbmdpbmUoImdsbW5ldCIpICU+JQogICAgc2V0X2FyZ3MobWl4dHVyZSA9IDEsIHBlbmFsdHkgPSB0dW5lKCkpICU+JSAjIGdvIHRocm91Z2ggZWFjaCBwZW5hbHR5CiAgICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKQoKIyBSZWNpcGUKbG9naXN0aWNfbGFzc29fcmVjIDwtIHJlY2lwZShEaWFiZXRlc19iaW5hcnkgfiAuLCBkYXRhID0gZGlhYmV0ZXMpICU+JQogICAgc3RlcF9ub3JtYWxpemUoYWxsX251bWVyaWNfcHJlZGljdG9ycygpKSAlPiUgCiAgICBzdGVwX2R1bW15KGFsbF9ub21pbmFsX3ByZWRpY3RvcnMoKSkKCiMgV29ya2Zsb3cgKFJlY2lwZSArIE1vZGVsKQpsb2dfbGFzc29fd2YgPC0gd29ya2Zsb3coKSAlPiUKICAgIGFkZF9tb2RlbChsb2dpc3RpY19sYXNzb19zcGVjKSAlPiUKICAgIGFkZF9yZWNpcGUobG9naXN0aWNfbGFzc29fcmVjKQoKIyBUdW5lIG1vZGVsOiBzcGVjaWZ5IGdyaWQgb2YgcGFyYW1ldGVycyBhbmQgdHVuZQpwZW5hbHR5X2dyaWQgPC0gZ3JpZF9yZWd1bGFyKAogICAgcGVuYWx0eShyYW5nZSA9IGMoLTUsMykpLCAjIGxvZzEwIHNjYWxlLCBsYW1iZGEgc2VxIDEwXi01IHRvIDEwXjMKICAgIGxldmVscyA9IDEwMAopCgp0dW5lX291dHB1dCA8LSB0dW5lX2dyaWQoCiAgICBsb2dfbGFzc29fd2YsCiAgICByZXNhbXBsZXMgPSBkYXRhX2N2LAogICAgbWV0cmljcyA9IG1ldHJpY19zZXQocm9jX2F1YywgYWNjdXJhY3kpLAogICAgZ3JpZCA9IHBlbmFsdHlfZ3JpZAopCmBgYAoKIyMjIFNlbGVjdCBhbmQgRml0IEJlc3QgTEFTU08gTW9kZWwKCmBgYHtyfQphdXRvcGxvdCh0dW5lX291dHB1dCkgKyB0aGVtZV9jbGFzc2ljKCkKYGBgCgoKYGBge3J9CiMgU2VsZWN0ICJiZXN0IiBwZW5hbHR5IGJ5IG9uZSBzdGFuZGFyZCBlcnJvcgpiZXN0X3NlX3BlbmFsdHkgPC0gc2VsZWN0X2J5X29uZV9zdGRfZXJyKHR1bmVfb3V0cHV0LCBtZXRyaWMgPSAicm9jX2F1YyIsIGRlc2MocGVuYWx0eSkpCgojIERlZmluZSB3b3JrZmxvdyB3aXRoICJiZXN0IiBwZW5hbHR5IHZhbHVlCmZpbmFsX3dmIDwtIGZpbmFsaXplX3dvcmtmbG93KGxvZ19sYXNzb193ZiwgYmVzdF9zZV9wZW5hbHR5KQoKIyBVc2UgZmluYWxfd2YgdG8gZml0IGZpbmFsIG1vZGVsIHdpdGggImJlc3QiIHBlbmFsdHkgdmFsdWUKZmluYWxfZml0X3NlIDwtIGZpdChmaW5hbF93ZiwgZGF0YSA9IGRpYWJldGVzKQpgYGAKCiMjIyBWYXJpYWJsZSBJbXBvcnRhbmNlCgpgYGB7cn0KZ2xtbmV0X291dHB1dCA8LSBmaW5hbF9maXRfc2UgJT4lIGV4dHJhY3RfZml0X3BhcnNuaXAoKSAlPiUgcGx1Y2soImZpdCIpICMgZ2V0IHRoZSBvcmlnaW5hbCBnbG1uZXQgb3V0cHV0CgpwbG90KGdsbW5ldF9vdXRwdXQsIHh2YXIgPSAibGFtYmRhIiwgbGFiZWwgPSBUUlVFLCBjb2wgPSByYWluYm93KDIwKSkKYGBgCgpMZWFzdCBpbXBvcnRhbnQgdmFyaWFibGVzOgoKYGBge3J9CmZpbmFsX2ZpdF9zZSAlPiUgdGlkeSgpICU+JSBmaWx0ZXIoZXN0aW1hdGUgPT0gMCkgIyBVbmltcG9ydGFudCB2YXJpYWJsZXMKYGBgCgpNb3N0IGltcG9ydGFudCB2YXJpYWJsZXM6CgpgYGB7cn0KZmluYWxfZml0X3NlICU+JSB0aWR5KCkgJT4lIGZpbHRlcihlc3RpbWF0ZSAhPSAwKSAlPiUgCiAgYXJyYW5nZShkZXNjKGFicyhlc3RpbWF0ZSkpKSAjIEltcG9ydGFudCB2YXJpYWJsZXMKYGBgCgojIyMgTW9kZWwgRXZhbHVhdGlvbgoKQWNjdXJhY3kgYW5kIFJPQyBBVUMgbWVhc3VyZXMKCmBgYHtyfQp0dW5lX291dHB1dCAlPiUKICAgIGNvbGxlY3RfbWV0cmljcygpICU+JQogICAgZmlsdGVyKHBlbmFsdHkgPT0gYmVzdF9zZV9wZW5hbHR5ICU+JSBwdWxsKHBlbmFsdHkpKSAjIGdldCBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWVhbiAodmFyaWFiaWxpdHkgYWNyb3NzIG11bHRpcGxlIHNhbXBsZXMgb2YgcG9wJ24pCmBgYAoKTm8gaW5mb3JtYXRpb24gcmF0ZQoKYGBge3J9CjM1MzQ2LygzNTM0NiszNTM0NikKYGBgCgoKIyMjIE1pc2NsYXNzaWZpY2F0aW9uIFBsb3RzCgpgYGB7cn0KbGFzc29fbW9kX291dHB1dCA8LSBwcmVkaWN0KGZpbmFsX2ZpdF9zZSwgbmV3X2RhdGEgPSBkaWFiZXRlcykgJT4lCiAgICBiaW5kX2NvbHMoZGlhYmV0ZXMpCgpwcmVkX2xhYmVscyA8LSBjKCIwIiA9ICJBY3R1YWwgMCIsICIxIiAgPSAiQWN0dWFsIDEiKQoKIyBQbG90cwpnZ3Bsb3QobGFzc29fbW9kX291dHB1dCwgYWVzKHggPSBCTUksIGZpbGwgPSAucHJlZF9jbGFzcykpICsKICBnZW9tX2hpc3RvZ3JhbSgpICsKICBmYWNldF93cmFwKH5EaWFiZXRlc19iaW5hcnkpICsKICBsYWJzKGZpbGwgPSAiUHJlZGljdGlvbnMgZnJvbSBMQVNTTyIsIHRpdGxlID0gIkJNSSBQcmVkaWN0aW9ucyIpICsKICB0aGVtZV9jbGFzc2ljKCkKIApnZ3Bsb3QobGFzc29fbW9kX291dHB1dCwgYWVzKHggPSBIaWdoQlAsIGZpbGwgPSAucHJlZF9jbGFzcykpICsKICBnZW9tX2JhcigpICsKICBmYWNldF93cmFwKH5EaWFiZXRlc19iaW5hcnksIGxhYmVsbGVyID0gbGFiZWxsZXIoRGlhYmV0ZXNfYmluYXJ5ID0gcHJlZF9sYWJlbHMpKSArCiAgbGFicyh5ID0gIkNvdW50IiwgZmlsbCA9ICJQcmVkaWN0aW9ucyBmcm9tIExBU1NPIiwgdGl0bGUgPSAiSGlnaEJQIFByZWRpY3Rpb25zIikgKwogIHRoZW1lX2NsYXNzaWMoKQoKZ2dwbG90KGxhc3NvX21vZF9vdXRwdXQsIGFlcyh4ID0gSGlnaENob2wsIGZpbGwgPSAucHJlZF9jbGFzcykpICsKICBnZW9tX2JhcigpICsKICBmYWNldF93cmFwKH5EaWFiZXRlc19iaW5hcnksIGxhYmVsbGVyID0gbGFiZWxsZXIoRGlhYmV0ZXNfYmluYXJ5ID0gcHJlZF9sYWJlbHMpKSArCiAgbGFicyh5ID0gIkNvdW50IiwgZmlsbCA9ICJQcmVkaWN0aW9ucyBmcm9tIExBU1NPIiwgdGl0bGUgPSAiSGlnaENob2wgUHJlZGljdGlvbnMiKSArCiAgdGhlbWVfY2xhc3NpYygpCgpnZ3Bsb3QobGFzc29fbW9kX291dHB1dCwgYWVzKHggPSBBZ2UsIGZpbGwgPSAucHJlZF9jbGFzcykpICsKICBnZW9tX2JhcigpICsKICBmYWNldF93cmFwKH5EaWFiZXRlc19iaW5hcnksIGxhYmVsbGVyID0gbGFiZWxsZXIoRGlhYmV0ZXNfYmluYXJ5ID0gcHJlZF9sYWJlbHMpKSArCiAgbGFicyh5ID0gIkNvdW50IiwgZmlsbCA9ICJQcmVkaWN0aW9ucyBmcm9tIExBU1NPIiwgdGl0bGUgPSAiQWdlIFByZWRpY3Rpb25zIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQoKZ2dwbG90KGxhc3NvX21vZF9vdXRwdXQsIGFlcyh4ID0gR2VuSGx0aCwgZmlsbCA9IC5wcmVkX2NsYXNzKSkgKwogIGdlb21fYmFyKCkgKwogIGZhY2V0X3dyYXAofkRpYWJldGVzX2JpbmFyeSwgbGFiZWxsZXIgPSBsYWJlbGxlcihEaWFiZXRlc19iaW5hcnkgPSBwcmVkX2xhYmVscykpICsKICBsYWJzKHkgPSAiQ291bnQiLCBmaWxsID0gIlByZWRpY3Rpb25zIGZyb20gTEFTU08iLCB0aXRsZSA9ICJHZW5IbHRoIFByZWRpY3Rpb25zIikgKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCgojIyMgTEFTU08gU3VtbWFyeQogIAogICAgVGhlIGJlc3QgdmFsdWUgb2YgbGFtYmRhLCBjaG9zZW4gYnkgc2VsZWN0aW5nIHRoZSBiZXN0IHBlbmFsdHkgYnkgb25lIHN0YW5kYXJkIGVycm9yLCBpcyAwLjAwMzE5OTI2Ny4gVGhlIGNvZWZmaWNpZW50IHNpZ25zIGFuZCBtYWduaXR1ZGVzIGZvciBvdXIgTEFTU08gbW9kZWwgbWFrZSBzZW5zZSwgYXMgdGhlIG9uZXMgdGhhdCBtb3N0IGdyZWF0bHkgaW1wYWN0IHRoZSBtb2RlbCBhcmUgY29tbW9ubHkga25vd24gcmlzayBmYWN0b3JzIG9yIG1lZGljYWwgY29tcGxpY2F0aW9ucyBvZiBkaWFiZXRlcy4KICAKICAgIFdpdGggdGhlIHNldCBvZiBwZW5hbHR5IHZhbHVlcyB0aGF0IEkgdXNlZCwgNSB2YXJpYWJsZXMgaGFkIHRoZWlyIGNvZWZmaWNpZW50cyBzZXQgZXF1YWwgdG8gMC4gUGh5c0hsdGgsIFNtb2tlciwgRnJ1aXRzLCBBbnlIZWFsdGhjYXJlLCBhbmQgTm9Eb2NiY0Nvc3Qgd2VyZSBub3QgcGFydGljdWxhcmx5IGltcG9ydGFudCBmb3IgcHJlZGljdGluZyB3aGV0aGVyIG9yIG5vdCBhbiBpbmRpdmlkdWFsIGhhcyBkaWFiZXRlcywgYWZ0ZXIgYWNjb3VudGluZyBmb3IgdGhlIG90aGVyIGZlYXR1cmVzIGluIHRoZSBtb2RlbC4gVGhlIDUgdmFyaWFibGVzIHdpdGggdGhlIGdyZWF0ZXN0IGltcG9ydGFuY2Ugd2VyZSBHZW5IbHRoLCBIaWdoQlAsIEJNSSwgYW5kIEhpZ2hDaG9sLCB3aGljaCBjb3JyZWxhdGVzIHdpdGggdGhlIGJpZ2dlc3QgcmlzayBmYWN0b3JzL2NvbXBsaWNhdGlvbnMgb2YgZGlhYmV0ZXMuIE9mIHRoZSB1bmltcG9ydGFudCB2YXJpYWJsZXMsIHdlIHdlcmUgbW9zdCBzdXJwcmlzZWQgYnkgdGhlIHNtb2tpbmcgdmFyaWFibGUsIHNpbmNlIHRoZSBDREMgc2F5cyBzbW9rZXJzIGhhdmUgYSBncmVhdGVyIHJpc2sgb2YgZGV2ZWxvcGluZyB0eXBlIDIgZGlhYmV0ZXMgdGhhbiBub24gc21va2Vycy4KICAKICAgIFRoZSBtb2RlbCdzIGFjY3VyYWN5ICg3NC45JSkgYW5kIHJvY19hdWMgKDgyLjUlKSBpcyBoaWdoZXIgdGhhbiB0aGUgTklSIG9mIDUwJSwgc28gb3VyIGFjY3VyYWN5IGlzbuKAmXQgcHVyZWx5IGRyaXZlbiBieSBhbiBvdmVyd2hlbG1pbmcgbWFqb3JpdHkgaW4gb25lIHBhcnRpY3VsYXIgY2xhc3MuCgogICAgV2l0aCB0aGUgc2V0IG9mIHBlbmFsdHkgdmFsdWVzIHRoYXQgSSB1c2VkLCA1IHZhcmlhYmxlcyBoYWQgdGhlaXIgY29lZmZpY2llbnRzIHNldCBlcXVhbCB0byAwLiBQaHlzSGx0aCwgU21va2VyLCBGcnVpdHMsIEFueUhlYWx0aGNhcmUsIGFuZCBOb0RvY2JjQ29zdCB3ZXJlIG5vdCB0aGF0IGltcG9ydGFudCBpbiBwcmVkaWN0aW5nIHdoZXRoZXIgb3Igbm90IGFuIGluZGl2aWR1YWwgaGFzIGRpYWJldGVzLCBhZnRlciBhY2NvdW50aW5nIGZvciB0aGUgb3RoZXIgZmVhdHVyZXMgaW4gdGhlIG1vZGVsLiBUaG9zZSBvZiBoaWdoZXN0IGltcG9ydGFuY2UgaW5jbHVkZWQgdmFyaWFibGVzIGxpa2UgR2VuSGx0aCwgSGlnaEJQLCBCTUksIGFuZCBIaWdoQ2hvbCwgYW5kIEFnZS4gT2YgdGhlIHVuaW1wb3J0YW50IHZhcmlhYmxlcywgd2Ugd2VyZSBtb3N0IHN1cnByaXNlZCBieSB0aGUgc21va2luZyB2YXJpYWJsZSwgc2luY2UgdGhlIENEQyBzYXlzIHNtb2tlcnMgaGF2ZSBhIGdyZWF0ZXIgcmlzayBvZiBkZXZlbG9waW5nIHR5cGUgMiBkaWFiZXRlcyB0aGFuIG5vbiBzbW9rZXJzLgogICAgCiAgICBUaGlzIExBU1NPIG1vZGVsIGlzIGdvb2QgYXQgcHJlZGljdGluZyB2YXJpYWJsZXMgZm9yIHJpc2sgZmFjdG9ycyBvZiB0aGUgb3V0Y29tZSwgYnV0IG5vdCBhcyBnb29kIGF0IHByZWRpY3RpbmcgdmFyaWFibGUgdmFsdWVzIHRoYXQgYXJlIGxlc3MgY29tbW9uIHdpdGggdGhhdCBvdXRjb21lLiBGb3IgZXhhbXBsZSwgaXQgd2FzIGFibGUgdG8gcHJlZGljdCBzb21lb25lIGFzIGhhdmluZyBkaWFiZXRlcyAoMSkgZm9yIGluZGl2aWR1YWxzIHdpdGggaGlnaCBibG9vZCBwcmVzc3VyZSAoSGlnaEJQIDEpLCBidXQgaGFkIGEgaGFyZGVyIHRpbWUgcHJlZGljdGluZyBubyBoaWdoIGJsb29kIHByZXNzdXJlIChIaWdoQlAgMCkuCgoKXFwKCgojIyBLLW5lYXJlc3QgbmVpZ2hib3JzCgojIyMgRml0IEtOTiBNb2RlbAoKYGBge3J9Cmtubl9zcGVjIDwtIG5lYXJlc3RfbmVpZ2hib3IoKSAlPiUgIyBHZW5lcmFsIG1vZGVsIHR5cGUKICAgIHNldF9hcmdzKG5laWdoYm9ycyA9IHR1bmUoKSkgJT4lICMgdHVuaW5nIHBhcmFtZXRlcgogICAgc2V0X2VuZ2luZShlbmdpbmUgPSAia2tubiIpICU+JSAjIEVuZ2luZSBuYW1lCiAgICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKQoKZGlhYmV0ZXNfY3YgPC0gdmZvbGRfY3YoZGlhYmV0ZXMsIHYgPSA2KSAjIFN1cHBseSBkYXRhc2V0IGFuZCAjIG9mIGZvbGRzCgpkaWFiZXRlc19yZWMgPC0gcmVjaXBlKERpYWJldGVzX2JpbmFyeSB+IEhpZ2hCUCArIEJNSSArIEhpZ2hDaG9sICsgRGlmZldhbGsgKyBHZW5IbHRoICsgSGVhcnREaXNlYXNlb3JBdHRhY2sgKyBJbmNvbWUgKyBDaG9sQ2hlY2sgKyBIdnlBbGNvaG9sQ29uc3VtcCArIEFnZSArIEVkdWNhdGlvbiArIFNleCArIFN0cm9rZSArIFBoeXNBY3Rpdml0eSArIFZlZ2dpZXMgKyBNZW50SGx0aCwgZGF0YSA9IGRpYWJldGVzKSAlPiUgCiAgICBzdGVwX2R1bW15KGFsbF9ub21pbmFsX3ByZWRpY3RvcnMoKSkgJT4lCiAgICBzdGVwX25vcm1hbGl6ZShhbGxfbnVtZXJpY19wcmVkaWN0b3JzKCkpCgpkaWFiZXRlc193ZiA8LSB3b3JrZmxvdygpICU+JQogICAgYWRkX21vZGVsKGtubl9zcGVjKSAlPiUgIyBNb2RlbCBzcGVjaWZpY2F0aW9uIG9iamVjdAogICAgYWRkX3JlY2lwZShkaWFiZXRlc19yZWMpICMgRGF0YSBwcmVwcm9jZXNzaW5nIHJlY2lwZSBvYmplY3QKCnR1bmluZ19wYXJhbV9ncmlkIDwtIGdyaWRfcmVndWxhcigKICAgIG5laWdoYm9ycyhyYW5nZSA9IGMoMSwgMTAwKSksICMgbWluIGFuZCBtYXggb2YgdmFsdWVzIGZvciBuZWlnaGJvcnMKICAgIGxldmVscyA9IDUgIyBudW1iZXIgb2YgbmVpZ2hib3JzIHZhbHVlcwopCgprbm5fdHVuZV9vdXRwdXQgPC0gdHVuZV9ncmlkKAogICAgZGlhYmV0ZXNfd2YsCiAgICByZXNhbXBsZXMgPSBkaWFiZXRlc19jdiwKICAgIG1ldHJpY3MgPSBtZXRyaWNfc2V0KHJvY19hdWMsIGFjY3VyYWN5KSwKICAgIGdyaWQgPSB0dW5pbmdfcGFyYW1fZ3JpZAopCgpgYGAKCiMjIyBTZWxlY3QgYW5kIEZpdCBCZXN0IE51bWJlciBvZiBOZWlnaGJvcnMKCmBgYHtyfQphdXRvcGxvdChrbm5fdHVuZV9vdXRwdXQpICsgdGhlbWVfY2xhc3NpYygpCmBgYAoKYGBge3J9CiMjIENob29zZSBuZWlnaGJvcnMgdmFsdWUgdGhhdCBsZWFkcyB0byB0aGUgaGlnaGVzdCBuZWlnaGJvcnMgd2l0aGluIDEgc3RkLiBlcnIuCmtubl90dW5lX291dHB1dCAlPiUgCiAgICBzZWxlY3RfYnlfb25lX3N0ZF9lcnIobWV0cmljID0gInJvY19hdWMiLCBkZXNjKG5laWdoYm9ycykpICMjIFRoZSBkZXNjKG5laWdoYm9ycykgc29ydHMgdGhlIGRhdGEgZnJvbSBoaWdoZXN0IHRvIGxvd2VzdCAjIG9mIG5laWdoYm9ycyAobW9zdCBzaW1wbGUgLT4gbW9zdCBjb21wbGV4KQprbm5fdHVuZV9vdXRwdXQgJT4lIAogICAgc2VsZWN0X2J5X29uZV9zdGRfZXJyKG1ldHJpYyA9ICJhY2N1cmFjeSIsIGRlc2MobmVpZ2hib3JzKSkKYGBgCgoKYGBge3J9CmJlc3Rfc2VfbmVpZ2hib3JzIDwtIHNlbGVjdF9ieV9vbmVfc3RkX2Vycihrbm5fdHVuZV9vdXRwdXQsIG1ldHJpYyA9ICJyb2NfYXVjIiwgZGVzYyhuZWlnaGJvcnMpKQpmaW5hbF93ZiA8LSBmaW5hbGl6ZV93b3JrZmxvdyhkaWFiZXRlc193ZiwgYmVzdF9zZV9uZWlnaGJvcnMpIApmaW5hbF9maXQgPC0gZml0KGZpbmFsX3dmLCBkYXRhID0gZGlhYmV0ZXMpCmBgYAogIAojIyMgTW9kZWwgRXZhbHVhdGlvbgoKYGBge3J9CiMgU2hvdyBldmFsdWF0aW9uIG1ldHJpY3MgZm9yIGRpZmZlcmVudCB2YWx1ZXMgb2YgbmVpZ2hib3JzLCBvcmRlcmVkCmtubl90dW5lX291dHB1dCAlPiUgc2hvd19iZXN0KG1ldHJpYyA9ICJyb2NfYXVjIikKa25uX3R1bmVfb3V0cHV0ICU+JSBzaG93X2Jlc3QobWV0cmljID0gImFjY3VyYWN5IikKYGBgCgojIyMgTWlzY2xhc3NpZmljYXRpb24gUGxvdHMKCmBgYHtyfQojIFVzZSB0aGUgYmVzdCBtb2RlbCB0byBtYWtlIHByZWRpY3Rpb25zCmtubl9tb2Rfb3V0IDwtIHByZWRpY3QoZmluYWxfZml0LCBuZXdfZGF0YSA9IGRpYWJldGVzKSAlPiUKICAgIGJpbmRfY29scyhkaWFiZXRlcykKCnByZWRfbGFiZWxzIDwtIGMoIjAiID0gIkFjdHVhbCAwIiwgIjEiICA9ICJBY3R1YWwgMSIpCgojIFBsb3RzCmdncGxvdChrbm5fbW9kX291dCwgYWVzKHggPSBCTUksIGZpbGwgPSAucHJlZF9jbGFzcykpICsKICBnZW9tX2hpc3RvZ3JhbSgpICsKICBsYWJzKHkgPSAiQk1JIiwgZmlsbCA9ICJQcmVkaWN0aW9ucyBmcm9tIEtOTiIpICsKICBmYWNldF93cmFwKH5EaWFiZXRlc19iaW5hcnkpICsKICB0aGVtZV9jbGFzc2ljKCkKIApnZ3Bsb3Qoa25uX21vZF9vdXQsIGFlcyh4ID0gSGlnaEJQLCBmaWxsID0gLnByZWRfY2xhc3MpKSArCiAgZ2VvbV9iYXIoKSArCiAgZmFjZXRfd3JhcCh+RGlhYmV0ZXNfYmluYXJ5LCBsYWJlbGxlciA9IGxhYmVsbGVyKERpYWJldGVzX2JpbmFyeSA9IHByZWRfbGFiZWxzKSkgKwogIGxhYnMoeSA9ICJDb3VudCIsIGZpbGwgPSAiUHJlZGljdGlvbnMgZnJvbSBLTk4iKSArCiAgdGhlbWVfY2xhc3NpYygpCgpnZ3Bsb3Qoa25uX21vZF9vdXQsIGFlcyh4ID0gSGlnaENob2wsIGZpbGwgPSAucHJlZF9jbGFzcykpICsKICBnZW9tX2JhcigpICsKICBmYWNldF93cmFwKH5EaWFiZXRlc19iaW5hcnksIGxhYmVsbGVyID0gbGFiZWxsZXIoRGlhYmV0ZXNfYmluYXJ5ID0gcHJlZF9sYWJlbHMpKSArCiAgbGFicyh5ID0gIkNvdW50IiwgZmlsbCA9ICJQcmVkaWN0aW9ucyBmcm9tIEtOTiIpICsKICB0aGVtZV9jbGFzc2ljKCkKCmdncGxvdChrbm5fbW9kX291dCwgYWVzKHggPSBBZ2UsIGZpbGwgPSAucHJlZF9jbGFzcykpICsKICBnZW9tX2JhcigpICsKICBmYWNldF93cmFwKH5EaWFiZXRlc19iaW5hcnksIGxhYmVsbGVyID0gbGFiZWxsZXIoRGlhYmV0ZXNfYmluYXJ5ID0gcHJlZF9sYWJlbHMpKSArCiAgbGFicyh5ID0gIkNvdW50IiwgZmlsbCA9ICJQcmVkaWN0aW9ucyBmcm9tIEtOTiIpICsKICB0aGVtZV9jbGFzc2ljKCkKCmdncGxvdChrbm5fbW9kX291dCwgYWVzKHggPSBHZW5IbHRoLCBmaWxsID0gLnByZWRfY2xhc3MpKSArCiAgZ2VvbV9iYXIoKSArCiAgZmFjZXRfd3JhcCh+RGlhYmV0ZXNfYmluYXJ5LCBsYWJlbGxlciA9IGxhYmVsbGVyKERpYWJldGVzX2JpbmFyeSA9IHByZWRfbGFiZWxzKSkgKwogIGxhYnMoeSA9ICJDb3VudCIsIGZpbGwgPSAiUHJlZGljdGlvbnMgZnJvbSBLTk4iKSArCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKCmBgYHtyfQpjb25mX21hdCggIyBjb25mdXNpb24gbWF0cml4CiAgICBkYXRhID0ga25uX21vZF9vdXQsCiAgICB0cnV0aCA9IERpYWJldGVzX2JpbmFyeSwKICAgIGVzdGltYXRlID0gLnByZWRfY2xhc3MKKQpgYGAKCgojIyMgS05OIFN1bW1hcnkKCiAgICBUaGUgYmVzdCB2YWx1ZSBmb3IgdGhlIG51bWJlciBvZiBuZWlnaGJvcnMgd291bGQgYmUgMTAwLCBjaG9zZW4gYnkgc2VsZWN0aW5nIHRoZSBiZXN0IG51bWJlciBvZiBuZWlnaGJvcnMgYnkgb25lIHN0YW5kYXJkIGVycm9yIHNvIGFzIHRvIGFjaGlldmUgdGhlIGJlc3QgbW9kZWwgd2l0aCB0aGUgbGVhc3QgY2hhbmNlIG9mIGJlaW5nIG92ZXJmaXQuIFNpbWlsYXJseSB0byB0aGUgTEFTU08gbW9kZWwsIG15IEtOTiBtb2RlbCBpcyBnb29kIGF0IHByZWRpY3RpbmcgZGlhYmV0ZXMgd2hlcmUgdGhlIHJpc2sgZmFjdG9yIGlzIGluIGxpbmUgd2l0aCB0aGUgb3V0Y29tZSwgeWV0IHN0cnVnZ2xlZCBtb3JlIHdoZW4gdGhlIHJpc2sgZmFjdG9yIGNvbnRyYWRpY3RlZCB0aGUgb3V0Y29tZSB2YXJpYWJsZS4gRm9yIGV4YW1wbGUsIHdoZW4gSGlnaENob2wgPSAxIGFuZCBkaWFiZXRlcyB3YXMgbm90IHByZXNlbnQsIHRoZSBtb2RlbCB0ZW5kZWQgdG8gaGF2ZSBhcm91bmQgYSA2MCUgYWNjdXJhY3kgcmF0ZSwgY29udHJhc3RlZCB3aXRoIGl0cyBoaWdoZXIgYWNjdXJhY3kgd2hlbiBIaWdoQ2hvbCBhbmQgZGlhYmV0ZXMgd2VyZSBib3RoIHByZXNlbnQuIAoKICAgIFdpdGggMTAwIG5laWdoYm9ycyBmb3Igb3VyIG1vZGVsIHdlIGhhdmUgYW4gYWNjdXJhY3kgb2YgNzMuNiUgKHN0YW5kYXJkIGVycm9yOiAwLjAwMjUpIGFuZCBhbiByb2NfYXVjIG9mIDgwLjglIChzdGFuZGFyZCBlcnJvcjogMC4wMDE4KS4KCgpcXAoKCiMjIERlY2lzaW9uIFRyZWUKCiMjIyBGaXQgRGVjaXNpb24gVHJlZQoKYGBge3J9CmN0X3NwZWMgPC0gZGVjaXNpb25fdHJlZSgpICU+JQogIHNldF9lbmdpbmUoZW5naW5lID0gInJwYXJ0IikgJT4lCiAgc2V0X2FyZ3MoCiAgICAgIGNvc3RfY29tcGxleGl0eSA9IHR1bmUoKSwKICAgICAgbWluX24gPSAyLAogICAgICB0cmVlX2RlcHRoID0gTlVMTAogICAgKSAlPiUKICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKQoKCmRhdGFfY3YgPC0gdmZvbGRfY3YoZGlhYmV0ZXMsIHYgPSA2KQoKZGF0YV9yZWMgPC0gcmVjaXBlKERpYWJldGVzX2JpbmFyeSB+IC4sIGRhdGEgPSBkaWFiZXRlcykKCgpkYXRhX3dmIDwtIHdvcmtmbG93KCkgJT4lIAogIGFkZF9tb2RlbChjdF9zcGVjKSAlPiUKICBhZGRfcmVjaXBlKGRhdGFfcmVjKQoKcGFyYW1fZ3JpZCA8LSBncmlkX3JlZ3VsYXIoCiAgY29zdF9jb21wbGV4aXR5KHJhbmdlID0gYygtNSwgLTEpKSwKICBsZXZlbHMgPSAxMCkKCnR1bmVfcmVzIDwtIHR1bmVfZ3JpZCgKICBkYXRhX3dmLCAKICByZXNhbXBsZXMgPSBkYXRhX2N2LCAKICBncmlkID0gcGFyYW1fZ3JpZCwgCiAgbWV0cmljcyA9IG1ldHJpY19zZXQoYWNjdXJhY3ksIHJvY19hdWMpIAopCmBgYAoKIyMjIFNlbGVjdCBhbmQgRml0IEJlc3QgVHJlZQoKYGBge3J9CmF1dG9wbG90KHR1bmVfcmVzKSArIHRoZW1lX2NsYXNzaWMoKQpgYGAKCmBgYHtyfQpiZXN0X2NvbXBsZXhpdHkgPC0gc2VsZWN0X2J5X29uZV9zdGRfZXJyKHR1bmVfcmVzLCBtZXRyaWMgPSAncm9jX2F1YycsIGRlc2MoY29zdF9jb21wbGV4aXR5KSkKZGF0YV93Zl9maW5hbCA8LSBmaW5hbGl6ZV93b3JrZmxvdyhkYXRhX3dmLCBiZXN0X2NvbXBsZXhpdHkpCgpmaW5hbF9maXQgPC0gZml0KGRhdGFfd2ZfZmluYWwsIGRhdGEgPSBkaWFiZXRlcykKYGBgCgojIyMgVmlzdWFsaXplIFRyZWUKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojIFBsb3QgdGhlIHRyZWUgKHJwYXJ0LnBsb3QgcGFja2FnZSkKZmluYWxfZml0ICU+JQogICAgZXh0cmFjdF9maXRfZW5naW5lKCkgJT4lCiAgICBycGFydC5wbG90KCkKYGBgCgojIyMgVmFyaWFibGUgSW1wb3J0YW5jZQoKYGBge3J9CiMgVmFyaWFibGUgaW1wb3J0YW5jZSBtZXRyaWNzIAojIFN1bSBvZiB0aGUgZ29vZG5lc3Mgb2Ygc3BsaXQgbWVhc3VyZXMgKGltcHVyaXR5IHJlZHVjdGlvbikgZm9yIGVhY2ggc3BsaXQgZm9yIHdoaWNoIGl0IHdhcyB0aGUgcHJpbWFyeSB2YXJpYWJsZS4KZmluYWxfZml0ICU+JQogICAgZXh0cmFjdF9maXRfZW5naW5lKCkgJT4lCiAgICBwbHVjaygndmFyaWFibGUuaW1wb3J0YW5jZScpCgojIFByZWRpY3Rpb25zIGFuZCBFeHBsb3JpbmcgRXJyb3IKZGlhYmV0ZXNfcHJlZHMgPC0gZGlhYmV0ZXMgJT4lCiAgICBtdXRhdGUoCiAgICAgICAgcHJlZF9wcm9iID0gcHJlZGljdChmaW5hbF9maXQsIG5ld19kYXRhID0gZGlhYmV0ZXMsIHR5cGUgPSAicHJvYiIpLAogICAgICAgIHByZWRfY2xhc3MgPSBwcmVkaWN0KGZpbmFsX2ZpdCwgbmV3X2RhdGEgPSBkaWFiZXRlcywgdHlwZSA9ICJjbGFzcyIpCiAgICApCmRpYWJldGVzX3ByZWRzMSA8LSBkaWFiZXRlc19wcmVkcyAlPiUgbXV0YXRlKG1pc2NsYXNzaWZpZWQgPSBEaWFiZXRlc19iaW5hcnkhPXByZWRfY2xhc3MkLnByZWRfY2xhc3MpCmBgYAoKIyMjIE1vZGVsIEV2YWx1YXRpb24KCmBgYHtyfQp0dW5lX3JlcyAlPiUgCiAgICBzZWxlY3RfYnlfb25lX3N0ZF9lcnIobWV0cmljID0gInJvY19hdWMiLCBkZXNjKGNvc3RfY29tcGxleGl0eSkpCgp0dW5lX3JlcyAlPiUgCiAgICBzZWxlY3RfYnlfb25lX3N0ZF9lcnIobWV0cmljID0gImFjY3VyYWN5IiwgZGVzYyhjb3N0X2NvbXBsZXhpdHkpKQpgYGAKCiMjIyBNaXNjbGFzc2lmaWNhdGlvbiBQbG90cwoKYGBge3J9CmdncGxvdChkaWFiZXRlc19wcmVkczEsIGFlcyh4ID0gbWlzY2xhc3NpZmllZCwgZmlsbCA9IEhpZ2hCUCkpICsKICBnZW9tX2JhcigpICsKICBsYWJzKHRpdGxlID0gIkhpZ2ggQlAgdnMgUHJlZGljdGlvbnMiKQoKZ2dwbG90KGRpYWJldGVzX3ByZWRzMSwgYWVzKHggPSBtaXNjbGFzc2lmaWVkLCBmaWxsID0gR2VuSGx0aCkpICsKICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKwogIGxhYnModGl0bGUgPSAiR2VuSGx0aCB2cyBQcmVkaWN0aW9ucyIsIHkgPSAiJSIpCgpnZ3Bsb3QoZGlhYmV0ZXNfcHJlZHMxLCBhZXMoeCA9IG1pc2NsYXNzaWZpZWQsIGZpbGwgPSBBZ2UpKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICArCiAgbGFicyh0aXRsZSA9ICJBZ2UgdnMgUHJlZGljdGlvbnMiLCB5ID0gIiUiKQoKZ2dwbG90KGRpYWJldGVzX3ByZWRzMSwgYWVzKHkgPSBCTUksIHggPSBtaXNjbGFzc2lmaWVkKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBsYWJzKHRpdGxlID0gIkJNSSB2cyBQcmVkaWN0aW9ucyIpCgpnZ3Bsb3QoZGlhYmV0ZXNfcHJlZHMxLCBhZXMoeCA9IG1pc2NsYXNzaWZpZWQsIGZpbGwgPSBIaWdoQ2hvbCkpICsKICBnZW9tX2JhcigpICsKICBsYWJzKHRpdGxlID0gIkhpZ2hDaG9sIHZzIFByZWRpY3Rpb25zIikKYGBgCgojIyMgRGVjaXNpb24gVHJlZSBTdW1tYXJ5CgogICAgVGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgcmVzdWx0cyBpbiBhIHNsaWdodGx5IGRpZmZlcmVudCB2YXJpYWJsZSBpbXBvcnRhbmNlIG9yZGVyIHRoYW4gTEFTU08sIHdpdGggdGhlIHRvcCA1IGFsc28gY29udGFpbmluZyBIaWdoQlAsIEdlbkhsdGgsIGFuZCBCTUksIGJ1dCBhbHNvIGluY2x1ZGluZyBBZ2UgYW5kIEhpZ2hDaG9sLiBUaGUgbGVhc3QgaW1wb3J0YW50IHZhcmlhYmxlcyB3ZXJlIHZlcnkgZGlmZmVyZW50LCB3aXRoIHRoZSB0b3AgNSBsZWFzdCBpbXBvcnRhbnQgcHJlZGljdG9ycyBpbmNsdWRpbmcgVmVnZ2llcywgRnJ1aXRzLCBBbnlIZWFsdGhjYXJlLCBTdHJva2UsIGFuZCBDaG9sQ2hlY2suCiAgCiAgICBGb3IgbW9zdCBvZiB0aGUgcHJlZGljdG9ycywgdGhlIHJhdGUgb2YgbWlzY2xhc3NpZmljYXRpb24gc2VlbXMgdG8gYmUgZXF1YWwsIGlsbHVzdHJhdGluZyB0aGUgc3RyZW5ndGggb2YgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwuIEZvciBIaWdoQlAgYW5kIEhpZ2hDaG9sLCB0aGUgcHJvcG9ydGlvbiBvZiBoYXZpbmcgZWl0aGVyIHdhcyByZWxhdGl2ZWx5IGVxdWFsIGluIGNhc2VzIHdoZXJlIHRoZSBtb2RlbCB3YXMgYWNjdXJhdGUgYW5kIHdoZXJlIGl0IHdhc27igJl0LiBUaHVzLCB0aGVyZSBkb2VzbuKAmXQgc2VlbSB0byBiZSBhIGJpYXMgcHJlc2VudCB3aGVyZSB0aGUgbW9kZWwgYXR0YWNoZXMgYSBwcmVkaWN0b3Igd2l0aCBoYXZpbmcgZGlhYmV0ZXMuIFRoZSBleGNlcHRpb24gdG8gdGhpcyBpcyB3aGVyZSB0aGVyZSBhcHBlYXJzIHRvIGJlIGEgaGlnaGVyIHJhdGUgb2YgbWlzY2xhc3NpZmljYXRpb24gYW1vbmcgaW5kaXZpZHVhbHMgd2l0aCBhIEdlbkhsdGggcmF0aW5nIG9mIOKAnEF2ZXJhZ2XigJ0gKDMpIGFuZCBvbGRlciBpbmRpdmlkdWFscyB3aXRoaW4gdGhlIEFnZSB2YXJpYWJsZS4KICAKICAgIFRoZSBhY2N1cmFjeSBvZiB0aGlzIGRlY2lzaW9uIHRyZWUgbW9kZWwgaXMgYXJvdW5kIDgwJSAoc3RhbmRhcmQgZXJyb3I6IDAuMDAxKSwgYW5kIHRoZSBST0MgQVVDIGlzIGFyb3VuZCA3NCUgKHN0YW5kYXJkIGVycm9yOiAwLjAwMSkuCgoKIyMgQmFnZ2luZyBhbmQgUmFuZG9tIEZvcmVzdHMKCiMjIyBGaXQgUmFuZG9tIEZvcmVzdCBNb2RlbApgYGB7cn0KIyBNb2RlbCBTcGVjaWZpY2F0aW9uCnJmX3NwZWMgPC0gcmFuZF9mb3Jlc3QoKSAlPiUKICAgIHNldF9lbmdpbmUoZW5naW5lID0gInJhbmdlciIpICU+JSAKICAgIHNldF9hcmdzKAogICAgICAgIG10cnkgPSBOVUxMLCAjIHNpemUgb2YgcmFuZG9tIHN1YnNldCBvZiB2YXJpYWJsZXMKICAgICAgICB0cmVlcyA9IDEwMDAsICMgTnVtYmVyIG9mIHRyZWVzCiAgICAgICAgbWluX24gPSAyLAogICAgICAgIHByb2JhYmlsaXR5ID0gRkFMU0UsICMgRkFMU0U6IGdldCBoYXJkIHByZWRpY3Rpb25zCiAgICAgICAgaW1wb3J0YW5jZSA9ICJpbXB1cml0eSIKICAgICkgJT4lCiAgICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKQoKIyBSZWNpcGUKZGF0YV9yZWMgPC0gcmVjaXBlKERpYWJldGVzX2JpbmFyeSB+IC4sIGRhdGEgPSBkaWFiZXRlcykKCiMgV29ya2Zsb3dzCmRhdGFfd2YgPC0gd29ya2Zsb3coKSAlPiUKICAgIGFkZF9tb2RlbChyZl9zcGVjKSAlPiUKICAgIGFkZF9yZWNpcGUoZGF0YV9yZWMpCgojIE5vIHR1bmVfZ3JpZCgpIG9yIHZmb2xkX2N2KCkKcmZfZml0IDwtIGZpdChkYXRhX3dmLCBkYXRhID0gZGlhYmV0ZXMpCgpgYGAKCiMjIyBWYXJpYWJsZSBJbXBvcnRhbmNlCgpgYGB7cn0KIyBQbG90IG9mIHRoZSB2YXJpYWJsZSBpbXBvcnRhbmNlIGluZm9ybWF0aW9uCnJmX2ZpdCAlPiUgCiAgICBleHRyYWN0X2ZpdF9lbmdpbmUoKSAlPiUgCiAgICB2aXAobnVtX2ZlYXR1cmVzID0gMzApICsgdGhlbWVfY2xhc3NpYygpCgojIEV4dHJhY3QgdGhlIG51bWVyaWNhbCBpbmZvcm1hdGlvbiBvbiB2YXJpYWJsZSBpbXBvcnRhbmNlIGFuZCBkaXNwbGF5IHRoZSBtb3N0IGFuZCBsZWFzdCBpbXBvcnRhbnQgcHJlZGljdG9ycwpyZl92YXJfaW1wIDwtIHJmX2ZpdCAlPiUgCiAgICBleHRyYWN0X2ZpdF9lbmdpbmUoKSAlPiUKICAgIHZpcDo6dmkoKQpoZWFkKHJmX3Zhcl9pbXApCnRhaWwocmZfdmFyX2ltcCkKYGBgCgojIyMgTW9kZWwgRXZhbHVhdGlvbgoKYGBge3J9CnJmX2ZpdApgYGAKCgojIyMgTWlzY2xhc3NpZmljYXRpb24gUGxvdHMKCmBgYHtyfQojIE9PQiBjb25mdXNpb24gbWF0cml4CnJmX291dHB1dCA8LSBkaWFiZXRlcyAlPiUKICAgIG11dGF0ZShPT0JfcHJlZF9kaWFiZXRlcyA9IHJmX2ZpdCAlPiUgZXh0cmFjdF9maXRfZW5naW5lKCkgJT4lIHBsdWNrKCJwcmVkaWN0aW9ucyIpKSAjIGV4dHJhY3RzIE9PQiBwcmVkaWN0aW9ucwoKY29uZl9tYXQoICMgY29uZnVzaW9uIG1hdHJpeAogICAgZGF0YSA9IHJmX291dHB1dCwKICAgIHRydXRoID0gRGlhYmV0ZXNfYmluYXJ5LAogICAgZXN0aW1hdGUgPSBPT0JfcHJlZF9kaWFiZXRlcwopCgpyZl9vdXRwdXQgPC0gcmZfb3V0cHV0ICU+JQogICAgbXV0YXRlKGlzX21pc2NsYXNzID0gRGlhYmV0ZXNfYmluYXJ5IT1PT0JfcHJlZF9kaWFiZXRlcykKIyBQbG90cwpnZ3Bsb3QocmZfb3V0cHV0LCBhZXMoeCA9IGlzX21pc2NsYXNzLCB5ID0gQk1JKSkgKwogIGdlb21fYm94cGxvdCgpICsgCiAgbGFicyh0aXRsZSA9ICJCTUkgdnMgUHJlZGljdGlvbiBNaXNjbGFzc2lmaWNhdGlvbiIsIHggPSAibWlzY2xhc3NpZmllZCIpCgpnZ3Bsb3QocmZfb3V0cHV0LCBhZXMoeCA9IGlzX21pc2NsYXNzLCBmaWxsID0gSGlnaEJQKSkgKwogIGdlb21fYmFyKCkgKwogIGxhYnModGl0bGUgPSAiSGlnaEJQIHZzIFByZWRpY3Rpb24gTWlzY2xhc3NpZmljYXRpb24iLCB4ID0gIm1pc2NsYXNzaWZpZWQiKQoKZ2dwbG90KHJmX291dHB1dCwgYWVzKHggPSBpc19taXNjbGFzcywgZmlsbCA9IEhpZ2hDaG9sKSkgKwogIGdlb21fYmFyKCkgKwogIGxhYnModGl0bGUgPSAiSGlnaENob2wgdnMgUHJlZGljdGlvbiBNaXNjbGFzc2lmaWNhdGlvbiIsIHggPSAibWlzY2xhc3NpZmllZCIpCgpnZ3Bsb3QocmZfb3V0cHV0LCBhZXMoeCA9IGlzX21pc2NsYXNzLCBmaWxsID0gR2VuSGx0aCkpICsKICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKwogIGxhYnModGl0bGUgPSAiR2VuSGx0aCB2cyBQcmVkaWN0aW9uIE1pc2NsYXNzaWZpY2F0aW9uIiwgeSA9ICIlIiwgeCA9ICJtaXNjbGFzc2lmaWVkIikKCmdncGxvdChyZl9vdXRwdXQsIGFlcyh4ID0gaXNfbWlzY2xhc3MsIGZpbGwgPSBBZ2UpKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsKICBsYWJzKHRpdGxlID0gIkFnZSB2cyBQcmVkaWN0aW9uIE1pc2NsYXNzaWZpY2F0aW9uIiwgeSA9ICIlIiwgeCA9ICJtaXNjbGFzc2lmaWVkIikKYGBgCgojIyMgQmFnZ2luZyBhbmQgUmFuZG9tIEZvcmVzdCBTdW1tYXJ5CgogICAgVmFyaWFibGUgaW1wb3J0YW5jZSBmb3IgYmFnZ2luZyBhbmQgcmFuZG9tIGZvcmVzdCBpcyB2ZXJ5IHNpbWlsYXIgdG8gdGhhdCBvZiB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCwgZXhjZXB0IHRoYXQgdGhlIEluY29tZSB2YXJpYWJsZSBpcyBpbiB0aGUgdG9wIDUgbW9yZSBpbXBvcnRhbnQgdmFyaWFibGVzIGluc3RlYWQgb2YgSGlnaENob2wuIFRoZSBsZWFzdCBpbXBvcnRhbnQgdmFyaWFibGVzIGFsc28gcmVtYWluZWQgbW9zdGx5IHRoZSBzYW1lLCBleGNlcHQgSHZ5QWxjb2hvbENvbnN1bXB0aW9uIHJlcGxhY2VkIHRoZSBGcnVpdHMgdmFyaWFibGUuCiAgICAKICAgIFNpbWlsYXJseSB0byBEZWNpc2lvbiB0cmVlcywgZm9yIG1vc3Qgb2YgdGhlIHByZWRpY3RvcnMgdGhlIHJhdGUgb2YgbWlzY2xhc3NpZmljYXRpb24gc2VlbXMgdG8gYmUgZXF1YWwsIGlsbHVzdHJhdGluZyB0aGUgc3RyZW5ndGggb2YgdGhlIGJhZ2dpbmcgYW5kIHJhbmRvbSBmb3Jlc3QgbW9kZWwuIFRoZSBleGNlcHRpb24gaXMgYWdhaW4gd2l0aCB0aGUgR2VuSGx0aCBjYXRlZ29yeSwgd2hlcmUgdGhlcmUgYXBwZWFycyB0byBiZSBhIGhpZ2hlciByYXRlIG9mIG1pc2NsYXNzaWZpY2F0aW9uIGFtb25nIGluZGl2aWR1YWxzIHdpdGggYSBHZW5IbHRoIHJhdGluZyBvZiDigJxBdmVyYWdl4oCdICgzKSwgYW5kIHRoZSBvbGRlciBhZ2UgY2F0ZWdvcmllcyB3aXRoaW4gdGhlIEFnZSB2YXJpYWJsZS4KICAgIAogICAgVGhlIG91dC1vZi1iYWcgcHJlZGljdGlvbiBlcnJvciByYXRlIGZvciB0aGlzIG1vZGVsIGlzIDI1LjI2JSwgd2hpY2ggbWFrZXMgdGhlIGFjY3VyYWN5IHJhdGUgYXJvdW5kIDc1JS4gVXNpbmcgdGhlIGNvbmZ1c2lvbiBtYXRyaXgsIEkgd2FzIGFibGUgdG8gY2FsdWNsYXRlIHRoZSBzcGVjaWZpY2l0eSBhbmQgc2Vuc2l0aXZpdHkgb2YgdGhlIG1vZGVsLCB3aGljaCB3ZXJlIDc5JSBhbmQgNzUlIHJlc3BlY3RpdmVseS4KCiMjIFN1cGVydmlzZWQgTGVhcm5pbmcgQW5zd2VyCgogICAgVGhlIG1vc3QgY29tbW9uIGltcG9ydGFudCB2YXJpYWJsZXMgZm9yIHRoZSBzdXBlcnZpc2VkIGxlYXJuaW5nIG1vZGVscyBpbmNsdWRlZCBIaWdoQlAsIEJNSSwgR2VuSGx0aCwgYW5kIEFnZSwgYW5kIHRoZSBtb3N0IGNvbW1vbiBsZWFzdCBpbXBvcnRhbnQgdmFyaWFibGVzIGluY2x1ZGVkIFN0cm9rZSwgVmVnZ2llcywgQ2hvbENoZWNrLCBBbnlIZWFsdGhjYXJlLCBGcnVpdHMuIFRoZXNlIHJlc3VsdHMgY29tZSBmcm9tIHN1cGVydmlzZWQgbGVhcm5pbmcgbW9kZWxzIHRoYXQgd2VyZSBiZXR3ZWVuIDc0JSBhbmQgODMlIGFjY3VyYXRlLgoKCiMgVW5zdXBlcnZpc2VkIExlYXJuaW5nIFF1ZXN0aW9uCgo+IFdoYXQgdHlwZXMgb2YgZGlhZ25vc2VzIG1pZ2h0IGEgZG9jdG9yIGNvbWUgdG8gd2hlbiBjb25zaWRlcmluZyB2YXJpYWJsZXMgc3VjaCBhcyBIaWdoQlAsIEhpZ2hDaG9sLCBhbmQgQk1JPwoKCiMjIEstbWVhbnMgQ2x1c3RlcmluZwoKYGBge3J9CmRpYWJldGVzX2NsdXN0IDwtIGRpYWJldGVzICU+JSAKICBzZWxlY3QoSGlnaEJQLCBIaWdoQ2hvbCwgQk1JLCBEaWFiZXRlc19iaW5hcnkpICU+JSAKICBzbGljZV9zYW1wbGUobiA9IDEwMCkKCgojIENob29zaW5nIGFuIGFwcHJvcHJpYXRlIG51bWJlciBvZiBjbHVzdGVycwojIENyZWF0ZSBzdG9yYWdlIHZlY3RvciBmb3IgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMKdG90X3djX3NzIDwtIHJlcCgwLCAyMCkgCgojIExvb3AKZm9yIChrIGluIDE6MjApIHsKICAgICMgUGVyZm9ybSBjbHVzdGVyaW5nCiAgICBwYW1fb3V0IDwtIHBhbShkYWlzeShkaWFiZXRlc19jbHVzdCksIGsgPSBrKQoKICAgICMgU3RvcmUgdGhlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzCiAgICB0b3Rfd2Nfc3Nbay0xXSA8LSBzdW0ocGFtX291dCRjbHVzaW5mb1ssImF2X2Rpc3MiXSpwYW1fb3V0JGNsdXNpbmZvWywic2l6ZSJdKQp9CgpwbG90KDE6MjAsIHRvdF93Y19zcywgeGxhYiA9ICJOdW1iZXIgb2YgY2x1c3RlcnMiLCB5bGFiID0gIlRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzIikKYGBgCgoKIyMgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcKYGBge3J9CiMgUmFuZG9tIHN1YnNhbXBsZSBvZiA1MCBwZW5ndWlucwpzZXQuc2VlZCgyNTMpCgoKIyBDb21wdXRlIGEgZGlzdGFuY2UgbWF0cml4IG9uIHRoZSBzY2FsZWQgZGF0YQpkaXN0X21hdF9zY2FsZWQgPC0gZGlzdChkYWlzeShkaWFiZXRlc19jbHVzdCAlPiUgc2VsZWN0KEJNSSwgSGlnaEJQLCBIaWdoQ2hvbCkpKQoKIyBUaGUgKHNjYWxlZCkgZGlzdGFuY2UgbWF0cml4IGlzIHRoZSBpbnB1dCB0byBoY2x1c3QoKQojIFRoZSBtZXRob2QgYXJndW1lbnQgaW5kaWNhdGVzIHRoZSBsaW5rYWdlIHR5cGUKaGNfY29tcGxldGUgPC0gaGNsdXN0KGRpc3RfbWF0X3NjYWxlZCwgbWV0aG9kID0gImNvbXBsZXRlIikKaGNfc2luZ2xlIDwtIGhjbHVzdChkaXN0X21hdF9zY2FsZWQsIG1ldGhvZCA9ICJzaW5nbGUiKQpoY19hdmVyYWdlIDwtIGhjbHVzdChkaXN0X21hdF9zY2FsZWQsIG1ldGhvZCA9ICJhdmVyYWdlIikKaGNfY2VudHJvaWQgPC0gaGNsdXN0KGRpc3RfbWF0X3NjYWxlZCwgbWV0aG9kID0gImNlbnRyb2lkIikKCiMgUGxvdCBkZW5kcm9ncmFtcwpwbG90KGhjX2NvbXBsZXRlLCBsYWJlbHMgPSBkaWFiZXRlc19jbHVzdCREaWFiZXRlc19iaW5hcnkpCnBsb3QoaGNfc2luZ2xlLCBsYWJlbHMgPSBkaWFiZXRlc19jbHVzdCREaWFiZXRlc19iaW5hcnkpCnBsb3QoaGNfYXZlcmFnZSwgbGFiZWxzID0gZGlhYmV0ZXNfY2x1c3QkRGlhYmV0ZXNfYmluYXJ5KQpwbG90KGhjX2NlbnRyb2lkLCBsYWJlbHMgPSBkaWFiZXRlc19jbHVzdCREaWFiZXRlc19iaW5hcnkpCgpkaWFiZXRlczQgPC0gZGlhYmV0ZXNfY2x1c3QgJT4lCiAgICBtdXRhdGUoCiAgICAgICAgaGNsdXN0X2hlaWdodDEuNSA9IGZhY3RvcihjdXRyZWUoaGNfY29tcGxldGUsIGggPSAxLjUpKSwgIyBDdXQgYXQgaGVpZ2h0IChoKSAzCiAgICAgICAgaGNsdXN0X251bTMgPSBmYWN0b3IoY3V0cmVlKGhjX2NvbXBsZXRlLCBrID0gMykpICMgQ3V0IGludG8gNiBjbHVzdGVycyAoaykKICAgICkKYGBgCgojIyBDbHVzdGVyIFBsb3RzCgpgYGB7cn0KZ2dwbG90KGRpYWJldGVzNCwgYWVzKHggPSBoY2x1c3RfbnVtMywgeSA9IEJNSSkpICsKICAgIGdlb21fYm94cGxvdCgpICsKICAgIGxhYnMoeCA9ICJDbHVzdGVyIiwgdGl0bGUgPSAiQk1JIHZzIENsdXN0ZXJzIikKCmdncGxvdChkaWFiZXRlczQsIGFlcyh4ID0gaGNsdXN0X251bTMsIGZpbGwgPSBIaWdoQlApKSArCiAgICBnZW9tX2JhcigpICsKICAgIGxhYnMoeCA9ICJDbHVzdGVyIiwgdGl0bGUgPSAiSGlnaEJQIHZzIENsdXN0ZXJzIikKCmdncGxvdChkaWFiZXRlczQsIGFlcyh4ID0gaGNsdXN0X251bTMsIGZpbGwgPSBIaWdoQ2hvbCkpICsKICAgIGdlb21fYmFyKCkgKwogICAgbGFicyh4ID0gIkNsdXN0ZXIiLCB0aXRsZSA9ICJIaWdoQ2hvbCB2cyBDbHVzdGVycyIpCmBgYAoKCiMjIENsdXN0ZXJpbmcgU3VtbWFyeQogICAgCiAgICBUaGUgcG9pbnQgYXQgd2hpY2ggdGhlcmUgYXJlIG5vIGxvbmdlciBtZWFuaW5nZnVsIGRlY3JlYXNlcyBpbiBoZXRlcm9nZW5laXR5IG9jY3VycyBzb21ld2hlcmUgYmV0d2VlbiAzIGFuZCA2IGNsdXN0ZXJzLiBSZW1lbWJlcmluZyBteSBnb2FsIG9mIGZpbmRpbmcgZGlhZ25vc2lzIHR5cGVzLCBJIHdhbnQgdG8gZmF2b3IgYSBmZXdlciBudW1iZXIgb2YgY2x1c3RlcnMgYW5kIGNob3NlIHRvIGNyZWF0ZSBwbG90cyBjb25zaWRlcmluZyAzIGNsdXN0ZXJzLiBUaGVzZSBjb25zaXN0ZWQgb2YgY2x1c3RlciAxLCB3aXRoIGluZGl2aWR1YWxzIHdobyBnZW5lcmFsbHkgaGF2ZSBhIEhpZ2ggQlAsIG1peGVkIGNob2xlc3Rlcm9sIGxldmVscywgYW5kIGEgaGlnaCBCTUksIGNsdXN0ZXIgMiwgd2l0aCBpbmRpdmlkdWFscyB3aG8gZ2VuZXJhbGx5IGhhdmUgbm9uLWhpZ2ggQlAsIGhpZ2ggY2hvbGVzdGVyb2wgbGV2ZWxzLCBhbmQgYSBtZWRpdW0gQk1JLCBhbmQgY2x1c3RlciAzLCB3aXRoIGluZGl2aWR1YWxzIHdobyBnZW5lcmFsbHkgaGF2ZSBub24taGlnaCBCUCwgbG93IGNob2xlc3Rlcm9sIGxldmVscywgYW5kIGxvdyBCTUkuIFRvIGFuc3dlciB0aGUgdW5zdXBlcnZpc2VkIGxlYXJuaW5nIHF1ZXN0aW9uLCBJIHdvdWxkIGh5cG90aGVzaXplIHRoYXQgY2x1c3RlciAxIGNvdWxkIGJlIGNvbnNpZGVyZWQgYSBkaWFnbm9zaXMgb2YgZGlhYmV0ZXMsIGNsdXN0ZXIgMiBjb3VsZCBiZSBjb25zaWRlcmVkIGEgZGlhZ25vc2lzIG9mIHByZWRpYWJldGVzLCBhbmQgY2x1c3RlciAzIGNvdWxkIGJlIGNvbnNpZGVyZWQgYSBkaWFnbm9zaXMgb2Ygbm8gZGlhYmV0ZXMuCgojIENhdXRpb25zIGFuZCBMaW1pdGF0aW9ucwoKICAgIExvb2tpbmcgYmFjayBhdCB0aGVzZSBtb2RlbHMsIEkgd291bGQgdXNlIHRoZW0gYXMgZGlhZ25vc3RpYyB0b29scyB2ZXJ5IGNhdXRpb3VzbHksIGNvbnNpZGVyaW5nIHRoZSBsb3dlciBhY2N1cmFjeSBhbmQgcm9jIGF1YyBtb2RlbCBldmFsdWF0aW9uIG1ldHJpY3MgKGJldHdlZW4gNzQlIGFuZCA4MyUpLiBJdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHByZWRpY3RvcnMgYXJlIG5vdCB0aGUgc2FtZSBhcyBjYXVzZXMgYW5kIHNob3VsZCBub3QgYmUgdHJlYXRlZCBhcyBzbyB3aGVuIGNvbnNpZGVyaW5nIG91ciBtb2RlbC4gRm9yIGxpbWl0YXRpb25zLCBJIG11c3QgY29uc2lkZXIgdGhhdCB0aGUgZGF0YSBjb2xsZWN0aW9uIG1ldGhvZCB3YXMgdGhyb3VnaCBwaG9uZSBzdXJ2ZXlzIGFuZCB0aGVyZWZvcmUgbWF5IGJlIHByb25lIHRvIGJpYXNlcyBjb25zaXN0YW50IHdpdGggc3VjaCBtZXRob2QuIEFub3RoZXIgbGltaXRhdGlvbiB3YXMgdGhlIHRpbWUgY29uc3RyYWludHMgZm9yIHRoaXMgcHJvamVjdCwgaW5jbHVkaW5nIHRoZSBmYWN0IHRoYXQgdGhpcyBkYXRhc2V0IHdhcyBzbyBsYXJnZSB0aGF0IGl0IHRha2VzIHNpZ25pZmljYW50IGNvbXB1dGF0aW9uYWwgdGltZSB0byBydW4gbWFueSBvZiB0aGUgbW9kZWxzLgoK