Intro

Setup

library(tidyverse)
library(lsa2017)
library(broom)
library(lme4)
ah <- iy_ah %>% filter(plt_vclass == "ah", 
                       context %in% c("internal", "final"))

The Agenda For today

  1. Introduce the justification for fitting LMEs
  2. Explore no-pooling models
  3. Fit some LMEs
  4. Discuss when & how to include random intercepts / slopes

The rationale behind LMEs

The original reasoning behind moving to linear mixed effects modelling has a lot to do with sophisticated statistical reasoning which I’ll just briefly discuss. Let’s return to our ah data.

ah_tomod <- ah %>% 
              mutate(log2dur_c = log2(dur) - median(log2(dur)))
ah_tomod %>% 
  ggplot(aes(log2dur_c, F1_n))+
    geom_point(alpha = 0.05)+
    stat_smooth(method = 'lm', color = 'red')+
    theme_bw()

NA

You may not even be able to tell that the regression line has has a confidence interval because it is narrower than the width of the line! That’s because the model has low uncertainty about the slope:

summary(lm(F1_n ~ log2dur_c, data = ah_tomod))$coef
             Estimate  Std. Error   t value Pr(>|t|)
(Intercept) 1.0605806 0.004278806 247.86834        0
log2dur_c   0.3896566 0.006997420  55.68575        0

But in an important sense, this model is overly confident. It thinks it’s working with 20813 independent observations. As if 20813 different people walked up to a microphone and said ah. But that is exactly not what happened here. Instead, a smaller number of speakers (292) had many tokens of ah, and not always the same number of tokens each.

To make the example more extreme, let’s imagine we had data from only two speakers, one who produced very many tokens, and one who produced relatively few.

ah_tomod %>%
  mutate(idstring = as.character(idstring))%>%
  group_by(idstring)%>%
  mutate(N = n())%>%
  filter(N > 10)%>%
  ungroup() %>%  
  filter(N == max(N) | N == min(N))%>%
  ggplot(aes(log2dur_c, F1_n))+
    geom_point()+
    stat_smooth(method = 'lm')+
    facet_wrap(~idstring)+
    theme_bw()

The speaker with very little data has a pretty shallow slope in the relationship between duration and F1, and the speaker with a lot of data has a much steeper slope. However, if you fit a model with just these two speakers’ data in the model, the estimated slope would be be dominated by the speaker with a lot of data, and almost none given to the speaker with very little. However, that’s not ideal, since with should give a bit more weight to the fact that at least one speaker has a weaker effect, even if they have less data.

An even more perverse situation could arise if there’s a strong correlation between a the speaker and their intercept. For example, in these figures it looks like the relationship between duration and F1 is positive, but it’s conceptually possible that the relationship within every individual is actually negative if they were arranged like this figure:

Thinking about Pooling

One way to start thinking about mixed effects models is to think about how you are “pooling” the data from your data sources. In this example, we’re talking about speakers, but this could also be study sites, lexical items, etc.

Complete Pooling

What people have been doing for a long time is “complete pooling.” All of the data from all of the sources all just goes into one big data bucket. You then fit a model with all of this data in the bucket, undifferentiated by who contributed what data point.

For the reasons we’ve discussed already, this isn’t a great approach.

  1. It violates some statistical assumptions.
  2. The parameter estimates are over-confident.
  3. The parameter estimates are too skewed towards speakers/words with more data.
  4. It is possible for the structure of the data to give you exactly the wrong estimate.

No Pooling

One way you could try to resolve some of these problems would be to fit one model for every speaker. Here, you don’t pool the data at all. Each speaker just has their own bucket of data.

We’ll actually do this in a second, and then discuss the pros and cons.

Partial Pooling

The final option is to do “partial pooling”. You model with all of the data, but preserve information about which speaker contributed which data. This approach allows the “model” for each speaker to mutually inform eachother.

This is a “mixed effects model”, which we’ll also look at.

Fitting no-pooling models

In this section, we’re going to fit some no-pooling models. It is not necessary to be able to do this to be able to fit mixed effects models, but I think this can help you understand what mixed effecs models are doing.

While the no-pooling approach to modelling isn’t the most sophisticated (and you definitely can’t use their p-values), I think it is often a good idea to try it out as an exploratory technique. You can check out a talk by Hadley Wickham about it here.

It is pretty easy to fit one model for one speaker, we just take a subset of the data and fit the model:

speakers <- unique(ah_tomod$idstring)
sp1 <- ah_tomod %>%
          filter(idstring == speakers[1])
sp1_mod <- lm(F1_n ~ log2dur_c, data = sp1)
summary(sp1_mod)

Call:
lm(formula = F1_n ~ log2dur_c, data = sp1)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.66684 -0.37403  0.02055  0.47308  1.74784 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.33411    0.05620  23.739  < 2e-16 ***
log2dur_c    0.48085    0.09345   5.145 7.97e-07 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.6659 on 155 degrees of freedom
Multiple R-squared:  0.1459,    Adjusted R-squared:  0.1404 
F-statistic: 26.48 on 1 and 155 DF,  p-value: 7.969e-07

There is even a handy dandy function in the broom package called tidy() that will convert a linear model object (and many others) into a data frame.

tidy(sp1_mod)

But there are 292 speakers in this data set. We can’t go repeating this procedure that many times. Defining the problem this way should start giving you the sense that we should be taking a Split-Apply-Combine approach!

Step 1 - Write a function that will fit the model when given a data frame.

We have unfortunately not been able to cover how to write functions in R, but here’s how this one will work:

fit_ah_mod <- function(df){
  lm(F1_n ~ log2dur_c, data = df)
}

What function() does is create a new function. It says “When passed some kind of data (let’s call it df), I’m going to try to use df to fit a linear model.” Here’s how it works in action:

fit_ah_mod(sp1)

Call:
lm(formula = F1_n ~ log2dur_c, data = df)

Coefficients:
(Intercept)    log2dur_c  
     1.3341       0.4809  

~2 minute break

Go ahead and create the fit_ah_mod function, and run it on the subset for one speaker. All the code to do that again is:

fit_ah_mod <- function(df){
  lm(F1_n ~ log2dur_c, data = df)
}

For fun here, choose some other number between 1 and 292.

speakers <- unique(ah_tomod$idstring)
sp_dat <- ah_tomod %>% filter(idstring == speakers[1])
fit_ah_mod(sp_dat)

Step 2 - Split up the data by speaker

fit_ah_mod() is now the function we want to apply to subsets of the data, but now we need to figure out how to split it up according to speaker. To start approaching that problem, we’re going to use the nest() function.

~2 minute break

Run this code:

ah_nested <- ah_tomod %>%
              group_by(idstring) %>%
              nest()
ah_nested

What happened?

This is a little mind bending, but in ah_nested, there is now one row per speaker (because we told it to group_by() speaker). Each cell in the idstring column has the speaker’s id. Each cell in the data column is a whole nother data frame.

ah_nested$data[[1]]

And, we can apply our fit_ah_mod() function to each of these data frames:

fit_ah_mod(ah_nested$data[[10]])

Call:
lm(formula = F1_n ~ log2dur_c, data = df)

Coefficients:
(Intercept)    log2dur_c  
     0.8434       0.0897  
fit_ah_mod(ah_nested$data[[210]])

Call:
lm(formula = F1_n ~ log2dur_c, data = df)

Coefficients:
(Intercept)    log2dur_c  
     1.3421       0.6129  

Step 3 - Apply the function to the data

But to do this in a clear and consistent way, we need to use map().

~2 minute break Run the following codeL

ah_nested <- ah_nested %>%
                mutate(model = map(data, fit_ah_mod))
ah_nested

What happened?

In the model column, which we created with mutate() there is now a linear model for each speaker.

ah_nested$model[[1]]

Call:
lm(formula = F1_n ~ log2dur_c, data = df)

Coefficients:
(Intercept)    log2dur_c  
     1.3341       0.4809  

Step 4 - Combining & Tidying

We now need to convert these linear models into a more useful data structure for plotting and analysis, i.e. a data frame. We can turn individual models into data frames with tidy().

tidy(ah_nested$model[[1]])

We can apply tidy() to each model in the model column with map() again.

~2 minute break Run this code

ah_nested <- ah_nested %>%
                mutate(params = map(model, tidy))
ah_nested

What happened?

We now have the model estimates in data frames in the params column. Getting them into a fully usable format is now as easy as unnest().

ah_params <- ah_nested %>%
                unnest(params)
ah_params

I want to make a plot where every speaker’s Intercept value is plotted against their slope value, so we need to use just the idstring, term and estimate columns, and use spread()

ah_params %>%
  select(idstring, term, estimate)%>%
  spread(term, estimate)%>%
  ggplot(aes(`(Intercept)`, log2dur_c))+
    geom_hline(yintercept = 0, linetype = 2)+  
    geom_point()+
    theme_minimal()+
    coord_fixed()

Here’s another version with the points scaled by size.

speaker_n <- ah_tomod %>%
                group_by(idstring)%>%
                tally()
ah_nested %>%
  unnest(params) %>%
  select(idstring, term, estimate) %>%
  spread(term, estimate) %>%
  left_join(speaker_n)%>%
  ggplot(aes(`(Intercept)`, log2dur_c))+
    geom_hline(yintercept = 0, linetype = 2)+  
    geom_point(aes(size = n), alpha = 0.5)+
    scale_size_area()+
    theme_minimal()+
    coord_fixed()
Joining, by = "idstring"

This no-pooling approach is both very informative, but also has some serious weaknesses. First of all, we can see that most speakers have the expected slope (on the y-axis) between duration and F1. That is, the slope is positive, meaning as duration increases, F1 increases (lowering the vowel). However, there is a lot if inter-speaker variability. Some speakers are estimated to have a fairly steep slope, and others have slopes closer to 0 or even negative.

Here’s a plot of the actual lines for each speaker.

ah_nested %>%
  unnest(params) %>%
  select(idstring, term, estimate) %>%
  spread(term, estimate) %>%
  ggplot()+
    geom_abline(aes(intercept = `(Intercept)`, 
                    slope = log2dur_c),
                alpha = 0.1)+
    xlim(-1.2, 2.85)+
    xlab("log2dur_c")+
    ylim(-2,4.3)+
    ylab("F1_n")+
    theme_minimal()

NA

Discussion

What are the upsides and the downsides to the no-pooling approach.

Partial Pooling (Mixed Effects)

The biggest downside to the no pooling approach is that there is no shared information between the models. It’s possible that speakers who had a negative slope were only estimated to have a negative slope because they had very little data. If there were some way to incorporate information about the slopes of every other speaker into these speakers’ slope estimates, they might be considerably different.

This is exactly what Mixed Effects Models do. You define “grouping variables” or “pooling variables” or “random effects” in the model to tell the model to estimate effects for each speaker, but not to do so completely independently for each speaker.

~2 Minute Activity

Fit a complete pooling model with lm and a partial pooling model with lmer with the following code.

ah_lm <-lm(F1_n ~ log2dur_c, data = ah_tomod)
ah_lme <- lmer(F1_n ~ log2dur_c + (1 + log2dur_c | idstring), data = ah_tomod)

The big difference between the complete pooling and the partial pooling models are the definition of the “random effects” structures.

(1 + log2dur_c | idstring)

  • Everything to the left of the |
    • Everything that can vary within the grouping variable.
    • 1 refers to the intercept
  • Everything to the right of the |
    • The grouping variable.

With (1 + log2dur_c | idstring ), we’re saying

Speakers can have variable intercepts, and variable slopes.

Let’s look at the summary of the mixed effects model.

summary(ah_lme)
Linear mixed model fit by REML ['lmerMod']
Formula: F1_n ~ log2dur_c + (log2dur_c | idstring)
   Data: ah_tomod

REML criterion at convergence: 37151.2

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-5.7512 -0.5717  0.0248  0.6012  5.6456 

Random effects:
 Groups   Name        Variance Std.Dev. Corr
 idstring (Intercept) 0.04204  0.2050       
          log2dur_c   0.01975  0.1405   0.37
 Residual             0.33511  0.5789       
Number of obs: 20813, groups:  idstring, 292

Fixed effects:
            Estimate Std. Error t value
(Intercept)  1.04005    0.01307   79.58
log2dur_c    0.40885    0.01141   35.84

Correlation of Fixed Effects:
          (Intr)
log2dur_c 0.249 

The “Random Effects”

By default, the summary doesn’t print out all of the “random effects”, because in this case there are two for each of the 292 speakers (intercept and slope). But it does give us estimates of the variance of these random effects, and their correlation. Here, it looks like speakers are more variable with respect to their baseline F1 than they are with respect to the effect duration has on their F1.

Fixed Effects

These are the estimated “fixed effect”. They can be interpreted a lot like the coefficients of the models we’ve been fitting already, in the sense that these are part of the formula to draw a line. Another way is to interpet these as the average intercept and slope between speakers.

Looking at the random effects

It is possible to extract the random effects using the function ranef().

head(ranef(ah_lme)$idstring)

These are the estimated differences in Intercept and slope for each speaker from the fixed effects. That is, if we added the (Intercept) value from the fixed effects to these random effects, we would get the estimated slope for each speaker. Let’s do that:

ah_lme_fixef <- fixef(ah_lme)
ranef(ah_lme)$idstring %>%
  ggplot(aes(`(Intercept)` + ah_lme_fixef[1], 
             log2dur_c + ah_lme_fixef[2]))+
    geom_point()+
    geom_hline(yintercept = 0, linetype = 2)+
    theme_minimal()+
    coord_fixed()

ah_lme_fixef <- fixef(ah_lme)
ranef(ah_lme)$idstring %>%
  ggplot()+
       geom_abline(aes(intercept = `(Intercept)` + ah_lme_fixef[1], 
                    slope = log2dur_c + ah_lme_fixef[2]),
                alpha = 0.1)+
    xlim(-1.2, 2.85)+
    xlab("log2dur_c")+
    ylim(-2,4.3)+
    ylab("F1_n")+
    theme_minimal()

We can get an idea of what the mixed-effects modelling has done by comparing these results to the no-pooling model. First, the clustering of individual speakers’ random effects is tighter. Also, no speaker is estiamted to have an individual slope less than 0.

It is also possible to see the positive correlation between the random effects. Speakers who have a baseline lower ah appear to be more affected by duration changes.

Multiple random effects

This is where it gets impossible to make illustrative graphs. It is also possible to include multiple grouping factors in a mixed effects model.

~5 minute break

We’re going to add word as a grouping factor. For now, we’ll treat word as only being able to modulate the baseline F1_n, which means we’ll add it with (1 | word) (a.k.a. a random intercept).

Go ahead and fit this model:

ah_lme_multi <- lmer(F1_n ~ log2dur_c + 
                       (1 + log2dur_c | idstring) + 
                       (1 | word), data = ah_tomod)

What happened?

However, it is possible to include a random slope by word too, so let’s do that as well:

ah_lme_multi2 <- ah_lme_multi <- lmer(F1_n ~ log2dur_c + 
                       (1 + log2dur_c | idstring) + 
                       (1 + log2dur_c| word), data = ah_tomod)
summary(ah_lme_multi2)
Linear mixed model fit by REML ['lmerMod']
Formula: 
F1_n ~ log2dur_c + (1 + log2dur_c | idstring) + (1 + log2dur_c |  
    word)
   Data: ah_tomod

REML criterion at convergence: 34226.6

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-5.8651 -0.5523  0.0174  0.5813  6.5283 

Random effects:
 Groups   Name        Variance Std.Dev. Corr 
 word     (Intercept) 0.07667  0.2769        
          log2dur_c   0.01929  0.1389   -0.40
 idstring (Intercept) 0.03487  0.1867        
          log2dur_c   0.01588  0.1260   0.50 
 Residual             0.27992  0.5291        
Number of obs: 20807, groups:  word, 939; idstring, 292

Fixed effects:
            Estimate Std. Error t value
(Intercept)  1.14155    0.01810   63.05
log2dur_c    0.28555    0.01689   16.90

Correlation of Fixed Effects:
          (Intr)
log2dur_c -0.078

One thing you might notice now is that the “fixed effect” of duration has gotten a bunch smaller.

coef(ah_lm)
(Intercept)   log2dur_c 
  1.0605806   0.3896566 
fixef(ah_lme_multi2)
(Intercept)   log2dur_c 
  1.1415491   0.2855541 

This may look scary, especially if the effect size gradually vanishes away, but this is exactly how mixed effects models are supposed to work. In some sense, in the complete-pooling model, we thought we were modelling the effect of duration, but actually some of that effect was actually due to the peculiarities of the words spoken, and who the speakers were.

When to add random intercepts/slopes?

Barr et al (2012) recommend (command?) fitting models with “maximal” random effects structures. That is, for every possible grouping factor, include a random slope for every grouping factor that’s logically possible. I think this is a good starting point, but isn’t always practical.

Logically Possible & Impossible Random Slopes

In the ah model we just fit, it was possible for us to fit a random slope of duration by speaker and word, because speakers produced tokens with different durations, and words were produced with different durations.

However, if we wanted to include context again (word initial or word final), we could include a random slope of context by speaker, but not by word. This isn’t a prohibition, it’s just not logically possible and might blow up your model. That’s because every word only has one vowel context. NAH, only has a word final vowel context, and there’s no way for it to be affected by being word internal.

Similarly, if we wanted to include year of birth in the model, we could include a random slope of dob by word, because many words were spoken by people across many ages. But we couldn’t include a random slope of dob by speaker, because each speaker only has one date of birth.

Necessary Random Effects

Any time you are looking at a between-grouping factor variable, you should include the grouping factor with a random intercept. This is especially true for any kind of research done outside of an experimental lab context.

  • age: (1 | speaker)
  • gender: (1 | speaker)
  • word frequency: (1 | word)
  • sentence grammaticality: (1 | token)

Other necessary random effects would be related to the nature of the data. In this ah model, we haven’t explored any between-grouping factors, but all of these data points were produced by speakers, embedded in specific words, so it’s important to capture that in the random effects.

Pitfalls in mixed effects modelling

The biggest issue people face when fitting a mixed effects model is that it may “fail to converge”. This is because these models aren’t ‘solved’ in the same way as the regular linear regression is. It is very important to only base your inference on converged models. There are a handful of tweaks that might help in your models converging.

  • center and scale all continuous predictors.
  • if possible, choose a different factor level that has more data as your reference level

The next step is to start taking out some of the recommended random slopes, or the ones most likely to be problematic. For example, in the ah model, the random slope of duration by word is likely to be problematic because many words only have one data point, making it more challenging to estimate a random slope for every word.

Mixed Effects Models Controversy?

One of the benefits of mixed-effects models is their limited ability to ameliorate unmodelled properties of the grouping factors.w

LS0tCnRpdGxlOiAiTWl4ZWQgRWZmZWN0cyBNb2RlbHMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBjb2RlX2ZvbGRpbmc6IG5vbmUKICAgIGNzczogLi4vY3VzdG9tLmNzcwogICAgdGhlbWU6IGZsYXRseQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDMKLS0tCgoKCiMgSW50cm8KCjxkaXYgY2xhc3MgPSAiYm94IGJyZWFrIj4KPHNwYW4gY2xhc3MgPSAiYmlnLWxhYmVsIj5TZXR1cDwvc3Bhbj4KCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsc2EyMDE3KQpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KGxtZTQpCgoKYWggPC0gaXlfYWggJT4lIGZpbHRlcihwbHRfdmNsYXNzID09ICJhaCIsIAogICAgICAgICAgICAgICAgICAgICAgIGNvbnRleHQgJWluJSBjKCJpbnRlcm5hbCIsICJmaW5hbCIpKQpgYGAKCgo8L2Rpdj4KCiMjIFRoZSBBZ2VuZGEgRm9yIHRvZGF5CgoxLiBJbnRyb2R1Y2UgdGhlIGp1c3RpZmljYXRpb24gZm9yIGZpdHRpbmcgTE1FcwoyLiBFeHBsb3JlIG5vLXBvb2xpbmcgbW9kZWxzCjMuIEZpdCBzb21lIExNRXMKNC4gRGlzY3VzcyB3aGVuICYgaG93IHRvIGluY2x1ZGUgcmFuZG9tIGludGVyY2VwdHMgLyBzbG9wZXMKCiMgVGhlIHJhdGlvbmFsZSBiZWhpbmQgTE1FcwoKVGhlIG9yaWdpbmFsIHJlYXNvbmluZyBiZWhpbmQgbW92aW5nIHRvIGxpbmVhciBtaXhlZCBlZmZlY3RzIG1vZGVsbGluZyBoYXMgYSBsb3QgdG8gZG8gd2l0aCBzb3BoaXN0aWNhdGVkIHN0YXRpc3RpY2FsIHJlYXNvbmluZyB3aGljaCBJJ2xsIGp1c3QgYnJpZWZseSBkaXNjdXNzLiBMZXQncyByZXR1cm4gdG8gb3VyIGBhaGAgZGF0YS4KCmBgYHtyfQoKYWhfdG9tb2QgPC0gYWggJT4lIAogICAgICAgICAgICAgIG11dGF0ZShsb2cyZHVyX2MgPSBsb2cyKGR1cikgLSBtZWRpYW4obG9nMihkdXIpKSkKCmFoX3RvbW9kICU+JSAKICBnZ3Bsb3QoYWVzKGxvZzJkdXJfYywgRjFfbikpKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMDUpKwogICAgc3RhdF9zbW9vdGgobWV0aG9kID0gJ2xtJywgY29sb3IgPSAncmVkJykrCiAgICB0aGVtZV9idygpCiAgICAKYGBgCgpZb3UgbWF5IG5vdCBldmVuIGJlIGFibGUgdG8gdGVsbCB0aGF0IHRoZSByZWdyZXNzaW9uIGxpbmUgaGFzIGhhcyBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgYmVjYXVzZSBpdCBpcyBuYXJyb3dlciB0aGFuIHRoZSB3aWR0aCBvZiB0aGUgbGluZSEgVGhhdCdzIGJlY2F1c2UgdGhlIG1vZGVsIGhhcyBsb3cgdW5jZXJ0YWludHkgYWJvdXQgdGhlIHNsb3BlOgoKYGBge3J9CnN1bW1hcnkobG0oRjFfbiB+IGxvZzJkdXJfYywgZGF0YSA9IGFoX3RvbW9kKSkkY29lZgpgYGAKCkJ1dCBpbiBhbiBpbXBvcnRhbnQgc2Vuc2UsIHRoaXMgbW9kZWwgaXMgKm92ZXJseSogY29uZmlkZW50LiBJdCB0aGlua3MgaXQncyB3b3JraW5nIHdpdGggYHIgbnJvdyhhaF90b21vZClgICppbmRlcGVuZGVudCogb2JzZXJ2YXRpb25zLiBBcyBpZiBgciBucm93KGFoX3RvbW9kKWAgZGlmZmVyZW50IHBlb3BsZSB3YWxrZWQgdXAgdG8gYSBtaWNyb3Bob25lIGFuZCBzYWlkIGBhaGAuIEJ1dCB0aGF0IGlzIGV4YWN0bHkgbm90IHdoYXQgaGFwcGVuZWQgaGVyZS4gSW5zdGVhZCwgYSBzbWFsbGVyIG51bWJlciBvZiBzcGVha2VycyAoYHIgbGVuZ3RoKHVuaXF1ZShhaCRpZHN0cmluZykpYCkgaGFkIG1hbnkgdG9rZW5zIG9mIGBhaGAsIGFuZCBub3QgYWx3YXlzIHRoZSBzYW1lIG51bWJlciBvZiB0b2tlbnMgZWFjaC4KClRvIG1ha2UgdGhlIGV4YW1wbGUgbW9yZSBleHRyZW1lLCBsZXQncyBpbWFnaW5lIHdlIGhhZCBkYXRhIGZyb20gb25seSB0d28gc3BlYWtlcnMsIG9uZSB3aG8gcHJvZHVjZWQgdmVyeSBtYW55IHRva2VucywgYW5kIG9uZSB3aG8gcHJvZHVjZWQgcmVsYXRpdmVseSBmZXcuCgpgYGB7cn0KYWhfdG9tb2QgJT4lCiAgbXV0YXRlKGlkc3RyaW5nID0gYXMuY2hhcmFjdGVyKGlkc3RyaW5nKSklPiUKICBncm91cF9ieShpZHN0cmluZyklPiUKICBtdXRhdGUoTiA9IG4oKSklPiUKICBmaWx0ZXIoTiA+IDEwKSU+JQogIHVuZ3JvdXAoKSAlPiUgIAogIGZpbHRlcihOID09IG1heChOKSB8IE4gPT0gbWluKE4pKSU+JQogIGdncGxvdChhZXMobG9nMmR1cl9jLCBGMV9uKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBzdGF0X3Ntb290aChtZXRob2QgPSAnbG0nKSsKICAgIGZhY2V0X3dyYXAofmlkc3RyaW5nKSsKICAgIHRoZW1lX2J3KCkKYGBgCgoKVGhlIHNwZWFrZXIgd2l0aCB2ZXJ5IGxpdHRsZSBkYXRhIGhhcyBhIHByZXR0eSBzaGFsbG93IHNsb3BlIGluIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBkdXJhdGlvbiBhbmQgRjEsIGFuZCB0aGUgc3BlYWtlciB3aXRoIGEgbG90IG9mIGRhdGEgaGFzIGEgbXVjaCBzdGVlcGVyIHNsb3BlLiBIb3dldmVyLCBpZiB5b3UgZml0IGEgbW9kZWwgd2l0aCBqdXN0IHRoZXNlIHR3byBzcGVha2VycycgZGF0YSBpbiB0aGUgbW9kZWwsIHRoZSBlc3RpbWF0ZWQgc2xvcGUgd291bGQgYmUgYmUgZG9taW5hdGVkIGJ5IHRoZSBzcGVha2VyIHdpdGggYSBsb3Qgb2YgZGF0YSwgYW5kIGFsbW9zdCBub25lIGdpdmVuIHRvIHRoZSBzcGVha2VyIHdpdGggdmVyeSBsaXR0bGUuIEhvd2V2ZXIsIHRoYXQncyBub3QgaWRlYWwsIHNpbmNlIHdpdGggc2hvdWxkIGdpdmUgYSBiaXQgbW9yZSB3ZWlnaHQgdG8gdGhlIGZhY3QgdGhhdCBhdCBsZWFzdCBvbmUgc3BlYWtlciBoYXMgYSB3ZWFrZXIgZWZmZWN0LCBldmVuIGlmIHRoZXkgaGF2ZSBsZXNzIGRhdGEuCgoKQW4gZXZlbiBtb3JlIHBlcnZlcnNlIHNpdHVhdGlvbiBjb3VsZCBhcmlzZSBpZiB0aGVyZSdzIGEgc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gYSB0aGUgc3BlYWtlciBhbmQgdGhlaXIgaW50ZXJjZXB0LiBGb3IgZXhhbXBsZSwgaW4gdGhlc2UgZmlndXJlcyBpdCBsb29rcyBsaWtlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBkdXJhdGlvbiBhbmQgRjEgaXMgKnBvc2l0aXZlKiwgYnV0IGl0J3MgY29uY2VwdHVhbGx5IHBvc3NpYmxlIHRoYXQgdGhlIHJlbGF0aW9uc2hpcCB3aXRoaW4gZXZlcnkgaW5kaXZpZHVhbCBpcyBhY3R1YWxseSAqbmVnYXRpdmUqIGlmIHRoZXkgd2VyZSBhcnJhbmdlZCBsaWtlIHRoaXMgZmlndXJlOgoKCmBgYHtyIGVjaG8gPSBGfQpwb2ludF9saW5lX2Rpc3RhbmNlIDwtIGZ1bmN0aW9uKGIsIG0sIHgsIHkpCiAgKHkgLSAobSp4ICsgYikpL3NxcnQobV4yICsgMSkKCmNvbmZvdW5kZWRfZGF0YV9mcmFtZSA8LSBmdW5jdGlvbih4LCB5LCBtLCBudW1fZ3JwKXsKICBiIDwtIDAgIyBpbnRlcmNlcHQgZG9lc24ndCBtYXR0ZXIKICBkIDwtIHBvaW50X2xpbmVfZGlzdGFuY2UoYiwgbSwgeCwgeSkKICBkX3NjYWxlZCA8LSAwLjAwMDUgKyAwLjk5OSAqIChkIC0gbWluKGQpKS8obWF4KGQpIC0gbWluKGQpKSAjIGF2b2lkIDAgYW5kIDEKICBkYXRhLmZyYW1lKHg9eCwgeT15LCAKICAgICAgICAgICAgZ3JvdXA9YXMuZmFjdG9yKHNwcmludGYoImdycCUwMmQiLCBjZWlsaW5nKG51bV9ncnAqKGRfc2NhbGVkKSkpKSkKfQoKY29uZm91bmRlZF9kdXIgPC0gY29uZm91bmRlZF9kYXRhX2ZyYW1lKHggPSBhaF90b21vZCRsb2cyZHVyX2MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYWhfdG9tb2QkRjFfbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG0gPSAtMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2dycCA9IDEwKQoKY29uZm91bmRlZF9kdXIgJT4lCiAgZ2dwbG90KGFlcyh4LCB5LCBjb2xvciA9IGdyb3VwKSkrCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4wNSkrCiAgICBzdGF0X3Ntb290aChtZXRob2QgPSAnbG0nKSsKICAgIHhsYWIoImxvZzJkdXJfYyIpKwogICAgeWxhYigiRjFfbiIpKwogICAgdGhlbWVfbWluaW1hbCgpCgpgYGAKCgojIFRoaW5raW5nIGFib3V0IFBvb2xpbmcKCk9uZSB3YXkgdG8gc3RhcnQgdGhpbmtpbmcgYWJvdXQgbWl4ZWQgZWZmZWN0cyBtb2RlbHMgaXMgdG8gdGhpbmsgYWJvdXQgaG93IHlvdSBhcmUgInBvb2xpbmciIHRoZSBkYXRhIGZyb20geW91ciBkYXRhIHNvdXJjZXMuIEluIHRoaXMgZXhhbXBsZSwgd2UncmUgdGFsa2luZyBhYm91dCBzcGVha2VycywgYnV0IHRoaXMgY291bGQgYWxzbyBiZSBzdHVkeSBzaXRlcywgbGV4aWNhbCBpdGVtcywgZXRjLgoKCiMjIENvbXBsZXRlIFBvb2xpbmcKV2hhdCBwZW9wbGUgaGF2ZSBiZWVuIGRvaW5nIGZvciBhIGxvbmcgdGltZSBpcyAiY29tcGxldGUgcG9vbGluZy4iIEFsbCBvZiB0aGUgZGF0YSBmcm9tIGFsbCBvZiB0aGUgc291cmNlcyBhbGwganVzdCBnb2VzIGludG8gb25lIGJpZyBkYXRhIGJ1Y2tldC4gWW91IHRoZW4gZml0IGEgbW9kZWwgd2l0aCBhbGwgb2YgdGhpcyBkYXRhIGluIHRoZSBidWNrZXQsIHVuZGlmZmVyZW50aWF0ZWQgYnkgd2hvIGNvbnRyaWJ1dGVkIHdoYXQgZGF0YSBwb2ludC4KCiFbXShmaWd1cmVzL2NvbXBsZXRlX3Bvb2xpbmcuc3ZnKQoKRm9yIHRoZSByZWFzb25zIHdlJ3ZlIGRpc2N1c3NlZCBhbHJlYWR5LCB0aGlzIGlzbid0IGEgZ3JlYXQgYXBwcm9hY2guCgoxLiBJdCB2aW9sYXRlcyBzb21lIHN0YXRpc3RpY2FsIGFzc3VtcHRpb25zLgoyLiBUaGUgcGFyYW1ldGVyIGVzdGltYXRlcyBhcmUgb3Zlci1jb25maWRlbnQuCjMuIFRoZSBwYXJhbWV0ZXIgZXN0aW1hdGVzIGFyZSB0b28gc2tld2VkIHRvd2FyZHMgc3BlYWtlcnMvd29yZHMgd2l0aCBtb3JlIGRhdGEuCjQuIEl0IGlzIHBvc3NpYmxlIGZvciB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhIHRvIGdpdmUgeW91IGV4YWN0bHkgdGhlIHdyb25nIGVzdGltYXRlLgoKCiMjIE5vIFBvb2xpbmcKCk9uZSB3YXkgeW91IGNvdWxkIHRyeSB0byByZXNvbHZlIHNvbWUgb2YgdGhlc2UgcHJvYmxlbXMgd291bGQgYmUgdG8gZml0IG9uZSBtb2RlbCBmb3IgZXZlcnkgc3BlYWtlci4gSGVyZSwgeW91IGRvbid0IHBvb2wgdGhlIGRhdGEgYXQgYWxsLiBFYWNoIHNwZWFrZXIganVzdCBoYXMgdGhlaXIgb3duIGJ1Y2tldCBvZiBkYXRhLgoKIVtdKGZpZ3VyZXMvbm9fcG9vbGluZy5zdmcpCgpXZSdsbCBhY3R1YWxseSBkbyB0aGlzIGluIGEgc2Vjb25kLCBhbmQgdGhlbiBkaXNjdXNzIHRoZSBwcm9zIGFuZCBjb25zLgoKCiMjIFBhcnRpYWwgUG9vbGluZwoKVGhlIGZpbmFsIG9wdGlvbiBpcyB0byBkbyAicGFydGlhbCBwb29saW5nIi4gWW91IG1vZGVsIHdpdGggYWxsIG9mIHRoZSBkYXRhLCBidXQgcHJlc2VydmUgaW5mb3JtYXRpb24gYWJvdXQgd2hpY2ggc3BlYWtlciBjb250cmlidXRlZCB3aGljaCBkYXRhLiBUaGlzIGFwcHJvYWNoIGFsbG93cyB0aGUgIm1vZGVsIiBmb3IgZWFjaCBzcGVha2VyIHRvIG11dHVhbGx5IGluZm9ybSBlYWNob3RoZXIuIAoKCiFbXShmaWd1cmVzL3BhcnRpYWxfcG9vbGluZy5zdmcpCgpUaGlzIGlzIGEgIm1peGVkIGVmZmVjdHMgbW9kZWwiLCB3aGljaCB3ZSdsbCBhbHNvIGxvb2sgYXQuCgojIEZpdHRpbmcgbm8tcG9vbGluZyBtb2RlbHMKCkluIHRoaXMgc2VjdGlvbiwgd2UncmUgZ29pbmcgdG8gZml0IHNvbWUgbm8tcG9vbGluZyBtb2RlbHMuIEl0IGlzIG5vdCBuZWNlc3NhcnkgdG8gYmUgYWJsZSB0byBkbyB0aGlzIHRvIGJlIGFibGUgdG8gZml0IG1peGVkIGVmZmVjdHMgbW9kZWxzLCBidXQgSSB0aGluayB0aGlzIGNhbiBoZWxwIHlvdSB1bmRlcnN0YW5kIHdoYXQgbWl4ZWQgZWZmZWNzIG1vZGVscyBhcmUgZG9pbmcuCgpXaGlsZSB0aGUgbm8tcG9vbGluZyBhcHByb2FjaCB0byBtb2RlbGxpbmcgaXNuJ3QgdGhlIG1vc3Qgc29waGlzdGljYXRlZCAoYW5kIHlvdSBkZWZpbml0ZWx5IGNhbid0IHVzZSB0aGVpciBwLXZhbHVlcyksIEkgdGhpbmsgaXQgaXMgb2Z0ZW4gW2EgZ29vZCBpZGVhIHRvIHRyeSBpdCBvdXQgYXMgYW4gZXhwbG9yYXRvcnkgdGVjaG5pcXVlXShodHRwOi8vam9mcmh3bGQuZ2l0aHViLmlvL2Jsb2cvMjAxNi8wNS8wMS9tYW55X21vZGVscy5odG1sKS4gIFlvdSBjYW4gY2hlY2sgb3V0IFthIHRhbGsgYnkgSGFkbGV5IFdpY2toYW0gYWJvdXQgaXQgaGVyZV0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1yejNfRkRWdDllZykuIAoKCkl0IGlzIHByZXR0eSBlYXN5IHRvIGZpdCBvbmUgbW9kZWwgZm9yIG9uZSBzcGVha2VyLCB3ZSBqdXN0IHRha2UgYSBzdWJzZXQgb2YgdGhlIGRhdGEgYW5kIGZpdCB0aGUgbW9kZWw6CgpgYGB7cn0Kc3BlYWtlcnMgPC0gdW5pcXVlKGFoX3RvbW9kJGlkc3RyaW5nKQoKc3AxIDwtIGFoX3RvbW9kICU+JQogICAgICAgICAgZmlsdGVyKGlkc3RyaW5nID09IHNwZWFrZXJzWzFdKQpzcDFfbW9kIDwtIGxtKEYxX24gfiBsb2cyZHVyX2MsIGRhdGEgPSBzcDEpCnN1bW1hcnkoc3AxX21vZCkKYGBgCgpUaGVyZSBpcyBldmVuIGEgaGFuZHkgZGFuZHkgZnVuY3Rpb24gaW4gdGhlIGBicm9vbWAgcGFja2FnZSBjYWxsZWQgYHRpZHkoKWAgdGhhdCB3aWxsIGNvbnZlcnQgYSBsaW5lYXIgbW9kZWwgb2JqZWN0IChhbmQgbWFueSBvdGhlcnMpIGludG8gYSBkYXRhIGZyYW1lLgoKYGBge3J9CnRpZHkoc3AxX21vZCkKYGBgCgpCdXQgdGhlcmUgYXJlIGByIGxlbmd0aCh1bmlxdWUoYWhfdG9tb2QkaWRzdHJpbmcpKWAgc3BlYWtlcnMgaW4gdGhpcyBkYXRhIHNldC4KV2UgY2FuJ3QgZ28gcmVwZWF0aW5nIHRoaXMgcHJvY2VkdXJlIHRoYXQgbWFueSB0aW1lcy4gRGVmaW5pbmcgdGhlIHByb2JsZW0gdGhpcyB3YXkgc2hvdWxkIHN0YXJ0IGdpdmluZyB5b3UgdGhlIHNlbnNlIHRoYXQgd2Ugc2hvdWxkIGJlIHRha2luZyBhIFNwbGl0LUFwcGx5LUNvbWJpbmUgYXBwcm9hY2ghCgojIyBTdGVwIDEgLSBXcml0ZSBhIGZ1bmN0aW9uIHRoYXQgd2lsbCBmaXQgdGhlIG1vZGVsIHdoZW4gZ2l2ZW4gYSBkYXRhIGZyYW1lLgoKV2UgaGF2ZSB1bmZvcnR1bmF0ZWx5IG5vdCBiZWVuIGFibGUgdG8gY292ZXIgaG93IHRvIHdyaXRlIGZ1bmN0aW9ucyBpbiBSLCBidXQgaGVyZSdzIGhvdyB0aGlzIG9uZSB3aWxsIHdvcms6CgoKYGBge3J9CmZpdF9haF9tb2QgPC0gZnVuY3Rpb24oZGYpewogIGxtKEYxX24gfiBsb2cyZHVyX2MsIGRhdGEgPSBkZikKfQpgYGAKCldoYXQgYGZ1bmN0aW9uKClgIGRvZXMgaXMgY3JlYXRlIGEgbmV3IGZ1bmN0aW9uLiBJdCBzYXlzICJXaGVuIHBhc3NlZCBzb21lIGtpbmQgb2YgZGF0YSAobGV0J3MgY2FsbCBpdCBgZGZgKSwgSSdtIGdvaW5nIHRvIHRyeSB0byB1c2UgYGRmYCB0byBmaXQgYSBsaW5lYXIgbW9kZWwuIiBIZXJlJ3MgaG93IGl0IHdvcmtzIGluIGFjdGlvbjoKCmBgYHtyfQpmaXRfYWhfbW9kKHNwMSkKYGBgCgo8ZGl2IGNsYXNzID0gImJyZWFrIGJveCI+CjxzcGFuIGNsYXNzID0gJ2JpZy1sYWJlbCc+fjIgbWludXRlIGJyZWFrPC9zcGFuPgoKR28gYWhlYWQgYW5kIGNyZWF0ZSB0aGUgYGZpdF9haF9tb2RgIGZ1bmN0aW9uLCBhbmQgcnVuIGl0IG9uIHRoZSBzdWJzZXQgZm9yIG9uZSBzcGVha2VyLiBBbGwgdGhlIGNvZGUgdG8gZG8gdGhhdCBhZ2FpbiBpczoKCmBgYHtyfQpmaXRfYWhfbW9kIDwtIGZ1bmN0aW9uKGRmKXsKICBsbShGMV9uIH4gbG9nMmR1cl9jLCBkYXRhID0gZGYpCn0KYGBgCgpGb3IgZnVuIGhlcmUsIGNob29zZSBzb21lIG90aGVyIG51bWJlciBiZXR3ZWVuIDEgYW5kIGByIGxlbmd0aCh1bmlxdWUoYWhfdG9tb2QkaWRzdHJpbmcpKWAuCgpgYGB7cn0Kc3BlYWtlcnMgPC0gdW5pcXVlKGFoX3RvbW9kJGlkc3RyaW5nKQpzcF9kYXQgPC0gYWhfdG9tb2QgJT4lIGZpbHRlcihpZHN0cmluZyA9PSBzcGVha2Vyc1sxXSkKYGBgCgpgYGB7cn0KZml0X2FoX21vZChzcF9kYXQpCmBgYAoKPC9kaXY+CgoKIyMgU3RlcCAyIC0gU3BsaXQgdXAgdGhlIGRhdGEgYnkgc3BlYWtlcgoKYGZpdF9haF9tb2QoKWAgaXMgbm93IHRoZSBmdW5jdGlvbiB3ZSB3YW50IHRvICphcHBseSogdG8gc3Vic2V0cyBvZiB0aGUgZGF0YSwgYnV0IG5vdyB3ZSBuZWVkIHRvIGZpZ3VyZSBvdXQgaG93IHRvICpzcGxpdCogaXQgdXAgYWNjb3JkaW5nIHRvIHNwZWFrZXIuIFRvIHN0YXJ0IGFwcHJvYWNoaW5nIHRoYXQgcHJvYmxlbSwgd2UncmUgZ29pbmcgdG8gdXNlIHRoZSBgbmVzdCgpYCBmdW5jdGlvbi4KCjxkaXYgY2xhc3MgPSAiYm94IGJyZWFrIj4KPHNwYW4gY2xhc3MgPSAnYmlnLWxhYmVsJz5+MiBtaW51dGUgYnJlYWs8L3NwYW4+CgpSdW4gdGhpcyBjb2RlOgpgYGB7cn0KYWhfbmVzdGVkIDwtIGFoX3RvbW9kICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkc3RyaW5nKSAlPiUKICAgICAgICAgICAgICBuZXN0KCkKYWhfbmVzdGVkCmBgYAoKV2hhdCBoYXBwZW5lZD8KCjwvZGl2PgoKClRoaXMgaXMgYSBsaXR0bGUgbWluZCBiZW5kaW5nLCBidXQgaW4gYGFoX25lc3RlZGAsIHRoZXJlIGlzIG5vdyBvbmUgcm93IHBlciBzcGVha2VyIChiZWNhdXNlIHdlIHRvbGQgaXQgdG8gYGdyb3VwX2J5KClgIHNwZWFrZXIpLiBFYWNoIGNlbGwgaW4gdGhlIGBpZHN0cmluZ2AgY29sdW1uIGhhcyB0aGUgc3BlYWtlcidzIGlkLiBFYWNoIGNlbGwgaW4gdGhlIGBkYXRhYCBjb2x1bW4gaXMgYSB3aG9sZSBub3RoZXIgZGF0YSBmcmFtZS4KCmBgYHtyfQphaF9uZXN0ZWQkZGF0YVtbMV1dCmBgYAoKQW5kLCB3ZSBjYW4gYXBwbHkgb3VyIGBmaXRfYWhfbW9kKClgIGZ1bmN0aW9uIHRvIGVhY2ggb2YgdGhlc2UgZGF0YSBmcmFtZXM6CgpgYGB7cn0KZml0X2FoX21vZChhaF9uZXN0ZWQkZGF0YVtbMTBdXSkKYGBgCmBgYHtyfQpmaXRfYWhfbW9kKGFoX25lc3RlZCRkYXRhW1syMTBdXSkKYGBgCgojIyBTdGVwIDMgLSBBcHBseSB0aGUgZnVuY3Rpb24gdG8gdGhlIGRhdGEKCkJ1dCB0byBkbyB0aGlzIGluIGEgY2xlYXIgYW5kIGNvbnNpc3RlbnQgd2F5LCB3ZSBuZWVkIHRvIHVzZSBgbWFwKClgLgoKPGRpdiBjbGFzcyA9ICdib3ggYnJlYWsnPgo8c3BhbiBjbGFzcyA9ICdiaWctbGFiZWwnPn4yIG1pbnV0ZSBicmVhazwvc3Bhbj4KUnVuIHRoZSBmb2xsb3dpbmcgY29kZUwKCmBgYHtyfQphaF9uZXN0ZWQgPC0gYWhfbmVzdGVkICU+JQogICAgICAgICAgICAgICAgbXV0YXRlKG1vZGVsID0gbWFwKGRhdGEsIGZpdF9haF9tb2QpKQphaF9uZXN0ZWQKYGBgCgpXaGF0IGhhcHBlbmVkPwoKPC9kaXY+CgpJbiB0aGUgYG1vZGVsYCBjb2x1bW4sIHdoaWNoIHdlIGNyZWF0ZWQgd2l0aCBgbXV0YXRlKClgIHRoZXJlIGlzIG5vdyBhIGxpbmVhciBtb2RlbCBmb3IgZWFjaCBzcGVha2VyLgoKYGBge3J9CmFoX25lc3RlZCRtb2RlbFtbMV1dCmBgYAoKIyMgU3RlcCA0IC0gQ29tYmluaW5nICYgVGlkeWluZwoKV2Ugbm93IG5lZWQgdG8gY29udmVydCB0aGVzZSBsaW5lYXIgbW9kZWxzIGludG8gYSBtb3JlIHVzZWZ1bCBkYXRhIHN0cnVjdHVyZSBmb3IgcGxvdHRpbmcgYW5kIGFuYWx5c2lzLCBpLmUuIGEgZGF0YSBmcmFtZS4gV2UgY2FuIHR1cm4gaW5kaXZpZHVhbCBtb2RlbHMgaW50byBkYXRhIGZyYW1lcyB3aXRoIGB0aWR5KClgLgoKYGBge3J9CnRpZHkoYWhfbmVzdGVkJG1vZGVsW1sxXV0pCmBgYAoKV2UgY2FuIGFwcGx5IGB0aWR5KClgIHRvIGVhY2ggbW9kZWwgaW4gdGhlIGBtb2RlbGAgY29sdW1uIHdpdGggYG1hcCgpYCBhZ2Fpbi4KCjxkaXYgY2xhc3MgPSAiYnJlYWsgYm94Ij4KPHNwYW4gY2xhc3MgPSAnYmlnLWxhYmVsJz5+MiBtaW51dGUgYnJlYWs8L3NwYW4+ClJ1biB0aGlzIGNvZGUKCmBgYHtyfQphaF9uZXN0ZWQgPC0gYWhfbmVzdGVkICU+JQogICAgICAgICAgICAgICAgbXV0YXRlKHBhcmFtcyA9IG1hcChtb2RlbCwgdGlkeSkpCmFoX25lc3RlZApgYGAKCldoYXQgaGFwcGVuZWQ/Cgo8L2Rpdj4KCgpXZSBub3cgaGF2ZSB0aGUgbW9kZWwgZXN0aW1hdGVzIGluIGRhdGEgZnJhbWVzIGluIHRoZSBgcGFyYW1zYCBjb2x1bW4uIEdldHRpbmcgdGhlbSBpbnRvIGEgZnVsbHkgdXNhYmxlIGZvcm1hdCBpcyBub3cgYXMgZWFzeSBhcyBgdW5uZXN0KClgLgoKYGBge3J9CmFoX3BhcmFtcyA8LSBhaF9uZXN0ZWQgJT4lCiAgICAgICAgICAgICAgICB1bm5lc3QocGFyYW1zKQphaF9wYXJhbXMKYGBgCgpJIHdhbnQgdG8gbWFrZSBhIHBsb3Qgd2hlcmUgZXZlcnkgc3BlYWtlcidzIEludGVyY2VwdCB2YWx1ZSBpcyBwbG90dGVkIGFnYWluc3QgdGhlaXIgc2xvcGUgdmFsdWUsIHNvIHdlIG5lZWQgdG8gdXNlIGp1c3QgdGhlIGBpZHN0cmluZ2AsIGB0ZXJtYCBhbmQgYGVzdGltYXRlYCBjb2x1bW5zLCBhbmQgdXNlIGBzcHJlYWQoKWAKCmBgYHtyfQphaF9wYXJhbXMgJT4lCiAgc2VsZWN0KGlkc3RyaW5nLCB0ZXJtLCBlc3RpbWF0ZSklPiUKICBzcHJlYWQodGVybSwgZXN0aW1hdGUpJT4lCiAgZ2dwbG90KGFlcyhgKEludGVyY2VwdClgLCBsb2cyZHVyX2MpKSsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gMikrICAKICAgIGdlb21fcG9pbnQoKSsKICAgIHRoZW1lX21pbmltYWwoKSsKICAgIGNvb3JkX2ZpeGVkKCkKYGBgCgpIZXJlJ3MgYW5vdGhlciB2ZXJzaW9uIHdpdGggdGhlIHBvaW50cyBzY2FsZWQgYnkgc2l6ZS4KCmBgYHtyfQpzcGVha2VyX24gPC0gYWhfdG9tb2QgJT4lCiAgICAgICAgICAgICAgICBncm91cF9ieShpZHN0cmluZyklPiUKICAgICAgICAgICAgICAgIHRhbGx5KCkKCmFoX25lc3RlZCAlPiUKICB1bm5lc3QocGFyYW1zKSAlPiUKICBzZWxlY3QoaWRzdHJpbmcsIHRlcm0sIGVzdGltYXRlKSAlPiUKICBzcHJlYWQodGVybSwgZXN0aW1hdGUpICU+JQogIGxlZnRfam9pbihzcGVha2VyX24pJT4lCiAgZ2dwbG90KGFlcyhgKEludGVyY2VwdClgLCBsb2cyZHVyX2MpKSsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gMikrICAKICAgIGdlb21fcG9pbnQoYWVzKHNpemUgPSBuKSwgYWxwaGEgPSAwLjUpKwogICAgc2NhbGVfc2l6ZV9hcmVhKCkrCiAgICB0aGVtZV9taW5pbWFsKCkrCiAgICBjb29yZF9maXhlZCgpCmBgYAoKClRoaXMgbm8tcG9vbGluZyBhcHByb2FjaCBpcyBib3RoIHZlcnkgaW5mb3JtYXRpdmUsIGJ1dCBhbHNvIGhhcyBzb21lIHNlcmlvdXMgd2Vha25lc3Nlcy4KRmlyc3Qgb2YgYWxsLCB3ZSBjYW4gc2VlIHRoYXQgKm1vc3QqIHNwZWFrZXJzIGhhdmUgdGhlIGV4cGVjdGVkIHNsb3BlIChvbiB0aGUgeS1heGlzKSBiZXR3ZWVuIGR1cmF0aW9uIGFuZCBGMS4gClRoYXQgaXMsIHRoZSBzbG9wZSBpcyBwb3NpdGl2ZSwgbWVhbmluZyBhcyBkdXJhdGlvbiBpbmNyZWFzZXMsIEYxIGluY3JlYXNlcyAobG93ZXJpbmcgdGhlIHZvd2VsKS4KSG93ZXZlciwgdGhlcmUgaXMgYSBsb3QgaWYgaW50ZXItc3BlYWtlciB2YXJpYWJpbGl0eS4KU29tZSBzcGVha2VycyBhcmUgZXN0aW1hdGVkIHRvIGhhdmUgYSBmYWlybHkgc3RlZXAgc2xvcGUsIGFuZCBvdGhlcnMgaGF2ZSBzbG9wZXMgY2xvc2VyIHRvIDAgb3IgZXZlbiBuZWdhdGl2ZS4KCkhlcmUncyBhIHBsb3Qgb2YgdGhlIGFjdHVhbCBsaW5lcyBmb3IgZWFjaCBzcGVha2VyLgoKYGBge3J9CmFoX25lc3RlZCAlPiUKICB1bm5lc3QocGFyYW1zKSAlPiUKICBzZWxlY3QoaWRzdHJpbmcsIHRlcm0sIGVzdGltYXRlKSAlPiUKICBzcHJlYWQodGVybSwgZXN0aW1hdGUpICU+JQogIGdncGxvdCgpKwogICAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IGAoSW50ZXJjZXB0KWAsIAogICAgICAgICAgICAgICAgICAgIHNsb3BlID0gbG9nMmR1cl9jKSwKICAgICAgICAgICAgICAgIGFscGhhID0gMC4xKSsKICAgIHhsaW0oLTEuMiwgMi44NSkrCiAgICB4bGFiKCJsb2cyZHVyX2MiKSsKICAgIHlsaW0oLTIsNC4zKSsKICAgIHlsYWIoIkYxX24iKSsKICAgIHRoZW1lX21pbmltYWwoKQogICAgCmBgYAoKPGRpdiBjbGFzcyA9ICJib3ggYnJlYWsiPgo8c3BhbiBjbGFzcyA9ICdiaWctbGFiZWwnPkRpc2N1c3Npb248L3NwYW4+CgpXaGF0IGFyZSB0aGUgdXBzaWRlcyBhbmQgdGhlIGRvd25zaWRlcyB0byB0aGUgbm8tcG9vbGluZyBhcHByb2FjaC4KCjwvZGl2PgoKCgoKIyBQYXJ0aWFsIFBvb2xpbmcgKE1peGVkIEVmZmVjdHMpCgpUaGUgYmlnZ2VzdCBkb3duc2lkZSB0byB0aGUgbm8gcG9vbGluZyBhcHByb2FjaCBpcyB0aGF0IHRoZXJlIGlzIG5vIHNoYXJlZCBpbmZvcm1hdGlvbiAqYmV0d2VlbiogdGhlIG1vZGVscy4gSXQncyBwb3NzaWJsZSB0aGF0IHNwZWFrZXJzIHdobyBoYWQgYSBuZWdhdGl2ZSBzbG9wZSB3ZXJlIG9ubHkgZXN0aW1hdGVkIHRvIGhhdmUgYSBuZWdhdGl2ZSBzbG9wZSBiZWNhdXNlIHRoZXkgaGFkIHZlcnkgbGl0dGxlIGRhdGEuCklmIHRoZXJlIHdlcmUgc29tZSB3YXkgdG8gaW5jb3Jwb3JhdGUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHNsb3BlcyBvZiBldmVyeSBvdGhlciBzcGVha2VyIGludG8gdGhlc2Ugc3BlYWtlcnMnIHNsb3BlIGVzdGltYXRlcywgdGhleSBtaWdodCBiZSBjb25zaWRlcmFibHkgZGlmZmVyZW50LgoKVGhpcyBpcyBleGFjdGx5IHdoYXQgTWl4ZWQgRWZmZWN0cyBNb2RlbHMgZG8uIFlvdSBkZWZpbmUgImdyb3VwaW5nIHZhcmlhYmxlcyIgb3IgInBvb2xpbmcgdmFyaWFibGVzIiBvciAicmFuZG9tIGVmZmVjdHMiIGluIHRoZSBtb2RlbCB0byB0ZWxsIHRoZSBtb2RlbCB0byBlc3RpbWF0ZSBlZmZlY3RzIGZvciBlYWNoIHNwZWFrZXIsIGJ1dCBub3QgdG8gZG8gc28gY29tcGxldGVseSBpbmRlcGVuZGVudGx5IGZvciBlYWNoIHNwZWFrZXIuCgo8ZGl2IGNsYXNzID0gImJveCBicmVhayI+CjxzcGFuIGNsYXNzID0gJ2JpZy1sYWJlbCc+fjIgTWludXRlIEFjdGl2aXR5PC9zcGFuPgoKRml0IGEgY29tcGxldGUgcG9vbGluZyBtb2RlbCB3aXRoIGBsbWAgYW5kIGEgcGFydGlhbCBwb29saW5nIG1vZGVsIHdpdGggYGxtZXJgIHdpdGggdGhlIGZvbGxvd2luZyBjb2RlLgoKYGBge3J9CmFoX2xtIDwtbG0oRjFfbiB+IGxvZzJkdXJfYywgZGF0YSA9IGFoX3RvbW9kKQphaF9sbWUgPC0gbG1lcihGMV9uIH4gbG9nMmR1cl9jICsgKDEgKyBsb2cyZHVyX2MgfCBpZHN0cmluZyksIGRhdGEgPSBhaF90b21vZCkKYGBgCgo8L2Rpdj4KClRoZSBiaWcgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBjb21wbGV0ZSBwb29saW5nIGFuZCB0aGUgcGFydGlhbCBwb29saW5nIG1vZGVscyBhcmUgdGhlIGRlZmluaXRpb24gb2YgdGhlICJyYW5kb20gZWZmZWN0cyIgc3RydWN0dXJlcy4gCgo8ZGl2IGNsYXNzID0gImlsbHVzdHJhdGUiPgooPHNwYW4gY2xhc3MgPSAicG9wIj4xPC9zcGFuPiArIDxzcGFuIGNsYXNzID0gInBvcCI+bG9nMmR1cl9jPC9zcGFuPiB8IDxzcGFuIGNsYXNzID0gInBvcCI+aWRzdHJpbmc8L3NwYW4+KQo8L2Rpdj4KCi0gRXZlcnl0aGluZyB0byB0aGUgbGVmdCBvZiB0aGUgYHxgCiAgICAtIEV2ZXJ5dGhpbmcgdGhhdCBjYW4gdmFyeSB3aXRoaW4gdGhlIGdyb3VwaW5nIHZhcmlhYmxlLiAKICAgIC0gYDFgIHJlZmVycyB0byB0aGUgaW50ZXJjZXB0Ci0gRXZlcnl0aGluZyB0byB0aGUgcmlnaHQgb2YgdGhlIGB8YAogICAgLSBUaGUgZ3JvdXBpbmcgdmFyaWFibGUuCiAgICAKV2l0aCBgKDEgKyBsb2cyZHVyX2MgfCBpZHN0cmluZyApYCwgd2UncmUgc2F5aW5nCgo+IFNwZWFrZXJzIGNhbiBoYXZlIHZhcmlhYmxlIGludGVyY2VwdHMsIGFuZCB2YXJpYWJsZSBzbG9wZXMuCgpMZXQncyBsb29rIGF0IHRoZSBzdW1tYXJ5IG9mIHRoZSBtaXhlZCBlZmZlY3RzIG1vZGVsLgoKYGBge3J9CnN1bW1hcnkoYWhfbG1lKQpgYGAKCgojIyBUaGUgIlJhbmRvbSBFZmZlY3RzIgoKPGRpdiBjbGFzcyA9ICdoYWxmLWltZyc+CgohW10oZmlndXJlcy9yYW5lZi5wbmcpCjwvZGl2PgoKQnkgZGVmYXVsdCwgdGhlIHN1bW1hcnkgZG9lc24ndCBwcmludCBvdXQgYWxsIG9mIHRoZSAicmFuZG9tIGVmZmVjdHMiLCBiZWNhdXNlIGluIHRoaXMgY2FzZSB0aGVyZSBhcmUgdHdvIGZvciBlYWNoIG9mIHRoZSAyOTIgc3BlYWtlcnMgKGludGVyY2VwdCBhbmQgc2xvcGUpLiBCdXQgaXQgZG9lcyBnaXZlIHVzIGVzdGltYXRlcyBvZiB0aGUgdmFyaWFuY2Ugb2YgdGhlc2UgcmFuZG9tIGVmZmVjdHMsIGFuZCB0aGVpciBjb3JyZWxhdGlvbi4gSGVyZSwgaXQgbG9va3MgbGlrZSBzcGVha2VycyBhcmUgbW9yZSB2YXJpYWJsZSB3aXRoIHJlc3BlY3QgdG8gdGhlaXIgYmFzZWxpbmUgRjEgdGhhbiB0aGV5IGFyZSB3aXRoIHJlc3BlY3QgdG8gdGhlIGVmZmVjdCBkdXJhdGlvbiBoYXMgb24gdGhlaXIgRjEuCgoKIyMgRml4ZWQgRWZmZWN0cwoKPGRpdiBjbGFzcyA9ICdoYWxmLWltZyc+CiFbXShmaWd1cmVzL2ZpeGVmLnBuZykKPC9kaXY+CgoKVGhlc2UgYXJlIHRoZSBlc3RpbWF0ZWQgImZpeGVkIGVmZmVjdCIuIFRoZXkgY2FuIGJlIGludGVycHJldGVkIGEgbG90IGxpa2UgdGhlIGNvZWZmaWNpZW50cyBvZiB0aGUgbW9kZWxzIHdlJ3ZlIGJlZW4gZml0dGluZyBhbHJlYWR5LCBpbiB0aGUgc2Vuc2UgdGhhdCB0aGVzZSBhcmUgcGFydCBvZiB0aGUgZm9ybXVsYSB0byBkcmF3IGEgbGluZS4gQW5vdGhlciB3YXkgaXMgdG8gaW50ZXJwZXQgdGhlc2UgYXMgdGhlICphdmVyYWdlKiBpbnRlcmNlcHQgYW5kIHNsb3BlIGJldHdlZW4gc3BlYWtlcnMuCgojIyBMb29raW5nIGF0IHRoZSByYW5kb20gZWZmZWN0cwoKSXQgaXMgcG9zc2libGUgdG8gZXh0cmFjdCB0aGUgcmFuZG9tIGVmZmVjdHMgdXNpbmcgdGhlIGZ1bmN0aW9uIGByYW5lZigpYC4KCmBgYHtyfQpoZWFkKHJhbmVmKGFoX2xtZSkkaWRzdHJpbmcpCmBgYAoKVGhlc2UgYXJlIHRoZSBlc3RpbWF0ZWQgZGlmZmVyZW5jZXMgaW4gSW50ZXJjZXB0IGFuZCBzbG9wZSBmb3IgZWFjaCBzcGVha2VyIGZyb20gdGhlIGZpeGVkIGVmZmVjdHMuIFRoYXQgaXMsIGlmIHdlIGFkZGVkIHRoZSBgKEludGVyY2VwdClgIHZhbHVlIGZyb20gdGhlIGZpeGVkIGVmZmVjdHMgdG8gdGhlc2UgcmFuZG9tIGVmZmVjdHMsIHdlIHdvdWxkIGdldCB0aGUgZXN0aW1hdGVkIHNsb3BlIGZvciBlYWNoIHNwZWFrZXIuIExldCdzIGRvIHRoYXQ6CgpgYGB7cn0KYWhfbG1lX2ZpeGVmIDwtIGZpeGVmKGFoX2xtZSkKCnJhbmVmKGFoX2xtZSkkaWRzdHJpbmcgJT4lCiAgZ2dwbG90KGFlcyhgKEludGVyY2VwdClgICsgYWhfbG1lX2ZpeGVmWzFdLCAKICAgICAgICAgICAgIGxvZzJkdXJfYyArIGFoX2xtZV9maXhlZlsyXSkpKwogICAgZ2VvbV9wb2ludCgpKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAyKSsKICAgIHRoZW1lX21pbmltYWwoKSsKICAgIGNvb3JkX2ZpeGVkKCkKYGBgCgoKYGBge3J9CmFoX2xtZV9maXhlZiA8LSBmaXhlZihhaF9sbWUpCgpyYW5lZihhaF9sbWUpJGlkc3RyaW5nICU+JQogIGdncGxvdCgpKwogICAgICAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IGAoSW50ZXJjZXB0KWAgKyBhaF9sbWVfZml4ZWZbMV0sIAogICAgICAgICAgICAgICAgICAgIHNsb3BlID0gbG9nMmR1cl9jICsgYWhfbG1lX2ZpeGVmWzJdKSwKICAgICAgICAgICAgICAgIGFscGhhID0gMC4xKSsKICAgIHhsaW0oLTEuMiwgMi44NSkrCiAgICB4bGFiKCJsb2cyZHVyX2MiKSsKICAgIHlsaW0oLTIsNC4zKSsKICAgIHlsYWIoIkYxX24iKSsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCldlIGNhbiBnZXQgYW4gaWRlYSBvZiB3aGF0IHRoZSBtaXhlZC1lZmZlY3RzIG1vZGVsbGluZyBoYXMgZG9uZSBieSBjb21wYXJpbmcgdGhlc2UgcmVzdWx0cyB0byB0aGUgbm8tcG9vbGluZyBtb2RlbC4gCkZpcnN0LCB0aGUgY2x1c3RlcmluZyBvZiBpbmRpdmlkdWFsIHNwZWFrZXJzJyByYW5kb20gZWZmZWN0cyBpcyB0aWdodGVyLiAKQWxzbywgbm8gc3BlYWtlciBpcyBlc3RpYW10ZWQgdG8gaGF2ZSBhbiBpbmRpdmlkdWFsIHNsb3BlIGxlc3MgdGhhbiAwLiAKCkl0IGlzIGFsc28gcG9zc2libGUgdG8gc2VlIHRoZSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSByYW5kb20gZWZmZWN0cy4KU3BlYWtlcnMgd2hvIGhhdmUgYSBiYXNlbGluZSBsb3dlciBgYWhgIGFwcGVhciB0byBiZSBtb3JlIGFmZmVjdGVkIGJ5IGR1cmF0aW9uIGNoYW5nZXMuCgojIyBNdWx0aXBsZSByYW5kb20gZWZmZWN0cwoKVGhpcyBpcyB3aGVyZSBpdCBnZXRzIGltcG9zc2libGUgdG8gbWFrZSBpbGx1c3RyYXRpdmUgZ3JhcGhzLgpJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIGluY2x1ZGUgbXVsdGlwbGUgZ3JvdXBpbmcgZmFjdG9ycyBpbiBhIG1peGVkIGVmZmVjdHMgbW9kZWwuCgo8ZGl2IGNsYXNzID0gJ2JyZWFrIGJveCc+CjxzcGFuIGNsYXNzID0gJ2JpZy1sYWJlbCc+fjUgbWludXRlIGJyZWFrPC9zcGFuPgoKV2UncmUgZ29pbmcgdG8gYWRkIGB3b3JkYCBhcyBhIGdyb3VwaW5nIGZhY3Rvci4gRm9yIG5vdywgd2UnbGwgdHJlYXQgYHdvcmRgIGFzIG9ubHkgYmVpbmcgYWJsZSB0byBtb2R1bGF0ZSB0aGUgYmFzZWxpbmUgYEYxX25gLCB3aGljaCBtZWFucyB3ZSdsbCBhZGQgaXQgd2l0aCBgKDEgfCB3b3JkKWAgKGEuay5hLiBhIHJhbmRvbSBpbnRlcmNlcHQpLgoKR28gYWhlYWQgYW5kIGZpdCB0aGlzIG1vZGVsOgoKYGBge3J9CmFoX2xtZV9tdWx0aSA8LSBsbWVyKEYxX24gfiBsb2cyZHVyX2MgKyAKICAgICAgICAgICAgICAgICAgICAgICAoMSArIGxvZzJkdXJfYyB8IGlkc3RyaW5nKSArIAogICAgICAgICAgICAgICAgICAgICAgICgxIHwgd29yZCksIGRhdGEgPSBhaF90b21vZCkKc3VtbWFyeShhaF9sbWVfbXVsdGkpCmBgYAoKV2hhdCBoYXBwZW5lZD8KCjwvZGl2PgoKSG93ZXZlciwgaXQgaXMgKnBvc3NpYmxlKiB0byBpbmNsdWRlIGEgcmFuZG9tIHNsb3BlIGJ5IHdvcmQgdG9vLCBzbyBsZXQncyBkbyB0aGF0IGFzIHdlbGw6CgpgYGB7cn0KYWhfbG1lX211bHRpMiA8LSBhaF9sbWVfbXVsdGkgPC0gbG1lcihGMV9uIH4gbG9nMmR1cl9jICsgCiAgICAgICAgICAgICAgICAgICAgICAgKDEgKyBsb2cyZHVyX2MgfCBpZHN0cmluZykgKyAKICAgICAgICAgICAgICAgICAgICAgICAoMSArIGxvZzJkdXJfY3wgd29yZCksIGRhdGEgPSBhaF90b21vZCkKc3VtbWFyeShhaF9sbWVfbXVsdGkyKQpgYGAKCk9uZSB0aGluZyB5b3UgbWlnaHQgbm90aWNlIG5vdyBpcyB0aGF0IHRoZSAiZml4ZWQgZWZmZWN0IiBvZiBkdXJhdGlvbiBoYXMgZ290dGVuIGEgYnVuY2ggc21hbGxlci4gCgpgYGB7cn0KIyBjb21wbGV0ZSBwb29saW5nCmNvZWYoYWhfbG0pCmBgYAoKYGBge3J9CiMgZnVsbCByYW5lZiBzdHJ1Y3R1cmUKZml4ZWYoYWhfbG1lX211bHRpMikKYGBgCgpUaGlzIG1heSBsb29rIHNjYXJ5LCBlc3BlY2lhbGx5IGlmIHRoZSBlZmZlY3Qgc2l6ZSBncmFkdWFsbHkgdmFuaXNoZXMgYXdheSwgYnV0IHRoaXMgaXMgZXhhY3RseSBob3cgbWl4ZWQgZWZmZWN0cyBtb2RlbHMgYXJlIHN1cHBvc2VkIHRvIHdvcmsuIApJbiBzb21lIHNlbnNlLCBpbiB0aGUgY29tcGxldGUtcG9vbGluZyBtb2RlbCwgd2UgKnRob3VnaHQqIHdlIHdlcmUgbW9kZWxsaW5nIHRoZSBlZmZlY3Qgb2YgZHVyYXRpb24sIGJ1dCBhY3R1YWxseSBzb21lIG9mIHRoYXQgZWZmZWN0IHdhcyBhY3R1YWxseSBkdWUgdG8gdGhlIHBlY3VsaWFyaXRpZXMgb2YgdGhlIHdvcmRzIHNwb2tlbiwgYW5kIHdobyB0aGUgc3BlYWtlcnMgd2VyZS4KCgojIyBXaGVuIHRvIGFkZCByYW5kb20gaW50ZXJjZXB0cy9zbG9wZXM/CgpbQmFyciBldCBhbCAoMjAxMildKGh0dHA6Ly9pZGlvbS51Y3NkLmVkdS9+cmxldnkvcGFwZXJzL2JhcnItZXRhbC0yMDEzLWptbC5wZGYpIHJlY29tbWVuZCAoY29tbWFuZD8pIGZpdHRpbmcgbW9kZWxzICB3aXRoICJtYXhpbWFsIiByYW5kb20gZWZmZWN0cyBzdHJ1Y3R1cmVzLgpUaGF0IGlzLCBmb3IgZXZlcnkgcG9zc2libGUgZ3JvdXBpbmcgZmFjdG9yLCBpbmNsdWRlIGEgcmFuZG9tIHNsb3BlIGZvciBldmVyeSBncm91cGluZyBmYWN0b3IgdGhhdCdzIGxvZ2ljYWxseSBwb3NzaWJsZS4KSSB0aGluayB0aGlzIGlzIGEgZ29vZCBzdGFydGluZyBwb2ludCwgYnV0IGlzbid0IGFsd2F5cyBwcmFjdGljYWwuCgojIyMgTG9naWNhbGx5IFBvc3NpYmxlICYgSW1wb3NzaWJsZSBSYW5kb20gU2xvcGVzCgpJbiB0aGUgYGFoYCBtb2RlbCB3ZSBqdXN0IGZpdCwgaXQgd2FzIHBvc3NpYmxlIGZvciB1cyB0byBmaXQgYSByYW5kb20gc2xvcGUgb2YgZHVyYXRpb24gYnkgc3BlYWtlciBhbmQgd29yZCwgYmVjYXVzZSBzcGVha2VycyBwcm9kdWNlZCB0b2tlbnMgd2l0aCBkaWZmZXJlbnQgZHVyYXRpb25zLCBhbmQgd29yZHMgd2VyZSBwcm9kdWNlZCB3aXRoIGRpZmZlcmVudCBkdXJhdGlvbnMuCgpIb3dldmVyLCBpZiB3ZSB3YW50ZWQgdG8gaW5jbHVkZSBgY29udGV4dGAgYWdhaW4gKHdvcmQgaW5pdGlhbCBvciB3b3JkIGZpbmFsKSwgd2UgY291bGQgaW5jbHVkZSBhIHJhbmRvbSBzbG9wZSBvZiBjb250ZXh0IGJ5IHNwZWFrZXIsIGJ1dCAqbm90KiBieSB3b3JkLgpUaGlzIGlzbid0IGEgcHJvaGliaXRpb24sIGl0J3MganVzdCBub3QgbG9naWNhbGx5IHBvc3NpYmxlIGFuZCBtaWdodCBibG93IHVwIHlvdXIgbW9kZWwuClRoYXQncyBiZWNhdXNlIGV2ZXJ5IHdvcmQgb25seSBoYXMgb25lIHZvd2VsIGNvbnRleHQuIGBOQUhgLCBvbmx5IGhhcyBhIHdvcmQgZmluYWwgdm93ZWwgY29udGV4dCwgYW5kIHRoZXJlJ3Mgbm8gd2F5IGZvciBpdCB0byBiZSBhZmZlY3RlZCBieSBiZWluZyB3b3JkIGludGVybmFsLgoKU2ltaWxhcmx5LCBpZiB3ZSB3YW50ZWQgdG8gaW5jbHVkZSB5ZWFyIG9mIGJpcnRoIGluIHRoZSBtb2RlbCwgd2UgY291bGQgaW5jbHVkZSBhIHJhbmRvbSBzbG9wZSBvZiBkb2IgYnkgd29yZCwgYmVjYXVzZSBtYW55IHdvcmRzIHdlcmUgc3Bva2VuIGJ5IHBlb3BsZSBhY3Jvc3MgbWFueSBhZ2VzLiBCdXQgd2UgKmNvdWxkbid0KiBpbmNsdWRlIGEgcmFuZG9tIHNsb3BlIG9mIGRvYiBieSBzcGVha2VyLCBiZWNhdXNlIGVhY2ggc3BlYWtlciBvbmx5IGhhcyBvbmUgZGF0ZSBvZiBiaXJ0aC4KCgojIyMgTmVjZXNzYXJ5IFJhbmRvbSBFZmZlY3RzCgpBbnkgdGltZSB5b3UgYXJlIGxvb2tpbmcgYXQgYSBiZXR3ZWVuLWdyb3VwaW5nIGZhY3RvciB2YXJpYWJsZSwgeW91IHNob3VsZCBpbmNsdWRlIHRoZSBncm91cGluZyBmYWN0b3Igd2l0aCBhIHJhbmRvbSBpbnRlcmNlcHQuIFRoaXMgaXMgKmVzcGVjaWFsbHkqIHRydWUgZm9yIGFueSBraW5kIG9mIHJlc2VhcmNoIGRvbmUgb3V0c2lkZSBvZiBhbiBleHBlcmltZW50YWwgbGFiIGNvbnRleHQuCgotIGFnZTogYCgxIHwgc3BlYWtlcilgCi0gZ2VuZGVyOiBgKDEgfCBzcGVha2VyKWAKLSB3b3JkIGZyZXF1ZW5jeTogYCgxIHwgd29yZClgCi0gc2VudGVuY2UgZ3JhbW1hdGljYWxpdHk6IGAoMSB8IHRva2VuKWAKCk90aGVyIG5lY2Vzc2FyeSByYW5kb20gZWZmZWN0cyB3b3VsZCBiZSByZWxhdGVkIHRvIHRoZSBuYXR1cmUgb2YgdGhlIGRhdGEuIEluIHRoaXMgYGFoYCBtb2RlbCwgd2UgaGF2ZW4ndCBleHBsb3JlZCBhbnkgYmV0d2Vlbi1ncm91cGluZyBmYWN0b3JzLCBidXQgYWxsIG9mIHRoZXNlIGRhdGEgcG9pbnRzIHdlcmUgcHJvZHVjZWQgYnkgc3BlYWtlcnMsIGVtYmVkZGVkIGluIHNwZWNpZmljIHdvcmRzLCBzbyBpdCdzIGltcG9ydGFudCB0byBjYXB0dXJlIHRoYXQgaW4gdGhlIHJhbmRvbSBlZmZlY3RzLgoKIyMjIFJlY29tbWVuZGVkIFJhbmRvbSBFZmZlY3RzCgpJdCBpcyBhIGdvb2QgaWRlYSB0byBpbmNsdWRlIHlvdXIgYmlnZ2VzdCBlZmZlY3RzLCBvciBlZmZlY3RzIG9mIGdyZWF0ZXN0IGludGVyZXN0LCBhcyByYW5kb20gc2xvcGVzIGFjcm9zcyB0aGUgZ3JvdXBpbmcgZmFjdG9ycyB3aGljaCBjb250YWluIHRoZSBtb3N0IGRhdGEuCgoKIyBQaXRmYWxscyBpbiBtaXhlZCBlZmZlY3RzIG1vZGVsbGluZwoKVGhlIGJpZ2dlc3QgaXNzdWUgcGVvcGxlIGZhY2Ugd2hlbiBmaXR0aW5nIGEgbWl4ZWQgZWZmZWN0cyBtb2RlbCBpcyB0aGF0IGl0IG1heSAiZmFpbCB0byBjb252ZXJnZSIuIFRoaXMgaXMgYmVjYXVzZSB0aGVzZSBtb2RlbHMgYXJlbid0ICdzb2x2ZWQnIGluIHRoZSBzYW1lIHdheSBhcyB0aGUgcmVndWxhciBsaW5lYXIgcmVncmVzc2lvbiBpcy4gSXQgaXMgdmVyeSBpbXBvcnRhbnQgdG8gb25seSBiYXNlIHlvdXIgaW5mZXJlbmNlIG9uIGNvbnZlcmdlZCBtb2RlbHMuIFRoZXJlIGFyZSBhIGhhbmRmdWwgIG9mIHR3ZWFrcyB0aGF0IG1pZ2h0IGhlbHAgaW4geW91ciBtb2RlbHMgY29udmVyZ2luZy4KCi0gY2VudGVyIGFuZCBzY2FsZSBhbGwgY29udGludW91cyBwcmVkaWN0b3JzLgotIGlmIHBvc3NpYmxlLCBjaG9vc2UgYSBkaWZmZXJlbnQgZmFjdG9yIGxldmVsIHRoYXQgaGFzIG1vcmUgZGF0YSBhcyB5b3VyIHJlZmVyZW5jZSBsZXZlbAoKClRoZSBuZXh0IHN0ZXAgaXMgdG8gc3RhcnQgdGFraW5nIG91dCBzb21lIG9mIHRoZSByZWNvbW1lbmRlZCByYW5kb20gc2xvcGVzLCBvciB0aGUgb25lcyBtb3N0IGxpa2VseSB0byBiZSBwcm9ibGVtYXRpYy4gRm9yIGV4YW1wbGUsIGluIHRoZSBgYWhgIG1vZGVsLCB0aGUgcmFuZG9tIHNsb3BlIG9mIGR1cmF0aW9uIGJ5IHdvcmQgaXMgbGlrZWx5IHRvIGJlIHByb2JsZW1hdGljIGJlY2F1c2UgbWFueSB3b3JkcyBvbmx5IGhhdmUgb25lIGRhdGEgcG9pbnQsIG1ha2luZyBpdCBtb3JlIGNoYWxsZW5naW5nIHRvIGVzdGltYXRlIGEgcmFuZG9tIHNsb3BlIGZvciBldmVyeSB3b3JkLgoKCgoKCiMgTWl4ZWQgRWZmZWN0cyBNb2RlbHMgQ29udHJvdmVyc3k/CgoKT25lIG9mIHRoZSBiZW5lZml0cyBvZiBtaXhlZC1lZmZlY3RzIG1vZGVscyBpcyB0aGVpciBsaW1pdGVkIGFiaWxpdHkgdG8gYW1lbGlvcmF0ZSB1bm1vZGVsbGVkIHByb3BlcnRpZXMgb2YgdGhlIGdyb3VwaW5nIGZhY3RvcnMudyAKCg==