Just some housekeeping

Load libraries for RStudio

Open the phyloseq object

ps <- readRDS("C:/Users/faysmith/Desktop/seq/ps.rds")
ps
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 555 taxa and 10 samples ]
sample_data() Sample Data:       [ 10 samples by 4 sample variables ]
tax_table()   Taxonomy Table:    [ 555 taxa by 7 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 555 tips and 553 internal nodes ]

Filter out everything but the kingdom of fungi, since it was not our intent to amplify anything else

ps_sub <- subset_taxa(ps, Kingdom == "k__Fungi")
ps_sub
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 549 taxa and 10 samples ]
sample_data() Sample Data:       [ 10 samples by 4 sample variables ]
tax_table()   Taxonomy Table:    [ 549 taxa by 7 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 549 tips and 547 internal nodes ]

Looks like we only lost 6 taxa from the OTU table, meaning our amplification seems to have worked well to only target fungi.

Normalize the OTUs to account for sampling/sequence differences

There is some randomization at work here, so better set some seed (so the randomized numbers are reproducable) rarefy_even_depth is a function that will randomly sample OTUs from each group until it reaches the same number of OTUs that is in the smallest sample size. There are some that claim that rarefaction should not be used on this kind of data, but everyone does it anyway. Some fancier methods of normalizing the data exist, but they are more complicated and not as well published on.

set.seed(100)
#Randomly sample to the lowest denomination
ps_sub <- rarefy_even_depth(ps_sub, rngseed = TRUE)
ps_sub
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 503 taxa and 10 samples ]
sample_data() Sample Data:       [ 10 samples by 4 sample variables ]
tax_table()   Taxonomy Table:    [ 503 taxa by 7 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 503 tips and 501 internal nodes ]

We went from 549 OTUs in our dataset to 503 after normalizing for sample size.

#Optional: Limit all analyses to the top 10 most abundant taxa I’m not doing this one just yet, but it can make a first-look anlyses a little easier to understand.

# TopNOTUs = names(sort(taxa_sums(ps_sub), TRUE)[1:50])
# ps_10 <- prune_taxa(TopNOTUs, ps_sub)
# plot_bar(ps_10, "Depth", fill = "Genus", facet_grid = ~Area)
# 
# ps_sub <- ps_10

Stacked barplot by taxa

Plot a stacked barchart that shows the distribution of taxa (at all levels). Extremely small groups (<2%) are removed for simplification and all groups are plotted as a % of the total taxa in each sample.

#Plot the Phylum composition of Soil Fungi
ps_phylum <- ps_sub %>%
  tax_glom(taxrank = "Phylum") %>%                     # agglomerate at phylum level
  transform_sample_counts(function(x) {x/sum(x)} ) %>% # Transform to rel. abundance
  psmelt() %>%                                         # Melt to long format
  filter(Abundance > 0.02) %>%                         # Filter out low abundance taxa
  arrange(Phylum)                                      # Sort data frame alphabetically by phylum
ggplot(ps_phylum, aes(x = Depth, y = Abundance, fill = Phylum)) + 
  facet_grid(Area~Replication) +
  geom_bar(stat = "identity") +
  scale_x_discrete(labels=c("0-10 cm", "10-20 cm"))+
  guides(fill = guide_legend(reverse = TRUE, keywidth = 1, keyheight = 1)) +
  ylab("Relative Abundance (Phyla > 2%) \n") +
  ggtitle("Phylum Composition of Soil Fungi \n Fungal Communities by Sampling Site and Depth") 

#Plot the Class composition of Soil Fungi
ps_Class <- ps_sub %>%
  tax_glom(taxrank = "Class") %>%                     # agglomerate at Class level
  transform_sample_counts(function(x) {x/sum(x)} ) %>% # Transform to rel. abundance
  psmelt() %>%                                         # Melt to long format
  filter(Abundance > 0.02) %>%                         # Filter out low abundance taxa
  arrange(Class)                                      # Sort data frame alphabetically by Class
ggplot(ps_Class, aes(x = Depth, y = Abundance, fill = Class)) + 
  facet_grid(Area~Replication) +
  geom_bar(stat = "identity") +
  scale_x_discrete(labels=c("0-10 cm", "10-20 cm"))+
  guides(fill = guide_legend(reverse = TRUE, keywidth = 1, keyheight = 1)) +
  ylab("Relative Abundance (Class > 2%) \n") +
  ggtitle("Class Composition of Soil Fungi \n Fungal Communities by Sampling Site and Depth") 

#Plot the Order composition of Soil Fungi
ps_Order <- ps_sub %>%
  tax_glom(taxrank = "Order") %>%                     # agglomerate at Order level
  transform_sample_counts(function(x) {x/sum(x)} ) %>% # Transform to rel. abundance
  psmelt() %>%                                         # Melt to long format
  filter(Abundance > 0.02) %>%                         # Filter out low abundance taxa
  arrange(Order)                                      # Sort data frame alphabetically by Order
ggplot(ps_Order, aes(x = Depth, y = Abundance, fill = Order)) + 
  facet_grid(Area~Replication) +
  geom_bar(stat = "identity") +
  scale_x_discrete(labels=c("0-10 cm", "10-20 cm"))+
  guides(fill = guide_legend(reverse = TRUE, keywidth = 1, keyheight = 1)) +
  ylab("Relative Abundance (Order > 2%) \n") +
  ggtitle("Order Composition of Soil Fungi \n Fungal Communities by Sampling Site and Depth")

#Plot the Family composition of Soil Fungi
ps_Family <- ps_sub %>%
  tax_glom(taxrank = "Family") %>%                     # agglomerate at Family level
  transform_sample_counts(function(x) {x/sum(x)} ) %>% # Transform to rel. abundance
  psmelt() %>%                                         # Melt to long format
  filter(Abundance > 0.02) %>%                         # Filter out low abundance taxa
  arrange(Family)                                      # Sort data frame alphabetically by Family
ggplot(ps_Family, aes(x = Depth, y = Abundance, fill = Family)) + 
  facet_grid(Area~Replication) +
  geom_bar(stat = "identity") +
  scale_x_discrete(labels=c("0-10 cm", "10-20 cm"))+
  guides(fill = guide_legend(reverse = TRUE, keywidth = 1, keyheight = 1)) +
  ylab("Relative Abundance (Family > 2%) \n") +
  ggtitle("Family Composition of Soil Fungi \n Fungal Communities by Sampling Site and Depth")

#Plot the Genus composition of Soil Fungi
ps_Genus <- ps_sub %>%
  tax_glom(taxrank = "Genus") %>%                     # agglomerate at Genus level
  transform_sample_counts(function(x) {x/sum(x)} ) %>% # Transform to rel. abundance
  psmelt() %>%                                         # Melt to long format
  filter(Abundance > 0.02) %>%                         # Filter out low abundance taxa
  arrange(Genus)                                      # Sort data frame alphabetically by Genus
ggplot(ps_Genus, aes(x = Depth, y = Abundance, fill = Genus)) + 
  facet_grid(Area~Replication) +
  geom_bar(stat = "identity") +
  scale_x_discrete(labels=c("0-10 cm", "10-20 cm"))+
  guides(fill = guide_legend(reverse = TRUE, keywidth = 1, keyheight = 1)) +
  ylab("Relative Abundance (Genus > 2%) \n") +
  ggtitle("Genus Composition of Soil Fungi \n Fungal Communities by Sampling Site and Depth")

Visually, it seems that there are differences between the samples, even the ones collected from the same area. It might be worth exploring within each class represented in this bargraph to more clearly see what differences in genus abundance there are among the samples. It would also be great to seperate these into functional groups, something that seems possible but a new program may have to be written for it to happen.

NMDS of Bray-Curtis Distances

This is a non-metric multidimensional scaling plot that uses the dissimilarities among each sample data set to map out distances. Samples are analyzed as a sum of all the sequences within and Taxa are scaled seperatly by unique sequences.

Square root transformation
Wisconsin double standardization
Run 0 stress 0.08932695 
Run 1 stress 0.08607235 
... New best solution
... Procrustes: rmse 0.1994656  max resid 0.3850024 
Run 2 stress 0.07945461 
... New best solution
... Procrustes: rmse 0.1334179  max resid 0.3312774 
Run 3 stress 0.08607371 
Run 4 stress 0.07945426 
... New best solution
... Procrustes: rmse 0.0003676254  max resid 0.0006650386 
... Similar to previous best
Run 5 stress 0.08888285 
Run 6 stress 0.08932516 
Run 7 stress 0.1318923 
Run 8 stress 0.08888249 
Run 9 stress 0.08888243 
Run 10 stress 0.08674561 
Run 11 stress 0.08888329 
Run 12 stress 0.08607378 
Run 13 stress 0.1079878 
Run 14 stress 0.1318941 
Run 15 stress 0.07945433 
... Procrustes: rmse 0.0001557373  max resid 0.000284411 
... Similar to previous best
Run 16 stress 0.07945426 
... Procrustes: rmse 6.085333e-05  max resid 0.0001132677 
... Similar to previous best
Run 17 stress 0.0893256 
Run 18 stress 0.1079886 
Run 19 stress 0.1607972 
Run 20 stress 0.07945433 
... Procrustes: rmse 0.0001964965  max resid 0.0003594765 
... Similar to previous best
*** Solution reached

This ordination plot seems to indicate clustering of samples according to the area from which they were taken. There also might be some clustering of taxa within classes around these points, indicating a possible difference in community structure between mounds, depressions, and the adjacent pasture.

Plot alpha diversity levels

I wanted to see these done for the groups of sample area and by depth (not single samples). To create this average, I merged the replications together using “merge_samples” function in phyloseq. This causes the other values to be currupted, so they are renamed in the following code as categorical variables.

[1] "SampleID"    "Depth"       "Area"        "Replication"

It is important to note that, for each of these measurements of diversity, the lower numbers indicate higher levels of diversity. For instance, the Simpson diversity index of “1” indicates no diversity and an index of “0” indicates infinite diversity. Clearly, the top 10 cm of the mound area has the highest level of diversity, as to be expected.

Plot the phylogenetic tree for all samples

N = 50
GPN = prune_taxa(names(sort(taxa_sums(ps_sub), decreasing = TRUE)[1:N]), 
    ps_sub)
p <- plot_tree(GPN, nodelabf = nodeplotblank, ladderize="left", color="Area", shape = "Depth", label.tips = "Genus", text.size = 2.5)
# p + coord_polar(theta = "y") #Opttion for doing a radial grap
p

There are clear groups that fall within the samples in the “depression” area, although they are “NA” for genus, meaning that they are identified as fungi, but do not have a clear enough match to describe them further than Phylum in some cases. Interesting that the NA seems to be more common in the “Depression”" areas.

Plot heatmap

Shows the prevalence of OTUs for each sample time

N = 200
GPN = prune_taxa(names(sort(taxa_sums(ps_sub), decreasing = TRUE)[1:N]), 
    ps_sub)
GPN1 = subset_samples(ps_sub, Depth=="1")
GPN1 = prune_taxa(names(sort(taxa_sums(GPN1), decreasing = TRUE)[1:N]), 
    GPN1)
GPN2 = subset_samples(GPN, Depth=="2")
GPN2 = prune_taxa(names(sort(taxa_sums(GPN2), decreasing = TRUE)[1:N]), 
    GPN2)
plot_heatmap(GPN, sample.label = "Area", taxa.label = "Genus")

plot_heatmap(GPN1, sample.label = "Area", taxa.label = "Genus", title="0-10 cm Depth")

plot_heatmap(GPN2, sample.label = "Area", taxa.label = "Genus", title="10-20 cm Depth")

There is clearly something going on in the 0-10 cm depth microbial community. It is odd that one of the mound replications contained an abundance in taxa that the other samples did not have.

In order to seperate the fungi by functional guilds, we will call in the whole FUNGuild database through the following code:

parse_funguild <- function(url = 'http://www.stbates.org/funguild_db.php', tax_name = TRUE){
  # require(XML)
  # require(jsonlite)
  # require(RCurl)
  ## Parse data
  tmp <- XML::htmlParse(url)
  tmp <- XML::xpathSApply(doc = tmp, path = "//body", fun = XML::xmlValue)
  ## Read url and convert to data.frame
  db <- jsonlite::fromJSON(txt=tmp)
  ## Remove IDs
  db$`_id` <- NULL
  if(tax_name == TRUE){
    ## Code legend
    ## Taxon Level: A numeral corresponding the correct taxonomic level for the taxon
    taxons <- c(
      "keyword",                                                       # 0
      "Phylum", "Subphylum", "Class", "Subclass", "Order", "Suborder", # 3:8
      "Family", "Subfamily", "Tribe", "Subtribe", "Genus",             # 9:13
      "Subgenus", "Section", "Subsection", "Series", "Subseries",      # 15:19
      "Species", "Subspecies", "Variety", "Subvariety", "Form",        # 20:24
      "Subform", "Form Species")
    ## Table with coding
    taxmatch <- data.frame(
      TaxID = c(0, 3:13, 15:26),
      Taxon = factor(taxons, levels = taxons))
    ## Match taxon codes
    db$taxonomicLevel <- taxmatch[match(x = db$taxonomicLevel, table = taxmatch$TaxID), "Taxon"]
  }
  # remove rows with missing data
  # which(
  #     with(db, trophicMode == "NULL" & guild == "NULL" & growthForm == "NULL" & trait == "NULL" & notes == "NULL")
  #     )
  ## Add database dump date as attributes to the result
  attr(db, "DownloadDate") <- date()
  return(db)
}
FUNGuild <- parse_funguild()
FUNGuild

COMING SOON: Using the FUNGuild database to apply functional guild distinctions to OTU tables in phyloseq.

Currently combining files outside of R:

write.table(FUNGuild, “C:/Users/faysmith/Desktop/R metagenomics output/fun_table.txt”, sep=“”) write.table(ps_Genus, “C:/Users/faysmith/Desktop/R metagenomics output/genus_table.txt”, sep=“”)

LS0tDQp0aXRsZTogIkdyYXBoaWNhbCBBbmFseXNpcyBvZiBBbXBsaWNvbiBNZXRhZ2Vub21pY3MgRGF0YSINCmF1dGhvcjogIlMuIEZheWUgU21pdGgiDQpkYXRlOiAiSnVseSAxMiwgMjAxOCINCg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpKdXN0IHNvbWUgaG91c2VrZWVwaW5nDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCiNMb2FkIGxpYnJhcmllcyBmb3IgUlN0dWRpbw0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KDQpzb3VyY2UoImh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9iaW9jTGl0ZS5SIikNCmJpb2NMaXRlKCkNCg0KYmlvY0xpdGUoIm1ldGFnZW5vbWVTZXEiKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh2ZWdhbikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHNjYWxlcykNCmxpYnJhcnkoZ3JpZCkNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KHBoeWxvc2VxKQ0KbGlicmFyeShtZXRhZ2Vub21lU2VxKQ0KDQojIyBGb2xsb3dpbmcgaXMgb3B0aW9uYWwgZm9yIGludGVyYWN0aXZlICJzaGlueSIgZ3JhcGhzDQoNCmxpYnJhcnkoc2hpbnkpDQoNCg0KYGBgDQoNCiNPcGVuIHRoZSBwaHlsb3NlcSBvYmplY3QNCg0KYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KcHMgPC0gcmVhZFJEUygiQzovVXNlcnMvZmF5c21pdGgvRGVza3RvcC9zZXEvcHMucmRzIikNCnBzDQpgYGANCg0KI0ZpbHRlciBvdXQgZXZlcnl0aGluZyBidXQgdGhlIGtpbmdkb20gb2YgZnVuZ2ksIHNpbmNlIGl0IHdhcyBub3Qgb3VyIGludGVudCB0byBhbXBsaWZ5IGFueXRoaW5nIGVsc2UNCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcHNfc3ViIDwtIHN1YnNldF90YXhhKHBzLCBLaW5nZG9tID09ICJrX19GdW5naSIpDQpwc19zdWINCmBgYA0KTG9va3MgbGlrZSB3ZSBvbmx5IGxvc3QgNiB0YXhhIGZyb20gdGhlIE9UVSB0YWJsZSwgbWVhbmluZyBvdXIgYW1wbGlmaWNhdGlvbiBzZWVtcyB0byBoYXZlIHdvcmtlZCB3ZWxsIHRvIG9ubHkgdGFyZ2V0IGZ1bmdpLg0KDQojIE5vcm1hbGl6ZSB0aGUgT1RVcyB0byBhY2NvdW50IGZvciBzYW1wbGluZy9zZXF1ZW5jZSBkaWZmZXJlbmNlcw0KVGhlcmUgaXMgc29tZSByYW5kb21pemF0aW9uIGF0IHdvcmsgaGVyZSwgc28gYmV0dGVyIHNldCBzb21lIHNlZWQgKHNvIHRoZSByYW5kb21pemVkIG51bWJlcnMgYXJlIHJlcHJvZHVjYWJsZSkNCnJhcmVmeV9ldmVuX2RlcHRoIGlzIGEgZnVuY3Rpb24gdGhhdCB3aWxsIHJhbmRvbWx5IHNhbXBsZSBPVFVzIGZyb20gZWFjaCBncm91cCB1bnRpbCBpdCByZWFjaGVzIHRoZSBzYW1lIG51bWJlciBvZiBPVFVzIHRoYXQgaXMgaW4gdGhlIHNtYWxsZXN0IHNhbXBsZSBzaXplLiBUaGVyZSBhcmUgc29tZSB0aGF0IGNsYWltIHRoYXQgcmFyZWZhY3Rpb24gc2hvdWxkIG5vdCBiZSB1c2VkIG9uIHRoaXMga2luZCBvZiBkYXRhLCBidXQgZXZlcnlvbmUgZG9lcyBpdCBhbnl3YXkuIFNvbWUgZmFuY2llciBtZXRob2RzIG9mIG5vcm1hbGl6aW5nIHRoZSBkYXRhIGV4aXN0LCBidXQgdGhleSBhcmUgbW9yZSBjb21wbGljYXRlZCBhbmQgbm90IGFzIHdlbGwgcHVibGlzaGVkIG9uLiANCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc2V0LnNlZWQoMTAwKQ0KI1JhbmRvbWx5IHNhbXBsZSB0byB0aGUgbG93ZXN0IGRlbm9taW5hdGlvbg0KcHNfc3ViIDwtIHJhcmVmeV9ldmVuX2RlcHRoKHBzX3N1Yiwgcm5nc2VlZCA9IFRSVUUpDQpwc19zdWINCmBgYA0KV2Ugd2VudCBmcm9tIDU0OSBPVFVzIGluIG91ciBkYXRhc2V0IHRvIDUwMyBhZnRlciBub3JtYWxpemluZyBmb3Igc2FtcGxlIHNpemUuDQoNCiAjT3B0aW9uYWw6IExpbWl0IGFsbCBhbmFseXNlcyB0byB0aGUgdG9wIDEwIG1vc3QgYWJ1bmRhbnQgdGF4YQ0KIEknbSBub3QgZG9pbmcgdGhpcyBvbmUganVzdCB5ZXQsIGJ1dCBpdCBjYW4gbWFrZSBhIGZpcnN0LWxvb2sgYW5seXNlcyBhIGxpdHRsZSBlYXNpZXIgdG8gdW5kZXJzdGFuZC4NCiANCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIFRvcE5PVFVzID0gbmFtZXMoc29ydCh0YXhhX3N1bXMocHNfc3ViKSwgVFJVRSlbMTo1MF0pDQojIHBzXzEwIDwtIHBydW5lX3RheGEoVG9wTk9UVXMsIHBzX3N1YikNCiMgcGxvdF9iYXIocHNfMTAsICJEZXB0aCIsIGZpbGwgPSAiR2VudXMiLCBmYWNldF9ncmlkID0gfkFyZWEpDQojIA0KIyBwc19zdWIgPC0gcHNfMTANCmBgYA0KDQojU3RhY2tlZCBiYXJwbG90IGJ5IHRheGENClBsb3QgYSBzdGFja2VkIGJhcmNoYXJ0IHRoYXQgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0YXhhIChhdCBhbGwgbGV2ZWxzKS4gRXh0cmVtZWx5IHNtYWxsIGdyb3VwcyAoPDIlKSBhcmUgcmVtb3ZlZCBmb3Igc2ltcGxpZmljYXRpb24gYW5kIGFsbCBncm91cHMgYXJlIHBsb3R0ZWQgYXMgYSAlIG9mIHRoZSB0b3RhbCB0YXhhIGluIGVhY2ggc2FtcGxlLiANCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI1Bsb3QgdGhlIFBoeWx1bSBjb21wb3NpdGlvbiBvZiBTb2lsIEZ1bmdpDQpwc19waHlsdW0gPC0gcHNfc3ViICU+JQ0KICB0YXhfZ2xvbSh0YXhyYW5rID0gIlBoeWx1bSIpICU+JSAgICAgICAgICAgICAgICAgICAgICMgYWdnbG9tZXJhdGUgYXQgcGh5bHVtIGxldmVsDQogIHRyYW5zZm9ybV9zYW1wbGVfY291bnRzKGZ1bmN0aW9uKHgpIHt4L3N1bSh4KX0gKSAlPiUgIyBUcmFuc2Zvcm0gdG8gcmVsLiBhYnVuZGFuY2UNCiAgcHNtZWx0KCkgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1lbHQgdG8gbG9uZyBmb3JtYXQNCiAgZmlsdGVyKEFidW5kYW5jZSA+IDAuMDIpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAjIEZpbHRlciBvdXQgbG93IGFidW5kYW5jZSB0YXhhDQogIGFycmFuZ2UoUGh5bHVtKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTb3J0IGRhdGEgZnJhbWUgYWxwaGFiZXRpY2FsbHkgYnkgcGh5bHVtDQoNCmdncGxvdChwc19waHlsdW0sIGFlcyh4ID0gRGVwdGgsIHkgPSBBYnVuZGFuY2UsIGZpbGwgPSBQaHlsdW0pKSArIA0KICBmYWNldF9ncmlkKEFyZWF+UmVwbGljYXRpb24pICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiMC0xMCBjbSIsICIxMC0yMCBjbSIpKSsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUsIGtleXdpZHRoID0gMSwga2V5aGVpZ2h0ID0gMSkpICsNCiAgeWxhYigiUmVsYXRpdmUgQWJ1bmRhbmNlIChQaHlsYSA+IDIlKSBcbiIpICsNCiAgZ2d0aXRsZSgiUGh5bHVtIENvbXBvc2l0aW9uIG9mIFNvaWwgRnVuZ2kgXG4gRnVuZ2FsIENvbW11bml0aWVzIGJ5IFNhbXBsaW5nIFNpdGUgYW5kIERlcHRoIikgDQoNCiNQbG90IHRoZSBDbGFzcyBjb21wb3NpdGlvbiBvZiBTb2lsIEZ1bmdpDQpwc19DbGFzcyA8LSBwc19zdWIgJT4lDQogIHRheF9nbG9tKHRheHJhbmsgPSAiQ2xhc3MiKSAlPiUgICAgICAgICAgICAgICAgICAgICAjIGFnZ2xvbWVyYXRlIGF0IENsYXNzIGxldmVsDQogIHRyYW5zZm9ybV9zYW1wbGVfY291bnRzKGZ1bmN0aW9uKHgpIHt4L3N1bSh4KX0gKSAlPiUgIyBUcmFuc2Zvcm0gdG8gcmVsLiBhYnVuZGFuY2UNCiAgcHNtZWx0KCkgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1lbHQgdG8gbG9uZyBmb3JtYXQNCiAgZmlsdGVyKEFidW5kYW5jZSA+IDAuMDIpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAjIEZpbHRlciBvdXQgbG93IGFidW5kYW5jZSB0YXhhDQogIGFycmFuZ2UoQ2xhc3MpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFNvcnQgZGF0YSBmcmFtZSBhbHBoYWJldGljYWxseSBieSBDbGFzcw0KDQpnZ3Bsb3QocHNfQ2xhc3MsIGFlcyh4ID0gRGVwdGgsIHkgPSBBYnVuZGFuY2UsIGZpbGwgPSBDbGFzcykpICsgDQogIGZhY2V0X2dyaWQoQXJlYX5SZXBsaWNhdGlvbikgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKCIwLTEwIGNtIiwgIjEwLTIwIGNtIikpKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSwga2V5d2lkdGggPSAxLCBrZXloZWlnaHQgPSAxKSkgKw0KICB5bGFiKCJSZWxhdGl2ZSBBYnVuZGFuY2UgKENsYXNzID4gMiUpIFxuIikgKw0KICBnZ3RpdGxlKCJDbGFzcyBDb21wb3NpdGlvbiBvZiBTb2lsIEZ1bmdpIFxuIEZ1bmdhbCBDb21tdW5pdGllcyBieSBTYW1wbGluZyBTaXRlIGFuZCBEZXB0aCIpIA0KDQojUGxvdCB0aGUgT3JkZXIgY29tcG9zaXRpb24gb2YgU29pbCBGdW5naQ0KcHNfT3JkZXIgPC0gcHNfc3ViICU+JQ0KICB0YXhfZ2xvbSh0YXhyYW5rID0gIk9yZGVyIikgJT4lICAgICAgICAgICAgICAgICAgICAgIyBhZ2dsb21lcmF0ZSBhdCBPcmRlciBsZXZlbA0KICB0cmFuc2Zvcm1fc2FtcGxlX2NvdW50cyhmdW5jdGlvbih4KSB7eC9zdW0oeCl9ICkgJT4lICMgVHJhbnNmb3JtIHRvIHJlbC4gYWJ1bmRhbmNlDQogIHBzbWVsdCgpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZWx0IHRvIGxvbmcgZm9ybWF0DQogIGZpbHRlcihBYnVuZGFuY2UgPiAwLjAyKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgIyBGaWx0ZXIgb3V0IGxvdyBhYnVuZGFuY2UgdGF4YQ0KICBhcnJhbmdlKE9yZGVyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTb3J0IGRhdGEgZnJhbWUgYWxwaGFiZXRpY2FsbHkgYnkgT3JkZXINCg0KZ2dwbG90KHBzX09yZGVyLCBhZXMoeCA9IERlcHRoLCB5ID0gQWJ1bmRhbmNlLCBmaWxsID0gT3JkZXIpKSArIA0KICBmYWNldF9ncmlkKEFyZWF+UmVwbGljYXRpb24pICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiMC0xMCBjbSIsICIxMC0yMCBjbSIpKSsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUsIGtleXdpZHRoID0gMSwga2V5aGVpZ2h0ID0gMSkpICsNCiAgeWxhYigiUmVsYXRpdmUgQWJ1bmRhbmNlIChPcmRlciA+IDIlKSBcbiIpICsNCiAgZ2d0aXRsZSgiT3JkZXIgQ29tcG9zaXRpb24gb2YgU29pbCBGdW5naSBcbiBGdW5nYWwgQ29tbXVuaXRpZXMgYnkgU2FtcGxpbmcgU2l0ZSBhbmQgRGVwdGgiKQ0KDQojUGxvdCB0aGUgRmFtaWx5IGNvbXBvc2l0aW9uIG9mIFNvaWwgRnVuZ2kNCnBzX0ZhbWlseSA8LSBwc19zdWIgJT4lDQogIHRheF9nbG9tKHRheHJhbmsgPSAiRmFtaWx5IikgJT4lICAgICAgICAgICAgICAgICAgICAgIyBhZ2dsb21lcmF0ZSBhdCBGYW1pbHkgbGV2ZWwNCiAgdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMoZnVuY3Rpb24oeCkge3gvc3VtKHgpfSApICU+JSAjIFRyYW5zZm9ybSB0byByZWwuIGFidW5kYW5jZQ0KICBwc21lbHQoKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVsdCB0byBsb25nIGZvcm1hdA0KICBmaWx0ZXIoQWJ1bmRhbmNlID4gMC4wMikgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICMgRmlsdGVyIG91dCBsb3cgYWJ1bmRhbmNlIHRheGENCiAgYXJyYW5nZShGYW1pbHkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFNvcnQgZGF0YSBmcmFtZSBhbHBoYWJldGljYWxseSBieSBGYW1pbHkNCg0KZ2dwbG90KHBzX0ZhbWlseSwgYWVzKHggPSBEZXB0aCwgeSA9IEFidW5kYW5jZSwgZmlsbCA9IEZhbWlseSkpICsgDQogIGZhY2V0X2dyaWQoQXJlYX5SZXBsaWNhdGlvbikgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKCIwLTEwIGNtIiwgIjEwLTIwIGNtIikpKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSwga2V5d2lkdGggPSAxLCBrZXloZWlnaHQgPSAxKSkgKw0KICB5bGFiKCJSZWxhdGl2ZSBBYnVuZGFuY2UgKEZhbWlseSA+IDIlKSBcbiIpICsNCiAgZ2d0aXRsZSgiRmFtaWx5IENvbXBvc2l0aW9uIG9mIFNvaWwgRnVuZ2kgXG4gRnVuZ2FsIENvbW11bml0aWVzIGJ5IFNhbXBsaW5nIFNpdGUgYW5kIERlcHRoIikNCg0KI1Bsb3QgdGhlIEdlbnVzIGNvbXBvc2l0aW9uIG9mIFNvaWwgRnVuZ2kNCnBzX0dlbnVzIDwtIHBzX3N1YiAlPiUNCiAgdGF4X2dsb20odGF4cmFuayA9ICJHZW51cyIpICU+JSAgICAgICAgICAgICAgICAgICAgICMgYWdnbG9tZXJhdGUgYXQgR2VudXMgbGV2ZWwNCiAgdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMoZnVuY3Rpb24oeCkge3gvc3VtKHgpfSApICU+JSAjIFRyYW5zZm9ybSB0byByZWwuIGFidW5kYW5jZQ0KICBwc21lbHQoKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVsdCB0byBsb25nIGZvcm1hdA0KICBmaWx0ZXIoQWJ1bmRhbmNlID4gMC4wMikgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICMgRmlsdGVyIG91dCBsb3cgYWJ1bmRhbmNlIHRheGENCiAgYXJyYW5nZShHZW51cykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU29ydCBkYXRhIGZyYW1lIGFscGhhYmV0aWNhbGx5IGJ5IEdlbnVzDQoNCmdncGxvdChwc19HZW51cywgYWVzKHggPSBEZXB0aCwgeSA9IEFidW5kYW5jZSwgZmlsbCA9IEdlbnVzKSkgKyANCiAgZmFjZXRfZ3JpZChBcmVhflJlcGxpY2F0aW9uKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIjAtMTAgY20iLCAiMTAtMjAgY20iKSkrDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFLCBrZXl3aWR0aCA9IDEsIGtleWhlaWdodCA9IDEpKSArDQogIHlsYWIoIlJlbGF0aXZlIEFidW5kYW5jZSAoR2VudXMgPiAyJSkgXG4iKSArDQogIGdndGl0bGUoIkdlbnVzIENvbXBvc2l0aW9uIG9mIFNvaWwgRnVuZ2kgXG4gRnVuZ2FsIENvbW11bml0aWVzIGJ5IFNhbXBsaW5nIFNpdGUgYW5kIERlcHRoIikNCg0KYGBgDQoNClZpc3VhbGx5LCBpdCBzZWVtcyB0aGF0IHRoZXJlIGFyZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBzYW1wbGVzLCBldmVuIHRoZSBvbmVzIGNvbGxlY3RlZCBmcm9tIHRoZSBzYW1lIGFyZWEuIEl0IG1pZ2h0IGJlIHdvcnRoIGV4cGxvcmluZyB3aXRoaW4gZWFjaCBjbGFzcyByZXByZXNlbnRlZCBpbiB0aGlzIGJhcmdyYXBoIHRvIG1vcmUgY2xlYXJseSBzZWUgd2hhdCBkaWZmZXJlbmNlcyBpbiBnZW51cyBhYnVuZGFuY2UgdGhlcmUgYXJlIGFtb25nIHRoZSBzYW1wbGVzLiBJdCB3b3VsZCBhbHNvIGJlIGdyZWF0IHRvIHNlcGVyYXRlIHRoZXNlIGludG8gZnVuY3Rpb25hbCBncm91cHMsIHNvbWV0aGluZyB0aGF0IHNlZW1zIHBvc3NpYmxlIGJ1dCBhIG5ldyBwcm9ncmFtIG1heSBoYXZlIHRvIGJlIHdyaXR0ZW4gZm9yIGl0IHRvIGhhcHBlbi4gDQoNCg0KI05NRFMgb2YgQnJheS1DdXJ0aXMgRGlzdGFuY2VzDQpUaGlzIGlzIGEgbm9uLW1ldHJpYyBtdWx0aWRpbWVuc2lvbmFsIHNjYWxpbmcgcGxvdCB0aGF0IHVzZXMgdGhlIGRpc3NpbWlsYXJpdGllcyBhbW9uZyBlYWNoIHNhbXBsZSBkYXRhIHNldCB0byBtYXAgb3V0IGRpc3RhbmNlcy4gU2FtcGxlcyBhcmUgYW5hbHl6ZWQgYXMgYSBzdW0gb2YgYWxsIHRoZSBzZXF1ZW5jZXMgd2l0aGluIGFuZCBUYXhhIGFyZSBzY2FsZWQgc2VwZXJhdGx5IGJ5IHVuaXF1ZSBzZXF1ZW5jZXMuIA0KDQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcHMub3JkIDwtIG9yZGluYXRlKHBzX3N1YiwgIk5NRFMiLCAiYnJheSIpDQpwbG90X29yZGluYXRpb24ocHNfc3ViLCBwcy5vcmQsIHR5cGU9InNwbGl0IiwgY29sb3I9IkNsYXNzIiwgc2hhcGU9IkFyZWEiLCBsYWJlbD0iRGVwdGgiKQ0KYGBgDQoNClRoaXMgb3JkaW5hdGlvbiBwbG90IHNlZW1zIHRvIGluZGljYXRlIGNsdXN0ZXJpbmcgb2Ygc2FtcGxlcyBhY2NvcmRpbmcgdG8gdGhlIGFyZWEgZnJvbSB3aGljaCB0aGV5IHdlcmUgdGFrZW4uIFRoZXJlIGFsc28gbWlnaHQgYmUgc29tZSBjbHVzdGVyaW5nIG9mIHRheGEgd2l0aGluIGNsYXNzZXMgYXJvdW5kIHRoZXNlIHBvaW50cywgaW5kaWNhdGluZyBhIHBvc3NpYmxlIGRpZmZlcmVuY2UgaW4gY29tbXVuaXR5IHN0cnVjdHVyZSBiZXR3ZWVuIG1vdW5kcywgZGVwcmVzc2lvbnMsIGFuZCB0aGUgYWRqYWNlbnQgcGFzdHVyZS4gDQoNCiNQbG90IGFscGhhIGRpdmVyc2l0eSBsZXZlbHMNCkkgd2FudGVkIHRvIHNlZSB0aGVzZSBkb25lIGZvciB0aGUgZ3JvdXBzIG9mIHNhbXBsZSBhcmVhIGFuZCBieSBkZXB0aCAobm90IHNpbmdsZSBzYW1wbGVzKS4gVG8gY3JlYXRlIHRoaXMgYXZlcmFnZSwgSSBtZXJnZWQgdGhlIHJlcGxpY2F0aW9ucyB0b2dldGhlciB1c2luZyAibWVyZ2Vfc2FtcGxlcyIgZnVuY3Rpb24gaW4gcGh5bG9zZXEuIFRoaXMgY2F1c2VzIHRoZSBvdGhlciB2YWx1ZXMgdG8gYmUgY3VycnVwdGVkLCBzbyB0aGV5IGFyZSByZW5hbWVkIGluIHRoZSBmb2xsb3dpbmcgY29kZSBhcyBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIA0KDQpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc2FtcGxlX3ZhcmlhYmxlcyhwc19zdWIpICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICB2YXJpYWJsZTIgPSBhcy5jaGFyYWN0ZXIoZ2V0X3ZhcmlhYmxlKHBzX3N1YiwgIkRlcHRoIikpDQogICAgICAgICAgICAgICAgICB2YXJpYWJsZTMgPSBhcy5jaGFyYWN0ZXIoZ2V0X3ZhcmlhYmxlKHBzX3N1YiwgIkFyZWEiKSkNCiAgICAgICAgICAgICAgICAgIHNhbXBsZV9kYXRhKHBzX3N1YikkTmV3UGFzdGVkVmFyIDwtIG1hcHBseShwYXN0ZTAsIHZhcmlhYmxlMiwgdmFyaWFibGUzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiXyIpDQpwc19tZXJnID0gbWVyZ2Vfc2FtcGxlcyhwc19zdWIsICJOZXdQYXN0ZWRWYXIiKQ0Kc2FtcGxlX2RhdGEocHNfbWVyZykkRGVwdGggPC0gZmFjdG9yKGMoIjAtMTAgY20iLCIxMC0yMCBjbSIsIjAtMTAgY20iLCIxMC0yMCBjbSIsIjAtMTAgY20iLCIxMC0yMCBjbSIpKQ0Kc2FtcGxlX2RhdGEocHNfbWVyZykkQXJlYSA8LSBmYWN0b3IoYyhyZXAoIk1vdW5kIiwyKSxyZXAoIkRlcHJlc3Npb24iLDIpLHJlcCgiUGFzdHVyZSIsMikpKQ0KDQpwbG90X3JpY2huZXNzKHBzX21lcmcsIHg9IkFyZWEiLCBjb2xvcj0iRGVwdGgiLCBtZWFzdXJlcz1jKCJTaGFubm9uIiwgIlNpbXBzb24iLCAiRmlzaGVyIikpDQpgYGANCg0KSXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCwgZm9yIGVhY2ggb2YgdGhlc2UgbWVhc3VyZW1lbnRzIG9mIGRpdmVyc2l0eSwgdGhlIGxvd2VyIG51bWJlcnMgaW5kaWNhdGUgaGlnaGVyIGxldmVscyBvZiBkaXZlcnNpdHkuIEZvciBpbnN0YW5jZSwgdGhlIFNpbXBzb24gZGl2ZXJzaXR5IGluZGV4IG9mICIxIiBpbmRpY2F0ZXMgbm8gZGl2ZXJzaXR5IGFuZCBhbiBpbmRleCBvZiAiMCIgaW5kaWNhdGVzIGluZmluaXRlIGRpdmVyc2l0eS4gQ2xlYXJseSwgdGhlIHRvcCAxMCBjbSBvZiB0aGUgbW91bmQgYXJlYSBoYXMgdGhlIGhpZ2hlc3QgbGV2ZWwgb2YgZGl2ZXJzaXR5LCBhcyB0byBiZSBleHBlY3RlZC4gDQoNCg0KI1Bsb3QgdGhlIHBoeWxvZ2VuZXRpYyB0cmVlIGZvciBhbGwgc2FtcGxlcw0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCk4gPSA1MA0KR1BOID0gcHJ1bmVfdGF4YShuYW1lcyhzb3J0KHRheGFfc3Vtcyhwc19zdWIpLCBkZWNyZWFzaW5nID0gVFJVRSlbMTpOXSksIA0KICAgIHBzX3N1YikNCg0KcCA8LSBwbG90X3RyZWUoR1BOLCBub2RlbGFiZiA9IG5vZGVwbG90YmxhbmssIGxhZGRlcml6ZT0ibGVmdCIsIGNvbG9yPSJBcmVhIiwgc2hhcGUgPSAiRGVwdGgiLCBsYWJlbC50aXBzID0gIkdlbnVzIiwgdGV4dC5zaXplID0gMi41KQ0KIyBwICsgY29vcmRfcG9sYXIodGhldGEgPSAieSIpICNPcHR0aW9uIGZvciBkb2luZyBhIHJhZGlhbCBncmFwDQpwDQoNCmBgYA0KVGhlcmUgYXJlIGNsZWFyIGdyb3VwcyB0aGF0IGZhbGwgd2l0aGluIHRoZSBzYW1wbGVzIGluIHRoZSAiZGVwcmVzc2lvbiIgYXJlYSwgYWx0aG91Z2ggdGhleSBhcmUgIk5BIiBmb3IgZ2VudXMsIG1lYW5pbmcgdGhhdCB0aGV5IGFyZSBpZGVudGlmaWVkIGFzIGZ1bmdpLCBidXQgZG8gbm90IGhhdmUgYSBjbGVhciBlbm91Z2ggbWF0Y2ggdG8gZGVzY3JpYmUgdGhlbSBmdXJ0aGVyIHRoYW4gUGh5bHVtIGluIHNvbWUgY2FzZXMuIEludGVyZXN0aW5nIHRoYXQgdGhlIE5BIHNlZW1zIHRvIGJlIG1vcmUgY29tbW9uIGluIHRoZSAiRGVwcmVzc2lvbiIiIGFyZWFzLiANCg0KDQojUGxvdCBoZWF0bWFwDQpTaG93cyB0aGUgcHJldmFsZW5jZSBvZiBPVFVzIGZvciBlYWNoIHNhbXBsZSB0aW1lDQoNCmBgYHtyIGVjaG89VFJVRSwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpOID0gMjAwDQpHUE4gPSBwcnVuZV90YXhhKG5hbWVzKHNvcnQodGF4YV9zdW1zKHBzX3N1YiksIGRlY3JlYXNpbmcgPSBUUlVFKVsxOk5dKSwgDQogICAgcHNfc3ViKQ0KR1BOMSA9IHN1YnNldF9zYW1wbGVzKHBzX3N1YiwgRGVwdGg9PSIxIikNCkdQTjEgPSBwcnVuZV90YXhhKG5hbWVzKHNvcnQodGF4YV9zdW1zKEdQTjEpLCBkZWNyZWFzaW5nID0gVFJVRSlbMTpOXSksIA0KICAgIEdQTjEpDQpHUE4yID0gc3Vic2V0X3NhbXBsZXMoR1BOLCBEZXB0aD09IjIiKQ0KR1BOMiA9IHBydW5lX3RheGEobmFtZXMoc29ydCh0YXhhX3N1bXMoR1BOMiksIGRlY3JlYXNpbmcgPSBUUlVFKVsxOk5dKSwgDQogICAgR1BOMikNCg0KcGxvdF9oZWF0bWFwKEdQTiwgc2FtcGxlLmxhYmVsID0gIkFyZWEiLCB0YXhhLmxhYmVsID0gIkdlbnVzIikNCnBsb3RfaGVhdG1hcChHUE4xLCBzYW1wbGUubGFiZWwgPSAiQXJlYSIsIHRheGEubGFiZWwgPSAiR2VudXMiLCB0aXRsZT0iMC0xMCBjbSBEZXB0aCIpDQpwbG90X2hlYXRtYXAoR1BOMiwgc2FtcGxlLmxhYmVsID0gIkFyZWEiLCB0YXhhLmxhYmVsID0gIkdlbnVzIiwgdGl0bGU9IjEwLTIwIGNtIERlcHRoIikNCg0KYGBgDQoNClRoZXJlIGlzIGNsZWFybHkgc29tZXRoaW5nIGdvaW5nIG9uIGluIHRoZSAwLTEwIGNtIGRlcHRoIG1pY3JvYmlhbCBjb21tdW5pdHkuIEl0IGlzIG9kZCB0aGF0IG9uZSBvZiB0aGUgbW91bmQgcmVwbGljYXRpb25zIGNvbnRhaW5lZCBhbiBhYnVuZGFuY2UgaW4gdGF4YSB0aGF0IHRoZSBvdGhlciBzYW1wbGVzIGRpZCBub3QgaGF2ZS4gDQoNCkluIG9yZGVyIHRvIHNlcGVyYXRlIHRoZSBmdW5naSBieSBmdW5jdGlvbmFsIGd1aWxkcywgd2Ugd2lsbCBjYWxsIGluIHRoZSB3aG9sZSBGVU5HdWlsZCBkYXRhYmFzZSB0aHJvdWdoIHRoZSBmb2xsb3dpbmcgY29kZToNCg0KYGBge3J9DQpwYXJzZV9mdW5ndWlsZCA8LSBmdW5jdGlvbih1cmwgPSAnaHR0cDovL3d3dy5zdGJhdGVzLm9yZy9mdW5ndWlsZF9kYi5waHAnLCB0YXhfbmFtZSA9IFRSVUUpew0KDQogICMgcmVxdWlyZShYTUwpDQogICMgcmVxdWlyZShqc29ubGl0ZSkNCiAgIyByZXF1aXJlKFJDdXJsKQ0KDQogICMjIFBhcnNlIGRhdGENCiAgdG1wIDwtIFhNTDo6aHRtbFBhcnNlKHVybCkNCiAgdG1wIDwtIFhNTDo6eHBhdGhTQXBwbHkoZG9jID0gdG1wLCBwYXRoID0gIi8vYm9keSIsIGZ1biA9IFhNTDo6eG1sVmFsdWUpDQoNCiAgIyMgUmVhZCB1cmwgYW5kIGNvbnZlcnQgdG8gZGF0YS5mcmFtZQ0KICBkYiA8LSBqc29ubGl0ZTo6ZnJvbUpTT04odHh0PXRtcCkNCg0KICAjIyBSZW1vdmUgSURzDQogIGRiJGBfaWRgIDwtIE5VTEwNCg0KICBpZih0YXhfbmFtZSA9PSBUUlVFKXsNCg0KICAgICMjIENvZGUgbGVnZW5kDQogICAgIyMgVGF4b24gTGV2ZWw6IEEgbnVtZXJhbCBjb3JyZXNwb25kaW5nIHRoZSBjb3JyZWN0IHRheG9ub21pYyBsZXZlbCBmb3IgdGhlIHRheG9uDQogICAgdGF4b25zIDwtIGMoDQogICAgICAia2V5d29yZCIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgMA0KICAgICAgIlBoeWx1bSIsICJTdWJwaHlsdW0iLCAiQ2xhc3MiLCAiU3ViY2xhc3MiLCAiT3JkZXIiLCAiU3Vib3JkZXIiLCAjIDM6OA0KICAgICAgIkZhbWlseSIsICJTdWJmYW1pbHkiLCAiVHJpYmUiLCAiU3VidHJpYmUiLCAiR2VudXMiLCAgICAgICAgICAgICAjIDk6MTMNCiAgICAgICJTdWJnZW51cyIsICJTZWN0aW9uIiwgIlN1YnNlY3Rpb24iLCAiU2VyaWVzIiwgIlN1YnNlcmllcyIsICAgICAgIyAxNToxOQ0KICAgICAgIlNwZWNpZXMiLCAiU3Vic3BlY2llcyIsICJWYXJpZXR5IiwgIlN1YnZhcmlldHkiLCAiRm9ybSIsICAgICAgICAjIDIwOjI0DQogICAgICAiU3ViZm9ybSIsICJGb3JtIFNwZWNpZXMiKQ0KDQogICAgIyMgVGFibGUgd2l0aCBjb2RpbmcNCiAgICB0YXhtYXRjaCA8LSBkYXRhLmZyYW1lKA0KICAgICAgVGF4SUQgPSBjKDAsIDM6MTMsIDE1OjI2KSwNCiAgICAgIFRheG9uID0gZmFjdG9yKHRheG9ucywgbGV2ZWxzID0gdGF4b25zKSkNCg0KICAgICMjIE1hdGNoIHRheG9uIGNvZGVzDQogICAgZGIkdGF4b25vbWljTGV2ZWwgPC0gdGF4bWF0Y2hbbWF0Y2goeCA9IGRiJHRheG9ub21pY0xldmVsLCB0YWJsZSA9IHRheG1hdGNoJFRheElEKSwgIlRheG9uIl0NCiAgfQ0KDQogICMgcmVtb3ZlIHJvd3Mgd2l0aCBtaXNzaW5nIGRhdGENCiAgIyB3aGljaCgNCiAgIyAJd2l0aChkYiwgdHJvcGhpY01vZGUgPT0gIk5VTEwiICYgZ3VpbGQgPT0gIk5VTEwiICYgZ3Jvd3RoRm9ybSA9PSAiTlVMTCIgJiB0cmFpdCA9PSAiTlVMTCIgJiBub3RlcyA9PSAiTlVMTCIpDQogICMgCSkNCg0KICAjIyBBZGQgZGF0YWJhc2UgZHVtcCBkYXRlIGFzIGF0dHJpYnV0ZXMgdG8gdGhlIHJlc3VsdA0KICBhdHRyKGRiLCAiRG93bmxvYWREYXRlIikgPC0gZGF0ZSgpDQoNCiAgcmV0dXJuKGRiKQ0KfQ0KDQpGVU5HdWlsZCA8LSBwYXJzZV9mdW5ndWlsZCgpDQpGVU5HdWlsZA0KYGBgDQoNCg0KDQoNCg0KDQpDT01JTkcgU09PTjogDQpVc2luZyAgdGhlIEZVTkd1aWxkIGRhdGFiYXNlIHRvIGFwcGx5IGZ1bmN0aW9uYWwgZ3VpbGQgZGlzdGluY3Rpb25zIHRvIE9UVSB0YWJsZXMgaW4gcGh5bG9zZXEuICANCg0KQ3VycmVudGx5IGNvbWJpbmluZyBmaWxlcyBvdXRzaWRlIG9mIFI6DQoNCndyaXRlLnRhYmxlKEZVTkd1aWxkLCAiQzovVXNlcnMvZmF5c21pdGgvRGVza3RvcC9SIG1ldGFnZW5vbWljcyBvdXRwdXQvZnVuX3RhYmxlLnR4dCIsIHNlcD0iXHQiKQ0Kd3JpdGUudGFibGUocHNfR2VudXMsICJDOi9Vc2Vycy9mYXlzbWl0aC9EZXNrdG9wL1IgbWV0YWdlbm9taWNzIG91dHB1dC9nZW51c190YWJsZS50eHQiLCBzZXA9Ilx0Iik=