R notebook for analysis of questionnaires in the sacc-tDCS dataset.

# Load some libraries
library(here) # file paths
here() starts at /Volumes/research$/reteig/sacc-tDCS
library(tidyverse) # importing, transforming, and visualizing data frames
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages ---------------------------------------------------------------------------------
filter(): dplyr, stats
lag():    dplyr, stats
library(ez) # ANOVA
library(knitr) # R markdown output (html, pdf, etc.)
# set default output and figure options
knitr::opts_chunk$set(message = FALSE, warning = FALSE, fig.width = 7, fig.asp = 0.618, out.width = "75%", fig.align = "center")
sessionInfo()
R version 3.4.0 (2017-04-21)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: OS X El Capitan 10.11.6

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRlapack.dylib

locale:
[1] C

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base

other attached packages:
 [1] knitr_1.15.1    ez_4.4-0        dplyr_0.5.0     purrr_0.2.2     readr_1.1.0     tidyr_0.6.1
 [7] tibble_1.3.0    ggplot2_2.2.1   tidyverse_1.1.1 here_0.1

loaded via a namespace (and not attached):
 [1] reshape2_1.4.2     splines_3.4.0      haven_1.0.0        lattice_0.20-35    colorspace_1.3-2
 [6] htmltools_0.3.6    yaml_2.1.14        mgcv_1.8-17        base64enc_0.1-3    nloptr_1.0.4
[11] foreign_0.8-67     DBI_0.6-1          modelr_0.1.0       readxl_1.0.0       plyr_1.8.4
[16] stringr_1.2.0      MatrixModels_0.4-1 munsell_0.4.3      gtable_0.2.0       cellranger_1.1.0
[21] rvest_0.3.2        psych_1.7.3.21     evaluate_0.10      forcats_0.2.0      SparseM_1.77
[26] quantreg_5.33      pbkrtest_0.4-7     parallel_3.4.0     broom_0.4.2        Rcpp_0.12.10
[31] scales_0.4.1       backports_1.0.5    jsonlite_1.4       lme4_1.1-13        mnormt_1.5-5
[36] hms_0.3            digest_0.6.12      stringi_1.1.5      grid_3.4.0         rprojroot_1.2
[41] tools_3.4.0        magrittr_1.5       lazyeval_0.2.0     car_2.1-4          MASS_7.3-47
[46] Matrix_1.2-9       xml2_1.1.1         lubridate_1.6.0    assertthat_0.2.0   minqa_1.2.4
[51] rmarkdown_1.5      httr_1.2.1         R6_2.2.0           nnet_7.3-12        nlme_3.1-131
[56] compiler_3.4.0    

PANAS

Load data

In each session of the experiment, participants completed the Positive and Negative Affect Scale (PANAS) twice:

  1. Pre-measurement: Before starting setup of the tDCS and eye tracker
  2. Post-measurement: After the electrodes were removed following task completion

As most participants were native Dutch speakers, we also used a Dutch language version of the PANAS, as reported by Engelen et al. (2006).

# Load the data frame
dataFile <- here("data", "PANAS.csv")
panasData <- read_csv2(dataFile, col_types = cols( # read in data; make columns into factors
  session = col_factor(c("first","second")),
  stimulation = col_factor(c("anodal","cathodal")),
  time = col_factor(c("pre","post"))
))
kable(head(panasData)) # show data frame
subject session stimulation time pos.1.interested neg.2.distressed pos.3.excited neg.4.upset pos.5.strong neg.6.guilty neg.7.scared neg.8.hostile pos.9.enthusiastic pos.10.proud neg.11.irritable pos.12alert neg.13.ashamed pos.14.inspired neg.15.nervous pos.16.determined pos.17.attentive neg.18.jittery pos.19.active neg.20.afraid
S01 first cathodal pre 2 1 3 1 2 1 1 1 2 2 1 1 1 2 1 1 2 1 2 1
S01 first cathodal post 2 1 3 1 2 1 1 1 2 2 1 1 1 1 1 2 1 1 3 1
S01 second anodal pre 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 2 1
S01 second anodal post 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
S02 first anodal pre 4 1 4 1 3 1 1 1 3 3 1 2 1 3 1 4 4 1 3 1
S02 first anodal post 4 1 1 1 4 1 1 1 3 4 1 4 1 3 1 4 4 1 4 1

Factors:

  • subject: subject ID (S01, S02, etc)
  • session: Whether data are from the first or second session
  • stimulation: Whether data are from the anodal or cathodal session
  • time: Whether data are from the pre or post measurement

DATA:

The PANAS contains 20 items, split equally into positive and negative affect. Each item is rated on a Likert scale:

  1. very slightly or not at all
  2. a little
  3. moderately
  4. quite a bit
  5. extremely

To obtain a positive affect score, we sum items: 1 (interested), 3 (excited), 5 (strong), 9 (enthusiastic), 10 (proud), 12 (alert), 14 (inspired), 16 (determined), 17 (attentive) and 19 (active).

To obtain a negative affect score, we sum items: 2 (distressed), 4 (upset), 6 (guilty), 7 (scared), 8 (hostile), 11 (irritable), 13 (ashamed), 15 (nervous), 18 (jittery) and 20 (afraid).

Post-pre differences per item

panasPrePost <- panasData %>%
  group_by(subject,session,stimulation) %>% # for each subject, session, stimulation combination
  select(-time) %>% # don't do subtraction for this column
  summarise_each(funs(diff(.))) # subtract pre and post

Let’s plot the post-pre differences for each item, split for stimulation session.

panasPrePost %>%
  gather(item, score, pos.1.interested:neg.20.afraid) %>% # gather all PANAS columns so it can be used as a factor
  ggplot(aes(item, score)) +
      stat_summary(fun.y = mean, geom = "point", position = position_dodge(width = 0.6), aes(color = stimulation)) +
      geom_hline(yintercept = 0, linetype = "dashed") +
      coord_cartesian(ylim = c(-1,1)) +
      theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

Most scores become negative, meaning the post score was smaller than pre. So they generally feel “less” of everything. This seems particularly the case for positive affect (except for determined).

For negative items, people become especially less jittery, nervous, irritable. The biggest decreases in positive scores are in the attentive, active, and enthusiastic items.

The biggest differences between the anodal and cathodal sessions seem to be in the positive items (except for irritable).

For some positive items, anodal stimulation results in a more positive score, while cathodal results in a more negative score: (determined and insprired).

Composite positive/negative scores

panasComposite <- panasData %>% 
  mutate(positive = rowSums(select(., contains("pos")))) %>% # sum all the positive scores together
  mutate(negative = rowSums(select(., contains("neg")))) %>% # sum all the negative scores together
  select(subject:time, positive, negative) %>% # drop the original PANAS columns
  gather(affect, score, positive, negative) # gather affect so it can be used as a factor
ggplot(panasComposite, aes(time,score)) +
facet_wrap(~affect) +
  geom_point(position = position_jitter(width = 0.2), aes(color = stimulation)) +
  stat_summary(fun.data = mean_cl_normal, position = position_dodge(width = .5), shape = 21, size = 1, aes(fill = stimulation), color = "black")

The negative scores are much lower than the positive overall.

The pre to post differences are larger for positive scores, particularly for the cathodal session.

Statistics

Positive scores

Repeated measures ANOVA with factors STIMULATION (anodal vs. cathodal) and TIME (pre vs. post)

# To run the ANOVA
panasCompositeStats <- panasComposite %>%
  group_by(subject) %>% # exclude all subjects with missing cases
  filter(sum(is.na(score)) == 0) %>%
  ungroup() %>%
  mutate(subject = factor(subject)) # "subject" must be factorized
modelPositive <- ezANOVA(data = data.frame(filter(panasCompositeStats, affect == "positive")), # Repeated over subjects; type 3 sums of squares (cf. SPSS)
                         dv = .(score), wid = .(subject), within = .(stimulation, time), type = 3)
kable(modelPositive$ANOVA)
Effect DFn DFd F p p<.05 ges
2 stimulation 1 29 0.3362518 0.5664791 0.0016411
3 time 1 29 20.9326283 0.0000825 * 0.0662793
4 stimulation:time 1 29 4.6731175 0.0390292 * 0.0070037

This confirms our observation that subjects’ mood becomes less positive after stimulation, particularly in the cathodal session

Negative scores

Repeated measures ANOVA with factors STIMULATION (anodal vs. cathodal) and TIME (pre vs. post)

modelNegative <- ezANOVA(data = data.frame(filter(panasCompositeStats, affect == "negative")), # Repeated over subjects; type 3 sums of squares (cf. SPSS)
                        dv = .(score), wid = .(subject), within = .(stimulation, time), type = 3)
kable(modelNegative$ANOVA)
Effect DFn DFd F p p<.05 ges
2 stimulation 1 29 0.0986395 0.7557167 0.0003892
3 time 1 29 22.4236431 0.0000529 * 0.0677905
4 stimulation:time 1 29 1.3206320 0.2598687 0.0021153

Subjects’ mood also become less negative, but this time there is no interaction with stimulation condition, so this is likely a pure effect of time.

tDCS sensations

After each session, subjects also completed a custom questionnaire probing tDCS sensations.

Load data

# Load the data frame
dataFile <- here("data", "tdcs_sensations.csv")
sensData <- read_csv2(dataFile, col_types = cols(
  session = col_factor(c("first","second")),
  stimulation = col_factor(c("anodal","cathodal"))
))
kable(head(sensData)) # show data frame
subject session stimulation itching tingling burning pain headache fatigue dizziness nausea conf.itching conf.tingling conf.burning conf.pain conf.headache conf.fatigue conf.dizziness conf.nausea felt.more
S01 first cathodal 2 4 4 2 0 0 0 0 3 4 4 4 0 0 0 0 cathode
S01 second anodal 1 1 1 0 0 1 0 0 4 4 4 0 0 1 0 0 anode
S02 first anodal 1 2 2 0 0 1 0 0 4 4 4 0 0 1 0 0 anode
S02 second cathodal 1 2 2 0 0 0 0 0 4 4 4 0 0 0 0 0 equal
S03 first cathodal 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 equal
S03 second anodal 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 equal

They were asked to which degree the following sensations were present during stimulation: tingling, itching sensation, burning sensation, pain, headache, fatigue, dizziness and nausea. Each was rated on a scale from 0-4:

  1. none
  2. a little
  3. moderate
  4. strong
  5. very strong

They also rated their confidence that the sensations were caused by the stimulation on a scale from 0-4 (columns starting with conf.):

  1. n/a (meaning they rated the sensation a 0 on the previous scale)
  2. unlikely
  3. possibly
  4. likely
  5. very likely

Finally, they filled in whether they felt one electrode more than the other (felt.more); and if so, which and for which sensations.

Factors:

  • subject: subject ID (S01, S02, etc)
  • session: Whether data are from the first or second session
  • stimulation: Whether data are from the anodal or cathodal session
idxComplete <- rowSums(is.na(sensData)) != ncol(sensData) - 3 # rows that do not have all NAs (except the 3 factor columns)
# calculate number of questionnaires completed per stimulation type
nAnodal <- sum(sensData$stimulation == "anodal" & idxComplete)
nCathodal <- sum(sensData$stimulation == "cathodal" & idxComplete)

Plot distributions

Sensation intensity

sensData %>%
  select(everything(), -contains("conf"), -felt.more) %>% # drop the confidence columns
  gather(sensation, rating, itching:nausea) %>% # gather sensations so they can be used as a factor
  ggplot(aes(rating, fill = stimulation)) +
    facet_wrap(~sensation, nrow = 2) +
    geom_histogram(position = "stack", binwidth = .5) +
    stat_bin(binwidth = 1, geom = "text", aes(label = ..count..), position = position_stack(vjust = 0.5)) +
    xlim(0.5,4.5) + ylim(0,30) + # exclude "0" ratings that were not present
    ggtitle(paste("Sensations over", nAnodal, "anodal sessions,", nCathodal, "cathodal sessions"))

Nausea was never experienced; dizziness is especially rare and mild, as are headache and pain.

Burning, itching and tingling are most frequently experienced and to a higher degree.

Sensation confidence

sensData %>%
  select(contains("conf"), subject, session, stimulation) %>% # drop the rating columns
  gather(sensation, rating, conf.itching:conf.nausea) %>%
  ggplot(aes(rating, fill = stimulation)) +
    facet_wrap(~sensation, nrow = 2) +
    geom_histogram(position = "stack", binwidth = .5) +
    stat_bin(binwidth = 1, geom = "text", aes(label = ..count..), position = position_stack(vjust = 0.5)) +
    xlim(0.5,4.5) +  ylim(0,30) + # exclude "0" ratings that were not present
    ggtitle(paste("Confidence over", nAnodal, "anodal sessions,", nCathodal, "cathodal sessions"))

For “local” sensations like burning, tingling and dizziness, subjects have high confidence that these are due to tDCS. For more diffuse sensations, like fatigue and headache, ratings are very low, so their occurence might just be due to performing the task for an extended period of time.

Note that nausea was never reported, so the two “1” confidence ratings are techincally non-sensical.

Sensation difference between anode and cathode

sensData %>%
  select(felt.more, subject, session, stimulation) %>% # keep only the relevant column
  mutate(felt.more = factor(felt.more, levels = c("anode", "equal", "cathode"))) %>% # keep colors consistent
  ggplot(aes(stimulation, fill = felt.more)) +
    geom_bar() +
    stat_count(geom = "text", aes(label = ..count..), position = position_stack(vjust = 0.5), color = "white")

There’s a pretty even split between which electrode people feel the most, so there don’t appear to be worrisome biases.

During anodal stimulation, the anode is felt more than the cathode; during cathodal stimulation the cathode is felt more than the anode. In other words, the electrode over the FEF is always felt more than the forehead electrode. However, this pattern is more pronounced in the anodal session.

About a third of people in the cathodal session did not indicate they felt one more than the other; this is slightly less in the anodal session.

Statistics

We will test for every sensation separately whether there was a difference between the anodal and cathodal sessions. Mann-Whitney-U tests are most appropriate here, as the data are ordinal (Likert) and do not look normally distributed.

sensationList <- c("itching", "tingling", "burning", "pain", "headache", "fatigue", "dizziness", "nausea")
senseTests <- data.frame(sensation = sensationList, p.value = NA) # initialize results data frame
for (item in sensationList) {
  testData <- sensData[[item]] # extract column with test dat
  tmp <- wilcox.test(testData[sensData$stimulation == "anodal"], testData[sensData$stimulation == "cathodal"])
  senseTests$p.value[senseTests$sensation %in% item] <- tmp$p.value # put p-value in row of results data frame
}
kable(senseTests)
sensation p.value
itching 0.5787360
tingling 0.3652154
burning 0.1913439
pain 0.9287263
headache 0.9648458
fatigue 0.4330014
dizziness 0.7203021
nausea NaN

None of the differences are significant. The p-value for nausea is undefined because it was rated 0 by everyone for both anodal and cathodal.

LS0tCnRpdGxlOiAic2FjYy10RENTOiBRdWVzdGlvbm5haXJlcyIKYXV0aG9yOiAiTGVvbiBSZXRlaWciCm91dHB1dDoKICBnaXRodWJfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogIGh0bWxfbm90ZWJvb2s6CiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKLS0tCgpSIG5vdGVib29rIGZvciBhbmFseXNpcyBvZiBxdWVzdGlvbm5haXJlcyBpbiB0aGUgYHNhY2MtdERDU2AgZGF0YXNldC4KCmBgYHtyIHNldHVwfQojIExvYWQgc29tZSBsaWJyYXJpZXMKbGlicmFyeShoZXJlKSAjIGZpbGUgcGF0aHMKbGlicmFyeSh0aWR5dmVyc2UpICMgaW1wb3J0aW5nLCB0cmFuc2Zvcm1pbmcsIGFuZCB2aXN1YWxpemluZyBkYXRhIGZyYW1lcwpsaWJyYXJ5KGV6KSAjIEFOT1ZBCmxpYnJhcnkoa25pdHIpICMgUiBtYXJrZG93biBvdXRwdXQgKGh0bWwsIHBkZiwgZXRjLikKIyBzZXQgZGVmYXVsdCBvdXRwdXQgYW5kIGZpZ3VyZSBvcHRpb25zCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoID0gNywgZmlnLmFzcCA9IDAuNjE4LCBvdXQud2lkdGggPSAiNzUlIiwgZmlnLmFsaWduID0gImNlbnRlciIpCgpzZXNzaW9uSW5mbygpCmBgYAoKIyBQQU5BUwoKIyMgTG9hZCBkYXRhCgpJbiBlYWNoIHNlc3Npb24gb2YgdGhlIGV4cGVyaW1lbnQsIHBhcnRpY2lwYW50cyBjb21wbGV0ZWQgdGhlIFtQb3NpdGl2ZSBhbmQgTmVnYXRpdmUgQWZmZWN0IFNjYWxlIChQQU5BUyldKGh0dHA6Ly9ib29rc2l0ZS5lbHNldmllci5jb20vOTc4MDEyMzc0NTE3MC9DaGFwdGVyJTIwMy9DaGFwdGVyXzNfV29ya3NoZWV0XzMuMS5wZGYpIHR3aWNlOgoKMS4gX19QcmUtbWVhc3VyZW1lbnRfXzogQmVmb3JlIHN0YXJ0aW5nIHNldHVwIG9mIHRoZSB0RENTIGFuZCBleWUgdHJhY2tlcgoyLiBfX1Bvc3QtbWVhc3VyZW1lbnRfXzogQWZ0ZXIgdGhlIGVsZWN0cm9kZXMgd2VyZSByZW1vdmVkIGZvbGxvd2luZyB0YXNrIGNvbXBsZXRpb24KCkFzIG1vc3QgcGFydGljaXBhbnRzIHdlcmUgbmF0aXZlIER1dGNoIHNwZWFrZXJzLCB3ZSBhbHNvIHVzZWQgYSBbRHV0Y2ggbGFuZ3VhZ2UgdmVyc2lvbl0oaHR0cDovL3d3dy5la2dwLnVnZW50LmJlL3BhZ2VzL25sL3ZyYWdlbmxpanN0ZW4vUEFOQVMucGRmKSBvZiB0aGUgUEFOQVMsIGFzIHJlcG9ydGVkIGJ5IFtFbmdlbGVuIGV0IGFsLiAoMjAwNildKGh0dHA6Ly9saW5rLnNwcmluZ2VyLmNvbS9hcnRpY2xlLzEwLjEwMDcvQkYwMzA4Nzk3OSkuCgpgYGB7ciBMb2FkIFBBTkFTIGRhdGF9CiMgTG9hZCB0aGUgZGF0YSBmcmFtZQpkYXRhRmlsZSA8LSBoZXJlKCJkYXRhIiwgIlBBTkFTLmNzdiIpCnBhbmFzRGF0YSA8LSByZWFkX2NzdjIoZGF0YUZpbGUsIGNvbF90eXBlcyA9IGNvbHMoICMgcmVhZCBpbiBkYXRhOyBtYWtlIGNvbHVtbnMgaW50byBmYWN0b3JzCiAgc2Vzc2lvbiA9IGNvbF9mYWN0b3IoYygiZmlyc3QiLCJzZWNvbmQiKSksCiAgc3RpbXVsYXRpb24gPSBjb2xfZmFjdG9yKGMoImFub2RhbCIsImNhdGhvZGFsIikpLAogIHRpbWUgPSBjb2xfZmFjdG9yKGMoInByZSIsInBvc3QiKSkKKSkKa2FibGUoaGVhZChwYW5hc0RhdGEpKSAjIHNob3cgZGF0YSBmcmFtZQpgYGAKCl9fRmFjdG9yc19fOgoKKiBfc3ViamVjdF86IHN1YmplY3QgSUQgKGBTMDFgLCBgUzAyYCwgZXRjKQoqIF9zZXNzaW9uXzogV2hldGhlciBkYXRhIGFyZSBmcm9tIHRoZSBgZmlyc3RgIG9yIGBzZWNvbmRgIHNlc3Npb24KKiBfc3RpbXVsYXRpb25fOiBXaGV0aGVyIGRhdGEgYXJlIGZyb20gdGhlIGBhbm9kYWxgIG9yIGBjYXRob2RhbGAgc2Vzc2lvbgoqIF90aW1lXzogV2hldGhlciBkYXRhIGFyZSBmcm9tIHRoZSBgcHJlYCBvciBgcG9zdGAgbWVhc3VyZW1lbnQKCl9fREFUQV9fOgoKVGhlIFBBTkFTIGNvbnRhaW5zIDIwIGl0ZW1zLCBzcGxpdCBlcXVhbGx5IGludG8gcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGFmZmVjdC4gRWFjaCBpdGVtIGlzIHJhdGVkIG9uIGEgTGlrZXJ0IHNjYWxlOgoKMS4gdmVyeSBzbGlnaHRseSBvciBub3QgYXQgYWxsCjIuIGEgbGl0dGxlCjMuIG1vZGVyYXRlbHkKNC4gcXVpdGUgYSBiaXQKNS4gZXh0cmVtZWx5CgpUbyBvYnRhaW4gYSBwb3NpdGl2ZSBhZmZlY3Qgc2NvcmUsIHdlIHN1bSBpdGVtczogMSAoaW50ZXJlc3RlZCksIDMgKGV4Y2l0ZWQpLCA1IChzdHJvbmcpLCA5IChlbnRodXNpYXN0aWMpLCAxMCAocHJvdWQpLCAxMiAoYWxlcnQpLCAxNCAoaW5zcGlyZWQpLCAxNiAoZGV0ZXJtaW5lZCksIDE3IChhdHRlbnRpdmUpIGFuZCAxOSAoYWN0aXZlKS4KClRvIG9idGFpbiBhIG5lZ2F0aXZlIGFmZmVjdCBzY29yZSwgd2Ugc3VtIGl0ZW1zOiAyIChkaXN0cmVzc2VkKSwgNCAodXBzZXQpLCA2IChndWlsdHkpLCA3IChzY2FyZWQpLCA4IChob3N0aWxlKSwgMTEgKGlycml0YWJsZSksIDEzIChhc2hhbWVkKSwgMTUgKG5lcnZvdXMpLCAxOCAoaml0dGVyeSkgYW5kIDIwIChhZnJhaWQpLgoKIyMgUG9zdC1wcmUgZGlmZmVyZW5jZXMgcGVyIGl0ZW0KCmBgYHtyIFN1YnRyYWN0IHByZSBmcm9tIHBvc3QgZm9yIGVhY2ggaXRlbX0KcGFuYXNQcmVQb3N0IDwtIHBhbmFzRGF0YSAlPiUKICBncm91cF9ieShzdWJqZWN0LHNlc3Npb24sc3RpbXVsYXRpb24pICU+JSAjIGZvciBlYWNoIHN1YmplY3QsIHNlc3Npb24sIHN0aW11bGF0aW9uIGNvbWJpbmF0aW9uCiAgc2VsZWN0KC10aW1lKSAlPiUgIyBkb24ndCBkbyBzdWJ0cmFjdGlvbiBmb3IgdGhpcyBjb2x1bW4KICBzdW1tYXJpc2VfZWFjaChmdW5zKGRpZmYoLikpKSAjIHN1YnRyYWN0IHByZSBhbmQgcG9zdApgYGAKCkxldCdzIHBsb3QgdGhlIHBvc3QtcHJlIGRpZmZlcmVuY2VzIGZvciBlYWNoIGl0ZW0sIHNwbGl0IGZvciBzdGltdWxhdGlvbiBzZXNzaW9uLgoKYGBge3IgUGxvdCBwb3N0LXByZSBkaWZmZXJlbmNlcyBwZXIgaXRlbX0KcGFuYXNQcmVQb3N0ICU+JQogIGdhdGhlcihpdGVtLCBzY29yZSwgcG9zLjEuaW50ZXJlc3RlZDpuZWcuMjAuYWZyYWlkKSAlPiUgIyBnYXRoZXIgYWxsIFBBTkFTIGNvbHVtbnMgc28gaXQgY2FuIGJlIHVzZWQgYXMgYSBmYWN0b3IKICBnZ3Bsb3QoYWVzKGl0ZW0sIHNjb3JlKSkgKwogICAgICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tID0gInBvaW50IiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNiksIGFlcyhjb2xvciA9IHN0aW11bGF0aW9uKSkgKwogICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgICAgIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygtMSwxKSkgKwogICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSkKYGBgCgpNb3N0IHNjb3JlcyBiZWNvbWUgbmVnYXRpdmUsIG1lYW5pbmcgdGhlIGBwb3N0YCBzY29yZSB3YXMgc21hbGxlciB0aGFuIGBwcmVgLiBTbyB0aGV5IGdlbmVyYWxseSBmZWVsICJsZXNzIiBvZiBldmVyeXRoaW5nLiBUaGlzIHNlZW1zIHBhcnRpY3VsYXJseSB0aGUgY2FzZSBmb3IgcG9zaXRpdmUgYWZmZWN0IChleGNlcHQgZm9yIF9kZXRlcm1pbmVkXykuCgpGb3IgbmVnYXRpdmUgaXRlbXMsIHBlb3BsZSBiZWNvbWUgZXNwZWNpYWxseSBsZXNzIF9qaXR0ZXJ5XywgX25lcnZvdXNfLCBfaXJyaXRhYmxlXy4gVGhlIGJpZ2dlc3QgZGVjcmVhc2VzIGluIHBvc2l0aXZlIHNjb3JlcyBhcmUgaW4gdGhlIF9hdHRlbnRpdmVfLCBfYWN0aXZlXywgYW5kIF9lbnRodXNpYXN0aWNfIGl0ZW1zLgoKVGhlIGJpZ2dlc3QgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgYW5vZGFsIGFuZCBjYXRob2RhbCBzZXNzaW9ucyBzZWVtIHRvIGJlIGluIHRoZSBwb3NpdGl2ZSBpdGVtcyAoZXhjZXB0IGZvciBfaXJyaXRhYmxlXykuCgpGb3Igc29tZSBwb3NpdGl2ZSBpdGVtcywgYW5vZGFsIHN0aW11bGF0aW9uIHJlc3VsdHMgaW4gYSBtb3JlIHBvc2l0aXZlIHNjb3JlLCB3aGlsZSBjYXRob2RhbCByZXN1bHRzIGluIGEgbW9yZSBuZWdhdGl2ZSBzY29yZTogKF9kZXRlcm1pbmVkXyBhbmQgX2luc3ByaXJlZF8pLgoKIyMgQ29tcG9zaXRlIHBvc2l0aXZlL25lZ2F0aXZlIHNjb3JlcwoKYGBge3IgU3VtIHRoZSBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgaXRlbXN9CnBhbmFzQ29tcG9zaXRlIDwtIHBhbmFzRGF0YSAlPiUgCiAgbXV0YXRlKHBvc2l0aXZlID0gcm93U3VtcyhzZWxlY3QoLiwgY29udGFpbnMoInBvcyIpKSkpICU+JSAjIHN1bSBhbGwgdGhlIHBvc2l0aXZlIHNjb3JlcyB0b2dldGhlcgogIG11dGF0ZShuZWdhdGl2ZSA9IHJvd1N1bXMoc2VsZWN0KC4sIGNvbnRhaW5zKCJuZWciKSkpKSAlPiUgIyBzdW0gYWxsIHRoZSBuZWdhdGl2ZSBzY29yZXMgdG9nZXRoZXIKICBzZWxlY3Qoc3ViamVjdDp0aW1lLCBwb3NpdGl2ZSwgbmVnYXRpdmUpICU+JSAjIGRyb3AgdGhlIG9yaWdpbmFsIFBBTkFTIGNvbHVtbnMKICBnYXRoZXIoYWZmZWN0LCBzY29yZSwgcG9zaXRpdmUsIG5lZ2F0aXZlKSAjIGdhdGhlciBhZmZlY3Qgc28gaXQgY2FuIGJlIHVzZWQgYXMgYSBmYWN0b3IKYGBgCgpgYGB7ciBQbG90IHRoZSBjb21wb3NpdGUgc2NvcmVzfQpnZ3Bsb3QocGFuYXNDb21wb3NpdGUsIGFlcyh0aW1lLHNjb3JlKSkgKwpmYWNldF93cmFwKH5hZmZlY3QpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4yKSwgYWVzKGNvbG9yID0gc3RpbXVsYXRpb24pKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9jbF9ub3JtYWwsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAuNSksIHNoYXBlID0gMjEsIHNpemUgPSAxLCBhZXMoZmlsbCA9IHN0aW11bGF0aW9uKSwgY29sb3IgPSAiYmxhY2siKQpgYGAKClRoZSBuZWdhdGl2ZSBzY29yZXMgYXJlIG11Y2ggbG93ZXIgdGhhbiB0aGUgcG9zaXRpdmUgb3ZlcmFsbC4KClRoZSBwcmUgdG8gcG9zdCBkaWZmZXJlbmNlcyBhcmUgbGFyZ2VyIGZvciBwb3NpdGl2ZSBzY29yZXMsIHBhcnRpY3VsYXJseSBmb3IgdGhlIGNhdGhvZGFsIHNlc3Npb24uCgojIyBTdGF0aXN0aWNzCgojIyMgUG9zaXRpdmUgc2NvcmVzCgpSZXBlYXRlZCBtZWFzdXJlcyBBTk9WQSB3aXRoIGZhY3RvcnMgU1RJTVVMQVRJT04gKGFub2RhbCB2cy4gY2F0aG9kYWwpIGFuZCBUSU1FIChwcmUgdnMuIHBvc3QpCgpgYGB7ciBQcmVwYXJlIGRhdGEgZnJhbWUgZm9yIHN0YXRpc3RpY3N9CiMgVG8gcnVuIHRoZSBBTk9WQQpwYW5hc0NvbXBvc2l0ZVN0YXRzIDwtIHBhbmFzQ29tcG9zaXRlICU+JQogIGdyb3VwX2J5KHN1YmplY3QpICU+JSAjIGV4Y2x1ZGUgYWxsIHN1YmplY3RzIHdpdGggbWlzc2luZyBjYXNlcwogIGZpbHRlcihzdW0oaXMubmEoc2NvcmUpKSA9PSAwKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHN1YmplY3QgPSBmYWN0b3Ioc3ViamVjdCkpICMgInN1YmplY3QiIG11c3QgYmUgZmFjdG9yaXplZApgYGAKCmBgYHtyIFJNIEFOT1ZBIGZvciBQb3NpdGl2ZSBzY29yZXMsIHJlc3VsdHM9J2FzaXMnfQptb2RlbFBvc2l0aXZlIDwtIGV6QU5PVkEoZGF0YSA9IGRhdGEuZnJhbWUoZmlsdGVyKHBhbmFzQ29tcG9zaXRlU3RhdHMsIGFmZmVjdCA9PSAicG9zaXRpdmUiKSksICMgUmVwZWF0ZWQgb3ZlciBzdWJqZWN0czsgdHlwZSAzIHN1bXMgb2Ygc3F1YXJlcyAoY2YuIFNQU1MpCiAgICAgICAgICAgICAgICAgICAgICAgICBkdiA9IC4oc2NvcmUpLCB3aWQgPSAuKHN1YmplY3QpLCB3aXRoaW4gPSAuKHN0aW11bGF0aW9uLCB0aW1lKSwgdHlwZSA9IDMpCmthYmxlKG1vZGVsUG9zaXRpdmUkQU5PVkEpCmBgYAoKVGhpcyBjb25maXJtcyBvdXIgb2JzZXJ2YXRpb24gdGhhdCBzdWJqZWN0cycgbW9vZCBiZWNvbWVzIGxlc3MgcG9zaXRpdmUgYWZ0ZXIgc3RpbXVsYXRpb24sIHBhcnRpY3VsYXJseSBpbiB0aGUgY2F0aG9kYWwgc2Vzc2lvbgoKIyMjIE5lZ2F0aXZlIHNjb3JlcwoKUmVwZWF0ZWQgbWVhc3VyZXMgQU5PVkEgd2l0aCBmYWN0b3JzIFNUSU1VTEFUSU9OIChhbm9kYWwgdnMuIGNhdGhvZGFsKSBhbmQgVElNRSAocHJlIHZzLiBwb3N0KQoKYGBge3IgUk0gQU5PVkEgZm9yIG5lZ2F0aXZlIHNjb3Jlc30KbW9kZWxOZWdhdGl2ZSA8LSBlekFOT1ZBKGRhdGEgPSBkYXRhLmZyYW1lKGZpbHRlcihwYW5hc0NvbXBvc2l0ZVN0YXRzLCBhZmZlY3QgPT0gIm5lZ2F0aXZlIikpLCAjIFJlcGVhdGVkIG92ZXIgc3ViamVjdHM7IHR5cGUgMyBzdW1zIG9mIHNxdWFyZXMgKGNmLiBTUFNTKQogICAgICAgICAgICAgICAgICAgICAgICBkdiA9IC4oc2NvcmUpLCB3aWQgPSAuKHN1YmplY3QpLCB3aXRoaW4gPSAuKHN0aW11bGF0aW9uLCB0aW1lKSwgdHlwZSA9IDMpCmthYmxlKG1vZGVsTmVnYXRpdmUkQU5PVkEpCmBgYAoKU3ViamVjdHMnIG1vb2QgYWxzbyBiZWNvbWUgbGVzcyBuZWdhdGl2ZSwgYnV0IHRoaXMgdGltZSB0aGVyZSBpcyBubyBpbnRlcmFjdGlvbiB3aXRoIHN0aW11bGF0aW9uIGNvbmRpdGlvbiwgc28gdGhpcyBpcyBsaWtlbHkgYSBwdXJlIGVmZmVjdCBvZiB0aW1lLgoKIyB0RENTIHNlbnNhdGlvbnMKCkFmdGVyIGVhY2ggc2Vzc2lvbiwgc3ViamVjdHMgYWxzbyBjb21wbGV0ZWQgYSBjdXN0b20gcXVlc3Rpb25uYWlyZSBwcm9iaW5nIHREQ1Mgc2Vuc2F0aW9ucy4KCiMjIExvYWQgZGF0YQoKYGBge3IgTG9hZCB0RENTIHNlbnNhdGlvbnMgZGF0YX0KIyBMb2FkIHRoZSBkYXRhIGZyYW1lCmRhdGFGaWxlIDwtIGhlcmUoImRhdGEiLCAidGRjc19zZW5zYXRpb25zLmNzdiIpCnNlbnNEYXRhIDwtIHJlYWRfY3N2MihkYXRhRmlsZSwgY29sX3R5cGVzID0gY29scygKICBzZXNzaW9uID0gY29sX2ZhY3RvcihjKCJmaXJzdCIsInNlY29uZCIpKSwKICBzdGltdWxhdGlvbiA9IGNvbF9mYWN0b3IoYygiYW5vZGFsIiwiY2F0aG9kYWwiKSkKKSkKa2FibGUoaGVhZChzZW5zRGF0YSkpICMgc2hvdyBkYXRhIGZyYW1lCmBgYAoKVGhleSB3ZXJlIGFza2VkIHRvIHdoaWNoIGRlZ3JlZSB0aGUgZm9sbG93aW5nIHNlbnNhdGlvbnMgd2VyZSBwcmVzZW50IGR1cmluZyBzdGltdWxhdGlvbjogX3RpbmdsaW5nXywgX2l0Y2hpbmcgc2Vuc2F0aW9uXywgX2J1cm5pbmcgc2Vuc2F0aW9uXywgX3BhaW5fLCBfaGVhZGFjaGVfLCBfZmF0aWd1ZV8sIF9kaXp6aW5lc3NfIGFuZCBfbmF1c2VhXy4gRWFjaCB3YXMgcmF0ZWQgb24gYSBzY2FsZSBmcm9tIDAtNDoKCjAuIG5vbmUKMS4gYSBsaXR0bGUKMi4gbW9kZXJhdGUKMy4gc3Ryb25nCjQuIHZlcnkgc3Ryb25nCgpUaGV5IGFsc28gcmF0ZWQgdGhlaXIgY29uZmlkZW5jZSBfdGhhdCB0aGUgc2Vuc2F0aW9ucyB3ZXJlIGNhdXNlZCBieSB0aGUgc3RpbXVsYXRpb25fIG9uIGEgc2NhbGUgZnJvbSAwLTQgKGNvbHVtbnMgc3RhcnRpbmcgd2l0aCBgY29uZi5gKToKCjAuIG4vYSAobWVhbmluZyB0aGV5IHJhdGVkIHRoZSBzZW5zYXRpb24gYSAwIG9uIHRoZSBwcmV2aW91cyBzY2FsZSkKMS4gdW5saWtlbHkKMi4gcG9zc2libHkKMy4gbGlrZWx5CjQuIHZlcnkgbGlrZWx5CgpGaW5hbGx5LCB0aGV5IGZpbGxlZCBpbiB3aGV0aGVyIHRoZXkgZmVsdCBvbmUgZWxlY3Ryb2RlIG1vcmUgdGhhbiB0aGUgb3RoZXIgKGBmZWx0Lm1vcmVgKTsgYW5kIGlmIHNvLCB3aGljaCBhbmQgZm9yIHdoaWNoIHNlbnNhdGlvbnMuCgpfX0ZhY3RvcnNfXzoKCiogX3N1YmplY3RfOiBzdWJqZWN0IElEIChgUzAxYCwgYFMwMmAsIGV0YykKKiBfc2Vzc2lvbl86IFdoZXRoZXIgZGF0YSBhcmUgZnJvbSB0aGUgYGZpcnN0YCBvciBgc2Vjb25kYCBzZXNzaW9uCiogX3N0aW11bGF0aW9uXzogV2hldGhlciBkYXRhIGFyZSBmcm9tIHRoZSBgYW5vZGFsYCBvciBgY2F0aG9kYWxgIHNlc3Npb24KCmBgYHtyIFRhbGx5IGNhc2VzfQppZHhDb21wbGV0ZSA8LSByb3dTdW1zKGlzLm5hKHNlbnNEYXRhKSkgIT0gbmNvbChzZW5zRGF0YSkgLSAzICMgcm93cyB0aGF0IGRvIG5vdCBoYXZlIGFsbCBOQXMgKGV4Y2VwdCB0aGUgMyBmYWN0b3IgY29sdW1ucykKIyBjYWxjdWxhdGUgbnVtYmVyIG9mIHF1ZXN0aW9ubmFpcmVzIGNvbXBsZXRlZCBwZXIgc3RpbXVsYXRpb24gdHlwZQpuQW5vZGFsIDwtIHN1bShzZW5zRGF0YSRzdGltdWxhdGlvbiA9PSAiYW5vZGFsIiAmIGlkeENvbXBsZXRlKSAKbkNhdGhvZGFsIDwtIHN1bShzZW5zRGF0YSRzdGltdWxhdGlvbiA9PSAiY2F0aG9kYWwiICYgaWR4Q29tcGxldGUpCmBgYAoKIyMgUGxvdCBkaXN0cmlidXRpb25zCgojIyMgU2Vuc2F0aW9uIGludGVuc2l0eQoKYGBge3IgdERDUyBzZW5zYXRpb24gZGlzdHJpYnV0aW9uc30Kc2Vuc0RhdGEgJT4lCiAgc2VsZWN0KGV2ZXJ5dGhpbmcoKSwgLWNvbnRhaW5zKCJjb25mIiksIC1mZWx0Lm1vcmUpICU+JSAjIGRyb3AgdGhlIGNvbmZpZGVuY2UgY29sdW1ucwogIGdhdGhlcihzZW5zYXRpb24sIHJhdGluZywgaXRjaGluZzpuYXVzZWEpICU+JSAjIGdhdGhlciBzZW5zYXRpb25zIHNvIHRoZXkgY2FuIGJlIHVzZWQgYXMgYSBmYWN0b3IKICBnZ3Bsb3QoYWVzKHJhdGluZywgZmlsbCA9IHN0aW11bGF0aW9uKSkgKwogICAgZmFjZXRfd3JhcCh+c2Vuc2F0aW9uLCBucm93ID0gMikgKwogICAgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb24gPSAic3RhY2siLCBiaW53aWR0aCA9IC41KSArCiAgICBzdGF0X2JpbihiaW53aWR0aCA9IDEsIGdlb20gPSAidGV4dCIsIGFlcyhsYWJlbCA9IC4uY291bnQuLiksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArCiAgICB4bGltKDAuNSw0LjUpICsgeWxpbSgwLDMwKSArICMgZXhjbHVkZSAiMCIgcmF0aW5ncyB0aGF0IHdlcmUgbm90IHByZXNlbnQKICAgIGdndGl0bGUocGFzdGUoIlNlbnNhdGlvbnMgb3ZlciIsIG5Bbm9kYWwsICJhbm9kYWwgc2Vzc2lvbnMsIiwgbkNhdGhvZGFsLCAiY2F0aG9kYWwgc2Vzc2lvbnMiKSkKYGBgCgpfTmF1c2VhXyB3YXMgbmV2ZXIgZXhwZXJpZW5jZWQ7IF9kaXp6aW5lc3NfIGlzIGVzcGVjaWFsbHkgcmFyZSBhbmQgbWlsZCwgYXMgYXJlIF9oZWFkYWNoZV8gYW5kIF9wYWluXy4KCl9CdXJuaW5nXywgX2l0Y2hpbmdfIGFuZCBfdGluZ2xpbmdfIGFyZSBtb3N0IGZyZXF1ZW50bHkgZXhwZXJpZW5jZWQgYW5kIHRvIGEgaGlnaGVyIGRlZ3JlZS4KCiMjIyBTZW5zYXRpb24gY29uZmlkZW5jZQoKYGBge3IgdERDUyBjb25maWRlbmNlIGRpc3RyaWJ1dGlvbnN9CnNlbnNEYXRhICU+JQogIHNlbGVjdChjb250YWlucygiY29uZiIpLCBzdWJqZWN0LCBzZXNzaW9uLCBzdGltdWxhdGlvbikgJT4lICMgZHJvcCB0aGUgcmF0aW5nIGNvbHVtbnMKICBnYXRoZXIoc2Vuc2F0aW9uLCByYXRpbmcsIGNvbmYuaXRjaGluZzpjb25mLm5hdXNlYSkgJT4lCiAgZ2dwbG90KGFlcyhyYXRpbmcsIGZpbGwgPSBzdGltdWxhdGlvbikpICsKICAgIGZhY2V0X3dyYXAofnNlbnNhdGlvbiwgbnJvdyA9IDIpICsKICAgIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gInN0YWNrIiwgYmlud2lkdGggPSAuNSkgKwogICAgc3RhdF9iaW4oYmlud2lkdGggPSAxLCBnZW9tID0gInRleHQiLCBhZXMobGFiZWwgPSAuLmNvdW50Li4pLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKwogICAgeGxpbSgwLjUsNC41KSArICB5bGltKDAsMzApICsgIyBleGNsdWRlICIwIiByYXRpbmdzIHRoYXQgd2VyZSBub3QgcHJlc2VudAogICAgZ2d0aXRsZShwYXN0ZSgiQ29uZmlkZW5jZSBvdmVyIiwgbkFub2RhbCwgImFub2RhbCBzZXNzaW9ucywiLCBuQ2F0aG9kYWwsICJjYXRob2RhbCBzZXNzaW9ucyIpKQpgYGAKCkZvciAibG9jYWwiIHNlbnNhdGlvbnMgbGlrZSBfYnVybmluZ18sIF90aW5nbGluZ18gYW5kIF9kaXp6aW5lc3NfLCBzdWJqZWN0cyBoYXZlIGhpZ2ggY29uZmlkZW5jZSB0aGF0IHRoZXNlIGFyZSBkdWUgdG8gdERDUy4gRm9yIG1vcmUgZGlmZnVzZSBzZW5zYXRpb25zLCBsaWtlIF9mYXRpZ3VlXyBhbmQgX2hlYWRhY2hlXywgcmF0aW5ncyBhcmUgdmVyeSBsb3csIHNvIHRoZWlyIG9jY3VyZW5jZSBtaWdodCBqdXN0IGJlIGR1ZSB0byBwZXJmb3JtaW5nIHRoZSB0YXNrIGZvciBhbiBleHRlbmRlZCBwZXJpb2Qgb2YgdGltZS4KCk5vdGUgdGhhdCBuYXVzZWEgd2FzIG5ldmVyIHJlcG9ydGVkLCBzbyB0aGUgdHdvICIxIiBjb25maWRlbmNlIHJhdGluZ3MgYXJlIHRlY2hpbmNhbGx5IG5vbi1zZW5zaWNhbC4KCiMjIyBTZW5zYXRpb24gZGlmZmVyZW5jZSBiZXR3ZWVuIGFub2RlIGFuZCBjYXRob2RlCgpgYGB7ciBTZW5zYXRpb25zIGZvciBhbm9kZSBhbmQgY2F0aG9kZX0Kc2Vuc0RhdGEgJT4lCiAgc2VsZWN0KGZlbHQubW9yZSwgc3ViamVjdCwgc2Vzc2lvbiwgc3RpbXVsYXRpb24pICU+JSAjIGtlZXAgb25seSB0aGUgcmVsZXZhbnQgY29sdW1uCiAgbXV0YXRlKGZlbHQubW9yZSA9IGZhY3RvcihmZWx0Lm1vcmUsIGxldmVscyA9IGMoImFub2RlIiwgImVxdWFsIiwgImNhdGhvZGUiKSkpICU+JSAjIGtlZXAgY29sb3JzIGNvbnNpc3RlbnQKICBnZ3Bsb3QoYWVzKHN0aW11bGF0aW9uLCBmaWxsID0gZmVsdC5tb3JlKSkgKwogICAgZ2VvbV9iYXIoKSArCiAgICBzdGF0X2NvdW50KGdlb20gPSAidGV4dCIsIGFlcyhsYWJlbCA9IC4uY291bnQuLiksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBjb2xvciA9ICJ3aGl0ZSIpCmBgYAoKVGhlcmUncyBhIHByZXR0eSBldmVuIHNwbGl0IGJldHdlZW4gd2hpY2ggZWxlY3Ryb2RlIHBlb3BsZSBmZWVsIHRoZSBtb3N0LCBzbyB0aGVyZSBkb24ndCBhcHBlYXIgdG8gYmUgd29ycmlzb21lIGJpYXNlcy4gCgpEdXJpbmcgYW5vZGFsIHN0aW11bGF0aW9uLCB0aGUgYW5vZGUgaXMgZmVsdCBtb3JlIHRoYW4gdGhlIGNhdGhvZGU7IGR1cmluZyBjYXRob2RhbCBzdGltdWxhdGlvbiB0aGUgY2F0aG9kZSBpcyBmZWx0IG1vcmUgdGhhbiB0aGUgYW5vZGUuIEluIG90aGVyIHdvcmRzLCB0aGUgZWxlY3Ryb2RlIG92ZXIgdGhlIEZFRiBpcyBhbHdheXMgZmVsdCBtb3JlIHRoYW4gdGhlIGZvcmVoZWFkIGVsZWN0cm9kZS4gSG93ZXZlciwgdGhpcyBwYXR0ZXJuIGlzIG1vcmUgcHJvbm91bmNlZCBpbiB0aGUgYW5vZGFsIHNlc3Npb24uCgpBYm91dCBhIHRoaXJkIG9mIHBlb3BsZSBpbiB0aGUgY2F0aG9kYWwgc2Vzc2lvbiBkaWQgbm90IGluZGljYXRlIHRoZXkgZmVsdCBvbmUgbW9yZSB0aGFuIHRoZSBvdGhlcjsgdGhpcyBpcyBzbGlnaHRseSBsZXNzIGluIHRoZSBhbm9kYWwgc2Vzc2lvbi4KCiMjIFN0YXRpc3RpY3MKCldlIHdpbGwgdGVzdCBmb3IgZXZlcnkgc2Vuc2F0aW9uIHNlcGFyYXRlbHkgd2hldGhlciB0aGVyZSB3YXMgYSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGFub2RhbCBhbmQgY2F0aG9kYWwgc2Vzc2lvbnMuIE1hbm4tV2hpdG5leS1VIHRlc3RzIGFyZSBtb3N0IGFwcHJvcHJpYXRlIGhlcmUsIGFzIHRoZSBkYXRhIGFyZSBvcmRpbmFsIChMaWtlcnQpIGFuZCBkbyBub3QgbG9vayBub3JtYWxseSBkaXN0cmlidXRlZC4KCmBgYHtyIFRlc3Qgc2Vuc2F0aW9ucyBhbm9kYWwgdnMuIGNhdGhvZGFsLCByZXN1bHRzPSdhc2lzJ30Kc2Vuc2F0aW9uTGlzdCA8LSBjKCJpdGNoaW5nIiwgInRpbmdsaW5nIiwgImJ1cm5pbmciLCAicGFpbiIsICJoZWFkYWNoZSIsICJmYXRpZ3VlIiwgImRpenppbmVzcyIsICJuYXVzZWEiKQpzZW5zZVRlc3RzIDwtIGRhdGEuZnJhbWUoc2Vuc2F0aW9uID0gc2Vuc2F0aW9uTGlzdCwgcC52YWx1ZSA9IE5BKSAjIGluaXRpYWxpemUgcmVzdWx0cyBkYXRhIGZyYW1lCmZvciAoaXRlbSBpbiBzZW5zYXRpb25MaXN0KSB7CiAgdGVzdERhdGEgPC0gc2Vuc0RhdGFbW2l0ZW1dXSAjIGV4dHJhY3QgY29sdW1uIHdpdGggdGVzdCBkYXQKICB0bXAgPC0gd2lsY294LnRlc3QodGVzdERhdGFbc2Vuc0RhdGEkc3RpbXVsYXRpb24gPT0gImFub2RhbCJdLCB0ZXN0RGF0YVtzZW5zRGF0YSRzdGltdWxhdGlvbiA9PSAiY2F0aG9kYWwiXSkKICBzZW5zZVRlc3RzJHAudmFsdWVbc2Vuc2VUZXN0cyRzZW5zYXRpb24gJWluJSBpdGVtXSA8LSB0bXAkcC52YWx1ZSAjIHB1dCBwLXZhbHVlIGluIHJvdyBvZiByZXN1bHRzIGRhdGEgZnJhbWUKfQprYWJsZShzZW5zZVRlc3RzKQpgYGAKCk5vbmUgb2YgdGhlIGRpZmZlcmVuY2VzIGFyZSBzaWduaWZpY2FudC4gVGhlIHAtdmFsdWUgZm9yIG5hdXNlYSBpcyB1bmRlZmluZWQgYmVjYXVzZSBpdCB3YXMgcmF0ZWQgMCBieSBldmVyeW9uZSBmb3IgYm90aCBhbm9kYWwgYW5kIGNhdGhvZGFsLg==