Intro

This is an initial analysis of the data set which contains within-participant data on (i) accuracy and rate of reading comprehension and (ii) accuracy of listening comprehension of oral texts recorded at different speeds.

Methods

Participants

Participants were recruited for the study via the crowdsourcing platform Prolific (prolific.co). The following selection criteria were defined using the Prolific settings: participants were between ages 18 and 30; born and residing in Canada or the USA; with no reported language, hearing or vision difficulties; and English monolinguals. As described in detail in the Procedure below, participants were initially invited to complete the reading task. They were compensated by 3.75 GBP for their time. Those who completed this task were further invited to the listening comprehension task: the compensation was an additional 3.75 GBP.

A total of 216 participants completed the reading task and 165 of these participants also completed the listening comprehension task. After excluding individuals with incomplete responses as well as individuals who indicated in the demographic quesitonnaire that they learned English after the age of 5 or had a reading impairment, the resulting pool of participants contained 152 participants. The mean age of the participants was 25.80 (SD = 3.25), with 70 participants indicating female, 79 male, and 2 other gender, and 1 decline to response.

Materials

All stimuli came from the Lecture, Interviews and Spoken Narratives (LISN) test, a listening comprehension test, developed by Sommer, Tye-Murray, and colleagues (Sommers et al., 2007; 2011; Tye-Murray 2008). The LISN test is composed of 3 different types of recorded audio passages (lecture, interviews and narratives). Our prior study showed that narratives are a more reliable and easier passage type: the present study only used narratives.

The narratives were selected from the Rutgers University Oral History Archives of personal descriptions of life experiences (Sommer et al., 2011). The passages were between 3-5 minutes long (542–661 words per passage, median = 616 words) and were recorded by male and female professional actors with North American accents. This study used 12 out of 16 narrative passages. Six passages were presented to a given participant in the written format for reading comprehension and another six orally, at different speeds (see below) for listening comprehension.

Each passage was followed by 6 comprehension questions. The questions are in the multiple choice format with 4 options per question. There are three types of comprehension questions: information questions, integration questions and inference questions. Information questions ask participants to recall a specific piece of information from the text. Integration questions assess if the participants are able to combine multiple bits of information learned from the text. Finally, inference questions ask participants to derive implications from the information in the passages (Sommer et al., 2011). Two of each type of questions were asked for a total of 6 questions per passage. By the end of each set of 6 passages for reading and listening, participants would have answered 36 comprehension questions, or 72 questions in total. The individual scores were calculated separately for reading and listening as a percentage of questions they answered correctly out of 36.

Audio recordings of texts were manipulated using computer software package PRAAT [REF] to represent a range of speech rates. Speech rate was first calculated for the original recordings of the 12 narrative passages from the LISN test as the total number of words in the passage divided by the duration of its recording, in words per minute (wpm). The average speech rate was 180 wpm, comparable to a typical speech rate of an audio book [REF]. We adjusted the duration of each recording through compression or lenghtening to ensure the 180 wpm speech rate in the baseline condition. Further, each recording was altered for duration to obtain speech rates equal to 125% (225 wpm), 150% (270 wpm), 175% (315 wpm), 200% (360 wpm), and 225% (405 wpm) of the baseline speech rate. The range of speech rates was chosen to represent both careful speech and comfortable for listening comprehension and very rapid speech, which hinders comprehension.

Procedure

The present experimental study was implemented online as two web-based experiments – for the reading and listening comprehension tasks, respectively – with a shared SQL database storing responses. Participants were recruited via the crowdsourcing platform Prolific (prolific.co) and were initially invited to complete the reading task on the crowdsourcing platform Prolific. This task took an average of roughly 20 minutes. Upon completion, same participants were further invited to complete a listening comprehension task. The listening comprehension task took an average of about 30 minutes. A break of at least one hour was observed between the tasks to minimize fatigue and potential spillover effects.

If a participant chose to complete both tasks, they were exposed to a total of 12 passages. The assignment of passages to the reading versus listening task (six each) and the assignment of passages in the listening task to six different speech rates was randomized for each participant. The reading task always came first, followed by the listening task. The order of speech rate conditions within the listening task was randomized.

Need to fill in details about consent forms, questionnaires, audio check, etc

Reading Each passage presented for reading was separated into 5-6 chunks equivalent to one or two paragraphs of the original text. Chunks appeared on the screen one at a time and participants pressed space to move to the next chunk. When the passage was read, six comprehension questions with 4 multiple choice options appeared on the screen one at a time. Participants responded by pressing a, b, c, or d. Reading times per chunk and response keys and latencies were recorded. After the last question to a passage was completed, the next passage appeared.

Listening In the beginning of this task, participants were asked to use their headphones. An audio-verification task was administered before stimulus presentation. An audio file with a test phrase was played and participants were requested to type that phrase on the screen. This step provided a chance to adjust audio settings of the participant’s computers or headphones. As the next step, audio recordings of passages were played one at a time. Participants did not have the option to pause or stop the recording. As in the reading task, after each recording, six comprehension questions appeared on the screen in the written format. Participants responded by pressing keys a, b, c, or d. Response keys and latencies were recorded.

Results

This is where the coherent narrative ends and a spotty reporting begins.

Load up libraries and primary data.

library(tidyverse)
library(ggplot2)
library(effects)
library(lme4)
load("data.rda")

Accuracy

Accuracy from both the reading task and listening task, reported jointly.

responses <- left_join(responses, questions) %>% filter(user_id %in% participants$user_id, user_id >= 9)
Joining, by = c("passage", "question_num", "correct_response")
good <- names(which(table(responses$user_id) == 72)) #only use participants with both reading an listening
responses <- responses %>% filter(user_id %in% good)
length(unique(responses$user_id))
[1] 153
responses$treatment = droplevels(responses$treatment)
responses$treatment = relevel(responses$treatment, ref = "reading")
responses$score <- ifelse(responses$response == tolower(responses$correct_response), 1, 0)
table(responses$score)/nrow(responses)

        0         1 
0.3868918 0.5991285 
responses$rt <- (responses$response_end - responses$response_start)/1000
#these are accuracy scores per subject, treatent and passage (reading/list together)
scores_passage <-  responses %>% group_by(user_id, treatment, passage) %>% 
  filter(is.na(score)==F) %>% summarise(sum_score = sum(score, na.rm = T), 
                                        n = NROW(score)) %>% mutate(mean_score = sum_score/n)
scores_read <-  responses %>% filter(treatment == "reading") %>% group_by(user_id, treatment) %>% 
  filter(is.na(score)==F) %>% summarise(sum_score = sum(score, na.rm = T), 
                                        n = NROW(score)) %>% mutate(mean_score = sum_score/n)
#these are accuracy scores per subject and treatment
scores_all <-  responses %>% group_by(user_id, treatment) %>% 
  filter(is.na(score)==F) %>% summarise(sum_score = sum(score, na.rm = T), 
                                        n = NROW(score)) %>% mutate(mean_score = sum_score/n)
#these are RTs to comprehension questions per subject and treatment *NOT USED SO FAR*
rt_all <- responses %>% group_by(user_id, treatment) %>% filter(is.na(score)==F, is.na(rt) ==F) %>%
  summarise(median_rt = median(rt), n_rt = NROW(rt)) 
#removing participants who close to  chance (30% vs 25 chance)
#prop.test(0.37 * 36, 36, 0.25, alternative = "greater")
bad <- scores_read[which(scores_read$mean_score <= 0.3),]$user_id
scores_all <- scores_all %>% filter(!user_id %in% bad)
rt_all <- rt_all %>% filter(!user_id %in% bad)
#participants descriptives (reported in Participants)
user_good <- rt_all$user_id
participants %>% filter(user_id %in% user_good) %>% summarise(mean(as.numeric(as.character(age))), sd(as.numeric(as.character(age))))
  mean(as.numeric(as.character(age)))
1                            25.79605
  sd(as.numeric(as.character(age)))
1                          3.249735
participants %>% filter(user_id %in% user_good) -> x
table(x$gender)

abstain  female    male    NULL   other 
      1      70      79       0       2 
responses$treatment = paste0("sp", as.character(responses$treatment))
responses[responses$treatment == "spreading",]$treatment = "reading"
responses$treatment = as.factor(responses$treatment)

Now to the models and plotting of accuracy. The LMER is set up for backward differences between levels of treatment (reading, speech 180, speech 225 etc.). Treatment1 is a difference in accuracy between sp180 minus reading, Treatment2 is sp225 minus sp180 etc.

my.backward.diff = matrix(c(-1/7, 6/7, 6/7, 6/7, 6/7, 6/7, 6/7,
                            -2/7, -2/7, 5/7, 5/7, 5/7, 5/7, 5/7,
                            -3/7, -3/7, -3/7, 4/7, 4/7, 4/7, 4/7,
                            -4/7, -4/7, -4/7, -4/7, 3/7, 3/7, 3/7,
                            -5/7, -5/7, -5/7, -5/7, -5/7, 2/7, 2/7,
                            -6/7, -6/7, -6/7, -6/7, -6/7, -6/7, 1/7), ncol = 6)
# my.backward.diff = matrix(c(-1/6, 5/6, 5/6, 5/6, 5/6, 5/6,
#                             -2/6, -2/6, 4/6, 4/6, 4/6, 4/6,
#                             -3/6, -3/6, -3/6, 3/6, 3/6, 3/6,
#                             -4/6, -4/6, -4/6, -4/6, 2/6, 2/6,
#                             -5/6, -5/6, -5/6, -5/6, -5/6, 1/6), ncol = 5)
my.backward.diff
           [,1]       [,2]       [,3]       [,4]       [,5]
[1,] -0.1428571 -0.2857143 -0.4285714 -0.5714286 -0.7142857
[2,]  0.8571429 -0.2857143 -0.4285714 -0.5714286 -0.7142857
[3,]  0.8571429  0.7142857 -0.4285714 -0.5714286 -0.7142857
[4,]  0.8571429  0.7142857  0.5714286 -0.5714286 -0.7142857
[5,]  0.8571429  0.7142857  0.5714286  0.4285714 -0.7142857
[6,]  0.8571429  0.7142857  0.5714286  0.4285714  0.2857143
[7,]  0.8571429  0.7142857  0.5714286  0.4285714  0.2857143
           [,6]
[1,] -0.8571429
[2,] -0.8571429
[3,] -0.8571429
[4,] -0.8571429
[5,] -0.8571429
[6,] -0.8571429
[7,]  0.1428571
contrasts(responses$treatment) = my.backward.diff
mod_comp = glmer(score ~ treatment + (1 | user_id) + 
  (1 | passage), data = responses, family = "binomial")
summary(mod_comp)
Generalized linear mixed model fit by maximum likelihood
  (Laplace Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: score ~ treatment + (1 | user_id) + (1 | passage)
   Data: responses

     AIC      BIC   logLik deviance df.resid 
 13555.5  13621.1  -6768.7  13537.5    10853 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.4462 -0.9848  0.5353  0.7489  3.0720 

Random effects:
 Groups  Name        Variance Std.Dev.
 user_id (Intercept) 0.299    0.5469  
 passage (Intercept) 0.275    0.5244  
Number of obs: 10862, groups:  user_id, 153; passage, 12

Fixed effects:
             Estimate Std. Error z value Pr(>|z|)   
(Intercept)  0.283208   0.172210   1.645  0.10006   
treatment1  -0.249715   0.077999  -3.201  0.00137 **
treatment2   0.009679   0.101917   0.095  0.92434   
treatment3   0.079326   0.101799   0.779  0.43584   
treatment4  -0.291307   0.100818  -2.889  0.00386 **
treatment5  -0.105192   0.100348  -1.048  0.29451   
treatment6  -0.171392   0.099616  -1.721  0.08534 . 
---
Signif. codes:  
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
           (Intr) trtmn1 trtmn2 trtmn3 trtmn4 trtmn5
treatment1 -0.114                                   
treatment2 -0.001 -0.651                            
treatment3  0.001 -0.001 -0.501                     
treatment4  0.001  0.002 -0.002 -0.502              
treatment5 -0.001 -0.003  0.002  0.001 -0.494       
treatment6  0.284  0.003 -0.001 -0.001  0.000 -0.508
#plot(Effect(mod_comp, focal.predictors = "treatment"))
ef <- data.frame(Effect(mod_comp, focal.predictors = "treatment"))

Now the accuracy plot

ggplot(data = ef, aes(x = treatment, y = fit))+ labs(x = "Treatment", y = "Comprehension score")+ ggtitle("Comprehension accuracy") +
  geom_errorbar(aes(ymin=fit-se, ymax=fit+se), width=.2, position=position_dodge(width=0.1))+
  geom_point(position=position_dodge(width=0.1), (aes(size=1.)), show.legend =F)

The results are beautiful. All average scores range between 66 and 49% (with the baseline of 25%). Comprehension scores across speech rates are virtually constant between 180 and 270 wpm, then they monotonically (linearly?) decrease. The only contrast between adjacent levels reaching significance is a drop between 270 and 315 wpm. Also, reading comprehension scores are numerically higher than all listening comprehension scores: significantly so when reading was compared with the audio book speed (which is an interesting finding on its own).

Reading rate

Reading times are given per chunk. Some reading times (and as a result, reading rates) are completely insane, in both directions – too long or too short. Need to decide how to trim them. Here are distributions of the raw data in minutes or wpm. The median before trimming is 254 wpm.

#this is removing all those who were bad listeners or otherwise infelicitous
reading_times <- reading_times %>% filter(!user_id %in% bad, user_id %in% good)
read_rate <- inner_join(reading_times, chunks[, -1])
Joining, by = c("passage", "chunk_num")
read_rate$read_time <- (read_rate$chunk_end - read_rate$chunk_start)/1000/60 
#this is a by-percent breakdown of the distribution
quantile(read_rate$read_time, seq(0, 1, 0.01))
         0%          1%          2%          3%          4% 
 0.00025000  0.01002733  0.01166667  0.01336600  0.01501400 
         5%          6%          7%          8%          9% 
 0.01717333  0.02065000  0.02705733  0.03729333  0.05449933 
        10%         11%         12%         13%         14% 
 0.07334000  0.08758133  0.09990067  0.11576267  0.12770800 
        15%         16%         17%         18%         19% 
 0.14040333  0.15310667  0.16526133  0.17709000  0.19108000 
        20%         21%         22%         23%         24% 
 0.20041000  0.21076133  0.21936867  0.22930600  0.23834133 
        25%         26%         27%         28%         29% 
 0.24613333  0.25222733  0.26102600  0.26786533  0.27573933 
        30%         31%         32%         33%         34% 
 0.28276333  0.28793200  0.29349067  0.29946400  0.30627933 
        35%         36%         37%         38%         39% 
 0.31237000  0.31844333  0.32407267  0.33040800  0.33667267 
        40%         41%         42%         43%         44% 
 0.34254667  0.34837067  0.35667400  0.36380000  0.36999600 
        45%         46%         47%         48%         49% 
 0.37615333  0.38351600  0.39223600  0.39997400  0.40752267 
        50%         51%         52%         53%         54% 
 0.41650000  0.42331000  0.43106867  0.43980600  0.44898867 
        55%         56%         57%         58%         59% 
 0.45738667  0.46286267  0.47117467  0.47819600  0.48600000 
        60%         61%         62%         63%         64% 
 0.49542000  0.50289267  0.51369733  0.52190133  0.52858600 
        65%         66%         67%         68%         69% 
 0.54126333  0.55027467  0.56006667  0.57049800  0.58106333 
        70%         71%         72%         73%         74% 
 0.59493333  0.60674400  0.62099600  0.63299667  0.64952400 
        75%         76%         77%         78%         79% 
 0.66586667  0.68034600  0.69636533  0.71279400  0.72663067 
        80%         81%         82%         83%         84% 
 0.74424000  0.76342867  0.78032200  0.80373600  0.82833733 
        85%         86%         87%         88%         89% 
 0.85652000  0.89149467  0.92244000  0.95265933  0.98772267 
        90%         91%         92%         93%         94% 
 1.03017000  1.08529267  1.13057467  1.19211133  1.28467400 
        95%         96%         97%         98%         99% 
 1.41357333  1.58037867  1.79813533  2.06467867  3.02722133 
       100% 
35.33828333 
read_rate$rate = read_rate$chunk_words/read_rate$read_time
round(quantile(read_rate$rate, seq(0, 1, 0.01)),2)
       0%        1%        2%        3%        4%        5% 
     2.63     37.37     50.54     62.94     69.74     76.23 
       6%        7%        8%        9%       10%       11% 
    82.85     89.28     96.28    101.93    107.33    112.33 
      12%       13%       14%       15%       16%       17% 
   116.27    119.57    123.95    127.42    130.99    134.92 
      18%       19%       20%       21%       22%       23% 
   137.39    141.60    145.92    149.52    152.79    155.89 
      24%       25%       26%       27%       28%       29% 
   158.97    163.26    167.21    170.79    174.81    177.50 
      30%       31%       32%       33%       34%       35% 
   181.58    185.68    189.66    194.21    197.29    200.88 
      36%       37%       38%       39%       40%       41% 
   203.24    207.48    211.96    215.24    219.31    222.06 
      42%       43%       44%       45%       46%       47% 
   226.75    230.22    233.84    238.46    241.72    244.49 
      48%       49%       50%       51%       52%       53% 
   246.77    250.10    253.91    257.98    263.46    267.46 
      54%       55%       56%       57%       58%       59% 
   271.45    275.15    279.89    283.95    288.74    293.24 
      60%       61%       62%       63%       64%       65% 
   298.80    303.66    308.38    314.46    320.19    325.52 
      66%       67%       68%       69%       70%       71% 
   331.97    339.70    346.52    354.20    361.19    368.69 
      72%       73%       74%       75%       76%       77% 
   376.36    386.51    396.15    407.88    417.26    429.75 
      78%       79%       80%       81%       82%       83% 
   443.38    458.75    481.94    511.25    556.47    607.89 
      84%       85%       86%       87%       88%       89% 
   663.09    743.87    844.24    928.41   1075.33   1267.16 
      90%       91%       92%       93%       94%       95% 
  1476.10   1996.83   2755.71   4053.62   5227.79   6159.40 
      96%       97%       98%       99%      100% 
  7051.93   7996.53   9061.46  11305.44 504000.00 
summary(read_rate$rate) #before trimming
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
     2.6    163.3    253.9   1278.5    407.9 504000.0 
#median 254

My solution so far was to take the hypothesized (and commonly observed) reading rate of 280 wpm and restrict the range to 2.5 or 1/2.5 times this rate. The median comes out at 246 wpm. But this is really something to discuss!

read_rate <- read_rate %>% filter(rate > 280/2.5 , rate < 280 * 2.5)
hist(read_rate$rate, breaks = 30)

summary(read_rate$rate) #after trimming
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  112.1   179.0   246.3   270.5   332.4   699.0 
#median 246

Given the trimming above, we find the mean reading rate by subject by passage and then average this by subject. Median is 262 wpm.

part_passage <- read_rate %>% group_by(user_id, passage) %>% summarise(mean_rate = mean(rate))
part_rate <- part_passage %>% group_by(user_id) %>% summarise(mean_rate = mean(mean_rate))
summary(part_rate$mean_rate)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  118.8   200.5   261.9   277.7   346.2   609.6 
#262

Now we combine reading rate and reading comprehension scores by passage.

scores_passage <- scores_passage %>% filter(treatment == "reading")
read_passage <- inner_join(part_passage, scores_passage)
Joining, by = c("user_id", "passage")
part_readcomp <- read_passage %>% group_by(user_id) %>% summarise(readcomp = mean(mean_score))

What is the influence of one’s reading rate on their reading comprehension? None whatsoever. This is another illustration of how these two are disjoint (a point made in Kuperman’s random forest paper and a recent special issue on online vs offline measures of comprehension). I could do a better job with scores (which are bound to the 0-1 scale now) etc, but I doubt it will change much.

plot(read_passage$mean_rate, read_passage$mean_score)

cor.test(read_passage$mean_score, read_passage$mean_rate)

    Pearson's product-moment correlation

data:  read_passage$mean_score and read_passage$mean_rate
t = -0.61629, df = 817, p-value = 0.5379
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.08992831  0.04701832
sample estimates:
        cor 
-0.02155611 
hist(read_passage$mean_rate)

mod_passage = lmer(mean_score ~ mean_rate + (1 | user_id) + (1 | passage), data = read_passage)
#cor(fitted(mod_passage), read_passage$mean_rate)^2
mod_passage1 = lmer(mean_score ~ mean_rate  + (1 | user_id) + (1 | passage), data = read_passage, subset = abs(scale(resid(mod_passage)))<2.5)
summary(mod_passage1)
Linear mixed model fit by REML ['lmerMod']
Formula: 
mean_score ~ mean_rate + (1 | user_id) + (1 | passage)
   Data: read_passage
 Subset: abs(scale(resid(mod_passage))) < 2.5

REML criterion at convergence: -444

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-2.73352 -0.57625  0.04311  0.61750  2.19748 

Random effects:
 Groups   Name        Variance Std.Dev.
 user_id  (Intercept) 0.01224  0.1106  
 passage  (Intercept) 0.02694  0.1641  
 Residual             0.02443  0.1563  
Number of obs: 809, groups:  user_id, 150; passage, 12

Fixed effects:
              Estimate Std. Error t value
(Intercept)  6.692e-01  5.223e-02  12.813
mean_rate   -6.237e-05  6.964e-05  -0.896

Correlation of Fixed Effects:
          (Intr)
mean_rate -0.368

Now let’s see if reading and listening comprehension correlate within participant.

scores_all %>% select(user_id, treatment, mean_score) %>% spread(key = treatment, value = mean_score) -> scores_wide
round(cor(scores_wide[, -1]),2)
        reading sp180 sp225 sp270 sp315 sp360 sp405
reading    1.00  0.36  0.19  0.26  0.24  0.30  0.13
sp180      0.36  1.00  0.22  0.25  0.28  0.33  0.17
sp225      0.19  0.22  1.00  0.17  0.20  0.23  0.16
sp270      0.26  0.25  0.17  1.00  0.25  0.33  0.10
sp315      0.24  0.28  0.20  0.25  1.00  0.39  0.23
sp360      0.30  0.33  0.23  0.33  0.39  1.00  0.22
sp405      0.13  0.17  0.16  0.10  0.23  0.22  1.00

Fairly weak correlations all around, especially for the very high speech rate 405 wpm.

Now let’s break participants into groups by their reading comprehension and see if this predicts a different inflection point in listening comprehension.

scores_all %>% filter(treatment == "reading") -> tmp1
tmp2 <- data.frame(user_id = tmp1$user_id, readcomp = cut(tmp1$mean_score, breaks = quantile(tmp1$mean_score, c(0, 0.33, 0.66, 1)), labels = c("low", "mid", "high"), include.lowest = T))
#include read comp level into raw data
responses1 <- inner_join(responses, tmp2)
Joining, by = "user_id"
table(responses1$readcomp)

 low  mid high 
3744 3528 3672 
#run a model with an interaction by read comp level
responses1 %>% group_by(treatment, readcomp) %>% 
  summarise(sum_score = sum(score, na.rm = T), 
                                        n = NROW(score)) %>% mutate(mean_score = sum_score/n) -> x
#ggplot(aes(x = treatment, y = mean_score, col = readcomp), data = x) + geom_point()
mod_comp_brdown = glmer(score ~ treatment * readcomp + (1 | user_id) + 
  (1 | passage), data = responses1, subset = treatment != "reading", family = "binomial", control=glmerControl(optimizer="bobyqa",
                                 optCtrl=list(maxfun=2e5)))
contrasts dropped from factor treatment due to missing levelscontrasts dropped from factor treatment due to missing levels
summary(mod_comp_brdown)
Generalized linear mixed model fit by maximum likelihood
  (Laplace Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: 
score ~ treatment * readcomp + (1 | user_id) + (1 | passage)
   Data: responses1
Control: 
glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 2e+05))
 Subset: treatment != "reading"

     AIC      BIC   logLik deviance df.resid 
  7044.0   7175.8  -3502.0   7004.0     5371 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.0929 -0.9621  0.5456  0.8091  2.0640 

Random effects:
 Groups  Name        Variance Std.Dev.
 user_id (Intercept) 0.2595   0.5094  
 passage (Intercept) 0.1401   0.3743  
Number of obs: 5391, groups:  user_id, 152; passage, 12

Fixed effects:
                              Estimate Std. Error z value
(Intercept)                   -0.12838    0.17620  -0.729
treatmentspsp225               0.19272    0.16910   1.140
treatmentspsp270               0.33480    0.16922   1.978
treatmentspsp315              -0.06359    0.17016  -0.374
treatmentspsp360              -0.06465    0.16979  -0.381
treatmentspsp405              -0.14100    0.16794  -0.840
readcompmid                    0.65779    0.20413   3.222
readcomphigh                   1.06321    0.20487   5.190
treatmentspsp225:readcompmid  -0.11970    0.24948  -0.480
treatmentspsp270:readcompmid  -0.28465    0.24822  -1.147
treatmentspsp315:readcompmid  -0.11964    0.24821  -0.482
treatmentspsp360:readcompmid  -0.33538    0.24743  -1.355
treatmentspsp405:readcompmid  -0.50129    0.24504  -2.046
treatmentspsp225:readcomphigh -0.44315    0.24682  -1.795
treatmentspsp270:readcomphigh -0.41198    0.24922  -1.653
treatmentspsp315:readcomphigh -0.34930    0.24666  -1.416
treatmentspsp360:readcomphigh -0.41692    0.24724  -1.686
treatmentspsp405:readcomphigh -0.52530    0.24427  -2.150
                              Pr(>|z|)    
(Intercept)                    0.46624    
treatmentspsp225               0.25441    
treatmentspsp270               0.04787 *  
treatmentspsp315               0.70862    
treatmentspsp360               0.70338    
treatmentspsp405               0.40113    
readcompmid                    0.00127 ** 
readcomphigh                  2.11e-07 ***
treatmentspsp225:readcompmid   0.63138    
treatmentspsp270:readcompmid   0.25146    
treatmentspsp315:readcompmid   0.62979    
treatmentspsp360:readcompmid   0.17528    
treatmentspsp405:readcompmid   0.04078 *  
treatmentspsp225:readcomphigh  0.07258 .  
treatmentspsp270:readcomphigh  0.09832 .  
treatmentspsp315:readcomphigh  0.15673    
treatmentspsp360:readcomphigh  0.09174 .  
treatmentspsp405:readcomphigh  0.03152 *  
---
Signif. codes:  
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation matrix not shown by default, as p = 18 > 12.
Use print(x, correlation=TRUE)  or
    vcov(x)        if you need it
ef1 = data.frame(Effect(mod_comp_brdown, focal.predictors = c("treatment", "readcomp")))
contrasts dropped from factor treatment due to missing levels
ggplot(data = ef1, aes(x = treatment, y = fit, col = readcomp))+ labs(x = "Treatment", y = "Comprehension score")+ ggtitle("Comprehension accuracy") +
  geom_errorbar(aes(ymin=fit-se, ymax=fit+se), width=.2, position=position_dodge(width=0.1))+
  geom_point(position=position_dodge(width=0.1), (aes(size=1.)), show.legend =F)

At all levels of reading comprehension, the drop is between 270 and 315 wpm - even if it’s not always significant. This is a tad greater than the reading rate.

Finally, moving to a very interesting question of whether people with greater reading rate tolerate higher speech rates better?

quantile(read_rate$rate, c(0, 0.33, 0.66, 1))
      0%      33%      66%     100% 
112.0598 201.3696 295.6064 699.0079 
tmp2 <- data.frame(user_id = part_rate$user_id, mean_rate = part_rate$mean_rate, ratefactor = cut(part_rate$mean_rate, breaks = quantile(part_rate$mean_rate, c(0, 0.33, 0.66, 1)), labels = c("low", "mid", "high"), include.lowest = T))
#include read comp level into raw data
responses2 <- inner_join(responses, tmp2)
Joining, by = "user_id"
table(responses2$ratefactor)

 low  mid high 
3600 3528 3672 
#run a model with an interaction by read comp level
responses2 %>% group_by(treatment, ratefactor) %>% 
  summarise(sum_score = sum(score, na.rm = T), 
                                        n = NROW(score)) %>% mutate(mean_score = sum_score/n) -> x
#ggplot(aes(x = treatment, y = mean_score, col = ratefactor), data = x) + geom_point() + facet_grid(~ ratefactor)
mod_rate_brdown = glmer(score ~ treatment * ratefactor + (1 | user_id) + 
  (1 | passage), data = responses2, family = "binomial", control=glmerControl(optimizer="bobyqa",
                                 optCtrl=list(maxfun=2e5)))
summary(mod_rate_brdown)
Generalized linear mixed model fit by maximum likelihood
  (Laplace Approximation) [glmerMod]
 Family: binomial  ( logit )
Formula: 
score ~ treatment * ratefactor + (1 | user_id) + (1 | passage)
   Data: responses2
Control: 
glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 2e+05))

     AIC      BIC   logLik deviance df.resid 
 13297.3  13464.5  -6625.6  13251.3    10626 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.3831 -0.9834  0.5358  0.7484  3.0261 

Random effects:
 Groups  Name        Variance Std.Dev.
 user_id (Intercept) 0.2782   0.5274  
 passage (Intercept) 0.2764   0.5257  
Number of obs: 10649, groups:  user_id, 150; passage, 12

Fixed effects:
                          Estimate Std. Error z value
(Intercept)                0.18232    0.20779   0.877
treatment1                -0.14324    0.13761  -1.041
treatment2                -0.11261    0.17841  -0.631
treatment3                 0.26491    0.17912   1.479
treatment4                -0.53761    0.17728  -3.033
treatment5                -0.23435    0.17344  -1.351
treatment6                -0.08183    0.17340  -0.472
ratefactormid              0.05281    0.20176   0.262
ratefactorhigh             0.26605    0.19945   1.334
treatment1:ratefactormid  -0.11536    0.19464  -0.593
treatment2:ratefactormid   0.30604    0.25433   1.203
treatment3:ratefactormid  -0.40940    0.25455  -1.608
treatment4:ratefactormid   0.55751    0.25295   2.204
treatment5:ratefactormid  -0.10753    0.24879  -0.432
treatment6:ratefactormid  -0.13827    0.24714  -0.559
treatment1:ratefactorhigh -0.17368    0.19150  -0.907
treatment2:ratefactorhigh  0.03997    0.24983   0.160
treatment3:ratefactorhigh -0.18234    0.25070  -0.727
treatment4:ratefactorhigh  0.24709    0.24878   0.993
treatment5:ratefactorhigh  0.42657    0.24765   1.722
treatment6:ratefactorhigh -0.13812    0.24546  -0.563
                          Pr(>|z|)   
(Intercept)                0.38026   
treatment1                 0.29791   
treatment2                 0.52789   
treatment3                 0.13916   
treatment4                 0.00243 **
treatment5                 0.17663   
treatment6                 0.63699   
ratefactormid              0.79351   
ratefactorhigh             0.18223   
treatment1:ratefactormid   0.55338   
treatment2:ratefactormid   0.22885   
treatment3:ratefactormid   0.10777   
treatment4:ratefactormid   0.02752 * 
treatment5:ratefactormid   0.66560   
treatment6:ratefactormid   0.57583   
treatment1:ratefactorhigh  0.36443   
treatment2:ratefactorhigh  0.87289   
treatment3:ratefactorhigh  0.46701   
treatment4:ratefactorhigh  0.32059   
treatment5:ratefactorhigh  0.08499 . 
treatment6:ratefactorhigh  0.57365   
---
Signif. codes:  
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation matrix not shown by default, as p = 21 > 12.
Use print(x, correlation=TRUE)  or
    vcov(x)        if you need it
ef2 = data.frame(Effect(mod_rate_brdown, focal.predictors = c("treatment", "ratefactor")))
ggplot(data = ef2, aes(x = treatment, y = fit, col = ratefactor))+ labs(x = "Treatment", y = "Comprehension score")+ ggtitle("Comprehension accuracy") +
  geom_errorbar(aes(ymin=fit-se, ymax=fit+se), width=.2, position=position_dodge(width=0.1))+
  geom_point(position=position_dodge(width=0.1), (aes(size=1.)), show.legend =F) + facet_grid(~ ratefactor)

This is really interesting! (The panels need reordering). What reading rate does is manipulate the range of changes in the listening rate, from the smallest in the fastest readers to the largest in the slowest readers. More specifically, fastest readers are worse at listening comprehension at low speeds but pretty consistent (i.e., lose less) over the entire speech rate range. They are the most tolerant to very high speech rates, and there is no clear drop in performance (inflection point).

In readers with both the mid- and low-reading rate, there is a clear drop. Readers with a mid-range reading rate maintain top performance at higher speech rates than slowest readers (up to 315 wp vs 270 wpm). So there is a clear hierarchy: fastest readers are the most tolerant, followed by mid-range, followed by low range of reading rate. But this tolerance comes at a cost. Top performance in listening comprehension is associated with mid and low reading rates, while fastest readers are mediocre in this task.

LS0tDQp0aXRsZTogIkFuYWx5c2lzIG9mIFJlYWRpbmcgYW5kIExpc3RlbmluZyBDb21wcmVoZW5zaW9uIFJhdGUiDQpjb250cmlidXRvcnM6IFNvcGhpYSBZYW5nLCBWaW5jZW50IFBvcnJldHRhLCBBa2ktSnVoYW5pIEt5csO2bMOkaW5lbiwgTWFyYyBCcnlzYmFlcnQsIFZpY3RvciBLdXBlcm1hbi4NCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgSW50cm8NCg0KVGhpcyBpcyBhbiBpbml0aWFsIGFuYWx5c2lzIG9mIHRoZSBkYXRhIHNldCB3aGljaCBjb250YWlucyB3aXRoaW4tcGFydGljaXBhbnQgZGF0YSBvbiAoaSkgYWNjdXJhY3kgYW5kIHJhdGUgb2YgcmVhZGluZyBjb21wcmVoZW5zaW9uIGFuZCAoaWkpIGFjY3VyYWN5IG9mIGxpc3RlbmluZyBjb21wcmVoZW5zaW9uIG9mIG9yYWwgdGV4dHMgcmVjb3JkZWQgYXQgZGlmZmVyZW50IHNwZWVkcy4gDQoNCiNNZXRob2RzDQojI1BhcnRpY2lwYW50cw0KUGFydGljaXBhbnRzIHdlcmUgcmVjcnVpdGVkIGZvciB0aGUgc3R1ZHkgdmlhIHRoZSBjcm93ZHNvdXJjaW5nIHBsYXRmb3JtIFByb2xpZmljIChwcm9saWZpYy5jbykuIFRoZSBmb2xsb3dpbmcgc2VsZWN0aW9uIGNyaXRlcmlhIHdlcmUgZGVmaW5lZCB1c2luZyB0aGUgUHJvbGlmaWMgc2V0dGluZ3M6IHBhcnRpY2lwYW50cyB3ZXJlIGJldHdlZW4gYWdlcyAxOCBhbmQgMzA7IGJvcm4gYW5kIHJlc2lkaW5nIGluICBDYW5hZGEgb3IgdGhlIFVTQTsgd2l0aCBubyByZXBvcnRlZCBsYW5ndWFnZSwgaGVhcmluZyBvciB2aXNpb24gZGlmZmljdWx0aWVzOyBhbmQgRW5nbGlzaCBtb25vbGluZ3VhbHMuIEFzIGRlc2NyaWJlZCAgaW4gZGV0YWlsIGluIHRoZSBQcm9jZWR1cmUgYmVsb3csIHBhcnRpY2lwYW50cyB3ZXJlIGluaXRpYWxseSBpbnZpdGVkIHRvIGNvbXBsZXRlIHRoZSByZWFkaW5nIHRhc2suIFRoZXkgd2VyZSBjb21wZW5zYXRlZCBieSAzLjc1IEdCUCBmb3IgdGhlaXIgdGltZS4gVGhvc2Ugd2hvIGNvbXBsZXRlZCB0aGlzIHRhc2sgd2VyZSBmdXJ0aGVyIGludml0ZWQgdG8gdGhlIGxpc3RlbmluZyBjb21wcmVoZW5zaW9uIHRhc2s6IHRoZSBjb21wZW5zYXRpb24gd2FzIGFuIGFkZGl0aW9uYWwgMy43NSBHQlAuICANCg0KQSB0b3RhbCBvZiAyMTYgcGFydGljaXBhbnRzIGNvbXBsZXRlZCB0aGUgcmVhZGluZyB0YXNrIGFuZCAxNjUgb2YgdGhlc2UgcGFydGljaXBhbnRzIGFsc28gY29tcGxldGVkIHRoZSBsaXN0ZW5pbmcgY29tcHJlaGVuc2lvbiB0YXNrLiBBZnRlciBleGNsdWRpbmcgaW5kaXZpZHVhbHMgd2l0aCBpbmNvbXBsZXRlIHJlc3BvbnNlcyBhcyB3ZWxsIGFzIGluZGl2aWR1YWxzIHdobyBpbmRpY2F0ZWQgaW4gdGhlIGRlbW9ncmFwaGljIHF1ZXNpdG9ubmFpcmUgdGhhdCB0aGV5IGxlYXJuZWQgRW5nbGlzaCBhZnRlciB0aGUgYWdlIG9mIDUgb3IgaGFkIGEgcmVhZGluZyBpbXBhaXJtZW50LCB0aGUgcmVzdWx0aW5nIHBvb2wgb2YgcGFydGljaXBhbnRzIGNvbnRhaW5lZCAxNTIgcGFydGljaXBhbnRzLiBUaGUgbWVhbiBhZ2Ugb2YgdGhlIHBhcnRpY2lwYW50cyB3YXMgMjUuODAgKFNEID0gMy4yNSksIHdpdGggNzAgcGFydGljaXBhbnRzIGluZGljYXRpbmcgZmVtYWxlLCA3OSBtYWxlLCBhbmQgMiBvdGhlciBnZW5kZXIsIGFuZCAxIGRlY2xpbmUgdG8gcmVzcG9uc2UuIA0KDQoNCiMjTWF0ZXJpYWxzDQoNCkFsbCBzdGltdWxpIGNhbWUgZnJvbSB0aGUgTGVjdHVyZSwgSW50ZXJ2aWV3cyBhbmQgU3Bva2VuIE5hcnJhdGl2ZXMgKExJU04pIHRlc3QsICBhIGxpc3RlbmluZyBjb21wcmVoZW5zaW9uIHRlc3QsIGRldmVsb3BlZCBieSBTb21tZXIsIFR5ZS1NdXJyYXksIGFuZCBjb2xsZWFndWVzIChTb21tZXJzIGV0IGFsLiwgMjAwNzsgMjAxMTsgVHllLU11cnJheSAyMDA4KS4gVGhlIExJU04gdGVzdCBpcyBjb21wb3NlZCBvZiAzIGRpZmZlcmVudCB0eXBlcyBvZiByZWNvcmRlZCBhdWRpbyBwYXNzYWdlcyAobGVjdHVyZSwgaW50ZXJ2aWV3cyBhbmQgbmFycmF0aXZlcykuIE91ciBwcmlvciBzdHVkeSBzaG93ZWQgdGhhdCBuYXJyYXRpdmVzIGFyZSBhIG1vcmUgcmVsaWFibGUgYW5kIGVhc2llciBwYXNzYWdlIHR5cGU6IHRoZSBwcmVzZW50IHN0dWR5IG9ubHkgdXNlZCBuYXJyYXRpdmVzLiANCg0KVGhlIG5hcnJhdGl2ZXMgd2VyZSBzZWxlY3RlZCBmcm9tIHRoZSBSdXRnZXJzIFVuaXZlcnNpdHkgT3JhbCBIaXN0b3J5IEFyY2hpdmVzIG9mIHBlcnNvbmFsIGRlc2NyaXB0aW9ucyBvZiBsaWZlIGV4cGVyaWVuY2VzIChTb21tZXIgZXQgYWwuLCAyMDExKS4gVGhlIHBhc3NhZ2VzIHdlcmUgYmV0d2VlbiAzLTUgbWludXRlcyBsb25nICg1NDItLTY2MSB3b3JkcyBwZXIgcGFzc2FnZSwgbWVkaWFuID0gNjE2IHdvcmRzKSBhbmQgd2VyZSByZWNvcmRlZCBieSBtYWxlIGFuZCBmZW1hbGUgcHJvZmVzc2lvbmFsIGFjdG9ycyB3aXRoIE5vcnRoIEFtZXJpY2FuIGFjY2VudHMuIFRoaXMgc3R1ZHkgIHVzZWQgMTIgb3V0IG9mIDE2IG5hcnJhdGl2ZSBwYXNzYWdlcy4gU2l4IHBhc3NhZ2VzIHdlcmUgcHJlc2VudGVkIHRvIGEgZ2l2ZW4gcGFydGljaXBhbnQgaW4gdGhlIHdyaXR0ZW4gZm9ybWF0IGZvciByZWFkaW5nIGNvbXByZWhlbnNpb24gYW5kIGFub3RoZXIgc2l4IG9yYWxseSwgYXQgZGlmZmVyZW50IHNwZWVkcyAoc2VlIGJlbG93KSBmb3IgbGlzdGVuaW5nIGNvbXByZWhlbnNpb24uDQoNCkVhY2ggcGFzc2FnZSB3YXMgZm9sbG93ZWQgYnkgNiBjb21wcmVoZW5zaW9uIHF1ZXN0aW9ucy4gVGhlIHF1ZXN0aW9ucyBhcmUgaW4gdGhlIG11bHRpcGxlIGNob2ljZSBmb3JtYXQgd2l0aCA0IG9wdGlvbnMgcGVyIHF1ZXN0aW9uLiBUaGVyZSBhcmUgdGhyZWUgdHlwZXMgb2YgY29tcHJlaGVuc2lvbiBxdWVzdGlvbnM6IGluZm9ybWF0aW9uIHF1ZXN0aW9ucywgaW50ZWdyYXRpb24gcXVlc3Rpb25zIGFuZCBpbmZlcmVuY2UgcXVlc3Rpb25zLiBJbmZvcm1hdGlvbiBxdWVzdGlvbnMgYXNrIHBhcnRpY2lwYW50cyB0byByZWNhbGwgYSBzcGVjaWZpYyBwaWVjZSBvZiBpbmZvcm1hdGlvbiBmcm9tIHRoZSB0ZXh0LiBJbnRlZ3JhdGlvbiBxdWVzdGlvbnMgYXNzZXNzIGlmIHRoZSBwYXJ0aWNpcGFudHMgYXJlIGFibGUgdG8gY29tYmluZSBtdWx0aXBsZSBiaXRzIG9mIGluZm9ybWF0aW9uIGxlYXJuZWQgZnJvbSB0aGUgdGV4dC4gRmluYWxseSwgaW5mZXJlbmNlIHF1ZXN0aW9ucyBhc2sgcGFydGljaXBhbnRzIHRvIGRlcml2ZSBpbXBsaWNhdGlvbnMgZnJvbSB0aGUgaW5mb3JtYXRpb24gaW4gdGhlIHBhc3NhZ2VzIChTb21tZXIgZXQgYWwuLCAyMDExKS4gVHdvIG9mIGVhY2ggdHlwZSBvZiBxdWVzdGlvbnMgd2VyZSBhc2tlZCBmb3IgYSB0b3RhbCBvZiA2IHF1ZXN0aW9ucyBwZXIgcGFzc2FnZS4gQnkgdGhlIGVuZCBvZiBlYWNoIHNldCBvZiA2IHBhc3NhZ2VzIGZvciByZWFkaW5nIGFuZCBsaXN0ZW5pbmcsIHBhcnRpY2lwYW50cyB3b3VsZCBoYXZlIGFuc3dlcmVkIDM2IGNvbXByZWhlbnNpb24gcXVlc3Rpb25zLCBvciA3MiBxdWVzdGlvbnMgaW4gdG90YWwuIFRoZSBpbmRpdmlkdWFsIHNjb3JlcyB3ZXJlIGNhbGN1bGF0ZWQgc2VwYXJhdGVseSBmb3IgcmVhZGluZyBhbmQgbGlzdGVuaW5nIGFzIGEgcGVyY2VudGFnZSBvZiBxdWVzdGlvbnMgdGhleSBhbnN3ZXJlZCBjb3JyZWN0bHkgb3V0IG9mIDM2LiANCg0KQXVkaW8gcmVjb3JkaW5ncyBvZiB0ZXh0cyB3ZXJlIG1hbmlwdWxhdGVkIHVzaW5nIGNvbXB1dGVyIHNvZnR3YXJlIHBhY2thZ2UgUFJBQVQgW1JFRl0gdG8gcmVwcmVzZW50IGEgcmFuZ2Ugb2Ygc3BlZWNoIHJhdGVzLiBTcGVlY2ggcmF0ZSB3YXMgZmlyc3QgY2FsY3VsYXRlZCBmb3IgdGhlIG9yaWdpbmFsIHJlY29yZGluZ3Mgb2YgdGhlIDEyIG5hcnJhdGl2ZSBwYXNzYWdlcyBmcm9tIHRoZSBMSVNOIHRlc3QgYXMgdGhlIHRvdGFsIG51bWJlciBvZiB3b3JkcyBpbiB0aGUgcGFzc2FnZSBkaXZpZGVkIGJ5IHRoZSBkdXJhdGlvbiBvZiBpdHMgcmVjb3JkaW5nLCBpbiB3b3JkcyBwZXIgbWludXRlICh3cG0pLiBUaGUgYXZlcmFnZSBzcGVlY2ggcmF0ZSB3YXMgMTgwIHdwbSwgY29tcGFyYWJsZSB0byBhIHR5cGljYWwgc3BlZWNoIHJhdGUgb2YgYW4gYXVkaW8gYm9vayBbUkVGXS4gV2UgYWRqdXN0ZWQgdGhlIGR1cmF0aW9uIG9mIGVhY2ggcmVjb3JkaW5nIHRocm91Z2ggY29tcHJlc3Npb24gb3IgbGVuZ2h0ZW5pbmcgdG8gZW5zdXJlIHRoZSAxODAgd3BtIHNwZWVjaCByYXRlIGluIHRoZSBiYXNlbGluZSBjb25kaXRpb24uIEZ1cnRoZXIsIGVhY2ggcmVjb3JkaW5nIHdhcyBhbHRlcmVkIGZvciBkdXJhdGlvbiB0byBvYnRhaW4gc3BlZWNoIHJhdGVzIGVxdWFsIHRvIDEyNVwlICgyMjUgd3BtKSwgMTUwXCUgKDI3MCB3cG0pLCAxNzVcJSAoMzE1IHdwbSksIDIwMFwlICgzNjAgd3BtKSwgYW5kIDIyNVwlICg0MDUgd3BtKSBvZiB0aGUgYmFzZWxpbmUgc3BlZWNoIHJhdGUuIFRoZSByYW5nZSBvZiBzcGVlY2ggcmF0ZXMgd2FzIGNob3NlbiB0byByZXByZXNlbnQgYm90aCBjYXJlZnVsIHNwZWVjaCBhbmQgY29tZm9ydGFibGUgZm9yIGxpc3RlbmluZyBjb21wcmVoZW5zaW9uIGFuZCB2ZXJ5IHJhcGlkIHNwZWVjaCwgd2hpY2ggaGluZGVycyBjb21wcmVoZW5zaW9uLg0KDQoNCiMjUHJvY2VkdXJlDQpUaGUgcHJlc2VudCBleHBlcmltZW50YWwgc3R1ZHkgd2FzIGltcGxlbWVudGVkIG9ubGluZSBhcyB0d28gIHdlYi1iYXNlZCBleHBlcmltZW50cyAtLSBmb3IgdGhlIHJlYWRpbmcgYW5kIGxpc3RlbmluZyBjb21wcmVoZW5zaW9uIHRhc2tzLCByZXNwZWN0aXZlbHkgLS0gd2l0aCBhIHNoYXJlZCBTUUwgZGF0YWJhc2Ugc3RvcmluZyByZXNwb25zZXMuIFBhcnRpY2lwYW50cyB3ZXJlIHJlY3J1aXRlZCB2aWEgdGhlIGNyb3dkc291cmNpbmcgcGxhdGZvcm0gUHJvbGlmaWMgKHByb2xpZmljLmNvKSBhbmQgd2VyZSBpbml0aWFsbHkgaW52aXRlZCB0byBjb21wbGV0ZSB0aGUgcmVhZGluZyB0YXNrIG9uIHRoZSBjcm93ZHNvdXJjaW5nIHBsYXRmb3JtIFByb2xpZmljLiBUaGlzIHRhc2sgdG9vayBhbiBhdmVyYWdlIG9mIHJvdWdobHkgMjAgbWludXRlcy4gVXBvbiBjb21wbGV0aW9uLCBzYW1lIHBhcnRpY2lwYW50cyB3ZXJlIGZ1cnRoZXIgaW52aXRlZCB0byBjb21wbGV0ZSBhIGxpc3RlbmluZyBjb21wcmVoZW5zaW9uIHRhc2suICBUaGUgbGlzdGVuaW5nIGNvbXByZWhlbnNpb24gdGFzayB0b29rIGFuIGF2ZXJhZ2Ugb2YgYWJvdXQgMzAgbWludXRlcy4gQSBicmVhayBvZiBhdCBsZWFzdCBvbmUgaG91ciB3YXMgb2JzZXJ2ZWQgYmV0d2VlbiB0aGUgdGFza3MgdG8gbWluaW1pemUgZmF0aWd1ZSBhbmQgcG90ZW50aWFsIHNwaWxsb3ZlciBlZmZlY3RzLg0KDQpJZiBhIHBhcnRpY2lwYW50IGNob3NlIHRvIGNvbXBsZXRlIGJvdGggdGFza3MsIHRoZXkgd2VyZSBleHBvc2VkIHRvIGEgdG90YWwgb2YgMTIgcGFzc2FnZXMuIFRoZSBhc3NpZ25tZW50IG9mIHBhc3NhZ2VzIHRvIHRoZSByZWFkaW5nIHZlcnN1cyBsaXN0ZW5pbmcgdGFzayAoc2l4IGVhY2gpIGFuZCB0aGUgYXNzaWdubWVudCBvZiBwYXNzYWdlcyBpbiB0aGUgbGlzdGVuaW5nIHRhc2sgdG8gc2l4IGRpZmZlcmVudCBzcGVlY2ggcmF0ZXMgd2FzIHJhbmRvbWl6ZWQgZm9yIGVhY2ggcGFydGljaXBhbnQuIFRoZSByZWFkaW5nIHRhc2sgYWx3YXlzIGNhbWUgZmlyc3QsIGZvbGxvd2VkIGJ5IHRoZSBsaXN0ZW5pbmcgdGFzay4gVGhlIG9yZGVyIG9mIHNwZWVjaCByYXRlIGNvbmRpdGlvbnMgd2l0aGluIHRoZSBsaXN0ZW5pbmcgdGFzayB3YXMgcmFuZG9taXplZC4NCg0KKk5lZWQgdG8gZmlsbCBpbiBkZXRhaWxzIGFib3V0IGNvbnNlbnQgZm9ybXMsIHF1ZXN0aW9ubmFpcmVzLCBhdWRpbyBjaGVjaywgZXRjKg0KDQoqUmVhZGluZyogRWFjaCBwYXNzYWdlIHByZXNlbnRlZCBmb3IgcmVhZGluZyB3YXMgc2VwYXJhdGVkIGludG8gNS02IGNodW5rcyBlcXVpdmFsZW50IHRvIG9uZSBvciB0d28gIHBhcmFncmFwaHMgb2YgdGhlIG9yaWdpbmFsIHRleHQuIENodW5rcyBhcHBlYXJlZCBvbiB0aGUgc2NyZWVuIG9uZSBhdCBhIHRpbWUgYW5kIHBhcnRpY2lwYW50cyBwcmVzc2VkIHNwYWNlIHRvIG1vdmUgdG8gdGhlIG5leHQgY2h1bmsuIFdoZW4gdGhlIHBhc3NhZ2Ugd2FzIHJlYWQsIHNpeCBjb21wcmVoZW5zaW9uIHF1ZXN0aW9ucyB3aXRoIDQgbXVsdGlwbGUgY2hvaWNlIG9wdGlvbnMgYXBwZWFyZWQgb24gdGhlIHNjcmVlbiBvbmUgYXQgYSB0aW1lLiBQYXJ0aWNpcGFudHMgcmVzcG9uZGVkIGJ5IHByZXNzaW5nIGEsIGIsIGMsIG9yIGQuIFJlYWRpbmcgdGltZXMgcGVyIGNodW5rIGFuZCByZXNwb25zZSBrZXlzIGFuZCBsYXRlbmNpZXMgd2VyZSByZWNvcmRlZC4gQWZ0ZXIgdGhlIGxhc3QgcXVlc3Rpb24gdG8gYSBwYXNzYWdlIHdhcyBjb21wbGV0ZWQsIHRoZSBuZXh0IHBhc3NhZ2UgYXBwZWFyZWQuDQoNCipMaXN0ZW5pbmcqIEluIHRoZSBiZWdpbm5pbmcgb2YgdGhpcyB0YXNrLCBwYXJ0aWNpcGFudHMgd2VyZSBhc2tlZCB0byB1c2UgdGhlaXIgaGVhZHBob25lcy4gQW4gYXVkaW8tdmVyaWZpY2F0aW9uIHRhc2sgd2FzIGFkbWluaXN0ZXJlZCBiZWZvcmUgc3RpbXVsdXMgcHJlc2VudGF0aW9uLiBBbiBhdWRpbyBmaWxlIHdpdGggYSB0ZXN0IHBocmFzZSB3YXMgcGxheWVkIGFuZCBwYXJ0aWNpcGFudHMgd2VyZSByZXF1ZXN0ZWQgdG8gdHlwZSB0aGF0IHBocmFzZSBvbiB0aGUgc2NyZWVuLiBUaGlzIHN0ZXAgcHJvdmlkZWQgYSBjaGFuY2UgdG8gYWRqdXN0IGF1ZGlvIHNldHRpbmdzIG9mIHRoZSBwYXJ0aWNpcGFudCdzIGNvbXB1dGVycyBvciBoZWFkcGhvbmVzLiBBcyB0aGUgbmV4dCBzdGVwLCBhdWRpbyByZWNvcmRpbmdzIG9mIHBhc3NhZ2VzIHdlcmUgcGxheWVkIG9uZSBhdCBhIHRpbWUuIFBhcnRpY2lwYW50cyBkaWQgbm90IGhhdmUgdGhlIG9wdGlvbiB0byBwYXVzZSBvciBzdG9wIHRoZSByZWNvcmRpbmcuIEFzIGluIHRoZSByZWFkaW5nIHRhc2ssIGFmdGVyIGVhY2ggcmVjb3JkaW5nLCBzaXggY29tcHJlaGVuc2lvbiBxdWVzdGlvbnMgYXBwZWFyZWQgb24gdGhlIHNjcmVlbiBpbiB0aGUgd3JpdHRlbiBmb3JtYXQuIFBhcnRpY2lwYW50cyByZXNwb25kZWQgYnkgcHJlc3Npbmcga2V5cyBhLCBiLCBjLCBvciBkLiANClJlc3BvbnNlIGtleXMgYW5kIGxhdGVuY2llcyB3ZXJlIHJlY29yZGVkLg0KDQojUmVzdWx0cw0KVGhpcyBpcyB3aGVyZSB0aGUgY29oZXJlbnQgbmFycmF0aXZlIGVuZHMgYW5kIGEgc3BvdHR5IHJlcG9ydGluZyBiZWdpbnMuDQoNCkxvYWQgdXAgbGlicmFyaWVzIGFuZCBwcmltYXJ5IGRhdGEuDQpgYGB7cixyZXN1bHRzPSdoaWRlJ30NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShlZmZlY3RzKQ0KbGlicmFyeShsbWU0KQ0KYGBgDQoNCmBgYHtyLHJlc3VsdHM9J2hpZGUnfQ0KbG9hZCgiZGF0YS5yZGEiKQ0KYGBgDQojI0FjY3VyYWN5DQoNCkFjY3VyYWN5IGZyb20gYm90aCB0aGUgcmVhZGluZyB0YXNrIGFuZCBsaXN0ZW5pbmcgdGFzaywgcmVwb3J0ZWQgam9pbnRseS4NCg0KYGBge3J9DQoNCnJlc3BvbnNlcyA8LSBsZWZ0X2pvaW4ocmVzcG9uc2VzLCBxdWVzdGlvbnMpICU+JSBmaWx0ZXIodXNlcl9pZCAlaW4lIHBhcnRpY2lwYW50cyR1c2VyX2lkLCB1c2VyX2lkID49IDkpDQpnb29kIDwtIG5hbWVzKHdoaWNoKHRhYmxlKHJlc3BvbnNlcyR1c2VyX2lkKSA9PSA3MikpICNvbmx5IHVzZSBwYXJ0aWNpcGFudHMgd2l0aCBib3RoIHJlYWRpbmcgYW4gbGlzdGVuaW5nDQoNCnJlc3BvbnNlcyA8LSByZXNwb25zZXMgJT4lIGZpbHRlcih1c2VyX2lkICVpbiUgZ29vZCkNCmxlbmd0aCh1bmlxdWUocmVzcG9uc2VzJHVzZXJfaWQpKQ0KDQpyZXNwb25zZXMkdHJlYXRtZW50ID0gZHJvcGxldmVscyhyZXNwb25zZXMkdHJlYXRtZW50KQ0KcmVzcG9uc2VzJHRyZWF0bWVudCA9IHJlbGV2ZWwocmVzcG9uc2VzJHRyZWF0bWVudCwgcmVmID0gInJlYWRpbmciKQ0KDQpyZXNwb25zZXMkc2NvcmUgPC0gaWZlbHNlKHJlc3BvbnNlcyRyZXNwb25zZSA9PSB0b2xvd2VyKHJlc3BvbnNlcyRjb3JyZWN0X3Jlc3BvbnNlKSwgMSwgMCkNCnRhYmxlKHJlc3BvbnNlcyRzY29yZSkvbnJvdyhyZXNwb25zZXMpDQoNCnJlc3BvbnNlcyRydCA8LSAocmVzcG9uc2VzJHJlc3BvbnNlX2VuZCAtIHJlc3BvbnNlcyRyZXNwb25zZV9zdGFydCkvMTAwMA0KDQojdGhlc2UgYXJlIGFjY3VyYWN5IHNjb3JlcyBwZXIgc3ViamVjdCwgdHJlYXRlbnQgYW5kIHBhc3NhZ2UgKHJlYWRpbmcvbGlzdCB0b2dldGhlcikNCnNjb3Jlc19wYXNzYWdlIDwtICByZXNwb25zZXMgJT4lIGdyb3VwX2J5KHVzZXJfaWQsIHRyZWF0bWVudCwgcGFzc2FnZSkgJT4lIA0KICBmaWx0ZXIoaXMubmEoc2NvcmUpPT1GKSAlPiUgc3VtbWFyaXNlKHN1bV9zY29yZSA9IHN1bShzY29yZSwgbmEucm0gPSBUKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IE5ST1coc2NvcmUpKSAlPiUgbXV0YXRlKG1lYW5fc2NvcmUgPSBzdW1fc2NvcmUvbikNCg0Kc2NvcmVzX3JlYWQgPC0gIHJlc3BvbnNlcyAlPiUgZmlsdGVyKHRyZWF0bWVudCA9PSAicmVhZGluZyIpICU+JSBncm91cF9ieSh1c2VyX2lkLCB0cmVhdG1lbnQpICU+JSANCiAgZmlsdGVyKGlzLm5hKHNjb3JlKT09RikgJT4lIHN1bW1hcmlzZShzdW1fc2NvcmUgPSBzdW0oc2NvcmUsIG5hLnJtID0gVCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBOUk9XKHNjb3JlKSkgJT4lIG11dGF0ZShtZWFuX3Njb3JlID0gc3VtX3Njb3JlL24pDQoNCg0KI3RoZXNlIGFyZSBhY2N1cmFjeSBzY29yZXMgcGVyIHN1YmplY3QgYW5kIHRyZWF0bWVudA0Kc2NvcmVzX2FsbCA8LSAgcmVzcG9uc2VzICU+JSBncm91cF9ieSh1c2VyX2lkLCB0cmVhdG1lbnQpICU+JSANCiAgZmlsdGVyKGlzLm5hKHNjb3JlKT09RikgJT4lIHN1bW1hcmlzZShzdW1fc2NvcmUgPSBzdW0oc2NvcmUsIG5hLnJtID0gVCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBOUk9XKHNjb3JlKSkgJT4lIG11dGF0ZShtZWFuX3Njb3JlID0gc3VtX3Njb3JlL24pDQoNCg0KI3RoZXNlIGFyZSBSVHMgdG8gY29tcHJlaGVuc2lvbiBxdWVzdGlvbnMgcGVyIHN1YmplY3QgYW5kIHRyZWF0bWVudCAqTk9UIFVTRUQgU08gRkFSKg0KDQpydF9hbGwgPC0gcmVzcG9uc2VzICU+JSBncm91cF9ieSh1c2VyX2lkLCB0cmVhdG1lbnQpICU+JSBmaWx0ZXIoaXMubmEoc2NvcmUpPT1GLCBpcy5uYShydCkgPT1GKSAlPiUNCiAgc3VtbWFyaXNlKG1lZGlhbl9ydCA9IG1lZGlhbihydCksIG5fcnQgPSBOUk9XKHJ0KSkgDQoNCiNyZW1vdmluZyBwYXJ0aWNpcGFudHMgd2hvIGNsb3NlIHRvICBjaGFuY2UgKDMwJSB2cyAyNSBjaGFuY2UpDQojcHJvcC50ZXN0KDAuMzcgKiAzNiwgMzYsIDAuMjUsIGFsdGVybmF0aXZlID0gImdyZWF0ZXIiKQ0KDQpiYWQgPC0gc2NvcmVzX3JlYWRbd2hpY2goc2NvcmVzX3JlYWQkbWVhbl9zY29yZSA8PSAwLjMpLF0kdXNlcl9pZA0KDQoNCnNjb3Jlc19hbGwgPC0gc2NvcmVzX2FsbCAlPiUgZmlsdGVyKCF1c2VyX2lkICVpbiUgYmFkKQ0KcnRfYWxsIDwtIHJ0X2FsbCAlPiUgZmlsdGVyKCF1c2VyX2lkICVpbiUgYmFkKQ0KDQojcGFydGljaXBhbnRzIGRlc2NyaXB0aXZlcyAocmVwb3J0ZWQgaW4gUGFydGljaXBhbnRzKQ0KdXNlcl9nb29kIDwtIHJ0X2FsbCR1c2VyX2lkDQpwYXJ0aWNpcGFudHMgJT4lIGZpbHRlcih1c2VyX2lkICVpbiUgdXNlcl9nb29kKSAlPiUgc3VtbWFyaXNlKG1lYW4oYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoYWdlKSkpLCBzZChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihhZ2UpKSkpDQpwYXJ0aWNpcGFudHMgJT4lIGZpbHRlcih1c2VyX2lkICVpbiUgdXNlcl9nb29kKSAtPiB4DQp0YWJsZSh4JGdlbmRlcikNCg0KcmVzcG9uc2VzJHRyZWF0bWVudCA9IHBhc3RlMCgic3AiLCBhcy5jaGFyYWN0ZXIocmVzcG9uc2VzJHRyZWF0bWVudCkpDQpyZXNwb25zZXNbcmVzcG9uc2VzJHRyZWF0bWVudCA9PSAic3ByZWFkaW5nIixdJHRyZWF0bWVudCA9ICJyZWFkaW5nIg0KcmVzcG9uc2VzJHRyZWF0bWVudCA9IGFzLmZhY3RvcihyZXNwb25zZXMkdHJlYXRtZW50KQ0KYGBgDQoNCk5vdyB0byB0aGUgbW9kZWxzIGFuZCBwbG90dGluZyBvZiBhY2N1cmFjeS4gVGhlIExNRVIgaXMgc2V0IHVwIGZvciBiYWNrd2FyZCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGxldmVscyBvZiB0cmVhdG1lbnQgKHJlYWRpbmcsIHNwZWVjaCAxODAsIHNwZWVjaCAyMjUgZXRjLikuIFRyZWF0bWVudDEgaXMgYSBkaWZmZXJlbmNlIGluIGFjY3VyYWN5IGJldHdlZW4gc3AxODAgbWludXMgcmVhZGluZywgVHJlYXRtZW50MiBpcyBzcDIyNSBtaW51cyBzcDE4MCBldGMuDQoNCmBgYHtyfQ0KbXkuYmFja3dhcmQuZGlmZiA9IG1hdHJpeChjKC0xLzcsIDYvNywgNi83LCA2LzcsIDYvNywgNi83LCA2LzcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLTIvNywgLTIvNywgNS83LCA1LzcsIDUvNywgNS83LCA1LzcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLTMvNywgLTMvNywgLTMvNywgNC83LCA0LzcsIDQvNywgNC83LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIC00LzcsIC00LzcsIC00LzcsIC00LzcsIDMvNywgMy83LCAzLzcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLTUvNywgLTUvNywgLTUvNywgLTUvNywgLTUvNywgMi83LCAyLzcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLTYvNywgLTYvNywgLTYvNywgLTYvNywgLTYvNywgLTYvNywgMS83KSwgbmNvbCA9IDYpDQoNCiMgbXkuYmFja3dhcmQuZGlmZiA9IG1hdHJpeChjKC0xLzYsIDUvNiwgNS82LCA1LzYsIDUvNiwgNS82LA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLTIvNiwgLTIvNiwgNC82LCA0LzYsIDQvNiwgNC82LA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLTMvNiwgLTMvNiwgLTMvNiwgMy82LCAzLzYsIDMvNiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC00LzYsIC00LzYsIC00LzYsIC00LzYsIDIvNiwgMi82LA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLTUvNiwgLTUvNiwgLTUvNiwgLTUvNiwgLTUvNiwgMS82KSwgbmNvbCA9IDUpDQpteS5iYWNrd2FyZC5kaWZmDQoNCmNvbnRyYXN0cyhyZXNwb25zZXMkdHJlYXRtZW50KSA9IG15LmJhY2t3YXJkLmRpZmYNCg0KDQptb2RfY29tcCA9IGdsbWVyKHNjb3JlIH4gdHJlYXRtZW50ICsgKDEgfCB1c2VyX2lkKSArIA0KICAoMSB8IHBhc3NhZ2UpLCBkYXRhID0gcmVzcG9uc2VzLCBmYW1pbHkgPSAiYmlub21pYWwiKQ0Kc3VtbWFyeShtb2RfY29tcCkNCiNwbG90KEVmZmVjdChtb2RfY29tcCwgZm9jYWwucHJlZGljdG9ycyA9ICJ0cmVhdG1lbnQiKSkNCmVmIDwtIGRhdGEuZnJhbWUoRWZmZWN0KG1vZF9jb21wLCBmb2NhbC5wcmVkaWN0b3JzID0gInRyZWF0bWVudCIpKQ0KYGBgDQoNCk5vdyB0aGUgYWNjdXJhY3kgcGxvdA0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gZWYsIGFlcyh4ID0gdHJlYXRtZW50LCB5ID0gZml0KSkrIGxhYnMoeCA9ICJUcmVhdG1lbnQiLCB5ID0gIkNvbXByZWhlbnNpb24gc2NvcmUiKSsgZ2d0aXRsZSgiQ29tcHJlaGVuc2lvbiBhY2N1cmFjeSIpICsNCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1maXQtc2UsIHltYXg9Zml0K3NlKSwgd2lkdGg9LjIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuMSkpKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuMSksIChhZXMoc2l6ZT0xLikpLCBzaG93LmxlZ2VuZCA9RikNCg0KYGBgDQoNClRoZSByZXN1bHRzIGFyZSBiZWF1dGlmdWwuIEFsbCBhdmVyYWdlIHNjb3JlcyByYW5nZSBiZXR3ZWVuIDY2IGFuZCA0OVwlICh3aXRoIHRoZSBiYXNlbGluZSBvZiAyNVwlKS4gQ29tcHJlaGVuc2lvbiBzY29yZXMgYWNyb3NzIHNwZWVjaCByYXRlcyBhcmUgdmlydHVhbGx5IGNvbnN0YW50IGJldHdlZW4gMTgwIGFuZCAyNzAgd3BtLCB0aGVuIHRoZXkgbW9ub3RvbmljYWxseSAobGluZWFybHk/KSBkZWNyZWFzZS4gVGhlIG9ubHkgY29udHJhc3QgYmV0d2VlbiBhZGphY2VudCBsZXZlbHMgcmVhY2hpbmcgc2lnbmlmaWNhbmNlIGlzIGEgZHJvcCBiZXR3ZWVuIDI3MCBhbmQgMzE1IHdwbS4gQWxzbywgcmVhZGluZyBjb21wcmVoZW5zaW9uIHNjb3JlcyBhcmUgbnVtZXJpY2FsbHkgaGlnaGVyIHRoYW4gYWxsIGxpc3RlbmluZyBjb21wcmVoZW5zaW9uIHNjb3Jlczogc2lnbmlmaWNhbnRseSBzbyB3aGVuIHJlYWRpbmcgd2FzIGNvbXBhcmVkIHdpdGggdGhlIGF1ZGlvIGJvb2sgc3BlZWQgKHdoaWNoIGlzIGFuIGludGVyZXN0aW5nIGZpbmRpbmcgb24gaXRzIG93bikuIA0KDQojI1JlYWRpbmcgcmF0ZQ0KDQpSZWFkaW5nIHRpbWVzIGFyZSBnaXZlbiBwZXIgY2h1bmsuIFNvbWUgcmVhZGluZyB0aW1lcyAoYW5kIGFzIGEgcmVzdWx0LCByZWFkaW5nIHJhdGVzKSBhcmUgY29tcGxldGVseSBpbnNhbmUsIGluIGJvdGggZGlyZWN0aW9ucyAtLSB0b28gbG9uZyBvciB0b28gc2hvcnQuIE5lZWQgdG8gZGVjaWRlIGhvdyB0byB0cmltIHRoZW0uIEhlcmUgYXJlIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIHJhdyBkYXRhIGluIG1pbnV0ZXMgb3Igd3BtLiBUaGUgbWVkaWFuIGJlZm9yZSB0cmltbWluZyBpcyAyNTQgd3BtLg0KDQpgYGB7cn0NCiN0aGlzIGlzIHJlbW92aW5nIGFsbCB0aG9zZSB3aG8gd2VyZSBiYWQgbGlzdGVuZXJzIG9yIG90aGVyd2lzZSBpbmZlbGljaXRvdXMNCnJlYWRpbmdfdGltZXMgPC0gcmVhZGluZ190aW1lcyAlPiUgZmlsdGVyKCF1c2VyX2lkICVpbiUgYmFkLCB1c2VyX2lkICVpbiUgZ29vZCkNCg0KcmVhZF9yYXRlIDwtIGlubmVyX2pvaW4ocmVhZGluZ190aW1lcywgY2h1bmtzWywgLTFdKQ0KcmVhZF9yYXRlJHJlYWRfdGltZSA8LSAocmVhZF9yYXRlJGNodW5rX2VuZCAtIHJlYWRfcmF0ZSRjaHVua19zdGFydCkvMTAwMC82MCANCiN0aGlzIGlzIGEgYnktcGVyY2VudCBicmVha2Rvd24gb2YgdGhlIGRpc3RyaWJ1dGlvbg0KcXVhbnRpbGUocmVhZF9yYXRlJHJlYWRfdGltZSwgc2VxKDAsIDEsIDAuMDEpKQ0KDQpyZWFkX3JhdGUkcmF0ZSA9IHJlYWRfcmF0ZSRjaHVua193b3Jkcy9yZWFkX3JhdGUkcmVhZF90aW1lDQpyb3VuZChxdWFudGlsZShyZWFkX3JhdGUkcmF0ZSwgc2VxKDAsIDEsIDAuMDEpKSwyKQ0Kc3VtbWFyeShyZWFkX3JhdGUkcmF0ZSkgI2JlZm9yZSB0cmltbWluZw0KI21lZGlhbiAyNTQNCmBgYA0KDQpNeSBzb2x1dGlvbiBzbyBmYXIgd2FzIHRvIHRha2UgdGhlIGh5cG90aGVzaXplZCAoYW5kIGNvbW1vbmx5IG9ic2VydmVkKSByZWFkaW5nIHJhdGUgb2YgMjgwIHdwbSBhbmQgcmVzdHJpY3QgdGhlIHJhbmdlIHRvIDIuNSBvciAxLzIuNSB0aW1lcyB0aGlzIHJhdGUuIFRoZSBtZWRpYW4gY29tZXMgb3V0IGF0IDI0NiB3cG0uIEJ1dCB0aGlzIGlzIHJlYWxseSBzb21ldGhpbmcgdG8gZGlzY3VzcyENCg0KYGBge3J9DQpyZWFkX3JhdGUgPC0gcmVhZF9yYXRlICU+JSBmaWx0ZXIocmF0ZSA+IDI4MC8yLjUgLCByYXRlIDwgMjgwICogMi41KQ0KaGlzdChyZWFkX3JhdGUkcmF0ZSwgYnJlYWtzID0gMzApDQpzdW1tYXJ5KHJlYWRfcmF0ZSRyYXRlKSAjYWZ0ZXIgdHJpbW1pbmcNCiNtZWRpYW4gMjQ2DQpgYGANCg0KDQpHaXZlbiB0aGUgdHJpbW1pbmcgYWJvdmUsIHdlIGZpbmQgdGhlIG1lYW4gcmVhZGluZyByYXRlIGJ5IHN1YmplY3QgYnkgcGFzc2FnZSBhbmQgdGhlbiBhdmVyYWdlIHRoaXMgYnkgc3ViamVjdC4gTWVkaWFuIGlzIDI2MiB3cG0uDQoNCmBgYHtyfQ0KcGFydF9wYXNzYWdlIDwtIHJlYWRfcmF0ZSAlPiUgZ3JvdXBfYnkodXNlcl9pZCwgcGFzc2FnZSkgJT4lIHN1bW1hcmlzZShtZWFuX3JhdGUgPSBtZWFuKHJhdGUpKQ0KcGFydF9yYXRlIDwtIHBhcnRfcGFzc2FnZSAlPiUgZ3JvdXBfYnkodXNlcl9pZCkgJT4lIHN1bW1hcmlzZShtZWFuX3JhdGUgPSBtZWFuKG1lYW5fcmF0ZSkpDQpzdW1tYXJ5KHBhcnRfcmF0ZSRtZWFuX3JhdGUpDQojMjYyDQpgYGANCg0KTm93IHdlIGNvbWJpbmUgcmVhZGluZyByYXRlIGFuZCByZWFkaW5nIGNvbXByZWhlbnNpb24gc2NvcmVzIGJ5IHBhc3NhZ2UuIA0KDQpgYGB7cn0NCnNjb3Jlc19wYXNzYWdlIDwtIHNjb3Jlc19wYXNzYWdlICU+JSBmaWx0ZXIodHJlYXRtZW50ID09ICJyZWFkaW5nIikNCg0KcmVhZF9wYXNzYWdlIDwtIGlubmVyX2pvaW4ocGFydF9wYXNzYWdlLCBzY29yZXNfcGFzc2FnZSkNCg0KcGFydF9yZWFkY29tcCA8LSByZWFkX3Bhc3NhZ2UgJT4lIGdyb3VwX2J5KHVzZXJfaWQpICU+JSBzdW1tYXJpc2UocmVhZGNvbXAgPSBtZWFuKG1lYW5fc2NvcmUpKQ0KYGBgDQoNCldoYXQgaXMgdGhlIGluZmx1ZW5jZSBvZiBvbmUncyByZWFkaW5nIHJhdGUgb24gdGhlaXIgcmVhZGluZyBjb21wcmVoZW5zaW9uPyBOb25lIHdoYXRzb2V2ZXIuIFRoaXMgaXMgYW5vdGhlciBpbGx1c3RyYXRpb24gb2YgaG93IHRoZXNlIHR3byBhcmUgZGlzam9pbnQgKGEgcG9pbnQgbWFkZSBpbiBLdXBlcm1hbidzIHJhbmRvbSBmb3Jlc3QgcGFwZXIgYW5kIGEgcmVjZW50IHNwZWNpYWwgaXNzdWUgb24gb25saW5lIHZzIG9mZmxpbmUgbWVhc3VyZXMgb2YgY29tcHJlaGVuc2lvbikuIEkgY291bGQgZG8gYSBiZXR0ZXIgam9iIHdpdGggc2NvcmVzICh3aGljaCBhcmUgYm91bmQgdG8gdGhlIDAtMSBzY2FsZSBub3cpIGV0YywgYnV0IEkgZG91YnQgaXQgd2lsbCBjaGFuZ2UgbXVjaC4NCg0KYGBge3J9DQpwbG90KHJlYWRfcGFzc2FnZSRtZWFuX3JhdGUsIHJlYWRfcGFzc2FnZSRtZWFuX3Njb3JlKQ0KY29yLnRlc3QocmVhZF9wYXNzYWdlJG1lYW5fc2NvcmUsIHJlYWRfcGFzc2FnZSRtZWFuX3JhdGUpDQpoaXN0KHJlYWRfcGFzc2FnZSRtZWFuX3JhdGUpDQoNCm1vZF9wYXNzYWdlID0gbG1lcihtZWFuX3Njb3JlIH4gbWVhbl9yYXRlICsgKDEgfCB1c2VyX2lkKSArICgxIHwgcGFzc2FnZSksIGRhdGEgPSByZWFkX3Bhc3NhZ2UpDQojY29yKGZpdHRlZChtb2RfcGFzc2FnZSksIHJlYWRfcGFzc2FnZSRtZWFuX3JhdGUpXjINCg0KbW9kX3Bhc3NhZ2UxID0gbG1lcihtZWFuX3Njb3JlIH4gbWVhbl9yYXRlICArICgxIHwgdXNlcl9pZCkgKyAoMSB8IHBhc3NhZ2UpLCBkYXRhID0gcmVhZF9wYXNzYWdlLCBzdWJzZXQgPSBhYnMoc2NhbGUocmVzaWQobW9kX3Bhc3NhZ2UpKSk8Mi41KQ0Kc3VtbWFyeShtb2RfcGFzc2FnZTEpDQpgYGANCg0KDQpOb3cgbGV0J3Mgc2VlIGlmIHJlYWRpbmcgYW5kIGxpc3RlbmluZyBjb21wcmVoZW5zaW9uIGNvcnJlbGF0ZSB3aXRoaW4gcGFydGljaXBhbnQuDQoNCmBgYHtyfQ0Kc2NvcmVzX2FsbCAlPiUgc2VsZWN0KHVzZXJfaWQsIHRyZWF0bWVudCwgbWVhbl9zY29yZSkgJT4lIHNwcmVhZChrZXkgPSB0cmVhdG1lbnQsIHZhbHVlID0gbWVhbl9zY29yZSkgLT4gc2NvcmVzX3dpZGUNCnJvdW5kKGNvcihzY29yZXNfd2lkZVssIC0xXSksMikNCmBgYA0KRmFpcmx5IHdlYWsgY29ycmVsYXRpb25zIGFsbCBhcm91bmQsIGVzcGVjaWFsbHkgZm9yIHRoZSB2ZXJ5IGhpZ2ggc3BlZWNoIHJhdGUgNDA1IHdwbS4NCg0KTm93IGxldCdzIGJyZWFrIHBhcnRpY2lwYW50cyBpbnRvIGdyb3VwcyBieSB0aGVpciByZWFkaW5nIGNvbXByZWhlbnNpb24gYW5kIHNlZSBpZiB0aGlzIHByZWRpY3RzIGEgZGlmZmVyZW50IGluZmxlY3Rpb24gcG9pbnQgaW4gbGlzdGVuaW5nIGNvbXByZWhlbnNpb24uIA0KDQpgYGB7cn0NCnNjb3Jlc19hbGwgJT4lIGZpbHRlcih0cmVhdG1lbnQgPT0gInJlYWRpbmciKSAtPiB0bXAxDQp0bXAyIDwtIGRhdGEuZnJhbWUodXNlcl9pZCA9IHRtcDEkdXNlcl9pZCwgcmVhZGNvbXAgPSBjdXQodG1wMSRtZWFuX3Njb3JlLCBicmVha3MgPSBxdWFudGlsZSh0bXAxJG1lYW5fc2NvcmUsIGMoMCwgMC4zMywgMC42NiwgMSkpLCBsYWJlbHMgPSBjKCJsb3ciLCAibWlkIiwgImhpZ2giKSwgaW5jbHVkZS5sb3dlc3QgPSBUKSkNCg0KI2luY2x1ZGUgcmVhZCBjb21wIGxldmVsIGludG8gcmF3IGRhdGENCnJlc3BvbnNlczEgPC0gaW5uZXJfam9pbihyZXNwb25zZXMsIHRtcDIpDQp0YWJsZShyZXNwb25zZXMxJHJlYWRjb21wKQ0KI3J1biBhIG1vZGVsIHdpdGggYW4gaW50ZXJhY3Rpb24gYnkgcmVhZCBjb21wIGxldmVsDQoNCnJlc3BvbnNlczEgJT4lIGdyb3VwX2J5KHRyZWF0bWVudCwgcmVhZGNvbXApICU+JSANCiAgc3VtbWFyaXNlKHN1bV9zY29yZSA9IHN1bShzY29yZSwgbmEucm0gPSBUKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IE5ST1coc2NvcmUpKSAlPiUgbXV0YXRlKG1lYW5fc2NvcmUgPSBzdW1fc2NvcmUvbikgLT4geA0KDQojZ2dwbG90KGFlcyh4ID0gdHJlYXRtZW50LCB5ID0gbWVhbl9zY29yZSwgY29sID0gcmVhZGNvbXApLCBkYXRhID0geCkgKyBnZW9tX3BvaW50KCkNCg0KbW9kX2NvbXBfYnJkb3duID0gZ2xtZXIoc2NvcmUgfiB0cmVhdG1lbnQgKiByZWFkY29tcCArICgxIHwgdXNlcl9pZCkgKyANCiAgKDEgfCBwYXNzYWdlKSwgZGF0YSA9IHJlc3BvbnNlczEsIHN1YnNldCA9IHRyZWF0bWVudCAhPSAicmVhZGluZyIsIGZhbWlseSA9ICJiaW5vbWlhbCIsIGNvbnRyb2w9Z2xtZXJDb250cm9sKG9wdGltaXplcj0iYm9ieXFhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wdEN0cmw9bGlzdChtYXhmdW49MmU1KSkpDQpzdW1tYXJ5KG1vZF9jb21wX2JyZG93bikNCmVmMSA9IGRhdGEuZnJhbWUoRWZmZWN0KG1vZF9jb21wX2JyZG93biwgZm9jYWwucHJlZGljdG9ycyA9IGMoInRyZWF0bWVudCIsICJyZWFkY29tcCIpKSkNCg0KZ2dwbG90KGRhdGEgPSBlZjEsIGFlcyh4ID0gdHJlYXRtZW50LCB5ID0gZml0LCBjb2wgPSByZWFkY29tcCkpKyBsYWJzKHggPSAiVHJlYXRtZW50IiwgeSA9ICJDb21wcmVoZW5zaW9uIHNjb3JlIikrIGdndGl0bGUoIkNvbXByZWhlbnNpb24gYWNjdXJhY3kiKSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49Zml0LXNlLCB5bWF4PWZpdCtzZSksIHdpZHRoPS4yLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjEpKSsNCiAgZ2VvbV9wb2ludChwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjEpLCAoYWVzKHNpemU9MS4pKSwgc2hvdy5sZWdlbmQgPUYpDQoNCmBgYA0KDQpBdCBhbGwgbGV2ZWxzIG9mIHJlYWRpbmcgY29tcHJlaGVuc2lvbiwgdGhlIGRyb3AgaXMgYmV0d2VlbiAyNzAgYW5kIDMxNSB3cG0gLSBldmVuIGlmIGl0J3Mgbm90IGFsd2F5cyBzaWduaWZpY2FudC4gVGhpcyBpcyBhIHRhZCBncmVhdGVyIHRoYW4gdGhlIHJlYWRpbmcgcmF0ZS4NCg0KDQpGaW5hbGx5LCBtb3ZpbmcgdG8gYSB2ZXJ5IGludGVyZXN0aW5nIHF1ZXN0aW9uIG9mIHdoZXRoZXIgcGVvcGxlIHdpdGggZ3JlYXRlciByZWFkaW5nIHJhdGUgdG9sZXJhdGUgaGlnaGVyIHNwZWVjaCByYXRlcyBiZXR0ZXI/DQoNCmBgYHtyfQ0KcXVhbnRpbGUocmVhZF9yYXRlJHJhdGUsIGMoMCwgMC4zMywgMC42NiwgMSkpDQoNCnRtcDIgPC0gZGF0YS5mcmFtZSh1c2VyX2lkID0gcGFydF9yYXRlJHVzZXJfaWQsIG1lYW5fcmF0ZSA9IHBhcnRfcmF0ZSRtZWFuX3JhdGUsIHJhdGVmYWN0b3IgPSBjdXQocGFydF9yYXRlJG1lYW5fcmF0ZSwgYnJlYWtzID0gcXVhbnRpbGUocGFydF9yYXRlJG1lYW5fcmF0ZSwgYygwLCAwLjMzLCAwLjY2LCAxKSksIGxhYmVscyA9IGMoImxvdyIsICJtaWQiLCAiaGlnaCIpLCBpbmNsdWRlLmxvd2VzdCA9IFQpKQ0KDQojaW5jbHVkZSByZWFkIGNvbXAgbGV2ZWwgaW50byByYXcgZGF0YQ0KcmVzcG9uc2VzMiA8LSBpbm5lcl9qb2luKHJlc3BvbnNlcywgdG1wMikNCnRhYmxlKHJlc3BvbnNlczIkcmF0ZWZhY3RvcikNCiNydW4gYSBtb2RlbCB3aXRoIGFuIGludGVyYWN0aW9uIGJ5IHJlYWQgY29tcCBsZXZlbA0KDQpyZXNwb25zZXMyICU+JSBncm91cF9ieSh0cmVhdG1lbnQsIHJhdGVmYWN0b3IpICU+JSANCiAgc3VtbWFyaXNlKHN1bV9zY29yZSA9IHN1bShzY29yZSwgbmEucm0gPSBUKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IE5ST1coc2NvcmUpKSAlPiUgbXV0YXRlKG1lYW5fc2NvcmUgPSBzdW1fc2NvcmUvbikgLT4geA0KDQojZ2dwbG90KGFlcyh4ID0gdHJlYXRtZW50LCB5ID0gbWVhbl9zY29yZSwgY29sID0gcmF0ZWZhY3RvciksIGRhdGEgPSB4KSArIGdlb21fcG9pbnQoKSArIGZhY2V0X2dyaWQofiByYXRlZmFjdG9yKQ0KDQptb2RfcmF0ZV9icmRvd24gPSBnbG1lcihzY29yZSB+IHRyZWF0bWVudCAqIHJhdGVmYWN0b3IgKyAoMSB8IHVzZXJfaWQpICsgDQogICgxIHwgcGFzc2FnZSksIGRhdGEgPSByZXNwb25zZXMyLCBmYW1pbHkgPSAiYmlub21pYWwiLCBjb250cm9sPWdsbWVyQ29udHJvbChvcHRpbWl6ZXI9ImJvYnlxYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcHRDdHJsPWxpc3QobWF4ZnVuPTJlNSkpKQ0Kc3VtbWFyeShtb2RfcmF0ZV9icmRvd24pDQplZjIgPSBkYXRhLmZyYW1lKEVmZmVjdChtb2RfcmF0ZV9icmRvd24sIGZvY2FsLnByZWRpY3RvcnMgPSBjKCJ0cmVhdG1lbnQiLCAicmF0ZWZhY3RvciIpKSkNCg0KZ2dwbG90KGRhdGEgPSBlZjIsIGFlcyh4ID0gdHJlYXRtZW50LCB5ID0gZml0LCBjb2wgPSByYXRlZmFjdG9yKSkrIGxhYnMoeCA9ICJUcmVhdG1lbnQiLCB5ID0gIkNvbXByZWhlbnNpb24gc2NvcmUiKSsgZ2d0aXRsZSgiQ29tcHJlaGVuc2lvbiBhY2N1cmFjeSIpICsNCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1maXQtc2UsIHltYXg9Zml0K3NlKSwgd2lkdGg9LjIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuMSkpKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuMSksIChhZXMoc2l6ZT0xLikpLCBzaG93LmxlZ2VuZCA9RikgKyBmYWNldF9ncmlkKH4gcmF0ZWZhY3RvcikNCg0KYGBgDQpUaGlzIGlzIHJlYWxseSBpbnRlcmVzdGluZyEgKFRoZSBwYW5lbHMgbmVlZCByZW9yZGVyaW5nKS4gV2hhdCByZWFkaW5nIHJhdGUgZG9lcyBpcyBtYW5pcHVsYXRlIHRoZSByYW5nZSBvZiBjaGFuZ2VzIGluIHRoZSBsaXN0ZW5pbmcgcmF0ZSwgZnJvbSB0aGUgc21hbGxlc3QgaW4gdGhlIGZhc3Rlc3QgcmVhZGVycyB0byB0aGUgbGFyZ2VzdCBpbiB0aGUgc2xvd2VzdCByZWFkZXJzLiBNb3JlIHNwZWNpZmljYWxseSwgZmFzdGVzdCByZWFkZXJzIGFyZSB3b3JzZSBhdCBsaXN0ZW5pbmcgY29tcHJlaGVuc2lvbiBhdCBsb3cgc3BlZWRzIGJ1dCBwcmV0dHkgY29uc2lzdGVudCAoaS5lLiwgbG9zZSBsZXNzKSBvdmVyIHRoZSBlbnRpcmUgc3BlZWNoIHJhdGUgcmFuZ2UuIFRoZXkgYXJlIHRoZSBtb3N0IHRvbGVyYW50IHRvIHZlcnkgaGlnaCBzcGVlY2ggcmF0ZXMsIGFuZCB0aGVyZSBpcyBubyBjbGVhciBkcm9wIGluIHBlcmZvcm1hbmNlIChpbmZsZWN0aW9uIHBvaW50KS4NCg0KSW4gcmVhZGVycyB3aXRoIGJvdGggdGhlIG1pZC0gYW5kIGxvdy1yZWFkaW5nIHJhdGUsIHRoZXJlIGlzIGEgY2xlYXIgZHJvcC4gUmVhZGVycyB3aXRoIGEgbWlkLXJhbmdlIHJlYWRpbmcgcmF0ZSBtYWludGFpbiB0b3AgcGVyZm9ybWFuY2UgYXQgaGlnaGVyIHNwZWVjaCByYXRlcyB0aGFuIHNsb3dlc3QgcmVhZGVycyAodXAgdG8gMzE1IHdwIHZzIDI3MCB3cG0pLiBTbyB0aGVyZSBpcyBhIGNsZWFyIGhpZXJhcmNoeTogZmFzdGVzdCByZWFkZXJzIGFyZSB0aGUgbW9zdCB0b2xlcmFudCwgZm9sbG93ZWQgYnkgbWlkLXJhbmdlLCBmb2xsb3dlZCBieSBsb3cgcmFuZ2Ugb2YgcmVhZGluZyByYXRlLiBCdXQgdGhpcyB0b2xlcmFuY2UgY29tZXMgYXQgYSBjb3N0LiBUb3AgcGVyZm9ybWFuY2UgaW4gbGlzdGVuaW5nIGNvbXByZWhlbnNpb24gaXMgYXNzb2NpYXRlZCB3aXRoIG1pZCBhbmQgbG93IHJlYWRpbmcgcmF0ZXMsIHdoaWxlIGZhc3Rlc3QgcmVhZGVycyBhcmUgbWVkaW9jcmUgaW4gdGhpcyB0YXNrLg0K