Problem 2

For parts (a) through (c), indicate which of i. through iv. is correct. Justify your answer.

(a) The lasso, relative to least squares, is:

i. More flexible and hence will give improved prediction accuracy when its increase in bias is less than its decrease in variance.

ii. More flexible and hence will give improved prediction accuracy when its increase in variance is less than its decrease in bias.

iii. Less flexible and hence will give improved prediction accuracy when its increase in bias is less than its decrease in variance.

iv. Less flexible and hence will give improved prediction accuracy when its increase in variance is less than its decrease in bias.

(b) Repeat (a) for ridge regression relative to least squares.

(c) Repeat (a) for non-linear methods relative to least squares.


Problem 9

In this exercise, we will predict the number of applications received using the other variables in the College data set.

(a) Split the data set into a training set and a test set.

library(ISLR2)
library(glmnet)
library(pls)

set.seed(1)
n <- nrow(College)
train_idx <- sample(1:n, size = floor(0.60 * n))
college_train <- College[train_idx, ]
college_test  <- College[-train_idx, ]

(b) Fit a linear model using least squares on the training set, and report the test error obtained.

lm_fit  <- lm(Apps ~ ., data = college_train)
lm_pred <- predict(lm_fit, college_test)
mean((lm_pred - college_test$Apps)^2)
[1] 1124482

(c) Fit a ridge regression model on the training set, with λ chosen by cross-validation. Report the test error obtained.

x_train <- model.matrix(Apps ~ ., college_train)[, -1]
y_train <- college_train$Apps
x_test  <- model.matrix(Apps ~ ., college_test)[, -1]
y_test  <- college_test$Apps

set.seed(1)
cv_ridge   <- cv.glmnet(x_train, y_train, alpha = 0)
best_lam <- cv_ridge$lambda.min

ridge_pred <- predict(cv_ridge, s = best_lam, newx = x_test)
ridge_mse  <- mean((ridge_pred - y_test)^2)

cat("Best Lam:", best_lam, "\n")
Best Lam: 382.881 
cat("Ridge test MSE:", ridge_mse, "\n")
Ridge test MSE: 1032921 

(d) Fit a lasso model on the training set, with λ chosen by cross-validation. Report the test error obtained, along with the number of non-zero coefficient estimates.

set.seed(1)
cv_lasso   <- cv.glmnet(x_train, y_train, alpha = 1)
best_lam_lasso <- cv_lasso$lambda.min

lasso_pred <- predict(cv_lasso, s = best_lam_lasso, newx = x_test)
lasso_mse  <- mean((lasso_pred - y_test)^2)

lasso_coef <- predict(cv_lasso,type = "coefficients", s = best_lam_lasso)
nonzero    <- sum(lasso_coef != 0) - 1 

cat("Best Lam:", best_lam_lasso, "\n")
Best Lam: 17.36319 
cat("Lasso test MSE:", lasso_mse, "\n")
Lasso test MSE: 1076820 
cat("Number fo non-zero coef:", nonzero, "\n")
Number fo non-zero coef: 13 

(e) Fit a PCR model on the training set, with M chosen by cross-validation. Report the test error obtained, along with the value of M selected by cross-validation.

set.seed(1)
pcr_fit <- pcr(Apps ~ ., data = college_train, scale = TRUE, validation = "CV")
validationplot(pcr_fit, val.type = "MSEP")

summary(pcr_fit)
Data:   X dimension: 466 17 
    Y dimension: 466 1
Fit method: svdpc
Number of components considered: 17

VALIDATION: RMSEP
Cross-validated using 10 random segments.
       (Intercept)  1 comps  2 comps  3 comps  4 comps  5 comps  6 comps  7 comps  8 comps  9 comps  10 comps
CV            4061     3921     2228     2215     1942     1781     1773     1757     1724     1668      1652
adjCV         4061     3920     2224     2213     1920     1774     1768     1767     1710     1663      1646
       11 comps  12 comps  13 comps  14 comps  15 comps  16 comps  17 comps
CV         1650      1659      1653      1680      1655      1242      1209
adjCV      1645      1653      1648      1684      1635      1230      1199

TRAINING: % variance explained
      1 comps  2 comps  3 comps  4 comps  5 comps  6 comps  7 comps  8 comps  9 comps  10 comps  11 comps
X      32.483    57.59    64.96    70.64    75.98    80.99    84.43    87.80    90.66     92.92     95.01
Apps    8.553    71.18    71.47    80.53    82.20    82.57    82.66    84.02    84.54     84.94     84.95
      12 comps  13 comps  14 comps  15 comps  16 comps  17 comps
X        96.78     97.82     98.69     99.37     99.85    100.00
Apps     84.99     85.19     85.23     89.92     93.18     93.39
# Select M with lowest CV error
cv_errs <- MSEP(pcr_fit)$val[1, 1, ]
best_M  <- which.min(cv_errs[-1])  # exclude intercept-only

pcr_pred <- predict(pcr_fit, college_test, ncomp = best_M)
pcr_mse  <- mean((pcr_pred - college_test$Apps)^2)

cat("Best M (PCR):", best_M, "\n")
Best M (PCR): 17 
cat("PCR Test MSE:", pcr_mse, "\n")
PCR Test MSE: 1124482 

(f) Fit a PLS model on the training set, with M chosen by cross-validation. Report the test error obtained, along with the value of M selected by cross-validation.

set.seed(1)
pls_fit <- plsr(Apps ~ ., data = college_train, scale = TRUE, validation = "CV")
validationplot(pls_fit, val.type = "MSEP")

summary(pls_fit)
Data:   X dimension: 466 17 
    Y dimension: 466 1
Fit method: kernelpls
Number of components considered: 17

VALIDATION: RMSEP
Cross-validated using 10 random segments.
       (Intercept)  1 comps  2 comps  3 comps  4 comps  5 comps  6 comps  7 comps  8 comps  9 comps  10 comps
CV            4061     2058     1844     1586     1536     1404     1286     1246     1223     1215      1213
adjCV         4061     2053     1838     1578     1514     1384     1269     1233     1212     1204      1202
       11 comps  12 comps  13 comps  14 comps  15 comps  16 comps  17 comps
CV         1214      1211      1210      1209      1209      1209      1209
adjCV      1203      1200      1199      1199      1199      1199      1199

TRAINING: % variance explained
      1 comps  2 comps  3 comps  4 comps  5 comps  6 comps  7 comps  8 comps  9 comps  10 comps  11 comps
X       26.31    50.01    62.94    65.76    70.18    74.34    77.94    81.22    83.06     86.39     89.37
Apps    75.94    81.67    87.08    90.72    92.44    93.14    93.24    93.29    93.34     93.36     93.37
      12 comps  13 comps  14 comps  15 comps  16 comps  17 comps
X        91.15     92.33     94.24     97.03     98.16    100.00
Apps     93.38     93.39     93.39     93.39     93.39     93.39
cv_errs_pls <- MSEP(pls_fit)$val[1, 1, ]
best_M_pls  <- which.min(cv_errs_pls[-1])

pls_pred <- predict(pls_fit, college_test, ncomp = best_M_pls)
pls_mse  <- mean((pls_pred - college_test$Apps)^2)

cat("Best M (PLS):", best_M_pls, "\n")
Best M (PLS): 15 
cat("PLS Test MSE:", pls_mse, "\n")
PLS Test MSE: 1124526 

(g) Comment on the results obtained. How accurately can we predict the number of college applications received? Is there much difference among the test errors resulting from these five approaches?


Problem 11

(a) Try out some of the regression methods explored in this chapter, such as best subset selection, the lasso, ridge regression, and PCR. Present and discuss results for the approaches that you consider.

library(leaps)

set.seed(1)
n_bos <- nrow(Boston)
bos_train_idx <- sample(1:n_bos, size = floor(0.65 * n_bos))
bos_train <- Boston[bos_train_idx, ]
bos_test  <- Boston[-bos_train_idx, ]

x_bos_train <- model.matrix(crim ~ ., bos_train)[, -1]
y_bos_train <- bos_train$crim
x_bos_test  <- model.matrix(crim ~ ., bos_test)[, -1]
y_bos_test  <- bos_test$crim

-Best Subset

regfit.full <- regsubsets(crim ~ ., data = bos_train, nvmax = 12) 
regfit.summary <- summary(regfit_full) 
par(mfrow=c(2,2))

par(mfrow=c(2,2 ))
plot(regfit.summary$rss,xlab ="Number of Variables", ylab = "RSS", type = "l")
plot(regfit.summary$adjr2,xlab ="Number of Variables", ylab = "adjr2", type = "l")
which.max(regfit.summary$adjr2)
[1] 8
points(11,regfit.summary$adjr2[11],col='red', cex=2,pch=20)
plot(regfit.summary$cp,xlab ="Number of Variables", ylab = "cp", type = "l")
which.min(regfit.summary$cp)
[1] 6
points(10,regfit.summary$cp[10],col='red', cex=2,pch=20)
plot(regfit.summary$bic,xlab ="Number of Variables", ylab = "bic", type = "l")
which.min(regfit.summary$bic)
[1] 2
points(6,regfit.summary$bic[6],col='red', cex=2,pch=20)


test_mat <- model.matrix(crim ~ ., data = bos_test)
best_size <- 11  
best_coef <- coef(regfit.full, id = best_size)
best_pred <- test_mat[, names(best_coef)] %*% best_coef
best_subset_mse <- mean((bos_test$crim - best_pred)^2)
best_subset_mse
[1] 50.2209

-Lasso

# Lasso
grid <- 10^seq(10, -2, length = 100)
lasso_mod <- glmnet(x_bos_train, y_bos_train, alpha = 1, lambda = grid)

set.seed(1)
cv_lasso_bos <- cv.glmnet(x_bos_train, y_bos_train, alpha = 1)

best_lam_boslasso <- cv_lasso_bos$lambda.min
lasso_pred_bos <- predict(lasso_mod, s = best_lam_boslasso, newx = x_bos_test)
lasso_mse_bos <- mean((lasso_pred_bos - y_bos_test)^2)
lasso_coef_bos <- predict(lasso_mod, s = cv_lasso_bos$lambda.min, type = "coefficients")
nonzero_bos <- sum(lasso_coef_bos != 0) -1

best_lam_boslasso 
[1] 0.0422181
lasso_mse_bos 
[1] 50.5437
lasso_coef_bos 
13 x 1 sparse Matrix of class "dgCMatrix"
            s=0.0422181
(Intercept)  9.15341877
zn           0.03465035
indus       -0.08413468
chas        -0.74979794
nox         -5.49801258
rm           0.17307922
age          .         
dis         -0.65772034
rad          0.52416009
tax          .         
ptratio     -0.29138583
lstat        0.23187008
medv        -0.13506184
nonzero_bos 
[1] 10

-PCR

# PCR
set.seed(1)
pcr_bos <- pcr(crim ~ ., data = bos_train, scale = TRUE, validation = "CV")
validationplot(pcr_bos, val.type = "MSEP")


cv_errs_bos <- MSEP(pcr_bos)$val[1, 1, ]
best_M_bos  <- which.min(cv_errs_bos[-1])

pcr_pred_bos <- predict(pcr_bos, bos_test, ncomp = best_M_bos)
pcr_mse_bos  <- mean((pcr_pred_bos - bos_test$crim)^2)

cat("Best M (PCR):", best_M_bos, "\n")
Best M (PCR): 12 
cat("PCR Test MSE:", pcr_mse_bos, "\n")
PCR Test MSE: 50.22142 

(b) Propose a model (or set of models) that seem to perform well on this data set, and justify your answer. Make sure that you are evaluating model performance using validation set error, cross-validation, or some other reasonable alternative, as opposed to using training error.

comparison <- data.frame(
  Method   = c("Best Subset", "Lasso", "PCR"),
  Test_MSE = c(best_subset_mse, lasso_mse_bos, pcr_mse_bos)
)
comparison

(c) Does your chosen model involve all of the features in the data set? Why or why not?

LS0tDQp0aXRsZTogIkFzc2lnbm1lbnQ1Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMjIFByb2JsZW0gMg0KDQpfX0ZvciBwYXJ0cyAoYSkgdGhyb3VnaCAoYyksIGluZGljYXRlIHdoaWNoIG9mIGkuIHRocm91Z2ggaXYuIGlzIGNvcnJlY3QuIEp1c3RpZnkgeW91ciBhbnN3ZXIuX18NCg0KX18oYSkgVGhlIGxhc3NvLCByZWxhdGl2ZSB0byBsZWFzdCBzcXVhcmVzLCBpczpfXw0KDQogIF9faS4gTW9yZSBmbGV4aWJsZSBhbmQgaGVuY2Ugd2lsbCBnaXZlIGltcHJvdmVkIHByZWRpY3Rpb24gYWNjdXJhY3kgd2hlbiBpdHMgaW5jcmVhc2UgaW4gYmlhcyBpcyBsZXNzIHRoYW4gaXRzIGRlY3JlYXNlIGluIHZhcmlhbmNlLl9fDQoNCiAgX19paS4gTW9yZSBmbGV4aWJsZSBhbmQgaGVuY2Ugd2lsbCBnaXZlIGltcHJvdmVkIHByZWRpY3Rpb24gYWNjdXJhY3kgd2hlbiBpdHMgaW5jcmVhc2UgaW4gdmFyaWFuY2UgaXMgbGVzcyB0aGFuIGl0cyBkZWNyZWFzZSBpbiBiaWFzLl9fDQoNCiAgX19paWkuIExlc3MgZmxleGlibGUgYW5kIGhlbmNlIHdpbGwgZ2l2ZSBpbXByb3ZlZCBwcmVkaWN0aW9uIGFjY3VyYWN5IHdoZW4gaXRzIGluY3JlYXNlIGluIGJpYXMgaXMgbGVzcyB0aGFuIGl0cyBkZWNyZWFzZSBpbiB2YXJpYW5jZS5fXw0KDQogIF9faXYuIExlc3MgZmxleGlibGUgYW5kIGhlbmNlIHdpbGwgZ2l2ZSBpbXByb3ZlZCBwcmVkaWN0aW9uIGFjY3VyYWN5IHdoZW4gaXRzIGluY3JlYXNlIGluIHZhcmlhbmNlIGlzIGxlc3MgdGhhbiBpdHMgZGVjcmVhc2UgaW4gYmlhcy5fXw0KDQogIC0gKippaWkgaXMgY29ycmVjdC4qKiBDb21wYXJlZCB0byBsZWFzdCBTcXVhcmVzLCBUaGUgKipMYXNzbyoqIGFkZHMgYSBwZW5hbHR5IHRvIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cywgc2hyaW5raW5nIHNvbWUgdG93YXJkIHplcm8gYW5kIGV2ZW4gc2V0dGluZyBzb21lIGV4YWN0bHkgdG8gemVyby4gVGhhdCBtYWtlcyB0aGUgbW9kZWwgbGVzcyBmbGV4aWJsZSwgaGlnaGVyIGJpYXMgYmVjYXVzZSBvZiB0aGUgY29uc3RyYWluZWQgY29lZmZpY2llbnRzIGFuZCBsb3dlciB2YXJpYW5jZSBiZWNhdXNlIGl0J3MgbGVzcyBzZW5zaXRpdmUgdG8gdHJhaW5pbmcgZGF0YS4NCg0KX18oYikgUmVwZWF0IChhKSBmb3IgcmlkZ2UgcmVncmVzc2lvbiByZWxhdGl2ZSB0byBsZWFzdCBzcXVhcmVzLl9fDQoNCiAgLSAqKmlpaSBpcyBjb3JyZWN0LioqIFJpZGdlIHJlZ3Jlc3Npb24gd29ya3MgdGhlIHNhbWUgd2F5IGFzIHRoZSBsYXNzbyBpbiB0aGlzIHJlZ2FyZC4gSXQgc2hyaW5rcyBjb2VmZmljaWVudHMgdG93YXJkIHplcm8gYnV0IG5ldmVyIGFjdHVhbGx5IHplcm8sIG1ha2luZyBpdCBsZXNzIGZsZXhpYmxlIHRoYW4gbGVhc3Qgc3F1YXJlcy5XaXRoIHRoZSBzYW1lIGhpZ2ggYmlhcyBhbmQgbG93IHZhcmlhbmNlIHRyYWRlIG9mZi4NCg0KX18oYykgUmVwZWF0IChhKSBmb3Igbm9uLWxpbmVhciBtZXRob2RzIHJlbGF0aXZlIHRvIGxlYXN0IHNxdWFyZXMuX18NCg0KICAtICoqaWkgaXMgY29ycmVjdC4qKiBOb24tbGluZWFyIG1ldGhvZHMgYXJlIGRpZmZlcmVudCB0aG91Z2gsIHRoZXkgYXJlIG1vcmUgZmxleGlibGUgdGhhbiBsZWFzdCBzcXVhcmVzLiBXaXRoIHRoZSBpbmNyZWFzZSBpbiBmbGV4aWJpbGl0eSBpdCB3aWxsIG1ha2UgdGhlIGJpYXMgZGVjcmVhc2UgYW5kIHRoZSB2YXJpYW5jZSBpbmNyZWFzZS4gVGhleSB3b3JrIGJldHRlciB3aGVuIHRoZSBkZWNyZWFzZSBpbiBiaWFzIGlzIGxhcmdlciB0aGVuIHRoZSBpbmNyZWFzZSBpbiB2YXJpYW5jZSwgZXNwZWNpYWxseSB3aGVuIHRoZSByZWxhdGlvbnNoaXAgaW4gbm90IGxpbmVhci4gDQogIA0KLS0tDQoNCiMjIyBQcm9ibGVtIDkNCg0KX19JbiB0aGlzIGV4ZXJjaXNlLCB3ZSB3aWxsIHByZWRpY3QgdGhlIG51bWJlciBvZiBhcHBsaWNhdGlvbnMgcmVjZWl2ZWQgdXNpbmcgdGhlIG90aGVyIHZhcmlhYmxlcyBpbiB0aGUgQ29sbGVnZSBkYXRhIHNldC5fXw0KDQpfXyhhKSBTcGxpdCB0aGUgZGF0YSBzZXQgaW50byBhIHRyYWluaW5nIHNldCBhbmQgYSB0ZXN0IHNldC5fXw0KYGBge3J9DQpsaWJyYXJ5KElTTFIyKQ0KbGlicmFyeShnbG1uZXQpDQpsaWJyYXJ5KHBscykNCg0Kc2V0LnNlZWQoMSkNCm4gPC0gbnJvdyhDb2xsZWdlKQ0KdHJhaW5faWR4IDwtIHNhbXBsZSgxOm4sIHNpemUgPSBmbG9vcigwLjYwICogbikpDQpjb2xsZWdlX3RyYWluIDwtIENvbGxlZ2VbdHJhaW5faWR4LCBdDQpjb2xsZWdlX3Rlc3QgIDwtIENvbGxlZ2VbLXRyYWluX2lkeCwgXQ0KYGBgDQoNCl9fKGIpIEZpdCBhIGxpbmVhciBtb2RlbCB1c2luZyBsZWFzdCBzcXVhcmVzIG9uIHRoZSB0cmFpbmluZyBzZXQsIGFuZCByZXBvcnQgdGhlIHRlc3QgZXJyb3Igb2J0YWluZWQuX18NCmBgYHtyfQ0KbG1fZml0ICA8LSBsbShBcHBzIH4gLiwgZGF0YSA9IGNvbGxlZ2VfdHJhaW4pDQpsbV9wcmVkIDwtIHByZWRpY3QobG1fZml0LCBjb2xsZWdlX3Rlc3QpDQptZWFuKChsbV9wcmVkIC0gY29sbGVnZV90ZXN0JEFwcHMpXjIpDQoNCmBgYA0KDQoNCl9fKGMpIEZpdCBhIHJpZGdlIHJlZ3Jlc3Npb24gbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldCwgd2l0aCDOuyBjaG9zZW4gYnkgY3Jvc3MtdmFsaWRhdGlvbi4gUmVwb3J0IHRoZSB0ZXN0IGVycm9yIG9idGFpbmVkLl9fDQpgYGB7cn0NCnhfdHJhaW4gPC0gbW9kZWwubWF0cml4KEFwcHMgfiAuLCBjb2xsZWdlX3RyYWluKVssIC0xXQ0KeV90cmFpbiA8LSBjb2xsZWdlX3RyYWluJEFwcHMNCnhfdGVzdCAgPC0gbW9kZWwubWF0cml4KEFwcHMgfiAuLCBjb2xsZWdlX3Rlc3QpWywgLTFdDQp5X3Rlc3QgIDwtIGNvbGxlZ2VfdGVzdCRBcHBzDQoNCnNldC5zZWVkKDEpDQpjdl9yaWRnZSAgIDwtIGN2LmdsbW5ldCh4X3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDApDQpiZXN0X2xhbSA8LSBjdl9yaWRnZSRsYW1iZGEubWluDQoNCnJpZGdlX3ByZWQgPC0gcHJlZGljdChjdl9yaWRnZSwgcyA9IGJlc3RfbGFtLCBuZXd4ID0geF90ZXN0KQ0KcmlkZ2VfbXNlICA8LSBtZWFuKChyaWRnZV9wcmVkIC0geV90ZXN0KV4yKQ0KDQpjYXQoIkJlc3QgTGFtOiIsIGJlc3RfbGFtLCAiXG4iKQ0KY2F0KCJSaWRnZSB0ZXN0IE1TRToiLCByaWRnZV9tc2UsICJcbiIpDQoNCmBgYA0KDQoNCl9fKGQpIEZpdCBhIGxhc3NvIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBzZXQsIHdpdGggzrsgY2hvc2VuIGJ5IGNyb3NzLXZhbGlkYXRpb24uIFJlcG9ydCB0aGUgdGVzdCBlcnJvciBvYnRhaW5lZCwgYWxvbmcgd2l0aCB0aGUgbnVtYmVyIG9mIG5vbi16ZXJvIGNvZWZmaWNpZW50IGVzdGltYXRlcy5fXw0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KY3ZfbGFzc28gICA8LSBjdi5nbG1uZXQoeF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAxKQ0KYmVzdF9sYW1fbGFzc28gPC0gY3ZfbGFzc28kbGFtYmRhLm1pbg0KDQpsYXNzb19wcmVkIDwtIHByZWRpY3QoY3ZfbGFzc28sIHMgPSBiZXN0X2xhbV9sYXNzbywgbmV3eCA9IHhfdGVzdCkNCmxhc3NvX21zZSAgPC0gbWVhbigobGFzc29fcHJlZCAtIHlfdGVzdCleMikNCg0KbGFzc29fY29lZiA8LSBwcmVkaWN0KGN2X2xhc3NvLHR5cGUgPSAiY29lZmZpY2llbnRzIiwgcyA9IGJlc3RfbGFtX2xhc3NvKQ0Kbm9uemVybyAgICA8LSBzdW0obGFzc29fY29lZiAhPSAwKSAtIDEgDQoNCmNhdCgiQmVzdCBMYW06IiwgYmVzdF9sYW1fbGFzc28sICJcbiIpDQpjYXQoIkxhc3NvIHRlc3QgTVNFOiIsIGxhc3NvX21zZSwgIlxuIikNCmNhdCgiTnVtYmVyIGZvIG5vbi16ZXJvIGNvZWY6Iiwgbm9uemVybywgIlxuIikNCmBgYA0KDQoNCl9fKGUpIEZpdCBhIFBDUiBtb2RlbCBvbiB0aGUgdHJhaW5pbmcgc2V0LCB3aXRoIE0gY2hvc2VuIGJ5IGNyb3NzLXZhbGlkYXRpb24uIFJlcG9ydCB0aGUgdGVzdCBlcnJvciBvYnRhaW5lZCwgYWxvbmcgd2l0aCB0aGUgdmFsdWUgb2YgTSBzZWxlY3RlZCBieSBjcm9zcy12YWxpZGF0aW9uLl9fDQpgYGB7cn0NCnNldC5zZWVkKDEpDQpwY3JfZml0IDwtIHBjcihBcHBzIH4gLiwgZGF0YSA9IGNvbGxlZ2VfdHJhaW4sIHNjYWxlID0gVFJVRSwgdmFsaWRhdGlvbiA9ICJDViIpDQp2YWxpZGF0aW9ucGxvdChwY3JfZml0LCB2YWwudHlwZSA9ICJNU0VQIikNCnN1bW1hcnkocGNyX2ZpdCkNCmBgYA0KDQpgYGB7cn0NCiMgU2VsZWN0IE0gd2l0aCBsb3dlc3QgQ1YgZXJyb3INCmN2X2VycnMgPC0gTVNFUChwY3JfZml0KSR2YWxbMSwgMSwgXQ0KYmVzdF9NICA8LSB3aGljaC5taW4oY3ZfZXJyc1stMV0pICAjIGV4Y2x1ZGUgaW50ZXJjZXB0LW9ubHkNCg0KcGNyX3ByZWQgPC0gcHJlZGljdChwY3JfZml0LCBjb2xsZWdlX3Rlc3QsIG5jb21wID0gYmVzdF9NKQ0KcGNyX21zZSAgPC0gbWVhbigocGNyX3ByZWQgLSBjb2xsZWdlX3Rlc3QkQXBwcyleMikNCg0KY2F0KCJCZXN0IE0gKFBDUik6IiwgYmVzdF9NLCAiXG4iKQ0KY2F0KCJQQ1IgVGVzdCBNU0U6IiwgcGNyX21zZSwgIlxuIikNCmBgYA0KDQoNCl9fKGYpIEZpdCBhIFBMUyBtb2RlbCBvbiB0aGUgdHJhaW5pbmcgc2V0LCB3aXRoIE0gY2hvc2VuIGJ5IGNyb3NzLXZhbGlkYXRpb24uIFJlcG9ydCB0aGUgdGVzdCBlcnJvciBvYnRhaW5lZCwgYWxvbmcgd2l0aCB0aGUgdmFsdWUgb2YgTSBzZWxlY3RlZCBieSBjcm9zcy12YWxpZGF0aW9uLl9fDQpgYGB7cn0NCnNldC5zZWVkKDEpDQpwbHNfZml0IDwtIHBsc3IoQXBwcyB+IC4sIGRhdGEgPSBjb2xsZWdlX3RyYWluLCBzY2FsZSA9IFRSVUUsIHZhbGlkYXRpb24gPSAiQ1YiKQ0KdmFsaWRhdGlvbnBsb3QocGxzX2ZpdCwgdmFsLnR5cGUgPSAiTVNFUCIpDQpzdW1tYXJ5KHBsc19maXQpDQpgYGANCg0KYGBge3J9DQpjdl9lcnJzX3BscyA8LSBNU0VQKHBsc19maXQpJHZhbFsxLCAxLCBdDQpiZXN0X01fcGxzICA8LSB3aGljaC5taW4oY3ZfZXJyc19wbHNbLTFdKQ0KDQpwbHNfcHJlZCA8LSBwcmVkaWN0KHBsc19maXQsIGNvbGxlZ2VfdGVzdCwgbmNvbXAgPSBiZXN0X01fcGxzKQ0KcGxzX21zZSAgPC0gbWVhbigocGxzX3ByZWQgLSBjb2xsZWdlX3Rlc3QkQXBwcyleMikNCg0KY2F0KCJCZXN0IE0gKFBMUyk6IiwgYmVzdF9NX3BscywgIlxuIikNCmNhdCgiUExTIFRlc3QgTVNFOiIsIHBsc19tc2UsICJcbiIpDQpgYGANCg0KDQpfXyhnKSBDb21tZW50IG9uIHRoZSByZXN1bHRzIG9idGFpbmVkLiBIb3cgYWNjdXJhdGVseSBjYW4gd2UgcHJlZGljdCB0aGUgbnVtYmVyIG9mIGNvbGxlZ2UgYXBwbGljYXRpb25zIHJlY2VpdmVkPyBJcyB0aGVyZSBtdWNoIGRpZmZlcmVuY2UgYW1vbmcgdGhlIHRlc3QgZXJyb3JzIHJlc3VsdGluZyBmcm9tIHRoZXNlIGZpdmUgYXBwcm9hY2hlcz9fXw0KDQogIC0gVGhlIGRpZmZlcmVudCBtZXRob2RzIHByb2R1Y2VkIHNpbWlsYXIgdGVzdCBNU0UgdmFsdWVzLCBhbGwgcGVyZm9ybWVkIHNpbWlsYXJseSB3aGVuIHByZWRpY3RpbmcgdGhlIG51bWJlciBvZiBjb2xsZWdlIGFwcGxpY2F0aW9ucyByZWNlaXZlZC4gUmlkZ2UgcmVncmVzc2lvbiBoYWQgdGhlIGxvd2VzdCB0ZXN0IE1TRSAoMTAzMjkyMSksIGZvbGxvd2VkIGJ5IGxhc3NvICgxMDc2ODIwKSwgd2hpbGUgUENSIGFuZCBQTFMgaGFkIHNsaWdodGx5IGhpZ2hlciBlcnJvcnMgKGFyb3VuZCAxMTI0MDAwKS4gT3ZlcmFsbCwgdGhlIG1vZGVscyB3ZXJlIGFibGUgdG8gcHJlZGljdCBhcHBsaWNhdGlvbnMgcmVhc29uYWJseSB3ZWxsLCBidXQgbm8gc2luZ2xlIG1ldGhvZCBwcm92aWRlZCBhIG1ham9yIGltcHJvdmVtZW50IG92ZXIgdGhlIG90aGVycy4gDQogIA0KLS0tDQoNCiMjIyBQcm9ibGVtIDExDQoNCg0KX18oYSkgVHJ5IG91dCBzb21lIG9mIHRoZSByZWdyZXNzaW9uIG1ldGhvZHMgZXhwbG9yZWQgaW4gdGhpcyBjaGFwdGVyLCBzdWNoIGFzIGJlc3Qgc3Vic2V0IHNlbGVjdGlvbiwgdGhlIGxhc3NvLCByaWRnZSByZWdyZXNzaW9uLCBhbmQgUENSLiBQcmVzZW50IGFuZCBkaXNjdXNzIHJlc3VsdHMgZm9yIHRoZSBhcHByb2FjaGVzIHRoYXQgeW91IGNvbnNpZGVyLl9fDQpgYGB7cn0NCmxpYnJhcnkobGVhcHMpDQoNCnNldC5zZWVkKDEpDQpuX2JvcyA8LSBucm93KEJvc3RvbikNCmJvc190cmFpbl9pZHggPC0gc2FtcGxlKDE6bl9ib3MsIHNpemUgPSBmbG9vcigwLjY1ICogbl9ib3MpKQ0KYm9zX3RyYWluIDwtIEJvc3Rvbltib3NfdHJhaW5faWR4LCBdDQpib3NfdGVzdCAgPC0gQm9zdG9uWy1ib3NfdHJhaW5faWR4LCBdDQoNCnhfYm9zX3RyYWluIDwtIG1vZGVsLm1hdHJpeChjcmltIH4gLiwgYm9zX3RyYWluKVssIC0xXQ0KeV9ib3NfdHJhaW4gPC0gYm9zX3RyYWluJGNyaW0NCnhfYm9zX3Rlc3QgIDwtIG1vZGVsLm1hdHJpeChjcmltIH4gLiwgYm9zX3Rlc3QpWywgLTFdDQp5X2Jvc190ZXN0ICA8LSBib3NfdGVzdCRjcmltDQpgYGANCg0KICAtKipCZXN0IFN1YnNldCoqDQoNCmBgYHtyfQ0KcmVnZml0LmZ1bGwgPC0gcmVnc3Vic2V0cyhjcmltIH4gLiwgZGF0YSA9IGJvc190cmFpbiwgbnZtYXggPSAxMikgDQpyZWdmaXQuc3VtbWFyeSA8LSBzdW1tYXJ5KHJlZ2ZpdF9mdWxsKSANCnBhcihtZnJvdz1jKDIsMikpDQoNCnBhcihtZnJvdz1jKDIsMiApKQ0KcGxvdChyZWdmaXQuc3VtbWFyeSRyc3MseGxhYiA9Ik51bWJlciBvZiBWYXJpYWJsZXMiLCB5bGFiID0gIlJTUyIsIHR5cGUgPSAibCIpDQpwbG90KHJlZ2ZpdC5zdW1tYXJ5JGFkanIyLHhsYWIgPSJOdW1iZXIgb2YgVmFyaWFibGVzIiwgeWxhYiA9ICJhZGpyMiIsIHR5cGUgPSAibCIpDQp3aGljaC5tYXgocmVnZml0LnN1bW1hcnkkYWRqcjIpDQpwb2ludHMoMTEscmVnZml0LnN1bW1hcnkkYWRqcjJbMTFdLGNvbD0ncmVkJywgY2V4PTIscGNoPTIwKQ0KcGxvdChyZWdmaXQuc3VtbWFyeSRjcCx4bGFiID0iTnVtYmVyIG9mIFZhcmlhYmxlcyIsIHlsYWIgPSAiY3AiLCB0eXBlID0gImwiKQ0Kd2hpY2gubWluKHJlZ2ZpdC5zdW1tYXJ5JGNwKQ0KcG9pbnRzKDEwLHJlZ2ZpdC5zdW1tYXJ5JGNwWzEwXSxjb2w9J3JlZCcsIGNleD0yLHBjaD0yMCkNCnBsb3QocmVnZml0LnN1bW1hcnkkYmljLHhsYWIgPSJOdW1iZXIgb2YgVmFyaWFibGVzIiwgeWxhYiA9ICJiaWMiLCB0eXBlID0gImwiKQ0Kd2hpY2gubWluKHJlZ2ZpdC5zdW1tYXJ5JGJpYykNCnBvaW50cyg2LHJlZ2ZpdC5zdW1tYXJ5JGJpY1s2XSxjb2w9J3JlZCcsIGNleD0yLHBjaD0yMCkNCg0KdGVzdF9tYXQgPC0gbW9kZWwubWF0cml4KGNyaW0gfiAuLCBkYXRhID0gYm9zX3Rlc3QpDQpiZXN0X3NpemUgPC0gMTEgIA0KYmVzdF9jb2VmIDwtIGNvZWYocmVnZml0LmZ1bGwsIGlkID0gYmVzdF9zaXplKQ0KYmVzdF9wcmVkIDwtIHRlc3RfbWF0WywgbmFtZXMoYmVzdF9jb2VmKV0gJSolIGJlc3RfY29lZg0KYmVzdF9zdWJzZXRfbXNlIDwtIG1lYW4oKGJvc190ZXN0JGNyaW0gLSBiZXN0X3ByZWQpXjIpDQpiZXN0X3N1YnNldF9tc2UNCg0KYGBgDQoNCg0KICAtKipMYXNzbyoqDQoNCmBgYHtyfQ0KIyBMYXNzbw0KZ3JpZCA8LSAxMF5zZXEoMTAsIC0yLCBsZW5ndGggPSAxMDApDQpsYXNzb19tb2QgPC0gZ2xtbmV0KHhfYm9zX3RyYWluLCB5X2Jvc190cmFpbiwgYWxwaGEgPSAxLCBsYW1iZGEgPSBncmlkKQ0KDQpzZXQuc2VlZCgxKQ0KY3ZfbGFzc29fYm9zIDwtIGN2LmdsbW5ldCh4X2Jvc190cmFpbiwgeV9ib3NfdHJhaW4sIGFscGhhID0gMSkNCg0KYmVzdF9sYW1fYm9zbGFzc28gPC0gY3ZfbGFzc29fYm9zJGxhbWJkYS5taW4NCmxhc3NvX3ByZWRfYm9zIDwtIHByZWRpY3QobGFzc29fbW9kLCBzID0gYmVzdF9sYW1fYm9zbGFzc28sIG5ld3ggPSB4X2Jvc190ZXN0KQ0KbGFzc29fbXNlX2JvcyA8LSBtZWFuKChsYXNzb19wcmVkX2JvcyAtIHlfYm9zX3Rlc3QpXjIpDQpsYXNzb19jb2VmX2JvcyA8LSBwcmVkaWN0KGxhc3NvX21vZCwgcyA9IGN2X2xhc3NvX2JvcyRsYW1iZGEubWluLCB0eXBlID0gImNvZWZmaWNpZW50cyIpDQpub256ZXJvX2JvcyA8LSBzdW0obGFzc29fY29lZl9ib3MgIT0gMCkgLTENCg0KYmVzdF9sYW1fYm9zbGFzc28gDQpsYXNzb19tc2VfYm9zIA0KbGFzc29fY29lZl9ib3MgDQpub256ZXJvX2JvcyANCmBgYA0KICAtKipQQ1IqKg0KDQpgYGB7cn0NCiMgUENSDQpzZXQuc2VlZCgxKQ0KcGNyX2JvcyA8LSBwY3IoY3JpbSB+IC4sIGRhdGEgPSBib3NfdHJhaW4sIHNjYWxlID0gVFJVRSwgdmFsaWRhdGlvbiA9ICJDViIpDQp2YWxpZGF0aW9ucGxvdChwY3JfYm9zLCB2YWwudHlwZSA9ICJNU0VQIikNCg0KY3ZfZXJyc19ib3MgPC0gTVNFUChwY3JfYm9zKSR2YWxbMSwgMSwgXQ0KYmVzdF9NX2JvcyAgPC0gd2hpY2gubWluKGN2X2VycnNfYm9zWy0xXSkNCg0KcGNyX3ByZWRfYm9zIDwtIHByZWRpY3QocGNyX2JvcywgYm9zX3Rlc3QsIG5jb21wID0gYmVzdF9NX2JvcykNCnBjcl9tc2VfYm9zICA8LSBtZWFuKChwY3JfcHJlZF9ib3MgLSBib3NfdGVzdCRjcmltKV4yKQ0KDQpjYXQoIkJlc3QgTSAoUENSKToiLCBiZXN0X01fYm9zLCAiXG4iKQ0KY2F0KCJQQ1IgVGVzdCBNU0U6IiwgcGNyX21zZV9ib3MsICJcbiIpDQpgYGANCg0KICAtIFRoZSB0aHJlZSBtZXRob2RzIHByb2R1Y2VkIHNpbWlsYXIgdGVzdCBNU0UgdmFsdWVzIG9uIHRoZSBkYXRhLCBtZWFuaW5nIHRoZXkgcGVyZm9ybWVkIHNpbWlsYXJseSB3aGVuIHByZWRpY3RpbmcgY3JpbWUgcmF0ZS4gQm90aCB0aGUgc3Vic2V0IGFuZCBsYXNzbyByZWR1Y2VkIHRoZSBudW1iZXIgb2YgcHJlZGljdG9ycyBpbiB0aGUgbW9kZWwgdG8gY29tZSB1cCB3aXRoIHRoZXJlIGJldHRlciBwcmVkaWN0aW9ucy4gUENSIHVzZWQgY29tYmluYXRpb25zIG9mIHRoZSBvcmlnaW5hbCBwcmVkaWN0b3JzIGluc3RlYWQgb2Ygc2VsZWN0aW5nIGluZGl2aWR1YWwgdmFyaWFibGVzLg0KDQoNCl9fKGIpIFByb3Bvc2UgYSBtb2RlbCAob3Igc2V0IG9mIG1vZGVscykgdGhhdCBzZWVtIHRvIHBlcmZvcm0gd2VsbCBvbiB0aGlzIGRhdGEgc2V0LCBhbmQganVzdGlmeSB5b3VyIGFuc3dlci4gTWFrZSBzdXJlIHRoYXQgeW91IGFyZSBldmFsdWF0aW5nIG1vZGVsIHBlcmZvcm1hbmNlIHVzaW5nIHZhbGlkYXRpb24gc2V0IGVycm9yLCBjcm9zcy12YWxpZGF0aW9uLCBvciBzb21lIG90aGVyIHJlYXNvbmFibGUgYWx0ZXJuYXRpdmUsIGFzIG9wcG9zZWQgdG8gdXNpbmcgdHJhaW5pbmcgZXJyb3IuX18NCmBgYHtyfQ0KY29tcGFyaXNvbiA8LSBkYXRhLmZyYW1lKA0KICBNZXRob2QgICA9IGMoIkJlc3QgU3Vic2V0IiwgIkxhc3NvIiwgIlBDUiIpLA0KICBUZXN0X01TRSA9IGMoYmVzdF9zdWJzZXRfbXNlLCBsYXNzb19tc2VfYm9zLCBwY3JfbXNlX2JvcykNCikNCmNvbXBhcmlzb24NCmBgYA0KDQogIC0gQmFzZWQgb24gdGhlIHRlc3QgcmVzdWx0cywgSSB3b3VsZCBjaG9vc2UgdGhlIFBDUiBtb2RlbCBhcyB0aGUgYmVzdCBhcHByb2FjaCBiZWNhdXNlIGl0IGhhcyB0aGUgbG93ZXN0IE1TRS4gSG93ZXZlciwgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIG1vZGVscyB3ZXJlIHN1cGVyIHNtYWxsLCBhbGwgdGhyZWUgbWV0aG9kcyBwZXJmb3JtZWQgc2ltaWxhcmx5IHdoZW4gcHJlZGljdGluZyBuZXcgZGF0YS4gQmVzdCBzdWJzZXQgc2VsZWN0aW9uIGFuZCBsYXNzbyBwcm9kdWNlZCBzaW1wbGVyIG1vZGVscyB0aGF0IGFyZSBlYXNpZXIgdG8gaW50ZXJwcmV0LCB3aGlsZSBQQ1IgcHJvdmlkZWQgc2xpZ2h0bHkgYmV0dGVyIHByZWRpY3Rpb24gcGVyZm9ybWFuY2UuIE92ZXJhbGwsIFBDUiB3b3VsZCBiZSB0aGUgcHJlZmVycmVkIG1vZGVsIGZvciBwcmVkaWN0aW9uLCB3aGlsZSBsYXNzbyBvciBiZXN0IHN1YnNldCBjb3VsZCBiZSB1c2VmdWwgaWYgaW50ZXJwcmV0IGFiaWxpdHkgaXMgbW9yZSBpbXBvcnRhbnQuDQoNCl9fKGMpIERvZXMgeW91ciBjaG9zZW4gbW9kZWwgaW52b2x2ZSBhbGwgb2YgdGhlIGZlYXR1cmVzIGluIHRoZSBkYXRhIHNldD8gV2h5IG9yIHdoeSBub3Q/X18NCg0KICAtIE5vLiBUaGUgUENSIG1vZGVsIGRvZXMgbm90IHVzZSB0aGUgb3JpZ2luYWwgZmVhdHVyZXMuIEl0IGNvbWJpbmVzIHRoZSBmZWF0dXJlcyBpbnRvIGEgc21hbGxlciBudW1iZXIgb2YgcHJpbmNpcGFsIGNvbXBvbmVudHMgdGhhdCBjYXB0dXJlIHRoZSBtb3N0IGltcG9ydGFudCBwYXR0ZXJucyBpbiB0aGUgZGF0YS4gVGhpcyBhbGxvd3MgdGhlIG1vZGVsIHRvIHVzZSB0aGUgaW5mb3JtYXRpb24gZnJvbSBtYW55IHZhcmlhYmxlcyB3aGlsZSByZWR1Y2luZyB1bm5lY2Vzc2FyeSBjb21wbGV4aXR5LiBQQ1Igd2FzIGNob3NlbiBiZWNhdXNlIGl0IHByb3ZpZGVkIHRoZSBsb3dlc3QgdGVzdCBNU0UsIG1lYW5pbmcgaXQgcGVyZm9ybWVkIHNsaWdodGx5IGJldHRlciBhdCBwcmVkaWN0aW5nIGNyaW1lIHJhdGUgb24gbmV3IGRhdGEuDQo=