Loading the data from the MASS package and cleaning the
names
Checking the data
cats |>
slice_sample(n = 10)
## body heart
## 1 2.1 7.6
## 2 2.9 10.1
## 3 2.5 11.0
## 4 3.6 15.0
## 5 2.9 11.8
## 6 3.2 12.3
## 7 2.2 8.7
## 8 2.5 8.8
## 9 2.2 11.0
## 10 3.1 12.1
Scatter plot for body vs heart:
We should start with a plot of body vs heart weight:
gg_cats <-
ggplot(
data = cats,
mapping = aes(
x = body,
y = heart
)
) +
geom_point() +
theme_bw() +
labs(
title = 'Cats: Body weight vs heart weight',
subtitle = 'LOESS line added',
x = 'Body',
y = 'Heart'
) +
# Adding the units to the axes
scale_x_continuous(
labels = scales::label_number(suffix = ' kg')
) +
scale_y_continuous(
labels = scales::label_number(suffix = ' g')
)
gg_cats +
geom_smooth(
method = 'loess',
formula = y ~ x,
se = F
)

Fitting the linear model:
While we’ve been calculating the slope ‘by hand’, we typically use a
function to do it for us, like lm(y ~ x, data = ...) and we
can get the summary stats from summary(model)
cats_lm <- lm(heart ~ body, data = cats)
summary(cats_lm)
##
## Call:
## lm(formula = heart ~ body, data = cats)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.5694 -0.9634 -0.0921 1.0426 5.1238
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.3567 0.6923 -0.515 0.607
## body 4.0341 0.2503 16.119 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.452 on 142 degrees of freedom
## Multiple R-squared: 0.6466, Adjusted R-squared: 0.6441
## F-statistic: 259.8 on 1 and 142 DF, p-value: < 2.2e-16
The broom package has some useful functions that are
popular as they return objects that are easier to work with
tidy(model) returns the coefficient table as a data
frame
glance(model) returns a 1 row data frame with the
fit statistics of the model
augment_columns(model, data) will add the predicted
response, residuals, and other useful stats to the data frame
For now, we’ll just work with tidy()
cats_lm_table <- broom::tidy(cats_lm)
cats_lm_table
## # A tibble: 2 × 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) -0.357 0.692 -0.515 6.07e- 1
## 2 body 4.03 0.250 16.1 6.97e-34
# Finding the MSE:
sum(cats_lm$residuals^2) / (nrow(cats) - 2)
## [1] 2.109388
# Finding S_XX
sum((cats$body - mean(cats$body))^2)
## [1] 33.67972
Inference for a mean of the response
What if I want to know the average heart weight for a cat that weighs
3 kilograms?
Instead of wanting to know how the heart weight changes as
body weight increases, I’m interested in the mean for a particular body
weight.
From the slides, we can get a confidence interval for the mean when
\(X = X_h\)
\[\hat{Y}_h \pm t(1-\alpha/2; n - 2)
\sqrt{MSE\left[\frac{1}{n} + \frac{(X_h - \bar{X})^2}{S_{XX}}
\right]}\]
We’ll create the confidence interval ‘by hand’ first:
Confidence interval for \(X_h = 3\)
by hand
Start by calculating the different statistics we need:
\(\hat{Y}_h\)
\(t(1-\alpha/2; n -
2)\)
\(MSE\)
\(S_{XX}\)
5 \((X_h - \bar{X})^2\)
X_h <- 3
n <- nrow(cats)
# 1) Pred Y Intercept slope
Y_hat_h <- cats_lm$coefficients[1] + cats_lm$coefficients[2] * X_h
# 2) Critical value
crit_val <- qt(1 - 0.05/2, df = n - 2)
# 3) MSE
MSE_cats <- sum(cats_lm$residuals^2) / (n-2)
# 4) S_XX
S_XX <- sum((cats$body-mean(cats$body))^2)
# 5) X_h squared deviance
dev_X_h <- (X_h - mean(cats$body))^2
# The standard error using 3, 4, and 5
SE_h <- sqrt(MSE_cats * (1/n + dev_X_h/S_XX))
# Display results
tribble(
~ stats, ~ value,
'pred Y when X = 3', round(Y_hat_h, 3),
'Critical Value', round(crit_val, 3),
'MSE', round(MSE_cats, 3),
'S_XX', round(S_XX, 3),
'(X - X_bar)^2', round(dev_X_h, 3),
'Standard Error', round(SE_h, 3)
)
## # A tibble: 6 × 2
## stats value
## <chr> <dbl>
## 1 pred Y when X = 3 11.7
## 2 Critical Value 1.98
## 3 MSE 2.11
## 4 S_XX 33.7
## 5 (X - X_bar)^2 0.076
## 6 Standard Error 0.139
Now that we have all the pieces, we can create the confidence
interval:
pred_Y_h_CI <-
c('lower' = Y_hat_h - crit_val * sqrt(MSE_cats *(1/n + dev_X_h/S_XX)),
'upper' = Y_hat_h + crit_val * sqrt(MSE_cats *(1/n + dev_X_h/S_XX))
)
pred_Y_h_CI
## lower.(Intercept) upper.(Intercept)
## 11.46995 12.02110
We can be 95% confident that the average heart weight of cats that
weigh 3 kg is between 11.5 g to 12 g.
We can also visualize the confidence interval for the predictions
using geom_smooth(method = 'lm')
gg_cats +
geom_smooth(
method = 'lm',
color = 'steelblue',
fill = 'steelblue',
formula = y ~ x,
level = 0.95 # Confidence level
) +
labs(subtitle = 'Linear regression line with 95% confidence band')

The shaded area is the confidence interval for the average response
of \(Y\) given the value of \(X\).
Prediction intervals
The confidence intervals in the previous section tries to estimate
the mean response, aka, the average of all cats that weigh 3 kg.
What if we don’t want the expected value (mean) of the response, but
instead to know the range where 90%, 95%, or 99% of the population will
be for a given value of \(X\)?
Instead, we want to make a prediction interval. Like with the
confidence interval, we can do it ‘by hand’, it just takes a small
adjustment!
From the slides, we found the variance for \(Y_h\) to be:
\[Var(Y_h) = Var(b_0 + b_1 X_h +
\varepsilon_h)\]
Looks similar to the variance for \(\hat{Y}_h\), but now we also have \(\varepsilon_h\). Weirdly, \(b_0\) and \(b_1\) are independent of \(\varepsilon_h\) (but NOT each other!), we
can split the variance term above:
\[Var(Y_h) = Var(b_0 + b_1 X_h) +
Var(\varepsilon_h)\]
From previous results and our model assumptions, we get:
\[Var(Y_h) = \sigma^2\left[\frac{1}{n} +
\frac{(X_h - \bar{X})^2}{S_{XX}} \right] + \sigma^2\]
which simplifies down to:
\[Var(Y_h) = \sigma^2\left[1 +\frac{1}{n}
+ \frac{(X_h - \bar{X})^2}{S_{XX}} \right]\]
The standard error is then:
\[SE(Y_h) = \sqrt{MSE\left[1 +\frac{1}{n}
+ \frac{(X_h - \bar{X})^2}{S_{XX}} \right]}\]
The only difference is the \(1\)
inside the square root of the standard error!
Let’s update the SE for the prediction interval:
SE_h_PI <- sqrt(MSE_cats * (1 + 1/n + dev_X_h/S_XX))
round(SE_h_PI, 2)
## [1] 1.46
While we’re “only” adding 1 to go from the CI to PI, the standard
error increases over 10 times!
Prediction interval for \(X_h =
3\)
c('lower' = as.numeric(Y_hat_h) - crit_val * SE_h_PI,
'upper' = as.numeric(Y_hat_h) + crit_val * SE_h_PI) |>
round(2)
## lower upper
## 8.86 14.63
We are 95% confident that a cat that weights 3kg will have a heart
weight between 8.9 to 14.6 grams.
Prediction band for cat heart weights
Like what we saw for the confidence interval, we can create a
prediction band around the line of best fit.
Unfortunately, there isn’t a quick way to create the prediction band
like what we had with geom_smooth(...). We have to
‘manually’ find the lower and upper band for the range of body
weights.
We’ll create the band for the range of body weights in increments of
0.01 kg:
# Creating the lower and upper range of the prediction band:
cats_PI_band <-
tibble(
# Range of body weight by 0.01 kg
body = seq(min(cats$body), max(cats$body), by = 0.001),
# Y_hat for each X:
Y_hat = cats_lm$coef[1] + cats_lm$coef[2] * body,
# Calculating (X - X_bar)^2
body_dev2 = (body - mean(cats$body))^2,
# Calculating the SE for each X
SE = sqrt(MSE_cats * (1 + 1/n + body_dev2 / S_XX)),
# Lower boundary:
lower_95_PI = Y_hat - crit_val * SE,
# upper boundary:
upper_95_PI = Y_hat + crit_val * SE
)
cats_PI_band |>
round(2)
## # A tibble: 1,901 × 6
## body Y_hat body_dev2 SE lower_95_PI upper_95_PI
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2 7.71 0.52 1.47 4.81 10.6
## 2 2 7.72 0.52 1.47 4.81 10.6
## 3 2 7.72 0.52 1.47 4.82 10.6
## 4 2 7.72 0.52 1.47 4.82 10.6
## 5 2 7.73 0.52 1.47 4.82 10.6
## 6 2 7.73 0.52 1.47 4.83 10.6
## 7 2.01 7.74 0.51 1.47 4.83 10.6
## 8 2.01 7.74 0.51 1.47 4.84 10.6
## 9 2.01 7.74 0.51 1.47 4.84 10.6
## 10 2.01 7.75 0.51 1.47 4.85 10.6
## # ℹ 1,891 more rows
Now we’ll add it to the scatterplot gg_cats using
geom_ribbon(...):
gg_cats +
# Changing the subtitle
labs(subtitle = 'Linear regression line with 95% confidence and prediction bands') +
# Adding the prediction band
geom_ribbon(
data = cats_PI_band,
mapping = aes(
x = body,
ymin = lower_95_PI,
ymax = upper_95_PI,
y = NULL # Since y is mapped in gg_cats, we need to remove it
),
alpha = 0.5,
color = 'white',
fill = 'steelblue'
) +
# Adding the prediction line and confidence band
geom_smooth(
method = 'lm',
formula = y ~ x,
color = 'steelblue',
fill = 'steelblue'
)

LS0tDQp0aXRsZTogJ0luZmVyZW5jZSBmb3IgdGhlIFJlc3BvbnNlIFZhcmlhYmxlIC0gQ2F0cyBleGFtcGxlJw0KYXV0aG9yOiAiQ2hhcHRlciAyIg0KZGF0ZTogIlNUQSA0MjEwIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGZpZ193aWR0aDogNg0KICAgIGZpZ19oZWlnaHQ6IDYNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdjZW50ZXInKQ0Kc2V0LnNlZWQoNDIxMCkNCg0KYGBgDQoNCkxvYWRpbmcgdGhlIGRhdGEgZnJvbSB0aGUgYE1BU1NgIHBhY2thZ2UgYW5kIGNsZWFuaW5nIHRoZSBuYW1lcw0KDQpgYGB7ciBwYWNrYWdlc19kYXRhLCBpbmNsdWRlID0gRn0NCiMgTG9hZGluZyBwYWNrYWdlcw0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCiMgR2V0dGluZyB0aGUgZGF0YQ0KY2F0cyA8LSBNQVNTOjpjYXRzIHw+DQogIGRwbHlyOjpzZWxlY3QoYm9keSA9IEJ3dCwNCiAgICAgICAgICAgICAgICBoZWFydCA9IEh3dCkNCmBgYA0KDQpDaGVja2luZyB0aGUgZGF0YQ0KDQpgYGB7cn0NCmNhdHMgfD4NCiAgc2xpY2Vfc2FtcGxlKG4gPSAxMCkNCmBgYA0KDQoNCiMjIFNjYXR0ZXIgcGxvdCBmb3IgYm9keSB2cyBoZWFydDoNCg0KV2Ugc2hvdWxkIHN0YXJ0IHdpdGggYSBwbG90IG9mIGJvZHkgdnMgaGVhcnQgd2VpZ2h0Og0KDQpgYGB7ciBzY2F0dGVyX3Bsb3R9DQpnZ19jYXRzIDwtIA0KICBnZ3Bsb3QoDQogICAgZGF0YSA9IGNhdHMsDQogICAgbWFwcGluZyA9IGFlcygNCiAgICAgIHggPSBib2R5LA0KICAgICAgeSA9IGhlYXJ0DQogICAgKQ0KICApICsgDQogIGdlb21fcG9pbnQoKSArIA0KICB0aGVtZV9idygpICsgDQogIGxhYnMoDQogICAgdGl0bGUgPSAnQ2F0czogQm9keSB3ZWlnaHQgdnMgaGVhcnQgd2VpZ2h0JywNCiAgICBzdWJ0aXRsZSA9ICdMT0VTUyBsaW5lIGFkZGVkJywNCiAgICB4ID0gJ0JvZHknLA0KICAgIHkgPSAnSGVhcnQnDQogICkgKyANCiAgIyBBZGRpbmcgdGhlIHVuaXRzIHRvIHRoZSBheGVzDQogIHNjYWxlX3hfY29udGludW91cygNCiAgICBsYWJlbHMgPSBzY2FsZXM6OmxhYmVsX251bWJlcihzdWZmaXggPSAnIGtnJykNCiAgKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgbGFiZWxzID0gc2NhbGVzOjpsYWJlbF9udW1iZXIoc3VmZml4ID0gJyBnJykNCiAgKQ0KDQpnZ19jYXRzICsNCiAgZ2VvbV9zbW9vdGgoDQogICAgbWV0aG9kID0gJ2xvZXNzJywNCiAgICBmb3JtdWxhID0geSB+IHgsDQogICAgc2UgPSBGDQogICkNCmBgYA0KDQojIyBGaXR0aW5nIHRoZSBsaW5lYXIgbW9kZWw6DQoNCldoaWxlIHdlJ3ZlIGJlZW4gY2FsY3VsYXRpbmcgdGhlIHNsb3BlICdieSBoYW5kJywgd2UgdHlwaWNhbGx5IHVzZSBhIGZ1bmN0aW9uIHRvIGRvIGl0IGZvciB1cywgbGlrZSBgbG0oeSB+IHgsIGRhdGEgPSAuLi4pYCBhbmQgd2UgY2FuIGdldCB0aGUgc3VtbWFyeSBzdGF0cyBmcm9tIGBzdW1tYXJ5KG1vZGVsKWANCg0KYGBge3IgY2F0c19sbX0NCmNhdHNfbG0gPC0gbG0oaGVhcnQgfiBib2R5LCBkYXRhID0gY2F0cykNCg0Kc3VtbWFyeShjYXRzX2xtKQ0KYGBgDQoNClRoZSBgYnJvb21gIHBhY2thZ2UgaGFzIHNvbWUgdXNlZnVsIGZ1bmN0aW9ucyB0aGF0IGFyZSBwb3B1bGFyIGFzIHRoZXkgcmV0dXJuIG9iamVjdHMgdGhhdCBhcmUgZWFzaWVyIHRvIHdvcmsgd2l0aA0KDQotIGB0aWR5KG1vZGVsKWAgcmV0dXJucyB0aGUgY29lZmZpY2llbnQgdGFibGUgYXMgYSBkYXRhIGZyYW1lDQoNCi0gYGdsYW5jZShtb2RlbClgIHJldHVybnMgYSAxIHJvdyBkYXRhIGZyYW1lIHdpdGggdGhlIGZpdCBzdGF0aXN0aWNzIG9mIHRoZSBtb2RlbA0KICAtIFdlJ2xsIGdldCB0byB0aGF0IGxhdGVyDQoNCi0gYGF1Z21lbnRfY29sdW1ucyhtb2RlbCwgZGF0YSlgIHdpbGwgYWRkIHRoZSBwcmVkaWN0ZWQgcmVzcG9uc2UsIHJlc2lkdWFscywgYW5kIG90aGVyIHVzZWZ1bCBzdGF0cyB0byB0aGUgZGF0YSBmcmFtZQ0KDQoNCkZvciBub3csIHdlJ2xsIGp1c3Qgd29yayB3aXRoIGB0aWR5KClgDQoNCmBgYHtyIHRpZHl9DQpjYXRzX2xtX3RhYmxlIDwtIGJyb29tOjp0aWR5KGNhdHNfbG0pDQoNCmNhdHNfbG1fdGFibGUNCmBgYA0KDQoNCmBgYHtyfQ0KIyBGaW5kaW5nIHRoZSBNU0U6DQpzdW0oY2F0c19sbSRyZXNpZHVhbHNeMikgLyAobnJvdyhjYXRzKSAtIDIpDQoNCiMgRmluZGluZyBTX1hYDQpzdW0oKGNhdHMkYm9keSAtIG1lYW4oY2F0cyRib2R5KSleMikNCmBgYA0KDQoNCiMjIEluZmVyZW5jZSBmb3IgYSBtZWFuIG9mIHRoZSByZXNwb25zZQ0KDQpXaGF0IGlmIEkgd2FudCB0byBrbm93IHRoZSBhdmVyYWdlIGhlYXJ0IHdlaWdodCBmb3IgYSBjYXQgdGhhdCB3ZWlnaHMgMyBraWxvZ3JhbXM/DQoNCkluc3RlYWQgb2Ygd2FudGluZyB0byBrbm93IGhvdyB0aGUgaGVhcnQgd2VpZ2h0ICpjaGFuZ2VzKiBhcyBib2R5IHdlaWdodCBpbmNyZWFzZXMsIEknbSBpbnRlcmVzdGVkIGluIHRoZSBtZWFuIGZvciBhIHBhcnRpY3VsYXIgYm9keSB3ZWlnaHQuDQoNCkZyb20gdGhlIHNsaWRlcywgd2UgY2FuIGdldCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIHRoZSBtZWFuIHdoZW4gJFggPSBYX2gkDQoNCiQkXGhhdHtZfV9oIFxwbSB0KDEtXGFscGhhLzI7IG4gLSAyKSBcc3FydHtNU0VcbGVmdFtcZnJhY3sxfXtufSArIFxmcmFjeyhYX2ggLSBcYmFye1h9KV4yfXtTX3tYWH19IFxyaWdodF19JCQNCg0KV2UnbGwgY3JlYXRlIHRoZSBjb25maWRlbmNlIGludGVydmFsICdieSBoYW5kJyBmaXJzdDoNCg0KIyMjIENvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yICRYX2ggPSAzJCBieSBoYW5kDQoNClN0YXJ0IGJ5IGNhbGN1bGF0aW5nIHRoZSBkaWZmZXJlbnQgc3RhdGlzdGljcyB3ZSBuZWVkOg0KDQoxKSAkXGhhdHtZfV9oJA0KDQoyKSAkdCgxLVxhbHBoYS8yOyBuIC0gMikkDQoNCjMpICRNU0UkDQoNCjQpICRTX3tYWH0kDQoNCjUgJChYX2ggLSBcYmFye1h9KV4yJA0KDQpgYGB7ciBzdGF0X25lZWRlZH0NClhfaCA8LSAzDQpuIDwtIG5yb3coY2F0cykNCg0KIyAxKSBQcmVkIFkgICAgICAgSW50ZXJjZXB0ICAgICAgICAgICAgICAgICAgICAgc2xvcGUNCllfaGF0X2ggPC0gICBjYXRzX2xtJGNvZWZmaWNpZW50c1sxXSArIGNhdHNfbG0kY29lZmZpY2llbnRzWzJdICogWF9oDQoNCg0KIyAyKSBDcml0aWNhbCB2YWx1ZQ0KY3JpdF92YWwgPC0gcXQoMSAtIDAuMDUvMiwgZGYgPSBuIC0gMikNCg0KIyAzKSBNU0UNCk1TRV9jYXRzIDwtIHN1bShjYXRzX2xtJHJlc2lkdWFsc14yKSAvIChuLTIpDQoNCiMgNCkgU19YWA0KU19YWCA8LSBzdW0oKGNhdHMkYm9keS1tZWFuKGNhdHMkYm9keSkpXjIpDQoNCiMgNSkgWF9oIHNxdWFyZWQgZGV2aWFuY2UNCmRldl9YX2ggPC0gKFhfaCAtIG1lYW4oY2F0cyRib2R5KSleMg0KDQojIFRoZSBzdGFuZGFyZCBlcnJvciB1c2luZyAzLCA0LCBhbmQgNQ0KU0VfaCA8LSBzcXJ0KE1TRV9jYXRzICogKDEvbiArIGRldl9YX2gvU19YWCkpDQoNCiMgRGlzcGxheSByZXN1bHRzDQp0cmliYmxlKA0KICAgICAgICAgICAgICB+IHN0YXRzLCAgICAgICAgICAgICAgICB+IHZhbHVlLA0KICAncHJlZCBZIHdoZW4gWCA9IDMnLCAgICAgIHJvdW5kKFlfaGF0X2gsIDMpLA0KICAgICAnQ3JpdGljYWwgVmFsdWUnLCAgICAgcm91bmQoY3JpdF92YWwsIDMpLA0KICAgICAgICAgICAgICAgICdNU0UnLCAgICAgcm91bmQoTVNFX2NhdHMsIDMpLA0KICAgICAgICAgICAgICAgJ1NfWFgnLCAgICAgICAgIHJvdW5kKFNfWFgsIDMpLA0KICAgICAgJyhYIC0gWF9iYXIpXjInLCAgICAgIHJvdW5kKGRldl9YX2gsIDMpLA0KICAgICAnU3RhbmRhcmQgRXJyb3InLCAgICAgICAgIHJvdW5kKFNFX2gsIDMpDQopDQoNCmBgYA0KDQpOb3cgdGhhdCB3ZSBoYXZlIGFsbCB0aGUgcGllY2VzLCB3ZSBjYW4gY3JlYXRlIHRoZSBjb25maWRlbmNlIGludGVydmFsOg0KDQpgYGB7ciBwcmVkX2hlYXJ0X0NJfQ0KcHJlZF9ZX2hfQ0kgPC0gDQogIGMoJ2xvd2VyJyA9IFlfaGF0X2ggLSBjcml0X3ZhbCAqIHNxcnQoTVNFX2NhdHMgKigxL24gKyBkZXZfWF9oL1NfWFgpKSwNCiAgICAndXBwZXInID0gWV9oYXRfaCArIGNyaXRfdmFsICogc3FydChNU0VfY2F0cyAqKDEvbiArIGRldl9YX2gvU19YWCkpDQogICAgKQ0KDQpwcmVkX1lfaF9DSQ0KYGBgDQoNCldlIGNhbiBiZSA5NSUgY29uZmlkZW50IHRoYXQgdGhlIGF2ZXJhZ2UgaGVhcnQgd2VpZ2h0IG9mIGNhdHMgdGhhdCB3ZWlnaCAzIGtnIGlzIGJldHdlZW4gMTEuNSBnIHRvIDEyIGcuDQoNCldlIGNhbiBhbHNvIHZpc3VhbGl6ZSB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIHByZWRpY3Rpb25zIHVzaW5nIGBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKWANCg0KYGBge3IgcHJlZF9DSV9wbG90fQ0KZ2dfY2F0cyArDQogIGdlb21fc21vb3RoKA0KICAgIG1ldGhvZCA9ICdsbScsDQogICAgY29sb3IgPSAnc3RlZWxibHVlJywNCiAgICBmaWxsID0gJ3N0ZWVsYmx1ZScsDQogICAgZm9ybXVsYSA9IHkgfiB4LA0KICAgIGxldmVsID0gMC45NSAgICAgICAjIENvbmZpZGVuY2UgbGV2ZWwNCiAgKSArIA0KICBsYWJzKHN1YnRpdGxlID0gJ0xpbmVhciByZWdyZXNzaW9uIGxpbmUgd2l0aCA5NSUgY29uZmlkZW5jZSBiYW5kJykNCmBgYA0KDQpUaGUgc2hhZGVkIGFyZWEgaXMgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIHRoZSBhdmVyYWdlIHJlc3BvbnNlIG9mICRZJCBnaXZlbiB0aGUgdmFsdWUgb2YgJFgkLg0KDQoNCiMjIFByZWRpY3Rpb24gaW50ZXJ2YWxzDQoNClRoZSBjb25maWRlbmNlIGludGVydmFscyBpbiB0aGUgcHJldmlvdXMgc2VjdGlvbiB0cmllcyB0byBlc3RpbWF0ZSB0aGUgbWVhbiByZXNwb25zZSwgYWthLCB0aGUgYXZlcmFnZSBvZiBhbGwgY2F0cyB0aGF0IHdlaWdoIDMga2cuDQoNCldoYXQgaWYgd2UgZG9uJ3Qgd2FudCB0aGUgZXhwZWN0ZWQgdmFsdWUgKG1lYW4pIG9mIHRoZSByZXNwb25zZSwgYnV0IGluc3RlYWQgdG8ga25vdyB0aGUgcmFuZ2Ugd2hlcmUgOTAlLCA5NSUsIG9yIDk5JSBvZiB0aGUgcG9wdWxhdGlvbiB3aWxsIGJlIGZvciBhIGdpdmVuIHZhbHVlIG9mICRYJD8NCg0KSW5zdGVhZCwgd2Ugd2FudCB0byBtYWtlIGEgcHJlZGljdGlvbiBpbnRlcnZhbC4gTGlrZSB3aXRoIHRoZSBjb25maWRlbmNlIGludGVydmFsLCB3ZSBjYW4gZG8gaXQgJ2J5IGhhbmQnLCBpdCBqdXN0IHRha2VzIGEgc21hbGwgYWRqdXN0bWVudCENCg0KRnJvbSB0aGUgc2xpZGVzLCB3ZSBmb3VuZCB0aGUgdmFyaWFuY2UgZm9yICRZX2gkIHRvIGJlOg0KDQokJFZhcihZX2gpID0gVmFyKGJfMCArIGJfMSBYX2ggKyBcdmFyZXBzaWxvbl9oKSQkDQoNCkxvb2tzIHNpbWlsYXIgdG8gdGhlIHZhcmlhbmNlIGZvciAkXGhhdHtZfV9oJCwgYnV0IG5vdyB3ZSBhbHNvIGhhdmUgJFx2YXJlcHNpbG9uX2gkLiBXZWlyZGx5LCAkYl8wJCBhbmQgJGJfMSQgYXJlIGluZGVwZW5kZW50IG9mICRcdmFyZXBzaWxvbl9oJCAoYnV0IE5PVCBlYWNoIG90aGVyISksIHdlIGNhbiBzcGxpdCB0aGUgdmFyaWFuY2UgdGVybSBhYm92ZToNCg0KJCRWYXIoWV9oKSA9IFZhcihiXzAgKyBiXzEgWF9oKSArIFZhcihcdmFyZXBzaWxvbl9oKSQkDQoNCkZyb20gcHJldmlvdXMgcmVzdWx0cyBhbmQgb3VyIG1vZGVsIGFzc3VtcHRpb25zLCB3ZSBnZXQ6DQoNCiQkVmFyKFlfaCkgPSBcc2lnbWFeMlxsZWZ0W1xmcmFjezF9e259ICsgXGZyYWN7KFhfaCAtIFxiYXJ7WH0pXjJ9e1Nfe1hYfX0gXHJpZ2h0XSAgKyBcc2lnbWFeMiQkDQoNCndoaWNoIHNpbXBsaWZpZXMgZG93biB0bzoNCg0KJCRWYXIoWV9oKSA9IFxzaWdtYV4yXGxlZnRbMSArXGZyYWN7MX17bn0gKyBcZnJhY3soWF9oIC0gXGJhcntYfSleMn17U197WFh9fSBccmlnaHRdJCQNCg0KVGhlIHN0YW5kYXJkIGVycm9yIGlzIHRoZW46DQoNCg0KJCRTRShZX2gpID0gXHNxcnR7TVNFXGxlZnRbMSArXGZyYWN7MX17bn0gKyBcZnJhY3soWF9oIC0gXGJhcntYfSleMn17U197WFh9fSBccmlnaHRdfSQkDQoNClRoZSBvbmx5IGRpZmZlcmVuY2UgaXMgdGhlICQxJCBpbnNpZGUgdGhlIHNxdWFyZSByb290IG9mIHRoZSBzdGFuZGFyZCBlcnJvciENCg0KTGV0J3MgdXBkYXRlIHRoZSBTRSBmb3IgdGhlIHByZWRpY3Rpb24gaW50ZXJ2YWw6DQoNCmBgYHtyfQ0KU0VfaF9QSSA8LSBzcXJ0KE1TRV9jYXRzICogKDEgKyAxL24gKyBkZXZfWF9oL1NfWFgpKQ0Kcm91bmQoU0VfaF9QSSwgMikNCmBgYA0KDQpXaGlsZSB3ZSdyZSAib25seSIgYWRkaW5nIDEgdG8gZ28gZnJvbSB0aGUgQ0kgdG8gUEksIHRoZSBzdGFuZGFyZCBlcnJvciBpbmNyZWFzZXMgb3ZlciAxMCB0aW1lcyENCg0KIyMjIyBQcmVkaWN0aW9uIGludGVydmFsIGZvciAkWF9oID0gMyQNCg0KYGBge3IgUElfWF8zfQ0KYygnbG93ZXInID0gYXMubnVtZXJpYyhZX2hhdF9oKSAtIGNyaXRfdmFsICogU0VfaF9QSSwNCiAgJ3VwcGVyJyA9IGFzLm51bWVyaWMoWV9oYXRfaCkgKyBjcml0X3ZhbCAqIFNFX2hfUEkpIHw+DQogIHJvdW5kKDIpDQpgYGANCg0KV2UgYXJlIDk1JSBjb25maWRlbnQgdGhhdCBhIGNhdCB0aGF0IHdlaWdodHMgM2tnIHdpbGwgaGF2ZSBhIGhlYXJ0IHdlaWdodCBiZXR3ZWVuIDguOSB0byAxNC42IGdyYW1zLg0KDQoNCg0KIyMjIyBQcmVkaWN0aW9uIGJhbmQgZm9yIGNhdCBoZWFydCB3ZWlnaHRzDQoNCkxpa2Ugd2hhdCB3ZSBzYXcgZm9yIHRoZSBjb25maWRlbmNlIGludGVydmFsLCB3ZSBjYW4gY3JlYXRlIGEgcHJlZGljdGlvbiBiYW5kIGFyb3VuZCB0aGUgbGluZSBvZiBiZXN0IGZpdC4NCg0KVW5mb3J0dW5hdGVseSwgdGhlcmUgaXNuJ3QgYSBxdWljayB3YXkgdG8gY3JlYXRlIHRoZSBwcmVkaWN0aW9uIGJhbmQgbGlrZSB3aGF0IHdlIGhhZCB3aXRoIGBnZW9tX3Ntb290aCguLi4pYC4gV2UgaGF2ZSB0byAnbWFudWFsbHknIGZpbmQgdGhlIGxvd2VyIGFuZCB1cHBlciBiYW5kIGZvciB0aGUgcmFuZ2Ugb2YgYm9keSB3ZWlnaHRzLg0KDQpXZSdsbCBjcmVhdGUgdGhlIGJhbmQgZm9yIHRoZSByYW5nZSBvZiBib2R5IHdlaWdodHMgaW4gaW5jcmVtZW50cyBvZiAwLjAxIGtnOg0KDQpgYGB7ciBQSV9iYW5kX2RhdGF9DQojIENyZWF0aW5nIHRoZSBsb3dlciBhbmQgdXBwZXIgcmFuZ2Ugb2YgdGhlIHByZWRpY3Rpb24gYmFuZDoNCmNhdHNfUElfYmFuZCA8LSANCiAgdGliYmxlKA0KICAgICMgUmFuZ2Ugb2YgYm9keSB3ZWlnaHQgYnkgMC4wMSBrZw0KICAgIGJvZHkgPSBzZXEobWluKGNhdHMkYm9keSksIG1heChjYXRzJGJvZHkpLCBieSA9IDAuMDAxKSwNCiAgICANCiAgICAjIFlfaGF0IGZvciBlYWNoIFg6DQogICAgWV9oYXQgPSBjYXRzX2xtJGNvZWZbMV0gKyBjYXRzX2xtJGNvZWZbMl0gKiBib2R5LA0KICAgIA0KICAgICMgQ2FsY3VsYXRpbmcgKFggLSBYX2JhcileMg0KICAgIGJvZHlfZGV2MiA9IChib2R5IC0gbWVhbihjYXRzJGJvZHkpKV4yLA0KICAgIA0KICAgICMgQ2FsY3VsYXRpbmcgdGhlIFNFIGZvciBlYWNoIFgNCiAgICBTRSA9IHNxcnQoTVNFX2NhdHMgKiAoMSArIDEvbiArIGJvZHlfZGV2MiAvIFNfWFgpKSwNCiAgICANCiAgICAjIExvd2VyIGJvdW5kYXJ5Og0KICAgIGxvd2VyXzk1X1BJID0gWV9oYXQgLSBjcml0X3ZhbCAqIFNFLA0KICAgIA0KICAgICMgdXBwZXIgYm91bmRhcnk6DQogICAgdXBwZXJfOTVfUEkgPSBZX2hhdCArIGNyaXRfdmFsICogU0UNCiAgKQ0KDQpjYXRzX1BJX2JhbmQgfD4NCiAgcm91bmQoMikNCmBgYA0KDQpOb3cgd2UnbGwgYWRkIGl0IHRvIHRoZSBzY2F0dGVycGxvdCBgZ2dfY2F0c2AgdXNpbmcgYGdlb21fcmliYm9uKC4uLilgOg0KDQpgYGB7ciBwbG90X3dpdGhfcGl9DQpnZ19jYXRzICsNCiAgIyBDaGFuZ2luZyB0aGUgc3VidGl0bGUNCiAgbGFicyhzdWJ0aXRsZSA9ICdMaW5lYXIgcmVncmVzc2lvbiBsaW5lIHdpdGggOTUlIGNvbmZpZGVuY2UgYW5kIHByZWRpY3Rpb24gYmFuZHMnKSArIA0KICAjIEFkZGluZyB0aGUgcHJlZGljdGlvbiBiYW5kDQogIGdlb21fcmliYm9uKA0KICAgIGRhdGEgPSBjYXRzX1BJX2JhbmQsDQogICAgbWFwcGluZyA9IGFlcygNCiAgICAgIHggPSBib2R5LA0KICAgICAgeW1pbiA9IGxvd2VyXzk1X1BJLA0KICAgICAgeW1heCA9IHVwcGVyXzk1X1BJLA0KICAgICAgeSA9IE5VTEwgICAgICAgICAgICAjIFNpbmNlIHkgaXMgbWFwcGVkIGluIGdnX2NhdHMsIHdlIG5lZWQgdG8gcmVtb3ZlIGl0IA0KICAgICksDQogICAgYWxwaGEgPSAwLjUsDQogICAgY29sb3IgPSAnd2hpdGUnLA0KICAgIGZpbGwgPSAnc3RlZWxibHVlJw0KICApICsgDQogDQogIA0KICAjIEFkZGluZyB0aGUgcHJlZGljdGlvbiBsaW5lIGFuZCBjb25maWRlbmNlIGJhbmQNCiAgZ2VvbV9zbW9vdGgoDQogICAgbWV0aG9kID0gJ2xtJywNCiAgICBmb3JtdWxhID0geSB+IHgsDQogICAgY29sb3IgPSAnc3RlZWxibHVlJywNCiAgICBmaWxsID0gJ3N0ZWVsYmx1ZScNCiAgKSANCg0KDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=