1 Loading packages

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

2 The Tidyverse

2.1 Introduction

The Tidyverse is a family of packages used to speed up the use of R.

You need to first install it (if you haven’t already done so) and then load it. To install, use Tools > Install packages or install.packages() then add tidyverse. To load a package, use the library() function.

Look at how many packages are installed within the Tidyverse. The messages you see are telling you which packages are loaded and which functions are in conflict (i.e., these are functions from other packages that are found within the Tidyverse). If you want to use the original function, simply add package_name::function.

2.2 Visualisation

In the tidyverse, the package for making elegant plots is called ggplot2. It works a lot like how pipes work, but since it was originally designed as a separate package, it uses + instead of %>%.

2.2.1 First steps

2.2.1.1 Empty plot area

Let’s produce a basic plot with nothing drawn on it. This is the basic plotting area in R. We need to then add layers on top of it to show our plot

english %>% 
  ggplot() +
  theme_bw()

2.2.1.2 Adding x and y values

Let’s add the x and y values from our dataset. X = subjective familiarity rating, y = RT in Visual Lexical Decision task

english %>% 
  ggplot(aes(x = Familiarity, 
             y = RTlexdec)) +
  theme_bw()

There are no differences between the two. We need to tell ggplot2 to add a geometric function for plotting

2.2.1.3 Adding geoms

Geoms are integrated within ggplot2 to obtain various types of plots.

english %>% 
  ggplot(aes(x = Familiarity, 
             y = RTlexdec)) +
  theme_bw() +
  geom_point()

2.2.1.4 Adding line of best fit

We will add a line of best fit. This is used to evaluate presence/absence of a relationship between two numeric variables

english %>% 
  ggplot(aes(x = Familiarity, 
             y = RTlexdec)) +
  theme_bw() +
  geom_point() +
  geom_smooth(method = "lm") # line of best fit based on the lm() method
`geom_smooth()` using formula 'y ~ x'

The result shows a nice negative correlation! RT lexical decision decreases when familiarity rating increases.

We can ask, are there differences related to the word category, i.e., verb vs noun?

2.2.1.5 By word category

We change colour by levels of word category;

english %>% 
  ggplot(aes(x = Familiarity, 
             y = RTlexdec,
             colour = WordCategory)) + # add colour to the base aesthetics
  theme_bw() +
  geom_point() +
  geom_smooth(method = "lm")
`geom_smooth()` using formula 'y ~ x'

2.2.1.6 Making final touches

Let’s add a title and a subtitle, change x and y labels, change size of overall plot, and colours of the categories.

english %>% 
  ggplot(aes(x = Familiarity, 
             y = RTlexdec,
             colour = WordCategory)) + # add colour to the base aesthetics
  theme_bw() +
  geom_point() +
  geom_smooth(method = "lm") +
  labs(x = "Familiarity rating", y = "RT Lexical Decision", title = "Familiarity rating vs RT in a lexical decision task", subtitle = "with a trend line") + # add labels
  theme(text = element_text(size = 15)) + # increase size of plot
  theme(legend.position = "bottom", legend.title = element_blank()) + # remove legend title and change position
  scale_color_manual(labels = c("Nouns", "Verbs"), values = c("blue", "red")) # change colours and names of legend
`geom_smooth()` using formula 'y ~ x'

To choose colours, use the addin colourpicker from above. See this link for full list of colours available. Use colours that are colour-blind friendly here

2.2.2 Activity on your own 3

Work through a few examples to plot data

2.3 Additional plots

We looked above at one example of plots (with points). We could use additional types of plots.

2.3.1 A bar plot

Will show barplots of the dataset

english %>%
  ggplot(aes(x = RTlexdec, 
             colour = AgeSubject)) +
  theme_bw() +
  geom_bar()

And another view with error bars! This is a nice example that shows how you can combine multiple chains with the pipe:

  • Group by Age of subject
  • Compute mean and SD
  • use ggplot2 syntax to plot a barplot and error bars
english %>%
  group_by(AgeSubject) %>%
  summarise(
    sd = sd(RTlexdec),
    RTlexdecM = mean(RTlexdec)
  ) %>% 
  ggplot(aes(x = AgeSubject, 
             y = RTlexdecM)) +
  theme_bw() +
  geom_col(fill = "lightgray", color = "black") +
  geom_errorbar(aes(ymin = RTlexdecM-sd, ymax = RTlexdecM+sd), width = 0.2)

2.3.2 A histogram

This looks at the distribution of the variable. We look at a histogram

english %>%
  ggplot(aes(x = RTlexdec, 
             colour = AgeSubject)) +
  theme_bw() +
  geom_histogram(fill = "white") +
  scale_color_manual(values = c("red", "blue"))
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

2.3.3 A density plot

This looks at the distribution of the variable. We see that the two variables have different means. We can superpose the density plot on top of the histogram or have the density plot on its own.

# histogram and density plot
english %>%
  ggplot(aes(x = RTlexdec, 
             colour = AgeSubject)) +
  theme_bw() +
  geom_histogram(aes(y = ..density..), fill = "white") +
  scale_color_manual(values = c("red", "blue")) +
  geom_density()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

# density plot only
english %>%
  ggplot(aes(x = RTlexdec, 
             colour = AgeSubject)) +
  theme_bw() +
  geom_density()

2.3.4 A boxplot

This allows you to see various information, including the Median, SD, Quartiles (25% and 75%) and outliers. Looking at the medians, we see clear difference between the two distributions.

english %>%
  ggplot(aes(x = AgeSubject, 
             y = RTlexdec)) +
  theme_bw() +
  geom_boxplot()

2.3.5 A Violin plot

This allows you to see various information, including the Median, SD, Quartiles (25% and 75%) and outliers. Looking at the medians, we see clear difference between the two distributions.

english %>%
  ggplot(aes(x = AgeSubject, 
             y = RTlexdec)) +
  theme_bw() +
  geom_violin()

2.4 Facet_grid

The plots we used so far allowed to plot data as a function of one categorical variable, e.g., AgeSubject. What if we wanted to show the different patterns emerging when combining AgeSubject (old vs young), WordCategory (Noun or Verb), CV (Consonant or Vowel) and Voice (Voiced and Voiceless) ? What if we also wanted to modify the labels and order of levels of variables?

We will start slowly below to show how we can combine two categorical variables and extend them to additional ones

2.4.1 Two categorical variables

2.4.1.1 First steps

Here we obtain a boxplot with two categorical variables AgeSubject and WordCategory

english %>%
  ggplot(aes(x = AgeSubject, 
             y = RTlexdec)) +
  theme_bw() +
  geom_boxplot() +
  facet_grid(~ WordCategory)

2.4.1.2 Changing order of levels within a variable and its labels

What would you do to change both order of levels within a variable and its labels? We want to change order for AgeSubject to be Young vs Old (rather than old vs young) and change labels of WordCategory from N vs V to Noun vs Verb.

Work on this with your peers. Answer is below!

2.4.1.2.1 Activity on your own 1
english %>%
  ggplot(aes(x = AgeSubject, 
             y = RTlexdec)) +
  theme_bw() +
  geom_boxplot() +
  facet_grid(~ WordCategory)

2.4.1.2.2 Answer
english %>%
  mutate(AgeSubject = factor(AgeSubject, levels = c("young", "old"), labels = c("Young", "Old")),
         WordCategory = factor(WordCategory, labels = c("Noun", "Verb"))) %>% 
  ggplot(aes(x = AgeSubject, 
             y = RTlexdec)) +
  theme_bw() +
  geom_boxplot() +
  facet_grid(~ WordCategory)

2.4.2 Three or more categorical variables

Let us obtain a boxplot with four categorical variables AgeSubject, WordCategory, CV and Voice. We still need to change names. We can also add margins = TRUE to obtain mean values for all categories (under all). We can also use scale = "free" to change limits of the y-axis.

Of course this figure is so complex that it needs a lot of interpretation. But it allows you to see how we can use facet_grid to get more categorical variables in. This visualisation suggests that there are no clear differences when plotting results by this 4-way interaction as we always have clear differences between “Young” and “Old” participants, with “Young” being faster than “Old” participants.

english %>%
  mutate(AgeSubject = factor(AgeSubject, levels = c("young", "old"), labels = c("Young", "Old")),
         WordCategory = factor(WordCategory, labels = c("Noun", "Verb")),
         CV = factor(CV, labels = c("Consonant", "Vowel"))) %>% 
  ggplot(aes(x = AgeSubject, 
             y = RTlexdec)) +
  theme_bw() +
  geom_boxplot() +
  facet_grid(CV + Voice ~ WordCategory, margins = TRUE, scales = "free")

2.4.3 Comparing two numeric outcomes

What if we want to compare performance in relation to reaction time for the lexical decision task (RTlexdec) and reaction time for naming (RTnaming). We want to see if there are differences related to the AgeSubject, WordCategory. We use pivot_longer here to do change the format of our table and then change names and use facet_grid.

english %>%
  select(RTlexdec, RTnaming, AgeSubject, WordCategory) %>% 
  pivot_longer(cols = c(RTlexdec, RTnaming),
               names_to = "variable",
               values_to = "values") %>% 
  mutate(AgeSubject = factor(AgeSubject, levels = c("young", "old"), labels = c("Young", "Old")),
         WordCategory = factor(WordCategory, labels = c("Noun", "Verb"))) %>% 
  ggplot(aes(x = variable, 
             y = values)) +
  theme_bw() +
  geom_boxplot() +
  facet_grid(AgeSubject ~ WordCategory, margins = TRUE, scales = "free")

2.5 Exporting images

When you use Rmarkdown, your figures are already embedded within the generated output. If you are using an R script and/or want to add the figure in a different document, you can use the following code:

jpeg(filename = "test.jpeg", width = 15, height = 15, units = "cm", res = 300)

english %>%
  select(RTlexdec, RTnaming, AgeSubject, WordCategory) %>% 
  pivot_longer(cols = c(RTlexdec, RTnaming),
               names_to = "variable",
               values_to = "values") %>% 
  mutate(AgeSubject = factor(AgeSubject, levels = c("young", "old"), labels = c("Young", "Old")),
         WordCategory = factor(WordCategory, labels = c("Noun", "Verb"))) %>% 
  ggplot(aes(x = variable, 
             y = values)) +
  theme_bw() +
  geom_boxplot() +
  facet_grid(AgeSubject ~ WordCategory, margins = TRUE, scales = "free")
dev.off()
null device 
          1 

The image is automatically saved into your working directory and you can import it to your word () document.

You can use any device to save the output. Jpeg, PNG, PDF, TIFF, etc.. From an R script, you can run the code and then the image will appear within the “Plots” area. Simply click on export and you will be able to save the image.

2.6 Conclusion

As you can see, visualisations in R using the Tidyverse provide you with many options and you can explore these further.

See here for a full list of geoms. This will help you in thinking about visualisation.

See extensions to ggplot2 here for additional plugins to enhance plots.

In the next section, we start with initial inferential statistics.

2.7 Going further

See here for a full list of geoms. This will help you in thinking about visualisation.

See extensions to ggplot2 here for additional plugins to enhance plots.

3 End of the session

This is the end of the third session. We used the package Tidyverse to manipulate objects. We obtained then basic summaries and basic plots. We looked at how to build a plot from scratch

Next week, we will look at basic inferential statistics

4 session info

sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19044)

Matrix products: default

locale:
[1] LC_COLLATE=English_United Kingdom.1252 
[2] LC_CTYPE=English_United Kingdom.1252   
[3] LC_MONETARY=English_United Kingdom.1252
[4] LC_NUMERIC=C                           
[5] LC_TIME=English_United Kingdom.1252    

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

other attached packages:
 [1] languageR_1.5.0 forcats_0.5.1   stringr_1.4.0   dplyr_1.0.7    
 [5] purrr_0.3.4     readr_2.0.2     tidyr_1.1.4     tibble_3.1.5   
 [9] ggplot2_3.3.5   tidyverse_1.3.1

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.7            lubridate_1.8.0       lattice_0.20-45      
 [4] assertthat_0.2.1      digest_0.6.28         utf8_1.2.2           
 [7] R6_2.5.1              cellranger_1.1.0      backports_1.3.0      
[10] reprex_2.0.1          evaluate_0.14         httr_1.4.2           
[13] pillar_1.6.4          rlang_0.4.12          readxl_1.3.1         
[16] rstudioapi_0.13       minqa_1.2.4           jquerylib_0.1.4      
[19] nloptr_1.2.2.2        Matrix_1.3-4          rmarkdown_2.11       
[22] labeling_0.4.2        splines_4.1.2         lme4_1.1-27.1        
[25] munsell_0.5.0         broom_0.7.10          compiler_4.1.2       
[28] modelr_0.1.8          xfun_0.27             pkgconfig_2.0.3      
[31] PresenceAbsence_1.1.9 mgcv_1.8-38           htmltools_0.5.2      
[34] tidyselect_1.1.1      fansi_0.5.0           crayon_1.4.2         
[37] tzdb_0.2.0            dbplyr_2.1.1          withr_2.4.2          
[40] MASS_7.3-54           psycho_0.6.1          grid_4.1.2           
[43] nlme_3.1-153          jsonlite_1.7.2        gtable_0.3.0         
[46] lifecycle_1.0.1       DBI_1.1.1             magrittr_2.0.1       
[49] scales_1.1.1          cli_3.1.0             stringi_1.7.5        
[52] farver_2.1.0          fs_1.5.0              xml2_1.3.2           
[55] bslib_0.3.1           ellipsis_0.3.2        generics_0.1.1       
[58] vctrs_0.3.8           boot_1.3-28           tools_4.1.2          
[61] glue_1.4.2            hms_1.1.1             fastmap_1.1.0        
[64] yaml_2.2.1            colorspace_2.0-2      rvest_1.0.2          
[67] knitr_1.36            haven_2.4.3           sass_0.4.0           
LS0tDQp0aXRsZTogIlNlc3Npb24gMyAtIEludHJvZHVjdGlvbiB0byB0aGUgVGlkeXZlcnNlIC0gVmlzdWFsaXNhdGlvbiINCmF1dGhvcjoNCiAgbmFtZTogSmFsYWwgQWwtVGFtaW1pDQogIGFmZmlsaWF0aW9uOiBVbml2ZXJzaXTDqSBkZSBQYXJpcw0KZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIgJVknKWAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgaGlnaGxpZ2h0OiBweWdtZW50cw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDYNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IHllcw0KLS0tDQoNCg0KDQojIExvYWRpbmcgcGFja2FnZXMgDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfQ0KIyMgVXNlIHRoZSBjb2RlIGJlbG93IHRvIGNoZWNrIGlmIHlvdSBoYXZlIGFsbCByZXF1aXJlZCBwYWNrYWdlcyBpbnN0YWxsZWQuIElmIHNvbWUgYXJlIG5vdCBpbnN0YWxsZWQgYWxyZWFkeSwgdGhlIGNvZGUgYmVsb3cgd2lsbCBpbnN0YWxsIHRoZXNlLiBJZiB5b3UgaGF2ZSBhbGwgcGFja2FnZXMgaW5zdGFsbGVkLCB0aGVuIHlvdSBjb3VsZCBsb2FkIHRoZW0gd2l0aCB0aGUgc2Vjb25kIGNvZGUuDQpyZXF1aXJlZFBhY2thZ2VzID0gYygndGlkeXZlcnNlJywgJ2xhbmd1YWdlUicpDQpmb3IocCBpbiByZXF1aXJlZFBhY2thZ2VzKXsNCiAgaWYoIXJlcXVpcmUocCxjaGFyYWN0ZXIub25seSA9IFRSVUUpKSBpbnN0YWxsLnBhY2thZ2VzKHApDQogIGxpYnJhcnkocCxjaGFyYWN0ZXIub25seSA9IFRSVUUpDQp9DQpgYGANCg0KDQojIFRoZSBgVGlkeXZlcnNlYA0KDQojIyBJbnRyb2R1Y3Rpb24NCg0KVGhlIGBUaWR5dmVyc2VgIGlzIGEgZmFtaWx5IG9mIHBhY2thZ2VzIHVzZWQgdG8gc3BlZWQgdXAgdGhlIHVzZSBvZiBSLiANCg0KIVtdKGltYWdlcy90aWR5dmVyc2UucG5nKQ0KDQpZb3UgbmVlZCB0byBmaXJzdCBpbnN0YWxsIGl0IChpZiB5b3UgaGF2ZW4ndCBhbHJlYWR5IGRvbmUgc28pIGFuZCB0aGVuIGxvYWQgaXQuIFRvIGluc3RhbGwsIHVzZSBgVG9vbHMgPiBJbnN0YWxsIHBhY2thZ2VzYCBvciBgaW5zdGFsbC5wYWNrYWdlcygpYCB0aGVuIGFkZCB0aWR5dmVyc2UuIFRvIGxvYWQgYSBwYWNrYWdlLCB1c2UgdGhlIGBsaWJyYXJ5KClgIGZ1bmN0aW9uLg0KDQoNCkxvb2sgYXQgaG93IG1hbnkgcGFja2FnZXMgYXJlIGluc3RhbGxlZCB3aXRoaW4gdGhlIGBUaWR5dmVyc2VgLiBUaGUgbWVzc2FnZXMgeW91IHNlZSBhcmUgdGVsbGluZyB5b3Ugd2hpY2ggcGFja2FnZXMgYXJlIGxvYWRlZCBhbmQgd2hpY2ggZnVuY3Rpb25zIGFyZSBpbiBjb25mbGljdCAoaS5lLiwgdGhlc2UgYXJlIGZ1bmN0aW9ucyBmcm9tIG90aGVyIHBhY2thZ2VzIHRoYXQgYXJlIGZvdW5kIHdpdGhpbiB0aGUgYFRpZHl2ZXJzZWApLiBJZiB5b3Ugd2FudCB0byB1c2UgdGhlIG9yaWdpbmFsIGZ1bmN0aW9uLCBzaW1wbHkgYWRkIGBwYWNrYWdlX25hbWU6OmZ1bmN0aW9uYC4NCg0KDQojIyBWaXN1YWxpc2F0aW9uDQoNCkluIHRoZSBgdGlkeXZlcnNlYCwgdGhlIHBhY2thZ2UgZm9yIG1ha2luZyBlbGVnYW50IHBsb3RzIGlzIGNhbGxlZCBgZ2dwbG90MmAuIEl0IHdvcmtzIGEgbG90IGxpa2UgaG93IHBpcGVzIHdvcmssIGJ1dCBzaW5jZSBpdCB3YXMgb3JpZ2luYWxseSBkZXNpZ25lZCBhcyBhIHNlcGFyYXRlIHBhY2thZ2UsIGl0IHVzZXMgYCtgIGluc3RlYWQgb2YgYCU+JWAuDQoNCiMjIyBGaXJzdCBzdGVwcw0KDQojIyMjIEVtcHR5IHBsb3QgYXJlYQ0KDQpMZXQncyBwcm9kdWNlIGEgYmFzaWMgcGxvdCB3aXRoIG5vdGhpbmcgZHJhd24gb24gaXQuIA0KVGhpcyBpcyB0aGUgYmFzaWMgcGxvdHRpbmcgYXJlYSBpbiBgUmAuIFdlIG5lZWQgdG8gdGhlbiBhZGQgbGF5ZXJzIG9uIHRvcCBvZiBpdCB0byBzaG93IG91ciBwbG90DQoNCmBgYHtyfQ0KZW5nbGlzaCAlPiUgDQogIGdncGxvdCgpICsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCg0KIyMjIyBBZGRpbmcgeCBhbmQgeSB2YWx1ZXMNCg0KTGV0J3MgYWRkIHRoZSB4IGFuZCB5IHZhbHVlcyBmcm9tIG91ciBkYXRhc2V0LiBYID0gc3ViamVjdGl2ZSBmYW1pbGlhcml0eSByYXRpbmcsIHkgPSBSVCBpbiBWaXN1YWwgTGV4aWNhbCBEZWNpc2lvbiB0YXNrDQoNCg0KYGBge3J9DQplbmdsaXNoICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gRmFtaWxpYXJpdHksIA0KICAgICAgICAgICAgIHkgPSBSVGxleGRlYykpICsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCg0KVGhlcmUgYXJlIG5vIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIHR3by4gV2UgbmVlZCB0byB0ZWxsIGBnZ3Bsb3QyYCB0byBhZGQgYSBnZW9tZXRyaWMgZnVuY3Rpb24gZm9yIHBsb3R0aW5nDQoNCg0KIyMjIyBBZGRpbmcgZ2VvbXMNCg0KR2VvbXMgYXJlIGludGVncmF0ZWQgd2l0aGluIGBnZ3Bsb3QyYCB0byBvYnRhaW4gdmFyaW91cyB0eXBlcyBvZiBwbG90cy4gDQoNCmBgYHtyfQ0KZW5nbGlzaCAlPiUgDQogIGdncGxvdChhZXMoeCA9IEZhbWlsaWFyaXR5LCANCiAgICAgICAgICAgICB5ID0gUlRsZXhkZWMpKSArDQogIHRoZW1lX2J3KCkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQoNCiMjIyMgQWRkaW5nIGxpbmUgb2YgYmVzdCBmaXQgDQoNCldlIHdpbGwgYWRkIGEgbGluZSBvZiBiZXN0IGZpdC4gVGhpcyBpcyB1c2VkIHRvIGV2YWx1YXRlIHByZXNlbmNlL2Fic2VuY2Ugb2YgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gbnVtZXJpYyB2YXJpYWJsZXMNCg0KDQpgYGB7cn0NCmVuZ2xpc2ggJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBGYW1pbGlhcml0eSwgDQogICAgICAgICAgICAgeSA9IFJUbGV4ZGVjKSkgKw0KICB0aGVtZV9idygpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgIyBsaW5lIG9mIGJlc3QgZml0IGJhc2VkIG9uIHRoZSBsbSgpIG1ldGhvZA0KYGBgDQoNCg0KVGhlIHJlc3VsdCBzaG93cyBhIG5pY2UgbmVnYXRpdmUgY29ycmVsYXRpb24hIFJUIGxleGljYWwgZGVjaXNpb24gZGVjcmVhc2VzIHdoZW4gZmFtaWxpYXJpdHkgcmF0aW5nIGluY3JlYXNlcy4gDQoNCldlIGNhbiBhc2ssIGFyZSB0aGVyZSBkaWZmZXJlbmNlcyByZWxhdGVkIHRvIHRoZSB3b3JkIGNhdGVnb3J5LCBpLmUuLCB2ZXJiIHZzIG5vdW4/DQoNCg0KIyMjIyBCeSB3b3JkIGNhdGVnb3J5DQoNCldlIGNoYW5nZSBjb2xvdXIgYnkgbGV2ZWxzIG9mIHdvcmQgY2F0ZWdvcnk7DQoNCmBgYHtyfQ0KZW5nbGlzaCAlPiUgDQogIGdncGxvdChhZXMoeCA9IEZhbWlsaWFyaXR5LCANCiAgICAgICAgICAgICB5ID0gUlRsZXhkZWMsDQogICAgICAgICAgICAgY29sb3VyID0gV29yZENhdGVnb3J5KSkgKyAjIGFkZCBjb2xvdXIgdG8gdGhlIGJhc2UgYWVzdGhldGljcw0KICB0aGVtZV9idygpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikNCmBgYA0KDQoNCiMjIyMgTWFraW5nIGZpbmFsIHRvdWNoZXMNCg0KTGV0J3MgYWRkIGEgdGl0bGUgYW5kIGEgc3VidGl0bGUsIGNoYW5nZSB4IGFuZCB5IGxhYmVscywgY2hhbmdlIHNpemUgb2Ygb3ZlcmFsbCBwbG90LCBhbmQgY29sb3VycyBvZiB0aGUgY2F0ZWdvcmllcy4NCg0KDQpgYGB7cn0NCmVuZ2xpc2ggJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBGYW1pbGlhcml0eSwgDQogICAgICAgICAgICAgeSA9IFJUbGV4ZGVjLA0KICAgICAgICAgICAgIGNvbG91ciA9IFdvcmRDYXRlZ29yeSkpICsgIyBhZGQgY29sb3VyIHRvIHRoZSBiYXNlIGFlc3RoZXRpY3MNCiAgdGhlbWVfYncoKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsNCiAgbGFicyh4ID0gIkZhbWlsaWFyaXR5IHJhdGluZyIsIHkgPSAiUlQgTGV4aWNhbCBEZWNpc2lvbiIsIHRpdGxlID0gIkZhbWlsaWFyaXR5IHJhdGluZyB2cyBSVCBpbiBhIGxleGljYWwgZGVjaXNpb24gdGFzayIsIHN1YnRpdGxlID0gIndpdGggYSB0cmVuZCBsaW5lIikgKyAjIGFkZCBsYWJlbHMNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArICMgaW5jcmVhc2Ugc2l6ZSBvZiBwbG90DQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgIyByZW1vdmUgbGVnZW5kIHRpdGxlIGFuZCBjaGFuZ2UgcG9zaXRpb24NCiAgc2NhbGVfY29sb3JfbWFudWFsKGxhYmVscyA9IGMoIk5vdW5zIiwgIlZlcmJzIiksIHZhbHVlcyA9IGMoImJsdWUiLCAicmVkIikpICMgY2hhbmdlIGNvbG91cnMgYW5kIG5hbWVzIG9mIGxlZ2VuZA0KDQpgYGANCg0KDQpUbyBjaG9vc2UgY29sb3VycywgdXNlIHRoZSBhZGRpbiBgY29sb3VycGlja2VyYCBmcm9tIGFib3ZlLiBTZWUgdGhpcyBbbGlua10oaHR0cDovL2FwcGxpZWQtci5jb20vci1jb2xvci10YWJsZXMvKSBmb3IgZnVsbCBsaXN0IG9mIGNvbG91cnMgYXZhaWxhYmxlLiBVc2UgY29sb3VycyB0aGF0IGFyZSBjb2xvdXItYmxpbmQgZnJpZW5kbHkgW2hlcmVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9jb2xvckJsaW5kbmVzcy92aWduZXR0ZXMvY29sb3JCbGluZG5lc3MuaHRtbCkNCg0KDQojIyMgQWN0aXZpdHkgb24geW91ciBvd24gMw0KDQpXb3JrIHRocm91Z2ggYSBmZXcgZXhhbXBsZXMgdG8gcGxvdCBkYXRhDQoNCg0KYGBge3J9DQoNCmBgYA0KDQoNCg0KIyMgQWRkaXRpb25hbCBwbG90cw0KDQpXZSBsb29rZWQgYWJvdmUgYXQgb25lIGV4YW1wbGUgb2YgcGxvdHMgKHdpdGggcG9pbnRzKS4gV2UgY291bGQgdXNlIGFkZGl0aW9uYWwgdHlwZXMgb2YgcGxvdHMuDQoNCg0KIyMjIEEgYmFyIHBsb3QNCg0KV2lsbCBzaG93IGJhcnBsb3RzIG9mIHRoZSBkYXRhc2V0DQoNCmBgYHtyfQ0KZW5nbGlzaCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gUlRsZXhkZWMsIA0KICAgICAgICAgICAgIGNvbG91ciA9IEFnZVN1YmplY3QpKSArDQogIHRoZW1lX2J3KCkgKw0KICBnZW9tX2JhcigpDQpgYGANCg0KDQpBbmQgYW5vdGhlciB2aWV3IHdpdGggZXJyb3IgYmFycyEgVGhpcyBpcyBhIG5pY2UgZXhhbXBsZSB0aGF0IHNob3dzIGhvdyB5b3UgY2FuIGNvbWJpbmUgbXVsdGlwbGUgY2hhaW5zIHdpdGggdGhlIHBpcGU6DQoNCi0gR3JvdXAgYnkgQWdlIG9mIHN1YmplY3QNCi0gQ29tcHV0ZSBtZWFuIGFuZCBTRA0KLSB1c2UgZ2dwbG90MiBzeW50YXggdG8gcGxvdCBhIGJhcnBsb3QgYW5kIGVycm9yIGJhcnMNCg0KDQpgYGB7cn0NCmVuZ2xpc2ggJT4lDQogIGdyb3VwX2J5KEFnZVN1YmplY3QpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgc2QgPSBzZChSVGxleGRlYyksDQogICAgUlRsZXhkZWNNID0gbWVhbihSVGxleGRlYykNCiAgKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IEFnZVN1YmplY3QsIA0KICAgICAgICAgICAgIHkgPSBSVGxleGRlY00pKSArDQogIHRoZW1lX2J3KCkgKw0KICBnZW9tX2NvbChmaWxsID0gImxpZ2h0Z3JheSIsIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gUlRsZXhkZWNNLXNkLCB5bWF4ID0gUlRsZXhkZWNNK3NkKSwgd2lkdGggPSAwLjIpDQpgYGANCg0KIyMjIEEgaGlzdG9ncmFtDQoNClRoaXMgbG9va3MgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdmFyaWFibGUuIFdlIGxvb2sgYXQgYSBoaXN0b2dyYW0gDQoNCmBgYHtyfQ0KZW5nbGlzaCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gUlRsZXhkZWMsIA0KICAgICAgICAgICAgIGNvbG91ciA9IEFnZVN1YmplY3QpKSArDQogIHRoZW1lX2J3KCkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImJsdWUiKSkNCmBgYA0KDQoNCiMjIyBBIGRlbnNpdHkgcGxvdA0KDQpUaGlzIGxvb2tzIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHZhcmlhYmxlLiBXZSBzZWUgdGhhdCB0aGUgdHdvIHZhcmlhYmxlcyBoYXZlIGRpZmZlcmVudCBtZWFucy4gV2UgY2FuIHN1cGVycG9zZSB0aGUgZGVuc2l0eSBwbG90IG9uIHRvcCBvZiB0aGUgaGlzdG9ncmFtIG9yIGhhdmUgdGhlIGRlbnNpdHkgcGxvdCBvbiBpdHMgb3duLg0KDQpgYGB7cn0NCiMgaGlzdG9ncmFtIGFuZCBkZW5zaXR5IHBsb3QNCmVuZ2xpc2ggJT4lDQogIGdncGxvdChhZXMoeCA9IFJUbGV4ZGVjLCANCiAgICAgICAgICAgICBjb2xvdXIgPSBBZ2VTdWJqZWN0KSkgKw0KICB0aGVtZV9idygpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLiksIGZpbGwgPSAid2hpdGUiKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCAiYmx1ZSIpKSArDQogIGdlb21fZGVuc2l0eSgpDQoNCiMgZGVuc2l0eSBwbG90IG9ubHkNCmVuZ2xpc2ggJT4lDQogIGdncGxvdChhZXMoeCA9IFJUbGV4ZGVjLCANCiAgICAgICAgICAgICBjb2xvdXIgPSBBZ2VTdWJqZWN0KSkgKw0KICB0aGVtZV9idygpICsNCiAgZ2VvbV9kZW5zaXR5KCkNCmBgYA0KDQoNCiMjIyBBIGJveHBsb3QNCg0KVGhpcyBhbGxvd3MgeW91IHRvIHNlZSB2YXJpb3VzIGluZm9ybWF0aW9uLCBpbmNsdWRpbmcgdGhlIE1lZGlhbiwgU0QsIFF1YXJ0aWxlcyAoMjUlIGFuZCA3NSUpIGFuZCBvdXRsaWVycy4gTG9va2luZyBhdCB0aGUgbWVkaWFucywgd2Ugc2VlIGNsZWFyIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvIGRpc3RyaWJ1dGlvbnMuIA0KDQpgYGB7cn0NCmVuZ2xpc2ggJT4lDQogIGdncGxvdChhZXMoeCA9IEFnZVN1YmplY3QsIA0KICAgICAgICAgICAgIHkgPSBSVGxleGRlYykpICsNCiAgdGhlbWVfYncoKSArDQogIGdlb21fYm94cGxvdCgpDQpgYGANCg0KDQoNCiMjIyBBIFZpb2xpbiBwbG90DQoNClRoaXMgYWxsb3dzIHlvdSB0byBzZWUgdmFyaW91cyBpbmZvcm1hdGlvbiwgaW5jbHVkaW5nIHRoZSBNZWRpYW4sIFNELCBRdWFydGlsZXMgKDI1JSBhbmQgNzUlKSBhbmQgb3V0bGllcnMuIExvb2tpbmcgYXQgdGhlIG1lZGlhbnMsIHdlIHNlZSBjbGVhciBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHR3byBkaXN0cmlidXRpb25zLiANCg0KYGBge3J9DQplbmdsaXNoICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBBZ2VTdWJqZWN0LCANCiAgICAgICAgICAgICB5ID0gUlRsZXhkZWMpKSArDQogIHRoZW1lX2J3KCkgKw0KICBnZW9tX3Zpb2xpbigpDQpgYGANCg0KDQojIyBGYWNldF9ncmlkDQoNClRoZSBwbG90cyB3ZSB1c2VkIHNvIGZhciBhbGxvd2VkIHRvIHBsb3QgZGF0YSBhcyBhIGZ1bmN0aW9uIG9mIG9uZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSwgZS5nLiwgYEFnZVN1YmplY3RgLiBXaGF0IGlmIHdlIHdhbnRlZCB0byBzaG93IHRoZSBkaWZmZXJlbnQgcGF0dGVybnMgZW1lcmdpbmcgd2hlbiBjb21iaW5pbmcgYEFnZVN1YmplY3RgIChvbGQgdnMgeW91bmcpLCBgV29yZENhdGVnb3J5YCAoTm91biBvciBWZXJiKSwgYENWYCAoQ29uc29uYW50IG9yIFZvd2VsKSBhbmQgYFZvaWNlYCAoVm9pY2VkIGFuZCBWb2ljZWxlc3MpID8NCldoYXQgaWYgd2UgYWxzbyB3YW50ZWQgdG8gbW9kaWZ5IHRoZSBsYWJlbHMgYW5kIG9yZGVyIG9mIGxldmVscyBvZiB2YXJpYWJsZXM/IA0KDQpXZSB3aWxsIHN0YXJ0IHNsb3dseSBiZWxvdyB0byBzaG93IGhvdyB3ZSBjYW4gY29tYmluZSB0d28gY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFuZCBleHRlbmQgdGhlbSB0byBhZGRpdGlvbmFsIG9uZXMgDQoNCiMjIyBUd28gY2F0ZWdvcmljYWwgdmFyaWFibGVzDQoNCiMjIyMgRmlyc3Qgc3RlcHMNCg0KSGVyZSB3ZSBvYnRhaW4gYSBib3hwbG90IHdpdGggdHdvIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBgQWdlU3ViamVjdGAgYW5kIGBXb3JkQ2F0ZWdvcnlgDQoNCmBgYHtyfQ0KZW5nbGlzaCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gQWdlU3ViamVjdCwgDQogICAgICAgICAgICAgeSA9IFJUbGV4ZGVjKSkgKw0KICB0aGVtZV9idygpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBmYWNldF9ncmlkKH4gV29yZENhdGVnb3J5KQ0KYGBgDQoNCg0KIyMjIyBDaGFuZ2luZyBvcmRlciBvZiBsZXZlbHMgd2l0aGluIGEgdmFyaWFibGUgYW5kIGl0cyBsYWJlbHMNCg0KV2hhdCB3b3VsZCB5b3UgZG8gdG8gY2hhbmdlIGJvdGggb3JkZXIgb2YgbGV2ZWxzIHdpdGhpbiBhIHZhcmlhYmxlIGFuZCBpdHMgbGFiZWxzPyBXZSB3YW50IHRvIGNoYW5nZSBvcmRlciBmb3IgYEFnZVN1YmplY3RgIHRvIGJlIFlvdW5nIHZzIE9sZCAocmF0aGVyIHRoYW4gb2xkIHZzIHlvdW5nKSBhbmQgY2hhbmdlIGxhYmVscyBvZiBgV29yZENhdGVnb3J5YCBmcm9tIE4gdnMgViB0byBOb3VuIHZzIFZlcmIuDQoNCldvcmsgb24gdGhpcyB3aXRoIHlvdXIgcGVlcnMuIEFuc3dlciBpcyBiZWxvdyENCg0KDQojIyMjIyBBY3Rpdml0eSBvbiB5b3VyIG93biAxDQoNCmBgYHtyfQ0KZW5nbGlzaCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gQWdlU3ViamVjdCwgDQogICAgICAgICAgICAgeSA9IFJUbGV4ZGVjKSkgKw0KICB0aGVtZV9idygpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBmYWNldF9ncmlkKH4gV29yZENhdGVnb3J5KQ0KDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQojIyMjIyBBbnN3ZXINCg0KDQoNCmBgYHtyfQ0KZW5nbGlzaCAlPiUNCiAgbXV0YXRlKEFnZVN1YmplY3QgPSBmYWN0b3IoQWdlU3ViamVjdCwgbGV2ZWxzID0gYygieW91bmciLCAib2xkIiksIGxhYmVscyA9IGMoIllvdW5nIiwgIk9sZCIpKSwNCiAgICAgICAgIFdvcmRDYXRlZ29yeSA9IGZhY3RvcihXb3JkQ2F0ZWdvcnksIGxhYmVscyA9IGMoIk5vdW4iLCAiVmVyYiIpKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBBZ2VTdWJqZWN0LCANCiAgICAgICAgICAgICB5ID0gUlRsZXhkZWMpKSArDQogIHRoZW1lX2J3KCkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGZhY2V0X2dyaWQofiBXb3JkQ2F0ZWdvcnkpDQpgYGANCg0KIyMjIFRocmVlIG9yIG1vcmUgY2F0ZWdvcmljYWwgdmFyaWFibGVzDQoNCkxldCB1cyBvYnRhaW4gYSBib3hwbG90IHdpdGggZm91ciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYEFnZVN1YmplY3RgLCBgV29yZENhdGVnb3J5YCwgYENWYCBhbmQgYFZvaWNlYC4gV2Ugc3RpbGwgbmVlZCB0byBjaGFuZ2UgbmFtZXMuDQpXZSBjYW4gYWxzbyBhZGQgYG1hcmdpbnMgPSBUUlVFYCB0byBvYnRhaW4gbWVhbiB2YWx1ZXMgZm9yIGFsbCBjYXRlZ29yaWVzICh1bmRlciBgYWxsYCkuIFdlIGNhbiBhbHNvIHVzZSBgc2NhbGUgPSAiZnJlZSJgIHRvIGNoYW5nZSBsaW1pdHMgb2YgdGhlIGB5LWF4aXNgLg0KDQpPZiBjb3Vyc2UgdGhpcyBmaWd1cmUgaXMgc28gY29tcGxleCB0aGF0IGl0IG5lZWRzIGEgbG90IG9mIGludGVycHJldGF0aW9uLiBCdXQgaXQgYWxsb3dzIHlvdSB0byBzZWUgaG93IHdlIGNhbiB1c2UgYGZhY2V0X2dyaWRgIHRvIGdldCBtb3JlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBpbi4gVGhpcyB2aXN1YWxpc2F0aW9uIHN1Z2dlc3RzIHRoYXQgdGhlcmUgYXJlIG5vIGNsZWFyIGRpZmZlcmVuY2VzIHdoZW4gcGxvdHRpbmcgcmVzdWx0cyBieSB0aGlzIDQtd2F5IGludGVyYWN0aW9uIGFzIHdlIGFsd2F5cyBoYXZlIGNsZWFyIGRpZmZlcmVuY2VzIGJldHdlZW4gIllvdW5nIiBhbmQgIk9sZCIgcGFydGljaXBhbnRzLCB3aXRoICJZb3VuZyIgYmVpbmcgZmFzdGVyIHRoYW4gIk9sZCIgcGFydGljaXBhbnRzLg0KDQoNCg0KYGBge3J9DQplbmdsaXNoICU+JQ0KICBtdXRhdGUoQWdlU3ViamVjdCA9IGZhY3RvcihBZ2VTdWJqZWN0LCBsZXZlbHMgPSBjKCJ5b3VuZyIsICJvbGQiKSwgbGFiZWxzID0gYygiWW91bmciLCAiT2xkIikpLA0KICAgICAgICAgV29yZENhdGVnb3J5ID0gZmFjdG9yKFdvcmRDYXRlZ29yeSwgbGFiZWxzID0gYygiTm91biIsICJWZXJiIikpLA0KICAgICAgICAgQ1YgPSBmYWN0b3IoQ1YsIGxhYmVscyA9IGMoIkNvbnNvbmFudCIsICJWb3dlbCIpKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBBZ2VTdWJqZWN0LCANCiAgICAgICAgICAgICB5ID0gUlRsZXhkZWMpKSArDQogIHRoZW1lX2J3KCkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGZhY2V0X2dyaWQoQ1YgKyBWb2ljZSB+IFdvcmRDYXRlZ29yeSwgbWFyZ2lucyA9IFRSVUUsIHNjYWxlcyA9ICJmcmVlIikNCmBgYA0KDQoNCiMjIyBDb21wYXJpbmcgdHdvIG51bWVyaWMgb3V0Y29tZXMNCg0KDQpXaGF0IGlmIHdlIHdhbnQgdG8gY29tcGFyZSBwZXJmb3JtYW5jZSBpbiByZWxhdGlvbiB0byByZWFjdGlvbiB0aW1lIGZvciB0aGUgbGV4aWNhbCBkZWNpc2lvbiB0YXNrIChSVGxleGRlYykgYW5kIHJlYWN0aW9uIHRpbWUgZm9yIG5hbWluZyAoUlRuYW1pbmcpLiBXZSB3YW50IHRvIHNlZSBpZiB0aGVyZSBhcmUgZGlmZmVyZW5jZXMgcmVsYXRlZCB0byB0aGUgYEFnZVN1YmplY3RgLCBgV29yZENhdGVnb3J5YC4NCldlIHVzZSBgcGl2b3RfbG9uZ2VyYCBoZXJlIHRvIGRvIGNoYW5nZSB0aGUgZm9ybWF0IG9mIG91ciB0YWJsZSBhbmQgdGhlbiBjaGFuZ2UgbmFtZXMgYW5kIHVzZSBgZmFjZXRfZ3JpZGAuDQoNCg0KYGBge3J9DQplbmdsaXNoICU+JQ0KICBzZWxlY3QoUlRsZXhkZWMsIFJUbmFtaW5nLCBBZ2VTdWJqZWN0LCBXb3JkQ2F0ZWdvcnkpICU+JSANCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKFJUbGV4ZGVjLCBSVG5hbWluZyksDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJ2YXJpYWJsZSIsDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWVzIikgJT4lIA0KICBtdXRhdGUoQWdlU3ViamVjdCA9IGZhY3RvcihBZ2VTdWJqZWN0LCBsZXZlbHMgPSBjKCJ5b3VuZyIsICJvbGQiKSwgbGFiZWxzID0gYygiWW91bmciLCAiT2xkIikpLA0KICAgICAgICAgV29yZENhdGVnb3J5ID0gZmFjdG9yKFdvcmRDYXRlZ29yeSwgbGFiZWxzID0gYygiTm91biIsICJWZXJiIikpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHZhcmlhYmxlLCANCiAgICAgICAgICAgICB5ID0gdmFsdWVzKSkgKw0KICB0aGVtZV9idygpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBmYWNldF9ncmlkKEFnZVN1YmplY3QgfiBXb3JkQ2F0ZWdvcnksIG1hcmdpbnMgPSBUUlVFLCBzY2FsZXMgPSAiZnJlZSIpDQpgYGANCg0KDQojIyBFeHBvcnRpbmcgaW1hZ2VzDQoNCldoZW4geW91IHVzZSBSbWFya2Rvd24sIHlvdXIgZmlndXJlcyBhcmUgYWxyZWFkeSBlbWJlZGRlZCB3aXRoaW4gdGhlIGdlbmVyYXRlZCBvdXRwdXQuIElmIHlvdSBhcmUgdXNpbmcgYW4gUiBzY3JpcHQgYW5kL29yIHdhbnQgdG8gYWRkIHRoZSBmaWd1cmUgaW4gYSBkaWZmZXJlbnQgZG9jdW1lbnQsIHlvdSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgY29kZToNCg0KDQpgYGB7cn0NCmpwZWcoZmlsZW5hbWUgPSAidGVzdC5qcGVnIiwgd2lkdGggPSAxNSwgaGVpZ2h0ID0gMTUsIHVuaXRzID0gImNtIiwgcmVzID0gMzAwKQ0KDQplbmdsaXNoICU+JQ0KICBzZWxlY3QoUlRsZXhkZWMsIFJUbmFtaW5nLCBBZ2VTdWJqZWN0LCBXb3JkQ2F0ZWdvcnkpICU+JSANCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKFJUbGV4ZGVjLCBSVG5hbWluZyksDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJ2YXJpYWJsZSIsDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWVzIikgJT4lIA0KICBtdXRhdGUoQWdlU3ViamVjdCA9IGZhY3RvcihBZ2VTdWJqZWN0LCBsZXZlbHMgPSBjKCJ5b3VuZyIsICJvbGQiKSwgbGFiZWxzID0gYygiWW91bmciLCAiT2xkIikpLA0KICAgICAgICAgV29yZENhdGVnb3J5ID0gZmFjdG9yKFdvcmRDYXRlZ29yeSwgbGFiZWxzID0gYygiTm91biIsICJWZXJiIikpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHZhcmlhYmxlLCANCiAgICAgICAgICAgICB5ID0gdmFsdWVzKSkgKw0KICB0aGVtZV9idygpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBmYWNldF9ncmlkKEFnZVN1YmplY3QgfiBXb3JkQ2F0ZWdvcnksIG1hcmdpbnMgPSBUUlVFLCBzY2FsZXMgPSAiZnJlZSIpDQpkZXYub2ZmKCkNCg0KYGBgDQoNClRoZSBpbWFnZSBpcyBhdXRvbWF0aWNhbGx5IHNhdmVkIGludG8geW91ciB3b3JraW5nIGRpcmVjdG9yeSBhbmQgeW91IGNhbiBpbXBvcnQgaXQgdG8geW91ciB3b3JkIChcTGFUZVgpIGRvY3VtZW50Lg0KDQpZb3UgY2FuIHVzZSBhbnkgZGV2aWNlIHRvIHNhdmUgdGhlIG91dHB1dC4gSnBlZywgUE5HLCBQREYsIFRJRkYsIGV0Yy4uIA0KRnJvbSBhbiBSIHNjcmlwdCwgeW91IGNhbiBydW4gdGhlIGNvZGUgYW5kIHRoZW4gdGhlIGltYWdlIHdpbGwgYXBwZWFyIHdpdGhpbiB0aGUgIlBsb3RzIiBhcmVhLiBTaW1wbHkgY2xpY2sgb24gZXhwb3J0IGFuZCB5b3Ugd2lsbCBiZSBhYmxlIHRvIHNhdmUgdGhlIGltYWdlLg0KDQoNCiMjIENvbmNsdXNpb24NCg0KQXMgeW91IGNhbiBzZWUsIHZpc3VhbGlzYXRpb25zIGluIGBSYCB1c2luZyB0aGUgYFRpZHl2ZXJzZWAgcHJvdmlkZSB5b3Ugd2l0aCBtYW55IG9wdGlvbnMgYW5kIHlvdSBjYW4gZXhwbG9yZSB0aGVzZSBmdXJ0aGVyLg0KDQpTZWUgW2hlcmVdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS8pIGZvciBhIGZ1bGwgbGlzdCBvZiBnZW9tcy4gVGhpcyB3aWxsIGhlbHAgeW91IGluIHRoaW5raW5nIGFib3V0IHZpc3VhbGlzYXRpb24uDQoNClNlZSBleHRlbnNpb25zIHRvIGdncGxvdDIgW2hlcmVdKGh0dHBzOi8vZXh0cy5nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvZ2FsbGVyeS8pIGZvciBhZGRpdGlvbmFsIHBsdWdpbnMgdG8gZW5oYW5jZSBwbG90cy4NCg0KSW4gdGhlIG5leHQgc2VjdGlvbiwgd2Ugc3RhcnQgd2l0aCBpbml0aWFsIGluZmVyZW50aWFsIHN0YXRpc3RpY3MuDQoNCg0KDQojIyBHb2luZyBmdXJ0aGVyDQoNClNlZSBbaGVyZV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlLykgZm9yIGEgZnVsbCBsaXN0IG9mIGdlb21zLiBUaGlzIHdpbGwgaGVscCB5b3UgaW4gdGhpbmtpbmcgYWJvdXQgdmlzdWFsaXNhdGlvbi4NCg0KU2VlIGV4dGVuc2lvbnMgdG8gZ2dwbG90MiBbaGVyZV0oaHR0cHM6Ly9leHRzLmdncGxvdDIudGlkeXZlcnNlLm9yZy9nYWxsZXJ5LykgZm9yIGFkZGl0aW9uYWwgcGx1Z2lucyB0byBlbmhhbmNlIHBsb3RzLiANCg0KIyBFbmQgb2YgdGhlIHNlc3Npb24NCg0KVGhpcyBpcyB0aGUgZW5kIG9mIHRoZSB0aGlyZCBzZXNzaW9uLiBXZSB1c2VkIHRoZSBwYWNrYWdlIGBUaWR5dmVyc2VgIHRvIG1hbmlwdWxhdGUgb2JqZWN0cy4gV2Ugb2J0YWluZWQgdGhlbiBiYXNpYyBzdW1tYXJpZXMgYW5kIGJhc2ljIHBsb3RzLiBXZSBsb29rZWQgYXQgaG93IHRvIGJ1aWxkIGEgcGxvdCBmcm9tIHNjcmF0Y2ggDQoNCk5leHQgd2Vlaywgd2Ugd2lsbCBsb29rIGF0IGJhc2ljIGluZmVyZW50aWFsIHN0YXRpc3RpY3MNCg0KDQojIHNlc3Npb24gaW5mbw0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCnNlc3Npb25JbmZvKCkNCmBgYA0KDQoNCg==