Learning objectives
By the end of this lecture you should be able to:
- Identify when one linear model is nested within another.
- State the hypothesis tested by comparing two nested models.
- Use a partial F test to decide whether the additional terms
in a larger model improve the fit.
- Interpret the change in RSS, degrees of freedom, F
statistic and P-value in a nested-model comparison.
- Explain the difference between
anova(model) and
anova(reduced_model, full_model).
- Apply nested-model comparisons to the Samara and body-fat examples
introduced in the course.
Quick recall of Lecture 25
Lecture 25 gave us three increasingly flexible ways to combine a
factor A with a numerical covariate x. The
factor can do nothing, shift the intercept, or also change the
slope.
The progression was:
Y ~ x
Y ~ A + x
Y ~ A * x
Now we ask the next question: is that extra flexibility actually
supported by the data?
Nested linear models
A model is nested within another model if it can be obtained by
placing restrictions on the parameters of the larger model.
For one factor A and one numerical covariate
x, the nesting relationships are:
Nesting relationships for one factor and one
numerical covariate.
Both Y ~ A and Y ~ x are nested within
Y ~ A + x. However, Y ~ A and
Y ~ x are not generally nested within each other.
The separate-lines model Y ~ A * x contains the
parallel-lines model Y ~ A + x, because setting all
A:x interaction coefficients to zero produces the
parallel-lines model.
We do not need a new test here. Lecture
14’s notes already gave us nested-model comparisons and the partial
F test. We now use the same machinery for the
factor-and-covariate models from Lecture 25, where one scientific effect
can correspond to several coefficients.
Adding terms to a linear model cannot increase its residual sum of
squares. A larger model has more flexibility, so its RSS will be the
same or smaller. The question is whether the decrease in RSS is large
enough to justify the additional parameters, relative to the residual
variation that remains in the larger model.
Let \(M_R\) be a reduced model and
\(M_F\) be a full model, with \(M_R\) nested within \(M_F\). The hypotheses are
\[
H_0:\text{the additional coefficients in }M_F\text{ are all zero}
\]
versus
\[
H_1:\text{at least one of the additional coefficients is non-zero}.
\]
The partial F statistic is
\[
F = \frac{ (RSS_R-RSS_F)/(df_R-df_F) }{ RSS_F/df_F }.
\]
The numerator is the reduction in RSS per additional parameter.
The denominator is the residual mean square from the full model,
which estimates the remaining error variance.
A large F statistic means that the additional terms have
removed a large amount of residual variation relative to the background
residual variation.
Under \(H_0\), the statistic follows
an F distribution with \(df_R-df_F\) and \(df_F\) degrees of freedom.
The models must be nested and fitted to the same observations. The
usual linear-model assumptions also apply.
The Samara data revisited
Recall that we model the mean speed of fall of samara as a function
of disk loading and the tree from which each fruit fell.
Grab the data here:
samara.csv
We use the same three models as Lecture 25:
m1 <- lm(Velocity ~ Load, data = Samara)
m2 <- lm(Velocity ~ Load + TreeF, data = Samara)
m3 <- lm(Velocity ~ Load * TreeF, data = Samara)
Here m1 is one common regression line, m2
gives parallel lines with different intercepts but a common slope, and
m3 gives separate lines with different intercepts and
potentially different slopes.
Comparing the Samara models
First look at what happens as we make the model more flexible:
residual degrees of freedom decrease and RSS gets smaller.
Residual information for the three Samara models
|
Model
|
Formula
|
Residual df
|
RSS
|
|
m1: one line
|
Velocity ~ Load
|
33
|
0.215
|
|
m2: parallel lines
|
Velocity ~ Load + TreeF
|
31
|
0.203
|
|
m3: separate lines
|
Velocity ~ Load * TreeF
|
29
|
0.165
|
Before looking at the tests, compare the fitted lines. The
observations, colours, axes, and scales are the same in all three
panels.
Moving from m1 to m2 allows the trees to
move vertically apart. Moving from m2 to m3
also allows their slopes to differ.
m1 |
Velocity ~ Load |
One common line |
m2 |
Velocity ~ Load + TreeF |
Adds TreeF: 2 additional df, allowing
vertical shifts |
m3 |
Velocity ~ Load * TreeF |
Adds TreeF:Load: 2 additional df, allowing
slope differences |
Now we can ask the two questions we actually care about:
- Does
TreeF shift the lines vertically? Compare
m1 with m2.
- Do the slopes differ by Tree? Compare
m2 with
m3.
Comparison 1: Do the slopes need to differ?
m3 adds the TreeF:Load interaction to
m2. The null hypothesis is therefore that the interaction
coefficients are all zero.
So, once we allow the trees to have different intercepts, is there
evidence that the relationship between Load and Velocity also differs
among trees?
anova(m2, m3)
Analysis of Variance Table
Model 1: Velocity ~ Load + TreeF
Model 2: Velocity ~ Load * TreeF
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
The evidence for different slopes is borderline. At a strict 5%
significance level we would not reject the parallel-slopes model, but a
P-value this close to 0.05 should not be interpreted as
evidence that the slopes are identical.
Comparison 2: If slopes are parallel, do the trees need different
intercepts?
m2 adds TreeF to the model that already
contains Load. This tests whether Tree provides additional
information about Velocity after accounting for Load.
anova(m1, m2)
Analysis of Variance Table
Model 1: Velocity ~ Load
Model 2: Velocity ~ Load + TreeF
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
There is little evidence that Tree requires an additional intercept
shift after accounting for Load.
Comparison 3: Joint comparison
This comparison adds both the Tree main effect and the Tree-by-Load
interaction at once. It tests whether allowing Tree to affect either the
intercept or the slope improves the model compared with one common
regression line.
anova(m1, m3)
Analysis of Variance Table
Model 1: Velocity ~ Load
Model 2: Velocity ~ Load * TreeF
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
This is a different question from carrying out the previous two tests
separately. The joint comparison has a P-value of approximately
0.099.
Why explicit comparisons matter
This is where the orthogonality point from Lecture 23 matters. In an
orthogonal design, changing the order of terms does not change the sums
of squares attributed to them. The Samara data are not orthogonal: the
trees were sampled over different Load distributions, so Tree and Load
overlap in the information they carry.
That means the variation attributed to Tree depends on whether Load
has already been accounted for. The two uses of anova()
below therefore answer different questions.
samara_tree_first <- lm(Velocity ~ TreeF + Load, data = Samara)
samara_load_first <- lm(Velocity ~ Load + TreeF, data = Samara)
anova(samara_tree_first)
Analysis of Variance Table
Response: Velocity
Df Sum Sq Mean Sq F value Pr(>F)
TreeF 2 0.53942 0.269708 41.098 1.913e-09 ***
Load 1 0.31554 0.315542 48.082 8.884e-08 ***
Residuals 31 0.20344 0.006563
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(samara_load_first)
Analysis of Variance Table
Response: Velocity
Df Sum Sq Mean Sq F value Pr(>F)
Load 1 0.84364 0.84364 128.5517 1.471e-12 ***
TreeF 2 0.01132 0.00566 0.8626 0.4319
Residuals 31 0.20344 0.00656
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(model) produces a sequential, or Type I, ANOVA
table. Terms are added to the model in sequence, so in a non-orthogonal
design the sums of squares can depend on the order of the terms.
In contrast, anova(reduced_model, full_model) directly
compares two fitted nested models. The hypothesis is determined by the
terms that are present in the full model but absent from the reduced
model.
If our scientific question is specifically whether Tree adds
information after accounting for Load, the comparison should be written
explicitly:
anova(m1, m2)
Analysis of Variance Table
Model 1: Velocity ~ Load
Model 2: Velocity ~ Load + TreeF
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
Here m1 already contains Load, and
m2 adds TreeF. The test therefore asks whether
Tree improves the model after Load has already been accounted for.
Type II and Type III sums of squares provide other conventions for
adjusted tests in non-orthogonal designs. We do not need them here
because we can state the scientific comparison directly using reduced
and full models.
A small R detail
When several nested models are supplied in one anova()
call, R uses the largest model’s residual mean square as the common
denominator. For a specific scientific hypothesis, use
anova(reduced, full) so the comparison is explicit. This is
why the m1 versus m2 result can differ when
m3 is included in the same call.
anova(m1, m2, m3)
Analysis of Variance Table
Model 1: Velocity ~ Load
Model 2: Velocity ~ Load + TreeF
Model 3: Velocity ~ Load * TreeF
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
Model comparison does not replace model checking. Once we have chosen
the mean structure, the usual LINE diagnostics still need to be
checked.
Body Fat case study
Now do the same thing with the Body Fat data. The response is
percentage body fat, Age is the numerical covariate, and Gender is the
factor.
Grab the data here:
fat.csv
Use the same nested-model logic as for the Samara data. Does Gender
need to change the Age slope, the intercept, or neither?
F0 <- lm(Percent.Fat ~ Age, data = Fat)
F1 <- lm(Percent.Fat ~ Gender + Age, data = Fat)
F2 <- lm(Percent.Fat ~ Gender * Age, data = Fat)
Before looking at the tests, predict which comparison tests the slope
difference and which tests the intercept difference.
Does Gender need to change the slope?
F2 adds one Gender-by-Age interaction coefficient. This
tests whether the Age slope differs between females and males.
anova(F1, F2)
Analysis of Variance Table
Model 1: Percent.Fat ~ Gender + Age
Model 2: Percent.Fat ~ 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
The P-value is approximately 0.068, so there is not strong
evidence at the 5% level that separate slopes are required. We therefore
continue with the parallel-lines model F1.
Does Gender need to change the intercept?
F1 adds the Gender intercept shift to a model that
already contains Age.
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
The P-value is approximately 0.018, providing evidence that
Gender contributes to expected percentage body fat after accounting for
Age.
Of these three prespecified models, F1 is the simplest
model supported by these comparisons: the fitted lines have different
intercepts but a common Age slope.
When the larger model adds exactly one coefficient, the partial
F test and the corresponding two-sided t test are
equivalent:
\[F=t^2.\]
This is why the P-value from anova(F1, F2)
matches the P-value for the Gender:Age coefficient
in summary(F2), and why anova(F0, F1) matches
the Gender coefficient test in summary(F1).
Fitted equations for the retained Body Fat model
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
With females as the reference level:
\[
E(\text{Fat}\mid\text{Female}) = \beta_0+\beta_{\text{Age}}\text{Age}
\]
and
\[
E(\text{Fat}\mid\text{Male}) = (\beta_0+\beta_{\text{GenderM}}) +
\beta_{\text{Age}}\text{Age}.
\]
Body Fat fitted equations
The common Age coefficient gives the slope of both lines.
GenderM gives the vertical difference between the male and
female fitted lines.
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 a nested-model comparison
A good write-up of a nested-model comparison needs five things:
- State the reduced and full models.
- State what additional terms are being tested and translate that into
the scientific question.
- Report the change in RSS and degrees of freedom, the F
statistic and the P-value.
- State whether the data provide evidence that the additional model
structure is needed.
- Interpret the retained model in terms of the response and
explanatory variables.
A non-significant comparison does not prove that the smaller model is
true. It means that the data do not provide sufficient evidence that the
additional structure improves the model.
Everything in this lecture was manageable because the candidate
models were specified in advance. The scientific question told us which
reduced model to compare with which full model.
The next problem is harder. What if there are many plausible
predictors and no single obvious reduced-versus-full comparison? That
takes us from model comparison to model
selection, which is where we go next.
LS0tDQp0aXRsZTogIkxlY3R1cmUgMjY6IENvbXBhcmlzb24gb2YgZ2VuZXJhbCBsaW5lYXIgbW9kZWxzIg0Kc3VidGl0bGU6IDE2MS4yNTEgUmVncmVzc2lvbiBNb2RlbGxpbmcNCmF1dGhvcjogIlByZXNlbnRlZCBieSBOaWNrIEtub3dsdG9uIDxOLktub3dsdG9uQG1hc3NleS5hYy5uej4iICANCmRhdGU6ICJXZWVrIDkgb2YgU2VtZXN0ZXIgMiwgYHIgbHVicmlkYXRlOjp5ZWFyKGx1YnJpZGF0ZTo6bm93KCkpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHRfc3R5bGU6IHB5Z21lbnRzDQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0X3N0eWxlOiBweWdtZW50cw0KICBpb3NsaWRlc19wcmVzZW50YXRpb246DQogICAgd2lkZXNjcmVlbjogdHJ1ZQ0KICAgIHNtYWxsZXI6IHRydWUNCiAgd29yZF9kb2N1bWVudDogZGVmYXVsdA0KICBzbGlkeV9wcmVzZW50YXRpb246IA0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0X3N0eWxlOiBweWdtZW50cw0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQoNCg0KDQo8IS0tLSBEYXRhIGlzIG9uDQpodHRwczovL3ItcmVzb3VyY2VzLm1hc3NleS5hYy5uei9kYXRhLzE2MTI1MS8NCi0tLT4NCg0KYGBge3Igc2V0dXAsIHB1cmw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KGtuaXRyKQ0Kb3B0c19jaHVuayRzZXQoZGV2PWMoInBuZyIsICJwZGYiKSkNCm9wdHNfY2h1bmskc2V0KGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTcsIGZpZy5wYXRoPSJGaWd1cmVzLyIsIGZpZy5hbHQ9InVubGFiZWxsZWQiKQ0Kb3B0c19jaHVuayRzZXQoY29tbWVudD0iIiwgZmlnLmFsaWduPSJjZW50ZXIiLCB0aWR5PVRSVUUpDQpvcHRpb25zKGtuaXRyLmthYmxlLk5BID0gJycpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoYnJvb20pDQpgYGANCg0KDQo8IS0tLSBEbyBub3QgZWRpdCBhbnl0aGluZyBhYm92ZSB0aGlzIGxpbmUuIC0tLT4NCmBgYHtyIGV4dHJhUGtncywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShrYWJsZUV4dHJhKQpgYGAKCiMjIExlYXJuaW5nIG9iamVjdGl2ZXMKCkJ5IHRoZSBlbmQgb2YgdGhpcyBsZWN0dXJlIHlvdSBzaG91bGQgYmUgYWJsZSB0bzoKCjEuIElkZW50aWZ5IHdoZW4gb25lIGxpbmVhciBtb2RlbCBpcyBuZXN0ZWQgd2l0aGluIGFub3RoZXIuCjIuIFN0YXRlIHRoZSBoeXBvdGhlc2lzIHRlc3RlZCBieSBjb21wYXJpbmcgdHdvIG5lc3RlZCBtb2RlbHMuCjMuIFVzZSBhIHBhcnRpYWwgKkYqIHRlc3QgdG8gZGVjaWRlIHdoZXRoZXIgdGhlIGFkZGl0aW9uYWwgdGVybXMgaW4gYSBsYXJnZXIgbW9kZWwgaW1wcm92ZSB0aGUgZml0Lgo0LiBJbnRlcnByZXQgdGhlIGNoYW5nZSBpbiBSU1MsIGRlZ3JlZXMgb2YgZnJlZWRvbSwgKkYqIHN0YXRpc3RpYyBhbmQgKlAqLXZhbHVlIGluIGEgbmVzdGVkLW1vZGVsIGNvbXBhcmlzb24uCjUuIEV4cGxhaW4gdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBgYW5vdmEobW9kZWwpYCBhbmQgYGFub3ZhKHJlZHVjZWRfbW9kZWwsIGZ1bGxfbW9kZWwpYC4KNi4gQXBwbHkgbmVzdGVkLW1vZGVsIGNvbXBhcmlzb25zIHRvIHRoZSBTYW1hcmEgYW5kIGJvZHktZmF0IGV4YW1wbGVzIGludHJvZHVjZWQgaW4gdGhlIGNvdXJzZS4KCiMjIFF1aWNrIHJlY2FsbCBvZiBMZWN0dXJlIDI1CgpMZWN0dXJlIDI1IGdhdmUgdXMgdGhyZWUgaW5jcmVhc2luZ2x5IGZsZXhpYmxlIHdheXMgdG8gY29tYmluZSBhIGZhY3RvciBgQWAgd2l0aCBhIG51bWVyaWNhbCBjb3ZhcmlhdGUgYHhgLiBUaGUgZmFjdG9yIGNhbiBkbyBub3RoaW5nLCBzaGlmdCB0aGUgaW50ZXJjZXB0LCBvciBhbHNvIGNoYW5nZSB0aGUgc2xvcGUuCgpUaGUgcHJvZ3Jlc3Npb24gd2FzOgoKYGBgClkgfiB4ClkgfiBBICsgeApZIH4gQSAqIHgKYGBgCgpOb3cgd2UgYXNrIHRoZSBuZXh0IHF1ZXN0aW9uOiBpcyB0aGF0IGV4dHJhIGZsZXhpYmlsaXR5IGFjdHVhbGx5IHN1cHBvcnRlZCBieSB0aGUgZGF0YT8KCiMjIE5lc3RlZCBsaW5lYXIgbW9kZWxzCgpBIG1vZGVsIGlzIG5lc3RlZCB3aXRoaW4gYW5vdGhlciBtb2RlbCBpZiBpdCBjYW4gYmUgb2J0YWluZWQgYnkgcGxhY2luZyByZXN0cmljdGlvbnMgb24gdGhlIHBhcmFtZXRlcnMgb2YgdGhlIGxhcmdlciBtb2RlbC4KCkZvciBvbmUgZmFjdG9yIGBBYCBhbmQgb25lIG51bWVyaWNhbCBjb3ZhcmlhdGUgYHhgLCB0aGUgbmVzdGluZyByZWxhdGlvbnNoaXBzIGFyZToKCiFbTmVzdGluZyByZWxhdGlvbnNoaXBzIGZvciBvbmUgZmFjdG9yIGFuZCBvbmUgbnVtZXJpY2FsIGNvdmFyaWF0ZS5dKC4uL3Jlc291cmNlcy8yNi1tb2RlbC1jb21wYXJpc29uLTItbmVzdGluZy5zdmcpe3dpZHRoPTcwJX0KCkJvdGggYFkgfiBBYCBhbmQgYFkgfiB4YCBhcmUgbmVzdGVkIHdpdGhpbiBgWSB+IEEgKyB4YC4gSG93ZXZlciwgYFkgfiBBYCBhbmQgYFkgfiB4YCBhcmUgbm90IGdlbmVyYWxseSBuZXN0ZWQgd2l0aGluIGVhY2ggb3RoZXIuCgpUaGUgc2VwYXJhdGUtbGluZXMgbW9kZWwgYFkgfiBBICogeGAgY29udGFpbnMgdGhlIHBhcmFsbGVsLWxpbmVzIG1vZGVsIGBZIH4gQSArIHhgLCBiZWNhdXNlIHNldHRpbmcgYWxsIGBBOnhgIGludGVyYWN0aW9uIGNvZWZmaWNpZW50cyB0byB6ZXJvIHByb2R1Y2VzIHRoZSBwYXJhbGxlbC1saW5lcyBtb2RlbC4KCiMjIFJlY2FsbCBmcm9tIFtMZWN0dXJlIDE0OiB0aGUgcGFydGlhbCAqRiogdGVzdF0oaHR0cHM6Ly9rbm93bHRvbi5jby5uei8xNjEyNTEvbm90ZXMvbW9kZWwtY29tcGFyaXNvbi0xLmh0bWwjZi10ZXN0cy1mb3ItbmVzdGVkLW1vZGVscykKCldlIGRvIG5vdCBuZWVkIGEgbmV3IHRlc3QgaGVyZS4gW0xlY3R1cmUgMTTigJlzIG5vdGVzXShodHRwczovL2tub3dsdG9uLmNvLm56LzE2MTI1MS9ub3Rlcy9tb2RlbC1jb21wYXJpc29uLTEuaHRtbCNmLXRlc3RzLWZvci1uZXN0ZWQtbW9kZWxzKSBhbHJlYWR5IGdhdmUgdXMgbmVzdGVkLW1vZGVsIGNvbXBhcmlzb25zIGFuZCB0aGUgcGFydGlhbCAqRiogdGVzdC4gV2Ugbm93IHVzZSB0aGUgc2FtZSBtYWNoaW5lcnkgZm9yIHRoZSBmYWN0b3ItYW5kLWNvdmFyaWF0ZSBtb2RlbHMgZnJvbSBMZWN0dXJlIDI1LCB3aGVyZSBvbmUgc2NpZW50aWZpYyBlZmZlY3QgY2FuIGNvcnJlc3BvbmQgdG8gc2V2ZXJhbCBjb2VmZmljaWVudHMuCgpBZGRpbmcgdGVybXMgdG8gYSBsaW5lYXIgbW9kZWwgY2Fubm90IGluY3JlYXNlIGl0cyByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcy4gQSBsYXJnZXIgbW9kZWwgaGFzIG1vcmUgZmxleGliaWxpdHksIHNvIGl0cyBSU1Mgd2lsbCBiZSB0aGUgc2FtZSBvciBzbWFsbGVyLiBUaGUgcXVlc3Rpb24gaXMgd2hldGhlciB0aGUgZGVjcmVhc2UgaW4gUlNTIGlzIGxhcmdlIGVub3VnaCB0byBqdXN0aWZ5IHRoZSBhZGRpdGlvbmFsIHBhcmFtZXRlcnMsIHJlbGF0aXZlIHRvIHRoZSByZXNpZHVhbCB2YXJpYXRpb24gdGhhdCByZW1haW5zIGluIHRoZSBsYXJnZXIgbW9kZWwuCgpMZXQgJE1fUiQgYmUgYSByZWR1Y2VkIG1vZGVsIGFuZCAkTV9GJCBiZSBhIGZ1bGwgbW9kZWwsIHdpdGggJE1fUiQgbmVzdGVkIHdpdGhpbiAkTV9GJC4gVGhlIGh5cG90aGVzZXMgYXJlCgokJApIXzA6XHRleHR7dGhlIGFkZGl0aW9uYWwgY29lZmZpY2llbnRzIGluIH1NX0ZcdGV4dHsgYXJlIGFsbCB6ZXJvfQokJAoKdmVyc3VzCgokJApIXzE6XHRleHR7YXQgbGVhc3Qgb25lIG9mIHRoZSBhZGRpdGlvbmFsIGNvZWZmaWNpZW50cyBpcyBub24temVyb30uCiQkCgpUaGUgcGFydGlhbCAqRiogc3RhdGlzdGljIGlzCgokJApGID0gXGZyYWN7IChSU1NfUi1SU1NfRikvKGRmX1ItZGZfRikgfXsgUlNTX0YvZGZfRiB9LgokJAoKVGhlIG51bWVyYXRvciBpcyB0aGUgcmVkdWN0aW9uIGluIFJTUyBwZXIgYWRkaXRpb25hbCBwYXJhbWV0ZXIuCgpUaGUgZGVub21pbmF0b3IgaXMgdGhlIHJlc2lkdWFsIG1lYW4gc3F1YXJlIGZyb20gdGhlIGZ1bGwgbW9kZWwsIHdoaWNoIGVzdGltYXRlcyB0aGUgcmVtYWluaW5nIGVycm9yIHZhcmlhbmNlLgoKQSBsYXJnZSAqRiogc3RhdGlzdGljIG1lYW5zIHRoYXQgdGhlIGFkZGl0aW9uYWwgdGVybXMgaGF2ZSByZW1vdmVkIGEgbGFyZ2UgYW1vdW50IG9mIHJlc2lkdWFsIHZhcmlhdGlvbiByZWxhdGl2ZSB0byB0aGUgYmFja2dyb3VuZCByZXNpZHVhbCB2YXJpYXRpb24uCgpVbmRlciAkSF8wJCwgdGhlIHN0YXRpc3RpYyBmb2xsb3dzIGFuICpGKiBkaXN0cmlidXRpb24gd2l0aCAkZGZfUi1kZl9GJCBhbmQgJGRmX0YkIGRlZ3JlZXMgb2YgZnJlZWRvbS4KClRoZSBtb2RlbHMgbXVzdCBiZSBuZXN0ZWQgYW5kIGZpdHRlZCB0byB0aGUgc2FtZSBvYnNlcnZhdGlvbnMuIFRoZSB1c3VhbCBsaW5lYXItbW9kZWwgYXNzdW1wdGlvbnMgYWxzbyBhcHBseS4KCiMjIFRoZSBTYW1hcmEgZGF0YSByZXZpc2l0ZWQKClJlY2FsbCB0aGF0IHdlIG1vZGVsIHRoZSBtZWFuIHNwZWVkIG9mIGZhbGwgb2Ygc2FtYXJhIGFzIGEgZnVuY3Rpb24gb2YgZGlzayBsb2FkaW5nIGFuZCB0aGUgdHJlZSBmcm9tIHdoaWNoIGVhY2ggZnJ1aXQgZmVsbC4KCkdyYWIgdGhlIGRhdGEgaGVyZTogYHIgeGZ1bjo6ZW1iZWRfZmlsZSgiLi4vZGF0YS9zYW1hcmEuY3N2Iix0ZXh0ID0gIiBzYW1hcmEuY3N2IilgCgpgYGB7ciBTYW1hcmFSZWFkRGF0YSwgZWNobz1GQUxTRX0KU2FtYXJhIDwtIHJlYWRfY3N2KCIuLi9kYXRhL3NhbWFyYS5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKSB8PgogIG11dGF0ZShUcmVlRiA9IGZhY3RvcihUcmVlKSkKYGBgCgpXZSB1c2UgdGhlIHNhbWUgdGhyZWUgbW9kZWxzIGFzIExlY3R1cmUgMjU6CgpgYGB7ciBzYW1hcmEtbW9kZWxzfQptMSA8LSBsbShWZWxvY2l0eSB+IExvYWQsIGRhdGEgPSBTYW1hcmEpCm0yIDwtIGxtKFZlbG9jaXR5IH4gTG9hZCArIFRyZWVGLCBkYXRhID0gU2FtYXJhKQptMyA8LSBsbShWZWxvY2l0eSB+IExvYWQgKiBUcmVlRiwgZGF0YSA9IFNhbWFyYSkKYGBgCgpIZXJlIGBtMWAgaXMgb25lIGNvbW1vbiByZWdyZXNzaW9uIGxpbmUsIGBtMmAgZ2l2ZXMgcGFyYWxsZWwgbGluZXMgd2l0aCBkaWZmZXJlbnQgaW50ZXJjZXB0cyBidXQgYSBjb21tb24gc2xvcGUsIGFuZCBgbTNgIGdpdmVzIHNlcGFyYXRlIGxpbmVzIHdpdGggZGlmZmVyZW50IGludGVyY2VwdHMgYW5kIHBvdGVudGlhbGx5IGRpZmZlcmVudCBzbG9wZXMuCgojIyBDb21wYXJpbmcgdGhlIFNhbWFyYSBtb2RlbHMKCkZpcnN0IGxvb2sgYXQgd2hhdCBoYXBwZW5zIGFzIHdlIG1ha2UgdGhlIG1vZGVsIG1vcmUgZmxleGlibGU6IHJlc2lkdWFsIGRlZ3JlZXMgb2YgZnJlZWRvbSBkZWNyZWFzZSBhbmQgUlNTIGdldHMgc21hbGxlci4KCmBgYHtyIHNhbWFyYS1zdW1tYXJpZXMsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQpzdW1tYXJpZXMgPC0gdGliYmxlKAogIE1vZGVsID0gYygibTE6IG9uZSBsaW5lIiwgIm0yOiBwYXJhbGxlbCBsaW5lcyIsICJtMzogc2VwYXJhdGUgbGluZXMiKSwKICBGb3JtdWxhID0gYygiVmVsb2NpdHkgfiBMb2FkIiwgIlZlbG9jaXR5IH4gTG9hZCArIFRyZWVGIiwgIlZlbG9jaXR5IH4gTG9hZCAqIFRyZWVGIiksCiAgYFJlc2lkdWFsIGRmYCA9IGMoZGYucmVzaWR1YWwobTEpLCBkZi5yZXNpZHVhbChtMiksIGRmLnJlc2lkdWFsKG0zKSksCiAgUlNTID0gYyhzdW0ocmVzaWR1YWxzKG0xKV4yKSwgc3VtKHJlc2lkdWFscyhtMileMiksIHN1bShyZXNpZHVhbHMobTMpXjIpKQopCgpzdW1tYXJpZXMgfD4KICBtdXRhdGUoUlNTID0gcm91bmQoUlNTLCAzKSkgfD4KICBrYWJsZShjYXB0aW9uID0gIlJlc2lkdWFsIGluZm9ybWF0aW9uIGZvciB0aGUgdGhyZWUgU2FtYXJhIG1vZGVscyIpIHw+CiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKQmVmb3JlIGxvb2tpbmcgYXQgdGhlIHRlc3RzLCBjb21wYXJlIHRoZSBmaXR0ZWQgbGluZXMuIFRoZSBvYnNlcnZhdGlvbnMsIGNvbG91cnMsIGF4ZXMsIGFuZCBzY2FsZXMgYXJlIHRoZSBzYW1lIGluIGFsbCB0aHJlZSBwYW5lbHMuCgpgYGB7ciBmaXR0ZWRsaW5lcywgZWNobz1GQUxTRSwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTQsIGRwaT0xNTAsIG91dC53aWR0aD0iMTAwJSIsIGZpZy5jYXA9IlRoZSBzYW1lIFNhbWFyYSBvYnNlcnZhdGlvbnMgd2l0aCBmaXR0ZWQgbGluZXMgZnJvbSB0aGUgdGhyZWUgY2FuZGlkYXRlIG1vZGVscy4ifQptb2RlbF9sYWJlbHMgPC0gYygKICBtMSA9ICJtMTogb25lIGxpbmUiLAogIG0yID0gIm0yOiBwYXJhbGxlbCBsaW5lcyIsCiAgbTMgPSAibTM6IHNlcGFyYXRlIGxpbmVzIgopCgpwbG90X2RhdGEgPC0gYmluZF9yb3dzKAogIFNhbWFyYSB8PiBtdXRhdGUobW9kZWwgPSB1bm5hbWUobW9kZWxfbGFiZWxzWyJtMSJdKSksCiAgU2FtYXJhIHw+IG11dGF0ZShtb2RlbCA9IHVubmFtZShtb2RlbF9sYWJlbHNbIm0yIl0pKSwKICBTYW1hcmEgfD4gbXV0YXRlKG1vZGVsID0gdW5uYW1lKG1vZGVsX2xhYmVsc1sibTMiXSkpCikgfD4KICBtdXRhdGUobW9kZWwgPSBmYWN0b3IobW9kZWwsIGxldmVscyA9IHVubmFtZShtb2RlbF9sYWJlbHMpKSkKCmxvYWRfZ3JpZCA8LSB0aWJibGUoCiAgTG9hZCA9IHNlcShtaW4oU2FtYXJhJExvYWQpLCBtYXgoU2FtYXJhJExvYWQpLCBsZW5ndGgub3V0ID0gMTAwKQopCgpwcmVkX20xIDwtIGxvYWRfZ3JpZCB8PgogIG11dGF0ZSgKICAgIFZlbG9jaXR5ID0gcHJlZGljdChtMSwgbmV3ZGF0YSA9IGxvYWRfZ3JpZCksCiAgICBtb2RlbCA9IHVubmFtZShtb2RlbF9sYWJlbHNbIm0xIl0pCiAgKQoKbmV3X20yIDwtIHRpZHlyOjpleHBhbmRfZ3JpZChMb2FkID0gbG9hZF9ncmlkJExvYWQsIFRyZWVGID0gbGV2ZWxzKFNhbWFyYSRUcmVlRikpCnByZWRfbTIgPC0gbmV3X20yIHw+CiAgbXV0YXRlKAogICAgVmVsb2NpdHkgPSBwcmVkaWN0KG0yLCBuZXdkYXRhID0gbmV3X20yKSwKICAgIG1vZGVsID0gdW5uYW1lKG1vZGVsX2xhYmVsc1sibTIiXSkKICApCgpuZXdfbTMgPC0gdGlkeXI6OmV4cGFuZF9ncmlkKExvYWQgPSBsb2FkX2dyaWQkTG9hZCwgVHJlZUYgPSBsZXZlbHMoU2FtYXJhJFRyZWVGKSkKcHJlZF9tMyA8LSBuZXdfbTMgfD4KICBtdXRhdGUoCiAgICBWZWxvY2l0eSA9IHByZWRpY3QobTMsIG5ld2RhdGEgPSBuZXdfbTMpLAogICAgbW9kZWwgPSB1bm5hbWUobW9kZWxfbGFiZWxzWyJtMyJdKQogICkKCnNhbWFyYV9tb2RlbF9wbG90IDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KAogICAgZGF0YSA9IHBsb3RfZGF0YSwKICAgIG1hcHBpbmcgPSBhZXMoeCA9IExvYWQsIHkgPSBWZWxvY2l0eSwgY29sb3VyID0gVHJlZUYsIHNoYXBlID0gVHJlZUYpLAogICAgc2l6ZSA9IDIuMiwKICAgIGFscGhhID0gMC44CiAgKSArCiAgZ2VvbV9saW5lKAogICAgZGF0YSA9IHByZWRfbTEsCiAgICBtYXBwaW5nID0gYWVzKHggPSBMb2FkLCB5ID0gVmVsb2NpdHkpLAogICAgY29sb3VyID0gImJsYWNrIiwKICAgIGxpbmV3aWR0aCA9IDEKICApICsKICBnZW9tX2xpbmUoCiAgICBkYXRhID0gcHJlZF9tMiwKICAgIG1hcHBpbmcgPSBhZXMoeCA9IExvYWQsIHkgPSBWZWxvY2l0eSwgY29sb3VyID0gVHJlZUYsIGdyb3VwID0gVHJlZUYpLAogICAgbGluZXdpZHRoID0gMQogICkgKwogIGdlb21fbGluZSgKICAgIGRhdGEgPSBwcmVkX20zLAogICAgbWFwcGluZyA9IGFlcyh4ID0gTG9hZCwgeSA9IFZlbG9jaXR5LCBjb2xvdXIgPSBUcmVlRiwgZ3JvdXAgPSBUcmVlRiksCiAgICBsaW5ld2lkdGggPSAxCiAgKSArCiAgZmFjZXRfd3JhcCh+IG1vZGVsLCBucm93ID0gMSkgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsKICBsYWJzKHggPSAiTG9hZCIsIHkgPSAiVmVsb2NpdHkiLCBjb2xvdXIgPSAiVHJlZSIsIHNoYXBlID0gIlRyZWUiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKc2FtYXJhX21vZGVsX3Bsb3QKYGBgCgpNb3ZpbmcgZnJvbSBgbTFgIHRvIGBtMmAgYWxsb3dzIHRoZSB0cmVlcyB0byBtb3ZlIHZlcnRpY2FsbHkgYXBhcnQuIE1vdmluZyBmcm9tIGBtMmAgdG8gYG0zYCBhbHNvIGFsbG93cyB0aGVpciBzbG9wZXMgdG8gZGlmZmVyLgoKfCBNb2RlbCB8IEZvcm11bGEgfCBXaGF0IGNoYW5nZWQ/IHwKfDotLXw6LS18Oi0tfAp8IGBtMWAgfCBgVmVsb2NpdHkgfiBMb2FkYCB8IE9uZSBjb21tb24gbGluZSB8CnwgYG0yYCB8IGBWZWxvY2l0eSB+IExvYWQgKyBUcmVlRmAgfCBBZGRzIGBUcmVlRmA6IDIgYWRkaXRpb25hbCBkZiwgYWxsb3dpbmcgdmVydGljYWwgc2hpZnRzIHwKfCBgbTNgIHwgYFZlbG9jaXR5IH4gTG9hZCAqIFRyZWVGYCB8IEFkZHMgYFRyZWVGOkxvYWRgOiAyIGFkZGl0aW9uYWwgZGYsIGFsbG93aW5nIHNsb3BlIGRpZmZlcmVuY2VzIHwKCk5vdyB3ZSBjYW4gYXNrIHRoZSB0d28gcXVlc3Rpb25zIHdlIGFjdHVhbGx5IGNhcmUgYWJvdXQ6CgotIERvZXMgYFRyZWVGYCBzaGlmdCB0aGUgbGluZXMgdmVydGljYWxseT8gQ29tcGFyZSBgbTFgIHdpdGggYG0yYC4KLSBEbyB0aGUgc2xvcGVzIGRpZmZlciBieSBUcmVlPyBDb21wYXJlIGBtMmAgd2l0aCBgbTNgLgoKIyMgQ29tcGFyaXNvbiAxOiBEbyB0aGUgc2xvcGVzIG5lZWQgdG8gZGlmZmVyPwoKYG0zYCBhZGRzIHRoZSBgVHJlZUY6TG9hZGAgaW50ZXJhY3Rpb24gdG8gYG0yYC4gVGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0aGVyZWZvcmUgdGhhdCB0aGUgaW50ZXJhY3Rpb24gY29lZmZpY2llbnRzIGFyZSBhbGwgemVyby4KClNvLCBvbmNlIHdlIGFsbG93IHRoZSB0cmVlcyB0byBoYXZlIGRpZmZlcmVudCBpbnRlcmNlcHRzLCBpcyB0aGVyZSBldmlkZW5jZSB0aGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBMb2FkIGFuZCBWZWxvY2l0eSBhbHNvIGRpZmZlcnMgYW1vbmcgdHJlZXM/CgpgYGB7ciBzYW1hcmEtYW5vdmEtbTItbTN9CmFub3ZhKG0yLCBtMykKYGBgCgpUaGUgZXZpZGVuY2UgZm9yIGRpZmZlcmVudCBzbG9wZXMgaXMgYm9yZGVybGluZS4gQXQgYSBzdHJpY3QgNSUgc2lnbmlmaWNhbmNlIGxldmVsIHdlIHdvdWxkIG5vdCByZWplY3QgdGhlIHBhcmFsbGVsLXNsb3BlcyBtb2RlbCwgYnV0IGEgKlAqLXZhbHVlIHRoaXMgY2xvc2UgdG8gMC4wNSBzaG91bGQgbm90IGJlIGludGVycHJldGVkIGFzIGV2aWRlbmNlIHRoYXQgdGhlIHNsb3BlcyBhcmUgaWRlbnRpY2FsLgoKIyMgQ29tcGFyaXNvbiAyOiBJZiBzbG9wZXMgYXJlIHBhcmFsbGVsLCBkbyB0aGUgdHJlZXMgbmVlZCBkaWZmZXJlbnQgaW50ZXJjZXB0cz8KCmBtMmAgYWRkcyBgVHJlZUZgIHRvIHRoZSBtb2RlbCB0aGF0IGFscmVhZHkgY29udGFpbnMgYExvYWRgLiBUaGlzIHRlc3RzIHdoZXRoZXIgVHJlZSBwcm92aWRlcyBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGFib3V0IFZlbG9jaXR5IGFmdGVyIGFjY291bnRpbmcgZm9yIExvYWQuCgpgYGB7ciBzYW1hcmEtYW5vdmEtbTEtbTJ9CmFub3ZhKG0xLCBtMikKYGBgCgpUaGVyZSBpcyBsaXR0bGUgZXZpZGVuY2UgdGhhdCBUcmVlIHJlcXVpcmVzIGFuIGFkZGl0aW9uYWwgaW50ZXJjZXB0IHNoaWZ0IGFmdGVyIGFjY291bnRpbmcgZm9yIExvYWQuCgojIyBDb21wYXJpc29uIDM6IEpvaW50IGNvbXBhcmlzb24KClRoaXMgY29tcGFyaXNvbiBhZGRzIGJvdGggdGhlIFRyZWUgbWFpbiBlZmZlY3QgYW5kIHRoZSBUcmVlLWJ5LUxvYWQgaW50ZXJhY3Rpb24gYXQgb25jZS4gSXQgdGVzdHMgd2hldGhlciBhbGxvd2luZyBUcmVlIHRvIGFmZmVjdCBlaXRoZXIgdGhlIGludGVyY2VwdCBvciB0aGUgc2xvcGUgaW1wcm92ZXMgdGhlIG1vZGVsIGNvbXBhcmVkIHdpdGggb25lIGNvbW1vbiByZWdyZXNzaW9uIGxpbmUuCgpgYGB7ciBzYW1hcmEtYW5vdmEtbTEtbTN9CmFub3ZhKG0xLCBtMykKYGBgCgpUaGlzIGlzIGEgZGlmZmVyZW50IHF1ZXN0aW9uIGZyb20gY2Fycnlpbmcgb3V0IHRoZSBwcmV2aW91cyB0d28gdGVzdHMgc2VwYXJhdGVseS4gVGhlIGpvaW50IGNvbXBhcmlzb24gaGFzIGEgKlAqLXZhbHVlIG9mIGFwcHJveGltYXRlbHkgMC4wOTkuCgojIyBXaHkgZXhwbGljaXQgY29tcGFyaXNvbnMgbWF0dGVyCgpUaGlzIGlzIHdoZXJlIHRoZSBvcnRob2dvbmFsaXR5IHBvaW50IGZyb20gTGVjdHVyZSAyMyBtYXR0ZXJzLiBJbiBhbiBvcnRob2dvbmFsIGRlc2lnbiwgY2hhbmdpbmcgdGhlIG9yZGVyIG9mIHRlcm1zIGRvZXMgbm90IGNoYW5nZSB0aGUgc3VtcyBvZiBzcXVhcmVzIGF0dHJpYnV0ZWQgdG8gdGhlbS4gVGhlIFNhbWFyYSBkYXRhIGFyZSBub3Qgb3J0aG9nb25hbDogdGhlIHRyZWVzIHdlcmUgc2FtcGxlZCBvdmVyIGRpZmZlcmVudCBMb2FkIGRpc3RyaWJ1dGlvbnMsIHNvIFRyZWUgYW5kIExvYWQgb3ZlcmxhcCBpbiB0aGUgaW5mb3JtYXRpb24gdGhleSBjYXJyeS4KCmBgYHtyIHNhbWFyYS1sb2FkLWJ5LXRyZWUsIGVjaG89RkFMU0UsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTQsIG91dC53aWR0aD0iNjUlIiwgZmlnLmNhcD0iVGhlIFNhbWFyYSBkYXRhIGhhdmUgZGlmZmVyZW50IExvYWQgZGlzdHJpYnV0aW9ucyBhY3Jvc3MgdHJlZXMuIn0KZ2dwbG90KFNhbWFyYSwgYWVzKHggPSBUcmVlRiwgeSA9IExvYWQsIGNvbG91ciA9IFRyZWVGKSkgKwogIGdlb21faml0dGVyKHdpZHRoID0gMC4wOCwgaGVpZ2h0ID0gMCwgYWxwaGEgPSAwLjc1LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc3RhdF9zdW1tYXJ5KGZ1biA9IG1lYW4sIGdlb20gPSAicG9pbnQiLCBzaXplID0gNCwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGxhYnMoeCA9ICJUcmVlIiwgeSA9ICJMb2FkIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTMpCmBgYAoKVGhhdCBtZWFucyB0aGUgdmFyaWF0aW9uIGF0dHJpYnV0ZWQgdG8gVHJlZSBkZXBlbmRzIG9uIHdoZXRoZXIgTG9hZCBoYXMgYWxyZWFkeSBiZWVuIGFjY291bnRlZCBmb3IuIFRoZSB0d28gdXNlcyBvZiBgYW5vdmEoKWAgYmVsb3cgdGhlcmVmb3JlIGFuc3dlciBkaWZmZXJlbnQgcXVlc3Rpb25zLgoKYGBge3Igc2FtYXJhLXR5cGUtb25lfQpzYW1hcmFfdHJlZV9maXJzdCA8LSBsbShWZWxvY2l0eSB+IFRyZWVGICsgTG9hZCwgZGF0YSA9IFNhbWFyYSkKc2FtYXJhX2xvYWRfZmlyc3QgPC0gbG0oVmVsb2NpdHkgfiBMb2FkICsgVHJlZUYsIGRhdGEgPSBTYW1hcmEpCgphbm92YShzYW1hcmFfdHJlZV9maXJzdCkKYW5vdmEoc2FtYXJhX2xvYWRfZmlyc3QpCmBgYAoKYGFub3ZhKG1vZGVsKWAgcHJvZHVjZXMgYSBzZXF1ZW50aWFsLCBvciBUeXBlIEksIEFOT1ZBIHRhYmxlLiBUZXJtcyBhcmUgYWRkZWQgdG8gdGhlIG1vZGVsIGluIHNlcXVlbmNlLCBzbyBpbiBhIG5vbi1vcnRob2dvbmFsIGRlc2lnbiB0aGUgc3VtcyBvZiBzcXVhcmVzIGNhbiBkZXBlbmQgb24gdGhlIG9yZGVyIG9mIHRoZSB0ZXJtcy4KCkluIGNvbnRyYXN0LCBgYW5vdmEocmVkdWNlZF9tb2RlbCwgZnVsbF9tb2RlbClgIGRpcmVjdGx5IGNvbXBhcmVzIHR3byBmaXR0ZWQgbmVzdGVkIG1vZGVscy4gVGhlIGh5cG90aGVzaXMgaXMgZGV0ZXJtaW5lZCBieSB0aGUgdGVybXMgdGhhdCBhcmUgcHJlc2VudCBpbiB0aGUgZnVsbCBtb2RlbCBidXQgYWJzZW50IGZyb20gdGhlIHJlZHVjZWQgbW9kZWwuCgpJZiBvdXIgc2NpZW50aWZpYyBxdWVzdGlvbiBpcyBzcGVjaWZpY2FsbHkgd2hldGhlciBUcmVlIGFkZHMgaW5mb3JtYXRpb24gYWZ0ZXIgYWNjb3VudGluZyBmb3IgTG9hZCwgdGhlIGNvbXBhcmlzb24gc2hvdWxkIGJlIHdyaXR0ZW4gZXhwbGljaXRseToKCmBgYHtyIHNhbWFyYS1hZGp1c3RlZC1xdWVzdGlvbn0KYW5vdmEobTEsIG0yKQpgYGAKCkhlcmUgYG0xYCBhbHJlYWR5IGNvbnRhaW5zIGBMb2FkYCwgYW5kIGBtMmAgYWRkcyBgVHJlZUZgLiBUaGUgdGVzdCB0aGVyZWZvcmUgYXNrcyB3aGV0aGVyIFRyZWUgaW1wcm92ZXMgdGhlIG1vZGVsIGFmdGVyIExvYWQgaGFzIGFscmVhZHkgYmVlbiBhY2NvdW50ZWQgZm9yLgoKVHlwZSBJSSBhbmQgVHlwZSBJSUkgc3VtcyBvZiBzcXVhcmVzIHByb3ZpZGUgb3RoZXIgY29udmVudGlvbnMgZm9yIGFkanVzdGVkIHRlc3RzIGluIG5vbi1vcnRob2dvbmFsIGRlc2lnbnMuIFdlIGRvIG5vdCBuZWVkIHRoZW0gaGVyZSBiZWNhdXNlIHdlIGNhbiBzdGF0ZSB0aGUgc2NpZW50aWZpYyBjb21wYXJpc29uIGRpcmVjdGx5IHVzaW5nIHJlZHVjZWQgYW5kIGZ1bGwgbW9kZWxzLgoKIyMgQSBzbWFsbCBSIGRldGFpbAoKV2hlbiBzZXZlcmFsIG5lc3RlZCBtb2RlbHMgYXJlIHN1cHBsaWVkIGluIG9uZSBgYW5vdmEoKWAgY2FsbCwgUiB1c2VzIHRoZSBsYXJnZXN0IG1vZGVsJ3MgcmVzaWR1YWwgbWVhbiBzcXVhcmUgYXMgdGhlIGNvbW1vbiBkZW5vbWluYXRvci4gRm9yIGEgc3BlY2lmaWMgc2NpZW50aWZpYyBoeXBvdGhlc2lzLCB1c2UgYGFub3ZhKHJlZHVjZWQsIGZ1bGwpYCBzbyB0aGUgY29tcGFyaXNvbiBpcyBleHBsaWNpdC4gVGhpcyBpcyB3aHkgdGhlIGBtMWAgdmVyc3VzIGBtMmAgcmVzdWx0IGNhbiBkaWZmZXIgd2hlbiBgbTNgIGlzIGluY2x1ZGVkIGluIHRoZSBzYW1lIGNhbGwuCgpgYGB7ciBzYW1hcmEtYW5vdmEtYWxsfQphbm92YShtMSwgbTIsIG0zKQpgYGAKCk1vZGVsIGNvbXBhcmlzb24gZG9lcyBub3QgcmVwbGFjZSBtb2RlbCBjaGVja2luZy4gT25jZSB3ZSBoYXZlIGNob3NlbiB0aGUgbWVhbiBzdHJ1Y3R1cmUsIHRoZSB1c3VhbCBMSU5FIGRpYWdub3N0aWNzIHN0aWxsIG5lZWQgdG8gYmUgY2hlY2tlZC4KCiMjIEJvZHkgRmF0IGNhc2Ugc3R1ZHkKCk5vdyBkbyB0aGUgc2FtZSB0aGluZyB3aXRoIHRoZSBCb2R5IEZhdCBkYXRhLiBUaGUgcmVzcG9uc2UgaXMgcGVyY2VudGFnZSBib2R5IGZhdCwgQWdlIGlzIHRoZSBudW1lcmljYWwgY292YXJpYXRlLCBhbmQgR2VuZGVyIGlzIHRoZSBmYWN0b3IuCgpHcmFiIHRoZSBkYXRhIGhlcmU6IGByIHhmdW46OmVtYmVkX2ZpbGUoIi4uL2RhdGEvZmF0LmNzdiIsdGV4dCA9ICIgZmF0LmNzdiIpYAoKYGBge3IgbG9hZC1mYXQsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CkZhdCA8LSByZWFkcjo6cmVhZF9jc3YoIi4uL2RhdGEvZmF0LmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpIHw+CiAgbXV0YXRlKEdlbmRlciA9IGZhY3RvcihHZW5kZXIpKQpgYGAKClVzZSB0aGUgc2FtZSBuZXN0ZWQtbW9kZWwgbG9naWMgYXMgZm9yIHRoZSBTYW1hcmEgZGF0YS4gRG9lcyBHZW5kZXIgbmVlZCB0byBjaGFuZ2UgdGhlIEFnZSBzbG9wZSwgdGhlIGludGVyY2VwdCwgb3IgbmVpdGhlcj8KCmBgYHtyIGZhdC1tb2RlbHN9CkYwIDwtIGxtKFBlcmNlbnQuRmF0IH4gQWdlLCBkYXRhID0gRmF0KQpGMSA8LSBsbShQZXJjZW50LkZhdCB+IEdlbmRlciArIEFnZSwgZGF0YSA9IEZhdCkKRjIgPC0gbG0oUGVyY2VudC5GYXQgfiBHZW5kZXIgKiBBZ2UsIGRhdGEgPSBGYXQpCmBgYAoKQmVmb3JlIGxvb2tpbmcgYXQgdGhlIHRlc3RzLCBwcmVkaWN0IHdoaWNoIGNvbXBhcmlzb24gdGVzdHMgdGhlIHNsb3BlIGRpZmZlcmVuY2UgYW5kIHdoaWNoIHRlc3RzIHRoZSBpbnRlcmNlcHQgZGlmZmVyZW5jZS4KCgpgYGB7ciBmYXQtZml0dGVkLWxpbmVzLCBlY2hvPUZBTFNFLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9NCwgZHBpPTE1MCwgb3V0LndpZHRoPSIxMDAlIiwgZmlnLmNhcD0iVGhlIHNhbWUgQm9keSBGYXQgb2JzZXJ2YXRpb25zIHdpdGggZml0dGVkIGxpbmVzIGZyb20gdGhlIHRocmVlIGNhbmRpZGF0ZSBtb2RlbHMuIn0KZmF0X21vZGVsX2xhYmVscyA8LSBjKAogIEYwID0gIkYwOiBvbmUgbGluZSIsCiAgRjEgPSAiRjE6IHBhcmFsbGVsIGxpbmVzIiwKICBGMiA9ICJGMjogc2VwYXJhdGUgbGluZXMiCikKCmZhdF9wbG90X2RhdGEgPC0gYmluZF9yb3dzKAogIEZhdCB8PiBtdXRhdGUobW9kZWwgPSB1bm5hbWUoZmF0X21vZGVsX2xhYmVsc1siRjAiXSkpLAogIEZhdCB8PiBtdXRhdGUobW9kZWwgPSB1bm5hbWUoZmF0X21vZGVsX2xhYmVsc1siRjEiXSkpLAogIEZhdCB8PiBtdXRhdGUobW9kZWwgPSB1bm5hbWUoZmF0X21vZGVsX2xhYmVsc1siRjIiXSkpCikgfD4KICBtdXRhdGUobW9kZWwgPSBmYWN0b3IobW9kZWwsIGxldmVscyA9IHVubmFtZShmYXRfbW9kZWxfbGFiZWxzKSkpCgphZ2VfZ3JpZCA8LSB0aWJibGUoQWdlID0gc2VxKG1pbihGYXQkQWdlKSwgbWF4KEZhdCRBZ2UpLCBsZW5ndGgub3V0ID0gMTAwKSkKCnByZWRfRjAgPC0gYWdlX2dyaWQgfD4KICBtdXRhdGUoCiAgICBQZXJjZW50LkZhdCA9IHByZWRpY3QoRjAsIG5ld2RhdGEgPSBhZ2VfZ3JpZCksCiAgICBtb2RlbCA9IHVubmFtZShmYXRfbW9kZWxfbGFiZWxzWyJGMCJdKQogICkKCm5ld19GMSA8LSB0aWR5cjo6ZXhwYW5kX2dyaWQoQWdlID0gYWdlX2dyaWQkQWdlLCBHZW5kZXIgPSBsZXZlbHMoRmF0JEdlbmRlcikpCnByZWRfRjEgPC0gbmV3X0YxIHw+CiAgbXV0YXRlKAogICAgUGVyY2VudC5GYXQgPSBwcmVkaWN0KEYxLCBuZXdkYXRhID0gbmV3X0YxKSwKICAgIG1vZGVsID0gdW5uYW1lKGZhdF9tb2RlbF9sYWJlbHNbIkYxIl0pCiAgKQoKbmV3X0YyIDwtIHRpZHlyOjpleHBhbmRfZ3JpZChBZ2UgPSBhZ2VfZ3JpZCRBZ2UsIEdlbmRlciA9IGxldmVscyhGYXQkR2VuZGVyKSkKcHJlZF9GMiA8LSBuZXdfRjIgfD4KICBtdXRhdGUoCiAgICBQZXJjZW50LkZhdCA9IHByZWRpY3QoRjIsIG5ld2RhdGEgPSBuZXdfRjIpLAogICAgbW9kZWwgPSB1bm5hbWUoZmF0X21vZGVsX2xhYmVsc1siRjIiXSkKICApCgpmYXRfbW9kZWxfcGxvdCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludCgKICAgIGRhdGEgPSBmYXRfcGxvdF9kYXRhLAogICAgbWFwcGluZyA9IGFlcyh4ID0gQWdlLCB5ID0gUGVyY2VudC5GYXQsIGNvbG91ciA9IEdlbmRlciwgc2hhcGUgPSBHZW5kZXIpLAogICAgc2l6ZSA9IDIuMiwKICAgIGFscGhhID0gMC44CiAgKSArCiAgZ2VvbV9saW5lKAogICAgZGF0YSA9IHByZWRfRjAsCiAgICBtYXBwaW5nID0gYWVzKHggPSBBZ2UsIHkgPSBQZXJjZW50LkZhdCksCiAgICBjb2xvdXIgPSAiYmxhY2siLAogICAgbGluZXdpZHRoID0gMQogICkgKwogIGdlb21fbGluZSgKICAgIGRhdGEgPSBwcmVkX0YxLAogICAgbWFwcGluZyA9IGFlcyh4ID0gQWdlLCB5ID0gUGVyY2VudC5GYXQsIGNvbG91ciA9IEdlbmRlciwgZ3JvdXAgPSBHZW5kZXIpLAogICAgbGluZXdpZHRoID0gMQogICkgKwogIGdlb21fbGluZSgKICAgIGRhdGEgPSBwcmVkX0YyLAogICAgbWFwcGluZyA9IGFlcyh4ID0gQWdlLCB5ID0gUGVyY2VudC5GYXQsIGNvbG91ciA9IEdlbmRlciwgZ3JvdXAgPSBHZW5kZXIpLAogICAgbGluZXdpZHRoID0gMQogICkgKwogIGZhY2V0X3dyYXAofiBtb2RlbCwgbnJvdyA9IDEpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSArCiAgbGFicyh4ID0gIkFnZSIsIHkgPSAiUGVyY2VudCBmYXQiLCBjb2xvdXIgPSAiR2VuZGVyIiwgc2hhcGUgPSAiR2VuZGVyIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmZhdF9tb2RlbF9wbG90CmBgYAoKIyMgRG9lcyBHZW5kZXIgbmVlZCB0byBjaGFuZ2UgdGhlIHNsb3BlPwoKYEYyYCBhZGRzIG9uZSBHZW5kZXItYnktQWdlIGludGVyYWN0aW9uIGNvZWZmaWNpZW50LiBUaGlzIHRlc3RzIHdoZXRoZXIgdGhlIEFnZSBzbG9wZSBkaWZmZXJzIGJldHdlZW4gZmVtYWxlcyBhbmQgbWFsZXMuCgpgYGB7ciBmYXQtYW5vdmEtRjEtRjJ9CmFub3ZhKEYxLCBGMikKYGBgCgpUaGUgKlAqLXZhbHVlIGlzIGFwcHJveGltYXRlbHkgMC4wNjgsIHNvIHRoZXJlIGlzIG5vdCBzdHJvbmcgZXZpZGVuY2UgYXQgdGhlIDUlIGxldmVsIHRoYXQgc2VwYXJhdGUgc2xvcGVzIGFyZSByZXF1aXJlZC4gV2UgdGhlcmVmb3JlIGNvbnRpbnVlIHdpdGggdGhlIHBhcmFsbGVsLWxpbmVzIG1vZGVsIGBGMWAuCgojIyBEb2VzIEdlbmRlciBuZWVkIHRvIGNoYW5nZSB0aGUgaW50ZXJjZXB0PwoKYEYxYCBhZGRzIHRoZSBHZW5kZXIgaW50ZXJjZXB0IHNoaWZ0IHRvIGEgbW9kZWwgdGhhdCBhbHJlYWR5IGNvbnRhaW5zIEFnZS4KCmBgYHtyIGZhdC1hbm92YS1GMC1GMX0KYW5vdmEoRjAsIEYxKQpgYGAKClRoZSAqUCotdmFsdWUgaXMgYXBwcm94aW1hdGVseSAwLjAxOCwgcHJvdmlkaW5nIGV2aWRlbmNlIHRoYXQgR2VuZGVyIGNvbnRyaWJ1dGVzIHRvIGV4cGVjdGVkIHBlcmNlbnRhZ2UgYm9keSBmYXQgYWZ0ZXIgYWNjb3VudGluZyBmb3IgQWdlLgoKT2YgdGhlc2UgdGhyZWUgcHJlc3BlY2lmaWVkIG1vZGVscywgYEYxYCBpcyB0aGUgc2ltcGxlc3QgbW9kZWwgc3VwcG9ydGVkIGJ5IHRoZXNlIGNvbXBhcmlzb25zOiB0aGUgZml0dGVkIGxpbmVzIGhhdmUgZGlmZmVyZW50IGludGVyY2VwdHMgYnV0IGEgY29tbW9uIEFnZSBzbG9wZS4KCldoZW4gdGhlIGxhcmdlciBtb2RlbCBhZGRzIGV4YWN0bHkgb25lIGNvZWZmaWNpZW50LCB0aGUgcGFydGlhbCAqRiogdGVzdCBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgdHdvLXNpZGVkICp0KiB0ZXN0IGFyZSBlcXVpdmFsZW50OgoKJCRGPXReMi4kJAoKVGhpcyBpcyB3aHkgdGhlICpQKi12YWx1ZSBmcm9tIGBhbm92YShGMSwgRjIpYCBtYXRjaGVzIHRoZSAqUCotdmFsdWUgZm9yIHRoZSBgR2VuZGVyOkFnZWAgY29lZmZpY2llbnQgaW4gYHN1bW1hcnkoRjIpYCwgYW5kIHdoeSBgYW5vdmEoRjAsIEYxKWAgbWF0Y2hlcyB0aGUgR2VuZGVyIGNvZWZmaWNpZW50IHRlc3QgaW4gYHN1bW1hcnkoRjEpYC4KCiMjIEZpdHRlZCBlcXVhdGlvbnMgZm9yIHRoZSByZXRhaW5lZCBCb2R5IEZhdCBtb2RlbAoKYGBge3IgZmF0LXN1bW1hcnl9CnN1bW1hcnkoRjEpCmBgYAoKV2l0aCBmZW1hbGVzIGFzIHRoZSByZWZlcmVuY2UgbGV2ZWw6CgokJApFKFx0ZXh0e0ZhdH1cbWlkXHRleHR7RmVtYWxlfSkgPSBcYmV0YV8wK1xiZXRhX3tcdGV4dHtBZ2V9fVx0ZXh0e0FnZX0KJCQKCmFuZAoKJCQKRShcdGV4dHtGYXR9XG1pZFx0ZXh0e01hbGV9KSA9IChcYmV0YV8wK1xiZXRhX3tcdGV4dHtHZW5kZXJNfX0pICsgXGJldGFfe1x0ZXh0e0FnZX19XHRleHR7QWdlfS4KJCQKCiMjIEJvZHkgRmF0IGZpdHRlZCBlcXVhdGlvbnMKClRoZSBjb21tb24gQWdlIGNvZWZmaWNpZW50IGdpdmVzIHRoZSBzbG9wZSBvZiBib3RoIGxpbmVzLiBgR2VuZGVyTWAgZ2l2ZXMgdGhlIHZlcnRpY2FsIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWFsZSBhbmQgZmVtYWxlIGZpdHRlZCBsaW5lcy4KCmBgYHtyIGZhdC1lcXVhdGlvbn0KY28gPC0gY29lZihGMSkKYmV0YTAgPC0gdW5uYW1lKGNvWyIoSW50ZXJjZXB0KSJdKQpiZXRhX2FnZSA8LSB1bm5hbWUoY29bIkFnZSJdKQpiZXRhX21hbGUgPC0gdW5uYW1lKGNvWyJHZW5kZXJNIl0pCnRpYmJsZSgKICBtb2RlbCA9IGMoIkZlbWFsZXMiLCAiTWFsZXMiKSwKICBlcXVhdGlvbiA9IGMoCiAgICBzcHJpbnRmKCJGYXQgPSAlLjJmICsgJS4yZiAqIEFnZSIsIGJldGEwLCBiZXRhX2FnZSksCiAgICBzcHJpbnRmKCJGYXQgPSAlLjJmICsgJS4yZiAqIEFnZSIsIGJldGEwICsgYmV0YV9tYWxlLCBiZXRhX2FnZSkKICApCikgfD4KICBrYWJsZShjYXB0aW9uID0gIkZpdHRlZCBlcXVhdGlvbnMgYnkgR2VuZGVyIChwYXJhbGxlbC1zbG9wZXMgbW9kZWwpIikgfD4KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgojIyBSZXBvcnRpbmcgYSBuZXN0ZWQtbW9kZWwgY29tcGFyaXNvbgoKQSBnb29kIHdyaXRlLXVwIG9mIGEgbmVzdGVkLW1vZGVsIGNvbXBhcmlzb24gbmVlZHMgZml2ZSB0aGluZ3M6CgoxLiBTdGF0ZSB0aGUgcmVkdWNlZCBhbmQgZnVsbCBtb2RlbHMuCjIuIFN0YXRlIHdoYXQgYWRkaXRpb25hbCB0ZXJtcyBhcmUgYmVpbmcgdGVzdGVkIGFuZCB0cmFuc2xhdGUgdGhhdCBpbnRvIHRoZSBzY2llbnRpZmljIHF1ZXN0aW9uLgozLiBSZXBvcnQgdGhlIGNoYW5nZSBpbiBSU1MgYW5kIGRlZ3JlZXMgb2YgZnJlZWRvbSwgdGhlICpGKiBzdGF0aXN0aWMgYW5kIHRoZSAqUCotdmFsdWUuCjQuIFN0YXRlIHdoZXRoZXIgdGhlIGRhdGEgcHJvdmlkZSBldmlkZW5jZSB0aGF0IHRoZSBhZGRpdGlvbmFsIG1vZGVsIHN0cnVjdHVyZSBpcyBuZWVkZWQuCjUuIEludGVycHJldCB0aGUgcmV0YWluZWQgbW9kZWwgaW4gdGVybXMgb2YgdGhlIHJlc3BvbnNlIGFuZCBleHBsYW5hdG9yeSB2YXJpYWJsZXMuCgpBIG5vbi1zaWduaWZpY2FudCBjb21wYXJpc29uIGRvZXMgbm90IHByb3ZlIHRoYXQgdGhlIHNtYWxsZXIgbW9kZWwgaXMgdHJ1ZS4gSXQgbWVhbnMgdGhhdCB0aGUgZGF0YSBkbyBub3QgcHJvdmlkZSBzdWZmaWNpZW50IGV2aWRlbmNlIHRoYXQgdGhlIGFkZGl0aW9uYWwgc3RydWN0dXJlIGltcHJvdmVzIHRoZSBtb2RlbC4KCkV2ZXJ5dGhpbmcgaW4gdGhpcyBsZWN0dXJlIHdhcyBtYW5hZ2VhYmxlIGJlY2F1c2UgdGhlIGNhbmRpZGF0ZSBtb2RlbHMgd2VyZSBzcGVjaWZpZWQgaW4gYWR2YW5jZS4gVGhlIHNjaWVudGlmaWMgcXVlc3Rpb24gdG9sZCB1cyB3aGljaCByZWR1Y2VkIG1vZGVsIHRvIGNvbXBhcmUgd2l0aCB3aGljaCBmdWxsIG1vZGVsLgoKVGhlIG5leHQgcHJvYmxlbSBpcyBoYXJkZXIuIFdoYXQgaWYgdGhlcmUgYXJlIG1hbnkgcGxhdXNpYmxlIHByZWRpY3RvcnMgYW5kIG5vIHNpbmdsZSBvYnZpb3VzIHJlZHVjZWQtdmVyc3VzLWZ1bGwgY29tcGFyaXNvbj8gVGhhdCB0YWtlcyB1cyBmcm9tICoqbW9kZWwgY29tcGFyaXNvbioqIHRvICoqbW9kZWwgc2VsZWN0aW9uKiosIHdoaWNoIGlzIHdoZXJlIHdlIGdvIG5leHQu