Ansgar Wolsing’s contribution to 30 Day Chart Challenge, 2022 Edition.
Code on GitHub (very slightly modified).
## remotes::install_github("davidsjoberg/ggstream")
library(ggstream)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.4
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.4.4 ✔ tibble 3.2.1
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.0
## ✔ purrr 1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(tidytext)
library(ggtext)
## download text from Project Gutenberg
if (! file.exists("macbeth.txt")) {
macbeth_url <- "https://www.gutenberg.org/cache/epub/1129/pg1129.txt"
download.file(macbeth_url, "macbeth.txt")
}
macbeth_lines <- read_lines("macbeth.txt")
## Lines where the manuscript starts and ends
start_line <- 282
end_line <- 2899
exclude_lines <- 2358:2365
text_df <- tibble(line = macbeth_lines[start_line:end_line]) |>
mutate(line = str_squish(line),
line_id = row_number()) |>
## remove empty or irrelevant lines
filter(line != "" | line_id %in% exclude_lines) |>
filter(!str_detect(line,
"SERVICE THAT CHARGES|WITH PERMISSION|COMMERCIALLY")) |>
## extract the act and scene
mutate(act = str_extract(line, "^ACT\\b.+?\\."),
scene = str_extract(line, "SCENE\\b.+?\\.")) |>
fill(act, scene, .direction = "down") |>
## extract the character name who speaks
mutate(speaker = str_extract(line, "^[A-Z\\s]+\\."),
speaker = str_remove(speaker, "\\.$"),
speaker = ifelse(str_detect(speaker, "^ACT|SCENE\\b"),
NA_character_, speaker),
speaker = str_to_title(speaker)) |>
## manage the switch of speakers from scene to scene
group_by(act, scene) |>
fill(speaker, .direction = "down") |>
ungroup() |>
## remove lines without a speaker
filter(!is.na(speaker)) |>
## remove speaker names from the lines
mutate(line = str_remove(line, paste0(speaker, ". ")))
## count the number of words per speaker in each act and scene
word_count_speakers <- text_df |>
## recode the witches and murderers into one category
mutate(speaker_grp = ifelse(str_detect(speaker, "Witch$"),
"Three Witches", speaker),
speaker_grp = ifelse(str_detect(speaker_grp, "Mutherers?$"),
"Mutherers", speaker_grp)) |>
unnest_tokens(word, line, token = "words", drop = TRUE) |>
count(act, scene, speaker_grp, speaker, name = "word_count")
## identify character with only a few appearances
few_appearances_speakers <- word_count_speakers |>
group_by(speaker_grp) |>
summarize(scenes_count = n_distinct(act, scene),
word_count_total = sum(word_count)) |>
filter(scenes_count <= 3, word_count_total < 500) |>
pull(speaker_grp)
## Custom color palette by character affiliation
speaker_grp_levels <- c(
"Macbeth", "Lady Macbeth",
"Duncan", "Malcolm", "Macduff", "Ross",
"Banquo", "Lennox",
"Three Witches",
"Other")
color_palette <- paletteer::paletteer_d(
"palettetown::pidgey")[c(9, 8,
2, 5, 4, 6,
12, 6,
3,
11)]
## Annotations
plot_titles <- list(
title = "Who speaks when in Shakespeare's MACBETH?",
subtitle = "Distribution of speech share (number of words) per character in
each scene. Acts are separated with vertical lines.",
caption = "Project Gutenberg. Visualization: Ansgar Wolsing"
)
## highlight key events - used for text and lines
story_annotations <- tibble(
x = c(13.5, 1.2, 7, 5, 14, 22),
xend = c(19, 1.2, 7, 5, 14, 22),
y = c(-5000, -4200, -4500, 5000, 5000, 3000),
yend = c(-5000, -200, 400, 800, 1000, 500),
vjust = c( 0, 0.25, 0.3, 0.85, 0.9, 0.8), ## nolint: spaces_inside
label = c(
"Macduff & Malcolm decide to go to war against Macbeth",
"Three Witches<br>appear",
"Macbeth kills King Duncan",
"Lady Macbeth & Macbeth<br>plan the murder of King Duncan",
"Murder of Banquo reported to Macbeth,<br>Ghost of Banquo appears",
"Macduff<br>kills<br>Macbeth"))
word_count_speakers |>
mutate(speaker_grp =
ifelse(speaker_grp %in% c("All", few_appearances_speakers),
"Other", speaker_grp),
speaker_grp = factor(speaker_grp, levels = speaker_grp_levels)) |>
count(act, scene, speaker_grp, wt = word_count, name = "word_count") |>
## increment counter across act and scene
group_by(act, scene) |>
mutate(act_scene_id = cur_group_id()) |>
ungroup() |>
ggplot(aes(act_scene_id, word_count, fill = speaker_grp)) +
## vertical lines for the acts
geom_vline(
data = function(d) filter(d, scene == "SCENE I."),
aes(xintercept = act_scene_id),
color = "grey50", size = 0.2, lty = "dotted") +
geom_stream(type = "mirror", bw = 0.5, extra_span = 0.1) +
## annotations for key events (text + segment)
geom_textbox(
data = story_annotations,
aes(x - 0.08, y, label = label, vjust = vjust),
inherit.aes = FALSE,
color = "grey90", family = "Forum", hjust = 0, fill = NA,
box.size = 0,
width = unit(3.5, "cm")) +
geom_segment(
data = story_annotations,
aes(x = x, xend = xend, y = y, yend = yend), inherit.aes = FALSE,
color = "grey90", size = 0.3) +
## text labels for the acts
geom_text(
data = function(d)
group_by(d, act) |>
summarize(x = min(act_scene_id) + n_distinct(scene) / 2),
aes(x, y = -Inf, label = act), inherit.aes = FALSE,
vjust = -1, hjust = 0.5, color = "grey60", family = "Forum") +
scale_fill_manual(values = color_palette) +
labs(
title = plot_titles$title,
subtitle = plot_titles$subtitle,
caption = plot_titles$caption,
fill = NULL) +
theme_void(base_family = "Forum", base_size = 10) +
theme(
plot.background = element_rect(color = NA, fill = "grey8"),
plot.margin = margin(10, 10, 10, 10),
legend.position = "bottom",
legend.direction = "horizontal",
legend.key.height = unit(3, "mm"),
legend.spacing.y = unit(4, "cm"),
legend.text = element_text(size = 9.5),
text = element_text(color = "white"),
plot.title = element_text(size = 24, family = "Forum"),
plot.subtitle = element_markdown(),
plot.caption = element_markdown(hjust = 1))
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

LS0tCnRpdGxlOiAiV2hvIFNwZWFrcyBXaGVuIGluIFNoYWtlc3BlYXJlJ3MgTUFDQkVUSD8iCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogaGlkZQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVkgJUg6JU0nKWAiCi0tLQoKYGBge3IgZ2xvYmFsX29wdGlvbnMsIGluY2x1ZGUgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNvbGxhcHNlID0gVFJVRSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAiY2VudGVyIikKYGBgCkFuc2dhciBXb2xzaW5nJ3MKW2NvbnRyaWJ1dGlvbl0oaHR0cHM6Ly90d2l0dGVyLmNvbS9fYW5zZ2FyL3N0YXR1cy8xNTEzMDg0NjU5MDEzNTA1MDI4KSB0bwpbMzAgRGF5IENoYXJ0IENoYWxsZW5nZSwgMjAyMiBFZGl0aW9uXShodHRwczovL3R3aXR0ZXIuY29tLzMwRGF5Q2hhcnRDaGFsbCkuCgpbQ29kZSBvbiBHaXRIdWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9ieWRhdGEvMzBEYXlDaGFydENoYWxsZW5nZS9ibG9iL21haW4vMjAyMi8xMC8xMC1leHBlcmltZW50YWwtbWFjYmV0aC5SKSAodmVyeSBzbGlnaHRseSBtb2RpZmllZCkuCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4fQojIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiZGF2aWRzam9iZXJnL2dnc3RyZWFtIikKbGlicmFyeShnZ3N0cmVhbSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkoZ2d0ZXh0KQoKIyMgZG93bmxvYWQgdGV4dCBmcm9tIFByb2plY3QgR3V0ZW5iZXJnCmlmICghIGZpbGUuZXhpc3RzKCJtYWNiZXRoLnR4dCIpKSB7CiAgICBtYWNiZXRoX3VybCA8LSAiaHR0cHM6Ly93d3cuZ3V0ZW5iZXJnLm9yZy9jYWNoZS9lcHViLzExMjkvcGcxMTI5LnR4dCIKICAgIGRvd25sb2FkLmZpbGUobWFjYmV0aF91cmwsICJtYWNiZXRoLnR4dCIpCn0KbWFjYmV0aF9saW5lcyA8LSByZWFkX2xpbmVzKCJtYWNiZXRoLnR4dCIpCgojIyBMaW5lcyB3aGVyZSB0aGUgbWFudXNjcmlwdCBzdGFydHMgYW5kIGVuZHMKc3RhcnRfbGluZSA8LSAyODIKZW5kX2xpbmUgPC0gMjg5OQpleGNsdWRlX2xpbmVzIDwtIDIzNTg6MjM2NQoKdGV4dF9kZiA8LSB0aWJibGUobGluZSA9IG1hY2JldGhfbGluZXNbc3RhcnRfbGluZTplbmRfbGluZV0pIHw+CiAgICBtdXRhdGUobGluZSA9IHN0cl9zcXVpc2gobGluZSksCiAgICAgICAgICAgbGluZV9pZCA9IHJvd19udW1iZXIoKSkgfD4KICAgICMjIHJlbW92ZSBlbXB0eSBvciBpcnJlbGV2YW50IGxpbmVzCiAgICBmaWx0ZXIobGluZSAhPSAiIiB8IGxpbmVfaWQgJWluJSBleGNsdWRlX2xpbmVzKSB8PgogICAgZmlsdGVyKCFzdHJfZGV0ZWN0KGxpbmUsCiAgICAgICAgICAgICAgICAgICAgICAgIlNFUlZJQ0UgVEhBVCBDSEFSR0VTfFdJVEggUEVSTUlTU0lPTnxDT01NRVJDSUFMTFkiKSkgfD4KICAgICMjIGV4dHJhY3QgdGhlIGFjdCBhbmQgc2NlbmUKICAgIG11dGF0ZShhY3QgPSBzdHJfZXh0cmFjdChsaW5lLCAiXkFDVFxcYi4rP1xcLiIpLAogICAgICAgICAgIHNjZW5lID0gc3RyX2V4dHJhY3QobGluZSwgIlNDRU5FXFxiLis/XFwuIikpIHw+CiAgICBmaWxsKGFjdCwgc2NlbmUsIC5kaXJlY3Rpb24gPSAiZG93biIpIHw+CiAgICAjIyBleHRyYWN0IHRoZSBjaGFyYWN0ZXIgbmFtZSB3aG8gc3BlYWtzCiAgICBtdXRhdGUoc3BlYWtlciA9IHN0cl9leHRyYWN0KGxpbmUsICJeW0EtWlxcc10rXFwuIiksCiAgICAgICAgICAgc3BlYWtlciA9IHN0cl9yZW1vdmUoc3BlYWtlciwgIlxcLiQiKSwKICAgICAgICAgICBzcGVha2VyID0gaWZlbHNlKHN0cl9kZXRlY3Qoc3BlYWtlciwgIl5BQ1R8U0NFTkVcXGIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX2NoYXJhY3Rlcl8sIHNwZWFrZXIpLAogICAgICAgICAgIHNwZWFrZXIgPSBzdHJfdG9fdGl0bGUoc3BlYWtlcikpIHw+CiAgICAjIyBtYW5hZ2UgdGhlIHN3aXRjaCBvZiBzcGVha2VycyBmcm9tIHNjZW5lIHRvIHNjZW5lCiAgICBncm91cF9ieShhY3QsIHNjZW5lKSB8PgogICAgZmlsbChzcGVha2VyLCAuZGlyZWN0aW9uID0gImRvd24iKSB8PgogICAgdW5ncm91cCgpIHw+CiAgICAjIyByZW1vdmUgbGluZXMgd2l0aG91dCBhIHNwZWFrZXIKICAgIGZpbHRlcighaXMubmEoc3BlYWtlcikpIHw+CiAgICAjIyByZW1vdmUgc3BlYWtlciBuYW1lcyBmcm9tIHRoZSBsaW5lcwogICAgbXV0YXRlKGxpbmUgPSBzdHJfcmVtb3ZlKGxpbmUsIHBhc3RlMChzcGVha2VyLCAiLiAiKSkpCgojIyBjb3VudCB0aGUgbnVtYmVyIG9mIHdvcmRzIHBlciBzcGVha2VyIGluIGVhY2ggYWN0IGFuZCBzY2VuZQp3b3JkX2NvdW50X3NwZWFrZXJzIDwtIHRleHRfZGYgfD4KICAgICMjIHJlY29kZSB0aGUgd2l0Y2hlcyBhbmQgbXVyZGVyZXJzIGludG8gb25lIGNhdGVnb3J5CiAgICBtdXRhdGUoc3BlYWtlcl9ncnAgPSBpZmVsc2Uoc3RyX2RldGVjdChzcGVha2VyLCAiV2l0Y2gkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRocmVlIFdpdGNoZXMiLCBzcGVha2VyKSwKICAgICAgICAgICBzcGVha2VyX2dycCA9IGlmZWxzZShzdHJfZGV0ZWN0KHNwZWFrZXJfZ3JwLCAiTXV0aGVyZXJzPyQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTXV0aGVyZXJzIiwgc3BlYWtlcl9ncnApKSB8PgogICAgdW5uZXN0X3Rva2Vucyh3b3JkLCBsaW5lLCB0b2tlbiA9ICJ3b3JkcyIsIGRyb3AgPSBUUlVFKSB8PgogICAgY291bnQoYWN0LCBzY2VuZSwgc3BlYWtlcl9ncnAsIHNwZWFrZXIsIG5hbWUgPSAid29yZF9jb3VudCIpCgojIyBpZGVudGlmeSBjaGFyYWN0ZXIgd2l0aCBvbmx5IGEgZmV3IGFwcGVhcmFuY2VzCmZld19hcHBlYXJhbmNlc19zcGVha2VycyA8LSB3b3JkX2NvdW50X3NwZWFrZXJzIHw+CiAgICBncm91cF9ieShzcGVha2VyX2dycCkgfD4KICAgIHN1bW1hcml6ZShzY2VuZXNfY291bnQgPSBuX2Rpc3RpbmN0KGFjdCwgc2NlbmUpLAogICAgICAgICAgICAgIHdvcmRfY291bnRfdG90YWwgPSBzdW0od29yZF9jb3VudCkpIHw+CiAgICBmaWx0ZXIoc2NlbmVzX2NvdW50IDw9IDMsIHdvcmRfY291bnRfdG90YWwgPCA1MDApIHw+CiAgICBwdWxsKHNwZWFrZXJfZ3JwKQoKIyMgQ3VzdG9tIGNvbG9yIHBhbGV0dGUgYnkgY2hhcmFjdGVyIGFmZmlsaWF0aW9uCnNwZWFrZXJfZ3JwX2xldmVscyA8LSBjKAogICJNYWNiZXRoIiwgIkxhZHkgTWFjYmV0aCIsCiAgIkR1bmNhbiIsICJNYWxjb2xtIiwgIk1hY2R1ZmYiLCAiUm9zcyIsCiAgIkJhbnF1byIsICJMZW5ub3giLAogICJUaHJlZSBXaXRjaGVzIiwKICAiT3RoZXIiKQpjb2xvcl9wYWxldHRlIDwtIHBhbGV0dGVlcjo6cGFsZXR0ZWVyX2QoCiAgInBhbGV0dGV0b3duOjpwaWRnZXkiKVtjKDksIDgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDIsIDUsIDQsIDYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDEyLCA2LAogICAgICAgICAgICAgICAgICAgICAgICAgICAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAxMSldCgojIyBBbm5vdGF0aW9ucwpwbG90X3RpdGxlcyA8LSBsaXN0KAogICAgdGl0bGUgPSAiV2hvIHNwZWFrcyB3aGVuIGluIFNoYWtlc3BlYXJlJ3MgTUFDQkVUSD8iLAogICAgc3VidGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIHNwZWVjaCBzaGFyZSAobnVtYmVyIG9mIHdvcmRzKSBwZXIgY2hhcmFjdGVyIGluCiAgZWFjaCBzY2VuZS4gQWN0cyBhcmUgc2VwYXJhdGVkIHdpdGggdmVydGljYWwgbGluZXMuIiwKICBjYXB0aW9uID0gIlByb2plY3QgR3V0ZW5iZXJnLiBWaXN1YWxpemF0aW9uOiBBbnNnYXIgV29sc2luZyIKKQoKIyMgaGlnaGxpZ2h0IGtleSBldmVudHMgLSB1c2VkIGZvciB0ZXh0IGFuZCBsaW5lcwpzdG9yeV9hbm5vdGF0aW9ucyA8LSB0aWJibGUoCiAgICB4ICAgID0gYygxMy41LCAxLjIsIDcsIDUsIDE0LCAyMiksCiAgICB4ZW5kID0gYygxOSwgICAxLjIsIDcsIDUsIDE0LCAyMiksCiAgICB5ICAgID0gYygtNTAwMCwgLTQyMDAsIC00NTAwLCA1MDAwLCA1MDAwLCAzMDAwKSwKICAgIHllbmQgPSBjKC01MDAwLCAgLTIwMCwgICA0MDAsICA4MDAsIDEwMDAsICA1MDApLAogICAgdmp1c3QgPSBjKCAgIDAsICAwLjI1LCAgIDAuMywgIDAuODUsICAwLjksICAwLjgpLCAjIyBub2xpbnQ6IHNwYWNlc19pbnNpZGUKICAgIGxhYmVsID0gYygKICAgICAgICAiTWFjZHVmZiAmIE1hbGNvbG0gZGVjaWRlIHRvIGdvIHRvIHdhciBhZ2FpbnN0IE1hY2JldGgiLAogICAgICAgICJUaHJlZSBXaXRjaGVzPGJyPmFwcGVhciIsCiAgICAgICAgIk1hY2JldGgga2lsbHMgS2luZyBEdW5jYW4iLAogICAgICAgICJMYWR5IE1hY2JldGggJiBNYWNiZXRoPGJyPnBsYW4gdGhlIG11cmRlciBvZiBLaW5nIER1bmNhbiIsCiAgICAgICAgIk11cmRlciBvZiBCYW5xdW8gcmVwb3J0ZWQgdG8gTWFjYmV0aCw8YnI+R2hvc3Qgb2YgQmFucXVvIGFwcGVhcnMiLAogICAgICAgICJNYWNkdWZmPGJyPmtpbGxzPGJyPk1hY2JldGgiKSkKCndvcmRfY291bnRfc3BlYWtlcnMgfD4KICAgIG11dGF0ZShzcGVha2VyX2dycCA9CiAgICAgICAgICAgICAgIGlmZWxzZShzcGVha2VyX2dycCAlaW4lIGMoIkFsbCIsIGZld19hcHBlYXJhbmNlc19zcGVha2VycyksCiAgICAgICAgICAgICAgICAgICAgICAiT3RoZXIiLCBzcGVha2VyX2dycCksCiAgICAgICAgICAgc3BlYWtlcl9ncnAgPSBmYWN0b3Ioc3BlYWtlcl9ncnAsIGxldmVscyA9IHNwZWFrZXJfZ3JwX2xldmVscykpIHw+CiAgICBjb3VudChhY3QsIHNjZW5lLCBzcGVha2VyX2dycCwgd3QgPSB3b3JkX2NvdW50LCBuYW1lID0gIndvcmRfY291bnQiKSB8PgogICAgIyMgaW5jcmVtZW50IGNvdW50ZXIgYWNyb3NzIGFjdCBhbmQgc2NlbmUKICAgIGdyb3VwX2J5KGFjdCwgc2NlbmUpIHw+CiAgICBtdXRhdGUoYWN0X3NjZW5lX2lkID0gY3VyX2dyb3VwX2lkKCkpIHw+CiAgICB1bmdyb3VwKCkgfD4KICAgIGdncGxvdChhZXMoYWN0X3NjZW5lX2lkLCB3b3JkX2NvdW50LCBmaWxsID0gc3BlYWtlcl9ncnApKSArCiAgICAjIyB2ZXJ0aWNhbCBsaW5lcyBmb3IgdGhlIGFjdHMKICAgIGdlb21fdmxpbmUoCiAgICAgICAgZGF0YSA9IGZ1bmN0aW9uKGQpIGZpbHRlcihkLCBzY2VuZSA9PSAiU0NFTkUgSS4iKSwKICAgICAgICBhZXMoeGludGVyY2VwdCA9IGFjdF9zY2VuZV9pZCksCiAgICAgICAgY29sb3IgPSAiZ3JleTUwIiwgc2l6ZSA9IDAuMiwgbHR5ID0gImRvdHRlZCIpICsKICAgIGdlb21fc3RyZWFtKHR5cGUgPSAibWlycm9yIiwgYncgPSAwLjUsICBleHRyYV9zcGFuID0gMC4xKSArCiAgICAjIyBhbm5vdGF0aW9ucyBmb3Iga2V5IGV2ZW50cyAodGV4dCArIHNlZ21lbnQpCiAgICBnZW9tX3RleHRib3goCiAgICAgICAgZGF0YSA9IHN0b3J5X2Fubm90YXRpb25zLAogICAgICAgIGFlcyh4IC0gMC4wOCwgeSwgbGFiZWwgPSBsYWJlbCwgdmp1c3QgPSB2anVzdCksCiAgICAgICAgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICBjb2xvciA9ICJncmV5OTAiLCBmYW1pbHkgPSAiRm9ydW0iLCBoanVzdCA9IDAsIGZpbGwgPSBOQSwKICAgICAgICBib3guc2l6ZSA9IDAsCiAgICAgICAgd2lkdGggPSB1bml0KDMuNSwgImNtIikpICsKICAgIGdlb21fc2VnbWVudCgKICAgICAgICBkYXRhID0gc3RvcnlfYW5ub3RhdGlvbnMsCiAgICAgICAgYWVzKHggPSB4LCB4ZW5kID0geGVuZCwgeSA9IHksIHllbmQgPSB5ZW5kKSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICBjb2xvciA9ICJncmV5OTAiLCBzaXplID0gMC4zKSArCiAgICAjIyB0ZXh0IGxhYmVscyBmb3IgdGhlIGFjdHMKICAgIGdlb21fdGV4dCgKICAgICAgICBkYXRhID0gZnVuY3Rpb24oZCkKICAgICAgICAgICAgZ3JvdXBfYnkoZCwgYWN0KSB8PgogICAgICAgICAgICBzdW1tYXJpemUoeCA9IG1pbihhY3Rfc2NlbmVfaWQpICsgbl9kaXN0aW5jdChzY2VuZSkgLyAyKSwKICAgICAgICBhZXMoeCwgeSA9IC1JbmYsIGxhYmVsID0gYWN0KSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICB2anVzdCA9IC0xLCBoanVzdCA9IDAuNSwgY29sb3IgPSAiZ3JleTYwIiwgZmFtaWx5ID0gIkZvcnVtIikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JfcGFsZXR0ZSkgKwogICAgbGFicygKICAgICAgICB0aXRsZSA9IHBsb3RfdGl0bGVzJHRpdGxlLAogICAgICAgIHN1YnRpdGxlID0gcGxvdF90aXRsZXMkc3VidGl0bGUsCiAgICAgICAgY2FwdGlvbiA9IHBsb3RfdGl0bGVzJGNhcHRpb24sCiAgICAgICAgZmlsbCA9IE5VTEwpICsKICAgIHRoZW1lX3ZvaWQoYmFzZV9mYW1pbHkgPSAiRm9ydW0iLCBiYXNlX3NpemUgPSAxMCkgKwogICAgdGhlbWUoCiAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yID0gTkEsIGZpbGwgPSAiZ3JleTgiKSwKICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxMCwgMTAsIDEwLCAxMCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMywgIm1tIiksCiAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoNCwgImNtIiksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkuNSksCiAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJ3aGl0ZSIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDI0LCBmYW1pbHkgPSAiRm9ydW0iKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF9tYXJrZG93bigpLAogICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfbWFya2Rvd24oaGp1c3QgPSAxKSkKYGBgCg==