Introduction to Regression Analysis

In this lecture, we are going to provide a general overview of regression analysis by walking through a typical regression problem. In the example we will be working through, we will be interested in studying the relationship between the price and mileage of used cars.

Assume that we gather a sample of 20 recently sold used 2016 Ford Fictus automobiles. For each vehicle, we record the sales price and mileage of the vehicle.

We will store the prices in an R vector called price. The prices are stated in thousands of dollars.

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)
hist(price, col='salmon')

Descriptive Statistics

Before looking at the mileages of the cars in the sample, let’s study the prices a bit more. We will begin by calculating the mean and standard deviation of the price of the cars in this sample.

xbar <- mean(price)
s <- sd(price)
stats <- c(xbar, s)
names(stats) <- c('mean', 'stdev')
stats
    mean    stdev 
28.08500 16.17273 

Constructing a (Naive) Prediction Interval

Assume that we wish to construct an interval that we we believe will contain the prices of 95% of all used 2016 Fictuses. Knowing that 95% of all observations of a normally distributed variable fall within 1.96 standard deviations of the mean, we might (naively) construct our interval by adding and subtracting 1.96 times the sample standard deviation to and from the sample mean.

lower <- xbar - 1.96*s
upper <- xbar + 1.96*s
interval <- c(lower, upper)
names(interval) <- c('lower', 'upper')
interval
    lower     upper 
-3.613547 59.783547 

Notice that this interval is fairly large. If we were trying to predict the price of a particular 2016 Fictus without any additional information about the car, we would not be able to provide a very precise estimate. In other words, we see that there is a lot of variability in the sales prices of this model of vehicle.

If we had some additional information about the car whose price we were trying to predict, then perhaps we could offer a better estimate.

Relationship between Price and Mileage

Assume that in addition to recording the prices of the cars in our sample, we also recorded the mileages (in thousands of miles). We will store these mileages in a vector called mileage.

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)
hist(mileage, col='cornflowerblue')

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.

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


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   16   17   18   19   20 
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 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    13    14    15    16    17 
  4.5   8.0  10.3  -5.5   6.8  -5.0 -12.3  -1.8  -1.8  -8.0  -1.2  -2.4  -4.9  -9.1  -1.4   9.4   1.1 
   18    19    20 
  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
LS0tDQp0aXRsZTogIkxlc3NvbiAwMSAtIEludHJvZHVjdGlvbiB0byBSZWdyZXNzaW9uIEFuYWx5c2lzIg0KYXV0aG9yOiAiUm9iYmllIEJlYW5lIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDINCi0tLQ0KDQoNCiMjIEludHJvZHVjdGlvbiB0byBSZWdyZXNzaW9uIEFuYWx5c2lzDQoNCkluIHRoaXMgbGVjdHVyZSwgd2UgYXJlIGdvaW5nIHRvIHByb3ZpZGUgYSBnZW5lcmFsIG92ZXJ2aWV3IG9mIHJlZ3Jlc3Npb24gYW5hbHlzaXMgYnkgd2Fsa2luZyB0aHJvdWdoIGEgdHlwaWNhbCByZWdyZXNzaW9uIHByb2JsZW0uIEluIHRoZSBleGFtcGxlIHdlIHdpbGwgYmUgd29ya2luZyB0aHJvdWdoLCB3ZSB3aWxsIGJlIGludGVyZXN0ZWQgaW4gc3R1ZHlpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBwcmljZSBhbmQgbWlsZWFnZSBvZiB1c2VkIGNhcnMuIA0KDQpBc3N1bWUgdGhhdCB3ZSBnYXRoZXIgYSBzYW1wbGUgb2YgMjAgcmVjZW50bHkgc29sZCB1c2VkIDIwMTYgRm9yZCBGaWN0dXMgYXV0b21vYmlsZXMuIEZvciBlYWNoIHZlaGljbGUsIHdlIHJlY29yZCB0aGUgc2FsZXMgcHJpY2UgYW5kIG1pbGVhZ2Ugb2YgdGhlIHZlaGljbGUuIA0KDQpXZSB3aWxsIHN0b3JlIHRoZSBwcmljZXMgaW4gYW4gUiB2ZWN0b3IgY2FsbGVkIGBwcmljZWAuIFRoZSBwcmljZXMgYXJlIHN0YXRlZCBpbiB0aG91c2FuZHMgb2YgZG9sbGFycy4gDQoNCg0KYGBge3J9DQpwcmljZSA8LSBjKDUzLjcsIDU2LjgsIDU4LjUsIDQyLjAsIDQ4LjksIDMzLjIsIDIyLjIsIDMyLjYsIDMwLjMsIDE5LjgsIA0KICAgICAgICAgICAyNi4xLCAyNC45LCAxOC4xLCAxMS43LCAxMy4zLCAyMy40LCAxMy4yLCAxMy42LCAxNC44LCA0LjYpDQoNCmhpc3QocHJpY2UsIGNvbD0nc2FsbW9uJykNCmBgYA0KDQojIERlc2NyaXB0aXZlIFN0YXRpc3RpY3MNCg0KQmVmb3JlIGxvb2tpbmcgYXQgdGhlIG1pbGVhZ2VzIG9mIHRoZSBjYXJzIGluIHRoZSBzYW1wbGUsIGxldCdzIHN0dWR5IHRoZSBwcmljZXMgYSBiaXQgbW9yZS4gV2Ugd2lsbCBiZWdpbiBieSBjYWxjdWxhdGluZyB0aGUgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBwcmljZSBvZiB0aGUgY2FycyBpbiB0aGlzIHNhbXBsZS4gDQoNCg0KYGBge3J9DQp4YmFyIDwtIG1lYW4ocHJpY2UpDQpzIDwtIHNkKHByaWNlKQ0KDQpzdGF0cyA8LSBjKHhiYXIsIHMpDQpuYW1lcyhzdGF0cykgPC0gYygnbWVhbicsICdzdGRldicpDQoNCnN0YXRzDQpgYGANCg0KIyBDb25zdHJ1Y3RpbmcgYSAoTmFpdmUpIFByZWRpY3Rpb24gSW50ZXJ2YWwNCg0KQXNzdW1lIHRoYXQgd2Ugd2lzaCB0byBjb25zdHJ1Y3QgYW4gaW50ZXJ2YWwgdGhhdCB3ZSB3ZSBiZWxpZXZlIHdpbGwgY29udGFpbiB0aGUgcHJpY2VzIG9mIDk1JSBvZiBhbGwgdXNlZCAyMDE2IEZpY3R1c2VzLiBLbm93aW5nIHRoYXQgOTUlIG9mIGFsbCBvYnNlcnZhdGlvbnMgb2YgYSBub3JtYWxseSBkaXN0cmlidXRlZCB2YXJpYWJsZSBmYWxsIHdpdGhpbiAxLjk2IHN0YW5kYXJkIGRldmlhdGlvbnMgb2YgdGhlIG1lYW4sIHdlIG1pZ2h0IChuYWl2ZWx5KSBjb25zdHJ1Y3Qgb3VyIGludGVydmFsIGJ5IGFkZGluZyBhbmQgc3VidHJhY3RpbmcgMS45NiB0aW1lcyB0aGUgc2FtcGxlIHN0YW5kYXJkIGRldmlhdGlvbiB0byBhbmQgZnJvbSB0aGUgc2FtcGxlIG1lYW4uIA0KDQoNCmBgYHtyfQ0KbG93ZXIgPC0geGJhciAtIDEuOTYqcw0KdXBwZXIgPC0geGJhciArIDEuOTYqcw0KDQppbnRlcnZhbCA8LSBjKGxvd2VyLCB1cHBlcikNCm5hbWVzKGludGVydmFsKSA8LSBjKCdsb3dlcicsICd1cHBlcicpDQoNCmludGVydmFsDQpgYGANCg0KTm90aWNlIHRoYXQgdGhpcyBpbnRlcnZhbCBpcyBmYWlybHkgbGFyZ2UuIElmIHdlIHdlcmUgdHJ5aW5nIHRvIHByZWRpY3QgdGhlIHByaWNlIG9mIGEgcGFydGljdWxhciAyMDE2IEZpY3R1cyB3aXRob3V0IGFueSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjYXIsIHdlIHdvdWxkIG5vdCBiZSBhYmxlIHRvIHByb3ZpZGUgYSB2ZXJ5IHByZWNpc2UgZXN0aW1hdGUuIEluIG90aGVyIHdvcmRzLCB3ZSBzZWUgdGhhdCB0aGVyZSBpcyBhIGxvdCBvZiB2YXJpYWJpbGl0eSBpbiB0aGUgc2FsZXMgcHJpY2VzIG9mIHRoaXMgbW9kZWwgb2YgdmVoaWNsZS4gDQoNCklmIHdlIGhhZCBzb21lIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGNhciB3aG9zZSBwcmljZSB3ZSB3ZXJlIHRyeWluZyB0byBwcmVkaWN0LCB0aGVuIHBlcmhhcHMgd2UgY291bGQgb2ZmZXIgYSBiZXR0ZXIgZXN0aW1hdGUuIA0KDQoNCiMgUmVsYXRpb25zaGlwIGJldHdlZW4gUHJpY2UgYW5kIE1pbGVhZ2UNCg0KQXNzdW1lIHRoYXQgaW4gYWRkaXRpb24gdG8gcmVjb3JkaW5nIHRoZSBwcmljZXMgb2YgdGhlIGNhcnMgaW4gb3VyIHNhbXBsZSwgd2UgYWxzbyByZWNvcmRlZCB0aGUgbWlsZWFnZXMgKGluIHRob3VzYW5kcyBvZiBtaWxlcykuIFdlIHdpbGwgc3RvcmUgdGhlc2UgbWlsZWFnZXMgaW4gYSB2ZWN0b3IgY2FsbGVkIGBtaWxlYWdlYC4gDQoNCmBgYHtyfQ0KbWlsZWFnZSA8LSBjKCAzLjEsICA0LjEsICA1LjMsICA3LjEsIDE5LjUsIDI4LjMsIDM2LjgsIDM3LjIsIDQyLjMsICA1Mi4zLCANCiAgICAgICAgICAgICA1My4zLCA1My40LCA2My4yLCA2OC40LCA4Mi4zLCA4My45LCA4OC40LCA5Ny42LCA5OS43LCAxMDUuOSkNCg0KaGlzdChtaWxlYWdlLCBjb2w9J2Nvcm5mbG93ZXJibHVlJykNCmBgYA0KDQpUbyBzdHVkeSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcHJpY2UgYW5kIG1pbGVhZ2Ugb2YgdGhpcyBhdXRvbW9iaWxlIG1vZGVsLCB3ZSBtaWdodCBjcmVhdGUgYSBzY2F0dGVycGxvdCBmcm9tIHRoZSBwYWlyZWQgb2JzZXJ2YXRpb25zIGluIG91ciBzYW1wbGUuDQoNCmBgYHtyfQ0KcGxvdChwcmljZSB+IG1pbGVhZ2UsIHBjaD0yMSwgYmc9J29yYW5nZScsIGNvbD0nYmxhY2snLCBjZXg9MS41LA0KICAgICB4bGFiID0gJ01pbGVhZ2UgKGluIDEwMDBzIG9mIE1pbGVzKScsIHlsYWIgPSAnUHJpY2UgKGluIDEwMDBzIG9mIERvbGxhcnMpJywgDQogICAgIG1haW4gPSAnUmVsYXRpb25zaGlwIGJldHdlZW4gTWlsZWFnZSBhbmQgUHJpY2UnKQ0KYGBgDQoNCg0KV2Ugc2VlIGZyb20gdGhpcyBwbG90IHRoYXQgdGhlIGNhcnMgd2l0aCBncmVhdGVyIG1pbGVhZ2UgdGVuZCB0byBoYXZlIGEgbG93ZXIgc2FsZXMgcHJpY2UgKGFzIHlvdSBtaWdodCBleHBlY3QpLiBJbiBmYWN0LCBpdCBhcHBlYXJzIHRoYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBwcmljZSBhbmQgbWlsZWFnZSBvZiB0aGUgdmVoaWNsZXMgbWlnaHQgYmUgcm91Z2hseSBsaW5lYXIuIA0KDQpXZSB3aWxsIHVzZSB0aGUgYGxtYCBmdW5jdGlvbiBpbiBSIHRvIGZpbmQgYSBsaW5lYXIgbW9kZWwgdGhhdCBhdHRlbXB0cyB0byBjYXB0dXJlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBwcmljZSBhbmQgbWlsZWFnZS4gV2Ugd2lsbCBzdG9yZSB0aGUgcmVzdWx0aW5nIG1vZGVsIGluIGEgdmFyaWFibGUgY2FsbGVkIGBtb2RlbGAgYW5kIHdpbGwgdGhlbiB1c2UgdGhlIGBzdW1tYXJ5YCBmdW5jdGlvbiB0byBnZXQgc29tZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbW9kZWwuIA0KDQoNCmBgYHtyfQ0KbW9kZWwgPC0gbG0ocHJpY2UgfiBtaWxlYWdlKQ0KDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQoNClRoZXJlIGlzIGEgbG90IG9mIGluZm9ybWF0aW9uIGluIHRoaXMgc3VtbWFyeS4gV2Ugd2lsbCBldmVudHVhbGx5IGxlYXJuIGhvdyB0byBpbnRlcnByZXQgYWxsIG9mIHRoZSBpbmZvcm1hdGlvbiBwcmVzZW50ZWQgaGVyZS4gRm9yIG5vdywgbGV0cyBmb2N1cyBvbiBvbmUgcGllY2Ugb2YgaW5mb3JtYXRpb246IHRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZXMuIA0KDQpUaGUgY29lZmZpY2llbnQgZXN0aW1hdGVzIGFyZSBzaG93biBpbiB0aGUgc3VtbWFyeSBhYm92ZSwgYnV0IHdlIGNhbiBhY2Nlc3MgdGhlbSBkaXJlY3RseSBhcyBmb2xsb3dzOg0KDQpgYGB7cn0NCm1vZGVsJGNvZWZmaWNpZW50cw0KYGBgDQoNClRoZXNlIGFyZSB0aGUgY29lZmZpY2llbnRzIHRoYXQgZGV0ZXJtaW5lIHRoZSBzbG9wZSBhbmQgaW50ZXJjZXB0IG9mIG91ciBsaW5lYXIgbW9kZWwuIFRoaXMgdGVsbHMgdXMgdGhhdCBvdXIgbW9kZWwgaGFzIHRoZSBmb2xsb3dpbmcgZm9ybToNCg0KPGNlbnRlcj4NCioqUHJlZGljdGVkIFByaWNlID0gNTAuNTUgLSAwLjQzNTMgwrcgTWlsZWFnZSoqDQo8L2NlbnRlcj4NCjxicj4NClRoaXMgcmVsYXRpb25zaGlwIGJldHdlZW4gcHJpY2UgYW5kIG1pbGVhZ2UgY2FuIGFsc28gYmUgcmVwcmVzZW50ZWQgYXMgZm9sbG93czoNCg0KPGNlbnRlcj4NCioqQWN0dWFsIFByaWNlID0gNTAuNTUgLSAwLjQzNTMgwrcgTWlsZWFnZSArIFVuZXhwbGFpbmVkIEVycm9yKioNCjwvY2VudGVyPg0KPGJyPg0KTGV0J3MgYWRkIG91ciByZWdyZXNzaW9uIGxpbmUgdG8gdGhlIHNjYXR0ZXIgcGxvdCBvZiBwcmljZSBhbmQgbWlsZWFnZS4gDQoNCg0KYGBge3J9DQpwbG90KHByaWNlIH4gbWlsZWFnZSwgcGNoPTIxLCBiZz0nb3JhbmdlJywgY29sPSdibGFjaycsIGNleD0xLjUsDQogICAgIHhsYWIgPSAnTWlsZWFnZSAoaW4gMTAwMHMgb2YgTWlsZXMpJywgeWxhYiA9ICdQcmljZSAoaW4gMTAwMHMgb2YgRG9sbGFycyknLCANCiAgICAgbWFpbiA9ICdSZWxhdGlvbnNoaXAgYmV0d2VlbiBNaWxlYWdlIGFuZCBQcmljZScpDQphYmxpbmUobW9kZWwkY29lZmZpY2llbnRzLCBjb2w9J2NhZGV0Ymx1ZScsIGx3ZD0yKQ0KYGBgDQoNCiMgRml0dGVkIFZhbHVlcw0KDQpUaGUgZml0dGVkIHZhbHVlIGZvciBhbnkgcGFydGljbGFyIG9ic2VydmF0aW9uIGluIG91ciBzYW1wbGUgaXMgdGhlIHByaWNlIHRoYXQgdGhlIG1vZGVsIHByZWRpY3RzIGZvciB0aGUgY2FyLCBnaXZlbiB0aGUgbWlsZWFnZSBvZiB0aGF0IGNhci4gVGhpcyBpcyBvYnRhaW5lZCBieSBwbHVnZ2luZyB0aGUgbWlsZWFnZSBpbnRvIHRoZSBlcXVhdGlvbjoNCjxjZW50ZXI+DQoqKlByZWRpY3RlZCBQcmljZSA9IDUwLjU1IC0gMC40MzUzIMK3IE1pbGVhZ2UqKg0KPC9jZW50ZXI+DQo8YnI+DQpUaGUgZml0dGVkIHZhbHVlcyBmb3IgdGhlIGNhcnMgaW4gb3VyIHNhbXBsZSBhcmUgc3RvcmVkIHdpdGhpbiB0aGUgYG1vZGVsYCB2YXJpYWJsZQ0KDQpgYGB7cn0NCnJvdW5kKG1vZGVsJGZpdHRlZC52YWx1ZXMsMSkNCmBgYA0KDQpUbyBpbGx1c3RyYXRlIHRoaXMgaWRlYSwgd2Ugd2lsbCBhZGQgdGhlIGZpdHRlZCB2YWx1ZXMgdG8gb3VyIHNjYXR0ZXJwbG90LiANCg0KYGBge3IsIGVjaG89J0ZBTFNFJ30NCnBsb3QocHJpY2UgfiBtaWxlYWdlLCBwY2g9MjEsIGJnPSdvcmFuZ2UnLCBjb2w9J2JsYWNrJywgY2V4PTEuNSwNCiAgICAgeGxhYiA9ICdNaWxlYWdlIChpbiAxMDAwcyBvZiBNaWxlcyknLCB5bGFiID0gJ1ByaWNlIChpbiAxMDAwcyBvZiBEb2xsYXJzKScsIA0KICAgICBtYWluID0gJ1JlbGF0aW9uc2hpcCBiZXR3ZWVuIE1pbGVhZ2UgYW5kIFByaWNlJykNCg0KYWJsaW5lKG1vZGVsJGNvZWZmaWNpZW50cywgY29sPSdjYWRldGJsdWUnLCBsd2Q9MikNCg0KcG9pbnRzKG1pbGVhZ2UsIG1vZGVsJGZpdHRlZC52YWx1ZXMsIHBjaD0xOCwgY2V4PTEuNSwgY29sPSdkYXJrZ3JlZW4nKQ0KcG9pbnRzKHByaWNlIH4gbWlsZWFnZSwgcGNoPTIxLCBiZz0nb3JhbmdlJywgY29sPSdibGFjaycsIGNleD0xLjUpDQoNCg0KYGBgDQoNCg0KI1Jlc2lkdWFscw0KDQpUaGUgcmVzaWR1YWxzIGFyZSB0aGUgZXJyb3IgaW4gdGhlIHByZWRpY3RlZCBwcmljZXMuIFRoZSByZXNpZHVhbCBmb3IgYSBwYXJ0aWN1bGFyIG9ic2VydmF0aW9uIGlzIGdpdmVuIGJ5IHRoZSBlcXVhdGlvbjoNCjxjZW50ZXI+DQoqKlJlc2lkYWwgPSBBY3R1YWwgUHJpY2UgLSBQcmVkaWN0ZWQgUHJpY2UqKg0KPC9jZW50ZXI+DQo8YnI+DQoNClJlc2lkdWFscyByZWZsZWN0IHRoZSB1bmNlcnRhaW50eSByZW1haW5pbmcgaW4gb3VyIG1vZGVsLiANCg0KDQpgYGB7ciwgZWNobz0nRkFMU0UnfQ0KcGxvdChwcmljZSB+IG1pbGVhZ2UsIHBjaD0yMSwgYmc9J29yYW5nZScsIGNvbD0nYmxhY2snLCBjZXg9MS41LA0KICAgICB4bGFiID0gJ01pbGVhZ2UgKGluIDEwMDBzIG9mIE1pbGVzKScsIHlsYWIgPSAnUHJpY2UgKGluIDEwMDBzIG9mIERvbGxhcnMpJywgDQogICAgIG1haW4gPSAnUmVsYXRpb25zaGlwIGJldHdlZW4gTWlsZWFnZSBhbmQgUHJpY2UnKQ0KDQpzZWdtZW50cyhtaWxlYWdlLCBtb2RlbCRmaXR0ZWQudmFsdWVzLCBtaWxlYWdlLCBwcmljZSwgY29sPSdyZWQnLCBsd2Q9MikNCnBvaW50cyhwcmljZSB+IG1pbGVhZ2UsIHBjaD0yMSwgYmc9J29yYW5nZScsIGNvbD0nYmxhY2snLCBjZXg9MS41KQ0KDQphYmxpbmUobW9kZWwkY29lZmZpY2llbnRzLCBjb2w9J2NhZGV0Ymx1ZScsIGx3ZD0yKQ0KYGBgDQoNCk91ciBtb2RlbCBvYmplY3QgYG1vZGVsYCBjb250YWlucyB0aGUgcmVzaWR1YWxzIGZvciB0aGUgb2JzZXJ2YXRpb25zIGluIG91ciBzYW1wbGUuIA0KDQoNCmBgYHtyfQ0KcmVzIDwtIG1vZGVsJHJlc2lkdWFscw0KDQpyb3VuZChyZXMsMSkNCmBgYA0KDQpTaW5jZSB0aGUgcmVzaWR1YWxzIHJlcHJlc2VudCB0aGUgdW5jZXJ0YWlubHkgaW4gb3VyIHByZWRpY3Rpb25zLCB3ZSB3b3VsZCBsaWtlIHRvIGdldCBhIHNlbnNlIGFzIHRvIGhvdyB0aGV5IGFyZSBkaXN0cmlidXRlZC4gVG8gdGhhdCBlbmQsIHdlIGdlbmVyYXRlIGEgaGlzdG9ncmFtIG9mIHRoZSByZXNpZHVhbHMuDQoNCg0KYGBge3J9DQpoaXN0KHJlcywgY29sPSdvcmNoaWQnKQ0KYGBgDQoNCg0KSXQgc2VlbXMgcmVhc29uYWJsZSB0byBhc3N1bWUgdGhhdCB0aGUgcmVzaWR1YWxzIG1pZ2h0IGJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHdpdGggYSBtZWFuIG9mIHplcm8uIExldCdzIGNhbGN1bGF0ZSB0aGVpciBzYW1wbGUgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uLg0KDQoNCmBgYHtyfQ0KcmVzX21lYW4gPC0gbWVhbihyZXMpDQpyZXNfc2QgPC0gc2QocmVzKQ0KDQpyZXNfc3RhdHMgPC0gYyhyZXNfbWVhbiwgcmVzX3NkKQ0KbmFtZXMocmVzX3N0YXRzKSA8LSBjKCdtZWFuJywgJ3N0ZGV2JykNCg0Kcm91bmQocmVzX3N0YXRzLDQpDQpgYGANCg0KIyBVc2luZyB0aGUgTW9kZWwgdG8gTWFrZSBQcmVkaWN0aW9ucw0KDQpBc3N1bWUgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBwdXJjaGFzaW5nIGEgdXNlZCAyMDE2IEZvcmQgRmljdHVzIHdpdGggNDUsMDAwIG1pbGVzLiBXZSB3b3VsZCBsaWtlIHRvIHVzZSBvdXIgbW9kZWwgdG8gZGV0ZXJtaW5lIGEgZmFpciBwcmljZSBmb3IgdGhlIGNhci4gV2UgY291bGQgY2FsdWxjYXRlIHRoaXMgYnkgcGx1Z2dpbmcgNDUgaW50byB0aGUgZXF1YXRpb24gZm9yIG91ciBtb2RlbC5UaGF0IGdpdmVzOg0KDQo8Y2VudGVyPg0KKipQcmVkaWN0ZWQgUHJpY2UgPSA1MC41NSAtIDAuNDM1MyDCtyA0NSA9IDMwLjk2KioNCjwvY2VudGVyPg0KPGJyPg0KSW4gb3RoZXIgd29yZHMsIG91ciBtb2RlbCBwcmVkaWN0cyB0aGF0IHN1Y2ggYSB2ZWhpY2xlIHNob3VsZCBjb3N0IChvbiBhdmVyYWdlKSAkMzAsOTYwLiANCg0KV2UgY2FuIHVzZSB0aGUgUiBmdW5jdGlvbiBgcHJlZGljdGAgdG8gY2FsY3VsYXRlIHRoaXMgcHJlZGljdGVkIHZhbHVlLiANCg0KYGBge3J9DQpuZXdkYXRhID0gZGF0YS5mcmFtZShtaWxlYWdlPWMoNDUpKQ0KcHJlZGljdChtb2RlbCwgbmV3ZGF0YSkNCmBgYA0KDQpXZSBrbm93IHRoYXQgdGhlIHByZWRpY3Rpb25zIG1hZGUgYnkgdGhlIG1vZGVsIGFyZSBub3QgMTAwJSBhY2N1cmF0ZS4gV2UgZXhwZWN0IHRoZXJlIHRvIGJlIHNvbWUgZXJyb3IgaW4gdGhlIHByZWRpY3Rpb25zLiBUbyBiZXR0ZXIgdW5kZXJzdGFuZCBob3cgbXVjaCB0aGUgYWN0dWFsIHByaWNlIG9mIGEgY2FyIHdpdGggNDUsMDAwIG1pbGVzIG1pZ2h0IHZhcnksIHdlIHdpbGwgdXNlIGBwcmVkaWN0YCB0byBjcmVhdGUgYW4gaW50ZXJ2YWwgdGhhdCB3ZSBhcmUgOTUlIGNlcnRhaW4gY29udGFpbnMgdGhlIHRydWUgcHJpY2Ugb2YgdGhlIGNhci4gVGhpcyBpbnRlcnZhbCBpcyBjYWxsZWQgYSAqKnByZWRpY3Rpb24gaW50ZXJ2YWwqKi4gDQoNCmBgYHtyfQ0KcHJlZGljdChtb2RlbCwgbmV3ZGF0YSwgaW50ZXJ2YWwgPSAncHJlZGljdGlvbicsIGxldmVsPTAuOTUpDQpgYGANCg0KDQo=