Data Science Module

Topic 10B: Machine Learning I


Welcome to the tenth computer lab for the Data Science module. In this computer lab we will fit our first machine learning model to real data.

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 should be comfortable preparing data for supervised machine learning tasks, and have a better understanding of how to assess the performance of a machine learning model. Let’s get started!


1 Preparations

Before we proceed, please make sure you have read the content in the Introduction to Machine Learning in R supplement - if you haven’t, these lab questions will be unnecessarily difficult to understand and complete. It may also be helpful to keep this content open in a separate tab while you work through the lab material.

1.1 Load Required Packages

In order to conduct our machine learning processes in R in this lab and the subsequent lab, we will need to install and load several R packages, chief among which is the caret package (Kuhn et al. 2021).

Run the R code below to install and load the R packages required for this lab:

# Install packages
install.packages("caret", "magrittr", "rpart.plot")

# Load packages
library(caret)
library(rpart.plot)

1.2 Wine Data

For our machine learning work, we will assess 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 articles1.

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.

1.3

You can download the file winequality_red.csv from LMS - please do so now.

Once you have downloaded this data set, load the red wine data into R and save it in the object red_wine.

Hint: You can check the Code chunk below for the code to do this. This code assumes your data is saved in your local working directory.

red_wine <- read.csv(file = "winequality_red.csv", header = T)

1.4 Aim

Our aim is to train a machine learning model which can accurately predict the quality of a red wine, based on feature variable inputs.

As this is a classification problem, 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 following R code before proceeding further:

red_wine$quality <- as.factor(red_wine$quality)

2 Data Visualisation

Before we start analysing the data, it is prudent to take a quick look at it.

2.1

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?

2.2

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: It’s ok if you’re not sure about this yet - after all, we have only just started learning about machine learning.

2.3

We can use the featurePlot function from the caret package 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")

2.4

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.

3 Pre-Processing

Before we begin fitting a machine learning model, we should conduct some pre-processing checks, as outlined in Section 3.3 of the Introduction to Machine Learning in R supplement.

3.1

First, note that all our feature variables are numeric - therefore there is no need to create any dummy variables.

3.2

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.

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 and a cut-off value of 5 for the percentUnique values. Run this function twice, once with saveMetrics = T and once with saveMetrics = F.

Hint: If you are not sure how to proceed, check section 3.3.2 of the Introduction to Machine Learning in R supplement.

3.3

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?

3.4

Next, we should check for correlated feature variables. Generally, some correlation is normal and expected, but often it is beneficial to remove highly correlated feature variables from our data.

Compute a correlation matrix for the red_wine feature variables, check for extreme correlations close to 1 in magnitude, and use the summary function to assess the spread of correlation values.

Hint: You can follow the steps in section 3.3.2.1 of the Introduction to Machine Learning in R supplement for this question.

3.5

What are the largest negative and positive correlation values? Do these seem problematic?

3.6

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.

3.7

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 labs, we can scale and center our wine data, using the caret package function preProcess.

Run the R code below, to scale and center our red_wine data:

centre_scale <- preProcess(red_wine[, -12], 
                           method = c("center", "scale"))
red_wine_updated <- predict(centre_scale, red_wine)

3.8

If you now compare the original data to the updated data, using head(red_wine) and head(red_wine_updated) respectively, you should see that the feature variable values are now scaled and centred.

3.9

Try running the R code below. The box plots should now be easier to assess.

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?

3.10

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 above 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?

3.11 Training and Validation Data

Our final pre-training 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.

Please note that 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 theCode` 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) 

3.12

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

4 Fitting a Decision Tree Machine Learning Model

Now that the preparation phase is complete, we are ready to fit our first machine learning model using our red_wine_updated data.

The focus in this lab will be to introduce you to the train function from the caret package. We can fit a variety of machine learning models using this function (although some will also require other packages). We will start with a simple model, the Decision Tree, and then next week we will expand to looking at Random Forests, k-Nearest Neighbours, and other types of algorithms.

Using the train function, the basic code framework to fit each model is as follows:

object <- train(... ~ ., # specify relationship between outcome and feature variables
                data = ... , # specify training data
                method = "specify method here")

Regardless of what algorithm you use, there will be three main arguments you will need to include in your train function:

  • The relationship between the outcome variable and the feature variables
  • The data set
  • The method/algorithm to use

Let’s cover these in more detail.

In the first argument we specify the relationship between the outcome variable and the feature variables.

For example, if our outcome variable was called outcome, and we had two feature variables, feature1 and feature2, the first part of our code could look like this:

object <- train(outcome ~ feature1 + feature2, 
                ...)

In general however, we will have more than two feature variables to include (sometimes dozens more!). Therefore, we can use the shortcut outcome ~. to specify that all variables in the data set, apart from outcome, should be included as feature variables in the model.

As a result, when training a supervised learning machine learning model using the train function, typically all you will need to do when specifying your first argument is identify the name of your outcome variable, and include this name in place of outcome in outcome ~..

For the data argument, you will need to specify your pre-processed data set, and for the method argument, you will need to specify the machine learning method you would like to use - each has a different name.

Some models will include additional arguments, usually specified within the argument tuneGrid, and we will explain these where relevant.

Let’s begin.

4.1 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")

Note that the decision tree method name is rpart (which is unintuitive).

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). As we can see, 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?

4.1.1

Run the following R 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.

4.1.2

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. The example in the supplement material that used the penguins data was an exception - usually the data is not so nicely partitioned.

5 Validating Results

While we have a predictive accuracy estimate for our decision tree model, it is important to remember that this has been computed using the training data.

We would also like to check how the model performs when presented with new data - i.e. our validation data!

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.

5.1

There are several ways to perform cross-validation. One of the simplest is demonstrated in section 4.1 of the Introduction to Machine Learning in R supplement).

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_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 %>% round(2) * 100

dec_tree_accuracy

Run this code now.

5.2

Discuss the results of the cross-validation. Do you think the decision tree model’s reported accuracy in 4.1 is reliable?


Great work, that’s everything for today!

While we have just brushed the surface of machine learning, hopefully this lab has provided you with a better understanding of how to use R for machine learning - as we can see, it’s actually not that complicated to train a machine learning model in R (although getting a highly accurate one is often another story…).

Next week, 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.


References

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


These notes have been prepared by Rupert Kuveke. Please note that some of the content in these notes has been developed from content in Thulin (2021). The copyright for the material in these notes resides with the authors named above, with the Department of Mathematics and Statistics 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.

LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiAxMEIiDQpvdXRwdXQ6DQogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjogDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KYmlibGlvZ3JhcGh5OiBTVE0xMDAxX0RTX0NMX3JlZmVyZW5jZXMuYmliIA0KbGluay1jaXRhdGlvbnM6IHllcw0KLS0tDQoNCjxzdHlsZT4NCiNUT0Mgew0KICBiYWNrZ3JvdW5kOiB1cmwoImh0dHBzOi8vd3d3LmxhdHJvYmUuZWR1LmF1L19tZWRpYS9sYS10cm9iZS1hcGkvdjUvaW1nL2xvZ28uc3ZnIik7DQogIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjsNCiAgcGFkZGluZy10b3A6IDgwcHggIWltcG9ydGFudDsNCiAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsNCn0NCjwvc3R5bGU+DQoNCiMjIyBEYXRhIFNjaWVuY2UgTW9kdWxlIHstfQ0KDQojIyMgVG9waWMgMTBCOiBNYWNoaW5lIExlYXJuaW5nIEkgey19DQoNCjxicj4NCg0KV2VsY29tZSB0byB0aGUgdGVudGggY29tcHV0ZXIgbGFiIGZvciB0aGUgRGF0YSBTY2llbmNlIG1vZHVsZS4NCkluIHRoaXMgY29tcHV0ZXIgbGFiIHdlIHdpbGwgZml0IG91ciBmaXJzdCBtYWNoaW5lIGxlYXJuaW5nIG1vZGVsIHRvIHJlYWwgZGF0YS4NCg0KVGhpcyBjb21wdXRlciBsYWIgaXMgZGVzaWduZWQgdG8gcnVuIGFsb25nc2lkZSB0aGUgY29udGVudCBpbiB0aGUgW0ludHJvZHVjdGlvbiB0byBNYWNoaW5lIExlYXJuaW5nIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV9pbnRyb2R1Y3Rpb25fdG9fbWFjaGluZV9sZWFybmluZ19pbl9yLykuIFRoZSBtYXRlcmlhbCBpbiB0aGlzIHN1cHBsZW1lbnQgcHJvdmlkZXMgYWxsIHRoZSBiYWNrZ3JvdW5kIGluZm9ybWF0aW9uIG9uIG1hY2hpbmUgbGVhcm5pbmcgYW5kIG1hY2hpbmUgbGVhcm5pbmcgdGVybWlub2xvZ3kgeW91IHdpbGwgbmVlZCB0byBjb21wbGV0ZSB0aGlzIGxhYi4gDQoNCkJ5IHRoZSBlbmQgb2YgdGhpcyBsYWIsIHlvdSBzaG91bGQgYmUgY29tZm9ydGFibGUgcHJlcGFyaW5nIGRhdGEgZm9yIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZyB0YXNrcywgYW5kIGhhdmUgYSBiZXR0ZXIgdW5kZXJzdGFuZGluZyBvZiBob3cgdG8gYXNzZXNzIHRoZSBwZXJmb3JtYW5jZSBvZiBhIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwuIExldCdzIGdldCBzdGFydGVkIQ0KDQo8YnI+DQoNCiMgUHJlcGFyYXRpb25zIHsjcHJlcH0NCg0KQmVmb3JlIHdlIHByb2NlZWQsIHBsZWFzZSBtYWtlIHN1cmUgeW91IGhhdmUgcmVhZCB0aGUgY29udGVudCBpbiB0aGUgW0ludHJvZHVjdGlvbiB0byBNYWNoaW5lIExlYXJuaW5nIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV9pbnRyb2R1Y3Rpb25fdG9fbWFjaGluZV9sZWFybmluZ19pbl9yLykgLSBpZiB5b3UgaGF2ZW4ndCwgdGhlc2UgbGFiIHF1ZXN0aW9ucyB3aWxsIGJlIHVubmVjZXNzYXJpbHkgZGlmZmljdWx0IHRvIHVuZGVyc3RhbmQgYW5kIGNvbXBsZXRlLiBJdCBtYXkgYWxzbyBiZSBoZWxwZnVsIHRvIGtlZXAgdGhpcyBjb250ZW50IG9wZW4gaW4gYSBzZXBhcmF0ZSB0YWIgd2hpbGUgeW91IHdvcmsgdGhyb3VnaCB0aGUgbGFiIG1hdGVyaWFsLg0KDQojIyBMb2FkIFJlcXVpcmVkIFBhY2thZ2VzIHsjbG9hZH0NCg0KSW4gb3JkZXIgdG8gY29uZHVjdCBvdXIgbWFjaGluZSBsZWFybmluZyBwcm9jZXNzZXMgaW4gUiBpbiB0aGlzIGxhYiBhbmQgdGhlIHN1YnNlcXVlbnQgbGFiLCB3ZSB3aWxsIG5lZWQgdG8gaW5zdGFsbCBhbmQgbG9hZCBzZXZlcmFsIFIgcGFja2FnZXMsIGNoaWVmIGFtb25nIHdoaWNoIGlzIHRoZSBgY2FyZXRgIHBhY2thZ2UgW0BjYXJldF0uDQoNClJ1biB0aGUgUiBjb2RlIGJlbG93IHRvIGluc3RhbGwgYW5kIGxvYWQgdGhlIFIgcGFja2FnZXMgcmVxdWlyZWQgZm9yIHRoaXMgbGFiOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQojIEluc3RhbGwgcGFja2FnZXMNCmluc3RhbGwucGFja2FnZXMoImNhcmV0IiwgIm1hZ3JpdHRyIiwgInJwYXJ0LnBsb3QiKQ0KDQojIExvYWQgcGFja2FnZXMNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBpbmNsdWRlID0gRn0NCiMgU3BlY2lmeSByZXF1aXJlZCBwYWNrYWdlcw0KbWxfcGFja2FnZXMgPC0gYygiY2FyZXQiLCAibWFncml0dHIiLCAicnBhcnQucGxvdCIpDQojIEluc3RhbGwgbWlzc2luZyBwYWNrYWdlcw0KaW5zdGFsbC5wYWNrYWdlcyhzZXRkaWZmKG1sX3BhY2thZ2VzLCByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpKQ0KIyBMb2FkIGFsbCBwYWNrYWdlcw0KbGFwcGx5KG1sX3BhY2thZ2VzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQpgYGANCg0KIyMgV2luZSBEYXRhDQoNCkZvciBvdXIgbWFjaGluZSBsZWFybmluZyB3b3JrLCB3ZSB3aWxsIGFzc2VzcyBkYXRhIG9uIFBvcnR1Z3Vlc2UgKlZpbmhvIFZlcmRlKiB3aW5lLCBvYnRhaW5lZCBmcm9tIHRoZSBAVUNJV2luZSBhbmQgb3JpZ2luYWxseSBjb2xsZWN0ZWQgYnkgQHdpbmUuDQpUaGlzIGlzIGEgcmVhbCBkYXRhIHNldCB0aGF0IGhhcyBiZWVuIHJlZmVyZW5jZWQgaW4gZG96ZW5zIG9mIGFjYWRlbWljIHJlc2VhcmNoIGFydGljbGVzXltbU2VlIGEgc2hvcnRsaXN0IGhlcmVdKGh0dHBzOi8vYXJjaGl2ZS5pY3MudWNpLmVkdS9tbC9kYXRhc2V0cy9XaW5lKV0uDQoNClRoaXMgZGF0YSBjb25zaXN0cyBvZiAxMSBmZWF0dXJlIHZhcmlhYmxlcyByZWxhdGluZyB0byBwaHlzaWNvY2hlbWljYWwgYXNwZWN0cyBvZiB0aGUgd2luZSwgbmFtZWx5Og0KDQoqIGZpeGVkIGFjaWRpdHkNCiogdm9sYXRpbGUgYWNpZGl0eQ0KKiBjaXRyaWMgYWNpZA0KKiByZXNpZHVhbCBzdWdhcg0KKiBjaGxvcmlkZXMNCiogZnJlZSBzdWxmdXIgZGlveGlkZQ0KKiB0b3RhbCBzdWxmdXIgZGlveGlkZQ0KKiBkZW5zaXR5DQoqIHBIDQoqIHN1bHBoYXRlcw0KKiBhbGNvaG9sDQoNCkZvciBwcml2YWN5IHJlYXNvbnMsIG90aGVyIGZlYXR1cmUgYW5kIG91dGNvbWUgdmFyaWFibGVzIGxpa2UgYGdyYXBlX3R5cGVgLCBgYnJhbmRgIGFuZCBgc2FsZSBwcmljZWAgYXJlIG5vdCBhdmFpbGFibGUgKGJ1dCB3b3VsZCBiZSB2ZXJ5IGludGVyZXN0aW5nIHRvIGNvbnNpZGVyISkuDQoNClRoZSBvbmx5IGF2YWlsYWJsZSBvdXRjb21lIHZhcmlhYmxlIGlzIGBxdWFsaXR5YCwgd2hpY2ggaXMgYW4gaW50ZWdlciBzY29yZSBmcm9tIDAgdG8gMTAgKHdpdGggMCBkZW5vdGluZyBhIHRlcnJpYmxlIHdpbmUgYW5kIDEwIGRlbm90aW5nIGFuIGV4Y2VwdGlvbmFsIHdpbmUpLg0KDQpUaGlzIGRhdGEgaXMgc3BsaXQgaW50byB0d28gc2V0cyAtIG9uZSBmb3IgcmVkIHdpbmUgYW5kIG9uZSBmb3Igd2hpdGUgd2luZS4gV2Ugd2lsbCBhc3Nlc3Mgb25seSB0aGUgcmVkIHdpbmUgZGF0YSBzZXQuDQoNCiMjDQoNCllvdSBjYW4gZG93bmxvYWQgdGhlIGZpbGUgYHdpbmVxdWFsaXR5X3JlZC5jc3ZgIGZyb20gTE1TIC0gcGxlYXNlIGRvIHNvIG5vdy4NCg0KT25jZSB5b3UgaGF2ZSBkb3dubG9hZGVkIHRoaXMgZGF0YSBzZXQsIGxvYWQgdGhlIHJlZCB3aW5lIGRhdGEgaW50byBSIGFuZCBzYXZlIGl0IGluIHRoZSBvYmplY3QgYHJlZF93aW5lYC4NCg0KKkhpbnQ6IFlvdSBjYW4gY2hlY2sgdGhlIGBDb2RlYCBjaHVuayBiZWxvdyBmb3IgdGhlIGNvZGUgdG8gZG8gdGhpcy4gVGhpcyBjb2RlIGFzc3VtZXMgeW91ciBkYXRhIGlzIHNhdmVkIGluIHlvdXIgbG9jYWwgd29ya2luZyBkaXJlY3RvcnkuKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpyZWRfd2luZSA8LSByZWFkLmNzdihmaWxlID0gIndpbmVxdWFsaXR5X3JlZC5jc3YiLCBoZWFkZXIgPSBUKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gVCwgaW5jbHVkZSA9IEZ9DQpyZWRfd2luZSA8LSByZWFkLmNzdihmaWxlID0gImRhdGEvd2luZXF1YWxpdHlfcmVkLmNzdiIsIGhlYWRlciA9IFQpDQpgYGANCg0KIyMgQWltDQoNCk91ciBhaW0gaXMgdG8gdHJhaW4gYSBtYWNoaW5lIGxlYXJuaW5nIG1vZGVsIHdoaWNoIGNhbiBhY2N1cmF0ZWx5IHByZWRpY3QgdGhlIHF1YWxpdHkgb2YgYSByZWQgd2luZSwgYmFzZWQgb24gZmVhdHVyZSB2YXJpYWJsZSBpbnB1dHMuDQoNCkFzIHRoaXMgaXMgYSBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtLCB3ZSBuZWVkIHRvIGVuc3VyZSB0aGF0IHRoZSBgcXVhbGl0eWAgdmFyaWFibGUgdmFsdWVzIGFyZSB0cmVhdGVkIGFzIGZhY3RvcnMsIHJhdGhlciB0aGFuIGFzIG51bWJlcnMgKG90aGVyd2lzZSBvdXIgbW9kZWwgbWlnaHQgZW5kIHVwIHByZWRpY3RpbmcgcXVhbGl0eSBzY29yZXMgb2YgZS5nLiA3LjI1KS4gDQoNClRoZXJlZm9yZSwgbWFrZSBzdXJlIHRvIHJ1biB0aGUgZm9sbG93aW5nIFIgY29kZSBiZWZvcmUgcHJvY2VlZGluZyBmdXJ0aGVyOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpyZWRfd2luZSRxdWFsaXR5IDwtIGFzLmZhY3RvcihyZWRfd2luZSRxdWFsaXR5KQ0KYGBgDQoNCg0KIyBEYXRhIFZpc3VhbGlzYXRpb24geyNkYXRhdml6fQ0KDQpCZWZvcmUgd2Ugc3RhcnQgYW5hbHlzaW5nIHRoZSBkYXRhLCBpdCBpcyBwcnVkZW50IHRvIHRha2UgYSBxdWljayBsb29rIGF0IGl0Lg0KDQojIw0KDQpUbyBiZWdpbiwgdXNlIHRoZSBgaGVhZGAsIGBzdW1tYXJ5YCBhbmQgYGRpbWAgUiBmdW5jdGlvbnMgdG8gb2J0YWluIGRldGFpbHMgYWJvdXQgdGhlIGNvbXBvc2l0aW9uIG9mIHRoZSBkYXRhLiBEbyB5b3Ugbm90aWNlIGFueSBpbnRlcmVzdGluZyBkZXRhaWxzPw0KDQojIyB7I3Byb2JsZW19DQoNClVzZSB0aGUgYHBsb3RgIGZ1bmN0aW9uIHRvIHByb2R1Y2UgYSBiYXIgcGxvdCBvZiB0aGUgYHF1YWxpdHlgIHZhcmlhYmxlIHZhbHVlcyAtIHdoYXQgZG8geW91IG5vdGljZT8NCg0KQmFzZWQgb24gdGhlIG9ic2VydmVkIHZhbHVlcyBmb3IgdGhpcyB2YXJpYWJsZSwgY2FuIHlvdSB0aGluayBvZiBhbnkgcG90ZW50aWFsIHByb2JsZW1zIHdlIG1pZ2h0IGVuY291bnRlciB3aGVuIHRyeWluZyB0byBwcmVkaWN0IGNlcnRhaW4gcXVhbGl0eSB2YWx1ZXM/DQoNCipOb3RlOiBJdCdzIG9rIGlmIHlvdSdyZSBub3Qgc3VyZSBhYm91dCB0aGlzIHlldCAtIGFmdGVyIGFsbCwgd2UgaGF2ZSBvbmx5IGp1c3Qgc3RhcnRlZCBsZWFybmluZyBhYm91dCBtYWNoaW5lIGxlYXJuaW5nLioNCg0KIyMgDQoNCldlIGNhbiB1c2UgdGhlIGBmZWF0dXJlUGxvdGAgZnVuY3Rpb24gZnJvbSB0aGUgYGNhcmV0YCBwYWNrYWdlIHRvIHByb2R1Y2UgYm94IHBsb3RzIGZvciBhbGwgdGhlIGZlYXR1cmUgdmFyaWFibGVzIGluIG91ciBkYXRhIHNldC4NClJ1biB0aGUgY29kZSBiZWxvdyB0byBkbyB0aGlzOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGZpZy5kaW0gPSBjKDYsNil9DQpmZWF0dXJlUGxvdCh4ID0gcmVkX3dpbmVbLCAtMTJdLCANCiAgICAgICAgICAgIHkgPSByZWRfd2luZSRxdWFsaXR5LCANCiAgICAgICAgICAgIHBsb3QgPSAiYm94IikNCmBgYA0KDQojIw0KDQpZb3Ugd2lsbCBub3RpY2UgdGhhdCBpdCdzIGhhcmQgdG8gc2VlIG1vc3Qgb2YgdGhlIGJveCBwbG90cywgYmVjYXVzZSB0d28gZmVhdHVyZSB2YXJpYWJsZXMsIGBmcmVlLnN1bGZ1ci5kaW94aWRlYCBhbmQgYHRvdGFsLnN1bGZ1ci5kaW94aWRlYCwgaGF2ZSBtdWNoIGxhcmdlciB2YWx1ZXMgdGhhbiB0aGUgb3RoZXIgZmVhdHVyZSB2YXJpYWJsZXMuIFdoaWxlIHRoaXMgaXNuJ3QgbmVjZXNzYXJpbHkgYSBwcm9ibGVtICh3ZSBjYW4ganVzdCBwcm9kdWNlIGJveCBwbG90cyBmb3IgZWFjaCBvZiB0aGUgZmVhdHVyZSB2YXJpYWJsZXMgaW4gdHVybiksIHRoZXJlIGlzIGEgbWV0aG9kIHRvIGFkZHJlc3MgdGhpcywgd2hpY2ggd2Ugd2lsbCBkaXNjdXNzIGluIHRoZSBuZXh0IHNlY3Rpb24uDQoNCkZvciB0aGUgbW9tZW50IGhvd2V2ZXIsIHRyeSB0byByZWNyZWF0ZSB0aGVzZSBib3ggcGxvdHMsIHdpdGhvdXQgaW5jbHVkaW5nIHRoZSBmZWF0dXJlIHZhcmlhYmxlcyBgZnJlZS5zdWxmdXIuZGlveGlkZWAgYW5kIGB0b3RhbC5zdWxmdXIuZGlveGlkZWAuDQoNCipIaW50OiBOb3RlIGhvdyBpbiB0aGUgYGZlYXR1cmVQbG90YCBjb2RlIGFib3ZlLCB3ZSBoYXZlIHVzZWQgYHggPSByZWRfd2luZVssIC0xMl1gIHRvIHNwZWNpZnkgdGhhdCB0aGUgMTJ0aCBjb2x1bW4gKHF1YWxpdHkpIHNob3VsZCBub3QgYmUgaW5jbHVkZWQgaW4gdGhlIHBsb3R0ZWQgeCB2YXJpYWJsZXMgLSB5b3UgY2FuIHVzZSB0aGlzIGFzIGEgZ3VpZGUgb24gaG93IHRvIGlnbm9yZSBgZnJlZS5zdWxmdXIuZGlveGlkZWAgYW5kIGB0b3RhbC5zdWxmdXIuZGlveGlkZWAuKg0KDQojIFByZS1Qcm9jZXNzaW5nIHsjcHJlcHJvfQ0KDQpCZWZvcmUgd2UgYmVnaW4gZml0dGluZyBhIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwsIHdlIHNob3VsZCBjb25kdWN0IHNvbWUgcHJlLXByb2Nlc3NpbmcgY2hlY2tzLCBhcyBvdXRsaW5lZCBpbiBbU2VjdGlvbiAzLjMgb2YgdGhlIEludHJvZHVjdGlvbiB0byBNYWNoaW5lIExlYXJuaW5nIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV9pbnRyb2R1Y3Rpb25fdG9fbWFjaGluZV9sZWFybmluZ19pbl9yL21hY2hpbmUtbGVhcm5pbmctaW4tci11c2luZy10aGUtY2FyZXQtcGFja2FnZS5odG1sI3ByZS1wcm9jZXNzaW5nLTEpLg0KDQojIw0KDQpGaXJzdCwgbm90ZSB0aGF0IGFsbCBvdXIgZmVhdHVyZSB2YXJpYWJsZXMgYXJlIG51bWVyaWMgLSB0aGVyZWZvcmUgdGhlcmUgaXMgbm8gbmVlZCB0byBjcmVhdGUgYW55IGR1bW15IHZhcmlhYmxlcy4NCg0KIyMNCg0KV2UgY2FuIHVzZSB0aGUgZnVuY3Rpb24gYG5lYXJaZXJvVmFyYCBmcm9tIHRoZSBgY2FyZXRgIHBhY2thZ2UgdG8gb2J0YWluIGRldGFpbHMgb24gdGhlIGBmcmVxUmF0aW9gIGFuZCBgcGVyY2VudFVuaXF1ZWAgdmFsdWVzIGZvciBlYWNoIG9mIHRoZSB2YXJpYWJsZXMgaW4gb3VyIGByZWRfd2luZWAgZGF0YSBzZXQuIA0KDQpSZWNhbGwgdGhhdCB0aGUgYG5lYXJaZXJvVmFyYCBmdW5jdGlvbiBjYW4gaW5jbHVkZSBhZGRpdGlvbmFsIGFyZ3VtZW50cywgYGZyZXFDdXRgIGFuZCBgdW5pcXVlQ3V0YCwgdGhhdCBzcGVjaWZ5IGN1dC1vZmYgdmFsdWVzIGZvciB0aGUgYGZyZXFSYXRpb2AgYW5kIGBwZXJjZW50VW5pcXVlYCByZXN1bHRzIHJlc3BlY3RpdmVseS4NCg0KVXNlIHRoZSBgbmVhclplcm9WYXJgIGZ1bmN0aW9uIHRvIGFzc2VzcyB0aGUgZmVhdHVyZSB2YXJpYWJsZXMgaW4gdGhlIGByZWRfd2luZWAgZGF0YSBzZXQsIGFuZCBzcGVjaWZ5IGEgY3V0LW9mZiB2YWx1ZSBvZiAyIGZvciB0aGUgYGZyZXFSYXRpb2AgdmFsdWVzIGFuZCBhIGN1dC1vZmYgdmFsdWUgb2YgNSBmb3IgdGhlIGBwZXJjZW50VW5pcXVlYCB2YWx1ZXMuIFJ1biB0aGlzIGZ1bmN0aW9uIHR3aWNlLCBvbmNlIHdpdGggYHNhdmVNZXRyaWNzID0gVGAgYW5kIG9uY2Ugd2l0aCBgc2F2ZU1ldHJpY3MgPSBGYC4NCg0KKkhpbnQ6IElmIHlvdSBhcmUgbm90IHN1cmUgaG93IHRvIHByb2NlZWQsIGNoZWNrIHNlY3Rpb24gMy4zLjIgb2YgdGhlIFtJbnRyb2R1Y3Rpb24gdG8gTWFjaGluZSBMZWFybmluZyBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21faW50cm9kdWN0aW9uX3RvX21hY2hpbmVfbGVhcm5pbmdfaW5fci9tYWNoaW5lLWxlYXJuaW5nLWluLXItdXNpbmctdGhlLWNhcmV0LXBhY2thZ2UuaHRtbCNpZGVudGlmeWluZy1zYW1wbGVzLWV4ZXJ0aW5nLWV4Y2Vzc2l2ZS1pbmZsdWVuY2UpLioNCg0KIyMNCg0KQmFzZWQgb24gdGhlIGBuZWFyWmVyb1ZhcmAgZnVuY3Rpb24gcmVzdWx0cywgY2hlY2sgZm9yIHBvdGVudGlhbGx5IHByb2JsZW1hdGljIHZhcmlhYmxlcy4gDQpXaGljaCBmZWF0dXJlIHZhcmlhYmxlIGhhcyB0aGUgaGlnaGVzdCBgZnJlcVJhdGlvYCB2YWx1ZSwgYW5kIHdoaWNoIGZlYXR1cmUgdmFyaWFibGUgaGFzIHRoZSBsb3dlc3QgYHBlcmNlbnRVbmlxdWVgIHZhbHVlPw0KV2hhdCBkbyB5b3UgY29uY2x1ZGU/DQoNCiMjDQoNCk5leHQsIHdlIHNob3VsZCBjaGVjayBmb3IgY29ycmVsYXRlZCBmZWF0dXJlIHZhcmlhYmxlcy4gDQpHZW5lcmFsbHksIHNvbWUgY29ycmVsYXRpb24gaXMgbm9ybWFsIGFuZCBleHBlY3RlZCwgYnV0IG9mdGVuIGl0IGlzIGJlbmVmaWNpYWwgdG8gcmVtb3ZlIGhpZ2hseSBjb3JyZWxhdGVkIGZlYXR1cmUgdmFyaWFibGVzIGZyb20gb3VyIGRhdGEuDQoNCkNvbXB1dGUgYSBjb3JyZWxhdGlvbiBtYXRyaXggZm9yIHRoZSBgcmVkX3dpbmVgIGZlYXR1cmUgdmFyaWFibGVzLCBjaGVjayBmb3IgZXh0cmVtZSBjb3JyZWxhdGlvbnMgY2xvc2UgdG8gMSBpbiBtYWduaXR1ZGUsIGFuZCB1c2UgdGhlIGBzdW1tYXJ5YCBmdW5jdGlvbiB0byBhc3Nlc3MgdGhlIHNwcmVhZCBvZiBjb3JyZWxhdGlvbiB2YWx1ZXMuIA0KDQoqSGludDogWW91IGNhbiBmb2xsb3cgdGhlIHN0ZXBzIGluIHNlY3Rpb24gMy4zLjIuMSBvZiB0aGUgW0ludHJvZHVjdGlvbiB0byBNYWNoaW5lIExlYXJuaW5nIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV9pbnRyb2R1Y3Rpb25fdG9fbWFjaGluZV9sZWFybmluZ19pbl9yL21hY2hpbmUtbGVhcm5pbmctaW4tci11c2luZy10aGUtY2FyZXQtcGFja2FnZS5odG1sI2NvcnJlbGF0ZWQtZmVhdHVyZS12YXJpYWJsZXMpIGZvciB0aGlzIHF1ZXN0aW9uLioNCg0KIyMgDQoNCldoYXQgYXJlIHRoZSBsYXJnZXN0IG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSBjb3JyZWxhdGlvbiB2YWx1ZXM/IERvIHRoZXNlIHNlZW0gcHJvYmxlbWF0aWM/DQoNCiMjIA0KDQpCYXNlZCBvbiB5b3VyIGNhbGN1bGF0aW9ucywgZG8geW91IHRoaW5rIHdlIG5lZWQgdG8gdXNlIHRoZSBgZmluZENvcnJlbGF0aW9uYCBmdW5jdGlvbiB0byBpZGVudGlmeSBoaWdobHkgY29ycmVsYXRlZCBmZWF0dXJlIHZhcmlhYmxlcyB0byByZW1vdmUgZnJvbSBvdXIgZGF0YSBzZXQ/IFdoeSBvciB3aHkgbm90Pw0KDQoqTm90ZTogRm9yIHRoZSByZW1haW5kZXIgb2YgdGhpcyBsYWIsIHdlIHdpbGwgYXNzdW1lIG5vIGZlYXR1cmUgdmFyaWFibGVzIG5lZWRlZCB0byBiZSByZW1vdmVkLioNCg0KIyMNCg0KT25lIGFzcGVjdCBvZiBwcmUtcHJvY2Vzc2luZyB0aGF0IHdlIGRpZCBub3QgZGlzY3VzcyBpbiB0aGUgc3VwcGxlbWVudGFyeSBtYXRlcmlhbCBpcyB0aGUgY29uY2VwdCBvZiBtb2RpZnlpbmcgb3VyIGRhdGEgdmFsdWVzLCB0byBlbnN1cmUgc21vb3RoZXIgY29tcGFyaXNvbnMgYmV0d2VlbiBkaWZmZXJlbnQgZmVhdHVyZSB2YXJpYWJsZXMuDQoNClNpbWlsYXJseSB0byB0aGUgbm9ybWFsaXNhdGlvbiBwcm9jZXNzZXMgY29uZHVjdGVkIGluIGVhcmxpZXIgbGFicywgd2UgY2FuIGBzY2FsZWAgYW5kIGBjZW50ZXJgIG91ciB3aW5lIGRhdGEsIHVzaW5nIHRoZSBgY2FyZXRgIHBhY2thZ2UgZnVuY3Rpb24gYHByZVByb2Nlc3NgLg0KDQpSdW4gdGhlIFIgY29kZSBiZWxvdywgdG8gc2NhbGUgYW5kIGNlbnRlciBvdXIgYHJlZF93aW5lYCBkYXRhOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpjZW50cmVfc2NhbGUgPC0gcHJlUHJvY2VzcyhyZWRfd2luZVssIC0xMl0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gYygiY2VudGVyIiwgInNjYWxlIikpDQpyZWRfd2luZV91cGRhdGVkIDwtIHByZWRpY3QoY2VudHJlX3NjYWxlLCByZWRfd2luZSkNCmBgYA0KDQojIw0KDQpJZiB5b3Ugbm93IGNvbXBhcmUgdGhlIG9yaWdpbmFsIGRhdGEgdG8gdGhlIHVwZGF0ZWQgZGF0YSwgdXNpbmcgYGhlYWQocmVkX3dpbmUpYCBhbmQgYGhlYWQocmVkX3dpbmVfdXBkYXRlZClgIHJlc3BlY3RpdmVseSwgeW91IHNob3VsZCBzZWUgdGhhdCB0aGUgZmVhdHVyZSB2YXJpYWJsZSB2YWx1ZXMgYXJlIG5vdyBzY2FsZWQgYW5kIGNlbnRyZWQuDQoNCiMjIHsjYm94cGxvdG5ld30NCg0KVHJ5IHJ1bm5pbmcgdGhlIFIgY29kZSBiZWxvdy4gVGhlIGJveCBwbG90cyBzaG91bGQgbm93IGJlIGVhc2llciB0byBhc3Nlc3MuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCmZlYXR1cmVQbG90KHggPSByZWRfd2luZV91cGRhdGVkWywgLTEyXSwgDQogICAgICAgICAgICB5ID0gcmVkX3dpbmVfdXBkYXRlZCRxdWFsaXR5LCANCiAgICAgICAgICAgIHBsb3QgPSAiYm94IiwNCiAgICAgICAgICAgIGF1dG8ua2V5ID0gbGlzdChjb2x1bW5zID0gNikpDQpgYGANCg0KRG8gYW55IG9mIHRoZSBmZWF0dXJlcyBzZWVtIHRvIGhhdmUgbXVjaCBhc3NvY2lhdGlvbiB3aXRoIHdpbmUgYHF1YWxpdHlgPyBEbyB5b3Ugbm90aWNlIGEgdHJlbmQgb3IgcGF0dGVybiBpbiB0aGUgYm94IHBsb3RzIGZvciB0aGUgZGlmZmVyZW50IGBxdWFsaXR5YCB2YWx1ZXMgd2l0aGluIGFueSBwYXJ0aWN1bGFyIGZlYXR1cmVzPw0KDQojIw0KDQpFdmVuIHRob3VnaCB0aGUgYm94IHBsb3RzIHByb2R1Y2VkIGluIFxAcmVmKGJveHBsb3RuZXcpIGFyZSBjbGVhcmVyIHRoYW4gdGhvc2UgcHJvZHVjZWQgaW4gc2VjdGlvbiBcQHJlZihkYXRhdml6KSwgaXQgaXMgc3RpbGwgcXVpdGUgZGlmZmljdWx0IHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gdGhlIGBxdWFsaXR5YCByYXRpbmdzLg0KDQpSZXBsYWNlIHRoZSBgcGxvdCA9ICJib3giYCBhcmd1bWVudCBpbiB0aGUgY29kZSBhYm92ZSB3aXRoIGBwbG90ID0gInBhaXJzImAgdG8gcHJvZHVjZSBzY2F0dGVyIHBsb3RzIGluc3RlYWQgb2YgYm94IHBsb3RzIGZvciBvdXIgcHJlLXByb2Nlc3NlZCBkYXRhLiANCg0KRG8geW91IG5vdGljZSBhbnkgY2xlYXIgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgZGlmZmVyZW50IGBxdWFsaXR5YCByYXRpbmdzPw0KDQojIyBUcmFpbmluZyBhbmQgVmFsaWRhdGlvbiBEYXRhIHsjdHJhaW59DQoNCk91ciBmaW5hbCBwcmUtdHJhaW5pbmcgc3RlcCBpcyB0byBzcGxpdCBvdXIgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uIHNldHMuDQpVc2UgdGhlIGBjcmVhdGVEYXRhUGFydGl0aW9uYCBmdW5jdGlvbiBmcm9tIHRoZSBgY2FyZXRgIHBhY2thZ2UgdG8gc3BsaXQgdGhlIGByZWRfd2luZV91cGRhdGVkYCBkYXRhIDgwLzIwLg0KDQoqUGxlYXNlIG5vdGUgdGhhdCB0aGUgZGF0YSBwYXJ0aXRpb25pbmcgaW50byB0cmFpbmluZyBvciB2YWxpZGF0aW9uIGNhdGVnb3JpZXMgaXMgcmFuZG9tIHRvIGFuIGV4dGVudCwgc28gaWYgeW91IGRvIG5vdCBydW4gdGhlIGBzZXQuc2VlZCgxNjUwKSBjb21tYW5kcyBzaG93biBpbiB0aGUgYENvZGVgIGNodW5rcyBiZWxvdywgeW91ciByZXN1bHRzIGZyb20gdGhpcyBwb2ludCBvbndhcmRzIG1heSBkaWZmZXIgc2xpZ2h0bHkgdG8gdGhvc2UgcHJlc2VudGVkIGluIHRoZSBzdWJzZXF1ZW50IHF1ZXN0aW9uIHNvbHV0aW9ucywgc2luY2UgeW91ciB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbiBkYXRhIHNldHMgd2lsbCBtb3N0IGxpa2VseSBjb250YWluIHNsaWdodGx5IGRpZmZlcmVudCBzZXRzIG9mIG9ic2VydmF0aW9ucy4qDQoNClRoZSBjb2RlIGJlbG93IGlzIHBhcnRpYWxseSBjb21wbGV0ZWQsIGp1c3QgZmlsbCBpbiB0aGUgYC4uLmAgbWlzc2luZyBwYXJ0czoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0Kc2V0LnNlZWQoMTY1MCkNCndpbmVfdHJhaW5faW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbiguLi4gLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwID0gLi4uICwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCA9IEZBTFNFLCB0aW1lcyA9IDEpIA0KYGBgDQoNCipIaW50OiBSZW1lbWJlciB0aGF0IHRoZSBhcmd1bWVudCBgcGAgZGVub3RlcyB0aGUgc3BsaXQuIElmIHlvdSBhcmUgc3R1Y2ssIHlvdSBjYW4gY2hlY2sgdGhlIGBDb2RlYCBjaHVuayBiZWxvdzoqDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnNldC5zZWVkKDE2NTApDQp3aW5lX3RyYWluX2luZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24ocmVkX3dpbmVfdXBkYXRlZCRxdWFsaXR5LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwID0gLjgsICMgaGVyZSBwIGRlc2lnbmF0ZXMgdGhlIHNwbGl0IC0gODAvMjANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ID0gRkFMU0UsIHRpbWVzID0gMSkgDQpgYGANCg0KIyMNCg0KTmV4dCwgYXNzaWduIHRoZSBgcmVkX3dpbmVfdXBkYXRlZGAgZGF0YSBpbnRvIHRoZSB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbiBzZXRzLCBhbmQgbmFtZSB0aGVzZSBgcmVkX3dpbmVfdHJhaW5gIGFuZCBgcmVkX3dpbmVfdmFsaWRhdGVgDQpyZXNwZWN0aXZlbHkuIENoZWNrIHRoZSBjb2RlIGJlbG93IGZvciBhIGhlYWQgc3RhcnQ6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnJlZF93aW5lX3ZhbGlkYXRlIDwtIHJlZF93aW5lX3VwZGF0ZWRbLXdpbmVfdHJhaW5faW5kZXgsIF0NCmBgYA0KDQoqSGludDogSWYgeW91IGFyZSBzdHVjaywgeW91IGNhbiBjaGVjayB0aGUgYENvZGVgIGNodW5rIGJlbG93OioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KIyBOb3RlIGhlcmUgd2UgYXJlIHVzaW5nIHRoZSB2YWx1ZXMgaW4gdGhlIHdpbmVfdHJhaW5faW5kZXgNCiMgKHdoZXJlYXMgZm9yIHRoZSB2YWxpZGF0aW9uIHNldCwgd2Ugc2VsZWN0IHRoZSB2YWx1ZXMgbm90IGluIHRoZSB3aW5lX3RyYWluX2luZGV4KQ0KcmVkX3dpbmVfdHJhaW4gPC0gcmVkX3dpbmVfdXBkYXRlZFt3aW5lX3RyYWluX2luZGV4LCBdDQpgYGANCg0KIyBGaXR0aW5nIGEgRGVjaXNpb24gVHJlZSBNYWNoaW5lIExlYXJuaW5nIE1vZGVsIHsjZml0fQ0KDQpOb3cgdGhhdCB0aGUgcHJlcGFyYXRpb24gcGhhc2UgaXMgY29tcGxldGUsIHdlIGFyZSByZWFkeSB0byBmaXQgb3VyIGZpcnN0IG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwgdXNpbmcgb3VyIGByZWRfd2luZV91cGRhdGVkYCBkYXRhLiANCg0KVGhlIGZvY3VzIGluIHRoaXMgbGFiIHdpbGwgYmUgdG8gaW50cm9kdWNlIHlvdSB0byB0aGUgYHRyYWluYCBmdW5jdGlvbiBmcm9tIHRoZSBgY2FyZXRgIHBhY2thZ2UuIFdlIGNhbiBmaXQgYSB2YXJpZXR5IG9mIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzIHVzaW5nIHRoaXMgZnVuY3Rpb24gKGFsdGhvdWdoIHNvbWUgd2lsbCBhbHNvIHJlcXVpcmUgb3RoZXIgcGFja2FnZXMpLiBXZSB3aWxsIHN0YXJ0IHdpdGggYSBzaW1wbGUgbW9kZWwsIHRoZSBEZWNpc2lvbiBUcmVlLCBhbmQgdGhlbiBuZXh0IHdlZWsgd2Ugd2lsbCBleHBhbmQgdG8gbG9va2luZyBhdCBSYW5kb20gRm9yZXN0cywgay1OZWFyZXN0IE5laWdoYm91cnMsIGFuZCBvdGhlciB0eXBlcyBvZiBhbGdvcml0aG1zLg0KDQpVc2luZyB0aGUgYHRyYWluYCBmdW5jdGlvbiwgdGhlIGJhc2ljIGNvZGUgZnJhbWV3b3JrIHRvIGZpdCBlYWNoIG1vZGVsIGlzIGFzIGZvbGxvd3M6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCm9iamVjdCA8LSB0cmFpbiguLi4gfiAuLCAjIHNwZWNpZnkgcmVsYXRpb25zaGlwIGJldHdlZW4gb3V0Y29tZSBhbmQgZmVhdHVyZSB2YXJpYWJsZXMNCiAgICAgICAgICAgICAgICBkYXRhID0gLi4uICwgIyBzcGVjaWZ5IHRyYWluaW5nIGRhdGENCiAgICAgICAgICAgICAgICBtZXRob2QgPSAic3BlY2lmeSBtZXRob2QgaGVyZSIpDQpgYGANCg0KUmVnYXJkbGVzcyBvZiB3aGF0IGFsZ29yaXRobSB5b3UgdXNlLCB0aGVyZSB3aWxsIGJlIHRocmVlIG1haW4gYXJndW1lbnRzIHlvdSB3aWxsIG5lZWQgdG8gaW5jbHVkZSBpbiB5b3VyIGB0cmFpbmAgZnVuY3Rpb246DQoNCiogVGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBvdXRjb21lIHZhcmlhYmxlIGFuZCB0aGUgZmVhdHVyZSB2YXJpYWJsZXMNCiogVGhlIGRhdGEgc2V0DQoqIFRoZSBtZXRob2QvYWxnb3JpdGhtIHRvIHVzZQ0KDQpMZXQncyBjb3ZlciB0aGVzZSBpbiBtb3JlIGRldGFpbC4NCg0KSW4gdGhlIGZpcnN0IGFyZ3VtZW50IHdlIHNwZWNpZnkgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBvdXRjb21lIHZhcmlhYmxlIGFuZCB0aGUgZmVhdHVyZSB2YXJpYWJsZXMuDQoNCkZvciBleGFtcGxlLCBpZiBvdXIgb3V0Y29tZSB2YXJpYWJsZSB3YXMgY2FsbGVkIGBvdXRjb21lYCwgYW5kIHdlIGhhZCB0d28gZmVhdHVyZSB2YXJpYWJsZXMsIGBmZWF0dXJlMWAgYW5kIGBmZWF0dXJlMmAsIHRoZSBmaXJzdCBwYXJ0IG9mIG91ciBjb2RlIGNvdWxkIGxvb2sgbGlrZSB0aGlzOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpvYmplY3QgPC0gdHJhaW4ob3V0Y29tZSB+IGZlYXR1cmUxICsgZmVhdHVyZTIsIA0KICAgICAgICAgICAgICAgIC4uLikNCmBgYA0KDQpJbiBnZW5lcmFsIGhvd2V2ZXIsIHdlIHdpbGwgaGF2ZSBtb3JlIHRoYW4gdHdvIGZlYXR1cmUgdmFyaWFibGVzIHRvIGluY2x1ZGUgKHNvbWV0aW1lcyBkb3plbnMgbW9yZSEpLiBUaGVyZWZvcmUsIHdlIGNhbiB1c2UgdGhlIHNob3J0Y3V0IGBvdXRjb21lIH4uYCB0byBzcGVjaWZ5IHRoYXQgYWxsIHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBzZXQsIGFwYXJ0IGZyb20gYG91dGNvbWVgLCBzaG91bGQgYmUgaW5jbHVkZWQgYXMgZmVhdHVyZSB2YXJpYWJsZXMgaW4gdGhlIG1vZGVsLg0KDQpBcyBhIHJlc3VsdCwgd2hlbiB0cmFpbmluZyBhIHN1cGVydmlzZWQgbGVhcm5pbmcgbWFjaGluZSBsZWFybmluZyBtb2RlbCB1c2luZyB0aGUgYHRyYWluYCBmdW5jdGlvbiwgdHlwaWNhbGx5IGFsbCB5b3Ugd2lsbCBuZWVkIHRvIGRvIHdoZW4gc3BlY2lmeWluZyB5b3VyIGZpcnN0IGFyZ3VtZW50IGlzIGlkZW50aWZ5IHRoZSBuYW1lIG9mIHlvdXIgb3V0Y29tZSB2YXJpYWJsZSwgYW5kIGluY2x1ZGUgdGhpcyBuYW1lIGluIHBsYWNlIG9mIGBvdXRjb21lYCBpbiBgb3V0Y29tZSB+LmAuDQoNCkZvciB0aGUgYGRhdGFgIGFyZ3VtZW50LCB5b3Ugd2lsbCBuZWVkIHRvIHNwZWNpZnkgeW91ciBwcmUtcHJvY2Vzc2VkIGRhdGEgc2V0LCBhbmQgZm9yIHRoZSBgbWV0aG9kYCBhcmd1bWVudCwgeW91IHdpbGwgbmVlZCB0byBzcGVjaWZ5IHRoZSBtYWNoaW5lIGxlYXJuaW5nIG1ldGhvZCB5b3Ugd291bGQgbGlrZSB0byB1c2UgLSBlYWNoIGhhcyBhIGRpZmZlcmVudCBuYW1lLg0KDQpTb21lIG1vZGVscyB3aWxsIGluY2x1ZGUgYWRkaXRpb25hbCBhcmd1bWVudHMsIHVzdWFsbHkgc3BlY2lmaWVkIHdpdGhpbiB0aGUgYXJndW1lbnQgYHR1bmVHcmlkYCwgYW5kIHdlIHdpbGwgZXhwbGFpbiB0aGVzZSB3aGVyZSByZWxldmFudC4NCg0KTGV0J3MgYmVnaW4uDQoNCiMjIERlY2lzaW9uIFRyZWUgeyNkZWN0cmVlfQ0KDQpPbmUgb2YgdGhlIHNpbXBsZXN0IG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzIHdlIGNhbiB1c2UgaXMgYSBkZWNpc2lvbiB0cmVlLiANCg0KVXNpbmcgdGhlIGluZm9ybWF0aW9uIGluIFxAcmVmKGZpdCksIGFuZCB0aGUgcGFydGlhbGx5IGNvbXBsZXRlIGNvZGUgaW4gdGhlIGBDb2RlYCBjaHVuayBiZWxvdywgZml0IGEgZGVjaXNpb24gdHJlZSB0byB5b3VyIHByZS1wcm9jZXNzZWQgYHJlZF93aW5lX3RyYWluYCB0cmFpbmluZyBkYXRhLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpzZXQuc2VlZCgxNjUwKSANCnJlZF93aW5lX2RlY2lzaW9uX3RyZWUgPC0gdHJhaW4oLi4uIH4gLiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gLi4uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJycGFydCIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgaW5jbHVkZSA9IEYsIGNhY2hlID0gVH0NCnNldC5zZWVkKDE2NTApIA0KcmVkX3dpbmVfZGVjaXNpb25fdHJlZSA8LSB0cmFpbihxdWFsaXR5IH4gLiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gcmVkX3dpbmVfdHJhaW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJwYXJ0IikNCmBgYA0KDQpOb3RlIHRoYXQgdGhlIGRlY2lzaW9uIHRyZWUgbWV0aG9kIG5hbWUgaXMgYHJwYXJ0YCAod2hpY2ggaXMgdW5pbnR1aXRpdmUpLiANCg0KT25jZSB5b3UgYXJlIGhhcHB5IHdpdGggeW91ciBjb2RlLCBydW4gaXQsIGFuZCB0aGVuIHJ1biB0aGUgb2JqZWN0IGByZWRfd2luZV9kZWNpc2lvbl90cmVlYCB0byBzZWUgdGhlIG91dHB1dC4gWW91ciBvdXRwdXQgc2hvdWxkIGxvb2sgbGlrZSB0aGUgb3V0cHV0IGluIHRoZSBgQ29kZWAgY2h1bmsgYmVsb3c6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCkNBUlQgDQoNCjEyODIgc2FtcGxlcw0KICAxMSBwcmVkaWN0b3INCiAgIDYgY2xhc3NlczogJzMnLCAnNCcsICc1JywgJzYnLCAnNycsICc4JyANCg0KTm8gcHJlLXByb2Nlc3NpbmcNClJlc2FtcGxpbmc6IEJvb3RzdHJhcHBlZCAoMjUgcmVwcykgDQpTdW1tYXJ5IG9mIHNhbXBsZSBzaXplczogMTI4MiwgMTI4MiwgMTI4MiwgMTI4MiwgMTI4MiwgMTI4MiwgLi4uIA0KUmVzYW1wbGluZyByZXN1bHRzIGFjcm9zcyB0dW5pbmcgcGFyYW1ldGVyczoNCg0KICBjcCAgICAgICAgICBBY2N1cmFjeSAgIEthcHBhICAgIA0KICAwLjAxMjIxMTY3ICAwLjU3MzcxMDcgIDAuMzAzMzUwOQ0KICAwLjAyMzc0NDkxICAwLjU2NTc0NTggIDAuMjY5NzA4NA0KICAwLjI1MjM3NDQ5ICAwLjQ3MTkwNjggIDAuMTExNjkxOQ0KDQpBY2N1cmFjeSB3YXMgdXNlZCB0byBzZWxlY3QgdGhlIG9wdGltYWwgbW9kZWwgdXNpbmcgdGhlIGxhcmdlc3QgdmFsdWUuDQpUaGUgZmluYWwgdmFsdWUgdXNlZCBmb3IgdGhlIG1vZGVsIHdhcyBjcCA9IDAuMDEyMjExNjcuDQpgYGANCg0KV2UgYXJlIG1haW5seSBpbnRlcmVzdGVkIGhlcmUgaW4gdGhlIGBBY2N1cmFjeWAgdmFsdWVzLCBmb3IgZGlmZmVyZW50IHR1bmluZyBwYXJhbWV0ZXIgdmFsdWVzICh3ZSBjYW4gaWdub3JlIHRoZSBgS2FwcGFgIHZhbHVlcykuDQpBcyB3ZSBjYW4gc2VlLCB0aGUgYmVzdCBhY2N1cmFjeSBhY2hpZXZlZCB3YXMgb25seSA1Ny4zNyUsIHdoaWNoIGlzIG5vdCBtdWNoIGJldHRlciB0aGFuIHJhbmRvbWx5IGd1ZXNzaW5nLg0KDQoqTm90ZTogRG9uJ3Qgd29ycnkgaWYgeW91ciByZXN1bHRzIGxvb2sgc2xpZ2h0bHkgZGlmZmVyZW50IC0gcGVyaGFwcyB5b3UgZGlkIG5vdCBydW4gYWxsIHRoZSBgc2V0LnNlZWQoMTY1MClgIGNvbW1hbmRzPyoNCg0KIyMjDQoNClJ1biB0aGUgZm9sbG93aW5nIFIgY29kZSB0byB2aXN1YWxpc2UgdGhpcyBkZWNpc2lvbiB0cmVlIG1vZGVsOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpycGFydC5wbG90KHJlZF93aW5lX2RlY2lzaW9uX3RyZWUkZmluYWxNb2RlbCkNCmBgYA0KDQoqTm90ZTogVGhlIGBycGFydC5wbG90YCBwYWNrYWdlIHJlcXVpcmVkIGhlcmUgc2hvdWxkIGhhdmUgYmVlbiBpbnN0YWxsZWQgYW5kIGxvYWRlZCBpbiBcQHJlZihwcmVwKS4qDQoNCiMjIw0KDQpBcyB5b3Ugc2hvdWxkIGJlIGFibGUgdG8gc2VlIGZyb20gdGhlIGRlY2lzaW9uIHRyZWUgZGlhZ3JhbSwgdGhlIG1vZGVsIGRpZCBub3QgaGF2ZSBlbm91Z2ggZGF0YSBvbiB3aW5lcyBvZiBsb3cgb3IgaGlnaCBxdWFsaXR5LCBhbmQgdGhlcmVmb3JlIGlzIG9ubHkgYWJsZSB0byBtYWtlIHByZWRpY3Rpb25zIGJldHdlZW4gd2luZXMgb2YgcXVhbGl0eSBgNWAgb3IgYDZgIC0gdGhpcyBleHBsYWlucyB0aGUgZGlzYXBwb2ludGluZyBvdmVyYWxsIHByZWRpY3RpdmUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsLg0KDQpUaGlzIGlzIHRoZSBwcm9ibGVtIHdlIHdlcmUgYWxsdWRpbmcgdG8gaW4gXEByZWYocHJvYmxlbSkuIFRoZSBleGFtcGxlIGluIHRoZSBzdXBwbGVtZW50IG1hdGVyaWFsIHRoYXQgdXNlZCB0aGUgYHBlbmd1aW5zYCBkYXRhIHdhcyBhbiBleGNlcHRpb24gLSB1c3VhbGx5IHRoZSBkYXRhIGlzIG5vdCBzbyBuaWNlbHkgcGFydGl0aW9uZWQuDQoNCiMgVmFsaWRhdGluZyBSZXN1bHRzIHsjdmFsfQ0KDQpXaGlsZSB3ZSBoYXZlIGEgcHJlZGljdGl2ZSBhY2N1cmFjeSBlc3RpbWF0ZSBmb3Igb3VyIGRlY2lzaW9uIHRyZWUgbW9kZWwsIGl0IGlzIGltcG9ydGFudCB0byByZW1lbWJlciB0aGF0IHRoaXMgaGFzIGJlZW4gY29tcHV0ZWQgdXNpbmcgdGhlIHRyYWluaW5nIGRhdGEuDQoNCldlIHdvdWxkIGFsc28gbGlrZSB0byBjaGVjayBob3cgdGhlIG1vZGVsIHBlcmZvcm1zIHdoZW4gcHJlc2VudGVkIHdpdGggbmV3IGRhdGEgLSBpLmUuIG91ciB2YWxpZGF0aW9uIGRhdGEhDQoNCldoZW4gY29uZHVjdGluZyBtYWNoaW5lIGxlYXJuaW5nLCB0aGVyZSBpcyBhIHJpc2sgb2Ygb3ZlcmZpdHRpbmcgb3VyIG1vZGVscyB0byBvdXIgdHJhaW5pbmcgZGF0YS4gVGhpcyBjYW4gcmVzdWx0IGluIHRoZSBtb2RlbHMgaGF2aW5nIGV4Y2VsbGVudCBhY2N1cmFjeSAqd2hlbiBhc3Nlc3NpbmcgdGhlIHRyYWluaW5nIGRhdGEqLCBidXQgaGF2aW5nIHN1YnBhciBwZXJmb3JtYW5jZSB3aGVuIHByZXNlbnRlZCB3aXRoIG5ldyBkYXRhLiANCg0KVGhpcyBpcyB3aHkgd2UgaGF2ZSBwdXQgYXNpZGUgc29tZSBvZiBvdXIgZGF0YSBhcyB2YWxpZGF0aW9uIGRhdGEgaW4gXEByZWYodHJhaW4pLCBzbyB0aGF0IHdlIGNhbiBwZXJmb3JtICpjcm9zcy12YWxpZGF0aW9uKi4NCklmIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwgcmVtYWlucyBzaW1pbGFyIHdoZW4gcHJlc2VudGVkIHdpdGggdGhlIHZhbGlkYXRpb24gZGF0YSwgdGhlbiB3ZSBjYW4gYmUgbW9yZSBjb25maWRlbnQgaW4gb3VyIG1vZGVsJ3MgcmVwb3J0ZWQgcGVyZm9ybWFuY2UuDQoNCiMjDQoNClRoZXJlIGFyZSBzZXZlcmFsIHdheXMgdG8gcGVyZm9ybSBjcm9zcy12YWxpZGF0aW9uLiBPbmUgb2YgdGhlIHNpbXBsZXN0IGlzIGRlbW9uc3RyYXRlZCBpbiBbc2VjdGlvbiA0LjEgb2YgdGhlIEludHJvZHVjdGlvbiB0byBNYWNoaW5lIExlYXJuaW5nIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV9pbnRyb2R1Y3Rpb25fdG9fbWFjaGluZV9sZWFybmluZ19pbl9yL21hY2hpbmUtbGVhcm5pbmctbW9kZWwtY2xhc3Nlcy5odG1sI2V4YW1wbGUtLS1ncmFkaWVudC1ib29zdGluZy1tYWNoaW5lLW1vZGVsKSkuIA0KDQpBbiBleGFtcGxlIGFwcGxpY2F0aW9uIG9mIHRoaXMgYXBwcm9hY2ggdG8gdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgcmVzdWx0cyBpcyBzaG93biBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KIyBMb2FkIG1hZ3JpdHRyIHBhY2thZ2UgZm9yIHBpcGluZw0KbGlicmFyeShtYWdyaXR0cikNCg0KIyBjb3VudCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHZhbGlkYXRpb24gZGF0YQ0KdmFsaWRhdGlvbl9udW1iZXJzIDwtIGRpbShyZWRfd2luZV92YWxpZGF0ZSlbMV0NCg0KIyBVc2UgdGhlIGZpdHRlZCBtb2RlbCB0byBwcmVkaWN0IHF1YWxpdHkgdmFsdWVzIGdpdmVuIHRoZSB2YWxpZGF0aW9uIGRhdGENCnByZWRpY3RfcmVkX3dpbmVfZGVjaXNpb25fdHJlZSA8LSBwcmVkaWN0KHJlZF93aW5lX2RlY2lzaW9uX3RyZWUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9cmVkX3dpbmVfdmFsaWRhdGUpDQojIFdoZW4gcnVuLCB0aGUgY29kZSBiZWxvdyBnaXZlcyB1cyB0aGUgcGVyY2VudGFnZSBvZiBjb3JyZWN0IHByZWRpY3Rpb25zDQpkZWNfdHJlZV9hY2N1cmFjeSA8LSBzdW0ocHJlZGljdF9yZWRfd2luZV9kZWNpc2lvbl90cmVlID09IHJlZF93aW5lX3ZhbGlkYXRlJHF1YWxpdHkpIA0KICAgICAgICAgICAgICAgICAgICAgLyB2YWxpZGF0aW9uX251bWJlcnMgJT4lIHJvdW5kKDIpICogMTAwDQoNCmRlY190cmVlX2FjY3VyYWN5DQpgYGANCg0KUnVuIHRoaXMgY29kZSBub3cuDQoNCiMjDQoNCkRpc2N1c3MgdGhlIHJlc3VsdHMgb2YgdGhlIGNyb3NzLXZhbGlkYXRpb24uIERvIHlvdSB0aGluayB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCdzIHJlcG9ydGVkIGFjY3VyYWN5IGluIFxAcmVmKGRlY3RyZWUpIGlzIHJlbGlhYmxlPw0KDQo8YnI+DQoNCiMjIyMgR3JlYXQgd29yaywgdGhhdCdzIGV2ZXJ5dGhpbmcgZm9yIHRvZGF5ISAjIyMjIHstfQ0KDQpXaGlsZSB3ZSBoYXZlIGp1c3QgYnJ1c2hlZCB0aGUgc3VyZmFjZSBvZiBtYWNoaW5lIGxlYXJuaW5nLCBob3BlZnVsbHkgdGhpcyBsYWIgaGFzIHByb3ZpZGVkIHlvdSB3aXRoIGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YgaG93IHRvIHVzZSBSIGZvciBtYWNoaW5lIGxlYXJuaW5nIC0gYXMgd2UgY2FuIHNlZSwgaXQncyBhY3R1YWxseSBub3QgdGhhdCBjb21wbGljYXRlZCB0byB0cmFpbiBhIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwgaW4gUiAoYWx0aG91Z2ggZ2V0dGluZyBhIGhpZ2hseSBhY2N1cmF0ZSBvbmUgaXMgb2Z0ZW4gYW5vdGhlciBzdG9yeS4uLikuIA0KDQpOZXh0IHdlZWssIHdlIHdpbGwgY29udGludWUgb3VyIGFuYWx5c2lzIG9mIHRoZSByZWQgd2luZSBkYXRhLCBhbmQgc2VlIGlmIHdlIGNhbiBpbXByb3ZlIG91ciByZXN1bHRzIGJ5IHVzaW5nIGRpZmZlcmVudCBtb2RlbHMgYW5kIGJ5IGFkanVzdGluZyB0cmFpbmluZyBwYXJhbWV0ZXJzLg0KDQo8YnI+DQoNCiMgUmVmZXJlbmNlcyB7LSAjUmVmfQ0KPGRpdiBpZD0icmVmcyI+PC9kaXY+DQoNCjxicj4NCg0KPGZvbnQgY29sb3IgPSAiZ3JleSI+DQpUaGVzZSBub3RlcyBoYXZlIGJlZW4gcHJlcGFyZWQgYnkgUnVwZXJ0IEt1dmVrZS4gUGxlYXNlIG5vdGUgdGhhdCBzb21lIG9mIHRoZSBjb250ZW50IGluIHRoZXNlIG5vdGVzIGhhcyBiZWVuIGRldmVsb3BlZCBmcm9tIGNvbnRlbnQgaW4gQE1vZFN0YXQuIFRoZSBjb3B5cmlnaHQgZm9yIHRoZSBtYXRlcmlhbCBpbiB0aGVzZSBub3RlcyByZXNpZGVzIHdpdGggdGhlIGF1dGhvcnMgbmFtZWQgYWJvdmUsIHdpdGggdGhlIERlcGFydG1lbnQgb2YgTWF0aGVtYXRpY3MgYW5kIFN0YXRpc3RpY3MgYW5kIHdpdGggTGEgVHJvYmUgVW5pdmVyc2l0eS4gQ29weXJpZ2h0IGluIHRoaXMgd29yayBpcyB2ZXN0ZWQgaW4gTGEgVHJvYmUgVW5pdmVyc2l0eSBpbmNsdWRpbmcgYWxsIExhIFRyb2JlIFVuaXZlcnNpdHkgYnJhbmRpbmcgYW5kIG5hbWluZy4gVW5sZXNzIG90aGVyd2lzZSBzdGF0ZWQsIG1hdGVyaWFsIHdpdGhpbiB0aGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgYSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbiBDb21tZXJjaWFsLU5vbiBEZXJpdmF0aXZlcyBMaWNlbnNlIA0KPGEgaHJlZiA9ICJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMtbmQvNC4wL0NDIiB0YXJnZXQ9Il9ibGFuayI+IEJZLU5DLU5ELiA8L2E+DQo8L2ZvbnQ+