Question 6

a

library(ISLR2)
library(boot)

set.seed(1)
cv.errors <- numeric(10)

for (i in 1:10) {
  glm.fit <- glm(wage ~ poly(age, i), data = Wage)
  cv.errors[i] <- cv.glm(Wage, glm.fit, K = 10)$delta[1]
}

best_d <- which.min(cv.errors)
cat("Optimal Polynomial Degree selected by CV:", best_d, "\n")
Optimal Polynomial Degree selected by CV: 9 
plot(1:10, cv.errors, type = "b", xlab = "Polynomial Degree", ylab = "10-Fold CV MSE",
     main = "10-Fold CV Error by Polynomial Degree", col = "royalblue", pch = 19, lwd = 2)
points(best_d, cv.errors[best_d], col = "red", cex = 2, pch = 20)

fit1 <- lm(wage ~ age, data = Wage)
fit2 <- lm(wage ~ poly(age, 2), data = Wage)
fit3 <- lm(wage ~ poly(age, 3), data = Wage)
fit4 <- lm(wage ~ poly(age, 4), data = Wage)
fit5 <- lm(wage ~ poly(age, 5), data = Wage)

anova(fit1, fit2, fit3, fit4, fit5)
Analysis of Variance Table

Model 1: wage ~ age
Model 2: wage ~ poly(age, 2)
Model 3: wage ~ poly(age, 3)
Model 4: wage ~ poly(age, 4)
Model 5: wage ~ poly(age, 5)
  Res.Df     RSS Df Sum of Sq        F    Pr(>F)    
1   2998 5022216                                    
2   2997 4793430  1    228786 143.5931 < 2.2e-16 ***
3   2996 4777674  1     15756   9.8888  0.001679 ** 
4   2995 4771604  1      6070   3.8098  0.051046 .  
5   2994 4770322  1      1283   0.8050  0.369682    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Cross validation selects degree 9. However, the ANOVA test shows that a degree 3 or degree 4 polynomial is good, as there is no statistically significant improvement for degree 5.

fit.poly <- lm(wage ~ poly(age, 4), data = Wage)
age.range <- range(Wage$age)
age.grid <- seq(from = age.range[1], to = age.range[2])
preds <- predict(fit.poly, newdata = list(age = age.grid), se = TRUE)
se.bands <- cbind(preds$fit + 2 * preds$se.fit, preds$fit - 2 * preds$se.fit)

plot(Wage$age, Wage$wage, xlim = age.range, cex = 0.5, col = "darkgrey",
     main = "Degree-4 Polynomial Fit of Wage vs. Age", xlab = "Age", ylab = "Wage")
lines(age.grid, preds$fit, lwd = 3, col = "royalblue")
matlines(age.grid, se.bands, lwd = 1.5, col = "royalblue", lty = 3)

b

set.seed(1)
cv.errors.step <- numeric(10)

for (i in 2:10) {
  Wage$age.cut <- cut(Wage$age, i)
  glm.fit <- glm(wage ~ age.cut, data = Wage)
  cv.errors.step[i] <- cv.glm(Wage, glm.fit, K = 10)$delta[1]
}

best_cuts <- which.min(cv.errors.step[-1]) + 1
cat("Optimal Number of Cuts selected by CV:", best_cuts, "\n")
Optimal Number of Cuts selected by CV: 8 
plot(2:10, cv.errors.step[-1], type = "b", xlab = "Number of Cuts", ylab = "10-Fold CV MSE",
     main = "10-Fold CV Error by Step Function Cuts", col = "forestgreen", pch = 19, lwd = 2)
points(best_cuts, cv.errors.step[best_cuts], col = "red", cex = 2, pch = 20)

Cross-validation selects 8 cuts.

fit.step <- lm(wage ~ cut(age, best_cuts), data = Wage)
preds.step <- predict(fit.step, newdata = list(age = age.grid), se = TRUE)
se.bands.step <- cbind(preds.step$fit + 2 * preds.step$se.fit, preds.step$fit - 2 * preds.step$se.fit)

plot(Wage$age, Wage$wage, xlim = age.range, cex = 0.5, col = "darkgrey",
     main = paste(best_cuts, "-Cut Step Function Fit of Wage vs. Age"), xlab = "Age", ylab = "Wage")
lines(age.grid, preds.step$fit, lwd = 3, col = "forestgreen")
matlines(age.grid, se.bands.step, lwd = 1.5, col = "forestgreen", lty = 3)


Question 10

a

library(leaps)
library(gam)

set.seed(1)
train_idx <- sample(nrow(College), nrow(College) / 2)
train_set <- College[train_idx, ]
test_set <- College[-train_idx, ]

fit.fwd <- regsubsets(Outstate ~ ., data = train_set, method = "forward", nvmax = 17)
fwd.summary <- summary(fit.fwd)

par(mfrow = c(1, 3))

# Cp
plot(fwd.summary$cp, xlab = "Number of variables", ylab = "Cp", type = "l")
min.cp <- min(fwd.summary$cp)
std.cp <- sd(fwd.summary$cp)
abline(h = min.cp + 0.2 * std.cp, col = "red", lty = 2)
abline(h = min.cp - 0.2 * std.cp, col = "red", lty = 2)

# BIC
plot(fwd.summary$bic, xlab = "Number of variables", ylab = "BIC", type = "l")
min.bic <- min(fwd.summary$bic)
std.bic <- sd(fwd.summary$bic)
abline(h = min.bic + 0.2 * std.bic, col = "red", lty = 2)
abline(h = min.bic - 0.2 * std.bic, col = "red", lty = 2)

# Adjusted R2
plot(fwd.summary$adjr2, xlab = "Number of variables", ylab = "Adjusted R2", type = "l", ylim = c(0.4, 0.84))
max.adjr2 <- max(fwd.summary$adjr2)
std.adjr2 <- sd(fwd.summary$adjr2)
abline(h = max.adjr2 + 0.2 * std.adjr2, col = "red", lty = 2)
abline(h = max.adjr2 - 0.2 * std.adjr2, col = "red", lty = 2)

Plots show that size 6 is the minimum size for which the scores are within 0.2 standard deviations of the optimum.

coeffs <- coef(fit.fwd, id = 6)
print(coeffs)
  (Intercept)    PrivateYes    Room.Board      Terminal   perc.alumni        Expend 
-4726.8810613  2717.7019276     1.1032433    36.9990286    59.0863753     0.1930814 
    Grad.Rate 
   33.8303314 

The selected features are: PrivateYes, Room.Board, PhD, perc.alumni, Expend, and Grad.Rate.

b

gam.fit <- gam(Outstate ~ Private + 
                 s(Room.Board, df = 2) +
                 s(PhD, df = 2) +
                 s(perc.alumni, df = 2) +
                 s(Expend, df = 5) +
                 s(Grad.Rate, df = 2),
               data = train_set)

par(mfrow = c(2, 3))
plot(gam.fit, se = TRUE, col = "blue")

c

gam.pred <- predict(gam.fit, newdata = test_set)
err <- mean((test_set$Outstate - gam.pred)^2)
cat("Test MSE:", err, "\n")
Test MSE: 3349290 
tss <- mean((test_set$Outstate - mean(test_set$Outstate))^2)
r2 <- 1 - err / tss
cat("Test R2:", r2, "\n")
Test R2: 0.7660016 

We obtain a test R2 of 0.77 using a GAM with 6 predictors.

d

summary(gam.fit)

Call: gam(formula = Outstate ~ Private + s(Room.Board, df = 2) + s(PhD, 
    df = 2) + s(perc.alumni, df = 2) + s(Expend, df = 5) + s(Grad.Rate, 
    df = 2), data = train_set)
Deviance Residuals:
     Min       1Q   Median       3Q      Max 
-7402.89 -1114.45   -12.67  1282.69  7470.60 

(Dispersion Parameter for gaussian family taken to be 3711182)

    Null Deviance: 6989966760 on 387 degrees of freedom
Residual Deviance: 1384271126 on 373 degrees of freedom
AIC: 6987.021 

Number of Local Scoring Iterations: NA 

Anova for Parametric Effects
                        Df     Sum Sq    Mean Sq F value    Pr(>F)    
Private                  1 1778718277 1778718277 479.286 < 2.2e-16 ***
s(Room.Board, df = 2)    1 1577115244 1577115244 424.963 < 2.2e-16 ***
s(PhD, df = 2)           1  322431195  322431195  86.881 < 2.2e-16 ***
s(perc.alumni, df = 2)   1  336869281  336869281  90.771 < 2.2e-16 ***
s(Expend, df = 5)        1  530538753  530538753 142.957 < 2.2e-16 ***
s(Grad.Rate, df = 2)     1   86504998   86504998  23.309 2.016e-06 ***
Residuals              373 1384271126    3711182                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Anova for Nonparametric Effects
                       Npar Df  Npar F     Pr(F)    
(Intercept)                                         
Private                                             
s(Room.Board, df = 2)        1  1.9157    0.1672    
s(PhD, df = 2)               1  0.9699    0.3253    
s(perc.alumni, df = 2)       1  0.1859    0.6666    
s(Expend, df = 5)            4 20.5075 2.665e-15 ***
s(Grad.Rate, df = 2)         1  0.5702    0.4506    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

ANOVA shows evidence of a non linear relationship between Outstate and Expend and moderately strong evidence of a non linear relationship between Outstate and Grad.Rate.

LS0tCnRpdGxlOiAiQ2hyaXMgU2VycmFubyAtIEFzc2lnbm1lbnQgNiIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgZWNobzogdHJ1ZQotLS0KCiMjIFF1ZXN0aW9uIDYKCiMjIyBhCgpgYGB7cn0KbGlicmFyeShJU0xSMikKbGlicmFyeShib290KQoKc2V0LnNlZWQoMSkKY3YuZXJyb3JzIDwtIG51bWVyaWMoMTApCgpmb3IgKGkgaW4gMToxMCkgewogIGdsbS5maXQgPC0gZ2xtKHdhZ2UgfiBwb2x5KGFnZSwgaSksIGRhdGEgPSBXYWdlKQogIGN2LmVycm9yc1tpXSA8LSBjdi5nbG0oV2FnZSwgZ2xtLmZpdCwgSyA9IDEwKSRkZWx0YVsxXQp9CgpiZXN0X2QgPC0gd2hpY2gubWluKGN2LmVycm9ycykKY2F0KCJPcHRpbWFsIFBvbHlub21pYWwgRGVncmVlIHNlbGVjdGVkIGJ5IENWOiIsIGJlc3RfZCwgIlxuIikKCnBsb3QoMToxMCwgY3YuZXJyb3JzLCB0eXBlID0gImIiLCB4bGFiID0gIlBvbHlub21pYWwgRGVncmVlIiwgeWxhYiA9ICIxMC1Gb2xkIENWIE1TRSIsCiAgICAgbWFpbiA9ICIxMC1Gb2xkIENWIEVycm9yIGJ5IFBvbHlub21pYWwgRGVncmVlIiwgY29sID0gInJveWFsYmx1ZSIsIHBjaCA9IDE5LCBsd2QgPSAyKQpwb2ludHMoYmVzdF9kLCBjdi5lcnJvcnNbYmVzdF9kXSwgY29sID0gInJlZCIsIGNleCA9IDIsIHBjaCA9IDIwKQpgYGAKCmBgYHtyfQpmaXQxIDwtIGxtKHdhZ2UgfiBhZ2UsIGRhdGEgPSBXYWdlKQpmaXQyIDwtIGxtKHdhZ2UgfiBwb2x5KGFnZSwgMiksIGRhdGEgPSBXYWdlKQpmaXQzIDwtIGxtKHdhZ2UgfiBwb2x5KGFnZSwgMyksIGRhdGEgPSBXYWdlKQpmaXQ0IDwtIGxtKHdhZ2UgfiBwb2x5KGFnZSwgNCksIGRhdGEgPSBXYWdlKQpmaXQ1IDwtIGxtKHdhZ2UgfiBwb2x5KGFnZSwgNSksIGRhdGEgPSBXYWdlKQoKYW5vdmEoZml0MSwgZml0MiwgZml0MywgZml0NCwgZml0NSkKYGBgCgpDcm9zcyB2YWxpZGF0aW9uIHNlbGVjdHMgZGVncmVlIDkuIEhvd2V2ZXIsIHRoZSBBTk9WQSB0ZXN0IHNob3dzIHRoYXQgYSBkZWdyZWUgMyBvciBkZWdyZWUgNCBwb2x5bm9taWFsIGlzIGdvb2QsIGFzIHRoZXJlIGlzIG5vIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgaW1wcm92ZW1lbnQgZm9yIGRlZ3JlZSA1LgoKYGBge3J9CmZpdC5wb2x5IDwtIGxtKHdhZ2UgfiBwb2x5KGFnZSwgNCksIGRhdGEgPSBXYWdlKQphZ2UucmFuZ2UgPC0gcmFuZ2UoV2FnZSRhZ2UpCmFnZS5ncmlkIDwtIHNlcShmcm9tID0gYWdlLnJhbmdlWzFdLCB0byA9IGFnZS5yYW5nZVsyXSkKcHJlZHMgPC0gcHJlZGljdChmaXQucG9seSwgbmV3ZGF0YSA9IGxpc3QoYWdlID0gYWdlLmdyaWQpLCBzZSA9IFRSVUUpCnNlLmJhbmRzIDwtIGNiaW5kKHByZWRzJGZpdCArIDIgKiBwcmVkcyRzZS5maXQsIHByZWRzJGZpdCAtIDIgKiBwcmVkcyRzZS5maXQpCgpwbG90KFdhZ2UkYWdlLCBXYWdlJHdhZ2UsIHhsaW0gPSBhZ2UucmFuZ2UsIGNleCA9IDAuNSwgY29sID0gImRhcmtncmV5IiwKICAgICBtYWluID0gIkRlZ3JlZSA0IFBvbHlub21pYWwgRml0IG9mIFdhZ2UgdiBBZ2UiLCB4bGFiID0gIkFnZSIsIHlsYWIgPSAiV2FnZSIpCmxpbmVzKGFnZS5ncmlkLCBwcmVkcyRmaXQsIGx3ZCA9IDMsIGNvbCA9ICJyb3lhbGJsdWUiKQptYXRsaW5lcyhhZ2UuZ3JpZCwgc2UuYmFuZHMsIGx3ZCA9IDEuNSwgY29sID0gInJveWFsYmx1ZSIsIGx0eSA9IDMpCmBgYAoKIyMjIGIKCmBgYHtyfQpzZXQuc2VlZCgxKQpjdi5lcnJvcnMuc3RlcCA8LSBudW1lcmljKDEwKQoKZm9yIChpIGluIDI6MTApIHsKICBXYWdlJGFnZS5jdXQgPC0gY3V0KFdhZ2UkYWdlLCBpKQogIGdsbS5maXQgPC0gZ2xtKHdhZ2UgfiBhZ2UuY3V0LCBkYXRhID0gV2FnZSkKICBjdi5lcnJvcnMuc3RlcFtpXSA8LSBjdi5nbG0oV2FnZSwgZ2xtLmZpdCwgSyA9IDEwKSRkZWx0YVsxXQp9CgpiZXN0X2N1dHMgPC0gd2hpY2gubWluKGN2LmVycm9ycy5zdGVwWy0xXSkgKyAxCmNhdCgiT3B0aW1hbCBOdW1iZXIgb2YgQ3V0cyBzZWxlY3RlZCBieSBDVjoiLCBiZXN0X2N1dHMsICJcbiIpCgpwbG90KDI6MTAsIGN2LmVycm9ycy5zdGVwWy0xXSwgdHlwZSA9ICJiIiwgeGxhYiA9ICJOdW1iZXIgb2YgQ3V0cyIsIHlsYWIgPSAiMTAtRm9sZCBDViBNU0UiLAogICAgIG1haW4gPSAiMTAgRm9sZCBDViBFcnJvciBieSBTdGVwIEZ1bmN0aW9uIEN1dHMiLCBjb2wgPSAiZm9yZXN0Z3JlZW4iLCBwY2ggPSAxOSwgbHdkID0gMikKcG9pbnRzKGJlc3RfY3V0cywgY3YuZXJyb3JzLnN0ZXBbYmVzdF9jdXRzXSwgY29sID0gInJlZCIsIGNleCA9IDIsIHBjaCA9IDIwKQpgYGAKCkNyb3NzLXZhbGlkYXRpb24gc2VsZWN0cyA4IGN1dHMuCgpgYGB7cn0KZml0LnN0ZXAgPC0gbG0od2FnZSB+IGN1dChhZ2UsIGJlc3RfY3V0cyksIGRhdGEgPSBXYWdlKQpwcmVkcy5zdGVwIDwtIHByZWRpY3QoZml0LnN0ZXAsIG5ld2RhdGEgPSBsaXN0KGFnZSA9IGFnZS5ncmlkKSwgc2UgPSBUUlVFKQpzZS5iYW5kcy5zdGVwIDwtIGNiaW5kKHByZWRzLnN0ZXAkZml0ICsgMiAqIHByZWRzLnN0ZXAkc2UuZml0LCBwcmVkcy5zdGVwJGZpdCAtIDIgKiBwcmVkcy5zdGVwJHNlLmZpdCkKCnBsb3QoV2FnZSRhZ2UsIFdhZ2Ukd2FnZSwgeGxpbSA9IGFnZS5yYW5nZSwgY2V4ID0gMC41LCBjb2wgPSAiZGFya2dyZXkiLAogICAgIG1haW4gPSBwYXN0ZShiZXN0X2N1dHMsICItQ3V0IFN0ZXAgRnVuY3Rpb24gRml0IG9mIFdhZ2UgdnMuIEFnZSIpLCB4bGFiID0gIkFnZSIsIHlsYWIgPSAiV2FnZSIpCmxpbmVzKGFnZS5ncmlkLCBwcmVkcy5zdGVwJGZpdCwgbHdkID0gMywgY29sID0gImZvcmVzdGdyZWVuIikKbWF0bGluZXMoYWdlLmdyaWQsIHNlLmJhbmRzLnN0ZXAsIGx3ZCA9IDEuNSwgY29sID0gImZvcmVzdGdyZWVuIiwgbHR5ID0gMykKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFF1ZXN0aW9uIDEwCgojIyMgYQoKYGBge3J9CmxpYnJhcnkobGVhcHMpCmxpYnJhcnkoZ2FtKQoKc2V0LnNlZWQoMSkKdHJhaW5faWR4IDwtIHNhbXBsZShucm93KENvbGxlZ2UpLCBucm93KENvbGxlZ2UpIC8gMikKdHJhaW5fc2V0IDwtIENvbGxlZ2VbdHJhaW5faWR4LCBdCnRlc3Rfc2V0IDwtIENvbGxlZ2VbLXRyYWluX2lkeCwgXQoKZml0LmZ3ZCA8LSByZWdzdWJzZXRzKE91dHN0YXRlIH4gLiwgZGF0YSA9IHRyYWluX3NldCwgbWV0aG9kID0gImZvcndhcmQiLCBudm1heCA9IDE3KQpmd2Quc3VtbWFyeSA8LSBzdW1tYXJ5KGZpdC5md2QpCgpwYXIobWZyb3cgPSBjKDEsIDMpKQoKIyBDcApwbG90KGZ3ZC5zdW1tYXJ5JGNwLCB4bGFiID0gIk51bWJlciBvZiB2YXJpYWJsZXMiLCB5bGFiID0gIkNwIiwgdHlwZSA9ICJsIikKbWluLmNwIDwtIG1pbihmd2Quc3VtbWFyeSRjcCkKc3RkLmNwIDwtIHNkKGZ3ZC5zdW1tYXJ5JGNwKQphYmxpbmUoaCA9IG1pbi5jcCArIDAuMiAqIHN0ZC5jcCwgY29sID0gInJlZCIsIGx0eSA9IDIpCmFibGluZShoID0gbWluLmNwIC0gMC4yICogc3RkLmNwLCBjb2wgPSAicmVkIiwgbHR5ID0gMikKCiMgQklDCnBsb3QoZndkLnN1bW1hcnkkYmljLCB4bGFiID0gIk51bWJlciBvZiB2YXJpYWJsZXMiLCB5bGFiID0gIkJJQyIsIHR5cGUgPSAibCIpCm1pbi5iaWMgPC0gbWluKGZ3ZC5zdW1tYXJ5JGJpYykKc3RkLmJpYyA8LSBzZChmd2Quc3VtbWFyeSRiaWMpCmFibGluZShoID0gbWluLmJpYyArIDAuMiAqIHN0ZC5iaWMsIGNvbCA9ICJyZWQiLCBsdHkgPSAyKQphYmxpbmUoaCA9IG1pbi5iaWMgLSAwLjIgKiBzdGQuYmljLCBjb2wgPSAicmVkIiwgbHR5ID0gMikKCiMgQWRqdXN0ZWQgUjIKcGxvdChmd2Quc3VtbWFyeSRhZGpyMiwgeGxhYiA9ICJOdW1iZXIgb2YgdmFyaWFibGVzIiwgeWxhYiA9ICJBZGp1c3RlZCBSMiIsIHR5cGUgPSAibCIsIHlsaW0gPSBjKDAuNCwgMC44NCkpCm1heC5hZGpyMiA8LSBtYXgoZndkLnN1bW1hcnkkYWRqcjIpCnN0ZC5hZGpyMiA8LSBzZChmd2Quc3VtbWFyeSRhZGpyMikKYWJsaW5lKGggPSBtYXguYWRqcjIgKyAwLjIgKiBzdGQuYWRqcjIsIGNvbCA9ICJyZWQiLCBsdHkgPSAyKQphYmxpbmUoaCA9IG1heC5hZGpyMiAtIDAuMiAqIHN0ZC5hZGpyMiwgY29sID0gInJlZCIsIGx0eSA9IDIpCmBgYAoKUGxvdHMgc2hvdyB0aGF0IHNpemUgNiBpcyB0aGUgbWluaW11bSBzaXplIGZvciB3aGljaCB0aGUgc2NvcmVzIGFyZSB3aXRoaW4gMC4yIHN0YW5kYXJkIGRldmlhdGlvbnMgb2YgdGhlIG9wdGltdW0uCgpgYGB7cn0KY29lZmZzIDwtIGNvZWYoZml0LmZ3ZCwgaWQgPSA2KQpwcmludChjb2VmZnMpCmBgYAoKVGhlIHNlbGVjdGVkIGZlYXR1cmVzIGFyZTogUHJpdmF0ZVllcywgUm9vbS5Cb2FyZCwgUGhELCBwZXJjLmFsdW1uaSwgRXhwZW5kLCBhbmQgR3JhZC5SYXRlLgoKIyMjIGIKCmBgYHtyfQpnYW0uZml0IDwtIGdhbShPdXRzdGF0ZSB+IFByaXZhdGUgKyAKICAgICAgICAgICAgICAgICBzKFJvb20uQm9hcmQsIGRmID0gMikgKwogICAgICAgICAgICAgICAgIHMoUGhELCBkZiA9IDIpICsKICAgICAgICAgICAgICAgICBzKHBlcmMuYWx1bW5pLCBkZiA9IDIpICsKICAgICAgICAgICAgICAgICBzKEV4cGVuZCwgZGYgPSA1KSArCiAgICAgICAgICAgICAgICAgcyhHcmFkLlJhdGUsIGRmID0gMiksCiAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl9zZXQpCgpwYXIobWZyb3cgPSBjKDIsIDMpKQpwbG90KGdhbS5maXQsIHNlID0gVFJVRSwgY29sID0gImJsdWUiKQpgYGAKCiMjIyBjCgpgYGB7cn0KZ2FtLnByZWQgPC0gcHJlZGljdChnYW0uZml0LCBuZXdkYXRhID0gdGVzdF9zZXQpCmVyciA8LSBtZWFuKCh0ZXN0X3NldCRPdXRzdGF0ZSAtIGdhbS5wcmVkKV4yKQpjYXQoIlRlc3QgTVNFOiIsIGVyciwgIlxuIikKCnRzcyA8LSBtZWFuKCh0ZXN0X3NldCRPdXRzdGF0ZSAtIG1lYW4odGVzdF9zZXQkT3V0c3RhdGUpKV4yKQpyMiA8LSAxIC0gZXJyIC8gdHNzCmNhdCgiVGVzdCBSMjoiLCByMiwgIlxuIikKYGBgCgpXZSBvYnRhaW4gYSB0ZXN0IFIyIG9mIDAuNzcgdXNpbmcgYSBHQU0gd2l0aCA2IHByZWRpY3RvcnMuCgojIyMgZAoKYGBge3J9CnN1bW1hcnkoZ2FtLmZpdCkKYGBgCgpBTk9WQSBzaG93cyBldmlkZW5jZSBvZiBhIG5vbiBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gT3V0c3RhdGUgYW5kIEV4cGVuZCBhbmQgbW9kZXJhdGVseSBzdHJvbmcgZXZpZGVuY2Ugb2YgYSBub24gbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIE91dHN0YXRlIGFuZCBHcmFkLlJhdGUuCg==