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))

Goal of evaluating model: extrapolattion - finding data outside the range of the data used to train model

Evaluate model using the evaluate_model function gives output on training data

mod2 <- rpart(gestation~wt.1+smoke+race+age,data=Gestation, method="anova", cp=.005, na.action = na.rpart)
fmodel(mod1)

fmodel(mod2)

# predict values 
# use the evaluate_model function in the statisticalModeling package for better formating
head(evaluate_model(mod1))
head(evaluate_model(mod2))
rpart.plot(mod2, yesno=2)

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

    1. fit null model 1 predictors with a constant value
data("ptitanic", package="rpart.plot")
ptitanic$x <- 1
null <- rpart(survived~x, data=ptitanic)
    1. evaluate null model with evaluate_model function
null_out <-evaluate_model(null, data=ptitanic, type="class")
    1. calculate error rate
with(data=null_out, mean(survived !=model_output, na.rm=TRUE))
[1] 0.381971
    1. generate random guess
null_out$rand_guess <- mosaic::shuffle(ptitanic$survived)
prop.table(table(null_out$rand_guess))

    died survived 
0.618029 0.381971 
    1. 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:

ploting data with prp (rpart.plot package), plots as a tree with a format specified by “type”

prp(mod, type=3)

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

total change: impact on the response of changing 1 input while letting others be freely estimated

leave out important variable from model to estimate effect size of another variable

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)

Scales & Transformations:

working with logs, interpretationp; proportional change

box-cox

rank transform, replace value c(2, 4, 2000, 1) as c(2, 3, 4, 1)

ex model

lm(log(y)~x) # implies exponential relationship lm(log(y)~log(x)) approximates linear relationship

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

modeling predictions of log transforms by comparing cross validation

mod1 <- lm(y~x) mod2 <- lm(log(y)~x) # compute model values pred1 <- evaluate_model(mod1, data=df) pred2 <- evaluate_model(mod2, data=df) # transform model output pred2\(mod_y <- exp(pred2\)model_output) # compute MSE mean((pred1\(model_output - df\)y)^2, na.rm=T) mean((pred1\(mod_y - df\)y)^2, na.rm=T)

confidence intervals on log transforms

mod <- lm(log(y)~x) # create bootstrap replications bootstrap_mod <- ensemble(mod, nreps=1000, data=df) # compute effect size y_effect <- effect_size(bootstrap_mod, ~x) # change slope to percent change y_effect\(per_change <- 100 * (exp(y_effect\)slope) -1) # compute confidence intervals with(y_effect, mean(per_change) + c(-2, 2) * sd(per_change))

Confidence & Collinearity

transform rsquared into variance inflation factor

vif= variance in spread from 1 sample to another

vif <- 1/(1-R2) # standard error inflation factor sqrt(vif) ## use the collinearity function in statisticalModeling collinearity(~x+z, data=df) collinearity(~x, data=df)

LS0tCnRpdGxlOiAiU3RhdGlzdGljYWwgTW9kZWxpbmcgaW4gUiAtIERhdGEgQ2FtcCBub3RlcyIKYXV0aG9yOiAiS2V2aW4gTC4iCmRhdGU6ICJKdWx5IDI2LCAyMDE4IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMgTm90ZXMgZnJvbSBTdGF0aXN0aWNhbCBNb2RlbGluZyBpbiBSIEkgJiBJSS4gV2FsayB0aHJvdWdoIG9uIGhvdyB0byBmaXQgcmVjdXJzaXZlIHBhcnRpdGlvbmluZyBtb2RlbHMgYW5kIHV0aWxpemUgdGhlIHN0YXRpc3RpY2FsTW9kZWxpbmcgcGFja2FnZS4KCiMjIFJDT0RFIElOIFBST0dSRVNTCgojIyBJbiB0aGlzIGxlY3R1cmUsIHN0YXRpc3RpY2FsIG1vZGVscyBhcmUgZGVmaW5lZCBhcyByZXByZXNlbnRhdGlvbiBvZiByZWFsaXR5IHdpdGggYSBwdXJwb3NlIHVzaW5nIG1hdGhlbWF0aWNhbCBwcm9wZXJ0aWVzLiBNb2RlbHMgY2FuIGJlIHVzZWQgdG8gdHJhaW4gZGF0YSBhbmQgZXhwbGFpbiB2YXJpYXRpb24uCgojIyMgTG9hZCB0aGUgTW9zYWljIGxpYnJhcnkgYW5kIHVzZSB0aGUgbWVhbiBmdW5jdGlvbgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJwYXJ0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmxpYnJhcnkoc3RhdGlzdGljYWxNb2RlbGluZykKbGlicmFyeShtb3NhaWMpCmRhdGEoR2VzdGF0aW9uKQptZWFuKGdlc3RhdGlvbiB+IHNtb2tlLCBkYXRhPUdlc3RhdGlvbiwgbmEucm09VFJVRSkKR2VzdGF0aW9uJHNtb2tlW0dlc3RhdGlvbiRzbW9rZT09Ml09MQpHZXN0YXRpb24kc21va2VbR2VzdGF0aW9uJHNtb2tlPT0zXT0wCkdlc3RhdGlvbiRzbW9rZSA8LSBhcy5mYWN0b3IoR2VzdGF0aW9uJHNtb2tlKQpgYGAKCiMgRGVzaWduaW5nICYgdHJhaW5pbmcgbW9kZWwsIGV2YWx1YXRpbmcsIHRlc3RpbmcsIGludGVycHJldGluZwoJIyMgLXByb2Nlc3MgYnkgY29tcGFyaW5nIG1vZGVscyBhbmQgZXZhbHVhdGluZwoJIyMgLXNlbGVjdCBtb2RlbCBhcmNoaXRlY3R1cmUgdG8gcmVwcmVzZW50IGRhdGEKCSMjIC1yZWN1cnNpdmUgcGFydGl0aW9uaW5nICh1c2luZyBycGFydCkKCSMjIC1maXQgbW9kZWwgCiMgRXZhbHVhdGluZyBNb2RlbHMKCSMjIGxpbmVhciBtb2RlbHMgdGVsbHMgYSBsaW5lYXIgc3RvcnksIHdoaWxlIHJlY3Vyc2l2ZSBmb2xsb3dzIHRoZSBkYXRhIHBhdHRlcm4KCSMjIHByZWRpY3Rpb25zIHdpbGwgYmUgZGlmZmVyZW50IGJldHdlZW4gdGhlIDIgYXJjaGl0ZWN0dXJlcwojIGJ1aWxkIGxpbmVhciBvciByZWN1cnNpdmUgbW9kZWw6IHdvcmtmbG93CgojIyBtb2RlbDEgPC0gbG0oKQojIyMgY29uc3RydWN0IGFuIGV4YW1wbGUgb2YgZGF0YSB0byBwcmVkaWN0IGZyb20KZXhfdmFsdWVzIDwtIGRhdGEuZnJhbWUoeDE9LCB4Mj0sIGRhdGE9ZGYpCiMjIyBwcmVkaWN0IGV4X3ZhbHVlcyB1c2luZyB0aGUgIGV2YWx1YXRlX21vZGVsIGZ1bmN0aW9uIGluIHRoZSBzdGF0aXN0aWNhbE1vZGVsaW5nIHBhY2thZ2UgZm9yIGJldHRlciBmb3JtYXRpbmcKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2QxIDwtIGxtKGdlc3RhdGlvbn4gd3QuMStzbW9rZStyYWNlK2FnZSxkYXRhPUdlc3RhdGlvbiwgbmEuYWN0aW9uPW5hLm9taXQpCnZhbHVlcyA8LSBkYXRhLmZyYW1lKHd0LjE9MTE1LCBzbW9rZT0iMSIsIHJhY2U9MSwgYWdlPTMwLCBkYXRhPUdlc3RhdGlvbikKaGVhZChldmFsdWF0ZV9tb2RlbChtb2QxLCB2YWx1ZXMpKQpgYGAKCgojIyBHb2FsIG9mIGV2YWx1YXRpbmcgbW9kZWw6IGV4dHJhcG9sYXR0aW9uIC0gZmluZGluZyBkYXRhIG91dHNpZGUgdGhlIHJhbmdlIG9mIHRoZSBkYXRhIHVzZWQgdG8gdHJhaW4gbW9kZWwKIyMjIEV2YWx1YXRlIG1vZGVsIHVzaW5nIHRoZSBldmFsdWF0ZV9tb2RlbCBmdW5jdGlvbiBnaXZlcyBvdXRwdXQgb24gdHJhaW5pbmcgZGF0YQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9kMiA8LSBycGFydChnZXN0YXRpb25+d3QuMStzbW9rZStyYWNlK2FnZSxkYXRhPUdlc3RhdGlvbiwgbWV0aG9kPSJhbm92YSIsIGNwPS4wMDUsIG5hLmFjdGlvbiA9IG5hLnJwYXJ0KQoKZm1vZGVsKG1vZDEpCmZtb2RlbChtb2QyKQojIHByZWRpY3QgdmFsdWVzIAojIHVzZSB0aGUgZXZhbHVhdGVfbW9kZWwgZnVuY3Rpb24gaW4gdGhlIHN0YXRpc3RpY2FsTW9kZWxpbmcgcGFja2FnZSBmb3IgYmV0dGVyIGZvcm1hdGluZwpoZWFkKGV2YWx1YXRlX21vZGVsKG1vZDEpKQpoZWFkKGV2YWx1YXRlX21vZGVsKG1vZDIpKQoKcnBhcnQucGxvdChtb2QyLCB5ZXNubz0yKQpgYGAKCgoKCgoKIyMgQ2hvc2luZyBleHBsYW5hdG9yeSB2YXJpYWJsZXMKCSMjIyBtZWFzdXJpbmcgaG93IGNsb3NlbHkgYSBtb2RlbCBwcmVkaWN0cyB5IHZhbHVlczoKCSMjIyBwcmVkaWN0aW9uIGVycm9yID0gJFxzdW17XGhhdHt5fS15fSQsIG1lYW4gc3F1YXJlZCBlcnJvcnMgZ2l2ZXMgdXMgYSB2YWx1ZSB0byBldmFsdWF0ZQojIyMgY29tcHV0ZSBtZWFuIHByZWRpY3Rpb25zIG9mIHR3byBtb2RlbHMsIHN1YnRyYWN0IGFuZCBzcXVhcmUgdG8gc2VlIGhvdyBtdWNoIHRoZSBwcmVkaWN0aW9ucyBkaWZmZXIgYnk7IG1lYW5zIHdvdWxkIG5vcm1hbGx5IGJlIGNvbXBhcmVkIGJldHdlZW4gbmVzdGVkIG1vZGVscy4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQooKG1lYW4ocHJlZGljdChtb2QxKSwgbmEucm09VFJVRSkgLSAobWVhbihwcmVkaWN0KG1vZDIpLCBuYS5ybT1UUlVFKSkpIF4yKQptb2QxX2RpZmYgPC0gd2l0aChHZXN0YXRpb24sIGdlc3RhdGlvbi0ocHJlZGljdChtb2QxLCBuZXdkYXRhPUdlc3RhdGlvbikpKQptb2QyX2RpZmYgPC0gd2l0aChHZXN0YXRpb24sIGdlc3RhdGlvbi0ocHJlZGljdChtb2QyLCBuZXdkYXRhPUdlc3RhdGlvbikpKQptZWFuKG1vZDFfZGlmZl4yLCBuYS5ybT1UUlVFKQptZWFuKG1vZDJfZGlmZl4yLCBuYS5ybT1UUlVFKQpgYGAKCgojIyBFeGFtaW5pbmcgTVNFCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptZWFuKEdlc3RhdGlvbiRnZXN0YXRpb24gLSBwcmVkaWN0KG1vZDEsIG5ld2RhdGE9R2VzdGF0aW9uKV4yLCBuYS5ybT1UUlVFKQptZWFuKEdlc3RhdGlvbiRnZXN0YXRpb24gLSBwcmVkaWN0KG1vZDIsIG5ld2RhdGE9R2VzdGF0aW9uKV4yLCBuYS5ybT1UUlVFKQpgYGAKCiMjIENyb3NzLXZhbGlkYXRpb246IGRpdmlkZSBpbnRvIHRyYWluL3Rlc3Qgc2V0cwojIyMgdXNlIHRyYWluIGRhdGEgdG8gYnVpbGQgYm90aCBtb2RlbHMKIyMjIGNhbGN1bGF0ZSBtb2RlbCBvdXRwdXRzIHVzaW5nIHRoZSBwcmVkaWN0IGZ1bmN0aW9uCiMjIyBjb21wYXJlIG1vZGVsIG91dHB1dHMgdG8gYWN0dWFsIGRhdGEgd2l0aCB0ZXN0IHNldAojIyMjIHRoZSB0aWR5IHdheTsgbW9kZWxfb3V0cHV0IHdpdGhpbiB0aGUgZXZhbHVhdGVfbW9kZWwgc2F2ZXMgb3V0cG91dCB2YWx1ZXMgZnJvbSB0aGUgZGF0YWZyYW1lCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpvdXRwdXQgPC0gZXZhbHVhdGVfbW9kZWwobW9kMSwgZGF0YT1HZXN0YXRpb24pCndpdGgoZGF0YT1vdXRwdXQsIG1lYW4oKEdlc3RhdGlvbiRnZXN0YXRpb24gLSBtb2RlbF9vdXRwdXQpXjIsIG5hLnJtPVRSVUUpKQpgYGAKCiMjIyBDcmVhdGUgYSBuZXcgdmFyaWFibGUgdGhhdCByYW5kb21seSBhc3NpZ25zIDc1JSBvZiByb3dzIHRvIHRyYWluIGFuZCB0aGUgcmVzdCB0byB0ZXN0CmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpyb3dfY291bnQgPC0gbnJvdyhHZXN0YXRpb24pCnNodWZmbGVkX3Jvd3MgPC0gc2FtcGxlKHJvd19jb3VudCkKdHJhaW4gPC0gR2VzdGF0aW9uW2hlYWQoc2h1ZmZsZWRfcm93cyxmbG9vcihyb3dfY291bnQqMC43NSkpLF0KdHJhaW4kdHJhaW4gPC0gMQp0cmFpbiA8LSB0cmFpblssLTI6LTIzXQpHZXN0YXRpb24gPC0gR2VzdGF0aW9uICU+JSBsZWZ0X2pvaW4odHJhaW4pCkdlc3RhdGlvbiR0cmFpbltpcy5uYShHZXN0YXRpb24kdHJhaW4pXSA8LTAKcHJvcC50YWJsZSh0YWJsZShHZXN0YXRpb24kdHJhaW4pKQpgYGAKIyMjIGZpdCBtb2RlbCB0byB0cmFpbiBhbmQgZXZhbHVhdGUgb24gdGVzdApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9kMSA8LSBsbShnZXN0YXRpb25+IHd0LjErc21va2UrcmFjZSthZ2UsZGF0YT1zdWJzZXQoR2VzdGF0aW9uLCB0cmFpbj09MSksIG5hLmFjdGlvbj1uYS5vbWl0KQpvdXRwdXQgPC0gZXZhbHVhdGVfbW9kZWwobW9kMSwgZGF0YSA9IHN1YnNldChHZXN0YXRpb24sIHRyYWluPT0wKSkKYGBgCgoKCiMjIyBSZXBlYXRlZCByYW5kb20gdHJpYWxzOiBjcm9zcyB2YWxpZGF0aW9uIHZhcmllcyBieSB0cmlhbCwgdGhlcmVmb3JlLCByZXBlYXRpbmcgdGhlIHByb2NlZHVyZSBpcyBhIGJlc3QgcHJhY3RpY2UKIyMjIyBDb21wdXRlIHRoZSBpbiBzYW1wbGUgZXJyb3Igd2l0aCB0cmFpbmluZyBzZXQuCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQppbl9zYW1wbGVfZXJyb3IgPC0gd2l0aChldmFsdWF0ZV9tb2RlbChtb2QxLCBkYXRhPXN1YnNldChHZXN0YXRpb24sIHRyYWluPT0xKSksIG1lYW4oKGdlc3RhdGlvbi1tb2RlbF9vdXRwdXQpXjIsIG5hLnJtPVRSVUUpKQppbl9zYW1wbGVfZXJyb3IKYGBgCgojIyMgVXNlIHRoZSBjdiBmdW5jdGlvbiB0byBpdGVyYXRlIG92ZXIgbiB0cmFpbHMKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnRyaWFscyA8LSBjdl9wcmVkX2Vycm9yKG1vZDEsIG50cmlhbHMgPSAxMDApIAptZWFuKHRyaWFscyRtc2UpCmBgYAoKIyBmaW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb24gdHJhaWxzIGNvbXBhcmVkIHRvIGluX3NhbXBsZSBlcnJvcgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9zYWljOjp0LnRlc3Qofm1zZSwgbXU9aW5fc2FtcGxlX2Vycm9yLCBkYXRhPXRyaWFscykKYGBgCgojIyMgQ29tcGFyaW5nIDIgbW9kZWxzCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQp0cmlhbHMgPC0gY3ZfcHJlZF9lcnJvcihtb2QxLCBtb2QyKSAjIG1vZDIgaXMgdGhlIHJwYXJ0IG1vZGVsCm5hbWVzKHRyaWFscykKdC50ZXN0KG1zZX5tb2RlbCwgZGF0YT10cmlhbHMpCmBgYAoKCgoKIyMgUHJlZGljdGlvbiBlcnJvciBmb3IgY2F0ZWdvcmljYWwgb3V0Y29tZQojIyMgV29yayBmbG93CiMjIyMgQ2FsY3VsYXRlIGVycm9yIHJhdGUgd2l0aCB0cnVlIGFuZCBmYWxzZSBwb3NzaXRpdmUKIyMjIyBSZXR1cm4gcHJvYmFiaWxpaWVzIGluc3RlYWQgb2YgMCwgMQojIyMjIEZvciBlYWNoIGNhc2UgZXh0cmFjdCB0aGUgcHJvYiB0aGF0IHRoZSBtb2RlbCBhc3NpZ25lZCBmb3IgZWFjaCBvdXRjb21lLCBsaWtlbGlob29kLCBhbmQgbXVsdGlwbHkgZWFjaCBjYXNlIHRvZ2V0aGVyIHRvIGdldCB0b3RhbCBsaWtlbGlob29kIG9mIG9ic2VydmF0aW9ucwoKIyMjIHByb2Nlc3MKKiAxLiBmaXQgbnVsbCBtb2RlbCAxIHByZWRpY3RvcnMgd2l0aCBhIGNvbnN0YW50IHZhbHVlCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhKCJwdGl0YW5pYyIsIHBhY2thZ2U9InJwYXJ0LnBsb3QiKQpwdGl0YW5pYyR4IDwtIDEKbnVsbCA8LSBycGFydChzdXJ2aXZlZH54LCBkYXRhPXB0aXRhbmljKQpgYGAKCiogMi4gIGV2YWx1YXRlIG51bGwgbW9kZWwgd2l0aCBldmFsdWF0ZV9tb2RlbCBmdW5jdGlvbgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbnVsbF9vdXQgPC1ldmFsdWF0ZV9tb2RlbChudWxsLCBkYXRhPXB0aXRhbmljLCB0eXBlPSJjbGFzcyIpCmBgYAoKKiAzLiBjYWxjdWxhdGUgZXJyb3IgcmF0ZQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kd2l0aChkYXRhPW51bGxfb3V0LCBtZWFuKHN1cnZpdmVkICE9bW9kZWxfb3V0cHV0LCBuYS5ybT1UUlVFKSkKYGBgCgoqIDQuIGdlbmVyYXRlIHJhbmRvbSBndWVzcwpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbnVsbF9vdXQkcmFuZF9ndWVzcyA8LSBtb3NhaWM6OnNodWZmbGUocHRpdGFuaWMkc3Vydml2ZWQpCnByb3AudGFibGUodGFibGUobnVsbF9vdXQkcmFuZF9ndWVzcykpCmBgYAoKKiA1LiBjb21wdXRlIGVycm9yIHJhdGUKIyMjIyBsb3dlciBlcnJvciByYXRlIHZhbHVlIGRldGVybWluZXMgaWYgbnVsbCBtb2RlbCBkb2VzIGJldHRlciB0aGFuIGd1ZXNzaW5nIGF0IHJhbmRvbSAKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CndpdGggKGRhdGE9bnVsbF9vdXQsIG1lYW4oc3Vydml2ZWQgIT1yYW5kX2d1ZXNzLCBuYS5ybT1UKSkKYGBgCgogCiMjIGFkZGluZyBwcmVkaWN0b3JzIGFuZCBjb21wdXRpbmcgZXJyb3IgcmF0ZSAoZXJyb3IgcmF0ZSBkcm9wcyB3aXRoIHByZWRpY3RvcnMgY29tcGFyZWQgdG8gdGhlIG51bGwgbW9kZWwpCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptb2QgPC0gcnBhcnQoc3Vydml2ZWR+LiwgZGF0YT1wdGl0YW5pYykKbW9kX291dCA8LSBldmFsdWF0ZV9tb2RlbChtb2QsIGRhdGE9cHRpdGFuaWMsIHR5cGU9ImNsYXNzIikKd2l0aChkYXRhPW1vZF9vdXQsIG1lYW4oc3Vydml2ZWQgIT0gbW9kZWxfb3V0cHV0LCBuYS5ybT1UKSkKYGBgCgoKIyMgRXhwbG9yaW5nIGRhdGEgZm9yIHJlbGF0aW9uc2hpcHM6CiMjIyBwbG90aW5nIGRhdGEgd2l0aCBwcnAgKHJwYXJ0LnBsb3QgcGFja2FnZSksIHBsb3RzIGFzIGEgdHJlZSB3aXRoIGEgZm9ybWF0IHNwZWNpZmllZCBieSAidHlwZSIKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnBycChtb2QsIHR5cGU9MykKYGBgCgoKIyMgTW9kZWwgY292YXJpYXRlczoKIyMjIGJ1aWxkIHR3byBtb2RlbHMsIDEgdy8gY292YXJpYXRlIGFuZCB3L28KIyMjdXNlIGV2YWx1YXRlX21vZGVsIHRvIGdldCBwcmVkaWN0aW9ucwojIyMgc3VidHJhY3QgcHJlZGljdGVkIHZhbHVlcyB3LyAmIHcvbyBob2xkaW5nIGNvdmFyaWF0ZSBjb25zdGFudAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1vZDEgPC0gbG0oc3Vydml2ZWR+cGNsYXNzLCBkYXRhID0gcHRpdGFuaWMpCm1vZDIgPC0gbG0oc3Vydml2ZWR+cGNsYXNzK3NleCwgZGF0YSA9IHB0aXRhbmljKQoKZXZhbHVhdGVfbW9kZWwobW9kMSwgcGNsYXNzPSIxc3QiKSAKZXZhbHVhdGVfbW9kZWwobW9kMiwgcGNsYXNzPSIxc3QiLCBzZXg9ImZlbWFsZSIpICMgd2l0aCBjb3ZhcmlhdGUKCmBgYAoKCgojIyMgRWZmZWN0IHNpemVzOiBIb3cgbXVjaCBpcyB0aGUgY2hhbmdlLCBvciBzdHJlbmd0aCBvZiBhc3NvY2lhdGlvbgojIyMjIHVzaW5nIGVmZmVjdF9zaXplIGZ1bmN0aW9uIGZyb20gc3RhdGlzdGljYWxNb2RlbGluZwpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9kMSA8LSBsbShnZXN0YXRpb25+c21va2UsIGRhdGE9R2VzdGF0aW9uKQptb2QyIDwtIGxtKGdlc3RhdGlvbn5zbW9rZSt3dC4xK2FnZSwgZGF0YT1HZXN0YXRpb24pCmVmZmVjdF9zaXplKG1vZDEsIH5zbW9rZSkKZWZmZWN0X3NpemUobW9kMiwgfnNtb2tlK3d0LjErYWdlKQpgYGAKCgoKCgpQYXJ0IDI6CgojIyBVc2luZyBlZmZlY3Rfc2l6ZSBmdW5jdGlvbiBpbiBzdGF0aXN0aWNhbE1vZGVsaW5nIHRvIGlkZW50aWZ5IHZhbHVlcyB0byBjZW50ZXIKIyMjIHVzaW5nIGZtb2RlbCBmdW5jdGlvbgojIyMjIyBidWlsZCBycGFydCBtb2RlbApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9kMyA8LSBycGFydChnZXN0YXRpb25+c21va2Urd3QuMStyYWNlLCBkYXRhPUdlc3RhdGlvbikKCmZtb2RlbChtb2QzLCB+d3QuMStzbW9rZStyYWNlLCBkYXRhPUdlc3RhdGlvbiwgIHJhY2U9Yyg1OjcgKSkgIyBzcGVjaWZ5IGZhY2V0cwpgYGAKCgoKIyMjIENhdGVnb3JpY2FsIHJlc3BvbnNlIHVzaW5nIGV2YWx1YXRlX21vZGVsCiMjIyMgZm9yIG1vZGVsX291dHB1dCA9IDEsMApgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcHRpdGFuaWMkc3Vydml2ZWRfZltwdGl0YW5pYyRzdXJ2aXZlZD09InN1cnZpdmVkIl09MQpwdGl0YW5pYyRzdXJ2aXZlZF9mW3B0aXRhbmljJHN1cnZpdmVkPT0iZGllZCJdPTAKcHRpdGFuaWMkc3Vydml2ZWRfZiA8LSBhcy5mYWN0b3IocHRpdGFuaWMkc3Vydml2ZWRfZikKbW9kIDwtIHJwYXJ0KHN1cnZpdmVkX2Z+cGNsYXNzK3NleCwgZGF0YT1wdGl0YW5pYywgbWV0aG9kPSJjbGFzcyIpCmV2YWxfb3V0MSA8LSBldmFsdWF0ZV9tb2RlbChtb2QsIHR5cGU9ImNsYXNzIiwgcGNsYXNzPWMoIjFzdCIsIjNyZCIpLCBzZXg9ImZlbWFsZSIpCmV2YWxfb3V0MQoKYGBgCgojIyMjIGZvciBwcm9iYWJpbGl0aWVzIG9mIHkgY2hhbmdlIHR5cGU9InByb3AiCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpldmFsX291dDIgPC0gZXZhbHVhdGVfbW9kZWwobW9kLCB0eXBlPSJwcm9iIiwgcGNsYXNzPWMoIjFzdCIsIjNyZCIpLCBzZXg9ImZlbWFsZSIpCmV2YWxfb3V0MgpgYGAKCgojIyMgSW50ZXJhY3Rpb24gd29ya2Zsb3c6CiMjIyMgZXhhbWluZSBpZiBpbnRlcmFjdGlvbnMgaGVscCBtb2RlbCwgdXNlIGNyb3NzIHZhbGlkYXRpb24KIyMjIyBidWlsZCBsbSBtb2RlbCBhbmQgcGxvdCB3LyAmIHcvbyBpbnRlcmFjdGlvbgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW9kMSA8LSBsbShnZXN0YXRpb25+c21va2Urd3QuMStyYWNlLCBkYXRhPUdlc3RhdGlvbiwgbmEuYWN0aW9uID1uYS5leGNsdWRlKQptb2QyIDwtIGxtKGdlc3RhdGlvbn5zbW9rZSp3dC4xKnJhY2UsIGRhdGE9R2VzdGF0aW9uLCBuYS5hY3Rpb24gPW5hLmV4Y2x1ZGUpCmZtb2RlbChtb2QxKSArIGdncGxvdDI6OnlsYWIoInN1cnZpdmVkIikKZm1vZGVsKG1vZDIpICsgZ2dwbG90Mjo6eWxhYigic3Vydml2ZWQiKQoKCmBgYAoKCiMjIyMgY3Jvc3MgdmFsaWRhdGUgJiBjb21wYXJlIHByZWRpY3Rpb24gZXJyb3JzCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpyZXMgPC0gY3ZfcHJlZF9lcnJvcihtb2QxLCBtb2QyKQpuYW1lcyhyZXMpCnQudGVzdChtc2V+bW9kZWwsIGRhdGE9cmVzKQpgYGAKCgoKCiMjIyBUb3RhbCAmIFBhcnRpYWwgQ2hhbmdlOgojIyB0byBjb21wYXJlIGVmZmVjdCBzaXplcywgbWFrZSBzdXJlIHVuaXRzIGFyZSBvbiB0aGUgc2FtZSBsZXZlbAojIyMgZXguIHByaWNlIG9mIGNhciA9IGFnZSBvZiBjYXIgKyBtaWxlYWdlIG9mIGNhcgojIyMgZWZmZWN0IHNpemUgb2YgYWdlIGlzIGRvbGxhcnMgdG8geWVhciBvciBkb2xsYXJzL3llYXIgdW5pdCBsZXZlbAojIyMgZWZmZWN0IHNpemUgb2YgbWlsZXMgaXMgZG9sbGFycyB0byBtaWxlLCBkb2xsYXJzL21pbGUgdW5pdCBsZXZlbAojIyMgY292ZXJ0IHVuaXRzIHRvIHNhbWUgc2NhbGUgYnkgbXVsdGlwbHlpbmcgc2xvcGVzLCBhbmQgdGFraW5nIGFuIGF2ZXJhZ2UgdmFsdWUgb2YgbWlsZXMgcGVyIHllYXIgKDEwaykKIyMjIHNsb3BlKGRvbGxhcnMvbWlsZSkgKiAxMDAwMChtaWxlcy95ZWFyKT1zbG9wZShkb2xsYXJzL3llYXIKIyMjIHNvbWV0aW1lcyBpdCBpcyBub3QgcG9zc2libGUgdG8gY2hhbmdlIHRoZSB1bml0cyBvZiBvbmUgdmFyaWFibGUgdy9vIGFmZmVjdGluZyB0aGUgb3RoZXIuCiMjIyBwYXJ0aWFsIGNoYW5nZTogaW1wYWN0IG9uIHRoZSByZXNwb25zZSBvZiBjaGFuZ2luZyAxIHVuaXQgaG9sZGluZyBhbGwgb3RoZXIgaW5wdXRzIGNvbnN0YW50CiMjIyB0b3RhbCBjaGFuZ2U6IGltcGFjdCBvbiB0aGUgcmVzcG9uc2Ugb2YgY2hhbmdpbmcgMSBpbnB1dCB3aGlsZSBsZXR0aW5nIG90aGVycyBiZSBmcmVlbHkgZXN0aW1hdGVkCiMjIyMgbGVhdmUgb3V0IGltcG9ydGFudCB2YXJpYWJsZSBmcm9tIG1vZGVsIHRvIGVzdGltYXRlIGVmZmVjdCBzaXplIG9mIGFub3RoZXIgdmFyaWFibGUKCiMjIGV2YWx1YXRlIG1vZGVsIHVzaW5nIGV2YWx1YXRlX21vZGVsIGZ1bmN0aW9uIGZyb20gc3RhdGlzdGljYWxNb2RlbGluZywgYXNzaWduIHZhbHVlcyB0byBleHBsYW5hdG9yeSB2YXJpYWJsZXMKZXZhbHVhdGVfbW9kZWwoeD0sIHo9KQoKCiMgUiBzcXVhcmVkOiBDb2VmZmljaWVudCBvZiBkZXRlcm1pbmF0aW9uCiMjIGZyYWN0aW9uIG9mIHZhcmlhdGlvbiBpbiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgYWNjb3VudGVkIGZvciBieSB0aGUgbW9kZWwKIyMgUiBzcXVhcmVkIG5vdCBhcyB1c2VmdWwgZm9yIHByZWRpY3Rpbmcgb2YgaW5mZXJlY2Ugc2luY2UgdGhlIHZhbHVlIGNoYW5nZXMgd2l0aCBtb2RlbHMgYW5kIHZhbHVlcyBkZXBlbmRzIG9uIHRoZSBjb250ZXh0IG9mIGRhdGEKIyMjIGFsdGVybmF0aXZlczsKIyMjIyBwcmVkaWN0aW9uIHB1cnBvc2UgPSBjcm9zcyB2YWxpZGF0ZWQgcHJlZGljdGlvbiBlcnJvciBvciBNU0UgZnJvbSBjcm9zcyB2YWxpZGF0aW9uCiMjIyMgaW5mZXJlbmNlIHB1cnBvc2UgPSAgZWZmZWN0IHNpemVzICYgY29uZmlkZW5jZSBpbnRlcnZhbHMKCiMjIyBjYWxjdWxhdGUgUi1zcXVhcmVkIGZvciBhIG1vZGVsCm1vZDE8LWxtKCkKb3V0IDwtIGV2YWx1YXRlX21vZGVsKG1vZDEsIGRhdGE9ZGQpCiMjIFItc3F1YXJlZCB2YXJpYW5jZSBvZiBtb2RlbCByZXNwb25zZSBwcmVkaWN0aW9uIC8gdmFyKHkpCndpdGgob3V0LCB2YXIobW9kZWxfb3V0cHV0KS8gdmFyKHkpCgojIHBsb3QgTVNFIGZyb20gYm90aCBtb2RlbHMgaW4gYSBib3hwbG90CiMjIGN2X3ByZWRfZXJyb3IgZnVuY3Rpb24gZnJvbSBzdGF0aXN0aWNhbE1vZGVsaW5nIHN1YnNldHMgTVNFIHByZWRpY3Rpb24gZXJyb3IKYm94cGxvdChtc2V+bW9kZWwsIGRhdGE9Y3ZfcHJlZF9lcnJvcihtb2RlbF8xLCBtb2RlbF8yKSkKCiMgRGVncmVlcyBvZiBGcmVlZG9tCiMjIGRpZmZlcmVuY2UgYmV0d2VlbiBkZiBpbiB0aGUgZGF0YSBhbmQgZGYgaW4gdGhlIG1vZGVsIGlzIHRoZSByZXNpZHVhbCBkZWdyZWVzIG9mIGZyZWVkb20KCSMjIG1vZGVscyB3aXRoIHNldmVyYWwgcmVzaWR1YWwgZGVncmVlcyBvZiBmcmVlZG9tIGFyZSBub3QgcmVsaWFibGUKCiMjIyBsZW5ndGgodW5pcXVlKHgpKSAjIGdpdmVzIHVzIHRoZSBudW1iZXIgb2YgbGV2ZWxzIGluIGEgY29udGludW91cyB2YXJpYWJsZQoKIyMjIGNyb3NzIHZhbGlkYXRpb24sIGNhbiBhbHNvIHJ1biBzZXZlcmFsIHRyaWFscwpjdl9yZXN1bHRzIDwtIGN2X3ByZWRfZXJyb3IobW9kMSwgbW9kZTIsIG50cmlhbHM9bikKCgoKIyBib290c3RyYXBwaW5nIGFuZCBwcmVjaXNpb24gKHJlc2FtcGxpbmcgbWV0aG9kKQojIyBDb25maWRlbmNlIGludGVydmFscyBhcmUgYSByZXByZXNlbnRhdGlvbiBvZiBwcmVjaXNpb24KIyMgYm9vdHN0cmFwIHdoZW4gZGF0YSBpcyBub3Qgc3VmZmljaWVudCB0byBzdHVkeSBwcmVjaXNpb24KIyMjIGlkZWEgPSB1c2UgMSBkYXRhc2V0IHRvIHNpbXVsYXRlIG1hbnkgZGF0YXNldHMgYXMgaWYgdGhleSB3ZXJlIHNhbXBsZWQgZnJvbSB0aGUgcG9wdWxhdGlvbgojIyMgYm9vdHN0cmFwaW5nIGZvciBxdW5hdGlmeWluZyBtYW55IHJlc3VsdHMgb24gYW4gZWZmZWN0IHNpemUKCiMgd29yayBmbG93IGZvciBjb25zdHJ1Y3RpbmcgMSByZXNhbXBsZWQgZGF0YSBzZXQKbW9kIDwtIGxtKCkKZWZmZWN0X3NpemUobW9kLCB+eCkKIyBjb25zdHJ1Y3QgYSByZXNhbXBsaW5nIG9mIHRoZSBkZiBhbmQgc2F2ZSBkZiBieSBzZWxlY3Rpbmcgcm93IG51bWJlcnMgYXQgcmFuZG9tIGZyb20gdGhlIG9yaWdpbmFsIGRhdGFzZXQKdHJpYWxfaW5kaWNlcyA8LSBzYW1wbGUoMTpucm93KGRmKSwgcmVwbGFjZT1UKQp0cmlhbF9kYXRhIDwtIGRmW3RyaWFsX2luZGljZXMsXQoKIyB0cmFpbiB0aGUgbW9kZWwgdG8gdGhlIHJlc2FtcGxlZCBkYXRhc2V0CnRyaWFsX21vZCA8LSBsbSgsIGRhdGE9dHJpYWxfZGF0YSkKIyBjb21wdXRlIGVmZmVjdCBzaXplCmVmZmVjdF9zaXplKHRyaWFsX21vZCwgfngpCgojIGNvbnN0cnVjdCAxMDAgcmVzYW1wbGVkIGRhdGEgc2V0cyB1c2luZyB0aGUgZW5zZW1ibGUgZnVuY3Rpb24gZnJvbSB0aGUgc3RhdGlzdGljYWxNb2RlbGluZyBwYWNrYWdlCm1vZCA8LSBsbSgpCnRyaWFsIDwtIGVuc2VtYmxlKG1vZCwgbnJlcHM9MTAwKQp0cmlhbF9lZmZlY3Rfc2l6ZSA8LSBlZmZlY3Rfc2l6ZSh0cmlhbCwgfngpCiMgY29tcHV0ZSBTRCBvZiB0aGUgMTAwIGVmZmVjdCBzaXplcwpzZCh0cmlhbF9lZmZlY3Rfc2l6ZSRzbG9wZSkKIyBwbG90IGVmZmVjdCBzaXplcwpoaXN0KHRyaWFsX2VmZmVjdF9zaXplcyRzbG9wZSkKCgoKIyBTY2FsZXMgJiBUcmFuc2Zvcm1hdGlvbnM6CiMjIHdvcmtpbmcgd2l0aCBsb2dzLCBpbnRlcnByZXRhdGlvbnA7IHByb3BvcnRpb25hbCBjaGFuZ2UKIyMgYm94LWNveAojIyByYW5rIHRyYW5zZm9ybSwgcmVwbGFjZSB2YWx1ZSBjKDIsIDQsIDIwMDAsIDEpIGFzIGMoMiwgMywgNCwgMSkKCiMjIGV4IG1vZGVsCmxtKGxvZyh5KX54KSAjIGltcGxpZXMgZXhwb25lbnRpYWwgcmVsYXRpb25zaGlwCmxtKGxvZyh5KX5sb2coeCkpIGFwcHJveGltYXRlcyBsaW5lYXIgcmVsYXRpb25zaGlwCgoKIyBNb2RlbGluZyBleHBvbmVudGlhbCBjaGFuZ2Ugb3ZlciB0aW1lCm1vZCA8LSBsbShzYWxhcnl+eWVhcikgIyBhIGxpbmVhciBtb2RlbCBmYWlscyB0byBjYXB0dXJlIHRoZSBkYXRhCiMjIHBsb3R0aW5nIGNhbiBzaG93IHRoaXMgZmFpbHV0cmUKZm1vZGVsKG1vZCwgZGF0YT1kZikgK2dlb21fcG9pbnQoZGF0YT1kZikKIyB0YWtlIGxvZyB0cmFuc2Zvcm1hdGlvbiBvZiB5CgojIG1vZGVsaW5nIHByZWRpY3Rpb25zIG9mIGxvZyB0cmFuc2Zvcm1zIGJ5IGNvbXBhcmluZyBjcm9zcyB2YWxpZGF0aW9uCm1vZDEgPC0gbG0oeX54KQptb2QyIDwtIGxtKGxvZyh5KX54KQojIGNvbXB1dGUgbW9kZWwgdmFsdWVzCnByZWQxIDwtIGV2YWx1YXRlX21vZGVsKG1vZDEsIGRhdGE9ZGYpCnByZWQyIDwtIGV2YWx1YXRlX21vZGVsKG1vZDIsIGRhdGE9ZGYpCiMgdHJhbnNmb3JtIG1vZGVsIG91dHB1dApwcmVkMiRtb2RfeSA8LSBleHAocHJlZDIkbW9kZWxfb3V0cHV0KQojIGNvbXB1dGUgTVNFCm1lYW4oKHByZWQxJG1vZGVsX291dHB1dCAtIGRmJHkpXjIsIG5hLnJtPVQpCm1lYW4oKHByZWQxJG1vZF95IC0gZGYkeSleMiwgbmEucm09VCkKCiMjIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIG9uIGxvZyB0cmFuc2Zvcm1zCm1vZCA8LSBsbShsb2coeSl+eCkKIyBjcmVhdGUgYm9vdHN0cmFwIHJlcGxpY2F0aW9ucwpib290c3RyYXBfbW9kIDwtIGVuc2VtYmxlKG1vZCwgbnJlcHM9MTAwMCwgZGF0YT1kZikKIyBjb21wdXRlIGVmZmVjdCBzaXplCnlfZWZmZWN0IDwtIGVmZmVjdF9zaXplKGJvb3RzdHJhcF9tb2QsIH54KQojIGNoYW5nZSBzbG9wZSB0byBwZXJjZW50IGNoYW5nZQp5X2VmZmVjdCRwZXJfY2hhbmdlIDwtIDEwMCAqIChleHAoeV9lZmZlY3Qkc2xvcGUpIC0xKQojIGNvbXB1dGUgY29uZmlkZW5jZSBpbnRlcnZhbHMKd2l0aCh5X2VmZmVjdCwgbWVhbihwZXJfY2hhbmdlKSArIGMoLTIsIDIpICogc2QocGVyX2NoYW5nZSkpCgoKCiMgQ29uZmlkZW5jZSAmIENvbGxpbmVhcml0eQojIyB0cmFuc2Zvcm0gcnNxdWFyZWQgaW50byB2YXJpYW5jZSBpbmZsYXRpb24gZmFjdG9yCiMjIyB2aWY9IHZhcmlhbmNlIGluIHNwcmVhZCBmcm9tIDEgc2FtcGxlIHRvIGFub3RoZXIKdmlmIDwtIDEvKDEtUjIpCiMgc3RhbmRhcmQgZXJyb3IgaW5mbGF0aW9uIGZhY3RvcgpzcXJ0KHZpZikKIyMgdXNlIHRoZSBjb2xsaW5lYXJpdHkgZnVuY3Rpb24gaW4gc3RhdGlzdGljYWxNb2RlbGluZwpjb2xsaW5lYXJpdHkofngreiwgZGF0YT1kZikKY29sbGluZWFyaXR5KH54LCBkYXRhPWRmKQoKCgoKCgo=