1 Visualizing two variables

1.1 Vizualizing bivariate relationships

1.1.1 Scatterplots

Scatterplots are the most common and effective tools for visualizing the relationship between two numeric variables.

The ncbirths dataset is a random sample of 1,000 cases taken from a larger dataset collected in 2004. Each case describes the birth of a single child born in North Carolina, along with various characteristics of the child (e.g. birth weight, length of gestation, etc.), the child’s mother (e.g. age, weight gained during pregnancy, smoking habits, etc.) and the child’s father (e.g. age). You can view the help file for these data by running ?ncbirths in the console.

library(openintro)
library(ggplot2)
library(dplyr)
head(ncbirths)
# Scatterplot of weight vs. weeks
ggplot(ncbirths, aes(x = weeks, y = weight)) +
  geom_point()

1.1.2 Boxplots as discretized/conditioned scatterplots

If it is helpful, you can think of boxplots as scatterplots for which the variable on the x-axis has been discretized.

The cut() function takes two arguments: the continuous variable you want to discretize and the number of breaks that you want to make in that continuous variable in order to discretize it.

# Boxplot of weight vs. weeks
ggplot(data = ncbirths, 
       aes(x = cut(weeks, breaks = 5), y = weight)) + 
  geom_boxplot()

Note how the relationship no longer seems linear.

1.2 Characterizing bivariate relationships

1.2.1 Creating scatterplots

Creating scatterplots is simple and they are so useful that it is worthwhile to expose yourself to many examples. Over time, you will gain familiarity with the types of patterns that you see. You will begin to recognize how scatterplots can reveal the nature of the relationship between two variables.

We will be using several datasets listed below, available through the openintro package:

  • The mammals dataset contains information about 39 different species of mammals, including their body weight, brain weight, gestation time, and a few other variables.
  • The mlbBat10 dataset contains batting statistics for 1,199 Major League Baseball players during the 2010 season.
  • The bdims dataset contains body girth and skeletal diameter measurements for 507 physically active individuals.
  • The smoking dataset contains information on the smoking habits of 1,691 citizens of the United Kingdom.
head(mammals)
# Mammals scatterplot
ggplot(mammals, aes(x = BodyWt, y = BrainWt)) +
geom_point()

head(mlbBat10)
ggplot(mlbBat10, aes(x = OBP, y = SLG)) +
geom_point()

head(bdims)
# Body dimensions scatterplot
ggplot(bdims, aes(x = hgt, y = wgt, color = as.factor(sex))) +
geom_point()

head(smoking)
# Smoking scatterplot
ggplot(smoking, aes(x = age, y = amtWeekdays)) +
geom_point()

1.2.2 Transformations

The relationship between two variables may not be linear. In these cases we can sometimes see strange and even inscrutable patterns in a scatterplot of the data. Sometimes there really is no meaningful relationship between the two variables. Other times, a careful transformation of one or both of the variables can reveal a clear relationship.

Recall the bizarre pattern that we saw in the scatterplot between brain weight and body weight among mammals in a previous exercise. Can we use transformations to clarify this relationship?

ggplot2 provides several different mechanisms for viewing transformed relationships. The coord_trans() function transforms the coordinates of the plot. Alternatively, the scale_x_log10() and scale_y_log10() functions perform a base-10 log transformation of each axis. Note the differences in the appearance of the axes.

# Scatterplot with coord_trans()
ggplot(data = mammals, aes(x = BodyWt, y = BrainWt)) +
  geom_point() + 
  coord_trans(x = "log10", y = "log10")

# Scatterplot with scale_x_log10() and scale_y_log10()
ggplot(data = mammals, aes(x = BodyWt, y = BrainWt)) +
  geom_point() +
  scale_x_log10() +
  scale_y_log10()

1.3 Outliers

we will discuss how outliers can affect the results of a linear regression model and how we can deal with them. For now, it is enough to simply identify them and note how the relationship between two variables may change as a result of removing outliers.

Recall that in the baseball example earlier in the chapter, most of the points were clustered in the lower left corner of the plot, making it difficult to see the general pattern of the majority of the data. This difficulty was caused by a few outlying players whose on-base percentages (OBPs) were exceptionally high. These values are present in our dataset only because these players had very few batting opportunities.

Both OBP and SLG are known as rate statistics, since they measure the frequency of certain events (as opposed to their count). In order to compare these rates sensibly, it makes sense to include only players with a reasonable number of opportunities, so that these observed rates have the chance to approach their long-run frequencies.

In Major League Baseball, batters qualify for the batting title only if they have 3.1 plate appearances per game. This translates into roughly 502 plate appearances in a 162-game season. The mlbBat10 dataset does not include plate appearances as a variable, but we can use at-bats (AB) – which constitute a subset of plate appearances – as a proxy.

# Filter for AB greater than or equal to 200
ab_gt_200 <- mlbBat10 %>%
  filter(AB >= 200) 

# Scatterplot of SLG vs. OBP
ggplot(ab_gt_200, aes(x = OBP, y = SLG)) +
  geom_point()


# Identify the outlying player
ab_gt_200 %>%
  filter(OBP < 0.200)

2 Correlation

2.1 Quantifying the strength of bivariate relationships

2.1.1 Computing correlation

The cor(x, y) function will compute the Pearson product-moment correlation between variables, x and y. Since this quantity is symmetric with respect to x and y, it doesn’t matter in which order you put the variables.

At the same time, the cor() function is very conservative when it encounters missing data (e.g. NAs). The use argument allows you to override the default behavior of returning NA whenever any of the values encountered is NA. Setting the use argument to “pairwise.complete.obs” allows cor() to compute the correlation coefficient for those observations where the values of x and y are both not missing.

# Compute correlation
ncbirths %>%
  summarize(N = n(), r = cor(weight, mage))

# Compute correlation for all non-missing pairs
ncbirths %>%
  summarize(N = n(), r = cor(weight, mage, use = "pairwise.complete.obs"))

2.2 The Anscombe dataset

In 1973, Francis Anscombe famously created four datasets with remarkably similar numerical properties, but obviously different graphic relationships. The Anscombe dataset contains the x and y coordinates for these four datasets, along with a grouping variable, set, that distinguishes the quartet.

It may be helpful to remind yourself of the graphic relationship by viewing the four scatterplots:

Anscombe <- readRDS("anscombe.rds")
ggplot(data = Anscombe, aes(x = x, y = y)) +
  geom_point() +
  facet_wrap(~ set)

# Compute properties of Anscombe
Anscombe %>%
  group_by(set) %>%
  summarize(
    N = n(), 
    mean_of_x = mean(x), 
    std_dev_of_x = sd(x), 
    mean_of_y = mean(y), 
    std_dev_of_y = sd(y), 
    correlation_between_x_and_y = cor(x, y)
  )

Note that all of the measures are identical (ignoring rounding error) across the four different sets.

2.2.1 Perception of correlation

Estimating the value of the correlation coefficient between two quantities from their scatterplot can be tricky. Statisticians have shown that people’s perception of the strength of these relationships can be influenced by design choices like the x and y scales.

# Run this and look at the plot
ggplot(data = mlbBat10, aes(x = OBP, y = SLG)) +
  geom_point()


# Correlation for all baseball players
mlbBat10 %>%
  summarize(N = n(), r = cor(OBP, SLG))
# Run this and look at the plot
mlbBat10 %>% 
    filter(AB > 200) %>%
    ggplot(aes(x = OBP, y = SLG)) + 
    geom_point()


# Correlation for all players with at least 200 ABs
mlbBat10 %>%
  filter(AB >= 200) %>%
  summarize(N = n(), r = cor(x = OBP, y = SLG))
# Run this and look at the plot
ggplot(data = bdims, aes(x = hgt, y = wgt, color = factor(sex))) +
  geom_point() 


# Correlation of body dimensions
bdims %>%
  group_by(sex) %>%
  summarize(N = n(), r = cor(hgt, wgt))
# Run this and look at the plot
ggplot(data = mammals, aes(x = BodyWt, y = BrainWt)) +
  geom_point() + scale_x_log10() + scale_y_log10()


# Correlation among mammals, with and without log
mammals %>%
  summarize(N = n(), 
            r = cor(BodyWt, BrainWt), 
            r_log = cor(log(BodyWt), log(BrainWt)))

2.3 Interpretation of Correlation

High correlation does not imply causation.

2.4 Spurious correlations

2.4.1 Spurious correlation in random data

Statisticians must always be skeptical of potentially spurious correlations. Human beings are very good at seeing patterns in data, sometimes when the patterns themselves are actually just random noise. To illustrate how easy it can be to fall into this trap, we will look for patterns in truly random data.

The noise dataset contains 20 sets of x and y variables drawn at random from a standard normal distribution. Each set, denoted as z, has 50 observations of x, y pairs. Do you see any pairs of variables that might be meaningfully correlated? Are all of the correlation coefficients close to zero?

noise <- readRDS("noise.rds")
# Create faceted scatterplot
ggplot(noise, aes(x, y)) +
  geom_point() +
  facet_wrap(~z)




# Compute correlations for each dataset
noise_summary <- noise %>%
  group_by(z) %>%
  summarize(N = n(), spurious_cor = cor(x, y))

# Isolate sets with correlations above 0.2 in absolute strength
noise_summary %>%
  filter((spurious_cor) > 0.2)

3 Simple linear regression

3.1 Visualization of Linear Models

3.1.1 The “best fit” line

The simple linear regression model for a numeric response as a function of a numeric explanatory variable can be visualized on the corresponding scatterplot by a straight line. This is a “best fit” line that cuts through the data in a way that minimizes the distance between the line and the data points.

We might consider linear regression to be a specific example of a larger class of smooth models. The geom_smooth() function allows you to draw such models over a scatterplot of the data itself. This technique is known as visualizing the model in the data space. The method argument to geom_smooth() allows you to specify what class of smooth model you want to see. Since we are exploring linear models, we’ll set this argument to the value “lm”.

Note that geom_smooth() also takes an se argument that controls the standard error, which we will ignore for now.

# Scatterplot with regression line
ggplot(data = bdims, aes(x = hgt, y = wgt)) + 
  geom_point( ) + 
  geom_smooth(method = "lm", se = FALSE)

3.1.2 Uniqueness of least squares regression line

The least squares criterion implies that the slope of the regression line is unique. In practice, the slope is computed by R. In this exercise, we will experiment with trying to find the optimal value for the regression slope for weight as a function of height in the bdims dataset via trial-and-error.

To help, we’ve built a custom function for you called add_line(), which takes a single argument: the proposed slope coefficient.

add_line <- function(my_slope){
  bdims_summary <- bdims %>%
    summarize(N = n(), r = cor(hgt, wgt),
              mean_hgt = mean(hgt), mean_wgt = mean(wgt),
              sd_hgt = sd(hgt), sd_wgt = sd(wgt)) %>%
    mutate(true_slope = r * sd_wgt / sd_hgt, 
           true_intercept = mean_wgt - true_slope * mean_hgt)
  p <- ggplot(data = bdims, aes(x = hgt, y = wgt)) + 
    geom_point() + 
    geom_point(data = bdims_summary, 
               aes(x = mean_hgt, y = mean_wgt), 
               color = "red", size = 3)
  
  my_data <- bdims_summary %>%
    mutate(my_slope = my_slope, 
           my_intercept = mean_wgt - my_slope * mean_hgt)
  p + geom_abline(data = my_data, 
                  aes(intercept = my_intercept, slope = my_slope), color = "dodgerblue")
}
# Estimate optimal value of my_slope
add_line(my_slope = 1)

3.2 Understanding Linear Models

\(Y = β_{0}+β_{1}⋅X+ϵ\), where \(ϵ∼N(0,σ_{ϵ})\).

3.2.1 Fitting a linear model “by hand”

Recall the simple linear regression model:

\(Y=b_{0}+b_{1}⋅X\)

Two facts enable you to compute the slope b1 and intercept b0 of a simple linear regression model from some basic summary statistics.

First, the slope can be defined as:

\(b_{1}=r_{X},_{Y}⋅\frac{s_{Y}}{s_{X}}\)

where \(r_{X},_{Y}\) represents the correlation (cor()) of \(X\) and \(Y\) and $sX and sY represent the standard deviation (sd()) of X and Y, respectively.

Second, the point (\(\tilde{x}\), \(\tilde{y}\)) is always on the least squares regression line, where \(\tilde{x}\) and \(\tilde{y}\) denote the average of x and y, respectively.

The bdims_summary data frame contains all of the information you need to compute the slope and intercept of the least squares regression line for body weight (\(Y\)) as a function of height (\(X\)). You might need to do some algebra to solve for \(b_{0}\)!

bdims_summary <- bdims %>%
   summarize(N = n(), r = cor(hgt, wgt),
             mean_hgt = mean(hgt), mean_wgt = mean(wgt),
             sd_hgt = sd(hgt), sd_wgt = sd(wgt))
bdims_summary
# Add slope and intercept
bdims_summary %>%
  mutate(slope = r*(sd_wgt/sd_hgt), 
         intercept = mean_wgt - (slope*mean_hgt))

3.3 Regression vs. regression to the mean

3.3.1 Regression to the mean

Regression to the mean is a concept attributed to Sir Francis Galton. The basic idea is that extreme random observations will tend to be less extreme upon a second trial. This is simply due to chance alone. While “regression to the mean” and “linear regression” are not the same thing, we will examine them together.

One way to see the effects of regression to the mean is to compare the heights of parents to their children’s heights. While it is true that tall mothers and fathers tend to have tall children, those children tend to be less tall than their parents, relative to average. That is, fathers who are 3 inches taller than the average father tend to have children who may be taller than average, but by less than 3 inches.

The Galton_men and Galton_women datasets contain data originally collected by Galton himself in the 1880s on the heights of men and women, respectively, along with their parents’ heights.

Compare the slope of the regression line to the slope of the diagonal line. What does this tell you?

Galton_men <- readRDS("Galton_men.rds")
Galton_women <- readRDS("Galton_women.rds")
# Height of children vs. height of father
ggplot(data = Galton_men, aes(x = father, y = height)) +
  geom_point() + 
  geom_abline(slope = 1, intercept = 0) + 
  geom_smooth(method = "lm", se = FALSE)


# Height of children vs. height of mother
ggplot(data = Galton_women, aes(x = mother, y = height)) +
  geom_point() + 
  geom_abline(slope = 1, intercept = 0) + 
  geom_smooth(method = "lm", se = FALSE)

Because the slope of the regression line is smaller than 1 (the slope of the diagonal line) for both males and females, we can verify Sir Francis Galton’s regression to the mean concept!

3.3.2 “Regression” in the parlance of our time

In an opinion piece about nepotism published in The New York Times in 2015, economist Seth Stephens-Davidowitz wrote that:

“Regression to the mean is so powerful that once-in-a-generation talent basically never sires once-in-a-generation talent. It explains why Michael Jordan’s sons were middling college basketball players and Jakob Dylan wrote two good songs. It is why there are no American parent-child pairs among Hall of Fame players in any major professional sports league.”

The author is arguing that “Because of regression to the mean, an outstanding basketball player is likely to have sons that are good at basketball, but not as good as him.”

4 Interpreting regression models

4.1 Interpretation of Regression

4.1.1 Coefficients

Recall that the fitted model for the poverty rate of U.S. counties as a function of high school graduation rate is:

\(\hat{poverty}=64.594−0.591⋅hs\_grad\)

Among U.S. counties, each additional percentage point increase in the high school graduation rate is associated with about a 0.591 percentage point decrease in the poverty rate.

4.1.2 Interpretation in context

A politician interpreting the relationship between poverty rates and high school graduation rates implores his constituents:

If we can lower the poverty rate by 59%, we’ll double the high school graduate rate in our county (i.e. raise it by 100%).

Which mistakes in interpretation has the politician made?

  • Implying that the regression model establishes a cause-and-effect relationship.
  • Switching the role of the response and explanatory variables.
  • Confusing percentage change with percentage point change. press

4.1.3 Fitting simple linear models

While the geom_smooth(method = “lm”) function is useful for drawing linear models on a scatterplot, it doesn’t actually return the characteristics of the model. As suggested by that syntax, however, the function that creates linear models is lm(). This function generally takes two arguments:

  • A formula that specifies the model
  • A data argument for the data frame that contains the data you want to use to fit the model

The lm() function return a model object having class “lm”. This object contains lots of information about your regression model, including the data used to fit the model, the specification of the model, the fitted values and residuals, etc.

# Linear model for weight as a function of height
lm(wgt ~ hgt, data = bdims)

Call:
lm(formula = wgt ~ hgt, data = bdims)

Coefficients:
(Intercept)          hgt  
   -105.011        1.018  
# Linear model for SLG as a function of OBP
lm(SLG ~ OBP, data = mlbBat10)

Call:
lm(formula = SLG ~ OBP, data = mlbBat10)

Coefficients:
(Intercept)          OBP  
   0.009407     1.110323  
# Log-linear model for body weight as a function of brain weight
lm(log(BodyWt) ~ log(BrainWt), data = mammals)

Call:
lm(formula = log(BodyWt) ~ log(BrainWt), data = mammals)

Coefficients:
 (Intercept)  log(BrainWt)  
      -2.509         1.225  

4.1.4 Units and scale

In the previous examples, we fit two regression models:

\(\hat{wgt}=−105.011+1.018⋅hgt\)

and

\(\hat{SLG}=0.009+1.110⋅OBP\)

A person who is 170 cm tall is expected to weigh about 68 kg.

4.2 Your linear model object

4.2.1 The lm summary output

An “lm” object contains a host of information about the regression model that you fit. There are various ways of extracting different pieces of information.

The coef() function displays only the values of the coefficients. Conversely, the summary() function displays not only that information, but a bunch of other information, including the associated standard error and p-value for each coefficient, the \(R^{2}\), adjusted \(R^{2}\), and the residual standard error. The summary of an “lm” object in R is very similar to the output you would see in other statistical computing environments (e.g. Stata, SPSS, etc.)

mod <- lm(wgt ~ hgt, data = bdims)
# Show the coefficients
coef(mod)
(Intercept)         hgt 
-105.011254    1.017617 
# Show the full output
summary(mod)

Call:
lm(formula = wgt ~ hgt, data = bdims)

Residuals:
    Min      1Q  Median      3Q     Max 
-18.743  -6.402  -1.231   5.059  41.103 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -105.01125    7.53941  -13.93   <2e-16 ***
hgt            1.01762    0.04399   23.14   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.308 on 505 degrees of freedom
Multiple R-squared:  0.5145,    Adjusted R-squared:  0.5136 
F-statistic: 535.2 on 1 and 505 DF,  p-value: < 2.2e-16

4.2.2 Fitted values and residuals

Once we have fit a regression model, we are often interested in the fitted values (\(\hat{y}_{i}\)) and the residuals (\(e_{i}\)), where i indexes the observations. Recall that:

\(e_{i}=y_{i}−\hat{y}_{i}\)

The least squares fitting procedure guarantees that the mean of the residuals is zero (n.b., numerical instability may result in the computed values not being exactly zero). At the same time, the mean of the fitted values must equal the mean of the response variable.

We will confirm these two mathematical facts by accessing the fitted values and residuals with the fitted.values() and residuals() functions, respectively, for the following model:

# Mean of weights equal to mean of fitted values?
mean(bdims$wgt) == mean(fitted.values(mod))
[1] TRUE
# Mean of the residuals
mean(residuals(mod))
[1] -1.538036e-15

4.2.3 Tidying your linear model

As you fit a regression model, there are some quantities (e.g. \(R^2\)) that apply to the model as a whole, while others apply to each observation (e.g. \(\hat{y}_{i}\)). If there are several of these per-observation quantities, it is sometimes convenient to attach them to the original data as new variables.

The augment() function from the broom package does exactly this. It takes a model object as an argument and returns a data frame that contains the data on which the model was fit, along with several quantities specific to the regression model, including the fitted values, residuals, leverage scores, and standardized residuals.

# Load broom
library(broom)

# Create bdims_tidy
bdims_tidy <- augment(mod)

# Glimpse the resulting data frame
glimpse(bdims_tidy)
Rows: 507
Columns: 9
$ wgt        <dbl> 65.6, 71.8, 80.7, 72.6, 78.8, 74.8, 86.4, 78.4, 62.0…
$ hgt        <dbl> 174.0, 175.3, 193.5, 186.5, 187.2, 181.5, 184.0, 184…
$ .fitted    <dbl> 72.05406, 73.37697, 91.89759, 84.77427, 85.48661, 79…
$ .se.fit    <dbl> 0.4320546, 0.4520060, 1.0667332, 0.7919264, 0.818347…
$ .resid     <dbl> -6.4540648, -1.5769666, -11.1975919, -12.1742745, -6…
$ .hat       <dbl> 0.002154570, 0.002358152, 0.013133942, 0.007238576, …
$ .sigma     <dbl> 9.312824, 9.317005, 9.303732, 9.301360, 9.312471, 9.…
$ .cooksd    <dbl> 5.201807e-04, 3.400330e-05, 9.758463e-03, 6.282074e-…
$ .std.resid <dbl> -0.69413418, -0.16961994, -1.21098084, -1.31269063, …

4.3 Using your linear model

4.3.1 Making predictions

The fitted.values() function or the augment()-ed data frame provides us with the fitted values for the observations that were in the original data. However, once we have fit the model, we may want to compute expected values for observations that were not present in the data on which the model was fit. These types of predictions are called out-of-sample.

The ben data frame contains a height and weight observation for one person. The mod object contains the fitted model for weight as a function of height for the observations in the bdims dataset. We can use the predict() function to generate expected values for the weight of new individuals. We must pass the data frame of new observations through the newdata argument.

ben <- data.frame("wgt" = 74.8, "hgt" = 182.8)
# Print ben
ben

# Predict the weight of ben
predict(mod, ben)
       1 
81.00909 

Note that the data frame ben has variables with the exact same names as those in the fitted model.

4.3.2 Adding a regression line to a plot manually

The geom_smooth() function makes it easy to add a simple linear regression line to a scatterplot of the corresponding variables. And in fact, there are more complicated regression models that can be visualized in the data space with geom_smooth(). However, there may still be times when we will want to add regression lines to our scatterplot manually. To do this, we will use the geom_abline() function, which takes slope and intercept arguments. Naturally, we have to compute those values ahead of time, but we already saw how to do this (e.g. using coef()).

The coefs data frame contains the model estimates retrieved from coef(). Passing this to geom_abline() as the data argument will enable you to draw a straight line on your scatterplot.

library(data.table)
coefs <- data.frame(coef(mod))
rnames <- rownames(coefs)
coefs <- coefs %>% 
  transpose()

colnames(coefs) <- rnames
# Add the line to the scatterplot
ggplot(data = bdims, aes(x = hgt, y = wgt)) + 
  geom_point() + 
  geom_abline(data = coefs, 
              aes(intercept = `(Intercept)`, slope = hgt),  
              color = "dodgerblue")

5 Model Fit

5.1 Assessing Model Fit

5.1.1 RMSE

The residual standard error reported for the regression model for poverty rate of U.S. counties in terms of high school graduation rate is 4.67. What does this mean?

The typical difference between the observed poverty rate and the poverty rate predicted by the model is about 4.67 percentage points.

5.1.2 Standard error of residuals

One way to assess strength of fit is to consider how far off the model is for a typical case. That is, for some observations, the fitted value will be very close to the actual value, while for others it will not. The magnitude of a typical residual can give us a sense of generally how close our estimates are.

However, recall that some of the residuals are positive, while others are negative. In fact, it is guaranteed by the least squares fitting procedure that the mean of the residuals is zero. Thus, it makes more sense to compute the square root of the mean squared residual, or root mean squared error (\(RMSE\)). R calls this quantity the residual standard error.

To make this estimate unbiased, you have to divide the sum of the squared residuals by the degrees of freedom in the model. Thus:

\(RMSE = \sqrt\frac{\sum_{i}e^2_{i}}{d.f}=\sqrt\frac{SSE}{d.f}\)

You can recover the residuals from mod with residuals(), and the degrees of freedom with df.residual().

# View summary of model
summary(mod)

Call:
lm(formula = wgt ~ hgt, data = bdims)

Residuals:
    Min      1Q  Median      3Q     Max 
-18.743  -6.402  -1.231   5.059  41.103 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -105.01125    7.53941  -13.93   <2e-16 ***
hgt            1.01762    0.04399   23.14   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.308 on 505 degrees of freedom
Multiple R-squared:  0.5145,    Adjusted R-squared:  0.5136 
F-statistic: 535.2 on 1 and 505 DF,  p-value: < 2.2e-16
# Compute the mean of the residuals
mean(residuals(mod))
[1] -1.538036e-15
# Compute RMSE
sqrt(sum(residuals(mod)^2) / df.residual(mod))
[1] 9.30804

5.2 Comparing model fits

5.2.1 Assessing simple linear model fit

The coefficient of determination (\(R^2\)), can be computed as

\(R^2 = 1 − \frac{SSE}{SST} = 1 − \frac{Var(e)}{Var(y)}\)

where \(e\) is the vector of residuals and \(y\) is the response variable. This gives us the interpretation of \(R^2\) as the percentage of the variability in the response that is explained by the model, since the residuals are the part of that variability that remains unexplained by the model.

# View model summary
summary(mod)

Call:
lm(formula = wgt ~ hgt, data = bdims)

Residuals:
    Min      1Q  Median      3Q     Max 
-18.743  -6.402  -1.231   5.059  41.103 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -105.01125    7.53941  -13.93   <2e-16 ***
hgt            1.01762    0.04399   23.14   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.308 on 505 degrees of freedom
Multiple R-squared:  0.5145,    Adjusted R-squared:  0.5136 
F-statistic: 535.2 on 1 and 505 DF,  p-value: < 2.2e-16
# Compute R-squared
bdims_tidy %>%
  summarize(var_y = var(wgt), var_e = var(.resid)) %>%
  mutate(R_squared = 1 - var_e / var_y)

This means that 51.4% of the variability in weight is explained by height.

5.2.2 Interpretation of R^2

The \(R^2\) reported for the regression model for poverty rate of U.S. counties in terms of high school graduation rate is 0.464.

lm(formula = poverty ~ hs_grad, data = countyComplete) %>%
  summary()

Call:
lm(formula = poverty ~ hs_grad, data = countyComplete)

Residuals:
    Min      1Q  Median      3Q     Max 
-18.035  -3.034  -0.434   2.405  36.874 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 64.59437    0.94619   68.27   <2e-16 ***
hs_grad     -0.59075    0.01134  -52.09   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.677 on 3141 degrees of freedom
Multiple R-squared:  0.4635,    Adjusted R-squared:  0.4633 
F-statistic:  2713 on 1 and 3141 DF,  p-value: < 2.2e-16

How should this result be interpreted?

46.4% of the variability in poverty rate among U.S. counties can be explained by high school graduation rate.

5.2.3 Linear vs. average

The \(R^2\) gives us a numerical measurement of the strength of fit relative to a null model based on the average of the response variable:

\(\hat{y}_{null} = \overline{y}\)

This model has an \(R^2\) of zero because \(SSE = SST\). That is, since the fitted values (\(\hat{y}_{null}\)) are all equal to the average (\(\overline{y}\)), the residual for each observation is the distance between that observation and the mean of the response. Since we can always fit the null model, it serves as a baseline against which all other models will be compared.

mean(bdims_tidy$wgt)
[1] 69.14753
library(cowplot)
plot_null <- ggplot(data = bdims_tidy, aes(x = hgt, y = wgt)) + 
  geom_point() + 
  geom_abline(data = coefs, 
              aes(intercept = mean(bdims_tidy$wgt), slope = 0),  
              color = "dodgerblue")+
  geom_segment(aes(x=hgt,y=wgt,xend=hgt,yend=mean(bdims_tidy$wgt)), color = "grey") +
  ggtitle("null")

plot_lm <- ggplot(data = bdims_tidy, aes(x = hgt, y = wgt)) + 
  geom_point() + 
  geom_abline(data = coefs, 
              aes(intercept = `(Intercept)`, slope = hgt),  
              color = "dodgerblue")+
  geom_segment(aes(x=hgt,y=wgt,xend=hgt,yend=.fitted), color = "grey") +
  ggtitle("slr")

plot_grid(plot_null, plot_lm)
Use of `bdims_tidy$wgt` is discouraged. Use `wgt` instead.

# Compute SSE for null model
mod_null %>%
  summarize(SSE = sum(.resid**2))

90123

# Compute SSE for regression model
mod_hgt %>%
  summarize(SSE = sum(.resid**2))
  

43753

5.3 Unusual points

5.3.1 Leverage

The leverage of an observation in a regression model is defined entirely in terms of the distance of that observation from the mean of the explanatory variable. That is, observations close to the mean of the explanatory variable have low leverage, while observations far from the mean of the explanatory variable have high leverage. Points of high leverage may or may not be influential.

The augment() function from the broom package will add the leverage scores (.hat) to a model data frame.

# Rank points of high leverage
mod %>%
  augment() %>%
  arrange(desc(.hat)) %>%
  head()

5.3.2 Influence

As noted previously, observations of high leverage may or may not be influential. The influence of an observation depends not only on its leverage, but also on the magnitude of its residual. Recall that while leverage only takes into account the explanatory variable (\(x\)), the residual depends on the response variable (\(y\)) and the fitted value (\(\hat{y}\).

Influential points are likely to have high leverage and deviate from the general relationship between the two variables. We measure influence using Cook’s distance, which incorporates both the leverage and residual of each observation.

# Rank influential points
mod %>%
  augment() %>%
  arrange(desc(.cooksd)) %>%
  head()

5.4 Dealing with Outliers

5.4.1 Removing outliers

Observations can be outliers for a number of different reasons. Statisticians must always be careful—and more importantly, transparent—when dealing with outliers. Sometimes, a better model fit can be achieved by simply removing outliers and re-fitting the model. However, one must have strong justification for doing this. A desire to have a higher \(R^2\) is not a good enough reason!

In the mlbBat10 data, the outlier with an OBP of 0.550 is Bobby Scales, an infielder who had four hits in 13 at-bats for the Chicago Cubs. Scales also walked seven times, resulting in his unusually high OBP. The justification for removing Scales here is weak. While his performance was unusual, there is nothing to suggest that it is not a valid data point, nor is there a good reason to think that somehow we will learn more about Major League Baseball players by excluding him.

Nevertheless, we can demonstrate how removing him will affect our model.

# Create nontrivial_players
nontrivial_players <- mlbBat10 %>%
  filter(AB >=10 &OBP < 0.500)


# Fit model to new data
mod_cleaner <- lm(SLG~OBP, nontrivial_players)

# View model summary
summary(mod_cleaner)

Call:
lm(formula = SLG ~ OBP, data = nontrivial_players)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.31383 -0.04165 -0.00261  0.03992  0.35819 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.043326   0.009823  -4.411 1.18e-05 ***
OBP          1.345816   0.033012  40.768  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.07011 on 734 degrees of freedom
Multiple R-squared:  0.6937,    Adjusted R-squared:  0.6932 
F-statistic:  1662 on 1 and 734 DF,  p-value: < 2.2e-16
# Visualize new model
ggplot(data = nontrivial_players, aes(x = OBP, y = SLG)) +
  geom_point() +
  geom_smooth(method = "lm")

5.4.2 High leverage points

Not all points of high leverage are influential. While the high leverage observation corresponding to Bobby Scales in the previous exercise is influential, the three observations for players with OBP and SLG values of 0 are not influential.

This is because they happen to lie right near the regression anyway. Thus, while their extremely low OBP gives them the power to exert influence over the slope of the regression line, their low SLG prevents them from using it.

# Rank high leverage points
mod %>%
  augment() %>%
  arrange(desc(.hat), (.cooksd)) %>%
  head()
LS0tCnRpdGxlOiAiQ29ycmVsYXRpb24gYW5kIFJlZ3Jlc3Npb24gaW4gUiIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgCnRvY19kZXB0aDogMwotLS0KIyBWaXN1YWxpemluZyB0d28gdmFyaWFibGVzCgojIyBWaXp1YWxpemluZyBiaXZhcmlhdGUgcmVsYXRpb25zaGlwcwoKIyMjIFNjYXR0ZXJwbG90cwoKU2NhdHRlcnBsb3RzIGFyZSB0aGUgbW9zdCBjb21tb24gYW5kIGVmZmVjdGl2ZSB0b29scyBmb3IgdmlzdWFsaXppbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byBudW1lcmljIHZhcmlhYmxlcy4KClRoZSBuY2JpcnRocyBkYXRhc2V0IGlzIGEgcmFuZG9tIHNhbXBsZSBvZiAxLDAwMCBjYXNlcyB0YWtlbiBmcm9tIGEgbGFyZ2VyIGRhdGFzZXQgY29sbGVjdGVkIGluIDIwMDQuIEVhY2ggY2FzZSBkZXNjcmliZXMgdGhlIGJpcnRoIG9mIGEgc2luZ2xlIGNoaWxkIGJvcm4gaW4gTm9ydGggQ2Fyb2xpbmEsIGFsb25nIHdpdGggdmFyaW91cyBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIGNoaWxkIChlLmcuIGJpcnRoIHdlaWdodCwgbGVuZ3RoIG9mIGdlc3RhdGlvbiwgZXRjLiksIHRoZSBjaGlsZCdzIG1vdGhlciAoZS5nLiBhZ2UsIHdlaWdodCBnYWluZWQgZHVyaW5nIHByZWduYW5jeSwgc21va2luZyBoYWJpdHMsIGV0Yy4pIGFuZCB0aGUgY2hpbGQncyBmYXRoZXIgKGUuZy4gYWdlKS4gWW91IGNhbiB2aWV3IHRoZSBoZWxwIGZpbGUgZm9yIHRoZXNlIGRhdGEgYnkgcnVubmluZyA/bmNiaXJ0aHMgaW4gdGhlIGNvbnNvbGUuCmBgYHtyfQpsaWJyYXJ5KG9wZW5pbnRybykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpgYGAKYGBge3J9CmhlYWQobmNiaXJ0aHMpCmBgYApgYGB7cn0KIyBTY2F0dGVycGxvdCBvZiB3ZWlnaHQgdnMuIHdlZWtzCmdncGxvdChuY2JpcnRocywgYWVzKHggPSB3ZWVrcywgeSA9IHdlaWdodCkpICsKICBnZW9tX3BvaW50KCkKYGBgCiMjIyBCb3hwbG90cyBhcyBkaXNjcmV0aXplZC9jb25kaXRpb25lZCBzY2F0dGVycGxvdHMKCklmIGl0IGlzIGhlbHBmdWwsIHlvdSBjYW4gdGhpbmsgb2YgYm94cGxvdHMgYXMgc2NhdHRlcnBsb3RzIGZvciB3aGljaCB0aGUgdmFyaWFibGUgb24gdGhlIHgtYXhpcyBoYXMgYmVlbiBkaXNjcmV0aXplZC4KClRoZSBjdXQoKSBmdW5jdGlvbiB0YWtlcyB0d28gYXJndW1lbnRzOiB0aGUgY29udGludW91cyB2YXJpYWJsZSB5b3Ugd2FudCB0byBkaXNjcmV0aXplIGFuZCB0aGUgbnVtYmVyIG9mIGJyZWFrcyB0aGF0IHlvdSB3YW50IHRvIG1ha2UgaW4gdGhhdCBjb250aW51b3VzIHZhcmlhYmxlIGluIG9yZGVyIHRvIGRpc2NyZXRpemUgaXQuCmBgYHtyfQojIEJveHBsb3Qgb2Ygd2VpZ2h0IHZzLiB3ZWVrcwpnZ3Bsb3QoZGF0YSA9IG5jYmlydGhzLCAKICAgICAgIGFlcyh4ID0gY3V0KHdlZWtzLCBicmVha3MgPSA1KSwgeSA9IHdlaWdodCkpICsgCiAgZ2VvbV9ib3hwbG90KCkKYGBgCk5vdGUgaG93IHRoZSByZWxhdGlvbnNoaXAgbm8gbG9uZ2VyIHNlZW1zIGxpbmVhci4KCiMjIENoYXJhY3Rlcml6aW5nIGJpdmFyaWF0ZSByZWxhdGlvbnNoaXBzCgojIyMgQ3JlYXRpbmcgc2NhdHRlcnBsb3RzCgpDcmVhdGluZyBzY2F0dGVycGxvdHMgaXMgc2ltcGxlIGFuZCB0aGV5IGFyZSBzbyB1c2VmdWwgdGhhdCBpdCBpcyB3b3J0aHdoaWxlIHRvIGV4cG9zZSB5b3Vyc2VsZiB0byBtYW55IGV4YW1wbGVzLiBPdmVyIHRpbWUsIHlvdSB3aWxsIGdhaW4gZmFtaWxpYXJpdHkgd2l0aCB0aGUgdHlwZXMgb2YgcGF0dGVybnMgdGhhdCB5b3Ugc2VlLiBZb3Ugd2lsbCBiZWdpbiB0byByZWNvZ25pemUgaG93IHNjYXR0ZXJwbG90cyBjYW4gcmV2ZWFsIHRoZSBuYXR1cmUgb2YgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byB2YXJpYWJsZXMuCgpXZSB3aWxsIGJlIHVzaW5nIHNldmVyYWwgZGF0YXNldHMgbGlzdGVkIGJlbG93LCBhdmFpbGFibGUgdGhyb3VnaCB0aGUgb3BlbmludHJvIHBhY2thZ2U6CgoqIFRoZSBtYW1tYWxzIGRhdGFzZXQgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgMzkgZGlmZmVyZW50IHNwZWNpZXMgb2YgbWFtbWFscywgaW5jbHVkaW5nIHRoZWlyIGJvZHkgd2VpZ2h0LCBicmFpbiB3ZWlnaHQsIGdlc3RhdGlvbiB0aW1lLCBhbmQgYSBmZXcgb3RoZXIgdmFyaWFibGVzLgoqIFRoZSBtbGJCYXQxMCBkYXRhc2V0IGNvbnRhaW5zIGJhdHRpbmcgc3RhdGlzdGljcyBmb3IgMSwxOTkgTWFqb3IgTGVhZ3VlIEJhc2ViYWxsIHBsYXllcnMgZHVyaW5nIHRoZSAyMDEwIHNlYXNvbi4KKiBUaGUgYmRpbXMgZGF0YXNldCBjb250YWlucyBib2R5IGdpcnRoIGFuZCBza2VsZXRhbCBkaWFtZXRlciBtZWFzdXJlbWVudHMgZm9yIDUwNyBwaHlzaWNhbGx5IGFjdGl2ZSBpbmRpdmlkdWFscy4KKiBUaGUgc21va2luZyBkYXRhc2V0IGNvbnRhaW5zIGluZm9ybWF0aW9uIG9uIHRoZSBzbW9raW5nIGhhYml0cyBvZiAxLDY5MSBjaXRpemVucyBvZiB0aGUgVW5pdGVkIEtpbmdkb20uCmBgYHtyfQpoZWFkKG1hbW1hbHMpCmBgYApgYGB7cn0KIyBNYW1tYWxzIHNjYXR0ZXJwbG90CmdncGxvdChtYW1tYWxzLCBhZXMoeCA9IEJvZHlXdCwgeSA9IEJyYWluV3QpKSArCmdlb21fcG9pbnQoKQpgYGAKYGBge3J9CmhlYWQobWxiQmF0MTApCmBgYApgYGB7cn0KZ2dwbG90KG1sYkJhdDEwLCBhZXMoeCA9IE9CUCwgeSA9IFNMRykpICsKZ2VvbV9wb2ludCgpCmBgYApgYGB7cn0KaGVhZChiZGltcykKYGBgCmBgYHtyfQojIEJvZHkgZGltZW5zaW9ucyBzY2F0dGVycGxvdApnZ3Bsb3QoYmRpbXMsIGFlcyh4ID0gaGd0LCB5ID0gd2d0LCBjb2xvciA9IGFzLmZhY3RvcihzZXgpKSkgKwpnZW9tX3BvaW50KCkKYGBgCmBgYHtyfQpoZWFkKHNtb2tpbmcpCmBgYApgYGB7cn0KIyBTbW9raW5nIHNjYXR0ZXJwbG90CmdncGxvdChzbW9raW5nLCBhZXMoeCA9IGFnZSwgeSA9IGFtdFdlZWtkYXlzKSkgKwpnZW9tX3BvaW50KCkKYGBgCiMjIyBUcmFuc2Zvcm1hdGlvbnMKVGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byB2YXJpYWJsZXMgbWF5IG5vdCBiZSBsaW5lYXIuIEluIHRoZXNlIGNhc2VzIHdlIGNhbiBzb21ldGltZXMgc2VlIHN0cmFuZ2UgYW5kIGV2ZW4gaW5zY3J1dGFibGUgcGF0dGVybnMgaW4gYSBzY2F0dGVycGxvdCBvZiB0aGUgZGF0YS4gU29tZXRpbWVzIHRoZXJlIHJlYWxseSBpcyBubyBtZWFuaW5nZnVsIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzLiBPdGhlciB0aW1lcywgYSBjYXJlZnVsIHRyYW5zZm9ybWF0aW9uIG9mIG9uZSBvciBib3RoIG9mIHRoZSB2YXJpYWJsZXMgY2FuIHJldmVhbCBhIGNsZWFyIHJlbGF0aW9uc2hpcC4KClJlY2FsbCB0aGUgYml6YXJyZSBwYXR0ZXJuIHRoYXQgd2Ugc2F3IGluIHRoZSBzY2F0dGVycGxvdCBiZXR3ZWVuIGJyYWluIHdlaWdodCBhbmQgYm9keSB3ZWlnaHQgYW1vbmcgbWFtbWFscyBpbiBhIHByZXZpb3VzIGV4ZXJjaXNlLiBDYW4gd2UgdXNlIHRyYW5zZm9ybWF0aW9ucyB0byBjbGFyaWZ5IHRoaXMgcmVsYXRpb25zaGlwPwoKZ2dwbG90MiBwcm92aWRlcyBzZXZlcmFsIGRpZmZlcmVudCBtZWNoYW5pc21zIGZvciB2aWV3aW5nIHRyYW5zZm9ybWVkIHJlbGF0aW9uc2hpcHMuIFRoZSBjb29yZF90cmFucygpIGZ1bmN0aW9uIHRyYW5zZm9ybXMgdGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBwbG90LiBBbHRlcm5hdGl2ZWx5LCB0aGUgc2NhbGVfeF9sb2cxMCgpIGFuZCBzY2FsZV95X2xvZzEwKCkgZnVuY3Rpb25zIHBlcmZvcm0gYSBiYXNlLTEwIGxvZyB0cmFuc2Zvcm1hdGlvbiBvZiBlYWNoIGF4aXMuIE5vdGUgdGhlIGRpZmZlcmVuY2VzIGluIHRoZSBhcHBlYXJhbmNlIG9mIHRoZSBheGVzLgpgYGB7cn0KIyBTY2F0dGVycGxvdCB3aXRoIGNvb3JkX3RyYW5zKCkKZ2dwbG90KGRhdGEgPSBtYW1tYWxzLCBhZXMoeCA9IEJvZHlXdCwgeSA9IEJyYWluV3QpKSArCiAgZ2VvbV9wb2ludCgpICsgCiAgY29vcmRfdHJhbnMoeCA9ICJsb2cxMCIsIHkgPSAibG9nMTAiKQpgYGAKYGBge3J9CiMgU2NhdHRlcnBsb3Qgd2l0aCBzY2FsZV94X2xvZzEwKCkgYW5kIHNjYWxlX3lfbG9nMTAoKQpnZ3Bsb3QoZGF0YSA9IG1hbW1hbHMsIGFlcyh4ID0gQm9keVd0LCB5ID0gQnJhaW5XdCkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMCgpCmBgYAojIyBPdXRsaWVycwoKd2Ugd2lsbCBkaXNjdXNzIGhvdyBvdXRsaWVycyBjYW4gYWZmZWN0IHRoZSByZXN1bHRzIG9mIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgYW5kIGhvdyB3ZSBjYW4gZGVhbCB3aXRoIHRoZW0uIEZvciBub3csIGl0IGlzIGVub3VnaCB0byBzaW1wbHkgaWRlbnRpZnkgdGhlbSBhbmQgbm90ZSBob3cgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byB2YXJpYWJsZXMgbWF5IGNoYW5nZSBhcyBhIHJlc3VsdCBvZiByZW1vdmluZyBvdXRsaWVycy4KClJlY2FsbCB0aGF0IGluIHRoZSBiYXNlYmFsbCBleGFtcGxlIGVhcmxpZXIgaW4gdGhlIGNoYXB0ZXIsIG1vc3Qgb2YgdGhlIHBvaW50cyB3ZXJlIGNsdXN0ZXJlZCBpbiB0aGUgbG93ZXIgbGVmdCBjb3JuZXIgb2YgdGhlIHBsb3QsIG1ha2luZyBpdCBkaWZmaWN1bHQgdG8gc2VlIHRoZSBnZW5lcmFsIHBhdHRlcm4gb2YgdGhlIG1ham9yaXR5IG9mIHRoZSBkYXRhLiBUaGlzIGRpZmZpY3VsdHkgd2FzIGNhdXNlZCBieSBhIGZldyBvdXRseWluZyBwbGF5ZXJzIHdob3NlIG9uLWJhc2UgcGVyY2VudGFnZXMgKE9CUHMpIHdlcmUgZXhjZXB0aW9uYWxseSBoaWdoLiBUaGVzZSB2YWx1ZXMgYXJlIHByZXNlbnQgaW4gb3VyIGRhdGFzZXQgb25seSBiZWNhdXNlIHRoZXNlIHBsYXllcnMgaGFkIHZlcnkgZmV3IGJhdHRpbmcgb3Bwb3J0dW5pdGllcy4KCkJvdGggT0JQIGFuZCBTTEcgYXJlIGtub3duIGFzIHJhdGUgc3RhdGlzdGljcywgc2luY2UgdGhleSBtZWFzdXJlIHRoZSBmcmVxdWVuY3kgb2YgY2VydGFpbiBldmVudHMgKGFzIG9wcG9zZWQgdG8gdGhlaXIgY291bnQpLiBJbiBvcmRlciB0byBjb21wYXJlIHRoZXNlIHJhdGVzIHNlbnNpYmx5LCBpdCBtYWtlcyBzZW5zZSB0byBpbmNsdWRlIG9ubHkgcGxheWVycyB3aXRoIGEgcmVhc29uYWJsZSBudW1iZXIgb2Ygb3Bwb3J0dW5pdGllcywgc28gdGhhdCB0aGVzZSBvYnNlcnZlZCByYXRlcyBoYXZlIHRoZSBjaGFuY2UgdG8gYXBwcm9hY2ggdGhlaXIgbG9uZy1ydW4gZnJlcXVlbmNpZXMuCgpJbiBNYWpvciBMZWFndWUgQmFzZWJhbGwsIGJhdHRlcnMgcXVhbGlmeSBmb3IgdGhlIGJhdHRpbmcgdGl0bGUgb25seSBpZiB0aGV5IGhhdmUgMy4xIHBsYXRlIGFwcGVhcmFuY2VzIHBlciBnYW1lLiBUaGlzIHRyYW5zbGF0ZXMgaW50byByb3VnaGx5IDUwMiBwbGF0ZSBhcHBlYXJhbmNlcyBpbiBhIDE2Mi1nYW1lIHNlYXNvbi4gVGhlIG1sYkJhdDEwIGRhdGFzZXQgZG9lcyBub3QgaW5jbHVkZSBwbGF0ZSBhcHBlYXJhbmNlcyBhcyBhIHZhcmlhYmxlLCBidXQgd2UgY2FuIHVzZSBhdC1iYXRzIChBQikgLS0gd2hpY2ggY29uc3RpdHV0ZSBhIHN1YnNldCBvZiBwbGF0ZSBhcHBlYXJhbmNlcyAtLSBhcyBhIHByb3h5LgpgYGB7cn0KIyBGaWx0ZXIgZm9yIEFCIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byAyMDAKYWJfZ3RfMjAwIDwtIG1sYkJhdDEwICU+JQogIGZpbHRlcihBQiA+PSAyMDApIAoKIyBTY2F0dGVycGxvdCBvZiBTTEcgdnMuIE9CUApnZ3Bsb3QoYWJfZ3RfMjAwLCBhZXMoeCA9IE9CUCwgeSA9IFNMRykpICsKICBnZW9tX3BvaW50KCkKCiMgSWRlbnRpZnkgdGhlIG91dGx5aW5nIHBsYXllcgphYl9ndF8yMDAgJT4lCiAgZmlsdGVyKE9CUCA8IDAuMjAwKQpgYGAKIyBDb3JyZWxhdGlvbgoKIyMgUXVhbnRpZnlpbmcgdGhlIHN0cmVuZ3RoIG9mIGJpdmFyaWF0ZSByZWxhdGlvbnNoaXBzCgojIyMgQ29tcHV0aW5nIGNvcnJlbGF0aW9uCgpUaGUgY29yKHgsIHkpIGZ1bmN0aW9uIHdpbGwgY29tcHV0ZSB0aGUgUGVhcnNvbiBwcm9kdWN0LW1vbWVudCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHZhcmlhYmxlcywgeCBhbmQgeS4gU2luY2UgdGhpcyBxdWFudGl0eSBpcyBzeW1tZXRyaWMgd2l0aCByZXNwZWN0IHRvIHggYW5kIHksIGl0IGRvZXNuJ3QgbWF0dGVyIGluIHdoaWNoIG9yZGVyIHlvdSBwdXQgdGhlIHZhcmlhYmxlcy4KCkF0IHRoZSBzYW1lIHRpbWUsIHRoZSBjb3IoKSBmdW5jdGlvbiBpcyB2ZXJ5IGNvbnNlcnZhdGl2ZSB3aGVuIGl0IGVuY291bnRlcnMgbWlzc2luZyBkYXRhIChlLmcuIE5BcykuIFRoZSB1c2UgYXJndW1lbnQgYWxsb3dzIHlvdSB0byBvdmVycmlkZSB0aGUgZGVmYXVsdCBiZWhhdmlvciBvZiByZXR1cm5pbmcgTkEgd2hlbmV2ZXIgYW55IG9mIHRoZSB2YWx1ZXMgZW5jb3VudGVyZWQgaXMgTkEuIFNldHRpbmcgdGhlIHVzZSBhcmd1bWVudCB0byAicGFpcndpc2UuY29tcGxldGUub2JzIiBhbGxvd3MgY29yKCkgdG8gY29tcHV0ZSB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgZm9yIHRob3NlIG9ic2VydmF0aW9ucyB3aGVyZSB0aGUgdmFsdWVzIG9mIHggYW5kIHkgYXJlIGJvdGggbm90IG1pc3NpbmcuCgpgYGB7cn0KIyBDb21wdXRlIGNvcnJlbGF0aW9uCm5jYmlydGhzICU+JQogIHN1bW1hcml6ZShOID0gbigpLCByID0gY29yKHdlaWdodCwgbWFnZSkpCgojIENvbXB1dGUgY29ycmVsYXRpb24gZm9yIGFsbCBub24tbWlzc2luZyBwYWlycwpuY2JpcnRocyAlPiUKICBzdW1tYXJpemUoTiA9IG4oKSwgciA9IGNvcih3ZWlnaHQsIG1hZ2UsIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKSkKYGBgCiMjIFRoZSBBbnNjb21iZSBkYXRhc2V0CgpJbiAxOTczLCBGcmFuY2lzIEFuc2NvbWJlIGZhbW91c2x5IGNyZWF0ZWQgZm91ciBkYXRhc2V0cyB3aXRoIHJlbWFya2FibHkgc2ltaWxhciBudW1lcmljYWwgcHJvcGVydGllcywgYnV0IG9idmlvdXNseSBkaWZmZXJlbnQgZ3JhcGhpYyByZWxhdGlvbnNoaXBzLiBUaGUgQW5zY29tYmUgZGF0YXNldCBjb250YWlucyB0aGUgeCBhbmQgeSBjb29yZGluYXRlcyBmb3IgdGhlc2UgZm91ciBkYXRhc2V0cywgYWxvbmcgd2l0aCBhIGdyb3VwaW5nIHZhcmlhYmxlLCBzZXQsIHRoYXQgZGlzdGluZ3Vpc2hlcyB0aGUgcXVhcnRldC4KCkl0IG1heSBiZSBoZWxwZnVsIHRvIHJlbWluZCB5b3Vyc2VsZiBvZiB0aGUgZ3JhcGhpYyByZWxhdGlvbnNoaXAgYnkgdmlld2luZyB0aGUgZm91ciBzY2F0dGVycGxvdHM6CmBgYHtyfQpBbnNjb21iZSA8LSByZWFkUkRTKCJhbnNjb21iZS5yZHMiKQpnZ3Bsb3QoZGF0YSA9IEFuc2NvbWJlLCBhZXMoeCA9IHgsIHkgPSB5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfd3JhcCh+IHNldCkKYGBgCmBgYHtyfQojIENvbXB1dGUgcHJvcGVydGllcyBvZiBBbnNjb21iZQpBbnNjb21iZSAlPiUKICBncm91cF9ieShzZXQpICU+JQogIHN1bW1hcml6ZSgKICAgIE4gPSBuKCksIAogICAgbWVhbl9vZl94ID0gbWVhbih4KSwgCiAgICBzdGRfZGV2X29mX3ggPSBzZCh4KSwgCiAgICBtZWFuX29mX3kgPSBtZWFuKHkpLCAKICAgIHN0ZF9kZXZfb2ZfeSA9IHNkKHkpLCAKICAgIGNvcnJlbGF0aW9uX2JldHdlZW5feF9hbmRfeSA9IGNvcih4LCB5KQogICkKYGBgCk5vdGUgdGhhdCBhbGwgb2YgdGhlIG1lYXN1cmVzIGFyZSBpZGVudGljYWwgKGlnbm9yaW5nIHJvdW5kaW5nIGVycm9yKSBhY3Jvc3MgdGhlIGZvdXIgZGlmZmVyZW50IHNldHMuCgojIyMgUGVyY2VwdGlvbiBvZiBjb3JyZWxhdGlvbgoKRXN0aW1hdGluZyB0aGUgdmFsdWUgb2YgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGJldHdlZW4gdHdvIHF1YW50aXRpZXMgZnJvbSB0aGVpciBzY2F0dGVycGxvdCBjYW4gYmUgdHJpY2t5LiBTdGF0aXN0aWNpYW5zIGhhdmUgc2hvd24gdGhhdCBwZW9wbGUncyBwZXJjZXB0aW9uIG9mIHRoZSBzdHJlbmd0aCBvZiB0aGVzZSByZWxhdGlvbnNoaXBzIGNhbiBiZSBpbmZsdWVuY2VkIGJ5IGRlc2lnbiBjaG9pY2VzIGxpa2UgdGhlIHggYW5kIHkgc2NhbGVzLgpgYGB7cn0KIyBSdW4gdGhpcyBhbmQgbG9vayBhdCB0aGUgcGxvdApnZ3Bsb3QoZGF0YSA9IG1sYkJhdDEwLCBhZXMoeCA9IE9CUCwgeSA9IFNMRykpICsKICBnZW9tX3BvaW50KCkKCiMgQ29ycmVsYXRpb24gZm9yIGFsbCBiYXNlYmFsbCBwbGF5ZXJzCm1sYkJhdDEwICU+JQogIHN1bW1hcml6ZShOID0gbigpLCByID0gY29yKE9CUCwgU0xHKSkKYGBgCmBgYHtyfQojIFJ1biB0aGlzIGFuZCBsb29rIGF0IHRoZSBwbG90Cm1sYkJhdDEwICU+JSAKICAgIGZpbHRlcihBQiA+IDIwMCkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBPQlAsIHkgPSBTTEcpKSArIAogICAgZ2VvbV9wb2ludCgpCgojIENvcnJlbGF0aW9uIGZvciBhbGwgcGxheWVycyB3aXRoIGF0IGxlYXN0IDIwMCBBQnMKbWxiQmF0MTAgJT4lCiAgZmlsdGVyKEFCID49IDIwMCkgJT4lCiAgc3VtbWFyaXplKE4gPSBuKCksIHIgPSBjb3IoeCA9IE9CUCwgeSA9IFNMRykpCmBgYApgYGB7cn0KIyBSdW4gdGhpcyBhbmQgbG9vayBhdCB0aGUgcGxvdApnZ3Bsb3QoZGF0YSA9IGJkaW1zLCBhZXMoeCA9IGhndCwgeSA9IHdndCwgY29sb3IgPSBmYWN0b3Ioc2V4KSkpICsKICBnZW9tX3BvaW50KCkgCgojIENvcnJlbGF0aW9uIG9mIGJvZHkgZGltZW5zaW9ucwpiZGltcyAlPiUKICBncm91cF9ieShzZXgpICU+JQogIHN1bW1hcml6ZShOID0gbigpLCByID0gY29yKGhndCwgd2d0KSkKYGBgCmBgYHtyfQojIFJ1biB0aGlzIGFuZCBsb29rIGF0IHRoZSBwbG90CmdncGxvdChkYXRhID0gbWFtbWFscywgYWVzKHggPSBCb2R5V3QsIHkgPSBCcmFpbld0KSkgKwogIGdlb21fcG9pbnQoKSArIHNjYWxlX3hfbG9nMTAoKSArIHNjYWxlX3lfbG9nMTAoKQoKIyBDb3JyZWxhdGlvbiBhbW9uZyBtYW1tYWxzLCB3aXRoIGFuZCB3aXRob3V0IGxvZwptYW1tYWxzICU+JQogIHN1bW1hcml6ZShOID0gbigpLCAKICAgICAgICAgICAgciA9IGNvcihCb2R5V3QsIEJyYWluV3QpLCAKICAgICAgICAgICAgcl9sb2cgPSBjb3IobG9nKEJvZHlXdCksIGxvZyhCcmFpbld0KSkpCmBgYAojIyBJbnRlcnByZXRhdGlvbiBvZiBDb3JyZWxhdGlvbgoKSGlnaCBjb3JyZWxhdGlvbiBkb2VzIG5vdCBpbXBseSBjYXVzYXRpb24uCgojIyBTcHVyaW91cyBjb3JyZWxhdGlvbnMKCiMjIyBTcHVyaW91cyBjb3JyZWxhdGlvbiBpbiByYW5kb20gZGF0YQoKU3RhdGlzdGljaWFucyBtdXN0IGFsd2F5cyBiZSBza2VwdGljYWwgb2YgcG90ZW50aWFsbHkgc3B1cmlvdXMgY29ycmVsYXRpb25zLiBIdW1hbiBiZWluZ3MgYXJlIHZlcnkgZ29vZCBhdCBzZWVpbmcgcGF0dGVybnMgaW4gZGF0YSwgc29tZXRpbWVzIHdoZW4gdGhlIHBhdHRlcm5zIHRoZW1zZWx2ZXMgYXJlIGFjdHVhbGx5IGp1c3QgcmFuZG9tIG5vaXNlLiBUbyBpbGx1c3RyYXRlIGhvdyBlYXN5IGl0IGNhbiBiZSB0byBmYWxsIGludG8gdGhpcyB0cmFwLCB3ZSB3aWxsIGxvb2sgZm9yIHBhdHRlcm5zIGluIHRydWx5IHJhbmRvbSBkYXRhLgoKVGhlIG5vaXNlIGRhdGFzZXQgY29udGFpbnMgMjAgc2V0cyBvZiB4IGFuZCB5IHZhcmlhYmxlcyBkcmF3biBhdCByYW5kb20gZnJvbSBhIHN0YW5kYXJkIG5vcm1hbCBkaXN0cmlidXRpb24uIEVhY2ggc2V0LCBkZW5vdGVkIGFzIHosIGhhcyA1MCBvYnNlcnZhdGlvbnMgb2YgeCwgeSBwYWlycy4gRG8geW91IHNlZSBhbnkgcGFpcnMgb2YgdmFyaWFibGVzIHRoYXQgbWlnaHQgYmUgbWVhbmluZ2Z1bGx5IGNvcnJlbGF0ZWQ/IEFyZSBhbGwgb2YgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyBjbG9zZSB0byB6ZXJvPwpgYGB7cn0Kbm9pc2UgPC0gcmVhZFJEUygibm9pc2UucmRzIikKYGBgCmBgYHtyfQojIENyZWF0ZSBmYWNldGVkIHNjYXR0ZXJwbG90CmdncGxvdChub2lzZSwgYWVzKHgsIHkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBmYWNldF93cmFwKH56KQoKCgojIENvbXB1dGUgY29ycmVsYXRpb25zIGZvciBlYWNoIGRhdGFzZXQKbm9pc2Vfc3VtbWFyeSA8LSBub2lzZSAlPiUKICBncm91cF9ieSh6KSAlPiUKICBzdW1tYXJpemUoTiA9IG4oKSwgc3B1cmlvdXNfY29yID0gY29yKHgsIHkpKQoKIyBJc29sYXRlIHNldHMgd2l0aCBjb3JyZWxhdGlvbnMgYWJvdmUgMC4yIGluIGFic29sdXRlIHN0cmVuZ3RoCm5vaXNlX3N1bW1hcnkgJT4lCiAgZmlsdGVyKChzcHVyaW91c19jb3IpID4gMC4yKQpgYGAKIyBTaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24KCiMjIFZpc3VhbGl6YXRpb24gb2YgTGluZWFyIE1vZGVscwoKIyMjIFRoZSAiYmVzdCBmaXQiIGxpbmUKClRoZSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgZm9yIGEgbnVtZXJpYyByZXNwb25zZSBhcyBhIGZ1bmN0aW9uIG9mIGEgbnVtZXJpYyBleHBsYW5hdG9yeSB2YXJpYWJsZSBjYW4gYmUgdmlzdWFsaXplZCBvbiB0aGUgY29ycmVzcG9uZGluZyBzY2F0dGVycGxvdCBieSBhIHN0cmFpZ2h0IGxpbmUuIFRoaXMgaXMgYSAiYmVzdCBmaXQiIGxpbmUgdGhhdCBjdXRzIHRocm91Z2ggdGhlIGRhdGEgaW4gYSB3YXkgdGhhdCBtaW5pbWl6ZXMgdGhlIGRpc3RhbmNlIGJldHdlZW4gdGhlIGxpbmUgYW5kIHRoZSBkYXRhIHBvaW50cy4KCldlIG1pZ2h0IGNvbnNpZGVyIGxpbmVhciByZWdyZXNzaW9uIHRvIGJlIGEgc3BlY2lmaWMgZXhhbXBsZSBvZiBhIGxhcmdlciBjbGFzcyBvZiBzbW9vdGggbW9kZWxzLiBUaGUgZ2VvbV9zbW9vdGgoKSBmdW5jdGlvbiBhbGxvd3MgeW91IHRvIGRyYXcgc3VjaCBtb2RlbHMgb3ZlciBhIHNjYXR0ZXJwbG90IG9mIHRoZSBkYXRhIGl0c2VsZi4gVGhpcyB0ZWNobmlxdWUgaXMga25vd24gYXMgdmlzdWFsaXppbmcgdGhlIG1vZGVsIGluIHRoZSBkYXRhIHNwYWNlLiBUaGUgbWV0aG9kIGFyZ3VtZW50IHRvIGdlb21fc21vb3RoKCkgYWxsb3dzIHlvdSB0byBzcGVjaWZ5IHdoYXQgY2xhc3Mgb2Ygc21vb3RoIG1vZGVsIHlvdSB3YW50IHRvIHNlZS4gU2luY2Ugd2UgYXJlIGV4cGxvcmluZyBsaW5lYXIgbW9kZWxzLCB3ZSdsbCBzZXQgdGhpcyBhcmd1bWVudCB0byB0aGUgdmFsdWUgImxtIi4KCk5vdGUgdGhhdCBnZW9tX3Ntb290aCgpIGFsc28gdGFrZXMgYW4gc2UgYXJndW1lbnQgdGhhdCBjb250cm9scyB0aGUgc3RhbmRhcmQgZXJyb3IsIHdoaWNoIHdlIHdpbGwgaWdub3JlIGZvciBub3cuCmBgYHtyfQojIFNjYXR0ZXJwbG90IHdpdGggcmVncmVzc2lvbiBsaW5lCmdncGxvdChkYXRhID0gYmRpbXMsIGFlcyh4ID0gaGd0LCB5ID0gd2d0KSkgKyAKICBnZW9tX3BvaW50KCApICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkKYGBgCiMjIyBVbmlxdWVuZXNzIG9mIGxlYXN0IHNxdWFyZXMgcmVncmVzc2lvbiBsaW5lCgpUaGUgbGVhc3Qgc3F1YXJlcyBjcml0ZXJpb24gaW1wbGllcyB0aGF0IHRoZSBzbG9wZSBvZiB0aGUgcmVncmVzc2lvbiBsaW5lIGlzIHVuaXF1ZS4gSW4gcHJhY3RpY2UsIHRoZSBzbG9wZSBpcyBjb21wdXRlZCBieSBSLiBJbiB0aGlzIGV4ZXJjaXNlLCB3ZSB3aWxsIGV4cGVyaW1lbnQgd2l0aCB0cnlpbmcgdG8gZmluZCB0aGUgb3B0aW1hbCB2YWx1ZSBmb3IgdGhlIHJlZ3Jlc3Npb24gc2xvcGUgZm9yIHdlaWdodCBhcyBhIGZ1bmN0aW9uIG9mIGhlaWdodCBpbiB0aGUgYmRpbXMgZGF0YXNldCB2aWEgdHJpYWwtYW5kLWVycm9yLgoKVG8gaGVscCwgd2UndmUgYnVpbHQgYSBjdXN0b20gZnVuY3Rpb24gZm9yIHlvdSBjYWxsZWQgYWRkX2xpbmUoKSwgd2hpY2ggdGFrZXMgYSBzaW5nbGUgYXJndW1lbnQ6IHRoZSBwcm9wb3NlZCBzbG9wZSBjb2VmZmljaWVudC4KCmBgYHtyfQphZGRfbGluZSA8LSBmdW5jdGlvbihteV9zbG9wZSl7CiAgYmRpbXNfc3VtbWFyeSA8LSBiZGltcyAlPiUKICAgIHN1bW1hcml6ZShOID0gbigpLCByID0gY29yKGhndCwgd2d0KSwKICAgICAgICAgICAgICBtZWFuX2hndCA9IG1lYW4oaGd0KSwgbWVhbl93Z3QgPSBtZWFuKHdndCksCiAgICAgICAgICAgICAgc2RfaGd0ID0gc2QoaGd0KSwgc2Rfd2d0ID0gc2Qod2d0KSkgJT4lCiAgICBtdXRhdGUodHJ1ZV9zbG9wZSA9IHIgKiBzZF93Z3QgLyBzZF9oZ3QsIAogICAgICAgICAgIHRydWVfaW50ZXJjZXB0ID0gbWVhbl93Z3QgLSB0cnVlX3Nsb3BlICogbWVhbl9oZ3QpCiAgcCA8LSBnZ3Bsb3QoZGF0YSA9IGJkaW1zLCBhZXMoeCA9IGhndCwgeSA9IHdndCkpICsgCiAgICBnZW9tX3BvaW50KCkgKyAKICAgIGdlb21fcG9pbnQoZGF0YSA9IGJkaW1zX3N1bW1hcnksIAogICAgICAgICAgICAgICBhZXMoeCA9IG1lYW5faGd0LCB5ID0gbWVhbl93Z3QpLCAKICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDMpCiAgCiAgbXlfZGF0YSA8LSBiZGltc19zdW1tYXJ5ICU+JQogICAgbXV0YXRlKG15X3Nsb3BlID0gbXlfc2xvcGUsIAogICAgICAgICAgIG15X2ludGVyY2VwdCA9IG1lYW5fd2d0IC0gbXlfc2xvcGUgKiBtZWFuX2hndCkKICBwICsgZ2VvbV9hYmxpbmUoZGF0YSA9IG15X2RhdGEsIAogICAgICAgICAgICAgICAgICBhZXMoaW50ZXJjZXB0ID0gbXlfaW50ZXJjZXB0LCBzbG9wZSA9IG15X3Nsb3BlKSwgY29sb3IgPSAiZG9kZ2VyYmx1ZSIpCn0KYGBgCmBgYHtyfQojIEVzdGltYXRlIG9wdGltYWwgdmFsdWUgb2YgbXlfc2xvcGUKYWRkX2xpbmUobXlfc2xvcGUgPSAxKQpgYGAKIyMgVW5kZXJzdGFuZGluZyBMaW5lYXIgTW9kZWxzCgoKJFkgPSDOsl97MH0rzrJfezF94ouFWCvPtSQsIHdoZXJlICTPteKIvE4oMCzPg197z7V9KSQuCgojIyMgRml0dGluZyBhIGxpbmVhciBtb2RlbCAiYnkgaGFuZCIKClJlY2FsbCB0aGUgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsOgoKJFk9Yl97MH0rYl97MX3ii4VYJAoKVHdvIGZhY3RzIGVuYWJsZSB5b3UgdG8gY29tcHV0ZSB0aGUgc2xvcGUgYjEgYW5kIGludGVyY2VwdCBiMCBvZiBhIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBmcm9tIHNvbWUgYmFzaWMgc3VtbWFyeSBzdGF0aXN0aWNzLgoKRmlyc3QsIHRoZSBzbG9wZSBjYW4gYmUgZGVmaW5lZCBhczoKCiRiX3sxfT1yX3tYfSxfe1l94ouFXGZyYWN7c197WX19e3Nfe1h9fSQKCndoZXJlICRyX3tYfSxfe1l9JCByZXByZXNlbnRzIHRoZSBjb3JyZWxhdGlvbiAoY29yKCkpIG9mICRYJCBhbmQgJFkkIGFuZCAkc1ggYW5kIHNZIHJlcHJlc2VudCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIChzZCgpKSBvZiBYIGFuZCBZLCByZXNwZWN0aXZlbHkuCgpTZWNvbmQsIHRoZSBwb2ludCAoJFx0aWxkZXt4fSQsICRcdGlsZGV7eX0kKSBpcyBhbHdheXMgb24gdGhlIGxlYXN0IHNxdWFyZXMgcmVncmVzc2lvbiBsaW5lLCB3aGVyZSAkXHRpbGRle3h9JCBhbmQgJFx0aWxkZXt5fSQgZGVub3RlIHRoZSBhdmVyYWdlIG9mIHggYW5kIHksIHJlc3BlY3RpdmVseS4KClRoZSBiZGltc19zdW1tYXJ5IGRhdGEgZnJhbWUgY29udGFpbnMgYWxsIG9mIHRoZSBpbmZvcm1hdGlvbiB5b3UgbmVlZCB0byBjb21wdXRlIHRoZSBzbG9wZSBhbmQgaW50ZXJjZXB0IG9mIHRoZSBsZWFzdCBzcXVhcmVzIHJlZ3Jlc3Npb24gbGluZSBmb3IgYm9keSB3ZWlnaHQgKCRZJCkgYXMgYSBmdW5jdGlvbiBvZiBoZWlnaHQgKCRYJCkuIFlvdSBtaWdodCBuZWVkIHRvIGRvIHNvbWUgYWxnZWJyYSB0byBzb2x2ZSBmb3IgJGJfezB9JCEKYGBge3J9CmJkaW1zX3N1bW1hcnkgPC0gYmRpbXMgJT4lCiAgIHN1bW1hcml6ZShOID0gbigpLCByID0gY29yKGhndCwgd2d0KSwKICAgICAgICAgICAgIG1lYW5faGd0ID0gbWVhbihoZ3QpLCBtZWFuX3dndCA9IG1lYW4od2d0KSwKICAgICAgICAgICAgIHNkX2hndCA9IHNkKGhndCksIHNkX3dndCA9IHNkKHdndCkpCmJkaW1zX3N1bW1hcnkKYGBgCmBgYHtyfQojIEFkZCBzbG9wZSBhbmQgaW50ZXJjZXB0CmJkaW1zX3N1bW1hcnkgJT4lCiAgbXV0YXRlKHNsb3BlID0gciooc2Rfd2d0L3NkX2hndCksIAogICAgICAgICBpbnRlcmNlcHQgPSBtZWFuX3dndCAtIChzbG9wZSptZWFuX2hndCkpCmBgYAojIyBSZWdyZXNzaW9uIHZzLiByZWdyZXNzaW9uIHRvIHRoZSBtZWFuCgojIyMgUmVncmVzc2lvbiB0byB0aGUgbWVhbgoKUmVncmVzc2lvbiB0byB0aGUgbWVhbiBpcyBhIGNvbmNlcHQgYXR0cmlidXRlZCB0byBTaXIgRnJhbmNpcyBHYWx0b24uIFRoZSBiYXNpYyBpZGVhIGlzIHRoYXQgZXh0cmVtZSByYW5kb20gb2JzZXJ2YXRpb25zIHdpbGwgdGVuZCB0byBiZSBsZXNzIGV4dHJlbWUgdXBvbiBhIHNlY29uZCB0cmlhbC4gVGhpcyBpcyBzaW1wbHkgZHVlIHRvIGNoYW5jZSBhbG9uZS4gV2hpbGUgInJlZ3Jlc3Npb24gdG8gdGhlIG1lYW4iIGFuZCAibGluZWFyIHJlZ3Jlc3Npb24iIGFyZSBub3QgdGhlIHNhbWUgdGhpbmcsIHdlIHdpbGwgZXhhbWluZSB0aGVtIHRvZ2V0aGVyLgoKT25lIHdheSB0byBzZWUgdGhlIGVmZmVjdHMgb2YgcmVncmVzc2lvbiB0byB0aGUgbWVhbiBpcyB0byBjb21wYXJlIHRoZSBoZWlnaHRzIG9mIHBhcmVudHMgdG8gdGhlaXIgY2hpbGRyZW4ncyBoZWlnaHRzLiBXaGlsZSBpdCBpcyB0cnVlIHRoYXQgdGFsbCBtb3RoZXJzIGFuZCBmYXRoZXJzIHRlbmQgdG8gaGF2ZSB0YWxsIGNoaWxkcmVuLCB0aG9zZSBjaGlsZHJlbiB0ZW5kIHRvIGJlIGxlc3MgdGFsbCB0aGFuIHRoZWlyIHBhcmVudHMsIHJlbGF0aXZlIHRvIGF2ZXJhZ2UuIFRoYXQgaXMsIGZhdGhlcnMgd2hvIGFyZSAzIGluY2hlcyB0YWxsZXIgdGhhbiB0aGUgYXZlcmFnZSBmYXRoZXIgdGVuZCB0byBoYXZlIGNoaWxkcmVuIHdobyBtYXkgYmUgdGFsbGVyIHRoYW4gYXZlcmFnZSwgYnV0IGJ5IGxlc3MgdGhhbiAzIGluY2hlcy4KClRoZSBHYWx0b25fbWVuIGFuZCBHYWx0b25fd29tZW4gZGF0YXNldHMgY29udGFpbiBkYXRhIG9yaWdpbmFsbHkgY29sbGVjdGVkIGJ5IEdhbHRvbiBoaW1zZWxmIGluIHRoZSAxODgwcyBvbiB0aGUgaGVpZ2h0cyBvZiBtZW4gYW5kIHdvbWVuLCByZXNwZWN0aXZlbHksIGFsb25nIHdpdGggdGhlaXIgcGFyZW50cycgaGVpZ2h0cy4KCkNvbXBhcmUgdGhlIHNsb3BlIG9mIHRoZSByZWdyZXNzaW9uIGxpbmUgdG8gdGhlIHNsb3BlIG9mIHRoZSBkaWFnb25hbCBsaW5lLiBXaGF0IGRvZXMgdGhpcyB0ZWxsIHlvdT8KYGBge3J9CkdhbHRvbl9tZW4gPC0gcmVhZFJEUygiR2FsdG9uX21lbi5yZHMiKQpHYWx0b25fd29tZW4gPC0gcmVhZFJEUygiR2FsdG9uX3dvbWVuLnJkcyIpCmBgYApgYGB7cn0KIyBIZWlnaHQgb2YgY2hpbGRyZW4gdnMuIGhlaWdodCBvZiBmYXRoZXIKZ2dwbG90KGRhdGEgPSBHYWx0b25fbWVuLCBhZXMoeCA9IGZhdGhlciwgeSA9IGhlaWdodCkpICsKICBnZW9tX3BvaW50KCkgKyAKICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDApICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkKCiMgSGVpZ2h0IG9mIGNoaWxkcmVuIHZzLiBoZWlnaHQgb2YgbW90aGVyCmdncGxvdChkYXRhID0gR2FsdG9uX3dvbWVuLCBhZXMoeCA9IG1vdGhlciwgeSA9IGhlaWdodCkpICsKICBnZW9tX3BvaW50KCkgKyAKICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDApICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkKYGBgCkJlY2F1c2UgdGhlIHNsb3BlIG9mIHRoZSByZWdyZXNzaW9uIGxpbmUgaXMgc21hbGxlciB0aGFuIDEgKHRoZSBzbG9wZSBvZiB0aGUgZGlhZ29uYWwgbGluZSkgZm9yIGJvdGggbWFsZXMgYW5kIGZlbWFsZXMsIHdlIGNhbiB2ZXJpZnkgU2lyIEZyYW5jaXMgR2FsdG9uJ3MgcmVncmVzc2lvbiB0byB0aGUgbWVhbiBjb25jZXB0IQoKIyMjICJSZWdyZXNzaW9uIiBpbiB0aGUgcGFybGFuY2Ugb2Ygb3VyIHRpbWUKCkluIGFuIG9waW5pb24gcGllY2UgYWJvdXQgbmVwb3Rpc20gW3B1Ymxpc2hlZCBpbiBUaGUgTmV3IFlvcmsgVGltZXMgaW4gMjAxNV0oaHR0cHM6Ly93d3cubnl0aW1lcy5jb20vMjAxNS8wMy8yMi9vcGluaW9uL3N1bmRheS9zZXRoLXN0ZXBoZW5zLWRhdmlkb3dpdHotanVzdC1ob3ctbmVwb3Rpc3RpYy1hcmUtd2UuaHRtbCksIGVjb25vbWlzdCBTZXRoIFN0ZXBoZW5zLURhdmlkb3dpdHogd3JvdGUgdGhhdDoKCj4gIlJlZ3Jlc3Npb24gdG8gdGhlIG1lYW4gaXMgc28gcG93ZXJmdWwgdGhhdCBvbmNlLWluLWEtZ2VuZXJhdGlvbiB0YWxlbnQgYmFzaWNhbGx5IG5ldmVyIHNpcmVzIG9uY2UtaW4tYS1nZW5lcmF0aW9uIHRhbGVudC4gSXQgZXhwbGFpbnMgd2h5IE1pY2hhZWwgSm9yZGFu4oCZcyBzb25zIHdlcmUgbWlkZGxpbmcgY29sbGVnZSBiYXNrZXRiYWxsIHBsYXllcnMgYW5kIEpha29iIER5bGFuIHdyb3RlIHR3byBnb29kIHNvbmdzLiBJdCBpcyB3aHkgdGhlcmUgYXJlIG5vIEFtZXJpY2FuIHBhcmVudC1jaGlsZCBwYWlycyBhbW9uZyBIYWxsIG9mIEZhbWUgcGxheWVycyBpbiBhbnkgbWFqb3IgcHJvZmVzc2lvbmFsIHNwb3J0cyBsZWFndWUuIgoKVGhlIGF1dGhvciBpcyBhcmd1aW5nIHRoYXQgIkJlY2F1c2Ugb2YgcmVncmVzc2lvbiB0byB0aGUgbWVhbiwgYW4gb3V0c3RhbmRpbmcgYmFza2V0YmFsbCBwbGF5ZXIgaXMgbGlrZWx5IHRvIGhhdmUgc29ucyB0aGF0IGFyZSBnb29kIGF0IGJhc2tldGJhbGwsIGJ1dCBub3QgYXMgZ29vZCBhcyBoaW0uIgoKIyBJbnRlcnByZXRpbmcgcmVncmVzc2lvbiBtb2RlbHMKCiMjIEludGVycHJldGF0aW9uIG9mIFJlZ3Jlc3Npb24KCgojIyMgQ29lZmZpY2llbnRzCgpSZWNhbGwgdGhhdCB0aGUgZml0dGVkIG1vZGVsIGZvciB0aGUgcG92ZXJ0eSByYXRlIG9mIFUuUy4gY291bnRpZXMgYXMgYSBmdW5jdGlvbiBvZiBoaWdoIHNjaG9vbCBncmFkdWF0aW9uIHJhdGUgaXM6CgokXGhhdHtwb3ZlcnR5fT02NC41OTTiiJIwLjU5MeKLhWhzXF9ncmFkJAoKQW1vbmcgVS5TLiBjb3VudGllcywgZWFjaCBhZGRpdGlvbmFsIHBlcmNlbnRhZ2UgcG9pbnQgaW5jcmVhc2UgaW4gdGhlIGhpZ2ggc2Nob29sIGdyYWR1YXRpb24gcmF0ZSBpcyBhc3NvY2lhdGVkIHdpdGggYWJvdXQgYSAwLjU5MSBwZXJjZW50YWdlIHBvaW50IGRlY3JlYXNlIGluIHRoZSBwb3ZlcnR5IHJhdGUuCgojIyMgSW50ZXJwcmV0YXRpb24gaW4gY29udGV4dAoKQSBwb2xpdGljaWFuIGludGVycHJldGluZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcG92ZXJ0eSByYXRlcyBhbmQgaGlnaCBzY2hvb2wgZ3JhZHVhdGlvbiByYXRlcyBpbXBsb3JlcyBoaXMgY29uc3RpdHVlbnRzOgoKPklmIHdlIGNhbiBsb3dlciB0aGUgcG92ZXJ0eSByYXRlIGJ5IDU5JSwgd2UnbGwgZG91YmxlIHRoZSBoaWdoIHNjaG9vbCBncmFkdWF0ZSByYXRlIGluIG91ciBjb3VudHkgKGkuZS4gcmFpc2UgaXQgYnkgMTAwJSkuCgpXaGljaCBtaXN0YWtlcyBpbiBpbnRlcnByZXRhdGlvbiBoYXMgdGhlIHBvbGl0aWNpYW4gbWFkZT8KCi0gSW1wbHlpbmcgdGhhdCB0aGUgcmVncmVzc2lvbiBtb2RlbCBlc3RhYmxpc2hlcyBhIGNhdXNlLWFuZC1lZmZlY3QgcmVsYXRpb25zaGlwLgotIFN3aXRjaGluZyB0aGUgcm9sZSBvZiB0aGUgcmVzcG9uc2UgYW5kIGV4cGxhbmF0b3J5IHZhcmlhYmxlcy4KLSBDb25mdXNpbmcgcGVyY2VudGFnZSBjaGFuZ2Ugd2l0aCBwZXJjZW50YWdlIHBvaW50IGNoYW5nZS4KcHJlc3MKCiMjIyBGaXR0aW5nIHNpbXBsZSBsaW5lYXIgbW9kZWxzCgpXaGlsZSB0aGUgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgZnVuY3Rpb24gaXMgdXNlZnVsIGZvciBkcmF3aW5nIGxpbmVhciBtb2RlbHMgb24gYSBzY2F0dGVycGxvdCwgaXQgZG9lc24ndCBhY3R1YWxseSByZXR1cm4gdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgbW9kZWwuIEFzIHN1Z2dlc3RlZCBieSB0aGF0IHN5bnRheCwgaG93ZXZlciwgdGhlIGZ1bmN0aW9uIHRoYXQgY3JlYXRlcyBsaW5lYXIgbW9kZWxzIGlzIGxtKCkuIFRoaXMgZnVuY3Rpb24gZ2VuZXJhbGx5IHRha2VzIHR3byBhcmd1bWVudHM6CgotIEEgZm9ybXVsYSB0aGF0IHNwZWNpZmllcyB0aGUgbW9kZWwKLSBBIGRhdGEgYXJndW1lbnQgZm9yIHRoZSBkYXRhIGZyYW1lIHRoYXQgY29udGFpbnMgdGhlIGRhdGEgeW91IHdhbnQgdG8gdXNlIHRvIGZpdCB0aGUgbW9kZWwKClRoZSBsbSgpIGZ1bmN0aW9uIHJldHVybiBhIG1vZGVsIG9iamVjdCBoYXZpbmcgY2xhc3MgImxtIi4gVGhpcyBvYmplY3QgY29udGFpbnMgbG90cyBvZiBpbmZvcm1hdGlvbiBhYm91dCB5b3VyIHJlZ3Jlc3Npb24gbW9kZWwsIGluY2x1ZGluZyB0aGUgZGF0YSB1c2VkIHRvIGZpdCB0aGUgbW9kZWwsIHRoZSBzcGVjaWZpY2F0aW9uIG9mIHRoZSBtb2RlbCwgdGhlIGZpdHRlZCB2YWx1ZXMgYW5kIHJlc2lkdWFscywgZXRjLgpgYGB7cn0KIyBMaW5lYXIgbW9kZWwgZm9yIHdlaWdodCBhcyBhIGZ1bmN0aW9uIG9mIGhlaWdodApsbSh3Z3QgfiBoZ3QsIGRhdGEgPSBiZGltcykKCiMgTGluZWFyIG1vZGVsIGZvciBTTEcgYXMgYSBmdW5jdGlvbiBvZiBPQlAKbG0oU0xHIH4gT0JQLCBkYXRhID0gbWxiQmF0MTApCgojIExvZy1saW5lYXIgbW9kZWwgZm9yIGJvZHkgd2VpZ2h0IGFzIGEgZnVuY3Rpb24gb2YgYnJhaW4gd2VpZ2h0CmxtKGxvZyhCb2R5V3QpIH4gbG9nKEJyYWluV3QpLCBkYXRhID0gbWFtbWFscykKYGBgCiMjIyBVbml0cyBhbmQgc2NhbGUKCkluIHRoZSBwcmV2aW91cyBleGFtcGxlcywgd2UgZml0IHR3byByZWdyZXNzaW9uIG1vZGVsczoKCiRcaGF0e3dndH094oiSMTA1LjAxMSsxLjAxOOKLhWhndCQKCmFuZAoKJFxoYXR7U0xHfT0wLjAwOSsxLjExMOKLhU9CUCQKCkEgcGVyc29uIHdobyBpcyAxNzAgY20gdGFsbCBpcyBleHBlY3RlZCB0byB3ZWlnaCBhYm91dCA2OCBrZy4KCiMjIFlvdXIgbGluZWFyIG1vZGVsIG9iamVjdAoKIyMjIFRoZSBsbSBzdW1tYXJ5IG91dHB1dAoKQW4gImxtIiBvYmplY3QgY29udGFpbnMgYSBob3N0IG9mIGluZm9ybWF0aW9uIGFib3V0IHRoZSByZWdyZXNzaW9uIG1vZGVsIHRoYXQgeW91IGZpdC4gVGhlcmUgYXJlIHZhcmlvdXMgd2F5cyBvZiBleHRyYWN0aW5nIGRpZmZlcmVudCBwaWVjZXMgb2YgaW5mb3JtYXRpb24uCgpUaGUgY29lZigpIGZ1bmN0aW9uIGRpc3BsYXlzIG9ubHkgdGhlIHZhbHVlcyBvZiB0aGUgY29lZmZpY2llbnRzLiBDb252ZXJzZWx5LCB0aGUgc3VtbWFyeSgpIGZ1bmN0aW9uIGRpc3BsYXlzIG5vdCBvbmx5IHRoYXQgaW5mb3JtYXRpb24sIGJ1dCBhIGJ1bmNoIG9mIG90aGVyIGluZm9ybWF0aW9uLCBpbmNsdWRpbmcgdGhlIGFzc29jaWF0ZWQgc3RhbmRhcmQgZXJyb3IgYW5kIHAtdmFsdWUgZm9yIGVhY2ggY29lZmZpY2llbnQsIHRoZSAkUl57Mn0kLCBhZGp1c3RlZCAkUl57Mn0kLCBhbmQgdGhlIHJlc2lkdWFsIHN0YW5kYXJkIGVycm9yLiBUaGUgc3VtbWFyeSBvZiBhbiAibG0iIG9iamVjdCBpbiBSIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgb3V0cHV0IHlvdSB3b3VsZCBzZWUgaW4gb3RoZXIgc3RhdGlzdGljYWwgY29tcHV0aW5nIGVudmlyb25tZW50cyAoZS5nLiBTdGF0YSwgU1BTUywgZXRjLikKYGBge3J9Cm1vZCA8LSBsbSh3Z3QgfiBoZ3QsIGRhdGEgPSBiZGltcykKYGBgCmBgYHtyfQojIFNob3cgdGhlIGNvZWZmaWNpZW50cwpjb2VmKG1vZCkKCiMgU2hvdyB0aGUgZnVsbCBvdXRwdXQKc3VtbWFyeShtb2QpCmBgYAojIyMgRml0dGVkIHZhbHVlcyBhbmQgcmVzaWR1YWxzCgpPbmNlIHdlIGhhdmUgZml0IGEgcmVncmVzc2lvbiBtb2RlbCwgd2UgYXJlIG9mdGVuIGludGVyZXN0ZWQgaW4gdGhlIGZpdHRlZCB2YWx1ZXMgKCRcaGF0e3l9X3tpfSQpIGFuZCB0aGUgcmVzaWR1YWxzICgkZV97aX0kKSwgd2hlcmUgaSBpbmRleGVzIHRoZSBvYnNlcnZhdGlvbnMuIFJlY2FsbCB0aGF0OgoKJGVfe2l9PXlfe2l94oiSXGhhdHt5fV97aX0kCgpUaGUgbGVhc3Qgc3F1YXJlcyBmaXR0aW5nIHByb2NlZHVyZSBndWFyYW50ZWVzIHRoYXQgdGhlIG1lYW4gb2YgdGhlIHJlc2lkdWFscyBpcyB6ZXJvIChuLmIuLCBudW1lcmljYWwgaW5zdGFiaWxpdHkgbWF5IHJlc3VsdCBpbiB0aGUgY29tcHV0ZWQgdmFsdWVzIG5vdCBiZWluZyBleGFjdGx5IHplcm8pLiBBdCB0aGUgc2FtZSB0aW1lLCB0aGUgbWVhbiBvZiB0aGUgZml0dGVkIHZhbHVlcyBtdXN0IGVxdWFsIHRoZSBtZWFuIG9mIHRoZSByZXNwb25zZSB2YXJpYWJsZS4KCldlIHdpbGwgY29uZmlybSB0aGVzZSB0d28gbWF0aGVtYXRpY2FsIGZhY3RzIGJ5IGFjY2Vzc2luZyB0aGUgZml0dGVkIHZhbHVlcyBhbmQgcmVzaWR1YWxzIHdpdGggdGhlIGZpdHRlZC52YWx1ZXMoKSBhbmQgcmVzaWR1YWxzKCkgZnVuY3Rpb25zLCByZXNwZWN0aXZlbHksIGZvciB0aGUgZm9sbG93aW5nIG1vZGVsOgpgYGB7cn0KIyBNZWFuIG9mIHdlaWdodHMgZXF1YWwgdG8gbWVhbiBvZiBmaXR0ZWQgdmFsdWVzPwptZWFuKGJkaW1zJHdndCkgPT0gbWVhbihmaXR0ZWQudmFsdWVzKG1vZCkpCgojIE1lYW4gb2YgdGhlIHJlc2lkdWFscwptZWFuKHJlc2lkdWFscyhtb2QpKQpgYGAKIyMjIFRpZHlpbmcgeW91ciBsaW5lYXIgbW9kZWwKCkFzIHlvdSBmaXQgYSByZWdyZXNzaW9uIG1vZGVsLCB0aGVyZSBhcmUgc29tZSBxdWFudGl0aWVzIChlLmcuICRSXjIkKSB0aGF0IGFwcGx5IHRvIHRoZSBtb2RlbCBhcyBhIHdob2xlLCB3aGlsZSBvdGhlcnMgYXBwbHkgdG8gZWFjaCBvYnNlcnZhdGlvbiAoZS5nLiAkXGhhdHt5fV97aX0kKS4gSWYgdGhlcmUgYXJlIHNldmVyYWwgb2YgdGhlc2UgcGVyLW9ic2VydmF0aW9uIHF1YW50aXRpZXMsIGl0IGlzIHNvbWV0aW1lcyBjb252ZW5pZW50IHRvIGF0dGFjaCB0aGVtIHRvIHRoZSBvcmlnaW5hbCBkYXRhIGFzIG5ldyB2YXJpYWJsZXMuCgpUaGUgYXVnbWVudCgpIGZ1bmN0aW9uIGZyb20gdGhlIGJyb29tIHBhY2thZ2UgZG9lcyBleGFjdGx5IHRoaXMuIEl0IHRha2VzIGEgbW9kZWwgb2JqZWN0IGFzIGFuIGFyZ3VtZW50IGFuZCByZXR1cm5zIGEgZGF0YSBmcmFtZSB0aGF0IGNvbnRhaW5zIHRoZSBkYXRhIG9uIHdoaWNoIHRoZSBtb2RlbCB3YXMgZml0LCBhbG9uZyB3aXRoIHNldmVyYWwgcXVhbnRpdGllcyBzcGVjaWZpYyB0byB0aGUgcmVncmVzc2lvbiBtb2RlbCwgaW5jbHVkaW5nIHRoZSBmaXR0ZWQgdmFsdWVzLCByZXNpZHVhbHMsIGxldmVyYWdlIHNjb3JlcywgYW5kIHN0YW5kYXJkaXplZCByZXNpZHVhbHMuCmBgYHtyfQojIExvYWQgYnJvb20KbGlicmFyeShicm9vbSkKCiMgQ3JlYXRlIGJkaW1zX3RpZHkKYmRpbXNfdGlkeSA8LSBhdWdtZW50KG1vZCkKCiMgR2xpbXBzZSB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUKZ2xpbXBzZShiZGltc190aWR5KQpgYGAKIyMgVXNpbmcgeW91ciBsaW5lYXIgbW9kZWwKCiMjIyBNYWtpbmcgcHJlZGljdGlvbnMKClRoZSBmaXR0ZWQudmFsdWVzKCkgZnVuY3Rpb24gb3IgdGhlIGF1Z21lbnQoKS1lZCBkYXRhIGZyYW1lIHByb3ZpZGVzIHVzIHdpdGggdGhlIGZpdHRlZCB2YWx1ZXMgZm9yIHRoZSBvYnNlcnZhdGlvbnMgdGhhdCB3ZXJlIGluIHRoZSBvcmlnaW5hbCBkYXRhLiBIb3dldmVyLCBvbmNlIHdlIGhhdmUgZml0IHRoZSBtb2RlbCwgd2UgbWF5IHdhbnQgdG8gY29tcHV0ZSBleHBlY3RlZCB2YWx1ZXMgZm9yIG9ic2VydmF0aW9ucyB0aGF0IHdlcmUgbm90IHByZXNlbnQgaW4gdGhlIGRhdGEgb24gd2hpY2ggdGhlIG1vZGVsIHdhcyBmaXQuIFRoZXNlIHR5cGVzIG9mIHByZWRpY3Rpb25zIGFyZSBjYWxsZWQgb3V0LW9mLXNhbXBsZS4KClRoZSBiZW4gZGF0YSBmcmFtZSBjb250YWlucyBhIGhlaWdodCBhbmQgd2VpZ2h0IG9ic2VydmF0aW9uIGZvciBvbmUgcGVyc29uLiBUaGUgbW9kIG9iamVjdCBjb250YWlucyB0aGUgZml0dGVkIG1vZGVsIGZvciB3ZWlnaHQgYXMgYSBmdW5jdGlvbiBvZiBoZWlnaHQgZm9yIHRoZSBvYnNlcnZhdGlvbnMgaW4gdGhlIGJkaW1zIGRhdGFzZXQuIFdlIGNhbiB1c2UgdGhlIHByZWRpY3QoKSBmdW5jdGlvbiB0byBnZW5lcmF0ZSBleHBlY3RlZCB2YWx1ZXMgZm9yIHRoZSB3ZWlnaHQgb2YgbmV3IGluZGl2aWR1YWxzLiBXZSBtdXN0IHBhc3MgdGhlIGRhdGEgZnJhbWUgb2YgbmV3IG9ic2VydmF0aW9ucyB0aHJvdWdoIHRoZSBuZXdkYXRhIGFyZ3VtZW50LgpgYGB7cn0KYmVuIDwtIGRhdGEuZnJhbWUoIndndCIgPSA3NC44LCAiaGd0IiA9IDE4Mi44KQpgYGAKYGBge3J9CiMgUHJpbnQgYmVuCmJlbgoKIyBQcmVkaWN0IHRoZSB3ZWlnaHQgb2YgYmVuCnByZWRpY3QobW9kLCBiZW4pCmBgYApOb3RlIHRoYXQgdGhlIGRhdGEgZnJhbWUgYmVuIGhhcyB2YXJpYWJsZXMgd2l0aCB0aGUgZXhhY3Qgc2FtZSBuYW1lcyBhcyB0aG9zZSBpbiB0aGUgZml0dGVkIG1vZGVsLgoKIyMjIEFkZGluZyBhIHJlZ3Jlc3Npb24gbGluZSB0byBhIHBsb3QgbWFudWFsbHkKClRoZSBnZW9tX3Ntb290aCgpIGZ1bmN0aW9uIG1ha2VzIGl0IGVhc3kgdG8gYWRkIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIGxpbmUgdG8gYSBzY2F0dGVycGxvdCBvZiB0aGUgY29ycmVzcG9uZGluZyB2YXJpYWJsZXMuIEFuZCBpbiBmYWN0LCB0aGVyZSBhcmUgbW9yZSBjb21wbGljYXRlZCByZWdyZXNzaW9uIG1vZGVscyB0aGF0IGNhbiBiZSB2aXN1YWxpemVkIGluIHRoZSBkYXRhIHNwYWNlIHdpdGggZ2VvbV9zbW9vdGgoKS4gSG93ZXZlciwgdGhlcmUgbWF5IHN0aWxsIGJlIHRpbWVzIHdoZW4gd2Ugd2lsbCB3YW50IHRvIGFkZCByZWdyZXNzaW9uIGxpbmVzIHRvIG91ciBzY2F0dGVycGxvdCBtYW51YWxseS4gVG8gZG8gdGhpcywgd2Ugd2lsbCB1c2UgdGhlIGdlb21fYWJsaW5lKCkgZnVuY3Rpb24sIHdoaWNoIHRha2VzIHNsb3BlIGFuZCBpbnRlcmNlcHQgYXJndW1lbnRzLiBOYXR1cmFsbHksIHdlIGhhdmUgdG8gY29tcHV0ZSB0aG9zZSB2YWx1ZXMgYWhlYWQgb2YgdGltZSwgYnV0IHdlIGFscmVhZHkgc2F3IGhvdyB0byBkbyB0aGlzIChlLmcuIHVzaW5nIGNvZWYoKSkuCgpUaGUgY29lZnMgZGF0YSBmcmFtZSBjb250YWlucyB0aGUgbW9kZWwgZXN0aW1hdGVzIHJldHJpZXZlZCBmcm9tIGNvZWYoKS4gUGFzc2luZyB0aGlzIHRvIGdlb21fYWJsaW5lKCkgYXMgdGhlIGRhdGEgYXJndW1lbnQgd2lsbCBlbmFibGUgeW91IHRvIGRyYXcgYSBzdHJhaWdodCBsaW5lIG9uIHlvdXIgc2NhdHRlcnBsb3QuCgpgYGB7cn0KbGlicmFyeShkYXRhLnRhYmxlKQpjb2VmcyA8LSBkYXRhLmZyYW1lKGNvZWYobW9kKSkKcm5hbWVzIDwtIHJvd25hbWVzKGNvZWZzKQpjb2VmcyA8LSBjb2VmcyAlPiUgCiAgdHJhbnNwb3NlKCkKCmNvbG5hbWVzKGNvZWZzKSA8LSBybmFtZXMKYGBgCmBgYHtyfQojIEFkZCB0aGUgbGluZSB0byB0aGUgc2NhdHRlcnBsb3QKZ2dwbG90KGRhdGEgPSBiZGltcywgYWVzKHggPSBoZ3QsIHkgPSB3Z3QpKSArIAogIGdlb21fcG9pbnQoKSArIAogIGdlb21fYWJsaW5lKGRhdGEgPSBjb2VmcywgCiAgICAgICAgICAgICAgYWVzKGludGVyY2VwdCA9IGAoSW50ZXJjZXB0KWAsIHNsb3BlID0gaGd0KSwgIAogICAgICAgICAgICAgIGNvbG9yID0gImRvZGdlcmJsdWUiKQpgYGAKIyBNb2RlbCBGaXQKCiMjIEFzc2Vzc2luZyBNb2RlbCBGaXQKCiMjIyBSTVNFCgpUaGUgcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgcmVwb3J0ZWQgZm9yIHRoZSByZWdyZXNzaW9uIG1vZGVsIGZvciBwb3ZlcnR5IHJhdGUgb2YgVS5TLiBjb3VudGllcyBpbiB0ZXJtcyBvZiBoaWdoIHNjaG9vbCBncmFkdWF0aW9uIHJhdGUgaXMgNC42Ny4gV2hhdCBkb2VzIHRoaXMgbWVhbj8KClRoZSB0eXBpY2FsIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgb2JzZXJ2ZWQgcG92ZXJ0eSByYXRlIGFuZCB0aGUgcG92ZXJ0eSByYXRlIHByZWRpY3RlZCBieSB0aGUgbW9kZWwgaXMgYWJvdXQgNC42NyBwZXJjZW50YWdlIHBvaW50cy4KCiMjIyBTdGFuZGFyZCBlcnJvciBvZiByZXNpZHVhbHMKCk9uZSB3YXkgdG8gYXNzZXNzIHN0cmVuZ3RoIG9mIGZpdCBpcyB0byBjb25zaWRlciBob3cgZmFyIG9mZiB0aGUgbW9kZWwgaXMgZm9yIGEgdHlwaWNhbCBjYXNlLiBUaGF0IGlzLCBmb3Igc29tZSBvYnNlcnZhdGlvbnMsIHRoZSBmaXR0ZWQgdmFsdWUgd2lsbCBiZSB2ZXJ5IGNsb3NlIHRvIHRoZSBhY3R1YWwgdmFsdWUsIHdoaWxlIGZvciBvdGhlcnMgaXQgd2lsbCBub3QuIFRoZSBtYWduaXR1ZGUgb2YgYSB0eXBpY2FsIHJlc2lkdWFsIGNhbiBnaXZlIHVzIGEgc2Vuc2Ugb2YgZ2VuZXJhbGx5IGhvdyBjbG9zZSBvdXIgZXN0aW1hdGVzIGFyZS4KCkhvd2V2ZXIsIHJlY2FsbCB0aGF0IHNvbWUgb2YgdGhlIHJlc2lkdWFscyBhcmUgcG9zaXRpdmUsIHdoaWxlIG90aGVycyBhcmUgbmVnYXRpdmUuIEluIGZhY3QsIGl0IGlzIGd1YXJhbnRlZWQgYnkgdGhlIGxlYXN0IHNxdWFyZXMgZml0dGluZyBwcm9jZWR1cmUgdGhhdCB0aGUgbWVhbiBvZiB0aGUgcmVzaWR1YWxzIGlzIHplcm8uIFRodXMsIGl0IG1ha2VzIG1vcmUgc2Vuc2UgdG8gY29tcHV0ZSB0aGUgc3F1YXJlIHJvb3Qgb2YgdGhlIG1lYW4gc3F1YXJlZCByZXNpZHVhbCwgb3Igcm9vdCBtZWFuIHNxdWFyZWQgZXJyb3IgKCRSTVNFJCkuIFIgY2FsbHMgdGhpcyBxdWFudGl0eSB0aGUgcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IuCgpUbyBtYWtlIHRoaXMgZXN0aW1hdGUgdW5iaWFzZWQsIHlvdSBoYXZlIHRvIGRpdmlkZSB0aGUgc3VtIG9mIHRoZSBzcXVhcmVkIHJlc2lkdWFscyBieSB0aGUgZGVncmVlcyBvZiBmcmVlZG9tIGluIHRoZSBtb2RlbC4gVGh1czoKCiRSTVNFID0gXHNxcnRcZnJhY3tcc3VtX3tpfWVeMl97aX19e2QuZn09XHNxcnRcZnJhY3tTU0V9e2QuZn0kCgpZb3UgY2FuIHJlY292ZXIgdGhlIHJlc2lkdWFscyBmcm9tIG1vZCB3aXRoIHJlc2lkdWFscygpLCBhbmQgdGhlIGRlZ3JlZXMgb2YgZnJlZWRvbSB3aXRoIGRmLnJlc2lkdWFsKCkuCmBgYHtyfQojIFZpZXcgc3VtbWFyeSBvZiBtb2RlbApzdW1tYXJ5KG1vZCkKCiMgQ29tcHV0ZSB0aGUgbWVhbiBvZiB0aGUgcmVzaWR1YWxzCm1lYW4ocmVzaWR1YWxzKG1vZCkpCgojIENvbXB1dGUgUk1TRQpzcXJ0KHN1bShyZXNpZHVhbHMobW9kKV4yKSAvIGRmLnJlc2lkdWFsKG1vZCkpCmBgYAojIyBDb21wYXJpbmcgbW9kZWwgZml0cwoKIyMjIEFzc2Vzc2luZyBzaW1wbGUgbGluZWFyIG1vZGVsIGZpdAoKVGhlIGNvZWZmaWNpZW50IG9mIGRldGVybWluYXRpb24gKCRSXjIkKSwgY2FuIGJlIGNvbXB1dGVkIGFzCgokUl4yID0gMSDiiJIgXGZyYWN7U1NFfXtTU1R9ID0gMSDiiJIgXGZyYWN7VmFyKGUpfXtWYXIoeSl9JAoKd2hlcmUgJGUkIGlzIHRoZSB2ZWN0b3Igb2YgcmVzaWR1YWxzIGFuZCAkeSQgaXMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLiBUaGlzIGdpdmVzIHVzIHRoZSBpbnRlcnByZXRhdGlvbiBvZiAkUl4yJCBhcyB0aGUgcGVyY2VudGFnZSBvZiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIHJlc3BvbnNlIHRoYXQgaXMgZXhwbGFpbmVkIGJ5IHRoZSBtb2RlbCwgc2luY2UgdGhlIHJlc2lkdWFscyBhcmUgdGhlIHBhcnQgb2YgdGhhdCB2YXJpYWJpbGl0eSB0aGF0IHJlbWFpbnMgdW5leHBsYWluZWQgYnkgdGhlIG1vZGVsLgpgYGB7cn0KIyBWaWV3IG1vZGVsIHN1bW1hcnkKc3VtbWFyeShtb2QpCgojIENvbXB1dGUgUi1zcXVhcmVkCmJkaW1zX3RpZHkgJT4lCiAgc3VtbWFyaXplKHZhcl95ID0gdmFyKHdndCksIHZhcl9lID0gdmFyKC5yZXNpZCkpICU+JQogIG11dGF0ZShSX3NxdWFyZWQgPSAxIC0gdmFyX2UgLyB2YXJfeSkKYGBgCiBUaGlzIG1lYW5zIHRoYXQgNTEuNCUgb2YgdGhlIHZhcmlhYmlsaXR5IGluIHdlaWdodCBpcyBleHBsYWluZWQgYnkgaGVpZ2h0LgoKIyMjIEludGVycHJldGF0aW9uIG9mIFJeMgoKVGhlICRSXjIkIHJlcG9ydGVkIGZvciB0aGUgcmVncmVzc2lvbiBtb2RlbCBmb3IgcG92ZXJ0eSByYXRlIG9mIFUuUy4gY291bnRpZXMgaW4gdGVybXMgb2YgaGlnaCBzY2hvb2wgZ3JhZHVhdGlvbiByYXRlIGlzIDAuNDY0LgpgYGB7cn0KbG0oZm9ybXVsYSA9IHBvdmVydHkgfiBoc19ncmFkLCBkYXRhID0gY291bnR5Q29tcGxldGUpICU+JQogIHN1bW1hcnkoKQpgYGAKSG93IHNob3VsZCB0aGlzIHJlc3VsdCBiZSBpbnRlcnByZXRlZD8KCjQ2LjQlIG9mIHRoZSB2YXJpYWJpbGl0eSBpbiBwb3ZlcnR5IHJhdGUgYW1vbmcgVS5TLiBjb3VudGllcyBjYW4gYmUgZXhwbGFpbmVkIGJ5IGhpZ2ggc2Nob29sIGdyYWR1YXRpb24gcmF0ZS4KCgojIyMgTGluZWFyIHZzLiBhdmVyYWdlCgpUaGUgJFJeMiQgZ2l2ZXMgdXMgYSBudW1lcmljYWwgbWVhc3VyZW1lbnQgb2YgdGhlIHN0cmVuZ3RoIG9mIGZpdCByZWxhdGl2ZSB0byBhIG51bGwgbW9kZWwgYmFzZWQgb24gdGhlIGF2ZXJhZ2Ugb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlOgoKJFxoYXR7eX1fe251bGx9ID0gXG92ZXJsaW5le3l9JAoKVGhpcyBtb2RlbCBoYXMgYW4gJFJeMiQgb2YgemVybyBiZWNhdXNlICRTU0UgPSBTU1QkLiBUaGF0IGlzLCBzaW5jZSB0aGUgZml0dGVkIHZhbHVlcyAoJFxoYXR7eX1fe251bGx9JCkgYXJlIGFsbCBlcXVhbCB0byB0aGUgYXZlcmFnZSAoJFxvdmVybGluZXt5fSQpLCB0aGUgcmVzaWR1YWwgZm9yIGVhY2ggb2JzZXJ2YXRpb24gaXMgdGhlIGRpc3RhbmNlIGJldHdlZW4gdGhhdCBvYnNlcnZhdGlvbiBhbmQgdGhlIG1lYW4gb2YgdGhlIHJlc3BvbnNlLiBTaW5jZSB3ZSBjYW4gYWx3YXlzIGZpdCB0aGUgbnVsbCBtb2RlbCwgaXQgc2VydmVzIGFzIGEgYmFzZWxpbmUgYWdhaW5zdCB3aGljaCBhbGwgb3RoZXIgbW9kZWxzIHdpbGwgYmUgY29tcGFyZWQuCmBgYHtyfQptZWFuKGJkaW1zX3RpZHkkd2d0KQpgYGAKYGBge3J9CmxpYnJhcnkoY293cGxvdCkKcGxvdF9udWxsIDwtIGdncGxvdChkYXRhID0gYmRpbXNfdGlkeSwgYWVzKHggPSBoZ3QsIHkgPSB3Z3QpKSArIAogIGdlb21fcG9pbnQoKSArIAogIGdlb21fYWJsaW5lKGRhdGEgPSBjb2VmcywgCiAgICAgICAgICAgICAgYWVzKGludGVyY2VwdCA9IG1lYW4oYmRpbXNfdGlkeSR3Z3QpLCBzbG9wZSA9IDApLCAgCiAgICAgICAgICAgICAgY29sb3IgPSAiZG9kZ2VyYmx1ZSIpKwogIGdlb21fc2VnbWVudChhZXMoeD1oZ3QseT13Z3QseGVuZD1oZ3QseWVuZD1tZWFuKGJkaW1zX3RpZHkkd2d0KSksIGNvbG9yID0gImdyZXkiKSArCiAgZ2d0aXRsZSgibnVsbCIpCgpwbG90X2xtIDwtIGdncGxvdChkYXRhID0gYmRpbXNfdGlkeSwgYWVzKHggPSBoZ3QsIHkgPSB3Z3QpKSArIAogIGdlb21fcG9pbnQoKSArIAogIGdlb21fYWJsaW5lKGRhdGEgPSBjb2VmcywgCiAgICAgICAgICAgICAgYWVzKGludGVyY2VwdCA9IGAoSW50ZXJjZXB0KWAsIHNsb3BlID0gaGd0KSwgIAogICAgICAgICAgICAgIGNvbG9yID0gImRvZGdlcmJsdWUiKSsKICBnZW9tX3NlZ21lbnQoYWVzKHg9aGd0LHk9d2d0LHhlbmQ9aGd0LHllbmQ9LmZpdHRlZCksIGNvbG9yID0gImdyZXkiKSArCiAgZ2d0aXRsZSgic2xyIikKCnBsb3RfZ3JpZChwbG90X251bGwsIHBsb3RfbG0pCmBgYAoKICAgICMgQ29tcHV0ZSBTU0UgZm9yIG51bGwgbW9kZWwKICAgIG1vZF9udWxsICU+JQogICAgICBzdW1tYXJpemUoU1NFID0gc3VtKC5yZXNpZCoqMikpCiAgIAo5MDEyMwogCiAgICAjIENvbXB1dGUgU1NFIGZvciByZWdyZXNzaW9uIG1vZGVsCiAgICBtb2RfaGd0ICU+JQogICAgICBzdW1tYXJpemUoU1NFID0gc3VtKC5yZXNpZCoqMikpCiAgICAgIAo0Mzc1MyAgCgojIyBVbnVzdWFsIHBvaW50cwoKIyMjIExldmVyYWdlCgpUaGUgbGV2ZXJhZ2Ugb2YgYW4gb2JzZXJ2YXRpb24gaW4gYSByZWdyZXNzaW9uIG1vZGVsIGlzIGRlZmluZWQgZW50aXJlbHkgaW4gdGVybXMgb2YgdGhlIGRpc3RhbmNlIG9mIHRoYXQgb2JzZXJ2YXRpb24gZnJvbSB0aGUgbWVhbiBvZiB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGUuIFRoYXQgaXMsIG9ic2VydmF0aW9ucyBjbG9zZSB0byB0aGUgbWVhbiBvZiB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGUgaGF2ZSBsb3cgbGV2ZXJhZ2UsIHdoaWxlIG9ic2VydmF0aW9ucyBmYXIgZnJvbSB0aGUgbWVhbiBvZiB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGUgaGF2ZSBoaWdoIGxldmVyYWdlLiBQb2ludHMgb2YgaGlnaCBsZXZlcmFnZSBtYXkgb3IgbWF5IG5vdCBiZSBpbmZsdWVudGlhbC4KClRoZSBhdWdtZW50KCkgZnVuY3Rpb24gZnJvbSB0aGUgYnJvb20gcGFja2FnZSB3aWxsIGFkZCB0aGUgbGV2ZXJhZ2Ugc2NvcmVzICguaGF0KSB0byBhIG1vZGVsIGRhdGEgZnJhbWUuCmBgYHtyfQojIFJhbmsgcG9pbnRzIG9mIGhpZ2ggbGV2ZXJhZ2UKbW9kICU+JQogIGF1Z21lbnQoKSAlPiUKICBhcnJhbmdlKGRlc2MoLmhhdCkpICU+JQogIGhlYWQoKQpgYGAKIyMjIEluZmx1ZW5jZQoKQXMgbm90ZWQgcHJldmlvdXNseSwgb2JzZXJ2YXRpb25zIG9mIGhpZ2ggbGV2ZXJhZ2UgbWF5IG9yIG1heSBub3QgYmUgaW5mbHVlbnRpYWwuIFRoZSBpbmZsdWVuY2Ugb2YgYW4gb2JzZXJ2YXRpb24gZGVwZW5kcyBub3Qgb25seSBvbiBpdHMgbGV2ZXJhZ2UsIGJ1dCBhbHNvIG9uIHRoZSBtYWduaXR1ZGUgb2YgaXRzIHJlc2lkdWFsLiBSZWNhbGwgdGhhdCB3aGlsZSBsZXZlcmFnZSBvbmx5IHRha2VzIGludG8gYWNjb3VudCB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGUgKCR4JCksIHRoZSByZXNpZHVhbCBkZXBlbmRzIG9uIHRoZSByZXNwb25zZSB2YXJpYWJsZSAoJHkkKSBhbmQgdGhlIGZpdHRlZCB2YWx1ZSAoJFxoYXR7eX0kLgoKSW5mbHVlbnRpYWwgcG9pbnRzIGFyZSBsaWtlbHkgdG8gaGF2ZSBoaWdoIGxldmVyYWdlIGFuZCBkZXZpYXRlIGZyb20gdGhlIGdlbmVyYWwgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMuIFdlIG1lYXN1cmUgaW5mbHVlbmNlIHVzaW5nIENvb2sncyBkaXN0YW5jZSwgd2hpY2ggaW5jb3Jwb3JhdGVzIGJvdGggdGhlIGxldmVyYWdlIGFuZCByZXNpZHVhbCBvZiBlYWNoIG9ic2VydmF0aW9uLgpgYGB7cn0KIyBSYW5rIGluZmx1ZW50aWFsIHBvaW50cwptb2QgJT4lCiAgYXVnbWVudCgpICU+JQogIGFycmFuZ2UoZGVzYyguY29va3NkKSkgJT4lCiAgaGVhZCgpCmBgYAojIyBEZWFsaW5nIHdpdGggT3V0bGllcnMKCiMjIyBSZW1vdmluZyBvdXRsaWVycwoKT2JzZXJ2YXRpb25zIGNhbiBiZSBvdXRsaWVycyBmb3IgYSBudW1iZXIgb2YgZGlmZmVyZW50IHJlYXNvbnMuIFN0YXRpc3RpY2lhbnMgbXVzdCBhbHdheXMgYmUgY2FyZWZ1bOKAlGFuZCBtb3JlIGltcG9ydGFudGx5LCB0cmFuc3BhcmVudOKAlHdoZW4gZGVhbGluZyB3aXRoIG91dGxpZXJzLiBTb21ldGltZXMsIGEgYmV0dGVyIG1vZGVsIGZpdCBjYW4gYmUgYWNoaWV2ZWQgYnkgc2ltcGx5IHJlbW92aW5nIG91dGxpZXJzIGFuZCByZS1maXR0aW5nIHRoZSBtb2RlbC4gSG93ZXZlciwgb25lIG11c3QgaGF2ZSBzdHJvbmcganVzdGlmaWNhdGlvbiBmb3IgZG9pbmcgdGhpcy4gQSBkZXNpcmUgdG8gaGF2ZSBhIGhpZ2hlciAkUl4yJCBpcyBub3QgYSBnb29kIGVub3VnaCByZWFzb24hCgpJbiB0aGUgbWxiQmF0MTAgZGF0YSwgdGhlIG91dGxpZXIgd2l0aCBhbiBPQlAgb2YgMC41NTAgaXMgQm9iYnkgU2NhbGVzLCBhbiBpbmZpZWxkZXIgd2hvIGhhZCBmb3VyIGhpdHMgaW4gMTMgYXQtYmF0cyBmb3IgdGhlIENoaWNhZ28gQ3Vicy4gU2NhbGVzIGFsc28gd2Fsa2VkIHNldmVuIHRpbWVzLCByZXN1bHRpbmcgaW4gaGlzIHVudXN1YWxseSBoaWdoIE9CUC4gVGhlIGp1c3RpZmljYXRpb24gZm9yIHJlbW92aW5nIFNjYWxlcyBoZXJlIGlzIHdlYWsuIFdoaWxlIGhpcyBwZXJmb3JtYW5jZSB3YXMgdW51c3VhbCwgdGhlcmUgaXMgbm90aGluZyB0byBzdWdnZXN0IHRoYXQgaXQgaXMgbm90IGEgdmFsaWQgZGF0YSBwb2ludCwgbm9yIGlzIHRoZXJlIGEgZ29vZCByZWFzb24gdG8gdGhpbmsgdGhhdCBzb21laG93IHdlIHdpbGwgbGVhcm4gbW9yZSBhYm91dCBNYWpvciBMZWFndWUgQmFzZWJhbGwgcGxheWVycyBieSBleGNsdWRpbmcgaGltLgoKTmV2ZXJ0aGVsZXNzLCB3ZSBjYW4gZGVtb25zdHJhdGUgaG93IHJlbW92aW5nIGhpbSB3aWxsIGFmZmVjdCBvdXIgbW9kZWwuCmBgYHtyfQojIENyZWF0ZSBub250cml2aWFsX3BsYXllcnMKbm9udHJpdmlhbF9wbGF5ZXJzIDwtIG1sYkJhdDEwICU+JQogIGZpbHRlcihBQiA+PTEwICZPQlAgPCAwLjUwMCkKCgojIEZpdCBtb2RlbCB0byBuZXcgZGF0YQptb2RfY2xlYW5lciA8LSBsbShTTEd+T0JQLCBub250cml2aWFsX3BsYXllcnMpCgojIFZpZXcgbW9kZWwgc3VtbWFyeQpzdW1tYXJ5KG1vZF9jbGVhbmVyKQoKIyBWaXN1YWxpemUgbmV3IG1vZGVsCmdncGxvdChkYXRhID0gbm9udHJpdmlhbF9wbGF5ZXJzLCBhZXMoeCA9IE9CUCwgeSA9IFNMRykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpCmBgYAojIyMgSGlnaCBsZXZlcmFnZSBwb2ludHMKCk5vdCBhbGwgcG9pbnRzIG9mIGhpZ2ggbGV2ZXJhZ2UgYXJlIGluZmx1ZW50aWFsLiBXaGlsZSB0aGUgaGlnaCBsZXZlcmFnZSBvYnNlcnZhdGlvbiBjb3JyZXNwb25kaW5nIHRvIEJvYmJ5IFNjYWxlcyBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UgaXMgaW5mbHVlbnRpYWwsIHRoZSB0aHJlZSBvYnNlcnZhdGlvbnMgZm9yIHBsYXllcnMgd2l0aCBPQlAgYW5kIFNMRyB2YWx1ZXMgb2YgMCBhcmUgbm90IGluZmx1ZW50aWFsLgoKVGhpcyBpcyBiZWNhdXNlIHRoZXkgaGFwcGVuIHRvIGxpZSByaWdodCBuZWFyIHRoZSByZWdyZXNzaW9uIGFueXdheS4gVGh1cywgd2hpbGUgdGhlaXIgZXh0cmVtZWx5IGxvdyBPQlAgZ2l2ZXMgdGhlbSB0aGUgcG93ZXIgdG8gZXhlcnQgaW5mbHVlbmNlIG92ZXIgdGhlIHNsb3BlIG9mIHRoZSByZWdyZXNzaW9uIGxpbmUsIHRoZWlyIGxvdyBTTEcgcHJldmVudHMgdGhlbSBmcm9tIHVzaW5nIGl0LgpgYGB7cn0KIyBSYW5rIGhpZ2ggbGV2ZXJhZ2UgcG9pbnRzCm1vZCAlPiUKICBhdWdtZW50KCkgJT4lCiAgYXJyYW5nZShkZXNjKC5oYXQpLCAoLmNvb2tzZCkpICU+JQogIGhlYWQoKQpgYGAKCg==