Session 12: Introduction to Estimation Theory: understanding the logic of statistical modeling. Introduction to covariance, correlation, and Simple Linear Regression.

Feedback should be send to goran.milovanovic@datakolektiv.com. These notebooks accompany the Intro to Data Science: Non-Technical Background course 2020/21.


What do we want to do today?

Correlations, correlations everywhere..! One would think that all that Data Scientists do nowadays is to look for them. Not even half true, however, they are of essential importance for our work. In this session, we introduce the concept of correlation, and expand it - in a gentle way - towards simple linear regression.

0. Prerequisits

Install:

install.packages('corrplot')
install.packages('Hmisc')
dataDir <- paste0(getwd(), "/_data/")

Setup:

library(tidyverse)
library(Hmisc)
library(ppcor)
library(corrplot)

1. Covariance, Variable Standardization, and Correlation

1.1 Linear relationships

We will start by inspecting two variables from the iris data set: Sepal.Length and Petal.Length:

# - plot layout: 2 x 2
par(mfcol = c(2, 2))
# - boxplot iris$Sepal.Length
boxplot(iris$Sepal.Length,
        horizontal = TRUE, 
        xlab = "Sepal Length")
# - histogram: iris$Sepal.Length
hist(iris$Sepal.Length, 
     main = "",
     xlab = "Sepal.Length", 
     prob = T)
# - overlay iris$Sepal.Length density 
# - function over the empirical distribution
lines(density(iris$Sepal.Length),
      lty = "dashed", 
      lwd = 2.5, 
      col = "red")
# - boxplot iris$Petal.Length
boxplot(iris$Petal.Length,
        horizontal = TRUE, 
        xlab = "Petal Length")
# - histogram: iris$Petal.Length,
hist(iris$Petal.Length,
     main = "",
     xlab = "Petal Length", 
     prob = T)
# - overlay iris$Petal.Length density 
# - function over the empirical distribution
lines(density(iris$Petal.Length),
      lty = "dashed", 
      lwd = 2.5, 
      col = "red")
# reset plot layout
par(mfcol = c(1, 1))

Q. Is there a linear relationship between these two variables? Let’s see:

# scatter plot w. {base}
plot(iris$Sepal.Length, iris$Petal.Length,
     main = "Sepal Length vs Petal Length",
     xlab = "Sepal Length", ylab = "Petal Length",
     cex.main = .85,
     cex.lab = .75)

One could think there is something going on here. But:

ggplot(data = iris, aes(x = Sepal.Length,
                        y = Petal.Length,
                        color = Species)
       ) + 
  geom_point() + 
  geom_smooth(method = "lm", se = F, size = .25) +
  theme_bw() + 
  theme(panel.border = element_blank())

There seems to be more than one important line to describe this data set… However, we will simplify for now:

ggplot(data = iris, aes(x = Sepal.Length,
                        y = Petal.Length)) + 
  geom_point(color = "black", size = 2) + 
  geom_point(color = "white", size = 1.5) +
  geom_smooth(method = lm, se = F, size = .25, color = "red") +
  theme_bw() + 
  theme(panel.border = element_blank())

1.2 Covariance and Standardization

Leaving aside the important question of whether there is a linear relationship between Sepal.Length and Petal.Length in iris for now, we ask: if it was a linear relationship, how good a linear relationship would it make? The answer is provided by computing the Pearson’s coefficient of linear correlation.

First things first. What is this:

cov(iris$Sepal.Length, iris$Petal.Length)
[1] 1.274315

Covariance. Given two random variables (RVs), \(X\) and \(Y\), their (sample) covariance is given by:

\[cov(X,Y) = E[(X-E[X])(Y-E[Y])] = \frac{(X-\bar{X})(Y-\bar{Y})}{N-1}\] where \(E[]\) denotes the expectation (the mean, if you prefer), \(\bar{X}\) is the mean of \(X\), \(\bar{Y}\) is the mean of \(Y\), and \(N\) is the sample size.

1.3 Correlation

Pearson’s coefficient of correlation is nothing else than a covariance between \(X\) and \(Y\) upon their standardization. The standardization of a RV - widely known as a variable z-score - is obtained upon subtracting all of its values from the mean, and dividing by the standard deviation; for the i-th observation of \(X\):

\[z(x_i) = \frac{x_i-\bar{X}}{\sigma}\] Thus,

zSepalLength <- (iris$Sepal.Length - mean(iris$Sepal.Length))/sd(iris$Sepal.Length)
zPetalLength <- (iris$Petal.Length-mean(iris$Petal.Length))/sd(iris$Petal.Length)
cov(zSepalLength, zPetalLength)
[1] 0.8717538

is the correlation of Sepal.Length and Petal.Length; let’s check with {base} R function cor() which computes the correlation:

cor(iris$Sepal.Length, iris$Petal.Length, method = "pearson")
[1] 0.8717538

Right. There are many formulas that compute r, the correlation coefficient; however, understanding that is simply the covariance of standardized RVs is essential. Once you know to standardize the variables and how to compute covariance (and that is easy), you don’t need to care about expressions like:

\[r_{XY} = \frac{N\sum{XY}-(\sum{X})(\sum{Y})}{\sqrt{[N\sum{X^2}-(\sum{X})^2][N\sum{Y^2}-(\sum{Y})^2]}}\]

This and similar expressions are good, and especially for two purposes: first, they will compute the desired value of the correlation coefficient in the end, that’s for sure, and second, writing them up in RMarkdown really helps mastering \(\LaTeX\). Besides these roles they play, there is really nothing essentially important in relation to them.

Somewhat easier to remember:

\[r_{XY} = \frac{cov(X,Y)}{\sigma(X)\sigma(Y)}\] - the covariance of \(X\) and \(Y\), divided by the product of their standard deviations.

There’s a nice scale() function that will quicken-up the computation of z-scores in R for you:

zSepalLength1 <-  scale(iris$Sepal.Length, center = T, scale = T)
sum(zSepalLength1 == zSepalLength) == length(zSepalLength)
[1] TRUE

Do ?scale - useful things can be done with it.

2. Correlation Matrices: Visualization and Treatment of Missing Values

The {base} cor() function produces correlation matrices too:

cor(iris[ , c(1:4)])
             Sepal.Length Sepal.Width Petal.Length Petal.Width
Sepal.Length    1.0000000  -0.1175698    0.8717538   0.8179411
Sepal.Width    -0.1175698   1.0000000   -0.4284401  -0.3661259
Petal.Length    0.8717538  -0.4284401    1.0000000   0.9628654
Petal.Width     0.8179411  -0.3661259    0.9628654   1.0000000

Missing data can be treated by listwise or pairwise deletion. In listwise deletion, any observation (== row) containing at least one NA(s) will be removed before the computation. Set the use argument in cor to complete.obs in order to use listwise deletion:

dSet <- iris
# Remove one nominal variable - Species
dSet$Species <- NULL
# introduce NA in dSet$Sepal.Length[5]
dSet$Sepal.Length[5] <- NA
# Pairwise and Listwise Deletion:
cor1a <- cor(dSet, use="complete.obs") # listwise deletion
cor1a
             Sepal.Length Sepal.Width Petal.Length Petal.Width
Sepal.Length    1.0000000  -0.1099640    0.8708659   0.8165243
Sepal.Width    -0.1099640   1.0000000   -0.4219569  -0.3590627
Petal.Length    0.8708659  -0.4219569    1.0000000   0.9624228
Petal.Width     0.8165243  -0.3590627    0.9624228   1.0000000

Pairwise deletion will compute the correlation coefficient using all available data. It will delete only the data corresponding to the missing values from one vector in another, and compute the correlation coefficient from what is left; set use to pairwise.complete.obs to use this approach:

cor1b <- cor(dSet, use = "pairwise.complete.obs") # pairwise deletion
cor1b
             Sepal.Length Sepal.Width Petal.Length Petal.Width
Sepal.Length    1.0000000  -0.1099640    0.8708659   0.8165243
Sepal.Width    -0.1099640   1.0000000   -0.4284401  -0.3661259
Petal.Length    0.8708659  -0.4284401    1.0000000   0.9628654
Petal.Width     0.8165243  -0.3661259    0.9628654   1.0000000

use = "all.obs" will produce an error in the presence of any NAs:

cor1c <- cor(dSet, use = "all.obs") # all observations - error
Error in cor(dSet, use = "all.obs") : missing observations in cov/cor
cor1c
Error: object 'cor1c' not found

To propagate NAs through the matrix wherever they are present in the respective columns, use = "everything" (this is the default; try cor(dSet)):

cor1d <- cor(dSet, use = "everything")
cor1d
             Sepal.Length Sepal.Width Petal.Length Petal.Width
Sepal.Length            1          NA           NA          NA
Sepal.Width            NA   1.0000000   -0.4284401  -0.3661259
Petal.Length           NA  -0.4284401    1.0000000   0.9628654
Petal.Width            NA  -0.3661259    0.9628654   1.0000000

There are many available methods to visualize correlation matrices in R. The {base} approach would be to use plot() on a data.frame like in the following example:

# {base} approach
data("mtcars")
corMatrix <- cor(mtcars[, 1:8])
plot(as.data.frame(corMatrix))

But there’s also the fantastic {corrplot} package to visualize correlation matrices:

# {corrplot} approach
corMatrix <- cor(mtcars)
# {corrplot} "circle" method: 
corrplot(corMatrix, 
         method = "circle")

# {corrplot} "ellipse" method: 
corrplot(corMatrix, 
         method = "ellipse")

# "mixed"
corrplot.mixed(corMatrix, 
               lower="ellipse", 
               upper="circle")

3. Introduction to Simple Linear Regression

We now begin considering the mathematical modeling of data in R. The first - and arguably the simplest - statistical model that we will face is the Simple Linear Regression Model. In a typical simple linear regression setting, we have one continuous predictor - also known as the independent variable - and one continuous criterion - a.k.a. the dependent variable. Both these are assumed to be unbounded, i.e. taking values across the whole domain of real numbers. Continuity here should be understood precisely as having measurements from an interval or ratio scale.

Linear regression does not imply any causality; it is up to the user of the model to impose causal assumptions, i.e. which variable takes the role of the criterion and which variable is assigned as a predictor. It is not even necessary to impose any such assumptions in order to obtain a valid linear regression model, although it is very customary to have some hypothesized direction of causality in order to discuss prediction meaningfully.

3.1 Linear Correlation, Assumption of Linearity, and Causality

## Pearson correlation in R {base}
cor1 <- cor(iris$Sepal.Length, iris$Petal.Length, 
            method = "pearson")
cor1
[1] 0.8717538

With \(R\) = .87 we hope to be able to say that there is a linear relationship, right? Time to learn something important about statistics: you can never rely on a conclusion that was reached by taking the values of the statistics prima facie while doing nothing else. Take a look at the scatter plot of these two variables again:

# Let's test the assumption of linearity:
ggplot(iris, aes(x = Sepal.Length, y = Petal.Length)) +
  geom_point(size = 2, color = 'black') +
  geom_point(size = 1.5, color = 'white') +
  geom_smooth(method = 'lm', size = .25, color = 'red', se = F) +
  ggtitle('Sepal Length vs Petal Length') +
  theme_bw() + 
  theme(panel.border = element_blank())

We have included the best fitting regression line in the scatter plot; does the relationship between the two variable really looks linear? Let’s remind ourselves of what we have already discovered:

ggplot(data = iris, aes(x = Sepal.Length,
                        y = Petal.Length,
                        color = Species)
       ) + 
  geom_point(size = 1.5) +
  geom_smooth(method = 'lm', size = .25, se = F) + 
  ggtitle('Sepal Length vs Petal Length') + 
  theme_bw() + 
  theme(panel.border = element_blank())

Interesting: there seem to be more than one linear relationship in this scatter plot, i.e. one per each group from the iris data set. What do we do, except for concluding that the assumption of linearity has failed? We will introduce a fix in one of our next sessions, showing how a multiple regression model can account for situations like the present one; in the meantime, pretend like nothing has happened..

By the way, is the \(R\) coefficient of linear correlation statistically significant?

# Is Pearson's product-moment correlation coefficient significant?
cor2 <- rcorr(iris$Sepal.Length, # {hmisc}
              iris$Petal.Length, 
              type="pearson")
# correlations
cor2$r
          x         y
x 1.0000000 0.8717538
y 0.8717538 1.0000000
cor2$r[1, 2] # Ok, the one we're looking for
[1] 0.8717538
cor2$P[1, 2] # significant at
[1] 0

Well, \(R\) is statistically significant indeed. Most social science students would typically conclude that everything’s superfine here… Don’t be lazy: (a) do the EDA of your variables before modeling, (b) inspect the scatter plot in smart ways - if there are natural groupings expected in the data set, use colors or shapes to mark them. In spite of the high, statistically significant Pearson’s correlation coefficient between Sepal.Length and Petal.Length, this relationship violates linearity, and a model more powerful than simple linear regression is needed.

However, let’s pretend we’ve never seen this and start doing simple linear regression in R.

3.2 Simple Linear Regression: The Model

We will now consider the following statistical model of a linear relationship between two random variables:

\[Y = \beta_0 + \beta_1X_1 + \epsilon \]

  • \(Y\) is the variable whose values we would like to be able to predict - and it is called a criterion or a dependent variable - from
  • \(X\), which is called a predictor, or an independent variable in the Simple Linear Regression setting;
  • \(\beta_0\) and \(\beta_1\) are model parameters, of which the former represents the intercept while the later is the slope of the regression line (note: besides \(\epsilon\), what the equation represents is nothing else but the equation of a straight line in a plane that you have seen a dozen times in high school); finally,
  • \(\epsilon\) represents the model error term, which we will discuss in length in our Session.

If we assume that the relationship between \(X\) and \(Y\) is indeed linear - and introduce some additional assumptions that we will discuss in our next sessiosn - the following question remains:

What values of \(\beta_0\) and \(\beta_1\) would pick a line in a plane spawned by \(X\) and \(Y\) values so that it describes the assumed linear relationship between them the best?

Again:

# Let's test the assumption of linearity:
ggplot(iris, aes(x = Sepal.Length, y = Petal.Length)) +
  geom_point(size = 2, color = 'black') +
  geom_point(size = 1.5, color = 'white') +
  geom_smooth(method = 'lm', size = .25, color = 'red', se = F) +
  ggtitle('Sepal Length vs Petal Length') +
  theme_bw() + 
  theme(panel.border = element_blank())

The line in this {ggplot2} scatter plot is the best fitting line for the assumed linear relationship between iris$Sepal.Length (taken to be a predictor, X-axis) and iris$Petal.Length (taken to be a criterion, Y-axis). {ggplot2} computed the best fitting line for us: how? Well, in the end, it did it by selecting the optimal values for \(\beta_0\) and \(\beta_1\). It is our task in this and the following sessions to figure out how does that selection of optimal parameter values takes place.

3.3 Simple Linear Regression w. lm() in R

In R we have the lm() function - short for linear model - to fit all different kinds of models in the scope of this model framework to the data:

### --- Linear Regression with lm()
# Predicting: Petal Length from Sepal Length
reg <- lm(Petal.Length ~ Sepal.Length, 
          data=iris) 
class(reg)
[1] "lm"

The Petal.Length ~ Sepal.Length is called a formula, and you should learn more about how formulas in R are syntactically composed. The simplest possible formula, like this one, simply informs R that we wish to model Petal.Length - standing to the left of ~ - by taking only Sepal.Length - standing to the right - as a predictor. For those who are already familiar with a multiple linear regression setting, doing A ~ B + C means calling for a linear model with A as a dependent variable and B, C as predictors. We will let these things complicate in the future, don’t worry. The object reg stores the results of our attempt at a simple linear regression model, and has its own class of lm, as you can observe following the call to class(reg).

Let’s inspect the result more thoroughly:

summary(reg)

Call:
lm(formula = Petal.Length ~ Sepal.Length, data = iris)

Residuals:
     Min       1Q   Median       3Q      Max 
-2.47747 -0.59072 -0.00668  0.60484  2.49512 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -7.10144    0.50666  -14.02   <2e-16 ***
Sepal.Length  1.85843    0.08586   21.65   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.8678 on 148 degrees of freedom
Multiple R-squared:   0.76, Adjusted R-squared:  0.7583 
F-statistic: 468.6 on 1 and 148 DF,  p-value: < 2.2e-16

The output provides:

  • a call that has generated the linear model object reg;
  • a basic overview of descriptive statistics for model residuals;
  • a table of regression coefficients: there are only two for the simple linear regression model, namely the model intercept and the slope (i.e. the regression coefficient for the one and only predictor in the model), and the raw (not standardized) values of the predictors are reported in the Estimate column, accompanied by respective standard errors, t-test against zero, and the probabilities of commiting to a \(Type I\) error in drawing conclusions from these t-tests;
  • Residual Standard Error;
  • Multiple \(R^2\) and the Adjusted \(R^2\) values;
  • The \(F\) test: ratio of variances computed from the regression and residual SSEs, with the respective number of degrees of freedom and its p-value.

To isolate the regression coefficients from the model:

coefsReg <- coefficients(reg)
coefsReg
 (Intercept) Sepal.Length 
   -7.101443     1.858433 
slopeReg <- coefsReg[2]
print(paste0("Slope: ", slopeReg))
[1] "Slope: 1.85843297825484"
interceptReg <- coefsReg[1]
print(paste0("Intercept: ", interceptReg))
[1] "Intercept: -7.10144336960246"

You can also work with the summary() of the lm class as an object:

sReg <- summary(reg)
str(sReg)
List of 11
 $ call         : language lm(formula = Petal.Length ~ Sepal.Length, data = iris)
 $ terms        :Classes 'terms', 'formula'  language Petal.Length ~ Sepal.Length
  .. ..- attr(*, "variables")= language list(Petal.Length, Sepal.Length)
  .. ..- attr(*, "factors")= int [1:2, 1] 0 1
  .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. ..$ : chr [1:2] "Petal.Length" "Sepal.Length"
  .. .. .. ..$ : chr "Sepal.Length"
  .. ..- attr(*, "term.labels")= chr "Sepal.Length"
  .. ..- attr(*, "order")= int 1
  .. ..- attr(*, "intercept")= int 1
  .. ..- attr(*, "response")= int 1
  .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. ..- attr(*, "predvars")= language list(Petal.Length, Sepal.Length)
  .. ..- attr(*, "dataClasses")= Named chr [1:2] "numeric" "numeric"
  .. .. ..- attr(*, "names")= chr [1:2] "Petal.Length" "Sepal.Length"
 $ residuals    : Named num [1:150] -0.9766 -0.6049 -0.3332 0.0527 -0.7907 ...
  ..- attr(*, "names")= chr [1:150] "1" "2" "3" "4" ...
 $ coefficients : num [1:2, 1:4] -7.1014 1.8584 0.5067 0.0859 -14.0161 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:2] "(Intercept)" "Sepal.Length"
  .. ..$ : chr [1:4] "Estimate" "Std. Error" "t value" "Pr(>|t|)"
 $ aliased      : Named logi [1:2] FALSE FALSE
  ..- attr(*, "names")= chr [1:2] "(Intercept)" "Sepal.Length"
 $ sigma        : num 0.868
 $ df           : int [1:3] 2 148 2
 $ r.squared    : num 0.76
 $ adj.r.squared: num 0.758
 $ fstatistic   : Named num [1:3] 469 1 148
  ..- attr(*, "names")= chr [1:3] "value" "numdf" "dendf"
 $ cov.unscaled : num [1:2, 1:2] 0.34087 -0.05719 -0.05719 0.00979
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:2] "(Intercept)" "Sepal.Length"
  .. ..$ : chr [1:2] "(Intercept)" "Sepal.Length"
 - attr(*, "class")= chr "summary.lm"

For example:

sReg$r.squared
[1] 0.7599546

Correlation is then:

sqrt(sReg$r.squared)
[1] 0.8717538
sReg$fstatistic
   value    numdf    dendf 
468.5502   1.0000 148.0000 
sReg$coefficients
              Estimate Std. Error   t value     Pr(>|t|)
(Intercept)  -7.101443 0.50666229 -14.01613 6.133586e-29
Sepal.Length  1.858433 0.08585565  21.64602 1.038667e-47

Now, the distribution of residuals - the \(\epsilon\) in the model equation - to be discussed in the Session:

hist(sReg$residuals, 20, probability = T,
     main = 'Residuals',
     xlab = 'Residuals', ylab = 'Density',
     col = "orange")
densRegRes <- data.frame(x  = sReg$residuals,
                         y = dnorm(sReg$residuals, 
                                   mean(sReg$residuals), 
                                   sd(sReg$residuals)))
densRegRes <- densRegRes[order(densRegRes$x), ]
lines(densRegRes,
      lty = "dashed", 
      lwd = 1, 
      col = "blue")

Interesting: from the last histogram, would you say that the residuals in this linear model are normally distributed?

R Markdown

R Markdown is what I have used to produce this beautiful Notebook. We will learn more about it near the end of the course, but if you already feel ready to dive deep, here’s a book: R Markdown: The Definitive Guide, Yihui Xie, J. J. Allaire, Garrett Grolemunds.


Goran S. Milovanović

DataKolektiv, 2020/21

contact:


License: GPLv3 This Notebook is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This Notebook is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this Notebook. If not, see http://www.gnu.org/licenses/.


LS0tDQp0aXRsZTogSW50cm8gdG8gRGF0YSBTY2llbmNlIChOb24tVGVjaG5pY2FsIEJhY2tncm91bmQsIFIpIC0gU2Vzc2lvbjEyDQphdXRob3I6DQotIG5hbWU6IEdvcmFuIFMuIE1pbG92YW5vdmnEhywgUGhEDQogIGFmZmlsaWF0aW9uOiBEYXRhS29sZWt0aXYsIENoaWVmIFNjaWVudGlzdCAmIE93bmVyOyBEYXRhIFNjaWVudGlzdCBmb3IgV2lraWRhdGEsIFdNREUNCmFic3RyYWN0OiANCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICB0aGVtZTogc3BhY2VsYWINCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgdG9jX2RlcHRoOiA1DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDUNCi0tLQ0KDQohW10oLi4vX2ltZy9ES19Mb2dvXzEwMC5wbmcpDQoNCioqKg0KIyBTZXNzaW9uIDEyOiBJbnRyb2R1Y3Rpb24gdG8gRXN0aW1hdGlvbiBUaGVvcnk6IHVuZGVyc3RhbmRpbmcgdGhlIGxvZ2ljIG9mIHN0YXRpc3RpY2FsIG1vZGVsaW5nLiBJbnRyb2R1Y3Rpb24gdG8gY292YXJpYW5jZSwgY29ycmVsYXRpb24sIGFuZCBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24uDQoNCg0KKipGZWVkYmFjayoqIHNob3VsZCBiZSBzZW5kIHRvIGBnb3Jhbi5taWxvdmFub3ZpY0BkYXRha29sZWt0aXYuY29tYC4gDQpUaGVzZSBub3RlYm9va3MgYWNjb21wYW55IHRoZSBJbnRybyB0byBEYXRhIFNjaWVuY2U6IE5vbi1UZWNobmljYWwgQmFja2dyb3VuZCBjb3Vyc2UgMjAyMC8yMS4NCg0KKioqDQoNCiMjIyBXaGF0IGRvIHdlIHdhbnQgdG8gZG8gdG9kYXk/DQoNCkNvcnJlbGF0aW9ucywgY29ycmVsYXRpb25zIGV2ZXJ5d2hlcmUuLiEgT25lIHdvdWxkIHRoaW5rIHRoYXQgYWxsIHRoYXQgRGF0YSBTY2llbnRpc3RzIGRvIG5vd2FkYXlzIGlzIHRvIGxvb2sgZm9yIHRoZW0uIE5vdCBldmVuIGhhbGYgdHJ1ZSwgaG93ZXZlciwgdGhleSBhcmUgb2YgZXNzZW50aWFsIGltcG9ydGFuY2UgZm9yIG91ciB3b3JrLiBJbiB0aGlzIHNlc3Npb24sIHdlIGludHJvZHVjZSB0aGUgY29uY2VwdCBvZiBjb3JyZWxhdGlvbiwgYW5kIGV4cGFuZCBpdCAtIGluIGEgZ2VudGxlIHdheSAtIHRvd2FyZHMgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uLg0KDQoNCiMjIyAwLiBQcmVyZXF1aXNpdHMNCg0KSW5zdGFsbDoNCg0KYGBge3IgZWNobyA9IFQsIGV2YWwgPSBGLCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEZ9DQppbnN0YWxsLnBhY2thZ2VzKCdjb3JycGxvdCcpDQppbnN0YWxsLnBhY2thZ2VzKCdIbWlzYycpDQpkYXRhRGlyIDwtIHBhc3RlMChnZXR3ZCgpLCAiL19kYXRhLyIpDQpgYGANCg0KU2V0dXA6DQoNCmBgYHtyIGVjaG8gPSBULCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEZ9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoSG1pc2MpDQpsaWJyYXJ5KHBwY29yKQ0KbGlicmFyeShjb3JycGxvdCkNCmBgYA0KDQojIyMgMS4gQ292YXJpYW5jZSwgVmFyaWFibGUgU3RhbmRhcmRpemF0aW9uLCBhbmQgQ29ycmVsYXRpb24NCg0KIyMjIyAxLjEgTGluZWFyIHJlbGF0aW9uc2hpcHMNCg0KV2Ugd2lsbCBzdGFydCBieSBpbnNwZWN0aW5nIHR3byB2YXJpYWJsZXMgZnJvbSB0aGUgaXJpcyBkYXRhIHNldDogYFNlcGFsLkxlbmd0aGAgYW5kIGBQZXRhbC5MZW5ndGhgOg0KDQpgYGB7ciBlY2hvID0gVH0NCiMgLSBwbG90IGxheW91dDogMiB4IDINCnBhcihtZmNvbCA9IGMoMiwgMikpDQojIC0gYm94cGxvdCBpcmlzJFNlcGFsLkxlbmd0aA0KYm94cGxvdChpcmlzJFNlcGFsLkxlbmd0aCwNCiAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsIA0KICAgICAgICB4bGFiID0gIlNlcGFsIExlbmd0aCIpDQojIC0gaGlzdG9ncmFtOiBpcmlzJFNlcGFsLkxlbmd0aA0KaGlzdChpcmlzJFNlcGFsLkxlbmd0aCwgDQogICAgIG1haW4gPSAiIiwNCiAgICAgeGxhYiA9ICJTZXBhbC5MZW5ndGgiLCANCiAgICAgcHJvYiA9IFQpDQojIC0gb3ZlcmxheSBpcmlzJFNlcGFsLkxlbmd0aCBkZW5zaXR5IA0KIyAtIGZ1bmN0aW9uIG92ZXIgdGhlIGVtcGlyaWNhbCBkaXN0cmlidXRpb24NCmxpbmVzKGRlbnNpdHkoaXJpcyRTZXBhbC5MZW5ndGgpLA0KICAgICAgbHR5ID0gImRhc2hlZCIsIA0KICAgICAgbHdkID0gMi41LCANCiAgICAgIGNvbCA9ICJyZWQiKQ0KIyAtIGJveHBsb3QgaXJpcyRQZXRhbC5MZW5ndGgNCmJveHBsb3QoaXJpcyRQZXRhbC5MZW5ndGgsDQogICAgICAgIGhvcml6b250YWwgPSBUUlVFLCANCiAgICAgICAgeGxhYiA9ICJQZXRhbCBMZW5ndGgiKQ0KIyAtIGhpc3RvZ3JhbTogaXJpcyRQZXRhbC5MZW5ndGgsDQpoaXN0KGlyaXMkUGV0YWwuTGVuZ3RoLA0KICAgICBtYWluID0gIiIsDQogICAgIHhsYWIgPSAiUGV0YWwgTGVuZ3RoIiwgDQogICAgIHByb2IgPSBUKQ0KIyAtIG92ZXJsYXkgaXJpcyRQZXRhbC5MZW5ndGggZGVuc2l0eSANCiMgLSBmdW5jdGlvbiBvdmVyIHRoZSBlbXBpcmljYWwgZGlzdHJpYnV0aW9uDQpsaW5lcyhkZW5zaXR5KGlyaXMkUGV0YWwuTGVuZ3RoKSwNCiAgICAgIGx0eSA9ICJkYXNoZWQiLCANCiAgICAgIGx3ZCA9IDIuNSwgDQogICAgICBjb2wgPSAicmVkIikNCiMgcmVzZXQgcGxvdCBsYXlvdXQNCnBhcihtZmNvbCA9IGMoMSwgMSkpDQpgYGANCg0KKipRLioqIElzIHRoZXJlIGEgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZXNlIHR3byB2YXJpYWJsZXM/IExldOKAmXMgc2VlOg0KDQpgYGB7ciBlY2hvID0gVH0NCiMgc2NhdHRlciBwbG90IHcuIHtiYXNlfQ0KcGxvdChpcmlzJFNlcGFsLkxlbmd0aCwgaXJpcyRQZXRhbC5MZW5ndGgsDQogICAgIG1haW4gPSAiU2VwYWwgTGVuZ3RoIHZzIFBldGFsIExlbmd0aCIsDQogICAgIHhsYWIgPSAiU2VwYWwgTGVuZ3RoIiwgeWxhYiA9ICJQZXRhbCBMZW5ndGgiLA0KICAgICBjZXgubWFpbiA9IC44NSwNCiAgICAgY2V4LmxhYiA9IC43NSkNCmBgYA0KT25lIGNvdWxkIHRoaW5rIHRoZXJlIGlzIHNvbWV0aGluZyBnb2luZyBvbiBoZXJlLiBCdXQ6DQoNCmBgYHtyIGVjaG8gPSBULCBtZXNzYWdlID0gRn0NCmdncGxvdChkYXRhID0gaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsDQogICAgICAgICAgICAgICAgICAgICAgICB5ID0gUGV0YWwuTGVuZ3RoLA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBTcGVjaWVzKQ0KICAgICAgICkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRiwgc2l6ZSA9IC4yNSkgKw0KICB0aGVtZV9idygpICsgDQogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQpUaGVyZSBzZWVtcyB0byBiZSBtb3JlIHRoYW4gb25lIGltcG9ydGFudCBsaW5lIHRvIGRlc2NyaWJlIHRoaXMgZGF0YSBzZXQuLi4gSG93ZXZlciwgd2Ugd2lsbCBzaW1wbGlmeSBmb3Igbm93Og0KDQpgYGB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEZ9DQpnZ3Bsb3QoZGF0YSA9IGlyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLA0KICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFBldGFsLkxlbmd0aCkpICsgDQogIGdlb21fcG9pbnQoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMikgKyANCiAgZ2VvbV9wb2ludChjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAxLjUpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0sIHNlID0gRiwgc2l6ZSA9IC4yNSwgY29sb3IgPSAicmVkIikgKw0KICB0aGVtZV9idygpICsgDQogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQojIyMjIDEuMiBDb3ZhcmlhbmNlIGFuZCBTdGFuZGFyZGl6YXRpb24NCg0KTGVhdmluZyBhc2lkZSB0aGUgaW1wb3J0YW50IHF1ZXN0aW9uIG9mIHdoZXRoZXIgdGhlcmUgaXMgYSBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gYFNlcGFsLkxlbmd0aGAgYW5kIGBQZXRhbC5MZW5ndGhgIGluIGBpcmlzYCBmb3Igbm93LCB3ZSBhc2s6ICoqaWYgaXQgd2FzIGEgbGluZWFyIHJlbGF0aW9uc2hpcCoqLCBob3cgZ29vZCBhIGxpbmVhciByZWxhdGlvbnNoaXAgd291bGQgaXQgbWFrZT8gVGhlIGFuc3dlciBpcyBwcm92aWRlZCBieSBjb21wdXRpbmcgdGhlIFBlYXJzb27igJlzIGNvZWZmaWNpZW50IG9mIGxpbmVhciBjb3JyZWxhdGlvbi4NCg0KRmlyc3QgdGhpbmdzIGZpcnN0LiBXaGF0IGlzIHRoaXM6DQoNCmBgYHtyIGVjaG8gPSBUfQ0KY292KGlyaXMkU2VwYWwuTGVuZ3RoLCBpcmlzJFBldGFsLkxlbmd0aCkNCmBgYA0KDQoqKkNvdmFyaWFuY2UqKi4gR2l2ZW4gdHdvIHJhbmRvbSB2YXJpYWJsZXMgKFJWcyksICRYJCBhbmQgJFkkLCB0aGVpciAoc2FtcGxlKSBjb3ZhcmlhbmNlIGlzIGdpdmVuIGJ5Og0KDQokJGNvdihYLFkpID0gRVsoWC1FW1hdKShZLUVbWV0pXSA9IFxmcmFjeyhYLVxiYXJ7WH0pKFktXGJhcntZfSl9e04tMX0kJA0Kd2hlcmUgJEVbXSQgZGVub3RlcyB0aGUgKmV4cGVjdGF0aW9uKiAodGhlICptZWFuKiwgaWYgeW91IHByZWZlciksICRcYmFye1h9JCBpcyB0aGUgbWVhbiBvZiAkWCQsICRcYmFye1l9JCBpcyB0aGUgbWVhbiBvZiAkWSQsIGFuZCAkTiQgaXMgdGhlIHNhbXBsZSBzaXplLg0KDQojIyMjIDEuMyBDb3JyZWxhdGlvbg0KDQpQZWFyc29uJ3MgY29lZmZpY2llbnQgb2YgY29ycmVsYXRpb24gaXMgbm90aGluZyBlbHNlIHRoYW4gYSBjb3ZhcmlhbmNlIGJldHdlZW4gJFgkIGFuZCAkWSQgdXBvbiB0aGVpciAqc3RhbmRhcmRpemF0aW9uKi4gVGhlIHN0YW5kYXJkaXphdGlvbiBvZiBhIFJWIC0gd2lkZWx5IGtub3duIGFzIGEgdmFyaWFibGUgKnotc2NvcmUqIC0gaXMgb2J0YWluZWQgdXBvbiBzdWJ0cmFjdGluZyBhbGwgb2YgaXRzIHZhbHVlcyBmcm9tIHRoZSBtZWFuLCBhbmQgZGl2aWRpbmcgYnkgdGhlIHN0YW5kYXJkIGRldmlhdGlvbjsgZm9yIHRoZSAqKmkqKi10aCBvYnNlcnZhdGlvbiBvZiAkWCQ6DQoNCiQkeih4X2kpID0gXGZyYWN7eF9pLVxiYXJ7WH19e1xzaWdtYX0kJA0KVGh1cywNCg0KYGBgIHtyIGVjaG8gPSBUfQ0KelNlcGFsTGVuZ3RoIDwtIChpcmlzJFNlcGFsLkxlbmd0aCAtIG1lYW4oaXJpcyRTZXBhbC5MZW5ndGgpKS9zZChpcmlzJFNlcGFsLkxlbmd0aCkNCnpQZXRhbExlbmd0aCA8LSAoaXJpcyRQZXRhbC5MZW5ndGgtbWVhbihpcmlzJFBldGFsLkxlbmd0aCkpL3NkKGlyaXMkUGV0YWwuTGVuZ3RoKQ0KY292KHpTZXBhbExlbmd0aCwgelBldGFsTGVuZ3RoKQ0KYGBgDQoNCmlzIHRoZSBjb3JyZWxhdGlvbiBvZiBgU2VwYWwuTGVuZ3RoYCBhbmQgYFBldGFsLkxlbmd0aGA7IGxldCdzIGNoZWNrIHdpdGgge2Jhc2V9IFIgZnVuY3Rpb24gYGNvcigpYCB3aGljaCBjb21wdXRlcyB0aGUgY29ycmVsYXRpb246DQoNCmBgYCB7ciBlY2hvID0gVH0NCmNvcihpcmlzJFNlcGFsLkxlbmd0aCwgaXJpcyRQZXRhbC5MZW5ndGgsIG1ldGhvZCA9ICJwZWFyc29uIikNCmBgYA0KDQpSaWdodC4gVGhlcmUgYXJlIG1hbnkgZm9ybXVsYXMgdGhhdCBjb21wdXRlIGByYCwgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50OyBob3dldmVyLCB1bmRlcnN0YW5kaW5nIHRoYXQgaXMgc2ltcGx5IHRoZSBjb3ZhcmlhbmNlIG9mIHN0YW5kYXJkaXplZCBSVnMgaXMgZXNzZW50aWFsLiBPbmNlIHlvdSBrbm93IHRvIHN0YW5kYXJkaXplIHRoZSB2YXJpYWJsZXMgYW5kIGhvdyB0byBjb21wdXRlIGNvdmFyaWFuY2UgKGFuZCB0aGF0IGlzIGVhc3kpLCB5b3UgZG9uJ3QgbmVlZCB0byBjYXJlIGFib3V0IGV4cHJlc3Npb25zIGxpa2U6DQoNCiQkcl97WFl9ID0gXGZyYWN7TlxzdW17WFl9LShcc3Vte1h9KShcc3Vte1l9KX17XHNxcnR7W05cc3Vte1heMn0tKFxzdW17WH0pXjJdW05cc3Vte1leMn0tKFxzdW17WX0pXjJdfX0kJA0KDQpUaGlzIGFuZCBzaW1pbGFyIGV4cHJlc3Npb25zIGFyZSBnb29kLCBhbmQgZXNwZWNpYWxseSBmb3IgdHdvIHB1cnBvc2VzOiBmaXJzdCwgdGhleSB3aWxsIGNvbXB1dGUgdGhlIGRlc2lyZWQgdmFsdWUgb2YgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGluIHRoZSBlbmQsIHRoYXQncyBmb3Igc3VyZSwgYW5kIHNlY29uZCwgd3JpdGluZyB0aGVtIHVwIGluIGBSTWFya2Rvd25gIHJlYWxseSBoZWxwcyBtYXN0ZXJpbmcgJFxMYVRlWCQuIEJlc2lkZXMgdGhlc2Ugcm9sZXMgdGhleSBwbGF5LCB0aGVyZSBpcyByZWFsbHkgbm90aGluZyBlc3NlbnRpYWxseSBpbXBvcnRhbnQgaW4gcmVsYXRpb24gdG8gdGhlbS4NCg0KU29tZXdoYXQgZWFzaWVyIHRvIHJlbWVtYmVyOg0KDQokJHJfe1hZfSA9IFxmcmFje2NvdihYLFkpfXtcc2lnbWEoWClcc2lnbWEoWSl9JCQNCi0gdGhlIGNvdmFyaWFuY2Ugb2YgJFgkIGFuZCAkWSQsIGRpdmlkZWQgYnkgdGhlIHByb2R1Y3Qgb2YgdGhlaXIgc3RhbmRhcmQgZGV2aWF0aW9ucy4NCg0KVGhlcmUncyBhIG5pY2UgYHNjYWxlKClgIGZ1bmN0aW9uIHRoYXQgd2lsbCBxdWlja2VuLXVwIHRoZSBjb21wdXRhdGlvbiBvZiAqei1zY29yZXMqIGluIFIgZm9yIHlvdToNCg0KYGBgIHtyIGVjaG8gPSBUfQ0KelNlcGFsTGVuZ3RoMSA8LSAgc2NhbGUoaXJpcyRTZXBhbC5MZW5ndGgsIGNlbnRlciA9IFQsIHNjYWxlID0gVCkNCnN1bSh6U2VwYWxMZW5ndGgxID09IHpTZXBhbExlbmd0aCkgPT0gbGVuZ3RoKHpTZXBhbExlbmd0aCkNCmBgYA0KDQpEbyBgP3NjYWxlYCAtIHVzZWZ1bCB0aGluZ3MgY2FuIGJlIGRvbmUgd2l0aCBpdC4NCg0KIyMjIDIuIENvcnJlbGF0aW9uIE1hdHJpY2VzOiBWaXN1YWxpemF0aW9uIGFuZCBUcmVhdG1lbnQgb2YgTWlzc2luZyBWYWx1ZXMNCg0KVGhlIHtiYXNlfSBgY29yKClgIGZ1bmN0aW9uIHByb2R1Y2VzIGNvcnJlbGF0aW9uIG1hdHJpY2VzIHRvbzoNCg0KYGBgIHtyIGVjaG8gPSBUfQ0KY29yKGlyaXNbICwgYygxOjQpXSkNCmBgYA0KDQpNaXNzaW5nIGRhdGEgY2FuIGJlIHRyZWF0ZWQgYnkgKmxpc3R3aXNlKiBvciAqcGFpcndpc2UqIGRlbGV0aW9uLiBJbiAqbGlzdHdpc2UqIGRlbGV0aW9uLCBhbnkgb2JzZXJ2YXRpb24gKD09IHJvdykgY29udGFpbmluZyBhdCBsZWFzdCBvbmUgYE5BYChzKSB3aWxsIGJlIHJlbW92ZWQgYmVmb3JlIHRoZSBjb21wdXRhdGlvbi4gU2V0IHRoZSBgdXNlYCBhcmd1bWVudCBpbiBgY29yYCB0byBgY29tcGxldGUub2JzYCBpbiBvcmRlciB0byB1c2UgbGlzdHdpc2UgZGVsZXRpb246DQoNCmBgYCB7ciBlY2hvID0gVH0NCmRTZXQgPC0gaXJpcw0KIyBSZW1vdmUgb25lIG5vbWluYWwgdmFyaWFibGUgLSBTcGVjaWVzDQpkU2V0JFNwZWNpZXMgPC0gTlVMTA0KIyBpbnRyb2R1Y2UgTkEgaW4gZFNldCRTZXBhbC5MZW5ndGhbNV0NCmRTZXQkU2VwYWwuTGVuZ3RoWzVdIDwtIE5BDQojIFBhaXJ3aXNlIGFuZCBMaXN0d2lzZSBEZWxldGlvbjoNCmNvcjFhIDwtIGNvcihkU2V0LCB1c2U9ImNvbXBsZXRlLm9icyIpICMgbGlzdHdpc2UgZGVsZXRpb24NCmNvcjFhDQpgYGANCg0KKlBhaXJ3aXNlIGRlbGV0aW9uKiB3aWxsIGNvbXB1dGUgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IHVzaW5nIGFsbCBhdmFpbGFibGUgZGF0YS4gSXQgd2lsbCBkZWxldGUgb25seSB0aGUgZGF0YSBjb3JyZXNwb25kaW5nIHRvIHRoZSBtaXNzaW5nIHZhbHVlcyBmcm9tIG9uZSB2ZWN0b3IgaW4gYW5vdGhlciwgYW5kIGNvbXB1dGUgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGZyb20gd2hhdCBpcyBsZWZ0OyBzZXQgYHVzZWAgdG8gYHBhaXJ3aXNlLmNvbXBsZXRlLm9ic2AgdG8gdXNlIHRoaXMgYXBwcm9hY2g6DQoNCmBgYCB7ciBlY2hvID0gVH0NCmNvcjFiIDwtIGNvcihkU2V0LCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikgIyBwYWlyd2lzZSBkZWxldGlvbg0KY29yMWINCmBgYA0KDQpgdXNlID0gImFsbC5vYnMiYCB3aWxsIHByb2R1Y2UgYW4gZXJyb3IgaW4gdGhlIHByZXNlbmNlIG9mIGFueSBgTkFgczoNCg0KYGBgIHtyIGVjaG8gPSBULCBlcnJvciA9IFR9DQpjb3IxYyA8LSBjb3IoZFNldCwgdXNlID0gImFsbC5vYnMiKSAjIGFsbCBvYnNlcnZhdGlvbnMgLSBlcnJvcg0KY29yMWMNCmBgYA0KDQpUbyBwcm9wYWdhdGUgYE5BYHMgdGhyb3VnaCB0aGUgbWF0cml4IHdoZXJldmVyIHRoZXkgYXJlIHByZXNlbnQgaW4gdGhlIHJlc3BlY3RpdmUgY29sdW1ucywgYHVzZSA9ICJldmVyeXRoaW5nImAgKHRoaXMgaXMgdGhlICpkZWZhdWx0KjsgdHJ5IGBjb3IoZFNldClgKToNCg0KYGBgIHtyIGVjaG8gPSBUfQ0KY29yMWQgPC0gY29yKGRTZXQsIHVzZSA9ICJldmVyeXRoaW5nIikNCmNvcjFkDQpgYGANCg0KVGhlcmUgYXJlIG1hbnkgYXZhaWxhYmxlIG1ldGhvZHMgdG8gdmlzdWFsaXplIGNvcnJlbGF0aW9uIG1hdHJpY2VzIGluIFIuIFRoZSB7YmFzZX0gYXBwcm9hY2ggd291bGQgYmUgdG8gdXNlIGBwbG90KClgIG9uIGEgYGRhdGEuZnJhbWVgIGxpa2UgaW4gdGhlIGZvbGxvd2luZyBleGFtcGxlOg0KDQpgYGAge3IgZWNobyA9IFR9DQojIHtiYXNlfSBhcHByb2FjaA0KZGF0YSgibXRjYXJzIikNCmNvck1hdHJpeCA8LSBjb3IobXRjYXJzWywgMTo4XSkNCnBsb3QoYXMuZGF0YS5mcmFtZShjb3JNYXRyaXgpKQ0KYGBgDQpCdXQgdGhlcmUncyBhbHNvIHRoZSBmYW50YXN0aWMgYHtjb3JycGxvdH1gIHBhY2thZ2UgdG8gdmlzdWFsaXplIGNvcnJlbGF0aW9uIG1hdHJpY2VzOg0KDQpgYGAge3IgZWNobyA9IFR9DQojIHtjb3JycGxvdH0gYXBwcm9hY2gNCmNvck1hdHJpeCA8LSBjb3IobXRjYXJzKQ0KIyB7Y29ycnBsb3R9ICJjaXJjbGUiIG1ldGhvZDogDQpjb3JycGxvdChjb3JNYXRyaXgsIA0KICAgICAgICAgbWV0aG9kID0gImNpcmNsZSIpDQpgYGANCg0KYGBgIHtyIGVjaG8gPSBUfQ0KIyB7Y29ycnBsb3R9ICJlbGxpcHNlIiBtZXRob2Q6IA0KY29ycnBsb3QoY29yTWF0cml4LCANCiAgICAgICAgIG1ldGhvZCA9ICJlbGxpcHNlIikNCmBgYA0KDQpgYGAge3IgZWNobyA9IFR9DQojICJtaXhlZCINCmNvcnJwbG90Lm1peGVkKGNvck1hdHJpeCwgDQogICAgICAgICAgICAgICBsb3dlcj0iZWxsaXBzZSIsIA0KICAgICAgICAgICAgICAgdXBwZXI9ImNpcmNsZSIpDQpgYGANCg0KIyMjIDMuIEludHJvZHVjdGlvbiB0byBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24NCg0KV2Ugbm93IGJlZ2luIGNvbnNpZGVyaW5nIHRoZSBtYXRoZW1hdGljYWwgbW9kZWxpbmcgb2YgZGF0YSBpbiBSLiBUaGUgZmlyc3QgLSBhbmQgYXJndWFibHkgdGhlIHNpbXBsZXN0IC0gc3RhdGlzdGljYWwgbW9kZWwgdGhhdCB3ZSB3aWxsIGZhY2UgaXMgdGhlICpTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWwqLiBJbiBhIHR5cGljYWwgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIHNldHRpbmcsIHdlIGhhdmUgb25lIGNvbnRpbnVvdXMgKnByZWRpY3RvciogLSAgYWxzbyBrbm93biBhcyB0aGUgKmluZGVwZW5kZW50IHZhcmlhYmxlKiAtIGFuZCBvbmUgY29udGludW91cyAqY3JpdGVyaW9uKiAtIGEuay5hLiB0aGUgKmRlcGVuZGVudCB2YXJpYWJsZSouIEJvdGggdGhlc2UgYXJlIGFzc3VtZWQgdG8gYmUgdW5ib3VuZGVkLCBpLmUuIHRha2luZyB2YWx1ZXMgYWNyb3NzIHRoZSB3aG9sZSBkb21haW4gb2YgcmVhbCBudW1iZXJzLiAqQ29udGludWl0eSogaGVyZSBzaG91bGQgYmUgdW5kZXJzdG9vZCBwcmVjaXNlbHkgYXMgaGF2aW5nIG1lYXN1cmVtZW50cyBmcm9tIGFuICppbnRlcnZhbCogb3IgKnJhdGlvIHNjYWxlKi4NCg0KTGluZWFyIHJlZ3Jlc3Npb24gKmRvZXMgbm90IGltcGx5IGFueSBjYXVzYWxpdHkqOyBpdCBpcyB1cCB0byB0aGUgdXNlciBvZiB0aGUgbW9kZWwgdG8gaW1wb3NlIGNhdXNhbCBhc3N1bXB0aW9ucywgaS5lLiB3aGljaCB2YXJpYWJsZSB0YWtlcyB0aGUgcm9sZSBvZiB0aGUgY3JpdGVyaW9uIGFuZCB3aGljaCB2YXJpYWJsZSBpcyBhc3NpZ25lZCBhcyBhIHByZWRpY3Rvci4gSXQgaXMgbm90IGV2ZW4gbmVjZXNzYXJ5IHRvIGltcG9zZSBhbnkgc3VjaCBhc3N1bXB0aW9ucyBpbiBvcmRlciB0byBvYnRhaW4gYSB2YWxpZCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCwgYWx0aG91Z2ggaXQgaXMgdmVyeSBjdXN0b21hcnkgdG8gaGF2ZSBzb21lIGh5cG90aGVzaXplZCBkaXJlY3Rpb24gb2YgY2F1c2FsaXR5IGluIG9yZGVyIHRvIGRpc2N1c3MgcHJlZGljdGlvbiBtZWFuaW5nZnVsbHkuDQoNCiMjIyMgMy4xIExpbmVhciBDb3JyZWxhdGlvbiwgQXNzdW1wdGlvbiBvZiBMaW5lYXJpdHksIGFuZCBDYXVzYWxpdHkNCg0KYGBgIHtyIGVjaG8gPSBUfQ0KIyMgUGVhcnNvbiBjb3JyZWxhdGlvbiBpbiBSIHtiYXNlfQ0KY29yMSA8LSBjb3IoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkUGV0YWwuTGVuZ3RoLCANCiAgICAgICAgICAgIG1ldGhvZCA9ICJwZWFyc29uIikNCmNvcjENCmBgYA0KDQpXaXRoICRSJCA9IC44NyB3ZSBob3BlIHRvIGJlIGFibGUgdG8gc2F5IHRoYXQgdGhlcmUgaXMgYSBsaW5lYXIgcmVsYXRpb25zaGlwLCByaWdodD8gVGltZSB0byBsZWFybiBzb21ldGhpbmcgaW1wb3J0YW50IGFib3V0IHN0YXRpc3RpY3M6IHlvdSBjYW4gbmV2ZXIgcmVseSBvbiBhIGNvbmNsdXNpb24gdGhhdCB3YXMgcmVhY2hlZCBieSB0YWtpbmcgdGhlIHZhbHVlcyBvZiB0aGUgc3RhdGlzdGljcyAqcHJpbWEgZmFjaWUqIHdoaWxlIGRvaW5nIG5vdGhpbmcgZWxzZS4gVGFrZSBhIGxvb2sgYXQgdGhlIHNjYXR0ZXIgcGxvdCBvZiB0aGVzZSB0d28gdmFyaWFibGVzIGFnYWluOg0KDQpgYGAge3IgZWNobyA9IFQsIG1lc3NhZ2UgPSBGfQ0KIyBMZXQncyB0ZXN0IHRoZSBhc3N1bXB0aW9uIG9mIGxpbmVhcml0eToNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFBldGFsLkxlbmd0aCkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMiwgY29sb3IgPSAnYmxhY2snKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAnd2hpdGUnKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScsIHNpemUgPSAuMjUsIGNvbG9yID0gJ3JlZCcsIHNlID0gRikgKw0KICBnZ3RpdGxlKCdTZXBhbCBMZW5ndGggdnMgUGV0YWwgTGVuZ3RoJykgKw0KICB0aGVtZV9idygpICsgDQogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQpXZSBoYXZlIGluY2x1ZGVkIHRoZSBiZXN0IGZpdHRpbmcgcmVncmVzc2lvbiBsaW5lIGluIHRoZSBzY2F0dGVyIHBsb3Q7IGRvZXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGUgcmVhbGx5IGxvb2tzICpsaW5lYXIqPyBMZXQncyByZW1pbmQgb3Vyc2VsdmVzIG9mIHdoYXQgd2UgaGF2ZSBhbHJlYWR5IGRpc2NvdmVyZWQ6DQoNCmBgYCB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEZ9DQpnZ3Bsb3QoZGF0YSA9IGlyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLA0KICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFBldGFsLkxlbmd0aCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3BlY2llcykNCiAgICAgICApICsgDQogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLCBzaXplID0gLjI1LCBzZSA9IEYpICsgDQogIGdndGl0bGUoJ1NlcGFsIExlbmd0aCB2cyBQZXRhbCBMZW5ndGgnKSArIA0KICB0aGVtZV9idygpICsgDQogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQpJbnRlcmVzdGluZzogdGhlcmUgc2VlbSB0byBiZSAqbW9yZSB0aGFuIG9uZSogbGluZWFyIHJlbGF0aW9uc2hpcCBpbiB0aGlzIHNjYXR0ZXIgcGxvdCwgaS5lLiBvbmUgcGVyIGVhY2ggZ3JvdXAgZnJvbSB0aGUgYGlyaXNgIGRhdGEgc2V0LiBXaGF0IGRvIHdlIGRvLCBleGNlcHQgZm9yIGNvbmNsdWRpbmcgdGhhdCB0aGUgKmFzc3VtcHRpb24gb2YgbGluZWFyaXR5KiBoYXMgZmFpbGVkPyBXZSB3aWxsIGludHJvZHVjZSBhIGZpeCBpbiBvbmUgb2Ygb3VyIG5leHQgc2Vzc2lvbnMsIHNob3dpbmcgaG93IGEgbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbCBjYW4gYWNjb3VudCBmb3Igc2l0dWF0aW9ucyBsaWtlIHRoZSBwcmVzZW50IG9uZTsgaW4gdGhlIG1lYW50aW1lLCBwcmV0ZW5kIGxpa2Ugbm90aGluZyBoYXMgaGFwcGVuZWQuLg0KDQpCeSB0aGUgd2F5LCBpcyB0aGUgJFIkIGNvZWZmaWNpZW50IG9mIGxpbmVhciBjb3JyZWxhdGlvbiBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50Pw0KDQpgYGAge3IgZWNobyA9IFR9DQojIElzIFBlYXJzb24ncyBwcm9kdWN0LW1vbWVudCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBzaWduaWZpY2FudD8NCmNvcjIgPC0gcmNvcnIoaXJpcyRTZXBhbC5MZW5ndGgsICMge2htaXNjfQ0KICAgICAgICAgICAgICBpcmlzJFBldGFsLkxlbmd0aCwgDQogICAgICAgICAgICAgIHR5cGU9InBlYXJzb24iKQ0KIyBjb3JyZWxhdGlvbnMNCmNvcjIkcg0KYGBgDQoNCmBgYCB7ciBlY2hvID0gVH0NCmNvcjIkclsxLCAyXSAjIE9rLCB0aGUgb25lIHdlJ3JlIGxvb2tpbmcgZm9yDQpgYGANCg0KYGBgIHtyIGVjaG8gPSBUfQ0KY29yMiRQWzEsIDJdICMgc2lnbmlmaWNhbnQgYXQNCmBgYA0KV2VsbCwgJFIkIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgaW5kZWVkLiBNb3N0IHNvY2lhbCBzY2llbmNlIHN0dWRlbnRzIHdvdWxkIHR5cGljYWxseSBjb25jbHVkZSB0aGF0IGV2ZXJ5dGhpbmcncyBzdXBlcmZpbmUgaGVyZS4uLiBEb24ndCBiZSBsYXp5OiAoYSkgZG8gdGhlIEVEQSBvZiB5b3VyIHZhcmlhYmxlcyBiZWZvcmUgbW9kZWxpbmcsIChiKSBpbnNwZWN0IHRoZSBzY2F0dGVyIHBsb3QgaW4gKnNtYXJ0IHdheXMqIC0gaWYgdGhlcmUgYXJlIG5hdHVyYWwgZ3JvdXBpbmdzIGV4cGVjdGVkIGluIHRoZSBkYXRhIHNldCwgdXNlIGNvbG9ycyBvciBzaGFwZXMgdG8gbWFyayB0aGVtLiBJbiBzcGl0ZSBvZiB0aGUgaGlnaCwgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBQZWFyc29uJ3MgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYmV0d2VlbiBgU2VwYWwuTGVuZ3RoYCBhbmQgYFBldGFsLkxlbmd0aGAsIHRoaXMgcmVsYXRpb25zaGlwIHZpb2xhdGVzIGxpbmVhcml0eSwgYW5kIGEgbW9kZWwgbW9yZSBwb3dlcmZ1bCB0aGFuIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBpcyBuZWVkZWQuDQoNCkhvd2V2ZXIsIGxldCdzIHByZXRlbmQgd2UndmUgbmV2ZXIgc2VlbiB0aGlzIGFuZCBzdGFydCBkb2luZyBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gaW4gUi4NCg0KIyMjIyAzLjIgU2ltcGxlIExpbmVhciBSZWdyZXNzaW9uOiBUaGUgTW9kZWwNCg0KV2Ugd2lsbCBub3cgY29uc2lkZXIgdGhlIGZvbGxvd2luZyBzdGF0aXN0aWNhbCBtb2RlbCBvZiBhIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gcmFuZG9tIHZhcmlhYmxlczoNCg0KJCRZID0gXGJldGFfMCArIFxiZXRhXzFYXzEgKyBcZXBzaWxvbiAkJA0KDQotICRZJCBpcyB0aGUgdmFyaWFibGUgd2hvc2UgdmFsdWVzIHdlIHdvdWxkIGxpa2UgdG8gYmUgYWJsZSB0byBwcmVkaWN0IC0gYW5kIGl0IGlzIGNhbGxlZCBhICpjcml0ZXJpb24qIG9yIGEgKmRlcGVuZGVudCB2YXJpYWJsZSogLSBmcm9tDQotICRYJCwgd2hpY2ggaXMgY2FsbGVkIGEgKnByZWRpY3RvciosIG9yIGFuICppbmRlcGVuZGVudCB2YXJpYWJsZSogaW4gdGhlIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbiBzZXR0aW5nOw0KLSAkXGJldGFfMCQgYW5kICRcYmV0YV8xJCBhcmUgKm1vZGVsIHBhcmFtZXRlcnMqLCBvZiB3aGljaCB0aGUgZm9ybWVyIHJlcHJlc2VudHMgdGhlICppbnRlcmNlcHQqIHdoaWxlIHRoZSBsYXRlciBpcyB0aGUgKnNsb3BlKiBvZiB0aGUgcmVncmVzc2lvbiBsaW5lICgqKm5vdGU6KiogYmVzaWRlcyAkXGVwc2lsb24kLCB3aGF0IHRoZSBlcXVhdGlvbiByZXByZXNlbnRzIGlzIG5vdGhpbmcgZWxzZSBidXQgdGhlIGVxdWF0aW9uIG9mIGEgc3RyYWlnaHQgbGluZSBpbiBhIHBsYW5lIHRoYXQgeW91IGhhdmUgc2VlbiBhIGRvemVuIHRpbWVzIGluIGhpZ2ggc2Nob29sKTsgZmluYWxseSwNCi0gJFxlcHNpbG9uJCByZXByZXNlbnRzIHRoZSBtb2RlbCBlcnJvciB0ZXJtLCB3aGljaCB3ZSB3aWxsIGRpc2N1c3MgaW4gbGVuZ3RoIGluIG91ciBTZXNzaW9uLg0KDQpJZiB3ZSBhc3N1bWUgdGhhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gJFgkIGFuZCAkWSQgaXMgaW5kZWVkIGxpbmVhciAtIGFuZCBpbnRyb2R1Y2Ugc29tZSBhZGRpdGlvbmFsIGFzc3VtcHRpb25zIHRoYXQgd2Ugd2lsbCBkaXNjdXNzIGluIG91ciBuZXh0IHNlc3Npb3NuIC0gdGhlIGZvbGxvd2luZyBxdWVzdGlvbiByZW1haW5zOg0KDQo+IFdoYXQgdmFsdWVzIG9mICRcYmV0YV8wJCBhbmQgJFxiZXRhXzEkIHdvdWxkIHBpY2sgYSBsaW5lIGluIGEgcGxhbmUgc3Bhd25lZCBieSAkWCQgYW5kICRZJCB2YWx1ZXMgc28gdGhhdCBpdCBkZXNjcmliZXMgdGhlIGFzc3VtZWQgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZW0gdGhlIGJlc3Q/DQoNCkFnYWluOg0KDQpgYGAge3IgZWNobyA9IFQsIG1lc3NhZ2UgPSBGfQ0KIyBMZXQncyB0ZXN0IHRoZSBhc3N1bXB0aW9uIG9mIGxpbmVhcml0eToNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFBldGFsLkxlbmd0aCkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMiwgY29sb3IgPSAnYmxhY2snKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAnd2hpdGUnKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScsIHNpemUgPSAuMjUsIGNvbG9yID0gJ3JlZCcsIHNlID0gRikgKw0KICBnZ3RpdGxlKCdTZXBhbCBMZW5ndGggdnMgUGV0YWwgTGVuZ3RoJykgKw0KICB0aGVtZV9idygpICsgDQogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQpUaGUgbGluZSBpbiB0aGlzIGB7Z2dwbG90Mn1gIHNjYXR0ZXIgcGxvdCBpcyB0aGUgYmVzdCBmaXR0aW5nIGxpbmUgZm9yIHRoZSBhc3N1bWVkIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBgaXJpcyRTZXBhbC5MZW5ndGhgICh0YWtlbiB0byBiZSBhIHByZWRpY3RvciwgWC1heGlzKSBhbmQgYGlyaXMkUGV0YWwuTGVuZ3RoYCAodGFrZW4gdG8gYmUgYSBjcml0ZXJpb24sIFktYXhpcykuIHtnZ3Bsb3QyfSBjb21wdXRlZCB0aGUgYmVzdCBmaXR0aW5nIGxpbmUgZm9yIHVzOiAqKmhvdz8qKiBXZWxsLCBpbiB0aGUgZW5kLCBpdCBkaWQgaXQgYnkgc2VsZWN0aW5nIHRoZSAqb3B0aW1hbCogdmFsdWVzIGZvciAkXGJldGFfMCQgYW5kICRcYmV0YV8xJC4gSXQgaXMgb3VyIHRhc2sgaW4gdGhpcyBhbmQgdGhlIGZvbGxvd2luZyBzZXNzaW9ucyB0byBmaWd1cmUgb3V0IGhvdyBkb2VzIHRoYXQgc2VsZWN0aW9uIG9mIG9wdGltYWwgcGFyYW1ldGVyIHZhbHVlcyB0YWtlcyBwbGFjZS4gDQoNCiMjIyMgMy4zIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbiB3LiBgbG0oKWAgaW4gUg0KDQpJbiBSIHdlIGhhdmUgdGhlIGBsbSgpYCBmdW5jdGlvbiAtIHNob3J0IGZvciAqbGluZWFyIG1vZGVsKiAtIHRvIGZpdCBhbGwgZGlmZmVyZW50IGtpbmRzIG9mIG1vZGVscyBpbiB0aGUgc2NvcGUgb2YgdGhpcyBtb2RlbCBmcmFtZXdvcmsgdG8gdGhlIGRhdGE6DQoNCmBgYCB7ciBlY2hvID0gVH0NCiMjIyAtLS0gTGluZWFyIFJlZ3Jlc3Npb24gd2l0aCBsbSgpDQojIFByZWRpY3Rpbmc6IFBldGFsIExlbmd0aCBmcm9tIFNlcGFsIExlbmd0aA0KcmVnIDwtIGxtKFBldGFsLkxlbmd0aCB+IFNlcGFsLkxlbmd0aCwgDQogICAgICAgICAgZGF0YT1pcmlzKSANCmNsYXNzKHJlZykNCmBgYA0KDQpUaGUgYFBldGFsLkxlbmd0aCB+IFNlcGFsLkxlbmd0aGAgaXMgY2FsbGVkIGEgKmZvcm11bGEqLCBhbmQgeW91IHNob3VsZCBsZWFybiBtb3JlIGFib3V0IGhvdyBmb3JtdWxhcyBpbiBSIGFyZSBzeW50YWN0aWNhbGx5IGNvbXBvc2VkLiBUaGUgc2ltcGxlc3QgcG9zc2libGUgZm9ybXVsYSwgbGlrZSB0aGlzIG9uZSwgc2ltcGx5IGluZm9ybXMgUiB0aGF0IHdlIHdpc2ggdG8gbW9kZWwgYFBldGFsLkxlbmd0aGAgLSBzdGFuZGluZyB0byB0aGUgbGVmdCBvZiBgfmAgLSBieSB0YWtpbmcgb25seSBgU2VwYWwuTGVuZ3RoYCAtIHN0YW5kaW5nIHRvIHRoZSByaWdodCAtIGFzIGEgcHJlZGljdG9yLiBGb3IgdGhvc2Ugd2hvIGFyZSBhbHJlYWR5IGZhbWlsaWFyIHdpdGggYSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBzZXR0aW5nLCBkb2luZyBgQSB+IEIgKyBDYCBtZWFucyBjYWxsaW5nIGZvciBhIGxpbmVhciBtb2RlbCB3aXRoIGBBYCBhcyBhIGRlcGVuZGVudCB2YXJpYWJsZSBhbmQgYEJgLCBgQ2AgYXMgcHJlZGljdG9ycy4gV2Ugd2lsbCBsZXQgdGhlc2UgdGhpbmdzIGNvbXBsaWNhdGUgaW4gdGhlIGZ1dHVyZSwgZG9uJ3Qgd29ycnkuIFRoZSBvYmplY3QgYHJlZ2Agc3RvcmVzIHRoZSByZXN1bHRzIG9mIG91ciBhdHRlbXB0IGF0IGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLCBhbmQgaGFzIGl0cyBvd24gY2xhc3Mgb2YgYGxtYCwgYXMgeW91IGNhbiBvYnNlcnZlIGZvbGxvd2luZyB0aGUgY2FsbCB0byBgY2xhc3MocmVnKWAuDQoNCkxldCdzIGluc3BlY3QgdGhlIHJlc3VsdCBtb3JlIHRob3JvdWdobHk6DQoNCmBgYCB7ciBlY2hvID0gVH0NCnN1bW1hcnkocmVnKQ0KYGBgDQoNClRoZSBvdXRwdXQgcHJvdmlkZXM6IA0KDQorIGEgY2FsbCB0aGF0IGhhcyBnZW5lcmF0ZWQgdGhlIGxpbmVhciBtb2RlbCBvYmplY3QgYHJlZ2A7DQorIGEgYmFzaWMgb3ZlcnZpZXcgb2YgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBmb3IgbW9kZWwgcmVzaWR1YWxzOw0KKyBhIHRhYmxlIG9mIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzOiB0aGVyZSBhcmUgb25seSB0d28gZm9yIHRoZSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwsIG5hbWVseSB0aGUgbW9kZWwgaW50ZXJjZXB0IGFuZCB0aGUgc2xvcGUgKGkuZS4gdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgZm9yIHRoZSBvbmUgYW5kIG9ubHkgcHJlZGljdG9yIGluIHRoZSBtb2RlbCksIGFuZCB0aGUgcmF3IChub3Qgc3RhbmRhcmRpemVkKSB2YWx1ZXMgb2YgdGhlIHByZWRpY3RvcnMgYXJlIHJlcG9ydGVkIGluIHRoZSBgRXN0aW1hdGVgIGNvbHVtbiwgYWNjb21wYW5pZWQgYnkgcmVzcGVjdGl2ZSBzdGFuZGFyZCBlcnJvcnMsIHQtdGVzdCBhZ2FpbnN0IHplcm8sIGFuZCB0aGUgcHJvYmFiaWxpdGllcyBvZiBjb21taXRpbmcgdG8gYSAkVHlwZSBJJCBlcnJvciBpbiBkcmF3aW5nIGNvbmNsdXNpb25zIGZyb20gdGhlc2UgdC10ZXN0czsNCisgUmVzaWR1YWwgU3RhbmRhcmQgRXJyb3I7DQorIE11bHRpcGxlICRSXjIkIGFuZCB0aGUgQWRqdXN0ZWQgJFJeMiQgdmFsdWVzOw0KKyBUaGUgJEYkIHRlc3Q6IHJhdGlvIG9mIHZhcmlhbmNlcyBjb21wdXRlZCBmcm9tIHRoZSAqcmVncmVzc2lvbiogYW5kICpyZXNpZHVhbCBTU0VzKiwgd2l0aCB0aGUgcmVzcGVjdGl2ZSBudW1iZXIgb2YgZGVncmVlcyBvZiBmcmVlZG9tIGFuZCBpdHMgcC12YWx1ZS4gDQoNClRvIGlzb2xhdGUgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGZyb20gdGhlIG1vZGVsOg0KDQpgYGAge3IgZWNobyA9IFR9DQpjb2Vmc1JlZyA8LSBjb2VmZmljaWVudHMocmVnKQ0KY29lZnNSZWcNCmBgYA0KDQpgYGAge3IgZWNobyA9IFR9DQpzbG9wZVJlZyA8LSBjb2Vmc1JlZ1syXQ0KcHJpbnQocGFzdGUwKCJTbG9wZTogIiwgc2xvcGVSZWcpKQ0KaW50ZXJjZXB0UmVnIDwtIGNvZWZzUmVnWzFdDQpwcmludChwYXN0ZTAoIkludGVyY2VwdDogIiwgaW50ZXJjZXB0UmVnKSkNCmBgYA0KDQpZb3UgY2FuIGFsc28gd29yayB3aXRoIHRoZSBgc3VtbWFyeSgpYCBvZiB0aGUgYGxtYCBjbGFzcyBhcyBhbiBvYmplY3Q6DQoNCmBgYCB7ciBlY2hvID0gVH0NCnNSZWcgPC0gc3VtbWFyeShyZWcpDQpzdHIoc1JlZykNCmBgYA0KDQpGb3IgZXhhbXBsZToNCg0KYGBgIHtyIGVjaG8gPSBUfQ0Kc1JlZyRyLnNxdWFyZWQNCmBgYA0KQ29ycmVsYXRpb24gaXMgdGhlbjoNCg0KYGBgIHtyIGVjaG8gPSBUfQ0Kc3FydChzUmVnJHIuc3F1YXJlZCkNCmBgYA0KDQpgYGAge3IgZWNobyA9IFR9DQpzUmVnJGZzdGF0aXN0aWMNCmBgYA0KDQpgYGAge3IgZWNobyA9IFR9DQpzUmVnJGNvZWZmaWNpZW50cw0KYGBgDQpOb3csIHRoZSBkaXN0cmlidXRpb24gb2YgcmVzaWR1YWxzIC0gdGhlICRcZXBzaWxvbiQgaW4gdGhlIG1vZGVsIGVxdWF0aW9uIC0gdG8gYmUgZGlzY3Vzc2VkIGluIHRoZSBTZXNzaW9uOg0KDQpgYGAge3IgZWNobyA9IFR9DQpoaXN0KHNSZWckcmVzaWR1YWxzLCAyMCwgcHJvYmFiaWxpdHkgPSBULA0KICAgICBtYWluID0gJ1Jlc2lkdWFscycsDQogICAgIHhsYWIgPSAnUmVzaWR1YWxzJywgeWxhYiA9ICdEZW5zaXR5JywNCiAgICAgY29sID0gIm9yYW5nZSIpDQpkZW5zUmVnUmVzIDwtIGRhdGEuZnJhbWUoeCAgPSBzUmVnJHJlc2lkdWFscywNCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZG5vcm0oc1JlZyRyZXNpZHVhbHMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuKHNSZWckcmVzaWR1YWxzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkKHNSZWckcmVzaWR1YWxzKSkpDQpkZW5zUmVnUmVzIDwtIGRlbnNSZWdSZXNbb3JkZXIoZGVuc1JlZ1JlcyR4KSwgXQ0KbGluZXMoZGVuc1JlZ1JlcywNCiAgICAgIGx0eSA9ICJkYXNoZWQiLCANCiAgICAgIGx3ZCA9IDEsIA0KICAgICAgY29sID0gImJsdWUiKQ0KYGBgDQoNCkludGVyZXN0aW5nOiBmcm9tIHRoZSBsYXN0IGhpc3RvZ3JhbSwgd291bGQgeW91IHNheSB0aGF0IHRoZSByZXNpZHVhbHMgaW4gdGhpcyBsaW5lYXIgbW9kZWwgYXJlICpub3JtYWxseSBkaXN0cmlidXRlZCo/IA0KDQojIyMgRnVydGhlciBSZWFkaW5ncw0KDQotIFtWSURFTyAtIEluIGRlcHRoLCBoaWdobHkgcmVjb21tZW5kZWQ6IEV4cGxvcmluZyBiaXZhcmlhdGUgbnVtZXJpY2FsIGRhdGEsIEtoYW4gQWNhZGVteV0oaHR0cHM6Ly93d3cua2hhbmFjYWRlbXkub3JnL21hdGgvc3RhdGlzdGljcy1wcm9iYWJpbGl0eS9kZXNjcmliaW5nLXJlbGF0aW9uc2hpcHMtcXVhbnRpdGF0aXZlLWRhdGEpDQotIFtTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24gaW4gUiBmcm9tIFNUSERBIC0gQSBicmllZiBvdmVydmlld10oaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC9hcnRpY2xlcy80MC1yZWdyZXNzaW9uLWFuYWx5c2lzLzE2Ny1zaW1wbGUtbGluZWFyLXJlZ3Jlc3Npb24taW4tci8pDQoNCg0KDQojIyMgUiBNYXJrZG93bg0KDQpbUiBNYXJrZG93bl0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vKSBpcyB3aGF0IEkgaGF2ZSB1c2VkIHRvIHByb2R1Y2UgdGhpcyBiZWF1dGlmdWwgTm90ZWJvb2suIFdlIHdpbGwgbGVhcm4gbW9yZSBhYm91dCBpdCBuZWFyIHRoZSBlbmQgb2YgdGhlIGNvdXJzZSwgYnV0IGlmIHlvdSBhbHJlYWR5IGZlZWwgcmVhZHkgdG8gZGl2ZSBkZWVwLCBoZXJlJ3MgYSBib29rOiBbUiBNYXJrZG93bjogVGhlIERlZmluaXRpdmUgR3VpZGUsIFlpaHVpIFhpZSwgSi4gSi4gQWxsYWlyZSwgR2FycmV0dCBHcm9sZW11bmRzLl0oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykgDQoNCg0KKioqDQpHb3JhbiBTLiBNaWxvdmFub3ZpxIcNCg0KRGF0YUtvbGVrdGl2LCAyMDIwLzIxDQoNCmNvbnRhY3Q6IGdvcmFuLm1pbG92YW5vdmljQGRhdGFrb2xla3Rpdi5jb20NCg0KIVtdKC4uL19pbWcvREtfTG9nb18xMDAucG5nKQ0KDQoqKioNCkxpY2Vuc2U6IFtHUEx2M10oaHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzL2dwbC0zLjAudHh0KQ0KVGhpcyBOb3RlYm9vayBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uDQpUaGlzIE5vdGVib29rIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4NCllvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFsb25nIHdpdGggdGhpcyBOb3RlYm9vay4gSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+Lg0KDQoqKioNCg0K