Type 3 Anova in R.

Intro

This is a simple exercise where we do an Anova test. However we have unbalanced data. We therefore have to be carefull what we ask R to do. This post will work through a simple example showing how to do type III in R. It matters because the default anova in R is type I. This might not always be what you want.

What does type III do?

Before we start some explanation about type III anova. You can skip this if you just want to know how to do this in R.

In unbalanced studies ie we dont have the same number of observation in every cell. This Type III or type I difference matters. When you use type III you hypothesise that sample size of the cell is independent of the sample size.

Therefore we use unweighted means. Alternatively one can say; H0, does not depend on the the proportion in the cells.

Some caveats => In Type III, the answer changes depending on the parametrization. that is it depends on the reference group. In this data we have man and woman. The P values for the Anova table will depend on who we consider as reference group.

However it is not order dependend.(type I is) type III tests the main effect to the full model. (we do not respect the rule of marginality). And thus depends on the parametrization. To be valid one needs to use ‘contr.sum’ in R.

So how does Type III anova test for main effects?

for the effect of factor A we test y= mu + b + ab eps y= mu + a + b + ab + eps

for the effect of factor B we test y= mu + a + ab eps y= mu + a + b + ab + eps

for the interaction effect we test y= mu + a + b + eps y= mu + a + b + ab + eps

The important take away for type III is that if you have an unbalanced study and you hypothesise that the cells have different sizes and that this is not due to something important. Use type III.

The magic code

# type III 
fit.type3 <- Anova(lm(DependentVar~factorA*factorB,
contrasts=list(factorA='contr.sum', FactorB ='contr.sum'), data = data),
type='III')

An example

Data

Below you can see the our data. In total we have 132 salries of both man and woman. We do not have the same count in every cell. what we mean by this is that, we do not have exactly the same amount of people in either male or woman or in the degree no degree groups. Therefore we have an unbalanced study.

        
         No degree Degree
  Male          42     18
  Female        24     48

Plots

Here I show a couple of ways you could visualize your data.

two plots depending on your preferense for Moustache plots (common name box plots) or Violin plots ( a very PC name for kutjes plot).

interaction plot

interaction.plot(Education, Gender, Salary, xlab = 'Education', ylab = 'Salary', main= 'interaction plot for Education and gender')

Mean plots

The mean plots show us a very nice descriptive because it shows that woman and men differ but not by a lot. However the difference in salary for those with degree vs those without degree is “dramatic”. Now we want to test if this difference we see in the plot is statistically different.

The test

we test, Do woman get paid on average less than man?

\(H_0\) = \[\dfrac { (\overline\mu_{11}+\overline\mu_{12})}{2} = \dfrac {( \overline\mu_{21}+\overline\mu_{22})}{2}\]

we need to specify contr.sum in R to use type III.

data$Gender =relevel(Gender, ref="Male" )
data$Education=relevel(Education, ref="No degree")
contrasts(data$Gender) = contr.sum
contrasts(data$Education) = contr.sum
fit.type3 <-Anova(lm(Salary ~ Gender*Education, data = data), type ='III')

summary(fit.type3)


pander(fit.type3)
data$Gender =relevel(Gender, ref="Female" )
data$Education=relevel(Education, ref="Degree")
contrasts(data$Gender) = contr.sum
contrasts(data$Education) = contr.sum
fit.type3.b
Analysis of Deviance Table (Type III tests)

Response: Salary
                  Df         F    Pr(>F)    
(Intercept)        1 6148.2213 < 2.2e-16 ***
Gender             1   23.2910 3.894e-06 ***
Education          1  111.9487 < 2.2e-16 ***
Gender:Education   1    0.3385    0.5617    
Residuals        128                        
---
Signif. codes:  
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

And there you have it, On average Gender and education lead to different salaries.

Do note to make our analysis robust we include the argument white.adjust = TRUE in the Anova function.

We can go more granular by using Tukey multicomp.

Tukey

Approach 1

The code below gives the robust Tukey test. The detail is in the vcov = vcovHC.

confint(tukey_treat_all)

     Simultaneous Confidence Intervals

Fit: lm(formula = Salary ~ Gender * Education, data = data)

Quantile = 2.505
95% family-wise confidence level
 

Linear Hypotheses:
                        Estimate lwr     upr    
(Intercept) == 0        22.9563  22.2229 23.6897
Gender1 == 0            -1.4129  -2.1463 -0.6795
Education1 == 0          3.0977   2.3643  3.8311
Gender1:Education1 == 0  0.1703  -0.5631  0.9037

Approach 2

In this approach we really compare all the groups. In our case we do not have that many groups so I would prefer to do this.

fit.lm2<- aov(Salary~Gender*Education, data = data)  
thsd<-TukeyHSD(fit.lm2)
pander(thsd$`Gender:Education`)

-----------------------------------------------------------------------
             &nbsp;                diff     lwr      upr       p adj   
-------------------------------- -------- -------- -------- -----------
 **Male:Degree-Female:Degree**    2.485    0.3809    4.59     0.01359  

          **Female:No             -6.536   -8.439   -4.633   5.218e-14 
     degree-Female:Degree**                                            

           **Male:No              -3.369   -4.978   -1.761   1.467e-06 
     degree-Female:Degree**                                            

          **Female:No             -9.021   -11.4    -6.647   2.82e-14  
      degree-Male:Degree**                                             

 **Male:No degree-Male:Degree**   -5.855     -8     -3.71    4.469e-10 

   **Male:No degree-Female:No     3.167    1.218    5.115    0.000255  
            degree**                                                   
-----------------------------------------------------------------------
thsd$`Gender:Education`
                                     diff         lwr
Male:Degree-Female:Degree        2.485193   0.3808626
Female:No degree-Female:Degree  -6.536031  -8.4394704
Male:No degree-Female:Degree    -3.369498  -4.9781975
Female:No degree-Male:Degree    -9.021225 -11.3952304
Male:No degree-Male:Degree      -5.854691  -7.9996242
Male:No degree-Female:No degree  3.166533   1.2183015
                                      upr        p adj
Male:Degree-Female:Degree        4.589524 1.358516e-02
Female:No degree-Female:Degree  -4.632592 5.218048e-14
Male:No degree-Female:Degree    -1.760798 1.467154e-06
Female:No degree-Male:Degree    -6.647219 2.819966e-14
Male:No degree-Male:Degree      -3.709759 4.469114e-10
Male:No degree-Female:No degree  5.114765 2.549672e-04

We can also plot the TukeyHSD, the best plot is the last one.

plot(thsd)

From the above you should conclude the following.

a Woman without a degree makes less than a man with a degree. a woman without a degree earns less than a woman with a degree. a woman with degree earns less than a man with a degree.

a Man without degree makes less than a woman with a degree. a man without degree makes less than a man with a degree. a man without degree makes more than a woman without degree.

You should check if your model does not violate assumtions

Assumptions what? you need the residuals to be normaly distributed. Equal variance (check for homoscedasticity, a difficult word that means equal variance). Outliers might cause issues.

shapiro.test(residuals(fit.lm2))

    Shapiro-Wilk normality test

data:  residuals(fit.lm2)
W = 0.99563, p-value = 0.9614

we do not reject the shapiro wilk test therefore we have normality.

durbinWatsonTest(fit.lm2, alternative="two.sided",data=data)
 lag Autocorrelation D-W Statistic p-value
   1      -0.1826385      2.345174   0.086
 Alternative hypothesis: rho != 0

we do not reject the durbin watson test hence we have independence.

leveneTest(Salary~Gender*Education, data=data)
Levene's Test for Homogeneity of Variance (center = median)
       Df F value Pr(>F)
group   3  0.8222 0.4839
      128               

No rejection hence we have equal variance.

plot(fit.lm2)

the residuals look normal we seem to have a few outliers but we should not worry about them affecting our estimates to much since we used a Robust approach above.

LS0tDQp0aXRsZTogIkFub3ZhIFR5cGUgMywgR2VuZGVyIHBheWdhcCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgVHlwZSAzIEFub3ZhIGluIFIuIA0KDQojIyBJbnRybyANClRoaXMgaXMgYSBzaW1wbGUgZXhlcmNpc2Ugd2hlcmUgd2UgZG8gYW4gQW5vdmEgdGVzdC4gSG93ZXZlciB3ZSBoYXZlIHVuYmFsYW5jZWQgZGF0YS4gDQpXZSB0aGVyZWZvcmUgaGF2ZSB0byBiZSBjYXJlZnVsbCB3aGF0IHdlIGFzayBSIHRvIGRvLiANClRoaXMgcG9zdCB3aWxsIHdvcmsgdGhyb3VnaCBhIHNpbXBsZSBleGFtcGxlIHNob3dpbmcgaG93IHRvIGRvIHR5cGUgSUlJIGluIFIuIA0KSXQgbWF0dGVycyBiZWNhdXNlIHRoZSBkZWZhdWx0IGFub3ZhIGluIFIgaXMgdHlwZSBJLiBUaGlzIG1pZ2h0IG5vdCBhbHdheXMgYmUgd2hhdCB5b3Ugd2FudC4NCg0KDQojIyMgV2hhdCBkb2VzIHR5cGUgSUlJIGRvPw0KQmVmb3JlIHdlIHN0YXJ0IHNvbWUgZXhwbGFuYXRpb24gYWJvdXQgdHlwZSBJSUkgYW5vdmEuIFlvdSBjYW4gc2tpcCB0aGlzIGlmIHlvdSBqdXN0IHdhbnQgdG8ga25vdyBob3cgdG8gZG8gdGhpcyBpbiBSLg0KDQpJbiB1bmJhbGFuY2VkIHN0dWRpZXMgaWUgd2UgZG9udCBoYXZlIHRoZSBzYW1lIG51bWJlciBvZiBvYnNlcnZhdGlvbiBpbiBldmVyeSBjZWxsLiBUaGlzIFR5cGUgSUlJIG9yIHR5cGUgSSBkaWZmZXJlbmNlIG1hdHRlcnMuIA0KV2hlbiB5b3UgdXNlIHR5cGUgSUlJIHlvdSBoeXBvdGhlc2lzZSB0aGF0IHNhbXBsZSBzaXplIG9mIHRoZSBjZWxsIGlzIGluZGVwZW5kZW50IG9mIHRoZSBzYW1wbGUgc2l6ZS4gDQoNClRoZXJlZm9yZSB3ZSB1c2UgdW53ZWlnaHRlZCBtZWFucy4gDQpBbHRlcm5hdGl2ZWx5IG9uZSBjYW4gc2F5OyANCkgwLCBkb2VzIG5vdCBkZXBlbmQgb24gdGhlIHRoZSBwcm9wb3J0aW9uIGluIHRoZSBjZWxscy4NCg0KU29tZSBjYXZlYXRzID0+IA0KSW4gVHlwZSBJSUksIHRoZSBhbnN3ZXIgY2hhbmdlcyBkZXBlbmRpbmcgb24gdGhlIHBhcmFtZXRyaXphdGlvbi4gDQp0aGF0IGlzIGl0IGRlcGVuZHMgb24gdGhlIHJlZmVyZW5jZSBncm91cC4gSW4gdGhpcyBkYXRhIHdlIGhhdmUgbWFuIGFuZCB3b21hbi4gDQpUaGUgUCB2YWx1ZXMgZm9yIHRoZSBBbm92YSB0YWJsZSB3aWxsIGRlcGVuZCBvbiB3aG8gd2UgY29uc2lkZXIgYXMgcmVmZXJlbmNlIGdyb3VwLiANCg0KSG93ZXZlciBpdCBpcyBub3Qgb3JkZXIgZGVwZW5kZW5kLih0eXBlIEkgaXMpIA0KdHlwZSBJSUkgdGVzdHMgdGhlIG1haW4gZWZmZWN0IHRvIHRoZSBmdWxsIG1vZGVsLiAod2UgZG8gKm5vdCByZXNwZWN0KiB0aGUgcnVsZSBvZiBtYXJnaW5hbGl0eSkuIEFuZCB0aHVzIGRlcGVuZHMgb24gdGhlIHBhcmFtZXRyaXphdGlvbi4gVG8gYmUgdmFsaWQgb25lIG5lZWRzIHRvIHVzZSAnY29udHIuc3VtJyBpbiBSLg0KDQpTbyBob3cgZG9lcyBUeXBlIElJSSBhbm92YSB0ZXN0IGZvciBtYWluIGVmZmVjdHM/IA0KDQpfZm9yIHRoZSBlZmZlY3Qgb2YgZmFjdG9yIEEgd2UgdGVzdF8gDQp5PSBtdSArIGIgKyBhYiBlcHMNCnk9IG11ICsgYSArIGIgKyBhYiArIGVwcyANCg0KX2ZvciB0aGUgZWZmZWN0IG9mIGZhY3RvciBCIHdlIHRlc3RfIA0KeT0gbXUgKyBhICsgYWIgZXBzDQp5PSBtdSArIGEgKyBiICsgYWIgKyBlcHMgDQoNCl9mb3IgdGhlIGludGVyYWN0aW9uIGVmZmVjdCB3ZSB0ZXN0Xw0KeT0gbXUgKyBhICsgYiArIGVwcw0KeT0gbXUgKyBhICsgYiArIGFiICsgZXBzIA0KDQoNClRoZSAqaW1wb3J0YW50KiB0YWtlIGF3YXkgZm9yIHR5cGUgSUlJIGlzIHRoYXQgaWYgeW91IGhhdmUgYW4gdW5iYWxhbmNlZCBzdHVkeSBhbmQgeW91IGh5cG90aGVzaXNlIHRoYXQgdGhlIGNlbGxzIGhhdmUgZGlmZmVyZW50IHNpemVzIGFuZCB0aGF0IHRoaXMgaXMgbm90IGR1ZSB0byBzb21ldGhpbmcgaW1wb3J0YW50LiAqVXNlIHR5cGUgSUlJKi4gICANCg0KIyMgVGhlIG1hZ2ljIGNvZGUgDQpgYGB7ciBnZW5lcmljIHR5cGUgSUlJIGFub3ZhfQ0KIyB0eXBlIElJSSANCmZpdC50eXBlMyA8LSBBbm92YShsbShEZXBlbmRlbnRWYXJ+ZmFjdG9yQSpmYWN0b3JCLA0KY29udHJhc3RzPWxpc3QoZmFjdG9yQT0nY29udHIuc3VtJywgRmFjdG9yQiA9J2NvbnRyLnN1bScpLCBkYXRhID0gZGF0YSksDQp0eXBlPSdJSUknKQ0KYGBgDQoNCiMgQW4gZXhhbXBsZSANCg0KYGBge3IgcGFja2FnZXMsIGluY2x1ZGU9RkFMU0V9DQojZ3JhcGgNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ3Bsb3RzKSANCiMgbWF0aCANCmxpYnJhcnkobXVsdGljb24pDQpsaWJyYXJ5KG11bHRjb21wKSAjIG9uZSBvZiB0aG9zZSBkb2VzIHRoZSBtZWFucyBwbG90cw0KbGlicmFyeShhZ3JpY29sYWUpICMgZG9lcyB0aGUgVHVrZXkgdGVzdA0KbGlicmFyeShNQVNTKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGNhcikgDQpsaWJyYXJ5KHNhbmR3aWNoKSAjcm9idXN0bmVzcw0KDQojb3V0cHV0DQpsaWJyYXJ5KHBhbmRlcikNCmxpYnJhcnkoa25pdHIpDQpgYGANCg0KIyMgRGF0YQ0KYGBge3IgZGF0YSwgaW5jbHVkZT1GQUxTRX0NCmRhdGEgPC0gcmVhZC50YWJsZSgiU0FMQVJZMS50eHQiLCBoZWFkZXIgPSBULCBzZXAgPSAiLCIsIGRlYyA9ICIuIikNCmF0dGFjaChkYXRhKQ0KYGBgDQoNCkJlbG93IHlvdSBjYW4gc2VlIHRoZSBvdXIgZGF0YS4gSW4gdG90YWwgd2UgaGF2ZSAxMzIgc2FscmllcyBvZiBib3RoIG1hbiBhbmQgd29tYW4uIFdlIGRvIG5vdCBoYXZlIHRoZSBzYW1lIGNvdW50IGluIGV2ZXJ5IGNlbGwuIHdoYXQgd2UgbWVhbiBieSB0aGlzIGlzIHRoYXQsIHdlIGRvIG5vdCBoYXZlIGV4YWN0bHkgdGhlIHNhbWUgYW1vdW50IG9mIHBlb3BsZSBpbiBlaXRoZXIgbWFsZSBvciB3b21hbiBvciBpbiB0aGUgZGVncmVlIG5vIGRlZ3JlZSBncm91cHMuIFRoZXJlZm9yZSB3ZSBoYXZlIGFuIHVuYmFsYW5jZWQgc3R1ZHkuIA0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KdW5iYWxhbmNlZDwtdGFibGUoZGF0YSRHZW5kZXIsZGF0YSRFZHVjYXRpb24pDQp1bmJhbGFuY2VkDQpgYGANCg0KIyMjIFBsb3RzIA0KDQpIZXJlIEkgc2hvdyBhIGNvdXBsZSBvZiB3YXlzIHlvdSBjb3VsZCB2aXN1YWxpemUgeW91ciBkYXRhLiANCmBgYHtyIGJveHBsb3QgdG8gY29tcGFyZSBncm91cHMsIGVjaG89RkFMU0V9DQojIFNhbGFyeSBkZXZpZGVkIGJ5IGdlbmRlciBhbmQgZWR1Y2F0aW9uIGxldmVsDQpnZ3Bsb3QoZGF0YSA9IGRhdGEpICsNCiAgYWVzKHggPSBHZW5kZXIsIHkgPSBTYWxhcnksIGZpbGwgPSBFZHVjYXRpb24pICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlJkWWxCdSIpICsNCiAgbGFicyh0aXRsZSA9ICJTYWxhcnkgZGV2aWRlZCBieSBnZW5kZXIgYW5kIGVkdWNhdGlvbiBsZXZlbCIsDQogICAgeCA9ICJHZW5kZXIiLA0KICAgIHkgPSAiU2FsYXJ5IikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ3RvcCcpKw0KICBjb29yZF9mbGlwKCkrDQogIGZhY2V0X2dyaWQoR2VuZGVyIH4gLiwgc2NhbGVzID0gImZyZWVfeSIpDQpgYGANCg0KDQpgYGB7ciB2aW9saW4gcGxvdHMsIGVjaG89RkFMU0V9DQpnZ3Bsb3QoZGF0YSA9IGRhdGEpICsNCmFlcyh4ID0gR2VuZGVyLCB5ID0gU2FsYXJ5LCBmaWxsID0gRWR1Y2F0aW9uKSArDQpnZW9tX3Zpb2xpbihzY2FsZSA9ICJhcmVhIikrDQogICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArDQogICBsYWJzKHRpdGxlID0gIlNhbGFyeSBkZXZpZGVkIGJ5IGdlbmRlciBhbmQgZWR1Y2F0aW9uIGxldmVsIiwNCiAgICB4ID0gIkdlbmRlciIsDQogICAgeSA9ICJTYWxhcnkiKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAndG9wJykNCmBgYA0KDQp0d28gcGxvdHMgZGVwZW5kaW5nIG9uIHlvdXIgcHJlZmVyZW5zZSBmb3IgTW91c3RhY2hlIHBsb3RzIChjb21tb24gbmFtZSBib3ggcGxvdHMpIG9yIFZpb2xpbiBwbG90cyAoIGEgdmVyeSBQQyBuYW1lIGZvciBrdXRqZXMgcGxvdCkuIA0KDQojIyMgaW50ZXJhY3Rpb24gcGxvdA0KDQpgYGB7cn0NCmludGVyYWN0aW9uLnBsb3QoRWR1Y2F0aW9uLCBHZW5kZXIsIFNhbGFyeSwgeGxhYiA9ICdFZHVjYXRpb24nLCB5bGFiID0gJ1NhbGFyeScsIG1haW49ICdpbnRlcmFjdGlvbiBwbG90IGZvciBFZHVjYXRpb24gYW5kIGdlbmRlcicpDQpgYGANCg0KDQojIyMgTWVhbiBwbG90cw0KYGBge3IgdGhlc2UgbWVhbiBwbG90cywgZWNobz1GQUxTRX0NCg0KcGxvdG1lYW5zKFNhbGFyeX4gR2VuZGVyLHlsYWI9ICJTYWxhcnkiLCB4bGFiPSAiR2VuZGVyIiwgbWFpbj0iTWVhbiBQbG90d2l0aCA5NSUgQ0kiLCBkYXRhPSBkYXRhKQ0KDQpwbG90bWVhbnMoU2FsYXJ5fkVkdWNhdGlvbix5bGFiPSAiU2FsYXJ5IiwgeGxhYj0gIkVkdWNhdGlvbiIsIG1haW49Ik1lYW4gUGxvdHdpdGggOTUlIENJIiwgZGF0YT0gZGF0YSkNCmBgYA0KDQpUaGUgbWVhbiBwbG90cyBzaG93IHVzIGEgdmVyeSBuaWNlIGRlc2NyaXB0aXZlIGJlY2F1c2UgaXQgc2hvd3MgdGhhdCB3b21hbiBhbmQgbWVuIGRpZmZlciBidXQgbm90IGJ5IGEgbG90LiBIb3dldmVyIHRoZSBkaWZmZXJlbmNlIGluIHNhbGFyeSBmb3IgdGhvc2Ugd2l0aCBkZWdyZWUgdnMgdGhvc2Ugd2l0aG91dCBkZWdyZWUgaXMgImRyYW1hdGljIi4NCk5vdyB3ZSB3YW50IHRvIHRlc3QgaWYgdGhpcyBkaWZmZXJlbmNlIHdlIHNlZSBpbiB0aGUgcGxvdCBpcyBzdGF0aXN0aWNhbGx5IGRpZmZlcmVudC4gDQoNCiMjIyBUaGUgdGVzdCANCg0Kd2UgdGVzdCwgRG8gd29tYW4gZ2V0IHBhaWQgb24gYXZlcmFnZSBsZXNzIHRoYW4gbWFuPyANCg0KJEhfMCQgPSAkJFxkZnJhYyB7IChcb3ZlcmxpbmVcbXVfezExfStcb3ZlcmxpbmVcbXVfezEyfSl9ezJ9ID0gXGRmcmFjIHsoIFxvdmVybGluZVxtdV97MjF9K1xvdmVybGluZVxtdV97MjJ9KX17Mn0kJA0KDQp3ZSBuZWVkIHRvIHNwZWNpZnkgY29udHIuc3VtIGluIFIgdG8gdXNlIHR5cGUgSUlJLiANCmBgYHtyfQ0KZGF0YSRHZW5kZXIgPXJlbGV2ZWwoR2VuZGVyLCByZWY9Ik1hbGUiICkNCmRhdGEkRWR1Y2F0aW9uPXJlbGV2ZWwoRWR1Y2F0aW9uLCByZWY9Ik5vIGRlZ3JlZSIpDQpjb250cmFzdHMoZGF0YSRHZW5kZXIpID0gY29udHIuc3VtDQpjb250cmFzdHMoZGF0YSRFZHVjYXRpb24pID0gY29udHIuc3VtDQpgYGANCg0KYGBge3IgZWNobz1UUlVFfQ0KZml0LnR5cGUzIDwtQW5vdmEobG0oU2FsYXJ5IH4gR2VuZGVyKkVkdWNhdGlvbiwgZGF0YSA9IGRhdGEpLCB0eXBlID0nSUlJJykNCg0Kc3VtbWFyeShmaXQudHlwZTMpDQoNCg0KcGFuZGVyKGZpdC50eXBlMykNCmBgYA0KDQpgYGB7ciBvdGhlciByZWZlcmVuY2UgZ3JvdXB9DQpkYXRhJEdlbmRlciA9cmVsZXZlbChHZW5kZXIsIHJlZj0iRmVtYWxlIiApDQpkYXRhJEVkdWNhdGlvbj1yZWxldmVsKEVkdWNhdGlvbiwgcmVmPSJEZWdyZWUiKQ0KY29udHJhc3RzKGRhdGEkR2VuZGVyKSA9IGNvbnRyLnN1bQ0KY29udHJhc3RzKGRhdGEkRWR1Y2F0aW9uKSA9IGNvbnRyLnN1bQ0KYGBgDQoNCmBgYHtyIGVjaG89VFJVRX0NCmZpdC50eXBlMy5iIDwtQW5vdmEobG0oU2FsYXJ5IH4gR2VuZGVyKkVkdWNhdGlvbiwgZGF0YSA9IGRhdGEpLCB0eXBlID0nSUlJJyx3aGl0ZS5hZGp1c3Q9VFJVRSkNCg0KZml0LnR5cGUzLmINCg0KDQpwYW5kZXIoZml0LnR5cGUzLmIpDQpgYGANCg0KQW5kIHRoZXJlIHlvdSBoYXZlIGl0LCANCk9uIGF2ZXJhZ2UgR2VuZGVyIGFuZCBlZHVjYXRpb24gbGVhZCB0byBkaWZmZXJlbnQgc2FsYXJpZXMuIA0KDQpfRG8gbm90ZV8gdG8gbWFrZSBvdXIgYW5hbHlzaXMgcm9idXN0IHdlIGluY2x1ZGUgdGhlIGFyZ3VtZW50IHdoaXRlLmFkanVzdCA9IFRSVUUgaW4gdGhlIEFub3ZhIGZ1bmN0aW9uLiANCg0KV2UgY2FuIGdvIG1vcmUgZ3JhbnVsYXIgYnkgdXNpbmcgVHVrZXkgbXVsdGljb21wLiANCg0KIyMjIFR1a2V5IA0KDQojIyMjIEFwcHJvYWNoIDENCg0KVGhlIGNvZGUgYmVsb3cgZ2l2ZXMgdGhlIHJvYnVzdCBUdWtleSB0ZXN0LiANClRoZSBkZXRhaWwgaXMgaW4gdGhlIHZjb3YgPSB2Y292SEMuIA0KYGBge3J9DQoNCmZpdC5sbSA8LSBsbShTYWxhcnl+R2VuZGVyKkVkdWNhdGlvbixkYXRhPWRhdGEpDQp0dWtleV90cmVhdF9hbGw8LSBnbGh0KGZpdC5sbSwgRWR1Y2F0aW9uID0gbWNwKHRyZWF0PSAiVHVrZXkiKSwgdmNvdiA9IHZjb3ZIQykNCg0KDQpjb25maW50KHR1a2V5X3RyZWF0X2FsbCkNCg0KYGBgDQoNCiMjIyMgQXBwcm9hY2ggMiANCg0KSW4gdGhpcyBhcHByb2FjaCB3ZSByZWFsbHkgY29tcGFyZSBhbGwgdGhlIGdyb3Vwcy4gSW4gb3VyIGNhc2Ugd2UgZG8gbm90IGhhdmUgdGhhdCBtYW55IGdyb3VwcyBzbyBJIHdvdWxkIHByZWZlciB0byBkbyB0aGlzLiANCg0KDQpgYGB7cn0NCg0KZml0LmxtMjwtIGFvdihTYWxhcnl+R2VuZGVyKkVkdWNhdGlvbiwgZGF0YSA9IGRhdGEpICANCg0KdGhzZDwtVHVrZXlIU0QoZml0LmxtMikNCg0KcGFuZGVyKHRoc2QkYEdlbmRlcjpFZHVjYXRpb25gKQ0KDQp0aHNkJGBHZW5kZXI6RWR1Y2F0aW9uYA0KDQpgYGANCg0KV2UgY2FuIGFsc28gcGxvdCB0aGUgVHVrZXlIU0QsIHRoZSBiZXN0IHBsb3QgaXMgdGhlIGxhc3Qgb25lLiANCg0KYGBge3J9DQpwbG90KHRoc2QpDQpgYGANCg0KDQpGcm9tIHRoZSBhYm92ZSB5b3Ugc2hvdWxkIGNvbmNsdWRlIHRoZSBmb2xsb3dpbmcuIA0KDQphIFdvbWFuIHdpdGhvdXQgYSBkZWdyZWUgbWFrZXMgbGVzcyB0aGFuIGEgbWFuIHdpdGggYSBkZWdyZWUuDQphIHdvbWFuIHdpdGhvdXQgYSBkZWdyZWUgZWFybnMgbGVzcyB0aGFuIGEgd29tYW4gd2l0aCBhIGRlZ3JlZS4NCmEgd29tYW4gd2l0aCBkZWdyZWUgZWFybnMgbGVzcyB0aGFuIGEgbWFuIHdpdGggYSBkZWdyZWUuDQogDQphIE1hbiB3aXRob3V0IGRlZ3JlZSBtYWtlcyBsZXNzIHRoYW4gYSB3b21hbiB3aXRoIGEgZGVncmVlLg0KYSBtYW4gd2l0aG91dCBkZWdyZWUgbWFrZXMgbGVzcyB0aGFuIGEgbWFuIHdpdGggYSBkZWdyZWUuDQphIG1hbiB3aXRob3V0IGRlZ3JlZSBtYWtlcyBtb3JlIHRoYW4gYSB3b21hbiB3aXRob3V0IGRlZ3JlZS4NCiANCiMjIyBZb3Ugc2hvdWxkIGNoZWNrIGlmIHlvdXIgbW9kZWwgZG9lcyBub3QgdmlvbGF0ZSBhc3N1bXRpb25zDQoNCkFzc3VtcHRpb25zIHdoYXQ/IA0KeW91IG5lZWQgdGhlIHJlc2lkdWFscyB0byBiZSBub3JtYWx5IGRpc3RyaWJ1dGVkLiANCkVxdWFsIHZhcmlhbmNlIChjaGVjayBmb3IgaG9tb3NjZWRhc3RpY2l0eSwgYSBkaWZmaWN1bHQgd29yZCB0aGF0IG1lYW5zIGVxdWFsIHZhcmlhbmNlKS4NCk91dGxpZXJzIG1pZ2h0IGNhdXNlIGlzc3Vlcy4gDQoNCmBgYHtyIHNoYXBpcm8gdGVzdH0NCnNoYXBpcm8udGVzdChyZXNpZHVhbHMoZml0LmxtMikpDQpgYGANCndlIGRvIG5vdCByZWplY3QgdGhlIHNoYXBpcm8gd2lsayB0ZXN0IHRoZXJlZm9yZSB3ZSBoYXZlIG5vcm1hbGl0eS4NCg0KYGBge3IgY2hlY2sgaW5kZXBlbmNlIGR3dGVzdH0NCmR1cmJpbldhdHNvblRlc3QoZml0LmxtMiwgYWx0ZXJuYXRpdmU9InR3by5zaWRlZCIsZGF0YT1kYXRhKQ0KYGBgDQp3ZSBkbyBub3QgcmVqZWN0IHRoZSBkdXJiaW4gd2F0c29uIHRlc3QgaGVuY2Ugd2UgaGF2ZSBpbmRlcGVuZGVuY2UuIA0KDQpgYGB7ciBIb21vb29vfQ0KbGV2ZW5lVGVzdChTYWxhcnl+R2VuZGVyKkVkdWNhdGlvbiwgZGF0YT1kYXRhKQ0KYGBgDQoNCk5vIHJlamVjdGlvbiBoZW5jZSB3ZSBoYXZlIGVxdWFsIHZhcmlhbmNlLiANCg0KYGBge3Igc3RhbmRhcmRpemVkIHJlc2lkdWFscyBjaGVja30NCnBsb3QoZml0LmxtMikNCg0KYGBgDQoNCnRoZSByZXNpZHVhbHMgbG9vayBub3JtYWwgd2Ugc2VlbSB0byBoYXZlIGEgZmV3IG91dGxpZXJzIGJ1dCB3ZSBzaG91bGQgbm90IHdvcnJ5IGFib3V0IHRoZW0gYWZmZWN0aW5nIG91ciBlc3RpbWF0ZXMgdG8gbXVjaCBzaW5jZSB3ZSB1c2VkIGEgUm9idXN0IGFwcHJvYWNoIGFib3ZlLiANCiANCg==