Preparations
🏡 Before we proceed, please make sure you have completed the first ML Data Science Computer Lab and read most of the content in the Introduction to Machine Learning in R supplement - if you haven’t, please spend some time now completing this material, as otherwise these lab questions will be unnecessarily difficult to understand and complete.
It may also be helpful to keep this content open in a separate tabs while you work through the lab material.
Load Required Packages
🏡 Run the R code below to load the R packages required for this lab:
library(caret)
library(rpart.plot)
Note: You should already have the caret
, magrittr
and rpart.plot
packages installed. If for whatever reason you do not, please install them using the code below:
# Install packages
install.packages("caret", "magrittr", "rpart.plot")
# Load packages
library(caret)
library(rpart.plot)
Wine Data
🏡 In this lab, we will assess a new data set - data on Portuguese Vinho Verde wine, obtained from the UCI Machine Learning Repository (2009) and originally collected by Cortez et al. (2009).
This is a real data set that has been referenced in dozens of academic research articles.
This 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
For privacy reasons, other feature and outcome variables like grape_type
, brand
and sale price
are not available (but would be very interesting to consider!).
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).
This data is split into two sets - one for red wine and one for white wine. We will assess only the red wine data set, which is stored in the file winequality_red.csv
.
🏡 You can download the file winequality_red.csv
from LMS - please do so now.
Once you have downloaded this data set, load the winequality_red.csv
data into RStudio and save it in the object red_wine
.
Hint: You can check the code chunk below for guidance. This code assumes your data is saved in your local working directory.
red_wine <- read.csv(file = "winequality_red.csv", header = T)
Aim
🏡 Our aim is to use the winequality_red.csv
data to train a machine learning model which can accurately predict the quality of a red wine, based on the feature variable inputs.
💻 In ML classification problems, it is important that our outcome variable is treated as a factor, rather than a continuous numeric variable.
In previous content, this has not been an issue (i.e. we had the distinct penguin species
).
However, with the red_wine
data, remember that the quality
scores only take integer values. Therefore we need to ensure that the quality
variable values are treated as factors, rather than as numbers (otherwise our model might end up predicting quality scores of e.g. 7.25).
Therefore, make sure to run the code below before proceeding further:
red_wine$quality <- as.factor(red_wine$quality)
Data Visualisation
💻 Before we start analysing the data, it is prudent to take a quick look at it.
💻 To begin, use the head
, summary
and dim
R functions to obtain details about the composition of the data. Do you notice any interesting details?
🎧 Online students
💬 Enter your answer next to the question on the shared jamboard.
💻 Use the plot
function to produce a bar plot of the quality
variable values - what do you notice?
Based on the observed values for this variable, can you think of any potential problems we might encounter when trying to predict certain quality values?
Note: Don’t worry if you’re not sure about this yet - we are still just starting to learn about machine learning.
🎧 Online students
💬 Enter your answer next to the question on the shared jamboard.
💻 Recall that we can use the featurePlot
function from the caret
package to visualise our data.
Rather than creating scatter plots, we can specify plot = "box"
to produce box plots for all the feature variables in our data set.
Run the code below to do this:
featurePlot(x = red_wine[, -12],
y = red_wine$quality,
plot = "box")
Note: The numbers along the bottom of the plots refer to the quality
scores.
💻 You will notice that it’s hard to see most of the box plots, because two feature variables, free.sulfur.dioxide
and total.sulfur.dioxide
, have much larger values than the other feature variables. While this isn’t necessarily a problem (we can just produce box plots for each of the feature variables in turn), there is a method to address this, which we will discuss in the next section.
For the moment however, try to recreate these box plots, without including the feature variables free.sulfur.dioxide
and total.sulfur.dioxide
.
Hint: Note how in the featurePlot
code above, we have used x = red_wine[, -12]
to specify that the 12th column (quality) should not be included in the plotted x variables - you can use this as a guide on how to ignore free.sulfur.dioxide
and total.sulfur.dioxide
. Check the code below if you are stuck:
featurePlot(x = red_wine[, -c(6,7,12)],
y = red_wine$quality,
plot = "box")
Pre-Processing
💻 Before we begin fitting a machine learning model, we should conduct some pre-processing checks. Having completed the first ML Data Science Computer Lab, most of the following steps should be familiar to you.
As we work through this section though, you may like to refer to the content presented in Section 3.2 of the Introduction to Machine Learning in R supplement.
💻 Since all our feature variables are numeric, there is no need to create any dummy variables for our data set.
Highly Influential Samples
💻 Recall that we can use the function nearZeroVar
from the caret
package to obtain details on the freqRatio
and percentUnique
values for each of the variables in our red_wine
data set.
Further, recall that the nearZeroVar
function can include additional arguments, freqCut
and uniqueCut
, that specify cut-off values for the freqRatio
and percentUnique
results respectively.
Use the nearZeroVar
function to assess the feature variables in the red_wine
data set, and specify:
- A cut-off value of 2 for the
freqRatio
values
- A cut-off value of 5 for the
percentUnique
values.
Run the nearZeroVar
function twice, once with saveMetrics = T
and once with saveMetrics = F
.
Hint: If you are not sure how to proceed, check sections 3.2.2 to 3.2.4 of the Introduction to Machine Learning in R supplement.
💻 Based on the nearZeroVar
function results, check for potentially problematic variables.
Which feature variable has the highest freqRatio
value, and which feature variable has the lowest percentUnique
value?
What do you conclude? Are there any problematic feature variables?
🎧 Online students
💬 Enter your answer next to the question on the shared jamboard.
💻 Next, we should check for correlated feature variables. Remember that it is often beneficial to remove highly correlated feature variables from our data.
Use the cor
function to compute a correlation matrix for the red_wine
feature variables. Assess the spread of correlation values, and check for extreme correlations close to 1 in magnitude.
Hint: You can follow the steps in section 3.2.5 of the Introduction to Machine Learning in R supplement for this question.
💻 What are the largest negative and positive correlation values? Do these seem problematic?
💻 Based on your calculations, do you think we need to use the findCorrelation
function to identify highly correlated feature variables to remove from our data set? Why or why not?
Note: For the remainder of this lab, we will assume no feature variables needed to be removed.
🎧 Online students
💬 Enter your answer next to the question on the shared jamboard.
💻 One aspect of pre-processing that we did not discuss in the supplementary material is the concept of modifying our data values, to ensure smoother comparisons between different feature variables.
Similarly to the normalisation processes conducted in earlier core computer labs, we can scale
and center
our wine data, using the caret
package function preProcess
.
Run the code below, to scale and center our red_wine
data, and then assign our updated data to the new object red_wine_updated
:
centre_scale <- preProcess(red_wine[, -12],
method = c("center", "scale"))
red_wine_updated <- predict(centre_scale, red_wine)
💻 Compare the original data to the updated data, using head(red_wine)
and head(red_wine_updated)
respectively. You should observe that the feature variable values are now scaled and centered.
💻 If we create box plots of the updated data red_wine_updated
, we observe that they are much easier to assess than the original box plots from 2.3:
featurePlot(x = red_wine_updated[, -12],
y = red_wine_updated$quality,
plot = "box",
auto.key = list(columns = 6))

Do any of the features seem to have much association with wine quality
? Do you notice a trend or pattern in the box plots for the different quality
values within any particular features?
🎧 Online students
💬 Enter your answer next to the question on the shared jamboard.
💻 Even though the box plots produced in 3.9 are clearer than those produced in section 2, it is still quite difficult to distinguish between the quality
ratings.
Replace the plot = "box"
argument in the code from 3.9 with plot = "pairs"
to produce scatter plots instead of box plots for our pre-processed data.
Do you notice any clear differences between the different quality
ratings?
🎧 Online students
💬 Enter your answer next to the question on the shared jamboard.
Training and Validation Data
💻 Our final preparatory step is to split our data into training and validation sets.
Use the createDataPartition
function from the caret
package to split the red_wine_updated
data 80/20.
Note: The data partitioning into training or validation categories is random to an extent, so if you do not run the set.seed(1650) commands shown in the
Code` chunks below, your results from this point onwards may differ slightly to those presented in the subsequent question solutions, since your training and validation data sets will most likely contain slightly different sets of observations.
The code below is partially completed, just fill in the ...
missing parts:
set.seed(1650)
wine_train_index <- createDataPartition(... ,
p = ... ,
list = FALSE, times = 1)
Hint: Remember that the argument p
denotes the split. If you are stuck, you can check the code chunk below:
set.seed(1650)
wine_train_index <- createDataPartition(red_wine_updated$quality,
p = .8, # here p designates the split - 80/20
list = FALSE, times = 1)
💻 Next, assign the red_wine_updated
data into the training and validation sets, and name these red_wine_train
and red_wine_validate
respectively. Check the code below for a head start:
red_wine_validate <- red_wine_updated[-wine_train_index, ]
Hint: If you are stuck, you can check the code chunk below:
# Note here we are using the values in the wine_train_index
# (whereas for the validation set, we select the values not in the wine_train_index)
red_wine_train <- red_wine_updated[wine_train_index, ]
Fitting a Decision Tree Machine Learning Model
💻 Now that the preparation phase is complete, we are ready to fit machine learning models using our red_wine_updated
data.
Recall that the basic code framework to fit a model using the train
function is as follows:
object <- train(... ~ ., # specify relationship between outcome and feature variables
data = ... , # specify training data
method = "specify method here")
Decision Tree
💻 One of the simplest machine learning models we can use is a decision tree.
Using the information in 4, and the partially complete code in the code chunk below, fit a decision tree to your pre-processed red_wine_train
training data.
set.seed(1650)
red_wine_decision_tree <- train(... ~ .,
data = ...,
method = "rpart")
Once you are happy with your code, run it, and then run the object red_wine_decision_tree
to see the output. Your output should look like the output in the code chunk below:
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.01221167 0.5737107 0.3033509
0.02374491 0.5657458 0.2697084
0.25237449 0.4719068 0.1116919
Accuracy was used to select the optimal model using the largest value.
The final value used for the model was cp = 0.01221167.
We are mainly interested here in the Accuracy
values, for different tuning parameter values (we can ignore the Kappa
values).
Unfortunately, the best accuracy achieved was only 57.37%, which is not much better than randomly guessing.
Note: Don’t worry if your results look slightly different - perhaps you did not run all the set.seed(1650)
commands?
💻 Run the following code to visualise this decision tree model:
rpart.plot(red_wine_decision_tree$finalModel)
Note: The rpart.plot
package required here should have been installed and loaded in 1.
💻 As you should be able to see from the decision tree diagram, the model did not have enough data on wines of low or high quality, and therefore is only able to make predictions between wines of quality 5
or 6
- this explains the disappointing overall predictive accuracy of the model.
This is the problem we were alluding to in 2.2. Our previous work using the penguins
data was an exception - usually the data is not so nicely partitioned.
Fitting a Random Forest Machine Learning Model
💻 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. Let’s see if we can obtain a better predictive accuracy using an ensemble model tree method.\(^{\dagger}\)
\(^{\dagger}\) Refer to section 4.2.1.2 of the Introduction to Machine Learning in R supplement for details.
Training the Random Forest Model
💻 Training a random forest model in RStudio is quite simple, once we understand the basics of the train
function.
Replace the method = "rpart"
part of your decision tree code from 4.1 with method = "rf"
.
This will tell the train
function to train a machine learning model using the random forest method (rf
).
Assign your output to the object red_wine_rf
.
Note: Don’t worry if it takes a couple of minutes for your code to run - this is normal for more complex models.
💻 Assess the red_wine_rf
object. What is the best accuracy achieved by the random forest method?
💻 Compare your results for your decision tree model and your random forest model. Which model achieves a higher predictive accuracy?
🎧 Online students
💬 Enter your answer next to the question on the shared jamboard.
Random Forest Plots
💻 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
- A plot of the importance of each feature variable in achieving an accurate model
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.
💻 Run the code below to produce plots for your red_wine_rf
random forest model:
What feature variables are considered most important?
ggplot(red_wine_rf)
dotPlot(varImp(red_wine_rf))
🎧 Online students
💬 Enter your answer next to the question on the shared jamboard.
Validating Results
💻 While we have predictive accuracy estimates for our decision tree and random forest models, it is important to remember that these have been computed using the training data.
We would also like to check how the models perform when presented with new data - i.e. our validation data!
Recall that when conducting machine learning, there is a risk of overfitting our models to our training data. This can result in the models having excellent accuracy when assessing the training data, but having subpar performance when presented with new data.
This is why we have put aside some of our data as validation data in 3.11, so that we can perform cross-validation.
If the accuracy of the model remains similar when presented with the validation data, then we can be more confident in our model’s reported performance.
💻 There are several ways to perform cross-validation. Run the code below to conduct a simple cross-validation check of the accuracy of the decision tree model obtained in 4.1.
# 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_decision_tree <- predict(red_wine_decision_tree,
newdata =red_wine_validate)
# When run, the code below gives us the percentage of correct predictions
dec_tree_accuracy <- sum(predict_red_wine_decision_tree ==
red_wine_validate$quality) / validation_numbers * 100
dec_tree_accuracy %>% round(2)
💻 Using the code in 6.1 as a guide, perform a simple cross-validation check of the accuracy of the random forest model you obtained in 5.1.
Discuss the results of the cross-validation. Do you think your models’ reported accuracies in 4.1 and 5.2 are reliable?
🎧 Online students
💬 Volunteer to share your screen and explain your answers to this question.
Great work, that’s everything for today!
Hopefully you are beginning to feel more comfortable conducting supervised machine learning in RStudio - as we can see, it’s actually not that complicated to train a machine learning model in RStudio (although getting a highly accurate one is often another story…).
In the next data science computer lab, we will continue our analysis of the red wine data, and see if we can improve our results by using different models and by adjusting training parameters.
LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiAxMEIiDQpvdXRwdXQ6DQogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjogDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KYmlibGlvZ3JhcGh5OiBTVE0xMDAxX0RTX0NMX3JlZmVyZW5jZXMuYmliIA0KbGluay1jaXRhdGlvbnM6IHllcw0KLS0tDQoNCjxzdHlsZT4NCiNUT0Mgew0KICBiYWNrZ3JvdW5kOiB1cmwoImh0dHBzOi8vd3d3LmxhdHJvYmUuZWR1LmF1L19tZWRpYS9sYS10cm9iZS1hcGkvdjUvaW1nL2xvZ28uc3ZnIik7DQogIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjsNCiAgcGFkZGluZy10b3A6IDgwcHggIWltcG9ydGFudDsNCiAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsNCn0NCjwvc3R5bGU+DQoNCiMjIyBEYXRhIFNjaWVuY2UgU3RyZWFtIHstfQ0KDQojIyMgVG9waWMgMTBCOiBNYWNoaW5lIExlYXJuaW5nIElJIHstfQ0KDQo8YnI+DQoNCldlbGNvbWUgdG8gdGhlIHRlbnRoIGNvbXB1dGVyIGxhYiBmb3IgdGhlIERhdGEgU2NpZW5jZSBzdHJlYW0gb2YgU1RNMTAwMS4NCkluIHRoaXMgY29tcHV0ZXIgbGFiIHdlIHdpbGwgY29uc29saWRhdGUgb3VyIHVuZGVyc3RhbmRpbmcgb2YgbWFjaGluZSBsZWFybmluZywgYW5kIGNvbnNpZGVyIGEgbmV3LCByZWFsIGRhdGEgc2V0Lg0KDQpUaGlzIGNvbXB1dGVyIGxhYiBpcyBkZXNpZ25lZCB0byBydW4gYWxvbmdzaWRlIHRoZSBjb250ZW50IGluIHRoZSBbSW50cm9kdWN0aW9uIHRvIE1hY2hpbmUgTGVhcm5pbmcgaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX2ludHJvZHVjdGlvbl90b19tYWNoaW5lX2xlYXJuaW5nX2luX3IvKS4NCg0KQnkgdGhlIGVuZCBvZiB0aGlzIGxhYiwgeW91IHNob3VsZCBmZWVsIGNvbWZvcnRhYmxlIHByZXBhcmluZyBkYXRhIGZvciBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcgdGFza3MuIFlvdSBzaG91bGQgYWxzbyBrbm93IGhvdyB0byBydW4gYW5kIGFzc2VzcyB0aGUgcGVyZm9ybWFuY2Ugb2YgYSBzaW1wbGUgbWFjaGluZSBsZWFybmluZyBtb2RlbC4gTGV0J3MgYmVnaW4hDQoNCjxicj4NCg0KIyBQcmVwYXJhdGlvbnMgeyNwcmVwfQ0KDQpgciBlbW86OmppKCJob3VzZV93aXRoX2dhcmRlbiIpYCBCZWZvcmUgd2UgcHJvY2VlZCwgcGxlYXNlIG1ha2Ugc3VyZSB5b3UgaGF2ZSBjb21wbGV0ZWQgW3RoZSBmaXJzdCBNTCBEYXRhIFNjaWVuY2UgQ29tcHV0ZXIgTGFiXShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDkpIGFuZCByZWFkIG1vc3Qgb2YgdGhlIGNvbnRlbnQgaW4gdGhlIFtJbnRyb2R1Y3Rpb24gdG8gTWFjaGluZSBMZWFybmluZyBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21faW50cm9kdWN0aW9uX3RvX21hY2hpbmVfbGVhcm5pbmdfaW5fci8pIC0gaWYgeW91IGhhdmVuJ3QsIHBsZWFzZSBzcGVuZCBzb21lIHRpbWUgbm93IGNvbXBsZXRpbmcgdGhpcyBtYXRlcmlhbCwgYXMgb3RoZXJ3aXNlIHRoZXNlIGxhYiBxdWVzdGlvbnMgd2lsbCBiZSB1bm5lY2Vzc2FyaWx5IGRpZmZpY3VsdCB0byB1bmRlcnN0YW5kIGFuZCBjb21wbGV0ZS4gDQoNCkl0IG1heSBhbHNvIGJlIGhlbHBmdWwgdG8ga2VlcCB0aGlzIGNvbnRlbnQgb3BlbiBpbiBhIHNlcGFyYXRlIHRhYnMgd2hpbGUgeW91IHdvcmsgdGhyb3VnaCB0aGUgbGFiIG1hdGVyaWFsLg0KDQojIyBMb2FkIFJlcXVpcmVkIFBhY2thZ2VzIHsjbG9hZH0NCg0KYHIgZW1vOjpqaSgiaG91c2Vfd2l0aF9nYXJkZW4iKWAgUnVuIHRoZSBSIGNvZGUgYmVsb3cgdG8gbG9hZCB0aGUgUiBwYWNrYWdlcyByZXF1aXJlZCBmb3IgdGhpcyBsYWI6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBpbmNsdWRlID0gRn0NCiMgU3BlY2lmeSByZXF1aXJlZCBwYWNrYWdlcw0KbWxfcGFja2FnZXMgPC0gYygiY2FyZXQiLCAibWFncml0dHIiLCAicnBhcnQucGxvdCIpDQojIEluc3RhbGwgbWlzc2luZyBwYWNrYWdlcw0KaW5zdGFsbC5wYWNrYWdlcyhzZXRkaWZmKG1sX3BhY2thZ2VzLCByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpKQ0KIyBMb2FkIGFsbCBwYWNrYWdlcw0KbGFwcGx5KG1sX3BhY2thZ2VzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQpgYGANCg0KKk5vdGU6IFlvdSBzaG91bGQgYWxyZWFkeSBoYXZlIHRoZSBgY2FyZXRgLCBgbWFncml0dHJgIGFuZCBgcnBhcnQucGxvdGAgcGFja2FnZXMgaW5zdGFsbGVkLiBJZiBmb3Igd2hhdGV2ZXIgcmVhc29uIHlvdSBkbyBub3QsIHBsZWFzZSBpbnN0YWxsIHRoZW0gdXNpbmcgdGhlIGNvZGUgYmVsb3c6Kg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQojIEluc3RhbGwgcGFja2FnZXMNCmluc3RhbGwucGFja2FnZXMoImNhcmV0IiwgIm1hZ3JpdHRyIiwgInJwYXJ0LnBsb3QiKQ0KIyBMb2FkIHBhY2thZ2VzDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KYGBgDQoNCiMjIFdpbmUgRGF0YQ0KDQpgciBlbW86OmppKCJob3VzZV93aXRoX2dhcmRlbiIpYCBJbiB0aGlzIGxhYiwgd2Ugd2lsbCBhc3Nlc3MgYSBuZXcgZGF0YSBzZXQgLSBkYXRhIG9uIFBvcnR1Z3Vlc2UgKlZpbmhvIFZlcmRlKiB3aW5lLCBvYnRhaW5lZCBmcm9tIHRoZSBAVUNJV2luZSBhbmQgb3JpZ2luYWxseSBjb2xsZWN0ZWQgYnkgQHdpbmUuDQpUaGlzIGlzIGEgcmVhbCBkYXRhIHNldCB0aGF0IGhhcyBiZWVuIHJlZmVyZW5jZWQgaW4gZG96ZW5zIG9mIGFjYWRlbWljIHJlc2VhcmNoIGFydGljbGVzXltbU2VlIGEgc2hvcnRsaXN0IGhlcmVdKGh0dHBzOi8vYXJjaGl2ZS5pY3MudWNpLmVkdS9tbC9kYXRhc2V0cy9XaW5lKV0uDQoNClRoaXMgZGF0YSBjb25zaXN0cyBvZiAxMSBmZWF0dXJlIHZhcmlhYmxlcyByZWxhdGluZyB0byBwaHlzaWNvY2hlbWljYWwgYXNwZWN0cyBvZiB0aGUgd2luZSwgbmFtZWx5Og0KDQoqIGZpeGVkIGFjaWRpdHkNCiogdm9sYXRpbGUgYWNpZGl0eQ0KKiBjaXRyaWMgYWNpZA0KKiByZXNpZHVhbCBzdWdhcg0KKiBjaGxvcmlkZXMNCiogZnJlZSBzdWxmdXIgZGlveGlkZQ0KKiB0b3RhbCBzdWxmdXIgZGlveGlkZQ0KKiBkZW5zaXR5DQoqIHBIDQoqIHN1bHBoYXRlcw0KKiBhbGNvaG9sDQoNCkZvciBwcml2YWN5IHJlYXNvbnMsIG90aGVyIGZlYXR1cmUgYW5kIG91dGNvbWUgdmFyaWFibGVzIGxpa2UgYGdyYXBlX3R5cGVgLCBgYnJhbmRgIGFuZCBgc2FsZSBwcmljZWAgYXJlIG5vdCBhdmFpbGFibGUgKGJ1dCB3b3VsZCBiZSB2ZXJ5IGludGVyZXN0aW5nIHRvIGNvbnNpZGVyISkuDQoNClRoZSBvbmx5IGF2YWlsYWJsZSBvdXRjb21lIHZhcmlhYmxlIGlzIGBxdWFsaXR5YCwgd2hpY2ggaXMgYW4gaW50ZWdlciBzY29yZSBmcm9tIDAgdG8gMTAgKHdpdGggMCBkZW5vdGluZyBhIHRlcnJpYmxlIHdpbmUgYW5kIDEwIGRlbm90aW5nIGFuIGV4Y2VwdGlvbmFsIHdpbmUpLg0KDQpUaGlzIGRhdGEgaXMgc3BsaXQgaW50byB0d28gc2V0cyAtIG9uZSBmb3IgcmVkIHdpbmUgYW5kIG9uZSBmb3Igd2hpdGUgd2luZS4gV2Ugd2lsbCBhc3Nlc3Mgb25seSB0aGUgcmVkIHdpbmUgZGF0YSBzZXQsIHdoaWNoIGlzIHN0b3JlZCBpbiB0aGUgZmlsZSBgd2luZXF1YWxpdHlfcmVkLmNzdmAuDQoNCiMjDQoNCmByIGVtbzo6amkoImhvdXNlX3dpdGhfZ2FyZGVuIilgIFlvdSBjYW4gZG93bmxvYWQgdGhlIGZpbGUgYHdpbmVxdWFsaXR5X3JlZC5jc3ZgIGZyb20gTE1TIC0gcGxlYXNlIGRvIHNvIG5vdy4NCg0KT25jZSB5b3UgaGF2ZSBkb3dubG9hZGVkIHRoaXMgZGF0YSBzZXQsIGxvYWQgdGhlIGB3aW5lcXVhbGl0eV9yZWQuY3N2YCBkYXRhIGludG8gUlN0dWRpbyBhbmQgc2F2ZSBpdCBpbiB0aGUgb2JqZWN0IGByZWRfd2luZWAuDQoNCipIaW50OiBZb3UgY2FuIGNoZWNrIHRoZSBjb2RlIGNodW5rIGJlbG93IGZvciBndWlkYW5jZS4gVGhpcyBjb2RlIGFzc3VtZXMgeW91ciBkYXRhIGlzIHNhdmVkIGluIHlvdXIgbG9jYWwgd29ya2luZyBkaXJlY3RvcnkuKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpyZWRfd2luZSA8LSByZWFkLmNzdihmaWxlID0gIndpbmVxdWFsaXR5X3JlZC5jc3YiLCBoZWFkZXIgPSBUKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gVCwgaW5jbHVkZSA9IEZ9DQpyZWRfd2luZSA8LSByZWFkLmNzdihmaWxlID0gImRhdGEvd2luZXF1YWxpdHlfcmVkLmNzdiIsIGhlYWRlciA9IFQpDQpgYGANCg0KIyMgQWltDQoNCmByIGVtbzo6amkoImhvdXNlX3dpdGhfZ2FyZGVuIilgIE91ciBhaW0gaXMgdG8gdXNlIHRoZSBgd2luZXF1YWxpdHlfcmVkLmNzdmAgZGF0YSB0byB0cmFpbiBhIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwgd2hpY2ggY2FuIGFjY3VyYXRlbHkgcHJlZGljdCB0aGUgcXVhbGl0eSBvZiBhIHJlZCB3aW5lLCBiYXNlZCBvbiB0aGUgZmVhdHVyZSB2YXJpYWJsZSBpbnB1dHMuDQoNCiMjIA0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBJbiBNTCBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtcywgaXQgaXMgaW1wb3J0YW50IHRoYXQgb3VyIG91dGNvbWUgdmFyaWFibGUgaXMgdHJlYXRlZCBhcyBhIGZhY3RvciwgcmF0aGVyIHRoYW4gYSBjb250aW51b3VzIG51bWVyaWMgdmFyaWFibGUuDQpJbiBwcmV2aW91cyBjb250ZW50LCB0aGlzIGhhcyBub3QgYmVlbiBhbiBpc3N1ZSAoaS5lLiB3ZSBoYWQgdGhlIGRpc3RpbmN0IHBlbmd1aW4gYHNwZWNpZXNgKS4NCg0KSG93ZXZlciwgd2l0aCB0aGUgYHJlZF93aW5lYCBkYXRhLCByZW1lbWJlciB0aGF0IHRoZSBgcXVhbGl0eWAgc2NvcmVzIG9ubHkgdGFrZSBpbnRlZ2VyIHZhbHVlcy4gVGhlcmVmb3JlIHdlIG5lZWQgdG8gZW5zdXJlIHRoYXQgdGhlIGBxdWFsaXR5YCB2YXJpYWJsZSB2YWx1ZXMgYXJlIHRyZWF0ZWQgYXMgZmFjdG9ycywgcmF0aGVyIHRoYW4gYXMgbnVtYmVycyAob3RoZXJ3aXNlIG91ciBtb2RlbCBtaWdodCBlbmQgdXAgcHJlZGljdGluZyBxdWFsaXR5IHNjb3JlcyBvZiBlLmcuIDcuMjUpLg0KDQpUaGVyZWZvcmUsIG1ha2Ugc3VyZSB0byBydW4gdGhlIGNvZGUgYmVsb3cgYmVmb3JlIHByb2NlZWRpbmcgZnVydGhlcjoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KcmVkX3dpbmUkcXVhbGl0eSA8LSBhcy5mYWN0b3IocmVkX3dpbmUkcXVhbGl0eSkNCmBgYA0KDQoNCiMgRGF0YSBWaXN1YWxpc2F0aW9uIHsjZGF0YXZpen0NCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgQmVmb3JlIHdlIHN0YXJ0IGFuYWx5c2luZyB0aGUgZGF0YSwgaXQgaXMgcHJ1ZGVudCB0byB0YWtlIGEgcXVpY2sgbG9vayBhdCBpdC4NCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgVG8gYmVnaW4sIHVzZSB0aGUgYGhlYWRgLCBgc3VtbWFyeWAgYW5kIGBkaW1gIFIgZnVuY3Rpb25zIHRvIG9idGFpbiBkZXRhaWxzIGFib3V0IHRoZSBjb21wb3NpdGlvbiBvZiB0aGUgZGF0YS4gRG8geW91IG5vdGljZSBhbnkgaW50ZXJlc3RpbmcgZGV0YWlscz8NCg0KPGRldGFpbHM+DQogIDxzdW1tYXJ5PmByIGVtbzo6amkoImhlYWRwaG9uZXMiKWAgKipPbmxpbmUgc3R1ZGVudHMqKjwvc3VtbWFyeT4NCmByIGVtbzo6amkoInNwZWVjaF9iYWxsb29uIilgICBFbnRlciB5b3VyIGFuc3dlciBuZXh0IHRvIHRoZSBxdWVzdGlvbiBvbiB0aGUgc2hhcmVkIGphbWJvYXJkLg0KPC9kZXRhaWxzPiANCg0KIyMgeyNwcm9ibGVtfQ0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBVc2UgdGhlIGBwbG90YCBmdW5jdGlvbiB0byBwcm9kdWNlIGEgYmFyIHBsb3Qgb2YgdGhlIGBxdWFsaXR5YCB2YXJpYWJsZSB2YWx1ZXMgLSB3aGF0IGRvIHlvdSBub3RpY2U/DQoNCkJhc2VkIG9uIHRoZSBvYnNlcnZlZCB2YWx1ZXMgZm9yIHRoaXMgdmFyaWFibGUsIGNhbiB5b3UgdGhpbmsgb2YgYW55IHBvdGVudGlhbCBwcm9ibGVtcyB3ZSBtaWdodCBlbmNvdW50ZXIgd2hlbiB0cnlpbmcgdG8gcHJlZGljdCBjZXJ0YWluIHF1YWxpdHkgdmFsdWVzPw0KDQoqTm90ZTogRG9uJ3Qgd29ycnkgaWYgeW91J3JlIG5vdCBzdXJlIGFib3V0IHRoaXMgeWV0IC0gd2UgYXJlIHN0aWxsIGp1c3Qgc3RhcnRpbmcgdG8gbGVhcm4gYWJvdXQgbWFjaGluZSBsZWFybmluZy4qDQoNCjxkZXRhaWxzPg0KICA8c3VtbWFyeT5gciBlbW86OmppKCJoZWFkcGhvbmVzIilgICoqT25saW5lIHN0dWRlbnRzKio8L3N1bW1hcnk+DQpgciBlbW86OmppKCJzcGVlY2hfYmFsbG9vbiIpYCAgRW50ZXIgeW91ciBhbnN3ZXIgbmV4dCB0byB0aGUgcXVlc3Rpb24gb24gdGhlIHNoYXJlZCBqYW1ib2FyZC4NCjwvZGV0YWlscz4gDQoNCiMjIHsjZmVhdHVyZXBsb3R9DQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIFJlY2FsbCB0aGF0IHdlIGNhbiB1c2UgdGhlIGBmZWF0dXJlUGxvdGAgZnVuY3Rpb24gZnJvbSB0aGUgYGNhcmV0YCBwYWNrYWdlIHRvIHZpc3VhbGlzZSBvdXIgZGF0YS4NClJhdGhlciB0aGFuIGNyZWF0aW5nIHNjYXR0ZXIgcGxvdHMsIHdlIGNhbiBzcGVjaWZ5IGBwbG90ID0gImJveCJgIHRvIHByb2R1Y2UgYm94IHBsb3RzIGZvciBhbGwgdGhlIGZlYXR1cmUgdmFyaWFibGVzIGluIG91ciBkYXRhIHNldC4NClJ1biB0aGUgY29kZSBiZWxvdyB0byBkbyB0aGlzOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGZpZy5kaW0gPSBjKDYsNil9DQpmZWF0dXJlUGxvdCh4ID0gcmVkX3dpbmVbLCAtMTJdLCANCiAgICAgICAgICAgIHkgPSByZWRfd2luZSRxdWFsaXR5LCANCiAgICAgICAgICAgIHBsb3QgPSAiYm94IikNCmBgYA0KDQoqTm90ZTogVGhlIG51bWJlcnMgYWxvbmcgdGhlIGJvdHRvbSBvZiB0aGUgcGxvdHMgcmVmZXIgdG8gdGhlIGBxdWFsaXR5YCBzY29yZXMuKg0KDQojIyB7I2ZlYXR1cmVwbG90c2ltcGxpZmllZH0NCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgWW91IHdpbGwgbm90aWNlIHRoYXQgaXQncyBoYXJkIHRvIHNlZSBtb3N0IG9mIHRoZSBib3ggcGxvdHMsIGJlY2F1c2UgdHdvIGZlYXR1cmUgdmFyaWFibGVzLCBgZnJlZS5zdWxmdXIuZGlveGlkZWAgYW5kIGB0b3RhbC5zdWxmdXIuZGlveGlkZWAsIGhhdmUgbXVjaCBsYXJnZXIgdmFsdWVzIHRoYW4gdGhlIG90aGVyIGZlYXR1cmUgdmFyaWFibGVzLiBXaGlsZSB0aGlzIGlzbid0IG5lY2Vzc2FyaWx5IGEgcHJvYmxlbSAod2UgY2FuIGp1c3QgcHJvZHVjZSBib3ggcGxvdHMgZm9yIGVhY2ggb2YgdGhlIGZlYXR1cmUgdmFyaWFibGVzIGluIHR1cm4pLCB0aGVyZSBpcyBhIG1ldGhvZCB0byBhZGRyZXNzIHRoaXMsIHdoaWNoIHdlIHdpbGwgZGlzY3VzcyBpbiB0aGUgbmV4dCBzZWN0aW9uLg0KDQpGb3IgdGhlIG1vbWVudCBob3dldmVyLCB0cnkgdG8gcmVjcmVhdGUgdGhlc2UgYm94IHBsb3RzLCB3aXRob3V0IGluY2x1ZGluZyB0aGUgZmVhdHVyZSB2YXJpYWJsZXMgYGZyZWUuc3VsZnVyLmRpb3hpZGVgIGFuZCBgdG90YWwuc3VsZnVyLmRpb3hpZGVgLg0KDQoqSGludDogTm90ZSBob3cgaW4gdGhlIGBmZWF0dXJlUGxvdGAgY29kZSBhYm92ZSwgd2UgaGF2ZSB1c2VkIGB4ID0gcmVkX3dpbmVbLCAtMTJdYCB0byBzcGVjaWZ5IHRoYXQgdGhlIDEydGggY29sdW1uIChxdWFsaXR5KSBzaG91bGQgbm90IGJlIGluY2x1ZGVkIGluIHRoZSBwbG90dGVkIHggdmFyaWFibGVzIC0geW91IGNhbiB1c2UgdGhpcyBhcyBhIGd1aWRlIG9uIGhvdyB0byBpZ25vcmUgYGZyZWUuc3VsZnVyLmRpb3hpZGVgIGFuZCBgdG90YWwuc3VsZnVyLmRpb3hpZGVgLiBDaGVjayB0aGUgY29kZSBiZWxvdyBpZiB5b3UgYXJlIHN0dWNrOioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGV2YWwgPSBGLCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBmaWcuZGltID0gYyg2LDYpfQ0KZmVhdHVyZVBsb3QoeCA9IHJlZF93aW5lWywgLWMoNiw3LDEyKV0sIA0KICAgICAgICAgICAgeSA9IHJlZF93aW5lJHF1YWxpdHksIA0KICAgICAgICAgICAgcGxvdCA9ICJib3giKQ0KYGBgDQoNCiMgUHJlLVByb2Nlc3NpbmcgeyNwcmVwcm99DQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIEJlZm9yZSB3ZSBiZWdpbiBmaXR0aW5nIGEgbWFjaGluZSBsZWFybmluZyBtb2RlbCwgd2Ugc2hvdWxkIGNvbmR1Y3Qgc29tZSBwcmUtcHJvY2Vzc2luZyBjaGVja3MuIEhhdmluZyBjb21wbGV0ZWQgW3RoZSBmaXJzdCBNTCBEYXRhIFNjaWVuY2UgQ29tcHV0ZXIgTGFiXShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDkpLCBtb3N0IG9mIHRoZSBmb2xsb3dpbmcgc3RlcHMgc2hvdWxkIGJlIGZhbWlsaWFyIHRvIHlvdS4gDQoNCkFzIHdlIHdvcmsgdGhyb3VnaCB0aGlzIHNlY3Rpb24gdGhvdWdoLCB5b3UgbWF5IGxpa2UgdG8gcmVmZXIgdG8gdGhlIGNvbnRlbnQgcHJlc2VudGVkIGluIFtTZWN0aW9uIDMuMiBvZiB0aGUgSW50cm9kdWN0aW9uIHRvIE1hY2hpbmUgTGVhcm5pbmcgaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX2ludHJvZHVjdGlvbl90b19tYWNoaW5lX2xlYXJuaW5nX2luX3Ivc3VwZXJ2aXNlZC1tYWNoaW5lLWxlYXJuaW5nLXVzaW5nLXRoZS1jYXJldC1yLXBhY2thZ2UuaHRtbCNwcmUtcHJvY2Vzc2luZy0xKS4NCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgU2luY2UgYWxsIG91ciBmZWF0dXJlIHZhcmlhYmxlcyBhcmUgbnVtZXJpYywgdGhlcmUgaXMgbm8gbmVlZCB0byBjcmVhdGUgYW55IGR1bW15IHZhcmlhYmxlcyBmb3Igb3VyIGRhdGEgc2V0Lg0KDQojIyBIaWdobHkgSW5mbHVlbnRpYWwgU2FtcGxlcw0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBSZWNhbGwgdGhhdCB3ZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgbmVhclplcm9WYXJgIGZyb20gdGhlIGBjYXJldGAgcGFja2FnZSB0byBvYnRhaW4gZGV0YWlscyBvbiB0aGUgYGZyZXFSYXRpb2AgYW5kIGBwZXJjZW50VW5pcXVlYCB2YWx1ZXMgZm9yIGVhY2ggb2YgdGhlIHZhcmlhYmxlcyBpbiBvdXIgYHJlZF93aW5lYCBkYXRhIHNldC4gDQoNCkZ1cnRoZXIsIHJlY2FsbCB0aGF0IHRoZSBgbmVhclplcm9WYXJgIGZ1bmN0aW9uIGNhbiBpbmNsdWRlIGFkZGl0aW9uYWwgYXJndW1lbnRzLCBgZnJlcUN1dGAgYW5kIGB1bmlxdWVDdXRgLCB0aGF0IHNwZWNpZnkgY3V0LW9mZiB2YWx1ZXMgZm9yIHRoZSBgZnJlcVJhdGlvYCBhbmQgYHBlcmNlbnRVbmlxdWVgIHJlc3VsdHMgcmVzcGVjdGl2ZWx5Lg0KDQpVc2UgdGhlIGBuZWFyWmVyb1ZhcmAgZnVuY3Rpb24gdG8gYXNzZXNzIHRoZSBmZWF0dXJlIHZhcmlhYmxlcyBpbiB0aGUgYHJlZF93aW5lYCBkYXRhIHNldCwgYW5kIHNwZWNpZnk6DQoNCiogQSBjdXQtb2ZmIHZhbHVlIG9mIDIgZm9yIHRoZSBgZnJlcVJhdGlvYCB2YWx1ZXMNCiogQSBjdXQtb2ZmIHZhbHVlIG9mIDUgZm9yIHRoZSBgcGVyY2VudFVuaXF1ZWAgdmFsdWVzLiANCg0KUnVuIHRoZSBgbmVhclplcm9WYXJgIGZ1bmN0aW9uIHR3aWNlLCBvbmNlIHdpdGggYHNhdmVNZXRyaWNzID0gVGAgYW5kIG9uY2Ugd2l0aCBgc2F2ZU1ldHJpY3MgPSBGYC4NCg0KKkhpbnQ6IElmIHlvdSBhcmUgbm90IHN1cmUgaG93IHRvIHByb2NlZWQsIGNoZWNrIHNlY3Rpb25zIDMuMi4yIHRvIDMuMi40IG9mIHRoZSBbSW50cm9kdWN0aW9uIHRvIE1hY2hpbmUgTGVhcm5pbmcgaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX2ludHJvZHVjdGlvbl90b19tYWNoaW5lX2xlYXJuaW5nX2luX3Ivc3VwZXJ2aXNlZC1tYWNoaW5lLWxlYXJuaW5nLXVzaW5nLXRoZS1jYXJldC1yLXBhY2thZ2UuaHRtbCNpbmZsdWVuY2VjaGVja2xpc3QpLioNCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgQmFzZWQgb24gdGhlIGBuZWFyWmVyb1ZhcmAgZnVuY3Rpb24gcmVzdWx0cywgY2hlY2sgZm9yIHBvdGVudGlhbGx5IHByb2JsZW1hdGljIHZhcmlhYmxlcy4gDQpXaGljaCBmZWF0dXJlIHZhcmlhYmxlIGhhcyB0aGUgaGlnaGVzdCBgZnJlcVJhdGlvYCB2YWx1ZSwgYW5kIHdoaWNoIGZlYXR1cmUgdmFyaWFibGUgaGFzIHRoZSBsb3dlc3QgYHBlcmNlbnRVbmlxdWVgIHZhbHVlPw0KV2hhdCBkbyB5b3UgY29uY2x1ZGU/IEFyZSB0aGVyZSBhbnkgcHJvYmxlbWF0aWMgZmVhdHVyZSB2YXJpYWJsZXM/DQoNCjxkZXRhaWxzPg0KICA8c3VtbWFyeT5gciBlbW86OmppKCJoZWFkcGhvbmVzIilgICoqT25saW5lIHN0dWRlbnRzKio8L3N1bW1hcnk+DQpgciBlbW86OmppKCJzcGVlY2hfYmFsbG9vbiIpYCAgRW50ZXIgeW91ciBhbnN3ZXIgbmV4dCB0byB0aGUgcXVlc3Rpb24gb24gdGhlIHNoYXJlZCBqYW1ib2FyZC4NCjwvZGV0YWlscz4gDQoNCiMjDQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIE5leHQsIHdlIHNob3VsZCBjaGVjayBmb3IgY29ycmVsYXRlZCBmZWF0dXJlIHZhcmlhYmxlcy4gUmVtZW1iZXIgdGhhdCBpdCBpcyBvZnRlbiBiZW5lZmljaWFsIHRvIHJlbW92ZSBoaWdobHkgY29ycmVsYXRlZCBmZWF0dXJlIHZhcmlhYmxlcyBmcm9tIG91ciBkYXRhLg0KDQpVc2UgdGhlIGBjb3JgIGZ1bmN0aW9uIHRvIGNvbXB1dGUgYSBjb3JyZWxhdGlvbiBtYXRyaXggZm9yIHRoZSBgcmVkX3dpbmVgIGZlYXR1cmUgdmFyaWFibGVzLiBBc3Nlc3MgdGhlIHNwcmVhZCBvZiBjb3JyZWxhdGlvbiB2YWx1ZXMsIGFuZCBjaGVjayBmb3IgZXh0cmVtZSBjb3JyZWxhdGlvbnMgY2xvc2UgdG8gMSBpbiBtYWduaXR1ZGUuDQoNCipIaW50OiBZb3UgY2FuIGZvbGxvdyB0aGUgc3RlcHMgaW4gc2VjdGlvbiAzLjIuNSBvZiB0aGUgW0ludHJvZHVjdGlvbiB0byBNYWNoaW5lIExlYXJuaW5nIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV9pbnRyb2R1Y3Rpb25fdG9fbWFjaGluZV9sZWFybmluZ19pbl9yL3N1cGVydmlzZWQtbWFjaGluZS1sZWFybmluZy11c2luZy10aGUtY2FyZXQtci1wYWNrYWdlLmh0bWwjY29ycmVsYXRlZC1mZWF0dXJlLXZhcmlhYmxlcykgZm9yIHRoaXMgcXVlc3Rpb24uKg0KDQojIyANCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgV2hhdCBhcmUgdGhlIGxhcmdlc3QgbmVnYXRpdmUgYW5kIHBvc2l0aXZlIGNvcnJlbGF0aW9uIHZhbHVlcz8gRG8gdGhlc2Ugc2VlbSBwcm9ibGVtYXRpYz8NCg0KIyMgDQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIEJhc2VkIG9uIHlvdXIgY2FsY3VsYXRpb25zLCBkbyB5b3UgdGhpbmsgd2UgbmVlZCB0byB1c2UgdGhlIGBmaW5kQ29ycmVsYXRpb25gIGZ1bmN0aW9uIHRvIGlkZW50aWZ5IGhpZ2hseSBjb3JyZWxhdGVkIGZlYXR1cmUgdmFyaWFibGVzIHRvIHJlbW92ZSBmcm9tIG91ciBkYXRhIHNldD8gV2h5IG9yIHdoeSBub3Q/DQoNCipOb3RlOiBGb3IgdGhlIHJlbWFpbmRlciBvZiB0aGlzIGxhYiwgd2Ugd2lsbCBhc3N1bWUgbm8gZmVhdHVyZSB2YXJpYWJsZXMgbmVlZGVkIHRvIGJlIHJlbW92ZWQuKg0KDQo8ZGV0YWlscz4NCiAgPHN1bW1hcnk+YHIgZW1vOjpqaSgiaGVhZHBob25lcyIpYCAqKk9ubGluZSBzdHVkZW50cyoqPC9zdW1tYXJ5Pg0KYHIgZW1vOjpqaSgic3BlZWNoX2JhbGxvb24iKWAgIEVudGVyIHlvdXIgYW5zd2VyIG5leHQgdG8gdGhlIHF1ZXN0aW9uIG9uIHRoZSBzaGFyZWQgamFtYm9hcmQuDQo8L2RldGFpbHM+IA0KDQojIw0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBPbmUgYXNwZWN0IG9mIHByZS1wcm9jZXNzaW5nIHRoYXQgd2UgZGlkIG5vdCBkaXNjdXNzIGluIHRoZSBzdXBwbGVtZW50YXJ5IG1hdGVyaWFsIGlzIHRoZSBjb25jZXB0IG9mIG1vZGlmeWluZyBvdXIgZGF0YSB2YWx1ZXMsIHRvIGVuc3VyZSBzbW9vdGhlciBjb21wYXJpc29ucyBiZXR3ZWVuIGRpZmZlcmVudCBmZWF0dXJlIHZhcmlhYmxlcy4NCg0KU2ltaWxhcmx5IHRvIHRoZSBub3JtYWxpc2F0aW9uIHByb2Nlc3NlcyBjb25kdWN0ZWQgaW4gZWFybGllciBjb3JlIGNvbXB1dGVyIGxhYnMsIHdlIGNhbiBgc2NhbGVgIGFuZCBgY2VudGVyYCBvdXIgd2luZSBkYXRhLCB1c2luZyB0aGUgYGNhcmV0YCBwYWNrYWdlIGZ1bmN0aW9uIGBwcmVQcm9jZXNzYC4NCg0KUnVuIHRoZSBjb2RlIGJlbG93LCB0byBzY2FsZSBhbmQgY2VudGVyIG91ciBgcmVkX3dpbmVgIGRhdGEsIGFuZCB0aGVuIGFzc2lnbiBvdXIgdXBkYXRlZCBkYXRhIHRvIHRoZSBuZXcgb2JqZWN0IGByZWRfd2luZV91cGRhdGVkYDoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KY2VudHJlX3NjYWxlIDwtIHByZVByb2Nlc3MocmVkX3dpbmVbLCAtMTJdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9IGMoImNlbnRlciIsICJzY2FsZSIpKQ0KcmVkX3dpbmVfdXBkYXRlZCA8LSBwcmVkaWN0KGNlbnRyZV9zY2FsZSwgcmVkX3dpbmUpDQpgYGANCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgQ29tcGFyZSB0aGUgb3JpZ2luYWwgZGF0YSB0byB0aGUgdXBkYXRlZCBkYXRhLCB1c2luZyBgaGVhZChyZWRfd2luZSlgIGFuZCBgaGVhZChyZWRfd2luZV91cGRhdGVkKWAgcmVzcGVjdGl2ZWx5LiBZb3Ugc2hvdWxkIG9ic2VydmUgdGhhdCB0aGUgZmVhdHVyZSB2YXJpYWJsZSB2YWx1ZXMgYXJlIG5vdyBzY2FsZWQgYW5kIGNlbnRlcmVkLg0KDQojIyB7I2JveHBsb3RuZXd9DQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIElmIHdlIGNyZWF0ZSBib3ggcGxvdHMgb2YgdGhlIHVwZGF0ZWQgZGF0YSBgcmVkX3dpbmVfdXBkYXRlZGAsIHdlIG9ic2VydmUgdGhhdCB0aGV5IGFyZSBtdWNoIGVhc2llciB0byBhc3Nlc3MgdGhhbiB0aGUgb3JpZ2luYWwgYm94IHBsb3RzIGZyb20gXEByZWYoZmVhdHVyZXBsb3QpOiANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KZmVhdHVyZVBsb3QoeCA9IHJlZF93aW5lX3VwZGF0ZWRbLCAtMTJdLCANCiAgICAgICAgICAgIHkgPSByZWRfd2luZV91cGRhdGVkJHF1YWxpdHksIA0KICAgICAgICAgICAgcGxvdCA9ICJib3giLA0KICAgICAgICAgICAgYXV0by5rZXkgPSBsaXN0KGNvbHVtbnMgPSA2KSkNCmBgYA0KDQpEbyBhbnkgb2YgdGhlIGZlYXR1cmVzIHNlZW0gdG8gaGF2ZSBtdWNoIGFzc29jaWF0aW9uIHdpdGggd2luZSBgcXVhbGl0eWA/IERvIHlvdSBub3RpY2UgYSB0cmVuZCBvciBwYXR0ZXJuIGluIHRoZSBib3ggcGxvdHMgZm9yIHRoZSBkaWZmZXJlbnQgYHF1YWxpdHlgIHZhbHVlcyB3aXRoaW4gYW55IHBhcnRpY3VsYXIgZmVhdHVyZXM/DQoNCjxkZXRhaWxzPg0KICA8c3VtbWFyeT5gciBlbW86OmppKCJoZWFkcGhvbmVzIilgICoqT25saW5lIHN0dWRlbnRzKio8L3N1bW1hcnk+DQpgciBlbW86OmppKCJzcGVlY2hfYmFsbG9vbiIpYCAgRW50ZXIgeW91ciBhbnN3ZXIgbmV4dCB0byB0aGUgcXVlc3Rpb24gb24gdGhlIHNoYXJlZCBqYW1ib2FyZC4NCjwvZGV0YWlscz4gDQoNCiMjDQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIEV2ZW4gdGhvdWdoIHRoZSBib3ggcGxvdHMgcHJvZHVjZWQgaW4gXEByZWYoYm94cGxvdG5ldykgYXJlIGNsZWFyZXIgdGhhbiB0aG9zZSBwcm9kdWNlZCBpbiBzZWN0aW9uIFxAcmVmKGRhdGF2aXopLCBpdCBpcyBzdGlsbCBxdWl0ZSBkaWZmaWN1bHQgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiB0aGUgYHF1YWxpdHlgIHJhdGluZ3MuDQoNClJlcGxhY2UgdGhlIGBwbG90ID0gImJveCJgIGFyZ3VtZW50IGluIHRoZSBjb2RlIGZyb20gXEByZWYoYm94cGxvdG5ldykgd2l0aCBgcGxvdCA9ICJwYWlycyJgIHRvIHByb2R1Y2Ugc2NhdHRlciBwbG90cyBpbnN0ZWFkIG9mIGJveCBwbG90cyBmb3Igb3VyIHByZS1wcm9jZXNzZWQgZGF0YS4gDQoNCkRvIHlvdSBub3RpY2UgYW55IGNsZWFyIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIGRpZmZlcmVudCBgcXVhbGl0eWAgcmF0aW5ncz8NCg0KPGRldGFpbHM+DQogIDxzdW1tYXJ5PmByIGVtbzo6amkoImhlYWRwaG9uZXMiKWAgKipPbmxpbmUgc3R1ZGVudHMqKjwvc3VtbWFyeT4NCmByIGVtbzo6amkoInNwZWVjaF9iYWxsb29uIilgICBFbnRlciB5b3VyIGFuc3dlciBuZXh0IHRvIHRoZSBxdWVzdGlvbiBvbiB0aGUgc2hhcmVkIGphbWJvYXJkLg0KPC9kZXRhaWxzPiANCg0KIyMgVHJhaW5pbmcgYW5kIFZhbGlkYXRpb24gRGF0YSB7I3RyYWlufQ0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBPdXIgZmluYWwgcHJlcGFyYXRvcnkgc3RlcCBpcyB0byBzcGxpdCBvdXIgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uIHNldHMuDQpVc2UgdGhlIGBjcmVhdGVEYXRhUGFydGl0aW9uYCBmdW5jdGlvbiBmcm9tIHRoZSBgY2FyZXRgIHBhY2thZ2UgdG8gc3BsaXQgdGhlIGByZWRfd2luZV91cGRhdGVkYCBkYXRhIDgwLzIwLg0KDQoqTm90ZTogVGhlIGRhdGEgcGFydGl0aW9uaW5nIGludG8gdHJhaW5pbmcgb3IgdmFsaWRhdGlvbiBjYXRlZ29yaWVzIGlzIHJhbmRvbSB0byBhbiBleHRlbnQsIHNvIGlmIHlvdSBkbyBub3QgcnVuIHRoZSBgc2V0LnNlZWQoMTY1MCkgY29tbWFuZHMgc2hvd24gaW4gdGhlIGBDb2RlYCBjaHVua3MgYmVsb3csIHlvdXIgcmVzdWx0cyBmcm9tIHRoaXMgcG9pbnQgb253YXJkcyBtYXkgZGlmZmVyIHNsaWdodGx5IHRvIHRob3NlIHByZXNlbnRlZCBpbiB0aGUgc3Vic2VxdWVudCBxdWVzdGlvbiBzb2x1dGlvbnMsIHNpbmNlIHlvdXIgdHJhaW5pbmcgYW5kIHZhbGlkYXRpb24gZGF0YSBzZXRzIHdpbGwgbW9zdCBsaWtlbHkgY29udGFpbiBzbGlnaHRseSBkaWZmZXJlbnQgc2V0cyBvZiBvYnNlcnZhdGlvbnMuKg0KDQpUaGUgY29kZSBiZWxvdyBpcyBwYXJ0aWFsbHkgY29tcGxldGVkLCBqdXN0IGZpbGwgaW4gdGhlIGAuLi5gIG1pc3NpbmcgcGFydHM6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnNldC5zZWVkKDE2NTApDQp3aW5lX3RyYWluX2luZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oLi4uICwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcCA9IC4uLiAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSwgdGltZXMgPSAxKSANCmBgYA0KDQoqSGludDogUmVtZW1iZXIgdGhhdCB0aGUgYXJndW1lbnQgYHBgIGRlbm90ZXMgdGhlIHNwbGl0LiBJZiB5b3UgYXJlIHN0dWNrLCB5b3UgY2FuIGNoZWNrIHRoZSBjb2RlIGNodW5rIGJlbG93OioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0Kc2V0LnNlZWQoMTY1MCkNCndpbmVfdHJhaW5faW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihyZWRfd2luZV91cGRhdGVkJHF1YWxpdHksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHAgPSAuOCwgIyBoZXJlIHAgZGVzaWduYXRlcyB0aGUgc3BsaXQgLSA4MC8yMA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSwgdGltZXMgPSAxKSANCmBgYA0KDQojIw0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBOZXh0LCBhc3NpZ24gdGhlIGByZWRfd2luZV91cGRhdGVkYCBkYXRhIGludG8gdGhlIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uIHNldHMsIGFuZCBuYW1lIHRoZXNlIGByZWRfd2luZV90cmFpbmAgYW5kIGByZWRfd2luZV92YWxpZGF0ZWANCnJlc3BlY3RpdmVseS4gQ2hlY2sgdGhlIGNvZGUgYmVsb3cgZm9yIGEgaGVhZCBzdGFydDoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KcmVkX3dpbmVfdmFsaWRhdGUgPC0gcmVkX3dpbmVfdXBkYXRlZFstd2luZV90cmFpbl9pbmRleCwgXQ0KYGBgDQoNCipIaW50OiBJZiB5b3UgYXJlIHN0dWNrLCB5b3UgY2FuIGNoZWNrIHRoZSBjb2RlIGNodW5rIGJlbG93OioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KIyBOb3RlIGhlcmUgd2UgYXJlIHVzaW5nIHRoZSB2YWx1ZXMgaW4gdGhlIHdpbmVfdHJhaW5faW5kZXgNCiMgKHdoZXJlYXMgZm9yIHRoZSB2YWxpZGF0aW9uIHNldCwgd2Ugc2VsZWN0IHRoZSB2YWx1ZXMgbm90IGluIHRoZSB3aW5lX3RyYWluX2luZGV4KQ0KcmVkX3dpbmVfdHJhaW4gPC0gcmVkX3dpbmVfdXBkYXRlZFt3aW5lX3RyYWluX2luZGV4LCBdDQpgYGANCg0KIyBGaXR0aW5nIGEgRGVjaXNpb24gVHJlZSBNYWNoaW5lIExlYXJuaW5nIE1vZGVsIHsjZml0fQ0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBOb3cgdGhhdCB0aGUgcHJlcGFyYXRpb24gcGhhc2UgaXMgY29tcGxldGUsIHdlIGFyZSByZWFkeSB0byBmaXQgIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzIHVzaW5nIG91ciBgcmVkX3dpbmVfdXBkYXRlZGAgZGF0YS4gDQoNClJlY2FsbCB0aGF0IHRoZSBiYXNpYyBjb2RlIGZyYW1ld29yayB0byBmaXQgYSBtb2RlbCB1c2luZyB0aGUgYHRyYWluYCBmdW5jdGlvbiBpcyBhcyBmb2xsb3dzOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpvYmplY3QgPC0gdHJhaW4oLi4uIH4gLiwgIyBzcGVjaWZ5IHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG91dGNvbWUgYW5kIGZlYXR1cmUgdmFyaWFibGVzDQogICAgICAgICAgICAgICAgZGF0YSA9IC4uLiAsICMgc3BlY2lmeSB0cmFpbmluZyBkYXRhDQogICAgICAgICAgICAgICAgbWV0aG9kID0gInNwZWNpZnkgbWV0aG9kIGhlcmUiKQ0KYGBgDQoNCiMjIERlY2lzaW9uIFRyZWUgeyNkZWN0cmVlfQ0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBPbmUgb2YgdGhlIHNpbXBsZXN0IG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzIHdlIGNhbiB1c2UgaXMgYSBkZWNpc2lvbiB0cmVlLiANCg0KVXNpbmcgdGhlIGluZm9ybWF0aW9uIGluIFxAcmVmKGZpdCksIGFuZCB0aGUgcGFydGlhbGx5IGNvbXBsZXRlIGNvZGUgaW4gdGhlIGNvZGUgY2h1bmsgYmVsb3csIGZpdCBhIGRlY2lzaW9uIHRyZWUgdG8geW91ciBwcmUtcHJvY2Vzc2VkIGByZWRfd2luZV90cmFpbmAgdHJhaW5pbmcgZGF0YS4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0Kc2V0LnNlZWQoMTY1MCkgDQpyZWRfd2luZV9kZWNpc2lvbl90cmVlIDwtIHRyYWluKC4uLiB+IC4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IC4uLiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicnBhcnQiKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IEYsIGluY2x1ZGUgPSBGLCBjYWNoZSA9IFR9DQpzZXQuc2VlZCgxNjUwKSANCnJlZF93aW5lX2RlY2lzaW9uX3RyZWUgPC0gdHJhaW4ocXVhbGl0eSB+IC4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHJlZF93aW5lX3RyYWluLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJycGFydCIpDQpgYGANCg0KT25jZSB5b3UgYXJlIGhhcHB5IHdpdGggeW91ciBjb2RlLCBydW4gaXQsIGFuZCB0aGVuIHJ1biB0aGUgb2JqZWN0IGByZWRfd2luZV9kZWNpc2lvbl90cmVlYCB0byBzZWUgdGhlIG91dHB1dC4gWW91ciBvdXRwdXQgc2hvdWxkIGxvb2sgbGlrZSB0aGUgb3V0cHV0IGluIHRoZSBjb2RlIGNodW5rIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpDQVJUIA0KDQoxMjgyIHNhbXBsZXMNCiAgMTEgcHJlZGljdG9yDQogICA2IGNsYXNzZXM6ICczJywgJzQnLCAnNScsICc2JywgJzcnLCAnOCcgDQoNCk5vIHByZS1wcm9jZXNzaW5nDQpSZXNhbXBsaW5nOiBCb290c3RyYXBwZWQgKDI1IHJlcHMpIA0KU3VtbWFyeSBvZiBzYW1wbGUgc2l6ZXM6IDEyODIsIDEyODIsIDEyODIsIDEyODIsIDEyODIsIDEyODIsIC4uLiANClJlc2FtcGxpbmcgcmVzdWx0cyBhY3Jvc3MgdHVuaW5nIHBhcmFtZXRlcnM6DQoNCiAgY3AgICAgICAgICAgQWNjdXJhY3kgICBLYXBwYSAgICANCiAgMC4wMTIyMTE2NyAgMC41NzM3MTA3ICAwLjMwMzM1MDkNCiAgMC4wMjM3NDQ5MSAgMC41NjU3NDU4ICAwLjI2OTcwODQNCiAgMC4yNTIzNzQ0OSAgMC40NzE5MDY4ICAwLjExMTY5MTkNCg0KQWNjdXJhY3kgd2FzIHVzZWQgdG8gc2VsZWN0IHRoZSBvcHRpbWFsIG1vZGVsIHVzaW5nIHRoZSBsYXJnZXN0IHZhbHVlLg0KVGhlIGZpbmFsIHZhbHVlIHVzZWQgZm9yIHRoZSBtb2RlbCB3YXMgY3AgPSAwLjAxMjIxMTY3Lg0KYGBgDQoNCldlIGFyZSBtYWlubHkgaW50ZXJlc3RlZCBoZXJlIGluIHRoZSBgQWNjdXJhY3lgIHZhbHVlcywgZm9yIGRpZmZlcmVudCB0dW5pbmcgcGFyYW1ldGVyIHZhbHVlcyAod2UgY2FuIGlnbm9yZSB0aGUgYEthcHBhYCB2YWx1ZXMpLg0KVW5mb3J0dW5hdGVseSwgdGhlIGJlc3QgYWNjdXJhY3kgYWNoaWV2ZWQgd2FzIG9ubHkgNTcuMzclLCB3aGljaCBpcyBub3QgbXVjaCBiZXR0ZXIgdGhhbiByYW5kb21seSBndWVzc2luZy4NCg0KKk5vdGU6IERvbid0IHdvcnJ5IGlmIHlvdXIgcmVzdWx0cyBsb29rIHNsaWdodGx5IGRpZmZlcmVudCAtIHBlcmhhcHMgeW91IGRpZCBub3QgcnVuIGFsbCB0aGUgYHNldC5zZWVkKDE2NTApYCBjb21tYW5kcz8qDQoNCiMjIw0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBSdW4gdGhlIGZvbGxvd2luZyBjb2RlIHRvIHZpc3VhbGlzZSB0aGlzIGRlY2lzaW9uIHRyZWUgbW9kZWw6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnJwYXJ0LnBsb3QocmVkX3dpbmVfZGVjaXNpb25fdHJlZSRmaW5hbE1vZGVsKQ0KYGBgDQoNCipOb3RlOiBUaGUgYHJwYXJ0LnBsb3RgIHBhY2thZ2UgcmVxdWlyZWQgaGVyZSBzaG91bGQgaGF2ZSBiZWVuIGluc3RhbGxlZCBhbmQgbG9hZGVkIGluIFxAcmVmKHByZXApLioNCg0KIyMjDQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIEFzIHlvdSBzaG91bGQgYmUgYWJsZSB0byBzZWUgZnJvbSB0aGUgZGVjaXNpb24gdHJlZSBkaWFncmFtLCB0aGUgbW9kZWwgZGlkIG5vdCBoYXZlIGVub3VnaCBkYXRhIG9uIHdpbmVzIG9mIGxvdyBvciBoaWdoIHF1YWxpdHksIGFuZCB0aGVyZWZvcmUgaXMgb25seSBhYmxlIHRvIG1ha2UgcHJlZGljdGlvbnMgYmV0d2VlbiB3aW5lcyBvZiBxdWFsaXR5IGA1YCBvciBgNmAgLSB0aGlzIGV4cGxhaW5zIHRoZSBkaXNhcHBvaW50aW5nIG92ZXJhbGwgcHJlZGljdGl2ZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwuDQoNClRoaXMgaXMgdGhlIHByb2JsZW0gd2Ugd2VyZSBhbGx1ZGluZyB0byBpbiBcQHJlZihwcm9ibGVtKS4gT3VyIHByZXZpb3VzIHdvcmsgdXNpbmcgdGhlIGBwZW5ndWluc2AgZGF0YSB3YXMgYW4gZXhjZXB0aW9uIC0gdXN1YWxseSB0aGUgZGF0YSBpcyBub3Qgc28gbmljZWx5IHBhcnRpdGlvbmVkLg0KDQojIEZpdHRpbmcgYSBSYW5kb20gRm9yZXN0IE1hY2hpbmUgTGVhcm5pbmcgTW9kZWwgeyNyYW5kb21mb3Jlc3R9DQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIFRoZSBkZWNpc2lvbiB0cmVlIGlzIG9uZSBvZiB0aGUgc2ltcGxlc3QgbWFjaGluZSBsZWFybmluZyBtb2RlbHMgd2UgY2FuIHVzZS4gT3RoZXIgbWV0aG9kcyBvZnRlbiBwcm9kdWNlIGJldHRlciByZXN1bHRzLCBidXQgdGhleSBhbHNvIHRlbmQgdG8gdGFrZSBsb25nZXIgdG8gdHJhaW4uIExldCdzIHNlZSBpZiB3ZSBjYW4gb2J0YWluIGEgYmV0dGVyIHByZWRpY3RpdmUgYWNjdXJhY3kgdXNpbmcgYW4gZW5zZW1ibGUgbW9kZWwgdHJlZSBtZXRob2QuJF57XGRhZ2dlcn0kDQoNCiRee1xkYWdnZXJ9JCAqUmVmZXIgdG8gc2VjdGlvbiA0LjIuMS4yIG9mIHRoZSBbSW50cm9kdWN0aW9uIHRvIE1hY2hpbmUgTGVhcm5pbmcgaW4gUiBzdXBwbGVtZW50XShodHRwczovL2Jvb2tkb3duLm9yZy9yZWhrL3N0bTEwMDFfZHNtX2ludHJvZHVjdGlvbl90b19tYWNoaW5lX2xlYXJuaW5nX2luX3IvbWFjaGluZS1sZWFybmluZy1tb2RlbHMuaHRtbCNtb2RlbC10cmVlcykgZm9yIGRldGFpbHMuKg0KDQojIyBUcmFpbmluZyB0aGUgUmFuZG9tIEZvcmVzdCBNb2RlbCB7I3JmfQ0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBUcmFpbmluZyBhIHJhbmRvbSBmb3Jlc3QgbW9kZWwgaW4gUlN0dWRpbyBpcyBxdWl0ZSBzaW1wbGUsIG9uY2Ugd2UgdW5kZXJzdGFuZCB0aGUgYmFzaWNzIG9mIHRoZSBgdHJhaW5gIGZ1bmN0aW9uLg0KDQpSZXBsYWNlIHRoZSBgbWV0aG9kID0gInJwYXJ0ImAgcGFydCBvZiB5b3VyIGRlY2lzaW9uIHRyZWUgY29kZSBmcm9tIFxAcmVmKGRlY3RyZWUpIHdpdGggYG1ldGhvZCA9ICJyZiJgLg0KVGhpcyB3aWxsIHRlbGwgdGhlIGB0cmFpbmAgZnVuY3Rpb24gdG8gdHJhaW4gYSBtYWNoaW5lIGxlYXJuaW5nIG1vZGVsIHVzaW5nIHRoZSByYW5kb20gZm9yZXN0IG1ldGhvZCAoYHJmYCkuIA0KDQpBc3NpZ24geW91ciBvdXRwdXQgdG8gdGhlIG9iamVjdCBgcmVkX3dpbmVfcmZgLg0KDQoqTm90ZTogRG9uJ3Qgd29ycnkgaWYgaXQgdGFrZXMgYSBjb3VwbGUgb2YgbWludXRlcyBmb3IgeW91ciBjb2RlIHRvIHJ1biAtIHRoaXMgaXMgbm9ybWFsIGZvciBtb3JlIGNvbXBsZXggbW9kZWxzLioNCg0KIyMgeyNyZmFjY3VyYWN5fQ0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBBc3Nlc3MgdGhlIGByZWRfd2luZV9yZmAgb2JqZWN0LiBXaGF0IGlzIHRoZSBiZXN0IGFjY3VyYWN5IGFjaGlldmVkIGJ5IHRoZSByYW5kb20gZm9yZXN0IG1ldGhvZD8gDQoNCiMjDQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIENvbXBhcmUgeW91ciByZXN1bHRzIGZvciB5b3VyIGRlY2lzaW9uIHRyZWUgbW9kZWwgYW5kIHlvdXIgcmFuZG9tIGZvcmVzdCBtb2RlbC4gV2hpY2ggbW9kZWwgYWNoaWV2ZXMgYSBoaWdoZXIgcHJlZGljdGl2ZSBhY2N1cmFjeT8NCg0KPGRldGFpbHM+DQogIDxzdW1tYXJ5PmByIGVtbzo6amkoImhlYWRwaG9uZXMiKWAgKipPbmxpbmUgc3R1ZGVudHMqKjwvc3VtbWFyeT4NCmByIGVtbzo6amkoInNwZWVjaF9iYWxsb29uIilgICBFbnRlciB5b3VyIGFuc3dlciBuZXh0IHRvIHRoZSBxdWVzdGlvbiBvbiB0aGUgc2hhcmVkIGphbWJvYXJkLg0KPC9kZXRhaWxzPiANCg0KIyMgUmFuZG9tIEZvcmVzdCBQbG90cyB7I3JmcGxvdHN9DQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIEZvciByYW5kb20gZm9yZXN0IG1vZGVscywgd2UgY2FuIGVhc2lseSBwcm9kdWNlIHR3byBoZWxwZnVsIHBsb3RzOg0KDQoqIEEgcGxvdCBvZiB0aGUgbW9kZWwgYWNjdXJhY3kgYmFzZWQgb24gdGhlIG51bWJlciBvZiBmZWF0dXJlIHZhcmlhYmxlcyB1c2VkIGZvciBlYWNoIGRlY2lzaW9uIHRyZWUgaW4gdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwNCiogQSBwbG90IG9mIHRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggZmVhdHVyZSB2YXJpYWJsZSBpbiBhY2hpZXZpbmcgYW4gYWNjdXJhdGUgbW9kZWwNCg0KTm90ZSBmb3IgdGhlIHZhcmlhYmxlIGltcG9ydGFuY2UgZ3JhcGggdGhhdCB0aGUgYmVzdCAobW9zdCBoZWxwZnVsKSB2YXJpYWJsZSBpcyBhbHdheXMgZ2l2ZW4gYW4gaW1wb3J0YW5jZSBvZiAxMDAgYW5kIHRoZSB3b3JzdCBpcyBnaXZlbiBhbiBpbXBvcnRhbmNlIG9mIDAuIFRoaXMgaXMgbm90IHRvIHNheSB0aGF0IHRoZSB2YXJpYWJsZSB3aXRoIGltcG9ydGFuY2UgcmFua2luZyAwIGlzIHVzZWxlc3MgLSBpdCBpcyBqdXN0IGNvbnNpZGVyZWQgbGVzcyB1c2VmdWwgdGhhbiB0aGUgb3RoZXIgdmFyaWFibGVzLg0KDQojIw0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBSdW4gdGhlIGNvZGUgYmVsb3cgdG8gcHJvZHVjZSBwbG90cyBmb3IgeW91ciBgcmVkX3dpbmVfcmZgIHJhbmRvbSBmb3Jlc3QgbW9kZWw6DQoNCldoYXQgZmVhdHVyZSB2YXJpYWJsZXMgYXJlIGNvbnNpZGVyZWQgbW9zdCBpbXBvcnRhbnQ/DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCmdncGxvdChyZWRfd2luZV9yZikNCg0KZG90UGxvdCh2YXJJbXAocmVkX3dpbmVfcmYpKQ0KYGBgDQoNCjxkZXRhaWxzPg0KICA8c3VtbWFyeT5gciBlbW86OmppKCJoZWFkcGhvbmVzIilgICoqT25saW5lIHN0dWRlbnRzKio8L3N1bW1hcnk+DQpgciBlbW86OmppKCJzcGVlY2hfYmFsbG9vbiIpYCAgRW50ZXIgeW91ciBhbnN3ZXIgbmV4dCB0byB0aGUgcXVlc3Rpb24gb24gdGhlIHNoYXJlZCBqYW1ib2FyZC4NCjwvZGV0YWlscz4gDQoNCiMgVmFsaWRhdGluZyBSZXN1bHRzIHsjdmFsfQ0KDQpgciBlbW86OmppKCJjb21wdXRlciIpYCBXaGlsZSB3ZSBoYXZlIHByZWRpY3RpdmUgYWNjdXJhY3kgZXN0aW1hdGVzIGZvciBvdXIgZGVjaXNpb24gdHJlZSBhbmQgcmFuZG9tIGZvcmVzdCBtb2RlbHMsIGl0IGlzIGltcG9ydGFudCB0byByZW1lbWJlciB0aGF0IHRoZXNlIGhhdmUgYmVlbiBjb21wdXRlZCB1c2luZyB0aGUgdHJhaW5pbmcgZGF0YS4NCg0KV2Ugd291bGQgYWxzbyBsaWtlIHRvIGNoZWNrIGhvdyB0aGUgbW9kZWxzIHBlcmZvcm0gd2hlbiBwcmVzZW50ZWQgd2l0aCBuZXcgZGF0YSAtIGkuZS4gb3VyIHZhbGlkYXRpb24gZGF0YSENCg0KUmVjYWxsIHRoYXQgd2hlbiBjb25kdWN0aW5nIG1hY2hpbmUgbGVhcm5pbmcsIHRoZXJlIGlzIGEgcmlzayBvZiBvdmVyZml0dGluZyBvdXIgbW9kZWxzIHRvIG91ciB0cmFpbmluZyBkYXRhLiBUaGlzIGNhbiByZXN1bHQgaW4gdGhlIG1vZGVscyBoYXZpbmcgZXhjZWxsZW50IGFjY3VyYWN5ICp3aGVuIGFzc2Vzc2luZyB0aGUgdHJhaW5pbmcgZGF0YSosIGJ1dCBoYXZpbmcgc3VicGFyIHBlcmZvcm1hbmNlIHdoZW4gcHJlc2VudGVkIHdpdGggbmV3IGRhdGEuIA0KDQpUaGlzIGlzIHdoeSB3ZSBoYXZlIHB1dCBhc2lkZSBzb21lIG9mIG91ciBkYXRhIGFzIHZhbGlkYXRpb24gZGF0YSBpbiBcQHJlZih0cmFpbiksIHNvIHRoYXQgd2UgY2FuIHBlcmZvcm0gKmNyb3NzLXZhbGlkYXRpb24qLg0KSWYgdGhlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbCByZW1haW5zIHNpbWlsYXIgd2hlbiBwcmVzZW50ZWQgd2l0aCB0aGUgdmFsaWRhdGlvbiBkYXRhLCB0aGVuIHdlIGNhbiBiZSBtb3JlIGNvbmZpZGVudCBpbiBvdXIgbW9kZWwncyByZXBvcnRlZCBwZXJmb3JtYW5jZS4NCg0KIyMgeyN2YWxpZGF0aW9uY2hlY2t9DQoNCmByIGVtbzo6amkoImNvbXB1dGVyIilgIFRoZXJlIGFyZSBzZXZlcmFsIHdheXMgdG8gcGVyZm9ybSBjcm9zcy12YWxpZGF0aW9uLiBSdW4gdGhlIGNvZGUgYmVsb3cgdG8gY29uZHVjdCBhIHNpbXBsZSBjcm9zcy12YWxpZGF0aW9uIGNoZWNrIG9mIHRoZSBhY2N1cmFjeSBvZiB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCBvYnRhaW5lZCBpbiBcQHJlZihkZWN0cmVlKS4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KIyBMb2FkIG1hZ3JpdHRyIHBhY2thZ2UgZm9yIHBpcGluZw0KbGlicmFyeShtYWdyaXR0cikNCg0KIyBjb3VudCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHZhbGlkYXRpb24gZGF0YQ0KdmFsaWRhdGlvbl9udW1iZXJzIDwtIGRpbShyZWRfd2luZV92YWxpZGF0ZSlbMV0NCg0KIyBVc2UgdGhlIGZpdHRlZCBtb2RlbCB0byBwcmVkaWN0IHF1YWxpdHkgdmFsdWVzIGdpdmVuIHRoZSB2YWxpZGF0aW9uIGRhdGENCnByZWRpY3RfcmVkX3dpbmVfZGVjaXNpb25fdHJlZSA8LSBwcmVkaWN0KHJlZF93aW5lX2RlY2lzaW9uX3RyZWUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9cmVkX3dpbmVfdmFsaWRhdGUpDQojIFdoZW4gcnVuLCB0aGUgY29kZSBiZWxvdyBnaXZlcyB1cyB0aGUgcGVyY2VudGFnZSBvZiBjb3JyZWN0IHByZWRpY3Rpb25zDQpkZWNfdHJlZV9hY2N1cmFjeSA8LSBzdW0ocHJlZGljdF9yZWRfd2luZV9kZWNpc2lvbl90cmVlID09IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkX3dpbmVfdmFsaWRhdGUkcXVhbGl0eSkgLyB2YWxpZGF0aW9uX251bWJlcnMgKiAxMDANCg0KZGVjX3RyZWVfYWNjdXJhY3kgJT4lIHJvdW5kKDIpDQpgYGANCg0KIyMNCg0KYHIgZW1vOjpqaSgiY29tcHV0ZXIiKWAgVXNpbmcgdGhlIGNvZGUgaW4gXEByZWYodmFsaWRhdGlvbmNoZWNrKSBhcyBhIGd1aWRlLCBwZXJmb3JtIGEgc2ltcGxlIGNyb3NzLXZhbGlkYXRpb24gY2hlY2sgb2YgdGhlIGFjY3VyYWN5IG9mIHRoZSByYW5kb20gZm9yZXN0IG1vZGVsIHlvdSBvYnRhaW5lZCBpbiBcQHJlZihyZikuDQoNCkRpc2N1c3MgdGhlIHJlc3VsdHMgb2YgdGhlIGNyb3NzLXZhbGlkYXRpb24uIERvIHlvdSB0aGluayB5b3VyIG1vZGVscycgcmVwb3J0ZWQgYWNjdXJhY2llcyBpbiBcQHJlZihkZWN0cmVlKSBhbmQgXEByZWYocmZhY2N1cmFjeSkgYXJlIHJlbGlhYmxlPw0KDQo8ZGV0YWlscz4NCiAgPHN1bW1hcnk+YHIgZW1vOjpqaSgiaGVhZHBob25lcyIpYCAqKk9ubGluZSBzdHVkZW50cyoqPC9zdW1tYXJ5Pg0KYHIgZW1vOjpqaSgic3BlZWNoX2JhbGxvb24iKWAgIFZvbHVudGVlciB0byBzaGFyZSB5b3VyIHNjcmVlbiBhbmQgZXhwbGFpbiB5b3VyIGFuc3dlcnMgdG8gdGhpcyBxdWVzdGlvbi4NCjwvZGV0YWlscz4gDQoNCjxicj4NCg0KIyMjIyBHcmVhdCB3b3JrLCB0aGF0J3MgZXZlcnl0aGluZyBmb3IgdG9kYXkhICMjIyMgey19DQoNCkhvcGVmdWxseSB5b3UgYXJlIGJlZ2lubmluZyB0byBmZWVsIG1vcmUgY29tZm9ydGFibGUgY29uZHVjdGluZyBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcgaW4gUlN0dWRpbyAtIGFzIHdlIGNhbiBzZWUsIGl0J3MgYWN0dWFsbHkgbm90IHRoYXQgY29tcGxpY2F0ZWQgdG8gdHJhaW4gYSBtYWNoaW5lIGxlYXJuaW5nIG1vZGVsIGluIFJTdHVkaW8gKGFsdGhvdWdoIGdldHRpbmcgYSBoaWdobHkgYWNjdXJhdGUgb25lIGlzIG9mdGVuIGFub3RoZXIgc3RvcnkuLi4pLiANCg0KSW4gdGhlIG5leHQgZGF0YSBzY2llbmNlIGNvbXB1dGVyIGxhYiwgd2Ugd2lsbCBjb250aW51ZSBvdXIgYW5hbHlzaXMgb2YgdGhlIHJlZCB3aW5lIGRhdGEsIGFuZCBzZWUgaWYgd2UgY2FuIGltcHJvdmUgb3VyIHJlc3VsdHMgYnkgdXNpbmcgZGlmZmVyZW50IG1vZGVscyBhbmQgYnkgYWRqdXN0aW5nIHRyYWluaW5nIHBhcmFtZXRlcnMuDQoNCjxicj4NCg0KIyBSZWZlcmVuY2VzIHstICNSZWZ9DQo8ZGl2IGlkPSJyZWZzIj48L2Rpdj4NCg0KPGJyPg0KDQo8Zm9udCBjb2xvciA9ICJncmV5Ij4NClRoZXNlIG5vdGVzIGhhdmUgYmVlbiBwcmVwYXJlZCBieSBSdXBlcnQgS3V2ZWtlLiBQbGVhc2Ugbm90ZSB0aGF0IHNvbWUgb2YgdGhlIGNvbnRlbnQgaW4gdGhlc2Ugbm90ZXMgaGFzIGJlZW4gZGV2ZWxvcGVkIGZyb20gY29udGVudCBpbiBATW9kU3RhdC4gVGhlIGNvcHlyaWdodCBmb3IgdGhlIG1hdGVyaWFsIGluIHRoZXNlIG5vdGVzIHJlc2lkZXMgd2l0aCB0aGUgYXV0aG9ycyBuYW1lZCBhYm92ZSwgd2l0aCB0aGUgRGVwYXJ0bWVudCBvZiBNYXRoZW1hdGljYWwgYW5kIFBoeXNpY2FsIFNjaWVuY2VzIGFuZCB3aXRoIExhIFRyb2JlIFVuaXZlcnNpdHkuIENvcHlyaWdodCBpbiB0aGlzIHdvcmsgaXMgdmVzdGVkIGluIExhIFRyb2JlIFVuaXZlcnNpdHkgaW5jbHVkaW5nIGFsbCBMYSBUcm9iZSBVbml2ZXJzaXR5IGJyYW5kaW5nIGFuZCBuYW1pbmcuIFVubGVzcyBvdGhlcndpc2Ugc3RhdGVkLCBtYXRlcmlhbCB3aXRoaW4gdGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIGEgQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbi1Ob24gQ29tbWVyY2lhbC1Ob24gRGVyaXZhdGl2ZXMgTGljZW5zZSANCjxhIGhyZWYgPSAiaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLW5kLzQuMC9DQyIgdGFyZ2V0PSJfYmxhbmsiPiBCWS1OQy1ORC4gPC9hPg0KPC9mb250Pg==