By Angus Ball

Hi, by now you’ve processed your data through DADA2 and now its time for some data analysis!!! haha sike, we have to format it first, get rekt

Biomanager is a name thats gonna come up alot, but what is biomanager? well its actually called bioconductor (love the naming scheme), and its a database with all the big boys in bioinformatics software, so when we want to use specific packages we’ll have to install them directly from biomanager.

This is how to do that:



#phyloseq and biomanager
if (!require("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install()
BiocManager::install(c("phyloseq"))

#microbiomeMaker
if (!requireNamespace("BiocManager", quietly = TRUE))
  install.packages("BiocManager")
BiocManager::install("microbiomeMarker")
#dada2
if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
BiocManager::install("dada2", version = "3.17")

These are the packages you’ll need

library(phyloseq)
library(ggplot2)
library(janitor)
library(dplyr)
library(tidyverse)
library(ggpubr)
library(ape)
library(dada2)

Now its time to import our data from DATA2, unfortunately we need to work with a .csv file and our data come off in text files Fun fact: .csv files are just basic excel files

To turn your text files into a .csv file open excel and then try to open your .txt file. Excel will go “hey…” and try to convert your txt file into an excel file. This is perfect! Our file is delimited (the autopicked option), via tab (the autopicked option after you click next once), so all you have to do is meander through the pop up and excel will do all the heavy lifting for you! Simply save your file as a .csv file where ever you’d like!

Now go off into to world and create a csv file of you 16S_R[name]_dada2_nochim_tax.txt file! Okay sweet, on the left should be a row of SV_0..Sv_10 ect, in the middle is you sample name with all the count data per species and on the right is tax.vector.

Fun fact: backups! hey i’ve noticed that you didn’t immediately create a backup and a working csv file, Shame on you! Its important to always create backup files you NEVER modify in excel when you get a new file you plan on modifying. It’d be a shame is you were to delete something you shouldn’t have and then restart from the beginning.

You’re 16S_R[name]_dada2_nochim_tax.csv file is now ready to be imported into R

Lets import this table

MetaG_1 <- read_csv("C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\ULTRA\\They call me... data\\Angus-16S\\16S-dada2_nochim_tax.csv")
MetaG_2 <- read_csv("C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\ULTRA\\They call me... data\\Angus-16S\\Redone mastermix stuff\\16S_RefInoculum_dada2_nochim_tax.csv")

Congrats! We now have the data in R, you may have noticed I’ve actually read 2 separate sets of data, 1 and 2. You might not have two sets of data but I do!. We will then create phyloseq objects with both these datasets

First things first, we have to format the tables in a way that phyloseq is expecting them and unfortunately, this is a bit of a pain. Phyloseq expects a couple tables. First a count table (this is the MetaG table), A taxonomy table (This is tax.vector), and a sample data table (This ones based off of the group of data that exist in your sample)

Lets first start by creating the taxonomy table from the MetaG tables!

Tax_1<-select(MetaG_1, ("tax.vector")) 
Tax_2<-select(MetaG_2, ("tax.vector"))

Oh bother! Phyloseq expects the taxonomy to be separated by taxons i.e. one column for kingdom, one for family ect. But everything is under one column. This means we have to deliminate again, and looking at the how to column is created from DADA2, we need to deliminate by colon

cSplit deliminates within a column and then the rename functions make all the columns names phyloseq expects!

Tax_1 <- cSplit(Tax_1, "tax.vector", ":")
Warning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUE
Tax_1 <- rename(Tax_1,
 Kingdom = tax.vector_1,
 Phylum = tax.vector_2,
 Class = tax.vector_3,
 Order = tax.vector_4,
 Family = tax.vector_5,
 Genus = tax.vector_6,
 Species = tax.vector_7,
  
  )
Tax_2 <- cSplit(Tax_2, "tax.vector", ":")
Warning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUEWarning: 'as.is' should be specified by the caller; using TRUE
Tax_2 <- rename(Tax_2,
 Kingdom = tax.vector_1,
 Phylum = tax.vector_2,
 Class = tax.vector_3,
 Order = tax.vector_4,
 Family = tax.vector_5,
 Genus = tax.vector_6,
 Species = tax.vector_7,
  
  )
#remove the sV value
MetaG_1 <- select(MetaG_1, -(1))
MetaG_1 <- select(MetaG_1, -("tax.vector"))
MetaG_2 <- select(MetaG_2, -(1))
MetaG_2 <- select(MetaG_2, -("tax.vector"))

What we did was remove the first column (column “1”) from the data set, i’ve also included the code to remove the tax.vector column here

Now this is real data, and for paper reasons I want to remove some samples from MetaG_1. We can use a similar compand to do this

#ULTRA
MetaG_1 <- select(MetaG_1, -("CHB1P1":"FNP9B3"))

We have to take our tables (that exist as a dataframe) and convert them into a maxtrix, why? because the phyloseq class uses matrixes and not dataframes (in the example above we couldn’t feed our class two int values, it wants and int and a char!)

#now real stuff
#convert these bad boys to matrixes
Tax_1 <- as.matrix(Tax_1)
MetaG_1 <- as.matrix(MetaG_1)

Tax_2 <- as.matrix(Tax_2)
MetaG_2 <- as.matrix(MetaG_2)

You can check the class of the objects with a simple command (oh yes the tables have been objects all along, we’re creating objects within classes within classes! neat isnt it?)

#check
class(Tax_1)
[1] "matrix" "array" 
class(MetaG_1)
[1] "matrix" "array" 
class(Tax_2)
[1] "matrix" "array" 
class(MetaG_2)
[1] "matrix" "array" 

Phyloseq expects the far left column to be called OTU, this is how to do it, I’m naming the OTUs 1 and 2 because of how phyloseq merges row names later on

#call row values OTU
rownames(MetaG_1) <- paste0("OTU", 1:nrow(MetaG_1))
rownames(Tax_1) <- paste0("OTU", 1:nrow(Tax_1))

rownames(MetaG_2) <- paste0("2_OTU", 1:nrow(MetaG_2))
rownames(Tax_2) <- paste0("2_OTU", 1:nrow(Tax_2))

Very scary looking message, but it only reads that we deleted and replaced the previous row names

Okay hopefully you know a bit about objects/classes, if not Funless fact: please go read the first couple chapters of a coding in java textbook Funner fact: data structure is neat, say you want to store a number, “1”, you can do so in java by telling it a datatype (int for integer), and then a value, for example int x=1; this tells the computer that x is equal to one. Great but int only works for numbers (and not even all of them ;), so what is you wanted to store a letter? well you use char (for character), so char y=“b”; this is saying that y is equal to the letter “b”. Obviously this quick example looses alot of the subtly and complexity of the system (re: go read the first couple chapters of a java textbook), but alas.

So now lets say you wanted a thing, lets call it a class, that contained a number and a character, you could create a class that used x and it used y to create an object. This is a poor explanation that has likely made several computer scientists sad but whatever, phyloseq is a class that creates phyloseq objects, each object having at a minimum an abundance table (metaG_) and a taxonomy table (Tax_). Excellent you are now equally confused as earlier but I feel like I’ve done my due dillegence, onward.

Now phyloseq wants us to turn these matrices into a different class created by phyloseq (not isnt a phyloseq object yet)


OTU_1 = otu_table(MetaG_1, taxa_are_rows = TRUE)
TAX_1 = tax_table(Tax_1)

OTU_2 = otu_table(MetaG_2, taxa_are_rows = TRUE)
TAX_2 = tax_table(Tax_2)

Now we’ll create a phyloseq object with each OTU table and TAXonomy table

#combine that data
physeq_1 = phyloseq(OTU_1, TAX_1)
physeq_2 = phyloseq(OTU_2, TAX_2)

Now we’ll merge the two phyloseq objects, because we named the OTU row values differently there will not be overlap between the two samples

physeq<-merge_phyloseq(physeq_1,physeq_2)

Now we have to conglomerate our data so that OTUs specifying the same thing are combined. This code destroys the species level comparison and says if it exists within this family add it to total family value If you are working on a species level comparison do not do this as it will not let you work on species level comparisons; however, for this data set we are looking on an order and family level so it does not lose sensitivity. There are better ways to merge data but those are done within the dada2 pipeline before taxonomy assignment, I did not do dada2 analysis on these samples and therefore i have to do this (poorer) way of merging samples. Maybe I’ll find a better way to do it later, If you do tell me.

physeq <- tax_glom(physeq, taxrank = rank_names(physeq)[5], NArm = FALSE)

Fun fact: 5 corresponds to family just like 6 is genus and 1 is kingdom if you inspect the physeq object you can see that the tax table ends at family, but didnt delete values with NA (NArm = FALSE, read NA remove = false)

physeq@tax_table

Excellent, almost done!

Now if you inspect the sample names they have a variety of meanings

physeq@otu_table

Things like “municipal compost”, “fertalized” ect, this is metadata that are in the sample names, but the phyloseq object doesn’t know about yet. Fun fact: if you want to know what this actually is or what the names mean ask to read my manuscript but we must import this metadata.

Key <- read_csv("C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\ULTRA\\They call me... data\\Angus-16S\\ultra to categories key.csv")
Rows: 52 Columns: 5── Column specification ───────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): Name, Amd, Fertalized, Inoculum
dbl (1): Conc
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

if you explore this document, you’ll realize it has all the samples in order they appear on column names within the MetaG table, but they are in the rows instead. The meta data in other columns

for the same reasons we converted to OTU earlier (phyloseq being fickle), we have to make the sample names be the left most row name

Key <- data.frame(Key[,-1], row.names=Key$Name)
sampledata = sample_data(Key)

Now we will add the key data

physeq_Key = merge_phyloseq(physeq, sampledata)

Congrats you are done (lie)!!! You now have a phyloseq object and can move on to transformations and data analysis

LS0tDQp0aXRsZTogIkltcG9ydGluZyBkYXRhIGZyb20gREFEQTIgdmlhIFBoeWxvc2VxIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCkJ5IEFuZ3VzIEJhbGwNCg0KSGksIGJ5IG5vdyB5b3UndmUgcHJvY2Vzc2VkIHlvdXIgZGF0YSB0aHJvdWdoIERBREEyIGFuZCBub3cgaXRzIHRpbWUgZm9yIHNvbWUgZGF0YSBhbmFseXNpcyEhISBoYWhhIHNpa2UsIHdlIGhhdmUgdG8gZm9ybWF0IGl0IGZpcnN0LCBnZXQgcmVrdA0KDQpCaW9tYW5hZ2VyIGlzIGEgbmFtZSB0aGF0cyBnb25uYSBjb21lIHVwIGFsb3QsIGJ1dCB3aGF0IGlzIGJpb21hbmFnZXI/IHdlbGwgaXRzIGFjdHVhbGx5IGNhbGxlZCBiaW9jb25kdWN0b3IgKGxvdmUgdGhlIG5hbWluZyBzY2hlbWUpLCBhbmQgaXRzIGEgZGF0YWJhc2Ugd2l0aCBhbGwgdGhlIGJpZyBib3lzIGluIGJpb2luZm9ybWF0aWNzIHNvZnR3YXJlLCBzbyB3aGVuIHdlIHdhbnQgdG8gdXNlIHNwZWNpZmljIHBhY2thZ2VzIHdlJ2xsIGhhdmUgdG8gaW5zdGFsbCB0aGVtIGRpcmVjdGx5IGZyb20gYmlvbWFuYWdlci4gDQoNClRoaXMgaXMgaG93IHRvIGRvIHRoYXQ6DQpgYGB7ciwgZWNobyA9IFQsIG1lc3NhZ2U9RkFMU0V9DQoNCg0KI3BoeWxvc2VxIGFuZCBiaW9tYW5hZ2VyDQppZiAoIXJlcXVpcmUoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQ0KaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQ0KQmlvY01hbmFnZXI6Omluc3RhbGwoKQ0KQmlvY01hbmFnZXI6Omluc3RhbGwoYygicGh5bG9zZXEiKSkNCg0KI21pY3JvYmlvbWVNYWtlcg0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkNCiAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQ0KQmlvY01hbmFnZXI6Omluc3RhbGwoIm1pY3JvYmlvbWVNYXJrZXIiKQ0KI2RhZGEyDQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQ0KICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikNCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJkYWRhMiIsIHZlcnNpb24gPSAiMy4xNyIpDQoNCmBgYA0KDQoNCg0KDQpUaGVzZSBhcmUgdGhlIHBhY2thZ2VzIHlvdSdsbCBuZWVkDQpgYGB7ciwgZWNobyA9IFQsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KHBoeWxvc2VxKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShqYW5pdG9yKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KGFwZSkNCmxpYnJhcnkoZGFkYTIpDQoNCmBgYA0KDQpOb3cgaXRzIHRpbWUgdG8gaW1wb3J0IG91ciBkYXRhIGZyb20gREFUQTIsIHVuZm9ydHVuYXRlbHkgd2UgbmVlZCB0byB3b3JrIHdpdGggYSAuY3N2IGZpbGUgYW5kIG91ciBkYXRhIGNvbWUgb2ZmIGluIHRleHQgZmlsZXMNCkZ1biBmYWN0OiAuY3N2IGZpbGVzIGFyZSBqdXN0IGJhc2ljIGV4Y2VsIGZpbGVzDQoNClRvIHR1cm4geW91ciB0ZXh0IGZpbGVzIGludG8gYSAuY3N2IGZpbGUgb3BlbiBleGNlbCBhbmQgdGhlbiB0cnkgdG8gb3BlbiB5b3VyIC50eHQgZmlsZS4gRXhjZWwgd2lsbCBnbyAiaGV5Li4uIiBhbmQgdHJ5IHRvIGNvbnZlcnQgeW91ciB0eHQgZmlsZSBpbnRvIGFuIGV4Y2VsIGZpbGUuIFRoaXMgaXMgcGVyZmVjdCEgT3VyIGZpbGUgaXMgZGVsaW1pdGVkICh0aGUgYXV0b3BpY2tlZCBvcHRpb24pLCB2aWEgdGFiICh0aGUgYXV0b3BpY2tlZCBvcHRpb24gYWZ0ZXIgeW91IGNsaWNrIG5leHQgb25jZSksIHNvIGFsbCB5b3UgaGF2ZSB0byBkbyBpcyBtZWFuZGVyIHRocm91Z2ggdGhlIHBvcCB1cCBhbmQgZXhjZWwgd2lsbCBkbyBhbGwgdGhlIGhlYXZ5IGxpZnRpbmcgZm9yIHlvdSEgU2ltcGx5IHNhdmUgeW91ciBmaWxlIGFzIGEgLmNzdiBmaWxlIHdoZXJlIGV2ZXIgeW91J2QgbGlrZSENCg0KTm93IGdvIG9mZiBpbnRvIHRvIHdvcmxkIGFuZCBjcmVhdGUgYSBjc3YgZmlsZSBvZiB5b3UgMTZTX1JbbmFtZV1fZGFkYTJfbm9jaGltX3RheC50eHQgZmlsZSENCk9rYXkgc3dlZXQsIG9uIHRoZSBsZWZ0IHNob3VsZCBiZSBhIHJvdyBvZiBTVl8wLi5Tdl8xMCBlY3QsIGluIHRoZSBtaWRkbGUgaXMgeW91IHNhbXBsZSBuYW1lIHdpdGggYWxsIHRoZSBjb3VudCBkYXRhIHBlciBzcGVjaWVzIGFuZCBvbiB0aGUgcmlnaHQgaXMgdGF4LnZlY3Rvci4NCg0KDQoNCkZ1biBmYWN0OiBiYWNrdXBzISBoZXkgaSd2ZSBub3RpY2VkIHRoYXQgeW91IGRpZG4ndCBpbW1lZGlhdGVseSBjcmVhdGUgYSBiYWNrdXAgYW5kIGEgd29ya2luZyBjc3YgZmlsZSwgU2hhbWUgb24geW91ISBJdHMgaW1wb3J0YW50IHRvIGFsd2F5cyBjcmVhdGUgYmFja3VwIGZpbGVzIHlvdSBORVZFUiBtb2RpZnkgaW4gZXhjZWwgd2hlbiB5b3UgZ2V0IGEgbmV3IGZpbGUgeW91IHBsYW4gb24gbW9kaWZ5aW5nLiBJdCdkIGJlIGEgc2hhbWUgaXMgeW91IHdlcmUgdG8gZGVsZXRlIHNvbWV0aGluZyB5b3Ugc2hvdWxkbid0IGhhdmUgYW5kIHRoZW4gcmVzdGFydCBmcm9tIHRoZSBiZWdpbm5pbmcuIA0KDQpZb3UncmUgMTZTX1JbbmFtZV1fZGFkYTJfbm9jaGltX3RheC5jc3YgZmlsZSBpcyBub3cgcmVhZHkgdG8gYmUgaW1wb3J0ZWQgaW50byBSDQoNCkxldHMgaW1wb3J0IHRoaXMgdGFibGUNCg0KYGBge3IsIGVjaG8gPSBULCBtZXNzYWdlPUZBTFNFfQ0KTWV0YUdfMSA8LSByZWFkX2NzdigiQzpcXFVzZXJzXFxhbmd1c1xcT25lRHJpdmUgLSBVTkJDXFxBbmd1cyBCYWxsXFxMYWIgd29ya1xcVUxUUkFcXFRoZXkgY2FsbCBtZS4uLiBkYXRhXFxBbmd1cy0xNlNcXDE2Uy1kYWRhMl9ub2NoaW1fdGF4LmNzdiIpDQpNZXRhR18yIDwtIHJlYWRfY3N2KCJDOlxcVXNlcnNcXGFuZ3VzXFxPbmVEcml2ZSAtIFVOQkNcXEFuZ3VzIEJhbGxcXExhYiB3b3JrXFxVTFRSQVxcVGhleSBjYWxsIG1lLi4uIGRhdGFcXEFuZ3VzLTE2U1xcUmVkb25lIG1hc3Rlcm1peCBzdHVmZlxcMTZTX1JlZklub2N1bHVtX2RhZGEyX25vY2hpbV90YXguY3N2IikNCg0KDQpgYGANCg0KQ29uZ3JhdHMhIFdlIG5vdyBoYXZlIHRoZSBkYXRhIGluIFIsIHlvdSBtYXkgaGF2ZSBub3RpY2VkIEkndmUgYWN0dWFsbHkgcmVhZCAyIHNlcGFyYXRlIHNldHMgb2YgZGF0YSwgMSBhbmQgMi4gWW91IG1pZ2h0IG5vdCBoYXZlIHR3byBzZXRzIG9mIGRhdGEgYnV0IEkgZG8hLiBXZSB3aWxsIHRoZW4gY3JlYXRlIHBoeWxvc2VxIG9iamVjdHMgd2l0aCBib3RoIHRoZXNlIGRhdGFzZXRzDQoNCg0KRmlyc3QgdGhpbmdzIGZpcnN0LCB3ZSBoYXZlIHRvIGZvcm1hdCB0aGUgdGFibGVzIGluIGEgd2F5IHRoYXQgcGh5bG9zZXEgaXMgZXhwZWN0aW5nIHRoZW0gYW5kIHVuZm9ydHVuYXRlbHksIHRoaXMgaXMgYSBiaXQgb2YgYSBwYWluLiBQaHlsb3NlcSBleHBlY3RzIGEgY291cGxlIHRhYmxlcy4gRmlyc3QgYSBjb3VudCB0YWJsZSAodGhpcyBpcyB0aGUgTWV0YUcgdGFibGUpLCBBIHRheG9ub215IHRhYmxlIChUaGlzIGlzIHRheC52ZWN0b3IpLCBhbmQgYSBzYW1wbGUgZGF0YSB0YWJsZSAoVGhpcyBvbmVzIGJhc2VkIG9mZiBvZiB0aGUgZ3JvdXAgb2YgZGF0YSB0aGF0IGV4aXN0IGluIHlvdXIgc2FtcGxlKQ0KDQpMZXRzIGZpcnN0IHN0YXJ0IGJ5IGNyZWF0aW5nIHRoZSB0YXhvbm9teSB0YWJsZSBmcm9tIHRoZSBNZXRhRyB0YWJsZXMhDQoNCmBgYHtyfQ0KVGF4XzE8LXNlbGVjdChNZXRhR18xLCAoInRheC52ZWN0b3IiKSkgDQpUYXhfMjwtc2VsZWN0KE1ldGFHXzIsICgidGF4LnZlY3RvciIpKQ0KYGBgDQpPaCBib3RoZXIhIFBoeWxvc2VxIGV4cGVjdHMgdGhlIHRheG9ub215IHRvIGJlIHNlcGFyYXRlZCBieSB0YXhvbnMgaS5lLiBvbmUgY29sdW1uIGZvciBraW5nZG9tLCBvbmUgZm9yIGZhbWlseSBlY3QuIEJ1dCBldmVyeXRoaW5nIGlzIHVuZGVyIG9uZSBjb2x1bW4uIFRoaXMgbWVhbnMgd2UgaGF2ZSB0byBkZWxpbWluYXRlIGFnYWluLCBhbmQgbG9va2luZyBhdCB0aGUgaG93IHRvIGNvbHVtbiBpcyBjcmVhdGVkIGZyb20gREFEQTIsIHdlIG5lZWQgdG8gZGVsaW1pbmF0ZSBieSBjb2xvbg0KDQpjU3BsaXQgZGVsaW1pbmF0ZXMgd2l0aGluIGEgY29sdW1uIGFuZCB0aGVuIHRoZSByZW5hbWUgZnVuY3Rpb25zIG1ha2UgYWxsIHRoZSBjb2x1bW5zIG5hbWVzIHBoeWxvc2VxIGV4cGVjdHMhDQoNCmBgYHtyfQ0KVGF4XzEgPC0gY1NwbGl0KFRheF8xLCAidGF4LnZlY3RvciIsICI6IikNClRheF8xIDwtIHJlbmFtZShUYXhfMSwNCiBLaW5nZG9tID0gdGF4LnZlY3Rvcl8xLA0KIFBoeWx1bSA9IHRheC52ZWN0b3JfMiwNCiBDbGFzcyA9IHRheC52ZWN0b3JfMywNCiBPcmRlciA9IHRheC52ZWN0b3JfNCwNCiBGYW1pbHkgPSB0YXgudmVjdG9yXzUsDQogR2VudXMgPSB0YXgudmVjdG9yXzYsDQogU3BlY2llcyA9IHRheC52ZWN0b3JfNywNCiAgDQogICkNClRheF8yIDwtIGNTcGxpdChUYXhfMiwgInRheC52ZWN0b3IiLCAiOiIpDQpUYXhfMiA8LSByZW5hbWUoVGF4XzIsDQogS2luZ2RvbSA9IHRheC52ZWN0b3JfMSwNCiBQaHlsdW0gPSB0YXgudmVjdG9yXzIsDQogQ2xhc3MgPSB0YXgudmVjdG9yXzMsDQogT3JkZXIgPSB0YXgudmVjdG9yXzQsDQogRmFtaWx5ID0gdGF4LnZlY3Rvcl81LA0KIEdlbnVzID0gdGF4LnZlY3Rvcl82LA0KIFNwZWNpZXMgPSB0YXgudmVjdG9yXzcsDQogIA0KICApDQoNCg0KYGBgDQoNCg0KDQpgYGB7cn0NCiNyZW1vdmUgdGhlIHNWIHZhbHVlDQpNZXRhR18xIDwtIHNlbGVjdChNZXRhR18xLCAtKDEpKQ0KTWV0YUdfMSA8LSBzZWxlY3QoTWV0YUdfMSwgLSgidGF4LnZlY3RvciIpKQ0KTWV0YUdfMiA8LSBzZWxlY3QoTWV0YUdfMiwgLSgxKSkNCk1ldGFHXzIgPC0gc2VsZWN0KE1ldGFHXzIsIC0oInRheC52ZWN0b3IiKSkNCg0KYGBgDQpXaGF0IHdlIGRpZCB3YXMgcmVtb3ZlIHRoZSBmaXJzdCBjb2x1bW4gKGNvbHVtbiAiMSIpIGZyb20gdGhlIGRhdGEgc2V0LCBpJ3ZlIGFsc28gaW5jbHVkZWQgdGhlIGNvZGUgdG8gcmVtb3ZlIHRoZSB0YXgudmVjdG9yIGNvbHVtbiBoZXJlIA0KDQpOb3cgdGhpcyBpcyByZWFsIGRhdGEsIGFuZCBmb3IgcGFwZXIgcmVhc29ucyBJIHdhbnQgdG8gcmVtb3ZlIHNvbWUgc2FtcGxlcyBmcm9tIE1ldGFHXzEuIFdlIGNhbiB1c2UgYSBzaW1pbGFyIGNvbXBhbmQgdG8gZG8gdGhpcw0KYGBge3J9DQojVUxUUkENCk1ldGFHXzEgPC0gc2VsZWN0KE1ldGFHXzEsIC0oIkNIQjFQMSI6IkZOUDlCMyIpKQ0KDQpgYGANCg0KV2UgaGF2ZSB0byB0YWtlIG91ciB0YWJsZXMgKHRoYXQgZXhpc3QgYXMgYSBkYXRhZnJhbWUpIGFuZCBjb252ZXJ0IHRoZW0gaW50byBhIG1heHRyaXgsIHdoeT8gYmVjYXVzZSB0aGUgcGh5bG9zZXEgY2xhc3MgdXNlcyBtYXRyaXhlcyBhbmQgbm90IGRhdGFmcmFtZXMgKGluIHRoZSBleGFtcGxlIGFib3ZlIHdlIGNvdWxkbid0IGZlZWQgb3VyIGNsYXNzIHR3byBpbnQgdmFsdWVzLCBpdCB3YW50cyBhbmQgaW50IGFuZCBhIGNoYXIhKQ0KDQpgYGB7cn0NCiNub3cgcmVhbCBzdHVmZg0KI2NvbnZlcnQgdGhlc2UgYmFkIGJveXMgdG8gbWF0cml4ZXMNClRheF8xIDwtIGFzLm1hdHJpeChUYXhfMSkNCk1ldGFHXzEgPC0gYXMubWF0cml4KE1ldGFHXzEpDQoNClRheF8yIDwtIGFzLm1hdHJpeChUYXhfMikNCk1ldGFHXzIgPC0gYXMubWF0cml4KE1ldGFHXzIpDQoNCmBgYA0KDQpZb3UgY2FuIGNoZWNrIHRoZSBjbGFzcyBvZiB0aGUgb2JqZWN0cyB3aXRoIGEgc2ltcGxlIGNvbW1hbmQgKG9oIHllcyB0aGUgdGFibGVzIGhhdmUgYmVlbiBvYmplY3RzIGFsbCBhbG9uZywgd2UncmUgY3JlYXRpbmcgb2JqZWN0cyB3aXRoaW4gY2xhc3NlcyB3aXRoaW4gY2xhc3NlcyEgbmVhdCBpc250IGl0PykNCg0KYGBge3J9DQojY2hlY2sNCmNsYXNzKFRheF8xKQ0KY2xhc3MoTWV0YUdfMSkNCmNsYXNzKFRheF8yKQ0KY2xhc3MoTWV0YUdfMikNCmBgYA0KDQoNClBoeWxvc2VxIGV4cGVjdHMgdGhlIGZhciBsZWZ0IGNvbHVtbiB0byBiZSBjYWxsZWQgT1RVLCB0aGlzIGlzIGhvdyB0byBkbyBpdCwgSSdtIG5hbWluZyB0aGUgT1RVcyAxIGFuZCAyIGJlY2F1c2Ugb2YgaG93IHBoeWxvc2VxIG1lcmdlcyByb3cgbmFtZXMgbGF0ZXIgb24NCmBgYHtyfQ0KI2NhbGwgcm93IHZhbHVlcyBPVFUNCnJvd25hbWVzKE1ldGFHXzEpIDwtIHBhc3RlMCgiT1RVIiwgMTpucm93KE1ldGFHXzEpKQ0Kcm93bmFtZXMoVGF4XzEpIDwtIHBhc3RlMCgiT1RVIiwgMTpucm93KFRheF8xKSkNCg0Kcm93bmFtZXMoTWV0YUdfMikgPC0gcGFzdGUwKCIyX09UVSIsIDE6bnJvdyhNZXRhR18yKSkNCnJvd25hbWVzKFRheF8yKSA8LSBwYXN0ZTAoIjJfT1RVIiwgMTpucm93KFRheF8yKSkNCg0KYGBgDQoNClZlcnkgc2NhcnkgbG9va2luZyBtZXNzYWdlLCBidXQgaXQgb25seSByZWFkcyB0aGF0IHdlIGRlbGV0ZWQgYW5kIHJlcGxhY2VkIHRoZSBwcmV2aW91cyByb3cgbmFtZXMNCg0KT2theSBob3BlZnVsbHkgeW91IGtub3cgYSBiaXQgYWJvdXQgb2JqZWN0cy9jbGFzc2VzLCBpZiBub3QNCkZ1bmxlc3MgZmFjdDogcGxlYXNlIGdvIHJlYWQgdGhlIGZpcnN0IGNvdXBsZSBjaGFwdGVycyBvZiBhIGNvZGluZyBpbiBqYXZhIHRleHRib29rDQpGdW5uZXIgZmFjdDogZGF0YSBzdHJ1Y3R1cmUgaXMgbmVhdCwgc2F5IHlvdSB3YW50IHRvIHN0b3JlIGEgbnVtYmVyLCAiMSIsIHlvdSBjYW4gZG8gc28gaW4gamF2YSBieSB0ZWxsaW5nIGl0IGEgZGF0YXR5cGUgKGludCBmb3IgaW50ZWdlciksIGFuZCB0aGVuIGEgdmFsdWUsIGZvciBleGFtcGxlIGludCB4PTE7IHRoaXMgdGVsbHMgdGhlIGNvbXB1dGVyIHRoYXQgeCBpcyBlcXVhbCB0byBvbmUuIEdyZWF0IGJ1dCBpbnQgb25seSB3b3JrcyBmb3IgbnVtYmVycyAoYW5kIG5vdCBldmVuIGFsbCBvZiB0aGVtIDspLCBzbyB3aGF0IGlzIHlvdSB3YW50ZWQgdG8gc3RvcmUgYSBsZXR0ZXI/IHdlbGwgeW91IHVzZSBjaGFyIChmb3IgY2hhcmFjdGVyKSwgc28gY2hhciB5PSJiIjsgdGhpcyBpcyBzYXlpbmcgdGhhdCB5IGlzIGVxdWFsIHRvIHRoZSBsZXR0ZXIgImIiLiBPYnZpb3VzbHkgdGhpcyBxdWljayBleGFtcGxlIGxvb3NlcyBhbG90IG9mIHRoZSBzdWJ0bHkgYW5kIGNvbXBsZXhpdHkgb2YgdGhlIHN5c3RlbSAocmU6IGdvIHJlYWQgdGhlIGZpcnN0IGNvdXBsZSBjaGFwdGVycyBvZiBhIGphdmEgdGV4dGJvb2spLCBidXQgYWxhcy4NCg0KU28gbm93IGxldHMgc2F5IHlvdSB3YW50ZWQgYSB0aGluZywgbGV0cyBjYWxsIGl0IGEgY2xhc3MsIHRoYXQgY29udGFpbmVkIGEgbnVtYmVyIGFuZCBhIGNoYXJhY3RlciwgeW91IGNvdWxkIGNyZWF0ZSBhIGNsYXNzIHRoYXQgdXNlZCB4IGFuZCBpdCB1c2VkIHkgdG8gY3JlYXRlIGFuIG9iamVjdC4gVGhpcyBpcyBhIHBvb3IgZXhwbGFuYXRpb24gdGhhdCBoYXMgbGlrZWx5IG1hZGUgc2V2ZXJhbCBjb21wdXRlciBzY2llbnRpc3RzIHNhZCBidXQgd2hhdGV2ZXIsIHBoeWxvc2VxIGlzIGEgY2xhc3MgdGhhdCBjcmVhdGVzIHBoeWxvc2VxIG9iamVjdHMsIGVhY2ggb2JqZWN0IGhhdmluZyBhdCBhIG1pbmltdW0gYW4gYWJ1bmRhbmNlIHRhYmxlIChtZXRhR18pIGFuZCBhIHRheG9ub215IHRhYmxlIChUYXhfKS4gRXhjZWxsZW50IHlvdSBhcmUgbm93IGVxdWFsbHkgY29uZnVzZWQgYXMgZWFybGllciBidXQgSSBmZWVsIGxpa2UgSSd2ZSBkb25lIG15IGR1ZSBkaWxsZWdlbmNlLCBvbndhcmQuDQoNCg0KTm93IHBoeWxvc2VxIHdhbnRzIHVzIHRvIHR1cm4gdGhlc2UgbWF0cmljZXMgaW50byBhIGRpZmZlcmVudCBjbGFzcyBjcmVhdGVkIGJ5IHBoeWxvc2VxIChub3QgaXNudCBhIHBoeWxvc2VxIG9iamVjdCB5ZXQpDQoNCmBgYHtyfQ0KDQpPVFVfMSA9IG90dV90YWJsZShNZXRhR18xLCB0YXhhX2FyZV9yb3dzID0gVFJVRSkNClRBWF8xID0gdGF4X3RhYmxlKFRheF8xKQ0KDQpPVFVfMiA9IG90dV90YWJsZShNZXRhR18yLCB0YXhhX2FyZV9yb3dzID0gVFJVRSkNClRBWF8yID0gdGF4X3RhYmxlKFRheF8yKQ0KYGBgDQoNCk5vdyB3ZSdsbCBjcmVhdGUgYSBwaHlsb3NlcSBvYmplY3Qgd2l0aCBlYWNoIE9UVSB0YWJsZSBhbmQgVEFYb25vbXkgdGFibGUNCg0KYGBge3J9DQojY29tYmluZSB0aGF0IGRhdGENCnBoeXNlcV8xID0gcGh5bG9zZXEoT1RVXzEsIFRBWF8xKQ0KcGh5c2VxXzIgPSBwaHlsb3NlcShPVFVfMiwgVEFYXzIpDQpgYGANCg0KTm93IHdlJ2xsIG1lcmdlIHRoZSB0d28gcGh5bG9zZXEgb2JqZWN0cywgYmVjYXVzZSB3ZSBuYW1lZCB0aGUgT1RVIHJvdyB2YWx1ZXMgZGlmZmVyZW50bHkgdGhlcmUgd2lsbCBub3QgYmUgb3ZlcmxhcCBiZXR3ZWVuIHRoZSB0d28gc2FtcGxlcw0KDQpgYGB7cn0NCnBoeXNlcTwtbWVyZ2VfcGh5bG9zZXEocGh5c2VxXzEscGh5c2VxXzIpDQpgYGANCg0KTm93IHdlIGhhdmUgdG8gY29uZ2xvbWVyYXRlIG91ciBkYXRhIHNvIHRoYXQgT1RVcyBzcGVjaWZ5aW5nIHRoZSBzYW1lIHRoaW5nIGFyZSBjb21iaW5lZC4gVGhpcyBjb2RlIGRlc3Ryb3lzIHRoZSBzcGVjaWVzIGxldmVsIGNvbXBhcmlzb24gYW5kIHNheXMgaWYgaXQgZXhpc3RzIHdpdGhpbiB0aGlzIGZhbWlseSBhZGQgaXQgdG8gdG90YWwgZmFtaWx5IHZhbHVlDQpJZiB5b3UgYXJlIHdvcmtpbmcgb24gYSBzcGVjaWVzIGxldmVsIGNvbXBhcmlzb24gZG8gbm90IGRvIHRoaXMgYXMgaXQgd2lsbCBub3QgbGV0IHlvdSB3b3JrIG9uIHNwZWNpZXMgbGV2ZWwgY29tcGFyaXNvbnM7IGhvd2V2ZXIsIGZvciB0aGlzIGRhdGEgc2V0IHdlIGFyZSBsb29raW5nIG9uIGFuIG9yZGVyIGFuZCBmYW1pbHkgbGV2ZWwgc28gaXQgZG9lcyBub3QgbG9zZSBzZW5zaXRpdml0eS4gVGhlcmUgYXJlIGJldHRlciB3YXlzIHRvIG1lcmdlIGRhdGEgYnV0IHRob3NlIGFyZSBkb25lIHdpdGhpbiB0aGUgZGFkYTIgcGlwZWxpbmUgYmVmb3JlIHRheG9ub215IGFzc2lnbm1lbnQsIEkgZGlkIG5vdCBkbyBkYWRhMiBhbmFseXNpcyBvbiB0aGVzZSBzYW1wbGVzIGFuZCB0aGVyZWZvcmUgaSBoYXZlIHRvIGRvIHRoaXMgKHBvb3Jlcikgd2F5IG9mIG1lcmdpbmcgc2FtcGxlcy4gTWF5YmUgSSdsbCBmaW5kIGEgYmV0dGVyIHdheSB0byBkbyBpdCBsYXRlciwgSWYgeW91IGRvIHRlbGwgbWUuDQoNCmBgYHtyfQ0KcGh5c2VxIDwtIHRheF9nbG9tKHBoeXNlcSwgdGF4cmFuayA9IHJhbmtfbmFtZXMocGh5c2VxKVs1XSwgTkFybSA9IEZBTFNFKQ0KYGBgDQpGdW4gZmFjdDogNSBjb3JyZXNwb25kcyB0byBmYW1pbHkganVzdCBsaWtlIDYgaXMgZ2VudXMgYW5kIDEgaXMga2luZ2RvbQ0KaWYgeW91IGluc3BlY3QgdGhlIHBoeXNlcSBvYmplY3QgeW91IGNhbiBzZWUgdGhhdCB0aGUgdGF4IHRhYmxlIGVuZHMgYXQgZmFtaWx5LCBidXQgZGlkbnQgZGVsZXRlIHZhbHVlcyB3aXRoIE5BIChOQXJtID0gRkFMU0UsIHJlYWQgTkEgcmVtb3ZlID0gZmFsc2UpDQpgYGB7cn0NCnBoeXNlcUB0YXhfdGFibGUNCmBgYA0KDQpFeGNlbGxlbnQsIGFsbW9zdCBkb25lIQ0KDQpOb3cgaWYgeW91IGluc3BlY3QgdGhlIHNhbXBsZSBuYW1lcyB0aGV5IGhhdmUgYSB2YXJpZXR5IG9mIG1lYW5pbmdzDQpgYGB7cn0NCnBoeXNlcUBvdHVfdGFibGUNCmBgYA0KVGhpbmdzIGxpa2UgIm11bmljaXBhbCBjb21wb3N0IiwgImZlcnRhbGl6ZWQiIGVjdCwgdGhpcyBpcyBtZXRhZGF0YSB0aGF0IGFyZSBpbiB0aGUgc2FtcGxlIG5hbWVzLCBidXQgdGhlIHBoeWxvc2VxIG9iamVjdCBkb2Vzbid0IGtub3cgYWJvdXQgeWV0LiANCkZ1biBmYWN0OiBpZiB5b3Ugd2FudCB0byBrbm93IHdoYXQgdGhpcyBhY3R1YWxseSBpcyBvciB3aGF0IHRoZSBuYW1lcyBtZWFuIGFzayB0byByZWFkIG15IG1hbnVzY3JpcHQNCmJ1dCB3ZSBtdXN0IGltcG9ydCB0aGlzIG1ldGFkYXRhLiANCg0KYGBge3J9DQpLZXkgPC0gcmVhZF9jc3YoIkM6XFxVc2Vyc1xcYW5ndXNcXE9uZURyaXZlIC0gVU5CQ1xcQW5ndXMgQmFsbFxcTGFiIHdvcmtcXFVMVFJBXFxUaGV5IGNhbGwgbWUuLi4gZGF0YVxcQW5ndXMtMTZTXFx1bHRyYSB0byBjYXRlZ29yaWVzIGtleS5jc3YiKQ0KYGBgDQoNCmlmIHlvdSBleHBsb3JlIHRoaXMgZG9jdW1lbnQsIHlvdSdsbCByZWFsaXplIGl0IGhhcyBhbGwgdGhlIHNhbXBsZXMgaW4gb3JkZXIgdGhleSBhcHBlYXIgb24gY29sdW1uIG5hbWVzIHdpdGhpbiB0aGUgTWV0YUcgdGFibGUsIGJ1dCB0aGV5IGFyZSBpbiB0aGUgcm93cyBpbnN0ZWFkLiBUaGUgbWV0YSBkYXRhIGluIG90aGVyIGNvbHVtbnMNCg0KDQpmb3IgdGhlIHNhbWUgcmVhc29ucyB3ZSBjb252ZXJ0ZWQgdG8gT1RVIGVhcmxpZXIgKHBoeWxvc2VxIGJlaW5nIGZpY2tsZSksIHdlIGhhdmUgdG8gbWFrZSB0aGUgc2FtcGxlIG5hbWVzIGJlIHRoZSBsZWZ0IG1vc3Qgcm93IG5hbWUNCmBgYHtyfQ0KS2V5IDwtIGRhdGEuZnJhbWUoS2V5WywtMV0sIHJvdy5uYW1lcz1LZXkkTmFtZSkNCnNhbXBsZWRhdGEgPSBzYW1wbGVfZGF0YShLZXkpDQpgYGANCg0KDQoNCk5vdyB3ZSB3aWxsIGFkZCB0aGUga2V5IGRhdGEgDQpgYGB7cn0NCnBoeXNlcV9LZXkgPSBtZXJnZV9waHlsb3NlcShwaHlzZXEsIHNhbXBsZWRhdGEpDQpgYGANCg0KDQpDb25ncmF0cyB5b3UgYXJlIGRvbmUgKGxpZSkhISEgWW91IG5vdyBoYXZlIGEgcGh5bG9zZXEgb2JqZWN0IGFuZCBjYW4gbW92ZSBvbiB0byB0cmFuc2Zvcm1hdGlvbnMgYW5kIGRhdGEgYW5hbHlzaXMNCg==