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.2     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ ggplot2   3.4.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.2     ✔ tidyr     1.3.0
## ✔ purrr     1.0.1     
## ── 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 = . %>% filter(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 = . %>%
            group_by(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.

LS0tCnRpdGxlOiAiV2hvIFNwZWFrcyBXaGVuIGluIFNoYWtlc3BlYXJlJ3MgTUFDQkVUSD8iCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogaGlkZQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVkgJUg6JU0nKWAiCi0tLQoKYGBge3IgZ2xvYmFsX29wdGlvbnMsIGluY2x1ZGUgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNvbGxhcHNlID0gVFJVRSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAiY2VudGVyIikKYGBgCkFuc2dhciBXb2xzaW5nJ3MKW2NvbnRyaWJ1dGlvbl0oaHR0cHM6Ly90d2l0dGVyLmNvbS9fYW5zZ2FyL3N0YXR1cy8xNTEzMDg0NjU5MDEzNTA1MDI4KSB0bwpbMzAgRGF5IENoYXJ0IENoYWxsZW5nZSwgMjAyMiBFZGl0aW9uXShodHRwczovL3R3aXR0ZXIuY29tLzMwRGF5Q2hhcnRDaGFsbCkuCgpbQ29kZSBvbiBHaXRIdWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9ieWRhdGEvMzBEYXlDaGFydENoYWxsZW5nZS9ibG9iL21haW4vMjAyMi8xMC8xMC1leHBlcmltZW50YWwtbWFjYmV0aC5SKSAodmVyeSBzbGlnaHRseSBtb2RpZmllZCkuCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4fQojIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiZGF2aWRzam9iZXJnL2dnc3RyZWFtIikKbGlicmFyeShnZ3N0cmVhbSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkoZ2d0ZXh0KQoKIyMgZG93bmxvYWQgdGV4dCBmcm9tIFByb2plY3QgR3V0ZW5iZXJnCmlmICghIGZpbGUuZXhpc3RzKCJtYWNiZXRoLnR4dCIpKSB7CiAgICBtYWNiZXRoX3VybCA8LSAiaHR0cHM6Ly93d3cuZ3V0ZW5iZXJnLm9yZy9jYWNoZS9lcHViLzExMjkvcGcxMTI5LnR4dCIKICAgIGRvd25sb2FkLmZpbGUobWFjYmV0aF91cmwsICJtYWNiZXRoLnR4dCIpCn0KbWFjYmV0aF9saW5lcyA8LSByZWFkX2xpbmVzKCJtYWNiZXRoLnR4dCIpCgojIyBMaW5lcyB3aGVyZSB0aGUgbWFudXNjcmlwdCBzdGFydHMgYW5kIGVuZHMKc3RhcnRfbGluZSA8LSAyODIKZW5kX2xpbmUgPC0gMjg5OQpleGNsdWRlX2xpbmVzIDwtIDIzNTg6MjM2NQoKdGV4dF9kZiA8LSB0aWJibGUobGluZSA9IG1hY2JldGhfbGluZXNbc3RhcnRfbGluZTplbmRfbGluZV0pICU+JQogICAgbXV0YXRlKGxpbmUgPSBzdHJfc3F1aXNoKGxpbmUpLAogICAgICAgICAgIGxpbmVfaWQgPSByb3dfbnVtYmVyKCkpICU+JQogICAgIyMgcmVtb3ZlIGVtcHR5IG9yIGlycmVsZXZhbnQgbGluZXMKICAgIGZpbHRlcihsaW5lICE9ICIiIHwgbGluZV9pZCAlaW4lIGV4Y2x1ZGVfbGluZXMpICU+JQogICAgZmlsdGVyKCFzdHJfZGV0ZWN0KGxpbmUsCiAgICAgICAgICAgICAgICAgICAgICAgIlNFUlZJQ0UgVEhBVCBDSEFSR0VTfFdJVEggUEVSTUlTU0lPTnxDT01NRVJDSUFMTFkiKSkgJT4lCiAgICAjIyBleHRyYWN0IHRoZSBhY3QgYW5kIHNjZW5lCiAgICBtdXRhdGUoYWN0ID0gc3RyX2V4dHJhY3QobGluZSwgIl5BQ1RcXGIuKz9cXC4iKSwKICAgICAgICAgICBzY2VuZSA9IHN0cl9leHRyYWN0KGxpbmUsICJTQ0VORVxcYi4rP1xcLiIpKSAlPiUKICAgIGZpbGwoYWN0LCBzY2VuZSwgLmRpcmVjdGlvbiA9ICJkb3duIikgJT4lCiAgICAjIyBleHRyYWN0IHRoZSBjaGFyYWN0ZXIgbmFtZSB3aG8gc3BlYWtzCiAgICBtdXRhdGUoc3BlYWtlciA9IHN0cl9leHRyYWN0KGxpbmUsICJeW0EtWlxcc10rXFwuIiksCiAgICAgICAgICAgc3BlYWtlciA9IHN0cl9yZW1vdmUoc3BlYWtlciwgIlxcLiQiKSwKICAgICAgICAgICBzcGVha2VyID0gaWZlbHNlKHN0cl9kZXRlY3Qoc3BlYWtlciwgIl5BQ1R8U0NFTkVcXGIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX2NoYXJhY3Rlcl8sIHNwZWFrZXIpLAogICAgICAgICAgIHNwZWFrZXIgPSBzdHJfdG9fdGl0bGUoc3BlYWtlcikpICU+JQogICAgIyMgbWFuYWdlIHRoZSBzd2l0Y2ggb2Ygc3BlYWtlcnMgZnJvbSBzY2VuZSB0byBzY2VuZQogICAgZ3JvdXBfYnkoYWN0LCBzY2VuZSkgJT4lCiAgICBmaWxsKHNwZWFrZXIsIC5kaXJlY3Rpb24gPSAiZG93biIpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgIyMgcmVtb3ZlIGxpbmVzIHdpdGhvdXQgYSBzcGVha2VyCiAgICBmaWx0ZXIoIWlzLm5hKHNwZWFrZXIpKSAlPiUKICAgICMjIHJlbW92ZSBzcGVha2VyIG5hbWVzIGZyb20gdGhlIGxpbmVzCiAgICBtdXRhdGUobGluZSA9IHN0cl9yZW1vdmUobGluZSwgcGFzdGUwKHNwZWFrZXIsICIuICIpKSkKCiMjIGNvdW50IHRoZSBudW1iZXIgb2Ygd29yZHMgcGVyIHNwZWFrZXIgaW4gZWFjaCBhY3QgYW5kIHNjZW5lCndvcmRfY291bnRfc3BlYWtlcnMgPC0gdGV4dF9kZiAlPiUKICAgICMjIHJlY29kZSB0aGUgd2l0Y2hlcyBhbmQgbXVyZGVyZXJzIGludG8gb25lIGNhdGVnb3J5CiAgICBtdXRhdGUoc3BlYWtlcl9ncnAgPSBpZmVsc2Uoc3RyX2RldGVjdChzcGVha2VyLCAiV2l0Y2gkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRocmVlIFdpdGNoZXMiLCBzcGVha2VyKSwKICAgICAgICAgICBzcGVha2VyX2dycCA9IGlmZWxzZShzdHJfZGV0ZWN0KHNwZWFrZXJfZ3JwLCAiTXV0aGVyZXJzPyQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTXV0aGVyZXJzIiwgc3BlYWtlcl9ncnApKSAlPiUKICAgIHVubmVzdF90b2tlbnMod29yZCwgbGluZSwgdG9rZW4gPSAid29yZHMiLCBkcm9wID0gVFJVRSkgJT4lCiAgICBjb3VudChhY3QsIHNjZW5lLCBzcGVha2VyX2dycCwgc3BlYWtlciwgbmFtZSA9ICJ3b3JkX2NvdW50IikKCiMjIGlkZW50aWZ5IGNoYXJhY3RlciB3aXRoIG9ubHkgYSBmZXcgYXBwZWFyYW5jZXMKZmV3X2FwcGVhcmFuY2VzX3NwZWFrZXJzIDwtIHdvcmRfY291bnRfc3BlYWtlcnMgJT4lCiAgICBncm91cF9ieShzcGVha2VyX2dycCkgJT4lCiAgICBzdW1tYXJpemUoc2NlbmVzX2NvdW50ID0gbl9kaXN0aW5jdChhY3QsIHNjZW5lKSwKICAgICAgICAgICAgICB3b3JkX2NvdW50X3RvdGFsID0gc3VtKHdvcmRfY291bnQpKSAlPiUKICAgIGZpbHRlcihzY2VuZXNfY291bnQgPD0gMywgd29yZF9jb3VudF90b3RhbCA8IDUwMCkgJT4lCiAgICBwdWxsKHNwZWFrZXJfZ3JwKQoKIyMgQ3VzdG9tIGNvbG9yIHBhbGV0dGUgYnkgY2hhcmFjdGVyIGFmZmlsaWF0aW9uCnNwZWFrZXJfZ3JwX2xldmVscyA8LSBjKAogICJNYWNiZXRoIiwgIkxhZHkgTWFjYmV0aCIsCiAgIkR1bmNhbiIsICJNYWxjb2xtIiwgIk1hY2R1ZmYiLCAiUm9zcyIsCiAgIkJhbnF1byIsICJMZW5ub3giLAogICJUaHJlZSBXaXRjaGVzIiwKICAiT3RoZXIiKQpjb2xvcl9wYWxldHRlIDwtIHBhbGV0dGVlcjo6cGFsZXR0ZWVyX2QoCiAgInBhbGV0dGV0b3duOjpwaWRnZXkiKVtjKDksIDgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDIsIDUsIDQsIDYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDEyLCA2LAogICAgICAgICAgICAgICAgICAgICAgICAgICAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAxMSldCgojIyBBbm5vdGF0aW9ucwpwbG90X3RpdGxlcyA8LSBsaXN0KAogICAgdGl0bGUgPSAiV2hvIHNwZWFrcyB3aGVuIGluIFNoYWtlc3BlYXJlJ3MgTUFDQkVUSD8iLAogICAgc3VidGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIHNwZWVjaCBzaGFyZSAobnVtYmVyIG9mIHdvcmRzKSBwZXIgY2hhcmFjdGVyIGluCiAgZWFjaCBzY2VuZS4gQWN0cyBhcmUgc2VwYXJhdGVkIHdpdGggdmVydGljYWwgbGluZXMuIiwKICBjYXB0aW9uID0gIlByb2plY3QgR3V0ZW5iZXJnLiBWaXN1YWxpemF0aW9uOiBBbnNnYXIgV29sc2luZyIKKQoKIyMgaGlnaGxpZ2h0IGtleSBldmVudHMgLSB1c2VkIGZvciB0ZXh0IGFuZCBsaW5lcwpzdG9yeV9hbm5vdGF0aW9ucyA8LSB0aWJibGUoCiAgICB4ICAgID0gYygxMy41LCAxLjIsIDcsIDUsIDE0LCAyMiksCiAgICB4ZW5kID0gYygxOSwgICAxLjIsIDcsIDUsIDE0LCAyMiksCiAgICB5ICAgID0gYygtNTAwMCwgLTQyMDAsIC00NTAwLCA1MDAwLCA1MDAwLCAzMDAwKSwKICAgIHllbmQgPSBjKC01MDAwLCAgLTIwMCwgICA0MDAsICA4MDAsIDEwMDAsICA1MDApLAogICAgdmp1c3QgPSBjKCAgIDAsICAwLjI1LCAgIDAuMywgIDAuODUsICAwLjksICAwLjgpLCAjIyBub2xpbnQ6IHNwYWNlc19pbnNpZGUKICAgIGxhYmVsID0gYygKICAgICAgICAiTWFjZHVmZiAmIE1hbGNvbG0gZGVjaWRlIHRvIGdvIHRvIHdhciBhZ2FpbnN0IE1hY2JldGgiLAogICAgICAgICJUaHJlZSBXaXRjaGVzPGJyPmFwcGVhciIsCiAgICAgICAgIk1hY2JldGgga2lsbHMgS2luZyBEdW5jYW4iLAogICAgICAgICJMYWR5IE1hY2JldGggJiBNYWNiZXRoPGJyPnBsYW4gdGhlIG11cmRlciBvZiBLaW5nIER1bmNhbiIsCiAgICAgICAgIk11cmRlciBvZiBCYW5xdW8gcmVwb3J0ZWQgdG8gTWFjYmV0aCw8YnI+R2hvc3Qgb2YgQmFucXVvIGFwcGVhcnMiLAogICAgICAgICJNYWNkdWZmPGJyPmtpbGxzPGJyPk1hY2JldGgiKSkKCndvcmRfY291bnRfc3BlYWtlcnMgJT4lCiAgICBtdXRhdGUoc3BlYWtlcl9ncnAgPQogICAgICAgICAgICAgICBpZmVsc2Uoc3BlYWtlcl9ncnAgJWluJSBjKCJBbGwiLCBmZXdfYXBwZWFyYW5jZXNfc3BlYWtlcnMpLAogICAgICAgICAgICAgICAgICAgICAgIk90aGVyIiwgc3BlYWtlcl9ncnApLAogICAgICAgICAgIHNwZWFrZXJfZ3JwID0gZmFjdG9yKHNwZWFrZXJfZ3JwLCBsZXZlbHMgPSBzcGVha2VyX2dycF9sZXZlbHMpKSAlPiUKICAgIGNvdW50KGFjdCwgc2NlbmUsIHNwZWFrZXJfZ3JwLCB3dCA9IHdvcmRfY291bnQsIG5hbWUgPSAid29yZF9jb3VudCIpICU+JQogICAgIyMgaW5jcmVtZW50IGNvdW50ZXIgYWNyb3NzIGFjdCBhbmQgc2NlbmUKICAgIGdyb3VwX2J5KGFjdCwgc2NlbmUpICU+JQogICAgbXV0YXRlKGFjdF9zY2VuZV9pZCA9IGN1cl9ncm91cF9pZCgpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIGdncGxvdChhZXMoYWN0X3NjZW5lX2lkLCB3b3JkX2NvdW50LCBmaWxsID0gc3BlYWtlcl9ncnApKSArCiAgICAjIyB2ZXJ0aWNhbCBsaW5lcyBmb3IgdGhlIGFjdHMKICAgIGdlb21fdmxpbmUoCiAgICAgICAgZGF0YSA9IC4gJT4lIGZpbHRlcihzY2VuZSA9PSAiU0NFTkUgSS4iKSwKICAgICAgICBhZXMoeGludGVyY2VwdCA9IGFjdF9zY2VuZV9pZCksCiAgICAgICAgY29sb3IgPSAiZ3JleTUwIiwgc2l6ZSA9IDAuMiwgbHR5ID0gImRvdHRlZCIpICsKICAgIGdlb21fc3RyZWFtKHR5cGUgPSAibWlycm9yIiwgYncgPSAwLjUsICBleHRyYV9zcGFuID0gMC4xKSArCiAgICAjIyBhbm5vdGF0aW9ucyBmb3Iga2V5IGV2ZW50cyAodGV4dCArIHNlZ21lbnQpCiAgICBnZW9tX3RleHRib3goCiAgICAgICAgZGF0YSA9IHN0b3J5X2Fubm90YXRpb25zLAogICAgICAgIGFlcyh4IC0gMC4wOCwgeSwgbGFiZWwgPSBsYWJlbCwgdmp1c3QgPSB2anVzdCksCiAgICAgICAgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICBjb2xvciA9ICJncmV5OTAiLCBmYW1pbHkgPSAiRm9ydW0iLCBoanVzdCA9IDAsIGZpbGwgPSBOQSwKICAgICAgICBib3guc2l6ZSA9IDAsCiAgICAgICAgd2lkdGggPSB1bml0KDMuNSwgImNtIikpICsKICAgIGdlb21fc2VnbWVudCgKICAgICAgICBkYXRhID0gc3RvcnlfYW5ub3RhdGlvbnMsCiAgICAgICAgYWVzKHggPSB4LCB4ZW5kID0geGVuZCwgeSA9IHksIHllbmQgPSB5ZW5kKSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICBjb2xvciA9ICJncmV5OTAiLCBzaXplID0gMC4zKSArCiAgICAjIyB0ZXh0IGxhYmVscyBmb3IgdGhlIGFjdHMKICAgIGdlb21fdGV4dCgKICAgICAgICBkYXRhID0gLiAlPiUKICAgICAgICAgICAgZ3JvdXBfYnkoYWN0KSAlPiUKICAgICAgICAgICAgc3VtbWFyaXplKHggPSBtaW4oYWN0X3NjZW5lX2lkKSArIG5fZGlzdGluY3Qoc2NlbmUpIC8gMiksCiAgICAgICAgYWVzKHgsIHkgPSAtSW5mLCBsYWJlbCA9IGFjdCksIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgdmp1c3QgPSAtMSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImdyZXk2MCIsIGZhbWlseSA9ICJGb3J1bSIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9yX3BhbGV0dGUpICsKICAgIGxhYnMoCiAgICAgICAgdGl0bGUgPSBwbG90X3RpdGxlcyR0aXRsZSwKICAgICAgICBzdWJ0aXRsZSA9IHBsb3RfdGl0bGVzJHN1YnRpdGxlLAogICAgICAgIGNhcHRpb24gPSBwbG90X3RpdGxlcyRjYXB0aW9uLAogICAgICAgIGZpbGwgPSBOVUxMKSArCiAgICB0aGVtZV92b2lkKGJhc2VfZmFtaWx5ID0gIkZvcnVtIiwgYmFzZV9zaXplID0gMTApICsKICAgIHRoZW1lKAogICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvciA9IE5BLCBmaWxsID0gImdyZXk4IiksCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMTAsIDEwLCAxMCwgMTApLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsCiAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDMsICJtbSIpLAogICAgICAgIGxlZ2VuZC5zcGFjaW5nLnkgPSB1bml0KDQsICJjbSIpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LjUpLAogICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAid2hpdGUiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyNCwgZmFtaWx5ID0gIkZvcnVtIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfbWFya2Rvd24oKSwKICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X21hcmtkb3duKGhqdXN0ID0gMSkpCmBgYAo=