The first we need to install and load the packages. You can install packages in R Studio by going to Tools -> Install Packages… in the menu bar. You can also install them in code (see below).

# install.packages("ggplot2")
library(ggplot2)

Great. Now we have ggplot2 loaded. (Let’s just call it ggplot from now on.) Hey! This sounds a lot like what we did last week. No harm in refreshing. (Fine. I copied and pasted the line above. You should be doing that, too. It can speed up your workflow sometimes.) Don’t worry, you’re about to learn the good stuff. Anyway, let’s load our beautifully cleaned data from last week.

# read in the data files
lex_data <- read.delim("lex_data.txt", sep = " ")  # for a space-delimited file
ph_effects <- read.csv("ph_effects.csv")

Did you try to glimpse at the data yet? Ha! That isn’t going to work unless you load the dplyr package. These packages work great together, but this tutorial is (for the most part) going to stick to base R and ggplot functions. Use the head or View functions (or just click on it in the Global Environment at the top right) to take a look at the data. We have a weird extra column in the pseudohomophone effects file. Let’s obliterate it. Subject should also be a factor. We can fix that, too.

# drop the unnecessary column
ph_effects$X <- NULL
# change id to a factor
lex_data$id <- as.factor(lex_data$id)

What’s that? You’re just here for the plots? If you’re going to be like that, you can make some plots in base R.

# make a plot of pseudohomophone error rates
plot(ph_effects$ph_err)

# make a plot of pseudohomophone and nonword RTs
plot(x = ph_effects$ph_rt, y = ph_effects$nw_rt)

Pretty, right? Wrong! (Are you high?) It’s all black and white. The scales are not even. There is no title. Worthless axis labels. Data points are overlapping. (And there are only 30 of them.) Did I even say I wanted scatterplots? No, I don’t think that I did.

Friends, I can tell you that there is a better way. That way is the Graphics of Grammar (wait, strike that, reverse it). That’s what the “gg” stands for, in case you haven’t figured that out yet. Let’s start with a simple histogram.

# save the base of the plot
ph_error_plot <- ggplot(ph_effects, aes(ph_err))
# show the data as a histogram
ph_error_plot + geom_histogram()

Well, at least it isn’t a scatterplot anymore. Also, it isn’t sideways, so that’s a plus. It’s pretty easy to see that some jerk was just answering randomly. Good to know, but I wouldn’t even publish this plot on my blog. Let’s clean it up.

# make a (somewhat) better looking histogram
ph_error_plot + 
  geom_histogram(binwidth = 0.02) + 
  coord_cartesian(xlim = c(0, 0.3)) + 
  labs(title = "Pseudohomophone Errors", x = "Error Rate", y = "Count") + 
  theme_minimal() + theme(text = element_text(size = 16, color = "dark blue"))

Not exactly a normal distribution, but it sure looks better. Let’s go through what we did. First, we chose a bin width for the histogram. We could have chosen the number of bins instead, it is up to you. We set the limits on the x-axis to between 0 and 0.3 to hide the outlier (not the Malcolm Gladwell kind). We changed the title and the labels for the axes so that I am not the only person who knows what they mean. We used the minimal theme, because that gray background is so 2016. But it still wasn’t perfect, so we changed the text size and color (Go Huskies!).

In ggplot, each plot is built in layers. This makes it really easy to change the data and get the same type of plot. (It also makes it really easy to make small changes to get your plots to look just the way you like them by adding another layer.) Why don’t you try making a histogram of the nonword error rates below? That is a rhetorical question. Get to it.

# save the base of the plot
nw_error_plot <- ggplot(ph_effects, aes(nw_err))
# do a barrel roll, er, I mean, make a nice looking histogram
nw_error_plot + geom_histogram()

Holy skewed data, Batman! Well, that happens with error rates. Let’s turn our attention to the RTs. We have the summarized data, but we lost a lot of information in calculating the means. Maybe the range of a participant is important as well. Praise Eris, ggplot has even more ways to plot two dimensional data. How about some box plots?

# Let's get this plot started
rt_plot <- ggplot(lex_data, aes(x = id, y = rt, group = id))
# make a boxplot for each participant
rt_plot + geom_boxplot()

Neat. (In the cool sense, not in the clean sense.) It looks like there is some variability between participants, which might be a good thing, depending on what you intended. Let’s make this one look even neater using those nifty violin plots.

# make a violin plot for each participant
rt_plot + 
  geom_violin(aes(color = id)) + 
  ylim(5.5, 8) + 
  labs(title = "Lexical Decision RTs", x = "Subject", y = "Log RT") + 
  theme_bw() + theme(text = element_text(size = 16, color = "dark blue"))

What is this garbage? We were doing almost the same thing as before there is just too much overlap and a pretty useless legend. I just wanted to make it a little more colorful. Make it go away!

# make a violin plot for each participant that isn't fugly
rt_plot + 
  geom_violin(aes(color = id)) + 
  ylim(5.5, 8) + 
  scale_x_discrete(labels = c(1:30)) + 
  labs(title = "Lexical Decision RTs", x = "Subject", y = "Log RT") + 
  theme_classic() + 
  theme(text = element_text(size = 16, color = "dark blue"), legend.position="none")

That sure looks a lot cleaner to me. But these are all of the RTs. What if I just want to look at pseudohomophone RTs? Too bad. Here’s the thing. In order to do that, you need to combine your dplyr skills with your ggplot skills. Since some of you might not have bothered to work through the dplyr tutorials, I am not going to go into too much depth on this, but here is an example that the overachievers can work from. Essentially, you want to filter your data before starting the plot. Check it.

# load dplyr
library(dplyr)
# filter before starting the plot
rt_plot <- lex_data %>% filter(type == "pseudohomophone") %>% 
  ggplot(., aes(x = id, y = rt, group = id))
# make violin plots for pseudohomophone RTs
rt_plot + 
  geom_violin(aes(color = id)) + 
  ylim(5.5, 8) + 
  scale_x_discrete(labels = c(1:30)) + 
  labs(title = "Lexical Decision RTs", x = "Subject", y = "Log RT") + 
  theme_light() + 
  theme(text = element_text(size = 16, color = "dark blue"), legend.position="none")

B-E-A-Utiful. Just a few more things left to do. First, maybe we actually do want a scatter plot when comparing two variables (like RT and error rate.) You’ve got the idea already, right? I knew you had it in you. Just in case, here you go.

# save the base of the plot
ph_rtxerr <- ggplot(ph_effects, aes(ph_rt, ph_err))
# make a scatterplot with a regression line
ph_rtxerr + geom_point() + geom_smooth(method = "lm")

I can tell that faster participants are making fewer errors, in general, but what about getting this published for the web? FiveThirtyEight has some pretty nice plots. What about copying their style when sharing with the general public? See, it can be a lot of work getting everything to look just right. Take a look at the theme file. It creates a new function that you can use on just about any plot. Edit the function and you can edit all of the plots you are going to make going forward. Useful if you want a unified style, but a bit beyond the scope of this tutorial.

# load necessary packages and theme file
# install.packages(c("RColorBrewer","scales"))
library(scales); library(grid); library(RColorBrewer)
source("theme_538.R")
# plot with the theme added
ph_rtxerr + geom_point() + geom_smooth(method = "lm") + fte_theme()

It is certainly modern. What does our histogram look like with the theme added?

ph_error_plot + geom_histogram() + fte_theme()

ph_error_plot + geom_dotplot() + fte_theme()

ph_error_plot + geom_density() + fte_theme()

Surprise! Three different ways to plot this type of data. I like the dot plot, personally. Is this theme perfect for all of your needs? Probably not. (Certainly not.) Work it. Change it. Twist it. Remix it. (Is that a song? I don’t even know.)

Alrighty then, I have been talking (well, typing) a whole lot. Time for you to give it another try. Let’s back up so that you can clean this graph up a bit. Take a look at the cheat sheet and try an entirely new type of plot, if you are feeling ambitious.

# choose your own adventure plot
ph_rtxerr + geom_point()

I like your style. That one is a keeper. Let’s save it and be done with this.

# save the last plot
ggsave("cool_plot.png", width = 7, height = 7)

Piece of cake! (Ice cream cake, preferably. I’m waiting.) There you have it, folks. You learned the entire grammar of graphics. A new language. You speak (well, type) it fluently now. Can you feel it? No. Well, sorry! An entire course could be taught on ggplot. Look forward to “Getting gooder at ggplot2” (maybe, no promises).

LS0tCnRpdGxlOiAiR2V0dGluZyBHb29kIGF0IGdncGxvdDIiCmF1dGhvcjogIkhlbnJ5IFdvbGYgVklJIgpkYXRlOiAiMjkgTWFyY2ggMjAxNyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhlIGZpcnN0IHdlIG5lZWQgdG8gaW5zdGFsbCBhbmQgbG9hZCB0aGUgcGFja2FnZXMuIFlvdSBjYW4gaW5zdGFsbCBwYWNrYWdlcyBpbiBSIFN0dWRpbyBieSBnb2luZyB0byAqVG9vbHMgLT4gSW5zdGFsbCBQYWNrYWdlcy4uLiogaW4gdGhlIG1lbnUgYmFyLiBZb3UgY2FuIGFsc28gaW5zdGFsbCB0aGVtIGluIGNvZGUgKHNlZSBiZWxvdykuCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKbGlicmFyeShnZ3Bsb3QyKQoKYGBgCgpHcmVhdC4gTm93IHdlIGhhdmUgZ2dwbG90MiBsb2FkZWQuIChMZXQncyBqdXN0IGNhbGwgaXQgZ2dwbG90IGZyb20gbm93IG9uLikgSGV5ISBUaGlzIHNvdW5kcyBhIGxvdCBsaWtlIHdoYXQgd2UgZGlkIGxhc3Qgd2Vlay4gTm8gaGFybSBpbiByZWZyZXNoaW5nLiAoRmluZS4gSSBjb3BpZWQgYW5kIHBhc3RlZCB0aGUgbGluZSBhYm92ZS4gWW91IHNob3VsZCBiZSBkb2luZyB0aGF0LCB0b28uIEl0IGNhbiBzcGVlZCB1cCB5b3VyIHdvcmtmbG93IHNvbWV0aW1lcy4pIERvbid0IHdvcnJ5LCB5b3UncmUgYWJvdXQgdG8gbGVhcm4gdGhlIGdvb2Qgc3R1ZmYuIEFueXdheSwgbGV0J3MgbG9hZCBvdXIgYmVhdXRpZnVsbHkgY2xlYW5lZCBkYXRhIGZyb20gbGFzdCB3ZWVrLgoKYGBge3J9CiMgcmVhZCBpbiB0aGUgZGF0YSBmaWxlcwpsZXhfZGF0YSA8LSByZWFkLmRlbGltKCJsZXhfZGF0YS50eHQiLCBzZXAgPSAiICIpICAjIGZvciBhIHNwYWNlLWRlbGltaXRlZCBmaWxlCnBoX2VmZmVjdHMgPC0gcmVhZC5jc3YoInBoX2VmZmVjdHMuY3N2IikKCmBgYAoKRGlkIHlvdSB0cnkgdG8gZ2xpbXBzZSBhdCB0aGUgZGF0YSB5ZXQ/IEhhISBUaGF0IGlzbid0IGdvaW5nIHRvIHdvcmsgdW5sZXNzIHlvdSBsb2FkIHRoZSBkcGx5ciBwYWNrYWdlLiBUaGVzZSBwYWNrYWdlcyB3b3JrIGdyZWF0IHRvZ2V0aGVyLCBidXQgdGhpcyB0dXRvcmlhbCBpcyAoZm9yIHRoZSBtb3N0IHBhcnQpIGdvaW5nIHRvIHN0aWNrIHRvIGJhc2UgUiBhbmQgZ2dwbG90IGZ1bmN0aW9ucy4gVXNlIHRoZSBoZWFkIG9yIFZpZXcgZnVuY3Rpb25zIChvciBqdXN0IGNsaWNrIG9uIGl0IGluIHRoZSBHbG9iYWwgRW52aXJvbm1lbnQgYXQgdGhlIHRvcCByaWdodCkgdG8gdGFrZSBhIGxvb2sgYXQgdGhlIGRhdGEuIFdlIGhhdmUgYSB3ZWlyZCBleHRyYSBjb2x1bW4gaW4gdGhlIHBzZXVkb2hvbW9waG9uZSBlZmZlY3RzIGZpbGUuIExldCdzIG9ibGl0ZXJhdGUgaXQuIFN1YmplY3Qgc2hvdWxkIGFsc28gYmUgYSBmYWN0b3IuIFdlIGNhbiBmaXggdGhhdCwgdG9vLgoKYGBge3J9CiMgZHJvcCB0aGUgdW5uZWNlc3NhcnkgY29sdW1uCnBoX2VmZmVjdHMkWCA8LSBOVUxMCgojIGNoYW5nZSBpZCB0byBhIGZhY3RvcgpsZXhfZGF0YSRpZCA8LSBhcy5mYWN0b3IobGV4X2RhdGEkaWQpCgpgYGAKCldoYXQncyB0aGF0PyBZb3UncmUganVzdCBoZXJlIGZvciB0aGUgcGxvdHM/IElmIHlvdSdyZSBnb2luZyB0byBiZSBsaWtlIHRoYXQsIHlvdSBjYW4gbWFrZSBzb21lIHBsb3RzIGluIGJhc2UgUi4KCmBgYHtyfQojIG1ha2UgYSBwbG90IG9mIHBzZXVkb2hvbW9waG9uZSBlcnJvciByYXRlcwpwbG90KHBoX2VmZmVjdHMkcGhfZXJyKQoKIyBtYWtlIGEgcGxvdCBvZiBwc2V1ZG9ob21vcGhvbmUgYW5kIG5vbndvcmQgUlRzCnBsb3QoeCA9IHBoX2VmZmVjdHMkcGhfcnQsIHkgPSBwaF9lZmZlY3RzJG53X3J0KQoKYGBgCgpQcmV0dHksIHJpZ2h0PyBXcm9uZyEgKEFyZSB5b3UgaGlnaD8pIEl0J3MgYWxsIGJsYWNrIGFuZCB3aGl0ZS4gVGhlIHNjYWxlcyBhcmUgbm90IGV2ZW4uIFRoZXJlIGlzIG5vIHRpdGxlLiBXb3J0aGxlc3MgYXhpcyBsYWJlbHMuIERhdGEgcG9pbnRzIGFyZSBvdmVybGFwcGluZy4gKEFuZCB0aGVyZSBhcmUgb25seSAzMCBvZiB0aGVtLikgRGlkIEkgZXZlbiBzYXkgSSB3YW50ZWQgc2NhdHRlcnBsb3RzPyBObywgSSBkb24ndCB0aGluayB0aGF0IEkgZGlkLgoKRnJpZW5kcywgSSBjYW4gdGVsbCB5b3UgdGhhdCB0aGVyZSBpcyBhIGJldHRlciB3YXkuIFRoYXQgd2F5IGlzIHRoZSBHcmFwaGljcyBvZiBHcmFtbWFyICh3YWl0LCBzdHJpa2UgdGhhdCwgcmV2ZXJzZSBpdCkuIFRoYXQncyB3aGF0IHRoZSAiZ2ciIHN0YW5kcyBmb3IsIGluIGNhc2UgeW91IGhhdmVuJ3QgZmlndXJlZCB0aGF0IG91dCB5ZXQuIExldCdzIHN0YXJ0IHdpdGggYSBzaW1wbGUgaGlzdG9ncmFtLgoKYGBge3J9CiMgc2F2ZSB0aGUgYmFzZSBvZiB0aGUgcGxvdApwaF9lcnJvcl9wbG90IDwtIGdncGxvdChwaF9lZmZlY3RzLCBhZXMocGhfZXJyKSkKCiMgc2hvdyB0aGUgZGF0YSBhcyBhIGhpc3RvZ3JhbQpwaF9lcnJvcl9wbG90ICsgZ2VvbV9oaXN0b2dyYW0oKQoKYGBgCgpXZWxsLCBhdCBsZWFzdCBpdCBpc24ndCBhIHNjYXR0ZXJwbG90IGFueW1vcmUuIEFsc28sIGl0IGlzbid0IHNpZGV3YXlzLCBzbyB0aGF0J3MgYSBwbHVzLiBJdCdzIHByZXR0eSBlYXN5IHRvIHNlZSB0aGF0IHNvbWUgamVyayB3YXMganVzdCBhbnN3ZXJpbmcgcmFuZG9tbHkuIEdvb2QgdG8ga25vdywgYnV0IEkgd291bGRuJ3QgZXZlbiBwdWJsaXNoIHRoaXMgcGxvdCBvbiBteSBibG9nLiBMZXQncyBjbGVhbiBpdCB1cC4KCmBgYHtyfQojIG1ha2UgYSAoc29tZXdoYXQpIGJldHRlciBsb29raW5nIGhpc3RvZ3JhbQpwaF9lcnJvcl9wbG90ICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjAyKSArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAwLjMpKSArIAogIGxhYnModGl0bGUgPSAiUHNldWRvaG9tb3Bob25lIEVycm9ycyIsIHggPSAiRXJyb3IgUmF0ZSIsIHkgPSAiQ291bnQiKSArIAogIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBjb2xvciA9ICJkYXJrIGJsdWUiKSkKCmBgYAoKTm90IGV4YWN0bHkgYSBub3JtYWwgZGlzdHJpYnV0aW9uLCBidXQgaXQgc3VyZSBsb29rcyBiZXR0ZXIuIExldCdzIGdvIHRocm91Z2ggd2hhdCB3ZSBkaWQuIEZpcnN0LCB3ZSBjaG9zZSBhIGJpbiB3aWR0aCBmb3IgdGhlIGhpc3RvZ3JhbS4gV2UgY291bGQgaGF2ZSBjaG9zZW4gdGhlIG51bWJlciBvZiBiaW5zIGluc3RlYWQsIGl0IGlzIHVwIHRvIHlvdS4gV2Ugc2V0IHRoZSBsaW1pdHMgb24gdGhlIHgtYXhpcyB0byBiZXR3ZWVuIDAgYW5kIDAuMyB0byBoaWRlIHRoZSBvdXRsaWVyIChub3QgdGhlIE1hbGNvbG0gR2xhZHdlbGwga2luZCkuIFdlIGNoYW5nZWQgdGhlIHRpdGxlIGFuZCB0aGUgbGFiZWxzIGZvciB0aGUgYXhlcyBzbyB0aGF0IEkgYW0gbm90IHRoZSBvbmx5IHBlcnNvbiB3aG8ga25vd3Mgd2hhdCB0aGV5IG1lYW4uIFdlIHVzZWQgdGhlIG1pbmltYWwgdGhlbWUsIGJlY2F1c2UgdGhhdCBncmF5IGJhY2tncm91bmQgaXMgc28gMjAxNi4gQnV0IGl0IHN0aWxsIHdhc24ndCBwZXJmZWN0LCBzbyB3ZSBjaGFuZ2VkIHRoZSB0ZXh0IHNpemUgYW5kIGNvbG9yIChHbyBIdXNraWVzISkuCgpJbiBnZ3Bsb3QsIGVhY2ggcGxvdCBpcyBidWlsdCBpbiBsYXllcnMuIFRoaXMgbWFrZXMgaXQgcmVhbGx5IGVhc3kgdG8gY2hhbmdlIHRoZSBkYXRhIGFuZCBnZXQgdGhlIHNhbWUgdHlwZSBvZiBwbG90LiAoSXQgYWxzbyBtYWtlcyBpdCByZWFsbHkgZWFzeSB0byBtYWtlIHNtYWxsIGNoYW5nZXMgdG8gZ2V0IHlvdXIgcGxvdHMgdG8gbG9vayBqdXN0IHRoZSB3YXkgeW91IGxpa2UgdGhlbSBieSBhZGRpbmcgYW5vdGhlciBsYXllci4pIFdoeSBkb24ndCB5b3UgdHJ5IG1ha2luZyBhIGhpc3RvZ3JhbSBvZiB0aGUgbm9ud29yZCBlcnJvciByYXRlcyBiZWxvdz8gVGhhdCBpcyBhIHJoZXRvcmljYWwgcXVlc3Rpb24uIEdldCB0byBpdC4KCmBgYHtyfQojIHNhdmUgdGhlIGJhc2Ugb2YgdGhlIHBsb3QKbndfZXJyb3JfcGxvdCA8LSBnZ3Bsb3QocGhfZWZmZWN0cywgYWVzKG53X2VycikpCgojIGRvIGEgYmFycmVsIHJvbGwsIGVyLCBJIG1lYW4sIG1ha2UgYSBuaWNlIGxvb2tpbmcgaGlzdG9ncmFtCm53X2Vycm9yX3Bsb3QgKyBnZW9tX2hpc3RvZ3JhbSgpCgpgYGAKCkhvbHkgc2tld2VkIGRhdGEsIEJhdG1hbiEgV2VsbCwgdGhhdCBoYXBwZW5zIHdpdGggZXJyb3IgcmF0ZXMuIExldCdzIHR1cm4gb3VyIGF0dGVudGlvbiB0byB0aGUgUlRzLiBXZSBoYXZlIHRoZSBzdW1tYXJpemVkIGRhdGEsIGJ1dCB3ZSBsb3N0IGEgbG90IG9mIGluZm9ybWF0aW9uIGluIGNhbGN1bGF0aW5nIHRoZSBtZWFucy4gTWF5YmUgdGhlIHJhbmdlIG9mIGEgcGFydGljaXBhbnQgaXMgaW1wb3J0YW50IGFzIHdlbGwuIFByYWlzZSBFcmlzLCBnZ3Bsb3QgaGFzIGV2ZW4gbW9yZSB3YXlzIHRvIHBsb3QgdHdvIGRpbWVuc2lvbmFsIGRhdGEuIEhvdyBhYm91dCBzb21lIGJveCBwbG90cz8KCmBgYHtyfQojIExldCdzIGdldCB0aGlzIHBsb3Qgc3RhcnRlZApydF9wbG90IDwtIGdncGxvdChsZXhfZGF0YSwgYWVzKHggPSBpZCwgeSA9IHJ0LCBncm91cCA9IGlkKSkKCiMgbWFrZSBhIGJveHBsb3QgZm9yIGVhY2ggcGFydGljaXBhbnQKcnRfcGxvdCArIGdlb21fYm94cGxvdCgpCgpgYGAKCk5lYXQuIChJbiB0aGUgY29vbCBzZW5zZSwgbm90IGluIHRoZSBjbGVhbiBzZW5zZS4pIEl0IGxvb2tzIGxpa2UgdGhlcmUgaXMgc29tZSB2YXJpYWJpbGl0eSBiZXR3ZWVuIHBhcnRpY2lwYW50cywgd2hpY2ggbWlnaHQgYmUgYSBnb29kIHRoaW5nLCBkZXBlbmRpbmcgb24gd2hhdCB5b3UgaW50ZW5kZWQuIExldCdzIG1ha2UgdGhpcyBvbmUgbG9vayBldmVuIG5lYXRlciB1c2luZyB0aG9zZSBuaWZ0eSB2aW9saW4gcGxvdHMuCgpgYGB7cn0KIyBtYWtlIGEgdmlvbGluIHBsb3QgZm9yIGVhY2ggcGFydGljaXBhbnQKcnRfcGxvdCArIAogIGdlb21fdmlvbGluKGFlcyhjb2xvciA9IGlkKSkgKyAKICB5bGltKDUuNSwgOCkgKyAKICBsYWJzKHRpdGxlID0gIkxleGljYWwgRGVjaXNpb24gUlRzIiwgeCA9ICJTdWJqZWN0IiwgeSA9ICJMb2cgUlQiKSArIAogIHRoZW1lX2J3KCkgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgY29sb3IgPSAiZGFyayBibHVlIikpCgpgYGAKCldoYXQgaXMgdGhpcyBnYXJiYWdlPyBXZSB3ZXJlIGRvaW5nIGFsbW9zdCB0aGUgc2FtZSB0aGluZyBhcyBiZWZvcmUgdGhlcmUgaXMganVzdCB0b28gbXVjaCBvdmVybGFwIGFuZCBhIHByZXR0eSB1c2VsZXNzIGxlZ2VuZC4gSSBqdXN0IHdhbnRlZCB0byBtYWtlIGl0IGEgbGl0dGxlIG1vcmUgY29sb3JmdWwuIE1ha2UgaXQgZ28gYXdheSEKCmBgYHtyfQojIG1ha2UgYSB2aW9saW4gcGxvdCBmb3IgZWFjaCBwYXJ0aWNpcGFudCB0aGF0IGlzbid0IGZ1Z2x5CnJ0X3Bsb3QgKyAKICBnZW9tX3Zpb2xpbihhZXMoY29sb3IgPSBpZCkpICsgCiAgeWxpbSg1LjUsIDgpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKDE6MzApKSArIAogIGxhYnModGl0bGUgPSAiTGV4aWNhbCBEZWNpc2lvbiBSVHMiLCB4ID0gIlN1YmplY3QiLCB5ID0gIkxvZyBSVCIpICsgCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gImRhcmsgYmx1ZSIpLCBsZWdlbmQucG9zaXRpb249Im5vbmUiKQoKYGBgCgpUaGF0IHN1cmUgbG9va3MgYSBsb3QgY2xlYW5lciB0byBtZS4gQnV0IHRoZXNlIGFyZSBhbGwgb2YgdGhlIFJUcy4gV2hhdCBpZiBJIGp1c3Qgd2FudCB0byBsb29rIGF0IHBzZXVkb2hvbW9waG9uZSBSVHM/IFRvbyBiYWQuIEhlcmUncyB0aGUgdGhpbmcuIEluIG9yZGVyIHRvIGRvIHRoYXQsIHlvdSBuZWVkIHRvIGNvbWJpbmUgeW91ciBkcGx5ciBza2lsbHMgd2l0aCB5b3VyIGdncGxvdCBza2lsbHMuIFNpbmNlIHNvbWUgb2YgeW91IG1pZ2h0IG5vdCBoYXZlIGJvdGhlcmVkIHRvIHdvcmsgdGhyb3VnaCB0aGUgZHBseXIgdHV0b3JpYWxzLCBJIGFtIG5vdCBnb2luZyB0byBnbyBpbnRvIHRvbyBtdWNoIGRlcHRoIG9uIHRoaXMsIGJ1dCBoZXJlIGlzIGFuIGV4YW1wbGUgdGhhdCB0aGUgb3ZlcmFjaGlldmVycyBjYW4gd29yayBmcm9tLiBFc3NlbnRpYWxseSwgeW91IHdhbnQgdG8gZmlsdGVyIHlvdXIgZGF0YSBiZWZvcmUgc3RhcnRpbmcgdGhlIHBsb3QuIENoZWNrIGl0LgoKYGBge3J9CiMgbG9hZCBkcGx5cgpsaWJyYXJ5KGRwbHlyKQoKIyBmaWx0ZXIgYmVmb3JlIHN0YXJ0aW5nIHRoZSBwbG90CnJ0X3Bsb3QgPC0gbGV4X2RhdGEgJT4lIGZpbHRlcih0eXBlID09ICJwc2V1ZG9ob21vcGhvbmUiKSAlPiUgCiAgZ2dwbG90KC4sIGFlcyh4ID0gaWQsIHkgPSBydCwgZ3JvdXAgPSBpZCkpCgojIG1ha2UgdmlvbGluIHBsb3RzIGZvciBwc2V1ZG9ob21vcGhvbmUgUlRzCnJ0X3Bsb3QgKyAKICBnZW9tX3Zpb2xpbihhZXMoY29sb3IgPSBpZCkpICsgCiAgeWxpbSg1LjUsIDgpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKDE6MzApKSArIAogIGxhYnModGl0bGUgPSAiTGV4aWNhbCBEZWNpc2lvbiBSVHMiLCB4ID0gIlN1YmplY3QiLCB5ID0gIkxvZyBSVCIpICsgCiAgdGhlbWVfbGlnaHQoKSArIAogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBjb2xvciA9ICJkYXJrIGJsdWUiKSwgbGVnZW5kLnBvc2l0aW9uPSJub25lIikKCgpgYGAKCkItRS1BLVV0aWZ1bC4gSnVzdCBhIGZldyBtb3JlIHRoaW5ncyBsZWZ0IHRvIGRvLiBGaXJzdCwgbWF5YmUgd2UgYWN0dWFsbHkgZG8gd2FudCBhIHNjYXR0ZXIgcGxvdCB3aGVuIGNvbXBhcmluZyB0d28gdmFyaWFibGVzIChsaWtlIFJUIGFuZCBlcnJvciByYXRlLikgWW91J3ZlIGdvdCB0aGUgaWRlYSBhbHJlYWR5LCByaWdodD8gSSBrbmV3IHlvdSBoYWQgaXQgaW4geW91LiBKdXN0IGluIGNhc2UsIGhlcmUgeW91IGdvLgoKYGBge3J9CiMgc2F2ZSB0aGUgYmFzZSBvZiB0aGUgcGxvdApwaF9ydHhlcnIgPC0gZ2dwbG90KHBoX2VmZmVjdHMsIGFlcyhwaF9ydCwgcGhfZXJyKSkKCiMgbWFrZSBhIHNjYXR0ZXJwbG90IHdpdGggYSByZWdyZXNzaW9uIGxpbmUKcGhfcnR4ZXJyICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikKCgpgYGAKCkkgY2FuIHRlbGwgdGhhdCBmYXN0ZXIgcGFydGljaXBhbnRzIGFyZSBtYWtpbmcgZmV3ZXIgZXJyb3JzLCBpbiBnZW5lcmFsLCBidXQgd2hhdCBhYm91dCBnZXR0aW5nIHRoaXMgcHVibGlzaGVkIGZvciB0aGUgd2ViPyBGaXZlVGhpcnR5RWlnaHQgaGFzIHNvbWUgcHJldHR5IG5pY2UgcGxvdHMuIFdoYXQgYWJvdXQgY29weWluZyB0aGVpciBzdHlsZSB3aGVuIHNoYXJpbmcgd2l0aCB0aGUgZ2VuZXJhbCBwdWJsaWM/IFNlZSwgaXQgY2FuIGJlIGEgbG90IG9mIHdvcmsgZ2V0dGluZyBldmVyeXRoaW5nIHRvIGxvb2sganVzdCByaWdodC4gVGFrZSBhIGxvb2sgYXQgdGhlIHRoZW1lIGZpbGUuIEl0IGNyZWF0ZXMgYSBuZXcgZnVuY3Rpb24gdGhhdCB5b3UgY2FuIHVzZSBvbiBqdXN0IGFib3V0IGFueSBwbG90LiBFZGl0IHRoZSBmdW5jdGlvbiBhbmQgeW91IGNhbiBlZGl0IGFsbCBvZiB0aGUgcGxvdHMgeW91IGFyZSBnb2luZyB0byBtYWtlIGdvaW5nIGZvcndhcmQuIFVzZWZ1bCBpZiB5b3Ugd2FudCBhIHVuaWZpZWQgc3R5bGUsIGJ1dCBhIGJpdCBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgdHV0b3JpYWwuIAoKYGBge3J9CiMgbG9hZCBuZWNlc3NhcnkgcGFja2FnZXMgYW5kIHRoZW1lIGZpbGUKIyBpbnN0YWxsLnBhY2thZ2VzKGMoIlJDb2xvckJyZXdlciIsInNjYWxlcyIpKQpsaWJyYXJ5KHNjYWxlcyk7IGxpYnJhcnkoZ3JpZCk7IGxpYnJhcnkoUkNvbG9yQnJld2VyKQpzb3VyY2UoInRoZW1lXzUzOC5SIikKCiMgcGxvdCB3aXRoIHRoZSB0aGVtZSBhZGRlZApwaF9ydHhlcnIgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArIGZ0ZV90aGVtZSgpCgpgYGAKCkl0IGlzIGNlcnRhaW5seSBtb2Rlcm4uIFdoYXQgZG9lcyBvdXIgaGlzdG9ncmFtIGxvb2sgbGlrZSB3aXRoIHRoZSB0aGVtZSBhZGRlZD8KCmBgYHtyfQpwaF9lcnJvcl9wbG90ICsgZ2VvbV9oaXN0b2dyYW0oKSArIGZ0ZV90aGVtZSgpCgpwaF9lcnJvcl9wbG90ICsgZ2VvbV9kb3RwbG90KCkgKyBmdGVfdGhlbWUoKQoKcGhfZXJyb3JfcGxvdCArIGdlb21fZGVuc2l0eSgpICsgZnRlX3RoZW1lKCkKCmBgYAoKU3VycHJpc2UhIFRocmVlIGRpZmZlcmVudCB3YXlzIHRvIHBsb3QgdGhpcyB0eXBlIG9mIGRhdGEuIEkgbGlrZSB0aGUgZG90IHBsb3QsIHBlcnNvbmFsbHkuIElzIHRoaXMgdGhlbWUgcGVyZmVjdCBmb3IgYWxsIG9mIHlvdXIgbmVlZHM/IFByb2JhYmx5IG5vdC4gKENlcnRhaW5seSBub3QuKSBXb3JrIGl0LiBDaGFuZ2UgaXQuIFR3aXN0IGl0LiBSZW1peCBpdC4gKElzIHRoYXQgYSBzb25nPyBJIGRvbid0IGV2ZW4ga25vdy4pCgpBbHJpZ2h0eSB0aGVuLCBJIGhhdmUgYmVlbiB0YWxraW5nICh3ZWxsLCB0eXBpbmcpIGEgd2hvbGUgbG90LiBUaW1lIGZvciB5b3UgdG8gZ2l2ZSBpdCBhbm90aGVyIHRyeS4gTGV0J3MgYmFjayB1cCBzbyB0aGF0IHlvdSBjYW4gY2xlYW4gdGhpcyBncmFwaCB1cCBhIGJpdC4gVGFrZSBhIGxvb2sgYXQgdGhlIGNoZWF0IHNoZWV0IGFuZCB0cnkgYW4gZW50aXJlbHkgbmV3IHR5cGUgb2YgcGxvdCwgaWYgeW91IGFyZSBmZWVsaW5nIGFtYml0aW91cy4KCmBgYHtyfQojIGNob29zZSB5b3VyIG93biBhZHZlbnR1cmUgcGxvdApwaF9ydHhlcnIgKyBnZW9tX3BvaW50KCkKCmBgYAoKSSBsaWtlIHlvdXIgc3R5bGUuIFRoYXQgb25lIGlzIGEga2VlcGVyLiBMZXQncyBzYXZlIGl0IGFuZCBiZSBkb25lIHdpdGggdGhpcy4KCmBgYHtyfQojIHNhdmUgdGhlIGxhc3QgcGxvdApnZ3NhdmUoImNvb2xfcGxvdC5wbmciLCB3aWR0aCA9IDcsIGhlaWdodCA9IDcpCgpgYGAKClBpZWNlIG9mIGNha2UhIChJY2UgY3JlYW0gY2FrZSwgcHJlZmVyYWJseS4gSSdtIHdhaXRpbmcuKSBUaGVyZSB5b3UgaGF2ZSBpdCwgZm9sa3MuIFlvdSBsZWFybmVkIHRoZSBlbnRpcmUgZ3JhbW1hciBvZiBncmFwaGljcy4gQSBuZXcgbGFuZ3VhZ2UuIFlvdSBzcGVhayAod2VsbCwgdHlwZSkgaXQgZmx1ZW50bHkgbm93LiBDYW4geW91IGZlZWwgaXQ/IE5vLiBXZWxsLCBzb3JyeSEgQW4gZW50aXJlIGNvdXJzZSBjb3VsZCBiZSB0YXVnaHQgb24gZ2dwbG90LiBMb29rIGZvcndhcmQgdG8gIkdldHRpbmcgZ29vZGVyIGF0IGdncGxvdDIiIChtYXliZSwgbm8gcHJvbWlzZXMpLiA=