setwd("/Users/kayhanbabakan/OneDrive/MIT/Analytics Edge/HW7")
rm(list=ls())
Hitters <- read.csv("Hitters.csv")
set.seed(15071)
train.obs <- sort(sample(seq_len(nrow(Hitters)), 0.7*nrow(Hitters))) 
train <- Hitters[train.obs,2:21]
test <- Hitters[-train.obs,2:21]

Initial Predictors
Explore the dataset by plotting the player salaries as a function of a few predictors. Include 2–3 visualizations. Characterize and discuss a few patterns that you observe.

AtBat=ggplot(Hitters,aes(AtBat,Salary))+
  geom_point()+
  geom_smooth()

Hits=ggplot(Hitters,aes(CHits,Salary))+
  geom_point()+
  geom_smooth()

HmRun=ggplot(Hitters,aes(CHmRun,Salary))+
  geom_point()+
  geom_smooth()

ggarrange(AtBat,Hits,HmRun,ncol=1,nrow=3)

The visualization I chose to incoporate show some of they key performance measures the league uses to justify salaray. As ATBat CHits, and Homeruns generally increases the total salary. It is good to note here that there are a few outliers in the data.

Correlation Matrix
Report the correlation matrix between the numerical predictors (i.e., all predictors except Name, League, Division and NewLeague). You can restrict the dataset to the numerical predictors with Hitters[,2:18]. What do you observe? Does this make sense, in view of the problem?

corplot=ggcorr(select(Hitters,-c("Name","League","Division","NewLeague")),size=2)+
  theme(axis.text.x = element_text(angle = 90, hjust = 1))
corplot

As expected there is a strong correltion amongst baseball stats and metrics relative to the players salary. The better the performance the stronger the salary! Additionaly there is alot of multicolinearity amongst the predictors. i.e the number of times at bat has a strong correlation with the number of times you are able to make a hit, a home run or even a run.

Linear Regression Fit a linear regression model with all the predictors using the training set, and make predictions on the test set. Report the in-sample and out-of-sample R2.Comment briefly on the sign and significance of the variables and the R2 values. Does this make sense, in view of your earlier observations?

lm = lm(Salary~.,train)
predict_train=predict(lm,train)
predict_test=predict(lm,test)
SSETrain= sum((predict_train - train$Salary)^2)
SSTTrain = sum((mean(train$Salary)-train$Salary)^2)
SSETest= sum((predict_test - test$Salary)^2)
SSTTest = sum((mean(train$Salary)-test$Salary)^2)
R2.LM = 1-(SSETrain/SSTTrain)
OSR2.LM <- 1-(SSETest/SSTTest)
summary(lm)

Call:
lm(formula = Salary ~ ., data = train)

Residuals:
   Min     1Q Median     3Q    Max 
-742.5 -176.4  -24.5  139.2 1838.7 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 246.38953  108.15508   2.278 0.024011 *  
AtBat        -3.18561    0.80557  -3.954 0.000114 ***
Hits         10.29267    2.97333   3.462 0.000685 ***
HmRun        -6.69027    7.47490  -0.895 0.372082    
Runs         -1.55120    3.65385  -0.425 0.671729    
RBI           1.27969    3.12671   0.409 0.682869    
Walks         7.67079    2.18198   3.516 0.000568 ***
Years       -22.90596   16.02952  -1.429 0.154910    
CAtBat        0.03047    0.16788   0.181 0.856207    
CHits         0.13791    0.83710   0.165 0.869350    
CHmRun        2.18851    2.12401   1.030 0.304355    
CRuns         0.47897    0.93183   0.514 0.607939    
CRBI          0.16728    0.86209   0.194 0.846387    
CWalks       -0.89886    0.40397  -2.225 0.027440 *  
PutOuts       0.25947    0.09390   2.763 0.006376 ** 
Assists       0.38031    0.28782   1.321 0.188236    
Errors        0.49402    5.88410   0.084 0.933193    
LeagueN      33.68519   96.03250   0.351 0.726212    
DivisionW   -84.15153   50.39587  -1.670 0.096865 .  
NewLeagueN   -6.42531   96.03884  -0.067 0.946740    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 319.9 on 164 degrees of freedom
Multiple R-squared:  0.5603,    Adjusted R-squared:  0.5093 
F-statistic:    11 on 19 and 164 DF,  p-value: < 2.2e-16
LMMod=data.frame("Model"="LM","R2"=R2.LM,"OSR2"=OSR2.LM)
LMMod

The significant independent variables are Walks, Cwalks, and PutOuts. Hits and AtBats. The signifcant varialbes dont always make sense as your hits increase your salary increases however as at bats increase you get less. This is due to the significant amount of multi-colinearity throughout the data. The R2 and OSR2 are .56 and .40, osr2 should be able to obtain better results by reducing the noise in the model.

Lasso/Ridge
Train ridge regression and LASSO models with 10-fold cross-validation to select the appropriate value of the shrinkage parameter λ, using the Mean Squared Error as the performance metric (this is the default option in the cv.glmnet() function). Plot the cross-validated Mean Squared Error as a function of λ. Report the value of λ that minimizes the Mean Squared Error for each method. [

x_salary.train=model.matrix(Salary~.-1,train) 
y_salary.train<-train[,c("Salary")] #set Y for glmnet fitting
x_salary.test=model.matrix(Salary~.-1,test) 
y_salary.test=test[,c("Salary")]


all.lambdas <- c(exp(seq(15, -10, -.1)))
set.seed(15071)
cv.ridge = cv.glmnet(x_salary.train,y_salary.train,alpha=0,lambda=all.lambdas,nfolds=10)
set.seed(15071)
cv.lasso = cv.glmnet(x_salary.train,y_salary.train,alpha=1,lambda=all.lambdas,nfolds=10)

bestlambdaridge = cv.ridge$lambda.min
bestlambdalasso = cv.lasso$lambda.min

plot(cv.ridge)

plot(cv.lasso)


rbind(data.frame("Model"="Ridge","Min RMSE Lambda"=bestlambdaridge),
data.frame("Model"="Lasso","Min RMSE Lambda"=bestlambdalasso))

#Retrain using best Lambda With the selected values of λ, re-train your ridge regression and LASSO models on the full training set. Report each model’s coefficients and comment on the effects of ridge regression vs. LASSO. Use each model to make predictions on the test set. Report the values of the in-sample R2 and the out-of-sample R2. Comment on your results.

set.seed(15071)
ridge.final <- glmnet(x_salary.train,y_salary.train,alpha=0,lambda=bestlambdaridge )
lasso.final <- glmnet(x_salary.train,y_salary.train,alpha=1,lambda=bestlambdalasso)

pred.train.lasso <- predict(lasso.final,x_salary.train)
pred.test.lasso <- predict(lasso.final,x_salary.test)
pred.train.ridge <- predict(ridge.final,x_salary.train)
pred.test.ridge <- predict(ridge.final,x_salary.test)

SSETrainLasso= sum((pred.train.lasso - train$Salary)^2)
SSETestLasso= sum((pred.test.lasso - test$Salary)^2)
SSETrainRidge= sum((pred.train.ridge - train$Salary)^2)
SSETestRidge= sum((pred.test.ridge - test$Salary)^2)

R2.Lasso = 1-(SSETrainLasso/SSTTrain)
OSR2.lasso <- 1-(SSETestLasso/SSTTest)
R2.Ridge = 1-(SSETrainRidge/SSTTrain)
OSR2.Ridge <- 1-(SSETestRidge/SSTTest)

LassoMod=data.frame("Model"="Lasso","R2"=R2.Lasso,"OSR2"=OSR2.lasso)
RidgeMod=data.frame("Model"="Ridge","R2"=R2.Ridge,"OSR2"=OSR2.Ridge)
rbind(LMMod,LassoMod,RidgeMod)

The main differnece between ridge and lasso is lassos ability to drive the coefficients to 0 where ridge brings them to a very small number. Of the models, Lasso performs optimally providing the best OSR2 and only slightly worse R2. LM includes all variables with alot of noise which allows it to fit the training data better than the other methods but poorly on the test data.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpgYGB7ciBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KCJnZ3Bsb3QyIikKbGlicmFyeSgiZ3Bsb3RzIikKbGlicmFyeSgiZ2xtbmV0IikKbGlicmFyeSgiTUFTUyIpCmxpYnJhcnkoInRpZHl2ZXJzZSIpCmxpYnJhcnkoImRwbHlyIikKbGlicmFyeSgicmVzaGFwZSIpCmxpYnJhcnkoImdncHViciIpCmxpYnJhcnkoImdncGxvdDIiKQpsaWJyYXJ5KCJnbG1uZXQiKQpsaWJyYXJ5KCJyZXNoYXBlMiIpCmxpYnJhcnkoImhlYXRtYXBseSIpCmxpYnJhcnkoImR1bW1pZXMiKQpsaWJyYXJ5KCJkcGx5ciIpCmxpYnJhcnkoInRpZHlyIikKbGlicmFyeSgiY2FUb29scyIpCmxpYnJhcnkoImNhcmV0IikKbGlicmFyeSgiUk9DUiIpCmxpYnJhcnkoImdncHViciIpCmxpYnJhcnkoImdsbW5ldFV0aWxzIikKbGlicmFyeSgiR0dhbGx5IikKbGlicmFyeSgiZ2xtbmV0IikKbGlicmFyeSgiZHBseXIiKQpsaWJyYXJ5KCJnZ3Bsb3QyIikKbGlicmFyeSgidGlkeXIiKQpsaWJyYXJ5KCJsYXJzIikKbGlicmFyeSgibGVhcHMiKQpsaWJyYXJ5KCJnYm0iKQpsaWJyYXJ5KCJycGFydCIpCmxpYnJhcnkoImNvcnJwbG90IikKbGlicmFyeSgiTWV0cmljcyIpCmxpYnJhcnkoInJwYXJ0LnBsb3QiKQpsaWJyYXJ5KCJyYW5kb21Gb3Jlc3QiKQpgYGAKCmBgYHtyfQpzZXR3ZCgiL1VzZXJzL2theWhhbmJhYmFrYW4vT25lRHJpdmUvTUlUL0FuYWx5dGljcyBFZGdlL0hXNyIpCnJtKGxpc3Q9bHMoKSkKSGl0dGVycyA8LSByZWFkLmNzdigiSGl0dGVycy5jc3YiKQpzZXQuc2VlZCgxNTA3MSkKdHJhaW4ub2JzIDwtIHNvcnQoc2FtcGxlKHNlcV9sZW4obnJvdyhIaXR0ZXJzKSksIDAuNypucm93KEhpdHRlcnMpKSkgCnRyYWluIDwtIEhpdHRlcnNbdHJhaW4ub2JzLDI6MjFdCnRlc3QgPC0gSGl0dGVyc1stdHJhaW4ub2JzLDI6MjFdCmBgYAo8Yj5Jbml0aWFsIFByZWRpY3RvcnM8L2I+PC9icj4KRXhwbG9yZSB0aGUgZGF0YXNldCBieSBwbG90dGluZyB0aGUgcGxheWVyIHNhbGFyaWVzIGFzIGEgZnVuY3Rpb24gb2YgYSBmZXcgcHJlZGljdG9ycy4gSW5jbHVkZSAy4oCTMwp2aXN1YWxpemF0aW9ucy4gQ2hhcmFjdGVyaXplIGFuZCBkaXNjdXNzIGEgZmV3IHBhdHRlcm5zIHRoYXQgeW91IG9ic2VydmUuIApgYGB7ciBtZXNzYWdlPUZBTFNFfQpBdEJhdD1nZ3Bsb3QoSGl0dGVycyxhZXMoQXRCYXQsU2FsYXJ5KSkrCiAgZ2VvbV9wb2ludCgpKwogIGdlb21fc21vb3RoKCkKCkhpdHM9Z2dwbG90KEhpdHRlcnMsYWVzKENIaXRzLFNhbGFyeSkpKwogIGdlb21fcG9pbnQoKSsKICBnZW9tX3Ntb290aCgpCgpIbVJ1bj1nZ3Bsb3QoSGl0dGVycyxhZXMoQ0htUnVuLFNhbGFyeSkpKwogIGdlb21fcG9pbnQoKSsKICBnZW9tX3Ntb290aCgpCgpnZ2FycmFuZ2UoQXRCYXQsSGl0cyxIbVJ1bixuY29sPTEsbnJvdz0zKQpgYGAKPiBUaGUgdmlzdWFsaXphdGlvbiBJIGNob3NlIHRvIGluY29wb3JhdGUgc2hvdyBzb21lIG9mIHRoZXkga2V5IHBlcmZvcm1hbmNlIG1lYXN1cmVzIHRoZSBsZWFndWUgdXNlcyB0byBqdXN0aWZ5IHNhbGFyYXkuIEFzIEFUQmF0IENIaXRzLCBhbmQgSG9tZXJ1bnMgZ2VuZXJhbGx5IGluY3JlYXNlcyB0aGUgdG90YWwgc2FsYXJ5LiBJdCBpcyBnb29kIHRvIG5vdGUgaGVyZSB0aGF0IHRoZXJlIGFyZSBhIGZldyBvdXRsaWVycyBpbiB0aGUgZGF0YS4KCjxiPkNvcnJlbGF0aW9uIE1hdHJpeDwvYj48L2JyPgpSZXBvcnQgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCBiZXR3ZWVuIHRoZSBudW1lcmljYWwgcHJlZGljdG9ycyAoaS5lLiwgYWxsIHByZWRpY3RvcnMgZXhjZXB0IE5hbWUsCkxlYWd1ZSwgRGl2aXNpb24gYW5kIE5ld0xlYWd1ZSkuIFlvdSBjYW4gcmVzdHJpY3QgdGhlIGRhdGFzZXQgdG8gdGhlIG51bWVyaWNhbCBwcmVkaWN0b3JzIHdpdGgKSGl0dGVyc1ssMjoxOF0uIFdoYXQgZG8geW91IG9ic2VydmU/IERvZXMgdGhpcyBtYWtlIHNlbnNlLCBpbiB2aWV3IG9mIHRoZSBwcm9ibGVtPyAKCmBgYHtyIGVjaG89VFJVRX0KY29ycGxvdD1nZ2NvcnIoc2VsZWN0KEhpdHRlcnMsLWMoIk5hbWUiLCJMZWFndWUiLCJEaXZpc2lvbiIsIk5ld0xlYWd1ZSIpKSxzaXplPTIpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCmNvcnBsb3QKCmBgYAo+QXMgZXhwZWN0ZWQgdGhlcmUgaXMgYSBzdHJvbmcgY29ycmVsdGlvbiBhbW9uZ3N0IGJhc2ViYWxsIHN0YXRzIGFuZCBtZXRyaWNzIHJlbGF0aXZlIHRvIHRoZSBwbGF5ZXJzIHNhbGFyeS4gVGhlIGJldHRlciB0aGUgcGVyZm9ybWFuY2UgdGhlIHN0cm9uZ2VyIHRoZSBzYWxhcnkhIEFkZGl0aW9uYWx5IHRoZXJlIGlzIGFsb3Qgb2YgbXVsdGljb2xpbmVhcml0eSBhbW9uZ3N0IHRoZSBwcmVkaWN0b3JzLiBpLmUgdGhlIG51bWJlciBvZiB0aW1lcyBhdCBiYXQgaGFzIGEgc3Ryb25nIGNvcnJlbGF0aW9uIHdpdGggdGhlIG51bWJlciBvZiB0aW1lcyB5b3UgYXJlIGFibGUgdG8gbWFrZSBhIGhpdCwgYSBob21lIHJ1biBvciBldmVuIGEgcnVuLgoKPGI+TGluZWFyIFJlZ3Jlc3Npb248L2I+CkZpdCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHdpdGggYWxsIHRoZSBwcmVkaWN0b3JzIHVzaW5nIHRoZSB0cmFpbmluZyBzZXQsIGFuZCBtYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldC4gUmVwb3J0IHRoZSBpbi1zYW1wbGUgYW5kIG91dC1vZi1zYW1wbGUgUjIuQ29tbWVudCBicmllZmx5IG9uIHRoZSBzaWduIGFuZCBzaWduaWZpY2FuY2Ugb2YgdGhlIHZhcmlhYmxlcyBhbmQgdGhlIFIyIHZhbHVlcy4gRG9lcyB0aGlzIG1ha2Ugc2Vuc2UsIGluIHZpZXcgb2YgeW91ciBlYXJsaWVyIG9ic2VydmF0aW9ucz8KYGBge3IgZWNobz1UUlVFfQpsbSA9IGxtKFNhbGFyeX4uLHRyYWluKQpwcmVkaWN0X3RyYWluPXByZWRpY3QobG0sdHJhaW4pCnByZWRpY3RfdGVzdD1wcmVkaWN0KGxtLHRlc3QpClNTRVRyYWluPSBzdW0oKHByZWRpY3RfdHJhaW4gLSB0cmFpbiRTYWxhcnkpXjIpClNTVFRyYWluID0gc3VtKChtZWFuKHRyYWluJFNhbGFyeSktdHJhaW4kU2FsYXJ5KV4yKQpTU0VUZXN0PSBzdW0oKHByZWRpY3RfdGVzdCAtIHRlc3QkU2FsYXJ5KV4yKQpTU1RUZXN0ID0gc3VtKChtZWFuKHRyYWluJFNhbGFyeSktdGVzdCRTYWxhcnkpXjIpClIyLkxNID0gMS0oU1NFVHJhaW4vU1NUVHJhaW4pCk9TUjIuTE0gPC0gMS0oU1NFVGVzdC9TU1RUZXN0KQpzdW1tYXJ5KGxtKQpMTU1vZD1kYXRhLmZyYW1lKCJNb2RlbCI9IkxNIiwiUjIiPVIyLkxNLCJPU1IyIj1PU1IyLkxNKQpMTU1vZApgYGAKPlRoZSBzaWduaWZpY2FudCBpbmRlcGVuZGVudCB2YXJpYWJsZXMgYXJlIFdhbGtzLCBDd2Fsa3MsIGFuZCBQdXRPdXRzLiBIaXRzIGFuZCBBdEJhdHMuIFRoZSBzaWduaWZjYW50IHZhcmlhbGJlcyBkb250IGFsd2F5cyBtYWtlIHNlbnNlIGFzIHlvdXIgaGl0cyBpbmNyZWFzZSB5b3VyIHNhbGFyeSBpbmNyZWFzZXMgaG93ZXZlciBhcyBhdCBiYXRzIGluY3JlYXNlIHlvdSBnZXQgbGVzcy4gVGhpcyBpcyBkdWUgdG8gdGhlIHNpZ25pZmljYW50IGFtb3VudCBvZiBtdWx0aS1jb2xpbmVhcml0eSB0aHJvdWdob3V0IHRoZSBkYXRhLiBUaGUgUjIgYW5kIE9TUjIgYXJlIC41NiBhbmQgLjQwLCBvc3IyIHNob3VsZCBiZSBhYmxlIHRvIG9idGFpbiBiZXR0ZXIgcmVzdWx0cyBieSByZWR1Y2luZyB0aGUgbm9pc2UgaW4gdGhlIG1vZGVsLgoKPGI+TGFzc28vUmlkZ2U8L2I+PC9icj4KVHJhaW4gcmlkZ2UgcmVncmVzc2lvbiBhbmQgTEFTU08gbW9kZWxzIHdpdGggMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uIHRvIHNlbGVjdCB0aGUgYXBwcm9wcmlhdGUgdmFsdWUgb2YgdGhlIHNocmlua2FnZSBwYXJhbWV0ZXIgzrssIHVzaW5nIHRoZSBNZWFuIFNxdWFyZWQgRXJyb3IgYXMgdGhlIHBlcmZvcm1hbmNlIG1ldHJpYyAodGhpcyBpcyB0aGUgZGVmYXVsdCBvcHRpb24gaW4gdGhlIGN2LmdsbW5ldCgpIGZ1bmN0aW9uKS4gUGxvdCB0aGUgY3Jvc3MtdmFsaWRhdGVkIE1lYW4gU3F1YXJlZCBFcnJvciBhcyBhIGZ1bmN0aW9uIG9mIM67LiBSZXBvcnQgdGhlIHZhbHVlIG9mIM67IHRoYXQgbWluaW1pemVzIHRoZSBNZWFuIFNxdWFyZWQgRXJyb3IgZm9yIGVhY2ggbWV0aG9kLiBbCmBgYHtyIGVjaG89VFJVRX0KeF9zYWxhcnkudHJhaW49bW9kZWwubWF0cml4KFNhbGFyeX4uLTEsdHJhaW4pIAp5X3NhbGFyeS50cmFpbjwtdHJhaW5bLGMoIlNhbGFyeSIpXSAjc2V0IFkgZm9yIGdsbW5ldCBmaXR0aW5nCnhfc2FsYXJ5LnRlc3Q9bW9kZWwubWF0cml4KFNhbGFyeX4uLTEsdGVzdCkgCnlfc2FsYXJ5LnRlc3Q9dGVzdFssYygiU2FsYXJ5IildCgoKYWxsLmxhbWJkYXMgPC0gYyhleHAoc2VxKDE1LCAtMTAsIC0uMSkpKQpzZXQuc2VlZCgxNTA3MSkKY3YucmlkZ2UgPSBjdi5nbG1uZXQoeF9zYWxhcnkudHJhaW4seV9zYWxhcnkudHJhaW4sYWxwaGE9MCxsYW1iZGE9YWxsLmxhbWJkYXMsbmZvbGRzPTEwKQpzZXQuc2VlZCgxNTA3MSkKY3YubGFzc28gPSBjdi5nbG1uZXQoeF9zYWxhcnkudHJhaW4seV9zYWxhcnkudHJhaW4sYWxwaGE9MSxsYW1iZGE9YWxsLmxhbWJkYXMsbmZvbGRzPTEwKQoKYmVzdGxhbWJkYXJpZGdlID0gY3YucmlkZ2UkbGFtYmRhLm1pbgpiZXN0bGFtYmRhbGFzc28gPSBjdi5sYXNzbyRsYW1iZGEubWluCgpwbG90KGN2LnJpZGdlKQpwbG90KGN2Lmxhc3NvKQoKcmJpbmQoZGF0YS5mcmFtZSgiTW9kZWwiPSJSaWRnZSIsIk1pbiBSTVNFIExhbWJkYSI9YmVzdGxhbWJkYXJpZGdlKSwKZGF0YS5mcmFtZSgiTW9kZWwiPSJMYXNzbyIsIk1pbiBSTVNFIExhbWJkYSI9YmVzdGxhbWJkYWxhc3NvKSkKYGBgCiNSZXRyYWluIHVzaW5nIGJlc3QgTGFtYmRhCldpdGggdGhlIHNlbGVjdGVkIHZhbHVlcyBvZiDOuywgcmUtdHJhaW4geW91ciByaWRnZSByZWdyZXNzaW9uIGFuZCBMQVNTTyBtb2RlbHMgb24gdGhlIGZ1bGwgdHJhaW5pbmcgc2V0LgpSZXBvcnQgZWFjaCBtb2RlbOKAmXMgY29lZmZpY2llbnRzIGFuZCBjb21tZW50IG9uIHRoZSBlZmZlY3RzIG9mIHJpZGdlIHJlZ3Jlc3Npb24gdnMuIExBU1NPLiBVc2UgZWFjaAptb2RlbCB0byBtYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldC4gUmVwb3J0IHRoZSB2YWx1ZXMgb2YgdGhlIGluLXNhbXBsZSBSMiBhbmQgdGhlIG91dC1vZi1zYW1wbGUKUjIuIENvbW1lbnQgb24geW91ciByZXN1bHRzLgpgYGB7ciBlY2hvPVRSVUV9CnNldC5zZWVkKDE1MDcxKQpyaWRnZS5maW5hbCA8LSBnbG1uZXQoeF9zYWxhcnkudHJhaW4seV9zYWxhcnkudHJhaW4sYWxwaGE9MCxsYW1iZGE9YmVzdGxhbWJkYXJpZGdlICkKbGFzc28uZmluYWwgPC0gZ2xtbmV0KHhfc2FsYXJ5LnRyYWluLHlfc2FsYXJ5LnRyYWluLGFscGhhPTEsbGFtYmRhPWJlc3RsYW1iZGFsYXNzbykKCnByZWQudHJhaW4ubGFzc28gPC0gcHJlZGljdChsYXNzby5maW5hbCx4X3NhbGFyeS50cmFpbikKcHJlZC50ZXN0Lmxhc3NvIDwtIHByZWRpY3QobGFzc28uZmluYWwseF9zYWxhcnkudGVzdCkKcHJlZC50cmFpbi5yaWRnZSA8LSBwcmVkaWN0KHJpZGdlLmZpbmFsLHhfc2FsYXJ5LnRyYWluKQpwcmVkLnRlc3QucmlkZ2UgPC0gcHJlZGljdChyaWRnZS5maW5hbCx4X3NhbGFyeS50ZXN0KQoKU1NFVHJhaW5MYXNzbz0gc3VtKChwcmVkLnRyYWluLmxhc3NvIC0gdHJhaW4kU2FsYXJ5KV4yKQpTU0VUZXN0TGFzc289IHN1bSgocHJlZC50ZXN0Lmxhc3NvIC0gdGVzdCRTYWxhcnkpXjIpClNTRVRyYWluUmlkZ2U9IHN1bSgocHJlZC50cmFpbi5yaWRnZSAtIHRyYWluJFNhbGFyeSleMikKU1NFVGVzdFJpZGdlPSBzdW0oKHByZWQudGVzdC5yaWRnZSAtIHRlc3QkU2FsYXJ5KV4yKQoKUjIuTGFzc28gPSAxLShTU0VUcmFpbkxhc3NvL1NTVFRyYWluKQpPU1IyLmxhc3NvIDwtIDEtKFNTRVRlc3RMYXNzby9TU1RUZXN0KQpSMi5SaWRnZSA9IDEtKFNTRVRyYWluUmlkZ2UvU1NUVHJhaW4pCk9TUjIuUmlkZ2UgPC0gMS0oU1NFVGVzdFJpZGdlL1NTVFRlc3QpCgpMYXNzb01vZD1kYXRhLmZyYW1lKCJNb2RlbCI9Ikxhc3NvIiwiUjIiPVIyLkxhc3NvLCJPU1IyIj1PU1IyLmxhc3NvKQpSaWRnZU1vZD1kYXRhLmZyYW1lKCJNb2RlbCI9IlJpZGdlIiwiUjIiPVIyLlJpZGdlLCJPU1IyIj1PU1IyLlJpZGdlKQpyYmluZChMTU1vZCxMYXNzb01vZCxSaWRnZU1vZCkKYGBgCj4gVGhlIG1haW4gZGlmZmVybmVjZSBiZXR3ZWVuIHJpZGdlIGFuZCBsYXNzbyBpcyBsYXNzb3MgYWJpbGl0eSB0byBkcml2ZSB0aGUgY29lZmZpY2llbnRzIHRvIDAgd2hlcmUgcmlkZ2UgYnJpbmdzIHRoZW0gdG8gYSB2ZXJ5IHNtYWxsIG51bWJlci4gT2YgdGhlIG1vZGVscywgTGFzc28gcGVyZm9ybXMgb3B0aW1hbGx5IHByb3ZpZGluZyB0aGUgYmVzdCBPU1IyIGFuZCBvbmx5IHNsaWdodGx5IHdvcnNlIFIyLiBMTSBpbmNsdWRlcyBhbGwgdmFyaWFibGVzIHdpdGggYWxvdCBvZiBub2lzZSB3aGljaCBhbGxvd3MgaXQgdG8gZml0IHRoZSB0cmFpbmluZyBkYXRhIGJldHRlciB0aGFuIHRoZSBvdGhlciBtZXRob2RzIGJ1dCBwb29ybHkgb24gdGhlIHRlc3QgZGF0YS4KCgo=