Data Science Stream

Topic 11B: Machine Learning III


Example solutions for the Data Science Computer Lab 11B, which uses the caret R package (Kuhn et al. 2021) and Portuguese wine data obtained from UCI Machine Learning Repository (2009) (originally collected by Cortez et al. (2009)), are presented below.

This computer lab is designed to run alongside the content in the Introduction to Machine Learning in R supplement. It might be helpful to have this material open as you look through these solutions.


1 Machine Learning (ML) Preparations

Before we proceed, please make sure you have read all the content in the Introduction to Machine Learning in R supplement and completed Computer Lab 10B. It may also be helpful to:

  • Use the same working directory as the one you used when completing Computer Lab 10B
  • Keep the supplement content and the Computer Lab 10B solutions open in separate tabs while you work through this lab material

1.1 Wine Data Review

No answer required.

1.2 ML Aim

No answer required.

1.3 Pre-prepared ML Code to Run

The following code should have been run at this point:

# Specify required packages
ml_packages <- c("caret", "gbm", "kernlab", "magrittr", "randomForest", "rpart.plot")
# Install missing packages
install.packages(setdiff(ml_packages, rownames(installed.packages())))
# Load all packages
lapply(ml_packages, library, character.only = TRUE)
# Load data
red_wine <- read.csv(file = "winequality_red.csv", header = T)
red_wine$quality <- as.factor(red_wine$quality)
centre_scale <- preProcess(red_wine[, -12], 
                           method = c("center", "scale"))
red_wine_updated <- predict(centre_scale, red_wine)
set.seed(1650)
wine_train_index <- createDataPartition(red_wine_updated$quality, 
                                        p = 0.8, 
                                        list = FALSE, times = 1) 
red_wine_train <- red_wine_updated[wine_train_index, ]
red_wine_validate <- red_wine_updated[-wine_train_index, ]

set.seed(1650)
red_wine_dec_tree <- train(quality ~ .,
                           data = red_wine_train,
                           method = "rpart")

set.seed(1650)
red_wine_rf <- train(quality ~ .,
                           data = red_wine_train,
                           method = "rf")

2 Fine-Tuning ML Models

2.1 Tuning a Decision Tree

Example code is shown below:

set.seed(1650)
red_wine_dec_tree_tuned <- train(quality ~ .,
                                 data = red_wine_train,
                                 method = "rpart", 
                                 tuneGrid = expand.grid(cp = seq(0.001, 0.01, 0.001))
                                 )
red_wine_dec_tree_tuned
## CART 
## 
## 1282 samples
##   11 predictor
##    6 classes: '3', '4', '5', '6', '7', '8' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 1282, 1282, 1282, 1282, 1282, 1282, ... 
## Resampling results across tuning parameters:
## 
##   cp     Accuracy   Kappa    
##   0.001  0.5646999  0.3079162
##   0.002  0.5708469  0.3163015
##   0.003  0.5707549  0.3145723
##   0.004  0.5719805  0.3150799
##   0.005  0.5772597  0.3207074
##   0.006  0.5763066  0.3148442
##   0.007  0.5778125  0.3147239
##   0.008  0.5758359  0.3104433
##   0.009  0.5719269  0.3024742
##   0.010  0.5706992  0.2998836
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was cp = 0.007.

2.1.1

The top accuracy of the red_wine_dec_tree_tuned model is 57.78%, which is a very slight improvement over the original decision tree accuracy.

2.2 Tuning a Random Forest Model

Example code is shown below:

set.seed(1650)
red_wine_rf_tuned <- train(quality ~ .,
                           data = red_wine_train,
                           method = "rf",
                           tuneGrid = expand.grid(mtry = c(1:3)))
red_wine_rf_tuned
## Random Forest 
## 
## 1282 samples
##   11 predictor
##    6 classes: '3', '4', '5', '6', '7', '8' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 1282, 1282, 1282, 1282, 1282, 1282, ... 
## Resampling results across tuning parameters:
## 
##   mtry  Accuracy   Kappa    
##   1     0.6668683  0.4555277
##   2     0.6660082  0.4567234
##   3     0.6648004  0.4560781
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 1.

2.3 Resampling Methods

No answer required.

2.3.1

Example code is shown below:

tr_control <- trainControl(method = "cv",
                           number = 25)

set.seed(1650)
red_wine_dec_tree_tuned_cv <- train(quality ~ .,
                              data = red_wine_train,
                              trControl = tr_control,
                              method = "rpart",
                              tuneGrid = expand.grid(cp = seq(0.001, 0.01, 0.001))
                              )
red_wine_dec_tree_tuned_cv
## CART 
## 
## 1282 samples
##   11 predictor
##    6 classes: '3', '4', '5', '6', '7', '8' 
## 
## No pre-processing
## Resampling: Cross-Validated (25 fold) 
## Summary of sample sizes: 1232, 1230, 1232, 1229, 1231, 1230, ... 
## Resampling results across tuning parameters:
## 
##   cp     Accuracy   Kappa    
##   0.001  0.5733088  0.3214443
##   0.002  0.5904327  0.3428007
##   0.003  0.5892240  0.3396075
##   0.004  0.5909453  0.3411524
##   0.005  0.5797784  0.3165351
##   0.006  0.5797941  0.3141993
##   0.007  0.5727636  0.3024321
##   0.008  0.5790092  0.3119535
##   0.009  0.5807764  0.3138709
##   0.010  0.5861471  0.3177705
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was cp = 0.004.
set.seed(1650)
red_wine_rf_tuned_cv <- train(quality ~ .,
                              data = red_wine_train,
                              trControl = tr_control,
                              method = "rf",
                              tuneGrid = expand.grid(mtry = c(1:3))
                              )
red_wine_rf_tuned_cv
## Random Forest 
## 
## 1282 samples
##   11 predictor
##    6 classes: '3', '4', '5', '6', '7', '8' 
## 
## No pre-processing
## Resampling: Cross-Validated (25 fold) 
## Summary of sample sizes: 1232, 1230, 1232, 1229, 1231, 1230, ... 
## Resampling results across tuning parameters:
## 
##   mtry  Accuracy   Kappa    
##   1     0.7067215  0.5200728
##   2     0.7106424  0.5286154
##   3     0.7022088  0.5163067
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 2.

2.3.2

  • The red_wine_rf model had a top accuracy of 67.01%
  • The red_wine_rf_tuned model had a top accuracy of 66.69%
  • The red_wine_rf_tuned_cv model had a top accuracy of 71.06%

So we can see that spending some time tuning and tweaking our ML model can lead to significant improvements in predictive accuracy. The red_wine_rf_tuned_cv model that achieved the accuracy of 71.06% did so for an mtry value of 2, and used the cv resampling method.

2.3.3

ggplot(red_wine_rf)

dotPlot(varImp(red_wine_rf))

2.3.4

ggplot(red_wine_rf_tuned)

dotPlot(varImp(red_wine_rf_tuned))

ggplot(red_wine_rf_tuned_cv)

dotPlot(varImp(red_wine_rf_tuned_cv))

Here we can see that the models’ accuracies depended on different numbers of randomly selected predictors (feature variables) being selected, for the different adjustments being made (tuning parameters or changing the resampling method).

We observe that alcohol remains the most important feature variable across the different random forest models. However the next-most-important feature variables change slightly as we change model. free.sulfur.dioxide was the least important feature variable across all models.

3 Validating Results

3.1

Example results are shown below.

For this check, we have chosen to use the models:

  • red_wine_dec_tree_tuned_cv
  • red_wine_rf_tuned_cv

Decision Tree Results

# Load magrittr package for piping
library(magrittr)

# count number of observations in validation data
validation_numbers <- dim(red_wine_validate)[1]

# Use the fitted model to predict quality values given the validation data
predict_red_wine_dec_tree <- predict(red_wine_dec_tree_tuned_cv, 
                                     newdata =red_wine_validate)
# When run, the code below gives us the percentage of correct predictions
dec_tree_accuracy <- sum(predict_red_wine_dec_tree == 
                         red_wine_validate$quality) / validation_numbers * 100

dec_tree_accuracy %>% round(2)
## [1] 56.47

Random Forest Results

# Use the fitted model to predict quality values given the validation data
predict_red_wine_rf <- predict(red_wine_rf_tuned_cv, 
                               newdata =red_wine_validate)
# When run, the code below gives us the percentage of correct predictions
rf_accuracy <- sum(predict_red_wine_rf == 
                   red_wine_validate$quality) / validation_numbers * 100

rf_accuracy %>% round(2)
## [1] 65.93

4 Summarising Results

4.1

Example code is shown below:

results_boot <- resamples(list(decision_tree = red_wine_dec_tree, 
                               decision_tree_tuned = red_wine_dec_tree_tuned,
                               random_forest = red_wine_rf,
                               random_forest_tuned = red_wine_rf_tuned)
                         )
summary(results_boot)
## 
## Call:
## summary.resamples(object = results_boot)
## 
## Models: decision_tree, decision_tree_tuned, random_forest, random_forest_tuned 
## Number of resamples: 25 
## 
## Accuracy 
##                          Min.   1st Qu.    Median      Mean   3rd Qu.      Max.
## decision_tree       0.5353319 0.5614407 0.5720430 0.5737107 0.5883621 0.6247241
## decision_tree_tuned 0.5354167 0.5578947 0.5819328 0.5778125 0.5953878 0.6120690
## random_forest       0.6493776 0.6615721 0.6666667 0.6700665 0.6783370 0.6966527
## random_forest_tuned 0.6410256 0.6559140 0.6673961 0.6668683 0.6724891 0.7008547
##                     NA's
## decision_tree          0
## decision_tree_tuned    0
## random_forest          0
## random_forest_tuned    0
## 
## Kappa 
##                          Min.   1st Qu.    Median      Mean   3rd Qu.      Max.
## decision_tree       0.2520370 0.2891515 0.3081993 0.3033509 0.3189378 0.3814905
## decision_tree_tuned 0.2577697 0.2867525 0.3221000 0.3147239 0.3442703 0.3683173
## random_forest       0.4344375 0.4486099 0.4570372 0.4633930 0.4771286 0.5110820
## random_forest_tuned 0.4155740 0.4360557 0.4559183 0.4555277 0.4681016 0.5210316
##                     NA's
## decision_tree          0
## decision_tree_tuned    0
## random_forest          0
## random_forest_tuned    0
results_cv <- resamples(list(decision_tree_tuned_cv = red_wine_dec_tree_tuned_cv,
                             random_forest_tuned_cv = red_wine_rf_tuned_cv))
summary(results_cv)
## 
## Call:
## summary.resamples(object = results_cv)
## 
## Models: decision_tree_tuned_cv, random_forest_tuned_cv 
## Number of resamples: 25 
## 
## Accuracy 
##                             Min.   1st Qu.    Median      Mean   3rd Qu. Max.
## decision_tree_tuned_cv 0.4339623 0.5576923 0.6078431 0.5909453 0.6470588 0.70
## random_forest_tuned_cv 0.5400000 0.6730769 0.7058824 0.7106424 0.7547170 0.86
##                        NA's
## decision_tree_tuned_cv    0
## random_forest_tuned_cv    0
## 
## Kappa 
##                              Min.   1st Qu.    Median      Mean   3rd Qu.
## decision_tree_tuned_cv 0.08830275 0.2930591 0.3598383 0.3411524 0.4287492
## random_forest_tuned_cv 0.24342105 0.4580012 0.5179584 0.5286154 0.6113931
##                             Max. NA's
## decision_tree_tuned_cv 0.4932432    0
## random_forest_tuned_cv 0.7695853    0

Note: The column of interest in the output is actually the Mean column (the average accuracy achieved by the model over all the resamples), not the Max. column.

4.2

dotplot(results_boot)

dotplot(results_cv)

4.3

Note: Your results and conclusion may be different - the results discussed in these solutions files are for models trained following the set.seed(1650) specification.

We trained Decision Tree and Random Forest Machine machine learning models on the Portuguese red wine data winequality_red.csv.

The Random Forest models had the best overall accuracy based on the training data, at 70.02% with tuned parameters and the cv resampling method. This was supported by the validation data test, for which the selected Random Forest model achieved an accuracy score of 65.93%.

Based on our results, we would recommend using the Random Forest machine learning model for this data. We do note however that the model can take some time to run.

It is worth noting here that there are other more advanced models which we haven’t tried that could lead to higher accuracy scores.

5 Extension: Gradient Boosting Machine ML Models

Example code is shown below:

set.seed(1650)
red_wine_boosted <- train(quality ~ .,
                          data = red_wine_train,
                          method = "gbm",
                          verbose = F
                          )

5.1

red_wine_boosted
## Stochastic Gradient Boosting 
## 
## 1282 samples
##   11 predictor
##    6 classes: '3', '4', '5', '6', '7', '8' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 1282, 1282, 1282, 1282, 1282, 1282, ... 
## Resampling results across tuning parameters:
## 
##   interaction.depth  n.trees  Accuracy   Kappa    
##   1                   50      0.6049492  0.3554880
##   1                  100      0.6103872  0.3703725
##   1                  150      0.6081707  0.3688512
##   2                   50      0.6133524  0.3748142
##   2                  100      0.6178042  0.3853569
##   2                  150      0.6160044  0.3838989
##   3                   50      0.6195430  0.3865898
##   3                  100      0.6225492  0.3934048
##   3                  150      0.6250933  0.3986685
## 
## Tuning parameter 'shrinkage' was held constant at a value of 0.1
## 
## Tuning parameter 'n.minobsinnode' was held constant at a value of 10
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were n.trees = 150, interaction.depth =
##  3, shrinkage = 0.1 and n.minobsinnode = 10.

The best accuracy achieved by your red_wine_boosted gradient boosting machine model is 62.51%, for an interaction.depth of 3 and n.trees = 150.

5.2

No answer required.

5.3

set.seed(1650)
red_wine_boosted_tuned <- train(quality ~ .,
                                data = red_wine_train,
                                method = "gbm",
                                verbose = FALSE,
                                tuneGrid = expand.grid(interaction.depth = 3:6,
                                                       n.trees = seq(50, 200, 50),
                                                       shrinkage = 0.1,
                                                       n.minobsinnode = 10)
                               )
red_wine_boosted_tuned
## Stochastic Gradient Boosting 
## 
## 1282 samples
##   11 predictor
##    6 classes: '3', '4', '5', '6', '7', '8' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 1282, 1282, 1282, 1282, 1282, 1282, ... 
## Resampling results across tuning parameters:
## 
##   interaction.depth  n.trees  Accuracy   Kappa    
##   3                   50      0.6198300  0.3862094
##   3                  100      0.6213315  0.3914632
##   3                  150      0.6283839  0.4033432
##   3                  200      0.6290806  0.4059713
##   4                   50      0.6223524  0.3915968
##   4                  100      0.6266637  0.4001994
##   4                  150      0.6325701  0.4106020
##   4                  200      0.6331125  0.4120082
##   5                   50      0.6262532  0.3985373
##   5                  100      0.6320015  0.4094948
##   5                  150      0.6372771  0.4186823
##   5                  200      0.6411313  0.4249792
##   6                   50      0.6313038  0.4050127
##   6                  100      0.6391566  0.4204912
##   6                  150      0.6472912  0.4339037
##   6                  200      0.6475264  0.4345551
## 
## Tuning parameter 'shrinkage' was held constant at a value of 0.1
## 
## Tuning parameter 'n.minobsinnode' was held constant at a value of 10
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were n.trees = 200, interaction.depth =
##  6, shrinkage = 0.1 and n.minobsinnode = 10.

5.3.1

Example code is shown below:

set.seed(1650)
red_wine_boosted_tuned_cv <- train(quality ~ .,
                                   data = red_wine_train,
                                   method = "gbm",
                                   verbose = FALSE,
                                   trControl = tr_control,
                                   tuneGrid = expand.grid(interaction.depth = 3:6,
                                                          n.trees = seq(50, 200, 50),
                                                          shrinkage = 0.1,
                                                          n.minobsinnode = 10)
                                   )
red_wine_boosted_tuned_cv
## Stochastic Gradient Boosting 
## 
## 1282 samples
##   11 predictor
##    6 classes: '3', '4', '5', '6', '7', '8' 
## 
## No pre-processing
## Resampling: Cross-Validated (25 fold) 
## Summary of sample sizes: 1232, 1230, 1232, 1229, 1231, 1230, ... 
## Resampling results across tuning parameters:
## 
##   interaction.depth  n.trees  Accuracy   Kappa    
##   3                   50      0.6172749  0.3824031
##   3                  100      0.6483437  0.4346333
##   3                  150      0.6476075  0.4341328
##   3                  200      0.6517277  0.4428700
##   4                   50      0.6287437  0.4008587
##   4                  100      0.6427785  0.4247133
##   4                  150      0.6483460  0.4371353
##   4                  200      0.6473312  0.4366541
##   5                   50      0.6280076  0.4004039
##   5                  100      0.6483478  0.4333239
##   5                  150      0.6513173  0.4394956
##   5                  200      0.6590764  0.4525660
##   6                   50      0.6380836  0.4150853
##   6                  100      0.6598444  0.4530962
##   6                  150      0.6660563  0.4633046
##   6                  200      0.6776168  0.4828904
## 
## Tuning parameter 'shrinkage' was held constant at a value of 0.1
## 
## Tuning parameter 'n.minobsinnode' was held constant at a value of 10
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were n.trees = 200, interaction.depth =
##  6, shrinkage = 0.1 and n.minobsinnode = 10.

5.4

Gradient Boosting Machine Results

  • The red_wine_boosted model had a top accuracy of 62.51%
  • The red_wine_boosted_tuned model had a top accuracy of 64.73%
  • The red_wine_boosted_tuned_cv model had a top accuracy of 67.76% for interaction.depth = 6 and n.trees = 200.

We observe that by tweaking the gbm model, we have increased the predictive accuracy by over 5%, which is a great result.

5.5

plot(red_wine_boosted_tuned_cv)

We observe that in general, as the interaction depth increases, so to does the accuracy of the model.

6 Extension: Additional ML Models

6.1 LDA

set.seed(1650)
red_wine_lda <- train(quality ~ .,
                      data = red_wine_train,
                      method = "lda")
red_wine_lda
## Linear Discriminant Analysis 
## 
## 1282 samples
##   11 predictor
##    6 classes: '3', '4', '5', '6', '7', '8' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 1282, 1282, 1282, 1282, 1282, 1282, ... 
## Resampling results:
## 
##   Accuracy   Kappa    
##   0.5943597  0.3549375

The best accuracy achieved by this method is 59.43% for the training data.

6.2 SVM

set.seed(1650)
red_wine_svm <- train(quality ~ .,
                      data = red_wine_train,
                      method = "svmLinear")
red_wine_svm
## Support Vector Machines with Linear Kernel 
## 
## 1282 samples
##   11 predictor
##    6 classes: '3', '4', '5', '6', '7', '8' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 1282, 1282, 1282, 1282, 1282, 1282, ... 
## Resampling results:
## 
##   Accuracy   Kappa    
##   0.5902292  0.3242403
## 
## Tuning parameter 'C' was held constant at a value of 1

The best accuracy achieved by this method is 59.02%.

6.3 kNN

set.seed(1650)
red_wine_knn <- train(quality ~ .,
                      data = red_wine_train,
                      method = "knn")
red_wine_knn
## k-Nearest Neighbors 
## 
## 1282 samples
##   11 predictor
##    6 classes: '3', '4', '5', '6', '7', '8' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 1282, 1282, 1282, 1282, 1282, 1282, ... 
## Resampling results across tuning parameters:
## 
##   k  Accuracy   Kappa    
##   5  0.5388836  0.2733503
##   7  0.5467209  0.2812282
##   9  0.5518291  0.2852813
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was k = 9.

The best accuracy achieved by this method is 55.18%, for \(k\) = 9.


Well done, that concludes our work in machine learning.

Hopefully this lab has enhanced your understanding of how to conduct supervised machine learning in RStudio. This is just the beginning - there are so many different models and methods out there!


References

Cortez, P., A. Cerdeira, F. Almeida, T. Matos, and J. Reis. 2009. “Modeling Wine Preferences by Data Mining from Physicochemical Properties.” Decision Support Systems 47 (4): 547–53.
Kuhn, M., J. Wing, S. Weston, A. Williams, C. Keefer, A. Engelhardt, T. Cooper, et al. 2021. caret: Classification and Regression Training. https://cran.r-project.org/web/packages/caret/index.html.
Thulin, M. 2021. Modern Statistics with R: From Wrangling and Exploring Data to Inference and Predictive Modelling.
UCI Machine Learning Repository. 2009. “Wine Quality Data Set[.csv File].” 2009. https://archive.ics.uci.edu/ml/datasets/Wine+Quality.


These notes have been prepared by Rupert Kuveke. Please note that some of the content in these notes has been developed from content in Thulin (2021). The copyright for the material in these notes resides with the authors named above, with the Department of Mathematical and Physical Sciences and with La Trobe University. Copyright in this work is vested in La Trobe University including all La Trobe University branding and naming. Unless otherwise stated, material within this work is licensed under a Creative Commons Attribution-Non Commercial-Non Derivatives License BY-NC-ND.

LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiAxMUIgU29sdXRpb25zIg0Kb3V0cHV0Og0KICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6IA0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogcmVhZGFibGUNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCmJpYmxpb2dyYXBoeTogU1RNMTAwMV9EU19DTF9yZWZlcmVuY2VzLmJpYiANCmxpbmstY2l0YXRpb25zOiB5ZXMNCi0tLQ0KDQo8c3R5bGU+DQojVE9DIHsNCiAgYmFja2dyb3VuZDogdXJsKCJodHRwczovL3d3dy5sYXRyb2JlLmVkdS5hdS9fbWVkaWEvbGEtdHJvYmUtYXBpL3Y1L2ltZy9sb2dvLnN2ZyIpOw0KICBiYWNrZ3JvdW5kLXNpemU6IGNvbnRhaW47DQogIHBhZGRpbmctdG9wOiA4MHB4ICFpbXBvcnRhbnQ7DQogIGJhY2tncm91bmQtcmVwZWF0OiBuby1yZXBlYXQ7DQp9DQo8L3N0eWxlPg0KDQojIyMgRGF0YSBTY2llbmNlIFN0cmVhbSB7LX0NCg0KIyMjIFRvcGljIDExQjogTWFjaGluZSBMZWFybmluZyBJSUkgey19DQoNCjxicj4NCg0KRXhhbXBsZSBzb2x1dGlvbnMgZm9yIHRoZSBbRGF0YSBTY2llbmNlIENvbXB1dGVyIExhYiAxMUJdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0RTTUNMMTApLCB3aGljaCB1c2VzIHRoZSBgY2FyZXRgIFIgcGFja2FnZSBbQGNhcmV0XSBhbmQgUG9ydHVndWVzZSB3aW5lIGRhdGEgb2J0YWluZWQgZnJvbSBAVUNJV2luZSAob3JpZ2luYWxseSBjb2xsZWN0ZWQgYnkgQHdpbmUpLCBhcmUgcHJlc2VudGVkIGJlbG93Lg0KDQpUaGlzIGNvbXB1dGVyIGxhYiBpcyBkZXNpZ25lZCB0byBydW4gYWxvbmdzaWRlIHRoZSBjb250ZW50IGluIHRoZSBbSW50cm9kdWN0aW9uIHRvIE1hY2hpbmUgTGVhcm5pbmcgaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX2ludHJvZHVjdGlvbl90b19tYWNoaW5lX2xlYXJuaW5nX2luX3IvKS4gSXQgbWlnaHQgYmUgaGVscGZ1bCB0byBoYXZlIHRoaXMgbWF0ZXJpYWwgb3BlbiBhcyB5b3UgbG9vayB0aHJvdWdoIHRoZXNlIHNvbHV0aW9ucy4NCg0KPGJyPg0KDQojIE1hY2hpbmUgTGVhcm5pbmcgKE1MKSBQcmVwYXJhdGlvbnMgeyNwcmVwfQ0KDQpCZWZvcmUgd2UgcHJvY2VlZCwgcGxlYXNlIG1ha2Ugc3VyZSB5b3UgaGF2ZSByZWFkIGFsbCB0aGUgY29udGVudCBpbiB0aGUgW0ludHJvZHVjdGlvbiB0byBNYWNoaW5lIExlYXJuaW5nIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV9pbnRyb2R1Y3Rpb25fdG9fbWFjaGluZV9sZWFybmluZ19pbl9yLykgYW5kIGNvbXBsZXRlZCBbQ29tcHV0ZXIgTGFiIDEwQl0oaHR0cHM6Ly9ycHVicy5jb20vTFRVX1NUTTEwMDEvRFNNQ0wxMCkuIEl0IG1heSBhbHNvIGJlIGhlbHBmdWwgdG86DQoNCiogVXNlIHRoZSBzYW1lIHdvcmtpbmcgZGlyZWN0b3J5IGFzIHRoZSBvbmUgeW91IHVzZWQgd2hlbiBjb21wbGV0aW5nIENvbXB1dGVyIExhYiAxMEINCiogS2VlcCB0aGUgc3VwcGxlbWVudCBjb250ZW50IGFuZCB0aGUgW0NvbXB1dGVyIExhYiAxMEIgc29sdXRpb25zXShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDEwU29sKSBvcGVuIGluIHNlcGFyYXRlIHRhYnMgd2hpbGUgeW91IHdvcmsgdGhyb3VnaCB0aGlzIGxhYiBtYXRlcmlhbA0KDQojIyBXaW5lIERhdGEgUmV2aWV3DQoNCk5vIGFuc3dlciByZXF1aXJlZC4NCg0KIyMgTUwgQWltDQoNCk5vIGFuc3dlciByZXF1aXJlZC4NCg0KIyMgUHJlLXByZXBhcmVkIE1MIENvZGUgdG8gUnVuIHsjc3RhcnRjb2RlfQ0KDQpUaGUgZm9sbG93aW5nIGNvZGUgc2hvdWxkIGhhdmUgYmVlbiBydW4gYXQgdGhpcyBwb2ludDoNCg0KDQoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBpbmNsdWRlID0gRn0NCiMgU3BlY2lmeSByZXF1aXJlZCBwYWNrYWdlcw0KbWxfcGFja2FnZXMgPC0gYygiY2FyZXQiLCAiZ2JtIiwgImtlcm5sYWIiLCAibWFncml0dHIiLCAicmFuZG9tRm9yZXN0IiwgInJwYXJ0LnBsb3QiKQ0KIyBJbnN0YWxsIG1pc3NpbmcgcGFja2FnZXMNCmluc3RhbGwucGFja2FnZXMoc2V0ZGlmZihtbF9wYWNrYWdlcywgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSkNCiMgTG9hZCBhbGwgcGFja2FnZXMNCmxhcHBseShtbF9wYWNrYWdlcywgbGlicmFyeSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KYGBgDQoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KIyBTcGVjaWZ5IHJlcXVpcmVkIHBhY2thZ2VzDQptbF9wYWNrYWdlcyA8LSBjKCJjYXJldCIsICJnYm0iLCAia2VybmxhYiIsICJtYWdyaXR0ciIsICJyYW5kb21Gb3Jlc3QiLCAicnBhcnQucGxvdCIpDQojIEluc3RhbGwgbWlzc2luZyBwYWNrYWdlcw0KaW5zdGFsbC5wYWNrYWdlcyhzZXRkaWZmKG1sX3BhY2thZ2VzLCByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpKQ0KIyBMb2FkIGFsbCBwYWNrYWdlcw0KbGFwcGx5KG1sX3BhY2thZ2VzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQojIExvYWQgZGF0YQ0KcmVkX3dpbmUgPC0gcmVhZC5jc3YoZmlsZSA9ICJ3aW5lcXVhbGl0eV9yZWQuY3N2IiwgaGVhZGVyID0gVCkNCnJlZF93aW5lJHF1YWxpdHkgPC0gYXMuZmFjdG9yKHJlZF93aW5lJHF1YWxpdHkpDQpjZW50cmVfc2NhbGUgPC0gcHJlUHJvY2VzcyhyZWRfd2luZVssIC0xMl0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gYygiY2VudGVyIiwgInNjYWxlIikpDQpyZWRfd2luZV91cGRhdGVkIDwtIHByZWRpY3QoY2VudHJlX3NjYWxlLCByZWRfd2luZSkNCnNldC5zZWVkKDE2NTApDQp3aW5lX3RyYWluX2luZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24ocmVkX3dpbmVfdXBkYXRlZCRxdWFsaXR5LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwID0gMC44LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UsIHRpbWVzID0gMSkgDQpyZWRfd2luZV90cmFpbiA8LSByZWRfd2luZV91cGRhdGVkW3dpbmVfdHJhaW5faW5kZXgsIF0NCnJlZF93aW5lX3ZhbGlkYXRlIDwtIHJlZF93aW5lX3VwZGF0ZWRbLXdpbmVfdHJhaW5faW5kZXgsIF0NCg0Kc2V0LnNlZWQoMTY1MCkNCnJlZF93aW5lX2RlY190cmVlIDwtIHRyYWluKHF1YWxpdHkgfiAuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHJlZF93aW5lX3RyYWluLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJwYXJ0IikNCg0Kc2V0LnNlZWQoMTY1MCkNCnJlZF93aW5lX3JmIDwtIHRyYWluKHF1YWxpdHkgfiAuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHJlZF93aW5lX3RyYWluLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJmIikNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBGLCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGNhY2hlID0gVH0NCnJlZF93aW5lIDwtIHJlYWQuY3N2KGZpbGUgPSAiZGF0YS93aW5lcXVhbGl0eV9yZWQuY3N2IiwgaGVhZGVyID0gVCkNCnJlZF93aW5lJHF1YWxpdHkgPC0gYXMuZmFjdG9yKHJlZF93aW5lJHF1YWxpdHkpDQpjZW50cmVfc2NhbGUgPC0gcHJlUHJvY2VzcyhyZWRfd2luZVssIC0xMl0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gYygiY2VudGVyIiwgInNjYWxlIikpDQpyZWRfd2luZV91cGRhdGVkIDwtIHByZWRpY3QoY2VudHJlX3NjYWxlLCByZWRfd2luZSkNCnNldC5zZWVkKDE2NTApDQp3aW5lX3RyYWluX2luZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24ocmVkX3dpbmVfdXBkYXRlZCRxdWFsaXR5LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwID0gMC44LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UsIHRpbWVzID0gMSkgDQpyZWRfd2luZV90cmFpbiA8LSByZWRfd2luZV91cGRhdGVkW3dpbmVfdHJhaW5faW5kZXgsIF0NCnJlZF93aW5lX3ZhbGlkYXRlIDwtIHJlZF93aW5lX3VwZGF0ZWRbLXdpbmVfdHJhaW5faW5kZXgsIF0NCg0Kc2V0LnNlZWQoMTY1MCkNCnJlZF93aW5lX2RlY190cmVlIDwtIHRyYWluKHF1YWxpdHkgfiAuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHJlZF93aW5lX3RyYWluLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJwYXJ0IikNCg0Kc2V0LnNlZWQoMTY1MCkNCnJlZF93aW5lX3JmIDwtIHRyYWluKHF1YWxpdHkgfiAuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHJlZF93aW5lX3RyYWluLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJmIikNCmBgYA0KDQoNCiMgRmluZS1UdW5pbmcgTUwgTW9kZWxzIHsjdHB9DQoNCiMjIFR1bmluZyBhIERlY2lzaW9uIFRyZWUgeyNkZWN0cmVlfQ0KDQpFeGFtcGxlIGNvZGUgaXMgc2hvd24gYmVsb3c6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgY2FjaGUgPSBUfQ0Kc2V0LnNlZWQoMTY1MCkNCnJlZF93aW5lX2RlY190cmVlX3R1bmVkIDwtIHRyYWluKHF1YWxpdHkgfiAuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHJlZF93aW5lX3RyYWluLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJwYXJ0IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKGNwID0gc2VxKDAuMDAxLCAwLjAxLCAwLjAwMSkpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQpyZWRfd2luZV9kZWNfdHJlZV90dW5lZA0KYGBgDQoNCiMjIw0KDQpUaGUgdG9wIGFjY3VyYWN5IG9mIHRoZSBgcmVkX3dpbmVfZGVjX3RyZWVfdHVuZWRgIG1vZGVsIGlzIDU3Ljc4JSwgd2hpY2ggaXMgYSB2ZXJ5IHNsaWdodCBpbXByb3ZlbWVudCBvdmVyIHRoZSBvcmlnaW5hbCBkZWNpc2lvbiB0cmVlIGFjY3VyYWN5Lg0KDQojIyBUdW5pbmcgYSBSYW5kb20gRm9yZXN0IE1vZGVsIHsjcmZ0dW5lZH0NCg0KRXhhbXBsZSBjb2RlIGlzIHNob3duIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGNhY2hlID0gVH0NCnNldC5zZWVkKDE2NTApDQpyZWRfd2luZV9yZl90dW5lZCA8LSB0cmFpbihxdWFsaXR5IH4gLiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSByZWRfd2luZV90cmFpbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKG10cnkgPSBjKDE6MykpKQ0KcmVkX3dpbmVfcmZfdHVuZWQNCmBgYA0KDQojIyBSZXNhbXBsaW5nIE1ldGhvZHMgeyNyZXNhbXBsZX0NCg0KTm8gYW5zd2VyIHJlcXVpcmVkLg0KDQojIyMgeyN0ckNvbnRyb2x9DQoNCkV4YW1wbGUgY29kZSBpcyBzaG93biBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBjYWNoZSA9IFR9DQp0cl9jb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gMjUpDQoNCnNldC5zZWVkKDE2NTApDQpyZWRfd2luZV9kZWNfdHJlZV90dW5lZF9jdiA8LSB0cmFpbihxdWFsaXR5IH4gLiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSByZWRfd2luZV90cmFpbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyX2NvbnRyb2wsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicnBhcnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChjcCA9IHNlcSgwLjAwMSwgMC4wMSwgMC4wMDEpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KcmVkX3dpbmVfZGVjX3RyZWVfdHVuZWRfY3YNCg0Kc2V0LnNlZWQoMTY1MCkNCnJlZF93aW5lX3JmX3R1bmVkX2N2IDwtIHRyYWluKHF1YWxpdHkgfiAuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHJlZF93aW5lX3RyYWluLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJfY29udHJvbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKG10cnkgPSBjKDE6MykpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQpyZWRfd2luZV9yZl90dW5lZF9jdg0KYGBgDQoNCiMjIw0KDQoqIFRoZSBgcmVkX3dpbmVfcmZgIG1vZGVsIGhhZCBhIHRvcCBhY2N1cmFjeSBvZiA2Ny4wMSUNCiogVGhlIGByZWRfd2luZV9yZl90dW5lZGAgbW9kZWwgaGFkIGEgdG9wIGFjY3VyYWN5IG9mIDY2LjY5JQ0KKiBUaGUgYHJlZF93aW5lX3JmX3R1bmVkX2N2YCBtb2RlbCBoYWQgYSB0b3AgYWNjdXJhY3kgb2YgNzEuMDYlDQoNClNvIHdlIGNhbiBzZWUgdGhhdCBzcGVuZGluZyBzb21lIHRpbWUgdHVuaW5nIGFuZCB0d2Vha2luZyBvdXIgTUwgbW9kZWwgY2FuIGxlYWQgdG8gc2lnbmlmaWNhbnQgaW1wcm92ZW1lbnRzIGluIHByZWRpY3RpdmUgYWNjdXJhY3kuDQpUaGUgYHJlZF93aW5lX3JmX3R1bmVkX2N2YCBtb2RlbCB0aGF0IGFjaGlldmVkIHRoZSBhY2N1cmFjeSBvZiA3MS4wNiUgZGlkIHNvIGZvciBhbiBgbXRyeWAgdmFsdWUgb2YgMiwgYW5kIHVzZWQgdGhlIGBjdmAgcmVzYW1wbGluZyBtZXRob2QuDQoNCiMjIyB7I3JmcGxvdHN9DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLmRpbSA9IGMoNiw2KSwgZmlnLmFsaWduPSdjZW50ZXInfQ0KZ2dwbG90KHJlZF93aW5lX3JmKQ0KZG90UGxvdCh2YXJJbXAocmVkX3dpbmVfcmYpKQ0KYGBgDQoNCiMjIw0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGZpZy5kaW0gPSBjKDYsNiksIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChyZWRfd2luZV9yZl90dW5lZCkNCmRvdFBsb3QodmFySW1wKHJlZF93aW5lX3JmX3R1bmVkKSkNCg0KZ2dwbG90KHJlZF93aW5lX3JmX3R1bmVkX2N2KQ0KZG90UGxvdCh2YXJJbXAocmVkX3dpbmVfcmZfdHVuZWRfY3YpKQ0KYGBgDQoNCkhlcmUgd2UgY2FuIHNlZSB0aGF0IHRoZSBtb2RlbHMnIGFjY3VyYWNpZXMgZGVwZW5kZWQgb24gZGlmZmVyZW50IG51bWJlcnMgb2YgcmFuZG9tbHkgc2VsZWN0ZWQgcHJlZGljdG9ycyAoZmVhdHVyZSB2YXJpYWJsZXMpIGJlaW5nIHNlbGVjdGVkLCBmb3IgdGhlIGRpZmZlcmVudCBhZGp1c3RtZW50cyBiZWluZyBtYWRlICh0dW5pbmcgcGFyYW1ldGVycyBvciBjaGFuZ2luZyB0aGUgcmVzYW1wbGluZyBtZXRob2QpLg0KDQpXZSBvYnNlcnZlIHRoYXQgYGFsY29ob2xgIHJlbWFpbnMgdGhlIG1vc3QgaW1wb3J0YW50IGZlYXR1cmUgdmFyaWFibGUgYWNyb3NzIHRoZSBkaWZmZXJlbnQgcmFuZG9tIGZvcmVzdCBtb2RlbHMuIEhvd2V2ZXIgdGhlIG5leHQtbW9zdC1pbXBvcnRhbnQgZmVhdHVyZSB2YXJpYWJsZXMgY2hhbmdlIHNsaWdodGx5IGFzIHdlIGNoYW5nZSBtb2RlbC4gYGZyZWUuc3VsZnVyLmRpb3hpZGVgIHdhcyB0aGUgbGVhc3QgaW1wb3J0YW50IGZlYXR1cmUgdmFyaWFibGUgYWNyb3NzIGFsbCBtb2RlbHMuDQoNCg0KIyBWYWxpZGF0aW5nIFJlc3VsdHMgeyN2YWx9DQoNCiMjDQoNCkV4YW1wbGUgcmVzdWx0cyBhcmUgc2hvd24gYmVsb3cuDQoNCkZvciB0aGlzIGNoZWNrLCB3ZSBoYXZlIGNob3NlbiB0byB1c2UgdGhlIG1vZGVsczoNCg0KKiBgcmVkX3dpbmVfZGVjX3RyZWVfdHVuZWRfY3ZgDQoqIGByZWRfd2luZV9yZl90dW5lZF9jdmANCg0KIyMjIyBEZWNpc2lvbiBUcmVlIFJlc3VsdHMgey19DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCiMgTG9hZCBtYWdyaXR0ciBwYWNrYWdlIGZvciBwaXBpbmcNCmxpYnJhcnkobWFncml0dHIpDQoNCiMgY291bnQgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiB2YWxpZGF0aW9uIGRhdGENCnZhbGlkYXRpb25fbnVtYmVycyA8LSBkaW0ocmVkX3dpbmVfdmFsaWRhdGUpWzFdDQoNCiMgVXNlIHRoZSBmaXR0ZWQgbW9kZWwgdG8gcHJlZGljdCBxdWFsaXR5IHZhbHVlcyBnaXZlbiB0aGUgdmFsaWRhdGlvbiBkYXRhDQpwcmVkaWN0X3JlZF93aW5lX2RlY190cmVlIDwtIHByZWRpY3QocmVkX3dpbmVfZGVjX3RyZWVfdHVuZWRfY3YsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPXJlZF93aW5lX3ZhbGlkYXRlKQ0KIyBXaGVuIHJ1biwgdGhlIGNvZGUgYmVsb3cgZ2l2ZXMgdXMgdGhlIHBlcmNlbnRhZ2Ugb2YgY29ycmVjdCBwcmVkaWN0aW9ucw0KZGVjX3RyZWVfYWNjdXJhY3kgPC0gc3VtKHByZWRpY3RfcmVkX3dpbmVfZGVjX3RyZWUgPT0gDQogICAgICAgICAgICAgICAgICAgICAgICAgcmVkX3dpbmVfdmFsaWRhdGUkcXVhbGl0eSkgLyB2YWxpZGF0aW9uX251bWJlcnMgKiAxMDANCg0KZGVjX3RyZWVfYWNjdXJhY3kgJT4lIHJvdW5kKDIpDQpgYGANCg0KIyMjIyBSYW5kb20gRm9yZXN0IFJlc3VsdHMgey19DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCiMgVXNlIHRoZSBmaXR0ZWQgbW9kZWwgdG8gcHJlZGljdCBxdWFsaXR5IHZhbHVlcyBnaXZlbiB0aGUgdmFsaWRhdGlvbiBkYXRhDQpwcmVkaWN0X3JlZF93aW5lX3JmIDwtIHByZWRpY3QocmVkX3dpbmVfcmZfdHVuZWRfY3YsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPXJlZF93aW5lX3ZhbGlkYXRlKQ0KIyBXaGVuIHJ1biwgdGhlIGNvZGUgYmVsb3cgZ2l2ZXMgdXMgdGhlIHBlcmNlbnRhZ2Ugb2YgY29ycmVjdCBwcmVkaWN0aW9ucw0KcmZfYWNjdXJhY3kgPC0gc3VtKHByZWRpY3RfcmVkX3dpbmVfcmYgPT0gDQogICAgICAgICAgICAgICAgICAgcmVkX3dpbmVfdmFsaWRhdGUkcXVhbGl0eSkgLyB2YWxpZGF0aW9uX251bWJlcnMgKiAxMDANCg0KcmZfYWNjdXJhY3kgJT4lIHJvdW5kKDIpDQpgYGANCg0KIyBTdW1tYXJpc2luZyBSZXN1bHRzIHsjc3VtfQ0KDQojIw0KDQpFeGFtcGxlIGNvZGUgaXMgc2hvd24gYmVsb3c6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnJlc3VsdHNfYm9vdCA8LSByZXNhbXBsZXMobGlzdChkZWNpc2lvbl90cmVlID0gcmVkX3dpbmVfZGVjX3RyZWUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY2lzaW9uX3RyZWVfdHVuZWQgPSByZWRfd2luZV9kZWNfdHJlZV90dW5lZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb21fZm9yZXN0ID0gcmVkX3dpbmVfcmYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tX2ZvcmVzdF90dW5lZCA9IHJlZF93aW5lX3JmX3R1bmVkKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICkNCnN1bW1hcnkocmVzdWx0c19ib290KQ0KDQpyZXN1bHRzX2N2IDwtIHJlc2FtcGxlcyhsaXN0KGRlY2lzaW9uX3RyZWVfdHVuZWRfY3YgPSByZWRfd2luZV9kZWNfdHJlZV90dW5lZF9jdiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tX2ZvcmVzdF90dW5lZF9jdiA9IHJlZF93aW5lX3JmX3R1bmVkX2N2KSkNCnN1bW1hcnkocmVzdWx0c19jdikNCmBgYA0KDQoqTm90ZTogVGhlIGNvbHVtbiBvZiBpbnRlcmVzdCBpbiB0aGUgb3V0cHV0IGlzIGFjdHVhbGx5IHRoZSBgTWVhbmAgY29sdW1uICh0aGUgYXZlcmFnZSBhY2N1cmFjeSBhY2hpZXZlZCBieSB0aGUgbW9kZWwgb3ZlciBhbGwgdGhlIHJlc2FtcGxlcyksIG5vdCB0aGUgYE1heC5gIGNvbHVtbi4qDQoNCiMjDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLmRpbSA9IGMoNiw2KSwgZmlnLmFsaWduPSdjZW50ZXInfQ0KZG90cGxvdChyZXN1bHRzX2Jvb3QpDQpkb3RwbG90KHJlc3VsdHNfY3YpDQpgYGANCg0KIyMNCg0KKk5vdGU6IFlvdXIgcmVzdWx0cyBhbmQgY29uY2x1c2lvbiBtYXkgYmUgZGlmZmVyZW50IC0gdGhlIHJlc3VsdHMgZGlzY3Vzc2VkIGluIHRoZXNlIHNvbHV0aW9ucyBmaWxlcyBhcmUgZm9yIG1vZGVscyB0cmFpbmVkIGZvbGxvd2luZyB0aGUgYHNldC5zZWVkKDE2NTApYCBzcGVjaWZpY2F0aW9uLioNCg0KV2UgdHJhaW5lZCBEZWNpc2lvbiBUcmVlIGFuZCBSYW5kb20gRm9yZXN0IE1hY2hpbmUgbWFjaGluZSBsZWFybmluZyBtb2RlbHMgb24gdGhlIFBvcnR1Z3Vlc2UgcmVkIHdpbmUgZGF0YSBgd2luZXF1YWxpdHlfcmVkLmNzdmAuDQoNClRoZSBSYW5kb20gRm9yZXN0IG1vZGVscyBoYWQgdGhlIGJlc3Qgb3ZlcmFsbCBhY2N1cmFjeSBiYXNlZCBvbiB0aGUgdHJhaW5pbmcgZGF0YSwgYXQgNzAuMDIlIHdpdGggdHVuZWQgcGFyYW1ldGVycyBhbmQgdGhlIGBjdmAgcmVzYW1wbGluZyBtZXRob2QuIFRoaXMgd2FzIHN1cHBvcnRlZCBieSB0aGUgdmFsaWRhdGlvbiBkYXRhIHRlc3QsIGZvciB3aGljaCB0aGUgc2VsZWN0ZWQgUmFuZG9tIEZvcmVzdCBtb2RlbCBhY2hpZXZlZCBhbiBhY2N1cmFjeSBzY29yZSBvZiA2NS45MyUuDQoNCkJhc2VkIG9uIG91ciByZXN1bHRzLCB3ZSB3b3VsZCByZWNvbW1lbmQgdXNpbmcgdGhlIFJhbmRvbSBGb3Jlc3QgbWFjaGluZSBsZWFybmluZyBtb2RlbCBmb3IgdGhpcyBkYXRhLiBXZSBkbyBub3RlIGhvd2V2ZXIgdGhhdCB0aGUgbW9kZWwgY2FuIHRha2Ugc29tZSB0aW1lIHRvIHJ1bi4NCg0KKkl0IGlzIHdvcnRoIG5vdGluZyBoZXJlIHRoYXQgdGhlcmUgYXJlIG90aGVyIG1vcmUgYWR2YW5jZWQgbW9kZWxzIHdoaWNoIHdlIGhhdmVuJ3QgdHJpZWQgdGhhdCBjb3VsZCBsZWFkIHRvIGhpZ2hlciBhY2N1cmFjeSBzY29yZXMuKg0KDQojIEV4dGVuc2lvbjogR3JhZGllbnQgQm9vc3RpbmcgTWFjaGluZSBNTCBNb2RlbHMNCg0KRXhhbXBsZSBjb2RlIGlzIHNob3duIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGNhY2hlID0gVH0NCnNldC5zZWVkKDE2NTApDQpyZWRfd2luZV9ib29zdGVkIDwtIHRyYWluKHF1YWxpdHkgfiAuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gcmVkX3dpbmVfdHJhaW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnYm0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRg0KICAgICAgICAgICAgICAgICAgICAgICAgICApDQpgYGANCg0KIyMNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBjYWNoZSA9IFR9DQpyZWRfd2luZV9ib29zdGVkDQpgYGANCg0KDQpUaGUgYmVzdCBhY2N1cmFjeSBhY2hpZXZlZCBieSB5b3VyIGByZWRfd2luZV9ib29zdGVkYCBncmFkaWVudCBib29zdGluZyBtYWNoaW5lIG1vZGVsIGlzIDYyLjUxJSwgZm9yIGFuIGBpbnRlcmFjdGlvbi5kZXB0aGAgb2YgMyBhbmQgYG4udHJlZXNgID0gMTUwLg0KDQojIyB7I2dibXR1bmVkfQ0KDQpObyBhbnN3ZXIgcmVxdWlyZWQuDQoNCiMjDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgY2FjaGUgPSBUfQ0Kc2V0LnNlZWQoMTY1MCkNCnJlZF93aW5lX2Jvb3N0ZWRfdHVuZWQgPC0gdHJhaW4ocXVhbGl0eSB+IC4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSByZWRfd2luZV90cmFpbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdibSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChpbnRlcmFjdGlvbi5kZXB0aCA9IDM6NiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLnRyZWVzID0gc2VxKDUwLCAyMDAsIDUwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaHJpbmthZ2UgPSAwLjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbi5taW5vYnNpbm5vZGUgPSAxMCkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQpyZWRfd2luZV9ib29zdGVkX3R1bmVkDQpgYGANCg0KIyMjDQoNCkV4YW1wbGUgY29kZSBpcyBzaG93biBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBjYWNoZSA9IFR9DQpzZXQuc2VlZCgxNjUwKQ0KcmVkX3dpbmVfYm9vc3RlZF90dW5lZF9jdiA8LSB0cmFpbihxdWFsaXR5IH4gLiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHJlZF93aW5lX3RyYWluLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiZ2JtIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cl9jb250cm9sLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKGludGVyYWN0aW9uLmRlcHRoID0gMzo2LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4udHJlZXMgPSBzZXEoNTAsIDIwMCwgNTApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNocmlua2FnZSA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLm1pbm9ic2lubm9kZSA9IDEwKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQpyZWRfd2luZV9ib29zdGVkX3R1bmVkX2N2DQpgYGANCg0KIyMNCg0KIyMjIyBHcmFkaWVudCBCb29zdGluZyBNYWNoaW5lIFJlc3VsdHMgey19DQoNCiogVGhlIGByZWRfd2luZV9ib29zdGVkYCBtb2RlbCBoYWQgYSB0b3AgYWNjdXJhY3kgb2YgNjIuNTElDQoqIFRoZSBgcmVkX3dpbmVfYm9vc3RlZF90dW5lZGAgbW9kZWwgaGFkIGEgdG9wIGFjY3VyYWN5IG9mIDY0LjczJQ0KKiBUaGUgYHJlZF93aW5lX2Jvb3N0ZWRfdHVuZWRfY3ZgIG1vZGVsIGhhZCBhIHRvcCBhY2N1cmFjeSBvZiA2Ny43NiUgZm9yIGBpbnRlcmFjdGlvbi5kZXB0aGAgPSA2IGFuZCBgbi50cmVlc2AgPSAyMDAuDQoNCldlIG9ic2VydmUgdGhhdCBieSB0d2Vha2luZyB0aGUgYGdibWAgbW9kZWwsIHdlIGhhdmUgaW5jcmVhc2VkIHRoZSBwcmVkaWN0aXZlIGFjY3VyYWN5IGJ5IG92ZXIgNSUsIHdoaWNoIGlzIGEgZ3JlYXQgcmVzdWx0Lg0KDQojIw0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGZpZy5kaW0gPSBjKDYsNiksIGZpZy5hbGlnbj0nY2VudGVyJ30NCnBsb3QocmVkX3dpbmVfYm9vc3RlZF90dW5lZF9jdikNCmBgYA0KDQpXZSBvYnNlcnZlIHRoYXQgaW4gZ2VuZXJhbCwgYXMgdGhlIGludGVyYWN0aW9uIGRlcHRoIGluY3JlYXNlcywgc28gdG8gZG9lcyB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsLg0KDQojIEV4dGVuc2lvbjogQWRkaXRpb25hbCBNTCBNb2RlbHMNCg0KIyMgTERBDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgY2FjaGUgPSBUfQ0Kc2V0LnNlZWQoMTY1MCkNCnJlZF93aW5lX2xkYSA8LSB0cmFpbihxdWFsaXR5IH4gLiwNCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gcmVkX3dpbmVfdHJhaW4sDQogICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImxkYSIpDQpyZWRfd2luZV9sZGENCmBgYA0KDQpUaGUgYmVzdCBhY2N1cmFjeSBhY2hpZXZlZCBieSB0aGlzIG1ldGhvZCBpcyA1OS40MyUgZm9yIHRoZSB0cmFpbmluZyBkYXRhLg0KDQojIyBTVk0NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBjYWNoZSA9IFR9DQpzZXQuc2VlZCgxNjUwKQ0KcmVkX3dpbmVfc3ZtIDwtIHRyYWluKHF1YWxpdHkgfiAuLA0KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSByZWRfd2luZV90cmFpbiwNCiAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3ZtTGluZWFyIikNCnJlZF93aW5lX3N2bQ0KYGBgDQoNClRoZSBiZXN0IGFjY3VyYWN5IGFjaGlldmVkIGJ5IHRoaXMgbWV0aG9kIGlzIDU5LjAyJS4NCg0KIyMga05ODQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgY2FjaGUgPSBUfQ0Kc2V0LnNlZWQoMTY1MCkNCnJlZF93aW5lX2tubiA8LSB0cmFpbihxdWFsaXR5IH4gLiwNCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gcmVkX3dpbmVfdHJhaW4sDQogICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImtubiIpDQpyZWRfd2luZV9rbm4NCmBgYA0KDQpUaGUgYmVzdCBhY2N1cmFjeSBhY2hpZXZlZCBieSB0aGlzIG1ldGhvZCBpcyA1NS4xOCUsIGZvciAkayQgPSA5Lg0KDQo8YnI+DQoNCiMjIyMgV2VsbCBkb25lLCB0aGF0IGNvbmNsdWRlcyBvdXIgd29yayBpbiBtYWNoaW5lIGxlYXJuaW5nLiAjIyMjIHstfQ0KDQpIb3BlZnVsbHkgdGhpcyBsYWIgaGFzIGVuaGFuY2VkIHlvdXIgdW5kZXJzdGFuZGluZyBvZiBob3cgdG8gY29uZHVjdCBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcgaW4gUlN0dWRpby4gVGhpcyBpcyBqdXN0IHRoZSBiZWdpbm5pbmcgLSB0aGVyZSBhcmUgc28gbWFueSBkaWZmZXJlbnQgbW9kZWxzIGFuZCBtZXRob2RzIG91dCB0aGVyZSENCg0KPGJyPg0KDQojIFJlZmVyZW5jZXMgey0gI1JlZn0NCjxkaXYgaWQ9InJlZnMiPjwvZGl2Pg0KDQo8YnI+DQoNCjxmb250IGNvbG9yID0gImdyZXkiPg0KVGhlc2Ugbm90ZXMgaGF2ZSBiZWVuIHByZXBhcmVkIGJ5IFJ1cGVydCBLdXZla2UuIFBsZWFzZSBub3RlIHRoYXQgc29tZSBvZiB0aGUgY29udGVudCBpbiB0aGVzZSBub3RlcyBoYXMgYmVlbiBkZXZlbG9wZWQgZnJvbSBjb250ZW50IGluIEBNb2RTdGF0LiBUaGUgY29weXJpZ2h0IGZvciB0aGUgbWF0ZXJpYWwgaW4gdGhlc2Ugbm90ZXMgcmVzaWRlcyB3aXRoIHRoZSBhdXRob3JzIG5hbWVkIGFib3ZlLCB3aXRoIHRoZSBEZXBhcnRtZW50IG9mIE1hdGhlbWF0aWNhbCBhbmQgUGh5c2ljYWwgU2NpZW5jZXMgYW5kIHdpdGggTGEgVHJvYmUgVW5pdmVyc2l0eS4gQ29weXJpZ2h0IGluIHRoaXMgd29yayBpcyB2ZXN0ZWQgaW4gTGEgVHJvYmUgVW5pdmVyc2l0eSBpbmNsdWRpbmcgYWxsIExhIFRyb2JlIFVuaXZlcnNpdHkgYnJhbmRpbmcgYW5kIG5hbWluZy4gVW5sZXNzIG90aGVyd2lzZSBzdGF0ZWQsIG1hdGVyaWFsIHdpdGhpbiB0aGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgYSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbiBDb21tZXJjaWFsLU5vbiBEZXJpdmF0aXZlcyBMaWNlbnNlIA0KPGEgaHJlZiA9ICJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMtbmQvNC4wL0NDIiB0YXJnZXQ9Il9ibGFuayI+IEJZLU5DLU5ELiA8L2E+DQo8L2ZvbnQ+