Contents


example 1

1. Boxplot

2. Correlation

3. Basic linear regression

4. ANOVA, pairwise comparison

5. Diagnostic Plots

6. VIF, Predictions, and intervals

\(\times\)

example 2

1. Stepwise Regression

\(\times\)

example 3

1. Boxplots

2. Binomial logistic regression

\(\times\)

example 4

1. Poisson Regression, Offsetting model

2. Chi-Square Signficance Test, Goodness of Fit

3. Dispersion parameter, Wald test

4. Count per area model

5. Wald test

\(\times\)

example 5

1. 10-fold and Leave one out cross validation

2. Mallow’s CP, AIC, BIC

3. Mallow’s CP model

4. Forward/Backward Stepwise

5. Ridge Regression

6. LASSO

7. Elastic Net

8. Predictions



example 1

1. Boxplot gives a visual display of categorical variables.

library(faraway)
library(ggplot2)

data(diabetes)
df=na.omit(diabetes[,c(5,8,10,11,12,17,18)])

ggplot(data=df, aes(x=as.factor(frame),y=ratio, fill=frame))+
  geom_boxplot(alpha=0.3)+
  xlab("frame")+
  ylab("ratio")+
  ggtitle("frame size vs diabetes")+ 
  scale_x_discrete(labels=c("0" = "Dead", "1" = "Alive"))+
  theme(axis.text.x = element_text(face="bold", color="cornflowerblue", size=14),
          axis.text.y = element_text(face="bold", color="royalblue4", 
          size=16, angle=25), legend.position = "none",
          axis.title=element_text(size=17,face="italic"),
          plot.title = element_text(size=20,face="bold.italic"))


2. Two methods are shown for displaying correlation between continuous variables.

library(GGally)
library(corrplot)

x <- cor(df[,c(1,2,3,4,6,7)])
corrplot.mixed(x)


ggpairs(df[,c(1,2,3,4,6,7)],ggplot2::aes(color="salmon"))


3. Basic Linear Regression with summary display.

library(broom)

model <- lm(ratio~.,data=df)
#broom summary with optional parameters
tidy(model,conf.int = TRUE, conf.level = 0.90)

#default summary with additional information
summary(model)

Call:
lm(formula = ratio ~ ., data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.5543 -1.1074 -0.1708  0.7815 13.9703 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)  
(Intercept)  2.1524812  2.2267713   0.967   0.3343  
age          0.0121734  0.0058294   2.088   0.0374 *
height      -0.0006488  0.0271067  -0.024   0.9809  
weight       0.0097284  0.0055837   1.742   0.0823 .
framemedium  0.4857166  0.2152938   2.256   0.0246 *
framelarge   0.3550940  0.2774862   1.280   0.2014  
waist        0.0822015  0.0336228   2.445   0.0150 *
hip         -0.0771822  0.0348566  -2.214   0.0274 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.628 on 375 degrees of freedom
Multiple R-squared:  0.1399,    Adjusted R-squared:  0.1238 
F-statistic: 8.713 on 7 and 375 DF,  p-value: 6.304e-10

4. ANOVA can be used to generate a partial F-score. If you want to compare differences between groups, use Tukey’s post-hoc test as a pairwise comparison. ANOVA can also be used to compare two models and determine if the predictors used in one model are significant and important to keep. Note the difference between using aov() and anova() functions.

anova.model <- aov(ratio~frame,data=df)
summary(anova.model)
             Df Sum Sq Mean Sq F value   Pr(>F)    
frame         2   76.5   38.27   13.47 2.22e-06 ***
Residuals   380 1079.2    2.84                     
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
TukeyHSD(anova.model)
  Tukey multiple comparisons of means
    95% family-wise confidence level

Fit: aov(formula = ratio ~ frame, data = df)

$frame
                  diff        lwr       upr     p adj
medium-small 0.8329398  0.3409425 1.3249372 0.0002398
large-small  1.1882475  0.6288722 1.7476228 0.0000026
large-medium 0.3553077 -0.1382692 0.8488845 0.2088717
#Compare two models
model2 <- lm(ratio~age,data=df)
anova(model,model2)
Analysis of Variance Table

Model 1: ratio ~ age + height + weight + frame + waist + hip
Model 2: ratio ~ age
  Res.Df     RSS Df Sum of Sq      F    Pr(>F)    
1    375  994.04                                  
2    381 1127.62 -6   -133.58 8.3989 1.465e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

5. Diagnostic Plots

library(ggfortify)
library(car)
library(lindia)
#histogram of residuals
gg_reshist(model, bins=30)

#residuals vs fitted
autoplot(model, label.size=3, which=c(1,0))

#Q-Q plot
autoplot(model, label.size=3, which=c(2,0))

#Two models for Cooks distance
autoplot(model, label.size=3, which=c(4,0))


df$cook <- cooks.distance(model) #adds column of cooks distance values to original df
cook_val <- 4/nrow(df) #standard "bad" cook distance is 4/n

ggplot(df, aes(x=as.numeric(rownames(df)),y=cook))+ 
  geom_histogram(stat="identity", color="cornflowerblue",alpha=.3,size=2)+
  ylab("Cook's Distance")+
  theme(axis.text.x = element_text(face="bold", color="royalblue4", size=14),
          axis.text.y = element_text(face="bold", color="royalblue4", 
          size=16, angle=25), legend.position = "none",
          axis.title=element_text(size=17,face="italic"),
          plot.title = element_text(size=20,face="bold.italic"))+
  xlab("")
Ignoring unknown parameters: binwidth, bins, pad

outliers <- which(df$cook > cook_val)
cat("There are ",length(outliers)," outliers detected by Cook's Distance in the model.")
There are  12  outliers detected by Cook's Distance in the model.

6. Predictions and Intervals. The predict() function can be used to make predictions with the model as well as create both prediction and confidence intervals.

pred.data <- data.frame("age"=30,"frame"="medium","height"=66,"weight"=250,"waist"=45,"hip"=55)
prediction <- predict(model, pred.data,interval=c("prediction"))

cat("The model predicts that a woman with the given parameters will have an HDL/Cholesterol ratio of ", prediction[1],".",sep="","\n")
The model predicts that a woman with the given parameters will have an HDL/Cholesterol ratio of 4.846717.
#confidence interval at 90%
confint <- predict(model, pred.data,interval=c("confidence"), level =0.9)
cat("The 90% confidence interval for this model is (",confint[2],", ",confint[3],").",sep="" )
The 90% confidence interval for this model is (4.449433, 5.244001).

example 2

1. Using forward-backward stepwise regression for variable selection. The final selected variables from this example are Day of Week, Scheduled Departure, Destination Airport, and Airline.

df <- read.csv("flights.csv", header=TRUE, sep=",")
model <- lm(DEPARTURE_DELAY~DAY_OF_WEEK+DISTANCE+AIRLINE+DESTINATION_AIRPORT+SCHEDULED_ARRIVAL+SCHEDULED_DEPARTURE,data=df)

empty <- lm(DEPARTURE_DELAY~1,data=df)
mstep <- step(model,scope=list(lower=empty,upper=model),direction = "both")
Start:  AIC=45981.69
DEPARTURE_DELAY ~ DAY_OF_WEEK + DISTANCE + AIRLINE + DESTINATION_AIRPORT + 
    SCHEDULED_ARRIVAL + SCHEDULED_DEPARTURE


Step:  AIC=45981.69
DEPARTURE_DELAY ~ DAY_OF_WEEK + AIRLINE + DESTINATION_AIRPORT + 
    SCHEDULED_ARRIVAL + SCHEDULED_DEPARTURE

                       Df Sum of Sq     RSS   AIC
- SCHEDULED_ARRIVAL     1       525 5212868 45980
<none>                              5212342 45982
- DAY_OF_WEEK           1      3220 5215562 45984
- SCHEDULED_DEPARTURE   1      3434 5215776 45984
- DESTINATION_AIRPORT 155    364978 5577321 46138
- AIRLINE              10    383447 5595789 46450

Step:  AIC=45980.38
DEPARTURE_DELAY ~ DAY_OF_WEEK + AIRLINE + DESTINATION_AIRPORT + 
    SCHEDULED_DEPARTURE

                       Df Sum of Sq     RSS   AIC
<none>                              5212868 45980
+ SCHEDULED_ARRIVAL     1       525 5212342 45982
- DAY_OF_WEEK           1      3115 5215983 45982
- SCHEDULED_DEPARTURE   1      8547 5221415 45990
- DESTINATION_AIRPORT 155    364459 5577327 46136
- AIRLINE              10    383614 5596481 46449

example 3

1. More Boxplots!

df= read.csv("echo.csv", header=TRUE, sep=",")
library(ggplot2)

ggplot(data=df, aes(x=as.factor(still_alive),y=age, fill=still_alive))+
  geom_boxplot(alpha=0.3)+
  xlab("State of Existence")+
  ylab("Age when Heart Attack Occurred")+
  ggtitle("Age x Death")+ 
  scale_x_discrete(labels=c("0" = "Dead", "1" = "Alive"))+
  theme(axis.text.x = element_text(face="bold", color="cornflowerblue", size=14),
          axis.text.y = element_text(face="bold", color="royalblue4", 
          size=16, angle=25), legend.position = "none",
          axis.title=element_text(size=17,face="italic"),
          plot.title = element_text(size=20,face="bold.italic"))

ggplot(data=df, aes(x=as.factor(still_alive),y=fractional_shortening, fill=still_alive))+
  geom_boxplot(alpha=0.3)+
  xlab("Presence of Mortality")+
  ylab("Contracility around heart")+
  ggtitle("fractional shortening x Death")+ 
  scale_x_discrete(labels=c("0" = "Dead", "1" = "Alive"))+
  theme(axis.text.x = element_text(face="bold", color="cornflowerblue", size=14),
          axis.text.y = element_text(face="bold", color="royalblue4", 
          size=16, angle=25), legend.position = "none",
          axis.title=element_text(size=17,face="italic"),
          plot.title = element_text(size=20,face="bold.italic"))


2. Create a binomial general linear model with glm(). Use model summary to determine coefficients.

parameters for \(still\_alive\)
\(intercept\): \(8.54944\) *significant at p<.04
\(age\): \(-0.08994\) *significant at p<.05
\(frac\_short\): \(5.90295\) *significant at p<.05
\(lvdd\): \(-0.65886\) *significant at p<.05
m <- glm(still_alive~age+fractional_shortening+lvdd, family="binomial", data=df)
tidy(m,conf.int = TRUE, conf.level = 0.90)

example 4

1. Create a poisson regression model with the data. Create a second model that offsets the response variable with the log of area. Model parameters are the coefficients to the five prediction variables Area, Elevation, Nearest, Scruz, and Adjacent along with the intercept. Estimates for each of these parameters are given in he ‘Estimate’ column of the model summary.

Poisson regression uses a log transformation on the desired response, so the equation is \(Species = exp(3.155 - 0.0005799*Area + 0.003541*Elevation + 0.008826*Nearest - 0.005709*Scruz - 0.0006630*Adjacent)\)

For the coefficient for Elevation:

  • For every 1m increase in highest elevation, the log expected number of species (or the log rate of species per island) increases by 0.003541 when holding the other variables constant.
  • The ratio of expected number of species per island for a 1m increase in highest elevation is exp(0.003541)=1.0035, holding the other variables constant.
  • For every 1m increase in highest elevation, the expected number of species increases by a factor of exp(0.003541)=1.0035 (i.e. a 0.35% increase) when holding the other variables constant.

For the coefficient for Scruz:

  • For every 1km increase in distance to Santa Cruz, the log expected number of species (or the log rate of species per island) decreases by 0.005709 when holding the other variables constant.
  • The expected ratio of number of species per island for a 1km increase in distance to Santa Cruz is exp(-0.005709)=0.9943, holding the other variables constant.
  • For every 1km increase in distance to Santa Cruz, the expected number of species decreases by a factor of exp(-0.005709)=0.9943 (i.e. a 0.57% decrease) when holding the other variables constant.
require(faraway)
data(gala)
mydata <- gala
df <- mydata[-2]

model1 <- glm(Species ~ .,family=poisson, df)
model2 = glm(Species ~ Elevation+Nearest+Scruz+Adjacent+offset(log(Area)),
             data = mydata, family = poisson)

2. Chi square: To test if the overall model is significant, we use a chi-square test comparing the fitted model to the null model. The p-value is close to 0 indicating the model is significant overall.

1-pchisq((model1$null.dev-model1$deviance), (model1$df.null-model1$df.resid))
[1] 0

3. Goodness of Fit and Dispersion: We perform goodness of fit hypothesis tests using both deviance and Pearson residual, and calculate the dispersion parameter.

P-values from both goodness of fit tests are close to 0, suggesting our model is not a good fit. These findings do not contradict those from Question 2b because it is possible for a model to have predictive ability while still being a poor fit. Further, the dispersion parameter is much larger than 2, so there is overdispersion.

#With deviance residuals
1-pchisq(model1$deviance,model1$df.residual)
[1] 0
#with Pearson residuals
pResid <- resid(model1, type = "pearson")
1-pchisq(sum(pResid^2),model1$df.residual)
[1] 0
#dispersion paramter
model1$deviance/model1$df.res
[1] 29.86857

4. Let’s create a rate based model for the same dataset. Now the response will be density of species (number of species/km^2). So the exposure in this case will be Area. Call this model2.

The number of species is now scaled by the area of the island, so the equation is \(Species/Area = exp(2.155 - 0.002966*Elevation - 0.01674*Nearest - 0.001078*Scruz + 0.0001568*Adjacent)\)

For the coefficient of Adjacent: * For every 1km^2 increase in adjacent island area, the log expected number of species per square kilometer increases by 0.0001568 when holding the other variables constant. * The ratio of the expected number of species per square kilometer with a 1km^2 increase in adjacent island area to the expected number without the increase is exp(0.0001568)=1.000157, holding the other variables constant. * For every 1km^2 increase in area of the adjacent island, the estimated rate of species per square kilometer increases by a factor of exp(0.0001568)=1.000157 (i.e. a 0.0157% increase) when holding the other variables constant.

model2 = glm(Species ~ Elevation+Nearest+Scruz+Adjacent+offset(log(Area)),
             data = mydata, family = poisson)
summary(model2)

Call:
glm(formula = Species ~ Elevation + Nearest + Scruz + Adjacent + 
    offset(log(Area)), family = poisson, data = mydata)

Deviance Residuals: 
     Min        1Q    Median        3Q       Max  
-14.2091   -0.6762    3.4247    6.9750   14.4016  

Coefficients:
              Estimate Std. Error z value Pr(>|z|)    
(Intercept)  2.155e+00  6.222e-02  34.642  < 2e-16 ***
Elevation   -2.966e-03  5.913e-05 -50.153  < 2e-16 ***
Nearest     -1.674e-02  1.553e-03 -10.780  < 2e-16 ***
Scruz       -1.078e-03  7.384e-04  -1.460    0.144    
Adjacent     1.568e-04  2.808e-05   5.582 2.37e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for poisson family taken to be 1)

    Null deviance: 5766.9  on 29  degrees of freedom
Residual deviance: 1565.0  on 25  degrees of freedom
AIC: 1735.9

Number of Fisher Scoring iterations: 6

5. Use wald test to compare model2 with a model containing only Elevation and Scruz to assess if information about nearby islands is significant.

library(aod)
wald.test(b=coef(model2), Sigma=vcov(model2), Terms=c(3,5))
Wald test:
----------

Chi-squared test:
X2 = 188.4, df = 2, P(> X2) = 0.0

example 5

1. Create a general linear model and find 10-fold and leave one out cross validation scores.

require(faraway)
data(debt)
fullData=na.omit(debt)
set.seed(69)
testRows=sample(nrow(fullData),0.1*nrow(fullData))
testData=fullData[testRows, ]
trainData=fullData[-testRows, ]
library(boot)

mod1=lm(prodebt~.,data=trainData)
n=nrow(trainData)
cat("10-fold cross validation score: ",cv.glm(trainData,mod1,K=10)$delta[1],"\nLeave on out cross validation (LOOCV) score: ", cv.glm(trainData,mod1,K=n)$delta[1])
10-fold cross validation score:  NaN 
Leave on out cross validation (LOOCV) score:  NaN

2. Mallow’s CP, AIC, and BIC

library(CombMSC)
cat("Mallow's CP: ",Cp(mod1,S2=summary(mod1)$sigma^2),"\nAIC: ", AIC(mod1,k=2),"\nBIC: ",AIC(mod1,k=log(n)))
Mallow's CP:  13 
AIC:  571.4466 
BIC:  622.0304

3. Create a model using Mallow’s CP. The 1’s and 0’s correspond to whether that variable should be used or not. This tells us that variables 1, 5, 8, 9, 11, and 12 should be used to build the new regression model.

library(leaps)
out = leaps(trainData[,-13], trainData$prodebt, method = "Cp")
#cbind(as.matrix(out$which),out$Cp) #uncomment to display full table of Mallows values

best.model = which(out$Cp==min(out$Cp))
cbind(as.matrix(out$which),out$Cp)[best.model,]
       1        2        3        4        5        6        7        8        9        A        B        C          
1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 1.000000 1.000000 0.000000 1.000000 1.000000 7.054524 
mod2=lm(formula = prodebt ~ incomegp + agegp + manage +
          ccarduse + cigbuy + xmasbuy + locintrn, data = trainData)

4. Forward and Backward stepwise regression

minmod=lm(prodebt~1,data=trainData)
mod3 <- step(minmod, scope = list(lower=minmod,upper=mod1), direction = "forward",trace=F)
backward.step <- step(mod1, scope = list(lower=minmod,upper=mod1), direction = "backward",trace=F)

summary(mod3);summary(backward.step)

Call:
lm(formula = prodebt ~ ccarduse + manage + agegp + locintrn + 
    xmasbuy + incomegp, data = trainData)

Residuals:
     Min       1Q   Median       3Q      Max 
-2.00195 -0.41792  0.03008  0.39156  1.85551 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  3.99454    0.30192  13.230  < 2e-16 ***
ccarduse     0.19906    0.05449   3.653 0.000312 ***
manage      -0.12607    0.04726  -2.667 0.008113 ** 
agegp       -0.13038    0.04433  -2.941 0.003555 ** 
locintrn    -0.15995    0.04749  -3.368 0.000868 ***
xmasbuy      0.25726    0.12677   2.029 0.043411 *  
incomegp     0.06169    0.03256   1.895 0.059229 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.6684 on 267 degrees of freedom
Multiple R-squared:  0.1823,    Adjusted R-squared:  0.164 
F-statistic: 9.923 on 6 and 267 DF,  p-value: 6.902e-10


Call:
lm(formula = prodebt ~ incomegp + agegp + bsocacc + manage + 
    ccarduse + cigbuy + xmasbuy + locintrn, data = trainData)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.99009 -0.45940  0.02515  0.40054  1.85617 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  4.12022    0.31368  13.135  < 2e-16 ***
incomegp     0.05792    0.03289   1.761  0.07942 .  
agegp       -0.13582    0.04447  -3.055  0.00248 ** 
bsocacc     -0.12329    0.08589  -1.435  0.15233    
manage      -0.12558    0.04773  -2.631  0.00900 ** 
ccarduse     0.18638    0.05550   3.358  0.00090 ***
cigbuy      -0.14622    0.09279  -1.576  0.11625    
xmasbuy      0.27425    0.12663   2.166  0.03122 *  
locintrn    -0.15372    0.04746  -3.239  0.00135 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.6659 on 265 degrees of freedom
Multiple R-squared:  0.1945,    Adjusted R-squared:  0.1702 
F-statistic: 7.999 on 8 and 265 DF,  p-value: 1.171e-09

5. Ridge Regression. First, the optimal \(\lambda\) value must selected, which minimzes the generalized cross validation (GCV). This value is then used to find the new values for coefficients. Remember that Ridge regression does not perform variable selection, only shrinkage. This is because it uses the L2 norm for penalization, which does not measure sparsity.

#Scaling
library(MASS)
#lm.ridge automatically scales the predictors, but if you want to do it manually, use `scale()` function as shown below
y.scaled = scale(trainData$prodebt)
X.scaled = scale(as.matrix(trainData[,-13]))

#Ridge Regression
lambda = seq(100, 110, by=0.01)
mod4 = lm.ridge(prodebt~.,data=trainData, lambda = lambda)
optimal <- which(mod4$GCV == min(mod4$GCV))

cat("The GVC score is minimized when lambda is equal to ",names(optimal),", making this optimal lambda value.\n\n",sep="")
The GVC score is minimized when lambda is equal to 100.00, making this optimal lambda value.
cat("Ridge Regression Coefficients at this optimal Lambda Value:\n")
Ridge Regression Coefficients at this optimal Lambda Value:
round(coef(mod4)[which(mod4$GCV == min(mod4$GCV)),],4)
         incomegp    house children  singpar    agegp  bankacc  bsocacc   manage ccarduse   cigbuy  xmasbuy locintrn 
  3.8549   0.0519  -0.0614   0.0281   0.0290  -0.0788   0.0885  -0.0752  -0.0972   0.1313  -0.1178   0.1818  -0.1118 

6. Perform LASSO Regression.

library(glmnet)
#Like lm.ridge, this function automatically scales the predicotrs.

mod5.cv=cv.glmnet(as.matrix(trainData[,-13]),trainData$prodebt,alpha=1,nfolds=10)
mod5 = glmnet(as.matrix(trainData[,-13]),trainData$prodebt, alpha = 1, nlambda = 100)
optimal <-mod5.cv$lambda.min

cat("The optimal lambda value is ",optimal,".\n\n",sep="")
The optimal lambda value is 0.007221254.
cat("The variables selected by the LASSO model are shown in the table below:\n")
The variables selected by the LASSO model are shown in the table below:
coef(mod5,s=mod5.cv$lambda.min)
13 x 1 sparse Matrix of class "dgCMatrix"
                      1
(Intercept)  4.08479288
incomegp     0.05419295
house       -0.06823412
children     0.02079927
singpar      .         
agegp       -0.10246998
bankacc      0.08440017
bsocacc     -0.08711751
manage      -0.11882583
ccarduse     0.17326417
cigbuy      -0.14058391
xmasbuy      0.23555527
locintrn    -0.14950893
cat("\nWe can also plot the regression coefficient path to get an understanding of how values were selected for each coefficient:\n")

We can also plot the regression coefficient path to get an understanding of how values were selected for each coefficient:
plot(mod5,xvar="lambda",label=TRUE,lwd=2)
abline(v=log(mod5.cv$lambda.min),col='black',lty = 2,lwd=2)


7. Elastic Net Regression is performed using 100 values for \(\lambda\) and giving equal weight to both penalties. 10-fold cross validation is used to find the optimal \(\lambda\).

#Again, predictors are scaled automatically. 
mod6.cv=cv.glmnet(as.matrix(trainData[,-13]),trainData$prodebt,alpha=0.5,nfolds=10)
mod6 = glmnet(as.matrix(trainData[,-13]), trainData$prodebt, alpha = 0.5, nlambda = 100)
optimal <- mod6.cv$lambda.min
cat("The optimal lambda value is ",optimal,".\n\n",sep="")
The optimal lambda value is 0.02095361.
cat("The variables selected by the LASSO model are shown in the table below:\n")
The variables selected by the LASSO model are shown in the table below:
coef(mod6,s=mod6.cv$lambda.min)
13 x 1 sparse Matrix of class "dgCMatrix"
                      1
(Intercept)  4.04248407
incomegp     0.05326085
house       -0.06192981
children     0.01974651
singpar      .         
agegp       -0.09955276
bankacc      0.07582673
bsocacc     -0.08018302
manage      -0.11499109
ccarduse     0.16811282
cigbuy      -0.12969537
xmasbuy      0.22113558
locintrn    -0.14251235

8. Predict prodebt for each of the rows in the test data using the full model, lowest Mallow’s Cp model, and the models found using forward stepwise regression, ridge regression, lasso regression, and elastic net.

full=predict(mod1,testData)
mincp=predict(mod2,testData)
stepwise=predict(mod3,testData)
ridge=cbind(1,as.matrix(testData[,-13]))%*%coef(mod4)[which(mod4$GCV == min(mod4$GCV)),]
modlasso=lm(prodebt~.,data=trainData[,-4])
lasso=predict(modlasso,testData)
elastic=as.vector(predict(mod6,as.matrix(testData[,-13]),s=mod6.cv$lambda.min))

preds=data.frame(prodebt=testData$prodebt,full,mincp,stepwise,ridge,lasso,elastic)
preds

Predictions are compared below using mean squared prediction error. The lowest MSPE indicates the “best” model, but these are all so close that it would be difficult to make that determination with a single metric.

sapply(preds[,-1],function(x){mean((x-testData$prodebt)^2)})
     full     mincp  stepwise     ridge     lasso   elastic 
0.3017985 0.2967393 0.3019744 0.2937757 0.3017978 0.2951887 

Additional Notes

Poisson is mainly used to address count data across continuous events rather than a discrete events. For example - How many student pass a test is a discrete event. where each student have a probability of passing a test when they take it. How many awards does a student get in high school is much more continuous, or how many people are in a line in the airport.

LS0tDQp0aXRsZTogIlIgSGVscGJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyMgX0NvbnRlbnRzXyAjIyMNCg0KLS0tLS0NCg0KIyMjIyMgX2V4YW1wbGUgMV8gIyMjIyMNCiMjIyMgMS4gQm94cGxvdA0KIyMjIyAyLiBDb3JyZWxhdGlvbg0KIyMjIyAzLiBCYXNpYyBsaW5lYXIgcmVncmVzc2lvbg0KIyMjIyA0LiBBTk9WQSwgcGFpcndpc2UgY29tcGFyaXNvbiANCiMjIyMgNS4gRGlhZ25vc3RpYyBQbG90cw0KIyMjIyA2LiBWSUYsIFByZWRpY3Rpb25zLCBhbmQgaW50ZXJ2YWxzDQoNCiRcdGltZXMkDQoNCiMjIyMjIF9leGFtcGxlIDJfICMjIyMjDQojIyMjIDEuIFN0ZXB3aXNlIFJlZ3Jlc3Npb24NCg0KJFx0aW1lcyQNCg0KIyMjIyMgX2V4YW1wbGUgM18gIyMjIyMNCiMjIyMgMS4gQm94cGxvdHMNCiMjIyMgMi4gQmlub21pYWwgbG9naXN0aWMgcmVncmVzc2lvbg0KDQokXHRpbWVzJA0KDQojIyMjIyBfZXhhbXBsZSA0XyAjIyMjIw0KIyMjIyAxLiBQb2lzc29uIFJlZ3Jlc3Npb24sIE9mZnNldHRpbmcgbW9kZWwNCiMjIyMgMi4gQ2hpLVNxdWFyZSBTaWduZmljYW5jZSBUZXN0LCBHb29kbmVzcyBvZiBGaXQNCiMjIyMgMy4gRGlzcGVyc2lvbiBwYXJhbWV0ZXIsIFdhbGQgdGVzdA0KIyMjIyA0LiBDb3VudCBwZXIgYXJlYSBtb2RlbA0KIyMjIyA1LiBXYWxkIHRlc3QNCg0KJFx0aW1lcyQNCg0KIyMjIyMgX2V4YW1wbGUgNV8gIyMjIyMNCiMjIyMgMS4gMTAtZm9sZCBhbmQgTGVhdmUgb25lIG91dCBjcm9zcyB2YWxpZGF0aW9uDQojIyMjIDIuIE1hbGxvdydzIENQLCBBSUMsIEJJQw0KIyMjIyAzLiBNYWxsb3cncyBDUCBtb2RlbA0KIyMjIyA0LiBGb3J3YXJkL0JhY2t3YXJkIFN0ZXB3aXNlDQojIyMjIDUuIFJpZGdlIFJlZ3Jlc3Npb24NCiMjIyMgNi4gTEFTU08NCiMjIyMgNy4gRWxhc3RpYyBOZXQNCiMjIyMgOC4gUHJlZGljdGlvbnMNCioqKg0KLS0tDQoNCiMjIyBfZXhhbXBsZSAxXyANCiMjIyMgMS4gQm94cGxvdCBnaXZlcyBhIHZpc3VhbCBkaXNwbGF5IG9mIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gDQpgYGB7cn0NCmxpYnJhcnkoZmFyYXdheSkNCmxpYnJhcnkoZ2dwbG90MikNCg0KZGF0YShkaWFiZXRlcykNCmRmPW5hLm9taXQoZGlhYmV0ZXNbLGMoNSw4LDEwLDExLDEyLDE3LDE4KV0pDQoNCmdncGxvdChkYXRhPWRmLCBhZXMoeD1hcy5mYWN0b3IoZnJhbWUpLHk9cmF0aW8sIGZpbGw9ZnJhbWUpKSsNCiAgZ2VvbV9ib3hwbG90KGFscGhhPTAuMykrDQogIHhsYWIoImZyYW1lIikrDQogIHlsYWIoInJhdGlvIikrDQogIGdndGl0bGUoImZyYW1lIHNpemUgdnMgZGlhYmV0ZXMiKSsgDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIjAiID0gIkRlYWQiLCAiMSIgPSAiQWxpdmUiKSkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBjb2xvcj0iY29ybmZsb3dlcmJsdWUiLCBzaXplPTE0KSwNCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIiwgY29sb3I9InJveWFsYmx1ZTQiLCANCiAgICAgICAgICBzaXplPTE2LCBhbmdsZT0yNSksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE3LGZhY2U9Iml0YWxpYyIpLA0KICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCxmYWNlPSJib2xkLml0YWxpYyIpKQ0KYGBgDQoqKioNCg0KIyMjIyAyLiBUd28gbWV0aG9kcyBhcmUgc2hvd24gZm9yIGRpc3BsYXlpbmcgY29ycmVsYXRpb24gYmV0d2VlbiBjb250aW51b3VzIHZhcmlhYmxlcy4gDQpgYGB7cn0NCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShjb3JycGxvdCkNCg0KeCA8LSBjb3IoZGZbLGMoMSwyLDMsNCw2LDcpXSkNCmNvcnJwbG90Lm1peGVkKHgpDQoNCmdncGFpcnMoZGZbLGMoMSwyLDMsNCw2LDcpXSxnZ3Bsb3QyOjphZXMoY29sb3I9InNhbG1vbiIpKQ0KDQpgYGANCioqKg0KDQojIyMjIDMuIEJhc2ljIExpbmVhciBSZWdyZXNzaW9uIHdpdGggc3VtbWFyeSBkaXNwbGF5LiANCmBgYHtyfQ0KbGlicmFyeShicm9vbSkNCg0KbW9kZWwgPC0gbG0ocmF0aW9+LixkYXRhPWRmKQ0KI2Jyb29tIHN1bW1hcnkgd2l0aCBvcHRpb25hbCBwYXJhbWV0ZXJzDQp0aWR5KG1vZGVsLGNvbmYuaW50ID0gVFJVRSwgY29uZi5sZXZlbCA9IDAuOTApDQoNCiNkZWZhdWx0IHN1bW1hcnkgd2l0aCBhZGRpdGlvbmFsIGluZm9ybWF0aW9uDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQoqKioNCg0KIyMjIyA0LiBBTk9WQSBjYW4gYmUgdXNlZCB0byBnZW5lcmF0ZSBhIHBhcnRpYWwgRi1zY29yZS4gSWYgeW91IHdhbnQgdG8gY29tcGFyZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGdyb3VwcywgdXNlIFR1a2V5J3MgcG9zdC1ob2MgdGVzdCBhcyBhIHBhaXJ3aXNlIGNvbXBhcmlzb24uIEFOT1ZBIGNhbiBhbHNvIGJlIHVzZWQgdG8gY29tcGFyZSB0d28gbW9kZWxzIGFuZCBkZXRlcm1pbmUgaWYgdGhlIHByZWRpY3RvcnMgdXNlZCBpbiBvbmUgbW9kZWwgYXJlIHNpZ25pZmljYW50IGFuZCBpbXBvcnRhbnQgdG8ga2VlcC4gTm90ZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHVzaW5nIGBhb3YoKWAgYW5kIGBhbm92YSgpYCBmdW5jdGlvbnMuIA0KYGBge3J9DQphbm92YS5tb2RlbCA8LSBhb3YocmF0aW9+ZnJhbWUsZGF0YT1kZikNCnN1bW1hcnkoYW5vdmEubW9kZWwpDQpUdWtleUhTRChhbm92YS5tb2RlbCkNCiNDb21wYXJlIHR3byBtb2RlbHMNCm1vZGVsMiA8LSBsbShyYXRpb35hZ2UsZGF0YT1kZikNCmFub3ZhKG1vZGVsLG1vZGVsMikNCmBgYA0KKioqDQoNCiMjIyMgNS4gRGlhZ25vc3RpYyBQbG90cw0KYGBge3J9DQpsaWJyYXJ5KGdnZm9ydGlmeSkNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShsaW5kaWEpDQojaGlzdG9ncmFtIG9mIHJlc2lkdWFscw0KZ2dfcmVzaGlzdChtb2RlbCwgYmlucz0zMCkNCiNyZXNpZHVhbHMgdnMgZml0dGVkDQphdXRvcGxvdChtb2RlbCwgbGFiZWwuc2l6ZT0zLCB3aGljaD1jKDEsMCkpDQojUS1RIHBsb3QNCmF1dG9wbG90KG1vZGVsLCBsYWJlbC5zaXplPTMsIHdoaWNoPWMoMiwwKSkNCiNUd28gbW9kZWxzIGZvciBDb29rcyBkaXN0YW5jZQ0KYXV0b3Bsb3QobW9kZWwsIGxhYmVsLnNpemU9Mywgd2hpY2g9Yyg0LDApKQ0KDQpkZiRjb29rIDwtIGNvb2tzLmRpc3RhbmNlKG1vZGVsKSAjYWRkcyBjb2x1bW4gb2YgY29va3MgZGlzdGFuY2UgdmFsdWVzIHRvIG9yaWdpbmFsIGRmDQpjb29rX3ZhbCA8LSA0L25yb3coZGYpICNzdGFuZGFyZCAiYmFkIiBjb29rIGRpc3RhbmNlIGlzIDQvbg0KDQpnZ3Bsb3QoZGYsIGFlcyh4PWFzLm51bWVyaWMocm93bmFtZXMoZGYpKSx5PWNvb2spKSsgDQogIGdlb21faGlzdG9ncmFtKHN0YXQ9ImlkZW50aXR5IiwgY29sb3I9ImNvcm5mbG93ZXJibHVlIixhbHBoYT0uMyxzaXplPTIpKw0KICB5bGFiKCJDb29rJ3MgRGlzdGFuY2UiKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZT0iYm9sZCIsIGNvbG9yPSJyb3lhbGJsdWU0Iiwgc2l6ZT0xNCksDQogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFjZT0iYm9sZCIsIGNvbG9yPSJyb3lhbGJsdWU0IiwgDQogICAgICAgICAgc2l6ZT0xNiwgYW5nbGU9MjUpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNyxmYWNlPSJpdGFsaWMiKSwNCiAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjAsZmFjZT0iYm9sZC5pdGFsaWMiKSkrDQogIHhsYWIoIiIpDQoNCm91dGxpZXJzIDwtIHdoaWNoKGRmJGNvb2sgPiBjb29rX3ZhbCkNCmNhdCgiVGhlcmUgYXJlICIsbGVuZ3RoKG91dGxpZXJzKSwiIG91dGxpZXJzIGRldGVjdGVkIGJ5IENvb2sncyBEaXN0YW5jZSBpbiB0aGUgbW9kZWwuIikNCmBgYA0KKioqDQoNCiMjIyMgNi4gUHJlZGljdGlvbnMgYW5kIEludGVydmFscy4gVGhlIGBwcmVkaWN0KClgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIG1ha2UgcHJlZGljdGlvbnMgd2l0aCB0aGUgbW9kZWwgYXMgd2VsbCBhcyBjcmVhdGUgYm90aCBwcmVkaWN0aW9uIGFuZCBjb25maWRlbmNlIGludGVydmFscy4gDQoNCmBgYHtyfQ0KcHJlZC5kYXRhIDwtIGRhdGEuZnJhbWUoImFnZSI9MzAsImZyYW1lIj0ibWVkaXVtIiwiaGVpZ2h0Ij02Niwid2VpZ2h0Ij0yNTAsIndhaXN0Ij00NSwiaGlwIj01NSkNCnByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbCwgcHJlZC5kYXRhLGludGVydmFsPWMoInByZWRpY3Rpb24iKSkNCg0KY2F0KCJUaGUgbW9kZWwgcHJlZGljdHMgdGhhdCBhIHdvbWFuIHdpdGggdGhlIGdpdmVuIHBhcmFtZXRlcnMgd2lsbCBoYXZlIGFuIEhETC9DaG9sZXN0ZXJvbCByYXRpbyBvZiAiLCBwcmVkaWN0aW9uWzFdLCIuIixzZXA9IiIsIlxuIikNCg0KI2NvbmZpZGVuY2UgaW50ZXJ2YWwgYXQgOTAlDQpjb25maW50IDwtIHByZWRpY3QobW9kZWwsIHByZWQuZGF0YSxpbnRlcnZhbD1jKCJjb25maWRlbmNlIiksIGxldmVsID0wLjkpDQpjYXQoIlRoZSA5MCUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhpcyBtb2RlbCBpcyAoIixjb25maW50WzJdLCIsICIsY29uZmludFszXSwiKS4iLHNlcD0iIiApDQoNCmBgYA0KKioqDQojIyMgX2V4YW1wbGUgMl8gIyMjDQojIyMjIDEuIFVzaW5nIGZvcndhcmQtYmFja3dhcmQgc3RlcHdpc2UgcmVncmVzc2lvbiBmb3IgdmFyaWFibGUgc2VsZWN0aW9uLiBUaGUgZmluYWwgc2VsZWN0ZWQgdmFyaWFibGVzIGZyb20gdGhpcyBleGFtcGxlIGFyZSBEYXkgb2YgV2VlaywgU2NoZWR1bGVkIERlcGFydHVyZSwgRGVzdGluYXRpb24gQWlycG9ydCwgYW5kIEFpcmxpbmUuDQpgYGB7cn0NCmRmIDwtIHJlYWQuY3N2KCJmbGlnaHRzLmNzdiIsIGhlYWRlcj1UUlVFLCBzZXA9IiwiKQ0KbW9kZWwgPC0gbG0oREVQQVJUVVJFX0RFTEFZfkRBWV9PRl9XRUVLK0RJU1RBTkNFK0FJUkxJTkUrREVTVElOQVRJT05fQUlSUE9SVCtTQ0hFRFVMRURfQVJSSVZBTCtTQ0hFRFVMRURfREVQQVJUVVJFLGRhdGE9ZGYpDQoNCmVtcHR5IDwtIGxtKERFUEFSVFVSRV9ERUxBWX4xLGRhdGE9ZGYpDQptc3RlcCA8LSBzdGVwKG1vZGVsLHNjb3BlPWxpc3QobG93ZXI9ZW1wdHksdXBwZXI9bW9kZWwpLGRpcmVjdGlvbiA9ICJib3RoIikNCmBgYA0KKioqDQojIyMgX2V4YW1wbGUgM18gIyMjDQoNCiMjIyMgMS4gTW9yZSBCb3hwbG90cyENCmBgYHtyfQ0KZGY9IHJlYWQuY3N2KCJlY2hvLmNzdiIsIGhlYWRlcj1UUlVFLCBzZXA9IiwiKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnZ3Bsb3QoZGF0YT1kZiwgYWVzKHg9YXMuZmFjdG9yKHN0aWxsX2FsaXZlKSx5PWFnZSwgZmlsbD1zdGlsbF9hbGl2ZSkpKw0KICBnZW9tX2JveHBsb3QoYWxwaGE9MC4zKSsNCiAgeGxhYigiU3RhdGUgb2YgRXhpc3RlbmNlIikrDQogIHlsYWIoIkFnZSB3aGVuIEhlYXJ0IEF0dGFjayBPY2N1cnJlZCIpKw0KICBnZ3RpdGxlKCJBZ2UgeCBEZWF0aCIpKyANCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiMCIgPSAiRGVhZCIsICIxIiA9ICJBbGl2ZSIpKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZT0iYm9sZCIsIGNvbG9yPSJjb3JuZmxvd2VyYmx1ZSIsIHNpemU9MTQpLA0KICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBjb2xvcj0icm95YWxibHVlNCIsIA0KICAgICAgICAgIHNpemU9MTYsIGFuZ2xlPTI1KSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTcsZmFjZT0iaXRhbGljIiksDQogICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIwLGZhY2U9ImJvbGQuaXRhbGljIikpDQpnZ3Bsb3QoZGF0YT1kZiwgYWVzKHg9YXMuZmFjdG9yKHN0aWxsX2FsaXZlKSx5PWZyYWN0aW9uYWxfc2hvcnRlbmluZywgZmlsbD1zdGlsbF9hbGl2ZSkpKw0KICBnZW9tX2JveHBsb3QoYWxwaGE9MC4zKSsNCiAgeGxhYigiUHJlc2VuY2Ugb2YgTW9ydGFsaXR5IikrDQogIHlsYWIoIkNvbnRyYWNpbGl0eSBhcm91bmQgaGVhcnQiKSsNCiAgZ2d0aXRsZSgiZnJhY3Rpb25hbCBzaG9ydGVuaW5nIHggRGVhdGgiKSsgDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIjAiID0gIkRlYWQiLCAiMSIgPSAiQWxpdmUiKSkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBjb2xvcj0iY29ybmZsb3dlcmJsdWUiLCBzaXplPTE0KSwNCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIiwgY29sb3I9InJveWFsYmx1ZTQiLCANCiAgICAgICAgICBzaXplPTE2LCBhbmdsZT0yNSksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE3LGZhY2U9Iml0YWxpYyIpLA0KICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCxmYWNlPSJib2xkLml0YWxpYyIpKQ0KDQpgYGANCioqKg0KDQojIyMjIDIuIENyZWF0ZSBhIGJpbm9taWFsIGdlbmVyYWwgbGluZWFyIG1vZGVsIHdpdGggYGdsbSgpYC4gVXNlIG1vZGVsIHN1bW1hcnkgdG8gZGV0ZXJtaW5lIGNvZWZmaWNpZW50cy4gDQoNCnxwYXJhbWV0ZXJzIGZvciAkc3RpbGxcX2FsaXZlJHwNCnwtLS0tLS0tLS0tLS0tLS18DQp8JGludGVyY2VwdCQ6ICQ4LjU0OTQ0JCAqc2lnbmlmaWNhbnQgYXQgcDwuMDR8DQp8JGFnZSQ6ICQtMC4wODk5NCQgKnNpZ25pZmljYW50IGF0IHA8LjA1fA0KJGZyYWNcX3Nob3J0JDogJDUuOTAyOTUkICpzaWduaWZpY2FudCBhdCBwPC4wNXwNCiRsdmRkJDogJC0wLjY1ODg2JCAqc2lnbmlmaWNhbnQgYXQgcDwuMDV8DQoNCmBgYHtyfQ0KbSA8LSBnbG0oc3RpbGxfYWxpdmV+YWdlK2ZyYWN0aW9uYWxfc2hvcnRlbmluZytsdmRkLCBmYW1pbHk9ImJpbm9taWFsIiwgZGF0YT1kZikNCnRpZHkobSxjb25mLmludCA9IFRSVUUsIGNvbmYubGV2ZWwgPSAwLjkwKQ0KYGBgDQoNCioqKg0KIyMjIF9leGFtcGxlIDRfICMjIw0KDQojIyMjIDEuIENyZWF0ZSBhIHBvaXNzb24gcmVncmVzc2lvbiBtb2RlbCB3aXRoIHRoZSBkYXRhLiBDcmVhdGUgYSBzZWNvbmQgbW9kZWwgdGhhdCBvZmZzZXRzIHRoZSByZXNwb25zZSB2YXJpYWJsZSB3aXRoIHRoZSBsb2cgb2YgYXJlYS4gTW9kZWwgcGFyYW1ldGVycyBhcmUgdGhlIGNvZWZmaWNpZW50cyB0byB0aGUgZml2ZSBwcmVkaWN0aW9uIHZhcmlhYmxlcyAqQXJlYSosICpFbGV2YXRpb24qLCAqTmVhcmVzdCosICpTY3J1eiosIGFuZCAqQWRqYWNlbnQqIGFsb25nIHdpdGggdGhlIGludGVyY2VwdC4gRXN0aW1hdGVzIGZvciBlYWNoIG9mIHRoZXNlIHBhcmFtZXRlcnMgYXJlIGdpdmVuIGluIGhlICdFc3RpbWF0ZScgY29sdW1uIG9mIHRoZSBtb2RlbCBzdW1tYXJ5Lg0KDQpQb2lzc29uIHJlZ3Jlc3Npb24gdXNlcyBhIGxvZyB0cmFuc2Zvcm1hdGlvbiBvbiB0aGUgZGVzaXJlZCByZXNwb25zZSwgc28gdGhlIGVxdWF0aW9uIGlzICRTcGVjaWVzID0gZXhwKDMuMTU1IC0gMC4wMDA1Nzk5KkFyZWEgKyAwLjAwMzU0MSpFbGV2YXRpb24gKyAwLjAwODgyNipOZWFyZXN0IC0gMC4wMDU3MDkqU2NydXogLSAwLjAwMDY2MzAqQWRqYWNlbnQpJA0KDQpGb3IgdGhlIGNvZWZmaWNpZW50IGZvciAqRWxldmF0aW9uKjoNCg0KKiBGb3IgZXZlcnkgMW0gaW5jcmVhc2UgaW4gaGlnaGVzdCBlbGV2YXRpb24sIHRoZSBsb2cgZXhwZWN0ZWQgbnVtYmVyIG9mIHNwZWNpZXMgKG9yIHRoZSBsb2cgcmF0ZSBvZiBzcGVjaWVzIHBlciBpc2xhbmQpIGluY3JlYXNlcyBieSAwLjAwMzU0MSB3aGVuIGhvbGRpbmcgdGhlIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudC4gDQoqIFRoZSByYXRpbyBvZiBleHBlY3RlZCBudW1iZXIgb2Ygc3BlY2llcyBwZXIgaXNsYW5kIGZvciBhIDFtIGluY3JlYXNlIGluIGhpZ2hlc3QgZWxldmF0aW9uIGlzIGV4cCgwLjAwMzU0MSk9MS4wMDM1LCBob2xkaW5nIHRoZSBvdGhlciB2YXJpYWJsZXMgY29uc3RhbnQuDQoqIEZvciBldmVyeSAxbSBpbmNyZWFzZSBpbiBoaWdoZXN0IGVsZXZhdGlvbiwgdGhlIGV4cGVjdGVkIG51bWJlciBvZiBzcGVjaWVzIGluY3JlYXNlcyBieSBhIGZhY3RvciBvZiBleHAoMC4wMDM1NDEpPTEuMDAzNSAoaS5lLiBhIDAuMzUlIGluY3JlYXNlKSB3aGVuIGhvbGRpbmcgdGhlIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudC4NCg0KRm9yIHRoZSBjb2VmZmljaWVudCBmb3IgKlNjcnV6KjoNCg0KKiBGb3IgZXZlcnkgMWttIGluY3JlYXNlIGluIGRpc3RhbmNlIHRvIFNhbnRhIENydXosIHRoZSBsb2cgZXhwZWN0ZWQgbnVtYmVyIG9mIHNwZWNpZXMgKG9yIHRoZSBsb2cgcmF0ZSBvZiBzcGVjaWVzIHBlciBpc2xhbmQpIGRlY3JlYXNlcyBieSAwLjAwNTcwOSB3aGVuIGhvbGRpbmcgdGhlIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudC4gDQoqIFRoZSBleHBlY3RlZCByYXRpbyBvZiBudW1iZXIgb2Ygc3BlY2llcyBwZXIgaXNsYW5kIGZvciBhIDFrbSBpbmNyZWFzZSBpbiBkaXN0YW5jZSB0byBTYW50YSBDcnV6IGlzIGV4cCgtMC4wMDU3MDkpPTAuOTk0MywgaG9sZGluZyB0aGUgb3RoZXIgdmFyaWFibGVzIGNvbnN0YW50Lg0KKiBGb3IgZXZlcnkgMWttIGluY3JlYXNlIGluIGRpc3RhbmNlIHRvIFNhbnRhIENydXosIHRoZSBleHBlY3RlZCBudW1iZXIgb2Ygc3BlY2llcyBkZWNyZWFzZXMgYnkgYSBmYWN0b3Igb2YgZXhwKC0wLjAwNTcwOSk9MC45OTQzIChpLmUuIGEgMC41NyUgZGVjcmVhc2UpIHdoZW4gaG9sZGluZyB0aGUgb3RoZXIgdmFyaWFibGVzIGNvbnN0YW50Lg0KDQpgYGB7ciwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQpyZXF1aXJlKGZhcmF3YXkpDQpkYXRhKGdhbGEpDQpteWRhdGEgPC0gZ2FsYQ0KZGYgPC0gbXlkYXRhWy0yXQ0KDQptb2RlbDEgPC0gZ2xtKFNwZWNpZXMgfiAuLGZhbWlseT1wb2lzc29uLCBkZikNCm1vZGVsMiA9IGdsbShTcGVjaWVzIH4gRWxldmF0aW9uK05lYXJlc3QrU2NydXorQWRqYWNlbnQrb2Zmc2V0KGxvZyhBcmVhKSksDQogICAgICAgICAgICAgZGF0YSA9IG15ZGF0YSwgZmFtaWx5ID0gcG9pc3NvbikNCg0KYGBgDQoqKioNCg0KIyMjIyAyLiBDaGkgc3F1YXJlOiBUbyB0ZXN0IGlmIHRoZSBvdmVyYWxsIG1vZGVsIGlzIHNpZ25pZmljYW50LCB3ZSB1c2UgYSBjaGktc3F1YXJlIHRlc3QgY29tcGFyaW5nIHRoZSBmaXR0ZWQgbW9kZWwgdG8gdGhlIG51bGwgbW9kZWwuIFRoZSBwLXZhbHVlIGlzIGNsb3NlIHRvIDAgaW5kaWNhdGluZyB0aGUgbW9kZWwgaXMgc2lnbmlmaWNhbnQgb3ZlcmFsbC4NCmBgYHtyfQ0KMS1wY2hpc3EoKG1vZGVsMSRudWxsLmRldi1tb2RlbDEkZGV2aWFuY2UpLCAobW9kZWwxJGRmLm51bGwtbW9kZWwxJGRmLnJlc2lkKSkNCmBgYA0KKioqDQoNCiMjIyMgMy4gR29vZG5lc3Mgb2YgRml0IGFuZCBEaXNwZXJzaW9uOiBXZSBwZXJmb3JtIGdvb2RuZXNzIG9mIGZpdCBoeXBvdGhlc2lzIHRlc3RzIHVzaW5nIGJvdGggZGV2aWFuY2UgYW5kIFBlYXJzb24gcmVzaWR1YWwsIGFuZCBjYWxjdWxhdGUgdGhlIGRpc3BlcnNpb24gcGFyYW1ldGVyLiANClAtdmFsdWVzIGZyb20gYm90aCBnb29kbmVzcyBvZiBmaXQgdGVzdHMgYXJlIGNsb3NlIHRvIDAsIHN1Z2dlc3Rpbmcgb3VyIG1vZGVsIGlzIG5vdCBhIGdvb2QgZml0LiBUaGVzZSBmaW5kaW5ncyBkbyBub3QgY29udHJhZGljdCB0aG9zZSBmcm9tIFF1ZXN0aW9uIDJiIGJlY2F1c2UgaXQgaXMgcG9zc2libGUgZm9yIGEgbW9kZWwgdG8gaGF2ZSBwcmVkaWN0aXZlIGFiaWxpdHkgd2hpbGUgc3RpbGwgYmVpbmcgYSBwb29yIGZpdC4gRnVydGhlciwgdGhlIGRpc3BlcnNpb24gcGFyYW1ldGVyIGlzIG11Y2ggbGFyZ2VyIHRoYW4gMiwgc28gdGhlcmUgaXMgb3ZlcmRpc3BlcnNpb24uDQpgYGB7ciwgcmVzdWx0cz0naG9sZCd9DQojV2l0aCBkZXZpYW5jZSByZXNpZHVhbHMNCjEtcGNoaXNxKG1vZGVsMSRkZXZpYW5jZSxtb2RlbDEkZGYucmVzaWR1YWwpDQojd2l0aCBQZWFyc29uIHJlc2lkdWFscw0KcFJlc2lkIDwtIHJlc2lkKG1vZGVsMSwgdHlwZSA9ICJwZWFyc29uIikNCjEtcGNoaXNxKHN1bShwUmVzaWReMiksbW9kZWwxJGRmLnJlc2lkdWFsKQ0KDQojZGlzcGVyc2lvbiBwYXJhbXRlcg0KbW9kZWwxJGRldmlhbmNlL21vZGVsMSRkZi5yZXMNCmBgYA0KDQoqKioNCg0KIyMjIyA0LiBMZXQncyBjcmVhdGUgYSByYXRlIGJhc2VkIG1vZGVsIGZvciB0aGUgc2FtZSBkYXRhc2V0LiBOb3cgdGhlIHJlc3BvbnNlIHdpbGwgYmUgZGVuc2l0eSBvZiBzcGVjaWVzIChudW1iZXIgb2Ygc3BlY2llcy9rbV4yKS4gU28gdGhlIGV4cG9zdXJlIGluIHRoaXMgY2FzZSB3aWxsIGJlIEFyZWEuIENhbGwgdGhpcyAqKm1vZGVsMioqLg0KDQpUaGUgbnVtYmVyIG9mIHNwZWNpZXMgaXMgbm93IHNjYWxlZCBieSB0aGUgYXJlYSBvZiB0aGUgaXNsYW5kLCBzbyB0aGUgZXF1YXRpb24gaXMgJFNwZWNpZXMvQXJlYSA9IGV4cCgyLjE1NSAtIDAuMDAyOTY2KkVsZXZhdGlvbiAtIDAuMDE2NzQqTmVhcmVzdCAtIDAuMDAxMDc4KlNjcnV6ICsgMC4wMDAxNTY4KkFkamFjZW50KSQNCg0KDQpGb3IgdGhlIGNvZWZmaWNpZW50IG9mICpBZGphY2VudCo6DQoqIEZvciBldmVyeSAxa21eMiBpbmNyZWFzZSBpbiBhZGphY2VudCBpc2xhbmQgYXJlYSwgdGhlIGxvZyBleHBlY3RlZCBudW1iZXIgb2Ygc3BlY2llcyBwZXIgc3F1YXJlIGtpbG9tZXRlciBpbmNyZWFzZXMgYnkgMC4wMDAxNTY4IHdoZW4gaG9sZGluZyB0aGUgb3RoZXIgdmFyaWFibGVzIGNvbnN0YW50LiANCiogVGhlIHJhdGlvIG9mIHRoZSBleHBlY3RlZCBudW1iZXIgb2Ygc3BlY2llcyBwZXIgc3F1YXJlIGtpbG9tZXRlciB3aXRoIGEgMWttXjIgaW5jcmVhc2UgaW4gYWRqYWNlbnQgaXNsYW5kIGFyZWEgdG8gdGhlIGV4cGVjdGVkIG51bWJlciB3aXRob3V0IHRoZSBpbmNyZWFzZSBpcyBleHAoMC4wMDAxNTY4KT0xLjAwMDE1NywgaG9sZGluZyB0aGUgb3RoZXIgdmFyaWFibGVzIGNvbnN0YW50Lg0KKiBGb3IgZXZlcnkgMWttXjIgaW5jcmVhc2UgaW4gYXJlYSBvZiB0aGUgYWRqYWNlbnQgaXNsYW5kLCB0aGUgZXN0aW1hdGVkIHJhdGUgb2Ygc3BlY2llcyBwZXIgc3F1YXJlIGtpbG9tZXRlciBpbmNyZWFzZXMgYnkgYSBmYWN0b3Igb2YgZXhwKDAuMDAwMTU2OCk9MS4wMDAxNTcgKGkuZS4gYSAwLjAxNTclIGluY3JlYXNlKSB3aGVuIGhvbGRpbmcgdGhlIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudC4NCmBgYHtyfQ0KbW9kZWwyID0gZ2xtKFNwZWNpZXMgfiBFbGV2YXRpb24rTmVhcmVzdCtTY3J1eitBZGphY2VudCtvZmZzZXQobG9nKEFyZWEpKSwNCiAgICAgICAgICAgICBkYXRhID0gbXlkYXRhLCBmYW1pbHkgPSBwb2lzc29uKQ0Kc3VtbWFyeShtb2RlbDIpDQpgYGANCg0KKioqDQojIyMjIDUuIFVzZSB3YWxkIHRlc3QgdG8gY29tcGFyZSBtb2RlbDIgd2l0aCBhIG1vZGVsIGNvbnRhaW5pbmcgb25seSAqRWxldmF0aW9uKiBhbmQgKlNjcnV6KiB0byBhc3Nlc3MgaWYgaW5mb3JtYXRpb24gYWJvdXQgbmVhcmJ5IGlzbGFuZHMgaXMgc2lnbmlmaWNhbnQuIA0KYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KbGlicmFyeShhb2QpDQp3YWxkLnRlc3QoYj1jb2VmKG1vZGVsMiksIFNpZ21hPXZjb3YobW9kZWwyKSwgVGVybXM9YygzLDUpKQ0KYGBgDQoNCiMjIyBfZXhhbXBsZSA1XyAjIyMNCg0KIyMjIyAxLiBDcmVhdGUgYSBnZW5lcmFsIGxpbmVhciBtb2RlbCBhbmQgZmluZCAxMC1mb2xkIGFuZCBsZWF2ZSBvbmUgb3V0IGNyb3NzIHZhbGlkYXRpb24gc2NvcmVzLiANCg0KYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KcmVxdWlyZShmYXJhd2F5KQ0KZGF0YShkZWJ0KQ0KZnVsbERhdGE9bmEub21pdChkZWJ0KQ0Kc2V0LnNlZWQoNjkpDQp0ZXN0Um93cz1zYW1wbGUobnJvdyhmdWxsRGF0YSksMC4xKm5yb3coZnVsbERhdGEpKQ0KdGVzdERhdGE9ZnVsbERhdGFbdGVzdFJvd3MsIF0NCnRyYWluRGF0YT1mdWxsRGF0YVstdGVzdFJvd3MsIF0NCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoYm9vdCkNCg0KbW9kMT1sbShwcm9kZWJ0fi4sZGF0YT10cmFpbkRhdGEpDQpuPW5yb3codHJhaW5EYXRhKQ0KY2F0KCIxMC1mb2xkIGNyb3NzIHZhbGlkYXRpb24gc2NvcmU6ICIsY3YuZ2xtKHRyYWluRGF0YSxtb2QxLEs9MTApJGRlbHRhWzFdLCJcbkxlYXZlIG9uIG91dCBjcm9zcyB2YWxpZGF0aW9uIChMT09DVikgc2NvcmU6ICIsIGN2LmdsbSh0cmFpbkRhdGEsbW9kMSxLPW4pJGRlbHRhWzFdKQ0KYGBgDQoqKioNCiMjIyMgMi4gTWFsbG93J3MgQ1AsIEFJQywgYW5kIEJJQyAjIyMjDQpgYGB7cn0NCmxpYnJhcnkoQ29tYk1TQykNCmNhdCgiTWFsbG93J3MgQ1A6ICIsQ3AobW9kMSxTMj1zdW1tYXJ5KG1vZDEpJHNpZ21hXjIpLCJcbkFJQzogIiwgQUlDKG1vZDEsaz0yKSwiXG5CSUM6ICIsQUlDKG1vZDEsaz1sb2cobikpKQ0KYGBgDQoqKioNCg0KIyMjIyAzLiBDcmVhdGUgYSBtb2RlbCB1c2luZyBNYWxsb3cncyBDUC4gVGhlIDEncyBhbmQgMCdzIGNvcnJlc3BvbmQgdG8gd2hldGhlciB0aGF0IHZhcmlhYmxlIHNob3VsZCBiZSB1c2VkIG9yIG5vdC4gVGhpcyB0ZWxscyB1cyB0aGF0IHZhcmlhYmxlcyAxLCA1LCA4LCA5LCAxMSwgYW5kIDEyIHNob3VsZCBiZSB1c2VkIHRvIGJ1aWxkIHRoZSBuZXcgcmVncmVzc2lvbiBtb2RlbC4gDQoNCmBgYHtyLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCmxpYnJhcnkobGVhcHMpDQpvdXQgPSBsZWFwcyh0cmFpbkRhdGFbLC0xM10sIHRyYWluRGF0YSRwcm9kZWJ0LCBtZXRob2QgPSAiQ3AiKQ0KI2NiaW5kKGFzLm1hdHJpeChvdXQkd2hpY2gpLG91dCRDcCkgI3VuY29tbWVudCB0byBkaXNwbGF5IGZ1bGwgdGFibGUgb2YgTWFsbG93cyB2YWx1ZXMNCg0KYmVzdC5tb2RlbCA9IHdoaWNoKG91dCRDcD09bWluKG91dCRDcCkpDQpjYmluZChhcy5tYXRyaXgob3V0JHdoaWNoKSxvdXQkQ3ApW2Jlc3QubW9kZWwsXQ0KDQptb2QyPWxtKGZvcm11bGEgPSBwcm9kZWJ0IH4gaW5jb21lZ3AgKyBhZ2VncCArIG1hbmFnZSArDQogICAgICAgICAgY2NhcmR1c2UgKyBjaWdidXkgKyB4bWFzYnV5ICsgbG9jaW50cm4sIGRhdGEgPSB0cmFpbkRhdGEpDQpgYGANCioqKg0KDQojIyMjIDQuIEZvcndhcmQgYW5kIEJhY2t3YXJkIHN0ZXB3aXNlIHJlZ3Jlc3Npb24NCg0KYGBge3J9DQptaW5tb2Q9bG0ocHJvZGVidH4xLGRhdGE9dHJhaW5EYXRhKQ0KbW9kMyA8LSBzdGVwKG1pbm1vZCwgc2NvcGUgPSBsaXN0KGxvd2VyPW1pbm1vZCx1cHBlcj1tb2QxKSwgZGlyZWN0aW9uID0gImZvcndhcmQiLHRyYWNlPUYpDQpiYWNrd2FyZC5zdGVwIDwtIHN0ZXAobW9kMSwgc2NvcGUgPSBsaXN0KGxvd2VyPW1pbm1vZCx1cHBlcj1tb2QxKSwgZGlyZWN0aW9uID0gImJhY2t3YXJkIix0cmFjZT1GKQ0KDQpzdW1tYXJ5KG1vZDMpO3N1bW1hcnkoYmFja3dhcmQuc3RlcCkNCmBgYA0KKioqDQoNCiMjIyMgNS4gUmlkZ2UgUmVncmVzc2lvbi4gRmlyc3QsIHRoZSBvcHRpbWFsICRcbGFtYmRhJCB2YWx1ZSBtdXN0IHNlbGVjdGVkLCB3aGljaCBtaW5pbXplcyB0aGUgZ2VuZXJhbGl6ZWQgY3Jvc3MgdmFsaWRhdGlvbiAoR0NWKS4gVGhpcyB2YWx1ZSBpcyB0aGVuIHVzZWQgdG8gZmluZCB0aGUgbmV3IHZhbHVlcyBmb3IgY29lZmZpY2llbnRzLiBSZW1lbWJlciB0aGF0IFJpZGdlIHJlZ3Jlc3Npb24gZG9lcyBub3QgcGVyZm9ybSB2YXJpYWJsZSBzZWxlY3Rpb24sIG9ubHkgc2hyaW5rYWdlLiBUaGlzIGlzIGJlY2F1c2UgaXQgdXNlcyB0aGUgTDIgbm9ybSBmb3IgcGVuYWxpemF0aW9uLCB3aGljaCBkb2VzIG5vdCBtZWFzdXJlIHNwYXJzaXR5Lg0KDQpgYGB7cn0NCiNTY2FsaW5nDQpsaWJyYXJ5KE1BU1MpDQojbG0ucmlkZ2UgYXV0b21hdGljYWxseSBzY2FsZXMgdGhlIHByZWRpY3RvcnMsIGJ1dCBpZiB5b3Ugd2FudCB0byBkbyBpdCBtYW51YWxseSwgdXNlIGBzY2FsZSgpYCBmdW5jdGlvbiBhcyBzaG93biBiZWxvdw0KeS5zY2FsZWQgPSBzY2FsZSh0cmFpbkRhdGEkcHJvZGVidCkNClguc2NhbGVkID0gc2NhbGUoYXMubWF0cml4KHRyYWluRGF0YVssLTEzXSkpDQoNCiNSaWRnZSBSZWdyZXNzaW9uDQpsYW1iZGEgPSBzZXEoMTAwLCAxMTAsIGJ5PTAuMDEpDQptb2Q0ID0gbG0ucmlkZ2UocHJvZGVidH4uLGRhdGE9dHJhaW5EYXRhLCBsYW1iZGEgPSBsYW1iZGEpDQpvcHRpbWFsIDwtIHdoaWNoKG1vZDQkR0NWID09IG1pbihtb2Q0JEdDVikpDQoNCmNhdCgiVGhlIEdWQyBzY29yZSBpcyBtaW5pbWl6ZWQgd2hlbiBsYW1iZGEgaXMgZXF1YWwgdG8gIixuYW1lcyhvcHRpbWFsKSwiLCBtYWtpbmcgdGhpcyBvcHRpbWFsIGxhbWJkYSB2YWx1ZS5cblxuIixzZXA9IiIpDQoNCg0KY2F0KCJSaWRnZSBSZWdyZXNzaW9uIENvZWZmaWNpZW50cyBhdCB0aGlzIG9wdGltYWwgTGFtYmRhIFZhbHVlOlxuIikNCnJvdW5kKGNvZWYobW9kNClbd2hpY2gobW9kNCRHQ1YgPT0gbWluKG1vZDQkR0NWKSksXSw0KQ0KYGBgDQoqKioNCg0KIyMjIyA2LiBQZXJmb3JtIExBU1NPIFJlZ3Jlc3Npb24uIA0KDQpgYGB7ciwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQpsaWJyYXJ5KGdsbW5ldCkNCiNMaWtlIGxtLnJpZGdlLCB0aGlzIGZ1bmN0aW9uIGF1dG9tYXRpY2FsbHkgc2NhbGVzIHRoZSBwcmVkaWNvdHJzLg0KDQptb2Q1LmN2PWN2LmdsbW5ldChhcy5tYXRyaXgodHJhaW5EYXRhWywtMTNdKSx0cmFpbkRhdGEkcHJvZGVidCxhbHBoYT0xLG5mb2xkcz0xMCkNCm1vZDUgPSBnbG1uZXQoYXMubWF0cml4KHRyYWluRGF0YVssLTEzXSksdHJhaW5EYXRhJHByb2RlYnQsIGFscGhhID0gMSwgbmxhbWJkYSA9IDEwMCkNCm9wdGltYWwgPC1tb2Q1LmN2JGxhbWJkYS5taW4NCg0KY2F0KCJUaGUgb3B0aW1hbCBsYW1iZGEgdmFsdWUgaXMgIixvcHRpbWFsLCIuXG5cbiIsc2VwPSIiKQ0KDQpjYXQoIlRoZSB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgdGhlIExBU1NPIG1vZGVsIGFyZSBzaG93biBpbiB0aGUgdGFibGUgYmVsb3c6XG4iKQ0KY29lZihtb2Q1LHM9bW9kNS5jdiRsYW1iZGEubWluKQ0KDQpjYXQoIlxuV2UgY2FuIGFsc28gcGxvdCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudCBwYXRoIHRvIGdldCBhbiB1bmRlcnN0YW5kaW5nIG9mIGhvdyB2YWx1ZXMgd2VyZSBzZWxlY3RlZCBmb3IgZWFjaCBjb2VmZmljaWVudDpcbiIpDQpwbG90KG1vZDUseHZhcj0ibGFtYmRhIixsYWJlbD1UUlVFLGx3ZD0yKQ0KYWJsaW5lKHY9bG9nKG1vZDUuY3YkbGFtYmRhLm1pbiksY29sPSdibGFjaycsbHR5ID0gMixsd2Q9MikNCg0KYGBgDQoqKioNCg0KIyMjIyA3LiBFbGFzdGljIE5ldCBSZWdyZXNzaW9uIGlzIHBlcmZvcm1lZCB1c2luZyAxMDAgdmFsdWVzIGZvciAkXGxhbWJkYSQgYW5kIGdpdmluZyBlcXVhbCB3ZWlnaHQgdG8gYm90aCBwZW5hbHRpZXMuIDEwLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiBpcyB1c2VkIHRvIGZpbmQgdGhlIG9wdGltYWwgJFxsYW1iZGEkLiANCg0KYGBge3J9DQojQWdhaW4sIHByZWRpY3RvcnMgYXJlIHNjYWxlZCBhdXRvbWF0aWNhbGx5LiANCm1vZDYuY3Y9Y3YuZ2xtbmV0KGFzLm1hdHJpeCh0cmFpbkRhdGFbLC0xM10pLHRyYWluRGF0YSRwcm9kZWJ0LGFscGhhPTAuNSxuZm9sZHM9MTApDQptb2Q2ID0gZ2xtbmV0KGFzLm1hdHJpeCh0cmFpbkRhdGFbLC0xM10pLCB0cmFpbkRhdGEkcHJvZGVidCwgYWxwaGEgPSAwLjUsIG5sYW1iZGEgPSAxMDApDQpvcHRpbWFsIDwtIG1vZDYuY3YkbGFtYmRhLm1pbg0KY2F0KCJUaGUgb3B0aW1hbCBsYW1iZGEgdmFsdWUgaXMgIixvcHRpbWFsLCIuXG5cbiIsc2VwPSIiKQ0KDQpjYXQoIlRoZSB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgdGhlIExBU1NPIG1vZGVsIGFyZSBzaG93biBpbiB0aGUgdGFibGUgYmVsb3c6XG4iKQ0KY29lZihtb2Q2LHM9bW9kNi5jdiRsYW1iZGEubWluKQ0KYGBgDQoNCiMjIyMgOC4gUHJlZGljdCAqcHJvZGVidCogZm9yIGVhY2ggb2YgdGhlIHJvd3MgaW4gdGhlIHRlc3QgZGF0YSB1c2luZyB0aGUgZnVsbCBtb2RlbCwgbG93ZXN0IE1hbGxvdydzIENwIG1vZGVsLCBhbmQgdGhlIG1vZGVscyBmb3VuZCB1c2luZyBmb3J3YXJkIHN0ZXB3aXNlIHJlZ3Jlc3Npb24sIHJpZGdlIHJlZ3Jlc3Npb24sIGxhc3NvIHJlZ3Jlc3Npb24sIGFuZCBlbGFzdGljIG5ldC4NCg0KYGBge3J9DQpmdWxsPXByZWRpY3QobW9kMSx0ZXN0RGF0YSkNCm1pbmNwPXByZWRpY3QobW9kMix0ZXN0RGF0YSkNCnN0ZXB3aXNlPXByZWRpY3QobW9kMyx0ZXN0RGF0YSkNCnJpZGdlPWNiaW5kKDEsYXMubWF0cml4KHRlc3REYXRhWywtMTNdKSklKiVjb2VmKG1vZDQpW3doaWNoKG1vZDQkR0NWID09IG1pbihtb2Q0JEdDVikpLF0NCm1vZGxhc3NvPWxtKHByb2RlYnR+LixkYXRhPXRyYWluRGF0YVssLTRdKQ0KbGFzc289cHJlZGljdChtb2RsYXNzbyx0ZXN0RGF0YSkNCmVsYXN0aWM9YXMudmVjdG9yKHByZWRpY3QobW9kNixhcy5tYXRyaXgodGVzdERhdGFbLC0xM10pLHM9bW9kNi5jdiRsYW1iZGEubWluKSkNCg0KcHJlZHM9ZGF0YS5mcmFtZShwcm9kZWJ0PXRlc3REYXRhJHByb2RlYnQsZnVsbCxtaW5jcCxzdGVwd2lzZSxyaWRnZSxsYXNzbyxlbGFzdGljKQ0KcHJlZHMNCmBgYA0KDQpQcmVkaWN0aW9ucyBhcmUgY29tcGFyZWQgYmVsb3cgdXNpbmcgbWVhbiBzcXVhcmVkIHByZWRpY3Rpb24gZXJyb3IuIFRoZSBsb3dlc3QgTVNQRSBpbmRpY2F0ZXMgdGhlICJiZXN0IiBtb2RlbCwgYnV0IHRoZXNlIGFyZSBhbGwgc28gY2xvc2UgdGhhdCBpdCB3b3VsZCBiZSBkaWZmaWN1bHQgdG8gbWFrZSB0aGF0IGRldGVybWluYXRpb24gd2l0aCBhIHNpbmdsZSBtZXRyaWMuIA0KYGBge3J9DQpzYXBwbHkocHJlZHNbLC0xXSxmdW5jdGlvbih4KXttZWFuKCh4LXRlc3REYXRhJHByb2RlYnQpXjIpfSkNCmBgYA0KDQoNCiMjIyBBZGRpdGlvbmFsIE5vdGVzICMjIw0KUG9pc3NvbiBpcyBtYWlubHkgdXNlZCB0byBhZGRyZXNzIGNvdW50IGRhdGEgYWNyb3NzIGNvbnRpbnVvdXMgZXZlbnRzIHJhdGhlciB0aGFuIGEgZGlzY3JldGUgZXZlbnRzLiBGb3IgZXhhbXBsZSAtIEhvdyBtYW55IHN0dWRlbnQgcGFzcyBhIHRlc3QgaXMgYSBkaXNjcmV0ZSBldmVudC4gIHdoZXJlIGVhY2ggc3R1ZGVudCBoYXZlIGEgcHJvYmFiaWxpdHkgb2YgcGFzc2luZyBhIHRlc3Qgd2hlbiB0aGV5IHRha2UgaXQuIEhvdyBtYW55IGF3YXJkcyBkb2VzIGEgc3R1ZGVudCBnZXQgaW4gaGlnaCBzY2hvb2wgaXMgbXVjaCBtb3JlIGNvbnRpbnVvdXMsIG9yIGhvdyBtYW55IHBlb3BsZSBhcmUgaW4gYSBsaW5lIGluIHRoZSBhaXJwb3J0LiANCg0KDQoNCg0KDQoNCg==