NOTE: This is part of a collection of notebooks that serve as my personal studies of the chapters from the Introduction to Statistical Learning with Applications in R. I include insights, highlights, and experiences in the implementation of the methods discussed in the book (as well attempts at the end-of-chapter exercises whenever I’m brave enough!).

Key Takeaways from This Chapter

Introduction

The title of this chapter is Statistical Learning. Here, a preliminary discussion of the chapter’s namesake and our motivations to study it is given. The concepts of Prediction and Inference, and assessments of statistical learning methods are also introduced and expounded upon.

Datasets Used

Below are the datasets used in this chapter.

Dataset Description
Advertising Suppose that we are statistical consultants hired by a client to provide advice on how to improve sales of a particular product. The Advertising data set consists of the sales of that product in 200 different markets, along with advertising budgets for the product in each of those markets for three different media: TV, radio, and newspaper.
Income Simulated dataset containing the income and years of education for 30 individuals.

2.1. What is Statistical Learning?

Using the Advertising dataset, we try to determine if there is an association between sales and the advertising budgets for three different media: TV, radio, and newspaper. We plot sales as a function of these input variables wherein each plot contains a least squares fit of sales to that media.

# AdHoc Plotting Function
get_figs_2_1 <- function(data, column) {
  
  ggplot(data = data, aes(x = data[,column], y = sales)) +
    
  # Geoms
  geom_point(color = "gold", size = 3) +
  geom_smooth(method = "lm", se = F, color = "royalblue") + # Use "lm" for least squares regression
    
  # Themes
  theme_light() +
  labs(x = ifelse(column == "TV", column, str_to_title(column)),
       y = "Sales") +
  theme(text = element_text(size = 18),
        panel.grid = element_blank(), 
        panel.border = element_rect(size = 2, color = "grey50")) 
  
}
  
# Apply Function over Columns
figs_2_1 <- lapply(c("TV", "radio", "newspaper"), get_figs_2_1, data = Advertising) 
  
# Print combined graphs
figs_2_1_caption <- paste("FIGURE 2.1.",
                          "In each plot we show the simple least squares fit of sales to that variable.",
                          "Each blue line represents a simple model that can be used to predict sales.")

grid.arrange(grobs = figs_2_1, nrow = 1,
             bottom = textGrob(x = 0.04, hjust = 0, gp = gpar(fontsize = 14, font = 3), label = figs_2_1_caption))

The book reiterates that \(f\) is in general, unknown, and must be estimated based on the observed points. Doing so with the simulated Income dataset, where we assume the smoothed curve is from a localized regression fit (the authors did not specify \(f\)), we see in the plot below that the estimated \(f\) contains errors \(\epsilon\) represented by the vertical lines between the curve and the observations.

# Left Plot
fig_2_2_left <- 
  
  Income1 %>%
  
    ggplot(aes(x = Education, y = Income)) +
  
    # Geoms
    geom_point(color = "gold", size = 3) +
  
    # Themes
    scale_x_continuous(breaks = c(10, 12, 14, 16, 18, 20, 22)) +
    theme_light() +
    labs(x = "Years of Education",
         y = "Income") +
    theme(text = element_text(size = 18),
          panel.grid = element_blank(),
          panel.border = element_rect(size = 2, color = "grey50"))

# Right Plot
fit_2_2_right_loess <- fitted(loess(Income ~ Education, data = Income1)) # Fit a loess curve first and get fitted values 

fig_2_2_right <-
  
  Income1 %>% 
  
    ggplot(aes(x = Education, y = Income)) +
  
    # Geoms
    geom_segment(data = Income1, size = 1,
                 aes(x = Education, y = Income, xend = Education, yend = fit_2_2_right_loess)) +
    geom_smooth(method = "loess", se = F, color = "royalblue") +
    geom_point(color = "gold", size = 3) +
  
    # Themes
    scale_x_continuous(breaks = c(10, 12, 14, 16, 18, 20, 22)) +
    theme_light() +
    labs(x = "Years of Education",
         y = "Income") +
    theme(text = element_text(size = 18),
          panel.grid = element_blank(),
          panel.border = element_rect(size = 2, color = "grey50"))

# Print Combined Graphs
figs_2_2_caption <- paste("FIGURE 2.2.",
                          "LEFT: the dots are the observed values of income and education.",
                          "RIGHT: the curve represents the TRUE relationship, while the black lines represent the error.")

grid.arrange(grobs = list(fig_2_2_left, fig_2_2_right), nrow = 1,
             bottom = textGrob(x = 0.04, hjust = 0, gp = gpar(fontsize = 14, font = 3), label = figs_2_2_caption))

More generally as well, \(f\) may involve more than one input variable.

fit_2_3_loess <- loess(Income ~ Education + Seniority, data = Income2) # Fit a loess curve first

# Predict Values on Expanded XY Grid
x.pred <- seq(min(Income2$Education), max(Income2$Education), length.out = 30)
y.pred <- seq(min(Income2$Seniority), max(Income2$Seniority), length.out = 30)
xy     <- expand.grid(Education = x.pred, Seniority = y.pred)
z.pred <- matrix(predict(fit_2_3_loess, newdata = xy), nrow = 30, ncol = 30)

# 3D Plot
figs_2_3_caption <- "FIGURE 2.3. Plot of income as a function of education and seniority."

Income2 %>% 
  
  scatter3D(
    
    type = "p",
    x = Income2$Education, 
    y = Income2$Seniority, 
    z = Income2$Income,
    colvar = NA, pch = 19, col = "gold", cex = 1.75,
    phi = 25, theta = 45, expand = 0.6,
    xlab = "Years of Education", ylab = "Seniority", zlab = "Income",
    
    # Plot Regression Plane First
    panel.first = scatter3D(
                    x = Income2$Education,
                    y = Income2$Seniority,
                    z = Income2$Income,
                    colvar = NA, col = "black", add = T,
                    surf = list(x = x.pred, y = y.pred, z = z.pred, fit = predict(fit_2_3_loess),
                                facets = T, col = "skyblue", border = "royalblue", alpha = 0.45))
    ); text(x = 0, y = 0, pos = 1, offset = 20, font = 3, cex = 1.25, labels = figs_2_3_caption, xpd = T)

2.1.1. Why Estimate \(f\)?

The two main reasons are Prediction and Inference.

Prediction

  • Most notable when the input variables are available but the output is not.
  • We can predict \(Y\) using \(\hat{Y} = \hat{f}(X)\) where \(\hat{f}(X)\) is an estimate of \(f\) and \(\hat{Y}\) is the predicted outcome.
  • When prediction is the main focus, the exact form of \(f\) is less of a concern as opposed to improving prediction accuracy.
    • The accuracy of the prediction depends on the reducible and irreducible error.
    • reducible: this error can be reduced by applying the most appropriate statistical learning technique to estimate \(f\).
    • irreducible: error due to the variability in \(\epsilon\) which cannot be predicted by \(X\). This can be due to not including other significant predictors, and/or unmeasurable variation.
    • This can be shown mathematically by splitting the expected value of the squared difference between the observed and predicted value of \(Y\), or \(E(Y - \hat{Y})^2\), into \(\underbrace{[f(X) - \hat{f}(X)]^2}_\text{Reducible} + \underbrace{Var(\epsilon)}_\text{Irreducible}\) where the latter term represents the variance in the error term \(\epsilon\).

Inference

  • We also estimate \(f\) to derive inferences from the relationship between the input and the output specifically in how \(Y\) changes as a function of \(X_1, ..., X_p\).
  • The accuracy of our predictions isn’t made as important as our understanding of this relationship, so the exact form of \(f\) must be known.
  • Some insights that are useful are:
    • Identifying important predictors that are often a subset of the entire list of possible variables
    • Determining the positive/negative relationship between the predictor and the response
    • Making adjustments to the assumption of \(f\) having a linear form so as to find the most accurate representation of this relationship which is often more complicated

It is worth noting that different situations will call for either of the two, or both—it depends on the questions that we want to answer knowing that some may offer more value than others. Either way, different statistical learning methods may be less/more appropriate. Linear models for example, allow for simple and interpretable inferences, but are not the most accurate in predictions. In contrast, methods that account for non-linearity can be very accurate, but may not be the most interpretable choice for inference. This is an important mindset that must be kept while studying the methods in the book.

2.1.2. How Do We Estimate \(f\)?

Many of the methods discussed in the book share certain characteristics as will be discussed in this section. The set of \(n\) different data points are called the training data in that these observations are used to train the selected method on how to estimate \(f\). If \(x_{ij}\) represents the value of the \(j\)th predictor for observation \(i\), and \(y_i\) represents the response for the \(i\)th observation, then our training data consists of \(\{(x_1,y_1), (x_2,y_2), ..., (x_n,y_n)\}\) with \(x_i = (x_{i1}, x_{i2}, x_{i3}, ..., x_{ip})^{T}\).

Goal: apply a Statistical Learning method to the training data to estimate \(f\). These methods can be characterized as either parametric or non-parametric.

Parametric Methods

This is a two-step, model-based approach that reduces the problem of estimating \(f\) down to estimating a set of parameters.

Pros

  • This is much easier than fitting an entirely arbitrary function.

Cons

  • The model chosen will usually not follow the true form of \(f\).
  • If the fit is too far from the true form, the estimate will be poor.
  • While this can be alleviated by using more flexible models with more parameters to estimate, doing so can lead to overfitting wherein the model follows the errors too closely.

Steps

  1. We first make an assumption about the form of \(f\). A linear model for example assumes that \(f\) is linear in \(X\).
  2. We then need a procedure that fits the training data to the model. For the linear model, this means estimating the parameters \(\beta_1, \beta_2, ..., \beta_p\). The most common method to fit this model is through ordinary least squares (check this out for a brief history lesson on OLS!).

Applying a linear fit to the Income dataset, wherein we fit \(income \approx \beta_0 + \beta_1 \times education + \beta_1 \times seniority\), we see that the fit does a reasonable job of capturing the positive relationship between the predictors and the response, but fails to capture the curvature present in the true \(f\) as seen in Figure 2.3..

par(mfrow = c(1, 2))

# --- Linear Fit
fit_2_4_lm <- lm(Income ~ Education + Seniority, data = Income2)

# Predict Values on Expanded XY Grid
z.pred_lm <- matrix(predict(fit_2_4_lm, newdata = xy), nrow = 30, ncol = 30)

# 3D Plot of Linear Model
figs_2_4_caption <- "FIGURE 2.4. Linear model fit by OLS to the Income dataset."

Income2 %>% 
  
  scatter3D(
    
    type = "p",
    x = Income2$Education, 
    y = Income2$Seniority, 
    z = Income2$Income,
    colvar = NA, pch = 19, col = "gold", cex = 1.75,
    phi = 25, theta = 45, expand = 0.6,
    xlab = "Years of Education", ylab = "Seniority", zlab = "Income",
    
    # Plot Regression Plane First
    panel.first = scatter3D(
                    x = Income2$Education,
                    y = Income2$Seniority,
                    z = Income2$Income,
                    colvar = NA, col = "black", add = T,
                    surf = list(x = x.pred, y = y.pred, z = z.pred_lm, fit = predict(fit_2_4_lm),
                                facets = T, col = "violet", border = "purple", alpha = 0.45))
    ); text(x = 0, y = 0, pos = 1, offset = 20, font = 3, cex = 1.25, labels = figs_2_4_caption, xpd = T)

# --- Smooth (!) Thin-Plate Spline Fit
Income2_X_scaled <- scale(Income2[c("Education", "Seniority")]) # Scale X first
Income3 <- data.frame(Income2_X_scaled, Income = Income2$Income)

fit_2_5_tp <- gam(Income ~ s(Education, Seniority, bs = "tp"), data = Income3)

# Predict Values on Expanded XY Grid
x.pred_tp <- seq(min(Income3$Education), max(Income3$Education), length.out = 30)
y.pred_tp <- seq(min(Income3$Seniority), max(Income3$Seniority), length.out = 30)
xy_tp     <- expand.grid(Education = x.pred_tp, Seniority = y.pred_tp)
z.pred_tp <- matrix(predict(fit_2_5_tp, newdata = xy_tp), nrow = 30, ncol = 30)

# Transform Back to Original Scale
scaled_center <- attr(Income2_X_scaled, "scaled:center")
scaled_scale  <- attr(Income2_X_scaled, "scaled:scale")
Income3$Education_orig <- x.pred_tp * scaled_scale[1] + scaled_center[1]
Income3$Seniority_orig <- y.pred_tp * scaled_scale[2] + scaled_center[2]

# 3D Plot of Thin-Plate Spline Regression Model
figs_2_5_caption <- "FIGURE 2.5. Smooth thin-plate spline fit."

Income3 %>% 
  
  scatter3D(
    
    type = "p",
    x = Income2$Education,
    y = Income2$Seniority,
    z = Income2$Income,
    colvar = NA, pch = 19, col = "gold", cex = 1.75,
    phi = 25, theta = 45, expand = 0.6,
    xlab = "Years of Education", ylab = "Seniority", zlab = "Income",
    
    # Plot Regression Plane First
    panel.first = scatter3D(
                    x = Income2$Education, 
                    y = Income2$Seniority, 
                    z = Income2$Income,
                    colvar = NA, col = "black", add = T,
                    surf = list(x = x.pred, y = y.pred, z = z.pred_tp, fit = predict(fit_2_5_tp),
                                facets = T, col = "violet", border = "purple", alpha = 0.45))
    ); text(x = 0, y = 0, pos = 1, offset = 20, font = 3, cex = 1.25, labels = figs_2_5_caption, xpd = T)

Non-Parametric Methods

These methods do not make assumptions about the form of \(f\), but instead aim to get an estimate of \(f\) that gets as close to the data points as possible without “being too rough or wiggly,” which I take to mean as not overfitting over the training data.

Pros

  • By not having strict assumptions about \(f\), these methods allow for a greater range of shapes while accurately fitting \(f\).

Cons

  • A large number of observations is needed to accurately estimate \(f\), much more than that needed of a parametric approach.

An example is given by Figure 2.5. wherein a thin-plate spline is used to fit \(f\) without a pre-specified model. Note, however, that it does so such that the estimated fit is close to the data, but is still smooth. Comparing this to the true \(f\) in Figure 2.3., we see that the estimate is accurate indeed. Another characteristic of a non-parametric method such as fitting a thin-plate spline is the need to choose a level of smoothness. The plot below for example has a lower level of smoothing and thus results in a rough fit that follows the observed data perfectly. In doing so, we have overfitted on the data, and we can expect to have inaccurate estimates on new data that is not part of the original training data. Thus, another factor to consider is how we will choose the correct amount of smoothing.

# --- Rough (!) Thin-Plate Spline Fit
Income2_X_scaled <- scale(Income2[c("Education", "Seniority")]) # Scale X first
Income3 <- data.frame(Income2_X_scaled, Income = Income2$Income)

# - k = 30 since there are 30 exact unique points in our data, thereby disabling low-rank approximation
# - sp = 0 for disabling penalization
# - bp = "tp" to use thin plate regression spline basis function

fit_2_6_tp <- gam(Income ~ s(Education, Seniority, k = 30, sp = 0, bs = "tp"), data = Income3)

# Predict Values on Expanded XY Grid
z.pred_rtp <- matrix(predict(fit_2_6_tp, newdata = xy_tp), nrow = 30, ncol = 30)

# 3D Plot of Thin-Plate Spline Regression Model
figs_2_6_caption <- "FIGURE 2.6. Rough thin-plate spline fit with zero errors."

Income3 %>% 
  
  scatter3D(
    
    type = "p",
    x = Income2$Education,
    y = Income2$Seniority,
    z = Income2$Income,
    colvar = NA, pch = 19, col = "gold", cex = 1.75,
    phi = 25, theta = 45, expand = 0.6,
    xlab = "Years of Education", ylab = "Seniority", zlab = "Income",
    
    # Plot Regression Plane First
    panel.first = scatter3D(
                    x = Income2$Education, 
                    y = Income2$Seniority, 
                    z = Income2$Income,
                    colvar = NA, col = "black", add = T,
                    surf = list(x = x.pred, y = y.pred, z = z.pred_rtp, fit = predict(fit_2_6_tp),
                                facets = T, col = "violet", border = "purple", alpha = 0.45))
    ); text(x = 0, y = 0, pos = 1, offset = 20, font = 3, cex = 1.25, labels = figs_2_6_caption, xpd = T)

2.1.3. The Trade-Off Between Prediction Accuracy and Model Interpretability

As we saw in some of the examples so far, some statistical learning methods are less flexible/more restrictive than others (e.g. OLS regression). There are reasons for choosing such methods as opposed to the flexible approaches (e.g. thin-plate spline regression):

  • If Inference is our main focus, then these restrictive methods are preferable in that it is easy to understand and interpret the relationship between \(X\) and \(Y\).
  • More flexible methods often lead to complex estimates that make this assessment of individual relationships between each predictor and the response very difficult.
  • Even when prediction is the sole interest, these restrictive approaches can sometimes outperform the most flexible method available because of the latter overfitting.

2.1.4. Supervised Versus Unsupervised Learning

Statistical learning problems can be categorized into supervised and unsupervised. The main difference between the two is that, in the former, for each observation there is an associated response, and that relationship is modeled with either prediction or inference in mind whereas in the latter, we have realized values of \(x_i\) for each observation \(i = 1, ..., n\), but no response. There is no response variable to supervise the method that we are using. One such method is Cluster Analysis which seeks to group observations into distinct clusters based on values of the input variables which may translate for example to segmenting customers based on spending habits and demographic factors.

Some problems can also call for semi-supervised learning methods wherein a subset of the data contains associated response values while another subset does not. An example of this scenario is when measuring the response is much more expensive than readily being able to measure the predictors. Thus, classifying problems between supervised and unsupervised may not always be clear from the get-go.

2.1.5. Regression versus Classification Problems

Problems with a quantitative response, or those with numerical values such as age and income, are often referred to as regression problems. Those that deal with a qualitative response wherein the variable takes one value of \(K\) different classes/categories (e.g. gender, presence of a disease, product brand) are referred to as classification problems. Whether the predictor variables are of either type is generally less important. This does not mean however that less attention should be given to the available features, especially when effectively engineered and coded.

2.2. Assessing Model Accuracy

It is necessary to know and understand many different statistical learning methods as there is no single method that works best on all possible data sets.

2.2.1. Measuring the Quality of Fit

Evaluating the performance of statistical learning methods require us to quantify how close the predictions are to the observed values. A very commonly used measure is the Mean Squared Error or MSE.

\[MSE = \frac{1}{n} \displaystyle\sum_{i = 1}^{n} (y_i - \hat{f}(x_i))^2\]

  • Used in the regression setting.
  • \(\hat{f}(x_i)\) is the predicted value for the ith observation (also \(\hat{y}_i\)).
  • A smaller value denotes predictions that are closer to the true value.
  • Computing the MSE on a model fit with the training data can be more accurately called as the training MSE.
  • However, we are more interested in the test MSE, or one that measures prediction accuracy when the method is applied to unseen test data.
  • We try to select the model with the lowest test MSE.

For unseen test data \((x_0, y_0)\) we compute the average squared prediction error.

\[Ave(y_0 - \hat{f}(x_0))^2\]

One way to implement the minimization of the MSE is to set aside data that was not used to train the model to serve as the test data, and evaluate the methods on that test data. However when test data is not available, simply relying on the training MSE is not ideal as well since these methods can have very large test MSEs despite having small training MSEs. Furthermore, the training MSE is monotonically decreasing as the flexibility of the method increases; relying on training MSE is likely to push us to use the most flexible/complex method which can have just as bad a test MSE than even an OLS model (consider when the true \(f\) is linear). This is illustrated when the test MSE is plotted as a function of model flexibility where a resulting U-shaped curve is observed.

When the model results in a small training MSE but a large test MSE, then we can say that the model has overfitted on the training data in that it has followed too closely the observed training data and its patterns that don’t exist in the test data. Another indicator is when a less flexible method would have resulted into a smaller test MSE. Note that we also almost always expect the training MSE to be smaller than the test MSE because “most statistical learning methods either directly or indirectly seek to minimize the training MSE.”

2.2.2. The Bias-Variance Trade-Off

The expected test MSE can always be decomposed into three fundamental terms (for a given value of \(x_0\)) : the variance of \(\hat{f}(x_0)\), the squared bias of \(\hat{f}(x_0)\), and the variance of the error terms \(\epsilon\) which is also the irreducible error. As the first two terms are nonnegative, the expected MSE can never go below \(Var(\epsilon)\).

\[\begin{align} MSE & = E(y_0 - \hat{f}(x_0))^2 \\ & = Var(\hat{f}(x_0)) + [Bias(\hat{f}(x_0))]^2 + Var(\epsilon) \end{align}\]

Thus, the optimal statistical learning method must have both the variance and bias be low.

  • “Expected Test MSE”: the average test MSE obtained from repeatedly estimating \(f\) using a number of training sets, and testing each \(\hat{f}\) at \(x_0\).
  • “Overall Expected Test MSE”: obtained by averaging the former over ALL possible values of \(x_0\) in the test set.
  • Variance: the amount of variation in the estimated \(\hat{f}\) over different training sets. Ideally, \(\hat{f}\) should not vary much. High variance in a method can result in large changes in \(\hat{f}\) even with small changes in the training set. Generally, more flexible methods have higher variance.
  • Bias: the error from approximating a complex phenomenon with a simpler model; also the inaccuracy of the prediction estimates. Generally, more flexible methods have lower bias.

It is important to note that the rate of change of both the Bias and the Variance as flexibility increases will also depend on the data sets themselves. As the two quantities determine whether the test MSE increases/decreases, the amount of flexibility corresponding to the optimal test MSE are different across the data sets. There is a point, however, where increasing the flexibility only slightly decreases the Bias but causes the Variance to go up, consequently increasing the MSE overall. Thus, there is a trade-off between the two, and the challenge is to find the method that optimally minimizes both.

2.2.3. The Classification Setting

The aforementioned concepts also apply to the classification setting with changes due to \(y_i\) being qualitative instead of quantitative when we estimate \(f\) over the training data. The most common approach to quantify the accuracy of the estimated \(f\) is the training error rate which computes the proportion of incorrect classifications after applying \(\hat{f}\) to the training data.

\[ \frac{1}{n} \displaystyle\sum_{i = 1}^{n} I(y_i \neq \hat{y}_i) \\ where\ I(y_i \neq \hat{y}_i) = \begin{cases} 0 & \text{if $y_i = \hat{y}_i$, or correct classification} \\ 1 & \text{if $y_i \neq \hat{y}_i$, or wrong classification} \end{cases} \]

The test error rate which is the result after applying the classifier to the test data \((x_0, y_0)\) is given by the below measure where \(\hat{y}_0\) is the predicted class label. We generally choose the model with the lowest test error rate.

\[Ave(I(y_0 \neq \hat{y}_0))\]

The Bayes Classifier

The above test error rate can be shown (see Section 2.4 of the Elements of Statistical Learning) to be minimized on the average by classifying each observation to the most probable class, or where the below conditional probability for a given test observation with predictor vector \(x_0\) to the class \(j\) is the largest. This is referred to as the Bayes Classifier:

\[Pr(Y = j\ |\ X = x_0)\]

In a two-class setting, this classifier corresponds to predicting a response value of class one when \(Pr(Y = 1\ |\ X = x_0) > 0.5\), and returns class two otherwise. If plotted, a line drawn from points where the conditional probability is exactly 50% is called the Bayes Decision Boundary. Additionally, the Bayes Classifier produces the Bayes Error Rate which is the lowest test error rate possible for a classifier with a random outcome, and is also analogous to the irreducible error. This is given below where the probability over all possible values of \(X\) are averaged over the expectation:

\[1 - E(\underset{j}{max}Pr(Y = j\ |\ X)\]

K-Nearest Neighbors

When the conditional distribution of \(Y\) given \(X\) is not known as in real-world data, the Bayes Classifier cannot be computed. The K-Nearest Neighbors is an approach that estimates this conditional distribution and predicts a class for a test observation with the highest estimated probability. This is done by:

  1. Choosing the \(K\) nearest points closest to the training observation \(x_0\), represented by \(N_0\)
  2. Estimating the conditional probability for class \(j\), or the number of points with response values equal to \(j\) divided by \(K\):
    \(Pr(Y = j\ |\ X = x_0) = \frac{1}{K}\displaystyle\sum_{i\ \in\ N_0}I(y_i = j)\)
  3. Classifying the observation to the class with the largest probability

The KNN classifier can perform closely to the Bayes Classifier, but the choice of \(K\) affects the flexibility of the model.

  • When \(K\) = 1, the classifier has low bias and high variance.
  • As \(K\) increases, it becomes less flexible and produces a decision boundary near to linear; i.e. it has high bias and low variance.

Similar to the regression setting, the training error rate decreases as the flexibility increases. The test error rate also similarly exhibits a U-shaped curve where the model eventually overfits.

LS0tDQp0aXRsZTogIkNoYXB0ZXIgMjogU3RhdGlzdGljYWwgTGVhcm5pbmciDQphdXRob3I6IERhdmlkIEFuZ2VsbyBCcmlsbGFudGVzIChAZGF2ZW9uYnJpbGwpDQpoZWFkZXItaW5jbHVkZXM6DQogICAtIFx1c2VwYWNrYWdle2Ftc21hdGh9DQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIGhpZ2hsaWdodDogemVuYnVybg0KICAgIHRoZW1lOiBwYXBlcg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogZmFsc2UNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCi0tLQ0KDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KLm1haW4tY29udGFpbmVyIHsNCiAgbWF4LXdpZHRoOiAxMDAwcHg7DQogIG1hcmdpbi1sZWZ0OiBhdXRvOw0KICBtYXJnaW4tcmlnaHQ6IGF1dG87DQp9DQo8L3N0eWxlPg0KDQotIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gDQoNCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmp1c3RpZnkiPg0KPGI+Tk9URTo8L2I+IFRoaXMgaXMgcGFydCBvZiBhIFtjb2xsZWN0aW9uXShodHRwczovL2dpdGh1Yi5jb20vZHNicmlsbGFudGVzL0lTTC1SLU5vdGVib29rcykgb2Ygbm90ZWJvb2tzIHRoYXQgc2VydmUgYXMgbXkgcGVyc29uYWwgc3R1ZGllcyBvZiB0aGUgY2hhcHRlcnMgZnJvbSB0aGUgW0ludHJvZHVjdGlvbiB0byBTdGF0aXN0aWNhbCBMZWFybmluZyB3aXRoIEFwcGxpY2F0aW9ucyBpbiBSXShodHRwOi8vZmFjdWx0eS5tYXJzaGFsbC51c2MuZWR1L2dhcmV0aC1qYW1lcy9JU0wvKS4gSSBpbmNsdWRlIGluc2lnaHRzLCBoaWdobGlnaHRzLCBhbmQgZXhwZXJpZW5jZXMgaW4gdGhlIGltcGxlbWVudGF0aW9uIG9mIHRoZSBtZXRob2RzIGRpc2N1c3NlZCBpbiB0aGUgYm9vayAoYXMgd2VsbCBhdHRlbXB0cyBhdCB0aGUgZW5kLW9mLWNoYXB0ZXIgZXhlcmNpc2VzIHdoZW5ldmVyIEknbSBicmF2ZSBlbm91Z2ghKS4NCjwvcD4NCg0KIyBLZXkgVGFrZWF3YXlzIGZyb20gVGhpcyBDaGFwdGVyDQoNCi0gPHAgc3R5bGU9InRleHQtYWxpZ246anVzdGlmeSI+U3RhdGlzdGljYWwgbGVhcm5pbmcgcmVmZXJzIHRvIGEgc2V0IG9mIGFwcHJvYWNoZXMgZm9yIGVzdGltYXRpbmcgJGYkIHdoaWNoIHJlcHJlc2VudHMgdGhlIDxpPnN5c3RlbWF0aWM8L2k+IGluZm9ybWF0aW9uIHRoYXQgdGhlIHByZWRpY3RvcnMgJFgkIHByb3ZpZGUgYWJvdXQgdGhlIHJlc3BvbnNlICRZJC4gVGhlIGdlbmVyYWwgZm9ybSBvZiB0aGUgcmVsYXRpb25zaGlwICh3aXRoICRcZXBzaWxvbiQgYXMgdGhlIDxpPnJhbmRvbSBlcnJvciB0ZXJtPC9pPikgY2FuIGJlIHdyaXR0ZW4gYXMgJFkgPSBmKFgpICsgXGVwc2lsb24kLjwvcD4NCi0gVGhlIHR3byBtYWluIHJlYXNvbnMgdG8gZXN0aW1hdGUgJGYkIGFyZSA8Yj5QcmVkaWN0aW9uPC9iPiBhbmQgPGI+SW5mZXJlbmNlPC9iPi4NCi0gVGhlIGdvYWwgaXMgdG8gYXBwbHkgYSBzdGF0aXN0aWNhbCBsZWFybmluZyBtZXRob2QgdG8gdGhlIHRyYWluaW5nIGRhdGEgdG8gZXN0aW1hdGUgJGYkLiBUaGVzZSBjYW4gYmUgY2xhc3NpZmllZCBpbnRvIDxiPlBhcmFtZXRyaWM8L2I+IGFuZCA8Yj5Ob24tUGFyYW1ldHJpYzwvYj4gbWV0aG9kcy4gQm90aCBoYXZlIHRoZWlyIG93biBzdHJlbmd0aHMgYW5kIHdlYWtuZXNzZXMgc3BlY2lmaWNhbGx5IGluIGVpdGhlciBhY2N1cmF0ZSBwcmVkaWN0aW9uIG9yIHVuZGVyc3RhbmRhYmxlIGluZmVyZW5jZXMuDQotIFN0YXRpc3RpY2FsIGxlYXJuaW5nIHByb2JsZW1zIGNhbiBnZW5lcmFsbHkgYmUgY2xhc3NpZmllZCBpbnRvIDxiPlN1cGVydmlzZWQ8L2I+IGFuZCA8Yj5VbnN1cGVydmlzZWQ8L2I+IGRlcGVuZGluZyBvbiB0aGUgcHJlc2VuY2Ugb2YgYW4gYXNzb2NpYXRlZCByZXNwb25zZSBmb3IgZWFjaCBvYnNlcnZlZCB2YWx1ZSBvZiB0aGUgcHJlZGljdG9ycy4NCi0gU3RhdGlzdGljYWwgbGVhcm5pbmcgcHJvYmxlbXMgY2FuIGFsc28gYmUgY2xhc3NpZmllZCBpbnRvIDxiPlJlZ3Jlc3Npb248L2I+IGFuZCA8Yj5DbGFzc2lmaWNhdGlvbjwvYj4gZGVwZW5kaW5nIG9uIHdoZXRoZXIgdGhlIHJlc3BvbnNlIHRha2VzIG9uIHF1YW50aXRhdGl2ZSBvciBxdWFsaXRhdGl2ZSB2YWx1ZXMuDQotIEl0IGlzIG5lY2Vzc2FyeSB0byBrbm93IGFuZCB1bmRlcnN0YW5kIG1hbnkgZGlmZmVyZW50IHN0YXRpc3RpY2FsIGxlYXJuaW5nIG1ldGhvZHMgYXMgdGhlcmUgaXMgPGI+bm8gc2luZ2xlIG1ldGhvZDwvYj4gdGhhdCB3b3JrcyBiZXN0IG9uIGFsbCBwb3NzaWJsZSBkYXRhIHNldHMuDQotIFRoZSBjaGFsbGVuZ2UgaXMgdG8gZmluZCB0aGUgbWV0aG9kIHRoYXQgb3B0aW1hbGx5IG1pbmltaXplcyBib3RoIEJpYXMgYW5kIFZhcmlhbmNlLg0KDQojIEludHJvZHVjdGlvbg0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjpqdXN0aWZ5Ij4NClRoZSB0aXRsZSBvZiB0aGlzIGNoYXB0ZXIgaXMgPGI+U3RhdGlzdGljYWwgTGVhcm5pbmc8L2I+LiBIZXJlLCBhIHByZWxpbWluYXJ5IGRpc2N1c3Npb24gb2YgdGhlIGNoYXB0ZXIncyBuYW1lc2FrZSBhbmQgb3VyIG1vdGl2YXRpb25zIHRvIHN0dWR5IGl0IGlzIGdpdmVuLiBUaGUgY29uY2VwdHMgb2YgUHJlZGljdGlvbiBhbmQgSW5mZXJlbmNlLCBhbmQgYXNzZXNzbWVudHMgb2Ygc3RhdGlzdGljYWwgbGVhcm5pbmcgbWV0aG9kcyBhcmUgYWxzbyBpbnRyb2R1Y2VkIGFuZCBleHBvdW5kZWQgdXBvbi4NCjwvcD4NCg0KDQojIyBEYXRhc2V0cyBVc2VkDQoNCkJlbG93IGFyZSB0aGUgZGF0YXNldHMgdXNlZCBpbiB0aGlzIGNoYXB0ZXIuDQoNCnwgRGF0YXNldCB8IERlc2NyaXB0aW9uIHwNCnwtfC18DQp8IEFkdmVydGlzaW5nIHwgU3VwcG9zZSB0aGF0IHdlIGFyZSBzdGF0aXN0aWNhbCBjb25zdWx0YW50cyBoaXJlZCBieSBhIGNsaWVudCB0byBwcm92aWRlIGFkdmljZSBvbiBob3cgdG8gaW1wcm92ZSBzYWxlcyBvZiBhIHBhcnRpY3VsYXIgcHJvZHVjdC4gVGhlIEFkdmVydGlzaW5nIGRhdGEgc2V0IGNvbnNpc3RzIG9mIHRoZSBzYWxlcyBvZiB0aGF0IHByb2R1Y3QgaW4gMjAwIGRpZmZlcmVudCBtYXJrZXRzLCBhbG9uZyB3aXRoIGFkdmVydGlzaW5nIGJ1ZGdldHMgZm9yIHRoZSBwcm9kdWN0IGluIGVhY2ggb2YgdGhvc2UgbWFya2V0cyBmb3IgdGhyZWUgZGlmZmVyZW50IG1lZGlhOiBUViwgcmFkaW8sIGFuZCBuZXdzcGFwZXIuIHwNCnwgSW5jb21lIHwgU2ltdWxhdGVkIGRhdGFzZXQgY29udGFpbmluZyB0aGUgaW5jb21lIGFuZCB5ZWFycyBvZiBlZHVjYXRpb24gZm9yIDMwIGluZGl2aWR1YWxzLiB8DQoNCg0KYGBge3IgU0VUVVAsIGluY2x1ZGUgPSBGfQ0KDQojIDw8IExpYnJhcmllcyA+Pg0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkoc3RyaW5naSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ3JpZCkNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeShwbG90M0QpDQpsaWJyYXJ5KElTTFIpDQpsaWJyYXJ5KG1nY3YpDQoNCiMgPDwgUGF0aHMgPj4NCiMgTGluayB0byBleHRlcm5hbCBkYXRhc2V0cyBub3QgaW4gdGhlIElTTFIgcGFja2FnZQ0KSVNMUl9kYXRhc2V0cyA8LSAiaHR0cDovL2ZhY3VsdHkubWFyc2hhbGwudXNjLmVkdS9nYXJldGgtamFtZXMvSVNMLyINCg0KIyA8PCBEYXRhID4+DQpBZHZlcnRpc2luZyA8LSBmcmVhZChwYXN0ZTAoSVNMUl9kYXRhc2V0cywgIkFkdmVydGlzaW5nLmNzdiIpLCBkYXRhLnRhYmxlID0gRikgJT4lIHNlbGVjdCgtYygiVjEiKSkNCkluY29tZTEgPC0gZnJlYWQocGFzdGUwKElTTFJfZGF0YXNldHMsICJJbmNvbWUxLmNzdiIpLCBkYXRhLnRhYmxlID0gRikNCkluY29tZTIgPC0gZnJlYWQocGFzdGUwKElTTFJfZGF0YXNldHMsICJJbmNvbWUyLmNzdiIpLCBkYXRhLnRhYmxlID0gRikNCg0KDQpgYGANCg0KIyAyLjEuIFdoYXQgaXMgU3RhdGlzdGljYWwgTGVhcm5pbmc/DQoNCi0gPGI+SW5wdXQgdmFyaWFibGVzPC9iPiBhcmUgYWxzbyBjYWxsZWQgcHJlZGljdG9ycywgaW5kZXBlbmRlbnQgdmFyaWFibGVzLCBmZWF0dXJlcywgb3Igc29tZXRpbWVzIGp1c3QgdmFyaWFibGVzLg0KLSA8Yj5PdXRwdXQgdmFyaWFibGVzPC9iPiBhcmUgYWxzbyBjYWxsZWQgcmVzcG9uc2Ugb3IgZGVwZW5kZW50IHZhcmlhYmxlcy4NCi0gJFkgPSBmKFgpICsgXGVwc2lsb24kIGlzIHRoZSBnZW5lcmFsIGZvcm0gb2YgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuICRZJCBhbmQgJFggPSAoWF8xLCBYXzIsIC4uLiwgWF9wKSQuDQotIFN0YXRpc3RpY2FsIGxlYXJuaW5nIHJlZmVycyB0byBhIHNldCBvZiBhcHByb2FjaGVzIGZvciBlc3RpbWF0aW5nICRmJC4NCg0KVXNpbmcgdGhlIDxjb2RlPkFkdmVydGlzaW5nPC9jb2RlPiBkYXRhc2V0LCB3ZSB0cnkgdG8gZGV0ZXJtaW5lIGlmIHRoZXJlIGlzIGFuIGFzc29jaWF0aW9uIGJldHdlZW4gPGNvZGU+c2FsZXM8L2NvZGU+IGFuZCB0aGUgYWR2ZXJ0aXNpbmcgYnVkZ2V0cyBmb3IgdGhyZWUgZGlmZmVyZW50IG1lZGlhOiA8Y29kZT5UVjwvY29kZT4sIDxjb2RlPnJhZGlvPC9jb2RlPiwgYW5kIDxjb2RlPm5ld3NwYXBlcjwvY29kZT4uIFdlIHBsb3QgPGNvZGU+c2FsZXM8L2NvZGU+IGFzIGEgZnVuY3Rpb24gb2YgdGhlc2UgaW5wdXQgdmFyaWFibGVzIHdoZXJlaW4gZWFjaCBwbG90IGNvbnRhaW5zIGEgPGI+bGVhc3Qgc3F1YXJlcyBmaXQ8L2I+IG9mIDxjb2RlPnNhbGVzPC9jb2RlPiB0byB0aGF0IG1lZGlhLg0KDQpgYGB7ciBGaWd1cmUgMi4xLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBmaWcud2lkdGggPSAxNiwgZmlnLmhlaWdodCA9IDh9DQojIEFkSG9jIFBsb3R0aW5nIEZ1bmN0aW9uDQpnZXRfZmlnc18yXzEgPC0gZnVuY3Rpb24oZGF0YSwgY29sdW1uKSB7DQogIA0KICBnZ3Bsb3QoZGF0YSA9IGRhdGEsIGFlcyh4ID0gZGF0YVssY29sdW1uXSwgeSA9IHNhbGVzKSkgKw0KICAgIA0KICAjIEdlb21zDQogIGdlb21fcG9pbnQoY29sb3IgPSAiZ29sZCIsIHNpemUgPSAzKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRiwgY29sb3IgPSAicm95YWxibHVlIikgKyAjIFVzZSAibG0iIGZvciBsZWFzdCBzcXVhcmVzIHJlZ3Jlc3Npb24NCiAgICANCiAgIyBUaGVtZXMNCiAgdGhlbWVfbGlnaHQoKSArDQogIGxhYnMoeCA9IGlmZWxzZShjb2x1bW4gPT0gIlRWIiwgY29sdW1uLCBzdHJfdG9fdGl0bGUoY29sdW1uKSksDQogICAgICAgeSA9ICJTYWxlcyIpICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLA0KICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KHNpemUgPSAyLCBjb2xvciA9ICJncmV5NTAiKSkgDQogIA0KfQ0KICANCiMgQXBwbHkgRnVuY3Rpb24gb3ZlciBDb2x1bW5zDQpmaWdzXzJfMSA8LSBsYXBwbHkoYygiVFYiLCAicmFkaW8iLCAibmV3c3BhcGVyIiksIGdldF9maWdzXzJfMSwgZGF0YSA9IEFkdmVydGlzaW5nKSANCiAgDQojIFByaW50IGNvbWJpbmVkIGdyYXBocw0KZmlnc18yXzFfY2FwdGlvbiA8LSBwYXN0ZSgiRklHVVJFIDIuMS4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAiSW4gZWFjaCBwbG90IHdlIHNob3cgdGhlIHNpbXBsZSBsZWFzdCBzcXVhcmVzIGZpdCBvZiBzYWxlcyB0byB0aGF0IHZhcmlhYmxlLiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICJFYWNoIGJsdWUgbGluZSByZXByZXNlbnRzIGEgc2ltcGxlIG1vZGVsIHRoYXQgY2FuIGJlIHVzZWQgdG8gcHJlZGljdCBzYWxlcy4iKQ0KDQpncmlkLmFycmFuZ2UoZ3JvYnMgPSBmaWdzXzJfMSwgbnJvdyA9IDEsDQogICAgICAgICAgICAgYm90dG9tID0gdGV4dEdyb2IoeCA9IDAuMDQsIGhqdXN0ID0gMCwgZ3AgPSBncGFyKGZvbnRzaXplID0gMTQsIGZvbnQgPSAzKSwgbGFiZWwgPSBmaWdzXzJfMV9jYXB0aW9uKSkNCmBgYA0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjpqdXN0aWZ5Ij4NClRoZSBib29rIHJlaXRlcmF0ZXMgdGhhdCAkZiQgaXMgaW4gZ2VuZXJhbCwgdW5rbm93biwgYW5kIG11c3QgYmUgZXN0aW1hdGVkIGJhc2VkIG9uIHRoZSBvYnNlcnZlZCBwb2ludHMuIERvaW5nIHNvIHdpdGggdGhlIHNpbXVsYXRlZCA8Y29kZT5JbmNvbWU8L2NvZGU+IGRhdGFzZXQsIHdoZXJlIHdlIGFzc3VtZSB0aGUgc21vb3RoZWQgY3VydmUgaXMgZnJvbSBhIGxvY2FsaXplZCByZWdyZXNzaW9uIGZpdCAodGhlIGF1dGhvcnMgZGlkIG5vdCBzcGVjaWZ5ICRmJCksIHdlIHNlZSBpbiB0aGUgcGxvdCBiZWxvdyB0aGF0IHRoZSBlc3RpbWF0ZWQgJGYkIGNvbnRhaW5zIGVycm9ycyAkXGVwc2lsb24kIHJlcHJlc2VudGVkIGJ5IHRoZSB2ZXJ0aWNhbCBsaW5lcyBiZXR3ZWVuIHRoZSBjdXJ2ZSBhbmQgdGhlIG9ic2VydmF0aW9ucy4NCjwvcD4NCg0KYGBge3IgRmlndXJlIDIuMi4sIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmFsaWduID0gImNlbnRlciIsIGZpZy53aWR0aCA9IDE2LCBmaWcuaGVpZ2h0ID0gOH0NCiMgTGVmdCBQbG90DQpmaWdfMl8yX2xlZnQgPC0gDQogIA0KICBJbmNvbWUxICU+JQ0KICANCiAgICBnZ3Bsb3QoYWVzKHggPSBFZHVjYXRpb24sIHkgPSBJbmNvbWUpKSArDQogIA0KICAgICMgR2VvbXMNCiAgICBnZW9tX3BvaW50KGNvbG9yID0gImdvbGQiLCBzaXplID0gMykgKw0KICANCiAgICAjIFRoZW1lcw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKDEwLCAxMiwgMTQsIDE2LCAxOCwgMjAsIDIyKSkgKw0KICAgIHRoZW1lX2xpZ2h0KCkgKw0KICAgIGxhYnMoeCA9ICJZZWFycyBvZiBFZHVjYXRpb24iLA0KICAgICAgICAgeSA9ICJJbmNvbWUiKSArDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLA0KICAgICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KHNpemUgPSAyLCBjb2xvciA9ICJncmV5NTAiKSkNCg0KIyBSaWdodCBQbG90DQpmaXRfMl8yX3JpZ2h0X2xvZXNzIDwtIGZpdHRlZChsb2VzcyhJbmNvbWUgfiBFZHVjYXRpb24sIGRhdGEgPSBJbmNvbWUxKSkgIyBGaXQgYSBsb2VzcyBjdXJ2ZSBmaXJzdCBhbmQgZ2V0IGZpdHRlZCB2YWx1ZXMgDQoNCmZpZ18yXzJfcmlnaHQgPC0NCiAgDQogIEluY29tZTEgJT4lIA0KICANCiAgICBnZ3Bsb3QoYWVzKHggPSBFZHVjYXRpb24sIHkgPSBJbmNvbWUpKSArDQogIA0KICAgICMgR2VvbXMNCiAgICBnZW9tX3NlZ21lbnQoZGF0YSA9IEluY29tZTEsIHNpemUgPSAxLA0KICAgICAgICAgICAgICAgICBhZXMoeCA9IEVkdWNhdGlvbiwgeSA9IEluY29tZSwgeGVuZCA9IEVkdWNhdGlvbiwgeWVuZCA9IGZpdF8yXzJfcmlnaHRfbG9lc3MpKSArDQogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2UgPSBGLCBjb2xvciA9ICJyb3lhbGJsdWUiKSArDQogICAgZ2VvbV9wb2ludChjb2xvciA9ICJnb2xkIiwgc2l6ZSA9IDMpICsNCiAgDQogICAgIyBUaGVtZXMNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygxMCwgMTIsIDE0LCAxNiwgMTgsIDIwLCAyMikpICsNCiAgICB0aGVtZV9saWdodCgpICsNCiAgICBsYWJzKHggPSAiWWVhcnMgb2YgRWR1Y2F0aW9uIiwNCiAgICAgICAgIHkgPSAiSW5jb21lIikgKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwNCiAgICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChzaXplID0gMiwgY29sb3IgPSAiZ3JleTUwIikpDQoNCiMgUHJpbnQgQ29tYmluZWQgR3JhcGhzDQpmaWdzXzJfMl9jYXB0aW9uIDwtIHBhc3RlKCJGSUdVUkUgMi4yLiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICJMRUZUOiB0aGUgZG90cyBhcmUgdGhlIG9ic2VydmVkIHZhbHVlcyBvZiBpbmNvbWUgYW5kIGVkdWNhdGlvbi4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAiUklHSFQ6IHRoZSBjdXJ2ZSByZXByZXNlbnRzIHRoZSBUUlVFIHJlbGF0aW9uc2hpcCwgd2hpbGUgdGhlIGJsYWNrIGxpbmVzIHJlcHJlc2VudCB0aGUgZXJyb3IuIikNCg0KZ3JpZC5hcnJhbmdlKGdyb2JzID0gbGlzdChmaWdfMl8yX2xlZnQsIGZpZ18yXzJfcmlnaHQpLCBucm93ID0gMSwNCiAgICAgICAgICAgICBib3R0b20gPSB0ZXh0R3JvYih4ID0gMC4wNCwgaGp1c3QgPSAwLCBncCA9IGdwYXIoZm9udHNpemUgPSAxNCwgZm9udCA9IDMpLCBsYWJlbCA9IGZpZ3NfMl8yX2NhcHRpb24pKQ0KDQpgYGANCg0KTW9yZSBnZW5lcmFsbHkgYXMgd2VsbCwgJGYkIG1heSBpbnZvbHZlIG1vcmUgdGhhbiBvbmUgaW5wdXQgdmFyaWFibGUuDQoNCmBgYHtyIEZpZ3VyZSAyLjMuLCBtZXNzYWdlID0gRiwgZmlnLmFsaWduID0gImNlbnRlciIsIGZpZy53aWR0aCA9IDE2LCBmaWcuaGVpZ2h0ID0gOH0NCmZpdF8yXzNfbG9lc3MgPC0gbG9lc3MoSW5jb21lIH4gRWR1Y2F0aW9uICsgU2VuaW9yaXR5LCBkYXRhID0gSW5jb21lMikgIyBGaXQgYSBsb2VzcyBjdXJ2ZSBmaXJzdA0KDQojIFByZWRpY3QgVmFsdWVzIG9uIEV4cGFuZGVkIFhZIEdyaWQNCngucHJlZCA8LSBzZXEobWluKEluY29tZTIkRWR1Y2F0aW9uKSwgbWF4KEluY29tZTIkRWR1Y2F0aW9uKSwgbGVuZ3RoLm91dCA9IDMwKQ0KeS5wcmVkIDwtIHNlcShtaW4oSW5jb21lMiRTZW5pb3JpdHkpLCBtYXgoSW5jb21lMiRTZW5pb3JpdHkpLCBsZW5ndGgub3V0ID0gMzApDQp4eSAgICAgPC0gZXhwYW5kLmdyaWQoRWR1Y2F0aW9uID0geC5wcmVkLCBTZW5pb3JpdHkgPSB5LnByZWQpDQp6LnByZWQgPC0gbWF0cml4KHByZWRpY3QoZml0XzJfM19sb2VzcywgbmV3ZGF0YSA9IHh5KSwgbnJvdyA9IDMwLCBuY29sID0gMzApDQoNCiMgM0QgUGxvdA0KZmlnc18yXzNfY2FwdGlvbiA8LSAiRklHVVJFIDIuMy4gUGxvdCBvZiBpbmNvbWUgYXMgYSBmdW5jdGlvbiBvZiBlZHVjYXRpb24gYW5kIHNlbmlvcml0eS4iDQoNCkluY29tZTIgJT4lIA0KICANCiAgc2NhdHRlcjNEKA0KICAgIA0KICAgIHR5cGUgPSAicCIsDQogICAgeCA9IEluY29tZTIkRWR1Y2F0aW9uLCANCiAgICB5ID0gSW5jb21lMiRTZW5pb3JpdHksIA0KICAgIHogPSBJbmNvbWUyJEluY29tZSwNCiAgICBjb2x2YXIgPSBOQSwgcGNoID0gMTksIGNvbCA9ICJnb2xkIiwgY2V4ID0gMS43NSwNCiAgICBwaGkgPSAyNSwgdGhldGEgPSA0NSwgZXhwYW5kID0gMC42LA0KICAgIHhsYWIgPSAiWWVhcnMgb2YgRWR1Y2F0aW9uIiwgeWxhYiA9ICJTZW5pb3JpdHkiLCB6bGFiID0gIkluY29tZSIsDQogICAgDQogICAgIyBQbG90IFJlZ3Jlc3Npb24gUGxhbmUgRmlyc3QNCiAgICBwYW5lbC5maXJzdCA9IHNjYXR0ZXIzRCgNCiAgICAgICAgICAgICAgICAgICAgeCA9IEluY29tZTIkRWR1Y2F0aW9uLA0KICAgICAgICAgICAgICAgICAgICB5ID0gSW5jb21lMiRTZW5pb3JpdHksDQogICAgICAgICAgICAgICAgICAgIHogPSBJbmNvbWUyJEluY29tZSwNCiAgICAgICAgICAgICAgICAgICAgY29sdmFyID0gTkEsIGNvbCA9ICJibGFjayIsIGFkZCA9IFQsDQogICAgICAgICAgICAgICAgICAgIHN1cmYgPSBsaXN0KHggPSB4LnByZWQsIHkgPSB5LnByZWQsIHogPSB6LnByZWQsIGZpdCA9IHByZWRpY3QoZml0XzJfM19sb2VzcyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2V0cyA9IFQsIGNvbCA9ICJza3libHVlIiwgYm9yZGVyID0gInJveWFsYmx1ZSIsIGFscGhhID0gMC40NSkpDQogICAgKTsgdGV4dCh4ID0gMCwgeSA9IDAsIHBvcyA9IDEsIG9mZnNldCA9IDIwLCBmb250ID0gMywgY2V4ID0gMS4yNSwgbGFiZWxzID0gZmlnc18yXzNfY2FwdGlvbiwgeHBkID0gVCkNCmBgYA0KDQojIyAyLjEuMS4gV2h5IEVzdGltYXRlICRmJD8NCg0KVGhlIHR3byBtYWluIHJlYXNvbnMgYXJlIDxiPlByZWRpY3Rpb248L2I+IGFuZCA8Yj5JbmZlcmVuY2U8L2I+Lg0KDQojIyMgUHJlZGljdGlvbg0KDQotIE1vc3Qgbm90YWJsZSB3aGVuIHRoZSBpbnB1dCB2YXJpYWJsZXMgYXJlIGF2YWlsYWJsZSBidXQgdGhlIG91dHB1dCBpcyBub3QuDQotIFdlIGNhbiBwcmVkaWN0ICRZJCB1c2luZyAkXGhhdHtZfSA9IFxoYXR7Zn0oWCkkIHdoZXJlICRcaGF0e2Z9KFgpJCBpcyBhbiBlc3RpbWF0ZSBvZiAkZiQgYW5kICRcaGF0e1l9JCBpcyB0aGUgcHJlZGljdGVkIG91dGNvbWUuDQotIFdoZW4gcHJlZGljdGlvbiBpcyB0aGUgbWFpbiBmb2N1cywgdGhlIGV4YWN0IGZvcm0gb2YgJGYkIGlzIGxlc3Mgb2YgYSBjb25jZXJuIGFzIG9wcG9zZWQgdG8gaW1wcm92aW5nIHByZWRpY3Rpb24gYWNjdXJhY3kuDQogIC0gVGhlIGFjY3VyYWN5IG9mIHRoZSBwcmVkaWN0aW9uIGRlcGVuZHMgb24gdGhlIDxiPnJlZHVjaWJsZTwvYj4gYW5kIDxiPmlycmVkdWNpYmxlPC9iPiBlcnJvci4NCiAgLSA8Yj5yZWR1Y2libGU8L2I+OiB0aGlzIGVycm9yIGNhbiBiZSByZWR1Y2VkIGJ5IGFwcGx5aW5nIHRoZSBtb3N0IGFwcHJvcHJpYXRlIHN0YXRpc3RpY2FsIGxlYXJuaW5nIHRlY2huaXF1ZSB0byBlc3RpbWF0ZSAkZiQuDQogIC0gPGI+aXJyZWR1Y2libGU8L2I+OiBlcnJvciBkdWUgdG8gdGhlIHZhcmlhYmlsaXR5IGluICRcZXBzaWxvbiQgd2hpY2ggY2Fubm90IGJlIHByZWRpY3RlZCBieSAkWCQuIFRoaXMgY2FuIGJlIGR1ZSB0byBub3QgaW5jbHVkaW5nIG90aGVyIHNpZ25pZmljYW50IHByZWRpY3RvcnMsIGFuZC9vciB1bm1lYXN1cmFibGUgdmFyaWF0aW9uLg0KICAtIDxwIHN0eWxlPSJ0ZXh0LWFsaWduOmp1c3RpZnkiPlRoaXMgY2FuIGJlIHNob3duIG1hdGhlbWF0aWNhbGx5IGJ5IHNwbGl0dGluZyB0aGUgZXhwZWN0ZWQgdmFsdWUgb2YgdGhlIHNxdWFyZWQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBvYnNlcnZlZCBhbmQgcHJlZGljdGVkIHZhbHVlIG9mICRZJCwgb3IgJEUoWSAtIFxoYXR7WX0pXjIkLCBpbnRvICRcdW5kZXJicmFjZXtbZihYKSAtIFxoYXR7Zn0oWCldXjJ9X1x0ZXh0e1JlZHVjaWJsZX0gKyBcdW5kZXJicmFjZXtWYXIoXGVwc2lsb24pfV9cdGV4dHtJcnJlZHVjaWJsZX0kIHdoZXJlIHRoZSBsYXR0ZXIgdGVybSByZXByZXNlbnRzIHRoZSA8Yj52YXJpYW5jZTwvYj4gaW4gdGhlIGVycm9yIHRlcm0gJFxlcHNpbG9uJC48L3A+DQogIA0KIyMjIEluZmVyZW5jZQ0KDQotIFdlIGFsc28gZXN0aW1hdGUgJGYkIHRvIGRlcml2ZSBpbmZlcmVuY2VzIGZyb20gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBpbnB1dCBhbmQgdGhlIG91dHB1dCBzcGVjaWZpY2FsbHkgaW4gaG93ICRZJCBjaGFuZ2VzIGFzIGEgZnVuY3Rpb24gb2YgJFhfMSwgLi4uLCBYX3AkLg0KLSBUaGUgYWNjdXJhY3kgb2Ygb3VyIHByZWRpY3Rpb25zIGlzbid0IG1hZGUgYXMgaW1wb3J0YW50IGFzIG91ciB1bmRlcnN0YW5kaW5nIG9mIHRoaXMgcmVsYXRpb25zaGlwLCBzbyB0aGUgZXhhY3QgZm9ybSBvZiAkZiQgbXVzdCBiZSA8Yj5rbm93bjwvYj4uDQotIFNvbWUgaW5zaWdodHMgdGhhdCBhcmUgdXNlZnVsIGFyZToNCiAgLSBJZGVudGlmeWluZyA8aT5pbXBvcnRhbnQ8L2k+IHByZWRpY3RvcnMgdGhhdCBhcmUgb2Z0ZW4gYSA8Yj5zdWJzZXQ8L2I+IG9mIHRoZSBlbnRpcmUgbGlzdCBvZiBwb3NzaWJsZSB2YXJpYWJsZXMNCiAgLSBEZXRlcm1pbmluZyB0aGUgPGI+cG9zaXRpdmUvbmVnYXRpdmUgcmVsYXRpb25zaGlwPC9iPiBiZXR3ZWVuIHRoZSBwcmVkaWN0b3IgYW5kIHRoZSByZXNwb25zZQ0KICAtIE1ha2luZyBhZGp1c3RtZW50cyB0byB0aGUgPGI+YXNzdW1wdGlvbjwvYj4gb2YgJGYkIGhhdmluZyBhIGxpbmVhciBmb3JtIHNvIGFzIHRvIGZpbmQgdGhlIG1vc3QgYWNjdXJhdGUgcmVwcmVzZW50YXRpb24gb2YgdGhpcyByZWxhdGlvbnNoaXAgd2hpY2ggaXMgb2Z0ZW4gbW9yZSBjb21wbGljYXRlZA0KICANCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmp1c3RpZnkiPg0KSXQgaXMgd29ydGggbm90aW5nIHRoYXQgZGlmZmVyZW50IHNpdHVhdGlvbnMgd2lsbCBjYWxsIGZvciBlaXRoZXIgb2YgdGhlIHR3bywgb3IgYm90aOKAlGl0IGRlcGVuZHMgb24gdGhlIDxiPnF1ZXN0aW9uczwvYj4gdGhhdCB3ZSB3YW50IHRvIGFuc3dlciBrbm93aW5nIHRoYXQgc29tZSBtYXkgb2ZmZXIgbW9yZSB2YWx1ZSB0aGFuIG90aGVycy4gRWl0aGVyIHdheSwgZGlmZmVyZW50IHN0YXRpc3RpY2FsIGxlYXJuaW5nIG1ldGhvZHMgbWF5IGJlIGxlc3MvbW9yZSBhcHByb3ByaWF0ZS4gTGluZWFyIG1vZGVscyBmb3IgZXhhbXBsZSwgYWxsb3cgZm9yIHNpbXBsZSBhbmQgaW50ZXJwcmV0YWJsZSBpbmZlcmVuY2VzLCBidXQgYXJlIG5vdCB0aGUgbW9zdCBhY2N1cmF0ZSBpbiBwcmVkaWN0aW9ucy4gSW4gY29udHJhc3QsIG1ldGhvZHMgdGhhdCBhY2NvdW50IGZvciBub24tbGluZWFyaXR5IGNhbiBiZSB2ZXJ5IGFjY3VyYXRlLCBidXQgbWF5IG5vdCBiZSB0aGUgbW9zdCBpbnRlcnByZXRhYmxlIGNob2ljZSBmb3IgaW5mZXJlbmNlLiBUaGlzIGlzIGFuIDxiPmltcG9ydGFudCBtaW5kc2V0PC9iPiB0aGF0IG11c3QgYmUga2VwdCB3aGlsZSBzdHVkeWluZyB0aGUgbWV0aG9kcyBpbiB0aGUgYm9vay4NCjwvcD4NCg0KIyMgMi4xLjIuIEhvdyBEbyBXZSBFc3RpbWF0ZSAkZiQ/DQoNCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmp1c3RpZnkiPg0KTWFueSBvZiB0aGUgbWV0aG9kcyBkaXNjdXNzZWQgaW4gdGhlIGJvb2sgc2hhcmUgY2VydGFpbiBjaGFyYWN0ZXJpc3RpY3MgYXMgd2lsbCBiZSBkaXNjdXNzZWQgaW4gdGhpcyBzZWN0aW9uLiBUaGUgc2V0IG9mICRuJCBkaWZmZXJlbnQgZGF0YSBwb2ludHMgYXJlIGNhbGxlZCB0aGUgPGI+IHRyYWluaW5nIGRhdGE8L2I+IGluIHRoYXQgdGhlc2Ugb2JzZXJ2YXRpb25zIGFyZSB1c2VkIHRvIHRyYWluIHRoZSBzZWxlY3RlZCBtZXRob2Qgb24gaG93IHRvIGVzdGltYXRlICRmJC4gSWYgJHhfe2lqfSQgcmVwcmVzZW50cyB0aGUgdmFsdWUgb2YgdGhlICRqJHRoIHByZWRpY3RvciBmb3Igb2JzZXJ2YXRpb24gJGkkLCBhbmQgJHlfaSQgcmVwcmVzZW50cyB0aGUgcmVzcG9uc2UgZm9yIHRoZSAkaSR0aCBvYnNlcnZhdGlvbiwgdGhlbiBvdXIgdHJhaW5pbmcgZGF0YSBjb25zaXN0cyBvZiAkXHsoeF8xLHlfMSksICh4XzIseV8yKSwgLi4uLCAoeF9uLHlfbilcfSQgd2l0aCAkeF9pID0gKHhfe2kxfSwgeF97aTJ9LCB4X3tpM30sIC4uLiwgeF97aXB9KV57VH0kLg0KPC9wPg0KDQo8Yj5Hb2FsOjwvYj4gYXBwbHkgYSBTdGF0aXN0aWNhbCBMZWFybmluZyBtZXRob2QgdG8gdGhlIHRyYWluaW5nIGRhdGEgdG8gZXN0aW1hdGUgJGYkLiBUaGVzZSBtZXRob2RzIGNhbiBiZSBjaGFyYWN0ZXJpemVkIGFzIGVpdGhlciA8Yj5wYXJhbWV0cmljPC9iPiBvciA8Yj5ub24tcGFyYW1ldHJpYzwvYj4uDQoNCiMjIyBQYXJhbWV0cmljIE1ldGhvZHMNCg0KVGhpcyBpcyBhIHR3by1zdGVwLCBtb2RlbC1iYXNlZCBhcHByb2FjaCB0aGF0IHJlZHVjZXMgdGhlIHByb2JsZW0gb2YgZXN0aW1hdGluZyAkZiQgZG93biB0byBlc3RpbWF0aW5nIGEgc2V0IG9mIHBhcmFtZXRlcnMuIA0KDQojIyMjIFByb3MNCi0gVGhpcyBpcyBtdWNoIGVhc2llciB0aGFuIGZpdHRpbmcgYW4gZW50aXJlbHkgYXJiaXRyYXJ5IGZ1bmN0aW9uLg0KDQojIyMjIENvbnMNCi0gVGhlIG1vZGVsIGNob3NlbiB3aWxsIHVzdWFsbHkgbm90IGZvbGxvdyB0aGUgdHJ1ZSBmb3JtIG9mICRmJC4NCi0gSWYgdGhlIGZpdCBpcyB0b28gZmFyIGZyb20gdGhlIHRydWUgZm9ybSwgdGhlIGVzdGltYXRlIHdpbGwgYmUgcG9vci4NCi0gV2hpbGUgdGhpcyBjYW4gYmUgYWxsZXZpYXRlZCBieSB1c2luZyBtb3JlIGZsZXhpYmxlIG1vZGVscyB3aXRoIG1vcmUgcGFyYW1ldGVycyB0byBlc3RpbWF0ZSwgZG9pbmcgc28gY2FuIGxlYWQgdG8gPGI+b3ZlcmZpdHRpbmc8L2I+IHdoZXJlaW4gdGhlIG1vZGVsIGZvbGxvd3MgdGhlIGVycm9ycyB0b28gY2xvc2VseS4NCg0KIyMjIyBTdGVwcw0KMS4gV2UgZmlyc3QgbWFrZSBhbiBhc3N1bXB0aW9uIGFib3V0IHRoZSBmb3JtIG9mICRmJC4gQSBsaW5lYXIgbW9kZWwgZm9yIGV4YW1wbGUgYXNzdW1lcyB0aGF0ICRmJCBpcyBsaW5lYXIgaW4gJFgkLg0KMi4gV2UgdGhlbiBuZWVkIGEgcHJvY2VkdXJlIHRoYXQgPGk+Zml0czwvaT4gdGhlIHRyYWluaW5nIGRhdGEgdG8gdGhlIG1vZGVsLiBGb3IgdGhlIGxpbmVhciBtb2RlbCwgdGhpcyBtZWFucyBlc3RpbWF0aW5nIHRoZSBwYXJhbWV0ZXJzICRcYmV0YV8xLCBcYmV0YV8yLCAuLi4sIFxiZXRhX3AkLiBUaGUgbW9zdCBjb21tb24gbWV0aG9kIHRvIGZpdCB0aGlzIG1vZGVsIGlzIHRocm91Z2ggPGI+b3JkaW5hcnkgbGVhc3Qgc3F1YXJlczwvYj4gKGNoZWNrIFt0aGlzXShodHRwOi8vZWNvbi51Y3NiLmVkdS9+ZG91Zy8yNDBhL1RoZSUyMERpc2NvdmVyeSUyMG9mJTIwU3RhdGlzdGljYWwlMjBSZWdyZXNzaW9uLmh0bSM6fjp0ZXh0PUxlZ2VuZHJlJTIwd2FzJTIwdGhlJTIwZmlyc3QlMjB0byx1c2UlMjBvZiUyMGxlYXN0JTIwc3F1YXJlcyUyMHJlZ3Jlc3Npb24uKSBvdXQgZm9yIGEgYnJpZWYgaGlzdG9yeSBsZXNzb24gb24gT0xTISkuDQoNCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmp1c3RpZnkiPg0KQXBwbHlpbmcgYSBsaW5lYXIgZml0IHRvIHRoZSA8Y29kZT5JbmNvbWU8L2NvZGU+IGRhdGFzZXQsIHdoZXJlaW4gd2UgZml0ICRpbmNvbWUgXGFwcHJveCBcYmV0YV8wICsgXGJldGFfMSBcdGltZXMgZWR1Y2F0aW9uICsgXGJldGFfMSBcdGltZXMgc2VuaW9yaXR5JCwgd2Ugc2VlIHRoYXQgdGhlIGZpdCBkb2VzIGEgcmVhc29uYWJsZSBqb2Igb2YgY2FwdHVyaW5nIHRoZSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcHJlZGljdG9ycyBhbmQgdGhlIHJlc3BvbnNlLCBidXQgZmFpbHMgdG8gY2FwdHVyZSB0aGUgY3VydmF0dXJlIHByZXNlbnQgaW4gdGhlIHRydWUgJGYkIGFzIHNlZW4gaW4gRmlndXJlIDIuMy4uDQo8L3A+DQoNCmBgYHtyIEZpZ3VyZSAyLjQuICYgRmlndXJlIDIuNS4sIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmFsaWduID0gImNlbnRlciIsIGZpZy53aWR0aCA9IDE2LCBmaWcuaGVpZ2h0ID0gOH0NCnBhcihtZnJvdyA9IGMoMSwgMikpDQoNCiMgLS0tIExpbmVhciBGaXQNCmZpdF8yXzRfbG0gPC0gbG0oSW5jb21lIH4gRWR1Y2F0aW9uICsgU2VuaW9yaXR5LCBkYXRhID0gSW5jb21lMikNCg0KIyBQcmVkaWN0IFZhbHVlcyBvbiBFeHBhbmRlZCBYWSBHcmlkDQp6LnByZWRfbG0gPC0gbWF0cml4KHByZWRpY3QoZml0XzJfNF9sbSwgbmV3ZGF0YSA9IHh5KSwgbnJvdyA9IDMwLCBuY29sID0gMzApDQoNCiMgM0QgUGxvdCBvZiBMaW5lYXIgTW9kZWwNCmZpZ3NfMl80X2NhcHRpb24gPC0gIkZJR1VSRSAyLjQuIExpbmVhciBtb2RlbCBmaXQgYnkgT0xTIHRvIHRoZSBJbmNvbWUgZGF0YXNldC4iDQoNCkluY29tZTIgJT4lIA0KICANCiAgc2NhdHRlcjNEKA0KICAgIA0KICAgIHR5cGUgPSAicCIsDQogICAgeCA9IEluY29tZTIkRWR1Y2F0aW9uLCANCiAgICB5ID0gSW5jb21lMiRTZW5pb3JpdHksIA0KICAgIHogPSBJbmNvbWUyJEluY29tZSwNCiAgICBjb2x2YXIgPSBOQSwgcGNoID0gMTksIGNvbCA9ICJnb2xkIiwgY2V4ID0gMS43NSwNCiAgICBwaGkgPSAyNSwgdGhldGEgPSA0NSwgZXhwYW5kID0gMC42LA0KICAgIHhsYWIgPSAiWWVhcnMgb2YgRWR1Y2F0aW9uIiwgeWxhYiA9ICJTZW5pb3JpdHkiLCB6bGFiID0gIkluY29tZSIsDQogICAgDQogICAgIyBQbG90IFJlZ3Jlc3Npb24gUGxhbmUgRmlyc3QNCiAgICBwYW5lbC5maXJzdCA9IHNjYXR0ZXIzRCgNCiAgICAgICAgICAgICAgICAgICAgeCA9IEluY29tZTIkRWR1Y2F0aW9uLA0KICAgICAgICAgICAgICAgICAgICB5ID0gSW5jb21lMiRTZW5pb3JpdHksDQogICAgICAgICAgICAgICAgICAgIHogPSBJbmNvbWUyJEluY29tZSwNCiAgICAgICAgICAgICAgICAgICAgY29sdmFyID0gTkEsIGNvbCA9ICJibGFjayIsIGFkZCA9IFQsDQogICAgICAgICAgICAgICAgICAgIHN1cmYgPSBsaXN0KHggPSB4LnByZWQsIHkgPSB5LnByZWQsIHogPSB6LnByZWRfbG0sIGZpdCA9IHByZWRpY3QoZml0XzJfNF9sbSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2V0cyA9IFQsIGNvbCA9ICJ2aW9sZXQiLCBib3JkZXIgPSAicHVycGxlIiwgYWxwaGEgPSAwLjQ1KSkNCiAgICApOyB0ZXh0KHggPSAwLCB5ID0gMCwgcG9zID0gMSwgb2Zmc2V0ID0gMjAsIGZvbnQgPSAzLCBjZXggPSAxLjI1LCBsYWJlbHMgPSBmaWdzXzJfNF9jYXB0aW9uLCB4cGQgPSBUKQ0KDQojIC0tLSBTbW9vdGggKCEpIFRoaW4tUGxhdGUgU3BsaW5lIEZpdA0KSW5jb21lMl9YX3NjYWxlZCA8LSBzY2FsZShJbmNvbWUyW2MoIkVkdWNhdGlvbiIsICJTZW5pb3JpdHkiKV0pICMgU2NhbGUgWCBmaXJzdA0KSW5jb21lMyA8LSBkYXRhLmZyYW1lKEluY29tZTJfWF9zY2FsZWQsIEluY29tZSA9IEluY29tZTIkSW5jb21lKQ0KDQpmaXRfMl81X3RwIDwtIGdhbShJbmNvbWUgfiBzKEVkdWNhdGlvbiwgU2VuaW9yaXR5LCBicyA9ICJ0cCIpLCBkYXRhID0gSW5jb21lMykNCg0KIyBQcmVkaWN0IFZhbHVlcyBvbiBFeHBhbmRlZCBYWSBHcmlkDQp4LnByZWRfdHAgPC0gc2VxKG1pbihJbmNvbWUzJEVkdWNhdGlvbiksIG1heChJbmNvbWUzJEVkdWNhdGlvbiksIGxlbmd0aC5vdXQgPSAzMCkNCnkucHJlZF90cCA8LSBzZXEobWluKEluY29tZTMkU2VuaW9yaXR5KSwgbWF4KEluY29tZTMkU2VuaW9yaXR5KSwgbGVuZ3RoLm91dCA9IDMwKQ0KeHlfdHAgICAgIDwtIGV4cGFuZC5ncmlkKEVkdWNhdGlvbiA9IHgucHJlZF90cCwgU2VuaW9yaXR5ID0geS5wcmVkX3RwKQ0Kei5wcmVkX3RwIDwtIG1hdHJpeChwcmVkaWN0KGZpdF8yXzVfdHAsIG5ld2RhdGEgPSB4eV90cCksIG5yb3cgPSAzMCwgbmNvbCA9IDMwKQ0KDQojIFRyYW5zZm9ybSBCYWNrIHRvIE9yaWdpbmFsIFNjYWxlDQpzY2FsZWRfY2VudGVyIDwtIGF0dHIoSW5jb21lMl9YX3NjYWxlZCwgInNjYWxlZDpjZW50ZXIiKQ0Kc2NhbGVkX3NjYWxlICA8LSBhdHRyKEluY29tZTJfWF9zY2FsZWQsICJzY2FsZWQ6c2NhbGUiKQ0KSW5jb21lMyRFZHVjYXRpb25fb3JpZyA8LSB4LnByZWRfdHAgKiBzY2FsZWRfc2NhbGVbMV0gKyBzY2FsZWRfY2VudGVyWzFdDQpJbmNvbWUzJFNlbmlvcml0eV9vcmlnIDwtIHkucHJlZF90cCAqIHNjYWxlZF9zY2FsZVsyXSArIHNjYWxlZF9jZW50ZXJbMl0NCg0KIyAzRCBQbG90IG9mIFRoaW4tUGxhdGUgU3BsaW5lIFJlZ3Jlc3Npb24gTW9kZWwNCmZpZ3NfMl81X2NhcHRpb24gPC0gIkZJR1VSRSAyLjUuIFNtb290aCB0aGluLXBsYXRlIHNwbGluZSBmaXQuIg0KDQpJbmNvbWUzICU+JSANCiAgDQogIHNjYXR0ZXIzRCgNCiAgICANCiAgICB0eXBlID0gInAiLA0KICAgIHggPSBJbmNvbWUyJEVkdWNhdGlvbiwNCiAgICB5ID0gSW5jb21lMiRTZW5pb3JpdHksDQogICAgeiA9IEluY29tZTIkSW5jb21lLA0KICAgIGNvbHZhciA9IE5BLCBwY2ggPSAxOSwgY29sID0gImdvbGQiLCBjZXggPSAxLjc1LA0KICAgIHBoaSA9IDI1LCB0aGV0YSA9IDQ1LCBleHBhbmQgPSAwLjYsDQogICAgeGxhYiA9ICJZZWFycyBvZiBFZHVjYXRpb24iLCB5bGFiID0gIlNlbmlvcml0eSIsIHpsYWIgPSAiSW5jb21lIiwNCiAgICANCiAgICAjIFBsb3QgUmVncmVzc2lvbiBQbGFuZSBGaXJzdA0KICAgIHBhbmVsLmZpcnN0ID0gc2NhdHRlcjNEKA0KICAgICAgICAgICAgICAgICAgICB4ID0gSW5jb21lMiRFZHVjYXRpb24sIA0KICAgICAgICAgICAgICAgICAgICB5ID0gSW5jb21lMiRTZW5pb3JpdHksIA0KICAgICAgICAgICAgICAgICAgICB6ID0gSW5jb21lMiRJbmNvbWUsDQogICAgICAgICAgICAgICAgICAgIGNvbHZhciA9IE5BLCBjb2wgPSAiYmxhY2siLCBhZGQgPSBULA0KICAgICAgICAgICAgICAgICAgICBzdXJmID0gbGlzdCh4ID0geC5wcmVkLCB5ID0geS5wcmVkLCB6ID0gei5wcmVkX3RwLCBmaXQgPSBwcmVkaWN0KGZpdF8yXzVfdHApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNldHMgPSBULCBjb2wgPSAidmlvbGV0IiwgYm9yZGVyID0gInB1cnBsZSIsIGFscGhhID0gMC40NSkpDQogICAgKTsgdGV4dCh4ID0gMCwgeSA9IDAsIHBvcyA9IDEsIG9mZnNldCA9IDIwLCBmb250ID0gMywgY2V4ID0gMS4yNSwgbGFiZWxzID0gZmlnc18yXzVfY2FwdGlvbiwgeHBkID0gVCkNCmBgYA0KDQojIyMgTm9uLVBhcmFtZXRyaWMgTWV0aG9kcw0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjpqdXN0aWZ5Ij4NClRoZXNlIG1ldGhvZHMgZG8gbm90IG1ha2UgYXNzdW1wdGlvbnMgYWJvdXQgdGhlIGZvcm0gb2YgJGYkLCBidXQgaW5zdGVhZCBhaW0gdG8gZ2V0IGFuIGVzdGltYXRlIG9mICRmJCB0aGF0IGdldHMgYXMgY2xvc2UgdG8gdGhlIGRhdGEgcG9pbnRzIGFzIHBvc3NpYmxlIHdpdGhvdXQgImJlaW5nIHRvbyByb3VnaCBvciB3aWdnbHksIiB3aGljaCBJIHRha2UgdG8gbWVhbiBhcyBub3Qgb3ZlcmZpdHRpbmcgb3ZlciB0aGUgdHJhaW5pbmcgZGF0YS4NCjwvcD4NCg0KIyMjIyBQcm9zDQotIEJ5IG5vdCBoYXZpbmcgc3RyaWN0IGFzc3VtcHRpb25zIGFib3V0ICRmJCwgdGhlc2UgbWV0aG9kcyBhbGxvdyBmb3IgYSBncmVhdGVyIHJhbmdlIG9mIHNoYXBlcyB3aGlsZSBhY2N1cmF0ZWx5IGZpdHRpbmcgJGYkLg0KDQojIyMjIENvbnMNCi0gQSBsYXJnZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGlzIG5lZWRlZCB0byBhY2N1cmF0ZWx5IGVzdGltYXRlICRmJCwgbXVjaCBtb3JlIHRoYW4gdGhhdCBuZWVkZWQgb2YgYSBwYXJhbWV0cmljIGFwcHJvYWNoLg0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjpqdXN0aWZ5Ij4NCkFuIGV4YW1wbGUgaXMgZ2l2ZW4gYnkgRmlndXJlIDIuNS4gd2hlcmVpbiBhIDxpPnRoaW4tcGxhdGUgc3BsaW5lPC9pPiBpcyB1c2VkIHRvIGZpdCAkZiQgd2l0aG91dCBhIHByZS1zcGVjaWZpZWQgbW9kZWwuIE5vdGUsIGhvd2V2ZXIsIHRoYXQgaXQgZG9lcyBzbyBzdWNoIHRoYXQgdGhlIGVzdGltYXRlZCBmaXQgaXMgY2xvc2UgdG8gdGhlIGRhdGEsIGJ1dCBpcyBzdGlsbCA8Yj5zbW9vdGg8L2I+LiBDb21wYXJpbmcgdGhpcyB0byB0aGUgdHJ1ZSAkZiQgaW4gRmlndXJlIDIuMy4sIHdlIHNlZSB0aGF0IHRoZSBlc3RpbWF0ZSBpcyBhY2N1cmF0ZSBpbmRlZWQuIEFub3RoZXIgY2hhcmFjdGVyaXN0aWMgb2YgYSBub24tcGFyYW1ldHJpYyBtZXRob2Qgc3VjaCBhcyBmaXR0aW5nIGEgdGhpbi1wbGF0ZSBzcGxpbmUgaXMgdGhlIG5lZWQgdG8gY2hvb3NlIGEgbGV2ZWwgb2Ygc21vb3RobmVzcy4gVGhlIHBsb3QgYmVsb3cgZm9yIGV4YW1wbGUgaGFzIGEgbG93ZXIgbGV2ZWwgb2Ygc21vb3RoaW5nIGFuZCB0aHVzIHJlc3VsdHMgaW4gYSA8Yj5yb3VnaDwvYj4gZml0IHRoYXQgZm9sbG93cyB0aGUgb2JzZXJ2ZWQgZGF0YSBwZXJmZWN0bHkuIEluIGRvaW5nIHNvLCB3ZSBoYXZlIG92ZXJmaXR0ZWQgb24gdGhlIGRhdGEsIGFuZCB3ZSBjYW4gZXhwZWN0IHRvIGhhdmUgaW5hY2N1cmF0ZSBlc3RpbWF0ZXMgb24gbmV3IGRhdGEgdGhhdCBpcyBub3QgcGFydCBvZiB0aGUgb3JpZ2luYWwgdHJhaW5pbmcgZGF0YS4gVGh1cywgYW5vdGhlciBmYWN0b3IgdG8gY29uc2lkZXIgaXMgaG93IHdlIHdpbGwgY2hvb3NlIHRoZSBjb3JyZWN0IGFtb3VudCBvZiBzbW9vdGhpbmcuDQo8L3A+DQoNCmBgYHtyIEZpZ3VyZSAyLjYuLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBmaWcud2lkdGggPSAxNiwgZmlnLmhlaWdodCA9IDh9DQojIC0tLSBSb3VnaCAoISkgVGhpbi1QbGF0ZSBTcGxpbmUgRml0DQpJbmNvbWUyX1hfc2NhbGVkIDwtIHNjYWxlKEluY29tZTJbYygiRWR1Y2F0aW9uIiwgIlNlbmlvcml0eSIpXSkgIyBTY2FsZSBYIGZpcnN0DQpJbmNvbWUzIDwtIGRhdGEuZnJhbWUoSW5jb21lMl9YX3NjYWxlZCwgSW5jb21lID0gSW5jb21lMiRJbmNvbWUpDQoNCiMgLSBrID0gMzAgc2luY2UgdGhlcmUgYXJlIDMwIGV4YWN0IHVuaXF1ZSBwb2ludHMgaW4gb3VyIGRhdGEsIHRoZXJlYnkgZGlzYWJsaW5nIGxvdy1yYW5rIGFwcHJveGltYXRpb24NCiMgLSBzcCA9IDAgZm9yIGRpc2FibGluZyBwZW5hbGl6YXRpb24NCiMgLSBicCA9ICJ0cCIgdG8gdXNlIHRoaW4gcGxhdGUgcmVncmVzc2lvbiBzcGxpbmUgYmFzaXMgZnVuY3Rpb24NCg0KZml0XzJfNl90cCA8LSBnYW0oSW5jb21lIH4gcyhFZHVjYXRpb24sIFNlbmlvcml0eSwgayA9IDMwLCBzcCA9IDAsIGJzID0gInRwIiksIGRhdGEgPSBJbmNvbWUzKQ0KDQojIFByZWRpY3QgVmFsdWVzIG9uIEV4cGFuZGVkIFhZIEdyaWQNCnoucHJlZF9ydHAgPC0gbWF0cml4KHByZWRpY3QoZml0XzJfNl90cCwgbmV3ZGF0YSA9IHh5X3RwKSwgbnJvdyA9IDMwLCBuY29sID0gMzApDQoNCiMgM0QgUGxvdCBvZiBUaGluLVBsYXRlIFNwbGluZSBSZWdyZXNzaW9uIE1vZGVsDQpmaWdzXzJfNl9jYXB0aW9uIDwtICJGSUdVUkUgMi42LiBSb3VnaCB0aGluLXBsYXRlIHNwbGluZSBmaXQgd2l0aCB6ZXJvIGVycm9ycy4iDQoNCkluY29tZTMgJT4lIA0KICANCiAgc2NhdHRlcjNEKA0KICAgIA0KICAgIHR5cGUgPSAicCIsDQogICAgeCA9IEluY29tZTIkRWR1Y2F0aW9uLA0KICAgIHkgPSBJbmNvbWUyJFNlbmlvcml0eSwNCiAgICB6ID0gSW5jb21lMiRJbmNvbWUsDQogICAgY29sdmFyID0gTkEsIHBjaCA9IDE5LCBjb2wgPSAiZ29sZCIsIGNleCA9IDEuNzUsDQogICAgcGhpID0gMjUsIHRoZXRhID0gNDUsIGV4cGFuZCA9IDAuNiwNCiAgICB4bGFiID0gIlllYXJzIG9mIEVkdWNhdGlvbiIsIHlsYWIgPSAiU2VuaW9yaXR5IiwgemxhYiA9ICJJbmNvbWUiLA0KICAgIA0KICAgICMgUGxvdCBSZWdyZXNzaW9uIFBsYW5lIEZpcnN0DQogICAgcGFuZWwuZmlyc3QgPSBzY2F0dGVyM0QoDQogICAgICAgICAgICAgICAgICAgIHggPSBJbmNvbWUyJEVkdWNhdGlvbiwgDQogICAgICAgICAgICAgICAgICAgIHkgPSBJbmNvbWUyJFNlbmlvcml0eSwgDQogICAgICAgICAgICAgICAgICAgIHogPSBJbmNvbWUyJEluY29tZSwNCiAgICAgICAgICAgICAgICAgICAgY29sdmFyID0gTkEsIGNvbCA9ICJibGFjayIsIGFkZCA9IFQsDQogICAgICAgICAgICAgICAgICAgIHN1cmYgPSBsaXN0KHggPSB4LnByZWQsIHkgPSB5LnByZWQsIHogPSB6LnByZWRfcnRwLCBmaXQgPSBwcmVkaWN0KGZpdF8yXzZfdHApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNldHMgPSBULCBjb2wgPSAidmlvbGV0IiwgYm9yZGVyID0gInB1cnBsZSIsIGFscGhhID0gMC40NSkpDQogICAgKTsgdGV4dCh4ID0gMCwgeSA9IDAsIHBvcyA9IDEsIG9mZnNldCA9IDIwLCBmb250ID0gMywgY2V4ID0gMS4yNSwgbGFiZWxzID0gZmlnc18yXzZfY2FwdGlvbiwgeHBkID0gVCkNCg0KYGBgDQoNCiMjIDIuMS4zLiBUaGUgVHJhZGUtT2ZmIEJldHdlZW4gUHJlZGljdGlvbiBBY2N1cmFjeSBhbmQgTW9kZWwgSW50ZXJwcmV0YWJpbGl0eQ0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjpqdXN0aWZ5Ij4NCkFzIHdlIHNhdyBpbiBzb21lIG9mIHRoZSBleGFtcGxlcyBzbyBmYXIsIHNvbWUgc3RhdGlzdGljYWwgbGVhcm5pbmcgbWV0aG9kcyBhcmUgbGVzcyBmbGV4aWJsZS9tb3JlIHJlc3RyaWN0aXZlIHRoYW4gb3RoZXJzIChlLmcuIE9MUyByZWdyZXNzaW9uKS4gVGhlcmUgYXJlIHJlYXNvbnMgZm9yIGNob29zaW5nIHN1Y2ggbWV0aG9kcyBhcyBvcHBvc2VkIHRvIHRoZSBmbGV4aWJsZSBhcHByb2FjaGVzIChlLmcuIHRoaW4tcGxhdGUgc3BsaW5lIHJlZ3Jlc3Npb24pOg0KPC9wPg0KLSBJZiBJbmZlcmVuY2UgaXMgb3VyIG1haW4gZm9jdXMsIHRoZW4gdGhlc2UgcmVzdHJpY3RpdmUgbWV0aG9kcyBhcmUgcHJlZmVyYWJsZSBpbiB0aGF0IGl0IGlzIGVhc3kgdG8gdW5kZXJzdGFuZCBhbmQgaW50ZXJwcmV0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiAkWCQgYW5kICRZJC4NCi0gTW9yZSBmbGV4aWJsZSBtZXRob2RzIG9mdGVuIGxlYWQgdG8gY29tcGxleCBlc3RpbWF0ZXMgdGhhdCBtYWtlIHRoaXMgYXNzZXNzbWVudCBvZiBpbmRpdmlkdWFsIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBlYWNoIHByZWRpY3RvciBhbmQgdGhlIHJlc3BvbnNlIHZlcnkgZGlmZmljdWx0Lg0KLSBFdmVuIHdoZW4gcHJlZGljdGlvbiBpcyB0aGUgc29sZSBpbnRlcmVzdCwgdGhlc2UgcmVzdHJpY3RpdmUgYXBwcm9hY2hlcyBjYW4gc29tZXRpbWVzIG91dHBlcmZvcm0gdGhlIG1vc3QgZmxleGlibGUgbWV0aG9kIGF2YWlsYWJsZSBiZWNhdXNlIG9mIHRoZSBsYXR0ZXIgb3ZlcmZpdHRpbmcuDQoNCiMjIDIuMS40LiBTdXBlcnZpc2VkIFZlcnN1cyBVbnN1cGVydmlzZWQgTGVhcm5pbmcNCg0KPHAgc3R5bGU9InRleHQtYWxpZ246anVzdGlmeSI+DQpTdGF0aXN0aWNhbCBsZWFybmluZyBwcm9ibGVtcyBjYW4gYmUgY2F0ZWdvcml6ZWQgaW50byA8Yj5zdXBlcnZpc2VkPC9iPiBhbmQgPGI+dW5zdXBlcnZpc2VkPC9iPi4gVGhlIG1haW4gZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28gaXMgdGhhdCwgaW4gdGhlIGZvcm1lciwgZm9yIGVhY2ggb2JzZXJ2YXRpb24gdGhlcmUgaXMgYW4gYXNzb2NpYXRlZCByZXNwb25zZSwgYW5kIHRoYXQgcmVsYXRpb25zaGlwIGlzIG1vZGVsZWQgd2l0aCBlaXRoZXIgcHJlZGljdGlvbiBvciBpbmZlcmVuY2UgaW4gbWluZCB3aGVyZWFzIGluIHRoZSBsYXR0ZXIsIHdlIGhhdmUgcmVhbGl6ZWQgdmFsdWVzIG9mICR4X2kkIGZvciBlYWNoIG9ic2VydmF0aW9uICRpID0gMSwgLi4uLCBuJCwgYnV0IG5vIHJlc3BvbnNlLiBUaGVyZSBpcyBubyByZXNwb25zZSB2YXJpYWJsZSB0byA8aT5zdXBlcnZpc2U8L2k+IHRoZSBtZXRob2QgdGhhdCB3ZSBhcmUgdXNpbmcuIE9uZSBzdWNoIG1ldGhvZCBpcyA8Yj5DbHVzdGVyIEFuYWx5c2lzPC9iPiB3aGljaCBzZWVrcyB0byBncm91cCBvYnNlcnZhdGlvbnMgaW50byBkaXN0aW5jdCBjbHVzdGVycyBiYXNlZCBvbiB2YWx1ZXMgb2YgdGhlIGlucHV0IHZhcmlhYmxlcyB3aGljaCBtYXkgdHJhbnNsYXRlIGZvciBleGFtcGxlIHRvIHNlZ21lbnRpbmcgY3VzdG9tZXJzIGJhc2VkIG9uIHNwZW5kaW5nIGhhYml0cyBhbmQgZGVtb2dyYXBoaWMgZmFjdG9ycy4NCjxicj4NCjxicj4NClNvbWUgcHJvYmxlbXMgY2FuIGFsc28gY2FsbCBmb3IgPGI+c2VtaS1zdXBlcnZpc2VkPC9iPiBsZWFybmluZyBtZXRob2RzIHdoZXJlaW4gYSBzdWJzZXQgb2YgdGhlIGRhdGEgY29udGFpbnMgYXNzb2NpYXRlZCByZXNwb25zZSB2YWx1ZXMgd2hpbGUgYW5vdGhlciBzdWJzZXQgZG9lcyBub3QuIEFuIGV4YW1wbGUgb2YgdGhpcyBzY2VuYXJpbyBpcyB3aGVuIG1lYXN1cmluZyB0aGUgcmVzcG9uc2UgaXMgbXVjaCBtb3JlIGV4cGVuc2l2ZSB0aGFuIHJlYWRpbHkgYmVpbmcgYWJsZSB0byBtZWFzdXJlIHRoZSBwcmVkaWN0b3JzLiBUaHVzLCBjbGFzc2lmeWluZyBwcm9ibGVtcyBiZXR3ZWVuIHN1cGVydmlzZWQgYW5kIHVuc3VwZXJ2aXNlZCBtYXkgbm90IGFsd2F5cyBiZSBjbGVhciBmcm9tIHRoZSBnZXQtZ28uDQo8L3A+DQoNCiMjIDIuMS41LiBSZWdyZXNzaW9uIHZlcnN1cyBDbGFzc2lmaWNhdGlvbiBQcm9ibGVtcw0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjpqdXN0aWZ5Ij4NClByb2JsZW1zIHdpdGggYSA8Yj5xdWFudGl0YXRpdmU8L2I+IHJlc3BvbnNlLCBvciB0aG9zZSB3aXRoIG51bWVyaWNhbCB2YWx1ZXMgc3VjaCBhcyBhZ2UgYW5kIGluY29tZSwgYXJlIG9mdGVuIHJlZmVycmVkIHRvIGFzIDxpPnJlZ3Jlc3Npb248L2k+IHByb2JsZW1zLiBUaG9zZSB0aGF0IGRlYWwgd2l0aCBhIDxiPnF1YWxpdGF0aXZlPC9iPiByZXNwb25zZSB3aGVyZWluIHRoZSB2YXJpYWJsZSB0YWtlcyBvbmUgdmFsdWUgb2YgJEskIGRpZmZlcmVudCBjbGFzc2VzL2NhdGVnb3JpZXMgKGUuZy4gZ2VuZGVyLCBwcmVzZW5jZSBvZiBhIGRpc2Vhc2UsIHByb2R1Y3QgYnJhbmQpIGFyZSByZWZlcnJlZCB0byBhcyA8aT5jbGFzc2lmaWNhdGlvbjwvaT4gcHJvYmxlbXMuIFdoZXRoZXIgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMgYXJlIG9mIGVpdGhlciB0eXBlIGlzIGdlbmVyYWxseSBsZXNzIGltcG9ydGFudC4gVGhpcyBkb2VzIG5vdCBtZWFuIGhvd2V2ZXIgdGhhdCBsZXNzIGF0dGVudGlvbiBzaG91bGQgYmUgZ2l2ZW4gdG8gdGhlIGF2YWlsYWJsZSBmZWF0dXJlcywgZXNwZWNpYWxseSB3aGVuIGVmZmVjdGl2ZWx5IGVuZ2luZWVyZWQgYW5kIGNvZGVkLg0KPC9wPg0KDQojIDIuMi4gQXNzZXNzaW5nIE1vZGVsIEFjY3VyYWN5DQoNCkl0IGlzIG5lY2Vzc2FyeSB0byBrbm93IGFuZCB1bmRlcnN0YW5kIG1hbnkgZGlmZmVyZW50IHN0YXRpc3RpY2FsIGxlYXJuaW5nIG1ldGhvZHMgYXMgdGhlcmUgaXMgPGI+bm8gc2luZ2xlIG1ldGhvZDwvYj4gdGhhdCB3b3JrcyBiZXN0IG9uIGFsbCBwb3NzaWJsZSBkYXRhIHNldHMuDQoNCiMjIDIuMi4xLiBNZWFzdXJpbmcgdGhlIFF1YWxpdHkgb2YgRml0DQoNCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmp1c3RpZnkiPg0KRXZhbHVhdGluZyB0aGUgcGVyZm9ybWFuY2Ugb2Ygc3RhdGlzdGljYWwgbGVhcm5pbmcgbWV0aG9kcyByZXF1aXJlIHVzIHRvIHF1YW50aWZ5IGhvdyBjbG9zZSB0aGUgcHJlZGljdGlvbnMgYXJlIHRvIHRoZSBvYnNlcnZlZCB2YWx1ZXMuIEEgdmVyeSBjb21tb25seSB1c2VkIG1lYXN1cmUgaXMgdGhlIDxiPk1lYW4gU3F1YXJlZCBFcnJvcjwvYj4gb3IgTVNFLg0KPC9wPg0KDQokJE1TRSA9IFxmcmFjezF9e259IFxkaXNwbGF5c3R5bGVcc3VtX3tpID0gMX1ee259ICh5X2kgLSBcaGF0e2Z9KHhfaSkpXjIkJA0KDQotIFVzZWQgaW4gdGhlIHJlZ3Jlc3Npb24gc2V0dGluZy4NCi0gJFxoYXR7Zn0oeF9pKSQgaXMgdGhlIHByZWRpY3RlZCB2YWx1ZSBmb3IgdGhlIDxpPmk8L2k+dGggb2JzZXJ2YXRpb24gKGFsc28gJFxoYXR7eX1faSQpLg0KLSBBIHNtYWxsZXIgdmFsdWUgZGVub3RlcyBwcmVkaWN0aW9ucyB0aGF0IGFyZSBjbG9zZXIgdG8gdGhlIHRydWUgdmFsdWUuDQotIENvbXB1dGluZyB0aGUgTVNFIG9uIGEgbW9kZWwgZml0IHdpdGggdGhlIHRyYWluaW5nIGRhdGEgY2FuIGJlIG1vcmUgYWNjdXJhdGVseSBjYWxsZWQgYXMgdGhlIDxiPnRyYWluaW5nIE1TRTwvYj4uDQotIEhvd2V2ZXIsIHdlIGFyZSBtb3JlIGludGVyZXN0ZWQgaW4gdGhlIDxiPnRlc3QgTVNFPC9iPiwgb3Igb25lIHRoYXQgbWVhc3VyZXMgcHJlZGljdGlvbiBhY2N1cmFjeSB3aGVuIHRoZSBtZXRob2QgaXMgYXBwbGllZCB0byB1bnNlZW4gdGVzdCBkYXRhLg0KLSBXZSB0cnkgdG8gc2VsZWN0IHRoZSBtb2RlbCB3aXRoIHRoZSBsb3dlc3QgdGVzdCBNU0UuDQoNCkZvciB1bnNlZW4gdGVzdCBkYXRhICQoeF8wLCB5XzApJCB3ZSBjb21wdXRlIHRoZSBhdmVyYWdlIHNxdWFyZWQgcHJlZGljdGlvbiBlcnJvci4NCg0KJCRBdmUoeV8wIC0gXGhhdHtmfSh4XzApKV4yJCQNCg0KPHAgc3R5bGU9InRleHQtYWxpZ246anVzdGlmeSI+DQpPbmUgd2F5IHRvIGltcGxlbWVudCB0aGUgbWluaW1pemF0aW9uIG9mIHRoZSBNU0UgaXMgdG8gc2V0IGFzaWRlIGRhdGEgdGhhdCB3YXMgbm90IHVzZWQgdG8gdHJhaW4gdGhlIG1vZGVsIHRvIHNlcnZlIGFzIHRoZSB0ZXN0IGRhdGEsIGFuZCBldmFsdWF0ZSB0aGUgbWV0aG9kcyBvbiB0aGF0IHRlc3QgZGF0YS4gSG93ZXZlciB3aGVuIHRlc3QgZGF0YSBpcyBub3QgYXZhaWxhYmxlLCBzaW1wbHkgcmVseWluZyBvbiB0aGUgdHJhaW5pbmcgTVNFIGlzIG5vdCBpZGVhbCBhcyB3ZWxsIHNpbmNlIHRoZXNlIG1ldGhvZHMgY2FuIGhhdmUgdmVyeSBsYXJnZSB0ZXN0IE1TRXMgZGVzcGl0ZSBoYXZpbmcgc21hbGwgdHJhaW5pbmcgTVNFcy4gRnVydGhlcm1vcmUsIHRoZSB0cmFpbmluZyBNU0UgaXMgPGI+bW9ub3RvbmljYWxseSBkZWNyZWFzaW5nPC9iPiBhcyB0aGUgZmxleGliaWxpdHkgb2YgdGhlIG1ldGhvZCBpbmNyZWFzZXM7IHJlbHlpbmcgb24gdHJhaW5pbmcgTVNFIGlzIGxpa2VseSB0byBwdXNoIHVzIHRvIHVzZSB0aGUgbW9zdCBmbGV4aWJsZS9jb21wbGV4IG1ldGhvZCB3aGljaCBjYW4gaGF2ZSBqdXN0IGFzIGJhZCBhIHRlc3QgTVNFIHRoYW4gZXZlbiBhbiBPTFMgbW9kZWwgKGNvbnNpZGVyIHdoZW4gdGhlIHRydWUgJGYkIGlzIGxpbmVhcikuIFRoaXMgaXMgaWxsdXN0cmF0ZWQgd2hlbiB0aGUgdGVzdCBNU0UgaXMgcGxvdHRlZCBhcyBhIGZ1bmN0aW9uIG9mIG1vZGVsIGZsZXhpYmlsaXR5IHdoZXJlIGEgcmVzdWx0aW5nIDxiPlUtc2hhcGVkPC9iPiBjdXJ2ZSBpcyBvYnNlcnZlZC4NCjxicj4NCjxicj4NCldoZW4gdGhlIG1vZGVsIHJlc3VsdHMgaW4gYSA8Yj48c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5zbWFsbDwvc3Bhbj48L2I+IHRyYWluaW5nIE1TRSBidXQgYSA8Yj48c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPmxhcmdlPC9zcGFuPjwvYj4gdGVzdCBNU0UsIHRoZW4gd2UgY2FuIHNheSB0aGF0IHRoZSBtb2RlbCBoYXMgPGI+b3ZlcmZpdHRlZDwvYj4gb24gdGhlIHRyYWluaW5nIGRhdGEgaW4gdGhhdCBpdCBoYXMgZm9sbG93ZWQgdG9vIGNsb3NlbHkgdGhlIG9ic2VydmVkIHRyYWluaW5nIGRhdGEgYW5kIGl0cyBwYXR0ZXJucyB0aGF0IGRvbid0IGV4aXN0IGluIHRoZSB0ZXN0IGRhdGEuIEFub3RoZXIgaW5kaWNhdG9yIGlzIHdoZW4gYSBsZXNzIGZsZXhpYmxlIG1ldGhvZCB3b3VsZCBoYXZlIHJlc3VsdGVkIGludG8gYSBzbWFsbGVyIHRlc3QgTVNFLiBOb3RlIHRoYXQgd2UgYWxzbyBhbG1vc3QgYWx3YXlzIGV4cGVjdCB0aGUgdHJhaW5pbmcgTVNFIHRvIGJlIHNtYWxsZXIgdGhhbiB0aGUgdGVzdCBNU0UgYmVjYXVzZSAibW9zdCBzdGF0aXN0aWNhbCBsZWFybmluZyBtZXRob2RzIGVpdGhlciBkaXJlY3RseSBvciBpbmRpcmVjdGx5IHNlZWsgdG8gbWluaW1pemUgdGhlIHRyYWluaW5nIE1TRS4iDQo8L3A+DQoNCiMjIDIuMi4yLiBUaGUgQmlhcy1WYXJpYW5jZSBUcmFkZS1PZmYNCg0KVGhlIGV4cGVjdGVkIHRlc3QgTVNFIGNhbiBhbHdheXMgYmUgZGVjb21wb3NlZCBpbnRvIHRocmVlIGZ1bmRhbWVudGFsIHRlcm1zIChmb3IgYSBnaXZlbiB2YWx1ZSBvZiAkeF8wJCkgOiB0aGUgPGI+dmFyaWFuY2U8L2I+IG9mICRcaGF0e2Z9KHhfMCkkLCB0aGUgPGI+c3F1YXJlZCBiaWFzPC9iPiBvZiAkXGhhdHtmfSh4XzApJCwgYW5kIHRoZSA8Yj52YXJpYW5jZSBvZiB0aGUgZXJyb3IgdGVybXM8L2I+ICRcZXBzaWxvbiQgd2hpY2ggaXMgYWxzbyB0aGUgaXJyZWR1Y2libGUgZXJyb3IuIEFzIHRoZSBmaXJzdCB0d28gdGVybXMgYXJlIG5vbm5lZ2F0aXZlLCB0aGUgZXhwZWN0ZWQgTVNFIGNhbiBuZXZlciBnbyBiZWxvdyAkVmFyKFxlcHNpbG9uKSQuDQoNClxiZWdpbnthbGlnbn0NCk1TRSAmID0gRSh5XzAgLSBcaGF0e2Z9KHhfMCkpXjIgXFwNCiYgPSBWYXIoXGhhdHtmfSh4XzApKSArIFtCaWFzKFxoYXR7Zn0oeF8wKSldXjIgKyBWYXIoXGVwc2lsb24pDQpcZW5ke2FsaWdufQ0KDQpUaHVzLCB0aGUgb3B0aW1hbCBzdGF0aXN0aWNhbCBsZWFybmluZyBtZXRob2QgbXVzdCBoYXZlIGJvdGggdGhlIHZhcmlhbmNlIGFuZCBiaWFzIGJlIGxvdy4NCg0KLSA8Yj4iRXhwZWN0ZWQgVGVzdCBNU0UiPC9iPjogdGhlIGF2ZXJhZ2UgdGVzdCBNU0Ugb2J0YWluZWQgZnJvbSByZXBlYXRlZGx5IGVzdGltYXRpbmcgJGYkIHVzaW5nIGEgbnVtYmVyIG9mIHRyYWluaW5nIHNldHMsIGFuZCB0ZXN0aW5nIGVhY2ggJFxoYXR7Zn0kIGF0ICR4XzAkLg0KLSA8Yj4iT3ZlcmFsbCBFeHBlY3RlZCBUZXN0IE1TRSI8L2I+OiBvYnRhaW5lZCBieSBhdmVyYWdpbmcgdGhlIGZvcm1lciBvdmVyIEFMTCBwb3NzaWJsZSB2YWx1ZXMgb2YgJHhfMCQgaW4gdGhlIHRlc3Qgc2V0Lg0KLSA8Yj5WYXJpYW5jZTwvYj46IHRoZSBhbW91bnQgb2YgdmFyaWF0aW9uIGluIHRoZSBlc3RpbWF0ZWQgJFxoYXR7Zn0kIG92ZXIgZGlmZmVyZW50IHRyYWluaW5nIHNldHMuIElkZWFsbHksICRcaGF0e2Z9JCBzaG91bGQgbm90IHZhcnkgbXVjaC4gSGlnaCB2YXJpYW5jZSBpbiBhIG1ldGhvZCBjYW4gcmVzdWx0IGluIGxhcmdlIGNoYW5nZXMgaW4gJFxoYXR7Zn0kIGV2ZW4gd2l0aCBzbWFsbCBjaGFuZ2VzIGluIHRoZSB0cmFpbmluZyBzZXQuIEdlbmVyYWxseSwgbW9yZSBmbGV4aWJsZSBtZXRob2RzIGhhdmUgaGlnaGVyIHZhcmlhbmNlLg0KLSA8Yj5CaWFzPC9iPjogdGhlIGVycm9yIGZyb20gYXBwcm94aW1hdGluZyBhIGNvbXBsZXggcGhlbm9tZW5vbiB3aXRoIGEgc2ltcGxlciBtb2RlbDsgYWxzbyB0aGUgaW5hY2N1cmFjeSBvZiB0aGUgcHJlZGljdGlvbiBlc3RpbWF0ZXMuIEdlbmVyYWxseSwgbW9yZSBmbGV4aWJsZSBtZXRob2RzIGhhdmUgbG93ZXIgYmlhcy4gDQoNCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmp1c3RpZnkiPg0KSXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCB0aGUgcmF0ZSBvZiBjaGFuZ2Ugb2YgYm90aCB0aGUgQmlhcyBhbmQgdGhlIFZhcmlhbmNlIGFzIGZsZXhpYmlsaXR5IGluY3JlYXNlcyB3aWxsIGFsc28gZGVwZW5kIG9uIHRoZSBkYXRhIHNldHMgdGhlbXNlbHZlcy4gQXMgdGhlIHR3byBxdWFudGl0aWVzIGRldGVybWluZSB3aGV0aGVyIHRoZSB0ZXN0IE1TRSBpbmNyZWFzZXMvZGVjcmVhc2VzLCB0aGUgYW1vdW50IG9mIGZsZXhpYmlsaXR5IGNvcnJlc3BvbmRpbmcgdG8gdGhlIG9wdGltYWwgdGVzdCBNU0UgYXJlIGRpZmZlcmVudCBhY3Jvc3MgdGhlIGRhdGEgc2V0cy4gVGhlcmUgaXMgYSBwb2ludCwgaG93ZXZlciwgd2hlcmUgaW5jcmVhc2luZyB0aGUgZmxleGliaWxpdHkgb25seSBzbGlnaHRseSBkZWNyZWFzZXMgdGhlIEJpYXMgYnV0IGNhdXNlcyB0aGUgVmFyaWFuY2UgdG8gZ28gdXAsIGNvbnNlcXVlbnRseSBpbmNyZWFzaW5nIHRoZSBNU0Ugb3ZlcmFsbC4gVGh1cywgdGhlcmUgaXMgYSA8Yj50cmFkZS1vZmY8L2I+IGJldHdlZW4gdGhlIHR3bywgYW5kIHRoZSBjaGFsbGVuZ2UgaXMgdG8gZmluZCB0aGUgbWV0aG9kIHRoYXQgb3B0aW1hbGx5IG1pbmltaXplcyBib3RoLg0KPC9wPg0KDQojIyAyLjIuMy4gVGhlIENsYXNzaWZpY2F0aW9uIFNldHRpbmcNCg0KPHAgc3R5bGU9InRleHQtYWxpZ246anVzdGlmeSI+DQpUaGUgYWZvcmVtZW50aW9uZWQgY29uY2VwdHMgYWxzbyBhcHBseSB0byB0aGUgY2xhc3NpZmljYXRpb24gc2V0dGluZyB3aXRoIGNoYW5nZXMgZHVlIHRvICR5X2kkIGJlaW5nIHF1YWxpdGF0aXZlIGluc3RlYWQgb2YgcXVhbnRpdGF0aXZlIHdoZW4gd2UgZXN0aW1hdGUgJGYkIG92ZXIgdGhlIHRyYWluaW5nIGRhdGEuIFRoZSBtb3N0IGNvbW1vbiBhcHByb2FjaCB0byBxdWFudGlmeSB0aGUgYWNjdXJhY3kgb2YgdGhlIGVzdGltYXRlZCAkZiQgaXMgdGhlIDxiPnRyYWluaW5nIGVycm9yIHJhdGU8L2I+IHdoaWNoIGNvbXB1dGVzIHRoZSBwcm9wb3J0aW9uIG9mIGluY29ycmVjdCBjbGFzc2lmaWNhdGlvbnMgYWZ0ZXIgYXBwbHlpbmcgJFxoYXR7Zn0kIHRvIHRoZSB0cmFpbmluZyBkYXRhLg0KPC9wPg0KDQokJA0KXGZyYWN7MX17bn0gXGRpc3BsYXlzdHlsZVxzdW1fe2kgPSAxfV57bn0gSSh5X2kgXG5lcSBcaGF0e3l9X2kpDQpcXA0Kd2hlcmVcIEkoeV9pIFxuZXEgXGhhdHt5fV9pKSA9IA0KICAgICAgICBcYmVnaW57Y2FzZXN9DQogICAgICAgICAgMCAmIFx0ZXh0e2lmICR5X2kgPSBcaGF0e3l9X2kkLCBvciBjb3JyZWN0IGNsYXNzaWZpY2F0aW9ufSBcXA0KICAgICAgICAgIDEgJiBcdGV4dHtpZiAkeV9pIFxuZXEgXGhhdHt5fV9pJCwgb3Igd3JvbmcgY2xhc3NpZmljYXRpb259DQogICAgICAgIFxlbmR7Y2FzZXN9DQokJA0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjpqdXN0aWZ5Ij4NClRoZSA8Yj50ZXN0IGVycm9yIHJhdGU8L2I+IHdoaWNoIGlzIHRoZSByZXN1bHQgYWZ0ZXIgYXBwbHlpbmcgdGhlIGNsYXNzaWZpZXIgdG8gdGhlIHRlc3QgZGF0YSAkKHhfMCwgeV8wKSQgaXMgZ2l2ZW4gYnkgdGhlIGJlbG93IG1lYXN1cmUgd2hlcmUgJFxoYXR7eX1fMCQgaXMgdGhlIHByZWRpY3RlZCBjbGFzcyBsYWJlbC4gV2UgZ2VuZXJhbGx5IGNob29zZSB0aGUgbW9kZWwgd2l0aCB0aGUgbG93ZXN0IHRlc3QgZXJyb3IgcmF0ZS4NCjwvcD4NCg0KJCRBdmUoSSh5XzAgXG5lcSBcaGF0e3l9XzApKSQkDQoNCiMjIyBUaGUgQmF5ZXMgQ2xhc3NpZmllcg0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjpqdXN0aWZ5Ij4NClRoZSBhYm92ZSB0ZXN0IGVycm9yIHJhdGUgY2FuIGJlIHNob3duIChzZWUgU2VjdGlvbiAyLjQgb2YgdGhlIDxpPkVsZW1lbnRzIG9mIFN0YXRpc3RpY2FsIExlYXJuaW5nPC9pPikgdG8gYmUgbWluaW1pemVkIG9uIHRoZSBhdmVyYWdlIGJ5IGNsYXNzaWZ5aW5nIGVhY2ggb2JzZXJ2YXRpb24gdG8gdGhlIG1vc3QgcHJvYmFibGUgY2xhc3MsIG9yIHdoZXJlIHRoZSBiZWxvdyA8Yj5jb25kaXRpb25hbCBwcm9iYWJpbGl0eTwvYj4gZm9yIGEgZ2l2ZW4gdGVzdCBvYnNlcnZhdGlvbiB3aXRoIHByZWRpY3RvciB2ZWN0b3IgJHhfMCQgdG8gdGhlIGNsYXNzICRqJCBpcyB0aGUgPGI+bGFyZ2VzdDwvYj4uIFRoaXMgaXMgcmVmZXJyZWQgdG8gYXMgdGhlIDxiPkJheWVzIENsYXNzaWZpZXI8L2I+Og0KPC9wPg0KDQokJFByKFkgPSBqXCB8XCBYID0geF8wKSQkDQo8cCBzdHlsZT0idGV4dC1hbGlnbjpqdXN0aWZ5Ij4NCkluIGEgdHdvLWNsYXNzIHNldHRpbmcsIHRoaXMgY2xhc3NpZmllciBjb3JyZXNwb25kcyB0byBwcmVkaWN0aW5nIGEgcmVzcG9uc2UgdmFsdWUgb2YgPGk+Y2xhc3Mgb25lPC9pPiB3aGVuICRQcihZID0gMVwgfFwgWCA9IHhfMCkgPiAwLjUkLCBhbmQgcmV0dXJucyA8aT5jbGFzcyB0d288L2k+IG90aGVyd2lzZS4gSWYgcGxvdHRlZCwgYSBsaW5lIGRyYXduIGZyb20gcG9pbnRzIHdoZXJlIHRoZSBjb25kaXRpb25hbCBwcm9iYWJpbGl0eSBpcyBleGFjdGx5IDUwJSBpcyBjYWxsZWQgdGhlIDxiPkJheWVzIERlY2lzaW9uIEJvdW5kYXJ5PC9iPi4gQWRkaXRpb25hbGx5LCB0aGUgQmF5ZXMgQ2xhc3NpZmllciBwcm9kdWNlcyB0aGUgPGI+QmF5ZXMgRXJyb3IgUmF0ZTwvYj4gd2hpY2ggaXMgdGhlIGxvd2VzdCB0ZXN0IGVycm9yIHJhdGUgcG9zc2libGUgZm9yIGEgY2xhc3NpZmllciB3aXRoIGEgcmFuZG9tIG91dGNvbWUsIGFuZCBpcyBhbHNvIGFuYWxvZ291cyB0byB0aGUgaXJyZWR1Y2libGUgZXJyb3IuIFRoaXMgaXMgZ2l2ZW4gYmVsb3cgd2hlcmUgdGhlIHByb2JhYmlsaXR5IG92ZXIgYWxsIHBvc3NpYmxlIHZhbHVlcyBvZiAkWCQgYXJlIGF2ZXJhZ2VkIG92ZXIgdGhlIGV4cGVjdGF0aW9uOg0KPC9wPg0KDQokJDEgLSBFKFx1bmRlcnNldHtqfXttYXh9UHIoWSA9IGpcIHxcIFgpJCQNCg0KIyMjIEstTmVhcmVzdCBOZWlnaGJvcnMNCg0KPHAgc3R5bGU9InRleHQtYWxpZ246anVzdGlmeSI+DQpXaGVuIHRoZSBjb25kaXRpb25hbCBkaXN0cmlidXRpb24gb2YgJFkkIGdpdmVuICRYJCBpcyBub3Qga25vd24gYXMgaW4gcmVhbC13b3JsZCBkYXRhLCB0aGUgQmF5ZXMgQ2xhc3NpZmllciBjYW5ub3QgYmUgY29tcHV0ZWQuIFRoZSA8Yj5LLU5lYXJlc3QgTmVpZ2hib3JzPC9iPiBpcyBhbiBhcHByb2FjaCB0aGF0IGVzdGltYXRlcyB0aGlzIGNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbiBhbmQgcHJlZGljdHMgYSBjbGFzcyBmb3IgYSB0ZXN0IG9ic2VydmF0aW9uIHdpdGggdGhlIGhpZ2hlc3QgZXN0aW1hdGVkIHByb2JhYmlsaXR5LiBUaGlzIGlzIGRvbmUgYnk6DQo8L3A+DQoxLiBDaG9vc2luZyB0aGUgJEskIG5lYXJlc3QgcG9pbnRzIGNsb3Nlc3QgdG8gdGhlIHRyYWluaW5nIG9ic2VydmF0aW9uICR4XzAkLCByZXByZXNlbnRlZCBieSAkTl8wJA0KMi4gRXN0aW1hdGluZyB0aGUgY29uZGl0aW9uYWwgcHJvYmFiaWxpdHkgZm9yIGNsYXNzICRqJCwgb3IgdGhlIG51bWJlciBvZiBwb2ludHMgd2l0aCByZXNwb25zZSB2YWx1ZXMgZXF1YWwgdG8gJGokIGRpdmlkZWQgYnkgJEskOjxicj48Y2VudGVyPiRQcihZID0galwgfFwgWCA9IHhfMCkgPSBcZnJhY3sxfXtLfVxkaXNwbGF5c3R5bGVcc3VtX3tpXCBcaW5cIE5fMH1JKHlfaSA9IGopJDwvY2VudGVyPg0KMy4gQ2xhc3NpZnlpbmcgdGhlIG9ic2VydmF0aW9uIHRvIHRoZSBjbGFzcyB3aXRoIHRoZSBsYXJnZXN0IHByb2JhYmlsaXR5DQoNClRoZSBLTk4gY2xhc3NpZmllciBjYW4gcGVyZm9ybSBjbG9zZWx5IHRvIHRoZSBCYXllcyBDbGFzc2lmaWVyLCBidXQgdGhlIGNob2ljZSBvZiAkSyQgYWZmZWN0cyB0aGUgZmxleGliaWxpdHkgb2YgdGhlIG1vZGVsLg0KDQotIFdoZW4gJEskID0gMSwgdGhlIGNsYXNzaWZpZXIgaGFzIGxvdyBiaWFzIGFuZCBoaWdoIHZhcmlhbmNlLg0KLSBBcyAkSyQgaW5jcmVhc2VzLCBpdCBiZWNvbWVzIGxlc3MgZmxleGlibGUgYW5kIHByb2R1Y2VzIGEgZGVjaXNpb24gYm91bmRhcnkgbmVhciB0byBsaW5lYXI7IGkuZS4gaXQgaGFzIGhpZ2ggYmlhcyBhbmQgbG93IHZhcmlhbmNlLg0KDQo8cCBzdHlsZT0idGV4dC1hbGlnbjpqdXN0aWZ5Ij4NClNpbWlsYXIgdG8gdGhlIHJlZ3Jlc3Npb24gc2V0dGluZywgdGhlIHRyYWluaW5nIGVycm9yIHJhdGUgZGVjcmVhc2VzIGFzIHRoZSBmbGV4aWJpbGl0eSBpbmNyZWFzZXMuIFRoZSB0ZXN0IGVycm9yIHJhdGUgYWxzbyBzaW1pbGFybHkgZXhoaWJpdHMgYSBVLXNoYXBlZCBjdXJ2ZSB3aGVyZSB0aGUgbW9kZWwgZXZlbnR1YWxseSBvdmVyZml0cy4NCjwvcD4NCg==