Introduction

Predictive modeling is about using algorithms to learn from history and make predictions for the future. The ultimate goal is to create a model that generalizes well to future data. However, in practice, it’s not always easy to achieve this goal. One common challenge is overfitting. This occurs when a model is too complex, and it fits the training data too closely. As a result, it doesn’t generalize well to new data. Regularization is one technique to address this problem. In this post, we’ll discuss what regularization is, what it’s used for, and how it works.

Bias, Variance, and Generalization Performance

Before we dive into regularization, it’s critical to understand some key concepts in predictive modeling. One of these is bias, which means how well a model fits the training data. If a model has high bias, it often indicates it’s too simple and underfits the data. Another concept is variance, which refers to how much the model’s predictions vary when applied to training and test data sets. If a model has high variance, it often means it’s too complex and overfits the training data. That is, the model tries to explain not only the true patterns in the population, but also the noises in the training data. Then, it works poorly as the test set noises are different. Generalization performance of a model refers to how well it performs on new, unseen data. Ideally, we want a model with low bias, low variance, and high generalization performance.

Bias Variance Trade Off

Models with high bias are too simple and do not capture the underlying patterns in the data, while models with high variance are overly complex and capture too much of the noise in the data. Thus, there is a trade-off between bias and variance, and finding the right balance between the two is crucial to achieving the best generalization performance. When the bias is small, the variance tends to be high, and vice versa.

The following chart shows that we should balance the bias and variance to minimize the total error (bias + variance), so as to ensure that the model is neither too simple nor too complex and can generalize well to new data.

What Is Regularization?

Regularization is a technique used to prevent overfitting by adding a penalty term to the model’s cost function. The cost function is a mathematical function that measures how well the model fits the training data. By adding a penalty term to the cost function, we can increase bias and decrease variance. The idea is, we want to make sure the model doesn’t fit the training data too closely with a very small bias. When the bias is not that small, the variance could not be that large. Thus, the decreased variance will help us prevent overfitting and improves model generalization performance. In practice, regularization is often used in scenarios where there’s a risk of overfitting. This is, for example, when there’s a large number of predictors or when the data is noisy.

L1 and L2 Regularization

L1 and L2 regularization are two of the most popular regularization techniques used in machine learning. L1 regularization (also called Lasso) adds a penalty term to the model’s cost function that is proportional to the absolute value of the predictor coefficients. This shrinks some of the coefficients to exactly zero, which makes the model more interpretable and helps with predictor selection. L2 regularization (also called Ridge), on the other hand, adds a penalty term proportional to the square of the coefficients. This shrinks all coefficients, but not necessarily to zero. This can help reduce the impact of outliers in the data.

R Example

In this demo, we will load the Boston Housing Data Set used in our Data Mining course, and predict the house pricing, MEDV, using linear regression (LR) and LR with L1 regularization.

df <- read.csv("./BostonHousing.csv")
head(df)

The data dictionary is:

Column Description
CRIM Per capita crime rate by town
ZN Proportion of residential land zoned for lots over 25,000 ft2
INDUS Proportion of nonretail business acres per town
CHAS Charles River dummy variable (= 1 if tract bounds river; = o otherwise)
NOX Nitric oxide concentration (parts per 10 million)
RM Average number of rooms per dwelling
AGE Proportion of owner-occupied units built prior to 1940
DIS Weighted distances to five Boston employment centers
RAD Index of accessibility to radial highways
TAX Full-value property-tax rate per $10,000
PTRATIO Pupil/teacher ratio by town
LSTAT Percentage lower status of the population
MEDV Median value of owner-occupied homes in Woos

Split the data into 80% for training and 20% for validation.

n <- nrow(df)
training.index <- sample(1:n, n*0.8)

set.seed(1)
df.train  <- df[training.index, ]
df.valid <- df[-training.index, ]

Train a linear regression model

lr <- lm(MEDV ~ ., data = df.train)
summary(lr)

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

Residuals:
     Min       1Q   Median       3Q      Max 
-15.9981  -2.7536  -0.7734   1.8649  26.4076 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  43.168498   5.750844   7.506 4.15e-13 ***
CRIM         -0.119151   0.034463  -3.457 0.000605 ***
ZN            0.049954   0.015507   3.221 0.001382 ** 
INDUS         0.017856   0.071050   0.251 0.801702    
CHAS          3.501406   0.961254   3.643 0.000306 ***
NOX         -18.990992   4.451848  -4.266 2.50e-05 ***
RM            3.777120   0.476313   7.930 2.32e-14 ***
AGE           0.002467   0.014762   0.167 0.867361    
DIS          -1.626294   0.230420  -7.058 7.75e-12 ***
RAD           0.290986   0.074049   3.930 0.000101 ***
TAX          -0.013343   0.004264  -3.129 0.001887 ** 
PTRATIO      -1.013125   0.148759  -6.811 3.68e-11 ***
LSTAT        -0.557248   0.056807  -9.810  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.827 on 391 degrees of freedom
Multiple R-squared:  0.7491,    Adjusted R-squared:  0.7414 
F-statistic: 97.29 on 12 and 391 DF,  p-value: < 2.2e-16

The performances of the train and validation sets are as follows:

library(forecast)

accuracy(predict(lr, df.train), df.train$MEDV)
                    ME     RMSE      MAE       MPE     MAPE
Test set -2.551424e-14 4.748246 3.412394 -3.366753 17.22943
accuracy(predict(lr, df.valid), df.valid$MEDV)
                 ME     RMSE      MAE       MPE     MAPE
Test set 0.01038326 4.757182 3.356853 -1.859107 17.06516

Train a linear regression with L1 regularization (Lasso)

First, define the response variable, y, and the matrix of predictor variables, x.

y <- df.train$MEDV
x <- as.matrix(df.train[,-ncol(df)])

Next, we’ll use the glmnet() function to fit the lasso regression model and specify alpha=1. Note that setting alpha equal to 0 is equivalent to using ridge regression and setting alpha to some value between 0 and 1 is equivalent to using an elastic net, which is a mixture of lasso and ridge.

To determine what value to use for lambda, which controls the strength of the penalty applied to the model’s coefficients, we’ll perform k-fold cross-validation and identify the lambda value, that produces the lowest test mean squared error (MSE).

Note that the function cv.glmnet() automatically performs k-fold cross validation using k = 10 folds.

library(glmnet)

#perform k-fold cross-validation to find optimal lambda value
lr.lasso.cv <- cv.glmnet(x, y, alpha = 1)

#find optimal lambda value that minimizes cross validation's test MSE
best_lambda <- lr.lasso.cv$lambda.min
best_lambda
[1] 0.03156218

Let’s find coefficients of best model

best_model <- glmnet(x, y, alpha = 1, lambda = best_lambda)
coef(best_model)
13 x 1 sparse Matrix of class "dgCMatrix"
                      s0
(Intercept)  40.59657058
CRIM         -0.10942610
ZN            0.04462373
INDUS         .         
CHAS          3.49727203
NOX         -17.17029966
RM            3.86573121
AGE           .         
DIS          -1.53216297
RAD           0.23699402
TAX          -0.01080162
PTRATIO      -0.98895684
LSTAT        -0.55167511

You will notice that the coefficients obtained in the early linear regression model are similar to those in our current lasso model. However, it is important to highlight that the coefficient for the predictor variable “INDUS” and “AGE” were found to be zero and therefore it was removed from the model. When dealing with a large number of predictors, Lasso regression is a valuable technique for selecting the most relevant predictors.

We also can check the performance of this lasso model:

pred.train <- as.vector(predict(best_model, s = best_lambda, newx = x))
valid_x <- as.matrix(df.valid[,-ncol(df)])
pred.valid <- as.vector(predict(best_model, s = best_lambda, newx = valid_x))

accuracy(pred.train, df.train$MEDV)
                   ME     RMSE      MAE       MPE     MAPE
Test set 1.946518e-14 4.753174 3.384511 -3.525581 17.08237
accuracy(pred.valid, df.valid$MEDV)
                 ME     RMSE      MAE       MPE     MAPE
Test set 0.02692724 4.731938 3.339242 -1.831327 17.01515

The validation performance appears to show a slight improvement after applying L1 regularization. However, note that a more significant enhancement in generalization performance is usually observed when dealing with more complex data.

Conclusion

Regularization is a powerful technique that can prevent overfitting and improve model generalization performance. By introducing bias, we can encourage the model to have smaller coefficients, which makes it less complex and less likely to overfit. L1 and L2 regularization are two of the most common regularization types, and they have different strengths and weaknesses.

L1 and L2 regularization can be applied to many machine learning algorithms, such as linear regression, logistic regression, support vector machines, and neural networks, among others.

LS0tCnRpdGxlOiAiUmVndWxhcml6YXRpb24iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyBJbnRyb2R1Y3Rpb24gCgpQcmVkaWN0aXZlIG1vZGVsaW5nIGlzIGFib3V0IHVzaW5nIGFsZ29yaXRobXMgdG8gbGVhcm4gZnJvbSBoaXN0b3J5IGFuZCBtYWtlIHByZWRpY3Rpb25zIGZvciB0aGUgZnV0dXJlLiBUaGUgdWx0aW1hdGUgZ29hbCBpcyB0byBjcmVhdGUgYSBtb2RlbCB0aGF0IGdlbmVyYWxpemVzIHdlbGwgdG8gZnV0dXJlIGRhdGEuIEhvd2V2ZXIsIGluIHByYWN0aWNlLCBpdCdzIG5vdCBhbHdheXMgZWFzeSB0byBhY2hpZXZlIHRoaXMgZ29hbC4gT25lIGNvbW1vbiBjaGFsbGVuZ2UgaXMgb3ZlcmZpdHRpbmcuIFRoaXMgb2NjdXJzIHdoZW4gYSBtb2RlbCBpcyB0b28gY29tcGxleCwgYW5kIGl0IGZpdHMgdGhlIHRyYWluaW5nIGRhdGEgdG9vIGNsb3NlbHkuIEFzIGEgcmVzdWx0LCBpdCBkb2Vzbid0IGdlbmVyYWxpemUgd2VsbCB0byBuZXcgZGF0YS4gUmVndWxhcml6YXRpb24gaXMgb25lIHRlY2huaXF1ZSB0byBhZGRyZXNzIHRoaXMgcHJvYmxlbS4gSW4gdGhpcyBwb3N0LCB3ZSdsbCBkaXNjdXNzIHdoYXQgcmVndWxhcml6YXRpb24gaXMsIHdoYXQgaXQncyB1c2VkIGZvciwgYW5kIGhvdyBpdCB3b3Jrcy4KCiMjIyBCaWFzLCBWYXJpYW5jZSwgYW5kIEdlbmVyYWxpemF0aW9uIFBlcmZvcm1hbmNlIAoKQmVmb3JlIHdlIGRpdmUgaW50byByZWd1bGFyaXphdGlvbiwgaXQncyBjcml0aWNhbCB0byB1bmRlcnN0YW5kIHNvbWUga2V5IGNvbmNlcHRzIGluIHByZWRpY3RpdmUgbW9kZWxpbmcuIE9uZSBvZiB0aGVzZSBpcyBiaWFzLCB3aGljaCBtZWFucyBob3cgd2VsbCBhIG1vZGVsIGZpdHMgdGhlIHRyYWluaW5nIGRhdGEuIElmIGEgbW9kZWwgaGFzIGhpZ2ggYmlhcywgaXQgb2Z0ZW4gaW5kaWNhdGVzIGl0J3MgdG9vIHNpbXBsZSBhbmQgdW5kZXJmaXRzIHRoZSBkYXRhLiBBbm90aGVyIGNvbmNlcHQgaXMgdmFyaWFuY2UsIHdoaWNoIHJlZmVycyB0byBob3cgbXVjaCB0aGUgbW9kZWwncyBwcmVkaWN0aW9ucyB2YXJ5IHdoZW4gYXBwbGllZCB0byB0cmFpbmluZyBhbmQgdGVzdCBkYXRhIHNldHMuIElmIGEgbW9kZWwgaGFzIGhpZ2ggdmFyaWFuY2UsIGl0IG9mdGVuIG1lYW5zIGl0J3MgdG9vIGNvbXBsZXggYW5kIG92ZXJmaXRzIHRoZSB0cmFpbmluZyBkYXRhLiBUaGF0IGlzLCB0aGUgbW9kZWwgdHJpZXMgdG8gZXhwbGFpbiBub3Qgb25seSB0aGUgdHJ1ZSBwYXR0ZXJucyBpbiB0aGUgcG9wdWxhdGlvbiwgYnV0IGFsc28gdGhlIG5vaXNlcyBpbiB0aGUgdHJhaW5pbmcgZGF0YS4gVGhlbiwgaXQgd29ya3MgcG9vcmx5IGFzIHRoZSB0ZXN0IHNldCBub2lzZXMgYXJlIGRpZmZlcmVudC4gR2VuZXJhbGl6YXRpb24gcGVyZm9ybWFuY2Ugb2YgYSBtb2RlbCByZWZlcnMgdG8gaG93IHdlbGwgaXQgcGVyZm9ybXMgb24gbmV3LCB1bnNlZW4gZGF0YS4gSWRlYWxseSwgd2Ugd2FudCBhIG1vZGVsIHdpdGggbG93IGJpYXMsIGxvdyB2YXJpYW5jZSwgYW5kIGhpZ2ggZ2VuZXJhbGl6YXRpb24gcGVyZm9ybWFuY2UuCgojIyMgQmlhcyBWYXJpYW5jZSBUcmFkZSBPZmYgCgpNb2RlbHMgd2l0aCBoaWdoIGJpYXMgYXJlIHRvbyBzaW1wbGUgYW5kIGRvIG5vdCBjYXB0dXJlIHRoZSB1bmRlcmx5aW5nIHBhdHRlcm5zIGluIHRoZSBkYXRhLCB3aGlsZSBtb2RlbHMgd2l0aCBoaWdoIHZhcmlhbmNlIGFyZSBvdmVybHkgY29tcGxleCBhbmQgY2FwdHVyZSB0b28gbXVjaCBvZiB0aGUgbm9pc2UgaW4gdGhlIGRhdGEuIFRodXMsIHRoZXJlIGlzIGEgdHJhZGUtb2ZmIGJldHdlZW4gYmlhcyBhbmQgdmFyaWFuY2UsIGFuZCBmaW5kaW5nIHRoZSByaWdodCBiYWxhbmNlIGJldHdlZW4gdGhlIHR3byBpcyBjcnVjaWFsIHRvIGFjaGlldmluZyB0aGUgYmVzdCBnZW5lcmFsaXphdGlvbiBwZXJmb3JtYW5jZS4gV2hlbiB0aGUgYmlhcyBpcyBzbWFsbCwgdGhlIHZhcmlhbmNlIHRlbmRzIHRvIGJlIGhpZ2gsIGFuZCB2aWNlIHZlcnNhLiAKClRoZSBmb2xsb3dpbmcgY2hhcnQgc2hvd3MgdGhhdCB3ZSBzaG91bGQgYmFsYW5jZSB0aGUgYmlhcyBhbmQgdmFyaWFuY2UgdG8gbWluaW1pemUgdGhlIHRvdGFsIGVycm9yIChiaWFzICsgdmFyaWFuY2UpLCBzbyBhcyB0byBlbnN1cmUgdGhhdCB0aGUgbW9kZWwgaXMgbmVpdGhlciB0b28gc2ltcGxlIG5vciB0b28gY29tcGxleCBhbmQgY2FuIGdlbmVyYWxpemUgd2VsbCB0byBuZXcgZGF0YS4KCjxpbWcgc3JjPSIuL2Vycm9yLmpwZWciIHdpZHRoPSI2MDAiLz4KCgojIyMgV2hhdCBJcyBSZWd1bGFyaXphdGlvbj8gCgpSZWd1bGFyaXphdGlvbiBpcyBhIHRlY2huaXF1ZSB1c2VkIHRvIHByZXZlbnQgb3ZlcmZpdHRpbmcgYnkgYWRkaW5nIGEgcGVuYWx0eSB0ZXJtIHRvIHRoZSBtb2RlbCdzIGNvc3QgZnVuY3Rpb24uIFRoZSBjb3N0IGZ1bmN0aW9uIGlzIGEgbWF0aGVtYXRpY2FsIGZ1bmN0aW9uIHRoYXQgbWVhc3VyZXMgaG93IHdlbGwgdGhlIG1vZGVsIGZpdHMgdGhlIHRyYWluaW5nIGRhdGEuIEJ5IGFkZGluZyBhIHBlbmFsdHkgdGVybSB0byB0aGUgY29zdCBmdW5jdGlvbiwgd2UgY2FuIGluY3JlYXNlIGJpYXMgYW5kIGRlY3JlYXNlIHZhcmlhbmNlLiBUaGUgaWRlYSBpcywgd2Ugd2FudCB0byBtYWtlIHN1cmUgdGhlIG1vZGVsIGRvZXNuJ3QgZml0IHRoZSB0cmFpbmluZyBkYXRhIHRvbyBjbG9zZWx5IHdpdGggYSB2ZXJ5IHNtYWxsIGJpYXMuIFdoZW4gdGhlIGJpYXMgaXMgbm90IHRoYXQgc21hbGwsIHRoZSB2YXJpYW5jZSBjb3VsZCBub3QgYmUgdGhhdCBsYXJnZS4gVGh1cywgdGhlIGRlY3JlYXNlZCB2YXJpYW5jZSB3aWxsIGhlbHAgdXMgcHJldmVudCBvdmVyZml0dGluZyBhbmQgaW1wcm92ZXMgbW9kZWwgZ2VuZXJhbGl6YXRpb24gcGVyZm9ybWFuY2UuIEluIHByYWN0aWNlLCByZWd1bGFyaXphdGlvbiBpcyBvZnRlbiB1c2VkIGluIHNjZW5hcmlvcyB3aGVyZSB0aGVyZSdzIGEgcmlzayBvZiBvdmVyZml0dGluZy4gVGhpcyBpcywgZm9yIGV4YW1wbGUsIHdoZW4gdGhlcmUncyBhIGxhcmdlIG51bWJlciBvZiBwcmVkaWN0b3JzIG9yIHdoZW4gdGhlIGRhdGEgaXMgbm9pc3kuCgojIyMgTDEgYW5kIEwyIFJlZ3VsYXJpemF0aW9uCgpMMSBhbmQgTDIgcmVndWxhcml6YXRpb24gYXJlIHR3byBvZiB0aGUgbW9zdCBwb3B1bGFyIHJlZ3VsYXJpemF0aW9uIHRlY2huaXF1ZXMgdXNlZCBpbiBtYWNoaW5lIGxlYXJuaW5nLiBMMSByZWd1bGFyaXphdGlvbiAoYWxzbyBjYWxsZWQgTGFzc28pIGFkZHMgYSBwZW5hbHR5IHRlcm0gdG8gdGhlIG1vZGVsJ3MgY29zdCBmdW5jdGlvbiB0aGF0IGlzIHByb3BvcnRpb25hbCB0byB0aGUgYWJzb2x1dGUgdmFsdWUgb2YgdGhlIHByZWRpY3RvciBjb2VmZmljaWVudHMuIFRoaXMgc2hyaW5rcyBzb21lIG9mIHRoZSBjb2VmZmljaWVudHMgdG8gZXhhY3RseSB6ZXJvLCB3aGljaCBtYWtlcyB0aGUgbW9kZWwgbW9yZSBpbnRlcnByZXRhYmxlIGFuZCBoZWxwcyB3aXRoIHByZWRpY3RvciBzZWxlY3Rpb24uIEwyIHJlZ3VsYXJpemF0aW9uIChhbHNvIGNhbGxlZCBSaWRnZSksIG9uIHRoZSBvdGhlciBoYW5kLCBhZGRzIGEgcGVuYWx0eSB0ZXJtIHByb3BvcnRpb25hbCB0byB0aGUgc3F1YXJlIG9mIHRoZSBjb2VmZmljaWVudHMuIFRoaXMgc2hyaW5rcyBhbGwgY29lZmZpY2llbnRzLCBidXQgbm90IG5lY2Vzc2FyaWx5IHRvIHplcm8uIFRoaXMgY2FuIGhlbHAgcmVkdWNlIHRoZSBpbXBhY3Qgb2Ygb3V0bGllcnMgaW4gdGhlIGRhdGEuCgojIyMgUiBFeGFtcGxlCgpJbiB0aGlzIGRlbW8sIHdlIHdpbGwgbG9hZCB0aGUgW0Jvc3RvbiBIb3VzaW5nIERhdGEgU2V0XShodHRwczovL3d3dy5kYXRhbWluaW5nYm9vay5jb20vY29udGVudC9kYXRhc2V0cy1kb3dubG9hZC1yLWFuZC1weXRob24tZWRpdGlvbnMpIHVzZWQgaW4gb3VyIERhdGEgTWluaW5nIGNvdXJzZSwgYW5kIHByZWRpY3QgdGhlIGhvdXNlIHByaWNpbmcsIE1FRFYsIHVzaW5nIGxpbmVhciByZWdyZXNzaW9uIChMUikgYW5kIExSIHdpdGggTDEgcmVndWxhcml6YXRpb24uCgpgYGB7ciwgd2FybmluZz1GLCBtZXNzYWdlPUZ9CmRmIDwtIHJlYWQuY3N2KCIuL0Jvc3RvbkhvdXNpbmcuY3N2IikKaGVhZChkZikKYGBgCgpUaGUgZGF0YSBkaWN0aW9uYXJ5IGlzOiAKCnxDb2x1bW4gIHwgRGVzY3JpcHRpb24gfAp8LS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBDUklNICAgIHwgUGVyIGNhcGl0YSBjcmltZSByYXRlIGJ5IHRvd24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgWk4gICAgICB8IFByb3BvcnRpb24gb2YgcmVzaWRlbnRpYWwgbGFuZCB6b25lZCBmb3IgbG90cyBvdmVyIDI1LDAwMCBmdDIgICAgICAgICAgICAgfAp8IElORFVTICAgfCBQcm9wb3J0aW9uIG9mIG5vbnJldGFpbCBidXNpbmVzcyBhY3JlcyBwZXIgdG93biAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBDSEFTICAgIHwgQ2hhcmxlcyBSaXZlciBkdW1teSB2YXJpYWJsZSAoPSAxIGlmIHRyYWN0IGJvdW5kcyByaXZlcjsgPSBvICAgb3RoZXJ3aXNlKSB8CnwgTk9YICAgICB8IE5pdHJpYyBveGlkZSBjb25jZW50cmF0aW9uIChwYXJ0cyBwZXIgMTAgbWlsbGlvbikgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IFJNICAgICAgfCBBdmVyYWdlIG51bWJlciBvZiByb29tcyBwZXIgZHdlbGxpbmcgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBBR0UgICAgIHwgUHJvcG9ydGlvbiBvZiBvd25lci1vY2N1cGllZCB1bml0cyBidWlsdCBwcmlvciB0byAxOTQwICAgICAgICAgICAgICAgICAgICB8CnwgRElTICAgICB8IFdlaWdodGVkIGRpc3RhbmNlcyB0byBmaXZlIEJvc3RvbiBlbXBsb3ltZW50IGNlbnRlcnMgICAgICAgICAgICAgICAgICAgICAgfAp8IFJBRCAgICAgfCBJbmRleCBvZiBhY2Nlc3NpYmlsaXR5IHRvIHJhZGlhbCBoaWdod2F5cyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBUQVggICAgIHwgRnVsbC12YWx1ZSBwcm9wZXJ0eS10YXggcmF0ZSBwZXIgJDEwLDAwMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgUFRSQVRJTyB8IFB1cGlsL3RlYWNoZXIgcmF0aW8gYnkgdG93biAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IExTVEFUICAgfCBQZXJjZW50YWdlIGxvd2VyIHN0YXR1cyBvZiB0aGUgcG9wdWxhdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgTUVEViAgICB8IE1lZGlhbiB2YWx1ZSBvZiBvd25lci1vY2N1cGllZCBob21lcyBpbiBXb29zICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAoKCiMjIyMgU3BsaXQgdGhlIGRhdGEgaW50byA4MCUgZm9yIHRyYWluaW5nIGFuZCAyMCUgZm9yIHZhbGlkYXRpb24uIAoKYGBge3J9Cm4gPC0gbnJvdyhkZikKdHJhaW5pbmcuaW5kZXggPC0gc2FtcGxlKDE6biwgbiowLjgpCgpzZXQuc2VlZCgxKQpkZi50cmFpbiAgPC0gZGZbdHJhaW5pbmcuaW5kZXgsIF0KZGYudmFsaWQgPC0gZGZbLXRyYWluaW5nLmluZGV4LCBdCmBgYAoKIyMjIyBUcmFpbiBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsCgpgYGB7cn0KbHIgPC0gbG0oTUVEViB+IC4sIGRhdGEgPSBkZi50cmFpbikKc3VtbWFyeShscikKYGBgCgpUaGUgcGVyZm9ybWFuY2VzIG9mIHRoZSB0cmFpbiBhbmQgdmFsaWRhdGlvbiBzZXRzIGFyZSBhcyBmb2xsb3dzOgoKYGBge3IsIG1lc3NhZ2U9Rn0KbGlicmFyeShmb3JlY2FzdCkKCmFjY3VyYWN5KHByZWRpY3QobHIsIGRmLnRyYWluKSwgZGYudHJhaW4kTUVEVikKYWNjdXJhY3kocHJlZGljdChsciwgZGYudmFsaWQpLCBkZi52YWxpZCRNRURWKQoKYGBgCgojIyMjIFRyYWluIGEgbGluZWFyIHJlZ3Jlc3Npb24gd2l0aCBMMSByZWd1bGFyaXphdGlvbiAoTGFzc28pCgpGaXJzdCwgZGVmaW5lIHRoZSByZXNwb25zZSB2YXJpYWJsZSwgeSwgYW5kIHRoZSBtYXRyaXggb2YgcHJlZGljdG9yIHZhcmlhYmxlcywgeC4KCgpgYGB7cn0KeSA8LSBkZi50cmFpbiRNRURWCnggPC0gYXMubWF0cml4KGRmLnRyYWluWywtbmNvbChkZildKQpgYGAKCgpOZXh0LCB3ZeKAmWxsIHVzZSB0aGUgZ2xtbmV0KCkgZnVuY3Rpb24gdG8gZml0IHRoZSBsYXNzbyByZWdyZXNzaW9uIG1vZGVsIGFuZCBzcGVjaWZ5IGFscGhhPTEuIE5vdGUgdGhhdCBzZXR0aW5nIGFscGhhIGVxdWFsIHRvIDAgaXMgZXF1aXZhbGVudCB0byB1c2luZyByaWRnZSByZWdyZXNzaW9uIGFuZCBzZXR0aW5nIGFscGhhIHRvIHNvbWUgdmFsdWUgYmV0d2VlbiAwIGFuZCAxIGlzIGVxdWl2YWxlbnQgdG8gdXNpbmcgYW4gZWxhc3RpYyBuZXQsIHdoaWNoIGlzIGEgbWl4dHVyZSBvZiBsYXNzbyBhbmQgcmlkZ2UuIAoKVG8gZGV0ZXJtaW5lIHdoYXQgdmFsdWUgdG8gdXNlIGZvciBsYW1iZGEsIHdoaWNoIGNvbnRyb2xzIHRoZSBzdHJlbmd0aCBvZiB0aGUgcGVuYWx0eSBhcHBsaWVkIHRvIHRoZSBtb2RlbCdzIGNvZWZmaWNpZW50cywgd2XigJlsbCBwZXJmb3JtIGstZm9sZCBjcm9zcy12YWxpZGF0aW9uIGFuZCBpZGVudGlmeSB0aGUgbGFtYmRhIHZhbHVlLCB0aGF0IHByb2R1Y2VzIHRoZSBsb3dlc3QgdGVzdCBtZWFuIHNxdWFyZWQgZXJyb3IgKE1TRSkuCgpOb3RlIHRoYXQgdGhlIGZ1bmN0aW9uIGN2LmdsbW5ldCgpIGF1dG9tYXRpY2FsbHkgcGVyZm9ybXMgay1mb2xkIGNyb3NzIHZhbGlkYXRpb24gdXNpbmcgayA9IDEwIGZvbGRzLgoKYGBge3J9CmxpYnJhcnkoZ2xtbmV0KQoKI3BlcmZvcm0gay1mb2xkIGNyb3NzLXZhbGlkYXRpb24gdG8gZmluZCBvcHRpbWFsIGxhbWJkYSB2YWx1ZQpsci5sYXNzby5jdiA8LSBjdi5nbG1uZXQoeCwgeSwgYWxwaGEgPSAxKQoKI2ZpbmQgb3B0aW1hbCBsYW1iZGEgdmFsdWUgdGhhdCBtaW5pbWl6ZXMgY3Jvc3MgdmFsaWRhdGlvbidzIHRlc3QgTVNFCmJlc3RfbGFtYmRhIDwtIGxyLmxhc3NvLmN2JGxhbWJkYS5taW4KYmVzdF9sYW1iZGEKYGBgCkxldCdzIGZpbmQgY29lZmZpY2llbnRzIG9mIGJlc3QgbW9kZWwKCmBgYHtyfQpiZXN0X21vZGVsIDwtIGdsbW5ldCh4LCB5LCBhbHBoYSA9IDEsIGxhbWJkYSA9IGJlc3RfbGFtYmRhKQpjb2VmKGJlc3RfbW9kZWwpCmBgYApZb3Ugd2lsbCBub3RpY2UgdGhhdCB0aGUgY29lZmZpY2llbnRzIG9idGFpbmVkIGluIHRoZSBlYXJseSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBhcmUgc2ltaWxhciB0byB0aG9zZSBpbiBvdXIgY3VycmVudCBsYXNzbyBtb2RlbC4gSG93ZXZlciwgaXQgaXMgaW1wb3J0YW50IHRvIGhpZ2hsaWdodCB0aGF0IHRoZSBjb2VmZmljaWVudCBmb3IgdGhlIHByZWRpY3RvciB2YXJpYWJsZSAiSU5EVVMiIGFuZCAiQUdFIiB3ZXJlIGZvdW5kIHRvIGJlIHplcm8gYW5kIHRoZXJlZm9yZSBpdCB3YXMgcmVtb3ZlZCBmcm9tIHRoZSBtb2RlbC4gV2hlbiBkZWFsaW5nIHdpdGggYSBsYXJnZSBudW1iZXIgb2YgcHJlZGljdG9ycywgTGFzc28gcmVncmVzc2lvbiBpcyBhIHZhbHVhYmxlIHRlY2huaXF1ZSBmb3Igc2VsZWN0aW5nIHRoZSBtb3N0IHJlbGV2YW50IHByZWRpY3RvcnMuCgpXZSBhbHNvIGNhbiBjaGVjayB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhpcyBsYXNzbyBtb2RlbDoKCmBgYHtyLCBtZXNzYWdlPUZ9CnByZWQudHJhaW4gPC0gYXMudmVjdG9yKHByZWRpY3QoYmVzdF9tb2RlbCwgcyA9IGJlc3RfbGFtYmRhLCBuZXd4ID0geCkpCnZhbGlkX3ggPC0gYXMubWF0cml4KGRmLnZhbGlkWywtbmNvbChkZildKQpwcmVkLnZhbGlkIDwtIGFzLnZlY3RvcihwcmVkaWN0KGJlc3RfbW9kZWwsIHMgPSBiZXN0X2xhbWJkYSwgbmV3eCA9IHZhbGlkX3gpKQoKYWNjdXJhY3kocHJlZC50cmFpbiwgZGYudHJhaW4kTUVEVikKYWNjdXJhY3kocHJlZC52YWxpZCwgZGYudmFsaWQkTUVEVikKCmBgYApUaGUgdmFsaWRhdGlvbiBwZXJmb3JtYW5jZSBhcHBlYXJzIHRvIHNob3cgYSBzbGlnaHQgaW1wcm92ZW1lbnQgYWZ0ZXIgYXBwbHlpbmcgTDEgcmVndWxhcml6YXRpb24uIEhvd2V2ZXIsIG5vdGUgdGhhdCBhIG1vcmUgc2lnbmlmaWNhbnQgZW5oYW5jZW1lbnQgaW4gZ2VuZXJhbGl6YXRpb24gcGVyZm9ybWFuY2UgaXMgdXN1YWxseSBvYnNlcnZlZCB3aGVuIGRlYWxpbmcgd2l0aCBtb3JlIGNvbXBsZXggZGF0YS4KCiMjIyBDb25jbHVzaW9uIAoKUmVndWxhcml6YXRpb24gaXMgYSBwb3dlcmZ1bCB0ZWNobmlxdWUgdGhhdCBjYW4gcHJldmVudCBvdmVyZml0dGluZyBhbmQgaW1wcm92ZSBtb2RlbCBnZW5lcmFsaXphdGlvbiBwZXJmb3JtYW5jZS4gQnkgaW50cm9kdWNpbmcgYmlhcywgd2UgY2FuIGVuY291cmFnZSB0aGUgbW9kZWwgdG8gaGF2ZSBzbWFsbGVyIGNvZWZmaWNpZW50cywgd2hpY2ggbWFrZXMgaXQgbGVzcyBjb21wbGV4IGFuZCBsZXNzIGxpa2VseSB0byBvdmVyZml0LiBMMSBhbmQgTDIgcmVndWxhcml6YXRpb24gYXJlIHR3byBvZiB0aGUgbW9zdCBjb21tb24gcmVndWxhcml6YXRpb24gdHlwZXMsIGFuZCB0aGV5IGhhdmUgZGlmZmVyZW50IHN0cmVuZ3RocyBhbmQgd2Vha25lc3Nlcy4KCkwxIGFuZCBMMiByZWd1bGFyaXphdGlvbiBjYW4gYmUgYXBwbGllZCB0byBtYW55IG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcywgc3VjaCBhcyBsaW5lYXIgcmVncmVzc2lvbiwgbG9naXN0aWMgcmVncmVzc2lvbiwgc3VwcG9ydCB2ZWN0b3IgbWFjaGluZXMsIGFuZCBuZXVyYWwgbmV0d29ya3MsIGFtb25nIG90aGVycy4KCg==