Introduction

The purpose of this document is to demonstrate the three different types of regularized regression: Elastic Net, Lasso and Ridge Regression. While the standard R glmnet function offers some tools for visualizing the results of these techniques, the coefplot package contains several useful tools for visualization that are a bit easier to interpret.

Baseball Data

In order to illustrate regularized regression and the coefplot package, we need some data on which to perform variable selection. The data for this exercise will be major league baseball data from the 1986 and 1987. We will have a number of predicting variables, for example Hits, Runs, RBI, Home Runs, and several others. From this data we want to build a model that can help us predict (or justify) a players salary based on their performance.

(Reference: James, G., Witten, D., Hastie, T., and Tibshirani, R. (2013) An Introduction to Statistical Learning with applications in R, www.StatLearning.com, Springer-Verlag, New York)

rm(list=ls())
set.seed(100)
library(ISLR)
names(Hitters)
 [1] "AtBat"     "Hits"      "HmRun"     "Runs"      "RBI"       "Walks"     "Years"     "CAtBat"    "CHits"     "CHmRun"    "CRuns"    
[12] "CRBI"      "CWalks"    "League"    "Division"  "PutOuts"   "Assists"   "Errors"    "Salary"    "NewLeague"
Hitters<-na.omit(Hitters)

We will want to do some comparison of models, so we will split the data into testing and training sets.

## 75% of the sample size
smp_size <- floor(0.75 * nrow(Hitters))

train_ind <- sample(seq_len(nrow(Hitters)), size = smp_size)

train <- Hitters[train_ind, ]
test <- Hitters[-train_ind, ]

Basic Model (No Variable Selection)

Now that we have some data, let’s reveiw what we want to do with regularized variable selection. In a multiple regression problem, we have multiple predictors and a target variable. In R, it is relatively easy to preform multiple regression on this data with the lm function. The result is a set of coefficients that we apply to each predictor. Also, we are able to use residual analysis to determine the statistical significance of each predictor in the overall model.

mdl1 <- lm(Salary~., data=train)
summary(mdl1)

Call:
lm(formula = Salary ~ ., data = train)

Residuals:
    Min      1Q  Median      3Q     Max 
-705.79 -150.74  -24.62  125.17 1063.50 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  33.57152   93.09123   0.361 0.718806    
AtBat        -2.41967    0.65567  -3.690 0.000298 ***
Hits         11.08951    2.60137   4.263 3.28e-05 ***
HmRun        -3.55591    6.29651  -0.565 0.572963    
Runs         -4.52955    2.96590  -1.527 0.128493    
RBI          -1.52156    2.62867  -0.579 0.563438    
Walks         7.05505    1.83780   3.839 0.000172 ***
Years        -3.40822   12.19201  -0.280 0.780153    
CAtBat        0.05533    0.13555   0.408 0.683618    
CHits        -0.80779    0.71644  -1.128 0.261056    
CHmRun        0.98164    1.73619   0.565 0.572517    
CRuns         2.05346    0.80316   2.557 0.011405 *  
CRBI          0.64249    0.72080   0.891 0.373949    
CWalks       -1.11918    0.33262  -3.365 0.000939 ***
LeagueN     116.04168   83.58204   1.388 0.166773    
DivisionW   -69.47276   41.41353  -1.678 0.095202 .  
PutOuts       0.32933    0.07670   4.294 2.89e-05 ***
Assists       0.10522    0.21140   0.498 0.619270    
Errors        1.46384    4.13709   0.354 0.723886    
NewLeagueN  -89.55925   83.87487  -1.068 0.287077    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 272 on 177 degrees of freedom
Multiple R-squared:  0.6702,    Adjusted R-squared:  0.6348 
F-statistic: 18.93 on 19 and 177 DF,  p-value: < 2.2e-16
mdl1.pred <- predict(mdl1, new_data=test)
lm_mse <- mean((mdl1.pred - test$Salary)^2)
longer object length is not a multiple of shorter object length

In the above output, we can see the various coefficients, and their statistical significance.

Bias-Variance Tradeoff

More predictor variables in a model will typically result in lower bias, but higher variability of model predictions. A common way to think about this is in terms of the graphic below. A model that has lots of predictors would likely results in the upper right hand corner. A model with few predictors results in the lower left. When performing predictions, it is often preferable to have less variance even if there is some bias.

Bias and Variance

Bias and Variance

The baseball salary model that we created above has several predictors. It’s mean squared error (MSE) is 3.356621710^{5}. Next, we will look at creating models using regularization variable selection techniques. We will then compare our regularized models with the full model above.

Mathematical Formulation of Regularization Regression Techniques

Regularized regression techniques include Lasso, Ridge Regression, and Elastic Net. Elasticnet is a generalization of the other two. Mathematically, these regression techniques all look like this:

\[ min \sum_{i=1}^n(y_i-(\beta_0+\beta_1x_{1,i}+\beta_2x_{2,i}+...+{\beta_nx_{p,i}}))^2 \] Subject to:

\[ \lambda(\alpha\sum_{i=1}^p|\beta_i| + (1-\alpha)\sum_{i=1}^p\beta_i^2)\leq\tau \]

We want a value of \(\lambda\) that minimizes the first equation, subject to the second constraint.

In effect, what we’re doing here is limiting either the sum of the absolute value of the coefficients, or on the sum of the coefficients squared. In the above, if we set \(\alpha=0\), the absolute value term goes away and we are left with Ridge Regression. Similarly, if we set \(\alpha=1\), then the sum of squared coefficients term goes away and we are left with Lasso. If we choose alpha somewhere in the middle, we have Elastic Net. One of the more subtle points of the above is that Ridge regression actually does not end up eliminating variables. Lasso, on the other hand, will reduce some of the coefficients to 0. This is the result of the \(L_1\) and \(L_2\) metrics used in Lasso and Ridge respectively.

Determining \(\alpha\) and \(\lambda\)

So, how do we determine the \(\alpha\) and \(\lambda\) values used in Elastic Net?

Essentially, \(\alpha\) is like a switch that can turn Elastic Net into Ridge or Lasso, or a combination. We can simply choose \(\alpha\). Once we have decided on an approach, we are left with finding an optmial value for \(\lambda\).

Picking \(\lambda\) can be done using a cross validation approach. In this approach, we divide up the training data for our model into a number of folds. The following diagram shows how we use one of the folds against the remaining data in order to evaluate the performance of the model. We can continue to do this with a range of \(\lambda\) values to determine the best value of \(\lambda\) based on some accuracy metric. (Typically the residual squared sum or RSS.) Note: As we perform cross validation, we will be doing so within the training set. We keep the test data separate until we wish to compare all our models.

10 Fold Cross Validation

10 Fold Cross Validation

Ridge Regression in R

The following code performs a ridge regression on the Hitters data training set.

library(glmnet) 
library(coefplot)
set.seed(100)
std <- TRUE  # Required for Lasso
Y <- train$Salary
X <- model.matrix(Salary~.,train)[,-1]
# 10-fold CV to find the optimal lambda 
ridge.cv=cv.glmnet(X, Y, alpha=0, type="deviance", family="gaussian", standardize=std, nfolds=10)
## Fit lasso model with 100 values for lambda
ridge_mdl = glmnet(X, Y, alpha=0, nlambda=100, standardize=std, family="gaussian")
## Extract coefficients at optimal lambda
coef(ridge_mdl,s=ridge.cv$lambda.min)
20 x 1 sparse Matrix of class "dgCMatrix"
                        1
(Intercept) -31.031994595
AtBat        -0.503926301
Hits          3.150077786
HmRun        -7.412961727
Runs          0.719601377
RBI           0.505273525
Walks         3.076459773
Years        -4.587219638
CAtBat        0.003743458
CHits         0.096606473
CHmRun        1.567366492
CRuns         0.271540296
CRBI          0.268394560
CWalks       -0.291011895
LeagueN      87.133238411
DivisionW   -81.542304442
PutOuts       0.309545878
Assists       0.058823277
Errors       -0.317446290
NewLeagueN  -73.521157177
plot(ridge_mdl, xvar="lambda", lwd=2, label=T)
abline(v=log(ridge.cv$lambda.min), col="blue", lty=2)

The resulting value of lambda for this variable selection method is 29.9798498

To get a sense of how the code settled on this value of lambda, we can take a look at the following plots. The first shows how the coefficients change as the log of lambda change. The second shows a plot of the coefficients sorted by magnitude at the optimal lambda value.

coefpath(ridge_mdl)

coefplot(ridge_mdl, lambda=ridge.cv$lambda.min, sort="magnitude")

Lasso Regression in R

We can repeat the same steps below for Lasso regression. This time we set the value of \(\alpha\) to 1 to force the model to use Lasso.

# 10-fold CV to find the optimal lambda 
lasso.cv=cv.glmnet(X,Y,alpha=1, type="deviance", family="gaussian", standardize=std, nfolds=10)
## Fit lasso model with 100 values for lambda
lasso_mdl = glmnet(X,Y,alpha=1,family="gaussian", standardize=std, nlambda=100)
## Extract coefficients at optimal lambda
coef(lasso_mdl,s=lasso.cv$lambda.min)
20 x 1 sparse Matrix of class "dgCMatrix"
                       1
(Intercept) -19.58957875
AtBat        -1.44032127
Hits          6.50025004
HmRun        -7.88008124
Runs          .         
RBI           .         
Walks         4.17171810
Years        -0.06029097
CAtBat        .         
CHits         .         
CHmRun        2.30642850
CRuns         0.67656267
CRBI          0.05039562
CWalks       -0.54228215
LeagueN      76.47735074
DivisionW   -69.56215881
PutOuts       0.32083857
Assists       0.08607745
Errors        0.13554310
NewLeagueN  -52.41700858
plot(lasso_mdl, xvar="lambda", lwd=2, label=T)
abline(v=log(lasso.cv$lambda.min), col="blue", lty=2)

The vertical line in the above plot indicates the optimal value for lambda.

The following two plots are similar to the ones we viewed above. This shows a slightly nicer display of the coefficients “path”, and the sorted magnitude of the coefficients at the optimal lambda.

coefpath(lasso_mdl)

coefplot(lasso_mdl, lambda=lasso.cv$lambda.min, sort="magnitude")

Elastic Net Regression in R

Finally, we will perform Elastic Net regression on the data. We choose \(\alpha=0.5\) in this case.

# 10-fold CV to find the optimal lambda 
enet.cv=cv.glmnet(X,Y,alpha=0.5, type="deviance", family="gaussian", standardize=std, nfolds=10)
## Fit lasso model with 100 values for lambda
enet_mdl = glmnet(X,Y,alpha=0.5,standardize=std,nlambda=100)
## Extract coefficients at optimal lambda
coef(enet_mdl,s=enet.cv$lambda.min)
20 x 1 sparse Matrix of class "dgCMatrix"
                       1
(Intercept) -15.37942454
AtBat        -1.31472788
Hits          6.06170510
HmRun        -7.85800229
Runs          .         
RBI           .         
Walks         4.16726809
Years        -1.65056351
CAtBat        .         
CHits         .         
CHmRun        2.10626204
CRuns         0.63563136
CRBI          0.15183129
CWalks       -0.53395778
LeagueN      85.84990831
DivisionW   -72.62437940
PutOuts       0.32095315
Assists       0.08592847
Errors        0.01992083
NewLeagueN  -64.54422786
plot(enet_mdl, xvar="lambda", lwd=2, label=T)
abline(v=log(enet.cv$lambda.min), col="blue", lty=2)

As before, the vertial dashed line indicates the optimal value for \(\lambda\).

The following to graphs show a slightly nicer display of the coefficient paths, as well as the coefficients, sorted by magnitude, at the optimal value for lambda.

coefplot(enet_mdl, lambda=enet.cv$lambda.min, sort="magnitude")

coefpath(enet_mdl)

Comparing the Resulting Final Models

The following table compares the results of the above three models:

tmatrix <- model.matrix(Salary~.,test)[,-1]
ridge.pred = predict(ridge_mdl, s=ridge.cv$lambda.min, newx=tmatrix)
ridge_mse = mean((ridge.pred-test$Salary)^2)

lasso.pred = predict(lasso_mdl, s=lasso.cv$lambda.min, newx=tmatrix)
lasso_mse = mean((lasso.pred-test$Salary)^2)

enet.pred = predict(enet_mdl, s=enet.cv$lambda.min, newx=tmatrix)
enet_mse = mean((enet.pred-test$Salary)^2)



barplot( c(ridge_mse, lasso_mse, enet_mse, lm_mse), main="Mean Squared Error", names.arg=c('Ridge','Lasso','Elastic Net','Full'))

Method \(\lambda\) MSE
Ridge 29.9798498 1.908433510^{5}
Elastic Net 4.7517036 1.944481210^{5}
Lasso 3.1407363 1.950363810^{5}
Full Model na 3.356621710^{5}

Conclusion

Regularized variable selection techniques provide a way to perform bias-variance tradeoffs for multiple regression problems. The methods of Ridge Regression, Lasso and Elastic Net are forms of regularized variable selection, and are mathematically interrelated. The coefplot library contains several useful tools for visualizing regularized regression techniques.

Thanks for reading this post.

LS0tCnRpdGxlOiAiUmVndWxhcml6ZWQgTW9kZWwgU2VsZWN0aW9uIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYXV0aG9yOiAgIk1pbGVzIFBvcnRlciIKZGF0ZTogIE1hcmNoIDI4LCAyMDIwCi0tLQoKIyBJbnRyb2R1Y3Rpb24KClRoZSBwdXJwb3NlIG9mIHRoaXMgZG9jdW1lbnQgaXMgdG8gZGVtb25zdHJhdGUgdGhlIHRocmVlIGRpZmZlcmVudCB0eXBlcyBvZiByZWd1bGFyaXplZCByZWdyZXNzaW9uOiBFbGFzdGljIE5ldCwgTGFzc28gYW5kIFJpZGdlIFJlZ3Jlc3Npb24uICBXaGlsZSB0aGUgc3RhbmRhcmQgUiBnbG1uZXQgZnVuY3Rpb24gb2ZmZXJzIHNvbWUgdG9vbHMgZm9yIHZpc3VhbGl6aW5nIHRoZSByZXN1bHRzIG9mIHRoZXNlIHRlY2huaXF1ZXMsIHRoZSBjb2VmcGxvdCBwYWNrYWdlIGNvbnRhaW5zIHNldmVyYWwgdXNlZnVsIHRvb2xzIGZvciB2aXN1YWxpemF0aW9uIHRoYXQgYXJlIGEgYml0IGVhc2llciB0byBpbnRlcnByZXQuCgojIEJhc2ViYWxsIERhdGEKCkluIG9yZGVyIHRvIGlsbHVzdHJhdGUgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBhbmQgdGhlIGNvZWZwbG90IHBhY2thZ2UsIHdlIG5lZWQgc29tZSBkYXRhIG9uIHdoaWNoIHRvIHBlcmZvcm0gdmFyaWFibGUgc2VsZWN0aW9uLiAgVGhlIGRhdGEgZm9yIHRoaXMgZXhlcmNpc2Ugd2lsbCBiZSBtYWpvciBsZWFndWUgYmFzZWJhbGwgZGF0YSBmcm9tIHRoZSAxOTg2IGFuZCAxOTg3LiAgV2Ugd2lsbCBoYXZlIGEgbnVtYmVyIG9mIHByZWRpY3RpbmcgdmFyaWFibGVzLCBmb3IgZXhhbXBsZSBIaXRzLCBSdW5zLCBSQkksIEhvbWUgUnVucywgYW5kIHNldmVyYWwgb3RoZXJzLiAgRnJvbSB0aGlzIGRhdGEgd2Ugd2FudCB0byBidWlsZCBhIG1vZGVsIHRoYXQgY2FuIGhlbHAgdXMgcHJlZGljdCAob3IganVzdGlmeSkgYSBwbGF5ZXJzIHNhbGFyeSBiYXNlZCBvbiB0aGVpciBwZXJmb3JtYW5jZS4KCihSZWZlcmVuY2U6ICBKYW1lcywgRy4sIFdpdHRlbiwgRC4sIEhhc3RpZSwgVC4sIGFuZCBUaWJzaGlyYW5pLCBSLiAoMjAxMykgQW4gSW50cm9kdWN0aW9uIHRvIFN0YXRpc3RpY2FsIExlYXJuaW5nIHdpdGggYXBwbGljYXRpb25zIGluIFIsIHd3dy5TdGF0TGVhcm5pbmcuY29tLCBTcHJpbmdlci1WZXJsYWcsIE5ldyBZb3JrKQoKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0LnNlZWQoMTAwKQpsaWJyYXJ5KElTTFIpCm5hbWVzKEhpdHRlcnMpCkhpdHRlcnM8LW5hLm9taXQoSGl0dGVycykKYGBgCgpXZSB3aWxsIHdhbnQgdG8gZG8gc29tZSBjb21wYXJpc29uIG9mIG1vZGVscywgc28gd2Ugd2lsbCBzcGxpdCB0aGUgZGF0YSBpbnRvIHRlc3RpbmcgYW5kIHRyYWluaW5nIHNldHMuCgpgYGB7cn0KIyMgNzUlIG9mIHRoZSBzYW1wbGUgc2l6ZQpzbXBfc2l6ZSA8LSBmbG9vcigwLjc1ICogbnJvdyhIaXR0ZXJzKSkKCnRyYWluX2luZCA8LSBzYW1wbGUoc2VxX2xlbihucm93KEhpdHRlcnMpKSwgc2l6ZSA9IHNtcF9zaXplKQoKdHJhaW4gPC0gSGl0dGVyc1t0cmFpbl9pbmQsIF0KdGVzdCA8LSBIaXR0ZXJzWy10cmFpbl9pbmQsIF0KCmBgYAoKIyMgQmFzaWMgTW9kZWwgKE5vIFZhcmlhYmxlIFNlbGVjdGlvbikKCk5vdyB0aGF0IHdlIGhhdmUgc29tZSBkYXRhLCBsZXQncyByZXZlaXcgd2hhdCB3ZSB3YW50IHRvIGRvIHdpdGggcmVndWxhcml6ZWQgdmFyaWFibGUgc2VsZWN0aW9uLiAgSW4gYSBtdWx0aXBsZSByZWdyZXNzaW9uIHByb2JsZW0sIHdlIGhhdmUgbXVsdGlwbGUgcHJlZGljdG9ycyBhbmQgYSB0YXJnZXQgdmFyaWFibGUuICBJbiBSLCBpdCBpcyByZWxhdGl2ZWx5IGVhc3kgdG8gcHJlZm9ybSBtdWx0aXBsZSByZWdyZXNzaW9uIG9uIHRoaXMgZGF0YSB3aXRoIHRoZSBsbSBmdW5jdGlvbi4gIFRoZSByZXN1bHQgaXMgYSBzZXQgb2YgY29lZmZpY2llbnRzIHRoYXQgd2UgYXBwbHkgdG8gZWFjaCBwcmVkaWN0b3IuICBBbHNvLCB3ZSBhcmUgYWJsZSB0byB1c2UgcmVzaWR1YWwgYW5hbHlzaXMgdG8gZGV0ZXJtaW5lIHRoZSBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2Ugb2YgZWFjaCBwcmVkaWN0b3IgaW4gdGhlIG92ZXJhbGwgbW9kZWwuCgpgYGB7cn0KbWRsMSA8LSBsbShTYWxhcnl+LiwgZGF0YT10cmFpbikKc3VtbWFyeShtZGwxKQptZGwxLnByZWQgPC0gcHJlZGljdChtZGwxLCBuZXdfZGF0YT10ZXN0KQpsbV9tc2UgPC0gbWVhbigobWRsMS5wcmVkIC0gdGVzdCRTYWxhcnkpXjIpCmBgYAoKSW4gdGhlIGFib3ZlIG91dHB1dCwgd2UgY2FuIHNlZSB0aGUgdmFyaW91cyBjb2VmZmljaWVudHMsIGFuZCB0aGVpciBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UuIAoKIyMgQmlhcy1WYXJpYW5jZSBUcmFkZW9mZgoKTW9yZSBwcmVkaWN0b3IgdmFyaWFibGVzIGluIGEgbW9kZWwgd2lsbCB0eXBpY2FsbHkgcmVzdWx0IGluIGxvd2VyIGJpYXMsIGJ1dCBoaWdoZXIgdmFyaWFiaWxpdHkgb2YgbW9kZWwgcHJlZGljdGlvbnMuICBBIGNvbW1vbiB3YXkgdG8gdGhpbmsgYWJvdXQgdGhpcyBpcyBpbiB0ZXJtcyBvZiB0aGUgZ3JhcGhpYyBiZWxvdy4gIEEgbW9kZWwgdGhhdCBoYXMgbG90cyBvZiBwcmVkaWN0b3JzIHdvdWxkIGxpa2VseSByZXN1bHRzIGluIHRoZSB1cHBlciByaWdodCBoYW5kIGNvcm5lci4gIEEgbW9kZWwgd2l0aCBmZXcgcHJlZGljdG9ycyByZXN1bHRzIGluIHRoZSBsb3dlciBsZWZ0LiAgV2hlbiBwZXJmb3JtaW5nIHByZWRpY3Rpb25zLCBpdCBpcyBvZnRlbiBwcmVmZXJhYmxlIHRvIGhhdmUgbGVzcyB2YXJpYW5jZSBldmVuIGlmIHRoZXJlIGlzIHNvbWUgYmlhcy4KCiFbQmlhcyBhbmQgVmFyaWFuY2VdKGJpYXMtYW5kLXZhcmlhbmNlLmpwZykKClRoZSBiYXNlYmFsbCBzYWxhcnkgbW9kZWwgdGhhdCB3ZSBjcmVhdGVkIGFib3ZlIGhhcyBzZXZlcmFsIHByZWRpY3RvcnMuICBJdCdzIG1lYW4gc3F1YXJlZCBlcnJvciAoTVNFKSBpcyBgciBsbV9tc2VgLiAgTmV4dCwgd2Ugd2lsbCBsb29rIGF0IGNyZWF0aW5nIG1vZGVscyB1c2luZyByZWd1bGFyaXphdGlvbiB2YXJpYWJsZSBzZWxlY3Rpb24gdGVjaG5pcXVlcy4gIFdlIHdpbGwgdGhlbiBjb21wYXJlIG91ciByZWd1bGFyaXplZCBtb2RlbHMgd2l0aCB0aGUgZnVsbCBtb2RlbCBhYm92ZS4KCiMgTWF0aGVtYXRpY2FsIEZvcm11bGF0aW9uIG9mIFJlZ3VsYXJpemF0aW9uIFJlZ3Jlc3Npb24gVGVjaG5pcXVlcwoKUmVndWxhcml6ZWQgcmVncmVzc2lvbiB0ZWNobmlxdWVzIGluY2x1ZGUgTGFzc28sIFJpZGdlIFJlZ3Jlc3Npb24sIGFuZCBFbGFzdGljIE5ldC4gIEVsYXN0aWNuZXQgaXMgYSBnZW5lcmFsaXphdGlvbiBvZiB0aGUgb3RoZXIgdHdvLiAgTWF0aGVtYXRpY2FsbHksIHRoZXNlIHJlZ3Jlc3Npb24gdGVjaG5pcXVlcyBhbGwgbG9vayBsaWtlIHRoaXM6CgokJCBtaW4gXHN1bV97aT0xfV5uKHlfaS0oXGJldGFfMCtcYmV0YV8xeF97MSxpfStcYmV0YV8yeF97MixpfSsuLi4re1xiZXRhX254X3twLGl9fSkpXjIgJCQKU3ViamVjdCB0bzoKCiQkIFxsYW1iZGEoXGFscGhhXHN1bV97aT0xfV5wfFxiZXRhX2l8ICsgKDEtXGFscGhhKVxzdW1fe2k9MX1ecFxiZXRhX2leMilcbGVxXHRhdSAkJAoKV2Ugd2FudCBhIHZhbHVlIG9mICRcbGFtYmRhJCB0aGF0IG1pbmltaXplcyB0aGUgZmlyc3QgZXF1YXRpb24sIHN1YmplY3QgdG8gdGhlIHNlY29uZCBjb25zdHJhaW50LiAgCgpJbiBlZmZlY3QsIHdoYXQgd2UncmUgZG9pbmcgaGVyZSBpcyBsaW1pdGluZyBlaXRoZXIgdGhlIHN1bSBvZiB0aGUgYWJzb2x1dGUgdmFsdWUgb2YgdGhlIGNvZWZmaWNpZW50cywgb3Igb24gdGhlIHN1bSBvZiB0aGUgY29lZmZpY2llbnRzIHNxdWFyZWQuICBJbiB0aGUgYWJvdmUsIGlmIHdlIHNldCAkXGFscGhhPTAkLCB0aGUgYWJzb2x1dGUgdmFsdWUgdGVybSBnb2VzIGF3YXkgYW5kIHdlIGFyZSBsZWZ0IHdpdGggUmlkZ2UgUmVncmVzc2lvbi4gIFNpbWlsYXJseSwgaWYgd2Ugc2V0ICRcYWxwaGE9MSQsIHRoZW4gdGhlIHN1bSBvZiBzcXVhcmVkIGNvZWZmaWNpZW50cyB0ZXJtIGdvZXMgYXdheSBhbmQgd2UgYXJlIGxlZnQgd2l0aCBMYXNzby4gIElmIHdlIGNob29zZSBhbHBoYSBzb21ld2hlcmUgaW4gdGhlIG1pZGRsZSwgd2UgaGF2ZSBFbGFzdGljIE5ldC4gIE9uZSBvZiB0aGUgbW9yZSBzdWJ0bGUgcG9pbnRzIG9mIHRoZSBhYm92ZSBpcyB0aGF0IFJpZGdlIHJlZ3Jlc3Npb24gYWN0dWFsbHkgZG9lcyBub3QgZW5kIHVwIGVsaW1pbmF0aW5nIHZhcmlhYmxlcy4gIExhc3NvLCBvbiB0aGUgb3RoZXIgaGFuZCwgd2lsbCByZWR1Y2Ugc29tZSBvZiB0aGUgY29lZmZpY2llbnRzIHRvIDAuICBUaGlzIGlzIHRoZSByZXN1bHQgb2YgdGhlICRMXzEkIGFuZCAkTF8yJCBtZXRyaWNzIHVzZWQgaW4gTGFzc28gYW5kIFJpZGdlIHJlc3BlY3RpdmVseS4KCiMgRGV0ZXJtaW5pbmcgJFxhbHBoYSQgYW5kICRcbGFtYmRhJAoKU28sIGhvdyBkbyB3ZSBkZXRlcm1pbmUgdGhlICRcYWxwaGEkIGFuZCAkXGxhbWJkYSQgdmFsdWVzIHVzZWQgaW4gRWxhc3RpYyBOZXQ/CgpFc3NlbnRpYWxseSwgJFxhbHBoYSQgaXMgbGlrZSBhIHN3aXRjaCB0aGF0IGNhbiB0dXJuIEVsYXN0aWMgTmV0IGludG8gUmlkZ2Ugb3IgTGFzc28sIG9yIGEgY29tYmluYXRpb24uICBXZSBjYW4gc2ltcGx5IGNob29zZSAkXGFscGhhJC4gIE9uY2Ugd2UgaGF2ZSBkZWNpZGVkIG9uIGFuIGFwcHJvYWNoLCB3ZSBhcmUgbGVmdCB3aXRoIGZpbmRpbmcgYW4gb3B0bWlhbCB2YWx1ZSBmb3IgJFxsYW1iZGEkLgoKUGlja2luZyAkXGxhbWJkYSQgY2FuIGJlIGRvbmUgdXNpbmcgYSBjcm9zcyB2YWxpZGF0aW9uIGFwcHJvYWNoLiAgSW4gdGhpcyBhcHByb2FjaCwgd2UgZGl2aWRlIHVwIHRoZSB0cmFpbmluZyBkYXRhIGZvciBvdXIgbW9kZWwgaW50byBhIG51bWJlciBvZiBmb2xkcy4gIFRoZSBmb2xsb3dpbmcgZGlhZ3JhbSBzaG93cyBob3cgd2UgdXNlIG9uZSBvZiB0aGUgZm9sZHMgYWdhaW5zdCB0aGUgcmVtYWluaW5nIGRhdGEgaW4gb3JkZXIgdG8gZXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBtb2RlbC4gIFdlIGNhbiBjb250aW51ZSB0byBkbyB0aGlzIHdpdGggYSByYW5nZSBvZiAkXGxhbWJkYSQgdmFsdWVzIHRvIGRldGVybWluZSB0aGUgYmVzdCB2YWx1ZSBvZiAkXGxhbWJkYSQgYmFzZWQgb24gc29tZSBhY2N1cmFjeSBtZXRyaWMuICAoVHlwaWNhbGx5IHRoZSByZXNpZHVhbCBzcXVhcmVkIHN1bSBvciBSU1MuKSAgTm90ZTogIEFzIHdlIHBlcmZvcm0gY3Jvc3MgdmFsaWRhdGlvbiwgd2Ugd2lsbCBiZSBkb2luZyBzbyB3aXRoaW4gdGhlIHRyYWluaW5nIHNldC4gIFdlIGtlZXAgdGhlIHRlc3QgZGF0YSBzZXBhcmF0ZSB1bnRpbCB3ZSB3aXNoIHRvIGNvbXBhcmUgYWxsIG91ciBtb2RlbHMuCgohWzEwIEZvbGQgQ3Jvc3MgVmFsaWRhdGlvbl0oY3YucG5nKQoKIyMgUmlkZ2UgUmVncmVzc2lvbiBpbiBSCgpUaGUgZm9sbG93aW5nIGNvZGUgcGVyZm9ybXMgYSByaWRnZSByZWdyZXNzaW9uIG9uIHRoZSBIaXR0ZXJzIGRhdGEgdHJhaW5pbmcgc2V0LgoKYGBge3J9CmxpYnJhcnkoZ2xtbmV0KSAKbGlicmFyeShjb2VmcGxvdCkKc2V0LnNlZWQoMTAwKQpzdGQgPC0gVFJVRSAgIyBSZXF1aXJlZCBmb3IgTGFzc28KWSA8LSB0cmFpbiRTYWxhcnkKWCA8LSBtb2RlbC5tYXRyaXgoU2FsYXJ5fi4sdHJhaW4pWywtMV0KIyAxMC1mb2xkIENWIHRvIGZpbmQgdGhlIG9wdGltYWwgbGFtYmRhIApyaWRnZS5jdj1jdi5nbG1uZXQoWCwgWSwgYWxwaGE9MCwgdHlwZT0iZGV2aWFuY2UiLCBmYW1pbHk9ImdhdXNzaWFuIiwgc3RhbmRhcmRpemU9c3RkLCBuZm9sZHM9MTApCiMjIEZpdCBsYXNzbyBtb2RlbCB3aXRoIDEwMCB2YWx1ZXMgZm9yIGxhbWJkYQpyaWRnZV9tZGwgPSBnbG1uZXQoWCwgWSwgYWxwaGE9MCwgbmxhbWJkYT0xMDAsIHN0YW5kYXJkaXplPXN0ZCwgZmFtaWx5PSJnYXVzc2lhbiIpCiMjIEV4dHJhY3QgY29lZmZpY2llbnRzIGF0IG9wdGltYWwgbGFtYmRhCmNvZWYocmlkZ2VfbWRsLHM9cmlkZ2UuY3YkbGFtYmRhLm1pbikKcGxvdChyaWRnZV9tZGwsIHh2YXI9ImxhbWJkYSIsIGx3ZD0yLCBsYWJlbD1UKQphYmxpbmUodj1sb2cocmlkZ2UuY3YkbGFtYmRhLm1pbiksIGNvbD0iYmx1ZSIsIGx0eT0yKQpgYGAKClRoZSByZXN1bHRpbmcgdmFsdWUgb2YgbGFtYmRhIGZvciB0aGlzIHZhcmlhYmxlIHNlbGVjdGlvbiBtZXRob2QgaXMgYHIgcmlkZ2UuY3YkbGFtYmRhLm1pbmAKClRvIGdldCBhIHNlbnNlIG9mIGhvdyB0aGUgY29kZSBzZXR0bGVkIG9uIHRoaXMgdmFsdWUgb2YgbGFtYmRhLCB3ZSBjYW4gdGFrZSBhIGxvb2sgYXQgdGhlIGZvbGxvd2luZyBwbG90cy4gIFRoZSBmaXJzdCAKc2hvd3MgaG93IHRoZSBjb2VmZmljaWVudHMgY2hhbmdlIGFzIHRoZSBsb2cgb2YgbGFtYmRhIGNoYW5nZS4gIFRoZSBzZWNvbmQgc2hvd3MgYSBwbG90IG9mIHRoZSBjb2VmZmljaWVudHMgc29ydGVkIGJ5IG1hZ25pdHVkZSBhdCB0aGUgb3B0aW1hbCBsYW1iZGEgdmFsdWUuCgpgYGB7cn0KY29lZnBhdGgocmlkZ2VfbWRsKQpjb2VmcGxvdChyaWRnZV9tZGwsIGxhbWJkYT1yaWRnZS5jdiRsYW1iZGEubWluLCBzb3J0PSJtYWduaXR1ZGUiKQpgYGAKCiMjIExhc3NvIFJlZ3Jlc3Npb24gaW4gUgoKV2UgY2FuIHJlcGVhdCB0aGUgc2FtZSBzdGVwcyBiZWxvdyBmb3IgTGFzc28gcmVncmVzc2lvbi4gIFRoaXMgdGltZSB3ZSBzZXQgdGhlIHZhbHVlIG9mICRcYWxwaGEkIHRvIDEgdG8gZm9yY2UgdGhlIG1vZGVsIHRvIHVzZSBMYXNzby4gIAoKYGBge3J9CiMgMTAtZm9sZCBDViB0byBmaW5kIHRoZSBvcHRpbWFsIGxhbWJkYSAKbGFzc28uY3Y9Y3YuZ2xtbmV0KFgsWSxhbHBoYT0xLCB0eXBlPSJkZXZpYW5jZSIsIGZhbWlseT0iZ2F1c3NpYW4iLCBzdGFuZGFyZGl6ZT1zdGQsIG5mb2xkcz0xMCkKIyMgRml0IGxhc3NvIG1vZGVsIHdpdGggMTAwIHZhbHVlcyBmb3IgbGFtYmRhCmxhc3NvX21kbCA9IGdsbW5ldChYLFksYWxwaGE9MSxmYW1pbHk9ImdhdXNzaWFuIiwgc3RhbmRhcmRpemU9c3RkLCBubGFtYmRhPTEwMCkKIyMgRXh0cmFjdCBjb2VmZmljaWVudHMgYXQgb3B0aW1hbCBsYW1iZGEKY29lZihsYXNzb19tZGwscz1sYXNzby5jdiRsYW1iZGEubWluKQpwbG90KGxhc3NvX21kbCwgeHZhcj0ibGFtYmRhIiwgbHdkPTIsIGxhYmVsPVQpCmFibGluZSh2PWxvZyhsYXNzby5jdiRsYW1iZGEubWluKSwgY29sPSJibHVlIiwgbHR5PTIpCmBgYAoKVGhlIHZlcnRpY2FsIGxpbmUgaW4gdGhlIGFib3ZlIHBsb3QgaW5kaWNhdGVzIHRoZSBvcHRpbWFsIHZhbHVlIGZvciBsYW1iZGEuCgpUaGUgZm9sbG93aW5nIHR3byBwbG90cyBhcmUgc2ltaWxhciB0byB0aGUgb25lcyB3ZSB2aWV3ZWQgYWJvdmUuICBUaGlzIHNob3dzIGEgc2xpZ2h0bHkgbmljZXIgZGlzcGxheSBvZiB0aGUgY29lZmZpY2llbnRzICJwYXRoIiwgYW5kIHRoZSBzb3J0ZWQgbWFnbml0dWRlIG9mIHRoZSBjb2VmZmljaWVudHMgYXQgdGhlIG9wdGltYWwgbGFtYmRhLiAKCmBgYHtyfQpjb2VmcGF0aChsYXNzb19tZGwpCmNvZWZwbG90KGxhc3NvX21kbCwgbGFtYmRhPWxhc3NvLmN2JGxhbWJkYS5taW4sIHNvcnQ9Im1hZ25pdHVkZSIpCmBgYAoKIyMgRWxhc3RpYyBOZXQgUmVncmVzc2lvbiBpbiBSCgpGaW5hbGx5LCB3ZSB3aWxsIHBlcmZvcm0gRWxhc3RpYyBOZXQgcmVncmVzc2lvbiBvbiB0aGUgZGF0YS4gIFdlIGNob29zZSAkXGFscGhhPTAuNSQgaW4gdGhpcyBjYXNlLgoKYGBge3J9CiMgMTAtZm9sZCBDViB0byBmaW5kIHRoZSBvcHRpbWFsIGxhbWJkYSAKZW5ldC5jdj1jdi5nbG1uZXQoWCxZLGFscGhhPTAuNSwgdHlwZT0iZGV2aWFuY2UiLCBmYW1pbHk9ImdhdXNzaWFuIiwgc3RhbmRhcmRpemU9c3RkLCBuZm9sZHM9MTApCiMjIEZpdCBsYXNzbyBtb2RlbCB3aXRoIDEwMCB2YWx1ZXMgZm9yIGxhbWJkYQplbmV0X21kbCA9IGdsbW5ldChYLFksYWxwaGE9MC41LHN0YW5kYXJkaXplPXN0ZCxubGFtYmRhPTEwMCkKIyMgRXh0cmFjdCBjb2VmZmljaWVudHMgYXQgb3B0aW1hbCBsYW1iZGEKY29lZihlbmV0X21kbCxzPWVuZXQuY3YkbGFtYmRhLm1pbikKcGxvdChlbmV0X21kbCwgeHZhcj0ibGFtYmRhIiwgbHdkPTIsIGxhYmVsPVQpCmFibGluZSh2PWxvZyhlbmV0LmN2JGxhbWJkYS5taW4pLCBjb2w9ImJsdWUiLCBsdHk9MikKYGBgCgpBcyBiZWZvcmUsIHRoZSB2ZXJ0aWFsIGRhc2hlZCBsaW5lIGluZGljYXRlcyB0aGUgb3B0aW1hbCB2YWx1ZSBmb3IgJFxsYW1iZGEkLgoKVGhlIGZvbGxvd2luZyB0byBncmFwaHMgc2hvdyBhIHNsaWdodGx5IG5pY2VyIGRpc3BsYXkgb2YgdGhlIGNvZWZmaWNpZW50IHBhdGhzLCBhcyB3ZWxsIGFzIHRoZSBjb2VmZmljaWVudHMsIHNvcnRlZCBieSBtYWduaXR1ZGUsIGF0IHRoZSBvcHRpbWFsIHZhbHVlIGZvciBsYW1iZGEuCgpgYGB7cn0KY29lZnBsb3QoZW5ldF9tZGwsIGxhbWJkYT1lbmV0LmN2JGxhbWJkYS5taW4sIHNvcnQ9Im1hZ25pdHVkZSIpCmNvZWZwYXRoKGVuZXRfbWRsKQpgYGAKCiMjIENvbXBhcmluZyB0aGUgUmVzdWx0aW5nIEZpbmFsIE1vZGVscwoKVGhlIGZvbGxvd2luZyB0YWJsZSBjb21wYXJlcyB0aGUgcmVzdWx0cyBvZiB0aGUgYWJvdmUgdGhyZWUgbW9kZWxzOgoKYGBge3J9CnRtYXRyaXggPC0gbW9kZWwubWF0cml4KFNhbGFyeX4uLHRlc3QpWywtMV0KcmlkZ2UucHJlZCA9IHByZWRpY3QocmlkZ2VfbWRsLCBzPXJpZGdlLmN2JGxhbWJkYS5taW4sIG5ld3g9dG1hdHJpeCkKcmlkZ2VfbXNlID0gbWVhbigocmlkZ2UucHJlZC10ZXN0JFNhbGFyeSleMikKCmxhc3NvLnByZWQgPSBwcmVkaWN0KGxhc3NvX21kbCwgcz1sYXNzby5jdiRsYW1iZGEubWluLCBuZXd4PXRtYXRyaXgpCmxhc3NvX21zZSA9IG1lYW4oKGxhc3NvLnByZWQtdGVzdCRTYWxhcnkpXjIpCgplbmV0LnByZWQgPSBwcmVkaWN0KGVuZXRfbWRsLCBzPWVuZXQuY3YkbGFtYmRhLm1pbiwgbmV3eD10bWF0cml4KQplbmV0X21zZSA9IG1lYW4oKGVuZXQucHJlZC10ZXN0JFNhbGFyeSleMikKCgoKYmFycGxvdCggYyhyaWRnZV9tc2UsIGxhc3NvX21zZSwgZW5ldF9tc2UsIGxtX21zZSksIG1haW49Ik1lYW4gU3F1YXJlZCBFcnJvciIsIG5hbWVzLmFyZz1jKCdSaWRnZScsJ0xhc3NvJywnRWxhc3RpYyBOZXQnLCdGdWxsJykpCmBgYAoKfCBNZXRob2QgfCAkXGxhbWJkYSQgIHwgTVNFIHwKfC0tLS0tLS0tfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwKfCBSaWRnZSAgfCBgciByaWRnZS5jdiRsYW1iZGEubWluYCB8IGByIHJpZGdlX21zZWAgfAp8IEVsYXN0aWMgTmV0IHwgYHIgZW5ldC5jdiRsYW1iZGEubWluYCB8IGByIGVuZXRfbXNlYCAKfCBMYXNzbyB8IGByIGxhc3NvLmN2JGxhbWJkYS5taW5gIHwgYHIgbGFzc29fbXNlYCB8CnwgRnVsbCBNb2RlbCB8IG5hIHwgYHIgbG1fbXNlYCB8CgoKIyBDb25jbHVzaW9uCgpSZWd1bGFyaXplZCB2YXJpYWJsZSBzZWxlY3Rpb24gdGVjaG5pcXVlcyBwcm92aWRlIGEgd2F5IHRvIHBlcmZvcm0gYmlhcy12YXJpYW5jZSB0cmFkZW9mZnMgZm9yIG11bHRpcGxlIHJlZ3Jlc3Npb24gcHJvYmxlbXMuICBUaGUgbWV0aG9kcyBvZiBSaWRnZSBSZWdyZXNzaW9uLCBMYXNzbyBhbmQgRWxhc3RpYyBOZXQgYXJlIGZvcm1zIG9mIHJlZ3VsYXJpemVkIHZhcmlhYmxlIHNlbGVjdGlvbiwgYW5kIGFyZSBtYXRoZW1hdGljYWxseSBpbnRlcnJlbGF0ZWQuICBUaGUgY29lZnBsb3QgbGlicmFyeSBjb250YWlucyBzZXZlcmFsIHVzZWZ1bCB0b29scyBmb3IgdmlzdWFsaXppbmcgcmVndWxhcml6ZWQgcmVncmVzc2lvbiB0ZWNobmlxdWVzLgoKVGhhbmtzIGZvciByZWFkaW5nIHRoaXMgcG9zdC4KCg==