Data

Today we are going to study linear regression using one of the available datasets in R. The dataset cars is taken from R package datasets. The dataframe is very straightforward. One column gives the speed of cars, and the other column reports the distances taken for the cars to stop.

library(datasets)
cars

There are 50 observations. Let’s take a look at the scatter plot of the data. Notice that the simple command plot will plot the dataframe automatically for you. It also assumes that the second variable is assigned to the y-axis and the first variable is assigned to the x-axis. In this case, it happens that the distance needed for a car to stop is our variable of interest. Having dist on the y-axis as the response seems to be a good choice.

plot(cars)

Clearly, there is a positive correlation between dist and speed. Intuitively, this also makes sense, as the faster one drives, the longer the distances required to bring the car to a full stop.

Linear Regression

Here we are going to consider a least squares fit of the data. Please keep in mind that least squares is not the only option for regression. Least squares solution minimizes the sum of squared distances from the data points to the linear fit, whereas there is also the option to minimzes the sum of absolute distances for example.

Rather than hand-computing the least squares solution, we will utilize one of the most popular functions in R, lm() function. The main argument of the function is the first argument formula. It should be provided in the form y ~ x, where y is the response and x is the explanatory variable.

fit <- lm(formula=dist~speed, data=cars)
fit

Call:
lm(formula = dist ~ speed, data = cars)

Coefficients:
(Intercept)        speed  
    -17.579        3.932  

As we see from the output of the linear model fit, the coefficients, intercept and slope, are both calculated already. Let’s take a look at the scatter plot again with the least squares fit.

plot(cars)
abline(fit, col="red")

There are some obvious issues that one might see from this fit. For example, the intercept is negative. The logical intercept is at zero, since a car with speed zero should require zero distance to stop. (I believe the speed and distance here follow the common understanding people share in daily conversations, not the rigorous ones presented in a physics class.) However, data is not perfect, there are always errors that come into play.

Diagnostics

Three characteristics have been mentioned in the lecture slides, linearity, residual normality, and constant variability. We first check linearity. For linearity, we usually check for the scatter plot and the residual plot. As we have seen the scatter plot for a couple of times already, we will take a look at the residual plot.

plot(fit$residuals)
abline(0, 0, col="red")

As linearity has been checked, we move on to residual normality. Let’s take a look at the histogram and the Q-Q plots of the residuals.

hist(fit$residuals)

qqnorm(fit$residuals)
qqline(fit$residuals, col="red")

The Q-Q plot does indicate that the data points with large deviations away from the theoretical quantile line may be outliers. But this may also be due to the small sample size we have, as the dataset only consists of 50 observations.

Last but not the least, we will check for the constant variability. Usually, this is verified by examining the residual plot. A fan shape in the residual plot indicates heteroscedacity (unequal variance). This is not the case with the current data we have.

plot(fit$residuals)
abline(0, 0, col="red")

In addition, one may want to identify outliers within the dataset. For instance, we had doubt about the data points with fit residuals largely deviating from the theoretical Q-Q line. Let’s figure out which data points these residuals correspond to.

res.rank <- sort(fit$residuals)
suspect <- which(fit$residuals %in% res.rank[47:50])

Let’s see how different the linear fit is, if we remove these points. The red line is the original fit, whereas the blue line is the fit with the two points removed.

plot(cars)
abline(fit, col="red")
fit.out <- lm(formula=dist[-suspect]~speed[-suspect], data=cars)
abline(fit.out, col="blue")

We do notice that the blue line has been tilted downward from the red line, and we do have a slightly better intercept. One may consider the two points as influential points, as they influence the slope and can be understood as outliers in response.

There are more rigorous procedures, such as various testing methods, to detect outliers in both response and the exploratory variables. However, they are out the scope of this class. We might talk about them later on.

More Details on Linear Regression

One of the most important measure in evaluating the linear fit of the data is through \(R^2\), which is the squared correlation coefficient. It is formally defined as \[R^2 = \frac{SSE_{reg}}{SSE_{total}}, \] where \(SSE_{reg} = \sum_{i=1}^n (\hat y_i - y_i)^2\) and \(SSE_{total} = \sum_{i=1}^n (y_i - \bar y)^2\), \(\hat y_i\) being the fitted values using the fitted linear model and \(\bar y\) being the mean of the response. This is understood as the fraction of variability explained by the regression model.

Let’s try to calculate this using the linear model we fitted earlier.

R.square <- sum((fit$fitted.values-mean(cars$dist))^2) / (sum((cars$dist - mean(cars$dist))^2))
R.square
[1] 0.6510794

As this number is used so often, one does not have to calculate this every time by hand. \(R^2\) information can be found using the summary() function for lm() fits.

summary(fit)

Call:
lm(formula = dist ~ speed, data = cars)

Residuals:
    Min      1Q  Median      3Q     Max 
-29.069  -9.525  -2.272   9.215  43.201 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -17.5791     6.7584  -2.601   0.0123 *  
speed         3.9324     0.4155   9.464 1.49e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 15.38 on 48 degrees of freedom
Multiple R-squared:  0.6511,    Adjusted R-squared:  0.6438 
F-statistic: 89.57 on 1 and 48 DF,  p-value: 1.49e-12

Usually, a larger \(R^2\) value means a larger proportion of the variation has been explained by the regression model, which seems very promising. However, a not of warning, \(R^2\) must be compared on the same level of model complexity. For example, one can fit the response in a linear model perfectly, if one can have arbitrary number of explanatory variables. In such cases, \(R^2\) value will be at its maximum possible value, which is 1. However, such a fitted model is not helpful, as it will not be able to predict unseen data well.

The summary() function actually provides much more information than merely \(R^2\). In fact, it contains the testing information under the coefficients.

Denote the intercept coefficient as \(\beta_0\) and the speed coefficient as \(\beta_1\). The two lines regarding testing in the summary refer to the following two tests. The first line tests \(H_0: \beta_0 = 0\) against \(H_1: \beta_0 \neq 0\). The second line tests \(H_0: \beta_1 = 0\) against \(H_1: \beta_1 \neq 0\).

If we further denote the estimates as \(\hat \beta_0\) and \(\hat \beta_1\), and the standard errors as \(\sigma_0\) and \(\sigma_1\). The test statistics respectively are \[\frac{\hat \beta_i - 0}{\sigma_i} \sim T_{n - 2},\] where \(i = 0 \text{ or } 1\).

LS0tCnRpdGxlOiAiTWF0aCAxODkvMjg5IExhYiA3IgphdXRob3I6ICJKaWFxaSBHdW8iCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKIyBEYXRhClRvZGF5IHdlIGFyZSBnb2luZyB0byBzdHVkeSBsaW5lYXIgcmVncmVzc2lvbiB1c2luZyBvbmUgb2YgdGhlIGF2YWlsYWJsZSBkYXRhc2V0cyBpbiBSLiBUaGUgZGF0YXNldCAqY2FycyogaXMgdGFrZW4gZnJvbSBSIHBhY2thZ2UgKmRhdGFzZXRzKi4gVGhlIGRhdGFmcmFtZSBpcyB2ZXJ5IHN0cmFpZ2h0Zm9yd2FyZC4gT25lIGNvbHVtbiBnaXZlcyB0aGUgc3BlZWQgb2YgY2FycywgYW5kIHRoZSBvdGhlciBjb2x1bW4gcmVwb3J0cyB0aGUgZGlzdGFuY2VzIHRha2VuIGZvciB0aGUgY2FycyB0byBzdG9wLgpgYGB7cn0KbGlicmFyeShkYXRhc2V0cykKY2FycwpgYGAKClRoZXJlIGFyZSA1MCBvYnNlcnZhdGlvbnMuIExldCdzIHRha2UgYSBsb29rIGF0IHRoZSBzY2F0dGVyIHBsb3Qgb2YgdGhlIGRhdGEuIE5vdGljZSB0aGF0IHRoZSBzaW1wbGUgY29tbWFuZCBwbG90IHdpbGwgcGxvdCB0aGUgZGF0YWZyYW1lIGF1dG9tYXRpY2FsbHkgZm9yIHlvdS4gSXQgYWxzbyBhc3N1bWVzIHRoYXQgdGhlIHNlY29uZCB2YXJpYWJsZSBpcyBhc3NpZ25lZCB0byB0aGUgeS1heGlzIGFuZCB0aGUgZmlyc3QgdmFyaWFibGUgaXMgYXNzaWduZWQgdG8gdGhlIHgtYXhpcy4gSW4gdGhpcyBjYXNlLCBpdCBoYXBwZW5zIHRoYXQgdGhlIGRpc3RhbmNlIG5lZWRlZCBmb3IgYSBjYXIgdG8gc3RvcCBpcyBvdXIgdmFyaWFibGUgb2YgaW50ZXJlc3QuIEhhdmluZyAqZGlzdCogb24gdGhlIHktYXhpcyBhcyB0aGUgcmVzcG9uc2Ugc2VlbXMgdG8gYmUgYSBnb29kIGNob2ljZS4KYGBge3J9CnBsb3QoY2FycykKYGBgCgpDbGVhcmx5LCB0aGVyZSBpcyBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gKmRpc3QqIGFuZCAqc3BlZWQqLiBJbnR1aXRpdmVseSwgdGhpcyBhbHNvIG1ha2VzIHNlbnNlLCBhcyB0aGUgZmFzdGVyIG9uZSBkcml2ZXMsIHRoZSBsb25nZXIgdGhlIGRpc3RhbmNlcyByZXF1aXJlZCB0byBicmluZyB0aGUgY2FyIHRvIGEgZnVsbCBzdG9wLgoKIyBMaW5lYXIgUmVncmVzc2lvbgpIZXJlIHdlIGFyZSBnb2luZyB0byBjb25zaWRlciBhIGxlYXN0IHNxdWFyZXMgZml0IG9mIHRoZSBkYXRhLiBQbGVhc2Uga2VlcCBpbiBtaW5kIHRoYXQgbGVhc3Qgc3F1YXJlcyBpcyBub3QgdGhlIG9ubHkgb3B0aW9uIGZvciByZWdyZXNzaW9uLiBMZWFzdCBzcXVhcmVzIHNvbHV0aW9uIG1pbmltaXplcyB0aGUgc3VtIG9mIHNxdWFyZWQgZGlzdGFuY2VzIGZyb20gdGhlIGRhdGEgcG9pbnRzIHRvIHRoZSBsaW5lYXIgZml0LCB3aGVyZWFzIHRoZXJlIGlzIGFsc28gdGhlIG9wdGlvbiB0byBtaW5pbXplcyB0aGUgc3VtIG9mIGFic29sdXRlIGRpc3RhbmNlcyBmb3IgZXhhbXBsZS4KClJhdGhlciB0aGFuIGhhbmQtY29tcHV0aW5nIHRoZSBsZWFzdCBzcXVhcmVzIHNvbHV0aW9uLCB3ZSB3aWxsIHV0aWxpemUgb25lIG9mIHRoZSBtb3N0IHBvcHVsYXIgZnVuY3Rpb25zIGluIFIsICpsbSgpKiBmdW5jdGlvbi4gVGhlIG1haW4gYXJndW1lbnQgb2YgdGhlIGZ1bmN0aW9uIGlzIHRoZSBmaXJzdCBhcmd1bWVudCAqZm9ybXVsYSouIEl0IHNob3VsZCBiZSBwcm92aWRlZCBpbiB0aGUgZm9ybSB5IH4geCwgd2hlcmUgeSBpcyB0aGUgcmVzcG9uc2UgYW5kIHggaXMgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlLgpgYGB7cn0KZml0IDwtIGxtKGZvcm11bGE9ZGlzdH5zcGVlZCwgZGF0YT1jYXJzKQpmaXQKYGBgCgpBcyB3ZSBzZWUgZnJvbSB0aGUgb3V0cHV0IG9mIHRoZSBsaW5lYXIgbW9kZWwgZml0LCB0aGUgY29lZmZpY2llbnRzLCBpbnRlcmNlcHQgYW5kIHNsb3BlLCBhcmUgYm90aCBjYWxjdWxhdGVkIGFscmVhZHkuIExldCdzIHRha2UgYSBsb29rIGF0IHRoZSBzY2F0dGVyIHBsb3QgYWdhaW4gd2l0aCB0aGUgbGVhc3Qgc3F1YXJlcyBmaXQuCmBgYHtyfQpwbG90KGNhcnMpCmFibGluZShmaXQsIGNvbD0icmVkIikKYGBgCgpUaGVyZSBhcmUgc29tZSBvYnZpb3VzIGlzc3VlcyB0aGF0IG9uZSBtaWdodCBzZWUgZnJvbSB0aGlzIGZpdC4gRm9yIGV4YW1wbGUsIHRoZSBpbnRlcmNlcHQgaXMgbmVnYXRpdmUuIFRoZSBsb2dpY2FsIGludGVyY2VwdCBpcyBhdCB6ZXJvLCBzaW5jZSBhIGNhciB3aXRoIHNwZWVkIHplcm8gc2hvdWxkIHJlcXVpcmUgemVybyBkaXN0YW5jZSB0byBzdG9wLiAoSSBiZWxpZXZlIHRoZSBzcGVlZCBhbmQgZGlzdGFuY2UgaGVyZSBmb2xsb3cgdGhlIGNvbW1vbiB1bmRlcnN0YW5kaW5nIHBlb3BsZSBzaGFyZSBpbiBkYWlseSBjb252ZXJzYXRpb25zLCBub3QgdGhlIHJpZ29yb3VzIG9uZXMgcHJlc2VudGVkIGluIGEgcGh5c2ljcyBjbGFzcy4pIEhvd2V2ZXIsIGRhdGEgaXMgbm90IHBlcmZlY3QsIHRoZXJlIGFyZSBhbHdheXMgZXJyb3JzIHRoYXQgY29tZSBpbnRvIHBsYXkuIAoKIyBEaWFnbm9zdGljcwpUaHJlZSBjaGFyYWN0ZXJpc3RpY3MgaGF2ZSBiZWVuIG1lbnRpb25lZCBpbiB0aGUgbGVjdHVyZSBzbGlkZXMsIGxpbmVhcml0eSwgcmVzaWR1YWwgbm9ybWFsaXR5LCBhbmQgY29uc3RhbnQgdmFyaWFiaWxpdHkuIFdlIGZpcnN0IGNoZWNrIGxpbmVhcml0eS4gRm9yIGxpbmVhcml0eSwgd2UgdXN1YWxseSBjaGVjayBmb3IgdGhlIHNjYXR0ZXIgcGxvdCBhbmQgdGhlIHJlc2lkdWFsIHBsb3QuIEFzIHdlIGhhdmUgc2VlbiB0aGUgc2NhdHRlciBwbG90IGZvciBhIGNvdXBsZSBvZiB0aW1lcyBhbHJlYWR5LCB3ZSB3aWxsIHRha2UgYSBsb29rIGF0IHRoZSByZXNpZHVhbCBwbG90LgpgYGB7cn0KcGxvdChmaXQkcmVzaWR1YWxzKQphYmxpbmUoMCwgMCwgY29sPSJyZWQiKQpgYGAKCkFzIGxpbmVhcml0eSBoYXMgYmVlbiBjaGVja2VkLCB3ZSBtb3ZlIG9uIHRvIHJlc2lkdWFsIG5vcm1hbGl0eS4gTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGhpc3RvZ3JhbSBhbmQgdGhlIFEtUSBwbG90cyBvZiB0aGUgcmVzaWR1YWxzLgpgYGB7cn0KaGlzdChmaXQkcmVzaWR1YWxzKQpxcW5vcm0oZml0JHJlc2lkdWFscykKcXFsaW5lKGZpdCRyZXNpZHVhbHMsIGNvbD0icmVkIikKYGBgClRoZSBRLVEgcGxvdCBkb2VzIGluZGljYXRlIHRoYXQgdGhlIGRhdGEgcG9pbnRzIHdpdGggbGFyZ2UgZGV2aWF0aW9ucyBhd2F5IGZyb20gdGhlIHRoZW9yZXRpY2FsIHF1YW50aWxlIGxpbmUgbWF5IGJlIG91dGxpZXJzLiBCdXQgdGhpcyBtYXkgYWxzbyBiZSBkdWUgdG8gdGhlIHNtYWxsIHNhbXBsZSBzaXplIHdlIGhhdmUsIGFzIHRoZSBkYXRhc2V0IG9ubHkgY29uc2lzdHMgb2YgNTAgb2JzZXJ2YXRpb25zLgoKTGFzdCBidXQgbm90IHRoZSBsZWFzdCwgd2Ugd2lsbCBjaGVjayBmb3IgdGhlIGNvbnN0YW50IHZhcmlhYmlsaXR5LiBVc3VhbGx5LCB0aGlzIGlzIHZlcmlmaWVkIGJ5IGV4YW1pbmluZyB0aGUgcmVzaWR1YWwgcGxvdC4gQSBmYW4gc2hhcGUgaW4gdGhlIHJlc2lkdWFsIHBsb3QgaW5kaWNhdGVzIGhldGVyb3NjZWRhY2l0eSAodW5lcXVhbCB2YXJpYW5jZSkuIFRoaXMgaXMgbm90IHRoZSBjYXNlIHdpdGggdGhlIGN1cnJlbnQgZGF0YSB3ZSBoYXZlLgpgYGB7cn0KcGxvdChmaXQkcmVzaWR1YWxzKQphYmxpbmUoMCwgMCwgY29sPSJyZWQiKQpgYGAKCkluIGFkZGl0aW9uLCBvbmUgbWF5IHdhbnQgdG8gaWRlbnRpZnkgb3V0bGllcnMgd2l0aGluIHRoZSBkYXRhc2V0LiBGb3IgaW5zdGFuY2UsIHdlIGhhZCBkb3VidCBhYm91dCB0aGUgZGF0YSBwb2ludHMgd2l0aCBmaXQgcmVzaWR1YWxzIGxhcmdlbHkgZGV2aWF0aW5nIGZyb20gdGhlIHRoZW9yZXRpY2FsIFEtUSBsaW5lLiBMZXQncyBmaWd1cmUgb3V0IHdoaWNoIGRhdGEgcG9pbnRzIHRoZXNlIHJlc2lkdWFscyBjb3JyZXNwb25kIHRvLgpgYGB7cn0KcmVzLnJhbmsgPC0gc29ydChmaXQkcmVzaWR1YWxzKQpzdXNwZWN0IDwtIHdoaWNoKGZpdCRyZXNpZHVhbHMgJWluJSByZXMucmFua1s0Nzo1MF0pCmBgYAoKTGV0J3Mgc2VlIGhvdyBkaWZmZXJlbnQgdGhlIGxpbmVhciBmaXQgaXMsIGlmIHdlIHJlbW92ZSB0aGVzZSBwb2ludHMuIFRoZSByZWQgbGluZSBpcyB0aGUgb3JpZ2luYWwgZml0LCB3aGVyZWFzIHRoZSBibHVlIGxpbmUgaXMgdGhlIGZpdCB3aXRoIHRoZSB0d28gcG9pbnRzIHJlbW92ZWQuCmBgYHtyfQpwbG90KGNhcnMpCmFibGluZShmaXQsIGNvbD0icmVkIikKZml0Lm91dCA8LSBsbShmb3JtdWxhPWRpc3RbLXN1c3BlY3RdfnNwZWVkWy1zdXNwZWN0XSwgZGF0YT1jYXJzKQphYmxpbmUoZml0Lm91dCwgY29sPSJibHVlIikKYGBgCgpXZSBkbyBub3RpY2UgdGhhdCB0aGUgYmx1ZSBsaW5lIGhhcyBiZWVuIHRpbHRlZCBkb3dud2FyZCBmcm9tIHRoZSByZWQgbGluZSwgYW5kIHdlIGRvIGhhdmUgYSBzbGlnaHRseSBiZXR0ZXIgaW50ZXJjZXB0LiBPbmUgbWF5IGNvbnNpZGVyIHRoZSB0d28gcG9pbnRzIGFzIGluZmx1ZW50aWFsIHBvaW50cywgYXMgdGhleSBpbmZsdWVuY2UgdGhlIHNsb3BlIGFuZCBjYW4gYmUgdW5kZXJzdG9vZCBhcyBvdXRsaWVycyBpbiByZXNwb25zZS4gCgpUaGVyZSBhcmUgbW9yZSByaWdvcm91cyBwcm9jZWR1cmVzLCBzdWNoIGFzIHZhcmlvdXMgdGVzdGluZyBtZXRob2RzLCB0byBkZXRlY3Qgb3V0bGllcnMgaW4gYm90aCByZXNwb25zZSBhbmQgdGhlIGV4cGxvcmF0b3J5IHZhcmlhYmxlcy4gSG93ZXZlciwgdGhleSBhcmUgb3V0IHRoZSBzY29wZSBvZiB0aGlzIGNsYXNzLiBXZSBtaWdodCB0YWxrIGFib3V0IHRoZW0gbGF0ZXIgb24uCgojIE1vcmUgRGV0YWlscyBvbiBMaW5lYXIgUmVncmVzc2lvbgpPbmUgb2YgdGhlIG1vc3QgaW1wb3J0YW50IG1lYXN1cmUgaW4gZXZhbHVhdGluZyB0aGUgbGluZWFyIGZpdCBvZiB0aGUgZGF0YSBpcyB0aHJvdWdoICRSXjIkLCB3aGljaCBpcyB0aGUgc3F1YXJlZCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudC4gSXQgaXMgZm9ybWFsbHkgZGVmaW5lZCBhcyAKJCRSXjIgPSBcZnJhY3tTU0Vfe3JlZ319e1NTRV97dG90YWx9fSwgJCQKd2hlcmUgJFNTRV97cmVnfSA9IFxzdW1fe2k9MX1ebiAoXGhhdCB5X2kgLSB5X2kpXjIkIGFuZCAkU1NFX3t0b3RhbH0gPSBcc3VtX3tpPTF9Xm4gKHlfaSAtIFxiYXIgeSleMiQsICRcaGF0IHlfaSQgYmVpbmcgdGhlIGZpdHRlZCB2YWx1ZXMgdXNpbmcgdGhlIGZpdHRlZCBsaW5lYXIgbW9kZWwgYW5kICRcYmFyIHkkIGJlaW5nIHRoZSBtZWFuIG9mIHRoZSByZXNwb25zZS4gVGhpcyBpcyB1bmRlcnN0b29kIGFzIHRoZSBmcmFjdGlvbiBvZiB2YXJpYWJpbGl0eSBleHBsYWluZWQgYnkgdGhlIHJlZ3Jlc3Npb24gbW9kZWwuCgpMZXQncyB0cnkgdG8gY2FsY3VsYXRlIHRoaXMgdXNpbmcgdGhlIGxpbmVhciBtb2RlbCB3ZSBmaXR0ZWQgZWFybGllci4gCmBgYHtyfQpSLnNxdWFyZSA8LSBzdW0oKGZpdCRmaXR0ZWQudmFsdWVzLW1lYW4oY2FycyRkaXN0KSleMikgLyAoc3VtKChjYXJzJGRpc3QgLSBtZWFuKGNhcnMkZGlzdCkpXjIpKQpSLnNxdWFyZQpgYGAKCkFzIHRoaXMgbnVtYmVyIGlzIHVzZWQgc28gb2Z0ZW4sIG9uZSBkb2VzIG5vdCBoYXZlIHRvIGNhbGN1bGF0ZSB0aGlzIGV2ZXJ5IHRpbWUgYnkgaGFuZC4gJFJeMiQgaW5mb3JtYXRpb24gY2FuIGJlIGZvdW5kIHVzaW5nIHRoZSAqc3VtbWFyeSgpKiBmdW5jdGlvbiBmb3IgKmxtKCkqIGZpdHMuCmBgYHtyfQpzdW1tYXJ5KGZpdCkKYGBgCgpVc3VhbGx5LCBhIGxhcmdlciAkUl4yJCB2YWx1ZSBtZWFucyBhIGxhcmdlciBwcm9wb3J0aW9uIG9mIHRoZSB2YXJpYXRpb24gaGFzIGJlZW4gZXhwbGFpbmVkIGJ5IHRoZSByZWdyZXNzaW9uIG1vZGVsLCB3aGljaCBzZWVtcyB2ZXJ5IHByb21pc2luZy4gSG93ZXZlciwgYSBub3Qgb2Ygd2FybmluZywgJFJeMiQgbXVzdCBiZSBjb21wYXJlZCBvbiB0aGUgc2FtZSBsZXZlbCBvZiBtb2RlbCBjb21wbGV4aXR5LiBGb3IgZXhhbXBsZSwgb25lIGNhbiBmaXQgdGhlIHJlc3BvbnNlIGluIGEgbGluZWFyIG1vZGVsIHBlcmZlY3RseSwgaWYgb25lIGNhbiBoYXZlIGFyYml0cmFyeSBudW1iZXIgb2YgZXhwbGFuYXRvcnkgdmFyaWFibGVzLiBJbiBzdWNoIGNhc2VzLCAkUl4yJCB2YWx1ZSB3aWxsIGJlIGF0IGl0cyBtYXhpbXVtIHBvc3NpYmxlIHZhbHVlLCB3aGljaCBpcyAxLiBIb3dldmVyLCBzdWNoIGEgZml0dGVkIG1vZGVsIGlzIG5vdCBoZWxwZnVsLCBhcyBpdCB3aWxsIG5vdCBiZSBhYmxlIHRvIHByZWRpY3QgdW5zZWVuIGRhdGEgd2VsbC4KClRoZSAqc3VtbWFyeSgpKiBmdW5jdGlvbiBhY3R1YWxseSBwcm92aWRlcyBtdWNoIG1vcmUgaW5mb3JtYXRpb24gdGhhbiBtZXJlbHkgJFJeMiQuIEluIGZhY3QsIGl0IGNvbnRhaW5zIHRoZSB0ZXN0aW5nIGluZm9ybWF0aW9uIHVuZGVyIHRoZSBjb2VmZmljaWVudHMuCgpEZW5vdGUgdGhlIGludGVyY2VwdCBjb2VmZmljaWVudCBhcyAkXGJldGFfMCQgYW5kIHRoZSBzcGVlZCBjb2VmZmljaWVudCBhcyAkXGJldGFfMSQuIFRoZSB0d28gbGluZXMgcmVnYXJkaW5nIHRlc3RpbmcgaW4gdGhlIHN1bW1hcnkgcmVmZXIgdG8gdGhlIGZvbGxvd2luZyB0d28gdGVzdHMuIFRoZSBmaXJzdCBsaW5lIHRlc3RzICRIXzA6IFxiZXRhXzAgPSAwJCBhZ2FpbnN0ICRIXzE6IFxiZXRhXzAgXG5lcSAwJC4gVGhlIHNlY29uZCBsaW5lIHRlc3RzICRIXzA6IFxiZXRhXzEgPSAwJCBhZ2FpbnN0ICRIXzE6IFxiZXRhXzEgXG5lcSAwJC4KCklmIHdlIGZ1cnRoZXIgZGVub3RlIHRoZSBlc3RpbWF0ZXMgYXMgJFxoYXQgXGJldGFfMCQgYW5kICRcaGF0IFxiZXRhXzEkLCBhbmQgdGhlIHN0YW5kYXJkIGVycm9ycyBhcyAkXHNpZ21hXzAkIGFuZCAkXHNpZ21hXzEkLiBUaGUgdGVzdCBzdGF0aXN0aWNzIHJlc3BlY3RpdmVseSBhcmUgCiQkXGZyYWN7XGhhdCBcYmV0YV9pIC0gMH17XHNpZ21hX2l9IFxzaW0gVF97biAtIDJ9LCQkCndoZXJlICRpID0gMCBcdGV4dHsgb3IgfSAxJC4K