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) and an outcome. We will start with a basic example. Using the dataframe “cars”.

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('ggplot2','tidyverse','psycho','lme4','ordinal','lmerTest','PresenceAbsence')
for(p in requiredPackages){
  if(!require(p,character.only = TRUE)) install.packages(p)
  library(p,character.only = TRUE)
}
detach("package:lmerTest", unload=TRUE)
# library(ggplot2);library(tidyverse);library(psycho);library(lme4);library(ordinal);library(lmerTest)

2 Starting with a linear model

head(cars)
str(cars)
'data.frame':   50 obs. of  2 variables:
 $ speed: num  4 4 7 7 8 9 10 10 10 11 ...
 $ dist : num  2 10 4 22 16 10 18 26 34 17 ...
summary(cars)
     speed           dist       
 Min.   : 4.0   Min.   :  2.00  
 1st Qu.:12.0   1st Qu.: 26.00  
 Median :15.0   Median : 36.00  
 Mean   :15.4   Mean   : 42.98  
 3rd Qu.:19.0   3rd Qu.: 56.00  
 Max.   :25.0   Max.   :120.00  
ggplot.cars1 <- ggplot(cars,aes(x=speed,y=dist))+
                geom_point()+theme_bw(base_size = 20)+
                labs(y = "Stopping distance (ft)", x = "Speed (mph)")
ggplot.cars1

2.1 Basic correlations and plotting the linear relationship

Let us do some basic correlation analysis using base R and package psycho. And let’s visualise this correlation using a linear regression line

cor(cars)
          speed      dist
speed 1.0000000 0.8068949
dist  0.8068949 1.0000000
## correlation using "psycho"
cars %>% 
  correlation()
Pearson Full Correlation (p value correction: holm):
   - speed - dist:   Results of the Pearson correlation showed a significant and strong negative association between speed and dist (r(48) = 0.81, p < .001***).
ggplot.cars2 <- ggplot(cars,aes(x=speed,y=dist))+
                geom_point()+theme_bw(base_size = 20)+
                labs(y = "Stopping distance (ft)", x = "Speed (mph)")+
                geom_smooth(method = lm, se=F)
ggplot.cars2

2.2 Our first statistical model

What about running our first statistical analysis of the data? We will use a Linear regression model. We can visualise the results using “summary”. If you are new to linear models and prefer an ANOVA-style analysis, then use the function “ANOVA” on the linear model. We’ll come back to the linear model later on.

mdl.lm <- lm(dist~speed, data=cars)
mdl.lm#also print(mdl.lm)

Call:
lm(formula = dist ~ speed, data = cars)

Coefficients:
(Intercept)        speed  
    -17.579        3.932  
summary(mdl.lm)

Call:
lm(formula = dist ~ speed, data = cars)

Residuals:
    Min      1Q  Median      3Q     Max 
-29.069  -9.525  -2.272   9.215  43.201 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -17.5791     6.7584  -2.601   0.0123 *  
speed         3.9324     0.4155   9.464 1.49e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 15.38 on 48 degrees of freedom
Multiple R-squared:  0.6511,    Adjusted R-squared:  0.6438 
F-statistic: 89.57 on 1 and 48 DF,  p-value: 1.49e-12
anova(mdl.lm)
Analysis of Variance Table

Response: dist
          Df Sum Sq Mean Sq F value   Pr(>F)    
speed      1  21186 21185.5  89.567 1.49e-12 ***
Residuals 48  11354   236.5                     
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

To interpret the model, we need look at the coefficients. The “Intercept” is -17.5791 and the coefficient for “speed” is 3.9324. This tells us that when “speed” = 0, the “distance” is at -17.5791; with one unit increase in “speed”, the “distance” increases by 3.9324 (ft). There may be some issues with interpretation of this model and we need to center, standardise scores. We are not going to cover this here, but you can center and/or scale with this formula:

  1. Center = scale(x, center = TRUE, scale = FALSE)
  2. Standardise = scale(x, center = TRUE, scale = TRUE)

2.2.1 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

2.2.2 “str” and “coef”

str(mdl.lm)
List of 12
 $ coefficients : Named num [1:2] -17.58 3.93
  ..- attr(*, "names")= chr [1:2] "(Intercept)" "speed"
 $ residuals    : Named num [1:50] 3.85 11.85 -5.95 12.05 2.12 ...
  ..- attr(*, "names")= chr [1:50] "1" "2" "3" "4" ...
 $ effects      : Named num [1:50] -303.914 145.552 -8.115 9.885 0.194 ...
  ..- attr(*, "names")= chr [1:50] "(Intercept)" "speed" "" "" ...
 $ rank         : int 2
 $ fitted.values: Named num [1:50] -1.85 -1.85 9.95 9.95 13.88 ...
  ..- attr(*, "names")= chr [1:50] "1" "2" "3" "4" ...
 $ assign       : int [1:2] 0 1
 $ qr           :List of 5
  ..$ qr   : num [1:50, 1:2] -7.071 0.141 0.141 0.141 0.141 ...
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : chr [1:50] "1" "2" "3" "4" ...
  .. .. ..$ : chr [1:2] "(Intercept)" "speed"
  .. ..- attr(*, "assign")= int [1:2] 0 1
  ..$ qraux: num [1:2] 1.14 1.27
  ..$ pivot: int [1:2] 1 2
  ..$ tol  : num 1e-07
  ..$ rank : int 2
  ..- attr(*, "class")= chr "qr"
 $ df.residual  : int 48
 $ xlevels      : Named list()
 $ call         : language lm(formula = dist ~ speed, data = cars)
 $ terms        :Classes 'terms', 'formula'  language dist ~ speed
  .. ..- attr(*, "variables")= language list(dist, speed)
  .. ..- attr(*, "factors")= int [1:2, 1] 0 1
  .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. ..$ : chr [1:2] "dist" "speed"
  .. .. .. ..$ : chr "speed"
  .. ..- attr(*, "term.labels")= chr "speed"
  .. ..- attr(*, "order")= int 1
  .. ..- attr(*, "intercept")= int 1
  .. ..- attr(*, "response")= int 1
  .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. ..- attr(*, "predvars")= language list(dist, speed)
  .. ..- attr(*, "dataClasses")= Named chr [1:2] "numeric" "numeric"
  .. .. ..- attr(*, "names")= chr [1:2] "dist" "speed"
 $ model        :'data.frame':  50 obs. of  2 variables:
  ..$ dist : num [1:50] 2 10 4 22 16 10 18 26 34 17 ...
  ..$ speed: num [1:50] 4 4 7 7 8 9 10 10 10 11 ...
  ..- attr(*, "terms")=Classes 'terms', 'formula'  language dist ~ speed
  .. .. ..- attr(*, "variables")= language list(dist, speed)
  .. .. ..- attr(*, "factors")= int [1:2, 1] 0 1
  .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. ..$ : chr [1:2] "dist" "speed"
  .. .. .. .. ..$ : chr "speed"
  .. .. ..- attr(*, "term.labels")= chr "speed"
  .. .. ..- attr(*, "order")= int 1
  .. .. ..- attr(*, "intercept")= int 1
  .. .. ..- attr(*, "response")= int 1
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. ..- attr(*, "predvars")= language list(dist, speed)
  .. .. ..- attr(*, "dataClasses")= Named chr [1:2] "numeric" "numeric"
  .. .. .. ..- attr(*, "names")= chr [1:2] "dist" "speed"
 - attr(*, "class")= chr "lm"
coef(mdl.lm)
(Intercept)       speed 
 -17.579095    3.932409 
## same as 
## mdl.lm$coefficients

2.2.2.1 “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) 
  -17.57909 
coef(mdl.lm)[2] # same as mdl.lm$coefficients[2]
   speed 
3.932409 
summary(mdl.lm)$coefficients[2, ] # full row
    Estimate   Std. Error      t value     Pr(>|t|) 
3.932409e+00 4.155128e-01 9.463990e+00 1.489836e-12 
summary(mdl.lm)$coefficients[2, 4] #for p value
[1] 1.489836e-12

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

residuals(mdl.lm)
         1          2          3          4          5          6          7 
  3.849460  11.849460  -5.947766  12.052234   2.119825  -7.812584  -3.744993 
         8          9         10         11         12         13         14 
  4.255007  12.255007  -8.677401   2.322599 -15.609810  -9.609810  -5.609810 
        15         16         17         18         19         20         21 
 -1.609810  -7.542219   0.457781   0.457781  12.457781 -11.474628  -1.474628 
        22         23         24         25         26         27         28 
 22.525372  42.525372 -21.407036 -15.407036  12.592964 -13.339445  -5.339445 
        29         30         31         32         33         34         35 
-17.271854  -9.271854   0.728146 -11.204263   2.795737  22.795737  30.795737 
        36         37         38         39         40         41         42 
-21.136672 -11.136672  10.863328 -29.069080 -13.069080  -9.069080  -5.069080 
        43         44         45         46         47         48         49 
  2.930920  -2.933898 -18.866307  -6.798715  15.201285  16.201285  43.201285 
        50 
  4.268876 
fitted(mdl.lm)
        1         2         3         4         5         6         7         8 
-1.849460 -1.849460  9.947766  9.947766 13.880175 17.812584 21.744993 21.744993 
        9        10        11        12        13        14        15        16 
21.744993 25.677401 25.677401 29.609810 29.609810 29.609810 29.609810 33.542219 
       17        18        19        20        21        22        23        24 
33.542219 33.542219 33.542219 37.474628 37.474628 37.474628 37.474628 41.407036 
       25        26        27        28        29        30        31        32 
41.407036 41.407036 45.339445 45.339445 49.271854 49.271854 49.271854 53.204263 
       33        34        35        36        37        38        39        40 
53.204263 53.204263 53.204263 57.136672 57.136672 57.136672 61.069080 61.069080 
       41        42        43        44        45        46        47        48 
61.069080 61.069080 61.069080 68.933898 72.866307 76.798715 76.798715 76.798715 
       49        50 
76.798715 80.731124 

2.2.2.2 Goodness of fit?

AIC(mdl.lm) # Akaike's Information Criterion, lower values are better
[1] 419.1569
BIC(mdl.lm) # Bayesian AIC
[1] 424.8929
logLik(mdl.lm)  # log likelihood
'log Lik.' -206.5784 (df=3)

2.2.2.3 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 <- lm(dist~1, data=cars)
anova(mdl.lm,mdl.lm.Null)
Analysis of Variance Table

Model 1: dist ~ speed
Model 2: dist ~ 1
  Res.Df   RSS Df Sum of Sq      F   Pr(>F)    
1     48 11354                                 
2     49 32539 -1    -21186 89.567 1.49e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

2.2.2.4 Plotting fitted values

Let’s plot our fitted values. Any problems with this?

ggplot.cars3 <- ggplot(cars,aes(x=speed,y=predict(mdl.lm)))+
                geom_point()+theme_bw(base_size = 20)+
                labs(y = "Stopping distance (ft)", x = "Speed (mph)")+
                geom_smooth(method = lm, se=F)
ggplot.cars3

2.3 Some cool correlations

Above, we had two predictors and we checked for correlations. What if we have more predictors and we want to have cool correlation tables and plots? (see here https://www.r-bloggers.com/beautiful-and-powerful-correlation-tables-in-r/amp/)

2.3.1 With corrections

cor <- psycho::affective %>% 
  correlation()
summary(cor)
plot(cor)

print(cor)
Pearson Full Correlation (p value correction: holm):
   - Age - Life_Satisfaction:   Results of the Pearson correlation showed a non significant and weak negative association between Age and Life_Satisfaction (r(1249) = 0.030, p > .1).
   - Age - Concealing:   Results of the Pearson correlation showed a non significant and weak positive association between Age and Concealing (r(1249) = -0.050, p > .1).
   - Life_Satisfaction - Concealing:   Results of the Pearson correlation showed a non significant and weak positive association between Life_Satisfaction and Concealing (r(1249) = -0.063, p > .1).
   - Age - Adjusting:   Results of the Pearson correlation showed a non significant and weak negative association between Age and Adjusting (r(1249) = 0.027, p > .1).
   - Life_Satisfaction - Adjusting:   Results of the Pearson correlation showed a significant and moderate negative association between Life_Satisfaction and Adjusting (r(1249) = 0.36, p < .001***).
   - Concealing - Adjusting:   Results of the Pearson correlation showed a significant and weak negative association between Concealing and Adjusting (r(1249) = 0.22, p < .001***).
   - Age - Tolerating:   Results of the Pearson correlation showed a non significant and weak negative association between Age and Tolerating (r(1249) = 0.031, p > .1).
   - Life_Satisfaction - Tolerating:   Results of the Pearson correlation showed a significant and weak negative association between Life_Satisfaction and Tolerating (r(1249) = 0.15, p < .001***).
   - Concealing - Tolerating:   Results of the Pearson correlation showed a non significant and weak negative association between Concealing and Tolerating (r(1249) = 0.074, p = 0.05°).
   - Adjusting - Tolerating:   Results of the Pearson correlation showed a significant and weak negative association between Adjusting and Tolerating (r(1249) = 0.29, p < .001***).

2.3.2 No corrections

What of we don’t want to use corrections for multiple comparisons?

cor <- psycho::affective %>% 
  correlation(adjust="none")
summary(cor)
plot(cor)

2.3.3 Issues with no corrections

Let’s generate a dataframe with 11 predictors and 1000 rows each. Because these are simulated data, there is no underlying correlation between these.

df_with_11_vars <- data.frame(replicate(11, rnorm(1000)))
cor2 <- correlation(df_with_11_vars, adjust="none")
summary(cor2)

Deactivate this by using “i_am_cheating”!

cor3 <- correlation(df_with_11_vars, adjust="none",i_am_cheating=TRUE)
summary(cor3)

2.3.4 Same model with corrections

cor4 <- correlation(df_with_11_vars)
summary(cor4)

3 From Linear to Logistic models

Here we will look at an example when the outcome is a binary outcome. 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.

3.1 Load and summaries

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

grammatical <- read.csv("grammatical.csv")
str(grammatical)
'data.frame':   165 obs. of  2 variables:
 $ grammaticality: Factor w/ 2 levels "grammatical",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ response      : Factor w/ 2 levels "no","yes": 2 2 2 2 2 2 2 2 2 2 ...
head(grammatical)
grammatical$response <- relevel(grammatical$response,ref="yes")
table(grammatical$grammaticality,grammatical$response)
               
                yes  no
  grammatical   100   5
  ungrammatical  10  50

3.2 Accuracy and Signal Detection Theory

We are generally interested in performance, i.e., whether the we have “accurately” categorised the outcome or not. 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
## TP = True Positive (Hit); FP = False Positive; FN = False Negative; TN = True Negative
TP = 100
FP = 10
FN = 5
TN = 50
Total = 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:
[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) # specificity, also 1-sensetivity 
[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)
$`dprime`
[1] 2.537557

$beta
[1] 0.4467859

$aprime
[1] 0.7892857

$bppd
[1] -0.9512195

$c
[1] -0.3175006

3.3 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.

3.3.1 Results

The results below show the logodds for our model. 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.61 (the intercept shows that when the response is “no”, the logodds are -1.61).

levels(grammatical$response)
[1] "yes" "no" 
levels(grammatical$grammaticality)
[1] "grammatical"   "ungrammatical"
grammatical$response <- relevel(grammatical$response, ref="no")
grammatical$grammaticality <- relevel(grammatical$grammaticality, ref="ungrammatical")
mdl.glm <- glm(response~grammaticality,data=grammatical,family = binomial)
summary(mdl.glm)

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

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

3.3.2 Getting “true” coefficients from GLM

Here we will run the same GLM model above but by supressing the “Intercept”. The idea here is to get the “true” coefficient for a “grammatical” and a “yes” response.

# An intercept is always included in any regression, but you can specify it with "1"
## glm(response~1+grammaticality,data=grammatical,family = binomial)
mdl.glm2 <- glm(response~-1+grammaticality,data=grammatical,family = binomial)
summary(mdl.glm2)

Call:
glm(formula = response ~ -1 + grammaticality, family = binomial, 
    data = grammatical)

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|)    
grammaticalityungrammatical  -1.6094     0.3464  -4.646 3.38e-06 ***
grammaticalitygrammatical     2.9957     0.4582   6.538 6.25e-11 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 228.739  on 165  degrees of freedom
Residual deviance:  94.271  on 163  degrees of freedom
AIC: 98.271

Number of Fisher Scoring iterations: 5

3.3.3 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 “yes” response increase by 100; whereas receiving a “no” is a mere 0.2. Using the second model (i.e., without an Intercept) allows us to get the odd rations for each of responses to “Ungrammatical” and “Grammatical”

exp(coef(mdl.glm))
              (Intercept) grammaticalitygrammatical 
                      0.2                     100.0 
exp(coef(mdl.glm2))
grammaticalityungrammatical   grammaticalitygrammatical 
                        0.2                        20.0 

3.3.4 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 increase by 99%

plogis(coef(mdl.glm))
              (Intercept) grammaticalitygrammatical 
                0.1666667                 0.9900990 
plogis(coef(mdl.glm2))
grammaticalityungrammatical   grammaticalitygrammatical 
                  0.1666667                   0.9523810 

3.3.5 GLM as a classification tool

The code below demosntrates 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 sensetivity, 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")
tbl.glm <- table(grammatical$response,predict(mdl.glm,type="response")>0.5)
tbl.glm
     
      FALSE TRUE
  no     50    5
  yes    10  100
PresenceAbsence::pcc(tbl.glm)
PresenceAbsence::specificity(tbl.glm)
PresenceAbsence::sensitivity(tbl.glm)
PresenceAbsence::Kappa(tbl.glm)
###etc..

3.3.6 Plotting

grammatical$prob <- predict(glm(response~grammaticality,data=grammatical,family = binomial),type="response")
ggplot(grammatical, 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"))

3.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.

5 From Linear to Mixed models. Why random effects matter

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

set.seed(666)
#we create 6 subjects
subjects <- paste0('S', 1:6)
#here we ad repetitions within speakers
subjects <- rep(subjects, each = 20)
items <- paste0('Item', 1:20)
#below repeates
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)

5.1 Correlations and 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.

cor5 <- xreal %>% 
  correlation()
summary(cor5)
ggplot.xreal <- ggplot(xreal,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

5.2 Linear model

Let’s run a simple linear model on the data. As we can see below, thereare 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 <- lm(dur~logFreq, data=xreal)
summary(mdl.lm.xreal)

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

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)

5.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 <- lmer(dur~logFreq+(1|subjects)+(1|items), data=xreal,REML=FALSE)
summary(mdl.lmer.xreal)
Linear mixed model fit by maximum likelihood  ['lmerMod']
Formula: dur ~ logFreq + (1 | subjects) + (1 | items)
   Data: xreal

     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 t value
(Intercept)  337.973     17.587  19.217
logFreq       -5.460      1.004  -5.436

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)

5.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 <- lmer(dur~logFreq+(logFreq|subjects)+(1|items), data=xreal,REML=FALSE)
summary(mdl.lmer.xreal.2)
Linear mixed model fit by maximum likelihood  ['lmerMod']
Formula: dur ~ logFreq + (logFreq | subjects) + (1 | items)
   Data: xreal

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

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-2.1087 -0.6068  0.0624  0.5827  2.4565 

Random effects:
 Groups   Name        Variance  Std.Dev. Corr
 items    (Intercept) 5.900e+02 24.2890      
 subjects (Intercept) 1.399e+03 37.4028      
          logFreq     2.898e-02  0.1702  1.00
 Residual             2.829e+02 16.8195      
Number of obs: 120, groups:  items, 20; subjects, 6

Fixed effects:
            Estimate Std. Error t value
(Intercept)  337.973     17.239  19.605
logFreq       -5.460      1.007  -5.423

Correlation of Fixed Effects:
        (Intr)
logFreq -0.267
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)

5.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 <- lmer(dur~1+(logFreq|subjects)+(1|items), data=xreal,REML=FALSE)
anova(mdl.lmer.xreal.Null,mdl.lmer.xreal.2)
Data: xreal
Models:
mdl.lmer.xreal.Null: dur ~ 1 + (logFreq | subjects) + (1 | items)
mdl.lmer.xreal.2: dur ~ logFreq + (logFreq | subjects) + (1 | items)
                    Df    AIC    BIC  logLik deviance  Chisq Chi Df Pr(>Chisq)
mdl.lmer.xreal.Null  6 1131.9 1148.6 -559.93   1119.9                         
mdl.lmer.xreal.2     7 1109.5 1129.0 -547.73   1095.5 24.394      1  7.852e-07
                       
mdl.lmer.xreal.Null    
mdl.lmer.xreal.2    ***
---
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: xreal
Models:
mdl.lmer.xreal: dur ~ logFreq + (1 | subjects) + (1 | items)
mdl.lmer.xreal.2: dur ~ logFreq + (logFreq | subjects) + (1 | items)
                 Df    AIC    BIC  logLik deviance  Chisq Chi 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.3788      2     0.8274

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

library(lmerTest)
mdl.lmer.xreal.lmerTest <- lmer(dur~logFreq+(logFreq|subjects)+(1|items), data=xreal,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: xreal

REML criterion at convergence: 1086.1

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-2.09692 -0.60118  0.06414  0.58479  2.46241 

Random effects:
 Groups   Name        Variance  Std.Dev. Corr
 items    (Intercept) 6.294e+02 25.088       
 subjects (Intercept) 1.651e+03 40.637       
          logFreq     3.421e-02  0.185   1.00
 Residual             2.829e+02 16.819       
Number of obs: 120, groups:  items, 20; subjects, 6

Fixed effects:
            Estimate Std. Error      df t value Pr(>|t|)    
(Intercept)  337.973     18.526   7.394  18.243 2.04e-07 ***
logFreq       -5.460      1.038  18.142  -5.261 5.17e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
        (Intr)
logFreq -0.249
detach("package:lmerTest", unload=TRUE)

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

REML criterion at convergence: 1086.1

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-2.09692 -0.60118  0.06414  0.58479  2.46241 

Random effects:
 Groups   Name        Variance  Std.Dev. Corr
 items    (Intercept) 6.294e+02 25.088       
 subjects (Intercept) 1.651e+03 40.637       
          logFreq     3.421e-02  0.185   1.00
 Residual             2.829e+02 16.819       
Number of obs: 120, groups:  items, 20; subjects, 6

Fixed effects:
            Estimate Std. Error t value
(Intercept)  337.973     18.526  18.243
logFreq       -5.460      1.038  -5.261

Correlation of Fixed Effects:
        (Intr)
logFreq -0.249
anova(mdl.lmer.xreal.Full)
Analysis of Variance Table
        Df Sum Sq Mean Sq F value
logFreq  1 7828.8  7828.8  27.677
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)

5.7 Dissecting the model

coef(mdl.lmer.xreal.Full)
$`items`
       (Intercept)   logFreq
Item1     352.3564 -5.460115
Item10    331.7619 -5.460115
Item11    324.7272 -5.460115
Item12    350.2316 -5.460115
Item13    353.1171 -5.460115
Item14    311.8359 -5.460115
Item15    354.0588 -5.460115
Item16    353.9386 -5.460115
Item17    288.7852 -5.460115
Item18    362.4698 -5.460115
Item19    338.1424 -5.460115
Item2     325.1858 -5.460115
Item20    359.7410 -5.460115
Item3     370.1798 -5.460115
Item4     302.4272 -5.460115
Item5     350.0497 -5.460115
Item6     338.9482 -5.460115
Item7     362.8397 -5.460115
Item8     295.5951 -5.460115
Item9     333.0694 -5.460115

$subjects
   (Intercept)   logFreq
S1    314.4695 -5.567091
S2    303.9038 -5.615180
S3    314.2921 -5.567898
S4    318.4283 -5.549073
S5    373.3005 -5.299324
S6    403.4440 -5.162127

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]

5.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$Pred_Dur <- predict(lmer(dur~logFreq+(logFreq|subjects)+(1|items), data=xreal,REML=TRUE))
ggplot.xreal.Pred <- ggplot(xreal,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))
ggplot.xreal.Pred

## original plot
ggplot.xreal.Original <- ggplot(xreal,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))
ggplot.xreal.Original

5.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 specificy 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.

6 Running your code and automatically drawing quality tables for publications

See this website for how to run and automatically draw tables for your publications. I haven’t tested this but it is in development: 7) https://www.r-bloggers.com/elegant-regression-results-tables-and-plots-in-r-the-finalfit-package/amp

LS0tDQp0aXRsZTogIlNlc3Npb25fNC1BbmFseXNpbmdEYXRhIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogeWVzDQotLS0NCg0KSW4gdGhpcyBzZXNzaW9uLCB3ZSB3aWxsIGxvb2sgYXQgYmFzaWMgZnVuY3Rpb25zIGluIFIgdGhhdCB3aWxsIGhlbHAgdXMgaW4gcnVubmluZyBzb21lIGluZmVyZW50aWFsIHN0YXRpc3RpY3MuIFRoZXNlIHdpbGwgaGVscCB1cyB0byBldmFsdWF0ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gb25lIChvciBtb3JlKSBwcmVkaWN0b3IocykgYW5kIGFuIG91dGNvbWUuIFdlIHdpbGwgc3RhcnQgd2l0aCBhIGJhc2ljIGV4YW1wbGUuIFVzaW5nIHRoZSBkYXRhZnJhbWUgImNhcnMiLg0KDQoNCiMgTG9hZGluZyBwYWNrYWdlcyANCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KIyMgVXNlIHRoZSBjb2RlIGJlbG93IHRvIGNoZWNrIGlmIHlvdSBoYXZlIGFsbCByZXF1aXJlZCBwYWNrYWdlcyBpbnN0YWxsZWQuIElmIHNvbWUgYXJlIG5vdCBpbnN0YWxsZWQgYWxyZWFkeSwgdGhlIGNvZGUgYmVsb3cgd2lsbCBpbnN0YWxsIHRoZXNlLiBJZiB5b3UgaGF2ZSBhbGwgcGFja2FnZXMgaW5zdGFsbGVkLCB0aGVuIHlvdSBjb3VsZCBsb2FkIHRoZW0gd2l0aCB0aGUgc2Vjb25kIGNvZGUuDQpyZXF1aXJlZFBhY2thZ2VzID0gYygnZ2dwbG90MicsJ3RpZHl2ZXJzZScsJ3BzeWNobycsJ2xtZTQnLCdvcmRpbmFsJywnbG1lclRlc3QnLCdQcmVzZW5jZUFic2VuY2UnKQ0KZm9yKHAgaW4gcmVxdWlyZWRQYWNrYWdlcyl7DQogIGlmKCFyZXF1aXJlKHAsY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgaW5zdGFsbC5wYWNrYWdlcyhwKQ0KICBsaWJyYXJ5KHAsY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KfQ0KZGV0YWNoKCJwYWNrYWdlOmxtZXJUZXN0IiwgdW5sb2FkPVRSVUUpDQojIGxpYnJhcnkoZ2dwbG90Mik7bGlicmFyeSh0aWR5dmVyc2UpO2xpYnJhcnkocHN5Y2hvKTtsaWJyYXJ5KGxtZTQpO2xpYnJhcnkob3JkaW5hbCk7bGlicmFyeShsbWVyVGVzdCkNCmBgYA0KDQojIFN0YXJ0aW5nIHdpdGggYSBsaW5lYXIgbW9kZWwNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KaGVhZChjYXJzKQ0Kc3RyKGNhcnMpDQpzdW1tYXJ5KGNhcnMpDQpnZ3Bsb3QuY2FyczEgPC0gZ2dwbG90KGNhcnMsYWVzKHg9c3BlZWQseT1kaXN0KSkrDQogICAgICAgICAgICAgICAgZ2VvbV9wb2ludCgpK3RoZW1lX2J3KGJhc2Vfc2l6ZSA9IDIwKSsNCiAgICAgICAgICAgICAgICBsYWJzKHkgPSAiU3RvcHBpbmcgZGlzdGFuY2UgKGZ0KSIsIHggPSAiU3BlZWQgKG1waCkiKQ0KZ2dwbG90LmNhcnMxDQoNCmBgYA0KDQojIyBCYXNpYyBjb3JyZWxhdGlvbnMgYW5kIHBsb3R0aW5nIHRoZSBsaW5lYXIgcmVsYXRpb25zaGlwIA0KTGV0IHVzIGRvIHNvbWUgYmFzaWMgY29ycmVsYXRpb24gYW5hbHlzaXMgdXNpbmcgYmFzZSBSIGFuZCBwYWNrYWdlIHBzeWNoby4gQW5kIGxldCdzIHZpc3VhbGlzZSB0aGlzIGNvcnJlbGF0aW9uIHVzaW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gbGluZQ0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmNvcihjYXJzKQ0KIyMgY29ycmVsYXRpb24gdXNpbmcgInBzeWNobyINCmNhcnMgJT4lIA0KICBjb3JyZWxhdGlvbigpDQoNCmdncGxvdC5jYXJzMiA8LSBnZ3Bsb3QoY2FycyxhZXMoeD1zcGVlZCx5PWRpc3QpKSsNCiAgICAgICAgICAgICAgICBnZW9tX3BvaW50KCkrdGhlbWVfYncoYmFzZV9zaXplID0gMjApKw0KICAgICAgICAgICAgICAgIGxhYnMoeSA9ICJTdG9wcGluZyBkaXN0YW5jZSAoZnQpIiwgeCA9ICJTcGVlZCAobXBoKSIpKw0KICAgICAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZT1GKQ0KZ2dwbG90LmNhcnMyDQoNCmBgYA0KDQojIyBPdXIgZmlyc3Qgc3RhdGlzdGljYWwgbW9kZWwNCldoYXQgYWJvdXQgcnVubmluZyBvdXIgZmlyc3Qgc3RhdGlzdGljYWwgYW5hbHlzaXMgb2YgdGhlIGRhdGE/IFdlIHdpbGwgdXNlIGEgTGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwuIFdlIGNhbiB2aXN1YWxpc2UgdGhlIHJlc3VsdHMgdXNpbmcgInN1bW1hcnkiLiBJZiB5b3UgYXJlIG5ldyB0byBsaW5lYXIgbW9kZWxzIGFuZCBwcmVmZXIgYW4gQU5PVkEtc3R5bGUgYW5hbHlzaXMsIHRoZW4gdXNlIHRoZSBmdW5jdGlvbiAiQU5PVkEiIG9uIHRoZSBsaW5lYXIgbW9kZWwuIFdlJ2xsIGNvbWUgYmFjayB0byB0aGUgbGluZWFyIG1vZGVsIGxhdGVyIG9uLiANCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQptZGwubG0gPC0gbG0oZGlzdH5zcGVlZCwgZGF0YT1jYXJzKQ0KbWRsLmxtI2Fsc28gcHJpbnQobWRsLmxtKQ0Kc3VtbWFyeShtZGwubG0pDQphbm92YShtZGwubG0pDQpgYGANCg0KVG8gaW50ZXJwcmV0IHRoZSBtb2RlbCwgd2UgbmVlZCBsb29rIGF0IHRoZSBjb2VmZmljaWVudHMuIFRoZSAiSW50ZXJjZXB0IiBpcyAtMTcuNTc5MSBhbmQgdGhlIGNvZWZmaWNpZW50IGZvciAic3BlZWQiIGlzIDMuOTMyNC4gVGhpcyB0ZWxscyB1cyB0aGF0IHdoZW4gInNwZWVkIiA9IDAsIHRoZSAiZGlzdGFuY2UiIGlzIGF0IC0xNy41NzkxOyB3aXRoIG9uZSB1bml0IGluY3JlYXNlIGluICJzcGVlZCIsIHRoZSAiZGlzdGFuY2UiIGluY3JlYXNlcyBieSAzLjkzMjQgKGZ0KS4gVGhlcmUgbWF5IGJlIHNvbWUgaXNzdWVzIHdpdGggaW50ZXJwcmV0YXRpb24gb2YgdGhpcyBtb2RlbCBhbmQgd2UgbmVlZCB0byBjZW50ZXIsIHN0YW5kYXJkaXNlIHNjb3Jlcy4gV2UgYXJlIG5vdCBnb2luZyB0byBjb3ZlciB0aGlzIGhlcmUsIGJ1dCB5b3UgY2FuIGNlbnRlciBhbmQvb3Igc2NhbGUgd2l0aCB0aGlzIGZvcm11bGE6IA0KDQoxLiBDZW50ZXIgPSBzY2FsZSh4LCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IEZBTFNFKQ0KMi4gU3RhbmRhcmRpc2UgPSBzY2FsZSh4LCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpDQoNCg0KIyMjIERpc3NlY3RpbmcgdGhlIG1vZGVsDQpMZXQgdXMgZGlzc2VjdCB0aGUgbW9kZWwuIElmIHlvdSB1c2UgInN0ciIsIHlvdSB3aWxsIGJlIGFibGUgdG8gc2VlIHdoYXQgaXMgYXZhaWxhYmxlIHVuZGVyIG91ciBsaW5lYXIgbW9kZWwuIFRvIGFjY2VzcyBzb21lIGluZm8gZnJvbSB0aGUgbW9kZWwNCg0KIyMjICJzdHIiIGFuZCAiY29lZiINCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0Kc3RyKG1kbC5sbSkNCmNvZWYobWRsLmxtKQ0KIyMgc2FtZSBhcyANCiMjIG1kbC5sbSRjb2VmZmljaWVudHMNCmBgYA0KDQojIyMjICJjb2VmIiBhbmQgImNvZWZmaWNpZW50cyINCldoYXQgaWYgSSB3YW50IHRvIG9idGFpbiB0aGUgIkludGVyY2VwdCI/IE9yIHRoZSBjb2VmZmljaWVudCBmb3IgZGlzdGFuY2U/IFdoYXQgaWYgSSB3YW50IHRoZSBmdWxsIHJvdyBmb3IgZGlzdGFuY2U/DQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmNvZWYobWRsLmxtKVsxXSAjIHNhbWUgYXMgbWRsLmxtJGNvZWZmaWNpZW50c1sxXQ0KY29lZihtZGwubG0pWzJdICMgc2FtZSBhcyBtZGwubG0kY29lZmZpY2llbnRzWzJdDQoNCnN1bW1hcnkobWRsLmxtKSRjb2VmZmljaWVudHNbMiwgXSAjIGZ1bGwgcm93DQpzdW1tYXJ5KG1kbC5sbSkkY29lZmZpY2llbnRzWzIsIDRdICNmb3IgcCB2YWx1ZQ0KDQpgYGANCg0KV2hhdCBhYm91dCByZXNpZHVhbHMgKGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgb2JzZXJ2ZWQgdmFsdWUgYW5kIHRoZSBlc3RpbWF0ZWQgdmFsdWUgb2YgdGhlIHF1YW50aXR5KSBhbmQgZml0dGVkIHZhbHVlcz8NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQpyZXNpZHVhbHMobWRsLmxtKQ0KZml0dGVkKG1kbC5sbSkNCmBgYA0KDQojIyMjIEdvb2RuZXNzIG9mIGZpdD8NCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KQUlDKG1kbC5sbSkJIyBBa2Fpa2UncyBJbmZvcm1hdGlvbiBDcml0ZXJpb24sIGxvd2VyIHZhbHVlcyBhcmUgYmV0dGVyDQpCSUMobWRsLmxtKQkjIEJheWVzaWFuIEFJQw0KbG9nTGlrKG1kbC5sbSkJIyBsb2cgbGlrZWxpaG9vZA0KYGBgDQoNCiMjIyMgU2lnbmlmaWNhbmNlIHRlc3RpbmcNCkFyZSB0aGUgYWJvdmUgaW5mb3JtYXRpdmU/IG9mIGNvdXJzZSBub3QgZGlyZWN0bHkuIElmIHdlIHdhbnQgdG8gdGVzdCBmb3Igb3ZlcmFsbCBzaWduaWZpY2FuY2Ugb2YgbW9kZWwuIFdlIHJ1biBhIG51bGwgbW9kZWwgKGFrYSBpbnRlcmNlcHQgb25seSkgYW5kIGNvbXBhcmUgbW9kZWxzLiANCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQptZGwubG0uTnVsbCA8LSBsbShkaXN0fjEsIGRhdGE9Y2FycykNCmFub3ZhKG1kbC5sbSxtZGwubG0uTnVsbCkNCg0KYGBgDQoNCg0KIyMjIyBQbG90dGluZyBmaXR0ZWQgdmFsdWVzDQpMZXQncyBwbG90IG91ciBmaXR0ZWQgdmFsdWVzLiBBbnkgcHJvYmxlbXMgd2l0aCB0aGlzPw0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQpnZ3Bsb3QuY2FyczMgPC0gZ2dwbG90KGNhcnMsYWVzKHg9c3BlZWQseT1wcmVkaWN0KG1kbC5sbSkpKSsNCiAgICAgICAgICAgICAgICBnZW9tX3BvaW50KCkrdGhlbWVfYncoYmFzZV9zaXplID0gMjApKw0KICAgICAgICAgICAgICAgIGxhYnMoeSA9ICJTdG9wcGluZyBkaXN0YW5jZSAoZnQpIiwgeCA9ICJTcGVlZCAobXBoKSIpKw0KICAgICAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZT1GKQ0KZ2dwbG90LmNhcnMzDQoNCmBgYA0KDQoNCiMjIFNvbWUgY29vbCBjb3JyZWxhdGlvbnMNCkFib3ZlLCB3ZSBoYWQgdHdvIHByZWRpY3RvcnMgYW5kIHdlIGNoZWNrZWQgZm9yIGNvcnJlbGF0aW9ucy4gV2hhdCBpZiB3ZSBoYXZlIG1vcmUgcHJlZGljdG9ycyBhbmQgd2Ugd2FudCB0byBoYXZlIGNvb2wgY29ycmVsYXRpb24gdGFibGVzIGFuZCBwbG90cz8gKHNlZSBoZXJlIGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL2JlYXV0aWZ1bC1hbmQtcG93ZXJmdWwtY29ycmVsYXRpb24tdGFibGVzLWluLXIvYW1wLykNCg0KIyMjIFdpdGggY29ycmVjdGlvbnMNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KY29yIDwtIHBzeWNobzo6YWZmZWN0aXZlICU+JSANCiAgY29ycmVsYXRpb24oKQ0Kc3VtbWFyeShjb3IpDQpwbG90KGNvcikNCnByaW50KGNvcikNCmBgYA0KDQojIyMgTm8gY29ycmVjdGlvbnMNCldoYXQgb2Ygd2UgZG9uJ3Qgd2FudCB0byB1c2UgY29ycmVjdGlvbnMgZm9yIG11bHRpcGxlIGNvbXBhcmlzb25zPw0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmNvciA8LSBwc3ljaG86OmFmZmVjdGl2ZSAlPiUgDQogIGNvcnJlbGF0aW9uKGFkanVzdD0ibm9uZSIpDQpzdW1tYXJ5KGNvcikNCnBsb3QoY29yKQ0KYGBgDQoNCg0KIyMjIElzc3VlcyB3aXRoIG5vIGNvcnJlY3Rpb25zDQpMZXQncyBnZW5lcmF0ZSBhIGRhdGFmcmFtZSB3aXRoIDExIHByZWRpY3RvcnMgYW5kIDEwMDAgcm93cyBlYWNoLiBCZWNhdXNlIHRoZXNlIGFyZSBzaW11bGF0ZWQgZGF0YSwgdGhlcmUgaXMgbm8gdW5kZXJseWluZyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZXNlLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmRmX3dpdGhfMTFfdmFycyA8LSBkYXRhLmZyYW1lKHJlcGxpY2F0ZSgxMSwgcm5vcm0oMTAwMCkpKQ0KY29yMiA8LSBjb3JyZWxhdGlvbihkZl93aXRoXzExX3ZhcnMsIGFkanVzdD0ibm9uZSIpDQpzdW1tYXJ5KGNvcjIpDQpgYGANCg0KRGVhY3RpdmF0ZSB0aGlzIGJ5IHVzaW5nICJpX2FtX2NoZWF0aW5nIiENCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KY29yMyA8LSBjb3JyZWxhdGlvbihkZl93aXRoXzExX3ZhcnMsIGFkanVzdD0ibm9uZSIsaV9hbV9jaGVhdGluZz1UUlVFKQ0Kc3VtbWFyeShjb3IzKQ0KYGBgDQoNCiMjIyBTYW1lIG1vZGVsIHdpdGggY29ycmVjdGlvbnMNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQpjb3I0IDwtIGNvcnJlbGF0aW9uKGRmX3dpdGhfMTFfdmFycykNCnN1bW1hcnkoY29yNCkNCmBgYA0KDQojIEZyb20gTGluZWFyIHRvIExvZ2lzdGljIG1vZGVscw0KSGVyZSB3ZSB3aWxsIGxvb2sgYXQgYW4gZXhhbXBsZSB3aGVuIHRoZSBvdXRjb21lIGlzIGEgYmluYXJ5IG91dGNvbWUuIFRoaXMgc2ltdWxhdGVkIGRhdGEgaXMgc3RydWN0dXJlZCBhcyBmb2xsb3dzLiBXZSBhc2tlZCBvbmUgcGFydGljaXBhbnQgdG8gbGlzdGVuIHRvIDE2NSBzZW50ZW5jZXMsIGFuZCB0byBqdWRnZSB3aGV0aGVyIHRoZXNlIGFyZSAiZ3JhbW1hdGljYWwiIG9yICJ1bmdyYW1tYXRpY2FsIi4gVGhlcmUgd2VyZSAxMDUgc2VudGVuY2VzIHRoYXQgd2VyZSAiZ3JhbW1hdGljYWwiIGFuZCA2MCAidW5ncmFtbWF0aWNhbCIuIFRoaXMgZmljdGl0aW91cyBleGFtcGxlIGNhbiBhcHBseSBpbiBhbnkgb3RoZXIgc2l0dWF0aW9uLiBMZXQncyB0aGluayBHZW9ncmFwaHk6IDE2NSBsYW5kczogMTA1ICJmbGF0IiBhbmQgNjAgIm5vbi1mbGF0IiwgZXRjLiBUaGlzIGFwcGxpZXMgdG8gYW55IGNhc2Ugd2hlcmUgeW91IG5lZWQgdG8gImNhdGVnb3Jpc2UiIHRoZSBvdXRjb21lIGludG8gdHdvIGdyb3Vwcy4gDQoNCiMjIExvYWQgYW5kIHN1bW1hcmllcw0KTGV0J3MgbG9hZCBpbiB0aGUgZGF0YSBhbmQgZG8gc29tZSBiYXNpYyBzdW1tYXJpZXMNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQpncmFtbWF0aWNhbCA8LSByZWFkLmNzdigiZ3JhbW1hdGljYWwuY3N2IikNCnN0cihncmFtbWF0aWNhbCkNCmhlYWQoZ3JhbW1hdGljYWwpDQpncmFtbWF0aWNhbCRyZXNwb25zZSA8LSByZWxldmVsKGdyYW1tYXRpY2FsJHJlc3BvbnNlLHJlZj0ieWVzIikNCnRhYmxlKGdyYW1tYXRpY2FsJGdyYW1tYXRpY2FsaXR5LGdyYW1tYXRpY2FsJHJlc3BvbnNlKQ0KYGBgDQoNCiMjIEFjY3VyYWN5IGFuZCBTaWduYWwgRGV0ZWN0aW9uIFRoZW9yeQ0KV2UgYXJlIGdlbmVyYWxseSBpbnRlcmVzdGVkIGluIHBlcmZvcm1hbmNlLCBpLmUuLCB3aGV0aGVyIHRoZSB3ZSBoYXZlICJhY2N1cmF0ZWx5IiBjYXRlZ29yaXNlZCB0aGUgb3V0Y29tZSBvciBub3QuIExldCdzIGRvIHNvbWUgc3RhdHMgb24gdGhpcyANCg0KfCAgfCBZZXMgfCBObyB8IFRvdGFsIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS18DQp8IEdyYW1tYXRpY2FsIChZZXMgQWN0dWFsKSB8IFRQID0gMTAwIHwgRk4gPSA1IHwgKFllcyBBY3R1YWwpIDEwNSB8DQp8IFVuZ3JhbW1hdGljYWwgKE5vIEFjdHVhbCkgIHwgRlAgPSAxMCB8IFROID0gNTAgfCAoTm8gQWN0dWFsKSA2MCB8DQp8IFRvdGFsIHwgKFllcyBSZXNwb25zZSkgMTEwIHwgKE5vIFJlc3BvbnNlKSA1NSB8IDE2NSB8DQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KIyMgVFAgPSBUcnVlIFBvc2l0aXZlIChIaXQpOyBGUCA9IEZhbHNlIFBvc2l0aXZlOyBGTiA9IEZhbHNlIE5lZ2F0aXZlOyBUTiA9IFRydWUgTmVnYXRpdmUNCg0KDQpUUCA9IDEwMA0KRlAgPSAxMA0KRk4gPSA1DQpUTiA9IDUwDQpUb3RhbCA9IDE2NQ0KDQooVFArVE4pL1RvdGFsICMgYWNjdXJhY3kNCihGUCtGTikvVG90YWwgIyBlcnJvciwgYWxzbyAxLWFjY3VyYWN5DQoNCiMgV2hlbiBzdGltdWx1cyA9IHllcywgaG93IG1hbnkgdGltZXMgcmVzcG9uc2UgPSB5ZXM/DQpUUC8oVFArRk4pICMgYWxzbyBUcnVlIFBvc2l0aXZlIFJhdGU6DQoNCiMgV2hlbiBzdGltdWx1cyA9IG5vLCBob3cgbWFueSB0aW1lcyByZXNwb25zZSA9IHllcz8NCkZQLyhGUCtUTikgIyBGYWxzZSBQb3NpdGl2ZSBSYXRlLCANCg0KIyBXaGVuIHN0aW11bHVzID0gbm8sIGhvdyBtYW55IHRpbWVzIHJlc3BvbnNlID0gbm8/DQpUTi8oRlArVE4pICMgc3BlY2lmaWNpdHksIGFsc28gMS1zZW5zZXRpdml0eSANCg0KIyBXaGVuIHN1YmplY3QgcmVzcG9uZHMgInllcyIgaG93IG1hbnkgdGltZXMgaXMgKHMpaGUgY29ycmVjdD8NClRQLyhUUCtGUCkgIyBwcmVjaXNpb24NCg0KIyBnZXR0aW5nIGRwcmltZSAob3IgdGhlIHNlbnNldGl2aXR5IGluZGV4KTsgYmV0YSAoYmlhcyBjcml0ZXJpb24sIDAtMSwgbG93ZXI9aW5jcmVhc2UgaW4gInllcyIpOyBBcHJpbWUgKGVzdGltYXRlIG9mIGRpc2NyaW1pbmFiaWxpdHksIDAtMSwgMT1nb29kIGRpc2NyaW1pbmF0aW9uOyAwIGF0IGNoYW5jZSk7IGJwcGQgKGIgcHJpbWUgcHJpbWUgZCwgLTEgdG8gMTsgMCA9IG5vIGJpYXMsIG5lZ2F0aXZlID0gdGVuZGVuY3kgdG8gcmVzcG9uZCAieWVzIiwgcG9zaXRpdmUgPSB0ZW5kZW5jeSB0byByZXNwb25kICJubyIpOyBjIChpbmRleCBvZiBiaWFzLCBlcXVhbHMgdG8gU0QpDQojKHNlZSBhbHNvIGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL2NvbXB1dGUtc2lnbmFsLWRldGVjdGlvbi10aGVvcnktaW5kaWNlcy13aXRoLXIvYW1wLykgDQpwc3ljaG86OmRwcmltZShUUCwgRlAsIEZOLCBUTikNCg0KYGBgDQoNCg0KIyMgR0xNDQpMZXQncyBydW4gYSBmaXJzdCBHTE0gKEdlbmVyYWxpc2VkIExpbmVhciBNb2RlbCkuIEEgR0xNIHVzZXMgYSBzcGVjaWFsIGZhbWlseSAiYmlub21pYWwiIGFzIGl0IGFzc3VtZXMgdGhlIG91dGNvbWUgaGFzIGEgYmlub21pYWwgZGlzdHJpYnV0aW9uLiBJbiBnZW5lcmFsLCByZXN1bHRzIGZyb20gYSBMb2dpc3RpYyBSZWdyZXNzaW9uIGFyZSBjbG9zZSB0byB3aGF0IHdlIGdldCBmcm9tIFNEVCAoc2VlIGFib3ZlKS4NCg0KVG8gcnVuIHRoZSByZXN1bHRzLCB3ZSB3aWxsIGNoYW5nZSB0aGUgcmVmZXJlbmNlIGxldmVsIGZvciBib3RoIHJlc3BvbnNlIGFuZCBncmFtbWF0aWNhbGl0eS4gDQoNCiMjIyBSZXN1bHRzDQoNClRoZSByZXN1bHRzIGJlbG93IHNob3cgdGhlIGxvZ29kZHMgZm9yIG91ciBtb2RlbC4gVGhlIHJlc3VsdHMgc2hvdyB0aGF0IGZvciBvbmUgdW5pdCBpbmNyZWFzZSBpbiB0aGUgcmVzcG9uc2UgKGkuZS4sIGZyb20gbm8gdG8geWVzKSwgdGhlIGxvZ29kZHMgb2YgYmVpbmcgImdyYW1tYXRpY2FsIiBpcyBpbmNyZWFzZWQgYnkgNC42MSAodGhlIGludGVyY2VwdCBzaG93cyB0aGF0IHdoZW4gdGhlIHJlc3BvbnNlIGlzICJubyIsIHRoZSBsb2dvZGRzIGFyZSAtMS42MSkuDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmxldmVscyhncmFtbWF0aWNhbCRyZXNwb25zZSkNCmxldmVscyhncmFtbWF0aWNhbCRncmFtbWF0aWNhbGl0eSkNCg0KZ3JhbW1hdGljYWwkcmVzcG9uc2UgPC0gcmVsZXZlbChncmFtbWF0aWNhbCRyZXNwb25zZSwgcmVmPSJubyIpDQpncmFtbWF0aWNhbCRncmFtbWF0aWNhbGl0eSA8LSByZWxldmVsKGdyYW1tYXRpY2FsJGdyYW1tYXRpY2FsaXR5LCByZWY9InVuZ3JhbW1hdGljYWwiKQ0KDQptZGwuZ2xtIDwtIGdsbShyZXNwb25zZX5ncmFtbWF0aWNhbGl0eSxkYXRhPWdyYW1tYXRpY2FsLGZhbWlseSA9IGJpbm9taWFsKQ0Kc3VtbWFyeShtZGwuZ2xtKQ0KDQpgYGANCg0KIyMjIEdldHRpbmcgInRydWUiIGNvZWZmaWNpZW50cyBmcm9tIEdMTQ0KSGVyZSB3ZSB3aWxsIHJ1biB0aGUgc2FtZSBHTE0gbW9kZWwgYWJvdmUgYnV0IGJ5IHN1cHJlc3NpbmcgdGhlICJJbnRlcmNlcHQiLiBUaGUgaWRlYSBoZXJlIGlzIHRvIGdldCB0aGUgInRydWUiIGNvZWZmaWNpZW50IGZvciBhICJncmFtbWF0aWNhbCIgYW5kIGEgInllcyIgcmVzcG9uc2UuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KIyBBbiBpbnRlcmNlcHQgaXMgYWx3YXlzIGluY2x1ZGVkIGluIGFueSByZWdyZXNzaW9uLCBidXQgeW91IGNhbiBzcGVjaWZ5IGl0IHdpdGggIjEiDQojIyBnbG0ocmVzcG9uc2V+MStncmFtbWF0aWNhbGl0eSxkYXRhPWdyYW1tYXRpY2FsLGZhbWlseSA9IGJpbm9taWFsKQ0KDQptZGwuZ2xtMiA8LSBnbG0ocmVzcG9uc2V+LTErZ3JhbW1hdGljYWxpdHksZGF0YT1ncmFtbWF0aWNhbCxmYW1pbHkgPSBiaW5vbWlhbCkNCnN1bW1hcnkobWRsLmdsbTIpDQoNCg0KYGBgDQoNCg0KIyMjIExvZ29kZHMgdG8gT2RkIHJhdGlvcw0KDQpMb2dvZGRzIGNhbiBiZSBtb2RpZmllZCB0byB0YWxrIGFib3V0IHRoZSBvZGRzIG9mIGFuIGV2ZW50LiBGb3Igb3VyIG1vZGVsIGFib3ZlLCB0aGUgb2RkcyBvZiAiZ3JhbW1hdGljYWwiIHJlY2VpdmluZyBhICJ5ZXMiIHJlc3BvbnNlIGluY3JlYXNlIGJ5IDEwMDsgd2hlcmVhcyByZWNlaXZpbmcgYSAibm8iIGlzIGEgbWVyZSAwLjIuIFVzaW5nIHRoZSBzZWNvbmQgbW9kZWwgKGkuZS4sIHdpdGhvdXQgYW4gSW50ZXJjZXB0KSBhbGxvd3MgdXMgdG8gZ2V0IHRoZSBvZGQgcmF0aW9ucyBmb3IgZWFjaCBvZiByZXNwb25zZXMgdG8gIlVuZ3JhbW1hdGljYWwiIGFuZCAiR3JhbW1hdGljYWwiDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KZXhwKGNvZWYobWRsLmdsbSkpDQpleHAoY29lZihtZGwuZ2xtMikpDQoNCmBgYA0KDQojIyMgTG9nT2RkcyB0byBwcm9wb3J0aW9ucw0KDQpJZiB5b3Ugd2FudCB0byB0YWxrIGFib3V0IHRoZSBwZXJjZW50YWdlICJhY2N1cmFjeSIgb2Ygb3VyIG1vZGVsLCB0aGVuIHdlIGNhbiB0cmFuc2Zvcm0gb3VyIGxvZ2dvZGRzIGludG8gcHJvcG9ydGlvbnMuIFRoaXMgc2hvd3MgdGhhdCB0aGUgcHJvcG9ydGlvbiBvZiAiZ3JhbW1hdGljYWwiIHJlY2VpdmluZyBhICJ5ZXMiIHJlc3BvbnNlIGluY3JlYXNlIGJ5IDk5JQ0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCnBsb2dpcyhjb2VmKG1kbC5nbG0pKQ0KcGxvZ2lzKGNvZWYobWRsLmdsbTIpKQ0KYGBgDQoNCiMjIyBHTE0gYXMgYSBjbGFzc2lmaWNhdGlvbiB0b29sDQoNClRoZSBjb2RlIGJlbG93IGRlbW9zbnRyYXRlcyB0aGUgbGlua3MgYmV0d2VlbiBvdXIgR0xNIG1vZGVsIGFuZCB3aGF0IHdlIGhhZCBvYnRhaW5lZCBhYm92ZSBmcm9tIFNEVC4gVGhlIHByZWRpY3Rpb25zJyB0YWJsZSBzaG93cyB0aGF0IG91ciBHTE0gd2FzIHN1Y2Nlc3NmdWwgYXQgb2J0YWluaW5nIHByZWRpY3Rpb24gdGhhdCBhcmUgaWRlbnRpY2FsIHRvIG91ciBpbml0aWFsIGRhdGEgc2V0dXAuIExvb2sgYXQgdGhlIHRhYmxlIGhlcmUgYW5kIHRoZSB0YWJsZSBhYm92ZS4gT25jZSB3ZSBoYXZlIGNyZWF0ZWQgb3VyIHRhYmxlIG9mIG91dGNvbWUsIHdlIGNhbiBjb21wdXRlIHBlcmNlbnQgY29ycmVjdCwgdGhlIHNwZWNpZmljaXR5LCB0aGUgc2Vuc2V0aXZpdHksIHRoZSBLYXBwYSBzY29yZSwgZXRjLi4gdGhpcyB5aWVsZHMgdGhlIGFjdHVhbCB2YWx1ZSB3aXRoIHRoZSBTRCB0aGF0IGlzIHJlbGF0ZWQgdG8gdmFyaWF0aW9ucyBpbiByZXNwb25zZXMNCg0KYGBge3J9DQojIyBwcmVkaWN0KG1kbC5nbG0pPjAuNSBpcyBpZGVudGljYWwgdG8gDQojIyBwcmVkaWN0KGdsbShyZXNwb25zZX5ncmFtbWF0aWNhbGl0eSxkYXRhPWdyYW1tYXRpY2FsLGZhbWlseSA9IGJpbm9taWFsKSx0eXBlPSJyZXNwb25zZSIpDQp0YmwuZ2xtIDwtIHRhYmxlKGdyYW1tYXRpY2FsJHJlc3BvbnNlLHByZWRpY3QobWRsLmdsbSx0eXBlPSJyZXNwb25zZSIpPjAuNSkNCnRibC5nbG0NClByZXNlbmNlQWJzZW5jZTo6cGNjKHRibC5nbG0pDQpQcmVzZW5jZUFic2VuY2U6OnNwZWNpZmljaXR5KHRibC5nbG0pDQpQcmVzZW5jZUFic2VuY2U6OnNlbnNpdGl2aXR5KHRibC5nbG0pDQpQcmVzZW5jZUFic2VuY2U6OkthcHBhKHRibC5nbG0pDQojIyNldGMuLg0KYGBgDQoNCiMjIyBQbG90dGluZw0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCmdyYW1tYXRpY2FsJHByb2IgPC0gcHJlZGljdChnbG0ocmVzcG9uc2V+Z3JhbW1hdGljYWxpdHksZGF0YT1ncmFtbWF0aWNhbCxmYW1pbHkgPSBiaW5vbWlhbCksdHlwZT0icmVzcG9uc2UiKQ0KZ2dwbG90KGdyYW1tYXRpY2FsLCBhZXMoeD1hcy5udW1lcmljKGdyYW1tYXRpY2FsaXR5KSwgeT1wcm9iKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAiZ2xtIiwgDQogICAgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseSA9ICJiaW5vbWlhbCIpLCANCiAgICBzZSA9IFQpICsgdGhlbWVfYncoYmFzZV9zaXplID0gMjApKw0KICAgIGxhYnMoeSA9ICJQcm9iYWJpbGl0eSIsIHggPSAiIikrDQogICAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLDEpKSsNCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoIlVuZ3JhbW1hdGljYWwiLCJHcmFtbWF0aWNhbCIpKQ0KYGBgDQoNCiMjIEdMTTogT3RoZXIgZGlzdHJpYnV0aW9ucw0KDQpJZiB5b3VyIGRhdGEgZG9lcyBub3QgZml0IGEgYmlub21pYWwgZGlzdHJpYnV0aW9uLCBhbmQgaXMgYSBtdWx0aW5vbWlhbCAoaS5lLiwgdGhyZWUgb3IgbW9yZSByZXNwb25zZSBjYXRlZ29yaWVzKSBvciBwb2lzc29uIChjb3VudCBkYXRhKSwgdGhlbiB5b3UgbmVlZCB0byB1c2UgdGhlIGdsbSBmdW5jdGlvbiB3aXRoIGEgc3BlY2lmaWMgZmFtaWx5IGZ1bmN0aW9uLiANCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0UsIGVjaG89RkFMU0V9DQojIyBGb3IgYSBtdWx0aW5vbWlhbCAoMyBvciBtb3JlIHJlc3BvbnNlIGNhdGVnb3JpZXMpLCBzZWUgYmVsb3cgYW5kIHVzZSB0aGUgZm9sbG93aW5nIHNwZWNpZmljYXRpb24NCiMjIGh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9yL2RhZS9tdWx0aW5vbWlhbC1sb2dpc3RpYy1yZWdyZXNzaW9uLw0KIyMgbWRsLm11bHRpIDwtIG5uZXQ6Om11bHRpbm9tKG91dGNvbWV+cHJlZGljdG9yLCBkYXRhPWRhdGEpDQoNCiMjIEZvciBhIHBvaXNzb24gKGNvdW50IGRhdGEpLCBzZWUgYmVsb3cgYW5kIHVzZSB0aGUgZm9sbG93aW5nIHNwZWNpZmljYXRpb24NCiMjIGh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9yL2RhZS9wb2lzc29uLXJlZ3Jlc3Npb24vDQoNCiMjIG1kbC5wb2lzc29uIDwtIGdsbShvdXRjb21lfnByZWRpY3RvciwgZGF0YT1kYXRhLGZhbWlseT0icG9pc3NvbiIpDQoNCg0KYGBgDQoNCg0KIyBDdW11bGF0aXZlIExpbmsgTW9kZWxzDQoNClRoZXNlIG1vZGVscyB3b3JrIHBlcmZlY3RseSB3aXRoIHJhdGluZyBkYXRhLiBSYXRpbmdzIGFyZSBpbmhlcmVudGx5IG9yZGVyZWQsIDEsIDIsIC4uLiBuLCBhbmQgZXhwZWN0IHRvIG9ic2VydmUgYW4gaW5jcmVhc2UgKG9yIGRlY3JlYXNlKSBpbiBvdmVyYWxsIHJhdGluZ3MgZnJvbSAxIHRvIG4uIFRvIGRlbW9uc3RyYXRlIHRoaXMsIHdlIHdpbGwgdXNlIGFuIGV4YW1wbGUgdXNpbmcgdGhlIHBhY2thZ2UgIm9yZGluYWwiLiBEYXRhIHdlcmUgZnJvbSBhIHJhdGluZyBleHBlcmltZW50IHdoZXJlIHNpeCBwYXJ0aWNpcGFudHMgcmF0ZWQgdGhlIHBlcmNlcHQgb2YgbmFzYWxpdHkgaW4gdGhlIHByb2R1Y3Rpb24gb2YgcGFydGljdWxhciBjb25zb25hbnRzIGluIEFyYWJpYy4gVGhlIGRhdGEgY2FtZSBmcm9tIG5pbmUgcHJvZHVjaW5nIHN1YmplY3RzLiBUaGUgcmF0aW5ncyB3ZXJlIGZyb20gMSB0byA1LiBUaGlzIGV4YW1wbGUgY2FuIGFwcGx5IHRvIGFueSBzdHVkeSwgZS5nLiwgcmF0aW5nIGdyYW1tYXRpY2FsaXR5IG9mIHNlbnRlbmNlcywgcmF0aW5nIGhvdyBwb3NpdGl2ZSB0aGUgc2VudGltZW50cyBhcmUgaW4gYSBhcnRpY2xlLCBpbnRlcnZpZXcgcmVzcG9uc2VzLCBldGMuDQoNCiMjIEltcG9ydGluZyBhbmQgcHJlLXByb2Nlc3NpbmcNCldlIHN0YXJ0IGJ5IGltcG9ydGluZyB0aGUgZGF0YSBhbmQgcHJvY2VzcyBpdC4gV2UgY2hhbmdlIHRoZSByZWZlcmVuY2UgbGV2ZWwgaW4gdGhlIHByZWRpY3Rvcg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCnJhdGluZyA8LSByZWFkLmNzdigicmF0aW5nLmNzdiIpDQpzdHIocmF0aW5nKQ0KIyMgd2UgbmVlZCB0byBjb252ZXJ0ICJSZXNwb25zZSIgdG8gYSBmYWN0b3INCnJhdGluZyRSZXNwb25zZSA8LSBhcy5mYWN0b3IocmF0aW5nJFJlc3BvbnNlKQ0KcmF0aW5nJENvbnRleHQgPC0gcmVsZXZlbChyYXRpbmckQ29udGV4dCwgcmVmPSJpc29sYXRpb24iKQ0KYGBgDQoNCiMjIE91ciBmaXJzdCBtb2RlbA0KV2UgcnVuIG91ciBmaXJzdCBjbG0gbW9kZWwgYXMgYSBzaW1wbGUsIGkuZS4sIHdpdGggbm8gcmFuZG9tIGVmZmVjdHMNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQptZGwuY2xtIDwtIGNsbShSZXNwb25zZX5Db250ZXh0LCBkYXRhPXJhdGluZykNCnN1bW1hcnkobWRsLmNsbSkNCmBgYA0KDQoNCiMjIFRlc3Rpbmcgc2lnbmlmaWNhbmNlIA0KV2UgY2FuIGV2YWx1YXRlIHdoZXRoZXIgIkNvbnRleHQiIGltcHJvdmVzIHRoZSBtb2RlbCBmaXQsIGJ5IGNvbXBhcmluZyBhIG51bGwgbW9kZWwgd2l0aCBvdXIgbW9kZWwuIE9mIGNvdXJzZSAiQ29udGV4dCIgaXMgaW1wcm92aW5nIHRoZSBtb2RlbCBmaXQuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KbWRsLmNsbS5OdWxsIDwtIGNsbShSZXNwb25zZX4xLCBkYXRhPXJhdGluZykNCmFub3ZhKG1kbC5jbG0sbWRsLmNsbS5OdWxsKQ0KDQpgYGANCg0KIyMgSW50ZXJwcmV0aW5nIGEgY3VtdWxhdGl2ZSBtb2RlbA0KQXMgYSB3YXkgdG8gaW50ZXJwcmV0IHRoZSBtb2RlbCwgd2UgY2FuIGxvb2sgYXQgdGhlIGNvZWZmaWNpZW50cyBhbmQgbWFrZSBzZW5zZSBvZiB0aGUgcmVzdWx0cy4gQSBDTE0gbW9kZWwgaXMgYSBMb2dpc3RpYyBtb2RlbCB3aXRoIGEgY3VtdWxhdGl2ZSBlZmZlY3QuIFRoZSAiQ29lZmZpY2llbnRzIiBhcmUgdGhlIGVzdGltYXRlcyBmb3IgZWFjaCBsZXZlbCBvZiB0aGUgZml4ZWQgZWZmZWN0OyB0aGUgIlRocmVzaG9sZCBjb2VmZmljaWVudHMiIGFyZSB0aG9zZSBvZiB0aGUgcmVzcG9uc2UuIEZvciB0aGUgZm9ybWVyLCBhIG5lZ2F0aXZlIGNvZWZmaWNpZW50IGluZGljYXRlcyBhIG5lZ2F0aXZlIGFzc29jaWF0aW9uIHdpdGggdGhlIHJlc3BvbnNlOyBhbmQgYSBwb3NpdGl2ZSBpcyBwb3NpdGl2ZWx5IGFzc29jaWF0ZWQgd2l0aCB0aGUgcmVzcG9uc2UuIFRoZSBwIHZhbHVlcyBhcmUgaW5kaWNhdGluZyB0aGUgc2lnbmlmaWNhbmNlIG9mIGVhY2ggbGV2ZWwuIEZvciB0aGUgIlRocmVzaG9sZCBjb2VmZmljaWVudHMiLCB3ZSBjYW4gc2VlIHRoZSBjdW11bGF0aXZlIGVmZmVjdHMgb2YgcmF0aW5ncyAxfDIsIDJ8MywgM3w0IGFuZCA0fDUgd2hpY2ggaW5kaWNhdGUgYW4gb3ZlcmFsbCBpbmNyZWFzZSBpbiBodCBlcmF0aW5ncyBmcm9tIDEgdG8gNS4gDQoNCiMjIFBsb3R0aW5nIA0KV2UgdXNlIGEgbW9kaWZpZWQgdmVyc2lvbiBvZiBhIHBsb3R0aW5nIGZ1bmN0aW9uIHRoYXQgYWxsb3dzIHVzIHRvIHZpc3VhbGlzZSB0aGUgZWZmZWN0cy4gRm9yIHRoaXMsIHdlIHVzZSB0aGUgYmFzZSBSIHBsb3R0aW5nIGZ1bmN0aW9ucw0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCnBhcihvbWE9YygxLCAwLCAwLCAzKSxtZ3A9YygyLCAxLCAwKSkNCnhsaW1OYXMgPSBjKG1pbihtZGwuY2xtJGJldGEpLCBtYXgobWRsLmNsbSRiZXRhKSkNCnlsaW1OYXMgPSBjKDAsMSkNCnBsb3QoMCwwLHhsaW09eGxpbU5hcywgeWxpbT15bGltTmFzLCB0eXBlPSJuIiwgeWxhYj1leHByZXNzaW9uKFByb2JhYmlsaXR5KSwgeGxhYj0iIiwgeGF4dCA9ICJuIixtYWluPSJQcmVkaWN0ZWQgY3VydmVzIC0gTmFzYWxpc2F0aW9uIixjZXg9MixjZXgubGFiPTEuNSxjZXgubWFpbj0xLjUsY2V4LmF4aXM9MS41KQ0KYXhpcyhzaWRlID0gMSwgYXQgPSBjKDAsbWRsLmNsbSRiZXRhKSxsYWJlbHMgPSBsZXZlbHMocmF0aW5nJENvbnRleHQpLCBsYXM9MixjZXg9MixjZXgubGFiPTEuNSxjZXguYXhpcz0xLjUpDQp4c05hcyA9IHNlcSh4bGltTmFzWzFdLCB4bGltTmFzWzJdLCBsZW5ndGgub3V0PTEwMCkNCmxpbmVzKHhzTmFzLCBwbG9naXMobWRsLmNsbSRUaGV0YVsxXSAtIHhzTmFzKSwgY29sPSdibGFjaycpDQpsaW5lcyh4c05hcywgcGxvZ2lzKG1kbC5jbG0kVGhldGFbMl0gLSB4c05hcyktcGxvZ2lzKG1kbC5jbG0kVGhldGFbMV0gLSB4c05hcyksIGNvbD0ncmVkJykNCmxpbmVzKHhzTmFzLCBwbG9naXMobWRsLmNsbSRUaGV0YVszXSAtIHhzTmFzKS1wbG9naXMobWRsLmNsbSRUaGV0YVsyXSAtIHhzTmFzKSwgY29sPSdncmVlbicpDQpsaW5lcyh4c05hcywgcGxvZ2lzKG1kbC5jbG0kVGhldGFbNF0gLSB4c05hcyktcGxvZ2lzKG1kbC5jbG0kVGhldGFbM10gLSB4c05hcyksIGNvbD0nb3JhbmdlJykNCmxpbmVzKHhzTmFzLCAxLShwbG9naXMobWRsLmNsbSRUaGV0YVs0XSAtIHhzTmFzKSksIGNvbD0nYmx1ZScpDQphYmxpbmUodj1jKDAsbWRsLmNsbSRiZXRhKSxsdHk9MykNCmFibGluZShoPTAsIGx0eT0iZGFzaGVkIikNCmFibGluZShoPTEsIGx0eT0iZGFzaGVkIikNCmxlZ2VuZChwYXIoJ3VzcicpWzJdLCBwYXIoJ3VzcicpWzRdLCBidHk9J24nLCB4cGQ9TkEsbHR5PTEsIGNvbD1jKCJibGFjayIsICJyZWQiLCAiZ3JlZW4iLCAib3JhbmdlIiwgImJsdWUiKSwgDQogICAgICAgbGVnZW5kPWMoIk9yYWwiLCAiMiIsICIzIiwgIjQiLCAiTmFzYWwiKSxjZXg9MC43NSkNCg0KYGBgDQoNCg0KIyBGcm9tIExpbmVhciB0byBNaXhlZCBtb2RlbHMuIFdoeSByYW5kb20gZWZmZWN0cyBtYXR0ZXINCkxldCdzIGdlbmVyYXRlIGEgbmV3IGRhdGFmcmFtZSB0aGF0IHdlIHdpbGwgdXNlIGxhdGVyIG9uIGZvciBvdXIgbWl4ZWQgbW9kZWxzDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0Kc2V0LnNlZWQoNjY2KQ0KI3dlIGNyZWF0ZSA2IHN1YmplY3RzDQpzdWJqZWN0cyA8LSBwYXN0ZTAoJ1MnLCAxOjYpDQojaGVyZSB3ZSBhZCByZXBldGl0aW9ucyB3aXRoaW4gc3BlYWtlcnMNCnN1YmplY3RzIDwtIHJlcChzdWJqZWN0cywgZWFjaCA9IDIwKQ0KaXRlbXMgPC0gcGFzdGUwKCdJdGVtJywgMToyMCkNCiNiZWxvdyByZXBlYXRlcw0KaXRlbXMgPC0gcmVwKGl0ZW1zLCA2KQ0KI2JlbG93IGlzIHRvIGdlbmVyYXRlIHJhbmRvbSBudW1iZXJzIHRoYXQgYXJlIGxvZyB2YWx1ZXMNCmxvZ0ZyZXEgPC0gcm91bmQocmV4cCgyMCkqNSwyKQ0KI2JlbG93IHdlIGFyZSByZXBlYXRpbmcgdGhlIGxvZ0ZyZXEgNiB0aW1lcyB0byBmaXQgd2l0aCB0aGUgbnVtYmVyIG9mIHNwZWFrZXJzIGFuZCBpdGVtcw0KbG9nRnJlcSA8LSByZXAobG9nRnJlcSw2KQ0KeGRmIDwtIGRhdGEuZnJhbWUoc3ViamVjdHMsIGl0ZW1zLCBsb2dGcmVxKQ0KI2JlbG93IHJlbW92ZXMgdGhlIGluZGl2aWR1YWwgdmFyaWFibGVzIHdlIGhhZCBjcmVhdGVkIGJlY2F1c2UgdGhleSBhcmUgYWxyZWFkeSBpbiB0aGUgZGF0YWZyYW1lDQpybShzdWJqZWN0cyxpdGVtcyxsb2dGcmVxKQ0KDQp4ZGYkSW50ZXJjZXB0IDwtIDMwMA0Kc3VibWVhbnMgPC0gcmVwKHJub3JtKDYsc2QgPSA0MCksMjApDQojc29ydCBtYWtlIHRoZSBtZWFucyBmb3IgZWFjaCBzdWJqZWN0IGlzIHRoZSBzYW1lLi4uDQpzdWJtZWFucyA8LSBzb3J0KHN1Ym1lYW5zKQ0KeGRmJHN1Ym1lYW5zIDwtIHN1Ym1lYW5zDQojd2UgY3JlYXRlIHRoZSBzYW1lIHRoaW5nIGZvciBpdGVtcy4uLiB3ZSBhbGxvdyB0aGUgaXRlbXMgbWVhbiB0byB2YXJ5IGJldHdlZW4gd29yZHMuLi4NCml0c21lYW5zIDwtIHJlcChybm9ybSgyMCxzZD0yMCksNikNCnhkZiRpdHNtZWFucyA8LSBpdHNtZWFucw0KeGRmJGVycm9yIDwtIHJub3JtKDEyMCxzZD0yMCkNCiNoZXJlIHdlIGNyZWF0ZSBhbiBlZmZlY3QgY29sdW1uLCAgDQojaGVyZSBmb3IgZWFjaCBsb2dGcmVxLCB3ZSBoYXZlIGEgZGVjcmVhc2Ugb2YgLTUgb2YgdGhhdCBwYXJ0aWN1bGFyIGxvZ0ZyZXEgDQp4ZGYkZWZmZWN0IDwtIC01KnhkZiRsb2dGcmVxDQoNCnhkZiRkdXIgPC0geGRmJEludGVyY2VwdCAreGRmJHN1Ym1lYW5zICt4ZGYkaXRzbWVhbnMgK3hkZiRlcnJvciArIHhkZiRlZmZlY3QNCg0KI2JlbG93IGlzIHRvIHN1YnNldCB0aGUgZGF0YSBhbmQgZ2V0IG9ubHkgYSBmZXcgY29sdW1ucy4uIHRoZSAtYyg0OjgpIHJlbW92ZXMgdGhlIGNvbHVtbnMgNCB0byA4Li4NCnhyZWFsIDwtIHhkZlssLWMoNDo4KV0NCmhlYWQoeHJlYWwpDQpybSh4ZGYsc3VibWVhbnMsaXRzbWVhbnMpDQpgYGANCg0KIyMgQ29ycmVsYXRpb25zIGFuZCBwbG90cw0KTGV0J3Mgc3RhcnQgYnkgZG9pbmcgYSBjb3JyZWxhdGlvbiB0ZXN0IGFuZCBwbG90dGluZyB0aGUgZGF0YS4gT3VyIHJlc3VsdHMgc2hvdyB0aGF0IHRoZXJlIGlzIGEgbmVnYXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiBkdXJhdGlvbiBhbmQgTG9nRnJlcXVlbmN5LCBhbmQgdGhlIHBsb3Qgc2hvd3MgdGhpcyBkZWNyZWFzZS4gDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KY29yNSA8LSB4cmVhbCAlPiUgDQogIGNvcnJlbGF0aW9uKCkNCnN1bW1hcnkoY29yNSkNCmdncGxvdC54cmVhbCA8LSBnZ3Bsb3QoeHJlYWwsYWVzKHg9bG9nRnJlcSx5PWR1cikpKw0KICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoKSt0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkrDQogICAgICAgICAgICAgICAgbGFicyh5ID0gIkR1cmF0aW9uIiwgeCA9ICJGcmVxdWVuY3kgKExvZykiKSsNCiAgICAgICAgICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwgc2U9RikNCmdncGxvdC54cmVhbA0KYGBgDQoNCg0KIyMgTGluZWFyIG1vZGVsDQoNCkxldCdzIHJ1biBhIHNpbXBsZSBsaW5lYXIgbW9kZWwgb24gdGhlIGRhdGEuIEFzIHdlIGNhbiBzZWUgYmVsb3csIHRoZXJlYXJlIHNvbWUgaXNzdWVzIHdpdGggdGhlICJzaW1wbGUiIGxpbmVhciBtb2RlbDogd2UgaGFkIHNldCBvdXIgU0QgZm9yIHN1YmplY3RzIHRvIGJlIDQwLCBidXQgdGhpcyB3YXMgcGlja2VkIHVwIGFzIDEyMCAoc2VlIGhpc3RvZ3JhbSBvZiByZXNpZHVhbHMpLiBUaGUgUVEgUGxvdCBpcyBub3QgIm5vcm1hbCIuIA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCm1kbC5sbS54cmVhbCA8LSBsbShkdXJ+bG9nRnJlcSwgZGF0YT14cmVhbCkNCnN1bW1hcnkobWRsLmxtLnhyZWFsKQ0KaGlzdChyZXNpZHVhbHMobWRsLmxtLnhyZWFsKSkNCnFxbm9ybShyZXNpZHVhbHMobWRsLmxtLnhyZWFsKSk7IHFxbGluZShyZXNpZHVhbHMobWRsLmxtLnhyZWFsKSkNCnBsb3QoZml0dGVkKG1kbC5sbS54cmVhbCkscmVzaWR1YWxzKG1kbC5sbS54cmVhbCksIGNleCA9IDQpDQpgYGANCg0KIyMgTGluZWFyIE1peGVkIE1vZGVsDQoNCk91ciBMaW5lYXIgTWl4ZWQgZWZmZWN0cyBNb2RlbCB3aWxsIHRha2UgaW50byBhY2NvdW50IHRoZSByYW5kb20gZWZmZWN0cyB3ZSBhZGRlZCBhbmQgYWxzbyBvdXIgbW9kZWwgc3BlY2lmaWNhdGlvbnMuIFdlIHVzZSBhIE1heGltdW0gTGlrZWxpaG9vZCBlc3RpbWF0ZSAoUkVNTD1GQUxTRSkgYXMgdGhpcyBpcyB3aGF0IHdlIHdpbGwgdXNlIGZvciBtb2RlbCBjb21wYXJpc29uLiBUaGUgTGluZWFyIE1peGVkIE1vZGVsIGlzIHJlZmxlY3Rpbmcgb3VyIG1vZGVsIHNwZWNpZmljYXRpb25zIFRoZSBTRCBvZiBvdXIgc3ViamVjdHMgaXMgcGlja2VkIHVwIGNvcnJlY3RseS4gVGhlIG1vZGVsIHJlc3VsdHMgYXJlICJhbG1vc3QiIHRoZSBzYW1lIGFzIG91ciBsaW5lYXIgbW9kZWwgYWJvdmUuIFRoZSBjb2VmZmljaWVudCBmb3IgdGhlICJJbnRlcmNlcHQiIGlzIGF0IDMzNy45NzMgYW5kIHRoZSBjb2VmZmljaWVudCBmb3IgTG9nRnJlcXVlbmN5IGlzIGF0IC01LjQ2MC4gVGhpcyBpbmRpY2F0ZXMgdGhhdCBmb3IgZWFjaCB1bml0IG9mIGluY3JlYXNlIGluIHRoZSBMb2dGcmVxdWVuY3ksIHRoZXJlIGlzIGEgZGVjcmVhc2UgYnkgNS40NjAgKG1zKS4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQptZGwubG1lci54cmVhbCA8LSBsbWVyKGR1cn5sb2dGcmVxKygxfHN1YmplY3RzKSsoMXxpdGVtcyksIGRhdGE9eHJlYWwsUkVNTD1GQUxTRSkNCnN1bW1hcnkobWRsLmxtZXIueHJlYWwpDQpoaXN0KHJlc2lkdWFscyhtZGwubG1lci54cmVhbCkpDQpxcW5vcm0ocmVzaWR1YWxzKG1kbC5sbWVyLnhyZWFsKSk7IHFxbGluZShyZXNpZHVhbHMobWRsLmxtZXIueHJlYWwpKQ0KcGxvdChmaXR0ZWQobWRsLmxtZXIueHJlYWwpLHJlc2lkdWFscyhtZGwubG1lci54cmVhbCksIGNleCA9IDQpDQpgYGANCg0KIyMgT3VyIHNlY29uZCBNaXhlZCBtb2RlbA0KDQpUaGlzIHNlY29uZCBtb2RlbCBhZGQgYSBieS1zdWJqZWN0IHJhbmRvbSBzbG9wZS4gUmFuZG9tIHNsb3BlcyBhbGxvdyBmb3IgdGhlIHZhcmlhdGlvbiB0aGF0IGV4aXN0cyBpbiB0aGUgcmFuZG9tIGVmZmVjdHMgdG8gYmUgdGFrZW4gaW50byBhY2NvdW50LiBBbiBpbnRlcmNlcHQgb25seSBtb2RlbCBwcm92aWRlcyBhbiBhdmVyYWdlZCB2YWx1ZXMgdG8gb3VyIHBhcnRpY2lwYW50cy4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQptZGwubG1lci54cmVhbC4yIDwtIGxtZXIoZHVyfmxvZ0ZyZXErKGxvZ0ZyZXF8c3ViamVjdHMpKygxfGl0ZW1zKSwgZGF0YT14cmVhbCxSRU1MPUZBTFNFKQ0Kc3VtbWFyeShtZGwubG1lci54cmVhbC4yKQ0KaGlzdChyZXNpZHVhbHMobWRsLmxtZXIueHJlYWwuMikpDQpxcW5vcm0ocmVzaWR1YWxzKG1kbC5sbWVyLnhyZWFsLjIpKTsgcXFsaW5lKHJlc2lkdWFscyhtZGwubG1lci54cmVhbC4yKSkNCnBsb3QoZml0dGVkKG1kbC5sbWVyLnhyZWFsLjIpLHJlc2lkdWFscyhtZGwubG1lci54cmVhbC4yKSwgY2V4ID0gNCkNCmBgYA0KDQojIyBNb2RlbCBjb21wYXJpc29uDQoNCkJ1dCB3aGVyZSBhcmUgb3VyIHAgdmFsdWVzPyBUaGUgbG1lNCBkZXZlbG9wZXJzIGRlY2lkZWQgbm90IHRvIGluY2x1ZGUgcCB2YWx1ZXMgZHVlIHRvIHZhcmlvdXMgaXNzdWVzIHdpdGggZXN0aW1hdGluZyBkZi4gV2hhdCB3ZSBjYW4gZG8gaW5zdGVhZCBpcyB0byBjb21wYXJlIG1vZGVscy4gV2UgbmVlZCB0byBjcmVhdGUgYSBudWxsIG1vZGVsIHRvIGFsbG93IGZvciBzaWduaWZpY2FuY2UgdGVzdGluZy4gQXMgZXhwZWN0ZWQgb3VyIHByZWRpY3RvciBpcyBzaWduaWZpY2FudGx5IGNvbnRyaWJ1dGluZyB0byB0aGUgZGlmZmVyZW5jZS4gDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KbWRsLmxtZXIueHJlYWwuTnVsbCA8LSBsbWVyKGR1cn4xKyhsb2dGcmVxfHN1YmplY3RzKSsoMXxpdGVtcyksIGRhdGE9eHJlYWwsUkVNTD1GQUxTRSkNCmFub3ZhKG1kbC5sbWVyLnhyZWFsLk51bGwsbWRsLmxtZXIueHJlYWwuMikNCmBgYA0KDQpBbHNvLCBkbyB3ZSByZWFsbHkgbmVlZCByYW5kb20gc2xvcGVzPyBGcm9tIHRoZSByZXN1bHQgYmVsb3csIHdlIGRvbid0IHNlZW0gdG8gbmVlZCByYW5kb20gc2xvcGVzIGF0IGFsbCwgZ2l2ZW4gdGhhdCBhZGRpbmcgcmFuZG9tIHNsb3BlcyBkb2VzIG5vdCBpbXByb3ZlIHRoZSBtb2RlbCBmaXQuIEkgYWx3YXlzIHJlY29tbWVuZCB0ZXN0aW5nIHRoaXMuIE1vc3Qgb2YgdGhlIHRpbWUgSSBrZWVwIHJhbmRvbSBzbG9wZXMuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KYW5vdmEobWRsLmxtZXIueHJlYWwsbWRsLmxtZXIueHJlYWwuMikNCmBgYA0KDQpCdXQgaWYgeW91IGFyZSByZWFsbHkgKHJlYWxseSEhISkgb2JzZXNzZWQgYnkgcCB2YWx1ZXMsIHRoZW4geW91IGNhbiBhbHNvIHVzZSBsbWVyVGVzdC4gQlVUIHVzZSBhZnRlciBjb21wYXJpbmcgbW9kZWxzIHRvIGV2YWx1YXRlIGNvbnRyaWJ1dGlvbiBvZiBwcmVkaWN0b3JzDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KbGlicmFyeShsbWVyVGVzdCkNCm1kbC5sbWVyLnhyZWFsLmxtZXJUZXN0IDwtIGxtZXIoZHVyfmxvZ0ZyZXErKGxvZ0ZyZXF8c3ViamVjdHMpKygxfGl0ZW1zKSwgZGF0YT14cmVhbCxSRU1MPVRSVUUpDQpzdW1tYXJ5KG1kbC5sbWVyLnhyZWFsLmxtZXJUZXN0KQ0KZGV0YWNoKCJwYWNrYWdlOmxtZXJUZXN0IiwgdW5sb2FkPVRSVUUpDQpgYGANCg0KDQojIyBPdXIgZmluYWwgTWl4ZWQgbW9kZWwNCg0KT3VyIGZpbmFsIG1vZGVsIHVzZXMgUkVNTCAob3IgUmVzdHJpY3RlZCBNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdGUgb2YgVmFyaWFuY2UgQ29tcG9uZW50KSB0byBlc3RpbWF0ZSB0aGUgbW9kZWwuIA0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCm1kbC5sbWVyLnhyZWFsLkZ1bGwgPC0gbG1lcihkdXJ+bG9nRnJlcSsobG9nRnJlcXxzdWJqZWN0cykrKDF8aXRlbXMpLCBkYXRhPXhyZWFsLFJFTUw9VFJVRSkNCnN1bW1hcnkobWRsLmxtZXIueHJlYWwuRnVsbCkNCmFub3ZhKG1kbC5sbWVyLnhyZWFsLkZ1bGwpDQpoaXN0KHJlc2lkdWFscyhtZGwubG1lci54cmVhbC5GdWxsKSkNCnFxbm9ybShyZXNpZHVhbHMobWRsLmxtZXIueHJlYWwuRnVsbCkpOyBxcWxpbmUocmVzaWR1YWxzKG1kbC5sbWVyLnhyZWFsLkZ1bGwpKQ0KcGxvdChmaXR0ZWQobWRsLmxtZXIueHJlYWwuRnVsbCkscmVzaWR1YWxzKG1kbC5sbWVyLnhyZWFsLkZ1bGwpLCBjZXggPSA0KQ0KYGBgDQoNCg0KIyMgRGlzc2VjdGluZyB0aGUgbW9kZWwNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0V9DQpjb2VmKG1kbC5sbWVyLnhyZWFsLkZ1bGwpDQpmaXhlZihtZGwubG1lci54cmVhbC5GdWxsKQ0KZml4ZWYobWRsLmxtZXIueHJlYWwuRnVsbClbMV0NCmZpeGVmKG1kbC5sbWVyLnhyZWFsLkZ1bGwpWzJdDQoNCmNvZWYobWRsLmxtZXIueHJlYWwuRnVsbCkkYHN1YmplY3RzYFsxXQ0KY29lZihtZGwubG1lci54cmVhbC5GdWxsKSRgc3ViamVjdHNgWzJdDQoNCmNvZWYobWRsLmxtZXIueHJlYWwuRnVsbCkkYGl0ZW1zYFsxXQ0KY29lZihtZGwubG1lci54cmVhbC5GdWxsKSRgaXRlbXNgWzJdDQoNCmBgYA0KDQojIyBVc2luZyBwcmVkaWN0aW9ucyBmcm9tIG91ciBtb2RlbA0KSW4gZ2VuZXJhbCwgSSB1c2UgdGhlIHByZWRpY3Rpb24gZnJvbSBteSBmaW5hbCBtb2RlbCBpbiBhbnkgcGxvdHMuIFRvIGdlbmVyYXRlIHRoaXMsIHdlIGNhbiB1c2UgdGhlIGZvbGxvd2luZw0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCnhyZWFsJFByZWRfRHVyIDwtIHByZWRpY3QobG1lcihkdXJ+bG9nRnJlcSsobG9nRnJlcXxzdWJqZWN0cykrKDF8aXRlbXMpLCBkYXRhPXhyZWFsLFJFTUw9VFJVRSkpDQpnZ3Bsb3QueHJlYWwuUHJlZCA8LSBnZ3Bsb3QoeHJlYWwsYWVzKHg9bG9nRnJlcSx5PVByZWRfRHVyKSkrDQogICAgICAgICAgICAgICAgZ2VvbV9wb2ludCgpK3RoZW1lX2J3KGJhc2Vfc2l6ZSA9IDIwKSsNCiAgICAgICAgICAgICAgICBsYWJzKHkgPSAiRHVyYXRpb24iLCB4ID0gIkZyZXF1ZW5jeSAoTG9nKSIsdGl0bGU9IlByZWRpY3RlZCIpKw0KICAgICAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZT1GKStjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDIwMCw0NTApKQ0KZ2dwbG90LnhyZWFsLlByZWQNCiMjIG9yaWdpbmFsIHBsb3QNCmdncGxvdC54cmVhbC5PcmlnaW5hbCA8LSBnZ3Bsb3QoeHJlYWwsYWVzKHg9bG9nRnJlcSx5PWR1cikpKw0KICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoKSt0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkrDQogICAgICAgICAgICAgICAgbGFicyh5ID0gIkR1cmF0aW9uIiwgeCA9ICJGcmVxdWVuY3kgKExvZykiLHRpdGxlPSJPcmlnaW5hbCIpKw0KICAgICAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZT1GKStjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDIwMCw0NTApKQ0KZ2dwbG90LnhyZWFsLk9yaWdpbmFsDQpgYGANCg0KIyMgR0xNTSBhbmQgQ0xNTQ0KDQpUaGUgY29kZSBhYm92ZSB3YXMgdXNpbmcgYSBMaW5lYXIgTWl4ZWQgRWZmZWN0cyBNb2RlbGxpbmcuIFRoZSBvdXRjb21lIHdhcyBhIG51bWVyaWMgb2JqZWN0LiBJbiBzb21lIGNhc2VzIChhcyB3ZSBoYXZlIHNlZW4gYWJvdmUpLCB3ZSBtYXkgaGF2ZTogDQoNCjEuIEJpbmFyeSBvdXRjb21lIChiaW5vbWlhbCkNCjIuIENvdW50IGRhdGEgKHBvaXNzb24pLCANCjMuIE11bHRpLWNhdGVnb3J5IG91dGNvbWUgKG11bHRpbm9taWFsKQ0KNC4gUmF0aW5nIGRhdGEgKGN1bXVsYXRpdmUgZnVuY3Rpb24pDQoNClRoZSBjb2RlIGJlbG93IGdpdmVzIHlvdSBhbiBpZGVhIG9mIGhvdyB0byBzcGVjaWZpY3kgdGhlc2UgbW9kZWxzDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KDQojIyBCaW5vbWlhbCBmYW1pbHkNCiMjIGxtZTQ6OmdsbWVyKG91dGNvbWV+cHJlZGljdG9yKHMpKygxfHN1YmplY3QpKygxfGl0ZW1zKS4uLiwgZGF0YT1kYXRhLCBmYW1pbHk9Ymlub21pYWwpDQoNCiMjIFBvaXNzb24gZmFtaWx5DQojIyBsbWU0OjpnbG1lcihvdXRjb21lfnByZWRpY3RvcihzKSsoMXxzdWJqZWN0KSsoMXxpdGVtcykuLi4sIGRhdGE9ZGF0YSwgZmFtaWx5PXBvaXNzb24pDQoNCiMjIE11bHRpbm9taWFsIGZhbWlseQ0KIyMgYSBiaXQgY29tcGxpY2F0ZWQgYXMgdGVocmUgaXMgYSBuZWVkIHRvIHVzZSBCYXllc2lhbiBhcHByb2FjaGVzLCBzZWUgZS5nLiwgDQojIyBnbG1tQURNQg0KIyMgbWl4Y2F0DQojIyBNQ01DZ2xtbQ0KIyMgc2VlIGh0dHBzOi8vZ2lzdC5naXRodWIuY29tL2Nhc2FsbGFzLzgyNjM4MTgNCg0KIyMgUmF0aW5nIGRhdGEsIHVzZSBmb2xsb3dpbmcNCiMjIG9yZGluYWw6OmNsbW0ob3V0Y29tZX5wcmVkaWN0b3IocykrKDF8c3ViamVjdCkrKDF8aXRlbXMpLi4uLCBkYXRhPWRhdGEpDQoNCg0KIyMgUmVtZW1iZXIgdG8gdGVzdCBmb3IgcmFuZG9tIGVmZmVjdHMgYW5kIHdoZXRoZXIgc2xvcGVzIGFyZSBuZWVkZWQuDQoNCmBgYA0KDQojIFJ1bm5pbmcgeW91ciBjb2RlIGFuZCBhdXRvbWF0aWNhbGx5IGRyYXdpbmcgcXVhbGl0eSB0YWJsZXMgZm9yIHB1YmxpY2F0aW9ucw0KDQpTZWUgdGhpcyB3ZWJzaXRlIGZvciBob3cgdG8gcnVuIGFuZCBhdXRvbWF0aWNhbGx5IGRyYXcgdGFibGVzIGZvciB5b3VyIHB1YmxpY2F0aW9ucy4gSSBoYXZlbid0IHRlc3RlZCB0aGlzIGJ1dCBpdCBpcyBpbiBkZXZlbG9wbWVudDoNCjcpCWh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL2VsZWdhbnQtcmVncmVzc2lvbi1yZXN1bHRzLXRhYmxlcy1hbmQtcGxvdHMtaW4tci10aGUtZmluYWxmaXQtcGFja2FnZS9hbXANCg0K