library(alr4)         # for data
library(tidyverse)    # for plotting and summarizing
library(ggridges)     # for ridge plots
library(ggmosaic)     # for mosaic plots
library(moderndive)   #for nice model output
library(broom)        # for nice model output 
library(infer)        # for making inferences about models
library(equatiomatic) # for nice model equations
theme_set(theme_minimal()) #changes the theme of ggplots to theme_minimal, my personal favorite

GOAL:

By the end of these notes and activities, you should be able to perform the following tasks.

  • Correctly set up a hypothesis test.
  • Simulate the distribution of an estimated model coefficient when the null hypothesis is true.
  • Compute the p-value using the simulated distribution.
  • Use model output to make conclusions about a hypothesis test.

Remember that our goal of building models is usually to model the real world. In the end, we would like to know if our model is right. Answering that question is nearly impossible since we don’t know the real world model … hence the reason we are building this model in the first place.

Instead, we will will set out to make a decision between two competing theories. In our models, the competing theories are usually: 1. \(\beta_i=0\) vs. 2. \(\beta_i \ne 0\). Why are these interesting?

Hypothesis testing framework

  1. Define your two competing hypotheses - the null and alternative hypotheses.
  2. Set up a hypothetical world where the null hypothesis is true. This world is understood because we decide what it is. Specifically, we are interested in model coefficients, so we will create distributions of estimated coefficients we would expect to see when the null hypothesis is true.
  3. Then we compare our actual data to data we would expect to see in this hypothetical world when the null hypothesis is true. In this step we calculate a test statistic and a p-value. These give us concrete ways to compare what we have observed in our actual data to what we would expect to see in the hypothetical world when the null hypothsis is true.
  4. Make a decision. If the actual data and data we’d expect to see in the hypothetical world don’t match, then there is reason to doubt that the data are from this hypothetical world. (Later we’ll see that we either reject or don’t reject our null hypothesis)

Example

We are interested in knowing if there is a relationship between Age and lengthcm of smallmouth bass. From the plot of the sample data, it seems like that would be the case.

wblake2 <- wblake %>% 
  mutate(lengthcm = Length/10)

wblake2 %>% 
  ggplot(aes(x=lengthcm, y=Age)) +
  geom_jitter()

fish_age_simple <- lm(Age ~ lengthcm, data=wblake2)
tidy(fish_age_simple)

I am going to set up a hypothetical world where there is no relationship between Age and lengthcm. For now, don’t worry about the code. I’ll explain that later. But, notice that if I (or you) run this over and over again, I get pictures that seem to show no relationship between Age and lengthcm.

wblake2 %>% 
  mutate(new_Age = sample(Age)) %>% 
  ggplot(aes(x=lengthcm, y=new_Age)) +
  geom_jitter()

Now, I am going to fit models to the data in this hypothetical world and plot them (the blue lines). Don’t worry about the code for now … we’ll discuss it later. The red line is the actual line fit to the sample of data. What do you notice?

set.seed(1119)

hypothetical_samples <-
  wblake2 %>% 
  rep_sample_n(size = 439, 
               replace = FALSE, 
               reps = 200) %>% 
  group_by(replicate) %>% 
  mutate(new_Age = sample(Age)) %>% 
  ungroup()

hypothetical_samples %>% 
  ggplot(aes(x = lengthcm, y = new_Age, group = replicate)) +
  geom_smooth(method = "lm", se = FALSE, color = "lightblue") +
  geom_abline(intercept = -0.9930344, slope = 0.2692521, color = "darkred") +
  ylim(0,8)

The plots below show more detail for nine of the lines.

hypothetical_samples %>% 
  filter(replicate<10) %>% 
  ggplot(aes(x = lengthcm, y = new_Age)) +
  geom_jitter(size = .5, alpha = .5) +
  geom_smooth(method = "lm", se = FALSE, color = "lightblue") +
  geom_abline(intercept = -0.9930344, slope = 0.2692521, color = "darkred") +
  facet_wrap(~replicate)
## `geom_smooth()` using formula 'y ~ x'

And we can examine the distribution of the slopes from the models fit to the data in this hypothetical world. The vertical line is the slope from the model fit to the original sample of data (from fish_age_simple).

This histogram/distribution simulates the sampling distribution of the slope in the hypothetical world, under the assumption that there is no relationship between the two variables.

hypothetical_samples %>%
  group_by(replicate) %>%     
  summarize(lm(new_Age ~ lengthcm) %>% tidy()) %>% 
  ungroup() %>% 
  filter(term == "lengthcm") %>% 
  ggplot(aes(x=estimate)) +
  geom_histogram(bins=30, fill = "lightblue") +
  geom_vline(xintercept = 0.2692521, color = "darkred")
## `summarise()` has grouped output by 'replicate'. You can override using the `.groups` argument.

YOUR TURN!

  • What is an observation in the histogram above?
  • What does this tell us about the slope from the sample data compared to what we’d expect to see in the hypothetical world where there is no relationship between Age and lengthcm?

Logic and Language of Hypothesis Tests.

In the example above, it may seem weird that we decided to test the hypothesis that there is not a relationship between Age and lengthcm. Shouldn’t we hypothesize that there is a relationship, since that is what we believe to be true? It turns out the answer is no. Why? Logic!

Short YOUR TURN! tangent

Before jumping into hypothesis test logic, I think it is worthwhile to do a simpler logic example. First, let’s assume the statement “All statistics classes are fun” is true (it is, right?). I can re-write this statement as an if-else statement: “If a class is a statistics class, then it is fun.”

How could you finish the following statements so that they are true?

  1. If a class is fun, then ….
  2. If a class is NOT fun, then ….
  1. We assume that IF the hypothesis is true, THEN our statistic of interest (the slope, in the example above) follows some known distribution (like the sampling distribution we simulated which I plotted again below), ie. it’s a random draw from this distribution.
## `summarise()` has grouped output by 'replicate'. You can override using the `.groups` argument.

  1. Compare the observed value of the statistic (the slope from the sample data) to the known distribution. There are two possible outcomes:
  • Agreement: the observed statistic is a plausible outcome from the distribution of the statistic
  • Disagreement: the observed statistic is not a plausible outcome from the distribution of the statistic
  1. Draw a conclusion (see contrapositive)
  • If the outcome is agreement, what can you logically conclude? In other words, if the statistic of interest follows some known distribution (ie. is a random draw from the hypothetical distribution), what can you conclude about the hypothesis? Specifically, is it logical to conclude that the hypothesis is true?
  • If the outcome is disagreement, what can you logically conclude? In other words, if the statistic of interest does not follow some known distribution (ie. is not a random draw from the hypothetical distribution), what can you conclude about the hypothesis?

















Thus, it is much more satisfying to be in disagreement with the hypothesis because then we can reject it! The hypothesis is really set up for us to obtain evidence to disagree with it. Since its role is to be disagreed with or “nullified”, it is given the name null hypothesis, or \(H_0\).

Criteria for Null hypotheses:

  1. Choose one that is interesting to reject since that is the only interesting conclusion we can make. This means they almost always take the form of no effect, like our example where there was no relationship or the slope was zero.
  2. The hypothesis needs to be specific in order to construct the distribution.

Example revisited

How did I set up a hypothetical world where there is no relationship between Age and lengthcm?

If there is no relationship between Age and lengthcm, then no matter the length, the plausible ages should be the same. Another way of saying this is that the distribution of ages should be the same no matter the length. That distribution of ages comes from our actual data.

Let’s more closely investigate the code that generated the “hypothetical world”. Discuss the code below.

set.seed(1119)

hypothetical_samples <-
  wblake2 %>% #original sample of data
  rep_sample_n(size = 439, #take samples of this size from the original sample - why this?
               replace = FALSE, #without replacement
               reps = 200) %>% #this many replicates of the process
  group_by(replicate) %>% #for each replicate
  mutate(new_Age = sample(Age)) %>% #shuffle/permute the Ages
  ungroup() #ungroup the data

So, we end up with 200 samples of data where there IS NO relationship between Age (now called new_Age) and lengthcm - that’s the world where the NULL HYPOTHESIS is true!.

Then, for each sample, we fit the model new_Age ~ lengthcm and keep track of the estimated coefficient for lengthcm. Lastly, we make a histogram the estimated coefficients. So, if the null hypothesis is true, this is what the sampling distribution of slopes would look like.

hypothetical_samples %>% #200 samples of data from "hypothetical world" where there is no relationship
  group_by(replicate) %>% #for each replicate/sample
  summarize(lm(new_Age ~ lengthcm) %>% tidy()) %>% #fit this model
  ungroup() %>% #ungroup the data
  filter(term == "lengthcm") %>% #filter to only this term
  ggplot(aes(x=estimate)) + #make a histogram of the estimated coefficients
  geom_histogram(bins=15, fill = "lightblue") 
## `summarise()` has grouped output by 'replicate'. You can override using the `.groups` argument.

Does the slope from our sample data seem to agree or disagree with the null hypothesis? What do we conclude?

hypothetical_samples %>%
  group_by(replicate) %>%     
  summarize(lm(new_Age ~ lengthcm) %>% tidy()) %>% 
  ungroup() %>% 
  filter(term == "lengthcm") %>% 
  ggplot(aes(x=estimate)) +
  geom_histogram(bins=30, fill = "lightblue") +
  geom_vline(xintercept = 0.2692521, color = "darkred")
## `summarise()` has grouped output by 'replicate'. You can override using the `.groups` argument.

P-values

In the discussion about agreement and disagreement above, I didn’t mention any detail around how you might decide if the observed statistic is a plausible outcome from the distribution of the statistic. What counts as plausible? And how do we calculate it?

The p-value is the fraction of the distribution of the statistics when the null hypothesis is true that are more extreme (less likely) than the observed statistic from the sample. Conventionally an observation is considered implausible when the p-value is less than .05.

How would you calculate the p-value using the simulated data?

  1. We need to understand that “more extreme” means further into the tails of the distribution. So, in our example, that would mean further to the right of where the observed value lies. It also means further to the left of the negative of the observed value because that is equally as extreme. In other problems, we could have a negative observed slope so “more extreme” would be to the left of the observed value and to the right of the positive of the observed value. You need to take time to think about this.

  2. Create a column/variable that indicates if the estimated slope is more extreme than the observed value. I do that below, creating a variable called more_extreme_than_actual. What values does this variable take?

hypothetical_samples %>%
  group_by(replicate) %>%     
  summarize(lm(new_Age ~ lengthcm) %>% tidy()) %>% 
  ungroup() %>% 
  filter(term == "lengthcm") %>% 
  mutate(more_extreme_than_actual = estimate > 0.2692521)
## `summarise()` has grouped output by 'replicate'. You can override using the `.groups` argument.
  1. Compute the p-value by finding the proportion or fraction of the distribution of the statistics when the null hypothesis is true (the estimated coeffficients) that are more extreme (less likely) than the observed statistic from the sample. In the code below, inside the summarize() function, I compute the p-value in two different ways. You DO NOT need to do both as they are doing the exact same thing. The more_extreme_than_actual variable takes TRUE/FALSE values but those are treated as 1/0. So, taking the mean is giving the proportion that are more extreme. We double it (multiply by 2) to account for the equivalent extreme cases in the other direction.
hypothetical_samples %>%
  group_by(replicate) %>%     
  summarize(lm(new_Age ~ lengthcm) %>% tidy()) %>% 
  ungroup() %>% 
  filter(term == "lengthcm") %>% 
  mutate(more_extreme_than_actual = estimate > 0.2692521) %>% 
  summarize(p_val = 2*sum(more_extreme_than_actual)/n(),
            p_val2 = 2*mean(more_extreme_than_actual))
## `summarise()` has grouped output by 'replicate'. You can override using the `.groups` argument.

Using theory/R to find p-values

It might seem like a lot of work to conduct a hypothesis test. So far, I’ve made it seem like we have to set up the distribution of the coefficient when the null hypothesis is true every time we want to conduct a test. Thankfully, we don’t have to do this. Just like we didn’t need to use bootstrapping to find confidence intervals, statistical theory (and R) saves us!

R uses something called a test statistic to do the probability calculations.

What are test statistics?

They are a special kind of statistic that follows a known distribution (like a Normal distribution or its close cousin, the t-distribution). They are a function of the statistic we are interested in, for coefficients in models they are equal to the estimated coefficient divided by its standard error (at least when the \(H_0\) is that the true coefficient is zero).

When the null hypothesis is true, the test statistic follows a t-distribution, which for large sample sizes is extremely close to a normal distribution that is centered at 0 and has a standard deviation of 1.

Finding the probability that the test statistic is more extreme than the observed test statistic when the null hypothesis is true is equivalent to finding the probability that the slope is more extreme than the observed slope when the null hypothesis is true.

Let’s look at the regression table.

tidy(fish_age_simple)

The column called statistic is the test statistic. We can draw a picture of the observed test statistic in relation to what we would expect when the null hypothesis is true. The column called p.value is the p-value.

Note that this is doing what is called a two-sided test. This means that values are considered extreme on both sides of the distribution. So, even though we had a value that was far to the right of the distribution, values equally as far to the left would also be considered in this calculation. This has to do with the assumed alternative hypothesis, which I’ll talk about next.

The alternative hypothesis

In practice, scientists usually also have an alternative hypothesis, which is the hypothesis they actually hope is true. This is done for a few reasons:

  1. To motivate the study.

  2. To compute sample sizes necessary to achieve a certain power of a test (we won’t discuss that in this class, although it’s very important in experimental studies).

Notice that having an alternative might be nice for motivation but it isn’t really involved in the conclusion of the hypothesis test since our only choices are either to reject the null hypothesis or not to reject the null hypothesis.

When we make hypothesis tests, we write them like

\[ H_0: \text{parameter of interest = value} \\ H_a: \text{parameter of interest} \ne \text{value} \]

For our fish example, we would write

\[ H_0: \beta_1 = 0 \\ H_a: \beta_1 \ne 0 \]

(or we could put a specific value in the alternative hypothesis that we think \(\beta_1\) might be equal to)

Then, we use the data to make our conclusion. We observe that \(\hat{\beta}_1 = 0.27\) and find the corresponding p-value to make our conclusion.

Summary of the process

  1. State \(H_0\) and \(H_a\).
  2. Select the threshold for a small enough p-value at which you will reject \(H_0\). This is called \(\alpha\) and is commonly set at .05.
  3. Calculate a test statistic and corresponding p-value. This can be done using theory (R output) or simulation.
  4. Make a decision - reject or don’t reject \(H_0\).
  5. Put your decision in the context of the data!

YOUR TURN!

Often, we have more than one variable in our model. For example with the fish data, we may also use the size of their scale to predict their age. What are the hypotheses being tested for each of the coefficients (not the intercept) in the output below? How would you use simulation to compute the p-value? Do you get a similar result to when you use the regression table directly?

fish_mod2 <- lm(Age ~ lengthcm + Scale,
                data=wblake2)
tidy(fish_mod2)

The data below were collected from ratemyprofessor.com. See more by typing Rateprof into the help search. What is the hypothesis? What does the p-value for the gendermale term tell you? What would you conclude?

lm.rateprof.pep <- lm(quality ~ gender, 
                  data=Rateprof)
tidy(lm.rateprof.pep)

BTW, most students in an introductory stats course would learn how to do this using a “Two Sample T-Test”. So, you know how to do one. You just use linear models to do it.

LS0tCnRpdGxlOiAiSHlwb3RoZXNpcyBUZXN0cyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoYWxyNCkgICAgICAgICAjIGZvciBkYXRhCmxpYnJhcnkodGlkeXZlcnNlKSAgICAjIGZvciBwbG90dGluZyBhbmQgc3VtbWFyaXppbmcKbGlicmFyeShnZ3JpZGdlcykgICAgICMgZm9yIHJpZGdlIHBsb3RzCmxpYnJhcnkoZ2dtb3NhaWMpICAgICAjIGZvciBtb3NhaWMgcGxvdHMKbGlicmFyeShtb2Rlcm5kaXZlKSAgICNmb3IgbmljZSBtb2RlbCBvdXRwdXQKbGlicmFyeShicm9vbSkgICAgICAgICMgZm9yIG5pY2UgbW9kZWwgb3V0cHV0IApsaWJyYXJ5KGluZmVyKSAgICAgICAgIyBmb3IgbWFraW5nIGluZmVyZW5jZXMgYWJvdXQgbW9kZWxzCmxpYnJhcnkoZXF1YXRpb21hdGljKSAjIGZvciBuaWNlIG1vZGVsIGVxdWF0aW9ucwp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKSAjY2hhbmdlcyB0aGUgdGhlbWUgb2YgZ2dwbG90cyB0byB0aGVtZV9taW5pbWFsLCBteSBwZXJzb25hbCBmYXZvcml0ZQpgYGAKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LXN1Y2Nlc3MiPgogIDxzdHJvbmc+R09BTDo8L3N0cm9uZz4KCkJ5IHRoZSBlbmQgb2YgdGhlc2Ugbm90ZXMgYW5kIGFjdGl2aXRpZXMsIHlvdSBzaG91bGQgYmUgYWJsZSB0byBwZXJmb3JtIHRoZSBmb2xsb3dpbmcgdGFza3MuCgoqIENvcnJlY3RseSBzZXQgdXAgYSBoeXBvdGhlc2lzIHRlc3QuICAKKiBTaW11bGF0ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGFuIGVzdGltYXRlZCBtb2RlbCBjb2VmZmljaWVudCB3aGVuIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZS4gIAoqIENvbXB1dGUgdGhlIHAtdmFsdWUgdXNpbmcgdGhlIHNpbXVsYXRlZCBkaXN0cmlidXRpb24uICAKKiBVc2UgbW9kZWwgb3V0cHV0IHRvIG1ha2UgY29uY2x1c2lvbnMgYWJvdXQgYSBoeXBvdGhlc2lzIHRlc3QuICAKCjwvZGl2PgoKCgpSZW1lbWJlciB0aGF0IG91ciBnb2FsIG9mIGJ1aWxkaW5nIG1vZGVscyBpcyB1c3VhbGx5IHRvIG1vZGVsIHRoZSByZWFsIHdvcmxkLiBJbiB0aGUgZW5kLCB3ZSB3b3VsZCBsaWtlIHRvIGtub3cgaWYgb3VyIG1vZGVsIGlzIHJpZ2h0LiBBbnN3ZXJpbmcgdGhhdCBxdWVzdGlvbiBpcyBuZWFybHkgaW1wb3NzaWJsZSBzaW5jZSB3ZSBkb24ndCBrbm93IHRoZSByZWFsIHdvcmxkIG1vZGVsIC4uLiBoZW5jZSB0aGUgcmVhc29uIHdlIGFyZSBidWlsZGluZyB0aGlzIG1vZGVsIGluIHRoZSBmaXJzdCBwbGFjZS4gCgpJbnN0ZWFkLCB3ZSB3aWxsIHdpbGwgc2V0IG91dCB0byBtYWtlIGEgZGVjaXNpb24gYmV0d2VlbiB0d28gY29tcGV0aW5nIHRoZW9yaWVzLiBJbiBvdXIgbW9kZWxzLCB0aGUgY29tcGV0aW5nIHRoZW9yaWVzIGFyZSB1c3VhbGx5OiAxLiAkXGJldGFfaT0wJCB2cy4gMi4gJFxiZXRhX2kgXG5lIDAkLiAqKldoeSBhcmUgdGhlc2UgaW50ZXJlc3Rpbmc/KioKCiMgSHlwb3RoZXNpcyB0ZXN0aW5nIGZyYW1ld29yawoKMS4gRGVmaW5lIHlvdXIgdHdvIGNvbXBldGluZyBoeXBvdGhlc2VzIC0gdGhlIG51bGwgYW5kIGFsdGVybmF0aXZlIGh5cG90aGVzZXMuICAKMi4gU2V0IHVwIGEgaHlwb3RoZXRpY2FsIHdvcmxkIHdoZXJlIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZS4gVGhpcyB3b3JsZCBpcyB1bmRlcnN0b29kIGJlY2F1c2Ugd2UgZGVjaWRlIHdoYXQgaXQgaXMuIFNwZWNpZmljYWxseSwgd2UgYXJlIGludGVyZXN0ZWQgaW4gbW9kZWwgY29lZmZpY2llbnRzLCBzbyB3ZSB3aWxsIGNyZWF0ZSBkaXN0cmlidXRpb25zIG9mIGVzdGltYXRlZCBjb2VmZmljaWVudHMgd2Ugd291bGQgZXhwZWN0IHRvIHNlZSB3aGVuIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZS4gIAozLiBUaGVuIHdlIGNvbXBhcmUgb3VyIGFjdHVhbCBkYXRhIHRvIGRhdGEgd2Ugd291bGQgZXhwZWN0IHRvIHNlZSBpbiB0aGlzIGh5cG90aGV0aWNhbCB3b3JsZCB3aGVuIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZS4gSW4gdGhpcyBzdGVwIHdlIGNhbGN1bGF0ZSBhIHRlc3Qgc3RhdGlzdGljIGFuZCBhIHAtdmFsdWUuIFRoZXNlIGdpdmUgdXMgY29uY3JldGUgd2F5cyB0byBjb21wYXJlIHdoYXQgd2UgaGF2ZSBvYnNlcnZlZCBpbiBvdXIgYWN0dWFsIGRhdGEgdG8gd2hhdCB3ZSB3b3VsZCBleHBlY3QgdG8gc2VlIGluIHRoZSBoeXBvdGhldGljYWwgd29ybGQgd2hlbiB0aGUgbnVsbCBoeXBvdGhzaXMgaXMgdHJ1ZS4KNC4gTWFrZSBhIGRlY2lzaW9uLiBJZiB0aGUgYWN0dWFsIGRhdGEgYW5kIGRhdGEgd2UnZCBleHBlY3QgdG8gc2VlIGluIHRoZSBoeXBvdGhldGljYWwgd29ybGQgZG9uJ3QgbWF0Y2gsIHRoZW4gdGhlcmUgaXMgcmVhc29uIHRvIGRvdWJ0IHRoYXQgdGhlIGRhdGEgYXJlIGZyb20gdGhpcyBoeXBvdGhldGljYWwgd29ybGQuIChMYXRlciB3ZSdsbCBzZWUgdGhhdCB3ZSBlaXRoZXIgcmVqZWN0IG9yIGRvbid0IHJlamVjdCBvdXIgbnVsbCBoeXBvdGhlc2lzKQoKIyBFeGFtcGxlCgpXZSBhcmUgaW50ZXJlc3RlZCBpbiBrbm93aW5nIGlmIHRoZXJlIGlzIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gYEFnZWAgYW5kIGBsZW5ndGhjbWAgb2Ygc21hbGxtb3V0aCBiYXNzLiBGcm9tIHRoZSBwbG90IG9mIHRoZSBzYW1wbGUgZGF0YSwgaXQgc2VlbXMgbGlrZSB0aGF0IHdvdWxkIGJlIHRoZSBjYXNlLgoKYGBge3J9CndibGFrZTIgPC0gd2JsYWtlICU+JSAKICBtdXRhdGUobGVuZ3RoY20gPSBMZW5ndGgvMTApCgp3Ymxha2UyICU+JSAKICBnZ3Bsb3QoYWVzKHg9bGVuZ3RoY20sIHk9QWdlKSkgKwogIGdlb21faml0dGVyKCkKYGBgCgpgYGB7cn0KZmlzaF9hZ2Vfc2ltcGxlIDwtIGxtKEFnZSB+IGxlbmd0aGNtLCBkYXRhPXdibGFrZTIpCnRpZHkoZmlzaF9hZ2Vfc2ltcGxlKQpgYGAKCgpJIGFtIGdvaW5nIHRvIHNldCB1cCBhIGh5cG90aGV0aWNhbCB3b3JsZCB3aGVyZSB0aGVyZSBpcyBubyByZWxhdGlvbnNoaXAgYmV0d2VlbiBgQWdlYCBhbmQgYGxlbmd0aGNtYC4gRm9yIG5vdywgZG9uJ3Qgd29ycnkgYWJvdXQgdGhlIGNvZGUuIEknbGwgZXhwbGFpbiB0aGF0IGxhdGVyLiBCdXQsIG5vdGljZSB0aGF0IGlmIEkgKG9yIHlvdSkgcnVuIHRoaXMgb3ZlciBhbmQgb3ZlciBhZ2FpbiwgSSBnZXQgcGljdHVyZXMgdGhhdCBzZWVtIHRvIHNob3cgbm8gcmVsYXRpb25zaGlwIGJldHdlZW4gYEFnZWAgYW5kIGBsZW5ndGhjbWAuIAoKYGBge3J9CndibGFrZTIgJT4lIAogIG11dGF0ZShuZXdfQWdlID0gc2FtcGxlKEFnZSkpICU+JSAKICBnZ3Bsb3QoYWVzKHg9bGVuZ3RoY20sIHk9bmV3X0FnZSkpICsKICBnZW9tX2ppdHRlcigpCmBgYAoKCk5vdywgSSBhbSBnb2luZyB0byBmaXQgbW9kZWxzIHRvIHRoZSBkYXRhIGluIHRoaXMgaHlwb3RoZXRpY2FsIHdvcmxkIGFuZCBwbG90IHRoZW0gKHRoZSBibHVlIGxpbmVzKS4gRG9uJ3Qgd29ycnkgYWJvdXQgdGhlIGNvZGUgZm9yIG5vdyAuLi4gd2UnbGwgZGlzY3VzcyBpdCBsYXRlci4gVGhlIHJlZCBsaW5lIGlzIHRoZSBhY3R1YWwgbGluZSBmaXQgdG8gdGhlIHNhbXBsZSBvZiBkYXRhLiAqKldoYXQgZG8geW91IG5vdGljZT8qKgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CnNldC5zZWVkKDExMTkpCgpoeXBvdGhldGljYWxfc2FtcGxlcyA8LQogIHdibGFrZTIgJT4lIAogIHJlcF9zYW1wbGVfbihzaXplID0gNDM5LCAKICAgICAgICAgICAgICAgcmVwbGFjZSA9IEZBTFNFLCAKICAgICAgICAgICAgICAgcmVwcyA9IDIwMCkgJT4lIAogIGdyb3VwX2J5KHJlcGxpY2F0ZSkgJT4lIAogIG11dGF0ZShuZXdfQWdlID0gc2FtcGxlKEFnZSkpICU+JSAKICB1bmdyb3VwKCkKCmh5cG90aGV0aWNhbF9zYW1wbGVzICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBsZW5ndGhjbSwgeSA9IG5ld19BZ2UsIGdyb3VwID0gcmVwbGljYXRlKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG9yID0gImxpZ2h0Ymx1ZSIpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAtMC45OTMwMzQ0LCBzbG9wZSA9IDAuMjY5MjUyMSwgY29sb3IgPSAiZGFya3JlZCIpICsKICB5bGltKDAsOCkKYGBgCgpUaGUgcGxvdHMgYmVsb3cgc2hvdyBtb3JlIGRldGFpbCBmb3IgbmluZSBvZiB0aGUgbGluZXMuCgpgYGB7cn0KaHlwb3RoZXRpY2FsX3NhbXBsZXMgJT4lIAogIGZpbHRlcihyZXBsaWNhdGU8MTApICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBsZW5ndGhjbSwgeSA9IG5ld19BZ2UpKSArCiAgZ2VvbV9qaXR0ZXIoc2l6ZSA9IC41LCBhbHBoYSA9IC41KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAibGlnaHRibHVlIikgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IC0wLjk5MzAzNDQsIHNsb3BlID0gMC4yNjkyNTIxLCBjb2xvciA9ICJkYXJrcmVkIikgKwogIGZhY2V0X3dyYXAofnJlcGxpY2F0ZSkKYGBgCgoKQW5kIHdlIGNhbiBleGFtaW5lIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHNsb3BlcyBmcm9tIHRoZSBtb2RlbHMgZml0IHRvIHRoZSBkYXRhIGluIHRoaXMgaHlwb3RoZXRpY2FsIHdvcmxkLiBUaGUgdmVydGljYWwgbGluZSBpcyB0aGUgc2xvcGUgZnJvbSB0aGUgbW9kZWwgZml0IHRvIHRoZSBvcmlnaW5hbCBzYW1wbGUgb2YgZGF0YSAoZnJvbSBgZmlzaF9hZ2Vfc2ltcGxlYCkuIAoKVGhpcyBoaXN0b2dyYW0vZGlzdHJpYnV0aW9uIHNpbXVsYXRlcyB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mIHRoZSBzbG9wZSBpbiB0aGUgaHlwb3RoZXRpY2FsIHdvcmxkLCB1bmRlciB0aGUgYXNzdW1wdGlvbiB0aGF0IHRoZXJlIGlzIG5vIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzLgoKYGBge3J9Cmh5cG90aGV0aWNhbF9zYW1wbGVzICU+JQogIGdyb3VwX2J5KHJlcGxpY2F0ZSkgJT4lICAgICAKICBzdW1tYXJpemUobG0obmV3X0FnZSB+IGxlbmd0aGNtKSAlPiUgdGlkeSgpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBmaWx0ZXIodGVybSA9PSAibGVuZ3RoY20iKSAlPiUgCiAgZ2dwbG90KGFlcyh4PWVzdGltYXRlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9MzAsIGZpbGwgPSAibGlnaHRibHVlIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAuMjY5MjUyMSwgY29sb3IgPSAiZGFya3JlZCIpCmBgYAoKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgogIDxzdHJvbmc+WU9VUiBUVVJOITwvc3Ryb25nPgoKKiBXaGF0IGlzIGFuIG9ic2VydmF0aW9uIGluIHRoZSBoaXN0b2dyYW0gYWJvdmU/ICAKKiBXaGF0IGRvZXMgdGhpcyB0ZWxsIHVzIGFib3V0IHRoZSBzbG9wZSBmcm9tIHRoZSBzYW1wbGUgZGF0YSBjb21wYXJlZCB0byB3aGF0IHdlJ2QgZXhwZWN0IHRvIHNlZSBpbiB0aGUgaHlwb3RoZXRpY2FsIHdvcmxkIHdoZXJlIHRoZXJlIGlzIG5vIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGBBZ2VgIGFuZCBgbGVuZ3RoY21gPwoKPC9kaXY+CgojIExvZ2ljIGFuZCBMYW5ndWFnZSBvZiBIeXBvdGhlc2lzIFRlc3RzLgoKSW4gdGhlIGV4YW1wbGUgYWJvdmUsIGl0IG1heSBzZWVtIHdlaXJkIHRoYXQgd2UgZGVjaWRlZCB0byB0ZXN0IHRoZSBoeXBvdGhlc2lzIHRoYXQgdGhlcmUgKmlzIG5vdCogYSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgQWdlYCBhbmQgYGxlbmd0aGNtYC4gU2hvdWxkbid0IHdlIGh5cG90aGVzaXplIHRoYXQgdGhlcmUgKmlzKiBhIHJlbGF0aW9uc2hpcCwgc2luY2UgdGhhdCBpcyB3aGF0IHdlIGJlbGlldmUgdG8gYmUgdHJ1ZT8gSXQgdHVybnMgb3V0IHRoZSBhbnN3ZXIgaXMgbm8uIFdoeT8gTG9naWMhIAoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+CiAgPHN0cm9uZz5TaG9ydCBZT1VSIFRVUk4hIHRhbmdlbnQgPC9zdHJvbmc+CgpCZWZvcmUganVtcGluZyBpbnRvIGh5cG90aGVzaXMgdGVzdCBsb2dpYywgSSB0aGluayBpdCBpcyB3b3J0aHdoaWxlIHRvIGRvIGEgc2ltcGxlciBsb2dpYyBleGFtcGxlLiBGaXJzdCwgbGV0J3MgYXNzdW1lIHRoZSBzdGF0ZW1lbnQgIkFsbCBzdGF0aXN0aWNzIGNsYXNzZXMgYXJlIGZ1biIgaXMgdHJ1ZSAoaXQgaXMsIHJpZ2h0PykuIEkgY2FuIHJlLXdyaXRlIHRoaXMgc3RhdGVtZW50IGFzIGFuIGlmLWVsc2Ugc3RhdGVtZW50OiAiSWYgYSBjbGFzcyBpcyBhIHN0YXRpc3RpY3MgY2xhc3MsIHRoZW4gaXQgaXMgZnVuLiIgCgpIb3cgY291bGQgeW91IGZpbmlzaCB0aGUgZm9sbG93aW5nIHN0YXRlbWVudHMgc28gdGhhdCB0aGV5IGFyZSB0cnVlPwoKMS4gSWYgYSBjbGFzcyBpcyBmdW4sIHRoZW4gLi4uLiAKMi4gSWYgYSBjbGFzcyBpcyBOT1QgZnVuLCB0aGVuIC4uLi4KCjwvZGl2PgoKMS4gV2UgYXNzdW1lIHRoYXQgSUYgdGhlIGh5cG90aGVzaXMgaXMgdHJ1ZSwgVEhFTiBvdXIgc3RhdGlzdGljIG9mIGludGVyZXN0ICh0aGUgc2xvcGUsIGluIHRoZSBleGFtcGxlIGFib3ZlKSBmb2xsb3dzIHNvbWUga25vd24gZGlzdHJpYnV0aW9uIChsaWtlIHRoZSBzYW1wbGluZyBkaXN0cmlidXRpb24gd2Ugc2ltdWxhdGVkIHdoaWNoIEkgcGxvdHRlZCBhZ2FpbiBiZWxvdyksIGllLiBpdCdzIGEgcmFuZG9tIGRyYXcgZnJvbSB0aGlzIGRpc3RyaWJ1dGlvbi4gCgpgYGB7ciwgZWNobz1GQUxTRX0KaHlwb3RoZXRpY2FsX3NhbXBsZXMgJT4lCiAgZ3JvdXBfYnkocmVwbGljYXRlKSAlPiUgICAgIAogIHN1bW1hcml6ZShsbShuZXdfQWdlIH4gbGVuZ3RoY20pICU+JSB0aWR5KCkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGZpbHRlcih0ZXJtID09ICJsZW5ndGhjbSIpICU+JSAKICBnZ3Bsb3QoYWVzKHg9ZXN0aW1hdGUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz0xNSwgZmlsbCA9ICJsaWdodGJsdWUiKQpgYGAKCjIuIENvbXBhcmUgdGhlIG9ic2VydmVkIHZhbHVlIG9mIHRoZSBzdGF0aXN0aWMgKHRoZSBzbG9wZSBmcm9tIHRoZSBzYW1wbGUgZGF0YSkgdG8gdGhlIGtub3duIGRpc3RyaWJ1dGlvbi4gVGhlcmUgYXJlIHR3byBwb3NzaWJsZSBvdXRjb21lczoKCiAgKiBBZ3JlZW1lbnQ6IHRoZSBvYnNlcnZlZCBzdGF0aXN0aWMgaXMgYSBwbGF1c2libGUgb3V0Y29tZSBmcm9tIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHN0YXRpc3RpYyAgCiAgKiBEaXNhZ3JlZW1lbnQ6IHRoZSBvYnNlcnZlZCBzdGF0aXN0aWMgaXMgbm90IGEgcGxhdXNpYmxlIG91dGNvbWUgZnJvbSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBzdGF0aXN0aWMKCjMuIERyYXcgYSBjb25jbHVzaW9uIChzZWUgW2NvbnRyYXBvc2l0aXZlXShodHRwOi8vd3d3Lm1hdGh3b3Jkcy5jb20vYy9jb250cmFwb3NpdGl2ZS5odG0pKQoKICAqIElmIHRoZSBvdXRjb21lIGlzIGFncmVlbWVudCwgd2hhdCBjYW4geW91IGxvZ2ljYWxseSBjb25jbHVkZT8gSW4gb3RoZXIgd29yZHMsIGlmIHRoZSBzdGF0aXN0aWMgb2YgaW50ZXJlc3QgZm9sbG93cyBzb21lIGtub3duIGRpc3RyaWJ1dGlvbiAoaWUuIGlzIGEgcmFuZG9tIGRyYXcgZnJvbSB0aGUgaHlwb3RoZXRpY2FsIGRpc3RyaWJ1dGlvbiksIHdoYXQgY2FuIHlvdSBjb25jbHVkZSBhYm91dCB0aGUgaHlwb3RoZXNpcz8gU3BlY2lmaWNhbGx5LCBpcyBpdCBsb2dpY2FsIHRvIGNvbmNsdWRlIHRoYXQgdGhlIGh5cG90aGVzaXMgaXMgdHJ1ZT8gIAogICogSWYgdGhlIG91dGNvbWUgaXMgZGlzYWdyZWVtZW50LCB3aGF0IGNhbiB5b3UgbG9naWNhbGx5IGNvbmNsdWRlPyBJbiBvdGhlciB3b3JkcywgaWYgdGhlIHN0YXRpc3RpYyBvZiBpbnRlcmVzdCBkb2VzIG5vdCBmb2xsb3cgc29tZSBrbm93biBkaXN0cmlidXRpb24gKGllLiBpcyBub3QgYSByYW5kb20gZHJhdyBmcm9tIHRoZSBoeXBvdGhldGljYWwgZGlzdHJpYnV0aW9uKSwgd2hhdCBjYW4geW91IGNvbmNsdWRlIGFib3V0IHRoZSBoeXBvdGhlc2lzPwoKCgpcClwKXApcClwKXApcClwKXApcClwKXApcClwKXApcCgoKClRodXMsIGl0IGlzIG11Y2ggbW9yZSBzYXRpc2Z5aW5nIHRvIGJlIGluIGRpc2FncmVlbWVudCB3aXRoIHRoZSBoeXBvdGhlc2lzIGJlY2F1c2UgdGhlbiB3ZSBjYW4gcmVqZWN0IGl0ISBUaGUgaHlwb3RoZXNpcyBpcyByZWFsbHkgc2V0IHVwIGZvciB1cyB0byBvYnRhaW4gZXZpZGVuY2UgdG8gZGlzYWdyZWUgd2l0aCBpdC4gU2luY2UgaXRzIHJvbGUgaXMgdG8gYmUgZGlzYWdyZWVkIHdpdGggb3IgIm51bGxpZmllZCIsIGl0IGlzIGdpdmVuIHRoZSBuYW1lICoqbnVsbCBoeXBvdGhlc2lzKiosIG9yICRIXzAkLiAKCkNyaXRlcmlhIGZvciBOdWxsIGh5cG90aGVzZXM6CgoxLiBDaG9vc2Ugb25lIHRoYXQgaXMgaW50ZXJlc3RpbmcgdG8gcmVqZWN0IHNpbmNlIHRoYXQgaXMgdGhlIG9ubHkgaW50ZXJlc3RpbmcgY29uY2x1c2lvbiB3ZSBjYW4gbWFrZS4gVGhpcyBtZWFucyB0aGV5IGFsbW9zdCBhbHdheXMgdGFrZSB0aGUgZm9ybSBvZiBubyBlZmZlY3QsIGxpa2Ugb3VyIGV4YW1wbGUgd2hlcmUgdGhlcmUgd2FzIG5vIHJlbGF0aW9uc2hpcCBvciB0aGUgc2xvcGUgd2FzIHplcm8uICAKMi4gVGhlIGh5cG90aGVzaXMgbmVlZHMgdG8gYmUgc3BlY2lmaWMgaW4gb3JkZXIgdG8gY29uc3RydWN0IHRoZSBkaXN0cmlidXRpb24uCgojIEV4YW1wbGUgcmV2aXNpdGVkCgpIb3cgZGlkIEkgc2V0IHVwIGEgaHlwb3RoZXRpY2FsIHdvcmxkIHdoZXJlIHRoZXJlIGlzIG5vIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGBBZ2VgIGFuZCBgbGVuZ3RoY21gPyAKCklmIHRoZXJlIGlzIG5vIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGBBZ2VgIGFuZCBgbGVuZ3RoY21gLCB0aGVuIG5vIG1hdHRlciB0aGUgbGVuZ3RoLCB0aGUgcGxhdXNpYmxlIGFnZXMgc2hvdWxkIGJlIHRoZSBzYW1lLiBBbm90aGVyIHdheSBvZiBzYXlpbmcgdGhpcyBpcyB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgYWdlcyBzaG91bGQgYmUgdGhlIHNhbWUgbm8gbWF0dGVyIHRoZSBsZW5ndGguIFRoYXQgZGlzdHJpYnV0aW9uIG9mIGFnZXMgY29tZXMgZnJvbSBvdXIgYWN0dWFsIGRhdGEuIAoKTGV0J3MgbW9yZSBjbG9zZWx5IGludmVzdGlnYXRlIHRoZSBjb2RlIHRoYXQgZ2VuZXJhdGVkIHRoZSAiaHlwb3RoZXRpY2FsIHdvcmxkIi4gKipEaXNjdXNzIHRoZSBjb2RlIGJlbG93LioqCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0Kc2V0LnNlZWQoMTExOSkKCmh5cG90aGV0aWNhbF9zYW1wbGVzIDwtCiAgd2JsYWtlMiAlPiUgI29yaWdpbmFsIHNhbXBsZSBvZiBkYXRhCiAgcmVwX3NhbXBsZV9uKHNpemUgPSA0MzksICN0YWtlIHNhbXBsZXMgb2YgdGhpcyBzaXplIGZyb20gdGhlIG9yaWdpbmFsIHNhbXBsZSAtIHdoeSB0aGlzPwogICAgICAgICAgICAgICByZXBsYWNlID0gRkFMU0UsICN3aXRob3V0IHJlcGxhY2VtZW50CiAgICAgICAgICAgICAgIHJlcHMgPSAyMDApICU+JSAjdGhpcyBtYW55IHJlcGxpY2F0ZXMgb2YgdGhlIHByb2Nlc3MKICBncm91cF9ieShyZXBsaWNhdGUpICU+JSAjZm9yIGVhY2ggcmVwbGljYXRlCiAgbXV0YXRlKG5ld19BZ2UgPSBzYW1wbGUoQWdlKSkgJT4lICNzaHVmZmxlL3Blcm11dGUgdGhlIEFnZXMKICB1bmdyb3VwKCkgI3VuZ3JvdXAgdGhlIGRhdGEKYGBgCgpTbywgd2UgZW5kIHVwIHdpdGggMjAwIHNhbXBsZXMgb2YgZGF0YSB3aGVyZSB0aGVyZSAqKklTIE5PKiogcmVsYXRpb25zaGlwIGJldHdlZW4gYEFnZWAgKG5vdyBjYWxsZWQgYG5ld19BZ2VgKSBhbmQgYGxlbmd0aGNtYCAtIHRoYXQncyB0aGUgd29ybGQgd2hlcmUgdGhlIE5VTEwgSFlQT1RIRVNJUyBpcyB0cnVlIS4gCgpUaGVuLCBmb3IgZWFjaCBzYW1wbGUsIHdlIGZpdCB0aGUgbW9kZWwgYG5ld19BZ2UgfiBsZW5ndGhjbWAgYW5kIGtlZXAgdHJhY2sgb2YgdGhlIGVzdGltYXRlZCBjb2VmZmljaWVudCBmb3IgYGxlbmd0aGNtYC4gTGFzdGx5LCB3ZSBtYWtlIGEgaGlzdG9ncmFtIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnRzLiBTbywgaWYgdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlLCB0aGlzIGlzIHdoYXQgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiBzbG9wZXMgd291bGQgbG9vayBsaWtlLgoKYGBge3J9Cmh5cG90aGV0aWNhbF9zYW1wbGVzICU+JSAjMjAwIHNhbXBsZXMgb2YgZGF0YSBmcm9tICJoeXBvdGhldGljYWwgd29ybGQiIHdoZXJlIHRoZXJlIGlzIG5vIHJlbGF0aW9uc2hpcAogIGdyb3VwX2J5KHJlcGxpY2F0ZSkgJT4lICNmb3IgZWFjaCByZXBsaWNhdGUvc2FtcGxlCiAgc3VtbWFyaXplKGxtKG5ld19BZ2UgfiBsZW5ndGhjbSkgJT4lIHRpZHkoKSkgJT4lICNmaXQgdGhpcyBtb2RlbAogIHVuZ3JvdXAoKSAlPiUgI3VuZ3JvdXAgdGhlIGRhdGEKICBmaWx0ZXIodGVybSA9PSAibGVuZ3RoY20iKSAlPiUgI2ZpbHRlciB0byBvbmx5IHRoaXMgdGVybQogIGdncGxvdChhZXMoeD1lc3RpbWF0ZSkpICsgI21ha2UgYSBoaXN0b2dyYW0gb2YgdGhlIGVzdGltYXRlZCBjb2VmZmljaWVudHMKICBnZW9tX2hpc3RvZ3JhbShiaW5zPTE1LCBmaWxsID0gImxpZ2h0Ymx1ZSIpIApgYGAKCgpEb2VzIHRoZSBzbG9wZSBmcm9tIG91ciBzYW1wbGUgZGF0YSBzZWVtIHRvIGFncmVlIG9yIGRpc2FncmVlIHdpdGggdGhlIG51bGwgaHlwb3RoZXNpcz8gV2hhdCBkbyB3ZSBjb25jbHVkZT8KCmBgYHtyfQpoeXBvdGhldGljYWxfc2FtcGxlcyAlPiUKICBncm91cF9ieShyZXBsaWNhdGUpICU+JSAgICAgCiAgc3VtbWFyaXplKGxtKG5ld19BZ2UgfiBsZW5ndGhjbSkgJT4lIHRpZHkoKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgZmlsdGVyKHRlcm0gPT0gImxlbmd0aGNtIikgJT4lIAogIGdncGxvdChhZXMoeD1lc3RpbWF0ZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwLCBmaWxsID0gImxpZ2h0Ymx1ZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjI2OTI1MjEsIGNvbG9yID0gImRhcmtyZWQiKQpgYGAKCgojIFAtdmFsdWVzCiAKSW4gdGhlIGRpc2N1c3Npb24gYWJvdXQgYWdyZWVtZW50IGFuZCBkaXNhZ3JlZW1lbnQgYWJvdmUsIEkgZGlkbid0IG1lbnRpb24gYW55IGRldGFpbCBhcm91bmQgaG93IHlvdSBtaWdodCBkZWNpZGUgaWYgdGhlIG9ic2VydmVkIHN0YXRpc3RpYyBpcyBhIHBsYXVzaWJsZSBvdXRjb21lIGZyb20gdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc3RhdGlzdGljLiBXaGF0IGNvdW50cyBhcyBwbGF1c2libGU/IEFuZCBob3cgZG8gd2UgY2FsY3VsYXRlIGl0PyAKClRoZSBwLXZhbHVlIGlzIHRoZSBmcmFjdGlvbiBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBzdGF0aXN0aWNzIHdoZW4gdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlIHRoYXQgYXJlIG1vcmUgZXh0cmVtZSAobGVzcyBsaWtlbHkpIHRoYW4gdGhlIG9ic2VydmVkIHN0YXRpc3RpYyBmcm9tIHRoZSBzYW1wbGUuIENvbnZlbnRpb25hbGx5IGFuIG9ic2VydmF0aW9uIGlzIGNvbnNpZGVyZWQgaW1wbGF1c2libGUgd2hlbiB0aGUgcC12YWx1ZSBpcyBsZXNzIHRoYW4gLjA1LiAKCkhvdyB3b3VsZCB5b3UgY2FsY3VsYXRlIHRoZSBwLXZhbHVlIHVzaW5nIHRoZSBzaW11bGF0ZWQgZGF0YT8gCgoxLiBXZSBuZWVkIHRvIHVuZGVyc3RhbmQgdGhhdCAibW9yZSBleHRyZW1lIiBtZWFucyBmdXJ0aGVyIGludG8gdGhlIHRhaWxzIG9mIHRoZSBkaXN0cmlidXRpb24uIFNvLCBpbiBvdXIgZXhhbXBsZSwgdGhhdCB3b3VsZCBtZWFuIGZ1cnRoZXIgdG8gdGhlIHJpZ2h0IG9mIHdoZXJlIHRoZSBvYnNlcnZlZCB2YWx1ZSBsaWVzLiBJdCBhbHNvIG1lYW5zIGZ1cnRoZXIgdG8gdGhlIGxlZnQgb2YgdGhlIG5lZ2F0aXZlIG9mIHRoZSBvYnNlcnZlZCB2YWx1ZSBiZWNhdXNlIHRoYXQgaXMgZXF1YWxseSBhcyBleHRyZW1lLiBJbiBvdGhlciBwcm9ibGVtcywgd2UgY291bGQgaGF2ZSBhIG5lZ2F0aXZlIG9ic2VydmVkIHNsb3BlIHNvICJtb3JlIGV4dHJlbWUiIHdvdWxkIGJlIHRvIHRoZSBsZWZ0IG9mIHRoZSBvYnNlcnZlZCB2YWx1ZSBhbmQgdG8gdGhlIHJpZ2h0IG9mIHRoZSBwb3NpdGl2ZSBvZiB0aGUgb2JzZXJ2ZWQgdmFsdWUuIFlvdSBuZWVkIHRvIHRha2UgdGltZSB0byB0aGluayBhYm91dCB0aGlzLgoKMi4gQ3JlYXRlIGEgY29sdW1uL3ZhcmlhYmxlIHRoYXQgaW5kaWNhdGVzIGlmIHRoZSBlc3RpbWF0ZWQgc2xvcGUgaXMgbW9yZSBleHRyZW1lIHRoYW4gdGhlIG9ic2VydmVkIHZhbHVlLiBJIGRvIHRoYXQgYmVsb3csIGNyZWF0aW5nIGEgdmFyaWFibGUgY2FsbGVkIGBtb3JlX2V4dHJlbWVfdGhhbl9hY3R1YWxgLiAqKldoYXQgdmFsdWVzIGRvZXMgdGhpcyB2YXJpYWJsZSB0YWtlPyoqCgpgYGB7cn0KaHlwb3RoZXRpY2FsX3NhbXBsZXMgJT4lCiAgZ3JvdXBfYnkocmVwbGljYXRlKSAlPiUgICAgIAogIHN1bW1hcml6ZShsbShuZXdfQWdlIH4gbGVuZ3RoY20pICU+JSB0aWR5KCkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGZpbHRlcih0ZXJtID09ICJsZW5ndGhjbSIpICU+JSAKICBtdXRhdGUobW9yZV9leHRyZW1lX3RoYW5fYWN0dWFsID0gZXN0aW1hdGUgPiAwLjI2OTI1MjEpCmBgYAoKMy4gQ29tcHV0ZSB0aGUgcC12YWx1ZSBieSBmaW5kaW5nIHRoZSBwcm9wb3J0aW9uIG9yIGZyYWN0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHN0YXRpc3RpY3Mgd2hlbiB0aGUgbnVsbCBoeXBvdGhlc2lzIGlzIHRydWUgKHRoZSBlc3RpbWF0ZWQgY29lZmZmaWNpZW50cykgdGhhdCBhcmUgbW9yZSBleHRyZW1lIChsZXNzIGxpa2VseSkgdGhhbiB0aGUgb2JzZXJ2ZWQgc3RhdGlzdGljIGZyb20gdGhlIHNhbXBsZS4gSW4gdGhlIGNvZGUgYmVsb3csIGluc2lkZSB0aGUgYHN1bW1hcml6ZSgpYCBmdW5jdGlvbiwgSSBjb21wdXRlIHRoZSBwLXZhbHVlIGluIHR3byBkaWZmZXJlbnQgd2F5cy4gWW91IERPIE5PVCBuZWVkIHRvIGRvIGJvdGggYXMgdGhleSBhcmUgZG9pbmcgdGhlIGV4YWN0IHNhbWUgdGhpbmcuIFRoZSBgbW9yZV9leHRyZW1lX3RoYW5fYWN0dWFsYCB2YXJpYWJsZSB0YWtlcyBUUlVFL0ZBTFNFIHZhbHVlcyBidXQgdGhvc2UgYXJlIHRyZWF0ZWQgYXMgMS8wLiBTbywgdGFraW5nIHRoZSBtZWFuIGlzIGdpdmluZyB0aGUgcHJvcG9ydGlvbiB0aGF0IGFyZSBtb3JlIGV4dHJlbWUuIFdlIGRvdWJsZSBpdCAobXVsdGlwbHkgYnkgMikgdG8gYWNjb3VudCBmb3IgdGhlIGVxdWl2YWxlbnQgZXh0cmVtZSBjYXNlcyBpbiB0aGUgb3RoZXIgZGlyZWN0aW9uLgoKYGBge3J9Cmh5cG90aGV0aWNhbF9zYW1wbGVzICU+JQogIGdyb3VwX2J5KHJlcGxpY2F0ZSkgJT4lICAgICAKICBzdW1tYXJpemUobG0obmV3X0FnZSB+IGxlbmd0aGNtKSAlPiUgdGlkeSgpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBmaWx0ZXIodGVybSA9PSAibGVuZ3RoY20iKSAlPiUgCiAgbXV0YXRlKG1vcmVfZXh0cmVtZV90aGFuX2FjdHVhbCA9IGVzdGltYXRlID4gMC4yNjkyNTIxKSAlPiUgCiAgc3VtbWFyaXplKHBfdmFsID0gMipzdW0obW9yZV9leHRyZW1lX3RoYW5fYWN0dWFsKS9uKCksCiAgICAgICAgICAgIHBfdmFsMiA9IDIqbWVhbihtb3JlX2V4dHJlbWVfdGhhbl9hY3R1YWwpKQpgYGAKCgojIFVzaW5nIHRoZW9yeS9SIHRvIGZpbmQgcC12YWx1ZXMKCkl0IG1pZ2h0IHNlZW0gbGlrZSBhIGxvdCBvZiB3b3JrIHRvIGNvbmR1Y3QgYSBoeXBvdGhlc2lzIHRlc3QuIFNvIGZhciwgSSd2ZSBtYWRlIGl0IHNlZW0gbGlrZSB3ZSBoYXZlIHRvIHNldCB1cCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBjb2VmZmljaWVudCB3aGVuIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZSBldmVyeSB0aW1lIHdlIHdhbnQgdG8gY29uZHVjdCBhIHRlc3QuIFRoYW5rZnVsbHksIHdlIGRvbid0IGhhdmUgdG8gZG8gdGhpcy4gSnVzdCBsaWtlIHdlIGRpZG4ndCAqbmVlZCogdG8gdXNlIGJvb3RzdHJhcHBpbmcgdG8gZmluZCBjb25maWRlbmNlIGludGVydmFscywgc3RhdGlzdGljYWwgdGhlb3J5IChhbmQgUikgc2F2ZXMgdXMhIAoKUiB1c2VzIHNvbWV0aGluZyBjYWxsZWQgYSAqdGVzdCBzdGF0aXN0aWMqIHRvIGRvIHRoZSBwcm9iYWJpbGl0eSBjYWxjdWxhdGlvbnMuIAoKKipXaGF0IGFyZSB0ZXN0IHN0YXRpc3RpY3M/ICoqCgpUaGV5IGFyZSBhIHNwZWNpYWwga2luZCBvZiBzdGF0aXN0aWMgdGhhdCBmb2xsb3dzIGEga25vd24gZGlzdHJpYnV0aW9uIChsaWtlIGEgTm9ybWFsIGRpc3RyaWJ1dGlvbiBvciBpdHMgY2xvc2UgY291c2luLCB0aGUgdC1kaXN0cmlidXRpb24pLiBUaGV5IGFyZSBhIGZ1bmN0aW9uIG9mIHRoZSBzdGF0aXN0aWMgd2UgYXJlIGludGVyZXN0ZWQgaW4sIGZvciBjb2VmZmljaWVudHMgaW4gbW9kZWxzIHRoZXkgYXJlIGVxdWFsIHRvIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnQgZGl2aWRlZCBieSBpdHMgc3RhbmRhcmQgZXJyb3IgKGF0IGxlYXN0IHdoZW4gdGhlICRIXzAkIGlzIHRoYXQgdGhlIHRydWUgY29lZmZpY2llbnQgaXMgemVybykuCgpXaGVuIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZSwgdGhlIHRlc3Qgc3RhdGlzdGljIGZvbGxvd3MgYSB0LWRpc3RyaWJ1dGlvbiwgd2hpY2ggZm9yIGxhcmdlIHNhbXBsZSBzaXplcyBpcyBleHRyZW1lbHkgY2xvc2UgdG8gYSBub3JtYWwgZGlzdHJpYnV0aW9uIHRoYXQgaXMgY2VudGVyZWQgYXQgMCBhbmQgaGFzIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDEuIAoKRmluZGluZyB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgdGVzdCBzdGF0aXN0aWMgaXMgbW9yZSBleHRyZW1lIHRoYW4gdGhlIG9ic2VydmVkIHRlc3Qgc3RhdGlzdGljIHdoZW4gdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlIGlzIGVxdWl2YWxlbnQgdG8gZmluZGluZyB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgc2xvcGUgaXMgbW9yZSBleHRyZW1lIHRoYW4gdGhlIG9ic2VydmVkIHNsb3BlIHdoZW4gdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlLgoKTGV0J3MgbG9vayBhdCB0aGUgcmVncmVzc2lvbiB0YWJsZS4gCgpgYGB7cn0KdGlkeShmaXNoX2FnZV9zaW1wbGUpCmBgYAoKVGhlIGNvbHVtbiBjYWxsZWQgYHN0YXRpc3RpY2AgaXMgdGhlIHRlc3Qgc3RhdGlzdGljLiBXZSBjYW4gZHJhdyBhIHBpY3R1cmUgb2YgdGhlIG9ic2VydmVkIHRlc3Qgc3RhdGlzdGljIGluIHJlbGF0aW9uIHRvIHdoYXQgd2Ugd291bGQgZXhwZWN0IHdoZW4gdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlLiBUaGUgY29sdW1uIGNhbGxlZCBgcC52YWx1ZWAgaXMgdGhlIHAtdmFsdWUuIAoKTm90ZSB0aGF0IHRoaXMgaXMgZG9pbmcgd2hhdCBpcyBjYWxsZWQgYSB0d28tc2lkZWQgdGVzdC4gVGhpcyBtZWFucyB0aGF0IHZhbHVlcyBhcmUgY29uc2lkZXJlZCBleHRyZW1lIG9uIGJvdGggc2lkZXMgb2YgdGhlIGRpc3RyaWJ1dGlvbi4gU28sIGV2ZW4gdGhvdWdoIHdlIGhhZCBhIHZhbHVlIHRoYXQgd2FzIGZhciB0byB0aGUgcmlnaHQgb2YgdGhlIGRpc3RyaWJ1dGlvbiwgdmFsdWVzIGVxdWFsbHkgYXMgZmFyIHRvIHRoZSBsZWZ0IHdvdWxkIGFsc28gYmUgY29uc2lkZXJlZCBpbiB0aGlzIGNhbGN1bGF0aW9uLiBUaGlzIGhhcyB0byBkbyB3aXRoIHRoZSBhc3N1bWVkIGFsdGVybmF0aXZlIGh5cG90aGVzaXMsIHdoaWNoIEknbGwgdGFsayBhYm91dCBuZXh0LgoKIyBUaGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcwoKSW4gcHJhY3RpY2UsIHNjaWVudGlzdHMgdXN1YWxseSBhbHNvIGhhdmUgYW4gYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcywgd2hpY2ggaXMgdGhlIGh5cG90aGVzaXMgdGhleSBhY3R1YWxseSBob3BlIGlzIHRydWUuIFRoaXMgaXMgZG9uZSBmb3IgYSBmZXcgcmVhc29uczoKCjEuIFRvIG1vdGl2YXRlIHRoZSBzdHVkeS4KCjIuIFRvIGNvbXB1dGUgc2FtcGxlIHNpemVzIG5lY2Vzc2FyeSB0byBhY2hpZXZlIGEgY2VydGFpbiBwb3dlciBvZiBhIHRlc3QgKHdlIHdvbid0IGRpc2N1c3MgdGhhdCBpbiB0aGlzIGNsYXNzLCBhbHRob3VnaCBpdCdzIHZlcnkgaW1wb3J0YW50IGluIGV4cGVyaW1lbnRhbCBzdHVkaWVzKS4KCk5vdGljZSB0aGF0IGhhdmluZyBhbiBhbHRlcm5hdGl2ZSBtaWdodCBiZSBuaWNlIGZvciBtb3RpdmF0aW9uIGJ1dCBpdCBpc24ndCByZWFsbHkgaW52b2x2ZWQgaW4gdGhlIGNvbmNsdXNpb24gb2YgdGhlIGh5cG90aGVzaXMgdGVzdCBzaW5jZSBvdXIgb25seSBjaG9pY2VzIGFyZSBlaXRoZXIgdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgb3Igbm90IHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLgoKV2hlbiB3ZSBtYWtlIGh5cG90aGVzaXMgdGVzdHMsIHdlIHdyaXRlIHRoZW0gbGlrZQoKJCQKSF8wOiBcdGV4dHtwYXJhbWV0ZXIgb2YgaW50ZXJlc3QgPSB2YWx1ZX0gICAgXFwKSF9hOiBcdGV4dHtwYXJhbWV0ZXIgb2YgaW50ZXJlc3R9IFxuZSBcdGV4dHt2YWx1ZX0KJCQKCkZvciBvdXIgZmlzaCBleGFtcGxlLCB3ZSB3b3VsZCB3cml0ZQoKJCQKSF8wOiBcYmV0YV8xID0gMCAgIFxcCkhfYTogXGJldGFfMSBcbmUgMAokJAoKKG9yIHdlIGNvdWxkIHB1dCBhIHNwZWNpZmljIHZhbHVlIGluIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIHRoYXQgd2UgdGhpbmsgJFxiZXRhXzEkIG1pZ2h0IGJlIGVxdWFsIHRvKQoKVGhlbiwgd2UgdXNlIHRoZSBkYXRhIHRvIG1ha2Ugb3VyIGNvbmNsdXNpb24uIFdlIG9ic2VydmUgdGhhdCAkXGhhdHtcYmV0YX1fMSA9IDAuMjckIGFuZCBmaW5kIHRoZSBjb3JyZXNwb25kaW5nIHAtdmFsdWUgdG8gbWFrZSBvdXIgY29uY2x1c2lvbi4KCiMgU3VtbWFyeSBvZiB0aGUgcHJvY2VzcwoKMS4gU3RhdGUgJEhfMCQgYW5kICRIX2EkLiAgICAKMi4gU2VsZWN0IHRoZSB0aHJlc2hvbGQgZm9yIGEgc21hbGwgZW5vdWdoIHAtdmFsdWUgYXQgd2hpY2ggeW91IHdpbGwgcmVqZWN0ICRIXzAkLiBUaGlzIGlzIGNhbGxlZCAkXGFscGhhJCBhbmQgaXMgY29tbW9ubHkgc2V0IGF0IC4wNS4gIAozLiBDYWxjdWxhdGUgYSB0ZXN0IHN0YXRpc3RpYyBhbmQgY29ycmVzcG9uZGluZyBwLXZhbHVlLiBUaGlzIGNhbiBiZSBkb25lIHVzaW5nIHRoZW9yeSAoUiBvdXRwdXQpIG9yIHNpbXVsYXRpb24uICAKNC4gTWFrZSBhIGRlY2lzaW9uIC0gcmVqZWN0IG9yIGRvbid0IHJlamVjdCAkSF8wJC4KNS4gUHV0IHlvdXIgZGVjaXNpb24gaW4gdGhlIGNvbnRleHQgb2YgdGhlIGRhdGEhIAoKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgogIDxzdHJvbmc+WU9VUiBUVVJOITwvc3Ryb25nPgoKT2Z0ZW4sIHdlIGhhdmUgbW9yZSB0aGFuIG9uZSB2YXJpYWJsZSBpbiBvdXIgbW9kZWwuIEZvciBleGFtcGxlIHdpdGggdGhlIGZpc2ggZGF0YSwgd2UgbWF5IGFsc28gdXNlIHRoZSBzaXplIG9mIHRoZWlyIHNjYWxlIHRvIHByZWRpY3QgdGhlaXIgYWdlLiBXaGF0IGFyZSB0aGUgaHlwb3RoZXNlcyBiZWluZyB0ZXN0ZWQgZm9yIGVhY2ggb2YgdGhlIGNvZWZmaWNpZW50cyAobm90IHRoZSBpbnRlcmNlcHQpIGluIHRoZSBvdXRwdXQgYmVsb3c/IEhvdyB3b3VsZCB5b3UgdXNlIHNpbXVsYXRpb24gdG8gY29tcHV0ZSB0aGUgcC12YWx1ZT8gRG8geW91IGdldCBhIHNpbWlsYXIgcmVzdWx0IHRvIHdoZW4geW91IHVzZSB0aGUgcmVncmVzc2lvbiB0YWJsZSBkaXJlY3RseT8KCgpgYGB7cn0KZmlzaF9tb2QyIDwtIGxtKEFnZSB+IGxlbmd0aGNtICsgU2NhbGUsCiAgICAgICAgICAgICAgICBkYXRhPXdibGFrZTIpCnRpZHkoZmlzaF9tb2QyKQpgYGAKClRoZSBkYXRhIGJlbG93IHdlcmUgY29sbGVjdGVkIGZyb20gcmF0ZW15cHJvZmVzc29yLmNvbS4gU2VlIG1vcmUgYnkgdHlwaW5nIFJhdGVwcm9mIGludG8gdGhlIGhlbHAgc2VhcmNoLiBXaGF0IGlzIHRoZSBoeXBvdGhlc2lzPyBXaGF0IGRvZXMgdGhlIHAtdmFsdWUgZm9yIHRoZSAqZ2VuZGVybWFsZSogdGVybSB0ZWxsIHlvdT8gV2hhdCB3b3VsZCB5b3UgY29uY2x1ZGU/CgpgYGB7cn0KbG0ucmF0ZXByb2YucGVwIDwtIGxtKHF1YWxpdHkgfiBnZW5kZXIsIAogICAgICAgICAgICAgICAgICBkYXRhPVJhdGVwcm9mKQp0aWR5KGxtLnJhdGVwcm9mLnBlcCkKYGBgCgpCVFcsIG1vc3Qgc3R1ZGVudHMgaW4gYW4gaW50cm9kdWN0b3J5IHN0YXRzIGNvdXJzZSB3b3VsZCBsZWFybiBob3cgdG8gZG8gdGhpcyB1c2luZyBhICJUd28gU2FtcGxlIFQtVGVzdCIuIFNvLCB5b3Uga25vdyBob3cgdG8gZG8gb25lLiBZb3UganVzdCB1c2UgbGluZWFyIG1vZGVscyB0byBkbyBpdC4KCjwvZGl2PgoKCg==