Session 16. Generalized Linear Models I. Binary classification problems: enters Binomial Logistic Regression. Probability Theory: a Maximum Likelihood Estimate (MLE).

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?

We will now begin to introduce a set of extremely useful statistical learning models in this Session. Their name - Generalized Linear Models (GLMs) - suggests that their are somehow related to Simple and Multiple Linear Regression models and yet somehow go beyond them. That is correct: GLMs generalize the linear model, where predictors and their respective coefficients produce a linear combination of vectors, by introducing link functions to solve those kinds of problems that cannot be handled by Linear Regression. For example, what if the problem is not to predict a continuous value of the criterion, but the outcome variable is rather a dichotomy and then the problem becomes the one of categorization? E.g. predict the sex of a respondent given a set \({X}\) of their features? Enters Binomial Logistic Regression, the simplest GLM. Another thing: GLMs cannot be estimated by minimizing the quadratic error as we have estimated Simple and Multiple Linear Regression in the previous Session15. The method used to estimate Linear Models is known as Least Squares Estimation. To fit GLMs to our data, we will introduce the concept of Likelihood in Probability Theory and learn about the Maximum Likelihood Estimate!

0. Prerequisites

Setup:

dataDir <- paste0(getwd(), "/_data/")
library(tidyverse)
library(data.table)

1. Expanding the Linear Model to solve for the categorization problem

1.1 Assumptions revisited

Let us briefly recall the assumptions of the (Multiple) Linear Regression model:

  • Variables are real numbers: both outcome and predictor variables are members of \(R\), the set of real numbers; at least in theory they can take any real value from -Inf to Inf.
  • Linearity: there must be a linear relationship between outcome variable and the predictor variable(s).
  • Normality: it is assumed that the residuals (i.e model errors) are normally distributed.
  • Homoscedasticity: the variances of error terms (i.e. residuals) are similar across the values of the predictor variables.
  • No autocorrelation: the residuals are not autocorrelated.
  • No influential cases: no outliers are present.
  • No Multicollinearity (in Multiple Regression only): the predictors are not that highly correlated with each other.

What if we observe a set of variables that somehow describe a statistical experiment that can result in any of the two discrete outcomes? For example, we observe a description of a behavior of a person, quantified in some way, and organized into a set of variables that should be used to predict the sex of that person? Or any other similar problem where the outcome can take only two values, say 0 or 1 (and immediately recall the Binomial Distribution)?

The assumptions of the Linear Model obviously constrain its application in such cases. We ask the following question now: would it be possible to generalize, or expand, modify the Linear Model somehow to be able to encompass the categorization problem? Because it sounds so appealing to be able to have a set of predictors, combine them in a linear fashion, and estimate the coefficients so to be able to predict whether the outcome would turn this way or another?

There is a way to develop such a generalization of the Linear Model. In its simplest form it represents the Binomial Logistic Regression. Binomial Logistic Regression is very similar to multiple regression, except that for the outcome variable is now a categorical variable (i.e. it is measured on a nominal scale that is a dichotomy).

1.2 Enters Logistic Regression

Let’s recall the form of the Linear Model with any number of predictors:

\[Y = \beta_0 + \beta_1X_1 + \beta_2X_2 + ... + \beta_kX_k + \epsilon\]

So we have a linear combination of \(k\) predictors \(\boldsymbol{X}\) plus the model error term \(\epsilon\) on the RHS, and the outcome variable \(Y\) on the LHS.

Now we assume that \(Y\) can take only two possible values, call them 0 and 1 for ease of discussion. We want to predict whether \(Y\) will happen to be (1) or not (0) given our observations of a set of predictors \(\boldsymbol{X}\). However, in Binary Logistic Regression we do not predict the value of the outcome itself, but rather the probability that the outcome will turn out 1 or 0 given the predictors.

In the simplest possible case, where there is only one predictor \(X_1\), this is exactly what we predict in Binary Logistic Regression:

\[P(Y) = p_1 = \frac{1}{1+e^{-(b_0 + b_1X_1)}}\] where \(b_0\) and \(b_1\) are the same old good linear model coefficients. As we will see, the linear coefficients have a new interpretation in Binary Logistic Regression - a one rather different that the one they receive in the scope of the Linear Model.

With \(k\) predictors we have:

\[P(Y) = p_1 = \frac{1}{1+e^{-(b_0 + b_1X_1 + b_2X_2 + ... + b_kX_k)}}\] Now the above equations looks like it felt from the clear blue sky to solve the problem. There is a clear motivation for its form, of course: imagine that instead of predicting the state of \(Y\) directly we decide to predicts the odds of \(Y\) turning out 1 instead of 0:

\[odds = \frac{p_1}{1-p_1}\] Now goes the trick: if instead of predicting the odds \(p_1/(1-p_1)\) we decide to predict the log-odds (also called: logit) from a linear combination of predictors

\[log \left( \frac{p_1}{1-p_1} \right) = b_0 + b_1X_1 + b_2X_2 + ... + b_kX_k\] it turns out that we can recover the odds by taking the exponent of both LHS and RHS:

\[\frac{p_1}{1-p_1} = e^{(b_0 + b_1X_1 + b_2X_2 + ... + b_kX_k)}\] and then by simple algebraic rearrangement we find that the probability \(p_1\) of the outcome \(Y\) turning out 1 is:

\[P(Y) = p_1 = \frac{1}{1+e^{-(b_0 + b_1X_1 + b_2X_2 + ... + b_kX_k)}}\]

Now, imagine we set a following criterion: anytime we estimate \(p_1\) to be larger than or equal to \(.5\) we predict that \(Y=1\), and anytime \(p_1 < .5\) we predict that \(Y=0\). What we need to do in order to be able to learn how to predict \(Y\) in this way is to estimate the coefficients \(b_0\), \(b_1\), \(b_2\), etc like we did in the case of a linear model. The estimation for GLMs is a bit different than we have learned in Session 15. But first let’s see how to perform Binary Logistic Regression in R.

2. Binomial Logistic Regression in R

We will use the dataset from the UCLA’s Institute of Digital Research and Education’s website on Statistical Consulting (they also have a nice exposition of the Binary Logistic regression):

A researcher is interested in how variables, such as GRE (Graduate Record Exam scores), GPA (grade point average) and prestige of the undergraduate institution, effect admission into graduate school. The response variable, admit/don’t admit, is a binary variable. Source: UCLA’s Institute of Digital Research and Education

dataSet <- read.csv("https://stats.idre.ucla.edu/stat/data/binary.csv")
head(dataSet)

Inspect the dataset:

dim(dataSet)
[1] 400   4

Let’s see what is in for us:

str(dataSet)
'data.frame':   400 obs. of  4 variables:
 $ admit: int  0 1 1 1 0 1 1 0 1 0 ...
 $ gre  : int  380 660 800 640 520 760 560 400 540 700 ...
 $ gpa  : num  3.61 3.67 4 3.19 2.93 3 2.98 3.08 3.39 3.92 ...
 $ rank : int  3 3 1 4 4 2 1 2 3 2 ...
# - descriptive statistics
summary(dataSet)
     admit             gre             gpa             rank      
 Min.   :0.0000   Min.   :220.0   Min.   :2.260   Min.   :1.000  
 1st Qu.:0.0000   1st Qu.:520.0   1st Qu.:3.130   1st Qu.:2.000  
 Median :0.0000   Median :580.0   Median :3.395   Median :2.000  
 Mean   :0.3175   Mean   :587.7   Mean   :3.390   Mean   :2.485  
 3rd Qu.:1.0000   3rd Qu.:660.0   3rd Qu.:3.670   3rd Qu.:3.000  
 Max.   :1.0000   Max.   :800.0   Max.   :4.000   Max.   :4.000  
sapply(dataSet, sd)
      admit         gre         gpa        rank 
  0.4660867 115.5165364   0.3805668   0.9444602 

So we need a model that predicts admit - a dichotomy - from the gre and gpa scores and the ranking of the educational institution found in rank. No wonder that the model can be written as admit ~ gre + gpa + rank in R:

# - rank to factor
# - Q: Why does rank go to factor?
# - A: Dummy coding... Remember?
dataSet$rank <- factor(dataSet$rank)

Here goes the glm() function:

# - model:
mylogit <- glm(admit ~ gre + gpa + rank,
               data = dataSet,
               family = "binomial")
modelsummary <- summary(mylogit)
print(modelsummary)

Call:
glm(formula = admit ~ gre + gpa + rank, family = "binomial", 
    data = dataSet)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.6268  -0.8662  -0.6388   1.1490   2.0790  

Coefficients:
             Estimate Std. Error z value Pr(>|z|)    
(Intercept) -3.989979   1.139951  -3.500 0.000465 ***
gre          0.002264   0.001094   2.070 0.038465 *  
gpa          0.804038   0.331819   2.423 0.015388 *  
rank2       -0.675443   0.316490  -2.134 0.032829 *  
rank3       -1.340204   0.345306  -3.881 0.000104 ***
rank4       -1.551464   0.417832  -3.713 0.000205 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 499.98  on 399  degrees of freedom
Residual deviance: 458.52  on 394  degrees of freedom
AIC: 470.52

Number of Fisher Scoring iterations: 4

A word on interpretation of the results:

  • Call is again just the model as we have formulated it;
  • Deviance residuals: in GLMs we do not use the same type of residuals as we did in Linear Models. There are several types of residuals of which the deviance residuals are most widely known and used (which does not mean that the remaining types of residuals in GLMs are not useful, to the contrary!). We will explain the deviance residuals later on.
  • Coefficients: as in lm() these are the model coefficients. The z value stands for the Wald’s test of whether the coefficient is significantly different from zero (remember that we have used the t-test in Linear Regression to test exactly the same hypothesis); the test is obtained by dividing the coefficient by its standard error.

N.B. There is a bug in the Wald’s Z, look:

The reason why the Wald statistic should be used cautiously is because, when the regression coefficient is large, the standard error tends to become inflated, resulting in the Wald statistic being underestimated (see Menard, 1995). The inflation of the standard error increases the probability of rejecting a predictor as being significant when in reality it is making a significant contribution to the model (i.e. you are more likely to make a Type II error). From: Andy Field, DISCOVERING STATISTICS USING SPSS, Third Edition, Sage.

  • Dispersion parameter for binomial family taken to be 1 - forget about this until we learn more about GLMs in the following sessions.
  • Null and Residual deviance: Remember how we have used the mean of the outcome \(Y\) as a baseline for the assessment of the Simple Linear Regression model in Session 15? In the Binary Logistic Regression setting that is not possible because the outcome variable is binary. What is the appropriate baseline model for a comparison of the effect of predictors in Binary Logistic Regression then? Well, we can take the probability of \(Y\) turning out 1 by just looking at the distribution of the outcome and pretend that there are no predictors at all: that would be the baseline model for Binary Logistic Regression. The null deviance describes the error of the baseline while the residual deviance describe the error from the current model. We will learn how to use them to assess the overall effect of the model.
  • AIC: short for the Akaike Information Criterion (to be explained in the Session). It is a measure of badness-of-fit: the lower the AIC the better the model.
  • Fisher scoring iterations: it has to do with model estimation and will not be considered here.

In order to understand precisely how a Binary Logistic Regression model is assessed - and more importantly why it is assessed in the way it is assessed - we first need to introduce the concept of Likelihood in Probability Theory. Now this is one powerful idea that we need to discuss!

3. Likelihood and the Maximum Likelihood Estimate (MLE)

3.1 Likelihood Function

Imagine we toss a fair coin with \(p_H = .5\) twice and observe two Heads.The probability of observing two heads with \(p_H = .5\) is:

\[P(HH|p_H = .5) = .5 * .5 = .5^{2} = .25\] Now take a look at the following function: \(P(HH|p_H)\). Imagine that the data, the results of our observations - that we have seen two heads in a row - are fixed. Than \(P(HH|p_H)\) is a function of the parameter \(p_H\). Imagine that we start changing the value of \(p_H\) while keeping the data fixed and with every change in parameter we compute the value of \(P(HH|p_H)\) again and again. For example, what is the value of \(P(HH|p_H)\) if \(p_H = .3\)?

\[P(HH|p_H = .3) = .3 * .3 = .3^{2} = .09\] And what if \(p_H = .9\)?

\[P(HH|p_H = .9) = .9 * .9 = .9^{2} = .81\] We have observed two heads; in the universe of our small statistical experiment we have actually observed all heads, right? So, as we increase the value of \(p_H\), the value of \(P(HH|p_H)\) tends to increase: it was .09 when \(p_H = .3\), then .25 for \(p_H = .5\), and finally .81 for \(p_H = .9\). Even if we already know that the coin is fair - hence \(p_H = .5\) - the observed data inform us that it is more likely to be higher.

\(P(HH|p_H)\), also written as \(L(p_H|HH)\), reads: the likelihood of the parameter value \(p_H\) given the data \(HH\). We can plot the whole Likelihood function for this experiment easily:

likelihood <- data.frame(parameter = seq(.01, .99, by = .01))
likelihood$likelihood <- likelihood$parameter^2

ggplot(likelihood, 
       aes(x = parameter, 
           y = likelihood)) + 
  geom_smooth(size = .25, se = F) + 
  ggtitle("Likelihood function for HH") +
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust = .5))

What if we have observed \(HHTTH\) in five tosses?

likelihood <- data.frame(parameter = seq(.01, .99, by = .01))

likelihood$likelihood <- 
  likelihood$parameter^2 * (1-likelihood$parameter)^2 * likelihood$parameter

ggplot(likelihood, 
       aes(x = parameter, 
           y = likelihood)) + 
  geom_smooth(size = .25, se = F) + 
  ggtitle("Likelihood function for HHTTH") +
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust = .5))

How did we get to it? Look, if the data are \(HHTTH\), then the Likelihood function for \(p_H = .2\) must be:

\[P(HHTTH|p_H = .2) = .2 * .2 * (1-.2) * (1-.2) * .2 = .2^{2} * (1-.2)^{2} * .2 = .00512\] Let’s check in R:

.2^2*(1-.2)^2*.2
[1] 0.00512

And now we just need to compute the Likelihood function across the whole domain of \(p_H\). As simple as that!

3.2 Likelihood in the Binomial Logistic Regression

Say we have observed the following data on higher education admissions: \(HHTHTTHHHT\). Assume that we know the parameter \(p_H\). We can compute the Likelihood function from the following equation:

\(L(p_H|HHTHTTHHHT)\) exactly as we did before. Now, this is the general form of the Binomial Likelihood (where \(Y\) stands for the observed data):

\[L(p|Y) = p_1^y(1-p_1)^{n-y}\] where \(y\) is the number of successes and \(n\) the total number of observations. For each observed data point then we have

\[L(p|y_i) = p_1^{y_i}(1-p_1)^{\bar{y_i}}\] where \({y_i}\) is the observed value of the outcome, \(Y\), and \(\bar{y_i}\) is its complement (e.g. 1 for 0 and 0 for 1). This form just determines which value will be used in the computation of the Likelihood function at each observed data point: it will be either \(p_1\) or \(1-p_1\). The likelihood function for a given value of \(p_1\) for the whole dataset is computed by multiplying the values of \(L(p|y_i)\) across the whole dataset (remember that multiplication in Probability is what conjunction is in Logic and Algebra).

Q: But… how do we get to \(p_1\), the parameter value that we will use at each data point? A: We will search the parameter space, of course, \(\beta_0, \beta_1, ... \beta_k\) of linear coefficients in our Binary Logistic Model, computing \(p_1\) every time, and compute the likelihood function from it! In other words: we will search the parameter space to find the combination of \(\beta_0, \beta_1, ... \beta_k\) that produces the maximum of the likelihood function similarly as we have searched the space of linear coefficients to find the combination that minimizes the squared error in Simple Linear Regression.

So what combination of the linear coefficients is the best one?

It is the one which gives the maximum likelihood. This approach, known as Maximum Likelihood Estimation (MLE), stands behind many important statistical learning models. It presents the corner stone of the Statistical Estimation Theory. It is contrasted with the Least Squares Estimation that we have used to estimate the Simple Linear Regression model in Session 15.

Now, there is a technical problem related to this approach. To obtain the likelihood for the whole dataset one needs to multiply as many very small numbers as there are data points. That can cause computational problems related to the smallest real numbers that can be represented by digital computers. The workaround is to use the logarithm of likelihood instead, known as log-likelihood (\(LL\)).

Thus, while the Likelihood function for the whole dataset would be

\[L(p|Y) = \prod_{i=1}^{n}p_1^{y_i}(1-p_1)^{\bar{y_i}}\] the Log-Likelihood function would be:

\[LL(p|Y) = \sum_{i=1}^{n} y_ilog(p_1)+\bar{y_i}log(1-p_1)\] And finally here is how we solve the Binomial Logistic Regression problem:

  • search throught the parameter space spawned by linear coefficients \(\beta_0, \beta_1, ... \beta_k\),
  • predict \(p_1\) from the model and a particular combination of the parameters,
  • compute the value of the Likelihood function for the whole dataset,
  • find the combination that yields the maximum value of the Likelihood function.

Technically, in optimization we would not go exactly for the maximum of the Likelihood function, because we use \(LL\) instead of \(L(p|Y)\). The solution is to minimize the negative \(LL\), sometimes written simply as \(NLL\), the Negative Log-Likelihood function.

4. Assessing the Binary Logistic Regression model

We will begin with the model coefficients. Coefficients in Binary Logistic Regression tell about the change in the log-odds of the outcome for a one unit increase in the predictor variable:

modelcoefs <- modelsummary$coefficients
print(modelcoefs)
                Estimate  Std. Error   z value     Pr(>|z|)
(Intercept) -3.989979073 1.139950936 -3.500132 0.0004650273
gre          0.002264426 0.001093998  2.069864 0.0384651284
gpa          0.804037549 0.331819298  2.423119 0.0153878974
rank2       -0.675442928 0.316489661 -2.134171 0.0328288188
rank3       -1.340203916 0.345306418 -3.881202 0.0001039415
rank4       -1.551463677 0.417831633 -3.713131 0.0002047107

To extract the coefficients only:

# - coefficients only:
mcoefs <- coef(mylogit)
mcoefs
 (Intercept)          gre          gpa        rank2        rank3        rank4 
-3.989979073  0.002264426  0.804037549 -0.675442928 -1.340203916 -1.551463677 

Now, the log-odds scale is odd in itself… But if take the exp() of the coefficients then their interpretation becomes: how much do the odds of being 1 instead of being 0 increase with a unit increase in the predictor.

# - You can also exponentiate the coefficients and 
# - interpret them as factors of odds-ratios:
exp(coef(mylogit))
(Intercept)         gre         gpa       rank2       rank3       rank4 
  0.0185001   1.0022670   2.2345448   0.5089310   0.2617923   0.2119375 

The obtain the confidence intervals:

# - Confidence Intervals on model coefficients:
confint(mylogit, level = .99)
                    0.5 %       99.5 %
(Intercept) -7.0108205916 -1.114337828
gre         -0.0005261955  0.005132102
gpa         -0.0401196327  1.676398025
rank2       -1.5005096781  0.137948894
rank3       -2.2490901281 -0.461746023
rank4       -2.6816859220 -0.509488785

Of course, exp() to change to the odds scale:

# - Confidence Intervals on model coefficients:
exp(confint(mylogit, level = .99))
                   0.5 %    99.5 %
(Intercept) 0.0009020681 0.3281325
gre         0.9994739429 1.0051453
gpa         0.9606745042 5.3462641
rank2       0.2230164646 1.1479169
rank3       0.1054951680 0.6301824
rank4       0.0684476594 0.6008026

The model log-likelihood at the optimal parameter values is obtained from logLik():

# - The model likelihood is:
logLik(mylogit)
'log Lik.' -229.2587 (df=6)
# - Note: models w. lower logLike are better

Let’s now talk about AIC, the Akaike Information Criterion.

# - Akaike Information Criterion
# - see: https://en.wikipedia.org/wiki/Akaike_information_criterion
mylogit$aic
[1] 470.5175
# - Note: models w. lower AIC are better

The AIC is computed in the following way:

\[AIC = 2k - 2LL\] where \(k\) is the number of parameters estimated:

-2*as.numeric(logLik(mylogit)) + 2*6
[1] 470.5175

There is also the AIC() function:

AIC(mylogit)
[1] 470.5175

Finally, let’s explain what the deviance residual is. The (squared) deviance of each data point is equal to -2 times the logarithm of the difference between its predicted probability and the complement of its actual value (1 for a 0 and a 0 for a 1). And why would anyone construct such a measure of model error?

# - model deviance: 
print(mylogit$deviance)
[1] 458.5175

Interesting enough, \(-2LL\) equals the total model deviance…

ll <- logLik(mylogit)
-2*as.numeric(ll) == mylogit$deviance
[1] TRUE

Check again… The total deviance first:

deviances <- residuals(mylogit, 
                       type = "deviance")
sum(deviances^2) == mylogit$deviance
[1] TRUE

So, the deviances decompose the total model likelihood similarly as residuals in Linear Regression decompose the total model error…

sum(deviances^2) == -2*as.numeric(ll)
[1] TRUE

Finally, does the model in itself have any effect? Did we gain anything from introducing the predictors? The following difference between the residual and null deviance follows a \(\chi^2\)-distribution…

# - Comparison to a so-called Null model (intercept only)
# - The following is Chi-Square distributed...
dev <- mylogit$null.deviance - mylogit$deviance
print(dev)
[1] 41.45903

… with the dfs number of degrees of freedom:

dfs <- mylogit$df.null - mylogit$df.residual
print(dfs)
[1] 5

And the, as ever: how extreme the probability of observing this is to check for the Type I Error:

pchisq(dev, dfs, lower.tail = FALSE)
[1] 7.578194e-08

R Markdown


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/.


LS0tCnRpdGxlOiBJbnRybyB0byBEYXRhIFNjaWVuY2UgKE5vbi1UZWNobmljYWwgQmFja2dyb3VuZCwgUikgLSBTZXNzaW9uMTYKYXV0aG9yOgotIG5hbWU6IEdvcmFuIFMuIE1pbG92YW5vdmnEhywgUGhECiAgYWZmaWxpYXRpb246IERhdGFLb2xla3RpdiwgQ2hpZWYgU2NpZW50aXN0ICYgT3duZXI7IERhdGEgU2NpZW50aXN0IGZvciBXaWtpZGF0YSwgV01ERQphYnN0cmFjdDogCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aGVtZTogc3BhY2VsYWIKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiA1CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDUKLS0tCgohW10oLi4vX2ltZy9ES19Mb2dvXzEwMC5wbmcpCgoqKioKIyBTZXNzaW9uIDE2LiBHZW5lcmFsaXplZCBMaW5lYXIgTW9kZWxzIEkuIEJpbmFyeSBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtczogZW50ZXJzIEJpbm9taWFsIExvZ2lzdGljIFJlZ3Jlc3Npb24uIFByb2JhYmlsaXR5IFRoZW9yeTogYSBNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdGUgKE1MRSkuCgoqKkZlZWRiYWNrKiogc2hvdWxkIGJlIHNlbmQgdG8gYGdvcmFuLm1pbG92YW5vdmljQGRhdGFrb2xla3Rpdi5jb21gLiAKVGhlc2Ugbm90ZWJvb2tzIGFjY29tcGFueSB0aGUgSW50cm8gdG8gRGF0YSBTY2llbmNlOiBOb24tVGVjaG5pY2FsIEJhY2tncm91bmQgY291cnNlIDIwMjAvMjEuCgoqKioKCiMjIyBXaGF0IGRvIHdlIHdhbnQgdG8gZG8gdG9kYXk/CgpXZSB3aWxsIG5vdyBiZWdpbiB0byBpbnRyb2R1Y2UgYSBzZXQgb2YgZXh0cmVtZWx5IHVzZWZ1bCBzdGF0aXN0aWNhbCBsZWFybmluZyBtb2RlbHMgaW4gdGhpcyBTZXNzaW9uLiBUaGVpciBuYW1lIC0gKkdlbmVyYWxpemVkIExpbmVhciBNb2RlbHMgKEdMTXMpKiAtIHN1Z2dlc3RzIHRoYXQgdGhlaXIgYXJlIHNvbWVob3cgcmVsYXRlZCB0byBTaW1wbGUgYW5kIE11bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uIG1vZGVscyBhbmQgeWV0IHNvbWVob3cgZ28gYmV5b25kIHRoZW0uIFRoYXQgaXMgY29ycmVjdDogR0xNcyBnZW5lcmFsaXplIHRoZSBsaW5lYXIgbW9kZWwsIHdoZXJlIHByZWRpY3RvcnMgYW5kIHRoZWlyIHJlc3BlY3RpdmUgY29lZmZpY2llbnRzIHByb2R1Y2UgYSBsaW5lYXIgY29tYmluYXRpb24gb2YgdmVjdG9ycywgYnkgaW50cm9kdWNpbmcgKmxpbmsqIGZ1bmN0aW9ucyB0byBzb2x2ZSB0aG9zZSBraW5kcyBvZiBwcm9ibGVtcyB0aGF0IGNhbm5vdCBiZSBoYW5kbGVkIGJ5IExpbmVhciBSZWdyZXNzaW9uLiBGb3IgZXhhbXBsZSwgd2hhdCBpZiB0aGUgcHJvYmxlbSBpcyBub3QgdG8gcHJlZGljdCBhIGNvbnRpbnVvdXMgdmFsdWUgb2YgdGhlIGNyaXRlcmlvbiwgYnV0IHRoZSBvdXRjb21lIHZhcmlhYmxlIGlzIHJhdGhlciBhIGRpY2hvdG9teSBhbmQgdGhlbiB0aGUgcHJvYmxlbSBiZWNvbWVzIHRoZSBvbmUgb2YgY2F0ZWdvcml6YXRpb24/IEUuZy4gcHJlZGljdCB0aGUgc2V4IG9mIGEgcmVzcG9uZGVudCBnaXZlbiBhIHNldCAke1h9JCBvZiB0aGVpciBmZWF0dXJlcz8gRW50ZXJzICpCaW5vbWlhbCBMb2dpc3RpYyBSZWdyZXNzaW9uKiwgdGhlIHNpbXBsZXN0IEdMTS4gQW5vdGhlciB0aGluZzogR0xNcyBjYW5ub3QgYmUgZXN0aW1hdGVkIGJ5IG1pbmltaXppbmcgdGhlIHF1YWRyYXRpYyBlcnJvciBhcyB3ZSBoYXZlIGVzdGltYXRlZCBTaW1wbGUgYW5kIE11bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uIGluIHRoZSBwcmV2aW91cyBTZXNzaW9uMTUuIFRoZSBtZXRob2QgdXNlZCB0byBlc3RpbWF0ZSBMaW5lYXIgTW9kZWxzIGlzIGtub3duIGFzICpMZWFzdCBTcXVhcmVzIEVzdGltYXRpb24qLiBUbyBmaXQgR0xNcyB0byBvdXIgZGF0YSwgd2Ugd2lsbCBpbnRyb2R1Y2UgdGhlIGNvbmNlcHQgb2YgKkxpa2VsaWhvb2QqIGluIFByb2JhYmlsaXR5IFRoZW9yeSBhbmQgbGVhcm4gYWJvdXQgdGhlICpNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdGUqIQoKCiMjIyAwLiBQcmVyZXF1aXNpdGVzCgpTZXR1cDoKCmBgYHtyIGVjaG8gPSBULCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEZ9CmRhdGFEaXIgPC0gcGFzdGUwKGdldHdkKCksICIvX2RhdGEvIikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZGF0YS50YWJsZSkKYGBgCgoKIyMjIDEuIEV4cGFuZGluZyB0aGUgTGluZWFyIE1vZGVsIHRvIHNvbHZlIGZvciB0aGUgY2F0ZWdvcml6YXRpb24gcHJvYmxlbQoKIyMjIyAxLjEgQXNzdW1wdGlvbnMgcmV2aXNpdGVkCgpMZXQgdXMgYnJpZWZseSByZWNhbGwgdGhlIGFzc3VtcHRpb25zIG9mIHRoZSAoTXVsdGlwbGUpIExpbmVhciBSZWdyZXNzaW9uIG1vZGVsOgoKKyAqVmFyaWFibGVzIGFyZSByZWFsIG51bWJlcnMqOiBib3RoIG91dGNvbWUgYW5kIHByZWRpY3RvciB2YXJpYWJsZXMgYXJlIG1lbWJlcnMgb2YgJFIkLCB0aGUgc2V0IG9mIHJlYWwgbnVtYmVyczsgYXQgbGVhc3QgaW4gdGhlb3J5IHRoZXkgY2FuIHRha2UgYW55IHJlYWwgdmFsdWUgZnJvbSBgLUluZmAgdG8gYEluZmAuCisgKkxpbmVhcml0eSo6IHRoZXJlIG11c3QgYmUgYSBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gb3V0Y29tZSB2YXJpYWJsZSBhbmQgdGhlIHByZWRpY3RvciB2YXJpYWJsZShzKS4KKyAqTm9ybWFsaXR5KjogaXQgaXMgYXNzdW1lZCB0aGF0IHRoZSByZXNpZHVhbHMgKGkuZSBtb2RlbCBlcnJvcnMpIGFyZSBub3JtYWxseSBkaXN0cmlidXRlZC4KKyAqSG9tb3NjZWRhc3RpY2l0eSo6IHRoZSB2YXJpYW5jZXMgb2YgZXJyb3IgdGVybXMgKGkuZS4gcmVzaWR1YWxzKSBhcmUgc2ltaWxhciBhY3Jvc3MgdGhlIHZhbHVlcyBvZiB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcy4KKyAqTm8gYXV0b2NvcnJlbGF0aW9uKjogdGhlIHJlc2lkdWFscyBhcmUgbm90IGF1dG9jb3JyZWxhdGVkLgorICpObyBpbmZsdWVudGlhbCBjYXNlcyo6IG5vIG91dGxpZXJzIGFyZSBwcmVzZW50LgorICpObyBNdWx0aWNvbGxpbmVhcml0eSogKGluIE11bHRpcGxlIFJlZ3Jlc3Npb24gb25seSk6IHRoZSBwcmVkaWN0b3JzIGFyZSBub3QgdGhhdCBoaWdobHkgY29ycmVsYXRlZCB3aXRoIGVhY2ggb3RoZXIuCgpXaGF0IGlmIHdlIG9ic2VydmUgYSBzZXQgb2YgdmFyaWFibGVzIHRoYXQgc29tZWhvdyBkZXNjcmliZSBhIHN0YXRpc3RpY2FsIGV4cGVyaW1lbnQgdGhhdCBjYW4gcmVzdWx0IGluIGFueSBvZiB0aGUgdHdvIGRpc2NyZXRlIG91dGNvbWVzPyBGb3IgZXhhbXBsZSwgd2Ugb2JzZXJ2ZSBhIGRlc2NyaXB0aW9uIG9mIGEgYmVoYXZpb3Igb2YgYSBwZXJzb24sIHF1YW50aWZpZWQgaW4gc29tZSB3YXksIGFuZCBvcmdhbml6ZWQgaW50byBhIHNldCBvZiB2YXJpYWJsZXMgdGhhdCBzaG91bGQgYmUgdXNlZCB0byBwcmVkaWN0IHRoZSBzZXggb2YgdGhhdCBwZXJzb24/IE9yIGFueSBvdGhlciBzaW1pbGFyIHByb2JsZW0gd2hlcmUgdGhlIG91dGNvbWUgY2FuIHRha2Ugb25seSB0d28gdmFsdWVzLCBzYXkgYDBgIG9yIGAxYCAoYW5kIGltbWVkaWF0ZWx5IHJlY2FsbCB0aGUgQmlub21pYWwgRGlzdHJpYnV0aW9uKT8KClRoZSBhc3N1bXB0aW9ucyBvZiB0aGUgTGluZWFyIE1vZGVsIG9idmlvdXNseSBjb25zdHJhaW4gaXRzIGFwcGxpY2F0aW9uIGluIHN1Y2ggY2FzZXMuIFdlIGFzayB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uIG5vdzogd291bGQgaXQgYmUgcG9zc2libGUgdG8gKmdlbmVyYWxpemUqLCBvciAqZXhwYW5kKiwgKm1vZGlmeSogdGhlIExpbmVhciBNb2RlbCBzb21laG93IHRvIGJlIGFibGUgdG8gZW5jb21wYXNzIHRoZSBjYXRlZ29yaXphdGlvbiBwcm9ibGVtPyBCZWNhdXNlIGl0IHNvdW5kcyBzbyBhcHBlYWxpbmcgdG8gYmUgYWJsZSB0byBoYXZlIGEgc2V0IG9mIHByZWRpY3RvcnMsIGNvbWJpbmUgdGhlbSBpbiBhIGxpbmVhciBmYXNoaW9uLCBhbmQgZXN0aW1hdGUgdGhlIGNvZWZmaWNpZW50cyBzbyB0byBiZSBhYmxlIHRvIHByZWRpY3Qgd2hldGhlciB0aGUgb3V0Y29tZSB3b3VsZCB0dXJuIHRoaXMgd2F5IG9yIGFub3RoZXI/CgpUaGVyZSBpcyBhIHdheSB0byBkZXZlbG9wIHN1Y2ggYSBnZW5lcmFsaXphdGlvbiBvZiB0aGUgTGluZWFyIE1vZGVsLiBJbiBpdHMgc2ltcGxlc3QgZm9ybSBpdCByZXByZXNlbnRzIHRoZSAqQmlub21pYWwgTG9naXN0aWMgUmVncmVzc2lvbiouIEJpbm9taWFsIExvZ2lzdGljIFJlZ3Jlc3Npb24gaXMgdmVyeSBzaW1pbGFyIHRvIG11bHRpcGxlIHJlZ3Jlc3Npb24sIGV4Y2VwdCB0aGF0IGZvciB0aGUgb3V0Y29tZSB2YXJpYWJsZSBpcyBub3cgYSAqY2F0ZWdvcmljYWwgdmFyaWFibGUqIChpLmUuIGl0IGlzIG1lYXN1cmVkIG9uIGEgbm9taW5hbCBzY2FsZSB0aGF0IGlzIGEgKmRpY2hvdG9teSopLgoKIyMjIyAxLjIgRW50ZXJzIExvZ2lzdGljIFJlZ3Jlc3Npb24KCkxldCdzIHJlY2FsbCB0aGUgZm9ybSBvZiB0aGUgTGluZWFyIE1vZGVsIHdpdGggYW55IG51bWJlciBvZiBwcmVkaWN0b3JzOgoKJCRZID0gXGJldGFfMCArIFxiZXRhXzFYXzEgKyBcYmV0YV8yWF8yICsgLi4uICsgXGJldGFfa1hfayArIFxlcHNpbG9uJCQKClNvIHdlIGhhdmUgYSBsaW5lYXIgY29tYmluYXRpb24gb2YgJGskIHByZWRpY3RvcnMgJFxib2xkc3ltYm9se1h9JCBwbHVzIHRoZSBtb2RlbCBlcnJvciB0ZXJtICRcZXBzaWxvbiQgb24gdGhlIFJIUywgYW5kIHRoZSBvdXRjb21lIHZhcmlhYmxlICRZJCBvbiB0aGUgTEhTLiAKCk5vdyB3ZSBhc3N1bWUgdGhhdCAkWSQgY2FuIHRha2Ugb25seSB0d28gcG9zc2libGUgdmFsdWVzLCBjYWxsIHRoZW0gYDBgIGFuZCBgMWAgZm9yIGVhc2Ugb2YgZGlzY3Vzc2lvbi4gV2Ugd2FudCB0byBwcmVkaWN0IHdoZXRoZXIgJFkkIHdpbGwgaGFwcGVuIHRvIGJlIChgMWApIG9yIG5vdCAoYDBgKSBnaXZlbiBvdXIgb2JzZXJ2YXRpb25zIG9mIGEgc2V0IG9mIHByZWRpY3RvcnMgJFxib2xkc3ltYm9se1h9JC4gSG93ZXZlciwgaW4gQmluYXJ5IExvZ2lzdGljIFJlZ3Jlc3Npb24gd2UgZG8gbm90IHByZWRpY3QgdGhlIHZhbHVlIG9mIHRoZSBvdXRjb21lIGl0c2VsZiwgYnV0IHJhdGhlciB0aGUgKnByb2JhYmlsaXR5KiB0aGF0IHRoZSBvdXRjb21lIHdpbGwgdHVybiBvdXQgYDFgIG9yIGAwYCBnaXZlbiB0aGUgcHJlZGljdG9ycy4gCgpJbiB0aGUgc2ltcGxlc3QgcG9zc2libGUgY2FzZSwgd2hlcmUgdGhlcmUgaXMgb25seSBvbmUgcHJlZGljdG9yICRYXzEkLCB0aGlzIGlzIGV4YWN0bHkgd2hhdCB3ZSBwcmVkaWN0IGluIEJpbmFyeSBMb2dpc3RpYyBSZWdyZXNzaW9uOgoKJCRQKFkpID0gcF8xID0gIFxmcmFjezF9ezErZV57LShiXzAgKyBiXzFYXzEpfX0kJAp3aGVyZSAkYl8wJCBhbmQgJGJfMSQgYXJlIHRoZSBzYW1lIG9sZCBnb29kIGxpbmVhciBtb2RlbCBjb2VmZmljaWVudHMuIEFzIHdlIHdpbGwgc2VlLCB0aGUgbGluZWFyIGNvZWZmaWNpZW50cyBoYXZlIGEgbmV3IGludGVycHJldGF0aW9uIGluIEJpbmFyeSBMb2dpc3RpYyBSZWdyZXNzaW9uIC0gYSBvbmUgcmF0aGVyIGRpZmZlcmVudCB0aGF0IHRoZSBvbmUgdGhleSByZWNlaXZlIGluIHRoZSBzY29wZSBvZiB0aGUgTGluZWFyIE1vZGVsLgoKV2l0aCAkayQgcHJlZGljdG9ycyB3ZSBoYXZlOgoKJCRQKFkpID0gcF8xID0gXGZyYWN7MX17MStlXnstKGJfMCArIGJfMVhfMSArIGJfMlhfMiArIC4uLiArIGJfa1hfayl9fSQkCk5vdyB0aGUgYWJvdmUgZXF1YXRpb25zIGxvb2tzIGxpa2UgaXQgZmVsdCBmcm9tIHRoZSBjbGVhciBibHVlIHNreSB0byBzb2x2ZSB0aGUgcHJvYmxlbS4gVGhlcmUgaXMgYSBjbGVhciBtb3RpdmF0aW9uIGZvciBpdHMgZm9ybSwgb2YgY291cnNlOiBpbWFnaW5lIHRoYXQgaW5zdGVhZCBvZiBwcmVkaWN0aW5nIHRoZSBzdGF0ZSBvZiAkWSQgZGlyZWN0bHkgd2UgZGVjaWRlIHRvIHByZWRpY3RzIHRoZSAqb2Rkcyogb2YgJFkkIHR1cm5pbmcgb3V0IGAxYCBpbnN0ZWFkIG9mIGAwYDoKCiQkb2RkcyA9IFxmcmFje3BfMX17MS1wXzF9JCQKTm93IGdvZXMgdGhlIHRyaWNrOiBpZiBpbnN0ZWFkIG9mIHByZWRpY3RpbmcgdGhlIG9kZHMgJHBfMS8oMS1wXzEpJCB3ZSBkZWNpZGUgdG8gcHJlZGljdCB0aGUgKipsb2ctb2RkcyoqIChhbHNvIGNhbGxlZDogKmxvZ2l0KikgZnJvbSBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiBwcmVkaWN0b3JzCgokJGxvZyBcbGVmdCggXGZyYWN7cF8xfXsxLXBfMX0gXHJpZ2h0KSA9IGJfMCArIGJfMVhfMSArIGJfMlhfMiArIC4uLiArIGJfa1hfayQkCml0IHR1cm5zIG91dCB0aGF0IHdlIGNhbiByZWNvdmVyIHRoZSBvZGRzIGJ5IHRha2luZyB0aGUgKmV4cG9uZW50KiBvZiBib3RoIExIUyBhbmQgUkhTOgoKJCRcZnJhY3twXzF9ezEtcF8xfSA9IGVeeyhiXzAgKyBiXzFYXzEgKyBiXzJYXzIgKyAuLi4gKyBiX2tYX2spfSQkCmFuZCB0aGVuIGJ5IHNpbXBsZSBhbGdlYnJhaWMgcmVhcnJhbmdlbWVudCB3ZSBmaW5kIHRoYXQgdGhlIHByb2JhYmlsaXR5ICRwXzEkIG9mIHRoZSBvdXRjb21lICRZJCB0dXJuaW5nIG91dCBgMWAgaXM6CgokJFAoWSkgPSBwXzEgPSBcZnJhY3sxfXsxK2Veey0oYl8wICsgYl8xWF8xICsgYl8yWF8yICsgLi4uICsgYl9rWF9rKX19JCQKCk5vdywgaW1hZ2luZSB3ZSBzZXQgYSBmb2xsb3dpbmcgY3JpdGVyaW9uOiBhbnl0aW1lIHdlIGVzdGltYXRlICRwXzEkIHRvIGJlIGxhcmdlciB0aGFuIG9yIGVxdWFsIHRvICQuNSQgd2UgcHJlZGljdCB0aGF0ICRZPTEkLCBhbmQgYW55dGltZSAkcF8xIDwgLjUkIHdlIHByZWRpY3QgdGhhdCAkWT0wJC4gV2hhdCB3ZSBuZWVkIHRvIGRvIGluIG9yZGVyIHRvIGJlIGFibGUgdG8gbGVhcm4gaG93IHRvIHByZWRpY3QgJFkkIGluIHRoaXMgd2F5IGlzIHRvIGVzdGltYXRlIHRoZSBjb2VmZmljaWVudHMgJGJfMCQsICRiXzEkLCAkYl8yJCwgZXRjIGxpa2Ugd2UgZGlkIGluIHRoZSBjYXNlIG9mIGEgbGluZWFyIG1vZGVsLiBUaGUgZXN0aW1hdGlvbiBmb3IgR0xNcyBpcyBhIGJpdCBkaWZmZXJlbnQgdGhhbiB3ZSBoYXZlIGxlYXJuZWQgaW4gU2Vzc2lvbiAxNS4gQnV0IGZpcnN0IGxldCdzIHNlZSBob3cgdG8gcGVyZm9ybSBCaW5hcnkgTG9naXN0aWMgUmVncmVzc2lvbiBpbiBSLgoKIyMjIDIuIEJpbm9taWFsIExvZ2lzdGljIFJlZ3Jlc3Npb24gaW4gUgoKV2Ugd2lsbCB1c2UgdGhlIGRhdGFzZXQgZnJvbSB0aGUgVUNMQSdzIEluc3RpdHV0ZSBvZiBEaWdpdGFsIFJlc2VhcmNoIGFuZCBFZHVjYXRpb24ncyB3ZWJzaXRlIG9uIFN0YXRpc3RpY2FsIENvbnN1bHRpbmcgKHRoZXkgYWxzbyBoYXZlIGEgW25pY2UgZXhwb3NpdGlvbl0oaHR0cHM6Ly9zdGF0cy5pZHJlLnVjbGEuZWR1L3IvZGFlL2xvZ2l0LXJlZ3Jlc3Npb24vKSBvZiB0aGUgQmluYXJ5IExvZ2lzdGljIHJlZ3Jlc3Npb24pOgoKPiBBIHJlc2VhcmNoZXIgaXMgaW50ZXJlc3RlZCBpbiBob3cgdmFyaWFibGVzLCBzdWNoIGFzIEdSRSAoR3JhZHVhdGUgUmVjb3JkIEV4YW0gc2NvcmVzKSwgR1BBIChncmFkZSBwb2ludCBhdmVyYWdlKSBhbmQgcHJlc3RpZ2Ugb2YgdGhlIHVuZGVyZ3JhZHVhdGUgaW5zdGl0dXRpb24sIGVmZmVjdCBhZG1pc3Npb24gaW50byBncmFkdWF0ZSBzY2hvb2wuIFRoZSByZXNwb25zZSB2YXJpYWJsZSwgYWRtaXQvZG9u4oCZdCBhZG1pdCwgaXMgYSBiaW5hcnkgdmFyaWFibGUuIFNvdXJjZTogW1VDTEEncyBJbnN0aXR1dGUgb2YgRGlnaXRhbCBSZXNlYXJjaCBhbmQgRWR1Y2F0aW9uXShodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvci9kYWUvbG9naXQtcmVncmVzc2lvbi8pCgpgYGB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEZ9CmRhdGFTZXQgPC0gcmVhZC5jc3YoImh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9zdGF0L2RhdGEvYmluYXJ5LmNzdiIpCmhlYWQoZGF0YVNldCkKYGBgCgpJbnNwZWN0IHRoZSBkYXRhc2V0OgoKYGBge3IgZWNobyA9IFQsIG1lc3NhZ2UgPSBGfQpkaW0oZGF0YVNldCkKYGBgCgpMZXQncyBzZWUgd2hhdCBpcyBpbiBmb3IgdXM6CgpgYGB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEZ9CnN0cihkYXRhU2V0KQpgYGAKCmBgYHtyIGVjaG8gPSBULCBtZXNzYWdlID0gRn0KIyAtIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MKc3VtbWFyeShkYXRhU2V0KQpzYXBwbHkoZGF0YVNldCwgc2QpCmBgYAoKU28gd2UgbmVlZCBhIG1vZGVsIHRoYXQgcHJlZGljdHMgYGFkbWl0YCAtIGEgZGljaG90b215IC0gZnJvbSB0aGUgYGdyZWAgYW5kIGBncGFgIHNjb3JlcyBhbmQgdGhlIHJhbmtpbmcgb2YgdGhlIGVkdWNhdGlvbmFsIGluc3RpdHV0aW9uIGZvdW5kIGluIGByYW5rYC4gTm8gd29uZGVyIHRoYXQgdGhlIG1vZGVsIGNhbiBiZSB3cml0dGVuIGFzIGBhZG1pdCB+IGdyZSArIGdwYSArIHJhbmtgIGluIFI6CgpgYGB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEZ9CiMgLSByYW5rIHRvIGZhY3RvcgojIC0gUTogV2h5IGRvZXMgcmFuayBnbyB0byBmYWN0b3I/CiMgLSBBOiBEdW1teSBjb2RpbmcuLi4gUmVtZW1iZXI/CmRhdGFTZXQkcmFuayA8LSBmYWN0b3IoZGF0YVNldCRyYW5rKQpgYGAKCkhlcmUgZ29lcyB0aGUgYGdsbSgpYCBmdW5jdGlvbjoKCmBgYHtyIGVjaG8gPSBULCBtZXNzYWdlID0gRn0KIyAtIG1vZGVsOgpteWxvZ2l0IDwtIGdsbShhZG1pdCB+IGdyZSArIGdwYSArIHJhbmssCiAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhU2V0LAogICAgICAgICAgICAgICBmYW1pbHkgPSAiYmlub21pYWwiKQptb2RlbHN1bW1hcnkgPC0gc3VtbWFyeShteWxvZ2l0KQpwcmludChtb2RlbHN1bW1hcnkpCmBgYAoKQSB3b3JkIG9uIGludGVycHJldGF0aW9uIG9mIHRoZSByZXN1bHRzOgoKKyAqQ2FsbCogaXMgYWdhaW4ganVzdCB0aGUgbW9kZWwgYXMgd2UgaGF2ZSBmb3JtdWxhdGVkIGl0OworICpEZXZpYW5jZSByZXNpZHVhbHMqOiBpbiBHTE1zIHdlIGRvIG5vdCB1c2UgdGhlIHNhbWUgdHlwZSBvZiByZXNpZHVhbHMgYXMgd2UgZGlkIGluIExpbmVhciBNb2RlbHMuIFRoZXJlIGFyZSBzZXZlcmFsIHR5cGVzIG9mIHJlc2lkdWFscyBvZiB3aGljaCB0aGUgKmRldmlhbmNlIHJlc2lkdWFscyogYXJlIG1vc3Qgd2lkZWx5IGtub3duIGFuZCB1c2VkICh3aGljaCBkb2VzIG5vdCBtZWFuIHRoYXQgdGhlIHJlbWFpbmluZyB0eXBlcyBvZiByZXNpZHVhbHMgaW4gR0xNcyBhcmUgbm90IHVzZWZ1bCwgdG8gdGhlIGNvbnRyYXJ5ISkuIFdlIHdpbGwgZXhwbGFpbiB0aGUgZGV2aWFuY2UgcmVzaWR1YWxzIGxhdGVyIG9uLgorICpDb2VmZmljaWVudHMqOiBhcyBpbiBgbG0oKWAgdGhlc2UgYXJlIHRoZSBtb2RlbCBjb2VmZmljaWVudHMuIFRoZSBgeiB2YWx1ZWAgc3RhbmRzIGZvciB0aGUgKldhbGQncyB0ZXN0KiBvZiB3aGV0aGVyIHRoZSBjb2VmZmljaWVudCBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIHplcm8gKHJlbWVtYmVyIHRoYXQgd2UgaGF2ZSB1c2VkIHRoZSAqdC10ZXN0KiBpbiBMaW5lYXIgUmVncmVzc2lvbiB0byB0ZXN0IGV4YWN0bHkgdGhlIHNhbWUgaHlwb3RoZXNpcyk7IHRoZSB0ZXN0IGlzIG9idGFpbmVkIGJ5IGRpdmlkaW5nIHRoZSBjb2VmZmljaWVudCBieSBpdHMgc3RhbmRhcmQgZXJyb3IuCgoqKk4uQi4qKiBUaGVyZSBpcyBhIGJ1ZyBpbiB0aGUgV2FsZCdzIFosIGxvb2s6Cgo+IFRoZSByZWFzb24gd2h5IHRoZSBXYWxkIHN0YXRpc3RpYyBzaG91bGQgYmUgdXNlZCBjYXV0aW91c2x5IGlzIGJlY2F1c2UsIHdoZW4gdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgaXMgbGFyZ2UsIHRoZSBzdGFuZGFyZCBlcnJvciB0ZW5kcyB0byBiZWNvbWUgaW5mbGF0ZWQsIHJlc3VsdGluZyBpbiB0aGUgV2FsZCBzdGF0aXN0aWMgYmVpbmcgdW5kZXJlc3RpbWF0ZWQgKHNlZSBNZW5hcmQsIDE5OTUpLiBUaGUgaW5mbGF0aW9uIG9mIHRoZSBzdGFuZGFyZCBlcnJvciBpbmNyZWFzZXMgdGhlIHByb2JhYmlsaXR5IG9mIHJlamVjdGluZyBhIHByZWRpY3RvciBhcyBiZWluZyBzaWduaWZpY2FudCB3aGVuIGluIHJlYWxpdHkgaXQgaXMgbWFraW5nIGEgc2lnbmlmaWNhbnQKY29udHJpYnV0aW9uIHRvIHRoZSBtb2RlbCAoaS5lLiB5b3UgYXJlIG1vcmUgbGlrZWx5IHRvIG1ha2UgYSBUeXBlIElJIGVycm9yKS4gRnJvbTogQW5keSBGaWVsZCwgRElTQ09WRVJJTkcgU1RBVElTVElDUyBVU0lORyBTUFNTLCBUaGlyZCBFZGl0aW9uLCBTYWdlLgoKKyAqRGlzcGVyc2lvbiBwYXJhbWV0ZXIgZm9yIGJpbm9taWFsIGZhbWlseSB0YWtlbiB0byBiZSAxKiAtIGZvcmdldCBhYm91dCB0aGlzIHVudGlsIHdlIGxlYXJuIG1vcmUgYWJvdXQgR0xNcyBpbiB0aGUgZm9sbG93aW5nIHNlc3Npb25zLgorICpOdWxsIGFuZCBSZXNpZHVhbCBkZXZpYW5jZSo6IFJlbWVtYmVyIGhvdyB3ZSBoYXZlIHVzZWQgdGhlIG1lYW4gb2YgdGhlIG91dGNvbWUgJFkkIGFzIGEgYmFzZWxpbmUgZm9yIHRoZSBhc3Nlc3NtZW50IG9mIHRoZSBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24gbW9kZWwgaW4gU2Vzc2lvbiAxNT8gSW4gdGhlIEJpbmFyeSBMb2dpc3RpYyBSZWdyZXNzaW9uIHNldHRpbmcgdGhhdCBpcyBub3QgcG9zc2libGUgYmVjYXVzZSB0aGUgb3V0Y29tZSB2YXJpYWJsZSBpcyBiaW5hcnkuIFdoYXQgaXMgdGhlIGFwcHJvcHJpYXRlICpiYXNlbGluZSBtb2RlbCogZm9yIGEgY29tcGFyaXNvbiBvZiB0aGUgZWZmZWN0IG9mIHByZWRpY3RvcnMgaW4gQmluYXJ5IExvZ2lzdGljIFJlZ3Jlc3Npb24gdGhlbj8gV2VsbCwgd2UgY2FuIHRha2UgdGhlIHByb2JhYmlsaXR5IG9mICRZJCB0dXJuaW5nIG91dCBgMWAgYnkganVzdCBsb29raW5nIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIG91dGNvbWUgYW5kIHByZXRlbmQgdGhhdCB0aGVyZSBhcmUgbm8gcHJlZGljdG9ycyBhdCBhbGw6IHRoYXQgd291bGQgYmUgdGhlICpiYXNlbGluZSBtb2RlbCogZm9yIEJpbmFyeSBMb2dpc3RpYyBSZWdyZXNzaW9uLiBUaGUgKm51bGwgZGV2aWFuY2UqIGRlc2NyaWJlcyB0aGUgZXJyb3Igb2YgdGhlIGJhc2VsaW5lIHdoaWxlIHRoZSAqcmVzaWR1YWwgZGV2aWFuY2UqIGRlc2NyaWJlIHRoZSBlcnJvciBmcm9tIHRoZSBjdXJyZW50IG1vZGVsLiBXZSB3aWxsIGxlYXJuIGhvdyB0byB1c2UgdGhlbSB0byBhc3Nlc3MgdGhlIG92ZXJhbGwgZWZmZWN0IG9mIHRoZSBtb2RlbC4gIAorICpBSUMqOiBzaG9ydCBmb3IgdGhlIFtBa2Fpa2UgSW5mb3JtYXRpb24gQ3JpdGVyaW9uXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Ba2Fpa2VfaW5mb3JtYXRpb25fY3JpdGVyaW9uKSAodG8gYmUgZXhwbGFpbmVkIGluIHRoZSBTZXNzaW9uKS4gSXQgaXMgYSBtZWFzdXJlIG9mICpiYWRuZXNzLW9mLWZpdCo6IHRoZSBsb3dlciB0aGUgQUlDIHRoZSBiZXR0ZXIgdGhlIG1vZGVsLiAKKyAqRmlzaGVyIHNjb3JpbmcgaXRlcmF0aW9uczoqIGl0IGhhcyB0byBkbyB3aXRoIG1vZGVsIGVzdGltYXRpb24gYW5kIHdpbGwgbm90IGJlIGNvbnNpZGVyZWQgaGVyZS4KCkluIG9yZGVyIHRvIHVuZGVyc3RhbmQgcHJlY2lzZWx5IGhvdyBhIEJpbmFyeSBMb2dpc3RpYyBSZWdyZXNzaW9uIG1vZGVsIGlzIGFzc2Vzc2VkIC0gYW5kIG1vcmUgaW1wb3J0YW50bHkgKndoeSogaXQgaXMgYXNzZXNzZWQgaW4gdGhlIHdheSBpdCBpcyBhc3Nlc3NlZCAtIHdlIGZpcnN0IG5lZWQgdG8gaW50cm9kdWNlIHRoZSBjb25jZXB0IG9mICpMaWtlbGlob29kKiBpbiBQcm9iYWJpbGl0eSBUaGVvcnkuIE5vdyB0aGlzIGlzIG9uZSBwb3dlcmZ1bCBpZGVhIHRoYXQgd2UgbmVlZCB0byBkaXNjdXNzIQoKIyMjIDMuIExpa2VsaWhvb2QgYW5kIHRoZSBNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdGUgKE1MRSkKCiMjIyMgMy4xIExpa2VsaWhvb2QgRnVuY3Rpb24KCkltYWdpbmUgd2UgdG9zcyBhIGZhaXIgY29pbiB3aXRoICRwX0ggPSAuNSQgdHdpY2UgYW5kIG9ic2VydmUgdHdvIEhlYWRzLlRoZSBwcm9iYWJpbGl0eSBvZiBvYnNlcnZpbmcgdHdvIGhlYWRzIHdpdGggJHBfSCA9IC41JCBpczoKCiQkUChISHxwX0ggPSAuNSkgPSAuNSAqIC41ID0gLjVeezJ9ID0gLjI1JCQKTm93IHRha2UgYSBsb29rIGF0IHRoZSBmb2xsb3dpbmcgZnVuY3Rpb246ICRQKEhIfHBfSCkkLiBJbWFnaW5lIHRoYXQgdGhlIGRhdGEsIHRoZSByZXN1bHRzIG9mIG91ciBvYnNlcnZhdGlvbnMgLSB0aGF0IHdlIGhhdmUgc2VlbiB0d28gaGVhZHMgaW4gYSByb3cgLSBhcmUgKmZpeGVkKi4gVGhhbiAkUChISHxwX0gpJCBpcyAqYSBmdW5jdGlvbiBvZiB0aGUgcGFyYW1ldGVyKiAkcF9IJC4gSW1hZ2luZSB0aGF0IHdlIHN0YXJ0IGNoYW5naW5nIHRoZSB2YWx1ZSBvZiAkcF9IJCB3aGlsZSBrZWVwaW5nIHRoZSBkYXRhIGZpeGVkIGFuZCB3aXRoIGV2ZXJ5IGNoYW5nZSBpbiBwYXJhbWV0ZXIgd2UgY29tcHV0ZSB0aGUgdmFsdWUgb2YgJFAoSEh8cF9IKSQgYWdhaW4gYW5kIGFnYWluLiBGb3IgZXhhbXBsZSwgd2hhdCBpcyB0aGUgdmFsdWUgb2YgJFAoSEh8cF9IKSQgaWYgJHBfSCA9IC4zJD8KCiQkUChISHxwX0ggPSAuMykgPSAuMyAqIC4zID0gLjNeezJ9ID0gLjA5JCQKQW5kIHdoYXQgaWYgJHBfSCA9IC45JD8KCiQkUChISHxwX0ggPSAuOSkgPSAuOSAqIC45ID0gLjleezJ9ID0gLjgxJCQKV2UgaGF2ZSBvYnNlcnZlZCB0d28gaGVhZHM7IGluIHRoZSB1bml2ZXJzZSBvZiBvdXIgc21hbGwgc3RhdGlzdGljYWwgZXhwZXJpbWVudCB3ZSBoYXZlIGFjdHVhbGx5IG9ic2VydmVkICphbGwgaGVhZHMqLCByaWdodD8gU28sIGFzIHdlIGluY3JlYXNlIHRoZSB2YWx1ZSBvZiAkcF9IJCwgdGhlIHZhbHVlIG9mICRQKEhIfHBfSCkkIHRlbmRzIHRvIGluY3JlYXNlOiBpdCB3YXMgYC4wOWAgd2hlbiAkcF9IID0gLjMkLCB0aGVuIGAuMjVgIGZvciAkcF9IID0gLjUkLCBhbmQgZmluYWxseSBgLjgxYCBmb3IgJHBfSCA9IC45JC4gRXZlbiBpZiB3ZSBhbHJlYWR5IGtub3cgdGhhdCB0aGUgY29pbiBpcyBmYWlyIC0gaGVuY2UgJHBfSCA9IC41JCAtIHRoZSAqb2JzZXJ2ZWQgZGF0YSBpbmZvcm0gdXMqIHRoYXQgaXQgaXMgbW9yZSAqbGlrZWx5KiB0byBiZSBoaWdoZXIuCgokUChISHxwX0gpJCwgYWxzbyB3cml0dGVuIGFzICRMKHBfSHxISCkkLCByZWFkczogdGhlICoqbGlrZWxpaG9vZCoqIG9mIHRoZSBwYXJhbWV0ZXIgdmFsdWUgJHBfSCQgKmdpdmVuKiB0aGUgZGF0YSAkSEgkLiBXZSBjYW4gcGxvdCB0aGUgd2hvbGUgKipMaWtlbGlob29kIGZ1bmN0aW9uKiogZm9yIHRoaXMgZXhwZXJpbWVudCBlYXNpbHk6CgpgYGB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEZ9Cmxpa2VsaWhvb2QgPC0gZGF0YS5mcmFtZShwYXJhbWV0ZXIgPSBzZXEoLjAxLCAuOTksIGJ5ID0gLjAxKSkKbGlrZWxpaG9vZCRsaWtlbGlob29kIDwtIGxpa2VsaWhvb2QkcGFyYW1ldGVyXjIKCmdncGxvdChsaWtlbGlob29kLCAKICAgICAgIGFlcyh4ID0gcGFyYW1ldGVyLCAKICAgICAgICAgICB5ID0gbGlrZWxpaG9vZCkpICsgCiAgZ2VvbV9zbW9vdGgoc2l6ZSA9IC4yNSwgc2UgPSBGKSArIAogIGdndGl0bGUoIkxpa2VsaWhvb2QgZnVuY3Rpb24gZm9yIEhIIikgKwogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSkKYGBgCgpXaGF0IGlmIHdlIGhhdmUgb2JzZXJ2ZWQgJEhIVFRIJCBpbiBmaXZlIHRvc3Nlcz8KCmBgYHtyIGVjaG8gPSBULCBtZXNzYWdlID0gRn0KbGlrZWxpaG9vZCA8LSBkYXRhLmZyYW1lKHBhcmFtZXRlciA9IHNlcSguMDEsIC45OSwgYnkgPSAuMDEpKQoKbGlrZWxpaG9vZCRsaWtlbGlob29kIDwtIAogIGxpa2VsaWhvb2QkcGFyYW1ldGVyXjIgKiAoMS1saWtlbGlob29kJHBhcmFtZXRlcileMiAqIGxpa2VsaWhvb2QkcGFyYW1ldGVyCgpnZ3Bsb3QobGlrZWxpaG9vZCwgCiAgICAgICBhZXMoeCA9IHBhcmFtZXRlciwgCiAgICAgICAgICAgeSA9IGxpa2VsaWhvb2QpKSArIAogIGdlb21fc21vb3RoKHNpemUgPSAuMjUsIHNlID0gRikgKyAKICBnZ3RpdGxlKCJMaWtlbGlob29kIGZ1bmN0aW9uIGZvciBISFRUSCIpICsKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSkpCmBgYAoKSG93IGRpZCB3ZSBnZXQgdG8gaXQ/IExvb2ssIGlmIHRoZSBkYXRhIGFyZSAkSEhUVEgkLCB0aGVuIHRoZSBMaWtlbGlob29kIGZ1bmN0aW9uIGZvciAkcF9IID0gLjIkIG11c3QgYmU6CgokJFAoSEhUVEh8cF9IID0gLjIpID0gLjIgKiAuMiAqICgxLS4yKSAqICgxLS4yKSAqIC4yID0gLjJeezJ9ICogICgxLS4yKV57Mn0gKiAuMiA9IC4wMDUxMiQkCkxldCdzIGNoZWNrIGluIFI6CgpgYGB7ciBlY2hvID0gVH0KLjJeMiooMS0uMileMiouMgpgYGAKCkFuZCBub3cgd2UganVzdCBuZWVkIHRvIGNvbXB1dGUgdGhlIExpa2VsaWhvb2QgZnVuY3Rpb24gYWNyb3NzIHRoZSB3aG9sZSBkb21haW4gb2YgJHBfSCQuIEFzIHNpbXBsZSBhcyB0aGF0IQoKIyMjIyAzLjIgTGlrZWxpaG9vZCBpbiB0aGUgQmlub21pYWwgTG9naXN0aWMgUmVncmVzc2lvbgoKU2F5IHdlIGhhdmUgb2JzZXJ2ZWQgdGhlIGZvbGxvd2luZyBkYXRhIG9uIGhpZ2hlciBlZHVjYXRpb24gYWRtaXNzaW9uczogJEhIVEhUVEhISFQkLiBBc3N1bWUgdGhhdCB3ZSBrbm93IHRoZSBwYXJhbWV0ZXIgJHBfSCQuIFdlIGNhbiBjb21wdXRlIHRoZSBMaWtlbGlob29kIGZ1bmN0aW9uIGZyb20gdGhlIGZvbGxvd2luZyBlcXVhdGlvbjoKCiRMKHBfSHxISFRIVFRISEhUKSQgZXhhY3RseSBhcyB3ZSBkaWQgYmVmb3JlLiBOb3csIHRoaXMgaXMgdGhlIGdlbmVyYWwgZm9ybSBvZiB0aGUgQmlub21pYWwgTGlrZWxpaG9vZCAod2hlcmUgJFkkIHN0YW5kcyBmb3IgdGhlIG9ic2VydmVkIGRhdGEpOgoKJCRMKHB8WSkgPSBwXzFeeSgxLXBfMSlee24teX0kJCAKd2hlcmUgJHkkIGlzIHRoZSBudW1iZXIgb2Ygc3VjY2Vzc2VzIGFuZCAkbiQgdGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMuIEZvciBlYWNoIG9ic2VydmVkIGRhdGEgcG9pbnQgdGhlbiB3ZSBoYXZlCgokJEwocHx5X2kpID0gcF8xXnt5X2l9KDEtcF8xKV57XGJhcnt5X2l9fSQkIAp3aGVyZSAke3lfaX0kIGlzIHRoZSBvYnNlcnZlZCB2YWx1ZSBvZiB0aGUgb3V0Y29tZSwgJFkkLCBhbmQgJFxiYXJ7eV9pfSQgaXMgaXRzIGNvbXBsZW1lbnQgKGUuZy4gYDFgIGZvciBgMGAgYW5kIGAwYCBmb3IgYDFgKS4gVGhpcyBmb3JtIGp1c3QgZGV0ZXJtaW5lcyB3aGljaCB2YWx1ZSB3aWxsIGJlIHVzZWQgaW4gdGhlIGNvbXB1dGF0aW9uIG9mIHRoZSBMaWtlbGlob29kIGZ1bmN0aW9uIGF0IGVhY2ggb2JzZXJ2ZWQgZGF0YSBwb2ludDogaXQgd2lsbCBiZSBlaXRoZXIgJHBfMSQgb3IgJDEtcF8xJC4gVGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gZm9yIGEgZ2l2ZW4gdmFsdWUgb2YgJHBfMSQgZm9yIHRoZSB3aG9sZSBkYXRhc2V0IGlzIGNvbXB1dGVkIGJ5IG11bHRpcGx5aW5nIHRoZSB2YWx1ZXMgb2YgJEwocHx5X2kpJCBhY3Jvc3MgdGhlIHdob2xlIGRhdGFzZXQgKHJlbWVtYmVyIHRoYXQgbXVsdGlwbGljYXRpb24gaW4gUHJvYmFiaWxpdHkgaXMgd2hhdCBjb25qdW5jdGlvbiBpcyBpbiBMb2dpYyBhbmQgQWxnZWJyYSkuCgoqKlE6KiogQnV0Li4uIGhvdyBkbyB3ZSBnZXQgdG8gJHBfMSQsIHRoZSBwYXJhbWV0ZXIgdmFsdWUgdGhhdCB3ZSB3aWxsIHVzZSBhdCBlYWNoIGRhdGEgcG9pbnQ/CioqQToqKiBXZSB3aWxsIHNlYXJjaCB0aGUgcGFyYW1ldGVyIHNwYWNlLCBvZiBjb3Vyc2UsICRcYmV0YV8wLCBcYmV0YV8xLCAuLi4gXGJldGFfayQgb2YgbGluZWFyIGNvZWZmaWNpZW50cyBpbiBvdXIgQmluYXJ5IExvZ2lzdGljIE1vZGVsLCBjb21wdXRpbmcgJHBfMSQgZXZlcnkgdGltZSwgYW5kIGNvbXB1dGUgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gZnJvbSBpdCEgSW4gb3RoZXIgd29yZHM6IHdlIHdpbGwgc2VhcmNoIHRoZSBwYXJhbWV0ZXIgc3BhY2UgdG8gZmluZCB0aGUgY29tYmluYXRpb24gb2YgJFxiZXRhXzAsIFxiZXRhXzEsIC4uLiBcYmV0YV9rJCB0aGF0IHByb2R1Y2VzIHRoZSAqbWF4aW11bSBvZiB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbiogc2ltaWxhcmx5IGFzIHdlIGhhdmUgc2VhcmNoZWQgdGhlIHNwYWNlIG9mIGxpbmVhciBjb2VmZmljaWVudHMgdG8gZmluZCB0aGUgY29tYmluYXRpb24gdGhhdCAqbWluaW1pemVzIHRoZSBzcXVhcmVkIGVycm9yKiBpbiBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24uCgpTbyB3aGF0IGNvbWJpbmF0aW9uIG9mIHRoZSBsaW5lYXIgY29lZmZpY2llbnRzIGlzIHRoZSBiZXN0IG9uZT8KCioqSXQgaXMgdGhlIG9uZSB3aGljaCBnaXZlcyB0aGUgbWF4aW11bSBsaWtlbGlob29kLioqIFRoaXMgYXBwcm9hY2gsIGtub3duIGFzICoqTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24gKE1MRSkqKiwgc3RhbmRzIGJlaGluZCAqbWFueSogaW1wb3J0YW50IHN0YXRpc3RpY2FsIGxlYXJuaW5nIG1vZGVscy4gSXQgcHJlc2VudHMgdGhlIGNvcm5lciBzdG9uZSBvZiB0aGUgKipTdGF0aXN0aWNhbCBFc3RpbWF0aW9uIFRoZW9yeSoqLiBJdCBpcyBjb250cmFzdGVkIHdpdGggdGhlICpMZWFzdCBTcXVhcmVzIEVzdGltYXRpb24qIHRoYXQgd2UgaGF2ZSB1c2VkIHRvIGVzdGltYXRlIHRoZSBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24gbW9kZWwgaW4gU2Vzc2lvbiAxNS4KCk5vdywgdGhlcmUgaXMgYSB0ZWNobmljYWwgcHJvYmxlbSByZWxhdGVkIHRvIHRoaXMgYXBwcm9hY2guIFRvIG9idGFpbiB0aGUgbGlrZWxpaG9vZCBmb3IgdGhlIHdob2xlIGRhdGFzZXQgb25lIG5lZWRzIHRvIG11bHRpcGx5IGFzIG1hbnkgdmVyeSBzbWFsbCBudW1iZXJzIGFzIHRoZXJlIGFyZSBkYXRhIHBvaW50cy4gVGhhdCBjYW4gY2F1c2UgY29tcHV0YXRpb25hbCBwcm9ibGVtcyByZWxhdGVkIHRvIHRoZSBzbWFsbGVzdCByZWFsIG51bWJlcnMgdGhhdCBjYW4gYmUgcmVwcmVzZW50ZWQgYnkgZGlnaXRhbCBjb21wdXRlcnMuIFRoZSB3b3JrYXJvdW5kIGlzIHRvIHVzZSB0aGUgKmxvZ2FyaXRobSogb2YgbGlrZWxpaG9vZCBpbnN0ZWFkLCBrbm93biBhcyAqKmxvZy1saWtlbGlob29kKiogKCRMTCQpLgoKVGh1cywgd2hpbGUgdGhlIExpa2VsaWhvb2QgZnVuY3Rpb24gZm9yIHRoZSB3aG9sZSBkYXRhc2V0IHdvdWxkIGJlCgokJEwocHxZKSA9IFxwcm9kX3tpPTF9XntufXBfMV57eV9pfSgxLXBfMSlee1xiYXJ7eV9pfX0kJCAKdGhlIExvZy1MaWtlbGlob29kIGZ1bmN0aW9uIHdvdWxkIGJlOgoKJCRMTChwfFkpID0gXHN1bV97aT0xfV57bn0geV9pbG9nKHBfMSkrXGJhcnt5X2l9bG9nKDEtcF8xKSQkIApBbmQgZmluYWxseSBoZXJlIGlzIGhvdyB3ZSBzb2x2ZSB0aGUgQmlub21pYWwgTG9naXN0aWMgUmVncmVzc2lvbiBwcm9ibGVtOgoKKyBzZWFyY2ggdGhyb3VnaHQgdGhlIHBhcmFtZXRlciBzcGFjZSBzcGF3bmVkIGJ5IGxpbmVhciBjb2VmZmljaWVudHMgJFxiZXRhXzAsIFxiZXRhXzEsIC4uLiBcYmV0YV9rJCwKKyBwcmVkaWN0ICRwXzEkIGZyb20gdGhlIG1vZGVsIGFuZCBhIHBhcnRpY3VsYXIgY29tYmluYXRpb24gb2YgdGhlIHBhcmFtZXRlcnMsCisgY29tcHV0ZSB0aGUgdmFsdWUgb2YgdGhlIExpa2VsaWhvb2QgZnVuY3Rpb24gZm9yIHRoZSB3aG9sZSBkYXRhc2V0LAorIGZpbmQgdGhlIGNvbWJpbmF0aW9uIHRoYXQgeWllbGRzIHRoZSBtYXhpbXVtIHZhbHVlIG9mIHRoZSBMaWtlbGlob29kIGZ1bmN0aW9uLgoKVGVjaG5pY2FsbHksIGluIG9wdGltaXphdGlvbiB3ZSB3b3VsZCBub3QgZ28gZXhhY3RseSBmb3IgdGhlIG1heGltdW0gb2YgdGhlIExpa2VsaWhvb2QgZnVuY3Rpb24sIGJlY2F1c2Ugd2UgdXNlICRMTCQgaW5zdGVhZCBvZiAkTChwfFkpJC4gVGhlIHNvbHV0aW9uIGlzIHRvIG1pbmltaXplIHRoZSBuZWdhdGl2ZSAkTEwkLCBzb21ldGltZXMgd3JpdHRlbiBzaW1wbHkgYXMgJE5MTCQsIHRoZSBOZWdhdGl2ZSBMb2ctTGlrZWxpaG9vZCBmdW5jdGlvbi4KCiMjIyA0LiBBc3Nlc3NpbmcgdGhlIEJpbmFyeSBMb2dpc3RpYyBSZWdyZXNzaW9uIG1vZGVsCgpXZSB3aWxsIGJlZ2luIHdpdGggdGhlIG1vZGVsIGNvZWZmaWNpZW50cy4gQ29lZmZpY2llbnRzIGluIEJpbmFyeSBMb2dpc3RpYyBSZWdyZXNzaW9uIHRlbGwgYWJvdXQgdGhlIGNoYW5nZSBpbiB0aGUgKipsb2ctb2RkcyoqIG9mIHRoZSBvdXRjb21lIGZvciBhICpvbmUgdW5pdCBpbmNyZWFzZSogaW4gdGhlIHByZWRpY3RvciB2YXJpYWJsZToKCmBgYHtyIGVjaG8gPSBUfQptb2RlbGNvZWZzIDwtIG1vZGVsc3VtbWFyeSRjb2VmZmljaWVudHMKcHJpbnQobW9kZWxjb2VmcykKYGBgCgpUbyBleHRyYWN0IHRoZSBjb2VmZmljaWVudHMgb25seToKCmBgYHtyIGVjaG8gPSBUfQojIC0gY29lZmZpY2llbnRzIG9ubHk6Cm1jb2VmcyA8LSBjb2VmKG15bG9naXQpCm1jb2VmcwpgYGAKCk5vdywgdGhlIGxvZy1vZGRzIHNjYWxlIGlzIG9kZCBpbiBpdHNlbGYuLi4gQnV0IGlmIHRha2UgdGhlIGBleHAoKWAgb2YgdGhlIGNvZWZmaWNpZW50cyB0aGVuIHRoZWlyIGludGVycHJldGF0aW9uIGJlY29tZXM6ICoqaG93IG11Y2ggZG8gdGhlIG9kZHMgb2YgYmVpbmcgYDFgIGluc3RlYWQgb2YgYmVpbmcgYDBgIGluY3JlYXNlIHdpdGggYSB1bml0IGluY3JlYXNlIGluIHRoZSBwcmVkaWN0b3IqKi4KCmBgYHtyIGVjaG8gPSBUfQojIC0gWW91IGNhbiBhbHNvIGV4cG9uZW50aWF0ZSB0aGUgY29lZmZpY2llbnRzIGFuZCAKIyAtIGludGVycHJldCB0aGVtIGFzIGZhY3RvcnMgb2Ygb2Rkcy1yYXRpb3M6CmV4cChjb2VmKG15bG9naXQpKQpgYGAKClRoZSBvYnRhaW4gdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzOgoKYGBge3IgZWNobyA9IFQsIG1lc3NhZ2UgPSBGfQojIC0gQ29uZmlkZW5jZSBJbnRlcnZhbHMgb24gbW9kZWwgY29lZmZpY2llbnRzOgpjb25maW50KG15bG9naXQsIGxldmVsID0gLjk5KQpgYGAKCk9mIGNvdXJzZSwgYGV4cCgpYCB0byBjaGFuZ2UgdG8gdGhlIG9kZHMgc2NhbGU6CgpgYGB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEZ9CiMgLSBDb25maWRlbmNlIEludGVydmFscyBvbiBtb2RlbCBjb2VmZmljaWVudHM6CmV4cChjb25maW50KG15bG9naXQsIGxldmVsID0gLjk5KSkKYGBgCgpUaGUgbW9kZWwgbG9nLWxpa2VsaWhvb2QgYXQgdGhlIG9wdGltYWwgcGFyYW1ldGVyIHZhbHVlcyBpcyBvYnRhaW5lZCBmcm9tIGBsb2dMaWsoKWA6CgpgYGB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEZ9CiMgLSBUaGUgbW9kZWwgbGlrZWxpaG9vZCBpczoKbG9nTGlrKG15bG9naXQpCiMgLSBOb3RlOiBtb2RlbHMgdy4gbG93ZXIgbG9nTGlrZSBhcmUgYmV0dGVyCmBgYAoKTGV0J3Mgbm93IHRhbGsgYWJvdXQgKipBSUMqKiwgdGhlIEFrYWlrZSBJbmZvcm1hdGlvbiBDcml0ZXJpb24uCgpgYGB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEZ9CiMgLSBBa2Fpa2UgSW5mb3JtYXRpb24gQ3JpdGVyaW9uCiMgLSBzZWU6IGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0FrYWlrZV9pbmZvcm1hdGlvbl9jcml0ZXJpb24KbXlsb2dpdCRhaWMKIyAtIE5vdGU6IG1vZGVscyB3LiBsb3dlciBBSUMgYXJlIGJldHRlcgpgYGAKClRoZSBBSUMgaXMgY29tcHV0ZWQgaW4gdGhlIGZvbGxvd2luZyB3YXk6CgokJEFJQyA9IDJrIC0gMkxMJCQKd2hlcmUgJGskIGlzIHRoZSBudW1iZXIgb2YgcGFyYW1ldGVycyBlc3RpbWF0ZWQ6IAoKYGBge3IgZWNobyA9IFQsIG1lc3NhZ2UgPSBGfQotMiphcy5udW1lcmljKGxvZ0xpayhteWxvZ2l0KSkgKyAyKjYKYGBgCgpUaGVyZSBpcyBhbHNvIHRoZSBgQUlDKClgIGZ1bmN0aW9uOgoKYGBge3IgZWNobyA9IFQsIG1lc3NhZ2UgPSBGfQpBSUMobXlsb2dpdCkKYGBgCgpGaW5hbGx5LCBsZXQncyBleHBsYWluIHdoYXQgdGhlIGRldmlhbmNlIHJlc2lkdWFsIGlzLiBUaGUgKHNxdWFyZWQpIGRldmlhbmNlIG9mIGVhY2ggZGF0YSBwb2ludCBpcyBlcXVhbCB0byBgLTJgIHRpbWVzIHRoZSBsb2dhcml0aG0gb2YgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBpdHMgcHJlZGljdGVkIHByb2JhYmlsaXR5IGFuZCB0aGUgKmNvbXBsZW1lbnQqIG9mIGl0cyBhY3R1YWwgdmFsdWUgKGAxYCBmb3IgYSBgMGAgYW5kIGEgYDBgIGZvciBhIGAxYCkuIEFuZCB3aHkgd291bGQgYW55b25lIGNvbnN0cnVjdCBzdWNoIGEgbWVhc3VyZSBvZiBtb2RlbCBlcnJvcj8KCmBgYHtyIGVjaG8gPSBULCBtZXNzYWdlID0gRn0KIyAtIG1vZGVsIGRldmlhbmNlOiAKcHJpbnQobXlsb2dpdCRkZXZpYW5jZSkKYGBgCgpJbnRlcmVzdGluZyBlbm91Z2gsICQtMkxMJCBlcXVhbHMgdGhlIHRvdGFsIG1vZGVsIGRldmlhbmNlLi4uCgpgYGB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEZ9CmxsIDwtIGxvZ0xpayhteWxvZ2l0KQotMiphcy5udW1lcmljKGxsKSA9PSBteWxvZ2l0JGRldmlhbmNlCmBgYAoKQ2hlY2sgYWdhaW4uLi4gVGhlIHRvdGFsIGRldmlhbmNlIGZpcnN0OgoKYGBge3IgZWNobyA9IFQsIG1lc3NhZ2UgPSBGfQpkZXZpYW5jZXMgPC0gcmVzaWR1YWxzKG15bG9naXQsIAogICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiZGV2aWFuY2UiKQpzdW0oZGV2aWFuY2VzXjIpID09IG15bG9naXQkZGV2aWFuY2UKYGBgCgoqU28sIHRoZSBkZXZpYW5jZXMgZGVjb21wb3NlIHRoZSB0b3RhbCBtb2RlbCBsaWtlbGlob29kIHNpbWlsYXJseSBhcyByZXNpZHVhbHMgaW4gTGluZWFyIFJlZ3Jlc3Npb24gZGVjb21wb3NlIHRoZSB0b3RhbCBtb2RlbCBlcnJvci4uLioKCmBgYHtyIGVjaG8gPSBULCBtZXNzYWdlID0gRn0Kc3VtKGRldmlhbmNlc14yKSA9PSAtMiphcy5udW1lcmljKGxsKQpgYGAKCkZpbmFsbHksIGRvZXMgdGhlIG1vZGVsIGluIGl0c2VsZiBoYXZlIGFueSBlZmZlY3Q/IERpZCB3ZSBnYWluIGFueXRoaW5nIGZyb20gaW50cm9kdWNpbmcgdGhlIHByZWRpY3RvcnM/IFRoZSBmb2xsb3dpbmcgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByZXNpZHVhbCBhbmQgbnVsbCBkZXZpYW5jZSBmb2xsb3dzIGEgJFxjaGleMiQtZGlzdHJpYnV0aW9uLi4uCgpgYGB7ciBlY2hvID0gVH0KIyAtIENvbXBhcmlzb24gdG8gYSBzby1jYWxsZWQgTnVsbCBtb2RlbCAoaW50ZXJjZXB0IG9ubHkpCiMgLSBUaGUgZm9sbG93aW5nIGlzIENoaS1TcXVhcmUgZGlzdHJpYnV0ZWQuLi4KZGV2IDwtIG15bG9naXQkbnVsbC5kZXZpYW5jZSAtIG15bG9naXQkZGV2aWFuY2UKcHJpbnQoZGV2KQpgYGAKCi4uLiB3aXRoIHRoZSBgZGZzYCBudW1iZXIgb2YgZGVncmVlcyBvZiBmcmVlZG9tOgoKYGBge3IgZWNobyA9IFR9CmRmcyA8LSBteWxvZ2l0JGRmLm51bGwgLSBteWxvZ2l0JGRmLnJlc2lkdWFsCnByaW50KGRmcykKYGBgCgpBbmQgdGhlLCBhcyBldmVyOiBob3cgZXh0cmVtZSB0aGUgcHJvYmFiaWxpdHkgb2Ygb2JzZXJ2aW5nIHRoaXMgaXMgdG8gY2hlY2sgZm9yIHRoZSBUeXBlIEkgRXJyb3I6CgpgYGB7ciBlY2hvID0gVH0KcGNoaXNxKGRldiwgZGZzLCBsb3dlci50YWlsID0gRkFMU0UpCmBgYAoKKioqCgojIyMgRnVydGhlciBSZWFkaW5ncwoKKyBbQW5keSBGaWVsZCwgSmVyZW15IE1pbGVzICYgWm/DqyBGaWVsZCwgRGlzY292ZXJpbmcgU3RhdGlzdGljcyBVc2luZyBSLCBTQUdFIFB1Ymxpc2hpbmcsIENoYXB0ZXIgOC4gTG9naXN0aWMgUmVncmVzc2lvbl0oaHR0cHM6Ly91ay5zYWdlcHViLmNvbS9lbi1nYi9ldXIvZGlzY292ZXJpbmctc3RhdGlzdGljcy11c2luZy1yL2Jvb2syMzYwNjcpCgorIFtQZXRlciBPbGl2ZXIgQ2F5YV0oaHR0cHM6Ly9tZWRpdW0uY29tL3BldGUtY2F5YS9pbXBsZW1lbnRpbmctYmluYXJ5LWxvZ2lzdGljLXJlZ3Jlc3Npb24taW4tci1lM2E2ZjU5YWUyOTQpCgorIFtKZWZmIFdlYmIsIENoYXB0ZXIgOCBMb2dpc3RpYyBSZWdyZXNzaW9uIGZyb20gQ291cnNlIE5vdGVzIGZvciBJUyA2NDg5LCBTdGF0aXN0aWNzIGFuZCBQcmVkaWN0aXZlIEFuYWx5dGljc10oaHR0cHM6Ly9ib29rZG93bi5vcmcvamVmZnRlbXBsZXdlYmIvSVMtNjQ4OS9sb2dpc3RpYy1yZWdyZXNzaW9uLmh0bWwpCgorIFtTVEhEQSwgTG9naXN0aWMgUmVncmVzc2lvbiBBc3N1bXB0aW9ucyBhbmQgRGlhZ25vc3RpY3MgaW4gUl0oaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC9hcnRpY2xlcy8zNi1jbGFzc2lmaWNhdGlvbi1tZXRob2RzLWVzc2VudGlhbHMvMTQ4LWxvZ2lzdGljLXJlZ3Jlc3Npb24tYXNzdW1wdGlvbnMtYW5kLWRpYWdub3N0aWNzLWluLXIvKQoKKyBbQmVuIEhvcnZhdGgsIERlcml2aW5nIExvZ2lzdGljIFJlZ3Jlc3Npb25dKGh0dHBzOi8vcnB1YnMuY29tL2JlbmhvcnZhdGgvbG9naXN0aWNfcmVncmVzc2lvbikKCgojIyMgUiBNYXJrZG93bgoKKyBbUiBNYXJrZG93bl0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vKSBpcyB3aGF0IEkgaGF2ZSB1c2VkIHRvIHByb2R1Y2UgdGhpcyBiZWF1dGlmdWwgTm90ZWJvb2suIFdlIHdpbGwgbGVhcm4gbW9yZSBhYm91dCBpdCBuZWFyIHRoZSBlbmQgb2YgdGhlIGNvdXJzZSwgYnV0IGlmIHlvdSBhbHJlYWR5IGZlZWwgcmVhZHkgdG8gZGl2ZSBkZWVwLCBoZXJlJ3MgYSBib29rOiBbUiBNYXJrZG93bjogVGhlIERlZmluaXRpdmUgR3VpZGUsIFlpaHVpIFhpZSwgSi4gSi4gQWxsYWlyZSwgR2FycmV0dCBHcm9sZW11bmRzLl0oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykgCgoqKioKR29yYW4gUy4gTWlsb3Zhbm92acSHCgpEYXRhS29sZWt0aXYsIDIwMjAvMjEKCmNvbnRhY3Q6IGdvcmFuLm1pbG92YW5vdmljQGRhdGFrb2xla3Rpdi5jb20KCiFbXSguLi9faW1nL0RLX0xvZ29fMTAwLnBuZykKCioqKgpMaWNlbnNlOiBbR1BMdjNdKGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy9ncGwtMy4wLnR4dCkKVGhpcyBOb3RlYm9vayBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uClRoaXMgTm90ZWJvb2sgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgpZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhbG9uZyB3aXRoIHRoaXMgTm90ZWJvb2suIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi4KCioqKgoK