🔙 Home

Snail races are a phenomenon on Twitter in which a Twitter poll is created in which all four of the possible options are labeled simply with the snail emoji (🐌). These polls can get very popular, and are noted to consistently have the same shape of responses. This notebook explores the consistency of Snail Race polls. It has been updated to demonstrate current tidyverse coding practices.

Packages used:

library(cowplot)
library(tidyverse)

Read in the data

As a way of procrastinating, I recorded the results of 150 “snail races” on Twitter. Not all of these polls had closed, and the poll status is recorded in this dataset. In the future, the open polls can be updated.

snail <- read_csv("data/snail_race.csv"); snail
Rows: 150 Columns: 9
── Column specification ──────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (3): completed, twitteruser, url
dbl  (5): snailA, snailB, snailC, snailD, votes
date (1): posted

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

But what we really want is to rearrange the four snail types into a single column.

snail %>%
  pivot_longer(cols = str_subset(colnames(.), "snail"),
               names_to = "variable",
               values_to = "value")

Plots

Bar plots

I originally used the summarySE() bespoke function from Cookbook for R, but updates to the tidyverse allow me to simplify my code and use functions directly from dplyr rather than creating my own.

Here is a nice summary of what I’ll be plotting:

## Gives the count, the mean, 
##   the standard deviation (sd), 
##   the standard error of the mean (se), 
##   and the 95% confidence interval (ci).

snail %>% 
  pivot_longer(cols = str_subset(colnames(.), "snail"),
               names_to = "variable",
               values_to = "value") %>% 
  group_by(variable) %>% 
  summarise(count = n(),
            mean = mean(value),
            sd = sd(value),
            se = sd / sqrt(count),
            ci = se * qt(.95/2 + .5, count-1))

Using the plot_grid() function from cowplot, we can put all three varieties of error bars in one compound figure.

p1 <- snail %>% 
  pivot_longer(cols = str_subset(colnames(.), "snail"),
               names_to = "variable",
               values_to = "value") %>% 
  group_by(variable) %>% 
  summarise(count = n(),
            mean = mean(value),
            sd = sd(value),
            se = sd / sqrt(count),
            ci = se * qt(.95/2 + .5, count-1)) %>%
  ggplot(aes(x=variable,y=(mean/100))) +
    theme_bw() +
    geom_bar(aes(fill=variable),stat="identity") +
    geom_errorbar(aes(ymin=(mean-sd)/100, ymax=(mean+sd)/100), colour="black", width=.1) +
    scale_y_continuous(labels=scales::percent, limits=c(0,.50)) +
    ggtitle("With standard deviation") +
    ylab("percent of responses") + xlab("") +
  theme(legend.position = "none",
           axis.text.x  = element_text(angle=90, vjust=0.5))

p2 <- snail %>% 
  pivot_longer(cols = str_subset(colnames(.), "snail"),
               names_to = "variable",
               values_to = "value") %>% 
  group_by(variable) %>% 
  summarise(count = n(),
            mean = mean(value),
            sd = sd(value),
            se = sd / sqrt(count),
            ci = se * qt(.95/2 + .5, count-1)) %>%
  ggplot(aes(x=variable,y=(mean/100))) +
    theme_bw() +
    geom_bar(aes(fill=variable),stat="identity") +
    geom_errorbar(aes(ymin=(mean-ci)/100, ymax=(mean+ci)/100), colour="black", width=.1) +
    scale_y_continuous(labels=scales::percent, limits=c(0,.50)) +
    ggtitle("With 95% Confidence Interval") +
    ylab("") + xlab("") +
  theme(legend.position = "none",
           axis.text.x  = element_text(angle=90, vjust=0.5))

p3 <- snail %>% 
  pivot_longer(cols = str_subset(colnames(.), "snail"),
               names_to = "variable",
               values_to = "value") %>% 
  group_by(variable) %>% 
  summarise(count = n(),
            mean = mean(value),
            sd = sd(value),
            se = sd / sqrt(count),
            ci = se * qt(.95/2 + .5, count-1)) %>%
  ggplot(aes(x=variable,y=(mean/100))) +
    theme_bw() +
    geom_bar(aes(fill=variable),stat="identity") +
    geom_errorbar(aes(ymin=(mean-se)/100, ymax=(mean+se)/100), colour="black", width=.1) +
    scale_y_continuous(labels=scales::percent, limits=c(0,.50)) +
    ggtitle("With standard error") +
    ylab("") + xlab("") +
  theme(legend.position = "none",
           axis.text.x  = element_text(angle=90, vjust=0.5))

plot_grid(p1,p2,p3, ncol = 3)

Box plots

Here’s another way to visualize the way these snail races tend to turn out:

snail %>% 
  pivot_longer(cols = str_subset(colnames(.), "snail"),
               names_to = "variable",
               values_to = "value") %>% 
  ggplot(aes(x=variable,y=value/100)) +
    theme_bw() +
    geom_boxplot(aes(fill=variable)) +
    scale_y_continuous(labels=scales::percent) +
    ggtitle("Distribution of snail race results in quartiles") +
    ylab("percent of responses") + xlab("")

Raincloud plots

Let’s see how a raincloud plot works with these data:

source("https://gist.githubusercontent.com/benmarwick/2a1bb0133ff568cbe28d/raw/fb53bd97121f7f9ce947837ef1a4c65a73bffb3f/geom_flat_violin.R")
raincloud_theme <- theme(
  text = element_text(size = 10),
  axis.title.x = element_text(size = 16),
  axis.title.y = element_text(size = 16),
  axis.text = element_text(size = 14),
  axis.text.x = element_text(angle = 45, vjust = 0.5),
  legend.title = element_text(size = 16),
  legend.text = element_text(size = 16),
  legend.position = "right",
  plot.title = element_text(lineheight = .8, face = "bold", size = 16),
  panel.border = element_blank(),
  panel.grid.minor = element_blank(),
  panel.grid.major = element_blank(),
  axis.line.x = element_line(colour = "black", size = 0.5, linetype = "solid"),
  axis.line.y = element_line(colour = "black", size = 0.5, linetype = "solid"))
snail %>%
  pivot_longer(cols = str_subset(colnames(.), "snail"),
               names_to = "variable",
               values_to = "value") %>% 
  ggplot(aes(x = variable, y = value/100, fill = variable)) +
    ggtitle("Raincloud plot of Twitter Snail Race outcomes") +
    geom_flat_violin(position = position_nudge(x = .2, y = 0), alpha = .8) +
    geom_point(aes(y = value/100, color = variable), 
              position = position_jitter(width = .15), size = .5, alpha = 0.8) +
    geom_boxplot(width = .1, outlier.shape = NA, alpha = 0.5) +
    scale_x_discrete(limits=c("snailD","snailC","snailB","snailA")) + xlab("") +
    scale_y_continuous(labels=scales::percent) + ylab("percent of responses") +
    expand_limits(x = 5.25) +
    coord_flip() + # flip or not
    raincloud_theme

Hmm… I wonder if I can include a dimension for how many votes each poll got? I’ll try do to that with the size aesthetic, but since there’s such a wide range (21 to over 3 million), I’ll have to take the log of the number of votes.

snail %>%
  pivot_longer(cols = str_subset(colnames(.), "snail"),
               names_to = "variable",
               values_to = "value") %>% 
  ggplot(aes(x = variable, y = value/100, fill = variable)) +
    ggtitle("Snowcloud plot of Twitter Snail Race outcomes") +
    geom_flat_violin(position = position_nudge(x = .29, y = 0), alpha = .8) +
    geom_point(aes(y = value/100, color = variable, size=log10(votes)), 
              position = position_jitter(width = .2), alpha = 0.35) +
    geom_boxplot(width = .1, outlier.shape = NA, alpha = 0.5) +
    scale_x_discrete(limits=c("snailD","snailC","snailB","snailA")) + xlab("") +
    scale_y_continuous(labels=scales::percent) + ylab("percent of responses") +
    expand_limits(x = 5.25) +
    coord_flip() + # flip or not
    raincloud_theme

Further info

Next steps: I’d like to figure out how to weight each poll by how many responses were received.

From the first 68 gathered polls:

# Now call back the plot
background <- image_read("images/snail-race-shells.png")
# And bring in a logo
logo_raw <- image_read("images/slug.gif") 
frames <- lapply(logo_raw, function(frame) {
  image_composite(background, frame, offset = "+700+500")
})
animation <- image_animate(image_join(frames))
image_write(animation, "images/slug-graph.gif") # 12 minutes to run?!
beepr::beep()

Discussion

Twitter user @queerterpreter has done some of their own work on Twitter Snail races! They also have lovely snails of their own.

One neat thread they did was trying to get people to produce pre-determined distributions. It was difficult!

Citation

Please cite as:

Ackerman, L.M. (2021, September 28). Snail Race (v2). Retrieved from: https://verbingnouns.github.io/notebooks/snail_race.nb.html

BibTeX:

@MISC{snailRace,
author = {Ackerman, Lauren M.},
title = {Snail Race},
month = {September},
year = {2021},
howpublished={\url{https://verbingnouns.github.io/notebooks/snail_race.nb.html}}
}
LS0tCnRpdGxlOiAiU25haWwgUmFjZSIKYXV0aG9yOiAiTGF1cmVuIE0gQWNrZXJtYW4iCmRhdGU6ICJMYXN0IHVwZGF0ZWQ6IDI4IFNlcHQgMjAyMTxici8+Rmlyc3QgcHVibGlzaGVkOiAyOCBNYXkgMjAxOCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGluY2x1ZGVzOiAKICAgICAgaW5faGVhZGVyOiBnb29nbGVfYW5hbHl0aWNzLmh0bWwKLS0tClvwn5SZIEhvbWVdKGh0dHBzOi8vdmVyYmluZ25vdW5zLmdpdGh1Yi5pby9ub3RlYm9va3MvKQoKU25haWwgcmFjZXMgYXJlIGEgcGhlbm9tZW5vbiBvbiBUd2l0dGVyIGluIHdoaWNoIGEgVHdpdHRlciBwb2xsIGlzIGNyZWF0ZWQgaW4gd2hpY2ggYWxsIGZvdXIgb2YgdGhlIHBvc3NpYmxlIG9wdGlvbnMgYXJlIGxhYmVsZWQgc2ltcGx5IHdpdGggdGhlIHNuYWlsIGVtb2ppICjwn5CMKS4gVGhlc2UgcG9sbHMgY2FuIGdldCB2ZXJ5IHBvcHVsYXIsIGFuZCBhcmUgbm90ZWQgdG8gY29uc2lzdGVudGx5IGhhdmUgdGhlIHNhbWUgc2hhcGUgb2YgcmVzcG9uc2VzLiBUaGlzIG5vdGVib29rIGV4cGxvcmVzIHRoZSBjb25zaXN0ZW5jeSBvZiBTbmFpbCBSYWNlIHBvbGxzLiBJdCBoYXMgYmVlbiB1cGRhdGVkIHRvIGRlbW9uc3RyYXRlIGN1cnJlbnQgYHRpZHl2ZXJzZWAgY29kaW5nIHByYWN0aWNlcy4KCiFbXShpbWFnZXMvNDE5dmluY2UucG5nKQoKUGFja2FnZXMgdXNlZDogIApgYGB7cn0KbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIFJlYWQgaW4gdGhlIGRhdGEKCkFzIGEgd2F5IG9mIHByb2NyYXN0aW5hdGluZywgSSByZWNvcmRlZCB0aGUgcmVzdWx0cyBvZiAxNTAgInNuYWlsIHJhY2VzIiBvbiBbVHdpdHRlcl0oaHR0cHM6Ly90d2l0dGVyLmNvbS9zZWFyY2g/cT1zbmFpbCUyMHJhY2Umc3JjPXR5cGQpLiBOb3QgYWxsIG9mIHRoZXNlIHBvbGxzIGhhZCBjbG9zZWQsIGFuZCB0aGUgcG9sbCBzdGF0dXMgaXMgcmVjb3JkZWQgaW4gdGhpcyBkYXRhc2V0LiBJbiB0aGUgZnV0dXJlLCB0aGUgb3BlbiBwb2xscyBjYW4gYmUgdXBkYXRlZC4KCiFbXShpbWFnZXMvc25haWwtcmFjZS1zZWFyY2gucG5nKQoKYGBge3J9CnNuYWlsIDwtIHJlYWRfY3N2KCJkYXRhL3NuYWlsX3JhY2UuY3N2Iik7IHNuYWlsCmBgYAoKQnV0IHdoYXQgd2UgcmVhbGx5IHdhbnQgaXMgdG8gcmVhcnJhbmdlIHRoZSBmb3VyIHNuYWlsIHR5cGVzIGludG8gYSBzaW5nbGUgY29sdW1uLgoKYGBge3J9CnNuYWlsICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RyX3N1YnNldChjb2xuYW1lcyguKSwgInNuYWlsIiksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIikKYGBgCgoKIyBQbG90cwoKIyMgQmFyIHBsb3RzCgpJIG9yaWdpbmFsbHkgdXNlZCB0aGUgYHN1bW1hcnlTRSgpYCBiZXNwb2tlIGZ1bmN0aW9uIGZyb20gW0Nvb2tib29rIGZvciBSXShodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy9QbG90dGluZ19tZWFuc19hbmRfZXJyb3JfYmFyc18oZ2dwbG90MikvI2hlbHBlci1mdW5jdGlvbnMpLCBidXQgdXBkYXRlcyB0byB0aGUgYHRpZHl2ZXJzZWAgYWxsb3cgbWUgdG8gc2ltcGxpZnkgbXkgY29kZSBhbmQgdXNlIGZ1bmN0aW9ucyBkaXJlY3RseSBmcm9tIGBkcGx5cmAgcmF0aGVyIHRoYW4gY3JlYXRpbmcgbXkgb3duLgoKSGVyZSBpcyBhIG5pY2Ugc3VtbWFyeSBvZiB3aGF0IEknbGwgYmUgcGxvdHRpbmc6CgpgYGB7cn0KIyMgR2l2ZXMgdGhlIGNvdW50LCB0aGUgbWVhbiwgCiMjICAgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiAoc2QpLCAKIyMgICB0aGUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1lYW4gKHNlKSwgCiMjICAgYW5kIHRoZSA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCAoY2kpLgoKc25haWwgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gc3RyX3N1YnNldChjb2xuYW1lcyguKSwgInNuYWlsIiksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lIAogIGdyb3VwX2J5KHZhcmlhYmxlKSAlPiUgCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpLAogICAgICAgICAgICBtZWFuID0gbWVhbih2YWx1ZSksCiAgICAgICAgICAgIHNkID0gc2QodmFsdWUpLAogICAgICAgICAgICBzZSA9IHNkIC8gc3FydChjb3VudCksCiAgICAgICAgICAgIGNpID0gc2UgKiBxdCguOTUvMiArIC41LCBjb3VudC0xKSkKYGBgCgoKVXNpbmcgdGhlIGBwbG90X2dyaWQoKWAgZnVuY3Rpb24gZnJvbSBgY293cGxvdGAsIHdlIGNhbiBwdXQgYWxsIHRocmVlIHZhcmlldGllcyBvZiBlcnJvciBiYXJzIGluIG9uZSBjb21wb3VuZCBmaWd1cmUuCgpgYGBge3IgZmlnLndpZHRoPTQuNzUsIGZpZy5hc3A9MC41fQpwMSA8LSBzbmFpbCAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdHJfc3Vic2V0KGNvbG5hbWVzKC4pLCAic25haWwiKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidmFyaWFibGUiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgCiAgZ3JvdXBfYnkodmFyaWFibGUpICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCksCiAgICAgICAgICAgIG1lYW4gPSBtZWFuKHZhbHVlKSwKICAgICAgICAgICAgc2QgPSBzZCh2YWx1ZSksCiAgICAgICAgICAgIHNlID0gc2QgLyBzcXJ0KGNvdW50KSwKICAgICAgICAgICAgY2kgPSBzZSAqIHF0KC45NS8yICsgLjUsIGNvdW50LTEpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9dmFyaWFibGUseT0obWVhbi8xMDApKSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZW9tX2JhcihhZXMoZmlsbD12YXJpYWJsZSksc3RhdD0iaWRlbnRpdHkiKSArCiAgICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPShtZWFuLXNkKS8xMDAsIHltYXg9KG1lYW4rc2QpLzEwMCksIGNvbG91cj0iYmxhY2siLCB3aWR0aD0uMSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQsIGxpbWl0cz1jKDAsLjUwKSkgKwogICAgZ2d0aXRsZSgiV2l0aCBzdGFuZGFyZCBkZXZpYXRpb24iKSArCiAgICB5bGFiKCJwZXJjZW50IG9mIHJlc3BvbnNlcyIpICsgeGxhYigiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICBheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIHZqdXN0PTAuNSkpCgpwMiA8LSBzbmFpbCAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdHJfc3Vic2V0KGNvbG5hbWVzKC4pLCAic25haWwiKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidmFyaWFibGUiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgCiAgZ3JvdXBfYnkodmFyaWFibGUpICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCksCiAgICAgICAgICAgIG1lYW4gPSBtZWFuKHZhbHVlKSwKICAgICAgICAgICAgc2QgPSBzZCh2YWx1ZSksCiAgICAgICAgICAgIHNlID0gc2QgLyBzcXJ0KGNvdW50KSwKICAgICAgICAgICAgY2kgPSBzZSAqIHF0KC45NS8yICsgLjUsIGNvdW50LTEpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9dmFyaWFibGUseT0obWVhbi8xMDApKSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZW9tX2JhcihhZXMoZmlsbD12YXJpYWJsZSksc3RhdD0iaWRlbnRpdHkiKSArCiAgICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPShtZWFuLWNpKS8xMDAsIHltYXg9KG1lYW4rY2kpLzEwMCksIGNvbG91cj0iYmxhY2siLCB3aWR0aD0uMSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQsIGxpbWl0cz1jKDAsLjUwKSkgKwogICAgZ2d0aXRsZSgiV2l0aCA5NSUgQ29uZmlkZW5jZSBJbnRlcnZhbCIpICsKICAgIHlsYWIoIiIpICsgeGxhYigiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICBheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIHZqdXN0PTAuNSkpCgpwMyA8LSBzbmFpbCAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdHJfc3Vic2V0KGNvbG5hbWVzKC4pLCAic25haWwiKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidmFyaWFibGUiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgCiAgZ3JvdXBfYnkodmFyaWFibGUpICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCksCiAgICAgICAgICAgIG1lYW4gPSBtZWFuKHZhbHVlKSwKICAgICAgICAgICAgc2QgPSBzZCh2YWx1ZSksCiAgICAgICAgICAgIHNlID0gc2QgLyBzcXJ0KGNvdW50KSwKICAgICAgICAgICAgY2kgPSBzZSAqIHF0KC45NS8yICsgLjUsIGNvdW50LTEpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9dmFyaWFibGUseT0obWVhbi8xMDApKSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZW9tX2JhcihhZXMoZmlsbD12YXJpYWJsZSksc3RhdD0iaWRlbnRpdHkiKSArCiAgICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPShtZWFuLXNlKS8xMDAsIHltYXg9KG1lYW4rc2UpLzEwMCksIGNvbG91cj0iYmxhY2siLCB3aWR0aD0uMSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQsIGxpbWl0cz1jKDAsLjUwKSkgKwogICAgZ2d0aXRsZSgiV2l0aCBzdGFuZGFyZCBlcnJvciIpICsKICAgIHlsYWIoIiIpICsgeGxhYigiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICBheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIHZqdXN0PTAuNSkpCgpwbG90X2dyaWQocDEscDIscDMsIG5jb2wgPSAzKQpgYGBgCgojIyBCb3ggcGxvdHMKCkhlcmUncyBhbm90aGVyIHdheSB0byB2aXN1YWxpemUgdGhlIHdheSB0aGVzZSBzbmFpbCByYWNlcyB0ZW5kIHRvIHR1cm4gb3V0OgoKYGBgYHtyfQpzbmFpbCAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdHJfc3Vic2V0KGNvbG5hbWVzKC4pLCAic25haWwiKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidmFyaWFibGUiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXZhcmlhYmxlLHk9dmFsdWUvMTAwKSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dmFyaWFibGUpKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogICAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIHNuYWlsIHJhY2UgcmVzdWx0cyBpbiBxdWFydGlsZXMiKSArCiAgICB5bGFiKCJwZXJjZW50IG9mIHJlc3BvbnNlcyIpICsgeGxhYigiIikKYGBgYAoKIyMgUmFpbmNsb3VkIHBsb3RzCgpMZXQncyBzZWUgaG93IGEgW3JhaW5jbG91ZCBwbG90XShodHRwczovL29yY2hpZDAwLmdpdGh1Yi5pby90aWR5X3JhaW5jbG91ZHBsb3QpIHdvcmtzIHdpdGggdGhlc2UgZGF0YToKCmBgYHtyfQpzb3VyY2UoImh0dHBzOi8vZ2lzdC5naXRodWJ1c2VyY29udGVudC5jb20vYmVubWFyd2ljay8yYTFiYjAxMzNmZjU2OGNiZTI4ZC9yYXcvZmI1M2JkOTcxMjFmN2Y5Y2U5NDc4MzdlZjFhNGM2NWE3M2JmZmIzZi9nZW9tX2ZsYXRfdmlvbGluLlIiKQoKcmFpbmNsb3VkX3RoZW1lIDwtIHRoZW1lKAogIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDAuNSksCiAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChsaW5laGVpZ2h0ID0gLjgsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNiksCiAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0id2hpdGUiKSwKICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSwgbGluZXR5cGUgPSAic29saWQiKSwKICBheGlzLmxpbmUueSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41LCBsaW5ldHlwZSA9ICJzb2xpZCIpKQpgYGAKCgpgYGB7ciwgd2FybmluZz1GQUxTRX0Kc25haWwgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdHJfc3Vic2V0KGNvbG5hbWVzKC4pLCAic25haWwiKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidmFyaWFibGUiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gdmFyaWFibGUsIHkgPSB2YWx1ZS8xMDAsIGZpbGwgPSB2YXJpYWJsZSkpICsKICAgIGdndGl0bGUoIlJhaW5jbG91ZCBwbG90IG9mIFR3aXR0ZXIgU25haWwgUmFjZSBvdXRjb21lcyIpICsKICAgIGdlb21fZmxhdF92aW9saW4ocG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gLjIsIHkgPSAwKSwgYWxwaGEgPSAuOCkgKwogICAgZ2VvbV9wb2ludChhZXMoeSA9IHZhbHVlLzEwMCwgY29sb3IgPSB2YXJpYWJsZSksIAogICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gLjE1KSwgc2l6ZSA9IC41LCBhbHBoYSA9IDAuOCkgKwogICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gLjEsIG91dGxpZXIuc2hhcGUgPSBOQSwgYWxwaGEgPSAwLjUpICsKICAgIHNjYWxlX3hfZGlzY3JldGUobGltaXRzPWMoInNuYWlsRCIsInNuYWlsQyIsInNuYWlsQiIsInNuYWlsQSIpKSArIHhsYWIoIiIpICsKICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArIHlsYWIoInBlcmNlbnQgb2YgcmVzcG9uc2VzIikgKwogICAgZXhwYW5kX2xpbWl0cyh4ID0gNS4yNSkgKwogICAgY29vcmRfZmxpcCgpICsgIyBmbGlwIG9yIG5vdAogICAgcmFpbmNsb3VkX3RoZW1lCmBgYAoKSG1t4oCmIEkgd29uZGVyIGlmIEkgY2FuIGluY2x1ZGUgYSBkaW1lbnNpb24gZm9yIGhvdyBtYW55IHZvdGVzIGVhY2ggcG9sbCBnb3Q/IEknbGwgdHJ5IGRvIHRvIHRoYXQgd2l0aCB0aGUgYHNpemVgIGFlc3RoZXRpYywgYnV0IHNpbmNlIHRoZXJlJ3Mgc3VjaCBhIHdpZGUgcmFuZ2UgKDIxIHRvIG92ZXIgMyBtaWxsaW9uKSwgSSdsbCBoYXZlIHRvIHRha2UgdGhlIGxvZyBvZiB0aGUgbnVtYmVyIG9mIHZvdGVzLgoKYGBge3J9CnNuYWlsICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RyX3N1YnNldChjb2xuYW1lcyguKSwgInNuYWlsIiksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lIAogIGdncGxvdChhZXMoeCA9IHZhcmlhYmxlLCB5ID0gdmFsdWUvMTAwLCBmaWxsID0gdmFyaWFibGUpKSArCiAgICBnZ3RpdGxlKCJTbm93Y2xvdWQgcGxvdCBvZiBUd2l0dGVyIFNuYWlsIFJhY2Ugb3V0Y29tZXMiKSArCiAgICBnZW9tX2ZsYXRfdmlvbGluKHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeCA9IC4yOSwgeSA9IDApLCBhbHBoYSA9IC44KSArCiAgICBnZW9tX3BvaW50KGFlcyh5ID0gdmFsdWUvMTAwLCBjb2xvciA9IHZhcmlhYmxlLCBzaXplPWxvZzEwKHZvdGVzKSksIAogICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gLjIpLCBhbHBoYSA9IDAuMzUpICsKICAgIGdlb21fYm94cGxvdCh3aWR0aCA9IC4xLCBvdXRsaWVyLnNoYXBlID0gTkEsIGFscGhhID0gMC41KSArCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1jKCJzbmFpbEQiLCJzbmFpbEMiLCJzbmFpbEIiLCJzbmFpbEEiKSkgKyB4bGFiKCIiKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKyB5bGFiKCJwZXJjZW50IG9mIHJlc3BvbnNlcyIpICsKICAgIGV4cGFuZF9saW1pdHMoeCA9IDUuMjUpICsKICAgIGNvb3JkX2ZsaXAoKSArICMgZmxpcCBvciBub3QKICAgIHJhaW5jbG91ZF90aGVtZQpgYGAKCiMgRnVydGhlciBpbmZvCgoqKk5leHQgc3RlcHM6KiogSSdkIGxpa2UgdG8gZmlndXJlIG91dCBob3cgdG8gd2VpZ2h0IGVhY2ggcG9sbCBieSBob3cgbWFueSByZXNwb25zZXMgd2VyZSByZWNlaXZlZC4KCkZyb20gdGhlIFtmaXJzdCA2OCBnYXRoZXJlZCBwb2xsc10oaHR0cHM6Ly90d2l0dGVyLmNvbS9WZXJiaW5nTm91bnMvc3RhdHVzLzEwMDA3NzUwOTUyOTQ1ODY4ODApOgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgTm93IGNhbGwgYmFjayB0aGUgcGxvdApiYWNrZ3JvdW5kIDwtIGltYWdlX3JlYWQoImltYWdlcy9zbmFpbC1yYWNlLXNoZWxscy5wbmciKQojIEFuZCBicmluZyBpbiBhIGxvZ28KbG9nb19yYXcgPC0gaW1hZ2VfcmVhZCgiaW1hZ2VzL3NsdWcuZ2lmIikgCmZyYW1lcyA8LSBsYXBwbHkobG9nb19yYXcsIGZ1bmN0aW9uKGZyYW1lKSB7CiAgaW1hZ2VfY29tcG9zaXRlKGJhY2tncm91bmQsIGZyYW1lLCBvZmZzZXQgPSAiKzcwMCs1MDAiKQp9KQphbmltYXRpb24gPC0gaW1hZ2VfYW5pbWF0ZShpbWFnZV9qb2luKGZyYW1lcykpCmltYWdlX3dyaXRlKGFuaW1hdGlvbiwgImltYWdlcy9zbHVnLWdyYXBoLmdpZiIpICMgMTIgbWludXRlcyB0byBydW4/IQpiZWVwcjo6YmVlcCgpCmBgYAoKIVtdKGltYWdlcy9zbHVnLWdyYXBoLmdpZikKCiMjIERpc2N1c3Npb24KClR3aXR0ZXIgdXNlciBbXEBxdWVlcnRlcnByZXRlcl0oaHR0cHM6Ly90d2l0dGVyLmNvbS9xdWVlcnRlcnByZXRlcikgaGFzIGRvbmUgc29tZSBvZiB0aGVpciBvd24gd29yayBvbiBUd2l0dGVyIFNuYWlsIHJhY2VzISBUaGV5IGFsc28gaGF2ZSBbbG92ZWx5IHNuYWlsc10oaHR0cHM6Ly90d2l0dGVyLmNvbS9zZWFyY2g/cT1mcm9tJTNBcXVlZXJ0ZXJwcmV0ZXIlMjBzbmFpbCZzcmM9dHlwZWRfcXVlcnkmZj12aWRlbykgb2YgdGhlaXIgb3duLgoKT25lIFtuZWF0IHRocmVhZF0oaHR0cHM6Ly90d2l0dGVyLmNvbS9xdWVlcnRlcnByZXRlci9zdGF0dXMvMTM0MDM5NTgwOTkwNTUxNjU0Nj9zPTIwKSB0aGV5IGRpZCB3YXMgdHJ5aW5nIHRvIGdldCBwZW9wbGUgdG8gcHJvZHVjZSBwcmUtZGV0ZXJtaW5lZCBkaXN0cmlidXRpb25zLiBJdCB3YXMgZGlmZmljdWx0IQoKIyMgQ2l0YXRpb24KClBsZWFzZSBjaXRlIGFzOgoKPiBBY2tlcm1hbiwgTC5NLiAoMjAyMSwgU2VwdGVtYmVyIDI4KS4gKlNuYWlsIFJhY2UqICh2MikuIFJldHJpZXZlZCBmcm9tOiBbaHR0cHM6Ly92ZXJiaW5nbm91bnMuZ2l0aHViLmlvL25vdGVib29rcy9zbmFpbF9yYWNlLm5iLmh0bWxdKCkKCkJpYlRlWDoKCmBgYApATUlTQ3tzbmFpbFJhY2UsCmF1dGhvciA9IHtBY2tlcm1hbiwgTGF1cmVuIE0ufSwKdGl0bGUgPSB7U25haWwgUmFjZX0sCm1vbnRoID0ge1NlcHRlbWJlcn0sCnllYXIgPSB7MjAyMX0sCmhvd3B1Ymxpc2hlZD17XHVybHtodHRwczovL3ZlcmJpbmdub3Vucy5naXRodWIuaW8vbm90ZWJvb2tzL3NuYWlsX3JhY2UubmIuaHRtbH19Cn0KYGBg