Likelihood Ratio Test (and AIC and BIC)
Let’s start off with fitting a fairly maximimal model for the effect of duration and voicing on F1 of ah
.
iy_ah_tomod <- iy_ah %>%
filter(context == "internal",
fol_seg %in% c("B", "CH", "D",
"DH", "F", "G",
"JH", "K", "P",
"S", "SH", "T",
"TH", "V", "Z",
"ZH"))%>%
mutate(log2dur = log2(dur),
log2dur_c = log2dur - median(log2dur),
is_voiceless = fol_seg %in% c("CH", "F", "K",
"P", "S", "SH",
"T", "TH"))
ah_tomod <- iy_ah_tomod %>% filter(plt_vclass == "ah")
ah_model_full <- lmer(F1_n ~ log2dur_c * is_voiceless +
(1 + log2dur_c + is_voiceless | idstring) +
(1 + log2dur_c | word),
data = ah_tomod)
summary(ah_model_full)
Linear mixed model fit by REML ['lmerMod']
Formula: F1_n ~ log2dur_c * is_voiceless + (1 + log2dur_c + is_voiceless |
idstring) + (1 + log2dur_c | word)
Data: ah_tomod
REML criterion at convergence: 26380.2
Scaled residuals:
Min 1Q Median 3Q Max
-6.1115 -0.5581 0.0060 0.5826 6.6176
Random effects:
Groups Name Variance Std.Dev. Corr
word (Intercept) 0.07500 0.2739
log2dur_c 0.01624 0.1274 -0.77
idstring (Intercept) 0.04864 0.2205
log2dur_c 0.01732 0.1316 0.26
is_voicelessTRUE 0.02188 0.1479 -0.63 0.15
Residual 0.25961 0.5095
Number of obs: 16804, groups: word, 524; idstring, 292
Fixed effects:
Estimate Std. Error t value
(Intercept) 1.10851 0.04339 25.547
log2dur_c 0.26614 0.03547 7.503
is_voicelessTRUE 0.09927 0.04759 2.086
log2dur_c:is_voicelessTRUE 0.01606 0.03950 0.407
Correlation of Fixed Effects:
(Intr) lg2dr_ i_TRUE
log2dur_c -0.635
is_vclsTRUE -0.862 0.600
lg2d_:_TRUE 0.583 -0.844 -0.656
Looking just at the t-values, it looks like we have a reliable effect of duration, and voicing on F1. The longer the vowel, the higher F1, and pre-voiceless ah
has a slightly higher F1. That’s a weaker effect than doubling the duration of the vowel.
However, one effect of interest is the interaction between duration and voicelessness. Looking at the coefficient, it looks kind of like pre-voiceless ah
is more sensitive to duration changes than pre-voiced ah
, but the effect size is small, and so is our certainty (t < 2).
One way to see whether it is worth including the interaction to improve the model fit is to fit another model where everything else is the same except for the effect of interest (in this case, the interaction between duration and voicelessness).
ah_model_2 <- lmer(F1_n ~ log2dur_c + is_voiceless +
(1 + log2dur_c + is_voiceless | idstring) +
(1 + log2dur_c | word),
data = ah_tomod)
Then, you compare these two models using the function anova()
. Now, when you use anova()
on to LMEs, it is not doing an ANOVA. The function anova()
has just become semantically bleached to mean “compare two models”.
~5 Minute Break
If you haven’t done it already, fit the two models and compare them with anova()
.
ah_model_full <- lmer(F1_n ~ log2dur_c * is_voiceless +
(1 + log2dur_c + is_voiceless | idstring) +
(1 + log2dur_c | word),
data = ah_tomod)
ah_model_2 <- lmer(F1_n ~ log2dur_c + is_voiceless +
(1 + log2dur_c + is_voiceless | idstring) +
(1 + log2dur_c | word),
data = ah_tomod)
anova(ah_model_full, ah_model_2)
There’s a lot to look at here:
The AIC and the BIC are metrics of model fit. They’re trying to quantify this balance between how well the models fit the data vs how complex they are. I’ve mentioned the the tradeoff between improving the model fit and trying to avoid overfitting, and these Information Criterions are one way of trying to quantify it. I have the formulas for the AIC and the BIC here, not because the actual math matters for us, but in order to do a contrastive analysis:
\[
AIC = 2k - 2\ln(\hat{L})
\]
\[
BIC = \ln(n)k - 2\ln(\hat{L})
\]
This is a classic example of having two competing metrics that are basically the same, but people have their preferences so we get both.
When you’re comparing models (even multiple models) using AIC and BIC, the models with the smallest AIC and BIC are the preferred ones. There isn’t an established difference in AIC and BIC which is considered a “significant” difference.
This block of numbers is dedicated to the Likelihood Ratio Test (or LRT). It is a significance test for whether the more complex model significantly improves model fit. Some notes:
- LRTs should only be carried out comparing “nested” models. That is, one model should have all the same parameters as the other, plus some.
- LRTs should ideally be carried out on minimally contrasting models.
- These are evaluating a “stepping up” of the model complexity.
I would recommend using LRTs to probe the reliablity of very specific predictors and their interactions. In this example, if we were interested more broadly in the effct of voicing, we could try fitting one more model that removes the voicing fixed effect altogheter (not touching the random effects though!).
ah_model_3 <- lmer(F1_n ~ log2dur_c +
(1 + log2dur_c + is_voiceless | idstring) +
(1 + log2dur_c | word),
data = ah_tomod)
anova(ah_model_full, ah_model_2, ah_model_3)
refitting model(s) with ML (instead of REML)
Data: ah_tomod
Models:
ah_model_3: F1_n ~ log2dur_c + (1 + log2dur_c + is_voiceless | idstring) +
ah_model_3: (1 + log2dur_c | word)
ah_model_2: F1_n ~ log2dur_c + is_voiceless + (1 + log2dur_c + is_voiceless |
ah_model_2: idstring) + (1 + log2dur_c | word)
ah_model_full: F1_n ~ log2dur_c * is_voiceless + (1 + log2dur_c + is_voiceless |
ah_model_full: idstring) + (1 + log2dur_c | word)
Df AIC BIC logLik deviance Chisq Chi Df Pr(>Chisq)
ah_model_3 12 26392 26485 -13184 26368
ah_model_2 13 26385 26485 -13179 26359 9.2778 1 0.002319 **
ah_model_full 14 26387 26495 -13179 26359 0.1783 1 0.672863
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
While the BIC does not distinguish between these three models, the model with just a main effect of voicing is preferred by the AIC. This is also the only model to significantly improve model fit over a simpler model according to the Likelihood Ratio Test. It would seem then that the following segment’s voicing has a reliable effect on F1, but it does not reliably interact with the duration of the vowel/vowel reduction.
The Bootstrap
If you want to get something p-value-like or confidence interval-like for the parameters in a mixed effects model, the people who have developed these techniques recommend bootstrapping. Bootstrapping is a very general purpose technique that sort of looks like it shouldn’t work.
Bootstrapping in Principle
Bootstrapping is a very general method for estimating our uncertainty about any given paremeter estimate. For example, let’s say that we had 8 randomly sampled heights of linguists at the Intitute, and we were interested in how noisy an estimate this is of the average height of linguists.
heights = c(a = 69, b = 75, c = 62, d = 62, e = 73, f = 71, g = 61, h = 64)
mean(heights)
[1] 67.125
One method statisticians have developed is the “bootstrap” estimate of uncertainty. The way that works is you first resample your sample, with replacement.
new_heights = sample(heights, replace = T)
new_heights
e f e c e c g b
73 71 73 62 73 62 61 75
Some people’s heights have shown up more than once, and some none at all. Then, you take the mean
mean(new_heights)
[1] 68.75
And now, you do this hundreds and hundreds of times.
replicates <- (1:100000) %>%
map(~sample(heights, replace = T))%>%
map(mean)%>%
simplify()
data_frame(replicates = replicates)%>%
ggplot(aes(replicates))+
stat_density(color = "black", alpha = 0.6)+
geom_vline(xintercept = mean(heights))+
theme_minimal()
The statisticians have set unto us that this is a sensible way to estimate uncertainty about the mean of our sample. As the bootstrap is to the sample, so the sample is to the population. We can use some very simplistic methods to get a confidence interval out of the boostraps, say by taking the center 95% interval from the sample:
quantile(replicates, prob = c(0.025, 0.975))
2.5% 97.5%
63.625 70.750
Basically, this means we’ve got a pretty lousy sample for trying estimate the average height of linguists. This 95% CI ranges from just a bit taller than the shortest people in the sample and just a bit shorter than the tallest, meaning that as far as we know with these 8 people, any one of them could be the average height.
sort(heights)
g c d h a f e b
61 62 62 64 69 71 73 75
You can use bootstrap replication for any property of the sample, incidentally. For example, we could try to estimate the standard deviation of linguists’ heights using this method.
sd_replicates <- (1:100000) %>%
map(~sample(heights, replace = T))%>%
map(sd)%>%
simplify()
data_frame(sd_replicates = sd_replicates)%>%
ggplot(aes(sd_replicates))+
stat_density(color = "black", alpha = 0.6)+
geom_vline(xintercept = sd(heights))+
theme_minimal()
Here’s a better example. Let’s look at the ratio of iy
duration to ah
duration again.
vowel_ratio <- iy_ah %>%
group_by(idstring, plt_vclass)%>%
summarise(dur = median(dur))%>%
spread(plt_vclass, dur)%>%
mutate(ratio = ah/iy)
vowel_ratio %>%
ggplot(aes(ratio))+
stat_density(alpha = 0.6, color = 'black')+
theme_minimal()
The median duration
ratio_bootstrap <- (1:100000)%>%
map(~sample(vowel_ratio$ratio, replace = T))%>%
map(median)%>%
simplify()
data_frame(ratio_bootstrap = ratio_bootstrap) %>%
ggplot(aes(ratio_bootstrap))+
stat_density(alpha = 0.6, color = "black")+
xlim(0.8, 2)+
theme_minimal()
Bootstrapping for Regression
NOTE!: I give a bunch of code to do bootstrap replication by hand here, but there is a pre-built function to use called bootMer()
.
Bootstrapping to get confidence intervals for regression paremeters works in a similar way, but implemented differently. For clarity’s sake, we’ll just look at the data from one speaker.
sp1 <- ah_tomod %>%
filter(idstring == idstring[1]) %>%
select(F1_n, log2dur_c)
ggplot(sp1, aes(log2dur_c, F1_n))+
geom_point()+
stat_smooth(method = 'lm')
sp1_mod <- lm(F1_n ~ log2dur_c, data = sp1)
sp1_mod
Call:
lm(formula = F1_n ~ log2dur_c, data = sp1)
Coefficients:
(Intercept) log2dur_c
1.3137 0.5726
With this model, every data point has three values associated with it:
- Its x value (in this case, its duration)
- Its fitted y value (in this case, its predicted F1)
- Its residual error (the difference between its observed F1 and and the predicted F1).
In order to get a bootstrap estimate of the intercept and slope here, you actually sample the residuals with replacement, add them to each point’s fitted value, and refit the model.
sp1$resid <- resid(sp1_mod)
sp1$fitted <- fitted(sp1_mod)
head(sp1)
As a reminder, here’s a plot of the observed duration values against the fitted F1 values.
ggplot(sp1, aes(log2dur_c, fitted))+
geom_point()
To get back the original data, you need to add each data point’s residual error to the fitted value.
ggplot(sp1, aes(log2dur_c, fitted + resid))+
geom_point()
In bootstrapping, you actually create a new set of residuals by sampling the existing residuals with replacement. Then you add these to the fitted values to get a new bootstrap replicate.
~2 minute break
Run this code in a chunk a handful of times.
ymax <- max(sp1$fitted) + max(sp1$resid)
ymin <- min(sp1$fitted) + min(sp1$resid)
sp1 %>%
mutate(resid_samp = sample(resid, replace = T))%>%
ggplot(aes(log2dur_c, fitted + resid_samp))+
geom_point()+
ylim(ymin, ymax)+
stat_smooth(method = 'lm')+
theme_minimal()
Now, we just need to collect up all of these re-fittings, and plot them.
lm_boot <- (1:1000)%>%
map(~(sp1 %>% mutate(resid_samp = sample(resid, replace = T),
log2dur_c = resid_samp + fitted)))%>%
map(~(lm(F1_n ~ log2dur_c, data = .x)))%>%
map(tidy)%>%
bind_rows(.id = "id")
lm_boot %>%
ggplot(aes(estimate))+
stat_density(alpha = 0.6, color = "black")+
facet_wrap(~term, scales = "free")+
geom_vline(xintercept = 0)+
theme_bw()
Bootstrapping LMEs
The specific way which boostrapping works for lmer
models is different than for vanilla linear models (dealing with the random effects structure appropriately is complex), but fortunately we don’t need to write code to do that for us. The function bootMer()
will do it for us.
bootMer(model, FUN = fixef, nsim, …)
model
- the model you want to do bootstrapping on (can be a linear or other generalized linear mixed effects model).
FUN
- The function you want to apply to each re-fit model. This will almost always be just
fixef
nsim
- The number of bootstrap simulations you want to do
...
- Additional optional arguments (e.g. what different kinds of bootstrapping you want to do, whether or not to use parallel processing).
~5 minute break
Run this code to do 5 bootstrap replications
ah_bootstraps <- bootMer(ah_model_full,
FUN = fixef,
nsim = 5,
verbose = T)
The somewhat annoying thing about bootstrapping is that it takes just as long to fit each replication as it does to fit the original model. That’s why I baked one at home to share in the lsa2017
.
ah_model_boot
unknown type of "boot" object
Call:
bootMer(x = ah_model_full, FUN = fixef, nsim = 1000, verbose = T)
Bootstrap Statistics :
original bias std. error
t1* 1.14191405 0.0002722577 0.03941867
t2* 0.26614247 0.0007798575 0.03584854
t3* 0.10128834 0.0003775092 0.04348148
t4* 0.01605835 -0.0016354358 0.03933166
Each t*
item corresponds to a fixed effect coefficient. They show up in the summary in the same order they did in the model summary:
fixef(ah_model_full)
(Intercept) log2dur_c is_voicelessTRUE
1.10850500 0.26614232 0.09927247
log2dur_c:is_voicelessTRUE
0.01605849
In the bootstrap summary, the original
column corresponds to the original coefficient estimate from the model.
To get a confidence interval for these parameters, we need to use the boot.ci()
function.
boot.ci(bootstrap, index, type = perc)
bootstrap
index
- The index of the coefficient you want a confidence interval for
type
- Unfortunately, there are many different ways of estimating a confidence interval from bootstrapped replicates, and not all of them work for a bootstrapped lme. I recommend
type = "perc"
So to get the bootstrap confidence interval for the intercept, we’d give index=1
.
boot.ci(ah_model_boot, index = 1, type = "perc")
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 1000 bootstrap replicates
CALL :
boot.ci(boot.out = ah_model_boot, type = "perc", index = 1)
Intervals :
Level Percentile
95% ( 1.068, 1.217 )
Calculations and Intervals on Original Scale
This confidence interval excludes 0, so we could call this a reliable effect.
To get the confidence interval for the interaction between voicing and duration, you give it index = 4
boot.ci(ah_model_boot, index = 4, type = "perc")
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 1000 bootstrap replicates
CALL :
boot.ci(boot.out = ah_model_boot, type = "perc", index = 4)
Intervals :
Level Percentile
95% (-0.0642, 0.0896 )
Calculations and Intervals on Original Scale
In this case, the confidence interval contains 0, so this does not look like a reliable effect.
So, if you were writing a paper on the effect of vowel duration and voicing on the F1 of ah
, you could argue based on convergent evidence from the model comparisons, likelihood ratio test, and the bootstrap confidence intervals that there is a consistent effect of following voiceless consonants raising F1 (lowering the vowel), but this does not interact with duration reliably.
ah_model_boot$t %>%
data.frame()%>%
gather()%>%
ggplot(aes(value))+
stat_density(alpha = 0.6, color = "black")+
geom_vline(xintercept = 0, linetype = 2)+
facet_wrap(~key, scales = "free")+
theme_bw()
LS0tCnRpdGxlOiAiTW9kZWwgQ29tcGFyaXNvbiBhbmQgQm9vdHN0cmFwcGluZyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogbm9uZQogICAgY3NzOiAuLi9jdXN0b20uY3NzCiAgICB0aGVtZTogZmxhdGx5CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogMwotLS0KCgojIEFnZW5kYQoKLSBNb2RlbCBjb21wYXJpc29uIHZpYSBBSUMsIEJJQyBhbmQgTGlrZWxpaG9vZCBSYXRpbyBUZXN0cwotIEJvb3RzdHJhcCByZXBsaWNhdGlvbgoKPGRpdiBjbGFzcyA9ICJicmVhayBib3giPgo8c3BhbiBjbGFzcyA9ICJiaWctbGFiZWwiPlNldHVwPC9zcGFuPgoKVG9kYXksIHdlJ3JlIGdvaW5nIHRvIG5lZWQgdG8gaW5zdGFsbCBhbmQgdXNlIGEgbmV3IHBhY2thZ2UgY2FsbGVkICJib290IiwgYW5kIHJlLWluc3RhbGwgdGhlIGNvdXJzZSBwYWNrYWdlIGJlY2F1c2UgSSd2ZSBhZGRlZCBhIG5ldyBkYXRhIG9iamVjdCB0byBpdCBmb3IgdG9kYXkuCgpgYGB7cn0KIycgSW5zdGFsbAppbnN0YWxsLnBhY2thZ2VzKCJib290IikKbGlicmFyeShkZXZ0b29scykKaW5zdGFsbF9naXRodWIoImpvZnJod2xkL2xzYTIwMTciKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsc2EyMDE3KQpsaWJyYXJ5KGxtZTQpCmxpYnJhcnkoYm9vdCkKbGlicmFyeShicm9vbSkKYGBgCjwvZGl2PgoKCiMgSW50cm8KClNvIGZhciwgSSBoYXZlIGJlZW4gY2FnZXkgYWJvdXQgdGhlIHVzZSBvZiBvciBpbnRlcnByZXRhdGlvbiBvZiBwLXZhbHVlcywgb3Igc29tZSBvdGhlciBtZXRob2RzIGZvciBkZWNpZGluZyB3aGVuIHRvIGluY2x1ZGUvZXhjbHVkZSBwcmVkaWN0b3JzIGZyb20geW91ciBtb2RlbCwgaW5zdGVhZCB0cnlpbmcgdG8gZW1waGFzaXplIHRoZSBpbXBvcnRhbmNlIG9uIGZvY3VzaW5nIG9uIGVmZmVjdCBzaXplICYgZGlyZWN0aW9uLCBhbmQgdHJ5aW5nIHRvIGltcHJlc3Mgb24geW91IHRoZSBpbXBvcnRhbmNlIG9mIGhhdmluZyBhIHRoZW9yeSBvZiAqd2hpY2ggZGlyZWN0aW9uKiBhbiBlZmZlY3Qgd2lsbCBnbywgYW5kICphYm91dCBob3cgbGFyZ2UqIGl0IHdpbGwgYmUgYmVmb3JlIHlvdSBmaXQgdGhlIG1vZGVsLgoKSG93ZXZlciwgc29tZXRpbWVzIHlvdSBkbyB3YW50IHRvIGNoZWNrIHdoZXRoZXIgYW4gZWZmZWN0IGluIGEgbW9kZWwgaXMgcmVsaWFibGUsIG9yIGlzIHdvcnRoIGNvbXBsZXhpZnlpbmcgdGhlIG1vZGVsIGluIG9yZGVyIHRvIGltcHJvdmUgdGhlIG1vZGVsIGZpdC4KVGhlIGN1cnJlbnQgW3JlY29tbWVuZGF0aW9ucyBmcm9tIHRoZSBmb2xrcyBkZXZlbG9waW5nIHRoZXNlIExNRSBtZXRob2RzIGFyZSB0byB1c2UgKmxpa2VsaWhvb2QgcmF0aW8gdGVzdHMqIGFuZCAqYm9vdHN0cmFwIGNvbmZpZGVuY2UgaW50ZXJ2YWxzKl0oaHR0cHM6Ly9iYm9sa2VyLmdpdGh1Yi5pby9taXhlZG1vZGVscy1taXNjL2dsbW1GQVEuaHRtbCNtZXRob2RzLWZvci10ZXN0aW5nLXNpbmdsZS1wYXJhbWV0ZXJzKS4KCiMgTGlrZWxpaG9vZCBSYXRpbyBUZXN0IChhbmQgQUlDIGFuZCBCSUMpCgpMZXQncyBzdGFydCBvZmYgd2l0aCBmaXR0aW5nIGEgZmFpcmx5IG1heGltaW1hbCBtb2RlbCBmb3IgdGhlIGVmZmVjdCBvZiBkdXJhdGlvbiBhbmQgdm9pY2luZyBvbiBGMSBvZiBgYWhgLgoKYGBge3J9Cml5X2FoX3RvbW9kIDwtIGl5X2FoICU+JSAKICAgICAgICAgICAgICAgICAgZmlsdGVyKGNvbnRleHQgPT0gImludGVybmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGZvbF9zZWcgJWluJSBjKCJCIiwgIkNIIiwgIkQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRIIiwgIkYiLCAiRyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSkgiLCAiSyIsICJQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTIiwgIlNIIiwgIlQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRIIiwgIlYiLCAiWiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiWkgiKSklPiUKICAgICAgICAgICAgICAgICAgbXV0YXRlKGxvZzJkdXIgPSBsb2cyKGR1ciksCiAgICAgICAgICAgICAgICAgICAgICAgICBsb2cyZHVyX2MgPSBsb2cyZHVyIC0gbWVkaWFuKGxvZzJkdXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgaXNfdm9pY2VsZXNzID0gZm9sX3NlZyAlaW4lIGMoIkNIIiwgIkYiLCAiSyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUCIsICJTIiwgIlNIIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUIiwgIlRIIikpCgphaF90b21vZCA8LSBpeV9haF90b21vZCAlPiUgZmlsdGVyKHBsdF92Y2xhc3MgPT0gImFoIikKYGBgCgoKYGBge3J9CmFoX21vZGVsX2Z1bGwgPC0gbG1lcihGMV9uIH4gbG9nMmR1cl9jICogaXNfdm9pY2VsZXNzICsgCiAgICAgICAgICAgICAgICAgICgxICsgbG9nMmR1cl9jICsgaXNfdm9pY2VsZXNzIHwgaWRzdHJpbmcpICsKICAgICAgICAgICAgICAgICAgKDEgKyBsb2cyZHVyX2MgfCB3b3JkKSwgCiAgICAgICAgICAgICAgICAgZGF0YSA9IGFoX3RvbW9kKQpgYGAKCgpgYGB7cn0Kc3VtbWFyeShhaF9tb2RlbF9mdWxsKQpgYGAKCkxvb2tpbmcganVzdCBhdCB0aGUgdC12YWx1ZXMsIGl0IGxvb2tzIGxpa2Ugd2UgaGF2ZSBhIHJlbGlhYmxlIGVmZmVjdCBvZiBkdXJhdGlvbiwgYW5kIHZvaWNpbmcgb24gRjEuIFRoZSBsb25nZXIgdGhlIHZvd2VsLCB0aGUgaGlnaGVyIEYxLCBhbmQgcHJlLXZvaWNlbGVzcyBgYWhgIGhhcyBhIHNsaWdodGx5IGhpZ2hlciBGMS4gVGhhdCdzIGEgd2Vha2VyIGVmZmVjdCB0aGFuIGRvdWJsaW5nIHRoZSBkdXJhdGlvbiBvZiB0aGUgdm93ZWwuCgpIb3dldmVyLCBvbmUgZWZmZWN0IG9mIGludGVyZXN0IGlzIHRoZSAqaW50ZXJhY3Rpb24qIGJldHdlZW4gZHVyYXRpb24gYW5kIHZvaWNlbGVzc25lc3MuIApMb29raW5nIGF0IHRoZSBjb2VmZmljaWVudCwgaXQgbG9va3Mga2luZCBvZiBsaWtlIHByZS12b2ljZWxlc3MgYGFoYCBpcyBtb3JlIHNlbnNpdGl2ZSB0byBkdXJhdGlvbiBjaGFuZ2VzIHRoYW4gcHJlLXZvaWNlZCBgYWhgLCBidXQgdGhlIGVmZmVjdCBzaXplIGlzIHNtYWxsLCBhbmQgc28gaXMgb3VyIGNlcnRhaW50eSAodCA8IDIpLgoKT25lIHdheSB0byBzZWUgd2hldGhlciBpdCBpcyB3b3J0aCBpbmNsdWRpbmcgdGhlIGludGVyYWN0aW9uIHRvIGltcHJvdmUgdGhlIG1vZGVsIGZpdCBpcyB0byBmaXQgYW5vdGhlciBtb2RlbCAqKndoZXJlIGV2ZXJ5dGhpbmcgZWxzZSBpcyB0aGUgc2FtZSoqIGV4Y2VwdCBmb3IgdGhlIGVmZmVjdCBvZiBpbnRlcmVzdCAoaW4gdGhpcyBjYXNlLCB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiBkdXJhdGlvbiBhbmQgdm9pY2VsZXNzbmVzcykuCgoKYGBge3J9CmFoX21vZGVsXzIgPC0gbG1lcihGMV9uIH4gbG9nMmR1cl9jICsgaXNfdm9pY2VsZXNzICsgCiAgICAgICAgICAgICAgICAgICgxICsgbG9nMmR1cl9jICsgaXNfdm9pY2VsZXNzIHwgaWRzdHJpbmcpICsKICAgICAgICAgICAgICAgICAgKDEgKyBsb2cyZHVyX2MgfCB3b3JkKSwgCiAgICAgICAgICAgICAgICAgZGF0YSA9IGFoX3RvbW9kKQpgYGAKClRoZW4sIHlvdSBjb21wYXJlIHRoZXNlIHR3byBtb2RlbHMgdXNpbmcgdGhlIGZ1bmN0aW9uIGBhbm92YSgpYC4gTm93LCB3aGVuIHlvdSB1c2UgYGFub3ZhKClgIG9uIHRvIExNRXMsIGl0IGlzICpub3QqIGRvaW5nIGFuIEFOT1ZBLiAKVGhlIGZ1bmN0aW9uIGBhbm92YSgpYCBoYXMganVzdCBiZWNvbWUgc2VtYW50aWNhbGx5IGJsZWFjaGVkIHRvIG1lYW4gImNvbXBhcmUgdHdvIG1vZGVscyIuXltJIGltYWdpbmUgdGhpcyBpcyBzaW1pbGFyIHRvIGhvdyB0aGUgRXh0ZW5kZWQgUHJvamVjdGlvbiBQcmluY2lwbGUgY2FtZSB0byBtZWFuICJIYXZlIHNwZWNpZmllciIsIGFuZCB0aGVyZSBhcmUgc3RpbGwgRVBQIGZlYXR1cmVzIGtpY2tpbmcgYXJvdW5kIGV2ZW4gdGhvdWdoIHRoZSB0aGVvcnkgb2YgcGhyYXNlIHN0cnVjdHVyZSBoYXMgY29tcGxldGVseSBjaGFuZ2VkLl0KCjxkaXYgY2xhc3MgPSAiYnJlYWsgYm94Ij4KPHNwYW4gY2xhc3MgPSAiYmlnLWxhYmVsIj5+NSBNaW51dGUgQnJlYWs8L3NwYW4+CgpJZiB5b3UgaGF2ZW4ndCBkb25lIGl0IGFscmVhZHksIGZpdCB0aGUgdHdvIG1vZGVscyBhbmQgY29tcGFyZSB0aGVtIHdpdGggYGFub3ZhKClgLgoKYGBge3J9CmFoX21vZGVsX2Z1bGwgPC0gbG1lcihGMV9uIH4gbG9nMmR1cl9jICogaXNfdm9pY2VsZXNzICsgCiAgICAgICAgICAgICAgICAgICgxICsgbG9nMmR1cl9jICsgaXNfdm9pY2VsZXNzIHwgaWRzdHJpbmcpICsKICAgICAgICAgICAgICAgICAgKDEgKyBsb2cyZHVyX2MgfCB3b3JkKSwgCiAgICAgICAgICAgICAgICAgZGF0YSA9IGFoX3RvbW9kKQpgYGAKCmBgYHtyfQphaF9tb2RlbF8yIDwtIGxtZXIoRjFfbiB+IGxvZzJkdXJfYyArIGlzX3ZvaWNlbGVzcyArIAogICAgICAgICAgICAgICAgICAoMSArIGxvZzJkdXJfYyArIGlzX3ZvaWNlbGVzcyB8IGlkc3RyaW5nKSArCiAgICAgICAgICAgICAgICAgICgxICsgbG9nMmR1cl9jIHwgd29yZCksIAogICAgICAgICAgICAgICAgIGRhdGEgPSBhaF90b21vZCkKYGBgCgoKYGBge3J9CmFub3ZhKGFoX21vZGVsX2Z1bGwsIGFoX21vZGVsXzIpCmBgYAoKPC9kaXY+CgoKVGhlcmUncyBhIGxvdCB0byBsb29rIGF0IGhlcmU6Cgo8ZGl2IGNsYXNzID0gJ2hhbGYtaW1nJz4KIVtdKGZpZ3VyZXMvYWljX2JpYy5wbmcpCjwvZGl2PgoKVGhlIEFJQyBhbmQgdGhlIEJJQyBhcmUgbWV0cmljcyBvZiBtb2RlbCBmaXQuIFRoZXkncmUgdHJ5aW5nIHRvIHF1YW50aWZ5IHRoaXMgYmFsYW5jZSBiZXR3ZWVuIGhvdyB3ZWxsIHRoZSBtb2RlbHMgZml0IHRoZSBkYXRhIHZzIGhvdyBjb21wbGV4IHRoZXkgYXJlLiAKSSd2ZSBtZW50aW9uZWQgdGhlIHRoZSB0cmFkZW9mZiBiZXR3ZWVuIGltcHJvdmluZyB0aGUgbW9kZWwgZml0IGFuZCB0cnlpbmcgdG8gYXZvaWQgb3ZlcmZpdHRpbmcsIGFuZCB0aGVzZSAqSW5mb3JtYXRpb24gQ3JpdGVyaW9ucyogYXJlIG9uZSB3YXkgb2YgdHJ5aW5nIHRvIHF1YW50aWZ5IGl0LgpJIGhhdmUgdGhlIGZvcm11bGFzIGZvciB0aGUgQUlDIGFuZCB0aGUgQklDIGhlcmUsIG5vdCBiZWNhdXNlIHRoZSBhY3R1YWwgbWF0aCBtYXR0ZXJzIGZvciB1cywgYnV0IGluIG9yZGVyIHRvIGRvIGEgY29udHJhc3RpdmUgYW5hbHlzaXM6CgokJApBSUMgPSAyayAtIDJcbG4oXGhhdHtMfSkKJCQKCiQkCkJJQyA9IFxsbihuKWsgLSAyXGxuKFxoYXR7TH0pCiQkCgpUaGlzIGlzIGEgY2xhc3NpYyBleGFtcGxlIG9mIGhhdmluZyB0d28gY29tcGV0aW5nIG1ldHJpY3MgdGhhdCBhcmUgYmFzaWNhbGx5IHRoZSBzYW1lLCBidXQgcGVvcGxlIGhhdmUgdGhlaXIgcHJlZmVyZW5jZXMgc28gd2UgZ2V0IGJvdGguIAoKV2hlbiB5b3UncmUgY29tcGFyaW5nIG1vZGVscyAoZXZlbiBtdWx0aXBsZSBtb2RlbHMpIHVzaW5nIEFJQyBhbmQgQklDLCB0aGUgbW9kZWxzIHdpdGggdGhlICpzbWFsbGVzdCogQUlDIGFuZCBCSUMgYXJlIHRoZSBwcmVmZXJyZWQgb25lcy4gVGhlcmUgaXNuJ3QgYW4gZXN0YWJsaXNoZWQgZGlmZmVyZW5jZSBpbiBBSUMgYW5kIEJJQyB3aGljaCBpcyBjb25zaWRlcmVkIGEgInNpZ25pZmljYW50IiBkaWZmZXJlbmNlLgoKPGRpdiBjbGFzcyA9ICdoYWxmLWltZyc+CiFbXShmaWd1cmVzL2xydC5wbmcpCjwvZGl2PgoKVGhpcyBibG9jayBvZiBudW1iZXJzIGlzIGRlZGljYXRlZCB0byB0aGUgTGlrZWxpaG9vZCBSYXRpbyBUZXN0IChvciBMUlQpLiBJdCBpcyBhIHNpZ25pZmljYW5jZSB0ZXN0IGZvciB3aGV0aGVyIHRoZSBtb3JlIGNvbXBsZXggbW9kZWwgc2lnbmlmaWNhbnRseSBpbXByb3ZlcyBtb2RlbCBmaXQuIFNvbWUgbm90ZXM6CgotIExSVHMgc2hvdWxkIG9ubHkgYmUgY2FycmllZCBvdXQgY29tcGFyaW5nICJuZXN0ZWQiIG1vZGVscy4gVGhhdCBpcywgb25lIG1vZGVsIHNob3VsZCBoYXZlIGFsbCB0aGUgc2FtZSBwYXJhbWV0ZXJzIGFzIHRoZSBvdGhlciwgcGx1cyBzb21lLgotIExSVHMgc2hvdWxkIGlkZWFsbHkgYmUgY2FycmllZCBvdXQgb24gbWluaW1hbGx5IGNvbnRyYXN0aW5nIG1vZGVscy4KLSBUaGVzZSBhcmUgZXZhbHVhdGluZyBhICJzdGVwcGluZyB1cCIgb2YgdGhlIG1vZGVsIGNvbXBsZXhpdHkuCgpJIHdvdWxkIHJlY29tbWVuZCB1c2luZyBMUlRzIHRvIHByb2JlIHRoZSByZWxpYWJsaXR5IG9mIHZlcnkgc3BlY2lmaWMgcHJlZGljdG9ycyBhbmQgdGhlaXIgaW50ZXJhY3Rpb25zLgpJbiB0aGlzIGV4YW1wbGUsIGlmIHdlIHdlcmUgaW50ZXJlc3RlZCBtb3JlIGJyb2FkbHkgaW4gdGhlIGVmZmN0IG9mIHZvaWNpbmcsIHdlIGNvdWxkIHRyeSBmaXR0aW5nIG9uZSBtb3JlIG1vZGVsIHRoYXQgcmVtb3ZlcyB0aGUgdm9pY2luZyBmaXhlZCBlZmZlY3QgYWx0b2doZXRlciAobm90IHRvdWNoaW5nIHRoZSByYW5kb20gZWZmZWN0cyB0aG91Z2ghKS4KCgpgYGB7cn0KYWhfbW9kZWxfMyA8LSBsbWVyKEYxX24gfiBsb2cyZHVyX2MgKyAKICAgICAgICAgICAgICAgICAgKDEgKyBsb2cyZHVyX2MgKyBpc192b2ljZWxlc3MgfCBpZHN0cmluZykgKwogICAgICAgICAgICAgICAgICAoMSArIGxvZzJkdXJfYyB8IHdvcmQpLCAKICAgICAgICAgICAgICAgICBkYXRhID0gYWhfdG9tb2QpCmBgYAoKCgpgYGB7cn0KYW5vdmEoYWhfbW9kZWxfZnVsbCwgYWhfbW9kZWxfMiwgYWhfbW9kZWxfMykKYGBgCgo+IFdoaWxlIHRoZSBCSUMgZG9lcyBub3QgZGlzdGluZ3Vpc2ggYmV0d2VlbiB0aGVzZSB0aHJlZSBtb2RlbHMsIHRoZSBtb2RlbCB3aXRoIGp1c3QgYSBtYWluIGVmZmVjdCBvZiB2b2ljaW5nIGlzIHByZWZlcnJlZCBieSB0aGUgQUlDLiBUaGlzIGlzIGFsc28gdGhlIG9ubHkgbW9kZWwgdG8gc2lnbmlmaWNhbnRseSBpbXByb3ZlIG1vZGVsIGZpdCBvdmVyIGEgc2ltcGxlciBtb2RlbCBhY2NvcmRpbmcgdG8gdGhlIExpa2VsaWhvb2QgUmF0aW8gVGVzdC4gSXQgd291bGQgc2VlbSB0aGVuIHRoYXQgdGhlIGZvbGxvd2luZyBzZWdtZW50J3Mgdm9pY2luZyBoYXMgYSByZWxpYWJsZSBlZmZlY3Qgb24gRjEsIGJ1dCBpdCBkb2VzIG5vdCByZWxpYWJseSBpbnRlcmFjdCB3aXRoIHRoZSBkdXJhdGlvbiBvZiB0aGUgdm93ZWwvdm93ZWwgcmVkdWN0aW9uLgoKCgoKIyBUaGUgQm9vdHN0cmFwCgpJZiB5b3Ugd2FudCB0byBnZXQgc29tZXRoaW5nIHAtdmFsdWUtbGlrZSBvciBjb25maWRlbmNlIGludGVydmFsLWxpa2UgZm9yIHRoZSBwYXJhbWV0ZXJzIGluIGEgbWl4ZWQgZWZmZWN0cyBtb2RlbCwgdGhlIHBlb3BsZSB3aG8gaGF2ZSBkZXZlbG9wZWQgdGhlc2UgdGVjaG5pcXVlcyByZWNvbW1lbmQgKmJvb3RzdHJhcHBpbmcqLgpCb290c3RyYXBwaW5nIGlzIGEgdmVyeSBnZW5lcmFsIHB1cnBvc2UgdGVjaG5pcXVlIHRoYXQgc29ydCBvZiBsb29rcyBsaWtlIGl0IHNob3VsZG4ndCB3b3JrLiAKCiMjIEJvb3RzdHJhcHBpbmcgaW4gUHJpbmNpcGxlCgoqQm9vdHN0cmFwcGluZyogaXMgYSB2ZXJ5IGdlbmVyYWwgbWV0aG9kIGZvciBlc3RpbWF0aW5nIG91ciB1bmNlcnRhaW50eSBhYm91dCBhbnkgZ2l2ZW4gcGFyZW1ldGVyIGVzdGltYXRlLiBGb3IgZXhhbXBsZSwgbGV0J3Mgc2F5IHRoYXQgd2UgaGFkIDggcmFuZG9tbHleW0luIHRoaXMgY2FzZSwgbm90IGFjdHVhbGx5IHJhbmRvbS5dIHNhbXBsZWQgaGVpZ2h0cyBvZiBsaW5ndWlzdHMgYXQgdGhlIEludGl0dXRlLCBhbmQgd2Ugd2VyZSBpbnRlcmVzdGVkIGluIGhvdyBub2lzeSBhbiBlc3RpbWF0ZSB0aGlzIGlzIG9mIHRoZSBhdmVyYWdlIGhlaWdodCBvZiBsaW5ndWlzdHMuCgpgYGB7cn0KaGVpZ2h0cyA9IGMoYSA9IDY5LCBiID0gNzUsIGMgPSA2MiwgZCA9IDYyLCBlID0gNzMsIGYgPSA3MSwgZyA9IDYxLCBoID0gNjQpCm1lYW4oaGVpZ2h0cykKYGBgCgpPbmUgbWV0aG9kIHN0YXRpc3RpY2lhbnMgaGF2ZSBkZXZlbG9wZWQgaXMgdGhlICJib290c3RyYXAiIGVzdGltYXRlIG9mIHVuY2VydGFpbnR5LiBUaGUgd2F5IHRoYXQgd29ya3MgaXMgeW91IGZpcnN0IHJlc2FtcGxlIHlvdXIgc2FtcGxlLCB3aXRoIHJlcGxhY2VtZW50LgoKYGBge3J9Cm5ld19oZWlnaHRzID0gc2FtcGxlKGhlaWdodHMsIHJlcGxhY2UgPSBUKQpuZXdfaGVpZ2h0cwpgYGAKClNvbWUgcGVvcGxlJ3MgaGVpZ2h0cyBoYXZlIHNob3duIHVwIG1vcmUgdGhhbiBvbmNlLCBhbmQgc29tZSBub25lIGF0IGFsbC4KVGhlbiwgeW91IHRha2UgdGhlIG1lYW4KCmBgYHtyfQptZWFuKG5ld19oZWlnaHRzKQpgYGAKCkFuZCBub3csIHlvdSBkbyB0aGlzIGh1bmRyZWRzIGFuZCBodW5kcmVkcyBvZiB0aW1lcy4KCmBgYHtyfQpyZXBsaWNhdGVzIDwtICgxOjEwMDAwMCkgJT4lCiAgICAgICAgICAgICAgICBtYXAofnNhbXBsZShoZWlnaHRzLCByZXBsYWNlID0gVCkpJT4lCiAgICAgICAgICAgICAgICBtYXAobWVhbiklPiUKICAgICAgICAgICAgICAgIHNpbXBsaWZ5KCkKYGBgCgpgYGB7cn0KZGF0YV9mcmFtZShyZXBsaWNhdGVzID0gcmVwbGljYXRlcyklPiUKICBnZ3Bsb3QoYWVzKHJlcGxpY2F0ZXMpKSsKICAgIHN0YXRfZGVuc2l0eShjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC42KSsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW4oaGVpZ2h0cykpKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAoKVGhlIHN0YXRpc3RpY2lhbnMgaGF2ZSBzZXQgdW50byB1cyB0aGF0IHRoaXMgaXMgYSBzZW5zaWJsZSB3YXkgdG8gZXN0aW1hdGUgdW5jZXJ0YWludHkgYWJvdXQgdGhlIG1lYW4gb2Ygb3VyIHNhbXBsZS4gCkFzIHRoZSBib290c3RyYXAgaXMgdG8gdGhlIHNhbXBsZSwgc28gdGhlIHNhbXBsZSBpcyB0byB0aGUgcG9wdWxhdGlvbi4KV2UgY2FuIHVzZSBzb21lIHZlcnkgc2ltcGxpc3RpYyBtZXRob2RzIHRvIGdldCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb3V0IG9mIHRoZSBib29zdHJhcHMsIHNheSBieSB0YWtpbmcgdGhlIGNlbnRlciA5NSUgaW50ZXJ2YWwgZnJvbSB0aGUgc2FtcGxlOgoKYGBge3J9CnF1YW50aWxlKHJlcGxpY2F0ZXMsIHByb2IgPSBjKDAuMDI1LCAwLjk3NSkpCmBgYAoKQmFzaWNhbGx5LCB0aGlzIG1lYW5zIHdlJ3ZlIGdvdCBhIHByZXR0eSBsb3VzeSBzYW1wbGUgZm9yIHRyeWluZyBlc3RpbWF0ZSB0aGUgKmF2ZXJhZ2UqIGhlaWdodCBvZiBsaW5ndWlzdHMuIFRoaXMgOTUlIENJIHJhbmdlcyBmcm9tIGp1c3QgYSBiaXQgdGFsbGVyIHRoYW4gdGhlIHNob3J0ZXN0IHBlb3BsZSBpbiB0aGUgc2FtcGxlIGFuZCBqdXN0IGEgYml0IHNob3J0ZXIgdGhhbiB0aGUgdGFsbGVzdCwgbWVhbmluZyB0aGF0IGFzIGZhciBhcyB3ZSBrbm93IHdpdGggdGhlc2UgOCBwZW9wbGUsIGFueSBvbmUgb2YgdGhlbSBjb3VsZCBiZSB0aGUgKmF2ZXJhZ2UqIGhlaWdodC4KCmBgYHtyfQpzb3J0KGhlaWdodHMpCmBgYAoKWW91IGNhbiB1c2UgYm9vdHN0cmFwIHJlcGxpY2F0aW9uIGZvciBhbnkgcHJvcGVydHkgb2YgdGhlIHNhbXBsZSwgaW5jaWRlbnRhbGx5LiBGb3IgZXhhbXBsZSwgd2UgY291bGQgdHJ5IHRvIGVzdGltYXRlIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgbGluZ3Vpc3RzJyBoZWlnaHRzIHVzaW5nIHRoaXMgbWV0aG9kLgoKYGBge3J9CnNkX3JlcGxpY2F0ZXMgPC0gKDE6MTAwMDAwKSAlPiUKICAgICAgICAgICAgICAgIG1hcCh+c2FtcGxlKGhlaWdodHMsIHJlcGxhY2UgPSBUKSklPiUKICAgICAgICAgICAgICAgIG1hcChzZCklPiUKICAgICAgICAgICAgICAgIHNpbXBsaWZ5KCkKYGBgCgoKYGBge3J9CmRhdGFfZnJhbWUoc2RfcmVwbGljYXRlcyA9IHNkX3JlcGxpY2F0ZXMpJT4lCiAgZ2dwbG90KGFlcyhzZF9yZXBsaWNhdGVzKSkrCiAgICBzdGF0X2RlbnNpdHkoY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNikrCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBzZChoZWlnaHRzKSkrCiAgICB0aGVtZV9taW5pbWFsKCkKYGBgCgoKSGVyZSdzIGEgYmV0dGVyIGV4YW1wbGUuIExldCdzIGxvb2sgYXQgdGhlIHJhdGlvIG9mIGBpeWAgZHVyYXRpb24gdG8gYGFoYCBkdXJhdGlvbiBhZ2Fpbi4KCmBgYHtyfQp2b3dlbF9yYXRpbyA8LSBpeV9haCAlPiUKICBncm91cF9ieShpZHN0cmluZywgcGx0X3ZjbGFzcyklPiUKICBzdW1tYXJpc2UoZHVyID0gbWVkaWFuKGR1cikpJT4lCiAgc3ByZWFkKHBsdF92Y2xhc3MsIGR1ciklPiUKICBtdXRhdGUocmF0aW8gPSBhaC9peSkKCgp2b3dlbF9yYXRpbyAlPiUKZ2dwbG90KGFlcyhyYXRpbykpKwogICAgc3RhdF9kZW5zaXR5KGFscGhhID0gMC42LCBjb2xvciA9ICdibGFjaycpKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAoKVGhlIG1lZGlhbiBkdXJhdGlvbiAKCgpgYGB7cn0KcmF0aW9fYm9vdHN0cmFwIDwtICgxOjEwMDAwMCklPiUKICAgICAgICAgICAgICAgICAgICAgIG1hcCh+c2FtcGxlKHZvd2VsX3JhdGlvJHJhdGlvLCByZXBsYWNlID0gVCkpJT4lCiAgICAgICAgICAgICAgICAgICAgICBtYXAobWVkaWFuKSU+JQogICAgICAgICAgICAgICAgICAgICAgc2ltcGxpZnkoKQpgYGAKCmBgYHtyfQpkYXRhX2ZyYW1lKHJhdGlvX2Jvb3RzdHJhcCA9IHJhdGlvX2Jvb3RzdHJhcCkgJT4lCiAgZ2dwbG90KGFlcyhyYXRpb19ib290c3RyYXApKSsKICAgIHN0YXRfZGVuc2l0eShhbHBoYSA9IDAuNiwgY29sb3IgPSAiYmxhY2siKSsKICAgIHhsaW0oMC44LCAyKSsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCgoKIyMgQm9vdHN0cmFwcGluZyBmb3IgUmVncmVzc2lvbgoKKipOT1RFISoqOiBJIGdpdmUgYSBidW5jaCBvZiBjb2RlIHRvIGRvIGJvb3RzdHJhcCByZXBsaWNhdGlvbiBieSBoYW5kIGhlcmUsIGJ1dCB0aGVyZSBpcyBhIHByZS1idWlsdCBmdW5jdGlvbiB0byB1c2UgY2FsbGVkIGBib290TWVyKClgLgoKQm9vdHN0cmFwcGluZyB0byBnZXQgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHJlZ3Jlc3Npb24gcGFyZW1ldGVycyB3b3JrcyBpbiBhIHNpbWlsYXIgd2F5LCBidXQgaW1wbGVtZW50ZWQgZGlmZmVyZW50bHkuIApGb3IgY2xhcml0eSdzIHNha2UsIHdlJ2xsIGp1c3QgbG9vayBhdCB0aGUgZGF0YSBmcm9tIG9uZSBzcGVha2VyLgoKCmBgYHtyfQpzcDEgPC0gYWhfdG9tb2QgJT4lIAogICAgICAgICAgZmlsdGVyKGlkc3RyaW5nID09IGlkc3RyaW5nWzFdKSAlPiUKICAgICAgICAgIHNlbGVjdChGMV9uLCBsb2cyZHVyX2MpCmBgYAoKYGBge3J9CmdncGxvdChzcDEsIGFlcyhsb2cyZHVyX2MsIEYxX24pKSsKICAgIGdlb21fcG9pbnQoKSsKICAgIHN0YXRfc21vb3RoKG1ldGhvZCA9ICdsbScpCmBgYAoKCmBgYHtyfQpzcDFfbW9kIDwtIGxtKEYxX24gfiBsb2cyZHVyX2MsIGRhdGEgPSBzcDEpCnNwMV9tb2QKYGBgCgpXaXRoIHRoaXMgbW9kZWwsIGV2ZXJ5IGRhdGEgcG9pbnQgaGFzIHRocmVlIHZhbHVlcyBhc3NvY2lhdGVkIHdpdGggaXQ6CgoxLiBJdHMgeCB2YWx1ZSAoaW4gdGhpcyBjYXNlLCBpdHMgZHVyYXRpb24pCjIuIEl0cyBmaXR0ZWQgeSB2YWx1ZSAoaW4gdGhpcyBjYXNlLCBpdHMgcHJlZGljdGVkIEYxKQozLiBJdHMgcmVzaWR1YWwgZXJyb3IgKHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gaXRzIG9ic2VydmVkIEYxIGFuZCBhbmQgdGhlIHByZWRpY3RlZCBGMSkuCgpJbiBvcmRlciB0byBnZXQgYSBib290c3RyYXAgZXN0aW1hdGUgb2YgdGhlIGludGVyY2VwdCBhbmQgc2xvcGUgaGVyZSwgeW91IGFjdHVhbGx5IHNhbXBsZSB0aGUgcmVzaWR1YWxzIHdpdGggcmVwbGFjZW1lbnQsIGFkZCB0aGVtIHRvIGVhY2ggcG9pbnQncyBmaXR0ZWQgdmFsdWUsIGFuZCByZWZpdCB0aGUgbW9kZWwuCgpgYGB7cn0Kc3AxJHJlc2lkIDwtIHJlc2lkKHNwMV9tb2QpCnNwMSRmaXR0ZWQgPC0gZml0dGVkKHNwMV9tb2QpCmhlYWQoc3AxKQpgYGAKCkFzIGEgcmVtaW5kZXIsIGhlcmUncyBhIHBsb3Qgb2YgdGhlIG9ic2VydmVkIGR1cmF0aW9uIHZhbHVlcyBhZ2FpbnN0IHRoZSBmaXR0ZWQgRjEgdmFsdWVzLgoKYGBge3J9CmdncGxvdChzcDEsIGFlcyhsb2cyZHVyX2MsIGZpdHRlZCkpKwogICAgZ2VvbV9wb2ludCgpCmBgYAoKVG8gZ2V0IGJhY2sgdGhlIG9yaWdpbmFsIGRhdGEsIHlvdSBuZWVkIHRvIGFkZCBlYWNoIGRhdGEgcG9pbnQncyByZXNpZHVhbCBlcnJvciB0byB0aGUgZml0dGVkIHZhbHVlLgoKYGBge3J9CmdncGxvdChzcDEsIGFlcyhsb2cyZHVyX2MsIGZpdHRlZCArIHJlc2lkKSkrCiAgICBnZW9tX3BvaW50KCkKYGBgCgpJbiBib290c3RyYXBwaW5nLCB5b3UgYWN0dWFsbHkgY3JlYXRlIGEgbmV3IHNldCBvZiByZXNpZHVhbHMgYnkgc2FtcGxpbmcgdGhlIGV4aXN0aW5nIHJlc2lkdWFscyB3aXRoIHJlcGxhY2VtZW50LiBUaGVuIHlvdSBhZGQgdGhlc2UgdG8gdGhlIGZpdHRlZCB2YWx1ZXMgdG8gZ2V0IGEgbmV3IGJvb3RzdHJhcCByZXBsaWNhdGUuCgo8ZGl2IGNsYXNzID0gImJveCBicmVhayI+CjxzcGFuIGNsYXNzPSdiaWctbGFiZWwnPn4yIG1pbnV0ZSBicmVhazwvc3Bhbj4KClJ1biB0aGlzIGNvZGUgaW4gYSBjaHVuayBhIGhhbmRmdWwgb2YgdGltZXMuCgpgYGB7cn0KeW1heCA8LSBtYXgoc3AxJGZpdHRlZCkgKyBtYXgoc3AxJHJlc2lkKQp5bWluIDwtIG1pbihzcDEkZml0dGVkKSArIG1pbihzcDEkcmVzaWQpCgoKc3AxICU+JQogIG11dGF0ZShyZXNpZF9zYW1wID0gc2FtcGxlKHJlc2lkLCByZXBsYWNlID0gVCkpJT4lCiAgZ2dwbG90KGFlcyhsb2cyZHVyX2MsIGZpdHRlZCArIHJlc2lkX3NhbXApKSsKICAgIGdlb21fcG9pbnQoKSsKICAgIHlsaW0oeW1pbiwgeW1heCkrCiAgICBzdGF0X3Ntb290aChtZXRob2QgPSAnbG0nKSsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCjwvZGl2PgoKTm93LCB3ZSBqdXN0IG5lZWQgdG8gY29sbGVjdCB1cCBhbGwgb2YgdGhlc2UgcmUtZml0dGluZ3MsIGFuZCBwbG90IHRoZW0uCgoKYGBge3J9CmxtX2Jvb3QgPC0gKDE6MTAwMCklPiUKICAgICAgICAgICAgICAgIG1hcCh+KHNwMSAlPiUgbXV0YXRlKHJlc2lkX3NhbXAgPSBzYW1wbGUocmVzaWQsIHJlcGxhY2UgPSBUKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZzJkdXJfYyA9IHJlc2lkX3NhbXAgKyBmaXR0ZWQpKSklPiUKICAgICAgICAgICAgICAgIG1hcCh+KGxtKEYxX24gfiBsb2cyZHVyX2MsIGRhdGEgPSAueCkpKSU+JQogICAgICAgICAgICAgICAgbWFwKHRpZHkpJT4lCiAgICAgICAgICAgICAgICBiaW5kX3Jvd3MoLmlkID0gImlkIikKYGBgCgpgYGB7cn0KbG1fYm9vdCAlPiUKICBnZ3Bsb3QoYWVzKGVzdGltYXRlKSkrCiAgICBzdGF0X2RlbnNpdHkoYWxwaGEgPSAwLjYsIGNvbG9yID0gImJsYWNrIikrCiAgICBmYWNldF93cmFwKH50ZXJtLCBzY2FsZXMgPSAiZnJlZSIpKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCkrCiAgICB0aGVtZV9idygpCmBgYAoKIyMgQm9vdHN0cmFwcGluZyBMTUVzCgpUaGUgc3BlY2lmaWMgd2F5IHdoaWNoIGJvb3N0cmFwcGluZyB3b3JrcyBmb3IgYGxtZXJgIG1vZGVscyBpcyBkaWZmZXJlbnQgdGhhbiBmb3IgdmFuaWxsYSBsaW5lYXIgbW9kZWxzIChkZWFsaW5nIHdpdGggdGhlIHJhbmRvbSBlZmZlY3RzIHN0cnVjdHVyZSBhcHByb3ByaWF0ZWx5IGlzIGNvbXBsZXgpLCBidXQgZm9ydHVuYXRlbHkgd2UgZG9uJ3QgbmVlZCB0byB3cml0ZSBjb2RlIHRvIGRvIHRoYXQgZm9yIHVzLiAKVGhlIGZ1bmN0aW9uIGBib290TWVyKClgIHdpbGwgZG8gaXQgZm9yIHVzLgoKPGRpdiBjbGFzcyA9ICJpbGx1c3RyYXRlIj4KYm9vdE1lcig8c3BhbiBjbGFzcyA9ICJwb3AiPm1vZGVsPC9zcGFuPiwgPHNwYW4gY2xhc3MgPSAicG9wIj5GVU4gPSBmaXhlZjwvc3Bhbj4sIDxzcGFuIGNsYXNzID0gInBvcCI+bnNpbTwvc3Bhbj4sIC4uLikKPC9kaXY+CgotIGBtb2RlbGAKICAgIC0gdGhlIG1vZGVsIHlvdSB3YW50IHRvIGRvIGJvb3RzdHJhcHBpbmcgb24gKGNhbiBiZSBhIGxpbmVhciBvciBvdGhlciBnZW5lcmFsaXplZCBsaW5lYXIgbWl4ZWQgZWZmZWN0cyBtb2RlbCkuCi0gYEZVTmAKICAgIC0gVGhlIGZ1bmN0aW9uIHlvdSB3YW50IHRvIGFwcGx5IHRvIGVhY2ggcmUtZml0IG1vZGVsLiBUaGlzIHdpbGwgYWxtb3N0IGFsd2F5cyBiZSBqdXN0IGBmaXhlZmAKLSBgbnNpbWAKICAgIC0gVGhlIG51bWJlciBvZiBib290c3RyYXAgc2ltdWxhdGlvbnMgeW91IHdhbnQgdG8gZG8KLSBgLi4uYAogICAgLSBBZGRpdGlvbmFsICBvcHRpb25hbCBhcmd1bWVudHMgKGUuZy4gd2hhdCBkaWZmZXJlbnQga2luZHMgb2YgYm9vdHN0cmFwcGluZyB5b3Ugd2FudCB0byBkbywgd2hldGhlciBvciBub3QgdG8gdXNlIHBhcmFsbGVsIHByb2Nlc3NpbmcpLgogICAKPGRpdiBjbGFzcyA9ICJicmVhayBib3giPgo8c3BhbiBjbGFzcyA9ICJiaWctbGFiZWwiPn41IG1pbnV0ZSBicmVhazwvc3Bhbj4KClJ1biB0aGlzIGNvZGUgdG8gZG8gNSBib290c3RyYXAgcmVwbGljYXRpb25zCgoKYGBge3J9CmFoX2Jvb3RzdHJhcHMgPC0gYm9vdE1lcihhaF9tb2RlbF9mdWxsLCAKICAgICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGZpeGVmLCAKICAgICAgICAgICAgICAgICAgICAgICAgIG5zaW0gPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFQpCmBgYAo8L2Rpdj4KClRoZSBzb21ld2hhdCBhbm5veWluZyB0aGluZyBhYm91dCBib290c3RyYXBwaW5nIGlzIHRoYXQgaXQgdGFrZXMganVzdCBhcyBsb25nIHRvIGZpdCBlYWNoIHJlcGxpY2F0aW9uIGFzIGl0IGRvZXMgdG8gZml0IHRoZSBvcmlnaW5hbCBtb2RlbC4KVGhhdCdzIHdoeSBJIGJha2VkIG9uZSBhdCBob21lIHRvIHNoYXJlIGluIHRoZSBgbHNhMjAxN2AuCgpgYGB7cn0KYWhfbW9kZWxfYm9vdApgYGAKCkVhY2ggYHQqYCBpdGVtIGNvcnJlc3BvbmRzIHRvIGEgZml4ZWQgZWZmZWN0IGNvZWZmaWNpZW50LiBUaGV5IHNob3cgdXAgaW4gdGhlIHN1bW1hcnkgaW4gdGhlIHNhbWUgb3JkZXIgdGhleSBkaWQgaW4gdGhlIG1vZGVsIHN1bW1hcnk6CgpgYGB7cn0KZml4ZWYoYWhfbW9kZWxfZnVsbCkKYGBgCgpJbiB0aGUgYm9vdHN0cmFwIHN1bW1hcnksIHRoZSBgb3JpZ2luYWxgIGNvbHVtbiBjb3JyZXNwb25kcyB0byB0aGUgb3JpZ2luYWwgY29lZmZpY2llbnQgZXN0aW1hdGUgZnJvbSB0aGUgbW9kZWwuCgpUbyBnZXQgYSBjb25maWRlbmNlIGludGVydmFsIGZvciB0aGVzZSBwYXJhbWV0ZXJzLCB3ZSBuZWVkIHRvIHVzZSB0aGUgYGJvb3QuY2koKWAgZnVuY3Rpb24uCgo8ZGl2IGNsYXNzID0gImlsbHVzdHJhdGUiPgpib290LmNpKDxzcGFuIGNsYXNzID0gInBvcCI+Ym9vdHN0cmFwPC9zcGFuPiwgPHNwYW4gY2xhc3MgPSAicG9wIj5pbmRleDwvc3Bhbj4sIDxzcGFuIGNsYXNzID0gInBvcCI+dHlwZSA9IHBlcmM8L3NwYW4+KQo8L2Rpdj4KCi0gYGJvb3RzdHJhcGAKICAgIC0gVGhlIGJvb3RzdHJhcCBvYmplY3QKLSBgaW5kZXhgCiAgICAtIFRoZSBpbmRleCBvZiB0aGUgY29lZmZpY2llbnQgeW91IHdhbnQgYSBjb25maWRlbmNlIGludGVydmFsIGZvcgotIGB0eXBlYAogICAgLSBVbmZvcnR1bmF0ZWx5LCB0aGVyZSBhcmUgbWFueSBkaWZmZXJlbnQgd2F5cyBvZiBlc3RpbWF0aW5nIGEgY29uZmlkZW5jZSBpbnRlcnZhbCBmcm9tIGJvb3RzdHJhcHBlZCByZXBsaWNhdGVzLCBhbmQgbm90IGFsbCBvZiB0aGVtIHdvcmsgZm9yIGEgYm9vdHN0cmFwcGVkIGxtZS4gSSByZWNvbW1lbmQgYHR5cGUgPSAicGVyYyJgCiAgICAKU28gdG8gZ2V0IHRoZSBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIGludGVyY2VwdCwgd2UnZCBnaXZlIGBpbmRleD0xYC4KCgpgYGB7cn0KYm9vdC5jaShhaF9tb2RlbF9ib290LCBpbmRleCA9IDEsIHR5cGUgPSAicGVyYyIpCmBgYAoKVGhpcyBjb25maWRlbmNlIGludGVydmFsIGV4Y2x1ZGVzIDAsIHNvIHdlIGNvdWxkIGNhbGwgdGhpcyBhIHJlbGlhYmxlIGVmZmVjdC4KClRvIGdldCB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIGludGVyYWN0aW9uIGJldHdlZW4gdm9pY2luZyBhbmQgZHVyYXRpb24sIHlvdSBnaXZlIGl0IGBpbmRleCA9IDRgCgoKYGBge3J9CmJvb3QuY2koYWhfbW9kZWxfYm9vdCwgaW5kZXggPSA0LCB0eXBlID0gInBlcmMiKQpgYGAKCkluIHRoaXMgY2FzZSwgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgY29udGFpbnMgMCwgc28gdGhpcyBkb2VzIG5vdCBsb29rIGxpa2UgYSByZWxpYWJsZSBlZmZlY3QuCgpTbywgaWYgeW91IHdlcmUgd3JpdGluZyBhIHBhcGVyIG9uIHRoZSBlZmZlY3Qgb2Ygdm93ZWwgZHVyYXRpb24gYW5kIHZvaWNpbmcgb24gdGhlIEYxIG9mIGBhaGAsIHlvdSBjb3VsZCBhcmd1ZSBiYXNlZCBvbiBjb252ZXJnZW50IGV2aWRlbmNlIGZyb20gdGhlIG1vZGVsIGNvbXBhcmlzb25zLCBsaWtlbGlob29kIHJhdGlvIHRlc3QsIGFuZCB0aGUgYm9vdHN0cmFwIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIHRoYXQgdGhlcmUgaXMgYSBjb25zaXN0ZW50IGVmZmVjdCBvZiBmb2xsb3dpbmcgdm9pY2VsZXNzIGNvbnNvbmFudHMgcmFpc2luZyBGMSAobG93ZXJpbmcgdGhlIHZvd2VsKSwgYnV0IHRoaXMgZG9lcyBub3QgaW50ZXJhY3Qgd2l0aCBkdXJhdGlvbiByZWxpYWJseS4KCgoKYGBge3J9CmFoX21vZGVsX2Jvb3QkdCAlPiUKICBkYXRhLmZyYW1lKCklPiUKICBnYXRoZXIoKSU+JQogIGdncGxvdChhZXModmFsdWUpKSsKICAgIHN0YXRfZGVuc2l0eShhbHBoYSA9IDAuNiwgY29sb3IgPSAiYmxhY2siKSsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gMikrCiAgICBmYWNldF93cmFwKH5rZXksIHNjYWxlcyA9ICJmcmVlIikrCiAgICB0aGVtZV9idygpCmBgYAoKCg==