1 Loading packages

## Use the code below to check if you have all required packages installed. If some are not installed already, the code below will install these. If you have all packages installed, then you could load them with the second code.
requiredPackages = c('tidyverse', 'ordinal', 'broom', 'emmeans', 'knitr', 'Hmisc', 'corrplot', 'psycho', 'PresenceAbsence', 'lme4', 'lmerTest')
for(p in requiredPackages){
  if(!require(p,character.only = TRUE)) install.packages(p)
  library(p,character.only = TRUE)
}

In this session, we will look at basic functions in R that will help us in running some inferential statistics. These will help us to evaluate the relationship between one (or more) predictor(s) (independent variable) and an outcome (dependent variable).

It is important to know the class of the outcome before doing any pre-data analyses or inferential statistics. Outcome classes can be one of:

  1. Numeric: As an example, we have length/width of leaf; height of mountain; fundamental frequency of the voice; etc. These are true numbers and we can use summaries, t-tests, linear models, etc.

  2. Categorical (Unordered): Observations for two or more categories. As an example, we can have gender of a speaker (male or female); responses to a True vs False perception tests; Colour (unordered) categorisation, e.g., red, blue, yellow, orange, etc.. For these we can use a Generalised Linear Model (binomial or multinomial) or a simple chi-square test. Count data are numbers related to a category. But these should be analysed using a poisson logistic regression

  3. Categorical (Ordered): When you run a rating experiment, where the outcome is either numeric (i.e., 1, 2, 3, 4, 5) or categories (i.e., disagree, neutral, agree). The numeric option is NOT a true number as for the participant, these are categories. Cumulative Logit models (or Generalised Linear Model with a cumulative function) are used. The mean is meaningless here, and the median is a preferred descriptive statistic.

The content will be split into 5 sections. Some of you will only ever look at some of these, while others may need to use all. Look at the section that works best for your data.

  1. Pre-data analyses: We will look at summaries, plotting, correlations, checking normality of distribution and homogeneity of variance (for t-tests)

  2. Statistical Analyses: from t-test and ANOVA, to Linear Models: model estimation, understanding coefficients, residuals, and predictions

  3. We then move to Generalised Linear Models, with Logistic Regression (with a binomial categorical outcome) and touch upon Signal Detection Theory for estimating accuracy, biases, with sensitivity and specificity measures.

  4. Dealing with rating data using Generalised Linear Models, with a Cumulative Logit function

  5. Linear Mixed effects Models: Introduction to random effects and how to deal with these.

2 Pre-data analsyes

2.1 Built-in datasets

We will use one of the built in R. You can check all available datasets in R using the following:

data()
# or below for all datasets available in all installed packages
data(package = .packages(all.available = TRUE))

We will use the iris dataset from the package MASS

2.2 Checking structure and summaries

2.2.1 Structure

iris %>% 
  str()  
'data.frame':   150 obs. of  5 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

We have a dataframe with 150 observations and 5 variables; 4 numeric and 1 factor with 3 levels.

2.2.2 Summary

We summarise the data to see the trends:

iris %>% 
  summary()
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300   versicolor:50  
 Median :5.800   Median :3.000   Median :4.350   Median :1.300   virginica :50  
 Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199                  
 3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800                  
 Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500                  

So we have an equal dataframe (50 observations under each level of the factor Species), with no missing values (aka NA).

2.2.3 Advanced

2.2.3.1 For a specific variable

What if you want to summarise the data and get the mean, SD, by each level of the factor for Sepal.Length?

iris %>% 
  group_by(Species) %>% 
  summarise(
  SL.Mean = mean(Sepal.Length),
  SL.SD = sd(Sepal.Length)
  )

2.2.3.2 For all variables

iris %>% 
  group_by(Species) %>% 
  summarise_all(list(mean = mean, sd = sd)
  )

2.2.4 Up to you

Do some additional summaries.. You may want to check the median, range, etc..

2.3 Plot

We can make a boxplot of the data and add a trend line. This allows us to visualise the median, and quantiles in addition to the standard deviation and any outliers… All in the same plot!

iris %>% 
  ggplot(aes(x = Species, y = Sepal.Length)) +
  geom_boxplot() +
  geom_smooth(aes(x = as.numeric(Species), y = Sepal.Length), method = "lm") +
  labs(x = "Species", y = "Length", title = "Boxplot and trend line", subtitle = "with ggplot2") + 
  theme_bw() + theme(text = element_text(size = 15))

Here I have used the variable Sepal.Length. You can use any of the additional variables to plot the data.

2.4 Correlation tests

2.4.1 Basic correlations

We use the function cor to obtain the pearson correlation and cor.test to run a basic correlation test on our data with significance testing

cor(iris$Sepal.Length, iris$Petal.Length, method = "pearson")
[1] 0.8717538
cor.test(iris$Sepal.Length, iris$Petal.Length)

    Pearson's product-moment correlation

data:  iris$Sepal.Length and iris$Petal.Length
t = 21.646, df = 148, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8270363 0.9055080
sample estimates:
      cor 
0.8717538 

2.4.1.1 Up to you

Can you check whether there is a correlation between the Sepal.Length and the Petal.Width? What about Petal.Length and Petal.Width?

2.4.2 Using the package corrplot

Above, we did a correlation test on two predictors. We run multiple correlations on multiple predictors.

2.4.2.1 Correlations

## correlation using "corrplot"
## based on the function `rcorr' from the `Hmisc` package
## Need to change dataframe into a matrix
corr <- as.matrix(iris[-5]) %>% 
  rcorr(type="pearson")
print(corr)
             Sepal.Length Sepal.Width Petal.Length Petal.Width
Sepal.Length         1.00       -0.12         0.87        0.82
Sepal.Width         -0.12        1.00        -0.43       -0.37
Petal.Length         0.87       -0.43         1.00        0.96
Petal.Width          0.82       -0.37         0.96        1.00

n= 150 


P
             Sepal.Length Sepal.Width Petal.Length Petal.Width
Sepal.Length              0.1519      0.0000       0.0000     
Sepal.Width  0.1519                   0.0000       0.0000     
Petal.Length 0.0000       0.0000                   0.0000     
Petal.Width  0.0000       0.0000      0.0000                  
corrplot(corr$r, p.mat = corr$P,
         addCoef.col = "black", diag = FALSE, type = "upper", tl.srt = 55)

2.4.2.2 Up to you

Look into the corrplot specification, using ?corrplot and amend some of the criteria. Run a correlation plot while filtering the data according to the species.

# hint 
# create a new dataframe by filtering `Species`, then compute correlations and plot
  

Up to now, we have done some basic summaries and checked the correlations in the data. The pearson correlations we have done provided us with significance levels related to the correlations between two numeric outcomes. We continue by examining the normality of distribution of our data.

2.5 Normality of distribution

2.5.1 Subsetting data

In the iris dataset, we have a categorical predictor: Species which has three levels

levels(iris$Species)
[1] "setosa"     "versicolor" "virginica" 

Let’s subset the data to setosa and versicolor. We will also check the normality and homogeneity of variance in the data

irisSub <- iris %>% 
  filter(Species %in% c("setosa", "versicolor"))

2.5.2 Shapiro test

To check normality of distribution, we use the shapiro.test on the numeric outcome. Given that our predictor now has two levels. We need to subset the data again to check normality of the outcome Sepal.Length for each level of our factor Species

irisSubSet <- iris %>% 
  filter(Species == "setosa")
irisSubVers <- iris %>% 
  filter(Species == "versicolor")
  
shapiro.test(irisSubSet$Sepal.Length)

    Shapiro-Wilk normality test

data:  irisSubSet$Sepal.Length
W = 0.9777, p-value = 0.4595
shapiro.test(irisSubVers$Sepal.Length)

    Shapiro-Wilk normality test

data:  irisSubVers$Sepal.Length
W = 0.97784, p-value = 0.4647

How to interpret this non-statistically significant result? This tells us that the distribution of the data is not statistically different from a normal distribution.

2.5.3 Density plot

We can also use a density plot to evaluate normality. The results show that both levels have bell shaped distributions.

irisSub %>% 
  ggplot(aes(x = Sepal.Length))+
  geom_density()+
  facet_wrap(~Species, scales = "free_x")

2.5.4 Homogeneity of variance

Because our data is normally distributed, we can use the bartlett test. If our data were non-normally distributed, we would use the Levene Test (either with var.test from base-R or leveneTest from the car package We can check both.

2.5.4.1 Bartlett test

irisSub %>% 
  bartlett.test(Sepal.Length ~ Species, data = .)

    Bartlett test of homogeneity of variances

data:  Sepal.Length by Species
Bartlett's K-squared = 6.8917, df = 1, p-value = 0.00866

2.5.4.2 Levene test

irisSub %>% 
  var.test(Sepal.Length ~ Species, data = .)

    F test to compare two variances

data:  Sepal.Length by Species
F = 0.46634, num df = 49, denom df = 49, p-value = 0.008657
alternative hypothesis: true ratio of variances is not equal to 1
95 percent confidence interval:
 0.2646385 0.8217841
sample estimates:
ratio of variances 
         0.4663429 
irisSub %>% 
  car::leveneTest(Sepal.Length ~ Species, data = .)
Levene's Test for Homogeneity of Variance (center = median)
      Df F value   Pr(>F)   
group  1  8.1727 0.005196 **
      98                    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

In all cases, the statistically significant result indicates that there is evidence that the variance of two levels of the factor Species is statistically significant; i.e., the variances are not equal. This is important as we will use this in our t-test later on

3 Linear Models

Up to now, we have looked at descriptive statistics, and evaluated summaries, correlations in the data (with p values), and checked the normality of distribution of our data.

We are now interested in looking at group differences.

Let us start with a simple t-test

3.1 First steps

3.1.1 T-test

We then run a t-test on the data. We specify the formula as y ~ x and add var.equal = FALSE (based on the normality tests above)

irisSub %>% 
  t.test(Sepal.Length ~ Species, data = ., var.equal = FALSE)

    Welch Two Sample t-test

data:  Sepal.Length by Species
t = -10.521, df = 86.538, p-value < 2.2e-16
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -1.1057074 -0.7542926
sample estimates:
    mean in group setosa mean in group versicolor 
                   5.006                    5.936 

To interpret the t-test, we say that there is evidence for a statistically significant difference between the two groups: setosa and versicolor: t(86) = -10.521, p < 2.2e-16. The mean of setosa is significantly lower than that of versicolor.

3.1.2 Linear Model

Let us run a linear model on the same data

irisSub %>% 
  lm(Sepal.Length ~ Species, data = .) %>% 
  summary()

Call:
lm(formula = Sepal.Length ~ Species, data = .)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.0360 -0.3135 -0.0060  0.2715  1.0640 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)        5.00600    0.06250   80.09   <2e-16 ***
Speciesversicolor  0.93000    0.08839   10.52   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.442 on 98 degrees of freedom
Multiple R-squared:  0.5304,    Adjusted R-squared:  0.5256 
F-statistic: 110.7 on 1 and 98 DF,  p-value: < 2.2e-16

Any comments? discuss with your neighbour.

The results of the linear model are exactly the same, albeit with a difference in the sign of the difference. This indicates that the \(\beta\) coefficient for versicolor is significantly higher than that of setosa by 0.93000: t(98) = 10.52, p < <2e-16.

The dataset iris contains three species. We will run an ANOVA and a linear model on the data.

3.1.3 Basic ANOVA

We can use the function aov to run an Analysis of Variance on the full dataset

iris %>% 
  aov(Sepal.Length ~ Species, data = .) %>% 
  summary()
             Df Sum Sq Mean Sq F value Pr(>F)    
Species       2  63.21  31.606   119.3 <2e-16 ***
Residuals   147  38.96   0.265                   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

3.1.4 Linear model

We can use the function lm to run a linear model

iris %>% 
  lm(Sepal.Length ~ Species, data = .) %>% 
  summary()

Call:
lm(formula = Sepal.Length ~ Species, data = .)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.6880 -0.3285 -0.0060  0.3120  1.3120 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)         5.0060     0.0728  68.762  < 2e-16 ***
Speciesversicolor   0.9300     0.1030   9.033 8.77e-16 ***
Speciesvirginica    1.5820     0.1030  15.366  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5148 on 147 degrees of freedom
Multiple R-squared:  0.6187,    Adjusted R-squared:  0.6135 
F-statistic: 119.3 on 2 and 147 DF,  p-value: < 2.2e-16

But wait… How is the linear model comparable to the analysis of variance we ran above? This linear model derives the analysis of variance we saw above, use anova on your linear model..

Here are the results of the initial Analysis of variance:

iris %>% 
  aov(Sepal.Length ~ Species, data = .) %>% 
  summary()
             Df Sum Sq Mean Sq F value Pr(>F)    
Species       2  63.21  31.606   119.3 <2e-16 ***
Residuals   147  38.96   0.265                   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

And here are the results of the linear model with the anova function

iris %>% 
  lm(Sepal.Length ~ Species, data = .) %>% 
  anova()
Analysis of Variance Table

Response: Sepal.Length
           Df Sum Sq Mean Sq F value    Pr(>F)    
Species     2 63.212  31.606  119.26 < 2.2e-16 ***
Residuals 147 38.956   0.265                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

They are exactly the same… The underlying of an Analysis of variance is a linear model. We will continue with a linear model to understand it better

3.2 Linear Model

The basic assumption of a Linear model is to create a regression analysis on the data. We have an outcome (or dependent variable) and a predictor (or an independent variable). The formula of a linear model is as follows outcome ~ predictor that can be read as “outcome as a function of the predictor”. We can add “1” to specify an intercept, but this is by default added to the model

3.2.1 Model estimation

mdl.lm <- iris %>% 
  lm(Sepal.Length ~ Species, data = .)
# same as below.
#mdl.lm <- lm(Sepal.Length ~ 1 + Species, data = iris)
mdl.lm #also print(mdl.lm)

Call:
lm(formula = Sepal.Length ~ Species, data = .)

Coefficients:
      (Intercept)  Speciesversicolor   Speciesvirginica  
            5.006              0.930              1.582  
summary(mdl.lm)

Call:
lm(formula = Sepal.Length ~ Species, data = .)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.6880 -0.3285 -0.0060  0.3120  1.3120 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)         5.0060     0.0728  68.762  < 2e-16 ***
Speciesversicolor   0.9300     0.1030   9.033 8.77e-16 ***
Speciesvirginica    1.5820     0.1030  15.366  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5148 on 147 degrees of freedom
Multiple R-squared:  0.6187,    Adjusted R-squared:  0.6135 
F-statistic: 119.3 on 2 and 147 DF,  p-value: < 2.2e-16

3.2.2 Tidying the output

# from library(broom)
tidy(mdl.lm) %>% 
  select(term, estimate) %>% 
  mutate(estimate = round(estimate, 3))
mycoefE <- tidy(mdl.lm) %>% pull(estimate)

To interpret the model, we need look at the coefficients. The Intercept (=Setosa) is 5.006 and the coefficients for Versicolor and for Virginica are respectively 5.936 and 6.588 This tells us that compared to Setosa, moving from this category to Versicolor leads to a significant increase by 5.936, and for Virginica, there is a significant increase by 6.588.

3.2.3 Obtaining our “true” coefficients

But where are our actual values based on the means in the table above?

We run a model that suppresses the intercept (i.e., adding 0 instead of 1) and this will allow us to obtain the “true” coefficients for each level of our predictor. This is also known as a saturated model

mdl.lm.2 <- iris %>% 
  lm(Sepal.Length ~ 0 + Species, data = .)
summary(mdl.lm.2)

Call:
lm(formula = Sepal.Length ~ 0 + Species, data = .)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.6880 -0.3285 -0.0060  0.3120  1.3120 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
Speciessetosa       5.0060     0.0728   68.76   <2e-16 ***
Speciesversicolor   5.9360     0.0728   81.54   <2e-16 ***
Speciesvirginica    6.5880     0.0728   90.49   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5148 on 147 degrees of freedom
Multiple R-squared:  0.9925,    Adjusted R-squared:  0.9924 
F-statistic:  6522 on 3 and 147 DF,  p-value: < 2.2e-16

This matches the original data. Setosa has a mean of 5.006, Versicolor 10.942, and Virginica 10.942. See table above and coefficients below

#Setosa
mycoefE[1]
[1] 5.006
#Versicolor
mycoefE[1] + mycoefE[2]
[1] 5.936
#Virginica
mycoefE[1] + mycoefE[3]
[1] 6.588

The same as

tidy(mdl.lm.2) %>% 
  select(term, estimate) %>% 
  mutate(estimate = round(estimate, 3))
mycoefE <- tidy(mdl.lm.2) %>%
  pull(estimate)

#Setosa
mycoefE[1]
[1] 5.006
#Versicolor
mycoefE[2]
[1] 5.936
#Virginica
mycoefE[3]
[1] 6.588

3.2.4 Nice table of our model summary

We can also obtain a nice table of our model summary. We can use the package knitr or xtable

3.2.4.1 Directly from model summary

kable(summary(mdl.lm)$coef, digits = 3)

Estimate Std. Error t value Pr(>|t|)
(Intercept) 5.006 0.073 68.762 0
Speciesversicolor 0.930 0.103 9.033 0
Speciesvirginica 1.582 0.103 15.366 0

NA

3.2.4.2 From the tidy output

mdl.lmT <- tidy(mdl.lm)
kable(mdl.lmT, digits = 3)
term estimate std.error statistic p.value
(Intercept) 5.006 0.073 68.762 0
Speciesversicolor 0.930 0.103 9.033 0
Speciesvirginica 1.582 0.103 15.366 0

3.2.5 Dissecting the model

Let us dissect the model. If you use “str”, you will be able to see what is available under our linear model. To access some info from the model

3.2.5.1 “str” and “coef”

str(mdl.lm)
List of 13
 $ coefficients : Named num [1:3] 5.01 0.93 1.58
  ..- attr(*, "names")= chr [1:3] "(Intercept)" "Speciesversicolor" "Speciesvirginica"
 $ residuals    : Named num [1:150] 0.094 -0.106 -0.306 -0.406 -0.006 ...
  ..- attr(*, "names")= chr [1:150] "1" "2" "3" "4" ...
 $ effects      : Named num [1:150] -71.5659 0.8025 7.91 -0.3826 0.0174 ...
  ..- attr(*, "names")= chr [1:150] "(Intercept)" "Speciesversicolor" "Speciesvirginica" "" ...
 $ rank         : int 3
 $ fitted.values: Named num [1:150] 5.01 5.01 5.01 5.01 5.01 ...
  ..- attr(*, "names")= chr [1:150] "1" "2" "3" "4" ...
 $ assign       : int [1:3] 0 1 1
 $ qr           :List of 5
  ..$ qr   : num [1:150, 1:3] -12.2474 0.0816 0.0816 0.0816 0.0816 ...
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : chr [1:150] "1" "2" "3" "4" ...
  .. .. ..$ : chr [1:3] "(Intercept)" "Speciesversicolor" "Speciesvirginica"
  .. ..- attr(*, "assign")= int [1:3] 0 1 1
  .. ..- attr(*, "contrasts")=List of 1
  .. .. ..$ Species: chr "contr.treatment"
  ..$ qraux: num [1:3] 1.08 1.05 1.09
  ..$ pivot: int [1:3] 1 2 3
  ..$ tol  : num 1e-07
  ..$ rank : int 3
  ..- attr(*, "class")= chr "qr"
 $ df.residual  : int 147
 $ contrasts    :List of 1
  ..$ Species: chr "contr.treatment"
 $ xlevels      :List of 1
  ..$ Species: chr [1:3] "setosa" "versicolor" "virginica"
 $ call         : language lm(formula = Sepal.Length ~ Species, data = .)
 $ terms        :Classes 'terms', 'formula'  language Sepal.Length ~ Species
  .. ..- attr(*, "variables")= language list(Sepal.Length, Species)
  .. ..- attr(*, "factors")= int [1:2, 1] 0 1
  .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. ..$ : chr [1:2] "Sepal.Length" "Species"
  .. .. .. ..$ : chr "Species"
  .. ..- attr(*, "term.labels")= chr "Species"
  .. ..- attr(*, "order")= int 1
  .. ..- attr(*, "intercept")= int 1
  .. ..- attr(*, "response")= int 1
  .. ..- attr(*, ".Environment")=<environment: 0x00000267f1c2e870> 
  .. ..- attr(*, "predvars")= language list(Sepal.Length, Species)
  .. ..- attr(*, "dataClasses")= Named chr [1:2] "numeric" "factor"
  .. .. ..- attr(*, "names")= chr [1:2] "Sepal.Length" "Species"
 $ model        :'data.frame':  150 obs. of  2 variables:
  ..$ Sepal.Length: num [1:150] 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
  ..$ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
  ..- attr(*, "terms")=Classes 'terms', 'formula'  language Sepal.Length ~ Species
  .. .. ..- attr(*, "variables")= language list(Sepal.Length, Species)
  .. .. ..- attr(*, "factors")= int [1:2, 1] 0 1
  .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. ..$ : chr [1:2] "Sepal.Length" "Species"
  .. .. .. .. ..$ : chr "Species"
  .. .. ..- attr(*, "term.labels")= chr "Species"
  .. .. ..- attr(*, "order")= int 1
  .. .. ..- attr(*, "intercept")= int 1
  .. .. ..- attr(*, "response")= int 1
  .. .. ..- attr(*, ".Environment")=<environment: 0x00000267f1c2e870> 
  .. .. ..- attr(*, "predvars")= language list(Sepal.Length, Species)
  .. .. ..- attr(*, "dataClasses")= Named chr [1:2] "numeric" "factor"
  .. .. .. ..- attr(*, "names")= chr [1:2] "Sepal.Length" "Species"
 - attr(*, "class")= chr "lm"
coef(mdl.lm)
      (Intercept) Speciesversicolor  Speciesvirginica 
            5.006             0.930             1.582 
## same as 
## mdl.lm$coefficients

3.2.5.2 “coef” and “coefficients”

What if I want to obtain the “Intercept”? Or the coefficient for distance? What if I want the full row for distance?

coef(mdl.lm)[1] # same as mdl.lm$coefficients[1]
(Intercept) 
      5.006 
coef(mdl.lm)[2] # same as mdl.lm$coefficients[2]
Speciesversicolor 
             0.93 
summary(mdl.lm)$coefficients[2, ] # full row
    Estimate   Std. Error      t value     Pr(>|t|) 
9.300000e-01 1.029579e-01 9.032819e+00 8.770194e-16 
summary(mdl.lm)$coefficients[2, 4] #for p value
[1] 8.770194e-16

3.2.5.3 Up to you

Play around with the model summary and obtain the t values for the three levels. You can do this by referring to the coefficient as above

3.2.5.4 Residuals

What about residuals (difference between the observed value and the estimated value of the quantity) and fitted values?

hist(residuals(mdl.lm))

qqnorm(residuals(mdl.lm)); qqline(residuals(mdl.lm))

plot(fitted(mdl.lm), residuals(mdl.lm), cex = 4)

3.2.5.5 Goodness of fit?

AIC(mdl.lm) # Akaike's Information Criterion, lower values are better
[1] 231.452
BIC(mdl.lm) # Bayesian AIC
[1] 243.4945
logLik(mdl.lm)  # log likelihood
'log Lik.' -111.726 (df=4)

Or use the following from broom

glance(mdl.lm)

3.2.5.6 Significance testing

Are the above informative? of course not directly. If we want to test for overall significance of model. We run a null model (aka intercept only) and compare models.

mdl.lm.Null <- iris %>% 
  lm(Sepal.Length ~ 1, data = .)
mdl.comp <- anova(mdl.lm.Null, mdl.lm)
mdl.comp
Analysis of Variance Table

Model 1: Sepal.Length ~ 1
Model 2: Sepal.Length ~ Species
  Res.Df     RSS Df Sum of Sq      F    Pr(>F)    
1    149 102.168                                  
2    147  38.956  2    63.212 119.26 < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

The results show that adding the factor “Species” improves the model fit. We can write this as follows: Model comparison showed that the addition of Species improved the model fit when compared with an intercept only model (\(F\)(2) = 119.26, p < 1.669669210^{-31})

3.2.5.7 Plotting fitted values

3.2.5.7.1 Trend line

Let’s plot our fitted values but only for the trend line

iris %>% 
  ggplot(aes(x = Species, y = Sepal.Length))+
  geom_boxplot() +
  labs(x = "Species", y = "Length", title = "Boxplot and predicted trend line", subtitle = "with ggplot2") + 
  theme_bw() + theme(text = element_text(size = 15))+
  geom_smooth(aes(x = as.numeric(Species), y = predict(mdl.lm)), method = "lm", color = "blue")

This allows us to plot the fitted values from our model with the predicted linear trend. This is exactly the same as our original data.

3.2.5.7.2 Predicted means and the trend line

We can also plot the predicted means and linear trend

iris %>% 
  ggplot(aes(x = Species, y = predict(mdl.lm)))+
  geom_boxplot(color = "blue") +
  labs(x = "Species", y = "Length", title = "Predicted means and trend line", subtitle = "with ggplot2") + 
  theme_bw() + theme(text = element_text(size = 15))+
  geom_smooth(aes(x = as.numeric(Species), y = predict(mdl.lm)), method = "lm", color = "blue")

3.2.5.7.3 Raw data, predicted means and the trend line

We can also plot the actual data, the predicted means and linear trend

iris %>% 
  ggplot(aes(x = Species, y = Sepal.Length))+
  geom_boxplot()+
  geom_boxplot(aes(x = Species, y = predict(mdl.lm)), color = "blue") +
  labs(x = "Species", y = "Length", title = "Boxplot raw data, predicted means (in blue) and trend line", subtitle = "with ggplot2") + 
  theme_bw() + theme(text = element_text(size = 15))+
  geom_smooth(aes(x = as.numeric(Species), y = predict(mdl.lm)), method = "lm", color = "blue")

3.2.6 What about pairwise comparison?

Based on our model’s summary, can you tell me if there is a difference between Versicolor and Virginica?

summary(mdl.lm)

Call:
lm(formula = Sepal.Length ~ Species, data = .)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.6880 -0.3285 -0.0060  0.3120  1.3120 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)         5.0060     0.0728  68.762  < 2e-16 ***
Speciesversicolor   0.9300     0.1030   9.033 8.77e-16 ***
Speciesvirginica    1.5820     0.1030  15.366  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5148 on 147 degrees of freedom
Multiple R-squared:  0.6187,    Adjusted R-squared:  0.6135 
F-statistic: 119.3 on 2 and 147 DF,  p-value: < 2.2e-16
mdl.lm %>% emmeans(pairwise ~ Species, adjust = "fdr")
$emmeans
 Species    emmean     SE  df lower.CL upper.CL
 setosa       5.01 0.0728 147     4.83     5.18
 versicolor   5.94 0.0728 147     5.76     6.11
 virginica    6.59 0.0728 147     6.41     6.76

Confidence level used: 0.95 
Conf-level adjustment: bonferroni method for 3 estimates 

$contrasts
 contrast               estimate    SE  df t.ratio p.value
 setosa - versicolor      -0.930 0.103 147  -9.033 <.0001 
 setosa - virginica       -1.582 0.103 147 -15.366 <.0001 
 versicolor - virginica   -0.652 0.103 147  -6.333 <.0001 

P value adjustment: fdr method for 3 tests 

How to interpret the output? Discuss with your neighbour and share with the group.

Hint… Look at the emmeans values for each level of our factor “Species” and the contrasts.

3.3 Other outcomes?

So far, we only looked at “Sepal.Length”. What about the other outcomes? how informative are they? Do we have statistical difference between the three levels of our predictor? You can do this in your spare time.

3.4 Conclusion

We have so far looked at the Linear Model. The underlying assumption about linear models is that we have a normal (Gaussian) distribution. This is the model to be used when we have a numeric outcome. What if our outcome is not numeric? What if we have two categories, i.e., black vs white? correct vs incorrect? yes vs no? These are categorical binary outcome. We look in the next section at Logistic Regression

4 Generalised Linear Models

Here we will look at an example when the outcome is binary. This simulated data is structured as follows. We asked one participant to listen to 165 sentences, and to judge whether these are “grammatical” or “ungrammatical”. There were 105 sentences that were “grammatical” and 60 “ungrammatical”. This fictitious example can apply in any other situation. Let’s think Geography: 165 lands: 105 “flat” and 60 “non-flat”, etc. This applies to any case where you need to “categorise” the outcome into two groups.

4.1 Load and summaries

Let’s load in the data and do some basic summaries

grammatical <- read_csv("grammatical.csv")
grammatical
str(grammatical)
tibble [165 x 2] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ grammaticality: chr [1:165] "grammatical" "grammatical" "grammatical" "grammatical" ...
 $ response      : chr [1:165] "yes" "yes" "yes" "yes" ...
 - attr(*, "spec")=
  .. cols(
  ..   grammaticality = col_character(),
  ..   response = col_character()
  .. )
head(grammatical)

4.2 GLM

Let’s run a first GLM (Generalised Linear Model). A GLM uses a special family “binomial” as it assumes the outcome has a binomial distribution. In general, results from a Logistic Regression are close to what we get from SDT (see above).

To run the results, we will change the reference level for both response and grammaticality. The basic assumption about GLM is that we start with our reference level being the “no” responses to the “ungrammatical” category. Any changes to this reference will be seen in the coefficients as “yes” responses to the “grammatical” category.

4.2.1 Model estimation and results

The results below show the logodds for our model.

grammatical <- grammatical %>% 
  mutate(response = factor(response, levels = c("no", "yes")),
         grammaticality = factor(grammaticality, levels = c("ungrammatical", "grammatical")))

grammatical %>% 
  group_by(grammaticality, response) %>% 
  table()
               response
grammaticality   no yes
  ungrammatical  50  10
  grammatical     5 100
mdl.glm <- grammatical %>% 
  glm(response ~ grammaticality, data = ., family = binomial)
summary(mdl.glm)

Call:
glm(formula = response ~ grammaticality, family = binomial, data = .)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.4676  -0.6039   0.3124   0.3124   1.8930  

Coefficients:
                          Estimate Std. Error z value Pr(>|z|)    
(Intercept)                -1.6094     0.3464  -4.646 3.38e-06 ***
grammaticalitygrammatical   4.6052     0.5744   8.017 1.08e-15 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 210.050  on 164  degrees of freedom
Residual deviance:  94.271  on 163  degrees of freedom
AIC: 98.271

Number of Fisher Scoring iterations: 5
tidy(mdl.glm) %>% 
  select(term, estimate) %>% 
  mutate(estimate = round(estimate, 3))
# to only get the coefficients
mycoef2 <- tidy(mdl.glm) %>% pull(estimate)

The results show that for one unit increase in the response (i.e., from no to yes), the logodds of being “grammatical” is increased by 4.6051702 (the intercept shows that when the response is “no”, the logodds are -1.6094379). The actual logodds for the response “yes” to grammatical is 2.9957323

4.2.2 Logodds to Odd ratios

Logodds can be modified to talk about the odds of an event. For our model above, the odds of “grammatical” receiving a “no” response is a mere 0.2; the odds of “grammatical” to receive a “yes” is a 20; i.e., 20 times more likely

exp(mycoef2[1])
[1] 0.2
exp(mycoef2[1] + mycoef2[2])
[1] 20

4.2.3 LogOdds to proportions

If you want to talk about the percentage “accuracy” of our model, then we can transform our loggodds into proportions. This shows that the proportion of “grammatical” receiving a “yes” response increases by 99% (or 95% based on our “true” coefficients)

plogis(mycoef2[1])
[1] 0.1666667
plogis(mycoef2[1] + mycoef2[2])
[1] 0.952381

4.2.4 Plotting

grammatical <- grammatical %>% 
  mutate(prob = predict(mdl.glm, type = "response"))
grammatical %>% 
  ggplot(aes(x = as.numeric(grammaticality), y = prob)) +
  geom_point() +
  geom_smooth(method = "glm", 
    method.args = list(family = "binomial"), 
    se = T) + theme_bw(base_size = 20)+
    labs(y = "Probability", x = "")+
    coord_cartesian(ylim = c(0,1))+
    scale_x_discrete(limits = c("Ungrammatical", "Grammatical"))

4.3 Accuracy and Signal Detection Theory

4.3.1 Rationale

We are generally interested in performance, i.e., whether the we have “accurately” categorised the outcome or not and at the same time want to evaluate our biases in responses. When deciding on categories, we are usually biased in our selection.

Let’s ask the question: How many of you have a Mac laptop and how many a Windows laptop? For those with a Mac, what was the main reason for choosing it? Are you biased in anyway by your decision?

To correct for these biases, we use some variants from Signal Detection Theory to obtain the true estimates without being influenced by the biases.

4.3.2 Running stats

Let’s do some stats on this

Yes No Total
Grammatical (Yes Actual) TP = 100 FN = 5 (Yes Actual) 105
Ungrammatical (No Actual) FP = 10 TN = 50 (No Actual) 60
Total (Yes Response) 110 (No Response) 55 165
grammatical <- grammatical %>% 
  mutate(response = factor(response, levels = c("yes", "no")),
         grammaticality = factor(grammaticality, levels = c("grammatical", "ungrammatical")))

## TP = True Positive (Hit); FP = False Positive; FN = False Negative; TN = True Negative


TP <- nrow(grammatical %>% 
             filter(grammaticality == "grammatical" &
                      response == "yes"))
FN <- nrow(grammatical %>% 
             filter(grammaticality == "grammatical" &
                      response == "no"))
FP <- nrow(grammatical %>% 
             filter(grammaticality == "ungrammatical" &
                      response == "yes"))
TN <- nrow(grammatical %>% 
             filter(grammaticality == "ungrammatical" &
                      response == "no"))
TP
[1] 100
FN
[1] 5
FP
[1] 10
TN
[1] 50
Total <- nrow(grammatical)
Total
[1] 165
(TP+TN)/Total # accuracy
[1] 0.9090909
(FP+FN)/Total # error, also 1-accuracy
[1] 0.09090909
# When stimulus = yes, how many times response = yes?
TP/(TP+FN) # also True Positive Rate or Specificity
[1] 0.952381
# When stimulus = no, how many times response = yes?
FP/(FP+TN) # False Positive Rate, 
[1] 0.1666667
# When stimulus = no, how many times response = no?
TN/(FP+TN) # True Negative Rate or Sensitivity 
[1] 0.8333333
# When subject responds "yes" how many times is (s)he correct?
TP/(TP+FP) # precision
[1] 0.9090909
# getting dprime (or the sensetivity index); beta (bias criterion, 0-1, lower=increase in "yes"); Aprime (estimate of discriminability, 0-1, 1=good discrimination; 0 at chance); bppd (b prime prime d, -1 to 1; 0 = no bias, negative = tendency to respond "yes", positive = tendency to respond "no"); c (index of bias, equals to SD)
#(see also https://www.r-bloggers.com/compute-signal-detection-theory-indices-with-r/amp/) 
psycho::dprime(TP, FP, FN, TN, 
               n_targets = TP+FN, 
               n_distractors = FP+TN,
               adjust=F)
$dprime
[1] 2.635813

$beta
[1] 0.3970026

$aprime
[1] 0.9419643

$bppd
[1] -0.5076923

$c
[1] -0.3504848

The most important from above, is d-prime. This is modelling the difference between the rate of “True Positive” responses and “False Positive” responses in standard unit (or z-scores). The formula can be written as:

d' (d prime) = Z(True Positive Rate) - Z(False Positive Rate)

4.3.3 GLM as a classification tool

The code below demonstrates the links between our GLM model and what we had obtained above from SDT. The predictions’ table shows that our GLM was successful at obtaining prediction that are identical to our initial data setup. Look at the table here and the table above. Once we have created our table of outcome, we can compute percent correct, the specificity, the sensitivity, the Kappa score, etc.. this yields the actual value with the SD that is related to variations in responses.

## predict(mdl.glm)>0.5 is identical to 
## predict(glm(response~grammaticality,data=grammatical,family = binomial),type="response")
grammatical <- grammatical %>% 
  mutate(response = factor(response, levels = c("yes", "no")),
         grammaticality = factor(grammaticality, levels = c("grammatical", "ungrammatical")))



mdl.glm.C <- grammatical %>% 
  glm(response ~ grammaticality, data = .,family = binomial)

tbl.glm <- table(grammatical$response, predict(mdl.glm.C, type = "response")>0.5)
colnames(tbl.glm) <- c("grammatical", "ungrammatical")
tbl.glm
     
      grammatical ungrammatical
  yes         100            10
  no            5            50
PresenceAbsence::pcc(tbl.glm)
PresenceAbsence::specificity(tbl.glm)
PresenceAbsence::sensitivity(tbl.glm)
###etc..

If you look at the results from SDT above, these results are the same as the following

Accuracy: (TP+TN)/Total (0.9090909)

True Positive Rate (or Specificity) TP/(TP+FN) (0.952381)

True Negative Rate (or Sensitivity) TN/(FP+TN) (0.8333333)

4.3.4 GLM and d prime

The values obtained here match those obtained from SDT. For d prime, the difference stems from the use of the logit variant of the Binomial family. By using a probit variant, one obtains the same values (see here for more details). A probit variant models the z-score differences in the outcome and is evaluated in change in 1-standard unit. This is modelling the change from “ungrammatical” “no” responses into “grammatical” “yes” responses in z-scores. The same conceptual underpinnings of d-prime from Signal Detection Theory.

## d prime
psycho::dprime(TP, FP, FN, TN, 
               n_targets = TP+FN, 
               n_distractors = FP+TN,
               adjust=F)$dprime
[1] 2.635813
## GLM with probit
coef(glm(response ~ grammaticality, data = grammatical, family = binomial(probit)))[2]
grammaticalityungrammatical 
                   2.635813 

4.4 GLM: Other distributions

If your data does not fit a binomial distribution, and is a multinomial (i.e., three or more response categories) or poisson (count data), then you need to use the glm function with a specific family function.

6 Linear Mixed-effects Models. Why random effects matter

Let’s generate a new dataframe that we will use later on for our mixed models

## Courtesy of Bodo Winter
set.seed(666)
#we create 6 subjects
subjects <- paste0('S', 1:6)
#here we add repetitions within speakers
subjects <- rep(subjects, each = 20)
items <- paste0('Item', 1:20)
#below repeats
items <- rep(items, 6)
#below is to generate random numbers that are log values
logFreq <- round(rexp(20)*5, 2)
#below we are repeating the logFreq 6 times to fit with the number of speakers and items
logFreq <- rep(logFreq, 6)
xdf <- data.frame(subjects, items, logFreq)
#below removes the individual variables we had created because they are already in the dataframe
rm(subjects, items, logFreq)

xdf$Intercept <- 300
submeans <- rep(rnorm(6, sd = 40), 20)
#sort make the means for each subject is the same...
submeans <- sort(submeans)
xdf$submeans <- submeans
#we create the same thing for items... we allow the items mean to vary between words...
itsmeans <- rep(rnorm(20, sd = 20), 6)
xdf$itsmeans <- itsmeans
xdf$error <- rnorm(120, sd = 20)
#here we create an effect column,  
#here for each logFreq, we have a decrease of -5 of that particular logFreq 
xdf$effect <- -5 * xdf$logFreq

xdf$dur <- xdf$Intercept + xdf$submeans + xdf$itsmeans + xdf$error + xdf$effect
#below is to subset the data and get only a few columns.. the -c(4:8) removes the columns 4 to 8..
xreal <- xdf[,-c(4:8)]
head(xreal)
rm(xdf, submeans, itsmeans)

6.1 Plots

Let’s start by doing a correlation test and plotting the data. Our results show that there is a negative correlation between duration and LogFrequency, and the plot shows this decrease.

corrMixed <- as.matrix(xreal[-c(1:2)]) %>% 
  rcorr(type="pearson")
print(corrMixed)
        logFreq   dur
logFreq    1.00 -0.54
dur       -0.54  1.00

n= 120 


P
        logFreq dur
logFreq          0 
dur      0         
corrplot(corrMixed$r, method = "circle", type = "upper", tl.srt = 45,
         addCoef.col = "black", diag = FALSE,
         p.mat = corrMixed$p, sig.level = 0.05)




ggplot.xreal <- xreal %>% 
  ggplot(aes(x = logFreq, y = dur)) +
  geom_point()+ theme_bw(base_size = 20) +
  labs(y = "Duration", x = "Frequency (Log)") +
  geom_smooth(method = lm, se=F)
ggplot.xreal

6.2 Linear model

Let’s run a simple linear model on the data. As we can see below, there are some issues with the “simple” linear model: we had set our SD for subjects to be 40, but this was picked up as 120 (see histogram of residuals). The QQ Plot is not “normal”.

mdl.lm.xreal <- xreal %>% 
  lm(dur ~ logFreq, data = .)
summary(mdl.lm.xreal)

Call:
lm(formula = dur ~ logFreq, data = .)

Residuals:
    Min      1Q  Median      3Q     Max 
-94.322 -35.465  -4.364  33.020 123.955 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 337.9730     6.2494  54.081  < 2e-16 ***
logFreq      -5.4601     0.7846  -6.959 2.06e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 48.29 on 118 degrees of freedom
Multiple R-squared:  0.291, Adjusted R-squared:  0.285 
F-statistic: 48.43 on 1 and 118 DF,  p-value: 2.057e-10
hist(residuals(mdl.lm.xreal))

qqnorm(residuals(mdl.lm.xreal)); qqline(residuals(mdl.lm.xreal))

plot(fitted(mdl.lm.xreal), residuals(mdl.lm.xreal), cex = 4)

6.3 Linear Mixed Model

Our Linear Mixed effects Model will take into account the random effects we added and also our model specifications. We use a Maximum Likelihood estimate (REML = FALSE) as this is what we will use for model comparison. The Linear Mixed Model is reflecting our model specifications The SD of our subjects is picked up correctly. The model results are “almost” the same as our linear model above. The coefficient for the “Intercept” is at 337.973 and the coefficient for LogFrequency is at -5.460. This indicates that for each unit of increase in the LogFrequency, there is a decrease by 5.460 (ms).

mdl.lmer.xreal <- xreal %>% 
  lmer(dur ~ logFreq  +(1|subjects) + (1|items), data = ., REML = FALSE)
summary(mdl.lmer.xreal)
Linear mixed model fit by maximum likelihood . t-tests use Satterthwaite's method [
lmerModLmerTest]
Formula: dur ~ logFreq + (1 | subjects) + (1 | items)
   Data: .

     AIC      BIC   logLik deviance df.resid 
  1105.8   1119.8   -547.9   1095.8      115 

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-2.06735 -0.60675  0.07184  0.61122  2.39854 

Random effects:
 Groups   Name        Variance Std.Dev.
 items    (Intercept)  589.8   24.29   
 subjects (Intercept) 1471.7   38.36   
 Residual              284.0   16.85   
Number of obs: 120, groups:  items, 20; subjects, 6

Fixed effects:
            Estimate Std. Error      df t value Pr(>|t|)    
(Intercept)  337.973     17.587   9.126  19.218 1.08e-08 ***
logFreq       -5.460      1.004  19.215  -5.436 2.92e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
        (Intr)
logFreq -0.322
hist(residuals(mdl.lmer.xreal))

qqnorm(residuals(mdl.lmer.xreal)); qqline(residuals(mdl.lmer.xreal))

plot(fitted(mdl.lmer.xreal), residuals(mdl.lmer.xreal), cex = 4)

6.4 Our second Mixed model

This second model add a by-subject random slope. Random slopes allow for the variation that exists in the random effects to be taken into account. An intercept only model provides an averaged values to our participants.

mdl.lmer.xreal.2 <- xreal %>% 
  lmer(dur ~ logFreq + (logFreq|subjects) + (1|items), data = ., REML = FALSE)
summary(mdl.lmer.xreal.2)
Linear mixed model fit by maximum likelihood . t-tests use Satterthwaite's method [
lmerModLmerTest]
Formula: dur ~ logFreq + (logFreq | subjects) + (1 | items)
   Data: .

     AIC      BIC   logLik deviance df.resid 
  1109.5   1129.0   -547.7   1095.5      113 

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-2.10907 -0.60681  0.06198  0.58272  2.45718 

Random effects:
 Groups   Name        Variance  Std.Dev. Corr
 items    (Intercept) 5.892e+02 24.2736      
 subjects (Intercept) 1.419e+03 37.6676      
          logFreq     2.917e-02  0.1708  1.00
 Residual             2.827e+02 16.8144      
Number of obs: 120, groups:  items, 20; subjects, 6

Fixed effects:
            Estimate Std. Error      df t value Pr(>|t|)    
(Intercept)  337.973     17.332   8.882  19.500 1.35e-08 ***
logFreq       -5.460      1.006  19.384  -5.426 2.89e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
        (Intr)
logFreq -0.265
convergence code: 0
boundary (singular) fit: see ?isSingular
hist(residuals(mdl.lmer.xreal.2))

qqnorm(residuals(mdl.lmer.xreal.2)); qqline(residuals(mdl.lmer.xreal.2))

plot(fitted(mdl.lmer.xreal.2), residuals(mdl.lmer.xreal.2), cex = 4)

6.5 Model comparison

But where are our p values? The lme4 developers decided not to include p values due to various issues with estimating df. What we can do instead is to compare models. We need to create a null model to allow for significance testing. As expected our predictor is significantly contributing to the difference.

mdl.lmer.xreal.Null <- xreal %>% 
  lmer(dur ~ 1 + (logFreq|subjects) + (1|items), data = ., REML = FALSE)
anova(mdl.lmer.xreal.Null, mdl.lmer.xreal.2)
Data: .
Models:
mdl.lmer.xreal.Null: dur ~ 1 + (logFreq | subjects) + (1 | items)
mdl.lmer.xreal.2: dur ~ logFreq + (logFreq | subjects) + (1 | items)
                    npar    AIC    BIC  logLik deviance  Chisq Df Pr(>Chisq)    
mdl.lmer.xreal.Null    6 1125.4 1142.1 -556.68   1113.4                         
mdl.lmer.xreal.2       7 1109.5 1129.0 -547.73   1095.5 17.891  1  2.339e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Also, do we really need random slopes? From the result below, we don’t seem to need random slopes at all, given that adding random slopes does not improve the model fit. I always recommend testing this. Most of the time I keep random slopes.

anova(mdl.lmer.xreal, mdl.lmer.xreal.2)
Data: .
Models:
mdl.lmer.xreal: dur ~ logFreq + (1 | subjects) + (1 | items)
mdl.lmer.xreal.2: dur ~ logFreq + (logFreq | subjects) + (1 | items)
                 npar    AIC    BIC  logLik deviance  Chisq Df Pr(>Chisq)
mdl.lmer.xreal      5 1105.8 1119.8 -547.92   1095.8                     
mdl.lmer.xreal.2    7 1109.5 1129.0 -547.73   1095.5 0.3782  2     0.8277

But if you are really (really!!!) obsessed by p values, then you can also use lmerTest. BUT use after comparing models to evaluate contribution of predictors

mdl.lmer.xreal.lmerTest <- xreal %>% 
  lmer(dur ~ logFreq + (logFreq|subjects) + (1|items), data = ., REML = TRUE)
summary(mdl.lmer.xreal.lmerTest)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: dur ~ logFreq + (logFreq | subjects) + (1 | items)
   Data: .

REML criterion at convergence: 1086.1

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-2.09668 -0.60121  0.06407  0.58451  2.46140 

Random effects:
 Groups   Name        Variance  Std.Dev. Corr
 items    (Intercept) 6.284e+02 25.0682      
 subjects (Intercept) 1.639e+03 40.4785      
          logFreq     3.402e-02  0.1845  1.00
 Residual             2.831e+02 16.8244      
Number of obs: 120, groups:  items, 20; subjects, 6

Fixed effects:
            Estimate Std. Error      df t value Pr(>|t|)    
(Intercept)  337.973     18.465   7.494  18.303 1.72e-07 ***
logFreq       -5.460      1.037  18.180  -5.265 5.09e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
        (Intr)
logFreq -0.250
convergence code: 0
boundary (singular) fit: see ?isSingular
detach("package:lmerTest", unload = TRUE)

6.6 Our final Mixed model

Our final model uses REML (or Restricted Maximum Likelihood Estimate of Variance Component) to estimate the model.

mdl.lmer.xreal.Full <- xreal %>% 
  lmer(dur ~ logFreq + (logFreq|subjects) + (1|items), data = ., REML = TRUE)
summary(mdl.lmer.xreal.Full)
Linear mixed model fit by REML ['lmerMod']
Formula: dur ~ logFreq + (logFreq | subjects) + (1 | items)
   Data: .

REML criterion at convergence: 1086.1

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-2.09668 -0.60121  0.06407  0.58451  2.46140 

Random effects:
 Groups   Name        Variance  Std.Dev. Corr
 items    (Intercept) 6.284e+02 25.0682      
 subjects (Intercept) 1.639e+03 40.4785      
          logFreq     3.402e-02  0.1845  1.00
 Residual             2.831e+02 16.8244      
Number of obs: 120, groups:  items, 20; subjects, 6

Fixed effects:
            Estimate Std. Error t value
(Intercept)  337.973     18.465  18.303
logFreq       -5.460      1.037  -5.265

Correlation of Fixed Effects:
        (Intr)
logFreq -0.250
convergence code: 0
boundary (singular) fit: see ?isSingular
anova(mdl.lmer.xreal.Full)
Analysis of Variance Table
        npar Sum Sq Mean Sq F value
logFreq    1 7845.7  7845.7  27.717
hist(residuals(mdl.lmer.xreal.Full))

qqnorm(residuals(mdl.lmer.xreal.Full)); qqline(residuals(mdl.lmer.xreal.Full))

plot(fitted(mdl.lmer.xreal.Full), residuals(mdl.lmer.xreal.Full), cex = 4)

6.7 Dissecting the model

coef(mdl.lmer.xreal.Full)
$items
       (Intercept)   logFreq
Item1     352.3541 -5.460115
Item10    331.7629 -5.460115
Item11    324.7293 -5.460115
Item12    350.2297 -5.460115
Item13    353.1147 -5.460115
Item14    311.8401 -5.460115
Item15    354.0563 -5.460115
Item16    353.9361 -5.460115
Item17    288.7930 -5.460115
Item18    362.4659 -5.460115
Item19    338.1424 -5.460115
Item2     325.1878 -5.460115
Item20    359.7375 -5.460115
Item3     370.1747 -5.460115
Item4     302.4329 -5.460115
Item5     350.0478 -5.460115
Item6     338.9481 -5.460115
Item7     362.8357 -5.460115
Item8     295.6018 -5.460115
Item9     333.0702 -5.460115

$subjects
   (Intercept)   logFreq
S1    314.4719 -5.567204
S2    303.9072 -5.615345
S3    314.2944 -5.568013
S4    318.4301 -5.549168
S5    373.2970 -5.299153
S6    403.4376 -5.161809

attr(,"class")
[1] "coef.mer"
fixef(mdl.lmer.xreal.Full)
(Intercept)     logFreq 
 337.973044   -5.460115 
fixef(mdl.lmer.xreal.Full)[1]
(Intercept) 
    337.973 
fixef(mdl.lmer.xreal.Full)[2]
  logFreq 
-5.460115 
coef(mdl.lmer.xreal.Full)$`subjects`[1]
coef(mdl.lmer.xreal.Full)$`subjects`[2]

coef(mdl.lmer.xreal.Full)$`items`[1]
coef(mdl.lmer.xreal.Full)$`items`[2]
NA

6.8 Using predictions from our model

In general, I use the prediction from my final model in any plots. To generate this, we can use the following

xreal <- xreal %>% 
  mutate(Pred_Dur = predict(mdl.lmer.xreal.Full))

xreal %>% 
  ggplot(aes(x = logFreq, y = Pred_Dur)) +
  geom_point() + theme_bw(base_size = 20) +
  labs(y = "Duration", x = "Frequency (Log)", title = "Predicted") +
  geom_smooth(method = lm, se = F) + coord_cartesian(ylim = c(200,450))


## original plot
xreal %>% 
  ggplot(aes(x = logFreq , y = dur)) +
  geom_point() + theme_bw(base_size = 20)+
  labs(y = "Duration", x = "Frequency (Log)", title = "Original")+
  geom_smooth(method = lm, se = F) + coord_cartesian(ylim = c(200,450))

6.9 GLMM and CLMM

The code above was using a Linear Mixed Effects Modelling. The outcome was a numeric object. In some cases (as we have seen above), we may have:

  1. Binary outcome (binomial)
  2. Count data (poisson),
  3. Multi-category outcome (multinomial)
  4. Rating data (cumulative function)

The code below gives you an idea of how to specify these models


## Binomial family
## lme4::glmer(outcome~predictor(s)+(1|subject)+(1|items)..., data=data, family=binomial)

## Poisson family
## lme4::glmer(outcome~predictor(s)+(1|subject)+(1|items)..., data=data, family=poisson)

## Multinomial family
## a bit complicated as tehre is a need to use Bayesian approaches, see e.g., 
## glmmADMB
## mixcat
## MCMCglmm
## see https://gist.github.com/casallas/8263818

## Rating data, use following
## ordinal::clmm(outcome~predictor(s)+(1|subject)+(1|items)..., data=data)


## Remember to test for random effects and whether slopes are needed.
LS0tDQp0aXRsZTogIlNlc3Npb25fMy1BbmFseXNpbmdEYXRhIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNg0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogeWVzDQogIA0KLS0tDQoNCg0KIyBMb2FkaW5nIHBhY2thZ2VzIA0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQojIyBVc2UgdGhlIGNvZGUgYmVsb3cgdG8gY2hlY2sgaWYgeW91IGhhdmUgYWxsIHJlcXVpcmVkIHBhY2thZ2VzIGluc3RhbGxlZC4gSWYgc29tZSBhcmUgbm90IGluc3RhbGxlZCBhbHJlYWR5LCB0aGUgY29kZSBiZWxvdyB3aWxsIGluc3RhbGwgdGhlc2UuIElmIHlvdSBoYXZlIGFsbCBwYWNrYWdlcyBpbnN0YWxsZWQsIHRoZW4geW91IGNvdWxkIGxvYWQgdGhlbSB3aXRoIHRoZSBzZWNvbmQgY29kZS4NCnJlcXVpcmVkUGFja2FnZXMgPSBjKCd0aWR5dmVyc2UnLCAnb3JkaW5hbCcsICdicm9vbScsICdlbW1lYW5zJywgJ2tuaXRyJywgJ0htaXNjJywgJ2NvcnJwbG90JywgJ3BzeWNobycsICdQcmVzZW5jZUFic2VuY2UnLCAnbG1lNCcsICdsbWVyVGVzdCcpDQpmb3IocCBpbiByZXF1aXJlZFBhY2thZ2VzKXsNCiAgaWYoIXJlcXVpcmUocCxjaGFyYWN0ZXIub25seSA9IFRSVUUpKSBpbnN0YWxsLnBhY2thZ2VzKHApDQogIGxpYnJhcnkocCxjaGFyYWN0ZXIub25seSA9IFRSVUUpDQp9DQpgYGANCg0KDQpJbiB0aGlzIHNlc3Npb24sIHdlIHdpbGwgbG9vayBhdCBiYXNpYyBmdW5jdGlvbnMgaW4gUiB0aGF0IHdpbGwgaGVscCB1cyBpbiBydW5uaW5nIHNvbWUgaW5mZXJlbnRpYWwgc3RhdGlzdGljcy4gVGhlc2Ugd2lsbCBoZWxwIHVzIHRvIGV2YWx1YXRlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBvbmUgKG9yIG1vcmUpIHByZWRpY3RvcihzKSAoaW5kZXBlbmRlbnQgdmFyaWFibGUpIGFuZCBhbiBvdXRjb21lIChkZXBlbmRlbnQgdmFyaWFibGUpLiANCg0KSXQgaXMgaW1wb3J0YW50IHRvIGtub3cgdGhlIGNsYXNzIG9mIHRoZSBvdXRjb21lIGJlZm9yZSBkb2luZyBhbnkgcHJlLWRhdGEgYW5hbHlzZXMgb3IgaW5mZXJlbnRpYWwgc3RhdGlzdGljcy4gT3V0Y29tZSBjbGFzc2VzIGNhbiBiZSBvbmUgb2Y6DQoNCjEuIGBOdW1lcmljYDogQXMgYW4gZXhhbXBsZSwgd2UgaGF2ZSBsZW5ndGgvd2lkdGggb2YgbGVhZjsgaGVpZ2h0IG9mIG1vdW50YWluOyBmdW5kYW1lbnRhbCBmcmVxdWVuY3kgb2YgdGhlIHZvaWNlOyBldGMuIFRoZXNlIGFyZSBgdHJ1ZWAgbnVtYmVycyBhbmQgd2UgY2FuIHVzZSBzdW1tYXJpZXMsIHQtdGVzdHMsIGxpbmVhciBtb2RlbHMsIGV0Yy4gDQoNCjIuIGBDYXRlZ29yaWNhbGAgKFVub3JkZXJlZCk6IE9ic2VydmF0aW9ucyBmb3IgdHdvIG9yIG1vcmUgY2F0ZWdvcmllcy4gQXMgYW4gZXhhbXBsZSwgd2UgY2FuIGhhdmUgZ2VuZGVyIG9mIGEgc3BlYWtlciAobWFsZSBvciBmZW1hbGUpOyByZXNwb25zZXMgdG8gYSBUcnVlIHZzIEZhbHNlIHBlcmNlcHRpb24gdGVzdHM7IENvbG91ciAodW5vcmRlcmVkKSBjYXRlZ29yaXNhdGlvbiwgZS5nLiwgcmVkLCBibHVlLCB5ZWxsb3csIG9yYW5nZSwgZXRjLi4gRm9yIHRoZXNlIHdlIGNhbiB1c2UgYSBHZW5lcmFsaXNlZCBMaW5lYXIgTW9kZWwgKGJpbm9taWFsIG9yIG11bHRpbm9taWFsKSBvciBhIHNpbXBsZSBjaGktc3F1YXJlIHRlc3QuIENvdW50IGRhdGEgYXJlIG51bWJlcnMgcmVsYXRlZCB0byBhIGNhdGVnb3J5LiBCdXQgdGhlc2Ugc2hvdWxkIGJlIGFuYWx5c2VkIHVzaW5nIGEgcG9pc3NvbiBsb2dpc3RpYyByZWdyZXNzaW9uDQoNCjMuIGBDYXRlZ29yaWNhbGAgKE9yZGVyZWQpOiBXaGVuIHlvdSBydW4gYSByYXRpbmcgZXhwZXJpbWVudCwgd2hlcmUgdGhlIG91dGNvbWUgaXMgZWl0aGVyIGBudW1lcmljYCAoaS5lLiwgMSwgMiwgMywgNCwgNSkgb3IgYGNhdGVnb3JpZXNgIChpLmUuLCBkaXNhZ3JlZSwgbmV1dHJhbCwgYWdyZWUpLiBUaGUgYG51bWVyaWNgIG9wdGlvbiBpcyBOT1QgYSB0cnVlIG51bWJlciBhcyBmb3IgdGhlIHBhcnRpY2lwYW50LCB0aGVzZSBhcmUgY2F0ZWdvcmllcy4gQ3VtdWxhdGl2ZSBMb2dpdCBtb2RlbHMgKG9yIEdlbmVyYWxpc2VkIExpbmVhciBNb2RlbCB3aXRoIGEgY3VtdWxhdGl2ZSBmdW5jdGlvbikgYXJlIHVzZWQuIFRoZSBtZWFuIGlzIG1lYW5pbmdsZXNzIGhlcmUsIGFuZCB0aGUgbWVkaWFuIGlzIGEgcHJlZmVycmVkIGRlc2NyaXB0aXZlIHN0YXRpc3RpYy4NCg0KVGhlIGNvbnRlbnQgd2lsbCBiZSBzcGxpdCBpbnRvIDUgc2VjdGlvbnMuIFNvbWUgb2YgeW91IHdpbGwgb25seSBldmVyIGxvb2sgYXQgc29tZSBvZiB0aGVzZSwgd2hpbGUgb3RoZXJzIG1heSBuZWVkIHRvIHVzZSBhbGwuIExvb2sgYXQgdGhlIHNlY3Rpb24gdGhhdCB3b3JrcyBiZXN0IGZvciB5b3VyIGRhdGEuDQoNCjEuIFByZS1kYXRhIGFuYWx5c2VzOiBXZSB3aWxsIGxvb2sgYXQgc3VtbWFyaWVzLCBwbG90dGluZywgY29ycmVsYXRpb25zLCBjaGVja2luZyBub3JtYWxpdHkgb2YgZGlzdHJpYnV0aW9uIGFuZCBob21vZ2VuZWl0eSBvZiB2YXJpYW5jZSAoZm9yIHQtdGVzdHMpDQoNCjIuIFN0YXRpc3RpY2FsIEFuYWx5c2VzOiBmcm9tIHQtdGVzdCBhbmQgQU5PVkEsIHRvIExpbmVhciBNb2RlbHM6IG1vZGVsIGVzdGltYXRpb24sIHVuZGVyc3RhbmRpbmcgY29lZmZpY2llbnRzLCByZXNpZHVhbHMsIGFuZCBwcmVkaWN0aW9ucw0KDQozLiBXZSB0aGVuIG1vdmUgdG8gR2VuZXJhbGlzZWQgTGluZWFyIE1vZGVscywgd2l0aCBMb2dpc3RpYyBSZWdyZXNzaW9uICh3aXRoIGEgYmlub21pYWwgY2F0ZWdvcmljYWwgb3V0Y29tZSkgYW5kIHRvdWNoIHVwb24gU2lnbmFsIERldGVjdGlvbiBUaGVvcnkgZm9yIGVzdGltYXRpbmcgYWNjdXJhY3ksIGJpYXNlcywgd2l0aCBzZW5zaXRpdml0eSBhbmQgc3BlY2lmaWNpdHkgbWVhc3VyZXMuDQoNCjQuIERlYWxpbmcgd2l0aCByYXRpbmcgZGF0YSB1c2luZyBHZW5lcmFsaXNlZCBMaW5lYXIgTW9kZWxzLCB3aXRoIGEgQ3VtdWxhdGl2ZSBMb2dpdCBmdW5jdGlvbg0KDQo1LiBMaW5lYXIgTWl4ZWQgZWZmZWN0cyBNb2RlbHM6IEludHJvZHVjdGlvbiB0byByYW5kb20gZWZmZWN0cyBhbmQgaG93IHRvIGRlYWwgd2l0aCB0aGVzZS4gDQoNCg0KIyBQcmUtZGF0YSBhbmFsc3llcw0KDQojIyBCdWlsdC1pbiBkYXRhc2V0cw0KDQpXZSB3aWxsIHVzZSBvbmUgb2YgdGhlIGJ1aWx0IGluIGBSYC4gWW91IGNhbiBjaGVjayBhbGwgYXZhaWxhYmxlIGRhdGFzZXRzIGluIGBSYCB1c2luZyB0aGUgZm9sbG93aW5nOg0KDQpgYGB7cn0NCmRhdGEoKQ0KIyBvciBiZWxvdyBmb3IgYWxsIGRhdGFzZXRzIGF2YWlsYWJsZSBpbiBhbGwgaW5zdGFsbGVkIHBhY2thZ2VzDQpkYXRhKHBhY2thZ2UgPSAucGFja2FnZXMoYWxsLmF2YWlsYWJsZSA9IFRSVUUpKQ0KYGBgDQoNCldlIHdpbGwgdXNlIHRoZSBgaXJpc2AgZGF0YXNldCBmcm9tIHRoZSBwYWNrYWdlIGBNQVNTYA0KDQojIyBDaGVja2luZyBzdHJ1Y3R1cmUgYW5kIHN1bW1hcmllcw0KDQojIyMgU3RydWN0dXJlDQoNCmBgYHtyfQ0KaXJpcyAlPiUgDQogIHN0cigpICANCmBgYA0KDQpXZSBoYXZlIGEgZGF0YWZyYW1lIHdpdGggMTUwIG9ic2VydmF0aW9ucyBhbmQgNSB2YXJpYWJsZXM7IDQgbnVtZXJpYyBhbmQgMSBmYWN0b3Igd2l0aCAzIGxldmVscy4gDQoNCiMjIyBTdW1tYXJ5DQoNCldlIHN1bW1hcmlzZSB0aGUgZGF0YSB0byBzZWUgdGhlIHRyZW5kczoNCg0KYGBge3J9DQppcmlzICU+JSANCiAgc3VtbWFyeSgpDQpgYGANCg0KU28gd2UgaGF2ZSBhbiBlcXVhbCBkYXRhZnJhbWUgKDUwIG9ic2VydmF0aW9ucyB1bmRlciBlYWNoIGxldmVsIG9mIHRoZSBmYWN0b3IgYFNwZWNpZXNgKSwgd2l0aCBubyBtaXNzaW5nIHZhbHVlcyAoYWthIGBOQWApLg0KDQoNCiMjIyBBZHZhbmNlZA0KDQoNCiMjIyMgRm9yIGEgc3BlY2lmaWMgdmFyaWFibGUNCg0KV2hhdCBpZiB5b3Ugd2FudCB0byBzdW1tYXJpc2UgdGhlIGRhdGEgYW5kIGdldCB0aGUgbWVhbiwgU0QsIGJ5IGVhY2ggbGV2ZWwgb2YgdGhlIGZhY3RvciBmb3IgYFNlcGFsLkxlbmd0aGA/IA0KDQpgYGB7cn0NCmlyaXMgJT4lIA0KICBncm91cF9ieShTcGVjaWVzKSAlPiUgDQogIHN1bW1hcmlzZSgNCiAgU0wuTWVhbiA9IG1lYW4oU2VwYWwuTGVuZ3RoKSwNCiAgU0wuU0QgPSBzZChTZXBhbC5MZW5ndGgpDQogICkNCmBgYA0KDQojIyMjIEZvciBhbGwgdmFyaWFibGVzDQoNCmBgYHtyfQ0KaXJpcyAlPiUgDQogIGdyb3VwX2J5KFNwZWNpZXMpICU+JSANCiAgc3VtbWFyaXNlX2FsbChsaXN0KG1lYW4gPSBtZWFuLCBzZCA9IHNkKQ0KICApDQpgYGANCg0KDQojIyMgVXAgdG8geW91DQoNCkRvIHNvbWUgYWRkaXRpb25hbCBzdW1tYXJpZXMuLiBZb3UgbWF5IHdhbnQgdG8gY2hlY2sgdGhlIGBtZWRpYW5gLCBgcmFuZ2VgLCBldGMuLg0KDQpgYGB7cn0NCg0KYGBgDQoNCg0KIyMgUGxvdA0KDQpXZSBjYW4gbWFrZSBhIGJveHBsb3Qgb2YgdGhlIGRhdGEgYW5kIGFkZCBhIHRyZW5kIGxpbmUuIFRoaXMgYWxsb3dzIHVzIHRvIHZpc3VhbGlzZSB0aGUgbWVkaWFuLCBhbmQgcXVhbnRpbGVzIGluIGFkZGl0aW9uIHRvIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gYW5kIGFueSBvdXRsaWVycy4uLiBBbGwgaW4gdGhlIHNhbWUgcGxvdCENCg0KYGBge3Isd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KaXJpcyAlPiUgDQogIGdncGxvdChhZXMoeCA9IFNwZWNpZXMsIHkgPSBTZXBhbC5MZW5ndGgpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgZ2VvbV9zbW9vdGgoYWVzKHggPSBhcy5udW1lcmljKFNwZWNpZXMpLCB5ID0gU2VwYWwuTGVuZ3RoKSwgbWV0aG9kID0gImxtIikgKw0KICBsYWJzKHggPSAiU3BlY2llcyIsIHkgPSAiTGVuZ3RoIiwgdGl0bGUgPSAiQm94cGxvdCBhbmQgdHJlbmQgbGluZSIsIHN1YnRpdGxlID0gIndpdGggZ2dwbG90MiIpICsgDQogIHRoZW1lX2J3KCkgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpDQpgYGANCg0KSGVyZSBJIGhhdmUgdXNlZCB0aGUgdmFyaWFibGUgYFNlcGFsLkxlbmd0aGAuIFlvdSBjYW4gdXNlIGFueSBvZiB0aGUgYWRkaXRpb25hbCB2YXJpYWJsZXMgdG8gcGxvdCB0aGUgZGF0YS4NCg0KIyMgQ29ycmVsYXRpb24gdGVzdHMNCg0KIyMjIEJhc2ljIGNvcnJlbGF0aW9ucw0KDQpXZSB1c2UgdGhlIGZ1bmN0aW9uIGBjb3JgIHRvIG9idGFpbiB0aGUgcGVhcnNvbiBjb3JyZWxhdGlvbiBhbmQgYGNvci50ZXN0YCB0byBydW4gYSBiYXNpYyBjb3JyZWxhdGlvbiB0ZXN0IG9uIG91ciBkYXRhIHdpdGggc2lnbmlmaWNhbmNlIHRlc3RpbmcNCg0KYGBge3J9DQpjb3IoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkUGV0YWwuTGVuZ3RoLCBtZXRob2QgPSAicGVhcnNvbiIpDQpjb3IudGVzdChpcmlzJFNlcGFsLkxlbmd0aCwgaXJpcyRQZXRhbC5MZW5ndGgpDQpgYGANCg0KIyMjIyBVcCB0byB5b3UNCg0KQ2FuIHlvdSBjaGVjayB3aGV0aGVyIHRoZXJlIGlzIGEgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgYFNlcGFsLkxlbmd0aGAgYW5kIHRoZSBgUGV0YWwuV2lkdGhgPyBXaGF0IGFib3V0IGBQZXRhbC5MZW5ndGhgIGFuZCBgUGV0YWwuV2lkdGhgPyANCg0KDQpgYGB7cn0NCg0KYGBgDQoNCg0KDQojIyMgVXNpbmcgdGhlIHBhY2thZ2UgYGNvcnJwbG90YA0KDQpBYm92ZSwgd2UgZGlkIGEgY29ycmVsYXRpb24gdGVzdCBvbiB0d28gcHJlZGljdG9ycy4gV2UgcnVuIG11bHRpcGxlIGNvcnJlbGF0aW9ucyBvbiBtdWx0aXBsZSBwcmVkaWN0b3JzLiANCg0KIyMjIyBDb3JyZWxhdGlvbnMNCg0KYGBge3J9DQojIyBjb3JyZWxhdGlvbiB1c2luZyAiY29ycnBsb3QiDQojIyBiYXNlZCBvbiB0aGUgZnVuY3Rpb24gYHJjb3JyJyBmcm9tIHRoZSBgSG1pc2NgIHBhY2thZ2UNCiMjIE5lZWQgdG8gY2hhbmdlIGRhdGFmcmFtZSBpbnRvIGEgbWF0cml4DQpjb3JyIDwtIGFzLm1hdHJpeChpcmlzWy01XSkgJT4lIA0KICByY29ycih0eXBlPSJwZWFyc29uIikNCnByaW50KGNvcnIpDQpjb3JycGxvdChjb3JyJHIsIHAubWF0ID0gY29yciRQLA0KICAgICAgICAgYWRkQ29lZi5jb2wgPSAiYmxhY2siLCBkaWFnID0gRkFMU0UsIHR5cGUgPSAidXBwZXIiLCB0bC5zcnQgPSA1NSkNCmBgYA0KDQoNCg0KDQojIyMjIFVwIHRvIHlvdQ0KDQpMb29rIGludG8gdGhlIGBjb3JycGxvdGAgc3BlY2lmaWNhdGlvbiwgdXNpbmcgYD9jb3JycGxvdGAgYW5kIGFtZW5kIHNvbWUgb2YgdGhlIGNyaXRlcmlhLiBSdW4gYSBjb3JyZWxhdGlvbiBwbG90IHdoaWxlIGZpbHRlcmluZyB0aGUgZGF0YSBhY2NvcmRpbmcgdG8gdGhlIGBzcGVjaWVzYC4NCg0KYGBge3J9DQojIGhpbnQgDQojIGNyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgYnkgZmlsdGVyaW5nIGBTcGVjaWVzYCwgdGhlbiBjb21wdXRlIGNvcnJlbGF0aW9ucyBhbmQgcGxvdA0KICANCg0KYGBgDQoNCg0KVXAgdG8gbm93LCB3ZSBoYXZlIGRvbmUgc29tZSBiYXNpYyBzdW1tYXJpZXMgYW5kIGNoZWNrZWQgdGhlIGNvcnJlbGF0aW9ucyBpbiB0aGUgZGF0YS4gVGhlIHBlYXJzb24gY29ycmVsYXRpb25zIHdlIGhhdmUgZG9uZSBwcm92aWRlZCB1cyB3aXRoIHNpZ25pZmljYW5jZSBsZXZlbHMgcmVsYXRlZCB0byB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gdHdvICpudW1lcmljKiBvdXRjb21lcy4gV2UgY29udGludWUgYnkgZXhhbWluaW5nIHRoZSBub3JtYWxpdHkgb2YgZGlzdHJpYnV0aW9uIG9mIG91ciBkYXRhLiANCg0KIyMgTm9ybWFsaXR5IG9mIGRpc3RyaWJ1dGlvbg0KDQojIyMgU3Vic2V0dGluZyBkYXRhDQoNCkluIHRoZSBgaXJpc2AgZGF0YXNldCwgd2UgaGF2ZSBhIGNhdGVnb3JpY2FsIHByZWRpY3RvcjogYFNwZWNpZXNgIHdoaWNoIGhhcyB0aHJlZSBsZXZlbHMNCg0KYGBge3J9DQpsZXZlbHMoaXJpcyRTcGVjaWVzKQ0KYGBgDQoNCkxldCdzIHN1YnNldCB0aGUgZGF0YSB0byBgc2V0b3NhYCBhbmQgYHZlcnNpY29sb3JgLiBXZSB3aWxsIGFsc28gY2hlY2sgdGhlIG5vcm1hbGl0eSBhbmQgaG9tb2dlbmVpdHkgb2YgdmFyaWFuY2UgaW4gdGhlIGRhdGENCg0KDQpgYGB7cn0NCmlyaXNTdWIgPC0gaXJpcyAlPiUgDQogIGZpbHRlcihTcGVjaWVzICVpbiUgYygic2V0b3NhIiwgInZlcnNpY29sb3IiKSkNCmBgYA0KDQoNCiMjIyBTaGFwaXJvIHRlc3QNCg0KVG8gY2hlY2sgbm9ybWFsaXR5IG9mIGRpc3RyaWJ1dGlvbiwgd2UgdXNlIHRoZSBgc2hhcGlyby50ZXN0YCBvbiB0aGUgbnVtZXJpYyBvdXRjb21lLiBHaXZlbiB0aGF0IG91ciBwcmVkaWN0b3Igbm93IGhhcyB0d28gbGV2ZWxzLiBXZSBuZWVkIHRvIHN1YnNldCB0aGUgZGF0YSBhZ2FpbiB0byBjaGVjayBub3JtYWxpdHkgb2YgdGhlIG91dGNvbWUgYFNlcGFsLkxlbmd0aGAgZm9yIGVhY2ggbGV2ZWwgb2Ygb3VyIGZhY3RvciBgU3BlY2llc2ANCg0KYGBge3J9DQppcmlzU3ViU2V0IDwtIGlyaXMgJT4lIA0KICBmaWx0ZXIoU3BlY2llcyA9PSAic2V0b3NhIikNCmlyaXNTdWJWZXJzIDwtIGlyaXMgJT4lIA0KICBmaWx0ZXIoU3BlY2llcyA9PSAidmVyc2ljb2xvciIpDQogIA0Kc2hhcGlyby50ZXN0KGlyaXNTdWJTZXQkU2VwYWwuTGVuZ3RoKQ0Kc2hhcGlyby50ZXN0KGlyaXNTdWJWZXJzJFNlcGFsLkxlbmd0aCkNCmBgYA0KDQpIb3cgdG8gaW50ZXJwcmV0IHRoaXMgbm9uLXN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgcmVzdWx0PyBUaGlzIHRlbGxzIHVzIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YSBpcyBub3Qgc3RhdGlzdGljYWxseSBkaWZmZXJlbnQgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24uDQoNCiMjIyBEZW5zaXR5IHBsb3QNCg0KV2UgY2FuIGFsc28gdXNlIGEgZGVuc2l0eSBwbG90IHRvIGV2YWx1YXRlIG5vcm1hbGl0eS4gVGhlIHJlc3VsdHMgc2hvdyB0aGF0IGJvdGggbGV2ZWxzIGhhdmUgYmVsbCBzaGFwZWQgZGlzdHJpYnV0aW9ucy4NCg0KYGBge3J9DQppcmlzU3ViICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gU2VwYWwuTGVuZ3RoKSkrDQogIGdlb21fZGVuc2l0eSgpKw0KICBmYWNldF93cmFwKH5TcGVjaWVzLCBzY2FsZXMgPSAiZnJlZV94IikNCmBgYA0KDQoNCiMjIyBIb21vZ2VuZWl0eSBvZiB2YXJpYW5jZQ0KDQpCZWNhdXNlIG91ciBkYXRhIGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCB3ZSBjYW4gdXNlIHRoZSBgYmFydGxldHRgIHRlc3QuIElmIG91ciBkYXRhIHdlcmUgbm9uLW5vcm1hbGx5IGRpc3RyaWJ1dGVkLCB3ZSB3b3VsZCB1c2UgdGhlIExldmVuZSBUZXN0IChlaXRoZXIgd2l0aCBgdmFyLnRlc3RgIGZyb20gYmFzZS1SIG9yIGBsZXZlbmVUZXN0YCBmcm9tIHRoZSBjYXIgcGFja2FnZSBXZSBjYW4gY2hlY2sgYm90aC4NCg0KIyMjIyBCYXJ0bGV0dCB0ZXN0DQoNCmBgYHtyfQ0KaXJpc1N1YiAlPiUgDQogIGJhcnRsZXR0LnRlc3QoU2VwYWwuTGVuZ3RoIH4gU3BlY2llcywgZGF0YSA9IC4pDQpgYGANCg0KIyMjIyBMZXZlbmUgdGVzdA0KDQpgYGB7cn0NCmlyaXNTdWIgJT4lIA0KICB2YXIudGVzdChTZXBhbC5MZW5ndGggfiBTcGVjaWVzLCBkYXRhID0gLikNCmlyaXNTdWIgJT4lIA0KICBjYXI6OmxldmVuZVRlc3QoU2VwYWwuTGVuZ3RoIH4gU3BlY2llcywgZGF0YSA9IC4pDQpgYGANCg0KSW4gYWxsIGNhc2VzLCB0aGUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCByZXN1bHQgaW5kaWNhdGVzIHRoYXQgdGhlcmUgaXMgZXZpZGVuY2UgdGhhdCB0aGUgdmFyaWFuY2Ugb2YgdHdvIGxldmVscyBvZiB0aGUgZmFjdG9yIGBTcGVjaWVzYCBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50OyBpLmUuLCB0aGUgdmFyaWFuY2VzIGFyZSBub3QgZXF1YWwuIFRoaXMgaXMgaW1wb3J0YW50IGFzIHdlIHdpbGwgdXNlIHRoaXMgaW4gb3VyIHQtdGVzdCBsYXRlciBvbg0KDQojIExpbmVhciBNb2RlbHMNCg0KVXAgdG8gbm93LCB3ZSBoYXZlIGxvb2tlZCBhdCBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzLCBhbmQgZXZhbHVhdGVkIHN1bW1hcmllcywgY29ycmVsYXRpb25zIGluIHRoZSBkYXRhICh3aXRoIHAgdmFsdWVzKSwgYW5kIGNoZWNrZWQgdGhlIG5vcm1hbGl0eSBvZiBkaXN0cmlidXRpb24gb2Ygb3VyIGRhdGEuDQoNCldlIGFyZSBub3cgaW50ZXJlc3RlZCBpbiBsb29raW5nIGF0IGdyb3VwIGRpZmZlcmVuY2VzLiANCg0KTGV0IHVzIHN0YXJ0IHdpdGggYSBzaW1wbGUgdC10ZXN0DQoNCiMjIEZpcnN0IHN0ZXBzDQoNCiMjIyBULXRlc3QNCg0KV2UgdGhlbiBydW4gYSB0LXRlc3Qgb24gdGhlIGRhdGEuIFdlIHNwZWNpZnkgdGhlIGZvcm11bGEgYXMgYHkgfiB4YCBhbmQgYWRkIGB2YXIuZXF1YWwgPSBGQUxTRWAgKGJhc2VkIG9uIHRoZSBub3JtYWxpdHkgdGVzdHMgYWJvdmUpDQoNCmBgYHtyfQ0KaXJpc1N1YiAlPiUgDQogIHQudGVzdChTZXBhbC5MZW5ndGggfiBTcGVjaWVzLCBkYXRhID0gLiwgdmFyLmVxdWFsID0gRkFMU0UpDQpgYGANCg0KVG8gaW50ZXJwcmV0IHRoZSB0LXRlc3QsIHdlIHNheSB0aGF0IHRoZXJlIGlzIGV2aWRlbmNlIGZvciBhIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzOiBgc2V0b3NhYCBhbmQgYHZlcnNpY29sb3JgOiBgdCg4NikgPSAtMTAuNTIxLCBwIDwgMi4yZS0xNmAuIFRoZSBtZWFuIG9mIGBzZXRvc2FgIGlzIHNpZ25pZmljYW50bHkgbG93ZXIgdGhhbiB0aGF0IG9mIGB2ZXJzaWNvbG9yYC4NCg0KIyMjIExpbmVhciBNb2RlbA0KDQpMZXQgdXMgcnVuIGEgbGluZWFyIG1vZGVsIG9uIHRoZSBzYW1lIGRhdGENCg0KDQpgYGB7cn0NCmlyaXNTdWIgJT4lIA0KICBsbShTZXBhbC5MZW5ndGggfiBTcGVjaWVzLCBkYXRhID0gLikgJT4lIA0KICBzdW1tYXJ5KCkNCmBgYA0KDQpBbnkgY29tbWVudHM/IGRpc2N1c3Mgd2l0aCB5b3VyIG5laWdoYm91ci4NCg0KVGhlIHJlc3VsdHMgb2YgdGhlIGxpbmVhciBtb2RlbCBhcmUgZXhhY3RseSB0aGUgc2FtZSwgYWxiZWl0IHdpdGggYSBkaWZmZXJlbmNlIGluIHRoZSBzaWduIG9mIHRoZSBkaWZmZXJlbmNlLiBUaGlzIGluZGljYXRlcyB0aGF0IHRoZSAkXGJldGEkIGNvZWZmaWNpZW50IGZvciBgdmVyc2ljb2xvcmAgaXMgc2lnbmlmaWNhbnRseSBoaWdoZXIgdGhhbiB0aGF0IG9mIGBzZXRvc2FgIGJ5IGAwLjkzMDAwYDogYHQoOTgpID0gMTAuNTIsIHAgPCA8MmUtMTZgLg0KDQoNClRoZSBkYXRhc2V0IGBpcmlzYCBjb250YWlucyB0aHJlZSBzcGVjaWVzLiBXZSB3aWxsIHJ1biBhbiBBTk9WQSBhbmQgYSBsaW5lYXIgbW9kZWwgb24gdGhlIGRhdGEuDQoNCiMjIyBCYXNpYyBBTk9WQQ0KDQpXZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgYW92YCB0byBydW4gYW4gQW5hbHlzaXMgb2YgVmFyaWFuY2Ugb24gdGhlIGZ1bGwgZGF0YXNldA0KDQpgYGB7cn0NCmlyaXMgJT4lIA0KICBhb3YoU2VwYWwuTGVuZ3RoIH4gU3BlY2llcywgZGF0YSA9IC4pICU+JSANCiAgc3VtbWFyeSgpDQpgYGANCg0KIyMjIExpbmVhciBtb2RlbA0KDQpXZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgbG1gIHRvIHJ1biBhIGxpbmVhciBtb2RlbA0KDQpgYGB7cn0NCmlyaXMgJT4lIA0KICBsbShTZXBhbC5MZW5ndGggfiBTcGVjaWVzLCBkYXRhID0gLikgJT4lIA0KICBzdW1tYXJ5KCkNCmBgYA0KDQpCdXQgd2FpdC4uLiBIb3cgaXMgdGhlIGxpbmVhciBtb2RlbCBjb21wYXJhYmxlIHRvIHRoZSBhbmFseXNpcyBvZiB2YXJpYW5jZSB3ZSByYW4gYWJvdmU/IFRoaXMgbGluZWFyIG1vZGVsIGRlcml2ZXMgdGhlIGFuYWx5c2lzIG9mIHZhcmlhbmNlIHdlIHNhdyBhYm92ZSwgdXNlIGBhbm92YWAgb24geW91ciBsaW5lYXIgbW9kZWwuLg0KDQpIZXJlIGFyZSB0aGUgcmVzdWx0cyBvZiB0aGUgaW5pdGlhbCBBbmFseXNpcyBvZiB2YXJpYW5jZToNCg0KYGBge3J9DQppcmlzICU+JSANCiAgYW92KFNlcGFsLkxlbmd0aCB+IFNwZWNpZXMsIGRhdGEgPSAuKSAlPiUgDQogIHN1bW1hcnkoKQ0KYGBgDQoNCkFuZCBoZXJlIGFyZSB0aGUgcmVzdWx0cyBvZiB0aGUgbGluZWFyIG1vZGVsIHdpdGggdGhlIGBhbm92YWAgZnVuY3Rpb24NCg0KYGBge3J9DQppcmlzICU+JSANCiAgbG0oU2VwYWwuTGVuZ3RoIH4gU3BlY2llcywgZGF0YSA9IC4pICU+JSANCiAgYW5vdmEoKQ0KYGBgDQoNClRoZXkgYXJlIGV4YWN0bHkgdGhlIHNhbWUuLi4gVGhlIHVuZGVybHlpbmcgb2YgYW4gQW5hbHlzaXMgb2YgdmFyaWFuY2UgaXMgYSBsaW5lYXIgbW9kZWwuIFdlIHdpbGwgY29udGludWUgd2l0aCBhIGxpbmVhciBtb2RlbCB0byB1bmRlcnN0YW5kIGl0IGJldHRlcg0KDQojIyBMaW5lYXIgTW9kZWwNCg0KVGhlIGJhc2ljIGFzc3VtcHRpb24gb2YgYSBMaW5lYXIgbW9kZWwgaXMgdG8gY3JlYXRlIGEgcmVncmVzc2lvbiBhbmFseXNpcyBvbiB0aGUgZGF0YS4gV2UgaGF2ZSBhbiBvdXRjb21lIChvciBkZXBlbmRlbnQgdmFyaWFibGUpIGFuZCBhIHByZWRpY3RvciAob3IgYW4gaW5kZXBlbmRlbnQgdmFyaWFibGUpLiBUaGUgZm9ybXVsYSBvZiBhIGxpbmVhciBtb2RlbCBpcyBhcyBmb2xsb3dzIGBvdXRjb21lIH4gcHJlZGljdG9yYCB0aGF0IGNhbiBiZSByZWFkIGFzICJvdXRjb21lIGFzIGEgZnVuY3Rpb24gb2YgdGhlIHByZWRpY3RvciIuIFdlIGNhbiBhZGQgIjEiIHRvIHNwZWNpZnkgYW4gaW50ZXJjZXB0LCBidXQgdGhpcyBpcyBieSBkZWZhdWx0IGFkZGVkIHRvIHRoZSBtb2RlbA0KDQojIyMgTW9kZWwgZXN0aW1hdGlvbg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCm1kbC5sbSA8LSBpcmlzICU+JSANCiAgbG0oU2VwYWwuTGVuZ3RoIH4gU3BlY2llcywgZGF0YSA9IC4pDQojIHNhbWUgYXMgYmVsb3cuDQojbWRsLmxtIDwtIGxtKFNlcGFsLkxlbmd0aCB+IDEgKyBTcGVjaWVzLCBkYXRhID0gaXJpcykNCm1kbC5sbSAjYWxzbyBwcmludChtZGwubG0pDQpzdW1tYXJ5KG1kbC5sbSkNCmBgYA0KDQojIyMgVGlkeWluZyB0aGUgb3V0cHV0DQoNCmBgYHtyfQ0KIyBmcm9tIGxpYnJhcnkoYnJvb20pDQp0aWR5KG1kbC5sbSkgJT4lIA0KICBzZWxlY3QodGVybSwgZXN0aW1hdGUpICU+JSANCiAgbXV0YXRlKGVzdGltYXRlID0gcm91bmQoZXN0aW1hdGUsIDMpKQ0KbXljb2VmRSA8LSB0aWR5KG1kbC5sbSkgJT4lIHB1bGwoZXN0aW1hdGUpDQoNCmBgYA0KDQoNCg0KVG8gaW50ZXJwcmV0IHRoZSBtb2RlbCwgd2UgbmVlZCBsb29rIGF0IHRoZSBjb2VmZmljaWVudHMuIFRoZSBgSW50ZXJjZXB0YCAoPVNldG9zYSkgaXMgYHIgbXljb2VmRVsxXWAgYW5kIHRoZSBjb2VmZmljaWVudHMgZm9yIGBWZXJzaWNvbG9yYCBhbmQgZm9yIGBWaXJnaW5pY2FgIGFyZSByZXNwZWN0aXZlbHkgYHIgbXljb2VmRVsyXWAgYW5kIGByIG15Y29lZkVbM11gIFRoaXMgdGVsbHMgdXMgdGhhdCBjb21wYXJlZCB0byBgU2V0b3NhYCwgbW92aW5nIGZyb20gdGhpcyBjYXRlZ29yeSB0byBgVmVyc2ljb2xvcmAgbGVhZHMgdG8gYSBzaWduaWZpY2FudCBpbmNyZWFzZSBieSBgciBteWNvZWZFWzJdYCwgYW5kIGZvciBgVmlyZ2luaWNhYCwgdGhlcmUgaXMgYSBzaWduaWZpY2FudCBpbmNyZWFzZSBieSBgciBteWNvZWZFWzNdYC4gDQoNCiMjIyBPYnRhaW5pbmcgb3VyICJ0cnVlIiBjb2VmZmljaWVudHMNCg0KQnV0IHdoZXJlIGFyZSBvdXIgYWN0dWFsIHZhbHVlcyBiYXNlZCBvbiB0aGUgbWVhbnMgaW4gdGhlIHRhYmxlIGFib3ZlPw0KDQpXZSBydW4gYSBtb2RlbCB0aGF0IHN1cHByZXNzZXMgdGhlIGludGVyY2VwdCAoaS5lLiwgYWRkaW5nIDAgaW5zdGVhZCBvZiAxKSBhbmQgdGhpcyB3aWxsIGFsbG93IHVzIHRvIG9idGFpbiB0aGUgInRydWUiIGNvZWZmaWNpZW50cyBmb3IgZWFjaCBsZXZlbCBvZiBvdXIgcHJlZGljdG9yLiBUaGlzIGlzIGFsc28ga25vd24gYXMgYSBgc2F0dXJhdGVkYCBtb2RlbA0KDQpgYGB7cn0NCm1kbC5sbS4yIDwtIGlyaXMgJT4lIA0KICBsbShTZXBhbC5MZW5ndGggfiAwICsgU3BlY2llcywgZGF0YSA9IC4pDQpzdW1tYXJ5KG1kbC5sbS4yKQ0KYGBgDQoNClRoaXMgbWF0Y2hlcyB0aGUgb3JpZ2luYWwgZGF0YS4gYFNldG9zYWAgaGFzIGEgbWVhbiBvZiBgciBteWNvZWZFWzFdYCwgYFZlcnNpY29sb3JgIGByIG15Y29lZkVbMV0gKyBteWNvZWZFWzJdYCwgYW5kIGBWaXJnaW5pY2FgIGByIG15Y29lZkVbMV0gKyBteWNvZWZFWzJdYC4gU2VlIHRhYmxlIGFib3ZlIGFuZCBjb2VmZmljaWVudHMgYmVsb3cgDQoNCmBgYHtyfQ0KI1NldG9zYQ0KbXljb2VmRVsxXQ0KI1ZlcnNpY29sb3INCm15Y29lZkVbMV0gKyBteWNvZWZFWzJdDQojVmlyZ2luaWNhDQpteWNvZWZFWzFdICsgbXljb2VmRVszXQ0KYGBgDQoNClRoZSBzYW1lIGFzDQoNCmBgYHtyfQ0KdGlkeShtZGwubG0uMikgJT4lIA0KICBzZWxlY3QodGVybSwgZXN0aW1hdGUpICU+JSANCiAgbXV0YXRlKGVzdGltYXRlID0gcm91bmQoZXN0aW1hdGUsIDMpKQ0KbXljb2VmRSA8LSB0aWR5KG1kbC5sbS4yKSAlPiUNCiAgcHVsbChlc3RpbWF0ZSkNCg0KI1NldG9zYQ0KbXljb2VmRVsxXQ0KI1ZlcnNpY29sb3INCm15Y29lZkVbMl0NCiNWaXJnaW5pY2ENCm15Y29lZkVbM10NCmBgYA0KDQoNCiMjIyBOaWNlIHRhYmxlIG9mIG91ciBtb2RlbCBzdW1tYXJ5DQoNCldlIGNhbiBhbHNvIG9idGFpbiBhIG5pY2UgdGFibGUgb2Ygb3VyIG1vZGVsIHN1bW1hcnkuIFdlIGNhbiB1c2UgdGhlIHBhY2thZ2UgYGtuaXRyYCBvciBgeHRhYmxlYA0KDQojIyMjIERpcmVjdGx5IGZyb20gbW9kZWwgc3VtbWFyeQ0KDQpgYGB7cn0NCmthYmxlKHN1bW1hcnkobWRsLmxtKSRjb2VmLCBkaWdpdHMgPSAzKQ0KDQpgYGANCg0KIyMjIyBGcm9tIHRoZSBgdGlkeWAgb3V0cHV0DQoNCmBgYHtyfQ0KbWRsLmxtVCA8LSB0aWR5KG1kbC5sbSkNCmthYmxlKG1kbC5sbVQsIGRpZ2l0cyA9IDMpDQpgYGANCg0KDQojIyMgRGlzc2VjdGluZyB0aGUgbW9kZWwNCg0KTGV0IHVzIGRpc3NlY3QgdGhlIG1vZGVsLiBJZiB5b3UgdXNlICJzdHIiLCB5b3Ugd2lsbCBiZSBhYmxlIHRvIHNlZSB3aGF0IGlzIGF2YWlsYWJsZSB1bmRlciBvdXIgbGluZWFyIG1vZGVsLiBUbyBhY2Nlc3Mgc29tZSBpbmZvIGZyb20gdGhlIG1vZGVsDQoNCiMjIyMgInN0ciIgYW5kICJjb2VmIg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCnN0cihtZGwubG0pDQpjb2VmKG1kbC5sbSkNCiMjIHNhbWUgYXMgDQojIyBtZGwubG0kY29lZmZpY2llbnRzDQpgYGANCg0KIyMjIyAiY29lZiIgYW5kICJjb2VmZmljaWVudHMiDQoNCldoYXQgaWYgSSB3YW50IHRvIG9idGFpbiB0aGUgIkludGVyY2VwdCI/IE9yIHRoZSBjb2VmZmljaWVudCBmb3IgZGlzdGFuY2U/IFdoYXQgaWYgSSB3YW50IHRoZSBmdWxsIHJvdyBmb3IgZGlzdGFuY2U/DQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KY29lZihtZGwubG0pWzFdICMgc2FtZSBhcyBtZGwubG0kY29lZmZpY2llbnRzWzFdDQpjb2VmKG1kbC5sbSlbMl0gIyBzYW1lIGFzIG1kbC5sbSRjb2VmZmljaWVudHNbMl0NCg0Kc3VtbWFyeShtZGwubG0pJGNvZWZmaWNpZW50c1syLCBdICMgZnVsbCByb3cNCnN1bW1hcnkobWRsLmxtKSRjb2VmZmljaWVudHNbMiwgNF0gI2ZvciBwIHZhbHVlDQoNCmBgYA0KDQoNCiMjIyMgVXAgdG8geW91DQoNClBsYXkgYXJvdW5kIHdpdGggdGhlIG1vZGVsIHN1bW1hcnkgYW5kIG9idGFpbiB0aGUgdCB2YWx1ZXMgZm9yIHRoZSB0aHJlZSBsZXZlbHMuIFlvdSBjYW4gZG8gdGhpcyBieSByZWZlcnJpbmcgdG8gdGhlIGNvZWZmaWNpZW50IGFzIGFib3ZlDQoNCmBgYHtyfQ0KDQpgYGANCg0KDQojIyMjIFJlc2lkdWFscw0KDQpXaGF0IGFib3V0IHJlc2lkdWFscyAoZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBvYnNlcnZlZCB2YWx1ZSBhbmQgdGhlIGVzdGltYXRlZCB2YWx1ZSBvZiB0aGUgcXVhbnRpdHkpIGFuZCBmaXR0ZWQgdmFsdWVzPw0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmhpc3QocmVzaWR1YWxzKG1kbC5sbSkpDQpxcW5vcm0ocmVzaWR1YWxzKG1kbC5sbSkpOyBxcWxpbmUocmVzaWR1YWxzKG1kbC5sbSkpDQpwbG90KGZpdHRlZChtZGwubG0pLCByZXNpZHVhbHMobWRsLmxtKSwgY2V4ID0gNCkNCmBgYA0KDQojIyMjIEdvb2RuZXNzIG9mIGZpdD8NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQpBSUMobWRsLmxtKQkjIEFrYWlrZSdzIEluZm9ybWF0aW9uIENyaXRlcmlvbiwgbG93ZXIgdmFsdWVzIGFyZSBiZXR0ZXINCkJJQyhtZGwubG0pCSMgQmF5ZXNpYW4gQUlDDQpsb2dMaWsobWRsLmxtKQkjIGxvZyBsaWtlbGlob29kDQpgYGANCg0KDQpPciB1c2UgdGhlIGZvbGxvd2luZyBmcm9tIGBicm9vbWANCg0KYGBge3J9DQpnbGFuY2UobWRsLmxtKQ0KYGBgDQoNCg0KIyMjIyBTaWduaWZpY2FuY2UgdGVzdGluZw0KDQpBcmUgdGhlIGFib3ZlIGluZm9ybWF0aXZlPyBvZiBjb3Vyc2Ugbm90IGRpcmVjdGx5LiBJZiB3ZSB3YW50IHRvIHRlc3QgZm9yIG92ZXJhbGwgc2lnbmlmaWNhbmNlIG9mIG1vZGVsLiBXZSBydW4gYSBudWxsIG1vZGVsIChha2EgaW50ZXJjZXB0IG9ubHkpIGFuZCBjb21wYXJlIG1vZGVscy4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQptZGwubG0uTnVsbCA8LSBpcmlzICU+JSANCiAgbG0oU2VwYWwuTGVuZ3RoIH4gMSwgZGF0YSA9IC4pDQptZGwuY29tcCA8LSBhbm92YShtZGwubG0uTnVsbCwgbWRsLmxtKQ0KbWRsLmNvbXANCmBgYA0KDQpUaGUgcmVzdWx0cyBzaG93IHRoYXQgYWRkaW5nIHRoZSBmYWN0b3IgIlNwZWNpZXMiIGltcHJvdmVzIHRoZSBtb2RlbCBmaXQuIFdlIGNhbiB3cml0ZSB0aGlzIGFzIGZvbGxvd3M6IE1vZGVsIGNvbXBhcmlzb24gc2hvd2VkIHRoYXQgdGhlIGFkZGl0aW9uIG9mIFNwZWNpZXMgaW1wcm92ZWQgdGhlIG1vZGVsIGZpdCB3aGVuIGNvbXBhcmVkIHdpdGggYW4gaW50ZXJjZXB0IG9ubHkgbW9kZWwgKCRGJChgciBtZGwuY29tcFsyLDNdYCkgPSBgciByb3VuZChtZGwuY29tcFsyLDVdLCAyKWAsICpwKiA8IGByIG1kbC5jb21wWzIsNl1gKSANCg0KIyMjIyBQbG90dGluZyBmaXR0ZWQgdmFsdWVzDQoNCiMjIyMjIFRyZW5kIGxpbmUNCg0KTGV0J3MgcGxvdCBvdXIgZml0dGVkIHZhbHVlcyBidXQgb25seSBmb3IgdGhlIHRyZW5kIGxpbmUNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQppcmlzICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gU3BlY2llcywgeSA9IFNlcGFsLkxlbmd0aCkpKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGxhYnMoeCA9ICJTcGVjaWVzIiwgeSA9ICJMZW5ndGgiLCB0aXRsZSA9ICJCb3hwbG90IGFuZCBwcmVkaWN0ZWQgdHJlbmQgbGluZSIsIHN1YnRpdGxlID0gIndpdGggZ2dwbG90MiIpICsgDQogIHRoZW1lX2J3KCkgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpKw0KICBnZW9tX3Ntb290aChhZXMoeCA9IGFzLm51bWVyaWMoU3BlY2llcyksIHkgPSBwcmVkaWN0KG1kbC5sbSkpLCBtZXRob2QgPSAibG0iLCBjb2xvciA9ICJibHVlIikNCmBgYA0KDQpUaGlzIGFsbG93cyB1cyB0byBwbG90IHRoZSBmaXR0ZWQgdmFsdWVzIGZyb20gb3VyIG1vZGVsIHdpdGggdGhlIHByZWRpY3RlZCBsaW5lYXIgdHJlbmQuIFRoaXMgaXMgZXhhY3RseSB0aGUgc2FtZSBhcyBvdXIgb3JpZ2luYWwgZGF0YS4NCg0KIyMjIyMgUHJlZGljdGVkIG1lYW5zIGFuZCB0aGUgdHJlbmQgbGluZQ0KDQpXZSBjYW4gYWxzbyBwbG90IHRoZSBwcmVkaWN0ZWQgbWVhbnMgYW5kIGxpbmVhciB0cmVuZA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmlyaXMgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBTcGVjaWVzLCB5ID0gcHJlZGljdChtZGwubG0pKSkrDQogIGdlb21fYm94cGxvdChjb2xvciA9ICJibHVlIikgKw0KICBsYWJzKHggPSAiU3BlY2llcyIsIHkgPSAiTGVuZ3RoIiwgdGl0bGUgPSAiUHJlZGljdGVkIG1lYW5zIGFuZCB0cmVuZCBsaW5lIiwgc3VidGl0bGUgPSAid2l0aCBnZ3Bsb3QyIikgKyANCiAgdGhlbWVfYncoKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkrDQogIGdlb21fc21vb3RoKGFlcyh4ID0gYXMubnVtZXJpYyhTcGVjaWVzKSwgeSA9IHByZWRpY3QobWRsLmxtKSksIG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gImJsdWUiKQ0KYGBgDQoNCg0KIyMjIyMgUmF3IGRhdGEsIHByZWRpY3RlZCBtZWFucyBhbmQgdGhlIHRyZW5kIGxpbmUNCg0KV2UgY2FuIGFsc28gcGxvdCB0aGUgYWN0dWFsIGRhdGEsIHRoZSBwcmVkaWN0ZWQgbWVhbnMgYW5kIGxpbmVhciB0cmVuZA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmlyaXMgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBTcGVjaWVzLCB5ID0gU2VwYWwuTGVuZ3RoKSkrDQogIGdlb21fYm94cGxvdCgpKw0KICBnZW9tX2JveHBsb3QoYWVzKHggPSBTcGVjaWVzLCB5ID0gcHJlZGljdChtZGwubG0pKSwgY29sb3IgPSAiYmx1ZSIpICsNCiAgbGFicyh4ID0gIlNwZWNpZXMiLCB5ID0gIkxlbmd0aCIsIHRpdGxlID0gIkJveHBsb3QgcmF3IGRhdGEsIHByZWRpY3RlZCBtZWFucyAoaW4gYmx1ZSkgYW5kIHRyZW5kIGxpbmUiLCBzdWJ0aXRsZSA9ICJ3aXRoIGdncGxvdDIiKSArIA0KICB0aGVtZV9idygpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSsNCiAgZ2VvbV9zbW9vdGgoYWVzKHggPSBhcy5udW1lcmljKFNwZWNpZXMpLCB5ID0gcHJlZGljdChtZGwubG0pKSwgbWV0aG9kID0gImxtIiwgY29sb3IgPSAiYmx1ZSIpDQpgYGANCg0KDQojIyMgV2hhdCBhYm91dCBwYWlyd2lzZSBjb21wYXJpc29uPw0KDQpCYXNlZCBvbiBvdXIgbW9kZWwncyBzdW1tYXJ5LCBjYW4geW91IHRlbGwgbWUgaWYgdGhlcmUgaXMgYSBkaWZmZXJlbmNlIGJldHdlZW4gVmVyc2ljb2xvciBhbmQgVmlyZ2luaWNhPw0KDQpgYGB7cn0NCnN1bW1hcnkobWRsLmxtKQ0KYGBgDQoNCg0KYGBge3J9DQptZGwubG0gJT4lIGVtbWVhbnMocGFpcndpc2UgfiBTcGVjaWVzLCBhZGp1c3QgPSAiZmRyIikNCmBgYA0KDQpIb3cgdG8gaW50ZXJwcmV0IHRoZSBvdXRwdXQ/IERpc2N1c3Mgd2l0aCB5b3VyIG5laWdoYm91ciBhbmQgc2hhcmUgd2l0aCB0aGUgZ3JvdXAuDQoNCkhpbnQuLi4gTG9vayBhdCB0aGUgZW1tZWFucyB2YWx1ZXMgZm9yIGVhY2ggbGV2ZWwgb2Ygb3VyIGZhY3RvciAiU3BlY2llcyIgYW5kIHRoZSBjb250cmFzdHMuIA0KDQojIyBPdGhlciBvdXRjb21lcz8NCg0KU28gZmFyLCB3ZSBvbmx5IGxvb2tlZCBhdCAiU2VwYWwuTGVuZ3RoIi4gV2hhdCBhYm91dCB0aGUgb3RoZXIgb3V0Y29tZXM/IGhvdyBpbmZvcm1hdGl2ZSBhcmUgdGhleT8gRG8gd2UgaGF2ZSBzdGF0aXN0aWNhbCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHRocmVlIGxldmVscyBvZiBvdXIgcHJlZGljdG9yPyBZb3UgY2FuIGRvIHRoaXMgaW4geW91ciBzcGFyZSB0aW1lLg0KDQojIyBDb25jbHVzaW9uDQoNCldlIGhhdmUgc28gZmFyIGxvb2tlZCBhdCB0aGUgTGluZWFyIE1vZGVsLiBUaGUgdW5kZXJseWluZyBhc3N1bXB0aW9uIGFib3V0IGxpbmVhciBtb2RlbHMgaXMgdGhhdCB3ZSBoYXZlIGEgbm9ybWFsIChHYXVzc2lhbikgZGlzdHJpYnV0aW9uLiBUaGlzIGlzIHRoZSBtb2RlbCB0byBiZSB1c2VkIHdoZW4gd2UgaGF2ZSBhIGBudW1lcmljYCBvdXRjb21lLiBXaGF0IGlmIG91ciBvdXRjb21lIGlzIG5vdCBudW1lcmljPyBXaGF0IGlmIHdlIGhhdmUgdHdvIGNhdGVnb3JpZXMsIGkuZS4sIGJsYWNrIHZzIHdoaXRlPyBjb3JyZWN0IHZzIGluY29ycmVjdD8geWVzIHZzIG5vPyBUaGVzZSBhcmUgY2F0ZWdvcmljYWwgKipiaW5hcnkqKiBvdXRjb21lLiBXZSBsb29rIGluIHRoZSBuZXh0IHNlY3Rpb24gYXQgTG9naXN0aWMgUmVncmVzc2lvbg0KDQojIEdlbmVyYWxpc2VkIExpbmVhciBNb2RlbHMNCg0KSGVyZSB3ZSB3aWxsIGxvb2sgYXQgYW4gZXhhbXBsZSB3aGVuIHRoZSBvdXRjb21lIGlzIGJpbmFyeS4gVGhpcyBzaW11bGF0ZWQgZGF0YSBpcyBzdHJ1Y3R1cmVkIGFzIGZvbGxvd3MuIFdlIGFza2VkIG9uZSBwYXJ0aWNpcGFudCB0byBsaXN0ZW4gdG8gMTY1IHNlbnRlbmNlcywgYW5kIHRvIGp1ZGdlIHdoZXRoZXIgdGhlc2UgYXJlICJncmFtbWF0aWNhbCIgb3IgInVuZ3JhbW1hdGljYWwiLiBUaGVyZSB3ZXJlIDEwNSBzZW50ZW5jZXMgdGhhdCB3ZXJlICJncmFtbWF0aWNhbCIgYW5kIDYwICJ1bmdyYW1tYXRpY2FsIi4gVGhpcyBmaWN0aXRpb3VzIGV4YW1wbGUgY2FuIGFwcGx5IGluIGFueSBvdGhlciBzaXR1YXRpb24uIExldCdzIHRoaW5rIEdlb2dyYXBoeTogMTY1IGxhbmRzOiAxMDUgImZsYXQiIGFuZCA2MCAibm9uLWZsYXQiLCBldGMuIFRoaXMgYXBwbGllcyB0byBhbnkgY2FzZSB3aGVyZSB5b3UgbmVlZCB0byAiY2F0ZWdvcmlzZSIgdGhlIG91dGNvbWUgaW50byB0d28gZ3JvdXBzLiANCg0KIyMgTG9hZCBhbmQgc3VtbWFyaWVzDQoNCkxldCdzIGxvYWQgaW4gdGhlIGRhdGEgYW5kIGRvIHNvbWUgYmFzaWMgc3VtbWFyaWVzDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KZ3JhbW1hdGljYWwgPC0gcmVhZF9jc3YoImdyYW1tYXRpY2FsLmNzdiIpDQpncmFtbWF0aWNhbA0Kc3RyKGdyYW1tYXRpY2FsKQ0KaGVhZChncmFtbWF0aWNhbCkNCmBgYA0KDQojIyBHTE0NCg0KTGV0J3MgcnVuIGEgZmlyc3QgR0xNIChHZW5lcmFsaXNlZCBMaW5lYXIgTW9kZWwpLiBBIEdMTSB1c2VzIGEgc3BlY2lhbCBmYW1pbHkgImJpbm9taWFsIiBhcyBpdCBhc3N1bWVzIHRoZSBvdXRjb21lIGhhcyBhIGJpbm9taWFsIGRpc3RyaWJ1dGlvbi4gSW4gZ2VuZXJhbCwgcmVzdWx0cyBmcm9tIGEgTG9naXN0aWMgUmVncmVzc2lvbiBhcmUgY2xvc2UgdG8gd2hhdCB3ZSBnZXQgZnJvbSBTRFQgKHNlZSBhYm92ZSkuDQoNClRvIHJ1biB0aGUgcmVzdWx0cywgd2Ugd2lsbCBjaGFuZ2UgdGhlIHJlZmVyZW5jZSBsZXZlbCBmb3IgYm90aCByZXNwb25zZSBhbmQgZ3JhbW1hdGljYWxpdHkuIFRoZSBiYXNpYyBhc3N1bXB0aW9uIGFib3V0IEdMTSBpcyB0aGF0IHdlIHN0YXJ0IHdpdGggb3VyIHJlZmVyZW5jZSBsZXZlbCBiZWluZyB0aGUgIm5vIiByZXNwb25zZXMgdG8gdGhlICJ1bmdyYW1tYXRpY2FsIiBjYXRlZ29yeS4gQW55IGNoYW5nZXMgdG8gdGhpcyByZWZlcmVuY2Ugd2lsbCBiZSBzZWVuIGluIHRoZSBjb2VmZmljaWVudHMgYXMgInllcyIgcmVzcG9uc2VzIHRvIHRoZSAiZ3JhbW1hdGljYWwiIGNhdGVnb3J5Lg0KDQojIyMgTW9kZWwgZXN0aW1hdGlvbiBhbmQgcmVzdWx0cw0KDQpUaGUgcmVzdWx0cyBiZWxvdyBzaG93IHRoZSBsb2dvZGRzIGZvciBvdXIgbW9kZWwuIA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmdyYW1tYXRpY2FsIDwtIGdyYW1tYXRpY2FsICU+JSANCiAgbXV0YXRlKHJlc3BvbnNlID0gZmFjdG9yKHJlc3BvbnNlLCBsZXZlbHMgPSBjKCJubyIsICJ5ZXMiKSksDQogICAgICAgICBncmFtbWF0aWNhbGl0eSA9IGZhY3RvcihncmFtbWF0aWNhbGl0eSwgbGV2ZWxzID0gYygidW5ncmFtbWF0aWNhbCIsICJncmFtbWF0aWNhbCIpKSkNCg0KZ3JhbW1hdGljYWwgJT4lIA0KICBncm91cF9ieShncmFtbWF0aWNhbGl0eSwgcmVzcG9uc2UpICU+JSANCiAgdGFibGUoKQ0KDQptZGwuZ2xtIDwtIGdyYW1tYXRpY2FsICU+JSANCiAgZ2xtKHJlc3BvbnNlIH4gZ3JhbW1hdGljYWxpdHksIGRhdGEgPSAuLCBmYW1pbHkgPSBiaW5vbWlhbCkNCnN1bW1hcnkobWRsLmdsbSkNCg0KdGlkeShtZGwuZ2xtKSAlPiUgDQogIHNlbGVjdCh0ZXJtLCBlc3RpbWF0ZSkgJT4lIA0KICBtdXRhdGUoZXN0aW1hdGUgPSByb3VuZChlc3RpbWF0ZSwgMykpDQojIHRvIG9ubHkgZ2V0IHRoZSBjb2VmZmljaWVudHMNCm15Y29lZjIgPC0gdGlkeShtZGwuZ2xtKSAlPiUgcHVsbChlc3RpbWF0ZSkNCmBgYA0KDQoNClRoZSByZXN1bHRzIHNob3cgdGhhdCBmb3Igb25lIHVuaXQgaW5jcmVhc2UgaW4gdGhlIHJlc3BvbnNlIChpLmUuLCBmcm9tIG5vIHRvIHllcyksIHRoZSBsb2dvZGRzIG9mIGJlaW5nICJncmFtbWF0aWNhbCIgaXMgaW5jcmVhc2VkIGJ5IGByIG15Y29lZjJbMl1gICh0aGUgaW50ZXJjZXB0IHNob3dzIHRoYXQgd2hlbiB0aGUgcmVzcG9uc2UgaXMgIm5vIiwgdGhlIGxvZ29kZHMgYXJlIGByIG15Y29lZjJbMV1gKS4gVGhlIGFjdHVhbCBsb2dvZGRzIGZvciB0aGUgcmVzcG9uc2UgInllcyIgdG8gZ3JhbW1hdGljYWwgaXMgYHIgbXljb2VmMlsxXStteWNvZWYyWzJdYCANCg0KIyMjIExvZ29kZHMgdG8gT2RkIHJhdGlvcw0KDQpMb2dvZGRzIGNhbiBiZSBtb2RpZmllZCB0byB0YWxrIGFib3V0IHRoZSBvZGRzIG9mIGFuIGV2ZW50LiBGb3Igb3VyIG1vZGVsIGFib3ZlLCB0aGUgb2RkcyBvZiAiZ3JhbW1hdGljYWwiIHJlY2VpdmluZyBhICJubyIgcmVzcG9uc2UgaXMgYSBtZXJlIDAuMjsgdGhlIG9kZHMgb2YgImdyYW1tYXRpY2FsIiB0byByZWNlaXZlIGEgInllcyIgaXMgYSAyMDsgaS5lLiwgMjAgdGltZXMgbW9yZSBsaWtlbHkgDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQpleHAobXljb2VmMlsxXSkNCmV4cChteWNvZWYyWzFdICsgbXljb2VmMlsyXSkNCg0KYGBgDQoNCiMjIyBMb2dPZGRzIHRvIHByb3BvcnRpb25zDQoNCklmIHlvdSB3YW50IHRvIHRhbGsgYWJvdXQgdGhlIHBlcmNlbnRhZ2UgImFjY3VyYWN5IiBvZiBvdXIgbW9kZWwsIHRoZW4gd2UgY2FuIHRyYW5zZm9ybSBvdXIgbG9nZ29kZHMgaW50byBwcm9wb3J0aW9ucy4gVGhpcyBzaG93cyB0aGF0IHRoZSBwcm9wb3J0aW9uIG9mICJncmFtbWF0aWNhbCIgcmVjZWl2aW5nIGEgInllcyIgcmVzcG9uc2UgaW5jcmVhc2VzIGJ5IDk5JSAob3IgOTUlIGJhc2VkIG9uIG91ciAidHJ1ZSIgY29lZmZpY2llbnRzKQ0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCnBsb2dpcyhteWNvZWYyWzFdKQ0KcGxvZ2lzKG15Y29lZjJbMV0gKyBteWNvZWYyWzJdKQ0KYGBgDQoNCiMjIyBQbG90dGluZw0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmdyYW1tYXRpY2FsIDwtIGdyYW1tYXRpY2FsICU+JSANCiAgbXV0YXRlKHByb2IgPSBwcmVkaWN0KG1kbC5nbG0sIHR5cGUgPSAicmVzcG9uc2UiKSkNCmdyYW1tYXRpY2FsICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gYXMubnVtZXJpYyhncmFtbWF0aWNhbGl0eSksIHkgPSBwcm9iKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAiZ2xtIiwgDQogICAgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseSA9ICJiaW5vbWlhbCIpLCANCiAgICBzZSA9IFQpICsgdGhlbWVfYncoYmFzZV9zaXplID0gMjApKw0KICAgIGxhYnMoeSA9ICJQcm9iYWJpbGl0eSIsIHggPSAiIikrDQogICAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsMSkpKw0KICAgIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gYygiVW5ncmFtbWF0aWNhbCIsICJHcmFtbWF0aWNhbCIpKQ0KYGBgDQoNCiMjIEFjY3VyYWN5IGFuZCBTaWduYWwgRGV0ZWN0aW9uIFRoZW9yeQ0KDQojIyMgUmF0aW9uYWxlDQoNCldlIGFyZSBnZW5lcmFsbHkgaW50ZXJlc3RlZCBpbiBwZXJmb3JtYW5jZSwgaS5lLiwgd2hldGhlciB0aGUgd2UgaGF2ZSAiYWNjdXJhdGVseSIgY2F0ZWdvcmlzZWQgdGhlIG91dGNvbWUgb3Igbm90IGFuZCBhdCB0aGUgc2FtZSB0aW1lIHdhbnQgdG8gZXZhbHVhdGUgb3VyIGJpYXNlcyBpbiByZXNwb25zZXMuIFdoZW4gZGVjaWRpbmcgb24gY2F0ZWdvcmllcywgd2UgYXJlIHVzdWFsbHkgYmlhc2VkIGluIG91ciBzZWxlY3Rpb24uIA0KDQpMZXQncyBhc2sgdGhlIHF1ZXN0aW9uOiBIb3cgbWFueSBvZiB5b3UgaGF2ZSBhIE1hYyBsYXB0b3AgYW5kIGhvdyBtYW55IGEgV2luZG93cyBsYXB0b3A/IEZvciB0aG9zZSB3aXRoIGEgTWFjLCB3aGF0IHdhcyB0aGUgbWFpbiByZWFzb24gZm9yIGNob29zaW5nIGl0PyBBcmUgeW91IGJpYXNlZCBpbiBhbnl3YXkgYnkgeW91ciBkZWNpc2lvbj8gDQoNClRvIGNvcnJlY3QgZm9yIHRoZXNlIGJpYXNlcywgd2UgdXNlIHNvbWUgdmFyaWFudHMgZnJvbSBTaWduYWwgRGV0ZWN0aW9uIFRoZW9yeSB0byBvYnRhaW4gdGhlIHRydWUgZXN0aW1hdGVzIHdpdGhvdXQgYmVpbmcgaW5mbHVlbmNlZCBieSB0aGUgYmlhc2VzLiANCg0KIyMjIFJ1bm5pbmcgc3RhdHMNCg0KTGV0J3MgZG8gc29tZSBzdGF0cyBvbiB0aGlzIA0KDQp8ICB8IFllcyB8IE5vIHwgVG90YWwgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgR3JhbW1hdGljYWwgKFllcyBBY3R1YWwpIHwgVFAgPSAxMDAgfCBGTiA9IDUgfCAoWWVzIEFjdHVhbCkgMTA1IHwNCnwgVW5ncmFtbWF0aWNhbCAoTm8gQWN0dWFsKSAgfCBGUCA9IDEwIHwgVE4gPSA1MCB8IChObyBBY3R1YWwpIDYwIHwNCnwgVG90YWwgfCAoWWVzIFJlc3BvbnNlKSAxMTAgfCAoTm8gUmVzcG9uc2UpIDU1IHwgMTY1IHwNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQpncmFtbWF0aWNhbCA8LSBncmFtbWF0aWNhbCAlPiUgDQogIG11dGF0ZShyZXNwb25zZSA9IGZhY3RvcihyZXNwb25zZSwgbGV2ZWxzID0gYygieWVzIiwgIm5vIikpLA0KICAgICAgICAgZ3JhbW1hdGljYWxpdHkgPSBmYWN0b3IoZ3JhbW1hdGljYWxpdHksIGxldmVscyA9IGMoImdyYW1tYXRpY2FsIiwgInVuZ3JhbW1hdGljYWwiKSkpDQoNCiMjIFRQID0gVHJ1ZSBQb3NpdGl2ZSAoSGl0KTsgRlAgPSBGYWxzZSBQb3NpdGl2ZTsgRk4gPSBGYWxzZSBOZWdhdGl2ZTsgVE4gPSBUcnVlIE5lZ2F0aXZlDQoNCg0KVFAgPC0gbnJvdyhncmFtbWF0aWNhbCAlPiUgDQogICAgICAgICAgICAgZmlsdGVyKGdyYW1tYXRpY2FsaXR5ID09ICJncmFtbWF0aWNhbCIgJg0KICAgICAgICAgICAgICAgICAgICAgIHJlc3BvbnNlID09ICJ5ZXMiKSkNCkZOIDwtIG5yb3coZ3JhbW1hdGljYWwgJT4lIA0KICAgICAgICAgICAgIGZpbHRlcihncmFtbWF0aWNhbGl0eSA9PSAiZ3JhbW1hdGljYWwiICYNCiAgICAgICAgICAgICAgICAgICAgICByZXNwb25zZSA9PSAibm8iKSkNCkZQIDwtIG5yb3coZ3JhbW1hdGljYWwgJT4lIA0KICAgICAgICAgICAgIGZpbHRlcihncmFtbWF0aWNhbGl0eSA9PSAidW5ncmFtbWF0aWNhbCIgJg0KICAgICAgICAgICAgICAgICAgICAgIHJlc3BvbnNlID09ICJ5ZXMiKSkNClROIDwtIG5yb3coZ3JhbW1hdGljYWwgJT4lIA0KICAgICAgICAgICAgIGZpbHRlcihncmFtbWF0aWNhbGl0eSA9PSAidW5ncmFtbWF0aWNhbCIgJg0KICAgICAgICAgICAgICAgICAgICAgIHJlc3BvbnNlID09ICJubyIpKQ0KVFANCkZODQpGUA0KVE4NCg0KVG90YWwgPC0gbnJvdyhncmFtbWF0aWNhbCkNClRvdGFsDQooVFArVE4pL1RvdGFsICMgYWNjdXJhY3kNCihGUCtGTikvVG90YWwgIyBlcnJvciwgYWxzbyAxLWFjY3VyYWN5DQoNCiMgV2hlbiBzdGltdWx1cyA9IHllcywgaG93IG1hbnkgdGltZXMgcmVzcG9uc2UgPSB5ZXM/DQpUUC8oVFArRk4pICMgYWxzbyBUcnVlIFBvc2l0aXZlIFJhdGUgb3IgU3BlY2lmaWNpdHkNCg0KIyBXaGVuIHN0aW11bHVzID0gbm8sIGhvdyBtYW55IHRpbWVzIHJlc3BvbnNlID0geWVzPw0KRlAvKEZQK1ROKSAjIEZhbHNlIFBvc2l0aXZlIFJhdGUsIA0KDQojIFdoZW4gc3RpbXVsdXMgPSBubywgaG93IG1hbnkgdGltZXMgcmVzcG9uc2UgPSBubz8NClROLyhGUCtUTikgIyBUcnVlIE5lZ2F0aXZlIFJhdGUgb3IgU2Vuc2l0aXZpdHkgDQoNCiMgV2hlbiBzdWJqZWN0IHJlc3BvbmRzICJ5ZXMiIGhvdyBtYW55IHRpbWVzIGlzIChzKWhlIGNvcnJlY3Q/DQpUUC8oVFArRlApICMgcHJlY2lzaW9uDQoNCiMgZ2V0dGluZyBkcHJpbWUgKG9yIHRoZSBzZW5zZXRpdml0eSBpbmRleCk7IGJldGEgKGJpYXMgY3JpdGVyaW9uLCAwLTEsIGxvd2VyPWluY3JlYXNlIGluICJ5ZXMiKTsgQXByaW1lIChlc3RpbWF0ZSBvZiBkaXNjcmltaW5hYmlsaXR5LCAwLTEsIDE9Z29vZCBkaXNjcmltaW5hdGlvbjsgMCBhdCBjaGFuY2UpOyBicHBkIChiIHByaW1lIHByaW1lIGQsIC0xIHRvIDE7IDAgPSBubyBiaWFzLCBuZWdhdGl2ZSA9IHRlbmRlbmN5IHRvIHJlc3BvbmQgInllcyIsIHBvc2l0aXZlID0gdGVuZGVuY3kgdG8gcmVzcG9uZCAibm8iKTsgYyAoaW5kZXggb2YgYmlhcywgZXF1YWxzIHRvIFNEKQ0KIyhzZWUgYWxzbyBodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS9jb21wdXRlLXNpZ25hbC1kZXRlY3Rpb24tdGhlb3J5LWluZGljZXMtd2l0aC1yL2FtcC8pIA0KcHN5Y2hvOjpkcHJpbWUoVFAsIEZQLCBGTiwgVE4sIA0KICAgICAgICAgICAgICAgbl90YXJnZXRzID0gVFArRk4sIA0KICAgICAgICAgICAgICAgbl9kaXN0cmFjdG9ycyA9IEZQK1ROLA0KICAgICAgICAgICAgICAgYWRqdXN0PUYpDQoNCmBgYA0KDQpUaGUgbW9zdCBpbXBvcnRhbnQgZnJvbSBhYm92ZSwgaXMgZC1wcmltZS4gVGhpcyBpcyBtb2RlbGxpbmcgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmF0ZSBvZiAiVHJ1ZSBQb3NpdGl2ZSIgcmVzcG9uc2VzIGFuZCAiRmFsc2UgUG9zaXRpdmUiIHJlc3BvbnNlcyBpbiBzdGFuZGFyZCB1bml0IChvciB6LXNjb3JlcykuIFRoZSBmb3JtdWxhIGNhbiBiZSB3cml0dGVuIGFzOg0KDQpgZCcgKGQgcHJpbWUpID0gWihUcnVlIFBvc2l0aXZlIFJhdGUpIC0gWihGYWxzZSBQb3NpdGl2ZSBSYXRlKWANCg0KIyMjIEdMTSBhcyBhIGNsYXNzaWZpY2F0aW9uIHRvb2wNCg0KVGhlIGNvZGUgYmVsb3cgZGVtb25zdHJhdGVzIHRoZSBsaW5rcyBiZXR3ZWVuIG91ciBHTE0gbW9kZWwgYW5kIHdoYXQgd2UgaGFkIG9idGFpbmVkIGFib3ZlIGZyb20gU0RULiBUaGUgcHJlZGljdGlvbnMnIHRhYmxlIHNob3dzIHRoYXQgb3VyIEdMTSB3YXMgc3VjY2Vzc2Z1bCBhdCBvYnRhaW5pbmcgcHJlZGljdGlvbiB0aGF0IGFyZSBpZGVudGljYWwgdG8gb3VyIGluaXRpYWwgZGF0YSBzZXR1cC4gTG9vayBhdCB0aGUgdGFibGUgaGVyZSBhbmQgdGhlIHRhYmxlIGFib3ZlLiBPbmNlIHdlIGhhdmUgY3JlYXRlZCBvdXIgdGFibGUgb2Ygb3V0Y29tZSwgd2UgY2FuIGNvbXB1dGUgcGVyY2VudCBjb3JyZWN0LCB0aGUgc3BlY2lmaWNpdHksIHRoZSBzZW5zaXRpdml0eSwgdGhlIEthcHBhIHNjb3JlLCBldGMuLiB0aGlzIHlpZWxkcyB0aGUgYWN0dWFsIHZhbHVlIHdpdGggdGhlIFNEIHRoYXQgaXMgcmVsYXRlZCB0byB2YXJpYXRpb25zIGluIHJlc3BvbnNlcy4gDQoNCmBgYHtyfQ0KIyMgcHJlZGljdChtZGwuZ2xtKT4wLjUgaXMgaWRlbnRpY2FsIHRvIA0KIyMgcHJlZGljdChnbG0ocmVzcG9uc2V+Z3JhbW1hdGljYWxpdHksZGF0YT1ncmFtbWF0aWNhbCxmYW1pbHkgPSBiaW5vbWlhbCksdHlwZT0icmVzcG9uc2UiKQ0KZ3JhbW1hdGljYWwgPC0gZ3JhbW1hdGljYWwgJT4lIA0KICBtdXRhdGUocmVzcG9uc2UgPSBmYWN0b3IocmVzcG9uc2UsIGxldmVscyA9IGMoInllcyIsICJubyIpKSwNCiAgICAgICAgIGdyYW1tYXRpY2FsaXR5ID0gZmFjdG9yKGdyYW1tYXRpY2FsaXR5LCBsZXZlbHMgPSBjKCJncmFtbWF0aWNhbCIsICJ1bmdyYW1tYXRpY2FsIikpKQ0KDQoNCg0KbWRsLmdsbS5DIDwtIGdyYW1tYXRpY2FsICU+JSANCiAgZ2xtKHJlc3BvbnNlIH4gZ3JhbW1hdGljYWxpdHksIGRhdGEgPSAuLGZhbWlseSA9IGJpbm9taWFsKQ0KDQp0YmwuZ2xtIDwtIHRhYmxlKGdyYW1tYXRpY2FsJHJlc3BvbnNlLCBwcmVkaWN0KG1kbC5nbG0uQywgdHlwZSA9ICJyZXNwb25zZSIpPjAuNSkNCmNvbG5hbWVzKHRibC5nbG0pIDwtIGMoImdyYW1tYXRpY2FsIiwgInVuZ3JhbW1hdGljYWwiKQ0KdGJsLmdsbQ0KUHJlc2VuY2VBYnNlbmNlOjpwY2ModGJsLmdsbSkNClByZXNlbmNlQWJzZW5jZTo6c3BlY2lmaWNpdHkodGJsLmdsbSkNClByZXNlbmNlQWJzZW5jZTo6c2Vuc2l0aXZpdHkodGJsLmdsbSkNCiMjI2V0Yy4uDQpgYGANCg0KSWYgeW91IGxvb2sgYXQgdGhlIHJlc3VsdHMgZnJvbSBTRFQgYWJvdmUsIHRoZXNlIHJlc3VsdHMgYXJlIHRoZSBzYW1lIGFzDQp0aGUgZm9sbG93aW5nDQoNCkFjY3VyYWN5OiAoVFArVE4pL1RvdGFsIChgciAoVFArVE4pL1RvdGFsYCkgDQoNClRydWUgUG9zaXRpdmUgUmF0ZSAob3IgU3BlY2lmaWNpdHkpIFRQLyhUUCtGTikgKGByIFRQLyhUUCtGTilgKQ0KDQpUcnVlIE5lZ2F0aXZlIFJhdGUgKG9yIFNlbnNpdGl2aXR5KSBUTi8oRlArVE4pIChgciBUTi8oRlArVE4pYCkgDQoNCiMjIyBHTE0gYW5kIGQgcHJpbWUNCg0KVGhlIHZhbHVlcyBvYnRhaW5lZCBoZXJlIG1hdGNoIHRob3NlIG9idGFpbmVkIGZyb20gU0RULiBGb3IgZCBwcmltZSwgdGhlIGRpZmZlcmVuY2Ugc3RlbXMgZnJvbSB0aGUgdXNlIG9mIHRoZSBsb2dpdCB2YXJpYW50IG9mIHRoZSBCaW5vbWlhbCBmYW1pbHkuIEJ5IHVzaW5nIGEgcHJvYml0IHZhcmlhbnQsIG9uZSBvYnRhaW5zIHRoZSBzYW1lIHZhbHVlcyAoW3NlZSBoZXJlXShodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvci9kYWUvcHJvYml0LXJlZ3Jlc3Npb24vKSBmb3IgbW9yZSBkZXRhaWxzKS4gQSBwcm9iaXQgdmFyaWFudCBtb2RlbHMgdGhlIHotc2NvcmUgZGlmZmVyZW5jZXMgaW4gdGhlIG91dGNvbWUgYW5kIGlzIGV2YWx1YXRlZCBpbiBjaGFuZ2UgaW4gMS1zdGFuZGFyZCB1bml0LiBUaGlzIGlzIG1vZGVsbGluZyB0aGUgY2hhbmdlIGZyb20gInVuZ3JhbW1hdGljYWwiICJubyIgcmVzcG9uc2VzIGludG8gImdyYW1tYXRpY2FsIiAieWVzIiByZXNwb25zZXMgaW4gei1zY29yZXMuIFRoZSBzYW1lIGNvbmNlcHR1YWwgdW5kZXJwaW5uaW5ncyBvZiBkLXByaW1lIGZyb20gU2lnbmFsIERldGVjdGlvbiBUaGVvcnkuDQoNCmBgYHtyfQ0KIyMgZCBwcmltZQ0KcHN5Y2hvOjpkcHJpbWUoVFAsIEZQLCBGTiwgVE4sIA0KICAgICAgICAgICAgICAgbl90YXJnZXRzID0gVFArRk4sIA0KICAgICAgICAgICAgICAgbl9kaXN0cmFjdG9ycyA9IEZQK1ROLA0KICAgICAgICAgICAgICAgYWRqdXN0PUYpJGRwcmltZQ0KDQojIyBHTE0gd2l0aCBwcm9iaXQNCmNvZWYoZ2xtKHJlc3BvbnNlIH4gZ3JhbW1hdGljYWxpdHksIGRhdGEgPSBncmFtbWF0aWNhbCwgZmFtaWx5ID0gYmlub21pYWwocHJvYml0KSkpWzJdDQoNCmBgYA0KDQoNCg0KDQojIyBHTE06IE90aGVyIGRpc3RyaWJ1dGlvbnMNCg0KSWYgeW91ciBkYXRhIGRvZXMgbm90IGZpdCBhIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiwgYW5kIGlzIGEgbXVsdGlub21pYWwgKGkuZS4sIHRocmVlIG9yIG1vcmUgcmVzcG9uc2UgY2F0ZWdvcmllcykgb3IgcG9pc3NvbiAoY291bnQgZGF0YSksIHRoZW4geW91IG5lZWQgdG8gdXNlIHRoZSBnbG0gZnVuY3Rpb24gd2l0aCBhIHNwZWNpZmljIGZhbWlseSBmdW5jdGlvbi4gDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCBlY2hvPUZBTFNFfQ0KIyMgRm9yIGEgbXVsdGlub21pYWwgKDMgb3IgbW9yZSByZXNwb25zZSBjYXRlZ29yaWVzKSwgc2VlIGJlbG93IGFuZCB1c2UgdGhlIGZvbGxvd2luZyBzcGVjaWZpY2F0aW9uDQojIyBodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvci9kYWUvbXVsdGlub21pYWwtbG9naXN0aWMtcmVncmVzc2lvbi8NCiMjIG1kbC5tdWx0aSA8LSBubmV0OjptdWx0aW5vbShvdXRjb21lfnByZWRpY3RvciwgZGF0YT1kYXRhKQ0KDQojIyBGb3IgYSBwb2lzc29uIChjb3VudCBkYXRhKSwgc2VlIGJlbG93IGFuZCB1c2UgdGhlIGZvbGxvd2luZyBzcGVjaWZpY2F0aW9uDQojIyBodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvci9kYWUvcG9pc3Nvbi1yZWdyZXNzaW9uLw0KDQojIyBtZGwucG9pc3NvbiA8LSBnbG0ob3V0Y29tZX5wcmVkaWN0b3IsIGRhdGEgPSBkYXRhLCBmYW1pbHkgPSAicG9pc3NvbiIpDQoNCg0KYGBgDQoNCg0KDQojIEN1bXVsYXRpdmUgTG9naXQgTGluayBNb2RlbHMNCg0KVGhlc2UgbW9kZWxzIHdvcmsgcGVyZmVjdGx5IHdpdGggcmF0aW5nIGRhdGEuIFJhdGluZ3MgYXJlIGluaGVyZW50bHkgb3JkZXJlZCwgMSwgMiwgLi4uIG4sIGFuZCBleHBlY3QgdG8gb2JzZXJ2ZSBhbiBpbmNyZWFzZSAob3IgZGVjcmVhc2UpIGluIG92ZXJhbGwgcmF0aW5ncyBmcm9tIDEgdG8gbi4gVG8gZGVtb25zdHJhdGUgdGhpcywgd2Ugd2lsbCB1c2UgYW4gZXhhbXBsZSB1c2luZyB0aGUgcGFja2FnZSAib3JkaW5hbCIuIERhdGEgd2VyZSBmcm9tIGEgcmF0aW5nIGV4cGVyaW1lbnQgd2hlcmUgc2l4IHBhcnRpY2lwYW50cyByYXRlZCB0aGUgcGVyY2VwdCBvZiBuYXNhbGl0eSBpbiB0aGUgcHJvZHVjdGlvbiBvZiBwYXJ0aWN1bGFyIGNvbnNvbmFudHMgaW4gQXJhYmljLiBUaGUgZGF0YSBjYW1lIGZyb20gbmluZSBwcm9kdWNpbmcgc3ViamVjdHMuIFRoZSByYXRpbmdzIHdlcmUgZnJvbSAxIHRvIDUuIFRoaXMgZXhhbXBsZSBjYW4gYXBwbHkgdG8gYW55IHN0dWR5LCBlLmcuLCByYXRpbmcgZ3JhbW1hdGljYWxpdHkgb2Ygc2VudGVuY2VzLCByYXRpbmcgaG93IHBvc2l0aXZlIHRoZSBzZW50aW1lbnRzIGFyZSBpbiBhIGFydGljbGUsIGludGVydmlldyByZXNwb25zZXMsIGV0Yy4NCg0KIyMgSW1wb3J0aW5nIGFuZCBwcmUtcHJvY2Vzc2luZw0KDQpXZSBzdGFydCBieSBpbXBvcnRpbmcgdGhlIGRhdGEgYW5kIHByb2Nlc3MgaXQuIFdlIGNoYW5nZSB0aGUgcmVmZXJlbmNlIGxldmVsIGluIHRoZSBwcmVkaWN0b3INCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQpyYXRpbmcgPC0gcmVhZF9jc3YoInJhdGluZy5jc3YiKQ0KcmF0aW5nDQpyYXRpbmcgPC0gcmF0aW5nICU+JSANCiAgbXV0YXRlKFJlc3BvbnNlID0gZmFjdG9yKFJlc3BvbnNlKSwNCiAgICAgICAgIENvbnRleHQgPSBmYWN0b3IoQ29udGV4dCkpICU+JSANCiAgbXV0YXRlKENvbnRleHQgPSByZWxldmVsKENvbnRleHQsICJpc29sYXRpb24iKSkNCnJhdGluZw0KYGBgDQoNCiMjIE91ciBmaXJzdCBtb2RlbA0KDQpXZSBydW4gb3VyIGZpcnN0IGNsbSBtb2RlbCBhcyBhIHNpbXBsZSwgaS5lLiwgd2l0aCBubyByYW5kb20gZWZmZWN0cw0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCm1kbC5jbG0gPC0gcmF0aW5nICU+JSANCiAgY2xtKFJlc3BvbnNlIH4gQ29udGV4dCwgZGF0YSA9IC4pDQpzdW1tYXJ5KG1kbC5jbG0pDQpgYGANCg0KDQojIyBUZXN0aW5nIHNpZ25pZmljYW5jZSANCg0KV2UgY2FuIGV2YWx1YXRlIHdoZXRoZXIgIkNvbnRleHQiIGltcHJvdmVzIHRoZSBtb2RlbCBmaXQsIGJ5IGNvbXBhcmluZyBhIG51bGwgbW9kZWwgd2l0aCBvdXIgbW9kZWwuIE9mIGNvdXJzZSAiQ29udGV4dCIgaXMgaW1wcm92aW5nIHRoZSBtb2RlbCBmaXQuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KbWRsLmNsbS5OdWxsIDwtIHJhdGluZyAlPiUgDQogIGNsbShSZXNwb25zZSB+IDEsIGRhdGEgPSAuKQ0KYW5vdmEobWRsLmNsbSwgbWRsLmNsbS5OdWxsKQ0KDQpgYGANCg0KIyMgSW50ZXJwcmV0aW5nIGEgY3VtdWxhdGl2ZSBtb2RlbA0KDQpBcyBhIHdheSB0byBpbnRlcnByZXQgdGhlIG1vZGVsLCB3ZSBjYW4gbG9vayBhdCB0aGUgY29lZmZpY2llbnRzIGFuZCBtYWtlIHNlbnNlIG9mIHRoZSByZXN1bHRzLiBBIENMTSBtb2RlbCBpcyBhIExvZ2lzdGljIG1vZGVsIHdpdGggYSBjdW11bGF0aXZlIGVmZmVjdC4gVGhlICJDb2VmZmljaWVudHMiIGFyZSB0aGUgZXN0aW1hdGVzIGZvciBlYWNoIGxldmVsIG9mIHRoZSBmaXhlZCBlZmZlY3Q7IHRoZSAiVGhyZXNob2xkIGNvZWZmaWNpZW50cyIgYXJlIHRob3NlIG9mIHRoZSByZXNwb25zZS4gRm9yIHRoZSBmb3JtZXIsIGEgbmVnYXRpdmUgY29lZmZpY2llbnQgaW5kaWNhdGVzIGEgbmVnYXRpdmUgYXNzb2NpYXRpb24gd2l0aCB0aGUgcmVzcG9uc2U7IGFuZCBhIHBvc2l0aXZlIGlzIHBvc2l0aXZlbHkgYXNzb2NpYXRlZCB3aXRoIHRoZSByZXNwb25zZS4gVGhlIHAgdmFsdWVzIGFyZSBpbmRpY2F0aW5nIHRoZSBzaWduaWZpY2FuY2Ugb2YgZWFjaCBsZXZlbC4gRm9yIHRoZSAiVGhyZXNob2xkIGNvZWZmaWNpZW50cyIsIHdlIGNhbiBzZWUgdGhlIGN1bXVsYXRpdmUgZWZmZWN0cyBvZiByYXRpbmdzIDF8MiwgMnwzLCAzfDQgYW5kIDR8NSB3aGljaCBpbmRpY2F0ZSBhbiBvdmVyYWxsIGluY3JlYXNlIGluIHRoZSByYXRpbmdzIGZyb20gMSB0byA1LiANCg0KIyMgUGxvdHRpbmcgDQoNCldlIHVzZSBhIG1vZGlmaWVkIHZlcnNpb24gb2YgYSBwbG90dGluZyBmdW5jdGlvbiB0aGF0IGFsbG93cyB1cyB0byB2aXN1YWxpc2UgdGhlIGVmZmVjdHMuIEZvciB0aGlzLCB3ZSB1c2UgdGhlIGJhc2UgUiBwbG90dGluZyBmdW5jdGlvbnMNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQpwYXIob21hPWMoMSwgMCwgMCwgMyksbWdwPWMoMiwgMSwgMCkpDQp4bGltTmFzID0gYyhtaW4obWRsLmNsbSRiZXRhKSwgbWF4KG1kbC5jbG0kYmV0YSkpDQp5bGltTmFzID0gYygwLDEpDQpwbG90KDAsMCx4bGltPXhsaW1OYXMsIHlsaW09eWxpbU5hcywgdHlwZT0ibiIsIHlsYWI9ZXhwcmVzc2lvbihQcm9iYWJpbGl0eSksIHhsYWI9IiIsIHhheHQgPSAibiIsbWFpbj0iUHJlZGljdGVkIGN1cnZlcyAtIE5hc2FsaXNhdGlvbiIsY2V4PTIsY2V4LmxhYj0xLjUsY2V4Lm1haW49MS41LGNleC5heGlzPTEuNSkNCmF4aXMoc2lkZSA9IDEsIGF0ID0gYygwLG1kbC5jbG0kYmV0YSksbGFiZWxzID0gbGV2ZWxzKHJhdGluZyRDb250ZXh0KSwgbGFzPTIsY2V4PTIsY2V4LmxhYj0xLjUsY2V4LmF4aXM9MS41KQ0KeHNOYXMgPSBzZXEoeGxpbU5hc1sxXSwgeGxpbU5hc1syXSwgbGVuZ3RoLm91dD0xMDApDQpsaW5lcyh4c05hcywgcGxvZ2lzKG1kbC5jbG0kVGhldGFbMV0gLSB4c05hcyksIGNvbD0nYmxhY2snKQ0KbGluZXMoeHNOYXMsIHBsb2dpcyhtZGwuY2xtJFRoZXRhWzJdIC0geHNOYXMpLXBsb2dpcyhtZGwuY2xtJFRoZXRhWzFdIC0geHNOYXMpLCBjb2w9J3JlZCcpDQpsaW5lcyh4c05hcywgcGxvZ2lzKG1kbC5jbG0kVGhldGFbM10gLSB4c05hcyktcGxvZ2lzKG1kbC5jbG0kVGhldGFbMl0gLSB4c05hcyksIGNvbD0nZ3JlZW4nKQ0KbGluZXMoeHNOYXMsIHBsb2dpcyhtZGwuY2xtJFRoZXRhWzRdIC0geHNOYXMpLXBsb2dpcyhtZGwuY2xtJFRoZXRhWzNdIC0geHNOYXMpLCBjb2w9J29yYW5nZScpDQpsaW5lcyh4c05hcywgMS0ocGxvZ2lzKG1kbC5jbG0kVGhldGFbNF0gLSB4c05hcykpLCBjb2w9J2JsdWUnKQ0KYWJsaW5lKHY9YygwLG1kbC5jbG0kYmV0YSksbHR5PTMpDQphYmxpbmUoaD0wLCBsdHk9ImRhc2hlZCIpDQphYmxpbmUoaD0xLCBsdHk9ImRhc2hlZCIpDQpsZWdlbmQocGFyKCd1c3InKVsyXSwgcGFyKCd1c3InKVs0XSwgYnR5PSduJywgeHBkPU5BLGx0eT0xLCBjb2w9YygiYmxhY2siLCAicmVkIiwgImdyZWVuIiwgIm9yYW5nZSIsICJibHVlIiksIA0KICAgICAgIGxlZ2VuZD1jKCJPcmFsIiwgIjIiLCAiMyIsICI0IiwgIk5hc2FsIiksY2V4PTAuNzUpDQoNCmBgYA0KDQojIExpbmVhciBNaXhlZC1lZmZlY3RzIE1vZGVscy4gV2h5IHJhbmRvbSBlZmZlY3RzIG1hdHRlcg0KDQpMZXQncyBnZW5lcmF0ZSBhIG5ldyBkYXRhZnJhbWUgdGhhdCB3ZSB3aWxsIHVzZSBsYXRlciBvbiBmb3Igb3VyIG1peGVkIG1vZGVscw0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCiMjIENvdXJ0ZXN5IG9mIEJvZG8gV2ludGVyDQpzZXQuc2VlZCg2NjYpDQojd2UgY3JlYXRlIDYgc3ViamVjdHMNCnN1YmplY3RzIDwtIHBhc3RlMCgnUycsIDE6NikNCiNoZXJlIHdlIGFkZCByZXBldGl0aW9ucyB3aXRoaW4gc3BlYWtlcnMNCnN1YmplY3RzIDwtIHJlcChzdWJqZWN0cywgZWFjaCA9IDIwKQ0KaXRlbXMgPC0gcGFzdGUwKCdJdGVtJywgMToyMCkNCiNiZWxvdyByZXBlYXRzDQppdGVtcyA8LSByZXAoaXRlbXMsIDYpDQojYmVsb3cgaXMgdG8gZ2VuZXJhdGUgcmFuZG9tIG51bWJlcnMgdGhhdCBhcmUgbG9nIHZhbHVlcw0KbG9nRnJlcSA8LSByb3VuZChyZXhwKDIwKSo1LCAyKQ0KI2JlbG93IHdlIGFyZSByZXBlYXRpbmcgdGhlIGxvZ0ZyZXEgNiB0aW1lcyB0byBmaXQgd2l0aCB0aGUgbnVtYmVyIG9mIHNwZWFrZXJzIGFuZCBpdGVtcw0KbG9nRnJlcSA8LSByZXAobG9nRnJlcSwgNikNCnhkZiA8LSBkYXRhLmZyYW1lKHN1YmplY3RzLCBpdGVtcywgbG9nRnJlcSkNCiNiZWxvdyByZW1vdmVzIHRoZSBpbmRpdmlkdWFsIHZhcmlhYmxlcyB3ZSBoYWQgY3JlYXRlZCBiZWNhdXNlIHRoZXkgYXJlIGFscmVhZHkgaW4gdGhlIGRhdGFmcmFtZQ0Kcm0oc3ViamVjdHMsIGl0ZW1zLCBsb2dGcmVxKQ0KDQp4ZGYkSW50ZXJjZXB0IDwtIDMwMA0Kc3VibWVhbnMgPC0gcmVwKHJub3JtKDYsIHNkID0gNDApLCAyMCkNCiNzb3J0IG1ha2UgdGhlIG1lYW5zIGZvciBlYWNoIHN1YmplY3QgaXMgdGhlIHNhbWUuLi4NCnN1Ym1lYW5zIDwtIHNvcnQoc3VibWVhbnMpDQp4ZGYkc3VibWVhbnMgPC0gc3VibWVhbnMNCiN3ZSBjcmVhdGUgdGhlIHNhbWUgdGhpbmcgZm9yIGl0ZW1zLi4uIHdlIGFsbG93IHRoZSBpdGVtcyBtZWFuIHRvIHZhcnkgYmV0d2VlbiB3b3Jkcy4uLg0KaXRzbWVhbnMgPC0gcmVwKHJub3JtKDIwLCBzZCA9IDIwKSwgNikNCnhkZiRpdHNtZWFucyA8LSBpdHNtZWFucw0KeGRmJGVycm9yIDwtIHJub3JtKDEyMCwgc2QgPSAyMCkNCiNoZXJlIHdlIGNyZWF0ZSBhbiBlZmZlY3QgY29sdW1uLCAgDQojaGVyZSBmb3IgZWFjaCBsb2dGcmVxLCB3ZSBoYXZlIGEgZGVjcmVhc2Ugb2YgLTUgb2YgdGhhdCBwYXJ0aWN1bGFyIGxvZ0ZyZXEgDQp4ZGYkZWZmZWN0IDwtIC01ICogeGRmJGxvZ0ZyZXENCg0KeGRmJGR1ciA8LSB4ZGYkSW50ZXJjZXB0ICsgeGRmJHN1Ym1lYW5zICsgeGRmJGl0c21lYW5zICsgeGRmJGVycm9yICsgeGRmJGVmZmVjdA0KI2JlbG93IGlzIHRvIHN1YnNldCB0aGUgZGF0YSBhbmQgZ2V0IG9ubHkgYSBmZXcgY29sdW1ucy4uIHRoZSAtYyg0OjgpIHJlbW92ZXMgdGhlIGNvbHVtbnMgNCB0byA4Li4NCnhyZWFsIDwtIHhkZlssLWMoNDo4KV0NCmhlYWQoeHJlYWwpDQpybSh4ZGYsIHN1Ym1lYW5zLCBpdHNtZWFucykNCmBgYA0KDQojIyBQbG90cw0KTGV0J3Mgc3RhcnQgYnkgZG9pbmcgYSBjb3JyZWxhdGlvbiB0ZXN0IGFuZCBwbG90dGluZyB0aGUgZGF0YS4gT3VyIHJlc3VsdHMgc2hvdyB0aGF0IHRoZXJlIGlzIGEgbmVnYXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiBkdXJhdGlvbiBhbmQgTG9nRnJlcXVlbmN5LCBhbmQgdGhlIHBsb3Qgc2hvd3MgdGhpcyBkZWNyZWFzZS4gDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KY29yck1peGVkIDwtIGFzLm1hdHJpeCh4cmVhbFstYygxOjIpXSkgJT4lIA0KICByY29ycih0eXBlPSJwZWFyc29uIikNCnByaW50KGNvcnJNaXhlZCkNCmNvcnJwbG90KGNvcnJNaXhlZCRyLCBtZXRob2QgPSAiY2lyY2xlIiwgdHlwZSA9ICJ1cHBlciIsIHRsLnNydCA9IDQ1LA0KICAgICAgICAgYWRkQ29lZi5jb2wgPSAiYmxhY2siLCBkaWFnID0gRkFMU0UsDQogICAgICAgICBwLm1hdCA9IGNvcnJNaXhlZCRwLCBzaWcubGV2ZWwgPSAwLjA1KQ0KDQoNCg0KZ2dwbG90LnhyZWFsIDwtIHhyZWFsICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gbG9nRnJlcSwgeSA9IGR1cikpICsNCiAgZ2VvbV9wb2ludCgpKyB0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkgKw0KICBsYWJzKHkgPSAiRHVyYXRpb24iLCB4ID0gIkZyZXF1ZW5jeSAoTG9nKSIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0sIHNlPUYpDQpnZ3Bsb3QueHJlYWwNCmBgYA0KDQoNCiMjIExpbmVhciBtb2RlbA0KDQpMZXQncyBydW4gYSBzaW1wbGUgbGluZWFyIG1vZGVsIG9uIHRoZSBkYXRhLiBBcyB3ZSBjYW4gc2VlIGJlbG93LCB0aGVyZSBhcmUgc29tZSBpc3N1ZXMgd2l0aCB0aGUgInNpbXBsZSIgbGluZWFyIG1vZGVsOiB3ZSBoYWQgc2V0IG91ciBTRCBmb3Igc3ViamVjdHMgdG8gYmUgNDAsIGJ1dCB0aGlzIHdhcyBwaWNrZWQgdXAgYXMgMTIwIChzZWUgaGlzdG9ncmFtIG9mIHJlc2lkdWFscykuIFRoZSBRUSBQbG90IGlzIG5vdCAibm9ybWFsIi4gDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KbWRsLmxtLnhyZWFsIDwtIHhyZWFsICU+JSANCiAgbG0oZHVyIH4gbG9nRnJlcSwgZGF0YSA9IC4pDQpzdW1tYXJ5KG1kbC5sbS54cmVhbCkNCmhpc3QocmVzaWR1YWxzKG1kbC5sbS54cmVhbCkpDQpxcW5vcm0ocmVzaWR1YWxzKG1kbC5sbS54cmVhbCkpOyBxcWxpbmUocmVzaWR1YWxzKG1kbC5sbS54cmVhbCkpDQpwbG90KGZpdHRlZChtZGwubG0ueHJlYWwpLCByZXNpZHVhbHMobWRsLmxtLnhyZWFsKSwgY2V4ID0gNCkNCmBgYA0KDQojIyBMaW5lYXIgTWl4ZWQgTW9kZWwNCg0KT3VyIExpbmVhciBNaXhlZCBlZmZlY3RzIE1vZGVsIHdpbGwgdGFrZSBpbnRvIGFjY291bnQgdGhlIHJhbmRvbSBlZmZlY3RzIHdlIGFkZGVkIGFuZCBhbHNvIG91ciBtb2RlbCBzcGVjaWZpY2F0aW9ucy4gV2UgdXNlIGEgTWF4aW11bSBMaWtlbGlob29kIGVzdGltYXRlIChSRU1MID0gRkFMU0UpIGFzIHRoaXMgaXMgd2hhdCB3ZSB3aWxsIHVzZSBmb3IgbW9kZWwgY29tcGFyaXNvbi4gVGhlIExpbmVhciBNaXhlZCBNb2RlbCBpcyByZWZsZWN0aW5nIG91ciBtb2RlbCBzcGVjaWZpY2F0aW9ucyBUaGUgU0Qgb2Ygb3VyIHN1YmplY3RzIGlzIHBpY2tlZCB1cCBjb3JyZWN0bHkuIFRoZSBtb2RlbCByZXN1bHRzIGFyZSAiYWxtb3N0IiB0aGUgc2FtZSBhcyBvdXIgbGluZWFyIG1vZGVsIGFib3ZlLiBUaGUgY29lZmZpY2llbnQgZm9yIHRoZSAiSW50ZXJjZXB0IiBpcyBhdCAzMzcuOTczIGFuZCB0aGUgY29lZmZpY2llbnQgZm9yIExvZ0ZyZXF1ZW5jeSBpcyBhdCAtNS40NjAuIFRoaXMgaW5kaWNhdGVzIHRoYXQgZm9yIGVhY2ggdW5pdCBvZiBpbmNyZWFzZSBpbiB0aGUgTG9nRnJlcXVlbmN5LCB0aGVyZSBpcyBhIGRlY3JlYXNlIGJ5IDUuNDYwIChtcykuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KbWRsLmxtZXIueHJlYWwgPC0geHJlYWwgJT4lIA0KICBsbWVyKGR1ciB+IGxvZ0ZyZXEgICsoMXxzdWJqZWN0cykgKyAoMXxpdGVtcyksIGRhdGEgPSAuLCBSRU1MID0gRkFMU0UpDQpzdW1tYXJ5KG1kbC5sbWVyLnhyZWFsKQ0KaGlzdChyZXNpZHVhbHMobWRsLmxtZXIueHJlYWwpKQ0KcXFub3JtKHJlc2lkdWFscyhtZGwubG1lci54cmVhbCkpOyBxcWxpbmUocmVzaWR1YWxzKG1kbC5sbWVyLnhyZWFsKSkNCnBsb3QoZml0dGVkKG1kbC5sbWVyLnhyZWFsKSwgcmVzaWR1YWxzKG1kbC5sbWVyLnhyZWFsKSwgY2V4ID0gNCkNCmBgYA0KDQojIyBPdXIgc2Vjb25kIE1peGVkIG1vZGVsDQoNClRoaXMgc2Vjb25kIG1vZGVsIGFkZCBhIGJ5LXN1YmplY3QgcmFuZG9tIHNsb3BlLiBSYW5kb20gc2xvcGVzIGFsbG93IGZvciB0aGUgdmFyaWF0aW9uIHRoYXQgZXhpc3RzIGluIHRoZSByYW5kb20gZWZmZWN0cyB0byBiZSB0YWtlbiBpbnRvIGFjY291bnQuIEFuIGludGVyY2VwdCBvbmx5IG1vZGVsIHByb3ZpZGVzIGFuIGF2ZXJhZ2VkIHZhbHVlcyB0byBvdXIgcGFydGljaXBhbnRzLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCm1kbC5sbWVyLnhyZWFsLjIgPC0geHJlYWwgJT4lIA0KICBsbWVyKGR1ciB+IGxvZ0ZyZXEgKyAobG9nRnJlcXxzdWJqZWN0cykgKyAoMXxpdGVtcyksIGRhdGEgPSAuLCBSRU1MID0gRkFMU0UpDQpzdW1tYXJ5KG1kbC5sbWVyLnhyZWFsLjIpDQpoaXN0KHJlc2lkdWFscyhtZGwubG1lci54cmVhbC4yKSkNCnFxbm9ybShyZXNpZHVhbHMobWRsLmxtZXIueHJlYWwuMikpOyBxcWxpbmUocmVzaWR1YWxzKG1kbC5sbWVyLnhyZWFsLjIpKQ0KcGxvdChmaXR0ZWQobWRsLmxtZXIueHJlYWwuMiksIHJlc2lkdWFscyhtZGwubG1lci54cmVhbC4yKSwgY2V4ID0gNCkNCmBgYA0KDQojIyBNb2RlbCBjb21wYXJpc29uDQoNCkJ1dCB3aGVyZSBhcmUgb3VyIHAgdmFsdWVzPyBUaGUgbG1lNCBkZXZlbG9wZXJzIGRlY2lkZWQgbm90IHRvIGluY2x1ZGUgcCB2YWx1ZXMgZHVlIHRvIHZhcmlvdXMgaXNzdWVzIHdpdGggZXN0aW1hdGluZyBkZi4gV2hhdCB3ZSBjYW4gZG8gaW5zdGVhZCBpcyB0byBjb21wYXJlIG1vZGVscy4gV2UgbmVlZCB0byBjcmVhdGUgYSBudWxsIG1vZGVsIHRvIGFsbG93IGZvciBzaWduaWZpY2FuY2UgdGVzdGluZy4gQXMgZXhwZWN0ZWQgb3VyIHByZWRpY3RvciBpcyBzaWduaWZpY2FudGx5IGNvbnRyaWJ1dGluZyB0byB0aGUgZGlmZmVyZW5jZS4gDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KbWRsLmxtZXIueHJlYWwuTnVsbCA8LSB4cmVhbCAlPiUgDQogIGxtZXIoZHVyIH4gMSArIChsb2dGcmVxfHN1YmplY3RzKSArICgxfGl0ZW1zKSwgZGF0YSA9IC4sIFJFTUwgPSBGQUxTRSkNCmFub3ZhKG1kbC5sbWVyLnhyZWFsLk51bGwsIG1kbC5sbWVyLnhyZWFsLjIpDQpgYGANCg0KQWxzbywgZG8gd2UgcmVhbGx5IG5lZWQgcmFuZG9tIHNsb3Blcz8gRnJvbSB0aGUgcmVzdWx0IGJlbG93LCB3ZSBkb24ndCBzZWVtIHRvIG5lZWQgcmFuZG9tIHNsb3BlcyBhdCBhbGwsIGdpdmVuIHRoYXQgYWRkaW5nIHJhbmRvbSBzbG9wZXMgZG9lcyBub3QgaW1wcm92ZSB0aGUgbW9kZWwgZml0LiBJIGFsd2F5cyByZWNvbW1lbmQgdGVzdGluZyB0aGlzLiBNb3N0IG9mIHRoZSB0aW1lIEkga2VlcCByYW5kb20gc2xvcGVzLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmFub3ZhKG1kbC5sbWVyLnhyZWFsLCBtZGwubG1lci54cmVhbC4yKQ0KYGBgDQoNCkJ1dCBpZiB5b3UgYXJlIHJlYWxseSAocmVhbGx5ISEhKSBvYnNlc3NlZCBieSBwIHZhbHVlcywgdGhlbiB5b3UgY2FuIGFsc28gdXNlIGxtZXJUZXN0LiBCVVQgdXNlIGFmdGVyIGNvbXBhcmluZyBtb2RlbHMgdG8gZXZhbHVhdGUgY29udHJpYnV0aW9uIG9mIHByZWRpY3RvcnMNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQptZGwubG1lci54cmVhbC5sbWVyVGVzdCA8LSB4cmVhbCAlPiUgDQogIGxtZXIoZHVyIH4gbG9nRnJlcSArIChsb2dGcmVxfHN1YmplY3RzKSArICgxfGl0ZW1zKSwgZGF0YSA9IC4sIFJFTUwgPSBUUlVFKQ0Kc3VtbWFyeShtZGwubG1lci54cmVhbC5sbWVyVGVzdCkNCmRldGFjaCgicGFja2FnZTpsbWVyVGVzdCIsIHVubG9hZCA9IFRSVUUpDQpgYGANCg0KDQojIyBPdXIgZmluYWwgTWl4ZWQgbW9kZWwNCg0KT3VyIGZpbmFsIG1vZGVsIHVzZXMgUkVNTCAob3IgUmVzdHJpY3RlZCBNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdGUgb2YgVmFyaWFuY2UgQ29tcG9uZW50KSB0byBlc3RpbWF0ZSB0aGUgbW9kZWwuIA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCm1kbC5sbWVyLnhyZWFsLkZ1bGwgPC0geHJlYWwgJT4lIA0KICBsbWVyKGR1ciB+IGxvZ0ZyZXEgKyAobG9nRnJlcXxzdWJqZWN0cykgKyAoMXxpdGVtcyksIGRhdGEgPSAuLCBSRU1MID0gVFJVRSkNCnN1bW1hcnkobWRsLmxtZXIueHJlYWwuRnVsbCkNCmFub3ZhKG1kbC5sbWVyLnhyZWFsLkZ1bGwpDQpoaXN0KHJlc2lkdWFscyhtZGwubG1lci54cmVhbC5GdWxsKSkNCnFxbm9ybShyZXNpZHVhbHMobWRsLmxtZXIueHJlYWwuRnVsbCkpOyBxcWxpbmUocmVzaWR1YWxzKG1kbC5sbWVyLnhyZWFsLkZ1bGwpKQ0KcGxvdChmaXR0ZWQobWRsLmxtZXIueHJlYWwuRnVsbCksIHJlc2lkdWFscyhtZGwubG1lci54cmVhbC5GdWxsKSwgY2V4ID0gNCkNCmBgYA0KDQoNCiMjIERpc3NlY3RpbmcgdGhlIG1vZGVsDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KY29lZihtZGwubG1lci54cmVhbC5GdWxsKQ0KZml4ZWYobWRsLmxtZXIueHJlYWwuRnVsbCkNCmZpeGVmKG1kbC5sbWVyLnhyZWFsLkZ1bGwpWzFdDQpmaXhlZihtZGwubG1lci54cmVhbC5GdWxsKVsyXQ0KDQpjb2VmKG1kbC5sbWVyLnhyZWFsLkZ1bGwpJGBzdWJqZWN0c2BbMV0NCmNvZWYobWRsLmxtZXIueHJlYWwuRnVsbCkkYHN1YmplY3RzYFsyXQ0KDQpjb2VmKG1kbC5sbWVyLnhyZWFsLkZ1bGwpJGBpdGVtc2BbMV0NCmNvZWYobWRsLmxtZXIueHJlYWwuRnVsbCkkYGl0ZW1zYFsyXQ0KDQpgYGANCg0KIyMgVXNpbmcgcHJlZGljdGlvbnMgZnJvbSBvdXIgbW9kZWwNCkluIGdlbmVyYWwsIEkgdXNlIHRoZSBwcmVkaWN0aW9uIGZyb20gbXkgZmluYWwgbW9kZWwgaW4gYW55IHBsb3RzLiBUbyBnZW5lcmF0ZSB0aGlzLCB3ZSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQp4cmVhbCA8LSB4cmVhbCAlPiUgDQogIG11dGF0ZShQcmVkX0R1ciA9IHByZWRpY3QobWRsLmxtZXIueHJlYWwuRnVsbCkpDQoNCnhyZWFsICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gbG9nRnJlcSwgeSA9IFByZWRfRHVyKSkgKw0KICBnZW9tX3BvaW50KCkgKyB0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkgKw0KICBsYWJzKHkgPSAiRHVyYXRpb24iLCB4ID0gIkZyZXF1ZW5jeSAoTG9nKSIsIHRpdGxlID0gIlByZWRpY3RlZCIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0sIHNlID0gRikgKyBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMjAwLDQ1MCkpDQoNCiMjIG9yaWdpbmFsIHBsb3QNCnhyZWFsICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gbG9nRnJlcSAsIHkgPSBkdXIpKSArDQogIGdlb21fcG9pbnQoKSArIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDIwKSsNCiAgbGFicyh5ID0gIkR1cmF0aW9uIiwgeCA9ICJGcmVxdWVuY3kgKExvZykiLCB0aXRsZSA9ICJPcmlnaW5hbCIpKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwgc2UgPSBGKSArIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygyMDAsNDUwKSkNCg0KYGBgDQoNCiMjIEdMTU0gYW5kIENMTU0NCg0KVGhlIGNvZGUgYWJvdmUgd2FzIHVzaW5nIGEgTGluZWFyIE1peGVkIEVmZmVjdHMgTW9kZWxsaW5nLiBUaGUgb3V0Y29tZSB3YXMgYSBudW1lcmljIG9iamVjdC4gSW4gc29tZSBjYXNlcyAoYXMgd2UgaGF2ZSBzZWVuIGFib3ZlKSwgd2UgbWF5IGhhdmU6IA0KDQoxLiBCaW5hcnkgb3V0Y29tZSAoYmlub21pYWwpDQoyLiBDb3VudCBkYXRhIChwb2lzc29uKSwgDQozLiBNdWx0aS1jYXRlZ29yeSBvdXRjb21lIChtdWx0aW5vbWlhbCkNCjQuIFJhdGluZyBkYXRhIChjdW11bGF0aXZlIGZ1bmN0aW9uKQ0KDQpUaGUgY29kZSBiZWxvdyBnaXZlcyB5b3UgYW4gaWRlYSBvZiBob3cgdG8gc3BlY2lmeSB0aGVzZSBtb2RlbHMNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQoNCiMjIEJpbm9taWFsIGZhbWlseQ0KIyMgbG1lNDo6Z2xtZXIob3V0Y29tZX5wcmVkaWN0b3IocykrKDF8c3ViamVjdCkrKDF8aXRlbXMpLi4uLCBkYXRhPWRhdGEsIGZhbWlseT1iaW5vbWlhbCkNCg0KIyMgUG9pc3NvbiBmYW1pbHkNCiMjIGxtZTQ6OmdsbWVyKG91dGNvbWV+cHJlZGljdG9yKHMpKygxfHN1YmplY3QpKygxfGl0ZW1zKS4uLiwgZGF0YT1kYXRhLCBmYW1pbHk9cG9pc3NvbikNCg0KIyMgTXVsdGlub21pYWwgZmFtaWx5DQojIyBhIGJpdCBjb21wbGljYXRlZCBhcyB0ZWhyZSBpcyBhIG5lZWQgdG8gdXNlIEJheWVzaWFuIGFwcHJvYWNoZXMsIHNlZSBlLmcuLCANCiMjIGdsbW1BRE1CDQojIyBtaXhjYXQNCiMjIE1DTUNnbG1tDQojIyBzZWUgaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vY2FzYWxsYXMvODI2MzgxOA0KDQojIyBSYXRpbmcgZGF0YSwgdXNlIGZvbGxvd2luZw0KIyMgb3JkaW5hbDo6Y2xtbShvdXRjb21lfnByZWRpY3RvcihzKSsoMXxzdWJqZWN0KSsoMXxpdGVtcykuLi4sIGRhdGE9ZGF0YSkNCg0KDQojIyBSZW1lbWJlciB0byB0ZXN0IGZvciByYW5kb20gZWZmZWN0cyBhbmQgd2hldGhlciBzbG9wZXMgYXJlIG5lZWRlZC4NCg0KYGBgDQo=