This example fits simple regression models to small data sets. The second shows lattice/trellis plots.
To demonstrate the extensibility of R, here’s two functions that allow me to show students the idea of visually testing for association. I would hide the definition of this function from students, but being written in R, the curious students could always find it.
Example 1: Locating a Franchise Outlet
Read the data into a data frame. The data frame has 80 observations of two variables.
Franchise <- read.csv("Data/21_4m_franchise.csv")
Each row of the data frame describes the amount of gasoline sales (in thousands of gallons) and the traffic volume, also in thousands. For example, sales in the first week were 6640 and 7760 in the second week. Traffic volume was 3.3610^{4} in the first week.
Franchise
The View
command opens a spreadsheet view of the data. You can only view, not change, the data. Buttons in the header of the view support sorting the columns.
View(Franchise)
Marginal Plots
Histograms are a good starting point, identifying the shape of the distribution and outliers.
hist(Franchise$Sales, breaks=10)
You can then get more clever and add a boxplot to the figure if you’d like, helping to explain both.
hist(Franchise$Sales, breaks=10)
boxplot(Franchise$Sales, add=TRUE, horizontal=TRUE, width=2, at=6)
Bivariate Plots
A scatterplot of sales on traffic volume shows that the two variables are linearly associated. The association is moderately strong. This chunk also adds the fitted regression line to the figure.
plot(Sales ~ Traffic, data=Franchise)
Once you’ve seen the plot, a regression line seems like a good summary. This is also a good chance to show the visual test for association
visual_test_for_association(Franchise$Traffic,Franchise$Sales,3,3)
plot(Sales ~ Traffic, data=Franchise)
regr <- lm(Sales ~ Traffic, data=Franchise)
abline(regr, col='red')
The summary of a regression creates a named list that has properties of the regression,
sRegr <- summary(regr)
names(sRegr)
[1] "call" "terms" "residuals" "coefficients" "aliased" "sigma" "df"
[8] "r.squared" "adj.r.squared" "fstatistic" "cov.unscaled"
These include the intercept, slope and standard error of the regression.
sRegr$coefficients
Estimate Std. Error t value Pr(>|t|)
(Intercept) -1.3380974 0.94584359 -1.414713 1.611324e-01
Traffic 0.2367286 0.02431421 9.736225 4.060496e-15
sRegr$sigma
[1] 1.505407
Printing the summary shows a table of the least squares estimates and the overall fit of the regression.
sRegr
Call:
lm(formula = Sales ~ Traffic, data = Franchise)
Residuals:
Min 1Q Median 3Q Max
-3.3329 -0.8684 0.0144 0.8478 3.8181
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -1.33810 0.94584 -1.415 0.161
Traffic 0.23673 0.02431 9.736 4.06e-15 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1.505 on 78 degrees of freedom
Multiple R-squared: 0.5486, Adjusted R-squared: 0.5428
F-statistic: 94.79 on 1 and 78 DF, p-value: 4.06e-15
You can add components from the model summary, embellishing the plot.
plot(Sales ~ Traffic, data=Franchise)
regr <- lm(Sales ~ Traffic, data=Franchise)
abline(regr, col='red')
b <- round(coefficients(regr),2)
text(45,4, paste0("Fit = ",b[1],"+",b[2],"Traffic"), col='blue')
You can also use the summary of the regression to build prediction intervals, though now you can start to see how R begins to look more like “programming” rather than “statistics”.
plot(Sales ~ Traffic, data=Franchise)
regr <- lm(Sales ~ Traffic, data=Franchise)
abline(regr, col='red')
xValues <- data.frame(Traffic = seq(25, 50, length.out=100))
predInt <- predict(regr, newdata = xValues, interval="prediction")
lines(xValues$Traffic, predInt[,'lwr'], lty=3, col='red')
lines(xValues$Traffic, predInt[,'upr'], lty=3, col='red')
Of course, packages exist that automate routine figures.
ggplot(Franchise, aes(x=Traffic, y=Sales)) +
geom_point() +
geom_smooth(method='lm')
Before relying on the summary statistics for inference, check that the residuals do not show an evident pattern.
plot(Franchise$Traffic, residuals(regr))
abline(h=0, col='gray')
In addition check that the distribution of the residuals appears nearly normal. To get the bands, use the version of the normal quantile plot from the car
package (see Chapter 12).
par(mfrow=c(1,2))
qqnorm(residuals(regr))
car::qqPlot(residuals(regr), main="Q-Q Plot")
At the extremes, the residuals seem to be a bit “fat-tailed”, more extreme that what normality would expect. The deviation is slight, but worth noting.
Example #2: Pricing Used Cars
The following data describe 276 “certified” used BMW sedans.
# Cars <- read.csv("http://www-stat.wharton.upenn.edu/~stine/stat405/bmw_2016.csv")
Cars <- read.csv("bmw_2016.csv")
dim(Cars)
[1] 276 7
The data include the model year, price (in dollars), mileage, model, model style, color, and type of transmission (auto or manual).
Cars
This analysis concerns the affect of the model type on price. Before continuing, convert the model number to a factor
(R’s version of a categorical variable) rather than leave as a number. Same for the model year. This conversion avoids treating these data as numbers rather than identifying groups.
Cars$Model <- as.factor(Cars$Model)
Cars$Age <- Cars$Year - 2016 # numerical
Cars$Year <- as.factor(Cars$Year)
Here’s the standard comparison boxplot display.
boxplot(Price ~ Model, data = Cars)
lattice
offers a similar plot.
lattice::bwplot(Price ~ Model, data = Cars)
lattice
produces marginal plots that are perhaps more attractive – and easier to construct – than if done in R. For example, here are kernel density plots of the prices of the 3 different models (listed price, in dollars). I prefer to have them “on top of each other” to help with the comparison, but the common scales are helpful. lattice
uses statistical notation for conditional association, with a vertical bar indicating conditional relationships, in this case showing the distribution of prices conditional on each of the 3 model types.
lattice::densityplot(~ Price | Model, data=Cars)
Boxplots like the following convey a sense of the general depreciation within each model type. Grid lines would help to align the data as well, but the option does not run in this plot.
bwplot(Price ~ Year | Model, data=Cars)
stripplot
shows the data without the boxes. With overprinting as in this example, you want the dithering option (“jitter” in lattice) turned on. This option adds a small amount of random variation to avoid overprinting. Gridlines work here very well.
stripplot(Price ~ Year | Model, data=Cars, jitter.data=TRUE, grid='h')
Lattice plots are helpful when looking at the effect of “lurking” variables on bivariate association. For example, here’s a “regular R” plot of price on mileage. To show that this relationship depends upon the model type, I’ve colored points by the model type. You can see what’s happening (maybe), but it’s subtle.
plot(Price ~ Mileage, data=Cars, col=Cars$Model)
Rather than drop the data into a regression, we can explore conditional associations graphically using lattice
. You can guess what the following plot does.
xyplot(Price ~ Mileage | Model, data=Cars, grid='h',
main="Scatterplot by Model Type")
Because of the common scaling used for the 3 frames of the plot, we can see that the slopes look very similar (ie, no interaction), but the level is higher for the 335 models that the other two.
You can add more to each panel. The optional type
setting for lattice plots includes the settings used in plot
(eg, ‘p’ for points and ‘h’ for histogram lines) and further adds regression lines and smooth curves. (Further customization is possible by using the panel
option to pass in a function that takes over how to draw the content of each panel.)
xyplot(Price~Mileage|Model, data=Cars, grid='h',
type=c('p','r'), # 'smooth' for smooth curve
main="Scatterplot by Model Type")
The slopes are very similar, and we can confirm that with a regression: a large shift indicated is indicated by the dummy variable coefficients, but nothing interesting in the way of interactions (which we should check with anova
because the left out group here is the smallest of the three).
regr.1 <- lm(Price~Mileage + Model, data=Cars)
summary(
regr.2 <- lm(Price~Mileage * Model, data=Cars)
)
Call:
lm(formula = Price ~ Mileage * Model, data = Cars)
Residuals:
Min 1Q Median 3Q Max
-10383.9 -2447.3 -171.1 2270.8 18819.5
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3.647e+04 1.478e+03 24.666 < 2e-16 ***
Mileage -2.884e-01 8.640e-02 -3.338 0.000963 ***
Model328 2.666e+03 1.580e+03 1.687 0.092796 .
Model335 1.062e+04 2.148e+03 4.942 1.36e-06 ***
Mileage:Model328 1.688e-02 8.823e-02 0.191 0.848416
Mileage:Model335 -3.836e-02 9.742e-02 -0.394 0.694090
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 3757 on 270 degrees of freedom
Multiple R-squared: 0.5706, Adjusted R-squared: 0.5626
F-statistic: 71.75 on 5 and 270 DF, p-value: < 2.2e-16
You can condition on more than one variable, but don’t get carried away or you won’t see much because of all of the plots. Again, the plot is nicer with Year as categorical. Some of the years are sparse, so let’s limit the analysis to the more common years 2013-2015.
table(Cars$Year)
2011 2012 2013 2014 2015 2016
20 19 154 31 49 3
When conditioned on both year and model, there’s little association between mileage and price for 2015 models because there are so few cases and little variation in mileage. The association is more clear in 2014 and evident in 2013, particularly for the common 328 model. (The fig.width
and fig.height
options control the size of the plot rendered in the Rmd document.)
xyplot(Price~Mileage|Model*Year, data=Cars[Cars$Year %in% 2013:2015,],
main="Scatterplot by Model Type and Model Year")
LS0tCnRpdGxlOiAiRFNJIEV4YW1wbGUiCmF1dGhvcjogQm9iIFN0aW5lCmRhdGU6ICAgTm92ZW1iZXIgMTksIDIwMTcKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBleGFtcGxlIGZpdHMgc2ltcGxlIHJlZ3Jlc3Npb24gbW9kZWxzIHRvIHNtYWxsIGRhdGEgc2V0cy4gIFRoZSBzZWNvbmQgc2hvd3MgbGF0dGljZS90cmVsbGlzIHBsb3RzLgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShjYXIpCmxpYnJhcnkobGF0dGljZSkKYGBgCgpUbyBkZW1vbnN0cmF0ZSB0aGUgZXh0ZW5zaWJpbGl0eSBvZiBSLCBoZXJlJ3MgdHdvIGZ1bmN0aW9ucyB0aGF0IGFsbG93IG1lIHRvIHNob3cgc3R1ZGVudHMgdGhlIGlkZWEgb2YgdmlzdWFsbHkgdGVzdGluZyBmb3IgYXNzb2NpYXRpb24uICBJIHdvdWxkIGhpZGUgdGhlIGRlZmluaXRpb24gb2YgdGhpcyBmdW5jdGlvbiBmcm9tIHN0dWRlbnRzLCBidXQgYmVpbmcgd3JpdHRlbiBpbiBSLCB0aGUgY3VyaW91cyBzdHVkZW50cyBjb3VsZCBhbHdheXMgZmluZCBpdC4KCmBgYHtyfQpwZXJtdXRlIDwtIGZ1bmN0aW9uKHgpIHsgeFtzYW1wbGUuaW50KGxlbmd0aCh4KSldIH0KCnZpc3VhbF90ZXN0X2Zvcl9hc3NvY2lhdGlvbiA8LSBmdW5jdGlvbih4LCB5LCByb3dzPTQsIGNvbHM9NSkgewogICAgcmVzZXQoKSAgIyBjb21wcmVzcyB0aGUgbWFyZ2lucyBvZiBwbG90cyB0byBmaXQgbW9yZSB0b2dldGhlcgogICAgcGFyKG1mcm93PWMocm93cyxjb2xzKSkKICAgIGl4IDwtIHNhbXBsZS5pbnQocm93cywxKQogICAgaXkgPC0gc2FtcGxlLmludChjb2xzLDEpCiAgICBmb3IoaSBpbiAxOnJvd3MpIHsKICAgICAgICBmb3IoaiBpbiAxOmNvbHMpIHsgICMgYWxsIGJ1dCBvbmUgcGxvdCBzaG93cyBwZXJtdXRlZCBpbmRpY2VzCiAgICAgICAgICAgIGlmKCAoaT09aXgpJihqPT1peSkgKSBwbG90KHgseSwgeGxhYj0neCcseWxhYj0neScpCiAgICAgICAgICAgIGVsc2UgcGxvdChwZXJtdXRlKHgpLHkseGxhYj0neCcseWxhYj0neScpCiAgICAgICAgfX19CmBgYAoKLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIAoKIyBFeGFtcGxlIDE6IExvY2F0aW5nIGEgRnJhbmNoaXNlIE91dGxldAoKLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIAoKUmVhZCB0aGUgZGF0YSBpbnRvIGEgZGF0YSBmcmFtZS4gIFRoZSBkYXRhIGZyYW1lIGhhcyA4MCBvYnNlcnZhdGlvbnMgb2YgdHdvIHZhcmlhYmxlcy4KCmBgYHtyfQpGcmFuY2hpc2UgPC0gcmVhZC5jc3YoIkRhdGEvMjFfNG1fZnJhbmNoaXNlLmNzdiIpCmBgYAoKRWFjaCByb3cgb2YgdGhlIGRhdGEgZnJhbWUgZGVzY3JpYmVzIHRoZSBhbW91bnQgb2YgZ2Fzb2xpbmUgc2FsZXMgKGluIHRob3VzYW5kcyBvZiBnYWxsb25zKSBhbmQgdGhlIHRyYWZmaWMgdm9sdW1lLCBhbHNvIGluIHRob3VzYW5kcy4gIEZvciBleGFtcGxlLCBzYWxlcyBpbiB0aGUgZmlyc3Qgd2VlayB3ZXJlIGByIDEwMDAgKiBGcmFuY2hpc2UkU2FsZXNbMV1gIGFuZCBgciAxMDAwICogRnJhbmNoaXNlJFNhbGVzWzJdYCBpbiB0aGUgc2Vjb25kIHdlZWsuICBUcmFmZmljIHZvbHVtZSB3YXMgYHIgMTAwMCAqIEZyYW5jaGlzZSRUcmFmZmljWzFdYCBpbiB0aGUgZmlyc3Qgd2Vlay4KCmBgYHtyfQpGcmFuY2hpc2UKYGBgCgpUaGUgYFZpZXdgIGNvbW1hbmQgb3BlbnMgYSBzcHJlYWRzaGVldCB2aWV3IG9mIHRoZSBkYXRhLiAgWW91IGNhbiBvbmx5IHZpZXcsIG5vdCBjaGFuZ2UsIHRoZSBkYXRhLiBCdXR0b25zIGluIHRoZSBoZWFkZXIgb2YgdGhlIHZpZXcgc3VwcG9ydCBzb3J0aW5nIHRoZSBjb2x1bW5zLgoKYGBge3IgZXZhbD1GQUxTRX0KVmlldyhGcmFuY2hpc2UpCmBgYAoKCgojIyAgTWFyZ2luYWwgUGxvdHMKCkhpc3RvZ3JhbXMgYXJlIGEgZ29vZCBzdGFydGluZyBwb2ludCwgaWRlbnRpZnlpbmcgdGhlIHNoYXBlIG9mIHRoZSBkaXN0cmlidXRpb24gYW5kIG91dGxpZXJzLgoKYGBge3J9Cmhpc3QoRnJhbmNoaXNlJFNhbGVzLCBicmVha3M9MTApCmBgYAoKWW91IGNhbiB0aGVuIGdldCBtb3JlIGNsZXZlciBhbmQgYWRkIGEgYm94cGxvdCB0byB0aGUgZmlndXJlIGlmIHlvdSdkIGxpa2UsIGhlbHBpbmcgdG8gZXhwbGFpbiBib3RoLgoKYGBge3IgZXZhbD1GQUxTRX0KaGlzdChGcmFuY2hpc2UkU2FsZXMsIGJyZWFrcz0xMCkKYm94cGxvdChGcmFuY2hpc2UkU2FsZXMsIGFkZD1UUlVFLCBob3Jpem9udGFsPVRSVUUsIHdpZHRoPTIsIGF0PTYpCmBgYAoKCiMjICBCaXZhcmlhdGUgUGxvdHMKCkEgc2NhdHRlcnBsb3Qgb2Ygc2FsZXMgb24gdHJhZmZpYyB2b2x1bWUgc2hvd3MgdGhhdCB0aGUgdHdvIHZhcmlhYmxlcyBhcmUgbGluZWFybHkgYXNzb2NpYXRlZC4gIFRoZSBhc3NvY2lhdGlvbiBpcyBtb2RlcmF0ZWx5IHN0cm9uZy4gIFRoaXMgY2h1bmsgYWxzbyBhZGRzIHRoZSBmaXR0ZWQgcmVncmVzc2lvbiBsaW5lIHRvIHRoZSBmaWd1cmUuCgpgYGB7cn0KcGxvdChTYWxlcyB+IFRyYWZmaWMsIGRhdGE9RnJhbmNoaXNlKQpgYGAKCk9uY2UgeW91J3ZlIHNlZW4gdGhlIHBsb3QsIGEgcmVncmVzc2lvbiBsaW5lIHNlZW1zIGxpa2UgYSBnb29kIHN1bW1hcnkuICBUaGlzIGlzIGFsc28gYSBnb29kIGNoYW5jZSB0byBzaG93IHRoZSAqdmlzdWFsIHRlc3QgZm9yIGFzc29jaWF0aW9uKgoKYGBge3J9CnZpc3VhbF90ZXN0X2Zvcl9hc3NvY2lhdGlvbihGcmFuY2hpc2UkVHJhZmZpYyxGcmFuY2hpc2UkU2FsZXMsMywzKQpgYGAKCgpgYGB7cn0KcGxvdChTYWxlcyB+IFRyYWZmaWMsIGRhdGE9RnJhbmNoaXNlKQoKcmVnciA8LSBsbShTYWxlcyB+IFRyYWZmaWMsIGRhdGE9RnJhbmNoaXNlKQphYmxpbmUocmVnciwgY29sPSdyZWQnKQpgYGAKClRoZSBzdW1tYXJ5IG9mIGEgcmVncmVzc2lvbiBjcmVhdGVzIGEgbmFtZWQgbGlzdCB0aGF0IGhhcyBwcm9wZXJ0aWVzIG9mIHRoZSByZWdyZXNzaW9uLAoKYGBge3J9CnNSZWdyIDwtIHN1bW1hcnkocmVncikKbmFtZXMoc1JlZ3IpCmBgYAoKVGhlc2UgaW5jbHVkZSB0aGUgaW50ZXJjZXB0LCBzbG9wZSBhbmQgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIHJlZ3Jlc3Npb24uCgpgYGB7cn0Kc1JlZ3IkY29lZmZpY2llbnRzCnNSZWdyJHNpZ21hCmBgYAoKUHJpbnRpbmcgdGhlIHN1bW1hcnkgc2hvd3MgYSB0YWJsZSBvZiB0aGUgbGVhc3Qgc3F1YXJlcyBlc3RpbWF0ZXMgYW5kIHRoZSBvdmVyYWxsIGZpdCBvZiB0aGUgcmVncmVzc2lvbi4KCmBgYHtyfQpzUmVncgpgYGAKCllvdSBjYW4gYWRkIGNvbXBvbmVudHMgZnJvbSB0aGUgbW9kZWwgc3VtbWFyeSwgZW1iZWxsaXNoaW5nIHRoZSBwbG90LgoKYGBge3IgZXZhbD1GQUxTRX0KcGxvdChTYWxlcyB+IFRyYWZmaWMsIGRhdGE9RnJhbmNoaXNlKQoKcmVnciA8LSBsbShTYWxlcyB+IFRyYWZmaWMsIGRhdGE9RnJhbmNoaXNlKQphYmxpbmUocmVnciwgY29sPSdyZWQnKQoKYiA8LSByb3VuZChjb2VmZmljaWVudHMocmVnciksMikKCnRleHQoNDUsNCwgcGFzdGUwKCJGaXQgPSAiLGJbMV0sIisiLGJbMl0sIlRyYWZmaWMiKSwgY29sPSdibHVlJykKYGBgCgpZb3UgY2FuIGFsc28gdXNlIHRoZSBzdW1tYXJ5IG9mIHRoZSByZWdyZXNzaW9uIHRvIGJ1aWxkIHByZWRpY3Rpb24gaW50ZXJ2YWxzLCB0aG91Z2ggbm93IHlvdSBjYW4gc3RhcnQgdG8gc2VlIGhvdyBSIGJlZ2lucyB0byBsb29rIG1vcmUgbGlrZSAicHJvZ3JhbW1pbmciIHJhdGhlciB0aGFuICJzdGF0aXN0aWNzIi4KCmBgYHtyfQpwbG90KFNhbGVzIH4gVHJhZmZpYywgZGF0YT1GcmFuY2hpc2UpCgpyZWdyIDwtIGxtKFNhbGVzIH4gVHJhZmZpYywgZGF0YT1GcmFuY2hpc2UpCmFibGluZShyZWdyLCBjb2w9J3JlZCcpCgp4VmFsdWVzIDwtIGRhdGEuZnJhbWUoVHJhZmZpYyA9IHNlcSgyNSwgNTAsIGxlbmd0aC5vdXQ9MTAwKSkKcHJlZEludCA8LSBwcmVkaWN0KHJlZ3IsIG5ld2RhdGEgPSB4VmFsdWVzLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCgpsaW5lcyh4VmFsdWVzJFRyYWZmaWMsIHByZWRJbnRbLCdsd3InXSwgbHR5PTMsIGNvbD0ncmVkJykKbGluZXMoeFZhbHVlcyRUcmFmZmljLCBwcmVkSW50WywndXByJ10sIGx0eT0zLCBjb2w9J3JlZCcpCmBgYAoKT2YgY291cnNlLCBwYWNrYWdlcyBleGlzdCB0aGF0IGF1dG9tYXRlIHJvdXRpbmUgZmlndXJlcy4KCmBgYHtyIGV2YWw9RkFMU0V9CmdncGxvdChGcmFuY2hpc2UsIGFlcyh4PVRyYWZmaWMsIHk9U2FsZXMpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScpCmBgYAoKQmVmb3JlIHJlbHlpbmcgb24gdGhlIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgaW5mZXJlbmNlLCBjaGVjayB0aGF0IHRoZSByZXNpZHVhbHMgZG8gbm90IHNob3cgYW4gZXZpZGVudCBwYXR0ZXJuLgoKYGBge3J9CnBsb3QoRnJhbmNoaXNlJFRyYWZmaWMsIHJlc2lkdWFscyhyZWdyKSkKYWJsaW5lKGg9MCwgY29sPSdncmF5JykKYGBgCgpJbiBhZGRpdGlvbiBjaGVjayB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHJlc2lkdWFscyBhcHBlYXJzIG5lYXJseSBub3JtYWwuIFRvIGdldCB0aGUgYmFuZHMsIHVzZSB0aGUgdmVyc2lvbiBvZiB0aGUgbm9ybWFsIHF1YW50aWxlIHBsb3QgZnJvbSB0aGUgYGNhcmAgcGFja2FnZSAoc2VlIENoYXB0ZXIgMTIpLgoKYGBge3J9CnBhcihtZnJvdz1jKDEsMikpCiAgcXFub3JtKHJlc2lkdWFscyhyZWdyKSkKICBjYXI6OnFxUGxvdChyZXNpZHVhbHMocmVnciksIG1haW49IlEtUSBQbG90IikKYGBgCgpBdCB0aGUgZXh0cmVtZXMsIHRoZSByZXNpZHVhbHMgc2VlbSB0byBiZSBhIGJpdCAiZmF0LXRhaWxlZCIsIG1vcmUgZXh0cmVtZSB0aGF0IHdoYXQgbm9ybWFsaXR5IHdvdWxkIGV4cGVjdC4gIFRoZSBkZXZpYXRpb24gaXMgc2xpZ2h0LCBidXQgd29ydGggbm90aW5nLgoKCi0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAKCiMgRXhhbXBsZSAjMjogIFByaWNpbmcgVXNlZCBDYXJzCgotIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gCgpUaGUgZm9sbG93aW5nIGRhdGEgZGVzY3JpYmUgMjc2ICJjZXJ0aWZpZWQiIHVzZWQgQk1XIHNlZGFucy4KCmBgYHtyfQojIENhcnMgPC0gcmVhZC5jc3YoImh0dHA6Ly93d3ctc3RhdC53aGFydG9uLnVwZW5uLmVkdS9+c3RpbmUvc3RhdDQwNS9ibXdfMjAxNi5jc3YiKQpDYXJzIDwtIHJlYWQuY3N2KCJibXdfMjAxNi5jc3YiKQpkaW0oQ2FycykKYGBgCgpUaGUgZGF0YSBpbmNsdWRlIHRoZSBtb2RlbCB5ZWFyLCBwcmljZSAoaW4gZG9sbGFycyksIG1pbGVhZ2UsIG1vZGVsLCBtb2RlbCBzdHlsZSwgY29sb3IsIGFuZCB0eXBlIG9mIHRyYW5zbWlzc2lvbiAoYXV0byBvciBtYW51YWwpLgoKYGBge3J9CkNhcnMKYGBgCgpUaGlzIGFuYWx5c2lzIGNvbmNlcm5zIHRoZSBhZmZlY3Qgb2YgdGhlIG1vZGVsIHR5cGUgb24gcHJpY2UuICBCZWZvcmUgY29udGludWluZywgY29udmVydCB0aGUgbW9kZWwgbnVtYmVyIHRvIGEgYGZhY3RvcmAgKFIncyB2ZXJzaW9uIG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUpIHJhdGhlciB0aGFuIGxlYXZlIGFzIGEgbnVtYmVyLiAgU2FtZSBmb3IgdGhlIG1vZGVsIHllYXIuICBUaGlzIGNvbnZlcnNpb24gYXZvaWRzIHRyZWF0aW5nIHRoZXNlIGRhdGEgYXMgbnVtYmVycyByYXRoZXIgdGhhbiBpZGVudGlmeWluZyBncm91cHMuCgpgYGB7cn0KQ2FycyRNb2RlbCA8LSBhcy5mYWN0b3IoQ2FycyRNb2RlbCkKCkNhcnMkQWdlIDwtIENhcnMkWWVhciAtIDIwMTYgICAgICAgIyBudW1lcmljYWwKQ2FycyRZZWFyIDwtIGFzLmZhY3RvcihDYXJzJFllYXIpCmBgYAoKSGVyZSdzIHRoZSBzdGFuZGFyZCBjb21wYXJpc29uIGJveHBsb3QgZGlzcGxheS4KCmBgYHtyfQpib3hwbG90KFByaWNlIH4gTW9kZWwsIGRhdGEgPSBDYXJzKQpgYGAKCmBsYXR0aWNlYCBvZmZlcnMgYSBzaW1pbGFyIHBsb3QuCgpgYGB7cn0KbGF0dGljZTo6YndwbG90KFByaWNlIH4gTW9kZWwsIGRhdGEgPSBDYXJzKQpgYGAKCmBsYXR0aWNlYCBwcm9kdWNlcyBtYXJnaW5hbCBwbG90cyB0aGF0IGFyZSBwZXJoYXBzIG1vcmUgYXR0cmFjdGl2ZSAtLSBhbmQgZWFzaWVyIHRvIGNvbnN0cnVjdCAtLSB0aGFuIGlmIGRvbmUgaW4gUi4gIEZvciBleGFtcGxlLCBoZXJlIGFyZSBrZXJuZWwgZGVuc2l0eSBwbG90cyBvZiB0aGUgcHJpY2VzIG9mIHRoZSAzIGRpZmZlcmVudCBtb2RlbHMgKGxpc3RlZCBwcmljZSwgaW4gZG9sbGFycykuICBJIHByZWZlciB0byBoYXZlIHRoZW0gIm9uIHRvcCBvZiBlYWNoIG90aGVyIiB0byBoZWxwIHdpdGggdGhlIGNvbXBhcmlzb24sIGJ1dCB0aGUgY29tbW9uIHNjYWxlcyBhcmUgaGVscGZ1bC4gIGBsYXR0aWNlYCB1c2VzIHN0YXRpc3RpY2FsIG5vdGF0aW9uIGZvciBjb25kaXRpb25hbCBhc3NvY2lhdGlvbiwgd2l0aCBhIHZlcnRpY2FsIGJhciBpbmRpY2F0aW5nIGNvbmRpdGlvbmFsIHJlbGF0aW9uc2hpcHMsIGluIHRoaXMgY2FzZSBzaG93aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgcHJpY2VzIGNvbmRpdGlvbmFsIG9uIGVhY2ggb2YgdGhlIDMgbW9kZWwgdHlwZXMuCgpgYGB7cn0KbGF0dGljZTo6ZGVuc2l0eXBsb3QofiBQcmljZSB8IE1vZGVsLCBkYXRhPUNhcnMpCmBgYAoKQm94cGxvdHMgbGlrZSB0aGUgZm9sbG93aW5nIGNvbnZleSBhIHNlbnNlIG9mIHRoZSBnZW5lcmFsIGRlcHJlY2lhdGlvbiB3aXRoaW4gZWFjaCBtb2RlbCB0eXBlLiAgR3JpZCBsaW5lcyB3b3VsZCBoZWxwIHRvIGFsaWduIHRoZSBkYXRhIGFzIHdlbGwsIGJ1dCB0aGUgb3B0aW9uIGRvZXMgbm90IHJ1biBpbiB0aGlzIHBsb3QuCgpgYGB7cn0KYndwbG90KFByaWNlIH4gWWVhciB8IE1vZGVsLCBkYXRhPUNhcnMpIApgYGAKCmBzdHJpcHBsb3RgIHNob3dzIHRoZSBkYXRhIHdpdGhvdXQgdGhlIGJveGVzLiAgV2l0aCBvdmVycHJpbnRpbmcgYXMgaW4gdGhpcyBleGFtcGxlLCB5b3Ugd2FudCB0aGUgZGl0aGVyaW5nIG9wdGlvbiAoImppdHRlciIgaW4gbGF0dGljZSkgdHVybmVkIG9uLiBUaGlzIG9wdGlvbiBhZGRzIGEgc21hbGwgYW1vdW50IG9mIHJhbmRvbSB2YXJpYXRpb24gdG8gYXZvaWQgb3ZlcnByaW50aW5nLiAgR3JpZGxpbmVzIHdvcmsgaGVyZSB2ZXJ5IHdlbGwuCgpgYGB7cn0Kc3RyaXBwbG90KFByaWNlIH4gWWVhciB8IE1vZGVsLCBkYXRhPUNhcnMsIGppdHRlci5kYXRhPVRSVUUsIGdyaWQ9J2gnKQpgYGAKCkxhdHRpY2UgcGxvdHMgYXJlIGhlbHBmdWwgd2hlbiBsb29raW5nIGF0IHRoZSBlZmZlY3Qgb2YgImx1cmtpbmciIHZhcmlhYmxlcyBvbiBiaXZhcmlhdGUgYXNzb2NpYXRpb24uICBGb3IgZXhhbXBsZSwgaGVyZSdzIGEgInJlZ3VsYXIgUiIgcGxvdCBvZiBwcmljZSBvbiBtaWxlYWdlLiAgVG8gc2hvdyB0aGF0IHRoaXMgcmVsYXRpb25zaGlwIGRlcGVuZHMgdXBvbiB0aGUgbW9kZWwgdHlwZSwgSSd2ZSBjb2xvcmVkIHBvaW50cyBieSB0aGUgbW9kZWwgdHlwZS4gIFlvdSBjYW4gc2VlIHdoYXQncyBoYXBwZW5pbmcgKG1heWJlKSwgYnV0IGl0J3Mgc3VidGxlLgoKYGBge3J9CnBsb3QoUHJpY2UgfiBNaWxlYWdlLCBkYXRhPUNhcnMsIGNvbD1DYXJzJE1vZGVsKQpgYGAKClJhdGhlciB0aGFuIGRyb3AgdGhlIGRhdGEgaW50byBhIHJlZ3Jlc3Npb24sIHdlIGNhbiBleHBsb3JlIGNvbmRpdGlvbmFsIGFzc29jaWF0aW9ucyBncmFwaGljYWxseSB1c2luZyBgbGF0dGljZWAuICBZb3UgY2FuIGd1ZXNzIHdoYXQgdGhlIGZvbGxvd2luZyBwbG90IGRvZXMuCgpgYGB7cn0KeHlwbG90KFByaWNlIH4gTWlsZWFnZSB8IE1vZGVsLCBkYXRhPUNhcnMsIGdyaWQ9J2gnLAogICAgICAgbWFpbj0iU2NhdHRlcnBsb3QgYnkgTW9kZWwgVHlwZSIpCmBgYAoKQmVjYXVzZSBvZiB0aGUgY29tbW9uIHNjYWxpbmcgdXNlZCBmb3IgdGhlIDMgZnJhbWVzIG9mIHRoZSBwbG90LCB3ZSBjYW4gc2VlIHRoYXQgdGhlIHNsb3BlcyBsb29rIHZlcnkgc2ltaWxhciAoaWUsIG5vIGludGVyYWN0aW9uKSwgYnV0IHRoZSBsZXZlbCBpcyBoaWdoZXIgZm9yIHRoZSAzMzUgbW9kZWxzIHRoYXQgdGhlIG90aGVyIHR3by4gIAoKWW91IGNhbiBhZGQgbW9yZSB0byBlYWNoIHBhbmVsLiAgVGhlIG9wdGlvbmFsIGB0eXBlYCBzZXR0aW5nIGZvciBsYXR0aWNlIHBsb3RzIGluY2x1ZGVzIHRoZSBzZXR0aW5ncyB1c2VkIGluIGBwbG90YCAoZWcsICdwJyBmb3IgcG9pbnRzIGFuZCAnaCcgZm9yIGhpc3RvZ3JhbSBsaW5lcykgYW5kIGZ1cnRoZXIgYWRkcyByZWdyZXNzaW9uIGxpbmVzIGFuZCBzbW9vdGggY3VydmVzLiAgKEZ1cnRoZXIgY3VzdG9taXphdGlvbiBpcyBwb3NzaWJsZSBieSB1c2luZyB0aGUgYHBhbmVsYCBvcHRpb24gdG8gcGFzcyBpbiBhIGZ1bmN0aW9uIHRoYXQgdGFrZXMgb3ZlciBob3cgdG8gZHJhdyB0aGUgY29udGVudCBvZiBlYWNoIHBhbmVsLikKCmBgYHtyfQp4eXBsb3QoUHJpY2V+TWlsZWFnZXxNb2RlbCwgZGF0YT1DYXJzLCBncmlkPSdoJywKICAgICAgIHR5cGU9YygncCcsJ3InKSwgICMgJ3Ntb290aCcgZm9yIHNtb290aCBjdXJ2ZQogICAgICAgbWFpbj0iU2NhdHRlcnBsb3QgYnkgTW9kZWwgVHlwZSIpCmBgYAoKVGhlIHNsb3BlcyBhcmUgdmVyeSBzaW1pbGFyLCBhbmQgd2UgY2FuIGNvbmZpcm0gdGhhdCB3aXRoIGEgcmVncmVzc2lvbjogYSBsYXJnZSBzaGlmdCBpbmRpY2F0ZWQgaXMgaW5kaWNhdGVkIGJ5IHRoZSBkdW1teSB2YXJpYWJsZSBjb2VmZmljaWVudHMsIGJ1dCBub3RoaW5nIGludGVyZXN0aW5nIGluIHRoZSB3YXkgb2YgaW50ZXJhY3Rpb25zICh3aGljaCB3ZSBzaG91bGQgY2hlY2sgd2l0aCBgYW5vdmFgIGJlY2F1c2UgdGhlIGxlZnQgb3V0IGdyb3VwIGhlcmUgaXMgdGhlIHNtYWxsZXN0IG9mIHRoZSB0aHJlZSkuCgpgYGB7cn0KcmVnci4xIDwtICAgICBsbShQcmljZX5NaWxlYWdlICsgTW9kZWwsIGRhdGE9Q2FycykKc3VtbWFyeSgKICAgIHJlZ3IuMiA8LSBsbShQcmljZX5NaWxlYWdlICogTW9kZWwsIGRhdGE9Q2FycykKICAgICkKYGBgCgpZb3UgY2FuIGNvbmRpdGlvbiBvbiBtb3JlIHRoYW4gb25lIHZhcmlhYmxlLCBidXQgZG9uJ3QgZ2V0IGNhcnJpZWQgYXdheSBvciB5b3Ugd29uJ3Qgc2VlIG11Y2ggYmVjYXVzZSBvZiBhbGwgb2YgdGhlIHBsb3RzLiAgQWdhaW4sIHRoZSBwbG90IGlzIG5pY2VyIHdpdGggWWVhciBhcyBjYXRlZ29yaWNhbC4gU29tZSBvZiB0aGUgeWVhcnMgYXJlIHNwYXJzZSwgc28gbGV0J3MgbGltaXQgdGhlIGFuYWx5c2lzIHRvIHRoZSBtb3JlIGNvbW1vbiB5ZWFycyAyMDEzLTIwMTUuCgpgYGB7cn0KdGFibGUoQ2FycyRZZWFyKQpgYGAKCldoZW4gY29uZGl0aW9uZWQgb24gYm90aCB5ZWFyIGFuZCBtb2RlbCwgdGhlcmUncyBsaXR0bGUgYXNzb2NpYXRpb24gYmV0d2VlbiBtaWxlYWdlIGFuZCBwcmljZSBmb3IgMjAxNSBtb2RlbHMgYmVjYXVzZSB0aGVyZSBhcmUgc28gZmV3IGNhc2VzIGFuZCBsaXR0bGUgdmFyaWF0aW9uIGluIG1pbGVhZ2UuIFRoZSBhc3NvY2lhdGlvbiBpcyBtb3JlIGNsZWFyIGluIDIwMTQgYW5kIGV2aWRlbnQgaW4gMjAxMywgcGFydGljdWxhcmx5IGZvciB0aGUgY29tbW9uIDMyOCBtb2RlbC4gKFRoZSBgZmlnLndpZHRoYCBhbmQgYGZpZy5oZWlnaHRgIG9wdGlvbnMgY29udHJvbCB0aGUgc2l6ZSBvZiB0aGUgcGxvdCByZW5kZXJlZCBpbiB0aGUgUm1kIGRvY3VtZW50LikKCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD02fQp4eXBsb3QoUHJpY2V+TWlsZWFnZXxNb2RlbCpZZWFyLCBkYXRhPUNhcnNbQ2FycyRZZWFyICVpbiUgMjAxMzoyMDE1LF0sCiAgICAgICBtYWluPSJTY2F0dGVycGxvdCBieSBNb2RlbCBUeXBlIGFuZCBNb2RlbCBZZWFyIikKYGBgCgotIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0gLSAtIAoK