1 Introduction

The bootstrap method is a data-based simulation method for statistical inference. The method assumes that

  • The sample is a random sample representing the population;

  • The sample size is large enough such that the empirical distribution can be close to the true distribution.

2 Basic Idea of Bootstrap Method.

The objective is to estimate a population parameter such as mean, variance, correlation coefficient, regression coefficients, etc. from a random sample without assuming any probability distribution of the underlying distribution of the population.

For convenience, we assume that the population of interest has a cumulative distribution function \(F(x: \theta)\), where \(\theta\) is a vector of the population. For example, You can think about the following distributions

  • Normal distribution: \(N(\mu, \sigma^2)\), the distribution function is given by

\[ f(x:\theta) = \frac{1}{\sqrt{2\pi}\sigma}\exp\left[-\frac{(x-\mu)^2}{2\sigma^2}\right] \]

where \(\theta = (\mu, \sigma)\). Since the normal distribution is so fundamental in statistics, we use the special notation for the cumulative distribution \(\phi_{\mu, \sigma^2}(x)\) or simply \(\phi(x)\). The corresponding probability function

  • Binomial distribution: \(Binom(n, p)\), the probability distribution is given by

\[ P(x) = \frac{n!}{x!(n-x)!}p^x(1-p)^{n-x}, x = 0, 1, 2, \cdots, n-1, n. \]

where \(\theta = p\). Caution: \(n\) is NOT a parameter!

We have already learned how to make inferences about population means and variances under various assumptions in elementary statistics. In this note, we introduce a new approach to making inferences only based on a given random sample taken from the underlying population.

As an example, we focus on the population mean. For other parameters, we can follow the same idea to make bootstrap inferences.

2.1 Sampling True Population - Monte Carlo Sampling

We have introduced various study designs and sampling plans to obtain random samples from a given population with the distribution function \(F(x:\theta)\). Let \(\mu\) be the population mean.

  • Random Sample. Let

\[ \{x_1, x_2, \cdots, x_n\} \to F(x:\theta) \]

be a random sample from population \(F(x:\theta)\).

  • Sample Mean. The point estimate is given by

\[\hat{\mu} = \frac{\sum_{i=1}^n x_i}{n}\]

  • Sampling Distribution of \(\hat{\mu}\). In order to construct the confidence interval of \(\mu\) or make hypothesis testing about \(\mu\), we need to know the sampling distribution of \(\hat{\mu}\). From elementary statistics, we have the following results.

    • \(\hat{\mu}\) is normally distributed if (1). \(n\) is large; or (2). the population is normal and population variance is known.

    • the standardized \(\hat{\mu}\) follows a t-distribution if the population is normal and population variance is unknown.

    • \(\hat{\mu}\) is unknown of the population is not normal and the sample size is not large enough.

  • In the last case of the previous bullet point, we don’t have the theory to derive the sampling distribution based on a single sample. However, if the sampling is not too expensive and time-consuming, we take following the sample study design and sampling plan to repeatedly take a large number, 1000, samples of the same size from the population. We calculate the mean of each of the 1000 samples and obtain 1000 sample means \(\{\hat{\mu}_1, \hat{\mu}_2, \cdots, \hat{\mu}_{1000}\}\). Then the empirical distribution of \(\hat{\mu}\).

The following figure depicts the process of how to sample the true population and approximate the sampling distribution of the point estimator of the population parameter.

Figure 1. Steps for estimating the sampling distribution of a point estimator of the population parameter

Figure 1. Steps for estimating the sampling distribution of a point estimator of the population parameter


2.2 Sampling Random Sample - Bootstrap Sampling

Sampling the true population can be very expensive in practice! That means the method of Monte Carlo sampling is infeasible from a practical perspective. The question is whether there are ways to estimate the sampling distribution of the sample means from a single given random sample? The answer is YES under the assumption the sample yields a valid estimation of the original population distribution.

  • Bootstrap Sampling With the assumption that the sample yields a good approximation of the population distribution, we can take bootstrap samples from the actual sample. Let \[\{x_1, x_2, \cdots, x_n\} \to F(x:\theta)\] be the actual random sample taken from the population. A bootstrap sample is obtained by taking a sample with replacement from the original data set (not the population!) with the same size as the original sample. Because with replacement was used, some values in the bootstrap sample appear once, some twice, and so on, and some do not appear at all.

  • Notation of Bootstrap Sample. We use \(\{x_1^{(i*)}, x_2^{(i*)}, \cdots, x_n^{(i*)}\}\) to denote the \(i^{th}\) bootstrap sample. Then the corresponding mean is called bootstrap sample mean and denoted by \(\hat{\mu}_i^*\), for \(i = 1, 2, ..., n\).

  • Bootstrap sampling distribution of the sample mean can be estimated by taking a large number, say B, of bootstrap samples. The resulting B bootstrap sample means are used to estimate the sampling distribution. Note that, in practice, B is bigger than 1000.

The above Bootstrap sampling process is illustrated in the following figure.

Figure 2. Steps for the Bootstrap sampling distribution of a point estimator of the population parameter

Figure 2. Steps for the Bootstrap sampling distribution of a point estimator of the population parameter


2.3 Case Study: Confidence Interval of Correlation Coefficient

There is no exact formula for the correlation coefficient of the two variables. Different approximate confidence intervals are available. One of them is based on Pearson transformation. The explicit form of the interval has the following form.

\[ \left( \frac{e^{2L}-1}{e^{2L}+1}, \frac{e^{2U}-1}{e^{2U}+1}\right) \]

where \[ L = z_r - \frac{Z_{0.975}}{\sqrt{n-3}}, \ \ \ U = z_r + \frac{Z_{0.975}}{\sqrt{n-3}} \]

and

\[ z_r = \frac{\ln(1+r)-\ln(1-r)}{2} \ \ \ \text{and} \ \ \ z_{0.975} = \text{97.5 th percentile of the standard normal distribution.} \]

y = Carseats$Sales
x = Carseats$Price
plot(Sales ~ Price, data = Carseats, pch=19, cex=0.8)
Figure 3. The scatter plot between sales and price in car seats data set

Figure 3. The scatter plot between sales and price in car seats data set

##  The following is the translation of the given formula
n = length(y)
r = cor(x,y)    # sample Pearson correlation coefficient
zr = (log(1+r)-log(1-r))/2
z.975 = qnorm(0.975)
L = zr - z.975/sqrt(n-3)
U = zr + z.975/sqrt(n-3)
LCI = (exp(2*L)-1)/(exp(2*L)+1)
UCI = (exp(2*U)-1)/(exp(2*U)+1)
CI = cbind(LCI = LCI, UCI = UCI)
### Bootstrap CI
B = 1000              # Take 1000 bootstrap sample
bt.r = c()            # empty vector to store bootstrap coefficient
for ( i in 1:B){
  bt.id = sample(1:n, n, replace = TRUE)   # bootstrapping observation IDs
  bt.x = x[bt.id]     # bootstrap x , must use the above bt.id
  bt.y = y[bt.id]     # bootstrap y , must use the above bt.id
  bt.r[i] = cor(bt.x, bt.y)
}
## 2.5% and 97.5% of the 1000 bootstrap correlation coefficients are the
## lower and upper limits of the 95% bootstrap confidence interval
bt.CI = quantile(bt.r, c(0.025, 0.975))
list(CI = CI, bt.CI = as.vector(bt.CI))
## $CI
##             LCI       UCI
## [1,] -0.5203026 -0.362724
## 
## $bt.CI
## [1] -0.5235370 -0.3618781


3 Bootstrap Confidence Interval of AUC for Logistic Model

In this section, we demonstrate how to find the confidence interval of the area of the ROC curve. This confidence interval is of practical importance.

3.1 Bootstrap Sampling for Logistic Modeling

In logistic regression models, the residuals are not defined as those in the linear regression (i.e., \(e_i = \text{observed }y - \text{fitted }y\)). Bootstrapping residuals does not work for logistic regression. We can use the following two steps to take bootstrap samples:

  1. Take a bootstrap sample from the observation IDs with the same size (and with replacement): \(\{1, 2, 3, \cdots, n \}\). Note that some of the IDs will be sampled multiple times.

  2. Using the bootstrap IDs to identify the corresponding records in the data set and defined a new data set called Bootstrap sample. Some of the records appear multiple times. These duplicate records with be kept in the sample. In other words, the distinct records in a bootstrap sample are less than the sample size!

Following the above steps, we can take many bootstrap samples. Recall that we can build a logistic regression for a given data set and construct an ROC curve and calculate the area under the curve. This means, if we draw \(B = 1000\) bootstrap samples, we can then build 1000 logistic regression models (also called bootstrap logistic regression models), hence, will have 1000 bootstrap AUCs (one for each bootstrap logistic model).

This further means that the histogram of these bootstrap AUCs can be considered as the sampling distribution of the actual sample AUC. The 95% Bootstrap confidence interval of AUC is defined as the 2.5% and 97.5% quantiles of the bootstrap AUCs.


3.2 Case Study - Confidence Interval of AUC

We present two examples showing how to calculate the bootstrap confidence interval of the AUC using a logistic regression model. One can apply the same steps for neural net and decision tree algorithms.

3.2.1 Predicting Graduate Admission

We use the following admission data set to illustrate the steps.

admitted = read.csv("https://pengdsci.github.io/STA551/w10/w09-admitted.csv")
notadmitted = read.csv("https://pengdsci.github.io/STA551/w10/w09-notAdmitted.csv")
## add an admission status variable to both sub-samples.
admitted$status = "yes"                         # admitted
notadmitted$status = "no "                      # not admitted
admission = rbind(admitted, notadmitted)        # combining the two sub-samples
## Define an empty vector to store bootstrap AUCs.
btAUC.vec = c()
## Select the number of bootstrap samples to be generated
B = 1000
## Size of the original sample
sample.size = dim(admission)[1]
## Vector of cut-off probabilities for construct ROC
cut.off.seq = seq(0,1, length = 100)
# bootstrap procedure starts here
for (k in 1:B){
  boot.id = sample(1:sample.size, sample.size, replace = TRUE)   # Bootstrap IDs
  boot.sample = admission[boot.id,]      # Bootstrap samples         
  ## Bootstrap logistic regression model is given below
  boot.logistic = glm(factor(status)~., family = binomial, data = boot.sample)
  ##
  pred.prob = predict.glm(boot.logistic, newdata = boot.sample, type = "response")
  ## vectors to store sensitivity and specificity
  sensitivity.vec = NULL
  specificity.vec = NULL
    for (i in 1:100){
   pred.status = as.numeric(pred.prob > cut.off.seq[i])
   ### components for defining various measures
   TN = sum(pred.status == 0 & boot.sample$status == "no ")
   FN = sum(pred.status == 0 & boot.sample$status == "yes")
   FP = sum(pred.status == 1 & boot.sample$status == "no ")
   TP = sum(pred.status == 1 & boot.sample$status == "yes")
   ###
   sensitivity.vec[i] = TP / (TP + FN)
   specificity.vec[i] = TN / (TN + FP)
  }
  one.minus.spec = 1 - specificity.vec
  sens.vec = sensitivity.vec
  ## A better approx of ROC, need library {pROC}
  prediction = pred.prob
  category = boot.sample$status == "yes"
  ROCobj <- roc(category, prediction)
  btAUC.vec[k] = round(auc(ROCobj),4)
}
hist(btAUC.vec, xlab = "Bootstrap AUC", main = "Bootstrap Sampling Distribution of AUCs \n (Admission Prediction Model)")
Figure 4. The bootstrap sampling distribution of the area under the curve of ROC (graduate admission prediction)

Figure 4. The bootstrap sampling distribution of the area under the curve of ROC (graduate admission prediction)

The 95% bootstrap confidence interval of the AUC is defined to be 2.5% and 97.5% quantiles of the bootstrap AUCs.

pander(quantile(btAUC.vec, c(0.025, 0.975)))
2.5% 97.5%
0.6322 0.7529

The confidence interval of AUC can be used for variable selection: if two confidence intervals of AUC overlapped, the predictive performances of the two corresponding predictive models are not significantly different. A simpler model should be recommended for implementation.


3.2.2 Fraud Detection Data

We use the fraud index data available on the instructor’s teaching data repository to construct the \(95\%\) confidence interval of the AUC. This is a simple data set with only two variables. We first prepare the data set for the subsequent modeling.

fraud.data = read.csv("https://pengdsci.github.io/datasets/FraudIndex/fraudidx.csv")[,-1]
## recode status variable: bad = 1 and good = 0
good.id = which(fraud.data$status == " good") 
bad.id = which(fraud.data$status == "fraud")
##
fraud.data$fraud.status = 0
fraud.data$fraud.status[bad.id] = 1

Next, we perform bootstrap logistic regression with 1000 bootstrap samples and build 1000 bootstrap logistic regression models and calculate the AUC of the ROC of the corresponding bootstrap logistic regression models.

## Define an empty vector to store bootstrap AUCs.
btAUC.vec = c()
## select the number of bootstrap samples to be generated
B = 1000
## Size of the original sample
sample.size = dim(fraud.data)[1]
## Vector of cut-off probabilities for construct ROC
cut.off.seq = seq(0,1, length = 100)
# bootstrap procedure starts here
for (k in 1:B){
  boot.id = sample(1:sample.size, sample.size, replace = TRUE)   # Bootstrap IDs
  boot.sample = fraud.data[boot.id,]      # Bootstrap samples         
  ## Bootstrap logistic regression model is given below
  boot.logistic = glm(factor(status) ~ index, family = binomial, data = boot.sample)
  ##
  newdata = data.frame(index= boot.sample$index) 
  pred.prob = predict.glm(boot.logistic, newdata, type = "response")
  ## vectors to store sensitivity and specificity
  sensitivity.vec = NULL
  specificity.vec = NULL
  for (i in 1:100){
   pred.status = as.numeric(pred.prob > cut.off.seq[i])
   ### components for defining various measures
   TN = sum(pred.status == 0 & boot.sample$fraud.status == 0)
   FN = sum(pred.status == 0 & boot.sample$fraud.status == 1)
   FP = sum(pred.status == 1 & boot.sample$fraud.status == 0)
   TP = sum(pred.status == 1 & boot.sample$fraud.status == 1)
   ###
   sensitivity.vec[i] = TP / (TP + FN)
   specificity.vec[i] = TN / (TN + FP)
  }
  one.minus.spec = 1 - specificity.vec
  sens.vec = sensitivity.vec
  ## A better approx of ROC, need library {pROC}
  prediction = pred.prob
  category = boot.sample$fraud.status == 1
  ROCobj <- roc(category, prediction)
  btAUC.vec[k] = round(auc(ROCobj),4)
}
hist(btAUC.vec, xlab = "Bootstrap AUC", main = "Bootstrap Sampling Distribution 
of AUCs \n (Fraud Detection Model)")
Figure 5. Bootstrap sampling distribution of AUC (fraud prediction)

Figure 5. Bootstrap sampling distribution of AUC (fraud prediction)

The 95% bootstrap confidence interval of the AUC is defined to be 2.5% and 97.5% quantiles of the bootstrap AUCs.

With the bootstrap AUCs, we can similarly construct the 95% bootstrap confidence interval for the AUC of the fraud detection model is \((0.9234, 0.9313 )\).


4 Concepts of Ensemble Algorithms

We have taken many bootstrap samples and built a logistic regression model on each of these bootstrap samples and calculate the area under the ROC curve of associated logistic regression models. Using these bootstrap AUCs, we approximate the find the bootstrap sampling distribution of the AUC and, hence, find the confidence interval of the AUC.

We can follow the same steps to find the bootstrap confidence intervals of the AUCs of decision trees and neural net algorithms. One important application of Bootstrap in machine learning is its ability to aggregate a set of weak models and algorithms to make a stronger combined model - This is the so-called ensemble learning method in machine learning. One way of improving the performance of a weak model through an ensemble approach is to reduce the risk of overfitting and underfitting by balancing the trade-off between bias and variance,

4.1 Overfitting v.s. Underfitting

In predictive modeling, bias is a phenomenon that skews the result of an algorithm in favor of or against the ground truth. It describes how well the model matches the training data set:

  • A model with a higher bias would not match the data set closely.
  • A low-bias model will closely match the training data set.

Variance refers to the changes in the model when using different portions of the training data set. The variance comes from highly complex (but valid) models with a large number of features

  • Models with high bias will have low variance.
  • Models with high variance will have a low bias.

The terms underfitting and overfitting refer to how the model fails to match the data.

  • Underfitting occurs when the model is too simple to be able to match the input data to the target data.

  • Overfitting occurs when the model is highly complex but perfectly matches almost all the given data points and performs well in training data sets. However, the model would not be able to generalize the data point in the test data set to predict the outcome accurately due to high error (variation).

Figure 6. Bias, variance, overfitting and underfitting of predictive models

Figure 6. Bias, variance, overfitting and underfitting of predictive models

4.2 The Logic of Ensemble Learning

Ensemble learning attests to the idea of the “wisdom of crowds,” which suggests that the decision-making of a larger group of people is typically better than that of an individual expert. Similarly, ensemble learning refers to a group (or ensemble) of base learners (eg., models and algorithms), which work collectively to achieve a better final prediction.

A single model or algorithm, also known as a base or weak learner, may not perform well individually due to high variance or high bias. However, when weak learners are aggregated, they can form a strong learner (with stable performance and low variance) yielding better model performance.

Many different types of ensemble learning methods have been developed in the past few decades, Among them, boosting and Bootstrap aggregation (BAGGING) methods are commonly used in practice.


4.3 BAGGING Ensemble Methods

Bagging is an acronym for Bootstrap Aggregation and is used to decrease the variance in the prediction model (the idea we adopted when identifying the optimal cut-off scores and the confidence interval of AUC).

Bagging is a parallel method that fits different, considered learners independently from each other, making it possible to train them simultaneously.

Bagging generates additional data for training from the data set. This is achieved by bootstrap sampling (random sampling with replacement) from the original data set. As discussed in earlier sections, Bootstrap Sampling may repeat some observations in each new training data set. This guarantees that every element in Bagging is equally probable for appearing in a bootstrap data set.

These bootstrap data sets are used to train multiple models in parallel. The average of all the predictions from different ensemble models is calculated. The majority vote gained from the voting mechanism is considered when classification is made. Bagging decreases the variance and tunes the prediction to an expected outcome.

The following figure explains the BAGGING algorithm applied to the decision tree algorithm.

Figure 7. The idea of Gagging ensemble algorithm.

Figure 7. The idea of Gagging ensemble algorithm.

In summary, the bagging algorithm has three basic steps:

Bootstrapping: Bagging leverages a bootstrapping sampling technique to create diverse samples.

  • Parallel training: These bootstrap samples are then trained independently and in parallel with each other using weak or base learners (de).

  • Aggregation: Finally, aggregating the outputs from individual algorithms/models. In the case of using a tree algorithm (like the above figure)

    • for regression, an average of all terminal node weights is taken of all the outputs predicted by the individual classifiers; this is known as soft voting.
    • for classification, the class (defined with default 0.5 in each individual tree) with the highest majority of votes is accepted; this is known as hard voting or majority voting.


Case Study: BAGGING Classification Trees

BAGGING is implemented in R libraries ipred{} and rpart{}. They are usually used with several other libraries such as caret{} and e1071{} to extract relevant information in the bagging algorithm.

Pima = read.csv("https://pengdsci.github.io/STA551/w10/AnalyticPimaDiabetes.csv")[,-1]
# We use a random split approach
n = dim(Pima)[1]  # sample size
# caution: using without replacement
train.id = sample(1:n, round(0.7*n), replace = FALSE)  
train = Pima[train.id, ]    # training data
test = Pima[-train.id, ]    # testing data
##
Diabetes.bag.train <- bagging(as.factor(diabetes) ~ ., 
                              data = train, 
                              nbagg = 150,    # number of trees
                              coob = TRUE, 
                              parms = list(loss = matrix(c(0, 10, 1, 0), 
                                                          ncol = 20, 
                                                          byrow = TRUE),   
                                                          split = "gini"),  
                              control = rpart.control(minsplit = 10, 
                                                      cp = 0.02))
### predict() returns either "class" or "prob" in the classification
### When specifying type = "class", the default cut-off of 0.5 is used.
pred = predict(Diabetes.bag.train, train, type = "prob")
### Optimal cut-off probability identification: no cross-validation is needed
cut.prob = seq(0,1, length = 20)
  senspe.mtx = matrix(0, ncol = length(cut.prob), nrow= 3, byrow = FALSE)
  for (i in 1:length(cut.prob)){
  # CAUTION: "pos" and "neg" are values of the label in this data set!
  # The following line uses only "pos" probability: pred[, "pos"] !!!!
  pred.out =  ifelse(pred[,"pos"] >= cut.prob[i], "pos", "neg")  
  TP = sum(pred.out =="pos" & train$diabetes == "pos")
  TN = sum(pred.out =="neg" & train$diabetes == "neg")
  FP = sum(pred.out =="pos" & train$diabetes == "neg")
  FN = sum(pred.out =="neg" & train$diabetes == "pos")
  senspe.mtx[1,i] = TP/(TP + FN)                  # sensitivity
  senspe.mtx[2,i] = TN/(TN + FP)                  # specificity
  senspe.mtx[3,i] = (TP+TN)/(TP + FN + TN + FP)   # accuracy
  }
  ## A better approx of ROC, need library {pROC}
  prediction = pred[, "pos"]
  category = train$diabetes == "pos"
  ROCobj <- roc(category, prediction)
  AUC = auc(ROCobj)
  AUC = round(as.vector(AUC[1]),3)
  ###
  n = length(senspe.mtx[3,])
  idx = which(senspe.mtx[3,] == max(senspe.mtx[3,]))
  tick.label = as.character(round(cut.prob,2))
  ###
  par(mfrow = c(1,2))
  plot(1-senspe.mtx[2,], senspe.mtx[1,], 
       type="l",
       lwd = 2,
       col = "navy",
       xlim=c(0,1), 
       ylim = c(0,1),
       xlab = "1 - specificity", 
       ylab = "Sensitivity", 
       main = "ROC (Taining Data)", 
       cex.main = 0.8)
  # Adding off diagonal line representing random guess
  segments(0, 0, 1, 1, lty = 2, col = "red", )
  legend("bottomright",c("fn = 10", "fp = 1", "cp = 0.02", paste("auc =", AUC)), bty="n", cex = 0.8)       
  
  plot(1:length(cut.prob), senspe.mtx[3,], 
       xlab="cut-off probability",
       ylab = "accuracy", 
       ylim=c(min(senspe.mtx[3,]),1),
       axes = FALSE,
       main="cut-off vs accuracy",
       cex.main = 0.9,
       col.main = "navy")
       # Adding axes and other graphical components to the plot
       axis(1, at=1:20, label = tick.label, las = 2)
       axis(2)
       points(idx, senspe.mtx[3,][idx], pch=19, col = "red")
       segments(idx , min(senspe.mtx[3,]), idx , senspe.mtx[3,][idx ], col = "red")
       #text(idx, senspe.mtx[3,][idx]+0.03, as.character(round(senspe.mtx[3,][idx],4)), 
       #     col = "red", cex = 0.8)
       legend("topright", c(paste("optimal cut-off prob = ",round(median(cut.prob[idx]),3), sep=""), 
                            paste("accuracy = ", round(mean(senspe.mtx[3,][idx ]),3), sep="")),
                          cex = 0.8, bty = "n", adj = -0)
Figure 8. The ROC curve and the plot for optimal cut-off determination.

Figure 8. The ROC curve and the plot for optimal cut-off determination.

Similarly, there are several hyperparameters one can tune to find the best-bootstrapped decision tree. For those who are interested in exploring more in tunning hyperparameters, you can write a wrapper of bagging() and pass hyperparameters such as fp, fn, cp, minisplit in the list of arguments in rpart() to find the best bootstrap decision tree.

4.4 Boosting Ensemble Methods

Boosting is a sequential ensemble method that iteratively adjusts the weight of data points as per the last classification. If a data point is incorrectly classified, it increases the weight of that data point. It decreases the bias and variance (hence the predictive error) and builds strong predictive models.

Data points misclassified in each iteration are spotted, and their weights are increased. The Boosting algorithm allocates weights to each resulting model during training. A model (also commonly called learner) with good training data prediction results will be assigned a higher weight. When evaluating a new learner, Boosting keeps track of the learner’s errors.

The steps of boosting algorithm are summarized in the following:

  1. Data points in the initial training data set are equally weighted.

  2. A based model is created for the initial training data set.

  3. classification Errors are counted using actual and predicted values. The data point that was incorrectly predicted is provided a higher weight.

  4. a model is built on the modified data (re-weighted data points)

  5. the process is iterated for multiple models and each of them corrects the previous model’s errors.

  6. The final model works as a strong learner and shows the weighted mean of all the models.

The following figure demonstrates the rough idea of boosting decision trees in a conceptual approach.

Figure 9. Graphical representation of boosting tree method

Figure 9. Graphical representation of boosting tree method

4.5 Bagging Versus Boosting

Bagging and Boosting have a universal similarity of being classified as ensemble methods. In addition, Bagging and Boosting

  • are ensemble methods focused on getting \(N\) learners from a single learner.

  • make random sampling and generate several training data sets.

  • arrive upon the end decision by making an average of \(N\) learners or taking the voting rank done by most of them.

  • reduce variance and provide higher stability by minimizing errors.

However, the two ensemble algorithms are very different from the technical perspective. The following table shows these structural differences.

Bagging Boosting
Merging the same type of predictions. Merging different types of predictions.
Decreases variance, not bias, and solves over-fitting issues. Decreases bias, not variance.
Each model receives an equal weight. Models are weighed based on their performance.
Models are built independently. New models are affected by a previously built model’s performance.
Training data subsets are drawn randomly with a bootstrap sample. Every new subset comprises the elements that were misclassified by previous models.
Usually applied where the classifier is unstable and has a high variance. Usually applied where the classifier is stable and simple and has a high bias.

The boosting ensemble algorithms have various new development in recent years. In general, they are more mathematically and pragmatically demanded in implementation. We will not go into details about any of boosting algorithms and implement them.

To conclude, we use the following figure to show the role of mathematical tools and thinking in the evolution of tree-based algorithms: the more mathematical tools you have, the more powerful models you are capable of developing!

Figure 10. Evolution of tree-based algorithms with various level of technical tools.

Figure 10. Evolution of tree-based algorithms with various level of technical tools.

LS0tDQp0aXRsZTogIkJvb3RzdHJhcCBBbGdvcml0aG1zIGFuZCBBcHBsaWNhdGljYXRpb25zIGluIFN1cGVydmlzZWQgQWxnb3JpdGhtcyINCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiU1RBNTUxIEZvdW5kYXRpb24gb2YgRGF0YSBTY2llbmNlICINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ193aWR0aDogNg0KICAgIGZpZ19oZWlnaHQ6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICB0aGVtZTogbHVtZW4NCiAgICBkZl9wcmludDoga2FibGUNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZmlnX3dpZHRoOiA1DQogICAgZmlnX2hlaWdodDogNA0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCi0tLQ0KDQpgYGB7PWh0bWx9DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQpkaXYjVE9DIGxpIHsNCiAgICBsaXN0LXN0eWxlOm5vbmU7DQogICAgYmFja2dyb3VuZC1pbWFnZTpub25lOw0KICAgIGJhY2tncm91bmQtcmVwZWF0Om5vbmU7DQogICAgYmFja2dyb3VuZC1wb3NpdGlvbjowOw0KfQ0KDQpoMS50aXRsZSB7DQogIGZvbnQtc2l6ZTogMjRweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMjJweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoMiB7IC8qIEhlYWRlciAzIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE1cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KPC9zdHlsZT4NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChjYWNoZSA9IEZBTFNFKQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoInBzeWNoIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBzeWNoIikNCiAgIGxpYnJhcnkocHN5Y2gpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQogICBsaWJyYXJ5KGdncGxvdDIpDQp9DQppZiAoIXJlcXVpcmUoInJwYXJ0IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCiAgIGxpYnJhcnkocnBhcnQpDQp9DQppZiAoIXJlcXVpcmUoInJhdHRsZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJyYXR0bGUiKQ0KICAgbGlicmFyeShyYXR0bGUpDQp9DQppZiAoIXJlcXVpcmUoInJwYXJ0LnBsb3QiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicnBhcnQucGxvdCIpDQogICBsaWJyYXJ5KHJwYXJ0LnBsb3QpDQp9DQppZiAoIXJlcXVpcmUoIlJDb2xvckJyZXdlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJSQ29sb3JCcmV3ZXIiKQ0KICAgbGlicmFyeShSQ29sb3JCcmV3ZXIpDQp9DQppZiAoIXJlcXVpcmUoImUxMDcxIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImUxMDcxIikNCiAgIGxpYnJhcnkoZTEwNzEpDQp9DQppZiAoIXJlcXVpcmUoImlwcmVkIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImlwcmVkIikNCiAgIGxpYnJhcnkoaXByZWQpDQp9DQppZiAoIXJlcXVpcmUoImNhcmV0IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCiAgIGxpYnJhcnkoY2FyZXQpDQp9DQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoIklTTFIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiSVNMUiIpDQogICBsaWJyYXJ5KElTTFIpDQp9DQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQ0KICAgbGlicmFyeShwYW5kZXIpDQp9DQppZiAoIXJlcXVpcmUoInBST0MiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicFJPQyIpDQogICBsaWJyYXJ5KHBST0MpDQp9DQojIw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgDQogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gVFJVRSwgICANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UpDQpgYGANCg0KDQpcDQoNCiMgIEludHJvZHVjdGlvbiAgDQoNClRoZSBib290c3RyYXAgbWV0aG9kIGlzIGEgZGF0YS1iYXNlZCBzaW11bGF0aW9uIG1ldGhvZCBmb3Igc3RhdGlzdGljYWwgaW5mZXJlbmNlLiBUaGUgbWV0aG9kIGFzc3VtZXMgdGhhdCANCg0KKiBUaGUgc2FtcGxlIGlzIGEgcmFuZG9tIHNhbXBsZSByZXByZXNlbnRpbmcgdGhlIHBvcHVsYXRpb247DQoNCiogVGhlIHNhbXBsZSBzaXplIGlzIGxhcmdlIGVub3VnaCBzdWNoIHRoYXQgdGhlIGVtcGlyaWNhbCBkaXN0cmlidXRpb24gY2FuIGJlIGNsb3NlIHRvIHRoZSB0cnVlIGRpc3RyaWJ1dGlvbi4NCg0KIwlCYXNpYyBJZGVhIG9mIEJvb3RzdHJhcCBNZXRob2QuIA0KDQpUaGUgb2JqZWN0aXZlIGlzIHRvIGVzdGltYXRlIGEgcG9wdWxhdGlvbiBwYXJhbWV0ZXIgc3VjaCBhcyBtZWFuLCB2YXJpYW5jZSwgY29ycmVsYXRpb24gY29lZmZpY2llbnQsIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLCBldGMuIGZyb20gYSByYW5kb20gc2FtcGxlIHdpdGhvdXQgYXNzdW1pbmcgYW55IHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBvZiB0aGUgdW5kZXJseWluZyBkaXN0cmlidXRpb24gb2YgdGhlIHBvcHVsYXRpb24uDQoNCkZvciBjb252ZW5pZW5jZSwgd2UgYXNzdW1lIHRoYXQgdGhlIHBvcHVsYXRpb24gb2YgaW50ZXJlc3QgaGFzIGEgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gJEYoeDogXHRoZXRhKSQsIHdoZXJlICRcdGhldGEkIGlzIGEgdmVjdG9yIG9mIHRoZSBwb3B1bGF0aW9uLiBGb3IgZXhhbXBsZSwgWW91IGNhbiB0aGluayBhYm91dCB0aGUgZm9sbG93aW5nIGRpc3RyaWJ1dGlvbnMNCg0KKiAqKk5vcm1hbCBkaXN0cmlidXRpb24qKjogJE4oXG11LCBcc2lnbWFeMikkLCB0aGUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIGlzIGdpdmVuIGJ5DQoNCiQkDQpmKHg6XHRoZXRhKSA9IFxmcmFjezF9e1xzcXJ0ezJccGl9XHNpZ21hfVxleHBcbGVmdFstXGZyYWN7KHgtXG11KV4yfXsyXHNpZ21hXjJ9XHJpZ2h0XQ0KJCQgDQogIA0Kd2hlcmUgJFx0aGV0YSA9IChcbXUsIFxzaWdtYSkkLiBTaW5jZSB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBpcyBzbyBmdW5kYW1lbnRhbCBpbiBzdGF0aXN0aWNzLCB3ZSB1c2UgdGhlIHNwZWNpYWwgbm90YXRpb24gZm9yIHRoZSBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiAkXHBoaV97XG11LCBcc2lnbWFeMn0oeCkkIG9yIHNpbXBseSAkXHBoaSh4KSQuIFRoZSBjb3JyZXNwb25kaW5nIHByb2JhYmlsaXR5IGZ1bmN0aW9uIA0KDQoqICoqQmlub21pYWwgZGlzdHJpYnV0aW9uKio6ICRCaW5vbShuLCBwKSQsIHRoZSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gaXMgZ2l2ZW4gYnkNCg0KJCQgDQpQKHgpID0gXGZyYWN7biF9e3ghKG4teCkhfXBeeCgxLXApXntuLXh9LCB4ID0gMCwgMSwgMiwgXGNkb3RzLCBuLTEsIG4uDQokJA0KDQp3aGVyZSAkXHRoZXRhID0gcCQuICpDYXV0aW9uKjogJG4kIGlzIE5PVCBhIHBhcmFtZXRlciENCg0KV2UgaGF2ZSBhbHJlYWR5IGxlYXJuZWQgaG93IHRvIG1ha2UgaW5mZXJlbmNlcyBhYm91dCBwb3B1bGF0aW9uIG1lYW5zIGFuZCB2YXJpYW5jZXMgdW5kZXIgdmFyaW91cyBhc3N1bXB0aW9ucyBpbiBlbGVtZW50YXJ5IHN0YXRpc3RpY3MuIEluIHRoaXMgbm90ZSwgd2UgaW50cm9kdWNlIGEgKipuZXcgYXBwcm9hY2gqKiB0byBtYWtpbmcgaW5mZXJlbmNlcyBvbmx5IGJhc2VkIG9uIGEgZ2l2ZW4gcmFuZG9tIHNhbXBsZSB0YWtlbiBmcm9tIHRoZSB1bmRlcmx5aW5nIHBvcHVsYXRpb24uDQoNCkFzIGFuIGV4YW1wbGUsIHdlIGZvY3VzIG9uIHRoZSBwb3B1bGF0aW9uIG1lYW4uIEZvciBvdGhlciBwYXJhbWV0ZXJzLCB3ZSBjYW4gZm9sbG93IHRoZSBzYW1lIGlkZWEgdG8gbWFrZSBib290c3RyYXAgaW5mZXJlbmNlcy4gDQoNCg0KIyMgU2FtcGxpbmcgVHJ1ZSBQb3B1bGF0aW9uIC0gTW9udGUgQ2FybG8gU2FtcGxpbmcNCg0KV2UgaGF2ZSBpbnRyb2R1Y2VkIHZhcmlvdXMgc3R1ZHkgZGVzaWducyBhbmQgc2FtcGxpbmcgcGxhbnMgdG8gb2J0YWluIHJhbmRvbSBzYW1wbGVzIGZyb20gYSBnaXZlbiBwb3B1bGF0aW9uIHdpdGggdGhlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiAkRih4Olx0aGV0YSkkLiBMZXQgJFxtdSQgYmUgdGhlIHBvcHVsYXRpb24gbWVhbi4NCg0KKiAqKlJhbmRvbSBTYW1wbGUqKi4gIExldCANCg0KJCQNClx7eF8xLCB4XzIsIFxjZG90cywgeF9uXH0gXHRvIEYoeDpcdGhldGEpDQokJA0KDQpiZSBhIHJhbmRvbSBzYW1wbGUgZnJvbSBwb3B1bGF0aW9uICRGKHg6XHRoZXRhKSQuDQoNCg0KKiAqKlNhbXBsZSBNZWFuKiouICBUaGUgcG9pbnQgZXN0aW1hdGUgaXMgZ2l2ZW4gYnkNCg0KJCRcaGF0e1xtdX0gPSBcZnJhY3tcc3VtX3tpPTF9Xm4geF9pfXtufSQkDQogICAgIA0KKiAqKlNhbXBsaW5nIERpc3RyaWJ1dGlvbiBvZiAkXGhhdHtcbXV9JCoqLiBJbiBvcmRlciB0byBjb25zdHJ1Y3QgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb2YgJFxtdSQgb3IgbWFrZSBoeXBvdGhlc2lzIHRlc3RpbmcgYWJvdXQgJFxtdSQsIHdlIG5lZWQgdG8ga25vdyB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mICRcaGF0e1xtdX0kLiBGcm9tIGVsZW1lbnRhcnkgc3RhdGlzdGljcywgd2UgaGF2ZSB0aGUgZm9sbG93aW5nIHJlc3VsdHMuDQoNCiAgKyAkXGhhdHtcbXV9JCBpcyBub3JtYWxseSBkaXN0cmlidXRlZCBpZiAoMSkuICRuJCBpcyBsYXJnZTsgb3IgKDIpLiB0aGUgcG9wdWxhdGlvbiBpcyBub3JtYWwgYW5kIHBvcHVsYXRpb24gdmFyaWFuY2UgaXMga25vd24uDQoNCiAgKyB0aGUgc3RhbmRhcmRpemVkICRcaGF0e1xtdX0kIGZvbGxvd3MgYSB0LWRpc3RyaWJ1dGlvbiBpZiB0aGUgcG9wdWxhdGlvbiBpcyBub3JtYWwgYW5kIHBvcHVsYXRpb24gdmFyaWFuY2UgaXMgdW5rbm93bi4NCiAgDQogICsgJFxoYXR7XG11fSQgaXMgKip1bmtub3duKiogb2YgdGhlIHBvcHVsYXRpb24gaXMgbm90IG5vcm1hbCBhbmQgdGhlIHNhbXBsZSBzaXplIGlzIG5vdCBsYXJnZSBlbm91Z2guDQogDQogDQoqIEluIHRoZSBsYXN0IGNhc2Ugb2YgdGhlIHByZXZpb3VzIGJ1bGxldCBwb2ludCwgd2UgZG9uJ3QgaGF2ZSB0aGUgdGhlb3J5IHRvIGRlcml2ZSB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIGJhc2VkIG9uIGEgKipzaW5nbGUqKiBzYW1wbGUuIEhvd2V2ZXIsIGlmIHRoZSBzYW1wbGluZyBpcyBub3QgdG9vIGV4cGVuc2l2ZSBhbmQgdGltZS1jb25zdW1pbmcsIHdlIHRha2UgZm9sbG93aW5nIHRoZSBzYW1wbGUgc3R1ZHkgZGVzaWduIGFuZCBzYW1wbGluZyBwbGFuIHRvIHJlcGVhdGVkbHkgdGFrZSBhIGxhcmdlIG51bWJlciwgMTAwMCwgc2FtcGxlcyBvZiB0aGUgc2FtZSBzaXplIGZyb20gdGhlIHBvcHVsYXRpb24uIFdlIGNhbGN1bGF0ZSB0aGUgbWVhbiBvZiBlYWNoIG9mIHRoZSAxMDAwIHNhbXBsZXMgYW5kIG9idGFpbiAxMDAwIHNhbXBsZSBtZWFucyAkXHtcaGF0e1xtdX1fMSwgIFxoYXR7XG11fV8yLCBcY2RvdHMsIFxoYXR7XG11fV97MTAwMH1cfSQuIFRoZW4gdGhlIGVtcGlyaWNhbCBkaXN0cmlidXRpb24gb2YgJFxoYXR7XG11fSQuIA0KDQoNClRoZSBmb2xsb3dpbmcgZmlndXJlIGRlcGljdHMgdGhlIHByb2Nlc3Mgb2YgaG93IHRvIHNhbXBsZSB0aGUgdHJ1ZSBwb3B1bGF0aW9uIGFuZCBhcHByb3hpbWF0ZSB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mIHRoZSBwb2ludCBlc3RpbWF0b3Igb2YgdGhlIHBvcHVsYXRpb24gcGFyYW1ldGVyLg0KDQoNCmBgYHtyIGVjaG89RkFMU0UsIGZpZy5hbGlnbiA9ImNlbnRlciIsIGZpZy5jYXA9IkZpZ3VyZSAxLiBTdGVwcyBmb3IgZXN0aW1hdGluZyB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mIGEgcG9pbnQgZXN0aW1hdG9yIG9mIHRoZSBwb3B1bGF0aW9uIHBhcmFtZXRlciIsIG91dC53aWR0aD0iODAlIiwgZGV2PSJqcGVnIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy93MDktQXBwcm94U2FtcGxpbmdEaXN0LmpwZyIpDQpgYGANCg0KDQpcDQoNCg0KIyMgU2FtcGxpbmcgUmFuZG9tIFNhbXBsZSAtIEJvb3RzdHJhcCBTYW1wbGluZw0KDQpTYW1wbGluZyB0aGUgdHJ1ZSBwb3B1bGF0aW9uIGNhbiBiZSB2ZXJ5IGV4cGVuc2l2ZSBpbiBwcmFjdGljZSEgVGhhdCBtZWFucyB0aGUgbWV0aG9kIG9mIE1vbnRlIENhcmxvIHNhbXBsaW5nIGlzIGluZmVhc2libGUgZnJvbSBhIHByYWN0aWNhbCBwZXJzcGVjdGl2ZS4gVGhlIHF1ZXN0aW9uIGlzIHdoZXRoZXIgdGhlcmUgYXJlIHdheXMgdG8gZXN0aW1hdGUgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc2FtcGxlIG1lYW5zIGZyb20gKiphIHNpbmdsZSBnaXZlbiByYW5kb20gc2FtcGxlKio/IFRoZSBhbnN3ZXIgaXMgWUVTIHVuZGVyIHRoZSBhc3N1bXB0aW9uIHRoZSBzYW1wbGUgeWllbGRzIGEgdmFsaWQgZXN0aW1hdGlvbiBvZiB0aGUgb3JpZ2luYWwgcG9wdWxhdGlvbiBkaXN0cmlidXRpb24uIA0KDQoqICoqQm9vdHN0cmFwIFNhbXBsaW5nKiogIFdpdGggdGhlIGFzc3VtcHRpb24gdGhhdCB0aGUgc2FtcGxlIHlpZWxkcyBhIGdvb2QgYXBwcm94aW1hdGlvbiBvZiB0aGUgcG9wdWxhdGlvbiBkaXN0cmlidXRpb24sIHdlIGNhbiB0YWtlIGJvb3RzdHJhcCBzYW1wbGVzIGZyb20gdGhlICoqYWN0dWFsKiogc2FtcGxlLiBMZXQNCiQkXHt4XzEsIHhfMiwgXGNkb3RzLCB4X25cfSBcdG8gRih4Olx0aGV0YSkkJCBiZSB0aGUgYWN0dWFsIHJhbmRvbSBzYW1wbGUgdGFrZW4gZnJvbSB0aGUgcG9wdWxhdGlvbi4gQSAqKmJvb3RzdHJhcCBzYW1wbGUqKiBpcyBvYnRhaW5lZCBieSB0YWtpbmcgYSBzYW1wbGUgKip3aXRoIHJlcGxhY2VtZW50KiogZnJvbSB0aGUgb3JpZ2luYWwgZGF0YSBzZXQgKG5vdCB0aGUgcG9wdWxhdGlvbiEpIHdpdGggdGhlIHNhbWUgc2l6ZSBhcyB0aGUgb3JpZ2luYWwgc2FtcGxlLiBCZWNhdXNlICoqd2l0aCByZXBsYWNlbWVudCoqIHdhcyB1c2VkLCBzb21lIHZhbHVlcyBpbiB0aGUgYm9vdHN0cmFwIHNhbXBsZSBhcHBlYXIgb25jZSwgc29tZSB0d2ljZSwgYW5kIHNvIG9uLCBhbmQgc29tZSBkbyBub3QgYXBwZWFyIGF0IGFsbC4gDQoNCiogKipOb3RhdGlvbiBvZiBCb290c3RyYXAgU2FtcGxlKiouIFdlIHVzZSAkXHt4XzFeeyhpKil9LCB4XzJeeyhpKil9LCBcY2RvdHMsIHhfbl57KGkqKX1cfSQgdG8gZGVub3RlIHRoZSAkaV57dGh9JCBib290c3RyYXAgc2FtcGxlLiBUaGVuIHRoZSBjb3JyZXNwb25kaW5nIG1lYW4gaXMgY2FsbGVkIGJvb3RzdHJhcCBzYW1wbGUgbWVhbiBhbmQgZGVub3RlZCBieSAkXGhhdHtcbXV9X2leKiQsIGZvciAkaSA9IDEsIDIsIC4uLiwgbiQuDQoNCioJKipCb290c3RyYXAgc2FtcGxpbmcgZGlzdHJpYnV0aW9uKiogb2YgdGhlIHNhbXBsZSBtZWFuIGNhbiBiZSBlc3RpbWF0ZWQgYnkgdGFraW5nIGEgbGFyZ2UgbnVtYmVyLCBzYXkgQiwgb2YgYm9vdHN0cmFwIHNhbXBsZXMuIFRoZSByZXN1bHRpbmcgQiBib290c3RyYXAgc2FtcGxlIG1lYW5zIGFyZSB1c2VkIHRvIGVzdGltYXRlIHRoZSBzYW1wbGluZyBkaXN0cmlidXRpb24uIE5vdGUgdGhhdCwgaW4gcHJhY3RpY2UsIEIgaXMgYmlnZ2VyIHRoYW4gMTAwMC4NCg0KVGhlIGFib3ZlIEJvb3RzdHJhcCBzYW1wbGluZyBwcm9jZXNzIGlzIGlsbHVzdHJhdGVkIGluIHRoZSBmb2xsb3dpbmcgZmlndXJlLg0KDQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuYWxpZ24gPSJjZW50ZXIiLCBmaWcuY2FwPSJGaWd1cmUgMi4gU3RlcHMgZm9yIHRoZSBCb290c3RyYXAgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mIGEgcG9pbnQgZXN0aW1hdG9yIG9mIHRoZSBwb3B1bGF0aW9uIHBhcmFtZXRlciIsIG91dC53aWR0aD0iODAlIiwgZGV2PSJqcGVnIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy93MDktQm9vdFNhbXBsaW5nRGlzdC5qcGciKQ0KYGBgDQoNClwNCg0KIyMgQ2FzZSBTdHVkeTogQ29uZmlkZW5jZSBJbnRlcnZhbCBvZiBDb3JyZWxhdGlvbiBDb2VmZmljaWVudA0KDQpUaGVyZSBpcyBubyBleGFjdCBmb3JtdWxhIGZvciB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgb2YgdGhlIHR3byB2YXJpYWJsZXMuIERpZmZlcmVudCBhcHByb3hpbWF0ZSBjb25maWRlbmNlIGludGVydmFscyBhcmUgYXZhaWxhYmxlLiBPbmUgb2YgdGhlbSBpcyBiYXNlZCBvbiBQZWFyc29uIHRyYW5zZm9ybWF0aW9uLiBUaGUgZXhwbGljaXQgZm9ybSBvZiB0aGUgaW50ZXJ2YWwgaGFzIHRoZSBmb2xsb3dpbmcgZm9ybS4NCg0KJCQNClxsZWZ0KCBcZnJhY3tlXnsyTH0tMX17ZV57Mkx9KzF9LCAgXGZyYWN7ZV57MlV9LTF9e2VeezJVfSsxfVxyaWdodCkNCiQkDQoNCndoZXJlDQokJA0KTCA9IHpfciAtIFxmcmFje1pfezAuOTc1fX17XHNxcnR7bi0zfX0sIFwgXCBcIFUgPSB6X3IgKyBcZnJhY3taX3swLjk3NX19e1xzcXJ0e24tM319DQokJA0KDQphbmQgDQoNCiQkDQp6X3IgPSBcZnJhY3tcbG4oMStyKS1cbG4oMS1yKX17Mn0gXCBcIFwgXHRleHR7YW5kfSBcIFwgXCB6X3swLjk3NX0gPSBcdGV4dHs5Ny41IHRoIHBlcmNlbnRpbGUgb2YgdGhlIHN0YW5kYXJkIG5vcm1hbCBkaXN0cmlidXRpb24ufQ0KJCQNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuY2FwPSJGaWd1cmUgMy4gVGhlIHNjYXR0ZXIgcGxvdCBiZXR3ZWVuIHNhbGVzIGFuZCBwcmljZSBpbiBjYXIgc2VhdHMgZGF0YSBzZXQifQ0KeSA9IENhcnNlYXRzJFNhbGVzDQp4ID0gQ2Fyc2VhdHMkUHJpY2UNCnBsb3QoU2FsZXMgfiBQcmljZSwgZGF0YSA9IENhcnNlYXRzLCBwY2g9MTksIGNleD0wLjgpDQoNCmBgYA0KDQpgYGB7cn0NCiMjICBUaGUgZm9sbG93aW5nIGlzIHRoZSB0cmFuc2xhdGlvbiBvZiB0aGUgZ2l2ZW4gZm9ybXVsYQ0KbiA9IGxlbmd0aCh5KQ0KciA9IGNvcih4LHkpICAgICMgc2FtcGxlIFBlYXJzb24gY29ycmVsYXRpb24gY29lZmZpY2llbnQNCnpyID0gKGxvZygxK3IpLWxvZygxLXIpKS8yDQp6Ljk3NSA9IHFub3JtKDAuOTc1KQ0KTCA9IHpyIC0gei45NzUvc3FydChuLTMpDQpVID0genIgKyB6Ljk3NS9zcXJ0KG4tMykNCkxDSSA9IChleHAoMipMKS0xKS8oZXhwKDIqTCkrMSkNClVDSSA9IChleHAoMipVKS0xKS8oZXhwKDIqVSkrMSkNCkNJID0gY2JpbmQoTENJID0gTENJLCBVQ0kgPSBVQ0kpDQojIyMgQm9vdHN0cmFwIENJDQpCID0gMTAwMCAgICAgICAgICAgICAgIyBUYWtlIDEwMDAgYm9vdHN0cmFwIHNhbXBsZQ0KYnQuciA9IGMoKSAgICAgICAgICAgICMgZW1wdHkgdmVjdG9yIHRvIHN0b3JlIGJvb3RzdHJhcCBjb2VmZmljaWVudA0KZm9yICggaSBpbiAxOkIpew0KICBidC5pZCA9IHNhbXBsZSgxOm4sIG4sIHJlcGxhY2UgPSBUUlVFKSAgICMgYm9vdHN0cmFwcGluZyBvYnNlcnZhdGlvbiBJRHMNCiAgYnQueCA9IHhbYnQuaWRdICAgICAjIGJvb3RzdHJhcCB4ICwgbXVzdCB1c2UgdGhlIGFib3ZlIGJ0LmlkDQogIGJ0LnkgPSB5W2J0LmlkXSAgICAgIyBib290c3RyYXAgeSAsIG11c3QgdXNlIHRoZSBhYm92ZSBidC5pZA0KICBidC5yW2ldID0gY29yKGJ0LngsIGJ0LnkpDQp9DQojIyAyLjUlIGFuZCA5Ny41JSBvZiB0aGUgMTAwMCBib290c3RyYXAgY29ycmVsYXRpb24gY29lZmZpY2llbnRzIGFyZSB0aGUNCiMjIGxvd2VyIGFuZCB1cHBlciBsaW1pdHMgb2YgdGhlIDk1JSBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbA0KYnQuQ0kgPSBxdWFudGlsZShidC5yLCBjKDAuMDI1LCAwLjk3NSkpDQpsaXN0KENJID0gQ0ksIGJ0LkNJID0gYXMudmVjdG9yKGJ0LkNJKSkNCmBgYA0KDQpcDQoNCiMgQm9vdHN0cmFwIENvbmZpZGVuY2UgSW50ZXJ2YWwgb2YgQVVDIGZvciBMb2dpc3RpYyBNb2RlbA0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIGRlbW9uc3RyYXRlIGhvdyB0byBmaW5kIHRoZSBjb25maWRlbmNlIGludGVydmFsIG9mIHRoZSBhcmVhIG9mIHRoZSBST0MgY3VydmUuIFRoaXMgY29uZmlkZW5jZSBpbnRlcnZhbCBpcyBvZiBwcmFjdGljYWwgaW1wb3J0YW5jZS4NCg0KIyMgQm9vdHN0cmFwIFNhbXBsaW5nIGZvciBMb2dpc3RpYyBNb2RlbGluZw0KDQpJbiBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscywgdGhlIHJlc2lkdWFscyBhcmUgbm90IGRlZmluZWQgYXMgdGhvc2UgaW4gdGhlIGxpbmVhciByZWdyZXNzaW9uIChpLmUuLCAkZV9pID0gXHRleHR7b2JzZXJ2ZWQgfXkgLSBcdGV4dHtmaXR0ZWQgfXkkKS4gQm9vdHN0cmFwcGluZyByZXNpZHVhbHMgZG9lcyBub3Qgd29yayBmb3IgbG9naXN0aWMgcmVncmVzc2lvbi4gV2UgY2FuIHVzZSB0aGUgZm9sbG93aW5nIHR3byBzdGVwcyB0byB0YWtlIGJvb3RzdHJhcCBzYW1wbGVzOg0KDQoxLiBUYWtlIGEgYm9vdHN0cmFwIHNhbXBsZSBmcm9tIHRoZSBvYnNlcnZhdGlvbiBJRHMgd2l0aCB0aGUgc2FtZSBzaXplIChhbmQgd2l0aCByZXBsYWNlbWVudCk6ICRcezEsIDIsIDMsIFxjZG90cywgbiBcfSQuIE5vdGUgdGhhdCBzb21lIG9mIHRoZSBJRHMgd2lsbCBiZSBzYW1wbGVkIG11bHRpcGxlIHRpbWVzLg0KDQoyLiBVc2luZyB0aGUgYm9vdHN0cmFwIElEcyB0byBpZGVudGlmeSB0aGUgY29ycmVzcG9uZGluZyByZWNvcmRzIGluIHRoZSBkYXRhIHNldCBhbmQgZGVmaW5lZCBhIG5ldyBkYXRhIHNldCBjYWxsZWQgICoqQm9vdHN0cmFwIHNhbXBsZSoqLiBTb21lIG9mIHRoZSByZWNvcmRzIGFwcGVhciBtdWx0aXBsZSB0aW1lcy4gVGhlc2UgZHVwbGljYXRlIHJlY29yZHMgd2l0aCBiZSBrZXB0IGluIHRoZSBzYW1wbGUuIEluIG90aGVyIHdvcmRzLCB0aGUgZGlzdGluY3QgcmVjb3JkcyBpbiBhIGJvb3RzdHJhcCBzYW1wbGUgYXJlIGxlc3MgdGhhbiB0aGUgc2FtcGxlIHNpemUhDQoNCg0KRm9sbG93aW5nIHRoZSBhYm92ZSBzdGVwcywgd2UgY2FuIHRha2UgbWFueSBib290c3RyYXAgc2FtcGxlcy4gUmVjYWxsIHRoYXQgd2UgY2FuIGJ1aWxkIGEgbG9naXN0aWMgcmVncmVzc2lvbiBmb3IgYSBnaXZlbiBkYXRhIHNldCBhbmQgY29uc3RydWN0IGFuIFJPQyBjdXJ2ZSBhbmQgY2FsY3VsYXRlIHRoZSBhcmVhIHVuZGVyIHRoZSBjdXJ2ZS4gVGhpcyBtZWFucywgaWYgd2UgZHJhdyAkQiA9IDEwMDAkIGJvb3RzdHJhcCBzYW1wbGVzLCB3ZSBjYW4gdGhlbiBidWlsZCAxMDAwIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIChhbHNvIGNhbGxlZCAqKmJvb3RzdHJhcCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyoqKSwgaGVuY2UsIHdpbGwgaGF2ZSAxMDAwICoqYm9vdHN0cmFwIEFVQ3MqKiAob25lIGZvciBlYWNoIGJvb3RzdHJhcCBsb2dpc3RpYyBtb2RlbCkuDQoNClRoaXMgZnVydGhlciBtZWFucyB0aGF0IHRoZSBoaXN0b2dyYW0gb2YgdGhlc2UgKipib290c3RyYXAgQVVDcyoqIGNhbiBiZSBjb25zaWRlcmVkIGFzIHRoZSBzYW1wbGluZyBkaXN0cmlidXRpb24gb2YgdGhlIGFjdHVhbCBzYW1wbGUgKipBVUMqKi4gVGhlICoqOTUlIEJvb3RzdHJhcCBjb25maWRlbmNlIGludGVydmFsKiogb2YgKipBVUMqKiBpcyBkZWZpbmVkIGFzIHRoZSAyLjUlIGFuZCA5Ny41JSBxdWFudGlsZXMgb2YgdGhlICoqYm9vdHN0cmFwIEFVQ3MqKi4NCg0KXA0KDQojIyBDYXNlIFN0dWR5IC0gQ29uZmlkZW5jZSBJbnRlcnZhbCBvZiBBVUMNCg0KV2UgcHJlc2VudCB0d28gZXhhbXBsZXMgc2hvd2luZyBob3cgdG8gY2FsY3VsYXRlIHRoZSBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbCBvZiB0aGUgQVVDIHVzaW5nIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbC4gT25lIGNhbiBhcHBseSB0aGUgc2FtZSBzdGVwcyBmb3IgbmV1cmFsIG5ldCBhbmQgZGVjaXNpb24gdHJlZSBhbGdvcml0aG1zLg0KDQojIyMgUHJlZGljdGluZyBHcmFkdWF0ZSBBZG1pc3Npb24NCg0KV2UgdXNlIHRoZSBmb2xsb3dpbmcgYWRtaXNzaW9uIGRhdGEgc2V0IHRvIGlsbHVzdHJhdGUgdGhlIHN0ZXBzLg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTUsIGZpZy5jYXA9IkZpZ3VyZSA0LiBUaGUgYm9vdHN0cmFwIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiB0aGUgYXJlYSB1bmRlciB0aGUgY3VydmUgb2YgUk9DIChncmFkdWF0ZSBhZG1pc3Npb24gcHJlZGljdGlvbikifQ0KYWRtaXR0ZWQgPSByZWFkLmNzdigiaHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUxL3cxMC93MDktYWRtaXR0ZWQuY3N2IikNCm5vdGFkbWl0dGVkID0gcmVhZC5jc3YoImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1MS93MTAvdzA5LW5vdEFkbWl0dGVkLmNzdiIpDQojIyBhZGQgYW4gYWRtaXNzaW9uIHN0YXR1cyB2YXJpYWJsZSB0byBib3RoIHN1Yi1zYW1wbGVzLg0KYWRtaXR0ZWQkc3RhdHVzID0gInllcyIgICAgICAgICAgICAgICAgICAgICAgICAgIyBhZG1pdHRlZA0Kbm90YWRtaXR0ZWQkc3RhdHVzID0gIm5vICIgICAgICAgICAgICAgICAgICAgICAgIyBub3QgYWRtaXR0ZWQNCmFkbWlzc2lvbiA9IHJiaW5kKGFkbWl0dGVkLCBub3RhZG1pdHRlZCkgICAgICAgICMgY29tYmluaW5nIHRoZSB0d28gc3ViLXNhbXBsZXMNCiMjIERlZmluZSBhbiBlbXB0eSB2ZWN0b3IgdG8gc3RvcmUgYm9vdHN0cmFwIEFVQ3MuDQpidEFVQy52ZWMgPSBjKCkNCiMjIFNlbGVjdCB0aGUgbnVtYmVyIG9mIGJvb3RzdHJhcCBzYW1wbGVzIHRvIGJlIGdlbmVyYXRlZA0KQiA9IDEwMDANCiMjIFNpemUgb2YgdGhlIG9yaWdpbmFsIHNhbXBsZQ0Kc2FtcGxlLnNpemUgPSBkaW0oYWRtaXNzaW9uKVsxXQ0KIyMgVmVjdG9yIG9mIGN1dC1vZmYgcHJvYmFiaWxpdGllcyBmb3IgY29uc3RydWN0IFJPQw0KY3V0Lm9mZi5zZXEgPSBzZXEoMCwxLCBsZW5ndGggPSAxMDApDQojIGJvb3RzdHJhcCBwcm9jZWR1cmUgc3RhcnRzIGhlcmUNCmZvciAoayBpbiAxOkIpew0KICBib290LmlkID0gc2FtcGxlKDE6c2FtcGxlLnNpemUsIHNhbXBsZS5zaXplLCByZXBsYWNlID0gVFJVRSkgICAjIEJvb3RzdHJhcCBJRHMNCiAgYm9vdC5zYW1wbGUgPSBhZG1pc3Npb25bYm9vdC5pZCxdICAgICAgIyBCb290c3RyYXAgc2FtcGxlcyAgICAgICAgIA0KICAjIyBCb290c3RyYXAgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBpcyBnaXZlbiBiZWxvdw0KICBib290LmxvZ2lzdGljID0gZ2xtKGZhY3RvcihzdGF0dXMpfi4sIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gYm9vdC5zYW1wbGUpDQogICMjDQogIHByZWQucHJvYiA9IHByZWRpY3QuZ2xtKGJvb3QubG9naXN0aWMsIG5ld2RhdGEgPSBib290LnNhbXBsZSwgdHlwZSA9ICJyZXNwb25zZSIpDQogICMjIHZlY3RvcnMgdG8gc3RvcmUgc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5DQogIHNlbnNpdGl2aXR5LnZlYyA9IE5VTEwNCiAgc3BlY2lmaWNpdHkudmVjID0gTlVMTA0KICAgIGZvciAoaSBpbiAxOjEwMCl7DQogICBwcmVkLnN0YXR1cyA9IGFzLm51bWVyaWMocHJlZC5wcm9iID4gY3V0Lm9mZi5zZXFbaV0pDQogICAjIyMgY29tcG9uZW50cyBmb3IgZGVmaW5pbmcgdmFyaW91cyBtZWFzdXJlcw0KICAgVE4gPSBzdW0ocHJlZC5zdGF0dXMgPT0gMCAmIGJvb3Quc2FtcGxlJHN0YXR1cyA9PSAibm8gIikNCiAgIEZOID0gc3VtKHByZWQuc3RhdHVzID09IDAgJiBib290LnNhbXBsZSRzdGF0dXMgPT0gInllcyIpDQogICBGUCA9IHN1bShwcmVkLnN0YXR1cyA9PSAxICYgYm9vdC5zYW1wbGUkc3RhdHVzID09ICJubyAiKQ0KICAgVFAgPSBzdW0ocHJlZC5zdGF0dXMgPT0gMSAmIGJvb3Quc2FtcGxlJHN0YXR1cyA9PSAieWVzIikNCiAgICMjIw0KICAgc2Vuc2l0aXZpdHkudmVjW2ldID0gVFAgLyAoVFAgKyBGTikNCiAgIHNwZWNpZmljaXR5LnZlY1tpXSA9IFROIC8gKFROICsgRlApDQogIH0NCiAgb25lLm1pbnVzLnNwZWMgPSAxIC0gc3BlY2lmaWNpdHkudmVjDQogIHNlbnMudmVjID0gc2Vuc2l0aXZpdHkudmVjDQogICMjIEEgYmV0dGVyIGFwcHJveCBvZiBST0MsIG5lZWQgbGlicmFyeSB7cFJPQ30NCiAgcHJlZGljdGlvbiA9IHByZWQucHJvYg0KICBjYXRlZ29yeSA9IGJvb3Quc2FtcGxlJHN0YXR1cyA9PSAieWVzIg0KICBST0NvYmogPC0gcm9jKGNhdGVnb3J5LCBwcmVkaWN0aW9uKQ0KICBidEFVQy52ZWNba10gPSByb3VuZChhdWMoUk9Db2JqKSw0KQ0KfQ0KaGlzdChidEFVQy52ZWMsIHhsYWIgPSAiQm9vdHN0cmFwIEFVQyIsIG1haW4gPSAiQm9vdHN0cmFwIFNhbXBsaW5nIERpc3RyaWJ1dGlvbiBvZiBBVUNzIFxuIChBZG1pc3Npb24gUHJlZGljdGlvbiBNb2RlbCkiKQ0KYGBgDQogIA0KIFRoZSA5NSUgYm9vdHN0cmFwIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb2YgdGhlIEFVQyBpcyBkZWZpbmVkIHRvIGJlIDIuNSUgYW5kIDk3LjUlIHF1YW50aWxlcyBvZiB0aGUgYm9vdHN0cmFwIEFVQ3MuDQogDQpgYGB7cn0NCnBhbmRlcihxdWFudGlsZShidEFVQy52ZWMsIGMoMC4wMjUsIDAuOTc1KSkpDQpgYGANClRoZSBjb25maWRlbmNlIGludGVydmFsIG9mIEFVQyBjYW4gYmUgdXNlZCBmb3IgdmFyaWFibGUgc2VsZWN0aW9uOiBpZiB0d28gY29uZmlkZW5jZSBpbnRlcnZhbHMgb2YgQVVDIG92ZXJsYXBwZWQsIHRoZSBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlcyBvZiB0aGUgdHdvIGNvcnJlc3BvbmRpbmcgcHJlZGljdGl2ZSBtb2RlbHMgYXJlIG5vdCBzaWduaWZpY2FudGx5IGRpZmZlcmVudC4gQSBzaW1wbGVyIG1vZGVsIHNob3VsZCBiZSByZWNvbW1lbmRlZCBmb3IgaW1wbGVtZW50YXRpb24uIA0KDQoNClwNCg0KIyMjIEZyYXVkIERldGVjdGlvbiBEYXRhDQoNCldlIHVzZSB0aGUgZnJhdWQgaW5kZXggZGF0YSBhdmFpbGFibGUgb24gdGhlIGluc3RydWN0b3IncyB0ZWFjaGluZyBkYXRhIHJlcG9zaXRvcnkgdG8gY29uc3RydWN0IHRoZSAkOTVcJSQgY29uZmlkZW5jZSBpbnRlcnZhbCBvZiB0aGUgQVVDLiBUaGlzIGlzIGEgc2ltcGxlIGRhdGEgc2V0IHdpdGggb25seSB0d28gdmFyaWFibGVzLiBXZSBmaXJzdCBwcmVwYXJlIHRoZSBkYXRhIHNldCBmb3IgdGhlIHN1YnNlcXVlbnQgbW9kZWxpbmcuDQoNCmBgYHtyfQ0KZnJhdWQuZGF0YSA9IHJlYWQuY3N2KCJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9kYXRhc2V0cy9GcmF1ZEluZGV4L2ZyYXVkaWR4LmNzdiIpWywtMV0NCiMjIHJlY29kZSBzdGF0dXMgdmFyaWFibGU6IGJhZCA9IDEgYW5kIGdvb2QgPSAwDQpnb29kLmlkID0gd2hpY2goZnJhdWQuZGF0YSRzdGF0dXMgPT0gIiBnb29kIikgDQpiYWQuaWQgPSB3aGljaChmcmF1ZC5kYXRhJHN0YXR1cyA9PSAiZnJhdWQiKQ0KIyMNCmZyYXVkLmRhdGEkZnJhdWQuc3RhdHVzID0gMA0KZnJhdWQuZGF0YSRmcmF1ZC5zdGF0dXNbYmFkLmlkXSA9IDENCmBgYA0KDQoNCk5leHQsIHdlIHBlcmZvcm0gYm9vdHN0cmFwIGxvZ2lzdGljIHJlZ3Jlc3Npb24gd2l0aCAxMDAwIGJvb3RzdHJhcCBzYW1wbGVzIGFuZCBidWlsZCAxMDAwIGJvb3RzdHJhcCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyBhbmQgY2FsY3VsYXRlIHRoZSBBVUMgb2YgdGhlIFJPQyBvZiB0aGUgY29ycmVzcG9uZGluZyBib290c3RyYXAgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMuIA0KDQpgYGB7fQ0KIyMgRGVmaW5lIGFuIGVtcHR5IHZlY3RvciB0byBzdG9yZSBib290c3RyYXAgQVVDcy4NCmJ0QVVDLnZlYyA9IGMoKQ0KIyMgc2VsZWN0IHRoZSBudW1iZXIgb2YgYm9vdHN0cmFwIHNhbXBsZXMgdG8gYmUgZ2VuZXJhdGVkDQpCID0gMTAwMA0KIyMgU2l6ZSBvZiB0aGUgb3JpZ2luYWwgc2FtcGxlDQpzYW1wbGUuc2l6ZSA9IGRpbShmcmF1ZC5kYXRhKVsxXQ0KIyMgVmVjdG9yIG9mIGN1dC1vZmYgcHJvYmFiaWxpdGllcyBmb3IgY29uc3RydWN0IFJPQw0KY3V0Lm9mZi5zZXEgPSBzZXEoMCwxLCBsZW5ndGggPSAxMDApDQojIGJvb3RzdHJhcCBwcm9jZWR1cmUgc3RhcnRzIGhlcmUNCmZvciAoayBpbiAxOkIpew0KICBib290LmlkID0gc2FtcGxlKDE6c2FtcGxlLnNpemUsIHNhbXBsZS5zaXplLCByZXBsYWNlID0gVFJVRSkgICAjIEJvb3RzdHJhcCBJRHMNCiAgYm9vdC5zYW1wbGUgPSBmcmF1ZC5kYXRhW2Jvb3QuaWQsXSAgICAgICMgQm9vdHN0cmFwIHNhbXBsZXMgICAgICAgICANCiAgIyMgQm9vdHN0cmFwIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgaXMgZ2l2ZW4gYmVsb3cNCiAgYm9vdC5sb2dpc3RpYyA9IGdsbShmYWN0b3Ioc3RhdHVzKSB+IGluZGV4LCBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IGJvb3Quc2FtcGxlKQ0KICAjIw0KICBuZXdkYXRhID0gZGF0YS5mcmFtZShpbmRleD0gYm9vdC5zYW1wbGUkaW5kZXgpIA0KICBwcmVkLnByb2IgPSBwcmVkaWN0LmdsbShib290LmxvZ2lzdGljLCBuZXdkYXRhLCB0eXBlID0gInJlc3BvbnNlIikNCiAgIyMgdmVjdG9ycyB0byBzdG9yZSBzZW5zaXRpdml0eSBhbmQgc3BlY2lmaWNpdHkNCiAgc2Vuc2l0aXZpdHkudmVjID0gTlVMTA0KICBzcGVjaWZpY2l0eS52ZWMgPSBOVUxMDQogIGZvciAoaSBpbiAxOjEwMCl7DQogICBwcmVkLnN0YXR1cyA9IGFzLm51bWVyaWMocHJlZC5wcm9iID4gY3V0Lm9mZi5zZXFbaV0pDQogICAjIyMgY29tcG9uZW50cyBmb3IgZGVmaW5pbmcgdmFyaW91cyBtZWFzdXJlcw0KICAgVE4gPSBzdW0ocHJlZC5zdGF0dXMgPT0gMCAmIGJvb3Quc2FtcGxlJGZyYXVkLnN0YXR1cyA9PSAwKQ0KICAgRk4gPSBzdW0ocHJlZC5zdGF0dXMgPT0gMCAmIGJvb3Quc2FtcGxlJGZyYXVkLnN0YXR1cyA9PSAxKQ0KICAgRlAgPSBzdW0ocHJlZC5zdGF0dXMgPT0gMSAmIGJvb3Quc2FtcGxlJGZyYXVkLnN0YXR1cyA9PSAwKQ0KICAgVFAgPSBzdW0ocHJlZC5zdGF0dXMgPT0gMSAmIGJvb3Quc2FtcGxlJGZyYXVkLnN0YXR1cyA9PSAxKQ0KICAgIyMjDQogICBzZW5zaXRpdml0eS52ZWNbaV0gPSBUUCAvIChUUCArIEZOKQ0KICAgc3BlY2lmaWNpdHkudmVjW2ldID0gVE4gLyAoVE4gKyBGUCkNCiAgfQ0KICBvbmUubWludXMuc3BlYyA9IDEgLSBzcGVjaWZpY2l0eS52ZWMNCiAgc2Vucy52ZWMgPSBzZW5zaXRpdml0eS52ZWMNCiAgIyMgQSBiZXR0ZXIgYXBwcm94IG9mIFJPQywgbmVlZCBsaWJyYXJ5IHtwUk9DfQ0KICBwcmVkaWN0aW9uID0gcHJlZC5wcm9iDQogIGNhdGVnb3J5ID0gYm9vdC5zYW1wbGUkZnJhdWQuc3RhdHVzID09IDENCiAgUk9Db2JqIDwtIHJvYyhjYXRlZ29yeSwgcHJlZGljdGlvbikNCiAgYnRBVUMudmVjW2tdID0gcm91bmQoYXVjKFJPQ29iaiksNCkNCn0NCmhpc3QoYnRBVUMudmVjLCB4bGFiID0gIkJvb3RzdHJhcCBBVUMiLCBtYWluID0gIkJvb3RzdHJhcCBTYW1wbGluZyBEaXN0cmlidXRpb24gDQpvZiBBVUNzIFxuIChGcmF1ZCBEZXRlY3Rpb24gTW9kZWwpIikNCmBgYA0KDQoNCmBgYHtyIGVjaG89RkFMU0UsIGZpZy5hbGlnbiA9ImNlbnRlciIsIGZpZy5jYXA9IkZpZ3VyZSA1LiBCb290c3RyYXAgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mIEFVQyAoZnJhdWQgcHJlZGljdGlvbikiLCBvdXQud2lkdGg9IjgwJSIsIGRldj0ianBlZyJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvdzA5LUJvb3RzdHJhcEFVQ0ZyYXVkUHJlZC5qcGciKQ0KYGBgDQoNCiAgDQpUaGUgOTUlIGJvb3RzdHJhcCBjb25maWRlbmNlIGludGVydmFsIG9mIHRoZSBBVUMgaXMgZGVmaW5lZCB0byBiZSAyLjUlIGFuZCA5Ny41JSBxdWFudGlsZXMgb2YgdGhlIGJvb3RzdHJhcCBBVUNzLg0KDQpXaXRoIHRoZSBib290c3RyYXAgQVVDcywgd2UgY2FuIHNpbWlsYXJseSBjb25zdHJ1Y3QgdGhlIDk1JSBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIEFVQyBvZiB0aGUgZnJhdWQgZGV0ZWN0aW9uIG1vZGVsIGlzICQoMC45MjM0LCAwLjkzMTMgKSQuDQoNClwNCg0KIyBDb25jZXB0cyBvZiBFbnNlbWJsZSBBbGdvcml0aG1zDQoNCldlIGhhdmUgdGFrZW4gbWFueSBib290c3RyYXAgc2FtcGxlcyBhbmQgYnVpbHQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIG9uIGVhY2ggb2YgdGhlc2UgYm9vdHN0cmFwIHNhbXBsZXMgYW5kIGNhbGN1bGF0ZSB0aGUgYXJlYSB1bmRlciB0aGUgUk9DIGN1cnZlIG9mIGFzc29jaWF0ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMuIFVzaW5nIHRoZXNlIGJvb3RzdHJhcCBBVUNzLCB3ZSBhcHByb3hpbWF0ZSB0aGUgZmluZCB0aGUgYm9vdHN0cmFwIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiB0aGUgQVVDIGFuZCwgaGVuY2UsIGZpbmQgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb2YgdGhlIEFVQy4NCg0KV2UgY2FuIGZvbGxvdyB0aGUgc2FtZSBzdGVwcyB0byBmaW5kIHRoZSBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbHMgb2YgdGhlIEFVQ3Mgb2YgZGVjaXNpb24gdHJlZXMgYW5kIG5ldXJhbCBuZXQgYWxnb3JpdGhtcy4gT25lIGltcG9ydGFudCBhcHBsaWNhdGlvbiBvZiBCb290c3RyYXAgaW4gbWFjaGluZSBsZWFybmluZyBpcyBpdHMgYWJpbGl0eSB0byBhZ2dyZWdhdGUgYSBzZXQgb2YgKndlYWsqIG1vZGVscyBhbmQgYWxnb3JpdGhtcyB0byBtYWtlIGEgKnN0cm9uZ2VyKiBjb21iaW5lZCBtb2RlbCAtIFRoaXMgaXMgdGhlIHNvLWNhbGxlZCAqKmVuc2VtYmxlKiogbGVhcm5pbmcgbWV0aG9kIGluIG1hY2hpbmUgbGVhcm5pbmcuIE9uZSB3YXkgb2YgaW1wcm92aW5nIHRoZSBwZXJmb3JtYW5jZSBvZiBhICp3ZWFrIG1vZGVsKiB0aHJvdWdoIGFuIGVuc2VtYmxlIGFwcHJvYWNoIGlzIHRvIHJlZHVjZSB0aGUgcmlzayBvZiAqKm92ZXJmaXR0aW5nKiogYW5kICoqdW5kZXJmaXR0aW5nKiogYnkgYmFsYW5jaW5nIHRoZSB0cmFkZS1vZmYgYmV0d2VlbiAqKmJpYXMqKiBhbmQgKip2YXJpYW5jZSoqLCANCg0KIyMgT3ZlcmZpdHRpbmcgdi5zLiBVbmRlcmZpdHRpbmcNCg0KSW4gcHJlZGljdGl2ZSBtb2RlbGluZywgKipiaWFzKiogaXMgYSBwaGVub21lbm9uIHRoYXQgc2tld3MgdGhlIHJlc3VsdCBvZiBhbiBhbGdvcml0aG0gaW4gZmF2b3Igb2Ygb3IgYWdhaW5zdCB0aGUgZ3JvdW5kIHRydXRoLiBJdCBkZXNjcmliZXMgaG93IHdlbGwgdGhlIG1vZGVsIG1hdGNoZXMgdGhlIHRyYWluaW5nIGRhdGEgc2V0Og0KDQoqIEEgbW9kZWwgd2l0aCBhIGhpZ2hlciBiaWFzIHdvdWxkIG5vdCBtYXRjaCB0aGUgZGF0YSBzZXQgY2xvc2VseS4NCiogQSBsb3ctYmlhcyBtb2RlbCB3aWxsIGNsb3NlbHkgbWF0Y2ggdGhlIHRyYWluaW5nIGRhdGEgc2V0Lg0KDQoNCioqVmFyaWFuY2UqKiByZWZlcnMgdG8gdGhlIGNoYW5nZXMgaW4gdGhlIG1vZGVsIHdoZW4gdXNpbmcgZGlmZmVyZW50IHBvcnRpb25zIG9mIHRoZSB0cmFpbmluZyBkYXRhIHNldC4gVGhlIHZhcmlhbmNlIGNvbWVzIGZyb20gaGlnaGx5IGNvbXBsZXggKGJ1dCB2YWxpZCkgbW9kZWxzIHdpdGggYSBsYXJnZSBudW1iZXIgb2YgZmVhdHVyZXMNCg0KKiBNb2RlbHMgd2l0aCBoaWdoIGJpYXMgd2lsbCBoYXZlIGxvdyB2YXJpYW5jZS4NCiogTW9kZWxzIHdpdGggaGlnaCB2YXJpYW5jZSB3aWxsIGhhdmUgYSBsb3cgYmlhcy4NCg0KVGhlIHRlcm1zICoqdW5kZXJmaXR0aW5nKiogYW5kICoqb3ZlcmZpdHRpbmcqKiByZWZlciB0byBob3cgdGhlIG1vZGVsIGZhaWxzIHRvIG1hdGNoIHRoZSBkYXRhLiANCg0KKiAqKlVuZGVyZml0dGluZyoqIG9jY3VycyB3aGVuIHRoZSBtb2RlbCBpcyB0b28gc2ltcGxlIHRvIGJlIGFibGUgdG8gbWF0Y2ggdGhlIGlucHV0IGRhdGEgdG8gdGhlIHRhcmdldCBkYXRhLiANCg0KKiAqKk92ZXJmaXR0aW5nKiogb2NjdXJzIHdoZW4gdGhlIG1vZGVsIGlzIGhpZ2hseSBjb21wbGV4IGJ1dCBwZXJmZWN0bHkgbWF0Y2hlcyBhbG1vc3QgYWxsIHRoZSBnaXZlbiBkYXRhIHBvaW50cyBhbmQgcGVyZm9ybXMgd2VsbCBpbiB0cmFpbmluZyBkYXRhIHNldHMuIEhvd2V2ZXIsIHRoZSBtb2RlbCB3b3VsZCBub3QgYmUgYWJsZSB0byBnZW5lcmFsaXplIHRoZSBkYXRhIHBvaW50IGluIHRoZSB0ZXN0IGRhdGEgc2V0IHRvIHByZWRpY3QgdGhlIG91dGNvbWUgYWNjdXJhdGVseSBkdWUgdG8gaGlnaCBlcnJvciAodmFyaWF0aW9uKS4NCg0KDQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuYWxpZ24gPSJjZW50ZXIiLCBmaWcuY2FwPSJGaWd1cmUgNi4gQmlhcywgdmFyaWFuY2UsIG92ZXJmaXR0aW5nIGFuZCB1bmRlcmZpdHRpbmcgb2YgcHJlZGljdGl2ZSBtb2RlbHMiLCBvdXQud2lkdGg9IjgwJSIsIGRldj0ianBlZyJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvdzA5LUJpYXNWYXJpYW5jZU92ZXJVbmRlckZpdHRpbmcuanBnIikNCmBgYA0KDQoNCiMjIFRoZSBMb2dpYyBvZiBFbnNlbWJsZSBMZWFybmluZw0KDQoqKkVuc2VtYmxlIGxlYXJuaW5nKiogYXR0ZXN0cyB0byB0aGUgaWRlYSBvZiB0aGUg4oCcd2lzZG9tIG9mIGNyb3dkcyzigJ0gd2hpY2ggc3VnZ2VzdHMgdGhhdCB0aGUgZGVjaXNpb24tbWFraW5nIG9mIGEgbGFyZ2VyIGdyb3VwIG9mIHBlb3BsZSBpcyB0eXBpY2FsbHkgYmV0dGVyIHRoYW4gdGhhdCBvZiBhbiBpbmRpdmlkdWFsIGV4cGVydC4gU2ltaWxhcmx5LCBlbnNlbWJsZSBsZWFybmluZyByZWZlcnMgdG8gYSBncm91cCAob3IgZW5zZW1ibGUpIG9mIGJhc2UgbGVhcm5lcnMgKGVnLiwgbW9kZWxzIGFuZCBhbGdvcml0aG1zKSwgd2hpY2ggd29yayBjb2xsZWN0aXZlbHkgdG8gYWNoaWV2ZSBhIGJldHRlciBmaW5hbCBwcmVkaWN0aW9uLiANCg0KQSBzaW5nbGUgbW9kZWwgb3IgYWxnb3JpdGhtLCBhbHNvIGtub3duIGFzIGEgYmFzZSBvciB3ZWFrIGxlYXJuZXIsIG1heSBub3QgcGVyZm9ybSB3ZWxsIGluZGl2aWR1YWxseSBkdWUgdG8gaGlnaCB2YXJpYW5jZSBvciBoaWdoIGJpYXMuIEhvd2V2ZXIsIHdoZW4gd2VhayBsZWFybmVycyBhcmUgYWdncmVnYXRlZCwgdGhleSBjYW4gZm9ybSBhIHN0cm9uZyBsZWFybmVyICh3aXRoIHN0YWJsZSBwZXJmb3JtYW5jZSBhbmQgbG93IHZhcmlhbmNlKSB5aWVsZGluZyBiZXR0ZXIgbW9kZWwgcGVyZm9ybWFuY2UuDQoNCk1hbnkgZGlmZmVyZW50IHR5cGVzIG9mIGVuc2VtYmxlIGxlYXJuaW5nIG1ldGhvZHMgaGF2ZSBiZWVuIGRldmVsb3BlZCBpbiB0aGUgcGFzdCBmZXcgZGVjYWRlcywgQW1vbmcgdGhlbSwgYm9vc3RpbmcgYW5kIEJvb3RzdHJhcCBhZ2dyZWdhdGlvbiAoQkFHR0lORykgbWV0aG9kcyBhcmUgY29tbW9ubHkgdXNlZCBpbiBwcmFjdGljZS4NCg0KXA0KDQojIyBCQUdHSU5HIEVuc2VtYmxlIE1ldGhvZHMNCg0KKipCYWdnaW5nKiogaXMgYW4gYWNyb255bSBmb3IgKkJvb3RzdHJhcCBBZ2dyZWdhdGlvbiogYW5kIGlzIHVzZWQgdG8gZGVjcmVhc2UgdGhlIHZhcmlhbmNlIGluIHRoZSBwcmVkaWN0aW9uIG1vZGVsICh0aGUgaWRlYSB3ZSBhZG9wdGVkIHdoZW4gaWRlbnRpZnlpbmcgdGhlIG9wdGltYWwgY3V0LW9mZiBzY29yZXMgYW5kIHRoZSBjb25maWRlbmNlIGludGVydmFsIG9mIEFVQykuIA0KDQoNCioqQmFnZ2luZyoqIGlzIGEgPGZvbnQgY29sb3IgPSAiYmx1ZSI+KipcY29sb3J7Ymx1ZX1wYXJhbGxlbCoqPC9mb250PiBtZXRob2QgdGhhdCBmaXRzIDxmb250IGNvbG9yID0gImJsdWUiPioqXGNvbG9ye2JsdWV9ZGlmZmVyZW50Kio8L2ZvbnQ+LCBjb25zaWRlcmVkIGxlYXJuZXJzIDxmb250IGNvbG9yID0gImJsdWUiPioqXGNvbG9ye2JsdWV9aW5kZXBlbmRlbnRseSoqPC9mb250PiBmcm9tIGVhY2ggb3RoZXIsIG1ha2luZyBpdCBwb3NzaWJsZSB0byB0cmFpbiB0aGVtIDxmb250IGNvbG9yID0gImJsdWUiPioqXGNvbG9ye3JlZH1zaW11bHRhbmVvdXNseSoqPC9mb250Pi4NCg0KKipCYWdnaW5nKiogZ2VuZXJhdGVzIGFkZGl0aW9uYWwgZGF0YSBmb3IgdHJhaW5pbmcgZnJvbSB0aGUgZGF0YSBzZXQuIFRoaXMgaXMgYWNoaWV2ZWQgYnkgPGZvbnQgY29sb3IgPSAicmVkIj4qKlxjb2xvcntyZWR9Ym9vdHN0cmFwIHNhbXBsaW5nKio8L2ZvbnQ+IChyYW5kb20gc2FtcGxpbmcgd2l0aCByZXBsYWNlbWVudCkgZnJvbSB0aGUgb3JpZ2luYWwgZGF0YSBzZXQuIEFzIGRpc2N1c3NlZCBpbiBlYXJsaWVyIHNlY3Rpb25zLCAgKipCb290c3RyYXAgU2FtcGxpbmcqKiBtYXkgcmVwZWF0IHNvbWUgb2JzZXJ2YXRpb25zIGluIGVhY2ggbmV3IHRyYWluaW5nIGRhdGEgc2V0LiBUaGlzIGd1YXJhbnRlZXMgdGhhdCBldmVyeSBlbGVtZW50IGluICoqQmFnZ2luZyoqIGlzIGVxdWFsbHkgcHJvYmFibGUgZm9yIGFwcGVhcmluZyBpbiBhICoqYm9vdHN0cmFwKiogZGF0YSBzZXQuIA0KDQpUaGVzZSBib290c3RyYXAgZGF0YSBzZXRzIGFyZSB1c2VkIHRvIHRyYWluIG11bHRpcGxlIG1vZGVscyBpbiAqKnBhcmFsbGVsKiouIFRoZSBhdmVyYWdlIG9mIGFsbCB0aGUgcHJlZGljdGlvbnMgZnJvbSBkaWZmZXJlbnQgZW5zZW1ibGUgbW9kZWxzIGlzIGNhbGN1bGF0ZWQuIFRoZSAqKm1ham9yaXR5IHZvdGUqKiBnYWluZWQgZnJvbSB0aGUgdm90aW5nIG1lY2hhbmlzbSBpcyBjb25zaWRlcmVkIHdoZW4gY2xhc3NpZmljYXRpb24gaXMgbWFkZS4gPGZvbnQgY29sb3IgPSAicmVkIj4qXGNvbG9ye3JlZH1CYWdnaW5nIGRlY3JlYXNlcyB0aGUgdmFyaWFuY2UgYW5kIHR1bmVzIHRoZSBwcmVkaWN0aW9uIHRvIGFuIGV4cGVjdGVkIG91dGNvbWUqPC9mb250Pi4NCg0KVGhlIGZvbGxvd2luZyBmaWd1cmUgZXhwbGFpbnMgdGhlIEJBR0dJTkcgYWxnb3JpdGhtIGFwcGxpZWQgdG8gdGhlIGRlY2lzaW9uIHRyZWUgYWxnb3JpdGhtLg0KDQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iODAlIiwgZmlnLmNhcD0iRmlndXJlIDcuIFRoZSBpZGVhIG9mIEdhZ2dpbmcgZW5zZW1ibGUgYWxnb3JpdGhtLiJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvdzA5LUJBR0dJTkcuanBnIikNCmBgYA0KDQpJbiBzdW1tYXJ5LCB0aGUgYmFnZ2luZyBhbGdvcml0aG0gaGFzIHRocmVlIGJhc2ljIHN0ZXBzOg0KDQoqKkJvb3RzdHJhcHBpbmcqKjogIEJhZ2dpbmcgbGV2ZXJhZ2VzIGEgYm9vdHN0cmFwcGluZyBzYW1wbGluZyB0ZWNobmlxdWUgdG8gY3JlYXRlIGRpdmVyc2Ugc2FtcGxlcy4gDQoNCiogKipQYXJhbGxlbCB0cmFpbmluZyoqOiBUaGVzZSBib290c3RyYXAgc2FtcGxlcyBhcmUgdGhlbiB0cmFpbmVkIGluZGVwZW5kZW50bHkgYW5kIGluIHBhcmFsbGVsIHdpdGggZWFjaCBvdGhlciB1c2luZyB3ZWFrIG9yIGJhc2UgbGVhcm5lcnMgKGRlKS4NCg0KKiAqKkFnZ3JlZ2F0aW9uKio6IEZpbmFsbHksIGFnZ3JlZ2F0aW5nIHRoZSBvdXRwdXRzIGZyb20gaW5kaXZpZHVhbCBhbGdvcml0aG1zL21vZGVscy4gSW4gdGhlIGNhc2Ugb2YgdXNpbmcgYSB0cmVlIGFsZ29yaXRobSAobGlrZSB0aGUgYWJvdmUgZmlndXJlKQ0KICArIDxmb250IGNvbG9yID0gInJlZCI+Klxjb2xvcntyZWR9Zm9yIHJlZ3Jlc3Npb24qPC9mb250PiwgYW4gYXZlcmFnZSBvZiBhbGwgdGVybWluYWwgbm9kZSB3ZWlnaHRzIGlzIHRha2VuIG9mIGFsbCB0aGUgb3V0cHV0cyBwcmVkaWN0ZWQgYnkgdGhlIGluZGl2aWR1YWwgY2xhc3NpZmllcnM7IHRoaXMgaXMga25vd24gYXMgKipzb2Z0IHZvdGluZyoqLiANCiAgKyA8Zm9udCBjb2xvciA9ICJyZWQiPipcY29sb3J7cmVkfWZvciBjbGFzc2lmaWNhdGlvbio8L2ZvbnQ+LCB0aGUgY2xhc3MgKGRlZmluZWQgd2l0aCBkZWZhdWx0IDAuNSBpbiBlYWNoIGluZGl2aWR1YWwgdHJlZSkgd2l0aCB0aGUgaGlnaGVzdCBtYWpvcml0eSBvZiB2b3RlcyBpcyBhY2NlcHRlZDsgdGhpcyBpcyBrbm93biBhcyAqKmhhcmQgdm90aW5nIG9yIG1ham9yaXR5IHZvdGluZyoqLg0KDQoNClwNCg0KKipDYXNlIFN0dWR5OiBCQUdHSU5HIENsYXNzaWZpY2F0aW9uIFRyZWVzKioNCg0KDQoqKkJBR0dJTkcqKiBpcyBpbXBsZW1lbnRlZCBpbiBSIGxpYnJhcmllcyAqKmlwcmVke30qKiBhbmQgKipycGFydHt9KiouIFRoZXkgYXJlIHVzdWFsbHkgdXNlZCB3aXRoIHNldmVyYWwgb3RoZXIgbGlicmFyaWVzIHN1Y2ggYXMgKipjYXJldHt9KiogYW5kICoqZTEwNzF7fSoqIHRvIGV4dHJhY3QgcmVsZXZhbnQgaW5mb3JtYXRpb24gaW4gdGhlIGJhZ2dpbmcgYWxnb3JpdGhtLg0KDQpgYGB7cn0NClBpbWEgPSByZWFkLmNzdigiaHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUxL3cxMC9BbmFseXRpY1BpbWFEaWFiZXRlcy5jc3YiKVssLTFdDQojIFdlIHVzZSBhIHJhbmRvbSBzcGxpdCBhcHByb2FjaA0KbiA9IGRpbShQaW1hKVsxXSAgIyBzYW1wbGUgc2l6ZQ0KIyBjYXV0aW9uOiB1c2luZyB3aXRob3V0IHJlcGxhY2VtZW50DQp0cmFpbi5pZCA9IHNhbXBsZSgxOm4sIHJvdW5kKDAuNypuKSwgcmVwbGFjZSA9IEZBTFNFKSAgDQp0cmFpbiA9IFBpbWFbdHJhaW4uaWQsIF0gICAgIyB0cmFpbmluZyBkYXRhDQp0ZXN0ID0gUGltYVstdHJhaW4uaWQsIF0gICAgIyB0ZXN0aW5nIGRhdGENCmBgYA0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTQsIGZpZy5jYXA9IkZpZ3VyZSA4LiBUaGUgUk9DIGN1cnZlIGFuZCB0aGUgcGxvdCBmb3Igb3B0aW1hbCBjdXQtb2ZmIGRldGVybWluYXRpb24uIn0NCiMjDQpEaWFiZXRlcy5iYWcudHJhaW4gPC0gYmFnZ2luZyhhcy5mYWN0b3IoZGlhYmV0ZXMpIH4gLiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmJhZ2cgPSAxNTAsICAgICMgbnVtYmVyIG9mIHRyZWVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb29iID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJtcyA9IGxpc3QobG9zcyA9IG1hdHJpeChjKDAsIDEwLCAxLCAwKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDIwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieXJvdyA9IFRSVUUpLCAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwbGl0ID0gImdpbmkiKSwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2wobWluc3BsaXQgPSAxMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcCA9IDAuMDIpKQ0KIyMjIHByZWRpY3QoKSByZXR1cm5zIGVpdGhlciAiY2xhc3MiIG9yICJwcm9iIiBpbiB0aGUgY2xhc3NpZmljYXRpb24NCiMjIyBXaGVuIHNwZWNpZnlpbmcgdHlwZSA9ICJjbGFzcyIsIHRoZSBkZWZhdWx0IGN1dC1vZmYgb2YgMC41IGlzIHVzZWQuDQpwcmVkID0gcHJlZGljdChEaWFiZXRlcy5iYWcudHJhaW4sIHRyYWluLCB0eXBlID0gInByb2IiKQ0KIyMjIE9wdGltYWwgY3V0LW9mZiBwcm9iYWJpbGl0eSBpZGVudGlmaWNhdGlvbjogbm8gY3Jvc3MtdmFsaWRhdGlvbiBpcyBuZWVkZWQNCmN1dC5wcm9iID0gc2VxKDAsMSwgbGVuZ3RoID0gMjApDQogIHNlbnNwZS5tdHggPSBtYXRyaXgoMCwgbmNvbCA9IGxlbmd0aChjdXQucHJvYiksIG5yb3c9IDMsIGJ5cm93ID0gRkFMU0UpDQogIGZvciAoaSBpbiAxOmxlbmd0aChjdXQucHJvYikpew0KICAjIENBVVRJT046ICJwb3MiIGFuZCAibmVnIiBhcmUgdmFsdWVzIG9mIHRoZSBsYWJlbCBpbiB0aGlzIGRhdGEgc2V0IQ0KICAjIFRoZSBmb2xsb3dpbmcgbGluZSB1c2VzIG9ubHkgInBvcyIgcHJvYmFiaWxpdHk6IHByZWRbLCAicG9zIl0gISEhIQ0KICBwcmVkLm91dCA9ICBpZmVsc2UocHJlZFssInBvcyJdID49IGN1dC5wcm9iW2ldLCAicG9zIiwgIm5lZyIpICANCiAgVFAgPSBzdW0ocHJlZC5vdXQgPT0icG9zIiAmIHRyYWluJGRpYWJldGVzID09ICJwb3MiKQ0KICBUTiA9IHN1bShwcmVkLm91dCA9PSJuZWciICYgdHJhaW4kZGlhYmV0ZXMgPT0gIm5lZyIpDQogIEZQID0gc3VtKHByZWQub3V0ID09InBvcyIgJiB0cmFpbiRkaWFiZXRlcyA9PSAibmVnIikNCiAgRk4gPSBzdW0ocHJlZC5vdXQgPT0ibmVnIiAmIHRyYWluJGRpYWJldGVzID09ICJwb3MiKQ0KICBzZW5zcGUubXR4WzEsaV0gPSBUUC8oVFAgKyBGTikgICAgICAgICAgICAgICAgICAjIHNlbnNpdGl2aXR5DQogIHNlbnNwZS5tdHhbMixpXSA9IFROLyhUTiArIEZQKSAgICAgICAgICAgICAgICAgICMgc3BlY2lmaWNpdHkNCiAgc2Vuc3BlLm10eFszLGldID0gKFRQK1ROKS8oVFAgKyBGTiArIFROICsgRlApICAgIyBhY2N1cmFjeQ0KICB9DQogICMjIEEgYmV0dGVyIGFwcHJveCBvZiBST0MsIG5lZWQgbGlicmFyeSB7cFJPQ30NCiAgcHJlZGljdGlvbiA9IHByZWRbLCAicG9zIl0NCiAgY2F0ZWdvcnkgPSB0cmFpbiRkaWFiZXRlcyA9PSAicG9zIg0KICBST0NvYmogPC0gcm9jKGNhdGVnb3J5LCBwcmVkaWN0aW9uKQ0KICBBVUMgPSBhdWMoUk9Db2JqKQ0KICBBVUMgPSByb3VuZChhcy52ZWN0b3IoQVVDWzFdKSwzKQ0KICAjIyMNCiAgbiA9IGxlbmd0aChzZW5zcGUubXR4WzMsXSkNCiAgaWR4ID0gd2hpY2goc2Vuc3BlLm10eFszLF0gPT0gbWF4KHNlbnNwZS5tdHhbMyxdKSkNCiAgdGljay5sYWJlbCA9IGFzLmNoYXJhY3Rlcihyb3VuZChjdXQucHJvYiwyKSkNCiAgIyMjDQogIHBhcihtZnJvdyA9IGMoMSwyKSkNCiAgcGxvdCgxLXNlbnNwZS5tdHhbMixdLCBzZW5zcGUubXR4WzEsXSwgDQogICAgICAgdHlwZT0ibCIsDQogICAgICAgbHdkID0gMiwNCiAgICAgICBjb2wgPSAibmF2eSIsDQogICAgICAgeGxpbT1jKDAsMSksIA0KICAgICAgIHlsaW0gPSBjKDAsMSksDQogICAgICAgeGxhYiA9ICIxIC0gc3BlY2lmaWNpdHkiLCANCiAgICAgICB5bGFiID0gIlNlbnNpdGl2aXR5IiwgDQogICAgICAgbWFpbiA9ICJST0MgKFRhaW5pbmcgRGF0YSkiLCANCiAgICAgICBjZXgubWFpbiA9IDAuOCkNCiAgIyBBZGRpbmcgb2ZmIGRpYWdvbmFsIGxpbmUgcmVwcmVzZW50aW5nIHJhbmRvbSBndWVzcw0KICBzZWdtZW50cygwLCAwLCAxLCAxLCBsdHkgPSAyLCBjb2wgPSAicmVkIiwgKQ0KICBsZWdlbmQoImJvdHRvbXJpZ2h0IixjKCJmbiA9IDEwIiwgImZwID0gMSIsICJjcCA9IDAuMDIiLCBwYXN0ZSgiYXVjID0iLCBBVUMpKSwgYnR5PSJuIiwgY2V4ID0gMC44KSAgICAgICANCiAgDQogIHBsb3QoMTpsZW5ndGgoY3V0LnByb2IpLCBzZW5zcGUubXR4WzMsXSwgDQogICAgICAgeGxhYj0iY3V0LW9mZiBwcm9iYWJpbGl0eSIsDQogICAgICAgeWxhYiA9ICJhY2N1cmFjeSIsIA0KICAgICAgIHlsaW09YyhtaW4oc2Vuc3BlLm10eFszLF0pLDEpLA0KICAgICAgIGF4ZXMgPSBGQUxTRSwNCiAgICAgICBtYWluPSJjdXQtb2ZmIHZzIGFjY3VyYWN5IiwNCiAgICAgICBjZXgubWFpbiA9IDAuOSwNCiAgICAgICBjb2wubWFpbiA9ICJuYXZ5IikNCiAgICAgICAjIEFkZGluZyBheGVzIGFuZCBvdGhlciBncmFwaGljYWwgY29tcG9uZW50cyB0byB0aGUgcGxvdA0KICAgICAgIGF4aXMoMSwgYXQ9MToyMCwgbGFiZWwgPSB0aWNrLmxhYmVsLCBsYXMgPSAyKQ0KICAgICAgIGF4aXMoMikNCiAgICAgICBwb2ludHMoaWR4LCBzZW5zcGUubXR4WzMsXVtpZHhdLCBwY2g9MTksIGNvbCA9ICJyZWQiKQ0KICAgICAgIHNlZ21lbnRzKGlkeCAsIG1pbihzZW5zcGUubXR4WzMsXSksIGlkeCAsIHNlbnNwZS5tdHhbMyxdW2lkeCBdLCBjb2wgPSAicmVkIikNCiAgICAgICAjdGV4dChpZHgsIHNlbnNwZS5tdHhbMyxdW2lkeF0rMC4wMywgYXMuY2hhcmFjdGVyKHJvdW5kKHNlbnNwZS5tdHhbMyxdW2lkeF0sNCkpLCANCiAgICAgICAjICAgICBjb2wgPSAicmVkIiwgY2V4ID0gMC44KQ0KICAgICAgIGxlZ2VuZCgidG9wcmlnaHQiLCBjKHBhc3RlKCJvcHRpbWFsIGN1dC1vZmYgcHJvYiA9ICIscm91bmQobWVkaWFuKGN1dC5wcm9iW2lkeF0pLDMpLCBzZXA9IiIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiYWNjdXJhY3kgPSAiLCByb3VuZChtZWFuKHNlbnNwZS5tdHhbMyxdW2lkeCBdKSwzKSwgc2VwPSIiKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNleCA9IDAuOCwgYnR5ID0gIm4iLCBhZGogPSAtMCkNCmBgYA0KDQpTaW1pbGFybHksIHRoZXJlIGFyZSBzZXZlcmFsIDxmb250IGNvbG9yID0gInJlZCI+KipcY29sb3J7cmVkfWh5cGVycGFyYW1ldGVycyoqPC9mb250PiBvbmUgY2FuICoqdHVuZSoqIHRvIGZpbmQgdGhlIGJlc3QtYm9vdHN0cmFwcGVkIGRlY2lzaW9uIHRyZWUuIEZvciB0aG9zZSB3aG8gYXJlIGludGVyZXN0ZWQgaW4gZXhwbG9yaW5nIG1vcmUgaW4gKip0dW5uaW5nIGh5cGVycGFyYW1ldGVycyoqLCB5b3UgY2FuIHdyaXRlIGEgd3JhcHBlciBvZiAqKmJhZ2dpbmcoKSoqIGFuZCBwYXNzIGh5cGVycGFyYW1ldGVycyBzdWNoIGFzICoqZnAsIGZuLCBjcCwgbWluaXNwbGl0KiogaW4gdGhlIGxpc3Qgb2YgYXJndW1lbnRzIGluICoqcnBhcnQoKSoqIHRvIGZpbmQgdGhlIGJlc3QgYm9vdHN0cmFwIGRlY2lzaW9uIHRyZWUuDQoNCg0KIyMgQm9vc3RpbmcgRW5zZW1ibGUgTWV0aG9kcyAgDQoNCkJvb3N0aW5nIGlzIGEgc2VxdWVudGlhbCBlbnNlbWJsZSBtZXRob2QgdGhhdCBpdGVyYXRpdmVseSAqKmFkanVzdHMgdGhlIHdlaWdodCBvZiBkYXRhIHBvaW50cyoqIGFzIHBlciB0aGUgbGFzdCBjbGFzc2lmaWNhdGlvbi4gSWYgYSBkYXRhIHBvaW50IGlzIGluY29ycmVjdGx5IGNsYXNzaWZpZWQsIGl0IGluY3JlYXNlcyB0aGUgd2VpZ2h0IG9mIHRoYXQgZGF0YSBwb2ludC4gSXQgZGVjcmVhc2VzIHRoZSBiaWFzIGFuZCB2YXJpYW5jZSAoaGVuY2UgdGhlIHByZWRpY3RpdmUgZXJyb3IpIGFuZCBidWlsZHMgc3Ryb25nIHByZWRpY3RpdmUgbW9kZWxzLg0KDQpEYXRhIHBvaW50cyBtaXNjbGFzc2lmaWVkIGluIGVhY2ggaXRlcmF0aW9uIGFyZSBzcG90dGVkLCBhbmQgdGhlaXIgd2VpZ2h0cyBhcmUgaW5jcmVhc2VkLiBUaGUgQm9vc3RpbmcgYWxnb3JpdGhtIGFsbG9jYXRlcyB3ZWlnaHRzIHRvIGVhY2ggcmVzdWx0aW5nIG1vZGVsIGR1cmluZyB0cmFpbmluZy4gQSBtb2RlbCAoYWxzbyBjb21tb25seSBjYWxsZWQgbGVhcm5lcikgd2l0aCBnb29kIHRyYWluaW5nIGRhdGEgcHJlZGljdGlvbiByZXN1bHRzIHdpbGwgYmUgYXNzaWduZWQgYSBoaWdoZXIgd2VpZ2h0LiBXaGVuIGV2YWx1YXRpbmcgYSBuZXcgbGVhcm5lciwgQm9vc3Rpbmcga2VlcHMgdHJhY2sgb2YgdGhlIGxlYXJuZXLigJlzIGVycm9ycy4gDQoNClRoZSBzdGVwcyBvZiBib29zdGluZyBhbGdvcml0aG0gYXJlIHN1bW1hcml6ZWQgaW4gdGhlIGZvbGxvd2luZzoNCg0KMS4gRGF0YSBwb2ludHMgaW4gdGhlIGluaXRpYWwgdHJhaW5pbmcgZGF0YSBzZXQgYXJlIGVxdWFsbHkgd2VpZ2h0ZWQuDQoNCjIuIEEgYmFzZWQgbW9kZWwgaXMgY3JlYXRlZCBmb3IgdGhlIGluaXRpYWwgdHJhaW5pbmcgZGF0YSBzZXQuIA0KDQozLiBjbGFzc2lmaWNhdGlvbiBFcnJvcnMgYXJlIGNvdW50ZWQgdXNpbmcgYWN0dWFsIGFuZCBwcmVkaWN0ZWQgdmFsdWVzLiBUaGUgZGF0YSBwb2ludCB0aGF0IHdhcyBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgaXMgcHJvdmlkZWQgYSBoaWdoZXIgd2VpZ2h0Lg0KDQo0LiBhIG1vZGVsIGlzIGJ1aWx0IG9uIHRoZSBtb2RpZmllZCBkYXRhIChyZS13ZWlnaHRlZCBkYXRhIHBvaW50cykNCg0KNS4gdGhlIHByb2Nlc3MgaXMgaXRlcmF0ZWQgZm9yIG11bHRpcGxlIG1vZGVscyBhbmQgZWFjaCBvZiB0aGVtIGNvcnJlY3RzIHRoZSBwcmV2aW91cyBtb2RlbOKAmXMgZXJyb3JzLg0KDQo2LiBUaGUgZmluYWwgbW9kZWwgd29ya3MgYXMgYSBzdHJvbmcgbGVhcm5lciBhbmQgc2hvd3MgdGhlIHdlaWdodGVkIG1lYW4gb2YgYWxsIHRoZSBtb2RlbHMuDQoNCg0KVGhlIGZvbGxvd2luZyBmaWd1cmUgZGVtb25zdHJhdGVzIHRoZSByb3VnaCBpZGVhIG9mIGJvb3N0aW5nIGRlY2lzaW9uIHRyZWVzIGluIGEgY29uY2VwdHVhbCBhcHByb2FjaC4NCg0KYGBge3IgZWNobz1GQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIiwgZmlnLmNhcD0iRmlndXJlIDkuIEdyYXBoaWNhbCByZXByZXNlbnRhdGlvbiBvZiBib29zdGluZyB0cmVlIG1ldGhvZCIsIG91dC53aWR0aD0iODAlIiwgZGV2PSJqcGVnIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy93MDktRGVtb0Jvb3N0ZWRUcmVlQWxnb3JpdGhtLmpwZyIpDQpgYGANCg0KIyMgQmFnZ2luZyBWZXJzdXMgQm9vc3RpbmcNCg0KKipCYWdnaW5nKiogYW5kICoqQm9vc3RpbmcqKiBoYXZlIGEgdW5pdmVyc2FsIHNpbWlsYXJpdHkgb2YgYmVpbmcgY2xhc3NpZmllZCBhcyBlbnNlbWJsZSBtZXRob2RzLiBJbiBhZGRpdGlvbiwgQmFnZ2luZyBhbmQgQm9vc3RpbmcNCg0KKiBhcmUgZW5zZW1ibGUgbWV0aG9kcyBmb2N1c2VkIG9uIGdldHRpbmcgJE4kIGxlYXJuZXJzIGZyb20gYSBzaW5nbGUgbGVhcm5lci4NCg0KKiBtYWtlIHJhbmRvbSBzYW1wbGluZyBhbmQgZ2VuZXJhdGUgc2V2ZXJhbCB0cmFpbmluZyBkYXRhIHNldHMuIA0KDQoqIGFycml2ZSB1cG9uIHRoZSBlbmQgZGVjaXNpb24gYnkgbWFraW5nIGFuIGF2ZXJhZ2Ugb2YgJE4kIGxlYXJuZXJzIG9yIHRha2luZyB0aGUgdm90aW5nIHJhbmsgZG9uZSBieSBtb3N0IG9mIHRoZW0uDQoNCiogcmVkdWNlIHZhcmlhbmNlIGFuZCBwcm92aWRlIGhpZ2hlciBzdGFiaWxpdHkgYnkgbWluaW1pemluZyBlcnJvcnMuDQoNCkhvd2V2ZXIsIHRoZSB0d28gZW5zZW1ibGUgYWxnb3JpdGhtcyBhcmUgdmVyeSBkaWZmZXJlbnQgZnJvbSB0aGUgdGVjaG5pY2FsIHBlcnNwZWN0aXZlLiBUaGUgZm9sbG93aW5nIHRhYmxlIHNob3dzIHRoZXNlIHN0cnVjdHVyYWwgZGlmZmVyZW5jZXMuIA0KDQp8ICAgICAgICAgICAgICAgICBCYWdnaW5nICAgICAgICAgICAgICB8ICAgICAgICAgICBCb29zdGluZyAgICAgICAgICAgICAgICAgfA0KfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnxNZXJnaW5nIHRoZSBzYW1lIHR5cGUgb2YgcHJlZGljdGlvbnMuICB8IE1lcmdpbmcgZGlmZmVyZW50IHR5cGVzIG9mIHByZWRpY3Rpb25zLiB8DQp8RGVjcmVhc2VzIHZhcmlhbmNlLCBub3QgYmlhcywgYW5kIHNvbHZlcyBvdmVyLWZpdHRpbmcgaXNzdWVzLiAgfERlY3JlYXNlcyBiaWFzLCBub3QgdmFyaWFuY2UuICB8DQp8IEVhY2ggbW9kZWwgcmVjZWl2ZXMgYW4gZXF1YWwgd2VpZ2h0LiAgfCBNb2RlbHMgYXJlIHdlaWdoZWQgYmFzZWQgb24gdGhlaXIgcGVyZm9ybWFuY2UuIHwNCnxNb2RlbHMgYXJlIGJ1aWx0IGluZGVwZW5kZW50bHkuICAgfE5ldyBtb2RlbHMgYXJlIGFmZmVjdGVkIGJ5IGEgcHJldmlvdXNseSBidWlsdCBtb2RlbOKAmXMgcGVyZm9ybWFuY2UuICAgfA0KfCBUcmFpbmluZyBkYXRhIHN1YnNldHMgYXJlIGRyYXduIHJhbmRvbWx5IHdpdGggYSBib290c3RyYXAgc2FtcGxlLiAgfCBFdmVyeSBuZXcgc3Vic2V0IGNvbXByaXNlcyB0aGUgZWxlbWVudHMgdGhhdCB3ZXJlIG1pc2NsYXNzaWZpZWQgYnkgcHJldmlvdXMgbW9kZWxzLiAgfA0KfFVzdWFsbHkgYXBwbGllZCB3aGVyZSB0aGUgY2xhc3NpZmllciBpcyB1bnN0YWJsZSBhbmQgaGFzIGEgaGlnaCB2YXJpYW5jZS4gIHxVc3VhbGx5IGFwcGxpZWQgd2hlcmUgdGhlIGNsYXNzaWZpZXIgaXMgc3RhYmxlIGFuZCBzaW1wbGUgYW5kIGhhcyBhIGhpZ2ggYmlhcy4gIHwNCg0KVGhlIGJvb3N0aW5nIGVuc2VtYmxlIGFsZ29yaXRobXMgaGF2ZSB2YXJpb3VzIG5ldyBkZXZlbG9wbWVudCBpbiByZWNlbnQgeWVhcnMuIEluIGdlbmVyYWwsIHRoZXkgYXJlIG1vcmUgbWF0aGVtYXRpY2FsbHkgYW5kIHByYWdtYXRpY2FsbHkgZGVtYW5kZWQgaW4gaW1wbGVtZW50YXRpb24uIFdlIHdpbGwgbm90IGdvIGludG8gZGV0YWlscyBhYm91dCBhbnkgb2YgYm9vc3RpbmcgYWxnb3JpdGhtcyBhbmQgaW1wbGVtZW50IHRoZW0uDQoNClRvIGNvbmNsdWRlLCB3ZSB1c2UgdGhlIGZvbGxvd2luZyBmaWd1cmUgdG8gc2hvdyB0aGUgcm9sZSBvZiBtYXRoZW1hdGljYWwgdG9vbHMgYW5kIHRoaW5raW5nIGluIHRoZSBldm9sdXRpb24gb2YgdHJlZS1iYXNlZCBhbGdvcml0aG1zOiB0aGUgbW9yZSBtYXRoZW1hdGljYWwgdG9vbHMgeW91IGhhdmUsIHRoZSBtb3JlIHBvd2VyZnVsIG1vZGVscyB5b3UgYXJlIGNhcGFibGUgb2YgZGV2ZWxvcGluZyENCg0KYGBge3IgZWNobz1GQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIiwgZmlnLmNhcD0iRmlndXJlIDEwLiBFdm9sdXRpb24gb2YgdHJlZS1iYXNlZCBhbGdvcml0aG1zIHdpdGggdmFyaW91cyBsZXZlbCBvZiB0ZWNobmljYWwgdG9vbHMuIiwgb3V0LndpZHRoPSI4MCUiLCBkZXY9ImpwZWcifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL3cwOS1Gcm9tQmFnZ2luZzJCb29zdGluZy5qcGciKQ0KYGBgDQoNCg0KDQoNCg0K