Introduction to Simple Linear Regression
Simple linear regression is a supervised learning method that can be used to model a single quantitative response \(Y\) as a function of a single quantitative predictor \(X\).
Hypothetical and Fitted Models
When performing simple linear regression to describe a relationship between two variables \(X\) and \(Y\), we begin by assuming that the relationship is determined by an unknown hypothetical model (or population model) of the form:
\[Y = \beta_0 + \beta_1 X + \varepsilon\]
We collect a sample of paired observations \((x_i, y_i)\) to use as a training set. We use the training set to create a fitted model. The fitted model is an approximation of the hypothetical model, and can be written in either of the following (equivalent) forms:
\[\hat Y = \hat \beta_0 + \hat \beta_1 X\] \[Y = \hat \beta_0 + \hat \beta_1 X + \hat \varepsilon\]
Given a paired observation \(\left(x_i, y_i \right)\), the fitted value of y given \(x_i\) is given by:
\[\hat y_i = \hat\beta_0 + \hat\beta_1 x_i\]
The residual associated with the observation is given by:
\[\hat \varepsilon_i = y_i - \hat y_i\]

Least Squares Regression
As discussed in previous lessons, we will select our fitted model \(\hat Y = \hat \beta_0 + \hat \beta_1 X\) by choosing parameter estimates \(\hat\beta_0\) and \(\hat\beta_1\) so that the following quantities are minimized on the training set:
\[SSE = \sum\limits_{i=1}^n \hat \varepsilon_i^2 \hspace{2 em} MSE = \frac{1}{n}\sum\limits_{i=1}^n \hat \varepsilon_i^2\] At present, we will work with \(SSE\). To emphasize that \(SSE\) is a function of the proposed parameter estimates \(\hat\beta_0\) and \(\hat\beta_1\), we sometimes write:
\[SSE\left ( \hat\beta_0, \hat\beta_1 \right) = \sum\limits_{i=1}^n \hat \varepsilon_i^2 = \sum\limits_{i=1}^n \left( y_i - \hat\beta_0 - \hat\beta_1 x_i \right) ^2\]

Parameter Estimates
Through the magic of calculus, we can show that for a given training set \(\left(x_i, y_i \right)\), the quantity \(SSE\left ( \hat\beta_0, \hat\beta_1 \right)\) has a unique minimum which is obtained as follows:
Define quantities \(S_{XX}\) and \(S_{XY}\) by:
\[S_{XX} = \sum\limits_{i=1}^n \left(x_i - \bar x \right)^2 = \sum\limits_{i=1}^n x_i^2 - n\bar x^2\]
\[S_{XY} = \sum\limits_{i=1}^n \left(x_i - \bar x \right)\left(y_i - \bar y \right) = \sum\limits_{i=1}^n x_i y_i - n\bar x \bar y\]
The parameter estimates that minimize SSE on the training set are given by:
\[\hat\beta_1= \frac{S_{XY}}{S_{XX}} \hspace{20px} \mathrm{and} \hspace{20px}\hat\beta_0 = \bar y - \hat\beta_1 \bar x\]
The derivation of the results above are shown in the accompanying notebook titled 3.1.a - Derivation of Parameter Estimates.
R-Squared
The quantities \(SSE\) and \(MSE\) provide measures for the quality of a fitted model. However, these metrics are measured in the square of the units in which \(Y\) is measured. This makes it difficult to use these quanties to assess the fitted model. Fortunately, there is a unitless measure, called the R-Squared statistic, that we can use to score our models.
The R-Squared statistic is given by:
\[R^2 = 1 - \frac{SSE}{SST}\]
Where:
\[SST = \sum\limits_{i=1}^n \left(y_i - \bar y \right)^2 \]
Note that the sample variance of the response variable \(Y\) is given by \(s_Y^2 = SST / (n-1)\). As such, \(SST\) is, in a sense, a measure of the variability of the response variable.
It can be shown that \(R^2\) is always between 0 and 1. Furthermore, we can show that \(R^2\) measures the proportion of variability in the response variable \(Y\) that the fitted model is able to explain using the predictor \(X\).
Example: Simple Linear Regression in R
In the following example, we will be interested in studying the relationship between the price and mileage of used cars.
Generate Data and Calculate Descriptive Statistics
Assume that we gather a sample of 20 recently sold used 2016 Ford Fictus automobiles. For each vehicle, we record the sales price (in thousands of dollars) and mileage (in thousands of miles) of the vehicle. In the code chunk below, We create vectors mileage and price to store these values.
mileage <- c( 3.1, 4.1, 5.3, 7.1, 19.5,
28.3, 36.8, 37.2, 42.3, 52.3,
53.3, 53.4, 63.2, 68.4, 82.3,
83.9, 88.4, 97.6, 99.7, 105.9)
price <- c(53.7, 56.8, 58.5, 42.0, 48.9,
33.2, 22.2, 32.6, 30.3, 19.8,
26.1, 24.9, 18.1, 11.7, 13.3,
23.4, 13.2, 13.6, 14.8, 4.6)
Descriptive Statistics
Before constructing our model, let’s calculate the sample mean and standard deviation for both the mileage and price of the automobiles in the sample.
xbar <- mean(mileage)
sx <- sd(mileage)
x_stats <- c(xbar, sx)
names(x_stats) <- c('mean', 'stdev')
ybar <- mean(price)
sy <- sd(price)
y_stats <- c(ybar, sy)
names(y_stats) <- c('mean', 'stdev')
rbind(x_stats, y_stats)
mean stdev
x_stats 51.605 33.99704
y_stats 28.085 16.17273
Notice that the the standard deviation of the price is very large with respect to its mean. This indicates that if we were to use only the sample mean price as an estimate for the price of one of these automobiles, there would be a lot of uncertainty in our estimate.
Relationship between Price and Mileage
To study the relationship between price and mileage of this automobile model, we might create a scatterplot from the paired observations in our sample.
plot(price ~ mileage, pch=21, bg='orange', col='black', cex=1.5,
xlab = 'Mileage (in 1000s of Miles)', ylab = 'Price (in 1000s of Dollars)',
main = 'Relationship between Mileage and Price')

We see from this plot that the cars with greater mileage tend to have a lower sales price (as you might expect). In fact, it appears that the relationship between the price and mileage of the vehicles might be roughly linear.
Creating the Fitted Model
We will use the lm function in R to find a linear model that attempts to capture the relationship between price and mileage. We will store the resulting model in a variable called model and will then use the summary function to get some information about the model.
model <- lm(price ~ mileage)
summary(model)
Call:
lm(formula = price ~ mileage)
Residuals:
Min 1Q Median 3Q Max
-12.329 -4.961 -1.335 5.862 10.259
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 50.54834 2.77369 18.224 4.76e-13 ***
mileage -0.43529 0.04523 -9.625 1.60e-08 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 6.702 on 18 degrees of freedom
Multiple R-squared: 0.8373, Adjusted R-squared: 0.8283
F-statistic: 92.63 on 1 and 18 DF, p-value: 1.604e-08
There is a lot of information in this summary. We will eventually learn how to interpret all of the information presented here. For now, lets focus on one piece of information: the coefficient estimates.
The coefficient estimates are shown in the summary above, but we can access them directly as follows:
model$coefficients
(Intercept) mileage
50.5483433 -0.4352939
These are the coefficients that determine the slope and intercept of our linear model. This tells us that our model has the following form:
Predicted Price = 50.55 - 0.4353 · Mileage
This relationship between price and mileage can also be represented as follows:
Actual Price = 50.55 - 0.4353 · Mileage + Unexplained Error
Assessing the Quality of the Fit
The summary of the model also reports the value of \(R^2\) under the listing “Multiple R-squared”. It can also be accessed directly as follows:
summary(model)$r.squared
[1] 0.8372993
This means that our model is able to explain roughly 83.72% of the variability in the price of a used 2016 Ford Fictus in terms of the mileage for the vehicle.
Plotting the Fitted Model
Let’s add our regression line to the scatter plot of price and mileage.
plot(price ~ mileage, pch=21, bg='orange', col='black', cex=1.5,
xlab = 'Mileage (in 1000s of Miles)', ylab = 'Price (in 1000s of Dollars)',
main = 'Relationship between Mileage and Price')
abline(model$coefficients, col='cadetblue', lwd=2)

Fitted Values
The fitted value for any particlar observation in our sample is the price that the model predicts for the car, given the mileage of that car. This is obtained by plugging the mileage into the equation:
Predicted Price = 50.55 - 0.4353 · Mileage
The fitted values for the cars in our sample are stored within the model variable
round(model$fitted.values,1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
49.2 48.8 48.2 47.5 42.1 38.2 34.5 34.4 32.1 27.8 27.3 27.3 23.0 20.8 14.7
16 17 18 19 20
14.0 12.1 8.1 7.1 4.5
To illustrate this idea, we will add the fitted values to our scatterplot.

Residuals
The residuals are the error in the predicted prices. The residual for a particular observation is given by the equation:
Residal = Actual Price - Predicted Price
Residuals reflect the uncertainty remaining in our model.

Our model object model contains the residuals for the observations in our sample.
res <- model$residuals
round(res,1)
1 2 3 4 5 6 7 8 9 10 11 12
4.5 8.0 10.3 -5.5 6.8 -5.0 -12.3 -1.8 -1.8 -8.0 -1.2 -2.4
13 14 15 16 17 18 19 20
-4.9 -9.1 -1.4 9.4 1.1 5.5 7.7 0.1
Since the residuals represent the uncertainly in our predictions, we would like to get a sense as to how they are distributed. To that end, we generate a histogram of the residuals.
hist(res, col='orchid')

It seems reasonable to assume that the residuals might be normally distributed with a mean of zero. Let’s calculate their sample mean and standard deviation.
res_mean <- mean(res)
res_sd <- sd(res)
res_stats <- c(res_mean, res_sd)
names(res_stats) <- c('mean', 'stdev')
round(res_stats,4)
mean stdev
0.0000 6.5235
Using the Model to Make Predictions
Assume that we are interested in purchasing a used 2016 Ford Fictus with 45,000 miles. We would like to use our model to determine a fair price for the car. We could calulcate this by plugging 45 into the equation for our model.That gives:
Predicted Price = 50.55 - 0.4353 · 45 = 30.96
In other words, our model predicts that such a vehicle should cost (on average) $30,960.
We can use the R function predict to calculate this predicted value.
newdata = data.frame(mileage=c(45))
predict(model, newdata)
1
30.96012
We know that the predictions made by the model are not 100% accurate. We expect there to be some error in the predictions. To better understand how much the actual price of a car with 45,000 miles might vary, we will use predict to create an interval that we are 95% certain contains the true price of the car. This interval is called a prediction interval.
predict(model, newdata, interval = 'prediction', level=0.95)
fit lwr upr
1 30.96012 16.51791 45.40232
We can use the predict function to generate predictions for several new observations at once. Let’s say that we would like to contruct 95% prediction intervals for the prices of 3 vehicles: one with 40,000 miles, one with 50,000 miles, and one with 70,000 miles. We can do so as follows:
newdata = data.frame(mileage=c(40, 50, 70))
predict(model, newdata, interval='prediction', level=0.95)
fit lwr upr
1 33.13659 18.665948 47.60722
2 28.78365 14.354278 43.21302
3 20.07777 5.543722 34.61181
LS0tDQp0aXRsZTogIkxlc3NvbiAzLjEgLSBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24iDQphdXRob3I6ICJSb2JiaWUgQmVhbmUiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdGhlbWU6IGZsYXRseQ0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogNA0KLS0tDQoNCiMjIyAqKkludHJvZHVjdGlvbiB0byBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24qKg0KDQpTaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gaXMgYSBzdXBlcnZpc2VkIGxlYXJuaW5nIG1ldGhvZCB0aGF0IGNhbiBiZSB1c2VkIHRvIG1vZGVsIGEgc2luZ2xlIHF1YW50aXRhdGl2ZSByZXNwb25zZSAkWSQgYXMgYSBmdW5jdGlvbiBvZiBhIHNpbmdsZSBxdWFudGl0YXRpdmUgcHJlZGljdG9yICRYJC4gDQoNCiMjIyMgKipIeXBvdGhldGljYWwgYW5kIEZpdHRlZCBNb2RlbHMqKg0KDQpXaGVuIHBlcmZvcm1pbmcgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIHRvIGRlc2NyaWJlIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIHZhcmlhYmxlcyAkWCQgYW5kICRZJCwgd2UgYmVnaW4gYnkgYXNzdW1pbmcgdGhhdCB0aGUgcmVsYXRpb25zaGlwIGlzIGRldGVybWluZWQgYnkgYW4gdW5rbm93biAqKmh5cG90aGV0aWNhbCBtb2RlbCoqIChvciAqKnBvcHVsYXRpb24gbW9kZWwqKikgb2YgdGhlIGZvcm06IA0KDQokJFkgPSBcYmV0YV8wICsgXGJldGFfMSBYICsgXHZhcmVwc2lsb24kJA0KDQoNCldlIGNvbGxlY3QgYSBzYW1wbGUgb2YgcGFpcmVkIG9ic2VydmF0aW9ucyAkKHhfaSwgeV9pKSQgdG8gdXNlIGFzIGEgKip0cmFpbmluZyBzZXQqKi4gV2UgdXNlIHRoZSB0cmFpbmluZyBzZXQgdG8gY3JlYXRlIGEgKipmaXR0ZWQgbW9kZWwqKi4gVGhlIGZpdHRlZCBtb2RlbCBpcyBhbiBhcHByb3hpbWF0aW9uIG9mIHRoZSBoeXBvdGhldGljYWwgbW9kZWwsIGFuZCBjYW4gYmUgd3JpdHRlbiBpbiBlaXRoZXIgb2YgdGhlIGZvbGxvd2luZyAoZXF1aXZhbGVudCkgZm9ybXM6DQoNCg0KJCRcaGF0IFkgPSBcaGF0IFxiZXRhXzAgKyBcaGF0IFxiZXRhXzEgWCQkDQokJFkgPSBcaGF0IFxiZXRhXzAgKyBcaGF0IFxiZXRhXzEgWCArIFxoYXQgXHZhcmVwc2lsb24kJA0KDQpHaXZlbiBhIHBhaXJlZCBvYnNlcnZhdGlvbiAkXGxlZnQoeF9pLCB5X2kgXHJpZ2h0KSQsIHRoZSAqKmZpdHRlZCB2YWx1ZSBvZiB5IGdpdmVuICR4X2kkKiogaXMgZ2l2ZW4gYnk6IA0KDQoNCiQkXGhhdCB5X2kgPSBcaGF0XGJldGFfMCArIFxoYXRcYmV0YV8xIHhfaSQkDQoNCg0KVGhlICoqcmVzaWR1YWwqKiBhc3NvY2lhdGVkIHdpdGggdGhlIG9ic2VydmF0aW9uIGlzIGdpdmVuIGJ5Og0KDQo8Y2VudGVyPg0KJCRcaGF0IFx2YXJlcHNpbG9uX2kgPSB5X2kgLSBcaGF0IHlfaSQkDQo8L2NlbnRlcj4NCg0KYGBge3IsIGVjaG89RkFMU0V9DQpzZXQuc2VlZCgzKQ0KeCA8LSBydW5pZihuPTEwLCA1LCAxNSkNCnkgPC0gNCArIDAuMyp4ICsgcm5vcm0oMTAsIDAsIDAuMikNCm1vZGVsIDwtIGxtKHl+eCkNCnBsb3QoeSB+IHgsIHBjaD0yMSwgYmc9J29yYW5nZScsIGNvbD0nYmxhY2snLCBjZXg9MS41KQ0KYWJsaW5lKDQsIDAuMywgY29sPSdkYXJrcmVkJywgbHR5PTIsIGx3ZD0yKQ0KYWJsaW5lKG1vZGVsJGNvZWZmaWNpZW50cywgY29sPSdibHVlJywgbHdkPTIpDQpwb2ludHMoeSB+IHgsIHBjaD0yMSwgYmc9J29yYW5nZScsIGNvbD0nYmxhY2snLCBjZXg9MS41KQ0KdGV4dCg3LCA2LjUsICdIeXBvdGhldGljYWwgTW9kZWwnLCBjb2w9J2RhcmtyZWQnKQ0KdGV4dCg5LjUsIDYuNSwgJ0ZpdHRlZCBNb2RlbCcsIGNvbD0nYmx1ZScpDQpgYGANCg0KDQojIyMjICoqTGVhc3QgU3F1YXJlcyBSZWdyZXNzaW9uKioNCg0KQXMgZGlzY3Vzc2VkIGluIHByZXZpb3VzIGxlc3NvbnMsIHdlIHdpbGwgc2VsZWN0IG91ciBmaXR0ZWQgbW9kZWwgJFxoYXQgWSA9IFxoYXQgXGJldGFfMCArIFxoYXQgXGJldGFfMSBYJCBieSBjaG9vc2luZyBwYXJhbWV0ZXIgZXN0aW1hdGVzICRcaGF0XGJldGFfMCQgYW5kICRcaGF0XGJldGFfMSQgc28gdGhhdCB0aGUgZm9sbG93aW5nIHF1YW50aXRpZXMgYXJlIG1pbmltaXplZCBvbiB0aGUgdHJhaW5pbmcgc2V0Og0KDQoNCiQkU1NFID0gXHN1bVxsaW1pdHNfe2k9MX1ebiBcaGF0IFx2YXJlcHNpbG9uX2leMiBcaHNwYWNlezIgZW19IE1TRSA9IFxmcmFjezF9e259XHN1bVxsaW1pdHNfe2k9MX1ebiBcaGF0IFx2YXJlcHNpbG9uX2leMiQkDQpBdCBwcmVzZW50LCB3ZSB3aWxsIHdvcmsgd2l0aCAkU1NFJC4gVG8gZW1waGFzaXplIHRoYXQgJFNTRSQgaXMgYSBmdW5jdGlvbiBvZiB0aGUgcHJvcG9zZWQgcGFyYW1ldGVyIGVzdGltYXRlcyAkXGhhdFxiZXRhXzAkIGFuZCAkXGhhdFxiZXRhXzEkLCB3ZSBzb21ldGltZXMgd3JpdGU6DQoNCiQkU1NFXGxlZnQgKCBcaGF0XGJldGFfMCwgXGhhdFxiZXRhXzEgXHJpZ2h0KSA9IFxzdW1cbGltaXRzX3tpPTF9Xm4gXGhhdCBcdmFyZXBzaWxvbl9pXjIgPSBcc3VtXGxpbWl0c197aT0xfV5uIFxsZWZ0KCB5X2kgLSBcaGF0XGJldGFfMCAtIFxoYXRcYmV0YV8xIHhfaSBccmlnaHQpIF4yJCQNCg0KYGBge3IsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTh9DQpzZXQuc2VlZCgzKQ0KeCA8LSBydW5pZihuPTEwLCA1LCAxNSkNCnkgPC0gNCArIDAuMyp4ICsgcm5vcm0oMTAsIDAsIDAuMikNCm1vZGVsIDwtIGxtKHl+eCkNCg0KcHJlZDEgPC0gNSArIDAuMip4DQpwcmVkMiA8LSAzICsgMC40KngNCnJlczEgPC0geSAtIHByZWQxDQpyZXMyIDwtIHkgLSBwcmVkMg0KcmVzMyA8LSBtb2RlbCRyZXNpZHVhbHMNCg0Kc3NlMSA8LSBzdW0ocmVzMV4yKQ0Kc3NlMiA8LSBzdW0ocmVzMl4yKQ0Kc3NlMyA8LSBzdW0ocmVzM14yKQ0KDQoNCnBhcihtZnJvdz1jKDEsMykpDQoNCnBsb3QoeSB+IHgsIHBjaD0yMSwgYmc9J29yYW5nZScsIGNvbD0nYmxhY2snLCBjZXg9MS41LCBtYWluPXBhc3RlKCJNb2RlbCAxOiBTU0UgPSAiLCByb3VuZChzc2UxLDMpKSkNCmFibGluZSg1LCAwLjIsIGNvbD0nYmx1ZScsIGx3ZD0yKQ0Kc2VnbWVudHMoeCwgcHJlZDEsIHgsIHksIGNvbD0ncmVkJywgbHdkPTIpDQpwb2ludHMoeSB+IHgsIHBjaD0yMSwgYmc9J29yYW5nZScsIGNvbD0nYmxhY2snLCBjZXg9MS41KQ0KDQpwbG90KHkgfiB4LCBwY2g9MjEsIGJnPSdvcmFuZ2UnLCBjb2w9J2JsYWNrJywgY2V4PTEuNSwgbWFpbj1wYXN0ZSgiTW9kZWwgMjogU1NFID0gIiwgcm91bmQoc3NlMiwzKSkpDQphYmxpbmUoMywgMC40LCBjb2w9J2JsdWUnLCBsd2Q9MikNCnNlZ21lbnRzKHgsIHByZWQyLCB4LCB5LCBjb2w9J3JlZCcsIGx3ZD0yKQ0KcG9pbnRzKHkgfiB4LCBwY2g9MjEsIGJnPSdvcmFuZ2UnLCBjb2w9J2JsYWNrJywgY2V4PTEuNSkNCg0KcGxvdCh5IH4geCwgcGNoPTIxLCBiZz0nb3JhbmdlJywgY29sPSdibGFjaycsIGNleD0xLjUsIG1haW49cGFzdGUoIkZpdHRlZCBNb2RlbDogU1NFID0gIiwgcm91bmQoc3NlMywzKSkpDQphYmxpbmUobW9kZWwkY29lZmZpY2llbnRzLCBjb2w9J2JsdWUnLCBsd2Q9MikNCnNlZ21lbnRzKHgsIG1vZGVsJGZpdHRlZC52YWx1ZXMsIHgsIHksIGNvbD0ncmVkJywgbHdkPTIpDQpwb2ludHMoeSB+IHgsIHBjaD0yMSwgYmc9J29yYW5nZScsIGNvbD0nYmxhY2snLCBjZXg9MS41KQ0KDQoNCnBhcihtZnJvdz1jKDEsMykpDQpgYGANCg0KDQoNCiMjIyMgKipQYXJhbWV0ZXIgRXN0aW1hdGVzKioNCg0KVGhyb3VnaCB0aGUgbWFnaWMgb2YgY2FsY3VsdXMsIHdlIGNhbiBzaG93IHRoYXQgZm9yIGEgZ2l2ZW4gdHJhaW5pbmcgc2V0ICRcbGVmdCh4X2ksIHlfaSBccmlnaHQpJCwgdGhlIHF1YW50aXR5ICRTU0VcbGVmdCAoIFxoYXRcYmV0YV8wLCBcaGF0XGJldGFfMSBccmlnaHQpJCBoYXMgYSB1bmlxdWUgbWluaW11bSB3aGljaCBpcyBvYnRhaW5lZCBhcyBmb2xsb3dzOg0KDQpEZWZpbmUgcXVhbnRpdGllcyAkU197WFh9JCBhbmQgJFNfe1hZfSQgYnk6DQoNCiQkU197WFh9ID0gXHN1bVxsaW1pdHNfe2k9MX1ebiBcbGVmdCh4X2kgLSBcYmFyIHggXHJpZ2h0KV4yID0gXHN1bVxsaW1pdHNfe2k9MX1ebiB4X2leMiAtIG5cYmFyIHheMiQkDQoNCiQkU197WFl9ID0gXHN1bVxsaW1pdHNfe2k9MX1ebiBcbGVmdCh4X2kgLSBcYmFyIHggXHJpZ2h0KVxsZWZ0KHlfaSAtIFxiYXIgeSBccmlnaHQpID0gXHN1bVxsaW1pdHNfe2k9MX1ebiB4X2kgeV9pIC0gblxiYXIgeCBcYmFyIHkkJA0KDQpUaGUgcGFyYW1ldGVyIGVzdGltYXRlcyB0aGF0IG1pbmltaXplIFNTRSBvbiB0aGUgdHJhaW5pbmcgc2V0IGFyZSBnaXZlbiBieToNCiANCg0KJCRcaGF0XGJldGFfMT0gXGZyYWN7U197WFl9fXtTX3tYWH19IFxoc3BhY2V7MjBweH0gXG1hdGhybXthbmR9IFxoc3BhY2V7MjBweH1caGF0XGJldGFfMCA9IFxiYXIgeSAtIFxoYXRcYmV0YV8xIFxiYXIgeCQkDQoNClRoZSBkZXJpdmF0aW9uIG9mIHRoZSByZXN1bHRzIGFib3ZlIGFyZSBzaG93biBpbiB0aGUgYWNjb21wYW55aW5nIG5vdGVib29rIHRpdGxlZCAqKjMuMS5hIC0gRGVyaXZhdGlvbiBvZiBQYXJhbWV0ZXIgRXN0aW1hdGVzKiouIA0KDQoNCiMjIyMgKipBbHRlcm5hdGUgZm9ybSBmb3IgJFxoYXRcYmV0YV8xJCoqDQoNClRoZXJlIGFyZSBtYW55IGRpZmZlcmVudCB3YXlzIHRvIHdyaXRlIHRoZSBmb3JtdWxhIGZvciAkXGhhdFxiZXRhXzEkLiBPbmUgY29tbW9ubHkgZW5jb3VudGVyZWQgZm9ybXVsYSBpcyAkXGhhdFxiZXRhXzEgPSBcZnJhY3tcbWF0aHJte2Nvdn1bWCxZXX17c19YXjJ9JC4gVGhpcyBmb3JtdWxhIGNhbiBiZSBkZXJpdmVkIGZyb20gb3VyIHByZXZpb3VzIGZvcm11bGEgZm9yICRcaGF0XGJldGFfMSQgYnkgbXVsdGlwbHlpbmcgdGhlIHRvcCBhbmQgYm90dG9tIG9mIHRoZSBleHByZXNzaW9uIGJ5ICQxLyhuLTEpJC4NCjwvYnI+DQo8L2JyPg0KDQojIyMjICoqUi1TcXVhcmVkKioNCg0KVGhlIHF1YW50aXRpZXMgJFNTRSQgYW5kICRNU0UkIHByb3ZpZGUgbWVhc3VyZXMgZm9yIHRoZSBxdWFsaXR5IG9mIGEgZml0dGVkIG1vZGVsLiBIb3dldmVyLCB0aGVzZSBtZXRyaWNzIGFyZSBtZWFzdXJlZCBpbiB0aGUgc3F1YXJlIG9mIHRoZSB1bml0cyBpbiB3aGljaCAkWSQgaXMgbWVhc3VyZWQuIFRoaXMgbWFrZXMgaXQgZGlmZmljdWx0IHRvIHVzZSB0aGVzZSBxdWFudGllcyB0byBhc3Nlc3MgdGhlIGZpdHRlZCBtb2RlbC4gRm9ydHVuYXRlbHksIHRoZXJlIGlzIGEgdW5pdGxlc3MgbWVhc3VyZSwgY2FsbGVkIHRoZSAqKlItU3F1YXJlZCoqIHN0YXRpc3RpYywgdGhhdCB3ZSBjYW4gdXNlIHRvIHNjb3JlIG91ciBtb2RlbHMuDQoNClRoZSAqKlItU3F1YXJlZCoqIHN0YXRpc3RpYyBpcyBnaXZlbiBieTogDQoNCiQkUl4yID0gMSAtIFxmcmFje1NTRX17U1NUfSQkDQoNCldoZXJlOiANCg0KJCRTU1QgPSBcc3VtXGxpbWl0c197aT0xfV5uIFxsZWZ0KHlfaSAtIFxiYXIgeSBccmlnaHQpXjIgJCQNCg0KTm90ZSB0aGF0IHRoZSBzYW1wbGUgdmFyaWFuY2Ugb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlICRZJCBpcyBnaXZlbiBieSAkc19ZXjIgPSBTU1QgLyAobi0xKSQuIEFzIHN1Y2gsICRTU1QkIGlzLCBpbiBhIHNlbnNlLCBhIG1lYXN1cmUgb2YgdGhlIHZhcmlhYmlsaXR5IG9mIHRoZSByZXNwb25zZSB2YXJpYWJsZS4gDQoNCg0KSXQgY2FuIGJlIHNob3duIHRoYXQgJFJeMiQgaXMgYWx3YXlzIGJldHdlZW4gMCBhbmQgMS4gRnVydGhlcm1vcmUsIHdlIGNhbiBzaG93IHRoYXQgJFJeMiQgbWVhc3VyZXMgdGhlIHByb3BvcnRpb24gb2YgdmFyaWFiaWxpdHkgaW4gdGhlIHJlc3BvbnNlIHZhcmlhYmxlICRZJCB0aGF0IHRoZSBmaXR0ZWQgbW9kZWwgaXMgYWJsZSB0byBleHBsYWluIHVzaW5nIHRoZSBwcmVkaWN0b3IgJFgkLiAgDQoNCg0KDQojIyMgKipFeGFtcGxlOiBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24gaW4gUioqDQoNCkluIHRoZSBmb2xsb3dpbmcgZXhhbXBsZSwgd2Ugd2lsbCBiZSBpbnRlcmVzdGVkIGluIHN0dWR5aW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcHJpY2UgYW5kIG1pbGVhZ2Ugb2YgdXNlZCBjYXJzLiANCg0KIyMjIyAqKkdlbmVyYXRlIERhdGEgYW5kIENhbGN1bGF0ZSBEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzKioNCg0KDQpBc3N1bWUgdGhhdCB3ZSBnYXRoZXIgYSBzYW1wbGUgb2YgMjAgcmVjZW50bHkgc29sZCB1c2VkIDIwMTYgRm9yZCBGaWN0dXMgYXV0b21vYmlsZXMuIEZvciBlYWNoIHZlaGljbGUsIHdlIHJlY29yZCB0aGUgc2FsZXMgcHJpY2UgKGluIHRob3VzYW5kcyBvZiBkb2xsYXJzKSBhbmQgbWlsZWFnZSAoaW4gdGhvdXNhbmRzIG9mIG1pbGVzKSBvZiB0aGUgdmVoaWNsZS4gSW4gdGhlIGNvZGUgY2h1bmsgYmVsb3csIFdlIGNyZWF0ZSB2ZWN0b3JzIGBtaWxlYWdlYCBhbmQgYHByaWNlYCB0byBzdG9yZSB0aGVzZSB2YWx1ZXMuIA0KDQoNCmBgYHtyfQ0KbWlsZWFnZSA8LSBjKCAzLjEsICA0LjEsICA1LjMsICA3LjEsIDE5LjUsIA0KICAgICAgICAgICAgIDI4LjMsIDM2LjgsIDM3LjIsIDQyLjMsIDUyLjMsIA0KICAgICAgICAgICAgIDUzLjMsIDUzLjQsIDYzLjIsIDY4LjQsIDgyLjMsIA0KICAgICAgICAgICAgIDgzLjksIDg4LjQsIDk3LjYsIDk5LjcsIDEwNS45KQ0KDQpwcmljZSA8LSBjKDUzLjcsIDU2LjgsIDU4LjUsIDQyLjAsIDQ4LjksIA0KICAgICAgICAgICAzMy4yLCAyMi4yLCAzMi42LCAzMC4zLCAxOS44LCANCiAgICAgICAgICAgMjYuMSwgMjQuOSwgMTguMSwgMTEuNywgMTMuMywgDQogICAgICAgICAgIDIzLjQsIDEzLjIsIDEzLjYsIDE0LjgsIDQuNikNCg0KYGBgDQoNCiMjIyMgKipEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzKioNCg0KQmVmb3JlIGNvbnN0cnVjdGluZyBvdXIgbW9kZWwsIGxldCdzIGNhbGN1bGF0ZSB0aGUgc2FtcGxlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBmb3IgYm90aCB0aGUgbWlsZWFnZSBhbmQgcHJpY2Ugb2YgdGhlIGF1dG9tb2JpbGVzIGluIHRoZSBzYW1wbGUuIA0KDQpgYGB7cn0NCnhiYXIgPC0gbWVhbihtaWxlYWdlKQ0Kc3ggPC0gc2QobWlsZWFnZSkNCg0KeF9zdGF0cyA8LSBjKHhiYXIsIHN4KQ0KbmFtZXMoeF9zdGF0cykgPC0gYygnbWVhbicsICdzdGRldicpDQoNCnliYXIgPC0gbWVhbihwcmljZSkNCnN5IDwtIHNkKHByaWNlKQ0KDQp5X3N0YXRzIDwtIGMoeWJhciwgc3kpDQpuYW1lcyh5X3N0YXRzKSA8LSBjKCdtZWFuJywgJ3N0ZGV2JykNCg0KcmJpbmQoeF9zdGF0cywgeV9zdGF0cykNCmBgYA0KDQpOb3RpY2UgdGhhdCB0aGUgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgcHJpY2UgaXMgdmVyeSBsYXJnZSB3aXRoIHJlc3BlY3QgdG8gaXRzIG1lYW4uIFRoaXMgaW5kaWNhdGVzIHRoYXQgaWYgd2Ugd2VyZSB0byB1c2Ugb25seSB0aGUgc2FtcGxlIG1lYW4gcHJpY2UgYXMgYW4gZXN0aW1hdGUgZm9yIHRoZSBwcmljZSBvZiBvbmUgb2YgdGhlc2UgYXV0b21vYmlsZXMsIHRoZXJlIHdvdWxkIGJlIGEgbG90IG9mIHVuY2VydGFpbnR5IGluIG91ciBlc3RpbWF0ZS4gDQoNCg0KIyMjIyAqKlJlbGF0aW9uc2hpcCBiZXR3ZWVuIFByaWNlIGFuZCBNaWxlYWdlKioNCg0KVG8gc3R1ZHkgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHByaWNlIGFuZCBtaWxlYWdlIG9mIHRoaXMgYXV0b21vYmlsZSBtb2RlbCwgd2UgbWlnaHQgY3JlYXRlIGEgc2NhdHRlcnBsb3QgZnJvbSB0aGUgcGFpcmVkIG9ic2VydmF0aW9ucyBpbiBvdXIgc2FtcGxlLg0KDQpgYGB7cn0NCnBsb3QocHJpY2UgfiBtaWxlYWdlLCBwY2g9MjEsIGJnPSdvcmFuZ2UnLCBjb2w9J2JsYWNrJywgY2V4PTEuNSwNCiAgICAgeGxhYiA9ICdNaWxlYWdlIChpbiAxMDAwcyBvZiBNaWxlcyknLCB5bGFiID0gJ1ByaWNlIChpbiAxMDAwcyBvZiBEb2xsYXJzKScsIA0KICAgICBtYWluID0gJ1JlbGF0aW9uc2hpcCBiZXR3ZWVuIE1pbGVhZ2UgYW5kIFByaWNlJykNCmBgYA0KDQpXZSBzZWUgZnJvbSB0aGlzIHBsb3QgdGhhdCB0aGUgY2FycyB3aXRoIGdyZWF0ZXIgbWlsZWFnZSB0ZW5kIHRvIGhhdmUgYSBsb3dlciBzYWxlcyBwcmljZSAoYXMgeW91IG1pZ2h0IGV4cGVjdCkuIEluIGZhY3QsIGl0IGFwcGVhcnMgdGhhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHByaWNlIGFuZCBtaWxlYWdlIG9mIHRoZSB2ZWhpY2xlcyBtaWdodCBiZSByb3VnaGx5IGxpbmVhci4gDQoNCiMjIyMgKipDcmVhdGluZyB0aGUgRml0dGVkIE1vZGVsKioNCg0KV2Ugd2lsbCB1c2UgdGhlIGBsbWAgZnVuY3Rpb24gaW4gUiB0byBmaW5kIGEgbGluZWFyIG1vZGVsIHRoYXQgYXR0ZW1wdHMgdG8gY2FwdHVyZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcHJpY2UgYW5kIG1pbGVhZ2UuIFdlIHdpbGwgc3RvcmUgdGhlIHJlc3VsdGluZyBtb2RlbCBpbiBhIHZhcmlhYmxlIGNhbGxlZCBgbW9kZWxgIGFuZCB3aWxsIHRoZW4gdXNlIHRoZSBgc3VtbWFyeWAgZnVuY3Rpb24gdG8gZ2V0IHNvbWUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIG1vZGVsLiANCg0KDQpgYGB7cn0NCm1vZGVsIDwtIGxtKHByaWNlIH4gbWlsZWFnZSkNCg0Kc3VtbWFyeShtb2RlbCkNCmBgYA0KDQpUaGVyZSBpcyBhIGxvdCBvZiBpbmZvcm1hdGlvbiBpbiB0aGlzIHN1bW1hcnkuIFdlIHdpbGwgZXZlbnR1YWxseSBsZWFybiBob3cgdG8gaW50ZXJwcmV0IGFsbCBvZiB0aGUgaW5mb3JtYXRpb24gcHJlc2VudGVkIGhlcmUuIEZvciBub3csIGxldHMgZm9jdXMgb24gb25lIHBpZWNlIG9mIGluZm9ybWF0aW9uOiB0aGUgY29lZmZpY2llbnQgZXN0aW1hdGVzLiANCg0KVGhlIGNvZWZmaWNpZW50IGVzdGltYXRlcyBhcmUgc2hvd24gaW4gdGhlIHN1bW1hcnkgYWJvdmUsIGJ1dCB3ZSBjYW4gYWNjZXNzIHRoZW0gZGlyZWN0bHkgYXMgZm9sbG93czoNCg0KYGBge3J9DQptb2RlbCRjb2VmZmljaWVudHMNCmBgYA0KDQpUaGVzZSBhcmUgdGhlIGNvZWZmaWNpZW50cyB0aGF0IGRldGVybWluZSB0aGUgc2xvcGUgYW5kIGludGVyY2VwdCBvZiBvdXIgbGluZWFyIG1vZGVsLiBUaGlzIHRlbGxzIHVzIHRoYXQgb3VyIG1vZGVsIGhhcyB0aGUgZm9sbG93aW5nIGZvcm06DQoNCjxjZW50ZXI+DQoqKlByZWRpY3RlZCBQcmljZSA9IDUwLjU1IC0gMC40MzUzIMK3IE1pbGVhZ2UqKg0KPC9jZW50ZXI+DQoNCjxicj4NClRoaXMgcmVsYXRpb25zaGlwIGJldHdlZW4gcHJpY2UgYW5kIG1pbGVhZ2UgY2FuIGFsc28gYmUgcmVwcmVzZW50ZWQgYXMgZm9sbG93czoNCg0KPGNlbnRlcj4NCioqQWN0dWFsIFByaWNlID0gNTAuNTUgLSAwLjQzNTMgwrcgTWlsZWFnZSArIFVuZXhwbGFpbmVkIEVycm9yKioNCjwvY2VudGVyPg0KDQojIyMjICoqQXNzZXNzaW5nIHRoZSBRdWFsaXR5IG9mIHRoZSBGaXQqKg0KDQpUaGUgc3VtbWFyeSBvZiB0aGUgbW9kZWwgYWxzbyByZXBvcnRzIHRoZSB2YWx1ZSBvZiAkUl4yJCB1bmRlciB0aGUgbGlzdGluZyAiTXVsdGlwbGUgUi1zcXVhcmVkIi4gSXQgY2FuIGFsc28gYmUgYWNjZXNzZWQgZGlyZWN0bHkgYXMgZm9sbG93czoNCg0KYGBge3J9DQogc3VtbWFyeShtb2RlbCkkci5zcXVhcmVkDQpgYGANCg0KVGhpcyBtZWFucyB0aGF0IG91ciBtb2RlbCBpcyBhYmxlIHRvIGV4cGxhaW4gcm91Z2hseSA4My43MiUgb2YgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSBwcmljZSBvZiBhIHVzZWQgMjAxNiBGb3JkIEZpY3R1cyBpbiB0ZXJtcyBvZiB0aGUgbWlsZWFnZSBmb3IgdGhlIHZlaGljbGUuIA0KDQoNCiMjIyMgKipQbG90dGluZyB0aGUgRml0dGVkIE1vZGVsKioNCg0KTGV0J3MgYWRkIG91ciByZWdyZXNzaW9uIGxpbmUgdG8gdGhlIHNjYXR0ZXIgcGxvdCBvZiBwcmljZSBhbmQgbWlsZWFnZS4gDQoNCg0KYGBge3J9DQpwbG90KHByaWNlIH4gbWlsZWFnZSwgcGNoPTIxLCBiZz0nb3JhbmdlJywgY29sPSdibGFjaycsIGNleD0xLjUsDQogICAgIHhsYWIgPSAnTWlsZWFnZSAoaW4gMTAwMHMgb2YgTWlsZXMpJywgeWxhYiA9ICdQcmljZSAoaW4gMTAwMHMgb2YgRG9sbGFycyknLCANCiAgICAgbWFpbiA9ICdSZWxhdGlvbnNoaXAgYmV0d2VlbiBNaWxlYWdlIGFuZCBQcmljZScpDQphYmxpbmUobW9kZWwkY29lZmZpY2llbnRzLCBjb2w9J2NhZGV0Ymx1ZScsIGx3ZD0yKQ0KYGBgDQoNCiMjIyMgKipGaXR0ZWQgVmFsdWVzKioNCg0KVGhlIGZpdHRlZCB2YWx1ZSBmb3IgYW55IHBhcnRpY2xhciBvYnNlcnZhdGlvbiBpbiBvdXIgc2FtcGxlIGlzIHRoZSBwcmljZSB0aGF0IHRoZSBtb2RlbCBwcmVkaWN0cyBmb3IgdGhlIGNhciwgZ2l2ZW4gdGhlIG1pbGVhZ2Ugb2YgdGhhdCBjYXIuIFRoaXMgaXMgb2J0YWluZWQgYnkgcGx1Z2dpbmcgdGhlIG1pbGVhZ2UgaW50byB0aGUgZXF1YXRpb246DQo8Y2VudGVyPg0KKipQcmVkaWN0ZWQgUHJpY2UgPSA1MC41NSAtIDAuNDM1MyDCtyBNaWxlYWdlKioNCjwvY2VudGVyPg0KPGJyPg0KVGhlIGZpdHRlZCB2YWx1ZXMgZm9yIHRoZSBjYXJzIGluIG91ciBzYW1wbGUgYXJlIHN0b3JlZCB3aXRoaW4gdGhlIGBtb2RlbGAgdmFyaWFibGUNCg0KYGBge3J9DQpyb3VuZChtb2RlbCRmaXR0ZWQudmFsdWVzLDEpDQpgYGANCg0KVG8gaWxsdXN0cmF0ZSB0aGlzIGlkZWEsIHdlIHdpbGwgYWRkIHRoZSBmaXR0ZWQgdmFsdWVzIHRvIG91ciBzY2F0dGVycGxvdC4gDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KcGxvdChwcmljZSB+IG1pbGVhZ2UsIHBjaD0yMSwgYmc9J29yYW5nZScsIGNvbD0nYmxhY2snLCBjZXg9MS41LA0KICAgICB4bGFiID0gJ01pbGVhZ2UgKGluIDEwMDBzIG9mIE1pbGVzKScsIHlsYWIgPSAnUHJpY2UgKGluIDEwMDBzIG9mIERvbGxhcnMpJywgDQogICAgIG1haW4gPSAnUmVsYXRpb25zaGlwIGJldHdlZW4gTWlsZWFnZSBhbmQgUHJpY2UnKQ0KDQphYmxpbmUobW9kZWwkY29lZmZpY2llbnRzLCBjb2w9J2NhZGV0Ymx1ZScsIGx3ZD0yKQ0KDQpwb2ludHMobWlsZWFnZSwgbW9kZWwkZml0dGVkLnZhbHVlcywgcGNoPTE4LCBjZXg9MS41LCBjb2w9J2RhcmtncmVlbicpDQpwb2ludHMocHJpY2UgfiBtaWxlYWdlLCBwY2g9MjEsIGJnPSdvcmFuZ2UnLCBjb2w9J2JsYWNrJywgY2V4PTEuNSkNCg0KDQpgYGANCg0KDQojIyMjICoqUmVzaWR1YWxzKioNCg0KVGhlIHJlc2lkdWFscyBhcmUgdGhlIGVycm9yIGluIHRoZSBwcmVkaWN0ZWQgcHJpY2VzLiBUaGUgcmVzaWR1YWwgZm9yIGEgcGFydGljdWxhciBvYnNlcnZhdGlvbiBpcyBnaXZlbiBieSB0aGUgZXF1YXRpb246DQo8Y2VudGVyPg0KKipSZXNpZGFsID0gQWN0dWFsIFByaWNlIC0gUHJlZGljdGVkIFByaWNlKioNCjwvY2VudGVyPg0KPGJyPg0KDQpSZXNpZHVhbHMgcmVmbGVjdCB0aGUgdW5jZXJ0YWludHkgcmVtYWluaW5nIGluIG91ciBtb2RlbC4gDQoNCg0KYGBge3IsIGVjaG89RkFMU0V9DQpwbG90KHByaWNlIH4gbWlsZWFnZSwgcGNoPTIxLCBiZz0nb3JhbmdlJywgY29sPSdibGFjaycsIGNleD0xLjUsDQogICAgIHhsYWIgPSAnTWlsZWFnZSAoaW4gMTAwMHMgb2YgTWlsZXMpJywgeWxhYiA9ICdQcmljZSAoaW4gMTAwMHMgb2YgRG9sbGFycyknLCANCiAgICAgbWFpbiA9ICdSZWxhdGlvbnNoaXAgYmV0d2VlbiBNaWxlYWdlIGFuZCBQcmljZScpDQoNCnNlZ21lbnRzKG1pbGVhZ2UsIG1vZGVsJGZpdHRlZC52YWx1ZXMsIG1pbGVhZ2UsIHByaWNlLCBjb2w9J3JlZCcsIGx3ZD0yKQ0KcG9pbnRzKHByaWNlIH4gbWlsZWFnZSwgcGNoPTIxLCBiZz0nb3JhbmdlJywgY29sPSdibGFjaycsIGNleD0xLjUpDQoNCmFibGluZShtb2RlbCRjb2VmZmljaWVudHMsIGNvbD0nY2FkZXRibHVlJywgbHdkPTIpDQpgYGANCg0KT3VyIG1vZGVsIG9iamVjdCBgbW9kZWxgIGNvbnRhaW5zIHRoZSByZXNpZHVhbHMgZm9yIHRoZSBvYnNlcnZhdGlvbnMgaW4gb3VyIHNhbXBsZS4gDQoNCg0KYGBge3J9DQpyZXMgPC0gbW9kZWwkcmVzaWR1YWxzDQoNCnJvdW5kKHJlcywxKQ0KYGBgDQoNClNpbmNlIHRoZSByZXNpZHVhbHMgcmVwcmVzZW50IHRoZSB1bmNlcnRhaW5seSBpbiBvdXIgcHJlZGljdGlvbnMsIHdlIHdvdWxkIGxpa2UgdG8gZ2V0IGEgc2Vuc2UgYXMgdG8gaG93IHRoZXkgYXJlIGRpc3RyaWJ1dGVkLiBUbyB0aGF0IGVuZCwgd2UgZ2VuZXJhdGUgYSBoaXN0b2dyYW0gb2YgdGhlIHJlc2lkdWFscy4NCg0KDQpgYGB7cn0NCmhpc3QocmVzLCBjb2w9J29yY2hpZCcpDQpgYGANCg0KDQpJdCBzZWVtcyByZWFzb25hYmxlIHRvIGFzc3VtZSB0aGF0IHRoZSByZXNpZHVhbHMgbWlnaHQgYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgd2l0aCBhIG1lYW4gb2YgemVyby4gTGV0J3MgY2FsY3VsYXRlIHRoZWlyIHNhbXBsZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24uDQoNCg0KYGBge3J9DQpyZXNfbWVhbiA8LSBtZWFuKHJlcykNCnJlc19zZCA8LSBzZChyZXMpDQoNCnJlc19zdGF0cyA8LSBjKHJlc19tZWFuLCByZXNfc2QpDQpuYW1lcyhyZXNfc3RhdHMpIDwtIGMoJ21lYW4nLCAnc3RkZXYnKQ0KDQpyb3VuZChyZXNfc3RhdHMsNCkNCmBgYA0KDQojIyMjICoqVXNpbmcgdGhlIE1vZGVsIHRvIE1ha2UgUHJlZGljdGlvbnMqKg0KDQpBc3N1bWUgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBwdXJjaGFzaW5nIGEgdXNlZCAyMDE2IEZvcmQgRmljdHVzIHdpdGggNDUsMDAwIG1pbGVzLiBXZSB3b3VsZCBsaWtlIHRvIHVzZSBvdXIgbW9kZWwgdG8gZGV0ZXJtaW5lIGEgZmFpciBwcmljZSBmb3IgdGhlIGNhci4gV2UgY291bGQgY2FsdWxjYXRlIHRoaXMgYnkgcGx1Z2dpbmcgNDUgaW50byB0aGUgZXF1YXRpb24gZm9yIG91ciBtb2RlbC5UaGF0IGdpdmVzOg0KDQo8Y2VudGVyPg0KKipQcmVkaWN0ZWQgUHJpY2UgPSA1MC41NSAtIDAuNDM1MyDCtyA0NSA9IDMwLjk2KioNCjwvY2VudGVyPg0KPGJyPg0KSW4gb3RoZXIgd29yZHMsIG91ciBtb2RlbCBwcmVkaWN0cyB0aGF0IHN1Y2ggYSB2ZWhpY2xlIHNob3VsZCBjb3N0IChvbiBhdmVyYWdlKSAkMzAsOTYwLiANCg0KV2UgY2FuIHVzZSB0aGUgUiBmdW5jdGlvbiBgcHJlZGljdGAgdG8gY2FsY3VsYXRlIHRoaXMgcHJlZGljdGVkIHZhbHVlLiANCg0KYGBge3J9DQpuZXdkYXRhID0gZGF0YS5mcmFtZShtaWxlYWdlPWMoNDUpKQ0KcHJlZGljdChtb2RlbCwgbmV3ZGF0YSkNCmBgYA0KDQpXZSBrbm93IHRoYXQgdGhlIHByZWRpY3Rpb25zIG1hZGUgYnkgdGhlIG1vZGVsIGFyZSBub3QgMTAwJSBhY2N1cmF0ZS4gV2UgZXhwZWN0IHRoZXJlIHRvIGJlIHNvbWUgZXJyb3IgaW4gdGhlIHByZWRpY3Rpb25zLiBUbyBiZXR0ZXIgdW5kZXJzdGFuZCBob3cgbXVjaCB0aGUgYWN0dWFsIHByaWNlIG9mIGEgY2FyIHdpdGggNDUsMDAwIG1pbGVzIG1pZ2h0IHZhcnksIHdlIHdpbGwgdXNlIGBwcmVkaWN0YCB0byBjcmVhdGUgYW4gaW50ZXJ2YWwgdGhhdCB3ZSBhcmUgOTUlIGNlcnRhaW4gY29udGFpbnMgdGhlIHRydWUgcHJpY2Ugb2YgdGhlIGNhci4gVGhpcyBpbnRlcnZhbCBpcyBjYWxsZWQgYSAqKnByZWRpY3Rpb24gaW50ZXJ2YWwqKi4gDQoNCmBgYHtyfQ0KcHJlZGljdChtb2RlbCwgbmV3ZGF0YSwgaW50ZXJ2YWwgPSAncHJlZGljdGlvbicsIGxldmVsPTAuOTUpDQpgYGANCg0KV2UgY2FuIHVzZSB0aGUgcHJlZGljdCBmdW5jdGlvbiB0byBnZW5lcmF0ZSBwcmVkaWN0aW9ucyBmb3Igc2V2ZXJhbCBuZXcgb2JzZXJ2YXRpb25zIGF0IG9uY2UuIExldCdzIHNheSB0aGF0IHdlIHdvdWxkIGxpa2UgdG8gY29udHJ1Y3QgOTUlIHByZWRpY3Rpb24gaW50ZXJ2YWxzIGZvciB0aGUgcHJpY2VzIG9mIDMgdmVoaWNsZXM6IG9uZSB3aXRoIDQwLDAwMCBtaWxlcywgb25lIHdpdGggNTAsMDAwIG1pbGVzLCBhbmQgb25lIHdpdGggNzAsMDAwIG1pbGVzLiBXZSBjYW4gZG8gc28gYXMgZm9sbG93czoNCg0KYGBge3J9DQpuZXdkYXRhID0gZGF0YS5mcmFtZShtaWxlYWdlPWMoNDAsIDUwLCA3MCkpDQpwcmVkaWN0KG1vZGVsLCBuZXdkYXRhLCBpbnRlcnZhbD0ncHJlZGljdGlvbicsIGxldmVsPTAuOTUpDQpgYGANCg0KDQo=