Agenda

  • Model comparison via AIC, BIC and Likelihood Ratio Tests
  • Bootstrap replication

Setup

Today, we’re going to need to install and use a new package called “boot”, and re-install the course package because I’ve added a new data object to it for today.

#' Install
install.packages("boot")
library(devtools)
install_github("jofrhwld/lsa2017")
library(tidyverse)
library(lsa2017)
library(lme4)
library(boot)
library(broom)

Intro

So far, I have been cagey about the use of or interpretation of p-values, or some other methods for deciding when to include/exclude predictors from your model, instead trying to emphasize the importance on focusing on effect size & direction, and trying to impress on you the importance of having a theory of which direction an effect will go, and about how large it will be before you fit the model.

However, sometimes you do want to check whether an effect in a model is reliable, or is worth complexifying the model in order to improve the model fit. The current recommendations from the folks developing these LME methods are to use likelihood ratio tests and bootstrap confidence intervals.

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”.1

~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 randomly2 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:

  1. Its x value (in this case, its duration)
  2. Its fitted y value (in this case, its predicted F1)
  3. 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
    • The bootstrap object
  • 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()

  1. I imagine this is similar to how the Extended Projection Principle came to mean “Have specifier”, and there are still EPP features kicking around even though the theory of phrase structure has completely changed.

  2. In this case, not actually random.

LS0tCnRpdGxlOiAiTW9kZWwgQ29tcGFyaXNvbiBhbmQgQm9vdHN0cmFwcGluZyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogbm9uZQogICAgY3NzOiAuLi9jdXN0b20uY3NzCiAgICB0aGVtZTogZmxhdGx5CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogMwotLS0KCgojIEFnZW5kYQoKLSBNb2RlbCBjb21wYXJpc29uIHZpYSBBSUMsIEJJQyBhbmQgTGlrZWxpaG9vZCBSYXRpbyBUZXN0cwotIEJvb3RzdHJhcCByZXBsaWNhdGlvbgoKPGRpdiBjbGFzcyA9ICJicmVhayBib3giPgo8c3BhbiBjbGFzcyA9ICJiaWctbGFiZWwiPlNldHVwPC9zcGFuPgoKVG9kYXksIHdlJ3JlIGdvaW5nIHRvIG5lZWQgdG8gaW5zdGFsbCBhbmQgdXNlIGEgbmV3IHBhY2thZ2UgY2FsbGVkICJib290IiwgYW5kIHJlLWluc3RhbGwgdGhlIGNvdXJzZSBwYWNrYWdlIGJlY2F1c2UgSSd2ZSBhZGRlZCBhIG5ldyBkYXRhIG9iamVjdCB0byBpdCBmb3IgdG9kYXkuCgpgYGB7cn0KIycgSW5zdGFsbAppbnN0YWxsLnBhY2thZ2VzKCJib290IikKbGlicmFyeShkZXZ0b29scykKaW5zdGFsbF9naXRodWIoImpvZnJod2xkL2xzYTIwMTciKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsc2EyMDE3KQpsaWJyYXJ5KGxtZTQpCmxpYnJhcnkoYm9vdCkKbGlicmFyeShicm9vbSkKYGBgCjwvZGl2PgoKCiMgSW50cm8KClNvIGZhciwgSSBoYXZlIGJlZW4gY2FnZXkgYWJvdXQgdGhlIHVzZSBvZiBvciBpbnRlcnByZXRhdGlvbiBvZiBwLXZhbHVlcywgb3Igc29tZSBvdGhlciBtZXRob2RzIGZvciBkZWNpZGluZyB3aGVuIHRvIGluY2x1ZGUvZXhjbHVkZSBwcmVkaWN0b3JzIGZyb20geW91ciBtb2RlbCwgaW5zdGVhZCB0cnlpbmcgdG8gZW1waGFzaXplIHRoZSBpbXBvcnRhbmNlIG9uIGZvY3VzaW5nIG9uIGVmZmVjdCBzaXplICYgZGlyZWN0aW9uLCBhbmQgdHJ5aW5nIHRvIGltcHJlc3Mgb24geW91IHRoZSBpbXBvcnRhbmNlIG9mIGhhdmluZyBhIHRoZW9yeSBvZiAqd2hpY2ggZGlyZWN0aW9uKiBhbiBlZmZlY3Qgd2lsbCBnbywgYW5kICphYm91dCBob3cgbGFyZ2UqIGl0IHdpbGwgYmUgYmVmb3JlIHlvdSBmaXQgdGhlIG1vZGVsLgoKSG93ZXZlciwgc29tZXRpbWVzIHlvdSBkbyB3YW50IHRvIGNoZWNrIHdoZXRoZXIgYW4gZWZmZWN0IGluIGEgbW9kZWwgaXMgcmVsaWFibGUsIG9yIGlzIHdvcnRoIGNvbXBsZXhpZnlpbmcgdGhlIG1vZGVsIGluIG9yZGVyIHRvIGltcHJvdmUgdGhlIG1vZGVsIGZpdC4KVGhlIGN1cnJlbnQgW3JlY29tbWVuZGF0aW9ucyBmcm9tIHRoZSBmb2xrcyBkZXZlbG9waW5nIHRoZXNlIExNRSBtZXRob2RzIGFyZSB0byB1c2UgKmxpa2VsaWhvb2QgcmF0aW8gdGVzdHMqIGFuZCAqYm9vdHN0cmFwIGNvbmZpZGVuY2UgaW50ZXJ2YWxzKl0oaHR0cHM6Ly9iYm9sa2VyLmdpdGh1Yi5pby9taXhlZG1vZGVscy1taXNjL2dsbW1GQVEuaHRtbCNtZXRob2RzLWZvci10ZXN0aW5nLXNpbmdsZS1wYXJhbWV0ZXJzKS4KCiMgTGlrZWxpaG9vZCBSYXRpbyBUZXN0IChhbmQgQUlDIGFuZCBCSUMpCgpMZXQncyBzdGFydCBvZmYgd2l0aCBmaXR0aW5nIGEgZmFpcmx5IG1heGltaW1hbCBtb2RlbCBmb3IgdGhlIGVmZmVjdCBvZiBkdXJhdGlvbiBhbmQgdm9pY2luZyBvbiBGMSBvZiBgYWhgLgoKYGBge3J9Cml5X2FoX3RvbW9kIDwtIGl5X2FoICU+JSAKICAgICAgICAgICAgICAgICAgZmlsdGVyKGNvbnRleHQgPT0gImludGVybmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGZvbF9zZWcgJWluJSBjKCJCIiwgIkNIIiwgIkQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRIIiwgIkYiLCAiRyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSkgiLCAiSyIsICJQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTIiwgIlNIIiwgIlQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRIIiwgIlYiLCAiWiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiWkgiKSklPiUKICAgICAgICAgICAgICAgICAgbXV0YXRlKGxvZzJkdXIgPSBsb2cyKGR1ciksCiAgICAgICAgICAgICAgICAgICAgICAgICBsb2cyZHVyX2MgPSBsb2cyZHVyIC0gbWVkaWFuKGxvZzJkdXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgaXNfdm9pY2VsZXNzID0gZm9sX3NlZyAlaW4lIGMoIkNIIiwgIkYiLCAiSyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUCIsICJTIiwgIlNIIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUIiwgIlRIIikpCgphaF90b21vZCA8LSBpeV9haF90b21vZCAlPiUgZmlsdGVyKHBsdF92Y2xhc3MgPT0gImFoIikKYGBgCgoKYGBge3J9CmFoX21vZGVsX2Z1bGwgPC0gbG1lcihGMV9uIH4gbG9nMmR1cl9jICogaXNfdm9pY2VsZXNzICsgCiAgICAgICAgICAgICAgICAgICgxICsgbG9nMmR1cl9jICsgaXNfdm9pY2VsZXNzIHwgaWRzdHJpbmcpICsKICAgICAgICAgICAgICAgICAgKDEgKyBsb2cyZHVyX2MgfCB3b3JkKSwgCiAgICAgICAgICAgICAgICAgZGF0YSA9IGFoX3RvbW9kKQpgYGAKCgpgYGB7cn0Kc3VtbWFyeShhaF9tb2RlbF9mdWxsKQpgYGAKCkxvb2tpbmcganVzdCBhdCB0aGUgdC12YWx1ZXMsIGl0IGxvb2tzIGxpa2Ugd2UgaGF2ZSBhIHJlbGlhYmxlIGVmZmVjdCBvZiBkdXJhdGlvbiwgYW5kIHZvaWNpbmcgb24gRjEuIFRoZSBsb25nZXIgdGhlIHZvd2VsLCB0aGUgaGlnaGVyIEYxLCBhbmQgcHJlLXZvaWNlbGVzcyBgYWhgIGhhcyBhIHNsaWdodGx5IGhpZ2hlciBGMS4gVGhhdCdzIGEgd2Vha2VyIGVmZmVjdCB0aGFuIGRvdWJsaW5nIHRoZSBkdXJhdGlvbiBvZiB0aGUgdm93ZWwuCgpIb3dldmVyLCBvbmUgZWZmZWN0IG9mIGludGVyZXN0IGlzIHRoZSAqaW50ZXJhY3Rpb24qIGJldHdlZW4gZHVyYXRpb24gYW5kIHZvaWNlbGVzc25lc3MuIApMb29raW5nIGF0IHRoZSBjb2VmZmljaWVudCwgaXQgbG9va3Mga2luZCBvZiBsaWtlIHByZS12b2ljZWxlc3MgYGFoYCBpcyBtb3JlIHNlbnNpdGl2ZSB0byBkdXJhdGlvbiBjaGFuZ2VzIHRoYW4gcHJlLXZvaWNlZCBgYWhgLCBidXQgdGhlIGVmZmVjdCBzaXplIGlzIHNtYWxsLCBhbmQgc28gaXMgb3VyIGNlcnRhaW50eSAodCA8IDIpLgoKT25lIHdheSB0byBzZWUgd2hldGhlciBpdCBpcyB3b3J0aCBpbmNsdWRpbmcgdGhlIGludGVyYWN0aW9uIHRvIGltcHJvdmUgdGhlIG1vZGVsIGZpdCBpcyB0byBmaXQgYW5vdGhlciBtb2RlbCAqKndoZXJlIGV2ZXJ5dGhpbmcgZWxzZSBpcyB0aGUgc2FtZSoqIGV4Y2VwdCBmb3IgdGhlIGVmZmVjdCBvZiBpbnRlcmVzdCAoaW4gdGhpcyBjYXNlLCB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiBkdXJhdGlvbiBhbmQgdm9pY2VsZXNzbmVzcykuCgoKYGBge3J9CmFoX21vZGVsXzIgPC0gbG1lcihGMV9uIH4gbG9nMmR1cl9jICsgaXNfdm9pY2VsZXNzICsgCiAgICAgICAgICAgICAgICAgICgxICsgbG9nMmR1cl9jICsgaXNfdm9pY2VsZXNzIHwgaWRzdHJpbmcpICsKICAgICAgICAgICAgICAgICAgKDEgKyBsb2cyZHVyX2MgfCB3b3JkKSwgCiAgICAgICAgICAgICAgICAgZGF0YSA9IGFoX3RvbW9kKQpgYGAKClRoZW4sIHlvdSBjb21wYXJlIHRoZXNlIHR3byBtb2RlbHMgdXNpbmcgdGhlIGZ1bmN0aW9uIGBhbm92YSgpYC4gTm93LCB3aGVuIHlvdSB1c2UgYGFub3ZhKClgIG9uIHRvIExNRXMsIGl0IGlzICpub3QqIGRvaW5nIGFuIEFOT1ZBLiAKVGhlIGZ1bmN0aW9uIGBhbm92YSgpYCBoYXMganVzdCBiZWNvbWUgc2VtYW50aWNhbGx5IGJsZWFjaGVkIHRvIG1lYW4gImNvbXBhcmUgdHdvIG1vZGVscyIuXltJIGltYWdpbmUgdGhpcyBpcyBzaW1pbGFyIHRvIGhvdyB0aGUgRXh0ZW5kZWQgUHJvamVjdGlvbiBQcmluY2lwbGUgY2FtZSB0byBtZWFuICJIYXZlIHNwZWNpZmllciIsIGFuZCB0aGVyZSBhcmUgc3RpbGwgRVBQIGZlYXR1cmVzIGtpY2tpbmcgYXJvdW5kIGV2ZW4gdGhvdWdoIHRoZSB0aGVvcnkgb2YgcGhyYXNlIHN0cnVjdHVyZSBoYXMgY29tcGxldGVseSBjaGFuZ2VkLl0KCjxkaXYgY2xhc3MgPSAiYnJlYWsgYm94Ij4KPHNwYW4gY2xhc3MgPSAiYmlnLWxhYmVsIj5+NSBNaW51dGUgQnJlYWs8L3NwYW4+CgpJZiB5b3UgaGF2ZW4ndCBkb25lIGl0IGFscmVhZHksIGZpdCB0aGUgdHdvIG1vZGVscyBhbmQgY29tcGFyZSB0aGVtIHdpdGggYGFub3ZhKClgLgoKYGBge3J9CmFoX21vZGVsX2Z1bGwgPC0gbG1lcihGMV9uIH4gbG9nMmR1cl9jICogaXNfdm9pY2VsZXNzICsgCiAgICAgICAgICAgICAgICAgICgxICsgbG9nMmR1cl9jICsgaXNfdm9pY2VsZXNzIHwgaWRzdHJpbmcpICsKICAgICAgICAgICAgICAgICAgKDEgKyBsb2cyZHVyX2MgfCB3b3JkKSwgCiAgICAgICAgICAgICAgICAgZGF0YSA9IGFoX3RvbW9kKQpgYGAKCmBgYHtyfQphaF9tb2RlbF8yIDwtIGxtZXIoRjFfbiB+IGxvZzJkdXJfYyArIGlzX3ZvaWNlbGVzcyArIAogICAgICAgICAgICAgICAgICAoMSArIGxvZzJkdXJfYyArIGlzX3ZvaWNlbGVzcyB8IGlkc3RyaW5nKSArCiAgICAgICAgICAgICAgICAgICgxICsgbG9nMmR1cl9jIHwgd29yZCksIAogICAgICAgICAgICAgICAgIGRhdGEgPSBhaF90b21vZCkKYGBgCgoKYGBge3J9CmFub3ZhKGFoX21vZGVsX2Z1bGwsIGFoX21vZGVsXzIpCmBgYAoKPC9kaXY+CgoKVGhlcmUncyBhIGxvdCB0byBsb29rIGF0IGhlcmU6Cgo8ZGl2IGNsYXNzID0gJ2hhbGYtaW1nJz4KIVtdKGZpZ3VyZXMvYWljX2JpYy5wbmcpCjwvZGl2PgoKVGhlIEFJQyBhbmQgdGhlIEJJQyBhcmUgbWV0cmljcyBvZiBtb2RlbCBmaXQuIFRoZXkncmUgdHJ5aW5nIHRvIHF1YW50aWZ5IHRoaXMgYmFsYW5jZSBiZXR3ZWVuIGhvdyB3ZWxsIHRoZSBtb2RlbHMgZml0IHRoZSBkYXRhIHZzIGhvdyBjb21wbGV4IHRoZXkgYXJlLiAKSSd2ZSBtZW50aW9uZWQgdGhlIHRoZSB0cmFkZW9mZiBiZXR3ZWVuIGltcHJvdmluZyB0aGUgbW9kZWwgZml0IGFuZCB0cnlpbmcgdG8gYXZvaWQgb3ZlcmZpdHRpbmcsIGFuZCB0aGVzZSAqSW5mb3JtYXRpb24gQ3JpdGVyaW9ucyogYXJlIG9uZSB3YXkgb2YgdHJ5aW5nIHRvIHF1YW50aWZ5IGl0LgpJIGhhdmUgdGhlIGZvcm11bGFzIGZvciB0aGUgQUlDIGFuZCB0aGUgQklDIGhlcmUsIG5vdCBiZWNhdXNlIHRoZSBhY3R1YWwgbWF0aCBtYXR0ZXJzIGZvciB1cywgYnV0IGluIG9yZGVyIHRvIGRvIGEgY29udHJhc3RpdmUgYW5hbHlzaXM6CgokJApBSUMgPSAyayAtIDJcbG4oXGhhdHtMfSkKJCQKCiQkCkJJQyA9IFxsbihuKWsgLSAyXGxuKFxoYXR7TH0pCiQkCgpUaGlzIGlzIGEgY2xhc3NpYyBleGFtcGxlIG9mIGhhdmluZyB0d28gY29tcGV0aW5nIG1ldHJpY3MgdGhhdCBhcmUgYmFzaWNhbGx5IHRoZSBzYW1lLCBidXQgcGVvcGxlIGhhdmUgdGhlaXIgcHJlZmVyZW5jZXMgc28gd2UgZ2V0IGJvdGguIAoKV2hlbiB5b3UncmUgY29tcGFyaW5nIG1vZGVscyAoZXZlbiBtdWx0aXBsZSBtb2RlbHMpIHVzaW5nIEFJQyBhbmQgQklDLCB0aGUgbW9kZWxzIHdpdGggdGhlICpzbWFsbGVzdCogQUlDIGFuZCBCSUMgYXJlIHRoZSBwcmVmZXJyZWQgb25lcy4gVGhlcmUgaXNuJ3QgYW4gZXN0YWJsaXNoZWQgZGlmZmVyZW5jZSBpbiBBSUMgYW5kIEJJQyB3aGljaCBpcyBjb25zaWRlcmVkIGEgInNpZ25pZmljYW50IiBkaWZmZXJlbmNlLgoKPGRpdiBjbGFzcyA9ICdoYWxmLWltZyc+CiFbXShmaWd1cmVzL2xydC5wbmcpCjwvZGl2PgoKVGhpcyBibG9jayBvZiBudW1iZXJzIGlzIGRlZGljYXRlZCB0byB0aGUgTGlrZWxpaG9vZCBSYXRpbyBUZXN0IChvciBMUlQpLiBJdCBpcyBhIHNpZ25pZmljYW5jZSB0ZXN0IGZvciB3aGV0aGVyIHRoZSBtb3JlIGNvbXBsZXggbW9kZWwgc2lnbmlmaWNhbnRseSBpbXByb3ZlcyBtb2RlbCBmaXQuIFNvbWUgbm90ZXM6CgotIExSVHMgc2hvdWxkIG9ubHkgYmUgY2FycmllZCBvdXQgY29tcGFyaW5nICJuZXN0ZWQiIG1vZGVscy4gVGhhdCBpcywgb25lIG1vZGVsIHNob3VsZCBoYXZlIGFsbCB0aGUgc2FtZSBwYXJhbWV0ZXJzIGFzIHRoZSBvdGhlciwgcGx1cyBzb21lLgotIExSVHMgc2hvdWxkIGlkZWFsbHkgYmUgY2FycmllZCBvdXQgb24gbWluaW1hbGx5IGNvbnRyYXN0aW5nIG1vZGVscy4KLSBUaGVzZSBhcmUgZXZhbHVhdGluZyBhICJzdGVwcGluZyB1cCIgb2YgdGhlIG1vZGVsIGNvbXBsZXhpdHkuCgpJIHdvdWxkIHJlY29tbWVuZCB1c2luZyBMUlRzIHRvIHByb2JlIHRoZSByZWxpYWJsaXR5IG9mIHZlcnkgc3BlY2lmaWMgcHJlZGljdG9ycyBhbmQgdGhlaXIgaW50ZXJhY3Rpb25zLgpJbiB0aGlzIGV4YW1wbGUsIGlmIHdlIHdlcmUgaW50ZXJlc3RlZCBtb3JlIGJyb2FkbHkgaW4gdGhlIGVmZmN0IG9mIHZvaWNpbmcsIHdlIGNvdWxkIHRyeSBmaXR0aW5nIG9uZSBtb3JlIG1vZGVsIHRoYXQgcmVtb3ZlcyB0aGUgdm9pY2luZyBmaXhlZCBlZmZlY3QgYWx0b2doZXRlciAobm90IHRvdWNoaW5nIHRoZSByYW5kb20gZWZmZWN0cyB0aG91Z2ghKS4KCgpgYGB7cn0KYWhfbW9kZWxfMyA8LSBsbWVyKEYxX24gfiBsb2cyZHVyX2MgKyAKICAgICAgICAgICAgICAgICAgKDEgKyBsb2cyZHVyX2MgKyBpc192b2ljZWxlc3MgfCBpZHN0cmluZykgKwogICAgICAgICAgICAgICAgICAoMSArIGxvZzJkdXJfYyB8IHdvcmQpLCAKICAgICAgICAgICAgICAgICBkYXRhID0gYWhfdG9tb2QpCmBgYAoKCgpgYGB7cn0KYW5vdmEoYWhfbW9kZWxfZnVsbCwgYWhfbW9kZWxfMiwgYWhfbW9kZWxfMykKYGBgCgo+IFdoaWxlIHRoZSBCSUMgZG9lcyBub3QgZGlzdGluZ3Vpc2ggYmV0d2VlbiB0aGVzZSB0aHJlZSBtb2RlbHMsIHRoZSBtb2RlbCB3aXRoIGp1c3QgYSBtYWluIGVmZmVjdCBvZiB2b2ljaW5nIGlzIHByZWZlcnJlZCBieSB0aGUgQUlDLiBUaGlzIGlzIGFsc28gdGhlIG9ubHkgbW9kZWwgdG8gc2lnbmlmaWNhbnRseSBpbXByb3ZlIG1vZGVsIGZpdCBvdmVyIGEgc2ltcGxlciBtb2RlbCBhY2NvcmRpbmcgdG8gdGhlIExpa2VsaWhvb2QgUmF0aW8gVGVzdC4gSXQgd291bGQgc2VlbSB0aGVuIHRoYXQgdGhlIGZvbGxvd2luZyBzZWdtZW50J3Mgdm9pY2luZyBoYXMgYSByZWxpYWJsZSBlZmZlY3Qgb24gRjEsIGJ1dCBpdCBkb2VzIG5vdCByZWxpYWJseSBpbnRlcmFjdCB3aXRoIHRoZSBkdXJhdGlvbiBvZiB0aGUgdm93ZWwvdm93ZWwgcmVkdWN0aW9uLgoKCgoKIyBUaGUgQm9vdHN0cmFwCgpJZiB5b3Ugd2FudCB0byBnZXQgc29tZXRoaW5nIHAtdmFsdWUtbGlrZSBvciBjb25maWRlbmNlIGludGVydmFsLWxpa2UgZm9yIHRoZSBwYXJhbWV0ZXJzIGluIGEgbWl4ZWQgZWZmZWN0cyBtb2RlbCwgdGhlIHBlb3BsZSB3aG8gaGF2ZSBkZXZlbG9wZWQgdGhlc2UgdGVjaG5pcXVlcyByZWNvbW1lbmQgKmJvb3RzdHJhcHBpbmcqLgpCb290c3RyYXBwaW5nIGlzIGEgdmVyeSBnZW5lcmFsIHB1cnBvc2UgdGVjaG5pcXVlIHRoYXQgc29ydCBvZiBsb29rcyBsaWtlIGl0IHNob3VsZG4ndCB3b3JrLiAKCiMjIEJvb3RzdHJhcHBpbmcgaW4gUHJpbmNpcGxlCgoqQm9vdHN0cmFwcGluZyogaXMgYSB2ZXJ5IGdlbmVyYWwgbWV0aG9kIGZvciBlc3RpbWF0aW5nIG91ciB1bmNlcnRhaW50eSBhYm91dCBhbnkgZ2l2ZW4gcGFyZW1ldGVyIGVzdGltYXRlLiBGb3IgZXhhbXBsZSwgbGV0J3Mgc2F5IHRoYXQgd2UgaGFkIDggcmFuZG9tbHleW0luIHRoaXMgY2FzZSwgbm90IGFjdHVhbGx5IHJhbmRvbS5dIHNhbXBsZWQgaGVpZ2h0cyBvZiBsaW5ndWlzdHMgYXQgdGhlIEludGl0dXRlLCBhbmQgd2Ugd2VyZSBpbnRlcmVzdGVkIGluIGhvdyBub2lzeSBhbiBlc3RpbWF0ZSB0aGlzIGlzIG9mIHRoZSBhdmVyYWdlIGhlaWdodCBvZiBsaW5ndWlzdHMuCgpgYGB7cn0KaGVpZ2h0cyA9IGMoYSA9IDY5LCBiID0gNzUsIGMgPSA2MiwgZCA9IDYyLCBlID0gNzMsIGYgPSA3MSwgZyA9IDYxLCBoID0gNjQpCm1lYW4oaGVpZ2h0cykKYGBgCgpPbmUgbWV0aG9kIHN0YXRpc3RpY2lhbnMgaGF2ZSBkZXZlbG9wZWQgaXMgdGhlICJib290c3RyYXAiIGVzdGltYXRlIG9mIHVuY2VydGFpbnR5LiBUaGUgd2F5IHRoYXQgd29ya3MgaXMgeW91IGZpcnN0IHJlc2FtcGxlIHlvdXIgc2FtcGxlLCB3aXRoIHJlcGxhY2VtZW50LgoKYGBge3J9Cm5ld19oZWlnaHRzID0gc2FtcGxlKGhlaWdodHMsIHJlcGxhY2UgPSBUKQpuZXdfaGVpZ2h0cwpgYGAKClNvbWUgcGVvcGxlJ3MgaGVpZ2h0cyBoYXZlIHNob3duIHVwIG1vcmUgdGhhbiBvbmNlLCBhbmQgc29tZSBub25lIGF0IGFsbC4KVGhlbiwgeW91IHRha2UgdGhlIG1lYW4KCmBgYHtyfQptZWFuKG5ld19oZWlnaHRzKQpgYGAKCkFuZCBub3csIHlvdSBkbyB0aGlzIGh1bmRyZWRzIGFuZCBodW5kcmVkcyBvZiB0aW1lcy4KCmBgYHtyfQpyZXBsaWNhdGVzIDwtICgxOjEwMDAwMCkgJT4lCiAgICAgICAgICAgICAgICBtYXAofnNhbXBsZShoZWlnaHRzLCByZXBsYWNlID0gVCkpJT4lCiAgICAgICAgICAgICAgICBtYXAobWVhbiklPiUKICAgICAgICAgICAgICAgIHNpbXBsaWZ5KCkKYGBgCgpgYGB7cn0KZGF0YV9mcmFtZShyZXBsaWNhdGVzID0gcmVwbGljYXRlcyklPiUKICBnZ3Bsb3QoYWVzKHJlcGxpY2F0ZXMpKSsKICAgIHN0YXRfZGVuc2l0eShjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC42KSsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW4oaGVpZ2h0cykpKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAoKVGhlIHN0YXRpc3RpY2lhbnMgaGF2ZSBzZXQgdW50byB1cyB0aGF0IHRoaXMgaXMgYSBzZW5zaWJsZSB3YXkgdG8gZXN0aW1hdGUgdW5jZXJ0YWludHkgYWJvdXQgdGhlIG1lYW4gb2Ygb3VyIHNhbXBsZS4gCkFzIHRoZSBib290c3RyYXAgaXMgdG8gdGhlIHNhbXBsZSwgc28gdGhlIHNhbXBsZSBpcyB0byB0aGUgcG9wdWxhdGlvbi4KV2UgY2FuIHVzZSBzb21lIHZlcnkgc2ltcGxpc3RpYyBtZXRob2RzIHRvIGdldCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb3V0IG9mIHRoZSBib29zdHJhcHMsIHNheSBieSB0YWtpbmcgdGhlIGNlbnRlciA5NSUgaW50ZXJ2YWwgZnJvbSB0aGUgc2FtcGxlOgoKYGBge3J9CnF1YW50aWxlKHJlcGxpY2F0ZXMsIHByb2IgPSBjKDAuMDI1LCAwLjk3NSkpCmBgYAoKQmFzaWNhbGx5LCB0aGlzIG1lYW5zIHdlJ3ZlIGdvdCBhIHByZXR0eSBsb3VzeSBzYW1wbGUgZm9yIHRyeWluZyBlc3RpbWF0ZSB0aGUgKmF2ZXJhZ2UqIGhlaWdodCBvZiBsaW5ndWlzdHMuIFRoaXMgOTUlIENJIHJhbmdlcyBmcm9tIGp1c3QgYSBiaXQgdGFsbGVyIHRoYW4gdGhlIHNob3J0ZXN0IHBlb3BsZSBpbiB0aGUgc2FtcGxlIGFuZCBqdXN0IGEgYml0IHNob3J0ZXIgdGhhbiB0aGUgdGFsbGVzdCwgbWVhbmluZyB0aGF0IGFzIGZhciBhcyB3ZSBrbm93IHdpdGggdGhlc2UgOCBwZW9wbGUsIGFueSBvbmUgb2YgdGhlbSBjb3VsZCBiZSB0aGUgKmF2ZXJhZ2UqIGhlaWdodC4KCmBgYHtyfQpzb3J0KGhlaWdodHMpCmBgYAoKWW91IGNhbiB1c2UgYm9vdHN0cmFwIHJlcGxpY2F0aW9uIGZvciBhbnkgcHJvcGVydHkgb2YgdGhlIHNhbXBsZSwgaW5jaWRlbnRhbGx5LiBGb3IgZXhhbXBsZSwgd2UgY291bGQgdHJ5IHRvIGVzdGltYXRlIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgbGluZ3Vpc3RzJyBoZWlnaHRzIHVzaW5nIHRoaXMgbWV0aG9kLgoKYGBge3J9CnNkX3JlcGxpY2F0ZXMgPC0gKDE6MTAwMDAwKSAlPiUKICAgICAgICAgICAgICAgIG1hcCh+c2FtcGxlKGhlaWdodHMsIHJlcGxhY2UgPSBUKSklPiUKICAgICAgICAgICAgICAgIG1hcChzZCklPiUKICAgICAgICAgICAgICAgIHNpbXBsaWZ5KCkKYGBgCgoKYGBge3J9CmRhdGFfZnJhbWUoc2RfcmVwbGljYXRlcyA9IHNkX3JlcGxpY2F0ZXMpJT4lCiAgZ2dwbG90KGFlcyhzZF9yZXBsaWNhdGVzKSkrCiAgICBzdGF0X2RlbnNpdHkoY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNikrCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBzZChoZWlnaHRzKSkrCiAgICB0aGVtZV9taW5pbWFsKCkKYGBgCgoKSGVyZSdzIGEgYmV0dGVyIGV4YW1wbGUuIExldCdzIGxvb2sgYXQgdGhlIHJhdGlvIG9mIGBpeWAgZHVyYXRpb24gdG8gYGFoYCBkdXJhdGlvbiBhZ2Fpbi4KCmBgYHtyfQp2b3dlbF9yYXRpbyA8LSBpeV9haCAlPiUKICBncm91cF9ieShpZHN0cmluZywgcGx0X3ZjbGFzcyklPiUKICBzdW1tYXJpc2UoZHVyID0gbWVkaWFuKGR1cikpJT4lCiAgc3ByZWFkKHBsdF92Y2xhc3MsIGR1ciklPiUKICBtdXRhdGUocmF0aW8gPSBhaC9peSkKCgp2b3dlbF9yYXRpbyAlPiUKZ2dwbG90KGFlcyhyYXRpbykpKwogICAgc3RhdF9kZW5zaXR5KGFscGhhID0gMC42LCBjb2xvciA9ICdibGFjaycpKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAoKVGhlIG1lZGlhbiBkdXJhdGlvbiAKCgpgYGB7cn0KcmF0aW9fYm9vdHN0cmFwIDwtICgxOjEwMDAwMCklPiUKICAgICAgICAgICAgICAgICAgICAgIG1hcCh+c2FtcGxlKHZvd2VsX3JhdGlvJHJhdGlvLCByZXBsYWNlID0gVCkpJT4lCiAgICAgICAgICAgICAgICAgICAgICBtYXAobWVkaWFuKSU+JQogICAgICAgICAgICAgICAgICAgICAgc2ltcGxpZnkoKQpgYGAKCmBgYHtyfQpkYXRhX2ZyYW1lKHJhdGlvX2Jvb3RzdHJhcCA9IHJhdGlvX2Jvb3RzdHJhcCkgJT4lCiAgZ2dwbG90KGFlcyhyYXRpb19ib290c3RyYXApKSsKICAgIHN0YXRfZGVuc2l0eShhbHBoYSA9IDAuNiwgY29sb3IgPSAiYmxhY2siKSsKICAgIHhsaW0oMC44LCAyKSsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCgoKIyMgQm9vdHN0cmFwcGluZyBmb3IgUmVncmVzc2lvbgoKKipOT1RFISoqOiBJIGdpdmUgYSBidW5jaCBvZiBjb2RlIHRvIGRvIGJvb3RzdHJhcCByZXBsaWNhdGlvbiBieSBoYW5kIGhlcmUsIGJ1dCB0aGVyZSBpcyBhIHByZS1idWlsdCBmdW5jdGlvbiB0byB1c2UgY2FsbGVkIGBib290TWVyKClgLgoKQm9vdHN0cmFwcGluZyB0byBnZXQgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHJlZ3Jlc3Npb24gcGFyZW1ldGVycyB3b3JrcyBpbiBhIHNpbWlsYXIgd2F5LCBidXQgaW1wbGVtZW50ZWQgZGlmZmVyZW50bHkuIApGb3IgY2xhcml0eSdzIHNha2UsIHdlJ2xsIGp1c3QgbG9vayBhdCB0aGUgZGF0YSBmcm9tIG9uZSBzcGVha2VyLgoKCmBgYHtyfQpzcDEgPC0gYWhfdG9tb2QgJT4lIAogICAgICAgICAgZmlsdGVyKGlkc3RyaW5nID09IGlkc3RyaW5nWzFdKSAlPiUKICAgICAgICAgIHNlbGVjdChGMV9uLCBsb2cyZHVyX2MpCmBgYAoKYGBge3J9CmdncGxvdChzcDEsIGFlcyhsb2cyZHVyX2MsIEYxX24pKSsKICAgIGdlb21fcG9pbnQoKSsKICAgIHN0YXRfc21vb3RoKG1ldGhvZCA9ICdsbScpCmBgYAoKCmBgYHtyfQpzcDFfbW9kIDwtIGxtKEYxX24gfiBsb2cyZHVyX2MsIGRhdGEgPSBzcDEpCnNwMV9tb2QKYGBgCgpXaXRoIHRoaXMgbW9kZWwsIGV2ZXJ5IGRhdGEgcG9pbnQgaGFzIHRocmVlIHZhbHVlcyBhc3NvY2lhdGVkIHdpdGggaXQ6CgoxLiBJdHMgeCB2YWx1ZSAoaW4gdGhpcyBjYXNlLCBpdHMgZHVyYXRpb24pCjIuIEl0cyBmaXR0ZWQgeSB2YWx1ZSAoaW4gdGhpcyBjYXNlLCBpdHMgcHJlZGljdGVkIEYxKQozLiBJdHMgcmVzaWR1YWwgZXJyb3IgKHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gaXRzIG9ic2VydmVkIEYxIGFuZCBhbmQgdGhlIHByZWRpY3RlZCBGMSkuCgpJbiBvcmRlciB0byBnZXQgYSBib290c3RyYXAgZXN0aW1hdGUgb2YgdGhlIGludGVyY2VwdCBhbmQgc2xvcGUgaGVyZSwgeW91IGFjdHVhbGx5IHNhbXBsZSB0aGUgcmVzaWR1YWxzIHdpdGggcmVwbGFjZW1lbnQsIGFkZCB0aGVtIHRvIGVhY2ggcG9pbnQncyBmaXR0ZWQgdmFsdWUsIGFuZCByZWZpdCB0aGUgbW9kZWwuCgpgYGB7cn0Kc3AxJHJlc2lkIDwtIHJlc2lkKHNwMV9tb2QpCnNwMSRmaXR0ZWQgPC0gZml0dGVkKHNwMV9tb2QpCmhlYWQoc3AxKQpgYGAKCkFzIGEgcmVtaW5kZXIsIGhlcmUncyBhIHBsb3Qgb2YgdGhlIG9ic2VydmVkIGR1cmF0aW9uIHZhbHVlcyBhZ2FpbnN0IHRoZSBmaXR0ZWQgRjEgdmFsdWVzLgoKYGBge3J9CmdncGxvdChzcDEsIGFlcyhsb2cyZHVyX2MsIGZpdHRlZCkpKwogICAgZ2VvbV9wb2ludCgpCmBgYAoKVG8gZ2V0IGJhY2sgdGhlIG9yaWdpbmFsIGRhdGEsIHlvdSBuZWVkIHRvIGFkZCBlYWNoIGRhdGEgcG9pbnQncyByZXNpZHVhbCBlcnJvciB0byB0aGUgZml0dGVkIHZhbHVlLgoKYGBge3J9CmdncGxvdChzcDEsIGFlcyhsb2cyZHVyX2MsIGZpdHRlZCArIHJlc2lkKSkrCiAgICBnZW9tX3BvaW50KCkKYGBgCgpJbiBib290c3RyYXBwaW5nLCB5b3UgYWN0dWFsbHkgY3JlYXRlIGEgbmV3IHNldCBvZiByZXNpZHVhbHMgYnkgc2FtcGxpbmcgdGhlIGV4aXN0aW5nIHJlc2lkdWFscyB3aXRoIHJlcGxhY2VtZW50LiBUaGVuIHlvdSBhZGQgdGhlc2UgdG8gdGhlIGZpdHRlZCB2YWx1ZXMgdG8gZ2V0IGEgbmV3IGJvb3RzdHJhcCByZXBsaWNhdGUuCgo8ZGl2IGNsYXNzID0gImJveCBicmVhayI+CjxzcGFuIGNsYXNzPSdiaWctbGFiZWwnPn4yIG1pbnV0ZSBicmVhazwvc3Bhbj4KClJ1biB0aGlzIGNvZGUgaW4gYSBjaHVuayBhIGhhbmRmdWwgb2YgdGltZXMuCgpgYGB7cn0KeW1heCA8LSBtYXgoc3AxJGZpdHRlZCkgKyBtYXgoc3AxJHJlc2lkKQp5bWluIDwtIG1pbihzcDEkZml0dGVkKSArIG1pbihzcDEkcmVzaWQpCgoKc3AxICU+JQogIG11dGF0ZShyZXNpZF9zYW1wID0gc2FtcGxlKHJlc2lkLCByZXBsYWNlID0gVCkpJT4lCiAgZ2dwbG90KGFlcyhsb2cyZHVyX2MsIGZpdHRlZCArIHJlc2lkX3NhbXApKSsKICAgIGdlb21fcG9pbnQoKSsKICAgIHlsaW0oeW1pbiwgeW1heCkrCiAgICBzdGF0X3Ntb290aChtZXRob2QgPSAnbG0nKSsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCjwvZGl2PgoKTm93LCB3ZSBqdXN0IG5lZWQgdG8gY29sbGVjdCB1cCBhbGwgb2YgdGhlc2UgcmUtZml0dGluZ3MsIGFuZCBwbG90IHRoZW0uCgoKYGBge3J9CmxtX2Jvb3QgPC0gKDE6MTAwMCklPiUKICAgICAgICAgICAgICAgIG1hcCh+KHNwMSAlPiUgbXV0YXRlKHJlc2lkX3NhbXAgPSBzYW1wbGUocmVzaWQsIHJlcGxhY2UgPSBUKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZzJkdXJfYyA9IHJlc2lkX3NhbXAgKyBmaXR0ZWQpKSklPiUKICAgICAgICAgICAgICAgIG1hcCh+KGxtKEYxX24gfiBsb2cyZHVyX2MsIGRhdGEgPSAueCkpKSU+JQogICAgICAgICAgICAgICAgbWFwKHRpZHkpJT4lCiAgICAgICAgICAgICAgICBiaW5kX3Jvd3MoLmlkID0gImlkIikKYGBgCgpgYGB7cn0KbG1fYm9vdCAlPiUKICBnZ3Bsb3QoYWVzKGVzdGltYXRlKSkrCiAgICBzdGF0X2RlbnNpdHkoYWxwaGEgPSAwLjYsIGNvbG9yID0gImJsYWNrIikrCiAgICBmYWNldF93cmFwKH50ZXJtLCBzY2FsZXMgPSAiZnJlZSIpKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCkrCiAgICB0aGVtZV9idygpCmBgYAoKIyMgQm9vdHN0cmFwcGluZyBMTUVzCgpUaGUgc3BlY2lmaWMgd2F5IHdoaWNoIGJvb3N0cmFwcGluZyB3b3JrcyBmb3IgYGxtZXJgIG1vZGVscyBpcyBkaWZmZXJlbnQgdGhhbiBmb3IgdmFuaWxsYSBsaW5lYXIgbW9kZWxzIChkZWFsaW5nIHdpdGggdGhlIHJhbmRvbSBlZmZlY3RzIHN0cnVjdHVyZSBhcHByb3ByaWF0ZWx5IGlzIGNvbXBsZXgpLCBidXQgZm9ydHVuYXRlbHkgd2UgZG9uJ3QgbmVlZCB0byB3cml0ZSBjb2RlIHRvIGRvIHRoYXQgZm9yIHVzLiAKVGhlIGZ1bmN0aW9uIGBib290TWVyKClgIHdpbGwgZG8gaXQgZm9yIHVzLgoKPGRpdiBjbGFzcyA9ICJpbGx1c3RyYXRlIj4KYm9vdE1lcig8c3BhbiBjbGFzcyA9ICJwb3AiPm1vZGVsPC9zcGFuPiwgPHNwYW4gY2xhc3MgPSAicG9wIj5GVU4gPSBmaXhlZjwvc3Bhbj4sIDxzcGFuIGNsYXNzID0gInBvcCI+bnNpbTwvc3Bhbj4sIC4uLikKPC9kaXY+CgotIGBtb2RlbGAKICAgIC0gdGhlIG1vZGVsIHlvdSB3YW50IHRvIGRvIGJvb3RzdHJhcHBpbmcgb24gKGNhbiBiZSBhIGxpbmVhciBvciBvdGhlciBnZW5lcmFsaXplZCBsaW5lYXIgbWl4ZWQgZWZmZWN0cyBtb2RlbCkuCi0gYEZVTmAKICAgIC0gVGhlIGZ1bmN0aW9uIHlvdSB3YW50IHRvIGFwcGx5IHRvIGVhY2ggcmUtZml0IG1vZGVsLiBUaGlzIHdpbGwgYWxtb3N0IGFsd2F5cyBiZSBqdXN0IGBmaXhlZmAKLSBgbnNpbWAKICAgIC0gVGhlIG51bWJlciBvZiBib290c3RyYXAgc2ltdWxhdGlvbnMgeW91IHdhbnQgdG8gZG8KLSBgLi4uYAogICAgLSBBZGRpdGlvbmFsICBvcHRpb25hbCBhcmd1bWVudHMgKGUuZy4gd2hhdCBkaWZmZXJlbnQga2luZHMgb2YgYm9vdHN0cmFwcGluZyB5b3Ugd2FudCB0byBkbywgd2hldGhlciBvciBub3QgdG8gdXNlIHBhcmFsbGVsIHByb2Nlc3NpbmcpLgogICAKPGRpdiBjbGFzcyA9ICJicmVhayBib3giPgo8c3BhbiBjbGFzcyA9ICJiaWctbGFiZWwiPn41IG1pbnV0ZSBicmVhazwvc3Bhbj4KClJ1biB0aGlzIGNvZGUgdG8gZG8gNSBib290c3RyYXAgcmVwbGljYXRpb25zCgoKYGBge3J9CmFoX2Jvb3RzdHJhcHMgPC0gYm9vdE1lcihhaF9tb2RlbF9mdWxsLCAKICAgICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGZpeGVmLCAKICAgICAgICAgICAgICAgICAgICAgICAgIG5zaW0gPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFQpCmBgYAo8L2Rpdj4KClRoZSBzb21ld2hhdCBhbm5veWluZyB0aGluZyBhYm91dCBib290c3RyYXBwaW5nIGlzIHRoYXQgaXQgdGFrZXMganVzdCBhcyBsb25nIHRvIGZpdCBlYWNoIHJlcGxpY2F0aW9uIGFzIGl0IGRvZXMgdG8gZml0IHRoZSBvcmlnaW5hbCBtb2RlbC4KVGhhdCdzIHdoeSBJIGJha2VkIG9uZSBhdCBob21lIHRvIHNoYXJlIGluIHRoZSBgbHNhMjAxN2AuCgpgYGB7cn0KYWhfbW9kZWxfYm9vdApgYGAKCkVhY2ggYHQqYCBpdGVtIGNvcnJlc3BvbmRzIHRvIGEgZml4ZWQgZWZmZWN0IGNvZWZmaWNpZW50LiBUaGV5IHNob3cgdXAgaW4gdGhlIHN1bW1hcnkgaW4gdGhlIHNhbWUgb3JkZXIgdGhleSBkaWQgaW4gdGhlIG1vZGVsIHN1bW1hcnk6CgpgYGB7cn0KZml4ZWYoYWhfbW9kZWxfZnVsbCkKYGBgCgpJbiB0aGUgYm9vdHN0cmFwIHN1bW1hcnksIHRoZSBgb3JpZ2luYWxgIGNvbHVtbiBjb3JyZXNwb25kcyB0byB0aGUgb3JpZ2luYWwgY29lZmZpY2llbnQgZXN0aW1hdGUgZnJvbSB0aGUgbW9kZWwuCgpUbyBnZXQgYSBjb25maWRlbmNlIGludGVydmFsIGZvciB0aGVzZSBwYXJhbWV0ZXJzLCB3ZSBuZWVkIHRvIHVzZSB0aGUgYGJvb3QuY2koKWAgZnVuY3Rpb24uCgo8ZGl2IGNsYXNzID0gImlsbHVzdHJhdGUiPgpib290LmNpKDxzcGFuIGNsYXNzID0gInBvcCI+Ym9vdHN0cmFwPC9zcGFuPiwgPHNwYW4gY2xhc3MgPSAicG9wIj5pbmRleDwvc3Bhbj4sIDxzcGFuIGNsYXNzID0gInBvcCI+dHlwZSA9IHBlcmM8L3NwYW4+KQo8L2Rpdj4KCi0gYGJvb3RzdHJhcGAKICAgIC0gVGhlIGJvb3RzdHJhcCBvYmplY3QKLSBgaW5kZXhgCiAgICAtIFRoZSBpbmRleCBvZiB0aGUgY29lZmZpY2llbnQgeW91IHdhbnQgYSBjb25maWRlbmNlIGludGVydmFsIGZvcgotIGB0eXBlYAogICAgLSBVbmZvcnR1bmF0ZWx5LCB0aGVyZSBhcmUgbWFueSBkaWZmZXJlbnQgd2F5cyBvZiBlc3RpbWF0aW5nIGEgY29uZmlkZW5jZSBpbnRlcnZhbCBmcm9tIGJvb3RzdHJhcHBlZCByZXBsaWNhdGVzLCBhbmQgbm90IGFsbCBvZiB0aGVtIHdvcmsgZm9yIGEgYm9vdHN0cmFwcGVkIGxtZS4gSSByZWNvbW1lbmQgYHR5cGUgPSAicGVyYyJgCiAgICAKU28gdG8gZ2V0IHRoZSBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIGludGVyY2VwdCwgd2UnZCBnaXZlIGBpbmRleD0xYC4KCgpgYGB7cn0KYm9vdC5jaShhaF9tb2RlbF9ib290LCBpbmRleCA9IDEsIHR5cGUgPSAicGVyYyIpCmBgYAoKVGhpcyBjb25maWRlbmNlIGludGVydmFsIGV4Y2x1ZGVzIDAsIHNvIHdlIGNvdWxkIGNhbGwgdGhpcyBhIHJlbGlhYmxlIGVmZmVjdC4KClRvIGdldCB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIGludGVyYWN0aW9uIGJldHdlZW4gdm9pY2luZyBhbmQgZHVyYXRpb24sIHlvdSBnaXZlIGl0IGBpbmRleCA9IDRgCgoKYGBge3J9CmJvb3QuY2koYWhfbW9kZWxfYm9vdCwgaW5kZXggPSA0LCB0eXBlID0gInBlcmMiKQpgYGAKCkluIHRoaXMgY2FzZSwgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgY29udGFpbnMgMCwgc28gdGhpcyBkb2VzIG5vdCBsb29rIGxpa2UgYSByZWxpYWJsZSBlZmZlY3QuCgpTbywgaWYgeW91IHdlcmUgd3JpdGluZyBhIHBhcGVyIG9uIHRoZSBlZmZlY3Qgb2Ygdm93ZWwgZHVyYXRpb24gYW5kIHZvaWNpbmcgb24gdGhlIEYxIG9mIGBhaGAsIHlvdSBjb3VsZCBhcmd1ZSBiYXNlZCBvbiBjb252ZXJnZW50IGV2aWRlbmNlIGZyb20gdGhlIG1vZGVsIGNvbXBhcmlzb25zLCBsaWtlbGlob29kIHJhdGlvIHRlc3QsIGFuZCB0aGUgYm9vdHN0cmFwIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIHRoYXQgdGhlcmUgaXMgYSBjb25zaXN0ZW50IGVmZmVjdCBvZiBmb2xsb3dpbmcgdm9pY2VsZXNzIGNvbnNvbmFudHMgcmFpc2luZyBGMSAobG93ZXJpbmcgdGhlIHZvd2VsKSwgYnV0IHRoaXMgZG9lcyBub3QgaW50ZXJhY3Qgd2l0aCBkdXJhdGlvbiByZWxpYWJseS4KCgoKYGBge3J9CmFoX21vZGVsX2Jvb3QkdCAlPiUKICBkYXRhLmZyYW1lKCklPiUKICBnYXRoZXIoKSU+JQogIGdncGxvdChhZXModmFsdWUpKSsKICAgIHN0YXRfZGVuc2l0eShhbHBoYSA9IDAuNiwgY29sb3IgPSAiYmxhY2siKSsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gMikrCiAgICBmYWNldF93cmFwKH5rZXksIHNjYWxlcyA9ICJmcmVlIikrCiAgICB0aGVtZV9idygpCmBgYAoKCg==