Data Science Module

Topic 11B: Machine Learning II


Welcome to the eleventh computer lab for the Data Science module. This will be our second lab focusing on machine learning.

In this computer lab we will fit a variety of machine learning models to the Portuguese Vinho Verde wine1 we began analysing in Computer Lab 10B. We will also cover how to tune machine learning parameters in order to achieve better results.

This computer lab is designed to run alongside the content in the Introduction to Machine Learning in R supplement. The material in this supplement provides all the background information on machine learning and machine learning terminology you will need to complete this lab.

By the end of this lab, you will have gained a greater understanding of machine learning in R, and should feel comfortable conducting the processes involved in training and improving a variety of important machine learning models.


1 Preparations

Before we proceed, please make sure you have read 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 completed 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 Load Required Packages

Several of the R packages we require for our machine learning processes should be already installed on your PC, but we will also be using some new packages, so please make sure to run the R code in the Code chunk below. This code will install any missing packages, and then load all the packages needed for this lab:

# 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)

1.2 Wine Data

Recall that the Portuguese Vinho Verde wine data consists of 11 feature variables relating to physicochemical aspects of the wine, namely:

  • fixed acidity
  • volatile acidity
  • citric acid
  • residual sugar
  • chlorides
  • free sulfur dioxide
  • total sulfur dioxide
  • density
  • pH
  • sulphates
  • alcohol

The only available outcome variable is quality, which is an integer score from 0 to 10 (with 0 denoting a terrible wine and 10 denoting an exceptional wine). Note that no wine actually receives an extreme rating of 0 or 10.

This data is split into two sets - one for red wine and one for white wine. As part of Computer Lab 10B, you should have already downloaded the red wine data set - we will continue to analyse this data set in this lab. If you do not have this file saved on your device, please download winequality_red.csv from the LMS now.

1.3 Aim

In Computer Lab 10B, we pre-processed our red wine data, split the data into training and validation data sets, and trained and validated a simple decision tree model.

The best accuracy achieved was 57.37% (using the training data) and 52.05 (using the validation data).

Our aim now is to use our pre-processed training and validation data sets to train other machine learning models, in the hope that one or more of these new models will outperform the decision tree model.

We will also extend our skills using the train function, and try tweaking the tuning parameters for the different models in order to further improve our results, rather than simply using the default settings.

In the interests of time, and since you may not have your code from the previous computer lab to hand, please run the R code in the Code chunk below now. This Code chunk contains all the relevant R code to get us up and running for the subsequent steps.

Note: You must have the winequality_red.csv saved in your current working directory for the code below to work.

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, ]

2 Machine Learning Models

The focus here is on having some fun and familiarising ourselves with the R code required to fit different machine learning models - you are not expected to understand or explain all the mathematics behind these models.

We will fit the following types of machine learning model:

  • Decision tree
  • Random Forest
  • Gradient boosting machine
  • LDA
  • SVM
  • k-Nearest-Neighbour

Each of these models can be fitted using the train function from the caret package (although some will also require the use of other packages).

2.1

Let’s take a look at the code we used for our decision tree model from the previous lab, to refresh our memory on the basic train code framework:

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

Recall that the three main arguments you will need to include in your train function are:

  • The relationship between the outcome variable and the feature variables (here quality ~.)
  • The data set (here red_wine_train)
  • The method/algorithm to use (here "rpart")

Run this code now, before proceeding to the next question.

2.2 Tuning Parameters

So far, we have been using the default tuning parameters for our decision tree model. While these often do a great job, sometimes intelligently tweaking the tuning parameters can make all the difference. Each machine learning model has a different set of tuning parameters, and there are dozens of different models, but don’t worry - we’ll just focus on a select few.

We can specify changes to tuning parameters using the optional argument tuneGrid within the train function. This can be a complicated argument, since, as noted above, each machine learning model has its own set of tuning parameters.

The main tuning parameter for a decision tree model is cp - the complexity parameter, which has a default value of 0.01. The complexity parameter penalises the decision tree if it has too many branches. If the cp value is too low, your tree model may be overfitted, with an excessive number of branches. Conversely, if the cp value is too high, your tree model may look more like a stick, i.e. be too simplistic to be of any use.

The code chunk below contains partially completed code, with the tuneGrid argument incorporated into the train function.

red_wine_dec_tree_tuned <- train(... ,
                                 data = ... ,
                                 method = ... , 
                                 tuneGrid = expand.grid(cp = seq(0.001, 0.01, 0.001))
                                 )

Here:

  • We have specified that the cp values to test are \(0.001, 0.002, 0.003, \dots, 0.009, 0.01\).

Fill in the missing ... details in the code chunk above, and once you are happy with your code, try running it.

2.2.1

Check your results for your modified model - as a result of adjusting the cp tuning parameter, has the top accuracy of the model increased?

2.3 Resampling Methods

So far, we have used the default bootstrap resampling method boot when fitting all our machine learning models.

In fact, the train function accepts 14 different resampling options! Don’t worry though, we will just consider one alternative to the boot method in this lab, namely the cross-validation (cv) method.

The cv method follows the process we are employing with our training and validation data, albeit on a smaller scale - i.e. the machine learning model will take a subset of the red_wine_train data, train the model, and then test it on the remaining portion of the red_wine_train data. We can also specify the number of cross-validation trials to perform - 10 is often considered adequate.

2.3.1

We can specify the machine learning resampling method to use within the train function via the argument trControl.

Take a look at the code below, and see if you can create a new decision tree model named red_wine_dec_tree_tuned_cv that uses the range of cp values specified in 2.2 and the cv resampling method (you will have to fill in the ... missing parts).

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

red_wine_dec_tree_tuned_cv <- train(quality ~ .,
                                    data = ... ,
                                    trControl = tr_control,
                                    method = ...,
                                    tuneGrid = ...
                                    )

2.4 Decision Tree Models

Compare your results for your decision tree models red_wine_dec_tree, red_wine_dec_tree_tuned and red_wine_dec_tree_tunde_cv. Which of your three approaches results in the best performing decision tree model? Note the specific tuning parameter value and resampling method that led to the best results.

2.4.1

Use the ggplot function to plot the accuracy of your three decision tree models for different cp values.

2.4.2

It might also help to visualise the decision trees - use the rpart.plot function to plot the results.

Do you notice any problems that might be leading to the poor predictive accuracy result?

Hint: Check the code below for a head-start on how to plot your results.

rpart.plot(red_wine_dec_tree$finalModel)

2.5 Random Forest Models

The decision tree is one of the simplest machine learning models we can use. Other methods often produce better results, but they also tend to take longer to train. For the following models, don’t worry if it takes a couple of minutes for your code to run - this is normal.

Using the code in 2.1 above as a guide, train a machine learning model using the random forest method rf.

Assign your output to the object red_wine_rf.

Hint: The method = rpart will need to be changed.

2.5.1

What is the best accuracy achieved by the random forest method?

2.5.2

The main tuning parameter for a random forest model is the mtry argument, which represents the number of variables to use in each level. Generally, the default values for this are quite good, but it’s often worth trying out a few different values, just to check.

From your initial random forest model red_wine_rf, you may have found that an mtry value of 2 led to better results than mtry =6 or mtry=10. Therefore, let’s try some values around 2.

Use your code from 2.5 to fit a new random forest model that includes inside the train function the argument tuneGrid = expand.grid(mtry = c(1,3)) (we can skip mtry=2 since we already have results for this value). Call this new model red_wine_rf_tuned.

Note: The training of this model might take a few minutes, depending on your device’s hardware.

2.5.3

Using the code in 2.3.1 above as a guide, train a new random forest model that uses the cv resampling method and the tuning specifications used in 2.5.2.

Name your output red_wine_rf_tuned_cv.

2.5.4

Compare your results for your three random forest models red_wine_rf, red_wine_rf_tuned and red_wine_rf_tuned_cv. Which of your three approaches results in the best performing random forest model? Note the specific tuning parameter values and resampling method that led to the best results.

2.5.5

For random forest models, we can easily produce two helpful plots:

  • A plot of the model accuracy based on the number of feature variables used for each decision tree in the random forest model\(^{\dagger}\)
  • A plot of the importance of each feature variable in achieving an accurate model

Run the following R code to produce these plots now, for your initial random forest model:

ggplot(red_wine_rf)

dotPlot(varImp(red_wine_rf))

Note for the variable importance graph that the best (most helpful) variable is always given an importance of 100 and the worst is given an importance of 0. This is not to say that the variable with importance ranking 0 is useless - it is just considered less useful than the other variables.

What feature variables are considered most important?

\(^{\dagger}\) Recall from section 4.0.1 of the Introduction to Machine Learning in R supplement that random forest models are a type of ensemble method, and combine multiple decision trees together.

2.5.6

Create the plots discussed in 2.5.5 for your other two random forest models, and comment on your results.

2.6 Gradient Boosting Machine Models

Another ensemble method that combines multiple decision trees is the gradient boosting machine model. There are several options for training a gradient boosting machine model in R. We will use the stochastic gradient boosting method gbm.

Using the gbm argument within the train function, train a gradient boosting machine model, and name your model red_wine_boosted.

Again, don’t worry if it takes a couple of minutes for your code to run - this is normal.

Note: You can also include the argument verbose = F within the train function so no calculations are shown while the model is being fitted. Alternatively, if you would like to see the model fitting in action in the R console, you can leave this argument out.

2.6.1

What is the best accuracy achieved by the gradient boosting machine method?

2.6.2

For the gradient boosting machine model, the main tuning parameters are:

  • n.trees (the number of iterations i.e. the number of decision trees used) and
  • interaction.depth (the complexity of the trees).

The code chunk below contains partially completed code for a tuned gradient boosting machine model, with the tuneGrid argument incorporated into the train function.

red_wine_boosted_tuned <- train(... ,
                                data = ... ,
                                method = ... , 
                                verbose = FALSE,
                                tuneGrid = expand.grid(interaction.depth = 3:6,
                                                       n.trees = seq(50, 200, 50),
                                                       shrinkage = 0.1,
                                                       n.minobsinnode = 10)
                               )

Here:

  • We have specified that the interaction depth can be between 3 and 6, rather than between 1 and 3 (usually, greater depth leads to better accuracy, although we have to be careful not to overfit).
  • We have also specified that the number of trees can be 50, 100, 150 or 200 (i.e. 200 trees is now our maximum, rather than 150).
  • Note that the arguments shrinkage and n.minobsinnode must be included within the tuneGrid function, otherwise the training will fail. The values for these arguments are simply the default ones.

Fill in the missing ... details in the code chunk above, and once you are happy with your code, try running it.

2.6.3

Using the code in 2.3.1 above as a guide, train a new gradient boosting machine model that uses the cv resampling method and the tuning specifications used in 2.6.2.

Name your output red_wine_boosted_tuned_cv.

2.6.4

Compare your results for your three gradient boosting machine models red_wine_boosted, red_wine_boosted_tuned and red_wine_boosted_tuned_cv. Which of your three approaches results in the best performing gradient boosting machine model? Note the specific tuning parameter values and resampling method that led to the best results.

2.6.5

To conclude our focus on gradient boosted machine tree models, use the plot function to visualise the results of the training process, for your best performing gradient boosting machine model.

2.7 Additional Models

Our focus in this lab has been on tree-based machine learning models, as these are flexible and can perform well in a variety of contexts. However, there are plenty more models to choose from, and so we will briefly introduce a few new options below:

  • Linear Discriminant Analysis Model
  • Support Vector Machine Model
  • k-Nearest-Neighbour Model

For these models, we will use the default tuning parameters and resampling methods.

2.7.1 LDA

Fit a linear discriminant analysis model to your red_wine_train data via the method specification lda.

What is the best accuracy achieved by this method?

2.7.2 SVM

Fit a support vector machine model to your red_wine_train data via the method specification svmLinear (there are other options, but these are more complicated and take longer to execute).

What is the best accuracy achieved by this method?

Note: In order to fit a svm model, we need to load the kernlab package. This should have been done already in 1.1, but if an error appears when fitting the model, just double-check this.

2.7.3 kNN

The final method we will try is the k-Nearest-Neighbours model, which is selected via the method specification knn. Fit a kNN model to your red_wine_train data.

What is the best accuracy achieved by this method?

3 Validating Results

Great job! We’ve now fitted 6 different types of machine learning models.

In addition to obtaining predictive accuracy estimates for each of our models, it is important to also check how the models perform when presented with our validation data. If the accuracy of the models remains similar, then we can be more confident in our models’ reported performances.

Recall that we carried out this check in Computer Lab 10B2.

An example application of this approach to the decision tree model results is shown below:

# 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, 
                                     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 %>% round(2) * 100
dec_tree_accuracy

3.1

For each of the different types of models you used in 2, select the model that had the best performance. For example, if your red_wine_dec_tree_tuned was the best performing model out of the three decision tree models, select that model. This should lead you to have 6 selected models overall - one for each model type.

Following the process outlined above in 3, use your red_wine_validate data to assess the predictive accuracy of your 6 selected models.

Which of these models has the best performance with the validation data?

4 Summarising Results

Now that we have tried fitting and validating a selection of popular machine learning models, we might like to summarise our findings; this can make it easier to compare the results of the different methods.

4.1

Machine learning models trained using the train function test combinations of tuning parameters to try to find a combination leading to a accurate prediction. By default, 25 resamples are conducted on the training data, using different tuning parameter combinations - you might have noticed the line Resampling: Bootstrapped (25 reps) in the model output.

However, if we have specified the cv resampling method, only 10 resamples will have been taken.

Using the incomplete R code in the Code chunk below, create two lists to summarise the results of your different machine learning models:

Note: We need two lists, one for bootstrap resample results, and one for cross-validation resample results, since the number of resamples used for the two methods differs.

results_boot <- resamples(list(decision_tree = red_wine_dec_tree, 
                               decision_tree_tuned = red_wine_dec_tree_tuned,
                               random_forest = red_wine_rf, 
                               ...
                               )
                          )

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_boot)
...

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

4.2

Apply the dotplot function to the results_boot and results_cv objects to plot the range of accuracy values for the different methods.

4.3

To conclude, write a brief, simple summary of your findings, explaining your process and the results of your machine learning models. Overall, based on the results obtained and the training processes involved, do you have a preference for one of the machine learning models we have used in this computer lab?


Well done, that concludes our work in machine learning.

Hopefully this lab has enhanced your understanding of how to use R for machine learning. 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.
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.


  1. This data was obtained from the UCI Machine Learning Repository (2009) and originally collected by Cortez et al. (2009).↩︎

  2. This approach was also demonstrated in section 4.1 of the Introduction to Machine Learning in R supplement).↩︎

LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiAxMUIiDQpvdXRwdXQ6DQogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjogDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KYmlibGlvZ3JhcGh5OiBTVE0xMDAxX0RTX0NMX3JlZmVyZW5jZXMuYmliIA0KbGluay1jaXRhdGlvbnM6IHllcw0KLS0tDQoNCjxzdHlsZT4NCiNUT0Mgew0KICBiYWNrZ3JvdW5kOiB1cmwoImh0dHBzOi8vd3d3LmxhdHJvYmUuZWR1LmF1L19tZWRpYS9sYS10cm9iZS1hcGkvdjUvaW1nL2xvZ28uc3ZnIik7DQogIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjsNCiAgcGFkZGluZy10b3A6IDgwcHggIWltcG9ydGFudDsNCiAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsNCn0NCjwvc3R5bGU+DQoNCiMjIyBEYXRhIFNjaWVuY2UgTW9kdWxlIHstfQ0KDQojIyMgVG9waWMgMTFCOiBNYWNoaW5lIExlYXJuaW5nIElJIHstfQ0KDQo8YnI+DQoNCldlbGNvbWUgdG8gdGhlIGVsZXZlbnRoIGNvbXB1dGVyIGxhYiBmb3IgdGhlIERhdGEgU2NpZW5jZSBtb2R1bGUuIFRoaXMgd2lsbCBiZSBvdXIgc2Vjb25kIGxhYiBmb2N1c2luZyBvbiBtYWNoaW5lIGxlYXJuaW5nLg0KDQpJbiB0aGlzIGNvbXB1dGVyIGxhYiB3ZSB3aWxsIGZpdCBhIHZhcmlldHkgb2YgbWFjaGluZSBsZWFybmluZyBtb2RlbHMgdG8gdGhlIFBvcnR1Z3Vlc2UgKlZpbmhvIFZlcmRlKiB3aW5lXltUaGlzIGRhdGEgd2FzIG9idGFpbmVkIGZyb20gdGhlIEBVQ0lXaW5lIGFuZCBvcmlnaW5hbGx5IGNvbGxlY3RlZCBieSBAd2luZS5dIHdlIGJlZ2FuIGFuYWx5c2luZyBpbiBbQ29tcHV0ZXIgTGFiIDEwQl0oaHR0cHM6Ly9ycHVicy5jb20vTFRVX1NUTTEwMDEvRFNNQ0wxMF9TKS4gV2Ugd2lsbCBhbHNvIGNvdmVyIGhvdyB0byB0dW5lIG1hY2hpbmUgbGVhcm5pbmcgcGFyYW1ldGVycyBpbiBvcmRlciB0byBhY2hpZXZlIGJldHRlciByZXN1bHRzLg0KDQpUaGlzIGNvbXB1dGVyIGxhYiBpcyBkZXNpZ25lZCB0byBydW4gYWxvbmdzaWRlIHRoZSBjb250ZW50IGluIHRoZSBbSW50cm9kdWN0aW9uIHRvIE1hY2hpbmUgTGVhcm5pbmcgaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX3QxX2ludHJvZHVjdGlvbl90b19tYWNoaW5lX2xlYXJuaW5nX2luX3IvKS4gVGhlIG1hdGVyaWFsIGluIHRoaXMgc3VwcGxlbWVudCBwcm92aWRlcyBhbGwgdGhlIGJhY2tncm91bmQgaW5mb3JtYXRpb24gb24gbWFjaGluZSBsZWFybmluZyBhbmQgbWFjaGluZSBsZWFybmluZyB0ZXJtaW5vbG9neSB5b3Ugd2lsbCBuZWVkIHRvIGNvbXBsZXRlIHRoaXMgbGFiLiANCg0KQnkgdGhlIGVuZCBvZiB0aGlzIGxhYiwgeW91IHdpbGwgaGF2ZSBnYWluZWQgYSBncmVhdGVyIHVuZGVyc3RhbmRpbmcgb2YgbWFjaGluZSBsZWFybmluZyBpbiBSLCBhbmQgc2hvdWxkIGZlZWwgY29tZm9ydGFibGUgY29uZHVjdGluZyB0aGUgcHJvY2Vzc2VzIGludm9sdmVkIGluIHRyYWluaW5nIGFuZCBpbXByb3ZpbmcgYSB2YXJpZXR5IG9mIGltcG9ydGFudCBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4NCg0KPGJyPg0KDQojIFByZXBhcmF0aW9ucyB7I3ByZXB9DQoNCkJlZm9yZSB3ZSBwcm9jZWVkLCBwbGVhc2UgbWFrZSBzdXJlIHlvdSBoYXZlIHJlYWQgdGhlIGNvbnRlbnQgaW4gdGhlIFtJbnRyb2R1Y3Rpb24gdG8gTWFjaGluZSBMZWFybmluZyBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21fdDFfaW50cm9kdWN0aW9uX3RvX21hY2hpbmVfbGVhcm5pbmdfaW5fci8pIGFuZCBjb21wbGV0ZWQgW0NvbXB1dGVyIExhYiAxMEJdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0RTTUNMMTBfUykuIEl0IG1heSBhbHNvIGJlIGhlbHBmdWwgdG86DQoNCiogVXNlIHRoZSBzYW1lIHdvcmtpbmcgZGlyZWN0b3J5IGFzIHRoZSBvbmUgeW91IHVzZWQgd2hlbiBjb21wbGV0ZWQgQ29tcHV0ZXIgTGFiIDEwQg0KKiBLZWVwIHRoZSBzdXBwbGVtZW50IGNvbnRlbnQgYW5kIHRoZSBbQ29tcHV0ZXIgTGFiIDEwQiBzb2x1dGlvbnNdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0RTTUNMMTBTb2xfUykgb3BlbiBpbiBzZXBhcmF0ZSB0YWJzIHdoaWxlIHlvdSB3b3JrIHRocm91Z2ggdGhpcyBsYWIgbWF0ZXJpYWwNCg0KIyMgTG9hZCBSZXF1aXJlZCBQYWNrYWdlcyB7I2xvYWR9DQoNClNldmVyYWwgb2YgdGhlIFIgcGFja2FnZXMgd2UgcmVxdWlyZSBmb3Igb3VyIG1hY2hpbmUgbGVhcm5pbmcgcHJvY2Vzc2VzIHNob3VsZCBiZSBhbHJlYWR5IGluc3RhbGxlZCBvbiB5b3VyIFBDLCBidXQgd2Ugd2lsbCBhbHNvIGJlIHVzaW5nIHNvbWUgbmV3IHBhY2thZ2VzLCBzbyBwbGVhc2UgbWFrZSBzdXJlIHRvIHJ1biB0aGUgUiBjb2RlIGluIHRoZSBgQ29kZWAgY2h1bmsgYmVsb3cuIFRoaXMgY29kZSB3aWxsIGluc3RhbGwgYW55IG1pc3NpbmcgcGFja2FnZXMsIGFuZCB0aGVuIGxvYWQgYWxsIHRoZSBwYWNrYWdlcyBuZWVkZWQgZm9yIHRoaXMgbGFiOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQojIFNwZWNpZnkgcmVxdWlyZWQgcGFja2FnZXMNCm1sX3BhY2thZ2VzIDwtIGMoImNhcmV0IiwgImdibSIsICJrZXJubGFiIiwgIm1hZ3JpdHRyIiwgInJhbmRvbUZvcmVzdCIsICJycGFydC5wbG90IikNCiMgSW5zdGFsbCBtaXNzaW5nIHBhY2thZ2VzDQppbnN0YWxsLnBhY2thZ2VzKHNldGRpZmYobWxfcGFja2FnZXMsIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSkpDQojIExvYWQgYWxsIHBhY2thZ2VzDQpsYXBwbHkobWxfcGFja2FnZXMsIGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCg0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgaW5jbHVkZSA9IEZ9DQojIExvYWQgcGFja2FnZXMNCm1sX3BhY2thZ2VzIDwtIGMoImNhcmV0IiwgImdibSIsICJrZXJubGFiIiwgIm1hZ3JpdHRyIiwgInJhbmRvbUZvcmVzdCIsICJycGFydCIsICJycGFydC5wbG90IikNCg0KaW5zdGFsbC5wYWNrYWdlcyhzZXRkaWZmKG1sX3BhY2thZ2VzLCByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpKQ0KbGFwcGx5KG1sX3BhY2thZ2VzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQpgYGANCg0KIyMgV2luZSBEYXRhDQoNClJlY2FsbCB0aGF0IHRoZSBQb3J0dWd1ZXNlICpWaW5obyBWZXJkZSogd2luZSBkYXRhIGNvbnNpc3RzIG9mIDExIGZlYXR1cmUgdmFyaWFibGVzIHJlbGF0aW5nIHRvIHBoeXNpY29jaGVtaWNhbCBhc3BlY3RzIG9mIHRoZSB3aW5lLCBuYW1lbHk6DQoNCiogZml4ZWQgYWNpZGl0eQ0KKiB2b2xhdGlsZSBhY2lkaXR5DQoqIGNpdHJpYyBhY2lkDQoqIHJlc2lkdWFsIHN1Z2FyDQoqIGNobG9yaWRlcw0KKiBmcmVlIHN1bGZ1ciBkaW94aWRlDQoqIHRvdGFsIHN1bGZ1ciBkaW94aWRlDQoqIGRlbnNpdHkNCiogcEgNCiogc3VscGhhdGVzDQoqIGFsY29ob2wNCg0KVGhlIG9ubHkgYXZhaWxhYmxlIG91dGNvbWUgdmFyaWFibGUgaXMgYHF1YWxpdHlgLCB3aGljaCBpcyBhbiBpbnRlZ2VyIHNjb3JlIGZyb20gMCB0byAxMCAod2l0aCAwIGRlbm90aW5nIGEgdGVycmlibGUgd2luZSBhbmQgMTAgZGVub3RpbmcgYW4gZXhjZXB0aW9uYWwgd2luZSkuIE5vdGUgdGhhdCBubyB3aW5lIGFjdHVhbGx5IHJlY2VpdmVzIGFuIGV4dHJlbWUgcmF0aW5nIG9mIDAgb3IgMTAuDQoNClRoaXMgZGF0YSBpcyBzcGxpdCBpbnRvIHR3byBzZXRzIC0gb25lIGZvciByZWQgd2luZSBhbmQgb25lIGZvciB3aGl0ZSB3aW5lLiANCkFzIHBhcnQgb2YgW0NvbXB1dGVyIExhYiAxMEJdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0RTTUNMMTBfUyksIHlvdSBzaG91bGQgaGF2ZSBhbHJlYWR5IGRvd25sb2FkZWQgdGhlIHJlZCB3aW5lIGRhdGEgc2V0IC0gd2Ugd2lsbCBjb250aW51ZSB0byBhbmFseXNlIHRoaXMgZGF0YSBzZXQgaW4gdGhpcyBsYWIuIElmIHlvdSBkbyBub3QgaGF2ZSB0aGlzIGZpbGUgc2F2ZWQgb24geW91ciBkZXZpY2UsIHBsZWFzZSBkb3dubG9hZCAgYHdpbmVxdWFsaXR5X3JlZC5jc3ZgIGZyb20gdGhlIExNUyBub3cuDQoNCiMjIEFpbQ0KDQpJbiBbQ29tcHV0ZXIgTGFiIDEwQl0oaHR0cHM6Ly9ycHVicy5jb20vTFRVX1NUTTEwMDEvRFNNQ0wxMF9TKSwgd2UgcHJlLXByb2Nlc3NlZCBvdXIgcmVkIHdpbmUgZGF0YSwgc3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbiBkYXRhIHNldHMsIGFuZCB0cmFpbmVkIGFuZCB2YWxpZGF0ZWQgYSBzaW1wbGUgZGVjaXNpb24gdHJlZSBtb2RlbC4gDQoNClRoZSBiZXN0IGFjY3VyYWN5IGFjaGlldmVkIHdhcyA1Ny4zNyUgKHVzaW5nIHRoZSB0cmFpbmluZyBkYXRhKSBhbmQgNTIuMDUgKHVzaW5nIHRoZSB2YWxpZGF0aW9uIGRhdGEpLg0KDQpPdXIgYWltIG5vdyBpcyB0byB1c2Ugb3VyIHByZS1wcm9jZXNzZWQgdHJhaW5pbmcgYW5kIHZhbGlkYXRpb24gZGF0YSBzZXRzIHRvIHRyYWluIG90aGVyIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLCBpbiB0aGUgaG9wZSB0aGF0IG9uZSBvciBtb3JlIG9mIHRoZXNlIG5ldyBtb2RlbHMgd2lsbCBvdXRwZXJmb3JtIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsLg0KDQpXZSB3aWxsIGFsc28gZXh0ZW5kIG91ciBza2lsbHMgdXNpbmcgdGhlIGB0cmFpbmAgZnVuY3Rpb24sIGFuZCB0cnkgdHdlYWtpbmcgdGhlIHR1bmluZyBwYXJhbWV0ZXJzIGZvciB0aGUgZGlmZmVyZW50IG1vZGVscyBpbiBvcmRlciB0byBmdXJ0aGVyIGltcHJvdmUgb3VyIHJlc3VsdHMsIHJhdGhlciB0aGFuIHNpbXBseSB1c2luZyB0aGUgZGVmYXVsdCBzZXR0aW5ncy4NCg0KSW4gdGhlIGludGVyZXN0cyBvZiB0aW1lLCBhbmQgc2luY2UgeW91IG1heSBub3QgaGF2ZSB5b3VyIGNvZGUgZnJvbSB0aGUgcHJldmlvdXMgY29tcHV0ZXIgbGFiIHRvIGhhbmQsIHBsZWFzZSBydW4gdGhlIFIgY29kZSBpbiB0aGUgYENvZGVgIGNodW5rIGJlbG93IG5vdy4gVGhpcyBgQ29kZWAgY2h1bmsgY29udGFpbnMgYWxsIHRoZSByZWxldmFudCBSIGNvZGUgdG8gZ2V0IHVzIHVwIGFuZCBydW5uaW5nIGZvciB0aGUgc3Vic2VxdWVudCBzdGVwcy4NCg0KKk5vdGU6IFlvdSBtdXN0IGhhdmUgdGhlIGB3aW5lcXVhbGl0eV9yZWQuY3N2YCBzYXZlZCBpbiB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkgZm9yIHRoZSBjb2RlIGJlbG93IHRvIHdvcmsuKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpyZWRfd2luZSA8LSByZWFkLmNzdihmaWxlID0gIndpbmVxdWFsaXR5X3JlZC5jc3YiLCBoZWFkZXIgPSBUKQ0KcmVkX3dpbmUkcXVhbGl0eSA8LSBhcy5mYWN0b3IocmVkX3dpbmUkcXVhbGl0eSkNCmNlbnRyZV9zY2FsZSA8LSBwcmVQcm9jZXNzKHJlZF93aW5lWywgLTEyXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSBjKCJjZW50ZXIiLCAic2NhbGUiKSkNCnJlZF93aW5lX3VwZGF0ZWQgPC0gcHJlZGljdChjZW50cmVfc2NhbGUsIHJlZF93aW5lKQ0Kc2V0LnNlZWQoMTY1MCkNCndpbmVfdHJhaW5faW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihyZWRfd2luZV91cGRhdGVkJHF1YWxpdHksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHAgPSAwLjgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSwgdGltZXMgPSAxKSANCnJlZF93aW5lX3RyYWluIDwtIHJlZF93aW5lX3VwZGF0ZWRbd2luZV90cmFpbl9pbmRleCwgXQ0KcmVkX3dpbmVfdmFsaWRhdGUgPC0gcmVkX3dpbmVfdXBkYXRlZFstd2luZV90cmFpbl9pbmRleCwgXQ0KYGBgDQoNCiMgTWFjaGluZSBMZWFybmluZyBNb2RlbHMgeyNtbH0NCg0KVGhlIGZvY3VzIGhlcmUgaXMgb24gaGF2aW5nIHNvbWUgZnVuIGFuZCBmYW1pbGlhcmlzaW5nIG91cnNlbHZlcyB3aXRoIHRoZSBSIGNvZGUgcmVxdWlyZWQgdG8gZml0IGRpZmZlcmVudCBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscyAtIHlvdSBhcmUgbm90IGV4cGVjdGVkIHRvIHVuZGVyc3RhbmQgb3IgZXhwbGFpbiBhbGwgdGhlIG1hdGhlbWF0aWNzIGJlaGluZCB0aGVzZSBtb2RlbHMuDQoNCldlIHdpbGwgZml0IHRoZSBmb2xsb3dpbmcgdHlwZXMgb2YgbWFjaGluZSBsZWFybmluZyBtb2RlbDoNCg0KKiBEZWNpc2lvbiB0cmVlDQoqIFJhbmRvbSBGb3Jlc3QNCiogR3JhZGllbnQgYm9vc3RpbmcgbWFjaGluZQ0KKiBMREENCiogU1ZNDQoqIGstTmVhcmVzdC1OZWlnaGJvdXINCg0KRWFjaCBvZiB0aGVzZSBtb2RlbHMgY2FuIGJlIGZpdHRlZCB1c2luZyB0aGUgYHRyYWluYCBmdW5jdGlvbiBmcm9tIHRoZSBgY2FyZXRgIHBhY2thZ2UgKGFsdGhvdWdoIHNvbWUgd2lsbCBhbHNvIHJlcXVpcmUgdGhlIHVzZSBvZiBvdGhlciBwYWNrYWdlcykuIA0KDQojIyB7I2RlY3RyZWV9DQoNCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBjb2RlIHdlIHVzZWQgZm9yIG91ciBkZWNpc2lvbiB0cmVlIG1vZGVsIGZyb20gdGhlIHByZXZpb3VzIGxhYiwgdG8gcmVmcmVzaCBvdXIgbWVtb3J5IG9uIHRoZSBiYXNpYyBgdHJhaW5gIGNvZGUgZnJhbWV3b3JrOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpzZXQuc2VlZCgxNjUwKQ0KcmVkX3dpbmVfZGVjX3RyZWUgPC0gdHJhaW4ocXVhbGl0eSB+IC4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gcmVkX3dpbmVfdHJhaW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicnBhcnQiKQ0KcmVkX3dpbmVfZGVjX3RyZWUNCmBgYA0KDQpSZWNhbGwgdGhhdCB0aGUgdGhyZWUgbWFpbiBhcmd1bWVudHMgeW91IHdpbGwgbmVlZCB0byBpbmNsdWRlIGluIHlvdXIgYHRyYWluYCBmdW5jdGlvbiBhcmU6DQoNCiogVGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBvdXRjb21lIHZhcmlhYmxlIGFuZCB0aGUgZmVhdHVyZSB2YXJpYWJsZXMgKGhlcmUgYHF1YWxpdHkgfi5gKQ0KKiBUaGUgZGF0YSBzZXQgKGhlcmUgYHJlZF93aW5lX3RyYWluYCkNCiogVGhlIG1ldGhvZC9hbGdvcml0aG0gdG8gdXNlIChoZXJlIGAicnBhcnQiYCkNCg0KUnVuIHRoaXMgY29kZSBub3csIGJlZm9yZSBwcm9jZWVkaW5nIHRvIHRoZSBuZXh0IHF1ZXN0aW9uLg0KDQojIyBUdW5pbmcgUGFyYW1ldGVycyB7I3RwfQ0KDQpTbyBmYXIsIHdlIGhhdmUgYmVlbiB1c2luZyB0aGUgZGVmYXVsdCB0dW5pbmcgcGFyYW1ldGVycyBmb3Igb3VyIGRlY2lzaW9uIHRyZWUgbW9kZWwuIFdoaWxlIHRoZXNlIG9mdGVuIGRvIGEgZ3JlYXQgam9iLCBzb21ldGltZXMgaW50ZWxsaWdlbnRseSB0d2Vha2luZyB0aGUgdHVuaW5nIHBhcmFtZXRlcnMgY2FuIG1ha2UgYWxsIHRoZSBkaWZmZXJlbmNlLiBFYWNoIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwgaGFzIGEgZGlmZmVyZW50IHNldCBvZiB0dW5pbmcgcGFyYW1ldGVycywgYW5kIHRoZXJlIGFyZSBkb3plbnMgb2YgZGlmZmVyZW50IG1vZGVscywgYnV0IGRvbid0IHdvcnJ5IC0gd2UnbGwganVzdCBmb2N1cyBvbiBhIHNlbGVjdCBmZXcuDQoNCldlIGNhbiBzcGVjaWZ5IGNoYW5nZXMgdG8gdHVuaW5nIHBhcmFtZXRlcnMgdXNpbmcgdGhlIG9wdGlvbmFsIGFyZ3VtZW50IGB0dW5lR3JpZGAgd2l0aGluIHRoZSBgdHJhaW5gIGZ1bmN0aW9uLiBUaGlzIGNhbiBiZSBhIGNvbXBsaWNhdGVkIGFyZ3VtZW50LCBzaW5jZSwgYXMgbm90ZWQgYWJvdmUsIGVhY2ggbWFjaGluZSBsZWFybmluZyBtb2RlbCBoYXMgaXRzIG93biBzZXQgb2YgdHVuaW5nIHBhcmFtZXRlcnMuDQoNClRoZSBtYWluIHR1bmluZyBwYXJhbWV0ZXIgZm9yIGEgZGVjaXNpb24gdHJlZSBtb2RlbCBpcyBgY3BgIC0gdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyLCB3aGljaCBoYXMgYSBkZWZhdWx0IHZhbHVlIG9mIDAuMDEuIFRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciBwZW5hbGlzZXMgdGhlIGRlY2lzaW9uIHRyZWUgaWYgaXQgaGFzIHRvbyBtYW55IGJyYW5jaGVzLiBJZiB0aGUgYGNwYCB2YWx1ZSBpcyB0b28gbG93LCB5b3VyIHRyZWUgbW9kZWwgbWF5IGJlIG92ZXJmaXR0ZWQsIHdpdGggYW4gZXhjZXNzaXZlIG51bWJlciBvZiBicmFuY2hlcy4gQ29udmVyc2VseSwgaWYgdGhlIGBjcGAgdmFsdWUgaXMgdG9vIGhpZ2gsIHlvdXIgdHJlZSBtb2RlbCBtYXkgbG9vayBtb3JlIGxpa2UgYSBzdGljaywgaS5lLiBiZSB0b28gc2ltcGxpc3RpYyB0byBiZSBvZiBhbnkgdXNlLg0KDQpUaGUgY29kZSBjaHVuayBiZWxvdyBjb250YWlucyBwYXJ0aWFsbHkgY29tcGxldGVkIGNvZGUsIHdpdGggdGhlIGB0dW5lR3JpZGAgYXJndW1lbnQgaW5jb3Jwb3JhdGVkIGludG8gdGhlIGB0cmFpbmAgZnVuY3Rpb24uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnJlZF93aW5lX2RlY190cmVlX3R1bmVkIDwtIHRyYWluKC4uLiAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gLi4uICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9IC4uLiAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChjcCA9IHNlcSgwLjAwMSwgMC4wMSwgMC4wMDEpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KYGBgDQoNCkhlcmU6DQoNCiogV2UgaGF2ZSBzcGVjaWZpZWQgdGhhdCB0aGUgYGNwYCB2YWx1ZXMgdG8gdGVzdCBhcmUgJDAuMDAxLCAwLjAwMiwgMC4wMDMsIFxkb3RzLCAwLjAwOSwgMC4wMSQuDQoNCkZpbGwgaW4gdGhlIG1pc3NpbmcgYC4uLmAgZGV0YWlscyBpbiB0aGUgY29kZSBjaHVuayBhYm92ZSwgYW5kIG9uY2UgeW91IGFyZSBoYXBweSB3aXRoIHlvdXIgY29kZSwgdHJ5IHJ1bm5pbmcgaXQuDQoNCiMjIw0KDQpDaGVjayB5b3VyIHJlc3VsdHMgZm9yIHlvdXIgbW9kaWZpZWQgbW9kZWwgLSBhcyBhIHJlc3VsdCBvZiBhZGp1c3RpbmcgdGhlIGBjcGAgdHVuaW5nIHBhcmFtZXRlciwgaGFzIHRoZSB0b3AgYWNjdXJhY3kgb2YgdGhlIG1vZGVsIGluY3JlYXNlZD8NCg0KIyMgUmVzYW1wbGluZyBNZXRob2RzIHsjcmVzYW1wbGV9DQoNClNvIGZhciwgd2UgaGF2ZSB1c2VkIHRoZSBkZWZhdWx0IGJvb3RzdHJhcCByZXNhbXBsaW5nIG1ldGhvZCBgYm9vdGAgd2hlbiBmaXR0aW5nIGFsbCBvdXIgbWFjaGluZSBsZWFybmluZyBtb2RlbHMuDQoNCkluIGZhY3QsIHRoZSBgdHJhaW5gIGZ1bmN0aW9uIGFjY2VwdHMgMTQgZGlmZmVyZW50IHJlc2FtcGxpbmcgb3B0aW9ucyEgRG9uJ3Qgd29ycnkgdGhvdWdoLCB3ZSB3aWxsIGp1c3QgY29uc2lkZXIgb25lIGFsdGVybmF0aXZlIHRvIHRoZSBgYm9vdGAgbWV0aG9kIGluIHRoaXMgbGFiLCBuYW1lbHkgdGhlDQogY3Jvc3MtdmFsaWRhdGlvbiAoYGN2YCkgbWV0aG9kLiANCiANClRoZSBgY3ZgIG1ldGhvZCBmb2xsb3dzIHRoZSBwcm9jZXNzIHdlIGFyZSBlbXBsb3lpbmcgd2l0aCBvdXIgdHJhaW5pbmcgYW5kIHZhbGlkYXRpb24gZGF0YSwgYWxiZWl0IG9uIGEgc21hbGxlciBzY2FsZSAtIGkuZS4gdGhlIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwgd2lsbCB0YWtlIGEgc3Vic2V0IG9mIHRoZSBgcmVkX3dpbmVfdHJhaW5gIGRhdGEsIHRyYWluIHRoZSBtb2RlbCwgYW5kIHRoZW4gdGVzdCBpdCBvbiB0aGUgcmVtYWluaW5nIHBvcnRpb24gb2YgdGhlIGByZWRfd2luZV90cmFpbmAgZGF0YS4gV2UgY2FuIGFsc28gc3BlY2lmeSB0aGUgbnVtYmVyIG9mIGNyb3NzLXZhbGlkYXRpb24gdHJpYWxzIHRvIHBlcmZvcm0gLSAxMCBpcyBvZnRlbiBjb25zaWRlcmVkIGFkZXF1YXRlLg0KDQojIyMgeyN0ckNvbnRyb2x9DQoNCldlIGNhbiBzcGVjaWZ5IHRoZSBtYWNoaW5lIGxlYXJuaW5nIHJlc2FtcGxpbmcgbWV0aG9kIHRvIHVzZSB3aXRoaW4gdGhlIGB0cmFpbmAgZnVuY3Rpb24gdmlhIHRoZSBhcmd1bWVudCBgdHJDb250cm9sYC4NCg0KVGFrZSBhIGxvb2sgYXQgdGhlIGNvZGUgYmVsb3csIGFuZCBzZWUgaWYgeW91IGNhbiBjcmVhdGUgYSBuZXcgZGVjaXNpb24gdHJlZSBtb2RlbCBuYW1lZCBgcmVkX3dpbmVfZGVjX3RyZWVfdHVuZWRfY3ZgIHRoYXQgdXNlcyB0aGUgcmFuZ2Ugb2YgYGNwYCB2YWx1ZXMgc3BlY2lmaWVkIGluIFxAcmVmKHRwKSBhbmQgdGhlIGBjdmAgcmVzYW1wbGluZyBtZXRob2QgKHlvdSB3aWxsIGhhdmUgdG8gZmlsbCBpbiB0aGUgYC4uLmAgbWlzc2luZyBwYXJ0cykuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnRyX2NvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIgPSAxMCkNCg0KcmVkX3dpbmVfZGVjX3RyZWVfdHVuZWRfY3YgPC0gdHJhaW4ocXVhbGl0eSB+IC4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gLi4uICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyX2NvbnRyb2wsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAuLi4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IC4uLg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KYGBgDQoNCiMjIERlY2lzaW9uIFRyZWUgTW9kZWxzDQoNCkNvbXBhcmUgeW91ciByZXN1bHRzIGZvciB5b3VyIGRlY2lzaW9uIHRyZWUgbW9kZWxzIGByZWRfd2luZV9kZWNfdHJlZWAsIGByZWRfd2luZV9kZWNfdHJlZV90dW5lZGAgYW5kIGByZWRfd2luZV9kZWNfdHJlZV90dW5kZV9jdmAuDQpXaGljaCBvZiB5b3VyIHRocmVlIGFwcHJvYWNoZXMgcmVzdWx0cyBpbiB0aGUgYmVzdCBwZXJmb3JtaW5nIGRlY2lzaW9uIHRyZWUgbW9kZWw/DQpOb3RlIHRoZSBzcGVjaWZpYyB0dW5pbmcgcGFyYW1ldGVyIHZhbHVlIGFuZCByZXNhbXBsaW5nIG1ldGhvZCB0aGF0IGxlZCB0byB0aGUgYmVzdCByZXN1bHRzLg0KDQojIyMNCg0KVXNlIHRoZSBgZ2dwbG90YCBmdW5jdGlvbiB0byBwbG90IHRoZSBhY2N1cmFjeSBvZiB5b3VyIHRocmVlIGRlY2lzaW9uIHRyZWUgbW9kZWxzIGZvciBkaWZmZXJlbnQgYGNwYCB2YWx1ZXMuDQoNCiMjIw0KDQpJdCBtaWdodCBhbHNvIGhlbHAgdG8gdmlzdWFsaXNlIHRoZSBkZWNpc2lvbiB0cmVlcyAtIHVzZSB0aGUgYHJwYXJ0LnBsb3RgIGZ1bmN0aW9uIHRvIHBsb3QgdGhlIHJlc3VsdHMuIA0KDQpEbyB5b3Ugbm90aWNlIGFueSBwcm9ibGVtcyB0aGF0IG1pZ2h0IGJlIGxlYWRpbmcgdG8gdGhlIHBvb3IgcHJlZGljdGl2ZSBhY2N1cmFjeSByZXN1bHQ/IA0KDQoqSGludDogQ2hlY2sgdGhlIGNvZGUgYmVsb3cgZm9yIGEgaGVhZC1zdGFydCBvbiBob3cgdG8gcGxvdCB5b3VyIHJlc3VsdHMuKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpycGFydC5wbG90KHJlZF93aW5lX2RlY190cmVlJGZpbmFsTW9kZWwpDQpgYGANCg0KIyMgUmFuZG9tIEZvcmVzdCBNb2RlbHMgeyNyZn0NCg0KVGhlIGRlY2lzaW9uIHRyZWUgaXMgb25lIG9mIHRoZSBzaW1wbGVzdCBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscyB3ZSBjYW4gdXNlLiBPdGhlciBtZXRob2RzIG9mdGVuIHByb2R1Y2UgYmV0dGVyIHJlc3VsdHMsIGJ1dCB0aGV5IGFsc28gdGVuZCB0byB0YWtlIGxvbmdlciB0byB0cmFpbi4NCkZvciB0aGUgZm9sbG93aW5nIG1vZGVscywgZG9uJ3Qgd29ycnkgaWYgaXQgdGFrZXMgYSBjb3VwbGUgb2YgbWludXRlcyBmb3IgeW91ciBjb2RlIHRvIHJ1biAtIHRoaXMgaXMgbm9ybWFsLg0KDQpVc2luZyB0aGUgY29kZSBpbiBcQHJlZihkZWN0cmVlKSBhYm92ZSBhcyBhIGd1aWRlLCB0cmFpbiBhIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwgdXNpbmcgdGhlIHJhbmRvbSBmb3Jlc3QgbWV0aG9kIGByZmAuIA0KDQpBc3NpZ24geW91ciBvdXRwdXQgdG8gdGhlIG9iamVjdCBgcmVkX3dpbmVfcmZgLg0KDQoqSGludDogVGhlIGBtZXRob2QgPSBycGFydGAgd2lsbCBuZWVkIHRvIGJlIGNoYW5nZWQuKg0KDQojIyMNCg0KV2hhdCBpcyB0aGUgYmVzdCBhY2N1cmFjeSBhY2hpZXZlZCBieSB0aGUgcmFuZG9tIGZvcmVzdCBtZXRob2Q/DQoNCiMjIyB7I3JmdHVuZWR9DQoNClRoZSBtYWluIHR1bmluZyBwYXJhbWV0ZXIgZm9yIGEgcmFuZG9tIGZvcmVzdCBtb2RlbCBpcyB0aGUgYG10cnlgIGFyZ3VtZW50LCB3aGljaCByZXByZXNlbnRzIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIHRvIHVzZSBpbiBlYWNoIGxldmVsLiANCkdlbmVyYWxseSwgdGhlIGRlZmF1bHQgdmFsdWVzIGZvciB0aGlzIGFyZSBxdWl0ZSBnb29kLCBidXQgaXQncyBvZnRlbiB3b3J0aCB0cnlpbmcgb3V0IGEgZmV3IGRpZmZlcmVudCB2YWx1ZXMsIGp1c3QgdG8gY2hlY2suIA0KDQpGcm9tIHlvdXIgaW5pdGlhbCByYW5kb20gZm9yZXN0IG1vZGVsIGByZWRfd2luZV9yZmAsIHlvdSBtYXkgaGF2ZSBmb3VuZCB0aGF0IGFuIGBtdHJ5YCB2YWx1ZSBvZiAyIGxlZCB0byBiZXR0ZXIgcmVzdWx0cyB0aGFuIGBtdHJ5ID02YCBvciBgbXRyeT0xMGAuIFRoZXJlZm9yZSwgbGV0J3MgdHJ5IHNvbWUgdmFsdWVzIGFyb3VuZCAyLg0KDQpVc2UgeW91ciBjb2RlIGZyb20gXEByZWYocmYpIHRvIGZpdCBhIG5ldyByYW5kb20gZm9yZXN0IG1vZGVsIHRoYXQgaW5jbHVkZXMgaW5zaWRlIHRoZSBgdHJhaW5gIGZ1bmN0aW9uIHRoZSBhcmd1bWVudA0KYHR1bmVHcmlkID0gZXhwYW5kLmdyaWQobXRyeSA9IGMoMSwzKSlgICh3ZSBjYW4gc2tpcCBgbXRyeT0yYCBzaW5jZSB3ZSBhbHJlYWR5IGhhdmUgcmVzdWx0cyBmb3IgdGhpcyB2YWx1ZSkuIENhbGwgdGhpcyBuZXcgbW9kZWwgYHJlZF93aW5lX3JmX3R1bmVkYC4NCg0KKk5vdGU6IFRoZSB0cmFpbmluZyBvZiB0aGlzIG1vZGVsIG1pZ2h0IHRha2UgYSBmZXcgbWludXRlcywgZGVwZW5kaW5nIG9uIHlvdXIgZGV2aWNlJ3MgaGFyZHdhcmUuKg0KDQojIyMgDQoNClVzaW5nIHRoZSBjb2RlIGluIFxAcmVmKHRyQ29udHJvbCkgYWJvdmUgYXMgYSBndWlkZSwgdHJhaW4gYSBuZXcgcmFuZG9tIGZvcmVzdCBtb2RlbCB0aGF0IHVzZXMgdGhlIGBjdmAgcmVzYW1wbGluZyBtZXRob2QgYW5kIHRoZSB0dW5pbmcgc3BlY2lmaWNhdGlvbnMgdXNlZCBpbiBcQHJlZihyZnR1bmVkKS4NCg0KTmFtZSB5b3VyIG91dHB1dCBgcmVkX3dpbmVfcmZfdHVuZWRfY3ZgLg0KDQojIyMNCg0KQ29tcGFyZSB5b3VyIHJlc3VsdHMgZm9yIHlvdXIgdGhyZWUgcmFuZG9tIGZvcmVzdCBtb2RlbHMgYHJlZF93aW5lX3JmYCwgYHJlZF93aW5lX3JmX3R1bmVkYCBhbmQgYHJlZF93aW5lX3JmX3R1bmVkX2N2YC4NCldoaWNoIG9mIHlvdXIgdGhyZWUgYXBwcm9hY2hlcyByZXN1bHRzIGluIHRoZSBiZXN0IHBlcmZvcm1pbmcgcmFuZG9tIGZvcmVzdCBtb2RlbD8gDQpOb3RlIHRoZSBzcGVjaWZpYyB0dW5pbmcgcGFyYW1ldGVyIHZhbHVlcyBhbmQgcmVzYW1wbGluZyBtZXRob2QgdGhhdCBsZWQgdG8gdGhlIGJlc3QgcmVzdWx0cy4NCg0KIyMjIHsjcmZwbG90c30NCg0KRm9yIHJhbmRvbSBmb3Jlc3QgbW9kZWxzLCB3ZSBjYW4gZWFzaWx5IHByb2R1Y2UgdHdvIGhlbHBmdWwgcGxvdHM6DQoNCiogQSBwbG90IG9mIHRoZSBtb2RlbCBhY2N1cmFjeSBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIGZlYXR1cmUgdmFyaWFibGVzIHVzZWQgZm9yIGVhY2ggZGVjaXNpb24gdHJlZSBpbiB0aGUgcmFuZG9tIGZvcmVzdCBtb2RlbCRee1xkYWdnZXJ9JA0KKiBBIHBsb3Qgb2YgdGhlIGltcG9ydGFuY2Ugb2YgZWFjaCBmZWF0dXJlIHZhcmlhYmxlIGluIGFjaGlldmluZyBhbiBhY2N1cmF0ZSBtb2RlbA0KDQpSdW4gdGhlIGZvbGxvd2luZyBSIGNvZGUgdG8gcHJvZHVjZSB0aGVzZSBwbG90cyBub3csIGZvciB5b3VyIGluaXRpYWwgcmFuZG9tIGZvcmVzdCBtb2RlbDoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KZ2dwbG90KHJlZF93aW5lX3JmKQ0KDQpkb3RQbG90KHZhckltcChyZWRfd2luZV9yZikpDQpgYGANCg0KTm90ZSBmb3IgdGhlIHZhcmlhYmxlIGltcG9ydGFuY2UgZ3JhcGggdGhhdCB0aGUgYmVzdCAobW9zdCBoZWxwZnVsKSB2YXJpYWJsZSBpcyBhbHdheXMgZ2l2ZW4gYW4gaW1wb3J0YW5jZSBvZiAxMDAgYW5kIHRoZSB3b3JzdCBpcyBnaXZlbiBhbiBpbXBvcnRhbmNlIG9mIDAuIFRoaXMgaXMgbm90IHRvIHNheSB0aGF0IHRoZSB2YXJpYWJsZSB3aXRoIGltcG9ydGFuY2UgcmFua2luZyAwIGlzIHVzZWxlc3MgLSBpdCBpcyBqdXN0IGNvbnNpZGVyZWQgbGVzcyB1c2VmdWwgdGhhbiB0aGUgb3RoZXIgdmFyaWFibGVzLg0KDQpXaGF0IGZlYXR1cmUgdmFyaWFibGVzIGFyZSBjb25zaWRlcmVkIG1vc3QgaW1wb3J0YW50Pw0KDQokXntcZGFnZ2VyfSQgKlJlY2FsbCBmcm9tIHNlY3Rpb24gNC4wLjEgb2YgdGhlIFtJbnRyb2R1Y3Rpb24gdG8gTWFjaGluZSBMZWFybmluZyBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21fdDFfaW50cm9kdWN0aW9uX3RvX21hY2hpbmVfbGVhcm5pbmdfaW5fci9tYWNoaW5lLWxlYXJuaW5nLW1vZGVsLWNsYXNzZXMuaHRtbCN0cmVlLW1vZGVscykgdGhhdCByYW5kb20gZm9yZXN0IG1vZGVscyBhcmUgYSB0eXBlIG9mIGVuc2VtYmxlIG1ldGhvZCwgYW5kIGNvbWJpbmUgbXVsdGlwbGUgZGVjaXNpb24gdHJlZXMgdG9nZXRoZXIuKg0KDQojIyMNCg0KQ3JlYXRlIHRoZSBwbG90cyBkaXNjdXNzZWQgaW4gXEByZWYocmZwbG90cykgZm9yIHlvdXIgb3RoZXIgdHdvIHJhbmRvbSBmb3Jlc3QgbW9kZWxzLCBhbmQgY29tbWVudCBvbiB5b3VyIHJlc3VsdHMuDQoNCiMjIEdyYWRpZW50IEJvb3N0aW5nIE1hY2hpbmUgTW9kZWxzDQoNCkFub3RoZXIgZW5zZW1ibGUgbWV0aG9kIHRoYXQgY29tYmluZXMgbXVsdGlwbGUgZGVjaXNpb24gdHJlZXMgaXMgdGhlIGdyYWRpZW50IGJvb3N0aW5nIG1hY2hpbmUgbW9kZWwuDQpUaGVyZSBhcmUgc2V2ZXJhbCBvcHRpb25zIGZvciB0cmFpbmluZyBhIGdyYWRpZW50IGJvb3N0aW5nIG1hY2hpbmUgbW9kZWwgaW4gUi4gDQpXZSB3aWxsIHVzZSB0aGUgc3RvY2hhc3RpYyBncmFkaWVudCBib29zdGluZyBtZXRob2QgYGdibWAuIA0KDQpVc2luZyB0aGUgYGdibWAgYXJndW1lbnQgd2l0aGluIHRoZSBgdHJhaW5gIGZ1bmN0aW9uLCB0cmFpbiBhIGdyYWRpZW50IGJvb3N0aW5nIG1hY2hpbmUgbW9kZWwsIGFuZCBuYW1lIHlvdXIgbW9kZWwgYHJlZF93aW5lX2Jvb3N0ZWRgLg0KDQpBZ2FpbiwgZG9uJ3Qgd29ycnkgaWYgaXQgdGFrZXMgYSBjb3VwbGUgb2YgbWludXRlcyBmb3IgeW91ciBjb2RlIHRvIHJ1biAtIHRoaXMgaXMgbm9ybWFsLg0KDQoqTm90ZTogWW91IGNhbiBhbHNvIGluY2x1ZGUgdGhlIGFyZ3VtZW50IGB2ZXJib3NlID0gRmAgd2l0aGluIHRoZSBgdHJhaW5gIGZ1bmN0aW9uIHNvIG5vIGNhbGN1bGF0aW9ucyBhcmUgc2hvd24gd2hpbGUgdGhlIG1vZGVsIGlzIGJlaW5nIGZpdHRlZC4gQWx0ZXJuYXRpdmVseSwgaWYgeW91IHdvdWxkIGxpa2UgdG8gc2VlIHRoZSBtb2RlbCBmaXR0aW5nIGluIGFjdGlvbiBpbiB0aGUgUiBjb25zb2xlLCB5b3UgY2FuIGxlYXZlIHRoaXMgYXJndW1lbnQgb3V0LioNCg0KIyMjDQoNCldoYXQgaXMgdGhlIGJlc3QgYWNjdXJhY3kgYWNoaWV2ZWQgYnkgdGhlIGdyYWRpZW50IGJvb3N0aW5nIG1hY2hpbmUgbWV0aG9kPw0KDQojIyMgeyNnYm10dW5lZH0NCg0KRm9yIHRoZSBncmFkaWVudCBib29zdGluZyBtYWNoaW5lIG1vZGVsLCB0aGUgbWFpbiB0dW5pbmcgcGFyYW1ldGVycyBhcmU6DQoNCiogYG4udHJlZXNgICh0aGUgbnVtYmVyIG9mIGl0ZXJhdGlvbnMgaS5lLiB0aGUgbnVtYmVyIG9mIGRlY2lzaW9uIHRyZWVzIHVzZWQpIGFuZCANCiogYGludGVyYWN0aW9uLmRlcHRoYCAodGhlIGNvbXBsZXhpdHkgb2YgdGhlIHRyZWVzKS4NCg0KVGhlIGNvZGUgY2h1bmsgYmVsb3cgY29udGFpbnMgcGFydGlhbGx5IGNvbXBsZXRlZCBjb2RlIGZvciBhIHR1bmVkIGdyYWRpZW50IGJvb3N0aW5nIG1hY2hpbmUgbW9kZWwsIHdpdGggdGhlIGB0dW5lR3JpZGAgYXJndW1lbnQgaW5jb3Jwb3JhdGVkIGludG8gdGhlIGB0cmFpbmAgZnVuY3Rpb24uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnJlZF93aW5lX2Jvb3N0ZWRfdHVuZWQgPC0gdHJhaW4oLi4uICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IC4uLiAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9IC4uLiAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gZXhwYW5kLmdyaWQoaW50ZXJhY3Rpb24uZGVwdGggPSAzOjYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbi50cmVlcyA9IHNlcSg1MCwgMjAwLCA1MCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hyaW5rYWdlID0gMC4xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4ubWlub2JzaW5ub2RlID0gMTApDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KYGBgDQoNCkhlcmU6DQoNCiogV2UgaGF2ZSBzcGVjaWZpZWQgdGhhdCB0aGUgaW50ZXJhY3Rpb24gZGVwdGggY2FuIGJlIGJldHdlZW4gMyBhbmQgNiwgcmF0aGVyIHRoYW4gYmV0d2VlbiAxIGFuZCAzICh1c3VhbGx5LCBncmVhdGVyIGRlcHRoIGxlYWRzIHRvIGJldHRlciBhY2N1cmFjeSwgYWx0aG91Z2ggd2UgaGF2ZSB0byBiZSBjYXJlZnVsIG5vdCB0byBvdmVyZml0KS4gDQoqIFdlIGhhdmUgYWxzbyBzcGVjaWZpZWQgdGhhdCB0aGUgbnVtYmVyIG9mIHRyZWVzIGNhbiBiZSA1MCwgMTAwLCAxNTAgb3IgMjAwIChpLmUuIDIwMCB0cmVlcyBpcyBub3cgb3VyIG1heGltdW0sIHJhdGhlciB0aGFuIDE1MCkuDQoqIE5vdGUgdGhhdCB0aGUgYXJndW1lbnRzIGBzaHJpbmthZ2VgIGFuZCBgbi5taW5vYnNpbm5vZGVgIG11c3QgYmUgaW5jbHVkZWQgd2l0aGluIHRoZSBgdHVuZUdyaWRgIGZ1bmN0aW9uLCBvdGhlcndpc2UgdGhlIHRyYWluaW5nIHdpbGwgZmFpbC4NClRoZSB2YWx1ZXMgZm9yIHRoZXNlIGFyZ3VtZW50cyBhcmUgc2ltcGx5IHRoZSBkZWZhdWx0IG9uZXMuDQoNCkZpbGwgaW4gdGhlIG1pc3NpbmcgYC4uLmAgZGV0YWlscyBpbiB0aGUgY29kZSBjaHVuayBhYm92ZSwgYW5kIG9uY2UgeW91IGFyZSBoYXBweSB3aXRoIHlvdXIgY29kZSwgdHJ5IHJ1bm5pbmcgaXQuDQoNCiMjIyANCg0KVXNpbmcgdGhlIGNvZGUgaW4gXEByZWYodHJDb250cm9sKSBhYm92ZSBhcyBhIGd1aWRlLCB0cmFpbiBhIG5ldyBncmFkaWVudCBib29zdGluZyBtYWNoaW5lIG1vZGVsIHRoYXQgdXNlcyB0aGUgYGN2YCByZXNhbXBsaW5nIG1ldGhvZCBhbmQgdGhlIHR1bmluZyBzcGVjaWZpY2F0aW9ucyB1c2VkIGluIFxAcmVmKGdibXR1bmVkKS4NCg0KTmFtZSB5b3VyIG91dHB1dCBgcmVkX3dpbmVfYm9vc3RlZF90dW5lZF9jdmAuDQoNCiMjIw0KDQpDb21wYXJlIHlvdXIgcmVzdWx0cyBmb3IgeW91ciB0aHJlZSBncmFkaWVudCBib29zdGluZyBtYWNoaW5lIG1vZGVscyBgcmVkX3dpbmVfYm9vc3RlZGAsIGByZWRfd2luZV9ib29zdGVkX3R1bmVkYCBhbmQgYHJlZF93aW5lX2Jvb3N0ZWRfdHVuZWRfY3ZgLg0KV2hpY2ggb2YgeW91ciB0aHJlZSBhcHByb2FjaGVzIHJlc3VsdHMgaW4gdGhlIGJlc3QgcGVyZm9ybWluZyBncmFkaWVudCBib29zdGluZyBtYWNoaW5lIG1vZGVsPyANCk5vdGUgdGhlIHNwZWNpZmljIHR1bmluZyBwYXJhbWV0ZXIgdmFsdWVzIGFuZCByZXNhbXBsaW5nIG1ldGhvZCB0aGF0IGxlZCB0byB0aGUgYmVzdCByZXN1bHRzLg0KDQojIyMNCg0KVG8gY29uY2x1ZGUgb3VyIGZvY3VzIG9uIGdyYWRpZW50IGJvb3N0ZWQgbWFjaGluZSB0cmVlIG1vZGVscywgdXNlIHRoZSBgcGxvdGAgZnVuY3Rpb24gdG8gdmlzdWFsaXNlIHRoZSByZXN1bHRzIG9mIHRoZSB0cmFpbmluZyBwcm9jZXNzLCBmb3IgeW91ciBiZXN0IHBlcmZvcm1pbmcgZ3JhZGllbnQgYm9vc3RpbmcgbWFjaGluZSBtb2RlbC4NCg0KIyMgQWRkaXRpb25hbCBNb2RlbHMNCg0KT3VyIGZvY3VzIGluIHRoaXMgbGFiIGhhcyBiZWVuIG9uIHRyZWUtYmFzZWQgbWFjaGluZSBsZWFybmluZyBtb2RlbHMsIGFzIHRoZXNlIGFyZSBmbGV4aWJsZSBhbmQgY2FuIHBlcmZvcm0gd2VsbCBpbiBhIHZhcmlldHkgb2YgY29udGV4dHMuIEhvd2V2ZXIsIHRoZXJlIGFyZSBwbGVudHkgbW9yZSBtb2RlbHMgdG8gY2hvb3NlIGZyb20sIGFuZCBzbyB3ZSB3aWxsIGJyaWVmbHkgaW50cm9kdWNlIGEgZmV3IG5ldyBvcHRpb25zIGJlbG93Og0KDQoqIExpbmVhciBEaXNjcmltaW5hbnQgQW5hbHlzaXMgTW9kZWwNCiogU3VwcG9ydCBWZWN0b3IgTWFjaGluZSBNb2RlbA0KKiBrLU5lYXJlc3QtTmVpZ2hib3VyIE1vZGVsDQoNCkZvciB0aGVzZSBtb2RlbHMsIHdlIHdpbGwgdXNlIHRoZSBkZWZhdWx0IHR1bmluZyBwYXJhbWV0ZXJzIGFuZCByZXNhbXBsaW5nIG1ldGhvZHMuDQoNCiMjIyBMREENCg0KRml0IGEgbGluZWFyIGRpc2NyaW1pbmFudCBhbmFseXNpcyBtb2RlbCB0byB5b3VyIGByZWRfd2luZV90cmFpbmAgZGF0YSB2aWEgdGhlIG1ldGhvZCBzcGVjaWZpY2F0aW9uIGBsZGFgLg0KDQpXaGF0IGlzIHRoZSBiZXN0IGFjY3VyYWN5IGFjaGlldmVkIGJ5IHRoaXMgbWV0aG9kPw0KDQojIyMgU1ZNDQoNCkZpdCBhIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmUgbW9kZWwgdG8geW91ciBgcmVkX3dpbmVfdHJhaW5gIGRhdGEgdmlhIHRoZSBtZXRob2Qgc3BlY2lmaWNhdGlvbiBgc3ZtTGluZWFyYCAodGhlcmUgYXJlIG90aGVyIG9wdGlvbnMsIGJ1dCB0aGVzZSBhcmUgbW9yZSBjb21wbGljYXRlZCBhbmQgdGFrZSBsb25nZXIgdG8gZXhlY3V0ZSkuIA0KDQpXaGF0IGlzIHRoZSBiZXN0IGFjY3VyYWN5IGFjaGlldmVkIGJ5IHRoaXMgbWV0aG9kPw0KIA0KKk5vdGU6IEluIG9yZGVyIHRvIGZpdCBhIHN2bSBtb2RlbCwgd2UgbmVlZCB0byBsb2FkIHRoZSBga2VybmxhYmAgcGFja2FnZS4gVGhpcyBzaG91bGQgaGF2ZSBiZWVuIGRvbmUgYWxyZWFkeSBpbiBcQHJlZihsb2FkKSwgYnV0IGlmIGFuIGVycm9yIGFwcGVhcnMgd2hlbiBmaXR0aW5nIHRoZSBtb2RlbCwganVzdCBkb3VibGUtY2hlY2sgdGhpcy4qDQoNCiMjIyBrTk4NCg0KVGhlIGZpbmFsIG1ldGhvZCB3ZSB3aWxsIHRyeSBpcyB0aGUgay1OZWFyZXN0LU5laWdoYm91cnMgbW9kZWwsIHdoaWNoIGlzIHNlbGVjdGVkIHZpYSB0aGUgbWV0aG9kIHNwZWNpZmljYXRpb24gYGtubmAuIEZpdCBhIGtOTiBtb2RlbCB0byB5b3VyIGByZWRfd2luZV90cmFpbmAgZGF0YS4NCg0KV2hhdCBpcyB0aGUgYmVzdCBhY2N1cmFjeSBhY2hpZXZlZCBieSB0aGlzIG1ldGhvZD8NCg0KIyBWYWxpZGF0aW5nIFJlc3VsdHMgeyN2YWx9DQoNCkdyZWF0IGpvYiEgV2UndmUgbm93IGZpdHRlZCA2IGRpZmZlcmVudCB0eXBlcyBvZiBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4gDQoNCkluIGFkZGl0aW9uIHRvIG9idGFpbmluZyBwcmVkaWN0aXZlIGFjY3VyYWN5IGVzdGltYXRlcyBmb3IgZWFjaCBvZiBvdXIgbW9kZWxzLCBpdCBpcyBpbXBvcnRhbnQgdG8gYWxzbyBjaGVjayBob3cgdGhlIG1vZGVscyBwZXJmb3JtIHdoZW4gcHJlc2VudGVkIHdpdGggb3VyIHZhbGlkYXRpb24gZGF0YS4NCklmIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWxzIHJlbWFpbnMgc2ltaWxhciwgdGhlbiB3ZSBjYW4gYmUgbW9yZSBjb25maWRlbnQgaW4gb3VyIG1vZGVscycgcmVwb3J0ZWQgcGVyZm9ybWFuY2VzLg0KDQpSZWNhbGwgdGhhdCB3ZSBjYXJyaWVkIG91dCB0aGlzIGNoZWNrIGluIFtDb21wdXRlciBMYWIgMTBCXShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDEwX1MpXltUaGlzIGFwcHJvYWNoIHdhcyBhbHNvIGRlbW9uc3RyYXRlZCBpbiBbc2VjdGlvbiA0LjEgb2YgdGhlIEludHJvZHVjdGlvbiB0byBNYWNoaW5lIExlYXJuaW5nIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV90MV9pbnRyb2R1Y3Rpb25fdG9fbWFjaGluZV9sZWFybmluZ19pbl9yL21hY2hpbmUtbGVhcm5pbmctbW9kZWwtY2xhc3Nlcy5odG1sI2V4YW1wbGUtLS1ncmFkaWVudC1ib29zdGluZy1tYWNoaW5lLW1vZGVsKSkuXS4NCg0KQW4gZXhhbXBsZSBhcHBsaWNhdGlvbiBvZiB0aGlzIGFwcHJvYWNoIHRvIHRoZSBkZWNpc2lvbiB0cmVlIG1vZGVsIHJlc3VsdHMgaXMgc2hvd24gYmVsb3c6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCiMgTG9hZCBtYWdyaXR0ciBwYWNrYWdlIGZvciBwaXBpbmcNCmxpYnJhcnkobWFncml0dHIpDQoNCiMgY291bnQgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiB2YWxpZGF0aW9uIGRhdGENCnZhbGlkYXRpb25fbnVtYmVycyA8LSBkaW0ocmVkX3dpbmVfdmFsaWRhdGUpWzFdDQoNCiMgVXNlIHRoZSBmaXR0ZWQgbW9kZWwgdG8gcHJlZGljdCBxdWFsaXR5IHZhbHVlcyBnaXZlbiB0aGUgdmFsaWRhdGlvbiBkYXRhDQpwcmVkaWN0X3JlZF93aW5lX2RlY190cmVlIDwtIHByZWRpY3QocmVkX3dpbmVfZGVjX3RyZWUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPXJlZF93aW5lX3ZhbGlkYXRlKQ0KIyBXaGVuIHJ1biwgdGhlIGNvZGUgYmVsb3cgZ2l2ZXMgdXMgdGhlIHBlcmNlbnRhZ2Ugb2YgY29ycmVjdCBwcmVkaWN0aW9ucw0KZGVjX3RyZWVfYWNjdXJhY3kgPC0gc3VtKHByZWRpY3RfcmVkX3dpbmVfZGVjX3RyZWUgPT0gcmVkX3dpbmVfdmFsaWRhdGUkcXVhbGl0eSkgLyANCiAgICAgICAgICAgICAgICAgICAgIHZhbGlkYXRpb25fbnVtYmVycyAlPiUgcm91bmQoMikgKiAxMDANCmRlY190cmVlX2FjY3VyYWN5DQpgYGANCg0KIyMNCg0KRm9yIGVhY2ggb2YgdGhlIGRpZmZlcmVudCB0eXBlcyBvZiBtb2RlbHMgeW91IHVzZWQgaW4gXEByZWYobWwpLCBzZWxlY3QgdGhlIG1vZGVsIHRoYXQgaGFkIHRoZSBiZXN0IHBlcmZvcm1hbmNlLiBGb3IgZXhhbXBsZSwgaWYgeW91ciBgcmVkX3dpbmVfZGVjX3RyZWVfdHVuZWRgIHdhcyB0aGUgYmVzdCBwZXJmb3JtaW5nIG1vZGVsIG91dCBvZiB0aGUgdGhyZWUgZGVjaXNpb24gdHJlZSBtb2RlbHMsIHNlbGVjdCB0aGF0IG1vZGVsLiBUaGlzIHNob3VsZCBsZWFkIHlvdSB0byBoYXZlIDYgc2VsZWN0ZWQgbW9kZWxzIG92ZXJhbGwgLSBvbmUgZm9yIGVhY2ggbW9kZWwgdHlwZS4NCg0KRm9sbG93aW5nIHRoZSBwcm9jZXNzIG91dGxpbmVkIGFib3ZlIGluIFxAcmVmKHZhbCksIHVzZSB5b3VyIGByZWRfd2luZV92YWxpZGF0ZWAgZGF0YSB0byBhc3Nlc3MgdGhlIHByZWRpY3RpdmUgYWNjdXJhY3kgb2YgeW91ciA2IHNlbGVjdGVkIG1vZGVscy4NCg0KV2hpY2ggb2YgdGhlc2UgbW9kZWxzIGhhcyB0aGUgYmVzdCBwZXJmb3JtYW5jZSB3aXRoIHRoZSB2YWxpZGF0aW9uIGRhdGE/DQoNCiMgU3VtbWFyaXNpbmcgUmVzdWx0cyB7I3N1bX0NCg0KTm93IHRoYXQgd2UgaGF2ZSB0cmllZCBmaXR0aW5nIGFuZCB2YWxpZGF0aW5nIGEgc2VsZWN0aW9uIG9mIHBvcHVsYXIgbWFjaGluZSBsZWFybmluZyBtb2RlbHMsIHdlIG1pZ2h0IGxpa2UgdG8gc3VtbWFyaXNlIG91ciBmaW5kaW5nczsgdGhpcyBjYW4gbWFrZSBpdCBlYXNpZXIgdG8gY29tcGFyZSB0aGUgcmVzdWx0cyBvZiB0aGUgZGlmZmVyZW50IG1ldGhvZHMuDQoNCiMjDQoNCk1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzIHRyYWluZWQgdXNpbmcgdGhlIGB0cmFpbmAgZnVuY3Rpb24gdGVzdCBjb21iaW5hdGlvbnMgb2YgdHVuaW5nIHBhcmFtZXRlcnMgdG8gdHJ5IHRvIGZpbmQgYSBjb21iaW5hdGlvbiBsZWFkaW5nIHRvIGEgYWNjdXJhdGUgcHJlZGljdGlvbi4gQnkgZGVmYXVsdCwgMjUgKnJlc2FtcGxlcyogYXJlIGNvbmR1Y3RlZCBvbiB0aGUgdHJhaW5pbmcgZGF0YSwgdXNpbmcgZGlmZmVyZW50IHR1bmluZyBwYXJhbWV0ZXIgY29tYmluYXRpb25zIC0geW91IG1pZ2h0IGhhdmUgbm90aWNlZCB0aGUgbGluZSBgUmVzYW1wbGluZzogQm9vdHN0cmFwcGVkICgyNSByZXBzKWAgaW4gdGhlIG1vZGVsIG91dHB1dC4NCg0KSG93ZXZlciwgaWYgd2UgaGF2ZSBzcGVjaWZpZWQgdGhlIGBjdmAgcmVzYW1wbGluZyBtZXRob2QsIG9ubHkgMTAgKnJlc2FtcGxlcyogd2lsbCBoYXZlIGJlZW4gdGFrZW4uDQoNClVzaW5nIHRoZSBpbmNvbXBsZXRlIFIgY29kZSBpbiB0aGUgYENvZGVgIGNodW5rIGJlbG93LCBjcmVhdGUgdHdvIGxpc3RzIHRvIHN1bW1hcmlzZSB0aGUgcmVzdWx0cyBvZiB5b3VyIGRpZmZlcmVudCBtYWNoaW5lIGxlYXJuaW5nIG1vZGVsczoNCg0KKipOb3RlOiBXZSBuZWVkIHR3byBsaXN0cywgb25lIGZvciBib290c3RyYXAgcmVzYW1wbGUgcmVzdWx0cywgYW5kIG9uZSBmb3IgY3Jvc3MtdmFsaWRhdGlvbiByZXNhbXBsZSByZXN1bHRzLCBzaW5jZSB0aGUgbnVtYmVyIG9mIHJlc2FtcGxlcyB1c2VkIGZvciB0aGUgdHdvIG1ldGhvZHMgZGlmZmVycy4qKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpyZXN1bHRzX2Jvb3QgPC0gcmVzYW1wbGVzKGxpc3QoZGVjaXNpb25fdHJlZSA9IHJlZF93aW5lX2RlY190cmVlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWNpc2lvbl90cmVlX3R1bmVkID0gcmVkX3dpbmVfZGVjX3RyZWVfdHVuZWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tX2ZvcmVzdCA9IHJlZF93aW5lX3JmLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuLi4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KcmVzdWx0c19jdiA8LSByZXNhbXBsZXMobGlzdChkZWNpc2lvbl90cmVlX3R1bmVkX2N2ID0gcmVkX3dpbmVfZGVjX3RyZWVfdHVuZWRfY3YsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbV9mb3Jlc3RfdHVuZWRfY3YgPSByZWRfd2luZV9yZl90dW5lZF9jdiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLi4uDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgICAgICAgICAgICAgICAgICkNCg0Kc3VtbWFyeShyZXN1bHRzX2Jvb3QpDQouLi4NCmBgYA0KDQoqTm90ZTogVGhlIGNvbHVtbiBvZiBpbnRlcmVzdCBoZXJlIGlzIGFjdHVhbGx5IHRoZSBgTWVhbmAgY29sdW1uICh0aGUgYXZlcmFnZSBhY2N1cmFjeSBhY2hpZXZlZCBieSB0aGUgbW9kZWwgb3ZlciBhbGwgdGhlIHJlc2FtcGxlcyksIG5vdCB0aGUgYE1heC5gIGNvbHVtbi4qDQoNCiMjDQoNCkFwcGx5IHRoZSBgZG90cGxvdGAgZnVuY3Rpb24gdG8gdGhlIGByZXN1bHRzX2Jvb3RgIGFuZCBgcmVzdWx0c19jdmAgb2JqZWN0cyB0byBwbG90IHRoZSByYW5nZSBvZiBhY2N1cmFjeSB2YWx1ZXMgZm9yIHRoZSBkaWZmZXJlbnQgbWV0aG9kcy4NCg0KIyMNCg0KVG8gY29uY2x1ZGUsIHdyaXRlIGEgYnJpZWYsIHNpbXBsZSBzdW1tYXJ5IG9mIHlvdXIgZmluZGluZ3MsIGV4cGxhaW5pbmcgeW91ciBwcm9jZXNzIGFuZCB0aGUgcmVzdWx0cyBvZiB5b3VyIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLg0KT3ZlcmFsbCwgYmFzZWQgb24gdGhlIHJlc3VsdHMgb2J0YWluZWQgYW5kIHRoZSB0cmFpbmluZyBwcm9jZXNzZXMgaW52b2x2ZWQsIGRvIHlvdSBoYXZlIGEgcHJlZmVyZW5jZSBmb3Igb25lIG9mIHRoZSBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscyB3ZSBoYXZlIHVzZWQgaW4gdGhpcyBjb21wdXRlciBsYWI/DQoNCjxicj4NCg0KIyMjIyBXZWxsIGRvbmUsIHRoYXQgY29uY2x1ZGVzIG91ciB3b3JrIGluIG1hY2hpbmUgbGVhcm5pbmcuICMjIyMgey19DQoNCkhvcGVmdWxseSB0aGlzIGxhYiBoYXMgZW5oYW5jZWQgeW91ciB1bmRlcnN0YW5kaW5nIG9mIGhvdyB0byB1c2UgUiBmb3IgbWFjaGluZSBsZWFybmluZy4gVGhpcyBpcyBqdXN0IHRoZSBiZWdpbm5pbmcgLSB0aGVyZSBhcmUgc28gbWFueSBkaWZmZXJlbnQgbW9kZWxzIGFuZCBtZXRob2RzIG91dCB0aGVyZSENCg0KPGJyPg0KDQojIFJlZmVyZW5jZXMgey0gI1JlZn0NCjxkaXYgaWQ9InJlZnMiPjwvZGl2Pg0KDQo8YnI+DQoNCjxmb250IGNvbG9yID0gImdyZXkiPg0KVGhlc2Ugbm90ZXMgaGF2ZSBiZWVuIHByZXBhcmVkIGJ5IFJ1cGVydCBLdXZla2UuIFBsZWFzZSBub3RlIHRoYXQgc29tZSBvZiB0aGUgY29udGVudCBpbiB0aGVzZSBub3RlcyBoYXMgYmVlbiBkZXZlbG9wZWQgZnJvbSBjb250ZW50IGluIEBNb2RTdGF0LiBUaGUgY29weXJpZ2h0IGZvciB0aGUgbWF0ZXJpYWwgaW4gdGhlc2Ugbm90ZXMgcmVzaWRlcyB3aXRoIHRoZSBhdXRob3JzIG5hbWVkIGFib3ZlLCB3aXRoIHRoZSBEZXBhcnRtZW50IG9mIE1hdGhlbWF0aWNhbCBhbmQgUGh5c2ljYWwgU2NpZW5jZXMgYW5kIHdpdGggTGEgVHJvYmUgVW5pdmVyc2l0eS4gQ29weXJpZ2h0IGluIHRoaXMgd29yayBpcyB2ZXN0ZWQgaW4gTGEgVHJvYmUgVW5pdmVyc2l0eSBpbmNsdWRpbmcgYWxsIExhIFRyb2JlIFVuaXZlcnNpdHkgYnJhbmRpbmcgYW5kIG5hbWluZy4gVW5sZXNzIG90aGVyd2lzZSBzdGF0ZWQsIG1hdGVyaWFsIHdpdGhpbiB0aGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgYSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbiBDb21tZXJjaWFsLU5vbiBEZXJpdmF0aXZlcyBMaWNlbnNlIA0KPGEgaHJlZiA9ICJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMtbmQvNC4wL0NDIiB0YXJnZXQ9Il9ibGFuayI+IEJZLU5DLU5ELiA8L2E+DQo8L2ZvbnQ+