By Angus Ball

This tutorial is based off of the information provided here: https://github.com/stefpeschel/NetCoMi

install the packages! Note you may have to install git for this to work: https://git-scm.com/downloads

devtools::install_github("zdk123/SpiecEasi")
devtools::install_github("GraceYoon/SPRING")

devtools::install_github("stefpeschel/NetCoMi", 
                         dependencies = c("Depends", "Imports", "LinkingTo"),
                         repos = c("https://cloud.r-project.org/",
                                   BiocManager::repositories()))

Load libraries

library(NetCoMi)
Loading required package: SpiecEasi
library(phyloseq)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(ggplot2)

For my dataset i want to construct two networks and compare them one for the inoculcated trials and one for the uninoculated trials. I’m also going to remove all the primary biosolids samples so they don’t screw up my analysis (This is an experimental design choice). NetCoMi can take phyloseq objects as inputs so this is pretty easy.

physeq_Key <- readRDS(file = "C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\Bioinformatics\\ULTRA\\physeq_Key_unmerged.rds") #load physeq object
physeq_Key <- subset_samples(physeq_Key, Amd != "Primary sludge and biosolids mixture") #take the inoculated samples
#then ill conglomerate taxa to genus level
physeq_Key <- tax_glom(physeq_Key, taxrank = "Genus")

#then I'll remove all the taxa with zero abundance
otu_table_physeq_Key <- otu_table(physeq_Key) #steals the OTU table from the phyloseq object
otu_table_physeq_Key<-as.data.frame(otu_table_physeq_Key) #converts it to a dataframe

otu_table_physeq_Key$Sum <- rowSums(otu_table_physeq_Key[,])


otu_table_physeq_sparceless <- otu_table_physeq_Key %>% filter(Sum >20)#this removes any taxa that have less than 20 reads across all samples. yes i remove rare taxa, but this hurts our network analysis (which is already pretty inaccurate), so its a necessary evil. Why 20? because each network will be constructed with ~20 samples therefore this would imply that 50% of all samples in the network have this taxa (thats not how it works but thats how it statistically works)

otu_table_physeq_fin <- select(otu_table_physeq_sparceless, -"Sum") #removes the sum column
otu_table_physeq_fin <- as.matrix(otu_table_physeq_fin)
OTU = otu_table(otu_table_physeq_fin, taxa_are_rows = TRUE)

#extract taxa and sample data tables
TAX <- tax_table(physeq_Key)
SAM <- sample_data(physeq_Key)
#merge it
physeq_sparceless_woutPB <- merge_phyloseq(TAX, OTU, SAM)
physeq_inoculated <- subset_samples(physeq_sparceless_woutPB, Inoculum == "Inoculated") #take the inoculated samples


physeq_uninoculated <- subset_samples(physeq_sparceless_woutPB, Inoculum == "Uninoculated")

also note, these groups have to have the same amount of samples to be compared. unfortunately physeq_uninoculated has one less sample, than physeq_inoculated. therefore i will remove one sample from physeq_inoculated. The sample removed is NOT a random choice, but based on my experimental design

physeq_inoculated<- subset_samples(physeq_inoculated, sample_names(physeq_inoculated) != "Tailings-01-Unfertalized-Inoculated")

save these!

saveRDS(physeq_inoculated,file = "C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\Bioinformatics\\ULTRA\\Robjects\\physeq_inoculated.rds")
saveRDS(physeq_uninoculated,file = "C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\Bioinformatics\\ULTRA\\Robjects\\physeq_uninoculated.rds")
physeq_uninoculated <- readRDS("C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\Bioinformatics\\ULTRA\\Robjects\\physeq_uninoculated.rds")
physeq_inoculated <- readRDS("C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\Bioinformatics\\ULTRA\\Robjects\\physeq_inoculated.rds")

The rest I do in my linux box for SPEED, but the program seems to run pretty fast you might be able to do it on your own computer

netcomi requires each genus to have a unique identifier, which is fair enough, this function finds any genus with a non unique name (i.e all the unlabelled genuses at “g__“) and renames them to their lowest unique identifier. so g__test1 ->“test1” f__test2, g__ ->“test2(f)” (f for family) ect

physeq_inoculated <- renameTaxa(physeq_inoculated, 
                                  pat = "<name>", 
                                  substPat = "<name>_<subst_name>(<subst_R>)",
                                  numDupli = "Genus")
physeq_uninoculated <- renameTaxa(physeq_uninoculated, 
                                  pat = "<name>", 
                                  substPat = "<name>_<subst_name>(<subst_R>)",
                                  numDupli = "Genus")

Now we run the network analysis. I’ve decided to use SPRING it seems like the chillest option out of the bunch, but you’re welcome to read all the papers and make an informed choice too ;)

(An actual less sarcastic answer to the question of “why spring” is follows):

SPRING uses the mclr transformation, its like the clr transformation but without pseudocounts! note it doesn’t do this like the beta diversity packages we’ve been using, its doesn’t care about the wibbly wobbly actual effect adding a pseudocount does to your clr transformation and just knows what hip and happening (the previous comment should make about 0 sense, which is infact more sense than a detailed explanation of the mclr transformation). See it calculates the geometric means of a sample based on all your rows with values in them, and then it orders these by rank and sets anything that was a zero before the transformation to be at the bottom. So its not a “True” transformation in the sense that it treats everything equally, but it does the transformation on the data we need AND is rank preserving on the data we don’t care about. (This is very sexy ngl)

SPRING also incorporates measures for sparsity which alot of network analysis don’t explicitly do.

The Final reason I like SPRING is the fact its a conditional dependence method. This is beneficial because correlation based methods (like SparCC) often fail to differentiate between direct and indirect associations and there are ALOT of indirect associations to be picked up on in microbial network analysis, so mitigating this mitigates false conclusions

net_inoc_spring <- netConstruct(data = physeq_inoculated,#your physeq object, if you only want to make one network, do not include data2 = ..., otherwise this is where we tell the network analysis to make two networks based on these groups
                                data2 =physeq_uninoculated, 
                                taxRank = "Genus", #tells the network analysis to use genus level taxonomic information
                           verbose = 2, #how talkative the program is
                           filtTax = "highestFreq", #this tells the program to look at the taxa with the highest frequency
                           filtTaxPar = list(highestFreq = 85), #the highest 85 taxa
                           measure = "spring", #here is where you say which measure you want spring sparcc ect
                           measurePar = list(nlambda = 50, #these measures are based on what the paper used so thats why im using them
                                             rep.num = 50,
                                             Rmethod = "approx",
                                             ncores=10), #number of computer cores to use
                           normMethod = "none", #handled by spring itseld
                           zeroMethod = "none", #handled by spring
                           sparsMethod = "none", #spring returns a sparse network already so no extra sparsification necessary
                           dissFunc = "signed",
                           seed = 123456)

hey did you get a warning that looks like this? Warning message: In Matrix::nearPD(R, corr = TRUE) : ‘nearPD()’ did not converge in 100 iterations That means your data was too scarce! The easiest way to do this is just decrease the amount of taxa you look at I.e. from 75->65 under filtTaxPar = list(highestFreq = x). This will functionally only ttake the highest abundance taxa and there for solve the sparcity issue

saveRDS(net_inoc_spring, "net_inoc_spring.rds")

Then we can analyze the networks!

props_inoc <- netAnalyze(net_inoc_spring, #your network
                           centrLCC = FALSE, #i dont understand
                           avDissIgnoreInf = TRUE, 
                           sPathNorm = FALSE,
                           clustMethod = "cluster_fast_greedy",
                           hubPar = c("degree", "eigenvector"),
                           hubQuant = 0.95,
                           lnormFit = TRUE,
                           normDeg = FALSE,
                           normBetw = FALSE,
                           normClose = FALSE,
                           normEigen = FALSE,
                           verbose = 2)

These should and can change based on your questions, but honestly I dont understand it very well, I’m using the tutorial specs

then we can plot it out

This will plot out just the networks as they want to exist individually, but this will make comparison hard!

props_inoc <- readRDS("C:\\Users\\angus\\OneDrive - UNBC\\Angus Ball\\Lab work\\Bioinformatics\\ULTRA\\Robjects\\props_inoc.rds")

plot(props_inoc, 
     layout = "spring", #shape, alternative option is circle
     sameLayout = FALSE, #same layout of both graphs?
     nodeColor = "cluster", #how to color nodes
     repulsion = 0.7, #how far apart are nodes
     nodeSize = "mclr", #node size is based off of clr transform size
     labelScale = F, #don't scale names with node size
     shortenLabels = "none",#if you want to shorten the label names, othe roption is "intelligent"
     cexNodes = 2, #size of nodes
     cexLabels = .5,#size of names
     cexHubLabels = .75,#size of hub names
     cexTitle = 2,#size of title
     groupNames = c("Inoculated", "Uninoculated"), #group names
     hubBorderCol  = "gray40")

legend(0.7, 1.1, cex = 2.2, title = "estimated association:",
       legend = c("+","-"), lty = 1, lwd = 3, col = c("#009900","red"), 
       bty = "n", horiz = TRUE)

We can instead force the networks to look the same in OTU order for easy comparison

plot(props_inoc, 
     layout = "spring", #shape, alternative option is circle
     sameLayout = TRUE, #same layout of both graphs?
     layoutGroup = "union", #combination of both layouts
     rmSingles = "inboth", #remove single taxa no associated with other taxa in both groups
     nodeColor = "cluster", #how to color nodes
     repulsion = 0.7, #how far apart are nodes
     nodeSize = "mclr", #node size is based off of clr transform size
     labelScale = F, #don't scale names with node size
     shortenLabels = "none",#if you want to shorten the label names, othe roption is "intelligent"
     cexNodes = 2, #size of nodes
     cexLabels = .5,#size of names
     cexHubLabels = .75,#size of hub names
     cexTitle = 2,#size of title
     groupNames = c("Inoculated", "Uninoculated"), #group names
     hubBorderCol  = "gray40")

legend(0.5, 1.1, cex = 2.2, title = "estimated association:",
       legend = c("+","-"), lty = 1, lwd = 3, col = c("#009900","red"), 
       bty = "n", horiz = TRUE)

For some reason ggsave doesn’t seem to want to work with these plots. But hand exporting it with the export button above the graph works so uhh just use that

explanations of the given variables is given at https://rdrr.io/github/stefpeschel/NetCoMi/man/plot.microNetProps.html

we will then use statistical tests to compare the structure of these two networks. NOTE: the p value displayed by this is NOT comparing the two networks. Its suggesting that the absolute difference between the networks is not equal to zero. take this as you will.

Note this process will take 5ever. estimated time 8-10hr

comp_inoc_orig <- netCompare(props_inoc, permTest = TRUE, nPerm = 1000, 
                          storeAssoPerm = TRUE,
                          fileStoreAssoPerm = "assoPerm_comp",
                          storeCountsPerm = FALSE,
                          cores = 1, #got an error for using multiple cores so only using one :(
                          seed = 123456,
                          adjust = "BH",
                          verbose = TRUE)
#we can explore the output in
summary(comp_inoc_orig)

Then we can see if there are differential associations within our network

diff_inoc <- diffnet(net_inoc_spring,
                       diffMethod = "fisherTest", 
                       adjust = "lfdr")
saveRDS(diff_inoc, "diff_inoc.rds")
#I didn't have any but if you do...
plot(diff_inoc, 
     cexNodes = 0.8, 
     cexLegend = 3,
     cexTitle = 4,
     mar = c(2,2,8,5),
     legendGroupnames = c("Inoculated", "Uninoculated"),
     legendPos = c(0.7,1.6))

props_inoc_pears <- netAnalyze(net_inoc_spring, 
                                 clustMethod = "cluster_fast_greedy",
                                 weightDeg = TRUE,
                                 normDeg = FALSE,
                                 gcmHeat = FALSE)
# Identify the differentially associated OTUs
diffmat_sums <- rowSums(diff_inoc$diffAdjustMat)
diff_asso_names <- names(diffmat_sums[diffmat_sums > 0])
plot(props_inoc_spring, 
     nodeFilter = "names",
     nodeFilterPar = diff_asso_names,
     nodeColor = "gray",
     highlightHubs = FALSE,
     sameLayout = TRUE, 
     layoutGroup = "union",
     rmSingles = FALSE, 
     nodeSize = "clr",
     edgeTranspHigh = 20,
     labelScale = FALSE,
     cexNodes = 1.5, 
     cexLabels = 3,
     cexTitle = 3.8,
     groupNames = c("Inoculated", "Uninoculated"),
     hubBorderCol  = "gray40")

legend(-0.15,-0.7, title = "estimated correlation:", legend = c("+","-"), 
       col = c("#009900","red"), inset = 0.05, cex = 4, lty = 1, lwd = 4, 
       bty = "n", horiz = TRUE)

And you’re done! FREE, to live another day

LS0tDQp0aXRsZTogIk5ldENvTWkiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KQnkgQW5ndXMgQmFsbA0KDQpUaGlzIHR1dG9yaWFsIGlzIGJhc2VkIG9mZiBvZiB0aGUgaW5mb3JtYXRpb24gcHJvdmlkZWQgaGVyZTogaHR0cHM6Ly9naXRodWIuY29tL3N0ZWZwZXNjaGVsL05ldENvTWkNCg0KDQoNCmluc3RhbGwgdGhlIHBhY2thZ2VzIQ0KTm90ZSB5b3UgbWF5IGhhdmUgdG8gaW5zdGFsbCBnaXQgZm9yIHRoaXMgdG8gd29yazogaHR0cHM6Ly9naXQtc2NtLmNvbS9kb3dubG9hZHMNCg0KYGBge3J9DQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInpkazEyMy9TcGllY0Vhc2kiKQ0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJHcmFjZVlvb24vU1BSSU5HIikNCg0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJzdGVmcGVzY2hlbC9OZXRDb01pIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgZGVwZW5kZW5jaWVzID0gYygiRGVwZW5kcyIsICJJbXBvcnRzIiwgIkxpbmtpbmdUbyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHJlcG9zID0gYygiaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnLyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJpb2NNYW5hZ2VyOjpyZXBvc2l0b3JpZXMoKSkpDQpgYGANCg0KDQpMb2FkIGxpYnJhcmllcw0KYGBge3J9DQpsaWJyYXJ5KE5ldENvTWkpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHBoeWxvc2VxKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmBgYA0KDQoNCg0KRm9yIG15IGRhdGFzZXQgaSB3YW50IHRvIGNvbnN0cnVjdCB0d28gbmV0d29ya3MgYW5kIGNvbXBhcmUgdGhlbSBvbmUgZm9yIHRoZSBpbm9jdWxjYXRlZCB0cmlhbHMgYW5kIG9uZSBmb3IgdGhlIHVuaW5vY3VsYXRlZCB0cmlhbHMuIEknbSBhbHNvIGdvaW5nIHRvIHJlbW92ZSBhbGwgdGhlIHByaW1hcnkgYmlvc29saWRzIHNhbXBsZXMgc28gdGhleSBkb24ndCBzY3JldyB1cCBteSBhbmFseXNpcyAoVGhpcyBpcyBhbiBleHBlcmltZW50YWwgZGVzaWduIGNob2ljZSkuIE5ldENvTWkgY2FuIHRha2UgcGh5bG9zZXEgb2JqZWN0cyBhcyBpbnB1dHMgc28gdGhpcyBpcyBwcmV0dHkgZWFzeS4NCg0KYGBge3J9DQpwaHlzZXFfS2V5IDwtIHJlYWRSRFMoZmlsZSA9ICJDOlxcVXNlcnNcXGFuZ3VzXFxPbmVEcml2ZSAtIFVOQkNcXEFuZ3VzIEJhbGxcXExhYiB3b3JrXFxCaW9pbmZvcm1hdGljc1xcVUxUUkFcXHBoeXNlcV9LZXlfdW5tZXJnZWQucmRzIikgI2xvYWQgcGh5c2VxIG9iamVjdA0KcGh5c2VxX0tleSA8LSBzdWJzZXRfc2FtcGxlcyhwaHlzZXFfS2V5LCBBbWQgIT0gIlByaW1hcnkgc2x1ZGdlIGFuZCBiaW9zb2xpZHMgbWl4dHVyZSIpICN0YWtlIHRoZSBpbm9jdWxhdGVkIHNhbXBsZXMNCiN0aGVuIGlsbCBjb25nbG9tZXJhdGUgdGF4YSB0byBnZW51cyBsZXZlbA0KcGh5c2VxX0tleSA8LSB0YXhfZ2xvbShwaHlzZXFfS2V5LCB0YXhyYW5rID0gIkdlbnVzIikNCg0KI3RoZW4gSSdsbCByZW1vdmUgYWxsIHRoZSB0YXhhIHdpdGggemVybyBhYnVuZGFuY2UNCm90dV90YWJsZV9waHlzZXFfS2V5IDwtIG90dV90YWJsZShwaHlzZXFfS2V5KSAjc3RlYWxzIHRoZSBPVFUgdGFibGUgZnJvbSB0aGUgcGh5bG9zZXEgb2JqZWN0DQpvdHVfdGFibGVfcGh5c2VxX0tleTwtYXMuZGF0YS5mcmFtZShvdHVfdGFibGVfcGh5c2VxX0tleSkgI2NvbnZlcnRzIGl0IHRvIGEgZGF0YWZyYW1lDQoNCm90dV90YWJsZV9waHlzZXFfS2V5JFN1bSA8LSByb3dTdW1zKG90dV90YWJsZV9waHlzZXFfS2V5WyxdKQ0KDQoNCm90dV90YWJsZV9waHlzZXFfc3BhcmNlbGVzcyA8LSBvdHVfdGFibGVfcGh5c2VxX0tleSAlPiUgZmlsdGVyKFN1bSA+MjApI3RoaXMgcmVtb3ZlcyBhbnkgdGF4YSB0aGF0IGhhdmUgbGVzcyB0aGFuIDIwIHJlYWRzIGFjcm9zcyBhbGwgc2FtcGxlcy4geWVzIGkgcmVtb3ZlIHJhcmUgdGF4YSwgYnV0IHRoaXMgaHVydHMgb3VyIG5ldHdvcmsgYW5hbHlzaXMgKHdoaWNoIGlzIGFscmVhZHkgcHJldHR5IGluYWNjdXJhdGUpLCBzbyBpdHMgYSBuZWNlc3NhcnkgZXZpbC4gV2h5IDIwPyBiZWNhdXNlIGVhY2ggbmV0d29yayB3aWxsIGJlIGNvbnN0cnVjdGVkIHdpdGggfjIwIHNhbXBsZXMgdGhlcmVmb3JlIHRoaXMgd291bGQgaW1wbHkgdGhhdCA1MCUgb2YgYWxsIHNhbXBsZXMgaW4gdGhlIG5ldHdvcmsgaGF2ZSB0aGlzIHRheGEgKHRoYXRzIG5vdCBob3cgaXQgd29ya3MgYnV0IHRoYXRzIGhvdyBpdCBzdGF0aXN0aWNhbGx5IHdvcmtzKQ0KDQpvdHVfdGFibGVfcGh5c2VxX2ZpbiA8LSBzZWxlY3Qob3R1X3RhYmxlX3BoeXNlcV9zcGFyY2VsZXNzLCAtIlN1bSIpICNyZW1vdmVzIHRoZSBzdW0gY29sdW1uDQpvdHVfdGFibGVfcGh5c2VxX2ZpbiA8LSBhcy5tYXRyaXgob3R1X3RhYmxlX3BoeXNlcV9maW4pDQpPVFUgPSBvdHVfdGFibGUob3R1X3RhYmxlX3BoeXNlcV9maW4sIHRheGFfYXJlX3Jvd3MgPSBUUlVFKQ0KDQojZXh0cmFjdCB0YXhhIGFuZCBzYW1wbGUgZGF0YSB0YWJsZXMNClRBWCA8LSB0YXhfdGFibGUocGh5c2VxX0tleSkNClNBTSA8LSBzYW1wbGVfZGF0YShwaHlzZXFfS2V5KQ0KI21lcmdlIGl0DQpwaHlzZXFfc3BhcmNlbGVzc193b3V0UEIgPC0gbWVyZ2VfcGh5bG9zZXEoVEFYLCBPVFUsIFNBTSkNCg0KDQpgYGANCg0KYGBge3J9DQpwaHlzZXFfaW5vY3VsYXRlZCA8LSBzdWJzZXRfc2FtcGxlcyhwaHlzZXFfc3BhcmNlbGVzc193b3V0UEIsIElub2N1bHVtID09ICJJbm9jdWxhdGVkIikgI3Rha2UgdGhlIGlub2N1bGF0ZWQgc2FtcGxlcw0KDQoNCnBoeXNlcV91bmlub2N1bGF0ZWQgPC0gc3Vic2V0X3NhbXBsZXMocGh5c2VxX3NwYXJjZWxlc3Nfd291dFBCLCBJbm9jdWx1bSA9PSAiVW5pbm9jdWxhdGVkIikNCg0KYGBgDQoNCg0KYWxzbyBub3RlLCB0aGVzZSBncm91cHMgaGF2ZSB0byBoYXZlIHRoZSBzYW1lIGFtb3VudCBvZiBzYW1wbGVzIHRvIGJlIGNvbXBhcmVkLiB1bmZvcnR1bmF0ZWx5IHBoeXNlcV91bmlub2N1bGF0ZWQgaGFzIG9uZSBsZXNzIHNhbXBsZSwgdGhhbiBwaHlzZXFfaW5vY3VsYXRlZC4gdGhlcmVmb3JlIGkgd2lsbCByZW1vdmUgb25lIHNhbXBsZSBmcm9tIHBoeXNlcV9pbm9jdWxhdGVkLiBUaGUgc2FtcGxlIHJlbW92ZWQgaXMgTk9UIGEgcmFuZG9tIGNob2ljZSwgYnV0IGJhc2VkIG9uIG15IGV4cGVyaW1lbnRhbCBkZXNpZ24NCg0KYGBge3J9DQpwaHlzZXFfaW5vY3VsYXRlZDwtIHN1YnNldF9zYW1wbGVzKHBoeXNlcV9pbm9jdWxhdGVkLCBzYW1wbGVfbmFtZXMocGh5c2VxX2lub2N1bGF0ZWQpICE9ICJUYWlsaW5ncy0wMS1VbmZlcnRhbGl6ZWQtSW5vY3VsYXRlZCIpDQpgYGANCg0KDQpzYXZlIHRoZXNlIQ0KDQpgYGB7cn0NCiNzYXZlUkRTKHBoeXNlcV9pbm9jdWxhdGVkLGZpbGUgPSAiQzpcXFVzZXJzXFxhbmd1c1xcT25lRHJpdmUgLSBVTkJDXFxBbmd1cyBCYWxsXFxMYWIgd29ya1xcQmlvaW5mb3JtYXRpY3NcXFVMVFJBXFxSb2JqZWN0c1xccGh5c2VxX2lub2N1bGF0ZWQucmRzIikNCiNzYXZlUkRTKHBoeXNlcV91bmlub2N1bGF0ZWQsZmlsZSA9ICJDOlxcVXNlcnNcXGFuZ3VzXFxPbmVEcml2ZSAtIFVOQkNcXEFuZ3VzIEJhbGxcXExhYiB3b3JrXFxCaW9pbmZvcm1hdGljc1xcVUxUUkFcXFJvYmplY3RzXFxwaHlzZXFfdW5pbm9jdWxhdGVkLnJkcyIpDQpgYGANCg0KYGBge3J9DQpwaHlzZXFfdW5pbm9jdWxhdGVkIDwtIHJlYWRSRFMoIkM6XFxVc2Vyc1xcYW5ndXNcXE9uZURyaXZlIC0gVU5CQ1xcQW5ndXMgQmFsbFxcTGFiIHdvcmtcXEJpb2luZm9ybWF0aWNzXFxVTFRSQVxcUm9iamVjdHNcXHBoeXNlcV91bmlub2N1bGF0ZWQucmRzIikNCnBoeXNlcV9pbm9jdWxhdGVkIDwtIHJlYWRSRFMoIkM6XFxVc2Vyc1xcYW5ndXNcXE9uZURyaXZlIC0gVU5CQ1xcQW5ndXMgQmFsbFxcTGFiIHdvcmtcXEJpb2luZm9ybWF0aWNzXFxVTFRSQVxcUm9iamVjdHNcXHBoeXNlcV9pbm9jdWxhdGVkLnJkcyIpDQpgYGANCg0KVGhlIHJlc3QgSSBkbyBpbiBteSBsaW51eCBib3ggZm9yIFNQRUVELCBidXQgdGhlIHByb2dyYW0gc2VlbXMgdG8gcnVuIHByZXR0eSBmYXN0IHlvdSBtaWdodCBiZSBhYmxlIHRvIGRvIGl0IG9uIHlvdXIgb3duIGNvbXB1dGVyDQoNCg0KbmV0Y29taSByZXF1aXJlcyBlYWNoIGdlbnVzIHRvIGhhdmUgYSB1bmlxdWUgaWRlbnRpZmllciwgd2hpY2ggaXMgZmFpciBlbm91Z2gsIHRoaXMgZnVuY3Rpb24gZmluZHMgYW55IGdlbnVzIHdpdGggYSBub24gdW5pcXVlIG5hbWUgKGkuZSBhbGwgdGhlIHVubGFiZWxsZWQgZ2VudXNlcyBhdCAiZ19fIikgYW5kIHJlbmFtZXMgdGhlbSB0byB0aGVpciBsb3dlc3QgdW5pcXVlIGlkZW50aWZpZXIuIA0Kc28gDQpnX190ZXN0MSAtPiJ0ZXN0MSINCmZfX3Rlc3QyLCBnX18gLT4idGVzdDIoZikiIChmIGZvciBmYW1pbHkpDQplY3QNCmBgYHtyfQ0KcGh5c2VxX2lub2N1bGF0ZWQgPC0gcmVuYW1lVGF4YShwaHlzZXFfaW5vY3VsYXRlZCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0ID0gIjxuYW1lPiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnN0UGF0ID0gIjxuYW1lPl88c3Vic3RfbmFtZT4oPHN1YnN0X1I+KSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtRHVwbGkgPSAiR2VudXMiKQ0KcGh5c2VxX3VuaW5vY3VsYXRlZCA8LSByZW5hbWVUYXhhKHBoeXNlcV91bmlub2N1bGF0ZWQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdCA9ICI8bmFtZT4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzdFBhdCA9ICI8bmFtZT5fPHN1YnN0X25hbWU+KDxzdWJzdF9SPikiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bUR1cGxpID0gIkdlbnVzIikNCg0KYGBgDQoNCg0KDQoNCg0KTm93IHdlIHJ1biB0aGUgbmV0d29yayBhbmFseXNpcy4NCkkndmUgZGVjaWRlZCB0byB1c2UgU1BSSU5HIGl0IHNlZW1zIGxpa2UgdGhlIGNoaWxsZXN0IG9wdGlvbiBvdXQgb2YgdGhlIGJ1bmNoLCBidXQgeW91J3JlIHdlbGNvbWUgdG8gcmVhZCBhbGwgdGhlIHBhcGVycyBhbmQgbWFrZSBhbiBpbmZvcm1lZCBjaG9pY2UgdG9vIDspDQoNCihBbiBhY3R1YWwgbGVzcyBzYXJjYXN0aWMgYW5zd2VyIHRvIHRoZSBxdWVzdGlvbiBvZiAid2h5IHNwcmluZyIgaXMgZm9sbG93cyk6DQoNClNQUklORyB1c2VzIHRoZSBtY2xyIHRyYW5zZm9ybWF0aW9uLCBpdHMgbGlrZSB0aGUgY2xyIHRyYW5zZm9ybWF0aW9uIGJ1dCB3aXRob3V0IHBzZXVkb2NvdW50cyEgbm90ZSBpdCBkb2Vzbid0IGRvIHRoaXMgbGlrZSB0aGUgYmV0YSBkaXZlcnNpdHkgcGFja2FnZXMgd2UndmUgYmVlbiB1c2luZywgaXRzIGRvZXNuJ3QgY2FyZSBhYm91dCB0aGUgd2liYmx5IHdvYmJseSBhY3R1YWwgZWZmZWN0IGFkZGluZyBhIHBzZXVkb2NvdW50IGRvZXMgdG8geW91ciBjbHIgdHJhbnNmb3JtYXRpb24gYW5kIGp1c3Qga25vd3Mgd2hhdCBoaXAgYW5kIGhhcHBlbmluZyAodGhlIHByZXZpb3VzIGNvbW1lbnQgc2hvdWxkIG1ha2UgYWJvdXQgMCBzZW5zZSwgd2hpY2ggaXMgaW5mYWN0IG1vcmUgc2Vuc2UgdGhhbiBhIGRldGFpbGVkIGV4cGxhbmF0aW9uIG9mIHRoZSBtY2xyIHRyYW5zZm9ybWF0aW9uKS4gU2VlIGl0IGNhbGN1bGF0ZXMgdGhlIGdlb21ldHJpYyBtZWFucyBvZiBhIHNhbXBsZSBiYXNlZCBvbiBhbGwgeW91ciByb3dzIHdpdGggdmFsdWVzIGluIHRoZW0sIGFuZCB0aGVuIGl0IG9yZGVycyB0aGVzZSBieSByYW5rIGFuZCBzZXRzIGFueXRoaW5nIHRoYXQgd2FzIGEgemVybyBiZWZvcmUgdGhlIHRyYW5zZm9ybWF0aW9uIHRvIGJlIGF0IHRoZSBib3R0b20uIFNvIGl0cyBub3QgYSAiVHJ1ZSIgdHJhbnNmb3JtYXRpb24gaW4gdGhlIHNlbnNlIHRoYXQgaXQgdHJlYXRzIGV2ZXJ5dGhpbmcgZXF1YWxseSwgYnV0IGl0IGRvZXMgdGhlIHRyYW5zZm9ybWF0aW9uIG9uIHRoZSBkYXRhIHdlIG5lZWQgQU5EIGlzIHJhbmsgcHJlc2VydmluZyBvbiB0aGUgZGF0YSB3ZSBkb24ndCBjYXJlIGFib3V0LiAoVGhpcyBpcyB2ZXJ5IHNleHkgbmdsKSANCg0KU1BSSU5HIGFsc28gaW5jb3Jwb3JhdGVzIG1lYXN1cmVzIGZvciBzcGFyc2l0eSB3aGljaCBhbG90IG9mIG5ldHdvcmsgYW5hbHlzaXMgZG9uJ3QgZXhwbGljaXRseSBkby4gDQoNClRoZSBGaW5hbCByZWFzb24gSSBsaWtlIFNQUklORyBpcyB0aGUgZmFjdCBpdHMgYSBjb25kaXRpb25hbCBkZXBlbmRlbmNlIG1ldGhvZC4gVGhpcyBpcyBiZW5lZmljaWFsIGJlY2F1c2UgY29ycmVsYXRpb24gYmFzZWQgbWV0aG9kcyAobGlrZSBTcGFyQ0MpIG9mdGVuIGZhaWwgdG8gZGlmZmVyZW50aWF0ZSBiZXR3ZWVuIGRpcmVjdCBhbmQgaW5kaXJlY3QgYXNzb2NpYXRpb25zIGFuZCB0aGVyZSBhcmUgQUxPVCBvZiBpbmRpcmVjdCBhc3NvY2lhdGlvbnMgdG8gYmUgcGlja2VkIHVwIG9uIGluIG1pY3JvYmlhbCBuZXR3b3JrIGFuYWx5c2lzLCBzbyBtaXRpZ2F0aW5nIHRoaXMgbWl0aWdhdGVzIGZhbHNlIGNvbmNsdXNpb25zDQoNCg0KYGBge3J9DQpuZXRfaW5vY19zcHJpbmcgPC0gbmV0Q29uc3RydWN0KGRhdGEgPSBwaHlzZXFfaW5vY3VsYXRlZCwjeW91ciBwaHlzZXEgb2JqZWN0LCBpZiB5b3Ugb25seSB3YW50IHRvIG1ha2Ugb25lIG5ldHdvcmssIGRvIG5vdCBpbmNsdWRlIGRhdGEyID0gLi4uLCBvdGhlcndpc2UgdGhpcyBpcyB3aGVyZSB3ZSB0ZWxsIHRoZSBuZXR3b3JrIGFuYWx5c2lzIHRvIG1ha2UgdHdvIG5ldHdvcmtzIGJhc2VkIG9uIHRoZXNlIGdyb3Vwcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhMiA9cGh5c2VxX3VuaW5vY3VsYXRlZCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRheFJhbmsgPSAiR2VudXMiLCAjdGVsbHMgdGhlIG5ldHdvcmsgYW5hbHlzaXMgdG8gdXNlIGdlbnVzIGxldmVsIHRheG9ub21pYyBpbmZvcm1hdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IDIsICNob3cgdGFsa2F0aXZlIHRoZSBwcm9ncmFtIGlzDQogICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0VGF4ID0gImhpZ2hlc3RGcmVxIiwgI3RoaXMgdGVsbHMgdGhlIHByb2dyYW0gdG8gbG9vayBhdCB0aGUgdGF4YSB3aXRoIHRoZSBoaWdoZXN0IGZyZXF1ZW5jeQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdFRheFBhciA9IGxpc3QoaGlnaGVzdEZyZXEgPSA4NSksICN0aGUgaGlnaGVzdCA4NSB0YXhhDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlID0gInNwcmluZyIsICNoZXJlIGlzIHdoZXJlIHlvdSBzYXkgd2hpY2ggbWVhc3VyZSB5b3Ugd2FudCBzcHJpbmcgc3BhcmNjIGVjdA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhc3VyZVBhciA9IGxpc3QobmxhbWJkYSA9IDUwLCAjdGhlc2UgbWVhc3VyZXMgYXJlIGJhc2VkIG9uIHdoYXQgdGhlIHBhcGVyIHVzZWQgc28gdGhhdHMgd2h5IGltIHVzaW5nIHRoZW0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcC5udW0gPSA1MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJtZXRob2QgPSAiYXBwcm94IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb3Jlcz0xMCksICNudW1iZXIgb2YgY29tcHV0ZXIgY29yZXMgdG8gdXNlDQogICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtTWV0aG9kID0gIm5vbmUiLCAjaGFuZGxlZCBieSBzcHJpbmcgaXRzZWxkDQogICAgICAgICAgICAgICAgICAgICAgICAgICB6ZXJvTWV0aG9kID0gIm5vbmUiLCAjaGFuZGxlZCBieSBzcHJpbmcNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwYXJzTWV0aG9kID0gIm5vbmUiLCAjc3ByaW5nIHJldHVybnMgYSBzcGFyc2UgbmV0d29yayBhbHJlYWR5IHNvIG5vIGV4dHJhIHNwYXJzaWZpY2F0aW9uIG5lY2Vzc2FyeQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzc0Z1bmMgPSAic2lnbmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSAxMjM0NTYpDQpgYGANCg0KaGV5IGRpZCB5b3UgZ2V0IGEgd2FybmluZyB0aGF0IGxvb2tzIGxpa2UgdGhpcz8NCldhcm5pbmcgbWVzc2FnZToNCkluIE1hdHJpeDo6bmVhclBEKFIsIGNvcnIgPSBUUlVFKSA6DQogICduZWFyUEQoKScgZGlkIG5vdCBjb252ZXJnZSBpbiAxMDAgaXRlcmF0aW9ucw0KVGhhdCBtZWFucyB5b3VyIGRhdGEgd2FzIHRvbyBzY2FyY2UhIFRoZSBlYXNpZXN0IHdheSB0byBkbyB0aGlzIGlzIGp1c3QgZGVjcmVhc2UgdGhlIGFtb3VudCBvZiB0YXhhIHlvdSBsb29rIGF0IEkuZS4gZnJvbSA3NS0+NjUgdW5kZXIgZmlsdFRheFBhciA9IGxpc3QoaGlnaGVzdEZyZXEgPSB4KS4gVGhpcyB3aWxsIGZ1bmN0aW9uYWxseSBvbmx5IHR0YWtlIHRoZSBoaWdoZXN0IGFidW5kYW5jZSB0YXhhIGFuZCB0aGVyZSBmb3Igc29sdmUgdGhlIHNwYXJjaXR5IGlzc3VlDQoNCg0KYGBge3J9DQpzYXZlUkRTKG5ldF9pbm9jX3NwcmluZywgIm5ldF9pbm9jX3NwcmluZy5yZHMiKQ0KYGBgDQoNClRoZW4gd2UgY2FuIGFuYWx5emUgdGhlIG5ldHdvcmtzIQ0KDQpgYGB7cn0NCnByb3BzX2lub2MgPC0gbmV0QW5hbHl6ZShuZXRfaW5vY19zcHJpbmcsICN5b3VyIG5ldHdvcmsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbnRyTENDID0gRkFMU0UsICNpIGRvbnQgdW5kZXJzdGFuZA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXZEaXNzSWdub3JlSW5mID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzUGF0aE5vcm0gPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0TWV0aG9kID0gImNsdXN0ZXJfZmFzdF9ncmVlZHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgaHViUGFyID0gYygiZGVncmVlIiwgImVpZ2VudmVjdG9yIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBodWJRdWFudCA9IDAuOTUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsbm9ybUZpdCA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtRGVnID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtQmV0dyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybUNsb3NlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtRWlnZW4gPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSAyKQ0KDQoNCg0KDQpgYGANClRoZXNlIHNob3VsZCBhbmQgY2FuIGNoYW5nZSBiYXNlZCBvbiB5b3VyIHF1ZXN0aW9ucywgYnV0IGhvbmVzdGx5IEkgZG9udCB1bmRlcnN0YW5kIGl0IHZlcnkgd2VsbCwgSSdtIHVzaW5nIHRoZSB0dXRvcmlhbCBzcGVjcw0KDQp0aGVuIHdlIGNhbiBwbG90IGl0IG91dA0KDQpUaGlzIHdpbGwgcGxvdCBvdXQganVzdCB0aGUgbmV0d29ya3MgYXMgdGhleSB3YW50IHRvIGV4aXN0IGluZGl2aWR1YWxseSwgYnV0IHRoaXMgd2lsbCBtYWtlIGNvbXBhcmlzb24gaGFyZCENCmBgYHtyfQ0KcHJvcHNfaW5vYyA8LSByZWFkUkRTKCJDOlxcVXNlcnNcXGFuZ3VzXFxPbmVEcml2ZSAtIFVOQkNcXEFuZ3VzIEJhbGxcXExhYiB3b3JrXFxCaW9pbmZvcm1hdGljc1xcVUxUUkFcXFJvYmplY3RzXFxwcm9wc19pbm9jLnJkcyIpDQoNCnBsb3QocHJvcHNfaW5vYywgDQogICAgIGxheW91dCA9ICJzcHJpbmciLCAjc2hhcGUsIGFsdGVybmF0aXZlIG9wdGlvbiBpcyBjaXJjbGUNCiAgICAgc2FtZUxheW91dCA9IEZBTFNFLCAjc2FtZSBsYXlvdXQgb2YgYm90aCBncmFwaHM/DQogICAgIG5vZGVDb2xvciA9ICJjbHVzdGVyIiwgI2hvdyB0byBjb2xvciBub2Rlcw0KICAgICByZXB1bHNpb24gPSAwLjcsICNob3cgZmFyIGFwYXJ0IGFyZSBub2Rlcw0KICAgICBub2RlU2l6ZSA9ICJtY2xyIiwgI25vZGUgc2l6ZSBpcyBiYXNlZCBvZmYgb2YgY2xyIHRyYW5zZm9ybSBzaXplDQogICAgIGxhYmVsU2NhbGUgPSBGLCAjZG9uJ3Qgc2NhbGUgbmFtZXMgd2l0aCBub2RlIHNpemUNCiAgICAgc2hvcnRlbkxhYmVscyA9ICJub25lIiwjaWYgeW91IHdhbnQgdG8gc2hvcnRlbiB0aGUgbGFiZWwgbmFtZXMsIG90aGUgcm9wdGlvbiBpcyAiaW50ZWxsaWdlbnQiDQogICAgIGNleE5vZGVzID0gMiwgI3NpemUgb2Ygbm9kZXMNCiAgICAgY2V4TGFiZWxzID0gLjUsI3NpemUgb2YgbmFtZXMNCiAgICAgY2V4SHViTGFiZWxzID0gLjc1LCNzaXplIG9mIGh1YiBuYW1lcw0KICAgICBjZXhUaXRsZSA9IDIsI3NpemUgb2YgdGl0bGUNCiAgICAgZ3JvdXBOYW1lcyA9IGMoIklub2N1bGF0ZWQiLCAiVW5pbm9jdWxhdGVkIiksICNncm91cCBuYW1lcw0KICAgICBodWJCb3JkZXJDb2wgID0gImdyYXk0MCIpDQoNCmxlZ2VuZCgwLjcsIDEuMSwgY2V4ID0gMi4yLCB0aXRsZSA9ICJlc3RpbWF0ZWQgYXNzb2NpYXRpb246IiwNCiAgICAgICBsZWdlbmQgPSBjKCIrIiwiLSIpLCBsdHkgPSAxLCBsd2QgPSAzLCBjb2wgPSBjKCIjMDA5OTAwIiwicmVkIiksIA0KICAgICAgIGJ0eSA9ICJuIiwgaG9yaXogPSBUUlVFKQ0KDQpgYGANCg0KV2UgY2FuIGluc3RlYWQgZm9yY2UgdGhlIG5ldHdvcmtzIHRvIGxvb2sgdGhlIHNhbWUgaW4gT1RVIG9yZGVyIGZvciBlYXN5IGNvbXBhcmlzb24NCmBgYHtyfQ0KcGxvdChwcm9wc19pbm9jLCANCiAgICAgbGF5b3V0ID0gInNwcmluZyIsICNzaGFwZSwgYWx0ZXJuYXRpdmUgb3B0aW9uIGlzIGNpcmNsZQ0KICAgICBzYW1lTGF5b3V0ID0gVFJVRSwgI3NhbWUgbGF5b3V0IG9mIGJvdGggZ3JhcGhzPw0KICAgICBsYXlvdXRHcm91cCA9ICJ1bmlvbiIsICNjb21iaW5hdGlvbiBvZiBib3RoIGxheW91dHMNCiAgICAgcm1TaW5nbGVzID0gImluYm90aCIsICNyZW1vdmUgc2luZ2xlIHRheGEgbm8gYXNzb2NpYXRlZCB3aXRoIG90aGVyIHRheGEgaW4gYm90aCBncm91cHMNCiAgICAgbm9kZUNvbG9yID0gImNsdXN0ZXIiLCAjaG93IHRvIGNvbG9yIG5vZGVzDQogICAgIHJlcHVsc2lvbiA9IDAuNywgI2hvdyBmYXIgYXBhcnQgYXJlIG5vZGVzDQogICAgIG5vZGVTaXplID0gIm1jbHIiLCAjbm9kZSBzaXplIGlzIGJhc2VkIG9mZiBvZiBjbHIgdHJhbnNmb3JtIHNpemUNCiAgICAgbGFiZWxTY2FsZSA9IEYsICNkb24ndCBzY2FsZSBuYW1lcyB3aXRoIG5vZGUgc2l6ZQ0KICAgICBzaG9ydGVuTGFiZWxzID0gIm5vbmUiLCNpZiB5b3Ugd2FudCB0byBzaG9ydGVuIHRoZSBsYWJlbCBuYW1lcywgb3RoZSByb3B0aW9uIGlzICJpbnRlbGxpZ2VudCINCiAgICAgY2V4Tm9kZXMgPSAyLCAjc2l6ZSBvZiBub2Rlcw0KICAgICBjZXhMYWJlbHMgPSAuNSwjc2l6ZSBvZiBuYW1lcw0KICAgICBjZXhIdWJMYWJlbHMgPSAuNzUsI3NpemUgb2YgaHViIG5hbWVzDQogICAgIGNleFRpdGxlID0gMiwjc2l6ZSBvZiB0aXRsZQ0KICAgICBncm91cE5hbWVzID0gYygiSW5vY3VsYXRlZCIsICJVbmlub2N1bGF0ZWQiKSwgI2dyb3VwIG5hbWVzDQogICAgIGh1YkJvcmRlckNvbCAgPSAiZ3JheTQwIikNCg0KbGVnZW5kKDAuNSwgMS4xLCBjZXggPSAyLjIsIHRpdGxlID0gImVzdGltYXRlZCBhc3NvY2lhdGlvbjoiLA0KICAgICAgIGxlZ2VuZCA9IGMoIisiLCItIiksIGx0eSA9IDEsIGx3ZCA9IDMsIGNvbCA9IGMoIiMwMDk5MDAiLCJyZWQiKSwgDQogICAgICAgYnR5ID0gIm4iLCBob3JpeiA9IFRSVUUpDQpgYGANCg0KRm9yIHNvbWUgcmVhc29uIGdnc2F2ZSBkb2Vzbid0IHNlZW0gdG8gd2FudCB0byB3b3JrIHdpdGggdGhlc2UgcGxvdHMuIEJ1dCBoYW5kIGV4cG9ydGluZyBpdCB3aXRoIHRoZSBleHBvcnQgYnV0dG9uIGFib3ZlIHRoZSBncmFwaCB3b3JrcyBzbyB1aGgganVzdCB1c2UgdGhhdA0KDQpleHBsYW5hdGlvbnMgb2YgdGhlIGdpdmVuIHZhcmlhYmxlcyBpcyBnaXZlbiBhdCBodHRwczovL3JkcnIuaW8vZ2l0aHViL3N0ZWZwZXNjaGVsL05ldENvTWkvbWFuL3Bsb3QubWljcm9OZXRQcm9wcy5odG1sDQoNCg0Kd2Ugd2lsbCB0aGVuIHVzZSBzdGF0aXN0aWNhbCB0ZXN0cyB0byBjb21wYXJlIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlc2UgdHdvIG5ldHdvcmtzLiBOT1RFOiB0aGUgcCB2YWx1ZSBkaXNwbGF5ZWQgYnkgdGhpcyBpcyBOT1QgY29tcGFyaW5nIHRoZSB0d28gbmV0d29ya3MuIEl0cyBzdWdnZXN0aW5nIHRoYXQgdGhlIGFic29sdXRlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbmV0d29ya3MgaXMgbm90IGVxdWFsIHRvIHplcm8uIHRha2UgdGhpcyBhcyB5b3Ugd2lsbC4NCg0KTm90ZSB0aGlzIHByb2Nlc3Mgd2lsbCB0YWtlIDVldmVyLiBlc3RpbWF0ZWQgdGltZSA4LTEwaHINCmBgYHtyfQ0KY29tcF9pbm9jX29yaWcgPC0gbmV0Q29tcGFyZShwcm9wc19pbm9jLCBwZXJtVGVzdCA9IFRSVUUsIG5QZXJtID0gMTAwMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHN0b3JlQXNzb1Blcm0gPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlU3RvcmVBc3NvUGVybSA9ICJhc3NvUGVybV9jb21wIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3RvcmVDb3VudHNQZXJtID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvcmVzID0gMSwgI2dvdCBhbiBlcnJvciBmb3IgdXNpbmcgbXVsdGlwbGUgY29yZXMgc28gb25seSB1c2luZyBvbmUgOigNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQ1NiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWRqdXN0ID0gIkJIIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUpDQojd2UgY2FuIGV4cGxvcmUgdGhlIG91dHB1dCBpbg0Kc3VtbWFyeShjb21wX2lub2Nfb3JpZykNCg0KYGBgDQoNCg0KDQpUaGVuIHdlIGNhbiBzZWUgaWYgdGhlcmUgYXJlIGRpZmZlcmVudGlhbCBhc3NvY2lhdGlvbnMgd2l0aGluIG91ciBuZXR3b3JrDQpgYGB7cn0NCmRpZmZfaW5vYyA8LSBkaWZmbmV0KG5ldF9pbm9jX3NwcmluZywNCiAgICAgICAgICAgICAgICAgICAgICAgZGlmZk1ldGhvZCA9ICJmaXNoZXJUZXN0IiwgDQogICAgICAgICAgICAgICAgICAgICAgIGFkanVzdCA9ICJsZmRyIikNCnNhdmVSRFMoZGlmZl9pbm9jLCAiZGlmZl9pbm9jLnJkcyIpDQojSSBkaWRuJ3QgaGF2ZSBhbnkgYnV0IGlmIHlvdSBkby4uLg0KcGxvdChkaWZmX2lub2MsIA0KICAgICBjZXhOb2RlcyA9IDAuOCwgDQogICAgIGNleExlZ2VuZCA9IDMsDQogICAgIGNleFRpdGxlID0gNCwNCiAgICAgbWFyID0gYygyLDIsOCw1KSwNCiAgICAgbGVnZW5kR3JvdXBuYW1lcyA9IGMoIklub2N1bGF0ZWQiLCAiVW5pbm9jdWxhdGVkIiksDQogICAgIGxlZ2VuZFBvcyA9IGMoMC43LDEuNikpDQoNCnByb3BzX2lub2NfcGVhcnMgPC0gbmV0QW5hbHl6ZShuZXRfaW5vY19zcHJpbmcsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3RNZXRob2QgPSAiY2x1c3Rlcl9mYXN0X2dyZWVkeSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHREZWcgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybURlZyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2NtSGVhdCA9IEZBTFNFKQ0KIyBJZGVudGlmeSB0aGUgZGlmZmVyZW50aWFsbHkgYXNzb2NpYXRlZCBPVFVzDQpkaWZmbWF0X3N1bXMgPC0gcm93U3VtcyhkaWZmX2lub2MkZGlmZkFkanVzdE1hdCkNCmRpZmZfYXNzb19uYW1lcyA8LSBuYW1lcyhkaWZmbWF0X3N1bXNbZGlmZm1hdF9zdW1zID4gMF0pDQpwbG90KHByb3BzX2lub2Nfc3ByaW5nLCANCiAgICAgbm9kZUZpbHRlciA9ICJuYW1lcyIsDQogICAgIG5vZGVGaWx0ZXJQYXIgPSBkaWZmX2Fzc29fbmFtZXMsDQogICAgIG5vZGVDb2xvciA9ICJncmF5IiwNCiAgICAgaGlnaGxpZ2h0SHVicyA9IEZBTFNFLA0KICAgICBzYW1lTGF5b3V0ID0gVFJVRSwgDQogICAgIGxheW91dEdyb3VwID0gInVuaW9uIiwNCiAgICAgcm1TaW5nbGVzID0gRkFMU0UsIA0KICAgICBub2RlU2l6ZSA9ICJjbHIiLA0KICAgICBlZGdlVHJhbnNwSGlnaCA9IDIwLA0KICAgICBsYWJlbFNjYWxlID0gRkFMU0UsDQogICAgIGNleE5vZGVzID0gMS41LCANCiAgICAgY2V4TGFiZWxzID0gMywNCiAgICAgY2V4VGl0bGUgPSAzLjgsDQogICAgIGdyb3VwTmFtZXMgPSBjKCJJbm9jdWxhdGVkIiwgIlVuaW5vY3VsYXRlZCIpLA0KICAgICBodWJCb3JkZXJDb2wgID0gImdyYXk0MCIpDQoNCmxlZ2VuZCgtMC4xNSwtMC43LCB0aXRsZSA9ICJlc3RpbWF0ZWQgY29ycmVsYXRpb246IiwgbGVnZW5kID0gYygiKyIsIi0iKSwgDQogICAgICAgY29sID0gYygiIzAwOTkwMCIsInJlZCIpLCBpbnNldCA9IDAuMDUsIGNleCA9IDQsIGx0eSA9IDEsIGx3ZCA9IDQsIA0KICAgICAgIGJ0eSA9ICJuIiwgaG9yaXogPSBUUlVFKQ0KDQpgYGANCg0KDQoNCg0KDQpBbmQgeW91J3JlIGRvbmUhIEZSRUUsIHRvIGxpdmUgYW5vdGhlciBkYXkNCg==