library(tidyverse)

# Install the `rethinking` package:
# devtools::install_github("rmcelreath/rethinking")
library(rethinking) 

It’s easy to get pretty far learning R and basic statistical analysis without really encountering the concept of multilevel modeling. (At least, that’s been the case for me.) This is unfortunate, because multilevel modeling can be a useful tool when data generating processes are hierarchical in nature–a situation which can be quite common in social, ecological, and political systems. This tutorial came about as part of a personal resolution to explore some of the Bayesian and multilevel modeling techniques and philosophies discussed in the book Statistical Rethinking by Richard McElreath. (Because, what better way to try to learn something than to commit to writing a tutorial about it?) As someone who is more of a social scientist by nature, I tend to be more interested in gaining intuition around concepts versus “learning all the math”, so this tutorial is written with that framing. I unfortunately can’t claim to understand all of the math–yet. ;-) The goals are as follows:

In Part 1, we’ll perform some modeling on the Minnesota public employees salaries data within a Bayesian framework, using the syntax and approach introduced by McElreath in his book Statistical Rethinking (2nd Edition). This syntax is quite different from the regression formulas you’re probably used to using in R, so it’s worth taking some time to understand the syntax and how to leverage it to give your regressions some of that exciting “Bayesian” flavor.

In Parts 2 and 3, we’ll look at how to approach this data through a multilevel modeling lens. At the risk of sounding like an infomercial, let’s just say that multilevel modeling is the coolest tool you probably never realized you needed…until now! Multilevel models are, first and foremost, an elegantly intuitive way of modeling hierarchical or “nested” data generating processes. Not only that, but multilevel models can actually be better at performing the bias-variance tradeoff that is the crux of a modeler’s woes! They are good at weighing how much information to share across different groups within a model, which gives them a built-in regularizing effect. This phenomenon is called pooling, where “each separate [group] in the model provides information that can be used to improve the estimates for all other [groups]” (McElrath, p. 405). As we go along, we’ll explore two different packages that empower different approaches to multilevel modeling:

When illustrating multilevel modeling approaches, we’ll always start with lmer models first to help get insights into the multilevel nature of the data. lmer is better for getting our feet wet, because the syntax is much more intuitive for a user who is used to R model formulas, and the models themselves are fast to run. When we’re comfortable with the basic concepts, we will take a brief foray into the Bayesian version of these models, which feature more complex syntax and are more computationally intensive to run.

Multilevel models perform an intriguing and delicate dance, estimating not only how observations vary within each group, but also how groups themselves vary across the context of all of the other groups in the population. By the end of this tutorial, you’ll hopefully start to agree that:

When it comes to regression, multilevel regression deserves to be the default approach. There are certainly contexts in which it would be better to use an old-fashioned single-level model. But the contexts in which multilevel models are superior are much more numerous. It is better to begin to build a multilevel analysis, and then realize it’s unnecessary, than to overlook it. ~ McElreath, p. 400


Fixed effects Bayesian model (for comparison)

Before we tackle multilevel models, let’s get used to the data, the syntax, and the Bayesian-ness of it all by running a very basic model. The main question we want to tackle in this model is: Are state employees’ wages systematically related to how many years they have worked for the state? For this analysis, let’s restrict our focus to the 25 most common jobs among state employees, just to keep things easier to visualize, and to make sure we have sufficient data from each job class. Here are the “top 25” jobs, along with the counts occupied by each gender.

top_25_job_classes <- hr_2019 %>%
  group_by(JOB_TITLE) %>%
  summarise(distinct_employees = length(unique(UNIQUE_ID))) %>%
  arrange(desc(distinct_employees)) %>%
  top_n(25) %>% select(JOB_TITLE)

top_25_job_classes <- top_25_job_classes$JOB_TITLE

hr_2019 %>%
  filter(JOB_TITLE %in% top_25_job_classes) %>%
ggplot(., aes(x=JOB_TITLE, fill=GENDER)) +
  geom_bar(position="stack", stat="count") +
  theme(axis.text.x = element_text(angle = 90, vjust=0.5, hjust=1))

Let’s filter the dataset to focus on these job classes only, leaving us with 16,715 observations:

hr_2019_top_25_job_classes <- hr_2019 %>%
  filter(JOB_TITLE %in% top_25_job_classes)

While we’re at it, let’s also turn job class into both a factor and an index variable. These are two representations of the same information–we’ll just use them slightly differently down the road, depending on which modeling approach we’re using:

hr_2019_top_25_job_classes$JOB_FACTOR <- relevel(as.factor(hr_2019_top_25_job_classes$JOB_TITLE), ref="State Patrol Trooper")
hr_2019_top_25_job_classes$JOB_INDEX <- as.integer(hr_2019_top_25_job_classes$JOB_FACTOR)

JOB_LABELS <- levels(hr_2019_top_25_job_classes$JOB_FACTOR)

For the sake of this particular exploration, we will not standardize or log scale the data, as this helps make coefficients and their units more directly interpretable at each step. This is something that we’d want to explore if we were doing this analysis beyond didactic reasons, but it can add an additional hurdle to interpreting model results that we’d prefer to avoid while we’re learning.

Pick priors

Before running the model, we need to pick three priors. Priors are simply a “starting theory”, informed by information outside of your target dataset, about what range of parameter values seem plausible for your modeling task. Before you get too worried about picking priors, note that these are simply a starting theory–they will ultimately be combined with the empirical evidence from the data itself before your model lands on its final, posterior theory. Don’t feel like you know enough to pick good priors? That’s not a huge problem–you can err on the side of picking “wider” priors and allow the evidence of the data to do more of the heavy lifting. (See: McElreath, p. 82 for a discussion on the thought process of picking these kinds of priors.) For this modeling task, however, a little basic research can give us some pretty decent priors:

  1. Prior for mean hourly wage: For this prior, we can use the Bureau of Labor Statistic’s data on the mean hourly wage for the Twin Cities metro area, which was ~$27 per hour in 2018. We’ll round that to $30 per hour, and set the standard deviation for this prior (chosen somewhat arbitrarily) to $10. (Because it seems likely that the mean hourly wage for an average Minnesota state employee would roughly mirror the overall mean hourly wage for the state across all employment sectors, and wouldn’t be more than +/-$10 in either direction.)

  2. Prior for the standard deviation across hourly wages: For this prior, let’s think about a reasonable range that we believe ~90% of hourly salaries are likely to lie within. On the low end, we would have minimum wage workers, who make approximately ~$10 per hour according to Minnesota law. On the high end, we can think of the types of high-paying jobs that the state tends to employ (ex: lawyers, upper-level personnel managers, IT managers, etc.). Looking at this Business Insider article as an external information source, we can get a ballpark hourly wage for these kinds of roles to help us get a sense of the upper end of the spectrum. It looks like many of these roles are somewhere in the ~$70 per hour range. So, that establishes our 90% range as $10 - 70 per hour, for a total span of $60. The 90% range represents ~3 standard deviations around the mean, so we can divide our range by 3 to yield 1 standard deviation, or $20 as our prior for the standard deviation.

  3. Prior for the average increase in hourly wages for each additional year that the individual has worked for the state: One bit of information we can use to inform this prior is the fact that the vast majority of Minnesota public employees are represented by labor bargaining units. So for this prior, we can do a little digging into the bargaining units. It looks like the MN Association of Professional Employees is the largest single bargaining unit for state employees. In their most recent wage contract, it states that employees are subject to a first- and second-year wage adjustment stipulating that “all salary ranges and rates for classes covered in this Agreement shall be increased by two and one-quarter percent (2.25%)”. Assuming a similar arrangement was in effect for prior contracts, then for an average worker making ~$30 per hour, we could expect an annual hourly wage increase of: 30 * 0.0225 = 0.675. We will use this as our prior for the beta coefficient representing the mean annual hourly compensation rate increase for each additional year worked at the state. It also seems like it would be highly unusual for employees to see an annual absolute change in their hourly compensation rate that is more extreme than 5% in either direction from the expected mean annual increase. We’ll set $1.5 as the standard deviation for this prior, which is the equivalent of assuming that most (95%) of the time, employees will experience a fluctuation in compensation rate that is between -5% and +5%. This is the prior that we know least about, so it’s okay to keep it “wide” for now and let the data persuade the model to adjust as necessary.

hr_2019 %>% 
  group_by(BARGAINING_UNIT_NAME) %>% 
  summarise(count_of_distinct_employees = length(unique(UNIQUE_ID))) %>%
  arrange(desc(count_of_distinct_employees)) %>%
  top_n(5)

We can now visualize each of these priors to get a better sense of how they are “grounding” our model:

par(mfrow=c(1,3))
curve(dnorm(x, 30, 10), from=0, to=80, main="Mean hourly wage")
curve(dcauchy(x, 0, 20), from=0, to=40, main="St dev across hourly wage")
curve(dnorm(x, 0.675, 1.5), from=-3, to=5, main="Avg annual increase in hourly wage")

Run the model with specified priors

Now we can run the Bayesian regression. You will need to have the rethinking library installed and loaded. Basic Bayesian regressions like this one can be fit using the quap() function from that package. The function expects two arguments: 1) an alist object containing the model formula, and 2) the data it should be run against. The model formula is pretty verbose; see the comments below to understand what each line is doing:

model1 <- quap(
  alist(
    COMP_RATE_STND_HOURLY ~ dnorm( mu , sigma ), # the response variable, which we believe comes from a normal distribution centered around mu with standard deviation sigma
    mu <- Intercept + b_YRS_SINCE_ORIGINAL_HIRE*YRS_SINCE_ORIGINAL_HIRE, # the 'meat' of the model--the formula that describes how the explanatory variables come together to influence the response variable 
    Intercept ~ dnorm(30, 10), # prior for mean hourly wage
    b_YRS_SINCE_ORIGINAL_HIRE ~ dnorm(0.675, 1.5), # prior for average increase in hourly wages for each additional year worked for the state
    sigma ~ dcauchy(0, 20) # prior for standard deviation across hourly wages (i.e. the remaining error after accounting for years worked for the state)
  ),
  data = hr_2019_top_25_job_classes
)

We can use the precis() function to view the model summary:

precis(model1) # view the model summary
plot(precis(model1)) # plot the coefficients and their "credible intervals" (See: McElreath, p. 54 for discussion on "credible interval" terminology)

Run the model with default priors

What if we got lazy about doing our background research and simply decided to use the priors that the rethinking package assigns by default? We can easily give that a try and see how much it affects the modeling results. Here’s a visualization of the default priors–you can see that these are pretty nonsensical. For example, the prior for the mean hourly wage makes it look like we’re we’re just as likely to have employees making negative wages as we are to see positive wages. And the prior for the standard deviation across wages makes it seem highly unusual to see wages that are +/- $4 from the mean in either direction, when we know from our Business Insider research above that the wage range should actually be quite wide between minimum-wage hourly workers to higher-level executives. Will these default priors mess up the model?

par(mfrow=c(1,3))
curve(dnorm(x, 0, 10), from=-15, to=15, main="Mean hourly wage")
curve(dcauchy(x, 0, 2), from=0, to=5, main="St dev across hourly wages")
curve(dnorm(x, 0, 10), from=-15, to=15, main="Avg annual increase in hourly wage")

To find out, we’ll fit the same model, but this time allowing it to pick the default priors instead of our carefully-selected priors above. This time, we’ll also demonstrate the use of the glimmer function, which allows you to specify the model formula using syntax that will seem more familiar to users who are used to the lm and glm packages. This amounts to running the exact same model as above, just using different (default) priors. And surprisingly, when we look at the precis() output, the results of this model–despite the nonsensical priors–are shockingly similar to the first model we ran!

model1_default_priors_params <- glimmer(COMP_RATE_STND_HOURLY ~ YRS_SINCE_ORIGINAL_HIRE, data = hr_2019_top_25_job_classes)
alist(
    COMP_RATE_STND_HOURLY ~ dnorm( mu , sigma ),
    mu <- Intercept +
        b_YRS_SINCE_ORIGINAL_HIRE*YRS_SINCE_ORIGINAL_HIRE,
    Intercept ~ dnorm(0,10),
    b_YRS_SINCE_ORIGINAL_HIRE ~ dnorm(0,10),
    sigma ~ dcauchy(0,2)
)
model1_default_priors <- quap(model1_default_priors_params$f, model1_default_priors_params$d) # pass in two arguments: the function (f) and the data (d) from the parameter list defined by glimmer above
precis(model1_default_priors)
plot(precis(model1_default_priors))

How can this be? We have a lot of data going into the model, so it’s actually the data–and not the priors–that is doing the bulk of the work here. The data itself is overwhelmingly convincing that the intercept (i.e. the mean hourly wage) is somewhere around $25. I also appears that employees’ hourly salaries increase somewhere around 25 cents for every additional year they have worked for the state. And it appears that the standard deviation across wages likely lies somewhere around $9.2.

Run the model with extreme priors

We have seen that, with enough data, our model can still prevail over nonsensical priors. Now, the question remains: can we “trick” the model into behaving badly if we give it really really bad priors? It turns out that wide, flat priors are unlikely to mess up the model very much, as the data can easily persuade these priors in the right direction. To be truly diabolical, we need to give it very narrow priors. Let’s do our best to thwart the model with the following priors:

par(mfrow=c(1,3))
curve(dnorm(x, -30, 1), from=-35, to=0, main="Mean hourly wage")
curve(dcauchy(x, 0, 0.001), from=0, to=1, main="St dev across hourly wages")
curve(dnorm(x, -5, 1), from=-15, to=15, main="Avg annual increase in hourly wage")

We run the model with these extreme priors, and…

model1_extreme_priors <- quap(
  alist(
    COMP_RATE_STND_HOURLY ~ dnorm( mu , sigma ),
    mu <- Intercept + b_YRS_SINCE_ORIGINAL_HIRE*YRS_SINCE_ORIGINAL_HIRE,
    Intercept ~ dnorm(-30, 1), # prior for mean hourly wage
    b_YRS_SINCE_ORIGINAL_HIRE ~ dnorm(-5, 1), # prior for average increase in hourly wages for each additional year worked for the state
    sigma ~ dcauchy(0, 0.001) # prior for standard deviation across hourly wages
  ),
  data = hr_2019_top_25_job_classes
)

precis(model1_extreme_priors)

plot(precis(model1_extreme_priors))

…the model still triumphs! The posterior parameter values are remarkably similar to the first two models, despite having used quite extreme priors this time around. This means that the evidence contained in the data is convincing enough to pull the posterior back to what we believe is closer to its “true” values!

Bonus challenge quest: Try to mess with the model even more by using even more extreme priors. At what point do the priors overwhelm the evidence of the data and manage to skew the model in the wrong direction?

Compare the models

We want to pay particular attention to two values here:

  1. The WAIC value, which is a more general version of the AIC metric, which estimates the out-of-sample predication error for a model. This allows us to compare across models to assess their goodness of fit. A lower WAIC represents a “better” model.

  2. The pWAIC value, which is a measure of the number of effective parameters in the model. These are “effective” parameters–not a literal count of all of the parameters. For these simple models, which all have the same basic structure, we don’t see a huge difference in the pWAIC values across models. For more complex models, for example when you’re comparing various different multilevel modeling architectures, you’ll likely notice a bigger difference between the pWAIC values for different modeling approaches.

compare(model1, model1_default_priors, model1_extreme_priors)
LS0tCnRpdGxlOiAiTU4gUHVibGljIFNhbGFyeSBEYXRhIC0gTXVsdGlsZXZlbCBNb2RlbHMiCnN1YnRpdGxlOiAiUGFydCAxOiBGaXhlZCBlZmZlY3RzIEJheWVzaWFuIG1vZGVsIgphdXRob3I6ICJBbGlzb24gTGluayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3IgbG9hZF9saWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQoKIyBJbnN0YWxsIHRoZSBgcmV0aGlua2luZ2AgcGFja2FnZToKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInJtY2VscmVhdGgvcmV0aGlua2luZyIpCmxpYnJhcnkocmV0aGlua2luZykgCmBgYAoKCkl0J3MgZWFzeSB0byBnZXQgcHJldHR5IGZhciBsZWFybmluZyBSIGFuZCBiYXNpYyBzdGF0aXN0aWNhbCBhbmFseXNpcyB3aXRob3V0IHJlYWxseSBlbmNvdW50ZXJpbmcgdGhlIGNvbmNlcHQgb2YgbXVsdGlsZXZlbCBtb2RlbGluZy4gKEF0IGxlYXN0LCB0aGF0J3MgYmVlbiB0aGUgY2FzZSBmb3IgbWUuKSAgVGhpcyBpcyB1bmZvcnR1bmF0ZSwgYmVjYXVzZSAqKm11bHRpbGV2ZWwgbW9kZWxpbmcgY2FuIGJlIGEgdXNlZnVsIHRvb2wgd2hlbiBkYXRhIGdlbmVyYXRpbmcgcHJvY2Vzc2VzIGFyZSBoaWVyYXJjaGljYWwgaW4gbmF0dXJlLS1hIHNpdHVhdGlvbiB3aGljaCBjYW4gYmUgcXVpdGUgY29tbW9uIGluIHNvY2lhbCwgZWNvbG9naWNhbCwgYW5kIHBvbGl0aWNhbCBzeXN0ZW1zKiouICBUaGlzIHR1dG9yaWFsIGNhbWUgYWJvdXQgYXMgcGFydCBvZiBhIHBlcnNvbmFsIHJlc29sdXRpb24gdG8gZXhwbG9yZSBzb21lIG9mIHRoZSBCYXllc2lhbiBhbmQgbXVsdGlsZXZlbCBtb2RlbGluZyB0ZWNobmlxdWVzIGFuZCBwaGlsb3NvcGhpZXMgZGlzY3Vzc2VkIGluIHRoZSBib29rIFtfU3RhdGlzdGljYWwgUmV0aGlua2luZ19dKGh0dHBzOi8vd3d3LnJvdXRsZWRnZS5jb20vU3RhdGlzdGljYWwtUmV0aGlua2luZy1BLUJheWVzaWFuLUNvdXJzZS13aXRoLUV4YW1wbGVzLWluLVItYW5kLVNUQU4vTWNFbHJlYXRoL3AvYm9vay85NzgwMzY3MTM5OTE5KSBieSBSaWNoYXJkIE1jRWxyZWF0aC4gIChCZWNhdXNlLCB3aGF0IGJldHRlciB3YXkgdG8gdHJ5IHRvIGxlYXJuIHNvbWV0aGluZyB0aGFuIHRvIGNvbW1pdCB0byB3cml0aW5nIGEgdHV0b3JpYWwgYWJvdXQgaXQ/KSAgQXMgc29tZW9uZSB3aG8gaXMgbW9yZSBvZiBhIHNvY2lhbCBzY2llbnRpc3QgYnkgbmF0dXJlLCBJIHRlbmQgdG8gYmUgbW9yZSBpbnRlcmVzdGVkIGluIGdhaW5pbmcgaW50dWl0aW9uIGFyb3VuZCBjb25jZXB0cyB2ZXJzdXMgImxlYXJuaW5nIGFsbCB0aGUgbWF0aCIsIHNvIHRoaXMgdHV0b3JpYWwgaXMgd3JpdHRlbiB3aXRoIHRoYXQgZnJhbWluZy4gIEkgdW5mb3J0dW5hdGVseSBjYW4ndCBjbGFpbSB0byB1bmRlcnN0YW5kIF9hbGxfIG9mIHRoZSBtYXRoLS15ZXQuICA7LSkgIFRoZSBnb2FscyBhcmUgYXMgZm9sbG93czoKCi0gR2FpbiBiYXNpYyBmYW1pbGlhcml0eSB3aXRoIEJheWVzaWFuIG1vZGVsaW5nIHByaW5jaXBsZXMKLSBMZWFybiBhYm91dCBtdWx0aWxldmVsIG1vZGVsaW5nIGFuZCB3aHkgaXQgY2FuIGJlIGEgdXNlZnVsIHRvb2wKLSBMb29rIGF0IGhvdyB0aGVzZSBjb25jZXB0cyBjYW4gYmUgYXBwbGllZCB0byBhIFtwdWJsaWMgc2FsYXJpZXMgZGF0YXNldCBvbiBNaW5uZXNvdGEgc3RhdGUgZW1wbG95ZWVzXShodHRwczovL21uLmdvdi9tbWIvdHJhbnNwYXJlbmN5LW1uL3BheXJvbGxkYXRhLmpzcCkuIChZYXkgb3BlbiBkYXRhISkKCkluICoqUGFydCAxKiosIHdlJ2xsIHBlcmZvcm0gc29tZSBtb2RlbGluZyBvbiB0aGUgTWlubmVzb3RhIHB1YmxpYyBlbXBsb3llZXMgc2FsYXJpZXMgZGF0YSB3aXRoaW4gYSBCYXllc2lhbiBmcmFtZXdvcmssIHVzaW5nIHRoZSBzeW50YXggYW5kIGFwcHJvYWNoIGludHJvZHVjZWQgYnkgTWNFbHJlYXRoIGluIGhpcyBib29rIF9TdGF0aXN0aWNhbCBSZXRoaW5raW5nXyAoMm5kIEVkaXRpb24pLiAgVGhpcyBzeW50YXggaXMgcXVpdGUgZGlmZmVyZW50IGZyb20gdGhlIHJlZ3Jlc3Npb24gZm9ybXVsYXMgeW91J3JlIHByb2JhYmx5IHVzZWQgdG8gdXNpbmcgaW4gUiwgc28gaXQncyB3b3J0aCB0YWtpbmcgc29tZSB0aW1lIHRvIHVuZGVyc3RhbmQgdGhlIHN5bnRheCBhbmQgaG93IHRvIGxldmVyYWdlIGl0IHRvIGdpdmUgeW91ciByZWdyZXNzaW9ucyBzb21lIG9mIHRoYXQgZXhjaXRpbmcgIkJheWVzaWFuIiBmbGF2b3IuCgpJbiAqKlBhcnRzIDIgYW5kIDMqKiwgd2UnbGwgbG9vayBhdCBob3cgdG8gYXBwcm9hY2ggdGhpcyBkYXRhIHRocm91Z2ggYSBtdWx0aWxldmVsIG1vZGVsaW5nIGxlbnMuICBBdCB0aGUgcmlzayBvZiBzb3VuZGluZyBsaWtlIGFuIGluZm9tZXJjaWFsLCBsZXQncyBqdXN0IHNheSB0aGF0IG11bHRpbGV2ZWwgbW9kZWxpbmcgaXMgdGhlIGNvb2xlc3QgdG9vbCB5b3UgcHJvYmFibHkgbmV2ZXIgcmVhbGl6ZWQgeW91IG5lZWRlZC4uLnVudGlsIG5vdyEgIE11bHRpbGV2ZWwgbW9kZWxzIGFyZSwgZmlyc3QgYW5kIGZvcmVtb3N0LCBhbiBlbGVnYW50bHkgaW50dWl0aXZlIHdheSBvZiBtb2RlbGluZyBoaWVyYXJjaGljYWwgb3IgIm5lc3RlZCIgZGF0YSBnZW5lcmF0aW5nIHByb2Nlc3Nlcy4gIE5vdCBvbmx5IHRoYXQsIGJ1dCBtdWx0aWxldmVsIG1vZGVscyBjYW4gYWN0dWFsbHkgYmUgYmV0dGVyIGF0IHBlcmZvcm1pbmcgdGhlIGJpYXMtdmFyaWFuY2UgdHJhZGVvZmYgdGhhdCBpcyB0aGUgY3J1eCBvZiBhIG1vZGVsZXIncyB3b2VzISAgVGhleSBhcmUgZ29vZCBhdCB3ZWlnaGluZyBob3cgbXVjaCBpbmZvcm1hdGlvbiB0byBzaGFyZSBhY3Jvc3MgZGlmZmVyZW50IGdyb3VwcyB3aXRoaW4gYSBtb2RlbCwgd2hpY2ggZ2l2ZXMgdGhlbSBhIGJ1aWx0LWluIHJlZ3VsYXJpemluZyBlZmZlY3QuICBUaGlzIHBoZW5vbWVub24gaXMgY2FsbGVkICoqcG9vbGluZyoqLCB3aGVyZSAiZWFjaCBzZXBhcmF0ZSBbZ3JvdXBdIGluIHRoZSBtb2RlbCBwcm92aWRlcyBpbmZvcm1hdGlvbiB0aGF0IGNhbiBiZSB1c2VkIHRvIGltcHJvdmUgdGhlIGVzdGltYXRlcyBmb3IgYWxsIG90aGVyIFtncm91cHNdIiAoTWNFbHJhdGgsIHAuIDQwNSkuICBBcyB3ZSBnbyBhbG9uZywgd2UnbGwgZXhwbG9yZSB0d28gZGlmZmVyZW50IHBhY2thZ2VzIHRoYXQgZW1wb3dlciBkaWZmZXJlbnQgYXBwcm9hY2hlcyB0byBtdWx0aWxldmVsIG1vZGVsaW5nOiAKCi0gKipsbWVyIChsbWU0L2xtZXJUZXN0KSoqIAogIC0gUHJvczogUG9wdWxhciBtdWx0aWxldmVsIG1vZGVsaW5nIHBhY2thZ2UgZm9yIFIuIEZvcm11bGEgc3ludGF4IHNpbWlsYXIgdG8gYGxtYC9gZ2xtYCBmdW5jdGlvbnMuIAogIC0gQ29uczogTm90IEJheWVzaWFuCi0gKipyZXRoaW5raW5nKiogCiAgLSBQcm9zOiBCYXllc2lhbi4gQWxsb3dzIHlvdSB0byBzcGVjaWZ5IHByaW9ycyBmb3IgYWxsIHRoZSB0aGluZ3MuCiAgLSBDb25zOiBDYW4gYmUgaGFyZCB0byBpbnN0YWxsLiBUYWtlcyBmb3JldmVyIHRvIGZpdC4KCldoZW4gaWxsdXN0cmF0aW5nIG11bHRpbGV2ZWwgbW9kZWxpbmcgYXBwcm9hY2hlcywgd2UnbGwgYWx3YXlzIHN0YXJ0IHdpdGggYGxtZXJgIG1vZGVscyBmaXJzdCB0byBoZWxwIGdldCBpbnNpZ2h0cyBpbnRvIHRoZSBtdWx0aWxldmVsIG5hdHVyZSBvZiB0aGUgZGF0YS4gIGBsbWVyYCBpcyBiZXR0ZXIgZm9yIGdldHRpbmcgb3VyIGZlZXQgd2V0LCBiZWNhdXNlIHRoZSBzeW50YXggaXMgbXVjaCBtb3JlIGludHVpdGl2ZSBmb3IgYSB1c2VyIHdobyBpcyB1c2VkIHRvIFIgbW9kZWwgZm9ybXVsYXMsIGFuZCB0aGUgbW9kZWxzIHRoZW1zZWx2ZXMgYXJlIGZhc3QgdG8gcnVuLiAgV2hlbiB3ZSdyZSBjb21mb3J0YWJsZSB3aXRoIHRoZSBiYXNpYyBjb25jZXB0cywgd2Ugd2lsbCB0YWtlIGEgYnJpZWYgZm9yYXkgaW50byB0aGUgQmF5ZXNpYW4gdmVyc2lvbiBvZiB0aGVzZSBtb2RlbHMsIHdoaWNoIGZlYXR1cmUgbW9yZSBjb21wbGV4IHN5bnRheCBhbmQgYXJlIG1vcmUgY29tcHV0YXRpb25hbGx5IGludGVuc2l2ZSB0byBydW4uCgpNdWx0aWxldmVsIG1vZGVscyBwZXJmb3JtIGFuIGludHJpZ3VpbmcgYW5kIGRlbGljYXRlIGRhbmNlLCBlc3RpbWF0aW5nIG5vdCBvbmx5IGhvdyBvYnNlcnZhdGlvbnMgdmFyeSBfd2l0aGluXyBlYWNoIGdyb3VwLCBidXQgYWxzbyBob3cgZ3JvdXBzIHRoZW1zZWx2ZXMgdmFyeSBfYWNyb3NzXyB0aGUgY29udGV4dCBvZiBhbGwgb2YgdGhlIG90aGVyIGdyb3VwcyBpbiB0aGUgcG9wdWxhdGlvbi4gIEJ5IHRoZSBlbmQgb2YgdGhpcyB0dXRvcmlhbCwgeW91J2xsIGhvcGVmdWxseSBzdGFydCB0byBhZ3JlZSB0aGF0OgoKPiBXaGVuIGl0IGNvbWVzIHRvIHJlZ3Jlc3Npb24sIG11bHRpbGV2ZWwgcmVncmVzc2lvbiBkZXNlcnZlcyB0byBiZSB0aGUgZGVmYXVsdCBhcHByb2FjaC4gVGhlcmUgYXJlIGNlcnRhaW5seSBjb250ZXh0cyBpbiB3aGljaCBpdCB3b3VsZCBiZSBiZXR0ZXIgdG8gdXNlIGFuIG9sZC1mYXNoaW9uZWQgc2luZ2xlLWxldmVsIG1vZGVsLiBCdXQgdGhlIGNvbnRleHRzIGluIHdoaWNoIG11bHRpbGV2ZWwgbW9kZWxzIGFyZSBzdXBlcmlvciBhcmUgbXVjaCBtb3JlIG51bWVyb3VzLiBJdCBpcyBiZXR0ZXIgdG8gYmVnaW4gdG8gYnVpbGQgYSBtdWx0aWxldmVsIGFuYWx5c2lzLCBhbmQgdGhlbiByZWFsaXplIGl0J3MgdW5uZWNlc3NhcnksIHRoYW4gdG8gb3Zlcmxvb2sgaXQuIH4gTWNFbHJlYXRoLCBwLiA0MDAKCgoqKioKCiMjIEZpeGVkIGVmZmVjdHMgQmF5ZXNpYW4gbW9kZWwgKGZvciBjb21wYXJpc29uKQoKQmVmb3JlIHdlIHRhY2tsZSBtdWx0aWxldmVsIG1vZGVscywgbGV0J3MgZ2V0IHVzZWQgdG8gdGhlIGRhdGEsIHRoZSBzeW50YXgsIGFuZCB0aGUgQmF5ZXNpYW4tbmVzcyBvZiBpdCBhbGwgYnkgcnVubmluZyBhIHZlcnkgYmFzaWMgbW9kZWwuICBUaGUgbWFpbiBxdWVzdGlvbiB3ZSB3YW50IHRvIHRhY2tsZSBpbiB0aGlzIG1vZGVsIGlzOiAqKkFyZSBzdGF0ZSBlbXBsb3llZXMnIHdhZ2VzIHN5c3RlbWF0aWNhbGx5IHJlbGF0ZWQgdG8gaG93IG1hbnkgeWVhcnMgdGhleSBoYXZlIHdvcmtlZCBmb3IgdGhlIHN0YXRlPyoqICBGb3IgdGhpcyBhbmFseXNpcywgbGV0J3MgcmVzdHJpY3Qgb3VyIGZvY3VzIHRvIHRoZSAyNSBtb3N0IGNvbW1vbiBqb2JzIGFtb25nIHN0YXRlIGVtcGxveWVlcywganVzdCB0byBrZWVwIHRoaW5ncyBlYXNpZXIgdG8gdmlzdWFsaXplLCBhbmQgdG8gbWFrZSBzdXJlIHdlIGhhdmUgc3VmZmljaWVudCBkYXRhIGZyb20gZWFjaCBqb2IgY2xhc3MuICBIZXJlIGFyZSB0aGUgInRvcCAyNSIgam9icywgYWxvbmcgd2l0aCB0aGUgY291bnRzIG9jY3VwaWVkIGJ5IGVhY2ggZ2VuZGVyLgoKYGBge3IgdG9wXzI1X2pvYnMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnRvcF8yNV9qb2JfY2xhc3NlcyA8LSBocl8yMDE5ICU+JQogIGdyb3VwX2J5KEpPQl9USVRMRSkgJT4lCiAgc3VtbWFyaXNlKGRpc3RpbmN0X2VtcGxveWVlcyA9IGxlbmd0aCh1bmlxdWUoVU5JUVVFX0lEKSkpICU+JQogIGFycmFuZ2UoZGVzYyhkaXN0aW5jdF9lbXBsb3llZXMpKSAlPiUKICB0b3BfbigyNSkgJT4lIHNlbGVjdChKT0JfVElUTEUpCgp0b3BfMjVfam9iX2NsYXNzZXMgPC0gdG9wXzI1X2pvYl9jbGFzc2VzJEpPQl9USVRMRQoKaHJfMjAxOSAlPiUKICBmaWx0ZXIoSk9CX1RJVExFICVpbiUgdG9wXzI1X2pvYl9jbGFzc2VzKSAlPiUKZ2dwbG90KC4sIGFlcyh4PUpPQl9USVRMRSwgZmlsbD1HRU5ERVIpKSArCiAgZ2VvbV9iYXIocG9zaXRpb249InN0YWNrIiwgc3RhdD0iY291bnQiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3Q9MC41LCBoanVzdD0xKSkKYGBgCgpMZXQncyBmaWx0ZXIgdGhlIGRhdGFzZXQgdG8gZm9jdXMgb24gdGhlc2Ugam9iIGNsYXNzZXMgb25seSwgbGVhdmluZyB1cyB3aXRoIDE2LDcxNSBvYnNlcnZhdGlvbnM6CgpgYGB7ciBmaWx0ZXJfdG9wMjVfam9ic30KaHJfMjAxOV90b3BfMjVfam9iX2NsYXNzZXMgPC0gaHJfMjAxOSAlPiUKICBmaWx0ZXIoSk9CX1RJVExFICVpbiUgdG9wXzI1X2pvYl9jbGFzc2VzKQpgYGAKCldoaWxlIHdlJ3JlIGF0IGl0LCBsZXQncyBhbHNvIHR1cm4gam9iIGNsYXNzIGludG8gYm90aCBhIGZhY3RvciBhbmQgYW4gaW5kZXggdmFyaWFibGUuICBUaGVzZSBhcmUgdHdvIHJlcHJlc2VudGF0aW9ucyBvZiB0aGUgc2FtZSBpbmZvcm1hdGlvbi0td2UnbGwganVzdCB1c2UgdGhlbSBzbGlnaHRseSBkaWZmZXJlbnRseSBkb3duIHRoZSByb2FkLCBkZXBlbmRpbmcgb24gd2hpY2ggbW9kZWxpbmcgYXBwcm9hY2ggd2UncmUgdXNpbmc6CgpgYGB7ciB0cmFuc2Zvcm1fam9iX2ZhY3Rvcn0KaHJfMjAxOV90b3BfMjVfam9iX2NsYXNzZXMkSk9CX0ZBQ1RPUiA8LSByZWxldmVsKGFzLmZhY3Rvcihocl8yMDE5X3RvcF8yNV9qb2JfY2xhc3NlcyRKT0JfVElUTEUpLCByZWY9IlN0YXRlIFBhdHJvbCBUcm9vcGVyIikKaHJfMjAxOV90b3BfMjVfam9iX2NsYXNzZXMkSk9CX0lOREVYIDwtIGFzLmludGVnZXIoaHJfMjAxOV90b3BfMjVfam9iX2NsYXNzZXMkSk9CX0ZBQ1RPUikKCkpPQl9MQUJFTFMgPC0gbGV2ZWxzKGhyXzIwMTlfdG9wXzI1X2pvYl9jbGFzc2VzJEpPQl9GQUNUT1IpCmBgYAoKRm9yIHRoZSBzYWtlIG9mIHRoaXMgcGFydGljdWxhciBleHBsb3JhdGlvbiwgd2UgX3dpbGwgbm90XyBzdGFuZGFyZGl6ZSBvciBsb2cgc2NhbGUgdGhlIGRhdGEsIGFzIHRoaXMgaGVscHMgbWFrZSBjb2VmZmljaWVudHMgYW5kIHRoZWlyIHVuaXRzIG1vcmUgZGlyZWN0bHkgaW50ZXJwcmV0YWJsZSBhdCBlYWNoIHN0ZXAuICBUaGlzIGlzIHNvbWV0aGluZyB0aGF0IHdlJ2Qgd2FudCB0byBleHBsb3JlIGlmIHdlIHdlcmUgZG9pbmcgdGhpcyBhbmFseXNpcyBiZXlvbmQgZGlkYWN0aWMgcmVhc29ucywgYnV0IGl0IGNhbiBhZGQgYW4gYWRkaXRpb25hbCBodXJkbGUgdG8gaW50ZXJwcmV0aW5nIG1vZGVsIHJlc3VsdHMgdGhhdCB3ZSdkIHByZWZlciB0byBhdm9pZCB3aGlsZSB3ZSdyZSBsZWFybmluZy4KCgojIyMgUGljayBwcmlvcnMKCkJlZm9yZSBydW5uaW5nIHRoZSBtb2RlbCwgd2UgbmVlZCB0byBwaWNrIHRocmVlICoqcHJpb3JzKiouICBQcmlvcnMgYXJlIHNpbXBseSBhICJzdGFydGluZyB0aGVvcnkiLCBpbmZvcm1lZCBieSBpbmZvcm1hdGlvbiBfb3V0c2lkZV8gb2YgeW91ciB0YXJnZXQgZGF0YXNldCwgYWJvdXQgd2hhdCByYW5nZSBvZiBwYXJhbWV0ZXIgdmFsdWVzIHNlZW0gcGxhdXNpYmxlIGZvciB5b3VyIG1vZGVsaW5nIHRhc2suICBCZWZvcmUgeW91IGdldCB0b28gd29ycmllZCBhYm91dCBwaWNraW5nIHByaW9ycywgbm90ZSB0aGF0IHRoZXNlIGFyZSBzaW1wbHkgYSBfc3RhcnRpbmdfIHRoZW9yeS0tdGhleSB3aWxsIHVsdGltYXRlbHkgYmUgY29tYmluZWQgd2l0aCB0aGUgZW1waXJpY2FsIGV2aWRlbmNlIGZyb20gdGhlIGRhdGEgaXRzZWxmIGJlZm9yZSB5b3VyIG1vZGVsIGxhbmRzIG9uIGl0cyBmaW5hbCwgKipwb3N0ZXJpb3IqKiB0aGVvcnkuICBEb24ndCBmZWVsIGxpa2UgeW91IGtub3cgZW5vdWdoIHRvIHBpY2sgZ29vZCBwcmlvcnM/ICBUaGF0J3Mgbm90IGEgaHVnZSBwcm9ibGVtLS15b3UgY2FuIGVyciBvbiB0aGUgc2lkZSBvZiBwaWNraW5nICJ3aWRlciIgcHJpb3JzIGFuZCBhbGxvdyB0aGUgZXZpZGVuY2Ugb2YgdGhlIGRhdGEgdG8gZG8gbW9yZSBvZiB0aGUgaGVhdnkgbGlmdGluZy4gIChTZWU6IE1jRWxyZWF0aCwgcC4gODIgZm9yIGEgZGlzY3Vzc2lvbiBvbiB0aGUgdGhvdWdodCBwcm9jZXNzIG9mIHBpY2tpbmcgdGhlc2Uga2luZHMgb2YgcHJpb3JzLikgIEZvciB0aGlzIG1vZGVsaW5nIHRhc2ssIGhvd2V2ZXIsIGEgbGl0dGxlIGJhc2ljIHJlc2VhcmNoIGNhbiBnaXZlIHVzIHNvbWUgcHJldHR5IGRlY2VudCBwcmlvcnM6CgoxLiAqKlByaW9yIGZvciBtZWFuIGhvdXJseSB3YWdlOioqIEZvciB0aGlzIHByaW9yLCB3ZSBjYW4gdXNlIHRoZSBbQnVyZWF1IG9mIExhYm9yIFN0YXRpc3RpYydzIGRhdGFdKGh0dHBzOi8vd3d3LmJscy5nb3YvcmVnaW9ucy9taWR3ZXN0L25ld3MtcmVsZWFzZS9vY2N1cGF0aW9uYWxlbXBsb3ltZW50YW5kd2FnZXNfbWlubmVhcG9saXMuaHRtKSBvbiB0aGUgbWVhbiBob3VybHkgd2FnZSBmb3IgdGhlIFR3aW4gQ2l0aWVzIG1ldHJvIGFyZWEsIHdoaWNoIHdhcyB+JDI3IHBlciBob3VyIGluIDIwMTguICBXZSdsbCByb3VuZCB0aGF0IHRvIFwkMzAgcGVyIGhvdXIsIGFuZCBzZXQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBmb3IgdGhpcyBwcmlvciAoY2hvc2VuIHNvbWV3aGF0IGFyYml0cmFyaWx5KSB0byBcJDEwLiAgKEJlY2F1c2UgaXQgc2VlbXMgbGlrZWx5IHRoYXQgdGhlIG1lYW4gaG91cmx5IHdhZ2UgZm9yIGFuIGF2ZXJhZ2UgTWlubmVzb3RhIHN0YXRlIGVtcGxveWVlIHdvdWxkIHJvdWdobHkgbWlycm9yIHRoZSBvdmVyYWxsIG1lYW4gaG91cmx5IHdhZ2UgZm9yIHRoZSBzdGF0ZSBhY3Jvc3MgYWxsIGVtcGxveW1lbnQgc2VjdG9ycywgYW5kIHdvdWxkbid0IGJlIG1vcmUgdGhhbiArLy1cJDEwIGluIGVpdGhlciBkaXJlY3Rpb24uKQoKMi4gKipQcmlvciBmb3IgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBhY3Jvc3MgaG91cmx5IHdhZ2VzOioqIEZvciB0aGlzIHByaW9yLCBsZXQncyB0aGluayBhYm91dCBhIHJlYXNvbmFibGUgcmFuZ2UgdGhhdCB3ZSBiZWxpZXZlIH45MCUgb2YgaG91cmx5IHNhbGFyaWVzIGFyZSBsaWtlbHkgdG8gbGllIHdpdGhpbi4gIE9uIHRoZSBsb3cgZW5kLCB3ZSB3b3VsZCBoYXZlIG1pbmltdW0gd2FnZSB3b3JrZXJzLCB3aG8gbWFrZSBhcHByb3hpbWF0ZWx5IH5cJDEwIHBlciBob3VyIFthY2NvcmRpbmcgdG8gTWlubmVzb3RhIGxhd10oaHR0cHM6Ly93d3cuZGxpLm1uLmdvdi9idXNpbmVzcy9lbXBsb3ltZW50LXByYWN0aWNlcy9taW5pbXVtLXdhZ2UtbWlubmVzb3RhKS4gIE9uIHRoZSBoaWdoIGVuZCwgd2UgY2FuIHRoaW5rIG9mIHRoZSB0eXBlcyBvZiBoaWdoLXBheWluZyBqb2JzIHRoYXQgdGhlIHN0YXRlIHRlbmRzIHRvIGVtcGxveSAoZXg6IGxhd3llcnMsIHVwcGVyLWxldmVsIHBlcnNvbm5lbCBtYW5hZ2VycywgSVQgbWFuYWdlcnMsIGV0Yy4pLiAgTG9va2luZyBhdCBbdGhpcyBCdXNpbmVzcyBJbnNpZGVyIGFydGljbGVdKGh0dHBzOi8vd3d3LmJ1c2luZXNzaW5zaWRlci5jb20vaG91cmx5LXNhbGFyaWVzLXN1cmdlb25zLWxhd3llcnMtMjAxNi05KSBhcyBhbiBleHRlcm5hbCBpbmZvcm1hdGlvbiBzb3VyY2UsIHdlIGNhbiBnZXQgYSBiYWxscGFyayBob3VybHkgd2FnZSBmb3IgdGhlc2Uga2luZHMgb2Ygcm9sZXMgdG8gaGVscCB1cyBnZXQgYSBzZW5zZSBvZiB0aGUgdXBwZXIgZW5kIG9mIHRoZSBzcGVjdHJ1bS4gIEl0IGxvb2tzIGxpa2UgbWFueSBvZiB0aGVzZSByb2xlcyBhcmUgc29tZXdoZXJlIGluIHRoZSB+XCQ3MCBwZXIgaG91ciByYW5nZS4gU28sIHRoYXQgZXN0YWJsaXNoZXMgb3VyIDkwJSByYW5nZSBhcyBcJDEwIC0gNzAgcGVyIGhvdXIsIGZvciBhIHRvdGFsIHNwYW4gb2YgXCQ2MC4gIFRoZSA5MCUgcmFuZ2UgcmVwcmVzZW50cyB+MyBzdGFuZGFyZCBkZXZpYXRpb25zIGFyb3VuZCB0aGUgbWVhbiwgc28gd2UgY2FuIGRpdmlkZSBvdXIgcmFuZ2UgYnkgMyB0byB5aWVsZCAxIHN0YW5kYXJkIGRldmlhdGlvbiwgb3IgJDIwIGFzIG91ciBwcmlvciBmb3IgdGhlIHN0YW5kYXJkIGRldmlhdGlvbi4KCjMuICoqUHJpb3IgZm9yIHRoZSBhdmVyYWdlIGluY3JlYXNlIGluIGhvdXJseSB3YWdlcyBmb3IgZWFjaCBhZGRpdGlvbmFsIHllYXIgdGhhdCB0aGUgaW5kaXZpZHVhbCBoYXMgd29ya2VkIGZvciB0aGUgc3RhdGU6KiogIE9uZSBiaXQgb2YgaW5mb3JtYXRpb24gd2UgY2FuIHVzZSB0byBpbmZvcm0gdGhpcyBwcmlvciBpcyB0aGUgZmFjdCB0aGF0IHRoZSB2YXN0IG1ham9yaXR5IG9mIE1pbm5lc290YSBwdWJsaWMgZW1wbG95ZWVzIGFyZSByZXByZXNlbnRlZCBieSBsYWJvciBiYXJnYWluaW5nIHVuaXRzLiAgU28gZm9yIHRoaXMgcHJpb3IsIHdlIGNhbiBkbyBhIGxpdHRsZSBkaWdnaW5nIGludG8gdGhlIGJhcmdhaW5pbmcgdW5pdHMuICBJdCBsb29rcyBsaWtlIHRoZSBNTiBBc3NvY2lhdGlvbiBvZiBQcm9mZXNzaW9uYWwgRW1wbG95ZWVzIGlzIHRoZSBsYXJnZXN0IHNpbmdsZSBiYXJnYWluaW5nIHVuaXQgZm9yIHN0YXRlIGVtcGxveWVlcy4gIEluIFt0aGVpciBtb3N0IHJlY2VudCB3YWdlIGNvbnRyYWN0XShodHRwczovL21hcGUub3JnL21hcGVzLWNvbnRyYWN0LXdvcmtpbmcvYXJ0aWNsZS0yNC13YWdlcyksIGl0IHN0YXRlcyB0aGF0IGVtcGxveWVlcyBhcmUgc3ViamVjdCB0byBhIGZpcnN0LSBhbmQgc2Vjb25kLXllYXIgd2FnZSBhZGp1c3RtZW50IHN0aXB1bGF0aW5nIHRoYXQgImFsbCBzYWxhcnkgcmFuZ2VzIGFuZCByYXRlcyBmb3IgY2xhc3NlcyBjb3ZlcmVkIGluIHRoaXMgQWdyZWVtZW50IHNoYWxsIGJlIGluY3JlYXNlZCBieSB0d28gYW5kIG9uZS1xdWFydGVyIHBlcmNlbnQgKDIuMjUlKSIuICBBc3N1bWluZyBhIHNpbWlsYXIgYXJyYW5nZW1lbnQgd2FzIGluIGVmZmVjdCBmb3IgcHJpb3IgY29udHJhY3RzLCB0aGVuIGZvciBhbiBhdmVyYWdlIHdvcmtlciBtYWtpbmcgflwkMzAgcGVyIGhvdXIsIHdlIGNvdWxkIGV4cGVjdCBhbiBhbm51YWwgaG91cmx5IHdhZ2UgaW5jcmVhc2Ugb2Y6IGAzMCAqIDAuMDIyNSA9YCBgciAzMCAqIDAuMDIyNWAuICBXZSB3aWxsIHVzZSB0aGlzIGFzIG91ciBwcmlvciBmb3IgdGhlIGJldGEgY29lZmZpY2llbnQgcmVwcmVzZW50aW5nIHRoZSBtZWFuIGFubnVhbCBob3VybHkgY29tcGVuc2F0aW9uIHJhdGUgaW5jcmVhc2UgZm9yIGVhY2ggYWRkaXRpb25hbCB5ZWFyIHdvcmtlZCBhdCB0aGUgc3RhdGUuICBJdCBhbHNvIHNlZW1zIGxpa2UgaXQgd291bGQgYmUgaGlnaGx5IHVudXN1YWwgZm9yIGVtcGxveWVlcyB0byBzZWUgYW4gYW5udWFsIGFic29sdXRlIGNoYW5nZSBpbiB0aGVpciBob3VybHkgY29tcGVuc2F0aW9uIHJhdGUgdGhhdCBpcyBtb3JlIGV4dHJlbWUgdGhhbiA1JSBpbiBlaXRoZXIgZGlyZWN0aW9uIGZyb20gdGhlIGV4cGVjdGVkIG1lYW4gYW5udWFsIGluY3JlYXNlLiAgV2UnbGwgc2V0IFwkMS41IGFzIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gZm9yIHRoaXMgcHJpb3IsIHdoaWNoIGlzIHRoZSBlcXVpdmFsZW50IG9mIGFzc3VtaW5nIHRoYXQgbW9zdCAoOTUlKSBvZiB0aGUgdGltZSwgZW1wbG95ZWVzIHdpbGwgZXhwZXJpZW5jZSBhIGZsdWN0dWF0aW9uIGluIGNvbXBlbnNhdGlvbiByYXRlIHRoYXQgaXMgYmV0d2VlbiAtNSUgYW5kICs1JS4gIFRoaXMgaXMgdGhlIHByaW9yIHRoYXQgd2Uga25vdyBsZWFzdCBhYm91dCwgc28gaXQncyBva2F5IHRvIGtlZXAgaXQgIndpZGUiIGZvciBub3cgYW5kIGxldCB0aGUgZGF0YSBwZXJzdWFkZSB0aGUgbW9kZWwgdG8gYWRqdXN0IGFzIG5lY2Vzc2FyeS4KCmBgYHtyIHZpZXdfYmFyZ2FpbmluZ191bml0LCBldmFsID0gRkFMU0V9CmhyXzIwMTkgJT4lIAogIGdyb3VwX2J5KEJBUkdBSU5JTkdfVU5JVF9OQU1FKSAlPiUgCiAgc3VtbWFyaXNlKGNvdW50X29mX2Rpc3RpbmN0X2VtcGxveWVlcyA9IGxlbmd0aCh1bmlxdWUoVU5JUVVFX0lEKSkpICU+JQogIGFycmFuZ2UoZGVzYyhjb3VudF9vZl9kaXN0aW5jdF9lbXBsb3llZXMpKSAlPiUKICB0b3Bfbig1KQpgYGAKCldlIGNhbiBub3cgdmlzdWFsaXplIGVhY2ggb2YgdGhlc2UgcHJpb3JzIHRvIGdldCBhIGJldHRlciBzZW5zZSBvZiBob3cgdGhleSBhcmUgImdyb3VuZGluZyIgb3VyIG1vZGVsOgoKYGBge3Igdmlld19wcmlvcnMsIHdhcm5pbmdzPUZBTFNFfQpwYXIobWZyb3c9YygxLDMpKQpjdXJ2ZShkbm9ybSh4LCAzMCwgMTApLCBmcm9tPTAsIHRvPTgwLCBtYWluPSJNZWFuIGhvdXJseSB3YWdlIikKY3VydmUoZGNhdWNoeSh4LCAwLCAyMCksIGZyb209MCwgdG89NDAsIG1haW49IlN0IGRldiBhY3Jvc3MgaG91cmx5IHdhZ2UiKQpjdXJ2ZShkbm9ybSh4LCAwLjY3NSwgMS41KSwgZnJvbT0tMywgdG89NSwgbWFpbj0iQXZnIGFubnVhbCBpbmNyZWFzZSBpbiBob3VybHkgd2FnZSIpCmBgYAoKCiMjIyBSdW4gdGhlIG1vZGVsIHdpdGggc3BlY2lmaWVkIHByaW9ycwoKTm93IHdlIGNhbiBydW4gdGhlIEJheWVzaWFuIHJlZ3Jlc3Npb24uICBZb3Ugd2lsbCBuZWVkIHRvIGhhdmUgdGhlIGByZXRoaW5raW5nYCBsaWJyYXJ5IGluc3RhbGxlZCBhbmQgbG9hZGVkLiAgQmFzaWMgQmF5ZXNpYW4gcmVncmVzc2lvbnMgbGlrZSB0aGlzIG9uZSBjYW4gYmUgZml0IHVzaW5nIHRoZSBgcXVhcCgpYCBmdW5jdGlvbiBmcm9tIHRoYXQgcGFja2FnZS4gIFRoZSBmdW5jdGlvbiBleHBlY3RzIHR3byBhcmd1bWVudHM6IDEpIGFuIGBhbGlzdGAgb2JqZWN0IGNvbnRhaW5pbmcgdGhlIG1vZGVsIGZvcm11bGEsIGFuZCAyKSB0aGUgZGF0YSBpdCBzaG91bGQgYmUgcnVuIGFnYWluc3QuICBUaGUgbW9kZWwgZm9ybXVsYSBpcyBwcmV0dHkgdmVyYm9zZTsgc2VlIHRoZSBjb21tZW50cyBiZWxvdyB0byB1bmRlcnN0YW5kIHdoYXQgZWFjaCBsaW5lIGlzIGRvaW5nOgoKYGBge3IgcnVuX2JheWVzX21vZGVsfQptb2RlbDEgPC0gcXVhcCgKICBhbGlzdCgKICAgIENPTVBfUkFURV9TVE5EX0hPVVJMWSB+IGRub3JtKCBtdSAsIHNpZ21hICksICMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLCB3aGljaCB3ZSBiZWxpZXZlIGNvbWVzIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uIGNlbnRlcmVkIGFyb3VuZCBtdSB3aXRoIHN0YW5kYXJkIGRldmlhdGlvbiBzaWdtYQogICAgbXUgPC0gSW50ZXJjZXB0ICsgYl9ZUlNfU0lOQ0VfT1JJR0lOQUxfSElSRSpZUlNfU0lOQ0VfT1JJR0lOQUxfSElSRSwgIyB0aGUgJ21lYXQnIG9mIHRoZSBtb2RlbC0tdGhlIGZvcm11bGEgdGhhdCBkZXNjcmliZXMgaG93IHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMgY29tZSB0b2dldGhlciB0byBpbmZsdWVuY2UgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIAogICAgSW50ZXJjZXB0IH4gZG5vcm0oMzAsIDEwKSwgIyBwcmlvciBmb3IgbWVhbiBob3VybHkgd2FnZQogICAgYl9ZUlNfU0lOQ0VfT1JJR0lOQUxfSElSRSB+IGRub3JtKDAuNjc1LCAxLjUpLCAjIHByaW9yIGZvciBhdmVyYWdlIGluY3JlYXNlIGluIGhvdXJseSB3YWdlcyBmb3IgZWFjaCBhZGRpdGlvbmFsIHllYXIgd29ya2VkIGZvciB0aGUgc3RhdGUKICAgIHNpZ21hIH4gZGNhdWNoeSgwLCAyMCkgIyBwcmlvciBmb3Igc3RhbmRhcmQgZGV2aWF0aW9uIGFjcm9zcyBob3VybHkgd2FnZXMgKGkuZS4gdGhlIHJlbWFpbmluZyBlcnJvciBhZnRlciBhY2NvdW50aW5nIGZvciB5ZWFycyB3b3JrZWQgZm9yIHRoZSBzdGF0ZSkKICApLAogIGRhdGEgPSBocl8yMDE5X3RvcF8yNV9qb2JfY2xhc3NlcwopCmBgYAoKV2UgY2FuIHVzZSB0aGUgYHByZWNpcygpYCBmdW5jdGlvbiB0byB2aWV3IHRoZSBtb2RlbCBzdW1tYXJ5OgoKYGBge3J9CnByZWNpcyhtb2RlbDEpICMgdmlldyB0aGUgbW9kZWwgc3VtbWFyeQpwbG90KHByZWNpcyhtb2RlbDEpKSAjIHBsb3QgdGhlIGNvZWZmaWNpZW50cyBhbmQgdGhlaXIgImNyZWRpYmxlIGludGVydmFscyIgKFNlZTogTWNFbHJlYXRoLCBwLiA1NCBmb3IgZGlzY3Vzc2lvbiBvbiAiY3JlZGlibGUgaW50ZXJ2YWwiIHRlcm1pbm9sb2d5KQpgYGAKCgojIyMgUnVuIHRoZSBtb2RlbCB3aXRoIGRlZmF1bHQgcHJpb3JzCgpXaGF0IGlmIHdlIGdvdCBsYXp5IGFib3V0IGRvaW5nIG91ciBiYWNrZ3JvdW5kIHJlc2VhcmNoIGFuZCBzaW1wbHkgZGVjaWRlZCB0byB1c2UgdGhlIHByaW9ycyB0aGF0IHRoZSBgcmV0aGlua2luZ2AgcGFja2FnZSBhc3NpZ25zIGJ5IGRlZmF1bHQ/ICBXZSBjYW4gZWFzaWx5IGdpdmUgdGhhdCBhIHRyeSBhbmQgc2VlIGhvdyBtdWNoIGl0IGFmZmVjdHMgdGhlIG1vZGVsaW5nIHJlc3VsdHMuICBIZXJlJ3MgYSB2aXN1YWxpemF0aW9uIG9mIHRoZSBkZWZhdWx0IHByaW9ycy0teW91IGNhbiBzZWUgdGhhdCB0aGVzZSBhcmUgcHJldHR5IG5vbnNlbnNpY2FsLiAgRm9yIGV4YW1wbGUsIHRoZSBwcmlvciBmb3IgdGhlIG1lYW4gaG91cmx5IHdhZ2UgbWFrZXMgaXQgbG9vayBsaWtlIHdlJ3JlIHdlJ3JlIGp1c3QgYXMgbGlrZWx5IHRvIGhhdmUgZW1wbG95ZWVzIG1ha2luZyBfbmVnYXRpdmVfIHdhZ2VzIGFzIHdlIGFyZSB0byBzZWUgcG9zaXRpdmUgd2FnZXMuICBBbmQgdGhlIHByaW9yIGZvciB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIGFjcm9zcyB3YWdlcyBtYWtlcyBpdCBzZWVtIGhpZ2hseSB1bnVzdWFsIHRvIHNlZSB3YWdlcyB0aGF0IGFyZSArLy0gXCQ0IGZyb20gdGhlIG1lYW4gaW4gZWl0aGVyIGRpcmVjdGlvbiwgd2hlbiB3ZSBrbm93IGZyb20gb3VyIEJ1c2luZXNzIEluc2lkZXIgcmVzZWFyY2ggYWJvdmUgdGhhdCB0aGUgd2FnZSByYW5nZSBzaG91bGQgYWN0dWFsbHkgYmUgcXVpdGUgd2lkZSBiZXR3ZWVuIG1pbmltdW0td2FnZSBob3VybHkgd29ya2VycyB0byBoaWdoZXItbGV2ZWwgZXhlY3V0aXZlcy4gIFdpbGwgdGhlc2UgZGVmYXVsdCBwcmlvcnMgbWVzcyB1cCB0aGUgbW9kZWw/CgpgYGB7ciBzaG93X2RlZmF1bHRfcHJpb3JzLCB3YXJuaW5ncz1GQUxTRX0KcGFyKG1mcm93PWMoMSwzKSkKY3VydmUoZG5vcm0oeCwgMCwgMTApLCBmcm9tPS0xNSwgdG89MTUsIG1haW49Ik1lYW4gaG91cmx5IHdhZ2UiKQpjdXJ2ZShkY2F1Y2h5KHgsIDAsIDIpLCBmcm9tPTAsIHRvPTUsIG1haW49IlN0IGRldiBhY3Jvc3MgaG91cmx5IHdhZ2VzIikKY3VydmUoZG5vcm0oeCwgMCwgMTApLCBmcm9tPS0xNSwgdG89MTUsIG1haW49IkF2ZyBhbm51YWwgaW5jcmVhc2UgaW4gaG91cmx5IHdhZ2UiKQpgYGAKClRvIGZpbmQgb3V0LCB3ZSdsbCBmaXQgdGhlIHNhbWUgbW9kZWwsIGJ1dCB0aGlzIHRpbWUgYWxsb3dpbmcgaXQgdG8gcGljayB0aGUgZGVmYXVsdCBwcmlvcnMgaW5zdGVhZCBvZiBvdXIgY2FyZWZ1bGx5LXNlbGVjdGVkIHByaW9ycyBhYm92ZS4gIFRoaXMgdGltZSwgd2UnbGwgYWxzbyBkZW1vbnN0cmF0ZSB0aGUgdXNlIG9mIHRoZSBgZ2xpbW1lcmAgZnVuY3Rpb24sIHdoaWNoIGFsbG93cyB5b3UgdG8gc3BlY2lmeSB0aGUgbW9kZWwgZm9ybXVsYSB1c2luZyBzeW50YXggdGhhdCB3aWxsIHNlZW0gbW9yZSBmYW1pbGlhciB0byB1c2VycyB3aG8gYXJlIHVzZWQgdG8gdGhlIGBsbWAgYW5kIGBnbG1gIHBhY2thZ2VzLiAgVGhpcyBhbW91bnRzIHRvIHJ1bm5pbmcgdGhlIGV4YWN0IHNhbWUgbW9kZWwgYXMgYWJvdmUsIGp1c3QgdXNpbmcgZGlmZmVyZW50IChkZWZhdWx0KSBwcmlvcnMuICBBbmQgc3VycHJpc2luZ2x5LCB3aGVuIHdlIGxvb2sgYXQgdGhlIGBwcmVjaXMoKWAgb3V0cHV0LCB0aGUgcmVzdWx0cyBvZiB0aGlzIG1vZGVsLS1kZXNwaXRlIHRoZSBub25zZW5zaWNhbCBwcmlvcnMtLWFyZSBzaG9ja2luZ2x5IHNpbWlsYXIgdG8gdGhlIGZpcnN0IG1vZGVsIHdlIHJhbiEKCmBgYHtyIHJ1bl9iYXllc19tb2RlbF9kZWZhdWx0fQptb2RlbDFfZGVmYXVsdF9wcmlvcnNfcGFyYW1zIDwtIGdsaW1tZXIoQ09NUF9SQVRFX1NUTkRfSE9VUkxZIH4gWVJTX1NJTkNFX09SSUdJTkFMX0hJUkUsIGRhdGEgPSBocl8yMDE5X3RvcF8yNV9qb2JfY2xhc3NlcykKCm1vZGVsMV9kZWZhdWx0X3ByaW9ycyA8LSBxdWFwKG1vZGVsMV9kZWZhdWx0X3ByaW9yc19wYXJhbXMkZiwgbW9kZWwxX2RlZmF1bHRfcHJpb3JzX3BhcmFtcyRkKSAjIHBhc3MgaW4gdHdvIGFyZ3VtZW50czogdGhlIGZ1bmN0aW9uIChmKSBhbmQgdGhlIGRhdGEgKGQpIGZyb20gdGhlIHBhcmFtZXRlciBsaXN0IGRlZmluZWQgYnkgZ2xpbW1lciBhYm92ZQpgYGAKCmBgYHtyfQpwcmVjaXMobW9kZWwxX2RlZmF1bHRfcHJpb3JzKQpwbG90KHByZWNpcyhtb2RlbDFfZGVmYXVsdF9wcmlvcnMpKQpgYGAKCkhvdyBjYW4gdGhpcyBiZT8gIFdlIGhhdmUgX2EgbG90XyBvZiBkYXRhIGdvaW5nIGludG8gdGhlIG1vZGVsLCBzbyBpdCdzIGFjdHVhbGx5IHRoZSBkYXRhLS1hbmQgX25vdF8gdGhlIHByaW9ycy0tdGhhdCBpcyBkb2luZyB0aGUgYnVsayBvZiB0aGUgd29yayBoZXJlLiAgVGhlIGRhdGEgaXRzZWxmIGlzIG92ZXJ3aGVsbWluZ2x5IGNvbnZpbmNpbmcgdGhhdCB0aGUgaW50ZXJjZXB0IChpLmUuIHRoZSBtZWFuIGhvdXJseSB3YWdlKSBpcyBzb21ld2hlcmUgYXJvdW5kIFwkMjUuICBJIGFsc28gYXBwZWFycyB0aGF0IGVtcGxveWVlcycgaG91cmx5IHNhbGFyaWVzIGluY3JlYXNlIHNvbWV3aGVyZSBhcm91bmQgMjUgY2VudHMgZm9yIGV2ZXJ5IGFkZGl0aW9uYWwgeWVhciB0aGV5IGhhdmUgd29ya2VkIGZvciB0aGUgc3RhdGUuICBBbmQgaXQgYXBwZWFycyB0aGF0IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gYWNyb3NzIHdhZ2VzIGxpa2VseSBsaWVzIHNvbWV3aGVyZSBhcm91bmQgXCQ5LjIuCgoKIyMjIFJ1biB0aGUgbW9kZWwgd2l0aCBleHRyZW1lIHByaW9ycwoKV2UgaGF2ZSBzZWVuIHRoYXQsIHdpdGggZW5vdWdoIGRhdGEsIG91ciBtb2RlbCBjYW4gc3RpbGwgcHJldmFpbCBvdmVyIG5vbnNlbnNpY2FsIHByaW9ycy4gIE5vdywgdGhlIHF1ZXN0aW9uIHJlbWFpbnM6IGNhbiB3ZSAidHJpY2siIHRoZSBtb2RlbCBpbnRvIGJlaGF2aW5nIGJhZGx5IGlmIHdlIGdpdmUgaXQgX3JlYWxseSByZWFsbHkgYmFkXyBwcmlvcnM/ICBJdCB0dXJucyBvdXQgdGhhdCB3aWRlLCBmbGF0IHByaW9ycyBhcmUgdW5saWtlbHkgdG8gbWVzcyB1cCB0aGUgbW9kZWwgdmVyeSBtdWNoLCBhcyB0aGUgZGF0YSBjYW4gZWFzaWx5IHBlcnN1YWRlIHRoZXNlIHByaW9ycyBpbiB0aGUgcmlnaHQgZGlyZWN0aW9uLiAgVG8gYmUgX3RydWx5XyBkaWFib2xpY2FsLCB3ZSBuZWVkIHRvIGdpdmUgaXQgdmVyeSBuYXJyb3cgcHJpb3JzLiAgTGV0J3MgZG8gb3VyIGJlc3QgdG8gdGh3YXJ0IHRoZSBtb2RlbCB3aXRoIHRoZSBmb2xsb3dpbmcgcHJpb3JzOgoKYGBge3Igc2hvd19leHRyZW1lX3ByaW9ycywgd2FybmluZ3M9RkFMU0V9CnBhcihtZnJvdz1jKDEsMykpCmN1cnZlKGRub3JtKHgsIC0zMCwgMSksIGZyb209LTM1LCB0bz0wLCBtYWluPSJNZWFuIGhvdXJseSB3YWdlIikKY3VydmUoZGNhdWNoeSh4LCAwLCAwLjAwMSksIGZyb209MCwgdG89MSwgbWFpbj0iU3QgZGV2IGFjcm9zcyBob3VybHkgd2FnZXMiKQpjdXJ2ZShkbm9ybSh4LCAtNSwgMSksIGZyb209LTE1LCB0bz0xNSwgbWFpbj0iQXZnIGFubnVhbCBpbmNyZWFzZSBpbiBob3VybHkgd2FnZSIpCmBgYApXZSBydW4gdGhlIG1vZGVsIHdpdGggdGhlc2UgZXh0cmVtZSBwcmlvcnMsIGFuZC4uLgoKYGBge3IgcnVuX2JheWVzX21vZGVsX2V4dHJlbWV9Cm1vZGVsMV9leHRyZW1lX3ByaW9ycyA8LSBxdWFwKAogIGFsaXN0KAogICAgQ09NUF9SQVRFX1NUTkRfSE9VUkxZIH4gZG5vcm0oIG11ICwgc2lnbWEgKSwKICAgIG11IDwtIEludGVyY2VwdCArIGJfWVJTX1NJTkNFX09SSUdJTkFMX0hJUkUqWVJTX1NJTkNFX09SSUdJTkFMX0hJUkUsCiAgICBJbnRlcmNlcHQgfiBkbm9ybSgtMzAsIDEpLCAjIHByaW9yIGZvciBtZWFuIGhvdXJseSB3YWdlCiAgICBiX1lSU19TSU5DRV9PUklHSU5BTF9ISVJFIH4gZG5vcm0oLTUsIDEpLCAjIHByaW9yIGZvciBhdmVyYWdlIGluY3JlYXNlIGluIGhvdXJseSB3YWdlcyBmb3IgZWFjaCBhZGRpdGlvbmFsIHllYXIgd29ya2VkIGZvciB0aGUgc3RhdGUKICAgIHNpZ21hIH4gZGNhdWNoeSgwLCAwLjAwMSkgIyBwcmlvciBmb3Igc3RhbmRhcmQgZGV2aWF0aW9uIGFjcm9zcyBob3VybHkgd2FnZXMKICApLAogIGRhdGEgPSBocl8yMDE5X3RvcF8yNV9qb2JfY2xhc3NlcwopCgpwcmVjaXMobW9kZWwxX2V4dHJlbWVfcHJpb3JzKQoKcGxvdChwcmVjaXMobW9kZWwxX2V4dHJlbWVfcHJpb3JzKSkKYGBgCgouLi50aGUgbW9kZWwgc3RpbGwgdHJpdW1waHMhICBUaGUgcG9zdGVyaW9yIHBhcmFtZXRlciB2YWx1ZXMgYXJlIHJlbWFya2FibHkgc2ltaWxhciB0byB0aGUgZmlyc3QgdHdvIG1vZGVscywgZGVzcGl0ZSBoYXZpbmcgdXNlZCBxdWl0ZSBleHRyZW1lIHByaW9ycyB0aGlzIHRpbWUgYXJvdW5kLiAgVGhpcyBtZWFucyB0aGF0IHRoZSBldmlkZW5jZSBjb250YWluZWQgaW4gdGhlIGRhdGEgaXMgY29udmluY2luZyBlbm91Z2ggdG8gcHVsbCB0aGUgcG9zdGVyaW9yIGJhY2sgdG8gd2hhdCB3ZSBiZWxpZXZlIGlzIGNsb3NlciB0byBpdHMgInRydWUiIHZhbHVlcyEKCioqQm9udXMgY2hhbGxlbmdlIHF1ZXN0OioqIFRyeSB0byBtZXNzIHdpdGggdGhlIG1vZGVsIGV2ZW4gbW9yZSBieSB1c2luZyBldmVuIF9tb3JlXyBleHRyZW1lIHByaW9ycy4gIEF0IHdoYXQgcG9pbnQgZG8gdGhlIHByaW9ycyBvdmVyd2hlbG0gdGhlIGV2aWRlbmNlIG9mIHRoZSBkYXRhIGFuZCBtYW5hZ2UgdG8gc2tldyB0aGUgbW9kZWwgaW4gdGhlIHdyb25nIGRpcmVjdGlvbj8KCgojIyBDb21wYXJlIHRoZSBtb2RlbHMKCldlIHdhbnQgdG8gcGF5IHBhcnRpY3VsYXIgYXR0ZW50aW9uIHRvIHR3byB2YWx1ZXMgaGVyZTogCgoxLiBUaGUgKipXQUlDIHZhbHVlKiosIHdoaWNoIGlzIGEgbW9yZSBnZW5lcmFsIHZlcnNpb24gb2YgdGhlIEFJQyBtZXRyaWMsIHdoaWNoIGVzdGltYXRlcyB0aGUgb3V0LW9mLXNhbXBsZSBwcmVkaWNhdGlvbiBlcnJvciBmb3IgYSBtb2RlbC4gIFRoaXMgYWxsb3dzIHVzIHRvIGNvbXBhcmUgYWNyb3NzIG1vZGVscyB0byBhc3Nlc3MgdGhlaXIgZ29vZG5lc3Mgb2YgZml0LiAgQSBsb3dlciBXQUlDIHJlcHJlc2VudHMgYSAiYmV0dGVyIiBtb2RlbC4gCgoyLiBUaGUgKipwV0FJQyB2YWx1ZSoqLCB3aGljaCBpcyBhIG1lYXN1cmUgb2YgdGhlIG51bWJlciBvZiBlZmZlY3RpdmUgcGFyYW1ldGVycyBpbiB0aGUgbW9kZWwuICBUaGVzZSBhcmUgImVmZmVjdGl2ZSIgcGFyYW1ldGVycy0tbm90IGEgbGl0ZXJhbCBjb3VudCBvZiBhbGwgb2YgdGhlIHBhcmFtZXRlcnMuICBGb3IgdGhlc2Ugc2ltcGxlIG1vZGVscywgd2hpY2ggYWxsIGhhdmUgdGhlIHNhbWUgYmFzaWMgc3RydWN0dXJlLCB3ZSBkb24ndCBzZWUgYSBodWdlIGRpZmZlcmVuY2UgaW4gdGhlIHBXQUlDIHZhbHVlcyBhY3Jvc3MgbW9kZWxzLiAgRm9yIG1vcmUgY29tcGxleCBtb2RlbHMsIGZvciBleGFtcGxlIHdoZW4geW91J3JlIGNvbXBhcmluZyB2YXJpb3VzIGRpZmZlcmVudCBtdWx0aWxldmVsIG1vZGVsaW5nIGFyY2hpdGVjdHVyZXMsIHlvdSdsbCBsaWtlbHkgbm90aWNlIGEgYmlnZ2VyIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcFdBSUMgdmFsdWVzIGZvciBkaWZmZXJlbnQgbW9kZWxpbmcgYXBwcm9hY2hlcy4gIAoKYGBge3J9CmNvbXBhcmUobW9kZWwxLCBtb2RlbDFfZGVmYXVsdF9wcmlvcnMsIG1vZGVsMV9leHRyZW1lX3ByaW9ycykKYGBgCgo=