🔙 Home

library(tidyverse)
library(lme4)

Using prop.test()

The following is some boilerplate R code I use to test whether, in my forced-choice (binomial) data, my participants are selecting one option different from chance (50%).

# Date: July 21, 2016; 
# updated January 30, 2018; 
# updated May 3, 2018;
# updated August 6, 2021

data = read.csv("data/binomial-data.csv",header=TRUE) # data file
# "selectCode" is the responses which consist of 1s (hits) and 0s (misses)
head(data)

1-sample proportions tests

A 1-sample proportions test is excellent for determining whether a single condition or vector of hits and misses (successes and failures) differs from some pre-determined level. This level could be chance (e.g., 50% with two options, 33.3% with three, etc) or it could be another relevant value (e.g., some threshold for accuracy).

First, we create a summary dataset from our raw data.

data.pct <- data %>% # save following operation into new dataset
   group_by(experiment, condition) %>% # choose which columns define "100%" of each subset
   summarise(total = n()) %>% # summarize to find the total number of each subset
   full_join(data, ., by = c("experiment", "condition")) %>% # join it back to your data
   group_by(experiment, condition, selectCode, total) %>% # choose columns to summarize
   summarise(count = n()) %>% # count how many correct and incorrect responses there were
   mutate(percent = count/total) # calculate the percent for each subset
`summarise()` has grouped output by 'experiment'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'experiment', 'condition', 'selectCode'. You can override using the `.groups` argument.
data.pct

Next, check if the Baseline condition in Experiment 1 is different from chance levels of performance.

# prop.test( x = successTrials ,
#            n = totalTrials ,
#            p = probabilityOfSuccess )

data.pct %>% # taking our summary table
   filter(experiment == "first", # keep only Experiment 1
          condition == "Baseline", # keep only Baseline condition
          selectCode == 1) %>% # keep only correct selections
   pull(count) -> baseline.success.exp1 # pull out the value(s) from the count column

data.pct %>% # taking our summary table
   filter(experiment == "first", # keep only Experiment 1
          condition == "Baseline", # keep only Baseline condition
          selectCode == 1) %>% # keep only correct selections
   pull(total) -> baseline.total.exp1 # pull out the value(s) from the total column


prop.test(x = baseline.success.exp1,
          n = baseline.total.exp1,
          p = 0.5) # check against "chance" levels of 50%

    1-sample proportions test with continuity correction

data:  baseline.success.exp1 out of baseline.total.exp1, null probability 0.5
X-squared = 1.5042, df = 1, p-value = 0.22
alternative hypothesis: true p is not equal to 0.5
95 percent confidence interval:
 0.4763963 0.6055812
sample estimates:
        p 
0.5416667 

No, it is not detectably different from chance (β=0.541, χ²(1)=1.50, p=0.22).

Now, do the same test with the Treatment condition in Experiment 1.

# prop.test( x = successTrials ,
#            n = totalTrials ,
#            p = probabilityOfSuccess )

data.pct %>% # taking our summary table
   filter(experiment == "first", # keep only Experiment 1
          condition == "Treatment", # keep only Baseline condition
          selectCode == 1) %>% # keep only correct selections
   pull(count) -> treatment.success.exp1 # pull out the value(s) from the count column

data.pct %>% # taking our summary table
   filter(experiment == "first", # keep only Experiment 1
          condition == "Treatment", # keep only Baseline condition
          selectCode == 1) %>% # keep only correct selections
   pull(total) -> treatment.total.exp1 # pull out the value(s) from the total column


prop.test(x = treatment.success.exp1,
          n = treatment.total.exp1,
          p = 0.5) # check against "chance" levels of 50%

    1-sample proportions test with continuity correction

data:  treatment.success.exp1 out of treatment.total.exp1, null probability 0.5
X-squared = 61.004, df = 1, p-value = 5.695e-15
alternative hypothesis: true p is not equal to 0.5
95 percent confidence interval:
 0.6937691 0.8062530
sample estimates:
        p 
0.7541667 

Yes, it is detectably different from chance (β=0.754, χ²(1)=61.0, p<0.0001).

GLM to compare across conditions

Because we are using binomial data, we must use a logistic regression. The logistic regress is nearly identical to a linear regression, but rather than finding a linear line of best fit, it finds a log-linear line of best fit, which looks like an S-curve when the predictor is continuous.

Let’s look at Experiment 1 only.

model.glm <- glm(selectCode ~ 1 +
                              condition,
                 family = "binomial",
                 data = data %>% filter(experiment == "first"))
summary(model.glm)

Call:
glm(formula = selectCode ~ 1 + condition, family = "binomial", 
    data = data %>% filter(experiment == "first"))

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.6752  -1.2491   0.7512   1.1073   1.1073  

Coefficients:
                   Estimate Std. Error z value Pr(>|z|)    
(Intercept)          0.1671     0.1296   1.289    0.197    
conditionTreatment   0.9539     0.1981   4.814 1.48e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 622.78  on 479  degrees of freedom
Residual deviance: 598.74  on 478  degrees of freedom
AIC: 602.74

Number of Fisher Scoring iterations: 4

Here we can see that our maximal model provides an estimate (β) for the Treatment condition as 0.9539. This is significant because the standard error (SE, or Std. Error) is smaller than the estimate (0.1981 < 0.9539), which is reflected in the test statistic, in this case a Z-value, of 4.814. This is larger than the threshold needed for significance, so the p-value is less than 0.05. In fact, the p-value written in scientific notation is is 1.48e-6 (“one point four eight times ten to the negative sixth power”), which means you would need to move the decimal point six places to the left (p = 0.00000148).

If you have a more complex model, e.g., a GLMER that also takes into account the other experiments included in this dataset, you may wish to do some model comparison.

model.max <- glmer(selectCode ~ 1 + # outcome measure as a function of the null hypothesis
                      condition + # and condition
                      experiment + # and experiment
                      condition:experiment + # and the interaction of condition and experiment
                      (1 | subject) + # with random intercepts for subject
                      (1 | item), # and item
                   family = "binomial", # using a binomial linking function
                   data = data) # and our dataset
boundary (singular) fit: see ?isSingular
# * the null hypothesis is by default included in all models and 
#   can be explicitly written out as 1, which I have done to allow
#   all the other terms to be on their own lines
summary(model.max)
Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
 Family: binomial  ( logit )
Formula: selectCode ~ 1 + condition + experiment + condition:experiment +      (1 | subject) + (1 | item)
   Data: data

     AIC      BIC   logLik deviance df.resid 
  1662.1   1704.3   -823.0   1646.1     1432 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.1688 -0.7435  0.3197  0.8767  1.9048 

Random effects:
 Groups  Name        Variance  Std.Dev. 
 item    (Intercept) 1.483e-02 1.218e-01
 subject (Intercept) 7.777e-10 2.789e-05
Number of obs: 1440, groups:  item, 24; subject, 20

Fixed effects:
                                    Estimate Std. Error z value Pr(>|z|)    
(Intercept)                           0.1677     0.1345   1.247   0.2124    
conditionTreatment                    0.9568     0.2046   4.676 2.93e-06 ***
experimentsecond                     -0.4030     0.1839  -2.192   0.0284 *  
experimentthird                      -0.7891     0.1878  -4.202 2.64e-05 ***
conditionTreatment:experimentsecond   1.5285     0.3233   4.728 2.27e-06 ***
conditionTreatment:experimentthird   -1.5761     0.2860  -5.511 3.56e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
                    (Intr) cndtnT exprmnts exprmntt cndtnTrtmnt:xprmnts
cndtnTrtmnt         -0.657                                             
exprmntscnd         -0.681  0.447                                      
exprmntthrd         -0.667  0.438  0.488                               
cndtnTrtmnt:xprmnts  0.388 -0.595 -0.569   -0.278                      
cndtnTrtmnt:xprmntt  0.438 -0.674 -0.320   -0.655    0.425             
optimizer (Nelder_Mead) convergence code: 0 (OK)
boundary (singular) fit: see ?isSingular

While we can see that the Treatment condition is still significantly different to the Baseline condition (β=0.96, SE=0.20, Z=4.68, p<0.0001), we can also see that the Experiments are only listed as pairwise comparisons, thus so are the interactions. In order to report them as a main effect and interaction effect rather than pairwise comparisons, we should do model comparison.

# remove the interaction term to compare this nested depleted model to the maximal one
model.int <- glmer(selectCode ~ 1 + 
                      condition + 
                      experiment + 
                      # condition:experiment + 
                      (1 | subject) + 
                      (1 | item), 
                   family = "binomial", 
                   data = data) 
boundary (singular) fit: see ?isSingular
# this model is identical to our maximal model, except we have commented out
# the term of interest

anova(model.max, model.int)
Data: data
Models:
model.int: selectCode ~ 1 + condition + experiment + (1 | subject) + (1 | item)
model.max: selectCode ~ 1 + condition + experiment + condition:experiment + (1 | subject) + (1 | item)
          npar    AIC    BIC  logLik deviance  Chisq Df Pr(>Chisq)    
model.int    6 1761.2 1792.8 -874.57   1749.2                         
model.max    8 1662.1 1704.3 -823.04   1646.1 103.06  2  < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Here, the model comparison shows us that the overall contribution of the interaction term is significant to the fit of our model (χ²(1)=103.06, p<0.0001). This means we can say there is a significant interaction between condition and experiment.

We should also check if there is a main effect of experiment:

# remove the all terms containing the term of interest to compare this nested depleted model
model.exp <- glmer(selectCode ~ 1 + 
                      condition + 
                      # experiment + 
                      # condition:experiment + 
                      (1 | subject) + 
                      (1 | item), 
                   family = "binomial", 
                   data = data) 
boundary (singular) fit: see ?isSingular
# this model is identical to our maximal model, except we have commented out
# the term of interest and all other terms containing it

# since we commented out two terms, we should compare this model to the next greater, not the maximal one
anova(model.int, model.exp)
Data: data
Models:
model.exp: selectCode ~ 1 + condition + (1 | subject) + (1 | item)
model.int: selectCode ~ 1 + condition + experiment + (1 | subject) + (1 | item)
          npar    AIC    BIC  logLik deviance  Chisq Df Pr(>Chisq)    
model.exp    4 1947.8 1968.9 -969.90   1939.8                         
model.int    6 1761.2 1792.8 -874.57   1749.2 190.66  2  < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

From this, we can see there is a main effect of experiment, regardless of pairwise comparisons (χ²(1)=190.66, p<0.0001).

Now, to understand what this means, we must plot our data. (In fact, we should have done this first…)

Plotting results with error bars

Tidyverse

This is tidyverse style code for producing a visualization of the data analyzed above. First, we will create a new dataset that will allow us to plot calculated estimates and confidence intervals. It is not necessary to save this as a separate dataset – it could be piped directly into the plot. However, this way, you get a chance to see the content and structure of the dataset first.

data.plot <- data.pct %>% # start with our SUMMARY data, not raw data and save a new copy
   # create a column that labels the select codes
   mutate(observed = case_when(selectCode == 1 ~ "correct", # 1 = correct
                               selectCode == 0 ~ "incorrect"), # 0 = incorrect
          observed = factor(observed, # make it a factor
                            levels = c("incorrect", "correct"))) %>% # order the levels
   rowwise() %>% # the next operate will be conducted for each row
   # using rowwise() is essential for applying certain base R functions such as prop.test
   # create columns for estimate, upper CI value, and lower CI value:
   mutate(estimate = prop.test(x = count, n = total, p = 0.5)$estimate, # extract estimate
          confint.lower = prop.test(x = count, n = total, p = 0.5)$conf.int[1], # extract lower CI
          confint.upper = prop.test(x = count, n = total, p = 0.5)$conf.int[2]) # extract upper CI

data.plot

Now, let’s plot it:

data.plot %>% 
   ggplot(aes(x = condition, # x-axis is by condition
              y = estimate, # y-axis is by calculated estimate
              fill = observed)) + # this will allow us to hide extra error bars later on*
   theme_bw() + # make the plot clean and pretty
   geom_col() + # plot the bars as stacked columns
   geom_hline(aes(yintercept=.5), # plot a line at 0.5 (50%)
              colour = "black", # make it black
              alpha=.5, # make it half-transparent
              linetype = "dashed") + # make it a dashed line
   geom_errorbar(aes(ymin = confint.lower, # define lower CI limit
                     ymax = confint.upper, # define upper CI limit
                     alpha = observed), # define transparency for error bars*
                 width = .1) + # keep the crossbars narrow
   scale_alpha_manual(values = c(0,1), guide = "none") + # *hide extra error bars for 'incorrect' values
   scale_y_continuous(labels = scales::percent_format()) + # y-axis converted from decimal to percent
   ylab("percent selected") + # relabel the y-axis
   facet_grid(~experiment) + # put each experiment in its own facet
   NULL # this line allows you comment out the previous one(s) without breaking the plot

From this plot, you can see that the Baseline conditions overlap with chance levels in Experiments 1 and 2 but not 3, where it is far below chance. Furthermore, you can see that Experiments 1 and 2 have higher-than-chance Treatment conditions, but not in Experiment 3. This is likely what is driving the interaction effect we observed. Now the statistical tests can be discussed with more clarity and conclusions about how outcomes in the two conditions and three experiments compare (and what this means) can be drawn.

Base R graphics

This code builds directly off of the previous code to produce the graph shown at the bottom of this section. It uses only the base graphics library. It looks at Experiment 1 only.

Step 1

# Author: Lauren M Ackerman
# Available: https://lmackerman.com/r-snippets/
# Date: January 30, 2018

# store the prop.test() results for use in the graph 
err.cond.1 <- prop.test(baseline.success.exp1, baseline.total.exp1, p = 0.5)
err.cond.2 <- prop.test(treatment.success.exp1, treatment.total.exp1, p = 0.5)

# format the table as percentages rounded to 2 decimal places
numOfConds <- length(levels(as.factor(data$condition[data$experiment=="first"])))
numerator <- (table(data$selection[data$experiment=="first"], 
                    data$condition[data$experiment=="first"]))
denominator <- (length(data$selection[data$experiment=="first"])/numOfConds)
dfTable <- (numerator/denominator)*100

counts <- round(dfTable,2)
barplot(counts,
#  legend = rownames(counts),
   main=paste0("Choice of ",rownames(counts)[1]," and ",rownames(counts)[2]," in a forced-choice task"),
   ylab="Percent (%)", #xlab="condition", 
   cex.names=1, names=c(colnames(counts)[1], colnames(counts)[2]),
   col=c("lightseagreen","salmon"), border=c("black","black")
   )

Step 2

barplot(counts,
   main=paste0("Choice of ",rownames(counts)[1]," and ",rownames(counts)[2]," in a forced-choice task"),
   ylab="Percent (%)",  
   cex.names=1, names=c(colnames(counts)[1], colnames(counts)[2]),
   col=c("lightseagreen","salmon"), border=c("black","black")
   )
# label the stacked sections of the bar plot with the estimated percents
mtext(paste0(rownames(counts)[1]," = ", counts[1],"%"), side=1,line=-1.5,at=0.7,col="black",cex=1)
mtext(paste0(rownames(counts)[1]," = ", counts[3],"%"), side=1,line=-1.5,at=1.9,col="black",cex=1)
mtext(paste0(rownames(counts)[2]," = ", counts[2],"%"), side=3,line=-1.5,at=0.7,col="black",cex=1)
mtext(paste0(rownames(counts)[2]," = ", counts[4],"%"), side=3,line=-1.5,at=1.9,col="black",cex=1)

Step 3

barplot(counts,
#  legend = rownames(counts),
   main=paste0("Choice of ",rownames(counts)[1]," and ",rownames(counts)[2]," in a forced-choice task"),
   ylab="Percent (%)", #xlab="condition", 
   cex.names=1, names=c(colnames(counts)[1], colnames(counts)[2]),
   col=c("lightseagreen","salmon"), border=c("black","black")
   )
# label the stacked sections of the bar plot with the estimated percents
mtext(paste0(rownames(counts)[1]," = ", counts[1],"%"), side=1,line=-1.5,at=0.7,col="black",cex=1)
mtext(paste0(rownames(counts)[1]," = ", counts[3],"%"), side=1,line=-1.5,at=1.9,col="black",cex=1)
mtext(paste0(rownames(counts)[2]," = ", counts[2],"%"), side=3,line=-1.5,at=0.7,col="black",cex=1)
mtext(paste0(rownames(counts)[2]," = ", counts[4],"%"), side=3,line=-1.5,at=1.9,col="black",cex=1)

# add the confidence intervals based on the prop.test() results
arrows(0.7,counts[1],0.7,(unlist(err.cond.1[6])[1])*100, angle=90,col="black",lwd=1)
arrows(0.7,counts[1],0.7,(unlist(err.cond.1[6])[2])*100, angle=90,col="black",lwd=1)
arrows(1.9,counts[3],1.9,(unlist(err.cond.2[6])[1])*100, angle=90,col="black",lwd=1)
arrows(1.9,counts[3],1.9,(unlist(err.cond.2[6])[2])*100, angle=90,col="black",lwd=1)

NOTE: Sometimes, 1-unlist() should be used because the count of “1” answers is on the top of this graph, alphabetically. If your error bars are misaligned, you may need to add 1- to these four lines of code.

Text size (cex) and line width of the error bars (lwd) are both set to size 1 in the above code, but should be increased for use on posters. The colors selected here are a close match to the default aesthetics of ggplot2, but are easily changed.

LS0tCnRpdGxlOiAiRm9yY2VkIGNob2ljZSBkYXRhIgphdXRob3I6ICJMYXVyZW4gTSBBY2tlcm1hbiIKZGF0ZTogIkxhc3QgdXBkYXRlZDogMDYgQXVndXN0IDIwMjEiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBpbmNsdWRlczogCiAgICAgIGluX2hlYWRlcjogZ29vZ2xlX2FuYWx5dGljcy5odG1sCi0tLQpb8J+UmSBIb21lXShodHRwczovL3ZlcmJpbmdub3Vucy5naXRodWIuaW8vbm90ZWJvb2tzLykKCmBgYHtyIHNldHVwLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsbWU0KQpgYGAKCgojIFVzaW5nIHByb3AudGVzdCgpCgpUaGUgZm9sbG93aW5nIGlzIHNvbWUgYm9pbGVycGxhdGUgUiBjb2RlIEkgdXNlIHRvIHRlc3Qgd2hldGhlciwgaW4gbXkgZm9yY2VkLWNob2ljZSAoYmlub21pYWwpIGRhdGEsIG15IHBhcnRpY2lwYW50cyBhcmUgc2VsZWN0aW5nIG9uZSBvcHRpb24gZGlmZmVyZW50IGZyb20gY2hhbmNlICg1MCUpLgoKYGBge3J9CiMgRGF0ZTogSnVseSAyMSwgMjAxNjsgCiMgdXBkYXRlZCBKYW51YXJ5IDMwLCAyMDE4OyAKIyB1cGRhdGVkIE1heSAzLCAyMDE4OwojIHVwZGF0ZWQgQXVndXN0IDYsIDIwMjEKCmRhdGEgPSByZWFkLmNzdigiZGF0YS9iaW5vbWlhbC1kYXRhLmNzdiIsaGVhZGVyPVRSVUUpICMgZGF0YSBmaWxlCiMgInNlbGVjdENvZGUiIGlzIHRoZSByZXNwb25zZXMgd2hpY2ggY29uc2lzdCBvZiAxcyAoaGl0cykgYW5kIDBzIChtaXNzZXMpCmhlYWQoZGF0YSkKYGBgCgojIyAxLXNhbXBsZSBwcm9wb3J0aW9ucyB0ZXN0cwoKQSAxLXNhbXBsZSBwcm9wb3J0aW9ucyB0ZXN0IGlzIGV4Y2VsbGVudCBmb3IgZGV0ZXJtaW5pbmcgd2hldGhlciBhIHNpbmdsZSBjb25kaXRpb24gb3IgdmVjdG9yIG9mIGhpdHMgYW5kIG1pc3NlcyAoc3VjY2Vzc2VzIGFuZCBmYWlsdXJlcykgZGlmZmVycyBmcm9tIHNvbWUgcHJlLWRldGVybWluZWQgbGV2ZWwuIFRoaXMgbGV2ZWwgY291bGQgYmUgY2hhbmNlIChlLmcuLCA1MCUgd2l0aCB0d28gb3B0aW9ucywgMzMuMyUgd2l0aCB0aHJlZSwgZXRjKSBvciBpdCBjb3VsZCBiZSBhbm90aGVyIHJlbGV2YW50IHZhbHVlIChlLmcuLCBzb21lIHRocmVzaG9sZCBmb3IgYWNjdXJhY3kpLgoKRmlyc3QsIHdlIGNyZWF0ZSBhIHN1bW1hcnkgZGF0YXNldCBmcm9tIG91ciByYXcgZGF0YS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhLnBjdCA8LSBkYXRhICU+JSAjIHNhdmUgZm9sbG93aW5nIG9wZXJhdGlvbiBpbnRvIG5ldyBkYXRhc2V0CiAgIGdyb3VwX2J5KGV4cGVyaW1lbnQsIGNvbmRpdGlvbikgJT4lICMgY2hvb3NlIHdoaWNoIGNvbHVtbnMgZGVmaW5lICIxMDAlIiBvZiBlYWNoIHN1YnNldAogICBzdW1tYXJpc2UodG90YWwgPSBuKCkpICU+JSAjIHN1bW1hcml6ZSB0byBmaW5kIHRoZSB0b3RhbCBudW1iZXIgb2YgZWFjaCBzdWJzZXQKICAgZnVsbF9qb2luKGRhdGEsIC4sIGJ5ID0gYygiZXhwZXJpbWVudCIsICJjb25kaXRpb24iKSkgJT4lICMgam9pbiBpdCBiYWNrIHRvIHlvdXIgZGF0YQogICBncm91cF9ieShleHBlcmltZW50LCBjb25kaXRpb24sIHNlbGVjdENvZGUsIHRvdGFsKSAlPiUgIyBjaG9vc2UgY29sdW1ucyB0byBzdW1tYXJpemUKICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUgIyBjb3VudCBob3cgbWFueSBjb3JyZWN0IGFuZCBpbmNvcnJlY3QgcmVzcG9uc2VzIHRoZXJlIHdlcmUKICAgbXV0YXRlKHBlcmNlbnQgPSBjb3VudC90b3RhbCkgIyBjYWxjdWxhdGUgdGhlIHBlcmNlbnQgZm9yIGVhY2ggc3Vic2V0CgpkYXRhLnBjdApgYGAKCk5leHQsIGNoZWNrIGlmIHRoZSBCYXNlbGluZSBjb25kaXRpb24gaW4gRXhwZXJpbWVudCAxIGlzIGRpZmZlcmVudCBmcm9tIGNoYW5jZSBsZXZlbHMgb2YgcGVyZm9ybWFuY2UuCgpgYGB7cn0KIyBwcm9wLnRlc3QoIHggPSBzdWNjZXNzVHJpYWxzICwKIyAgICAgICAgICAgIG4gPSB0b3RhbFRyaWFscyAsCiMgICAgICAgICAgICBwID0gcHJvYmFiaWxpdHlPZlN1Y2Nlc3MgKQoKZGF0YS5wY3QgJT4lICMgdGFraW5nIG91ciBzdW1tYXJ5IHRhYmxlCiAgIGZpbHRlcihleHBlcmltZW50ID09ICJmaXJzdCIsICMga2VlcCBvbmx5IEV4cGVyaW1lbnQgMQogICAgICAgICAgY29uZGl0aW9uID09ICJCYXNlbGluZSIsICMga2VlcCBvbmx5IEJhc2VsaW5lIGNvbmRpdGlvbgogICAgICAgICAgc2VsZWN0Q29kZSA9PSAxKSAlPiUgIyBrZWVwIG9ubHkgY29ycmVjdCBzZWxlY3Rpb25zCiAgIHB1bGwoY291bnQpIC0+IGJhc2VsaW5lLnN1Y2Nlc3MuZXhwMSAjIHB1bGwgb3V0IHRoZSB2YWx1ZShzKSBmcm9tIHRoZSBjb3VudCBjb2x1bW4KCmRhdGEucGN0ICU+JSAjIHRha2luZyBvdXIgc3VtbWFyeSB0YWJsZQogICBmaWx0ZXIoZXhwZXJpbWVudCA9PSAiZmlyc3QiLCAjIGtlZXAgb25seSBFeHBlcmltZW50IDEKICAgICAgICAgIGNvbmRpdGlvbiA9PSAiQmFzZWxpbmUiLCAjIGtlZXAgb25seSBCYXNlbGluZSBjb25kaXRpb24KICAgICAgICAgIHNlbGVjdENvZGUgPT0gMSkgJT4lICMga2VlcCBvbmx5IGNvcnJlY3Qgc2VsZWN0aW9ucwogICBwdWxsKHRvdGFsKSAtPiBiYXNlbGluZS50b3RhbC5leHAxICMgcHVsbCBvdXQgdGhlIHZhbHVlKHMpIGZyb20gdGhlIHRvdGFsIGNvbHVtbgoKCnByb3AudGVzdCh4ID0gYmFzZWxpbmUuc3VjY2Vzcy5leHAxLAogICAgICAgICAgbiA9IGJhc2VsaW5lLnRvdGFsLmV4cDEsCiAgICAgICAgICBwID0gMC41KSAjIGNoZWNrIGFnYWluc3QgImNoYW5jZSIgbGV2ZWxzIG9mIDUwJQpgYGAKCk5vLCBpdCBpcyBub3QgZGV0ZWN0YWJseSBkaWZmZXJlbnQgZnJvbSBjaGFuY2UgKM6yPTAuNTQxLCDPh8KyKDEpPTEuNTAsIHA9MC4yMikuCgpOb3csIGRvIHRoZSBzYW1lIHRlc3Qgd2l0aCB0aGUgVHJlYXRtZW50IGNvbmRpdGlvbiBpbiBFeHBlcmltZW50IDEuCgpgYGB7cn0KIyBwcm9wLnRlc3QoIHggPSBzdWNjZXNzVHJpYWxzICwKIyAgICAgICAgICAgIG4gPSB0b3RhbFRyaWFscyAsCiMgICAgICAgICAgICBwID0gcHJvYmFiaWxpdHlPZlN1Y2Nlc3MgKQoKZGF0YS5wY3QgJT4lICMgdGFraW5nIG91ciBzdW1tYXJ5IHRhYmxlCiAgIGZpbHRlcihleHBlcmltZW50ID09ICJmaXJzdCIsICMga2VlcCBvbmx5IEV4cGVyaW1lbnQgMQogICAgICAgICAgY29uZGl0aW9uID09ICJUcmVhdG1lbnQiLCAjIGtlZXAgb25seSBCYXNlbGluZSBjb25kaXRpb24KICAgICAgICAgIHNlbGVjdENvZGUgPT0gMSkgJT4lICMga2VlcCBvbmx5IGNvcnJlY3Qgc2VsZWN0aW9ucwogICBwdWxsKGNvdW50KSAtPiB0cmVhdG1lbnQuc3VjY2Vzcy5leHAxICMgcHVsbCBvdXQgdGhlIHZhbHVlKHMpIGZyb20gdGhlIGNvdW50IGNvbHVtbgoKZGF0YS5wY3QgJT4lICMgdGFraW5nIG91ciBzdW1tYXJ5IHRhYmxlCiAgIGZpbHRlcihleHBlcmltZW50ID09ICJmaXJzdCIsICMga2VlcCBvbmx5IEV4cGVyaW1lbnQgMQogICAgICAgICAgY29uZGl0aW9uID09ICJUcmVhdG1lbnQiLCAjIGtlZXAgb25seSBCYXNlbGluZSBjb25kaXRpb24KICAgICAgICAgIHNlbGVjdENvZGUgPT0gMSkgJT4lICMga2VlcCBvbmx5IGNvcnJlY3Qgc2VsZWN0aW9ucwogICBwdWxsKHRvdGFsKSAtPiB0cmVhdG1lbnQudG90YWwuZXhwMSAjIHB1bGwgb3V0IHRoZSB2YWx1ZShzKSBmcm9tIHRoZSB0b3RhbCBjb2x1bW4KCgpwcm9wLnRlc3QoeCA9IHRyZWF0bWVudC5zdWNjZXNzLmV4cDEsCiAgICAgICAgICBuID0gdHJlYXRtZW50LnRvdGFsLmV4cDEsCiAgICAgICAgICBwID0gMC41KSAjIGNoZWNrIGFnYWluc3QgImNoYW5jZSIgbGV2ZWxzIG9mIDUwJQpgYGAKClllcywgaXQgaXMgZGV0ZWN0YWJseSBkaWZmZXJlbnQgZnJvbSBjaGFuY2UgKM6yPTAuNzU0LCDPh8KyKDEpPTYxLjAsIHA8MC4wMDAxKS4KCiMjIEdMTSB0byBjb21wYXJlIGFjcm9zcyBjb25kaXRpb25zCgpCZWNhdXNlIHdlIGFyZSB1c2luZyBiaW5vbWlhbCBkYXRhLCB3ZSBtdXN0IHVzZSBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIFRoZSBsb2dpc3RpYyByZWdyZXNzIGlzIG5lYXJseSBpZGVudGljYWwgdG8gYSBsaW5lYXIgcmVncmVzc2lvbiwgYnV0IHJhdGhlciB0aGFuIGZpbmRpbmcgYSBsaW5lYXIgbGluZSBvZiBiZXN0IGZpdCwgaXQgZmluZHMgYSBsb2ctbGluZWFyIGxpbmUgb2YgYmVzdCBmaXQsIHdoaWNoIFtsb29rcyBsaWtlIGFuIFMtY3VydmVdKGh0dHBzOi8vdmVyYmluZ25vdW5zLmdpdGh1Yi5pby9ub3RlYm9va3MvcmVhZGluZy9ub3RlYm9va3MvMjAyMTA2MTUtbGluZWFybW9kZWxzLmh0bWwjMjNfQmlub21pYWxfZGF0YSkgd2hlbiB0aGUgcHJlZGljdG9yIGlzIGNvbnRpbnVvdXMuCgpMZXQncyBsb29rIGF0IEV4cGVyaW1lbnQgMSBvbmx5LgoKYGBge3J9Cm1vZGVsLmdsbSA8LSBnbG0oc2VsZWN0Q29kZSB+IDEgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25kaXRpb24sCiAgICAgICAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwKICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YSAlPiUgZmlsdGVyKGV4cGVyaW1lbnQgPT0gImZpcnN0IikpCnN1bW1hcnkobW9kZWwuZ2xtKQpgYGAKCkhlcmUgd2UgY2FuIHNlZSB0aGF0IG91ciBtYXhpbWFsIG1vZGVsIHByb3ZpZGVzIGFuIGVzdGltYXRlICjOsikgZm9yIHRoZSBUcmVhdG1lbnQgY29uZGl0aW9uIGFzIDAuOTUzOS4gVGhpcyBpcyBzaWduaWZpY2FudCBiZWNhdXNlIHRoZSBzdGFuZGFyZCBlcnJvciAoU0UsIG9yIFN0ZC4gRXJyb3IpIGlzIHNtYWxsZXIgdGhhbiB0aGUgZXN0aW1hdGUgKDAuMTk4MSA8IDAuOTUzOSksIHdoaWNoIGlzIHJlZmxlY3RlZCBpbiB0aGUgdGVzdCBzdGF0aXN0aWMsIGluIHRoaXMgY2FzZSBhIFotdmFsdWUsIG9mIDQuODE0LiBUaGlzIGlzIGxhcmdlciB0aGFuIHRoZSB0aHJlc2hvbGQgbmVlZGVkIGZvciBzaWduaWZpY2FuY2UsIHNvIHRoZSBwLXZhbHVlIGlzIGxlc3MgdGhhbiAwLjA1LiBJbiBmYWN0LCB0aGUgcC12YWx1ZSB3cml0dGVuIGluIHNjaWVudGlmaWMgbm90YXRpb24gaXMgaXMgMS40OGUtNiAoIm9uZSBwb2ludCBmb3VyIGVpZ2h0IHRpbWVzIHRlbiB0byB0aGUgbmVnYXRpdmUgc2l4dGggcG93ZXIiKSwgd2hpY2ggbWVhbnMgeW91IHdvdWxkIG5lZWQgdG8gbW92ZSB0aGUgZGVjaW1hbCBwb2ludCBzaXggcGxhY2VzIHRvIHRoZSBsZWZ0IChwID0gMC4wMDAwMDE0OCkuCgpJZiB5b3UgaGF2ZSBhIG1vcmUgY29tcGxleCBtb2RlbCwgZS5nLiwgYSBHTE1FUiB0aGF0IGFsc28gdGFrZXMgaW50byBhY2NvdW50IHRoZSBvdGhlciBleHBlcmltZW50cyBpbmNsdWRlZCBpbiB0aGlzIGRhdGFzZXQsIHlvdSBtYXkgd2lzaCB0byBkbyBzb21lIG1vZGVsIGNvbXBhcmlzb24uCgpgYGB7cn0KbW9kZWwubWF4IDwtIGdsbWVyKHNlbGVjdENvZGUgfiAxICsgIyBvdXRjb21lIG1lYXN1cmUgYXMgYSBmdW5jdGlvbiBvZiB0aGUgbnVsbCBoeXBvdGhlc2lzCiAgICAgICAgICAgICAgICAgICAgICBjb25kaXRpb24gKyAjIGFuZCBjb25kaXRpb24KICAgICAgICAgICAgICAgICAgICAgIGV4cGVyaW1lbnQgKyAjIGFuZCBleHBlcmltZW50CiAgICAgICAgICAgICAgICAgICAgICBjb25kaXRpb246ZXhwZXJpbWVudCArICMgYW5kIHRoZSBpbnRlcmFjdGlvbiBvZiBjb25kaXRpb24gYW5kIGV4cGVyaW1lbnQKICAgICAgICAgICAgICAgICAgICAgICgxIHwgc3ViamVjdCkgKyAjIHdpdGggcmFuZG9tIGludGVyY2VwdHMgZm9yIHN1YmplY3QKICAgICAgICAgICAgICAgICAgICAgICgxIHwgaXRlbSksICMgYW5kIGl0ZW0KICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsICMgdXNpbmcgYSBiaW5vbWlhbCBsaW5raW5nIGZ1bmN0aW9uCiAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YSkgIyBhbmQgb3VyIGRhdGFzZXQKCiMgKiB0aGUgbnVsbCBoeXBvdGhlc2lzIGlzIGJ5IGRlZmF1bHQgaW5jbHVkZWQgaW4gYWxsIG1vZGVscyBhbmQgCiMgICBjYW4gYmUgZXhwbGljaXRseSB3cml0dGVuIG91dCBhcyAxLCB3aGljaCBJIGhhdmUgZG9uZSB0byBhbGxvdwojICAgYWxsIHRoZSBvdGhlciB0ZXJtcyB0byBiZSBvbiB0aGVpciBvd24gbGluZXMKc3VtbWFyeShtb2RlbC5tYXgpCmBgYAoKV2hpbGUgd2UgY2FuIHNlZSB0aGF0IHRoZSBUcmVhdG1lbnQgY29uZGl0aW9uIGlzIHN0aWxsIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IHRvIHRoZSBCYXNlbGluZSBjb25kaXRpb24gKM6yPTAuOTYsIFNFPTAuMjAsIFo9NC42OCwgcDwwLjAwMDEpLCB3ZSBjYW4gYWxzbyBzZWUgdGhhdCB0aGUgRXhwZXJpbWVudHMgYXJlIG9ubHkgbGlzdGVkIGFzIHBhaXJ3aXNlIGNvbXBhcmlzb25zLCB0aHVzIHNvIGFyZSB0aGUgaW50ZXJhY3Rpb25zLiBJbiBvcmRlciB0byByZXBvcnQgdGhlbSBhcyBhIG1haW4gZWZmZWN0IGFuZCBpbnRlcmFjdGlvbiBlZmZlY3QgcmF0aGVyIHRoYW4gcGFpcndpc2UgY29tcGFyaXNvbnMsIHdlIHNob3VsZCBkbyBtb2RlbCBjb21wYXJpc29uLgoKYGBge3J9CiMgcmVtb3ZlIHRoZSBpbnRlcmFjdGlvbiB0ZXJtIHRvIGNvbXBhcmUgdGhpcyBuZXN0ZWQgZGVwbGV0ZWQgbW9kZWwgdG8gdGhlIG1heGltYWwgb25lCm1vZGVsLmludCA8LSBnbG1lcihzZWxlY3RDb2RlIH4gMSArIAogICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uICsgCiAgICAgICAgICAgICAgICAgICAgICBleHBlcmltZW50ICsgCiAgICAgICAgICAgICAgICAgICAgICAjIGNvbmRpdGlvbjpleHBlcmltZW50ICsgCiAgICAgICAgICAgICAgICAgICAgICAoMSB8IHN1YmplY3QpICsgCiAgICAgICAgICAgICAgICAgICAgICAoMSB8IGl0ZW0pLCAKICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsIAogICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGEpIAojIHRoaXMgbW9kZWwgaXMgaWRlbnRpY2FsIHRvIG91ciBtYXhpbWFsIG1vZGVsLCBleGNlcHQgd2UgaGF2ZSBjb21tZW50ZWQgb3V0CiMgdGhlIHRlcm0gb2YgaW50ZXJlc3QKCmFub3ZhKG1vZGVsLm1heCwgbW9kZWwuaW50KQpgYGAKCkhlcmUsIHRoZSBtb2RlbCBjb21wYXJpc29uIHNob3dzIHVzIHRoYXQgdGhlIG92ZXJhbGwgY29udHJpYnV0aW9uIG9mIHRoZSBpbnRlcmFjdGlvbiB0ZXJtIGlzIHNpZ25pZmljYW50IHRvIHRoZSBmaXQgb2Ygb3VyIG1vZGVsICjPh8KyKDEpPTEwMy4wNiwgcDwwLjAwMDEpLiBUaGlzIG1lYW5zIHdlIGNhbiBzYXkgdGhlcmUgaXMgYSBzaWduaWZpY2FudCBpbnRlcmFjdGlvbiBiZXR3ZWVuIGNvbmRpdGlvbiBhbmQgZXhwZXJpbWVudC4KCldlIHNob3VsZCBhbHNvIGNoZWNrIGlmIHRoZXJlIGlzIGEgbWFpbiBlZmZlY3Qgb2YgZXhwZXJpbWVudDoKCmBgYHtyfQojIHJlbW92ZSB0aGUgYWxsIHRlcm1zIGNvbnRhaW5pbmcgdGhlIHRlcm0gb2YgaW50ZXJlc3QgdG8gY29tcGFyZSB0aGlzIG5lc3RlZCBkZXBsZXRlZCBtb2RlbAptb2RlbC5leHAgPC0gZ2xtZXIoc2VsZWN0Q29kZSB+IDEgKyAKICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbiArIAogICAgICAgICAgICAgICAgICAgICAgIyBleHBlcmltZW50ICsgCiAgICAgICAgICAgICAgICAgICAgICAjIGNvbmRpdGlvbjpleHBlcmltZW50ICsgCiAgICAgICAgICAgICAgICAgICAgICAoMSB8IHN1YmplY3QpICsgCiAgICAgICAgICAgICAgICAgICAgICAoMSB8IGl0ZW0pLCAKICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsIAogICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGEpIAojIHRoaXMgbW9kZWwgaXMgaWRlbnRpY2FsIHRvIG91ciBtYXhpbWFsIG1vZGVsLCBleGNlcHQgd2UgaGF2ZSBjb21tZW50ZWQgb3V0CiMgdGhlIHRlcm0gb2YgaW50ZXJlc3QgYW5kIGFsbCBvdGhlciB0ZXJtcyBjb250YWluaW5nIGl0CgojIHNpbmNlIHdlIGNvbW1lbnRlZCBvdXQgdHdvIHRlcm1zLCB3ZSBzaG91bGQgY29tcGFyZSB0aGlzIG1vZGVsIHRvIHRoZSBuZXh0IGdyZWF0ZXIsIG5vdCB0aGUgbWF4aW1hbCBvbmUKYW5vdmEobW9kZWwuaW50LCBtb2RlbC5leHApCmBgYAoKRnJvbSB0aGlzLCB3ZSBjYW4gc2VlIHRoZXJlIGlzIGEgbWFpbiBlZmZlY3Qgb2YgZXhwZXJpbWVudCwgcmVnYXJkbGVzcyBvZiBwYWlyd2lzZSBjb21wYXJpc29ucyAoz4fCsigxKT0xOTAuNjYsIHA8MC4wMDAxKS4KCk5vdywgdG8gdW5kZXJzdGFuZCB3aGF0IHRoaXMgbWVhbnMsIHdlIG11c3QgcGxvdCBvdXIgZGF0YS4gKEluIGZhY3QsIHdlIHNob3VsZCBoYXZlIGRvbmUgdGhpcyBmaXJzdC4uLikKCiMgUGxvdHRpbmcgcmVzdWx0cyB3aXRoIGVycm9yIGJhcnMKCiMjIFRpZHl2ZXJzZQoKVGhpcyBpcyB0aWR5dmVyc2Ugc3R5bGUgY29kZSBmb3IgcHJvZHVjaW5nIGEgdmlzdWFsaXphdGlvbiBvZiB0aGUgZGF0YSBhbmFseXplZCBhYm92ZS4gRmlyc3QsIHdlIHdpbGwgY3JlYXRlIGEgbmV3IGRhdGFzZXQgdGhhdCB3aWxsIGFsbG93IHVzIHRvIHBsb3QgY2FsY3VsYXRlZCBlc3RpbWF0ZXMgYW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWxzLiBJdCBpcyBub3QgbmVjZXNzYXJ5IHRvIHNhdmUgdGhpcyBhcyBhIHNlcGFyYXRlIGRhdGFzZXQgLS0gaXQgY291bGQgYmUgcGlwZWQgZGlyZWN0bHkgaW50byB0aGUgcGxvdC4gSG93ZXZlciwgdGhpcyB3YXksIHlvdSBnZXQgYSBjaGFuY2UgdG8gc2VlIHRoZSBjb250ZW50IGFuZCBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGFzZXQgZmlyc3QuCgpgYGB7cn0KZGF0YS5wbG90IDwtIGRhdGEucGN0ICU+JSAjIHN0YXJ0IHdpdGggb3VyIFNVTU1BUlkgZGF0YSwgbm90IHJhdyBkYXRhIGFuZCBzYXZlIGEgbmV3IGNvcHkKICAgIyBjcmVhdGUgYSBjb2x1bW4gdGhhdCBsYWJlbHMgdGhlIHNlbGVjdCBjb2RlcwogICBtdXRhdGUob2JzZXJ2ZWQgPSBjYXNlX3doZW4oc2VsZWN0Q29kZSA9PSAxIH4gImNvcnJlY3QiLCAjIDEgPSBjb3JyZWN0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3RDb2RlID09IDAgfiAiaW5jb3JyZWN0IiksICMgMCA9IGluY29ycmVjdAogICAgICAgICAgb2JzZXJ2ZWQgPSBmYWN0b3Iob2JzZXJ2ZWQsICMgbWFrZSBpdCBhIGZhY3RvcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiaW5jb3JyZWN0IiwgImNvcnJlY3QiKSkpICU+JSAjIG9yZGVyIHRoZSBsZXZlbHMKICAgcm93d2lzZSgpICU+JSAjIHRoZSBuZXh0IG9wZXJhdGUgd2lsbCBiZSBjb25kdWN0ZWQgZm9yIGVhY2ggcm93CiAgICMgdXNpbmcgcm93d2lzZSgpIGlzIGVzc2VudGlhbCBmb3IgYXBwbHlpbmcgY2VydGFpbiBiYXNlIFIgZnVuY3Rpb25zIHN1Y2ggYXMgcHJvcC50ZXN0CiAgICMgY3JlYXRlIGNvbHVtbnMgZm9yIGVzdGltYXRlLCB1cHBlciBDSSB2YWx1ZSwgYW5kIGxvd2VyIENJIHZhbHVlOgogICBtdXRhdGUoZXN0aW1hdGUgPSBwcm9wLnRlc3QoeCA9IGNvdW50LCBuID0gdG90YWwsIHAgPSAwLjUpJGVzdGltYXRlLCAjIGV4dHJhY3QgZXN0aW1hdGUKICAgICAgICAgIGNvbmZpbnQubG93ZXIgPSBwcm9wLnRlc3QoeCA9IGNvdW50LCBuID0gdG90YWwsIHAgPSAwLjUpJGNvbmYuaW50WzFdLCAjIGV4dHJhY3QgbG93ZXIgQ0kKICAgICAgICAgIGNvbmZpbnQudXBwZXIgPSBwcm9wLnRlc3QoeCA9IGNvdW50LCBuID0gdG90YWwsIHAgPSAwLjUpJGNvbmYuaW50WzJdKSAjIGV4dHJhY3QgdXBwZXIgQ0kKCmRhdGEucGxvdApgYGAKCk5vdywgbGV0J3MgcGxvdCBpdDoKCmBgYHtyfQpkYXRhLnBsb3QgJT4lIAogICBnZ3Bsb3QoYWVzKHggPSBjb25kaXRpb24sICMgeC1heGlzIGlzIGJ5IGNvbmRpdGlvbgogICAgICAgICAgICAgIHkgPSBlc3RpbWF0ZSwgIyB5LWF4aXMgaXMgYnkgY2FsY3VsYXRlZCBlc3RpbWF0ZQogICAgICAgICAgICAgIGZpbGwgPSBvYnNlcnZlZCkpICsgIyB0aGlzIHdpbGwgYWxsb3cgdXMgdG8gaGlkZSBleHRyYSBlcnJvciBiYXJzIGxhdGVyIG9uKgogICB0aGVtZV9idygpICsgIyBtYWtlIHRoZSBwbG90IGNsZWFuIGFuZCBwcmV0dHkKICAgZ2VvbV9jb2woKSArICMgcGxvdCB0aGUgYmFycyBhcyBzdGFja2VkIGNvbHVtbnMKICAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD0uNSksICMgcGxvdCBhIGxpbmUgYXQgMC41ICg1MCUpCiAgICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwgIyBtYWtlIGl0IGJsYWNrCiAgICAgICAgICAgICAgYWxwaGE9LjUsICMgbWFrZSBpdCBoYWxmLXRyYW5zcGFyZW50CiAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIikgKyAjIG1ha2UgaXQgYSBkYXNoZWQgbGluZQogICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gY29uZmludC5sb3dlciwgIyBkZWZpbmUgbG93ZXIgQ0kgbGltaXQKICAgICAgICAgICAgICAgICAgICAgeW1heCA9IGNvbmZpbnQudXBwZXIsICMgZGVmaW5lIHVwcGVyIENJIGxpbWl0CiAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gb2JzZXJ2ZWQpLCAjIGRlZmluZSB0cmFuc3BhcmVuY3kgZm9yIGVycm9yIGJhcnMqCiAgICAgICAgICAgICAgICAgd2lkdGggPSAuMSkgKyAjIGtlZXAgdGhlIGNyb3NzYmFycyBuYXJyb3cKICAgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcyA9IGMoMCwxKSwgZ3VpZGUgPSAibm9uZSIpICsgIyAqaGlkZSBleHRyYSBlcnJvciBiYXJzIGZvciAnaW5jb3JyZWN0JyB2YWx1ZXMKICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKyAjIHktYXhpcyBjb252ZXJ0ZWQgZnJvbSBkZWNpbWFsIHRvIHBlcmNlbnQKICAgeWxhYigicGVyY2VudCBzZWxlY3RlZCIpICsgIyByZWxhYmVsIHRoZSB5LWF4aXMKICAgZmFjZXRfZ3JpZCh+ZXhwZXJpbWVudCkgKyAjIHB1dCBlYWNoIGV4cGVyaW1lbnQgaW4gaXRzIG93biBmYWNldAogICBOVUxMICMgdGhpcyBsaW5lIGFsbG93cyB5b3UgY29tbWVudCBvdXQgdGhlIHByZXZpb3VzIG9uZShzKSB3aXRob3V0IGJyZWFraW5nIHRoZSBwbG90CmBgYAoKRnJvbSB0aGlzIHBsb3QsIHlvdSBjYW4gc2VlIHRoYXQgdGhlIEJhc2VsaW5lIGNvbmRpdGlvbnMgb3ZlcmxhcCB3aXRoIGNoYW5jZSBsZXZlbHMgaW4gRXhwZXJpbWVudHMgMSBhbmQgMiBidXQgbm90IDMsIHdoZXJlIGl0IGlzIGZhciBiZWxvdyBjaGFuY2UuIEZ1cnRoZXJtb3JlLCB5b3UgY2FuIHNlZSB0aGF0IEV4cGVyaW1lbnRzIDEgYW5kIDIgaGF2ZSBoaWdoZXItdGhhbi1jaGFuY2UgVHJlYXRtZW50IGNvbmRpdGlvbnMsIGJ1dCBub3QgaW4gRXhwZXJpbWVudCAzLiBUaGlzIGlzIGxpa2VseSB3aGF0IGlzIGRyaXZpbmcgdGhlIGludGVyYWN0aW9uIGVmZmVjdCB3ZSBvYnNlcnZlZC4gTm93IHRoZSBzdGF0aXN0aWNhbCB0ZXN0cyBjYW4gYmUgZGlzY3Vzc2VkIHdpdGggbW9yZSBjbGFyaXR5IGFuZCBjb25jbHVzaW9ucyBhYm91dCBob3cgb3V0Y29tZXMgaW4gdGhlIHR3byBjb25kaXRpb25zIGFuZCB0aHJlZSBleHBlcmltZW50cyBjb21wYXJlIChhbmQgd2hhdCB0aGlzIG1lYW5zKSBjYW4gYmUgZHJhd24uCgojIyBCYXNlIFIgZ3JhcGhpY3MKClRoaXMgY29kZSBidWlsZHMgZGlyZWN0bHkgb2ZmIG9mIHRoZSBwcmV2aW91cyBjb2RlIHRvIHByb2R1Y2UgdGhlIGdyYXBoIHNob3duIGF0IHRoZSBib3R0b20gb2YgdGhpcyBzZWN0aW9uLiBJdCB1c2VzIG9ubHkgdGhlIGJhc2UgZ3JhcGhpY3MgbGlicmFyeS4gSXQgbG9va3MgYXQgRXhwZXJpbWVudCAxIG9ubHkuCgojIyMgU3RlcCAxCgpgYGB7cn0KIyBBdXRob3I6IExhdXJlbiBNIEFja2VybWFuCiMgQXZhaWxhYmxlOiBodHRwczovL2xtYWNrZXJtYW4uY29tL3Itc25pcHBldHMvCiMgRGF0ZTogSmFudWFyeSAzMCwgMjAxOAoKIyBzdG9yZSB0aGUgcHJvcC50ZXN0KCkgcmVzdWx0cyBmb3IgdXNlIGluIHRoZSBncmFwaCAKZXJyLmNvbmQuMSA8LSBwcm9wLnRlc3QoYmFzZWxpbmUuc3VjY2Vzcy5leHAxLCBiYXNlbGluZS50b3RhbC5leHAxLCBwID0gMC41KQplcnIuY29uZC4yIDwtIHByb3AudGVzdCh0cmVhdG1lbnQuc3VjY2Vzcy5leHAxLCB0cmVhdG1lbnQudG90YWwuZXhwMSwgcCA9IDAuNSkKCiMgZm9ybWF0IHRoZSB0YWJsZSBhcyBwZXJjZW50YWdlcyByb3VuZGVkIHRvIDIgZGVjaW1hbCBwbGFjZXMKbnVtT2ZDb25kcyA8LSBsZW5ndGgobGV2ZWxzKGFzLmZhY3RvcihkYXRhJGNvbmRpdGlvbltkYXRhJGV4cGVyaW1lbnQ9PSJmaXJzdCJdKSkpCm51bWVyYXRvciA8LSAodGFibGUoZGF0YSRzZWxlY3Rpb25bZGF0YSRleHBlcmltZW50PT0iZmlyc3QiXSwgCiAgICAgICAgICAgICAgICAgICAgZGF0YSRjb25kaXRpb25bZGF0YSRleHBlcmltZW50PT0iZmlyc3QiXSkpCmRlbm9taW5hdG9yIDwtIChsZW5ndGgoZGF0YSRzZWxlY3Rpb25bZGF0YSRleHBlcmltZW50PT0iZmlyc3QiXSkvbnVtT2ZDb25kcykKZGZUYWJsZSA8LSAobnVtZXJhdG9yL2Rlbm9taW5hdG9yKSoxMDAKCmNvdW50cyA8LSByb3VuZChkZlRhYmxlLDIpCmJhcnBsb3QoY291bnRzLAojICBsZWdlbmQgPSByb3duYW1lcyhjb3VudHMpLAogICBtYWluPXBhc3RlMCgiQ2hvaWNlIG9mICIscm93bmFtZXMoY291bnRzKVsxXSwiIGFuZCAiLHJvd25hbWVzKGNvdW50cylbMl0sIiBpbiBhIGZvcmNlZC1jaG9pY2UgdGFzayIpLAogICB5bGFiPSJQZXJjZW50ICglKSIsICN4bGFiPSJjb25kaXRpb24iLCAKICAgY2V4Lm5hbWVzPTEsIG5hbWVzPWMoY29sbmFtZXMoY291bnRzKVsxXSwgY29sbmFtZXMoY291bnRzKVsyXSksCiAgIGNvbD1jKCJsaWdodHNlYWdyZWVuIiwic2FsbW9uIiksIGJvcmRlcj1jKCJibGFjayIsImJsYWNrIikKICAgKQpgYGAKCiMjIyBTdGVwIDIKCmBgYHtyfQpiYXJwbG90KGNvdW50cywKICAgbWFpbj1wYXN0ZTAoIkNob2ljZSBvZiAiLHJvd25hbWVzKGNvdW50cylbMV0sIiBhbmQgIixyb3duYW1lcyhjb3VudHMpWzJdLCIgaW4gYSBmb3JjZWQtY2hvaWNlIHRhc2siKSwKICAgeWxhYj0iUGVyY2VudCAoJSkiLCAgCiAgIGNleC5uYW1lcz0xLCBuYW1lcz1jKGNvbG5hbWVzKGNvdW50cylbMV0sIGNvbG5hbWVzKGNvdW50cylbMl0pLAogICBjb2w9YygibGlnaHRzZWFncmVlbiIsInNhbG1vbiIpLCBib3JkZXI9YygiYmxhY2siLCJibGFjayIpCiAgICkKIyBsYWJlbCB0aGUgc3RhY2tlZCBzZWN0aW9ucyBvZiB0aGUgYmFyIHBsb3Qgd2l0aCB0aGUgZXN0aW1hdGVkIHBlcmNlbnRzCm10ZXh0KHBhc3RlMChyb3duYW1lcyhjb3VudHMpWzFdLCIgPSAiLCBjb3VudHNbMV0sIiUiKSwgc2lkZT0xLGxpbmU9LTEuNSxhdD0wLjcsY29sPSJibGFjayIsY2V4PTEpCm10ZXh0KHBhc3RlMChyb3duYW1lcyhjb3VudHMpWzFdLCIgPSAiLCBjb3VudHNbM10sIiUiKSwgc2lkZT0xLGxpbmU9LTEuNSxhdD0xLjksY29sPSJibGFjayIsY2V4PTEpCm10ZXh0KHBhc3RlMChyb3duYW1lcyhjb3VudHMpWzJdLCIgPSAiLCBjb3VudHNbMl0sIiUiKSwgc2lkZT0zLGxpbmU9LTEuNSxhdD0wLjcsY29sPSJibGFjayIsY2V4PTEpCm10ZXh0KHBhc3RlMChyb3duYW1lcyhjb3VudHMpWzJdLCIgPSAiLCBjb3VudHNbNF0sIiUiKSwgc2lkZT0zLGxpbmU9LTEuNSxhdD0xLjksY29sPSJibGFjayIsY2V4PTEpCmBgYAoKIyMjIFN0ZXAgMwoKYGBge3J9CmJhcnBsb3QoY291bnRzLAojICBsZWdlbmQgPSByb3duYW1lcyhjb3VudHMpLAogICBtYWluPXBhc3RlMCgiQ2hvaWNlIG9mICIscm93bmFtZXMoY291bnRzKVsxXSwiIGFuZCAiLHJvd25hbWVzKGNvdW50cylbMl0sIiBpbiBhIGZvcmNlZC1jaG9pY2UgdGFzayIpLAogICB5bGFiPSJQZXJjZW50ICglKSIsICN4bGFiPSJjb25kaXRpb24iLCAKICAgY2V4Lm5hbWVzPTEsIG5hbWVzPWMoY29sbmFtZXMoY291bnRzKVsxXSwgY29sbmFtZXMoY291bnRzKVsyXSksCiAgIGNvbD1jKCJsaWdodHNlYWdyZWVuIiwic2FsbW9uIiksIGJvcmRlcj1jKCJibGFjayIsImJsYWNrIikKICAgKQojIGxhYmVsIHRoZSBzdGFja2VkIHNlY3Rpb25zIG9mIHRoZSBiYXIgcGxvdCB3aXRoIHRoZSBlc3RpbWF0ZWQgcGVyY2VudHMKbXRleHQocGFzdGUwKHJvd25hbWVzKGNvdW50cylbMV0sIiA9ICIsIGNvdW50c1sxXSwiJSIpLCBzaWRlPTEsbGluZT0tMS41LGF0PTAuNyxjb2w9ImJsYWNrIixjZXg9MSkKbXRleHQocGFzdGUwKHJvd25hbWVzKGNvdW50cylbMV0sIiA9ICIsIGNvdW50c1szXSwiJSIpLCBzaWRlPTEsbGluZT0tMS41LGF0PTEuOSxjb2w9ImJsYWNrIixjZXg9MSkKbXRleHQocGFzdGUwKHJvd25hbWVzKGNvdW50cylbMl0sIiA9ICIsIGNvdW50c1syXSwiJSIpLCBzaWRlPTMsbGluZT0tMS41LGF0PTAuNyxjb2w9ImJsYWNrIixjZXg9MSkKbXRleHQocGFzdGUwKHJvd25hbWVzKGNvdW50cylbMl0sIiA9ICIsIGNvdW50c1s0XSwiJSIpLCBzaWRlPTMsbGluZT0tMS41LGF0PTEuOSxjb2w9ImJsYWNrIixjZXg9MSkKCiMgYWRkIHRoZSBjb25maWRlbmNlIGludGVydmFscyBiYXNlZCBvbiB0aGUgcHJvcC50ZXN0KCkgcmVzdWx0cwphcnJvd3MoMC43LGNvdW50c1sxXSwwLjcsKHVubGlzdChlcnIuY29uZC4xWzZdKVsxXSkqMTAwLCBhbmdsZT05MCxjb2w9ImJsYWNrIixsd2Q9MSkKYXJyb3dzKDAuNyxjb3VudHNbMV0sMC43LCh1bmxpc3QoZXJyLmNvbmQuMVs2XSlbMl0pKjEwMCwgYW5nbGU9OTAsY29sPSJibGFjayIsbHdkPTEpCmFycm93cygxLjksY291bnRzWzNdLDEuOSwodW5saXN0KGVyci5jb25kLjJbNl0pWzFdKSoxMDAsIGFuZ2xlPTkwLGNvbD0iYmxhY2siLGx3ZD0xKQphcnJvd3MoMS45LGNvdW50c1szXSwxLjksKHVubGlzdChlcnIuY29uZC4yWzZdKVsyXSkqMTAwLCBhbmdsZT05MCxjb2w9ImJsYWNrIixsd2Q9MSkKCmBgYAoKKipOT1RFKio6IFNvbWV0aW1lcywgYDEtdW5saXN0KClgIHNob3VsZCBiZSB1c2VkIGJlY2F1c2UgdGhlIGNvdW50IG9mICIxIiBhbnN3ZXJzIGlzIG9uIHRoZSB0b3Agb2YgdGhpcyBncmFwaCwgYWxwaGFiZXRpY2FsbHkuIElmIHlvdXIgZXJyb3IgYmFycyBhcmUgbWlzYWxpZ25lZCwgeW91IG1heSBuZWVkIHRvIGFkZCAgYDEtYCB0byB0aGVzZSBmb3VyIGxpbmVzIG9mIGNvZGUuCgpUZXh0IHNpemUgKGBjZXhgKSBhbmQgbGluZSB3aWR0aCBvZiB0aGUgZXJyb3IgYmFycyAoYGx3ZGApIGFyZSBib3RoIHNldCB0byBzaXplIDEgaW4gdGhlIGFib3ZlIGNvZGUsIGJ1dCBzaG91bGQgYmUgaW5jcmVhc2VkIGZvciB1c2Ugb24gcG9zdGVycy4gVGhlIGNvbG9ycyBzZWxlY3RlZCBoZXJlIGFyZSBhIGNsb3NlIG1hdGNoIHRvIHRoZSBkZWZhdWx0IGFlc3RoZXRpY3Mgb2YgZ2dwbG90MiwgYnV0IGFyZSBlYXNpbHkgY2hhbmdlZC4=