Background
These lab exercises will demonstrate some of the options for model
selection that we discussed in lecture, which include both in-sample and
out-of-sample methods. We will return to the plant species richness data
from the Galapagos Archipelago contained in the gala
dataset in the Faraway package. As a reminder, here are
the variables:
Species: the number of plant species found on the
island
Endemics: the number of endemic species
Area: the area of the island (km\(^2\))
Elevation: the highest elevation of the island
(m)
Nearest: the distance from the nearest island
(km)
Scruz: the distance from Santa Cruz island
(km)
Adjacent: the area of the adjacent island (km\(^2\))
We will ignore the number of endemic species (Endemics)
and focus on possible models to explain the total number of species
(Species) as a function of the 5 covariates (predictors).
Before diving right in, let’s consider the possible correlations among
these covariates, as we want to avoid including any two covariates in
the same model if theyare highly correlated.
## get data
data(gala, package = "faraway")
dat <- gala
head(dat)
## Species Endemics Area Elevation Nearest Scruz Adjacent
## Baltra 58 23 25.09 346 0.6 0.6 1.84
## Bartolome 31 21 1.24 109 0.6 26.3 572.33
## Caldwell 3 3 0.21 114 2.8 58.7 0.78
## Champion 25 9 0.10 46 1.9 47.4 0.18
## Coamano 2 1 0.05 77 1.9 1.9 903.82
## Daphne.Major 18 11 0.34 119 8.0 8.0 1.84
## check correlations
round(cor(dat[,3:7]), 2)
## Area Elevation Nearest Scruz Adjacent
## Area 1.00 0.75 -0.11 -0.10 0.18
## Elevation 0.75 1.00 -0.01 -0.02 0.54
## Nearest -0.11 -0.01 1.00 0.62 -0.12
## Scruz -0.10 -0.02 0.62 1.00 0.05
## Adjacent 0.18 0.54 -0.12 0.05 1.00
There appears to be high correlation between Area and
Elevation (\(\rho\) =
0.75), and reasonably high correlation between Nearest and
Scruz (\(\rho\) = 0.62),
so let’s be careful to keep those variables separate from one another in
our models.
Creating a model set
We need to create a set of all the models we’d like to fit and
evaluate for their data support. To do so, we can make use of the
expand.grid() function in R, which will
create a data frame from all of the combinations of a supplied vector or
data frame. We can then use the rows of this data frame as a set of
indices for which covariates to include/exclude from our models.
For example, say we had 2 covariates (\(A\) and \(B\)) that we wanted to use as predictors,
and we wanted all possible combinations of them in our models. To do so,
we create a Boolean indicator (T/F) for the inclusion/exclusion of each
covariate.
## possible covariates
## listing FALSE first builds from fewest to most
df <- data.frame(A = c(FALSE, TRUE), B = c(FALSE, TRUE))
## all possible combinations
expand.grid(df)
## A B
## 1 FALSE FALSE
## 2 TRUE FALSE
## 3 FALSE TRUE
## 4 TRUE TRUE
Here you can see that there are 4 possible combinations of the 2
predictors: none (intercept-only), only A, only
B, both A & B.
Model set for species diversity
Let’s go ahead and create the set of all possible combinations of our
5 covariates in the gala data. Afterwards, we can remove
those rows where both Area and Elevation or
Nearest and Scruz occur in the same model.
## data frame specifying predictors to include
df <- as.data.frame(matrix(c(FALSE, TRUE), 2, 5))
## add col names
cov_names <- colnames(df) <- colnames(gala)[3:7]
## create set of all possible combinations
full_set <- expand.grid(df)
## rows with (`Area` and `Elevation` = 1) or (`Nearest` and `Scruz` = 1)
ii <- which(full_set$Area + full_set$Elevation == 2 |
full_set$Nearest + full_set$Scruz == 2)
## create reduced set of models
## converting to a matrix for easier indexing
use_set <- as.matrix(full_set[-ii,])
## number of models in our set
(n_mods <- nrow(use_set))
## [1] 18
In-sample selection
We saw in class that we could use AIC, BIC, or both for in-sample
model selection. We can set up a routine to do the following:
- loop over all possible model combinations
- fit each model
- calculate and store the information criteria
To fit each model, we need to pass lm() a
formula object, which we can build from the covariate names
in our data frame of possible models.
Fit models
## empty matrix for storing results
mod_res <- matrix(NA, n_mods, 2)
colnames(mod_res) <- c("AIC", "BIC")
## fit models & store AIC & BIC
for(i in 1:n_mods) {
if(i == 1) {
fmla <- "Species ~ 1"
} else {
fmla <- paste("Species ~", paste(cov_names[use_set[i,]], collapse = " + "))
}
mod_fit <- lm(as.formula(fmla), data = gala)
mod_res[i,"AIC"] <- AIC(mod_fit)
mod_res[i,"BIC"] <- BIC(mod_fit)
}
Calculate \(\Delta\)IC
We saw in class that it’s easier to interpret the information
criteria if we adjust them to \(\Delta\)IC values, such that
\[
\Delta IC = IC - \min IC
\]
## empty matrix for storing results
delta_res <- matrix(NA, n_mods, 2)
colnames(delta_res) <- c("deltaAIC", "deltaBIC")
## convert IC to deltaIC
delta_res[,"deltaAIC"] <- mod_res[,"AIC"] - min(mod_res[,"AIC"])
delta_res[,"deltaBIC"] <- mod_res[,"BIC"] - min(mod_res[,"BIC"])
## round them for easier viewing
(delta_res <- round(delta_res, 2))
## deltaAIC deltaBIC
## [1,] 36.13 33.33
## [2,] 23.71 22.31
## [3,] 14.49 13.09
## [4,] 38.13 36.73
## [5,] 25.56 25.56
## [6,] 16.48 16.48
## [7,] 37.24 35.84
## [8,] 25.12 25.12
## [9,] 14.75 14.75
## [10,] 38.11 36.71
## [11,] 25.34 25.34
## [12,] 0.00 0.00
## [13,] 40.11 40.11
## [14,] 27.24 28.64
## [15,] 1.53 2.93
## [16,] 39.20 39.20
## [17,] 26.81 28.21
## [18,] 0.04 1.44
Based on AIC, model 12 is the best of the set, but model 18 has
nearly identical support from the data, and model 15 is within 2 units
as well. Based on BIC, model 12 is the best of the set, with model 18
being within 2 units as well. Let’s see which predictors are in these
models.
## "best" models from our set
cov_names[use_set[12,]]
cov_names[use_set[15,]]
cov_names[use_set[18,]]
## [1] "Elevation" "Adjacent"
## [1] "Elevation" "Nearest" "Adjacent"
## [1] "Elevation" "Scruz" "Adjacent"
All 3 of these models include both Elevation and
Adjacent as predictors, so they are clearly important.
There is some evidence that Nearest and Scruz
are also important, but they were pretty highly correlated, so they
appear to be trading off with one another. Although models 15 and 18 are
within 2-3 units of model 12, here we would choose model 12 over the
others because it only has 2 regression parameters, making it the most
parsimonious.
Best model
Let’s take a look at the summary information for model 12 with
Elevation and Adjacent as predictors.
m12 <- lm(Species ~ Elevation + Adjacent, data = gala)
faraway::sumary(m12)
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.432872 15.024687 0.0954 0.9247269
## Elevation 0.276568 0.031763 8.7074 2.533e-09
## Adjacent -0.068886 0.015490 -4.4471 0.0001344
##
## n = 30, p = 3, Residual SE = 60.85898, R-Squared = 0.74
This looks like a pretty good model based upon its \(R^2\) value, but we should really check our
model residuals for possible violations of our model assumptions, check
leverages, look for outliers, etc.
Akaike weights
We saw in lecture that we can compute the likelihood of a given model
as
\[
\mathcal{L}(y; f_i) \propto \exp \left( - \frac{1}{2} \Delta_i \right)
\]
Because the model likelihoods are all relative (just as with other
likelihoods), we can create a set of normalized Akaike weights that sum
to 1
\[
w_i = \frac{\exp \left( - \frac{1}{2} \Delta_i \right)}{\sum_{s = 1}^S
\exp \left( - \frac{1}{2} \Delta_i \right)}
\]
We can then compare the support for any model \(i\) over another model \(j\) via evidence ratios
\[
ER_{ij} = \frac{\mathcal{L}(y; f_i)}{\mathcal{L}(y; f_j)} =
\frac{w_i}{w_j}
\]
Most often, we want to compare lower ranked models to the best, which
we can simplify to
\[
\begin{aligned}
ER_{1j} &= \frac{\exp \left( - \frac{1}{2} 0 \right)}{\exp \left( -
\frac{1}{2} \Delta_j \right)} \\
&= \frac{1}{\exp \left( - \frac{1}{2} \Delta_j \right)} \\
&= \exp \left( \frac{1}{2} \Delta_j \right)
\end{aligned}
\]
Let’s go ahead and calculate our model weights and the evidence
ratios compared to our best model (#12).
## numerator
num <- exp(-0.5 * delta_res[,"deltaAIC"])
## denominator
dem <- sum(num)
## Akaike weights
wts <- num / dem
## evidence ratios
ER <- exp(0.5 * delta_res[,"deltaAIC"])
## data frame with our results
data.frame(model = seq(n_mods),
weights = round(wts, 3),
ER = floor(ER))
## model weights ER
## 1 1 0.000 70069628
## 2 2 0.000 140786
## 3 3 0.000 1401
## 4 4 0.000 190468998
## 5 5 0.000 355045
## 6 6 0.000 3789
## 7 7 0.000 122057157
## 8 8 0.000 284930
## 9 9 0.000 1595
## 10 10 0.000 188573799
## 11 11 0.000 318061
## 12 12 0.409 1
## 13 13 0.000 512596733
## 14 14 0.000 822414
## 15 15 0.190 2
## 16 16 0.000 325215956
## 17 17 0.000 663311
## 18 18 0.401 1
Clearly the evidence against most of the models is very strong, as
their weights are ~0 and the evidence ratios in favor of model #12 are
enormous.
Corrected AIC
We learned in lecture that AIC is biased when the sample size is
small, but we can account for this by using a corrected form (AICc)
given by
\[
AICc = AIC + \frac{2 k (k + 1)}{n - k - 1}
\]
Our sample size for this analysis is 30, so it’s worth seeing how the
results would differ if we used AICc instead.
## number of parameters in each model
k <- 1 + apply(use_set, 1, sum)
## calculate penalty term
pterm <- (2 * k * (k + 1)) / (nrow(gala) - k - 1)
## calculate AICc
aicc <- mod_res[,"AIC"] + pterm
## compare delta-values for both
data.frame(deltaAIC = round(delta_res[,"deltaAIC"], 1),
deltaAICc = round(aicc - min(aicc), 1))
## deltaAIC deltaAICc
## 1 36.1 35.4
## 2 23.7 23.2
## 3 14.5 14.0
## 5 38.1 37.6
## 6 25.6 25.6
## 7 16.5 16.5
## 9 37.2 36.8
## 10 25.1 25.1
## 11 14.8 14.8
## 17 38.1 37.6
## 18 25.3 25.3
## 19 0.0 0.0
## 21 40.1 40.1
## 22 27.2 27.9
## 23 1.5 2.2
## 25 39.2 39.2
## 26 26.8 27.5
## 27 0.0 0.7
Here we see very little difference between the \(\Delta\)-values.
Out-of-sample selection
We saw in lecture that we can use different forms of cross-validation
for out-of-sample model selection, which include exhaustive and
non-exhaustive methods. Let’s repeat our evaluation of the models in our
candidate set using both of these approaches. Before doing so, we also
need to choose one of the options for evaluating our predictions. Here
we’ll use the mean squared prediction error (MSPE), which is perhaps the
most common. Recall that for a model fit to \(n - q\) data points, the MSPE for the
remaining \(q\) data points it
\[
MSPE = \frac{\sum_{i = 1}^q (y_i - \hat{y}_i)^2}{q}
\]
Exhaustive cross-validation
Recall that exhaustive cross-validation works via a “leave-\(q\)-out” procedure, where we treat \(n - q\) data points as the “training”
(fitting) data and the remaining \(q\)
data points for evaluating the predictions. If \(q > 1\) and \(n\) even somewhat large, this can be
prohibitively slow because there are \(\left(
\begin{matrix} n \\ q \end{matrix} \right)\) combinations. For
example, if \(q = 3\) and \(n = 20\) there are \(\left( \begin{matrix} 20 \\ 3 \end{matrix}
\right)\) = 1140 different permutations. Therefore, here we’ll
use \(q = 1\), which gives us the
familiar “leave-one-out” (LOO) method and results in \(n\) different models being fit.
Leave-one-out
The general idea here is to set up a routine to do the following:
- loop over all possible model combinations
- for each model form, loop over the number of observations
- drop one observation and fit the model
- predict the missing value
- calculate the MSPE for the predictions
## sample size
nn <- nrow(gala)
## empty vector for predictions
loo_res <- rep(NA, nn)
## empty vector for MSPE
mspe_l <- rep(NA, n_mods)
## loop over all possible model combinations
for(i in 1:n_mods) {
## create model formula
if(i == 1) {
fmla <- "Species ~ 1"
} else {
fmla <- paste("Species ~", paste(cov_names[use_set[i,]], collapse = " + "))
}
## loop over number of observations
for(j in 1:nn) {
## drop one observation and fit the model
fm <- lm(as.formula(fmla), gala[-j,])
## predict the missing value
loo_res[j] <- predict(fm, newdata = data.frame(gala[j,]))
}
## calculate MSPE for the predictions
mspe_l[i] <- sum((gala$Species - loo_res)^2) / nn
}
Let’s check to see if these results match those from our in-sample
comparisons via AIC and BIC.
data.frame(AIC = order(mod_res[,"AIC"]),
BIC = order(mod_res[,"BIC"]),
LOO = order(mspe_l))
## AIC BIC LOO
## 1 12 12 12
## 2 18 18 18
## 3 15 15 15
## 4 3 3 9
## 5 9 9 3
## 6 6 6 6
## 7 2 2 7
## 8 8 8 1
## 9 11 11 16
## 10 5 5 10
## 11 17 17 4
## 12 14 14 13
## 13 1 1 8
## 14 7 7 2
## 15 10 10 5
## 16 4 4 11
## 17 16 16 17
## 18 13 13 14
It looks like all 3 methods agree on the top 3 models, but then the
results from the leave-one-out cross-validation start to diverge from
the in-sample results.
Non-exhaustive cross-validation
Let’s now try a non-exhaustive method where we don’t use every
combination of the \(n - q\) training
data.
\(k\)-fold
Here we’ll use \(k\)-fold
cross-validation where the data are randomly partitioned into \(k\) equal sized groups. We’ll retain one of
the \(k\) sub-samples for validation
while the remaining \(k − 1\) groups
are used for fitting. This process is then repeated \(k\) times, with each of the \(k\) sub-samples used exactly once for
validation.
We have 30 observations in our dataset, so let’s use 5 groups of 6
observations each. The rest of the analysis will proceed as above for
the leave-one-out method.
## number of groups
kk <- 5
## grop size
gs <- nn / kk
## empty vector for predictions
kf_res <- rep(NA, nn)
## empty vector for MSPE
mspe_k <- rep(NA, n_mods)
## loop over all possible model combinations
for(i in 1:n_mods) {
## create model formula
if(i == 1) {
fmla <- "Species ~ 1"
} else {
fmla <- paste("Species ~", paste(cov_names[use_set[i,]], collapse = " + "))
}
## loop over folds
for(fold in 1:kk) {
## group index
grp <- seq(gs) + gs*(fold - 1)
## drop one group and fit the model
fm <- lm(as.formula(fmla), gala[-grp,])
## predict the missing values
kf_res[grp] <- predict(fm, newdata = data.frame(gala[grp,]))
}
## calculate MSPE for the predictions
mspe_k[i] <- sum((gala$Species - kf_res)^2) / nn
}
Now we can compare these results to the other model selection results
from above.
data.frame(AIC = order(mod_res[,"AIC"]),
BIC = order(mod_res[,"BIC"]),
LOO = order(mspe_l),
kfold = order(mspe_k))
## AIC BIC LOO kfold
## 1 12 12 12 12
## 2 18 18 18 18
## 3 15 15 15 15
## 4 3 3 9 9
## 5 9 9 3 3
## 6 6 6 6 6
## 7 2 2 7 1
## 8 8 8 1 10
## 9 11 11 16 7
## 10 5 5 10 16
## 11 17 17 4 4
## 12 14 14 13 13
## 13 1 1 8 8
## 14 7 7 2 2
## 15 10 10 5 5
## 16 4 4 11 11
## 17 16 16 17 14
## 18 13 13 14 17
Here we see that all four methods come up with the same rankings for
the top 3 models, and that both of the out-of-sample methods are nearly
identical, too. It’s also important to recognize that we likely would
have found a somewhat different result if we had chosen a differet
number of groups for the \(k\)-fold
cross-validation.
Multi-model inference
We saw in lecture that we can use the Akaike weights to average
parameters from different models as a means of addressing uncertainty in
our model structures. Recall that for a given parameter \(\theta\), it’s model averaged estimate
is
\[
\bar{\hat{\theta}} = \sum_{i = 1}^S w_i \hat{\theta}_i
\]
where \(S\) is the total number of
models in the set. Usually a given parameter \(\theta\) does not appear in all models, so
we can use an indicator function to compute the average estimate
\[
\bar{\hat{\theta}} = \frac{\sum_{i = 1}^S I(f_i) w_i
\hat{\theta}_i}{\sum_{i = 1}^S I(f_i) w_i} \\
~ \\
I(f_i) =
\left\{
\begin{array} {ll}
1 & \text{if} ~ \theta ~\text{is in} ~ f_i \\
0 & \text{otherwise}
\end{array}
\right.
\]
Here we saw that 2 or 3 of our models had very similar support from
the data, so it might be worth averaging our coefficients across all of
the models. In this case, however, the top 3 models contain 99.9% of the
total weights, so the remaining models will have very little influence
on the results. (Also, it would have been more efficient to do all of
this the first time we fit our models, but that’s okay.)
## empty matrix for storing coefficients
## we'll fill it with 0's and replace them with the param estimates
mod_coef <- matrix(0, n_mods, 1 + ncol(df))
colnames(mod_coef) <- c("Intercept", colnames(df))
## fit models & store AIC & BIC
for(i in 1:n_mods) {
if(i == 1) {
fmla <- "Species ~ 1"
} else {
fmla <- paste("Species ~", paste(cov_names[use_set[i,]], collapse = " + "))
}
mod_fit <- lm(as.formula(fmla), data = gala)
mod_coef[i, c(TRUE, use_set[i,])] <- coef(mod_fit)
}
## calculate weighted coefs
wtd_coef <- mod_coef * wts
(avg_coef <- colSums(wtd_coef))
## Intercept Area Elevation Nearest Scruz
## 7.544112e+00 6.494906e-07 2.759027e-01 -9.823119e-02 -8.732447e-02
## Adjacent
## -6.851311e-02
It’s important to note here that using this model in practice isn’t a
good idea because of the high correlation between Area and
Elevation and between Nearest and
Scruz. An alternative approach would be to use each of the
18 models to make a prediction for a given \(\mathbf{X}\) and then weight those
predictions to come up with a model-averaged predictions.
LS0tCnRpdGxlOiAiTW9kZWwgc2VsZWN0aW9uIGFuZCBtdWx0aS1tb2RlbCBpbmZlcmVuY2UiCmF1dGhvcjogIk1hcmsgU2NoZXVlcmVsbCIKZGF0ZTogIjEgTWF5IDIwMjYiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IAogICAgICBib290c3dhdGNoOiBqb3VybmFsCiAgICAgIHByaW1hcnk6ICIjMzIwMDZlIgogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQogICAgY3NzOiAuLi9sZWN0dXJlX2luc3QuY3NzCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfZGVwdGg6IDQKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UpCmBgYAoKIyBCYWNrZ3JvdW5kCgpUaGVzZSBsYWIgZXhlcmNpc2VzIHdpbGwgZGVtb25zdHJhdGUgc29tZSBvZiB0aGUgb3B0aW9ucyBmb3IgbW9kZWwgc2VsZWN0aW9uIHRoYXQgd2UgZGlzY3Vzc2VkIGluIGxlY3R1cmUsIHdoaWNoIGluY2x1ZGUgYm90aCBpbi1zYW1wbGUgYW5kIG91dC1vZi1zYW1wbGUgbWV0aG9kcy4gV2Ugd2lsbCByZXR1cm4gdG8gdGhlIHBsYW50IHNwZWNpZXMgcmljaG5lc3MgZGF0YSBmcm9tIHRoZSBHYWxhcGFnb3MgQXJjaGlwZWxhZ28gY29udGFpbmVkIGluIHRoZSBgZ2FsYWAgZGF0YXNldCBpbiB0aGUgKipGYXJhd2F5KiogcGFja2FnZS4gQXMgYSByZW1pbmRlciwgaGVyZSBhcmUgdGhlIHZhcmlhYmxlczoKCiogYFNwZWNpZXNgOiB0aGUgbnVtYmVyIG9mIHBsYW50IHNwZWNpZXMgZm91bmQgb24gdGhlIGlzbGFuZAoKKiBgRW5kZW1pY3NgOiB0aGUgbnVtYmVyIG9mIGVuZGVtaWMgc3BlY2llcwoKKiBgQXJlYWA6IHRoZSBhcmVhIG9mIHRoZSBpc2xhbmQgKGttJF4yJCkKCiogYEVsZXZhdGlvbmA6IHRoZSBoaWdoZXN0IGVsZXZhdGlvbiBvZiB0aGUgaXNsYW5kIChtKQoKKiBgTmVhcmVzdGA6IHRoZSBkaXN0YW5jZSBmcm9tIHRoZSBuZWFyZXN0IGlzbGFuZCAoa20pCgoqIGBTY3J1emA6IHRoZSBkaXN0YW5jZSBmcm9tIFNhbnRhIENydXogaXNsYW5kIChrbSkKCiogYEFkamFjZW50YDogdGhlIGFyZWEgb2YgdGhlIGFkamFjZW50IGlzbGFuZCAoa20kXjIkKQoKV2Ugd2lsbCBpZ25vcmUgdGhlIG51bWJlciBvZiBlbmRlbWljIHNwZWNpZXMgKGBFbmRlbWljc2ApIGFuZCBmb2N1cyBvbiBwb3NzaWJsZSBtb2RlbHMgdG8gZXhwbGFpbiB0aGUgdG90YWwgbnVtYmVyIG9mIHNwZWNpZXMgKGBTcGVjaWVzYCkgYXMgYSBmdW5jdGlvbiBvZiB0aGUgNSBjb3ZhcmlhdGVzIChwcmVkaWN0b3JzKS4gQmVmb3JlIGRpdmluZyByaWdodCBpbiwgbGV0J3MgY29uc2lkZXIgdGhlIHBvc3NpYmxlIGNvcnJlbGF0aW9ucyBhbW9uZyB0aGVzZSBjb3ZhcmlhdGVzLCBhcyB3ZSB3YW50IHRvIGF2b2lkIGluY2x1ZGluZyBhbnkgdHdvIGNvdmFyaWF0ZXMgaW4gdGhlIHNhbWUgbW9kZWwgaWYgdGhleWFyZSBoaWdobHkgY29ycmVsYXRlZC4KCmBgYHtyIGNoZWNrX2RhdGF9CiMjIGdldCBkYXRhCmRhdGEoZ2FsYSwgcGFja2FnZSA9ICJmYXJhd2F5IikKZGF0IDwtIGdhbGEKaGVhZChkYXQpCiMjIGNoZWNrIGNvcnJlbGF0aW9ucwpyb3VuZChjb3IoZGF0WywzOjddKSwgMikKYGBgCgpUaGVyZSBhcHBlYXJzIHRvIGJlIGhpZ2ggY29ycmVsYXRpb24gYmV0d2VlbiBgQXJlYWAgYW5kIGBFbGV2YXRpb25gICgkXHJobyQgPSBgciByb3VuZChjb3IoZ2FsYVssMzo0XSksIDIpWzEsMl1gKSwgYW5kIHJlYXNvbmFibHkgaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIGBOZWFyZXN0YCBhbmQgYFNjcnV6YCAoJFxyaG8kID0gYHIgcm91bmQoY29yKGdhbGFbLDU6Nl0pLCAyKVsxLDJdYCksIHNvIGxldCdzIGJlIGNhcmVmdWwgdG8ga2VlcCB0aG9zZSB2YXJpYWJsZXMgc2VwYXJhdGUgZnJvbSBvbmUgYW5vdGhlciBpbiBvdXIgbW9kZWxzLgoKIyMgQ3JlYXRpbmcgYSBtb2RlbCBzZXQKCldlIG5lZWQgdG8gY3JlYXRlIGEgc2V0IG9mIGFsbCB0aGUgbW9kZWxzIHdlJ2QgbGlrZSB0byBmaXQgYW5kIGV2YWx1YXRlIGZvciB0aGVpciBkYXRhIHN1cHBvcnQuIFRvIGRvIHNvLCB3ZSBjYW4gbWFrZSB1c2Ugb2YgdGhlIGBleHBhbmQuZ3JpZCgpYCBmdW5jdGlvbiBpbiAqKlIqKiwgd2hpY2ggd2lsbCBjcmVhdGUgYSBkYXRhIGZyYW1lIGZyb20gYWxsIG9mIHRoZSBjb21iaW5hdGlvbnMgb2YgYSBzdXBwbGllZCB2ZWN0b3Igb3IgZGF0YSBmcmFtZS4gV2UgY2FuIHRoZW4gdXNlIHRoZSByb3dzIG9mIHRoaXMgZGF0YSBmcmFtZSBhcyBhIHNldCBvZiBpbmRpY2VzIGZvciB3aGljaCBjb3ZhcmlhdGVzIHRvIGluY2x1ZGUvZXhjbHVkZSBmcm9tIG91ciBtb2RlbHMuIAoKRm9yIGV4YW1wbGUsIHNheSB3ZSBoYWQgMiBjb3ZhcmlhdGVzICgkQSQgYW5kICRCJCkgdGhhdCB3ZSB3YW50ZWQgdG8gdXNlIGFzIHByZWRpY3RvcnMsIGFuZCB3ZSB3YW50ZWQgYWxsIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBvZiB0aGVtIGluIG91ciBtb2RlbHMuIFRvIGRvIHNvLCB3ZSBjcmVhdGUgYSBCb29sZWFuIGluZGljYXRvciAoVC9GKSBmb3IgdGhlIGluY2x1c2lvbi9leGNsdXNpb24gb2YgZWFjaCBjb3ZhcmlhdGUuCgpgYGB7ciBkZW1vX2V4cGFuZGdyaWR9CiMjIHBvc3NpYmxlIGNvdmFyaWF0ZXMKIyMgbGlzdGluZyBGQUxTRSBmaXJzdCBidWlsZHMgZnJvbSBmZXdlc3QgdG8gbW9zdApkZiA8LSBkYXRhLmZyYW1lKEEgPSBjKEZBTFNFLCBUUlVFKSwgQiA9IGMoRkFMU0UsIFRSVUUpKQojIyBhbGwgcG9zc2libGUgY29tYmluYXRpb25zCmV4cGFuZC5ncmlkKGRmKQpgYGAKCkhlcmUgeW91IGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgNCBwb3NzaWJsZSBjb21iaW5hdGlvbnMgb2YgdGhlIDIgcHJlZGljdG9yczogbm9uZSAoaW50ZXJjZXB0LW9ubHkpLCBvbmx5IGBBYCwgb25seSBgQmAsIGJvdGggYEFgICYgYEJgLgoKIyMgTW9kZWwgc2V0IGZvciBzcGVjaWVzIGRpdmVyc2l0eQoKTGV0J3MgZ28gYWhlYWQgYW5kIGNyZWF0ZSB0aGUgc2V0IG9mIGFsbCBwb3NzaWJsZSBjb21iaW5hdGlvbnMgb2Ygb3VyIDUgY292YXJpYXRlcyBpbiB0aGUgYGdhbGFgIGRhdGEuIEFmdGVyd2FyZHMsIHdlIGNhbiByZW1vdmUgdGhvc2Ugcm93cyB3aGVyZSBib3RoIGBBcmVhYCBhbmQgYEVsZXZhdGlvbmAgb3IgYE5lYXJlc3RgIGFuZCBgU2NydXpgIG9jY3VyIGluIHRoZSBzYW1lIG1vZGVsLgoKYGBge3IgY3JlYXRlX21vZGVsX3NldHN9CiMjIGRhdGEgZnJhbWUgc3BlY2lmeWluZyBwcmVkaWN0b3JzIHRvIGluY2x1ZGUKZGYgPC0gYXMuZGF0YS5mcmFtZShtYXRyaXgoYyhGQUxTRSwgVFJVRSksIDIsIDUpKQojIyBhZGQgY29sIG5hbWVzCmNvdl9uYW1lcyA8LSBjb2xuYW1lcyhkZikgPC0gY29sbmFtZXMoZ2FsYSlbMzo3XQoKIyMgY3JlYXRlIHNldCBvZiBhbGwgcG9zc2libGUgY29tYmluYXRpb25zCmZ1bGxfc2V0IDwtIGV4cGFuZC5ncmlkKGRmKQoKIyMgcm93cyB3aXRoIChgQXJlYWAgYW5kIGBFbGV2YXRpb25gID0gMSkgb3IgKGBOZWFyZXN0YCBhbmQgYFNjcnV6YCA9IDEpCmlpIDwtIHdoaWNoKGZ1bGxfc2V0JEFyZWEgKyBmdWxsX3NldCRFbGV2YXRpb24gPT0gMiB8CiAgICAgICAgICAgICAgZnVsbF9zZXQkTmVhcmVzdCArIGZ1bGxfc2V0JFNjcnV6ID09IDIpCiMjIGNyZWF0ZSByZWR1Y2VkIHNldCBvZiBtb2RlbHMKIyMgY29udmVydGluZyB0byBhIG1hdHJpeCBmb3IgZWFzaWVyIGluZGV4aW5nCnVzZV9zZXQgPC0gYXMubWF0cml4KGZ1bGxfc2V0Wy1paSxdKQoKIyMgbnVtYmVyIG9mIG1vZGVscyBpbiBvdXIgc2V0CihuX21vZHMgPC0gbnJvdyh1c2Vfc2V0KSkKYGBgCgoKIyBJbi1zYW1wbGUgc2VsZWN0aW9uCgpXZSBzYXcgaW4gY2xhc3MgdGhhdCB3ZSBjb3VsZCB1c2UgQUlDLCBCSUMsIG9yIGJvdGggZm9yIGluLXNhbXBsZSBtb2RlbCBzZWxlY3Rpb24uIFdlIGNhbiBzZXQgdXAgYSByb3V0aW5lIHRvIGRvIHRoZSBmb2xsb3dpbmc6CgoqIGxvb3Agb3ZlciBhbGwgcG9zc2libGUgbW9kZWwgY29tYmluYXRpb25zICAKICAgICogZml0IGVhY2ggbW9kZWwgIAogICAgKiBjYWxjdWxhdGUgYW5kIHN0b3JlIHRoZSBpbmZvcm1hdGlvbiBjcml0ZXJpYQoKVG8gZml0IGVhY2ggbW9kZWwsIHdlIG5lZWQgdG8gcGFzcyBgbG0oKWAgYSBgZm9ybXVsYWAgb2JqZWN0LCB3aGljaCB3ZSBjYW4gYnVpbGQgZnJvbSB0aGUgY292YXJpYXRlIG5hbWVzIGluIG91ciBkYXRhIGZyYW1lIG9mIHBvc3NpYmxlIG1vZGVscy4KCiMjIEZpdCBtb2RlbHMKCmBgYHtyIGZpdF9tb2RlbF9zZXR9CiMjIGVtcHR5IG1hdHJpeCBmb3Igc3RvcmluZyByZXN1bHRzCm1vZF9yZXMgPC0gbWF0cml4KE5BLCBuX21vZHMsIDIpCmNvbG5hbWVzKG1vZF9yZXMpIDwtIGMoIkFJQyIsICJCSUMiKQoKIyMgZml0IG1vZGVscyAmIHN0b3JlIEFJQyAmIEJJQwpmb3IoaSBpbiAxOm5fbW9kcykgewogIGlmKGkgPT0gMSkgewogICAgZm1sYSA8LSAiU3BlY2llcyB+IDEiCiAgfSBlbHNlIHsKICAgIGZtbGEgPC0gcGFzdGUoIlNwZWNpZXMgfiIsIHBhc3RlKGNvdl9uYW1lc1t1c2Vfc2V0W2ksXV0sIGNvbGxhcHNlID0gIiArICIpKQogIH0KICBtb2RfZml0IDwtIGxtKGFzLmZvcm11bGEoZm1sYSksIGRhdGEgPSBnYWxhKQogIG1vZF9yZXNbaSwiQUlDIl0gPC0gQUlDKG1vZF9maXQpCiAgbW9kX3Jlc1tpLCJCSUMiXSA8LSBCSUMobW9kX2ZpdCkKfQpgYGAKCiMjIENhbGN1bGF0ZSAkXERlbHRhJElDCgpXZSBzYXcgaW4gY2xhc3MgdGhhdCBpdCdzIGVhc2llciB0byBpbnRlcnByZXQgdGhlIGluZm9ybWF0aW9uIGNyaXRlcmlhIGlmIHdlIGFkanVzdCB0aGVtIHRvICRcRGVsdGEkSUMgdmFsdWVzLCBzdWNoIHRoYXQKCiQkClxEZWx0YSBJQyA9IElDIC0gXG1pbiBJQwokJAoKYGBge3IgZGVsdGFfSUN9CiMjIGVtcHR5IG1hdHJpeCBmb3Igc3RvcmluZyByZXN1bHRzCmRlbHRhX3JlcyA8LSBtYXRyaXgoTkEsIG5fbW9kcywgMikKY29sbmFtZXMoZGVsdGFfcmVzKSA8LSBjKCJkZWx0YUFJQyIsICJkZWx0YUJJQyIpCgojIyBjb252ZXJ0IElDIHRvIGRlbHRhSUMKZGVsdGFfcmVzWywiZGVsdGFBSUMiXSA8LSBtb2RfcmVzWywiQUlDIl0gLSBtaW4obW9kX3Jlc1ssIkFJQyJdKQpkZWx0YV9yZXNbLCJkZWx0YUJJQyJdIDwtIG1vZF9yZXNbLCJCSUMiXSAtIG1pbihtb2RfcmVzWywiQklDIl0pCgojIyByb3VuZCB0aGVtIGZvciBlYXNpZXIgdmlld2luZwooZGVsdGFfcmVzIDwtIHJvdW5kKGRlbHRhX3JlcywgMikpCmBgYAoKQmFzZWQgb24gQUlDLCBtb2RlbCAxMiBpcyB0aGUgYmVzdCBvZiB0aGUgc2V0LCBidXQgbW9kZWwgMTggaGFzIG5lYXJseSBpZGVudGljYWwgc3VwcG9ydCBmcm9tIHRoZSBkYXRhLCBhbmQgbW9kZWwgMTUgaXMgd2l0aGluIDIgdW5pdHMgYXMgd2VsbC4gQmFzZWQgb24gQklDLCBtb2RlbCAxMiBpcyB0aGUgYmVzdCBvZiB0aGUgc2V0LCB3aXRoIG1vZGVsIDE4IGJlaW5nIHdpdGhpbiAyIHVuaXRzIGFzIHdlbGwuIExldCdzIHNlZSB3aGljaCBwcmVkaWN0b3JzIGFyZSBpbiB0aGVzZSBtb2RlbHMuCgpgYGB7ciBjaGVja19zZXQsIHJlc3VsdHM9J2hvbGQnfQojIyAiYmVzdCIgbW9kZWxzIGZyb20gb3VyIHNldApjb3ZfbmFtZXNbdXNlX3NldFsxMixdXQpjb3ZfbmFtZXNbdXNlX3NldFsxNSxdXQpjb3ZfbmFtZXNbdXNlX3NldFsxOCxdXQpgYGAKCkFsbCAzIG9mIHRoZXNlIG1vZGVscyBpbmNsdWRlIGJvdGggYEVsZXZhdGlvbmAgYW5kIGBBZGphY2VudGAgYXMgcHJlZGljdG9ycywgc28gdGhleSBhcmUgY2xlYXJseSBpbXBvcnRhbnQuIFRoZXJlIGlzIHNvbWUgZXZpZGVuY2UgdGhhdCBgTmVhcmVzdGAgYW5kIGBTY3J1emAgYXJlIGFsc28gaW1wb3J0YW50LCBidXQgdGhleSB3ZXJlIHByZXR0eSBoaWdobHkgY29ycmVsYXRlZCwgc28gdGhleSBhcHBlYXIgdG8gYmUgdHJhZGluZyBvZmYgd2l0aCBvbmUgYW5vdGhlci4gQWx0aG91Z2ggbW9kZWxzIDE1IGFuZCAxOCBhcmUgd2l0aGluIDItMyB1bml0cyBvZiBtb2RlbCAxMiwgaGVyZSB3ZSB3b3VsZCBjaG9vc2UgbW9kZWwgMTIgb3ZlciB0aGUgb3RoZXJzIGJlY2F1c2UgaXQgb25seSBoYXMgMiByZWdyZXNzaW9uIHBhcmFtZXRlcnMsIG1ha2luZyBpdCB0aGUgbW9zdCBwYXJzaW1vbmlvdXMuCgojIyMgQmVzdCBtb2RlbAoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIHN1bW1hcnkgaW5mb3JtYXRpb24gZm9yIG1vZGVsIDEyIHdpdGggYEVsZXZhdGlvbmAgYW5kIGBBZGphY2VudGAgYXMgcHJlZGljdG9ycy4KCmBgYHtyIGNoZWNrX2Jlc3R9Cm0xMiA8LSBsbShTcGVjaWVzIH4gRWxldmF0aW9uICsgQWRqYWNlbnQsIGRhdGEgPSBnYWxhKQpmYXJhd2F5OjpzdW1hcnkobTEyKQpgYGAKClRoaXMgbG9va3MgbGlrZSBhIHByZXR0eSBnb29kIG1vZGVsIGJhc2VkIHVwb24gaXRzICRSXjIkIHZhbHVlLCBidXQgd2Ugc2hvdWxkIHJlYWxseSBjaGVjayBvdXIgbW9kZWwgcmVzaWR1YWxzIGZvciBwb3NzaWJsZSB2aW9sYXRpb25zIG9mIG91ciBtb2RlbCBhc3N1bXB0aW9ucywgY2hlY2sgbGV2ZXJhZ2VzLCBsb29rIGZvciBvdXRsaWVycywgZXRjLgoKIyMgQWthaWtlIHdlaWdodHMKCldlIHNhdyBpbiBsZWN0dXJlIHRoYXQgd2UgY2FuIGNvbXB1dGUgdGhlIGxpa2VsaWhvb2Qgb2YgYSBnaXZlbiBtb2RlbCBhcwoKJCQKXG1hdGhjYWx7TH0oeTsgZl9pKSBccHJvcHRvIFxleHAgXGxlZnQoIC0gXGZyYWN7MX17Mn0gXERlbHRhX2kgXHJpZ2h0KQokJAoKQmVjYXVzZSB0aGUgbW9kZWwgbGlrZWxpaG9vZHMgYXJlIGFsbCByZWxhdGl2ZSAoanVzdCBhcyB3aXRoIG90aGVyIGxpa2VsaWhvb2RzKSwgd2UgY2FuIGNyZWF0ZSBhIHNldCBvZiBub3JtYWxpemVkIEFrYWlrZSB3ZWlnaHRzIHRoYXQgc3VtIHRvIDEKCiQkCndfaSA9IFxmcmFje1xleHAgXGxlZnQoIC0gXGZyYWN7MX17Mn0gXERlbHRhX2kgXHJpZ2h0KX17XHN1bV97cyA9IDF9XlMgXGV4cCBcbGVmdCggLSBcZnJhY3sxfXsyfSBcRGVsdGFfaSBccmlnaHQpfQokJAoKV2UgY2FuIHRoZW4gY29tcGFyZSB0aGUgc3VwcG9ydCBmb3IgYW55IG1vZGVsICRpJCBvdmVyIGFub3RoZXIgbW9kZWwgJGokIHZpYSBldmlkZW5jZSByYXRpb3MKCiQkCkVSX3tpan0gPSBcZnJhY3tcbWF0aGNhbHtMfSh5OyBmX2kpfXtcbWF0aGNhbHtMfSh5OyBmX2opfSA9IFxmcmFje3dfaX17d19qfQokJAoKTW9zdCBvZnRlbiwgd2Ugd2FudCB0byBjb21wYXJlIGxvd2VyIHJhbmtlZCBtb2RlbHMgdG8gdGhlIGJlc3QsIHdoaWNoIHdlIGNhbiBzaW1wbGlmeSB0bwoKJCQKXGJlZ2lue2FsaWduZWR9CkVSX3sxan0gJj0gIFxmcmFje1xleHAgXGxlZnQoIC0gXGZyYWN7MX17Mn0gMCBccmlnaHQpfXtcZXhwIFxsZWZ0KCAtIFxmcmFjezF9ezJ9IFxEZWx0YV9qIFxyaWdodCl9IFxcCiAgJj0gXGZyYWN7MX17XGV4cCBcbGVmdCggLSBcZnJhY3sxfXsyfSBcRGVsdGFfaiBccmlnaHQpfSAgXFwKICAmPSBcZXhwIFxsZWZ0KCBcZnJhY3sxfXsyfSBcRGVsdGFfaiBccmlnaHQpClxlbmR7YWxpZ25lZH0KJCQKCkxldCdzIGdvIGFoZWFkIGFuZCBjYWxjdWxhdGUgb3VyIG1vZGVsIHdlaWdodHMgYW5kIHRoZSBldmlkZW5jZSByYXRpb3MgY29tcGFyZWQgdG8gb3VyIGJlc3QgbW9kZWwgKCMxMikuCgpgYGB7ciBtb2RlbF93ZWlnaHRzfQojIyBudW1lcmF0b3IKbnVtIDwtIGV4cCgtMC41ICogZGVsdGFfcmVzWywiZGVsdGFBSUMiXSkKIyMgZGVub21pbmF0b3IKZGVtIDwtIHN1bShudW0pCiMjIEFrYWlrZSB3ZWlnaHRzCnd0cyA8LSBudW0gLyBkZW0KIyMgZXZpZGVuY2UgcmF0aW9zCkVSIDwtIGV4cCgwLjUgKiBkZWx0YV9yZXNbLCJkZWx0YUFJQyJdKQojIyBkYXRhIGZyYW1lIHdpdGggb3VyIHJlc3VsdHMKZGF0YS5mcmFtZShtb2RlbCA9IHNlcShuX21vZHMpLAogICAgICAgICAgIHdlaWdodHMgPSByb3VuZCh3dHMsIDMpLAogICAgICAgICAgIEVSID0gZmxvb3IoRVIpKQpgYGAKCkNsZWFybHkgdGhlIGV2aWRlbmNlIGFnYWluc3QgbW9zdCBvZiB0aGUgbW9kZWxzIGlzIHZlcnkgc3Ryb25nLCBhcyB0aGVpciB3ZWlnaHRzIGFyZSB+MCBhbmQgdGhlIGV2aWRlbmNlIHJhdGlvcyBpbiBmYXZvciBvZiBtb2RlbCAjMTIgYXJlIGVub3Jtb3VzLgoKIyMgQ29ycmVjdGVkIEFJQwoKV2UgbGVhcm5lZCBpbiBsZWN0dXJlIHRoYXQgQUlDIGlzIGJpYXNlZCB3aGVuIHRoZSBzYW1wbGUgc2l6ZSBpcyBzbWFsbCwgYnV0IHdlIGNhbiBhY2NvdW50IGZvciB0aGlzIGJ5IHVzaW5nIGEgY29ycmVjdGVkIGZvcm0gKEFJQ2MpIGdpdmVuIGJ5CgokJApBSUNjID0gQUlDICsgXGZyYWN7MiBrIChrICsgMSl9e24gLSBrIC0gMX0KJCQKCk91ciBzYW1wbGUgc2l6ZSBmb3IgdGhpcyBhbmFseXNpcyBpcyBgciBucm93KGdhbGEpYCwgc28gaXQncyB3b3J0aCBzZWVpbmcgaG93IHRoZSByZXN1bHRzIHdvdWxkIGRpZmZlciBpZiB3ZSB1c2VkIEFJQ2MgaW5zdGVhZC4KCmBgYHtyIGFpY2N9CiMjIG51bWJlciBvZiBwYXJhbWV0ZXJzIGluIGVhY2ggbW9kZWwKayA8LSAxICsgYXBwbHkodXNlX3NldCwgMSwgc3VtKQojIyBjYWxjdWxhdGUgcGVuYWx0eSB0ZXJtCnB0ZXJtIDwtICgyICogayAqIChrICsgMSkpIC8gKG5yb3coZ2FsYSkgLSBrIC0gMSkKIyMgY2FsY3VsYXRlIEFJQ2MKYWljYyA8LSBtb2RfcmVzWywiQUlDIl0gKyBwdGVybQojIyBjb21wYXJlIGRlbHRhLXZhbHVlcyBmb3IgYm90aApkYXRhLmZyYW1lKGRlbHRhQUlDID0gcm91bmQoZGVsdGFfcmVzWywiZGVsdGFBSUMiXSwgMSksCiAgICAgICAgICAgZGVsdGFBSUNjID0gcm91bmQoYWljYyAtIG1pbihhaWNjKSwgMSkpCmBgYAoKSGVyZSB3ZSBzZWUgdmVyeSBsaXR0bGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSAkXERlbHRhJC12YWx1ZXMuCgoKIyBPdXQtb2Ytc2FtcGxlIHNlbGVjdGlvbgoKV2Ugc2F3IGluIGxlY3R1cmUgdGhhdCB3ZSBjYW4gdXNlIGRpZmZlcmVudCBmb3JtcyBvZiBjcm9zcy12YWxpZGF0aW9uIGZvciBvdXQtb2Ytc2FtcGxlIG1vZGVsIHNlbGVjdGlvbiwgd2hpY2ggaW5jbHVkZSBleGhhdXN0aXZlIGFuZCBub24tZXhoYXVzdGl2ZSBtZXRob2RzLiBMZXQncyByZXBlYXQgb3VyIGV2YWx1YXRpb24gb2YgdGhlIG1vZGVscyBpbiBvdXIgY2FuZGlkYXRlIHNldCB1c2luZyBib3RoIG9mIHRoZXNlIGFwcHJvYWNoZXMuIEJlZm9yZSBkb2luZyBzbywgd2UgYWxzbyBuZWVkIHRvIGNob29zZSBvbmUgb2YgdGhlIG9wdGlvbnMgZm9yIGV2YWx1YXRpbmcgb3VyIHByZWRpY3Rpb25zLiBIZXJlIHdlJ2xsIHVzZSB0aGUgbWVhbiBzcXVhcmVkIHByZWRpY3Rpb24gZXJyb3IgKE1TUEUpLCB3aGljaCBpcyBwZXJoYXBzIHRoZSBtb3N0IGNvbW1vbi4gUmVjYWxsIHRoYXQgZm9yIGEgbW9kZWwgZml0IHRvICRuIC0gcSQgZGF0YSBwb2ludHMsIHRoZSBNU1BFIGZvciB0aGUgcmVtYWluaW5nICRxJCBkYXRhIHBvaW50cyBpdAoKJCQKTVNQRSA9IFxmcmFje1xzdW1fe2kgPSAxfV5xICh5X2kgLSBcaGF0e3l9X2kpXjJ9e3F9CiQkCgojIyBFeGhhdXN0aXZlIGNyb3NzLXZhbGlkYXRpb24KClJlY2FsbCB0aGF0IGV4aGF1c3RpdmUgY3Jvc3MtdmFsaWRhdGlvbiB3b3JrcyB2aWEgYSAibGVhdmUtJHEkLW91dCIgcHJvY2VkdXJlLCB3aGVyZSB3ZSB0cmVhdCAkbiAtIHEkIGRhdGEgcG9pbnRzIGFzIHRoZSAidHJhaW5pbmciIChmaXR0aW5nKSBkYXRhIGFuZCB0aGUgcmVtYWluaW5nICRxJCBkYXRhIHBvaW50cyBmb3IgZXZhbHVhdGluZyB0aGUgcHJlZGljdGlvbnMuIElmICRxID4gMSQgYW5kICRuJCBldmVuIHNvbWV3aGF0IGxhcmdlLCB0aGlzIGNhbiBiZSBwcm9oaWJpdGl2ZWx5IHNsb3cgYmVjYXVzZSB0aGVyZSBhcmUgJFxsZWZ0KCBcYmVnaW57bWF0cml4fSBuIFxcIHEgXGVuZHttYXRyaXh9IFxyaWdodCkkIGNvbWJpbmF0aW9ucy4gRm9yIGV4YW1wbGUsIGlmICRxID0gMyQgYW5kICRuID0gMjAkIHRoZXJlIGFyZSAkXGxlZnQoIFxiZWdpbnttYXRyaXh9IDIwIFxcIDMgXGVuZHttYXRyaXh9IFxyaWdodCkkID0gYHIgY2hvb3NlKDIwLCAzKWAgZGlmZmVyZW50IHBlcm11dGF0aW9ucy4gVGhlcmVmb3JlLCBoZXJlIHdlJ2xsIHVzZSAkcSA9IDEkLCB3aGljaCBnaXZlcyB1cyB0aGUgZmFtaWxpYXIgImxlYXZlLW9uZS1vdXQiIChMT08pIG1ldGhvZCBhbmQgcmVzdWx0cyBpbiAkbiQgZGlmZmVyZW50IG1vZGVscyBiZWluZyBmaXQuCgojIyMgTGVhdmUtb25lLW91dAoKVGhlIGdlbmVyYWwgaWRlYSBoZXJlIGlzIHRvIHNldCB1cCBhIHJvdXRpbmUgdG8gZG8gdGhlIGZvbGxvd2luZzoKCiogbG9vcCBvdmVyIGFsbCBwb3NzaWJsZSBtb2RlbCBjb21iaW5hdGlvbnMgIAogICAgKiBmb3IgZWFjaCBtb2RlbCBmb3JtLCBsb29wIG92ZXIgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMKICAgICAgICAqIGRyb3Agb25lIG9ic2VydmF0aW9uIGFuZCBmaXQgdGhlIG1vZGVsCiAgICAgICAgKiBwcmVkaWN0IHRoZSBtaXNzaW5nIHZhbHVlCiAgICAgICAgKiBjYWxjdWxhdGUgdGhlIE1TUEUgZm9yIHRoZSBwcmVkaWN0aW9ucwoKYGBge3IgTE9PX2J5X2hhbmR9CiMjIHNhbXBsZSBzaXplCm5uIDwtIG5yb3coZ2FsYSkKIyMgZW1wdHkgdmVjdG9yIGZvciBwcmVkaWN0aW9ucwpsb29fcmVzIDwtIHJlcChOQSwgbm4pCiMjIGVtcHR5IHZlY3RvciBmb3IgTVNQRQptc3BlX2wgPC0gcmVwKE5BLCBuX21vZHMpCgojIyBsb29wIG92ZXIgYWxsIHBvc3NpYmxlIG1vZGVsIGNvbWJpbmF0aW9ucwpmb3IoaSBpbiAxOm5fbW9kcykgewogICMjIGNyZWF0ZSBtb2RlbCBmb3JtdWxhCiAgaWYoaSA9PSAxKSB7CiAgICBmbWxhIDwtICJTcGVjaWVzIH4gMSIKICB9IGVsc2UgewogICAgZm1sYSA8LSBwYXN0ZSgiU3BlY2llcyB+IiwgcGFzdGUoY292X25hbWVzW3VzZV9zZXRbaSxdXSwgY29sbGFwc2UgPSAiICsgIikpCiAgfQogICMjIGxvb3Agb3ZlciBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zCiAgZm9yKGogaW4gMTpubikgewogICAgIyMgZHJvcCBvbmUgb2JzZXJ2YXRpb24gYW5kIGZpdCB0aGUgbW9kZWwKICAgIGZtIDwtIGxtKGFzLmZvcm11bGEoZm1sYSksIGdhbGFbLWosXSkKICAgICMjIHByZWRpY3QgdGhlIG1pc3NpbmcgdmFsdWUKICAgIGxvb19yZXNbal0gPC0gcHJlZGljdChmbSwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoZ2FsYVtqLF0pKQogIH0KICAjIyBjYWxjdWxhdGUgTVNQRSBmb3IgdGhlIHByZWRpY3Rpb25zCiAgbXNwZV9sW2ldIDwtIHN1bSgoZ2FsYSRTcGVjaWVzIC0gbG9vX3JlcyleMikgLyBubgp9CmBgYAoKTGV0J3MgY2hlY2sgdG8gc2VlIGlmIHRoZXNlIHJlc3VsdHMgbWF0Y2ggdGhvc2UgZnJvbSBvdXIgaW4tc2FtcGxlIGNvbXBhcmlzb25zIHZpYSBBSUMgYW5kIEJJQy4KCmBgYHtyIGNoa19tZXRob2RzfQpkYXRhLmZyYW1lKEFJQyA9IG9yZGVyKG1vZF9yZXNbLCJBSUMiXSksCiAgICAgICAgICAgQklDID0gb3JkZXIobW9kX3Jlc1ssIkJJQyJdKSwKICAgICAgICAgICBMT08gPSBvcmRlcihtc3BlX2wpKQpgYGAKCkl0IGxvb2tzIGxpa2UgYWxsIDMgbWV0aG9kcyBhZ3JlZSBvbiB0aGUgdG9wIDMgbW9kZWxzLCBidXQgdGhlbiB0aGUgcmVzdWx0cyBmcm9tIHRoZSBsZWF2ZS1vbmUtb3V0IGNyb3NzLXZhbGlkYXRpb24gc3RhcnQgdG8gZGl2ZXJnZSBmcm9tIHRoZSBpbi1zYW1wbGUgcmVzdWx0cy4KCiMjIE5vbi1leGhhdXN0aXZlIGNyb3NzLXZhbGlkYXRpb24KCkxldCdzIG5vdyB0cnkgYSBub24tZXhoYXVzdGl2ZSBtZXRob2Qgd2hlcmUgd2UgZG9uJ3QgdXNlIGV2ZXJ5IGNvbWJpbmF0aW9uIG9mIHRoZSAkbiAtIHEkIHRyYWluaW5nIGRhdGEuCgojIyMgJGskLWZvbGQKCkhlcmUgd2UnbGwgdXNlICRrJC1mb2xkIGNyb3NzLXZhbGlkYXRpb24gd2hlcmUgdGhlIGRhdGEgYXJlIHJhbmRvbWx5IHBhcnRpdGlvbmVkIGludG8gJGskIGVxdWFsIHNpemVkIGdyb3Vwcy4gV2UnbGwgcmV0YWluIG9uZSBvZiB0aGUgJGskIHN1Yi1zYW1wbGVzIGZvciB2YWxpZGF0aW9uIHdoaWxlIHRoZSByZW1haW5pbmcgJGsg4oiSIDEkIGdyb3VwcyBhcmUgdXNlZCBmb3IgZml0dGluZy4gVGhpcyBwcm9jZXNzIGlzIHRoZW4gcmVwZWF0ZWQgJGskIHRpbWVzLCB3aXRoIGVhY2ggb2YgdGhlICRrJCBzdWItc2FtcGxlcyB1c2VkIGV4YWN0bHkgb25jZSBmb3IgdmFsaWRhdGlvbi4KCldlIGhhdmUgYHIgbm5gIG9ic2VydmF0aW9ucyBpbiBvdXIgZGF0YXNldCwgc28gbGV0J3MgdXNlIDUgZ3JvdXBzIG9mIGByIG5uLzVgIG9ic2VydmF0aW9ucyBlYWNoLiBUaGUgcmVzdCBvZiB0aGUgYW5hbHlzaXMgd2lsbCBwcm9jZWVkIGFzIGFib3ZlIGZvciB0aGUgbGVhdmUtb25lLW91dCBtZXRob2QuCgpgYGB7ciBrZm9sZF9oYW5kfQojIyBudW1iZXIgb2YgZ3JvdXBzCmtrIDwtIDUKIyMgZ3JvcCBzaXplCmdzIDwtIG5uIC8ga2sKIyMgZW1wdHkgdmVjdG9yIGZvciBwcmVkaWN0aW9ucwprZl9yZXMgPC0gcmVwKE5BLCBubikKIyMgZW1wdHkgdmVjdG9yIGZvciBNU1BFCm1zcGVfayA8LSByZXAoTkEsIG5fbW9kcykKCiMjIGxvb3Agb3ZlciBhbGwgcG9zc2libGUgbW9kZWwgY29tYmluYXRpb25zCmZvcihpIGluIDE6bl9tb2RzKSB7CiAgIyMgY3JlYXRlIG1vZGVsIGZvcm11bGEKICBpZihpID09IDEpIHsKICAgIGZtbGEgPC0gIlNwZWNpZXMgfiAxIgogIH0gZWxzZSB7CiAgICBmbWxhIDwtIHBhc3RlKCJTcGVjaWVzIH4iLCBwYXN0ZShjb3ZfbmFtZXNbdXNlX3NldFtpLF1dLCBjb2xsYXBzZSA9ICIgKyAiKSkKICB9CiAgIyMgbG9vcCBvdmVyIGZvbGRzCiAgZm9yKGZvbGQgaW4gMTpraykgewogICAgIyMgZ3JvdXAgaW5kZXgKICAgIGdycCA8LSBzZXEoZ3MpICsgZ3MqKGZvbGQgLSAxKQogICAgIyMgZHJvcCBvbmUgZ3JvdXAgYW5kIGZpdCB0aGUgbW9kZWwKICAgIGZtIDwtIGxtKGFzLmZvcm11bGEoZm1sYSksIGdhbGFbLWdycCxdKQogICAgIyMgcHJlZGljdCB0aGUgbWlzc2luZyB2YWx1ZXMKICAgIGtmX3Jlc1tncnBdIDwtIHByZWRpY3QoZm0sIG5ld2RhdGEgPSBkYXRhLmZyYW1lKGdhbGFbZ3JwLF0pKQogIH0KICAjIyBjYWxjdWxhdGUgTVNQRSBmb3IgdGhlIHByZWRpY3Rpb25zCiAgbXNwZV9rW2ldIDwtIHN1bSgoZ2FsYSRTcGVjaWVzIC0ga2ZfcmVzKV4yKSAvIG5uCn0KYGBgCgpOb3cgd2UgY2FuIGNvbXBhcmUgdGhlc2UgcmVzdWx0cyB0byB0aGUgb3RoZXIgbW9kZWwgc2VsZWN0aW9uIHJlc3VsdHMgZnJvbSBhYm92ZS4KCmBgYHtyIGNvbXBhcmVfYWxsX3Jlc3VsdHN9CmRhdGEuZnJhbWUoQUlDID0gb3JkZXIobW9kX3Jlc1ssIkFJQyJdKSwKICAgICAgICAgICBCSUMgPSBvcmRlcihtb2RfcmVzWywiQklDIl0pLAogICAgICAgICAgIExPTyA9IG9yZGVyKG1zcGVfbCksCiAgICAgICAgICAga2ZvbGQgPSBvcmRlcihtc3BlX2spKQpgYGAKCkhlcmUgd2Ugc2VlIHRoYXQgYWxsIGZvdXIgbWV0aG9kcyBjb21lIHVwIHdpdGggdGhlIHNhbWUgcmFua2luZ3MgZm9yIHRoZSB0b3AgMyBtb2RlbHMsIGFuZCB0aGF0IGJvdGggb2YgdGhlIG91dC1vZi1zYW1wbGUgbWV0aG9kcyBhcmUgbmVhcmx5IGlkZW50aWNhbCwgdG9vLiBJdCdzIGFsc28gaW1wb3J0YW50IHRvIHJlY29nbml6ZSB0aGF0IHdlIGxpa2VseSB3b3VsZCBoYXZlIGZvdW5kIGEgc29tZXdoYXQgZGlmZmVyZW50IHJlc3VsdCBpZiB3ZSBoYWQgY2hvc2VuIGEgZGlmZmVyZXQgbnVtYmVyIG9mIGdyb3VwcyBmb3IgdGhlICRrJC1mb2xkIGNyb3NzLXZhbGlkYXRpb24uCgoKIyBNdWx0aS1tb2RlbCBpbmZlcmVuY2UKCldlIHNhdyBpbiBsZWN0dXJlIHRoYXQgd2UgY2FuIHVzZSB0aGUgQWthaWtlIHdlaWdodHMgdG8gYXZlcmFnZSBwYXJhbWV0ZXJzIGZyb20gZGlmZmVyZW50IG1vZGVscyBhcyBhIG1lYW5zIG9mIGFkZHJlc3NpbmcgdW5jZXJ0YWludHkgaW4gb3VyIG1vZGVsIHN0cnVjdHVyZXMuIFJlY2FsbCB0aGF0IGZvciBhIGdpdmVuIHBhcmFtZXRlciAkXHRoZXRhJCwgaXQncyBtb2RlbCBhdmVyYWdlZCBlc3RpbWF0ZSBpcwoKJCQKXGJhcntcaGF0e1x0aGV0YX19ID0gXHN1bV97aSA9IDF9XlMgd19pIFxoYXR7XHRoZXRhfV9pCiQkCgp3aGVyZSAkUyQgaXMgdGhlIHRvdGFsIG51bWJlciBvZiBtb2RlbHMgaW4gdGhlIHNldC4gVXN1YWxseSBhIGdpdmVuIHBhcmFtZXRlciAkXHRoZXRhJCBkb2VzIG5vdCBhcHBlYXIgaW4gYWxsIG1vZGVscywgc28gd2UgY2FuIHVzZSBhbiBpbmRpY2F0b3IgZnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgYXZlcmFnZSBlc3RpbWF0ZQoKJCQKXGJhcntcaGF0e1x0aGV0YX19ID0gXGZyYWN7XHN1bV97aSA9IDF9XlMgSShmX2kpIHdfaSBcaGF0e1x0aGV0YX1faX17XHN1bV97aSA9IDF9XlMgSShmX2kpIHdfaX0gXFwKfiBcXApJKGZfaSkgPSAKXGxlZnRcewpcYmVnaW57YXJyYXl9IHtsbH0KMSAmIFx0ZXh0e2lmfSB+IFx0aGV0YSB+XHRleHR7aXMgaW59IH4gZl9pIFxcCjAgJiBcdGV4dHtvdGhlcndpc2V9ClxlbmR7YXJyYXl9ClxyaWdodC4KJCQKCkhlcmUgd2Ugc2F3IHRoYXQgMiBvciAzIG9mIG91ciBtb2RlbHMgaGFkIHZlcnkgc2ltaWxhciBzdXBwb3J0IGZyb20gdGhlIGRhdGEsIHNvIGl0IG1pZ2h0IGJlIHdvcnRoIGF2ZXJhZ2luZyBvdXIgY29lZmZpY2llbnRzIGFjcm9zcyBhbGwgb2YgdGhlIG1vZGVscy4gSW4gdGhpcyBjYXNlLCBob3dldmVyLCB0aGUgdG9wIDMgbW9kZWxzIGNvbnRhaW4gYHIgcm91bmQoc3VtKHJldihzb3J0KHd0cykpWzE6M10pLCAzKSoxMDBgJSBvZiB0aGUgdG90YWwgd2VpZ2h0cywgc28gdGhlIHJlbWFpbmluZyBtb2RlbHMgd2lsbCBoYXZlIHZlcnkgbGl0dGxlIGluZmx1ZW5jZSBvbiB0aGUgcmVzdWx0cy4gKEFsc28sIGl0IHdvdWxkIGhhdmUgYmVlbiBtb3JlIGVmZmljaWVudCB0byBkbyBhbGwgb2YgdGhpcyB0aGUgZmlyc3QgdGltZSB3ZSBmaXQgb3VyIG1vZGVscywgYnV0IHRoYXQncyBva2F5LikKCmBgYHtyIG1vZF9hdmd9CiMjIGVtcHR5IG1hdHJpeCBmb3Igc3RvcmluZyBjb2VmZmljaWVudHMKIyMgd2UnbGwgZmlsbCBpdCB3aXRoIDAncyBhbmQgcmVwbGFjZSB0aGVtIHdpdGggdGhlIHBhcmFtIGVzdGltYXRlcwptb2RfY29lZiA8LSBtYXRyaXgoMCwgbl9tb2RzLCAxICsgbmNvbChkZikpCmNvbG5hbWVzKG1vZF9jb2VmKSA8LSBjKCJJbnRlcmNlcHQiLCBjb2xuYW1lcyhkZikpCgojIyBmaXQgbW9kZWxzICYgc3RvcmUgQUlDICYgQklDCmZvcihpIGluIDE6bl9tb2RzKSB7CiAgaWYoaSA9PSAxKSB7CiAgICBmbWxhIDwtICJTcGVjaWVzIH4gMSIKICB9IGVsc2UgewogICAgZm1sYSA8LSBwYXN0ZSgiU3BlY2llcyB+IiwgcGFzdGUoY292X25hbWVzW3VzZV9zZXRbaSxdXSwgY29sbGFwc2UgPSAiICsgIikpCiAgfQogIG1vZF9maXQgPC0gbG0oYXMuZm9ybXVsYShmbWxhKSwgZGF0YSA9IGdhbGEpCiAgbW9kX2NvZWZbaSwgYyhUUlVFLCB1c2Vfc2V0W2ksXSldIDwtIGNvZWYobW9kX2ZpdCkKfQoKIyMgY2FsY3VsYXRlIHdlaWdodGVkIGNvZWZzCnd0ZF9jb2VmIDwtIG1vZF9jb2VmICogd3RzCihhdmdfY29lZiA8LSBjb2xTdW1zKHd0ZF9jb2VmKSkKYGBgCgpJdCdzIGltcG9ydGFudCB0byBub3RlIGhlcmUgdGhhdCB1c2luZyB0aGlzIG1vZGVsIGluIHByYWN0aWNlIGlzbid0IGEgZ29vZCBpZGVhIGJlY2F1c2Ugb2YgdGhlIGhpZ2ggY29ycmVsYXRpb24gYmV0d2VlbiBgQXJlYWAgYW5kIGBFbGV2YXRpb25gIGFuZCBiZXR3ZWVuIGBOZWFyZXN0YCBhbmQgYFNjcnV6YC4gQW4gYWx0ZXJuYXRpdmUgYXBwcm9hY2ggd291bGQgYmUgdG8gdXNlIGVhY2ggb2YgdGhlIGByIG5fbW9kc2AgbW9kZWxzIHRvIG1ha2UgYSBwcmVkaWN0aW9uIGZvciBhIGdpdmVuICRcbWF0aGJme1h9JCBhbmQgdGhlbiB3ZWlnaHQgdGhvc2UgcHJlZGljdGlvbnMgdG8gY29tZSB1cCB3aXRoIGEgbW9kZWwtYXZlcmFnZWQgcHJlZGljdGlvbnMuCgo=