Resampling methods involve repeatedly drawing samples from a training set and refitting a model of interest on each sample in order to obtain additional information about the fitted model. They may allow us to obtain information that would not be available from fitting the model only once using the original training sample.

Resampling approaches can be computationally expensive, because they involve fitting the same statistical method multiple times using different subsets of the training data. However, due to recent advances in computing power, the computational requirements of resampling methods generally are not prohibitive. In these exercises, we investigate two of the most commonly used resampling methods, cross-validation and the bootstrap.

Question 3.

We now review k-fold cross-validation. (a) Explain how k-fold cross-validation is implemented.

k-fold cross-validation involves randomly k-fold CV dividing the set of observations into k groups, or folds, of approximately equal size. The first fold is treated as a validation set, and the method is fit on the remaining k − 1 folds.
–Page 181, An Introduction to Statistical Learning, 2013.

(b) What are the advantages and disadvantages of k-fold crossvalidation relative to:

  • The validation set approach?
    The validation set approach is conceptually simple and easily implemented as you are simply partitioning the existing training data into two sets. However, there are two drawbacks: (1.) the estimate of the test error rate can be highly variable depending on which observations are included in the training and validation sets. (2.) the validation set error rate may tend to overestimate the test error rate for the model fit on the entire data set.

  • LOOCV?
    LOOCV is a special case of k-fold cross-validation with k = n. Thus, LOOCV is the most computationally intense method since the model must be fit n times. Also, LOOCV has higher variance, but lower bias, than k-fold CV.

Question 5.

In Chapter 4, we used logistic regression to predict the probability of default using income and balance on the Default data set. We will now estimate the test error of this logistic regression model using the validation set approach. Do not forget to set a random seed before beginning your analysis.

library(ISLR)
summary(Default)
 default    student       balance           income     
 No :9667   No :7056   Min.   :   0.0   Min.   :  772  
 Yes: 333   Yes:2944   1st Qu.: 481.7   1st Qu.:21340  
                       Median : 823.6   Median :34553  
                       Mean   : 835.4   Mean   :33517  
                       3rd Qu.:1166.3   3rd Qu.:43808  
                       Max.   :2654.3   Max.   :73554  
attach(Default)
set.seed(1)

(a) Fit a logistic regression model that uses income and balance to predict default.

glm.fit = glm(default ~ income + balance, data = Default, family = binomial)

(b) Using the validation set approach, estimate the test error of this model. In order to do this, you must perform the following steps:
1. Split the sample set into a training set and a validation set.
2. Fit a multiple logistic regression model using only the training observations. 3. Obtain a prediction of default status for each individual in the validation set by computing the posterior probability of default for that individual, and classifying the individual to the default category if the posterior probability is greater than 0.5.
4. Compute the validation set error, which is the fraction of the observations in the validation set that are misclassified.

FiveB = function() {
    # i.
    train = sample(dim(Default)[1], dim(Default)[1]/2)
    # ii.
    glm.fit = glm(default ~ income + balance, data = Default, family = binomial, 
        subset = train)
    # iii.
    glm.pred = rep("No", dim(Default)[1]/2)
    glm.probs = predict(glm.fit, Default[-train, ], type = "response")
    glm.pred[glm.probs > 0.5] = "Yes"
    # iv.
    return(mean(glm.pred != Default[-train, ]$default))
}
FiveB()
[1] 0.0254

(c) Repeat the process in (b) three times, using three different splits of the observations into a training set and a validation set. Comment on the results obtained.

FiveB()
[1] 0.0274
FiveB()
[1] 0.0244
FiveB()
[1] 0.0244

The error rate seems to hover between 0.024 and 0.026.

(d) Now consider a logistic regression model that predicts the probability of default using income, balance, and a dummy variable for student. Estimate the test error for this model using the validation set approach. Comment on whether or not including a dummy variable for student leads to a reduction in the test error rate.

train = sample(dim(Default)[1], dim(Default)[1]/2)
glm.fit = glm(default ~ income + balance + student, data = Default, family = binomial, 
    subset = train)
glm.pred = rep("No", dim(Default)[1]/2)
glm.probs = predict(glm.fit, Default[-train, ], type = "response")
glm.pred[glm.probs > 0.5] = "Yes"
mean(glm.pred != Default[-train, ]$default)
[1] 0.0262

The higher estimated test error rate including the student dummy variable indicates that adding the student dummy variable does not appear to lead to a reduction in the test error rate.

Question 6.

We continue to consider the use of a logistic regression model to predict the probability of default using income and balance on the Default data set. In particular, we will now compute estimates for the standard errors of the income and balance logistic regression coefficients in two different ways: (1) using the bootstrap, and (2) using the standard formula for computing the standard errors in the glm() function. Do not forget to set a random seed before beginning your analysis.

(a) Using the summary() and glm() functions, determine the estimated standard errors for the coefficients associated with income and balance in a multiple logistic regression model that uses both predictors.

set.seed(1)
glm.fit = glm(default ~ income + balance, data = Default, family = binomial)
summary(glm.fit)

Call:
glm(formula = default ~ income + balance, family = binomial, 
    data = Default)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.4725  -0.1444  -0.0574  -0.0211   3.7245  

Coefficients:
              Estimate Std. Error z value Pr(>|z|)    
(Intercept) -1.154e+01  4.348e-01 -26.545  < 2e-16 ***
income       2.081e-05  4.985e-06   4.174 2.99e-05 ***
balance      5.647e-03  2.274e-04  24.836  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 2920.6  on 9999  degrees of freedom
Residual deviance: 1579.0  on 9997  degrees of freedom
AIC: 1585

Number of Fisher Scoring iterations: 8

(b) Write a function, boot.fn(), that takes as input the Default data set as well as an index of the observations, and that outputs the coefficient estimates for income and balance in the multiple logistic regression model.

boot.fn = function(data, index){
  return(coef(glm(default ~ income + balance, data = data, family = binomial, subset = index)))
}

(c) Use the boot() function together with your boot.fn() function to estimate the standard errors of the logistic regression coefficients for income and balance.

library(boot)
boot(Default, boot.fn, 50)

ORDINARY NONPARAMETRIC BOOTSTRAP


Call:
boot(data = Default, statistic = boot.fn, R = 50)


Bootstrap Statistics :
         original        bias     std. error
t1* -1.154047e+01 -5.661486e-02 4.847786e-01
t2*  2.080898e-05 -7.436578e-08 4.456965e-06
t3*  5.647103e-03  1.854126e-05 2.639029e-04

(d) Comment on the estimated standard errors obtained using the glm() function and using your bootstrap function.

The estimates from the two separate estimation methods are similar all the way to the second and third significant digits.

Question 7.

In Sections 5.3.2 and 5.3.3, we saw that the cv.glm() function can be used in order to compute the LOOCV test error estimate. Alternatively, one could compute those quantities using just the glm() and predict.glm() functions, and a for loop. You will now take this approach in order to compute the LOOCV error for a simple logistic regression model on the Weekly data set. Recall that in the context of classification problems, the LOOCV error is given in (5.4).

detach(Default)
summary(Weekly)
      Year           Lag1         
 Min.   :1990   Min.   :-18.1950  
 1st Qu.:1995   1st Qu.: -1.1540  
 Median :2000   Median :  0.2410  
 Mean   :2000   Mean   :  0.1506  
 3rd Qu.:2005   3rd Qu.:  1.4050  
 Max.   :2010   Max.   : 12.0260  
      Lag2               Lag3         
 Min.   :-18.1950   Min.   :-18.1950  
 1st Qu.: -1.1540   1st Qu.: -1.1580  
 Median :  0.2410   Median :  0.2410  
 Mean   :  0.1511   Mean   :  0.1472  
 3rd Qu.:  1.4090   3rd Qu.:  1.4090  
 Max.   : 12.0260   Max.   : 12.0260  
      Lag4               Lag5         
 Min.   :-18.1950   Min.   :-18.1950  
 1st Qu.: -1.1580   1st Qu.: -1.1660  
 Median :  0.2380   Median :  0.2340  
 Mean   :  0.1458   Mean   :  0.1399  
 3rd Qu.:  1.4090   3rd Qu.:  1.4050  
 Max.   : 12.0260   Max.   : 12.0260  
     Volume            Today         
 Min.   :0.08747   Min.   :-18.1950  
 1st Qu.:0.33202   1st Qu.: -1.1540  
 Median :1.00268   Median :  0.2410  
 Mean   :1.57462   Mean   :  0.1499  
 3rd Qu.:2.05373   3rd Qu.:  1.4050  
 Max.   :9.32821   Max.   : 12.0260  
 Direction 
 Down:484  
 Up  :605  
           
           
           
           
set.seed(1)
attach(Weekly)

(a) Fit a logistic regression model that predicts Direction using Lag1 and Lag2.

glm.fit = glm(Direction ~ Lag1 + Lag2, data = Weekly, family = binomial)
summary(glm.fit)

Call:
glm(formula = Direction ~ Lag1 + Lag2, family = binomial, data = Weekly)

Deviance Residuals: 
   Min      1Q  Median      3Q     Max  
-1.623  -1.261   1.001   1.083   1.506  

Coefficients:
            Estimate Std. Error z value
(Intercept)  0.22122    0.06147   3.599
Lag1        -0.03872    0.02622  -1.477
Lag2         0.06025    0.02655   2.270
            Pr(>|z|)    
(Intercept) 0.000319 ***
Lag1        0.139672    
Lag2        0.023232 *  
---
Signif. codes:  
  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’
  0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 1496.2  on 1088  degrees of freedom
Residual deviance: 1488.2  on 1086  degrees of freedom
AIC: 1494.2

Number of Fisher Scoring iterations: 4

(b) Fit a logistic regression model that predicts Direction using Lag1 and Lag2 using all but the first observation.

glm.fit = glm(Direction ~ Lag1 + Lag2, data = Weekly[-1, ], family = binomial)
summary(glm.fit)

(c) Use the model from (b) to predict the direction of the first observation. You can do this by predicting that the first observation will go up if P(Direction="Up"|Lag1, Lag2) > 0.5. Was this observation correctly classified?

predict.glm(glm.fit, Weekly[1, ], type = "response") > 0.5
   1 
TRUE 
Weekly$Direction[1]
[1] Down
Levels: Down Up

We predict the Direction to be UP; the true Direction was DOWN.

(d) Write a for loop from i = 1 to i = n, where n is the number of observations in the data set, that performs each of the following steps:

1. Fit a logistic regression model using all but the ith observation to predict Direction using Lag1 and Lag2.
2. Compute the posterior probability of the market moving up for the ith observation.
3. Use the posterior probability for the ith observation in order to predict whether or not the market moves up.
4. Determine whether or not an error was made in predicting the direction for the ith observation. If an error was made, then indicate this as a 1, and otherwise indicate it as a 0.

count = rep(0, dim(Weekly)[1])
for (i in 1:(dim(Weekly)[1])) {
    glm.fit = glm(Direction ~ Lag1 + Lag2, data = Weekly[-i, ], family = binomial)
    is_up = predict.glm(glm.fit, Weekly[i, ], type = "response") > 0.5
    is_true_up = Weekly[i, ]$Direction == "Up"
    if (is_up != is_true_up) 
        count[i] = 1
}
sum(count)
[1] 490

490 errors.

(e) Take the average of the n numbers obtained in (d)iv in order to obtain the LOOCV estimate for the test error. Comment on the results.

mean(count)
[1] 0.4499541

LOOCV estimates a test error rate of 45%.

LS0tDQp0aXRsZTogIkNoLiA1IFJlc2FtcGxpbmcgTWV0aG9kcyINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQotLS0NClJlc2FtcGxpbmcgbWV0aG9kcyBpbnZvbHZlIHJlcGVhdGVkbHkgZHJhd2luZyBzYW1wbGVzIGZyb20gYSB0cmFpbmluZyBzZXQgYW5kIHJlZml0dGluZyBhIG1vZGVsIG9mIGludGVyZXN0IG9uIGVhY2ggc2FtcGxlIGluIG9yZGVyIHRvIG9idGFpbiBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGFib3V0IHRoZSBmaXR0ZWQgbW9kZWwuIFRoZXkgbWF5IGFsbG93IHVzIHRvIG9idGFpbiBpbmZvcm1hdGlvbiB0aGF0IHdvdWxkIG5vdCBiZSBhdmFpbGFibGUgZnJvbSBmaXR0aW5nIHRoZSBtb2RlbCBvbmx5IG9uY2UgdXNpbmcgdGhlIG9yaWdpbmFsIHRyYWluaW5nIHNhbXBsZS4NCg0KUmVzYW1wbGluZyBhcHByb2FjaGVzIGNhbiBiZSBjb21wdXRhdGlvbmFsbHkgZXhwZW5zaXZlLCBiZWNhdXNlIHRoZXkgaW52b2x2ZSBmaXR0aW5nIHRoZSBzYW1lIHN0YXRpc3RpY2FsIG1ldGhvZCBtdWx0aXBsZSB0aW1lcyB1c2luZyBkaWZmZXJlbnQgc3Vic2V0cyBvZiB0aGUgdHJhaW5pbmcgZGF0YS4gSG93ZXZlciwgZHVlIHRvIHJlY2VudCBhZHZhbmNlcyBpbiBjb21wdXRpbmcgcG93ZXIsIHRoZSBjb21wdXRhdGlvbmFsIHJlcXVpcmVtZW50cyBvZiByZXNhbXBsaW5nIG1ldGhvZHMgZ2VuZXJhbGx5IGFyZSBub3QgcHJvaGliaXRpdmUuIEluIHRoZXNlIGV4ZXJjaXNlcywgd2UgaW52ZXN0aWdhdGUgdHdvIG9mIHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgcmVzYW1wbGluZyBtZXRob2RzLCBjcm9zcy12YWxpZGF0aW9uIGFuZCB0aGUgYm9vdHN0cmFwLg0KDQojIyMgUXVlc3Rpb24gMy4gICAgDQoqKldlIG5vdyByZXZpZXcgKmsqLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbi4qKg0KKiooYSkgRXhwbGFpbiBob3cgKmsqLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBpcyBpbXBsZW1lbnRlZC4qKg0KDQo+ICprKi1mb2xkIGNyb3NzLXZhbGlkYXRpb24gaW52b2x2ZXMgcmFuZG9tbHkgKmsqLWZvbGQgQ1YgZGl2aWRpbmcgdGhlIHNldCBvZiBvYnNlcnZhdGlvbnMgaW50byBrIGdyb3Vwcywgb3IgZm9sZHMsIG9mIGFwcHJveGltYXRlbHkgZXF1YWwgc2l6ZS4gVGhlIGZpcnN0IGZvbGQgaXMgdHJlYXRlZCBhcyBhIHZhbGlkYXRpb24gc2V0LCBhbmQgdGhlIG1ldGhvZCBpcyBmaXQgb24gdGhlIHJlbWFpbmluZyAqayog4oiSIDEgZm9sZHMuICANCi0tUGFnZSAxODEsIFtBbiBJbnRyb2R1Y3Rpb24gdG8gU3RhdGlzdGljYWxdKGh0dHA6Ly9mYWN1bHR5Lm1hcnNoYWxsLnVzYy5lZHUvZ2FyZXRoLWphbWVzL0lTTC9JU0xSJTIwU2V2ZW50aCUyMFByaW50aW5nLnBkZikgTGVhcm5pbmcsIDIwMTMuDQoNCioqKGIpIFdoYXQgYXJlIHRoZSBhZHZhbnRhZ2VzIGFuZCBkaXNhZHZhbnRhZ2VzIG9mICprKi1mb2xkIGNyb3NzdmFsaWRhdGlvbiByZWxhdGl2ZSB0bzoqKg0KDQogLSAqKlRoZSB2YWxpZGF0aW9uIHNldCBhcHByb2FjaD8qKiAgDQpUaGUgdmFsaWRhdGlvbiBzZXQgYXBwcm9hY2ggaXMgY29uY2VwdHVhbGx5IHNpbXBsZSBhbmQgZWFzaWx5IGltcGxlbWVudGVkIGFzIHlvdSBhcmUgc2ltcGx5IHBhcnRpdGlvbmluZyB0aGUgZXhpc3RpbmcgdHJhaW5pbmcgZGF0YSBpbnRvIHR3byBzZXRzLiBIb3dldmVyLCB0aGVyZSBhcmUgdHdvIGRyYXdiYWNrczogKDEuKSB0aGUgZXN0aW1hdGUgb2YgdGhlIHRlc3QgZXJyb3IgcmF0ZSBjYW4gYmUgaGlnaGx5IHZhcmlhYmxlIGRlcGVuZGluZyBvbiB3aGljaCBvYnNlcnZhdGlvbnMgYXJlIGluY2x1ZGVkIGluIHRoZSB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbiBzZXRzLiAoMi4pIHRoZSB2YWxpZGF0aW9uIHNldCBlcnJvciByYXRlIG1heSB0ZW5kIHRvIG92ZXJlc3RpbWF0ZSB0aGUgdGVzdCBlcnJvciByYXRlIGZvciB0aGUgbW9kZWwgZml0IG9uIHRoZSBlbnRpcmUgZGF0YSBzZXQuDQoNCiAtICoqTE9PQ1Y/KiogIA0KTE9PQ1YgaXMgYSBzcGVjaWFsIGNhc2Ugb2YgKmsqLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiB3aXRoICprKiA9ICpuKi4gVGh1cywgTE9PQ1YgaXMgdGhlIG1vc3QgY29tcHV0YXRpb25hbGx5IGludGVuc2UgbWV0aG9kIHNpbmNlIHRoZSBtb2RlbCBtdXN0IGJlIGZpdCBuIHRpbWVzLiBBbHNvLCBMT09DViBoYXMgaGlnaGVyIHZhcmlhbmNlLCBidXQgbG93ZXIgYmlhcywgdGhhbiAqayotZm9sZCBDVi4NCg0KIyMjIFF1ZXN0aW9uIDUuICANCioqSW4gQ2hhcHRlciA0LCB3ZSB1c2VkIGxvZ2lzdGljIHJlZ3Jlc3Npb24gdG8gcHJlZGljdCB0aGUgcHJvYmFiaWxpdHkgb2YgYGRlZmF1bHRgIHVzaW5nIGBpbmNvbWVgIGFuZCBgYmFsYW5jZWAgb24gdGhlIGBEZWZhdWx0YCBkYXRhIHNldC4gV2Ugd2lsbCBub3cgZXN0aW1hdGUgdGhlIHRlc3QgZXJyb3Igb2YgdGhpcyBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIHRoZSB2YWxpZGF0aW9uIHNldCBhcHByb2FjaC4gRG8gbm90IGZvcmdldCB0byBzZXQgYSByYW5kb20gc2VlZCBiZWZvcmUgYmVnaW5uaW5nIHlvdXIgYW5hbHlzaXMuKioNCg0KYGBge3J9DQpsaWJyYXJ5KElTTFIpDQpzdW1tYXJ5KERlZmF1bHQpDQphdHRhY2goRGVmYXVsdCkNCnNldC5zZWVkKDEpDQpgYGANCg0KKiooYSkgRml0IGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0aGF0IHVzZXMgYGluY29tZWAgYW5kIGBiYWxhbmNlYCB0byBwcmVkaWN0IGBkZWZhdWx0YC4qKg0KYGBge3J9DQpnbG0uZml0ID0gZ2xtKGRlZmF1bHQgfiBpbmNvbWUgKyBiYWxhbmNlLCBkYXRhID0gRGVmYXVsdCwgZmFtaWx5ID0gYmlub21pYWwpDQpgYGANCg0KKiooYikgVXNpbmcgdGhlIHZhbGlkYXRpb24gc2V0IGFwcHJvYWNoLCBlc3RpbWF0ZSB0aGUgdGVzdCBlcnJvciBvZiB0aGlzIG1vZGVsLiBJbiBvcmRlciB0byBkbyB0aGlzLCB5b3UgbXVzdCBwZXJmb3JtIHRoZSBmb2xsb3dpbmcgc3RlcHM6KiogIA0KKioxLioqIFNwbGl0IHRoZSBzYW1wbGUgc2V0IGludG8gYSB0cmFpbmluZyBzZXQgYW5kIGEgdmFsaWRhdGlvbiBzZXQuICANCioqMi4qKiBGaXQgYSBtdWx0aXBsZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIG9ubHkgdGhlIHRyYWluaW5nIG9ic2VydmF0aW9ucy4gDQoqKjMuKiogT2J0YWluIGEgcHJlZGljdGlvbiBvZiBkZWZhdWx0IHN0YXR1cyBmb3IgZWFjaCBpbmRpdmlkdWFsIGluIHRoZSB2YWxpZGF0aW9uIHNldCBieSBjb21wdXRpbmcgdGhlIHBvc3RlcmlvciBwcm9iYWJpbGl0eSBvZiBkZWZhdWx0IGZvciB0aGF0IGluZGl2aWR1YWwsIGFuZCBjbGFzc2lmeWluZyB0aGUgaW5kaXZpZHVhbCB0byB0aGUgYGRlZmF1bHRgIGNhdGVnb3J5IGlmIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgaXMgZ3JlYXRlciB0aGFuIDAuNS4gICANCioqNC4qKiBDb21wdXRlIHRoZSB2YWxpZGF0aW9uIHNldCBlcnJvciwgd2hpY2ggaXMgdGhlIGZyYWN0aW9uIG9mIHRoZSBvYnNlcnZhdGlvbnMgaW4gdGhlIHZhbGlkYXRpb24gc2V0IHRoYXQgYXJlIG1pc2NsYXNzaWZpZWQuDQogDQpgYGB7cn0NCkZpdmVCID0gZnVuY3Rpb24oKSB7DQogICMgaS4NCiAgdHJhaW4gPSBzYW1wbGUoZGltKERlZmF1bHQpWzFdLCBkaW0oRGVmYXVsdClbMV0vMikNCiAgIyBpaS4NCiAgZ2xtLmZpdCA9IGdsbShkZWZhdWx0IH4gaW5jb21lICsgYmFsYW5jZSwgZGF0YSA9IERlZmF1bHQsIGZhbWlseSA9IGJpbm9taWFsLCBzdWJzZXQgPSB0cmFpbikNCiAgIyBpaWkuDQogIGdsbS5wcmVkID0gcmVwKCJObyIsIGRpbShEZWZhdWx0KVsxXS8yKQ0KICBnbG0ucHJvYnMgPSBwcmVkaWN0KGdsbS5maXQsIERlZmF1bHRbLXRyYWluLCBdLCB0eXBlID0gInJlc3BvbnNlIikNCiAgZ2xtLnByZWRbZ2xtLnByb2JzID4gMC41XSA9ICJZZXMiDQogICMgaXYuDQogIHJldHVybihtZWFuKGdsbS5wcmVkICE9IERlZmF1bHRbLXRyYWluLF0kZGVmYXVsdCkpDQp9DQpGaXZlQigpDQpgYGANCg0KKiooYykgUmVwZWF0IHRoZSBwcm9jZXNzIGluIChiKSB0aHJlZSB0aW1lcywgdXNpbmcgdGhyZWUgZGlmZmVyZW50IHNwbGl0cyBvZiB0aGUgb2JzZXJ2YXRpb25zIGludG8gYSB0cmFpbmluZyBzZXQgYW5kIGEgdmFsaWRhdGlvbiBzZXQuIENvbW1lbnQgb24gdGhlIHJlc3VsdHMgb2J0YWluZWQuKioNCg0KYGBge3J9DQpGaXZlQigpDQpGaXZlQigpDQpGaXZlQigpDQpgYGANClRoZSBlcnJvciByYXRlIHNlZW1zIHRvIGhvdmVyIGJldHdlZW4gMC4wMjQgYW5kIDAuMDI2Lg0KDQoqKihkKSBOb3cgY29uc2lkZXIgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHRoYXQgcHJlZGljdHMgdGhlIHByb2JhYmlsaXR5IG9mIGBkZWZhdWx0YCB1c2luZyBgaW5jb21lYCwgYGJhbGFuY2VgLCBhbmQgYSBkdW1teSB2YXJpYWJsZSBmb3IgYHN0dWRlbnRgLiBFc3RpbWF0ZSB0aGUgdGVzdCBlcnJvciBmb3IgdGhpcyBtb2RlbCB1c2luZyB0aGUgdmFsaWRhdGlvbiBzZXQgYXBwcm9hY2guIENvbW1lbnQgb24gd2hldGhlciBvciBub3QgaW5jbHVkaW5nIGEgZHVtbXkgdmFyaWFibGUgZm9yIGBzdHVkZW50YCBsZWFkcyB0byBhIHJlZHVjdGlvbiBpbiB0aGUgdGVzdCBlcnJvciByYXRlLioqDQoNCmBgYHtyfQ0KdHJhaW4gPSBzYW1wbGUoZGltKERlZmF1bHQpWzFdLCBkaW0oRGVmYXVsdClbMV0vMikNCmdsbS5maXQgPSBnbG0oZGVmYXVsdCB+IGluY29tZSArIGJhbGFuY2UgKyBzdHVkZW50LCBkYXRhID0gRGVmYXVsdCwgZmFtaWx5ID0gYmlub21pYWwsIA0KICAgIHN1YnNldCA9IHRyYWluKQ0KZ2xtLnByZWQgPSByZXAoIk5vIiwgZGltKERlZmF1bHQpWzFdLzIpDQpnbG0ucHJvYnMgPSBwcmVkaWN0KGdsbS5maXQsIERlZmF1bHRbLXRyYWluLCBdLCB0eXBlID0gInJlc3BvbnNlIikNCmdsbS5wcmVkW2dsbS5wcm9icyA+IDAuNV0gPSAiWWVzIg0KbWVhbihnbG0ucHJlZCAhPSBEZWZhdWx0Wy10cmFpbiwgXSRkZWZhdWx0KQ0KYGBgDQpUaGUgaGlnaGVyIGVzdGltYXRlZCB0ZXN0IGVycm9yIHJhdGUgaW5jbHVkaW5nIHRoZSBzdHVkZW50IGR1bW15IHZhcmlhYmxlIGluZGljYXRlcyB0aGF0IGFkZGluZyB0aGUgc3R1ZGVudCBkdW1teSB2YXJpYWJsZSBkb2VzIG5vdCBhcHBlYXIgdG8gbGVhZCB0byBhIHJlZHVjdGlvbiBpbiB0aGUgdGVzdCBlcnJvciByYXRlLg0KDQojIyMgUXVlc3Rpb24gNi4gIA0KKipXZSBjb250aW51ZSB0byBjb25zaWRlciB0aGUgdXNlIG9mIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0byBwcmVkaWN0IHRoZSBwcm9iYWJpbGl0eSBvZiBgZGVmYXVsdGAgdXNpbmcgYGluY29tZWAgYW5kIGBiYWxhbmNlYCBvbiB0aGUgYERlZmF1bHRgIGRhdGEgc2V0LiBJbiBwYXJ0aWN1bGFyLCB3ZSB3aWxsIG5vdyBjb21wdXRlIGVzdGltYXRlcyBmb3IgdGhlIHN0YW5kYXJkIGVycm9ycyBvZiB0aGUgYGluY29tZWAgYW5kIGBiYWxhbmNlYCBsb2dpc3RpYyByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBpbiB0d28gZGlmZmVyZW50IHdheXM6ICgxKSB1c2luZyB0aGUgYm9vdHN0cmFwLCBhbmQgKDIpIHVzaW5nIHRoZSBzdGFuZGFyZCBmb3JtdWxhIGZvciBjb21wdXRpbmcgdGhlIHN0YW5kYXJkIGVycm9ycyBpbiB0aGUgYGdsbSgpYCBmdW5jdGlvbi4gRG8gbm90IGZvcmdldCB0byBzZXQgYSByYW5kb20gc2VlZCBiZWZvcmUgYmVnaW5uaW5nIHlvdXIgYW5hbHlzaXMuKioNCg0KKiooYSkgVXNpbmcgdGhlIGBzdW1tYXJ5KClgIGFuZCBgZ2xtKClgIGZ1bmN0aW9ucywgZGV0ZXJtaW5lIHRoZSBlc3RpbWF0ZWQgc3RhbmRhcmQgZXJyb3JzIGZvciB0aGUgY29lZmZpY2llbnRzIGFzc29jaWF0ZWQgd2l0aCBgaW5jb21lYCBhbmQgYGJhbGFuY2VgIGluIGEgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0aGF0IHVzZXMgYm90aCBwcmVkaWN0b3JzLioqDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkNCmdsbS5maXQgPSBnbG0oZGVmYXVsdCB+IGluY29tZSArIGJhbGFuY2UsIGRhdGEgPSBEZWZhdWx0LCBmYW1pbHkgPSBiaW5vbWlhbCkNCnN1bW1hcnkoZ2xtLmZpdCkNCmBgYA0KDQoqKihiKSBXcml0ZSBhIGZ1bmN0aW9uLCBgYm9vdC5mbigpYCwgdGhhdCB0YWtlcyBhcyBpbnB1dCB0aGUgYERlZmF1bHRgIGRhdGEgc2V0IGFzIHdlbGwgYXMgYW4gaW5kZXggb2YgdGhlIG9ic2VydmF0aW9ucywgYW5kIHRoYXQgb3V0cHV0cyB0aGUgY29lZmZpY2llbnQgZXN0aW1hdGVzIGZvciBgaW5jb21lYCBhbmQgYGJhbGFuY2VgIGluIHRoZSBtdWx0aXBsZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLioqDQoNCmBgYHtyfQ0KYm9vdC5mbiA9IGZ1bmN0aW9uKGRhdGEsIGluZGV4KXsNCiAgcmV0dXJuKGNvZWYoZ2xtKGRlZmF1bHQgfiBpbmNvbWUgKyBiYWxhbmNlLCBkYXRhID0gZGF0YSwgZmFtaWx5ID0gYmlub21pYWwsIHN1YnNldCA9IGluZGV4KSkpDQp9DQpgYGANCg0KKiooYykgVXNlIHRoZSBgYm9vdCgpYCBmdW5jdGlvbiB0b2dldGhlciB3aXRoIHlvdXIgYGJvb3QuZm4oKWAgZnVuY3Rpb24gdG8gZXN0aW1hdGUgdGhlIHN0YW5kYXJkIGVycm9ycyBvZiB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgZm9yIGBpbmNvbWVgIGFuZCBgYmFsYW5jZWAuKioNCg0KYGBge3J9DQpsaWJyYXJ5KGJvb3QpDQpib290KERlZmF1bHQsIGJvb3QuZm4sIDUwKQ0KYGBgDQoNCioqKGQpIENvbW1lbnQgb24gdGhlIGVzdGltYXRlZCBzdGFuZGFyZCBlcnJvcnMgb2J0YWluZWQgdXNpbmcgdGhlIGBnbG0oKWAgZnVuY3Rpb24gYW5kIHVzaW5nIHlvdXIgYm9vdHN0cmFwIGZ1bmN0aW9uLioqDQoNClRoZSBlc3RpbWF0ZXMgZnJvbSB0aGUgdHdvIHNlcGFyYXRlIGVzdGltYXRpb24gbWV0aG9kcyBhcmUgc2ltaWxhciBhbGwgdGhlIHdheSB0byB0aGUgc2Vjb25kIGFuZCB0aGlyZCBzaWduaWZpY2FudCBkaWdpdHMuDQoNCiMjIyBRdWVzdGlvbiA3LiAgDQoNCioqSW4gU2VjdGlvbnMgNS4zLjIgYW5kIDUuMy4zLCB3ZSBzYXcgdGhhdCB0aGUgYGN2LmdsbSgpYCBmdW5jdGlvbiBjYW4gYmUgdXNlZCBpbiBvcmRlciB0byBjb21wdXRlIHRoZSBMT09DViB0ZXN0IGVycm9yIGVzdGltYXRlLiBBbHRlcm5hdGl2ZWx5LCBvbmUgY291bGQgY29tcHV0ZSB0aG9zZSBxdWFudGl0aWVzIHVzaW5nIGp1c3QgdGhlIGBnbG0oKWAgYW5kIGBwcmVkaWN0LmdsbSgpYCBmdW5jdGlvbnMsIGFuZCBhIGZvciBsb29wLiBZb3Ugd2lsbCBub3cgdGFrZSB0aGlzIGFwcHJvYWNoIGluIG9yZGVyIHRvIGNvbXB1dGUgdGhlIExPT0NWIGVycm9yIGZvciBhIHNpbXBsZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIG9uIHRoZSBgV2Vla2x5YCBkYXRhIHNldC4gUmVjYWxsIHRoYXQgaW4gdGhlIGNvbnRleHQgb2YgY2xhc3NpZmljYXRpb24gcHJvYmxlbXMsIHRoZSBMT09DViBlcnJvciBpcyBnaXZlbiBpbiAoNS40KS4qKg0KDQpgYGB7cn0NCmRldGFjaChEZWZhdWx0KQ0Kc3VtbWFyeShXZWVrbHkpDQpzZXQuc2VlZCgxKQ0KYXR0YWNoKFdlZWtseSkNCmBgYA0KDQoqKihhKSBGaXQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHRoYXQgcHJlZGljdHMgRGlyZWN0aW9uIHVzaW5nIGBMYWcxYCBhbmQgYExhZzJgLioqDQoNCmBgYHtyfQ0KZ2xtLmZpdCA9IGdsbShEaXJlY3Rpb24gfiBMYWcxICsgTGFnMiwgZGF0YSA9IFdlZWtseSwgZmFtaWx5ID0gYmlub21pYWwpDQpzdW1tYXJ5KGdsbS5maXQpDQpgYGANCg0KKiooYikgRml0IGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0aGF0IHByZWRpY3RzIERpcmVjdGlvbiB1c2luZyBgTGFnMWAgYW5kIGBMYWcyYCB1c2luZyBhbGwgYnV0IHRoZSBmaXJzdCBvYnNlcnZhdGlvbi4qKg0KDQpgYGB7cn0NCmdsbS5maXQgPSBnbG0oRGlyZWN0aW9uIH4gTGFnMSArIExhZzIsIGRhdGEgPSBXZWVrbHlbLTEsIF0sIGZhbWlseSA9IGJpbm9taWFsKQ0Kc3VtbWFyeShnbG0uZml0KQ0KYGBgDQoNCioqKGMpIFVzZSB0aGUgbW9kZWwgZnJvbSAoYikgdG8gcHJlZGljdCB0aGUgZGlyZWN0aW9uIG9mIHRoZSBmaXJzdCBvYnNlcnZhdGlvbi4gWW91IGNhbiBkbyB0aGlzIGJ5IHByZWRpY3RpbmcgdGhhdCB0aGUgZmlyc3Qgb2JzZXJ2YXRpb24gd2lsbCBnbyB1cCBpZiAqUCooYERpcmVjdGlvbj0iVXAifExhZzFgLCBgTGFnMmApID4gMC41LiBXYXMgdGhpcyBvYnNlcnZhdGlvbiBjb3JyZWN0bHkgY2xhc3NpZmllZD8qKg0KDQpgYGB7cn0NCnByZWRpY3QuZ2xtKGdsbS5maXQsIFdlZWtseVsxLCBdLCB0eXBlID0gInJlc3BvbnNlIikgPiAwLjUNCg0KV2Vla2x5JERpcmVjdGlvblsxXQ0KYGBgDQpXZSBwcmVkaWN0IHRoZSBgRGlyZWN0aW9uYCB0byBiZSBgVVBgOyB0aGUgdHJ1ZSBgRGlyZWN0aW9uYCB3YXMgYERPV05gLg0KDQpfXyhkKSBXcml0ZSBhIGZvciBsb29wIGZyb20gKmkqID0gMSB0byAqaSogPSAqbiosIHdoZXJlICpuKiBpcyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiB0aGUgZGF0YSBzZXQsIHRoYXQgcGVyZm9ybXMgZWFjaCBvZiB0aGUgZm9sbG93aW5nIHN0ZXBzOl9fDQoNCl9fMS5fXyBGaXQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIGFsbCBidXQgdGhlIGl0aCBvYnNlcnZhdGlvbiB0byBwcmVkaWN0IGBEaXJlY3Rpb25gIHVzaW5nIGBMYWcxYCBhbmQgYExhZzJgLiAgDQpfXzIuX18gQ29tcHV0ZSB0aGUgcG9zdGVyaW9yIHByb2JhYmlsaXR5IG9mIHRoZSBtYXJrZXQgbW92aW5nIHVwIGZvciB0aGUgKmkqdGggb2JzZXJ2YXRpb24uICAgDQpfXzMuX18gVXNlIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgZm9yIHRoZSAqaSp0aCBvYnNlcnZhdGlvbiBpbiBvcmRlciB0byBwcmVkaWN0IHdoZXRoZXIgb3Igbm90IHRoZSBtYXJrZXQgbW92ZXMgdXAuICANCl9fNC5fXyBEZXRlcm1pbmUgd2hldGhlciBvciBub3QgYW4gZXJyb3Igd2FzIG1hZGUgaW4gcHJlZGljdGluZyB0aGUgZGlyZWN0aW9uIGZvciB0aGUgKmkqdGggb2JzZXJ2YXRpb24uIElmIGFuIGVycm9yIHdhcyBtYWRlLCB0aGVuIGluZGljYXRlIHRoaXMgYXMgYSAxLCBhbmQgb3RoZXJ3aXNlIGluZGljYXRlIGl0IGFzIGEgMC4NCiANCmBgYHtyfQ0KY291bnQgPSByZXAoMCwgZGltKFdlZWtseSlbMV0pDQpmb3IgKGkgaW4gMTooZGltKFdlZWtseSlbMV0pKSB7DQogICAgZ2xtLmZpdCA9IGdsbShEaXJlY3Rpb24gfiBMYWcxICsgTGFnMiwgZGF0YSA9IFdlZWtseVstaSwgXSwgZmFtaWx5ID0gYmlub21pYWwpDQogICAgaXNfdXAgPSBwcmVkaWN0LmdsbShnbG0uZml0LCBXZWVrbHlbaSwgXSwgdHlwZSA9ICJyZXNwb25zZSIpID4gMC41DQogICAgaXNfdHJ1ZV91cCA9IFdlZWtseVtpLCBdJERpcmVjdGlvbiA9PSAiVXAiDQogICAgaWYgKGlzX3VwICE9IGlzX3RydWVfdXApIA0KICAgICAgICBjb3VudFtpXSA9IDENCn0NCnN1bShjb3VudCkNCmBgYA0KNDkwIGVycm9ycy4NCg0KKiooZSkgVGFrZSB0aGUgYXZlcmFnZSBvZiB0aGUgX25fIG51bWJlcnMgb2J0YWluZWQgaW4gKGQpaXYgaW4gb3JkZXIgdG8gb2J0YWluIHRoZSBMT09DViBlc3RpbWF0ZSBmb3IgdGhlIHRlc3QgZXJyb3IuIENvbW1lbnQgb24gdGhlIHJlc3VsdHMuKioNCg0KYGBge3J9DQptZWFuKGNvdW50KQ0KYGBgDQpMT09DViBlc3RpbWF0ZXMgYSB0ZXN0IGVycm9yIHJhdGUgb2YgNDUlLg==