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
These are the packages you’ll need
library(phyloseq)
library(ggplot2)
library(janitor)
library(dplyr)
library(tidyverse)
library(ggpubr)
library(ape)
library(splitstackshape)
library(Biostrings)
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")
Seq_1 <- read_csv("C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\ULTRA\\They call me... data\\Angus-16S\\16S-sv_seqs.csv")
Seq_2 <- read_csv("C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\ULTRA\\They call me... data\\Angus-16S\\Redone mastermix stuff\\16S_RefInoculum_sv_seqs.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
This is what the data looks like (the second copy is the same)
head(MetaG_1)
head(Seq_1)
We’ll start by concatenating all these tables! The MetaG tables are
OTU tables, with count and species data, the Seq tables are the exact
ASVs for each sequence. Since we want to combine these tables we must
make sure that the ASV’s exactly match each other
So,
MetaG_1_total <- cbind(MetaG_1, Seq_1$Seq)
MetaG_2_total <- cbind(MetaG_2, Seq_2$Seq)
Then we have to remove the SVs so there isnt multiple of the same
value in the final table
#remove the sV value
MetaG_1_total <- select(MetaG_1_total, -(1))
MetaG_2_total <- select(MetaG_2_total, -(1))
Then we can combine these two tables based on the ASV data is Seq,
BUT to make sure we dont loose anything lets find out the amount of
counts per row. Im just going to spot check two
sum(MetaG_1_total$`Municipal compost-10-Fertalized-Inoculated`)
[1] 20690
sum(MetaG_2_total$MasterMix)
[1] 22651
okay time to combine them
MetaG <- merge(MetaG_1_total, MetaG_2_total, by.x = "Seq_1$Seq", by.y = "Seq_2$Seq", all.x = TRUE, all.y = TRUE)
Lets check those sums again!
sum(MetaG$`Municipal compost-10-Fertalized-Inoculated`)
[1] NA
sum(MetaG$MasterMix)
[1] NA
Thats because in the merge step a bunch of NA’s were added on rows
not seen in both samples, we can just set those NA’s to 0
MetaG[is.na(MetaG)]<-0
sum(MetaG$`Municipal compost-10-Fertalized-Inoculated`)
[1] 20690
sum(MetaG$MasterMix)
[1] 22651
Perfect same as before so there was no data loss!
Lets just clean up the table a bit
#first seperate the ASV's into a new table
asvs <- select(MetaG, "Seq_1$Seq")
MetaG <- select(MetaG, -"Seq_1$Seq") #then remove them from the table
#lets seperate the taxa values
Tax_temp<-select(MetaG, c("tax.vector.x", "tax.vector.y"))
#then lets put them in the same column, so that tax.vector.x contains no zeros and holds all the data
Tax_fixed <- Tax_temp %>%
mutate(tax.vector.x = ifelse(tax.vector.x == 0, tax.vector.y,tax.vector.x))#spoken, if tax.x contains a zero replace with value from tax.y (which we know has to have a value if tax.x doesn't)
#rename
Tax_fixed <- select(Tax_fixed, -"tax.vector.y")
#Then we can fully clean up the metaG table
MetaG <- MetaG %>%
select(-"tax.vector.x") %>%
select(-"tax.vector.y") %>%
select(-("CHB1P1":"FNP9B3")) #removing the tax's bc they are in tax_fixed now, and removing samples not applicable to this dataset
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<-select(Tax_fixed, ("tax.vector.x"))
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 <- cSplit(Tax, "tax.vector.x", ":")
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 <- dplyr::rename(Tax,
Kingdom = tax.vector.x_1,
Phylum = tax.vector.x_2,
Class = tax.vector.x_3,
Order = tax.vector.x_4,
Family = tax.vector.x_5,
Genus = tax.vector.x_6,
Species = tax.vector.x_7,
)
#our datasets kinda wibbly wobbly because we've been adding things together, so sometimes if there isnt a designated taxon it writes NA and sometimes it write S__ or F__, lets fix that so it always just writes S__ ect
#first set to nas to zero
Tax[is.na(Tax)]<-0
#then find and replace with a similar command as above
Tax <- Tax %>%
mutate(Kingdom = ifelse(Kingdom == 0, "k__",Kingdom))%>%
mutate(Phylum = ifelse(Phylum == 0, "p__NA",Phylum))%>%
mutate(Class = ifelse(Class == 0, "c__",Class))%>%
mutate(Order = ifelse(Order == 0, "o__",Order))%>%
mutate(Family = ifelse(Family == 0, "f__",Family))%>%
mutate(Genus = ifelse(Genus == 0, "g__",Genus))%>%
mutate(Species = ifelse(Species == 0, "s__",Species))
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 <- as.matrix(Tax)
MetaG <- as.matrix(MetaG)
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] "matrix" "array"
class(MetaG)
[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) <- paste0("OTU", 1:nrow(MetaG))
rownames(Tax) <- paste0("OTU", 1:nrow(Tax))
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 = otu_table(MetaG, taxa_are_rows = TRUE)
TAX = tax_table(Tax)
Now we’ll create a phyloseq object with each OTU table and TAXonomy
table
#combine that data
physeq = phyloseq(OTU, TAX)
Side point time! Hey do you need to add sequence information to your
phyloseq object? follow these next couple commands First import your
sequence information
Then we will pull the sequences from the file using biostrings, then
the taxon names from the phyloseq object, then combine this information
and add it to the phyloseq object
sequences <- Biostrings::DNAStringSet(asvs$`Seq_1$Seq`)
names(sequences) <- taxa_names(physeq)
physeq <- merge_phyloseq(physeq, sequences)
You can see it worked by
physeq
phyloseq-class experiment-level object
otu_table() OTU Table: [ 48054 taxa and 52 samples ]
tax_table() Taxonomy Table: [ 48054 taxa by 7 taxonomic ranks ]
refseq() DNAStringSet: [ 48054 reference sequences ]
The Otu and tax table were added earlier this step added the refseq()
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 (5): Name, Amd, Conc, Fertalized, Inoculum
ℹ 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
lets save it
#saveRDS(physeq_Key, file = "C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\Bioinformatics\\ULTRA\\physeq_Key_merged.rds")
physeq_Key <- readRDS(file = "C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\Bioinformatics\\ULTRA\\physeq_Key_merged.rds")
LS0tDQp0aXRsZTogIkltcG9ydGluZyBkYXRhIGZyb20gREFEQTIgdmlhIFBoeWxvc2VxIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCkJ5IEFuZ3VzIEJhbGwNCg0KSGksIGJ5IG5vdyB5b3UndmUgcHJvY2Vzc2VkIHlvdXIgZGF0YSB0aHJvdWdoIERBREEyIGFuZCBub3cgaXRzIHRpbWUgZm9yIHNvbWUgZGF0YSBhbmFseXNpcyEhISBoYWhhIHNpa2UsIHdlIGhhdmUgdG8gZm9ybWF0IGl0IGZpcnN0LCBnZXQgcmVrdA0KDQpUaGVzZSBhcmUgdGhlIHBhY2thZ2VzIHlvdSdsbCBuZWVkDQpgYGB7ciwgZWNobyA9IFQsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KHBoeWxvc2VxKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShqYW5pdG9yKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KGFwZSkNCmxpYnJhcnkoc3BsaXRzdGFja3NoYXBlKQ0KbGlicmFyeShCaW9zdHJpbmdzKQ0KYGBgDQoNCg0KDQoNCk5vdyBpdHMgdGltZSB0byBpbXBvcnQgb3VyIGRhdGEgZnJvbSBEQVRBMiwgdW5mb3J0dW5hdGVseSB3ZSBuZWVkIHRvIHdvcmsgd2l0aCBhIC5jc3YgZmlsZSBhbmQgb3VyIGRhdGEgY29tZSBvZmYgaW4gdGV4dCBmaWxlcw0KRnVuIGZhY3Q6IC5jc3YgZmlsZXMgYXJlIGp1c3QgYmFzaWMgZXhjZWwgZmlsZXMNCg0KVG8gdHVybiB5b3VyIHRleHQgZmlsZXMgaW50byBhIC5jc3YgZmlsZSBvcGVuIGV4Y2VsIGFuZCB0aGVuIHRyeSB0byBvcGVuIHlvdXIgLnR4dCBmaWxlLiBFeGNlbCB3aWxsIGdvICJoZXkuLi4iIGFuZCB0cnkgdG8gY29udmVydCB5b3VyIHR4dCBmaWxlIGludG8gYW4gZXhjZWwgZmlsZS4gVGhpcyBpcyBwZXJmZWN0ISBPdXIgZmlsZSBpcyBkZWxpbWl0ZWQgKHRoZSBhdXRvcGlja2VkIG9wdGlvbiksIHZpYSB0YWIgKHRoZSBhdXRvcGlja2VkIG9wdGlvbiBhZnRlciB5b3UgY2xpY2sgbmV4dCBvbmNlKSwgc28gYWxsIHlvdSBoYXZlIHRvIGRvIGlzIG1lYW5kZXIgdGhyb3VnaCB0aGUgcG9wIHVwIGFuZCBleGNlbCB3aWxsIGRvIGFsbCB0aGUgaGVhdnkgbGlmdGluZyBmb3IgeW91ISBTaW1wbHkgc2F2ZSB5b3VyIGZpbGUgYXMgYSAuY3N2IGZpbGUgd2hlcmUgZXZlciB5b3UnZCBsaWtlIQ0KDQpOb3cgZ28gb2ZmIGludG8gdG8gd29ybGQgYW5kIGNyZWF0ZSBhIGNzdiBmaWxlIG9mIHlvdSAxNlNfUltuYW1lXV9kYWRhMl9ub2NoaW1fdGF4LnR4dCBmaWxlIQ0KT2theSBzd2VldCwgb24gdGhlIGxlZnQgc2hvdWxkIGJlIGEgcm93IG9mIFNWXzAuLlN2XzEwIGVjdCwgaW4gdGhlIG1pZGRsZSBpcyB5b3Ugc2FtcGxlIG5hbWUgd2l0aCBhbGwgdGhlIGNvdW50IGRhdGEgcGVyIHNwZWNpZXMgYW5kIG9uIHRoZSByaWdodCBpcyB0YXgudmVjdG9yLg0KDQoNCg0KRnVuIGZhY3Q6IGJhY2t1cHMhIGhleSBpJ3ZlIG5vdGljZWQgdGhhdCB5b3UgZGlkbid0IGltbWVkaWF0ZWx5IGNyZWF0ZSBhIGJhY2t1cCBhbmQgYSB3b3JraW5nIGNzdiBmaWxlLCBTaGFtZSBvbiB5b3UhIEl0cyBpbXBvcnRhbnQgdG8gYWx3YXlzIGNyZWF0ZSBiYWNrdXAgZmlsZXMgeW91IE5FVkVSIG1vZGlmeSBpbiBleGNlbCB3aGVuIHlvdSBnZXQgYSBuZXcgZmlsZSB5b3UgcGxhbiBvbiBtb2RpZnlpbmcuIEl0J2QgYmUgYSBzaGFtZSBpcyB5b3Ugd2VyZSB0byBkZWxldGUgc29tZXRoaW5nIHlvdSBzaG91bGRuJ3QgaGF2ZSBhbmQgdGhlbiByZXN0YXJ0IGZyb20gdGhlIGJlZ2lubmluZy4gDQoNCllvdSdyZSAxNlNfUltuYW1lXV9kYWRhMl9ub2NoaW1fdGF4LmNzdiBmaWxlIGlzIG5vdyByZWFkeSB0byBiZSBpbXBvcnRlZCBpbnRvIFINCg0KTGV0cyBpbXBvcnQgdGhpcyB0YWJsZQ0KDQpgYGB7ciwgZWNobyA9IFQsIG1lc3NhZ2U9RkFMU0V9DQpNZXRhR18xIDwtIHJlYWRfY3N2KCJDOlxcVXNlcnNcXGFuZ3VzXFxPbmVEcml2ZSAtIFVOQkNcXEFuZ3VzIEJhbGxcXExhYiB3b3JrXFxVTFRSQVxcVGhleSBjYWxsIG1lLi4uIGRhdGFcXEFuZ3VzLTE2U1xcMTZTLWRhZGEyX25vY2hpbV90YXguY3N2IikNCk1ldGFHXzIgPC0gcmVhZF9jc3YoIkM6XFxVc2Vyc1xcYW5ndXNcXE9uZURyaXZlIC0gVU5CQ1xcQW5ndXMgQmFsbFxcTGFiIHdvcmtcXFVMVFJBXFxUaGV5IGNhbGwgbWUuLi4gZGF0YVxcQW5ndXMtMTZTXFxSZWRvbmUgbWFzdGVybWl4IHN0dWZmXFwxNlNfUmVmSW5vY3VsdW1fZGFkYTJfbm9jaGltX3RheC5jc3YiKQ0KU2VxXzEgPC0gcmVhZF9jc3YoIkM6XFxVc2Vyc1xcYW5ndXNcXE9uZURyaXZlIC0gVU5CQ1xcQW5ndXMgQmFsbFxcTGFiIHdvcmtcXFVMVFJBXFxUaGV5IGNhbGwgbWUuLi4gZGF0YVxcQW5ndXMtMTZTXFwxNlMtc3Zfc2Vxcy5jc3YiKQ0KU2VxXzIgPC0gcmVhZF9jc3YoIkM6XFxVc2Vyc1xcYW5ndXNcXE9uZURyaXZlIC0gVU5CQ1xcQW5ndXMgQmFsbFxcTGFiIHdvcmtcXFVMVFJBXFxUaGV5IGNhbGwgbWUuLi4gZGF0YVxcQW5ndXMtMTZTXFxSZWRvbmUgbWFzdGVybWl4IHN0dWZmXFwxNlNfUmVmSW5vY3VsdW1fc3Zfc2Vxcy5jc3YiKQ0KDQpgYGANCg0KQ29uZ3JhdHMhIFdlIG5vdyBoYXZlIHRoZSBkYXRhIGluIFIsIHlvdSBtYXkgaGF2ZSBub3RpY2VkIEkndmUgYWN0dWFsbHkgcmVhZCAyIHNlcGFyYXRlIHNldHMgb2YgZGF0YSwgMSBhbmQgMi4gWW91IG1pZ2h0IG5vdCBoYXZlIHR3byBzZXRzIG9mIGRhdGEgYnV0IEkgZG8hLiBXZSB3aWxsIHRoZW4gY3JlYXRlIHBoeWxvc2VxIG9iamVjdHMgd2l0aCBib3RoIHRoZXNlIGRhdGFzZXRzDQoNClRoaXMgaXMgd2hhdCB0aGUgZGF0YSBsb29rcyBsaWtlICh0aGUgc2Vjb25kIGNvcHkgaXMgdGhlIHNhbWUpDQpgYGB7cn0NCmhlYWQoTWV0YUdfMSkNCmBgYA0KYGBge3J9DQpoZWFkKFNlcV8xKQ0KYGBgDQoNCg0KDQoNCldlJ2xsIHN0YXJ0IGJ5IGNvbmNhdGVuYXRpbmcgYWxsIHRoZXNlIHRhYmxlcyEgVGhlIE1ldGFHIHRhYmxlcyBhcmUgT1RVIHRhYmxlcywgd2l0aCBjb3VudCBhbmQgc3BlY2llcyBkYXRhLCB0aGUgU2VxIHRhYmxlcyBhcmUgdGhlIGV4YWN0IEFTVnMgZm9yIGVhY2ggc2VxdWVuY2UuIFNpbmNlIHdlIHdhbnQgdG8gY29tYmluZSB0aGVzZSB0YWJsZXMgd2UgbXVzdCBtYWtlIHN1cmUgdGhhdCB0aGUgQVNWJ3MgZXhhY3RseSBtYXRjaCBlYWNoIG90aGVyDQoNClNvLCANCg0KYGBge3J9DQpNZXRhR18xX3RvdGFsIDwtIGNiaW5kKE1ldGFHXzEsIFNlcV8xJFNlcSkNCk1ldGFHXzJfdG90YWwgPC0gY2JpbmQoTWV0YUdfMiwgU2VxXzIkU2VxKQ0KDQpgYGANCg0KVGhlbiB3ZSBoYXZlIHRvIHJlbW92ZSB0aGUgU1ZzIHNvIHRoZXJlIGlzbnQgbXVsdGlwbGUgb2YgdGhlIHNhbWUgdmFsdWUgaW4gdGhlIGZpbmFsIHRhYmxlDQoNCmBgYHtyfQ0KI3JlbW92ZSB0aGUgc1YgdmFsdWUNCk1ldGFHXzFfdG90YWwgPC0gc2VsZWN0KE1ldGFHXzFfdG90YWwsIC0oMSkpDQpNZXRhR18yX3RvdGFsIDwtIHNlbGVjdChNZXRhR18yX3RvdGFsLCAtKDEpKQ0KDQpgYGANCg0KVGhlbiB3ZSBjYW4gY29tYmluZSB0aGVzZSB0d28gdGFibGVzIGJhc2VkIG9uIHRoZSBBU1YgZGF0YSBpcyBTZXEsIEJVVCB0byBtYWtlIHN1cmUgd2UgZG9udCBsb29zZSBhbnl0aGluZyBsZXRzIGZpbmQgb3V0IHRoZSBhbW91bnQgb2YgY291bnRzIHBlciByb3cuIEltIGp1c3QgZ29pbmcgdG8gc3BvdCBjaGVjayB0d28NCg0KYGBge3J9DQpzdW0oTWV0YUdfMV90b3RhbCRgTXVuaWNpcGFsIGNvbXBvc3QtMTAtRmVydGFsaXplZC1Jbm9jdWxhdGVkYCkNCnN1bShNZXRhR18yX3RvdGFsJE1hc3Rlck1peCkNCmBgYA0Kb2theSB0aW1lIHRvIGNvbWJpbmUgdGhlbQ0KYGBge3J9DQpNZXRhRyA8LSBtZXJnZShNZXRhR18xX3RvdGFsLCBNZXRhR18yX3RvdGFsLCBieS54ID0gIlNlcV8xJFNlcSIsIGJ5LnkgPSAiU2VxXzIkU2VxIiwgYWxsLnggPSBUUlVFLCBhbGwueSA9IFRSVUUpDQpgYGANCg0KTGV0cyBjaGVjayB0aG9zZSBzdW1zIGFnYWluIQ0KDQpgYGB7cn0NCnN1bShNZXRhRyRgTXVuaWNpcGFsIGNvbXBvc3QtMTAtRmVydGFsaXplZC1Jbm9jdWxhdGVkYCkNCnN1bShNZXRhRyRNYXN0ZXJNaXgpDQpgYGANClRoYXRzIGJlY2F1c2UgaW4gdGhlIG1lcmdlIHN0ZXAgYSBidW5jaCBvZiBOQSdzIHdlcmUgYWRkZWQgb24gcm93cyBub3Qgc2VlbiBpbiBib3RoIHNhbXBsZXMsIHdlIGNhbiBqdXN0IHNldCB0aG9zZSBOQSdzIHRvIDANCg0KYGBge3J9DQpNZXRhR1tpcy5uYShNZXRhRyldPC0wDQpgYGANCmBgYHtyfQ0Kc3VtKE1ldGFHJGBNdW5pY2lwYWwgY29tcG9zdC0xMC1GZXJ0YWxpemVkLUlub2N1bGF0ZWRgKQ0Kc3VtKE1ldGFHJE1hc3Rlck1peCkNCmBgYA0KUGVyZmVjdCBzYW1lIGFzIGJlZm9yZSBzbyB0aGVyZSB3YXMgbm8gZGF0YSBsb3NzIQ0KDQpMZXRzIGp1c3QgY2xlYW4gdXAgdGhlIHRhYmxlIGEgYml0DQpgYGB7cn0NCiNmaXJzdCBzZXBlcmF0ZSB0aGUgQVNWJ3MgaW50byBhIG5ldyB0YWJsZQ0KYXN2cyA8LSBzZWxlY3QoTWV0YUcsICJTZXFfMSRTZXEiKQ0KTWV0YUcgPC0gc2VsZWN0KE1ldGFHLCAtIlNlcV8xJFNlcSIpICN0aGVuIHJlbW92ZSB0aGVtIGZyb20gdGhlIHRhYmxlDQojbGV0cyBzZXBlcmF0ZSB0aGUgdGF4YSB2YWx1ZXMNClRheF90ZW1wPC1zZWxlY3QoTWV0YUcsIGMoInRheC52ZWN0b3IueCIsICJ0YXgudmVjdG9yLnkiKSkNCiN0aGVuIGxldHMgcHV0IHRoZW0gaW4gdGhlIHNhbWUgY29sdW1uLCBzbyB0aGF0IHRheC52ZWN0b3IueCBjb250YWlucyBubyB6ZXJvcyBhbmQgaG9sZHMgYWxsIHRoZSBkYXRhDQpUYXhfZml4ZWQgPC0gVGF4X3RlbXAgJT4lDQogIG11dGF0ZSh0YXgudmVjdG9yLnggPSBpZmVsc2UodGF4LnZlY3Rvci54ID09IDAsIHRheC52ZWN0b3IueSx0YXgudmVjdG9yLngpKSNzcG9rZW4sIGlmIHRheC54IGNvbnRhaW5zIGEgemVybyByZXBsYWNlIHdpdGggdmFsdWUgZnJvbSB0YXgueSAod2hpY2ggd2Uga25vdyBoYXMgdG8gaGF2ZSBhIHZhbHVlIGlmIHRheC54IGRvZXNuJ3QpDQojcmVuYW1lDQpUYXhfZml4ZWQgPC0gc2VsZWN0KFRheF9maXhlZCwgLSJ0YXgudmVjdG9yLnkiKQ0KYGBgDQoNCg0KYGBge3J9DQojVGhlbiB3ZSBjYW4gZnVsbHkgY2xlYW4gdXAgdGhlIG1ldGFHIHRhYmxlDQpNZXRhRyA8LSBNZXRhRyAlPiUNCiAgc2VsZWN0KC0idGF4LnZlY3Rvci54IikgJT4lDQogICAgICAgICAgIHNlbGVjdCgtInRheC52ZWN0b3IueSIpICU+JQ0KICAgICAgICBzZWxlY3QoLSgiQ0hCMVAxIjoiRk5QOUIzIikpICNyZW1vdmluZyB0aGUgdGF4J3MgYmMgdGhleSBhcmUgaW4gdGF4X2ZpeGVkIG5vdywgYW5kIHJlbW92aW5nIHNhbXBsZXMgbm90IGFwcGxpY2FibGUgdG8gdGhpcyBkYXRhc2V0DQoNCmBgYA0KDQoNCg0KDQoNCg0KRmlyc3QgdGhpbmdzIGZpcnN0LCB3ZSBoYXZlIHRvIGZvcm1hdCB0aGUgdGFibGVzIGluIGEgd2F5IHRoYXQgcGh5bG9zZXEgaXMgZXhwZWN0aW5nIHRoZW0gYW5kIHVuZm9ydHVuYXRlbHksIHRoaXMgaXMgYSBiaXQgb2YgYSBwYWluLiBQaHlsb3NlcSBleHBlY3RzIGEgY291cGxlIHRhYmxlcy4gRmlyc3QgYSBjb3VudCB0YWJsZSAodGhpcyBpcyB0aGUgTWV0YUcgdGFibGUpLCBBIHRheG9ub215IHRhYmxlIChUaGlzIGlzIHRheC52ZWN0b3IpLCBhbmQgYSBzYW1wbGUgZGF0YSB0YWJsZSAoVGhpcyBvbmVzIGJhc2VkIG9mZiBvZiB0aGUgZ3JvdXAgb2YgZGF0YSB0aGF0IGV4aXN0IGluIHlvdXIgc2FtcGxlKQ0KDQpMZXRzIGZpcnN0IHN0YXJ0IGJ5IGNyZWF0aW5nIHRoZSB0YXhvbm9teSB0YWJsZSBmcm9tIHRoZSBNZXRhRyB0YWJsZXMhDQoNCmBgYHtyfQ0KVGF4PC1zZWxlY3QoVGF4X2ZpeGVkLCAoInRheC52ZWN0b3IueCIpKSANCmBgYA0KT2ggYm90aGVyISBQaHlsb3NlcSBleHBlY3RzIHRoZSB0YXhvbm9teSB0byBiZSBzZXBhcmF0ZWQgYnkgdGF4b25zIGkuZS4gb25lIGNvbHVtbiBmb3Iga2luZ2RvbSwgb25lIGZvciBmYW1pbHkgZWN0LiBCdXQgZXZlcnl0aGluZyBpcyB1bmRlciBvbmUgY29sdW1uLiBUaGlzIG1lYW5zIHdlIGhhdmUgdG8gZGVsaW1pbmF0ZSBhZ2FpbiwgYW5kIGxvb2tpbmcgYXQgdGhlIGhvdyB0byBjb2x1bW4gaXMgY3JlYXRlZCBmcm9tIERBREEyLCB3ZSBuZWVkIHRvIGRlbGltaW5hdGUgYnkgY29sb24NCg0KY1NwbGl0IGRlbGltaW5hdGVzIHdpdGhpbiBhIGNvbHVtbiBhbmQgdGhlbiB0aGUgcmVuYW1lIGZ1bmN0aW9ucyBtYWtlIGFsbCB0aGUgY29sdW1ucyBuYW1lcyBwaHlsb3NlcSBleHBlY3RzIQ0KDQpgYGB7cn0NClRheCA8LSBjU3BsaXQoVGF4LCAidGF4LnZlY3Rvci54IiwgIjoiKQ0KVGF4IDwtIGRwbHlyOjpyZW5hbWUoVGF4LA0KIEtpbmdkb20gPSAgdGF4LnZlY3Rvci54XzEsDQogUGh5bHVtID0gdGF4LnZlY3Rvci54XzIsDQogQ2xhc3MgPSB0YXgudmVjdG9yLnhfMywNCiBPcmRlciA9IHRheC52ZWN0b3IueF80LA0KIEZhbWlseSA9IHRheC52ZWN0b3IueF81LA0KIEdlbnVzID0gdGF4LnZlY3Rvci54XzYsDQogU3BlY2llcyA9IHRheC52ZWN0b3IueF83LA0KICANCiAgKQ0KDQojb3VyIGRhdGFzZXRzIGtpbmRhIHdpYmJseSB3b2JibHkgYmVjYXVzZSB3ZSd2ZSBiZWVuIGFkZGluZyB0aGluZ3MgdG9nZXRoZXIsIHNvIHNvbWV0aW1lcyBpZiB0aGVyZSBpc250IGEgZGVzaWduYXRlZCB0YXhvbiBpdCB3cml0ZXMgTkEgYW5kIHNvbWV0aW1lcyBpdCB3cml0ZSBTX18gb3IgRl9fLCBsZXRzIGZpeCB0aGF0IHNvIGl0IGFsd2F5cyBqdXN0IHdyaXRlcyBTX18gZWN0DQojZmlyc3Qgc2V0IHRvIG5hcyB0byB6ZXJvDQpUYXhbaXMubmEoVGF4KV08LTANCiN0aGVuIGZpbmQgYW5kIHJlcGxhY2Ugd2l0aCBhIHNpbWlsYXIgY29tbWFuZCBhcyBhYm92ZQ0KVGF4IDwtIFRheCAlPiUNCiAgbXV0YXRlKEtpbmdkb20gPSBpZmVsc2UoS2luZ2RvbSA9PSAwLCAia19fIixLaW5nZG9tKSklPiUNCiAgbXV0YXRlKFBoeWx1bSA9IGlmZWxzZShQaHlsdW0gPT0gMCwgInBfX05BIixQaHlsdW0pKSU+JQ0KICBtdXRhdGUoQ2xhc3MgPSBpZmVsc2UoQ2xhc3MgPT0gMCwgImNfXyIsQ2xhc3MpKSU+JQ0KICBtdXRhdGUoT3JkZXIgPSBpZmVsc2UoT3JkZXIgPT0gMCwgIm9fXyIsT3JkZXIpKSU+JQ0KICBtdXRhdGUoRmFtaWx5ID0gaWZlbHNlKEZhbWlseSA9PSAwLCAiZl9fIixGYW1pbHkpKSU+JQ0KICBtdXRhdGUoR2VudXMgPSBpZmVsc2UoR2VudXMgPT0gMCwgImdfXyIsR2VudXMpKSU+JQ0KICBtdXRhdGUoU3BlY2llcyA9IGlmZWxzZShTcGVjaWVzID09IDAsICJzX18iLFNwZWNpZXMpKQ0KDQpgYGANCg0KDQoNCldlIGhhdmUgdG8gdGFrZSBvdXIgdGFibGVzICh0aGF0IGV4aXN0IGFzIGEgZGF0YWZyYW1lKSBhbmQgY29udmVydCB0aGVtIGludG8gYSBtYXh0cml4LCB3aHk/IGJlY2F1c2UgdGhlIHBoeWxvc2VxIGNsYXNzIHVzZXMgbWF0cml4ZXMgYW5kIG5vdCBkYXRhZnJhbWVzIChpbiB0aGUgZXhhbXBsZSBhYm92ZSB3ZSBjb3VsZG4ndCBmZWVkIG91ciBjbGFzcyB0d28gaW50IHZhbHVlcywgaXQgd2FudHMgYW5kIGludCBhbmQgYSBjaGFyISkNCg0KYGBge3J9DQojbm93IHJlYWwgc3R1ZmYNCiNjb252ZXJ0IHRoZXNlIGJhZCBib3lzIHRvIG1hdHJpeGVzDQpUYXggPC0gYXMubWF0cml4KFRheCkNCk1ldGFHIDwtIGFzLm1hdHJpeChNZXRhRykNCg0KDQpgYGANCg0KWW91IGNhbiBjaGVjayB0aGUgY2xhc3Mgb2YgdGhlIG9iamVjdHMgd2l0aCBhIHNpbXBsZSBjb21tYW5kIChvaCB5ZXMgdGhlIHRhYmxlcyBoYXZlIGJlZW4gb2JqZWN0cyBhbGwgYWxvbmcsIHdlJ3JlIGNyZWF0aW5nIG9iamVjdHMgd2l0aGluIGNsYXNzZXMgd2l0aGluIGNsYXNzZXMhIG5lYXQgaXNudCBpdD8pDQoNCmBgYHtyfQ0KI2NoZWNrDQpjbGFzcyhUYXgpDQpjbGFzcyhNZXRhRykNCg0KYGBgDQoNCg0KUGh5bG9zZXEgZXhwZWN0cyB0aGUgZmFyIGxlZnQgY29sdW1uIHRvIGJlIGNhbGxlZCBPVFUsIHRoaXMgaXMgaG93IHRvIGRvIGl0LCBJJ20gbmFtaW5nIHRoZSBPVFVzIDEgYW5kIDIgYmVjYXVzZSBvZiBob3cgcGh5bG9zZXEgbWVyZ2VzIHJvdyBuYW1lcyBsYXRlciBvbg0KYGBge3J9DQojY2FsbCByb3cgdmFsdWVzIE9UVQ0Kcm93bmFtZXMoTWV0YUcpIDwtIHBhc3RlMCgiT1RVIiwgMTpucm93KE1ldGFHKSkNCnJvd25hbWVzKFRheCkgPC0gcGFzdGUwKCJPVFUiLCAxOm5yb3coVGF4KSkNCg0KDQpgYGANCg0KDQpPa2F5IGhvcGVmdWxseSB5b3Uga25vdyBhIGJpdCBhYm91dCBvYmplY3RzL2NsYXNzZXMsIGlmIG5vdA0KRnVubGVzcyBmYWN0OiBwbGVhc2UgZ28gcmVhZCB0aGUgZmlyc3QgY291cGxlIGNoYXB0ZXJzIG9mIGEgY29kaW5nIGluIGphdmEgdGV4dGJvb2sNCkZ1bm5lciBmYWN0OiBkYXRhIHN0cnVjdHVyZSBpcyBuZWF0LCBzYXkgeW91IHdhbnQgdG8gc3RvcmUgYSBudW1iZXIsICIxIiwgeW91IGNhbiBkbyBzbyBpbiBqYXZhIGJ5IHRlbGxpbmcgaXQgYSBkYXRhdHlwZSAoaW50IGZvciBpbnRlZ2VyKSwgYW5kIHRoZW4gYSB2YWx1ZSwgZm9yIGV4YW1wbGUgaW50IHg9MTsgdGhpcyB0ZWxscyB0aGUgY29tcHV0ZXIgdGhhdCB4IGlzIGVxdWFsIHRvIG9uZS4gR3JlYXQgYnV0IGludCBvbmx5IHdvcmtzIGZvciBudW1iZXJzIChhbmQgbm90IGV2ZW4gYWxsIG9mIHRoZW0gOyksIHNvIHdoYXQgaXMgeW91IHdhbnRlZCB0byBzdG9yZSBhIGxldHRlcj8gd2VsbCB5b3UgdXNlIGNoYXIgKGZvciBjaGFyYWN0ZXIpLCBzbyBjaGFyIHk9ImIiOyB0aGlzIGlzIHNheWluZyB0aGF0IHkgaXMgZXF1YWwgdG8gdGhlIGxldHRlciAiYiIuIE9idmlvdXNseSB0aGlzIHF1aWNrIGV4YW1wbGUgbG9vc2VzIGFsb3Qgb2YgdGhlIHN1YnRseSBhbmQgY29tcGxleGl0eSBvZiB0aGUgc3lzdGVtIChyZTogZ28gcmVhZCB0aGUgZmlyc3QgY291cGxlIGNoYXB0ZXJzIG9mIGEgamF2YSB0ZXh0Ym9vayksIGJ1dCBhbGFzLg0KDQpTbyBub3cgbGV0cyBzYXkgeW91IHdhbnRlZCBhIHRoaW5nLCBsZXRzIGNhbGwgaXQgYSBjbGFzcywgdGhhdCBjb250YWluZWQgYSBudW1iZXIgYW5kIGEgY2hhcmFjdGVyLCB5b3UgY291bGQgY3JlYXRlIGEgY2xhc3MgdGhhdCB1c2VkIHggYW5kIGl0IHVzZWQgeSB0byBjcmVhdGUgYW4gb2JqZWN0LiBUaGlzIGlzIGEgcG9vciBleHBsYW5hdGlvbiB0aGF0IGhhcyBsaWtlbHkgbWFkZSBzZXZlcmFsIGNvbXB1dGVyIHNjaWVudGlzdHMgc2FkIGJ1dCB3aGF0ZXZlciwgcGh5bG9zZXEgaXMgYSBjbGFzcyB0aGF0IGNyZWF0ZXMgcGh5bG9zZXEgb2JqZWN0cywgZWFjaCBvYmplY3QgaGF2aW5nIGF0IGEgbWluaW11bSBhbiBhYnVuZGFuY2UgdGFibGUgKG1ldGFHXykgYW5kIGEgdGF4b25vbXkgdGFibGUgKFRheF8pLiBFeGNlbGxlbnQgeW91IGFyZSBub3cgZXF1YWxseSBjb25mdXNlZCBhcyBlYXJsaWVyIGJ1dCBJIGZlZWwgbGlrZSBJJ3ZlIGRvbmUgbXkgZHVlIGRpbGxlZ2VuY2UsIG9ud2FyZC4NCg0KDQpOb3cgcGh5bG9zZXEgd2FudHMgdXMgdG8gdHVybiB0aGVzZSBtYXRyaWNlcyBpbnRvIGEgZGlmZmVyZW50IGNsYXNzIGNyZWF0ZWQgYnkgcGh5bG9zZXEgKG5vdCBpc250IGEgcGh5bG9zZXEgb2JqZWN0IHlldCkNCg0KYGBge3J9DQoNCk9UVSA9IG90dV90YWJsZShNZXRhRywgdGF4YV9hcmVfcm93cyA9IFRSVUUpDQpUQVggPSB0YXhfdGFibGUoVGF4KQ0KDQpgYGANCg0KTm93IHdlJ2xsIGNyZWF0ZSBhIHBoeWxvc2VxIG9iamVjdCB3aXRoIGVhY2ggT1RVIHRhYmxlIGFuZCBUQVhvbm9teSB0YWJsZQ0KDQpgYGB7cn0NCiNjb21iaW5lIHRoYXQgZGF0YQ0KcGh5c2VxID0gcGh5bG9zZXEoT1RVLCBUQVgpDQpgYGANCg0KU2lkZSBwb2ludCB0aW1lISBIZXkgZG8geW91IG5lZWQgdG8gYWRkIHNlcXVlbmNlIGluZm9ybWF0aW9uIHRvIHlvdXIgcGh5bG9zZXEgb2JqZWN0PyBmb2xsb3cgdGhlc2UgbmV4dCBjb3VwbGUgY29tbWFuZHMNCkZpcnN0IGltcG9ydCB5b3VyIHNlcXVlbmNlIGluZm9ybWF0aW9uDQoNCg0KVGhlbiB3ZSB3aWxsIHB1bGwgdGhlIHNlcXVlbmNlcyBmcm9tIHRoZSBmaWxlIHVzaW5nIGJpb3N0cmluZ3MsIHRoZW4gdGhlIHRheG9uIG5hbWVzIGZyb20gdGhlIHBoeWxvc2VxIG9iamVjdCwgdGhlbiBjb21iaW5lIHRoaXMgaW5mb3JtYXRpb24gYW5kIGFkZCBpdCB0byB0aGUgcGh5bG9zZXEgb2JqZWN0DQpgYGB7cn0NCnNlcXVlbmNlcyA8LSBCaW9zdHJpbmdzOjpETkFTdHJpbmdTZXQoYXN2cyRgU2VxXzEkU2VxYCkNCm5hbWVzKHNlcXVlbmNlcykgPC0gdGF4YV9uYW1lcyhwaHlzZXEpDQpwaHlzZXEgPC0gbWVyZ2VfcGh5bG9zZXEocGh5c2VxLCBzZXF1ZW5jZXMpDQpgYGANCg0KWW91IGNhbiBzZWUgaXQgd29ya2VkIGJ5DQpgYGB7cn0NCnBoeXNlcQ0KYGBgDQpUaGUgT3R1IGFuZCB0YXggdGFibGUgd2VyZSBhZGRlZCBlYXJsaWVyIHRoaXMgc3RlcCBhZGRlZCB0aGUgcmVmc2VxKCkgdGFibGUNCg0KRXhjZWxsZW50LCBhbG1vc3QgZG9uZSENCg0KTm93IGlmIHlvdSBpbnNwZWN0IHRoZSBzYW1wbGUgbmFtZXMgdGhleSBoYXZlIGEgdmFyaWV0eSBvZiBtZWFuaW5ncw0KYGBge3J9DQpwaHlzZXFAb3R1X3RhYmxlDQpgYGANClRoaW5ncyBsaWtlICJtdW5pY2lwYWwgY29tcG9zdCIsICJmZXJ0YWxpemVkIiBlY3QsIHRoaXMgaXMgbWV0YWRhdGEgdGhhdCBhcmUgaW4gdGhlIHNhbXBsZSBuYW1lcywgYnV0IHRoZSBwaHlsb3NlcSBvYmplY3QgZG9lc24ndCBrbm93IGFib3V0IHlldC4gDQpGdW4gZmFjdDogaWYgeW91IHdhbnQgdG8ga25vdyB3aGF0IHRoaXMgYWN0dWFsbHkgaXMgb3Igd2hhdCB0aGUgbmFtZXMgbWVhbiBhc2sgdG8gcmVhZCBteSBtYW51c2NyaXB0DQpidXQgd2UgbXVzdCBpbXBvcnQgdGhpcyBtZXRhZGF0YS4gDQoNCmBgYHtyfQ0KS2V5IDwtIHJlYWRfY3N2KCJDOlxcVXNlcnNcXGFuZ3VzXFxPbmVEcml2ZSAtIFVOQkNcXEFuZ3VzIEJhbGxcXExhYiB3b3JrXFxVTFRSQVxcVGhleSBjYWxsIG1lLi4uIGRhdGFcXEFuZ3VzLTE2U1xcdWx0cmEgdG8gY2F0ZWdvcmllcyBrZXkuY3N2IikNCmBgYA0KDQppZiB5b3UgZXhwbG9yZSB0aGlzIGRvY3VtZW50LCB5b3UnbGwgcmVhbGl6ZSBpdCBoYXMgYWxsIHRoZSBzYW1wbGVzIGluIG9yZGVyIHRoZXkgYXBwZWFyIG9uIGNvbHVtbiBuYW1lcyB3aXRoaW4gdGhlIE1ldGFHIHRhYmxlLCBidXQgdGhleSBhcmUgaW4gdGhlIHJvd3MgaW5zdGVhZC4gVGhlIG1ldGEgZGF0YSBpbiBvdGhlciBjb2x1bW5zDQoNCg0KZm9yIHRoZSBzYW1lIHJlYXNvbnMgd2UgY29udmVydGVkIHRvIE9UVSBlYXJsaWVyIChwaHlsb3NlcSBiZWluZyBmaWNrbGUpLCB3ZSBoYXZlIHRvIG1ha2UgdGhlIHNhbXBsZSBuYW1lcyBiZSB0aGUgbGVmdCBtb3N0IHJvdyBuYW1lDQpgYGB7cn0NCktleSA8LSBkYXRhLmZyYW1lKEtleVssLTFdLCByb3cubmFtZXM9S2V5JE5hbWUpDQpzYW1wbGVkYXRhID0gc2FtcGxlX2RhdGEoS2V5KQ0KYGBgDQoNCg0KDQpOb3cgd2Ugd2lsbCBhZGQgdGhlIGtleSBkYXRhIA0KYGBge3J9DQpwaHlzZXFfS2V5ID0gbWVyZ2VfcGh5bG9zZXEocGh5c2VxLCBzYW1wbGVkYXRhKQ0KYGBgDQoNCg0KQ29uZ3JhdHMgeW91IGFyZSBkb25lIChsaWUpISEhIFlvdSBub3cgaGF2ZSBhIHBoeWxvc2VxIG9iamVjdCBhbmQgY2FuIG1vdmUgb24gdG8gdHJhbnNmb3JtYXRpb25zIGFuZCBkYXRhIGFuYWx5c2lzDQoNCg0KDQpsZXRzIHNhdmUgaXQNCg0KYGBge3J9DQojc2F2ZVJEUyhwaHlzZXFfS2V5LCBmaWxlID0gIkM6XFxVc2Vyc1xcYW5ndXNcXE9uZURyaXZlIC0gVU5CQ1xcQW5ndXMgQmFsbFxcTGFiIHdvcmtcXEJpb2luZm9ybWF0aWNzXFxVTFRSQVxccGh5c2VxX0tleV9tZXJnZWQucmRzIikNCg0KcGh5c2VxX0tleSA8LSByZWFkUkRTKGZpbGUgPSAiQzpcXFVzZXJzXFxhbmd1c1xcT25lRHJpdmUgLSBVTkJDXFxBbmd1cyBCYWxsXFxMYWIgd29ya1xcQmlvaW5mb3JtYXRpY3NcXFVMVFJBXFxwaHlzZXFfS2V5X21lcmdlZC5yZHMiKQ0KYGBgDQo=