In this lab, we explore the resampling techniques covered in this chapter. Some of the commands in this lab may take a while to run on your computer.
The Validation Set Approach
We explore the use of the validation set approach in order to estimate the test error rates that result from fitting various linear models on the Auto data set.
Before we begin, we use the set.seed()
function in order to set a seed for R
’s random number generator, so that the reader of this book will obtain precisely the same results as those shown below. It is generally a good idea to set a random seed when performing an analysis such as cross-validation that contains an element of randomness, so that the results obtained can be reproduced precisely at a later time.
We begin by using the sample()
function to split the set of observations into two halves, by selecting a random subset of 196 observations out of the original 392 observations. We refer to these observations as the training set.
#load ISLR library to access Auto data
library(ISLR)
#set the starting point for the random number generator to aide reproducibility
set.seed(1)
#create vector of random 196 random row numbers to help split training and test datasets
train=sample(392,196)
(Here we use a shortcut in the sample command; see ?sample
for details.) We then use the subset
option in lm()
to fit a linear regression using only the observations corresponding to the training set.
#fit simple linear regression model using horsepower against miles per gallon
lm.fit=lm(mpg~horsepower,data=Auto ,subset=train)
We now use the predict()
function to estimate the response for all 392 observations, and we use the mean()
function to calculate the MSE of the 196 observations in the validation set. Note that the -train
index below selects only the observations that are not in the training set.
#attach the Auto data for easier syntax access to variables
attach(Auto)
#calculate test mean squared error
mean((mpg-predict(lm.fit,Auto))[-train ]^2)
[1] 23.26601
Therefore, the estimated test MSE for the linear regression fit is 26.14. We can use the poly()
function to estimate the test error for the quadratic and cubic regressions.
#fit linear regression model including a horsepower squared term
lm.fit2=lm(mpg~poly(horsepower,2), data=Auto, subset=train)
#calculate test mean squared error
mean((mpg-predict(lm.fit2,Auto ))[- train]^2)
[1] 18.71646
#fit linear regression model including a horsepower cubed term
lm.fit3=lm(mpg~poly(horsepower,3), data=Auto, subset=train)
#calculate test mean squared error
mean((mpg-predict(lm.fit3,Auto))[-train ]^2)
[1] 18.79401
Using this split of the observations into a training set and a validation set, we find that the validation set error rates for the models with linear, quadratic, and cubic terms are 23.30, 18.90, and 19.26, respectively.
These results are consistent with our previous findings: a model that predicts mpg
using a quadratic function of horsepower
performs better than a model that involves only a linear function of horsepower
, and there is little evidence in favor of a model that uses a cubic function of horsepower
Leave-One-Out Cross-Validation
The LOOCV estimate can be automatically computed for any generalized linear model using the glm()
and cv.glm()
functions. In the lab for Chapter 4, we used the glm()
function to perform logistic regression by passing in the family="binomial"
argument. But if we use glm()
to fit a model without passing in the family argument, then it performs linear regression, just like the lm()
function. So for instance,
#fit simple linear regression using the glm function
glm.fit=glm(mpg~horsepower ,data=Auto)
#examine the coefficients
coef(glm.fit)
(Intercept) horsepower
39.9358610 -0.1578447
and
#fit simple linear regression using the glm function
lm.fit=lm(mpg~horsepower ,data=Auto)
#examine the coefficients
coef(lm.fit)
(Intercept) horsepower
39.9358610 -0.1578447
yield identical linear regression models. In this lab, we will perform linear regression using the glm(
) function rather than the lm(
) function because the former can be used together with cv.glm()
. The cv.glm()
function is part of the boot
library.
#load the boot library to access the cv.glm function
library(boot)
#fit simple linear regression using the glm function
glm.fit=glm(mpg~horsepower ,data=Auto)
#calculate the cross-validation error of the linear model
cv.err=cv.glm(Auto ,glm.fit)
#print out the cross-validation estimates
cv.err$delta
[1] 24.23151 24.23114
The cv.glm()
function produces a list with several components. The two numbers in the delta vector contain the cross-validation results. In this case the numbers are identical (up to two decimal places) and correspond to the LOOCV statistic given in (5.1). Below, we discuss a situation in which the two numbers differ. Our cross-validation estimate for the test error is approximately 24.23.
We can repeat this procedure for increasingly complex polynomial fits. To automate the process, we use the for()
function to initiate a for loop which iteratively fits polynomial regressions for polynomials of order i = 1 to i = 5, computes the associated cross-validation error, and stores it in the ith element of the vector cv.error
. We begin by initializing the vector. This command will likely take a couple of minutes to run.
#initiate an empty vector to store computed cross-validation errors
cv.error=rep(0,5)
#fit five regressions against miles per gallon incrementing the power for the horsepower variable
for (i in 1:5){ #set up for loop to increment from 1 to five
glm.fit=glm(mpg~poly(horsepower ,i),data=Auto) #fit the linear regression model
cv.error[i]=cv.glm(Auto ,glm.fit)$delta [1] #calculate the cross validation error and store the result in our cv.error vector
}
cv.error #print out the resulting cross validation error
[1] 24.23151 19.24821 19.33498 19.42443 19.03321
As in Figure 5.4, we see a sharp drop in the estimated test MSE between the linear and quadratic fits, but then no clear improvement from using higher-order polynomials.
k-Fold Cross-Validation
The cv.glm()
function can also be used to implement k-fold CV. Below we use k = 10, a common choice for k, on the Auto
data set. We once again set a random seed and initialize a vector in which we will store the CV errors corresponding to the polynomial fits of orders one to ten.
#initialize the random number generator to aide reproducibility
set.seed(17)
cv.error.10=rep(0,10) #initiate an empty vector to store computed cross-validation errors
for (i in 1:10){ #set up for loop to increment from 1 to five
glm.fit=glm(mpg~poly(horsepower ,i),data=Auto) #fit the linear regression model
cv.error.10[i]=cv.glm(Auto,glm.fit,K=10)$delta[1] #calculate the cross validation error and store the result in our cv.error vector
}
cv.error.10 #print out the resulting cross validation error
[1] 24.27207 19.26909 19.34805 19.29496 19.03198 18.89781 19.12061 19.14666
[9] 18.87013 20.95520
Notice that the computation time is much shorter than that of LOOCV. (In principle, the computation time for LOOCV for a least squares linear model should be faster than for k-fold CV, due to the availability of the formula (5.2) for LOOCV; however, unfortunately the cv.glm()
function does not make use of this formula.) We still see little evidence that using cubic or higher-order polynomial terms leads to lower test error than simply using a quadratic fit.
We saw in Section 5.3.2 that the two numbers associated with delta
are essentially the same when LOOCV is performed. When we instead perform k-fold CV, then the two numbers associated with delta differ slightly. The first is the standard k-fold CV estimate, as in (5.3). The second is a biascorrected version. On this data set, the two estimates are very similar to each other.
The Bootstrap
We illustrate the use of the bootstrap in the simple example of Section 5.2, as well as on an example involving estimating the accuracy of the linear regression model on the Auto
data set.
Estimating the Accuracy of a Statistic of Interest
One of the great advantages of the bootstrap approach is that it can be applied in almost all situations. No complicated mathematical calculations are required. Performing a bootstrap analysis in R
entails only two steps. First, we must create a function that computes the statistic of interest. Second, we use the boot()
function, which is part of the boot
library, to perform the bootstrap by repeatedly sampling observations from the data set with replacement.
The Portfolio
data set in the ISLR
package is described in Section 5.2. To illustrate the use of the bootstrap on this data, we must first create a function, alpha.fn()
, which takes as input the \((X,Y)\) data as well as a vector indicating which observations should be used to estimate \(\alpha\). The function then outputs the estimate for \(\alpha\) based on the selected observations.
#create a function for alpha statistic which compares its variance to the overall covariance between two variables
alpha.fn=function (data ,index){ #initialize function to accept a data object and index object
X=data$X[index] #assign X the value of the data object subsetted on the X variable for the index observations
Y=data$Y[index] #assign Y the value of the data object subsetted on the X variable for the index observations
return ((var(Y)-cov(X,Y))/(var(X)+var(Y)-2*cov(X,Y))) #calculate the statistic
}
This function returns, or outputs, an estimate for \(\alpha\) based on applying (5.7) to the observations indexed by the argument index
. For instance, the following command tells R
to estimate \(\alpha\) using all 100 observations.
alpha.fn(Portfolio ,1:100) #test the function on the Portfolio data set using the numbers 1 to 100 as the index
[1] 0.5758321
The next command uses the sample()
function to randomly select 100 observations from the range 1 to 100, with replacement. This is equivalent to constructing a new bootstrap data set and recomputing \(\hat{\alpha}\) based on the new data set.
set.seed(1) #initialize the random number generator to aide reproducibility
alpha.fn(Portfolio ,sample (100,100, replace=T)) #test the function on the Portfolio data set using 100 randomly selected observations as the index
[1] 0.7368375
We can implement a bootstrap analysis by performing this command many times, recording all of the corresponding estimates for \(\alpha\), and computing the resulting standard deviation. However, the boot()
function automates this approach. Below we produce \(R\) = 1,000 bootstrap estimates for \(\alpha\).
#automate the bootstrap analysis using the boot function
boot(Portfolio,alpha.fn,R=1000)
ORDINARY NONPARAMETRIC BOOTSTRAP
Call:
boot(data = Portfolio, statistic = alpha.fn, R = 1000)
Bootstrap Statistics :
original bias std. error
t1* 0.5758321 -0.001695873 0.09366347
The final output shows that using the original data, \(\hat{\alpha}\) = 0.5758, and that the bootstrap estimate for \(SE(\hat{\alpha})\) is 0.0937.
Estimating the Accuracy of a Linear Regression Model
The bootstrap approach can be used to assess the variability of the coefficient estimates and predictions from a statistical learning method. Here we use the bootstrap approach in order to assess the variability of the estimates for \(\beta_0\) and \(\beta_1\), the intercept and slope terms for the linear regression model that uses horsepower
to predict mpg
in the Auto
data set. We will compare the estimates obtained using the bootstrap to those obtained using the formulas for \(SE(\hat{\beta_0})\) and \(SE(\hat{\beta_1})\) described in Section 3.1.2.
We first create a simple function, boot.fn()
, which takes in the Auto
data set as well as a set of indices for the observations, and returns the intercept and slope estimates for the linear regression model. We then apply this function to the full set of 392 observations in order to compute the estimates of \(\beta_0\) and \(\beta_0\) on the entire data set using the usual linear regression coefficient estimate formulas from Chapter 3. Note that we do not need the {
and }
at the beginning and end of the function because it is only one line long.
# create the boot.fn function to estimate the errors associated with the coefficients of a linear model
boot.fn=function (data ,index) #initialize function to accept a data and index object
return(coef(lm(mpg~horsepower ,data=data , subset=index))) # grab the coefficients of the linear model
boot.fn(Auto,1:392) #run the function on the Auto data
(Intercept) horsepower
39.9358610 -0.1578447
The boot.fn()
function can also be used in order to create bootstrap estimates for the intercept and slope terms by randomly sampling from among the observations with replacement. Here we give two examples.
set.seed(1) #initialize the random number generator to aide reproducibility
boot.fn(Auto ,sample (392,392, replace=T)) #run the function on the random permutations of observations in the Auto data
(Intercept) horsepower
40.3404517 -0.1634868
boot.fn(Auto ,sample (392,392, replace=T)) #run the function on the random permutations of observations in the Auto data again
(Intercept) horsepower
40.1186906 -0.1577063
Next, we use the boot()
function to compute the standard errors of 1,000 bootstrap estimates for the intercept and slope terms.
boot(Auto ,boot.fn ,1000) #compute the standard errors of 1,000 bootstrap estimates of the linear model coefficients using the boot function
ORDINARY NONPARAMETRIC BOOTSTRAP
Call:
boot(data = Auto, statistic = boot.fn, R = 1000)
Bootstrap Statistics :
original bias std. error
t1* 39.9358610 0.0544513229 0.841289790
t2* -0.1578447 -0.0006170901 0.007343073
This indicates that the bootstrap estimate for \(SE(\hat{\beta}_0)\) is 0.8412, and that the bootstrap estimate for \(SE(\hat{\beta}_1)\) is 0.0073. As discussed in Section 3.1.2, standard formulas can be used to compute the standard errors for the regression coefficients in a linear model. These can be obtained using the summary()
function.
#Get the computed estimates, standard errors, and associated statistics
summary(lm(mpg~horsepower ,data=Auto))$coef
Estimate Std. Error t value Pr(>|t|)
(Intercept) 39.9358610 0.717498656 55.65984 1.220362e-187
horsepower -0.1578447 0.006445501 -24.48914 7.031989e-81
The standard error estimates for \(\hat{\beta}_0\) and \(\hat{\beta}_1\) obtained using the formulas from Section 3.1.2 are 0.717 for the intercept and 0.0064 for the slope. Interestingly, these are somewhat different from the estimates obtained using the bootstrap. Does this indicate a problem with the bootstrap? In fact, it suggests the opposite. Recall that the standard formulas given in Equation 3.8 on page 66 rely on certain assumptions. For example, they depend on the unknown parameter \(\sigma^2\), the noise variance. We then estimate \(\sigma^2\) using the RSS. Now although the formula for the standard errors do not rely on the linear model being correct, the estimate for \(\sigma^2\) does. We see in Figure 3.8 on page 91 that there is a non-linear relationship in the data, and so the residuals from a linear fit will be inflated, and so will \(\hat{\sigma}^2\). Secondly, the standard formulas assume (somewhat unrealistically) that the \(x_i\) are fixed, and all the variability comes from the variation in the errors \(\epsilon_i\). The bootstrap approach does not rely on any of these assumptions, and so it is likely giving a more accurate estimate of the standard errors of \(\hat{\beta}_0\) and \(\hat{\beta}_1\) than is the summary()
function.
Below we compute the bootstrap standard error estimates and the standard linear regression estimates that result from fitting the quadratic model to the data. Since this model provides a good fit to the data (Figure 3.8), there is now a better correspondence between the bootstrap estimates and the standard estimates of \(SE(\hat{\beta}_0)\), \(SE(\hat{\beta}_1)\) and \(SE(\hat{\beta}_2)\).
#compute the bootstrap standard error estimates and the standard linear regression estimates
boot.fn=function (data ,index) #initialize boot.fn function to take data and index objects
coefficients(lm(mpg~horsepower +I(horsepower^2),data=data , subset=index)) #estimate the coefficients of a linear model
set.seed(1) #initialize the random number generator to aide reproducibility
boot(Auto ,boot.fn ,1000) #run the boot.fn function 1000 times
ORDINARY NONPARAMETRIC BOOTSTRAP
Call:
boot(data = Auto, statistic = boot.fn, R = 1000)
Bootstrap Statistics :
original bias std. error
t1* 56.900099702 3.511640e-02 2.0300222526
t2* -0.466189630 -7.080834e-04 0.0324241984
t3* 0.001230536 2.840324e-06 0.0001172164
summary(lm(mpg~horsepower +I(horsepower^2),data=Auto))$coef #compare the bootstrapped results with the statistics calculated in R
Estimate Std. Error t value Pr(>|t|)
(Intercept) 56.900099702 1.8004268063 31.60367 1.740911e-109
horsepower -0.466189630 0.0311246171 -14.97816 2.289429e-40
I(horsepower^2) 0.001230536 0.0001220759 10.08009 2.196340e-21
LS0tDQp0aXRsZTogIkNyb3NzLVZhbGlkYXRpb24gYW5kIHRoZSBCb290c3RyYXAiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkNCmBgYA0KDQpJbiB0aGlzIGxhYiwgd2UgZXhwbG9yZSB0aGUgcmVzYW1wbGluZyB0ZWNobmlxdWVzIGNvdmVyZWQgaW4gdGhpcyBjaGFwdGVyLiBTb21lIG9mIHRoZSBjb21tYW5kcyBpbiB0aGlzIGxhYiBtYXkgdGFrZSBhIHdoaWxlIHRvIHJ1biBvbiB5b3VyIGNvbXB1dGVyLg0KDQojIyMgVGhlIFZhbGlkYXRpb24gU2V0IEFwcHJvYWNoDQpXZSBleHBsb3JlIHRoZSB1c2Ugb2YgdGhlIHZhbGlkYXRpb24gc2V0IGFwcHJvYWNoIGluIG9yZGVyIHRvIGVzdGltYXRlIHRoZSB0ZXN0IGVycm9yIHJhdGVzIHRoYXQgcmVzdWx0IGZyb20gZml0dGluZyB2YXJpb3VzIGxpbmVhciBtb2RlbHMgb24gdGhlIEF1dG8gZGF0YSBzZXQuDQoNCkJlZm9yZSB3ZSBiZWdpbiwgd2UgdXNlIHRoZSBgc2V0LnNlZWQoKWAgZnVuY3Rpb24gaW4gb3JkZXIgdG8gc2V0IGEgc2VlZCBmb3IgYFJgJ3MgcmFuZG9tIG51bWJlciBnZW5lcmF0b3IsIHNvIHRoYXQgdGhlIHJlYWRlciBvZiB0aGlzIGJvb2sgd2lsbCBvYnRhaW4gcHJlY2lzZWx5IHRoZSBzYW1lIHJlc3VsdHMgYXMgdGhvc2Ugc2hvd24gYmVsb3cuIEl0IGlzIGdlbmVyYWxseSBhIGdvb2QgaWRlYSB0byBzZXQgYSByYW5kb20gc2VlZCB3aGVuIHBlcmZvcm1pbmcgYW4gYW5hbHlzaXMgc3VjaCBhcyBjcm9zcy12YWxpZGF0aW9uIHRoYXQgY29udGFpbnMgYW4gZWxlbWVudCBvZiByYW5kb21uZXNzLCBzbyB0aGF0IHRoZSByZXN1bHRzIG9idGFpbmVkIGNhbg0KYmUgcmVwcm9kdWNlZCBwcmVjaXNlbHkgYXQgYSBsYXRlciB0aW1lLg0KDQpXZSBiZWdpbiBieSB1c2luZyB0aGUgYHNhbXBsZSgpYCBmdW5jdGlvbiB0byBzcGxpdCB0aGUgc2V0IG9mIG9ic2VydmF0aW9ucyBpbnRvIHR3byBoYWx2ZXMsIGJ5IHNlbGVjdGluZyBhIHJhbmRvbSBzdWJzZXQgb2YgMTk2IG9ic2VydmF0aW9ucyBvdXQgb2YgdGhlIG9yaWdpbmFsIDM5MiBvYnNlcnZhdGlvbnMuIFdlIHJlZmVyIHRvIHRoZXNlIG9ic2VydmF0aW9ucyBhcyB0aGUgdHJhaW5pbmcgc2V0Lg0KDQpgYGB7cn0NCiNsb2FkIElTTFIgbGlicmFyeSB0byBhY2Nlc3MgQXV0byBkYXRhDQpsaWJyYXJ5KElTTFIpDQojc2V0IHRoZSBzdGFydGluZyBwb2ludCBmb3IgdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yIHRvIGFpZGUgcmVwcm9kdWNpYmlsaXR5DQpzZXQuc2VlZCgxKQ0KI2NyZWF0ZSB2ZWN0b3Igb2YgcmFuZG9tIDE5NiByYW5kb20gcm93IG51bWJlcnMgdG8gaGVscCBzcGxpdCB0cmFpbmluZyBhbmQgdGVzdCBkYXRhc2V0cw0KdHJhaW49c2FtcGxlKDM5MiwxOTYpDQpgYGANCg0KKEhlcmUgd2UgdXNlIGEgc2hvcnRjdXQgaW4gdGhlIHNhbXBsZSBjb21tYW5kOyBzZWUgYD9zYW1wbGVgIGZvciBkZXRhaWxzLikgV2UgdGhlbiB1c2UgdGhlIGBzdWJzZXRgIG9wdGlvbiBpbiBgbG0oKWAgdG8gZml0IGEgbGluZWFyIHJlZ3Jlc3Npb24gdXNpbmcgb25seSB0aGUgb2JzZXJ2YXRpb25zIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHRyYWluaW5nIHNldC4NCg0KYGBge3J9DQojZml0IHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBob3JzZXBvd2VyIGFnYWluc3QgbWlsZXMgcGVyIGdhbGxvbg0KbG0uZml0PWxtKG1wZ35ob3JzZXBvd2VyLGRhdGE9QXV0byAsc3Vic2V0PXRyYWluKQ0KYGBgDQoNCldlIG5vdyB1c2UgdGhlIGBwcmVkaWN0KClgIGZ1bmN0aW9uIHRvIGVzdGltYXRlIHRoZSByZXNwb25zZSBmb3IgYWxsIDM5MiBvYnNlcnZhdGlvbnMsIGFuZCB3ZSB1c2UgdGhlIGBtZWFuKClgIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgTVNFIG9mIHRoZSAxOTYgb2JzZXJ2YXRpb25zIGluIHRoZSB2YWxpZGF0aW9uIHNldC4gTm90ZSB0aGF0IHRoZSBgLXRyYWluYCBpbmRleCBiZWxvdyBzZWxlY3RzIG9ubHkgdGhlIG9ic2VydmF0aW9ucyB0aGF0IGFyZSBub3QgaW4gdGhlIHRyYWluaW5nIHNldC4NCg0KYGBge3J9DQojYXR0YWNoIHRoZSBBdXRvIGRhdGEgZm9yIGVhc2llciBzeW50YXggYWNjZXNzIHRvIHZhcmlhYmxlcw0KYXR0YWNoKEF1dG8pDQojY2FsY3VsYXRlIHRlc3QgbWVhbiBzcXVhcmVkIGVycm9yDQptZWFuKChtcGctcHJlZGljdChsbS5maXQsQXV0bykpWy10cmFpbiBdXjIpDQpgYGANCg0KVGhlcmVmb3JlLCB0aGUgZXN0aW1hdGVkIHRlc3QgTVNFIGZvciB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gZml0IGlzIDI2LjE0LiBXZSBjYW4gdXNlIHRoZSBgcG9seSgpYCBmdW5jdGlvbiB0byBlc3RpbWF0ZSB0aGUgdGVzdCBlcnJvciBmb3IgdGhlIHF1YWRyYXRpYyBhbmQgY3ViaWMgcmVncmVzc2lvbnMuDQoNCmBgYHtyfQ0KI2ZpdCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBpbmNsdWRpbmcgYSBob3JzZXBvd2VyIHNxdWFyZWQgdGVybQ0KbG0uZml0Mj1sbShtcGd+cG9seShob3JzZXBvd2VyLDIpLCBkYXRhPUF1dG8sIHN1YnNldD10cmFpbikNCiNjYWxjdWxhdGUgdGVzdCBtZWFuIHNxdWFyZWQgZXJyb3INCm1lYW4oKG1wZy1wcmVkaWN0KGxtLmZpdDIsQXV0byApKVstIHRyYWluXV4yKQ0KI2ZpdCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBpbmNsdWRpbmcgYSBob3JzZXBvd2VyIGN1YmVkIHRlcm0NCmxtLmZpdDM9bG0obXBnfnBvbHkoaG9yc2Vwb3dlciwzKSwgZGF0YT1BdXRvLCBzdWJzZXQ9dHJhaW4pDQojY2FsY3VsYXRlIHRlc3QgbWVhbiBzcXVhcmVkIGVycm9yDQptZWFuKChtcGctcHJlZGljdChsbS5maXQzLEF1dG8pKVstdHJhaW4gXV4yKQ0KYGBgDQoNClVzaW5nIHRoaXMgc3BsaXQgb2YgdGhlIG9ic2VydmF0aW9ucyBpbnRvIGEgdHJhaW5pbmcgc2V0IGFuZCBhIHZhbGlkYXRpb24gc2V0LCB3ZSBmaW5kIHRoYXQgdGhlIHZhbGlkYXRpb24gc2V0IGVycm9yIHJhdGVzIGZvciB0aGUgbW9kZWxzIHdpdGggbGluZWFyLCBxdWFkcmF0aWMsIGFuZCBjdWJpYyB0ZXJtcyBhcmUgMjMuMzAsIDE4LjkwLCBhbmQgMTkuMjYsIHJlc3BlY3RpdmVseS4NCg0KVGhlc2UgcmVzdWx0cyBhcmUgY29uc2lzdGVudCB3aXRoIG91ciBwcmV2aW91cyBmaW5kaW5nczogYSBtb2RlbCB0aGF0IHByZWRpY3RzIGBtcGdgIHVzaW5nIGEgcXVhZHJhdGljIGZ1bmN0aW9uIG9mIGBob3JzZXBvd2VyYCBwZXJmb3JtcyBiZXR0ZXIgdGhhbiBhIG1vZGVsIHRoYXQgaW52b2x2ZXMgb25seSBhIGxpbmVhciBmdW5jdGlvbiBvZiBgaG9yc2Vwb3dlcmAsIGFuZCB0aGVyZSBpcyBsaXR0bGUgZXZpZGVuY2UgaW4gZmF2b3Igb2YgYSBtb2RlbCB0aGF0IHVzZXMgYSBjdWJpYyBmdW5jdGlvbiBvZiBgaG9yc2Vwb3dlcmANCg0KDQojIyMgTGVhdmUtT25lLU91dCBDcm9zcy1WYWxpZGF0aW9uDQpUaGUgTE9PQ1YgZXN0aW1hdGUgY2FuIGJlIGF1dG9tYXRpY2FsbHkgY29tcHV0ZWQgZm9yIGFueSBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWwgdXNpbmcgdGhlIGBnbG0oKWAgYW5kIGBjdi5nbG0oKWAgZnVuY3Rpb25zLiBJbiB0aGUgbGFiIGZvciBDaGFwdGVyIDQsIHdlIHVzZWQgdGhlIGBnbG0oKWAgZnVuY3Rpb24gdG8gcGVyZm9ybSBsb2dpc3RpYyByZWdyZXNzaW9uIGJ5IHBhc3NpbmcgaW4gdGhlIGBmYW1pbHk9ImJpbm9taWFsImAgYXJndW1lbnQuIEJ1dCBpZiB3ZSB1c2UgYGdsbSgpYCB0byBmaXQgYSBtb2RlbCB3aXRob3V0IHBhc3NpbmcgaW4gdGhlIGZhbWlseSBhcmd1bWVudCwgdGhlbiBpdCBwZXJmb3JtcyBsaW5lYXIgcmVncmVzc2lvbiwganVzdCBsaWtlIHRoZSBgbG0oKWAgZnVuY3Rpb24uIFNvIGZvciBpbnN0YW5jZSwNCg0KYGBge3J9DQojZml0IHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiB1c2luZyB0aGUgZ2xtIGZ1bmN0aW9uDQpnbG0uZml0PWdsbShtcGd+aG9yc2Vwb3dlciAsZGF0YT1BdXRvKQ0KI2V4YW1pbmUgdGhlIGNvZWZmaWNpZW50cw0KY29lZihnbG0uZml0KQ0KYGBgDQoNCmFuZA0KDQpgYGB7cn0NCiNmaXQgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIHVzaW5nIHRoZSBnbG0gZnVuY3Rpb24NCmxtLmZpdD1sbShtcGd+aG9yc2Vwb3dlciAsZGF0YT1BdXRvKQ0KI2V4YW1pbmUgdGhlIGNvZWZmaWNpZW50cw0KY29lZihsbS5maXQpDQpgYGANCg0KeWllbGQgaWRlbnRpY2FsIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscy4gSW4gdGhpcyBsYWIsIHdlIHdpbGwgcGVyZm9ybSBsaW5lYXIgcmVncmVzc2lvbiB1c2luZyB0aGUgYGdsbShgKSBmdW5jdGlvbiByYXRoZXIgdGhhbiB0aGUgYGxtKGApIGZ1bmN0aW9uIGJlY2F1c2UgdGhlIGZvcm1lciBjYW4gYmUgdXNlZCB0b2dldGhlciB3aXRoIGBjdi5nbG0oKWAuIFRoZSBgY3YuZ2xtKClgIGZ1bmN0aW9uIGlzIHBhcnQgb2YgdGhlIGBib290YCBsaWJyYXJ5Lg0KDQpgYGB7cn0NCiNsb2FkIHRoZSBib290IGxpYnJhcnkgdG8gYWNjZXNzIHRoZSBjdi5nbG0gZnVuY3Rpb24NCmxpYnJhcnkoYm9vdCkNCiNmaXQgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIHVzaW5nIHRoZSBnbG0gZnVuY3Rpb24NCmdsbS5maXQ9Z2xtKG1wZ35ob3JzZXBvd2VyICxkYXRhPUF1dG8pDQojY2FsY3VsYXRlIHRoZSBjcm9zcy12YWxpZGF0aW9uIGVycm9yIG9mIHRoZSBsaW5lYXIgbW9kZWwNCmN2LmVycj1jdi5nbG0oQXV0byAsZ2xtLmZpdCkNCiNwcmludCBvdXQgdGhlIGNyb3NzLXZhbGlkYXRpb24gZXN0aW1hdGVzDQpjdi5lcnIkZGVsdGENCmBgYA0KDQpUaGUgYGN2LmdsbSgpYCBmdW5jdGlvbiBwcm9kdWNlcyBhIGxpc3Qgd2l0aCBzZXZlcmFsIGNvbXBvbmVudHMuIFRoZSB0d28gbnVtYmVycyBpbiB0aGUgZGVsdGEgdmVjdG9yIGNvbnRhaW4gdGhlIGNyb3NzLXZhbGlkYXRpb24gcmVzdWx0cy4gSW4gdGhpcyBjYXNlIHRoZSBudW1iZXJzIGFyZSBpZGVudGljYWwgKHVwIHRvIHR3byBkZWNpbWFsIHBsYWNlcykgYW5kIGNvcnJlc3BvbmQgdG8gdGhlIExPT0NWIHN0YXRpc3RpYyBnaXZlbiBpbiAoNS4xKS4gQmVsb3csIHdlIGRpc2N1c3MgYSBzaXR1YXRpb24gaW4gd2hpY2ggdGhlIHR3byBudW1iZXJzIGRpZmZlci4gT3VyIGNyb3NzLXZhbGlkYXRpb24gZXN0aW1hdGUgZm9yIHRoZSB0ZXN0IGVycm9yIGlzIGFwcHJveGltYXRlbHkgMjQuMjMuDQoNCldlIGNhbiByZXBlYXQgdGhpcyBwcm9jZWR1cmUgZm9yIGluY3JlYXNpbmdseSBjb21wbGV4IHBvbHlub21pYWwgZml0cy4gVG8gYXV0b21hdGUgdGhlIHByb2Nlc3MsIHdlIHVzZSB0aGUgYGZvcigpYCBmdW5jdGlvbiB0byBpbml0aWF0ZSBhICpmb3IgbG9vcCogIHdoaWNoIGl0ZXJhdGl2ZWx5IGZpdHMgcG9seW5vbWlhbCByZWdyZXNzaW9ucyBmb3IgcG9seW5vbWlhbHMgb2Ygb3JkZXIgKmkqID0gMSB0byAqaSogPSA1LCBjb21wdXRlcyB0aGUgYXNzb2NpYXRlZCBjcm9zcy12YWxpZGF0aW9uIGVycm9yLCBhbmQgc3RvcmVzIGl0IGluIHRoZSBpdGggZWxlbWVudCBvZiB0aGUgdmVjdG9yIGBjdi5lcnJvcmAuIFdlIGJlZ2luIGJ5IGluaXRpYWxpemluZyB0aGUgdmVjdG9yLiBUaGlzIGNvbW1hbmQgd2lsbCBsaWtlbHkgdGFrZSBhIGNvdXBsZSBvZiBtaW51dGVzIHRvIHJ1bi4NCg0KYGBge3IsIGNhY2hlPVRSVUV9DQojaW5pdGlhdGUgYW4gZW1wdHkgdmVjdG9yIHRvIHN0b3JlIGNvbXB1dGVkIGNyb3NzLXZhbGlkYXRpb24gZXJyb3JzDQpjdi5lcnJvcj1yZXAoMCw1KQ0KI2ZpdCBmaXZlIHJlZ3Jlc3Npb25zIGFnYWluc3QgbWlsZXMgcGVyIGdhbGxvbiBpbmNyZW1lbnRpbmcgdGhlIHBvd2VyIGZvciB0aGUgaG9yc2Vwb3dlciB2YXJpYWJsZQ0KDQpmb3IgKGkgaW4gMTo1KXsgI3NldCB1cCBmb3IgbG9vcCB0byBpbmNyZW1lbnQgZnJvbSAxIHRvIGZpdmUNCiBnbG0uZml0PWdsbShtcGd+cG9seShob3JzZXBvd2VyICxpKSxkYXRhPUF1dG8pICNmaXQgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsDQogY3YuZXJyb3JbaV09Y3YuZ2xtKEF1dG8gLGdsbS5maXQpJGRlbHRhIFsxXSAjY2FsY3VsYXRlIHRoZSBjcm9zcyB2YWxpZGF0aW9uIGVycm9yIGFuZCBzdG9yZSB0aGUgcmVzdWx0IGluIG91ciBjdi5lcnJvciB2ZWN0b3INCn0NCmN2LmVycm9yICNwcmludCBvdXQgdGhlIHJlc3VsdGluZyBjcm9zcyB2YWxpZGF0aW9uIGVycm9yDQpgYGANCg0KQXMgaW4gRmlndXJlIDUuNCwgd2Ugc2VlIGEgc2hhcnAgZHJvcCBpbiB0aGUgZXN0aW1hdGVkIHRlc3QgTVNFIGJldHdlZW4gdGhlIGxpbmVhciBhbmQgcXVhZHJhdGljIGZpdHMsIGJ1dCB0aGVuIG5vIGNsZWFyIGltcHJvdmVtZW50IGZyb20gdXNpbmcgaGlnaGVyLW9yZGVyIHBvbHlub21pYWxzLg0KDQoNCiMjIyBrLUZvbGQgQ3Jvc3MtVmFsaWRhdGlvbg0KVGhlIGBjdi5nbG0oKWAgZnVuY3Rpb24gY2FuIGFsc28gYmUgdXNlZCB0byBpbXBsZW1lbnQgKmsqLWZvbGQgQ1YuIEJlbG93IHdlIHVzZSAqayogPSAxMCwgYSBjb21tb24gY2hvaWNlIGZvciAqayosIG9uIHRoZSBgQXV0b2AgZGF0YSBzZXQuIFdlIG9uY2UgYWdhaW4gc2V0IGEgcmFuZG9tIHNlZWQgYW5kIGluaXRpYWxpemUgYSB2ZWN0b3IgaW4gd2hpY2ggd2Ugd2lsbCBzdG9yZSB0aGUgQ1YgZXJyb3JzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHBvbHlub21pYWwgZml0cyBvZiBvcmRlcnMgb25lIHRvIHRlbi4NCg0KYGBge3J9DQojaW5pdGlhbGl6ZSB0aGUgcmFuZG9tIG51bWJlciBnZW5lcmF0b3IgdG8gYWlkZSByZXByb2R1Y2liaWxpdHkNCnNldC5zZWVkKDE3KQ0KY3YuZXJyb3IuMTA9cmVwKDAsMTApICNpbml0aWF0ZSBhbiBlbXB0eSB2ZWN0b3IgdG8gc3RvcmUgY29tcHV0ZWQgY3Jvc3MtdmFsaWRhdGlvbiBlcnJvcnMNCmZvciAoaSBpbiAxOjEwKXsgI3NldCB1cCBmb3IgbG9vcCB0byBpbmNyZW1lbnQgZnJvbSAxIHRvIGZpdmUNCiBnbG0uZml0PWdsbShtcGd+cG9seShob3JzZXBvd2VyICxpKSxkYXRhPUF1dG8pICNmaXQgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsDQogY3YuZXJyb3IuMTBbaV09Y3YuZ2xtKEF1dG8sZ2xtLmZpdCxLPTEwKSRkZWx0YVsxXSAjY2FsY3VsYXRlIHRoZSBjcm9zcyB2YWxpZGF0aW9uIGVycm9yIGFuZCBzdG9yZSB0aGUgcmVzdWx0IGluIG91ciBjdi5lcnJvciB2ZWN0b3INCn0NCmN2LmVycm9yLjEwICNwcmludCBvdXQgdGhlIHJlc3VsdGluZyBjcm9zcyB2YWxpZGF0aW9uIGVycm9yDQpgYGANCg0KTm90aWNlIHRoYXQgdGhlIGNvbXB1dGF0aW9uIHRpbWUgaXMgbXVjaCBzaG9ydGVyIHRoYW4gdGhhdCBvZiBMT09DVi4gKEluIHByaW5jaXBsZSwgdGhlIGNvbXB1dGF0aW9uIHRpbWUgZm9yIExPT0NWIGZvciBhIGxlYXN0IHNxdWFyZXMgbGluZWFyIG1vZGVsIHNob3VsZCBiZSBmYXN0ZXIgdGhhbiBmb3IgKmsqLWZvbGQgQ1YsIGR1ZSB0byB0aGUgYXZhaWxhYmlsaXR5IG9mIHRoZSBmb3JtdWxhICg1LjIpIGZvciBMT09DVjsgaG93ZXZlciwgdW5mb3J0dW5hdGVseSB0aGUgYGN2LmdsbSgpYCBmdW5jdGlvbiBkb2VzIG5vdCBtYWtlIHVzZSBvZiB0aGlzIGZvcm11bGEuKSBXZSBzdGlsbCBzZWUgbGl0dGxlIGV2aWRlbmNlIHRoYXQgdXNpbmcgY3ViaWMgb3IgaGlnaGVyLW9yZGVyIHBvbHlub21pYWwgdGVybXMgbGVhZHMgdG8gbG93ZXIgdGVzdCBlcnJvciB0aGFuIHNpbXBseSB1c2luZyBhIHF1YWRyYXRpYyBmaXQuDQoNCldlIHNhdyBpbiBTZWN0aW9uIDUuMy4yIHRoYXQgdGhlIHR3byBudW1iZXJzIGFzc29jaWF0ZWQgd2l0aCBgZGVsdGFgIGFyZSBlc3NlbnRpYWxseSB0aGUgc2FtZSB3aGVuIExPT0NWIGlzIHBlcmZvcm1lZC4gV2hlbiB3ZSBpbnN0ZWFkIHBlcmZvcm0gKmsqLWZvbGQgQ1YsIHRoZW4gdGhlIHR3byBudW1iZXJzIGFzc29jaWF0ZWQgd2l0aCBkZWx0YSBkaWZmZXIgc2xpZ2h0bHkuIFRoZSBmaXJzdCBpcyB0aGUgc3RhbmRhcmQgKmsqLWZvbGQgQ1YgZXN0aW1hdGUsIGFzIGluICg1LjMpLiBUaGUgc2Vjb25kIGlzIGEgYmlhc2NvcnJlY3RlZCB2ZXJzaW9uLiBPbiB0aGlzIGRhdGEgc2V0LCB0aGUgdHdvIGVzdGltYXRlcyBhcmUgdmVyeSBzaW1pbGFyIHRvIGVhY2ggb3RoZXIuDQoNCg0KIyMjIFRoZSBCb290c3RyYXANCldlIGlsbHVzdHJhdGUgdGhlIHVzZSBvZiB0aGUgYm9vdHN0cmFwIGluIHRoZSBzaW1wbGUgZXhhbXBsZSBvZiBTZWN0aW9uIDUuMiwgYXMgd2VsbCBhcyBvbiBhbiBleGFtcGxlIGludm9sdmluZyBlc3RpbWF0aW5nIHRoZSBhY2N1cmFjeSBvZiB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgb24gdGhlIGBBdXRvYCBkYXRhIHNldC4NCg0KKipFc3RpbWF0aW5nIHRoZSBBY2N1cmFjeSBvZiBhIFN0YXRpc3RpYyBvZiBJbnRlcmVzdCoqICANCk9uZSBvZiB0aGUgZ3JlYXQgYWR2YW50YWdlcyBvZiB0aGUgYm9vdHN0cmFwIGFwcHJvYWNoIGlzIHRoYXQgaXQgY2FuIGJlIGFwcGxpZWQgaW4gYWxtb3N0IGFsbCBzaXR1YXRpb25zLiBObyBjb21wbGljYXRlZCBtYXRoZW1hdGljYWwgY2FsY3VsYXRpb25zIGFyZSByZXF1aXJlZC4gUGVyZm9ybWluZyBhIGJvb3RzdHJhcCBhbmFseXNpcyBpbiBgUmAgZW50YWlscyBvbmx5IHR3byBzdGVwcy4gRmlyc3QsIHdlIG11c3QgY3JlYXRlIGEgZnVuY3Rpb24gdGhhdCBjb21wdXRlcyB0aGUgc3RhdGlzdGljIG9mIGludGVyZXN0LiBTZWNvbmQsIHdlIHVzZSB0aGUgYGJvb3QoKWAgZnVuY3Rpb24sIHdoaWNoIGlzIHBhcnQgb2YgdGhlIGBib290YCBsaWJyYXJ5LCB0byBwZXJmb3JtIHRoZSBib290c3RyYXAgYnkgcmVwZWF0ZWRseSBzYW1wbGluZyBvYnNlcnZhdGlvbnMgZnJvbSB0aGUgZGF0YSBzZXQgd2l0aCByZXBsYWNlbWVudC4NCg0KVGhlIGBQb3J0Zm9saW9gIGRhdGEgc2V0IGluIHRoZSBgSVNMUmAgcGFja2FnZSBpcyBkZXNjcmliZWQgaW4gU2VjdGlvbiA1LjIuIFRvIGlsbHVzdHJhdGUgdGhlIHVzZSBvZiB0aGUgYm9vdHN0cmFwIG9uIHRoaXMgZGF0YSwgd2UgbXVzdCBmaXJzdCBjcmVhdGUgYSBmdW5jdGlvbiwgYGFscGhhLmZuKClgLCB3aGljaCB0YWtlcyBhcyBpbnB1dCB0aGUgJChYLFkpJCBkYXRhIGFzIHdlbGwgYXMgYSB2ZWN0b3IgaW5kaWNhdGluZyB3aGljaCBvYnNlcnZhdGlvbnMgc2hvdWxkIGJlIHVzZWQgdG8gZXN0aW1hdGUgJFxhbHBoYSQuIFRoZSBmdW5jdGlvbiB0aGVuIG91dHB1dHMgdGhlIGVzdGltYXRlIGZvciAkXGFscGhhJCBiYXNlZCBvbiB0aGUgc2VsZWN0ZWQgb2JzZXJ2YXRpb25zLg0KDQpgYGB7cn0NCiNjcmVhdGUgYSBmdW5jdGlvbiBmb3IgYWxwaGEgc3RhdGlzdGljIHdoaWNoIGNvbXBhcmVzIGl0cyB2YXJpYW5jZSB0byB0aGUgb3ZlcmFsbCBjb3ZhcmlhbmNlIGJldHdlZW4gdHdvIHZhcmlhYmxlcw0KYWxwaGEuZm49ZnVuY3Rpb24gKGRhdGEgLGluZGV4KXsgI2luaXRpYWxpemUgZnVuY3Rpb24gdG8gYWNjZXB0IGEgZGF0YSBvYmplY3QgYW5kIGluZGV4IG9iamVjdA0KIFg9ZGF0YSRYW2luZGV4XSAjYXNzaWduIFggdGhlIHZhbHVlIG9mIHRoZSBkYXRhIG9iamVjdCBzdWJzZXR0ZWQgb24gdGhlIFggdmFyaWFibGUgZm9yIHRoZSBpbmRleCBvYnNlcnZhdGlvbnMNCiBZPWRhdGEkWVtpbmRleF0gI2Fzc2lnbiBZIHRoZSB2YWx1ZSBvZiB0aGUgZGF0YSBvYmplY3Qgc3Vic2V0dGVkIG9uIHRoZSBYIHZhcmlhYmxlIGZvciB0aGUgaW5kZXggb2JzZXJ2YXRpb25zDQogcmV0dXJuICgodmFyKFkpLWNvdihYLFkpKS8odmFyKFgpK3ZhcihZKS0yKmNvdihYLFkpKSkgI2NhbGN1bGF0ZSB0aGUgc3RhdGlzdGljDQp9DQpgYGANCg0KVGhpcyBmdW5jdGlvbiByZXR1cm5zLCBvciBvdXRwdXRzLCBhbiBlc3RpbWF0ZSBmb3IgJFxhbHBoYSQgYmFzZWQgb24gYXBwbHlpbmcgKDUuNykgdG8gdGhlIG9ic2VydmF0aW9ucyBpbmRleGVkIGJ5IHRoZSBhcmd1bWVudCBgaW5kZXhgLiBGb3IgaW5zdGFuY2UsIHRoZSBmb2xsb3dpbmcgY29tbWFuZCB0ZWxscyBgUmAgdG8gZXN0aW1hdGUgJFxhbHBoYSQgdXNpbmcgYWxsIDEwMCBvYnNlcnZhdGlvbnMuDQoNCmBgYHtyfQ0KYWxwaGEuZm4oUG9ydGZvbGlvICwxOjEwMCkgI3Rlc3QgdGhlIGZ1bmN0aW9uIG9uIHRoZSBQb3J0Zm9saW8gZGF0YSBzZXQgdXNpbmcgdGhlIG51bWJlcnMgMSB0byAxMDAgYXMgdGhlIGluZGV4DQpgYGANCg0KVGhlIG5leHQgY29tbWFuZCB1c2VzIHRoZSBgc2FtcGxlKClgIGZ1bmN0aW9uIHRvIHJhbmRvbWx5IHNlbGVjdCAxMDAgb2JzZXJ2YXRpb25zIGZyb20gdGhlIHJhbmdlIDEgdG8gMTAwLCB3aXRoIHJlcGxhY2VtZW50LiBUaGlzIGlzIGVxdWl2YWxlbnQgdG8gY29uc3RydWN0aW5nIGEgbmV3IGJvb3RzdHJhcCBkYXRhIHNldCBhbmQgcmVjb21wdXRpbmcgJFxoYXR7XGFscGhhfSQgYmFzZWQgb24gdGhlIG5ldyBkYXRhIHNldC4NCg0KYGBge3J9DQpzZXQuc2VlZCgxKSAjaW5pdGlhbGl6ZSB0aGUgcmFuZG9tIG51bWJlciBnZW5lcmF0b3IgdG8gYWlkZSByZXByb2R1Y2liaWxpdHkNCmFscGhhLmZuKFBvcnRmb2xpbyAsc2FtcGxlICgxMDAsMTAwLCByZXBsYWNlPVQpKSAjdGVzdCB0aGUgZnVuY3Rpb24gb24gdGhlIFBvcnRmb2xpbyBkYXRhIHNldCB1c2luZyAxMDAgcmFuZG9tbHkgc2VsZWN0ZWQgb2JzZXJ2YXRpb25zIGFzIHRoZSBpbmRleA0KYGBgDQoNCldlIGNhbiBpbXBsZW1lbnQgYSBib290c3RyYXAgYW5hbHlzaXMgYnkgcGVyZm9ybWluZyB0aGlzIGNvbW1hbmQgbWFueSB0aW1lcywgcmVjb3JkaW5nIGFsbCBvZiB0aGUgY29ycmVzcG9uZGluZyBlc3RpbWF0ZXMgZm9yICRcYWxwaGEkLCBhbmQgY29tcHV0aW5nIHRoZSByZXN1bHRpbmcgc3RhbmRhcmQgZGV2aWF0aW9uLiBIb3dldmVyLCB0aGUgYGJvb3QoKWAgZnVuY3Rpb24gYXV0b21hdGVzIHRoaXMgYXBwcm9hY2guIEJlbG93IHdlIHByb2R1Y2UgJFIkID0gMSwwMDAgYm9vdHN0cmFwIGVzdGltYXRlcyBmb3IgJFxhbHBoYSQuDQoNCmBgYHtyfQ0KI2F1dG9tYXRlIHRoZSBib290c3RyYXAgYW5hbHlzaXMgdXNpbmcgdGhlIGJvb3QgZnVuY3Rpb24NCmJvb3QoUG9ydGZvbGlvLGFscGhhLmZuLFI9MTAwMCkNCmBgYA0KDQpUaGUgZmluYWwgb3V0cHV0IHNob3dzIHRoYXQgdXNpbmcgdGhlIG9yaWdpbmFsIGRhdGEsICRcaGF0e1xhbHBoYX0kID0gMC41NzU4LCBhbmQgdGhhdCB0aGUgYm9vdHN0cmFwIGVzdGltYXRlIGZvciAkU0UoXGhhdHtcYWxwaGF9KSQgaXMgMC4wOTM3Lg0KDQoqKkVzdGltYXRpbmcgdGhlIEFjY3VyYWN5IG9mIGEgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWwqKiAgDQpUaGUgYm9vdHN0cmFwIGFwcHJvYWNoIGNhbiBiZSB1c2VkIHRvIGFzc2VzcyB0aGUgdmFyaWFiaWxpdHkgb2YgdGhlIGNvZWZmaWNpZW50IGVzdGltYXRlcyBhbmQgcHJlZGljdGlvbnMgZnJvbSBhIHN0YXRpc3RpY2FsIGxlYXJuaW5nIG1ldGhvZC4gSGVyZSB3ZSB1c2UgdGhlIGJvb3RzdHJhcCBhcHByb2FjaCBpbiBvcmRlciB0byBhc3Nlc3MgdGhlIHZhcmlhYmlsaXR5IG9mIHRoZSBlc3RpbWF0ZXMgZm9yICRcYmV0YV8wJCBhbmQgJFxiZXRhXzEkLCB0aGUgaW50ZXJjZXB0IGFuZCBzbG9wZSB0ZXJtcyBmb3IgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRoYXQgdXNlcyBgaG9yc2Vwb3dlcmAgdG8gcHJlZGljdCBgbXBnYCBpbiB0aGUgYEF1dG9gIGRhdGEgc2V0LiBXZSB3aWxsIGNvbXBhcmUgdGhlIGVzdGltYXRlcyBvYnRhaW5lZCB1c2luZyB0aGUgYm9vdHN0cmFwIHRvIHRob3NlIG9idGFpbmVkIHVzaW5nIHRoZSBmb3JtdWxhcyBmb3IgJFNFKFxoYXR7XGJldGFfMH0pJCBhbmQgJFNFKFxoYXR7XGJldGFfMX0pJCBkZXNjcmliZWQgaW4gU2VjdGlvbiAzLjEuMi4NCg0KV2UgZmlyc3QgY3JlYXRlIGEgc2ltcGxlIGZ1bmN0aW9uLCBgYm9vdC5mbigpYCwgd2hpY2ggdGFrZXMgaW4gdGhlIGBBdXRvYCBkYXRhIHNldCBhcyB3ZWxsIGFzIGEgc2V0IG9mIGluZGljZXMgZm9yIHRoZSBvYnNlcnZhdGlvbnMsIGFuZCByZXR1cm5zIHRoZSBpbnRlcmNlcHQgYW5kIHNsb3BlIGVzdGltYXRlcyBmb3IgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLiBXZSB0aGVuIGFwcGx5IHRoaXMgZnVuY3Rpb24gdG8gdGhlIGZ1bGwgc2V0IG9mIDM5MiBvYnNlcnZhdGlvbnMgaW4gb3JkZXIgdG8gY29tcHV0ZSB0aGUgZXN0aW1hdGVzIG9mICRcYmV0YV8wJCBhbmQgJFxiZXRhXzAkIG9uIHRoZSBlbnRpcmUgZGF0YSBzZXQgdXNpbmcgdGhlIHVzdWFsIGxpbmVhciByZWdyZXNzaW9uIGNvZWZmaWNpZW50IGVzdGltYXRlIGZvcm11bGFzIGZyb20gQ2hhcHRlciAzLiBOb3RlIHRoYXQgd2UgZG8gbm90IG5lZWQgdGhlIGB7YCBhbmQgYH1gIGF0IHRoZSBiZWdpbm5pbmcgYW5kIGVuZCBvZiB0aGUgZnVuY3Rpb24gYmVjYXVzZSBpdCBpcyBvbmx5IG9uZSBsaW5lIGxvbmcuDQoNCmBgYHtyfQ0KIyBjcmVhdGUgdGhlIGJvb3QuZm4gZnVuY3Rpb24gdG8gZXN0aW1hdGUgdGhlIGVycm9ycyBhc3NvY2lhdGVkIHdpdGggdGhlIGNvZWZmaWNpZW50cyBvZiBhIGxpbmVhciBtb2RlbA0KYm9vdC5mbj1mdW5jdGlvbiAoZGF0YSAsaW5kZXgpICNpbml0aWFsaXplIGZ1bmN0aW9uIHRvIGFjY2VwdCBhIGRhdGEgYW5kIGluZGV4IG9iamVjdA0KIHJldHVybihjb2VmKGxtKG1wZ35ob3JzZXBvd2VyICxkYXRhPWRhdGEgLCBzdWJzZXQ9aW5kZXgpKSkgIyBncmFiIHRoZSBjb2VmZmljaWVudHMgb2YgdGhlIGxpbmVhciBtb2RlbA0KYm9vdC5mbihBdXRvLDE6MzkyKSAjcnVuIHRoZSBmdW5jdGlvbiBvbiB0aGUgQXV0byBkYXRhDQpgYGANCg0KVGhlIGBib290LmZuKClgIGZ1bmN0aW9uIGNhbiBhbHNvIGJlIHVzZWQgaW4gb3JkZXIgdG8gY3JlYXRlIGJvb3RzdHJhcCBlc3RpbWF0ZXMgZm9yIHRoZSBpbnRlcmNlcHQgYW5kIHNsb3BlIHRlcm1zIGJ5IHJhbmRvbWx5IHNhbXBsaW5nIGZyb20gYW1vbmcgdGhlIG9ic2VydmF0aW9ucyB3aXRoIHJlcGxhY2VtZW50LiBIZXJlIHdlIGdpdmUgdHdvIGV4YW1wbGVzLg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpICNpbml0aWFsaXplIHRoZSByYW5kb20gbnVtYmVyIGdlbmVyYXRvciB0byBhaWRlIHJlcHJvZHVjaWJpbGl0eQ0KYm9vdC5mbihBdXRvICxzYW1wbGUgKDM5MiwzOTIsIHJlcGxhY2U9VCkpICNydW4gdGhlIGZ1bmN0aW9uIG9uIHRoZSByYW5kb20gcGVybXV0YXRpb25zIG9mIG9ic2VydmF0aW9ucyBpbiB0aGUgQXV0byBkYXRhDQpgYGANCmBgYHtyfQ0KYm9vdC5mbihBdXRvICxzYW1wbGUgKDM5MiwzOTIsIHJlcGxhY2U9VCkpICNydW4gdGhlIGZ1bmN0aW9uIG9uIHRoZSByYW5kb20gcGVybXV0YXRpb25zIG9mIG9ic2VydmF0aW9ucyBpbiB0aGUgQXV0byBkYXRhIGFnYWluDQpgYGANCg0KTmV4dCwgd2UgdXNlIHRoZSBgYm9vdCgpYCBmdW5jdGlvbiB0byBjb21wdXRlIHRoZSBzdGFuZGFyZCBlcnJvcnMgb2YgMSwwMDAgYm9vdHN0cmFwIGVzdGltYXRlcyBmb3IgdGhlIGludGVyY2VwdCBhbmQgc2xvcGUgdGVybXMuDQoNCmBgYHtyfQ0KYm9vdChBdXRvICxib290LmZuICwxMDAwKSAjY29tcHV0ZSB0aGUgc3RhbmRhcmQgZXJyb3JzIG9mIDEsMDAwIGJvb3RzdHJhcCBlc3RpbWF0ZXMgb2YgdGhlIGxpbmVhciBtb2RlbCBjb2VmZmljaWVudHMgdXNpbmcgdGhlIGJvb3QgZnVuY3Rpb24NCmBgYA0KDQpUaGlzIGluZGljYXRlcyB0aGF0IHRoZSBib290c3RyYXAgZXN0aW1hdGUgZm9yICRTRShcaGF0e1xiZXRhfV8wKSQgaXMgMC44NDEyLCBhbmQgdGhhdCB0aGUgYm9vdHN0cmFwIGVzdGltYXRlIGZvciAkU0UoXGhhdHtcYmV0YX1fMSkkIGlzIDAuMDA3My4gQXMgZGlzY3Vzc2VkIGluIFNlY3Rpb24gMy4xLjIsIHN0YW5kYXJkIGZvcm11bGFzIGNhbiBiZSB1c2VkIHRvIGNvbXB1dGUgdGhlIHN0YW5kYXJkIGVycm9ycyBmb3IgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGluIGEgbGluZWFyIG1vZGVsLiBUaGVzZSBjYW4gYmUgb2J0YWluZWQgdXNpbmcgdGhlIGBzdW1tYXJ5KClgIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCiNHZXQgdGhlIGNvbXB1dGVkIGVzdGltYXRlcywgc3RhbmRhcmQgZXJyb3JzLCBhbmQgYXNzb2NpYXRlZCBzdGF0aXN0aWNzDQpzdW1tYXJ5KGxtKG1wZ35ob3JzZXBvd2VyICxkYXRhPUF1dG8pKSRjb2VmDQpgYGANCg0KVGhlIHN0YW5kYXJkIGVycm9yIGVzdGltYXRlcyBmb3IgJFxoYXR7XGJldGF9XzAkIGFuZCAkXGhhdHtcYmV0YX1fMSQgb2J0YWluZWQgdXNpbmcgdGhlIGZvcm11bGFzIGZyb20gU2VjdGlvbiAzLjEuMiBhcmUgMC43MTcgZm9yIHRoZSBpbnRlcmNlcHQgYW5kIDAuMDA2NCBmb3IgdGhlIHNsb3BlLiBJbnRlcmVzdGluZ2x5LCB0aGVzZSBhcmUgc29tZXdoYXQgZGlmZmVyZW50IGZyb20gdGhlIGVzdGltYXRlcyBvYnRhaW5lZCB1c2luZyB0aGUgYm9vdHN0cmFwLiBEb2VzIHRoaXMgaW5kaWNhdGUgYSBwcm9ibGVtIHdpdGggdGhlIGJvb3RzdHJhcD8gSW4gZmFjdCwgaXQgc3VnZ2VzdHMgdGhlIG9wcG9zaXRlLiBSZWNhbGwgdGhhdCB0aGUgc3RhbmRhcmQgZm9ybXVsYXMgZ2l2ZW4gaW4gRXF1YXRpb24gMy44IG9uIHBhZ2UgNjYgcmVseSBvbiBjZXJ0YWluIGFzc3VtcHRpb25zLiBGb3IgZXhhbXBsZSwgdGhleSBkZXBlbmQgb24gdGhlIHVua25vd24gcGFyYW1ldGVyICRcc2lnbWFeMiQsIHRoZSBub2lzZSB2YXJpYW5jZS4gV2UgdGhlbiBlc3RpbWF0ZSAkXHNpZ21hXjIkIHVzaW5nIHRoZSBSU1MuIE5vdyBhbHRob3VnaCB0aGUgZm9ybXVsYSBmb3IgdGhlIHN0YW5kYXJkIGVycm9ycyBkbyBub3QgcmVseSBvbiB0aGUgbGluZWFyIG1vZGVsIGJlaW5nIGNvcnJlY3QsIHRoZSBlc3RpbWF0ZSBmb3IgJFxzaWdtYV4yJCBkb2VzLiBXZSBzZWUgaW4gRmlndXJlIDMuOCBvbiBwYWdlIDkxIHRoYXQgdGhlcmUgaXMgYSBub24tbGluZWFyIHJlbGF0aW9uc2hpcCBpbiB0aGUgZGF0YSwgYW5kIHNvIHRoZSByZXNpZHVhbHMgZnJvbSBhIGxpbmVhciBmaXQgd2lsbCBiZSBpbmZsYXRlZCwgYW5kIHNvIHdpbGwgJFxoYXR7XHNpZ21hfV4yJC4gU2Vjb25kbHksIHRoZSBzdGFuZGFyZCBmb3JtdWxhcyBhc3N1bWUgKHNvbWV3aGF0IHVucmVhbGlzdGljYWxseSkgdGhhdCB0aGUgJHhfaSQgYXJlIGZpeGVkLCBhbmQgYWxsIHRoZSB2YXJpYWJpbGl0eSBjb21lcyBmcm9tIHRoZSB2YXJpYXRpb24gaW4gdGhlIGVycm9ycyAkXGVwc2lsb25faSQuIFRoZSBib290c3RyYXAgYXBwcm9hY2ggZG9lcyBub3QgcmVseSBvbiBhbnkgb2YgdGhlc2UgYXNzdW1wdGlvbnMsIGFuZCBzbyBpdCBpcyBsaWtlbHkgZ2l2aW5nIGEgbW9yZSBhY2N1cmF0ZSBlc3RpbWF0ZSBvZiB0aGUgc3RhbmRhcmQgZXJyb3JzIG9mICRcaGF0e1xiZXRhfV8wJCBhbmQgJFxoYXR7XGJldGF9XzEkIHRoYW4gaXMgdGhlIGBzdW1tYXJ5KClgIGZ1bmN0aW9uLg0KDQpCZWxvdyB3ZSBjb21wdXRlIHRoZSBib290c3RyYXAgc3RhbmRhcmQgZXJyb3IgZXN0aW1hdGVzIGFuZCB0aGUgc3RhbmRhcmQgbGluZWFyIHJlZ3Jlc3Npb24gZXN0aW1hdGVzIHRoYXQgcmVzdWx0IGZyb20gZml0dGluZyB0aGUgcXVhZHJhdGljIG1vZGVsIHRvIHRoZSBkYXRhLiBTaW5jZSB0aGlzIG1vZGVsIHByb3ZpZGVzIGEgZ29vZCBmaXQgdG8gdGhlIGRhdGEgKEZpZ3VyZSAzLjgpLCB0aGVyZSBpcyBub3cgYSBiZXR0ZXIgY29ycmVzcG9uZGVuY2UgYmV0d2VlbiB0aGUgYm9vdHN0cmFwIGVzdGltYXRlcyBhbmQgdGhlIHN0YW5kYXJkIGVzdGltYXRlcyBvZiAkU0UoXGhhdHtcYmV0YX1fMCkkLCAkU0UoXGhhdHtcYmV0YX1fMSkkIGFuZCAkU0UoXGhhdHtcYmV0YX1fMikkLg0KDQpgYGB7cn0NCiNjb21wdXRlIHRoZSBib290c3RyYXAgc3RhbmRhcmQgZXJyb3IgZXN0aW1hdGVzIGFuZCB0aGUgc3RhbmRhcmQgbGluZWFyIHJlZ3Jlc3Npb24gZXN0aW1hdGVzDQpib290LmZuPWZ1bmN0aW9uIChkYXRhICxpbmRleCkgI2luaXRpYWxpemUgYm9vdC5mbiBmdW5jdGlvbiB0byB0YWtlIGRhdGEgYW5kIGluZGV4IG9iamVjdHMNCiBjb2VmZmljaWVudHMobG0obXBnfmhvcnNlcG93ZXIgK0koaG9yc2Vwb3dlcl4yKSxkYXRhPWRhdGEgLCBzdWJzZXQ9aW5kZXgpKSAjZXN0aW1hdGUgdGhlIGNvZWZmaWNpZW50cyBvZiBhIGxpbmVhciBtb2RlbA0Kc2V0LnNlZWQoMSkgI2luaXRpYWxpemUgdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yIHRvIGFpZGUgcmVwcm9kdWNpYmlsaXR5DQpib290KEF1dG8gLGJvb3QuZm4gLDEwMDApICNydW4gdGhlIGJvb3QuZm4gZnVuY3Rpb24gMTAwMCB0aW1lcw0KYGBgDQpgYGB7cn0NCnN1bW1hcnkobG0obXBnfmhvcnNlcG93ZXIgK0koaG9yc2Vwb3dlcl4yKSxkYXRhPUF1dG8pKSRjb2VmICNjb21wYXJlIHRoZSBib290c3RyYXBwZWQgcmVzdWx0cyB3aXRoIHRoZSBzdGF0aXN0aWNzIGNhbGN1bGF0ZWQgaW4gUg0KYGBgDQoNCg0KDQoNCg==