This lecture will cover prediction for (Simple) Linear
Regression.
Prediction from Regression Models
Simple linear regression models can be used to predict the response
at any given value of the predictor.
Nonetheless, we should beware predicting far beyond the range of the
data.
Point predictions should be accompanied by corresponding prediction
intervals, providing a range of plausible values.
The Basics of Prediction
Suppose that we want to predict the response when the predictor
x takes the specific value x0, a value not
necessarily seen in the observed data. N.B. The use of the zero in the
subscript relates to the specific observation; you might ignore it for
clarity.
We denote the prediction by \(\hat{y_0}\), called “y hat”. The subscript
links this prediction to the specific x value denoted
x0. If we were doing this prediction for the
observed data, we could use the subscript i in these
expressions.
Our model for the corresponding response, y0,
is
\[Y_0 = \mu_0 + \varepsilon_0\]
where \[\mu_0 = \beta_0 + \beta_1
x_0\]
It is natural to estimate \(\mu_0\)
by
\[\hat \mu_0 = \hat \beta_0 + \hat \beta_1
x_0\]
We have no information about \(\varepsilon_0\) so it is estimated by its
expected value. i.e. we define \(\hat
\varepsilon_0 = 0\) as the estimate of \(\varepsilon_0\).
Then \[\begin{aligned}
\hat y_0 &=& \hat \mu_0 + \hat \varepsilon_0\\
&=& \hat \beta_0 + \hat \beta_1
x_0\end{aligned}\]
This should be fairly intuitive — we just read prediction from fitted
regression line.
Prediction Versus Estimation of Mean Response
The prediction was \(\hat y_0 = \hat
\mu_0\)
But \(\hat \mu_0\) is also the
natural estimate of \(\mu_0 =
E[Y_0]\).
Hence prediction is the same as the estimate of the mean
response.
However, differences arise if we want to construct corresponding
interval estimates.
Confidence Interval for the Mean Response
The standard deviation of the sampling distribution of \(\hat \mu_0\) is
\[\sigma_{\hat\mu_0} = \sigma \sqrt{
\frac1n + \frac{ (x_0 - \bar x)^2}
{s_{xx}} }\]
The standard error for \(\hat
\mu_0\) is
\[SE(\hat \mu_0) = s \sqrt{ \frac1n +
\frac{(x_0 - \bar x)^2}
{s_{xx}} } \; .\]
It can be shown that the sampling distribution of \(\hat \mu_0\) is defined by
\[\frac{ \hat \mu_0 - \mu_0 }{SE(\hat
\mu_0)} \sim
t(n-2)\]
Hence a \(100(1-\alpha)\%\)
confidence interval for \(\mu_0\) is
given by
\[\left ( \hat \mu_0 - t_{\alpha/2}
SE(\hat \mu_0), \;
\hat \mu_0 + t_{\alpha/2} SE(\hat \mu_0) \right )\]
where \(t_{\alpha/2}\) is a critical
value from the t distribution with df=n-2.
This confidence interval provides a range of plausible values for the
mean response \(\hat \mu_0\) and the
interval gets narrower as we gather larger samples. In practice we will
not need to use these formulas: R will produce the confidence intervals
for us.
Prediction Intervals
When it comes to finding the prediction error for
y0, we must take into account the additional
variation among individuals expressed by the \(\varepsilon_0\) term:
\[\mbox{var}(\hat{y_0}) =
\mbox{var}(\hat{\mu_0}) + \mbox{var}(\varepsilon_0)
= [SE(\hat{\mu_0})]^2 + \sigma^2\]
Replacing \(\sigma^2\) by its
estimate s2, we get the prediction error of \(\hat y_0\) as
\[PE(\hat y_0) = s \sqrt{ \frac1n +
\frac{(x_0 - \bar{x})^2}{s_{xx}
}+1} \; .\]
A \(100(1-\alpha)\%\) prediction
interval for y0 is then
\[\left ( \hat{y_0} - t_{\alpha/2}
PE(\hat{y_0}), \; \hat{y_0} +
t_{\alpha/2} PE(\hat y_0) \right )\]
As the name suggests, the probability that y0
will actually lie in this prediction interval is \(1-\alpha\).
Again, we will not need to calculate these intervals by hand: R will
do it for us. To get the predicted values we set \(x_0\) (which can be more than one value) in
an object called a data.frame
, then invoke the
predict()
function based on an existing linear model.
Boiling Point Data
We will use data on boiling point in degrees Fahrenheit (y)
and pressure in inches of mercury (x), collected during an
expedition in the Alps. Data source: A Handbook of Small Data Sets by
Hand, Daly, Lunn, McConway and Ostrowski; also known as
forbes
in the `MASS package.
Prediction for Boiling Point Data in R
Download boiling.csv
## Boil <- read.csv(file = "https://r-resources.massey.ac.nz/data/161251/boiling.csv",
## header = TRUE)
Boil.lm <- lm(BPt ~ Pressure, data = Boil)
summary(Boil.lm)
Call:
lm(formula = BPt ~ Pressure, data = Boil)
Residuals:
Min 1Q Median 3Q Max
-1.22687 -0.22178 0.07723 0.19687 0.51001
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 155.29648 0.92734 167.47 <2e-16 ***
Pressure 1.90178 0.03676 51.74 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.444 on 15 degrees of freedom
Multiple R-squared: 0.9944, Adjusted R-squared: 0.9941
F-statistic: 2677 on 1 and 15 DF, p-value: < 2.2e-16
x0 <- data.frame(Pressure = c(27, 32))
predict(Boil.lm, newdata = x0, interval = "prediction", level = 0.95)
fit lwr upr
1 206.6446 205.6590 207.6303
2 216.1536 215.0382 217.2690
We can use the predict()
function (with one small
change) to obtain the confidence intervals for the mean response at the
same values of the predictor using:
predict(Boil.lm, newdata = x0, interval = "confidence", level = 0.95)
fit lwr upr
1 206.6446 206.3693 206.9200
2 216.1536 215.5633 216.7438
Fitted Line Plot for Boiling Point Data
ggplot(Boil, mapping = aes(y = BPt, x = Pressure)) + geom_point() + geom_smooth(method = "lm",
se = FALSE) + ylab("Boiling Point") + xlab("Pressure")
`geom_smooth()` using formula = 'y ~ x'
The model fit looks good so we should expect fairly tight prediction
intervals.
Conclusions for Boiling Point Data
Fitted model is \[E[\mbox{BPt}] = 155.3 +
1.902~\mbox{Pressure}\]
Pressure explains over 99% of the variation in boiling point.
When pressure is 27 inches of mercury, predicted temperature is 206.6
degrees Fahrenheit with 95% prediction interval (205.7, 207.6).
When pressure is 32 inches of mercury, prediction is 216.2 with 95%
prediction interval (215.0, 217.3).
Test your understanding
Use the model above to prove to yourself that:
- The prediction interval is wider than the confidence interval for a
given x0.
- the intervals get wider the farther x0 gets from
\(\bar{x}\).
- the intervals get wider when the level of confidence for the
interval increases.
x0 <- data.frame(Pressure = c(27:32))
predict(Boil.lm, newdata = x0, interval = "prediction", level = 0.95)
fit lwr upr
1 206.6446 205.6590 207.6303
2 208.5464 207.5457 209.5472
3 210.4482 209.4266 211.4698
4 212.3500 211.3020 213.3980
5 214.2518 213.1724 215.3312
6 216.1536 215.0382 217.2690
predict(Boil.lm, newdata = x0, interval = "confidence", level = 0.95)
fit lwr upr
1 206.6446 206.3693 206.9200
2 208.5464 208.2212 208.8717
3 210.4482 210.0635 210.8329
4 212.3500 211.8999 212.8000
5 214.2518 213.7328 214.7707
6 216.1536 215.5633 216.7438
predict(Boil.lm, newdata = x0, interval = "confidence", level = 0.99)
fit lwr upr
1 206.6446 206.2640 207.0253
2 208.5464 208.0968 208.9961
3 210.4482 209.9163 210.9801
4 212.3500 211.7278 212.9722
5 214.2518 213.5343 214.9693
6 216.1536 215.3375 216.9696
LS0tDQp0aXRsZTogIkxlY3R1cmUgODogUHJlZGljdGlvbiBpbiBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24iDQpzdWJ0aXRsZTogMTYxLjI1MSBSZWdyZXNzaW9uIE1vZGVsbGluZw0KYXV0aG9yOiAiUHJlc2VudGVkIGJ5IE1hdHRoZXcgUGF3bGV5IDxNLlBhd2xleUBtYXNzZXkuYWMubno+IiAgDQpkYXRlOiAiV2VlayAzIG9mIFNlbWVzdGVyIDIsIGByIGx1YnJpZGF0ZTo6eWVhcihsdWJyaWRhdGU6Om5vdygpKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgaW9zbGlkZXNfcHJlc2VudGF0aW9uOg0KICAgIHdpZGVzY3JlZW46IHRydWUNCiAgICBzbWFsbGVyOiB0cnVlDQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgc2xpZHlfcHJlc2VudGF0aW9uOiANCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KDQoNCg0KPCEtLS0gRGF0YSBpcyBvbg0KaHR0cHM6Ly9yLXJlc291cmNlcy5tYXNzZXkuYWMubnovZGF0YS8xNjEyNTEvDQotLS0+DQoNCmBgYHtyIHNldHVwLCBwdXJsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCm9wdHNfY2h1bmskc2V0KGRldj1jKCJwbmciLCAicGRmIikpDQpvcHRzX2NodW5rJHNldChmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD03LCBmaWcucGF0aD0iRmlndXJlcy8iLCBmaWcuYWx0PSJ1bmxhYmVsbGVkIikNCm9wdHNfY2h1bmskc2V0KGNvbW1lbnQ9IiIsIGZpZy5hbGlnbj0iY2VudGVyIiwgdGlkeT1UUlVFKQ0Kb3B0aW9ucyhrbml0ci5rYWJsZS5OQSA9ICcnKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGJyb29tKQ0KYGBgDQoNCg0KPCEtLS0gRG8gbm90IGVkaXQgYW55dGhpbmcgYWJvdmUgdGhpcyBsaW5lLiAtLS0+DQoNCg0KDQoNClRoaXMgbGVjdHVyZSB3aWxsIGNvdmVyIHByZWRpY3Rpb24gZm9yIChTaW1wbGUpIExpbmVhciBSZWdyZXNzaW9uLg0KDQojIyBQcmVkaWN0aW9uIGZyb20gUmVncmVzc2lvbiBNb2RlbHMNCg0KU2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyBjYW4gYmUgdXNlZCB0byBwcmVkaWN0IHRoZSByZXNwb25zZSBhdCBhbnkgZ2l2ZW4gdmFsdWUgb2YgdGhlIHByZWRpY3Rvci4NCg0KTm9uZXRoZWxlc3MsIHdlIHNob3VsZCBiZXdhcmUgcHJlZGljdGluZyBmYXIgYmV5b25kIHRoZSByYW5nZSBvZiB0aGUgZGF0YS4NCg0KUG9pbnQgcHJlZGljdGlvbnMgc2hvdWxkIGJlIGFjY29tcGFuaWVkIGJ5IGNvcnJlc3BvbmRpbmcgcHJlZGljdGlvbiBpbnRlcnZhbHMsIHByb3ZpZGluZyBhIHJhbmdlIG9mIHBsYXVzaWJsZSB2YWx1ZXMuDQoNCiMjIFRoZSBCYXNpY3Mgb2YgUHJlZGljdGlvbg0KDQpTdXBwb3NlIHRoYXQgd2Ugd2FudCB0byBwcmVkaWN0IHRoZSByZXNwb25zZSB3aGVuIHRoZSBwcmVkaWN0b3IgKngqIHRha2VzIHRoZSBzcGVjaWZpYyB2YWx1ZSAqeH4wfiosIGEgdmFsdWUgbm90IG5lY2Vzc2FyaWx5IHNlZW4gaW4gdGhlIG9ic2VydmVkIGRhdGEuIE4uQi4gVGhlIHVzZSBvZiB0aGUgemVybyBpbiB0aGUgc3Vic2NyaXB0IHJlbGF0ZXMgdG8gdGhlIHNwZWNpZmljIG9ic2VydmF0aW9uOyB5b3UgbWlnaHQgaWdub3JlIGl0IGZvciBjbGFyaXR5Lg0KDQpXZSBkZW5vdGUgdGhlIHByZWRpY3Rpb24gYnkgJFxoYXR7eV8wfSQsIGNhbGxlZCAieSBoYXQiLiBUaGUgc3Vic2NyaXB0IGxpbmtzIHRoaXMgcHJlZGljdGlvbiB0byB0aGUgc3BlY2lmaWMgKngqIHZhbHVlIGRlbm90ZWQgKnh+MH4qLiBJZiB3ZSB3ZXJlIGRvaW5nIHRoaXMgcHJlZGljdGlvbiBmb3IgdGhlIG9ic2VydmVkIGRhdGEsIHdlIGNvdWxkIHVzZSB0aGUgc3Vic2NyaXB0ICppKiBpbiB0aGVzZSBleHByZXNzaW9ucy4NCg0KT3VyIG1vZGVsIGZvciB0aGUgY29ycmVzcG9uZGluZyByZXNwb25zZSwgKnl+MH4qLCBpcw0KDQoNCiQkWV8wID0gXG11XzAgKyBcdmFyZXBzaWxvbl8wJCQgd2hlcmUNCiAgICAkJFxtdV8wID0gXGJldGFfMCArIFxiZXRhXzEgeF8wJCQNCg0KSXQgaXMgbmF0dXJhbCB0byBlc3RpbWF0ZSAkXG11XzAkIGJ5DQoNCiQkXGhhdCBcbXVfMCA9IFxoYXQgXGJldGFfMCArIFxoYXQgXGJldGFfMSB4XzAkJA0KDQoNCldlIGhhdmUgbm8gaW5mb3JtYXRpb24gYWJvdXQgJFx2YXJlcHNpbG9uXzAkIHNvIGl0IGlzIGVzdGltYXRlZCBieSBpdHMgZXhwZWN0ZWQgdmFsdWUuIGkuZS4gd2UgZGVmaW5lICRcaGF0IFx2YXJlcHNpbG9uXzAgPSAwJCBhcyB0aGUgZXN0aW1hdGUgb2YgJFx2YXJlcHNpbG9uXzAkLg0KDQpUaGVuICQkXGJlZ2lue2FsaWduZWR9DQogICAgXGhhdCB5XzAgJj0mIFxoYXQgXG11XzAgKyBcaGF0IFx2YXJlcHNpbG9uXzBcXA0KICAgICAmPSYgXGhhdCBcYmV0YV8wICsgXGhhdCBcYmV0YV8xIHhfMFxlbmR7YWxpZ25lZH0kJA0KDQpUaGlzIHNob3VsZCBiZSBmYWlybHkgaW50dWl0aXZlIC0tLSB3ZSBqdXN0IHJlYWQgcHJlZGljdGlvbiBmcm9tIGZpdHRlZCByZWdyZXNzaW9uIGxpbmUuDQoNCiMjIFByZWRpY3Rpb24gVmVyc3VzIEVzdGltYXRpb24gb2YgTWVhbiBSZXNwb25zZQ0KDQpUaGUgcHJlZGljdGlvbiB3YXMgJFxoYXQgeV8wID0gXGhhdCBcbXVfMCQNCg0KQnV0ICRcaGF0IFxtdV8wJCBpcyBhbHNvIHRoZSBuYXR1cmFsIGVzdGltYXRlIG9mICRcbXVfMCA9IEVbWV8wXSQuDQoNCkhlbmNlIHByZWRpY3Rpb24gaXMgdGhlIHNhbWUgYXMgdGhlIGVzdGltYXRlIG9mIHRoZSBtZWFuIHJlc3BvbnNlLg0KDQpIb3dldmVyLCBkaWZmZXJlbmNlcyBhcmlzZSBpZiB3ZSB3YW50IHRvIGNvbnN0cnVjdCBjb3JyZXNwb25kaW5nIGludGVydmFsIGVzdGltYXRlcy4NCg0KIyMgQ29uZmlkZW5jZSBJbnRlcnZhbCBmb3IgdGhlIE1lYW4gUmVzcG9uc2UNCg0KVGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mICRcaGF0IFxtdV8wJCBpcw0KDQokJFxzaWdtYV97XGhhdFxtdV8wfSA9IFxzaWdtYSBcc3FydHsgXGZyYWMxbiArIFxmcmFjeyAoeF8wIC0gXGJhciB4KV4yfQ0KICAgIHtzX3t4eH19IH0kJA0KDQpUaGUgc3RhbmRhcmQgZXJyb3IgZm9yICRcaGF0IFxtdV8wJCBpcw0KDQokJFNFKFxoYXQgXG11XzApID0gcyBcc3FydHsgXGZyYWMxbiArIFxmcmFjeyh4XzAgLSBcYmFyIHgpXjJ9DQogICAge3Nfe3h4fX0gfSBcOyAuJCQNCg0KSXQgY2FuIGJlIHNob3duIHRoYXQgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiAkXGhhdCBcbXVfMCQgaXMgZGVmaW5lZCBieSANCg0KJCRcZnJhY3sgXGhhdCBcbXVfMCAtIFxtdV8wIH17U0UoXGhhdCBcbXVfMCl9IFxzaW0NCiAgICB0KG4tMikkJA0KDQpIZW5jZSBhICQxMDAoMS1cYWxwaGEpXCUkIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yICRcbXVfMCQgaXMgZ2l2ZW4gYnkgDQoNCiQkXGxlZnQgKCBcaGF0IFxtdV8wIC0gdF97XGFscGhhLzJ9IFNFKFxoYXQgXG11XzApLCBcOyANCiAgICBcaGF0IFxtdV8wICsgdF97XGFscGhhLzJ9IFNFKFxoYXQgXG11XzApIFxyaWdodCApJCQgDQoNCndoZXJlICR0X3tcYWxwaGEvMn0kIGlzIGEgY3JpdGljYWwgdmFsdWUgZnJvbSB0aGUgKnQqIGRpc3RyaWJ1dGlvbiB3aXRoICpkZj1uLTIqLg0KDQpUaGlzIGNvbmZpZGVuY2UgaW50ZXJ2YWwgcHJvdmlkZXMgYSByYW5nZSBvZiBwbGF1c2libGUgdmFsdWVzIGZvciAgdGhlIG1lYW4gcmVzcG9uc2UgJFxoYXQgXG11XzAkIGFuZCB0aGUgaW50ZXJ2YWwgZ2V0cyBuYXJyb3dlciBhcyB3ZSBnYXRoZXIgbGFyZ2VyIHNhbXBsZXMuICAgIEluIHByYWN0aWNlIHdlIHdpbGwgbm90IG5lZWQgdG8gdXNlIHRoZXNlIGZvcm11bGFzOiAgUiB3aWxsIHByb2R1Y2UgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGZvciB1cy4gDQoNCiMjIFByZWRpY3Rpb24gSW50ZXJ2YWxzDQoNCldoZW4gaXQgY29tZXMgdG8gZmluZGluZyB0aGUgKipwcmVkaWN0aW9uIGVycm9yKiogZm9yICp5fjB+Kiwgd2UgbXVzdCB0YWtlIGludG8gYWNjb3VudCB0aGUgKmFkZGl0aW9uYWwgdmFyaWF0aW9uKiBhbW9uZyBpbmRpdmlkdWFscyBleHByZXNzZWQgYnkgdGhlICRcdmFyZXBzaWxvbl8wJCB0ZXJtOg0KDQokJFxtYm94e3Zhcn0oXGhhdHt5XzB9KSA9IFxtYm94e3Zhcn0oXGhhdHtcbXVfMH0pICsgXG1ib3h7dmFyfShcdmFyZXBzaWxvbl8wKSANCiAgICA9IFtTRShcaGF0e1xtdV8wfSldXjIgKyBcc2lnbWFeMiQkDQoNClJlcGxhY2luZyAkXHNpZ21hXjIkIGJ5IGl0cyBlc3RpbWF0ZSAqc14yXiosIHdlIGdldCB0aGUgcHJlZGljdGlvbiBlcnJvciBvZiAkXGhhdCB5XzAkIGFzDQoNCiQkUEUoXGhhdCB5XzApID0gcyBcc3FydHsgXGZyYWMxbiArIFxmcmFjeyh4XzAgLSBcYmFye3h9KV4yfXtzX3t4eH0gDQogICAgfSsxfSBcOyAuJCQNCg0KQSAkMTAwKDEtXGFscGhhKVwlJCBwcmVkaWN0aW9uIGludGVydmFsIGZvciAqeX4wfiogaXMgdGhlbg0KDQokJFxsZWZ0ICggXGhhdHt5XzB9IC0gdF97XGFscGhhLzJ9IFBFKFxoYXR7eV8wfSksIFw7IFxoYXR7eV8wfSArIA0KICAgIHRfe1xhbHBoYS8yfSBQRShcaGF0IHlfMCkgXHJpZ2h0ICkkJA0KDQpBcyB0aGUgbmFtZSBzdWdnZXN0cywgdGhlIHByb2JhYmlsaXR5IHRoYXQgKnl+MH4qIHdpbGwgYWN0dWFsbHkgbGllIGluIHRoaXMgcHJlZGljdGlvbiBpbnRlcnZhbCBpcyAkMS1cYWxwaGEkLg0KDQpBZ2Fpbiwgd2Ugd2lsbCBub3QgbmVlZCB0byBjYWxjdWxhdGUgdGhlc2UgaW50ZXJ2YWxzIGJ5IGhhbmQ6IFIgd2lsbCBkbyBpdCBmb3IgdXMuIFRvIGdldCB0aGUgcHJlZGljdGVkIHZhbHVlcyB3ZSBzZXQgJHhfMCQgKHdoaWNoIGNhbiBiZSBtb3JlIHRoYW4gb25lIHZhbHVlKSBpbiBhbiBvYmplY3QgY2FsbGVkIGEgIGBkYXRhLmZyYW1lYCwgIHRoZW4gaW52b2tlIHRoZSBgcHJlZGljdCgpYCBmdW5jdGlvbiBiYXNlZCBvbiBhbiBleGlzdGluZyBsaW5lYXIgbW9kZWwuIA0KDQoNCg0KIyMgQm9pbGluZyBQb2ludCBEYXRhDQoNCldlIHdpbGwgdXNlIGRhdGEgb24gYm9pbGluZyBwb2ludCBpbiBkZWdyZWVzIEZhaHJlbmhlaXQgKCp5KikgYW5kIHByZXNzdXJlIGluIGluY2hlcyBvZiBtZXJjdXJ5ICgqeCopLCBjb2xsZWN0ZWQgZHVyaW5nIGFuIGV4cGVkaXRpb24gaW4gdGhlIEFscHMuDQpEYXRhIHNvdXJjZTogQSBIYW5kYm9vayBvZiBTbWFsbCBEYXRhIFNldHMgYnkgSGFuZCwgRGFseSwgTHVubiwgTWNDb253YXkgYW5kIE9zdHJvd3NraTsgYWxzbyBrbm93biBhcyBgZm9yYmVzYCBpbiB0aGUgYE1BU1MgcGFja2FnZS4NCg0KIVtzb21ldGhpbmcgaXMgYm9pbGluZyBpbiBhIGJlYWtlci5dKC4uL2dyYXBoaWNzL2JvaWxpbmcuanBnKSBbXSgpDQoNCiMjIyBQcmVkaWN0aW9uIGZvciBCb2lsaW5nIFBvaW50IERhdGEgaW4gUg0KDQpgciB4ZnVuOjplbWJlZF9maWxlKCIuLi8uLi9kYXRhL2JvaWxpbmcuY3N2IilgDQoNCg0KYGBge3IgZ2V0Qm9pbERhdGEsIGVjaG89LTEsIGV2YWw9LTJ9DQpCb2lsIDwtIHJlYWQuY3N2KGZpbGU9Ii4uLy4uL2RhdGEvYm9pbGluZy5jc3YiLCBoZWFkZXI9VFJVRSkgDQpCb2lsIDwtIHJlYWQuY3N2KGZpbGU9Imh0dHBzOi8vci1yZXNvdXJjZXMubWFzc2V5LmFjLm56L2RhdGEvMTYxMjUxL2JvaWxpbmcuY3N2IiwgaGVhZGVyPVRSVUUpIA0KQm9pbC5sbSA8LSBsbShCUHR+UHJlc3N1cmUsIGRhdGE9Qm9pbCkNCnN1bW1hcnkoQm9pbC5sbSkNCngwIDwtIGRhdGEuZnJhbWUoUHJlc3N1cmU9YygyNywzMikpDQpwcmVkaWN0KEJvaWwubG0sbmV3ZGF0YT14MCxpbnRlcnZhbD0icHJlZGljdGlvbiIsbGV2ZWw9MC45NSkNCmBgYA0KDQpXZSBjYW4gdXNlIHRoZSBgcHJlZGljdCgpYCBmdW5jdGlvbiAod2l0aCBvbmUgc21hbGwgY2hhbmdlKSB0byBvYnRhaW4gdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGZvciB0aGUgbWVhbiByZXNwb25zZSBhdCB0aGUgc2FtZSB2YWx1ZXMgb2YgdGhlIHByZWRpY3RvciB1c2luZzoNCg0KDQpgYGB7ciBjb25maWRlbmNlfQ0KcHJlZGljdChCb2lsLmxtLG5ld2RhdGE9eDAsaW50ZXJ2YWw9ImNvbmZpZGVuY2UiLGxldmVsPTAuOTUpDQpgYGANCg0KIyMjIEZpdHRlZCBMaW5lIFBsb3QgZm9yIEJvaWxpbmcgUG9pbnQgRGF0YQ0KDQoNCg0KDQpgYGB7ciBCb2lsRml0dGVkTGluZX0NCmdncGxvdChCb2lsLCBtYXBwaW5nID0gYWVzKHk9QlB0LCB4PVByZXNzdXJlKSkgK2dlb21fcG9pbnQoKSArIA0KZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKyANCnlsYWIoIkJvaWxpbmcgUG9pbnQiKSArIHhsYWIoIlByZXNzdXJlIikNCmBgYA0KDQpUaGUgbW9kZWwgZml0IGxvb2tzIGdvb2Qgc28gd2Ugc2hvdWxkIGV4cGVjdCBmYWlybHkgdGlnaHQgcHJlZGljdGlvbiBpbnRlcnZhbHMuDQoNCg0KDQoNCg0KIyMgQ29uY2x1c2lvbnMgZm9yIEJvaWxpbmcgUG9pbnQgRGF0YQ0KDQpGaXR0ZWQgbW9kZWwgaXMgJCRFW1xtYm94e0JQdH1dID0gYHIgcm91bmQoY29lZihCb2lsLmxtKVsxXSwgMSlgICArIGByIHJvdW5kKGNvZWYoQm9pbC5sbSlbMl0sIDMpYH5cbWJveHtQcmVzc3VyZX0kJA0KDQpQcmVzc3VyZSBleHBsYWlucyBvdmVyIDk5JSBvZiB0aGUgdmFyaWF0aW9uIGluIGJvaWxpbmcgcG9pbnQuDQoNCldoZW4gcHJlc3N1cmUgaXMgMjcgaW5jaGVzIG9mIG1lcmN1cnksIHByZWRpY3RlZCB0ZW1wZXJhdHVyZSBpcyAyMDYuNiBkZWdyZWVzIEZhaHJlbmhlaXQgd2l0aCA5NSUgcHJlZGljdGlvbiBpbnRlcnZhbCAoMjA1LjcsIDIwNy42KS4NCg0KV2hlbiBwcmVzc3VyZSBpcyAzMiBpbmNoZXMgb2YgbWVyY3VyeSwgcHJlZGljdGlvbiBpcyAyMTYuMiB3aXRoICAgICA5NSUgcHJlZGljdGlvbiBpbnRlcnZhbCAoMjE1LjAsIDIxNy4zKS4NCg0KDQojIyBUZXN0IHlvdXIgdW5kZXJzdGFuZGluZw0KDQpVc2UgdGhlIG1vZGVsIGFib3ZlIHRvIHByb3ZlIHRvIHlvdXJzZWxmIHRoYXQ6DQoNCjEuIFRoZSBwcmVkaWN0aW9uIGludGVydmFsIGlzIHdpZGVyIHRoYW4gdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIGEgZ2l2ZW4gKnh+MH4qLg0KMy4gdGhlIGludGVydmFscyBnZXQgd2lkZXIgdGhlIGZhcnRoZXIgKnh+MH4qIGdldHMgZnJvbSAkXGJhcnt4fSQuDQo0LiB0aGUgaW50ZXJ2YWxzIGdldCB3aWRlciB3aGVuIHRoZSBsZXZlbCBvZiBjb25maWRlbmNlIGZvciB0aGUgaW50ZXJ2YWwgaW5jcmVhc2VzLg0KDQpgYGB7ciBleHRyYVdvcmt9DQp4MCA8LSBkYXRhLmZyYW1lKFByZXNzdXJlPWMoMjc6MzIpKQ0KcHJlZGljdChCb2lsLmxtLCBuZXdkYXRhPXgwLCBpbnRlcnZhbD0icHJlZGljdGlvbiIsIGxldmVsPTAuOTUpDQpwcmVkaWN0KEJvaWwubG0sIG5ld2RhdGE9eDAsIGludGVydmFsPSJjb25maWRlbmNlIiwgbGV2ZWw9MC45NSkNCnByZWRpY3QoQm9pbC5sbSwgbmV3ZGF0YT14MCwgaW50ZXJ2YWw9ImNvbmZpZGVuY2UiLCBsZXZlbD0wLjk5KQ0KYGBgDQo=