library(alr4)        # NEW! For data. Let me know if you have issues installing this one)
library(GGally)      # New ... maybe? For pairwise plots
library(nullabor)    # for picking out residual plots from a "line-up" of plots
library(tidyverse)   # for plotting and summarizing
library(moderndive)  # for nice model output
library(broom)       # for nice model output 
library(patchwork)   # for nicely organizing plots
library(openintro)   # for data
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.

  • Check for non-constant variance and linearity using a residuals versus fitted values plot.
  • Check for normality by looking at a histogram of the residuals.
  • Know when to add a polynomial term to a model and implement it.
  • Know a few key scenarios when log-transforming the response variable and/or predictor variables could help satisfy model assumptions.
  • Interpret model coefficients with a log-transformed response variable.

REVIEW: Models we have explored so far - quantitative response variable

  1. No explanatory variables. Every predicted value is the average response.

  2. One quantitative explanatory variable.

  • Intercept is the average response when explanatory variable is zero.
  • Coefficient of explanatory variable is the average change in the response for a one unit change in the explanatory variable.
  1. One categorical explanatory variable.
  • Intercept is the average response when explanatory variable is zero. In this case it is the average response for the baseline/referece level.
  • Coefficient of an indicator variable is the difference in average response between the level of the indicator variable and the baseline/reference level.
  1. More than one explanatory variable.
  • Interpretations much the same as above, except when interpreting each variable, it is necessary to acknowledge that there are other variables in the model. So, adding something like, “With all other variables held fixed” or “Accounting for (name of other variable or variables)”.
  • Models with only main effects imply that the effect of a variable on the response is the same, regardless of values of other variable(s) in the model. But, this effect is diffferent than if the other variables were not included in the model.
  1. Models with interaction effects. Interpretations change depending on what types of variables are in the model, but models with interaction effects allow the relationship between each explanatory variable involved in the interaction and the response variable to differ depending on the value of the other explanatory variable involved in the interaction.

  2. (We haven’t actually discussed this, but we’re ready to!) Models with more than two variables. The most important piece is to always acknowledge the that you’ve accounted for other variables when interpreting specific coefficients.

Residuals & Model Assumptions

The residuals in linear models are assumed to follow some assumptions.

  1. They should be normally distributed. (Normality)
  2. They should have a mean of 0 consistently.(Mean zero/linearity)
  3. They should have constant variance, meaning how spread out they are should not vary depending on something else. (Constant variance)
  4. They should be independent.

Violations of these assumptions can cause problems when we want to make inferences, which we’ll want to do very soon. We can check many of these assumptions by looking at a plot of the residuals vs. fitted values and a histogram of the residuals.

Example: assumptions satisfied

Below I simulated some data to assure assumptions are met. Simulated means I created the data in a specific way to adhere to certain properties. Do not worry about the code.

set.seed(10)
x <- rnorm(500, mean = 400, sd = 200)
y <- 100 + 4*x + rnorm(500, mean = 0, sd = 300)

simdat <- tibble(x, y)

ggplot(simdat) +
  geom_point(aes(x = x, y = y)) +
  theme_minimal()

Let’s fit a model and look at some helpful plots.

sim_mod <- lm(y ~ x, data=simdat)
tidy(sim_mod)
augment(sim_mod) %>% 
  # plot residuals on y-axis and fitted values on x-axis
  ggplot(aes(x=.fitted, y=.resid)) + 
  geom_point() +
  # add a line that shows a "moving average" of the fitted values
  geom_smooth(se = FALSE) +
  # add a horizontal line at y = 0
  geom_hline(yintercept = 0, color = "red")

augment(sim_mod) %>% 
  ggplot(aes(x=.resid)) +
  geom_histogram(bins = 30) 

YOUR TURN!

  1. In the first plot, we can assess if the residuals have constant variance and mean zero. How? What might the plot look like if they didn’t?

  2. The second plot allows us to check the normality of the residuals. How? What might it look like if it weren’t normal?

  3. It is difficult to check for independence. Can you think of any scenarios where our data might not be independent?

It can be difficult to decide if the residual plot shows any “weird” patterns. Our eyeballs and brains are pretty good at seeing patterns where they don’t actually exist.

One way we can combat this is by looking at the plot from our model among a lineup of other plots where there is no pattern. If we can’t pick out the plot from our model, then likely there is no weird pattern.

I won’t ever ask you to write this code on your own, but I will explain what it is doing. First, we create 20 datasets that have .fitted and .resid. One of the datasets is our actual dataset, created using the augment() function. The other 19 are the same except the residuals are permuted (randomly mixed up).

The weird code in the output can be used to find out which .sample is the true data. Run it in the console after looking at the plots to find out if your guess what correct.

set.seed(155) # for reproducibility
# create a "lineup" of data from our model
# BUT, in each dataset, the residuals are permuted (mixed up)
mod_lineup <- lineup(null_permute(".resid"),
                 true = augment(sim_mod))
## decrypt("sD0f gCdC En JP2EdEPn 8j")
mod_lineup

Then, we look at all 20 .resid vs. .fitted plots. Can we pick out our residual plot from the lineup? If not, there’s likely nothing “weird” about it and our assumptions are probably satisfied (at least the ones we can check with this plot). If we CAN pick it out, then an assumption is likely violated.

ggplot(mod_lineup) +
  geom_point(aes(x = .fitted, y = .resid)) +
  facet_wrap(vars(.sample))

Examples: assumptions not satisfied

King County house data

Let’s try this with the King County house data. First, we read in the data and do some slight modifications.

kc_house_data2 <-
  house_prices %>% 
  filter(bedrooms<=5, bedrooms>0) %>% 
  mutate(grade_CAT = fct_relevel(ifelse(grade %in% "1":"7", "Low",
                                        ifelse(grade == "8", "Medium","High")),
                                     "Low", "Medium", "High"),
         age=2015-yr_built)

Next, fit the model price ~ sqft_living15 + age.

kc_2var <- lm(price ~ sqft_living15 + age, 
                   data=kc_house_data2)
tidy(kc_2var)

Now, let’s look at a plot of residuals vs. fitted values and a histogram of the residuals.

a1 <- augment(kc_2var) %>% 
  ggplot(aes(x=.fitted, y = .resid)) +
  geom_point(size = .5, alpha = .3) +
  geom_smooth(se=FALSE) +
  geom_hline(yintercept = 0, color = "darkred") +
  scale_y_continuous(labels = scales::comma) +
  labs(x = "Fitted Values", y = "Residuals")

a2 <- augment(kc_2var) %>% 
  ggplot(aes(x=.resid)) +
  geom_histogram(bins=50) +
  scale_x_continuous(labels = scales::comma)

a1 + a2

I will also create the lineup of residuals vs. fitted values. Can you pick out one that looks different? After you’ve looked and think you might have found it (if you think you can), you can copy and paste the decrypt() code in the console to see if it’s the one you thought.

set.seed(155) # for reproducibility

kc_lineup <- lineup(null_permute(".resid"),
                 true = augment(kc_2var))
## decrypt("sD0f gCdC En JP2EdEPn 8T")
#
#null_lm(price ~ sqft_living15 + age, method = 'rotate')
ggplot(kc_lineup) +
  geom_point(aes(x = .fitted, y = .resid),
             size = .5, alpha = .3) +
  facet_wrap(vars(.sample))

YOUR TURN!

What do you observe in these plots? Do any assumptions appear violated?

Walleye data

Let’s look at another plot. This is a new dataset that has the age and length of walleye (a type of fish), from the alr4 package.

ggplot(walleye) + 
  geom_jitter(aes(x = age, y = length)) +
  theme_minimal()

YOUR TURN!

  1. Fit a model that uses age to explain length.
  2. Look at the 2 residual plots we discussed and assess them.
  3. OPTIONAL CHALLENGE: Create a lineup and see if you can spot this model’s residual plot.

Correcting model assumptions with transformations

There are many sophisticated methods that can be used to fix problems with linear model assumptions. But a fairly simple solution that often works well is to transform variables. We will discuss two different transformations and apply them to the two examples from above.

Logarithmic transformation

When variables range across more than one order of magnitude in their values (ie. they have values in the 1,000’s AND 10,000’s or 100,000’s AND the 1,000,000’s), log transforming can often help fix non-constant variance. Let’s look at the following scatterplot matrix. I did a log base 2 transformation of both price and sqft_living15. What do you notice?

kc_house_data2 %>% 
  mutate(log2_price = log2(price),
         log2_sqft = log2(sqft_living15)) %>% 
  select(age, sqft_living15, log2_sqft, log2_price) %>% 
  ggpairs()

Now, let’s fit a model that uses log2_price as the response and age and log2_sqft as explanatory variables. First, I need to create a new dataset with these variables.

kc_house_log <- kc_house_data2 %>% 
  mutate(log2_price = log2(price),
         log2_sqft = log2(sqft_living15))

kc_log <- lm(log2_price ~ age + log2_sqft,
             data = kc_house_log)
tidy(kc_log)

YOUR TURN!

Use the residual plots to check the model assumptions. How do they look now?

Adding a polynomial term to the model

For the walleye data, the biggest problem seemed to be that the mean was not zero throughout the range of fitted values. This is because the relationship between age and length was not linear to begin with.

We can try to address this by adding a quadratic (or higher order polynomial) term to our model. I have done that below. Notice that you need to enclose the polynomial term in I().

walleye_quadratic <-  lm(length ~ age + I(age^2), 
                         data=walleye)
tidy(walleye_quadratic)

YOUR TURN!

  1. Plot this model on the scatterplot of age versus length.
  2. Create the two residual plots and evaluate them.
  3. Try interpreting the coefficients of the model.

Interpreting a model after transforming variables

Interpretation can get a bit tricky after transforming variables. We usually still prefer to interpret the model in the original units. When doing log transformations, it will be helpful to remember some rules of logs of exponents (see the help sheet on the moodle page). Below you will work through an example using the following model.

tidy(kc_log)

YOUR TURN!

  1. Write down the model equation in terms of price. That is, rather than having log2_price on the left hand side of the equation, price is on the left hand side.

  2. Using the equation from the previous step, how does an increase of 1 year in age of a home typically affect the price (with all other variables in the model held fixed)? It might be helpful to try an example.

  3. How does doubling square footage typically affect the price (with all other variables in the model held fixed)? There is a reason I’m asking this question in this way …

  4. Now build a more complex model with log2_price as the response. Add a categorical variable and/or interaction effect. How do you interpret the coefficients of a categorical variable? An interaction effect?

Rules of logs and exponents

In our class, anytime we use \(log\) without a subscript, it should be taken as the natural log, \(log_e\) or \(ln\).

In the rules below, \(a, b\) are numbers.

  • \(a^{log_a x} = x\)
  • \(log_a(a^x) = x\)
  • \(log_a (xy) = log_a(x) + log_a(y)\)
  • \(log_a(x^b) = b (log_a(x))\)
  • \(x^{a+b} = x^a x^b\)
  • \((x^a)^b = x^{ab}\)

R tips

  • exp(x) = \(e^x\)
  • log(x) = \(log_e(x)\)
  • base 2 and base 10 logarithms have special functions in R, log2() and log10(), respectively.
LS0tCnRpdGxlOiAiUmVzaWR1YWxzLCBBc3N1bXB0aW9ucywgYW5kIFRyYW5zZm9ybWF0aW9ucyIKb3V0cHV0OiAgCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShhbHI0KSAgICAgICAgIyBORVchIEZvciBkYXRhLiBMZXQgbWUga25vdyBpZiB5b3UgaGF2ZSBpc3N1ZXMgaW5zdGFsbGluZyB0aGlzIG9uZSkKbGlicmFyeShHR2FsbHkpICAgICAgIyBOZXcgLi4uIG1heWJlPyBGb3IgcGFpcndpc2UgcGxvdHMKbGlicmFyeShudWxsYWJvcikgICAgIyBmb3IgcGlja2luZyBvdXQgcmVzaWR1YWwgcGxvdHMgZnJvbSBhICJsaW5lLXVwIiBvZiBwbG90cwpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAjIGZvciBwbG90dGluZyBhbmQgc3VtbWFyaXppbmcKbGlicmFyeShtb2Rlcm5kaXZlKSAgIyBmb3IgbmljZSBtb2RlbCBvdXRwdXQKbGlicmFyeShicm9vbSkgICAgICAgIyBmb3IgbmljZSBtb2RlbCBvdXRwdXQgCmxpYnJhcnkocGF0Y2h3b3JrKSAgICMgZm9yIG5pY2VseSBvcmdhbml6aW5nIHBsb3RzCmxpYnJhcnkob3BlbmludHJvKSAgICMgZm9yIGRhdGEKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkgI2NoYW5nZXMgdGhlIHRoZW1lIG9mIGdncGxvdHMgdG8gdGhlbWVfbWluaW1hbCwgbXkgcGVyc29uYWwgZmF2b3JpdGUKYGBgCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1zdWNjZXNzIj4KPHN0cm9uZz5HT0FMOjwvc3Ryb25nPgoKQnkgdGhlIGVuZCBvZiB0aGVzZSBub3RlcyBhbmQgYWN0aXZpdGllcywgeW91IHNob3VsZCBiZSBhYmxlIHRvIHBlcmZvcm0gdGhlIGZvbGxvd2luZyB0YXNrcy4KCi0gICBDaGVjayBmb3Igbm9uLWNvbnN0YW50IHZhcmlhbmNlIGFuZCBsaW5lYXJpdHkgdXNpbmcgYSByZXNpZHVhbHMgdmVyc3VzIGZpdHRlZCB2YWx1ZXMgcGxvdC5cCi0gICBDaGVjayBmb3Igbm9ybWFsaXR5IGJ5IGxvb2tpbmcgYXQgYSBoaXN0b2dyYW0gb2YgdGhlIHJlc2lkdWFscy5cCi0gICBLbm93IHdoZW4gdG8gYWRkIGEgcG9seW5vbWlhbCB0ZXJtIHRvIGEgbW9kZWwgYW5kIGltcGxlbWVudCBpdC5cCi0gICBLbm93IGEgZmV3IGtleSBzY2VuYXJpb3Mgd2hlbiBsb2ctdHJhbnNmb3JtaW5nIHRoZSByZXNwb25zZSB2YXJpYWJsZSBhbmQvb3IgcHJlZGljdG9yIHZhcmlhYmxlcyBjb3VsZCBoZWxwIHNhdGlzZnkgbW9kZWwgYXNzdW1wdGlvbnMuXAotICAgSW50ZXJwcmV0IG1vZGVsIGNvZWZmaWNpZW50cyB3aXRoIGEgbG9nLXRyYW5zZm9ybWVkIHJlc3BvbnNlIHZhcmlhYmxlLgo8L2Rpdj4KCiMgUkVWSUVXOiBNb2RlbHMgd2UgaGF2ZSBleHBsb3JlZCBzbyBmYXIgLSBxdWFudGl0YXRpdmUgcmVzcG9uc2UgdmFyaWFibGUKCjAuICBObyBleHBsYW5hdG9yeSB2YXJpYWJsZXMuIEV2ZXJ5IHByZWRpY3RlZCB2YWx1ZSBpcyB0aGUgYXZlcmFnZSByZXNwb25zZS4KCjEuICBPbmUgcXVhbnRpdGF0aXZlIGV4cGxhbmF0b3J5IHZhcmlhYmxlLgoKLSAgIEludGVyY2VwdCBpcyB0aGUgYXZlcmFnZSByZXNwb25zZSB3aGVuIGV4cGxhbmF0b3J5IHZhcmlhYmxlIGlzIHplcm8uXAotICAgQ29lZmZpY2llbnQgb2YgZXhwbGFuYXRvcnkgdmFyaWFibGUgaXMgdGhlIGF2ZXJhZ2UgY2hhbmdlIGluIHRoZSByZXNwb25zZSBmb3IgYSBvbmUgdW5pdCBjaGFuZ2UgaW4gdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlLgoKMi4gIE9uZSBjYXRlZ29yaWNhbCBleHBsYW5hdG9yeSB2YXJpYWJsZS4KCi0gICBJbnRlcmNlcHQgaXMgdGhlIGF2ZXJhZ2UgcmVzcG9uc2Ugd2hlbiBleHBsYW5hdG9yeSB2YXJpYWJsZSBpcyB6ZXJvLiBJbiB0aGlzIGNhc2UgaXQgaXMgdGhlIGF2ZXJhZ2UgcmVzcG9uc2UgZm9yIHRoZSBiYXNlbGluZS9yZWZlcmVjZSBsZXZlbC5cCi0gICBDb2VmZmljaWVudCBvZiBhbiBpbmRpY2F0b3IgdmFyaWFibGUgaXMgdGhlIGRpZmZlcmVuY2UgaW4gYXZlcmFnZSByZXNwb25zZSBiZXR3ZWVuIHRoZSBsZXZlbCBvZiB0aGUgaW5kaWNhdG9yIHZhcmlhYmxlIGFuZCB0aGUgYmFzZWxpbmUvcmVmZXJlbmNlIGxldmVsLgoKMy4gIE1vcmUgdGhhbiBvbmUgZXhwbGFuYXRvcnkgdmFyaWFibGUuCgotICAgSW50ZXJwcmV0YXRpb25zIG11Y2ggdGhlIHNhbWUgYXMgYWJvdmUsIGV4Y2VwdCB3aGVuIGludGVycHJldGluZyBlYWNoIHZhcmlhYmxlLCBpdCBpcyBuZWNlc3NhcnkgdG8gYWNrbm93bGVkZ2UgdGhhdCB0aGVyZSBhcmUgb3RoZXIgdmFyaWFibGVzIGluIHRoZSBtb2RlbC4gU28sIGFkZGluZyBzb21ldGhpbmcgbGlrZSwgIldpdGggYWxsIG90aGVyIHZhcmlhYmxlcyBoZWxkIGZpeGVkIiBvciAiQWNjb3VudGluZyBmb3IgKG5hbWUgb2Ygb3RoZXIgdmFyaWFibGUgb3IgdmFyaWFibGVzKSIuCi0gICBNb2RlbHMgd2l0aCBvbmx5IG1haW4gZWZmZWN0cyBpbXBseSB0aGF0IHRoZSBlZmZlY3Qgb2YgYSB2YXJpYWJsZSBvbiB0aGUgcmVzcG9uc2UgaXMgdGhlIHNhbWUsIHJlZ2FyZGxlc3Mgb2YgdmFsdWVzIG9mIG90aGVyIHZhcmlhYmxlKHMpIGluIHRoZSBtb2RlbC4gQnV0LCB0aGlzIGVmZmVjdCBpcyBkaWZmZmVyZW50IHRoYW4gaWYgdGhlIG90aGVyIHZhcmlhYmxlcyB3ZXJlIG5vdCBpbmNsdWRlZCBpbiB0aGUgbW9kZWwuCgo0LiAgTW9kZWxzIHdpdGggaW50ZXJhY3Rpb24gZWZmZWN0cy4gSW50ZXJwcmV0YXRpb25zIGNoYW5nZSBkZXBlbmRpbmcgb24gd2hhdCB0eXBlcyBvZiB2YXJpYWJsZXMgYXJlIGluIHRoZSBtb2RlbCwgYnV0IG1vZGVscyB3aXRoIGludGVyYWN0aW9uIGVmZmVjdHMgYWxsb3cgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGVhY2ggZXhwbGFuYXRvcnkgdmFyaWFibGUgaW52b2x2ZWQgaW4gdGhlIGludGVyYWN0aW9uIGFuZCB0aGUgcmVzcG9uc2UgdmFyaWFibGUgdG8gZGlmZmVyIGRlcGVuZGluZyBvbiB0aGUgdmFsdWUgb2YgdGhlIG90aGVyIGV4cGxhbmF0b3J5IHZhcmlhYmxlIGludm9sdmVkIGluIHRoZSBpbnRlcmFjdGlvbi4KCjUuICAoV2UgaGF2ZW4ndCBhY3R1YWxseSBkaXNjdXNzZWQgdGhpcywgYnV0IHdlJ3JlIHJlYWR5IHRvISkgTW9kZWxzIHdpdGggbW9yZSB0aGFuIHR3byB2YXJpYWJsZXMuIFRoZSBtb3N0IGltcG9ydGFudCBwaWVjZSBpcyB0byBhbHdheXMgYWNrbm93bGVkZ2UgdGhlIHRoYXQgeW91J3ZlIGFjY291bnRlZCBmb3Igb3RoZXIgdmFyaWFibGVzIHdoZW4gaW50ZXJwcmV0aW5nIHNwZWNpZmljIGNvZWZmaWNpZW50cy4KCiMgUmVzaWR1YWxzICYgTW9kZWwgQXNzdW1wdGlvbnMKClRoZSByZXNpZHVhbHMgaW4gbGluZWFyIG1vZGVscyBhcmUgYXNzdW1lZCB0byBmb2xsb3cgc29tZSBhc3N1bXB0aW9ucy4KCjEuICBUaGV5IHNob3VsZCBiZSBub3JtYWxseSBkaXN0cmlidXRlZC4gKE5vcm1hbGl0eSkKMi4gIFRoZXkgc2hvdWxkIGhhdmUgYSBtZWFuIG9mIDAgY29uc2lzdGVudGx5LihNZWFuIHplcm8vbGluZWFyaXR5KQozLiAgVGhleSBzaG91bGQgaGF2ZSBjb25zdGFudCB2YXJpYW5jZSwgbWVhbmluZyBob3cgc3ByZWFkIG91dCB0aGV5IGFyZSBzaG91bGQgbm90IHZhcnkgZGVwZW5kaW5nIG9uIHNvbWV0aGluZyBlbHNlLiAoQ29uc3RhbnQgdmFyaWFuY2UpCjQuICBUaGV5IHNob3VsZCBiZSBpbmRlcGVuZGVudC4KClZpb2xhdGlvbnMgb2YgdGhlc2UgYXNzdW1wdGlvbnMgY2FuIGNhdXNlIHByb2JsZW1zIHdoZW4gd2Ugd2FudCB0byBtYWtlIGluZmVyZW5jZXMsIHdoaWNoIHdlJ2xsIHdhbnQgdG8gZG8gdmVyeSBzb29uLiBXZSBjYW4gY2hlY2sgbWFueSBvZiB0aGVzZSBhc3N1bXB0aW9ucyBieSBsb29raW5nIGF0IGEgcGxvdCBvZiB0aGUgcmVzaWR1YWxzIHZzLiBmaXR0ZWQgdmFsdWVzIGFuZCBhIGhpc3RvZ3JhbSBvZiB0aGUgcmVzaWR1YWxzLgoKIyMgRXhhbXBsZTogYXNzdW1wdGlvbnMgc2F0aXNmaWVkCgpCZWxvdyBJIHNpbXVsYXRlZCBzb21lIGRhdGEgdG8gYXNzdXJlIGFzc3VtcHRpb25zIGFyZSBtZXQuIFNpbXVsYXRlZCBtZWFucyBJIGNyZWF0ZWQgdGhlIGRhdGEgaW4gYSBzcGVjaWZpYyB3YXkgdG8gYWRoZXJlIHRvIGNlcnRhaW4gcHJvcGVydGllcy4gRG8gbm90IHdvcnJ5IGFib3V0IHRoZSBjb2RlLgoKYGBge3J9CnNldC5zZWVkKDEwKQp4IDwtIHJub3JtKDUwMCwgbWVhbiA9IDQwMCwgc2QgPSAyMDApCnkgPC0gMTAwICsgNCp4ICsgcm5vcm0oNTAwLCBtZWFuID0gMCwgc2QgPSAzMDApCgpzaW1kYXQgPC0gdGliYmxlKHgsIHkpCgpnZ3Bsb3Qoc2ltZGF0KSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgsIHkgPSB5KSkgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCkxldCdzIGZpdCBhIG1vZGVsIGFuZCBsb29rIGF0IHNvbWUgaGVscGZ1bCBwbG90cy4KCmBgYHtyfQpzaW1fbW9kIDwtIGxtKHkgfiB4LCBkYXRhPXNpbWRhdCkKdGlkeShzaW1fbW9kKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQphdWdtZW50KHNpbV9tb2QpICU+JSAKICAjIHBsb3QgcmVzaWR1YWxzIG9uIHktYXhpcyBhbmQgZml0dGVkIHZhbHVlcyBvbiB4LWF4aXMKICBnZ3Bsb3QoYWVzKHg9LmZpdHRlZCwgeT0ucmVzaWQpKSArIAogIGdlb21fcG9pbnQoKSArCiAgIyBhZGQgYSBsaW5lIHRoYXQgc2hvd3MgYSAibW92aW5nIGF2ZXJhZ2UiIG9mIHRoZSBmaXR0ZWQgdmFsdWVzCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkgKwogICMgYWRkIGEgaG9yaXpvbnRhbCBsaW5lIGF0IHkgPSAwCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAicmVkIikKYGBgCgpgYGB7cn0KYXVnbWVudChzaW1fbW9kKSAlPiUgCiAgZ2dwbG90KGFlcyh4PS5yZXNpZCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzApIApgYGAKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgo8c3Ryb25nPllPVVIgVFVSTiE8L3N0cm9uZz4KCjEuICBJbiB0aGUgZmlyc3QgcGxvdCwgd2UgY2FuIGFzc2VzcyBpZiB0aGUgcmVzaWR1YWxzIGhhdmUgY29uc3RhbnQgdmFyaWFuY2UgYW5kIG1lYW4gemVyby4gSG93PyBXaGF0IG1pZ2h0IHRoZSBwbG90IGxvb2sgbGlrZSBpZiB0aGV5IGRpZG4ndD9cCgoyLiAgVGhlIHNlY29uZCBwbG90IGFsbG93cyB1cyB0byBjaGVjayB0aGUgbm9ybWFsaXR5IG9mIHRoZSByZXNpZHVhbHMuIEhvdz8gV2hhdCBtaWdodCBpdCBsb29rIGxpa2UgaWYgaXQgd2VyZW4ndCBub3JtYWw/CgozLiAgSXQgaXMgZGlmZmljdWx0IHRvIGNoZWNrIGZvciBpbmRlcGVuZGVuY2UuIENhbiB5b3UgdGhpbmsgb2YgYW55IHNjZW5hcmlvcyB3aGVyZSBvdXIgZGF0YSBtaWdodCBub3QgYmUgaW5kZXBlbmRlbnQ/XAo8L2Rpdj4KCgpJdCBjYW4gYmUgZGlmZmljdWx0IHRvIGRlY2lkZSBpZiB0aGUgcmVzaWR1YWwgcGxvdCBzaG93cyBhbnkgIndlaXJkIiBwYXR0ZXJucy4gT3VyIGV5ZWJhbGxzIGFuZCBicmFpbnMgYXJlIHByZXR0eSBnb29kIGF0IHNlZWluZyBwYXR0ZXJucyB3aGVyZSB0aGV5IGRvbid0IGFjdHVhbGx5IGV4aXN0LiAKCk9uZSB3YXkgd2UgY2FuIGNvbWJhdCB0aGlzIGlzIGJ5IGxvb2tpbmcgYXQgdGhlIHBsb3QgZnJvbSBvdXIgbW9kZWwgYW1vbmcgYSBsaW5ldXAgb2Ygb3RoZXIgcGxvdHMgd2hlcmUgdGhlcmUgaXMgbm8gcGF0dGVybi4gSWYgd2UgY2FuJ3QgcGljayBvdXQgdGhlIHBsb3QgZnJvbSBvdXIgbW9kZWwsIHRoZW4gbGlrZWx5IHRoZXJlIGlzIG5vIHdlaXJkIHBhdHRlcm4uCgpJIHdvbid0IGV2ZXIgYXNrIHlvdSB0byB3cml0ZSB0aGlzIGNvZGUgb24geW91ciBvd24sIGJ1dCBJIHdpbGwgZXhwbGFpbiB3aGF0IGl0IGlzIGRvaW5nLiBGaXJzdCwgd2UgY3JlYXRlIDIwIGRhdGFzZXRzIHRoYXQgaGF2ZSBgLmZpdHRlZGAgYW5kIGAucmVzaWRgLiBPbmUgb2YgdGhlIGRhdGFzZXRzIGlzIG91ciBhY3R1YWwgZGF0YXNldCwgY3JlYXRlZCB1c2luZyB0aGUgYGF1Z21lbnQoKWAgZnVuY3Rpb24uIFRoZSBvdGhlciAxOSBhcmUgdGhlIHNhbWUgZXhjZXB0IHRoZSByZXNpZHVhbHMgYXJlIHBlcm11dGVkIChyYW5kb21seSBtaXhlZCB1cCkuCgpUaGUgd2VpcmQgY29kZSBpbiB0aGUgb3V0cHV0IGNhbiBiZSB1c2VkIHRvIGZpbmQgb3V0IHdoaWNoIGAuc2FtcGxlYCBpcyB0aGUgdHJ1ZSBkYXRhLiBSdW4gaXQgaW4gdGhlIGNvbnNvbGUgYWZ0ZXIgbG9va2luZyBhdCB0aGUgcGxvdHMgdG8gZmluZCBvdXQgaWYgeW91ciBndWVzcyB3aGF0IGNvcnJlY3QuCgpgYGB7cn0Kc2V0LnNlZWQoMTU1KSAjIGZvciByZXByb2R1Y2liaWxpdHkKIyBjcmVhdGUgYSAibGluZXVwIiBvZiBkYXRhIGZyb20gb3VyIG1vZGVsCiMgQlVULCBpbiBlYWNoIGRhdGFzZXQsIHRoZSByZXNpZHVhbHMgYXJlIHBlcm11dGVkIChtaXhlZCB1cCkKbW9kX2xpbmV1cCA8LSBsaW5ldXAobnVsbF9wZXJtdXRlKCIucmVzaWQiKSwKICAgICAgICAgICAgICAgICB0cnVlID0gYXVnbWVudChzaW1fbW9kKSkKCm1vZF9saW5ldXAKYGBgCgpUaGVuLCB3ZSBsb29rIGF0IGFsbCAyMCBgLnJlc2lkYCB2cy4gYC5maXR0ZWRgIHBsb3RzLiBDYW4gd2UgcGljayBvdXQgb3VyIHJlc2lkdWFsIHBsb3QgZnJvbSB0aGUgbGluZXVwPyBJZiBub3QsIHRoZXJlJ3MgbGlrZWx5IG5vdGhpbmcgIndlaXJkIiBhYm91dCBpdCBhbmQgb3VyIGFzc3VtcHRpb25zIGFyZSBwcm9iYWJseSBzYXRpc2ZpZWQgKGF0IGxlYXN0IHRoZSBvbmVzIHdlIGNhbiBjaGVjayB3aXRoIHRoaXMgcGxvdCkuIElmIHdlIENBTiBwaWNrIGl0IG91dCwgdGhlbiBhbiBhc3N1bXB0aW9uIGlzIGxpa2VseSB2aW9sYXRlZC4KCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpnZ3Bsb3QobW9kX2xpbmV1cCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSAuZml0dGVkLCB5ID0gLnJlc2lkKSkgKwogIGZhY2V0X3dyYXAodmFycyguc2FtcGxlKSkKYGBgCgoKIyMgRXhhbXBsZXM6IGFzc3VtcHRpb25zIG5vdCBzYXRpc2ZpZWQKCiMjIyBLaW5nIENvdW50eSBob3VzZSBkYXRhCgpMZXQncyB0cnkgdGhpcyB3aXRoIHRoZSBLaW5nIENvdW50eSBob3VzZSBkYXRhLiBGaXJzdCwgd2UgcmVhZCBpbiB0aGUgZGF0YSBhbmQgZG8gc29tZSBzbGlnaHQgbW9kaWZpY2F0aW9ucy4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQprY19ob3VzZV9kYXRhMiA8LQogIGhvdXNlX3ByaWNlcyAlPiUgCiAgZmlsdGVyKGJlZHJvb21zPD01LCBiZWRyb29tcz4wKSAlPiUgCiAgbXV0YXRlKGdyYWRlX0NBVCA9IGZjdF9yZWxldmVsKGlmZWxzZShncmFkZSAlaW4lICIxIjoiNyIsICJMb3ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyYWRlID09ICI4IiwgIk1lZGl1bSIsIkhpZ2giKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTG93IiwgIk1lZGl1bSIsICJIaWdoIiksCiAgICAgICAgIGFnZT0yMDE1LXlyX2J1aWx0KQpgYGAKCk5leHQsIGZpdCB0aGUgbW9kZWwgYHByaWNlIH4gc3FmdF9saXZpbmcxNSArIGFnZWAuCgpgYGB7cn0Ka2NfMnZhciA8LSBsbShwcmljZSB+IHNxZnRfbGl2aW5nMTUgKyBhZ2UsIAogICAgICAgICAgICAgICAgICAgZGF0YT1rY19ob3VzZV9kYXRhMikKdGlkeShrY18ydmFyKQpgYGAKCk5vdywgbGV0J3MgbG9vayBhdCBhIHBsb3Qgb2YgcmVzaWR1YWxzIHZzLiBmaXR0ZWQgdmFsdWVzIGFuZCBhIGhpc3RvZ3JhbSBvZiB0aGUgcmVzaWR1YWxzLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmExIDwtIGF1Z21lbnQoa2NfMnZhcikgJT4lIAogIGdncGxvdChhZXMoeD0uZml0dGVkLCB5ID0gLnJlc2lkKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IC41LCBhbHBoYSA9IC4zKSArCiAgZ2VvbV9zbW9vdGgoc2U9RkFMU0UpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBjb2xvciA9ICJkYXJrcmVkIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArCiAgbGFicyh4ID0gIkZpdHRlZCBWYWx1ZXMiLCB5ID0gIlJlc2lkdWFscyIpCgphMiA8LSBhdWdtZW50KGtjXzJ2YXIpICU+JSAKICBnZ3Bsb3QoYWVzKHg9LnJlc2lkKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9NTApICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkKCmExICsgYTIKYGBgCgpJIHdpbGwgYWxzbyBjcmVhdGUgdGhlIGxpbmV1cCBvZiByZXNpZHVhbHMgdnMuIGZpdHRlZCB2YWx1ZXMuIENhbiB5b3UgcGljayBvdXQgb25lIHRoYXQgbG9va3MgZGlmZmVyZW50PyBBZnRlciB5b3UndmUgbG9va2VkIGFuZCB0aGluayB5b3UgbWlnaHQgaGF2ZSBmb3VuZCBpdCAoaWYgeW91IHRoaW5rIHlvdSBjYW4pLCB5b3UgY2FuIGNvcHkgYW5kIHBhc3RlIHRoZSBgZGVjcnlwdCgpYCBjb2RlIGluIHRoZSBjb25zb2xlIHRvIHNlZSBpZiBpdCdzIHRoZSBvbmUgeW91IHRob3VnaHQuCgpgYGB7cn0Kc2V0LnNlZWQoMTU1KSAjIGZvciByZXByb2R1Y2liaWxpdHkKCmtjX2xpbmV1cCA8LSBsaW5ldXAobnVsbF9wZXJtdXRlKCIucmVzaWQiKSwKICAgICAgICAgICAgICAgICB0cnVlID0gYXVnbWVudChrY18ydmFyKSkKCgojCiNudWxsX2xtKHByaWNlIH4gc3FmdF9saXZpbmcxNSArIGFnZSwgbWV0aG9kID0gJ3JvdGF0ZScpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpnZ3Bsb3Qoa2NfbGluZXVwKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IC5maXR0ZWQsIHkgPSAucmVzaWQpLAogICAgICAgICAgICAgc2l6ZSA9IC41LCBhbHBoYSA9IC4zKSArCiAgZmFjZXRfd3JhcCh2YXJzKC5zYW1wbGUpKQpgYGAKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgo8c3Ryb25nPllPVVIgVFVSTiE8L3N0cm9uZz4KCldoYXQgZG8geW91IG9ic2VydmUgaW4gdGhlc2UgcGxvdHM/IERvIGFueSBhc3N1bXB0aW9ucyBhcHBlYXIgdmlvbGF0ZWQ/CjwvZGl2PgoKCiMjIyBXYWxsZXllIGRhdGEKCkxldCdzIGxvb2sgYXQgYW5vdGhlciBwbG90LiBUaGlzIGlzIGEgbmV3IGRhdGFzZXQgdGhhdCBoYXMgdGhlIGFnZSBhbmQgbGVuZ3RoIG9mIHdhbGxleWUgKGEgdHlwZSBvZiBmaXNoKSwgZnJvbSB0aGUgYGFscjRgIHBhY2thZ2UuCgpgYGB7cn0KZ2dwbG90KHdhbGxleWUpICsgCiAgZ2VvbV9qaXR0ZXIoYWVzKHggPSBhZ2UsIHkgPSBsZW5ndGgpKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+CjxzdHJvbmc+WU9VUiBUVVJOITwvc3Ryb25nPgoKMS4gRml0IGEgbW9kZWwgdGhhdCB1c2VzIGBhZ2VgIHRvIGV4cGxhaW4gYGxlbmd0aGAuCjIuIExvb2sgYXQgdGhlIDIgcmVzaWR1YWwgcGxvdHMgd2UgZGlzY3Vzc2VkIGFuZCBhc3Nlc3MgdGhlbS4KMy4gT1BUSU9OQUwgQ0hBTExFTkdFOiBDcmVhdGUgYSBsaW5ldXAgYW5kIHNlZSBpZiB5b3UgY2FuIHNwb3QgdGhpcyBtb2RlbCdzIHJlc2lkdWFsIHBsb3QuCjwvZGl2PgoKCiMgQ29ycmVjdGluZyBtb2RlbCBhc3N1bXB0aW9ucyB3aXRoIHRyYW5zZm9ybWF0aW9ucwoKVGhlcmUgYXJlIG1hbnkgc29waGlzdGljYXRlZCBtZXRob2RzIHRoYXQgY2FuIGJlIHVzZWQgdG8gZml4IHByb2JsZW1zIHdpdGggbGluZWFyIG1vZGVsIGFzc3VtcHRpb25zLiBCdXQgYSBmYWlybHkgc2ltcGxlIHNvbHV0aW9uIHRoYXQgb2Z0ZW4gd29ya3Mgd2VsbCBpcyB0byB0cmFuc2Zvcm0gdmFyaWFibGVzLiBXZSB3aWxsIGRpc2N1c3MgdHdvIGRpZmZlcmVudCB0cmFuc2Zvcm1hdGlvbnMgYW5kIGFwcGx5IHRoZW0gdG8gdGhlIHR3byBleGFtcGxlcyBmcm9tIGFib3ZlLgoKIyMgTG9nYXJpdGhtaWMgdHJhbnNmb3JtYXRpb24KCldoZW4gdmFyaWFibGVzIHJhbmdlIGFjcm9zcyBtb3JlIHRoYW4gb25lIG9yZGVyIG9mIG1hZ25pdHVkZSBpbiB0aGVpciB2YWx1ZXMgKGllLiB0aGV5IGhhdmUgdmFsdWVzIGluIHRoZSAxLDAwMCdzIEFORCAxMCwwMDAncyBvciAxMDAsMDAwJ3MgQU5EIHRoZSAxLDAwMCwwMDAncyksIGxvZyB0cmFuc2Zvcm1pbmcgY2FuIG9mdGVuIGhlbHAgZml4IG5vbi1jb25zdGFudCB2YXJpYW5jZS4gTGV0J3MgbG9vayBhdCB0aGUgZm9sbG93aW5nIHNjYXR0ZXJwbG90IG1hdHJpeC4gSSBkaWQgYSBsb2cgYmFzZSAyIHRyYW5zZm9ybWF0aW9uIG9mIGJvdGggKnByaWNlKiBhbmQgKnNxZnRfbGl2aW5nMTUqLiBXaGF0IGRvIHlvdSBub3RpY2U/CgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0Ka2NfaG91c2VfZGF0YTIgJT4lIAogIG11dGF0ZShsb2cyX3ByaWNlID0gbG9nMihwcmljZSksCiAgICAgICAgIGxvZzJfc3FmdCA9IGxvZzIoc3FmdF9saXZpbmcxNSkpICU+JSAKICBzZWxlY3QoYWdlLCBzcWZ0X2xpdmluZzE1LCBsb2cyX3NxZnQsIGxvZzJfcHJpY2UpICU+JSAKICBnZ3BhaXJzKCkKYGBgCgpOb3csIGxldCdzIGZpdCBhIG1vZGVsIHRoYXQgdXNlcyAqbG9nMl9wcmljZSogYXMgdGhlIHJlc3BvbnNlIGFuZCAqYWdlKiBhbmQgKmxvZzJfc3FmdCogYXMgZXhwbGFuYXRvcnkgdmFyaWFibGVzLiBGaXJzdCwgSSBuZWVkIHRvIGNyZWF0ZSBhIG5ldyBkYXRhc2V0IHdpdGggdGhlc2UgdmFyaWFibGVzLgoKYGBge3J9CmtjX2hvdXNlX2xvZyA8LSBrY19ob3VzZV9kYXRhMiAlPiUgCiAgbXV0YXRlKGxvZzJfcHJpY2UgPSBsb2cyKHByaWNlKSwKICAgICAgICAgbG9nMl9zcWZ0ID0gbG9nMihzcWZ0X2xpdmluZzE1KSkKCmtjX2xvZyA8LSBsbShsb2cyX3ByaWNlIH4gYWdlICsgbG9nMl9zcWZ0LAogICAgICAgICAgICAgZGF0YSA9IGtjX2hvdXNlX2xvZykKdGlkeShrY19sb2cpCmBgYAoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+CjxzdHJvbmc+WU9VUiBUVVJOITwvc3Ryb25nPgoKVXNlIHRoZSByZXNpZHVhbCBwbG90cyB0byBjaGVjayB0aGUgbW9kZWwgYXNzdW1wdGlvbnMuIEhvdyBkbyB0aGV5IGxvb2sgbm93Pwo8L2Rpdj4KCgojIyBBZGRpbmcgYSBwb2x5bm9taWFsIHRlcm0gdG8gdGhlIG1vZGVsCgpGb3IgdGhlIHdhbGxleWUgZGF0YSwgdGhlIGJpZ2dlc3QgcHJvYmxlbSBzZWVtZWQgdG8gYmUgdGhhdCB0aGUgbWVhbiB3YXMgbm90IHplcm8gdGhyb3VnaG91dCB0aGUgcmFuZ2Ugb2YgZml0dGVkIHZhbHVlcy4gVGhpcyBpcyBiZWNhdXNlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgYWdlYCBhbmQgYGxlbmd0aGAgd2FzIG5vdCBsaW5lYXIgdG8gYmVnaW4gd2l0aC4KCldlIGNhbiB0cnkgdG8gYWRkcmVzcyB0aGlzIGJ5IGFkZGluZyBhIHF1YWRyYXRpYyAob3IgaGlnaGVyIG9yZGVyIHBvbHlub21pYWwpIHRlcm0gdG8gb3VyIG1vZGVsLiBJIGhhdmUgZG9uZSB0aGF0IGJlbG93LiBOb3RpY2UgdGhhdCB5b3UgbmVlZCB0byBlbmNsb3NlIHRoZSBwb2x5bm9taWFsIHRlcm0gaW4gYEkoKWAuCgpgYGB7cn0Kd2FsbGV5ZV9xdWFkcmF0aWMgPC0gIGxtKGxlbmd0aCB+IGFnZSArIEkoYWdlXjIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9d2FsbGV5ZSkKdGlkeSh3YWxsZXllX3F1YWRyYXRpYykKYGBgCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4KPHN0cm9uZz5ZT1VSIFRVUk4hPC9zdHJvbmc+CgoxLiAgUGxvdCB0aGlzIG1vZGVsIG9uIHRoZSBzY2F0dGVycGxvdCBvZiBgYWdlYCB2ZXJzdXMgYGxlbmd0aGAuCjIuICBDcmVhdGUgdGhlIHR3byByZXNpZHVhbCBwbG90cyBhbmQgZXZhbHVhdGUgdGhlbS4KMy4gIFRyeSBpbnRlcnByZXRpbmcgdGhlIGNvZWZmaWNpZW50cyBvZiB0aGUgbW9kZWwuCjwvZGl2PgoKCiMjIEludGVycHJldGluZyBhIG1vZGVsIGFmdGVyIHRyYW5zZm9ybWluZyB2YXJpYWJsZXMKCkludGVycHJldGF0aW9uIGNhbiBnZXQgYSBiaXQgdHJpY2t5IGFmdGVyIHRyYW5zZm9ybWluZyB2YXJpYWJsZXMuIFdlIHVzdWFsbHkgc3RpbGwgcHJlZmVyIHRvIGludGVycHJldCB0aGUgbW9kZWwgaW4gdGhlIG9yaWdpbmFsIHVuaXRzLiBXaGVuIGRvaW5nIGxvZyB0cmFuc2Zvcm1hdGlvbnMsIGl0IHdpbGwgYmUgaGVscGZ1bCB0byByZW1lbWJlciBzb21lIHJ1bGVzIG9mIGxvZ3Mgb2YgZXhwb25lbnRzIChzZWUgdGhlIGhlbHAgc2hlZXQgb24gdGhlIG1vb2RsZSBwYWdlKS4gQmVsb3cgeW91IHdpbGwgd29yayB0aHJvdWdoIGFuIGV4YW1wbGUgdXNpbmcgdGhlIGZvbGxvd2luZyBtb2RlbC4KCmBgYHtyfQp0aWR5KGtjX2xvZykKYGBgCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4KPHN0cm9uZz5ZT1VSIFRVUk4hPC9zdHJvbmc+CgoxLiAgV3JpdGUgZG93biB0aGUgbW9kZWwgZXF1YXRpb24gaW4gdGVybXMgb2YgYHByaWNlYC4gVGhhdCBpcywgcmF0aGVyIHRoYW4gaGF2aW5nIGBsb2cyX3ByaWNlYCBvbiB0aGUgbGVmdCBoYW5kIHNpZGUgb2YgdGhlIGVxdWF0aW9uLCBgcHJpY2VgIGlzIG9uIHRoZSBsZWZ0IGhhbmQgc2lkZS4KCjIuICBVc2luZyB0aGUgZXF1YXRpb24gZnJvbSB0aGUgcHJldmlvdXMgc3RlcCwgaG93IGRvZXMgYW4gaW5jcmVhc2Ugb2YgMSB5ZWFyIGluIGFnZSBvZiBhIGhvbWUgdHlwaWNhbGx5IGFmZmVjdCB0aGUgcHJpY2UgKHdpdGggYWxsIG90aGVyIHZhcmlhYmxlcyBpbiB0aGUgbW9kZWwgaGVsZCBmaXhlZCk/IEl0IG1pZ2h0IGJlIGhlbHBmdWwgdG8gdHJ5IGFuIGV4YW1wbGUuCgozLiAgSG93IGRvZXMgZG91Ymxpbmcgc3F1YXJlIGZvb3RhZ2UgdHlwaWNhbGx5IGFmZmVjdCB0aGUgcHJpY2UgKHdpdGggYWxsIG90aGVyIHZhcmlhYmxlcyBpbiB0aGUgbW9kZWwgaGVsZCBmaXhlZCk/IFRoZXJlIGlzIGEgcmVhc29uIEknbSBhc2tpbmcgdGhpcyBxdWVzdGlvbiBpbiB0aGlzIHdheSAuLi4KCjQuICBOb3cgYnVpbGQgYSBtb3JlIGNvbXBsZXggbW9kZWwgd2l0aCAqbG9nMl9wcmljZSogYXMgdGhlIHJlc3BvbnNlLiBBZGQgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBhbmQvb3IgaW50ZXJhY3Rpb24gZWZmZWN0LiBIb3cgZG8geW91IGludGVycHJldCB0aGUgY29lZmZpY2llbnRzIG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGU/IEFuIGludGVyYWN0aW9uIGVmZmVjdD8KPC9kaXY+CgojIyBSdWxlcyBvZiBsb2dzIGFuZCBleHBvbmVudHMKCkluIG91ciBjbGFzcywgYW55dGltZSB3ZSB1c2UgJGxvZyQgd2l0aG91dCBhIHN1YnNjcmlwdCwgaXQgc2hvdWxkIGJlIHRha2VuIGFzIHRoZSBuYXR1cmFsIGxvZywgJGxvZ19lJCBvciAkbG4kLiAKCkluIHRoZSBydWxlcyBiZWxvdywgJGEsIGIkIGFyZSBudW1iZXJzLgoKKiAkYV57bG9nX2EgeH0gPSB4JAoqICRsb2dfYShhXngpID0geCQKKiAkbG9nX2EgKHh5KSA9IGxvZ19hKHgpICsgbG9nX2EoeSkkCiogJGxvZ19hKHheYikgPSBiIChsb2dfYSh4KSkkCiogJHhee2ErYn0gPSB4XmEgeF5iJAoqICQoeF5hKV5iID0geF57YWJ9JAoKUiB0aXBzCgoqIGBleHAoeClgID0gJGVeeCQKKiBgbG9nKHgpYCA9ICRsb2dfZSh4KSQKKiBiYXNlIDIgYW5kIGJhc2UgMTAgbG9nYXJpdGhtcyBoYXZlIHNwZWNpYWwgZnVuY3Rpb25zIGluIFIsIGBsb2cyKClgIGFuZCBgbG9nMTAoKWAsIHJlc3BlY3RpdmVseS4KCg==