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.

something is boiling in a beaker.

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'

unlabelled

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:

  1. The prediction interval is wider than the confidence interval for a given x0.
  2. the intervals get wider the farther x0 gets from \(\bar{x}\).
  3. 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=