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)

unlabelled

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:

  1. set up the group indicator variable z,

  2. regress Y on z and

  3. 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.

library(car)
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:

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:

unlabelled

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==