library(tidyverse) # for plotting and summarizing
library(moderndive) # for nice model output
library(broom) # for nice model output
library(equatiomatic) # for writing model equations
theme_set(theme_minimal()) # changes the theme of ggplots to theme_minimal, my personal favorite
As we begin to study models, remember this quote by famous statistician George Box,
“All models are wrong but some are useful.”
GOAL:
By the end of these notes and activities, you should be able to perform the following tasks.
- Fit a linear regression model using the
lm
function.
- Write out a model equation by hand.
- Obtain model output using
tidy()
or get_regression_table()
.
- Interpret the coefficients from a simple linear regression model that uses one quantitative OR one categorical predictor variable.
- Use the
augment()
function (or get_regression_points()
) to obtain fitted values and residuals.
- Use the
augment()
function to predict new values.
- Plot the fitted line on top of the observed values.
The data
We will use the mpg
dataset from the ggplot2
library (part of the tidyverse
). This is a subset of the vehicles
dataset we have been examining. It has been further cleaned. To learn more about the dataset, search for mpg
in the Help tab or run ?mpg
in the console.
YOUR TURN!
- How would you describe the relationship between displ and hwy?
- Is the description different for each drv?
mpg %>%
ggplot() +
geom_jitter(aes(x=displ, y=hwy, color=drv))
- Create some graphs and calculate statistics to describe the distribution of each of the three variables we’re studying. Write down a description in 1-2 sentences for each.
Introduction
We are going to discuss different types of linear models, their notation in R, how to interpret the model coefficients, and how to use them for prediction.
What does it mean to fit a model to data? We are looking for a mathematical equation to represent the “general” relationship between a response variable and explanatory variables. We know it won’t be perfect, but we would like something useful. In this course, we focus on linear models. They take the form:
\[
\hat{y} = \hat{\beta}_0 + \hat{\beta}_1 x_1 + \hat{\beta}_2 x_2 + ... + \hat{\beta}_p x_p.
\]
The “hats” on top of the \(y\) and the \(\beta\)s are to indicate that those quantities are estimates obtained from data. The \(\beta\)s will be numbers. The above is just a more complex version of the traditional equation of a line:
\[
y = mx + b
\]
The resulting model equation has many different names: model equation (or specifically linear model equation or just model), fitted model, least squares regression line, multiple regression line, estimated line, etc. These all mean the same thing.
The model has two main uses:
Explanation: to explain the relationship between the explanatory variables and the response variable, ie. how \(y\) relates to each of \(x_1\), \(x_2\), …,\(x_p\). The coefficients (\(\hat{\beta}_1\), \(\hat{\beta}_2\), …, \(\hat{\beta}_p\)) tell us about that.
Prediction: to find the predicted value (also called predicted response, fitted value, model value, and a few other things) which is the value the response variable takes when you plug in the values of the explanatory variables. Or to predict values of \(y\) when it is unknown.
We will use the lm
function (which stands for linear model) to fit models. This function requires two arguments: the model equation and the data. The model equation is written in the form y ~ x1 + x2
, where y
is the response and x1
and x2
are the explanatory variables. More variables can be added by using the +
sign to separate them. Here is a generic example
lm(y ~ x1 + x2, data=the_data)
Linear regression with one quantitative explanatory variable
Let’s look at an example.
lm_displ <- lm(hwy ~ displ, data=mpg)
lm_displ
##
## Call:
## lm(formula = hwy ~ displ, data = mpg)
##
## Coefficients:
## (Intercept) displ
## 35.698 -3.531
Notice a couple things:
- I have saved this model to an object named
lm_displ
.
- The output gives us the intercept, \(\hat{\beta}_0\) and the slope or coefficient for the
displ
term, \(\hat{\beta}_1\).
We can obtain more output (which we’ll want when we start doing inference), using the either the get_regression_table()
or tidy()
function.
get_regression_table(lm_displ)
tidy(lm_displ)
For now, we are just interested in the term and estimate columns. The term column is the term/variable in the model equation. The column titled estimate gives the estimated coefficients of the variables/terms (ie. the \(\hat{\beta}\)s.
We could write out the equation above as:
\[
\hat{hwy} = 35.70 - 3.53 displ
\]
There’s even a function that will write out the model equation for us!
extract_eq(lm_displ,
wrap = TRUE,
use_coefs = TRUE,
ital_vars = TRUE)
\[
\begin{aligned}
\widehat{hwy} &= 35.7 - 3.53(displ)
\end{aligned}
\]
Explanation
YOUR TURN! (don’t look ahead)
- How do you interpret the intercept, 35.70?
- How do you interpret the slope, -3.53?
In general, in a simple linear regression with one quantitative explanatory variable, the intercept is the average response when the explanatory variable(s) is equal to zero. The slope is the average change in the response variable for a one unit increase in the explanatory variable. The image below helps illustrate this (don’t worry about the code).
## `geom_smooth()` using formula 'y ~ x'
The blue line shows the fitted line (the model). The purple vertical line highlights engine displacement values of 3 cubic inches. The purple horizontal line reflects the fitted value, \(\hat{y}_i\) for an observation with a displacement of 3. The yellow vertical line highlights engine displacement values of 4 cubic inches. The yellow horizontal line reflects the fitted value, \(\hat{y}_i\) for an observation with a displacement of 4. The distance between the purple and yellow lines is about - 3.53, which reflects the estimated slope. On average, cars with a displacement that is 1 cubic inch larger will get 3.53 MPG less. The intercept, 35.70, is interpreted as the average MPG for a car with a displacement of 0 cubic inches.
Prediction
In addition to interpreting the models, we often want to use them to predict future or unknown values of the response variable for observations where we know the values of the explanatory variables. We will introduce and reiterate some terms.
- actual values, also called observed values, are exactly what you might guess, the actual values of the response variable. They are the \(y_i\)’s.
- fitted values, also called predicted values or model values, are the value the response variable takes when you plug in the values of the explanatory variables. They are the \(\hat{y}_i\)’s.
- residuals are the difference between the actual and fitted values, \(y_i - \hat{y}_i\)’s.
YOUR TURN! (don’t look ahead)
How would you find the predicted highway miles per gallon for a vehicle with an engine displacement of 4.5 litres?
We can use the augment()
function to help us do this. The get_regression_points()
function from the moderndive
library also works. The names of the variables in the output are slightly different, though, and it will not work later when we do logistic regression.
We can use it to find the predicted responses for all the data used to fit this model. Below I just show the first 10 rows.
aug_displ <- augment(lm_displ)
aug_displ %>%
slice(1:10)
This dataset contains the response variable, all explanatory variables, and some additional information. One piece of additional information is the variable .fitted. These are the predicted (fitted) values.
Sometimes we want all the variables in our original dataset, plus these new variables. We can get that, by adding a data =
argument to the augment()
function.
augment(lm_displ, data = mpg) %>%
slice(1:10)
We can also use augment()
to predict unknown or future values, like predicting the highway MPG for a displacement of 4.5. We need to add a newdata =
argument to the function and give it a dataset as an argument. Because there is just one value, we can create the dataset “on the fly.” The tibble()
function can be used to create a dataset. If we wanted multiple predictions, we separate them with commas inside the c()
. Be sure the variable is named EXACTLY as the variable name appears in the original dataset.
augment(lm_displ,
newdata = tibble(displ = c(4.5)))
#example of more than one prediction
augment(lm_displ,
newdata = tibble(displ = c(3, 3.5, 4.0, 4.5, 5.0, 5.5)))
#OR
my_new_data <- tibble(displ = c(3, 3.5, 4.0, 4.5, 5.0, 5.5),
garbage = c(1,2,3,4,5,6))
augment(lm_displ,
newdata = my_new_data)
We can also use this to plot our fitted line on the plot of the original data.
augment(lm_displ, data = mpg) %>%
ggplot() +
geom_jitter(aes(x = displ, y = hwy)) +
geom_line(aes(x = displ, y = .fitted), color = "blue") + #the fitted line
labs(x = "Engine Displacement", y = "Highway MPG")
YOUR TURN!
- Fit a model that used cty to explain hwy. Write down the model equation.
- Interpret the intercept and slope from the model.
- Plot the line on top of a scatterplot of the data.
- Use the model to predict the highway MPG, hwy, for a car that gets 24 MPG in the city and for a car that gets 65 MPG in the city. Do you trust both of these predictions?
Linear regression with one categorical (factor) variable
Now, let’s examine a model that uses a categorical (factor) explanatory variable. First, we’ll try just putting a categorical variable in the model equation.
lm_drv <- lm(hwy ~ drv,
data=mpg)
tidy(lm_drv)
It did something. WHAT did it do? Any ideas? First, let’s run the following code. How is what we see below related to what we see above?
mpg %>%
group_by(drv) %>%
summarize(mean_hwy = mean(hwy))
Since R cannot use drv directly (its values are words not numbers), it is creating 2 new variables:
drvf is 1 if drv is f and 0 otherwise, and drvr is 1 if drv is r and 0 otherwise.
Let’s write out the model equation:
\[
\hat{hwy} = 19.18 + 8.99drvf + 1.83drvr
\]
Or, we can write code that writes the equation for us:
extract_eq(lm_drv,
wrap = TRUE,
use_coefs = TRUE,
ital_vars = TRUE)
\[
\begin{aligned}
\widehat{hwy} &= 19.17 + 8.99(drv_{f}) + 1.83(drv_{r})
\end{aligned}
\]
YOUR TURN! (don’t look ahead)
- When will both drvf and drvr take a value of 0?
- What is the predicted value for a front wheel drive vehicle?
- What is the predicted value for a rear wheel drive vehicle?
- What is the predicted value for a 4 wheel drive vehicle?
- Interpret each of the coefficients.
In general, in a simple linear regression with one categorical explanatory variable, the intercept is the average response when the explanatory variable is at the “baseline” or “reference” level. That is the level of the categorical variable that does not have a dummy variable created for it. Notice, that is also the average response when all other variables are zero, same as when we had one quantitative explanatory variable.
The other coefficients are the difference in average response between the level of the indicator variable and the reference level. This is similar to “increasing the explanatory variable by one,” but it doesn’t really make sense to talk about it that way.
YOUR TURN!
- Create a model that uses class to explain hwy. Interpret each of the coefficients.
- What is the predicted highway MPG for a pickup?
Intercept Only Model
A very simple model we thus far have avoided considering, is one with only an intercept. We can fit that model using the code below.
lm_int <- lm(hwy ~ 1, data=mpg)
YOUR TURN!
- How would you interpret the estimated intercept?
- What does this model mean conceptually? When would this be a “good” model?
LS0tCnRpdGxlOiAiU2ltcGxlIExpbmVhciBSZWdyZXNzaW9uIChTTFIpOiBDcmVhdGluZyBtb2RlbHMsIGludGVycHJldGluZyBtb2RlbHMsIHBvaW50IHByZWRpY3Rpb24iCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpICAgICAgIyBmb3IgcGxvdHRpbmcgYW5kIHN1bW1hcml6aW5nCmxpYnJhcnkobW9kZXJuZGl2ZSkgICAgICMgZm9yIG5pY2UgbW9kZWwgb3V0cHV0CmxpYnJhcnkoYnJvb20pICAgICAgICAgICMgZm9yIG5pY2UgbW9kZWwgb3V0cHV0IApsaWJyYXJ5KGVxdWF0aW9tYXRpYykgICAjIGZvciB3cml0aW5nIG1vZGVsIGVxdWF0aW9ucwp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKSAjIGNoYW5nZXMgdGhlIHRoZW1lIG9mIGdncGxvdHMgdG8gdGhlbWVfbWluaW1hbCwgbXkgcGVyc29uYWwgZmF2b3JpdGUKYGBgCgoKIVtdKGltYWdlcy9tb2Rlcm5kaXZlX2Zsb3djaGFydF9zbHIucG5nKXt3aWR0aD02MDBweH0KCkFzIHdlIGJlZ2luIHRvIHN0dWR5IG1vZGVscywgcmVtZW1iZXIgdGhpcyBxdW90ZSBieSBmYW1vdXMgc3RhdGlzdGljaWFuIEdlb3JnZSBCb3gsIApcClwKCjxjZW50ZXI+CioqIkFsbCBtb2RlbHMgYXJlIHdyb25nIGJ1dCBzb21lIGFyZSB1c2VmdWwuIioqCjwvY2VudGVyPgpcClwKCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1zdWNjZXNzIj4KICA8c3Ryb25nPkdPQUw6PC9zdHJvbmc+CgpCeSB0aGUgZW5kIG9mIHRoZXNlIG5vdGVzIGFuZCBhY3Rpdml0aWVzLCB5b3Ugc2hvdWxkIGJlIGFibGUgdG8gcGVyZm9ybSB0aGUgZm9sbG93aW5nIHRhc2tzLgoKKiBGaXQgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB1c2luZyB0aGUgYGxtYCBmdW5jdGlvbi4gIAoqIFdyaXRlIG91dCBhIG1vZGVsIGVxdWF0aW9uIGJ5IGhhbmQuICAKKiBPYnRhaW4gbW9kZWwgb3V0cHV0IHVzaW5nIGB0aWR5KClgIG9yIGBnZXRfcmVncmVzc2lvbl90YWJsZSgpYC4gIAoqIEludGVycHJldCB0aGUgY29lZmZpY2llbnRzIGZyb20gYSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdGhhdCB1c2VzIG9uZSBxdWFudGl0YXRpdmUgT1Igb25lIGNhdGVnb3JpY2FsIHByZWRpY3RvciB2YXJpYWJsZS4gIAoqIFVzZSB0aGUgYGF1Z21lbnQoKWAgZnVuY3Rpb24gKG9yIGBnZXRfcmVncmVzc2lvbl9wb2ludHMoKWApIHRvIG9idGFpbiBmaXR0ZWQgdmFsdWVzIGFuZCByZXNpZHVhbHMuICAKKiBVc2UgdGhlIGBhdWdtZW50KClgIGZ1bmN0aW9uIHRvIHByZWRpY3QgbmV3IHZhbHVlcy4gIAoqIFBsb3QgdGhlIGZpdHRlZCBsaW5lIG9uIHRvcCBvZiB0aGUgb2JzZXJ2ZWQgdmFsdWVzLgoKPC9kaXY+CgoKIyBUaGUgZGF0YQoKV2Ugd2lsbCB1c2UgdGhlIGBtcGdgIGRhdGFzZXQgZnJvbSB0aGUgYGdncGxvdDJgIGxpYnJhcnkgKHBhcnQgb2YgdGhlIGB0aWR5dmVyc2VgKS4gVGhpcyBpcyBhIHN1YnNldCBvZiB0aGUgYHZlaGljbGVzYCBkYXRhc2V0IHdlIGhhdmUgYmVlbiBleGFtaW5pbmcuIEl0IGhhcyBiZWVuIGZ1cnRoZXIgY2xlYW5lZC4gVG8gbGVhcm4gbW9yZSBhYm91dCB0aGUgZGF0YXNldCwgc2VhcmNoIGZvciBgbXBnYCBpbiB0aGUgSGVscCB0YWIgb3IgcnVuIGA/bXBnYCBpbiB0aGUgY29uc29sZS4KCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgogIDxzdHJvbmc+WU9VUiBUVVJOITwvc3Ryb25nPgoKMS4gSG93IHdvdWxkIHlvdSBkZXNjcmliZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gKmRpc3BsKiBhbmQgKmh3eSo/ICAKMi4gSXMgdGhlIGRlc2NyaXB0aW9uIGRpZmZlcmVudCBmb3IgZWFjaCAqZHJ2Kj8KCmBgYHtyfQptcGcgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2ppdHRlcihhZXMoeD1kaXNwbCwgeT1od3ksIGNvbG9yPWRydikpCmBgYAoKMy4gQ3JlYXRlIHNvbWUgZ3JhcGhzIGFuZCBjYWxjdWxhdGUgc3RhdGlzdGljcyB0byBkZXNjcmliZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGVhY2ggb2YgdGhlIHRocmVlIHZhcmlhYmxlcyB3ZSdyZSBzdHVkeWluZy4gV3JpdGUgZG93biBhIGRlc2NyaXB0aW9uIGluIDEtMiBzZW50ZW5jZXMgZm9yIGVhY2guCgpgYGB7cn0KYGBgCgo8L2Rpdj4KCiMgSW50cm9kdWN0aW9uCgpXZSBhcmUgZ29pbmcgdG8gZGlzY3VzcyBkaWZmZXJlbnQgdHlwZXMgb2YgbGluZWFyIG1vZGVscywgdGhlaXIgbm90YXRpb24gaW4gUiwgaG93IHRvIGludGVycHJldCB0aGUgbW9kZWwgY29lZmZpY2llbnRzLCBhbmQgaG93IHRvIHVzZSB0aGVtIGZvciBwcmVkaWN0aW9uLgoKV2hhdCBkb2VzIGl0IG1lYW4gdG8gZml0IGEgbW9kZWwgdG8gZGF0YT8gV2UgYXJlIGxvb2tpbmcgZm9yIGEgbWF0aGVtYXRpY2FsIGVxdWF0aW9uIHRvIHJlcHJlc2VudCB0aGUgImdlbmVyYWwiIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgKnJlc3BvbnNlIHZhcmlhYmxlKiBhbmQgKmV4cGxhbmF0b3J5IHZhcmlhYmxlcyouIFdlIGtub3cgaXQgd29uJ3QgYmUgcGVyZmVjdCwgYnV0IHdlIHdvdWxkIGxpa2Ugc29tZXRoaW5nIHVzZWZ1bC4gSW4gdGhpcyBjb3Vyc2UsIHdlIGZvY3VzIG9uIGxpbmVhciBtb2RlbHMuIFRoZXkgdGFrZSB0aGUgZm9ybToKCiQkClxoYXR7eX0gPSBcaGF0e1xiZXRhfV8wICsgXGhhdHtcYmV0YX1fMSB4XzEgKyBcaGF0e1xiZXRhfV8yIHhfMiArIC4uLiArIFxoYXR7XGJldGF9X3AgeF9wLgokJAoKVGhlICJoYXRzIiBvbiB0b3Agb2YgdGhlICR5JCBhbmQgdGhlICRcYmV0YSRzIGFyZSB0byBpbmRpY2F0ZSB0aGF0IHRob3NlIHF1YW50aXRpZXMgYXJlIGVzdGltYXRlcyBvYnRhaW5lZCBmcm9tIGRhdGEuIFRoZSAkXGJldGEkcyB3aWxsIGJlIG51bWJlcnMuIFRoZSBhYm92ZSBpcyBqdXN0IGEgbW9yZSBjb21wbGV4IHZlcnNpb24gb2YgdGhlIHRyYWRpdGlvbmFsIGVxdWF0aW9uIG9mIGEgbGluZToKCiQkCnkgPSBteCArIGIKJCQKClRoZSByZXN1bHRpbmcgbW9kZWwgZXF1YXRpb24gaGFzIG1hbnkgZGlmZmVyZW50IG5hbWVzOiBtb2RlbCBlcXVhdGlvbiAob3Igc3BlY2lmaWNhbGx5IGxpbmVhciBtb2RlbCBlcXVhdGlvbiBvciBqdXN0IG1vZGVsKSwgZml0dGVkIG1vZGVsLCBsZWFzdCBzcXVhcmVzIHJlZ3Jlc3Npb24gbGluZSwgbXVsdGlwbGUgcmVncmVzc2lvbiBsaW5lLCBlc3RpbWF0ZWQgbGluZSwgZXRjLiBUaGVzZSBhbGwgbWVhbiB0aGUgc2FtZSB0aGluZy4KClRoZSBtb2RlbCBoYXMgdHdvIG1haW4gdXNlczoKCjEuIEV4cGxhbmF0aW9uOiB0byBleHBsYWluIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIGFuZCB0aGUgcmVzcG9uc2UgdmFyaWFibGUsIGllLiBob3cgJHkkIHJlbGF0ZXMgdG8gZWFjaCBvZiAkeF8xJCwgJHhfMiQsIC4uLiwkeF9wJC4gVGhlIGNvZWZmaWNpZW50cyAoJFxoYXR7XGJldGF9XzEkLCAkXGhhdHtcYmV0YX1fMiQsIC4uLiwgJFxoYXR7XGJldGF9X3AkKSB0ZWxsIHVzIGFib3V0IHRoYXQuIAoKMi4gUHJlZGljdGlvbjogdG8gZmluZCB0aGUgKnByZWRpY3RlZCB2YWx1ZSogKGFsc28gY2FsbGVkICpwcmVkaWN0ZWQgcmVzcG9uc2UqLCAqZml0dGVkIHZhbHVlKiwgKm1vZGVsIHZhbHVlKiwgYW5kIGEgZmV3IG90aGVyIHRoaW5ncykgd2hpY2ggaXMgdGhlIHZhbHVlIHRoZSByZXNwb25zZSB2YXJpYWJsZSB0YWtlcyB3aGVuIHlvdSBwbHVnIGluIHRoZSB2YWx1ZXMgb2YgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcy4gT3IgdG8gcHJlZGljdCB2YWx1ZXMgb2YgJHkkIHdoZW4gaXQgaXMgdW5rbm93bi4KCldlIHdpbGwgdXNlIHRoZSBgbG1gIGZ1bmN0aW9uICh3aGljaCBzdGFuZHMgZm9yIGxpbmVhciBtb2RlbCkgdG8gZml0IG1vZGVscy4gVGhpcyBmdW5jdGlvbiByZXF1aXJlcyB0d28gYXJndW1lbnRzOiB0aGUgbW9kZWwgZXF1YXRpb24gYW5kIHRoZSBkYXRhLiBUaGUgbW9kZWwgZXF1YXRpb24gaXMgd3JpdHRlbiBpbiB0aGUgZm9ybSBgeSB+IHgxICsgeDJgLCB3aGVyZSBgeWAgaXMgdGhlIHJlc3BvbnNlIGFuZCBgeDFgIGFuZCBgeDJgIGFyZSB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzLiBNb3JlIHZhcmlhYmxlcyBjYW4gYmUgYWRkZWQgYnkgdXNpbmcgdGhlIGArYCBzaWduIHRvIHNlcGFyYXRlIHRoZW0uIEhlcmUgaXMgYSBnZW5lcmljIGV4YW1wbGUKCmBgYHtyLCBldmFsPUZBTFNFfQpsbSh5IH4geDEgKyB4MiwgZGF0YT10aGVfZGF0YSkKYGBgCgojIExpbmVhciByZWdyZXNzaW9uIHdpdGggb25lIHF1YW50aXRhdGl2ZSBleHBsYW5hdG9yeSB2YXJpYWJsZQoKTGV0J3MgbG9vayBhdCBhbiBleGFtcGxlLgoKYGBge3J9CmxtX2Rpc3BsIDwtIGxtKGh3eSB+IGRpc3BsLCBkYXRhPW1wZykKCmxtX2Rpc3BsCmBgYAoKTm90aWNlIGEgY291cGxlIHRoaW5nczoKCjEuIEkgaGF2ZSBzYXZlZCB0aGlzIG1vZGVsIHRvIGFuIG9iamVjdCBuYW1lZCBgbG1fZGlzcGxgLiAgCjIuIFRoZSBvdXRwdXQgZ2l2ZXMgdXMgdGhlIGludGVyY2VwdCwgJFxoYXR7XGJldGF9XzAkIGFuZCB0aGUgc2xvcGUgIG9yIGNvZWZmaWNpZW50IGZvciB0aGUgYGRpc3BsYCB0ZXJtLCAkXGhhdHtcYmV0YX1fMSQuCgoKV2UgY2FuIG9idGFpbiBtb3JlIG91dHB1dCAod2hpY2ggd2UnbGwgd2FudCB3aGVuIHdlIHN0YXJ0IGRvaW5nIGluZmVyZW5jZSksIHVzaW5nIHRoZSBlaXRoZXIgdGhlIGBnZXRfcmVncmVzc2lvbl90YWJsZSgpYCBvciBgdGlkeSgpYCBmdW5jdGlvbi4KCmBgYHtyfQpnZXRfcmVncmVzc2lvbl90YWJsZShsbV9kaXNwbCkKYGBgCgoKYGBge3J9CnRpZHkobG1fZGlzcGwpCmBgYAoKRm9yIG5vdywgd2UgYXJlIGp1c3QgaW50ZXJlc3RlZCBpbiB0aGUgKnRlcm0qIGFuZCAqZXN0aW1hdGUqIGNvbHVtbnMuIFRoZSAqdGVybSogY29sdW1uIGlzIHRoZSB0ZXJtL3ZhcmlhYmxlIGluIHRoZSBtb2RlbCBlcXVhdGlvbi4gVGhlIGNvbHVtbiB0aXRsZWQgKmVzdGltYXRlKiBnaXZlcyB0aGUgZXN0aW1hdGVkIGNvZWZmaWNpZW50cyBvZiB0aGUgdmFyaWFibGVzL3Rlcm1zIChpZS4gdGhlICRcaGF0e1xiZXRhfSRzLiAKCldlIGNvdWxkIHdyaXRlIG91dCB0aGUgZXF1YXRpb24gYWJvdmUgYXM6CgokJApcaGF0e2h3eX0gPSAzNS43MCAtIDMuNTMgZGlzcGwKJCQKClRoZXJlJ3MgZXZlbiBhIGZ1bmN0aW9uIHRoYXQgd2lsbCB3cml0ZSBvdXQgdGhlIG1vZGVsIGVxdWF0aW9uIGZvciB1cyEKCmBgYHtyfQpleHRyYWN0X2VxKGxtX2Rpc3BsLCAKICAgICAgICAgICB3cmFwID0gVFJVRSwgCiAgICAgICAgICAgdXNlX2NvZWZzID0gVFJVRSwKICAgICAgICAgICBpdGFsX3ZhcnMgPSBUUlVFKQpgYGAKCgoKIyMgRXhwbGFuYXRpb24KCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgogIDxzdHJvbmc+WU9VUiBUVVJOISAoZG9uJ3QgbG9vayBhaGVhZCk8L3N0cm9uZz4KCjEuIEhvdyBkbyB5b3UgaW50ZXJwcmV0IHRoZSBpbnRlcmNlcHQsIDM1LjcwPyAgCjIuIEhvdyBkbyB5b3UgaW50ZXJwcmV0IHRoZSBzbG9wZSwgLTMuNTM/Cgo8L2Rpdj4KCgpcClwKXApcClwKXApcClwKXApcClwKXApcClwKXApcClwKCkluIGdlbmVyYWwsIGluIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIHdpdGggb25lIHF1YW50aXRhdGl2ZSBleHBsYW5hdG9yeSB2YXJpYWJsZSwgdGhlIGludGVyY2VwdCBpcyB0aGUgKmF2ZXJhZ2UqIHJlc3BvbnNlIHdoZW4gdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlKHMpIGlzIGVxdWFsIHRvIHplcm8uIFRoZSBzbG9wZSBpcyB0aGUgKmF2ZXJhZ2UqIGNoYW5nZSBpbiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgZm9yIGEgb25lIHVuaXQgaW5jcmVhc2UgaW4gdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlLiBUaGUgaW1hZ2UgYmVsb3cgaGVscHMgaWxsdXN0cmF0ZSB0aGlzIChkb24ndCB3b3JyeSBhYm91dCB0aGUgY29kZSkuCgpgYGB7ciwgZWNobz1GQUxTRX0KZ2dwbG90KG1wZywKICAgICAgIGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArIAogIGdlb21faml0dGVyKCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LCBjb2xvciA9ICJsaWdodGdvbGRlbnJvZCIsIGFscGhhID0gLjUsIHNpemUgPSAyKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMywgY29sb3IgPSAibWVkaXVtcHVycGxlMSIsIGFscGhhID0gLjUsIHNpemUgPSAyKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMzUuNzAgLSAzLjUzKjMsIGNvbG9yID0gIm1lZGl1bXB1cnBsZTEiLCBhbHBoYSA9IC41LCBzaXplID0gMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDM1LjcwIC0gMy41Myo0LCBjb2xvciA9ICJsaWdodGdvbGRlbnJvZCIsIGFscGhhID0gLjUsIHNpemUgPSAyKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6OCkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMTAsNjAsMTApKSArCiAgbGFicyh0aXRsZSA9ICJSYXcgZGF0YSB3aXRoIG1vZGVsIChibHVlIGxpbmUpIikKYGBgCgpUaGUgYmx1ZSBsaW5lIHNob3dzIHRoZSBmaXR0ZWQgbGluZSAodGhlIG1vZGVsKS4gVGhlIHB1cnBsZSB2ZXJ0aWNhbCBsaW5lIGhpZ2hsaWdodHMgZW5naW5lIGRpc3BsYWNlbWVudCB2YWx1ZXMgb2YgMyBjdWJpYyBpbmNoZXMuIFRoZSBwdXJwbGUgaG9yaXpvbnRhbCBsaW5lIHJlZmxlY3RzIHRoZSBmaXR0ZWQgdmFsdWUsICRcaGF0e3l9X2kkIGZvciBhbiBvYnNlcnZhdGlvbiB3aXRoIGEgZGlzcGxhY2VtZW50IG9mIDMuIFRoZSB5ZWxsb3cgdmVydGljYWwgbGluZSBoaWdobGlnaHRzIGVuZ2luZSBkaXNwbGFjZW1lbnQgdmFsdWVzIG9mIDQgY3ViaWMgaW5jaGVzLiBUaGUgeWVsbG93IGhvcml6b250YWwgbGluZSByZWZsZWN0cyB0aGUgZml0dGVkIHZhbHVlLCAkXGhhdHt5fV9pJCBmb3IgYW4gb2JzZXJ2YXRpb24gd2l0aCBhIGRpc3BsYWNlbWVudCBvZiA0LiBUaGUgZGlzdGFuY2UgYmV0d2VlbiB0aGUgcHVycGxlIGFuZCB5ZWxsb3cgbGluZXMgaXMgYWJvdXQgLSAzLjUzLCB3aGljaCByZWZsZWN0cyB0aGUgZXN0aW1hdGVkIHNsb3BlLiBPbiBhdmVyYWdlLCBjYXJzIHdpdGggYSBkaXNwbGFjZW1lbnQgdGhhdCBpcyAxIGN1YmljIGluY2ggbGFyZ2VyIHdpbGwgZ2V0IDMuNTMgTVBHIGxlc3MuIFRoZSBpbnRlcmNlcHQsIDM1LjcwLCBpcyBpbnRlcnByZXRlZCBhcyB0aGUgYXZlcmFnZSBNUEcgZm9yIGEgY2FyIHdpdGggYSBkaXNwbGFjZW1lbnQgb2YgMCBjdWJpYyBpbmNoZXMuCgoKIyMgUHJlZGljdGlvbgoKSW4gYWRkaXRpb24gdG8gaW50ZXJwcmV0aW5nIHRoZSBtb2RlbHMsIHdlIG9mdGVuIHdhbnQgdG8gdXNlIHRoZW0gdG8gcHJlZGljdCBmdXR1cmUgb3IgdW5rbm93biB2YWx1ZXMgb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGZvciBvYnNlcnZhdGlvbnMgd2hlcmUgd2Uga25vdyB0aGUgdmFsdWVzIG9mIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMuIFdlIHdpbGwgaW50cm9kdWNlIGFuZCByZWl0ZXJhdGUgc29tZSB0ZXJtcy4KCiogKiphY3R1YWwgdmFsdWVzKiosIGFsc28gY2FsbGVkIG9ic2VydmVkIHZhbHVlcywgYXJlIGV4YWN0bHkgd2hhdCB5b3UgbWlnaHQgZ3Vlc3MsIHRoZSBhY3R1YWwgdmFsdWVzIG9mIHRoZSByZXNwb25zZSB2YXJpYWJsZS4gVGhleSBhcmUgdGhlICR5X2kkJ3MuCiogKipmaXR0ZWQgdmFsdWVzKiosIGFsc28gY2FsbGVkIHByZWRpY3RlZCB2YWx1ZXMgb3IgbW9kZWwgdmFsdWVzLCBhcmUgdGhlIHZhbHVlIHRoZSByZXNwb25zZSB2YXJpYWJsZSB0YWtlcyB3aGVuIHlvdSBwbHVnIGluIHRoZSB2YWx1ZXMgb2YgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcy4gVGhleSBhcmUgdGhlICRcaGF0e3l9X2kkJ3MuICAKKiAqKnJlc2lkdWFscyoqIGFyZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBhY3R1YWwgYW5kIGZpdHRlZCB2YWx1ZXMsICR5X2kgLSBcaGF0e3l9X2kkJ3MuIAoKYGBge3IsIGVjaG89RkFMU0V9CmF1Z21lbnQobG1fZGlzcGwsIGRhdGEgPSBtcGcpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsKICBnZW9tX3BvaW50KGFscGhhPS4zKSArIAogIGdlb21fbGluZShhZXMoeCA9IGRpc3BsLCB5ID0gLmZpdHRlZCksIGNvbG9yID0gImJsdWUiLCBzaXplID0gLjUpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gZGlzcGwsIHkgPSAuZml0dGVkKSwgY29sb3IgPSAiYmx1ZSIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHhlbmQgPSBkaXNwbCwgCiAgICAgICAgICAgICAgICAgICB5ZW5kID0gMzUuNzAgLSAzLjUzKmRpc3BsKSwgCiAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6OCkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMTAsNjAsMTApKSArCiAgbGFicyh0aXRsZSA9ICJBY3R1YWwgKGJsYWNrKSwgRml0dGVkIChibHVlKSwgYW5kIHJlc2lkdWFscyAocmVkKSIpCmBgYAoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+CiAgPHN0cm9uZz5ZT1VSIFRVUk4hIChkb24ndCBsb29rIGFoZWFkKTwvc3Ryb25nPgoKSG93IHdvdWxkIHlvdSBmaW5kIHRoZSBwcmVkaWN0ZWQgaGlnaHdheSBtaWxlcyBwZXIgZ2FsbG9uIGZvciBhIHZlaGljbGUgd2l0aCBhbiBlbmdpbmUgZGlzcGxhY2VtZW50IG9mIDQuNSBsaXRyZXM/Cgo8L2Rpdj4KClwKXApcClwKXApcClwKXApcClwKXApcClwKXApcClwKCldlIGNhbiB1c2UgdGhlIGBhdWdtZW50KClgIGZ1bmN0aW9uIHRvIGhlbHAgdXMgZG8gdGhpcy4gVGhlIGBnZXRfcmVncmVzc2lvbl9wb2ludHMoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYG1vZGVybmRpdmVgIGxpYnJhcnkgYWxzbyB3b3Jrcy4gVGhlIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhlIG91dHB1dCBhcmUgc2xpZ2h0bHkgZGlmZmVyZW50LCB0aG91Z2gsIGFuZCBpdCB3aWxsIG5vdCB3b3JrIGxhdGVyIHdoZW4gd2UgZG8gbG9naXN0aWMgcmVncmVzc2lvbi4KCldlIGNhbiB1c2UgaXQgdG8gZmluZCB0aGUgcHJlZGljdGVkIHJlc3BvbnNlcyBmb3IgYWxsIHRoZSBkYXRhIHVzZWQgdG8gZml0IHRoaXMgbW9kZWwuIEJlbG93IEkganVzdCBzaG93IHRoZSBmaXJzdCAxMCByb3dzLgoKYGBge3J9CmF1Z19kaXNwbCA8LSBhdWdtZW50KGxtX2Rpc3BsKQoKYXVnX2Rpc3BsICU+JSAKICBzbGljZSgxOjEwKQpgYGAKClRoaXMgZGF0YXNldCBjb250YWlucyB0aGUgcmVzcG9uc2UgdmFyaWFibGUsIGFsbCBleHBsYW5hdG9yeSB2YXJpYWJsZXMsIGFuZCBzb21lIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24uIE9uZSBwaWVjZSBvZiBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGlzIHRoZSB2YXJpYWJsZSAqLmZpdHRlZCouIFRoZXNlIGFyZSB0aGUgcHJlZGljdGVkIChmaXR0ZWQpIHZhbHVlcy4gCgpTb21ldGltZXMgd2Ugd2FudCBhbGwgdGhlIHZhcmlhYmxlcyBpbiBvdXIgb3JpZ2luYWwgZGF0YXNldCwgcGx1cyB0aGVzZSBuZXcgdmFyaWFibGVzLiBXZSBjYW4gZ2V0IHRoYXQsIGJ5IGFkZGluZyBhIGBkYXRhID0gYCBhcmd1bWVudCB0byB0aGUgYGF1Z21lbnQoKWAgZnVuY3Rpb24uIAoKYGBge3J9CmF1Z21lbnQobG1fZGlzcGwsIGRhdGEgPSBtcGcpICU+JSAKICBzbGljZSgxOjEwKQpgYGAKCldlIGNhbiBhbHNvIHVzZSBgYXVnbWVudCgpIGAgdG8gcHJlZGljdCB1bmtub3duIG9yIGZ1dHVyZSB2YWx1ZXMsIGxpa2UgcHJlZGljdGluZyB0aGUgaGlnaHdheSBNUEcgZm9yIGEgZGlzcGxhY2VtZW50IG9mIDQuNS4gV2UgbmVlZCB0byBhZGQgYSBgbmV3ZGF0YSA9IGAgYXJndW1lbnQgdG8gdGhlIGZ1bmN0aW9uIGFuZCBnaXZlIGl0IGEgZGF0YXNldCBhcyBhbiBhcmd1bWVudC4gQmVjYXVzZSB0aGVyZSBpcyBqdXN0IG9uZSB2YWx1ZSwgd2UgY2FuIGNyZWF0ZSB0aGUgZGF0YXNldCAib24gdGhlIGZseS4iIFRoZSBgdGliYmxlKClgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIGNyZWF0ZSBhIGRhdGFzZXQuIElmIHdlIHdhbnRlZCBtdWx0aXBsZSBwcmVkaWN0aW9ucywgd2Ugc2VwYXJhdGUgdGhlbSB3aXRoIGNvbW1hcyBpbnNpZGUgdGhlIGBjKClgLiBCZSBzdXJlIHRoZSB2YXJpYWJsZSBpcyBuYW1lZCBFWEFDVExZIGFzIHRoZSB2YXJpYWJsZSBuYW1lIGFwcGVhcnMgaW4gdGhlIG9yaWdpbmFsIGRhdGFzZXQuCgpgYGB7cn0KYXVnbWVudChsbV9kaXNwbCwgCiAgICAgICAgbmV3ZGF0YSA9IHRpYmJsZShkaXNwbCA9IGMoNC41KSkpCgojZXhhbXBsZSBvZiBtb3JlIHRoYW4gb25lIHByZWRpY3Rpb24KYXVnbWVudChsbV9kaXNwbCwgCiAgICAgICAgbmV3ZGF0YSA9IHRpYmJsZShkaXNwbCA9IGMoMywgMy41LCA0LjAsIDQuNSwgNS4wLCA1LjUpKSkKCiNPUgoKbXlfbmV3X2RhdGEgPC0gdGliYmxlKGRpc3BsID0gYygzLCAzLjUsIDQuMCwgNC41LCA1LjAsIDUuNSksCiAgICAgICAgICAgICAgICAgICAgICBnYXJiYWdlID0gYygxLDIsMyw0LDUsNikpCgphdWdtZW50KGxtX2Rpc3BsLCAKICAgICAgICBuZXdkYXRhID0gbXlfbmV3X2RhdGEpCgpgYGAKCldlIGNhbiBhbHNvIHVzZSB0aGlzIHRvIHBsb3Qgb3VyIGZpdHRlZCBsaW5lIG9uIHRoZSBwbG90IG9mIHRoZSBvcmlnaW5hbCBkYXRhLgoKYGBge3J9CmF1Z21lbnQobG1fZGlzcGwsIGRhdGEgPSBtcGcpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsgCiAgZ2VvbV9saW5lKGFlcyh4ID0gZGlzcGwsIHkgPSAuZml0dGVkKSwgY29sb3IgPSAiYmx1ZSIpICsgI3RoZSBmaXR0ZWQgbGluZQogIGxhYnMoeCA9ICJFbmdpbmUgRGlzcGxhY2VtZW50IiwgeSA9ICJIaWdod2F5IE1QRyIpCmBgYAoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+CiAgPHN0cm9uZz5ZT1VSIFRVUk4hPC9zdHJvbmc+CgoxLiBGaXQgYSBtb2RlbCB0aGF0IHVzZWQgKmN0eSogdG8gZXhwbGFpbiAqaHd5Ki4gV3JpdGUgZG93biB0aGUgbW9kZWwgZXF1YXRpb24uICAKMi4gSW50ZXJwcmV0IHRoZSBpbnRlcmNlcHQgYW5kIHNsb3BlIGZyb20gdGhlIG1vZGVsLiAgCjMuIFBsb3QgdGhlIGxpbmUgb24gdG9wIG9mIGEgc2NhdHRlcnBsb3Qgb2YgdGhlIGRhdGEuICAKNC4gVXNlIHRoZSBtb2RlbCB0byBwcmVkaWN0IHRoZSBoaWdod2F5IE1QRywgKmh3eSosICBmb3IgYSBjYXIgdGhhdCBnZXRzIDI0IE1QRyBpbiB0aGUgY2l0eSBhbmQgZm9yIGEgY2FyIHRoYXQgZ2V0cyA2NSBNUEcgaW4gdGhlIGNpdHkuIERvIHlvdSB0cnVzdCBib3RoIG9mIHRoZXNlIHByZWRpY3Rpb25zPwoKPC9kaXY+CgojIExpbmVhciByZWdyZXNzaW9uIHdpdGggb25lIGNhdGVnb3JpY2FsIChmYWN0b3IpIHZhcmlhYmxlCgpOb3csIGxldCdzIGV4YW1pbmUgYSBtb2RlbCB0aGF0IHVzZXMgYSBjYXRlZ29yaWNhbCAoZmFjdG9yKSBleHBsYW5hdG9yeSB2YXJpYWJsZS4gRmlyc3QsIHdlJ2xsIHRyeSBqdXN0IHB1dHRpbmcgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBpbiB0aGUgbW9kZWwgZXF1YXRpb24uCgpgYGB7cn0KbG1fZHJ2IDwtIGxtKGh3eSB+IGRydiwKICAgICAgICAgICAgICAgICBkYXRhPW1wZykKdGlkeShsbV9kcnYpCmBgYAoKSXQgZGlkIHNvbWV0aGluZy4gKipXSEFUIGRpZCBpdCBkbz8gQW55IGlkZWFzPyoqIEZpcnN0LCBsZXQncyBydW4gdGhlIGZvbGxvd2luZyBjb2RlLiAqKkhvdyBpcyB3aGF0IHdlIHNlZSBiZWxvdyByZWxhdGVkIHRvIHdoYXQgd2Ugc2VlIGFib3ZlPyoqCgpgYGB7cn0KbXBnICU+JSAKICBncm91cF9ieShkcnYpICU+JSAKICBzdW1tYXJpemUobWVhbl9od3kgPSBtZWFuKGh3eSkpCmBgYAoKU2luY2UgUiBjYW5ub3QgdXNlICpkcnYqIGRpcmVjdGx5IChpdHMgdmFsdWVzIGFyZSB3b3JkcyBub3QgbnVtYmVycyksIGl0IGlzIGNyZWF0aW5nIDIgbmV3IHZhcmlhYmxlczogCgoqZHJ2ZiogaXMgMSBpZiAqZHJ2KiBpcyAqZiogYW5kIDAgb3RoZXJ3aXNlLCBhbmQgCipkcnZyKiAgaXMgMSBpZiAqZHJ2KiBpcyAqciogYW5kIDAgb3RoZXJ3aXNlLiAgCgpMZXQncyB3cml0ZSBvdXQgdGhlIG1vZGVsIGVxdWF0aW9uOgoKJCQKXGhhdHtod3l9ID0gMTkuMTggKyA4Ljk5ZHJ2ZiArIDEuODNkcnZyCiQkCgpPciwgd2UgY2FuIHdyaXRlIGNvZGUgdGhhdCB3cml0ZXMgdGhlIGVxdWF0aW9uIGZvciB1czoKCmBgYHtyfQpleHRyYWN0X2VxKGxtX2RydiwgCiAgICAgICAgICAgd3JhcCA9IFRSVUUsIAogICAgICAgICAgIHVzZV9jb2VmcyA9IFRSVUUsCiAgICAgICAgICAgaXRhbF92YXJzID0gVFJVRSkKYGBgCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4KICA8c3Ryb25nPllPVVIgVFVSTiEgKGRvbid0IGxvb2sgYWhlYWQpPC9zdHJvbmc+CgoxLiBXaGVuIHdpbGwgYm90aCAqZHJ2ZiogYW5kICpkcnZyKiB0YWtlIGEgdmFsdWUgb2YgMD8gIAoyLiBXaGF0IGlzIHRoZSBwcmVkaWN0ZWQgdmFsdWUgZm9yIGEgZnJvbnQgd2hlZWwgZHJpdmUgdmVoaWNsZT8gIAozLiBXaGF0IGlzIHRoZSBwcmVkaWN0ZWQgdmFsdWUgZm9yIGEgcmVhciB3aGVlbCBkcml2ZSB2ZWhpY2xlPwo0LiBXaGF0IGlzIHRoZSBwcmVkaWN0ZWQgdmFsdWUgZm9yIGEgNCB3aGVlbCBkcml2ZSB2ZWhpY2xlPyAKNS4gSW50ZXJwcmV0IGVhY2ggb2YgdGhlIGNvZWZmaWNpZW50cy4KCjwvZGl2PgoKXApcClwKXApcClwKXApcClwKXApcClwKXApcClwKXAoKSW4gZ2VuZXJhbCwgaW4gYSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gd2l0aCBvbmUgY2F0ZWdvcmljYWwgZXhwbGFuYXRvcnkgdmFyaWFibGUsIHRoZSBpbnRlcmNlcHQgaXMgdGhlICphdmVyYWdlKiByZXNwb25zZSB3aGVuIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZSBpcyBhdCB0aGUgImJhc2VsaW5lIiBvciAicmVmZXJlbmNlIiBsZXZlbC4gVGhhdCBpcyB0aGUgbGV2ZWwgb2YgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHRoYXQgZG9lcyBub3QgaGF2ZSBhIGR1bW15IHZhcmlhYmxlIGNyZWF0ZWQgZm9yIGl0LiBOb3RpY2UsIHRoYXQgaXMgYWxzbyB0aGUgYXZlcmFnZSByZXNwb25zZSB3aGVuIGFsbCBvdGhlciB2YXJpYWJsZXMgYXJlIHplcm8sIHNhbWUgYXMgd2hlbiB3ZSBoYWQgb25lIHF1YW50aXRhdGl2ZSBleHBsYW5hdG9yeSB2YXJpYWJsZS4gCgpUaGUgb3RoZXIgY29lZmZpY2llbnRzIGFyZSB0aGUgZGlmZmVyZW5jZSBpbiBhdmVyYWdlIHJlc3BvbnNlIGJldHdlZW4gdGhlIGxldmVsIG9mIHRoZSBpbmRpY2F0b3IgdmFyaWFibGUgYW5kIHRoZSByZWZlcmVuY2UgbGV2ZWwuIFRoaXMgaXMgc2ltaWxhciB0byAiaW5jcmVhc2luZyB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGUgYnkgb25lLCIgYnV0IGl0IGRvZXNuJ3QgcmVhbGx5IG1ha2Ugc2Vuc2UgdG8gdGFsayBhYm91dCBpdCB0aGF0IHdheS4KCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgogIDxzdHJvbmc+WU9VUiBUVVJOITwvc3Ryb25nPgoKMS4gQ3JlYXRlIGEgbW9kZWwgdGhhdCB1c2VzICpjbGFzcyogdG8gZXhwbGFpbiAqaHd5Ki4gSW50ZXJwcmV0IGVhY2ggb2YgdGhlIGNvZWZmaWNpZW50cy4gIAoyLiBXaGF0IGlzIHRoZSBwcmVkaWN0ZWQgaGlnaHdheSBNUEcgZm9yIGEgcGlja3VwPwoKPC9kaXY+CgoKIyBJbnRlcmNlcHQgT25seSBNb2RlbAoKQSAqdmVyeSogc2ltcGxlIG1vZGVsIHdlIHRodXMgZmFyIGhhdmUgYXZvaWRlZCBjb25zaWRlcmluZywgaXMgb25lIHdpdGggb25seSBhbiBpbnRlcmNlcHQuIFdlIGNhbiBmaXQgdGhhdCBtb2RlbCB1c2luZyB0aGUgY29kZSBiZWxvdy4gCgpgYGB7cn0KbG1faW50IDwtIGxtKGh3eSB+IDEsIGRhdGE9bXBnKQpgYGAKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgogIDxzdHJvbmc+WU9VUiBUVVJOITwvc3Ryb25nPgogIAoxLiBIb3cgd291bGQgeW91IGludGVycHJldCB0aGUgZXN0aW1hdGVkIGludGVyY2VwdD8gIAoyLiBXaGF0IGRvZXMgdGhpcyBtb2RlbCBtZWFuIGNvbmNlcHR1YWxseT8gV2hlbiB3b3VsZCB0aGlzIGJlIGEgImdvb2QiIG1vZGVsPwoKPC9kaXY+CgoK