1 Introduction and data preprocessing

1.1 Introduction and data structure

1.1.1 Exploring the credit data

We will be examining the dataset loan_data:

loan_data <- readRDS("data/loan_data_ch1.rds")

After being given loan_data, you are particularly interested about the defaulted loans in the data set. You want to get an idea of the number, and percentage of defaults. Defaults are rare, so you always want to check what the proportion of defaults is in a loan dataset. The CrossTable() function is very useful here.

Remember that default information is stored in the response variable loan_status, where 1 represents a default, and 0 represents non-default.

To learn more about variable structures and spot unexpected tendencies in the data, you should examine the relationship between loan_status and certain factor variables. For example, you would expect that the proportion of defaults in the group of customers with grade G (worst credit rating score) is substantially higher than the proportion of defaults in the grade A group (best credit rating score).

Conveniently, CrossTable() can also be applied on two categorical variables.

# View the structure of loan_data
str(loan_data)
'data.frame':   29092 obs. of  8 variables:
 $ loan_status   : int  0 0 0 0 0 0 1 0 1 0 ...
 $ loan_amnt     : int  5000 2400 10000 5000 3000 12000 9000 3000 10000 1000 ...
 $ int_rate      : num  10.6 NA 13.5 NA NA ...
 $ grade         : Factor w/ 7 levels "A","B","C","D",..: 2 3 3 1 5 2 3 2 2 4 ...
 $ emp_length    : int  10 25 13 3 9 11 0 3 3 0 ...
 $ home_ownership: Factor w/ 4 levels "MORTGAGE","OTHER",..: 4 4 4 4 4 3 4 4 4 4 ...
 $ annual_inc    : num  24000 12252 49200 36000 48000 ...
 $ age           : int  33 31 24 39 24 28 22 22 28 22 ...
# Load the gmodels package 
library(gmodels)

# Call CrossTable() on loan_status
CrossTable(loan_data$loan_status)

 
   Cell Contents
|-------------------------|
|                       N |
|         N / Table Total |
|-------------------------|

 
Total Observations in Table:  29092 

 
          |         0 |         1 | 
          |-----------|-----------|
          |     25865 |      3227 | 
          |     0.889 |     0.111 | 
          |-----------|-----------|



 

Call CrossTable() with x argument loan_data$grade and y argument loan_data$loan_status. We only want row-wise proportions, so set prop.r to TRUE, but prop.c , prop.t and prop.chisq to FALSE (default values here are TRUE, and this would lead to inclusion of column proportions, table proportions and chi-square contributions for each cell. We do not need these here.)

# Call CrossTable() on grade and loan_status
CrossTable(loan_data$grade, loan_data$loan_status, prop.r = TRUE, prop.c = FALSE, prop.t  = FALSE, prop.chisq = FALSE)

 
   Cell Contents
|-------------------------|
|                       N |
|           N / Row Total |
|-------------------------|

 
Total Observations in Table:  29092 

 
                | loan_data$loan_status 
loan_data$grade |         0 |         1 | Row Total | 
----------------|-----------|-----------|-----------|
              A |      9084 |       565 |      9649 | 
                |     0.941 |     0.059 |     0.332 | 
----------------|-----------|-----------|-----------|
              B |      8344 |       985 |      9329 | 
                |     0.894 |     0.106 |     0.321 | 
----------------|-----------|-----------|-----------|
              C |      4904 |       844 |      5748 | 
                |     0.853 |     0.147 |     0.198 | 
----------------|-----------|-----------|-----------|
              D |      2651 |       580 |      3231 | 
                |     0.820 |     0.180 |     0.111 | 
----------------|-----------|-----------|-----------|
              E |       692 |       176 |       868 | 
                |     0.797 |     0.203 |     0.030 | 
----------------|-----------|-----------|-----------|
              F |       155 |        56 |       211 | 
                |     0.735 |     0.265 |     0.007 | 
----------------|-----------|-----------|-----------|
              G |        35 |        21 |        56 | 
                |     0.625 |     0.375 |     0.002 | 
----------------|-----------|-----------|-----------|
   Column Total |     25865 |      3227 |     29092 | 
----------------|-----------|-----------|-----------|

 

The proportion of defaults increases when the credit rating moves from A to G (worse credit scores).

1.2 Histograms and outliers

1.2.1 Histograms

You previously explored categorical variables using the CrossTable() function. Now you would like to explore continuous variables to identify potential outliers or unexpected data structures.

To do this, let’s experiment with the function hist() to understand the distribution of the number of loans for different customers.

# Create histogram of loan_amnt: hist_1
hist_1 <- hist(loan_data$loan_amnt)

Use $breaks along with the object hist_1 to get more information on the histogram breaks. Knowing the location of the breaks is important because if they are poorly chosen, the histogram may be misleading.

# Print locations of the breaks in hist_1
hist_1$breaks
 [1]     0  2000  4000  6000  8000 10000 12000 14000 16000 18000 20000 22000 24000 26000 28000 30000
[17] 32000 34000 36000

Change the number of breaks in hist_1 to 200 by specifying the breaks argument. Additionally, name the x-axis “Loan amount” using the xlab argument and title it “Histogram of the loan amount” using the main argument. Save the result to hist_2.

# Change number of breaks and add labels: hist_2
hist_2 <- hist(loan_data$loan_amnt, breaks = 200, xlab = "Loan amount", 
               main = "Histogram of the loan amount")

Why do the peaks occur where they occur?

Note that there are some high peaks at round values: 5000, 10000, 15000,… People tend to borrow round numbers.

1.2.2 Outliers

Now it’s time to look at the structure of the variable age.

hist(loan_data$age)

There is a lot of blank space on the right-hand side of the plot. This is an indication of possible outliers. You will look at a scatterplot to verify this. If you find any outliers you will delete them.

If outliers are observed for several variables, it might be useful to look at bivariate plots. It’s possible the outliers belong to the same observation. If so, there is even more reason to delete the observation because it is more likely that some information stored in it is wrong.

# Plot the age variable
plot(loan_data$age, ylab = "Age")

The oldest person in this data set is older than 122 years! Get the index of this outlier using which() and the age of 122 as a cutoff (you can do this using loan_data$age > 122). Assign it to the object index_highage.

# Save the outlier's index to index_highage
index_highage <- which(loan_data$age > 122)

Create a new data set new_data, after removing the observation with the high age using the object index_highage.

# Create data set new_data with outlier deleted
new_data <- loan_data[-index_highage, ]

Have a look at the bivariate scatterplot, with age on the x-axis and annual income on the y-axis.

# Make bivariate scatterplot of age and annual income
plot(loan_data$age, loan_data$annual_inc, xlab = "Age", ylab = "Annual Income")

Did you notice that you have a bivariate outlier? The person with the huge annual wage of $6 million appeared to be 144 years old. This must be a mistake, so you will definitely delete this observation from the data in the following exercises.

1.3 Missing data and coarse classification

1.3.1 Deleting missing data

You saw before that the interest rate (int_rate) in the data set loan_data depends on the customer. Unfortunately some observations are missing interest rates. You now need to identify how many interest rates are missing and then delete them.

# Look at summary of loan_data
summary(loan_data$int_rate)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
   5.42    7.90   10.99   11.00   13.47   23.22    2776 
# Get indices of missing interest rates: na_index
na_index <- which(is.na(loan_data$int_rate))

# Remove observations with missing interest rates: loan_data_delrow_na
loan_data_delrow_na <- loan_data[-na_index, ]

# Make copy of loan_data
loan_data_delcol_na <- loan_data

# Delete interest rate column from loan_data_delcol_na
loan_data_delcol_na$int_rate <- NULL

1.3.2 Replacing missing data

Rather than deleting the missing interest rates, you may want to replace them instead.

Create an object called median_ir, containing the median of the interest rates in loan_data using the median() function. Don’t forget to include the argument na.rm = TRUE.

# Compute the median of int_rate
median_ir <- median(loan_data$int_rate, na.rm = TRUE)

# Make copy of loan_data
loan_data_replace <- loan_data

# Replace missing interest rates with median
loan_data_replace$int_rate[na_index] <- median_ir

# Check if the NAs are gone
summary(loan_data_replace$int_rate)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   5.42    8.49   10.99   11.00   13.11   23.22 

1.3.3 Keeping missing data

In some situations, the fact that an input is missing is important information in itself. NAs can be kept in a separate “missing” category using coarse classification.

Coarse classification allows you to simplify your data and improve the interpretability of your model. Coarse classification requires you to bin your responses into groups that contain ranges of values. You can use this binning technique to place all NAs in their own bin.

# Make the necessary replacements in the coarse classification example below 
loan_data$ir_cat <- rep(NA, length(loan_data$int_rate))

loan_data$ir_cat[which(loan_data$int_rate <= 8)] <- "0-8"
loan_data$ir_cat[which(loan_data$int_rate > 8 & loan_data$int_rate <= 11)] <- "8-11"
loan_data$ir_cat[which(loan_data$int_rate > 11 & loan_data$int_rate <= 13.5)] <- "11-13.5"
loan_data$ir_cat[which(loan_data$int_rate > 13.5)] <- "13.5+"
loan_data$ir_cat[which(is.na(loan_data$int_rate))] <- "Missing"

loan_data$ir_cat <- as.factor(loan_data$ir_cat)

# Look at your new variable using plot()
plot(loan_data$ir_cat)

1.4 Data splitting and confusion matrices

1.4.1 Splitting the data set

To make your training and test sets, you should first set a seed using set.seed(). Seeds allow you to create a starting point for randomly generated numbers, so that each time your code is run the same answer is generated. The advantage of doing this in your sampling is that you or anyone else can recreate the exact same training and test sets by using the same seed.

Using sample(), you can randomly assign observations to the training and test set.

For this exercise you will use the two first arguments in the sample() function: - The first argument is the vector from which we will sample values. We will randomly pick row numbers as indices; you can use 1:nrow(loan_data) to create the vector of row numbers. - The second argument is the number of items to choose. We will enter 2 / 3 * nrow(loan_data), as we construct the training set first.

# Set seed of 567
set.seed(567)

# Store row numbers for training set: index_train
index_train <- sample(1:nrow(loan_data), 2 / 3 * nrow(loan_data))

# Create training set: training_set
training_set <- loan_data[index_train, ]

# Create test set: test_set
test_set <- loan_data[-index_train, ]

1.4.2 Creating a confusion matrix

In this example, assume that you have run a model and stored the predicted outcomes in a vector called model_pred. You want to see how the model performed so you will construct a confusion matrix. You will compare the actual loan status column (loan_status) to the predicted values (model_pred), using the table() function, where the arguments are the true values and the predicted values. Recall the confusion matrix structure:

\(Classification accuracy = \frac{(TP+TN)}{(TP+FP+TN+FN)}\)

\(Sensitivity=\frac{TP}{(TP+FN)}\)

\(Specificity=\frac{TN}{(TN+FP)}\)

model_pred <- sample(c(0, 1), replace = TRUE, size = 9698)
# Create confusion matrix
conf_matrix <- table(test_set$loan_status, model_pred)
conf_matrix
   model_pred
       0    1
  0 4268 4341
  1  535  554
# Compute classification accuracy
(conf_matrix[1, 1] + conf_matrix[2, 2]) / (conf_matrix[1, 1] + conf_matrix[1, 2] + conf_matrix[2, 1] + conf_matrix[2, 2])
[1] 0.4972159
# Compute sensitivity
conf_matrix[2, 2] / (conf_matrix[2, 1] + conf_matrix[2, 2])
[1] 0.5087236

2 Logistic regression

2.1 Logistic regression: introduction

2.1.1 Basic logistic regression

In the video, you looked at a logistic regression model including the variable age as a predictor. Now, you will include a categorical variable, and learn how to interpret its parameter estimates.

When you include a categorical variable in a logistic regression model in R, you will obtain a parameter estimate for all but one of its categories. This category for which no parameter estimate is given is called the reference category. The parameter for each of the other categories represents the odds ratio in favor of a loan default between the category of interest and the reference category.

loan_data <- readRDS("data/loan_data_ch2.rds")
# Set seed of 567
set.seed(567)

# Store row numbers for training set: index_train
index_train <- sample(1:nrow(loan_data), 2 / 3 * nrow(loan_data))

# Create training set: training_set
training_set <- loan_data[index_train, ]

# Create test set: test_set
test_set <- loan_data[-index_train, ]
# Build a glm model with variable ir_cat as a predictor
log_model_cat <- glm(formula = loan_status ~ ir_cat, family = "binomial",
                     data = training_set)

# Print the parameter estimates 
log_model_cat

Call:  glm(formula = loan_status ~ ir_cat, family = "binomial", data = training_set)

Coefficients:
  (Intercept)  ir_cat11-13.5    ir_cat13.5+     ir_cat8-11  ir_catMissing  
      -2.8893         0.9849         1.3805         0.5496         0.7225  

Degrees of Freedom: 19393 Total (i.e. Null);  19389 Residual
Null Deviance:      13440 
Residual Deviance: 13030    AIC: 13040
# Look at the different categories in ir_cat using table()
table(loan_data$ir_cat)

    0-8 11-13.5   13.5+    8-11 Missing 
   7130    6953    6002    6230    2776 

How do you interpret the parameter estimate for the interest rates that are between 8% and 11%? Compared to the reference category with interest rates between 0% and 8%, the odds in favor of default change by a multiple of…

exp(0.5496)
[1] 1.73256

2.1.2 Multiple variables in a logistic regression model

The interpretation of a single parameter still holds when including several variables in a model. When you do include several variables and ask for the interpretation when a certain variable changes, it is assumed that the other variables remain constant, or unchanged. There is a fancy latin phrase for this, ceteris paribus, literally meaning “keeping all others the same”.

To build a logistic regression model with multiple variables, you can use the + sign to add variables. Your formula will look something like:

\(y ~ x1 + ... + xk\)

In order to evaluate the model there are a number of things to be aware of. You already looked at the parameter values, but that is not the only thing of importance. Also important is the statistical significance of a certain parameter estimate. The significance of a parameter is often referred to as a p-value, however in a model output you will see it denoted as \(Pr(>|t|)\). In glm, mild significance is denoted by a “.” to very strong significance denoted by "***". When a parameter is not significant, this means you cannot assure that this parameter is significantly different from 0. Statistical significance is important. In general, it only makes sense to interpret the effect on default for significant parameters.

# Build the logistic regression model
log_model_multi <- glm(loan_status ~ age + ir_cat + grade + loan_amnt + annual_inc, family = "binomial", data = training_set)


# Obtain significance levels using summary()
summary(log_model_multi)

Call:
glm(formula = loan_status ~ age + ir_cat + grade + loan_amnt + 
    annual_inc, family = "binomial", data = training_set)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.1799  -0.5321  -0.4329  -0.3342   3.4786  

Coefficients:
                Estimate Std. Error z value Pr(>|z|)    
(Intercept)   -2.400e+00  1.273e-01 -18.851  < 2e-16 ***
age           -2.505e-03  3.815e-03  -0.657 0.511375    
ir_cat11-13.5  7.048e-01  1.319e-01   5.345 9.06e-08 ***
ir_cat13.5+    7.012e-01  1.466e-01   4.784 1.72e-06 ***
ir_cat8-11     4.624e-01  1.159e-01   3.992 6.57e-05 ***
ir_catMissing  4.587e-01  1.285e-01   3.571 0.000356 ***
gradeB         1.108e-01  1.049e-01   1.056 0.290875    
gradeC         4.542e-01  1.209e-01   3.757 0.000172 ***
gradeD         7.549e-01  1.373e-01   5.497 3.85e-08 ***
gradeE         8.481e-01  1.662e-01   5.103 3.34e-07 ***
gradeF         1.484e+00  2.326e-01   6.381 1.75e-10 ***
gradeG         1.954e+00  3.331e-01   5.866 4.47e-09 ***
loan_amnt     -6.373e-06  4.219e-06  -1.511 0.130904    
annual_inc    -5.890e-06  7.631e-07  -7.719 1.17e-14 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 13443  on 19393  degrees of freedom
Residual deviance: 12854  on 19380  degrees of freedom
AIC: 12882

Number of Fisher Scoring iterations: 5

2.2 Logistic regression: predicting the probability of default

2.2.1 Predicting the probability of default

you can predict the probability for all the test set cases at once using the predict() function.

After having obtained all the predictions for the test set elements, it is useful to get an initial idea of how good the model is at discriminating by looking at the range of predicted probabilities. A small range means that predictions for the test set cases do not lie far apart, and therefore the model might not be very good at discriminating good from bad customers. With low default percentages, you will notice that in general, very low probabilities of default are predicted. It’s time to have a look at a first model.

log_model_small <- glm(formula = loan_status ~ age + ir_cat, family = "binomial", data = training_set)
# Build the logistic regression model
predictions_all_small <- predict(log_model_small, newdata = test_set, type = "response")

# Look at the range of the object "predictions_all_small"
range(predictions_all_small)
[1] 0.03853433 0.18866941

2.2.2 Making more discriminative models

In the previous exercise, the range for predicted probabilities of default was rather small. As discussed, small predicted default probabilities are to be expected with low default rates, but building bigger models (which basically means: including more predictors) can expand the range of your predictions.

Whether this will eventually lead to better predictions still needs to be validated and depends on the quality of the newly included predictors. But first, have a look at how bigger models can expand the range.

# Change the code below to construct a logistic regression model using all available predictors in the data set
log_model_full <- glm(loan_status ~ ., family = "binomial", data = training_set)

# Make PD-predictions for all test set elements using the the full logistic regression model
predictions_all_full <- predict(log_model_full, newdata = test_set, type = "response")

# Look at the predictions range
range(predictions_all_full)
[1] 6.471741e-06 5.436561e-01

2.3 Evaluating the logistic regression model result

2.3.1 Specifying a cut-off

We have shown you how the specification of a cut-off can make the difference to obtain a good confusion matrix. Now, you will learn how to transform the prediction vector to a vector of binary values indicating the status of the loan. The ifelse() function in R can help you here.

Applying the ifelse() function in the context of a cut-off, you would have something like

ifelse(predictions > 0.3, 1, 0) In the first argument, you are testing whether a certain value in the predictions-vector is bigger than 0.3. If this is TRUE, R returns “1” (specified in the second argument), if FALSE, R returns “0” (specified in the third argument), representing “default” and “no default”, respectively.

# The code for the logistic regression model and the predictions is given below
log_model_full <- glm(loan_status ~ ., family = "binomial", data = training_set)
predictions_all_full <- predict(log_model_full, newdata = test_set, type = "response")

# Make a binary predictions-vector using a cut-off of 15%
pred_cutoff_15 <- ifelse(predictions_all_full > 0.15, 1, 0)

# Construct a confusion matrix
table(test_set$loan_status, pred_cutoff_15)
   pred_cutoff_15
       0    1
  0 6726 1878
  1  683  410

2.3.2 Comparing two cut-offs

Have a look again at the confusion matrix using the full model and a cut-off of 15%, which is stored in the object conf_matrix_15, and another confusion matrix using a cut-off of 20% and the same model, stored in conf_matrix_20.

Accuracy increases, sensitivity decreases and specificity increases.

2.4 Wrap-up and remarks

3 Decision trees

3.1 What is a decision tree?

3.1.1 Computing the gain for a tree

We looked at how the Gini-measure is used to create the perfect split for a tree. Now, you will compute the gain for the tree loaded in your workspace.

The data set contains 500 cases, 89 of these cases are defaults. This led to a Gini of 0.292632 in the root node. As a small reminder, remember that Gini of a certain node = 2 * proportion of defaults in this node * proportion of non-defaults in this node. Have a look at the code for a refresher.

gini_root <- 2 * (89 / 500) * (411 / 500)

You will use these Gini measures to help you calculate the gain of the leaf nodes with respect to the root node. Look at the following code to get an idea of how you can use the gini measures you created to calculate the gain of a node.

Gain = gini_root - (prop(cases left leaf) * gini_left) - (prop(cases right leaf * gini_right))

Compute the gini in the left hand and the right hand node, and the gain of the two leaf nodes with respect to the root node. The object containing the tree is small_tree.

# Load package rpart in your workspace.
library(rpart)

# The Gini-measure of the root node is given below
gini_root <- 2 * 89 / 500 * 411 / 500

# Compute the Gini measure for the left leaf node
gini_ll <- 2 * (401/446)*(45/446) 

# Compute the Gini measure for the right leaf node
gini_rl <-2 * (10/54)*(44/54) 

# Compute the gain
gain <- gini_root - 446 / 500 * gini_ll - 54 / 500 * gini_rl
gain
[1] 0.09820084
# compare the gain-column in small_tree$splits with our computed gain, multiplied by 500, and assure they are the same

small_set <- readRDS("small_set.rds")
small_tree <- rpart(formula = status ~ age, data = small_set, method = "class", 
    control = rpart.control(minsplit = 5, cp = 0.001, maxdepth = 1))
small_tree$splits
    count ncat  improve index adj
age   500    1 49.10042  32.5   0
improve <- gain * 500
improve
[1] 49.10042

3.2 Building decision trees using the rpart()-package

3.2.1 Undersampling the training set

In the video, you saw that to overcome the unbalanced data problem, you can use under- or oversampling. The training set has been undersampled for you, such that 1/3 of the training set consists of defaults, and 2/3 of non-defaults. The resulting data set is available in your workspace and named undersampled_training_set, and contains less observations (6570 instead of 19394). In this exercise, you will create a decision tree using the undersampled data set.

You will notice that the trees in this and the next exercises are very big, so big that you cannot really read them anymore.

# Load package rpart in your workspace.
library(rpart)

# Change the code provided in the video such that a decision tree is constructed using the undersampled training set. Include rpart.control to relax the complexity parameter to 0.001.
tree_undersample <- rpart(loan_status ~ ., method = "class",
                          data =  training_set)

Change the code provided such that a decision tree is constructed using the undersampled training set instead of training_set. Additionally, add the argument control = rpart.control(cp = 0.001). cp, which is the complexity parameter, is the threshold value for a decrease in overall lack of fit for any split. If cp is not met, further splits will no longer be pursued. cp’s default value is 0.01, but for complex problems, it is advised to relax cp.

Plot the decision tree using the function plot and the tree object name. Add a second argument uniform = TRUE to get equal-sized branches.

library(ROSE)
undersampled_training_set <- ovun.sample(loan_status ~ ., data = training_set, method = "under", N = 6570, seed = 1)$data
table(undersampled_training_set$loan_status )

   0    1 
4436 2134 
# Change the code provided in the video such that a decision tree is constructed using the undersampled training set. Include rpart.control to relax the complexity parameter to 0.001.
tree_undersample <- rpart(loan_status ~ ., method = "class",
                          data =  undersampled_training_set,  control = rpart.control(cp = 0.001))

# Plot the decision tree
plot(tree_undersample, uniform = TRUE)

# Add labels to the decision tree
text(tree_undersample)

3.2.2 Changing the prior probabilities

As mentioned in the video, you can also change the prior probabilities to obtain a decision tree. This is an indirect way of adjusting the importance of misclassifications for each class. You can specify another argument inside rpart() to include prior probabities. The argument you are looking for has the following form

parms = list(prior=c(non_default_proportion, default_proportion))

# Change the code below such that a tree is constructed with adjusted prior probabilities.
tree_prior <- rpart(loan_status ~ ., method = "class",
                    data = training_set)

Change the code provided such that a decision tree is constructed , including the argument parms and changing the proportion of non-defaults to 0.7, and of defaults to 0.3 (they should always sum up to 1). Additionally, include control = rpart.control(cp = 0.001) as well.

# Change the code below such that a tree is constructed with adjusted prior probabilities.
tree_prior <- rpart(loan_status ~ ., method = "class",
                    data = training_set, parms = list(prior = c(0.7, 0.3)),
                    control = rpart.control(cp = 0.001))

# Plot the decision tree
plot(tree_prior, uniform = TRUE)

# Add labels to the decision tree
text(tree_prior)

3.2.3 Including a loss matrix

Thirdly, you can include a loss matrix, changing the relative importance of misclassifying a default as non-default versus a non-default as a default. You want to stress that misclassifying a default as a non-default should be penalized more heavily. Including a loss matrix can again be done in the argument parms in the loss matrix.

parms = list(loss = matrix(c(0, cost_def_as_nondef, cost_nondef_as_def, 0), ncol=2))

Doing this, you are constructing a 2x2-matrix with zeroes on the diagonal and changed loss penalties off-diagonal. The default loss matrix is all ones off-diagonal.

tree_loss_matrix <- rpart(loan_status ~ ., method = "class",
                          data =  training_set)

Change the code provided such a loss matrix is included, with a penalization that is 10 times bigger when misclassifying an actual default as a non-default. This can be done replacing cost_def_as_nondef by 10, and cost_nondef_as_def by 1. Similar to what you’ve done in the previous exercises, include rpart.control to relax the complexity parameter to 0.001.

# Change the code below such that a decision tree is constructed using a loss matrix penalizing 10 times more heavily for misclassified defaults.
tree_loss_matrix <- rpart(loan_status ~ ., method = "class",
                          data =  training_set,
                          parms = list(loss = matrix(c(0, 10, 1, 0), ncol = 2)),
                          control = rpart.control(cp = 0.001))


# Plot the decision tree
plot(tree_loss_matrix, uniform = TRUE)

# Add labels to the decision tree
text(tree_loss_matrix)

3.3 Pruning the decision tree

3.3.1 Pruning the tree with changed prior probabilities

Pruning a tree is necessary to avoid overfitting. There were some big trees in the previous exercises and now you will put what you have learned into practice, and prune the previously constructed tree with the changed prior probabilities. The rpart package is already loaded in your workspace.

You will first set a seed to make sure the results are reproducible as mentioned in the video, because you will be examining cross-validated error results. Results involve randomness and could differ slightly upon running the function again with a different seed.

In this exercise you will learn to identify which complexity parameter (CP) will minimize the cross-validated error results, then prune your tree based on this value.

tree_undersample tree_prior tree_loss_matrix

Use plotcp() to visualize cross-vaidated error (X-val Relative Error) in relation to the complexity parameter for tree_prior. Use printcp() to print a table of information about CP, splits, and errors. See if you can identify which split has the minimum cross-validated error in tree_prior.

set.seed(345)
# tree_prior is loaded in your workspace

# Plot the cross-validated error rate as a function of the complexity parameter
plotcp(tree_prior)


# Use printcp() to identify for which complexity parameter the cross-validated error rate is minimized.
printcp(tree_prior)

Classification tree:
rpart(formula = loan_status ~ ., data = training_set, method = "class", 
    parms = list(prior = c(0.7, 0.3)), control = rpart.control(cp = 0.001))

Variables actually used in tree construction:
[1] age            annual_inc     emp_cat        grade          home_ownership ir_cat        
[7] loan_amnt     

Root node error: 5818.2/19394 = 0.3

n= 19394 

          CP nsplit rel error  xerror     xstd
1  0.0055301      0   1.00000 1.00000 0.020422
2  0.0047484      5   0.97235 0.99718 0.020347
3  0.0043786      6   0.96760 0.99623 0.020180
4  0.0028281      7   0.96322 0.97751 0.019459
5  0.0026498      9   0.95757 0.97588 0.019344
6  0.0018569     10   0.95492 0.97468 0.019267
7  0.0014322     11   0.95306 0.97759 0.019326
8  0.0013610     20   0.93861 0.98244 0.019346
9  0.0013529     24   0.93317 0.98163 0.019346
10 0.0011536     27   0.92793 0.98505 0.019308
11 0.0010000     28   0.92677 0.98986 0.019247
# Create an index for of the row with the minimum xerror
index <- which.min(tree_prior$cptable[ , "xerror"])

# Create tree_min
tree_min <- tree_prior$cptable[index, "CP"]

#  Prune the tree using tree_min
ptree_prior <- prune(tree_prior, cp = tree_min)

# Use prp() to plot the pruned tree
library(rpart.plot)
prp(ptree_prior) 

3.3.2 Pruning the tree with the loss matrix

In this exercise, you will prune the tree that was built using a loss matrix in order to penalize misclassified defaults more than misclassified non-defaults.

# set a seed and run the code to construct the tree with the loss matrix again
set.seed(345)
tree_loss_matrix  <- rpart(loan_status ~ ., method = "class", data = training_set,
                           parms = list(loss=matrix(c(0, 10, 1, 0), ncol = 2)),
                           control = rpart.control(cp = 0.001))

# Plot the cross-validated error rate as a function of the complexity parameter
plotcp(tree_loss_matrix)

Looking at the cp-plot, you will notice that pruning the tree using the minimum cross-validated error will lead to a tree that is as big as the unpruned tree, as the cross-validated error reaches its minimum for cp = 0.001. Because you would like to make the tree somewhat smaller, try pruning the tree using cp = 0.0012788. For this complexity parameter, the cross-validated error approaches the minimum observed error. Call the pruned tree ptree_loss_matrix.

# Prune the tree using cp = 0.0012788
ptree_loss_matrix <- prune(tree_loss_matrix, cp = 0.0012788)

# Use prp() and argument extra = 1 to plot the pruned tree
prp(ptree_loss_matrix, extra = 1)

# Create an index for of the row with the minimum xerror
index <- which.min(tree_loss_matrix$cptable[ , "xerror"])

# Create tree_min
tree_min <- tree_loss_matrix$cptable[index, "CP"]
# Prune the tree using cp = 0.0012788
ptree_loss_matrix <- prune(tree_loss_matrix, cp = tree_min)

# Use prp() and argument extra = 1 to plot the pruned tree
prp(ptree_loss_matrix, extra = 1)

3.4 Other tree options and the construction of confusion matrices

3.4.1 One final tree using more options

You will use some final arguments that were discussed in the video. Some specifications in the rpart.control()-function will be changed, and some weights will be included using the weights argument in rpart(). The vector case_weights has been constructed for you and is loaded in your workspace. This vector contains weights of 1 for the non-defaults in the training set, and weights of 3 for defaults in the training sets. By specifying higher weights for default, the model will assign higher importance to classifying defaults correctly.

case_weights <- ifelse(training_set$loan_status == 0, 1, 3)
# set a seed and run the code to obtain a tree using weights, minsplit and minbucket
set.seed(345)
tree_weights <- rpart(loan_status ~ ., method = "class",
                      data = training_set,
                      weights = case_weights, 
                      control = rpart.control(minsplit = 5, minbucket = 2, cp = 0.001))

# Plot the cross-validated error rate for a changing cp
plotcp(tree_weights)


# Create an index for of the row with the minimum xerror
index <- which.min(tree_weights$cp[ , "xerror"])

# Create tree_min
tree_min <- tree_weights$cp[index, "CP"]

# Prune the tree using tree_min
ptree_weights <- prune(tree_weights, cp = tree_min)

# Plot the pruned tree using the rpart.plot()-package
prp(ptree_weights, extra = 1)

3.4.2 Confusion matrices and accuracy of our final trees

Over the past few exercises, you have constructed quite a few pruned decision trees, with four in total. As you can see, the eventual number of splits varies quite a bit from one tree to another:

ptree_undersample  # 7 splits
ptree_prior  # 9 splits
ptree_loss_matrix  # 24 splits
ptree_weights  # 6 splits

Now it is important to know which tree performs best in terms of accuracy. In order to get the accuracy, you will start off by making predictions using the test set, and construct the confusion matrix for each of these trees. You will add the argument type = “class” when doing these predictions. By doing this there is no need to set a cut-off. Nevertheless, it is important to be aware of the fact that not only the accuracy is important, but also the sensitivity and specificity. Additionally, predicting probabilities instead of binary values (0 or 1) has the advantage that the cut-off can be moved along. Then again, the difficulty here is the choice of the cut-off.

\(Classification accuracy = \frac{(TP+TN)}{(TP+FP+TN+FN)}\)

ptree_undersample <- tree_undersample
ptree_prior <- tree_prior 
ptree_loss_matrix <- tree_loss_matrix
ptree_weights <- tree_weights
# Make predictions for each of the pruned trees using the test set.
pred_undersample <- predict(ptree_undersample, newdata = test_set,  type = "class")
pred_prior <- predict(ptree_prior, newdata = test_set,  type = "class")
pred_loss_matrix <- predict(ptree_loss_matrix, newdata = test_set,  type = "class")
pred_weights <- predict(ptree_weights, newdata = test_set,  type = "class")

# construct confusion matrices using the predictions.
confmat_undersample <- table(test_set$loan_status, pred_undersample)
confmat_prior <- table(test_set$loan_status, pred_prior)
confmat_loss_matrix <- table(test_set$loan_status, pred_loss_matrix)
confmat_weights <- table(test_set$loan_status, pred_weights)

# Compute the accuracies
acc_undersample <- sum(diag(confmat_undersample)) / nrow(test_set)
acc_undersample
[1] 0.8210787
acc_prior <- sum(diag(confmat_prior)) / nrow(test_set)
acc_prior
[1] 0.848613
acc_loss_matrix <- sum(diag(confmat_loss_matrix)) / nrow(test_set)
acc_loss_matrix
[1] 0.5271734
acc_weights <- sum(diag(confmat_weights)) / nrow(test_set)
acc_weights
[1] 0.8627411

4 Evaluating a credit risk model

4.1 Finding the right cut-off: the strategy curve

In the video, you learned how to compute the bad rate (or, the percentage of defaults) in the loan portfolio of a bank when given:

  • a specific model
  • the acceptance rate

In this exercise, you will compute the bad rate that a bank can expect when using the pruned tree ptree_prior that you fitted before, and an acceptance rate of 80%.

n the script, you are provided the code to make predictions for the probability of default using the pruned tree and test_set. Remember that if you use the predict() function for a tree, the probability of default can be found in the second column. Therefore [,2] was pasted to the predict() function.

Obtain the cut-off that leads to an acceptance rate of 80%, using prob_default_prior. You can use the quantile()- function to do this, setting the second argument to 0.8. Assign the name cutoff_prior. The code to obtain the actual binary default predictions (0 or 1) is provided. ifelse() here. Name the object bin_pred_prior_80.

The code to select the default indicators of test_set for the accepted loans acording to a 80% acceptance rate is provided.

Compute the percentage of defaults (or the “bad rate”) for the accepted loans. This is the number of occurences of 1 in accepted_status_prior_80, divided by the total number of instances in this vector. Print the solution to your R-console.

# Make predictions for the probability of default using the pruned tree and the test set.
prob_default_prior <- predict(ptree_prior, newdata = test_set)[ ,2]

# Obtain the cutoff for acceptance rate 80%
cutoff_prior <- quantile(prob_default_prior, 0.8)  

# Obtain the binary predictions.
bin_pred_prior_80 <- ifelse(prob_default_prior > cutoff_prior, 1, 0)

# Obtain the actual default status for the accepted loans
accepted_status_prior_80 <- test_set$loan_status[bin_pred_prior_80 == 0]

# Obtain the bad rate for the accepted loans
sum(accepted_status_prior_80) / length(accepted_status_prior_80)
[1] 0.102678

4.1.1 The strategy table and strategy curve

Repeating the calculations you did in the previous exercise for several acceptance rates, you can obtain a strategy table. This table can be a useful tool for banks, as they can give them a better insight to define an acceptance strategy.

You know how to compute a bad rate for a certain acceptance rate by now, so the function strategy_bank was written and loaded into your workspace to speed things up. This function computes the cut-off and bad rate for the acceptance rates that are multiples of 5% (0%, 5%, 10%, …).

strategy_bank <- function(prob_of_def){
    cutoff = rep(NA, 21)
    bad_rate = rep(NA, 21)
    accept_rate = seq(1, 0, by = -0.05)
    for (i in 1:21) {
        cutoff[i] = quantile(prob_of_def, accept_rate[i])
        pred_i = ifelse(prob_of_def > cutoff[i], 1, 0)
        pred_as_good = test_set$loan_status[pred_i == 0]
        bad_rate[i] = sum(pred_as_good)/length(pred_as_good)
    }
        table = cbind(accept_rate, cutoff = round(cutoff, 4), bad_rate = round(bad_rate, 
        4))
    return(list(table = table, bad_rate = bad_rate, accept_rate = accept_rate, 
        cutoff = cutoff))
}
  • Have a look at the function strategy_bank.
  • The vector predictions_cloglog contains predicted probabilities of default using the cloglog model you used in chapter 2, the vector predictions_loss_matrixcontains the predicted probabilities of default using the pruned tree including a loss matrix (previously constructed in chapter 3). Apply function strategy_bank to each of the prediction-vectors, assign the name strategy_cloglog and strategy_loss_matrix respectively.
  • The strategy tables can be obtained using the object names in combination with $table.
  • The strategy curves have been plotted for you. The strategy curve of the tree model shows pretty strange behavior. Because of the structure of classification trees, you might have a bigger chance for weird “jumps” here. Additionally, the tree with a loss matrix was a very big one, so this might be the result of overfitting!
#withougth type = "class" to get probabilities
predictions_loss_matrix <- predict(ptree_loss_matrix, newdata = test_set)[,2]
# Apply the function strategy_bank to both predictions_cloglog and predictions_loss_matrix
strategy_cloglog <- strategy_bank(predictions_cloglog)
strategy_loss_matrix <- strategy_bank(predictions_loss_matrix)

# Obtain the strategy tables for both prediction-vectors
strategy_cloglog$table
      accept_rate cutoff bad_rate
 [1,]        1.00 0.3404   0.1127
 [2,]        0.95 0.1939   0.1090
 [3,]        0.90 0.1832   0.1047
 [4,]        0.85 0.1702   0.1003
 [5,]        0.80 0.1477   0.0973
 [6,]        0.75 0.1388   0.0936
 [7,]        0.70 0.1329   0.0894
 [8,]        0.65 0.1267   0.0855
 [9,]        0.60 0.1185   0.0823
[10,]        0.55 0.1093   0.0802
[11,]        0.50 0.1012   0.0773
[12,]        0.45 0.0949   0.0721
[13,]        0.40 0.0913   0.0686
[14,]        0.35 0.0875   0.0632
[15,]        0.30 0.0831   0.0622
[16,]        0.25 0.0730   0.0519
[17,]        0.20 0.0552   0.0499
[18,]        0.15 0.0531   0.0437
[19,]        0.10 0.0508   0.0489
[20,]        0.05 0.0481   0.0468
[21,]        0.00 0.0327   0.0000
strategy_loss_matrix$table
      accept_rate cutoff bad_rate
 [1,]        1.00 0.3913   0.1127
 [2,]        0.95 0.1911   0.1110
 [3,]        0.90 0.1911   0.1110
 [4,]        0.85 0.1911   0.1110
 [5,]        0.80 0.1911   0.1110
 [6,]        0.75 0.1818   0.0942
 [7,]        0.70 0.1545   0.0899
 [8,]        0.65 0.1503   0.0901
 [9,]        0.60 0.1299   0.0872
[10,]        0.55 0.1299   0.0872
[11,]        0.50 0.1263   0.0759
[12,]        0.45 0.0635   0.0729
[13,]        0.40 0.0635   0.0729
[14,]        0.35 0.0630   0.0690
[15,]        0.30 0.0630   0.0690
[16,]        0.25 0.0486   0.0570
[17,]        0.20 0.0471   0.0589
[18,]        0.15 0.0325   0.0471
[19,]        0.10 0.0298   0.0445
[20,]        0.05 0.0298   0.0445
[21,]        0.00 0.0000   0.1039
# Plot the strategy functions
par(mfrow = c(1,2))
plot(strategy_cloglog$accept_rate, strategy_cloglog$bad_rate, 
     type = "l", xlab = "Acceptance rate", ylab = "Bad rate", 
     lwd = 2, main = "logistic regression")

plot(strategy_loss_matrix$accept_rate, strategy_loss_matrix$bad_rate, 
     type = "l", xlab = "Acceptance rate", 
     ylab = "Bad rate", lwd = 2, main = "tree")

4.2 The ROC-curve

4.2.1 ROC-curves for comparison of logistic regression models

ROC-curves can easily be created using the pROC-package in R. Let’s have a look if there is a big difference between ROC-curves for the four logistic regression-models previously used throughout this course. A small heads up:

  • predictions_logit contains probability of default (PD) predictions using the default logit link and containing variables age, emp_cat, ir_cat and loan_amnt.
  • predictions_probit contains PD-predictions using the probit and containing variables age, emp_cat, ir_cat and loan_amnt.
  • predictions_cloglog contains PD-predictions using the cloglog link and containing variables age, emp_cat, ir_cat and loan_amnt.
  • predictions_all_full contains PD-predictions using the default logit link and containing all seven variables in the data set.

Load the pROC-package in your R-console. Construct the ROC-objects for the four logistic regression models using function roc(response, predictor). Remember that the response is the loan status indicator in the test_set, which can be obtained through test_set$loan_status. Use the previously created objects to construct ROC-curves. To draw them all on one plot, use plot() for the first ROC-curve drawn (for ROC_logit), and use lines() for the other three models to the same plot. Use the col-argument to change the color of the curve of ROC_probit to “blue”, ROC_cloglog to “red” and ROC_all_full to “green”. Note that, in contrast with what has been discussed in the video, the x-axis label is Specificity and not “1-Specificity”, resulting in an axis that goes from 1 on the left-hand side to 0 on the right-hand side. It seems that the link function does not have a big impact on the ROC here, and the main trigger of a better ROC is the inclusion of more variables in a model. To get an exact idea of the performance of the ROC-curves, have a look at the AUC’s, using function auc().

# Load the pROC-package
library(pROC)

# Construct the objects containing ROC-information
ROC_logit <- roc(test_set$loan_status, predictions_logit)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
ROC_probit <- roc(test_set$loan_status, predictions_probit)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
ROC_cloglog <- roc(test_set$loan_status, predictions_cloglog)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
ROC_all_full <- roc(test_set$loan_status, predictions_all_full)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
# Draw all ROCs on one plot
plot(ROC_logit)
lines(ROC_probit, col="blue")
lines(ROC_cloglog, col="red")
lines(ROC_all_full, col="green")


# Compute the AUCs
auc(ROC_logit)
Area under the curve: 0.6263
auc(ROC_probit)
Area under the curve: 0.6263
auc(ROC_cloglog)
Area under the curve: 0.6263
auc(ROC_all_full)
Area under the curve: 0.6481

4.2.2 ROC-curves for comparison of tree-based models

It’s time for you to repeat the previous exercises, now comparing the tree-based models. The pROC() is now loaded in your workspace. The PD-predictions for tree-based methods are stored in the objects

predictions_undersample predictions_prior predictions_loss_matrix predictions_weights

predictions_undersample <- predict(tree_undersample, newdata = test_set)[,2]
predictions_prior <- predict(tree_prior, newdata = test_set)[,2]
predictions_loss_matrix <- predict(ptree_loss_matrix, newdata = test_set)[,2]
predictions_weights <- predict(tree_weights , newdata = test_set)[,2]
# Construct the objects containing ROC-information
ROC_undersample <- roc(test_set$loan_status, predictions_undersample)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
ROC_prior <- roc(test_set$loan_status, predictions_prior)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
ROC_loss_matrix <- roc(test_set$loan_status, predictions_loss_matrix)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
ROC_weights <- roc(test_set$loan_status, predictions_weights)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
# Draw the ROC-curves in one plot
plot(ROC_undersample)
lines(ROC_prior, col="blue")
lines(ROC_loss_matrix, col="red")
lines(ROC_weights, col="green")


# Compute the AUCs
auc(ROC_undersample)
Area under the curve: 0.6016
auc(ROC_prior)
Area under the curve: 0.5821
auc(ROC_loss_matrix)
Area under the curve: 0.617
auc(ROC_weights)
Area under the curve: 0.5858

4.3 Input selection based on the AUC

4.3.1 Another round of pruning based on AUC

In the video you saw how the “full” logistic regression model with a logit link was being pruned based on the AUC. You saw how the variable home_ownership was deleted from the model, as it improved the overall AUC. After repeating this process for two additional rounds, the variables age and ir_cat were deleted, leading to the model:

log_3_remove_ir <- glm(loan_status ~ loan_amnt + grade + annual_inc + emp_cat, family = binomial, data = training_set)

with an AUC of 0.6545. Now, it’s your turn to see whether the AUC can still be improved by deleting another variable from the model.

# Build four models each time deleting one variable in log_3_remove_ir
log_4_remove_amnt <- glm(loan_status ~ grade + annual_inc + emp_cat, 
                         family = binomial, data = training_set) 
log_4_remove_grade <- glm(loan_status ~ loan_amnt  + annual_inc + emp_cat, 
                          family = binomial, data = training_set)
log_4_remove_inc <- glm(loan_status ~ loan_amnt + grade  + emp_cat , 
                        family = binomial, data = training_set)
log_4_remove_emp <- glm(loan_status ~ loan_amnt + grade + annual_inc, 
                        family = binomial, data = training_set)

# Make PD-predictions for each of the models
pred_4_remove_amnt <- predict(log_4_remove_amnt, newdata = test_set, type = "response")
pred_4_remove_grade <- predict(log_4_remove_grade, newdata = test_set, type = "response")
pred_4_remove_inc <- predict(log_4_remove_inc, newdata = test_set, type = "response")
pred_4_remove_emp <- predict(log_4_remove_emp, newdata = test_set, type = "response")

# Compute the AUCs
auc(test_set$loan_status, pred_4_remove_amnt)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
Area under the curve: 0.6484
auc(test_set$loan_status, pred_4_remove_grade)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
Area under the curve: 0.582
auc(test_set$loan_status, pred_4_remove_inc)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
Area under the curve: 0.6366
auc(test_set$loan_status, pred_4_remove_emp)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
Area under the curve: 0.641

4.3.2 Further model reduction?

Deleting the variable loan_amnt, the AUC can be further improved to 0.6548! The resulting model is

log_4_remove_amnt <- glm(loan_status ~ grade + annual_inc + emp_cat, family = binomial, data = training_set) 

Is it possible to reduce the logistic regression model to only two variable without reducing the AUC? In this exercise you will see if it is possible!

# Build three models each time deleting one variable in log_4_remove_amnt
log_5_remove_grade <- glm(loan_status ~ annual_inc + emp_cat, family = binomial, data = training_set)  
log_5_remove_inc <- glm(loan_status ~ grade  + emp_cat , family = binomial, data = training_set)
log_5_remove_emp <- glm(loan_status ~ grade + annual_inc, family = binomial, data = training_set)

# Make PD-predictions for each of the models
pred_5_remove_grade <- predict(log_5_remove_grade, newdata = test_set, type = "response")
pred_5_remove_inc <- predict(log_5_remove_inc, newdata = test_set, type = "response")
pred_5_remove_emp <- predict(log_5_remove_emp, newdata = test_set, type = "response")

# Compute the AUCs
auc(test_set$loan_status, pred_5_remove_grade)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
Area under the curve: 0.5766
auc(test_set$loan_status, pred_5_remove_inc)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
Area under the curve: 0.6345
auc(test_set$loan_status, pred_5_remove_emp)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
Area under the curve: 0.6419
# Plot the ROC-curve for the best model here
plot(roc(test_set$loan_status,pred_4_remove_amnt))
Setting levels: control = 0, case = 1
Setting direction: controls < cases

LS0tDQp0aXRsZTogIkNyZWRpdCBSaXNrIE1vZGVsaW5nIGluIFIiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdG9jX2NvbGxhcHNlZDogZmFsc2UNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICANCnRvY19kZXB0aDogMw0KLS0tDQojIEludHJvZHVjdGlvbiBhbmQgZGF0YSBwcmVwcm9jZXNzaW5nDQoNCiMjIEludHJvZHVjdGlvbiBhbmQgZGF0YSBzdHJ1Y3R1cmUNCg0KIyMjIEV4cGxvcmluZyB0aGUgY3JlZGl0IGRhdGENCg0KV2Ugd2lsbCBiZSBleGFtaW5pbmcgdGhlIGRhdGFzZXQgbG9hbl9kYXRhOg0KYGBge3J9DQpsb2FuX2RhdGEgPC0gcmVhZFJEUygiZGF0YS9sb2FuX2RhdGFfY2gxLnJkcyIpDQpgYGANCkFmdGVyIGJlaW5nIGdpdmVuIGxvYW5fZGF0YSwgeW91IGFyZSBwYXJ0aWN1bGFybHkgaW50ZXJlc3RlZCBhYm91dCB0aGUgZGVmYXVsdGVkIGxvYW5zIGluIHRoZSBkYXRhIHNldC4gWW91IHdhbnQgdG8gZ2V0IGFuIGlkZWEgb2YgdGhlIG51bWJlciwgYW5kIHBlcmNlbnRhZ2Ugb2YgZGVmYXVsdHMuIERlZmF1bHRzIGFyZSByYXJlLCBzbyB5b3UgYWx3YXlzIHdhbnQgdG8gY2hlY2sgd2hhdCB0aGUgcHJvcG9ydGlvbiBvZiBkZWZhdWx0cyBpcyBpbiBhIGxvYW4gZGF0YXNldC4gVGhlIENyb3NzVGFibGUoKSBmdW5jdGlvbiBpcyB2ZXJ5IHVzZWZ1bCBoZXJlLg0KDQpSZW1lbWJlciB0aGF0IGRlZmF1bHQgaW5mb3JtYXRpb24gaXMgc3RvcmVkIGluIHRoZSByZXNwb25zZSB2YXJpYWJsZSBsb2FuX3N0YXR1cywgd2hlcmUgMSByZXByZXNlbnRzIGEgZGVmYXVsdCwgYW5kIDAgcmVwcmVzZW50cyBub24tZGVmYXVsdC4NCg0KVG8gbGVhcm4gbW9yZSBhYm91dCB2YXJpYWJsZSBzdHJ1Y3R1cmVzIGFuZCBzcG90IHVuZXhwZWN0ZWQgdGVuZGVuY2llcyBpbiB0aGUgZGF0YSwgeW91IHNob3VsZCBleGFtaW5lIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBsb2FuX3N0YXR1cyBhbmQgY2VydGFpbiBmYWN0b3IgdmFyaWFibGVzLiBGb3IgZXhhbXBsZSwgeW91IHdvdWxkIGV4cGVjdCB0aGF0IHRoZSBwcm9wb3J0aW9uIG9mIGRlZmF1bHRzIGluIHRoZSBncm91cCBvZiBjdXN0b21lcnMgd2l0aCBncmFkZSBHICh3b3JzdCBjcmVkaXQgcmF0aW5nIHNjb3JlKSBpcyBzdWJzdGFudGlhbGx5IGhpZ2hlciB0aGFuIHRoZSBwcm9wb3J0aW9uIG9mIGRlZmF1bHRzIGluIHRoZSBncmFkZSBBIGdyb3VwIChiZXN0IGNyZWRpdCByYXRpbmcgc2NvcmUpLg0KDQpDb252ZW5pZW50bHksIENyb3NzVGFibGUoKSBjYW4gYWxzbyBiZSBhcHBsaWVkIG9uIHR3byBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuDQpgYGB7cn0NCiMgVmlldyB0aGUgc3RydWN0dXJlIG9mIGxvYW5fZGF0YQ0Kc3RyKGxvYW5fZGF0YSkNCg0KIyBMb2FkIHRoZSBnbW9kZWxzIHBhY2thZ2UgDQpsaWJyYXJ5KGdtb2RlbHMpDQoNCiMgQ2FsbCBDcm9zc1RhYmxlKCkgb24gbG9hbl9zdGF0dXMNCkNyb3NzVGFibGUobG9hbl9kYXRhJGxvYW5fc3RhdHVzKQ0KYGBgDQpDYWxsIENyb3NzVGFibGUoKSB3aXRoIHggYXJndW1lbnQgbG9hbl9kYXRhXCRncmFkZSBhbmQgeSBhcmd1bWVudCBsb2FuX2RhdGFcJGxvYW5fc3RhdHVzLiBXZSBvbmx5IHdhbnQgcm93LXdpc2UgcHJvcG9ydGlvbnMsIHNvIHNldCBwcm9wLnIgdG8gVFJVRSwgYnV0IHByb3AuYyAsIHByb3AudCBhbmQgcHJvcC5jaGlzcSB0byBGQUxTRSAoZGVmYXVsdCB2YWx1ZXMgaGVyZSBhcmUgVFJVRSwgYW5kIHRoaXMgd291bGQgbGVhZCB0byBpbmNsdXNpb24gb2YgY29sdW1uIHByb3BvcnRpb25zLCB0YWJsZSBwcm9wb3J0aW9ucyBhbmQgY2hpLXNxdWFyZSBjb250cmlidXRpb25zIGZvciBlYWNoIGNlbGwuIFdlIGRvIG5vdCBuZWVkIHRoZXNlIGhlcmUuKQ0KYGBge3J9DQojIENhbGwgQ3Jvc3NUYWJsZSgpIG9uIGdyYWRlIGFuZCBsb2FuX3N0YXR1cw0KQ3Jvc3NUYWJsZShsb2FuX2RhdGEkZ3JhZGUsIGxvYW5fZGF0YSRsb2FuX3N0YXR1cywgcHJvcC5yID0gVFJVRSwgcHJvcC5jID0gRkFMU0UsIHByb3AudCAgPSBGQUxTRSwgcHJvcC5jaGlzcSA9IEZBTFNFKQ0KYGBgDQpUaGUgcHJvcG9ydGlvbiBvZiBkZWZhdWx0cyBpbmNyZWFzZXMgd2hlbiB0aGUgY3JlZGl0IHJhdGluZyBtb3ZlcyBmcm9tIEEgdG8gRyAod29yc2UgY3JlZGl0IHNjb3JlcykuDQoNCiMjIEhpc3RvZ3JhbXMgYW5kIG91dGxpZXJzDQoNCiMjIyBIaXN0b2dyYW1zDQoNCllvdSBwcmV2aW91c2x5IGV4cGxvcmVkIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB1c2luZyB0aGUgQ3Jvc3NUYWJsZSgpIGZ1bmN0aW9uLiBOb3cgeW91IHdvdWxkIGxpa2UgdG8gZXhwbG9yZSBjb250aW51b3VzIHZhcmlhYmxlcyB0byBpZGVudGlmeSBwb3RlbnRpYWwgb3V0bGllcnMgb3IgdW5leHBlY3RlZCBkYXRhIHN0cnVjdHVyZXMuDQoNClRvIGRvIHRoaXMsIGxldCdzIGV4cGVyaW1lbnQgd2l0aCB0aGUgZnVuY3Rpb24gaGlzdCgpIHRvIHVuZGVyc3RhbmQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbnVtYmVyIG9mIGxvYW5zIGZvciBkaWZmZXJlbnQgY3VzdG9tZXJzLg0KYGBge3J9DQojIENyZWF0ZSBoaXN0b2dyYW0gb2YgbG9hbl9hbW50OiBoaXN0XzENCmhpc3RfMSA8LSBoaXN0KGxvYW5fZGF0YSRsb2FuX2FtbnQpDQpgYGANClVzZSAkYnJlYWtzIGFsb25nIHdpdGggdGhlIG9iamVjdCBoaXN0XzEgdG8gZ2V0IG1vcmUgaW5mb3JtYXRpb24gb24gdGhlIGhpc3RvZ3JhbSBicmVha3MuIEtub3dpbmcgdGhlIGxvY2F0aW9uIG9mIHRoZSBicmVha3MgaXMgaW1wb3J0YW50IGJlY2F1c2UgaWYgdGhleSBhcmUgcG9vcmx5IGNob3NlbiwgdGhlIGhpc3RvZ3JhbSBtYXkgYmUgbWlzbGVhZGluZy4NCmBgYHtyfQ0KIyBQcmludCBsb2NhdGlvbnMgb2YgdGhlIGJyZWFrcyBpbiBoaXN0XzENCmhpc3RfMSRicmVha3MNCmBgYA0KQ2hhbmdlIHRoZSBudW1iZXIgb2YgYnJlYWtzIGluIGhpc3RfMSB0byAyMDAgYnkgc3BlY2lmeWluZyB0aGUgYnJlYWtzIGFyZ3VtZW50LiBBZGRpdGlvbmFsbHksIG5hbWUgdGhlIHgtYXhpcyAiTG9hbiBhbW91bnQiIHVzaW5nIHRoZSB4bGFiIGFyZ3VtZW50IGFuZCB0aXRsZSBpdCAiSGlzdG9ncmFtIG9mIHRoZSBsb2FuIGFtb3VudCIgdXNpbmcgdGhlIG1haW4gYXJndW1lbnQuIFNhdmUgdGhlIHJlc3VsdCB0byBoaXN0XzIuDQpgYGB7cn0NCiMgQ2hhbmdlIG51bWJlciBvZiBicmVha3MgYW5kIGFkZCBsYWJlbHM6IGhpc3RfMg0KaGlzdF8yIDwtIGhpc3QobG9hbl9kYXRhJGxvYW5fYW1udCwgYnJlYWtzID0gMjAwLCB4bGFiID0gIkxvYW4gYW1vdW50IiwgDQogICAgICAgICAgICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiB0aGUgbG9hbiBhbW91bnQiKQ0KYGBgDQpXaHkgZG8gdGhlIHBlYWtzIG9jY3VyIHdoZXJlIHRoZXkgb2NjdXI/DQoNCk5vdGUgdGhhdCB0aGVyZSBhcmUgc29tZSBoaWdoIHBlYWtzIGF0IHJvdW5kIHZhbHVlczogNTAwMCwgMTAwMDAsIDE1MDAwLOKApiBQZW9wbGUgdGVuZCB0byBib3Jyb3cgcm91bmQgbnVtYmVycy4gDQoNCiMjIyBPdXRsaWVycw0KDQpOb3cgaXQncyB0aW1lIHRvIGxvb2sgYXQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgdmFyaWFibGUgYWdlLg0KYGBge3J9DQpoaXN0KGxvYW5fZGF0YSRhZ2UpDQpgYGANClRoZXJlIGlzIGEgbG90IG9mIGJsYW5rIHNwYWNlIG9uIHRoZSByaWdodC1oYW5kIHNpZGUgb2YgdGhlIHBsb3QuIFRoaXMgaXMgYW4gaW5kaWNhdGlvbiBvZiBwb3NzaWJsZSBvdXRsaWVycy4gWW91IHdpbGwgbG9vayBhdCBhIHNjYXR0ZXJwbG90IHRvIHZlcmlmeSB0aGlzLiBJZiB5b3UgZmluZCBhbnkgb3V0bGllcnMgeW91IHdpbGwgZGVsZXRlIHRoZW0uDQoNCklmIG91dGxpZXJzIGFyZSBvYnNlcnZlZCBmb3Igc2V2ZXJhbCB2YXJpYWJsZXMsIGl0IG1pZ2h0IGJlIHVzZWZ1bCB0byBsb29rIGF0IGJpdmFyaWF0ZSBwbG90cy4gSXQncyBwb3NzaWJsZSB0aGUgb3V0bGllcnMgYmVsb25nIHRvIHRoZSBzYW1lIG9ic2VydmF0aW9uLiBJZiBzbywgdGhlcmUgaXMgZXZlbiBtb3JlIHJlYXNvbiB0byBkZWxldGUgdGhlIG9ic2VydmF0aW9uIGJlY2F1c2UgaXQgaXMgbW9yZSBsaWtlbHkgdGhhdCBzb21lIGluZm9ybWF0aW9uIHN0b3JlZCBpbiBpdCBpcyB3cm9uZy4NCmBgYHtyfQ0KIyBQbG90IHRoZSBhZ2UgdmFyaWFibGUNCnBsb3QobG9hbl9kYXRhJGFnZSwgeWxhYiA9ICJBZ2UiKQ0KYGBgDQpUaGUgb2xkZXN0IHBlcnNvbiBpbiB0aGlzIGRhdGEgc2V0IGlzIG9sZGVyIHRoYW4gMTIyIHllYXJzISBHZXQgdGhlIGluZGV4IG9mIHRoaXMgb3V0bGllciB1c2luZyB3aGljaCgpIGFuZCB0aGUgYWdlIG9mIDEyMiBhcyBhIGN1dG9mZiAoeW91IGNhbiBkbyB0aGlzIHVzaW5nIGxvYW5fZGF0YSRhZ2UgPiAxMjIpLiBBc3NpZ24gaXQgdG8gdGhlIG9iamVjdCBpbmRleF9oaWdoYWdlLg0KYGBge3J9DQojIFNhdmUgdGhlIG91dGxpZXIncyBpbmRleCB0byBpbmRleF9oaWdoYWdlDQppbmRleF9oaWdoYWdlIDwtIHdoaWNoKGxvYW5fZGF0YSRhZ2UgPiAxMjIpDQpgYGANCkNyZWF0ZSBhIG5ldyBkYXRhIHNldCBuZXdfZGF0YSwgYWZ0ZXIgcmVtb3ZpbmcgdGhlIG9ic2VydmF0aW9uIHdpdGggdGhlIGhpZ2ggYWdlIHVzaW5nIHRoZSBvYmplY3QgaW5kZXhfaGlnaGFnZS4NCmBgYHtyfQ0KIyBDcmVhdGUgZGF0YSBzZXQgbmV3X2RhdGEgd2l0aCBvdXRsaWVyIGRlbGV0ZWQNCm5ld19kYXRhIDwtIGxvYW5fZGF0YVstaW5kZXhfaGlnaGFnZSwgXQ0KYGBgDQoNCkhhdmUgYSBsb29rIGF0IHRoZSBiaXZhcmlhdGUgc2NhdHRlcnBsb3QsIHdpdGggYWdlIG9uIHRoZSB4LWF4aXMgYW5kIGFubnVhbCBpbmNvbWUgb24gdGhlIHktYXhpcy4gDQpgYGB7cn0NCiMgTWFrZSBiaXZhcmlhdGUgc2NhdHRlcnBsb3Qgb2YgYWdlIGFuZCBhbm51YWwgaW5jb21lDQpwbG90KGxvYW5fZGF0YSRhZ2UsIGxvYW5fZGF0YSRhbm51YWxfaW5jLCB4bGFiID0gIkFnZSIsIHlsYWIgPSAiQW5udWFsIEluY29tZSIpDQpgYGANCkRpZCB5b3Ugbm90aWNlIHRoYXQgeW91IGhhdmUgYSBiaXZhcmlhdGUgb3V0bGllcj8gVGhlIHBlcnNvbiB3aXRoIHRoZSBodWdlIGFubnVhbCB3YWdlIG9mICQ2IG1pbGxpb24gYXBwZWFyZWQgdG8gYmUgMTQ0IHllYXJzIG9sZC4gVGhpcyBtdXN0IGJlIGEgbWlzdGFrZSwgc28geW91IHdpbGwgZGVmaW5pdGVseSBkZWxldGUgdGhpcyBvYnNlcnZhdGlvbiBmcm9tIHRoZSBkYXRhIGluIHRoZSBmb2xsb3dpbmcgZXhlcmNpc2VzLg0KDQojIyBNaXNzaW5nIGRhdGEgYW5kIGNvYXJzZSBjbGFzc2lmaWNhdGlvbg0KDQojIyMgRGVsZXRpbmcgbWlzc2luZyBkYXRhDQoNCllvdSBzYXcgYmVmb3JlIHRoYXQgdGhlIGludGVyZXN0IHJhdGUgKGludF9yYXRlKSBpbiB0aGUgZGF0YSBzZXQgbG9hbl9kYXRhIGRlcGVuZHMgb24gdGhlIGN1c3RvbWVyLiBVbmZvcnR1bmF0ZWx5IHNvbWUgb2JzZXJ2YXRpb25zIGFyZSBtaXNzaW5nIGludGVyZXN0IHJhdGVzLiBZb3Ugbm93IG5lZWQgdG8gaWRlbnRpZnkgaG93IG1hbnkgaW50ZXJlc3QgcmF0ZXMgYXJlIG1pc3NpbmcgYW5kIHRoZW4gZGVsZXRlIHRoZW0uDQpgYGB7cn0NCiMgTG9vayBhdCBzdW1tYXJ5IG9mIGxvYW5fZGF0YQ0Kc3VtbWFyeShsb2FuX2RhdGEkaW50X3JhdGUpDQoNCiMgR2V0IGluZGljZXMgb2YgbWlzc2luZyBpbnRlcmVzdCByYXRlczogbmFfaW5kZXgNCm5hX2luZGV4IDwtIHdoaWNoKGlzLm5hKGxvYW5fZGF0YSRpbnRfcmF0ZSkpDQoNCiMgUmVtb3ZlIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcgaW50ZXJlc3QgcmF0ZXM6IGxvYW5fZGF0YV9kZWxyb3dfbmENCmxvYW5fZGF0YV9kZWxyb3dfbmEgPC0gbG9hbl9kYXRhWy1uYV9pbmRleCwgXQ0KDQojIE1ha2UgY29weSBvZiBsb2FuX2RhdGENCmxvYW5fZGF0YV9kZWxjb2xfbmEgPC0gbG9hbl9kYXRhDQoNCiMgRGVsZXRlIGludGVyZXN0IHJhdGUgY29sdW1uIGZyb20gbG9hbl9kYXRhX2RlbGNvbF9uYQ0KbG9hbl9kYXRhX2RlbGNvbF9uYSRpbnRfcmF0ZSA8LSBOVUxMDQpgYGANCiMjIyBSZXBsYWNpbmcgbWlzc2luZyBkYXRhDQoNClJhdGhlciB0aGFuIGRlbGV0aW5nIHRoZSBtaXNzaW5nIGludGVyZXN0IHJhdGVzLCB5b3UgbWF5IHdhbnQgdG8gcmVwbGFjZSB0aGVtIGluc3RlYWQuIA0KDQpDcmVhdGUgYW4gb2JqZWN0IGNhbGxlZCBtZWRpYW5faXIsIGNvbnRhaW5pbmcgdGhlIG1lZGlhbiBvZiB0aGUgaW50ZXJlc3QgcmF0ZXMgaW4gbG9hbl9kYXRhIHVzaW5nIHRoZSBtZWRpYW4oKSBmdW5jdGlvbi4gRG9uJ3QgZm9yZ2V0IHRvIGluY2x1ZGUgdGhlIGFyZ3VtZW50IG5hLnJtID0gVFJVRS4NCmBgYHtyfQ0KIyBDb21wdXRlIHRoZSBtZWRpYW4gb2YgaW50X3JhdGUNCm1lZGlhbl9pciA8LSBtZWRpYW4obG9hbl9kYXRhJGludF9yYXRlLCBuYS5ybSA9IFRSVUUpDQoNCiMgTWFrZSBjb3B5IG9mIGxvYW5fZGF0YQ0KbG9hbl9kYXRhX3JlcGxhY2UgPC0gbG9hbl9kYXRhDQoNCiMgUmVwbGFjZSBtaXNzaW5nIGludGVyZXN0IHJhdGVzIHdpdGggbWVkaWFuDQpsb2FuX2RhdGFfcmVwbGFjZSRpbnRfcmF0ZVtuYV9pbmRleF0gPC0gbWVkaWFuX2lyDQoNCiMgQ2hlY2sgaWYgdGhlIE5BcyBhcmUgZ29uZQ0Kc3VtbWFyeShsb2FuX2RhdGFfcmVwbGFjZSRpbnRfcmF0ZSkNCmBgYA0KIyMjIEtlZXBpbmcgbWlzc2luZyBkYXRhDQoNCkluIHNvbWUgc2l0dWF0aW9ucywgdGhlIGZhY3QgdGhhdCBhbiBpbnB1dCBpcyBtaXNzaW5nIGlzIGltcG9ydGFudCBpbmZvcm1hdGlvbiBpbiBpdHNlbGYuIE5BcyBjYW4gYmUga2VwdCBpbiBhIHNlcGFyYXRlICJtaXNzaW5nIiBjYXRlZ29yeSB1c2luZyBjb2Fyc2UgY2xhc3NpZmljYXRpb24uDQoNCkNvYXJzZSBjbGFzc2lmaWNhdGlvbiBhbGxvd3MgeW91IHRvIHNpbXBsaWZ5IHlvdXIgZGF0YSBhbmQgaW1wcm92ZSB0aGUgaW50ZXJwcmV0YWJpbGl0eSBvZiB5b3VyIG1vZGVsLiBDb2Fyc2UgY2xhc3NpZmljYXRpb24gcmVxdWlyZXMgeW91IHRvIGJpbiB5b3VyIHJlc3BvbnNlcyBpbnRvIGdyb3VwcyB0aGF0IGNvbnRhaW4gcmFuZ2VzIG9mIHZhbHVlcy4gWW91IGNhbiB1c2UgdGhpcyBiaW5uaW5nIHRlY2huaXF1ZSB0byBwbGFjZSBhbGwgTkFzIGluIHRoZWlyIG93biBiaW4uDQpgYGB7cn0NCiMgTWFrZSB0aGUgbmVjZXNzYXJ5IHJlcGxhY2VtZW50cyBpbiB0aGUgY29hcnNlIGNsYXNzaWZpY2F0aW9uIGV4YW1wbGUgYmVsb3cgDQpsb2FuX2RhdGEkaXJfY2F0IDwtIHJlcChOQSwgbGVuZ3RoKGxvYW5fZGF0YSRpbnRfcmF0ZSkpDQoNCmxvYW5fZGF0YSRpcl9jYXRbd2hpY2gobG9hbl9kYXRhJGludF9yYXRlIDw9IDgpXSA8LSAiMC04Ig0KbG9hbl9kYXRhJGlyX2NhdFt3aGljaChsb2FuX2RhdGEkaW50X3JhdGUgPiA4ICYgbG9hbl9kYXRhJGludF9yYXRlIDw9IDExKV0gPC0gIjgtMTEiDQpsb2FuX2RhdGEkaXJfY2F0W3doaWNoKGxvYW5fZGF0YSRpbnRfcmF0ZSA+IDExICYgbG9hbl9kYXRhJGludF9yYXRlIDw9IDEzLjUpXSA8LSAiMTEtMTMuNSINCmxvYW5fZGF0YSRpcl9jYXRbd2hpY2gobG9hbl9kYXRhJGludF9yYXRlID4gMTMuNSldIDwtICIxMy41KyINCmxvYW5fZGF0YSRpcl9jYXRbd2hpY2goaXMubmEobG9hbl9kYXRhJGludF9yYXRlKSldIDwtICJNaXNzaW5nIg0KDQpsb2FuX2RhdGEkaXJfY2F0IDwtIGFzLmZhY3Rvcihsb2FuX2RhdGEkaXJfY2F0KQ0KDQojIExvb2sgYXQgeW91ciBuZXcgdmFyaWFibGUgdXNpbmcgcGxvdCgpDQpwbG90KGxvYW5fZGF0YSRpcl9jYXQpDQpgYGANCiMjIERhdGEgc3BsaXR0aW5nIGFuZCBjb25mdXNpb24gbWF0cmljZXMNCg0KIyMjIFNwbGl0dGluZyB0aGUgZGF0YSBzZXQNCg0KVG8gbWFrZSB5b3VyIHRyYWluaW5nIGFuZCB0ZXN0IHNldHMsIHlvdSBzaG91bGQgZmlyc3Qgc2V0IGEgc2VlZCB1c2luZyBzZXQuc2VlZCgpLiBTZWVkcyBhbGxvdyB5b3UgdG8gY3JlYXRlIGEgc3RhcnRpbmcgcG9pbnQgZm9yIHJhbmRvbWx5IGdlbmVyYXRlZCBudW1iZXJzLCBzbyB0aGF0IGVhY2ggdGltZSB5b3VyIGNvZGUgaXMgcnVuIHRoZSBzYW1lIGFuc3dlciBpcyBnZW5lcmF0ZWQuIFRoZSBhZHZhbnRhZ2Ugb2YgZG9pbmcgdGhpcyBpbiB5b3VyIHNhbXBsaW5nIGlzIHRoYXQgeW91IG9yIGFueW9uZSBlbHNlIGNhbiByZWNyZWF0ZSB0aGUgZXhhY3Qgc2FtZSB0cmFpbmluZyBhbmQgdGVzdCBzZXRzIGJ5IHVzaW5nIHRoZSBzYW1lIHNlZWQuDQoNClVzaW5nIHNhbXBsZSgpLCB5b3UgY2FuIHJhbmRvbWx5IGFzc2lnbiBvYnNlcnZhdGlvbnMgdG8gdGhlIHRyYWluaW5nIGFuZCB0ZXN0IHNldC4NCg0KRm9yIHRoaXMgZXhlcmNpc2UgeW91IHdpbGwgdXNlIHRoZSB0d28gZmlyc3QgYXJndW1lbnRzIGluIHRoZSBzYW1wbGUoKSBmdW5jdGlvbjoNCi0JVGhlIGZpcnN0IGFyZ3VtZW50IGlzIHRoZSB2ZWN0b3IgZnJvbSB3aGljaCB3ZSB3aWxsIHNhbXBsZSB2YWx1ZXMuIFdlIHdpbGwgcmFuZG9tbHkgcGljayByb3cgbnVtYmVycyBhcyBpbmRpY2VzOyB5b3UgY2FuIHVzZSAxOm5yb3cobG9hbl9kYXRhKSB0byBjcmVhdGUgdGhlIHZlY3RvciBvZiByb3cgbnVtYmVycy4NCi0JVGhlIHNlY29uZCBhcmd1bWVudCBpcyB0aGUgbnVtYmVyIG9mIGl0ZW1zIHRvIGNob29zZS4gV2Ugd2lsbCBlbnRlciAyIC8gMyAqIG5yb3cobG9hbl9kYXRhKSwgYXMgd2UgY29uc3RydWN0IHRoZSB0cmFpbmluZyBzZXQgZmlyc3QuDQoNCmBgYHtyfQ0KIyBTZXQgc2VlZCBvZiA1NjcNCnNldC5zZWVkKDU2NykNCg0KIyBTdG9yZSByb3cgbnVtYmVycyBmb3IgdHJhaW5pbmcgc2V0OiBpbmRleF90cmFpbg0KaW5kZXhfdHJhaW4gPC0gc2FtcGxlKDE6bnJvdyhsb2FuX2RhdGEpLCAyIC8gMyAqIG5yb3cobG9hbl9kYXRhKSkNCg0KIyBDcmVhdGUgdHJhaW5pbmcgc2V0OiB0cmFpbmluZ19zZXQNCnRyYWluaW5nX3NldCA8LSBsb2FuX2RhdGFbaW5kZXhfdHJhaW4sIF0NCg0KIyBDcmVhdGUgdGVzdCBzZXQ6IHRlc3Rfc2V0DQp0ZXN0X3NldCA8LSBsb2FuX2RhdGFbLWluZGV4X3RyYWluLCBdDQpgYGANCiMjIyBDcmVhdGluZyBhIGNvbmZ1c2lvbiBtYXRyaXgNCg0KSW4gdGhpcyBleGFtcGxlLCBhc3N1bWUgdGhhdCB5b3UgaGF2ZSBydW4gYSBtb2RlbCBhbmQgc3RvcmVkIHRoZSBwcmVkaWN0ZWQgb3V0Y29tZXMgaW4gYSB2ZWN0b3IgY2FsbGVkIG1vZGVsX3ByZWQuIFlvdSB3YW50IHRvIHNlZSBob3cgdGhlIG1vZGVsIHBlcmZvcm1lZCBzbyB5b3Ugd2lsbCBjb25zdHJ1Y3QgYSBjb25mdXNpb24gbWF0cml4LiBZb3Ugd2lsbCBjb21wYXJlIHRoZSBhY3R1YWwgbG9hbiBzdGF0dXMgY29sdW1uIChsb2FuX3N0YXR1cykgdG8gdGhlIHByZWRpY3RlZCB2YWx1ZXMgKG1vZGVsX3ByZWQpLCB1c2luZyB0aGUgdGFibGUoKSBmdW5jdGlvbiwgd2hlcmUgdGhlIGFyZ3VtZW50cyBhcmUgdGhlIHRydWUgdmFsdWVzIGFuZCB0aGUgcHJlZGljdGVkIHZhbHVlcy4gUmVjYWxsIHRoZSBjb25mdXNpb24gbWF0cml4IHN0cnVjdHVyZToNCg0KIVtdKGNvbmZ1c2lvbl9tYXRyaXgucG5nKQ0KDQokQ2xhc3NpZmljYXRpb24gYWNjdXJhY3kgPSBcZnJhY3soVFArVE4pfXsoVFArRlArVE4rRk4pfSQNCg0KJFNlbnNpdGl2aXR5PVxmcmFje1RQfXsoVFArRk4pfSQNCg0KJFNwZWNpZmljaXR5PVxmcmFje1ROfXsoVE4rRlApfSQNCg0KYGBge3J9DQptb2RlbF9wcmVkIDwtIHNhbXBsZShjKDAsIDEpLCByZXBsYWNlID0gVFJVRSwgc2l6ZSA9IDk2OTgpDQpgYGANCmBgYHtyfQ0KIyBDcmVhdGUgY29uZnVzaW9uIG1hdHJpeA0KY29uZl9tYXRyaXggPC0gdGFibGUodGVzdF9zZXQkbG9hbl9zdGF0dXMsIG1vZGVsX3ByZWQpDQpjb25mX21hdHJpeA0KDQojIENvbXB1dGUgY2xhc3NpZmljYXRpb24gYWNjdXJhY3kNCihjb25mX21hdHJpeFsxLCAxXSArIGNvbmZfbWF0cml4WzIsIDJdKSAvIChjb25mX21hdHJpeFsxLCAxXSArIGNvbmZfbWF0cml4WzEsIDJdICsgY29uZl9tYXRyaXhbMiwgMV0gKyBjb25mX21hdHJpeFsyLCAyXSkNCg0KIyBDb21wdXRlIHNlbnNpdGl2aXR5DQpjb25mX21hdHJpeFsyLCAyXSAvIChjb25mX21hdHJpeFsyLCAxXSArIGNvbmZfbWF0cml4WzIsIDJdKQ0KYGBgDQojIExvZ2lzdGljIHJlZ3Jlc3Npb24NCg0KIyMgTG9naXN0aWMgcmVncmVzc2lvbjogaW50cm9kdWN0aW9uDQoNCiMjIyBCYXNpYyBsb2dpc3RpYyByZWdyZXNzaW9uDQoNCkluIHRoZSB2aWRlbywgeW91IGxvb2tlZCBhdCBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgaW5jbHVkaW5nIHRoZSB2YXJpYWJsZSBhZ2UgYXMgYSBwcmVkaWN0b3IuIE5vdywgeW91IHdpbGwgaW5jbHVkZSBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLCBhbmQgbGVhcm4gaG93IHRvIGludGVycHJldCBpdHMgcGFyYW1ldGVyIGVzdGltYXRlcy4NCg0KV2hlbiB5b3UgaW5jbHVkZSBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGluIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBpbiBSLCB5b3Ugd2lsbCBvYnRhaW4gYSBwYXJhbWV0ZXIgZXN0aW1hdGUgZm9yIGFsbCBidXQgb25lIG9mIGl0cyBjYXRlZ29yaWVzLiBUaGlzIGNhdGVnb3J5IGZvciB3aGljaCBubyBwYXJhbWV0ZXIgZXN0aW1hdGUgaXMgZ2l2ZW4gaXMgY2FsbGVkIHRoZSByZWZlcmVuY2UgY2F0ZWdvcnkuIFRoZSBwYXJhbWV0ZXIgZm9yIGVhY2ggb2YgdGhlIG90aGVyIGNhdGVnb3JpZXMgcmVwcmVzZW50cyB0aGUgb2RkcyByYXRpbyBpbiBmYXZvciBvZiBhIGxvYW4gZGVmYXVsdCBiZXR3ZWVuIHRoZSBjYXRlZ29yeSBvZiBpbnRlcmVzdCBhbmQgdGhlIHJlZmVyZW5jZSBjYXRlZ29yeS4NCmBgYHtyfQ0KbG9hbl9kYXRhIDwtIHJlYWRSRFMoImRhdGEvbG9hbl9kYXRhX2NoMi5yZHMiKQ0KYGBgDQpgYGB7cn0NCiMgU2V0IHNlZWQgb2YgNTY3DQpzZXQuc2VlZCg1NjcpDQoNCiMgU3RvcmUgcm93IG51bWJlcnMgZm9yIHRyYWluaW5nIHNldDogaW5kZXhfdHJhaW4NCmluZGV4X3RyYWluIDwtIHNhbXBsZSgxOm5yb3cobG9hbl9kYXRhKSwgMiAvIDMgKiBucm93KGxvYW5fZGF0YSkpDQoNCiMgQ3JlYXRlIHRyYWluaW5nIHNldDogdHJhaW5pbmdfc2V0DQp0cmFpbmluZ19zZXQgPC0gbG9hbl9kYXRhW2luZGV4X3RyYWluLCBdDQoNCiMgQ3JlYXRlIHRlc3Qgc2V0OiB0ZXN0X3NldA0KdGVzdF9zZXQgPC0gbG9hbl9kYXRhWy1pbmRleF90cmFpbiwgXQ0KYGBgDQoNCmBgYHtyfQ0KIyBCdWlsZCBhIGdsbSBtb2RlbCB3aXRoIHZhcmlhYmxlIGlyX2NhdCBhcyBhIHByZWRpY3Rvcg0KbG9nX21vZGVsX2NhdCA8LSBnbG0oZm9ybXVsYSA9IGxvYW5fc3RhdHVzIH4gaXJfY2F0LCBmYW1pbHkgPSAiYmlub21pYWwiLA0KICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluaW5nX3NldCkNCg0KIyBQcmludCB0aGUgcGFyYW1ldGVyIGVzdGltYXRlcyANCmxvZ19tb2RlbF9jYXQNCg0KIyBMb29rIGF0IHRoZSBkaWZmZXJlbnQgY2F0ZWdvcmllcyBpbiBpcl9jYXQgdXNpbmcgdGFibGUoKQ0KdGFibGUobG9hbl9kYXRhJGlyX2NhdCkNCmBgYA0KSG93IGRvIHlvdSBpbnRlcnByZXQgdGhlIHBhcmFtZXRlciBlc3RpbWF0ZSBmb3IgdGhlIGludGVyZXN0IHJhdGVzIHRoYXQgYXJlIGJldHdlZW4gOCUgYW5kIDExJT8gQ29tcGFyZWQgdG8gdGhlIHJlZmVyZW5jZSBjYXRlZ29yeSB3aXRoIGludGVyZXN0IHJhdGVzIGJldHdlZW4gMCUgYW5kIDglLCB0aGUgb2RkcyBpbiBmYXZvciBvZiBkZWZhdWx0IGNoYW5nZSBieSBhIG11bHRpcGxlIG9m4oCmDQpgYGB7cn0NCmV4cCgwLjU0OTYpDQpgYGANCiMjIyBNdWx0aXBsZSB2YXJpYWJsZXMgaW4gYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsDQoNClRoZSBpbnRlcnByZXRhdGlvbiBvZiBhIHNpbmdsZSBwYXJhbWV0ZXIgc3RpbGwgaG9sZHMgd2hlbiBpbmNsdWRpbmcgc2V2ZXJhbCB2YXJpYWJsZXMgaW4gYSBtb2RlbC4gV2hlbiB5b3UgZG8gaW5jbHVkZSBzZXZlcmFsIHZhcmlhYmxlcyBhbmQgYXNrIGZvciB0aGUgaW50ZXJwcmV0YXRpb24gd2hlbiBhIGNlcnRhaW4gdmFyaWFibGUgY2hhbmdlcywgaXQgaXMgYXNzdW1lZCB0aGF0IHRoZSBvdGhlciB2YXJpYWJsZXMgcmVtYWluIGNvbnN0YW50LCBvciB1bmNoYW5nZWQuIFRoZXJlIGlzIGEgZmFuY3kgbGF0aW4gcGhyYXNlIGZvciB0aGlzLCBjZXRlcmlzIHBhcmlidXMsIGxpdGVyYWxseSBtZWFuaW5nICJrZWVwaW5nIGFsbCBvdGhlcnMgdGhlIHNhbWUiLg0KDQpUbyBidWlsZCBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBtdWx0aXBsZSB2YXJpYWJsZXMsIHlvdSBjYW4gdXNlIHRoZSArIHNpZ24gdG8gYWRkIHZhcmlhYmxlcy4gWW91ciBmb3JtdWxhIHdpbGwgbG9vayBzb21ldGhpbmcgbGlrZToNCg0KJHkgfiB4MSArIC4uLiArIHhrJA0KDQpJbiBvcmRlciB0byBldmFsdWF0ZSB0aGUgbW9kZWwgdGhlcmUgYXJlIGEgbnVtYmVyIG9mIHRoaW5ncyB0byBiZSBhd2FyZSBvZi4gWW91IGFscmVhZHkgbG9va2VkIGF0IHRoZSBwYXJhbWV0ZXIgdmFsdWVzLCBidXQgdGhhdCBpcyBub3QgdGhlIG9ubHkgdGhpbmcgb2YgaW1wb3J0YW5jZS4gQWxzbyBpbXBvcnRhbnQgaXMgdGhlIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSBvZiBhIGNlcnRhaW4gcGFyYW1ldGVyIGVzdGltYXRlLiBUaGUgc2lnbmlmaWNhbmNlIG9mIGEgcGFyYW1ldGVyIGlzIG9mdGVuIHJlZmVycmVkIHRvIGFzIGEgcC12YWx1ZSwgaG93ZXZlciBpbiBhIG1vZGVsIG91dHB1dCB5b3Ugd2lsbCBzZWUgaXQgZGVub3RlZCBhcyAkUHIoPnx0fCkkLiBJbiBnbG0sIG1pbGQgc2lnbmlmaWNhbmNlIGlzIGRlbm90ZWQgYnkgYSAiLiIgdG8gdmVyeSBzdHJvbmcgc2lnbmlmaWNhbmNlIGRlbm90ZWQgYnkgIioqKiIuIFdoZW4gYSBwYXJhbWV0ZXIgaXMgbm90IHNpZ25pZmljYW50LCB0aGlzIG1lYW5zIHlvdSBjYW5ub3QgYXNzdXJlIHRoYXQgdGhpcyBwYXJhbWV0ZXIgaXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSAwLiBTdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UgaXMgaW1wb3J0YW50LiBJbiBnZW5lcmFsLCBpdCBvbmx5IG1ha2VzIHNlbnNlIHRvIGludGVycHJldCB0aGUgZWZmZWN0IG9uIGRlZmF1bHQgZm9yIHNpZ25pZmljYW50IHBhcmFtZXRlcnMuDQpgYGB7cn0NCiMgQnVpbGQgdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwNCmxvZ19tb2RlbF9tdWx0aSA8LSBnbG0obG9hbl9zdGF0dXMgfiBhZ2UgKyBpcl9jYXQgKyBncmFkZSArIGxvYW5fYW1udCArIGFubnVhbF9pbmMsIGZhbWlseSA9ICJiaW5vbWlhbCIsIGRhdGEgPSB0cmFpbmluZ19zZXQpDQoNCg0KIyBPYnRhaW4gc2lnbmlmaWNhbmNlIGxldmVscyB1c2luZyBzdW1tYXJ5KCkNCnN1bW1hcnkobG9nX21vZGVsX211bHRpKQ0KYGBgDQojIyBMb2dpc3RpYyByZWdyZXNzaW9uOiBwcmVkaWN0aW5nIHRoZSBwcm9iYWJpbGl0eSBvZiBkZWZhdWx0DQoNCiMjIyBQcmVkaWN0aW5nIHRoZSBwcm9iYWJpbGl0eSBvZiBkZWZhdWx0DQoNCnlvdSBjYW4gcHJlZGljdCB0aGUgcHJvYmFiaWxpdHkgZm9yIGFsbCB0aGUgdGVzdCBzZXQgY2FzZXMgYXQgb25jZSB1c2luZyB0aGUgcHJlZGljdCgpIGZ1bmN0aW9uLg0KDQpBZnRlciBoYXZpbmcgb2J0YWluZWQgYWxsIHRoZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHRlc3Qgc2V0IGVsZW1lbnRzLCBpdCBpcyB1c2VmdWwgdG8gZ2V0IGFuIGluaXRpYWwgaWRlYSBvZiBob3cgZ29vZCB0aGUgbW9kZWwgaXMgYXQgZGlzY3JpbWluYXRpbmcgYnkgbG9va2luZyBhdCB0aGUgcmFuZ2Ugb2YgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMuIEEgc21hbGwgcmFuZ2UgbWVhbnMgdGhhdCBwcmVkaWN0aW9ucyBmb3IgdGhlIHRlc3Qgc2V0IGNhc2VzIGRvIG5vdCBsaWUgZmFyIGFwYXJ0LCBhbmQgdGhlcmVmb3JlIHRoZSBtb2RlbCBtaWdodCBub3QgYmUgdmVyeSBnb29kIGF0IGRpc2NyaW1pbmF0aW5nIGdvb2QgZnJvbSBiYWQgY3VzdG9tZXJzLiBXaXRoIGxvdyBkZWZhdWx0IHBlcmNlbnRhZ2VzLCB5b3Ugd2lsbCBub3RpY2UgdGhhdCBpbiBnZW5lcmFsLCB2ZXJ5IGxvdyBwcm9iYWJpbGl0aWVzIG9mIGRlZmF1bHQgYXJlIHByZWRpY3RlZC4gSXQncyB0aW1lIHRvIGhhdmUgYSBsb29rIGF0IGEgZmlyc3QgbW9kZWwuDQoNCmBgYHtyfQ0KbG9nX21vZGVsX3NtYWxsIDwtIGdsbShmb3JtdWxhID0gbG9hbl9zdGF0dXMgfiBhZ2UgKyBpcl9jYXQsIGZhbWlseSA9ICJiaW5vbWlhbCIsIGRhdGEgPSB0cmFpbmluZ19zZXQpDQpgYGANCmBgYHtyfQ0KIyBCdWlsZCB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbA0KcHJlZGljdGlvbnNfYWxsX3NtYWxsIDwtIHByZWRpY3QobG9nX21vZGVsX3NtYWxsLCBuZXdkYXRhID0gdGVzdF9zZXQsIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQojIExvb2sgYXQgdGhlIHJhbmdlIG9mIHRoZSBvYmplY3QgInByZWRpY3Rpb25zX2FsbF9zbWFsbCINCnJhbmdlKHByZWRpY3Rpb25zX2FsbF9zbWFsbCkNCmBgYA0KIyMjIE1ha2luZyBtb3JlIGRpc2NyaW1pbmF0aXZlIG1vZGVscw0KDQpJbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UsIHRoZSByYW5nZSBmb3IgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgb2YgZGVmYXVsdCB3YXMgcmF0aGVyIHNtYWxsLiBBcyBkaXNjdXNzZWQsIHNtYWxsIHByZWRpY3RlZCBkZWZhdWx0IHByb2JhYmlsaXRpZXMgYXJlIHRvIGJlIGV4cGVjdGVkIHdpdGggbG93IGRlZmF1bHQgcmF0ZXMsIGJ1dCBidWlsZGluZyBiaWdnZXIgbW9kZWxzICh3aGljaCBiYXNpY2FsbHkgbWVhbnM6IGluY2x1ZGluZyBtb3JlIHByZWRpY3RvcnMpIGNhbiBleHBhbmQgdGhlIHJhbmdlIG9mIHlvdXIgcHJlZGljdGlvbnMuDQoNCldoZXRoZXIgdGhpcyB3aWxsIGV2ZW50dWFsbHkgbGVhZCB0byBiZXR0ZXIgcHJlZGljdGlvbnMgc3RpbGwgbmVlZHMgdG8gYmUgdmFsaWRhdGVkIGFuZCBkZXBlbmRzIG9uIHRoZSBxdWFsaXR5IG9mIHRoZSBuZXdseSBpbmNsdWRlZCBwcmVkaWN0b3JzLiBCdXQgZmlyc3QsIGhhdmUgYSBsb29rIGF0IGhvdyBiaWdnZXIgbW9kZWxzIGNhbiBleHBhbmQgdGhlIHJhbmdlLg0KYGBge3J9DQojIENoYW5nZSB0aGUgY29kZSBiZWxvdyB0byBjb25zdHJ1Y3QgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIGFsbCBhdmFpbGFibGUgcHJlZGljdG9ycyBpbiB0aGUgZGF0YSBzZXQNCmxvZ19tb2RlbF9mdWxsIDwtIGdsbShsb2FuX3N0YXR1cyB+IC4sIGZhbWlseSA9ICJiaW5vbWlhbCIsIGRhdGEgPSB0cmFpbmluZ19zZXQpDQoNCiMgTWFrZSBQRC1wcmVkaWN0aW9ucyBmb3IgYWxsIHRlc3Qgc2V0IGVsZW1lbnRzIHVzaW5nIHRoZSB0aGUgZnVsbCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsDQpwcmVkaWN0aW9uc19hbGxfZnVsbCA8LSBwcmVkaWN0KGxvZ19tb2RlbF9mdWxsLCBuZXdkYXRhID0gdGVzdF9zZXQsIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQojIExvb2sgYXQgdGhlIHByZWRpY3Rpb25zIHJhbmdlDQpyYW5nZShwcmVkaWN0aW9uc19hbGxfZnVsbCkNCmBgYA0KIyMgRXZhbHVhdGluZyB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCByZXN1bHQNCg0KIyMjIFNwZWNpZnlpbmcgYSBjdXQtb2ZmDQoNCldlIGhhdmUgc2hvd24geW91IGhvdyB0aGUgc3BlY2lmaWNhdGlvbiBvZiBhIGN1dC1vZmYgY2FuIG1ha2UgdGhlIGRpZmZlcmVuY2UgdG8gb2J0YWluIGEgZ29vZCBjb25mdXNpb24gbWF0cml4LiBOb3csIHlvdSB3aWxsIGxlYXJuIGhvdyB0byB0cmFuc2Zvcm0gdGhlIHByZWRpY3Rpb24gdmVjdG9yIHRvIGEgdmVjdG9yIG9mIGJpbmFyeSB2YWx1ZXMgaW5kaWNhdGluZyB0aGUgc3RhdHVzIG9mIHRoZSBsb2FuLiBUaGUgaWZlbHNlKCkgZnVuY3Rpb24gaW4gUiBjYW4gaGVscCB5b3UgaGVyZS4NCg0KQXBwbHlpbmcgdGhlIGlmZWxzZSgpIGZ1bmN0aW9uIGluIHRoZSBjb250ZXh0IG9mIGEgY3V0LW9mZiwgeW91IHdvdWxkIGhhdmUgc29tZXRoaW5nIGxpa2UNCg0KaWZlbHNlKHByZWRpY3Rpb25zID4gMC4zLCAxLCAwKQ0KSW4gdGhlIGZpcnN0IGFyZ3VtZW50LCB5b3UgYXJlIHRlc3Rpbmcgd2hldGhlciBhIGNlcnRhaW4gdmFsdWUgaW4gdGhlIHByZWRpY3Rpb25zLXZlY3RvciBpcyBiaWdnZXIgdGhhbiAwLjMuIElmIHRoaXMgaXMgVFJVRSwgUiByZXR1cm5zICIxIiAoc3BlY2lmaWVkIGluIHRoZSBzZWNvbmQgYXJndW1lbnQpLCBpZiBGQUxTRSwgUiByZXR1cm5zICIwIiAoc3BlY2lmaWVkIGluIHRoZSB0aGlyZCBhcmd1bWVudCksIHJlcHJlc2VudGluZyAiZGVmYXVsdCIgYW5kICJubyBkZWZhdWx0IiwgcmVzcGVjdGl2ZWx5Lg0KYGBge3J9DQojIFRoZSBjb2RlIGZvciB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBhbmQgdGhlIHByZWRpY3Rpb25zIGlzIGdpdmVuIGJlbG93DQpsb2dfbW9kZWxfZnVsbCA8LSBnbG0obG9hbl9zdGF0dXMgfiAuLCBmYW1pbHkgPSAiYmlub21pYWwiLCBkYXRhID0gdHJhaW5pbmdfc2V0KQ0KcHJlZGljdGlvbnNfYWxsX2Z1bGwgPC0gcHJlZGljdChsb2dfbW9kZWxfZnVsbCwgbmV3ZGF0YSA9IHRlc3Rfc2V0LCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyBNYWtlIGEgYmluYXJ5IHByZWRpY3Rpb25zLXZlY3RvciB1c2luZyBhIGN1dC1vZmYgb2YgMTUlDQpwcmVkX2N1dG9mZl8xNSA8LSBpZmVsc2UocHJlZGljdGlvbnNfYWxsX2Z1bGwgPiAwLjE1LCAxLCAwKQ0KDQojIENvbnN0cnVjdCBhIGNvbmZ1c2lvbiBtYXRyaXgNCnRhYmxlKHRlc3Rfc2V0JGxvYW5fc3RhdHVzLCBwcmVkX2N1dG9mZl8xNSkNCmBgYA0KIyMjIENvbXBhcmluZyB0d28gY3V0LW9mZnMNCg0KSGF2ZSBhIGxvb2sgYWdhaW4gYXQgdGhlIGNvbmZ1c2lvbiBtYXRyaXggdXNpbmcgdGhlIGZ1bGwgbW9kZWwgYW5kIGEgY3V0LW9mZiBvZiAxNSUsIHdoaWNoIGlzIHN0b3JlZCBpbiB0aGUgb2JqZWN0IGNvbmZfbWF0cml4XzE1LCBhbmQgYW5vdGhlciBjb25mdXNpb24gbWF0cml4IHVzaW5nIGEgY3V0LW9mZiBvZiAyMCUgYW5kIHRoZSBzYW1lIG1vZGVsLCBzdG9yZWQgaW4gY29uZl9tYXRyaXhfMjAuDQoNCkFjY3VyYWN5IGluY3JlYXNlcywgc2Vuc2l0aXZpdHkgZGVjcmVhc2VzIGFuZCBzcGVjaWZpY2l0eSBpbmNyZWFzZXMuDQoNCiMjIFdyYXAtdXAgYW5kIHJlbWFya3MNCg0KIyMjIENvbXBhcmluZyBsaW5rIGZ1bmN0aW9ucyBmb3IgYSBnaXZlbiBjdXQtb2ZmDQoNCkluIHRoaXMgbGFzdCBleGVyY2lzZSwgeW91IHdpbGwgZml0IGEgbW9kZWwgdXNpbmcgZWFjaCBvZiB0aGUgdGhyZWUgbGluayBmdW5jdGlvbnMgKGxvZ2l0LCBwcm9iaXQgYW5kIGNsb2dsb2cpLCBtYWtlIHByZWRpY3Rpb25zIGZvciB0aGUgdGVzdCBzZXQsIGNsYXNzaWZ5IHRoZSBwcmVkaWN0aW9ucyBpbiB0aGUgYXBwcm9wcmlhdGUgZ3JvdXAgKGRlZmF1bHQgdmVyc3VzIG5vbi1kZWZhdWx0KSBmb3IgYSBnaXZlbiBjdXQtb2ZmLCBtYWtlIGEgY29uZnVzaW9uIG1hdHJpeCBhbmQgY29tcHV0ZSB0aGUgYWNjdXJhY3kgYW5kIHNlbnNpdGl2aXR5IGZvciBlYWNoIG9mIHRoZSBtb2RlbHMgZ2l2ZW4gdGhlIGN1dC1vZmYgdmFsdWUhIFdvdywgeW91J3ZlIGxlYXJuZWQgYSBsb3Qgc28gZmFyLiBBbmQgZmluYWxseSwgeW91IHdpbGwgdHJ5IHRvIGlkZW50aWZ5IHRoZSBtb2RlbCB0aGF0IHBlcmZvcm1zIGJlc3QgaW4gdGVybXMgb2YgYWNjdXJhY3kgZ2l2ZW4gdGhlIGN1dC1vZmYgdmFsdWUhDQoNCkl0IGlzIGltcG9ydGFudCB0byBrbm93IHRoYXQgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIG1vZGVscyB3aWxsIGdlbmVyYWxseSBiZSB2ZXJ5IHNtYWxsLCBhbmQgYWdhaW4sIHRoZSByZXN1bHRzIHdpbGwgZGVwZW5kIG9uIHRoZSBjaG9zZW4gY3V0LW9mZiB2YWx1ZS4gVGhlIG9ic2VydmVkIG91dGNvbWUgKGRlZmF1bHQgdmVyc3VzIG5vbi1kZWZhdWx0KSBpcyBzdG9yZWQgaW4gdHJ1ZV92YWwgaW4gdGhlIGNvbnNvbGUuDQpgYGB7cn0NCiMgRml0IHRoZSBsb2dpdCwgcHJvYml0IGFuZCBjbG9nbG9nLWxpbmsgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMNCmxvZ19tb2RlbF9sb2dpdCA8LSBnbG0obG9hbl9zdGF0dXMgfiBhZ2UgKyBlbXBfY2F0ICsgaXJfY2F0ICsgbG9hbl9hbW50LA0KICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gbG9naXQpLCBkYXRhID0gdHJhaW5pbmdfc2V0KQ0KbG9nX21vZGVsX3Byb2JpdCA8LSBnbG0obG9hbl9zdGF0dXMgfiBhZ2UgKyBlbXBfY2F0ICsgaXJfY2F0ICsgbG9hbl9hbW50LA0KICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gcHJvYml0KSwgZGF0YSA9IHRyYWluaW5nX3NldCkNCg0KbG9nX21vZGVsX2Nsb2dsb2cgPC0gZ2xtKGxvYW5fc3RhdHVzIH4gYWdlICsgZW1wX2NhdCArIGlyX2NhdCArIGxvYW5fYW1udCwNCiAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwobGluayA9IGNsb2dsb2cpLCBkYXRhID0gdHJhaW5pbmdfc2V0KQ0KICANCiMgTWFrZSBwcmVkaWN0aW9ucyBmb3IgYWxsIG1vZGVscyB1c2luZyB0aGUgdGVzdCBzZXQNCnByZWRpY3Rpb25zX2xvZ2l0IDwtIHByZWRpY3QobG9nX21vZGVsX2xvZ2l0LCBuZXdkYXRhID0gdGVzdF9zZXQsIHR5cGUgPSAicmVzcG9uc2UiKQ0KcHJlZGljdGlvbnNfcHJvYml0IDwtIHByZWRpY3QobG9nX21vZGVsX3Byb2JpdCwgbmV3ZGF0YSA9IHRlc3Rfc2V0LCB0eXBlID0gInJlc3BvbnNlIikNCnByZWRpY3Rpb25zX2Nsb2dsb2cgPC0gcHJlZGljdChsb2dfbW9kZWxfY2xvZ2xvZywgbmV3ZGF0YSA9IHRlc3Rfc2V0LCB0eXBlID0gInJlc3BvbnNlIikNCiAgDQojIFVzZSBhIGN1dC1vZmYgb2YgMTQlIHRvIG1ha2UgYmluYXJ5IHByZWRpY3Rpb25zLXZlY3RvcnMNCmN1dG9mZiA8LSAwLjE0DQpjbGFzc19wcmVkX2xvZ2l0IDwtIGlmZWxzZShwcmVkaWN0aW9uc19sb2dpdCA+IGN1dG9mZiwgMSwgMCkNCmNsYXNzX3ByZWRfcHJvYml0IDwtIGlmZWxzZShwcmVkaWN0aW9uc19wcm9iaXQgPiBjdXRvZmYsIDEsIDApDQpjbGFzc19wcmVkX2Nsb2dsb2cgPC0gaWZlbHNlKHByZWRpY3Rpb25zX2Nsb2dsb2cgPiBjdXRvZmYsIDEsIDApDQogIA0KIyBNYWtlIGEgY29uZnVzaW9uIG1hdHJpeCBmb3IgdGhlIHRocmVlIG1vZGVscw0KdHJ1ZV92YWwgPC0gdGVzdF9zZXQkbG9hbl9zdGF0dXMNCnRhYl9jbGFzc19sb2dpdCA8LSB0YWJsZSh0cnVlX3ZhbCxjbGFzc19wcmVkX2xvZ2l0KQ0KdGFiX2NsYXNzX2xvZ2l0DQp0YWJfY2xhc3NfcHJvYml0IDwtIHRhYmxlKHRydWVfdmFsLGNsYXNzX3ByZWRfcHJvYml0KQ0KdGFiX2NsYXNzX3Byb2JpdA0KdGFiX2NsYXNzX2Nsb2dsb2cgPC0gdGFibGUodHJ1ZV92YWwsY2xhc3NfcHJlZF9jbG9nbG9nKQ0KdGFiX2NsYXNzX2Nsb2dsb2cNCiAgDQojIENvbXB1dGUgdGhlIGNsYXNzaWZpY2F0aW9uIGFjY3VyYWN5IGZvciBhbGwgdGhyZWUgbW9kZWxzDQphY2NfbG9naXQgPC0gc3VtKGRpYWcodGFiX2NsYXNzX2xvZ2l0KSkgLyBucm93KHRlc3Rfc2V0KQ0KYWNjX2xvZ2l0DQphY2NfcHJvYml0IDwtIHN1bShkaWFnKHRhYl9jbGFzc19wcm9iaXQpKSAvIG5yb3codGVzdF9zZXQpDQphY2NfcHJvYml0DQphY2NfY2xvZ2xvZyA8LSBzdW0oZGlhZyh0YWJfY2xhc3NfY2xvZ2xvZykpIC8gbnJvdyh0ZXN0X3NldCkNCmFjY19jbG9nbG9nDQpgYGANCiMgRGVjaXNpb24gdHJlZXMNCg0KIyMgV2hhdCBpcyBhIGRlY2lzaW9uIHRyZWU/DQoNCiMjIyBDb21wdXRpbmcgdGhlIGdhaW4gZm9yIGEgdHJlZQ0KDQpXZSBsb29rZWQgYXQgaG93IHRoZSBHaW5pLW1lYXN1cmUgaXMgdXNlZCB0byBjcmVhdGUgdGhlIHBlcmZlY3Qgc3BsaXQgZm9yIGEgdHJlZS4gTm93LCB5b3Ugd2lsbCBjb21wdXRlIHRoZSBnYWluIGZvciB0aGUgdHJlZSBsb2FkZWQgaW4geW91ciB3b3Jrc3BhY2UuDQoNClRoZSBkYXRhIHNldCBjb250YWlucyA1MDAgY2FzZXMsIDg5IG9mIHRoZXNlIGNhc2VzIGFyZSBkZWZhdWx0cy4gVGhpcyBsZWQgdG8gYSBHaW5pIG9mIDAuMjkyNjMyIGluIHRoZSByb290IG5vZGUuIEFzIGEgc21hbGwgcmVtaW5kZXIsIHJlbWVtYmVyIHRoYXQgR2luaSBvZiBhIGNlcnRhaW4gbm9kZSA9IDIgKiBwcm9wb3J0aW9uIG9mIGRlZmF1bHRzIGluIHRoaXMgbm9kZSAqIHByb3BvcnRpb24gb2Ygbm9uLWRlZmF1bHRzIGluIHRoaXMgbm9kZS4gSGF2ZSBhIGxvb2sgYXQgdGhlIGNvZGUgZm9yIGEgcmVmcmVzaGVyLg0KDQogICAgZ2luaV9yb290IDwtIDIgKiAoODkgLyA1MDApICogKDQxMSAvIDUwMCkNCg0KDQpZb3Ugd2lsbCB1c2UgdGhlc2UgR2luaSBtZWFzdXJlcyB0byBoZWxwIHlvdSBjYWxjdWxhdGUgdGhlIGdhaW4gb2YgdGhlIGxlYWYgbm9kZXMgd2l0aCByZXNwZWN0IHRvIHRoZSByb290IG5vZGUuIExvb2sgYXQgdGhlIGZvbGxvd2luZyBjb2RlIHRvIGdldCBhbiBpZGVhIG9mIGhvdyB5b3UgY2FuIHVzZSB0aGUgZ2luaSBtZWFzdXJlcyB5b3UgY3JlYXRlZCB0byBjYWxjdWxhdGUgdGhlIGdhaW4gb2YgYSBub2RlLg0KDQogICAgR2FpbiA9IGdpbmlfcm9vdCAtIChwcm9wKGNhc2VzIGxlZnQgbGVhZikgKiBnaW5pX2xlZnQpIC0gKHByb3AoY2FzZXMgcmlnaHQgbGVhZiAqIGdpbmlfcmlnaHQpKQ0KICAgIA0KQ29tcHV0ZSB0aGUgZ2luaSBpbiB0aGUgbGVmdCBoYW5kIGFuZCB0aGUgcmlnaHQgaGFuZCBub2RlLCBhbmQgdGhlIGdhaW4gb2YgdGhlIHR3byBsZWFmIG5vZGVzIHdpdGggcmVzcGVjdCB0byB0aGUgcm9vdCBub2RlLiBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgdGhlIHRyZWUgaXMgc21hbGxfdHJlZS4NCmBgYHtyfQ0KIyBMb2FkIHBhY2thZ2UgcnBhcnQgaW4geW91ciB3b3Jrc3BhY2UuDQpsaWJyYXJ5KHJwYXJ0KQ0KDQojIFRoZSBHaW5pLW1lYXN1cmUgb2YgdGhlIHJvb3Qgbm9kZSBpcyBnaXZlbiBiZWxvdw0KZ2luaV9yb290IDwtIDIgKiA4OSAvIDUwMCAqIDQxMSAvIDUwMA0KDQojIENvbXB1dGUgdGhlIEdpbmkgbWVhc3VyZSBmb3IgdGhlIGxlZnQgbGVhZiBub2RlDQpnaW5pX2xsIDwtIDIgKiAoNDAxLzQ0NikqKDQ1LzQ0NikgDQoNCiMgQ29tcHV0ZSB0aGUgR2luaSBtZWFzdXJlIGZvciB0aGUgcmlnaHQgbGVhZiBub2RlDQpnaW5pX3JsIDwtMiAqICgxMC81NCkqKDQ0LzU0KSANCg0KIyBDb21wdXRlIHRoZSBnYWluDQpnYWluIDwtIGdpbmlfcm9vdCAtIDQ0NiAvIDUwMCAqIGdpbmlfbGwgLSA1NCAvIDUwMCAqIGdpbmlfcmwNCmdhaW4NCg0KIyBjb21wYXJlIHRoZSBnYWluLWNvbHVtbiBpbiBzbWFsbF90cmVlJHNwbGl0cyB3aXRoIG91ciBjb21wdXRlZCBnYWluLCBtdWx0aXBsaWVkIGJ5IDUwMCwgYW5kIGFzc3VyZSB0aGV5IGFyZSB0aGUgc2FtZQ0KDQpzbWFsbF9zZXQgPC0gcmVhZFJEUygic21hbGxfc2V0LnJkcyIpDQpzbWFsbF90cmVlIDwtIHJwYXJ0KGZvcm11bGEgPSBzdGF0dXMgfiBhZ2UsIGRhdGEgPSBzbWFsbF9zZXQsIG1ldGhvZCA9ICJjbGFzcyIsIA0KICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKG1pbnNwbGl0ID0gNSwgY3AgPSAwLjAwMSwgbWF4ZGVwdGggPSAxKSkNCnNtYWxsX3RyZWUkc3BsaXRzDQppbXByb3ZlIDwtIGdhaW4gKiA1MDANCmltcHJvdmUNCmBgYA0KIyMgQnVpbGRpbmcgZGVjaXNpb24gdHJlZXMgdXNpbmcgdGhlIHJwYXJ0KCktcGFja2FnZQ0KDQojIyMgVW5kZXJzYW1wbGluZyB0aGUgdHJhaW5pbmcgc2V0DQoNCkluIHRoZSB2aWRlbywgeW91IHNhdyB0aGF0IHRvIG92ZXJjb21lIHRoZSB1bmJhbGFuY2VkIGRhdGEgcHJvYmxlbSwgeW91IGNhbiB1c2UgdW5kZXItIG9yIG92ZXJzYW1wbGluZy4gVGhlIHRyYWluaW5nIHNldCBoYXMgYmVlbiB1bmRlcnNhbXBsZWQgZm9yIHlvdSwgc3VjaCB0aGF0IDEvMyBvZiB0aGUgdHJhaW5pbmcgc2V0IGNvbnNpc3RzIG9mIGRlZmF1bHRzLCBhbmQgMi8zIG9mIG5vbi1kZWZhdWx0cy4gVGhlIHJlc3VsdGluZyBkYXRhIHNldCBpcyBhdmFpbGFibGUgaW4geW91ciB3b3Jrc3BhY2UgYW5kIG5hbWVkIHVuZGVyc2FtcGxlZF90cmFpbmluZ19zZXQsIGFuZCBjb250YWlucyBsZXNzIG9ic2VydmF0aW9ucyAoNjU3MCBpbnN0ZWFkIG9mIDE5Mzk0KS4gSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgY3JlYXRlIGEgZGVjaXNpb24gdHJlZSB1c2luZyB0aGUgdW5kZXJzYW1wbGVkIGRhdGEgc2V0Lg0KDQpZb3Ugd2lsbCBub3RpY2UgdGhhdCB0aGUgdHJlZXMgaW4gdGhpcyBhbmQgdGhlIG5leHQgZXhlcmNpc2VzIGFyZSB2ZXJ5IGJpZywgc28gYmlnIHRoYXQgeW91IGNhbm5vdCByZWFsbHkgcmVhZCB0aGVtIGFueW1vcmUuDQoNCmBgYHtyfQ0KIyBMb2FkIHBhY2thZ2UgcnBhcnQgaW4geW91ciB3b3Jrc3BhY2UuDQpsaWJyYXJ5KHJwYXJ0KQ0KDQojIENoYW5nZSB0aGUgY29kZSBwcm92aWRlZCBpbiB0aGUgdmlkZW8gc3VjaCB0aGF0IGEgZGVjaXNpb24gdHJlZSBpcyBjb25zdHJ1Y3RlZCB1c2luZyB0aGUgdW5kZXJzYW1wbGVkIHRyYWluaW5nIHNldC4gSW5jbHVkZSBycGFydC5jb250cm9sIHRvIHJlbGF4IHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciB0byAwLjAwMS4NCnRyZWVfdW5kZXJzYW1wbGUgPC0gcnBhcnQobG9hbl9zdGF0dXMgfiAuLCBtZXRob2QgPSAiY2xhc3MiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gIHRyYWluaW5nX3NldCkNCmBgYA0KQ2hhbmdlIHRoZSBjb2RlIHByb3ZpZGVkIHN1Y2ggdGhhdCBhIGRlY2lzaW9uIHRyZWUgaXMgY29uc3RydWN0ZWQgdXNpbmcgdGhlIHVuZGVyc2FtcGxlZCB0cmFpbmluZyBzZXQgaW5zdGVhZCBvZiB0cmFpbmluZ19zZXQuIEFkZGl0aW9uYWxseSwgYWRkIHRoZSBhcmd1bWVudCBjb250cm9sID0gcnBhcnQuY29udHJvbChjcCA9IDAuMDAxKS4gY3AsIHdoaWNoIGlzIHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciwgaXMgdGhlIHRocmVzaG9sZCB2YWx1ZSBmb3IgYSBkZWNyZWFzZSBpbiBvdmVyYWxsIGxhY2sgb2YgZml0IGZvciBhbnkgc3BsaXQuIElmIGNwIGlzIG5vdCBtZXQsIGZ1cnRoZXIgc3BsaXRzIHdpbGwgbm8gbG9uZ2VyIGJlIHB1cnN1ZWQuIGNwJ3MgZGVmYXVsdCB2YWx1ZSBpcyAwLjAxLCBidXQgZm9yIGNvbXBsZXggcHJvYmxlbXMsIGl0IGlzIGFkdmlzZWQgdG8gcmVsYXggY3AuDQoNClBsb3QgdGhlIGRlY2lzaW9uIHRyZWUgdXNpbmcgdGhlIGZ1bmN0aW9uIHBsb3QgYW5kIHRoZSB0cmVlIG9iamVjdCBuYW1lLiBBZGQgYSBzZWNvbmQgYXJndW1lbnQgdW5pZm9ybSA9IFRSVUUgdG8gZ2V0IGVxdWFsLXNpemVkIGJyYW5jaGVzLg0KYGBge3J9DQpsaWJyYXJ5KFJPU0UpDQp1bmRlcnNhbXBsZWRfdHJhaW5pbmdfc2V0IDwtIG92dW4uc2FtcGxlKGxvYW5fc3RhdHVzIH4gLiwgZGF0YSA9IHRyYWluaW5nX3NldCwgbWV0aG9kID0gInVuZGVyIiwgTiA9IDY1NzAsIHNlZWQgPSAxKSRkYXRhDQp0YWJsZSh1bmRlcnNhbXBsZWRfdHJhaW5pbmdfc2V0JGxvYW5fc3RhdHVzICkNCmBgYA0KDQpgYGB7cn0NCiMgQ2hhbmdlIHRoZSBjb2RlIHByb3ZpZGVkIGluIHRoZSB2aWRlbyBzdWNoIHRoYXQgYSBkZWNpc2lvbiB0cmVlIGlzIGNvbnN0cnVjdGVkIHVzaW5nIHRoZSB1bmRlcnNhbXBsZWQgdHJhaW5pbmcgc2V0LiBJbmNsdWRlIHJwYXJ0LmNvbnRyb2wgdG8gcmVsYXggdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyIHRvIDAuMDAxLg0KdHJlZV91bmRlcnNhbXBsZSA8LSBycGFydChsb2FuX3N0YXR1cyB+IC4sIG1ldGhvZCA9ICJjbGFzcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSAgdW5kZXJzYW1wbGVkX3RyYWluaW5nX3NldCwgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKGNwID0gMC4wMDEpKQ0KDQojIFBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCnBsb3QodHJlZV91bmRlcnNhbXBsZSwgdW5pZm9ybSA9IFRSVUUpDQoNCiMgQWRkIGxhYmVscyB0byB0aGUgZGVjaXNpb24gdHJlZQ0KdGV4dCh0cmVlX3VuZGVyc2FtcGxlKQ0KYGBgDQojIyMgQ2hhbmdpbmcgdGhlIHByaW9yIHByb2JhYmlsaXRpZXMNCg0KQXMgbWVudGlvbmVkIGluIHRoZSB2aWRlbywgeW91IGNhbiBhbHNvIGNoYW5nZSB0aGUgcHJpb3IgcHJvYmFiaWxpdGllcyB0byBvYnRhaW4gYSBkZWNpc2lvbiB0cmVlLiBUaGlzIGlzIGFuIGluZGlyZWN0IHdheSBvZiBhZGp1c3RpbmcgdGhlIGltcG9ydGFuY2Ugb2YgbWlzY2xhc3NpZmljYXRpb25zIGZvciBlYWNoIGNsYXNzLiBZb3UgY2FuIHNwZWNpZnkgYW5vdGhlciBhcmd1bWVudCBpbnNpZGUgcnBhcnQoKSB0byBpbmNsdWRlIHByaW9yIHByb2JhYml0aWVzLiBUaGUgYXJndW1lbnQgeW91IGFyZSBsb29raW5nIGZvciBoYXMgdGhlIGZvbGxvd2luZyBmb3JtDQogICAgDQogICAgcGFybXMgPSBsaXN0KHByaW9yPWMobm9uX2RlZmF1bHRfcHJvcG9ydGlvbiwgZGVmYXVsdF9wcm9wb3J0aW9uKSkNCg0KICAgICMgQ2hhbmdlIHRoZSBjb2RlIGJlbG93IHN1Y2ggdGhhdCBhIHRyZWUgaXMgY29uc3RydWN0ZWQgd2l0aCBhZGp1c3RlZCBwcmlvciBwcm9iYWJpbGl0aWVzLg0KICAgIHRyZWVfcHJpb3IgPC0gcnBhcnQobG9hbl9zdGF0dXMgfiAuLCBtZXRob2QgPSAiY2xhc3MiLA0KICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluaW5nX3NldCkNCg0KQ2hhbmdlIHRoZSBjb2RlIHByb3ZpZGVkIHN1Y2ggdGhhdCBhIGRlY2lzaW9uIHRyZWUgaXMgY29uc3RydWN0ZWQgLCBpbmNsdWRpbmcgdGhlIGFyZ3VtZW50IHBhcm1zIGFuZCBjaGFuZ2luZyB0aGUgcHJvcG9ydGlvbiBvZiBub24tZGVmYXVsdHMgdG8gMC43LCBhbmQgb2YgZGVmYXVsdHMgdG8gMC4zICh0aGV5IHNob3VsZCBhbHdheXMgc3VtIHVwIHRvIDEpLiBBZGRpdGlvbmFsbHksIGluY2x1ZGUgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woY3AgPSAwLjAwMSkgYXMgd2VsbC4NCg0KYGBge3J9DQojIENoYW5nZSB0aGUgY29kZSBiZWxvdyBzdWNoIHRoYXQgYSB0cmVlIGlzIGNvbnN0cnVjdGVkIHdpdGggYWRqdXN0ZWQgcHJpb3IgcHJvYmFiaWxpdGllcy4NCnRyZWVfcHJpb3IgPC0gcnBhcnQobG9hbl9zdGF0dXMgfiAuLCBtZXRob2QgPSAiY2xhc3MiLA0KICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5pbmdfc2V0LCBwYXJtcyA9IGxpc3QocHJpb3IgPSBjKDAuNywgMC4zKSksDQogICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKGNwID0gMC4wMDEpKQ0KDQojIFBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCnBsb3QodHJlZV9wcmlvciwgdW5pZm9ybSA9IFRSVUUpDQoNCiMgQWRkIGxhYmVscyB0byB0aGUgZGVjaXNpb24gdHJlZQ0KdGV4dCh0cmVlX3ByaW9yKQ0KYGBgDQojIyMgSW5jbHVkaW5nIGEgbG9zcyBtYXRyaXgNCg0KVGhpcmRseSwgeW91IGNhbiBpbmNsdWRlIGEgbG9zcyBtYXRyaXgsIGNoYW5naW5nIHRoZSByZWxhdGl2ZSBpbXBvcnRhbmNlIG9mIG1pc2NsYXNzaWZ5aW5nIGEgZGVmYXVsdCBhcyBub24tZGVmYXVsdCB2ZXJzdXMgYSBub24tZGVmYXVsdCBhcyBhIGRlZmF1bHQuIFlvdSB3YW50IHRvIHN0cmVzcyB0aGF0IG1pc2NsYXNzaWZ5aW5nIGEgZGVmYXVsdCBhcyBhIG5vbi1kZWZhdWx0IHNob3VsZCBiZSBwZW5hbGl6ZWQgbW9yZSBoZWF2aWx5LiBJbmNsdWRpbmcgYSBsb3NzIG1hdHJpeCBjYW4gYWdhaW4gYmUgZG9uZSBpbiB0aGUgYXJndW1lbnQgcGFybXMgaW4gdGhlIGxvc3MgbWF0cml4Lg0KDQogICAgcGFybXMgPSBsaXN0KGxvc3MgPSBtYXRyaXgoYygwLCBjb3N0X2RlZl9hc19ub25kZWYsIGNvc3Rfbm9uZGVmX2FzX2RlZiwgMCksIG5jb2w9MikpDQoNCkRvaW5nIHRoaXMsIHlvdSBhcmUgY29uc3RydWN0aW5nIGEgMngyLW1hdHJpeCB3aXRoIHplcm9lcyBvbiB0aGUgZGlhZ29uYWwgYW5kIGNoYW5nZWQgbG9zcyBwZW5hbHRpZXMgb2ZmLWRpYWdvbmFsLiBUaGUgZGVmYXVsdCBsb3NzIG1hdHJpeCBpcyBhbGwgb25lcyBvZmYtZGlhZ29uYWwuDQoNCiAgICB0cmVlX2xvc3NfbWF0cml4IDwtIHJwYXJ0KGxvYW5fc3RhdHVzIH4gLiwgbWV0aG9kID0gImNsYXNzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSAgdHJhaW5pbmdfc2V0KQ0KDQpDaGFuZ2UgdGhlIGNvZGUgcHJvdmlkZWQgc3VjaCBhIGxvc3MgbWF0cml4IGlzIGluY2x1ZGVkLCB3aXRoIGEgcGVuYWxpemF0aW9uIHRoYXQgaXMgMTAgdGltZXMgYmlnZ2VyIHdoZW4gbWlzY2xhc3NpZnlpbmcgYW4gYWN0dWFsIGRlZmF1bHQgYXMgYSBub24tZGVmYXVsdC4gVGhpcyBjYW4gYmUgZG9uZSByZXBsYWNpbmcgY29zdF9kZWZfYXNfbm9uZGVmIGJ5IDEwLCBhbmQgY29zdF9ub25kZWZfYXNfZGVmIGJ5IDEuIFNpbWlsYXIgdG8gd2hhdCB5b3UndmUgZG9uZSBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2VzLCBpbmNsdWRlIHJwYXJ0LmNvbnRyb2wgdG8gcmVsYXggdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyIHRvIDAuMDAxLg0KYGBge3J9DQojIENoYW5nZSB0aGUgY29kZSBiZWxvdyBzdWNoIHRoYXQgYSBkZWNpc2lvbiB0cmVlIGlzIGNvbnN0cnVjdGVkIHVzaW5nIGEgbG9zcyBtYXRyaXggcGVuYWxpemluZyAxMCB0aW1lcyBtb3JlIGhlYXZpbHkgZm9yIG1pc2NsYXNzaWZpZWQgZGVmYXVsdHMuDQp0cmVlX2xvc3NfbWF0cml4IDwtIHJwYXJ0KGxvYW5fc3RhdHVzIH4gLiwgbWV0aG9kID0gImNsYXNzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9ICB0cmFpbmluZ19zZXQsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHBhcm1zID0gbGlzdChsb3NzID0gbWF0cml4KGMoMCwgMTAsIDEsIDApLCBuY29sID0gMikpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbChjcCA9IDAuMDAxKSkNCg0KDQojIFBsb3QgdGhlIGRlY2lzaW9uIHRyZWUNCnBsb3QodHJlZV9sb3NzX21hdHJpeCwgdW5pZm9ybSA9IFRSVUUpDQoNCiMgQWRkIGxhYmVscyB0byB0aGUgZGVjaXNpb24gdHJlZQ0KdGV4dCh0cmVlX2xvc3NfbWF0cml4KQ0KYGBgDQojIyBQcnVuaW5nIHRoZSBkZWNpc2lvbiB0cmVlDQoNCiMjIyBQcnVuaW5nIHRoZSB0cmVlIHdpdGggY2hhbmdlZCBwcmlvciBwcm9iYWJpbGl0aWVzDQoNClBydW5pbmcgYSB0cmVlIGlzIG5lY2Vzc2FyeSB0byBhdm9pZCBvdmVyZml0dGluZy4gVGhlcmUgd2VyZSBzb21lIGJpZyB0cmVlcyBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2VzIGFuZCBub3cgeW91IHdpbGwgcHV0IHdoYXQgeW91IGhhdmUgbGVhcm5lZCBpbnRvIHByYWN0aWNlLCBhbmQgcHJ1bmUgdGhlIHByZXZpb3VzbHkgY29uc3RydWN0ZWQgdHJlZSB3aXRoIHRoZSBjaGFuZ2VkIHByaW9yIHByb2JhYmlsaXRpZXMuIFRoZSBycGFydCBwYWNrYWdlIGlzIGFscmVhZHkgbG9hZGVkIGluIHlvdXIgd29ya3NwYWNlLg0KDQpZb3Ugd2lsbCBmaXJzdCBzZXQgYSBzZWVkIHRvIG1ha2Ugc3VyZSB0aGUgcmVzdWx0cyBhcmUgcmVwcm9kdWNpYmxlIGFzIG1lbnRpb25lZCBpbiB0aGUgdmlkZW8sIGJlY2F1c2UgeW91IHdpbGwgYmUgZXhhbWluaW5nIGNyb3NzLXZhbGlkYXRlZCBlcnJvciByZXN1bHRzLiBSZXN1bHRzIGludm9sdmUgcmFuZG9tbmVzcyBhbmQgY291bGQgZGlmZmVyIHNsaWdodGx5IHVwb24gcnVubmluZyB0aGUgZnVuY3Rpb24gYWdhaW4gd2l0aCBhIGRpZmZlcmVudCBzZWVkLg0KDQpJbiB0aGlzIGV4ZXJjaXNlIHlvdSB3aWxsIGxlYXJuIHRvIGlkZW50aWZ5IHdoaWNoIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChDUCkgd2lsbCBtaW5pbWl6ZSB0aGUgY3Jvc3MtdmFsaWRhdGVkIGVycm9yIHJlc3VsdHMsIHRoZW4gcHJ1bmUgeW91ciB0cmVlIGJhc2VkIG9uIHRoaXMgdmFsdWUuDQoNCnRyZWVfdW5kZXJzYW1wbGUNCnRyZWVfcHJpb3IgDQp0cmVlX2xvc3NfbWF0cml4IA0KDQpVc2UgcGxvdGNwKCkgdG8gdmlzdWFsaXplIGNyb3NzLXZhaWRhdGVkIGVycm9yIChYLXZhbCBSZWxhdGl2ZSBFcnJvcikgaW4gcmVsYXRpb24gdG8gdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyIGZvciB0cmVlX3ByaW9yLg0KVXNlIHByaW50Y3AoKSB0byBwcmludCBhIHRhYmxlIG9mIGluZm9ybWF0aW9uIGFib3V0IENQLCBzcGxpdHMsIGFuZCBlcnJvcnMuIFNlZSBpZiB5b3UgY2FuIGlkZW50aWZ5IHdoaWNoIHNwbGl0IGhhcyB0aGUgbWluaW11bSBjcm9zcy12YWxpZGF0ZWQgZXJyb3IgaW4gdHJlZV9wcmlvci4NCmBgYHtyfQ0Kc2V0LnNlZWQoMzQ1KQ0KIyB0cmVlX3ByaW9yIGlzIGxvYWRlZCBpbiB5b3VyIHdvcmtzcGFjZQ0KDQojIFBsb3QgdGhlIGNyb3NzLXZhbGlkYXRlZCBlcnJvciByYXRlIGFzIGEgZnVuY3Rpb24gb2YgdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyDQpwbG90Y3AodHJlZV9wcmlvcikNCg0KIyBVc2UgcHJpbnRjcCgpIHRvIGlkZW50aWZ5IGZvciB3aGljaCBjb21wbGV4aXR5IHBhcmFtZXRlciB0aGUgY3Jvc3MtdmFsaWRhdGVkIGVycm9yIHJhdGUgaXMgbWluaW1pemVkLg0KcHJpbnRjcCh0cmVlX3ByaW9yKQ0KDQojIENyZWF0ZSBhbiBpbmRleCBmb3Igb2YgdGhlIHJvdyB3aXRoIHRoZSBtaW5pbXVtIHhlcnJvcg0KaW5kZXggPC0gd2hpY2gubWluKHRyZWVfcHJpb3IkY3B0YWJsZVsgLCAieGVycm9yIl0pDQoNCiMgQ3JlYXRlIHRyZWVfbWluDQp0cmVlX21pbiA8LSB0cmVlX3ByaW9yJGNwdGFibGVbaW5kZXgsICJDUCJdDQoNCiMgIFBydW5lIHRoZSB0cmVlIHVzaW5nIHRyZWVfbWluDQpwdHJlZV9wcmlvciA8LSBwcnVuZSh0cmVlX3ByaW9yLCBjcCA9IHRyZWVfbWluKQ0KDQojIFVzZSBwcnAoKSB0byBwbG90IHRoZSBwcnVuZWQgdHJlZQ0KbGlicmFyeShycGFydC5wbG90KQ0KcHJwKHB0cmVlX3ByaW9yKSANCmBgYA0KIyMjIFBydW5pbmcgdGhlIHRyZWUgd2l0aCB0aGUgbG9zcyBtYXRyaXgNCg0KSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgcHJ1bmUgdGhlIHRyZWUgdGhhdCB3YXMgYnVpbHQgdXNpbmcgYSBsb3NzIG1hdHJpeCBpbiBvcmRlciB0byBwZW5hbGl6ZSBtaXNjbGFzc2lmaWVkIGRlZmF1bHRzIG1vcmUgdGhhbiBtaXNjbGFzc2lmaWVkIG5vbi1kZWZhdWx0cy4NCg0KYGBge3J9DQojIHNldCBhIHNlZWQgYW5kIHJ1biB0aGUgY29kZSB0byBjb25zdHJ1Y3QgdGhlIHRyZWUgd2l0aCB0aGUgbG9zcyBtYXRyaXggYWdhaW4NCnNldC5zZWVkKDM0NSkNCnRyZWVfbG9zc19tYXRyaXggIDwtIHJwYXJ0KGxvYW5fc3RhdHVzIH4gLiwgbWV0aG9kID0gImNsYXNzIiwgZGF0YSA9IHRyYWluaW5nX3NldCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcm1zID0gbGlzdChsb3NzPW1hdHJpeChjKDAsIDEwLCAxLCAwKSwgbmNvbCA9IDIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKGNwID0gMC4wMDEpKQ0KDQojIFBsb3QgdGhlIGNyb3NzLXZhbGlkYXRlZCBlcnJvciByYXRlIGFzIGEgZnVuY3Rpb24gb2YgdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyDQpwbG90Y3AodHJlZV9sb3NzX21hdHJpeCkNCmBgYA0KTG9va2luZyBhdCB0aGUgY3AtcGxvdCwgeW91IHdpbGwgbm90aWNlIHRoYXQgcHJ1bmluZyB0aGUgdHJlZSB1c2luZyB0aGUgbWluaW11bSBjcm9zcy12YWxpZGF0ZWQgZXJyb3Igd2lsbCBsZWFkIHRvIGEgdHJlZSB0aGF0IGlzIGFzIGJpZyBhcyB0aGUgdW5wcnVuZWQgdHJlZSwgYXMgdGhlIGNyb3NzLXZhbGlkYXRlZCBlcnJvciByZWFjaGVzIGl0cyBtaW5pbXVtIGZvciBjcCA9IDAuMDAxLiBCZWNhdXNlIHlvdSB3b3VsZCBsaWtlIHRvIG1ha2UgdGhlIHRyZWUgc29tZXdoYXQgc21hbGxlciwgdHJ5IHBydW5pbmcgdGhlIHRyZWUgdXNpbmcgY3AgPSAwLjAwMTI3ODguIEZvciB0aGlzIGNvbXBsZXhpdHkgcGFyYW1ldGVyLCB0aGUgY3Jvc3MtdmFsaWRhdGVkIGVycm9yIGFwcHJvYWNoZXMgdGhlIG1pbmltdW0gb2JzZXJ2ZWQgZXJyb3IuIENhbGwgdGhlIHBydW5lZCB0cmVlIHB0cmVlX2xvc3NfbWF0cml4Lg0KYGBge3J9DQojIFBydW5lIHRoZSB0cmVlIHVzaW5nIGNwID0gMC4wMDEyNzg4DQpwdHJlZV9sb3NzX21hdHJpeCA8LSBwcnVuZSh0cmVlX2xvc3NfbWF0cml4LCBjcCA9IDAuMDAxMjc4OCkNCg0KIyBVc2UgcHJwKCkgYW5kIGFyZ3VtZW50IGV4dHJhID0gMSB0byBwbG90IHRoZSBwcnVuZWQgdHJlZQ0KcHJwKHB0cmVlX2xvc3NfbWF0cml4LCBleHRyYSA9IDEpDQpgYGANCmBgYHtyfQ0KIyBDcmVhdGUgYW4gaW5kZXggZm9yIG9mIHRoZSByb3cgd2l0aCB0aGUgbWluaW11bSB4ZXJyb3INCmluZGV4IDwtIHdoaWNoLm1pbih0cmVlX2xvc3NfbWF0cml4JGNwdGFibGVbICwgInhlcnJvciJdKQ0KDQojIENyZWF0ZSB0cmVlX21pbg0KdHJlZV9taW4gPC0gdHJlZV9sb3NzX21hdHJpeCRjcHRhYmxlW2luZGV4LCAiQ1AiXQ0KIyBQcnVuZSB0aGUgdHJlZSB1c2luZyBjcCA9IDAuMDAxMjc4OA0KcHRyZWVfbG9zc19tYXRyaXggPC0gcHJ1bmUodHJlZV9sb3NzX21hdHJpeCwgY3AgPSB0cmVlX21pbikNCg0KIyBVc2UgcHJwKCkgYW5kIGFyZ3VtZW50IGV4dHJhID0gMSB0byBwbG90IHRoZSBwcnVuZWQgdHJlZQ0KcHJwKHB0cmVlX2xvc3NfbWF0cml4LCBleHRyYSA9IDEpDQpgYGANCiMjIE90aGVyIHRyZWUgb3B0aW9ucyBhbmQgdGhlIGNvbnN0cnVjdGlvbiBvZiBjb25mdXNpb24gbWF0cmljZXMNCg0KIyMjIE9uZSBmaW5hbCB0cmVlIHVzaW5nIG1vcmUgb3B0aW9ucw0KDQpZb3Ugd2lsbCB1c2Ugc29tZSBmaW5hbCBhcmd1bWVudHMgdGhhdCB3ZXJlIGRpc2N1c3NlZCBpbiB0aGUgdmlkZW8uIFNvbWUgc3BlY2lmaWNhdGlvbnMgaW4gdGhlIHJwYXJ0LmNvbnRyb2woKS1mdW5jdGlvbiB3aWxsIGJlIGNoYW5nZWQsIGFuZCBzb21lIHdlaWdodHMgd2lsbCBiZSBpbmNsdWRlZCB1c2luZyB0aGUgd2VpZ2h0cyBhcmd1bWVudCBpbiBycGFydCgpLiBUaGUgdmVjdG9yIGNhc2Vfd2VpZ2h0cyBoYXMgYmVlbiBjb25zdHJ1Y3RlZCBmb3IgeW91IGFuZCBpcyBsb2FkZWQgaW4geW91ciB3b3Jrc3BhY2UuIFRoaXMgdmVjdG9yIGNvbnRhaW5zIHdlaWdodHMgb2YgMSBmb3IgdGhlIG5vbi1kZWZhdWx0cyBpbiB0aGUgdHJhaW5pbmcgc2V0LCBhbmQgd2VpZ2h0cyBvZiAzIGZvciBkZWZhdWx0cyBpbiB0aGUgdHJhaW5pbmcgc2V0cy4gQnkgc3BlY2lmeWluZyBoaWdoZXIgd2VpZ2h0cyBmb3IgZGVmYXVsdCwgdGhlIG1vZGVsIHdpbGwgYXNzaWduIGhpZ2hlciBpbXBvcnRhbmNlIHRvIGNsYXNzaWZ5aW5nIGRlZmF1bHRzIGNvcnJlY3RseS4NCmBgYHtyfQ0KY2FzZV93ZWlnaHRzIDwtIGlmZWxzZSh0cmFpbmluZ19zZXQkbG9hbl9zdGF0dXMgPT0gMCwgMSwgMykNCmBgYA0KDQpgYGB7cn0NCiMgc2V0IGEgc2VlZCBhbmQgcnVuIHRoZSBjb2RlIHRvIG9idGFpbiBhIHRyZWUgdXNpbmcgd2VpZ2h0cywgbWluc3BsaXQgYW5kIG1pbmJ1Y2tldA0Kc2V0LnNlZWQoMzQ1KQ0KdHJlZV93ZWlnaHRzIDwtIHJwYXJ0KGxvYW5fc3RhdHVzIH4gLiwgbWV0aG9kID0gImNsYXNzIiwNCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5pbmdfc2V0LA0KICAgICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSBjYXNlX3dlaWdodHMsIA0KICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKG1pbnNwbGl0ID0gNSwgbWluYnVja2V0ID0gMiwgY3AgPSAwLjAwMSkpDQoNCiMgUGxvdCB0aGUgY3Jvc3MtdmFsaWRhdGVkIGVycm9yIHJhdGUgZm9yIGEgY2hhbmdpbmcgY3ANCnBsb3RjcCh0cmVlX3dlaWdodHMpDQoNCiMgQ3JlYXRlIGFuIGluZGV4IGZvciBvZiB0aGUgcm93IHdpdGggdGhlIG1pbmltdW0geGVycm9yDQppbmRleCA8LSB3aGljaC5taW4odHJlZV93ZWlnaHRzJGNwWyAsICJ4ZXJyb3IiXSkNCg0KIyBDcmVhdGUgdHJlZV9taW4NCnRyZWVfbWluIDwtIHRyZWVfd2VpZ2h0cyRjcFtpbmRleCwgIkNQIl0NCg0KIyBQcnVuZSB0aGUgdHJlZSB1c2luZyB0cmVlX21pbg0KcHRyZWVfd2VpZ2h0cyA8LSBwcnVuZSh0cmVlX3dlaWdodHMsIGNwID0gdHJlZV9taW4pDQoNCiMgUGxvdCB0aGUgcHJ1bmVkIHRyZWUgdXNpbmcgdGhlIHJwYXJ0LnBsb3QoKS1wYWNrYWdlDQpwcnAocHRyZWVfd2VpZ2h0cywgZXh0cmEgPSAxKQ0KYGBgDQojIyMgQ29uZnVzaW9uIG1hdHJpY2VzIGFuZCBhY2N1cmFjeSBvZiBvdXIgZmluYWwgdHJlZXMNCg0KT3ZlciB0aGUgcGFzdCBmZXcgZXhlcmNpc2VzLCB5b3UgaGF2ZSBjb25zdHJ1Y3RlZCBxdWl0ZSBhIGZldyBwcnVuZWQgZGVjaXNpb24gdHJlZXMsIHdpdGggZm91ciBpbiB0b3RhbC4gQXMgeW91IGNhbiBzZWUsIHRoZSBldmVudHVhbCBudW1iZXIgb2Ygc3BsaXRzIHZhcmllcyBxdWl0ZSBhIGJpdCBmcm9tIG9uZSB0cmVlIHRvIGFub3RoZXI6DQoNCiAgICBwdHJlZV91bmRlcnNhbXBsZSAgIyA3IHNwbGl0cw0KICAgIHB0cmVlX3ByaW9yICAjIDkgc3BsaXRzDQogICAgcHRyZWVfbG9zc19tYXRyaXggICMgMjQgc3BsaXRzDQogICAgcHRyZWVfd2VpZ2h0cyAgIyA2IHNwbGl0cw0KDQpOb3cgaXQgaXMgaW1wb3J0YW50IHRvIGtub3cgd2hpY2ggdHJlZSBwZXJmb3JtcyBiZXN0IGluIHRlcm1zIG9mIGFjY3VyYWN5LiBJbiBvcmRlciB0byBnZXQgdGhlIGFjY3VyYWN5LCB5b3Ugd2lsbCBzdGFydCBvZmYgYnkgbWFraW5nIHByZWRpY3Rpb25zIHVzaW5nIHRoZSB0ZXN0IHNldCwgYW5kIGNvbnN0cnVjdCB0aGUgY29uZnVzaW9uIG1hdHJpeCBmb3IgZWFjaCBvZiB0aGVzZSB0cmVlcy4gWW91IHdpbGwgYWRkIHRoZSBhcmd1bWVudCB0eXBlID0gImNsYXNzIiB3aGVuIGRvaW5nIHRoZXNlIHByZWRpY3Rpb25zLiBCeSBkb2luZyB0aGlzIHRoZXJlIGlzIG5vIG5lZWQgdG8gc2V0IGEgY3V0LW9mZi4NCk5ldmVydGhlbGVzcywgaXQgaXMgaW1wb3J0YW50IHRvIGJlIGF3YXJlIG9mIHRoZSBmYWN0IHRoYXQgbm90IG9ubHkgdGhlIGFjY3VyYWN5IGlzIGltcG9ydGFudCwgYnV0IGFsc28gdGhlIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eS4gQWRkaXRpb25hbGx5LCBwcmVkaWN0aW5nIHByb2JhYmlsaXRpZXMgaW5zdGVhZCBvZiBiaW5hcnkgdmFsdWVzICgwIG9yIDEpIGhhcyB0aGUgYWR2YW50YWdlIHRoYXQgdGhlIGN1dC1vZmYgY2FuIGJlIG1vdmVkIGFsb25nLiBUaGVuIGFnYWluLCB0aGUgZGlmZmljdWx0eSBoZXJlIGlzIHRoZSBjaG9pY2Ugb2YgdGhlIGN1dC1vZmYuDQoNCiRDbGFzc2lmaWNhdGlvbiBhY2N1cmFjeSA9IFxmcmFjeyhUUCtUTil9eyhUUCtGUCtUTitGTil9JA0KDQpgYGB7cn0NCnB0cmVlX3VuZGVyc2FtcGxlIDwtIHRyZWVfdW5kZXJzYW1wbGUNCnB0cmVlX3ByaW9yIDwtIHRyZWVfcHJpb3IgDQpwdHJlZV9sb3NzX21hdHJpeCA8LSB0cmVlX2xvc3NfbWF0cml4DQpwdHJlZV93ZWlnaHRzIDwtIHRyZWVfd2VpZ2h0cw0KIyBNYWtlIHByZWRpY3Rpb25zIGZvciBlYWNoIG9mIHRoZSBwcnVuZWQgdHJlZXMgdXNpbmcgdGhlIHRlc3Qgc2V0Lg0KcHJlZF91bmRlcnNhbXBsZSA8LSBwcmVkaWN0KHB0cmVlX3VuZGVyc2FtcGxlLCBuZXdkYXRhID0gdGVzdF9zZXQsICB0eXBlID0gImNsYXNzIikNCnByZWRfcHJpb3IgPC0gcHJlZGljdChwdHJlZV9wcmlvciwgbmV3ZGF0YSA9IHRlc3Rfc2V0LCAgdHlwZSA9ICJjbGFzcyIpDQpwcmVkX2xvc3NfbWF0cml4IDwtIHByZWRpY3QocHRyZWVfbG9zc19tYXRyaXgsIG5ld2RhdGEgPSB0ZXN0X3NldCwgIHR5cGUgPSAiY2xhc3MiKQ0KcHJlZF93ZWlnaHRzIDwtIHByZWRpY3QocHRyZWVfd2VpZ2h0cywgbmV3ZGF0YSA9IHRlc3Rfc2V0LCAgdHlwZSA9ICJjbGFzcyIpDQoNCiMgY29uc3RydWN0IGNvbmZ1c2lvbiBtYXRyaWNlcyB1c2luZyB0aGUgcHJlZGljdGlvbnMuDQpjb25mbWF0X3VuZGVyc2FtcGxlIDwtIHRhYmxlKHRlc3Rfc2V0JGxvYW5fc3RhdHVzLCBwcmVkX3VuZGVyc2FtcGxlKQ0KY29uZm1hdF9wcmlvciA8LSB0YWJsZSh0ZXN0X3NldCRsb2FuX3N0YXR1cywgcHJlZF9wcmlvcikNCmNvbmZtYXRfbG9zc19tYXRyaXggPC0gdGFibGUodGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRfbG9zc19tYXRyaXgpDQpjb25mbWF0X3dlaWdodHMgPC0gdGFibGUodGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRfd2VpZ2h0cykNCg0KIyBDb21wdXRlIHRoZSBhY2N1cmFjaWVzDQphY2NfdW5kZXJzYW1wbGUgPC0gc3VtKGRpYWcoY29uZm1hdF91bmRlcnNhbXBsZSkpIC8gbnJvdyh0ZXN0X3NldCkNCmFjY191bmRlcnNhbXBsZQ0KYWNjX3ByaW9yIDwtIHN1bShkaWFnKGNvbmZtYXRfcHJpb3IpKSAvIG5yb3codGVzdF9zZXQpDQphY2NfcHJpb3INCmFjY19sb3NzX21hdHJpeCA8LSBzdW0oZGlhZyhjb25mbWF0X2xvc3NfbWF0cml4KSkgLyBucm93KHRlc3Rfc2V0KQ0KYWNjX2xvc3NfbWF0cml4DQphY2Nfd2VpZ2h0cyA8LSBzdW0oZGlhZyhjb25mbWF0X3dlaWdodHMpKSAvIG5yb3codGVzdF9zZXQpDQphY2Nfd2VpZ2h0cw0KYGBgDQojIEV2YWx1YXRpbmcgYSBjcmVkaXQgcmlzayBtb2RlbA0KDQojIyBGaW5kaW5nIHRoZSByaWdodCBjdXQtb2ZmOiB0aGUgc3RyYXRlZ3kgY3VydmUNCg0KSW4gdGhlIHZpZGVvLCB5b3UgbGVhcm5lZCBob3cgdG8gY29tcHV0ZSB0aGUgYmFkIHJhdGUgKG9yLCB0aGUgcGVyY2VudGFnZSBvZiBkZWZhdWx0cykgaW4gdGhlIGxvYW4gcG9ydGZvbGlvIG9mIGEgYmFuayB3aGVuIGdpdmVuOg0KDQotCWEgc3BlY2lmaWMgbW9kZWwNCi0JdGhlIGFjY2VwdGFuY2UgcmF0ZQ0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBjb21wdXRlIHRoZSBiYWQgcmF0ZSB0aGF0IGEgYmFuayBjYW4gZXhwZWN0IHdoZW4gdXNpbmcgdGhlIHBydW5lZCB0cmVlIHB0cmVlX3ByaW9yIHRoYXQgeW91IGZpdHRlZCBiZWZvcmUsIGFuZCBhbiBhY2NlcHRhbmNlIHJhdGUgb2YgODAlLg0KDQpuIHRoZSBzY3JpcHQsIHlvdSBhcmUgcHJvdmlkZWQgdGhlIGNvZGUgdG8gbWFrZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHByb2JhYmlsaXR5IG9mIGRlZmF1bHQgdXNpbmcgdGhlIHBydW5lZCB0cmVlIGFuZCB0ZXN0X3NldC4gUmVtZW1iZXIgdGhhdCBpZiB5b3UgdXNlIHRoZSBwcmVkaWN0KCkgZnVuY3Rpb24gZm9yIGEgdHJlZSwgdGhlIHByb2JhYmlsaXR5IG9mIGRlZmF1bHQgY2FuIGJlIGZvdW5kIGluIHRoZSBzZWNvbmQgY29sdW1uLiBUaGVyZWZvcmUgWywyXSB3YXMgcGFzdGVkIHRvIHRoZSBwcmVkaWN0KCkgZnVuY3Rpb24uDQoNCk9idGFpbiB0aGUgY3V0LW9mZiB0aGF0IGxlYWRzIHRvIGFuIGFjY2VwdGFuY2UgcmF0ZSBvZiA4MCUsIHVzaW5nIHByb2JfZGVmYXVsdF9wcmlvci4gWW91IGNhbiB1c2UgdGhlIHF1YW50aWxlKCktIGZ1bmN0aW9uIHRvIGRvIHRoaXMsIHNldHRpbmcgdGhlIHNlY29uZCBhcmd1bWVudCB0byAwLjguIEFzc2lnbiB0aGUgbmFtZSBjdXRvZmZfcHJpb3IuDQpUaGUgY29kZSB0byBvYnRhaW4gdGhlIGFjdHVhbCBiaW5hcnkgZGVmYXVsdCBwcmVkaWN0aW9ucyAoMCBvciAxKSBpcyBwcm92aWRlZC4gaWZlbHNlKCkgaGVyZS4gTmFtZSB0aGUgb2JqZWN0IGJpbl9wcmVkX3ByaW9yXzgwLg0KDQpUaGUgY29kZSB0byBzZWxlY3QgdGhlIGRlZmF1bHQgaW5kaWNhdG9ycyBvZiB0ZXN0X3NldCBmb3IgdGhlIGFjY2VwdGVkIGxvYW5zIGFjb3JkaW5nIHRvIGEgODAlIGFjY2VwdGFuY2UgcmF0ZSBpcyBwcm92aWRlZC4NCg0KQ29tcHV0ZSB0aGUgcGVyY2VudGFnZSBvZiBkZWZhdWx0cyAob3IgdGhlICJiYWQgcmF0ZSIpIGZvciB0aGUgYWNjZXB0ZWQgbG9hbnMuIFRoaXMgaXMgdGhlIG51bWJlciBvZiBvY2N1cmVuY2VzIG9mIDEgaW4gYWNjZXB0ZWRfc3RhdHVzX3ByaW9yXzgwLCBkaXZpZGVkIGJ5IHRoZSB0b3RhbCBudW1iZXIgb2YgaW5zdGFuY2VzIGluIHRoaXMgdmVjdG9yLiBQcmludCB0aGUgc29sdXRpb24gdG8geW91ciBSLWNvbnNvbGUuDQpgYGB7cn0NCiMgTWFrZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHByb2JhYmlsaXR5IG9mIGRlZmF1bHQgdXNpbmcgdGhlIHBydW5lZCB0cmVlIGFuZCB0aGUgdGVzdCBzZXQuDQpwcm9iX2RlZmF1bHRfcHJpb3IgPC0gcHJlZGljdChwdHJlZV9wcmlvciwgbmV3ZGF0YSA9IHRlc3Rfc2V0KVsgLDJdDQoNCiMgT2J0YWluIHRoZSBjdXRvZmYgZm9yIGFjY2VwdGFuY2UgcmF0ZSA4MCUNCmN1dG9mZl9wcmlvciA8LSBxdWFudGlsZShwcm9iX2RlZmF1bHRfcHJpb3IsIDAuOCkgIA0KDQojIE9idGFpbiB0aGUgYmluYXJ5IHByZWRpY3Rpb25zLg0KYmluX3ByZWRfcHJpb3JfODAgPC0gaWZlbHNlKHByb2JfZGVmYXVsdF9wcmlvciA+IGN1dG9mZl9wcmlvciwgMSwgMCkNCg0KIyBPYnRhaW4gdGhlIGFjdHVhbCBkZWZhdWx0IHN0YXR1cyBmb3IgdGhlIGFjY2VwdGVkIGxvYW5zDQphY2NlcHRlZF9zdGF0dXNfcHJpb3JfODAgPC0gdGVzdF9zZXQkbG9hbl9zdGF0dXNbYmluX3ByZWRfcHJpb3JfODAgPT0gMF0NCg0KIyBPYnRhaW4gdGhlIGJhZCByYXRlIGZvciB0aGUgYWNjZXB0ZWQgbG9hbnMNCnN1bShhY2NlcHRlZF9zdGF0dXNfcHJpb3JfODApIC8gbGVuZ3RoKGFjY2VwdGVkX3N0YXR1c19wcmlvcl84MCkNCmBgYA0KIyMjIFRoZSBzdHJhdGVneSB0YWJsZSBhbmQgc3RyYXRlZ3kgY3VydmUNCg0KUmVwZWF0aW5nIHRoZSBjYWxjdWxhdGlvbnMgeW91IGRpZCBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UgZm9yIHNldmVyYWwgYWNjZXB0YW5jZSByYXRlcywgeW91IGNhbiBvYnRhaW4gYSBzdHJhdGVneSB0YWJsZS4gVGhpcyB0YWJsZSBjYW4gYmUgYSB1c2VmdWwgdG9vbCBmb3IgYmFua3MsIGFzIHRoZXkgY2FuIGdpdmUgdGhlbSBhIGJldHRlciBpbnNpZ2h0IHRvIGRlZmluZSBhbiBhY2NlcHRhbmNlIHN0cmF0ZWd5Lg0KDQpZb3Uga25vdyBob3cgdG8gY29tcHV0ZSBhIGJhZCByYXRlIGZvciBhIGNlcnRhaW4gYWNjZXB0YW5jZSByYXRlIGJ5IG5vdywgc28gdGhlIGZ1bmN0aW9uIHN0cmF0ZWd5X2Jhbmsgd2FzIHdyaXR0ZW4gYW5kIGxvYWRlZCBpbnRvIHlvdXIgd29ya3NwYWNlIHRvIHNwZWVkIHRoaW5ncyB1cC4gVGhpcyBmdW5jdGlvbiBjb21wdXRlcyB0aGUgY3V0LW9mZiBhbmQgYmFkIHJhdGUgZm9yIHRoZSBhY2NlcHRhbmNlIHJhdGVzIHRoYXQgYXJlIG11bHRpcGxlcyBvZiA1JSAoMCUsIDUlLCAxMCUsIOKApikuDQpgYGB7cn0NCnN0cmF0ZWd5X2JhbmsgPC0gZnVuY3Rpb24ocHJvYl9vZl9kZWYpew0KICAgIGN1dG9mZiA9IHJlcChOQSwgMjEpDQogICAgYmFkX3JhdGUgPSByZXAoTkEsIDIxKQ0KICAgIGFjY2VwdF9yYXRlID0gc2VxKDEsIDAsIGJ5ID0gLTAuMDUpDQogICAgZm9yIChpIGluIDE6MjEpIHsNCiAgICAgICAgY3V0b2ZmW2ldID0gcXVhbnRpbGUocHJvYl9vZl9kZWYsIGFjY2VwdF9yYXRlW2ldKQ0KICAgICAgICBwcmVkX2kgPSBpZmVsc2UocHJvYl9vZl9kZWYgPiBjdXRvZmZbaV0sIDEsIDApDQogICAgICAgIHByZWRfYXNfZ29vZCA9IHRlc3Rfc2V0JGxvYW5fc3RhdHVzW3ByZWRfaSA9PSAwXQ0KICAgICAgICBiYWRfcmF0ZVtpXSA9IHN1bShwcmVkX2FzX2dvb2QpL2xlbmd0aChwcmVkX2FzX2dvb2QpDQogICAgfQ0KICAgICAgICB0YWJsZSA9IGNiaW5kKGFjY2VwdF9yYXRlLCBjdXRvZmYgPSByb3VuZChjdXRvZmYsIDQpLCBiYWRfcmF0ZSA9IHJvdW5kKGJhZF9yYXRlLCANCiAgICAgICAgNCkpDQogICAgcmV0dXJuKGxpc3QodGFibGUgPSB0YWJsZSwgYmFkX3JhdGUgPSBiYWRfcmF0ZSwgYWNjZXB0X3JhdGUgPSBhY2NlcHRfcmF0ZSwgDQogICAgICAgIGN1dG9mZiA9IGN1dG9mZikpDQp9DQpgYGANCg0KLSBIYXZlIGEgbG9vayBhdCB0aGUgZnVuY3Rpb24gc3RyYXRlZ3lfYmFuay4NCi0gVGhlIHZlY3RvciBwcmVkaWN0aW9uc19jbG9nbG9nIGNvbnRhaW5zIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIG9mIGRlZmF1bHQgdXNpbmcgdGhlIGNsb2dsb2cgbW9kZWwgeW91IHVzZWQgaW4gY2hhcHRlciAyLCB0aGUgdmVjdG9yIHByZWRpY3Rpb25zX2xvc3NfbWF0cml4Y29udGFpbnMgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIG9mIGRlZmF1bHQgdXNpbmcgdGhlIHBydW5lZCB0cmVlIGluY2x1ZGluZyBhIGxvc3MgbWF0cml4IChwcmV2aW91c2x5IGNvbnN0cnVjdGVkIGluIGNoYXB0ZXIgMykuIEFwcGx5IGZ1bmN0aW9uIHN0cmF0ZWd5X2JhbmsgdG8gZWFjaCBvZiB0aGUgcHJlZGljdGlvbi12ZWN0b3JzLCBhc3NpZ24gdGhlIG5hbWUgc3RyYXRlZ3lfY2xvZ2xvZyBhbmQgc3RyYXRlZ3lfbG9zc19tYXRyaXggcmVzcGVjdGl2ZWx5Lg0KLSBUaGUgc3RyYXRlZ3kgdGFibGVzIGNhbiBiZSBvYnRhaW5lZCB1c2luZyB0aGUgb2JqZWN0IG5hbWVzIGluIGNvbWJpbmF0aW9uIHdpdGggJHRhYmxlLg0KLSBUaGUgc3RyYXRlZ3kgY3VydmVzIGhhdmUgYmVlbiBwbG90dGVkIGZvciB5b3UuIFRoZSBzdHJhdGVneSBjdXJ2ZSBvZiB0aGUgdHJlZSBtb2RlbCBzaG93cyBwcmV0dHkgc3RyYW5nZSBiZWhhdmlvci4gQmVjYXVzZSBvZiB0aGUgc3RydWN0dXJlIG9mIGNsYXNzaWZpY2F0aW9uIHRyZWVzLCB5b3UgbWlnaHQgaGF2ZSBhIGJpZ2dlciBjaGFuY2UgZm9yIHdlaXJkICJqdW1wcyIgaGVyZS4gQWRkaXRpb25hbGx5LCB0aGUgdHJlZSB3aXRoIGEgbG9zcyBtYXRyaXggd2FzIGEgdmVyeSBiaWcgb25lLCBzbyB0aGlzIG1pZ2h0IGJlIHRoZSByZXN1bHQgb2Ygb3ZlcmZpdHRpbmchDQpgYGB7cn0NCiN3aXRob3VndGggdHlwZSA9ICJjbGFzcyIgdG8gZ2V0IHByb2JhYmlsaXRpZXMNCnByZWRpY3Rpb25zX2xvc3NfbWF0cml4IDwtIHByZWRpY3QocHRyZWVfbG9zc19tYXRyaXgsIG5ld2RhdGEgPSB0ZXN0X3NldClbLDJdDQpgYGANCg0KYGBge3J9DQojIEFwcGx5IHRoZSBmdW5jdGlvbiBzdHJhdGVneV9iYW5rIHRvIGJvdGggcHJlZGljdGlvbnNfY2xvZ2xvZyBhbmQgcHJlZGljdGlvbnNfbG9zc19tYXRyaXgNCnN0cmF0ZWd5X2Nsb2dsb2cgPC0gc3RyYXRlZ3lfYmFuayhwcmVkaWN0aW9uc19jbG9nbG9nKQ0Kc3RyYXRlZ3lfbG9zc19tYXRyaXggPC0gc3RyYXRlZ3lfYmFuayhwcmVkaWN0aW9uc19sb3NzX21hdHJpeCkNCg0KIyBPYnRhaW4gdGhlIHN0cmF0ZWd5IHRhYmxlcyBmb3IgYm90aCBwcmVkaWN0aW9uLXZlY3RvcnMNCnN0cmF0ZWd5X2Nsb2dsb2ckdGFibGUNCnN0cmF0ZWd5X2xvc3NfbWF0cml4JHRhYmxlDQoNCiMgUGxvdCB0aGUgc3RyYXRlZ3kgZnVuY3Rpb25zDQpwYXIobWZyb3cgPSBjKDEsMikpDQpwbG90KHN0cmF0ZWd5X2Nsb2dsb2ckYWNjZXB0X3JhdGUsIHN0cmF0ZWd5X2Nsb2dsb2ckYmFkX3JhdGUsIA0KICAgICB0eXBlID0gImwiLCB4bGFiID0gIkFjY2VwdGFuY2UgcmF0ZSIsIHlsYWIgPSAiQmFkIHJhdGUiLCANCiAgICAgbHdkID0gMiwgbWFpbiA9ICJsb2dpc3RpYyByZWdyZXNzaW9uIikNCg0KcGxvdChzdHJhdGVneV9sb3NzX21hdHJpeCRhY2NlcHRfcmF0ZSwgc3RyYXRlZ3lfbG9zc19tYXRyaXgkYmFkX3JhdGUsIA0KICAgICB0eXBlID0gImwiLCB4bGFiID0gIkFjY2VwdGFuY2UgcmF0ZSIsIA0KICAgICB5bGFiID0gIkJhZCByYXRlIiwgbHdkID0gMiwgbWFpbiA9ICJ0cmVlIikNCmBgYA0KIyMgVGhlIFJPQy1jdXJ2ZQ0KDQojIyMgUk9DLWN1cnZlcyBmb3IgY29tcGFyaXNvbiBvZiBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscw0KDQpST0MtY3VydmVzIGNhbiBlYXNpbHkgYmUgY3JlYXRlZCB1c2luZyB0aGUgcFJPQy1wYWNrYWdlIGluIFIuIExldCdzIGhhdmUgYSBsb29rIGlmIHRoZXJlIGlzIGEgYmlnIGRpZmZlcmVuY2UgYmV0d2VlbiBST0MtY3VydmVzIGZvciB0aGUgZm91ciBsb2dpc3RpYyByZWdyZXNzaW9uLW1vZGVscyBwcmV2aW91c2x5IHVzZWQgdGhyb3VnaG91dCB0aGlzIGNvdXJzZS4gQSBzbWFsbCBoZWFkcyB1cDoNCg0KKiBwcmVkaWN0aW9uc19sb2dpdCBjb250YWlucyBwcm9iYWJpbGl0eSBvZiBkZWZhdWx0IChQRCkgcHJlZGljdGlvbnMgdXNpbmcgdGhlIGRlZmF1bHQgbG9naXQgbGluayBhbmQgY29udGFpbmluZyB2YXJpYWJsZXMgYWdlLCBlbXBfY2F0LCBpcl9jYXQgYW5kIGxvYW5fYW1udC4NCiogcHJlZGljdGlvbnNfcHJvYml0IGNvbnRhaW5zIFBELXByZWRpY3Rpb25zIHVzaW5nIHRoZSBwcm9iaXQgYW5kIGNvbnRhaW5pbmcgdmFyaWFibGVzIGFnZSwgZW1wX2NhdCwgaXJfY2F0IGFuZCBsb2FuX2FtbnQuDQoqIHByZWRpY3Rpb25zX2Nsb2dsb2cgY29udGFpbnMgUEQtcHJlZGljdGlvbnMgdXNpbmcgdGhlIGNsb2dsb2cgbGluayBhbmQgY29udGFpbmluZyB2YXJpYWJsZXMgYWdlLCBlbXBfY2F0LCBpcl9jYXQgYW5kIGxvYW5fYW1udC4NCiogcHJlZGljdGlvbnNfYWxsX2Z1bGwgY29udGFpbnMgUEQtcHJlZGljdGlvbnMgdXNpbmcgdGhlIGRlZmF1bHQgbG9naXQgbGluayBhbmQgY29udGFpbmluZyBhbGwgc2V2ZW4gdmFyaWFibGVzIGluIHRoZSBkYXRhIHNldC4NCg0KTG9hZCB0aGUgcFJPQy1wYWNrYWdlIGluIHlvdXIgUi1jb25zb2xlLg0KQ29uc3RydWN0IHRoZSBST0Mtb2JqZWN0cyBmb3IgdGhlIGZvdXIgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgdXNpbmcgZnVuY3Rpb24gcm9jKHJlc3BvbnNlLCBwcmVkaWN0b3IpLiBSZW1lbWJlciB0aGF0IHRoZSByZXNwb25zZSBpcyB0aGUgbG9hbiBzdGF0dXMgaW5kaWNhdG9yIGluIHRoZSB0ZXN0X3NldCwgd2hpY2ggY2FuIGJlIG9idGFpbmVkIHRocm91Z2ggdGVzdF9zZXQkbG9hbl9zdGF0dXMuDQpVc2UgdGhlIHByZXZpb3VzbHkgY3JlYXRlZCBvYmplY3RzIHRvIGNvbnN0cnVjdCBST0MtY3VydmVzLiBUbyBkcmF3IHRoZW0gYWxsIG9uIG9uZSBwbG90LCB1c2UgcGxvdCgpIGZvciB0aGUgZmlyc3QgUk9DLWN1cnZlIGRyYXduIChmb3IgUk9DX2xvZ2l0KSwgYW5kIHVzZSBbbGluZXMoKV0oaHR0cDovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvZ3JhcGhpY3MvZnVuY3Rpb25zL2xpbmVzIHRvIGFkZCB0aGUgUk9DLWN1cnZlcykgZm9yIHRoZSBvdGhlciB0aHJlZSBtb2RlbHMgdG8gdGhlIHNhbWUgcGxvdC4NClVzZSB0aGUgY29sLWFyZ3VtZW50IHRvIGNoYW5nZSB0aGUgY29sb3Igb2YgdGhlIGN1cnZlIG9mIFJPQ19wcm9iaXQgdG8gImJsdWUiLCBST0NfY2xvZ2xvZyB0byAicmVkIiBhbmQgUk9DX2FsbF9mdWxsIHRvICJncmVlbiIuIE5vdGUgdGhhdCwgaW4gY29udHJhc3Qgd2l0aCB3aGF0IGhhcyBiZWVuIGRpc2N1c3NlZCBpbiB0aGUgdmlkZW8sIHRoZSB4LWF4aXMgbGFiZWwgaXMgU3BlY2lmaWNpdHkgYW5kIG5vdCAiMS1TcGVjaWZpY2l0eSIsIHJlc3VsdGluZyBpbiBhbiBheGlzIHRoYXQgZ29lcyBmcm9tIDEgb24gdGhlIGxlZnQtaGFuZCBzaWRlIHRvIDAgb24gdGhlIHJpZ2h0LWhhbmQgc2lkZS4NCkl0IHNlZW1zIHRoYXQgdGhlIGxpbmsgZnVuY3Rpb24gZG9lcyBub3QgaGF2ZSBhIGJpZyBpbXBhY3Qgb24gdGhlIFJPQyBoZXJlLCBhbmQgdGhlIG1haW4gdHJpZ2dlciBvZiBhIGJldHRlciBST0MgaXMgdGhlIGluY2x1c2lvbiBvZiBtb3JlIHZhcmlhYmxlcyBpbiBhIG1vZGVsLiBUbyBnZXQgYW4gZXhhY3QgaWRlYSBvZiB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIFJPQy1jdXJ2ZXMsIGhhdmUgYSBsb29rIGF0IHRoZSBBVUMncywgdXNpbmcgZnVuY3Rpb24gYXVjKCkuDQoNCmBgYHtyfQ0KIyBMb2FkIHRoZSBwUk9DLXBhY2thZ2UNCmxpYnJhcnkocFJPQykNCg0KIyBDb25zdHJ1Y3QgdGhlIG9iamVjdHMgY29udGFpbmluZyBST0MtaW5mb3JtYXRpb24NClJPQ19sb2dpdCA8LSByb2ModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRpY3Rpb25zX2xvZ2l0KQ0KUk9DX3Byb2JpdCA8LSByb2ModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRpY3Rpb25zX3Byb2JpdCkNClJPQ19jbG9nbG9nIDwtIHJvYyh0ZXN0X3NldCRsb2FuX3N0YXR1cywgcHJlZGljdGlvbnNfY2xvZ2xvZykNClJPQ19hbGxfZnVsbCA8LSByb2ModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRpY3Rpb25zX2FsbF9mdWxsKQ0KDQojIERyYXcgYWxsIFJPQ3Mgb24gb25lIHBsb3QNCnBsb3QoUk9DX2xvZ2l0KQ0KbGluZXMoUk9DX3Byb2JpdCwgY29sPSJibHVlIikNCmxpbmVzKFJPQ19jbG9nbG9nLCBjb2w9InJlZCIpDQpsaW5lcyhST0NfYWxsX2Z1bGwsIGNvbD0iZ3JlZW4iKQ0KDQojIENvbXB1dGUgdGhlIEFVQ3MNCmF1YyhST0NfbG9naXQpDQphdWMoUk9DX3Byb2JpdCkNCmF1YyhST0NfY2xvZ2xvZykNCmF1YyhST0NfYWxsX2Z1bGwpDQpgYGANCiMjIyBST0MtY3VydmVzIGZvciBjb21wYXJpc29uIG9mIHRyZWUtYmFzZWQgbW9kZWxzDQpJdCdzIHRpbWUgZm9yIHlvdSB0byByZXBlYXQgdGhlIHByZXZpb3VzIGV4ZXJjaXNlcywgbm93IGNvbXBhcmluZyB0aGUgdHJlZS1iYXNlZCBtb2RlbHMuIFRoZSBwUk9DKCkgaXMgbm93IGxvYWRlZCBpbiB5b3VyIHdvcmtzcGFjZS4gVGhlIFBELXByZWRpY3Rpb25zIGZvciB0cmVlLWJhc2VkIG1ldGhvZHMgYXJlIHN0b3JlZCBpbiB0aGUgb2JqZWN0cw0KDQpwcmVkaWN0aW9uc191bmRlcnNhbXBsZQ0KcHJlZGljdGlvbnNfcHJpb3INCnByZWRpY3Rpb25zX2xvc3NfbWF0cml4DQpwcmVkaWN0aW9uc193ZWlnaHRzDQpgYGB7cn0NCnByZWRpY3Rpb25zX3VuZGVyc2FtcGxlIDwtIHByZWRpY3QodHJlZV91bmRlcnNhbXBsZSwgbmV3ZGF0YSA9IHRlc3Rfc2V0KVssMl0NCnByZWRpY3Rpb25zX3ByaW9yIDwtIHByZWRpY3QodHJlZV9wcmlvciwgbmV3ZGF0YSA9IHRlc3Rfc2V0KVssMl0NCnByZWRpY3Rpb25zX2xvc3NfbWF0cml4IDwtIHByZWRpY3QocHRyZWVfbG9zc19tYXRyaXgsIG5ld2RhdGEgPSB0ZXN0X3NldClbLDJdDQpwcmVkaWN0aW9uc193ZWlnaHRzIDwtIHByZWRpY3QodHJlZV93ZWlnaHRzICwgbmV3ZGF0YSA9IHRlc3Rfc2V0KVssMl0NCmBgYA0KDQoNCmBgYHtyfQ0KIyBDb25zdHJ1Y3QgdGhlIG9iamVjdHMgY29udGFpbmluZyBST0MtaW5mb3JtYXRpb24NClJPQ191bmRlcnNhbXBsZSA8LSByb2ModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRpY3Rpb25zX3VuZGVyc2FtcGxlKQ0KUk9DX3ByaW9yIDwtIHJvYyh0ZXN0X3NldCRsb2FuX3N0YXR1cywgcHJlZGljdGlvbnNfcHJpb3IpDQpST0NfbG9zc19tYXRyaXggPC0gcm9jKHRlc3Rfc2V0JGxvYW5fc3RhdHVzLCBwcmVkaWN0aW9uc19sb3NzX21hdHJpeCkNClJPQ193ZWlnaHRzIDwtIHJvYyh0ZXN0X3NldCRsb2FuX3N0YXR1cywgcHJlZGljdGlvbnNfd2VpZ2h0cykNCg0KIyBEcmF3IHRoZSBST0MtY3VydmVzIGluIG9uZSBwbG90DQpwbG90KFJPQ191bmRlcnNhbXBsZSkNCmxpbmVzKFJPQ19wcmlvciwgY29sPSJibHVlIikNCmxpbmVzKFJPQ19sb3NzX21hdHJpeCwgY29sPSJyZWQiKQ0KbGluZXMoUk9DX3dlaWdodHMsIGNvbD0iZ3JlZW4iKQ0KDQojIENvbXB1dGUgdGhlIEFVQ3MNCmF1YyhST0NfdW5kZXJzYW1wbGUpDQphdWMoUk9DX3ByaW9yKQ0KYXVjKFJPQ19sb3NzX21hdHJpeCkNCmF1YyhST0Nfd2VpZ2h0cykNCmBgYA0KIyMgSW5wdXQgc2VsZWN0aW9uIGJhc2VkIG9uIHRoZSBBVUMNCg0KDQojIyMgQW5vdGhlciByb3VuZCBvZiBwcnVuaW5nIGJhc2VkIG9uIEFVQw0KSW4gdGhlIHZpZGVvIHlvdSBzYXcgaG93IHRoZSAiZnVsbCIgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB3aXRoIGEgbG9naXQgbGluayB3YXMgYmVpbmcgcHJ1bmVkIGJhc2VkIG9uIHRoZSBBVUMuIFlvdSBzYXcgaG93IHRoZSB2YXJpYWJsZSBob21lX293bmVyc2hpcCB3YXMgZGVsZXRlZCBmcm9tIHRoZSBtb2RlbCwgYXMgaXQgaW1wcm92ZWQgdGhlIG92ZXJhbGwgQVVDLiBBZnRlciByZXBlYXRpbmcgdGhpcyBwcm9jZXNzIGZvciB0d28gYWRkaXRpb25hbCByb3VuZHMsIHRoZSB2YXJpYWJsZXMgYWdlIGFuZCBpcl9jYXQgd2VyZSBkZWxldGVkLCBsZWFkaW5nIHRvIHRoZSBtb2RlbDoNCg0KICAgIGxvZ18zX3JlbW92ZV9pciA8LSBnbG0obG9hbl9zdGF0dXMgfiBsb2FuX2FtbnQgKyBncmFkZSArIGFubnVhbF9pbmMgKyBlbXBfY2F0LCBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IHRyYWluaW5nX3NldCkNCg0Kd2l0aCBhbiBBVUMgb2YgMC42NTQ1LiBOb3csIGl0J3MgeW91ciB0dXJuIHRvIHNlZSB3aGV0aGVyIHRoZSBBVUMgY2FuIHN0aWxsIGJlIGltcHJvdmVkIGJ5IGRlbGV0aW5nIGFub3RoZXIgdmFyaWFibGUgZnJvbSB0aGUgbW9kZWwuDQpgYGB7cn0NCiMgQnVpbGQgZm91ciBtb2RlbHMgZWFjaCB0aW1lIGRlbGV0aW5nIG9uZSB2YXJpYWJsZSBpbiBsb2dfM19yZW1vdmVfaXINCmxvZ180X3JlbW92ZV9hbW50IDwtIGdsbShsb2FuX3N0YXR1cyB+IGdyYWRlICsgYW5udWFsX2luYyArIGVtcF9jYXQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gdHJhaW5pbmdfc2V0KSANCmxvZ180X3JlbW92ZV9ncmFkZSA8LSBnbG0obG9hbl9zdGF0dXMgfiBsb2FuX2FtbnQgICsgYW5udWFsX2luYyArIGVtcF9jYXQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IHRyYWluaW5nX3NldCkNCmxvZ180X3JlbW92ZV9pbmMgPC0gZ2xtKGxvYW5fc3RhdHVzIH4gbG9hbl9hbW50ICsgZ3JhZGUgICsgZW1wX2NhdCAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwsIGRhdGEgPSB0cmFpbmluZ19zZXQpDQpsb2dfNF9yZW1vdmVfZW1wIDwtIGdsbShsb2FuX3N0YXR1cyB+IGxvYW5fYW1udCArIGdyYWRlICsgYW5udWFsX2luYywgDQogICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IHRyYWluaW5nX3NldCkNCg0KIyBNYWtlIFBELXByZWRpY3Rpb25zIGZvciBlYWNoIG9mIHRoZSBtb2RlbHMNCnByZWRfNF9yZW1vdmVfYW1udCA8LSBwcmVkaWN0KGxvZ180X3JlbW92ZV9hbW50LCBuZXdkYXRhID0gdGVzdF9zZXQsIHR5cGUgPSAicmVzcG9uc2UiKQ0KcHJlZF80X3JlbW92ZV9ncmFkZSA8LSBwcmVkaWN0KGxvZ180X3JlbW92ZV9ncmFkZSwgbmV3ZGF0YSA9IHRlc3Rfc2V0LCB0eXBlID0gInJlc3BvbnNlIikNCnByZWRfNF9yZW1vdmVfaW5jIDwtIHByZWRpY3QobG9nXzRfcmVtb3ZlX2luYywgbmV3ZGF0YSA9IHRlc3Rfc2V0LCB0eXBlID0gInJlc3BvbnNlIikNCnByZWRfNF9yZW1vdmVfZW1wIDwtIHByZWRpY3QobG9nXzRfcmVtb3ZlX2VtcCwgbmV3ZGF0YSA9IHRlc3Rfc2V0LCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyBDb21wdXRlIHRoZSBBVUNzDQphdWModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRfNF9yZW1vdmVfYW1udCkNCmF1Yyh0ZXN0X3NldCRsb2FuX3N0YXR1cywgcHJlZF80X3JlbW92ZV9ncmFkZSkNCmF1Yyh0ZXN0X3NldCRsb2FuX3N0YXR1cywgcHJlZF80X3JlbW92ZV9pbmMpDQphdWModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRfNF9yZW1vdmVfZW1wKQ0KYGBgDQojIyMgRnVydGhlciBtb2RlbCByZWR1Y3Rpb24/DQoNCkRlbGV0aW5nIHRoZSB2YXJpYWJsZSBsb2FuX2FtbnQsIHRoZSBBVUMgY2FuIGJlIGZ1cnRoZXIgaW1wcm92ZWQgdG8gMC42NTQ4ISBUaGUgcmVzdWx0aW5nIG1vZGVsIGlzDQoNCiAgICBsb2dfNF9yZW1vdmVfYW1udCA8LSBnbG0obG9hbl9zdGF0dXMgfiBncmFkZSArIGFubnVhbF9pbmMgKyBlbXBfY2F0LCBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IHRyYWluaW5nX3NldCkgDQoNCklzIGl0IHBvc3NpYmxlIHRvIHJlZHVjZSB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0byBvbmx5IHR3byB2YXJpYWJsZSB3aXRob3V0IHJlZHVjaW5nIHRoZSBBVUM/IEluIHRoaXMgZXhlcmNpc2UgeW91IHdpbGwgc2VlIGlmIGl0IGlzIHBvc3NpYmxlIQ0KYGBge3J9DQojIEJ1aWxkIHRocmVlIG1vZGVscyBlYWNoIHRpbWUgZGVsZXRpbmcgb25lIHZhcmlhYmxlIGluIGxvZ180X3JlbW92ZV9hbW50DQpsb2dfNV9yZW1vdmVfZ3JhZGUgPC0gZ2xtKGxvYW5fc3RhdHVzIH4gYW5udWFsX2luYyArIGVtcF9jYXQsIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gdHJhaW5pbmdfc2V0KSAgDQpsb2dfNV9yZW1vdmVfaW5jIDwtIGdsbShsb2FuX3N0YXR1cyB+IGdyYWRlICArIGVtcF9jYXQgLCBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IHRyYWluaW5nX3NldCkNCmxvZ181X3JlbW92ZV9lbXAgPC0gZ2xtKGxvYW5fc3RhdHVzIH4gZ3JhZGUgKyBhbm51YWxfaW5jLCBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IHRyYWluaW5nX3NldCkNCg0KIyBNYWtlIFBELXByZWRpY3Rpb25zIGZvciBlYWNoIG9mIHRoZSBtb2RlbHMNCnByZWRfNV9yZW1vdmVfZ3JhZGUgPC0gcHJlZGljdChsb2dfNV9yZW1vdmVfZ3JhZGUsIG5ld2RhdGEgPSB0ZXN0X3NldCwgdHlwZSA9ICJyZXNwb25zZSIpDQpwcmVkXzVfcmVtb3ZlX2luYyA8LSBwcmVkaWN0KGxvZ181X3JlbW92ZV9pbmMsIG5ld2RhdGEgPSB0ZXN0X3NldCwgdHlwZSA9ICJyZXNwb25zZSIpDQpwcmVkXzVfcmVtb3ZlX2VtcCA8LSBwcmVkaWN0KGxvZ181X3JlbW92ZV9lbXAsIG5ld2RhdGEgPSB0ZXN0X3NldCwgdHlwZSA9ICJyZXNwb25zZSIpDQoNCiMgQ29tcHV0ZSB0aGUgQVVDcw0KYXVjKHRlc3Rfc2V0JGxvYW5fc3RhdHVzLCBwcmVkXzVfcmVtb3ZlX2dyYWRlKQ0KYXVjKHRlc3Rfc2V0JGxvYW5fc3RhdHVzLCBwcmVkXzVfcmVtb3ZlX2luYykNCmF1Yyh0ZXN0X3NldCRsb2FuX3N0YXR1cywgcHJlZF81X3JlbW92ZV9lbXApDQoNCiMgUGxvdCB0aGUgUk9DLWN1cnZlIGZvciB0aGUgYmVzdCBtb2RlbCBoZXJlDQpwbG90KHJvYyh0ZXN0X3NldCRsb2FuX3N0YXR1cyxwcmVkXzRfcmVtb3ZlX2FtbnQpKQ0KYGBgDQoNCg==