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 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.
\(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
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
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
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)

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
LS0tDQp0aXRsZTogIlIgSGVscGJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyMgX0NvbnRlbnRzXyAjIyMNCg0KLS0tLS0NCg0KIyMjIyMgX2V4YW1wbGUgMV8gIyMjIyMNCiMjIyMgMS4gQm94cGxvdA0KIyMjIyAyLiBDb3JyZWxhdGlvbg0KIyMjIyAzLiBCYXNpYyBsaW5lYXIgcmVncmVzc2lvbg0KIyMjIyA0LiBBTk9WQSwgcGFpcndpc2UgY29tcGFyaXNvbiANCiMjIyMgNS4gRGlhZ25vc3RpYyBQbG90cw0KIyMjIyA2LiBWSUYsIFByZWRpY3Rpb25zLCBhbmQgaW50ZXJ2YWxzDQoNCiRcdGltZXMkDQoNCiMjIyMjIF9leGFtcGxlIDJfICMjIyMjDQojIyMjIDEuIFN0ZXB3aXNlIFJlZ3Jlc3Npb24NCg0KJFx0aW1lcyQNCg0KIyMjIyMgX2V4YW1wbGUgM18gIyMjIyMNCiMjIyMgMS4gQm94cGxvdHMNCiMjIyMgMi4gQmlub21pYWwgbG9naXN0aWMgcmVncmVzc2lvbg0KDQokXHRpbWVzJA0KDQojIyMjIyBfZXhhbXBsZSA0XyAjIyMjIw0KIyMjIyAxLiBQb2lzc29uIFJlZ3Jlc3Npb24sIE9mZnNldHRpbmcgbW9kZWwNCiMjIyMgMi4gQ2hpLVNxdWFyZSBTaWduZmljYW5jZSBUZXN0LCBHb29kbmVzcyBvZiBGaXQNCiMjIyMgMy4gRGlzcGVyc2lvbiBwYXJhbWV0ZXIsIFdhbGQgdGVzdA0KIyMjIyA0LiBDb3VudCBwZXIgYXJlYSBtb2RlbA0KIyMjIyA1LiBXYWxkIHRlc3QNCg0KJFx0aW1lcyQNCg0KIyMjIyMgX2V4YW1wbGUgNV8gIyMjIyMNCiMjIyMgMS4gMTAtZm9sZCBhbmQgTGVhdmUgb25lIG91dCBjcm9zcyB2YWxpZGF0aW9uDQojIyMjIDIuIE1hbGxvdydzIENQLCBBSUMsIEJJQw0KIyMjIyAzLiBNYWxsb3cncyBDUCBtb2RlbA0KIyMjIyA0LiBGb3J3YXJkL0JhY2t3YXJkIFN0ZXB3aXNlDQojIyMjIDUuIFJpZGdlIFJlZ3Jlc3Npb24NCiMjIyMgNi4gTEFTU08NCiMjIyMgNy4gRWxhc3RpYyBOZXQNCiMjIyMgOC4gUHJlZGljdGlvbnMNCioqKg0KLS0tDQoNCiMjIyBfZXhhbXBsZSAxXyANCiMjIyMgMS4gQm94cGxvdCBnaXZlcyBhIHZpc3VhbCBkaXNwbGF5IG9mIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gDQpgYGB7cn0NCmxpYnJhcnkoZmFyYXdheSkNCmxpYnJhcnkoZ2dwbG90MikNCg0KZGF0YShkaWFiZXRlcykNCmRmPW5hLm9taXQoZGlhYmV0ZXNbLGMoNSw4LDEwLDExLDEyLDE3LDE4KV0pDQoNCmdncGxvdChkYXRhPWRmLCBhZXMoeD1hcy5mYWN0b3IoZnJhbWUpLHk9cmF0aW8sIGZpbGw9ZnJhbWUpKSsNCiAgZ2VvbV9ib3hwbG90KGFscGhhPTAuMykrDQogIHhsYWIoImZyYW1lIikrDQogIHlsYWIoInJhdGlvIikrDQogIGdndGl0bGUoImZyYW1lIHNpemUgdnMgZGlhYmV0ZXMiKSsgDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIjAiID0gIkRlYWQiLCAiMSIgPSAiQWxpdmUiKSkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBjb2xvcj0iY29ybmZsb3dlcmJsdWUiLCBzaXplPTE0KSwNCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIiwgY29sb3I9InJveWFsYmx1ZTQiLCANCiAgICAgICAgICBzaXplPTE2LCBhbmdsZT0yNSksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE3LGZhY2U9Iml0YWxpYyIpLA0KICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCxmYWNlPSJib2xkLml0YWxpYyIpKQ0KYGBgDQoqKioNCg0KIyMjIyAyLiBUd28gbWV0aG9kcyBhcmUgc2hvd24gZm9yIGRpc3BsYXlpbmcgY29ycmVsYXRpb24gYmV0d2VlbiBjb250aW51b3VzIHZhcmlhYmxlcy4gDQpgYGB7cn0NCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShjb3JycGxvdCkNCg0KeCA8LSBjb3IoZGZbLGMoMSwyLDMsNCw2LDcpXSkNCmNvcnJwbG90Lm1peGVkKHgpDQoNCmdncGFpcnMoZGZbLGMoMSwyLDMsNCw2LDcpXSxnZ3Bsb3QyOjphZXMoY29sb3I9InNhbG1vbiIpKQ0KDQpgYGANCioqKg0KDQojIyMjIDMuIEJhc2ljIExpbmVhciBSZWdyZXNzaW9uIHdpdGggc3VtbWFyeSBkaXNwbGF5LiANCmBgYHtyfQ0KbGlicmFyeShicm9vbSkNCg0KbW9kZWwgPC0gbG0ocmF0aW9+LixkYXRhPWRmKQ0KI2Jyb29tIHN1bW1hcnkgd2l0aCBvcHRpb25hbCBwYXJhbWV0ZXJzDQp0aWR5KG1vZGVsLGNvbmYuaW50ID0gVFJVRSwgY29uZi5sZXZlbCA9IDAuOTApDQoNCiNkZWZhdWx0IHN1bW1hcnkgd2l0aCBhZGRpdGlvbmFsIGluZm9ybWF0aW9uDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQoqKioNCg0KIyMjIyA0LiBBTk9WQSBjYW4gYmUgdXNlZCB0byBnZW5lcmF0ZSBhIHBhcnRpYWwgRi1zY29yZS4gSWYgeW91IHdhbnQgdG8gY29tcGFyZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGdyb3VwcywgdXNlIFR1a2V5J3MgcG9zdC1ob2MgdGVzdCBhcyBhIHBhaXJ3aXNlIGNvbXBhcmlzb24uIEFOT1ZBIGNhbiBhbHNvIGJlIHVzZWQgdG8gY29tcGFyZSB0d28gbW9kZWxzIGFuZCBkZXRlcm1pbmUgaWYgdGhlIHByZWRpY3RvcnMgdXNlZCBpbiBvbmUgbW9kZWwgYXJlIHNpZ25pZmljYW50IGFuZCBpbXBvcnRhbnQgdG8ga2VlcC4gTm90ZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHVzaW5nIGBhb3YoKWAgYW5kIGBhbm92YSgpYCBmdW5jdGlvbnMuIA0KYGBge3J9DQphbm92YS5tb2RlbCA8LSBhb3YocmF0aW9+ZnJhbWUsZGF0YT1kZikNCnN1bW1hcnkoYW5vdmEubW9kZWwpDQpUdWtleUhTRChhbm92YS5tb2RlbCkNCiNDb21wYXJlIHR3byBtb2RlbHMNCm1vZGVsMiA8LSBsbShyYXRpb35hZ2UsZGF0YT1kZikNCmFub3ZhKG1vZGVsLG1vZGVsMikNCmBgYA0KKioqDQoNCiMjIyMgNS4gRGlhZ25vc3RpYyBQbG90cw0KYGBge3J9DQpsaWJyYXJ5KGdnZm9ydGlmeSkNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShsaW5kaWEpDQojaGlzdG9ncmFtIG9mIHJlc2lkdWFscw0KZ2dfcmVzaGlzdChtb2RlbCwgYmlucz0zMCkNCiNyZXNpZHVhbHMgdnMgZml0dGVkDQphdXRvcGxvdChtb2RlbCwgbGFiZWwuc2l6ZT0zLCB3aGljaD1jKDEsMCkpDQojUS1RIHBsb3QNCmF1dG9wbG90KG1vZGVsLCBsYWJlbC5zaXplPTMsIHdoaWNoPWMoMiwwKSkNCiNUd28gbW9kZWxzIGZvciBDb29rcyBkaXN0YW5jZQ0KYXV0b3Bsb3QobW9kZWwsIGxhYmVsLnNpemU9Mywgd2hpY2g9Yyg0LDApKQ0KDQpkZiRjb29rIDwtIGNvb2tzLmRpc3RhbmNlKG1vZGVsKSAjYWRkcyBjb2x1bW4gb2YgY29va3MgZGlzdGFuY2UgdmFsdWVzIHRvIG9yaWdpbmFsIGRmDQpjb29rX3ZhbCA8LSA0L25yb3coZGYpICNzdGFuZGFyZCAiYmFkIiBjb29rIGRpc3RhbmNlIGlzIDQvbg0KDQpnZ3Bsb3QoZGYsIGFlcyh4PWFzLm51bWVyaWMocm93bmFtZXMoZGYpKSx5PWNvb2spKSsgDQogIGdlb21faGlzdG9ncmFtKHN0YXQ9ImlkZW50aXR5IiwgY29sb3I9ImNvcm5mbG93ZXJibHVlIixhbHBoYT0uMyxzaXplPTIpKw0KICB5bGFiKCJDb29rJ3MgRGlzdGFuY2UiKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZT0iYm9sZCIsIGNvbG9yPSJyb3lhbGJsdWU0Iiwgc2l6ZT0xNCksDQogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFjZT0iYm9sZCIsIGNvbG9yPSJyb3lhbGJsdWU0IiwgDQogICAgICAgICAgc2l6ZT0xNiwgYW5nbGU9MjUpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNyxmYWNlPSJpdGFsaWMiKSwNCiAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjAsZmFjZT0iYm9sZC5pdGFsaWMiKSkrDQogIHhsYWIoIiIpDQoNCm91dGxpZXJzIDwtIHdoaWNoKGRmJGNvb2sgPiBjb29rX3ZhbCkNCmNhdCgiVGhlcmUgYXJlICIsbGVuZ3RoKG91dGxpZXJzKSwiIG91dGxpZXJzIGRldGVjdGVkIGJ5IENvb2sncyBEaXN0YW5jZSBpbiB0aGUgbW9kZWwuIikNCmBgYA0KKioqDQoNCiMjIyMgNi4gUHJlZGljdGlvbnMgYW5kIEludGVydmFscy4gVGhlIGBwcmVkaWN0KClgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIG1ha2UgcHJlZGljdGlvbnMgd2l0aCB0aGUgbW9kZWwgYXMgd2VsbCBhcyBjcmVhdGUgYm90aCBwcmVkaWN0aW9uIGFuZCBjb25maWRlbmNlIGludGVydmFscy4gDQoNCmBgYHtyfQ0KcHJlZC5kYXRhIDwtIGRhdGEuZnJhbWUoImFnZSI9MzAsImZyYW1lIj0ibWVkaXVtIiwiaGVpZ2h0Ij02Niwid2VpZ2h0Ij0yNTAsIndhaXN0Ij00NSwiaGlwIj01NSkNCnByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbCwgcHJlZC5kYXRhLGludGVydmFsPWMoInByZWRpY3Rpb24iKSkNCg0KY2F0KCJUaGUgbW9kZWwgcHJlZGljdHMgdGhhdCBhIHdvbWFuIHdpdGggdGhlIGdpdmVuIHBhcmFtZXRlcnMgd2lsbCBoYXZlIGFuIEhETC9DaG9sZXN0ZXJvbCByYXRpbyBvZiAiLCBwcmVkaWN0aW9uWzFdLCIuIixzZXA9IiIsIlxuIikNCg0KI2NvbmZpZGVuY2UgaW50ZXJ2YWwgYXQgOTAlDQpjb25maW50IDwtIHByZWRpY3QobW9kZWwsIHByZWQuZGF0YSxpbnRlcnZhbD1jKCJjb25maWRlbmNlIiksIGxldmVsID0wLjkpDQpjYXQoIlRoZSA5MCUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhpcyBtb2RlbCBpcyAoIixjb25maW50WzJdLCIsICIsY29uZmludFszXSwiKS4iLHNlcD0iIiApDQoNCmBgYA0KKioqDQojIyMgX2V4YW1wbGUgMl8gIyMjDQojIyMjIDEuIFVzaW5nIGZvcndhcmQtYmFja3dhcmQgc3RlcHdpc2UgcmVncmVzc2lvbiBmb3IgdmFyaWFibGUgc2VsZWN0aW9uLiBUaGUgZmluYWwgc2VsZWN0ZWQgdmFyaWFibGVzIGZyb20gdGhpcyBleGFtcGxlIGFyZSBEYXkgb2YgV2VlaywgU2NoZWR1bGVkIERlcGFydHVyZSwgRGVzdGluYXRpb24gQWlycG9ydCwgYW5kIEFpcmxpbmUuDQpgYGB7cn0NCmRmIDwtIHJlYWQuY3N2KCJmbGlnaHRzLmNzdiIsIGhlYWRlcj1UUlVFLCBzZXA9IiwiKQ0KbW9kZWwgPC0gbG0oREVQQVJUVVJFX0RFTEFZfkRBWV9PRl9XRUVLK0RJU1RBTkNFK0FJUkxJTkUrREVTVElOQVRJT05fQUlSUE9SVCtTQ0hFRFVMRURfQVJSSVZBTCtTQ0hFRFVMRURfREVQQVJUVVJFLGRhdGE9ZGYpDQoNCmVtcHR5IDwtIGxtKERFUEFSVFVSRV9ERUxBWX4xLGRhdGE9ZGYpDQptc3RlcCA8LSBzdGVwKG1vZGVsLHNjb3BlPWxpc3QobG93ZXI9ZW1wdHksdXBwZXI9bW9kZWwpLGRpcmVjdGlvbiA9ICJib3RoIikNCmBgYA0KKioqDQojIyMgX2V4YW1wbGUgM18gIyMjDQoNCiMjIyMgMS4gTW9yZSBCb3hwbG90cyENCmBgYHtyfQ0KZGY9IHJlYWQuY3N2KCJlY2hvLmNzdiIsIGhlYWRlcj1UUlVFLCBzZXA9IiwiKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnZ3Bsb3QoZGF0YT1kZiwgYWVzKHg9YXMuZmFjdG9yKHN0aWxsX2FsaXZlKSx5PWFnZSwgZmlsbD1zdGlsbF9hbGl2ZSkpKw0KICBnZW9tX2JveHBsb3QoYWxwaGE9MC4zKSsNCiAgeGxhYigiU3RhdGUgb2YgRXhpc3RlbmNlIikrDQogIHlsYWIoIkFnZSB3aGVuIEhlYXJ0IEF0dGFjayBPY2N1cnJlZCIpKw0KICBnZ3RpdGxlKCJBZ2UgeCBEZWF0aCIpKyANCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiMCIgPSAiRGVhZCIsICIxIiA9ICJBbGl2ZSIpKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZT0iYm9sZCIsIGNvbG9yPSJjb3JuZmxvd2VyYmx1ZSIsIHNpemU9MTQpLA0KICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBjb2xvcj0icm95YWxibHVlNCIsIA0KICAgICAgICAgIHNpemU9MTYsIGFuZ2xlPTI1KSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTcsZmFjZT0iaXRhbGljIiksDQogICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIwLGZhY2U9ImJvbGQuaXRhbGljIikpDQpnZ3Bsb3QoZGF0YT1kZiwgYWVzKHg9YXMuZmFjdG9yKHN0aWxsX2FsaXZlKSx5PWZyYWN0aW9uYWxfc2hvcnRlbmluZywgZmlsbD1zdGlsbF9hbGl2ZSkpKw0KICBnZW9tX2JveHBsb3QoYWxwaGE9MC4zKSsNCiAgeGxhYigiUHJlc2VuY2Ugb2YgTW9ydGFsaXR5IikrDQogIHlsYWIoIkNvbnRyYWNpbGl0eSBhcm91bmQgaGVhcnQiKSsNCiAgZ2d0aXRsZSgiZnJhY3Rpb25hbCBzaG9ydGVuaW5nIHggRGVhdGgiKSsgDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIjAiID0gIkRlYWQiLCAiMSIgPSAiQWxpdmUiKSkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBjb2xvcj0iY29ybmZsb3dlcmJsdWUiLCBzaXplPTE0KSwNCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIiwgY29sb3I9InJveWFsYmx1ZTQiLCANCiAgICAgICAgICBzaXplPTE2LCBhbmdsZT0yNSksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE3LGZhY2U9Iml0YWxpYyIpLA0KICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCxmYWNlPSJib2xkLml0YWxpYyIpKQ0KDQpgYGANCioqKg0KDQojIyMjIDIuIENyZWF0ZSBhIGJpbm9taWFsIGdlbmVyYWwgbGluZWFyIG1vZGVsIHdpdGggYGdsbSgpYC4gVXNlIG1vZGVsIHN1bW1hcnkgdG8gZGV0ZXJtaW5lIGNvZWZmaWNpZW50cy4gDQoNCnxwYXJhbWV0ZXJzIGZvciAkc3RpbGxcX2FsaXZlJHwNCnwtLS0tLS0tLS0tLS0tLS18DQp8JGludGVyY2VwdCQ6ICQ4LjU0OTQ0JCAqc2lnbmlmaWNhbnQgYXQgcDwuMDR8DQp8JGFnZSQ6ICQtMC4wODk5NCQgKnNpZ25pZmljYW50IGF0IHA8LjA1fA0KJGZyYWNcX3Nob3J0JDogJDUuOTAyOTUkICpzaWduaWZpY2FudCBhdCBwPC4wNXwNCiRsdmRkJDogJC0wLjY1ODg2JCAqc2lnbmlmaWNhbnQgYXQgcDwuMDV8DQoNCmBgYHtyfQ0KbSA8LSBnbG0oc3RpbGxfYWxpdmV+YWdlK2ZyYWN0aW9uYWxfc2hvcnRlbmluZytsdmRkLCBmYW1pbHk9ImJpbm9taWFsIiwgZGF0YT1kZikNCnRpZHkobSxjb25mLmludCA9IFRSVUUsIGNvbmYubGV2ZWwgPSAwLjkwKQ0KYGBgDQoNCioqKg0KIyMjIF9leGFtcGxlIDRfICMjIw0KDQojIyMjIDEuIENyZWF0ZSBhIHBvaXNzb24gcmVncmVzc2lvbiBtb2RlbCB3aXRoIHRoZSBkYXRhLiBDcmVhdGUgYSBzZWNvbmQgbW9kZWwgdGhhdCBvZmZzZXRzIHRoZSByZXNwb25zZSB2YXJpYWJsZSB3aXRoIHRoZSBsb2cgb2YgYXJlYS4gTW9kZWwgcGFyYW1ldGVycyBhcmUgdGhlIGNvZWZmaWNpZW50cyB0byB0aGUgZml2ZSBwcmVkaWN0aW9uIHZhcmlhYmxlcyAqQXJlYSosICpFbGV2YXRpb24qLCAqTmVhcmVzdCosICpTY3J1eiosIGFuZCAqQWRqYWNlbnQqIGFsb25nIHdpdGggdGhlIGludGVyY2VwdC4gRXN0aW1hdGVzIGZvciBlYWNoIG9mIHRoZXNlIHBhcmFtZXRlcnMgYXJlIGdpdmVuIGluIGhlICdFc3RpbWF0ZScgY29sdW1uIG9mIHRoZSBtb2RlbCBzdW1tYXJ5Lg0KDQpQb2lzc29uIHJlZ3Jlc3Npb24gdXNlcyBhIGxvZyB0cmFuc2Zvcm1hdGlvbiBvbiB0aGUgZGVzaXJlZCByZXNwb25zZSwgc28gdGhlIGVxdWF0aW9uIGlzICRTcGVjaWVzID0gZXhwKDMuMTU1IC0gMC4wMDA1Nzk5KkFyZWEgKyAwLjAwMzU0MSpFbGV2YXRpb24gKyAwLjAwODgyNipOZWFyZXN0IC0gMC4wMDU3MDkqU2NydXogLSAwLjAwMDY2MzAqQWRqYWNlbnQpJA0KDQpGb3IgdGhlIGNvZWZmaWNpZW50IGZvciAqRWxldmF0aW9uKjoNCg0KKiBGb3IgZXZlcnkgMW0gaW5jcmVhc2UgaW4gaGlnaGVzdCBlbGV2YXRpb24sIHRoZSBsb2cgZXhwZWN0ZWQgbnVtYmVyIG9mIHNwZWNpZXMgKG9yIHRoZSBsb2cgcmF0ZSBvZiBzcGVjaWVzIHBlciBpc2xhbmQpIGluY3JlYXNlcyBieSAwLjAwMzU0MSB3aGVuIGhvbGRpbmcgdGhlIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudC4gDQoqIFRoZSByYXRpbyBvZiBleHBlY3RlZCBudW1iZXIgb2Ygc3BlY2llcyBwZXIgaXNsYW5kIGZvciBhIDFtIGluY3JlYXNlIGluIGhpZ2hlc3QgZWxldmF0aW9uIGlzIGV4cCgwLjAwMzU0MSk9MS4wMDM1LCBob2xkaW5nIHRoZSBvdGhlciB2YXJpYWJsZXMgY29uc3RhbnQuDQoqIEZvciBldmVyeSAxbSBpbmNyZWFzZSBpbiBoaWdoZXN0IGVsZXZhdGlvbiwgdGhlIGV4cGVjdGVkIG51bWJlciBvZiBzcGVjaWVzIGluY3JlYXNlcyBieSBhIGZhY3RvciBvZiBleHAoMC4wMDM1NDEpPTEuMDAzNSAoaS5lLiBhIDAuMzUlIGluY3JlYXNlKSB3aGVuIGhvbGRpbmcgdGhlIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudC4NCg0KRm9yIHRoZSBjb2VmZmljaWVudCBmb3IgKlNjcnV6KjoNCg0KKiBGb3IgZXZlcnkgMWttIGluY3JlYXNlIGluIGRpc3RhbmNlIHRvIFNhbnRhIENydXosIHRoZSBsb2cgZXhwZWN0ZWQgbnVtYmVyIG9mIHNwZWNpZXMgKG9yIHRoZSBsb2cgcmF0ZSBvZiBzcGVjaWVzIHBlciBpc2xhbmQpIGRlY3JlYXNlcyBieSAwLjAwNTcwOSB3aGVuIGhvbGRpbmcgdGhlIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudC4gDQoqIFRoZSBleHBlY3RlZCByYXRpbyBvZiBudW1iZXIgb2Ygc3BlY2llcyBwZXIgaXNsYW5kIGZvciBhIDFrbSBpbmNyZWFzZSBpbiBkaXN0YW5jZSB0byBTYW50YSBDcnV6IGlzIGV4cCgtMC4wMDU3MDkpPTAuOTk0MywgaG9sZGluZyB0aGUgb3RoZXIgdmFyaWFibGVzIGNvbnN0YW50Lg0KKiBGb3IgZXZlcnkgMWttIGluY3JlYXNlIGluIGRpc3RhbmNlIHRvIFNhbnRhIENydXosIHRoZSBleHBlY3RlZCBudW1iZXIgb2Ygc3BlY2llcyBkZWNyZWFzZXMgYnkgYSBmYWN0b3Igb2YgZXhwKC0wLjAwNTcwOSk9MC45OTQzIChpLmUuIGEgMC41NyUgZGVjcmVhc2UpIHdoZW4gaG9sZGluZyB0aGUgb3RoZXIgdmFyaWFibGVzIGNvbnN0YW50Lg0KDQpgYGB7ciwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQpyZXF1aXJlKGZhcmF3YXkpDQpkYXRhKGdhbGEpDQpteWRhdGEgPC0gZ2FsYQ0KZGYgPC0gbXlkYXRhWy0yXQ0KDQptb2RlbDEgPC0gZ2xtKFNwZWNpZXMgfiAuLGZhbWlseT1wb2lzc29uLCBkZikNCm1vZGVsMiA9IGdsbShTcGVjaWVzIH4gRWxldmF0aW9uK05lYXJlc3QrU2NydXorQWRqYWNlbnQrb2Zmc2V0KGxvZyhBcmVhKSksDQogICAgICAgICAgICAgZGF0YSA9IG15ZGF0YSwgZmFtaWx5ID0gcG9pc3NvbikNCg0KYGBgDQoqKioNCg0KIyMjIyAyLiBDaGkgc3F1YXJlOiBUbyB0ZXN0IGlmIHRoZSBvdmVyYWxsIG1vZGVsIGlzIHNpZ25pZmljYW50LCB3ZSB1c2UgYSBjaGktc3F1YXJlIHRlc3QgY29tcGFyaW5nIHRoZSBmaXR0ZWQgbW9kZWwgdG8gdGhlIG51bGwgbW9kZWwuIFRoZSBwLXZhbHVlIGlzIGNsb3NlIHRvIDAgaW5kaWNhdGluZyB0aGUgbW9kZWwgaXMgc2lnbmlmaWNhbnQgb3ZlcmFsbC4NCmBgYHtyfQ0KMS1wY2hpc3EoKG1vZGVsMSRudWxsLmRldi1tb2RlbDEkZGV2aWFuY2UpLCAobW9kZWwxJGRmLm51bGwtbW9kZWwxJGRmLnJlc2lkKSkNCmBgYA0KKioqDQoNCiMjIyMgMy4gR29vZG5lc3Mgb2YgRml0IGFuZCBEaXNwZXJzaW9uOiBXZSBwZXJmb3JtIGdvb2RuZXNzIG9mIGZpdCBoeXBvdGhlc2lzIHRlc3RzIHVzaW5nIGJvdGggZGV2aWFuY2UgYW5kIFBlYXJzb24gcmVzaWR1YWwsIGFuZCBjYWxjdWxhdGUgdGhlIGRpc3BlcnNpb24gcGFyYW1ldGVyLiANClAtdmFsdWVzIGZyb20gYm90aCBnb29kbmVzcyBvZiBmaXQgdGVzdHMgYXJlIGNsb3NlIHRvIDAsIHN1Z2dlc3Rpbmcgb3VyIG1vZGVsIGlzIG5vdCBhIGdvb2QgZml0LiBUaGVzZSBmaW5kaW5ncyBkbyBub3QgY29udHJhZGljdCB0aG9zZSBmcm9tIFF1ZXN0aW9uIDJiIGJlY2F1c2UgaXQgaXMgcG9zc2libGUgZm9yIGEgbW9kZWwgdG8gaGF2ZSBwcmVkaWN0aXZlIGFiaWxpdHkgd2hpbGUgc3RpbGwgYmVpbmcgYSBwb29yIGZpdC4gRnVydGhlciwgdGhlIGRpc3BlcnNpb24gcGFyYW1ldGVyIGlzIG11Y2ggbGFyZ2VyIHRoYW4gMiwgc28gdGhlcmUgaXMgb3ZlcmRpc3BlcnNpb24uDQpgYGB7ciwgcmVzdWx0cz0naG9sZCd9DQojV2l0aCBkZXZpYW5jZSByZXNpZHVhbHMNCjEtcGNoaXNxKG1vZGVsMSRkZXZpYW5jZSxtb2RlbDEkZGYucmVzaWR1YWwpDQojd2l0aCBQZWFyc29uIHJlc2lkdWFscw0KcFJlc2lkIDwtIHJlc2lkKG1vZGVsMSwgdHlwZSA9ICJwZWFyc29uIikNCjEtcGNoaXNxKHN1bShwUmVzaWReMiksbW9kZWwxJGRmLnJlc2lkdWFsKQ0KDQojZGlzcGVyc2lvbiBwYXJhbXRlcg0KbW9kZWwxJGRldmlhbmNlL21vZGVsMSRkZi5yZXMNCmBgYA0KDQoqKioNCg0KIyMjIyA0LiBMZXQncyBjcmVhdGUgYSByYXRlIGJhc2VkIG1vZGVsIGZvciB0aGUgc2FtZSBkYXRhc2V0LiBOb3cgdGhlIHJlc3BvbnNlIHdpbGwgYmUgZGVuc2l0eSBvZiBzcGVjaWVzIChudW1iZXIgb2Ygc3BlY2llcy9rbV4yKS4gU28gdGhlIGV4cG9zdXJlIGluIHRoaXMgY2FzZSB3aWxsIGJlIEFyZWEuIENhbGwgdGhpcyAqKm1vZGVsMioqLg0KDQpUaGUgbnVtYmVyIG9mIHNwZWNpZXMgaXMgbm93IHNjYWxlZCBieSB0aGUgYXJlYSBvZiB0aGUgaXNsYW5kLCBzbyB0aGUgZXF1YXRpb24gaXMgJFNwZWNpZXMvQXJlYSA9IGV4cCgyLjE1NSAtIDAuMDAyOTY2KkVsZXZhdGlvbiAtIDAuMDE2NzQqTmVhcmVzdCAtIDAuMDAxMDc4KlNjcnV6ICsgMC4wMDAxNTY4KkFkamFjZW50KSQNCg0KDQpGb3IgdGhlIGNvZWZmaWNpZW50IG9mICpBZGphY2VudCo6DQoqIEZvciBldmVyeSAxa21eMiBpbmNyZWFzZSBpbiBhZGphY2VudCBpc2xhbmQgYXJlYSwgdGhlIGxvZyBleHBlY3RlZCBudW1iZXIgb2Ygc3BlY2llcyBwZXIgc3F1YXJlIGtpbG9tZXRlciBpbmNyZWFzZXMgYnkgMC4wMDAxNTY4IHdoZW4gaG9sZGluZyB0aGUgb3RoZXIgdmFyaWFibGVzIGNvbnN0YW50LiANCiogVGhlIHJhdGlvIG9mIHRoZSBleHBlY3RlZCBudW1iZXIgb2Ygc3BlY2llcyBwZXIgc3F1YXJlIGtpbG9tZXRlciB3aXRoIGEgMWttXjIgaW5jcmVhc2UgaW4gYWRqYWNlbnQgaXNsYW5kIGFyZWEgdG8gdGhlIGV4cGVjdGVkIG51bWJlciB3aXRob3V0IHRoZSBpbmNyZWFzZSBpcyBleHAoMC4wMDAxNTY4KT0xLjAwMDE1NywgaG9sZGluZyB0aGUgb3RoZXIgdmFyaWFibGVzIGNvbnN0YW50Lg0KKiBGb3IgZXZlcnkgMWttXjIgaW5jcmVhc2UgaW4gYXJlYSBvZiB0aGUgYWRqYWNlbnQgaXNsYW5kLCB0aGUgZXN0aW1hdGVkIHJhdGUgb2Ygc3BlY2llcyBwZXIgc3F1YXJlIGtpbG9tZXRlciBpbmNyZWFzZXMgYnkgYSBmYWN0b3Igb2YgZXhwKDAuMDAwMTU2OCk9MS4wMDAxNTcgKGkuZS4gYSAwLjAxNTclIGluY3JlYXNlKSB3aGVuIGhvbGRpbmcgdGhlIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudC4NCmBgYHtyfQ0KbW9kZWwyID0gZ2xtKFNwZWNpZXMgfiBFbGV2YXRpb24rTmVhcmVzdCtTY3J1eitBZGphY2VudCtvZmZzZXQobG9nKEFyZWEpKSwNCiAgICAgICAgICAgICBkYXRhID0gbXlkYXRhLCBmYW1pbHkgPSBwb2lzc29uKQ0Kc3VtbWFyeShtb2RlbDIpDQpgYGANCg0KKioqDQojIyMjIDUuIFVzZSB3YWxkIHRlc3QgdG8gY29tcGFyZSBtb2RlbDIgd2l0aCBhIG1vZGVsIGNvbnRhaW5pbmcgb25seSAqRWxldmF0aW9uKiBhbmQgKlNjcnV6KiB0byBhc3Nlc3MgaWYgaW5mb3JtYXRpb24gYWJvdXQgbmVhcmJ5IGlzbGFuZHMgaXMgc2lnbmlmaWNhbnQuIA0KYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KbGlicmFyeShhb2QpDQp3YWxkLnRlc3QoYj1jb2VmKG1vZGVsMiksIFNpZ21hPXZjb3YobW9kZWwyKSwgVGVybXM9YygzLDUpKQ0KYGBgDQoNCiMjIyBfZXhhbXBsZSA1XyAjIyMNCg0KIyMjIyAxLiBDcmVhdGUgYSBnZW5lcmFsIGxpbmVhciBtb2RlbCBhbmQgZmluZCAxMC1mb2xkIGFuZCBsZWF2ZSBvbmUgb3V0IGNyb3NzIHZhbGlkYXRpb24gc2NvcmVzLiANCg0KYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQ0KcmVxdWlyZShmYXJhd2F5KQ0KZGF0YShkZWJ0KQ0KZnVsbERhdGE9bmEub21pdChkZWJ0KQ0Kc2V0LnNlZWQoNjkpDQp0ZXN0Um93cz1zYW1wbGUobnJvdyhmdWxsRGF0YSksMC4xKm5yb3coZnVsbERhdGEpKQ0KdGVzdERhdGE9ZnVsbERhdGFbdGVzdFJvd3MsIF0NCnRyYWluRGF0YT1mdWxsRGF0YVstdGVzdFJvd3MsIF0NCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoYm9vdCkNCg0KbW9kMT1sbShwcm9kZWJ0fi4sZGF0YT10cmFpbkRhdGEpDQpuPW5yb3codHJhaW5EYXRhKQ0KY2F0KCIxMC1mb2xkIGNyb3NzIHZhbGlkYXRpb24gc2NvcmU6ICIsY3YuZ2xtKHRyYWluRGF0YSxtb2QxLEs9MTApJGRlbHRhWzFdLCJcbkxlYXZlIG9uIG91dCBjcm9zcyB2YWxpZGF0aW9uIChMT09DVikgc2NvcmU6ICIsIGN2LmdsbSh0cmFpbkRhdGEsbW9kMSxLPW4pJGRlbHRhWzFdKQ0KYGBgDQoqKioNCiMjIyMgMi4gTWFsbG93J3MgQ1AsIEFJQywgYW5kIEJJQyAjIyMjDQpgYGB7cn0NCmxpYnJhcnkoQ29tYk1TQykNCmNhdCgiTWFsbG93J3MgQ1A6ICIsQ3AobW9kMSxTMj1zdW1tYXJ5KG1vZDEpJHNpZ21hXjIpLCJcbkFJQzogIiwgQUlDKG1vZDEsaz0yKSwiXG5CSUM6ICIsQUlDKG1vZDEsaz1sb2cobikpKQ0KYGBgDQoqKioNCg0KIyMjIyAzLiBDcmVhdGUgYSBtb2RlbCB1c2luZyBNYWxsb3cncyBDUC4gVGhlIDEncyBhbmQgMCdzIGNvcnJlc3BvbmQgdG8gd2hldGhlciB0aGF0IHZhcmlhYmxlIHNob3VsZCBiZSB1c2VkIG9yIG5vdC4gVGhpcyB0ZWxscyB1cyB0aGF0IHZhcmlhYmxlcyAxLCA1LCA4LCA5LCAxMSwgYW5kIDEyIHNob3VsZCBiZSB1c2VkIHRvIGJ1aWxkIHRoZSBuZXcgcmVncmVzc2lvbiBtb2RlbC4gDQoNCmBgYHtyLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCmxpYnJhcnkobGVhcHMpDQpvdXQgPSBsZWFwcyh0cmFpbkRhdGFbLC0xM10sIHRyYWluRGF0YSRwcm9kZWJ0LCBtZXRob2QgPSAiQ3AiKQ0KI2NiaW5kKGFzLm1hdHJpeChvdXQkd2hpY2gpLG91dCRDcCkgI3VuY29tbWVudCB0byBkaXNwbGF5IGZ1bGwgdGFibGUgb2YgTWFsbG93cyB2YWx1ZXMNCg0KYmVzdC5tb2RlbCA9IHdoaWNoKG91dCRDcD09bWluKG91dCRDcCkpDQpjYmluZChhcy5tYXRyaXgob3V0JHdoaWNoKSxvdXQkQ3ApW2Jlc3QubW9kZWwsXQ0KDQptb2QyPWxtKGZvcm11bGEgPSBwcm9kZWJ0IH4gaW5jb21lZ3AgKyBhZ2VncCArIG1hbmFnZSArDQogICAgICAgICAgY2NhcmR1c2UgKyBjaWdidXkgKyB4bWFzYnV5ICsgbG9jaW50cm4sIGRhdGEgPSB0cmFpbkRhdGEpDQpgYGANCioqKg0KDQojIyMjIDQuIEZvcndhcmQgYW5kIEJhY2t3YXJkIHN0ZXB3aXNlIHJlZ3Jlc3Npb24NCg0KYGBge3J9DQptaW5tb2Q9bG0ocHJvZGVidH4xLGRhdGE9dHJhaW5EYXRhKQ0KbW9kMyA8LSBzdGVwKG1pbm1vZCwgc2NvcGUgPSBsaXN0KGxvd2VyPW1pbm1vZCx1cHBlcj1tb2QxKSwgZGlyZWN0aW9uID0gImZvcndhcmQiLHRyYWNlPUYpDQpiYWNrd2FyZC5zdGVwIDwtIHN0ZXAobW9kMSwgc2NvcGUgPSBsaXN0KGxvd2VyPW1pbm1vZCx1cHBlcj1tb2QxKSwgZGlyZWN0aW9uID0gImJhY2t3YXJkIix0cmFjZT1GKQ0KDQpzdW1tYXJ5KG1vZDMpO3N1bW1hcnkoYmFja3dhcmQuc3RlcCkNCmBgYA0KKioqDQoNCiMjIyMgNS4gUmlkZ2UgUmVncmVzc2lvbi4gRmlyc3QsIHRoZSBvcHRpbWFsICRcbGFtYmRhJCB2YWx1ZSBtdXN0IHNlbGVjdGVkLCB3aGljaCBtaW5pbXplcyB0aGUgZ2VuZXJhbGl6ZWQgY3Jvc3MgdmFsaWRhdGlvbiAoR0NWKS4gVGhpcyB2YWx1ZSBpcyB0aGVuIHVzZWQgdG8gZmluZCB0aGUgbmV3IHZhbHVlcyBmb3IgY29lZmZpY2llbnRzLiBSZW1lbWJlciB0aGF0IFJpZGdlIHJlZ3Jlc3Npb24gZG9lcyBub3QgcGVyZm9ybSB2YXJpYWJsZSBzZWxlY3Rpb24sIG9ubHkgc2hyaW5rYWdlLiBUaGlzIGlzIGJlY2F1c2UgaXQgdXNlcyB0aGUgTDIgbm9ybSBmb3IgcGVuYWxpemF0aW9uLCB3aGljaCBkb2VzIG5vdCBtZWFzdXJlIHNwYXJzaXR5Lg0KDQpgYGB7cn0NCiNTY2FsaW5nDQpsaWJyYXJ5KE1BU1MpDQojbG0ucmlkZ2UgYXV0b21hdGljYWxseSBzY2FsZXMgdGhlIHByZWRpY3RvcnMsIGJ1dCBpZiB5b3Ugd2FudCB0byBkbyBpdCBtYW51YWxseSwgdXNlIGBzY2FsZSgpYCBmdW5jdGlvbiBhcyBzaG93biBiZWxvdw0KeS5zY2FsZWQgPSBzY2FsZSh0cmFpbkRhdGEkcHJvZGVidCkNClguc2NhbGVkID0gc2NhbGUoYXMubWF0cml4KHRyYWluRGF0YVssLTEzXSkpDQoNCiNSaWRnZSBSZWdyZXNzaW9uDQpsYW1iZGEgPSBzZXEoMTAwLCAxMTAsIGJ5PTAuMDEpDQptb2Q0ID0gbG0ucmlkZ2UocHJvZGVidH4uLGRhdGE9dHJhaW5EYXRhLCBsYW1iZGEgPSBsYW1iZGEpDQpvcHRpbWFsIDwtIHdoaWNoKG1vZDQkR0NWID09IG1pbihtb2Q0JEdDVikpDQoNCmNhdCgiVGhlIEdWQyBzY29yZSBpcyBtaW5pbWl6ZWQgd2hlbiBsYW1iZGEgaXMgZXF1YWwgdG8gIixuYW1lcyhvcHRpbWFsKSwiLCBtYWtpbmcgdGhpcyBvcHRpbWFsIGxhbWJkYSB2YWx1ZS5cblxuIixzZXA9IiIpDQoNCg0KY2F0KCJSaWRnZSBSZWdyZXNzaW9uIENvZWZmaWNpZW50cyBhdCB0aGlzIG9wdGltYWwgTGFtYmRhIFZhbHVlOlxuIikNCnJvdW5kKGNvZWYobW9kNClbd2hpY2gobW9kNCRHQ1YgPT0gbWluKG1vZDQkR0NWKSksXSw0KQ0KYGBgDQoqKioNCg0KIyMjIyA2LiBQZXJmb3JtIExBU1NPIFJlZ3Jlc3Npb24uIA0KDQpgYGB7ciwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9DQpsaWJyYXJ5KGdsbW5ldCkNCiNMaWtlIGxtLnJpZGdlLCB0aGlzIGZ1bmN0aW9uIGF1dG9tYXRpY2FsbHkgc2NhbGVzIHRoZSBwcmVkaWNvdHJzLg0KDQptb2Q1LmN2PWN2LmdsbW5ldChhcy5tYXRyaXgodHJhaW5EYXRhWywtMTNdKSx0cmFpbkRhdGEkcHJvZGVidCxhbHBoYT0xLG5mb2xkcz0xMCkNCm1vZDUgPSBnbG1uZXQoYXMubWF0cml4KHRyYWluRGF0YVssLTEzXSksdHJhaW5EYXRhJHByb2RlYnQsIGFscGhhID0gMSwgbmxhbWJkYSA9IDEwMCkNCm9wdGltYWwgPC1tb2Q1LmN2JGxhbWJkYS5taW4NCg0KY2F0KCJUaGUgb3B0aW1hbCBsYW1iZGEgdmFsdWUgaXMgIixvcHRpbWFsLCIuXG5cbiIsc2VwPSIiKQ0KDQpjYXQoIlRoZSB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgdGhlIExBU1NPIG1vZGVsIGFyZSBzaG93biBpbiB0aGUgdGFibGUgYmVsb3c6XG4iKQ0KY29lZihtb2Q1LHM9bW9kNS5jdiRsYW1iZGEubWluKQ0KDQpjYXQoIlxuV2UgY2FuIGFsc28gcGxvdCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudCBwYXRoIHRvIGdldCBhbiB1bmRlcnN0YW5kaW5nIG9mIGhvdyB2YWx1ZXMgd2VyZSBzZWxlY3RlZCBmb3IgZWFjaCBjb2VmZmljaWVudDpcbiIpDQpwbG90KG1vZDUseHZhcj0ibGFtYmRhIixsYWJlbD1UUlVFLGx3ZD0yKQ0KYWJsaW5lKHY9bG9nKG1vZDUuY3YkbGFtYmRhLm1pbiksY29sPSdibGFjaycsbHR5ID0gMixsd2Q9MikNCg0KYGBgDQoqKioNCg0KIyMjIyA3LiBFbGFzdGljIE5ldCBSZWdyZXNzaW9uIGlzIHBlcmZvcm1lZCB1c2luZyAxMDAgdmFsdWVzIGZvciAkXGxhbWJkYSQgYW5kIGdpdmluZyBlcXVhbCB3ZWlnaHQgdG8gYm90aCBwZW5hbHRpZXMuIDEwLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiBpcyB1c2VkIHRvIGZpbmQgdGhlIG9wdGltYWwgJFxsYW1iZGEkLiANCg0KYGBge3J9DQojQWdhaW4sIHByZWRpY3RvcnMgYXJlIHNjYWxlZCBhdXRvbWF0aWNhbGx5LiANCm1vZDYuY3Y9Y3YuZ2xtbmV0KGFzLm1hdHJpeCh0cmFpbkRhdGFbLC0xM10pLHRyYWluRGF0YSRwcm9kZWJ0LGFscGhhPTAuNSxuZm9sZHM9MTApDQptb2Q2ID0gZ2xtbmV0KGFzLm1hdHJpeCh0cmFpbkRhdGFbLC0xM10pLCB0cmFpbkRhdGEkcHJvZGVidCwgYWxwaGEgPSAwLjUsIG5sYW1iZGEgPSAxMDApDQpvcHRpbWFsIDwtIG1vZDYuY3YkbGFtYmRhLm1pbg0KY2F0KCJUaGUgb3B0aW1hbCBsYW1iZGEgdmFsdWUgaXMgIixvcHRpbWFsLCIuXG5cbiIsc2VwPSIiKQ0KDQpjYXQoIlRoZSB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgdGhlIExBU1NPIG1vZGVsIGFyZSBzaG93biBpbiB0aGUgdGFibGUgYmVsb3c6XG4iKQ0KY29lZihtb2Q2LHM9bW9kNi5jdiRsYW1iZGEubWluKQ0KYGBgDQoNCiMjIyMgOC4gUHJlZGljdCAqcHJvZGVidCogZm9yIGVhY2ggb2YgdGhlIHJvd3MgaW4gdGhlIHRlc3QgZGF0YSB1c2luZyB0aGUgZnVsbCBtb2RlbCwgbG93ZXN0IE1hbGxvdydzIENwIG1vZGVsLCBhbmQgdGhlIG1vZGVscyBmb3VuZCB1c2luZyBmb3J3YXJkIHN0ZXB3aXNlIHJlZ3Jlc3Npb24sIHJpZGdlIHJlZ3Jlc3Npb24sIGxhc3NvIHJlZ3Jlc3Npb24sIGFuZCBlbGFzdGljIG5ldC4NCg0KYGBge3J9DQpmdWxsPXByZWRpY3QobW9kMSx0ZXN0RGF0YSkNCm1pbmNwPXByZWRpY3QobW9kMix0ZXN0RGF0YSkNCnN0ZXB3aXNlPXByZWRpY3QobW9kMyx0ZXN0RGF0YSkNCnJpZGdlPWNiaW5kKDEsYXMubWF0cml4KHRlc3REYXRhWywtMTNdKSklKiVjb2VmKG1vZDQpW3doaWNoKG1vZDQkR0NWID09IG1pbihtb2Q0JEdDVikpLF0NCm1vZGxhc3NvPWxtKHByb2RlYnR+LixkYXRhPXRyYWluRGF0YVssLTRdKQ0KbGFzc289cHJlZGljdChtb2RsYXNzbyx0ZXN0RGF0YSkNCmVsYXN0aWM9YXMudmVjdG9yKHByZWRpY3QobW9kNixhcy5tYXRyaXgodGVzdERhdGFbLC0xM10pLHM9bW9kNi5jdiRsYW1iZGEubWluKSkNCg0KcHJlZHM9ZGF0YS5mcmFtZShwcm9kZWJ0PXRlc3REYXRhJHByb2RlYnQsZnVsbCxtaW5jcCxzdGVwd2lzZSxyaWRnZSxsYXNzbyxlbGFzdGljKQ0KcHJlZHMNCmBgYA0KDQpQcmVkaWN0aW9ucyBhcmUgY29tcGFyZWQgYmVsb3cgdXNpbmcgbWVhbiBzcXVhcmVkIHByZWRpY3Rpb24gZXJyb3IuIFRoZSBsb3dlc3QgTVNQRSBpbmRpY2F0ZXMgdGhlICJiZXN0IiBtb2RlbCwgYnV0IHRoZXNlIGFyZSBhbGwgc28gY2xvc2UgdGhhdCBpdCB3b3VsZCBiZSBkaWZmaWN1bHQgdG8gbWFrZSB0aGF0IGRldGVybWluYXRpb24gd2l0aCBhIHNpbmdsZSBtZXRyaWMuIA0KYGBge3J9DQpzYXBwbHkocHJlZHNbLC0xXSxmdW5jdGlvbih4KXttZWFuKCh4LXRlc3REYXRhJHByb2RlYnQpXjIpfSkNCmBgYA0KDQoNCiMjIyBBZGRpdGlvbmFsIE5vdGVzICMjIw0KUG9pc3NvbiBpcyBtYWlubHkgdXNlZCB0byBhZGRyZXNzIGNvdW50IGRhdGEgYWNyb3NzIGNvbnRpbnVvdXMgZXZlbnRzIHJhdGhlciB0aGFuIGEgZGlzY3JldGUgZXZlbnRzLiBGb3IgZXhhbXBsZSAtIEhvdyBtYW55IHN0dWRlbnQgcGFzcyBhIHRlc3QgaXMgYSBkaXNjcmV0ZSBldmVudC4gIHdoZXJlIGVhY2ggc3R1ZGVudCBoYXZlIGEgcHJvYmFiaWxpdHkgb2YgcGFzc2luZyBhIHRlc3Qgd2hlbiB0aGV5IHRha2UgaXQuIEhvdyBtYW55IGF3YXJkcyBkb2VzIGEgc3R1ZGVudCBnZXQgaW4gaGlnaCBzY2hvb2wgaXMgbXVjaCBtb3JlIGNvbnRpbnVvdXMsIG9yIGhvdyBtYW55IHBlb3BsZSBhcmUgaW4gYSBsaW5lIGluIHRoZSBhaXJwb3J0LiANCg0KDQoNCg0KDQoNCg==