1 Interactions

  • Interactions have the same meaning they did in ANOVA
  • there is a synergistic effect between two variables
  • However now we can examine interactions between continuous variables
  • Additive Effects: \(Y = B_1X + B_2Z + B_0\)
  • Interactive Effects: \(Y = B_1X + B_2Z + B_3XZ + B_0\)

1.1 Example of Interaction

  • We need to make multivariate set of variables and then force an interaction
  • DV: Reading Speed, IVs: Age + IQ
library(car)
set.seed(42)
# 250 people
n <- 250
# Uniform distrobution of Ages (5 to 15 year olds) 
X <- runif(n, 5, 15)

# normal distrobution of IQ (100 +-15) 
Z <- rnorm(n, 100, 15)

# Our equation to  create Y
Y <- .3*X + .5*Z + .3*X*Z +300 + rnorm(n, sd=10)
#Built our data frame
Reading.Data<-data.frame(ReadingSpeed=Y,Age=X,IQ=Z)

# Plot: Notes: reg.line=FALSE means remove best fit line. 
# smoother=loessLine means add smoothed line called the loess
scatterplot(ReadingSpeed~Age, data= Reading.Data, reg.line=FALSE, smoother=loessLine)

scatterplot(ReadingSpeed~IQ, data= Reading.Data, reg.line=FALSE, smoother=loessLine)

scatterplot(Age~IQ, data= Reading.Data, reg.line=FALSE, smoother=loessLine)

1.2 Lets test an interaction

  1. Center your variables
  2. Model 1: Examine a main-effects model (X + Z) [not necessary but useful]
  3. Model 2: Examine a main-effects + Interaction model (X + Z + X:Z)
  • Note: You can simply code it as X*Z in r, as it will automatically do (X + Z + X:Z)
#Center
Reading.Data$Age.C<-scale(Reading.Data$Age, center = TRUE, scale = FALSE)[,]
Reading.Data$IQ.C<-scale(Reading.Data$IQ, center = TRUE, scale = FALSE)[,]

Centered.Read.1<-lm(ReadingSpeed~Age.C+IQ.C,Reading.Data)
Centered.Read.2<-lm(ReadingSpeed~Age.C*IQ.C,Reading.Data)


library(stargazer)
stargazer(Centered.Read.1,Centered.Read.2,type="html",
          column.labels = c("Main Effects", "Interaction"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE,
          header=FALSE)
Dependent variable:
ReadingSpeed
Main Effects Interaction
(1) (2)
Constant 653.207*** 653.632***
(0.980) (0.653)
Age.C 29.778*** 29.851***
(0.337) (0.224)
IQ.C 3.579*** 3.556***
(0.071) (0.047)
Age.C:IQ.C 0.305***
(0.017)
Observations 250 250
R2 0.976 0.989
Adjusted R2 0.976 0.989
Residual Std. Error 15.502 (df = 247) 10.314 (df = 246)
F Statistic 5,028.365*** (df = 2; 247) 7,676.634*** (df = 3; 246)
Note: p<0.1; p<0.05; p<0.01
  • Also change in \(R^2\) is significant, as we might expect
ChangeInR<-anova(Centered.Read.1,Centered.Read.2)
knitr::kable(ChangeInR, digits=4)
Res.Df RSS Df Sum of Sq F Pr(>F)
247 59357.43 NA NA NA NA
246 26169.85 1 33187.58 311.9676 0

1.3 How visualize this?

  • Well, there are some options
  • We can do a fancy-pants surface plot, but that is hard to put into a paper
  • More common this is to examine slope of one factor at different levels of the other (Simple Slopes)
  • What we need to decide is at which levels

1.3.1 Hand picking

  • Manually select the what levels of each you are going to examine
  • For for tracking values of interest
  • here I picked -3 to 3 for age (mix and max) and -15, 0 15 for IQ (theoretical 1 SD and mean)
library(effects)
Inter.1a<-effect(c("Age.C*IQ.C"), Centered.Read.2,
                           xlevels=list(Age.C=seq(-3,3, 1), 
                                        IQ.C=c(-15,0,15)))
knitr::kable(summary(Inter.1a)$effect, digits=4)
-15 0 15
-3 524.445 564.0773 603.7095
-2 549.727 593.9288 638.1305
-1 575.009 623.7803 672.5515
0 600.291 653.6317 706.9725
1 625.573 683.4832 741.3935
2 650.855 713.3347 775.8144
3 676.137 743.1862 810.2354
plot(Inter.1a, multiline = TRUE)

1.3.2 Quantile

  • Examine the levels based on quantiles (bins based on probabiltiy)
  • We will do this into 5 equal bins based on probability cut-offs
  • Does not assume normality for IV
IQ.Quantile<-quantile(Reading.Data$IQ.C,probs=c(0,.25,.50,.75,1))
IQ.Quantile<-round(IQ.Quantile,2)
IQ.Quantile
##     0%    25%    50%    75%   100% 
## -39.89  -9.91  -0.19   9.45  37.50
Inter.1b<-effect(c("Age.C*IQ.C"), Centered.Read.2,
                 xlevels=list(Age.C=seq(-3,3, 1), 
                              IQ.C=IQ.Quantile))
plot(Inter.1b, multiline = TRUE)

1.3.3 Based on the SD

  • We select 3 values: \(M-1SD\), \(M\), \(M+1SD\)
  • Not it does not have to be 1 SD, it can be 1.5,2 or 3
  • Assumes normality for IV
IQ.SD<-c(mean(Reading.Data$IQ.C)-sd(Reading.Data$IQ.C),
         mean(Reading.Data$IQ.C),
         mean(Reading.Data$IQ.C)+sd(Reading.Data$IQ.C))
IQ.SD<-round(IQ.SD,2)
IQ.SD
## [1] -13.89   0.00  13.89
Inter.1c<-effect(c("Age.C*IQ.C"), Centered.Read.2,
                 xlevels=list(Age.C=seq(-3,3, 1), 
                              IQ.C=IQ.SD))
plot(Inter.1c, multiline = TRUE)

1.4 Why center your IVs?

  • The center of each IV represents the mean of that IV
  • Thus when you interact them \(X*Z\), that means \(0*0 = 0\)
  • Also this can reduce multicollineatity issues
  • This is because if \(X*Z\) creates a line, it means you have added a new predictor (XZ) that strongly correlates with X and Z
X<-c(0,2,4,6,8,10)
Z<-c(0,2,4,6,8,10)
XZ<-X*Z
plot(XZ)

c(cor(X,Z), cor(X,XZ), cor(Z,XZ))
## [1] 1.0000000 0.9598833 0.9598833

-But if you center them, now you will make a U-shaped interaction which will be orthogonal to X and Z!

X.C<-scale(X, center = TRUE, scale = FALSE)[,]
Z.C<-scale(Z, center = TRUE, scale = FALSE)[,]
XZ.C<-X.C*Z.C
plot(XZ.C)

c(cor(X.C,Z.C), cor(X.C,XZ.C), cor(Z.C,XZ.C))
## [1] 1 0 0
  • Lets apply this to our class example
  • For example, see below where I manually multiply the values and you can see a very strong positive slope
Reading.Data$Age.X.IQ<-Reading.Data$Age*Reading.Data$IQ
scatterplot(ReadingSpeed~Age.X.IQ, data= Reading.Data, reg.line=FALSE, smoother=loessLine)

  • But when you center them, things change
Reading.Data$Age.X.IQ.C<-Reading.Data$Age.C*Reading.Data$IQ.C
scatterplot(ReadingSpeed~Age.X.IQ.C, data= Reading.Data, reg.line=FALSE, smoother=loessLine)

1.4.1 Lets run an uncentered regression

  • as you can see the the terms have changed from centered models
Uncentered.Read.1<-lm(ReadingSpeed~IQ+Age,Reading.Data)
Uncentered.Read.2<-lm(ReadingSpeed~IQ*Age,Reading.Data)

stargazer(Uncentered.Read.1,Uncentered.Read.2,type="html",
          column.labels = c("Main Effects", "Interaction"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE,
          header=FALSE)
Dependent variable:
ReadingSpeed
Main Effects Interaction
(1) (2)
Constant -3.036 304.494***
(7.979) (18.203)
IQ 3.579*** 0.482***
(0.071) (0.182)
Age 29.778*** -0.427
(0.337) (1.725)
IQ:Age 0.305***
(0.017)
Observations 250 250
R2 0.976 0.989
Adjusted R2 0.976 0.989
Residual Std. Error 15.502 (df = 247) 10.314 (df = 246)
F Statistic 5,028.365*** (df = 2; 247) 7,676.634*** (df = 3; 246)
Note: p<0.1; p<0.05; p<0.01
  • lets check the multicollinearity of the uncentered model
vif(Uncentered.Read.2)
##       IQ      Age   IQ:Age 
## 14.89057 59.14549 71.06919
  • We lost our main effect of Age as the variances got all inflated
  • We did not have this problem in the centered model
vif(Centered.Read.2)
##      Age.C       IQ.C Age.C:IQ.C 
##   1.001540   1.001961   1.001144
  • Note: Its more standard to plot the uncentered version, but run the analysis on the centered data

2 Testing Simple Slopes

  • The goal here it figure out when the slope at a given level of another variable is different from zero
  • We chop up the interaction at specific places as we did with the interactions plots (-1 SD, M, +1 SD) on the moderating variable (a third variable that affects the strength of the relationship between a dependent and independent variable)
  • Next we test the slopes on the predictor variable (IV of main interest)
  • Example: Performance on a task, predicted by Level of training, moderated by how hard the challange is
set.seed(42)
# 250 people
n <- 250
#Training (low to high)
X <- runif(n, -10, 10)
# normal distrobution of Challenge Difficulty
Z <- rnorm(n, 0, 15)
# Our equation to  create Y
Y <- 2.2*X + 2.46*Z + .75*X*Z + 16 + rnorm(n, sd=50)
#Built our data frame
Skill.Data<-data.frame(Performance=Y,Training=X,Challenge=Z)
#run our regression
Skill.Model.1<-lm(Performance~Training+Challenge,Skill.Data)
Skill.Model.2<-lm(Performance~Training*Challenge,Skill.Data)
stargazer(Skill.Model.1,Skill.Model.2,type="html",
          column.labels = c("Main Effects", "Interaction"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE,
          header=FALSE)
Dependent variable:
Performance
Main Effects Interaction
(1) (2)
Constant 14.238*** 16.173***
(4.909) (3.268)
Training 0.895 1.542***
(0.843) (0.562)
Challenge 2.855*** 2.600***
(0.354) (0.236)
Training:Challenge 0.762***
(0.043)
Observations 250 250
R2 0.210 0.652
Adjusted R2 0.204 0.648
Residual Std. Error 77.510 (df = 247) 51.571 (df = 246)
F Statistic 32.867*** (df = 2; 247) 153.486*** (df = 3; 246)
Note: p<0.1; p<0.05; p<0.01

2.1 Ploting

  • We will use the rockchalk library to visualize our interaction and test our simple slopes
library(rockchalk)
## 
## Attaching package: 'rockchalk'
## The following object is masked from 'package:plyr':
## 
##     summarize
## The following object is masked from 'package:MASS':
## 
##     mvrnorm
m1ps <- plotSlopes(Skill.Model.2, modx = "Challenge", plotx = "Training", n=3, modxVals="std.dev")

m1psts <- testSlopes(m1ps)
## Values of Challenge OUTSIDE this interval:
##         lo         hi 
## -3.5050807 -0.5730182 
## cause the slope of (b1 + b2*Challenge)Training to be statistically significant
round(m1psts$hypotests,4)
##        "Challenge"   slope Std. Error  t value Pr(>|t|)
## (m-sd)      -14.50 -9.5013     0.8131 -11.6848   0.0000
## (m)          -0.61  1.0771     0.5611   1.9196   0.0561
## (m+sd)       13.28 11.6554     0.8282  14.0737   0.0000
  • We see the slope at the mean is not significant,
  • The +1SD and -1SD are significant, this is what is driving the interaction
  • Well trained people at low levels of challenge get worse with more training, while high levels of challenge with more training get better
  • The bonus using rockchalk is that you can change the number of lines (change n=3) you want to see or you can test quantiles as well (change modxVals=“quantile”)
m1pQ <- plotSlopes(Skill.Model.2, modx = "Challenge", plotx = "Training", n=5, modxVals="quantile")

m1pQts <- testSlopes(m1pQ)
## Values of Challenge OUTSIDE this interval:
##         lo         hi 
## -3.5050807 -0.5730182 
## cause the slope of (b1 + b2*Challenge)Training to be statistically significant
round(m1pQts$hypotests,4)
##      "Challenge"    slope Std. Error  t value Pr(>|t|)
## 0%      -40.4989 -29.3016     1.7993 -16.2847   0.0000
## 25%     -10.5190  -6.4694     0.6990  -9.2555   0.0000
## 50%      -0.7985   0.9335     0.5610   1.6640   0.0974
## 75%       8.8382   8.2726     0.6994  11.8278   0.0000
## 100%     36.8939  29.6394     1.7214  17.2183   0.0000

2.1.1 Notes

  • You often don’t need to run the t-tests on the simple slopes, but they can useful to test very specific hypotheses
  • You can run simple slopes analysis on three-way interactions, but lets leave that aside for now as you would have to use a different R-package, again often the plot tells the story

3 Non-Linear interactions

  • You can have non-linear terms interacting with other linear and non-linear terms
  • Example: Quit smoking, X = fear of you health, Z = moderated by Self-Efficacy for quiting
set.seed(42)
# 250 people
n <- 250
#Fear of Health
X <- runif(n, 1, 9)
#Centered Self-Efficacy
Z <- runif(n, 0, 15)
# Our equation to  create Y
Y <- .178*X - .052*X^2 + .164*X*Z + .164*(X^2)*Z+ 3.651 + rnorm(n, sd=50)
#Built our data frame
Smoke.Data<-data.frame(Smoking=Y,Health=X,SelfEfficacy=Z)
#run our regression
Smoke.Model.1<-lm(Smoking~poly(Health,2)+SelfEfficacy,Smoke.Data)
Smoke.Model.2<-lm(Smoking~poly(Health,2)*SelfEfficacy,Smoke.Data)
stargazer(Smoke.Model.1,Smoke.Model.2,type="html",
          column.labels = c("Main Effects", "Interaction"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE,
          header=FALSE)
Dependent variable:
Smoking
Main Effects Interaction
(1) (2)
Constant 1.121 0.888
(6.187) (5.857)
poly(Health, 2)1 464.121*** 49.056
(51.816) (90.966)
poly(Health, 2)2 118.649** 50.228
(51.721) (93.073)
SelfEfficacy 5.713*** 5.931***
(0.745) (0.706)
poly(Health, 2)1:SelfEfficacy 60.253***
(11.088)
poly(Health, 2)2:SelfEfficacy 10.879
(11.441)
Observations 250 250
R2 0.353 0.425
Adjusted R2 0.345 0.413
Residual Std. Error 51.685 (df = 246) 48.903 (df = 244)
F Statistic 44.648*** (df = 3; 246) 36.081*** (df = 5; 244)
Note: p<0.1; p<0.05; p<0.01

3.1 Plotting the Simple Slopes when there are Curves

  • These can be done with the ‘effects’ package as I showed you above and last week
  • or we can simply use the plotCurves function from the ‘rockchalk’ package
SmokeInterPlot <- plotCurves(Smoke.Model.2, modx = "SelfEfficacy", plotx = "Health",n=3,modxVals="std.dev")

  • Notice that the second order term was not significant in the main effect model
  • This means you could have missed it. As the model 1 plot would have looked like this:
plotCurves(Smoke.Model.1, plotx = "Health")

  • In other words, you cannot see the higher order effects as they are hidden by the moderating variable
  • How to find these hidden things?
  • You have to test for interactions and changes in \(R^2\)
  • You have try to add higher order terms, but they should be motivated by some theory
  • What if you did not know the second order term was in the model above.
Smoke.Model.1.L<-lm(Smoking~Health+SelfEfficacy,Smoke.Data)
Smoke.Model.2.L<-lm(Smoking~Health*SelfEfficacy,Smoke.Data)
stargazer(Smoke.Model.1.L,Smoke.Model.2.L,type="html",
          column.labels = c("Main Effects", "Interaction"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE,
          header=FALSE)
Dependent variable:
Smoking
Main Effects Interaction
(1) (2)
Constant -62.385*** -5.746
(9.811) (14.105)
Health 12.606*** 1.370
(1.420) (2.497)
SelfEfficacy 5.649*** -2.384
(0.751) (1.664)
Health:SelfEfficacy 1.627***
(0.304)
Observations 250 250
R2 0.339 0.407
Adjusted R2 0.333 0.400
Residual Std. Error 52.129 (df = 247) 49.444 (df = 246)
F Statistic 63.249*** (df = 2; 247) 56.387*** (df = 3; 246)
Note: p<0.1; p<0.05; p<0.01
ChangeInR.Smoke<-anova(Smoke.Model.1.L,Smoke.Model.2.L)
knitr::kable(ChangeInR.Smoke, digits=4)
Res.Df RSS Df Sum of Sq F Pr(>F)
247 671215.5 NA NA NA NA
246 601410.1 1 69805.32 28.5531 0
  • Reploting as linear by linear interaction
library(rockchalk)
SmokeInterPlot.Linear <- plotSlopes(Smoke.Model.2.L, modx = "SelfEfficacy", plotx = "Health", n=3, modxVals="std.dev")

  • lets check the change in \(R^2\) from Linear interaction model to poly interaction model now
stargazer(Smoke.Model.2.L,Smoke.Model.2,type="html",
          column.labels = c("Linear", "Poly"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE,
          header=FALSE)
Dependent variable:
Smoking
Linear Poly
(1) (2)
Constant -5.746 0.888
(14.105) (5.857)
Health 1.370
(2.497)
poly(Health, 2)1 49.056
(90.966)
poly(Health, 2)2 50.228
(93.073)
SelfEfficacy -2.384 5.931***
(1.664) (0.706)
Health:SelfEfficacy 1.627***
(0.304)
poly(Health, 2)1:SelfEfficacy 60.253***
(11.088)
poly(Health, 2)2:SelfEfficacy 10.879
(11.441)
Observations 250 250
R2 0.407 0.425
Adjusted R2 0.400 0.413
Residual Std. Error 49.444 (df = 246) 48.903 (df = 244)
F Statistic 56.387*** (df = 3; 246) 36.081*** (df = 5; 244)
Note: p<0.1; p<0.05; p<0.01
ChangeInR.Smoke.2<-anova(Smoke.Model.2.L,Smoke.Model.2)
knitr::kable(ChangeInR.Smoke.2, digits=4)
Res.Df RSS Df Sum of Sq F Pr(>F)
246 601410.1 NA NA NA NA
244 583529.7 2 17880.49 3.7383 0.0252

3.2 Notes

  • So model with poly is a significantly better fit, but really its not a huge change
  • Would it change the story by much?
  • Most people would ignore the higher order term, but you the choice depends on your theory!
  • Ignoring the higher order term can make it easier to test simple slopes
  • But ignoring the higher order terms can voilate your assumptions, lets compare our linear vs poly models residuals
plot(Smoke.Model.2.L, which=1)

plot(Smoke.Model.2, which=1)

LS0tDQp0aXRsZTogJ0ludGVyYWN0aW9ucyBhbmQgU2ltcGxlIFNsb3BlcycNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBmb250c2l6ZTogOHB0DQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6IGZsYXRseQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBubw0KLS0tDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoPTUpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLmhlaWdodD0zLjc1KQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5hbGlnbj0nY2VudGVyJykgDQpgYGANCg0KIyBJbnRlcmFjdGlvbnMNCg0KKiBJbnRlcmFjdGlvbnMgaGF2ZSB0aGUgc2FtZSBtZWFuaW5nIHRoZXkgZGlkIGluIEFOT1ZBDQogICsgdGhlcmUgaXMgYSBzeW5lcmdpc3RpYyBlZmZlY3QgYmV0d2VlbiB0d28gdmFyaWFibGVzDQogICsgSG93ZXZlciBub3cgd2UgY2FuIGV4YW1pbmUgaW50ZXJhY3Rpb25zIGJldHdlZW4gY29udGludW91cyB2YXJpYWJsZXMNCiogQWRkaXRpdmUgRWZmZWN0czogJFkgPSBCXzFYICsgQl8yWiArIEJfMCQNCiogSW50ZXJhY3RpdmUgRWZmZWN0czogJFkgPSBCXzFYICsgQl8yWiArIEJfM1haICsgQl8wJA0KDQojIyBFeGFtcGxlIG9mIEludGVyYWN0aW9uDQotIFdlIG5lZWQgdG8gbWFrZSBtdWx0aXZhcmlhdGUgc2V0IG9mIHZhcmlhYmxlcyBhbmQgdGhlbiBmb3JjZSBhbiBpbnRlcmFjdGlvbg0KLSBEVjogUmVhZGluZyBTcGVlZCwgSVZzOiBBZ2UgKyBJUSANCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoY2FyKQ0Kc2V0LnNlZWQoNDIpDQojIDI1MCBwZW9wbGUNCm4gPC0gMjUwDQojIFVuaWZvcm0gZGlzdHJvYnV0aW9uIG9mIEFnZXMgKDUgdG8gMTUgeWVhciBvbGRzKSANClggPC0gcnVuaWYobiwgNSwgMTUpDQoNCiMgbm9ybWFsIGRpc3Ryb2J1dGlvbiBvZiBJUSAoMTAwICstMTUpIA0KWiA8LSBybm9ybShuLCAxMDAsIDE1KQ0KDQojIE91ciBlcXVhdGlvbiB0byAgY3JlYXRlIFkNClkgPC0gLjMqWCArIC41KlogKyAuMypYKlogKzMwMCArIHJub3JtKG4sIHNkPTEwKQ0KI0J1aWx0IG91ciBkYXRhIGZyYW1lDQpSZWFkaW5nLkRhdGE8LWRhdGEuZnJhbWUoUmVhZGluZ1NwZWVkPVksQWdlPVgsSVE9WikNCg0KIyBQbG90OiBOb3RlczogcmVnLmxpbmU9RkFMU0UgbWVhbnMgcmVtb3ZlIGJlc3QgZml0IGxpbmUuIA0KIyBzbW9vdGhlcj1sb2Vzc0xpbmUgbWVhbnMgYWRkIHNtb290aGVkIGxpbmUgY2FsbGVkIHRoZSBsb2Vzcw0Kc2NhdHRlcnBsb3QoUmVhZGluZ1NwZWVkfkFnZSwgZGF0YT0gUmVhZGluZy5EYXRhLCByZWcubGluZT1GQUxTRSwgc21vb3RoZXI9bG9lc3NMaW5lKQ0Kc2NhdHRlcnBsb3QoUmVhZGluZ1NwZWVkfklRLCBkYXRhPSBSZWFkaW5nLkRhdGEsIHJlZy5saW5lPUZBTFNFLCBzbW9vdGhlcj1sb2Vzc0xpbmUpDQpzY2F0dGVycGxvdChBZ2V+SVEsIGRhdGE9IFJlYWRpbmcuRGF0YSwgcmVnLmxpbmU9RkFMU0UsIHNtb290aGVyPWxvZXNzTGluZSkNCg0KDQpgYGANCg0KIyMgTGV0cyB0ZXN0IGFuIGludGVyYWN0aW9uDQoxLiBDZW50ZXIgeW91ciB2YXJpYWJsZXMNCjIuIE1vZGVsIDE6IEV4YW1pbmUgYSBtYWluLWVmZmVjdHMgbW9kZWwgKFggKyBaKSBbbm90IG5lY2Vzc2FyeSBidXQgdXNlZnVsXQ0KMy4gTW9kZWwgMjogRXhhbWluZSBhIG1haW4tZWZmZWN0cyArIEludGVyYWN0aW9uIG1vZGVsIChYICsgWiArIFg6WikNCg0KLSBOb3RlOiBZb3UgY2FuIHNpbXBseSBjb2RlIGl0IGFzIFgqWiBpbiByLCBhcyBpdCB3aWxsIGF1dG9tYXRpY2FsbHkgZG8gKFggKyBaICsgWDpaKQ0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UscmVzdWx0cz0nYXNpcyd9DQoNCiNDZW50ZXINClJlYWRpbmcuRGF0YSRBZ2UuQzwtc2NhbGUoUmVhZGluZy5EYXRhJEFnZSwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBGQUxTRSlbLF0NClJlYWRpbmcuRGF0YSRJUS5DPC1zY2FsZShSZWFkaW5nLkRhdGEkSVEsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gRkFMU0UpWyxdDQoNCkNlbnRlcmVkLlJlYWQuMTwtbG0oUmVhZGluZ1NwZWVkfkFnZS5DK0lRLkMsUmVhZGluZy5EYXRhKQ0KQ2VudGVyZWQuUmVhZC4yPC1sbShSZWFkaW5nU3BlZWR+QWdlLkMqSVEuQyxSZWFkaW5nLkRhdGEpDQoNCg0KbGlicmFyeShzdGFyZ2F6ZXIpDQpzdGFyZ2F6ZXIoQ2VudGVyZWQuUmVhZC4xLENlbnRlcmVkLlJlYWQuMix0eXBlPSJodG1sIiwNCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gYygiTWFpbiBFZmZlY3RzIiwgIkludGVyYWN0aW9uIiksDQogICAgICAgICAgaW50ZXJjZXB0LmJvdHRvbSA9IEZBTFNFLA0KICAgICAgICAgIHNpbmdsZS5yb3c9RkFMU0UsIA0KICAgICAgICAgIG5vdGVzLmFwcGVuZCA9IEZBTFNFLA0KICAgICAgICAgIGhlYWRlcj1GQUxTRSkNCg0KYGBgDQoNCg0KDQotIEFsc28gY2hhbmdlIGluICRSXjIkIGlzIHNpZ25pZmljYW50LCBhcyB3ZSBtaWdodCBleHBlY3QNCg0KYGBge3J9DQpDaGFuZ2VJblI8LWFub3ZhKENlbnRlcmVkLlJlYWQuMSxDZW50ZXJlZC5SZWFkLjIpDQprbml0cjo6a2FibGUoQ2hhbmdlSW5SLCBkaWdpdHM9NCkNCmBgYA0KDQoNCg0KIyMgSG93IHZpc3VhbGl6ZSB0aGlzPw0KLSBXZWxsLCB0aGVyZSBhcmUgc29tZSBvcHRpb25zDQotIFdlIGNhbiBkbyBhIGZhbmN5LXBhbnRzIHN1cmZhY2UgcGxvdCwgYnV0IHRoYXQgaXMgaGFyZCB0byBwdXQgaW50byBhIHBhcGVyDQotIE1vcmUgY29tbW9uIHRoaXMgaXMgdG8gZXhhbWluZSBzbG9wZSBvZiBvbmUgZmFjdG9yIGF0IGRpZmZlcmVudCBsZXZlbHMgb2YgdGhlIG90aGVyIChTaW1wbGUgU2xvcGVzKSANCi0gV2hhdCB3ZSBuZWVkIHRvIGRlY2lkZSBpcyBhdCB3aGljaCBsZXZlbHMNCg0KDQoNCiMjIyBIYW5kIHBpY2tpbmcNCi0gTWFudWFsbHkgc2VsZWN0IHRoZSB3aGF0IGxldmVscyBvZiBlYWNoIHlvdSBhcmUgZ29pbmcgdG8gZXhhbWluZQ0KLSBGb3IgZm9yIHRyYWNraW5nIHZhbHVlcyBvZiBpbnRlcmVzdA0KLSBoZXJlIEkgcGlja2VkIC0zIHRvIDMgZm9yIGFnZSAobWl4IGFuZCBtYXgpIGFuZCAtMTUsIDAgMTUgZm9yIElRICh0aGVvcmV0aWNhbCAxIFNEIGFuZCBtZWFuKQ0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShlZmZlY3RzKQ0KSW50ZXIuMWE8LWVmZmVjdChjKCJBZ2UuQypJUS5DIiksIENlbnRlcmVkLlJlYWQuMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsZXZlbHM9bGlzdChBZ2UuQz1zZXEoLTMsMywgMSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElRLkM9YygtMTUsMCwxNSkpKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmtuaXRyOjprYWJsZShzdW1tYXJ5KEludGVyLjFhKSRlZmZlY3QsIGRpZ2l0cz00KQ0KcGxvdChJbnRlci4xYSwgbXVsdGlsaW5lID0gVFJVRSkNCmBgYA0KDQoNCg0KIyMjIFF1YW50aWxlDQotIEV4YW1pbmUgdGhlIGxldmVscyBiYXNlZCBvbiBxdWFudGlsZXMgKGJpbnMgYmFzZWQgb24gcHJvYmFiaWx0aXkpDQotIFdlIHdpbGwgZG8gdGhpcyBpbnRvIDUgZXF1YWwgYmlucyBiYXNlZCBvbiBwcm9iYWJpbGl0eSBjdXQtb2ZmcyANCi0gRG9lcyBub3QgYXNzdW1lIG5vcm1hbGl0eSBmb3IgSVYNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCklRLlF1YW50aWxlPC1xdWFudGlsZShSZWFkaW5nLkRhdGEkSVEuQyxwcm9icz1jKDAsLjI1LC41MCwuNzUsMSkpDQpJUS5RdWFudGlsZTwtcm91bmQoSVEuUXVhbnRpbGUsMikNCklRLlF1YW50aWxlDQoNCkludGVyLjFiPC1lZmZlY3QoYygiQWdlLkMqSVEuQyIpLCBDZW50ZXJlZC5SZWFkLjIsDQogICAgICAgICAgICAgICAgIHhsZXZlbHM9bGlzdChBZ2UuQz1zZXEoLTMsMywgMSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSVEuQz1JUS5RdWFudGlsZSkpDQpwbG90KEludGVyLjFiLCBtdWx0aWxpbmUgPSBUUlVFKQ0KYGBgDQoNCiMjIyBCYXNlZCBvbiB0aGUgU0QgDQotIFdlIHNlbGVjdCAzIHZhbHVlczogJE0tMVNEJCwgJE0kLCAkTSsxU0QkDQotIE5vdCBpdCBkb2VzIG5vdCBoYXZlIHRvIGJlIDEgU0QsIGl0IGNhbiBiZSAxLjUsMiBvciAzDQotIEFzc3VtZXMgbm9ybWFsaXR5IGZvciBJVg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KDQpJUS5TRDwtYyhtZWFuKFJlYWRpbmcuRGF0YSRJUS5DKS1zZChSZWFkaW5nLkRhdGEkSVEuQyksDQogICAgICAgICBtZWFuKFJlYWRpbmcuRGF0YSRJUS5DKSwNCiAgICAgICAgIG1lYW4oUmVhZGluZy5EYXRhJElRLkMpK3NkKFJlYWRpbmcuRGF0YSRJUS5DKSkNCklRLlNEPC1yb3VuZChJUS5TRCwyKQ0KSVEuU0QNCg0KSW50ZXIuMWM8LWVmZmVjdChjKCJBZ2UuQypJUS5DIiksIENlbnRlcmVkLlJlYWQuMiwNCiAgICAgICAgICAgICAgICAgeGxldmVscz1saXN0KEFnZS5DPXNlcSgtMywzLCAxKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJUS5DPUlRLlNEKSkNCnBsb3QoSW50ZXIuMWMsIG11bHRpbGluZSA9IFRSVUUpDQoNCg0KYGBgDQoNCg0KIyMgV2h5IGNlbnRlciB5b3VyIElWcz8NCi0gVGhlIGNlbnRlciBvZiBlYWNoIElWIHJlcHJlc2VudHMgdGhlIG1lYW4gb2YgdGhhdCBJVg0KLSBUaHVzIHdoZW4geW91IGludGVyYWN0IHRoZW0gJFgqWiQsIHRoYXQgbWVhbnMgJDAqMCA9IDAkDQotIEFsc28gdGhpcyBjYW4gcmVkdWNlIG11bHRpY29sbGluZWF0aXR5IGlzc3Vlcw0KLSBUaGlzIGlzIGJlY2F1c2UgaWYgJFgqWiQgY3JlYXRlcyBhIGxpbmUsIGl0IG1lYW5zIHlvdSBoYXZlIGFkZGVkIGEgbmV3IHByZWRpY3RvciAoWFopIHRoYXQgc3Ryb25nbHkgY29ycmVsYXRlcyB3aXRoIFggYW5kIFoNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NClg8LWMoMCwyLDQsNiw4LDEwKQ0KWjwtYygwLDIsNCw2LDgsMTApDQpYWjwtWCpaDQpwbG90KFhaKQ0KYyhjb3IoWCxaKSwgY29yKFgsWFopLCBjb3IoWixYWikpDQpgYGANCg0KLUJ1dCBpZiB5b3UgY2VudGVyIHRoZW0sIG5vdyB5b3Ugd2lsbCBtYWtlIGEgVS1zaGFwZWQgaW50ZXJhY3Rpb24gd2hpY2ggd2lsbCBiZSBvcnRob2dvbmFsIHRvIFggYW5kIFohDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpYLkM8LXNjYWxlKFgsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gRkFMU0UpWyxdDQpaLkM8LXNjYWxlKFosIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gRkFMU0UpWyxdDQpYWi5DPC1YLkMqWi5DDQpwbG90KFhaLkMpDQpjKGNvcihYLkMsWi5DKSwgY29yKFguQyxYWi5DKSwgY29yKFouQyxYWi5DKSkNCmBgYA0KDQotIExldHMgYXBwbHkgdGhpcyB0byBvdXIgY2xhc3MgZXhhbXBsZQ0KLSBGb3IgZXhhbXBsZSwgc2VlIGJlbG93IHdoZXJlIEkgbWFudWFsbHkgbXVsdGlwbHkgdGhlIHZhbHVlcyBhbmQgeW91IGNhbiBzZWUgYSB2ZXJ5IHN0cm9uZyBwb3NpdGl2ZSBzbG9wZQ0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NClJlYWRpbmcuRGF0YSRBZ2UuWC5JUTwtUmVhZGluZy5EYXRhJEFnZSpSZWFkaW5nLkRhdGEkSVENCnNjYXR0ZXJwbG90KFJlYWRpbmdTcGVlZH5BZ2UuWC5JUSwgZGF0YT0gUmVhZGluZy5EYXRhLCByZWcubGluZT1GQUxTRSwgc21vb3RoZXI9bG9lc3NMaW5lKQ0KYGBgDQoNCi0gQnV0IHdoZW4geW91IGNlbnRlciB0aGVtLCB0aGluZ3MgY2hhbmdlDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KUmVhZGluZy5EYXRhJEFnZS5YLklRLkM8LVJlYWRpbmcuRGF0YSRBZ2UuQypSZWFkaW5nLkRhdGEkSVEuQw0Kc2NhdHRlcnBsb3QoUmVhZGluZ1NwZWVkfkFnZS5YLklRLkMsIGRhdGE9IFJlYWRpbmcuRGF0YSwgcmVnLmxpbmU9RkFMU0UsIHNtb290aGVyPWxvZXNzTGluZSkNCg0KYGBgDQoNCg0KDQojIyMgTGV0cyBydW4gYW4gdW5jZW50ZXJlZCByZWdyZXNzaW9uDQotIGFzIHlvdSBjYW4gc2VlIHRoZSB0aGUgdGVybXMgaGF2ZSBjaGFuZ2VkIGZyb20gY2VudGVyZWQgbW9kZWxzDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxyZXN1bHRzPSdhc2lzJ30NClVuY2VudGVyZWQuUmVhZC4xPC1sbShSZWFkaW5nU3BlZWR+SVErQWdlLFJlYWRpbmcuRGF0YSkNClVuY2VudGVyZWQuUmVhZC4yPC1sbShSZWFkaW5nU3BlZWR+SVEqQWdlLFJlYWRpbmcuRGF0YSkNCg0Kc3RhcmdhemVyKFVuY2VudGVyZWQuUmVhZC4xLFVuY2VudGVyZWQuUmVhZC4yLHR5cGU9Imh0bWwiLA0KICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKCJNYWluIEVmZmVjdHMiLCAiSW50ZXJhY3Rpb24iKSwNCiAgICAgICAgICBpbnRlcmNlcHQuYm90dG9tID0gRkFMU0UsDQogICAgICAgICAgc2luZ2xlLnJvdz1GQUxTRSwgDQogICAgICAgICAgbm90ZXMuYXBwZW5kID0gRkFMU0UsDQogICAgICAgICAgaGVhZGVyPUZBTFNFKQ0KDQpgYGANCg0KDQoNCi0gbGV0cyBjaGVjayB0aGUgbXVsdGljb2xsaW5lYXJpdHkgb2YgdGhlIHVuY2VudGVyZWQgbW9kZWwNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnZpZihVbmNlbnRlcmVkLlJlYWQuMikNCg0KYGBgDQoNCi0gV2UgbG9zdCBvdXIgbWFpbiBlZmZlY3Qgb2YgKipBZ2UqKiBhcyB0aGUgdmFyaWFuY2VzIGdvdCBhbGwgaW5mbGF0ZWQNCi0gV2UgZGlkIG5vdCBoYXZlIHRoaXMgcHJvYmxlbSBpbiB0aGUgY2VudGVyZWQgbW9kZWwNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnZpZihDZW50ZXJlZC5SZWFkLjIpDQoNCmBgYA0KDQotIE5vdGU6IEl0cyBtb3JlIHN0YW5kYXJkIHRvIHBsb3QgdGhlIHVuY2VudGVyZWQgdmVyc2lvbiwgYnV0IHJ1biB0aGUgYW5hbHlzaXMgb24gdGhlIGNlbnRlcmVkIGRhdGENCg0KIyBUZXN0aW5nIFNpbXBsZSBTbG9wZXMgDQotIFRoZSBnb2FsIGhlcmUgaXQgZmlndXJlIG91dCB3aGVuIHRoZSBzbG9wZSBhdCBhIGdpdmVuIGxldmVsIG9mIGFub3RoZXIgdmFyaWFibGUgaXMgZGlmZmVyZW50IGZyb20gemVybw0KLSBXZSBjaG9wIHVwIHRoZSBpbnRlcmFjdGlvbiBhdCBzcGVjaWZpYyBwbGFjZXMgYXMgd2UgZGlkIHdpdGggdGhlIGludGVyYWN0aW9ucyBwbG90cyAoLTEgU0QsIE0sICsxIFNEKSBvbiB0aGUgKm1vZGVyYXRpbmcqIHZhcmlhYmxlIChhIHRoaXJkIHZhcmlhYmxlIHRoYXQgYWZmZWN0cyB0aGUgc3RyZW5ndGggb2YgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgZGVwZW5kZW50IGFuZCBpbmRlcGVuZGVudCB2YXJpYWJsZSkgDQotIE5leHQgd2UgdGVzdCB0aGUgc2xvcGVzIG9uIHRoZSAqcHJlZGljdG9yKiB2YXJpYWJsZSAoSVYgb2YgbWFpbiBpbnRlcmVzdCkNCi0gRXhhbXBsZTogUGVyZm9ybWFuY2Ugb24gYSB0YXNrLCBwcmVkaWN0ZWQgYnkgTGV2ZWwgb2YgdHJhaW5pbmcsIG1vZGVyYXRlZCBieSBob3cgaGFyZCB0aGUgY2hhbGxhbmdlIGlzDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0Kc2V0LnNlZWQoNDIpDQojIDI1MCBwZW9wbGUNCm4gPC0gMjUwDQojVHJhaW5pbmcgKGxvdyB0byBoaWdoKQ0KWCA8LSBydW5pZihuLCAtMTAsIDEwKQ0KIyBub3JtYWwgZGlzdHJvYnV0aW9uIG9mIENoYWxsZW5nZSBEaWZmaWN1bHR5DQpaIDwtIHJub3JtKG4sIDAsIDE1KQ0KIyBPdXIgZXF1YXRpb24gdG8gIGNyZWF0ZSBZDQpZIDwtIDIuMipYICsgMi40NipaICsgLjc1KlgqWiArIDE2ICsgcm5vcm0obiwgc2Q9NTApDQojQnVpbHQgb3VyIGRhdGEgZnJhbWUNClNraWxsLkRhdGE8LWRhdGEuZnJhbWUoUGVyZm9ybWFuY2U9WSxUcmFpbmluZz1YLENoYWxsZW5nZT1aKQ0KI3J1biBvdXIgcmVncmVzc2lvbg0KU2tpbGwuTW9kZWwuMTwtbG0oUGVyZm9ybWFuY2V+VHJhaW5pbmcrQ2hhbGxlbmdlLFNraWxsLkRhdGEpDQpTa2lsbC5Nb2RlbC4yPC1sbShQZXJmb3JtYW5jZX5UcmFpbmluZypDaGFsbGVuZ2UsU2tpbGwuRGF0YSkNCg0KYGBgDQoNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2FzaXMnfQ0KDQpzdGFyZ2F6ZXIoU2tpbGwuTW9kZWwuMSxTa2lsbC5Nb2RlbC4yLHR5cGU9Imh0bWwiLA0KICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKCJNYWluIEVmZmVjdHMiLCAiSW50ZXJhY3Rpb24iKSwNCiAgICAgICAgICBpbnRlcmNlcHQuYm90dG9tID0gRkFMU0UsDQogICAgICAgICAgc2luZ2xlLnJvdz1GQUxTRSwgDQogICAgICAgICAgbm90ZXMuYXBwZW5kID0gRkFMU0UsDQogICAgICAgICAgaGVhZGVyPUZBTFNFKQ0KDQpgYGANCg0KDQoNCiMjIFBsb3RpbmcgDQotIFdlIHdpbGwgdXNlIHRoZSByb2NrY2hhbGsgbGlicmFyeSB0byB2aXN1YWxpemUgb3VyIGludGVyYWN0aW9uIGFuZCB0ZXN0IG91ciBzaW1wbGUgc2xvcGVzDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHJvY2tjaGFsaykNCm0xcHMgPC0gcGxvdFNsb3BlcyhTa2lsbC5Nb2RlbC4yLCBtb2R4ID0gIkNoYWxsZW5nZSIsIHBsb3R4ID0gIlRyYWluaW5nIiwgbj0zLCBtb2R4VmFscz0ic3RkLmRldiIpDQptMXBzdHMgPC0gdGVzdFNsb3BlcyhtMXBzKQ0Kcm91bmQobTFwc3RzJGh5cG90ZXN0cyw0KQ0KYGBgDQoNCi0gV2Ugc2VlIHRoZSBzbG9wZSBhdCB0aGUgbWVhbiBpcyBub3Qgc2lnbmlmaWNhbnQsIA0KLSBUaGUgKzFTRCBhbmQgLTFTRCBhcmUgc2lnbmlmaWNhbnQsIHRoaXMgaXMgd2hhdCBpcyBkcml2aW5nIHRoZSBpbnRlcmFjdGlvbg0KLSBXZWxsIHRyYWluZWQgcGVvcGxlIGF0IGxvdyBsZXZlbHMgb2YgY2hhbGxlbmdlIGdldCB3b3JzZSB3aXRoIG1vcmUgdHJhaW5pbmcsIHdoaWxlIGhpZ2ggbGV2ZWxzIG9mIGNoYWxsZW5nZSB3aXRoIG1vcmUgdHJhaW5pbmcgZ2V0IGJldHRlcg0KLSBUaGUgYm9udXMgdXNpbmcgcm9ja2NoYWxrIGlzIHRoYXQgeW91IGNhbiBjaGFuZ2UgdGhlIG51bWJlciBvZiBsaW5lcyAoY2hhbmdlIG49MykgeW91IHdhbnQgdG8gc2VlIG9yIHlvdSBjYW4gdGVzdCBxdWFudGlsZXMgYXMgd2VsbCAoY2hhbmdlIG1vZHhWYWxzPSJxdWFudGlsZSIpDQoNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCm0xcFEgPC0gcGxvdFNsb3BlcyhTa2lsbC5Nb2RlbC4yLCBtb2R4ID0gIkNoYWxsZW5nZSIsIHBsb3R4ID0gIlRyYWluaW5nIiwgbj01LCBtb2R4VmFscz0icXVhbnRpbGUiKQ0KbTFwUXRzIDwtIHRlc3RTbG9wZXMobTFwUSkNCnJvdW5kKG0xcFF0cyRoeXBvdGVzdHMsNCkNCmBgYA0KDQojIyMgTm90ZXMNCi0gWW91IG9mdGVuIGRvbid0IG5lZWQgdG8gcnVuIHRoZSB0LXRlc3RzIG9uIHRoZSBzaW1wbGUgc2xvcGVzLCBidXQgdGhleSBjYW4gdXNlZnVsIHRvIHRlc3QgdmVyeSBzcGVjaWZpYyBoeXBvdGhlc2VzDQotIFlvdSBjYW4gcnVuIHNpbXBsZSBzbG9wZXMgYW5hbHlzaXMgb24gdGhyZWUtd2F5IGludGVyYWN0aW9ucywgYnV0IGxldHMgbGVhdmUgdGhhdCBhc2lkZSBmb3Igbm93IGFzIHlvdSB3b3VsZCBoYXZlIHRvIHVzZSBhIGRpZmZlcmVudCBSLXBhY2thZ2UsIGFnYWluIG9mdGVuIHRoZSBwbG90IHRlbGxzIHRoZSBzdG9yeQ0KDQojIE5vbi1MaW5lYXIgaW50ZXJhY3Rpb25zDQotIFlvdSBjYW4gaGF2ZSBub24tbGluZWFyIHRlcm1zIGludGVyYWN0aW5nIHdpdGggb3RoZXIgbGluZWFyIGFuZCBub24tbGluZWFyIHRlcm1zDQotIEV4YW1wbGU6IFF1aXQgc21va2luZywgWCA9IGZlYXIgb2YgeW91IGhlYWx0aCwgWiA9IG1vZGVyYXRlZCBieSBTZWxmLUVmZmljYWN5IGZvciBxdWl0aW5nDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0Kc2V0LnNlZWQoNDIpDQojIDI1MCBwZW9wbGUNCm4gPC0gMjUwDQojRmVhciBvZiBIZWFsdGgNClggPC0gcnVuaWYobiwgMSwgOSkNCiNDZW50ZXJlZCBTZWxmLUVmZmljYWN5DQpaIDwtIHJ1bmlmKG4sIDAsIDE1KQ0KIyBPdXIgZXF1YXRpb24gdG8gIGNyZWF0ZSBZDQpZIDwtIC4xNzgqWCAtIC4wNTIqWF4yICsgLjE2NCpYKlogKyAuMTY0KihYXjIpKlorIDMuNjUxICsgcm5vcm0obiwgc2Q9NTApDQojQnVpbHQgb3VyIGRhdGEgZnJhbWUNClNtb2tlLkRhdGE8LWRhdGEuZnJhbWUoU21va2luZz1ZLEhlYWx0aD1YLFNlbGZFZmZpY2FjeT1aKQ0KI3J1biBvdXIgcmVncmVzc2lvbg0KU21va2UuTW9kZWwuMTwtbG0oU21va2luZ35wb2x5KEhlYWx0aCwyKStTZWxmRWZmaWNhY3ksU21va2UuRGF0YSkNClNtb2tlLk1vZGVsLjI8LWxtKFNtb2tpbmd+cG9seShIZWFsdGgsMikqU2VsZkVmZmljYWN5LFNtb2tlLkRhdGEpDQoNCmBgYA0KDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxyZXN1bHRzPSdhc2lzJ30NCg0Kc3RhcmdhemVyKFNtb2tlLk1vZGVsLjEsU21va2UuTW9kZWwuMix0eXBlPSJodG1sIiwNCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gYygiTWFpbiBFZmZlY3RzIiwgIkludGVyYWN0aW9uIiksDQogICAgICAgICAgaW50ZXJjZXB0LmJvdHRvbSA9IEZBTFNFLA0KICAgICAgICAgIHNpbmdsZS5yb3c9RkFMU0UsIA0KICAgICAgICAgIG5vdGVzLmFwcGVuZCA9IEZBTFNFLA0KICAgICAgICAgIGhlYWRlcj1GQUxTRSkNCg0KYGBgDQoNCiMjIFBsb3R0aW5nIHRoZSBTaW1wbGUgU2xvcGVzIHdoZW4gdGhlcmUgYXJlIEN1cnZlcw0KLSBUaGVzZSBjYW4gYmUgZG9uZSB3aXRoIHRoZSAnZWZmZWN0cycgcGFja2FnZSBhcyBJIHNob3dlZCB5b3UgYWJvdmUgYW5kIGxhc3Qgd2Vlaw0KLSBvciB3ZSBjYW4gc2ltcGx5IHVzZSB0aGUgcGxvdEN1cnZlcyBmdW5jdGlvbiBmcm9tIHRoZSAncm9ja2NoYWxrJyBwYWNrYWdlDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpTbW9rZUludGVyUGxvdCA8LSBwbG90Q3VydmVzKFNtb2tlLk1vZGVsLjIsIG1vZHggPSAiU2VsZkVmZmljYWN5IiwgcGxvdHggPSAiSGVhbHRoIixuPTMsbW9keFZhbHM9InN0ZC5kZXYiKQ0KYGBgDQoNCi0gTm90aWNlIHRoYXQgdGhlIHNlY29uZCBvcmRlciB0ZXJtIHdhcyBub3Qgc2lnbmlmaWNhbnQgaW4gdGhlIG1haW4gZWZmZWN0IG1vZGVsDQotIFRoaXMgbWVhbnMgeW91IGNvdWxkIGhhdmUgbWlzc2VkIGl0LiBBcyB0aGUgbW9kZWwgMSBwbG90IHdvdWxkIGhhdmUgbG9va2VkIGxpa2UgdGhpczogIA0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KcGxvdEN1cnZlcyhTbW9rZS5Nb2RlbC4xLCBwbG90eCA9ICJIZWFsdGgiKQ0KYGBgDQoNCi0gSW4gb3RoZXIgd29yZHMsIHlvdSBjYW5ub3Qgc2VlIHRoZSBoaWdoZXIgb3JkZXIgZWZmZWN0cyBhcyB0aGV5IGFyZSBoaWRkZW4gYnkgdGhlIG1vZGVyYXRpbmcgdmFyaWFibGUNCi0gSG93IHRvIGZpbmQgdGhlc2UgaGlkZGVuIHRoaW5ncz8gDQotIFlvdSBoYXZlIHRvIHRlc3QgZm9yIGludGVyYWN0aW9ucyBhbmQgY2hhbmdlcyBpbiAkUl4yJA0KLSBZb3UgaGF2ZSB0cnkgdG8gYWRkIGhpZ2hlciBvcmRlciB0ZXJtcywgYnV0IHRoZXkgc2hvdWxkIGJlIG1vdGl2YXRlZCBieSBzb21lIHRoZW9yeQ0KLSBXaGF0IGlmIHlvdSBkaWQgbm90IGtub3cgdGhlIHNlY29uZCBvcmRlciB0ZXJtIHdhcyBpbiB0aGUgbW9kZWwgYWJvdmUuIA0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KU21va2UuTW9kZWwuMS5MPC1sbShTbW9raW5nfkhlYWx0aCtTZWxmRWZmaWNhY3ksU21va2UuRGF0YSkNClNtb2tlLk1vZGVsLjIuTDwtbG0oU21va2luZ35IZWFsdGgqU2VsZkVmZmljYWN5LFNtb2tlLkRhdGEpDQoNCmBgYA0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UscmVzdWx0cz0nYXNpcyd9DQoNCnN0YXJnYXplcihTbW9rZS5Nb2RlbC4xLkwsU21va2UuTW9kZWwuMi5MLHR5cGU9Imh0bWwiLA0KICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKCJNYWluIEVmZmVjdHMiLCAiSW50ZXJhY3Rpb24iKSwNCiAgICAgICAgICBpbnRlcmNlcHQuYm90dG9tID0gRkFMU0UsDQogICAgICAgICAgc2luZ2xlLnJvdz1GQUxTRSwgDQogICAgICAgICAgbm90ZXMuYXBwZW5kID0gRkFMU0UsDQogICAgICAgICAgaGVhZGVyPUZBTFNFKQ0KDQpgYGANCg0KYGBge3J9DQoNCkNoYW5nZUluUi5TbW9rZTwtYW5vdmEoU21va2UuTW9kZWwuMS5MLFNtb2tlLk1vZGVsLjIuTCkNCmtuaXRyOjprYWJsZShDaGFuZ2VJblIuU21va2UsIGRpZ2l0cz00KQ0KDQpgYGANCg0KLSBSZXBsb3RpbmcgYXMgbGluZWFyIGJ5IGxpbmVhciBpbnRlcmFjdGlvbg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShyb2NrY2hhbGspDQpTbW9rZUludGVyUGxvdC5MaW5lYXIgPC0gcGxvdFNsb3BlcyhTbW9rZS5Nb2RlbC4yLkwsIG1vZHggPSAiU2VsZkVmZmljYWN5IiwgcGxvdHggPSAiSGVhbHRoIiwgbj0zLCBtb2R4VmFscz0ic3RkLmRldiIpDQpgYGANCg0KDQotIGxldHMgY2hlY2sgdGhlIGNoYW5nZSBpbiAkUl4yJCBmcm9tIExpbmVhciBpbnRlcmFjdGlvbiBtb2RlbCB0byBwb2x5IGludGVyYWN0aW9uIG1vZGVsIG5vdw0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UscmVzdWx0cz0nYXNpcyd9DQoNCnN0YXJnYXplcihTbW9rZS5Nb2RlbC4yLkwsU21va2UuTW9kZWwuMix0eXBlPSJodG1sIiwNCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gYygiTGluZWFyIiwgIlBvbHkiKSwNCiAgICAgICAgICBpbnRlcmNlcHQuYm90dG9tID0gRkFMU0UsDQogICAgICAgICAgc2luZ2xlLnJvdz1GQUxTRSwgDQogICAgICAgICAgbm90ZXMuYXBwZW5kID0gRkFMU0UsDQogICAgICAgICAgaGVhZGVyPUZBTFNFKQ0KDQpgYGANCg0KYGBge3J9DQoNCkNoYW5nZUluUi5TbW9rZS4yPC1hbm92YShTbW9rZS5Nb2RlbC4yLkwsU21va2UuTW9kZWwuMikNCmtuaXRyOjprYWJsZShDaGFuZ2VJblIuU21va2UuMiwgZGlnaXRzPTQpDQoNCmBgYA0KDQojIyBOb3Rlcw0KLSBTbyBtb2RlbCB3aXRoIHBvbHkgaXMgYSBzaWduaWZpY2FudGx5IGJldHRlciBmaXQsIGJ1dCByZWFsbHkgaXRzIG5vdCBhIGh1Z2UgY2hhbmdlDQotIFdvdWxkIGl0IGNoYW5nZSB0aGUgc3RvcnkgYnkgbXVjaD8NCi0gTW9zdCBwZW9wbGUgd291bGQgaWdub3JlIHRoZSBoaWdoZXIgb3JkZXIgdGVybSwgYnV0IHlvdSB0aGUgY2hvaWNlIGRlcGVuZHMgb24geW91ciB0aGVvcnkhDQotIElnbm9yaW5nIHRoZSBoaWdoZXIgb3JkZXIgdGVybSBjYW4gbWFrZSBpdCBlYXNpZXIgdG8gdGVzdCBzaW1wbGUgc2xvcGVzDQotIEJ1dCBpZ25vcmluZyB0aGUgaGlnaGVyIG9yZGVyIHRlcm1zIGNhbiB2b2lsYXRlIHlvdXIgYXNzdW1wdGlvbnMsIGxldHMgY29tcGFyZSBvdXIgbGluZWFyIHZzIHBvbHkgbW9kZWxzIHJlc2lkdWFscw0KDQpgYGB7cn0NCnBsb3QoU21va2UuTW9kZWwuMi5MLCB3aGljaD0xKQ0KcGxvdChTbW9rZS5Nb2RlbC4yLCB3aGljaD0xKQ0KYGBgDQoNCg0KPHNjcmlwdD4NCiAgKGZ1bmN0aW9uKGkscyxvLGcscixhLG0pe2lbJ0dvb2dsZUFuYWx5dGljc09iamVjdCddPXI7aVtyXT1pW3JdfHxmdW5jdGlvbigpew0KICAoaVtyXS5xPWlbcl0ucXx8W10pLnB1c2goYXJndW1lbnRzKX0saVtyXS5sPTEqbmV3IERhdGUoKTthPXMuY3JlYXRlRWxlbWVudChvKSwNCiAgbT1zLmdldEVsZW1lbnRzQnlUYWdOYW1lKG8pWzBdO2EuYXN5bmM9MTthLnNyYz1nO20ucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoYSxtKQ0KICB9KSh3aW5kb3csZG9jdW1lbnQsJ3NjcmlwdCcsJ2h0dHBzOi8vd3d3Lmdvb2dsZS1hbmFseXRpY3MuY29tL2FuYWx5dGljcy5qcycsJ2dhJyk7DQoNCiAgZ2EoJ2NyZWF0ZScsICdVQS05MDQxNTE2MC0xJywgJ2F1dG8nKTsNCiAgZ2EoJ3NlbmQnLCAncGFnZXZpZXcnKTsNCg0KPC9zY3JpcHQ+