Homework 5

For this assignment, we will be working with the file "pokemon.csv", found in /data. The file is from Kaggle: https://www.kaggle.com/abcsds/pokemon.

The Pokémon franchise encompasses video games, TV shows, movies, books, and a card game. This data set was drawn from the video game series and contains statistics about 721 Pokémon, or “pocket monsters.” In Pokémon games, the user plays as a trainer who collects, trades, and battles Pokémon to (a) collect all the Pokémon and (b) become the champion Pokémon trainer.

Each Pokémon has a primary type (some even have secondary types). Based on their type, a Pokémon is strong against some types, and vulnerable to others. (Think rock, paper, scissors.) A Fire-type Pokémon, for example, is vulnerable to Water-type Pokémon, but strong against Grass-type.

Fig 1. Vulpix, a Fire-type fox Pokémon from Generation 1 (also my favorite Pokémon!)
Fig 1. Vulpix, a Fire-type fox Pokémon from Generation 1 (also my favorite Pokémon!)

The goal of this assignment is to build a statistical learning model that can predict the primary type of a Pokémon based on its generation, legendary status, and six battle statistics. This is an example of a classification problem, but these models can also be used for regression problems.

Read in the file and familiarize yourself with the variables using pokemon_codebook.txt.

Exercise 1

Install and load the janitor package. Use its clean_names() function on the Pokémon data, and save the results to work with for the rest of the assignment. What happened to the data? Why do you think clean_names() is useful?

pokemon <- read.csv("/Users/zhaolei/Downloads/homework-5/data/Pokemon.csv")
library(vip)
library(janitor)
library(forcats)
library(tidyverse)
library(tidymodels)
library(ISLR)
library(ISLR2)
library(glmnet)
library(modeldata)
library(ggthemes)
library(naniar) # to assess missing data patterns
library(corrplot) # for a correlation plot
library(patchwork) # for putting plots together
library(rpart.plot)
library(verification)
library(verification)
tidymodels_prefer()
library(ggplot2)
pokemon <-clean_names(pokemon)
head(pokemon)
##   x                  name type_1 type_2 total hp attack defense sp_atk sp_def
## 1 1             Bulbasaur  Grass Poison   318 45     49      49     65     65
## 2 2               Ivysaur  Grass Poison   405 60     62      63     80     80
## 3 3              Venusaur  Grass Poison   525 80     82      83    100    100
## 4 3 VenusaurMega Venusaur  Grass Poison   625 80    100     123    122    120
## 5 4            Charmander   Fire          309 39     52      43     60     50
## 6 5            Charmeleon   Fire          405 58     64      58     80     65
##   speed generation legendary
## 1    45          1     False
## 2    60          1     False
## 3    80          1     False
## 4    80          1     False
## 5    65          1     False
## 6    80          1     False

The clean_names function standardizes the dataset’s column names by removing leading and trailing spaces and replacing special characters with underscores. This ensures consistency among variable names, making them easier to access and work with.

Exercise 2

Using the entire data set, create a bar chart of the outcome variable, type_1.

How many classes of the outcome are there? Are there any Pokémon types with very few Pokémon? If so, which ones?

For this assignment, we’ll handle the rarer classes by grouping them, or “lumping them,” together into an ‘other’ category. Using the forcats package, determine how to do this, and lump all the other levels together except for the top 6 most frequent (which are Bug, Fire, Grass, Normal, Water, and Psychic).

Convert type_1 and legendary to factors.

pokemon%>%
  ggplot(aes(x=type_1))+
  geom_bar()

There are 18 outcomes here, and classes such as Flying, fairy, Ice do include very small number of observations, so we have to lumping them.

pokemon$type_1 <- pokemon %>%
  mutate(type_1 = fct_lump_n(type_1, n = 6, w = NULL, other_level = "Other"))%>%
   pull(type_1)

pokemon %>%
  ggplot(aes(x = type_1)) +
  geom_bar()

# factorization
pokemon$type_1 <- as.factor(pokemon$type_1)
pokemon$legendary <- as.factor(pokemon $legendary)

Exercise 3

Perform an initial split of the data. Stratify by the outcome variable. You can choose a proportion to use. Verify that your training and test sets have the desired number of observations.

Next, use v-fold cross-validation on the training set. Use 5 folds. Stratify the folds by type_1 as well. Hint: Look for a strata argument.

Why do you think doing stratified sampling for cross-validation is useful?

set.seed(0926)


pokemon_split <- initial_split(pokemon, prop = 0.75,strata =   "type_1",)

pokemon_train <- training(pokemon_split)
pokemon_test <- testing(pokemon_split)
pokemon_folds <-vfold_cv(pokemon_train, v = 5, strata = "type_1")

Stratified sampling for cross-validation ensures that the training and testing sets maintain the same proportion of features as the original data, allowing the results to better represent the entire dataset.

Exercise 4

Create a correlation matrix of the training set, using the corrplot package. Note: You can choose how to handle the categorical variables for this plot; justify your decision(s).

What relationships, if any, do you notice?

pokemon_train%>%select(is.numeric)%>%cor()%>%corrplot()

the variable total is strongly and positively related with hp, attack, defense, sp_atk, sp_def, and speed.

Exercise 5

Set up a recipe to predict type_1 with legendary, generation, sp_atk, attack, speed, defense, hp, and sp_def.

  • Dummy-code legendary and generation;

  • Center and scale all predictors.

pokemon_recipe<-recipe(type_1~legendary + generation + sp_atk + attack + speed 
                       +defense + hp + sp_def,data=pokemon_train)%>%
  step_dummy(all_nominal(), -all_outcomes())%>%
  step_scale(all_predictors())%>%
  step_center(all_predictors())

Exercise 6

We’ll be fitting and tuning an elastic net, tuning penalty and mixture (use multinom_reg() with the glmnet engine).

Set up this model and workflow. Create a regular grid for penalty and mixture with 10 levels each; mixture should range from 0 to 1. For this assignment, let penalty range from 0.01 to 3 (this is on the identity_trans() scale; note that you’ll need to specify these values in base 10 otherwise).

nom_model <- multinom_reg(penalty = tune(),
                         mixture = tune()) %>%
  set_engine("glmnet") %>%
  set_mode("classification")
  
# set up the workflow
nom_wkflow <-workflow() %>%
  add_model(nom_model) %>%
  add_recipe(pokemon_recipe)

# create grid
nom_grid <- grid_regular(penalty(range =c(0.01, 3), trans = identity_trans()),
                        
                        mixture(range = c(0,1)),
                        levels = 10)

Exercise 7

Now set up a random forest model and workflow. Use the ranger engine and set importance = "impurity"; we’ll be tuning mtry, trees, and min_n. Using the documentation for rand_forest(), explain in your own words what each of these hyperparameters represent.

Create a regular grid with 8 levels each. You can choose plausible ranges for each hyperparameter. Note that mtry should not be smaller than 1 or larger than 8. Explain why neither of those values would make sense.

What type of model does mtry = 8 represent?

# set up model
randomf_model <- rand_forest(mtry = tune(), trees = tune(), min_n = tune()) %>%
  set_engine("ranger", importance = "impurity") %>%
  set_mode("classification")

# set up workflow
randomf_wkflow <- workflow() %>%
  add_model(randomf_model) %>%
  add_recipe(pokemon_recipe)

randomf_grid <- grid_regular(mtry(range = c(2,7)),
                        trees(range = c(100,1000)),
                        min_n(range = c(2,10)),
                        levels = 8)

mtry represents the number of variables randomly sampled as candidates at each split when building a tree. We need at least one variable, so mtry can’t be less than 1. Since we have at most 8 predictors in the model, mtry can’t be larger than 8. When mtry is set to 8, the model becomes a decision tree that uses all features.

Exercise 8

Fit all models to your folded data using tune_grid().

Note: Tuning your random forest model will take a few minutes to run, anywhere from 5 minutes to 15 minutes and up. Consider running your models outside of the .Rmd, storing the results, and loading them in your .Rmd to minimize time to knit. We’ll go over how to do this in lecture.

Use autoplot() on the results. What do you notice? Do larger or smaller values of penalty and mixture produce better ROC AUC? What about values of min_n, trees, and mtry?

What elastic net model and what random forest model perform the best on your folded data? (What specific values of the hyperparameters resulted in the optimal ROC AUC?)

nom_res <- tune_grid(
  nom_wkflow,
  resamples = pokemon_folds,
  grid = nom_grid,
  control = control_grid(save_pred = TRUE)
)

load("/Users/zhaolei/Downloads/homework-5/tune_random_forest.rda")
autoplot(nom_res)

For this particular dataset and elastic net model, using smaller values for both the penalty and mixture parameters results in better model performance, as indicated by higher ROC AUC values. This suggests that the model benefits from less regularization and a balance that favors Ridge regularization.

collect_metrics(nom_res) %>%
  filter(.metric == "roc_auc") %>%
  select(penalty, mixture, mean, std_err)
## # A tibble: 100 × 4
##    penalty mixture  mean std_err
##      <dbl>   <dbl> <dbl>   <dbl>
##  1   0.01        0 0.682  0.0167
##  2   0.342       0 0.638  0.0157
##  3   0.674       0 0.628  0.0159
##  4   1.01        0 0.623  0.0160
##  5   1.34        0 0.620  0.0158
##  6   1.67        0 0.617  0.0164
##  7   2.00        0 0.615  0.0161
##  8   2.34        0 0.613  0.0161
##  9   2.67        0 0.611  0.0162
## 10   3           0 0.611  0.0162
## # ℹ 90 more rows
select_best(nom_res)
## # A tibble: 1 × 3
##   penalty mixture .config               
##     <dbl>   <dbl> <chr>                 
## 1    0.01   0.667 Preprocessor1_Model061

The best roc_auc value is when penalty = 0.01 and mixture is about 0.556

autoplot(rf_res)

select_best(rf_res)
## # A tibble: 1 × 4
##    mtry trees min_n .config               
##   <int> <int> <int> <chr>                 
## 1     3   742     6 Preprocessor1_Model224

e random forest model, although the results varies a lot, the highest roc_auc value is when selecting 3 predictors , node size64, and 742 trees. ### Exercise 9

Select your optimal random forest modelin terms of roc_auc. Then fit that model to your training set and evaluate its performance on the testing set.

Using the training set:

  • Create a variable importance plot, using vip(). Note that you’ll still need to have set importance = "impurity" when fitting the model to your entire training set in order for this to work.

    • What variables were most useful? Which were least useful? Are these results what you expected, or not?

Using the testing set:

  • Create plots of the different ROC curves, one per level of the outcome variable;

  • Make a heat map of the confusion matrix.

final_wkflow <- finalize_workflow(randomf_wkflow,
                                  select_best(rf_res))
pokemon_fit <- fit(final_wkflow, pokemon_train) # fit the training set

vip(pokemon_fit, importance = "impurity")

predict(pokemon_fit, new_data=pokemon_test, type ="prob")
## # A tibble: 201 × 7
##    .pred_Bug .pred_Fire .pred_Grass .pred_Normal .pred_Psychic .pred_Water
##        <dbl>      <dbl>       <dbl>        <dbl>         <dbl>       <dbl>
##  1   0.00620    0.516        0.0553       0.0294       0.0208       0.126 
##  2   0.0739     0.262        0.0544       0.0337       0.0729       0.158 
##  3   0.0201     0.0509       0.350        0.0233       0.0196       0.291 
##  4   0.0134     0.147        0.0510       0.0988       0.0616       0.109 
##  5   0.0271     0.00460      0.0211       0.471        0.00106      0.0710
##  6   0.0668     0.0148       0.173        0.114        0.1          0.284 
##  7   0.124      0.0710       0.0832       0.0812       0.0580       0.354 
##  8   0.0144     0.0652       0.267        0.0870       0.0302       0.229 
##  9   0.110      0.131        0.188        0.191        0.0207       0.102 
## 10   0.0494     0.00966      0.0192       0.201        0.0412       0.388 
## # ℹ 191 more rows
## # ℹ 1 more variable: .pred_Other <dbl>
augment(pokemon_fit, new_data = pokemon_test) %>%
  roc_curve(type_1, .pred_Bug,.pred_Fire, .pred_Grass, .pred_Normal, .pred_Psychic, .pred_Water, .pred_Other) %>%
  autoplot()

augment(pokemon_fit, new_data= pokemon_test) %>%
  conf_mat(truth = type_1, estimate = .pred_class) %>%
  autoplot(type = "heatmap")

The model is best at predicting Other type, this might because after the lumping, the Other type becomes most prevalent in the dataset.

Accuracy: 46.27%
Indicates that the model correctly predicted the Pokémon type for 46.27% of the instances. Sensitivity (Recall): 25.15%
Suggests the model struggles to identify true positives, often missing the correct Pokémon types. Specificity: 88.07%
Shows the model is good at avoiding false positives, correctly identifying instances that do not belong to each type.

Exercise 10

How did your best random forest model do on the testing set?

Which Pokemon types is the model best at predicting, and which is it worst at? (Do you have any ideas why this might be?)

multi_metric <- metric_set(accuracy, sensitivity, specificity)

augment(pokemon_fit, new_data = pokemon_test) %>%
  multi_metric(truth = type_1, estimate = .pred_class)
## # A tibble: 3 × 3
##   .metric     .estimator .estimate
##   <chr>       <chr>          <dbl>
## 1 accuracy    multiclass     0.478
## 2 sensitivity macro          0.269
## 3 specificity macro          0.884

For 231 Students

Exercise 11

In the 2020-2021 season, Stephen Curry, an NBA basketball player, made 337 out of 801 three point shot attempts (42.1%). Use bootstrap resampling on a sequence of 337 1’s (makes) and 464 0’s (misses). For each bootstrap sample, compute and save the sample mean (e.g. bootstrap FG% for the player). Use 1000 bootstrap samples to plot a histogram of those values. Compute the 99% bootstrap confidence interval for Stephen Curry’s “true” end-of-season FG% using the quantile function in R. Print the endpoints of this interval.

Exercise 12

Using the abalone.txt data from previous assignments, fit and tune a random forest model to predict age. Use stratified cross-validation and select ranges for mtry, min_n, and trees. Present your results. What was your final chosen model’s RMSE on your testing set?

LS0tCnRpdGxlOiAiSG9tZXdvcmsgNSIKYXV0aG9yOiAiTGVpIFpoYW8iCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSkKYGBgCgojIyBIb21ld29yayA1CgpGb3IgdGhpcyBhc3NpZ25tZW50LCB3ZSB3aWxsIGJlIHdvcmtpbmcgd2l0aCB0aGUgZmlsZSBgInBva2Vtb24uY3N2ImAsIGZvdW5kIGluIGAvZGF0YWAuIFRoZSBmaWxlIGlzIGZyb20gS2FnZ2xlOiA8aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9hYmNzZHMvcG9rZW1vbj4uCgpUaGUgW1Bva8OpbW9uXShodHRwczovL3d3dy5wb2tlbW9uLmNvbS91cy8pIGZyYW5jaGlzZSBlbmNvbXBhc3NlcyB2aWRlbyBnYW1lcywgVFYgc2hvd3MsIG1vdmllcywgYm9va3MsIGFuZCBhIGNhcmQgZ2FtZS4gVGhpcyBkYXRhIHNldCB3YXMgZHJhd24gZnJvbSB0aGUgdmlkZW8gZ2FtZSBzZXJpZXMgYW5kIGNvbnRhaW5zIHN0YXRpc3RpY3MgYWJvdXQgNzIxIFBva8OpbW9uLCBvciAicG9ja2V0IG1vbnN0ZXJzLiIgSW4gUG9rw6ltb24gZ2FtZXMsIHRoZSB1c2VyIHBsYXlzIGFzIGEgdHJhaW5lciB3aG8gY29sbGVjdHMsIHRyYWRlcywgYW5kIGJhdHRsZXMgUG9rw6ltb24gdG8gKGEpIGNvbGxlY3QgYWxsIHRoZSBQb2vDqW1vbiBhbmQgKGIpIGJlY29tZSB0aGUgY2hhbXBpb24gUG9rw6ltb24gdHJhaW5lci4KCkVhY2ggUG9rw6ltb24gaGFzIGEgW3ByaW1hcnkgdHlwZV0oaHR0cHM6Ly9idWxiYXBlZGlhLmJ1bGJhZ2FyZGVuLm5ldC93aWtpL1R5cGUpIChzb21lIGV2ZW4gaGF2ZSBzZWNvbmRhcnkgdHlwZXMpLiBCYXNlZCBvbiB0aGVpciB0eXBlLCBhIFBva8OpbW9uIGlzIHN0cm9uZyBhZ2FpbnN0IHNvbWUgdHlwZXMsIGFuZCB2dWxuZXJhYmxlIHRvIG90aGVycy4gKFRoaW5rIHJvY2ssIHBhcGVyLCBzY2lzc29ycy4pIEEgRmlyZS10eXBlIFBva8OpbW9uLCBmb3IgZXhhbXBsZSwgaXMgdnVsbmVyYWJsZSB0byBXYXRlci10eXBlIFBva8OpbW9uLCBidXQgc3Ryb25nIGFnYWluc3QgR3Jhc3MtdHlwZS4KCiFbRmlnIDEuIFZ1bHBpeCwgYSBGaXJlLXR5cGUgZm94IFBva8OpbW9uIGZyb20gR2VuZXJhdGlvbiAxIChhbHNvIG15IGZhdm9yaXRlIFBva8OpbW9uISkgXShpbWFnZXMvdnVscGl4LnBuZyl7d2lkdGg9IjE5NiJ9CgpUaGUgZ29hbCBvZiB0aGlzIGFzc2lnbm1lbnQgaXMgdG8gYnVpbGQgYSBzdGF0aXN0aWNhbCBsZWFybmluZyBtb2RlbCB0aGF0IGNhbiBwcmVkaWN0IHRoZSAqKnByaW1hcnkgdHlwZSoqIG9mIGEgUG9rw6ltb24gYmFzZWQgb24gaXRzIGdlbmVyYXRpb24sIGxlZ2VuZGFyeSBzdGF0dXMsIGFuZCBzaXggYmF0dGxlIHN0YXRpc3RpY3MuICpUaGlzIGlzIGFuIGV4YW1wbGUgb2YgYSAqKmNsYXNzaWZpY2F0aW9uIHByb2JsZW0qKiwgYnV0IHRoZXNlIG1vZGVscyBjYW4gYWxzbyBiZSB1c2VkIGZvciAqKnJlZ3Jlc3Npb24gcHJvYmxlbXMqKiouCgpSZWFkIGluIHRoZSBmaWxlIGFuZCBmYW1pbGlhcml6ZSB5b3Vyc2VsZiB3aXRoIHRoZSB2YXJpYWJsZXMgdXNpbmcgYHBva2Vtb25fY29kZWJvb2sudHh0YC4KCgojIyMgRXhlcmNpc2UgMQoKSW5zdGFsbCBhbmQgbG9hZCB0aGUgYGphbml0b3JgIHBhY2thZ2UuIFVzZSBpdHMgYGNsZWFuX25hbWVzKClgIGZ1bmN0aW9uIG9uIHRoZSBQb2vDqW1vbiBkYXRhLCBhbmQgc2F2ZSB0aGUgcmVzdWx0cyB0byB3b3JrIHdpdGggZm9yIHRoZSByZXN0IG9mIHRoZSBhc3NpZ25tZW50LiBXaGF0IGhhcHBlbmVkIHRvIHRoZSBkYXRhPyBXaHkgZG8geW91IHRoaW5rIGBjbGVhbl9uYW1lcygpYCBpcyB1c2VmdWw/CmBgYHtyfQpwb2tlbW9uIDwtIHJlYWQuY3N2KCIvVXNlcnMvemhhb2xlaS9Eb3dubG9hZHMvaG9tZXdvcmstNS9kYXRhL1Bva2Vtb24uY3N2IikKbGlicmFyeSh2aXApCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5bW9kZWxzKQpsaWJyYXJ5KElTTFIpCmxpYnJhcnkoSVNMUjIpCmxpYnJhcnkoZ2xtbmV0KQpsaWJyYXJ5KG1vZGVsZGF0YSkKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShuYW5pYXIpICMgdG8gYXNzZXNzIG1pc3NpbmcgZGF0YSBwYXR0ZXJucwpsaWJyYXJ5KGNvcnJwbG90KSAjIGZvciBhIGNvcnJlbGF0aW9uIHBsb3QKbGlicmFyeShwYXRjaHdvcmspICMgZm9yIHB1dHRpbmcgcGxvdHMgdG9nZXRoZXIKbGlicmFyeShycGFydC5wbG90KQpsaWJyYXJ5KHZlcmlmaWNhdGlvbikKbGlicmFyeSh2ZXJpZmljYXRpb24pCnRpZHltb2RlbHNfcHJlZmVyKCkKbGlicmFyeShnZ3Bsb3QyKQpwb2tlbW9uIDwtY2xlYW5fbmFtZXMocG9rZW1vbikKaGVhZChwb2tlbW9uKQoKYGBgClRoZSBjbGVhbl9uYW1lcyBmdW5jdGlvbiBzdGFuZGFyZGl6ZXMgdGhlIGRhdGFzZXQncyBjb2x1bW4gbmFtZXMgYnkgcmVtb3ZpbmcgbGVhZGluZyBhbmQgdHJhaWxpbmcgc3BhY2VzIGFuZCByZXBsYWNpbmcgc3BlY2lhbCBjaGFyYWN0ZXJzIHdpdGggdW5kZXJzY29yZXMuIFRoaXMgZW5zdXJlcyBjb25zaXN0ZW5jeSBhbW9uZyB2YXJpYWJsZSBuYW1lcywgbWFraW5nIHRoZW0gZWFzaWVyIHRvIGFjY2VzcyBhbmQgd29yayB3aXRoLgoKIyMjIEV4ZXJjaXNlIDIKClVzaW5nIHRoZSBlbnRpcmUgZGF0YSBzZXQsIGNyZWF0ZSBhIGJhciBjaGFydCBvZiB0aGUgb3V0Y29tZSB2YXJpYWJsZSwgYHR5cGVfMWAuCgpIb3cgbWFueSBjbGFzc2VzIG9mIHRoZSBvdXRjb21lIGFyZSB0aGVyZT8gQXJlIHRoZXJlIGFueSBQb2vDqW1vbiB0eXBlcyB3aXRoIHZlcnkgZmV3IFBva8OpbW9uPyBJZiBzbywgd2hpY2ggb25lcz8KCkZvciB0aGlzIGFzc2lnbm1lbnQsIHdlJ2xsIGhhbmRsZSB0aGUgcmFyZXIgY2xhc3NlcyBieSBncm91cGluZyB0aGVtLCBvciAibHVtcGluZyB0aGVtLCIgdG9nZXRoZXIgaW50byBhbiAnb3RoZXInIGNhdGVnb3J5LiBbVXNpbmcgdGhlIGBmb3JjYXRzYCBwYWNrYWdlXShodHRwczovL2ZvcmNhdHMudGlkeXZlcnNlLm9yZy8pLCBkZXRlcm1pbmUgaG93IHRvIGRvIHRoaXMsIGFuZCAqKmx1bXAgYWxsIHRoZSBvdGhlciBsZXZlbHMgdG9nZXRoZXIgZXhjZXB0IGZvciB0aGUgdG9wIDYgbW9zdCBmcmVxdWVudCoqICh3aGljaCBhcmUgQnVnLCBGaXJlLCBHcmFzcywgTm9ybWFsLCBXYXRlciwgYW5kIFBzeWNoaWMpLgoKQ29udmVydCBgdHlwZV8xYCBhbmQgYGxlZ2VuZGFyeWAgdG8gZmFjdG9ycy4KCmBgYHtyfQpwb2tlbW9uJT4lCiAgZ2dwbG90KGFlcyh4PXR5cGVfMSkpKwogIGdlb21fYmFyKCkKCmBgYApUaGVyZSBhcmUgMTggb3V0Y29tZXMgaGVyZSwgYW5kIGNsYXNzZXMgc3VjaCBhcyBGbHlpbmcsIGZhaXJ5LCBJY2UgZG8gaW5jbHVkZSB2ZXJ5IHNtYWxsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMsIHNvIHdlIGhhdmUgdG8gbHVtcGluZyB0aGVtLgoKYGBge3J9CnBva2Vtb24kdHlwZV8xIDwtIHBva2Vtb24gJT4lCiAgbXV0YXRlKHR5cGVfMSA9IGZjdF9sdW1wX24odHlwZV8xLCBuID0gNiwgdyA9IE5VTEwsIG90aGVyX2xldmVsID0gIk90aGVyIikpJT4lCiAgIHB1bGwodHlwZV8xKQoKcG9rZW1vbiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB0eXBlXzEpKSArCiAgZ2VvbV9iYXIoKQoKIyBmYWN0b3JpemF0aW9uCnBva2Vtb24kdHlwZV8xIDwtIGFzLmZhY3Rvcihwb2tlbW9uJHR5cGVfMSkKcG9rZW1vbiRsZWdlbmRhcnkgPC0gYXMuZmFjdG9yKHBva2Vtb24gJGxlZ2VuZGFyeSkKYGBgCiMjIyBFeGVyY2lzZSAzCgpQZXJmb3JtIGFuIGluaXRpYWwgc3BsaXQgb2YgdGhlIGRhdGEuIFN0cmF0aWZ5IGJ5IHRoZSBvdXRjb21lIHZhcmlhYmxlLiBZb3UgY2FuIGNob29zZSBhIHByb3BvcnRpb24gdG8gdXNlLiBWZXJpZnkgdGhhdCB5b3VyIHRyYWluaW5nIGFuZCB0ZXN0IHNldHMgaGF2ZSB0aGUgZGVzaXJlZCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zLgoKTmV4dCwgdXNlICp2Ki1mb2xkIGNyb3NzLXZhbGlkYXRpb24gb24gdGhlIHRyYWluaW5nIHNldC4gVXNlIDUgZm9sZHMuIFN0cmF0aWZ5IHRoZSBmb2xkcyBieSBgdHlwZV8xYCBhcyB3ZWxsLiAqSGludDogTG9vayBmb3IgYSBgc3RyYXRhYCBhcmd1bWVudC4qCgpXaHkgZG8geW91IHRoaW5rIGRvaW5nIHN0cmF0aWZpZWQgc2FtcGxpbmcgZm9yIGNyb3NzLXZhbGlkYXRpb24gaXMgdXNlZnVsPwoKYGBge3J9CnNldC5zZWVkKDA5MjYpCgoKcG9rZW1vbl9zcGxpdCA8LSBpbml0aWFsX3NwbGl0KHBva2Vtb24sIHByb3AgPSAwLjc1LHN0cmF0YSA9ICAgInR5cGVfMSIsKQoKcG9rZW1vbl90cmFpbiA8LSB0cmFpbmluZyhwb2tlbW9uX3NwbGl0KQpwb2tlbW9uX3Rlc3QgPC0gdGVzdGluZyhwb2tlbW9uX3NwbGl0KQpwb2tlbW9uX2ZvbGRzIDwtdmZvbGRfY3YocG9rZW1vbl90cmFpbiwgdiA9IDUsIHN0cmF0YSA9ICJ0eXBlXzEiKQoKYGBgClN0cmF0aWZpZWQgc2FtcGxpbmcgZm9yIGNyb3NzLXZhbGlkYXRpb24gZW5zdXJlcyB0aGF0IHRoZSB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzIG1haW50YWluIHRoZSBzYW1lIHByb3BvcnRpb24gb2YgZmVhdHVyZXMgYXMgdGhlIG9yaWdpbmFsIGRhdGEsIGFsbG93aW5nIHRoZSByZXN1bHRzIHRvIGJldHRlciByZXByZXNlbnQgdGhlIGVudGlyZSBkYXRhc2V0LgoKIyMjIEV4ZXJjaXNlIDQKCkNyZWF0ZSBhIGNvcnJlbGF0aW9uIG1hdHJpeCBvZiB0aGUgdHJhaW5pbmcgc2V0LCB1c2luZyB0aGUgYGNvcnJwbG90YCBwYWNrYWdlLiAqTm90ZTogWW91IGNhbiBjaG9vc2UgaG93IHRvIGhhbmRsZSB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGZvciB0aGlzIHBsb3Q7IGp1c3RpZnkgeW91ciBkZWNpc2lvbihzKS4qCgpXaGF0IHJlbGF0aW9uc2hpcHMsIGlmIGFueSwgZG8geW91IG5vdGljZT8KCmBgYHtyfQpwb2tlbW9uX3RyYWluJT4lc2VsZWN0KGlzLm51bWVyaWMpJT4lY29yKCklPiVjb3JycGxvdCgpCmBgYAp0aGUgdmFyaWFibGUgdG90YWwgaXMgc3Ryb25nbHkgYW5kIHBvc2l0aXZlbHkgcmVsYXRlZCB3aXRoIGhwLCBhdHRhY2ssIGRlZmVuc2UsIHNwX2F0aywgc3BfZGVmLCBhbmQgc3BlZWQuCgoKIyMjIEV4ZXJjaXNlIDUKClNldCB1cCBhIHJlY2lwZSB0byBwcmVkaWN0IGB0eXBlXzFgIHdpdGggYGxlZ2VuZGFyeWAsIGBnZW5lcmF0aW9uYCwgYHNwX2F0a2AsIGBhdHRhY2tgLCBgc3BlZWRgLCBgZGVmZW5zZWAsIGBocGAsIGFuZCBgc3BfZGVmYC4KCi0gICBEdW1teS1jb2RlIGBsZWdlbmRhcnlgIGFuZCBgZ2VuZXJhdGlvbmA7CgotICAgQ2VudGVyIGFuZCBzY2FsZSBhbGwgcHJlZGljdG9ycy4KCmBgYHtyfQpwb2tlbW9uX3JlY2lwZTwtcmVjaXBlKHR5cGVfMX5sZWdlbmRhcnkgKyBnZW5lcmF0aW9uICsgc3BfYXRrICsgYXR0YWNrICsgc3BlZWQgCiAgICAgICAgICAgICAgICAgICAgICAgK2RlZmVuc2UgKyBocCArIHNwX2RlZixkYXRhPXBva2Vtb25fdHJhaW4pJT4lCiAgc3RlcF9kdW1teShhbGxfbm9taW5hbCgpLCAtYWxsX291dGNvbWVzKCkpJT4lCiAgc3RlcF9zY2FsZShhbGxfcHJlZGljdG9ycygpKSU+JQogIHN0ZXBfY2VudGVyKGFsbF9wcmVkaWN0b3JzKCkpCgoKYGBgCgojIyMgRXhlcmNpc2UgNgoKV2UnbGwgYmUgZml0dGluZyBhbmQgdHVuaW5nIGFuIGVsYXN0aWMgbmV0LCB0dW5pbmcgYHBlbmFsdHlgIGFuZCBgbWl4dHVyZWAgKHVzZSBgbXVsdGlub21fcmVnKClgIHdpdGggdGhlIGBnbG1uZXRgIGVuZ2luZSkuCgpTZXQgdXAgdGhpcyBtb2RlbCBhbmQgd29ya2Zsb3cuIENyZWF0ZSBhIHJlZ3VsYXIgZ3JpZCBmb3IgYHBlbmFsdHlgIGFuZCBgbWl4dHVyZWAgd2l0aCAxMCBsZXZlbHMgZWFjaDsgYG1peHR1cmVgIHNob3VsZCByYW5nZSBmcm9tIDAgdG8gMS4gRm9yIHRoaXMgYXNzaWdubWVudCwgbGV0IGBwZW5hbHR5YCByYW5nZSBmcm9tIDAuMDEgdG8gMyAodGhpcyBpcyBvbiB0aGUgYGlkZW50aXR5X3RyYW5zKClgIHNjYWxlOyBub3RlIHRoYXQgeW91J2xsIG5lZWQgdG8gc3BlY2lmeSB0aGVzZSB2YWx1ZXMgaW4gYmFzZSAxMCBvdGhlcndpc2UpLgoKYGBge3J9Cm5vbV9tb2RlbCA8LSBtdWx0aW5vbV9yZWcocGVuYWx0eSA9IHR1bmUoKSwKICAgICAgICAgICAgICAgICAgICAgICAgIG1peHR1cmUgPSB0dW5lKCkpICU+JQogIHNldF9lbmdpbmUoImdsbW5ldCIpICU+JQogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpCiAgCiMgc2V0IHVwIHRoZSB3b3JrZmxvdwpub21fd2tmbG93IDwtd29ya2Zsb3coKSAlPiUKICBhZGRfbW9kZWwobm9tX21vZGVsKSAlPiUKICBhZGRfcmVjaXBlKHBva2Vtb25fcmVjaXBlKQoKIyBjcmVhdGUgZ3JpZApub21fZ3JpZCA8LSBncmlkX3JlZ3VsYXIocGVuYWx0eShyYW5nZSA9YygwLjAxLCAzKSwgdHJhbnMgPSBpZGVudGl0eV90cmFucygpKSwKICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgIG1peHR1cmUocmFuZ2UgPSBjKDAsMSkpLAogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSAxMCkKYGBgCgojIyMgRXhlcmNpc2UgNwoKTm93IHNldCB1cCBhIHJhbmRvbSBmb3Jlc3QgbW9kZWwgYW5kIHdvcmtmbG93LiBVc2UgdGhlIGByYW5nZXJgIGVuZ2luZSBhbmQgc2V0IGBpbXBvcnRhbmNlID0gImltcHVyaXR5ImA7IHdlJ2xsIGJlIHR1bmluZyBgbXRyeWAsIGB0cmVlc2AsIGFuZCBgbWluX25gLiBVc2luZyB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgYHJhbmRfZm9yZXN0KClgLCBleHBsYWluIGluIHlvdXIgb3duIHdvcmRzIHdoYXQgZWFjaCBvZiB0aGVzZSBoeXBlcnBhcmFtZXRlcnMgcmVwcmVzZW50LgoKQ3JlYXRlIGEgcmVndWxhciBncmlkIHdpdGggOCBsZXZlbHMgZWFjaC4gWW91IGNhbiBjaG9vc2UgcGxhdXNpYmxlIHJhbmdlcyBmb3IgZWFjaCBoeXBlcnBhcmFtZXRlci4gTm90ZSB0aGF0IGBtdHJ5YCBzaG91bGQgbm90IGJlIHNtYWxsZXIgdGhhbiAxIG9yIGxhcmdlciB0aGFuIDguICoqRXhwbGFpbiB3aHkgbmVpdGhlciBvZiB0aG9zZSB2YWx1ZXMgd291bGQgbWFrZSBzZW5zZS4qKgoKV2hhdCB0eXBlIG9mIG1vZGVsIGRvZXMgYG10cnkgPSA4YCByZXByZXNlbnQ/CgpgYGB7cn0KIyBzZXQgdXAgbW9kZWwKcmFuZG9tZl9tb2RlbCA8LSByYW5kX2ZvcmVzdChtdHJ5ID0gdHVuZSgpLCB0cmVlcyA9IHR1bmUoKSwgbWluX24gPSB0dW5lKCkpICU+JQogIHNldF9lbmdpbmUoInJhbmdlciIsIGltcG9ydGFuY2UgPSAiaW1wdXJpdHkiKSAlPiUKICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKQoKIyBzZXQgdXAgd29ya2Zsb3cKcmFuZG9tZl93a2Zsb3cgPC0gd29ya2Zsb3coKSAlPiUKICBhZGRfbW9kZWwocmFuZG9tZl9tb2RlbCkgJT4lCiAgYWRkX3JlY2lwZShwb2tlbW9uX3JlY2lwZSkKCnJhbmRvbWZfZ3JpZCA8LSBncmlkX3JlZ3VsYXIobXRyeShyYW5nZSA9IGMoMiw3KSksCiAgICAgICAgICAgICAgICAgICAgICAgIHRyZWVzKHJhbmdlID0gYygxMDAsMTAwMCkpLAogICAgICAgICAgICAgICAgICAgICAgICBtaW5fbihyYW5nZSA9IGMoMiwxMCkpLAogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSA4KQoKCmBgYAptdHJ5IHJlcHJlc2VudHMgdGhlIG51bWJlciBvZiB2YXJpYWJsZXMgcmFuZG9tbHkgc2FtcGxlZCBhcyBjYW5kaWRhdGVzIGF0IGVhY2ggc3BsaXQgd2hlbiBidWlsZGluZyBhIHRyZWUuIFdlIG5lZWQgYXQgbGVhc3Qgb25lIHZhcmlhYmxlLCBzbyBtdHJ5IGNhbid0IGJlIGxlc3MgdGhhbiAxLiBTaW5jZSB3ZSBoYXZlIGF0IG1vc3QgOCBwcmVkaWN0b3JzIGluIHRoZSBtb2RlbCwgbXRyeSBjYW4ndCBiZSBsYXJnZXIgdGhhbiA4LiBXaGVuIG10cnkgaXMgc2V0IHRvIDgsIHRoZSBtb2RlbCBiZWNvbWVzIGEgZGVjaXNpb24gdHJlZSB0aGF0IHVzZXMgYWxsIGZlYXR1cmVzLgoKIyMjIEV4ZXJjaXNlIDgKCkZpdCBhbGwgbW9kZWxzIHRvIHlvdXIgZm9sZGVkIGRhdGEgdXNpbmcgYHR1bmVfZ3JpZCgpYC4KCioqTm90ZTogVHVuaW5nIHlvdXIgcmFuZG9tIGZvcmVzdCBtb2RlbCB3aWxsIHRha2UgYSBmZXcgbWludXRlcyB0byBydW4sIGFueXdoZXJlIGZyb20gNSBtaW51dGVzIHRvIDE1IG1pbnV0ZXMgYW5kIHVwLiBDb25zaWRlciBydW5uaW5nIHlvdXIgbW9kZWxzIG91dHNpZGUgb2YgdGhlIC5SbWQsIHN0b3JpbmcgdGhlIHJlc3VsdHMsIGFuZCBsb2FkaW5nIHRoZW0gaW4geW91ciAuUm1kIHRvIG1pbmltaXplIHRpbWUgdG8ga25pdC4gV2UnbGwgZ28gb3ZlciBob3cgdG8gZG8gdGhpcyBpbiBsZWN0dXJlLioqCgpVc2UgYGF1dG9wbG90KClgIG9uIHRoZSByZXN1bHRzLiBXaGF0IGRvIHlvdSBub3RpY2U/IERvIGxhcmdlciBvciBzbWFsbGVyIHZhbHVlcyBvZiBgcGVuYWx0eWAgYW5kIGBtaXh0dXJlYCBwcm9kdWNlIGJldHRlciBST0MgQVVDPyBXaGF0IGFib3V0IHZhbHVlcyBvZiBgbWluX25gLCBgdHJlZXNgLCBhbmQgYG10cnlgPwoKV2hhdCBlbGFzdGljIG5ldCBtb2RlbCBhbmQgd2hhdCByYW5kb20gZm9yZXN0IG1vZGVsIHBlcmZvcm0gdGhlIGJlc3Qgb24geW91ciBmb2xkZWQgZGF0YT8gKFdoYXQgc3BlY2lmaWMgdmFsdWVzIG9mIHRoZSBoeXBlcnBhcmFtZXRlcnMgcmVzdWx0ZWQgaW4gdGhlIG9wdGltYWwgUk9DIEFVQz8pCgpgYGB7cn0Kbm9tX3JlcyA8LSB0dW5lX2dyaWQoCiAgbm9tX3drZmxvdywKICByZXNhbXBsZXMgPSBwb2tlbW9uX2ZvbGRzLAogIGdyaWQgPSBub21fZ3JpZCwKICBjb250cm9sID0gY29udHJvbF9ncmlkKHNhdmVfcHJlZCA9IFRSVUUpCikKCmxvYWQoIi9Vc2Vycy96aGFvbGVpL0Rvd25sb2Fkcy9ob21ld29yay01L3R1bmVfcmFuZG9tX2ZvcmVzdC5yZGEiKQpgYGAKCgpgYGB7cn0KYXV0b3Bsb3Qobm9tX3JlcykKYGBgCkZvciB0aGlzIHBhcnRpY3VsYXIgZGF0YXNldCBhbmQgZWxhc3RpYyBuZXQgbW9kZWwsIHVzaW5nIHNtYWxsZXIgdmFsdWVzIGZvciBib3RoIHRoZSBwZW5hbHR5IGFuZCBtaXh0dXJlIHBhcmFtZXRlcnMgcmVzdWx0cyBpbiBiZXR0ZXIgbW9kZWwgcGVyZm9ybWFuY2UsIGFzIGluZGljYXRlZCBieSBoaWdoZXIgUk9DIEFVQyB2YWx1ZXMuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgbW9kZWwgYmVuZWZpdHMgZnJvbSBsZXNzIHJlZ3VsYXJpemF0aW9uIGFuZCBhIGJhbGFuY2UgdGhhdCBmYXZvcnMgUmlkZ2UgcmVndWxhcml6YXRpb24uCmBgYHtyfQpjb2xsZWN0X21ldHJpY3Mobm9tX3JlcykgJT4lCiAgZmlsdGVyKC5tZXRyaWMgPT0gInJvY19hdWMiKSAlPiUKICBzZWxlY3QocGVuYWx0eSwgbWl4dHVyZSwgbWVhbiwgc3RkX2VycikKYGBgCmBgYHtyfQpzZWxlY3RfYmVzdChub21fcmVzKQpgYGAKVGhlIGJlc3Qgcm9jX2F1YyB2YWx1ZSBpcyB3aGVuIHBlbmFsdHkgPSAwLjAxIGFuZCBtaXh0dXJlIGlzIGFib3V0IDAuNTU2CgoKYGBge3J9CmF1dG9wbG90KHJmX3JlcykKYGBgCgpgYGB7cn0Kc2VsZWN0X2Jlc3QocmZfcmVzKQpgYGAKZSByYW5kb20gZm9yZXN0IG1vZGVsLCBhbHRob3VnaCB0aGUgcmVzdWx0cyB2YXJpZXMgYSBsb3QsIHRoZSBoaWdoZXN0IHJvY19hdWMgdmFsdWUgaXMgd2hlbiBzZWxlY3RpbmcgMyBwcmVkaWN0b3JzICwgbm9kZSBzaXplNjQsIGFuZCA3NDIgdHJlZXMuCiMjIyBFeGVyY2lzZSA5CgpTZWxlY3QgeW91ciBvcHRpbWFsIFsqKnJhbmRvbSBmb3Jlc3QgbW9kZWwqKl17LnVuZGVybGluZX1pbiB0ZXJtcyBvZiBgcm9jX2F1Y2AuIFRoZW4gZml0IHRoYXQgbW9kZWwgdG8geW91ciB0cmFpbmluZyBzZXQgYW5kIGV2YWx1YXRlIGl0cyBwZXJmb3JtYW5jZSBvbiB0aGUgdGVzdGluZyBzZXQuCgpVc2luZyB0aGUgKip0cmFpbmluZyoqIHNldDoKCi0gICBDcmVhdGUgYSB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3QsIHVzaW5nIGB2aXAoKWAuICpOb3RlIHRoYXQgeW91J2xsIHN0aWxsIG5lZWQgdG8gaGF2ZSBzZXQgYGltcG9ydGFuY2UgPSAiaW1wdXJpdHkiYCB3aGVuIGZpdHRpbmcgdGhlIG1vZGVsIHRvIHlvdXIgZW50aXJlIHRyYWluaW5nIHNldCBpbiBvcmRlciBmb3IgdGhpcyB0byB3b3JrLioKCiAgICAtICAgV2hhdCB2YXJpYWJsZXMgd2VyZSBtb3N0IHVzZWZ1bD8gV2hpY2ggd2VyZSBsZWFzdCB1c2VmdWw/IEFyZSB0aGVzZSByZXN1bHRzIHdoYXQgeW91IGV4cGVjdGVkLCBvciBub3Q/CgpVc2luZyB0aGUgdGVzdGluZyBzZXQ6CgotICAgQ3JlYXRlIHBsb3RzIG9mIHRoZSBkaWZmZXJlbnQgUk9DIGN1cnZlcywgb25lIHBlciBsZXZlbCBvZiB0aGUgb3V0Y29tZSB2YXJpYWJsZTsKCi0gICBNYWtlIGEgaGVhdCBtYXAgb2YgdGhlIGNvbmZ1c2lvbiBtYXRyaXguCgpgYGB7cn0KZmluYWxfd2tmbG93IDwtIGZpbmFsaXplX3dvcmtmbG93KHJhbmRvbWZfd2tmbG93LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0X2Jlc3QocmZfcmVzKSkKcG9rZW1vbl9maXQgPC0gZml0KGZpbmFsX3drZmxvdywgcG9rZW1vbl90cmFpbikgIyBmaXQgdGhlIHRyYWluaW5nIHNldAoKdmlwKHBva2Vtb25fZml0LCBpbXBvcnRhbmNlID0gImltcHVyaXR5IikKCmBgYAoKYGBge3J9CnByZWRpY3QocG9rZW1vbl9maXQsIG5ld19kYXRhPXBva2Vtb25fdGVzdCwgdHlwZSA9InByb2IiKQpgYGAKCmBgYHtyfQphdWdtZW50KHBva2Vtb25fZml0LCBuZXdfZGF0YSA9IHBva2Vtb25fdGVzdCkgJT4lCiAgcm9jX2N1cnZlKHR5cGVfMSwgLnByZWRfQnVnLC5wcmVkX0ZpcmUsIC5wcmVkX0dyYXNzLCAucHJlZF9Ob3JtYWwsIC5wcmVkX1BzeWNoaWMsIC5wcmVkX1dhdGVyLCAucHJlZF9PdGhlcikgJT4lCiAgYXV0b3Bsb3QoKQpgYGAKCmBgYHtyfQphdWdtZW50KHBva2Vtb25fZml0LCBuZXdfZGF0YT0gcG9rZW1vbl90ZXN0KSAlPiUKICBjb25mX21hdCh0cnV0aCA9IHR5cGVfMSwgZXN0aW1hdGUgPSAucHJlZF9jbGFzcykgJT4lCiAgYXV0b3Bsb3QodHlwZSA9ICJoZWF0bWFwIikKYGBgClRoZSBtb2RlbCBpcyBiZXN0IGF0IHByZWRpY3RpbmcgT3RoZXIgdHlwZSwgdGhpcyBtaWdodCBiZWNhdXNlIGFmdGVyIHRoZSBsdW1waW5nLCB0aGUgT3RoZXIgdHlwZSBiZWNvbWVzIG1vc3QgcHJldmFsZW50IGluIHRoZSBkYXRhc2V0LgoKQWNjdXJhY3k6IDQ2LjI3JVwKSW5kaWNhdGVzIHRoYXQgdGhlIG1vZGVsIGNvcnJlY3RseSBwcmVkaWN0ZWQgdGhlIFBva8OpbW9uIHR5cGUgZm9yIDQ2LjI3JSBvZiB0aGUgaW5zdGFuY2VzLgpTZW5zaXRpdml0eSAoUmVjYWxsKTogMjUuMTUlXApTdWdnZXN0cyB0aGUgbW9kZWwgc3RydWdnbGVzIHRvIGlkZW50aWZ5IHRydWUgcG9zaXRpdmVzLCBvZnRlbiBtaXNzaW5nIHRoZSBjb3JyZWN0IFBva8OpbW9uIHR5cGVzLgpTcGVjaWZpY2l0eTogODguMDclXApTaG93cyB0aGUgbW9kZWwgaXMgZ29vZCBhdCBhdm9pZGluZyBmYWxzZSBwb3NpdGl2ZXMsIGNvcnJlY3RseSBpZGVudGlmeWluZyBpbnN0YW5jZXMgdGhhdCBkbyBub3QgYmVsb25nIHRvIGVhY2ggdHlwZS4KCiMjIyBFeGVyY2lzZSAxMAoKSG93IGRpZCB5b3VyIGJlc3QgcmFuZG9tIGZvcmVzdCBtb2RlbCBkbyBvbiB0aGUgdGVzdGluZyBzZXQ/CgpXaGljaCBQb2tlbW9uIHR5cGVzIGlzIHRoZSBtb2RlbCBiZXN0IGF0IHByZWRpY3RpbmcsIGFuZCB3aGljaCBpcyBpdCB3b3JzdCBhdD8gKERvIHlvdSBoYXZlIGFueSBpZGVhcyB3aHkgdGhpcyBtaWdodCBiZT8pCgpgYGB7cn0KbXVsdGlfbWV0cmljIDwtIG1ldHJpY19zZXQoYWNjdXJhY3ksIHNlbnNpdGl2aXR5LCBzcGVjaWZpY2l0eSkKCmF1Z21lbnQocG9rZW1vbl9maXQsIG5ld19kYXRhID0gcG9rZW1vbl90ZXN0KSAlPiUKICBtdWx0aV9tZXRyaWModHJ1dGggPSB0eXBlXzEsIGVzdGltYXRlID0gLnByZWRfY2xhc3MpCmBgYAoKCiMjIEZvciAyMzEgU3R1ZGVudHMKCiMjIyBFeGVyY2lzZSAxMQoKSW4gdGhlIDIwMjAtMjAyMSBzZWFzb24sIFN0ZXBoZW4gQ3VycnksIGFuIE5CQSBiYXNrZXRiYWxsIHBsYXllciwgbWFkZSAzMzcgb3V0IG9mIDgwMSB0aHJlZSBwb2ludCBzaG90IGF0dGVtcHRzICg0Mi4xJSkuIFVzZSBib290c3RyYXAgcmVzYW1wbGluZyBvbiBhIHNlcXVlbmNlIG9mIDMzNyAxJ3MgKG1ha2VzKSBhbmQgNDY0IDAncyAobWlzc2VzKS4gRm9yIGVhY2ggYm9vdHN0cmFwIHNhbXBsZSwgY29tcHV0ZSBhbmQgc2F2ZSB0aGUgc2FtcGxlIG1lYW4gKGUuZy4gYm9vdHN0cmFwIEZHJSBmb3IgdGhlIHBsYXllcikuIFVzZSAxMDAwIGJvb3RzdHJhcCBzYW1wbGVzIHRvIHBsb3QgYSBoaXN0b2dyYW0gb2YgdGhvc2UgdmFsdWVzLiBDb21wdXRlIHRoZSA5OSUgYm9vdHN0cmFwIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIFN0ZXBoZW4gQ3VycnkncyAidHJ1ZSIgZW5kLW9mLXNlYXNvbiBGRyUgdXNpbmcgdGhlIHF1YW50aWxlIGZ1bmN0aW9uIGluIFIuIFByaW50IHRoZSBlbmRwb2ludHMgb2YgdGhpcyBpbnRlcnZhbC4KCiMjIyBFeGVyY2lzZSAxMgoKVXNpbmcgdGhlIGBhYmFsb25lLnR4dGAgZGF0YSBmcm9tIHByZXZpb3VzIGFzc2lnbm1lbnRzLCBmaXQgYW5kIHR1bmUgYSAqKnJhbmRvbSBmb3Jlc3QqKiBtb2RlbCB0byBwcmVkaWN0IGBhZ2VgLiBVc2Ugc3RyYXRpZmllZCBjcm9zcy12YWxpZGF0aW9uIGFuZCBzZWxlY3QgcmFuZ2VzIGZvciBgbXRyeWAsIGBtaW5fbmAsIGFuZCBgdHJlZXNgLiBQcmVzZW50IHlvdXIgcmVzdWx0cy4gV2hhdCB3YXMgeW91ciBmaW5hbCBjaG9zZW4gbW9kZWwncyAqKlJNU0UqKiBvbiB5b3VyIHRlc3Rpbmcgc2V0Pwo=