1 Prepping your R

1.1 Packages you will need:

  • install.packages(“lme4”) # Allows you to fit linear mixed-effects models.
  • install.packages(“effects”) # Helps you visualize interactions
  • install.packages(“corrplot”) # Helps you to visualize correlations
  • install.packages(“stargazer”) # Makes tables
  • install.packages(“car”) # Helps with Diagnostics
  • install.packages(“MASS”) # For stepwise regression
  • install.packages(“ggplot2”) # To create plots
  • install.packages(“texreg”) # Allows you to save tables.

1.2 Load the libraries you need.

library(lme4)
library(effects)
library(corrplot)
library(stargazer)
library(car)
library(MASS)
library(ggplot2)
library(texreg)

2 The Data Set:

This is a special type of data where there is only one measurement per student (i.e., there is no within subject variable), and students are nested in schools so we must control for the effects of schools.

Multilevel data occur when observations are nested within groups, for example, when students are nested within schools in a district.

Our simple story -

We looked at 6 schools (3 rich and 3 poor) with 40 students in each rich school and 160 students in each poor school, and we measured them on Happiness, number of Friends, and GPA. We wondered if happiness could be predicted by number of friends and/or GPA.

Our Level 1 will be the lowest unit of observation - those nested in groups. In this case, Level 1 is the students.

Our Level 2 refers to the groups in which the level 1 units are nested; in this case Level 2 is the schools.

2.1 Simulating the Dataset:

2.1.1 Make your equations

Create a distribution for each school, making the variance of rich schools small and the variance of poor schools large. We’ve also made the equations different so that our X (number of friends) coefficient is negative in rich schools but positive in the poor schools. We keep our Z (GPA) coefficient the same for all schools.

set.seed(42)
nrich=40
npoor=160
#Paramaters
S.F.Rich=-2 # setting paramaeters for the Friends variable in the Rich schools
S.F.Poor=6 # setting parameters for the Friends variable in the Poor schools

S.G.Rich=.7 # setting parameters for the GPA variable in the Rich schools
S.G.Poor=.7 # setting parameters for the GPA variable in the Poor schools
# Rich Schools
# School 1
X1 <- rnorm(nrich, 10, 2) # number of friends
Z1 <- runif(nrich, 1.0, 4.0) # GPA
Y1 <- S.F.Rich*X1 + S.G.Rich*Z1 + 80 + rnorm(nrich, sd= 5) # Our equation to create Y

# School 2
X2 <- rnorm(nrich, 10, 2) # number of friends
Z2 <- runif(nrich, 1.0, 4.0) # GPA
Y2 <- S.F.Rich*X2 + S.G.Rich*Z2 + 75 + rnorm(nrich, sd= 5)

# School 3
X3 <- rnorm(nrich, 10, 2) # number of friends
Z3 <- runif(nrich, 1.0, 4.0) # GPA
Y3 <- S.F.Rich*X3 + S.G.Rich*Z3 +90 + rnorm(nrich, sd= 5)

# Poor Schools
# School 4
X4 <- rnorm(npoor, 5, 2) #number of friends
Z4 <- runif(npoor, 1.0, 4.0) #GPA
Y4 <- S.F.Poor*X4 + S.G.Poor*Z4 + 35 + rnorm(npoor, sd = 10)

# School 5
X5 <- rnorm(npoor, 5, 2) #number of friends
Z5 <- runif(npoor, 1.0, 4.0) #GPA
Y5 <- S.F.Poor*X5 + S.G.Poor*Z5 + 40 + rnorm(npoor, sd = 10)

# School 6
X6 <- rnorm(npoor, 5, 2) #number of friends
Z6 <- runif(npoor, 1.0, 4.0) #GPA
Y6 <- S.F.Poor*X6 + S.G.Poor*Z6 + 50 + rnorm(npoor, sd = 10)

2.1.2 Make your data frames

Now that we have our equations, we’re going to create the data frame for each school.

# The 3 Rich Schools:
Student.Data.School.1<-data.frame(Happiness=Y1, Friends=X1, GPA=Z1)
Student.Data.School.2<-data.frame(Happiness=Y2, Friends=X2, GPA=Z2)
Student.Data.School.3<-data.frame(Happiness=Y3, Friends=X3, GPA=Z3)

# The 3 Poor Schools:
Student.Data.School.4<-data.frame(Happiness=Y4, Friends=X4, GPA=Z4)
Student.Data.School.5<-data.frame(Happiness=Y5, Friends=X5, GPA=Z5)
Student.Data.School.6<-data.frame(Happiness=Y6, Friends=X6, GPA=Z6)

2.1.3 Testing the formulas

Let’s test if these formulas work with an example from each set of schools (one rich and one poor).

# Rich - School 1
corr.student = cor(Student.Data.School.1)
corrplot(corr.student, method = "number",diag = FALSE,type = "lower")

Great, our correlation between Friends and Happiness is -.76 and we have a correlation between GPA and Happiness of -.05.

# Poor - School 4
corr.student = cor(Student.Data.School.4)
corrplot(corr.student, method = "number",diag = FALSE,type = "lower")

Great, our correlation between Friends and Happiness is .77, which is higher AND in the opposite direction than in rich schools. There was no correlation between GPA and Happiness.

Pay attention to the fact that the schools have different correlation values for Friends and Happiness depending on if they’re rich or poor. Rich schools have r = -.76 and poor schools have r = .77. For GPA and Happiness, only the rich school had a correlation. This will be important.

NOTE: Although I am telling you that some schools are rich and some are poor, you may not know this information when you come in and try to do the analysis. If we knew this information, we would put SES as another Level 2 predictor.

2.1.4 Put ’em together and what have you got!?

Now that we know they work, let’s put all schools together into one dataset.

All.Schools.Data <- rbind(Student.Data.School.1, Student.Data.School.2, Student.Data.School.3, Student.Data.School.4, Student.Data.School.5, Student.Data.School.6) 
head(All.Schools.Data)
##   Happiness   Friends      GPA
## 1  54.60136 12.741917 2.744812
## 2  64.21655  8.870604 1.473716
## 3  62.91056 10.726257 2.077085
## 4  66.52306 11.265725 2.936896
## 5  57.07570 10.808537 3.327470
## 6  68.82087  9.787751 2.690941

2.1.5 Adding Student and School Variables

Now let’s add in Student ID’s and the Schools as variables.

# Adding the subject variable (Student ID)
All.Schools.Data$StudentID<-seq(1:nrow(All.Schools.Data))
# Did it work?
head(All.Schools.Data)
##   Happiness   Friends      GPA StudentID
## 1  54.60136 12.741917 2.744812         1
## 2  64.21655  8.870604 1.473716         2
## 3  62.91056 10.726257 2.077085         3
## 4  66.52306 11.265725 2.936896         4
## 5  57.07570 10.808537 3.327470         5
## 6  68.82087  9.787751 2.690941         6
# Yes!

# Adding the School variable. 
All.Schools.Data$School<-c(rep(1, nrich), rep(2,nrich), rep(3, nrich), rep(4, npoor), 
                           rep(5, npoor), rep(6, npoor))

2.1.6 Test the full dataset

Let’s see what the scatter plots for each school.

  • Schools 1-3 are richand schools 4-6 are poor.

3 Plotting

Now let’s plot up raw data.

First, let’s plot with Friends as our IV.

theme_set(theme_bw(base_size = 12, base_family = "")) 

# Friends
Model.Plot.Friends <-ggplot(data = All.Schools.Data, aes(x = Friends, y=Happiness,group=School))+   
  facet_grid( ~ School)+    
  geom_point(aes(colour = School))+ 
  geom_smooth(method = "lm", se = TRUE, aes(colour = School))+  
  xlab("Friends")+ylab("Happiness")+    
  theme(legend.position = "none")   
Model.Plot.Friends  

Now, let’s plot with GPA as our IV.

Model.Plot.GPA <-ggplot(data = All.Schools.Data, aes(x =GPA, y=Happiness,group=School))+    
  facet_grid( ~ School)+    
  geom_point(aes(colour = School))+ 
  geom_smooth(method = "lm", se = TRUE, aes(colour = School))+  
  xlab("GPA")+ylab("Happiness")+    
  theme(legend.position = "none")   
Model.Plot.GPA  

Let’s see what our correlations look like when lumping all the rich and poor schools together.

corr.student = cor(All.Schools.Data[,1:3])
corrplot(corr.student, method = "number",diag = FALSE,type = "lower")

Notice how the correlation between Friends and Happiness has changed when you collapse across all rich and poor schools. The correlation is now .24 (which is between Rich and Poor schools). It is a positive correlation as well, but only poor schools (those with more students) had a positive correlation. Also notice that the correlation between GPA and Happiness is .05, which is the same as for the rich school - the only one that had showed a correlation.

4 Regular Regression

4.1 All Data

Let’s try running a normal regression on all the data at once.

First, let’s center the variables.

All.Schools.Data$Friends.C<-scale(All.Schools.Data$Friends, scale = FALSE)[,]
All.Schools.Data$GPA.C<-scale(All.Schools.Data$GPA, scale = FALSE)[,]

Now we can run the regression.

Reg.Model<-lm(Happiness ~ Friends.C + GPA.C, data = All.Schools.Data)
summary(Reg.Model)
## 
## Call:
## lm(formula = Happiness ~ Friends.C + GPA.C, data = All.Schools.Data)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -35.136 -10.946  -0.396  10.747  46.556 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  70.6843     0.6396 110.519  < 2e-16 ***
## Friends.C     1.4021     0.2317   6.051 2.54e-09 ***
## GPA.C         0.9560     0.7142   1.339    0.181    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 15.67 on 597 degrees of freedom
## Multiple R-squared:  0.05975,    Adjusted R-squared:  0.0566 
## F-statistic: 18.97 on 2 and 597 DF,  p-value: 1.03e-08
#plot(Reg.Model)

Putting all schools together in a regular regression, we have a positive, significant effect of Friends and no effect of GPA. But what happens when we separate the schools by rich or poor status?

4.2 By School

Now let’s see what happens if you run a normal regression on rich and poor schools separately.

# Rich Schools
School.Rich.Reg.Model<-lm(Happiness ~ Friends.C + GPA.C, data = subset(All.Schools.Data, School<4))
summary(School.Rich.Reg.Model)
## 
## Call:
## lm(formula = Happiness ~ Friends.C + GPA.C, data = subset(All.Schools.Data, 
##     School < 4))
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -15.956  -6.003  -1.160   5.468  20.517 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  72.4094     1.5372  47.106  < 2e-16 ***
## Friends.C    -2.1276     0.3464  -6.142 1.16e-08 ***
## GPA.C         0.4517     0.8171   0.553    0.581    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 7.924 on 117 degrees of freedom
## Multiple R-squared:  0.2457, Adjusted R-squared:  0.2328 
## F-statistic: 19.05 on 2 and 117 DF,  p-value: 6.868e-08

Notice, we have a negative, significant effect for Friends but no effect for GPA.

# Poor Schools
School.Poor.Reg.Model<-lm(Happiness ~ Friends.C + GPA.C, data = subset(All.Schools.Data, School>3))
summary(School.Poor.Reg.Model)
## 
## Call:
## lm(formula = Happiness ~ Friends.C + GPA.C, data = subset(All.Schools.Data, 
##     School > 3))
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -36.265  -8.085   0.389   8.044  34.997 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  78.6701     0.6012 130.854   <2e-16 ***
## Friends.C     6.4806     0.2798  23.162   <2e-16 ***
## GPA.C         0.6250     0.5962   1.048    0.295    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 11.72 on 477 degrees of freedom
## Multiple R-squared:   0.53,  Adjusted R-squared:  0.528 
## F-statistic: 268.9 on 2 and 477 DF,  p-value: < 2.2e-16

Uh-oh. Now we have a positive, significant effect of Friends and no effect of GPA.

4.3 What’s the problem?

Remember! Although I am telling you that some schools are rich and some are poor, you may not know this information when you come in and try to do the analysis. If we knew this information, we could put SES as another Level 2 predictor. Also, I am only using six schools to demonstrate, but in a more “normal” case of nested design there may be way too many schools to treat SES as a fixed factor in the design.

The regular regression did not reflect what was happening in each school type. It gave us a positive effect (Friends) when only poor schools had a positive effect. Collapsing across school types in this case was not ideal because different things were happening within each school type, compromising the generalizability of the findings.

Another way to put this, the regular (lm) regression indicates that the more friends a student has, the happier they are, but looking closer this is not the case in all schools (and is, in fact, the opposite in some). If you were trying to generalize your findings or use them to argue for/show a need for an intervention, your results would be misleading and could cause problems.

SO, Let’s try running this as a multilevel, random effects model:

5 Random Effects Model

Using a multi-level model allows us to separate the within-group effects from the between-group effects, whereas regular regression blends them together into a single coefficient.

5.1 Running the Random Effects Model

5.1.1 First, we run the null model.

Null<-lmer(Happiness ~ 1 # This simply means Happiness predicted by the intercept
                  +(1|School), # each school gets its own intercept 
                  data=All.Schools.Data, REML = FALSE)
summary(Null)
## Linear mixed model fit by maximum likelihood  ['lmerMod']
## Formula: Happiness ~ 1 + (1 | School)
##    Data: All.Schools.Data
## 
##      AIC      BIC   logLik deviance df.resid 
##   4933.3   4946.5  -2463.7   4927.3      597 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -3.0540 -0.6053  0.0226  0.6014  3.9123 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  School   (Intercept)  54.56    7.387  
##  Residual             209.21   14.464  
## Number of obs: 600, groups:  School, 6
## 
## Fixed effects:
##             Estimate Std. Error t value
## (Intercept)   68.336      3.103   22.02

We examine the intra-class correlation (ICC) to determine if multi-level modeling is the correct choice for our analysis. The ICC measures the degree of clustering in our data and answers the question, “How much does my Level 2 predict the total variance of my study?” If your ICC is greater than 0, you have a multi-level study.

ICC.Model<-function(Model.Name) {
  tau.Null<-as.numeric(lapply(summary(Model.Name)$varcor, diag))
  sigma.Null <- as.numeric(attr(summary(Model.Name)$varcor, "sc")^2)
  ICC.Null <- tau.Null/(tau.Null+sigma.Null)
  return(ICC.Null)
}

ICC.Model(Null)
## [1] 0.2068566

Our ICC is greater than 0, meaning we were correct to think of this as a multi-level problem.

5.1.2 Next, we run the Level 1 predictor (GPA).

The.Model.1<-lmer(Happiness ~ GPA.C 
                  +(1|School),
                data=All.Schools.Data, REML = FALSE)
summary(The.Model.1)
## Linear mixed model fit by maximum likelihood  ['lmerMod']
## Formula: Happiness ~ GPA.C + (1 | School)
##    Data: All.Schools.Data
## 
##      AIC      BIC   logLik deviance df.resid 
##   4934.7   4952.3  -2463.3   4926.7      596 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -3.0109 -0.5984  0.0242  0.5938  3.8676 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  School   (Intercept)  54.31    7.37   
##  Residual             209.01   14.46   
## Number of obs: 600, groups:  School, 6
## 
## Fixed effects:
##             Estimate Std. Error t value
## (Intercept)  68.3517     3.0960  22.077
## GPA.C         0.5192     0.6622   0.784
## 
## Correlation of Fixed Effects:
##       (Intr)
## GPA.C 0.006

The results indicate that a student’s GPA does not have an effect on their Happiness when controlling for Level 2 fluctuations in Happiness.

5.1.3 Finally, we run the Level 2 predictor (Friends).

This model allows the variable Friends to vary between schools.

The.Model.2<-lmer(Happiness ~ Friends.C + GPA.C 
                  +(1+Friends.C|School), # each school gets its own intercept, and Friends can vary as a function of the school.
                data=All.Schools.Data, REML = FALSE)
summary(The.Model.2)
## Linear mixed model fit by maximum likelihood  ['lmerMod']
## Formula: Happiness ~ Friends.C + GPA.C + (1 + Friends.C | School)
##    Data: All.Schools.Data
## 
##      AIC      BIC   logLik deviance df.resid 
##   4438.4   4469.2  -2212.2   4424.4      593 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -3.6347 -0.6060  0.0229  0.6675  3.7765 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev. Corr
##  School   (Intercept) 42.14    6.492        
##           Friends.C   17.94    4.235    0.51
##  Residual             86.37    9.294        
## Number of obs: 600, groups:  School, 6
## 
## Fixed effects:
##             Estimate Std. Error t value
## (Intercept)  75.3044     2.7888  27.003
## Friends.C     2.1566     1.7431   1.237
## GPA.C         0.4235     0.4271   0.991
## 
## Correlation of Fixed Effects:
##           (Intr) Frnd.C
## Friends.C 0.454        
## GPA.C     0.002  0.001

The results indicate that the number of Friends a student has does not have an effect on Happiness when controlling for the random effects of Level 2 influences.

5.1.4 What do the results mean?

In our regular (lm) regression, Friends had a significant effect, b = 1.40 (p < .001). However, in our mixed (lmer) regression, Friends had a larger (2.16), but non-significant effect.

Why is this important? The goal of multi-level modeling is to draw a conclusion about the general sample that you have while controlling for differences you are not trying to explain (in this example, rich vs. poor). Not properly controlling for these differences, which you may often not know are there, will increase your chance of Type I error. Because the effect of Friends was different in different schools, it makes sense that the multi-level model (MLM) did not show a significant effect. In the present example, our MLM gave us a more accurate interpretation - that no main effect of Friends existed for all schools generally.

5.2 Checking your Assumptions

For a comprehensive look at how to run diagnostics, please see Chapter 12 and Chapter 18

6 P-Values

P-values are hotly disputed in Mixed Models.

One solution is to estimate degrees of freedom to get p-values. SAS/SPSS uses Satterthwaite approximations. There are also Kenward-Roger approximations (see Westfall, Kenny, & Judd, 2014). Either are acceptable. Making a number of assumptions, these outputs will mirror the results of a traditional ANOVA fairly closely.

install.packages(“lmerTest”)

This is a mixed model add-on package to calculate p-values based on a certain set of assumptions. Each ddf is a different method of attaining p-values, so you can choose which to run. I give you three examples below. you will need to refit the models with the package lmerTest installed and loaded.

library(lmerTest)
The.Model.2<-lmer(Happiness ~ Friends.C + GPA.C 
                  +(1+Friends.C|School),
                data=All.Schools.Data, REML = FALSE)

summary(The.Model.2,ddf = "lme4")
## Linear mixed model fit by maximum likelihood  ['lmerMod']
## Formula: Happiness ~ Friends.C + GPA.C + (1 + Friends.C | School)
##    Data: All.Schools.Data
## 
##      AIC      BIC   logLik deviance df.resid 
##   4438.4   4469.2  -2212.2   4424.4      593 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -3.6347 -0.6060  0.0229  0.6675  3.7765 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev. Corr
##  School   (Intercept) 42.14    6.492        
##           Friends.C   17.94    4.235    0.51
##  Residual             86.37    9.294        
## Number of obs: 600, groups:  School, 6
## 
## Fixed effects:
##             Estimate Std. Error t value
## (Intercept)  75.3044     2.7888  27.003
## Friends.C     2.1566     1.7431   1.237
## GPA.C         0.4235     0.4271   0.991
## 
## Correlation of Fixed Effects:
##           (Intr) Frnd.C
## Friends.C 0.454        
## GPA.C     0.002  0.001
summary(The.Model.2, ddf = "Satterthwaite") # SAS method
## Linear mixed model fit by maximum likelihood t-tests use Satterthwaite
##   approximations to degrees of freedom [lmerMod]
## Formula: Happiness ~ Friends.C + GPA.C + (1 + Friends.C | School)
##    Data: All.Schools.Data
## 
##      AIC      BIC   logLik deviance df.resid 
##   4438.4   4469.2  -2212.2   4424.4      593 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -3.6347 -0.6060  0.0229  0.6675  3.7765 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev. Corr
##  School   (Intercept) 42.14    6.492        
##           Friends.C   17.94    4.235    0.51
##  Residual             86.37    9.294        
## Number of obs: 600, groups:  School, 6
## 
## Fixed effects:
##             Estimate Std. Error       df t value Pr(>|t|)    
## (Intercept)  75.3044     2.7888   6.0000  27.003 1.55e-07 ***
## Friends.C     2.1566     1.7431   6.0000   1.237    0.262    
## GPA.C         0.4235     0.4271 588.8000   0.991    0.322    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##           (Intr) Frnd.C
## Friends.C 0.454        
## GPA.C     0.002  0.001

If you want to report main effects and interactions instead of slopes, you might convert your mixed model into an ANOVA.

anova(The.Model.2,ddf = "Kenward-Roger") # Kenny et al suggested method
## Analysis of Variance Table of type III  with  Kenward-Roger 
## approximation for degrees of freedom
##            Sum Sq Mean Sq NumDF  DenDF F.value Pr(>F)
## Friends.C 108.798 108.798     1   5.00  1.2596 0.3127
## GPA.C      83.824  83.824     1 587.57  0.9705 0.3250

7 References

Huta, V. (2014). When to use hierarchical linear modeling. The Quantitative Methods for Psychology, 10(1): 13- 28.

Westfall, J., Kenny, D. A., & Judd, C. M. (2014). Statistical power and optimal design in experiments in which samples of participants respond to samples of stimuli. Journal of Experimental Psychology: General, 143(5), 2020-2045.

Woltman, H., Feldstain, A., MacKay, J. C., & Rocchi, M. (2012). An introduction to hierarchical linear modeling. Tutorials in Quantitative Methods for Psychology, 8(1): 52-69.

LS0tDQp0aXRsZTogIkNoYXB0ZXIgMTY6IE11bHRpbGV2ZWwgTW9kZWxpbmciDQphdXRob3I6ICJUZXJlc2EgRy4gQm9yb3dza2kiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdGhlbWU6IGNlcnVsZWFuDQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQ0KICAgIGZvbnRzaXplOiA4cHQNCiAgICB0b2M6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoPTUpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLmhlaWdodD0zLjc1KQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5hbGlnbj0nY2VudGVyJykgDQpgYGANCg0KIyBQcmVwcGluZyB5b3VyIFINCg0KIyNQYWNrYWdlcyB5b3Ugd2lsbCBuZWVkOg0KDQotIGluc3RhbGwucGFja2FnZXMoImxtZTQiKSAjIEFsbG93cyB5b3UgdG8gZml0IGxpbmVhciBtaXhlZC1lZmZlY3RzIG1vZGVscy4NCi0gaW5zdGFsbC5wYWNrYWdlcygiZWZmZWN0cyIpICMgSGVscHMgeW91IHZpc3VhbGl6ZSBpbnRlcmFjdGlvbnMNCi0gaW5zdGFsbC5wYWNrYWdlcygiY29ycnBsb3QiKSAjIEhlbHBzIHlvdSB0byB2aXN1YWxpemUgY29ycmVsYXRpb25zIA0KLSBpbnN0YWxsLnBhY2thZ2VzKCJzdGFyZ2F6ZXIiKSAjIE1ha2VzIHRhYmxlcw0KLSBpbnN0YWxsLnBhY2thZ2VzKCJjYXIiKSAjIEhlbHBzIHdpdGggRGlhZ25vc3RpY3MNCi0gaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpICMgRm9yIHN0ZXB3aXNlIHJlZ3Jlc3Npb24NCi0gaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpICMgVG8gY3JlYXRlIHBsb3RzDQotIGluc3RhbGwucGFja2FnZXMoInRleHJlZyIpICMgQWxsb3dzIHlvdSB0byBzYXZlIHRhYmxlcy4NCg0KIyNMb2FkIHRoZSBsaWJyYXJpZXMgeW91IG5lZWQuDQoNCmBgYHtyfQ0KbGlicmFyeShsbWU0KQ0KbGlicmFyeShlZmZlY3RzKQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkoc3RhcmdhemVyKQ0KbGlicmFyeShjYXIpDQpsaWJyYXJ5KE1BU1MpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRleHJlZykNCmBgYA0KDQoNCiMgVGhlIERhdGEgU2V0Og0KDQpUaGlzIGlzIGEgc3BlY2lhbCB0eXBlIG9mIGRhdGEgd2hlcmUgdGhlcmUgaXMgb25seSBvbmUgbWVhc3VyZW1lbnQgcGVyIHN0dWRlbnQgKGkuZS4sIHRoZXJlIGlzIG5vIHdpdGhpbiBzdWJqZWN0IHZhcmlhYmxlKSwgYW5kIHN0dWRlbnRzIGFyZSBuZXN0ZWQgaW4gc2Nob29scyBzbyB3ZSBtdXN0IGNvbnRyb2wgZm9yIHRoZSBlZmZlY3RzIG9mIHNjaG9vbHMuIA0KDQpNdWx0aWxldmVsIGRhdGEgb2NjdXIgd2hlbiBvYnNlcnZhdGlvbnMgYXJlIG5lc3RlZCB3aXRoaW4gZ3JvdXBzLCBmb3IgZXhhbXBsZSwgd2hlbiBzdHVkZW50cyBhcmUgbmVzdGVkIHdpdGhpbiBzY2hvb2xzIGluIGEgZGlzdHJpY3QuDQoNCk91ciBzaW1wbGUgc3RvcnkgLQ0KDQpXZSBsb29rZWQgYXQgNiBzY2hvb2xzICgzIHJpY2ggYW5kIDMgcG9vcikgd2l0aCA0MCBzdHVkZW50cyBpbiBlYWNoIHJpY2ggc2Nob29sIGFuZCAxNjAgc3R1ZGVudHMgaW4gZWFjaCBwb29yIHNjaG9vbCwgYW5kIHdlIG1lYXN1cmVkIHRoZW0gb24gSGFwcGluZXNzLCBudW1iZXIgb2YgRnJpZW5kcywgYW5kIEdQQS4gV2Ugd29uZGVyZWQgaWYgaGFwcGluZXNzIGNvdWxkIGJlIHByZWRpY3RlZCBieSBudW1iZXIgb2YgZnJpZW5kcyBhbmQvb3IgR1BBLg0KDQpPdXIgTGV2ZWwgMSB3aWxsIGJlIHRoZSBsb3dlc3QgdW5pdCBvZiBvYnNlcnZhdGlvbiAtIHRob3NlIG5lc3RlZCBpbiBncm91cHMuIEluIHRoaXMgY2FzZSwgTGV2ZWwgMSBpcyB0aGUgc3R1ZGVudHMuDQoNCk91ciBMZXZlbCAyIHJlZmVycyB0byB0aGUgZ3JvdXBzIGluIHdoaWNoIHRoZSBsZXZlbCAxIHVuaXRzIGFyZSBuZXN0ZWQ7IGluIHRoaXMgY2FzZSBMZXZlbCAyIGlzIHRoZSBzY2hvb2xzLg0KDQojIyBTaW11bGF0aW5nIHRoZSBEYXRhc2V0Og0KDQojIyMgTWFrZSB5b3VyIGVxdWF0aW9ucw0KQ3JlYXRlIGEgZGlzdHJpYnV0aW9uIGZvciBlYWNoIHNjaG9vbCwgbWFraW5nIHRoZSB2YXJpYW5jZSBvZiByaWNoIHNjaG9vbHMgc21hbGwgYW5kIHRoZSB2YXJpYW5jZSBvZiBwb29yIHNjaG9vbHMgbGFyZ2UuIFdlJ3ZlIGFsc28gbWFkZSB0aGUgZXF1YXRpb25zIGRpZmZlcmVudCBzbyB0aGF0IG91ciBYIChudW1iZXIgb2YgZnJpZW5kcykgY29lZmZpY2llbnQgaXMgbmVnYXRpdmUgaW4gcmljaCBzY2hvb2xzIGJ1dCBwb3NpdGl2ZSBpbiB0aGUgcG9vciBzY2hvb2xzLiBXZSBrZWVwIG91ciBaIChHUEEpIGNvZWZmaWNpZW50IHRoZSBzYW1lIGZvciBhbGwgc2Nob29scy4gDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNDIpDQpucmljaD00MA0KbnBvb3I9MTYwDQojUGFyYW1hdGVycw0KUy5GLlJpY2g9LTIgIyBzZXR0aW5nIHBhcmFtYWV0ZXJzIGZvciB0aGUgRnJpZW5kcyB2YXJpYWJsZSBpbiB0aGUgUmljaCBzY2hvb2xzDQpTLkYuUG9vcj02ICMgc2V0dGluZyBwYXJhbWV0ZXJzIGZvciB0aGUgRnJpZW5kcyB2YXJpYWJsZSBpbiB0aGUgUG9vciBzY2hvb2xzDQoNClMuRy5SaWNoPS43ICMgc2V0dGluZyBwYXJhbWV0ZXJzIGZvciB0aGUgR1BBIHZhcmlhYmxlIGluIHRoZSBSaWNoIHNjaG9vbHMNClMuRy5Qb29yPS43ICMgc2V0dGluZyBwYXJhbWV0ZXJzIGZvciB0aGUgR1BBIHZhcmlhYmxlIGluIHRoZSBQb29yIHNjaG9vbHMNCiMgUmljaCBTY2hvb2xzDQojIFNjaG9vbCAxDQpYMSA8LSBybm9ybShucmljaCwgMTAsIDIpICMgbnVtYmVyIG9mIGZyaWVuZHMNCloxIDwtIHJ1bmlmKG5yaWNoLCAxLjAsIDQuMCkgIyBHUEENClkxIDwtIFMuRi5SaWNoKlgxICsgUy5HLlJpY2gqWjEgKyA4MCArIHJub3JtKG5yaWNoLCBzZD0gNSkgIyBPdXIgZXF1YXRpb24gdG8gY3JlYXRlIFkNCg0KIyBTY2hvb2wgMg0KWDIgPC0gcm5vcm0obnJpY2gsIDEwLCAyKSAjIG51bWJlciBvZiBmcmllbmRzDQpaMiA8LSBydW5pZihucmljaCwgMS4wLCA0LjApICMgR1BBDQpZMiA8LSBTLkYuUmljaCpYMiArIFMuRy5SaWNoKloyICsgNzUgKyBybm9ybShucmljaCwgc2Q9IDUpDQoNCiMgU2Nob29sIDMNClgzIDwtIHJub3JtKG5yaWNoLCAxMCwgMikgIyBudW1iZXIgb2YgZnJpZW5kcw0KWjMgPC0gcnVuaWYobnJpY2gsIDEuMCwgNC4wKSAjIEdQQQ0KWTMgPC0gUy5GLlJpY2gqWDMgKyBTLkcuUmljaCpaMyArOTAgKyBybm9ybShucmljaCwgc2Q9IDUpDQoNCiMgUG9vciBTY2hvb2xzDQojIFNjaG9vbCA0DQpYNCA8LSBybm9ybShucG9vciwgNSwgMikgI251bWJlciBvZiBmcmllbmRzDQpaNCA8LSBydW5pZihucG9vciwgMS4wLCA0LjApICNHUEENClk0IDwtIFMuRi5Qb29yKlg0ICsgUy5HLlBvb3IqWjQgKyAzNSArIHJub3JtKG5wb29yLCBzZCA9IDEwKQ0KDQojIFNjaG9vbCA1DQpYNSA8LSBybm9ybShucG9vciwgNSwgMikgI251bWJlciBvZiBmcmllbmRzDQpaNSA8LSBydW5pZihucG9vciwgMS4wLCA0LjApICNHUEENClk1IDwtIFMuRi5Qb29yKlg1ICsgUy5HLlBvb3IqWjUgKyA0MCArIHJub3JtKG5wb29yLCBzZCA9IDEwKQ0KDQojIFNjaG9vbCA2DQpYNiA8LSBybm9ybShucG9vciwgNSwgMikgI251bWJlciBvZiBmcmllbmRzDQpaNiA8LSBydW5pZihucG9vciwgMS4wLCA0LjApICNHUEENClk2IDwtIFMuRi5Qb29yKlg2ICsgUy5HLlBvb3IqWjYgKyA1MCArIHJub3JtKG5wb29yLCBzZCA9IDEwKQ0KDQpgYGAgDQoNCiMjIyBNYWtlIHlvdXIgZGF0YSBmcmFtZXMNCk5vdyB0aGF0IHdlIGhhdmUgb3VyIGVxdWF0aW9ucywgd2UncmUgZ29pbmcgdG8gY3JlYXRlIHRoZSBkYXRhIGZyYW1lIGZvciBlYWNoIHNjaG9vbC4NCg0KYGBge3J9DQojIFRoZSAzIFJpY2ggU2Nob29sczoNClN0dWRlbnQuRGF0YS5TY2hvb2wuMTwtZGF0YS5mcmFtZShIYXBwaW5lc3M9WTEsIEZyaWVuZHM9WDEsIEdQQT1aMSkNClN0dWRlbnQuRGF0YS5TY2hvb2wuMjwtZGF0YS5mcmFtZShIYXBwaW5lc3M9WTIsIEZyaWVuZHM9WDIsIEdQQT1aMikNClN0dWRlbnQuRGF0YS5TY2hvb2wuMzwtZGF0YS5mcmFtZShIYXBwaW5lc3M9WTMsIEZyaWVuZHM9WDMsIEdQQT1aMykNCg0KIyBUaGUgMyBQb29yIFNjaG9vbHM6DQpTdHVkZW50LkRhdGEuU2Nob29sLjQ8LWRhdGEuZnJhbWUoSGFwcGluZXNzPVk0LCBGcmllbmRzPVg0LCBHUEE9WjQpDQpTdHVkZW50LkRhdGEuU2Nob29sLjU8LWRhdGEuZnJhbWUoSGFwcGluZXNzPVk1LCBGcmllbmRzPVg1LCBHUEE9WjUpDQpTdHVkZW50LkRhdGEuU2Nob29sLjY8LWRhdGEuZnJhbWUoSGFwcGluZXNzPVk2LCBGcmllbmRzPVg2LCBHUEE9WjYpDQoNCmBgYA0KDQojIyMgVGVzdGluZyB0aGUgZm9ybXVsYXMNCkxldCdzIHRlc3QgaWYgdGhlc2UgZm9ybXVsYXMgd29yayB3aXRoIGFuIGV4YW1wbGUgZnJvbSBlYWNoIHNldCBvZiBzY2hvb2xzIChvbmUgcmljaCBhbmQgb25lIHBvb3IpLg0KDQpgYGB7cn0NCiMgUmljaCAtIFNjaG9vbCAxDQpjb3JyLnN0dWRlbnQgPSBjb3IoU3R1ZGVudC5EYXRhLlNjaG9vbC4xKQ0KY29ycnBsb3QoY29yci5zdHVkZW50LCBtZXRob2QgPSAibnVtYmVyIixkaWFnID0gRkFMU0UsdHlwZSA9ICJsb3dlciIpDQpgYGANCg0KR3JlYXQsIG91ciBjb3JyZWxhdGlvbiBiZXR3ZWVuIEZyaWVuZHMgYW5kIEhhcHBpbmVzcyBpcyAtLjc2IGFuZCB3ZSBoYXZlIGEgY29ycmVsYXRpb24gYmV0d2VlbiBHUEEgYW5kIEhhcHBpbmVzcyBvZiAtLjA1Lg0KDQpgYGB7cn0NCiMgUG9vciAtIFNjaG9vbCA0DQpjb3JyLnN0dWRlbnQgPSBjb3IoU3R1ZGVudC5EYXRhLlNjaG9vbC40KQ0KY29ycnBsb3QoY29yci5zdHVkZW50LCBtZXRob2QgPSAibnVtYmVyIixkaWFnID0gRkFMU0UsdHlwZSA9ICJsb3dlciIpDQpgYGANCg0KR3JlYXQsIG91ciBjb3JyZWxhdGlvbiBiZXR3ZWVuIEZyaWVuZHMgYW5kIEhhcHBpbmVzcyBpcyAuNzcsIHdoaWNoIGlzIGhpZ2hlciBBTkQgaW4gdGhlIG9wcG9zaXRlIGRpcmVjdGlvbiB0aGFuIGluIHJpY2ggc2Nob29scy4gVGhlcmUgd2FzIG5vIGNvcnJlbGF0aW9uIGJldHdlZW4gR1BBIGFuZCBIYXBwaW5lc3MuDQoNClBheSBhdHRlbnRpb24gdG8gdGhlIGZhY3QgdGhhdCB0aGUgc2Nob29scyBoYXZlIGRpZmZlcmVudCBjb3JyZWxhdGlvbiB2YWx1ZXMgZm9yIEZyaWVuZHMgYW5kIEhhcHBpbmVzcyBkZXBlbmRpbmcgb24gaWYgdGhleSdyZSByaWNoIG9yIHBvb3IuIFJpY2ggc2Nob29scyBoYXZlIHIgPSAtLjc2IGFuZCBwb29yIHNjaG9vbHMgaGF2ZSByID0gLjc3LiBGb3IgR1BBIGFuZCBIYXBwaW5lc3MsIG9ubHkgdGhlIHJpY2ggc2Nob29sIGhhZCBhIGNvcnJlbGF0aW9uLiBUaGlzIHdpbGwgYmUgaW1wb3J0YW50LiANCg0KTk9URTogQWx0aG91Z2ggSSBhbSB0ZWxsaW5nIHlvdSB0aGF0IHNvbWUgc2Nob29scyBhcmUgcmljaCBhbmQgc29tZSBhcmUgcG9vciwgeW91IG1heSBub3Qga25vdyB0aGlzIGluZm9ybWF0aW9uIHdoZW4geW91IGNvbWUgaW4gYW5kIHRyeSB0byBkbyB0aGUgYW5hbHlzaXMuIElmIHdlIGtuZXcgdGhpcyBpbmZvcm1hdGlvbiwgd2Ugd291bGQgcHV0IFNFUyBhcyBhbm90aGVyIExldmVsIDIgcHJlZGljdG9yLiANCg0KDQojIyMgUHV0ICdlbSB0b2dldGhlciBhbmQgd2hhdCBoYXZlIHlvdSBnb3QhPw0KTm93IHRoYXQgd2Uga25vdyB0aGV5IHdvcmssIGxldCdzIHB1dCBhbGwgc2Nob29scyB0b2dldGhlciBpbnRvIG9uZSBkYXRhc2V0LiANCg0KYGBge3J9DQpBbGwuU2Nob29scy5EYXRhIDwtIHJiaW5kKFN0dWRlbnQuRGF0YS5TY2hvb2wuMSwgU3R1ZGVudC5EYXRhLlNjaG9vbC4yLCBTdHVkZW50LkRhdGEuU2Nob29sLjMsIFN0dWRlbnQuRGF0YS5TY2hvb2wuNCwgU3R1ZGVudC5EYXRhLlNjaG9vbC41LCBTdHVkZW50LkRhdGEuU2Nob29sLjYpIA0KaGVhZChBbGwuU2Nob29scy5EYXRhKQ0KYGBgDQoNCiMjIyBBZGRpbmcgU3R1ZGVudCBhbmQgU2Nob29sIFZhcmlhYmxlcw0KTm93IGxldCdzIGFkZCBpbiBTdHVkZW50IElEJ3MgYW5kIHRoZSBTY2hvb2xzIGFzIHZhcmlhYmxlcy4NCg0KYGBge3J9DQojIEFkZGluZyB0aGUgc3ViamVjdCB2YXJpYWJsZSAoU3R1ZGVudCBJRCkNCkFsbC5TY2hvb2xzLkRhdGEkU3R1ZGVudElEPC1zZXEoMTpucm93KEFsbC5TY2hvb2xzLkRhdGEpKQ0KIyBEaWQgaXQgd29yaz8NCmhlYWQoQWxsLlNjaG9vbHMuRGF0YSkNCiMgWWVzIQ0KDQojIEFkZGluZyB0aGUgU2Nob29sIHZhcmlhYmxlLiANCkFsbC5TY2hvb2xzLkRhdGEkU2Nob29sPC1jKHJlcCgxLCBucmljaCksIHJlcCgyLG5yaWNoKSwgcmVwKDMsIG5yaWNoKSwgcmVwKDQsIG5wb29yKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoNSwgbnBvb3IpLCByZXAoNiwgbnBvb3IpKQ0KYGBgDQoNCiMjIyBUZXN0IHRoZSBmdWxsIGRhdGFzZXQNCkxldCdzIHNlZSB3aGF0IHRoZSBzY2F0dGVyIHBsb3RzIGZvciBlYWNoIHNjaG9vbC4NCg0KLSBTY2hvb2xzIDEtMyBhcmUgcmljaGFuZCBzY2hvb2xzIDQtNiBhcmUgcG9vci4NCg0KDQojIFBsb3R0aW5nIA0KDQpOb3cgbGV0J3MgcGxvdCB1cCByYXcgZGF0YS4NCg0KRmlyc3QsIGxldCdzIHBsb3Qgd2l0aCBGcmllbmRzIGFzIG91ciBJVi4NCg0KYGBge3J9DQp0aGVtZV9zZXQodGhlbWVfYncoYmFzZV9zaXplID0gMTIsIGJhc2VfZmFtaWx5ID0gIiIpKSANCg0KIyBGcmllbmRzDQpNb2RlbC5QbG90LkZyaWVuZHMgPC1nZ3Bsb3QoZGF0YSA9IEFsbC5TY2hvb2xzLkRhdGEsIGFlcyh4ID0gRnJpZW5kcywgeT1IYXBwaW5lc3MsZ3JvdXA9U2Nob29sKSkrCQ0KICBmYWNldF9ncmlkKCB+IFNjaG9vbCkrCQ0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBTY2hvb2wpKSsJDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgYWVzKGNvbG91ciA9IFNjaG9vbCkpKwkNCiAgeGxhYigiRnJpZW5kcyIpK3lsYWIoIkhhcHBpbmVzcyIpKwkNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQkNCk1vZGVsLlBsb3QuRnJpZW5kcwkNCmBgYA0KDQpOb3csIGxldCdzIHBsb3Qgd2l0aCBHUEEgYXMgb3VyIElWLg0KDQpgYGB7cn0NCk1vZGVsLlBsb3QuR1BBIDwtZ2dwbG90KGRhdGEgPSBBbGwuU2Nob29scy5EYXRhLCBhZXMoeCA9R1BBLCB5PUhhcHBpbmVzcyxncm91cD1TY2hvb2wpKSsJDQogIGZhY2V0X2dyaWQoIH4gU2Nob29sKSsJDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IFNjaG9vbCkpKwkNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBUUlVFLCBhZXMoY29sb3VyID0gU2Nob29sKSkrCQ0KICB4bGFiKCJHUEEiKSt5bGFiKCJIYXBwaW5lc3MiKSsJDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikJDQpNb2RlbC5QbG90LkdQQQkNCmBgYA0KDQpMZXQncyBzZWUgd2hhdCBvdXIgY29ycmVsYXRpb25zIGxvb2sgbGlrZSB3aGVuIGx1bXBpbmcgYWxsIHRoZSByaWNoIGFuZCBwb29yIHNjaG9vbHMgdG9nZXRoZXIuDQoNCmBgYHtyfQ0KY29yci5zdHVkZW50ID0gY29yKEFsbC5TY2hvb2xzLkRhdGFbLDE6M10pDQpjb3JycGxvdChjb3JyLnN0dWRlbnQsIG1ldGhvZCA9ICJudW1iZXIiLGRpYWcgPSBGQUxTRSx0eXBlID0gImxvd2VyIikNCmBgYA0KDQpOb3RpY2UgaG93IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIEZyaWVuZHMgYW5kIEhhcHBpbmVzcyBoYXMgY2hhbmdlZCB3aGVuIHlvdSBjb2xsYXBzZSBhY3Jvc3MgYWxsIHJpY2ggYW5kIHBvb3Igc2Nob29scy4gVGhlIGNvcnJlbGF0aW9uIGlzIG5vdyAuMjQgKHdoaWNoIGlzIGJldHdlZW4gUmljaCBhbmQgUG9vciBzY2hvb2xzKS4gSXQgaXMgYSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBhcyB3ZWxsLCBidXQgb25seSBwb29yIHNjaG9vbHMgKHRob3NlIHdpdGggbW9yZSBzdHVkZW50cykgaGFkIGEgcG9zaXRpdmUgY29ycmVsYXRpb24uDQpBbHNvIG5vdGljZSB0aGF0IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIEdQQSBhbmQgSGFwcGluZXNzIGlzIC4wNSwgd2hpY2ggaXMgdGhlIHNhbWUgYXMgZm9yIHRoZSByaWNoIHNjaG9vbCAtIHRoZSBvbmx5IG9uZSB0aGF0IGhhZCBzaG93ZWQgYSBjb3JyZWxhdGlvbi4gDQoNCg0KIyBSZWd1bGFyIFJlZ3Jlc3Npb24NCg0KIyMgQWxsIERhdGENCkxldCdzIHRyeSBydW5uaW5nIGEgbm9ybWFsIHJlZ3Jlc3Npb24gb24gYWxsIHRoZSBkYXRhIGF0IG9uY2UuDQoNCkZpcnN0LCBsZXQncyBjZW50ZXIgdGhlIHZhcmlhYmxlcy4NCg0KYGBge3J9DQpBbGwuU2Nob29scy5EYXRhJEZyaWVuZHMuQzwtc2NhbGUoQWxsLlNjaG9vbHMuRGF0YSRGcmllbmRzLCBzY2FsZSA9IEZBTFNFKVssXQ0KQWxsLlNjaG9vbHMuRGF0YSRHUEEuQzwtc2NhbGUoQWxsLlNjaG9vbHMuRGF0YSRHUEEsIHNjYWxlID0gRkFMU0UpWyxdDQpgYGANCg0KTm93IHdlIGNhbiBydW4gdGhlIHJlZ3Jlc3Npb24uDQoNCmBgYHtyfQ0KUmVnLk1vZGVsPC1sbShIYXBwaW5lc3MgfiBGcmllbmRzLkMgKyBHUEEuQywgZGF0YSA9IEFsbC5TY2hvb2xzLkRhdGEpDQpzdW1tYXJ5KFJlZy5Nb2RlbCkNCiNwbG90KFJlZy5Nb2RlbCkNCmBgYA0KDQpQdXR0aW5nIGFsbCBzY2hvb2xzIHRvZ2V0aGVyIGluIGEgcmVndWxhciByZWdyZXNzaW9uLCB3ZSBoYXZlIGEgcG9zaXRpdmUsIHNpZ25pZmljYW50IGVmZmVjdCBvZiBGcmllbmRzIGFuZCBubyBlZmZlY3Qgb2YgR1BBLiBCdXQgd2hhdCBoYXBwZW5zIHdoZW4gd2Ugc2VwYXJhdGUgdGhlIHNjaG9vbHMgYnkgcmljaCBvciBwb29yIHN0YXR1cz8NCg0KIyMgQnkgU2Nob29sDQpOb3cgbGV0J3Mgc2VlIHdoYXQgaGFwcGVucyBpZiB5b3UgcnVuIGEgbm9ybWFsIHJlZ3Jlc3Npb24gb24gcmljaCBhbmQgcG9vciBzY2hvb2xzIHNlcGFyYXRlbHkuDQoNCmBgYHtyfQ0KIyBSaWNoIFNjaG9vbHMNClNjaG9vbC5SaWNoLlJlZy5Nb2RlbDwtbG0oSGFwcGluZXNzIH4gRnJpZW5kcy5DICsgR1BBLkMsIGRhdGEgPSBzdWJzZXQoQWxsLlNjaG9vbHMuRGF0YSwgU2Nob29sPDQpKQ0Kc3VtbWFyeShTY2hvb2wuUmljaC5SZWcuTW9kZWwpDQpgYGANCg0KTm90aWNlLCB3ZSBoYXZlIGEgbmVnYXRpdmUsIHNpZ25pZmljYW50IGVmZmVjdCBmb3IgRnJpZW5kcyBidXQgbm8gZWZmZWN0IGZvciBHUEEuDQoNCmBgYHtyfQ0KIyBQb29yIFNjaG9vbHMNClNjaG9vbC5Qb29yLlJlZy5Nb2RlbDwtbG0oSGFwcGluZXNzIH4gRnJpZW5kcy5DICsgR1BBLkMsIGRhdGEgPSBzdWJzZXQoQWxsLlNjaG9vbHMuRGF0YSwgU2Nob29sPjMpKQ0Kc3VtbWFyeShTY2hvb2wuUG9vci5SZWcuTW9kZWwpDQpgYGANCg0KVWgtb2guIE5vdyB3ZSBoYXZlIGEgcG9zaXRpdmUsIHNpZ25pZmljYW50IGVmZmVjdCBvZiBGcmllbmRzIGFuZCBubyBlZmZlY3Qgb2YgR1BBLg0KDQojIyBXaGF0J3MgdGhlIHByb2JsZW0/DQoNClJlbWVtYmVyISBBbHRob3VnaCBJIGFtIHRlbGxpbmcgeW91IHRoYXQgc29tZSBzY2hvb2xzIGFyZSByaWNoIGFuZCBzb21lIGFyZSBwb29yLCB5b3UgbWF5IG5vdCBrbm93IHRoaXMgaW5mb3JtYXRpb24gd2hlbiB5b3UgY29tZSBpbiBhbmQgdHJ5IHRvIGRvIHRoZSBhbmFseXNpcy4gSWYgd2Uga25ldyB0aGlzIGluZm9ybWF0aW9uLCB3ZSBjb3VsZCBwdXQgU0VTIGFzIGFub3RoZXIgTGV2ZWwgMiBwcmVkaWN0b3IuIEFsc28sIEkgYW0gb25seSB1c2luZyBzaXggc2Nob29scyB0byBkZW1vbnN0cmF0ZSwgYnV0IGluIGEgbW9yZSAibm9ybWFsIiBjYXNlIG9mIG5lc3RlZCBkZXNpZ24gdGhlcmUgbWF5IGJlIHdheSB0b28gbWFueSBzY2hvb2xzIHRvIHRyZWF0IFNFUyBhcyBhIGZpeGVkIGZhY3RvciBpbiB0aGUgZGVzaWduLg0KDQpUaGUgcmVndWxhciByZWdyZXNzaW9uIGRpZCBub3QgcmVmbGVjdCB3aGF0IHdhcyBoYXBwZW5pbmcgaW4gZWFjaCBzY2hvb2wgdHlwZS4gSXQgZ2F2ZSB1cyBhIHBvc2l0aXZlIGVmZmVjdCAoRnJpZW5kcykgd2hlbiBvbmx5IHBvb3Igc2Nob29scyBoYWQgYSBwb3NpdGl2ZSBlZmZlY3QuIENvbGxhcHNpbmcgYWNyb3NzIHNjaG9vbCB0eXBlcyBpbiB0aGlzIGNhc2Ugd2FzIG5vdCBpZGVhbCBiZWNhdXNlIGRpZmZlcmVudCB0aGluZ3Mgd2VyZSBoYXBwZW5pbmcgd2l0aGluIGVhY2ggc2Nob29sIHR5cGUsIGNvbXByb21pc2luZyB0aGUgZ2VuZXJhbGl6YWJpbGl0eSBvZiB0aGUgZmluZGluZ3MuDQoNCkFub3RoZXIgd2F5IHRvIHB1dCB0aGlzLCB0aGUgcmVndWxhciAobG0pIHJlZ3Jlc3Npb24gaW5kaWNhdGVzIHRoYXQgdGhlIG1vcmUgZnJpZW5kcyBhIHN0dWRlbnQgaGFzLCB0aGUgaGFwcGllciB0aGV5IGFyZSwgYnV0IGxvb2tpbmcgY2xvc2VyIHRoaXMgaXMgbm90IHRoZSBjYXNlIGluIGFsbCBzY2hvb2xzIChhbmQgaXMsIGluIGZhY3QsIHRoZSBvcHBvc2l0ZSBpbiBzb21lKS4gSWYgeW91IHdlcmUgdHJ5aW5nIHRvIGdlbmVyYWxpemUgeW91ciBmaW5kaW5ncyBvciB1c2UgdGhlbSB0byBhcmd1ZSBmb3Ivc2hvdyBhIG5lZWQgZm9yIGFuIGludGVydmVudGlvbiwgeW91ciByZXN1bHRzIHdvdWxkIGJlIG1pc2xlYWRpbmcgYW5kIGNvdWxkIGNhdXNlIHByb2JsZW1zLiANCg0KU08sDQpMZXQncyB0cnkgcnVubmluZyB0aGlzIGFzIGEgbXVsdGlsZXZlbCwgcmFuZG9tIGVmZmVjdHMgbW9kZWw6DQoNCg0KIyBSYW5kb20gRWZmZWN0cyBNb2RlbA0KDQpVc2luZyBhIG11bHRpLWxldmVsIG1vZGVsIGFsbG93cyB1cyB0byBzZXBhcmF0ZSB0aGUgd2l0aGluLWdyb3VwIGVmZmVjdHMgZnJvbSB0aGUgYmV0d2Vlbi1ncm91cCBlZmZlY3RzLCB3aGVyZWFzIHJlZ3VsYXIgcmVncmVzc2lvbiBibGVuZHMgdGhlbSB0b2dldGhlciBpbnRvIGEgc2luZ2xlIGNvZWZmaWNpZW50LiANCg0KDQojIyBSdW5uaW5nIHRoZSBSYW5kb20gRWZmZWN0cyBNb2RlbCANCg0KDQojIyMgRmlyc3QsIHdlIHJ1biB0aGUgbnVsbCBtb2RlbC4NCg0KYGBge3J9DQpOdWxsPC1sbWVyKEhhcHBpbmVzcyB+IDEgIyBUaGlzIHNpbXBseSBtZWFucyBIYXBwaW5lc3MgcHJlZGljdGVkIGJ5IHRoZSBpbnRlcmNlcHQNCiAgICAgICAgICAgICAgICAgICsoMXxTY2hvb2wpLCAjIGVhY2ggc2Nob29sIGdldHMgaXRzIG93biBpbnRlcmNlcHQgDQogICAgICAgICAgICAgICAgICBkYXRhPUFsbC5TY2hvb2xzLkRhdGEsIFJFTUwgPSBGQUxTRSkNCnN1bW1hcnkoTnVsbCkNCmBgYA0KDQpXZSBleGFtaW5lIHRoZSBpbnRyYS1jbGFzcyBjb3JyZWxhdGlvbiAoSUNDKSB0byBkZXRlcm1pbmUgaWYgbXVsdGktbGV2ZWwgbW9kZWxpbmcgaXMgdGhlIGNvcnJlY3QgY2hvaWNlIGZvciBvdXIgYW5hbHlzaXMuIFRoZSBJQ0MgbWVhc3VyZXMgdGhlIGRlZ3JlZSBvZiBjbHVzdGVyaW5nIGluIG91ciBkYXRhIGFuZCBhbnN3ZXJzIHRoZSBxdWVzdGlvbiwgIkhvdyBtdWNoIGRvZXMgbXkgTGV2ZWwgMiBwcmVkaWN0IHRoZSB0b3RhbCB2YXJpYW5jZSBvZiBteSBzdHVkeT8iIElmIHlvdXIgSUNDIGlzIGdyZWF0ZXIgdGhhbiAwLCB5b3UgaGF2ZSBhIG11bHRpLWxldmVsIHN0dWR5LiANCg0KYGBge3J9DQpJQ0MuTW9kZWw8LWZ1bmN0aW9uKE1vZGVsLk5hbWUpIHsNCiAgdGF1Lk51bGw8LWFzLm51bWVyaWMobGFwcGx5KHN1bW1hcnkoTW9kZWwuTmFtZSkkdmFyY29yLCBkaWFnKSkNCiAgc2lnbWEuTnVsbCA8LSBhcy5udW1lcmljKGF0dHIoc3VtbWFyeShNb2RlbC5OYW1lKSR2YXJjb3IsICJzYyIpXjIpDQogIElDQy5OdWxsIDwtIHRhdS5OdWxsLyh0YXUuTnVsbCtzaWdtYS5OdWxsKQ0KICByZXR1cm4oSUNDLk51bGwpDQp9DQoNCklDQy5Nb2RlbChOdWxsKQ0KYGBgDQoNCk91ciBJQ0MgaXMgZ3JlYXRlciB0aGFuIDAsIG1lYW5pbmcgd2Ugd2VyZSBjb3JyZWN0IHRvIHRoaW5rIG9mIHRoaXMgYXMgYSBtdWx0aS1sZXZlbCBwcm9ibGVtLg0KDQoNCiMjIyBOZXh0LCB3ZSBydW4gdGhlIExldmVsIDEgcHJlZGljdG9yIChHUEEpLg0KDQpgYGB7cn0NClRoZS5Nb2RlbC4xPC1sbWVyKEhhcHBpbmVzcyB+IEdQQS5DIA0KICAgICAgICAgICAgICAgICAgKygxfFNjaG9vbCksDQogICAgICAgICAgICAgICAgZGF0YT1BbGwuU2Nob29scy5EYXRhLCBSRU1MID0gRkFMU0UpDQpzdW1tYXJ5KFRoZS5Nb2RlbC4xKQ0KYGBgDQoNClRoZSByZXN1bHRzIGluZGljYXRlIHRoYXQgYSBzdHVkZW50J3MgR1BBIGRvZXMgbm90IGhhdmUgYW4gZWZmZWN0IG9uIHRoZWlyIEhhcHBpbmVzcyB3aGVuIGNvbnRyb2xsaW5nIGZvciBMZXZlbCAyIGZsdWN0dWF0aW9ucyBpbiBIYXBwaW5lc3MuIA0KDQoNCiMjIyBGaW5hbGx5LCB3ZSBydW4gdGhlIExldmVsIDIgcHJlZGljdG9yIChGcmllbmRzKS4NCg0KVGhpcyBtb2RlbCBhbGxvd3MgdGhlIHZhcmlhYmxlIEZyaWVuZHMgdG8gdmFyeSBiZXR3ZWVuIHNjaG9vbHMuDQoNCg0KYGBge3J9DQpUaGUuTW9kZWwuMjwtbG1lcihIYXBwaW5lc3MgfiBGcmllbmRzLkMgKyBHUEEuQyANCiAgICAgICAgICAgICAgICAgICsoMStGcmllbmRzLkN8U2Nob29sKSwgIyBlYWNoIHNjaG9vbCBnZXRzIGl0cyBvd24gaW50ZXJjZXB0LCBhbmQgRnJpZW5kcyBjYW4gdmFyeSBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBzY2hvb2wuDQogICAgICAgICAgICAgICAgZGF0YT1BbGwuU2Nob29scy5EYXRhLCBSRU1MID0gRkFMU0UpDQpzdW1tYXJ5KFRoZS5Nb2RlbC4yKQ0KYGBgDQoNClRoZSByZXN1bHRzIGluZGljYXRlIHRoYXQgdGhlIG51bWJlciBvZiBGcmllbmRzIGEgc3R1ZGVudCBoYXMgZG9lcyBub3QgaGF2ZSBhbiBlZmZlY3Qgb24gSGFwcGluZXNzIHdoZW4gY29udHJvbGxpbmcgZm9yIHRoZSByYW5kb20gZWZmZWN0cyBvZiBMZXZlbCAyIGluZmx1ZW5jZXMuDQoNCg0KIyMjIFdoYXQgZG8gdGhlIHJlc3VsdHMgbWVhbj8NCg0KSW4gb3VyIHJlZ3VsYXIgKGxtKSByZWdyZXNzaW9uLCBGcmllbmRzIGhhZCBhIHNpZ25pZmljYW50IGVmZmVjdCwgYiA9IDEuNDAgKHAgPCAuMDAxKS4gSG93ZXZlciwgaW4gb3VyIG1peGVkIChsbWVyKSByZWdyZXNzaW9uLCBGcmllbmRzIGhhZCBhIGxhcmdlciAoMi4xNiksIGJ1dCBub24tc2lnbmlmaWNhbnQgZWZmZWN0Lg0KDQpXaHkgaXMgdGhpcyBpbXBvcnRhbnQ/IFRoZSBnb2FsIG9mIG11bHRpLWxldmVsIG1vZGVsaW5nIGlzIHRvIGRyYXcgYSBjb25jbHVzaW9uIGFib3V0IHRoZSBnZW5lcmFsIHNhbXBsZSB0aGF0IHlvdSBoYXZlIHdoaWxlIGNvbnRyb2xsaW5nIGZvciBkaWZmZXJlbmNlcyB5b3UgYXJlIG5vdCB0cnlpbmcgdG8gZXhwbGFpbiAoaW4gdGhpcyBleGFtcGxlLCByaWNoIHZzLiBwb29yKS4gTm90IHByb3Blcmx5IGNvbnRyb2xsaW5nIGZvciB0aGVzZSBkaWZmZXJlbmNlcywgd2hpY2ggeW91IG1heSBvZnRlbiBub3Qga25vdyBhcmUgdGhlcmUsIHdpbGwgaW5jcmVhc2UgeW91ciBjaGFuY2Ugb2YgVHlwZSBJIGVycm9yLiBCZWNhdXNlIHRoZSBlZmZlY3Qgb2YgRnJpZW5kcyB3YXMgZGlmZmVyZW50IGluIGRpZmZlcmVudCBzY2hvb2xzLCBpdCBtYWtlcyBzZW5zZSB0aGF0IHRoZSBtdWx0aS1sZXZlbCBtb2RlbCAoTUxNKSBkaWQgbm90IHNob3cgYSBzaWduaWZpY2FudCBlZmZlY3QuIEluIHRoZSBwcmVzZW50IGV4YW1wbGUsIG91ciBNTE0gZ2F2ZSB1cyBhIG1vcmUgYWNjdXJhdGUgaW50ZXJwcmV0YXRpb24gLSB0aGF0IG5vIG1haW4gZWZmZWN0IG9mIEZyaWVuZHMgZXhpc3RlZCBmb3IgYWxsIHNjaG9vbHMgZ2VuZXJhbGx5LiANCg0KDQojIyBDaGVja2luZyB5b3VyIEFzc3VtcHRpb25zDQoNCkZvciBhIGNvbXByZWhlbnNpdmUgbG9vayBhdCBob3cgdG8gcnVuIGRpYWdub3N0aWNzLCBwbGVhc2Ugc2VlIFtDaGFwdGVyIDEyXShodHRwOi8vYWRlbW9zLnBlb3BsZS51aWMuZWR1L0NoYXB0ZXIxMi5odG1sKSBhbmQgW0NoYXB0ZXIgMThdKGh0dHA6Ly9hZGVtb3MucGVvcGxlLnVpYy5lZHUvQ2hhcHRlcjE4Lmh0bWwpICANCg0KDQojIFAtVmFsdWVzDQoNCipQKi12YWx1ZXMgYXJlIGhvdGx5IGRpc3B1dGVkIGluIE1peGVkIE1vZGVscy4gDQoNCk9uZSBzb2x1dGlvbiBpcyB0byBlc3RpbWF0ZSBkZWdyZWVzIG9mIGZyZWVkb20gdG8gZ2V0IHAtdmFsdWVzLiBTQVMvU1BTUyB1c2VzIFNhdHRlcnRod2FpdGUgYXBwcm94aW1hdGlvbnMuIFRoZXJlIGFyZSBhbHNvIEtlbndhcmQtUm9nZXIgYXBwcm94aW1hdGlvbnMgKHNlZSBXZXN0ZmFsbCwgS2VubnksICYgSnVkZCwgMjAxNCkuIEVpdGhlciBhcmUgYWNjZXB0YWJsZS4gTWFraW5nIGEgbnVtYmVyIG9mIGFzc3VtcHRpb25zLCB0aGVzZSBvdXRwdXRzIHdpbGwgbWlycm9yIHRoZSByZXN1bHRzIG9mIGEgdHJhZGl0aW9uYWwgQU5PVkEgZmFpcmx5IGNsb3NlbHkuIA0KDQppbnN0YWxsLnBhY2thZ2VzKCJsbWVyVGVzdCIpDQoNClRoaXMgaXMgYSBtaXhlZCBtb2RlbCBhZGQtb24gcGFja2FnZSB0byBjYWxjdWxhdGUgcC12YWx1ZXMgYmFzZWQgb24gYSBjZXJ0YWluIHNldCBvZiBhc3N1bXB0aW9ucy4NCkVhY2ggZGRmIGlzIGEgZGlmZmVyZW50IG1ldGhvZCBvZiBhdHRhaW5pbmcgcC12YWx1ZXMsIHNvIHlvdSBjYW4gY2hvb3NlIHdoaWNoIHRvIHJ1bi4gSSBnaXZlIHlvdSB0aHJlZSBleGFtcGxlcyBiZWxvdy4gDQp5b3Ugd2lsbCBuZWVkIHRvIHJlZml0IHRoZSBtb2RlbHMgd2l0aCB0aGUgcGFja2FnZSBgbG1lclRlc3RgIGluc3RhbGxlZCBhbmQgbG9hZGVkLg0KDQpgYGB7cn0NCmxpYnJhcnkobG1lclRlc3QpDQpUaGUuTW9kZWwuMjwtbG1lcihIYXBwaW5lc3MgfiBGcmllbmRzLkMgKyBHUEEuQyANCiAgICAgICAgICAgICAgICAgICsoMStGcmllbmRzLkN8U2Nob29sKSwNCiAgICAgICAgICAgICAgICBkYXRhPUFsbC5TY2hvb2xzLkRhdGEsIFJFTUwgPSBGQUxTRSkNCg0Kc3VtbWFyeShUaGUuTW9kZWwuMixkZGYgPSAibG1lNCIpDQpzdW1tYXJ5KFRoZS5Nb2RlbC4yLCBkZGYgPSAiU2F0dGVydGh3YWl0ZSIpICMgU0FTIG1ldGhvZA0KYGBgDQoNCklmIHlvdSB3YW50IHRvIHJlcG9ydCBtYWluIGVmZmVjdHMgYW5kIGludGVyYWN0aW9ucyBpbnN0ZWFkIG9mIHNsb3BlcywgeW91IG1pZ2h0IGNvbnZlcnQgeW91ciBtaXhlZCBtb2RlbCBpbnRvIGFuIEFOT1ZBLg0KDQpgYGB7cn0NCmFub3ZhKFRoZS5Nb2RlbC4yLGRkZiA9ICJLZW53YXJkLVJvZ2VyIikgIyBLZW5ueSBldCBhbCBzdWdnZXN0ZWQgbWV0aG9kDQpgYGANCg0KIyBSZWZlcmVuY2VzDQoNCkh1dGEsIFYuICgyMDE0KS4gV2hlbiB0byB1c2UgaGllcmFyY2hpY2FsIGxpbmVhciBtb2RlbGluZy4gVGhlIFF1YW50aXRhdGl2ZSBNZXRob2RzIGZvciBQc3ljaG9sb2d5LCAxMCgxKTogMTMtIDI4Lg0KDQpXZXN0ZmFsbCwgSi4sIEtlbm55LCBELiBBLiwgJiBKdWRkLCBDLiBNLiAoMjAxNCkuIFN0YXRpc3RpY2FsIHBvd2VyIGFuZCBvcHRpbWFsIGRlc2lnbiBpbiBleHBlcmltZW50cyBpbiB3aGljaCBzYW1wbGVzIG9mIHBhcnRpY2lwYW50cyByZXNwb25kIHRvIHNhbXBsZXMgb2Ygc3RpbXVsaS4gSm91cm5hbCBvZiBFeHBlcmltZW50YWwgUHN5Y2hvbG9neTogR2VuZXJhbCwgMTQzKDUpLCAyMDIwLTIwNDUuDQoNCldvbHRtYW4sIEguLCBGZWxkc3RhaW4sIEEuLCBNYWNLYXksIEouIEMuLCAmIFJvY2NoaSwgTS4gKDIwMTIpLiBBbiBpbnRyb2R1Y3Rpb24gdG8gaGllcmFyY2hpY2FsIGxpbmVhciBtb2RlbGluZy4gVHV0b3JpYWxzIGluIFF1YW50aXRhdGl2ZSBNZXRob2RzIGZvciBQc3ljaG9sb2d5LCA4KDEpOiA1Mi02OS4NCg0KDQo8c2NyaXB0Pg0KICAoZnVuY3Rpb24oaSxzLG8sZyxyLGEsbSl7aVsnR29vZ2xlQW5hbHl0aWNzT2JqZWN0J109cjtpW3JdPWlbcl18fGZ1bmN0aW9uKCl7DQogIChpW3JdLnE9aVtyXS5xfHxbXSkucHVzaChhcmd1bWVudHMpfSxpW3JdLmw9MSpuZXcgRGF0ZSgpO2E9cy5jcmVhdGVFbGVtZW50KG8pLA0KICBtPXMuZ2V0RWxlbWVudHNCeVRhZ05hbWUobylbMF07YS5hc3luYz0xO2Euc3JjPWc7bS5wYXJlbnROb2RlLmluc2VydEJlZm9yZShhLG0pDQogIH0pKHdpbmRvdyxkb2N1bWVudCwnc2NyaXB0JywnaHR0cHM6Ly93d3cuZ29vZ2xlLWFuYWx5dGljcy5jb20vYW5hbHl0aWNzLmpzJywnZ2EnKTsNCg0KICBnYSgnY3JlYXRlJywgJ1VBLTk4ODc4NzkzLTEnLCAnYXV0bycpOw0KICBnYSgnc2VuZCcsICdwYWdldmlldycpOw0KDQo8L3NjcmlwdD4NCg0K