By Angus Ball
Welcome to learn how to make relative abundance graphs! Some say my
codes actually pretty good so this should be a treat! This first graph
will be a generic bar graph with all samples.
I’m working with FUNGuild data in this example. (This explains why I
have more options to choose from in my taxVal)
First things first get some packages
library(phyloseq)
library(speedyseq)
library(ggplot2)
library(tidyverse)
Second choose what you want to plot!
taxVal <- "Phylum" #What taxa do you want
samdata1 <- "Location" #First sample metadata to facet_grid
samdata2 <- "Layer" #second sample metadata to facet_grid
#filterVal <- "p__Ascomycota"
#filterVal <- ""
filepathing <- "C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\Bioinformatics\\Kenzies Data\\test.pdf" #this is where you want your plots to be stored on your computer, remember it has to change for each graph!, don't make fun of my folder structure...
This is where the glory of this code really starts, you NEVER need to
modify code below this and it should just work! (The obvious exception
being the ggplot function which you may want to modify for your own
pastel driven dreams!)
Eitherway, you will put whatever taxa or FUNGuild column name in
taxVal youd like What are your options? try:
physeq_Key %>% rank_names
[1] "Kingdom" "Phylum" "Class" "Order" "Family"
[6] "Genus" "Species" "taxon" "guid" "mbNumber"
[11] "taxonomicLevel" "trophicMode" "guild" "confidenceRanking" "growthForm"
[16] "trait" "notes" "citationSource"
Any of the above options are valid.
The same goes for sample data (samdata)
physeq_Key %>% sample_variables()
[1] "Location" "Layer" "Treatment" "Site" "FullName"
Any of these are valid choices!, They do matter which order you put
them in, ie samdata1 will facet_grid the data before samdata2 but just
run the script once or twice and it’ll make sense.
What about filterVal? Say you only wanted to plot the p__Ascomycota
species in your graph? to do a bulk comparison of just them. Well
uncomment filterVal and put p__Ascomycota in the quotes. This can be
done with any species taxonomy or any information/subsample from
funguild. PS. you’ll also have to uncomment a line of code before
plotting. I’ll include a graph of what this looks like at the very
end
Quick side point: wanna separate your data into separate graphs? Use
this command to choose what samples you want based on metadata for this
run and then change it later on
#physeq_Subset <- subset_samples(physeq_Key, Location != "Fallow")
This removes all the samples that are from Fallow land
What if you only wanted specific taxa associated with trophic
mode?
#physeq_Subset<-subset_taxa(physeq_Key, trophicMode == "Symbiotroph")
Note that these are sending to physeq_test and not _key or _Bar as
expected in a second, this is to make sure you don’t accidentally throw
these commands in your code and destroy something
And honestly, you can just run the code from here on out and it’ll
just work, but for the fool who wants to understand~…
This command physically changes the column names of your phyloseq
object, The names that are choosen are based off of what input you
provided above. Notice that the first command reads from physeq_Key, if
you wanted to subset any of your samples as above, replace this with
physeq_Subset
physeq_Bar<- rename_tax_table(physeq_Key, TaxChoice = taxVal)
physeq_Bar <- rename_sample_data(physeq_Bar, SamChoice1 = samdata1)
physeq_Bar <- rename_sample_data(physeq_Bar, SamChoice2 = samdata2)
This commander transforms our samples to relative abundance Note this
isn’t clr transformed, but I think thanks okay, we are accounting for
compositionality with relative abundance sorta, and we arent using this
data for statistical tests, just visualization.
physeq_rel = transform_sample_counts(physeq_Bar, function(x) x/sum(x)*100)
we first con(glom)erate the taxa to a specific taxrank dictated by
your choice above, NArm means NA remove, don’t remove your NAs!!!! this
looses data especially if you are using environmental data were not
every microbe in the world has been categorized. The other two commands
are modifying the tables so they can be read by ggplot easily
glom <- tax_glom(physeq_rel, taxrank = 'TaxChoice', NArm = FALSE)
physeq_melt <- psmelt(glom)
physeq_melt$TaxChoice <- as.character(physeq_melt$TaxChoice)
This code takes the groups we’ve chosen before and figures out the
median abundance of each OTU in each sample
physeq_melt <- physeq_melt %>%
group_by(SamChoice1, SamChoice2,TaxChoice) %>%
mutate(median=median(Abundance))
Which is immediately useful, because we remove OTUs that are less
than 1% in a sample. They are still represented in the graph, but they
don’t get their own category. You Don’t Have to do this, or you can make
the value higher, its just to make the graph a bit more readable
keep <- unique(physeq_melt$TaxChoice[physeq_melt$median > 1])
physeq_melt$TaxChoice[!(physeq_melt$TaxChoice %in% keep)] <- "< 1%"
Similar command as above, but summarizes all the information
physeq_melt_sum <- physeq_melt %>%
group_by(Sample,SamChoice1, SamChoice2,TaxChoice) %>%
summarise(Abundance=sum(Abundance))
`summarise()` has grouped output by 'Sample', 'SamChoice1', 'SamChoice2'. You can override using the `.groups` argument.
Hey I said filterVal would come up again! Uncomment this code if you
want to apply the filter!
#physeq_melt_sum <- filter(physeq_melt_sum, TaxChoice == filterVal)
Lets plot it!
ggplot(physeq_melt_sum, aes(x = Sample, y = Abundance, fill = TaxChoice)) +
geom_bar(stat = "identity", aes(fill=TaxChoice)) +
labs(x="", y="%") +
facet_wrap(SamChoice1~SamChoice2, scales= "free_x", nrow=1) +
scale_fill_discrete(name = taxVal)+
theme_classic() +
#scale_x_discrete(guide=guide_axis(n.dodge=3))+
theme(strip.background = element_blank(),
axis.text.x.bottom = element_text(angle = -90))
ggsave(filepathing)
Saving 7.29 x 4.5 in image

You’ll have to decide what your ggsave location is.
Looking at the graph, one of the important, non-normal things added
is scale_fill_discrete, we need this to rename the the legend because of
the object, taxVal, shenanigans earlier.
Looks great right? well, sorta I guess, see how the sample type text
is crumpled on the screen, well try expanding your plots view in R to
expand the spacing in the sample (you can also do this in ggsave by
setting sizes), or playing around with the n.dodge function I’ve
commented out in the gg plot formula (I dont like the look of it tho).
You can also plot some samples by themselves to manage images.
Hey hey HEY, whats NA, and why are there phylum’s under <1%? NA
stands for not available, and are reflective of sequences that exist but
we havent categorized them into species yet. but there being phylum’s
under <1%?, rerun the same code without the <1% modification.
ggplot(physeq_melt_sum, aes(x = Sample, y = Abundance, fill = TaxChoice)) +
geom_bar(stat = "identity", aes(fill=TaxChoice)) +
labs(x="", y="%") +
facet_wrap(SamChoice1~SamChoice2, scales= "free_x", nrow=1) +
scale_fill_discrete(name = taxVal)+
theme_classic() +
#scale_x_discrete(guide=guide_axis(n.dodge=3))+
theme(strip.background = element_blank(),
axis.text.x.bottom = element_text(angle = -90))

what the frick, why are there anthropod samples in my fungal
data!
This is a good example of why you must always review your data! and
relative abundance plots are a good way to do it! I’m going to go back
and remove these phylums so they dont affect downstream analysis
As promised heres a graph of only the Ascomycota samples
ggplot(physeq_melt_sum, aes(x = Sample, y = Abundance, fill = TaxChoice)) +
geom_bar(stat = "identity", aes(fill=TaxChoice)) +
labs(x="", y="%") +
facet_wrap(SamChoice1~SamChoice2, scales= "free_x", nrow=1) +
scale_fill_discrete(name = taxVal)+
theme_classic() +
#scale_x_discrete(guide=guide_axis(n.dodge=3))+
theme(strip.background = element_blank(),
axis.text.x.bottom = element_text(angle = -90))

LS0tDQp0aXRsZTogIlJlbGF0aXZlIEFidW5kYW5jZSBHcmFwaHMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpCeSBBbmd1cyBCYWxsDQoNCg0KV2VsY29tZSB0byBsZWFybiBob3cgdG8gbWFrZSByZWxhdGl2ZSBhYnVuZGFuY2UgZ3JhcGhzIQ0KU29tZSBzYXkgbXkgY29kZXMgYWN0dWFsbHkgcHJldHR5IGdvb2Qgc28gdGhpcyBzaG91bGQgYmUgYSB0cmVhdCENClRoaXMgZmlyc3QgZ3JhcGggd2lsbCBiZSBhIGdlbmVyaWMgYmFyIGdyYXBoIHdpdGggYWxsIHNhbXBsZXMuDQoNCkknbSB3b3JraW5nIHdpdGggRlVOR3VpbGQgZGF0YSBpbiB0aGlzIGV4YW1wbGUuIChUaGlzIGV4cGxhaW5zIHdoeSBJIGhhdmUgbW9yZSBvcHRpb25zIHRvIGNob29zZSBmcm9tIGluIG15IHRheFZhbCkNCg0KDQpGaXJzdCB0aGluZ3MgZmlyc3QgZ2V0IHNvbWUgcGFja2FnZXMNCmBgYHtyLCBlY2hvID0gVCwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkocGh5bG9zZXEpDQpsaWJyYXJ5KHNwZWVkeXNlcSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNClNlY29uZCBjaG9vc2Ugd2hhdCB5b3Ugd2FudCB0byBwbG90IQ0KYGBge3J9DQp0YXhWYWwgPC0gIlBoeWx1bSIgI1doYXQgdGF4YSBsZXZlbCBkbyB5b3UgY2FyZSBhYm91dA0Kc2FtZGF0YTEgPC0gIkxvY2F0aW9uIiAjRmlyc3Qgc2FtcGxlIG1ldGFkYXRhIHRvIGZhY2V0X2dyaWQNCnNhbWRhdGEyIDwtICJMYXllciIgI3NlY29uZCBzYW1wbGUgbWV0YWRhdGEgdG8gZmFjZXRfZ3JpZA0KI2ZpbHRlclZhbCA8LSAicF9fQXNjb215Y290YSIgI2ZpbHRlciB0byBvbmx5IHRoaXMgZ3JvdXAsIGlmIHlvdSB3YW50DQpmaWxlcGF0aGluZyA8LSAiQzpcXFVzZXJzXFxhbmd1c1xcT25lRHJpdmUgLSBVTkJDXFxBbmd1cyBCYWxsXFxMYWIgd29ya1xcQmlvaW5mb3JtYXRpY3NcXEtlbnppZXMgRGF0YVxcdGVzdC5wZGYiICN0aGlzIGlzIHdoZXJlIHlvdSB3YW50IHlvdXIgcGxvdHMgdG8gYmUgc3RvcmVkIG9uIHlvdXIgY29tcHV0ZXIsIHJlbWVtYmVyIGl0IGhhcyB0byBjaGFuZ2UgZm9yIGVhY2ggZ3JhcGghLCBkb24ndCBtYWtlIGZ1biBvZiBteSBmb2xkZXIgc3RydWN0dXJlLi4uDQpgYGANClRoaXMgaXMgd2hlcmUgdGhlIGdsb3J5IG9mIHRoaXMgY29kZSByZWFsbHkgc3RhcnRzLCB5b3UgTkVWRVIgbmVlZCB0byBtb2RpZnkgY29kZSBiZWxvdyB0aGlzIGFuZCBpdCBzaG91bGQganVzdCB3b3JrISAoVGhlIG9idmlvdXMgZXhjZXB0aW9uIGJlaW5nIHRoZSBnZ3Bsb3QgZnVuY3Rpb24gd2hpY2ggeW91IG1heSB3YW50IHRvIG1vZGlmeSBmb3IgeW91ciBvd24gcGFzdGVsIGRyaXZlbiBkcmVhbXMhKQ0KDQpFaXRoZXJ3YXksIHlvdSB3aWxsIHB1dCB3aGF0ZXZlciB0YXhhIG9yIEZVTkd1aWxkIGNvbHVtbiBuYW1lIGluIHRheFZhbCB5b3VkIGxpa2UNCldoYXQgYXJlIHlvdXIgb3B0aW9ucz8gdHJ5Og0KYGBge3J9DQpwaHlzZXFfS2V5ICU+JSByYW5rX25hbWVzDQpgYGANCkFueSBvZiB0aGUgYWJvdmUgb3B0aW9ucyBhcmUgdmFsaWQuDQoNClRoZSBzYW1lIGdvZXMgZm9yIHNhbXBsZSBkYXRhIChzYW1kYXRhKQ0KYGBge3J9DQpwaHlzZXFfS2V5ICU+JSBzYW1wbGVfdmFyaWFibGVzKCkNCmBgYA0KQW55IG9mIHRoZXNlIGFyZSB2YWxpZCBjaG9pY2VzISwgVGhleSBkbyBtYXR0ZXIgd2hpY2ggb3JkZXIgeW91IHB1dCB0aGVtIGluLCBpZSBzYW1kYXRhMSB3aWxsIGZhY2V0X2dyaWQgdGhlIGRhdGEgYmVmb3JlIHNhbWRhdGEyIGJ1dCBqdXN0IHJ1biB0aGUgc2NyaXB0IG9uY2Ugb3IgdHdpY2UgYW5kIGl0J2xsIG1ha2Ugc2Vuc2UuDQoNCldoYXQgYWJvdXQgZmlsdGVyVmFsPyBTYXkgeW91IG9ubHkgd2FudGVkIHRvIHBsb3QgdGhlIHBfX0FzY29teWNvdGEgc3BlY2llcyBpbiB5b3VyIGdyYXBoPyB0byBkbyBhIGJ1bGsgY29tcGFyaXNvbiBvZiBqdXN0IHRoZW0uIFdlbGwgdW5jb21tZW50IGZpbHRlclZhbCBhbmQgcHV0IHBfX0FzY29teWNvdGEgaW4gdGhlIHF1b3Rlcy4gVGhpcyBjYW4gYmUgZG9uZSB3aXRoIGFueSBzcGVjaWVzIHRheG9ub215IG9yIGFueSBpbmZvcm1hdGlvbi9zdWJzYW1wbGUgZnJvbSBmdW5ndWlsZC4gUFMuIHlvdSdsbCBhbHNvIGhhdmUgdG8gdW5jb21tZW50IGEgbGluZSBvZiBjb2RlIGJlZm9yZSBwbG90dGluZy4gSSdsbCBpbmNsdWRlIGEgZ3JhcGggb2Ygd2hhdCB0aGlzIGxvb2tzIGxpa2UgYXQgdGhlIHZlcnkgZW5kDQoNCg0KDQoNClF1aWNrIHNpZGUgcG9pbnQ6IHdhbm5hIHNlcGFyYXRlIHlvdXIgZGF0YSBpbnRvIHNlcGFyYXRlIGdyYXBocz8gVXNlIHRoaXMgY29tbWFuZCB0byBjaG9vc2Ugd2hhdCBzYW1wbGVzIHlvdSB3YW50IGJhc2VkIG9uIG1ldGFkYXRhIGZvciB0aGlzIHJ1biBhbmQgdGhlbiBjaGFuZ2UgaXQgbGF0ZXIgb24NCg0KYGBge3J9DQojcGh5c2VxX1N1YnNldCA8LSBzdWJzZXRfc2FtcGxlcyhwaHlzZXFfS2V5LCBMb2NhdGlvbiAhPSAiRmFsbG93IikNCmBgYA0KVGhpcyByZW1vdmVzIGFsbCB0aGUgc2FtcGxlcyB0aGF0IGFyZSBmcm9tIEZhbGxvdyBsYW5kDQoNCldoYXQgaWYgeW91IG9ubHkgd2FudGVkIHNwZWNpZmljIHRheGEgYXNzb2NpYXRlZCB3aXRoIHRyb3BoaWMgbW9kZT8NCg0KYGBge3J9DQojcGh5c2VxX1N1YnNldDwtc3Vic2V0X3RheGEocGh5c2VxX0tleSwgdHJvcGhpY01vZGUgPT0gIlN5bWJpb3Ryb3BoIikNCmBgYA0KDQpOb3RlIHRoYXQgdGhlc2UgYXJlIHNlbmRpbmcgdG8gcGh5c2VxX3Rlc3QgYW5kIG5vdCBfa2V5IG9yIF9CYXIgYXMgZXhwZWN0ZWQgaW4gYSBzZWNvbmQsIHRoaXMgaXMgdG8gbWFrZSBzdXJlIHlvdSBkb24ndCBhY2NpZGVudGFsbHkgdGhyb3cgdGhlc2UgY29tbWFuZHMgaW4geW91ciBjb2RlIGFuZCBkZXN0cm95IHNvbWV0aGluZw0KDQoNCg0KDQoNCkFuZCBob25lc3RseSwgeW91IGNhbiBqdXN0IHJ1biB0aGUgY29kZSBmcm9tIGhlcmUgb24gb3V0IGFuZCBpdCdsbCBqdXN0IHdvcmssIGJ1dCBmb3IgdGhlIGZvb2wgd2hvIHdhbnRzIHRvIHVuZGVyc3RhbmR+Li4uDQoNClRoaXMgY29tbWFuZCBwaHlzaWNhbGx5IGNoYW5nZXMgdGhlIGNvbHVtbiBuYW1lcyBvZiB5b3VyIHBoeWxvc2VxIG9iamVjdCwgVGhlIG5hbWVzIHRoYXQgYXJlIGNob29zZW4gYXJlIGJhc2VkIG9mZiBvZiB3aGF0IGlucHV0IHlvdSBwcm92aWRlZCBhYm92ZS4gTm90aWNlIHRoYXQgdGhlIGZpcnN0IGNvbW1hbmQgcmVhZHMgZnJvbSBwaHlzZXFfS2V5LCBpZiB5b3Ugd2FudGVkIHRvIHN1YnNldCBhbnkgb2YgeW91ciBzYW1wbGVzIGFzIGFib3ZlLCByZXBsYWNlIHRoaXMgd2l0aCBwaHlzZXFfU3Vic2V0DQpgYGB7cn0NCnBoeXNlcV9CYXI8LSByZW5hbWVfdGF4X3RhYmxlKHBoeXNlcV9LZXksIFRheENob2ljZSA9IHRheFZhbCkNCnBoeXNlcV9CYXIgPC0gcmVuYW1lX3NhbXBsZV9kYXRhKHBoeXNlcV9CYXIsIFNhbUNob2ljZTEgPSBzYW1kYXRhMSkNCnBoeXNlcV9CYXIgPC0gcmVuYW1lX3NhbXBsZV9kYXRhKHBoeXNlcV9CYXIsIFNhbUNob2ljZTIgPSBzYW1kYXRhMikNCmBgYA0KDQoNCg0KDQpUaGlzIGNvbW1hbmRlciB0cmFuc2Zvcm1zIG91ciBzYW1wbGVzIHRvIHJlbGF0aXZlIGFidW5kYW5jZSANCk5vdGUgdGhpcyBpc24ndCBjbHIgdHJhbnNmb3JtZWQsIGJ1dCBJIHRoaW5rIHRoYW5rcyBva2F5LCB3ZSBhcmUgYWNjb3VudGluZyBmb3IgY29tcG9zaXRpb25hbGl0eSB3aXRoIHJlbGF0aXZlIGFidW5kYW5jZSBzb3J0YSwgYW5kIHdlIGFyZW50IHVzaW5nIHRoaXMgZGF0YSBmb3Igc3RhdGlzdGljYWwgdGVzdHMsIGp1c3QgdmlzdWFsaXphdGlvbi4NCmBgYHtyfQ0KcGh5c2VxX3JlbCA9IHRyYW5zZm9ybV9zYW1wbGVfY291bnRzKHBoeXNlcV9CYXIsIGZ1bmN0aW9uKHgpIHgvc3VtKHgpKjEwMCkNCmBgYA0KDQp3ZSBmaXJzdCBjb24oZ2xvbSllcmF0ZSB0aGUgdGF4YSB0byBhIHNwZWNpZmljIHRheHJhbmsgZGljdGF0ZWQgYnkgeW91ciBjaG9pY2UgYWJvdmUsIE5Bcm0gbWVhbnMgTkEgcmVtb3ZlLCBkb24ndCByZW1vdmUgeW91ciBOQXMhISEhIHRoaXMgbG9vc2VzIGRhdGEgZXNwZWNpYWxseSBpZiB5b3UgYXJlIHVzaW5nIGVudmlyb25tZW50YWwgZGF0YSB3ZXJlIG5vdCBldmVyeSBtaWNyb2JlIGluIHRoZSB3b3JsZCBoYXMgYmVlbiBjYXRlZ29yaXplZC4NClRoZSBvdGhlciB0d28gY29tbWFuZHMgYXJlIG1vZGlmeWluZyB0aGUgdGFibGVzIHNvIHRoZXkgY2FuIGJlIHJlYWQgYnkgZ2dwbG90IGVhc2lseQ0KYGBge3J9DQpnbG9tIDwtIHRheF9nbG9tKHBoeXNlcV9yZWwsIHRheHJhbmsgPSAnVGF4Q2hvaWNlJywgTkFybSA9IEZBTFNFKQ0KcGh5c2VxX21lbHQgPC0gcHNtZWx0KGdsb20pDQpwaHlzZXFfbWVsdCRUYXhDaG9pY2UgPC0gYXMuY2hhcmFjdGVyKHBoeXNlcV9tZWx0JFRheENob2ljZSkNCmBgYA0KDQpUaGlzIGNvZGUgdGFrZXMgdGhlIGdyb3VwcyB3ZSd2ZSBjaG9zZW4gYmVmb3JlIGFuZCBmaWd1cmVzIG91dCB0aGUgbWVkaWFuIGFidW5kYW5jZSBvZiBlYWNoIE9UVSBpbiBlYWNoIHNhbXBsZSANCmBgYHtyfQ0KcGh5c2VxX21lbHQgPC0gcGh5c2VxX21lbHQgJT4lDQogIGdyb3VwX2J5KFNhbUNob2ljZTEsIFNhbUNob2ljZTIsVGF4Q2hvaWNlKSAlPiUNCiAgbXV0YXRlKG1lZGlhbj1tZWRpYW4oQWJ1bmRhbmNlKSkNCmBgYA0KDQpXaGljaCBpcyBpbW1lZGlhdGVseSB1c2VmdWwsIGJlY2F1c2Ugd2UgcmVtb3ZlIE9UVXMgdGhhdCBhcmUgbGVzcyB0aGFuIDElIGluIGEgc2FtcGxlLiBUaGV5IGFyZSBzdGlsbCByZXByZXNlbnRlZCBpbiB0aGUgZ3JhcGgsIGJ1dCB0aGV5IGRvbid0IGdldCB0aGVpciBvd24gY2F0ZWdvcnkuIFlvdSBEb24ndCBIYXZlIHRvIGRvIHRoaXMsIG9yIHlvdSBjYW4gbWFrZSB0aGUgdmFsdWUgaGlnaGVyLCBpdHMganVzdCB0byBtYWtlIHRoZSBncmFwaCBhIGJpdCBtb3JlIHJlYWRhYmxlDQoNCmBgYHtyfQ0Ka2VlcCA8LSB1bmlxdWUocGh5c2VxX21lbHQkVGF4Q2hvaWNlW3BoeXNlcV9tZWx0JG1lZGlhbiA+IDFdKQ0KcGh5c2VxX21lbHQkVGF4Q2hvaWNlWyEocGh5c2VxX21lbHQkVGF4Q2hvaWNlICVpbiUga2VlcCldIDwtICI8IDElIg0KYGBgDQoNClNpbWlsYXIgY29tbWFuZCBhcyBhYm92ZSwgYnV0IHN1bW1hcml6ZXMgYWxsIHRoZSBpbmZvcm1hdGlvbg0KYGBge3J9DQpwaHlzZXFfbWVsdF9zdW0gPC0gcGh5c2VxX21lbHQgJT4lDQogIGdyb3VwX2J5KFNhbXBsZSxTYW1DaG9pY2UxLCBTYW1DaG9pY2UyLFRheENob2ljZSkgJT4lDQogIHN1bW1hcmlzZShBYnVuZGFuY2U9c3VtKEFidW5kYW5jZSkpDQpgYGANCg0KSGV5IEkgc2FpZCBmaWx0ZXJWYWwgd291bGQgY29tZSB1cCBhZ2FpbiENClVuY29tbWVudCB0aGlzIGNvZGUgaWYgeW91IHdhbnQgdG8gYXBwbHkgdGhlIGZpbHRlciENCmBgYHtyfQ0KI3BoeXNlcV9tZWx0X3N1bSA8LSBmaWx0ZXIocGh5c2VxX21lbHRfc3VtLCBUYXhDaG9pY2UgPT0gZmlsdGVyVmFsKQ0KYGBgDQoNCg0KDQpMZXRzIHBsb3QgaXQhDQoNCmBgYHtyfQ0KZ2dwbG90KHBoeXNlcV9tZWx0X3N1bSwgYWVzKHggPSBTYW1wbGUsIHkgPSBBYnVuZGFuY2UsIGZpbGwgPSBUYXhDaG9pY2UpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgYWVzKGZpbGw9VGF4Q2hvaWNlKSkgKyANCiAgbGFicyh4PSIiLCB5PSIlIikgKw0KICBmYWNldF93cmFwKFNhbUNob2ljZTF+U2FtQ2hvaWNlMiwgc2NhbGVzPSAiZnJlZV94IiwgbnJvdz0xKSArDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9IHRheFZhbCkrDQogIHRoZW1lX2NsYXNzaWMoKSArIA0KICAjc2NhbGVfeF9kaXNjcmV0ZShndWlkZT1ndWlkZV9heGlzKG4uZG9kZ2U9MykpKw0KICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgYXhpcy50ZXh0LnguYm90dG9tID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gLTkwKSkgICAgICAgIA0KZ2dzYXZlKGZpbGVwYXRoaW5nKQ0KDQpgYGANCg0KDQpZb3UnbGwgaGF2ZSB0byBkZWNpZGUgd2hhdCB5b3VyIGdnc2F2ZSBsb2NhdGlvbiBpcy4NCg0KTG9va2luZyBhdCB0aGUgZ3JhcGgsIG9uZSBvZiB0aGUgaW1wb3J0YW50LCBub24tbm9ybWFsIHRoaW5ncyBhZGRlZCBpcyBzY2FsZV9maWxsX2Rpc2NyZXRlLCB3ZSBuZWVkIHRoaXMgdG8gcmVuYW1lIHRoZSB0aGUgbGVnZW5kIGJlY2F1c2Ugb2YgdGhlIG9iamVjdCwgdGF4VmFsLCBzaGVuYW5pZ2FucyBlYXJsaWVyLiAgDQoNCg0KDQpMb29rcyBncmVhdCByaWdodD8gd2VsbCwgc29ydGEgSSBndWVzcywgc2VlIGhvdyB0aGUgc2FtcGxlIHR5cGUgdGV4dCBpcyBjcnVtcGxlZCBvbiB0aGUgc2NyZWVuLCB3ZWxsIHRyeSBleHBhbmRpbmcgeW91ciBwbG90cyB2aWV3IGluIFIgdG8gZXhwYW5kIHRoZSBzcGFjaW5nIGluIHRoZSBzYW1wbGUgKHlvdSBjYW4gYWxzbyBkbyB0aGlzIGluIGdnc2F2ZSBieSBzZXR0aW5nIHNpemVzKSwgb3IgcGxheWluZyBhcm91bmQgd2l0aCB0aGUgbi5kb2RnZSBmdW5jdGlvbiBJJ3ZlIGNvbW1lbnRlZCBvdXQgaW4gdGhlIGdnIHBsb3QgZm9ybXVsYSAoSSBkb250IGxpa2UgdGhlIGxvb2sgb2YgaXQgdGhvKS4gWW91IGNhbiBhbHNvIHBsb3Qgc29tZSBzYW1wbGVzIGJ5IHRoZW1zZWx2ZXMgdG8gbWFuYWdlIGltYWdlcy4gDQoNCg0KSGV5IGhleSBIRVksIHdoYXRzIE5BLCBhbmQgd2h5IGFyZSB0aGVyZSBwaHlsdW0ncyB1bmRlciA8MSU/DQpOQSBzdGFuZHMgZm9yIG5vdCBhdmFpbGFibGUsIGFuZCBhcmUgcmVmbGVjdGl2ZSBvZiBzZXF1ZW5jZXMgdGhhdCBleGlzdCBidXQgd2UgaGF2ZW50IGNhdGVnb3JpemVkIHRoZW0gaW50byBzcGVjaWVzIHlldC4gDQpidXQgdGhlcmUgYmVpbmcgcGh5bHVtJ3MgdW5kZXIgPDElPywgcmVydW4gdGhlIHNhbWUgY29kZSB3aXRob3V0IHRoZSA8MSUgbW9kaWZpY2F0aW9uLiANCg0KDQpgYGB7cn0NCmdncGxvdChwaHlzZXFfbWVsdF9zdW0sIGFlcyh4ID0gU2FtcGxlLCB5ID0gQWJ1bmRhbmNlLCBmaWxsID0gVGF4Q2hvaWNlKSkgKyANCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGFlcyhmaWxsPVRheENob2ljZSkpICsgDQogIGxhYnMoeD0iIiwgeT0iJSIpICsNCiAgZmFjZXRfd3JhcChTYW1DaG9pY2UxflNhbUNob2ljZTIsIHNjYWxlcz0gImZyZWVfeCIsIG5yb3c9MSkgKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSB0YXhWYWwpKw0KICB0aGVtZV9jbGFzc2ljKCkgKyANCiAgI3NjYWxlX3hfZGlzY3JldGUoZ3VpZGU9Z3VpZGVfYXhpcyhuLmRvZGdlPTMpKSsNCiAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgIGF4aXMudGV4dC54LmJvdHRvbSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IC05MCkpIA0KYGBgDQoNCg0Kd2hhdCB0aGUgZnJpY2ssIHdoeSBhcmUgdGhlcmUgYW50aHJvcG9kIHNhbXBsZXMgaW4gbXkgZnVuZ2FsIGRhdGEhDQoNClRoaXMgaXMgYSBnb29kIGV4YW1wbGUgb2Ygd2h5IHlvdSBtdXN0IGFsd2F5cyByZXZpZXcgeW91ciBkYXRhISBhbmQgcmVsYXRpdmUgYWJ1bmRhbmNlIHBsb3RzIGFyZSBhIGdvb2Qgd2F5IHRvIGRvIGl0ISBJJ20gZ29pbmcgdG8gZ28gYmFjayBhbmQgcmVtb3ZlIHRoZXNlIHBoeWx1bXMgc28gdGhleSBkb250IGFmZmVjdCBkb3duc3RyZWFtIGFuYWx5c2lzDQoNCg0KDQoNCkFzIHByb21pc2VkIGhlcmVzIGEgZ3JhcGggb2Ygb25seSB0aGUgQXNjb215Y290YSBzYW1wbGVzDQoNCmBgYHtyfQ0KZ2dwbG90KHBoeXNlcV9tZWx0X3N1bSwgYWVzKHggPSBTYW1wbGUsIHkgPSBBYnVuZGFuY2UsIGZpbGwgPSBUYXhDaG9pY2UpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgYWVzKGZpbGw9VGF4Q2hvaWNlKSkgKyANCiAgbGFicyh4PSIiLCB5PSIlIikgKw0KICBmYWNldF93cmFwKFNhbUNob2ljZTF+U2FtQ2hvaWNlMiwgc2NhbGVzPSAiZnJlZV94IiwgbnJvdz0xKSArDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9IHRheFZhbCkrDQogIHRoZW1lX2NsYXNzaWMoKSArIA0KICAjc2NhbGVfeF9kaXNjcmV0ZShndWlkZT1ndWlkZV9heGlzKG4uZG9kZ2U9MykpKw0KICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgYXhpcy50ZXh0LnguYm90dG9tID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gLTkwKSkgDQpgYGANCg==