View the
latest recording of this lecture
Let’s go back to the Climate data example, which had two binary
predictors NorthIsland
and Sea
. In this
lecture we will focus on the predictor NorthIsland
only.
We can briefly examine the effects of this predictor via a boxplot.
NorthIsland
seems to have an effect on temperature, but is
the effect statistically significant?
N.B.
Download Climate.csv
to replicate the following examples.
## Climate <- read.csv(file = "Climate.csv", header = TRUE, row.names = 1)
boxplot(MnJlyTemp ~ NorthIsland, data = Climate)
Idea of comparing groups using lm()
Suppose we denote the \(i\)th data
value (response) from group \(j\) by
\(Y_{ij}\), where \(j= 0,1\) and \(i=1,2,…,n_0\) or \(1,2,…,n_1\) respectively. Thus the group
means are \(\mu_0\) and \(\mu_1\) .
One way to analyse the data would be to use a two-sample t-test to
test the hypothesis \(H_0:~~\mu_0=
\mu_1\) versus \(H_1:~~\mu_0
\ne\mu_1\). You may have been introduced to such tests in your
first-year statistics course.
Another way of writing down the hypothesis (which will turn out to be
convenient) is to treat one group as a “baseline group” with mean \(\mu\) and the other group as a shifted or
“treatment” group with mean \(\mu+\delta\). Then the hypotheses become
\(H_0:~\delta=0\) vs versus \(H_1:~\delta\ne 0\).
There is a definite advantage in writing the hypothesis this way.
Namely, we can easily put the test into the linear model framework,
since the regression models are written in the form where we are testing
whether a slope (which will be \(\delta\)) equals zero.
To write it this way, define an indicator variable
as \(z_i = 0\) if the datum is in group
0, and \(z_i=1\) if if is in group
1.
Then we can write a formula for the response as
\[ E[Y_i] = \mu + \delta z_i = \beta_0 +
\beta_1 z_i\] where \(\beta_0 =
\mu\) and \(\beta_1 =
\delta\).
In other words the two-group comparison can be put in the same form
as a linear regression. So all we would need to do is:
set up the group indicator variable z,
regress Y on z and
test the hypothesis that the slope =0.
Testing using lm()
If we want to test the significance of the relationship between
NorthIsland
and temperature, we can use lm()
.
Remember lm()
assumes the variance of residuals does not
depend on the values of the predictor. From the boxplot it looks like
this assumption is justified.
Climate.lm1 <- lm(MnJlyTemp ~ NorthIsland, data = Climate)
summary(Climate.lm1)
Call:
lm(formula = MnJlyTemp ~ NorthIsland, data = Climate)
Residuals:
Min 1Q Median 3Q Max
-3.8647 -1.7421 -0.1034 1.8079 4.5579
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5.4647 0.5187 10.536 3.02e-12 ***
NorthIsland 3.5774 0.7140 5.011 1.66e-05 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.139 on 34 degrees of freedom
Multiple R-squared: 0.4248, Adjusted R-squared: 0.4078
F-statistic: 25.11 on 1 and 34 DF, p-value: 1.665e-05
The t-test for the slope of the model is significant
(p-value = 1.66e-05
), indicating that there is a
significant difference between the mean of the two groups.
Testing using t.test()
Since this is a simple two-group comparison, we can also investigate
using a standard two-sample t.test
, which gives basically
the same conclusion.
t.test(MnJlyTemp ~ NorthIsland, data = Climate)
Welch Two Sample t-test
data: MnJlyTemp by NorthIsland
t = -5.0114, df = 33.581, p-value = 1.711e-05
alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
95 percent confidence interval:
-5.028803 -2.125996
sample estimates:
mean in group 0 mean in group 1
5.464706 9.042105
On close inspection you will see that the p-value and df are
not exactly the same as for the lm()
.
The reason is that the default settings for t.test()
do not
assume equal variances, and so uses a method called Welch’s method.
Essentially it does a fudge on the df to make the t-distribution
work.
If we want to get exactly the same results in the
t.test()
as in the lm()
, then we need to set
the var.equal=TRUE option.
t.test(MnJlyTemp ~ NorthIsland, var.equal = TRUE, data = Climate)
Two Sample t-test
data: MnJlyTemp by NorthIsland
t = -5.0105, df = 34, p-value = 1.665e-05
alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
95 percent confidence interval:
-5.028371 -2.126428
sample estimates:
mean in group 0 mean in group 1
5.464706 9.042105
In this case the t statistics, df and p-value are equal to
those from lm
.
How do we know whether to assume equal variances?
A simple rule of thumb we can use is that if the standard deviation
of residuals for the two groups does not differ by more than a factor of
2, then we can assume equal variances.
with(Climate, sd(MnJlyTemp[NorthIsland == 0])/sd(MnJlyTemp[NorthIsland == 1]))
[1] 0.9971634
(Note that the with()
function allows us to use the
variables from the Climate
dataframe without writing
Climate$
; MnJlyTemp[NorthIsland == 0]
gives us
the temperatures for observations not in the North Island). Clearly the
ratio is much less than 2.
Preferably, we will perform a formal statistical test of \(H_0: \sigma_0^2 = \sigma_1^2\)
(i.e. variances are equal) vs \(H_1:\)
variances are not equal.
There are two tests commonly used: Bartlett’s test (which depends
more on the errors being normally distributed) and Levene’s test (which
works even if the data are not normal).
We will normally use the Levene’s test. The corresponding R function
is available through the car
package.
bartlett.test(MnJlyTemp ~ factor(NorthIsland), data = Climate)
Bartlett test of homogeneity of variances
data: MnJlyTemp by factor(NorthIsland)
Bartlett's K-squared = 0.00013276, df = 1, p-value = 0.9908
library(car)
leveneTest(MnJlyTemp ~ factor(NorthIsland), data = Climate)
Levene's Test for Homogeneity of Variance (center = median)
Df F value Pr(>F)
group 1 2e-04 0.9902
34
Note the use of the factor()
function to indicate that
we want to treat the NorthIsland
variable as a group
indicator variable (rather than a numerical variable).
The p-value for both these tests is large, which confirms
that, for the Climate data, we have no reason to doubt the equal
variance assumption.
Going further
A natural question is to ask what happens when we have more than two
groups. This will be the topic of the next lecture.
Models for a single numeric predictor and a binary group
variable
Binary predictors can also be used to deal with changes in slope
between two groups.
For example, in their book “Regression Analysis by Example”,
Chatterjee and Price present some data on the impact of amount of
revision impacts scores in an English exam differently for boys and
girls. Let:
- \(Y_i\) = score in English
exam,
- \(x_i\) = number of hours spent in
revision,
- \(z_i\) = sex (0 for boys, 1 for
girls).
N.B.
Download EnglishExam.csv
if you want to replicate the following examples.
## English <- read.csv(file = "EnglishExam.csv", header = TRUE)
head(English)
resulty revision sex
1 64 12 1
2 43 14 1
3 37 4 0
4 50 5 0
5 52 6 0
6 23 6 1
Looking at the data graphically we see:
The question we are asking is: should the lines for boys and girls
have a different slope?
To answer this question, we need to fit a model in which boys and
girls have different slopes. As in the previous section, it is
convenient to test for a difference in slope by setting a baseline group
(say, the boys) with slope \(\beta_1\),
and expressing the slope for the girls group as a shifted slope \(\beta_1 + \delta\). That is, we want a
model with the following response:
\[E[Y_i] = \beta_0 + \beta_1 x_i ~\mbox{
for boys, and}\] \[E[Y_i] = \beta_0 +
(\beta_1 + \delta) x_i ~\mbox{ for girls}\]
The group indicator variable \(z_i\)
is very convenient to construct the appropriate model:
\[E[Y_i] = \beta_0 + (\beta_1 + \delta
z_i) x_i = \beta_0 + \beta_1 x_i + \delta x_i z_i\]
Here, \(x_i z_i\) corresponds to a
new variable that is equal to 0 for boys and to \(x_i\) for girls. In order to fit the model
with lm()
in the usual way, we can easily construct this
new variable:
English$revisionGirls <- English$revision * English$sex
head(English)
resulty revision sex revisionGirls
1 64 12 1 12
2 43 14 1 14
3 37 4 0 0
4 50 5 0 0
5 52 6 0 0
6 23 6 1 6
We can now fit the model:
English.lm1 <- lm(resulty ~ revision, data = English)
English.lm2 <- lm(resulty ~ revision + revisionGirls, data = English)
summary(English.lm2)
Call:
lm(formula = resulty ~ revision + revisionGirls, data = English)
Residuals:
Min 1Q Median 3Q Max
-16.4760 -5.8961 0.4898 7.4113 13.4149
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 28.3669 4.9866 5.689 2.67e-05 ***
revision 2.7342 0.5644 4.844 0.000152 ***
revisionGirls -0.8827 0.3954 -2.232 0.039334 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 9.074 on 17 degrees of freedom
Multiple R-squared: 0.6111, Adjusted R-squared: 0.5653
F-statistic: 13.35 on 2 and 17 DF, p-value: 0.0003266
anova(English.lm1, English.lm2)
Analysis of Variance Table
Model 1: resulty ~ revision
Model 2: resulty ~ revision + revisionGirls
Res.Df RSS Df Sum of Sq F Pr(>F)
1 18 1809.9
2 17 1399.7 1 410.29 4.9833 0.03933 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
The coefficient associated with our constructed variable
revisionGirls
is significant (p-value \(\approx 0.04\)), which indicates that there
is a difference in the slope between boys and girls.
LS0tDQp0aXRsZTogIkxlY3R1cmUgMTU6IE1vZGVscyB3aXRoIGJpbmFyeSBwcmVkaWN0b3JzIg0Kc3VidGl0bGU6IDE2MS4yNTEgUmVncmVzc2lvbiBNb2RlbGxpbmcNCmF1dGhvcjogIlByZXNlbnRlZCBieSBKb25hdGhhbiBHb2RmcmV5IDxhLmouZ29kZnJleUBtYXNzZXkuYWMubno+IiAgDQpkYXRlOiAiV2VlayA1IG9mIFNlbWVzdGVyIDIsIGByIGx1YnJpZGF0ZTo6eWVhcihsdWJyaWRhdGU6Om5vdygpKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgaW9zbGlkZXNfcHJlc2VudGF0aW9uOg0KICAgIHdpZGVzY3JlZW46IHRydWUNCiAgICBzbWFsbGVyOiB0cnVlDQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgc2xpZHlfcHJlc2VudGF0aW9uOiANCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KDQoNCg0KDQpbVmlldyB0aGUgbGF0ZXN0IHJlY29yZGluZyBvZiB0aGlzIGxlY3R1cmVdKGh0dHBzOi8vUi1SZXNvdXJjZXMubWFzc2V5LmFjLm56L3ZpZGVvcy8yNTFMMTUubXA0KQ0KPCEtLS0gRGF0YSBpcyBvbg0KaHR0cHM6Ly9yLXJlc291cmNlcy5tYXNzZXkuYWMubnovZGF0YS8xNjEyNTEvDQotLS0+DQoNCmBgYHtyIHNldHVwLCBwdXJsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCm9wdHNfY2h1bmskc2V0KGRldj1jKCJwbmciLCAicGRmIikpDQpvcHRzX2NodW5rJHNldChmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD03LCBmaWcucGF0aD0iRmlndXJlcy8iLCBmaWcuYWx0PSJ1bmxhYmVsbGVkIikNCm9wdHNfY2h1bmskc2V0KGNvbW1lbnQ9IiIsIGZpZy5hbGlnbj0iY2VudGVyIiwgdGlkeT1UUlVFKQ0Kb3B0aW9ucyhrbml0ci5rYWJsZS5OQSA9ICcnKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGJyb29tKQ0KYGBgDQoNCg0KPCEtLS0gRG8gbm90IGVkaXQgYW55dGhpbmcgYWJvdmUgdGhpcyBsaW5lLiAtLS0+DQoNCmBgYHtyIGV4dHJhTGlicmFyeSwgZWNobz1GQUxTRSxpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShjYXIpDQpgYGANCg0KTGV0J3MgZ28gYmFjayB0byB0aGUgQ2xpbWF0ZSBkYXRhIGV4YW1wbGUsIHdoaWNoIGhhZCB0d28gYmluYXJ5IHByZWRpY3RvcnMgYE5vcnRoSXNsYW5kYCBhbmQgYFNlYWAuIEluIHRoaXMgbGVjdHVyZSB3ZSB3aWxsIGZvY3VzIG9uIHRoZSBwcmVkaWN0b3IgYE5vcnRoSXNsYW5kYCBvbmx5Lg0KDQpXZSBjYW4gYnJpZWZseSBleGFtaW5lIHRoZSBlZmZlY3RzIG9mIHRoaXMgcHJlZGljdG9yIHZpYSBhIGJveHBsb3QuIGBOb3J0aElzbGFuZGAgc2VlbXMgdG8gaGF2ZSBhbiBlZmZlY3Qgb24gdGVtcGVyYXR1cmUsIGJ1dCBpcyB0aGUgZWZmZWN0IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQ/IA0KDQpOLkIuIGByIHhmdW46OmVtYmVkX2ZpbGUoIi4uLy4uL2RhdGEvQ2xpbWF0ZS5jc3YiKWAgdG8gcmVwbGljYXRlIHRoZSBmb2xsb3dpbmcgZXhhbXBsZXMuDQoNCmBgYHtyIENsaW1hdGUgYm94cGxvdHMsIGV2YWwgPSAtMiwgZWNobyA9IC0xfQ0KQ2xpbWF0ZSA8LSByZWFkLmNzdihmaWxlPSAiLi4vLi4vZGF0YS9DbGltYXRlLmNzdiIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpDQpDbGltYXRlIDwtIHJlYWQuY3N2KGZpbGU9ICJDbGltYXRlLmNzdiIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpDQpib3hwbG90KE1uSmx5VGVtcCB+IE5vcnRoSXNsYW5kLCBkYXRhID0gQ2xpbWF0ZSkNCmBgYA0KDQojIyBJZGVhIG9mIGNvbXBhcmluZyBncm91cHMgdXNpbmcgbG0oKQ0KDQpTdXBwb3NlIHdlIGRlbm90ZSB0aGUgJGkkdGggZGF0YSB2YWx1ZSAocmVzcG9uc2UpIGZyb20gZ3JvdXAgJGokIGJ5ICRZX3tpan0kLCB3aGVyZSAkaj0gMCwxJCBhbmQgJGk9MSwyLOKApixuXzAkIG9yICQxLDIs4oCmLG5fMSQgICByZXNwZWN0aXZlbHkuICBUaHVzIHRoZSBncm91cCBtZWFucyBhcmUgICRcbXVfMCQgIGFuZCAkXG11XzEkIC4NCg0KT25lIHdheSB0byBhbmFseXNlIHRoZSBkYXRhIHdvdWxkIGJlIHRvIHVzZSBhIHR3by1zYW1wbGUgdC10ZXN0IHRvIHRlc3QgdGhlIGh5cG90aGVzaXMgJEhfMDp+flxtdV8wPSBcbXVfMSQgICAgICB2ZXJzdXMgICAkSF8xOn5+XG11XzAgXG5lXG11XzEkLiBZb3UgbWF5IGhhdmUgYmVlbiBpbnRyb2R1Y2VkIHRvIHN1Y2ggdGVzdHMgaW4geW91ciBmaXJzdC15ZWFyIHN0YXRpc3RpY3MgY291cnNlLg0KDQpBbm90aGVyIHdheSBvZiB3cml0aW5nIGRvd24gdGhlIGh5cG90aGVzaXMgKHdoaWNoIHdpbGwgdHVybiBvdXQgdG8gYmUgY29udmVuaWVudCkgaXMgdG8gdHJlYXQgb25lIGdyb3VwIGFzIGEg4oCcYmFzZWxpbmUgZ3JvdXDigJ0gIHdpdGggbWVhbiAkXG11JCBhbmQgdGhlIG90aGVyIGdyb3VwIGFzIGEgIHNoaWZ0ZWQgb3INCiDigJx0cmVhdG1lbnTigJ0gZ3JvdXAgIHdpdGggbWVhbiAgJFxtdStcZGVsdGEkLiAgVGhlbiB0aGUgaHlwb3RoZXNlcyBiZWNvbWUgICAkSF8wOn5cZGVsdGE9MCQgIHZzICB2ZXJzdXMgJEhfMTp+XGRlbHRhXG5lIDAkLg0KCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQpUaGVyZSBpcyBhIGRlZmluaXRlIGFkdmFudGFnZSBpbiB3cml0aW5nIHRoZSBoeXBvdGhlc2lzIHRoaXMgd2F5LiBOYW1lbHksIHdlIGNhbiAgIGVhc2lseSBwdXQgdGhlIHRlc3QgaW50byB0aGUgbGluZWFyIG1vZGVsICBmcmFtZXdvcmssIHNpbmNlIHRoZSByZWdyZXNzaW9uIG1vZGVscyBhcmUgd3JpdHRlbiBpbiB0aGUgZm9ybSB3aGVyZSB3ZSBhcmUgdGVzdGluZyB3aGV0aGVyIGEgc2xvcGUgKHdoaWNoIHdpbGwgYmUgJFxkZWx0YSQpIGVxdWFscyB6ZXJvLiAgIA0KDQpUbyB3cml0ZSAgaXQgdGhpcyB3YXksIGRlZmluZSBhbiAqKmluZGljYXRvciB2YXJpYWJsZSoqICBhcyAkel9pID0gMCQgaWYgIHRoZSBkYXR1bSBpcyBpbiBncm91cCAwLCBhbmQgJHpfaT0xJCBpZiBpZiBpcyBpbiBncm91cCAxLiANCiAgIA0KVGhlbiB3ZSBjYW4gd3JpdGUgYSBmb3JtdWxhIGZvciB0aGUgcmVzcG9uc2UgYXMgICAgDQokJCBFW1lfaV0gPSBcbXUgKyBcZGVsdGEgel9pICAgPSBcYmV0YV8wICsgXGJldGFfMSB6X2kkJA0Kd2hlcmUgJFxiZXRhXzAgPSBcbXUkIGFuZCAkXGJldGFfMSA9IFxkZWx0YSQuDQoNCkluIG90aGVyIHdvcmRzIHRoZSB0d28tZ3JvdXAgY29tcGFyaXNvbiBjYW4gYmUgcHV0IGluIHRoZSBzYW1lIGZvcm0gYXMgYSBsaW5lYXIgcmVncmVzc2lvbi4gU28gIGFsbCB3ZSB3b3VsZCBuZWVkIHRvIGRvIGlzOg0KDQoxLiBzZXQgdXAgdGhlIGdyb3VwIGluZGljYXRvciB2YXJpYWJsZSAqeiosIA0KDQoyLiByZWdyZXNzICpZKiBvbiAqeiogYW5kIA0KDQozLiB0ZXN0IHRoZSBoeXBvdGhlc2lzIHRoYXQgdGhlIHNsb3BlID0wLiANCg0KIyMgVGVzdGluZyB1c2luZyBsbSgpDQoNCklmIHdlIHdhbnQgdG8gdGVzdCB0aGUgc2lnbmlmaWNhbmNlIG9mIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgTm9ydGhJc2xhbmRgIGFuZCB0ZW1wZXJhdHVyZSwgd2UgY2FuIHVzZSBgbG0oKWAuIFJlbWVtYmVyIGBsbSgpYCAgYXNzdW1lcyB0aGUgdmFyaWFuY2Ugb2YgcmVzaWR1YWxzIGRvZXMgbm90IGRlcGVuZCBvbiB0aGUgdmFsdWVzIG9mIHRoZSBwcmVkaWN0b3IuIEZyb20gdGhlIGJveHBsb3QgaXQgbG9va3MgbGlrZSB0aGlzIGFzc3VtcHRpb24gaXMganVzdGlmaWVkLiANCg0KYGBge3IgbG1UZXN0aW5nfQ0KQ2xpbWF0ZS5sbTEgPC0gbG0oTW5KbHlUZW1wIH4gTm9ydGhJc2xhbmQsIGRhdGEgPSBDbGltYXRlKQ0Kc3VtbWFyeShDbGltYXRlLmxtMSkgDQpgYGANCg0KVGhlIHQtdGVzdCBmb3IgdGhlIHNsb3BlIG9mIHRoZSBtb2RlbCBpcyBzaWduaWZpY2FudCAoKnAqLXZhbHVlID0gYDEuNjZlLTA1YCksIGluZGljYXRpbmcgdGhhdCB0aGVyZSBpcyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWVhbiBvZiB0aGUgdHdvIGdyb3Vwcy4NCg0KDQojIyBUZXN0aW5nIHVzaW5nIHQudGVzdCgpDQoNClNpbmNlIHRoaXMgaXMgYSBzaW1wbGUgdHdvLWdyb3VwIGNvbXBhcmlzb24sIHdlIGNhbiBhbHNvIGludmVzdGlnYXRlIHVzaW5nIGEgc3RhbmRhcmQgdHdvLXNhbXBsZSBgdC50ZXN0YCwgd2hpY2ggZ2l2ZXMgYmFzaWNhbGx5IHRoZSBzYW1lIGNvbmNsdXNpb24uDQoNCmBgYHtyIHQudGVzdCBUZXN0aW5nfQ0KdC50ZXN0KE1uSmx5VGVtcCB+IE5vcnRoSXNsYW5kLCBkYXRhID0gQ2xpbWF0ZSkNCmBgYCANCiANCk9uIGNsb3NlIGluc3BlY3Rpb24geW91IHdpbGwgc2VlIHRoYXQgdGhlICpwKi12YWx1ZSBhbmQgZGYgYXJlIG5vdCBleGFjdGx5IHRoZSBzYW1lIGFzIGZvciB0aGUgYGxtKClgLiAgIA0KVGhlIHJlYXNvbiBpcyB0aGF0IHRoZSBkZWZhdWx0IHNldHRpbmdzIGZvciBgdC50ZXN0KClgIGRvIG5vdCBhc3N1bWUgZXF1YWwgdmFyaWFuY2VzLCBhbmQgc28gdXNlcyBhIG1ldGhvZCBjYWxsZWQgV2VsY2gncyBtZXRob2QuIEVzc2VudGlhbGx5IGl0IGRvZXMgYSBmdWRnZSBvbiB0aGUgZGYgIHRvIG1ha2UgdGhlIHQtZGlzdHJpYnV0aW9uIHdvcmsuICAgDQoNCklmIHdlIHdhbnQgdG8gZ2V0IGV4YWN0bHkgdGhlIHNhbWUgcmVzdWx0cyBpbiB0aGUgYHQudGVzdCgpYCBhcyBpbiB0aGUgYGxtKClgLCB0aGVuIHdlIG5lZWQgdG8gc2V0IHRoZSB2YXIuZXF1YWw9VFJVRSBvcHRpb24uDQoNCmBgYHtyIHQudGVzdFZhci5lcXVhbH0NCnQudGVzdChNbkpseVRlbXB+Tm9ydGhJc2xhbmQsIHZhci5lcXVhbD1UUlVFLCBkYXRhPUNsaW1hdGUpDQpgYGAgDQoNCkluIHRoaXMgY2FzZSB0aGUgdCBzdGF0aXN0aWNzLCBkZiBhbmQgKnAqLXZhbHVlIGFyZSBlcXVhbCB0byB0aG9zZSBmcm9tIGBsbWAuDQoNCg0KIyMgSG93IGRvIHdlIGtub3cgd2hldGhlciB0byBhc3N1bWUgZXF1YWwgdmFyaWFuY2VzPw0KDQpBIHNpbXBsZSBydWxlIG9mIHRodW1iIHdlIGNhbiB1c2UgaXMgdGhhdCBpZiB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHJlc2lkdWFscyBmb3IgdGhlIHR3byBncm91cHMgZG9lcyBub3QgZGlmZmVyIGJ5IG1vcmUgdGhhbiBhIGZhY3RvciBvZiAyLCB0aGVuIHdlIGNhbiBhc3N1bWUgZXF1YWwgdmFyaWFuY2VzLg0KDQpgYGB7ciBDb21wYXJlIHNkfQ0Kd2l0aChDbGltYXRlLCBzZCggTW5KbHlUZW1wW05vcnRoSXNsYW5kPT0wXSApLyBzZCggTW5KbHlUZW1wW05vcnRoSXNsYW5kPT0xXSApKQ0KYGBgDQoNCihOb3RlIHRoYXQgdGhlIGB3aXRoKClgIGZ1bmN0aW9uIGFsbG93cyB1cyB0byB1c2UgdGhlIHZhcmlhYmxlcyBmcm9tIHRoZSBgQ2xpbWF0ZWAgZGF0YWZyYW1lIHdpdGhvdXQgd3JpdGluZyBgQ2xpbWF0ZSRgOyBgTW5KbHlUZW1wW05vcnRoSXNsYW5kID09IDBdYCBnaXZlcyB1cyB0aGUgdGVtcGVyYXR1cmVzIGZvciBvYnNlcnZhdGlvbnMgbm90IGluIHRoZSBOb3J0aCBJc2xhbmQpLiBDbGVhcmx5IHRoZSByYXRpbyBpcyBtdWNoIGxlc3MgdGhhbiAyLiANCg0KUHJlZmVyYWJseSwgd2Ugd2lsbCBwZXJmb3JtIGEgZm9ybWFsIHN0YXRpc3RpY2FsIHRlc3Qgb2YgJEhfMDogXHNpZ21hXzBeMiA9IFxzaWdtYV8xXjIkIChpLmUuIHZhcmlhbmNlcyBhcmUgZXF1YWwpICB2cyAgJEhfMTokIHZhcmlhbmNlcyBhcmUgbm90IGVxdWFsLiANCg0KVGhlcmUgYXJlIHR3byB0ZXN0cyBjb21tb25seSB1c2VkOiANCkJhcnRsZXR0J3MgdGVzdCAod2hpY2ggZGVwZW5kcyBtb3JlIG9uIHRoZSBlcnJvcnMgYmVpbmcgbm9ybWFsbHkgZGlzdHJpYnV0ZWQpIGFuZCBMZXZlbmUncyB0ZXN0ICh3aGljaCB3b3JrcyBldmVuIGlmIHRoZSBkYXRhIGFyZSBub3Qgbm9ybWFsKS4gIA0KDQpXZSB3aWxsIG5vcm1hbGx5IHVzZSB0aGUgTGV2ZW5lJ3MgdGVzdC4gVGhlIGNvcnJlc3BvbmRpbmcgUiBmdW5jdGlvbiBpcyBhdmFpbGFibGUgdGhyb3VnaCB0aGUgYGNhcmAgcGFja2FnZS4NCg0KDQoNCmBgYHtyIGdldExpYnJhcnl9DQpsaWJyYXJ5KGNhcikNCmBgYA0KDQpgYGB7ciBDbGltYXRlLmJ0fQ0KYmFydGxldHQudGVzdChNbkpseVRlbXAgfiBmYWN0b3IoTm9ydGhJc2xhbmQpLCBkYXRhPUNsaW1hdGUpDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkoY2FyKQ0KbGV2ZW5lVGVzdChNbkpseVRlbXAgfiBmYWN0b3IoTm9ydGhJc2xhbmQpLCBkYXRhPUNsaW1hdGUpDQpgYGANCg0KDQpOb3RlIHRoZSB1c2Ugb2YgdGhlIGBmYWN0b3IoKWAgZnVuY3Rpb24gdG8gaW5kaWNhdGUgdGhhdCB3ZSB3YW50IHRvIHRyZWF0IHRoZSBgTm9ydGhJc2xhbmRgIHZhcmlhYmxlIGFzIGEgZ3JvdXAgaW5kaWNhdG9yIHZhcmlhYmxlIChyYXRoZXIgdGhhbiBhIG51bWVyaWNhbCB2YXJpYWJsZSkuDQoNClRoZSAqcCotdmFsdWUgZm9yIGJvdGggdGhlc2UgdGVzdHMgaXMgbGFyZ2UsICB3aGljaCBjb25maXJtcyB0aGF0LCBmb3IgdGhlIENsaW1hdGUgZGF0YSwgd2UgaGF2ZSBubyByZWFzb24gdG8gZG91YnQgdGhlIGVxdWFsIHZhcmlhbmNlICBhc3N1bXB0aW9uLg0KDQoNCiMjIyBHb2luZyBmdXJ0aGVyDQoNCkEgbmF0dXJhbCBxdWVzdGlvbiBpcyB0byBhc2sgd2hhdCBoYXBwZW5zIHdoZW4gd2UgaGF2ZSBtb3JlIHRoYW4gdHdvIGdyb3Vwcy4gVGhpcyB3aWxsIGJlIHRoZSB0b3BpYyBvZiB0aGUgbmV4dCBsZWN0dXJlLg0KDQoNCiMjIE1vZGVscyBmb3IgYSBzaW5nbGUgbnVtZXJpYyBwcmVkaWN0b3IgYW5kIGEgYmluYXJ5IGdyb3VwIHZhcmlhYmxlDQoNCkJpbmFyeSBwcmVkaWN0b3JzIGNhbiBhbHNvIGJlIHVzZWQgdG8gZGVhbCB3aXRoIGNoYW5nZXMgaW4gc2xvcGUgYmV0d2VlbiB0d28gZ3JvdXBzLg0KDQpGb3IgZXhhbXBsZSwgaW4gdGhlaXIgYm9vayAiUmVncmVzc2lvbiBBbmFseXNpcyBieSBFeGFtcGxlIiwgQ2hhdHRlcmplZSBhbmQgUHJpY2UgcHJlc2VudCBzb21lIGRhdGEgb24gdGhlIGltcGFjdCBvZiBhbW91bnQgb2YgcmV2aXNpb24gaW1wYWN0cyBzY29yZXMgaW4gYW4gRW5nbGlzaCBleGFtIGRpZmZlcmVudGx5IGZvciBib3lzIGFuZCBnaXJscy4gTGV0Og0KDQotICRZX2kkCT0gc2NvcmUgaW4gRW5nbGlzaCBleGFtLA0KLQkkeF9pJAk9IG51bWJlciBvZiBob3VycyBzcGVudCBpbiByZXZpc2lvbiwNCi0gJHpfaSQgPSBzZXggKDAgZm9yIGJveXMsIDEgZm9yIGdpcmxzKS4NCg0KDQpOLkIuIGByIHhmdW46OmVtYmVkX2ZpbGUoIi4uLy4uL2RhdGEvRW5nbGlzaEV4YW0uY3N2IilgIGlmIHlvdSB3YW50IHRvIHJlcGxpY2F0ZSB0aGUgZm9sbG93aW5nIGV4YW1wbGVzLg0KDQoNCmBgYHtyIGdldEVuZ2xpc2gsIGV2YWwgPSAtMiwgZWNobyA9IC0xfQ0KRW5nbGlzaCA8LSByZWFkLmNzdihmaWxlPSIuLi8uLi9kYXRhL0VuZ2xpc2hFeGFtLmNzdiIsIGhlYWRlcj1UUlVFKQ0KRW5nbGlzaCA8LSByZWFkLmNzdihmaWxlPSJFbmdsaXNoRXhhbS5jc3YiLCBoZWFkZXI9VFJVRSkNCmhlYWQoRW5nbGlzaCkNCmBgYA0KDQoNCkxvb2tpbmcgYXQgdGhlIGRhdGEgZ3JhcGhpY2FsbHkgd2Ugc2VlOg0KDQpgYGB7ciBFbmdsaXNoRURBLCBlY2hvID0gRkFMU0V9DQpFbmdsaXNoIHw+IA0KICBnZ3Bsb3QoYWVzKHk9cmVzdWx0eSwgeD0gcmV2aXNpb24sIGdyb3VwPSBzZXgsIGNvbG91ciA9IGFzLmZhY3RvcihzZXgpKSkgKyAgDQogIGdlb21fc21vb3RoKGZvcm11bGEgPSB5IH4geCwgbWV0aG9kID0gImxvZXNzIiwgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgbGFicyh4ID0gIlJldmlzaW9uIChob3VycykiLA0KICAgICAgIHkgPSAiUmVzdWx0IiwNCiAgICAgICBjb2xvdXIgPSAiU2V4IikgKyANCiAgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlID0gIlNldDEiKQ0KYGBgDQoNCg0KVGhlIHF1ZXN0aW9uIHdlIGFyZSBhc2tpbmcgaXM6IHNob3VsZCB0aGUgbGluZXMgZm9yIGJveXMgYW5kIGdpcmxzIGhhdmUgYSBkaWZmZXJlbnQgc2xvcGU/DQoNClRvIGFuc3dlciB0aGlzIHF1ZXN0aW9uLCB3ZSBuZWVkIHRvIGZpdCBhIG1vZGVsIGluIHdoaWNoIGJveXMgYW5kIGdpcmxzIGhhdmUgZGlmZmVyZW50IHNsb3Blcy4gQXMgaW4gdGhlIHByZXZpb3VzIHNlY3Rpb24sIGl0IGlzIGNvbnZlbmllbnQgdG8gdGVzdCBmb3IgYSBkaWZmZXJlbmNlIGluIHNsb3BlIGJ5IHNldHRpbmcgYSBiYXNlbGluZSBncm91cCAoc2F5LCB0aGUgYm95cykgd2l0aCBzbG9wZSAkXGJldGFfMSQsIGFuZCBleHByZXNzaW5nIHRoZSBzbG9wZSBmb3IgdGhlIGdpcmxzIGdyb3VwIGFzIGEgc2hpZnRlZCBzbG9wZSAkXGJldGFfMSArIFxkZWx0YSQuIFRoYXQgaXMsIHdlIHdhbnQgYSBtb2RlbCB3aXRoIHRoZSBmb2xsb3dpbmcgcmVzcG9uc2U6IA0KDQokJEVbWV9pXSA9IFxiZXRhXzAgKyBcYmV0YV8xIHhfaSB+XG1ib3h7IGZvciBib3lzLCBhbmR9JCQNCiQkRVtZX2ldID0gXGJldGFfMCArIChcYmV0YV8xICsgXGRlbHRhKSB4X2kgflxtYm94eyBmb3IgZ2lybHN9JCQNCg0KVGhlIGdyb3VwIGluZGljYXRvciB2YXJpYWJsZSAkel9pJCBpcyB2ZXJ5IGNvbnZlbmllbnQgdG8gY29uc3RydWN0IHRoZSBhcHByb3ByaWF0ZSBtb2RlbDoNCg0KJCRFW1lfaV0gPSBcYmV0YV8wICsgKFxiZXRhXzEgKyBcZGVsdGEgel9pKSB4X2kgPSBcYmV0YV8wICsgXGJldGFfMSB4X2kgKyBcZGVsdGEgeF9pIHpfaSQkDQoNCg0KSGVyZSwgJHhfaSB6X2kkIGNvcnJlc3BvbmRzIHRvIGEgbmV3IHZhcmlhYmxlIHRoYXQgaXMgZXF1YWwgdG8gMCBmb3IgYm95cyBhbmQgdG8gJHhfaSQgZm9yIGdpcmxzLiBJbiBvcmRlciB0byBmaXQgdGhlIG1vZGVsIHdpdGggYGxtKClgIGluIHRoZSB1c3VhbCB3YXksIHdlIGNhbiBlYXNpbHkgY29uc3RydWN0IHRoaXMgbmV3IHZhcmlhYmxlOg0KDQpgYGB7ciBjb25zdHJ1Y3ROZXdWYXJpYWJsZX0NCkVuZ2xpc2gkcmV2aXNpb25HaXJscyA8LSBFbmdsaXNoJHJldmlzaW9uICogRW5nbGlzaCRzZXgNCmhlYWQoRW5nbGlzaCkNCmBgYA0KDQoNCldlIGNhbiBub3cgZml0IHRoZSBtb2RlbDoNCg0KYGBge3IgRGlmZlNsb3Blc01vZGVsc30NCkVuZ2xpc2gubG0xIDwtIGxtKHJlc3VsdHkgfiByZXZpc2lvbiwgZGF0YSA9IEVuZ2xpc2gpDQpFbmdsaXNoLmxtMiA8LSBsbShyZXN1bHR5IH4gcmV2aXNpb24gKyByZXZpc2lvbkdpcmxzLCBkYXRhID0gRW5nbGlzaCkNCg0Kc3VtbWFyeShFbmdsaXNoLmxtMikNCmFub3ZhKEVuZ2xpc2gubG0xLCBFbmdsaXNoLmxtMikNCmBgYA0KDQpUaGUgY29lZmZpY2llbnQgYXNzb2NpYXRlZCB3aXRoIG91ciBjb25zdHJ1Y3RlZCB2YXJpYWJsZSBgcmV2aXNpb25HaXJsc2AgaXMgc2lnbmlmaWNhbnQgKCpwKi12YWx1ZSAkXGFwcHJveCAwLjA0JCksIHdoaWNoIGluZGljYXRlcyB0aGF0IHRoZXJlIGlzIGEgZGlmZmVyZW5jZSBpbiB0aGUgc2xvcGUgYmV0d2VlbiBib3lzIGFuZCBnaXJscy4NCg==