Notes from Statistical Modeling in R I & II. Walk through on how to fit recursive partitioning models and utilize the statisticalModeling package.
RCODE IN PROGRESS
In this lecture, statistical models are defined as representation of reality with a purpose using mathematical properties. Models can be used to train data and explain variation.
Load the Mosaic library and use the mean function
library(ggplot2)
library(rpart)
library(dplyr)
library(rpart.plot)
library(statisticalModeling)
library(mosaic)
data(Gestation)
mean(gestation ~ smoke, data=Gestation, na.rm=TRUE)
0 1 2 3
279.6357 277.9792 281.3263 282.0700
Gestation$smoke[Gestation$smoke==2]=1
Gestation$smoke[Gestation$smoke==3]=0
Gestation$smoke <- as.factor(Gestation$smoke)
Designing & training model, evaluating, testing, interpreting
## -process by comparing models and evaluating
## -select model architecture to represent data
## -recursive partitioning (using rpart)
## -fit model
Evaluating Models
## linear models tells a linear story, while recursive follows the data pattern
## predictions will be different between the 2 architectures
build linear or recursive model: workflow
model1 <- lm()
construct an example of data to predict from
ex_values <- data.frame(x1=, x2=, data=df) ### predict ex_values using the evaluate_model function in the statisticalModeling package for better formating
mod1 <- lm(gestation~ wt.1+smoke+race+age,data=Gestation, na.action=na.omit)
values <- data.frame(wt.1=115, smoke="1", race=1, age=30, data=Gestation)
head(evaluate_model(mod1, values))
Chosing explanatory variables
### measuring how closely a model predicts y values:
### prediction error = $\sum{\hat{y}-y}$, mean squared errors gives us a value to evaluate
compute mean predictions of two models, subtract and square to see how much the predictions differ by; means would normally be compared between nested models.
((mean(predict(mod1), na.rm=TRUE) - (mean(predict(mod2), na.rm=TRUE))) ^2)
[1] 0.07270266
mod1_diff <- with(Gestation, gestation-(predict(mod1, newdata=Gestation)))
mod2_diff <- with(Gestation, gestation-(predict(mod2, newdata=Gestation)))
mean(mod1_diff^2, na.rm=TRUE)
[1] 247.0878
mean(mod2_diff^2, na.rm=TRUE)
[1] 244.1467
Examining MSE
mean(Gestation$gestation - predict(mod1, newdata=Gestation)^2, na.rm=TRUE)
[1] -77608.6
mean(Gestation$gestation - predict(mod2, newdata=Gestation)^2, na.rm=TRUE)
[1] -77763.2
Cross-validation: divide into train/test sets
use train data to build both models
calculate model outputs using the predict function
compare model outputs to actual data with test set
the tidy way; model_output within the evaluate_model saves outpout values from the dataframe
output <- evaluate_model(mod1, data=Gestation)
with(data=output, mean((Gestation$gestation - model_output)^2, na.rm=TRUE))
[1] 247.0878
Create a new variable that randomly assigns 75% of rows to train and the rest to test
row_count <- nrow(Gestation)
shuffled_rows <- sample(row_count)
train <- Gestation[head(shuffled_rows,floor(row_count*0.75)),]
train$train <- 1
train <- train[,-2:-23]
Gestation <- Gestation %>% left_join(train)
Gestation$train[is.na(Gestation$train)] <-0
prop.table(table(Gestation$train))
0 1
0.25 0.75
fit model to train and evaluate on test
mod1 <- lm(gestation~ wt.1+smoke+race+age,data=subset(Gestation, train==1), na.action=na.omit)
output <- evaluate_model(mod1, data = subset(Gestation, train==0))
Repeated random trials: cross validation varies by trial, therefore, repeating the procedure is a best practice
Compute the in sample error with training set.
in_sample_error <- with(evaluate_model(mod1, data=subset(Gestation, train==1)), mean((gestation-model_output)^2, na.rm=TRUE))
in_sample_error
[1] 246.556
Use the cv function to iterate over n trails
trials <- cv_pred_error(mod1, ntrials = 100)
mean(trials$mse)
[1] 249.5662
find confidence interval on trails compared to in_sample error
mosaic::t.test(~mse, mu=in_sample_error, data=trials)
One Sample t-test
data: mse
t = 46.877, df = 99, p-value < 2.2e-16
alternative hypothesis: true mean is not equal to 246.556
95 percent confidence interval:
249.4388 249.6937
sample estimates:
mean of x
249.5662
Comparing 2 models
trials <- cv_pred_error(mod1, mod2) # mod2 is the rpart model
names(trials)
[1] "mse" "model"
t.test(mse~model, data=trials)
Welch Two Sample t-test
data: mse by model
t = -6.9307, df = 4.201, p-value = 0.001895
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-25.64725 -11.17166
sample estimates:
mean in group mod1 mean in group mod2
249.9818 268.3912
Prediction error for categorical outcome
Work flow
Calculate error rate with true and false possitive
Return probabiliies instead of 0, 1
For each case extract the prob that the model assigned for each outcome, likelihood, and multiply each case together to get total likelihood of observations
process
- fit null model 1 predictors with a constant value
data("ptitanic", package="rpart.plot")
ptitanic$x <- 1
null <- rpart(survived~x, data=ptitanic)
- evaluate null model with evaluate_model function
null_out <-evaluate_model(null, data=ptitanic, type="class")
with(data=null_out, mean(survived !=model_output, na.rm=TRUE))
[1] 0.381971
null_out$rand_guess <- mosaic::shuffle(ptitanic$survived)
prop.table(table(null_out$rand_guess))
died survived
0.618029 0.381971
- compute error rate #### lower error rate value determines if null model does better than guessing at random
with (data=null_out, mean(survived !=rand_guess, na.rm=T))
[1] 0.487395
adding predictors and computing error rate (error rate drops with predictors compared to the null model)
mod <- rpart(survived~., data=ptitanic)
mod_out <- evaluate_model(mod, data=ptitanic, type="class")
with(data=mod_out, mean(survived != model_output, na.rm=T))
[1] 0.1749427
Exploring data for relationships:
Model covariates:
build two models, 1 w/ covariate and w/o
use evaluate_model to get predictions
subtract predicted values w/ & w/o holding covariate constant
mod1 <- lm(survived~pclass, data = ptitanic)
mod2 <- lm(survived~pclass+sex, data = ptitanic)
evaluate_model(mod1, pclass="1st")
evaluate_model(mod2, pclass="1st", sex="female") # with covariate
Effect sizes: How much is the change, or strength of association
using effect_size function from statisticalModeling
mod1 <- lm(gestation~smoke, data=Gestation)
mod2 <- lm(gestation~smoke+wt.1+age, data=Gestation)
effect_size(mod1, ~smoke)
effect_size(mod2, ~smoke+wt.1+age)
Part 2:
Using effect_size function in statisticalModeling to identify values to center
using fmodel function
build rpart model
mod3 <- rpart(gestation~smoke+wt.1+race, data=Gestation)
fmodel(mod3, ~wt.1+smoke+race, data=Gestation, race=c(5:7 )) # specify facets

Categorical response using evaluate_model
for model_output = 1,0
ptitanic$survived_f[ptitanic$survived=="survived"]=1
ptitanic$survived_f[ptitanic$survived=="died"]=0
ptitanic$survived_f <- as.factor(ptitanic$survived_f)
mod <- rpart(survived_f~pclass+sex, data=ptitanic, method="class")
eval_out1 <- evaluate_model(mod, type="class", pclass=c("1st","3rd"), sex="female")
eval_out1
for probabilities of y change type=“prop”
eval_out2 <- evaluate_model(mod, type="prob", pclass=c("1st","3rd"), sex="female")
eval_out2
Interaction workflow:
examine if interactions help model, use cross validation
build lm model and plot w/ & w/o interaction
mod1 <- lm(gestation~smoke+wt.1+race, data=Gestation, na.action =na.exclude)
mod2 <- lm(gestation~smoke*wt.1*race, data=Gestation, na.action =na.exclude)
fmodel(mod1) + ggplot2::ylab("survived")

fmodel(mod2) + ggplot2::ylab("survived")

cross validate & compare prediction errors
res <- cv_pred_error(mod1, mod2)
names(res)
[1] "mse" "model"
t.test(mse~model, data=res)
Welch Two Sample t-test
data: mse by model
t = -2.6061, df = 4.8822, p-value = 0.04901
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-2.710891798 -0.008725852
sample estimates:
mean in group mod1 mean in group mod2
249.3320 250.6918
Total & Partial Change:
to compare effect sizes, make sure units are on the same level
ex. price of car = age of car + mileage of car
effect size of age is dollars to year or dollars/year unit level
effect size of miles is dollars to mile, dollars/mile unit level
covert units to same scale by multiplying slopes, and taking an average value of miles per year (10k)
slope(dollars/mile) * 10000(miles/year)=slope(dollars/year
sometimes it is not possible to change the units of one variable w/o affecting the other.
partial change: impact on the response of changing 1 unit holding all other inputs constant
evaluate model using evaluate_model function from statisticalModeling, assign values to explanatory variables
evaluate_model(x=, z=)
R squared: Coefficient of determination
fraction of variation in the response variable accounted for by the model
R squared not as useful for predicting of inferece since the value changes with models and values depends on the context of data
alternatives;
prediction purpose = cross validated prediction error or MSE from cross validation
inference purpose = effect sizes & confidence intervals
calculate R-squared for a model
mod1<-lm() out <- evaluate_model(mod1, data=dd) ## R-squared variance of model response prediction / var(y) with(out, var(model_output)/ var(y)
plot MSE from both models in a boxplot
cv_pred_error function from statisticalModeling subsets MSE prediction error
boxplot(mse~model, data=cv_pred_error(model_1, model_2))
Degrees of Freedom
difference between df in the data and df in the model is the residual degrees of freedom
## models with several residual degrees of freedom are not reliable
length(unique(x)) # gives us the number of levels in a continuous variable
cross validation, can also run several trials
cv_results <- cv_pred_error(mod1, mode2, ntrials=n)
bootstrapping and precision (resampling method)
Confidence intervals are a representation of precision
bootstrap when data is not sufficient to study precision
idea = use 1 dataset to simulate many datasets as if they were sampled from the population
bootstraping for qunatifying many results on an effect size
work flow for constructing 1 resampled data set
mod <- lm() effect_size(mod, ~x) # construct a resampling of the df and save df by selecting row numbers at random from the original dataset trial_indices <- sample(1:nrow(df), replace=T) trial_data <- df[trial_indices,]
train the model to the resampled dataset
trial_mod <- lm(, data=trial_data) # compute effect size effect_size(trial_mod, ~x)
construct 100 resampled data sets using the ensemble function from the statisticalModeling package
mod <- lm() trial <- ensemble(mod, nreps=100) trial_effect_size <- effect_size(trial, ~x) # compute SD of the 100 effect sizes sd(trial_effect_size\(slope) # plot effect sizes hist(trial_effect_sizes\)slope)
Modeling exponential change over time
mod <- lm(salary~year) # a linear model fails to capture the data ## plotting can show this failutre fmodel(mod, data=df) +geom_point(data=df) # take log transformation of y
Confidence & Collinearity
LS0tCnRpdGxlOiAiU3RhdGlzdGljYWwgTW9kZWxpbmcgaW4gUiAtIERhdGEgQ2FtcCBub3RlcyIKYXV0aG9yOiAiS2V2aW4gTC4iCmRhdGU6ICJKdWx5IDI2LCAyMDE4IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMgTm90ZXMgZnJvbSBTdGF0aXN0aWNhbCBNb2RlbGluZyBpbiBSIEkgJiBJSS4gV2FsayB0aHJvdWdoIG9uIGhvdyB0byBmaXQgcmVjdXJzaXZlIHBhcnRpdGlvbmluZyBtb2RlbHMgYW5kIHV0aWxpemUgdGhlIHN0YXRpc3RpY2FsTW9kZWxpbmcgcGFja2FnZS4KCiMjIFJDT0RFIElOIFBST0dSRVNTCgojIyBJbiB0aGlzIGxlY3R1cmUsIHN0YXRpc3RpY2FsIG1vZGVscyBhcmUgZGVmaW5lZCBhcyByZXByZXNlbnRhdGlvbiBvZiByZWFsaXR5IHdpdGggYSBwdXJwb3NlIHVzaW5nIG1hdGhlbWF0aWNhbCBwcm9wZXJ0aWVzLiBNb2RlbHMgY2FuIGJlIHVzZWQgdG8gdHJhaW4gZGF0YSBhbmQgZXhwbGFpbiB2YXJpYXRpb24uCgojIyMgTG9hZCB0aGUgTW9zYWljIGxpYnJhcnkgYW5kIHVzZSB0aGUgbWVhbiBmdW5jdGlvbgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJwYXJ0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmxpYnJhcnkoc3RhdGlzdGljYWxNb2RlbGluZykKbGlicmFyeShtb3NhaWMpCmRhdGEoR2VzdGF0aW9uKQptZWFuKGdlc3RhdGlvbiB+IHNtb2tlLCBkYXRhPUdlc3RhdGlvbiwgbmEucm09VFJVRSkKR2VzdGF0aW9uJHNtb2tlW0dlc3RhdGlvbiRzbW9rZT09Ml09MQpHZXN0YXRpb24kc21va2VbR2VzdGF0aW9uJHNtb2tlPT0zXT0wCkdlc3RhdGlvbiRzbW9rZSA8LSBhcy5mYWN0b3IoR2VzdGF0aW9uJHNtb2tlKQpgYGAKCiMgRGVzaWduaW5nICYgdHJhaW5pbmcgbW9kZWwsIGV2YWx1YXRpbmcsIHRlc3RpbmcsIGludGVycHJldGluZwoJIyMgLXByb2Nlc3MgYnkgY29tcGFyaW5nIG1vZGVscyBhbmQgZXZhbHVhdGluZwoJIyMgLXNlbGVjdCBtb2RlbCBhcmNoaXRlY3R1cmUgdG8gcmVwcmVzZW50IGRhdGEKCSMjIC1yZWN1cnNpdmUgcGFydGl0aW9uaW5nICh1c2luZyBycGFydCkKCSMjIC1maXQgbW9kZWwgCiMgRXZhbHVhdGluZyBNb2RlbHMKCSMjIGxpbmVhciBtb2RlbHMgdGVsbHMgYSBsaW5lYXIgc3RvcnksIHdoaWxlIHJlY3Vyc2l2ZSBmb2xsb3dzIHRoZSBkYXRhIHBhdHRlcm4KCSMjIHByZWRpY3Rpb25zIHdpbGwgYmUgZGlmZmVyZW50IGJldHdlZW4gdGhlIDIgYXJjaGl0ZWN0dXJlcwojIGJ1aWxkIGxpbmVhciBvciByZWN1cnNpdmUgbW9kZWw6IHdvcmtmbG93CgojIyBtb2RlbDEgPC0gbG0oKQojIyMgY29uc3RydWN0IGFuIGV4YW1wbGUgb2YgZGF0YSB0byBwcmVkaWN0IGZyb20KZXhfdmFsdWVzIDwtIGRhdGEuZnJhbWUoeDE9LCB4Mj0sIGRhdGE9ZGYpCiMjIyBwcmVkaWN0IGV4X3ZhbHVlcyB1c2luZyB0aGUgIGV2YWx1YXRlX21vZGVsIGZ1bmN0aW9uIGluIHRoZSBzdGF0aXN0aWNhbE1vZGVsaW5nIHBhY2thZ2UgZm9yIGJldHRlciBmb3JtYXRpbmcKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2QxIDwtIGxtKGdlc3RhdGlvbn4gd3QuMStzbW9rZStyYWNlK2FnZSxkYXRhPUdlc3RhdGlvbiwgbmEuYWN0aW9uPW5hLm9taXQpCnZhbHVlcyA8LSBkYXRhLmZyYW1lKHd0LjE9MTE1LCBzbW9rZT0iMSIsIHJhY2U9MSwgYWdlPTMwLCBkYXRhPUdlc3RhdGlvbikKaGVhZChldmFsdWF0ZV9tb2RlbChtb2QxLCB2YWx1ZXMpKQpgYGAKCgojIyBHb2FsIG9mIGV2YWx1YXRpbmcgbW9kZWw6IGV4dHJhcG9sYXR0aW9uIC0gZmluZGluZyBkYXRhIG91dHNpZGUgdGhlIHJhbmdlIG9mIHRoZSBkYXRhIHVzZWQgdG8gdHJhaW4gbW9kZWwKIyMjIEV2YWx1YXRlIG1vZGVsIHVzaW5nIHRoZSBldmFsdWF0ZV9tb2RlbCBmdW5jdGlvbiBnaXZlcyBvdXRwdXQgb24gdHJhaW5pbmcgZGF0YQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9kMiA8LSBycGFydChnZXN0YXRpb25+d3QuMStzbW9rZStyYWNlK2FnZSxkYXRhPUdlc3RhdGlvbiwgbWV0aG9kPSJhbm92YSIsIGNwPS4wMDUsIG5hLmFjdGlvbiA9IG5hLnJwYXJ0KQoKZm1vZGVsKG1vZDEpCmZtb2RlbChtb2QyKQojIHByZWRpY3QgdmFsdWVzIAojIHVzZSB0aGUgZXZhbHVhdGVfbW9kZWwgZnVuY3Rpb24gaW4gdGhlIHN0YXRpc3RpY2FsTW9kZWxpbmcgcGFja2FnZSBmb3IgYmV0dGVyIGZvcm1hdGluZwpoZWFkKGV2YWx1YXRlX21vZGVsKG1vZDEpKQpoZWFkKGV2YWx1YXRlX21vZGVsKG1vZDIpKQoKcnBhcnQucGxvdChtb2QyLCB5ZXNubz0yKQpgYGAKCgoKCgoKIyMgQ2hvc2luZyBleHBsYW5hdG9yeSB2YXJpYWJsZXMKCSMjIyBtZWFzdXJpbmcgaG93IGNsb3NlbHkgYSBtb2RlbCBwcmVkaWN0cyB5IHZhbHVlczoKCSMjIyBwcmVkaWN0aW9uIGVycm9yID0gJFxzdW17XGhhdHt5fS15fSQsIG1lYW4gc3F1YXJlZCBlcnJvcnMgZ2l2ZXMgdXMgYSB2YWx1ZSB0byBldmFsdWF0ZQojIyMgY29tcHV0ZSBtZWFuIHByZWRpY3Rpb25zIG9mIHR3byBtb2RlbHMsIHN1YnRyYWN0IGFuZCBzcXVhcmUgdG8gc2VlIGhvdyBtdWNoIHRoZSBwcmVkaWN0aW9ucyBkaWZmZXIgYnk7IG1lYW5zIHdvdWxkIG5vcm1hbGx5IGJlIGNvbXBhcmVkIGJldHdlZW4gbmVzdGVkIG1vZGVscy4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQooKG1lYW4ocHJlZGljdChtb2QxKSwgbmEucm09VFJVRSkgLSAobWVhbihwcmVkaWN0KG1vZDIpLCBuYS5ybT1UUlVFKSkpIF4yKQptb2QxX2RpZmYgPC0gd2l0aChHZXN0YXRpb24sIGdlc3RhdGlvbi0ocHJlZGljdChtb2QxLCBuZXdkYXRhPUdlc3RhdGlvbikpKQptb2QyX2RpZmYgPC0gd2l0aChHZXN0YXRpb24sIGdlc3RhdGlvbi0ocHJlZGljdChtb2QyLCBuZXdkYXRhPUdlc3RhdGlvbikpKQptZWFuKG1vZDFfZGlmZl4yLCBuYS5ybT1UUlVFKQptZWFuKG1vZDJfZGlmZl4yLCBuYS5ybT1UUlVFKQpgYGAKCgojIyBFeGFtaW5pbmcgTVNFCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptZWFuKEdlc3RhdGlvbiRnZXN0YXRpb24gLSBwcmVkaWN0KG1vZDEsIG5ld2RhdGE9R2VzdGF0aW9uKV4yLCBuYS5ybT1UUlVFKQptZWFuKEdlc3RhdGlvbiRnZXN0YXRpb24gLSBwcmVkaWN0KG1vZDIsIG5ld2RhdGE9R2VzdGF0aW9uKV4yLCBuYS5ybT1UUlVFKQpgYGAKCiMjIENyb3NzLXZhbGlkYXRpb246IGRpdmlkZSBpbnRvIHRyYWluL3Rlc3Qgc2V0cwojIyMgdXNlIHRyYWluIGRhdGEgdG8gYnVpbGQgYm90aCBtb2RlbHMKIyMjIGNhbGN1bGF0ZSBtb2RlbCBvdXRwdXRzIHVzaW5nIHRoZSBwcmVkaWN0IGZ1bmN0aW9uCiMjIyBjb21wYXJlIG1vZGVsIG91dHB1dHMgdG8gYWN0dWFsIGRhdGEgd2l0aCB0ZXN0IHNldAojIyMjIHRoZSB0aWR5IHdheTsgbW9kZWxfb3V0cHV0IHdpdGhpbiB0aGUgZXZhbHVhdGVfbW9kZWwgc2F2ZXMgb3V0cG91dCB2YWx1ZXMgZnJvbSB0aGUgZGF0YWZyYW1lCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpvdXRwdXQgPC0gZXZhbHVhdGVfbW9kZWwobW9kMSwgZGF0YT1HZXN0YXRpb24pCndpdGgoZGF0YT1vdXRwdXQsIG1lYW4oKEdlc3RhdGlvbiRnZXN0YXRpb24gLSBtb2RlbF9vdXRwdXQpXjIsIG5hLnJtPVRSVUUpKQpgYGAKCiMjIyBDcmVhdGUgYSBuZXcgdmFyaWFibGUgdGhhdCByYW5kb21seSBhc3NpZ25zIDc1JSBvZiByb3dzIHRvIHRyYWluIGFuZCB0aGUgcmVzdCB0byB0ZXN0CmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpyb3dfY291bnQgPC0gbnJvdyhHZXN0YXRpb24pCnNodWZmbGVkX3Jvd3MgPC0gc2FtcGxlKHJvd19jb3VudCkKdHJhaW4gPC0gR2VzdGF0aW9uW2hlYWQoc2h1ZmZsZWRfcm93cyxmbG9vcihyb3dfY291bnQqMC43NSkpLF0KdHJhaW4kdHJhaW4gPC0gMQp0cmFpbiA8LSB0cmFpblssLTI6LTIzXQpHZXN0YXRpb24gPC0gR2VzdGF0aW9uICU+JSBsZWZ0X2pvaW4odHJhaW4pCkdlc3RhdGlvbiR0cmFpbltpcy5uYShHZXN0YXRpb24kdHJhaW4pXSA8LTAKcHJvcC50YWJsZSh0YWJsZShHZXN0YXRpb24kdHJhaW4pKQpgYGAKIyMjIGZpdCBtb2RlbCB0byB0cmFpbiBhbmQgZXZhbHVhdGUgb24gdGVzdApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9kMSA8LSBsbShnZXN0YXRpb25+IHd0LjErc21va2UrcmFjZSthZ2UsZGF0YT1zdWJzZXQoR2VzdGF0aW9uLCB0cmFpbj09MSksIG5hLmFjdGlvbj1uYS5vbWl0KQpvdXRwdXQgPC0gZXZhbHVhdGVfbW9kZWwobW9kMSwgZGF0YSA9IHN1YnNldChHZXN0YXRpb24sIHRyYWluPT0wKSkKYGBgCgoKCiMjIyBSZXBlYXRlZCByYW5kb20gdHJpYWxzOiBjcm9zcyB2YWxpZGF0aW9uIHZhcmllcyBieSB0cmlhbCwgdGhlcmVmb3JlLCByZXBlYXRpbmcgdGhlIHByb2NlZHVyZSBpcyBhIGJlc3QgcHJhY3RpY2UKIyMjIyBDb21wdXRlIHRoZSBpbiBzYW1wbGUgZXJyb3Igd2l0aCB0cmFpbmluZyBzZXQuCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQppbl9zYW1wbGVfZXJyb3IgPC0gd2l0aChldmFsdWF0ZV9tb2RlbChtb2QxLCBkYXRhPXN1YnNldChHZXN0YXRpb24sIHRyYWluPT0xKSksIG1lYW4oKGdlc3RhdGlvbi1tb2RlbF9vdXRwdXQpXjIsIG5hLnJtPVRSVUUpKQppbl9zYW1wbGVfZXJyb3IKYGBgCgojIyMgVXNlIHRoZSBjdiBmdW5jdGlvbiB0byBpdGVyYXRlIG92ZXIgbiB0cmFpbHMKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnRyaWFscyA8LSBjdl9wcmVkX2Vycm9yKG1vZDEsIG50cmlhbHMgPSAxMDApIAptZWFuKHRyaWFscyRtc2UpCmBgYAoKIyBmaW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb24gdHJhaWxzIGNvbXBhcmVkIHRvIGluX3NhbXBsZSBlcnJvcgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9zYWljOjp0LnRlc3Qofm1zZSwgbXU9aW5fc2FtcGxlX2Vycm9yLCBkYXRhPXRyaWFscykKYGBgCgojIyMgQ29tcGFyaW5nIDIgbW9kZWxzCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQp0cmlhbHMgPC0gY3ZfcHJlZF9lcnJvcihtb2QxLCBtb2QyKSAjIG1vZDIgaXMgdGhlIHJwYXJ0IG1vZGVsCm5hbWVzKHRyaWFscykKdC50ZXN0KG1zZX5tb2RlbCwgZGF0YT10cmlhbHMpCmBgYAoKCgoKIyMgUHJlZGljdGlvbiBlcnJvciBmb3IgY2F0ZWdvcmljYWwgb3V0Y29tZQojIyMgV29yayBmbG93CiMjIyMgQ2FsY3VsYXRlIGVycm9yIHJhdGUgd2l0aCB0cnVlIGFuZCBmYWxzZSBwb3NzaXRpdmUKIyMjIyBSZXR1cm4gcHJvYmFiaWxpaWVzIGluc3RlYWQgb2YgMCwgMQojIyMjIEZvciBlYWNoIGNhc2UgZXh0cmFjdCB0aGUgcHJvYiB0aGF0IHRoZSBtb2RlbCBhc3NpZ25lZCBmb3IgZWFjaCBvdXRjb21lLCBsaWtlbGlob29kLCBhbmQgbXVsdGlwbHkgZWFjaCBjYXNlIHRvZ2V0aGVyIHRvIGdldCB0b3RhbCBsaWtlbGlob29kIG9mIG9ic2VydmF0aW9ucwoKIyMjIHByb2Nlc3MKKiAxLiBmaXQgbnVsbCBtb2RlbCAxIHByZWRpY3RvcnMgd2l0aCBhIGNvbnN0YW50IHZhbHVlCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhKCJwdGl0YW5pYyIsIHBhY2thZ2U9InJwYXJ0LnBsb3QiKQpwdGl0YW5pYyR4IDwtIDEKbnVsbCA8LSBycGFydChzdXJ2aXZlZH54LCBkYXRhPXB0aXRhbmljKQpgYGAKCiogMi4gIGV2YWx1YXRlIG51bGwgbW9kZWwgd2l0aCBldmFsdWF0ZV9tb2RlbCBmdW5jdGlvbgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbnVsbF9vdXQgPC1ldmFsdWF0ZV9tb2RlbChudWxsLCBkYXRhPXB0aXRhbmljLCB0eXBlPSJjbGFzcyIpCmBgYAoKKiAzLiBjYWxjdWxhdGUgZXJyb3IgcmF0ZQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kd2l0aChkYXRhPW51bGxfb3V0LCBtZWFuKHN1cnZpdmVkICE9bW9kZWxfb3V0cHV0LCBuYS5ybT1UUlVFKSkKYGBgCgoqIDQuIGdlbmVyYXRlIHJhbmRvbSBndWVzcwpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbnVsbF9vdXQkcmFuZF9ndWVzcyA8LSBtb3NhaWM6OnNodWZmbGUocHRpdGFuaWMkc3Vydml2ZWQpCnByb3AudGFibGUodGFibGUobnVsbF9vdXQkcmFuZF9ndWVzcykpCmBgYAoKKiA1LiBjb21wdXRlIGVycm9yIHJhdGUKIyMjIyBsb3dlciBlcnJvciByYXRlIHZhbHVlIGRldGVybWluZXMgaWYgbnVsbCBtb2RlbCBkb2VzIGJldHRlciB0aGFuIGd1ZXNzaW5nIGF0IHJhbmRvbSAKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CndpdGggKGRhdGE9bnVsbF9vdXQsIG1lYW4oc3Vydml2ZWQgIT1yYW5kX2d1ZXNzLCBuYS5ybT1UKSkKYGBgCgogCiMjIGFkZGluZyBwcmVkaWN0b3JzIGFuZCBjb21wdXRpbmcgZXJyb3IgcmF0ZSAoZXJyb3IgcmF0ZSBkcm9wcyB3aXRoIHByZWRpY3RvcnMgY29tcGFyZWQgdG8gdGhlIG51bGwgbW9kZWwpCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2QgPC0gcnBhcnQoc3Vydml2ZWR+LiwgZGF0YT1wdGl0YW5pYykKbW9kX291dCA8LSBldmFsdWF0ZV9tb2RlbChtb2QsIGRhdGE9cHRpdGFuaWMsIHR5cGU9ImNsYXNzIikKd2l0aChkYXRhPW1vZF9vdXQsIG1lYW4oc3Vydml2ZWQgIT0gbW9kZWxfb3V0cHV0LCBuYS5ybT1UKSkKYGBgCgoKIyMgRXhwbG9yaW5nIGRhdGEgZm9yIHJlbGF0aW9uc2hpcHM6CiMjIyBwbG90aW5nIGRhdGEgd2l0aCBwcnAgKHJwYXJ0LnBsb3QgcGFja2FnZSksIHBsb3RzIGFzIGEgdHJlZSB3aXRoIGEgZm9ybWF0IHNwZWNpZmllZCBieSAidHlwZSIKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnBycChtb2QsIHR5cGU9MykKYGBgCgoKIyMgTW9kZWwgY292YXJpYXRlczoKIyMjIGJ1aWxkIHR3byBtb2RlbHMsIDEgdy8gY292YXJpYXRlIGFuZCB3L28KIyMjdXNlIGV2YWx1YXRlX21vZGVsIHRvIGdldCBwcmVkaWN0aW9ucwojIyMgc3VidHJhY3QgcHJlZGljdGVkIHZhbHVlcyB3LyAmIHcvbyBob2xkaW5nIGNvdmFyaWF0ZSBjb25zdGFudAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1vZDEgPC0gbG0oc3Vydml2ZWR+cGNsYXNzLCBkYXRhID0gcHRpdGFuaWMpCm1vZDIgPC0gbG0oc3Vydml2ZWR+cGNsYXNzK3NleCwgZGF0YSA9IHB0aXRhbmljKQoKZXZhbHVhdGVfbW9kZWwobW9kMSwgcGNsYXNzPSIxc3QiKSAKZXZhbHVhdGVfbW9kZWwobW9kMiwgcGNsYXNzPSIxc3QiLCBzZXg9ImZlbWFsZSIpICMgd2l0aCBjb3ZhcmlhdGUKCmBgYAoKCgojIyMgRWZmZWN0IHNpemVzOiBIb3cgbXVjaCBpcyB0aGUgY2hhbmdlLCBvciBzdHJlbmd0aCBvZiBhc3NvY2lhdGlvbgojIyMjIHVzaW5nIGVmZmVjdF9zaXplIGZ1bmN0aW9uIGZyb20gc3RhdGlzdGljYWxNb2RlbGluZwpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9kMSA8LSBsbShnZXN0YXRpb25+c21va2UsIGRhdGE9R2VzdGF0aW9uKQptb2QyIDwtIGxtKGdlc3RhdGlvbn5zbW9rZSt3dC4xK2FnZSwgZGF0YT1HZXN0YXRpb24pCmVmZmVjdF9zaXplKG1vZDEsIH5zbW9rZSkKZWZmZWN0X3NpemUobW9kMiwgfnNtb2tlK3d0LjErYWdlKQpgYGAKCgoKCgpQYXJ0IDI6CgojIyBVc2luZyBlZmZlY3Rfc2l6ZSBmdW5jdGlvbiBpbiBzdGF0aXN0aWNhbE1vZGVsaW5nIHRvIGlkZW50aWZ5IHZhbHVlcyB0byBjZW50ZXIKIyMjIHVzaW5nIGZtb2RlbCBmdW5jdGlvbgojIyMjIyBidWlsZCBycGFydCBtb2RlbApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9kMyA8LSBycGFydChnZXN0YXRpb25+c21va2Urd3QuMStyYWNlLCBkYXRhPUdlc3RhdGlvbikKCmZtb2RlbChtb2QzLCB+d3QuMStzbW9rZStyYWNlLCBkYXRhPUdlc3RhdGlvbiwgIHJhY2U9Yyg1OjcgKSkgIyBzcGVjaWZ5IGZhY2V0cwpgYGAKCgoKIyMjIENhdGVnb3JpY2FsIHJlc3BvbnNlIHVzaW5nIGV2YWx1YXRlX21vZGVsCiMjIyMgZm9yIG1vZGVsX291dHB1dCA9IDEsMApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcHRpdGFuaWMkc3Vydml2ZWRfZltwdGl0YW5pYyRzdXJ2aXZlZD09InN1cnZpdmVkIl09MQpwdGl0YW5pYyRzdXJ2aXZlZF9mW3B0aXRhbmljJHN1cnZpdmVkPT0iZGllZCJdPTAKcHRpdGFuaWMkc3Vydml2ZWRfZiA8LSBhcy5mYWN0b3IocHRpdGFuaWMkc3Vydml2ZWRfZikKbW9kIDwtIHJwYXJ0KHN1cnZpdmVkX2Z+cGNsYXNzK3NleCwgZGF0YT1wdGl0YW5pYywgbWV0aG9kPSJjbGFzcyIpCmV2YWxfb3V0MSA8LSBldmFsdWF0ZV9tb2RlbChtb2QsIHR5cGU9ImNsYXNzIiwgcGNsYXNzPWMoIjFzdCIsIjNyZCIpLCBzZXg9ImZlbWFsZSIpCmV2YWxfb3V0MQoKYGBgCgojIyMjIGZvciBwcm9iYWJpbGl0aWVzIG9mIHkgY2hhbmdlIHR5cGU9InByb3AiCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpldmFsX291dDIgPC0gZXZhbHVhdGVfbW9kZWwobW9kLCB0eXBlPSJwcm9iIiwgcGNsYXNzPWMoIjFzdCIsIjNyZCIpLCBzZXg9ImZlbWFsZSIpCmV2YWxfb3V0MgpgYGAKCgojIyMgSW50ZXJhY3Rpb24gd29ya2Zsb3c6CiMjIyMgZXhhbWluZSBpZiBpbnRlcmFjdGlvbnMgaGVscCBtb2RlbCwgdXNlIGNyb3NzIHZhbGlkYXRpb24KIyMjIyBidWlsZCBsbSBtb2RlbCBhbmQgcGxvdCB3LyAmIHcvbyBpbnRlcmFjdGlvbgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9kMSA8LSBsbShnZXN0YXRpb25+c21va2Urd3QuMStyYWNlLCBkYXRhPUdlc3RhdGlvbiwgbmEuYWN0aW9uID1uYS5leGNsdWRlKQptb2QyIDwtIGxtKGdlc3RhdGlvbn5zbW9rZSp3dC4xKnJhY2UsIGRhdGE9R2VzdGF0aW9uLCBuYS5hY3Rpb24gPW5hLmV4Y2x1ZGUpCmZtb2RlbChtb2QxKSArIGdncGxvdDI6OnlsYWIoInN1cnZpdmVkIikKZm1vZGVsKG1vZDIpICsgZ2dwbG90Mjo6eWxhYigic3Vydml2ZWQiKQoKCmBgYAoKCiMjIyMgY3Jvc3MgdmFsaWRhdGUgJiBjb21wYXJlIHByZWRpY3Rpb24gZXJyb3JzCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpyZXMgPC0gY3ZfcHJlZF9lcnJvcihtb2QxLCBtb2QyKQpuYW1lcyhyZXMpCnQudGVzdChtc2V+bW9kZWwsIGRhdGE9cmVzKQpgYGAKCgoKCiMjIyBUb3RhbCAmIFBhcnRpYWwgQ2hhbmdlOgojIyB0byBjb21wYXJlIGVmZmVjdCBzaXplcywgbWFrZSBzdXJlIHVuaXRzIGFyZSBvbiB0aGUgc2FtZSBsZXZlbAojIyMgZXguIHByaWNlIG9mIGNhciA9IGFnZSBvZiBjYXIgKyBtaWxlYWdlIG9mIGNhcgojIyMgZWZmZWN0IHNpemUgb2YgYWdlIGlzIGRvbGxhcnMgdG8geWVhciBvciBkb2xsYXJzL3llYXIgdW5pdCBsZXZlbAojIyMgZWZmZWN0IHNpemUgb2YgbWlsZXMgaXMgZG9sbGFycyB0byBtaWxlLCBkb2xsYXJzL21pbGUgdW5pdCBsZXZlbAojIyMgY292ZXJ0IHVuaXRzIHRvIHNhbWUgc2NhbGUgYnkgbXVsdGlwbHlpbmcgc2xvcGVzLCBhbmQgdGFraW5nIGFuIGF2ZXJhZ2UgdmFsdWUgb2YgbWlsZXMgcGVyIHllYXIgKDEwaykKIyMjIHNsb3BlKGRvbGxhcnMvbWlsZSkgKiAxMDAwMChtaWxlcy95ZWFyKT1zbG9wZShkb2xsYXJzL3llYXIKIyMjIHNvbWV0aW1lcyBpdCBpcyBub3QgcG9zc2libGUgdG8gY2hhbmdlIHRoZSB1bml0cyBvZiBvbmUgdmFyaWFibGUgdy9vIGFmZmVjdGluZyB0aGUgb3RoZXIuCiMjIyBwYXJ0aWFsIGNoYW5nZTogaW1wYWN0IG9uIHRoZSByZXNwb25zZSBvZiBjaGFuZ2luZyAxIHVuaXQgaG9sZGluZyBhbGwgb3RoZXIgaW5wdXRzIGNvbnN0YW50CiMjIyB0b3RhbCBjaGFuZ2U6IGltcGFjdCBvbiB0aGUgcmVzcG9uc2Ugb2YgY2hhbmdpbmcgMSBpbnB1dCB3aGlsZSBsZXR0aW5nIG90aGVycyBiZSBmcmVlbHkgZXN0aW1hdGVkCiMjIyMgbGVhdmUgb3V0IGltcG9ydGFudCB2YXJpYWJsZSBmcm9tIG1vZGVsIHRvIGVzdGltYXRlIGVmZmVjdCBzaXplIG9mIGFub3RoZXIgdmFyaWFibGUKCiMjIGV2YWx1YXRlIG1vZGVsIHVzaW5nIGV2YWx1YXRlX21vZGVsIGZ1bmN0aW9uIGZyb20gc3RhdGlzdGljYWxNb2RlbGluZywgYXNzaWduIHZhbHVlcyB0byBleHBsYW5hdG9yeSB2YXJpYWJsZXMKZXZhbHVhdGVfbW9kZWwoeD0sIHo9KQoKCiMgUiBzcXVhcmVkOiBDb2VmZmljaWVudCBvZiBkZXRlcm1pbmF0aW9uCiMjIGZyYWN0aW9uIG9mIHZhcmlhdGlvbiBpbiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgYWNjb3VudGVkIGZvciBieSB0aGUgbW9kZWwKIyMgUiBzcXVhcmVkIG5vdCBhcyB1c2VmdWwgZm9yIHByZWRpY3Rpbmcgb2YgaW5mZXJlY2Ugc2luY2UgdGhlIHZhbHVlIGNoYW5nZXMgd2l0aCBtb2RlbHMgYW5kIHZhbHVlcyBkZXBlbmRzIG9uIHRoZSBjb250ZXh0IG9mIGRhdGEKIyMjIGFsdGVybmF0aXZlczsKIyMjIyBwcmVkaWN0aW9uIHB1cnBvc2UgPSBjcm9zcyB2YWxpZGF0ZWQgcHJlZGljdGlvbiBlcnJvciBvciBNU0UgZnJvbSBjcm9zcyB2YWxpZGF0aW9uCiMjIyMgaW5mZXJlbmNlIHB1cnBvc2UgPSAgZWZmZWN0IHNpemVzICYgY29uZmlkZW5jZSBpbnRlcnZhbHMKCiMjIyBjYWxjdWxhdGUgUi1zcXVhcmVkIGZvciBhIG1vZGVsCm1vZDE8LWxtKCkKb3V0IDwtIGV2YWx1YXRlX21vZGVsKG1vZDEsIGRhdGE9ZGQpCiMjIFItc3F1YXJlZCB2YXJpYW5jZSBvZiBtb2RlbCByZXNwb25zZSBwcmVkaWN0aW9uIC8gdmFyKHkpCndpdGgob3V0LCB2YXIobW9kZWxfb3V0cHV0KS8gdmFyKHkpCgojIHBsb3QgTVNFIGZyb20gYm90aCBtb2RlbHMgaW4gYSBib3hwbG90CiMjIGN2X3ByZWRfZXJyb3IgZnVuY3Rpb24gZnJvbSBzdGF0aXN0aWNhbE1vZGVsaW5nIHN1YnNldHMgTVNFIHByZWRpY3Rpb24gZXJyb3IKYm94cGxvdChtc2V+bW9kZWwsIGRhdGE9Y3ZfcHJlZF9lcnJvcihtb2RlbF8xLCBtb2RlbF8yKSkKCiMgRGVncmVlcyBvZiBGcmVlZG9tCiMjIGRpZmZlcmVuY2UgYmV0d2VlbiBkZiBpbiB0aGUgZGF0YSBhbmQgZGYgaW4gdGhlIG1vZGVsIGlzIHRoZSByZXNpZHVhbCBkZWdyZWVzIG9mIGZyZWVkb20KCSMjIG1vZGVscyB3aXRoIHNldmVyYWwgcmVzaWR1YWwgZGVncmVlcyBvZiBmcmVlZG9tIGFyZSBub3QgcmVsaWFibGUKCiMjIyBsZW5ndGgodW5pcXVlKHgpKSAjIGdpdmVzIHVzIHRoZSBudW1iZXIgb2YgbGV2ZWxzIGluIGEgY29udGludW91cyB2YXJpYWJsZQoKIyMjIGNyb3NzIHZhbGlkYXRpb24sIGNhbiBhbHNvIHJ1biBzZXZlcmFsIHRyaWFscwpjdl9yZXN1bHRzIDwtIGN2X3ByZWRfZXJyb3IobW9kMSwgbW9kZTIsIG50cmlhbHM9bikKCgoKIyBib290c3RyYXBwaW5nIGFuZCBwcmVjaXNpb24gKHJlc2FtcGxpbmcgbWV0aG9kKQojIyBDb25maWRlbmNlIGludGVydmFscyBhcmUgYSByZXByZXNlbnRhdGlvbiBvZiBwcmVjaXNpb24KIyMgYm9vdHN0cmFwIHdoZW4gZGF0YSBpcyBub3Qgc3VmZmljaWVudCB0byBzdHVkeSBwcmVjaXNpb24KIyMjIGlkZWEgPSB1c2UgMSBkYXRhc2V0IHRvIHNpbXVsYXRlIG1hbnkgZGF0YXNldHMgYXMgaWYgdGhleSB3ZXJlIHNhbXBsZWQgZnJvbSB0aGUgcG9wdWxhdGlvbgojIyMgYm9vdHN0cmFwaW5nIGZvciBxdW5hdGlmeWluZyBtYW55IHJlc3VsdHMgb24gYW4gZWZmZWN0IHNpemUKCiMgd29yayBmbG93IGZvciBjb25zdHJ1Y3RpbmcgMSByZXNhbXBsZWQgZGF0YSBzZXQKbW9kIDwtIGxtKCkKZWZmZWN0X3NpemUobW9kLCB+eCkKIyBjb25zdHJ1Y3QgYSByZXNhbXBsaW5nIG9mIHRoZSBkZiBhbmQgc2F2ZSBkZiBieSBzZWxlY3Rpbmcgcm93IG51bWJlcnMgYXQgcmFuZG9tIGZyb20gdGhlIG9yaWdpbmFsIGRhdGFzZXQKdHJpYWxfaW5kaWNlcyA8LSBzYW1wbGUoMTpucm93KGRmKSwgcmVwbGFjZT1UKQp0cmlhbF9kYXRhIDwtIGRmW3RyaWFsX2luZGljZXMsXQoKIyB0cmFpbiB0aGUgbW9kZWwgdG8gdGhlIHJlc2FtcGxlZCBkYXRhc2V0CnRyaWFsX21vZCA8LSBsbSgsIGRhdGE9dHJpYWxfZGF0YSkKIyBjb21wdXRlIGVmZmVjdCBzaXplCmVmZmVjdF9zaXplKHRyaWFsX21vZCwgfngpCgojIGNvbnN0cnVjdCAxMDAgcmVzYW1wbGVkIGRhdGEgc2V0cyB1c2luZyB0aGUgZW5zZW1ibGUgZnVuY3Rpb24gZnJvbSB0aGUgc3RhdGlzdGljYWxNb2RlbGluZyBwYWNrYWdlCm1vZCA8LSBsbSgpCnRyaWFsIDwtIGVuc2VtYmxlKG1vZCwgbnJlcHM9MTAwKQp0cmlhbF9lZmZlY3Rfc2l6ZSA8LSBlZmZlY3Rfc2l6ZSh0cmlhbCwgfngpCiMgY29tcHV0ZSBTRCBvZiB0aGUgMTAwIGVmZmVjdCBzaXplcwpzZCh0cmlhbF9lZmZlY3Rfc2l6ZSRzbG9wZSkKIyBwbG90IGVmZmVjdCBzaXplcwpoaXN0KHRyaWFsX2VmZmVjdF9zaXplcyRzbG9wZSkKCgoKIyBTY2FsZXMgJiBUcmFuc2Zvcm1hdGlvbnM6CiMjIHdvcmtpbmcgd2l0aCBsb2dzLCBpbnRlcnByZXRhdGlvbnA7IHByb3BvcnRpb25hbCBjaGFuZ2UKIyMgYm94LWNveAojIyByYW5rIHRyYW5zZm9ybSwgcmVwbGFjZSB2YWx1ZSBjKDIsIDQsIDIwMDAsIDEpIGFzIGMoMiwgMywgNCwgMSkKCiMjIGV4IG1vZGVsCmxtKGxvZyh5KX54KSAjIGltcGxpZXMgZXhwb25lbnRpYWwgcmVsYXRpb25zaGlwCmxtKGxvZyh5KX5sb2coeCkpIGFwcHJveGltYXRlcyBsaW5lYXIgcmVsYXRpb25zaGlwCgoKIyBNb2RlbGluZyBleHBvbmVudGlhbCBjaGFuZ2Ugb3ZlciB0aW1lCm1vZCA8LSBsbShzYWxhcnl+eWVhcikgIyBhIGxpbmVhciBtb2RlbCBmYWlscyB0byBjYXB0dXJlIHRoZSBkYXRhCiMjIHBsb3R0aW5nIGNhbiBzaG93IHRoaXMgZmFpbHV0cmUKZm1vZGVsKG1vZCwgZGF0YT1kZikgK2dlb21fcG9pbnQoZGF0YT1kZikKIyB0YWtlIGxvZyB0cmFuc2Zvcm1hdGlvbiBvZiB5CgojIG1vZGVsaW5nIHByZWRpY3Rpb25zIG9mIGxvZyB0cmFuc2Zvcm1zIGJ5IGNvbXBhcmluZyBjcm9zcyB2YWxpZGF0aW9uCm1vZDEgPC0gbG0oeX54KQptb2QyIDwtIGxtKGxvZyh5KX54KQojIGNvbXB1dGUgbW9kZWwgdmFsdWVzCnByZWQxIDwtIGV2YWx1YXRlX21vZGVsKG1vZDEsIGRhdGE9ZGYpCnByZWQyIDwtIGV2YWx1YXRlX21vZGVsKG1vZDIsIGRhdGE9ZGYpCiMgdHJhbnNmb3JtIG1vZGVsIG91dHB1dApwcmVkMiRtb2RfeSA8LSBleHAocHJlZDIkbW9kZWxfb3V0cHV0KQojIGNvbXB1dGUgTVNFCm1lYW4oKHByZWQxJG1vZGVsX291dHB1dCAtIGRmJHkpXjIsIG5hLnJtPVQpCm1lYW4oKHByZWQxJG1vZF95IC0gZGYkeSleMiwgbmEucm09VCkKCiMjIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIG9uIGxvZyB0cmFuc2Zvcm1zCm1vZCA8LSBsbShsb2coeSl+eCkKIyBjcmVhdGUgYm9vdHN0cmFwIHJlcGxpY2F0aW9ucwpib290c3RyYXBfbW9kIDwtIGVuc2VtYmxlKG1vZCwgbnJlcHM9MTAwMCwgZGF0YT1kZikKIyBjb21wdXRlIGVmZmVjdCBzaXplCnlfZWZmZWN0IDwtIGVmZmVjdF9zaXplKGJvb3RzdHJhcF9tb2QsIH54KQojIGNoYW5nZSBzbG9wZSB0byBwZXJjZW50IGNoYW5nZQp5X2VmZmVjdCRwZXJfY2hhbmdlIDwtIDEwMCAqIChleHAoeV9lZmZlY3Qkc2xvcGUpIC0xKQojIGNvbXB1dGUgY29uZmlkZW5jZSBpbnRlcnZhbHMKd2l0aCh5X2VmZmVjdCwgbWVhbihwZXJfY2hhbmdlKSArIGMoLTIsIDIpICogc2QocGVyX2NoYW5nZSkpCgoKCiMgQ29uZmlkZW5jZSAmIENvbGxpbmVhcml0eQojIyB0cmFuc2Zvcm0gcnNxdWFyZWQgaW50byB2YXJpYW5jZSBpbmZsYXRpb24gZmFjdG9yCiMjIyB2aWY9IHZhcmlhbmNlIGluIHNwcmVhZCBmcm9tIDEgc2FtcGxlIHRvIGFub3RoZXIKdmlmIDwtIDEvKDEtUjIpCiMgc3RhbmRhcmQgZXJyb3IgaW5mbGF0aW9uIGZhY3RvcgpzcXJ0KHZpZikKIyMgdXNlIHRoZSBjb2xsaW5lYXJpdHkgZnVuY3Rpb24gaW4gc3RhdGlzdGljYWxNb2RlbGluZwpjb2xsaW5lYXJpdHkofngreiwgZGF0YT1kZikKY29sbGluZWFyaXR5KH54LCBkYXRhPWRmKQoKCgoKCgo=