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)
}
The Tidyverse
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
.
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 %>%
.
First steps
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()
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
Adding geoms
Geoms are integrated within ggplot2
to obtain various types of plots.
english %>%
ggplot(aes(x = Familiarity,
y = RTlexdec)) +
theme_bw() +
geom_point()
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?
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'
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
Activity on your own 3
Work through a few examples to plot data
Additional plots
We looked above at one example of plots (with points). We could use additional types of plots.
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)
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`.
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()
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()
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()
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
Two categorical variables
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)
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!
Activity on your own 1
english %>%
ggplot(aes(x = AgeSubject,
y = RTlexdec)) +
theme_bw() +
geom_boxplot() +
facet_grid(~ WordCategory)
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)
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")
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")
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.
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.
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.
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
session info
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==