Q3a

It’s done by splitting the data into k in roughly equal groups. Then it’s trained on all but one group then tested on the group that was left out. Then this is repeated “k” times, then it round the test errors to give one big overall estimate of the model’s test error.

Q3b(i)

The validation set approach has the ability to use data more efficiently. It also separates some of the data and doesn’t use it to train the model so the results depend a lot of the data in the training sets. Cross-validation wouldn’t have this problem because it uses all data in the training and testing at some point.However, cross-validation would take longer to run because it has to fit “k” amount of times.

Q3b(ii)

LOOCV compared to k-fold cross-validation is usually less computationally expensive. LOOCV leaves out one single observation at a time, so it has to fit n times, in which n is the number of observations. This can take a long time for a large data set, while k-fold cross-validation only has to do it k number of times. However LOOCV would be less bias because each model is trained on larger portions of the data. Additionally, LOOCV may be more respective of outliers in the data set making more variance than k-fold cross-validation would.

Q5a

library(ISLR2)
data(Default)
Default <- na.omit(Default)

Default$default <- as.factor(Default$default)
Default$student <- as.factor(Default$student)

set.seed(146)

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

summary(glm.fit)

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

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

Q5b

library(rsample)
set.seed(146)

data_split <- initial_split(Default, prop = 0.80, strata = default)

train_data1 <- training(data_split)
test_data1  <- testing(data_split)

glm.fit.train <- glm(default ~ income + balance,
                     data = train_data1,
                     family = binomial)

glm.probs <- predict(glm.fit.train,
                     newdata = test_data1,
                     type = "response")

glm.pred <- rep("No", nrow(test_data1))
glm.pred[glm.probs > 0.5] <- "Yes"

validation.error <- mean(glm.pred != test_data1$default)
validation.error
[1] 0.0215

Q5c


# Test 2
set.seed(501)

data_split1 <- initial_split(Default, prop = 0.70, strata = default)

train_data2 <- training(data_split1)
test_data2  <- testing(data_split1)

glm.fit.train <- glm(default ~ income + balance,
                     data = train_data2,
                     family = binomial)

glm.probs <- predict(glm.fit.train,
                     newdata = test_data2,
                     type = "response")

glm.pred <- rep("No", nrow(test_data2))
glm.pred[glm.probs > 0.5] <- "Yes"

validation.error <- mean(glm.pred != test_data2$default)
validation.error
[1] 0.025
#Test 3
set.seed(4)

data_split3 <- initial_split(Default, prop = 0.60, strata = default)

train_data3 <- training(data_split3)
test_data3  <- testing(data_split3)

glm.fit.train <- glm(default ~ income + balance,
                     data = train_data3,
                     family = binomial)

glm.probs <- predict(glm.fit.train,
                     newdata = test_data3,
                     type = "response")

glm.pred <- rep("No", nrow(test_data3))
glm.pred[glm.probs > 0.5] <- "Yes"

validation.error <- mean(glm.pred != test_data3$default)
validation.error
[1] 0.0255
#Test 4

set.seed(4)

data_split4 <- initial_split(Default, prop = 0.85, strata = default)

train_data4 <- training(data_split4)
test_data4  <- testing(data_split4)

glm.fit.train <- glm(default ~ income + balance,
                     data = train_data4,
                     family = binomial)

glm.probs <- predict(glm.fit.train,
                     newdata = test_data4,
                     type = "response")

glm.pred <- rep("No", nrow(test_data4))
glm.pred[glm.probs > 0.5] <- "Yes"

validation.error <- mean(glm.pred != test_data4$default)
validation.error
[1] 0.026

I decided to change up both the split sizes and the seeds, it seems like first split and training size was the best but all the rest stay around 2.5% so that’s not that bad I would imagine.

Q5d

set.seed(146)

data_split5 <- initial_split(Default, prop = 0.80, strata = default)

train_data5 <- training(data_split5)
test_data5  <- testing(data_split5)

glm.fit.train <- glm(default ~ income + balance + student,
                     data = train_data5,
                     family = binomial)

glm.probs <- predict(glm.fit.train,
                     newdata = test_data5,
                     type = "response")

glm.pred <- rep("No", nrow(test_data5))
glm.pred[glm.probs > 0.5] <- "Yes"

validation.error <- mean(glm.pred != test_data5$default)
validation.error
[1] 0.022

For me it didn’t actually make the model any better, it made it .05% worse which isn’t much but I would test it in each other model to see if it’s consistently that and then I would remove it if so.

Q6a

set.seed(146)

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

summary(glm.fit)

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

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

Q6b

library(boot)

boot.fn <- function(data, index) {
  glm.fit <- glm(default ~ income + balance,
                 data = data,
                 family = binomial,
                 subset = index)
  
  return(coef(glm.fit)[c("income", "balance")])
}

Q6c

boot.results <- boot(Default,
                     boot.fn,
                     R = 1000)

boot.results

ORDINARY NONPARAMETRIC BOOTSTRAP


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


Bootstrap Statistics :
        original       bias     std. error
t1* 2.080898e-05 1.893728e-07 5.043453e-06
t2* 5.647103e-03 1.224738e-05 2.222933e-04

Q6d

The results of the standard error were extremely similar between the glm() function and the bootstrap function. For income, in the glm() function the SE was 4.99e-06 and for bootstrap it was 4.72e-06. As for balance it was 2.24e-04 for glm(), and for bootstrap it was 2.27e-06. So it seems like the SE is consistent with what the bootstrap estimates for the model.

Q9a

data(Boston)
Boston <- na.omit(Boston)

mu_hat <- mean(Boston$medv)
print(mu_hat)
[1] 22.53281

Q9b

se_mu_hat <- sd(Boston$medv) / sqrt(length(Boston$medv))
se_mu_hat
[1] 0.4088611

The standard error for μ̂ is .409. Which means if we decided to repeatedly take samples and calculated the sample mean each time, it would be vary around the value of .409 from the true population mean.

Q9c

set.seed(146)

boot.fn.mean <- function(data, index) {
  return(mean(data$medv[index]))
}

boot.mean <- boot(Boston,
                  boot.fn.mean,
                  R = 1000)

boot.mean

ORDINARY NONPARAMETRIC BOOTSTRAP


Call:
boot(data = Boston, statistic = boot.fn.mean, R = 1000)


Bootstrap Statistics :
    original      bias    std. error
t1* 22.53281 0.009530632   0.4083056

My standard error was .408 so it was only .001 different so extremely similar to the formula based estimate.

Q9d


boot_se_mean <- sd(boot.mean$t)

ci_lower <- mu_hat - 2 * boot_se_mean
ci_upper <- mu_hat + 2 * boot_se_mean

c(ci_lower, ci_upper)
[1] 21.71620 23.34942
t.test(Boston$medv)

    One Sample t-test

data:  Boston$medv
t = 55.111, df = 505, p-value < 2.2e-16
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
 21.72953 23.33608
sample estimates:
mean of x 
 22.53281 

The two versions giving 95% CI for the mean of medv are extremely similar with bootstrap being 21.72, 23.35 and the t.test being 21.73, 23.35 so almost identicial

Q9e

mu_hat_med <- median(Boston$medv)
mu_hat_med
[1] 21.2

Q9f

boot.fn.median <- function(data, index) {
  return(median(data$medv[index]))
}


boot.median <- boot(Boston,
                    boot.fn.median,
                    R = 1000)

boot.median

ORDINARY NONPARAMETRIC BOOTSTRAP


Call:
boot(data = Boston, statistic = boot.fn.median, R = 1000)


Bootstrap Statistics :
    original  bias    std. error
t1*     21.2  0.0052    0.384761

This bootstrap standard error would allow me to know that the sample median of 21.2 would vary by ~.385 across repeated sampling

Q9g

mu_hat_0.1 <- quantile(Boston$medv, 0.1)
mu_hat_0.1
  10% 
12.75 

Q9h

boot.fn.quantile <- function(data, index) {
  return(quantile(data$medv[index], 0.1))
}

boot.quantile <- boot(Boston,
                      boot.fn.quantile,
                      R = 1000)

boot.quantile

ORDINARY NONPARAMETRIC BOOTSTRAP


Call:
boot(data = Boston, statistic = boot.fn.quantile, R = 1000)


Bootstrap Statistics :
    original  bias    std. error
t1*    12.75 0.00405   0.5040537

This shows that the bottom 10% quantile of medv is 12.75 and that the SE is .504 which does mean it has more variablility than the median.

LS0tDQp0aXRsZTogIkV4ZXJjaXNlIDQiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCiAgDQotLS0NCg0KIyMgUTNhDQoqSXQncyBkb25lIGJ5IHNwbGl0dGluZyB0aGUgZGF0YSBpbnRvIGsgaW4gcm91Z2hseSBlcXVhbCBncm91cHMuIFRoZW4gaXQncyB0cmFpbmVkIG9uIGFsbCBidXQgb25lIGdyb3VwIHRoZW4gdGVzdGVkIG9uIHRoZSBncm91cCB0aGF0IHdhcyBsZWZ0IG91dC4gVGhlbiB0aGlzIGlzIHJlcGVhdGVkICJrIiB0aW1lcywgdGhlbiBpdCByb3VuZCB0aGUgdGVzdCBlcnJvcnMgdG8gZ2l2ZSBvbmUgYmlnIG92ZXJhbGwgZXN0aW1hdGUgb2YgdGhlIG1vZGVsJ3MgdGVzdCBlcnJvci4qDQoNCiMjIFEzYihpKQ0KKlRoZSB2YWxpZGF0aW9uIHNldCBhcHByb2FjaCBoYXMgdGhlIGFiaWxpdHkgdG8gdXNlIGRhdGEgbW9yZSBlZmZpY2llbnRseS4gSXQgYWxzbyBzZXBhcmF0ZXMgc29tZSBvZiB0aGUgZGF0YSBhbmQgZG9lc24ndCB1c2UgaXQgdG8gdHJhaW4gdGhlIG1vZGVsIHNvIHRoZSByZXN1bHRzIGRlcGVuZCBhIGxvdCBvZiB0aGUgZGF0YSBpbiB0aGUgdHJhaW5pbmcgc2V0cy4gQ3Jvc3MtdmFsaWRhdGlvbiB3b3VsZG4ndCBoYXZlIHRoaXMgcHJvYmxlbSBiZWNhdXNlIGl0IHVzZXMgYWxsIGRhdGEgaW4gdGhlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIGF0IHNvbWUgcG9pbnQuSG93ZXZlciwgY3Jvc3MtdmFsaWRhdGlvbiB3b3VsZCB0YWtlIGxvbmdlciB0byBydW4gYmVjYXVzZSBpdCBoYXMgdG8gZml0ICJrIiBhbW91bnQgb2YgdGltZXMuKg0KDQojIyBRM2IoaWkpDQoqTE9PQ1YgY29tcGFyZWQgdG8gay1mb2xkIGNyb3NzLXZhbGlkYXRpb24gaXMgdXN1YWxseSBsZXNzIGNvbXB1dGF0aW9uYWxseSBleHBlbnNpdmUuIExPT0NWIGxlYXZlcyBvdXQgb25lIHNpbmdsZSBvYnNlcnZhdGlvbiBhdCBhIHRpbWUsIHNvIGl0IGhhcyB0byBmaXQgbiB0aW1lcywgaW4gd2hpY2ggbiBpcyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucy4gVGhpcyBjYW4gdGFrZSBhIGxvbmcgdGltZSBmb3IgYSBsYXJnZSBkYXRhIHNldCwgd2hpbGUgay1mb2xkIGNyb3NzLXZhbGlkYXRpb24gb25seSBoYXMgdG8gZG8gaXQgayBudW1iZXIgb2YgdGltZXMuIEhvd2V2ZXIgTE9PQ1Ygd291bGQgYmUgbGVzcyBiaWFzIGJlY2F1c2UgZWFjaCBtb2RlbCBpcyB0cmFpbmVkIG9uIGxhcmdlciBwb3J0aW9ucyBvZiB0aGUgZGF0YS4gQWRkaXRpb25hbGx5LCBMT09DViBtYXkgYmUgbW9yZSByZXNwZWN0aXZlIG9mIG91dGxpZXJzIGluIHRoZSBkYXRhIHNldCBtYWtpbmcgbW9yZSB2YXJpYW5jZSB0aGFuIGstZm9sZCBjcm9zcy12YWxpZGF0aW9uIHdvdWxkLioNCg0KDQojIyBRNWENCmBgYHtyfQ0KbGlicmFyeShJU0xSMikNCmRhdGEoRGVmYXVsdCkNCkRlZmF1bHQgPC0gbmEub21pdChEZWZhdWx0KQ0KDQpEZWZhdWx0JGRlZmF1bHQgPC0gYXMuZmFjdG9yKERlZmF1bHQkZGVmYXVsdCkNCkRlZmF1bHQkc3R1ZGVudCA8LSBhcy5mYWN0b3IoRGVmYXVsdCRzdHVkZW50KQ0KDQpzZXQuc2VlZCgxNDYpDQoNCmdsbS5maXQgPC0gZ2xtKGRlZmF1bHQgfiBpbmNvbWUgKyBiYWxhbmNlLA0KICAgICAgICAgICAgICAgZGF0YSA9IERlZmF1bHQsDQogICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCkNCg0Kc3VtbWFyeShnbG0uZml0KQ0KDQoNCmBgYA0KDQojIyBRNWINCmBgYHtyfQ0KbGlicmFyeShyc2FtcGxlKQ0Kc2V0LnNlZWQoMTQ2KQ0KDQpkYXRhX3NwbGl0IDwtIGluaXRpYWxfc3BsaXQoRGVmYXVsdCwgcHJvcCA9IDAuODAsIHN0cmF0YSA9IGRlZmF1bHQpDQoNCnRyYWluX2RhdGExIDwtIHRyYWluaW5nKGRhdGFfc3BsaXQpDQp0ZXN0X2RhdGExICA8LSB0ZXN0aW5nKGRhdGFfc3BsaXQpDQoNCmdsbS5maXQudHJhaW4gPC0gZ2xtKGRlZmF1bHQgfiBpbmNvbWUgKyBiYWxhbmNlLA0KICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGExLA0KICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwpDQoNCmdsbS5wcm9icyA8LSBwcmVkaWN0KGdsbS5maXQudHJhaW4sDQogICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9kYXRhMSwNCiAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQpnbG0ucHJlZCA8LSByZXAoIk5vIiwgbnJvdyh0ZXN0X2RhdGExKSkNCmdsbS5wcmVkW2dsbS5wcm9icyA+IDAuNV0gPC0gIlllcyINCg0KdmFsaWRhdGlvbi5lcnJvciA8LSBtZWFuKGdsbS5wcmVkICE9IHRlc3RfZGF0YTEkZGVmYXVsdCkNCnZhbGlkYXRpb24uZXJyb3INCg0KYGBgDQoNCiMjIFE1Yw0KYGBge3J9DQoNCiMgVGVzdCAyDQpzZXQuc2VlZCg1MDEpDQoNCmRhdGFfc3BsaXQxIDwtIGluaXRpYWxfc3BsaXQoRGVmYXVsdCwgcHJvcCA9IDAuNzAsIHN0cmF0YSA9IGRlZmF1bHQpDQoNCnRyYWluX2RhdGEyIDwtIHRyYWluaW5nKGRhdGFfc3BsaXQxKQ0KdGVzdF9kYXRhMiAgPC0gdGVzdGluZyhkYXRhX3NwbGl0MSkNCg0KZ2xtLmZpdC50cmFpbiA8LSBnbG0oZGVmYXVsdCB+IGluY29tZSArIGJhbGFuY2UsDQogICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fZGF0YTIsDQogICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCkNCg0KZ2xtLnByb2JzIDwtIHByZWRpY3QoZ2xtLmZpdC50cmFpbiwNCiAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSB0ZXN0X2RhdGEyLA0KICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpDQoNCmdsbS5wcmVkIDwtIHJlcCgiTm8iLCBucm93KHRlc3RfZGF0YTIpKQ0KZ2xtLnByZWRbZ2xtLnByb2JzID4gMC41XSA8LSAiWWVzIg0KDQp2YWxpZGF0aW9uLmVycm9yIDwtIG1lYW4oZ2xtLnByZWQgIT0gdGVzdF9kYXRhMiRkZWZhdWx0KQ0KdmFsaWRhdGlvbi5lcnJvcg0KDQojVGVzdCAzDQpzZXQuc2VlZCg0KQ0KDQpkYXRhX3NwbGl0MyA8LSBpbml0aWFsX3NwbGl0KERlZmF1bHQsIHByb3AgPSAwLjYwLCBzdHJhdGEgPSBkZWZhdWx0KQ0KDQp0cmFpbl9kYXRhMyA8LSB0cmFpbmluZyhkYXRhX3NwbGl0MykNCnRlc3RfZGF0YTMgIDwtIHRlc3RpbmcoZGF0YV9zcGxpdDMpDQoNCmdsbS5maXQudHJhaW4gPC0gZ2xtKGRlZmF1bHQgfiBpbmNvbWUgKyBiYWxhbmNlLA0KICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGEzLA0KICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwpDQoNCmdsbS5wcm9icyA8LSBwcmVkaWN0KGdsbS5maXQudHJhaW4sDQogICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9kYXRhMywNCiAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQpnbG0ucHJlZCA8LSByZXAoIk5vIiwgbnJvdyh0ZXN0X2RhdGEzKSkNCmdsbS5wcmVkW2dsbS5wcm9icyA+IDAuNV0gPC0gIlllcyINCg0KdmFsaWRhdGlvbi5lcnJvciA8LSBtZWFuKGdsbS5wcmVkICE9IHRlc3RfZGF0YTMkZGVmYXVsdCkNCnZhbGlkYXRpb24uZXJyb3INCg0KI1Rlc3QgNA0KDQpzZXQuc2VlZCg0KQ0KDQpkYXRhX3NwbGl0NCA8LSBpbml0aWFsX3NwbGl0KERlZmF1bHQsIHByb3AgPSAwLjg1LCBzdHJhdGEgPSBkZWZhdWx0KQ0KDQp0cmFpbl9kYXRhNCA8LSB0cmFpbmluZyhkYXRhX3NwbGl0NCkNCnRlc3RfZGF0YTQgIDwtIHRlc3RpbmcoZGF0YV9zcGxpdDQpDQoNCmdsbS5maXQudHJhaW4gPC0gZ2xtKGRlZmF1bHQgfiBpbmNvbWUgKyBiYWxhbmNlLA0KICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGE0LA0KICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwpDQoNCmdsbS5wcm9icyA8LSBwcmVkaWN0KGdsbS5maXQudHJhaW4sDQogICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9kYXRhNCwNCiAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQpnbG0ucHJlZCA8LSByZXAoIk5vIiwgbnJvdyh0ZXN0X2RhdGE0KSkNCmdsbS5wcmVkW2dsbS5wcm9icyA+IDAuNV0gPC0gIlllcyINCg0KdmFsaWRhdGlvbi5lcnJvciA8LSBtZWFuKGdsbS5wcmVkICE9IHRlc3RfZGF0YTQkZGVmYXVsdCkNCnZhbGlkYXRpb24uZXJyb3INCmBgYA0KDQoqSSBkZWNpZGVkIHRvIGNoYW5nZSB1cCBib3RoIHRoZSBzcGxpdCBzaXplcyBhbmQgdGhlIHNlZWRzLCBpdCBzZWVtcyBsaWtlIGZpcnN0IHNwbGl0IGFuZCB0cmFpbmluZyBzaXplIHdhcyB0aGUgYmVzdCBidXQgYWxsIHRoZSByZXN0IHN0YXkgYXJvdW5kIDIuNSUgc28gdGhhdCdzIG5vdCB0aGF0IGJhZCBJIHdvdWxkIGltYWdpbmUuKg0KDQojIyBRNWQNCg0KYGBge3J9DQpzZXQuc2VlZCgxNDYpDQoNCmRhdGFfc3BsaXQ1IDwtIGluaXRpYWxfc3BsaXQoRGVmYXVsdCwgcHJvcCA9IDAuODAsIHN0cmF0YSA9IGRlZmF1bHQpDQoNCnRyYWluX2RhdGE1IDwtIHRyYWluaW5nKGRhdGFfc3BsaXQ1KQ0KdGVzdF9kYXRhNSAgPC0gdGVzdGluZyhkYXRhX3NwbGl0NSkNCg0KZ2xtLmZpdC50cmFpbiA8LSBnbG0oZGVmYXVsdCB+IGluY29tZSArIGJhbGFuY2UgKyBzdHVkZW50LA0KICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RhdGE1LA0KICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwpDQoNCmdsbS5wcm9icyA8LSBwcmVkaWN0KGdsbS5maXQudHJhaW4sDQogICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gdGVzdF9kYXRhNSwNCiAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQpnbG0ucHJlZCA8LSByZXAoIk5vIiwgbnJvdyh0ZXN0X2RhdGE1KSkNCmdsbS5wcmVkW2dsbS5wcm9icyA+IDAuNV0gPC0gIlllcyINCg0KdmFsaWRhdGlvbi5lcnJvciA8LSBtZWFuKGdsbS5wcmVkICE9IHRlc3RfZGF0YTUkZGVmYXVsdCkNCnZhbGlkYXRpb24uZXJyb3INCg0KYGBgDQoNCipGb3IgbWUgaXQgZGlkbid0IGFjdHVhbGx5IG1ha2UgdGhlIG1vZGVsIGFueSBiZXR0ZXIsIGl0IG1hZGUgaXQgLjA1JSB3b3JzZSB3aGljaCBpc24ndCBtdWNoIGJ1dCBJIHdvdWxkIHRlc3QgaXQgaW4gZWFjaCBvdGhlciBtb2RlbCB0byBzZWUgaWYgaXQncyBjb25zaXN0ZW50bHkgdGhhdCBhbmQgdGhlbiBJIHdvdWxkIHJlbW92ZSBpdCBpZiBzby4qDQoNCiMjIFE2YQ0KYGBge3J9DQpzZXQuc2VlZCgxNDYpDQoNCmdsbS5maXQgPC0gZ2xtKGRlZmF1bHQgfiBpbmNvbWUgKyBiYWxhbmNlLA0KICAgICAgICAgICAgICAgZGF0YSA9IERlZmF1bHQsDQogICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCkNCg0Kc3VtbWFyeShnbG0uZml0KQ0KDQoNCmBgYA0KDQojIyBRNmIgDQpgYGB7cn0NCmxpYnJhcnkoYm9vdCkNCg0KYm9vdC5mbiA8LSBmdW5jdGlvbihkYXRhLCBpbmRleCkgew0KICBnbG0uZml0IDwtIGdsbShkZWZhdWx0IH4gaW5jb21lICsgYmFsYW5jZSwNCiAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGEsDQogICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsLA0KICAgICAgICAgICAgICAgICBzdWJzZXQgPSBpbmRleCkNCiAgDQogIHJldHVybihjb2VmKGdsbS5maXQpW2MoImluY29tZSIsICJiYWxhbmNlIildKQ0KfQ0KDQpgYGANCg0KIyMgUTZjDQpgYGB7cn0NCmJvb3QucmVzdWx0cyA8LSBib290KERlZmF1bHQsDQogICAgICAgICAgICAgICAgICAgICBib290LmZuLA0KICAgICAgICAgICAgICAgICAgICAgUiA9IDEwMDApDQoNCmJvb3QucmVzdWx0cw0KDQpgYGANCg0KIyMgUTZkDQoNCipUaGUgcmVzdWx0cyBvZiB0aGUgc3RhbmRhcmQgZXJyb3Igd2VyZSBleHRyZW1lbHkgc2ltaWxhciBiZXR3ZWVuIHRoZSBnbG0oKSBmdW5jdGlvbiBhbmQgdGhlIGJvb3RzdHJhcCBmdW5jdGlvbi4gRm9yIGluY29tZSwgaW4gdGhlIGdsbSgpIGZ1bmN0aW9uIHRoZSBTRSB3YXMgNC45OWUtMDYgYW5kIGZvciBib290c3RyYXAgaXQgd2FzIDQuNzJlLTA2LiBBcyBmb3IgYmFsYW5jZSBpdCB3YXMgMi4yNGUtMDQgZm9yIGdsbSgpLCBhbmQgZm9yIGJvb3RzdHJhcCBpdCB3YXMgMi4yN2UtMDYuIFNvIGl0IHNlZW1zIGxpa2UgdGhlIFNFIGlzIGNvbnNpc3RlbnQgd2l0aCB3aGF0IHRoZSBib290c3RyYXAgZXN0aW1hdGVzIGZvciB0aGUgbW9kZWwuKg0KDQojIyBROWENCmBgYHtyfQ0KZGF0YShCb3N0b24pDQpCb3N0b24gPC0gbmEub21pdChCb3N0b24pDQoNCm11X2hhdCA8LSBtZWFuKEJvc3RvbiRtZWR2KQ0KcHJpbnQobXVfaGF0KQ0KDQpgYGANCg0KIyMgUTliDQpgYGB7cn0NCnNlX211X2hhdCA8LSBzZChCb3N0b24kbWVkdikgLyBzcXJ0KGxlbmd0aChCb3N0b24kbWVkdikpDQpzZV9tdV9oYXQNCg0KYGBgDQoNCipUaGUgc3RhbmRhcmQgZXJyb3IgZm9yIM68zIIgaXMgLjQwOS4gV2hpY2ggbWVhbnMgaWYgd2UgZGVjaWRlZCB0byByZXBlYXRlZGx5IHRha2Ugc2FtcGxlcyBhbmQgY2FsY3VsYXRlZCB0aGUgc2FtcGxlIG1lYW4gZWFjaCB0aW1lLCBpdCB3b3VsZCBiZSB2YXJ5IGFyb3VuZCB0aGUgdmFsdWUgb2YgLjQwOSBmcm9tIHRoZSB0cnVlIHBvcHVsYXRpb24gbWVhbi4qDQoNCiMjIFE5Yw0KYGBge3J9DQpzZXQuc2VlZCgxNDYpDQoNCmJvb3QuZm4ubWVhbiA8LSBmdW5jdGlvbihkYXRhLCBpbmRleCkgew0KICByZXR1cm4obWVhbihkYXRhJG1lZHZbaW5kZXhdKSkNCn0NCg0KYm9vdC5tZWFuIDwtIGJvb3QoQm9zdG9uLA0KICAgICAgICAgICAgICAgICAgYm9vdC5mbi5tZWFuLA0KICAgICAgICAgICAgICAgICAgUiA9IDEwMDApDQoNCmJvb3QubWVhbg0KDQpgYGANCg0KKk15IHN0YW5kYXJkIGVycm9yIHdhcyAuNDA4IHNvIGl0IHdhcyBvbmx5IC4wMDEgZGlmZmVyZW50IHNvIGV4dHJlbWVseSBzaW1pbGFyIHRvIHRoZSBmb3JtdWxhIGJhc2VkIGVzdGltYXRlLioNCg0KIyMgUTlkDQpgYGB7cn0NCg0KYm9vdF9zZV9tZWFuIDwtIHNkKGJvb3QubWVhbiR0KQ0KDQpjaV9sb3dlciA8LSBtdV9oYXQgLSAyICogYm9vdF9zZV9tZWFuDQpjaV91cHBlciA8LSBtdV9oYXQgKyAyICogYm9vdF9zZV9tZWFuDQoNCmMoY2lfbG93ZXIsIGNpX3VwcGVyKQ0KDQp0LnRlc3QoQm9zdG9uJG1lZHYpDQoNCmBgYA0KDQoqVGhlIHR3byB2ZXJzaW9ucyBnaXZpbmcgOTUlIENJIGZvciB0aGUgbWVhbiBvZiBtZWR2IGFyZSBleHRyZW1lbHkgc2ltaWxhciB3aXRoIGJvb3RzdHJhcCBiZWluZyAyMS43MiwgMjMuMzUgYW5kIHRoZSB0LnRlc3QgYmVpbmcgMjEuNzMsIDIzLjM1IHNvIGFsbW9zdCBpZGVudGljaWFsKg0KDQojIyBROWUNCmBgYHtyfQ0KbXVfaGF0X21lZCA8LSBtZWRpYW4oQm9zdG9uJG1lZHYpDQptdV9oYXRfbWVkDQoNCmBgYA0KDQojIyBROWYNCmBgYHtyfQ0KYm9vdC5mbi5tZWRpYW4gPC0gZnVuY3Rpb24oZGF0YSwgaW5kZXgpIHsNCiAgcmV0dXJuKG1lZGlhbihkYXRhJG1lZHZbaW5kZXhdKSkNCn0NCg0KDQpib290Lm1lZGlhbiA8LSBib290KEJvc3RvbiwNCiAgICAgICAgICAgICAgICAgICAgYm9vdC5mbi5tZWRpYW4sDQogICAgICAgICAgICAgICAgICAgIFIgPSAxMDAwKQ0KDQpib290Lm1lZGlhbg0KDQpgYGANCg0KKlRoaXMgYm9vdHN0cmFwIHN0YW5kYXJkIGVycm9yIHdvdWxkIGFsbG93IG1lIHRvIGtub3cgdGhhdCB0aGUgc2FtcGxlIG1lZGlhbiBvZiAyMS4yIHdvdWxkIHZhcnkgYnkgfi4zODUgYWNyb3NzIHJlcGVhdGVkIHNhbXBsaW5nKg0KDQojIyBROWcNCmBgYHtyfQ0KbXVfaGF0XzAuMSA8LSBxdWFudGlsZShCb3N0b24kbWVkdiwgMC4xKQ0KbXVfaGF0XzAuMQ0KDQpgYGANCg0KIyMgUTloDQpgYGB7cn0NCmJvb3QuZm4ucXVhbnRpbGUgPC0gZnVuY3Rpb24oZGF0YSwgaW5kZXgpIHsNCiAgcmV0dXJuKHF1YW50aWxlKGRhdGEkbWVkdltpbmRleF0sIDAuMSkpDQp9DQoNCmJvb3QucXVhbnRpbGUgPC0gYm9vdChCb3N0b24sDQogICAgICAgICAgICAgICAgICAgICAgYm9vdC5mbi5xdWFudGlsZSwNCiAgICAgICAgICAgICAgICAgICAgICBSID0gMTAwMCkNCg0KYm9vdC5xdWFudGlsZQ0KYGBgDQoNCipUaGlzIHNob3dzIHRoYXQgdGhlIGJvdHRvbSAxMCUgcXVhbnRpbGUgb2YgbWVkdiBpcyAxMi43NSBhbmQgdGhhdCB0aGUgU0UgaXMgLjUwNCB3aGljaCBkb2VzIG1lYW4gaXQgaGFzIG1vcmUgdmFyaWFibGlsaXR5IHRoYW4gdGhlIG1lZGlhbi4qDQoNCg==