Loading the data from the MASS package

# Getting the data
cats <- MASS::cats

Plotting the data

Any good examination starts with exploratory data analysis. Let’s create the scatterplot between body weight and heart weight:

gg_cats <- 
  ggplot(
    data = cats,
    mapping = aes(
      x = Bwt,
      y = Hwt
    )
  ) + 
  # Adding the points to the graph:
  geom_point(color = 'steelblue') + 
  # Adding the correlation between body and heart weight
  annotate(
    geom = 'text',
    label = paste0('r = ', round(cor(cats$Bwt, cats$Hwt), 3)),
    x = 2.2,
    y = 17.5,
    size = 5,
    color = 'red'
  ) +
  # Changing the theme
  theme_bw() + 
  # Adding a title and labeling the axes
  labs(
    title = 'Cats: Body weight vs Heart weight',
    x = 'Body Weight (kg)',
    y = 'Heart Weight (g)'
  )


gg_cats

Stating the model:

Since the association between body weight (\(X\)) and heart weight (\(Y\)) is linear, we can model \(Y\) using a linear function of \(X\):

\[Y_i= \beta_0 + \beta_1 X_i + \varepsilon_i\]

\[E(Y_i|X_i) = \beta_0 + \beta_1 X_i\]

Note: For the remainder of this example, \(Y_i|X_i\) will be shortened just as \(Y_i\) unless stated otherwise

We apply the distributional assumption on the random error, \(\varepsilon_i\):

\[\varepsilon_i \sim N(0, \sigma^2)\]

which implies a distribution for \(Y_i\):

\[\varepsilon_i \sim N(0, \sigma^2) \rightarrow Y_i \sim N(\beta_0 + \beta_1 X_i, \sigma^2 )\]

Fitting the model

Since the \(\beta\)’s are unknown population parameters, we need to estimate them using the sample data. So how do formulate their estimators?

\[\hat{\beta}_0 = b_0\]

\[\hat{\beta}_1 = b_1\]

The sample model is:

\[\hat{y}_i = b_0 + b_1 x_i\]

and

\[y_i = b_0 + b_1 x_i + e_i\]

where \(e_i\) is called the residual, which is a prediction of the random error, \(\varepsilon\):

\[\hat{\varepsilon}_i = e_i\]

We calculate the residual using the difference between the observed value, \(y_i\), and the estimated value, \(\hat{y}_i\):

\[e_i = y_i - \hat{y}_i\]

What is the best fitting line?

Estimating the line:

We can add the line of best fit to the scatterplot created earlier (gg_cats) by adding geom_smooth(method = 'lm'):

gg_cats_SLR <- 
  gg_cats + 
  geom_smooth(
    method = 'lm',
    formula = y ~ x,  
    se = F,           # This removes a confidence band we'll look at later
    color = 'orchid'
  )

gg_cats_SLR

The red line above uses the sample model:

\[\hat{y}_i = b_0 + b_1 x_i\]

How do we find that line? That is to say, what are the estimators for \(b_0\) and \(b_1\)?

Since we’ll be using the line of best fit for the remainder of the script, let’s find \((b_0, b_1)\) and discuss how they’re calculated.

The lm() function

Most functions in R that create a model use a formula type object to define the explanatory and response variables. The lm() function (short for linear model) is no exception!

The code chunk below will fit the SLR model for us:

cats_lm <- lm(formula = Hwt ~ Bwt, data = cats)

cats_lm
## 
## Call:
## lm(formula = Hwt ~ Bwt, data = cats)
## 
## Coefficients:
## (Intercept)          Bwt  
##     -0.3567       4.0341

The formula is coded as response ~ explanatory, which reads as ‘The response variable by the explanatory variable’, or Heart Weight by Body Weight.

The intercept is \(b_0 = -0.36\) and \(b_1 = 4.03\). Why are those the ‘best’?

Which line is the best line?

Why is the purple line better than the added red line below?

gg_cats_SLR + 
  # Adding an arbitrary line:
  geom_abline(
    slope = 3, 
    intercept = mean(cats$Hwt) - 3*mean(cats$Bwt),
    color = 'red',
    linetype = 'dashed',
    linewidth = 1
  )

Visually we can tell that the red line is better than the blue line. But how do we show that, statistically speaking?

We define some criteria that the line needs to meet to be the ‘best line’:

  1. The predicted value of \(Y\) when \(X\) is at the mean is the mean of \(Y\):

\[\bar{y} = b_0 + b_1 \bar{x} \]

or, in other words, the line must pass through the coordinate \((\bar{x}, \bar{y})\):

gg_cats_SLR + 
  # Adding an arbitrary line:
  geom_abline(
    slope = 3, 
    intercept = mean(cats$Hwt) - 3*mean(cats$Bwt),
    color = 'red',
    linetype = 'dashed',
    linewidth = 1
  ) + 
  # Adding the 'center' of the plot
  geom_vline(
    mapping = aes(xintercept = mean(Bwt)),
    linewidth = 1,
    color = 'orange'
  ) + 
  geom_hline(
    mapping = aes(yintercept = mean(Hwt)),
    linewidth = 1,
    color = 'steelblue'
  )

Both the red and purple line pass through \((\bar{x}, \bar{y})\), so what other criteria does the best line need to meet?

We want the line to be as close as possible to the points in the data. One initially obvious choice is to try to minimize the average residual, the vertical distance a point is from the line:

# Creating a sample to calculate the residuals:
cats_res_sample <- 
  cats |> 
  slice(c(3, 40, 100, 130, 144)) 

# Adding the predicted heart weight:
cats_res_sample$Hwt_hat <- predict(cats_lm, newdata = cats_res_sample)

cats_res_sample
##   Sex Bwt  Hwt   Hwt_hat
## 1   F 2.0  9.5  7.711463
## 2   F 2.7  8.5 10.535307
## 3   M 3.0 10.0 11.745526
## 4   M 3.4 14.4 13.359151
## 5   M 3.9 20.5 15.376182
# Visualizing the residuals for the 5 sample cats
ggplot(
  data = cats_res_sample,
  mapping = aes(
    x = Bwt,
    y = Hwt
  )
) + 
  # Adding the 'center' of the plot
  geom_vline(
    data = cats,
    mapping = aes(xintercept = mean(Bwt)),
    linewidth = 1,
    color = 'orange'
  ) + 
  geom_hline(
    data = cats,
    mapping = aes(yintercept = mean(Hwt)),
    linewidth = 1,
    color = 'steelblue'
  ) + 
  geom_segment(
    mapping = aes(
      xend = Bwt,
      yend = Hwt_hat
    ), 
    linewidth = 1,
    color = 'orchid',
    linetype = 'dashed'
  ) + 
  geom_smooth(
    data = cats,
    se = F,
    method = 'lm',
    formula = y ~ x,
    color = 'orchid'
  ) + 
  geom_point(size = 3) 

Let’s calculate the average residual for our best fitting line. You can ‘extract’ the residuals using cats_lm$res

round(cats_lm$residuals[1:10], 3)
##      1      2      3      4      5      6      7      8      9     10 
## -0.711 -0.311  1.789 -0.915 -0.815 -0.515 -0.015  0.085  0.185  0.385

Let’s calculate the mean of the residuals, \(\bar{e}\):

mean(cats_lm$residuals)
## [1] -3.58901e-17

which is computationally 0.

How about our other line? What is the average residual of the red line:

\[\widetilde{Hwt} = 2.46 + 3 \times Bwt\]

Note: We use \(\widetilde{Hwt}\) to indicate that it is different that the predicted heart weight from the best model, \(\widehat{Hwt}\)

cats |>
  # Adding the predicted heart weight using the red line and the residual
  mutate(
    hwt_pred = mean(Hwt) - 3*mean(Bwt) + 3 * Bwt,
    hwt_res = Hwt - hwt_pred
  ) |>
  # Averaging the residuals:
  summarize(
    res_avg = mean(hwt_res)
  )
##         res_avg
## 1 -1.782109e-15

which is also computationally 0! So both lines have an average residual of 0, therefore using that criteria would indicate that both are equally good, despite what our eyes told us earlier.

In fact, any line that passes through \((\bar{x}, \bar{y})\) will have an average residual of 0. That sounds weird at first, but you can demonstrate it mathematically:

If a line passes through \((\bar{x}, \bar{y})\), then for any slope, \(b_1\),

\[b_0 = \bar{y} - b_1\bar{x}\]

which gives us the residual as:

\[e_i = y - (\bar{y} - b_1\bar{x} + b_1x_i) \]

\[e_i = (y - \bar{y}) - b_1(x_i - \bar{x})\]

Instead, we minimize the sum (or mean) of the squared residuals!

The Sums of Squares Criterion and the Normal Equations

First, we’ll define the function \(Q\):

\[Q = \sum_{i = 1}^n [Y_i - (\beta_0 - \beta_1X_i)]^2\]

We want to find the values of \((\beta_0, \beta_1)\) that minimize \(Q\)!

Let’s compare the two models we have so far:

cats |>
  # Adding the predicted heart weight using the red line and the residual
  mutate(
    hwt_hat  = cats_lm$fit,                        # Preds from the best line
    hwt_pred = mean(Hwt) - 3*mean(Bwt) + 3 * Bwt   # Preds from other line
  ) |>
  # Averaging the residuals:
  summarize(
    best_SSE = sum((Hwt - hwt_hat)^2),
    not_as_good_SSE = sum((Hwt - hwt_pred)^2)
  )
##   best_SSE not_as_good_SSE
## 1 299.5331        335.5464

Notice that the sums of the squares of the residuals is smaller!

How do we find the values that minimize \(Q\)? Do we try every combination possible and keep the one that makes the sums of squares the smallest?

While we theoretically could, there is a much more efficient way to do that: calculus!

We’ll do the math on the board, but we can take the derivative of \(Q\) with respect to the two \(\beta\)’s individually:

\[\frac{\partial Q}{\partial \beta_0} = \sum_{i = 1}^n (-2)(Y_i - \beta_0 - \beta_1 X_i) \]

\[\frac{\partial Q}{\partial \beta_1} = \sum_{i = 1}^n (-2X_i)(Y_i - \beta_0 - \beta_1 X_i) \]

To find the values of \((\beta_0, \beta_1)\) that minimize Q, we take the partial derivatives, set them equal to 0, then do a little simplification, we can create what we call The Normal Equations:

\[\sum_{i = 1}^n (Y_i - b_0 - b_1 X_i) = 0\]

\[\sum_{i = 1}^n (Y_i - b_0 - b_1 X_i)X_i = 0\]

We can solve the system of the Normal Equations to find the estimators of our model parameters:

\[b_1 = \frac{\sum_{i = 1}^n(X_i - \bar{X})(Y_i - \bar{Y})}{\sum_{i = 1}^n(X_i - \bar{X})^2}= \frac{S_{XY}}{S_{XX}}\] where \(S_{XY}\) is the estimated covariance between \(X, Y\) and \(S_{XX}\) is the estimated variance of \(X\)

and

\[b_0 = \bar{Y} - b_1\bar{X}\]

There are a couple of alternative ways we can write \(b_1\) that are easier to work with:

  1. \[b_1 = \frac{(\sum_{i = 1}^n X_iY_i) - n\bar{X}\bar{Y}}{(\sum_{i = 1}^n X_i^2) - n\bar{X}^2}\]

which is often easier to use when calculating expected values and variances of the estimates, which we’ll do later!

  1. \[b_1 = r\frac{s_Y}{s_X}\]

where \(r\) is the correlation between \(X, Y\), \(s_X\) is the estimated standard deviation of \(X\), and the same for \(Y\)

The fitted model:

Now that we have the functions for how to calculate \(b_0\) and \(b_1\), we can write our model as:

\[\hat{Y_i} = \bar{Y} + r\frac{s_Y}{s_X}(X_i - \bar{X})\]

Calculating the estimates ‘by hand’

Next, let’s calculate the estimates, \(b_0\) and \(b_1\) using the equations above

cats |>
  mutate(
    dev_x = Bwt - mean(Bwt),  # X - X_bar
    dev_y = Hwt - mean(Hwt)   # Y - Y_bar
  ) |>
  
  # Calculating b1 and b0
  summarize(
    # Slope using the 'raw' data
    b1_manual1  = sum(dev_x * dev_y) / sum(dev_x^2),
    b1_manual2  = (sum(Bwt*Hwt) - n() * mean(Bwt)*mean(Hwt))/(sum(Bwt^2) - n() * mean(Bwt)^2),
    b1_formula1 = cov(Bwt, Hwt) / var(Bwt),            # S_XY / S_XX
    b1_formula2 = cor(Bwt, Hwt) * sd(Hwt) / sd(Bwt),   # r (s_Y / s_X)
    
    # Calculating b0
    b0_manual = mean(Hwt) - b1_manual1 * mean(Bwt)
  )
##   b1_manual1 b1_manual2 b1_formula1 b1_formula2  b0_manual
## 1   4.034063   4.034063    4.034063    4.034063 -0.3566624

and we can compare it to the estimates from lm() using cats_lm$coef:

cats_lm$coef
## (Intercept)         Bwt 
##  -0.3566624   4.0340627

They’re the same!

LS0tDQp0aXRsZTogJ0ludHJvZHVjdGlvbjogRml0dGluZyB0aGUgTGluZWFyIE1vZGVsIC0gQ2F0cyBleGFtcGxlJw0KYXV0aG9yOiAiQ2hhcHRlciAxIg0KZGF0ZTogIlNUQSA0MjEwIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGZpZ193aWR0aDogOA0KICAgIGZpZ19oZWlnaHQ6IDYNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdjZW50ZXInKQ0Kc2V0LnNlZWQoNDIxMCkNCiMgTG9hZGluZyBwYWNrYWdlcw0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCiMgU2V0dGluZyB0aGUgZGVmYXVsdCB0aGVtZSB0byB0aGVtZV9idygpDQp0aGVtZV9zZXQodGhlbWVfYncoKSkNCmBgYA0KDQpMb2FkaW5nIHRoZSBkYXRhIGZyb20gdGhlIGBNQVNTYCBwYWNrYWdlDQoNCmBgYHtyfQ0KIyBHZXR0aW5nIHRoZSBkYXRhDQpjYXRzIDwtIE1BU1M6OmNhdHMNCmBgYA0KDQojIyBQbG90dGluZyB0aGUgZGF0YQ0KDQpBbnkgZ29vZCBleGFtaW5hdGlvbiBzdGFydHMgd2l0aCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzLiBMZXQncyBjcmVhdGUgdGhlIHNjYXR0ZXJwbG90IGJldHdlZW4gYm9keSB3ZWlnaHQgYW5kIGhlYXJ0IHdlaWdodDoNCg0KYGBge3J9DQpnZ19jYXRzIDwtIA0KICBnZ3Bsb3QoDQogICAgZGF0YSA9IGNhdHMsDQogICAgbWFwcGluZyA9IGFlcygNCiAgICAgIHggPSBCd3QsDQogICAgICB5ID0gSHd0DQogICAgKQ0KICApICsgDQogICMgQWRkaW5nIHRoZSBwb2ludHMgdG8gdGhlIGdyYXBoOg0KICBnZW9tX3BvaW50KGNvbG9yID0gJ3N0ZWVsYmx1ZScpICsgDQogICMgQWRkaW5nIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGJvZHkgYW5kIGhlYXJ0IHdlaWdodA0KICBhbm5vdGF0ZSgNCiAgICBnZW9tID0gJ3RleHQnLA0KICAgIGxhYmVsID0gcGFzdGUwKCdyID0gJywgcm91bmQoY29yKGNhdHMkQnd0LCBjYXRzJEh3dCksIDMpKSwNCiAgICB4ID0gMi4yLA0KICAgIHkgPSAxNy41LA0KICAgIHNpemUgPSA1LA0KICAgIGNvbG9yID0gJ3JlZCcNCiAgKSArDQogICMgQ2hhbmdpbmcgdGhlIHRoZW1lDQogIHRoZW1lX2J3KCkgKyANCiAgIyBBZGRpbmcgYSB0aXRsZSBhbmQgbGFiZWxpbmcgdGhlIGF4ZXMNCiAgbGFicygNCiAgICB0aXRsZSA9ICdDYXRzOiBCb2R5IHdlaWdodCB2cyBIZWFydCB3ZWlnaHQnLA0KICAgIHggPSAnQm9keSBXZWlnaHQgKGtnKScsDQogICAgeSA9ICdIZWFydCBXZWlnaHQgKGcpJw0KICApDQoNCg0KZ2dfY2F0cw0KYGBgDQoNCiMjIFN0YXRpbmcgdGhlIG1vZGVsOg0KDQpTaW5jZSB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiBib2R5IHdlaWdodCAoJFgkKSBhbmQgaGVhcnQgd2VpZ2h0ICgkWSQpIGlzIGxpbmVhciwgd2UgY2FuIG1vZGVsICRZJCB1c2luZyBhIGxpbmVhciBmdW5jdGlvbiBvZiAkWCQ6DQoNCiQkWV9pPSBcYmV0YV8wICsgXGJldGFfMSBYX2kgKyBcdmFyZXBzaWxvbl9pJCQNCg0KJCRFKFlfaXxYX2kpID0gXGJldGFfMCArIFxiZXRhXzEgWF9pJCQNCg0KKipOb3RlOiBGb3IgdGhlIHJlbWFpbmRlciBvZiB0aGlzIGV4YW1wbGUsICRZX2l8WF9pJCB3aWxsIGJlIHNob3J0ZW5lZCBqdXN0IGFzICRZX2kkIHVubGVzcyBzdGF0ZWQgb3RoZXJ3aXNlKioNCg0KV2UgYXBwbHkgdGhlICpkaXN0cmlidXRpb25hbCBhc3N1bXB0aW9uKiBvbiB0aGUgKipyYW5kb20gZXJyb3IqKiwgJFx2YXJlcHNpbG9uX2kkOg0KDQokJFx2YXJlcHNpbG9uX2kgXHNpbSBOKDAsIFxzaWdtYV4yKSQkDQoNCndoaWNoIGltcGxpZXMgYSBkaXN0cmlidXRpb24gZm9yICRZX2kkOg0KDQokJFx2YXJlcHNpbG9uX2kgXHNpbSBOKDAsIFxzaWdtYV4yKSBccmlnaHRhcnJvdyBZX2kgXHNpbSBOKFxiZXRhXzAgKyBcYmV0YV8xIFhfaSwgXHNpZ21hXjIgKSQkDQoNCiMjIEZpdHRpbmcgdGhlIG1vZGVsDQoNClNpbmNlIHRoZSAqKiRcYmV0YSQqKidzIGFyZSB1bmtub3duIHBvcHVsYXRpb24gcGFyYW1ldGVycywgd2UgbmVlZCB0byBlc3RpbWF0ZSB0aGVtIHVzaW5nIHRoZSBzYW1wbGUgZGF0YS4gU28gaG93IGRvIGZvcm11bGF0ZSB0aGVpciBlc3RpbWF0b3JzPw0KDQokJFxoYXR7XGJldGF9XzAgPSBiXzAkJA0KDQokJFxoYXR7XGJldGF9XzEgPSBiXzEkJA0KDQpUaGUgc2FtcGxlIG1vZGVsIGlzOg0KDQokJFxoYXR7eX1faSA9IGJfMCArIGJfMSB4X2kkJA0KDQphbmQgDQoNCiQkeV9pID0gYl8wICsgYl8xIHhfaSArIGVfaSQkDQoNCndoZXJlICRlX2kkIGlzIGNhbGxlZCB0aGUgKnJlc2lkdWFsKiwgd2hpY2ggaXMgYSBwcmVkaWN0aW9uIG9mIHRoZSAqcmFuZG9tIGVycm9yKiwgJFx2YXJlcHNpbG9uJDoNCg0KJCRcaGF0e1x2YXJlcHNpbG9ufV9pID0gZV9pJCQNCg0KV2UgY2FsY3VsYXRlIHRoZSByZXNpZHVhbCB1c2luZyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBvYnNlcnZlZCB2YWx1ZSwgJHlfaSQsIGFuZCB0aGUgZXN0aW1hdGVkIHZhbHVlLCAkXGhhdHt5fV9pJDoNCg0KJCRlX2kgPSB5X2kgLSBcaGF0e3l9X2kkJA0KDQpXaGF0IGlzIHRoZSBiZXN0IGZpdHRpbmcgbGluZT8NCg0KIyMjIEVzdGltYXRpbmcgdGhlIGxpbmU6DQoNCldlIGNhbiBhZGQgdGhlICoqbGluZSBvZiBiZXN0IGZpdCoqIHRvIHRoZSBzY2F0dGVycGxvdCBjcmVhdGVkIGVhcmxpZXIgKGBnZ19jYXRzYCkgYnkgYWRkaW5nIGBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKWA6DQoNCmBgYHtyfQ0KZ2dfY2F0c19TTFIgPC0gDQogIGdnX2NhdHMgKyANCiAgZ2VvbV9zbW9vdGgoDQogICAgbWV0aG9kID0gJ2xtJywNCiAgICBmb3JtdWxhID0geSB+IHgsICANCiAgICBzZSA9IEYsICAgICAgICAgICAjIFRoaXMgcmVtb3ZlcyBhIGNvbmZpZGVuY2UgYmFuZCB3ZSdsbCBsb29rIGF0IGxhdGVyDQogICAgY29sb3IgPSAnb3JjaGlkJw0KICApDQoNCmdnX2NhdHNfU0xSDQpgYGANCg0KVGhlIHJlZCBsaW5lIGFib3ZlIHVzZXMgdGhlIHNhbXBsZSBtb2RlbDoNCg0KJCRcaGF0e3l9X2kgPSBiXzAgKyBiXzEgeF9pJCQNCg0KSG93IGRvIHdlIGZpbmQgdGhhdCBsaW5lPyBUaGF0IGlzIHRvIHNheSwgd2hhdCBhcmUgdGhlIGVzdGltYXRvcnMgZm9yICRiXzAkIGFuZCAkYl8xJD8NCg0KU2luY2Ugd2UnbGwgYmUgdXNpbmcgdGhlIGxpbmUgb2YgYmVzdCBmaXQgZm9yIHRoZSByZW1haW5kZXIgb2YgdGhlIHNjcmlwdCwgbGV0J3MgZmluZCAkKGJfMCwgYl8xKSQgYW5kIGRpc2N1c3MgaG93IHRoZXkncmUgY2FsY3VsYXRlZC4NCg0KIyMjIyBUaGUgYGxtKClgIGZ1bmN0aW9uDQoNCk1vc3QgZnVuY3Rpb25zIGluICoqUioqIHRoYXQgY3JlYXRlIGEgbW9kZWwgdXNlIGEgYGZvcm11bGFgIHR5cGUgb2JqZWN0IHRvIGRlZmluZSB0aGUgZXhwbGFuYXRvcnkgYW5kIHJlc3BvbnNlIHZhcmlhYmxlcy4gVGhlIGBsbSgpYCBmdW5jdGlvbiAoc2hvcnQgZm9yICpsaW5lYXIgbW9kZWwqKSBpcyBubyBleGNlcHRpb24hIA0KDQpUaGUgY29kZSBjaHVuayBiZWxvdyB3aWxsIGZpdCB0aGUgU0xSIG1vZGVsIGZvciB1czoNCg0KYGBge3IgY2F0c19TTFJfZml0fQ0KY2F0c19sbSA8LSBsbShmb3JtdWxhID0gSHd0IH4gQnd0LCBkYXRhID0gY2F0cykNCg0KY2F0c19sbQ0KYGBgDQoNClRoZSBmb3JtdWxhIGlzIGNvZGVkIGFzIGByZXNwb25zZSB+IGV4cGxhbmF0b3J5YCwgd2hpY2ggcmVhZHMgYXMgJ1RoZSByZXNwb25zZSB2YXJpYWJsZSAqYnkqIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZScsIG9yIEhlYXJ0IFdlaWdodCBieSBCb2R5IFdlaWdodC4NCg0KVGhlIGludGVyY2VwdCBpcyAkYl8wID0gLTAuMzYkIGFuZCAkYl8xID0gNC4wMyQuIFdoeSBhcmUgdGhvc2UgdGhlICdiZXN0Jz8NCg0KIyMjIFdoaWNoIGxpbmUgaXMgdGhlIGJlc3QgbGluZT8NCg0KDQpXaHkgaXMgdGhlIHB1cnBsZSBsaW5lIGJldHRlciB0aGFuIHRoZSBhZGRlZCByZWQgbGluZSBiZWxvdz8NCg0KYGBge3J9DQpnZ19jYXRzX1NMUiArIA0KICAjIEFkZGluZyBhbiBhcmJpdHJhcnkgbGluZToNCiAgZ2VvbV9hYmxpbmUoDQogICAgc2xvcGUgPSAzLCANCiAgICBpbnRlcmNlcHQgPSBtZWFuKGNhdHMkSHd0KSAtIDMqbWVhbihjYXRzJEJ3dCksDQogICAgY29sb3IgPSAncmVkJywNCiAgICBsaW5ldHlwZSA9ICdkYXNoZWQnLA0KICAgIGxpbmV3aWR0aCA9IDENCiAgKQ0KYGBgDQoNClZpc3VhbGx5IHdlIGNhbiB0ZWxsIHRoYXQgdGhlIHJlZCBsaW5lIGlzIGJldHRlciB0aGFuIHRoZSBibHVlIGxpbmUuIEJ1dCBob3cgZG8gd2Ugc2hvdyB0aGF0LCBzdGF0aXN0aWNhbGx5IHNwZWFraW5nPw0KDQpXZSBkZWZpbmUgc29tZSBjcml0ZXJpYSB0aGF0IHRoZSBsaW5lIG5lZWRzIHRvIG1lZXQgdG8gYmUgdGhlICdiZXN0IGxpbmUnOg0KDQoxKSBUaGUgcHJlZGljdGVkIHZhbHVlIG9mICRZJCB3aGVuICRYJCBpcyBhdCB0aGUgbWVhbiBpcyB0aGUgbWVhbiBvZiAkWSQ6DQoNCiQkXGJhcnt5fSA9IGJfMCArIGJfMSBcYmFye3h9ICQkDQoNCm9yLCBpbiBvdGhlciB3b3JkcywgdGhlIGxpbmUgbXVzdCBwYXNzIHRocm91Z2ggdGhlIGNvb3JkaW5hdGUgJChcYmFye3h9LCBcYmFye3l9KSQ6DQoNCmBgYHtyfQ0KZ2dfY2F0c19TTFIgKyANCiAgIyBBZGRpbmcgYW4gYXJiaXRyYXJ5IGxpbmU6DQogIGdlb21fYWJsaW5lKA0KICAgIHNsb3BlID0gMywgDQogICAgaW50ZXJjZXB0ID0gbWVhbihjYXRzJEh3dCkgLSAzKm1lYW4oY2F0cyRCd3QpLA0KICAgIGNvbG9yID0gJ3JlZCcsDQogICAgbGluZXR5cGUgPSAnZGFzaGVkJywNCiAgICBsaW5ld2lkdGggPSAxDQogICkgKyANCiAgIyBBZGRpbmcgdGhlICdjZW50ZXInIG9mIHRoZSBwbG90DQogIGdlb21fdmxpbmUoDQogICAgbWFwcGluZyA9IGFlcyh4aW50ZXJjZXB0ID0gbWVhbihCd3QpKSwNCiAgICBsaW5ld2lkdGggPSAxLA0KICAgIGNvbG9yID0gJ29yYW5nZScNCiAgKSArIA0KICBnZW9tX2hsaW5lKA0KICAgIG1hcHBpbmcgPSBhZXMoeWludGVyY2VwdCA9IG1lYW4oSHd0KSksDQogICAgbGluZXdpZHRoID0gMSwNCiAgICBjb2xvciA9ICdzdGVlbGJsdWUnDQogICkNCmBgYA0KDQpCb3RoIHRoZSByZWQgYW5kIHB1cnBsZSBsaW5lIHBhc3MgdGhyb3VnaCAkKFxiYXJ7eH0sIFxiYXJ7eX0pJCwgc28gd2hhdCBvdGhlciBjcml0ZXJpYSBkb2VzIHRoZSBiZXN0IGxpbmUgbmVlZCB0byBtZWV0Pw0KDQpXZSB3YW50IHRoZSBsaW5lIHRvIGJlIGFzIGNsb3NlIGFzIHBvc3NpYmxlIHRvIHRoZSBwb2ludHMgaW4gdGhlIGRhdGEuIE9uZSBpbml0aWFsbHkgb2J2aW91cyBjaG9pY2UgaXMgdG8gdHJ5IHRvIG1pbmltaXplIHRoZSAqYXZlcmFnZSogcmVzaWR1YWwsIHRoZSB2ZXJ0aWNhbCBkaXN0YW5jZSBhIHBvaW50IGlzIGZyb20gdGhlIGxpbmU6DQoNCmBgYHtyfQ0KIyBDcmVhdGluZyBhIHNhbXBsZSB0byBjYWxjdWxhdGUgdGhlIHJlc2lkdWFsczoNCmNhdHNfcmVzX3NhbXBsZSA8LSANCiAgY2F0cyB8PiANCiAgc2xpY2UoYygzLCA0MCwgMTAwLCAxMzAsIDE0NCkpIA0KDQojIEFkZGluZyB0aGUgcHJlZGljdGVkIGhlYXJ0IHdlaWdodDoNCmNhdHNfcmVzX3NhbXBsZSRId3RfaGF0IDwtIHByZWRpY3QoY2F0c19sbSwgbmV3ZGF0YSA9IGNhdHNfcmVzX3NhbXBsZSkNCg0KY2F0c19yZXNfc2FtcGxlDQoNCiMgVmlzdWFsaXppbmcgdGhlIHJlc2lkdWFscyBmb3IgdGhlIDUgc2FtcGxlIGNhdHMNCmdncGxvdCgNCiAgZGF0YSA9IGNhdHNfcmVzX3NhbXBsZSwNCiAgbWFwcGluZyA9IGFlcygNCiAgICB4ID0gQnd0LA0KICAgIHkgPSBId3QNCiAgKQ0KKSArIA0KICAjIEFkZGluZyB0aGUgJ2NlbnRlcicgb2YgdGhlIHBsb3QNCiAgZ2VvbV92bGluZSgNCiAgICBkYXRhID0gY2F0cywNCiAgICBtYXBwaW5nID0gYWVzKHhpbnRlcmNlcHQgPSBtZWFuKEJ3dCkpLA0KICAgIGxpbmV3aWR0aCA9IDEsDQogICAgY29sb3IgPSAnb3JhbmdlJw0KICApICsgDQogIGdlb21faGxpbmUoDQogICAgZGF0YSA9IGNhdHMsDQogICAgbWFwcGluZyA9IGFlcyh5aW50ZXJjZXB0ID0gbWVhbihId3QpKSwNCiAgICBsaW5ld2lkdGggPSAxLA0KICAgIGNvbG9yID0gJ3N0ZWVsYmx1ZScNCiAgKSArIA0KICBnZW9tX3NlZ21lbnQoDQogICAgbWFwcGluZyA9IGFlcygNCiAgICAgIHhlbmQgPSBCd3QsDQogICAgICB5ZW5kID0gSHd0X2hhdA0KICAgICksIA0KICAgIGxpbmV3aWR0aCA9IDEsDQogICAgY29sb3IgPSAnb3JjaGlkJywNCiAgICBsaW5ldHlwZSA9ICdkYXNoZWQnDQogICkgKyANCiAgZ2VvbV9zbW9vdGgoDQogICAgZGF0YSA9IGNhdHMsDQogICAgc2UgPSBGLA0KICAgIG1ldGhvZCA9ICdsbScsDQogICAgZm9ybXVsYSA9IHkgfiB4LA0KICAgIGNvbG9yID0gJ29yY2hpZCcNCiAgKSArIA0KICBnZW9tX3BvaW50KHNpemUgPSAzKSANCiAgDQpgYGANCg0KTGV0J3MgY2FsY3VsYXRlIHRoZSAqYXZlcmFnZSogcmVzaWR1YWwgZm9yIG91ciBiZXN0IGZpdHRpbmcgbGluZS4gWW91IGNhbiAnZXh0cmFjdCcgdGhlIHJlc2lkdWFscyB1c2luZyBgY2F0c19sbSRyZXNgDQoNCmBgYHtyfQ0Kcm91bmQoY2F0c19sbSRyZXNpZHVhbHNbMToxMF0sIDMpDQpgYGANCg0KTGV0J3MgY2FsY3VsYXRlIHRoZSBtZWFuIG9mIHRoZSByZXNpZHVhbHMsICRcYmFye2V9JDoNCg0KYGBge3J9DQptZWFuKGNhdHNfbG0kcmVzaWR1YWxzKQ0KYGBgDQoNCndoaWNoIGlzIGNvbXB1dGF0aW9uYWxseSAwLiANCg0KSG93IGFib3V0IG91ciBvdGhlciBsaW5lPyBXaGF0IGlzIHRoZSBhdmVyYWdlIHJlc2lkdWFsIG9mIHRoZSByZWQgbGluZToNCg0KJCRcd2lkZXRpbGRle0h3dH0gPSAyLjQ2ICsgMyBcdGltZXMgQnd0JCQNCg0KKipOb3RlOiBXZSB1c2UgJFx3aWRldGlsZGV7SHd0fSQgdG8gaW5kaWNhdGUgdGhhdCBpdCBpcyBkaWZmZXJlbnQgdGhhdCB0aGUgcHJlZGljdGVkIGhlYXJ0IHdlaWdodCBmcm9tIHRoZSBiZXN0IG1vZGVsLCAgJFx3aWRlaGF0e0h3dH0kKioNCg0KYGBge3J9DQpjYXRzIHw+DQogICMgQWRkaW5nIHRoZSBwcmVkaWN0ZWQgaGVhcnQgd2VpZ2h0IHVzaW5nIHRoZSByZWQgbGluZSBhbmQgdGhlIHJlc2lkdWFsDQogIG11dGF0ZSgNCiAgICBod3RfcHJlZCA9IG1lYW4oSHd0KSAtIDMqbWVhbihCd3QpICsgMyAqIEJ3dCwNCiAgICBod3RfcmVzID0gSHd0IC0gaHd0X3ByZWQNCiAgKSB8Pg0KICAjIEF2ZXJhZ2luZyB0aGUgcmVzaWR1YWxzOg0KICBzdW1tYXJpemUoDQogICAgcmVzX2F2ZyA9IG1lYW4oaHd0X3JlcykNCiAgKQ0KYGBgDQoNCndoaWNoIGlzIGFsc28gY29tcHV0YXRpb25hbGx5IDAhIFNvIGJvdGggbGluZXMgaGF2ZSBhbiBhdmVyYWdlIHJlc2lkdWFsIG9mIDAsIHRoZXJlZm9yZSB1c2luZyB0aGF0IGNyaXRlcmlhIHdvdWxkIGluZGljYXRlIHRoYXQgYm90aCBhcmUgZXF1YWxseSBnb29kLCBkZXNwaXRlIHdoYXQgb3VyIGV5ZXMgdG9sZCB1cyBlYXJsaWVyLiANCg0KSW4gZmFjdCwgYW55IGxpbmUgdGhhdCBwYXNzZXMgdGhyb3VnaCAkKFxiYXJ7eH0sIFxiYXJ7eX0pJCB3aWxsIGhhdmUgYW4gYXZlcmFnZSByZXNpZHVhbCBvZiAwLiBUaGF0IHNvdW5kcyB3ZWlyZCBhdCBmaXJzdCwgYnV0IHlvdSBjYW4gZGVtb25zdHJhdGUgaXQgbWF0aGVtYXRpY2FsbHk6DQoNCklmIGEgbGluZSBwYXNzZXMgdGhyb3VnaCAkKFxiYXJ7eH0sIFxiYXJ7eX0pJCwgdGhlbiBmb3IgYW55IHNsb3BlLCAkYl8xJCwgDQoNCiQkYl8wID0gXGJhcnt5fSAtIGJfMVxiYXJ7eH0kJA0KDQp3aGljaCBnaXZlcyB1cyB0aGUgcmVzaWR1YWwgYXM6DQoNCiQkZV9pID0geSAtIChcYmFye3l9IC0gYl8xXGJhcnt4fSArIGJfMXhfaSkgJCQNCg0KJCRlX2kgPSAgKHkgLSBcYmFye3l9KSAtIGJfMSh4X2kgLSBcYmFye3h9KSQkDQoNCkluc3RlYWQsIHdlIG1pbmltaXplIHRoZSBzdW0gKG9yIG1lYW4pIG9mIHRoZSAqc3F1YXJlZCogcmVzaWR1YWxzIQ0KDQojIyMgVGhlIFN1bXMgb2YgU3F1YXJlcyBDcml0ZXJpb24gYW5kIHRoZSBOb3JtYWwgRXF1YXRpb25zDQoNCkZpcnN0LCB3ZSdsbCBkZWZpbmUgdGhlIGZ1bmN0aW9uICRRJDoNCg0KJCRRID0gXHN1bV97aSA9IDF9Xm4gW1lfaSAtIChcYmV0YV8wIC0gXGJldGFfMVhfaSldXjIkJA0KDQpXZSB3YW50IHRvIGZpbmQgdGhlIHZhbHVlcyBvZiAkKFxiZXRhXzAsIFxiZXRhXzEpJCB0aGF0IG1pbmltaXplICRRJCENCg0KTGV0J3MgY29tcGFyZSB0aGUgdHdvIG1vZGVscyB3ZSBoYXZlIHNvIGZhcjoNCg0KYGBge3IgU1NFX2NvbXBhcmV9IA0KY2F0cyB8Pg0KICAjIEFkZGluZyB0aGUgcHJlZGljdGVkIGhlYXJ0IHdlaWdodCB1c2luZyB0aGUgcmVkIGxpbmUgYW5kIHRoZSByZXNpZHVhbA0KICBtdXRhdGUoDQogICAgaHd0X2hhdCAgPSBjYXRzX2xtJGZpdCwgICAgICAgICAgICAgICAgICAgICAgICAjIFByZWRzIGZyb20gdGhlIGJlc3QgbGluZQ0KICAgIGh3dF9wcmVkID0gbWVhbihId3QpIC0gMyptZWFuKEJ3dCkgKyAzICogQnd0ICAgIyBQcmVkcyBmcm9tIG90aGVyIGxpbmUNCiAgKSB8Pg0KICAjIEF2ZXJhZ2luZyB0aGUgcmVzaWR1YWxzOg0KICBzdW1tYXJpemUoDQogICAgYmVzdF9TU0UgPSBzdW0oKEh3dCAtIGh3dF9oYXQpXjIpLA0KICAgIG5vdF9hc19nb29kX1NTRSA9IHN1bSgoSHd0IC0gaHd0X3ByZWQpXjIpDQogICkNCmBgYA0KDQpOb3RpY2UgdGhhdCB0aGUgc3VtcyBvZiB0aGUgc3F1YXJlcyBvZiB0aGUgcmVzaWR1YWxzIGlzIHNtYWxsZXIhDQoNCkhvdyBkbyB3ZSBmaW5kIHRoZSB2YWx1ZXMgdGhhdCBtaW5pbWl6ZSAkUSQ/IERvIHdlIHRyeSBldmVyeSBjb21iaW5hdGlvbiBwb3NzaWJsZSBhbmQga2VlcCB0aGUgb25lIHRoYXQgbWFrZXMgdGhlIHN1bXMgb2Ygc3F1YXJlcyB0aGUgc21hbGxlc3Q/DQoNCldoaWxlIHdlIHRoZW9yZXRpY2FsbHkgY291bGQsIHRoZXJlIGlzIGEgbXVjaCBtb3JlIGVmZmljaWVudCB3YXkgdG8gZG8gdGhhdDogY2FsY3VsdXMhDQoNCldlJ2xsIGRvIHRoZSBtYXRoIG9uIHRoZSBib2FyZCwgYnV0IHdlIGNhbiB0YWtlIHRoZSBkZXJpdmF0aXZlIG9mICRRJCB3aXRoIHJlc3BlY3QgdG8gdGhlIHR3byAkXGJldGEkJ3MgaW5kaXZpZHVhbGx5Og0KDQokJFxmcmFje1xwYXJ0aWFsIFF9e1xwYXJ0aWFsIFxiZXRhXzB9ID0gXHN1bV97aSA9IDF9Xm4gKC0yKShZX2kgLSBcYmV0YV8wIC0gXGJldGFfMSBYX2kpICQkDQoNCiQkXGZyYWN7XHBhcnRpYWwgUX17XHBhcnRpYWwgXGJldGFfMX0gPSBcc3VtX3tpID0gMX1ebiAoLTJYX2kpKFlfaSAtIFxiZXRhXzAgLSBcYmV0YV8xIFhfaSkgJCQNCg0KVG8gZmluZCB0aGUgdmFsdWVzIG9mICQoXGJldGFfMCwgXGJldGFfMSkkIHRoYXQgbWluaW1pemUgUSwgd2UgdGFrZSB0aGUgcGFydGlhbCBkZXJpdmF0aXZlcywgc2V0IHRoZW0gZXF1YWwgdG8gMCwgdGhlbiBkbyBhIGxpdHRsZSBzaW1wbGlmaWNhdGlvbiwgd2UgY2FuIGNyZWF0ZSB3aGF0IHdlIGNhbGwgKipUaGUgTm9ybWFsIEVxdWF0aW9ucyoqOg0KDQokJFxzdW1fe2kgPSAxfV5uIChZX2kgLSBiXzAgLSBiXzEgWF9pKSA9IDAkJA0KDQokJFxzdW1fe2kgPSAxfV5uIChZX2kgLSBiXzAgLSBiXzEgWF9pKVhfaSA9IDAkJA0KDQpXZSBjYW4gc29sdmUgdGhlIHN5c3RlbSBvZiB0aGUgTm9ybWFsIEVxdWF0aW9ucyB0byBmaW5kIHRoZSBlc3RpbWF0b3JzIG9mIG91ciBtb2RlbCBwYXJhbWV0ZXJzOg0KDQokJGJfMSA9IFxmcmFje1xzdW1fe2kgPSAxfV5uKFhfaSAtIFxiYXJ7WH0pKFlfaSAtIFxiYXJ7WX0pfXtcc3VtX3tpID0gMX1ebihYX2kgLSBcYmFye1h9KV4yfT0gXGZyYWN7U197WFl9fXtTX3tYWH19JCQNCndoZXJlICRTX3tYWX0kIGlzIHRoZSBlc3RpbWF0ZWQgY292YXJpYW5jZSBiZXR3ZWVuICRYLCBZJCBhbmQgJFNfe1hYfSQgaXMgdGhlIGVzdGltYXRlZCB2YXJpYW5jZSBvZiAkWCQNCg0KDQoNCmFuZA0KDQokJGJfMCA9IFxiYXJ7WX0gLSBiXzFcYmFye1h9JCQNCg0KDQoNClRoZXJlIGFyZSBhIGNvdXBsZSBvZiBhbHRlcm5hdGl2ZSB3YXlzIHdlIGNhbiB3cml0ZSAkYl8xJCB0aGF0IGFyZSBlYXNpZXIgdG8gd29yayB3aXRoOg0KDQoxKSAkJGJfMSA9IFxmcmFjeyhcc3VtX3tpID0gMX1ebiBYX2lZX2kpIC0gblxiYXJ7WH1cYmFye1l9fXsoXHN1bV97aSA9IDF9Xm4gWF9pXjIpIC0gblxiYXJ7WH1eMn0kJA0KDQp3aGljaCBpcyBvZnRlbiBlYXNpZXIgdG8gdXNlIHdoZW4gY2FsY3VsYXRpbmcgZXhwZWN0ZWQgdmFsdWVzIGFuZCB2YXJpYW5jZXMgb2YgdGhlIGVzdGltYXRlcywgd2hpY2ggd2UnbGwgZG8gbGF0ZXIhDQoNCg0KDQoyKSAkJGJfMSA9IHJcZnJhY3tzX1l9e3NfWH0kJA0KDQp3aGVyZSAkciQgaXMgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gJFgsIFkkLCAkc19YJCBpcyB0aGUgZXN0aW1hdGVkIHN0YW5kYXJkIGRldmlhdGlvbiBvZiAkWCQsIGFuZCB0aGUgc2FtZSBmb3IgJFkkDQoNCg0KIyMjIFRoZSBmaXR0ZWQgbW9kZWw6DQoNCk5vdyB0aGF0IHdlIGhhdmUgdGhlIGZ1bmN0aW9ucyBmb3IgaG93IHRvIGNhbGN1bGF0ZSAkYl8wJCBhbmQgJGJfMSQsIHdlIGNhbiB3cml0ZSBvdXIgbW9kZWwgYXM6DQoNCiQkXGhhdHtZX2l9ID0gXGJhcntZfSArIHJcZnJhY3tzX1l9e3NfWH0oWF9pIC0gXGJhcntYfSkkJA0KDQojIyMgQ2FsY3VsYXRpbmcgdGhlIGVzdGltYXRlcyAnYnkgaGFuZCcNCg0KTmV4dCwgbGV0J3MgY2FsY3VsYXRlIHRoZSBlc3RpbWF0ZXMsICRiXzAkIGFuZCAkYl8xJCB1c2luZyB0aGUgZXF1YXRpb25zIGFib3ZlDQoNCmBgYHtyfQ0KY2F0cyB8Pg0KICBtdXRhdGUoDQogICAgZGV2X3ggPSBCd3QgLSBtZWFuKEJ3dCksICAjIFggLSBYX2Jhcg0KICAgIGRldl95ID0gSHd0IC0gbWVhbihId3QpICAgIyBZIC0gWV9iYXINCiAgKSB8Pg0KICANCiAgIyBDYWxjdWxhdGluZyBiMSBhbmQgYjANCiAgc3VtbWFyaXplKA0KICAgICMgU2xvcGUgdXNpbmcgdGhlICdyYXcnIGRhdGENCiAgICBiMV9tYW51YWwxICA9IHN1bShkZXZfeCAqIGRldl95KSAvIHN1bShkZXZfeF4yKSwNCiAgICBiMV9tYW51YWwyICA9IChzdW0oQnd0Kkh3dCkgLSBuKCkgKiBtZWFuKEJ3dCkqbWVhbihId3QpKS8oc3VtKEJ3dF4yKSAtIG4oKSAqIG1lYW4oQnd0KV4yKSwNCiAgICBiMV9mb3JtdWxhMSA9IGNvdihCd3QsIEh3dCkgLyB2YXIoQnd0KSwgICAgICAgICAgICAjIFNfWFkgLyBTX1hYDQogICAgYjFfZm9ybXVsYTIgPSBjb3IoQnd0LCBId3QpICogc2QoSHd0KSAvIHNkKEJ3dCksICAgIyByIChzX1kgLyBzX1gpDQogICAgDQogICAgIyBDYWxjdWxhdGluZyBiMA0KICAgIGIwX21hbnVhbCA9IG1lYW4oSHd0KSAtIGIxX21hbnVhbDEgKiBtZWFuKEJ3dCkNCiAgKQ0KICANCmBgYA0KDQphbmQgd2UgY2FuIGNvbXBhcmUgaXQgdG8gdGhlIGVzdGltYXRlcyBmcm9tIGBsbSgpYCB1c2luZyBgY2F0c19sbSRjb2VmYDoNCg0KYGBge3J9DQpjYXRzX2xtJGNvZWYNCmBgYA0KDQpUaGV5J3JlIHRoZSBzYW1lIQ0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==