Learning objectives

By the end of this lecture you should be able to:

  1. Identify when one linear model is nested within another.
  2. State the hypothesis tested by comparing two nested models.
  3. Use a partial F test to decide whether the additional terms in a larger model improve the fit.
  4. Interpret the change in RSS, degrees of freedom, F statistic and P-value in a nested-model comparison.
  5. Explain the difference between anova(model) and anova(reduced_model, full_model).
  6. 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.
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.

Recall from Lecture 14: the partial F test

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.

unlabelled

The same Samara observations with fitted lines from the three candidate models.

Moving from m1 to m2 allows the trees to move vertically apart. Moving from m2 to m3 also allows their slopes to differ.

Model Formula What changed?
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:

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.

unlabelled

The Samara data have different Load distributions across trees.

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.

unlabelled

The same Body Fat observations with fitted lines from the three candidate models.

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:

  1. State the reduced and full models.
  2. State what additional terms are being tested and translate that into the scientific question.
  3. Report the change in RSS and degrees of freedom, the F statistic and the P-value.
  4. State whether the data provide evidence that the additional model structure is needed.
  5. 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