library(tidyverse)
library(openintro)
library(ISLR)
## Warning: package 'ISLR' was built under R version 4.2.3
library(glmnet)
## Warning: package 'glmnet' was built under R version 4.2.3
library(pls)
## Warning: package 'pls' was built under R version 4.2.3
library(MASS)

Question 2

  1. The lasso, relative to least squares, is:
  1. More flexible and hence will give improved prediction accuracy when its increase in bias is less than its decrease in variance.

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

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

iii is less flexible and will give an improved prediction accuracy. This is done when there is an increase in bias and a decrease in the variance. Lasso allows for us to reduce the coefficients estimates.

  1. Repeat (a) for ridge regression relative to least squares.
  1. the less flexible and hence will give improved prediction accuracy when its increase in bias is less than its decrease in variance. Ridge regression can also reduce coefficient estimates
  1. Repeat (a) for non-linear methods relative to least squares.
  1. More flexible and hence will give improved prediction accuracy when its increase in variance is less than its decrease in bias.

Question 9

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

  1. Split the data set into a training set and a test set.
set.seed(1)
num = sample(nrow(College), 0.75*nrow(College))
train = College[num,]
test = College[-num,]
  1. Fit a linear model using least squares on the training set, and report the test error obtained.
lmfit = lm(Apps~.,data=train)
lmpred = predict(lmfit,test)
lmerr = mean((test$Apps-lmpred)^2)
lmerr
## [1] 1384604
avg.apps=mean(test$Apps)
lm.r2=1-mean((lmpred-test$Apps)^2)/mean((avg.apps-test$Apps)^2)
lm.r2
## [1] 0.9086432

The test error obtained is 1384604.

  1. Fit a ridge regression model on the training set, with λ chosen by cross-validation. Report the test error obtained.
trainc = model.matrix(Apps~.,data=train)[,-1]
testc = model.matrix(Apps~.,data=test)[,-1]
rridge = cv.glmnet(trainc, train$Apps, alpha = 0)
lambda = rridge$lambda.min
rrpred = predict(rridge, s=lambda, newx = testc)
rrerr = mean((test$Apps-rrpred)^2)
rrerr
## [1] 1206346
rr.r2=1-mean((rrpred-test$Apps)^2)/mean((avg.apps-test$Apps)^2)
rr.r2
## [1] 0.9204048

The test error is 1206346

  1. Fit a lasso model on the training set, with λ chosen by crossvalidation. Report the test error obtained, along with the number of non-zero coefficient estimates.
lass.train = model.matrix(Apps~., data=train)[,-1]
lass.test = model.matrix(Apps~., data=test)[,-1]
lass.fit = cv.glmnet(lass.train, train$Apps, alpha=1)
lambda = lass.fit$lambda.min
lass.pred = predict(lass.fit, s=lambda, newx=lass.test)
lass.err = mean((test$Apps - lass.pred)^2)
lass.err
## [1] 1370403
lass.cof = predict(lass.fit, type="coefficients", s=lambda)[1:ncol(College),]
lass.cof
##   (Intercept)    PrivateYes        Accept        Enroll     Top10perc 
## -5.651833e+02 -4.646154e+02  1.696513e+00 -1.079012e+00  5.131131e+01 
##     Top25perc   F.Undergrad   P.Undergrad      Outstate    Room.Board 
## -1.397974e+01  5.644318e-02  5.646120e-02 -7.818063e-02  1.592739e-01 
##         Books      Personal           PhD      Terminal     S.F.Ratio 
##  2.282606e-01  3.466774e-03 -1.025118e+01  0.000000e+00  1.561720e+01 
##   perc.alumni        Expend     Grad.Rate 
##  1.514189e+00  5.513999e-02  6.157195e+00
lass.r2=1-mean((lass.pred-test$Apps)^2)/mean((avg.apps-test$Apps)^2)
lass.r2
## [1] 0.9095802

The lasso method gives a test error 1370493

  1. Fit a PCR model on the training set, with M chosen by crossvalidation. Report the test error obtained, along with the value of M selected by cross-validation.
pcr.fit = pcr(Apps~., data=train, scale=TRUE, validation="CV")
summary(pcr.fit)
## Data:    X dimension: 582 17 
##  Y dimension: 582 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
## CV            3862     3761     2078     2076     1809     1709     1662
## adjCV         3862     3761     2075     2076     1790     1689     1657
##        7 comps  8 comps  9 comps  10 comps  11 comps  12 comps  13 comps
## CV        1654     1615     1573      1578      1584      1587      1586
## adjCV     1649     1605     1568      1572      1579      1582      1580
##        14 comps  15 comps  16 comps  17 comps
## CV         1588      1528      1193      1133
## adjCV      1583      1511      1183      1124
## 
## TRAINING: % variance explained
##       1 comps  2 comps  3 comps  4 comps  5 comps  6 comps  7 comps  8 comps
## X      32.159    57.17    64.41    70.20    75.53    80.48    84.24    87.56
## Apps    5.226    71.83    71.84    80.02    83.01    83.07    83.21    84.46
##       9 comps  10 comps  11 comps  12 comps  13 comps  14 comps  15 comps
## X       90.54     92.81     94.92     96.73     97.81     98.69     99.35
## Apps    85.00     85.22     85.22     85.23     85.36     85.45     89.93
##       16 comps  17 comps
## X        99.82    100.00
## Apps     92.84     93.36
pcr.pred = predict(pcr.fit, test, ncomp=10)  
pcr.err = mean((test$Apps - pcr.pred)^2)
pcr.err
## [1] 1952693
pcr.r2=1-mean((pcr.pred-test$Apps)^2)/mean((avg.apps-test$Apps)^2)
pcr.r2
## [1] 0.8711605

Using PCR, we get a test error 1952693 with M being 10.

  1. Fit a PLS model on the training set, with M chosen by crossvalidation. Report the test error obtained, along with the value of M selected by cross-validation.
pls.fit = plsr(Apps~., data=train, scale=TRUE, validation="CV")
summary(pls.fit)
## Data:    X dimension: 582 17 
##  Y dimension: 582 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
## CV            3862     1933     1688     1489     1424     1250     1167
## adjCV         3862     1927     1687     1482     1404     1227     1157
##        7 comps  8 comps  9 comps  10 comps  11 comps  12 comps  13 comps
## CV        1149     1144     1138      1134      1135      1132      1132
## adjCV     1140     1136     1130      1126      1127      1124      1123
##        14 comps  15 comps  16 comps  17 comps
## CV         1130      1131      1130      1130
## adjCV      1122      1122      1122      1122
## 
## TRAINING: % variance explained
##       1 comps  2 comps  3 comps  4 comps  5 comps  6 comps  7 comps  8 comps
## X       25.67    47.09    62.54     65.0    67.54    72.28    76.80    80.63
## Apps    76.80    82.71    87.20     90.8    92.79    93.05    93.14    93.22
##       9 comps  10 comps  11 comps  12 comps  13 comps  14 comps  15 comps
## X       82.71     85.53     88.01     90.95     93.07     95.18     96.86
## Apps    93.30     93.32     93.34     93.35     93.36     93.36     93.36
##       16 comps  17 comps
## X        98.00    100.00
## Apps     93.36     93.36
pls.pred = predict(pls.fit, test, ncomp=9)
pls.err = mean((test$Apps - pls.pred)^2)
pls.err
## [1] 1381335
pls.r2=1-mean((pls.pred-test$Apps)^2)/mean((avg.apps-test$Apps)^2)
pls.r2
## [1] 0.9088589

Using PLS, the test error is 1381335 and M selected with M being 9.

  1. 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?

The results from the models tested, was the test error of 1384604 for the linear model, 1206346 for the ridge regression model, 1370403 for the lasso model,1952693 for the PCR model, and finally 1381335 for the PLS model.The result tells us that the ridge regression method is the best model for the data. In order to see if the models can accurately predict the number of applications received, we look at the individual r-squared value for each model. All have an r- squared of .87 or higher meaning they can all accurately model the data. Overall, there isn’t that much of a difference between test errors of the 5 tests, however the PCR model had a higher error vs the ridge regression model which had a lower error compared to the others.

Question 11

We will now try to predict per capita crime rate in the Boston data set. (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.

set.seed(10)
training = sample(1:nrow(Boston), nrow(Boston)/2)
train = Boston[training,]
test = Boston[-training,]
trainset = model.matrix(crim~., data=train)[,-1]
testset = model.matrix(crim~., data=test)[,-1]
#Lasso 
lasso.fit = cv.glmnet(trainset, train$crim, alpha=1)
lambda = lasso.fit$lambda.min
lasso.pred = predict(lasso.fit, s=lambda, newx=testset)
lasso.err = mean((test$crim - lasso.pred)^2)
lasso.err
## [1] 56.09141

The test error is 55.89314 with the lasso technique

#PCR
pcr2.fit = pcr(crim~., data=train, scale=TRUE, validation="CV")
summary(pcr2.fit)
## Data:    X dimension: 253 13 
##  Y dimension: 253 1
## Fit method: svdpc
## Number of components considered: 13
## 
## VALIDATION: RMSEP
## Cross-validated using 10 random segments.
##        (Intercept)  1 comps  2 comps  3 comps  4 comps  5 comps  6 comps
## CV           7.521    6.136    6.095    5.727    5.626    5.675    5.777
## adjCV        7.521    6.130    6.089    5.717    5.616    5.661    5.759
##        7 comps  8 comps  9 comps  10 comps  11 comps  12 comps  13 comps
## CV       5.782    5.673    5.552     5.546     5.554     5.542     5.505
## adjCV    5.764    5.649    5.530     5.526     5.536     5.521     5.484
## 
## TRAINING: % variance explained
##       1 comps  2 comps  3 comps  4 comps  5 comps  6 comps  7 comps  8 comps
## X       46.57    58.79    68.40    76.02    82.37    87.52    90.99    93.28
## crim    35.21    36.53    45.03    47.18    47.30    47.35    47.35    49.85
##       9 comps  10 comps  11 comps  12 comps  13 comps
## X       95.35     97.13     98.44     99.51    100.00
## crim    51.98     51.99     52.05     52.64     53.51
pcr2.pred = predict(pcr2.fit, test, ncomp=12)  
pcr2.err = mean((test$crim - pcr2.pred)^2)
pcr2.err
## [1] 56.58621

We get a test error of 56.59 using the PCR method

#Ridge
ridge.fit = cv.glmnet(trainset, train$crim, alpha=0)
lambda = ridge.fit$lambda.min
ridge.pred = predict(ridge.fit, s=lambda, newx=testset)
ridge.err = mean((test$crim - ridge.pred)^2)
ridge.err
## [1] 56.34103

We get a test error of 56.34 using Ridge Regression

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

In part A, we are given the test errors given were 55.95 for the lasso regression, 56.59 for the PCR model, and 56.34 for the ridge regression. The lowest test error was the lasso model, therefore we can propose that this model would perform best compared to the others. However the other models were close to the lasso technique so all could have the potential to perform well.

  1. Does your chosen model involve all of the features in the data set? Why or why not?

No, because my chosen model (the lasso model) removes variables deemed “unimportant” or barely significant, and changes their coefficients to 0.

LS0tDQp0aXRsZTogIkxpbmVhciBNb2RlbCBTZWxlY3Rpb24gYW5kIFJlZ3VsYXJpemF0aW9uIg0KYXV0aG9yOiAiU2FuamFuYSBLdW1hciINCmRhdGU6ICJgMy8yMy8yMDIzYCINCm91dHB1dDogb3BlbmludHJvOjpsYWJfcmVwb3J0DQotLS0NCg0KYGBge3IgbG9hZC1wYWNrYWdlcywgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShvcGVuaW50cm8pDQpsaWJyYXJ5KElTTFIpDQpsaWJyYXJ5KGdsbW5ldCkNCmxpYnJhcnkocGxzKQ0KbGlicmFyeShNQVNTKQ0KYGBgDQoNCiMjIyBRdWVzdGlvbiAyDQoNCihhKSBUaGUgbGFzc28sIHJlbGF0aXZlIHRvIGxlYXN0IHNxdWFyZXMsIGlzOiANCg0KaS4gTW9yZSBmbGV4aWJsZSBhbmQgaGVuY2Ugd2lsbCBnaXZlIGltcHJvdmVkIHByZWRpY3Rpb24gYWNjdXJhY3kgd2hlbiBpdHMgaW5jcmVhc2UgaW4gYmlhcyBpcyBsZXNzIHRoYW4gaXRzIGRlY3JlYXNlIGluIHZhcmlhbmNlLiANCg0KaWkuIE1vcmUgZmxleGlibGUgYW5kIGhlbmNlIHdpbGwgZ2l2ZSBpbXByb3ZlZCBwcmVkaWN0aW9uIGFjY3VyYWN5IHdoZW4gaXRzIGluY3JlYXNlIGluIHZhcmlhbmNlIGlzIGxlc3MgdGhhbiBpdHMgZGVjcmVhc2UgaW4gYmlhcy4gDQoNCmlpaS4gTGVzcyBmbGV4aWJsZSBhbmQgaGVuY2Ugd2lsbCBnaXZlIGltcHJvdmVkIHByZWRpY3Rpb24gYWNjdXJhY3kgd2hlbiBpdHMgaW5jcmVhc2UgaW4gYmlhcyBpcyBsZXNzIHRoYW4gaXRzIGRlY3JlYXNlIGluIHZhcmlhbmNlLiBpdi4gTGVzcyBmbGV4aWJsZSBhbmQgaGVuY2Ugd2lsbCBnaXZlIGltcHJvdmVkIHByZWRpY3Rpb24gYWNjdXJhY3kgd2hlbiBpdHMgaW5jcmVhc2UgaW4gdmFyaWFuY2UgaXMgbGVzcyB0aGFuIGl0cyBkZWNyZWFzZSBpbiBiaWFzLg0KDQppaWkgaXMgbGVzcyBmbGV4aWJsZSBhbmQgd2lsbCBnaXZlIGFuIGltcHJvdmVkIHByZWRpY3Rpb24gYWNjdXJhY3kuIFRoaXMgaXMgZG9uZSB3aGVuIHRoZXJlIGlzIGFuIGluY3JlYXNlIGluIGJpYXMgYW5kIGEgZGVjcmVhc2UgaW4gdGhlIHZhcmlhbmNlLiBMYXNzbyBhbGxvd3MgZm9yIHVzIHRvIHJlZHVjZSB0aGUgY29lZmZpY2llbnRzIGVzdGltYXRlcy4NCg0KKGIpIFJlcGVhdCAoYSkgZm9yIHJpZGdlIHJlZ3Jlc3Npb24gcmVsYXRpdmUgdG8gbGVhc3Qgc3F1YXJlcy4NCg0KaWlpLiB0aGUgbGVzcyBmbGV4aWJsZSBhbmQgaGVuY2Ugd2lsbCBnaXZlIGltcHJvdmVkIHByZWRpY3Rpb24gYWNjdXJhY3kgd2hlbiBpdHMgaW5jcmVhc2UgaW4gYmlhcyBpcyBsZXNzIHRoYW4gaXRzIGRlY3JlYXNlIGluIHZhcmlhbmNlLiBSaWRnZSByZWdyZXNzaW9uIGNhbiBhbHNvIHJlZHVjZSBjb2VmZmljaWVudCBlc3RpbWF0ZXMNCg0KDQoNCg0KKGMpIFJlcGVhdCAoYSkgZm9yIG5vbi1saW5lYXIgbWV0aG9kcyByZWxhdGl2ZSB0byBsZWFzdCBzcXVhcmVzLg0KDQppaS4gTW9yZSBmbGV4aWJsZSBhbmQgaGVuY2Ugd2lsbCBnaXZlIGltcHJvdmVkIHByZWRpY3Rpb24gYWNjdXJhY3kgd2hlbiBpdHMgaW5jcmVhc2UgaW4gdmFyaWFuY2UgaXMgbGVzcyB0aGFuIGl0cyBkZWNyZWFzZSBpbiBiaWFzLg0KDQojIyMgUXVlc3Rpb24gOQ0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB3ZSB3aWxsIHByZWRpY3QgdGhlIG51bWJlciBvZiBhcHBsaWNhdGlvbnMgcmVjZWl2ZWQgdXNpbmcgdGhlIG90aGVyIHZhcmlhYmxlcyBpbiB0aGUgQ29sbGVnZSBkYXRhIHNldC4NCg0KKGEpIFNwbGl0IHRoZSBkYXRhIHNldCBpbnRvIGEgdHJhaW5pbmcgc2V0IGFuZCBhIHRlc3Qgc2V0Lg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQpudW0gPSBzYW1wbGUobnJvdyhDb2xsZWdlKSwgMC43NSpucm93KENvbGxlZ2UpKQ0KdHJhaW4gPSBDb2xsZWdlW251bSxdDQp0ZXN0ID0gQ29sbGVnZVstbnVtLF0NCg0KYGBgDQoNCihiKSBGaXQgYSBsaW5lYXIgbW9kZWwgdXNpbmcgbGVhc3Qgc3F1YXJlcyBvbiB0aGUgdHJhaW5pbmcgc2V0LCBhbmQgcmVwb3J0IHRoZSB0ZXN0IGVycm9yIG9idGFpbmVkLg0KDQpgYGB7cn0NCmxtZml0ID0gbG0oQXBwc34uLGRhdGE9dHJhaW4pDQpsbXByZWQgPSBwcmVkaWN0KGxtZml0LHRlc3QpDQpsbWVyciA9IG1lYW4oKHRlc3QkQXBwcy1sbXByZWQpXjIpDQpsbWVycg0KDQphdmcuYXBwcz1tZWFuKHRlc3QkQXBwcykNCmxtLnIyPTEtbWVhbigobG1wcmVkLXRlc3QkQXBwcyleMikvbWVhbigoYXZnLmFwcHMtdGVzdCRBcHBzKV4yKQ0KbG0ucjINCg0KYGBgDQpUaGUgdGVzdCBlcnJvciBvYnRhaW5lZCBpcyAxMzg0NjA0Lg0KDQooYykgRml0IGEgcmlkZ2UgcmVncmVzc2lvbiBtb2RlbCBvbiB0aGUgdHJhaW5pbmcgc2V0LCB3aXRoIM67IGNob3NlbiBieSBjcm9zcy12YWxpZGF0aW9uLiBSZXBvcnQgdGhlIHRlc3QgZXJyb3Igb2J0YWluZWQuDQoNCmBgYHtyfQ0KdHJhaW5jID0gbW9kZWwubWF0cml4KEFwcHN+LixkYXRhPXRyYWluKVssLTFdDQp0ZXN0YyA9IG1vZGVsLm1hdHJpeChBcHBzfi4sZGF0YT10ZXN0KVssLTFdDQpycmlkZ2UgPSBjdi5nbG1uZXQodHJhaW5jLCB0cmFpbiRBcHBzLCBhbHBoYSA9IDApDQpsYW1iZGEgPSBycmlkZ2UkbGFtYmRhLm1pbg0KcnJwcmVkID0gcHJlZGljdChycmlkZ2UsIHM9bGFtYmRhLCBuZXd4ID0gdGVzdGMpDQpycmVyciA9IG1lYW4oKHRlc3QkQXBwcy1ycnByZWQpXjIpDQpycmVycg0KDQpyci5yMj0xLW1lYW4oKHJycHJlZC10ZXN0JEFwcHMpXjIpL21lYW4oKGF2Zy5hcHBzLXRlc3QkQXBwcyleMikNCnJyLnIyDQoNCmBgYA0KVGhlIHRlc3QgZXJyb3IgaXMgMTIwNjM0Ng0KDQooZCkgRml0IGEgbGFzc28gbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldCwgd2l0aCDOuyBjaG9zZW4gYnkgY3Jvc3N2YWxpZGF0aW9uLiBSZXBvcnQgdGhlIHRlc3QgZXJyb3Igb2J0YWluZWQsIGFsb25nIHdpdGggdGhlIG51bWJlciBvZiBub24temVybyBjb2VmZmljaWVudCBlc3RpbWF0ZXMuDQoNCmBgYHtyfQ0KbGFzcy50cmFpbiA9IG1vZGVsLm1hdHJpeChBcHBzfi4sIGRhdGE9dHJhaW4pWywtMV0NCmxhc3MudGVzdCA9IG1vZGVsLm1hdHJpeChBcHBzfi4sIGRhdGE9dGVzdClbLC0xXQ0KbGFzcy5maXQgPSBjdi5nbG1uZXQobGFzcy50cmFpbiwgdHJhaW4kQXBwcywgYWxwaGE9MSkNCmxhbWJkYSA9IGxhc3MuZml0JGxhbWJkYS5taW4NCmxhc3MucHJlZCA9IHByZWRpY3QobGFzcy5maXQsIHM9bGFtYmRhLCBuZXd4PWxhc3MudGVzdCkNCmxhc3MuZXJyID0gbWVhbigodGVzdCRBcHBzIC0gbGFzcy5wcmVkKV4yKQ0KbGFzcy5lcnINCg0KbGFzcy5jb2YgPSBwcmVkaWN0KGxhc3MuZml0LCB0eXBlPSJjb2VmZmljaWVudHMiLCBzPWxhbWJkYSlbMTpuY29sKENvbGxlZ2UpLF0NCmxhc3MuY29mDQoNCmxhc3MucjI9MS1tZWFuKChsYXNzLnByZWQtdGVzdCRBcHBzKV4yKS9tZWFuKChhdmcuYXBwcy10ZXN0JEFwcHMpXjIpDQpsYXNzLnIyDQoNCmBgYA0KVGhlIGxhc3NvIG1ldGhvZCBnaXZlcyBhIHRlc3QgZXJyb3IgMTM3MDQ5Mw0KDQooZSkgRml0IGEgUENSIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBzZXQsIHdpdGggTSBjaG9zZW4gYnkgY3Jvc3N2YWxpZGF0aW9uLiBSZXBvcnQgdGhlIHRlc3QgZXJyb3Igb2J0YWluZWQsIGFsb25nIHdpdGggdGhlIHZhbHVlIG9mIE0gc2VsZWN0ZWQgYnkgY3Jvc3MtdmFsaWRhdGlvbi4NCg0KYGBge3J9DQpwY3IuZml0ID0gcGNyKEFwcHN+LiwgZGF0YT10cmFpbiwgc2NhbGU9VFJVRSwgdmFsaWRhdGlvbj0iQ1YiKQ0Kc3VtbWFyeShwY3IuZml0KQ0KDQpwY3IucHJlZCA9IHByZWRpY3QocGNyLmZpdCwgdGVzdCwgbmNvbXA9MTApICANCnBjci5lcnIgPSBtZWFuKCh0ZXN0JEFwcHMgLSBwY3IucHJlZCleMikNCnBjci5lcnINCg0KcGNyLnIyPTEtbWVhbigocGNyLnByZWQtdGVzdCRBcHBzKV4yKS9tZWFuKChhdmcuYXBwcy10ZXN0JEFwcHMpXjIpDQpwY3IucjINCg0KYGBgDQpVc2luZyBQQ1IsIHdlIGdldCBhIHRlc3QgZXJyb3IgMTk1MjY5MyB3aXRoIE0gYmVpbmcgMTAuDQoNCihmKSBGaXQgYSBQTFMgbW9kZWwgb24gdGhlIHRyYWluaW5nIHNldCwgd2l0aCBNIGNob3NlbiBieSBjcm9zc3ZhbGlkYXRpb24uIFJlcG9ydCB0aGUgdGVzdCBlcnJvciBvYnRhaW5lZCwgYWxvbmcgd2l0aCB0aGUgdmFsdWUgb2YgTSBzZWxlY3RlZCBieSBjcm9zcy12YWxpZGF0aW9uLg0KDQpgYGB7cn0NCnBscy5maXQgPSBwbHNyKEFwcHN+LiwgZGF0YT10cmFpbiwgc2NhbGU9VFJVRSwgdmFsaWRhdGlvbj0iQ1YiKQ0Kc3VtbWFyeShwbHMuZml0KQ0KDQoNCnBscy5wcmVkID0gcHJlZGljdChwbHMuZml0LCB0ZXN0LCBuY29tcD05KQ0KcGxzLmVyciA9IG1lYW4oKHRlc3QkQXBwcyAtIHBscy5wcmVkKV4yKQ0KcGxzLmVycg0KDQpwbHMucjI9MS1tZWFuKChwbHMucHJlZC10ZXN0JEFwcHMpXjIpL21lYW4oKGF2Zy5hcHBzLXRlc3QkQXBwcyleMikNCnBscy5yMg0KYGBgDQpVc2luZyBQTFMsIHRoZSB0ZXN0IGVycm9yIGlzIDEzODEzMzUgYW5kIE0gc2VsZWN0ZWQgd2l0aCBNIGJlaW5nIDkuDQoNCihnKSBDb21tZW50IG9uIHRoZSByZXN1bHRzIG9idGFpbmVkLiBIb3cgYWNjdXJhdGVseSBjYW4gd2UgcHJlZGljdCB0aGUgbnVtYmVyIG9mIGNvbGxlZ2UgYXBwbGljYXRpb25zIHJlY2VpdmVkPyBJcyB0aGVyZSBtdWNoIGRpZmZlcmVuY2UgYW1vbmcgdGhlIHRlc3QgZXJyb3JzIHJlc3VsdGluZyBmcm9tIHRoZXNlIGZpdmUgYXBwcm9hY2hlcz8NCg0KVGhlIHJlc3VsdHMgZnJvbSB0aGUgbW9kZWxzIHRlc3RlZCwgd2FzIHRoZSB0ZXN0IGVycm9yIG9mIDEzODQ2MDQgZm9yIHRoZSBsaW5lYXIgbW9kZWwsIDEyMDYzNDYgZm9yIHRoZSByaWRnZSByZWdyZXNzaW9uIG1vZGVsLCAxMzcwNDAzIGZvciB0aGUgbGFzc28gbW9kZWwsMTk1MjY5MyBmb3IgdGhlIFBDUiBtb2RlbCwgYW5kIGZpbmFsbHkgMTM4MTMzNSBmb3IgdGhlIFBMUyBtb2RlbC5UaGUgcmVzdWx0IHRlbGxzIHVzIHRoYXQgdGhlIHJpZGdlIHJlZ3Jlc3Npb24gbWV0aG9kIGlzIHRoZSBiZXN0IG1vZGVsIGZvciB0aGUgZGF0YS4gSW4gb3JkZXIgdG8gc2VlIGlmIHRoZSBtb2RlbHMgY2FuIGFjY3VyYXRlbHkgcHJlZGljdCB0aGUgbnVtYmVyIG9mIGFwcGxpY2F0aW9ucyByZWNlaXZlZCwgd2UgbG9vayBhdCB0aGUgaW5kaXZpZHVhbCByLXNxdWFyZWQgdmFsdWUgZm9yIGVhY2ggbW9kZWwuIEFsbCBoYXZlIGFuIHItIHNxdWFyZWQgb2YgLjg3IG9yIGhpZ2hlciBtZWFuaW5nIHRoZXkgY2FuIGFsbCBhY2N1cmF0ZWx5IG1vZGVsIHRoZSBkYXRhLiBPdmVyYWxsLCB0aGVyZSBpc27igJl0IHRoYXQgbXVjaCBvZiBhIGRpZmZlcmVuY2UgYmV0d2VlbiB0ZXN0IGVycm9ycyBvZiB0aGUgNSB0ZXN0cywgaG93ZXZlciB0aGUgUENSIG1vZGVsIGhhZCBhIGhpZ2hlciBlcnJvciB2cyB0aGUgcmlkZ2UgcmVncmVzc2lvbiBtb2RlbCB3aGljaCBoYWQgYSBsb3dlciBlcnJvciBjb21wYXJlZCB0byB0aGUgb3RoZXJzLg0KDQojIyMgUXVlc3Rpb24gMTENCg0KV2Ugd2lsbCBub3cgdHJ5IHRvIHByZWRpY3QgcGVyIGNhcGl0YSBjcmltZSByYXRlIGluIHRoZSBCb3N0b24gZGF0YQ0Kc2V0Lg0KKGEpIFRyeSBvdXQgc29tZSBvZiB0aGUgcmVncmVzc2lvbiBtZXRob2RzIGV4cGxvcmVkIGluIHRoaXMgY2hhcHRlciwNCnN1Y2ggYXMgYmVzdCBzdWJzZXQgc2VsZWN0aW9uLCB0aGUgbGFzc28sIHJpZGdlIHJlZ3Jlc3Npb24sIGFuZA0KUENSLiBQcmVzZW50IGFuZCBkaXNjdXNzIHJlc3VsdHMgZm9yIHRoZSBhcHByb2FjaGVzIHRoYXQgeW91DQpjb25zaWRlci4NCg0KYGBge3J9DQpzZXQuc2VlZCgxMCkNCnRyYWluaW5nID0gc2FtcGxlKDE6bnJvdyhCb3N0b24pLCBucm93KEJvc3RvbikvMikNCnRyYWluID0gQm9zdG9uW3RyYWluaW5nLF0NCnRlc3QgPSBCb3N0b25bLXRyYWluaW5nLF0NCnRyYWluc2V0ID0gbW9kZWwubWF0cml4KGNyaW1+LiwgZGF0YT10cmFpbilbLC0xXQ0KdGVzdHNldCA9IG1vZGVsLm1hdHJpeChjcmltfi4sIGRhdGE9dGVzdClbLC0xXQ0KYGBgDQoNCmBgYHtyfQ0KI0xhc3NvIA0KbGFzc28uZml0ID0gY3YuZ2xtbmV0KHRyYWluc2V0LCB0cmFpbiRjcmltLCBhbHBoYT0xKQ0KbGFtYmRhID0gbGFzc28uZml0JGxhbWJkYS5taW4NCmxhc3NvLnByZWQgPSBwcmVkaWN0KGxhc3NvLmZpdCwgcz1sYW1iZGEsIG5ld3g9dGVzdHNldCkNCmxhc3NvLmVyciA9IG1lYW4oKHRlc3QkY3JpbSAtIGxhc3NvLnByZWQpXjIpDQpsYXNzby5lcnINCmBgYA0KVGhlIHRlc3QgZXJyb3IgaXMgNTUuODkzMTQgd2l0aCB0aGUgbGFzc28gdGVjaG5pcXVlDQpgYGB7cn0NCiNQQ1INCnBjcjIuZml0ID0gcGNyKGNyaW1+LiwgZGF0YT10cmFpbiwgc2NhbGU9VFJVRSwgdmFsaWRhdGlvbj0iQ1YiKQ0Kc3VtbWFyeShwY3IyLmZpdCkNCg0KcGNyMi5wcmVkID0gcHJlZGljdChwY3IyLmZpdCwgdGVzdCwgbmNvbXA9MTIpICANCnBjcjIuZXJyID0gbWVhbigodGVzdCRjcmltIC0gcGNyMi5wcmVkKV4yKQ0KcGNyMi5lcnINCg0KYGBgDQpXZSBnZXQgYSB0ZXN0IGVycm9yIG9mIDU2LjU5IHVzaW5nIHRoZSBQQ1IgbWV0aG9kDQpgYGB7cn0NCiNSaWRnZQ0KcmlkZ2UuZml0ID0gY3YuZ2xtbmV0KHRyYWluc2V0LCB0cmFpbiRjcmltLCBhbHBoYT0wKQ0KbGFtYmRhID0gcmlkZ2UuZml0JGxhbWJkYS5taW4NCnJpZGdlLnByZWQgPSBwcmVkaWN0KHJpZGdlLmZpdCwgcz1sYW1iZGEsIG5ld3g9dGVzdHNldCkNCnJpZGdlLmVyciA9IG1lYW4oKHRlc3QkY3JpbSAtIHJpZGdlLnByZWQpXjIpDQpyaWRnZS5lcnINCg0KYGBgDQpXZSBnZXQgYSB0ZXN0IGVycm9yIG9mIDU2LjM0IHVzaW5nIFJpZGdlIFJlZ3Jlc3Npb24NCg0KKGIpIFByb3Bvc2UgYSBtb2RlbCAob3Igc2V0IG9mIG1vZGVscykgdGhhdCBzZWVtIHRvIHBlcmZvcm0gd2VsbCBvbg0KdGhpcyBkYXRhIHNldCwgYW5kIGp1c3RpZnkgeW91ciBhbnN3ZXIuIE1ha2Ugc3VyZSB0aGF0IHlvdSBhcmUNCmV2YWx1YXRpbmcgbW9kZWwgcGVyZm9ybWFuY2UgdXNpbmcgdmFsaWRhdGlvbiBzZXQgZXJyb3IsIGNyb3NzLXZhbGlkYXRpb24sIG9yIHNvbWUgb3RoZXIgcmVhc29uYWJsZSBhbHRlcm5hdGl2ZSwgYXMgb3Bwb3NlZCB0bw0KdXNpbmcgdHJhaW5pbmcgZXJyb3IuDQoNCkluIHBhcnQgQSwgd2UgYXJlIGdpdmVuIHRoZSB0ZXN0IGVycm9ycyBnaXZlbiB3ZXJlIDU1Ljk1IGZvciB0aGUgbGFzc28gcmVncmVzc2lvbiwgNTYuNTkgZm9yIHRoZSBQQ1IgbW9kZWwsIGFuZCA1Ni4zNCBmb3IgdGhlIHJpZGdlIHJlZ3Jlc3Npb24uIFRoZSBsb3dlc3QgdGVzdCBlcnJvciB3YXMgdGhlIGxhc3NvIG1vZGVsLCB0aGVyZWZvcmUgd2UgY2FuIHByb3Bvc2UgdGhhdCB0aGlzIG1vZGVsIHdvdWxkIHBlcmZvcm0gYmVzdCBjb21wYXJlZCB0byB0aGUgb3RoZXJzLiBIb3dldmVyIHRoZSBvdGhlciBtb2RlbHMgd2VyZSBjbG9zZSB0byB0aGUgbGFzc28gdGVjaG5pcXVlIHNvIGFsbCBjb3VsZCBoYXZlIHRoZSBwb3RlbnRpYWwgdG8gcGVyZm9ybSB3ZWxsLg0KDQooYykgRG9lcyB5b3VyIGNob3NlbiBtb2RlbCBpbnZvbHZlIGFsbCBvZiB0aGUgZmVhdHVyZXMgaW4gdGhlIGRhdGEgc2V0PyBXaHkgb3Igd2h5IG5vdD8NCg0KTm8sIGJlY2F1c2UgbXkgY2hvc2VuIG1vZGVsICh0aGUgbGFzc28gbW9kZWwpIHJlbW92ZXMgdmFyaWFibGVzIGRlZW1lZCDigJx1bmltcG9ydGFudOKAnSBvciBiYXJlbHkgc2lnbmlmaWNhbnQsIGFuZCBjaGFuZ2VzIHRoZWlyIGNvZWZmaWNpZW50cyB0byAwLg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K