library(car)  # Type I vs Type III ANOVA
Loading required package: carData

Attaching package: 'car'
The following object is masked from 'package:dplyr':

    recode
The following object is masked from 'package:purrr':

    some
library(emmeans)  # Marginal means and trends
Welcome to emmeans.
Caution: You lose important information if you filter this package's results.
See '? untidy'
library(kableExtra)  # Table styling

Attaching package: 'kableExtra'
The following object is masked from 'package:dplyr':

    group_rows
library(patchwork)  # Plot composition
library(scales)

Attaching package: 'scales'
The following object is masked from 'package:purrr':

    discard
The following object is masked from 'package:readr':

    col_factor
library(gridExtra)

Attaching package: 'gridExtra'
The following object is masked from 'package:dplyr':

    combine
library(grid)

Learning objectives

By the end of this in-class session you will be able to:

  1. Recall the five canonical GLM forms from Lecture 25 and how parameterization and contrasts affect interpretation.

  2. Map natural orderings of GLMs and identify which pairs are nested.

  3. Conduct partial F tests for nested GLMs and interpret RSS, df, F, and P.

  4. Explain why p-values can change when multiple models are passed to anova().

  5. Compare three Samara models and select a defensible specification.

  6. Transfer the workflow to a second dataset on body fat with one covariate and one factor.

Quick recall of Lecture 25

Five canonical model forms for one factor A and one covariate x:

Equivalence of parameterizations: two formulas can span the same model space even if coefficients differ. Same fitted values implies same residuals and identical SSE.

Centering covariates: centering turns the intercept into the mean response at average x, which improves interpretability in interaction models.

Comparison on Nested Linear Models

For any linear model \(M0\) nested within \(M1\), we can test:

using an F test.

The hypotheses can be stated in terms of parameters of model \(M1\).

The F test statistic is

\[F = \frac{[RSS_{M0} - RSS_{M1}]/d}{RSS_{M1}/r}\] where d is the difference in the number of (regression) parameters between the models, and r is the residual degrees of freedom for \(M1\).

If \(H_0\) is correct then F has an F distribution with \(d,r\) degrees of freedom.

Extreme large values of the test statistic provide evidence against \(H_0\).

Hence if \(f_{obs}\) is the observed value of the test statistic, and X is a random variable from an \(F_{d,r}\) distribution, then the P-value is given by \(P= P(X \ge f_{obs})\)

This test can be carried out in R using anova(M0, M1) where M0 and M1 are respectively the \(M0\) and \(M1\) fitted models.

The Samara Data revisited

Recall that we were modelling the mean speed of fall of samara (fruit from a maple tree) as a function of the disk loading (a covariate) and the tree from which it fell (a factor) for each fruit.

We fitted separate linear regressions for each of the three trees.

We shall now consider simpler models:

We shall compare these models with each other, and with the separate regressions model.

Grab the data here: samara.csv

unlabelled

Natural ordering of GLMs and nesting

We focus on the nesting relations for one factor Tree and covariate Load:

Single line vs Intercepts-only are not nested when groups differ in mean at x = 0. Valid partial F tests require true nesting and common error variance.

We fit:

M2_sep <- lm(Velocity ~ Tree + Load + Tree:Load, data = Samara)  # separate slopes
M1_par <- lm(Velocity ~ Tree + Load, data = Samara)  # parallel slopes
M0_one <- lm(Velocity ~ Load, data = Samara)  # single line
unlabelled

Model summaries with RSS, df, R^2, AIC, BIC

Visualizing fitted lines

Let’s look at these fits visually to remind ourselves what the data looks like and how the models differ.

unlabelled

Pairwise nested comparisons with partial F tests

# M1 vs M2 tests the interaction
anova(M1_par, M2_sep)
Analysis of Variance Table

Model 1: Velocity ~ Tree + Load
Model 2: Velocity ~ Tree + Load + Tree:Load
  Res.Df     RSS Df Sum of Sq     F  Pr(>F)  
1     31 0.20344                             
2     29 0.16549  2  0.037949 3.325 0.05011 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Pairwise nested comparisons with partial F tests continued

# M0 vs M1 tests Tree after Load
anova(M0_one, M1_par)
Analysis of Variance Table

Model 1: Velocity ~ Load
Model 2: Velocity ~ Tree + Load
  Res.Df     RSS Df Sum of Sq      F Pr(>F)
1     33 0.21476                           
2     31 0.20344  2  0.011322 0.8626 0.4319

Pairwise nested comparisons with partial F tests continued

# M0 vs M2 tests Tree and interaction together
anova(M0_one, M2_sep)
Analysis of Variance Table

Model 1: Velocity ~ Load
Model 2: Velocity ~ Tree + Load + Tree:Load
  Res.Df     RSS Df Sum of Sq      F  Pr(>F)  
1     33 0.21476                              
2     29 0.16549  4  0.049272 2.1585 0.09885 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

All models in one call and denominator effect

anova(M0_one, M1_par, M2_sep)
Analysis of Variance Table

Model 1: Velocity ~ Load
Model 2: Velocity ~ Tree + Load
Model 3: Velocity ~ Tree + Load + Tree:Load
  Res.Df     RSS Df Sum of Sq     F  Pr(>F)  
1     33 0.21476                             
2     31 0.20344  2  0.011322 0.992 0.38306  
3     29 0.16549  2  0.037949 3.325 0.05011 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Note: The p-value for M0 vs M1 can change because the denominator uses the largest model in the call. Decide a maximal model ahead of time or clearly document the sequential plan.

Manual check of the F test

We verify M1 vs M2 by hand.

Remember \[ F = \frac{(RSS_{M1} - RSS_{M2})/d}{RSS_{M2}/r} \]

rss_M1 <- sum(residuals(M1_par)^2)
rss_M2 <- sum(residuals(M2_sep)^2)
d <- attr(logLik(M2_sep), "df") - attr(logLik(M1_par), "df")  # number of added parameters
r <- df.residual(M2_sep)  # residual df in larger model
Fval <- ((rss_M1 - rss_M2)/d)/(rss_M2/r)
pval <- pf(Fval, d, r, lower.tail = FALSE)
Manual partial F reproduces anova()
comparison d r F Pr(>F)
M1 vs M2 2 29 3.325032 0.050107

Diagnostics checklist for the chosen model

chosen <- M1_par
p1 <- ggplot(data.frame(fitted = fitted(chosen), resid = resid(chosen)), aes(fitted,
    resid)) + geom_point() + geom_hline(yintercept = 0, linetype = 2) + labs(title = "Residuals vs Fitted") +
    theme_minimal(base_size = 13)
p2 <- ggplot(data.frame(stdres = rstandard(chosen)), aes(sample = stdres)) + stat_qq() +
    stat_qq_line() + labs(title = "Normal Q-Q") + theme_minimal(base_size = 13)
p1 | p2

unlabelled

Body Fat case study

In this section, we are concerned with modelling a small data set on human body fat.

The response is percentage fat. Age is a numerical covariate, gender is a factor.

Grab the data here: fat.csv

Various model comparisons and model summaries are provided. Decide which is the best model, and write down the fitted model equation for males and females.

Columns: Percent.Fat response, Age numeric, Gender factor.

p_fat <- ggplot(Fat, aes(x = Age, y = Percent.Fat, color = Gender, shape = Gender)) +
    geom_point(size = 2.6, alpha = 0.9) + labs(title = "Percent Body Fat vs Age by Gender",
    x = "Age", y = "Percent Fat") + theme_minimal(base_size = 14) + theme(legend.position = "bottom")
p_fat

unlabelled

Three models and comparisons

F0 <- lm(Percent.Fat ~ Age, data = Fat)
F1 <- lm(Percent.Fat ~ Gender + Age, data = Fat)
F2 <- lm(Percent.Fat ~ Gender + Age + Gender:Age, data = Fat)

anova(F0, F1)
Analysis of Variance Table

Model 1: Percent.Fat ~ Age
Model 2: Percent.Fat ~ Gender + Age
  Res.Df    RSS Df Sum of Sq      F  Pr(>F)  
1     16 529.66                              
2     15 360.88  1    168.79 7.0157 0.01824 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(F1, F2)
Analysis of Variance Table

Model 1: Percent.Fat ~ Gender + Age
Model 2: Percent.Fat ~ Gender + Age + Gender:Age
  Res.Df    RSS Df Sum of Sq      F Pr(>F)  
1     15 360.88                             
2     14 282.02  1    78.853 3.9144 0.0679 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Since we are talking about a one degree of freedom difference, the p-value from anova(F1, F2) is the same as the p-value for the interaction term in F2, namely 0.0679. So we don’t use the interaction model.

Similarly the difference between the first two models is one df, so the p-value from anova(F0, F1) would be the same as for the coefficient of GenderM, namely 0.0182.

So we need to use the second model.

Selected model terms

summary(F1)

Call:
lm(formula = Percent.Fat ~ Gender + Age, data = Fat)

Residuals:
   Min     1Q Median     3Q    Max 
-6.638 -3.455 -1.103  3.297  8.952 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept)  15.0708     6.2243   2.421   0.0286 *
GenderM      -9.7914     3.6966  -2.649   0.0182 *
Age           0.3392     0.1196   2.835   0.0125 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 4.905 on 15 degrees of freedom
Multiple R-squared:  0.7461,    Adjusted R-squared:  0.7123 
F-statistic: 22.04 on 2 and 15 DF,  p-value: 3.424e-05

Fitted equations for the selected model

If we select the parallel slopes F1 with treatment contrasts and Gender coded with F as reference:

co <- coef(F1)
beta0 <- unname(co["(Intercept)"])
beta_age <- unname(co["Age"])
beta_male <- unname(co["GenderM"])
tibble(model = c("Females", "Males"), equation = c(sprintf("Fat = %.2f + %.2f * Age",
    beta0, beta_age), sprintf("Fat = %.2f + %.2f * Age", beta0 + beta_male, beta_age))) |>
    kable(caption = "Fitted equations by Gender (parallel slopes model)") |>
    kable_styling(full_width = FALSE)
Fitted equations by Gender (parallel slopes model)
model equation
Females Fat = 15.07 + 0.34 * Age
Males Fat = 5.28 + 0.34 * Age

Reporting template

LS0tDQp0aXRsZTogIkxlY3R1cmUgMjY6IENvbXBhcmlzb24gb2YgZ2VuZXJhbCBsaW5lYXIgbW9kZWxzIg0Kc3VidGl0bGU6IDE2MS4yNTEgUmVncmVzc2lvbiBNb2RlbGxpbmcNCmF1dGhvcjogIlByZXNlbnRlZCBieSBOaWNrIEtub3dsdG9uIDxOLktub3dsdG9uQG1hc3NleS5hYy5uej4iICANCmRhdGU6ICJXZWVrIDkgb2YgU2VtZXN0ZXIgMiwgYHIgbHVicmlkYXRlOjp5ZWFyKGx1YnJpZGF0ZTo6bm93KCkpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBpb3NsaWRlc19wcmVzZW50YXRpb246DQogICAgd2lkZXNjcmVlbjogdHJ1ZQ0KICAgIHNtYWxsZXI6IHRydWUNCiAgd29yZF9kb2N1bWVudDogZGVmYXVsdA0KICBzbGlkeV9wcmVzZW50YXRpb246IA0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQoNCg0KDQo8IS0tLSBEYXRhIGlzIG9uDQpodHRwczovL3ItcmVzb3VyY2VzLm1hc3NleS5hYy5uei9kYXRhLzE2MTI1MS8NCi0tLT4NCg0KYGBge3Igc2V0dXAsIHB1cmw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KGtuaXRyKQ0Kb3B0c19jaHVuayRzZXQoZGV2PWMoInBuZyIsICJwZGYiKSkNCm9wdHNfY2h1bmskc2V0KGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTcsIGZpZy5wYXRoPSJGaWd1cmVzLyIsIGZpZy5hbHQ9InVubGFiZWxsZWQiKQ0Kb3B0c19jaHVuayRzZXQoY29tbWVudD0iIiwgZmlnLmFsaWduPSJjZW50ZXIiLCB0aWR5PVRSVUUpDQpvcHRpb25zKGtuaXRyLmthYmxlLk5BID0gJycpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoYnJvb20pDQpgYGANCg0KDQo8IS0tLSBEbyBub3QgZWRpdCBhbnl0aGluZyBhYm92ZSB0aGlzIGxpbmUuIC0tLT4NCmBgYHtyIGV4dHJhUGtnc30KbGlicmFyeShjYXIpICAgICAgICAgICMgVHlwZSBJIHZzIFR5cGUgSUlJIEFOT1ZBCmxpYnJhcnkoZW1tZWFucykgICAgICAjIE1hcmdpbmFsIG1lYW5zIGFuZCB0cmVuZHMKbGlicmFyeShrYWJsZUV4dHJhKSAgICMgVGFibGUgc3R5bGluZwpsaWJyYXJ5KHBhdGNod29yaykgICAgIyBQbG90IGNvbXBvc2l0aW9uCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShncmlkKQpgYGAKCgojIyBMZWFybmluZyBvYmplY3RpdmVzCgpCeSB0aGUgZW5kIG9mIHRoaXMgaW4tY2xhc3Mgc2Vzc2lvbiB5b3Ugd2lsbCBiZSBhYmxlIHRvOgoKMS4gUmVjYWxsIHRoZSBmaXZlIGNhbm9uaWNhbCBHTE0gZm9ybXMgZnJvbSBMZWN0dXJlIDI1IGFuZCBob3cgcGFyYW1ldGVyaXphdGlvbiBhbmQgY29udHJhc3RzIGFmZmVjdCBpbnRlcnByZXRhdGlvbi4KCjIuIE1hcCBuYXR1cmFsIG9yZGVyaW5ncyBvZiBHTE1zIGFuZCBpZGVudGlmeSB3aGljaCBwYWlycyBhcmUgbmVzdGVkLgoKMy4gQ29uZHVjdCBwYXJ0aWFsIEYgdGVzdHMgZm9yIG5lc3RlZCBHTE1zIGFuZCBpbnRlcnByZXQgUlNTLCBkZiwgRiwgYW5kIFAuCgo0LiBFeHBsYWluIHdoeSBwLXZhbHVlcyBjYW4gY2hhbmdlIHdoZW4gbXVsdGlwbGUgbW9kZWxzIGFyZSBwYXNzZWQgdG8gYGFub3ZhKClgLgoKNS4gQ29tcGFyZSB0aHJlZSBTYW1hcmEgbW9kZWxzIGFuZCBzZWxlY3QgYSBkZWZlbnNpYmxlIHNwZWNpZmljYXRpb24uCgo2LiBUcmFuc2ZlciB0aGUgd29ya2Zsb3cgdG8gYSBzZWNvbmQgZGF0YXNldCBvbiBib2R5IGZhdCB3aXRoIG9uZSBjb3ZhcmlhdGUgYW5kIG9uZSBmYWN0b3IuCgoKIyMgUXVpY2sgcmVjYWxsIG9mIExlY3R1cmUgMjUKCioqRml2ZSBjYW5vbmljYWwgbW9kZWwgZm9ybXMgZm9yIG9uZSBmYWN0b3IgYEFgIGFuZCBvbmUgY292YXJpYXRlIGB4YDoqKgoKLSBTZXBhcmF0ZSBzbG9wZXM6IGBZIH4gQSArIEE6eGAgb3IgZXF1aXZhbGVudGx5IGBZIH4gQSAqIHhgCi0gUGFyYWxsZWwgc2xvcGVzOiBgWSB+IEEgKyB4YAotIFNpbmdsZSByZWdyZXNzaW9uIGxpbmU6IGBZIH4geGAKLSBJbnRlcmNlcHRzIG9ubHkgYnkgbGV2ZWw6IGBZIH4gQWAKLSBOdWxsIG1vZGVsOiBgWSB+IDFgCgoqKkVxdWl2YWxlbmNlIG9mIHBhcmFtZXRlcml6YXRpb25zOioqIHR3byBmb3JtdWxhcyBjYW4gc3BhbiB0aGUgc2FtZSBtb2RlbCBzcGFjZSBldmVuIGlmIGNvZWZmaWNpZW50cyBkaWZmZXIuIFNhbWUgZml0dGVkIHZhbHVlcyBpbXBsaWVzIHNhbWUgcmVzaWR1YWxzIGFuZCBpZGVudGljYWwgU1NFLgoKKipDZW50ZXJpbmcgY292YXJpYXRlczoqKiBjZW50ZXJpbmcgdHVybnMgdGhlIGludGVyY2VwdCBpbnRvIHRoZSBtZWFuIHJlc3BvbnNlIGF0IGF2ZXJhZ2UgYHhgLCB3aGljaCBpbXByb3ZlcyBpbnRlcnByZXRhYmlsaXR5IGluIGludGVyYWN0aW9uIG1vZGVscy4KCiMjIENvbXBhcmlzb24gb24gTmVzdGVkIExpbmVhciBNb2RlbHMKCkZvciBhbnkgbGluZWFyIG1vZGVsICRNMCQgbmVzdGVkIHdpdGhpbiAkTTEkLCB3ZSBjYW4gdGVzdDoKCiogJEhfMCQ6ICRNMCQgYWRlcXVhdGUgdnMKCiogJEhfMSQ6ICRNMCQgbm90IGFkZXF1YXRlLgoKCnVzaW5nIGFuICpGKiB0ZXN0LgoKVGhlIGh5cG90aGVzZXMgY2FuIGJlIHN0YXRlZCBpbiB0ZXJtcyBvZiBwYXJhbWV0ZXJzIG9mIG1vZGVsICRNMSQuCgpUaGUgKkYqIHRlc3Qgc3RhdGlzdGljIGlzCgokJEYgPSBcZnJhY3tbUlNTX3tNMH0gLSBSU1Nfe00xfV0vZH17UlNTX3tNMX0vcn0kJCB3aGVyZSAqZCogaXMKICAgIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBudW1iZXIgb2YgKHJlZ3Jlc3Npb24pIHBhcmFtZXRlcnMgYmV0d2VlbiB0aGUKICAgIG1vZGVscywgYW5kICpyKiBpcyB0aGUgcmVzaWR1YWwgZGVncmVlcyBvZiBmcmVlZG9tIGZvciAkTTEkLgoKCklmICRIXzAkIGlzIGNvcnJlY3QgdGhlbiAqRiogaGFzIGFuICpGKiBkaXN0cmlidXRpb24gd2l0aCAkZCxyJAogICAgZGVncmVlcyBvZiBmcmVlZG9tLgoKRXh0cmVtZSBsYXJnZSB2YWx1ZXMgb2YgdGhlIHRlc3Qgc3RhdGlzdGljIHByb3ZpZGUgZXZpZGVuY2UgYWdhaW5zdAogICAgJEhfMCQuCgpIZW5jZSBpZiAkZl97b2JzfSQgaXMgdGhlIG9ic2VydmVkIHZhbHVlIG9mIHRoZSB0ZXN0IHN0YXRpc3RpYywKICAgIGFuZCAqWCogaXMgYSByYW5kb20gdmFyaWFibGUgZnJvbSBhbiAkRl97ZCxyfSQgZGlzdHJpYnV0aW9uLAogICAgdGhlbiB0aGUgKlAqLXZhbHVlIGlzIGdpdmVuIGJ5ICRQPSBQKFggXGdlIGZfe29ic30pJAoKVGhpcyB0ZXN0IGNhbiBiZSBjYXJyaWVkIG91dCBpbiBSIHVzaW5nIGBhbm92YShNMCwgTTEpYCB3aGVyZSBgTTBgIGFuZCBgTTFgIGFyZSByZXNwZWN0aXZlbHkgdGhlICRNMCQgYW5kICRNMSQgZml0dGVkIG1vZGVscy4KCiMjIFRoZSBTYW1hcmEgRGF0YSByZXZpc2l0ZWQKClJlY2FsbCB0aGF0IHdlIHdlcmUgbW9kZWxsaW5nIHRoZSBtZWFuIHNwZWVkIG9mIGZhbGwgb2Ygc2FtYXJhCiAgICAoZnJ1aXQgZnJvbSBhIG1hcGxlIHRyZWUpIGFzIGEgZnVuY3Rpb24gb2YgdGhlIGRpc2sgbG9hZGluZyAoYQogICAgY292YXJpYXRlKSBhbmQgdGhlIHRyZWUgZnJvbSB3aGljaCBpdCBmZWxsIChhIGZhY3RvcikgZm9yIGVhY2gKICAgIGZydWl0LgoKV2UgZml0dGVkIHNlcGFyYXRlIGxpbmVhciByZWdyZXNzaW9ucyBmb3IgZWFjaCBvZiB0aGUgdGhyZWUgdHJlZXMuCgpXZSBzaGFsbCBub3cgY29uc2lkZXIgc2ltcGxlciBtb2RlbHM6CiAgICAKLSBQYXJhbGxlbCByZWdyZXNzaW9ucyBmb3IgZWFjaCB0cmVlIChpLmUuIGVxdWFsIHNsb3BlIGZvciBgTG9hZGAgZm9yIGFsbCB0cmVlcyk7Ci0gQSBzaW5nbGUgcmVncmVzc2lvbiBtb2RlbCBmb3IgZXZlcnkgZnJ1aXQgKGkuZS4gZXF1YWwgc2xvcGUgYW5kIGludGVyY2VwdCBmb3IgZWFjaCB0cmVlKS4KICAgIApXZSBzaGFsbCBjb21wYXJlIHRoZXNlIG1vZGVscyB3aXRoIGVhY2ggb3RoZXIsIGFuZCB3aXRoIHRoZSBzZXBhcmF0ZSByZWdyZXNzaW9ucyBtb2RlbC4KCgpHcmFiIHRoZSBkYXRhIGhlcmU6YHIgeGZ1bjo6ZW1iZWRfZmlsZSgiLi4vLi4vZGF0YS9zYW1hcmEuY3N2Iix0ZXh0ID0gIiBzYW1hcmEuY3N2IilgCgpgYGB7ciBTYW1hcmFSZWFkRGF0YSwgZWNobz1GQUxTRX0KU2FtYXJhIDwtIHJlYWRfY3N2KCIuLi8uLi9kYXRhL3NhbWFyYS5jc3YiLHNob3dfY29sX3R5cGVzID0gRkFMU0UpIHw+CiAgdHJhbnNmb3JtKFRyZWUgPSBmYWN0b3IoVHJlZSkpCgpgYGAKCmBgYHtyIHNhbWFyYS1wbG90LCBlY2hvPUZBTFNFfQpwX3NhbWFyYSA8LSBTYW1hcmEgfD4KICBnZ3Bsb3QoYWVzKHggPSBMb2FkLCB5ID0gVmVsb2NpdHksIGNvbG9yID0gVHJlZSwgc2hhcGUgPSBUcmVlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIuNiwgYWxwaGEgPSAwLjkpICsKICBsYWJzKHRpdGxlID0gIlNhbWFyYTogVmVsb2NpdHkgdnMgTG9hZCBieSBUcmVlIiwKICAgICAgIHggPSAiTG9hZCAoZGlzayBsb2FkaW5nKSIsCiAgICAgICB5ID0gIlZlbG9jaXR5IikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTQpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKcF9zYW1hcmEKYGBgCgojIyBOYXR1cmFsIG9yZGVyaW5nIG9mIEdMTXMgYW5kIG5lc3RpbmcKCldlIGZvY3VzIG9uIHRoZSBuZXN0aW5nIHJlbGF0aW9ucyBmb3Igb25lIGZhY3RvciBgVHJlZWAgYW5kIGNvdmFyaWF0ZSBgTG9hZGA6CgotIE51bGwg4oqCIEludGVyY2VwdHMtb25seSDiioIgUGFyYWxsZWwgc2xvcGVzIOKKgiBTZXBhcmF0ZSBzbG9wZXMKCmBTaW5nbGUgbGluZWAgdnMgYEludGVyY2VwdHMtb25seWAgYXJlIG5vdCBuZXN0ZWQgd2hlbiBncm91cHMgZGlmZmVyIGluIG1lYW4gYXQgYHggPSAwYC4gClZhbGlkIHBhcnRpYWwgRiB0ZXN0cyByZXF1aXJlIHRydWUgbmVzdGluZyBhbmQgY29tbW9uIGVycm9yIHZhcmlhbmNlLgoKCldlIGZpdDoKCi0gTTI6IFNlcGFyYXRlIHJlZ3Jlc3Npb25zOiBgVmVsb2NpdHkgfiBUcmVlICsgTG9hZCArIFRyZWU6TG9hZGAKLSBNMTogUGFyYWxsZWwgcmVncmVzc2lvbnM6IGBWZWxvY2l0eSB+IFRyZWUgKyBMb2FkYAotIE0wOiBTaW5nbGUgcmVncmVzc2lvbjogYFZlbG9jaXR5IH4gTG9hZGAKCmBgYHtyIHNhbWFyYS1tb2RlbHN9Ck0yX3NlcCA8LSBsbShWZWxvY2l0eSB+IFRyZWUgKyBMb2FkICsgVHJlZTpMb2FkLCBkYXRhID0gU2FtYXJhKSAgIyBzZXBhcmF0ZSBzbG9wZXMKTTFfcGFyIDwtIGxtKFZlbG9jaXR5IH4gVHJlZSArIExvYWQsIGRhdGEgPSBTYW1hcmEpICAgICAgICAgICAgICAjIHBhcmFsbGVsIHNsb3BlcwpNMF9vbmUgPC0gbG0oVmVsb2NpdHkgfiBMb2FkLCBkYXRhID0gU2FtYXJhKSAgICAgICAgICAgICAgICAgICAgICMgc2luZ2xlIGxpbmUKYGBgCgpgYGB7ciBzYW1hcmEtc3VtbWFyaWVzLCBlY2hvPUZBTFNFLCAgcmVzdWx0cz0nYXNpcyd9CnN1bW1hcmllcyA8LSBiaW5kX3Jvd3MoCiAgZ2xhbmNlKE0yX3NlcCkgfD4gbXV0YXRlKG1vZGVsID0gIlNlcGFyYXRlIChUcmVlICsgTG9hZCArIFRyZWU6TG9hZCkiKSwKICBnbGFuY2UoTTFfcGFyKSB8PiBtdXRhdGUobW9kZWwgPSAiUGFyYWxsZWwgKFRyZWUgKyBMb2FkKSIpLAogIGdsYW5jZShNMF9vbmUpIHw+IG11dGF0ZShtb2RlbCA9ICJTaW5nbGUgKExvYWQpIikKKSB8PgogIG11dGF0ZShyc3MgPSBtYXBfZGJsKGxpc3QoTTJfc2VwLCBNMV9wYXIsIE0wX29uZSksIH4gc3VtKHJlc2lkdWFscygueCleMikpKSB8PgogIHRyYW5zbXV0ZShtb2RlbCwKICAgICAgICAgICAgZGZfcmVzaWQgPSBkZi5yZXNpZHVhbCwKICAgICAgICAgICAgcnNzLAogICAgICAgICAgICByLnNxID0gci5zcXVhcmVkLAogICAgICAgICAgICBhZGouci5zcSA9IGFkai5yLnNxdWFyZWQsCiAgICAgICAgICAgIEFJQyA9IEFJQywKICAgICAgICAgICAgQklDID0gQklDKSB8PgogIGFycmFuZ2UoQUlDKQoKYGBgCgoKYGBge3IgdGJsLXN1bW1hcmllcywgZWNobz1GQUxTRSwgZmlnLmNhcD0iTW9kZWwgc3VtbWFyaWVzIHdpdGggUlNTLCBkZiwgUl4yLCBBSUMsIEJJQyIsZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTQsIG91dC53aWR0aD0iOTAlIiwgZmlnLmFsaWduPSdjZW50ZXInfQoKCnRibCA8LSBzdW1tYXJpZXMgfD4KICBtdXRhdGUoYWNyb3NzKGMocnNzLCByLnNxLCBhZGouci5zcSwgQUlDLCBCSUMpLCB+cm91bmQoLiwgMykpKQoKdGhlbWVfdGJsIDwtIHR0aGVtZV9taW5pbWFsKAogIGJhc2Vfc2l6ZSA9IDE2LCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvbnQgc2l6ZQogIGNvcmUgICAgPSBsaXN0KHBhZGRpbmcgPSB1bml0KGMoNiwgMTApLCAicHQiKSksICAjIGNlbGwgcGFkZGluZwogIGNvbGhlYWQgPSBsaXN0KGZnX3BhcmFtcyA9IGxpc3QoZm9udGZhY2UgPSAiYm9sZCIpKQopCgp0ZyA8LSB0YWJsZUdyb2IodGJsLCByb3dzID0gTlVMTCwgdGhlbWUgPSB0aGVtZV90YmwpCmdyaWQubmV3cGFnZSgpOyBncmlkLmRyYXcodGcpCmBgYAoKIyMgVmlzdWFsaXppbmcgZml0dGVkIGxpbmVzCgpMZXQncyBsb29rIGF0IHRoZXNlIGZpdHMgdmlzdWFsbHkgdG8gcmVtaW5kIG91cnNlbHZlcyB3aGF0IHRoZSBkYXRhIGxvb2tzIGxpa2UgYW5kIGhvdyB0aGUgbW9kZWxzIGRpZmZlci4KCmBgYHtyIGZpdHRlZGxpbmVzLCBlY2hvPUZBTFNFLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9NCwgZHBpPTE1MCwgb3V0LndpZHRoPSIxMDAlIn0KCiMgTWFrZSBhIHZlcnNpb24gb2YgcGxvdF9saW5lcyB0aGF0IG1hcHMgY29sb3IgZm9yIHRoZSBwcmVkaWN0ZWQgbGluZXMKcGxvdF9saW5lc193aXRoX2xlZ2VuZCA8LSBmdW5jdGlvbihtb2RlbCwgdGl0bGUpIHsKICBuZXdkYXQgPC0gdGlkeXI6OmNyb3NzaW5nKAogICAgVHJlZSA9IGZhY3RvcihsZXZlbHMoU2FtYXJhJFRyZWUpLCBsZXZlbHMgPSBsZXZlbHMoU2FtYXJhJFRyZWUpKSwKICAgIExvYWQgPSBzZXEobWluKFNhbWFyYSRMb2FkKSwgbWF4KFNhbWFyYSRMb2FkKSwgbGVuZ3RoLm91dCA9IDEwMCkKICApCiAgbmV3ZGF0JFZlbG9jaXR5X2hhdCA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gbmV3ZGF0KQogICMgRW5zdXJlIGJvdGggcG9pbnRzIGFuZCBsaW5lcyB1c2UgdGhlIHNhbWUgY29sb3IgbWFwcGluZyAoVHJlZSkKICBwIDwtIHBfc2FtYXJhICsKICAgIGdlb21fbGluZShkYXRhID0gbmV3ZGF0LCBhZXMoeCA9IExvYWQsIHkgPSBWZWxvY2l0eV9oYXQsIGNvbG9yID0gVHJlZSksIGxpbmV3aWR0aCA9IDEpCiAgcCArIGxhYnModGl0bGUgPSB0aXRsZSkKfQoKcDEgPC0gcGxvdF9saW5lc193aXRoX2xlZ2VuZChNMl9zZXAsICJTZXBhcmF0ZSBzbG9wZXMiKQpwMiA8LSBwbG90X2xpbmVzX3dpdGhfbGVnZW5kKE0xX3BhciwgIlBhcmFsbGVsIHNsb3BlcyIpCnAzIDwtIHBsb3RfbGluZXNfd2l0aF9sZWdlbmQoTTBfb25lLCAiU2luZ2xlIHJlZ3Jlc3Npb24gbGluZSIpCgojIENvbGxlY3QgYSBzaW5nbGUgbGVnZW5kIGFuZCBwdXQgaXQgYXQgdGhlIGJvdHRvbQoocDEgKyBwMiArIHAzKSArIHBsb3RfbGF5b3V0KG5jb2wgPSAzLCBndWlkZXMgPSAiY29sbGVjdCIpICYgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpgYGAKCgojIyBQYWlyd2lzZSBuZXN0ZWQgY29tcGFyaXNvbnMgd2l0aCBwYXJ0aWFsIEYgdGVzdHMKCmBgYHtyIHNhbWFyYS1hbm92YXN9CiMgTTEgdnMgTTIgdGVzdHMgdGhlIGludGVyYWN0aW9uCmFub3ZhKE0xX3BhciwgTTJfc2VwKQpgYGAKCiMjIFBhaXJ3aXNlIG5lc3RlZCBjb21wYXJpc29ucyB3aXRoIHBhcnRpYWwgRiB0ZXN0cyBjb250aW51ZWQKCmBgYHtyIHNhbWFyYS1hbm92YXMtMn0KIyBNMCB2cyBNMSB0ZXN0cyBUcmVlIGFmdGVyIExvYWQKYW5vdmEoTTBfb25lLCBNMV9wYXIpCmBgYAoKCiMjIFBhaXJ3aXNlIG5lc3RlZCBjb21wYXJpc29ucyB3aXRoIHBhcnRpYWwgRiB0ZXN0cyBjb250aW51ZWQKYGBge3Igc2FtYXJhLWFub3Zhcy0zfQojIE0wIHZzIE0yIHRlc3RzIFRyZWUgYW5kIGludGVyYWN0aW9uIHRvZ2V0aGVyCmFub3ZhKE0wX29uZSwgTTJfc2VwKQpgYGAKCiMjIEFsbCBtb2RlbHMgaW4gb25lIGNhbGwgYW5kIGRlbm9taW5hdG9yIGVmZmVjdAoKYGBge3Igc2FtYXJhLWFub3ZhLTN9CmFub3ZhKE0wX29uZSwgTTFfcGFyLCBNMl9zZXApCmBgYAoKKipOb3RlOioqIFRoZSBwLXZhbHVlIGZvciBgTTBgIHZzIGBNMWAgY2FuIGNoYW5nZSBiZWNhdXNlIHRoZSBkZW5vbWluYXRvciB1c2VzIHRoZSBsYXJnZXN0IG1vZGVsIGluIHRoZSBjYWxsLiBEZWNpZGUgYSBtYXhpbWFsIG1vZGVsIGFoZWFkIG9mIHRpbWUgb3IgY2xlYXJseSBkb2N1bWVudCB0aGUgc2VxdWVudGlhbCBwbGFuLgoKIyMgTWFudWFsIGNoZWNrIG9mIHRoZSBGIHRlc3QKCldlIHZlcmlmeSBgTTFgIHZzIGBNMmAgYnkgaGFuZC4KClJlbWVtYmVyIFxbIEYgPSBcZnJhY3soUlNTX3tNMX0gLSBSU1Nfe00yfSkvZH17UlNTX3tNMn0vcn0gXF0KCmBgYHtyIG1hbnVhbC1GfQpyc3NfTTEgPC0gc3VtKHJlc2lkdWFscyhNMV9wYXIpXjIpCnJzc19NMiA8LSBzdW0ocmVzaWR1YWxzKE0yX3NlcCleMikKZCAgICAgIDwtIGF0dHIobG9nTGlrKE0yX3NlcCksImRmIikgLSBhdHRyKGxvZ0xpayhNMV9wYXIpLCJkZiIpICAjIG51bWJlciBvZiBhZGRlZCBwYXJhbWV0ZXJzCnIgICAgICA8LSBkZi5yZXNpZHVhbChNMl9zZXApICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyByZXNpZHVhbCBkZiBpbiBsYXJnZXIgbW9kZWwKRnZhbCAgIDwtICgocnNzX00xIC0gcnNzX00yKS9kKSAvIChyc3NfTTIvcikKcHZhbCAgIDwtIHBmKEZ2YWwsIGQsIHIsIGxvd2VyLnRhaWwgPSBGQUxTRSkKCmBgYAoKYGBge3IgbWFudWFsLUYtdGFibGUsIGVjaG89RkFMU0V9CnRpYmJsZShjb21wYXJpc29uID0gIk0xIHZzIE0yIiwgZCA9IGQsIHIgPSByLCBGID0gRnZhbCwgYFByKD5GKWAgPSBwdmFsKSB8PgogIGthYmxlKGRpZ2l0cyA9IDYsIGNhcHRpb24gPSAiTWFudWFsIHBhcnRpYWwgRiByZXByb2R1Y2VzIGFub3ZhKCkiKSB8PgogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKQpgYGAKCgoKIyMgRGlhZ25vc3RpY3MgY2hlY2tsaXN0IGZvciB0aGUgY2hvc2VuIG1vZGVsCgpgYGB7ciBkaWFnbm9zdGljc30KY2hvc2VuIDwtIE0xX3BhcgpwMSA8LSBnZ3Bsb3QoZGF0YS5mcmFtZShmaXR0ZWQgPSBmaXR0ZWQoY2hvc2VuKSwgcmVzaWQgPSByZXNpZChjaG9zZW4pKSwgYWVzKGZpdHRlZCwgcmVzaWQpKSArCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAyKSArCiAgbGFicyh0aXRsZSA9ICJSZXNpZHVhbHMgdnMgRml0dGVkIikgKyB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKQpwMiA8LSBnZ3Bsb3QoZGF0YS5mcmFtZShzdGRyZXMgPSByc3RhbmRhcmQoY2hvc2VuKSksIGFlcyhzYW1wbGUgPSBzdGRyZXMpKSArCiAgc3RhdF9xcSgpICsgc3RhdF9xcV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAiTm9ybWFsIFEtUSIpICsgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMykKcDEgfCBwMgpgYGAKCgojIyBCb2R5IEZhdCBjYXNlIHN0dWR5CgpJbiB0aGlzIHNlY3Rpb24sIHdlIGFyZSBjb25jZXJuZWQgd2l0aCBtb2RlbGxpbmcgYSBzbWFsbCBkYXRhIHNldCBvbiBodW1hbiBib2R5IGZhdC4KClRoZSByZXNwb25zZSBpcyBwZXJjZW50YWdlIGZhdC4gIEFnZSBpcyBhIG51bWVyaWNhbCBjb3ZhcmlhdGUsIGdlbmRlciBpcyBhIGZhY3Rvci4KCgpHcmFiIHRoZSBkYXRhIGhlcmU6IGByIHhmdW46OmVtYmVkX2ZpbGUoIi4uLy4uL2RhdGEvZmF0LmNzdiIsdGV4dCA9ICIgZmF0LmNzdiIpYAoKYGBge3IgbG9hZC1mYXQsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CkZhdCA8LSByZWFkcjo6cmVhZF9jc3YoIi4uLy4uL2RhdGEvZmF0LmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCmBgYAoKVmFyaW91cyBtb2RlbCBjb21wYXJpc29ucyBhbmQgbW9kZWwgc3VtbWFyaWVzIGFyZSBwcm92aWRlZC4gRGVjaWRlIHdoaWNoIGlzIHRoZSBiZXN0IG1vZGVsLCBhbmQgd3JpdGUgZG93biB0aGUgZml0dGVkIG1vZGVsIGVxdWF0aW9uIGZvciBtYWxlcyBhbmQgZmVtYWxlcy4KCkNvbHVtbnM6IGBQZXJjZW50LkZhdGAgcmVzcG9uc2UsIGBBZ2VgIG51bWVyaWMsIGBHZW5kZXJgIGZhY3Rvci4KCmBgYHtyIGZhdC1wbG90fQpwX2ZhdCA8LSBnZ3Bsb3QoRmF0LCBhZXMoeCA9IEFnZSwgeSA9IFBlcmNlbnQuRmF0LCBjb2xvciA9IEdlbmRlciwgc2hhcGUgPSBHZW5kZXIpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMi42LCBhbHBoYSA9IDAuOSkgKwogIGxhYnModGl0bGUgPSAiUGVyY2VudCBCb2R5IEZhdCB2cyBBZ2UgYnkgR2VuZGVyIiwKICAgICAgIHggPSAiQWdlIiwgeSA9ICJQZXJjZW50IEZhdCIpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE0KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCnBfZmF0CmBgYAoKIyMgVGhyZWUgbW9kZWxzIGFuZCBjb21wYXJpc29ucwoKYGBge3IgZmF0LW1vZGVsc30KRjAgPC0gbG0oUGVyY2VudC5GYXQgfiBBZ2UsIGRhdGEgPSBGYXQpCkYxIDwtIGxtKFBlcmNlbnQuRmF0IH4gR2VuZGVyICsgQWdlLCBkYXRhID0gRmF0KQpGMiA8LSBsbShQZXJjZW50LkZhdCB+IEdlbmRlciArIEFnZSArIEdlbmRlcjpBZ2UsIGRhdGEgPSBGYXQpCgphbm92YShGMCwgRjEpCmFub3ZhKEYxLCBGMikKCmBgYApTaW5jZSB3ZSBhcmUgdGFsa2luZyBhYm91dCBhIG9uZSBkZWdyZWUgb2YgZnJlZWRvbSBkaWZmZXJlbmNlLCB0aGUgcC12YWx1ZSBmcm9tIAphbm92YShGMSwgRjIpIGlzIHRoZSBzYW1lIGFzIHRoZSBwLXZhbHVlIGZvciB0aGUgaW50ZXJhY3Rpb24gdGVybSBpbiBGMiwgbmFtZWx5IDAuMDY3OS4gU28gd2UgZG9uJ3QgdXNlIHRoZSBpbnRlcmFjdGlvbiBtb2RlbC4gIAoKU2ltaWxhcmx5IHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGZpcnN0IHR3byBtb2RlbHMgaXMgb25lIGRmLCBzbyB0aGUgcC12YWx1ZSBmcm9tIGFub3ZhKEYwLCBGMSkgd291bGQgYmUgdGhlIHNhbWUgYXMgZm9yIHRoZSBjb2VmZmljaWVudCBvZiBHZW5kZXJNLCBuYW1lbHkgMC4wMTgyLiAKClNvIHdlIG5lZWQgdG8gdXNlIHRoZSBzZWNvbmQgbW9kZWwuICAKCiMjIFNlbGVjdGVkIG1vZGVsIHRlcm1zCgpgYGB7ciBmYXQtc3VtbWFyaWVzfQoKc3VtbWFyeShGMSkKCmBgYAoKCiMjIEZpdHRlZCBlcXVhdGlvbnMgZm9yIHRoZSBzZWxlY3RlZCBtb2RlbAoKSWYgd2Ugc2VsZWN0IHRoZSBwYXJhbGxlbCBzbG9wZXMgYEYxYCB3aXRoIHRyZWF0bWVudCBjb250cmFzdHMgYW5kIGBHZW5kZXJgIGNvZGVkIHdpdGggRiBhcyByZWZlcmVuY2U6CgotIEZlbWFsZXM6IGBFW0ZhdF0gPSDOsjAgKyDOsl9BZ2UgKiBBZ2VgCgotIE1hbGVzOiBgRVtGYXRdID0gKM6yMCArIM6yX0dlbmRlck0pICsgzrJfQWdlICogQWdlYAoKYGBge3IgZmF0LWVxdWF0aW9ufQpjbyA8LSBjb2VmKEYxKQpiZXRhMCA8LSB1bm5hbWUoY29bIihJbnRlcmNlcHQpIl0pCmJldGFfYWdlIDwtIHVubmFtZShjb1siQWdlIl0pCmJldGFfbWFsZSA8LSB1bm5hbWUoY29bIkdlbmRlck0iXSkKdGliYmxlKAogIG1vZGVsID0gYygiRmVtYWxlcyIsICJNYWxlcyIpLAogIGVxdWF0aW9uID0gYygKICAgIHNwcmludGYoIkZhdCA9ICUuMmYgKyAlLjJmICogQWdlIiwgYmV0YTAsIGJldGFfYWdlKSwKICAgIHNwcmludGYoIkZhdCA9ICUuMmYgKyAlLjJmICogQWdlIiwgYmV0YTAgKyBiZXRhX21hbGUsIGJldGFfYWdlKQogICkKKSB8PgogIGthYmxlKGNhcHRpb24gPSAiRml0dGVkIGVxdWF0aW9ucyBieSBHZW5kZXIgKHBhcmFsbGVsIHNsb3BlcyBtb2RlbCkiKSB8PgogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKQpgYGAKCgoKCiMjIFJlcG9ydGluZyB0ZW1wbGF0ZQoKLSBTdGF0ZSB0aGUgbW9kZWwgZm9ybXMsIG5lc3RpbmcsIGFuZCB0aGUgc2NpZW50aWZpYyBjb250cmFzdC4KLSBSZXBvcnQgUlNTLCBkZiwgRiwgYW5kIHAgd2l0aCB0aGUgZGVub21pbmF0b3IgbW9kZWwgaWRlbnRpZmllZC4KLSBBZGQgYSBzZW50ZW5jZSBvbiBkaWFnbm9zdGljcyBhbmQgZWZmZWN0IGludGVycHJldGF0aW9uLgotIElmIHR3byBtb2RlbHMgYXJlIGNsb3NlLCBwcmVmZXIgdGhlIHNpbXBsZXIgb25lIGZvciBjbGFyaXR5IHVubGVzcyB0aGUgY29tcGxleCBtb2RlbCBhZGRzIG5lZWRlZCBzY2llbnRpZmljIG51YW5jZS4K