This document was created for a workshop I ran at NYU in 2018. It contains explanations and examples of using ggplot2()
to generate plots using tidy data in R.
ggplot is part of the tidyverse() package
#install.packages("tidyverse")
library(tidyverse)
ggplot
the grammar of graphics
- data the data you want to plot
- aesthetics how the data is mapped
- geometries vizualization of the data
- stats representations of data that aid understanding
- coordinates space on which data is plotted
- facets how plots are subsetted
- themes non-data aspects of plots
ggplot
topics
- basic plotting
- scale adjustments
- position adjustment
- zooming
- facetting
- labels
- themes
ggplot
the basic syntax
ggplot(data = <DATA>, mapping = aes(<Mapping>)) +
<GEOM_FUNCTION>()
ggplot constraints
- data must be in a dataframe
- data should be in tidy format
- each variable must have its own column
- each observation must have its own row
- each value must have its own cell
- for gene expression data this means one row per gene per experiment
example dataset (modified yeast gff file)
feature and chromosome are factors
levels(yeast_features$feature)
[1] "CDS" "chromosome" "exon" "gene" "mRNA" "ncRNA_gene" "pseudogene"
[8] "rRNA" "rRNA_gene" "snoRNA" "snoRNA_gene" "snRNA" "snRNA_gene" "transcript"
[15] "tRNA_gene"
levels(yeast_features$chromosome)
[1] "I" "II" "III" "IV" "IX" "Mito" "V" "VI" "VII" "VIII" "X" "XI" "XII" "XIII" "XIV"
[16] "XV" "XVI"
view structure using str()
str(yeast_features)
tibble [7,448 × 6] (S3: tbl_df/tbl/data.frame)
$ chromosome: Factor w/ 17 levels "I","II","III",..: 1 1 1 1 1 1 1 1 1 1 ...
$ feature : Factor w/ 15 levels "CDS","chromosome",..: 1 1 1 1 1 1 1 1 1 1 ...
$ start : num [1:7448] 335 538 1807 2480 7235 ...
$ stop : num [1:7448] 649 792 2169 2707 9016 ...
$ strand : Factor w/ 3 levels "-",".","+": 3 3 1 3 1 3 1 3 1 3 ...
$ length : num [1:7448] 314 254 362 227 1781 ...
histogram of feature lengths
ggplot(data = yeast_features, mapping = aes(x = length)) +
geom_histogram()

plot as a continuous distribution
ggplot(data = yeast_features, mapping = aes(x = length)) +
geom_freqpoly()

color according to feature type
ggplot(data = yeast_features, mapping = aes(x = length)) +
geom_freqpoly(mapping = aes(color = feature))

Plot as a probability density
ggplot(data = yeast_features, mapping = aes(x = length, y = ..density..,color = feature)) +
geom_freqpoly()

Plot all data using geom_point
ggplot(data = yeast_features, mapping = aes(x = chromosome, y = length)) +
geom_point(aes(color = feature))

Dealing with overplotting using jitter
ggplot(data = yeast_features, mapping = aes(x = chromosome, y = length)) +
geom_point(aes(color = feature), position = "jitter")

Dealing with overplotting using alpha
ggplot(data = yeast_features, mapping = aes(x = chromosome, y = length)) +
geom_jitter(aes(color = feature), alpha = 1/10)

Boxplots using geom_boxplot()
ggplot(data = yeast_features, mapping = aes(x = chromosome, y = length)) +
geom_boxplot()

Mapping aesthetics within layers
ggplot(data = yeast_features, mapping = aes(x = chromosome, y = length, color = feature)) +
geom_boxplot()

Combining layers
ggplot(data = yeast_features, mapping = aes(x = chromosome, y = length)) +
geom_boxplot() +
geom_point(position = "jitter", shape = ".", mapping= aes(color = feature))

Modifying scales
ggplot(data = yeast_features, mapping = aes(x = chromosome, y = length)) +
geom_boxplot() +
geom_point(position = "jitter", shape = ".", mapping= aes(color = feature)) +
scale_y_log10()

Changing the order using reorder
ggplot(data = yeast_features, mapping = aes(x = reorder(chromosome, length, FUN = median), y = length)) +
geom_boxplot() +
geom_point(position = "jitter", shape = ".", mapping= aes(color = feature)) +
scale_y_log10()

Flipping coordinates
ggplot(data = yeast_features, mapping = aes(x = reorder(chromosome, length, FUN = median), y = length)) +
geom_boxplot() +
geom_point(position = "jitter", shape = ".", mapping= aes(color = feature)) +
scale_y_log10() +
coord_flip()

Barplots
With one categorical datatype (factor): chromosome
ggplot(data = yeast_features, mapping = aes(x = chromosome, fill = chromosome)) +
geom_bar()

Barplots
two categorical datatypes/factors: chromosome & features
ggplot(data = yeast_features, mapping = aes(x = chromosome, fill = feature)) +
geom_bar()

Barplot variant: fill
ggplot(data = yeast_features, mapping = aes(x = chromosome, fill = feature)) +
geom_bar(position = "fill")

Barplot variant: dodge
ggplot(data = yeast_features, mapping = aes(x = chromosome, fill = feature)) +
geom_bar(position = "dodge")

Computing and ploting statistics
ggplot(data = yeast_features, mapping = aes(x = feature, y = length)) +
stat_summary(fun.data = mean_sdl)

Facetting plots
ggplot(data = yeast_features, mapping = aes(x = length, fill = chromosome)) +
geom_histogram() +
facet_wrap( ~ chromosome)

Facetting (grid)
ggplot(data = yeast_features, mapping = aes(x = length, y = ..density..)) +
geom_histogram() +
facet_wrap(feature ~ strand, nrow = 2)

Scatter plots
ggplot(data = yeast_features, mapping = aes(x = start, y = stop)) +
geom_point(aes(size = feature, color = feature, shape = feature), alpha = 1/3) +
scale_x_log10() +
scale_y_log10()

Statistics: adding trend line
ggplot(data = yeast_features, mapping = aes(x = start, y = stop)) +
geom_point(aes(size = feature, color = feature, shape = feature), alpha = 1/3) +
scale_x_log10() +
scale_y_log10() +
geom_smooth()

Statistics: controlling trend line
ggplot(data = yeast_features, mapping = aes(x = start, y = stop)) +
geom_point(aes(size = feature, color = feature, shape = feature), alpha = 1/3) +
scale_x_log10() +
scale_y_log10() +
geom_smooth(method = "lm")

Colors (setting color)
ggplot(data = yeast_features, mapping = aes(x = length, fill = chromosome)) +
geom_histogram(fill = "red") +
scale_x_log10() +
facet_wrap( ~ chromosome)

Zooming in with coord_cartesian()
ggplot(data = yeast_features, mapping = aes(x = start, y = stop)) +
geom_point(aes(size = feature, color = feature, shape = feature), alpha = 1/3) +
scale_x_log10() +
scale_y_log10() +
geom_smooth() +
coord_cartesian(xlim = c(10,5000), ylim = c(10,5000))

Adding Labels to plots
ggplot(data = yeast_features, mapping = aes(x = length, y = ..density..)) +
geom_freqpoly(mapping = aes(color = feature)) +
scale_x_log10() +
labs(
title = "Distribution of feature sizes",
x = "length (base pairs)",
y = "probability density"
) +
theme(legend.position = "bottom")

Themes change the overall look
ggplot(data = yeast_features, mapping = aes(x = length, y = ..density..)) +
geom_freqpoly(mapping = aes(color = feature)) +
scale_x_log10() +
labs(
title = "Distribution of feature sizes",
x = "length (base pairs)",
y = "probability density"
) +
theme(legend.position = "bottom") +
theme_light()

Themes
more themes available in add-on package ggthemes
#install.packages("ggthemes")
library(ggthemes)
Additional themes : theme_tufte()
ggplot(data = yeast_features, mapping = aes(x = length, y = ..density..)) +
geom_freqpoly(mapping = aes(color = feature)) +
scale_x_log10() +
theme_tufte()

Additional themes : theme_excel()
Don’t do this!
ggplot(data = yeast_features, mapping = aes(x = length, y = ..density..)) +
geom_freqpoly(mapping = aes(color = feature)) +
scale_x_log10() +
theme_excel()

Generic plot function
ggplot(data = <DATA>, mapping = aes(<Mapping>)) +
<GEOM_FUNCTION>() +
<STAT_FUNCTYION>() +
<FACET_FUNCTION>() +
<SCALE_FUNCTION>() +
<THEME_FUNCTION>()
Concise code
code can be made concise excluding some definitions
ggplot(yeast_features, aes(length, ..density..)) +
geom_freqpoly(aes(color = feature)) +
scale_x_log10() +
theme(legend.position = "bottom") +
theme_light()

Saving plots as variables
my_plot <- ggplot(yeast_features, aes(length, ..density..)) +
geom_freqpoly(aes(color = feature)) +
scale_x_log10() +
labs(
title = "Distribution of feature sizes",
x = "length (base pairs)",
y = "probability density"
) +
theme(legend.position = "bottom") +
theme_light()
Adding to plot variables
my_plot + geom_hline(yintercept = 2, color = "red")

Exercise 1
Add: 1. new axis labels, 2. title, 3. trend line, and 4. change theme to tufte_theme
ggplot(data = a, aes(x = x, y = y, color = Class))+
geom_point(size = 3)

Read in and modify gff
gff <- read_delim("Saccharomyces_cerevisiae.R64-1-1.34.gff3",
"\t", escape_double = FALSE, col_names = FALSE,
comment = "#", trim_ws = TRUE, skip = 24)
── Column specification ────────────────────────────────────────────────────────────────────────────────────────
cols(
X1 = col_character(),
X2 = col_character(),
X3 = col_character(),
X4 = col_double(),
X5 = col_double(),
X6 = col_character(),
X7 = col_character(),
X8 = col_character(),
X9 = col_character()
)
names(gff) <- c("chromosome",
"source",
"feature",
"start",
"stop",
"unknown1",
"strand",
"unknown2",
"info"
)
#correct data types
gff$feature = as.factor(gff$feature)
gff$chromosome = as.factor(gff$chromosome)
gff$strand = as.factor(gff$strand)
yeast_features <- gff %>%
select(chromosome, feature, start, stop, strand) %>%
mutate(length = abs(start - stop)) %>%
filter(feature == "CDS" | feature == "rRNA" | feature == "snoRNA" | feature == "snRNA" | feature == "tRNA_gene")
Exercise 2
Plot as barplots with error bars
ggplot(data = yeast_features, mapping = aes(x = feature, y = length)) +
stat_summary(fun.data = mean_sdl)

Exercise 3
Change bin size for histogram
ggplot(data = yeast_features, mapping = aes(x = length)) +
geom_histogram()

LS0tCnRpdGxlOiAiZ2dwbG90IgphdXRob3I6ICJEYXZpZCBHcmVzaGFtIgpkYXRlOiBMYXN0IGNvbXBpbGVkIG9uIGByIFN5cy5EYXRlKClgCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB5ZXMKLS0tCgpUaGlzIGRvY3VtZW50IHdhcyBjcmVhdGVkIGZvciBhIHdvcmtzaG9wIEkgcmFuIGF0IE5ZVSBpbiAyMDE4LiAgSXQgY29udGFpbnMgZXhwbGFuYXRpb25zIGFuZCBleGFtcGxlcyBvZiB1c2luZyBgZ2dwbG90MigpYCB0byBnZW5lcmF0ZSBwbG90cyB1c2luZyB0aWR5IGRhdGEgaW4gUi4gCgojIyBnZ3Bsb3QgaXMgcGFydCBvZiB0aGUgX3RpZHl2ZXJzZSgpXyBwYWNrYWdlCgpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9VFJVRSwgd2FybmluZz1UUlVFfQojaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyMgZ2dwbG90CgojIyMgdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MKCi0gX19kYXRhX18gIHRoZSBkYXRhIHlvdSB3YW50IHRvIHBsb3QKLSBfX2Flc3RoZXRpY3NfXyBob3cgdGhlIGRhdGEgaXMgbWFwcGVkCi0gX19nZW9tZXRyaWVzX18gdml6dWFsaXphdGlvbiBvZiB0aGUgZGF0YQotIF9fc3RhdHNfXyByZXByZXNlbnRhdGlvbnMgb2YgZGF0YSB0aGF0IGFpZCB1bmRlcnN0YW5kaW5nCi0gX19jb29yZGluYXRlc19fIHNwYWNlIG9uIHdoaWNoIGRhdGEgaXMgcGxvdHRlZAotIF9fZmFjZXRzX18gaG93IHBsb3RzIGFyZSBzdWJzZXR0ZWQKLSBfX3RoZW1lc19fIG5vbi1kYXRhIGFzcGVjdHMgb2YgcGxvdHMKCiMjIGdncGxvdAoKIyMjIHRvcGljcwoKLSBiYXNpYyBwbG90dGluZwotIHNjYWxlIGFkanVzdG1lbnRzCi0gcG9zaXRpb24gYWRqdXN0bWVudAotIHpvb21pbmcKLSBmYWNldHRpbmcKLSBsYWJlbHMKLSB0aGVtZXMKCiMjIGdncGxvdAoKIyMjIHRoZSBiYXNpYyBzeW50YXgKCmBgYApnZ3Bsb3QoZGF0YSA9IDxEQVRBPiwgbWFwcGluZyA9IGFlcyg8TWFwcGluZz4pKSArCiAgICAgICAgPEdFT01fRlVOQ1RJT04+KCkKYGBgCgojIyBnZ3Bsb3QgY29uc3RyYWludHMKCi0gZGF0YSBtdXN0IGJlIGluIGEgZGF0YWZyYW1lCi0gZGF0YSBzaG91bGQgYmUgaW4gX3RpZHlfIGZvcm1hdAogICAgKyBlYWNoIHZhcmlhYmxlIG11c3QgaGF2ZSBpdHMgb3duIGNvbHVtbgogICAgKyBlYWNoIG9ic2VydmF0aW9uIG11c3QgaGF2ZSBpdHMgb3duIHJvdwogICAgKyBlYWNoIHZhbHVlIG11c3QgaGF2ZSBpdHMgb3duIGNlbGwKCi0gZm9yIGdlbmUgZXhwcmVzc2lvbiBkYXRhIHRoaXMgbWVhbnMgb25lIHJvdyBwZXIgZ2VuZSBwZXIgZXhwZXJpbWVudAogICAgKyBub3Qgb25lIHJvdyBwZXIgZ2VuZQoKIyMgZXhhbXBsZSBkYXRhc2V0IChtb2RpZmllZCB5ZWFzdCBnZmYgZmlsZSkKCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQpnZmYgPC0gcmVhZF9kZWxpbSgiU2FjY2hhcm9teWNlc19jZXJldmlzaWFlLlI2NC0xLTEuMzQuZ2ZmMyIsIAogICAgIlx0IiwgZXNjYXBlX2RvdWJsZSA9IEZBTFNFLCBjb2xfbmFtZXMgPSBGQUxTRSwgCiAgICBjb21tZW50ID0gIiMiLCB0cmltX3dzID0gVFJVRSwgc2tpcCA9IDI0KQpuYW1lcyhnZmYpIDwtIGMoImNocm9tb3NvbWUiLCAKICAgICAgICAgICAgICAgICJzb3VyY2UiLCAKICAgICAgICAgICAgICAgICJmZWF0dXJlIiwgCiAgICAgICAgICAgICAgICAic3RhcnQiLAogICAgICAgICAgICAgICAgInN0b3AiLCAKICAgICAgICAgICAgICAgICJ1bmtub3duMSIsCiAgICAgICAgICAgICAgICAic3RyYW5kIiwKICAgICAgICAgICAgICAgICJ1bmtub3duMiIsCiAgICAgICAgICAgICAgICAiaW5mbyIKICAgICAgICAgICAgICAgICkKI2NvcnJlY3QgZGF0YSB0eXBlcwpnZmYkZmVhdHVyZSA9IGFzLmZhY3RvcihnZmYkZmVhdHVyZSkKZ2ZmJGNocm9tb3NvbWUgPSBhcy5mYWN0b3IoZ2ZmJGNocm9tb3NvbWUpCmdmZiRzdHJhbmQgPSBhcy5mYWN0b3IoZ2ZmJHN0cmFuZCkKCnllYXN0X2ZlYXR1cmVzIDwtIGdmZiAlPiUKICAgICAgICBzZWxlY3QoY2hyb21vc29tZSwgZmVhdHVyZSwgc3RhcnQsIHN0b3AsIHN0cmFuZCkgJT4lCiAgICAgICAgbXV0YXRlKGxlbmd0aCA9IGFicyhzdGFydCAtIHN0b3ApKSAlPiUKICAgICAgICBmaWx0ZXIoZmVhdHVyZSA9PSAiQ0RTIiB8IGZlYXR1cmUgPT0gInJSTkEiIHwgZmVhdHVyZSA9PSAic25vUk5BIiB8IGZlYXR1cmUgPT0gInNuUk5BIiB8IGZlYXR1cmUgPT0gInRSTkFfZ2VuZSIpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFfQpoZWFkKHllYXN0X2ZlYXR1cmVzKQpgYGAKCiMjIGZlYXR1cmUgYW5kIGNocm9tb3NvbWUgYXJlIGZhY3RvcnMKCmBgYHtyLCBlY2hvPVRSVUV9CmxldmVscyh5ZWFzdF9mZWF0dXJlcyRmZWF0dXJlKQpsZXZlbHMoeWVhc3RfZmVhdHVyZXMkY2hyb21vc29tZSkKYGBgCgojIyB2aWV3IHN0cnVjdHVyZSB1c2luZyBzdHIoKSAKCmBgYHtyIGVjaG89VFJVRX0Kc3RyKHllYXN0X2ZlYXR1cmVzKQpgYGAKCgojIyBoaXN0b2dyYW0gb2YgZmVhdHVyZSBsZW5ndGhzIHsuc21hbGxjb2RlfSAgICAKCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkYXRhID0geWVhc3RfZmVhdHVyZXMsIG1hcHBpbmcgPSBhZXMoeCA9IGxlbmd0aCkpICsKICAgICAgICBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKIyMgcGxvdCBhcyBhIGNvbnRpbnVvdXMgZGlzdHJpYnV0aW9uCgpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IHllYXN0X2ZlYXR1cmVzLCBtYXBwaW5nID0gYWVzKHggPSBsZW5ndGgpKSArCiAgICAgICAgZ2VvbV9mcmVxcG9seSgpCmBgYAoKIyMgY29sb3IgYWNjb3JkaW5nIHRvIGZlYXR1cmUgdHlwZQoKYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gbGVuZ3RoKSkgKwogICAgICAgIGdlb21fZnJlcXBvbHkobWFwcGluZyA9IGFlcyhjb2xvciA9IGZlYXR1cmUpKQpgYGAKCiMjIFBsb3QgYXMgYSBwcm9iYWJpbGl0eSBkZW5zaXR5CgpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IHllYXN0X2ZlYXR1cmVzLCBtYXBwaW5nID0gYWVzKHggPSBsZW5ndGgsIHkgPSAuLmRlbnNpdHkuLixjb2xvciA9IGZlYXR1cmUpKSArCiAgICAgICAgZ2VvbV9mcmVxcG9seSgpCmBgYAoKIyMgUGxvdCBhbGwgZGF0YSB1c2luZyBnZW9tX3BvaW50CgpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IHllYXN0X2ZlYXR1cmVzLCBtYXBwaW5nID0gYWVzKHggPSBjaHJvbW9zb21lLCB5ID0gbGVuZ3RoKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZmVhdHVyZSkpCmBgYAoKIyMgRGVhbGluZyB3aXRoIG92ZXJwbG90dGluZyB1c2luZyBqaXR0ZXIKCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkYXRhID0geWVhc3RfZmVhdHVyZXMsIG1hcHBpbmcgPSBhZXMoeCA9IGNocm9tb3NvbWUsIHkgPSBsZW5ndGgpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBmZWF0dXJlKSwgcG9zaXRpb24gPSAiaml0dGVyIikKYGBgCgojIyBEZWFsaW5nIHdpdGggb3ZlcnBsb3R0aW5nIHVzaW5nIGFscGhhCgpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IHllYXN0X2ZlYXR1cmVzLCBtYXBwaW5nID0gYWVzKHggPSBjaHJvbW9zb21lLCB5ID0gbGVuZ3RoKSkgKwogICAgICAgIGdlb21faml0dGVyKGFlcyhjb2xvciA9IGZlYXR1cmUpLCBhbHBoYSA9IDEvMTApCmBgYAoKIyMgQm94cGxvdHMgdXNpbmcgZ2VvbV9ib3hwbG90KCkKCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkYXRhID0geWVhc3RfZmVhdHVyZXMsIG1hcHBpbmcgPSBhZXMoeCA9IGNocm9tb3NvbWUsIHkgPSBsZW5ndGgpKSArCiAgICAgICAgZ2VvbV9ib3hwbG90KCkKYGBgCgojIyBNYXBwaW5nIGFlc3RoZXRpY3Mgd2l0aGluIGxheWVycwoKYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gY2hyb21vc29tZSwgeSA9IGxlbmd0aCwgY29sb3IgPSBmZWF0dXJlKSkgKwogICAgICAgIGdlb21fYm94cGxvdCgpCmBgYAoKIyMgQ29tYmluaW5nIGxheWVycwoKYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gY2hyb21vc29tZSwgeSA9IGxlbmd0aCkpICsKICAgICAgICBnZW9tX2JveHBsb3QoKSArIAogICAgICAgIGdlb21fcG9pbnQocG9zaXRpb24gPSAiaml0dGVyIiwgc2hhcGUgPSAiLiIsIG1hcHBpbmc9IGFlcyhjb2xvciA9IGZlYXR1cmUpKQpgYGAKCiMjIE1vZGlmeWluZyBzY2FsZXMKCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkYXRhID0geWVhc3RfZmVhdHVyZXMsIG1hcHBpbmcgPSBhZXMoeCA9IGNocm9tb3NvbWUsIHkgPSBsZW5ndGgpKSArCiAgICAgICAgZ2VvbV9ib3hwbG90KCkgKyAKICAgICAgICBnZW9tX3BvaW50KHBvc2l0aW9uID0gImppdHRlciIsIHNoYXBlID0gIi4iLCBtYXBwaW5nPSBhZXMoY29sb3IgPSBmZWF0dXJlKSkgKwogICAgICAgIHNjYWxlX3lfbG9nMTAoKQoKYGBgCgojIyBDaGFuZ2luZyB0aGUgb3JkZXIgdXNpbmcgcmVvcmRlcgoKYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihjaHJvbW9zb21lLCBsZW5ndGgsIEZVTiA9IG1lZGlhbiksIHkgPSBsZW5ndGgpKSArCiAgICAgICAgZ2VvbV9ib3hwbG90KCkgKyAKICAgICAgICBnZW9tX3BvaW50KHBvc2l0aW9uID0gImppdHRlciIsIHNoYXBlID0gIi4iLCBtYXBwaW5nPSBhZXMoY29sb3IgPSBmZWF0dXJlKSkgKwogICAgICAgIHNjYWxlX3lfbG9nMTAoKQoKYGBgCgojIyBGbGlwcGluZyBjb29yZGluYXRlcyB7LnNtYWxsZXJ9CgpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IHllYXN0X2ZlYXR1cmVzLCBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNocm9tb3NvbWUsIGxlbmd0aCwgRlVOID0gbWVkaWFuKSwgeSA9IGxlbmd0aCkpICsKICAgICAgICBnZW9tX2JveHBsb3QoKSArIAogICAgICAgIGdlb21fcG9pbnQocG9zaXRpb24gPSAiaml0dGVyIiwgc2hhcGUgPSAiLiIsIG1hcHBpbmc9IGFlcyhjb2xvciA9IGZlYXR1cmUpKSArCiAgICAgICAgc2NhbGVfeV9sb2cxMCgpICsKICAgICAgICBjb29yZF9mbGlwKCkKCmBgYAoKIyMgQmFycGxvdHMKV2l0aCBvbmUgY2F0ZWdvcmljYWwgZGF0YXR5cGUgKGZhY3Rvcik6IGNocm9tb3NvbWUKCmBgYHtyLCBlY2hvPVRSVUV9CmdncGxvdChkYXRhID0geWVhc3RfZmVhdHVyZXMsIG1hcHBpbmcgPSBhZXMoeCA9IGNocm9tb3NvbWUsIGZpbGwgPSBjaHJvbW9zb21lKSkgKwogICAgICAgIGdlb21fYmFyKCkKYGBgCgojIyBCYXJwbG90cwp0d28gY2F0ZWdvcmljYWwgZGF0YXR5cGVzL2ZhY3RvcnM6IGNocm9tb3NvbWUgJiBmZWF0dXJlcwoKYGBge3IsIGVjaG89VFJVRX0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gY2hyb21vc29tZSwgZmlsbCA9IGZlYXR1cmUpKSArCiAgICAgICAgZ2VvbV9iYXIoKQpgYGAKCiMjIEJhcnBsb3QgdmFyaWFudDogZmlsbAoKYGBge3IsIGVjaG89VFJVRX0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gY2hyb21vc29tZSwgZmlsbCA9IGZlYXR1cmUpKSArCiAgICAgICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpCmBgYAoKIyMgQmFycGxvdCB2YXJpYW50OiBkb2RnZQoKYGBge3IsIGVjaG89VFJVRX0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gY2hyb21vc29tZSwgZmlsbCA9IGZlYXR1cmUpKSArCiAgICAgICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKCiMjIENvbXB1dGluZyBhbmQgcGxvdGluZyBzdGF0aXN0aWNzCgpgYGB7ciwgZWNobz1UUlVFfQpnZ3Bsb3QoZGF0YSA9IHllYXN0X2ZlYXR1cmVzLCBtYXBwaW5nID0gYWVzKHggPSBmZWF0dXJlLCB5ID0gbGVuZ3RoKSkgKwogICAgICAgIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IG1lYW5fc2RsKQpgYGAKCgoKIyMgRmFjZXR0aW5nIHBsb3RzCgpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IHllYXN0X2ZlYXR1cmVzLCBtYXBwaW5nID0gYWVzKHggPSBsZW5ndGgsIGZpbGwgPSBjaHJvbW9zb21lKSkgKwogICAgICAgIGdlb21faGlzdG9ncmFtKCkgKyAKICAgICAgICBmYWNldF93cmFwKCB+IGNocm9tb3NvbWUpIApgYGAKCiMjIEZhY2V0dGluZyAoZ3JpZCkKYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gbGVuZ3RoLCB5ID0gLi5kZW5zaXR5Li4pKSArCiAgICAgICAgZ2VvbV9oaXN0b2dyYW0oKSArIAogICAgICAgIGZhY2V0X3dyYXAoZmVhdHVyZSB+IHN0cmFuZCwgbnJvdyA9IDIpIApgYGAKCiMjIFNjYXR0ZXIgcGxvdHMKCmBgYHtyIGVjaG89VFJVRSwgZmlnLmhlaWdodD0zLjUsIGZpZy53aWR0aD00LjUsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkYXRhID0geWVhc3RfZmVhdHVyZXMsIG1hcHBpbmcgPSBhZXMoeCA9IHN0YXJ0LCB5ID0gc3RvcCkpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplID0gZmVhdHVyZSwgY29sb3IgPSBmZWF0dXJlLCBzaGFwZSA9IGZlYXR1cmUpLCBhbHBoYSA9IDEvMykgKwogICAgICAgIHNjYWxlX3hfbG9nMTAoKSArIAogICAgICAgIHNjYWxlX3lfbG9nMTAoKQpgYGAKCiMjIFN0YXRpc3RpY3M6IGFkZGluZyB0cmVuZCBsaW5lCgpgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9My41LCBmaWcud2lkdGg9NC41LCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IHllYXN0X2ZlYXR1cmVzLCBtYXBwaW5nID0gYWVzKHggPSBzdGFydCwgeSA9IHN0b3ApKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IGZlYXR1cmUsIGNvbG9yID0gZmVhdHVyZSwgc2hhcGUgPSBmZWF0dXJlKSwgYWxwaGEgPSAxLzMpICsKICAgICAgICBzY2FsZV94X2xvZzEwKCkgKyAKICAgICAgICBzY2FsZV95X2xvZzEwKCkgKyAKICAgICAgICBnZW9tX3Ntb290aCgpCmBgYAoKIyMgU3RhdGlzdGljczogY29udHJvbGxpbmcgdHJlbmQgbGluZQoKYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTMuNSwgZmlnLndpZHRoPTQuNSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gc3RhcnQsIHkgPSBzdG9wKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKHNpemUgPSBmZWF0dXJlLCBjb2xvciA9IGZlYXR1cmUsIHNoYXBlID0gZmVhdHVyZSksIGFscGhhID0gMS8zKSArCiAgICAgICAgc2NhbGVfeF9sb2cxMCgpICsgCiAgICAgICAgc2NhbGVfeV9sb2cxMCgpICsgCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikKYGBgCgojIyBDb2xvcnMgKHNldHRpbmcgY29sb3IpCgpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IHllYXN0X2ZlYXR1cmVzLCBtYXBwaW5nID0gYWVzKHggPSBsZW5ndGgsIGZpbGwgPSBjaHJvbW9zb21lKSkgKwogICAgICAgIGdlb21faGlzdG9ncmFtKGZpbGwgPSAicmVkIikgKyAKICAgICAgICBzY2FsZV94X2xvZzEwKCkgKwogICAgICAgIGZhY2V0X3dyYXAoIH4gY2hyb21vc29tZSkgCmBgYAoKIyMgWm9vbWluZyBpbiB3aXRoIGNvb3JkX2NhcnRlc2lhbigpCgpgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9My41LCBmaWcud2lkdGg9NC41LCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IHllYXN0X2ZlYXR1cmVzLCBtYXBwaW5nID0gYWVzKHggPSBzdGFydCwgeSA9IHN0b3ApKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IGZlYXR1cmUsIGNvbG9yID0gZmVhdHVyZSwgc2hhcGUgPSBmZWF0dXJlKSwgYWxwaGEgPSAxLzMpICsKICAgICAgICBzY2FsZV94X2xvZzEwKCkgKyAKICAgICAgICBzY2FsZV95X2xvZzEwKCkgKyAKICAgICAgICBnZW9tX3Ntb290aCgpICsKICAgICAgICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMTAsNTAwMCksIHlsaW0gPSBjKDEwLDUwMDApKQpgYGAKCiMjIEFkZGluZyBMYWJlbHMgdG8gcGxvdHMKYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTMuNSwgZmlnLndpZHRoPTYsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkYXRhID0geWVhc3RfZmVhdHVyZXMsIG1hcHBpbmcgPSBhZXMoeCA9IGxlbmd0aCwgeSA9IC4uZGVuc2l0eS4uKSkgKwogICAgICAgIGdlb21fZnJlcXBvbHkobWFwcGluZyA9IGFlcyhjb2xvciA9IGZlYXR1cmUpKSArCiAgICAgICAgc2NhbGVfeF9sb2cxMCgpICsKICAgICAgICBsYWJzKAogICAgICAgICAgICAgICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIGZlYXR1cmUgc2l6ZXMiLAogICAgICAgICAgICAgICAgeCA9ICJsZW5ndGggKGJhc2UgcGFpcnMpIiwKICAgICAgICAgICAgICAgIHkgPSAicHJvYmFiaWxpdHkgZGVuc2l0eSIKICAgICAgICApICsgCiAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmBgYAoKIyMgVGhlbWVzIGNoYW5nZSB0aGUgb3ZlcmFsbCBsb29rCgpgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9My41LCBmaWcud2lkdGg9NiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gbGVuZ3RoLCB5ID0gLi5kZW5zaXR5Li4pKSArCiAgICAgICAgZ2VvbV9mcmVxcG9seShtYXBwaW5nID0gYWVzKGNvbG9yID0gZmVhdHVyZSkpICsKICAgICAgICBzY2FsZV94X2xvZzEwKCkgKwogICAgICAgIGxhYnMoCiAgICAgICAgICAgICAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgZmVhdHVyZSBzaXplcyIsCiAgICAgICAgICAgICAgICB4ID0gImxlbmd0aCAoYmFzZSBwYWlycykiLAogICAgICAgICAgICAgICAgeSA9ICJwcm9iYWJpbGl0eSBkZW5zaXR5IgogICAgICAgICkgKyAKICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKyAKICAgICAgICB0aGVtZV9saWdodCgpCmBgYAoKIyMgVGhlbWVzCgptb3JlIHRoZW1lcyBhdmFpbGFibGUgaW4gYWRkLW9uIHBhY2thZ2UgX19nZ3RoZW1lc19fCgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoImdndGhlbWVzIikKbGlicmFyeShnZ3RoZW1lcykKYGBgCgojIyBBZGRpdGlvbmFsIHRoZW1lcyA6IHRoZW1lX3R1ZnRlKCkKCmBgYHtyIGVjaG89VFJVRSwgZmlnLmhlaWdodD0zLjUsIGZpZy53aWR0aD02LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IHllYXN0X2ZlYXR1cmVzLCBtYXBwaW5nID0gYWVzKHggPSBsZW5ndGgsIHkgPSAuLmRlbnNpdHkuLikpICsKICAgICAgICBnZW9tX2ZyZXFwb2x5KG1hcHBpbmcgPSBhZXMoY29sb3IgPSBmZWF0dXJlKSkgKwogICAgICAgIHNjYWxlX3hfbG9nMTAoKSArCiAgICAgICAgdGhlbWVfdHVmdGUoKQpgYGAKCiMjIEFkZGl0aW9uYWwgdGhlbWVzIDogdGhlbWVfZXhjZWwoKQpEb24ndCBkbyB0aGlzIQoKYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTMuNSwgZmlnLndpZHRoPTYsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkYXRhID0geWVhc3RfZmVhdHVyZXMsIG1hcHBpbmcgPSBhZXMoeCA9IGxlbmd0aCwgeSA9IC4uZGVuc2l0eS4uKSkgKwogICAgICAgIGdlb21fZnJlcXBvbHkobWFwcGluZyA9IGFlcyhjb2xvciA9IGZlYXR1cmUpKSArCiAgICAgICAgc2NhbGVfeF9sb2cxMCgpICsKICAgICAgICB0aGVtZV9leGNlbCgpCmBgYAoKCiMjIEdlbmVyaWMgcGxvdCBmdW5jdGlvbgoKYGBgCmdncGxvdChkYXRhID0gPERBVEE+LCBtYXBwaW5nID0gYWVzKDxNYXBwaW5nPikpICsKICAgICAgICA8R0VPTV9GVU5DVElPTj4oKSArIAogICAgICAgIDxTVEFUX0ZVTkNUWUlPTj4oKSArCiAgICAgICAgPEZBQ0VUX0ZVTkNUSU9OPigpICsgCiAgICAgICAgPFNDQUxFX0ZVTkNUSU9OPigpICsKICAgICAgICA8VEhFTUVfRlVOQ1RJT04+KCkKICAgICAgICAKYGBgCgojIyBDb25jaXNlIGNvZGUKY29kZSBjYW4gYmUgbWFkZSBjb25jaXNlIGV4Y2x1ZGluZyBzb21lIGRlZmluaXRpb25zCgpgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9My41LCBmaWcud2lkdGg9Nn0KZ2dwbG90KHllYXN0X2ZlYXR1cmVzLCBhZXMobGVuZ3RoLCAuLmRlbnNpdHkuLikpICsKICAgICAgICBnZW9tX2ZyZXFwb2x5KGFlcyhjb2xvciA9IGZlYXR1cmUpKSArCiAgICAgICAgc2NhbGVfeF9sb2cxMCgpICsKICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKyAKICAgICAgICB0aGVtZV9saWdodCgpCmBgYAoKIyMgU2F2aW5nIHBsb3RzIGFzIHZhcmlhYmxlcwoKYGBge3IsIGVjaG89VFJVRX0KbXlfcGxvdCA8LSBnZ3Bsb3QoeWVhc3RfZmVhdHVyZXMsIGFlcyhsZW5ndGgsIC4uZGVuc2l0eS4uKSkgKwogICAgICAgIGdlb21fZnJlcXBvbHkoYWVzKGNvbG9yID0gZmVhdHVyZSkpICsKICAgICAgICBzY2FsZV94X2xvZzEwKCkgKwogICAgICAgIGxhYnMoCiAgICAgICAgICAgICAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgZmVhdHVyZSBzaXplcyIsCiAgICAgICAgICAgICAgICB4ID0gImxlbmd0aCAoYmFzZSBwYWlycykiLAogICAgICAgICAgICAgICAgeSA9ICJwcm9iYWJpbGl0eSBkZW5zaXR5IgogICAgICAgICkgKyAKICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKyAKICAgICAgICB0aGVtZV9saWdodCgpCmBgYAoKIyMgQWRkaW5nIHRvIHBsb3QgdmFyaWFibGVzCgpgYGB7ciwgZWNobz1UUlVFfQpteV9wbG90ICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMiwgY29sb3IgPSAicmVkIikKYGBgCgojIyBFeGVyY2lzZSAxCkFkZDogMS4gbmV3IGF4aXMgbGFiZWxzLCAyLiB0aXRsZSwgMy4gdHJlbmQgbGluZSwgYW5kIDQuIGNoYW5nZSB0aGVtZSB0byB0dWZ0ZV90aGVtZQoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBhLCBhZXMoeCA9IHgsIHkgPSB5LCBjb2xvciA9IENsYXNzKSkrCiAgICAgICAgZ2VvbV9wb2ludChzaXplID0gMykKYGBgCgojIyBSZWFkIGluIGFuZCBtb2RpZnkgZ2ZmCgpgYGB7ciBlY2hvPVRSVUV9CmdmZiA8LSByZWFkX2RlbGltKCJTYWNjaGFyb215Y2VzX2NlcmV2aXNpYWUuUjY0LTEtMS4zNC5nZmYzIiwgCiAgICAiXHQiLCBlc2NhcGVfZG91YmxlID0gRkFMU0UsIGNvbF9uYW1lcyA9IEZBTFNFLCAKICAgIGNvbW1lbnQgPSAiIyIsIHRyaW1fd3MgPSBUUlVFLCBza2lwID0gMjQpCm5hbWVzKGdmZikgPC0gYygiY2hyb21vc29tZSIsIAogICAgICAgICAgICAgICAgInNvdXJjZSIsIAogICAgICAgICAgICAgICAgImZlYXR1cmUiLCAKICAgICAgICAgICAgICAgICJzdGFydCIsCiAgICAgICAgICAgICAgICAic3RvcCIsIAogICAgICAgICAgICAgICAgInVua25vd24xIiwKICAgICAgICAgICAgICAgICJzdHJhbmQiLAogICAgICAgICAgICAgICAgInVua25vd24yIiwKICAgICAgICAgICAgICAgICJpbmZvIgogICAgICAgICAgICAgICAgKQojY29ycmVjdCBkYXRhIHR5cGVzCmdmZiRmZWF0dXJlID0gYXMuZmFjdG9yKGdmZiRmZWF0dXJlKQpnZmYkY2hyb21vc29tZSA9IGFzLmZhY3RvcihnZmYkY2hyb21vc29tZSkKZ2ZmJHN0cmFuZCA9IGFzLmZhY3RvcihnZmYkc3RyYW5kKQoKeWVhc3RfZmVhdHVyZXMgPC0gZ2ZmICU+JQogICAgICAgIHNlbGVjdChjaHJvbW9zb21lLCBmZWF0dXJlLCBzdGFydCwgc3RvcCwgc3RyYW5kKSAlPiUKICAgICAgICBtdXRhdGUobGVuZ3RoID0gYWJzKHN0YXJ0IC0gc3RvcCkpICU+JQogICAgICAgIGZpbHRlcihmZWF0dXJlID09ICJDRFMiIHwgZmVhdHVyZSA9PSAiclJOQSIgfCBmZWF0dXJlID09ICJzbm9STkEiIHwgZmVhdHVyZSA9PSAic25STkEiIHwgZmVhdHVyZSA9PSAidFJOQV9nZW5lIikKYGBgCgojIyBFeGVyY2lzZSAyClBsb3QgYXMgYmFycGxvdHMgd2l0aCBlcnJvciBiYXJzCmBgYHtyIGVjaG89VFJVRSwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gZmVhdHVyZSwgeSA9IGxlbmd0aCkpICsKICAgICAgICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NkbCkKYGBgCgojIyBFeGVyY2lzZSAzCkNoYW5nZSBiaW4gc2l6ZSBmb3IgaGlzdG9ncmFtCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChkYXRhID0geWVhc3RfZmVhdHVyZXMsIG1hcHBpbmcgPSBhZXMoeCA9IGxlbmd0aCkpICsKICAgICAgICBnZW9tX2hpc3RvZ3JhbSgpCmBgYA==