This assignment follows questions modeled in chapter 7 of the ISLR textbook for data analysis.

Question 6

In this exercise, you will further analyze the Wage data set considered throughout this chapter

a.) Polynomial Regression

Perform polynomial regression to predict wage using age. Use cross-validation to select the optimal degree d for the polynomial. What degree was chosen, and how does this compare to the results of hypothesis testing using ANOVA? Make a plot of the resulting polynomial fit to the data.

library(ISLR2)
library(caret)
library(ggplot2)

data(Wage)

set.seed(1)

ctrl <- trainControl(method = "cv", number = 10)

poly.models <- list()
cv.errors <- numeric(10)

for(i in 1:10) {
  poly.formula <- as.formula(
    paste("wage~poly(age,", i, ")")
  )
  poly.models[[i]] <- train(
    poly.formula, 
    data = Wage, 
    method = "lm",
    trControl = ctrl
  )
  cv.errors[i] <- min(poly.models[[i]]$results$RMSE^2)
}
cv.errors
 [1] 1670.277 1596.717 1582.212 1581.502 1584.812 1578.691
 [7] 1591.044 1585.468 1586.835 1583.362
best.degree <- which.min(poly.models[[i]]$results$RMSE^2)
cat("Degree Chosen:", best.degree, "\n")
Degree Chosen: 1 
poly.fit <- lm(wage ~ poly(age, best.degree), data = Wage)
summary(poly.fit)

Call:
lm(formula = wage ~ poly(age, best.degree), data = Wage)

Residuals:
     Min       1Q   Median       3Q      Max 
-100.265  -25.115   -6.063   16.601  205.748 

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)            111.7036     0.7473  149.48   <2e-16 ***
poly(age, best.degree) 447.0679    40.9291   10.92   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 40.93 on 2998 degrees of freedom
Multiple R-squared:  0.03827,   Adjusted R-squared:  0.03795 
F-statistic: 119.3 on 1 and 2998 DF,  p-value: < 2.2e-16
#Compare to anova
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
#plot
age.grid <- data.frame(age = seq(min(Wage$age), 
                                 max(Wage$age), 
                                 length = 100))
pred <- predict(poly.fit, newdata = age.grid)

ggplot(Wage, aes(age, wage))+
  geom_point(alpha = .4)+
  geom_line(data = age.grid,
            aes(age, pred),
            color = "blue",
            linewidth = 1.2)+
  labs(title = paste("Polynomial Regression (Degree", best.degree, ")"))

b.) Fit a step function

Fit a step function to predict wage using age, and perform cross-validation to choose the optimal number of cuts. Make a plot of the fit obtained.

cuts <- 2:10
step.models <- list()
step.errors <- numeric(length(cuts))

for(i in seq_along(cuts)){
  step.formula <- as.formula(
    paste("wage ~ cut(age,", cuts[i], ")")
  )
  step.models[[i]] <- train(
    step.formula,
    data = Wage,
    method = "lm", 
    trControl = ctrl
  )
  step.errors[i] <- min(step.models[[i]]$results$RMSE^2)
}
step.errors
[1] 1724.581 1674.529 1613.872 1623.198 1614.032 1603.570 1594.231
[8] 1600.528 1602.152
best.cuts <- cuts[which.min(step.errors)]
cat("Best cuts:", best.cuts, "\n")
Best cuts: 8 
step.fit <- lm(
  wage ~ cut(age, best.cuts),
  data = Wage
)

summary(step.fit)

Call:
lm(formula = wage ~ cut(age, best.cuts), data = Wage)

Residuals:
    Min      1Q  Median      3Q     Max 
-99.697 -24.552  -5.307  15.417 198.560 

Coefficients:
                               Estimate Std. Error t value
(Intercept)                      76.282      2.630  29.007
cut(age, best.cuts)(25.8,33.5]   25.833      3.161   8.172
cut(age, best.cuts)(33.5,41.2]   40.226      3.049  13.193
cut(age, best.cuts)(41.2,49]     43.501      3.018  14.412
cut(age, best.cuts)(49,56.8]     40.136      3.177  12.634
cut(age, best.cuts)(56.8,64.5]   44.102      3.564  12.373
cut(age, best.cuts)(64.5,72.2]   28.948      6.042   4.792
cut(age, best.cuts)(72.2,80.1]   15.224      9.781   1.556
                               Pr(>|t|)    
(Intercept)                     < 2e-16 ***
cut(age, best.cuts)(25.8,33.5] 4.44e-16 ***
cut(age, best.cuts)(33.5,41.2]  < 2e-16 ***
cut(age, best.cuts)(41.2,49]    < 2e-16 ***
cut(age, best.cuts)(49,56.8]    < 2e-16 ***
cut(age, best.cuts)(56.8,64.5]  < 2e-16 ***
cut(age, best.cuts)(64.5,72.2] 1.74e-06 ***
cut(age, best.cuts)(72.2,80.1]     0.12    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 39.97 on 2992 degrees of freedom
Multiple R-squared:  0.08467,   Adjusted R-squared:  0.08253 
F-statistic: 39.54 on 7 and 2992 DF,  p-value: < 2.2e-16
#plot

step.pred <-  predict(
  step.fit, 
  newdata = data.frame(age = age.grid)
)

step.plot <- data.frame(
  age = age.grid, 
  wage = step.pred
)

ggplot(Wage, aes(age, wage)) +
  geom_point(alpha = .4) +
  geom_line(
    data = step.plot,
    aes(age, wage), 
    color = "blue",
    linewidth = 1.2
  ) +
  labs(title = paste("Step Function (", best.cuts, "cuts)", sep = ""),
       xlab = "Age",
       ylab = "Wage")

Question 10

This Question relates to the College data set

a.) Stepwise Selection

split the data into a training set and a test set. Using out-of-state tuition as the response and the other variables as the predictors, perform stepwise selection on the training set in order to identify a satisfactory model that uses just a subset of the predictors.

library(ISLR2)
library(caret)
library(MASS)
library(gam)

data(College)

set.seed(1)

train.index <- createDataPartition(College$Outstate,
                                   p = 0.7,
                                   list = FALSE)

College.train <- College[train.index, ]
College.test  <- College[-train.index, ]

full.fit <- lm(Outstate ~ ., data = College.train)

step.fit <- stepAIC(full.fit,
                    direction = "both",
                    trace = FALSE)

summary(step.fit)

b.) GAM

fit a GAM on the training data, using out-of-state tuition as the response and the features selected in the previous step as the predictors. Plot the results, and explain your findings.

gam.fit <- gam(
  Outstate ~
    Private +
    lo(Room.Board) +
    lo(Expend) +
    lo(PhD) +
    lo(perc.alumni) +
    lo(Grad.Rate),
  data = College.train
)

summary(gam.fit)

Call: gam(formula = Outstate ~ Private + lo(Room.Board) + lo(Expend) + 
    lo(PhD) + lo(perc.alumni) + lo(Grad.Rate), data = College.train)
Deviance Residuals:
   Min     1Q Median     3Q    Max 
 -6785  -1139     62   1229   4768 

(Dispersion Parameter for gaussian family taken to be 3242650)

    Null Deviance: 8648835880 on 545 degrees of freedom
Residual Deviance: 1700199687 on 524.3242 degrees of freedom
AIC: 9758.293 

Number of Local Scoring Iterations: NA 

Anova for Parametric Effects
                    Df     Sum Sq    Mean Sq F value    Pr(>F)    
Private           1.00 2619605560 2619605560 807.860 < 2.2e-16 ***
lo(Room.Board)    1.00 1684465323 1684465323 519.472 < 2.2e-16 ***
lo(Expend)        1.00 1284709139 1284709139 396.191 < 2.2e-16 ***
lo(PhD)           1.00  174755812  174755812  53.893 8.128e-13 ***
lo(perc.alumni)   1.00  160221174  160221174  49.411 6.494e-12 ***
lo(Grad.Rate)     1.00   78079935   78079935  24.079 1.236e-06 ***
Residuals       524.32 1700199687    3242650                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Anova for Nonparametric Effects
                Npar Df  Npar F    Pr(F)    
(Intercept)                                 
Private                                     
lo(Room.Board)      3.0  3.4006  0.01816 *  
lo(Expend)          4.0 20.3418 1.11e-15 ***
lo(PhD)             2.8  0.9328  0.41989    
lo(perc.alumni)     2.3  0.7208  0.50664    
lo(Grad.Rate)       2.6  2.0513  0.11582    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
par(mfrow = c(2,3))

plot(gam.fit,
     se = TRUE,
     col = "blue")

c.) Results

Evaluate the model obtained on the test set, and explain the results obtained.

gam.pred <- predict(gam.fit,
                    newdata = College.test)

gam.mse <- mean((gam.pred - College.test$Outstate)^2)

gam.rmse <- sqrt(gam.mse)

gam.mse
[1] 4193772
gam.rmse
[1] 2047.87

The model used produced a mean squared error (MSE) of 4,193,773, while the root mean squared error (RMSE) produced was 2,047. Meaning the predicted tuition differs $2,047 on average when compared to the actual tuition values.

d.) Non-linearity

For which variables, if any, is there evidence of a non-linear relationship with the response?

summary(gam.fit)

Call: gam(formula = Outstate ~ Private + lo(Room.Board) + lo(Expend) + 
    lo(PhD) + lo(perc.alumni) + lo(Grad.Rate), data = College.train)
Deviance Residuals:
   Min     1Q Median     3Q    Max 
 -6785  -1139     62   1229   4768 

(Dispersion Parameter for gaussian family taken to be 3242650)

    Null Deviance: 8648835880 on 545 degrees of freedom
Residual Deviance: 1700199687 on 524.3242 degrees of freedom
AIC: 9758.293 

Number of Local Scoring Iterations: NA 

Anova for Parametric Effects
                    Df     Sum Sq    Mean Sq F value    Pr(>F)    
Private           1.00 2619605560 2619605560 807.860 < 2.2e-16 ***
lo(Room.Board)    1.00 1684465323 1684465323 519.472 < 2.2e-16 ***
lo(Expend)        1.00 1284709139 1284709139 396.191 < 2.2e-16 ***
lo(PhD)           1.00  174755812  174755812  53.893 8.128e-13 ***
lo(perc.alumni)   1.00  160221174  160221174  49.411 6.494e-12 ***
lo(Grad.Rate)     1.00   78079935   78079935  24.079 1.236e-06 ***
Residuals       524.32 1700199687    3242650                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Anova for Nonparametric Effects
                Npar Df  Npar F    Pr(F)    
(Intercept)                                 
Private                                     
lo(Room.Board)      3.0  3.4006  0.01816 *  
lo(Expend)          4.0 20.3418 1.11e-15 ***
lo(PhD)             2.8  0.9328  0.41989    
lo(perc.alumni)     2.3  0.7208  0.50664    
lo(Grad.Rate)       2.6  2.0513  0.11582    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Based on Anova for Nonparametric Effects, variables that show evidence of non-linearity include Expend (p = 1.1e-15) and Room.Board (p = .0182). With Expend showing significant non-linearity.

LS0tDQp0aXRsZTogIkFzc2lnbm1lbnQgNiINCmF1dGhvcjogIkFzaGxleSBUb3JyZXMiDQpkYXRlOiAiMjAyNi0wNy0xNSINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdG9jLWRlcHRoOiAzDQogICAgdGhlbWU6IGNvc21vDQotLS0NCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB3YXJuaW5nID0gRkFMU0UsDQogIG1lc3NhZ2UgPSBGQUxTRSwNCiAgcmVzdWx0cyA9ICdob2xkJywNCiAgZmlnLnNob3cgPSAnaG9sZCcNCikNCmBgYA0KDQpUaGlzIGFzc2lnbm1lbnQgZm9sbG93cyBxdWVzdGlvbnMgbW9kZWxlZCBpbiBjaGFwdGVyIDcgb2YgdGhlIFtJU0xSIHRleHRib29rXShodHRwczovL3d3dy5zdGF0bGVhcm5pbmcuY29tLykgZm9yIGRhdGEgYW5hbHlzaXMuIA0KDQoNCiMjIFF1ZXN0aW9uIDYNCg0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBmdXJ0aGVyIGFuYWx5emUgdGhlIFdhZ2UgZGF0YSBzZXQgY29uc2lkZXJlZCB0aHJvdWdob3V0IHRoaXMgY2hhcHRlcg0KDQoNCiMjIyBhLikgUG9seW5vbWlhbCBSZWdyZXNzaW9uDQoNClBlcmZvcm0gcG9seW5vbWlhbCByZWdyZXNzaW9uIHRvIHByZWRpY3Qgd2FnZSB1c2luZyBhZ2UuIFVzZSBjcm9zcy12YWxpZGF0aW9uIHRvIHNlbGVjdCB0aGUgb3B0aW1hbCBkZWdyZWUgZCBmb3IgdGhlIHBvbHlub21pYWwuIFdoYXQgZGVncmVlIHdhcyBjaG9zZW4sIGFuZCBob3cgZG9lcyB0aGlzIGNvbXBhcmUgdG8gdGhlIHJlc3VsdHMgb2YgaHlwb3RoZXNpcyB0ZXN0aW5nIHVzaW5nIEFOT1ZBPyBNYWtlIGEgcGxvdCBvZiB0aGUgcmVzdWx0aW5nIHBvbHlub21pYWwgZml0IHRvIHRoZSBkYXRhLg0KDQpgYGB7cn0NCmxpYnJhcnkoSVNMUjIpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpkYXRhKFdhZ2UpDQoNCnNldC5zZWVkKDEpDQoNCmN0cmwgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKQ0KDQpwb2x5Lm1vZGVscyA8LSBsaXN0KCkNCmN2LmVycm9ycyA8LSBudW1lcmljKDEwKQ0KDQpmb3IoaSBpbiAxOjEwKSB7DQogIHBvbHkuZm9ybXVsYSA8LSBhcy5mb3JtdWxhKA0KICAgIHBhc3RlKCJ3YWdlfnBvbHkoYWdlLCIsIGksICIpIikNCiAgKQ0KICBwb2x5Lm1vZGVsc1tbaV1dIDwtIHRyYWluKA0KICAgIHBvbHkuZm9ybXVsYSwgDQogICAgZGF0YSA9IFdhZ2UsIA0KICAgIG1ldGhvZCA9ICJsbSIsDQogICAgdHJDb250cm9sID0gY3RybA0KICApDQogIGN2LmVycm9yc1tpXSA8LSBtaW4ocG9seS5tb2RlbHNbW2ldXSRyZXN1bHRzJFJNU0VeMikNCn0NCmN2LmVycm9ycw0KDQpiZXN0LmRlZ3JlZSA8LSB3aGljaC5taW4ocG9seS5tb2RlbHNbW2ldXSRyZXN1bHRzJFJNU0VeMikNCmNhdCgiRGVncmVlIENob3NlbjoiLCBiZXN0LmRlZ3JlZSwgIlxuIikNCg0KcG9seS5maXQgPC0gbG0od2FnZSB+IHBvbHkoYWdlLCBiZXN0LmRlZ3JlZSksIGRhdGEgPSBXYWdlKQ0Kc3VtbWFyeShwb2x5LmZpdCkNCg0KI0NvbXBhcmUgdG8gYW5vdmENCmZpdDEgPC0gbG0od2FnZSB+IGFnZSwgZGF0YSA9IFdhZ2UpDQpmaXQyIDwtIGxtKHdhZ2UgfiBwb2x5KGFnZSwyKSwgZGF0YSA9IFdhZ2UpDQpmaXQzIDwtIGxtKHdhZ2UgfiBwb2x5KGFnZSwzKSwgZGF0YSA9IFdhZ2UpDQpmaXQ0IDwtIGxtKHdhZ2UgfiBwb2x5KGFnZSw0KSwgZGF0YSA9IFdhZ2UpDQpmaXQ1IDwtIGxtKHdhZ2UgfiBwb2x5KGFnZSw1KSwgZGF0YSA9IFdhZ2UpDQphbm92YShmaXQxLCBmaXQyLCBmaXQzLCBmaXQ0LCBmaXQ1KQ0KDQojcGxvdA0KYWdlLmdyaWQgPC0gZGF0YS5mcmFtZShhZ2UgPSBzZXEobWluKFdhZ2UkYWdlKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgoV2FnZSRhZ2UpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aCA9IDEwMCkpDQpwcmVkIDwtIHByZWRpY3QocG9seS5maXQsIG5ld2RhdGEgPSBhZ2UuZ3JpZCkNCg0KZ2dwbG90KFdhZ2UsIGFlcyhhZ2UsIHdhZ2UpKSsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IC40KSsNCiAgZ2VvbV9saW5lKGRhdGEgPSBhZ2UuZ3JpZCwNCiAgICAgICAgICAgIGFlcyhhZ2UsIHByZWQpLA0KICAgICAgICAgICAgY29sb3IgPSAiYmx1ZSIsDQogICAgICAgICAgICBsaW5ld2lkdGggPSAxLjIpKw0KICBsYWJzKHRpdGxlID0gcGFzdGUoIlBvbHlub21pYWwgUmVncmVzc2lvbiAoRGVncmVlIiwgYmVzdC5kZWdyZWUsICIpIikpDQpgYGANCg0KDQoNCg0KIyMjIGIuKSBGaXQgYSBzdGVwIGZ1bmN0aW9uIA0KDQpGaXQgYSBzdGVwIGZ1bmN0aW9uIHRvIHByZWRpY3Qgd2FnZSB1c2luZyBhZ2UsIGFuZCBwZXJmb3JtIGNyb3NzLXZhbGlkYXRpb24gdG8gY2hvb3NlIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjdXRzLiBNYWtlIGEgcGxvdCBvZiB0aGUgZml0IG9idGFpbmVkLg0KDQpgYGB7cn0NCmN1dHMgPC0gMjoxMA0Kc3RlcC5tb2RlbHMgPC0gbGlzdCgpDQpzdGVwLmVycm9ycyA8LSBudW1lcmljKGxlbmd0aChjdXRzKSkNCg0KZm9yKGkgaW4gc2VxX2Fsb25nKGN1dHMpKXsNCiAgc3RlcC5mb3JtdWxhIDwtIGFzLmZvcm11bGEoDQogICAgcGFzdGUoIndhZ2UgfiBjdXQoYWdlLCIsIGN1dHNbaV0sICIpIikNCiAgKQ0KICBzdGVwLm1vZGVsc1tbaV1dIDwtIHRyYWluKA0KICAgIHN0ZXAuZm9ybXVsYSwNCiAgICBkYXRhID0gV2FnZSwNCiAgICBtZXRob2QgPSAibG0iLCANCiAgICB0ckNvbnRyb2wgPSBjdHJsDQogICkNCiAgc3RlcC5lcnJvcnNbaV0gPC0gbWluKHN0ZXAubW9kZWxzW1tpXV0kcmVzdWx0cyRSTVNFXjIpDQp9DQpzdGVwLmVycm9ycw0KDQpiZXN0LmN1dHMgPC0gY3V0c1t3aGljaC5taW4oc3RlcC5lcnJvcnMpXQ0KY2F0KCJCZXN0IGN1dHM6IiwgYmVzdC5jdXRzLCAiXG4iKQ0KDQpzdGVwLmZpdCA8LSBsbSgNCiAgd2FnZSB+IGN1dChhZ2UsIGJlc3QuY3V0cyksDQogIGRhdGEgPSBXYWdlDQopDQoNCnN1bW1hcnkoc3RlcC5maXQpDQoNCiNwbG90DQoNCnN0ZXAucHJlZCA8LSAgcHJlZGljdCgNCiAgc3RlcC5maXQsIA0KICBuZXdkYXRhID0gZGF0YS5mcmFtZShhZ2UgPSBhZ2UuZ3JpZCkNCikNCg0Kc3RlcC5wbG90IDwtIGRhdGEuZnJhbWUoDQogIGFnZSA9IGFnZS5ncmlkLCANCiAgd2FnZSA9IHN0ZXAucHJlZA0KKQ0KDQpnZ3Bsb3QoV2FnZSwgYWVzKGFnZSwgd2FnZSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IC40KSArDQogIGdlb21fbGluZSgNCiAgICBkYXRhID0gc3RlcC5wbG90LA0KICAgIGFlcyhhZ2UsIHdhZ2UpLCANCiAgICBjb2xvciA9ICJibHVlIiwNCiAgICBsaW5ld2lkdGggPSAxLjINCiAgKSArDQogIGxhYnModGl0bGUgPSBwYXN0ZSgiU3RlcCBGdW5jdGlvbiAoIiwgYmVzdC5jdXRzLCAiY3V0cykiLCBzZXAgPSAiIiksDQogICAgICAgeGxhYiA9ICJBZ2UiLA0KICAgICAgIHlsYWIgPSAiV2FnZSIpDQpgYGANCg0KDQoNCiMjIFF1ZXN0aW9uIDEwDQoNClRoaXMgUXVlc3Rpb24gcmVsYXRlcyB0byB0aGUgQ29sbGVnZSBkYXRhIHNldA0KDQojIyMgYS4pIFN0ZXB3aXNlIFNlbGVjdGlvbg0KDQpzcGxpdCB0aGUgZGF0YSBpbnRvIGEgdHJhaW5pbmcgc2V0IGFuZCBhIHRlc3Qgc2V0LiBVc2luZyBvdXQtb2Ytc3RhdGUgdHVpdGlvbiBhcyB0aGUgcmVzcG9uc2UgYW5kIHRoZSBvdGhlciB2YXJpYWJsZXMgYXMgdGhlIHByZWRpY3RvcnMsIHBlcmZvcm0gc3RlcHdpc2Ugc2VsZWN0aW9uIG9uIHRoZSB0cmFpbmluZyBzZXQgaW4gb3JkZXIgdG8gaWRlbnRpZnkgYSBzYXRpc2ZhY3RvcnkgbW9kZWwgdGhhdCB1c2VzIGp1c3QgYSBzdWJzZXQgb2YgdGhlIHByZWRpY3RvcnMuDQoNCmBgYHtyLCByZXN1bHRzID0gRkFMU0V9DQpsaWJyYXJ5KElTTFIyKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoTUFTUykNCmxpYnJhcnkoZ2FtKQ0KDQpkYXRhKENvbGxlZ2UpDQoNCnNldC5zZWVkKDEpDQoNCnRyYWluLmluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oQ29sbGVnZSRPdXRzdGF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcCA9IDAuNywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCA9IEZBTFNFKQ0KDQpDb2xsZWdlLnRyYWluIDwtIENvbGxlZ2VbdHJhaW4uaW5kZXgsIF0NCkNvbGxlZ2UudGVzdCAgPC0gQ29sbGVnZVstdHJhaW4uaW5kZXgsIF0NCg0KZnVsbC5maXQgPC0gbG0oT3V0c3RhdGUgfiAuLCBkYXRhID0gQ29sbGVnZS50cmFpbikNCg0Kc3RlcC5maXQgPC0gc3RlcEFJQyhmdWxsLmZpdCwNCiAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImJvdGgiLA0KICAgICAgICAgICAgICAgICAgICB0cmFjZSA9IEZBTFNFKQ0KDQpzdW1tYXJ5KHN0ZXAuZml0KQ0KYGBgDQoNCg0KDQoNCiMjIyBiLikgR0FNDQoNCmZpdCBhIEdBTSBvbiB0aGUgdHJhaW5pbmcgZGF0YSwgdXNpbmcgb3V0LW9mLXN0YXRlIHR1aXRpb24gYXMgdGhlIHJlc3BvbnNlIGFuZCB0aGUgZmVhdHVyZXMgc2VsZWN0ZWQgaW4gdGhlIHByZXZpb3VzIHN0ZXAgYXMgdGhlIHByZWRpY3RvcnMuIFBsb3QgdGhlIHJlc3VsdHMsIGFuZCBleHBsYWluIHlvdXIgZmluZGluZ3MuDQoNCmBgYHtyfQ0KZ2FtLmZpdCA8LSBnYW0oDQogIE91dHN0YXRlIH4NCiAgICBQcml2YXRlICsNCiAgICBsbyhSb29tLkJvYXJkKSArDQogICAgbG8oRXhwZW5kKSArDQogICAgbG8oUGhEKSArDQogICAgbG8ocGVyYy5hbHVtbmkpICsNCiAgICBsbyhHcmFkLlJhdGUpLA0KICBkYXRhID0gQ29sbGVnZS50cmFpbg0KKQ0KDQpzdW1tYXJ5KGdhbS5maXQpDQoNCnBhcihtZnJvdyA9IGMoMiwzKSkNCg0KcGxvdChnYW0uZml0LA0KICAgICBzZSA9IFRSVUUsDQogICAgIGNvbCA9ICJibHVlIikNCmBgYA0KDQoNCg0KDQojIyMgYy4pIFJlc3VsdHMNCg0KRXZhbHVhdGUgdGhlIG1vZGVsIG9idGFpbmVkIG9uIHRoZSB0ZXN0IHNldCwgYW5kIGV4cGxhaW4gdGhlIHJlc3VsdHMgb2J0YWluZWQuDQoNCmBgYHtyfQ0KZ2FtLnByZWQgPC0gcHJlZGljdChnYW0uZml0LA0KICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gQ29sbGVnZS50ZXN0KQ0KDQpnYW0ubXNlIDwtIG1lYW4oKGdhbS5wcmVkIC0gQ29sbGVnZS50ZXN0JE91dHN0YXRlKV4yKQ0KDQpnYW0ucm1zZSA8LSBzcXJ0KGdhbS5tc2UpDQoNCmdhbS5tc2UNCmdhbS5ybXNlDQpgYGANCg0KKipUaGUgbW9kZWwgdXNlZCBwcm9kdWNlZCBhIG1lYW4gc3F1YXJlZCBlcnJvciAoTVNFKSBvZiA0LDE5Myw3NzMsIHdoaWxlIHRoZSByb290IG1lYW4gc3F1YXJlZCBlcnJvciAoUk1TRSkgcHJvZHVjZWQgd2FzIDIsMDQ3LiBNZWFuaW5nIHRoZSBwcmVkaWN0ZWQgdHVpdGlvbiBkaWZmZXJzICQyLDA0NyBvbiBhdmVyYWdlIHdoZW4gY29tcGFyZWQgdG8gdGhlIGFjdHVhbCB0dWl0aW9uIHZhbHVlcy4qKg0KDQoNCg0KDQojIyMgZC4pIE5vbi1saW5lYXJpdHkNCg0KRm9yIHdoaWNoIHZhcmlhYmxlcywgaWYgYW55LCBpcyB0aGVyZSBldmlkZW5jZSBvZiBhIG5vbi1saW5lYXIgcmVsYXRpb25zaGlwIHdpdGggdGhlIHJlc3BvbnNlPw0KDQpgYGB7cn0NCnN1bW1hcnkoZ2FtLmZpdCkNCmBgYA0KDQoqKkJhc2VkIG9uIEFub3ZhIGZvciBOb25wYXJhbWV0cmljIEVmZmVjdHMsIHZhcmlhYmxlcyB0aGF0IHNob3cgZXZpZGVuY2Ugb2Ygbm9uLWxpbmVhcml0eSBpbmNsdWRlIEV4cGVuZCAocCA9IDEuMWUtMTUpIGFuZCBSb29tLkJvYXJkIChwID0gLjAxODIpLiBXaXRoIEV4cGVuZCBzaG93aW5nIHNpZ25pZmljYW50IG5vbi1saW5lYXJpdHkuKioNCg==