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

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==