# 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