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==