In this note we’re going to explore interactions and contrasts in linear models.

We’ll download the data from Scientific Reports by Jonas O. Wolff and Stanislav N. Gorb. It’s looking at the four pair of legs (\(Li\) for each \(i\in{1,..,4}\)) (first column) on a spider and the frictional coefficient of the legs (second column) with respect to some interaction (pull or push)(third column).

#install.packages("downloader","ggplot2")
#library(downloader,ggplot2)
url<- "https://raw.githubusercontent.com/genomicsclass/dagdata/master/inst/extdata/spider_wolff_gorb_2013.csv"
filname<-"spider_wolff_gorb_2013.csv"
if (!file.exists(filname)) download(url,filname)
spider<-read.csv(filname, skip=1)
head(spider)
nrow(spider)
[1] 282

We’d like to recreate the box plot is in the paper. It looks like this:

boxplot(spider$friction~spider$type*spider$leg,
        col=c("grey90","grey40"),las=2,
        main="Comparision of Friction Coefficients of Different Pair Legs", xlab="Legs",ylab="Friction Coefficient")

spider.sub<-spider[spider$leg=="L1",]
fit<-lm(friction~type,data=spider.sub)
summary(fit)

Call:
lm(formula = friction ~ type, data = spider.sub)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.33147 -0.10735 -0.04941 -0.00147  0.76853 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.92147    0.03827  24.078  < 2e-16 ***
typepush    -0.51412    0.05412  -9.499  5.7e-14 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2232 on 66 degrees of freedom
Multiple R-squared:  0.5776,    Adjusted R-squared:  0.5711 
F-statistic: 90.23 on 1 and 66 DF,  p-value: 5.698e-14
(coefs<-coef(fit))
(Intercept)    typepush 
  0.9214706  -0.5141176 

Interpreting the above results:

mean(spider.sub[spider.sub$type=="pull",]$friction)
[1] 0.9214706
mean(spider.sub[spider.sub$type=="push",]$friction)-mean(spider.sub[spider.sub$type=="pull",]$friction)
[1] -0.5141176
#install.packages("devtools")
#library(devtools)
#install_github("ririzarr/rafalib")
#library(rafalib)
X<-model.matrix(~type,data=spider.sub) #pull=0,push=1
colnames(X)
[1] "(Intercept)" "typepush"   
head(X)
  (Intercept) typepush
1           1        0
2           1        0
3           1        0
4           1        0
5           1        0
6           1        0
imagemat(X, main="Model Matrix for Linear Model with Interactions")

Graphically:

install.packages("ggplot2")
Error in install.packages : Updating loaded packages
library(ggplot2)
p<-ggplot(spider.sub)
p<-p+aes(x=type,y=friction)
p<-p+geom_point()+geom_jitter(width=0.1)+geom_smooth(method="loess",formula = "y ~ x")
p<-p+ylab("Friction Coefficient")+xlab("Movement Type")
p

Now we’re going to get the linear models that are slightly more interesting. So now we are going to have two variables in the formula:

X<-model.matrix(~type+leg,data=spider)
colnames(X) #intercept, typepush, and the three non-reference level of leg (L1)
[1] "(Intercept)" "typepush"    "legL2"       "legL3"       "legL4"      
head(X)
  (Intercept) typepush legL2 legL3 legL4
1           1        0     0     0     0
2           1        0     0     0     0
3           1        0     0     0     0
4           1        0     0     0     0
5           1        0     0     0     0
6           1        0     0     0     0
imagemat(X,main="Model Matrix for Linear Model with 2 Factors")

fit2<-lm(friction~type+leg,data=spider)
summary(fit2)

Call:
lm(formula = friction ~ type + leg, data = spider)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.46392 -0.13441 -0.00525  0.10547  0.69509 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  1.05392    0.02816  37.426  < 2e-16 ***
typepush    -0.77901    0.02482 -31.380  < 2e-16 ***
legL2        0.17192    0.04569   3.763 0.000205 ***
legL3        0.16049    0.03251   4.937 1.37e-06 ***
legL4        0.28134    0.03438   8.183 1.01e-14 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2084 on 277 degrees of freedom
Multiple R-squared:  0.7916,    Adjusted R-squared:  0.7886 
F-statistic:   263 on 4 and 277 DF,  p-value: < 2.2e-16
(coefs<-coef(fit2))
(Intercept)    typepush       legL2       legL3       legL4 
  1.0539153  -0.7790071   0.1719216   0.1604921   0.2813382 
spider$group<-factor(paste0(spider$leg,spider$type))
p<-ggplot(spider)
p<-p+aes(x=group,y=friction)
p<-p+geom_point()+geom_jitter(width=0.1)
p

#install.packages("RColorBrewer")
#library(RColorBrewer)
a<- -0.25
lgth<- .1
stripchart(split(spider$friction,spider$group),
           vertical=T,pch=1, method = "jitter",las=2,xlim=c(0,11),ylim=c(0,2))
cols<-brewer.pal(5,"Dark2")
abline(h=0)
arrows(1+a,0,1+a,coefs[1],lwd=3,col=cols[1],length = lgth)
abline(h=coefs[1],col=cols[1])
arrows(3+a,coefs[1],3+a,coefs[1]+coefs[3],lwd=3,col=cols[3],length = lgth)
arrows(5+a,coefs[1],5+a,coefs[1]+coefs[4],lwd=4,col=cols[4],length = lgth)
arrows(7+a,coefs[1],7+a,coefs[1]+coefs[5],lwd=5,col=cols[5],length = lgth)
arrows(2+a,coefs[1],2+a,coefs[1]+coefs[3]+coefs[2],lwd=3,col=cols[2],length = lgth)
segments(3+a,coefs[1]+coefs[3],4+a,coefs[1]+coefs[3],lwd=3,col=cols[3])
arrows(4+a,coefs[1]+coefs[3],4+a,coefs[1]+coefs[3]+coefs[2],lwd=3,col=cols[2],length = lgth)
segments(5+a,coefs[1]+coefs[4],6+a,coefs[1]+coefs[4],lwd=3,col=cols[4])
arrows(6+a,coefs[1]+coefs[4],6+a,coefs[1]+coefs[4]+coefs[2],lwd=3,col=cols[2],length = lgth)
segments(7+a,coefs[1]+coefs[5],8+a,coefs[1]+coefs[5],lwd=3,col=cols[5])
arrows(8+a,coefs[1]+coefs[5],8+a,coefs[1]+coefs[5]+coefs[2],lwd=3,col=cols[2],length = lgth)
legend("right",names(coefs),fill = cols,cex=0.7,bg="beige")

Note that

mean(spider[spider$group=="L1pull",]$friction)
[1] 0.9214706

This is because when we had 2 coefficients and 2 groups, the coefficients could be such that they predicted the exact mean of the two groups. And now we have 8 groups and only 5 coefficients.

Finally, let’s to see how to compare two groups where one of them is not the reference level.

For instance, we want to compare the leg L2 with L3 effect. The way to do that is to use the “contrast”

L3vsL2.equiv<-contrast(fit2,list(leg="L3",type="push"),list(leg="L2",type="push"))
L3vsL2.equiv
lm model parameter contrast

    Contrast       S.E.      Lower      Upper     t  df Pr(>|t|)
 -0.01142949 0.04319685 -0.0964653 0.07360632 -0.26 277   0.7915
L3vsL2.equiv$X
  (Intercept) typepush legL2 legL3 legL4
1           0        0    -1     1     0
attr(,"assign")
[1] 0 1 2 2 2
attr(,"contrasts")
attr(,"contrasts")$type
[1] "contr.treatment"

attr(,"contrasts")$leg
[1] "contr.treatment"

Contrasts Exercises

Suppose we have an experiment with two species A and B, and two conditions: control and treated.

species <- factor(c("A","A","B","B"))
condition <- factor(c("control","treated","control","treated"))

\[\begin{matrix}\text{species}(A)=0, & \text{species}(B)=1\\ \text{condition}(control)=0, & \text{condition}(trated)=1 \end{matrix}\]

And we will use a formula of ~ species + condition.

X<-model.matrix(~ species + condition)
X
  (Intercept) speciesB conditiontreated
1           1        0                0
2           1        0                1
3           1        1                0
4           1        1                1
attr(,"assign")
[1] 0 1 2
attr(,"contrasts")
attr(,"contrasts")$species
[1] "contr.treatment"

attr(,"contrasts")$condition
[1] "contr.treatment"

Contrasts Exercises #1

Suppose we want to build a contrast of coefficients for the above experimental design.

You can either figure this question out through logic, by looking at the design matrix, or using the contrast() function from the contrast library. If you have not done so already, you should download the contrast library. The contrast vector is returned as contrast()$X.

What should the contrast vector be, for the contrast of (species=B and condition=control) vs (species=A and condition=treatment)? Assume that the beta vector from the model fit by R is: Intercept, speciesB, conditiontreated.

Y<-matrix(rnorm(4),ncol=1)
fit4<-lm(Y~species+condition)
summary(fit4)

Call:
lm(formula = Y ~ species + condition)

Residuals:
      1       2       3       4 
 0.2285 -0.2285 -0.2285  0.2285 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)
(Intercept)      -0.03171    0.39571  -0.080    0.949
speciesB         -0.50565    0.45693  -1.107    0.468
conditiontreated -0.84439    0.45693  -1.848    0.316

Residual standard error: 0.4569 on 1 degrees of freedom
Multiple R-squared:  0.8227,    Adjusted R-squared:  0.468 
F-statistic:  2.32 on 2 and 1 DF,  p-value: 0.4211
contrast(fit4,list(species="B",condition="control"),list(species="A",condition="treated"))$X
  (Intercept) speciesB conditiontreated
1           0        1               -1
attr(,"assign")
[1] 0 1 2
attr(,"contrasts")
attr(,"contrasts")$species
[1] "contr.treatment"

attr(,"contrasts")$condition
[1] "contr.treatment"

Contrasts Exercises #2

Suppose we build a model using two variables: ~ type + leg.

What is the t-value for the contrast of leg pair L4 vs leg pair L2?

L4vsL2<-contrast(fit2,list(leg="L4",type="pull"),list(leg="L2",type="pull"))
L4vsL2
lm model parameter contrast

  Contrast       S.E.      Lower     Upper    t  df Pr(>|t|)
 0.1094167 0.04462392 0.02157158 0.1972618 2.45 277   0.0148
L4vsL2$X
  (Intercept) typepush legL2 legL3 legL4
1           0        0    -1     0     1
attr(,"assign")
[1] 0 1 2 2 2
attr(,"contrasts")
attr(,"contrasts")$type
[1] "contr.treatment"

attr(,"contrasts")$leg
[1] "contr.treatment"

Now we can use a linear model with interactions.

X<-model.matrix(~type+leg+type:leg,data=spider)
colnames(X)
[1] "(Intercept)"    "typepush"       "legL2"          "legL3"          "legL4"         
[6] "typepush:legL2" "typepush:legL3" "typepush:legL4"
head(X)
  (Intercept) typepush legL2 legL3 legL4 typepush:legL2 typepush:legL3 typepush:legL4
1           1        0     0     0     0              0              0              0
2           1        0     0     0     0              0              0              0
3           1        0     0     0     0              0              0              0
4           1        0     0     0     0              0              0              0
5           1        0     0     0     0              0              0              0
6           1        0     0     0     0              0              0              0
imagemat(X, main="Matrix Model for Linear Model with Interations")

fit5<-lm(friction~type+leg+type:leg,data=spider)
summary(fit5)

Call:
lm(formula = friction ~ type + leg + type:leg, data = spider)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.46385 -0.10735 -0.01111  0.07848  0.76853 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)     0.92147    0.03266  28.215  < 2e-16 ***
typepush       -0.51412    0.04619 -11.131  < 2e-16 ***
legL2           0.22386    0.05903   3.792 0.000184 ***
legL3           0.35238    0.04200   8.390 2.62e-15 ***
legL4           0.47928    0.04442  10.789  < 2e-16 ***
typepush:legL2 -0.10388    0.08348  -1.244 0.214409    
typepush:legL3 -0.38377    0.05940  -6.461 4.73e-10 ***
typepush:legL4 -0.39588    0.06282  -6.302 1.17e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1904 on 274 degrees of freedom
Multiple R-squared:  0.8279,    Adjusted R-squared:  0.8235 
F-statistic: 188.3 on 7 and 274 DF,  p-value: < 2.2e-16
(coefs<-coef(fit5))
   (Intercept)       typepush          legL2          legL3          legL4 
     0.9214706     -0.5141176      0.2238627      0.3523756      0.4792794 
typepush:legL2 typepush:legL3 typepush:legL4 
    -0.1038824     -0.3837670     -0.3958824 
stripchart(split(spider$friction,spider$group),
           vertical=T,pch=1, method = "jitter",las=2,xlim=c(0,11),ylim=c(0,2))
cols<-brewer.pal(8,"Dark2")
abline(h=0)
arrows(1+a,0,1+a,coefs[1],lwd=3,col=cols[1],length = lgth)
abline(h=coefs[1],col=cols[1])

arrows(2+a,coefs[1],2+a,coefs[1]+coefs[2],lwd=3,col=cols[2],length = lgth)

arrows(3+a,coefs[1],3+a,coefs[1]+coefs[3],lwd=3,col=cols[3],length = lgth)
segments(3+a,coefs[1]+coefs[3],4+a,coefs[1]+coefs[3],lwd=3,col=cols[3])
arrows(4+a,coefs[1]+coefs[3],4+a,coefs[1]+coefs[3]+coefs[2],lwd=3,col=cols[2],length = lgth)
arrows(4+a,coefs[1]+coefs[2]+coefs[3],4+a,coefs[1]+coefs[3]+coefs[2]+coefs[6],lwd=3,col=cols[6],length = lgth)

arrows(5+a,coefs[1],5+a,coefs[1]+coefs[4],lwd=4,col=cols[4],length = lgth)
segments(5+a,coefs[1]+coefs[4],6+a,coefs[1]+coefs[4],lwd=3,col=cols[4])
arrows(6+a,coefs[1]+coefs[4],6+a,coefs[1]+coefs[4]+coefs[2],lwd=3,col=cols[2],length = lgth)
arrows(6+a,coefs[1]+coefs[2]+coefs[4],6+a,coefs[1]+coefs[4]+coefs[2]+coefs[7],lwd=3,col=cols[7],length = lgth)


arrows(7+a,coefs[1],7+a,coefs[1]+coefs[5],lwd=5,col=cols[5],length = lgth)
segments(7+a,coefs[1]+coefs[5],8+a,coefs[1]+coefs[5],lwd=3,col=cols[5])
arrows(8+a,coefs[1]+coefs[5],8+a,coefs[1]+coefs[5]+coefs[2],lwd=3,col=cols[2],length = lgth)
arrows(8+a,coefs[1]+coefs[2]+coefs[5],8+a,coefs[1]+coefs[5]+coefs[2]+coefs[8],lwd=3,col=cols[8],length = lgth)





legend("right",names(coefs),fill = cols,cex=0.7,bg="beige")

LS0tCnRpdGxlOiAiSW50ZXJhY3Rpb24gYW5kIENvbnRyYXN0IEkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkluIHRoaXMgbm90ZSB3ZSdyZSBnb2luZyB0byBleHBsb3JlIGludGVyYWN0aW9ucyBhbmQgY29udHJhc3RzIGluIGxpbmVhciBtb2RlbHMuCgpXZSdsbCBkb3dubG9hZCB0aGUgZGF0YSBmcm9tIFNjaWVudGlmaWMgUmVwb3J0cyBieSBKb25hcyBPLiBXb2xmZiBhbmQgU3RhbmlzbGF2IE4uIEdvcmIuIEl0J3MgbG9va2luZyBhdCB0aGUgZm91ciBwYWlyIG9mIGxlZ3MgKCRMaSQgZm9yIGVhY2ggJGlcaW57MSwuLiw0fSQpIChmaXJzdCBjb2x1bW4pIG9uIGEgc3BpZGVyIGFuZCB0aGUgZnJpY3Rpb25hbCBjb2VmZmljaWVudCBvZiB0aGUgbGVncyAoc2Vjb25kIGNvbHVtbikgd2l0aCByZXNwZWN0IHRvIHNvbWUgaW50ZXJhY3Rpb24gKHB1bGwgb3IgcHVzaCkodGhpcmQgY29sdW1uKS4gCgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoImRvd25sb2FkZXIiLCJnZ3Bsb3QyIikKI2xpYnJhcnkoZG93bmxvYWRlcixnZ3Bsb3QyKQp1cmw8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2dlbm9taWNzY2xhc3MvZGFnZGF0YS9tYXN0ZXIvaW5zdC9leHRkYXRhL3NwaWRlcl93b2xmZl9nb3JiXzIwMTMuY3N2IgpmaWxuYW1lPC0ic3BpZGVyX3dvbGZmX2dvcmJfMjAxMy5jc3YiCmlmICghZmlsZS5leGlzdHMoZmlsbmFtZSkpIGRvd25sb2FkKHVybCxmaWxuYW1lKQpzcGlkZXI8LXJlYWQuY3N2KGZpbG5hbWUsIHNraXA9MSkKaGVhZChzcGlkZXIpCm5yb3coc3BpZGVyKQpgYGAKV2UnZCBsaWtlIHRvIHJlY3JlYXRlIHRoZSBib3ggcGxvdCBpcyBpbiB0aGUgcGFwZXIuIEl0IGxvb2tzIGxpa2UgdGhpczoKCmBgYHtyfQpib3hwbG90KHNwaWRlciRmcmljdGlvbn5zcGlkZXIkdHlwZSpzcGlkZXIkbGVnLAogICAgICAgIGNvbD1jKCJncmV5OTAiLCJncmV5NDAiKSxsYXM9MiwKICAgICAgICBtYWluPSJDb21wYXJpc2lvbiBvZiBGcmljdGlvbiBDb2VmZmljaWVudHMgb2YgRGlmZmVyZW50IFBhaXIgTGVncyIsIHhsYWI9IkxlZ3MiLHlsYWI9IkZyaWN0aW9uIENvZWZmaWNpZW50IikKYGBgCgpgYGB7cn0Kc3BpZGVyLnN1Yjwtc3BpZGVyW3NwaWRlciRsZWc9PSJMMSIsXQpmaXQ8LWxtKGZyaWN0aW9ufnR5cGUsZGF0YT1zcGlkZXIuc3ViKQpzdW1tYXJ5KGZpdCkKKGNvZWZzPC1jb2VmKGZpdCkpCgpgYGAKSW50ZXJwcmV0aW5nIHRoZSBhYm92ZSByZXN1bHRzOgoKKiAiSW50ZXJjZXB0PTAuOTIxNDciIGlzIHRoZSBtZWFuIG9mIHRoZSAicHVsbCIgdHlwZSBvZiAxc3QgcGFpciBvZiBsZWdzLiBXaGljaCBjYW4gYmUgY29tcHV0ZWQgYWxzbyBieToKYGBge3J9Cm1lYW4oc3BpZGVyLnN1YltzcGlkZXIuc3ViJHR5cGU9PSJwdWxsIixdJGZyaWN0aW9uKQpgYGAKKiAidHlwZXB1c2g9LTAuNTE0MTIiIGlzIHRoZSBkaWZmZXJlbmNlIG9mICJwdWxsIiB0eXBlIGFuZCAicHVzaCIgdHlwZSBvZiAxc3QgcGFpciBvZiBsZWdzLiBXaGljaCBjYW4gYmUgY29tcHV0ZWQgYWxzbyBieToKYGBge3J9Cm1lYW4oc3BpZGVyLnN1YltzcGlkZXIuc3ViJHR5cGU9PSJwdXNoIixdJGZyaWN0aW9uKS1tZWFuKHNwaWRlci5zdWJbc3BpZGVyLnN1YiR0eXBlPT0icHVsbCIsXSRmcmljdGlvbikKYGBgCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQojbGlicmFyeShkZXZ0b29scykKI2luc3RhbGxfZ2l0aHViKCJyaXJpemFyci9yYWZhbGliIikKI2xpYnJhcnkocmFmYWxpYikKWDwtbW9kZWwubWF0cml4KH50eXBlLGRhdGE9c3BpZGVyLnN1YikgI3B1bGw9MCxwdXNoPTEKY29sbmFtZXMoWCkKaGVhZChYKQppbWFnZW1hdChYLCBtYWluPSJNb2RlbCBNYXRyaXggZm9yIExpbmVhciBNb2RlbCB3aXRoIEludGVyYWN0aW9ucyIpCmBgYAoKR3JhcGhpY2FsbHk6CgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQojbGlicmFyeShnZ3Bsb3QyKQpwPC1nZ3Bsb3Qoc3BpZGVyLnN1YikKcDwtcCthZXMoeD10eXBlLHk9ZnJpY3Rpb24pCnA8LXArZ2VvbV9wb2ludCgpK2dlb21faml0dGVyKHdpZHRoPTAuMSkrZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsZm9ybXVsYSA9ICJ5IH4geCIpCnA8LXAreWxhYigiRnJpY3Rpb24gQ29lZmZpY2llbnQiKSt4bGFiKCJNb3ZlbWVudCBUeXBlIikKcApgYGAKCk5vdyB3ZSdyZSBnb2luZyB0byBnZXQgdGhlIGxpbmVhciBtb2RlbHMgdGhhdCBhcmUgc2xpZ2h0bHkgbW9yZSBpbnRlcmVzdGluZy4gU28gbm93IHdlIGFyZSBnb2luZyB0byBoYXZlIHR3byB2YXJpYWJsZXMgaW4gdGhlIGZvcm11bGE6CgpgYGB7cn0KWDwtbW9kZWwubWF0cml4KH50eXBlK2xlZyxkYXRhPXNwaWRlcikKY29sbmFtZXMoWCkgI2ludGVyY2VwdCwgdHlwZXB1c2gsIGFuZCB0aGUgdGhyZWUgbm9uLXJlZmVyZW5jZSBsZXZlbCBvZiBsZWcgKEwxKQpoZWFkKFgpCmltYWdlbWF0KFgsbWFpbj0iTW9kZWwgTWF0cml4IGZvciBMaW5lYXIgTW9kZWwgd2l0aCAyIEZhY3RvcnMiKQpmaXQyPC1sbShmcmljdGlvbn50eXBlK2xlZyxkYXRhPXNwaWRlcikKc3VtbWFyeShmaXQyKQooY29lZnM8LWNvZWYoZml0MikpCmBgYAoKYGBge3J9CnNwaWRlciRncm91cDwtZmFjdG9yKHBhc3RlMChzcGlkZXIkbGVnLHNwaWRlciR0eXBlKSkKcDwtZ2dwbG90KHNwaWRlcikKcDwtcCthZXMoeD1ncm91cCx5PWZyaWN0aW9uKQpwPC1wK2dlb21fcG9pbnQoKStnZW9tX2ppdHRlcih3aWR0aD0wLjEpCnAKYGBgCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygiUkNvbG9yQnJld2VyIikKI2xpYnJhcnkoUkNvbG9yQnJld2VyKQphPC0gLTAuMjUKbGd0aDwtIC4xCnN0cmlwY2hhcnQoc3BsaXQoc3BpZGVyJGZyaWN0aW9uLHNwaWRlciRncm91cCksCiAgICAgICAgICAgdmVydGljYWw9VCxwY2g9MSwgbWV0aG9kID0gImppdHRlciIsbGFzPTIseGxpbT1jKDAsMTEpLHlsaW09YygwLDIpKQpjb2xzPC1icmV3ZXIucGFsKDUsIkRhcmsyIikKYWJsaW5lKGg9MCkKYXJyb3dzKDErYSwwLDErYSxjb2Vmc1sxXSxsd2Q9Myxjb2w9Y29sc1sxXSxsZW5ndGggPSBsZ3RoKQphYmxpbmUoaD1jb2Vmc1sxXSxjb2w9Y29sc1sxXSkKYXJyb3dzKDMrYSxjb2Vmc1sxXSwzK2EsY29lZnNbMV0rY29lZnNbM10sbHdkPTMsY29sPWNvbHNbM10sbGVuZ3RoID0gbGd0aCkKYXJyb3dzKDUrYSxjb2Vmc1sxXSw1K2EsY29lZnNbMV0rY29lZnNbNF0sbHdkPTQsY29sPWNvbHNbNF0sbGVuZ3RoID0gbGd0aCkKYXJyb3dzKDcrYSxjb2Vmc1sxXSw3K2EsY29lZnNbMV0rY29lZnNbNV0sbHdkPTUsY29sPWNvbHNbNV0sbGVuZ3RoID0gbGd0aCkKYXJyb3dzKDIrYSxjb2Vmc1sxXSwyK2EsY29lZnNbMV0rY29lZnNbM10rY29lZnNbMl0sbHdkPTMsY29sPWNvbHNbMl0sbGVuZ3RoID0gbGd0aCkKc2VnbWVudHMoMythLGNvZWZzWzFdK2NvZWZzWzNdLDQrYSxjb2Vmc1sxXStjb2Vmc1szXSxsd2Q9Myxjb2w9Y29sc1szXSkKYXJyb3dzKDQrYSxjb2Vmc1sxXStjb2Vmc1szXSw0K2EsY29lZnNbMV0rY29lZnNbM10rY29lZnNbMl0sbHdkPTMsY29sPWNvbHNbMl0sbGVuZ3RoID0gbGd0aCkKc2VnbWVudHMoNSthLGNvZWZzWzFdK2NvZWZzWzRdLDYrYSxjb2Vmc1sxXStjb2Vmc1s0XSxsd2Q9Myxjb2w9Y29sc1s0XSkKYXJyb3dzKDYrYSxjb2Vmc1sxXStjb2Vmc1s0XSw2K2EsY29lZnNbMV0rY29lZnNbNF0rY29lZnNbMl0sbHdkPTMsY29sPWNvbHNbMl0sbGVuZ3RoID0gbGd0aCkKc2VnbWVudHMoNythLGNvZWZzWzFdK2NvZWZzWzVdLDgrYSxjb2Vmc1sxXStjb2Vmc1s1XSxsd2Q9Myxjb2w9Y29sc1s1XSkKYXJyb3dzKDgrYSxjb2Vmc1sxXStjb2Vmc1s1XSw4K2EsY29lZnNbMV0rY29lZnNbNV0rY29lZnNbMl0sbHdkPTMsY29sPWNvbHNbMl0sbGVuZ3RoID0gbGd0aCkKbGVnZW5kKCJyaWdodCIsbmFtZXMoY29lZnMpLGZpbGwgPSBjb2xzLGNleD0wLjcsYmc9ImJlaWdlIikKYGBgCgpOb3RlIHRoYXQKCiogSW50ZXJjZXB0PTEuMDUzOTIgaXMgbm8gbG9uZ2VyIHRoZSB2YWx1ZSBvZiBtZWFuIG9mICJMMXB1bGwiIGdyb3VwOgpgYGB7cn0KbWVhbihzcGlkZXJbc3BpZGVyJGdyb3VwPT0iTDFwdWxsIixdJGZyaWN0aW9uKQpgYGAKKiBhbmQgb3RoZXIgY29lZmZpY2llbnRzIGFyZSBub3QgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWVhbnMuCgpUaGlzIGlzIGJlY2F1c2Ugd2hlbiB3ZSBoYWQgMiBjb2VmZmljaWVudHMgYW5kIDIgZ3JvdXBzLCB0aGUgY29lZmZpY2llbnRzIGNvdWxkIGJlIHN1Y2ggdGhhdCB0aGV5IHByZWRpY3RlZCB0aGUgZXhhY3QgbWVhbiBvZiB0aGUgdHdvIGdyb3Vwcy4gQW5kIG5vdyB3ZSBoYXZlIDggZ3JvdXBzIGFuZCBvbmx5IDUgY29lZmZpY2llbnRzLiAKCgpGaW5hbGx5LCBsZXQncyB0byBzZWUgaG93IHRvIGNvbXBhcmUgdHdvIGdyb3VwcyB3aGVyZSBvbmUgb2YgdGhlbSBpcyBub3QgdGhlIHJlZmVyZW5jZSBsZXZlbC4KCkZvciBpbnN0YW5jZSwgd2Ugd2FudCB0byBjb21wYXJlIHRoZSBsZWcgTDIgd2l0aCBMMyBlZmZlY3QuIFRoZSB3YXkgdG8gZG8gdGhhdCBpcyB0byB1c2UgdGhlICJjb250cmFzdCIgCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygiY29udHJhc3QiKQojbGlicmFyeShjb250cmFzdCkKTDN2c0wyPC1jb250cmFzdChmaXQyLGxpc3QobGVnPSJMMyIsdHlwZT0icHVsbCIpLGxpc3QobGVnPSJMMiIsdHlwZT0icHVsbCIpKQpMM3ZzTDIKY29lZnNbNF0tY29lZnNbM10KKEM8LUwzdnNMMiRYKQpMM3ZzTDIuZXF1aXY8LWNvbnRyYXN0KGZpdDIsbGlzdChsZWc9IkwzIix0eXBlPSJwdXNoIiksbGlzdChsZWc9IkwyIix0eXBlPSJwdXNoIikpCkwzdnNMMi5lcXVpdgpMM3ZzTDIuZXF1aXYkWApgYGAKCkNvbnRyYXN0cyBFeGVyY2lzZXMKClN1cHBvc2Ugd2UgaGF2ZSBhbiBleHBlcmltZW50IHdpdGggdHdvIHNwZWNpZXMgQSBhbmQgQiwgYW5kIHR3byBjb25kaXRpb25zOiBjb250cm9sIGFuZCB0cmVhdGVkLgoKYGBge3J9CnNwZWNpZXMgPC0gZmFjdG9yKGMoIkEiLCJBIiwiQiIsIkIiKSkKY29uZGl0aW9uIDwtIGZhY3RvcihjKCJjb250cm9sIiwidHJlYXRlZCIsImNvbnRyb2wiLCJ0cmVhdGVkIikpCmBgYAokJFxiZWdpbnttYXRyaXh9XHRleHR7c3BlY2llc30oQSk9MCwgJiAgXHRleHR7c3BlY2llc30oQik9MVxcIFx0ZXh0e2NvbmRpdGlvbn0oY29udHJvbCk9MCwgJiBcdGV4dHtjb25kaXRpb259KHRyYXRlZCk9MSAgXGVuZHttYXRyaXh9JCQKCkFuZCB3ZSB3aWxsIHVzZSBhIGZvcm11bGEgb2YgfiBzcGVjaWVzICsgY29uZGl0aW9uLgoKYGBge3J9Clg8LW1vZGVsLm1hdHJpeCh+IHNwZWNpZXMgKyBjb25kaXRpb24pClgKYGBgCgpDb250cmFzdHMgRXhlcmNpc2VzICMxCgpTdXBwb3NlIHdlIHdhbnQgdG8gYnVpbGQgYSBjb250cmFzdCBvZiBjb2VmZmljaWVudHMgZm9yIHRoZSBhYm92ZSBleHBlcmltZW50YWwgZGVzaWduLgoKWW91IGNhbiBlaXRoZXIgZmlndXJlIHRoaXMgcXVlc3Rpb24gb3V0IHRocm91Z2ggbG9naWMsIGJ5IGxvb2tpbmcgYXQgdGhlIGRlc2lnbiBtYXRyaXgsIG9yIHVzaW5nIHRoZSBjb250cmFzdCgpIGZ1bmN0aW9uIGZyb20gdGhlIGNvbnRyYXN0IGxpYnJhcnkuIElmIHlvdSBoYXZlIG5vdCBkb25lIHNvIGFscmVhZHksIHlvdSBzaG91bGQgZG93bmxvYWQgdGhlIGNvbnRyYXN0IGxpYnJhcnkuIFRoZSBjb250cmFzdCB2ZWN0b3IgaXMgcmV0dXJuZWQgYXMgY29udHJhc3QoKSRYLgoKV2hhdCBzaG91bGQgdGhlIGNvbnRyYXN0IHZlY3RvciBiZSwgZm9yIHRoZSBjb250cmFzdCBvZiAoc3BlY2llcz1CIGFuZCBjb25kaXRpb249Y29udHJvbCkgdnMgKHNwZWNpZXM9QSBhbmQgY29uZGl0aW9uPXRyZWF0bWVudCk/IEFzc3VtZSB0aGF0IHRoZSBiZXRhIHZlY3RvciBmcm9tIHRoZSBtb2RlbCBmaXQgYnkgUiBpczogSW50ZXJjZXB0LCBzcGVjaWVzQiwgY29uZGl0aW9udHJlYXRlZC4KCmBgYHtyfQpZPC1tYXRyaXgocm5vcm0oNCksbmNvbD0xKQpmaXQ0PC1sbShZfnNwZWNpZXMrY29uZGl0aW9uKQpzdW1tYXJ5KGZpdDQpCmNvbnRyYXN0KGZpdDQsbGlzdChzcGVjaWVzPSJCIixjb25kaXRpb249ImNvbnRyb2wiKSxsaXN0KHNwZWNpZXM9IkEiLGNvbmRpdGlvbj0idHJlYXRlZCIpKSRYCmBgYAoKQ29udHJhc3RzIEV4ZXJjaXNlcyAjMgoKU3VwcG9zZSB3ZSBidWlsZCBhIG1vZGVsIHVzaW5nIHR3byB2YXJpYWJsZXM6IH4gdHlwZSArIGxlZy4KCldoYXQgaXMgdGhlIHQtdmFsdWUgZm9yIHRoZSBjb250cmFzdCBvZiBsZWcgcGFpciBMNCB2cyBsZWcgcGFpciBMMj8KCmBgYHtyfQpMNHZzTDI8LWNvbnRyYXN0KGZpdDIsbGlzdChsZWc9Ikw0Iix0eXBlPSJwdWxsIiksbGlzdChsZWc9IkwyIix0eXBlPSJwdWxsIikpCkw0dnNMMgpMNHZzTDIkWApgYGAKTm93IHdlIGNhbiB1c2UgYSBsaW5lYXIgbW9kZWwgd2l0aCBpbnRlcmFjdGlvbnMuCgpgYGB7ciBNYXRyaXggbW9kZWwgZm9yIGxpbmVhciBtb2RlbCB3aXRoIEludGVyYWN0aW9uc30KWDwtbW9kZWwubWF0cml4KH50eXBlK2xlZyt0eXBlOmxlZyxkYXRhPXNwaWRlcikKY29sbmFtZXMoWCkKaGVhZChYKQppbWFnZW1hdChYLCBtYWluPSJNYXRyaXggTW9kZWwgZm9yIExpbmVhciBNb2RlbCB3aXRoIEludGVyYXRpb25zIikKYGBgCmBgYHtyIExpbmVhciBtb2RlbCB3aXRoIEludGVyYWN0aW9uc30KZml0NTwtbG0oZnJpY3Rpb25+dHlwZStsZWcrdHlwZTpsZWcsZGF0YT1zcGlkZXIpCnN1bW1hcnkoZml0NSkKKGNvZWZzPC1jb2VmKGZpdDUpKQpgYGAKYGBge3J9CnN0cmlwY2hhcnQoc3BsaXQoc3BpZGVyJGZyaWN0aW9uLHNwaWRlciRncm91cCksCiAgICAgICAgICAgdmVydGljYWw9VCxwY2g9MSwgbWV0aG9kID0gImppdHRlciIsbGFzPTIseGxpbT1jKDAsMTEpLHlsaW09YygwLDIpKQpjb2xzPC1icmV3ZXIucGFsKDgsIkRhcmsyIikKYWJsaW5lKGg9MCkKYXJyb3dzKDErYSwwLDErYSxjb2Vmc1sxXSxsd2Q9Myxjb2w9Y29sc1sxXSxsZW5ndGggPSBsZ3RoKQphYmxpbmUoaD1jb2Vmc1sxXSxjb2w9Y29sc1sxXSkKCmFycm93cygyK2EsY29lZnNbMV0sMithLGNvZWZzWzFdK2NvZWZzWzJdLGx3ZD0zLGNvbD1jb2xzWzJdLGxlbmd0aCA9IGxndGgpCgphcnJvd3MoMythLGNvZWZzWzFdLDMrYSxjb2Vmc1sxXStjb2Vmc1szXSxsd2Q9Myxjb2w9Y29sc1szXSxsZW5ndGggPSBsZ3RoKQpzZWdtZW50cygzK2EsY29lZnNbMV0rY29lZnNbM10sNCthLGNvZWZzWzFdK2NvZWZzWzNdLGx3ZD0zLGNvbD1jb2xzWzNdKQphcnJvd3MoNCthLGNvZWZzWzFdK2NvZWZzWzNdLDQrYSxjb2Vmc1sxXStjb2Vmc1szXStjb2Vmc1syXSxsd2Q9Myxjb2w9Y29sc1syXSxsZW5ndGggPSBsZ3RoKQphcnJvd3MoNCthLGNvZWZzWzFdK2NvZWZzWzJdK2NvZWZzWzNdLDQrYSxjb2Vmc1sxXStjb2Vmc1szXStjb2Vmc1syXStjb2Vmc1s2XSxsd2Q9Myxjb2w9Y29sc1s2XSxsZW5ndGggPSBsZ3RoKQoKYXJyb3dzKDUrYSxjb2Vmc1sxXSw1K2EsY29lZnNbMV0rY29lZnNbNF0sbHdkPTQsY29sPWNvbHNbNF0sbGVuZ3RoID0gbGd0aCkKc2VnbWVudHMoNSthLGNvZWZzWzFdK2NvZWZzWzRdLDYrYSxjb2Vmc1sxXStjb2Vmc1s0XSxsd2Q9Myxjb2w9Y29sc1s0XSkKYXJyb3dzKDYrYSxjb2Vmc1sxXStjb2Vmc1s0XSw2K2EsY29lZnNbMV0rY29lZnNbNF0rY29lZnNbMl0sbHdkPTMsY29sPWNvbHNbMl0sbGVuZ3RoID0gbGd0aCkKYXJyb3dzKDYrYSxjb2Vmc1sxXStjb2Vmc1syXStjb2Vmc1s0XSw2K2EsY29lZnNbMV0rY29lZnNbNF0rY29lZnNbMl0rY29lZnNbN10sbHdkPTMsY29sPWNvbHNbN10sbGVuZ3RoID0gbGd0aCkKCgphcnJvd3MoNythLGNvZWZzWzFdLDcrYSxjb2Vmc1sxXStjb2Vmc1s1XSxsd2Q9NSxjb2w9Y29sc1s1XSxsZW5ndGggPSBsZ3RoKQpzZWdtZW50cyg3K2EsY29lZnNbMV0rY29lZnNbNV0sOCthLGNvZWZzWzFdK2NvZWZzWzVdLGx3ZD0zLGNvbD1jb2xzWzVdKQphcnJvd3MoOCthLGNvZWZzWzFdK2NvZWZzWzVdLDgrYSxjb2Vmc1sxXStjb2Vmc1s1XStjb2Vmc1syXSxsd2Q9Myxjb2w9Y29sc1syXSxsZW5ndGggPSBsZ3RoKQphcnJvd3MoOCthLGNvZWZzWzFdK2NvZWZzWzJdK2NvZWZzWzVdLDgrYSxjb2Vmc1sxXStjb2Vmc1s1XStjb2Vmc1syXStjb2Vmc1s4XSxsd2Q9Myxjb2w9Y29sc1s4XSxsZW5ndGggPSBsZ3RoKQoKbGVnZW5kKCJyaWdodCIsbmFtZXMoY29lZnMpLGZpbGwgPSBjb2xzLGNleD0wLjcsYmc9ImJlaWdlIikKYGBgCgoKCgoKCgoKCgoKCgo=