3. Load the example dataset
data(kostic_crc)
kostic_crc
phyloseq-class experiment-level object
otu_table() OTU Table: [ 2505 taxa and 177 samples ]
sample_data() Sample Data: [ 177 samples by 71 sample variables ]
tax_table() Taxonomy Table: [ 2505 taxa by 7 taxonomic ranks ]
## Explore the dataset
dim(sample_data(kostic_crc))
[1] 177 71
head(sample_data(kostic_crc))
Sample Data: [6 samples by 71 sample variables]:
table(sample_data(kostic_crc)$DIAGNOSIS)
Healthy Tumor
91 86
head(tax_table(kostic_crc))
Taxonomy Table: [6 taxa by 7 taxonomic ranks]:
Kingdom Phylum Class Order Family
304309 "Bacteria" "Firmicutes" "Clostridia" "Clostridiales" "Lachnospiraceae"
469478 "Bacteria" "Firmicutes" "Clostridia" "Clostridiales" "Lachnospiraceae"
208196 "Bacteria" "Proteobacteria" "Alphaproteobacteria" "Rhizobiales" "Methylobacteriaceae"
358030 "Bacteria" "Firmicutes" "Clostridia" "Clostridiales" NA
16076 "Bacteria" "Firmicutes" "Clostridia" "Clostridiales" "Ruminococcaceae"
35786 "Bacteria" "Firmicutes" "Clostridia" "Clostridiales" "Ruminococcaceae"
Genus Species
304309 "Blautia" "Blautia producta"
469478 "Catonella" NA
208196 "Methylobacterium" "Methylobacterium organophilum"
358030 NA NA
16076 "Ruminococcus" "Ruminococcus bromii"
35786 NA NA
head(otu_table(kostic_crc)[,1:20])
OTU Table: [6 taxa and 20 samples]
taxa are rows
C0333.N.518126 C0333.T.518046 38U4VAHB.518100 XZ33PN7O.518030 GQ6LSNI6.518106
304309 40 4 1 2 30
469478 0 0 0 0 0
208196 0 0 0 0 0
358030 0 0 0 0 0
16076 271 28 110 7 34
35786 0 0 0 0 0
C0270.N.518041 HZIMMAM6.518095 LRQ9WN6C.518048 A8A34NCW.518023 C0332.N.518027
304309 1 4 8 7 2
469478 0 0 0 0 0
208196 0 0 0 0 0
358030 0 0 0 0 0
16076 0 0 0 0 8
35786 0 0 0 0 0
C10.S.517995 MQE9ONGV.518038 C0230.N.517992 C0256.N.518170 HZIMMNL5.518062
304309 2 2 1 1 4
469478 0 0 0 0 0
208196 0 0 0 0 0
358030 0 0 0 0 0
16076 21 0 3 11 0
35786 0 0 0 0 0
GQ6LSABJ.518010 C0282.N.518138 TV28INUO.518004 C0315.N.518021 C0235.N.517993
304309 7 1 1 1 1
469478 0 0 1 0 0
208196 0 0 0 0 0
358030 0 0 0 0 0
16076 9 13 2 7 0
35786 0 0 0 0 0
5. Normalization of the sample counts
## check how large the sample sizes (number of counts) are
max_difference = max(sample_sums(kostic_crc))/min(sample_sums(kostic_crc))
max_difference
[1] 21.34924
max(sample_sums(kostic_crc))
[1] 11187
min(sample_sums(kostic_crc))
[1] 524
## norm is only needed if sample sizes are more than 10x different (and they are)
## distribution of the sample sizes
hist(sample_sums(kostic_crc), breaks = 177)

## get relative abundance with the microbiome package
kostic_crc.compositional <- microbiome::transform(kostic_crc, "compositional")
head(otu_table(kostic_crc.compositional)[,1:20])
OTU Table: [6 taxa and 20 samples]
taxa are rows
C0333.N.518126 C0333.T.518046 38U4VAHB.518100 XZ33PN7O.518030 GQ6LSNI6.518106
304309 0.007078393 0.00311042 0.000152765 0.0004374453 0.003442736
469478 0.000000000 0.00000000 0.000000000 0.0000000000 0.000000000
208196 0.000000000 0.00000000 0.000000000 0.0000000000 0.000000000
358030 0.000000000 0.00000000 0.000000000 0.0000000000 0.000000000
16076 0.047956114 0.02177294 0.016804155 0.0015310586 0.003901767
35786 0.000000000 0.00000000 0.000000000 0.0000000000 0.000000000
C0270.N.518041 HZIMMAM6.518095 LRQ9WN6C.518048 A8A34NCW.518023 C0332.N.518027
304309 0.0001247194 0.0004288624 0.001371507 0.00100416 0.001091107
469478 0.0000000000 0.0000000000 0.000000000 0.00000000 0.000000000
208196 0.0000000000 0.0000000000 0.000000000 0.00000000 0.000000000
358030 0.0000000000 0.0000000000 0.000000000 0.00000000 0.000000000
16076 0.0000000000 0.0000000000 0.000000000 0.00000000 0.004364430
35786 0.0000000000 0.0000000000 0.000000000 0.00000000 0.000000000
C10.S.517995 MQE9ONGV.518038 C0230.N.517992 C0256.N.518170 HZIMMNL5.518062
304309 0.0004409171 0.0007501875 0.0009823183 0.0001157273 0.0006255865
469478 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
208196 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
358030 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
16076 0.0046296296 0.0000000000 0.0029469548 0.0012730008 0.0000000000
35786 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
GQ6LSABJ.518010 C0282.N.518138 TV28INUO.518004 C0315.N.518021 C0235.N.517993
304309 0.00112 0.0005980861 0.0002352941 0.0001344267 0.0006609385
469478 0.00000 0.0000000000 0.0002352941 0.0000000000 0.0000000000
208196 0.00000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
358030 0.00000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
16076 0.00144 0.0077751196 0.0004705882 0.0009409867 0.0000000000
35786 0.00000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
## get relative abundance with the microbiomeMarker package
kostic_crc_TSS = microbiomeMarker::normalize(kostic_crc, method="TSS")
head(otu_table(kostic_crc_TSS)[,1:20])
OTU Table: [6 taxa and 20 samples]
taxa are rows
C0333.N.518126 C0333.T.518046 38U4VAHB.518100 XZ33PN7O.518030 GQ6LSNI6.518106
304309 0.007078393 0.00311042 0.000152765 0.0004374453 0.003442736
469478 0.000000000 0.00000000 0.000000000 0.0000000000 0.000000000
208196 0.000000000 0.00000000 0.000000000 0.0000000000 0.000000000
358030 0.000000000 0.00000000 0.000000000 0.0000000000 0.000000000
16076 0.047956114 0.02177294 0.016804155 0.0015310586 0.003901767
35786 0.000000000 0.00000000 0.000000000 0.0000000000 0.000000000
C0270.N.518041 HZIMMAM6.518095 LRQ9WN6C.518048 A8A34NCW.518023 C0332.N.518027
304309 0.0001247194 0.0004288624 0.001371507 0.00100416 0.001091107
469478 0.0000000000 0.0000000000 0.000000000 0.00000000 0.000000000
208196 0.0000000000 0.0000000000 0.000000000 0.00000000 0.000000000
358030 0.0000000000 0.0000000000 0.000000000 0.00000000 0.000000000
16076 0.0000000000 0.0000000000 0.000000000 0.00000000 0.004364430
35786 0.0000000000 0.0000000000 0.000000000 0.00000000 0.000000000
C10.S.517995 MQE9ONGV.518038 C0230.N.517992 C0256.N.518170 HZIMMNL5.518062
304309 0.0004409171 0.0007501875 0.0009823183 0.0001157273 0.0006255865
469478 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
208196 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
358030 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
16076 0.0046296296 0.0000000000 0.0029469548 0.0012730008 0.0000000000
35786 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
GQ6LSABJ.518010 C0282.N.518138 TV28INUO.518004 C0315.N.518021 C0235.N.517993
304309 0.00112 0.0005980861 0.0002352941 0.0001344267 0.0006609385
469478 0.00000 0.0000000000 0.0002352941 0.0000000000 0.0000000000
208196 0.00000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
358030 0.00000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
16076 0.00144 0.0077751196 0.0004705882 0.0009409867 0.0000000000
35786 0.00000 0.0000000000 0.0000000000 0.0000000000 0.0000000000
## output of both from above are relative abundances
## css norm (originally from metagenomeSeq packege) ##
# Cumulative Sum Scaling (CSS) is a median-like quantile normalization which corrects differences in sampling depth (library size).
# While standard relative abundance (fraction/percentage) normalization re-scales all samples to the same total sum (100%), CSS keeps a variation in total counts between samples.
# CSS re-scales the samples based on a subset (quartile) of lower abundant taxa
#(relatively constant and independent),
# thereby excluding the impact of (study dominating) high abundant taxa.
kostic_crc_CSS = microbiomeMarker::normalize(kostic_crc, method="CSS")
Default value being used.
head(otu_table(kostic_crc_CSS)[,1:20])
OTU Table: [6 taxa and 20 samples]
taxa are rows
C0333.N.518126 C0333.T.518046 38U4VAHB.518100 XZ33PN7O.518030 GQ6LSNI6.518106
304309 40 4 1 2 30
469478 0 0 0 0 0
208196 0 0 0 0 0
358030 0 0 0 0 0
16076 271 28 110 7 34
35786 0 0 0 0 0
C0270.N.518041 HZIMMAM6.518095 LRQ9WN6C.518048 A8A34NCW.518023 C0332.N.518027
304309 1 4 8 7 2
469478 0 0 0 0 0
208196 0 0 0 0 0
358030 0 0 0 0 0
16076 0 0 0 0 8
35786 0 0 0 0 0
C10.S.517995 MQE9ONGV.518038 C0230.N.517992 C0256.N.518170 HZIMMNL5.518062
304309 2 2 1 1 4
469478 0 0 0 0 0
208196 0 0 0 0 0
358030 0 0 0 0 0
16076 21 0 3 11 0
35786 0 0 0 0 0
GQ6LSABJ.518010 C0282.N.518138 TV28INUO.518004 C0315.N.518021 C0235.N.517993
304309 7 1 1 1 1
469478 0 0 1 0 0
208196 0 0 0 0 0
358030 0 0 0 0 0
16076 9 13 2 7 0
35786 0 0 0 0 0
## cpm norm originally lefse package ###
kostic_crc_CPM = microbiomeMarker::normalize(kostic_crc, method="CPM")
head(otu_table(kostic_crc_CPM)[,1:20])
OTU Table: [6 taxa and 20 samples]
taxa are rows
C0333.N.518126 C0333.T.518046 38U4VAHB.518100 XZ33PN7O.518030 GQ6LSNI6.518106
304309 7078.393 3110.42 152.765 437.4453 3442.736
469478 0.000 0.00 0.000 0.0000 0.000
208196 0.000 0.00 0.000 0.0000 0.000
358030 0.000 0.00 0.000 0.0000 0.000
16076 47956.114 21772.94 16804.155 1531.0586 3901.767
35786 0.000 0.00 0.000 0.0000 0.000
C0270.N.518041 HZIMMAM6.518095 LRQ9WN6C.518048 A8A34NCW.518023 C0332.N.518027
304309 124.7194 428.8624 1371.507 1004.16 1091.107
469478 0.0000 0.0000 0.000 0.00 0.000
208196 0.0000 0.0000 0.000 0.00 0.000
358030 0.0000 0.0000 0.000 0.00 0.000
16076 0.0000 0.0000 0.000 0.00 4364.430
35786 0.0000 0.0000 0.000 0.00 0.000
C10.S.517995 MQE9ONGV.518038 C0230.N.517992 C0256.N.518170 HZIMMNL5.518062
304309 440.9171 750.1875 982.3183 115.7273 625.5865
469478 0.0000 0.0000 0.0000 0.0000 0.0000
208196 0.0000 0.0000 0.0000 0.0000 0.0000
358030 0.0000 0.0000 0.0000 0.0000 0.0000
16076 4629.6296 0.0000 2946.9548 1273.0008 0.0000
35786 0.0000 0.0000 0.0000 0.0000 0.0000
GQ6LSABJ.518010 C0282.N.518138 TV28INUO.518004 C0315.N.518021 C0235.N.517993
304309 1120 598.0861 235.2941 134.4267 660.9385
469478 0 0.0000 235.2941 0.0000 0.0000
208196 0 0.0000 0.0000 0.0000 0.0000
358030 0 0.0000 0.0000 0.0000 0.0000
16076 1440 7775.1196 470.5882 940.9867 0.0000
35786 0 0.0000 0.0000 0.0000 0.0000
#tail(otu_table(kostic_crc_CSS))
## rarefy norm ###
## from microbiomeMarker package ##
#
# kostic_crc_rare = microbiomeMarker::normalize(kostic_crc, method="rarefy")
#
# head(otu_table(kostic_crc_rare)[,1:20])
## from phyloseq package
ps.rarefied = phyloseq::rarefy_even_depth(kostic_crc, rngseed=123, sample.size=524, replace=F)
`set.seed(123)` was used to initialize repeatable random subsampling.
Please record this for your records so others can reproduce.
Try `set.seed(123); .Random.seed` for the full vector
...
1110OTUs were removed because they are no longer
present in any sample after random subsampling
...
6. Plot stack bar plots of the top 15 taxa based on abund
## extract the top 15 taxa on this taxa level for CSS
top_phy = tax_glom(kostic_crc_CSS, "Genus")
top15 = names(sort(taxa_sums(top_phy), decreasing=TRUE)[1:15])
top15_css_ps = prune_taxa(top15, top_phy)
phyloseq::plot_bar(top15_css_ps, "DIAGNOSIS", fill="Genus")+
theme( axis.ticks.x=element_blank(), panel.background=element_rect(fill=NA), panel.grid.major=element_line(colour="#ebebeb")) +
labs(x=NULL)

## get rid of the black squares and get better colors
plot_bar(top15_css_ps, "DIAGNOSIS", fill="Genus")+
theme( axis.ticks.x=element_blank(), panel.background=element_rect(fill=NA), panel.grid.major=element_line(colour="#ebebeb")) +
labs(x=NULL) + geom_bar(stat="identity")

## Generate 15 distinct colors from the palette
color = grDevices::colors()[grep('gr(a|e)y', grDevices::colors(), invert = T)]
sampled_colors <- sample(color, 15)
plot_bar(top15_css_ps, "DIAGNOSIS", fill = "Genus") +
theme(axis.ticks.x = element_blank(), panel.background = element_rect(fill = NA), panel.grid.major = element_line(colour = "#ebebeb")) +
labs(x = NULL) +
geom_bar(stat = "identity", aes(fill = Genus)) + # Specify fill aesthetic here
scale_fill_manual(values = sampled_colors ) # Use scale_fill_manual for fill colors

## plot each genus in a separate facet
# plot_bar(top15_css_ps, "DIAGNOSIS", fill="DIAGNOSIS", facet_grid=~Genus) + # # #geom_bar(stat="identity")
## plot top 5 ##
top_phy5 = tax_glom(kostic_crc_CSS, "Genus")
top5 = names(sort(taxa_sums(top_phy5), decreasing=TRUE)[1:5])
top5_css_ps = prune_taxa(top5, top_phy5)
tax_table(top5_css_ps)
Taxonomy Table: [5 taxa by 7 taxonomic ranks]:
Kingdom Phylum Class Order Family
180285 "Bacteria" "Firmicutes" "Clostridia" "Clostridiales" "Ruminococcaceae"
470429 "Bacteria" "Fusobacteria" "Fusobacteria (class)" "Fusobacteriales" "Fusobacteriaceae"
49274 "Bacteria" "Firmicutes" "Clostridia" "Clostridiales" "Lachnospiraceae"
332077 "Bacteria" "Bacteroidetes" "Bacteroidia" "Bacteroidales" "Prevotellaceae"
469709 "Bacteria" "Bacteroidetes" "Bacteroidia" "Bacteroidales" "Bacteroidaceae"
Genus Species
180285 "Faecalibacterium" NA
470429 "Fusobacterium" NA
49274 "Clostridium" NA
332077 "Prevotella" NA
469709 "Bacteroides" NA
## plot each genus in a separate facet
plot_bar(top5_css_ps, "DIAGNOSIS", fill="DIAGNOSIS", facet_grid=~Genus) + geom_bar(stat="identity")

## changing the appearance
## https://stackoverflow.com/questions/63133884/removing-the-original-color-outline-in-r-when-using-a-new-pallette-in-a-barplot
## remove black boxes
## https://github.com/joey711/phyloseq/issues/721
## extract the top 15 taxa on this taxa level for TSS
top_phy = tax_glom(kostic_crc_TSS, "Genus")
top15 = names(sort(taxa_sums(top_phy), decreasing=TRUE)[1:15])
top15_tss_ps = prune_taxa(top15, top_phy)
# prepare to create a nice plot for saving
top15_tss_G =plot_bar(top15_tss_ps, "DIAGNOSIS", fill="Genus")+
theme( axis.ticks.x=element_blank(), panel.background=element_rect(fill=NA), panel.grid.major=element_line(colour="#ebebeb")) +
labs(x=NULL)+
geom_bar(stat = "identity", aes(fill = Genus)) +
scale_fill_manual(values = sampled_colors ) +
theme_bw() +
theme(
text = element_text(size = 12, face = "bold"), # Make all text bold
axis.title = element_text(size = 14, face = "bold"), # Make axis titles bold
axis.text = element_text(size = 12), # Adjust axis text size
legend.title = element_text(size = 14, face = "bold"), # Make legend title bold
legend.text = element_text(size = 12), # Adjust legend text size
plot.title = element_text(size = 16, face = "bold") # Make plot title bold
)
top15_tss_G

#save the image
#ggsave("top15_Genera_plot_TSS.png", top15_tss_G, width = 6, height = 8, dpi = 300)
# plot rarefaction curve
# Extract OTU abundance data and transpose
# otu_table_transposed<- as.data.frame(t(otu_table(kostic_crc)))
# head(otu_table_transposed)
# rarecurve(as.matrix(otu_table_transposed), step=500, cex=0.1)
7. Plot alpha diversity between groups
## estimation of alpha div only works with integers and not rel abund values (float numbers)
## what each of the alpha div means
## Observed - counts the number of unique species (OTUs or ASVs) present in a sample
## Chao1 - It takes into account the number of rare species (singletons and doubletons) observed in the sample and extrapolates the total richness based on the frequency of these rare species
## ACE - Takes into account both the number of rare species observed and their relative abundances. inflates the number of rare taxa and inflates again the number of taxa with abundance 1.
## Shannon's diversity index (also known as Shannon-Wiener index) considers both species richness and evenness in a community. It takes into account the number of different species present as well as the relative abundance of each species.
## Simpson's diversity index measures the probability that two randomly selected sequences are of the same species
## CSS ##
alpha_div = plot_richness(kostic_crc_CSS, x="DIAGNOSIS", color="DIAGNOSIS", title="CSS", measures=c("Observed", "Chao1", "ACE", "Simpson", "Shannon")) +
geom_boxplot() +
theme_bw() +
theme(
text = element_text(size = 12, face = "bold"), # Make all text bold
axis.title = element_text(size = 14, face = "bold"), # Make axis titles bold
axis.text = element_text(size = 12), # Adjust axis text size
legend.title = element_text(size = 14, face = "bold"), # Make legend title bold
legend.text = element_text(size = 12), # Adjust legend text size
plot.title = element_text(size = 16, face = "bold") # Make plot title bold
)
alpha_div

#save the image
#ggsave("alpha_diversity_plot_CSS.png", alpha_div, width = 10, height = 6, dpi = 300)
## rarefies ##
plot_richness( ps.rarefied, x="DIAGNOSIS", color="DIAGNOSIS", title="rarefied", measures=c("Observed", "Chao1", "ACE", "Simpson", "Shannon")) +
geom_boxplot() +
theme_bw()

## stat sign diffrences in alpha?
##https://microbiome.github.io/course_2021_radboud/alpha-diversity.html
## https://rpubs.com/lconteville/713954
richness <- estimate_richness(kostic_crc_CSS)
head(richness)
## for normally distributed shannon index values
hist(richness$Shannon, breaks = 25)

shapiro_test <- shapiro.test(richness$Shannon)
shapiro_test
Shapiro-Wilk normality test
data: richness$Shannon
W = 0.95795, p-value = 3.802e-05
## almost normally distributed but because of the outliers it is not
anova.sh = aov(richness$Shannon ~ sample_data(kostic_crc_CSS)$DIAGNOSIS)
summary(anova.sh)
Df Sum Sq Mean Sq F value Pr(>F)
sample_data(kostic_crc_CSS)$DIAGNOSIS 1 7.53 7.527 17.31 4.97e-05 ***
Residuals 175 76.10 0.435
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
kruskal.test(richness$Shannon ~ sample_data(kostic_crc_CSS)$DIAGNOSIS)
Kruskal-Wallis rank sum test
data: richness$Shannon by sample_data(kostic_crc_CSS)$DIAGNOSIS
Kruskal-Wallis chi-squared = 16.548, df = 1, p-value = 4.743e-05
## because the alpha div is one number per sample we can append this to the metadata of
## our phyloseq object
## accounting for the multiple sampling from the same individial
# Load the nlme package
tail(sample_data(kostic_crc_CSS)$ANONYMIZED_NAME)
[1] "MQE9O" "32I9U" "UTWNW" "UTWNW" "BFJMK" "32I9U"
richness_exp <- cbind(richness, DIAGNOSIS = sample_data(kostic_crc_CSS)$DIAGNOSIS, ANONYMIZED_NAME = as.factor(sample_data(kostic_crc_CSS)$ANONYMIZED_NAME))
mixed_model <- lme(Shannon ~ DIAGNOSIS, data = richness_exp, random = ~ 1 | ANONYMIZED_NAME)
summary(mixed_model)
Linear mixed-effects model fit by REML
Data: richness_exp
Random effects:
Formula: ~1 | ANONYMIZED_NAME
(Intercept) Residual
StdDev: 0.3081981 0.5851293
Fixed effects: Shannon ~ DIAGNOSIS
Correlation:
(Intr)
DIAGNOSISTumor -0.618
Standardized Within-Group Residuals:
Min Q1 Med Q3 Max
-4.12130368 -0.46019741 0.05461757 0.57266976 2.52865584
Number of Observations: 177
Number of Groups: 94
## if you have experimental design where the group you check has more than two levels
## you will have to perform posthoc test in addition to anova and KW
## here is an online tutorial that can help with this
## https://scienceparkstudygroup.github.io/microbiome-lesson/04-alpha-diversity/index.html
## Faith's diversity or PD (Phylogenetic diversity) are based on phylogenetic information for each sample (which we don't have for this data)
# library(biomeUtils)
# data("FuentesIliGutData")
# # reduce size for example
# ps1 <- subset_samples(FuentesIliGutData, ILI == "C")
# ps1 <- prune_taxa(taxa_sums(ps1) > 0, ps1)
#
# meta_tib <- calculatePD(ps1, justDF = TRUE)
# # check
# meta_tib[c(1, 2, 3), c("PD", "SR")]
# #> PD SR
# #> sample_1 967.3003 259
# #> sample_2 1035.1305 291
# #> sample_3 1189.0248 336
## calculate Pielou’s evenness
data_evenness <- vegan::diversity(data.frame(otu_table(kostic_crc_CSS))) / log(specnumber(data.frame(otu_table(kostic_crc_CSS))))
head(data_evenness)
304309 469478 208196 358030 16076 35786
0.7162901 0.6620351 0.5970948 NaN 0.7280863 0.5032583
8. Estimate and plot the overall dissimilarity (beta diversity) in
the microbial composition between the groups
## beta diversity ##
dist_methods = unlist(distanceMethodList)
dist_methods
UniFrac1 UniFrac2 DPCoA JSD vegdist1 vegdist2 vegdist3
"unifrac" "wunifrac" "dpcoa" "jsd" "manhattan" "euclidean" "canberra"
vegdist4 vegdist5 vegdist6 vegdist7 vegdist8 vegdist9 vegdist10
"bray" "kulczynski" "jaccard" "gower" "altGower" "morisita" "horn"
vegdist11 vegdist12 vegdist13 vegdist14 vegdist15 betadiver1 betadiver2
"mountford" "raup" "binomial" "chao" "cao" "w" "-1"
betadiver3 betadiver4 betadiver5 betadiver6 betadiver7 betadiver8 betadiver9
"c" "wb" "r" "I" "e" "t" "me"
betadiver10 betadiver11 betadiver12 betadiver13 betadiver14 betadiver15 betadiver16
"j" "sor" "m" "-2" "co" "cc" "g"
betadiver17 betadiver18 betadiver19 betadiver20 betadiver21 betadiver22 betadiver23
"-3" "l" "19" "hk" "rlb" "sim" "gl"
betadiver24 dist1 dist2 dist3 designdist
"z" "maximum" "binary" "minkowski" "ANY"
## Jaccard and Bray-Curtis dissimilarities are non-phylogenetic measures that consider the presence/absence and abundance of features in samples, respectively, while UniFrac dissimilarities are phylogenetic measures that
## incorporate information about the evolutionary relatedness of microbial taxa
physeq2.ord <- phyloseq::ordinate(kostic_crc_CSS, "NMDS", "bray")
Square root transformation
Wisconsin double standardization
Run 0 stress 0.2315217
Run 1 stress 0.2350943
Run 2 stress 0.2275925
... New best solution
... Procrustes: rmse 0.04361084 max resid 0.327775
Run 3 stress 0.2321461
Run 4 stress 0.2386185
Run 5 stress 0.2289021
Run 6 stress 0.226593
... New best solution
... Procrustes: rmse 0.03804441 max resid 0.2846506
Run 7 stress 0.2309735
Run 8 stress 0.2328498
Run 9 stress 0.2342875
Run 10 stress 0.2322772
Run 11 stress 0.2336188
Run 12 stress 0.2452596
Run 13 stress 0.2404151
Run 14 stress 0.2344747
Run 15 stress 0.2329664
Run 16 stress 0.2321521
Run 17 stress 0.2311227
Run 18 stress 0.2300795
Run 19 stress 0.2349975
Run 20 stress 0.2359982
*** Best solution was not repeated -- monoMDS stopping criteria:
10: no. of iterations >= maxit
10: stress ratio > sratmax
## NMDS is non-linear and focuses on preserving rank order of dissimilarities, PCA is linear and focuses on capturing maximum variance,
## and PCoA can capture both linear and non-linear relationships and different types of distances measures
#shape is also a parameter we can assign metadata variable to
p = plot_ordination(kostic_crc_CSS, physeq2.ord, type="samples", color="DIAGNOSIS",
title="OTUs")
# Add ellipses
beta_div <- p + stat_ellipse(level = 0.95, type = "norm", geom = "polygon", alpha = 0, aes(color = DIAGNOSIS), size = 1) +
geom_point(size = 4) + # Adjust the size of points+
theme_bw() +
guides(size = "none") + #
theme(
text = element_text(size = 12, face = "bold"), # Make all text bold
axis.title = element_text(size = 14, face = "bold"), # Make axis titles bold
axis.text = element_text(size = 12), # Adjust axis text size
legend.title = element_text(size = 14, face = "bold"), # Make legend title bold
legend.text = element_text(size = 12), # Adjust legend text size
plot.title = element_text(size = 16, face = "bold") # Make plot title bold
)
beta_div

#save image
#ggsave("beta_diversity_plot_CSS.png", beta_div , width = 10, height = 6, dpi = 300)
## stat of the beta diversity
metadata = data.frame(sample_data(kostic_crc_CSS))
phy_beta = phyloseq::distance(kostic_crc_CSS, method = "bray")
adonis2(phy_beta ~ DIAGNOSIS, data = metadata, perm=999)
Permutation test for adonis under reduced model
Terms added sequentially (first to last)
Permutation: free
Number of permutations: 999
adonis2(formula = phy_beta ~ DIAGNOSIS, data = metadata, permutations = 999)
Df SumOfSqs R2 F Pr(>F)
DIAGNOSIS 1 1.434 0.02351 4.2126 0.001 ***
Residual 175 59.579 0.97649
Total 176 61.013 1.00000
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
## The adonis2 function fits a multivariate linear model to the distance matrix using the grouping variable (DIAGNOSIS) as a predictor.
## It then assesses the significance of the relationship between the grouping variable and the structure of the distance matrix through permutation testing.
## it assumes homogeneity of dispersion among groups
## A small p-value suggests that the grouping variable significantly explains the variation in the distance matrix,
## indicating differences in community composition or structure between the groups defined by the DIAGNOSIS variable
## betadisper function checks if the within groups dispersion are homogeneous(compositions vary similarly within the group)
bd = betadisper(phy_beta, metadata$'DIAGNOSIS')
anova(bd)
Analysis of Variance Table
Response: Distances
Df Sum Sq Mean Sq F value Pr(>F)
Groups 1 0.05661 0.056606 12.841 0.0004393 ***
Residuals 175 0.77141 0.004408
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
## perform the analyses correcting for multiple sampling from the same individual
## change the sample id to be a factor
# Extract sample data from the phyloseq object
sample_data <- sample_data(kostic_crc_CSS)
# write.csv(data.frame(sample_data), "sample_data.csv")
# Modify the variable of interest to be a factor
sample_data$ANONYMIZED_NAME <- as.factor(sample_data$ANONYMIZED_NAME)
levels(sample_data$ANONYMIZED_NAME)
[1] "32I9U" "38U4V" "41E1K" "59S9W" "5EKFO" "5TA9V" "6G2KB" "82S3M" "A8A34" "BFJMK" "C0095"
[12] "C0112" "C0133" "C0134" "C0149" "C0154" "C0159" "C0186" "C0195" "C0198" "C0206" "C0209"
[23] "C0211" "C0212" "C0225" "C0230" "C0235" "C0240" "C0241" "C0252" "C0256" "C0258" "C0268"
[34] "C0269" "C0270" "C0271" "C0275" "C0282" "C0285" "C0294" "C0306" "C0308" "C0311" "C0314"
[45] "C0315" "C0318" "C0322" "C0325" "C0330" "C0332" "C0333" "C0334" "C0335" "C0341" "C0342"
[56] "C0344" "C0349" "C0355" "C0362" "C0366" "C0371" "C0374" "C0378" "C0388" "C0394" "C0395"
[67] "C0398" "C0399" "C1" "C10" "C4" "C6" "G2OD5" "G2OD6" "G3UBQ" "GQ6LS" "HZIMM"
[78] "I7ROL" "JIDZE" "KIXFR" "LRQ9W" "MQE9O" "MQETM" "O436F" "OTGGZ" "QFHRS" "R8J9Z" "TV28I"
[89] "UEL2G" "UTWNW" "UZ65X" "XBS5M" "XZ33P" "YOTV6"
# Replace the modified sample data in the phyloseq object
sample_data(kostic_crc_CSS) <- sample_data
## check relationship between individuals
plot_ordination(kostic_crc_CSS, physeq2.ord, type="samples", color="ANONYMIZED_NAME",
title="OTUs")+
guides(color = FALSE) # no legend

# Extract the NMDS results from physeq2.ord
nmds_results <- scores(physeq2.ord)
# Combine the NMDS results with the sample IDs
plot_data <- cbind(sample_data(kostic_crc_CSS), nmds_results$sites)
# Plot the ordination
p <- ggplot(plot_data, aes(x = NMDS1, y = NMDS2, color = X.SampleID)) +
geom_point() +
labs(title = "OTUs") +
guides(color = FALSE)
p + geom_text(aes(label = ANONYMIZED_NAME), size =2.5, vjust = -0.5)

adonis2(phy_beta ~ metadata$DIAGNOSIS, data = metadata, perm=999, strata = metadata$ANONYMIZED_NAME)
Permutation test for adonis under reduced model
Terms added sequentially (first to last)
Blocks: strata
Permutation: free
Number of permutations: 999
adonis2(formula = phy_beta ~ metadata$DIAGNOSIS, data = metadata, permutations = 999, strata = metadata$ANONYMIZED_NAME)
Df SumOfSqs R2 F Pr(>F)
metadata$DIAGNOSIS 1 1.434 0.02351 4.2126 0.001 ***
Residual 175 59.579 0.97649
Total 176 61.013 1.00000
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
## try weighted unifrac for diagnosis
# phy_tree(kostic_crc_CSS)
# physeq2.ord1<- ordinate(kostic_crc_CSS, "NMDS", "wunifrac") #or unifrac
# we don't have the phy_tree
# but if we did, we could have also explored this beta_div measure
9. Identify DA taxa between groups
## microbiomeMarker is a wrapper for a lot of R packages specifically designed to
## profile DA taxa from microbial compositional data
## lefse ##
## KW detect stat sign features
## Wolcoxon and multigrp_strat test for consistency of stat sign across groups of replicates
## LDA analyses are finally performed to identify the exact effect size of the difference
## CPM is the default norm method for lefse ##
df_kostic_cpm =normalize(kostic_crc, method = "CPM")%>%microbiomeMarker::run_lefse(
wilcoxon_cutoff = 0.05,
norm = "none",
group = "DIAGNOSIS",
kw_cutoff = 0.05,
multigrp_strat = TRUE,
lda_cutoff = 1.5
)
df_kostic_cpm_table = (marker_table(df_kostic_cpm))
dim(df_kostic_cpm_table)
# 158 DA taxa across taxa levels
head(df_kostic_cpm_table)
#write the table
#write.csv(data.frame(df_kostic_cpm_table), "df_kostic_cpm_table_DA.csv")
## results are obtained not only for species but for all taxa levels
## We can't easily correct for co-founders or repeated measures with these analyses
### prepare the data for Maaslin2
## MaAsLin2 relies on general linear models to accommodate most modern epidemiological study designs,
## including cross-sectional and longitudinal, and offers a variety of data exploration, normalization, and transformation methods
## it allows correction for co-founders and repeated measures (correcting for intraindividual variability)
kostic_crc_CPM = microbiomeMarker::normalize(kostic_crc, method = "CPM")
crc_CPM_OTU = data.frame(otu_table(kostic_crc_CPM))
dim(crc_CPM_OTU)
head(crc_CPM_OTU)
crc_CPM_TAXA = data.frame((tax_table(kostic_crc_CPM)))
dim(tax_table(kostic_crc_CPM))
head(crc_CPM_TAXA)
crc_CPM_META = data.frame(sample_data(kostic_crc_CPM))
head(crc_CPM_META)
# These are how they should be
crc_CPM_TAXA = crc_CPM_TAXA %>% dplyr::mutate(Species1 = paste(rownames(crc_CPM_TAXA), Genus, Species, sep = "_"))
crc_CPM_TAXA = crc_CPM_TAXA %>% dplyr::select(Species1)
head(crc_CPM_TAXA)
head(crc_CPM_OTU[, 1:20])
# Add row names as a column in both data frames
crc_CPM_OTU <- rownames_to_column(crc_CPM_OTU, "RowNames")
crc_CPM_TAXA <- rownames_to_column(crc_CPM_TAXA, "RowNames")
# Merge the two data frames by the new column "RowNames"
crc_CPM_OTU_S <- merge(crc_CPM_OTU, crc_CPM_TAXA, by.x = "RowNames", by.y = "RowNames")
head(crc_CPM_OTU_S)
# Optionally, remove the redundant "RowNames" column after merging
crc_CPM_OTU_S <- crc_CPM_OTU_S %>% dplyr::select(-RowNames)
crc_CPM_OTU_S = column_to_rownames(crc_CPM_OTU_S, "Species1")
crc_CPM_OTU_S = data.frame(t(crc_CPM_OTU_S))
head(crc_CPM_OTU_S)
colnames(crc_CPM_OTU_S) <- sub("X", "", colnames(crc_CPM_OTU_S))
crc_CPM_META$ANONYMIZED_NAME = as.factor(crc_CPM_META$ANONYMIZED_NAME)
head(crc_CPM_OTU_S[1:20, 1:20])
head(crc_CPM_META[1:20, 1:20])
## We will need crc_CPM_OTU_S and crc_CPM_META
fit_data <- Maaslin2(
crc_CPM_OTU_S, crc_CPM_META, 'C://Users//Malina//Desktop//Microbial compositionality/maaslin_results_random_effect/',
fixed_effects = c('DIAGNOSIS'),
normalization = "NONE",
reference = c("DIAGNOSIS,Healthy"),
random_effects = c("ANONYMIZED_NAME")
)
## DA analyses of the same dataset with deseq2
## https://www.bioconductor.org/packages/release/bioc/vignettes/phyloseq/inst/doc/phyloseq-mixture-models.html
10. Get co-abundance interaction network for either Tumor or Healthy
samples
### will subset the initial CPM norm phyloseq obj to get only the Tumor samples and will
### generate graph from it
## will subset species that are present at least in 50% of the samples (filtering on prevalence)
## threshold for correlation will be 0.4 (spearman)
kostic_crc_CPM
phyloseq-class experiment-level object
otu_table() OTU Table: [ 2505 taxa and 177 samples ]
sample_data() Sample Data: [ 177 samples by 71 sample variables ]
tax_table() Taxonomy Table: [ 2505 taxa by 7 taxonomic ranks ]
subset_kostic_crc <- subset_samples(kostic_crc_CPM, DIAGNOSIS == "Tumor")
# Define the prevalence threshold (e.g., taxa present in at least 50% of samples)
prevalence_threshold <- 0.5
# Filter taxa based on prevalence
subset_kostic_crc <- filter_taxa(subset_kostic_crc , function(x) sum(x > 0) / length(x) >= prevalence_threshold, TRUE)
## create OTU input table with species or genus (when NA is present for species) names
crc_CPM_OTU = data.frame(otu_table(subset_kostic_crc))
dim(crc_CPM_OTU)
[1] 70 86
crc_CPM_TAXA = data.frame((tax_table(subset_kostic_crc)))
dim(tax_table(subset_kostic_crc))
[1] 70 7
crc_CPM_META = data.frame(sample_data(subset_kostic_crc))
head(crc_CPM_META)
# These are how they should be
crc_CPM_TAXA = crc_CPM_TAXA %>% dplyr::mutate(Species1= paste(rownames(crc_CPM_TAXA), Genus, Species, sep = "_"))
crc_CPM_TAXA = crc_CPM_TAXA %>% dplyr::select(Species1)
head(crc_CPM_TAXA)
head(crc_CPM_OTU[, 1:20])
# Add row names as a column in both data frames
crc_CPM_OTU <- rownames_to_column(crc_CPM_OTU, "RowNames")
crc_CPM_TAXA <- rownames_to_column(crc_CPM_TAXA, "RowNames")
# Merge the two data frames by the new column "RowNames"
crc_CPM_OTU_S <- merge(crc_CPM_OTU, crc_CPM_TAXA, by.x = "RowNames", by.y = "RowNames")
head(crc_CPM_OTU_S)
# Optionally, remove the redundant "RowNames" column after merging
crc_CPM_OTU_S <- crc_CPM_OTU_S %>% dplyr::select(-RowNames)
crc_CPM_OTU_S = column_to_rownames(crc_CPM_OTU_S, "Species1")
rownames(crc_CPM_OTU_S) <- sub("X", "", rownames(crc_CPM_OTU_S))
head(crc_CPM_OTU_S[,1:20])
[1] 70 86
### build unweighted graph
correlation_matrix <- cor(data.frame(t(crc_CPM_OTU_S)),method = "spearman")
head(correlation_matrix[,1:5])
X127309_Bacteroides_NA
X127309_Bacteroides_NA 1.00000000
X13825_Dialister_Dialister.pneumosintes -0.04489624
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius -0.12919516
X145149_Veillonella_NA 0.18438430
X15054_Granulicatella_Granulicatella.elegans -0.26004938
X157566_Alistipes_NA 0.33367864
X13825_Dialister_Dialister.pneumosintes
X127309_Bacteroides_NA -0.04489624
X13825_Dialister_Dialister.pneumosintes 1.00000000
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0.27232119
X145149_Veillonella_NA 0.02085916
X15054_Granulicatella_Granulicatella.elegans -0.13407975
X157566_Alistipes_NA 0.05705999
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius
X127309_Bacteroides_NA -0.12919516
X13825_Dialister_Dialister.pneumosintes 0.27232119
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 1.00000000
X145149_Veillonella_NA 0.18551504
X15054_Granulicatella_Granulicatella.elegans 0.04136805
X157566_Alistipes_NA -0.01130812
X145149_Veillonella_NA
X127309_Bacteroides_NA 0.18438430
X13825_Dialister_Dialister.pneumosintes 0.02085916
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0.18551504
X145149_Veillonella_NA 1.00000000
X15054_Granulicatella_Granulicatella.elegans -0.09595219
X157566_Alistipes_NA 0.10021371
X15054_Granulicatella_Granulicatella.elegans
X127309_Bacteroides_NA -0.26004938
X13825_Dialister_Dialister.pneumosintes -0.13407975
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0.04136805
X145149_Veillonella_NA -0.09595219
X15054_Granulicatella_Granulicatella.elegans 1.00000000
X157566_Alistipes_NA -0.16257067
threshold <- 0.4
# Create an adjacency matrix based on the correlation threshold
adjacency_matrix <- ifelse(abs(correlation_matrix) > threshold, 1, 0)
head(adjacency_matrix[,1:5])
X127309_Bacteroides_NA
X127309_Bacteroides_NA 1
X13825_Dialister_Dialister.pneumosintes 0
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0
X145149_Veillonella_NA 0
X15054_Granulicatella_Granulicatella.elegans 0
X157566_Alistipes_NA 0
X13825_Dialister_Dialister.pneumosintes
X127309_Bacteroides_NA 0
X13825_Dialister_Dialister.pneumosintes 1
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0
X145149_Veillonella_NA 0
X15054_Granulicatella_Granulicatella.elegans 0
X157566_Alistipes_NA 0
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius
X127309_Bacteroides_NA 0
X13825_Dialister_Dialister.pneumosintes 0
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 1
X145149_Veillonella_NA 0
X15054_Granulicatella_Granulicatella.elegans 0
X157566_Alistipes_NA 0
X145149_Veillonella_NA
X127309_Bacteroides_NA 0
X13825_Dialister_Dialister.pneumosintes 0
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0
X145149_Veillonella_NA 1
X15054_Granulicatella_Granulicatella.elegans 0
X157566_Alistipes_NA 0
X15054_Granulicatella_Granulicatella.elegans
X127309_Bacteroides_NA 0
X13825_Dialister_Dialister.pneumosintes 0
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0
X145149_Veillonella_NA 0
X15054_Granulicatella_Granulicatella.elegans 1
X157566_Alistipes_NA 0
# Remove the diagonal values
diag(adjacency_matrix) <- 0
head(adjacency_matrix[,1:5])
X127309_Bacteroides_NA
X127309_Bacteroides_NA 0
X13825_Dialister_Dialister.pneumosintes 0
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0
X145149_Veillonella_NA 0
X15054_Granulicatella_Granulicatella.elegans 0
X157566_Alistipes_NA 0
X13825_Dialister_Dialister.pneumosintes
X127309_Bacteroides_NA 0
X13825_Dialister_Dialister.pneumosintes 0
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0
X145149_Veillonella_NA 0
X15054_Granulicatella_Granulicatella.elegans 0
X157566_Alistipes_NA 0
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius
X127309_Bacteroides_NA 0
X13825_Dialister_Dialister.pneumosintes 0
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0
X145149_Veillonella_NA 0
X15054_Granulicatella_Granulicatella.elegans 0
X157566_Alistipes_NA 0
X145149_Veillonella_NA
X127309_Bacteroides_NA 0
X13825_Dialister_Dialister.pneumosintes 0
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0
X145149_Veillonella_NA 0
X15054_Granulicatella_Granulicatella.elegans 0
X157566_Alistipes_NA 0
X15054_Granulicatella_Granulicatella.elegans
X127309_Bacteroides_NA 0
X13825_Dialister_Dialister.pneumosintes 0
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius 0
X145149_Veillonella_NA 0
X15054_Granulicatella_Granulicatella.elegans 0
X157566_Alistipes_NA 0
graph <- graph_from_adjacency_matrix(adjacency_matrix ,
mode = "undirected")
graph
IGRAPH 99c429d UN-- 70 162 --
+ attr: name (v/c)
+ edges from 99c429d (vertex names):
[1] X127309_Bacteroides_NA --X203590_NA_NA
[2] X127309_Bacteroides_NA --X320768_Oscillospira_NA
[3] X127309_Bacteroides_NA --X470973_Ruminococcus_Ruminococcus.torques
[4] X13825_Dialister_Dialister.pneumosintes--X470239_Dialister_Dialister.invisus
[5] X13825_Dialister_Dialister.pneumosintes--X470805_NA_Parvimonas.micra
[6] X13825_Dialister_Dialister.pneumosintes--X500281_NA_NA
[7] X13825_Dialister_Dialister.pneumosintes--X89770_Peptostreptococcus_Peptostreptococcus.anaerobius
+ ... omitted several edges
#plot(graph)
#Cluster based on edge betweenness
ceb = cluster_edge_betweenness(graph)
#Extract community memberships from the clustering
community_membership <- membership(ceb)
head(community_membership)
X127309_Bacteroides_NA
1
X13825_Dialister_Dialister.pneumosintes
2
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius
2
X145149_Veillonella_NA
3
X15054_Granulicatella_Granulicatella.elegans
4
X157566_Alistipes_NA
5
[1] 22
# Count the number of nodes in each cluster
cluster_sizes <- table(community_membership)
cluster_sizes
community_membership
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
2 9 1 1 25 7 1 3 1 1 2 1 2 1 3 1 1 4 1 1 1 1
# Add cluster memberships as a vertex attribute
igraph::V(graph)$cluster_membership <- community_membership
# Map cluster memberships to colors using a color palette function
cluster_colors <- rainbow(max(community_membership))
head(cluster_colors)
[1] "#FF0000" "#FF4600" "#FF8B00" "#FFD100" "#E8FF00" "#A2FF00"
# Create a color variable based on cluster memberships
V(graph)$color_variable <- cluster_colors[community_membership]
# vetrex degree
vertex_degrees <- igraph::degree(graph)
head(vertex_degrees)
X127309_Bacteroides_NA
3
X13825_Dialister_Dialister.pneumosintes
4
X14227_Peptostreptococcus_Peptostreptococcus.anaerobius
4
X145149_Veillonella_NA
0
X15054_Granulicatella_Granulicatella.elegans
0
X157566_Alistipes_NA
6
# Add vertex degree as a vertex attribute
igraph::V(graph)$degree <- vertex_degrees
vertex.attr = list(
cluster_membership = V(graph)$cluster_membership,
name = V(graph)$name,
degrees = V(graph)$degree,
color = V(graph)$color_variable)
# Specify the file name for saving the GraphML file
graphml_file <- "T1graph_kostic_CPM_with_metadata_unweighted_undirected.graphml"
# Write the igraph object to GraphML format with metadata
write_graph(
graph,
graphml_file,
format = "graphml")
# Combine vertex attributes into a data frame
vertex_data <- data.frame(vertex.attr)
head(vertex_data)
write.csv(vertex_data, "T1vertex_data_kostic_CPM_with_metadata_unweighted_undirected.csv", row.names = FALSE)
## create a function for building co-abundance network
get_interaction_network = function(norm_phyloseq_obj, group, graphml_file, vertex_data_file){
subset_kostic_crc <- subset_samples(norm_phyloseq_obj, DIAGNOSIS == paste(group))
# Define the prevalence threshold (e.g., taxa present in at least 50% of samples)
prevalence_threshold <- 0.5
# Filter taxa based on prevalence
subset_kostic_crc <- filter_taxa(subset_kostic_crc , function(x) sum(x > 0) / length(x) >= prevalence_threshold, TRUE)
## create OTU input table with species or genus (when NA is present for species) names
crc_CPM_OTU = data.frame(otu_table(subset_kostic_crc))
crc_CPM_TAXA = data.frame((tax_table(subset_kostic_crc)))
crc_CPM_META = data.frame(sample_data(subset_kostic_crc))
# These are how they should be
crc_CPM_TAXA = crc_CPM_TAXA %>% dplyr::mutate(Species1= paste(rownames(crc_CPM_TAXA), Genus, Species, sep = "_"))
crc_CPM_TAXA = crc_CPM_TAXA %>% dplyr::select(Species1)
# Add row names as a column in both data frames
crc_CPM_OTU <- rownames_to_column(crc_CPM_OTU, "RowNames")
crc_CPM_TAXA <- rownames_to_column(crc_CPM_TAXA, "RowNames")
# Merge the two data frames by the new column "RowNames"
crc_CPM_OTU_S <- merge(crc_CPM_OTU, crc_CPM_TAXA, by.x = "RowNames", by.y = "RowNames")
# Optionally, remove the redundant "RowNames" column after merging
crc_CPM_OTU_S <- crc_CPM_OTU_S %>% dplyr::select(-RowNames)
crc_CPM_OTU_S = column_to_rownames(crc_CPM_OTU_S, "Species1")
rownames(crc_CPM_OTU_S) <- sub("X", "", rownames(crc_CPM_OTU_S))
### build unweighted graph
correlation_matrix <- cor(data.frame(t(crc_CPM_OTU_S)),method = "spearman")
threshold <- 0.4
# Create an adjacency matrix based on the correlation threshold
adjacency_matrix <- ifelse(abs(correlation_matrix) > threshold, 1, 0)
# Remove the diagonal values
diag(adjacency_matrix) <- 0
graph <- graph_from_adjacency_matrix(adjacency_matrix ,
mode = "undirected")
#Cluster based on edge betweenness
ceb = cluster_edge_betweenness(graph)
#Extract community memberships from the clustering
community_membership <- membership(ceb)
# Count the number of nodes in each cluster
cluster_sizes <- table(community_membership)
# Add cluster memberships as a vertex attribute
igraph::V(graph)$cluster_membership <- community_membership
# Map cluster memberships to colors using a color palette function
cluster_colors <- rainbow(max(community_membership))
# Create a color variable based on cluster memberships
V(graph)$color_variable <- cluster_colors[community_membership]
# vetrex degree
vertex_degrees <- igraph::degree(graph)
# Add vertex degree as a vertex attribute
igraph::V(graph)$degree <- vertex_degrees
vertex.attr = list(
cluster_membership = V(graph)$cluster_membership,
name = V(graph)$name,
degrees = V(graph)$degree,
color = V(graph)$color_variable)
# Specify the file name for saving the GraphML file
graphml_file <- graphml_file
# Write the igraph object to GraphML format with metadata
write_graph(
graph,
graphml_file,
format = "graphml")
# Combine vertex attributes into a data frame
vertex_data <- data.frame(vertex.attr)
write.csv(vertex_data, vertex_data_file, row.names = FALSE)
}
## run the function to obtain the graph file and vertex metadata for the Healthy samples
get_interaction_network(
norm_phyloseq_obj = kostic_crc_CPM,
group = "Healthy",
graphml_file = "H1graph_kostic_CPM_with_metadata_unweighted_undirected.graphml",
vertex_data_file = "H1vertex_data_kostic_CPM_with_metadata_unweighted_undirected.csv")
LS0tDQp0aXRsZTogIjE2UyBkb3duc3RyZWFtIGFuYWx5c2lzIGluIGNvbG9yZWN0YWwgY2FyY2lub21hIg0KYXV0aG9yOiAiTWFsaW5hIERveW5vdmEiDQpkYXRlOiAiNS8wMi8yMDI0Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRoZW1lOiBzaW1wbGV4ICMgQ2hhbmdlIHRoZSB0aGVtZSB0byAnZGFya2x5Jw0KICAgIGhpZ2hsaWdodDogdGFuZ28gIyBDaGFuZ2UgdGhlIGhpZ2hsaWdodCBzdHlsZQ0KICAgIHRvYzogeWVzICMgSW5jbHVkZSB0YWJsZSBvZiBjb250ZW50DQogICAgdG9jX2Zsb2F0OiBmYWxzZSAjIElmIHRvIGJlIGZsb2F0aW5nIG9yIG5vdCANCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQojIDEuICBJbnN0YWxsIGFuZC9vciBsb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcw0KDQpgYGB7cn0NCg0KIyBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpDQojIEJpb2NNYW5hZ2VyOjppbnN0YWxsKGMoInBoeWxvc2VxIiwgInZlZ2FuIiwgIm1pY3JvYmlvbWUiLCAiTWFhc2xpbjIiKSkNCiMgQmlvY01hbmFnZXI6Omluc3RhbGwoYygibWljcm9iaW9tZU1hcmtlciIpKQ0KIyBCaW9jTWFuYWdlcjo6aW5zdGFsbChjKCJnZ3Bsb3QyIiwgInRpZHl2ZXJzZSIsICJkcGx5ciIsICJkYXRhLnRhYmxlIikpDQoNCmxpYnJhcnkoInBoeWxvc2VxIikNCmxpYnJhcnkoInZlZ2FuIikNCmxpYnJhcnkoImdncGxvdDIiKQ0KbGlicmFyeSgibWljcm9iaW9tZU1hcmtlciIpDQpsaWJyYXJ5KCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSgiZHBseXIiKQ0KbGlicmFyeSgiZGF0YS50YWJsZSIpDQpsaWJyYXJ5KCJtaWNyb2Jpb21lIikNCmxpYnJhcnkoIk1hYXNsaW4yIikNCmxpYnJhcnkoImlncmFwaCIpDQpsaWJyYXJ5KCJSQ29sb3JCcmV3ZXIiKQ0KbGlicmFyeSgibmxtZSIpDQoNCmBgYA0KDQojIDIuIFNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkNCg0KYGBge3J9DQpzZXR3ZCgiQzovL1VzZXJzLy9NYWxpbmEvL0Rlc2t0b3AvL01pY3JvYmlhbCBjb21wb3NpdGlvbmFsaXR5IikNCmBgYA0KDQojIDMuIExvYWQgdGhlIGV4YW1wbGUgZGF0YXNldA0KDQpgYGB7ciwgZWNobyA9IFRSVUV9DQpkYXRhKGtvc3RpY19jcmMpDQprb3N0aWNfY3JjDQojIyBFeHBsb3JlIHRoZSBkYXRhc2V0DQpkaW0oc2FtcGxlX2RhdGEoa29zdGljX2NyYykpDQpoZWFkKHNhbXBsZV9kYXRhKGtvc3RpY19jcmMpKQ0KDQp0YWJsZShzYW1wbGVfZGF0YShrb3N0aWNfY3JjKSRESUFHTk9TSVMpDQoNCmhlYWQodGF4X3RhYmxlKGtvc3RpY19jcmMpKQ0KDQpoZWFkKG90dV90YWJsZShrb3N0aWNfY3JjKVssMToyMF0pDQpgYGANCg0KIyA0LiBGdW5jdGlvbiB0byBjcmVhdGUgYSBwaHlsb3NlcSBvYmplY3QgZnJvbSBkYXRhIGZpbGVzDQoNCmBgYHtyfQ0KDQpjcmVhdGVfcGh5bG9zZXEgPC0gZnVuY3Rpb24ob3R1X2ZpbGUsIHNhbXBsZV9maWxlLCB0YXhfZmlsZSkgew0KICAjIExvYWQgT1RVIHRhYmxlDQogIG90dV9kYXRhIDwtIHJlYWQuY3N2KG90dV9maWxlLCByb3cubmFtZXMgPSAxLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQogIG90dV9tYXRyaXggPC0gYXMubWF0cml4KG90dV9kYXRhKQ0KICANCiAgIyBMb2FkIHNhbXBsZSBkYXRhDQogIHNhbXBsZV9kYXRhIDwtIHJlYWQuY3N2KHNhbXBsZV9maWxlLCByb3cubmFtZXMgPSAxKQ0KICANCiAgIyBMb2FkIHRheG9ub21pYyB0YWJsZQ0KICB0YXhfZGF0YSA8LSByZWFkLmNzdih0YXhfZmlsZSwgcm93Lm5hbWVzID0gMSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KICB0YXhfdGFibGUgPC0gdGF4X2RhdGFbLCAxOjddICAjIEFzc3VtaW5nIHRheG9ub21pYyByYW5rcyBhcmUgaW4gY29sdW1ucyAxIHRvIDcNCiAgDQogICMgQ3JlYXRlIHBoeWxvc2VxIG9iamVjdA0KICBwaHlzZXFfb2JqIDwtIHBoeWxvc2VxKG90dV90YWJsZShvdHVfbWF0cml4LCB0YXhhX2FyZV9yb3dzID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2RhdGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgdGF4X3RhYmxlKQ0KICANCiAgcmV0dXJuKHBoeXNlcV9vYmopDQp9DQoNCiMgdGhpcyBpcyBob3cgdGhlIGZ1bmN0aW9uIGlzIHJ1bg0KDQojICMgUHJvdmlkZSBmaWxlIHBhdGhzDQojIG90dV9maWxlIDwtICJwYXRoL3RvL290dV90YWJsZS5jc3YiDQojIHNhbXBsZV9maWxlIDwtICJwYXRoL3RvL3NhbXBsZV9kYXRhLmNzdiINCiMgdGF4X2ZpbGUgPC0gInBhdGgvdG8vdGF4b25vbWljX3RhYmxlLmNzdiINCiMgDQojICMgQ3JlYXRlIHBoeWxvc2VxIG9iamVjdA0KIyBwaHlzZXFfb2JqIDwtIGNyZWF0ZV9waHlsb3NlcShvdHVfZmlsZSwgc2FtcGxlX2ZpbGUsIHRheF9maWxlKQ0KDQoNCmBgYA0KIyA1LiBOb3JtYWxpemF0aW9uIG9mIHRoZSBzYW1wbGUgY291bnRzDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQojIyBjaGVjayBob3cgbGFyZ2UgdGhlIHNhbXBsZSBzaXplcyAobnVtYmVyIG9mIGNvdW50cykgYXJlIA0KDQptYXhfZGlmZmVyZW5jZSA9IG1heChzYW1wbGVfc3Vtcyhrb3N0aWNfY3JjKSkvbWluKHNhbXBsZV9zdW1zKGtvc3RpY19jcmMpKQ0KbWF4X2RpZmZlcmVuY2UNCg0KbWF4KHNhbXBsZV9zdW1zKGtvc3RpY19jcmMpKQ0KbWluKHNhbXBsZV9zdW1zKGtvc3RpY19jcmMpKQ0KDQojIyBub3JtIGlzIG9ubHkgbmVlZGVkIGlmIHNhbXBsZSBzaXplcyBhcmUgbW9yZSB0aGFuIDEweCBkaWZmZXJlbnQgKGFuZCB0aGV5IGFyZSkNCg0KIyMgZGlzdHJpYnV0aW9uIG9mIHRoZSBzYW1wbGUgc2l6ZXMNCmhpc3Qoc2FtcGxlX3N1bXMoa29zdGljX2NyYyksIGJyZWFrcyA9IDE3NykNCg0KDQojIyBnZXQgcmVsYXRpdmUgYWJ1bmRhbmNlIHdpdGggdGhlIG1pY3JvYmlvbWUgcGFja2FnZQ0Ka29zdGljX2NyYy5jb21wb3NpdGlvbmFsIDwtIG1pY3JvYmlvbWU6OnRyYW5zZm9ybShrb3N0aWNfY3JjLCAiY29tcG9zaXRpb25hbCIpDQoNCmhlYWQob3R1X3RhYmxlKGtvc3RpY19jcmMuY29tcG9zaXRpb25hbClbLDE6MjBdKQ0KDQojIyBnZXQgcmVsYXRpdmUgYWJ1bmRhbmNlIHdpdGggdGhlIG1pY3JvYmlvbWVNYXJrZXIgcGFja2FnZQ0Ka29zdGljX2NyY19UU1MgPSBtaWNyb2Jpb21lTWFya2VyOjpub3JtYWxpemUoa29zdGljX2NyYywgbWV0aG9kPSJUU1MiKQ0KaGVhZChvdHVfdGFibGUoa29zdGljX2NyY19UU1MpWywxOjIwXSkNCg0KIyMgb3V0cHV0IG9mIGJvdGggZnJvbSBhYm92ZSBhcmUgcmVsYXRpdmUgYWJ1bmRhbmNlcw0KDQojIyBjc3Mgbm9ybSAob3JpZ2luYWxseSBmcm9tIG1ldGFnZW5vbWVTZXEgcGFja2VnZSkgIyMgDQojIEN1bXVsYXRpdmUgU3VtIFNjYWxpbmcgKENTUykgaXMgYSBtZWRpYW4tbGlrZSBxdWFudGlsZSBub3JtYWxpemF0aW9uIHdoaWNoIGNvcnJlY3RzIGRpZmZlcmVuY2VzIGluIHNhbXBsaW5nIGRlcHRoIChsaWJyYXJ5IHNpemUpLg0KIyBXaGlsZSBzdGFuZGFyZCByZWxhdGl2ZSBhYnVuZGFuY2UgKGZyYWN0aW9uL3BlcmNlbnRhZ2UpIG5vcm1hbGl6YXRpb24gcmUtc2NhbGVzIGFsbCBzYW1wbGVzIHRvIHRoZSBzYW1lIHRvdGFsIHN1bSAoMTAwJSksIENTUyBrZWVwcyBhIHZhcmlhdGlvbiBpbiB0b3RhbCBjb3VudHMgYmV0d2VlbiBzYW1wbGVzLiANCiMgQ1NTIHJlLXNjYWxlcyB0aGUgc2FtcGxlcyBiYXNlZCBvbiBhIHN1YnNldCAocXVhcnRpbGUpIG9mIGxvd2VyIGFidW5kYW50IHRheGEgDQojKHJlbGF0aXZlbHkgY29uc3RhbnQgYW5kIGluZGVwZW5kZW50KSwgDQojIHRoZXJlYnkgZXhjbHVkaW5nIHRoZSBpbXBhY3Qgb2YgKHN0dWR5IGRvbWluYXRpbmcpIGhpZ2ggYWJ1bmRhbnQgdGF4YS4NCg0Ka29zdGljX2NyY19DU1MgPSBtaWNyb2Jpb21lTWFya2VyOjpub3JtYWxpemUoa29zdGljX2NyYywgbWV0aG9kPSJDU1MiKQ0KaGVhZChvdHVfdGFibGUoa29zdGljX2NyY19DU1MpWywxOjIwXSkNCg0KIyMgY3BtIG5vcm0gb3JpZ2luYWxseSBsZWZzZSBwYWNrYWdlICMjIw0Ka29zdGljX2NyY19DUE0gPSBtaWNyb2Jpb21lTWFya2VyOjpub3JtYWxpemUoa29zdGljX2NyYywgbWV0aG9kPSJDUE0iKQ0KDQpoZWFkKG90dV90YWJsZShrb3N0aWNfY3JjX0NQTSlbLDE6MjBdKQ0KI3RhaWwob3R1X3RhYmxlKGtvc3RpY19jcmNfQ1NTKSkNCg0KDQojIyByYXJlZnkgbm9ybSAjIyMNCiMjIGZyb20gbWljcm9iaW9tZU1hcmtlciBwYWNrYWdlICMjDQojIA0KIyBrb3N0aWNfY3JjX3JhcmUgPSBtaWNyb2Jpb21lTWFya2VyOjpub3JtYWxpemUoa29zdGljX2NyYywgbWV0aG9kPSJyYXJlZnkiKQ0KIyANCiMgaGVhZChvdHVfdGFibGUoa29zdGljX2NyY19yYXJlKVssMToyMF0pDQoNCiMjIGZyb20gcGh5bG9zZXEgcGFja2FnZSANCg0KcHMucmFyZWZpZWQgPSBwaHlsb3NlcTo6cmFyZWZ5X2V2ZW5fZGVwdGgoa29zdGljX2NyYywgcm5nc2VlZD0xMjMsIHNhbXBsZS5zaXplPTUyNCwgcmVwbGFjZT1GKQ0KDQoNCmBgYA0KDQoNCiMgNi4gUGxvdCBzdGFjayBiYXIgcGxvdHMgb2YgdGhlIHRvcCAxNSB0YXhhIGJhc2VkIG9uIGFidW5kDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQojIyAgZXh0cmFjdCB0aGUgdG9wIDE1IHRheGEgb24gdGhpcyB0YXhhIGxldmVsIGZvciBDU1MgDQp0b3BfcGh5ID0gdGF4X2dsb20oa29zdGljX2NyY19DU1MsICJHZW51cyIpDQp0b3AxNSA9IG5hbWVzKHNvcnQodGF4YV9zdW1zKHRvcF9waHkpLCBkZWNyZWFzaW5nPVRSVUUpWzE6MTVdKQ0KdG9wMTVfY3NzX3BzID0gcHJ1bmVfdGF4YSh0b3AxNSwgdG9wX3BoeSkNCg0KDQpwaHlsb3NlcTo6cGxvdF9iYXIodG9wMTVfY3NzX3BzLCAiRElBR05PU0lTIiwgZmlsbD0iR2VudXMiKSsNCiAgdGhlbWUoIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCksIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGw9TkEpLCBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXI9IiNlYmViZWIiKSkgKyANCiAgbGFicyh4PU5VTEwpICANCg0KIyMgZ2V0IHJpZCBvZiB0aGUgYmxhY2sgc3F1YXJlcyBhbmQgZ2V0IGJldHRlciBjb2xvcnMNCnBsb3RfYmFyKHRvcDE1X2Nzc19wcywgIkRJQUdOT1NJUyIsIGZpbGw9IkdlbnVzIikrDQogIHRoZW1lKCBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLCBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsPU5BKSwgcGFuZWwuZ3JpZC5tYWpvcj1lbGVtZW50X2xpbmUoY29sb3VyPSIjZWJlYmViIikpICsgDQogIGxhYnMoeD1OVUxMKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgDQoNCg0KIyMgR2VuZXJhdGUgMTUgZGlzdGluY3QgY29sb3JzIGZyb20gdGhlIHBhbGV0dGUNCg0KY29sb3IgPSBnckRldmljZXM6OmNvbG9ycygpW2dyZXAoJ2dyKGF8ZSl5JywgZ3JEZXZpY2VzOjpjb2xvcnMoKSwgaW52ZXJ0ID0gVCldDQpzYW1wbGVkX2NvbG9ycyA8LSBzYW1wbGUoY29sb3IsIDE1KQ0KDQpwbG90X2Jhcih0b3AxNV9jc3NfcHMsICJESUFHTk9TSVMiLCBmaWxsID0gIkdlbnVzIikgKw0KICB0aGVtZShheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BKSwgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiI2ViZWJlYiIpKSArDQogIGxhYnMoeCA9IE5VTEwpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGFlcyhmaWxsID0gR2VudXMpKSArICAjIFNwZWNpZnkgZmlsbCBhZXN0aGV0aWMgaGVyZQ0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVkX2NvbG9ycyApICAjIFVzZSBzY2FsZV9maWxsX21hbnVhbCBmb3IgZmlsbCBjb2xvcnMNCg0KIyMgcGxvdCBlYWNoIGdlbnVzIGluIGEgc2VwYXJhdGUgZmFjZXQNCg0KIyBwbG90X2Jhcih0b3AxNV9jc3NfcHMsICJESUFHTk9TSVMiLCBmaWxsPSJESUFHTk9TSVMiLCBmYWNldF9ncmlkPX5HZW51cykgICsgIyAjICNnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpDQoNCiMjIHBsb3QgdG9wIDUgIyMNCg0KdG9wX3BoeTUgPSB0YXhfZ2xvbShrb3N0aWNfY3JjX0NTUywgIkdlbnVzIikNCnRvcDUgPSBuYW1lcyhzb3J0KHRheGFfc3Vtcyh0b3BfcGh5NSksIGRlY3JlYXNpbmc9VFJVRSlbMTo1XSkNCnRvcDVfY3NzX3BzID0gcHJ1bmVfdGF4YSh0b3A1LCB0b3BfcGh5NSkNCg0KdGF4X3RhYmxlKHRvcDVfY3NzX3BzKQ0KDQojIyBwbG90IGVhY2ggZ2VudXMgaW4gYSBzZXBhcmF0ZSBmYWNldA0KcGxvdF9iYXIodG9wNV9jc3NfcHMsICJESUFHTk9TSVMiLCBmaWxsPSJESUFHTk9TSVMiLCBmYWNldF9ncmlkPX5HZW51cykgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpDQoNCiMjIGNoYW5naW5nIHRoZSBhcHBlYXJhbmNlIA0KIyMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNjMxMzM4ODQvcmVtb3ZpbmctdGhlLW9yaWdpbmFsLWNvbG9yLW91dGxpbmUtaW4tci13aGVuLXVzaW5nLWEtbmV3LXBhbGxldHRlLWluLWEtYmFycGxvdA0KIyMgcmVtb3ZlIGJsYWNrIGJveGVzIA0KIyMgaHR0cHM6Ly9naXRodWIuY29tL2pvZXk3MTEvcGh5bG9zZXEvaXNzdWVzLzcyMQ0KDQojIyAgZXh0cmFjdCB0aGUgdG9wIDE1IHRheGEgb24gdGhpcyB0YXhhIGxldmVsIGZvciBUU1MNCnRvcF9waHkgPSB0YXhfZ2xvbShrb3N0aWNfY3JjX1RTUywgIkdlbnVzIikNCnRvcDE1ID0gbmFtZXMoc29ydCh0YXhhX3N1bXModG9wX3BoeSksIGRlY3JlYXNpbmc9VFJVRSlbMToxNV0pDQp0b3AxNV90c3NfcHMgPSBwcnVuZV90YXhhKHRvcDE1LCB0b3BfcGh5KQ0KDQoNCiMgcHJlcGFyZSB0byBjcmVhdGUgYSBuaWNlIHBsb3QgZm9yIHNhdmluZw0KdG9wMTVfdHNzX0cgID1wbG90X2Jhcih0b3AxNV90c3NfcHMsICJESUFHTk9TSVMiLCBmaWxsPSJHZW51cyIpKw0KICB0aGVtZSggYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbD1OQSksIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG91cj0iI2ViZWJlYiIpKSArIA0KICBsYWJzKHg9TlVMTCkrDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBhZXMoZmlsbCA9IEdlbnVzKSkgKyAgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHNhbXBsZWRfY29sb3JzICkgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoDQogICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLCAgIyBNYWtlIGFsbCB0ZXh0IGJvbGQNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIiksICAjIE1ha2UgYXhpcyB0aXRsZXMgYm9sZA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAgIyBBZGp1c3QgYXhpcyB0ZXh0IHNpemUNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwgICMgTWFrZSBsZWdlbmQgdGl0bGUgYm9sZA0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAjIEFkanVzdCBsZWdlbmQgdGV4dCBzaXplDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpICAjIE1ha2UgcGxvdCB0aXRsZSBib2xkDQogICkNCg0KICANCnRvcDE1X3Rzc19HDQoNCiNzYXZlIHRoZSBpbWFnZQ0KI2dnc2F2ZSgidG9wMTVfR2VuZXJhX3Bsb3RfVFNTLnBuZyIsIHRvcDE1X3Rzc19HLCB3aWR0aCA9IDYsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkNCg0KIyBwbG90IHJhcmVmYWN0aW9uIGN1cnZlIA0KIyBFeHRyYWN0IE9UVSBhYnVuZGFuY2UgZGF0YSBhbmQgdHJhbnNwb3NlDQojIG90dV90YWJsZV90cmFuc3Bvc2VkPC0gYXMuZGF0YS5mcmFtZSh0KG90dV90YWJsZShrb3N0aWNfY3JjKSkpDQojIGhlYWQob3R1X3RhYmxlX3RyYW5zcG9zZWQpDQojIHJhcmVjdXJ2ZShhcy5tYXRyaXgob3R1X3RhYmxlX3RyYW5zcG9zZWQpLCBzdGVwPTUwMCwgY2V4PTAuMSkNCg0KYGBgDQojIDcuIFBsb3QgYWxwaGEgZGl2ZXJzaXR5IGJldHdlZW4gZ3JvdXBzDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQoNCiMjIGVzdGltYXRpb24gb2YgYWxwaGEgZGl2IG9ubHkgd29ya3Mgd2l0aCBpbnRlZ2VycyBhbmQgbm90IHJlbCBhYnVuZCB2YWx1ZXMgKGZsb2F0IG51bWJlcnMpDQojIyB3aGF0IGVhY2ggb2YgdGhlIGFscGhhIGRpdiBtZWFucyANCiMjIE9ic2VydmVkIC0gY291bnRzIHRoZSBudW1iZXIgb2YgdW5pcXVlIHNwZWNpZXMgKE9UVXMgb3IgQVNWcykgcHJlc2VudCBpbiBhIHNhbXBsZQ0KIyMgQ2hhbzEgLSAgSXQgdGFrZXMgaW50byBhY2NvdW50IHRoZSBudW1iZXIgb2YgcmFyZSBzcGVjaWVzIChzaW5nbGV0b25zIGFuZCBkb3VibGV0b25zKSBvYnNlcnZlZCBpbiB0aGUgc2FtcGxlIGFuZCBleHRyYXBvbGF0ZXMgdGhlIHRvdGFsIHJpY2huZXNzIGJhc2VkIG9uIHRoZSBmcmVxdWVuY3kgb2YgdGhlc2UgcmFyZSBzcGVjaWVzDQojIyBBQ0UgLSAgVGFrZXMgaW50byBhY2NvdW50IGJvdGggdGhlIG51bWJlciBvZiByYXJlIHNwZWNpZXMgb2JzZXJ2ZWQgYW5kIHRoZWlyIHJlbGF0aXZlIGFidW5kYW5jZXMuIGluZmxhdGVzIHRoZSBudW1iZXIgb2YgcmFyZSB0YXhhIGFuZCBpbmZsYXRlcyBhZ2FpbiB0aGUgbnVtYmVyIG9mIHRheGEgd2l0aCBhYnVuZGFuY2UgMS4NCiMjIFNoYW5ub24ncyBkaXZlcnNpdHkgaW5kZXggKGFsc28ga25vd24gYXMgU2hhbm5vbi1XaWVuZXIgaW5kZXgpIGNvbnNpZGVycyBib3RoIHNwZWNpZXMgcmljaG5lc3MgYW5kIGV2ZW5uZXNzIGluIGEgY29tbXVuaXR5LiBJdCB0YWtlcyBpbnRvIGFjY291bnQgdGhlIG51bWJlciBvZiBkaWZmZXJlbnQgc3BlY2llcyBwcmVzZW50IGFzIHdlbGwgYXMgdGhlIHJlbGF0aXZlIGFidW5kYW5jZSBvZiBlYWNoIHNwZWNpZXMuDQojIyBTaW1wc29uJ3MgZGl2ZXJzaXR5IGluZGV4IG1lYXN1cmVzIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHR3byByYW5kb21seSBzZWxlY3RlZCBzZXF1ZW5jZXMgYXJlIG9mIHRoZSBzYW1lIHNwZWNpZXMNCg0KDQojIyBDU1MgIyMNCmFscGhhX2RpdiA9IHBsb3RfcmljaG5lc3Moa29zdGljX2NyY19DU1MsIHg9IkRJQUdOT1NJUyIsIGNvbG9yPSJESUFHTk9TSVMiLCB0aXRsZT0iQ1NTIiwgbWVhc3VyZXM9YygiT2JzZXJ2ZWQiLCAiQ2hhbzEiLCAiQUNFIiwgIlNpbXBzb24iLCAiU2hhbm5vbiIpKSArIA0KICBnZW9tX2JveHBsb3QoKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZSgNCiAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksICAjIE1ha2UgYWxsIHRleHQgYm9sZA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwgICMgTWFrZSBheGlzIHRpdGxlcyBib2xkDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAjIEFkanVzdCBheGlzIHRleHQgc2l6ZQ0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLCAgIyBNYWtlIGxlZ2VuZCB0aXRsZSBib2xkDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgQWRqdXN0IGxlZ2VuZCB0ZXh0IHNpemUNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIikgICMgTWFrZSBwbG90IHRpdGxlIGJvbGQNCiAgKQ0KDQphbHBoYV9kaXYNCg0KI3NhdmUgdGhlIGltYWdlDQojZ2dzYXZlKCJhbHBoYV9kaXZlcnNpdHlfcGxvdF9DU1MucG5nIiwgYWxwaGFfZGl2LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA2LCBkcGkgPSAzMDApDQoNCiMjIHJhcmVmaWVzICMjDQoNCnBsb3RfcmljaG5lc3MoIHBzLnJhcmVmaWVkLCB4PSJESUFHTk9TSVMiLCBjb2xvcj0iRElBR05PU0lTIiwgdGl0bGU9InJhcmVmaWVkIiwgbWVhc3VyZXM9YygiT2JzZXJ2ZWQiLCAiQ2hhbzEiLCAiQUNFIiwgIlNpbXBzb24iLCAiU2hhbm5vbiIpKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICB0aGVtZV9idygpIA0KDQojIyBzdGF0IHNpZ24gZGlmZnJlbmNlcyBpbiBhbHBoYT8NCiMjaHR0cHM6Ly9taWNyb2Jpb21lLmdpdGh1Yi5pby9jb3Vyc2VfMjAyMV9yYWRib3VkL2FscGhhLWRpdmVyc2l0eS5odG1sDQoNCiMjIGh0dHBzOi8vcnB1YnMuY29tL2xjb250ZXZpbGxlLzcxMzk1NA0KcmljaG5lc3MgPC0gZXN0aW1hdGVfcmljaG5lc3Moa29zdGljX2NyY19DU1MpDQpoZWFkKHJpY2huZXNzKQ0KDQojIyBmb3Igbm9ybWFsbHkgZGlzdHJpYnV0ZWQgc2hhbm5vbiBpbmRleCB2YWx1ZXMgDQpoaXN0KHJpY2huZXNzJFNoYW5ub24sIGJyZWFrcyA9ICAyNSkNCnNoYXBpcm9fdGVzdCA8LSBzaGFwaXJvLnRlc3QocmljaG5lc3MkU2hhbm5vbikNCnNoYXBpcm9fdGVzdA0KIyMgYWxtb3N0IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGJ1dCBiZWNhdXNlIG9mIHRoZSBvdXRsaWVycyBpdCBpcyBub3QgDQphbm92YS5zaCA9IGFvdihyaWNobmVzcyRTaGFubm9uIH4gc2FtcGxlX2RhdGEoa29zdGljX2NyY19DU1MpJERJQUdOT1NJUykNCnN1bW1hcnkoYW5vdmEuc2gpDQoNCmtydXNrYWwudGVzdChyaWNobmVzcyRTaGFubm9uIH4gc2FtcGxlX2RhdGEoa29zdGljX2NyY19DU1MpJERJQUdOT1NJUykNCg0KIyMgYmVjYXVzZSB0aGUgYWxwaGEgZGl2IGlzIG9uZSBudW1iZXIgcGVyIHNhbXBsZSB3ZSBjYW4gYXBwZW5kIHRoaXMgdG8gdGhlIG1ldGFkYXRhIG9mIA0KIyMgb3VyIHBoeWxvc2VxIG9iamVjdCANCg0KIyMgYWNjb3VudGluZyBmb3IgdGhlIG11bHRpcGxlIHNhbXBsaW5nIGZyb20gdGhlIHNhbWUgaW5kaXZpZGlhbA0KIyBMb2FkIHRoZSBubG1lIHBhY2thZ2UNCg0KdGFpbChzYW1wbGVfZGF0YShrb3N0aWNfY3JjX0NTUykkQU5PTllNSVpFRF9OQU1FKQ0KDQpyaWNobmVzc19leHAgPC0gY2JpbmQocmljaG5lc3MsIERJQUdOT1NJUyA9IHNhbXBsZV9kYXRhKGtvc3RpY19jcmNfQ1NTKSRESUFHTk9TSVMsIEFOT05ZTUlaRURfTkFNRSA9IGFzLmZhY3RvcihzYW1wbGVfZGF0YShrb3N0aWNfY3JjX0NTUykkQU5PTllNSVpFRF9OQU1FKSkNCg0KbWl4ZWRfbW9kZWwgPC0gbG1lKFNoYW5ub24gfiBESUFHTk9TSVMsIGRhdGEgPSByaWNobmVzc19leHAsIHJhbmRvbSA9IH4gMSB8IEFOT05ZTUlaRURfTkFNRSkNCg0Kc3VtbWFyeShtaXhlZF9tb2RlbCkNCg0KDQojIyBpZiB5b3UgaGF2ZSBleHBlcmltZW50YWwgZGVzaWduIHdoZXJlIHRoZSBncm91cCB5b3UgY2hlY2sgaGFzIG1vcmUgdGhhbiB0d28gbGV2ZWxzDQojIyB5b3Ugd2lsbCBoYXZlIHRvIHBlcmZvcm0gcG9zdGhvYyB0ZXN0IGluIGFkZGl0aW9uIHRvIGFub3ZhIGFuZCBLVw0KDQojIyBoZXJlIGlzIGFuIG9ubGluZSB0dXRvcmlhbCB0aGF0IGNhbiBoZWxwIHdpdGggdGhpcw0KIyMgaHR0cHM6Ly9zY2llbmNlcGFya3N0dWR5Z3JvdXAuZ2l0aHViLmlvL21pY3JvYmlvbWUtbGVzc29uLzA0LWFscGhhLWRpdmVyc2l0eS9pbmRleC5odG1sDQoNCg0KIyMgRmFpdGgncyBkaXZlcnNpdHkgb3IgUEQgKFBoeWxvZ2VuZXRpYyBkaXZlcnNpdHkpIGFyZSBiYXNlZCBvbiBwaHlsb2dlbmV0aWMgaW5mb3JtYXRpb24gZm9yIGVhY2ggc2FtcGxlICh3aGljaCB3ZSBkb24ndCBoYXZlIGZvciB0aGlzIGRhdGEpDQoNCiMgbGlicmFyeShiaW9tZVV0aWxzKQ0KIyBkYXRhKCJGdWVudGVzSWxpR3V0RGF0YSIpDQojICMgcmVkdWNlIHNpemUgZm9yIGV4YW1wbGUNCiMgcHMxIDwtIHN1YnNldF9zYW1wbGVzKEZ1ZW50ZXNJbGlHdXREYXRhLCBJTEkgPT0gIkMiKQ0KIyBwczEgPC0gcHJ1bmVfdGF4YSh0YXhhX3N1bXMocHMxKSA+IDAsIHBzMSkNCiMgDQojIG1ldGFfdGliIDwtIGNhbGN1bGF0ZVBEKHBzMSwganVzdERGID0gVFJVRSkNCiMgIyBjaGVjaw0KIyBtZXRhX3RpYltjKDEsIDIsIDMpLCBjKCJQRCIsICJTUiIpXQ0KIyAjPiAgICAgICAgICAgICAgICAgUEQgIFNSDQojICM+IHNhbXBsZV8xICA5NjcuMzAwMyAyNTkNCiMgIz4gc2FtcGxlXzIgMTAzNS4xMzA1IDI5MQ0KIyAjPiBzYW1wbGVfMyAxMTg5LjAyNDggMzM2DQoNCiMjIGNhbGN1bGF0ZSBQaWVsb3XigJlzIGV2ZW5uZXNzDQpkYXRhX2V2ZW5uZXNzIDwtIHZlZ2FuOjpkaXZlcnNpdHkoZGF0YS5mcmFtZShvdHVfdGFibGUoa29zdGljX2NyY19DU1MpKSkgLyBsb2coc3BlY251bWJlcihkYXRhLmZyYW1lKG90dV90YWJsZShrb3N0aWNfY3JjX0NTUykpKSkNCmhlYWQoZGF0YV9ldmVubmVzcykNCg0KYGBgDQoNCiMgOC4gRXN0aW1hdGUgYW5kIHBsb3QgdGhlIG92ZXJhbGwgZGlzc2ltaWxhcml0eSAoYmV0YSBkaXZlcnNpdHkpIGluIHRoZSBtaWNyb2JpYWwgY29tcG9zaXRpb24gYmV0d2VlbiB0aGUgZ3JvdXBzDQoNCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2UgPSBGQUxTRX0NCg0KIyMgYmV0YSBkaXZlcnNpdHkgIyMNCmRpc3RfbWV0aG9kcyA9IHVubGlzdChkaXN0YW5jZU1ldGhvZExpc3QpDQpkaXN0X21ldGhvZHMNCg0KIyMgSmFjY2FyZCBhbmQgQnJheS1DdXJ0aXMgZGlzc2ltaWxhcml0aWVzIGFyZSBub24tcGh5bG9nZW5ldGljIG1lYXN1cmVzIHRoYXQgY29uc2lkZXIgdGhlIHByZXNlbmNlL2Fic2VuY2UgYW5kIGFidW5kYW5jZSBvZiBmZWF0dXJlcyBpbiBzYW1wbGVzLCByZXNwZWN0aXZlbHksIHdoaWxlIFVuaUZyYWMgZGlzc2ltaWxhcml0aWVzIGFyZSBwaHlsb2dlbmV0aWMgbWVhc3VyZXMgdGhhdCANCiMjIGluY29ycG9yYXRlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBldm9sdXRpb25hcnkgcmVsYXRlZG5lc3Mgb2YgbWljcm9iaWFsIHRheGENCg0KcGh5c2VxMi5vcmQgPC0gcGh5bG9zZXE6Om9yZGluYXRlKGtvc3RpY19jcmNfQ1NTLCAiTk1EUyIsICJicmF5IikNCg0KIyMgTk1EUyBpcyBub24tbGluZWFyIGFuZCBmb2N1c2VzIG9uIHByZXNlcnZpbmcgcmFuayBvcmRlciBvZiBkaXNzaW1pbGFyaXRpZXMsIFBDQSBpcyBsaW5lYXIgYW5kIGZvY3VzZXMgb24gY2FwdHVyaW5nIG1heGltdW0gdmFyaWFuY2UsIA0KIyMgYW5kIFBDb0EgY2FuIGNhcHR1cmUgYm90aCBsaW5lYXIgYW5kIG5vbi1saW5lYXIgcmVsYXRpb25zaGlwcyBhbmQgZGlmZmVyZW50IHR5cGVzIG9mIGRpc3RhbmNlcyBtZWFzdXJlcw0KDQojc2hhcGUgaXMgYWxzbyBhIHBhcmFtZXRlciB3ZSBjYW4gYXNzaWduIG1ldGFkYXRhIHZhcmlhYmxlIHRvIA0KcCA9IHBsb3Rfb3JkaW5hdGlvbihrb3N0aWNfY3JjX0NTUywgcGh5c2VxMi5vcmQsIHR5cGU9InNhbXBsZXMiLCBjb2xvcj0iRElBR05PU0lTIiwNCiAgICAgICAgICAgICAgICAgICAgdGl0bGU9Ik9UVXMiKQ0KDQojIEFkZCBlbGxpcHNlcw0KYmV0YV9kaXYgPC0gcCArIHN0YXRfZWxsaXBzZShsZXZlbCA9IDAuOTUsIHR5cGUgPSAibm9ybSIsIGdlb20gPSAicG9seWdvbiIsIGFscGhhID0gMCwgYWVzKGNvbG9yID0gRElBR05PU0lTKSwgc2l6ZSA9IDEpICsNCiAgZ2VvbV9wb2ludChzaXplID0gNCkgKyAjIEFkanVzdCB0aGUgc2l6ZSBvZiBwb2ludHMrDQogIHRoZW1lX2J3KCkgKw0KICBndWlkZXMoc2l6ZSA9ICJub25lIikgKyAjIA0KICB0aGVtZSgNCiAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksICAjIE1ha2UgYWxsIHRleHQgYm9sZA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwgICMgTWFrZSBheGlzIHRpdGxlcyBib2xkDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAjIEFkanVzdCBheGlzIHRleHQgc2l6ZQ0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLCAgIyBNYWtlIGxlZ2VuZCB0aXRsZSBib2xkDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgQWRqdXN0IGxlZ2VuZCB0ZXh0IHNpemUNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIikgICMgTWFrZSBwbG90IHRpdGxlIGJvbGQNCiAgKQ0KDQpiZXRhX2Rpdg0KDQojc2F2ZSBpbWFnZSANCiNnZ3NhdmUoImJldGFfZGl2ZXJzaXR5X3Bsb3RfQ1NTLnBuZyIsIGJldGFfZGl2ICwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQ0KDQoNCiMjIHN0YXQgb2YgdGhlIGJldGEgZGl2ZXJzaXR5IA0KDQptZXRhZGF0YSA9IGRhdGEuZnJhbWUoc2FtcGxlX2RhdGEoa29zdGljX2NyY19DU1MpKQ0KcGh5X2JldGEgPSBwaHlsb3NlcTo6ZGlzdGFuY2Uoa29zdGljX2NyY19DU1MsIG1ldGhvZCA9ICJicmF5IikNCmFkb25pczIocGh5X2JldGEgfiBESUFHTk9TSVMsIGRhdGEgPSBtZXRhZGF0YSwgcGVybT05OTkpDQoNCiMjIFRoZSBhZG9uaXMyIGZ1bmN0aW9uIGZpdHMgYSBtdWx0aXZhcmlhdGUgbGluZWFyIG1vZGVsIHRvIHRoZSBkaXN0YW5jZSBtYXRyaXggdXNpbmcgdGhlIGdyb3VwaW5nIHZhcmlhYmxlIChESUFHTk9TSVMpIGFzIGEgcHJlZGljdG9yLiANCiMjIEl0IHRoZW4gYXNzZXNzZXMgdGhlIHNpZ25pZmljYW5jZSBvZiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGdyb3VwaW5nIHZhcmlhYmxlIGFuZCB0aGUgc3RydWN0dXJlIG9mIHRoZSBkaXN0YW5jZSBtYXRyaXggdGhyb3VnaCBwZXJtdXRhdGlvbiB0ZXN0aW5nLg0KIyMgaXQgYXNzdW1lcyBob21vZ2VuZWl0eSBvZiBkaXNwZXJzaW9uIGFtb25nIGdyb3Vwcw0KDQojIyBBIHNtYWxsIHAtdmFsdWUgc3VnZ2VzdHMgdGhhdCB0aGUgZ3JvdXBpbmcgdmFyaWFibGUgc2lnbmlmaWNhbnRseSBleHBsYWlucyB0aGUgdmFyaWF0aW9uIGluIHRoZSBkaXN0YW5jZSBtYXRyaXgsDQojIyBpbmRpY2F0aW5nIGRpZmZlcmVuY2VzIGluIGNvbW11bml0eSBjb21wb3NpdGlvbiBvciBzdHJ1Y3R1cmUgYmV0d2VlbiB0aGUgZ3JvdXBzIGRlZmluZWQgYnkgdGhlIERJQUdOT1NJUyB2YXJpYWJsZQ0KDQojIyBiZXRhZGlzcGVyIGZ1bmN0aW9uIGNoZWNrcyBpZiB0aGUgd2l0aGluIGdyb3VwcyBkaXNwZXJzaW9uIGFyZSBob21vZ2VuZW91cyhjb21wb3NpdGlvbnMgdmFyeSBzaW1pbGFybHkgd2l0aGluIHRoZSBncm91cCkNCg0KYmQgPSBiZXRhZGlzcGVyKHBoeV9iZXRhLCBtZXRhZGF0YSQnRElBR05PU0lTJykNCmFub3ZhKGJkKQ0KDQoNCiMjIHBlcmZvcm0gdGhlIGFuYWx5c2VzIGNvcnJlY3RpbmcgZm9yIG11bHRpcGxlIHNhbXBsaW5nIGZyb20gdGhlIHNhbWUgaW5kaXZpZHVhbA0KDQojIyBjaGFuZ2UgdGhlIHNhbXBsZSBpZCB0byBiZSBhIGZhY3RvciANCg0KIyBFeHRyYWN0IHNhbXBsZSBkYXRhIGZyb20gdGhlIHBoeWxvc2VxIG9iamVjdA0Kc2FtcGxlX2RhdGEgPC0gc2FtcGxlX2RhdGEoa29zdGljX2NyY19DU1MpDQojIHdyaXRlLmNzdihkYXRhLmZyYW1lKHNhbXBsZV9kYXRhKSwgInNhbXBsZV9kYXRhLmNzdiIpDQoNCiMgTW9kaWZ5IHRoZSB2YXJpYWJsZSBvZiBpbnRlcmVzdCB0byBiZSBhIGZhY3Rvcg0Kc2FtcGxlX2RhdGEkQU5PTllNSVpFRF9OQU1FIDwtIGFzLmZhY3RvcihzYW1wbGVfZGF0YSRBTk9OWU1JWkVEX05BTUUpDQpsZXZlbHMoc2FtcGxlX2RhdGEkQU5PTllNSVpFRF9OQU1FKQ0KIyBSZXBsYWNlIHRoZSBtb2RpZmllZCBzYW1wbGUgZGF0YSBpbiB0aGUgcGh5bG9zZXEgb2JqZWN0DQpzYW1wbGVfZGF0YShrb3N0aWNfY3JjX0NTUykgPC0gc2FtcGxlX2RhdGENCg0KDQojIyBjaGVjayByZWxhdGlvbnNoaXAgYmV0d2VlbiBpbmRpdmlkdWFscyANCnBsb3Rfb3JkaW5hdGlvbihrb3N0aWNfY3JjX0NTUywgcGh5c2VxMi5vcmQsIHR5cGU9InNhbXBsZXMiLCBjb2xvcj0iQU5PTllNSVpFRF9OQU1FIiwNCiAgICAgICAgICAgICAgICB0aXRsZT0iT1RVcyIpKw0KICBndWlkZXMoY29sb3IgPSBGQUxTRSkgIyBubyBsZWdlbmQgDQoNCg0KIyBFeHRyYWN0IHRoZSBOTURTIHJlc3VsdHMgZnJvbSBwaHlzZXEyLm9yZA0Kbm1kc19yZXN1bHRzIDwtIHNjb3JlcyhwaHlzZXEyLm9yZCkNCg0KIyBDb21iaW5lIHRoZSBOTURTIHJlc3VsdHMgd2l0aCB0aGUgc2FtcGxlIElEcw0KcGxvdF9kYXRhIDwtIGNiaW5kKHNhbXBsZV9kYXRhKGtvc3RpY19jcmNfQ1NTKSwgbm1kc19yZXN1bHRzJHNpdGVzKQ0KDQojIFBsb3QgdGhlIG9yZGluYXRpb24NCnAgPC0gZ2dwbG90KHBsb3RfZGF0YSwgYWVzKHggPSBOTURTMSwgeSA9IE5NRFMyLCBjb2xvciA9IFguU2FtcGxlSUQpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGxhYnModGl0bGUgPSAiT1RVcyIpICsNCiAgZ3VpZGVzKGNvbG9yID0gRkFMU0UpDQoNCnAgKyBnZW9tX3RleHQoYWVzKGxhYmVsID0gQU5PTllNSVpFRF9OQU1FKSwgc2l6ZSA9Mi41LCB2anVzdCA9IC0wLjUpDQoNCmFkb25pczIocGh5X2JldGEgfiBtZXRhZGF0YSRESUFHTk9TSVMsIGRhdGEgPSBtZXRhZGF0YSwgcGVybT05OTksIHN0cmF0YSA9IG1ldGFkYXRhJEFOT05ZTUlaRURfTkFNRSkNCg0KIyMgdHJ5IHdlaWdodGVkIHVuaWZyYWMgZm9yIGRpYWdub3NpcyANCg0KIyBwaHlfdHJlZShrb3N0aWNfY3JjX0NTUykNCiMgcGh5c2VxMi5vcmQxPC0gb3JkaW5hdGUoa29zdGljX2NyY19DU1MsICJOTURTIiwgInd1bmlmcmFjIikgI29yIHVuaWZyYWMNCiMgd2UgZG9uJ3QgaGF2ZSB0aGUgcGh5X3RyZWUgDQojIGJ1dCBpZiB3ZSBkaWQsIHdlIGNvdWxkIGhhdmUgYWxzbyBleHBsb3JlZCB0aGlzIGJldGFfZGl2IG1lYXN1cmUgDQoNCmBgYA0KIyA5LiBJZGVudGlmeSBEQSB0YXhhIGJldHdlZW4gZ3JvdXBzDQpgYGB7ciwgZXZhbD0gRkFMU0V9DQoNCiMjIG1pY3JvYmlvbWVNYXJrZXIgaXMgYSB3cmFwcGVyIGZvciBhIGxvdCBvZiBSIHBhY2thZ2VzIHNwZWNpZmljYWxseSBkZXNpZ25lZCB0byANCiMjIHByb2ZpbGUgREEgdGF4YSBmcm9tIG1pY3JvYmlhbCBjb21wb3NpdGlvbmFsIGRhdGEgDQoNCiMjIGxlZnNlICMjDQoNCiMjIEtXIGRldGVjdCBzdGF0IHNpZ24gZmVhdHVyZXMNCiMjIFdvbGNveG9uIGFuZCBtdWx0aWdycF9zdHJhdCB0ZXN0IGZvciBjb25zaXN0ZW5jeSBvZiBzdGF0IHNpZ24gYWNyb3NzIGdyb3VwcyBvZiByZXBsaWNhdGVzDQojIyBMREEgYW5hbHlzZXMgYXJlIGZpbmFsbHkgcGVyZm9ybWVkIHRvIGlkZW50aWZ5IHRoZSBleGFjdCBlZmZlY3Qgc2l6ZSBvZiB0aGUgZGlmZmVyZW5jZSANCg0KIyMgQ1BNIGlzIHRoZSBkZWZhdWx0IG5vcm0gbWV0aG9kIGZvciBsZWZzZSAjIw0KDQpkZl9rb3N0aWNfY3BtID1ub3JtYWxpemUoa29zdGljX2NyYywgbWV0aG9kID0gIkNQTSIpJT4lbWljcm9iaW9tZU1hcmtlcjo6cnVuX2xlZnNlKA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lsY294b25fY3V0b2ZmID0gMC4wNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm0gPSAibm9uZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9ICJESUFHTk9TSVMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga3dfY3V0b2ZmID0gMC4wNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11bHRpZ3JwX3N0cmF0ID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxkYV9jdXRvZmYgPSAxLjUNCikNCg0KZGZfa29zdGljX2NwbV90YWJsZSAgPSAobWFya2VyX3RhYmxlKGRmX2tvc3RpY19jcG0pKQ0KZGltKGRmX2tvc3RpY19jcG1fdGFibGUpDQojIDE1OCBEQSB0YXhhIGFjcm9zcyB0YXhhIGxldmVscyANCmhlYWQoZGZfa29zdGljX2NwbV90YWJsZSkNCg0KI3dyaXRlIHRoZSB0YWJsZQ0KI3dyaXRlLmNzdihkYXRhLmZyYW1lKGRmX2tvc3RpY19jcG1fdGFibGUpLCAiZGZfa29zdGljX2NwbV90YWJsZV9EQS5jc3YiKQ0KDQojIyByZXN1bHRzIGFyZSBvYnRhaW5lZCBub3Qgb25seSBmb3Igc3BlY2llcyBidXQgZm9yIGFsbCB0YXhhIGxldmVscyANCiMjIFdlIGNhbid0IGVhc2lseSBjb3JyZWN0IGZvciBjby1mb3VuZGVycyBvciByZXBlYXRlZCBtZWFzdXJlcyB3aXRoIHRoZXNlIGFuYWx5c2VzIA0KDQoNCiMjIyBwcmVwYXJlIHRoZSBkYXRhIGZvciBNYWFzbGluMg0KIyMgTWFBc0xpbjIgcmVsaWVzIG9uIGdlbmVyYWwgbGluZWFyIG1vZGVscyB0byBhY2NvbW1vZGF0ZSBtb3N0IG1vZGVybiBlcGlkZW1pb2xvZ2ljYWwgc3R1ZHkgZGVzaWducywgDQojIyBpbmNsdWRpbmcgY3Jvc3Mtc2VjdGlvbmFsIGFuZCBsb25naXR1ZGluYWwsIGFuZCBvZmZlcnMgYSB2YXJpZXR5IG9mIGRhdGEgZXhwbG9yYXRpb24sIG5vcm1hbGl6YXRpb24sIGFuZCB0cmFuc2Zvcm1hdGlvbiBtZXRob2RzDQojIyBpdCBhbGxvd3MgY29ycmVjdGlvbiBmb3IgY28tZm91bmRlcnMgYW5kIHJlcGVhdGVkIG1lYXN1cmVzIChjb3JyZWN0aW5nIGZvciBpbnRyYWluZGl2aWR1YWwgdmFyaWFiaWxpdHkpDQoNCg0Ka29zdGljX2NyY19DUE0gPSBtaWNyb2Jpb21lTWFya2VyOjpub3JtYWxpemUoa29zdGljX2NyYywgbWV0aG9kID0gIkNQTSIpDQoNCmNyY19DUE1fT1RVID0gZGF0YS5mcmFtZShvdHVfdGFibGUoa29zdGljX2NyY19DUE0pKQ0KZGltKGNyY19DUE1fT1RVKQ0KaGVhZChjcmNfQ1BNX09UVSkNCmNyY19DUE1fVEFYQSA9IGRhdGEuZnJhbWUoKHRheF90YWJsZShrb3N0aWNfY3JjX0NQTSkpKQ0KZGltKHRheF90YWJsZShrb3N0aWNfY3JjX0NQTSkpDQpoZWFkKGNyY19DUE1fVEFYQSkNCmNyY19DUE1fTUVUQSA9IGRhdGEuZnJhbWUoc2FtcGxlX2RhdGEoa29zdGljX2NyY19DUE0pKQ0KaGVhZChjcmNfQ1BNX01FVEEpDQojIFRoZXNlIGFyZSBob3cgdGhleSBzaG91bGQgYmUgDQpjcmNfQ1BNX1RBWEEgPSBjcmNfQ1BNX1RBWEEgJT4lIGRwbHlyOjptdXRhdGUoU3BlY2llczEgPSBwYXN0ZShyb3duYW1lcyhjcmNfQ1BNX1RBWEEpLCBHZW51cywgU3BlY2llcywgc2VwID0gIl8iKSkNCmNyY19DUE1fVEFYQSA9IGNyY19DUE1fVEFYQSAlPiUgZHBseXI6OnNlbGVjdChTcGVjaWVzMSkNCg0KaGVhZChjcmNfQ1BNX1RBWEEpDQpoZWFkKGNyY19DUE1fT1RVWywgMToyMF0pDQoNCiMgQWRkIHJvdyBuYW1lcyBhcyBhIGNvbHVtbiBpbiBib3RoIGRhdGEgZnJhbWVzDQpjcmNfQ1BNX09UVSA8LSByb3duYW1lc190b19jb2x1bW4oY3JjX0NQTV9PVFUsICJSb3dOYW1lcyIpDQpjcmNfQ1BNX1RBWEEgPC0gcm93bmFtZXNfdG9fY29sdW1uKGNyY19DUE1fVEFYQSwgIlJvd05hbWVzIikNCg0KIyBNZXJnZSB0aGUgdHdvIGRhdGEgZnJhbWVzIGJ5IHRoZSBuZXcgY29sdW1uICJSb3dOYW1lcyINCmNyY19DUE1fT1RVX1MgPC0gbWVyZ2UoY3JjX0NQTV9PVFUsIGNyY19DUE1fVEFYQSwgYnkueCA9ICJSb3dOYW1lcyIsIGJ5LnkgPSAiUm93TmFtZXMiKQ0KaGVhZChjcmNfQ1BNX09UVV9TKQ0KIyBPcHRpb25hbGx5LCByZW1vdmUgdGhlIHJlZHVuZGFudCAiUm93TmFtZXMiIGNvbHVtbiBhZnRlciBtZXJnaW5nDQpjcmNfQ1BNX09UVV9TIDwtIGNyY19DUE1fT1RVX1MgJT4lIGRwbHlyOjpzZWxlY3QoLVJvd05hbWVzKQ0KY3JjX0NQTV9PVFVfUyA9IGNvbHVtbl90b19yb3duYW1lcyhjcmNfQ1BNX09UVV9TLCAiU3BlY2llczEiKQ0KDQpjcmNfQ1BNX09UVV9TID0gZGF0YS5mcmFtZSh0KGNyY19DUE1fT1RVX1MpKQ0KaGVhZChjcmNfQ1BNX09UVV9TKQ0KY29sbmFtZXMoY3JjX0NQTV9PVFVfUykgPC0gc3ViKCJYIiwgIiIsIGNvbG5hbWVzKGNyY19DUE1fT1RVX1MpKQ0KDQpjcmNfQ1BNX01FVEEkQU5PTllNSVpFRF9OQU1FID0gYXMuZmFjdG9yKGNyY19DUE1fTUVUQSRBTk9OWU1JWkVEX05BTUUpDQoNCg0KaGVhZChjcmNfQ1BNX09UVV9TWzE6MjAsIDE6MjBdKQ0KaGVhZChjcmNfQ1BNX01FVEFbMToyMCwgMToyMF0pDQoNCiMjIFdlIHdpbGwgbmVlZCBjcmNfQ1BNX09UVV9TIGFuZCBjcmNfQ1BNX01FVEENCg0KZml0X2RhdGEgPC0gTWFhc2xpbjIoDQogIGNyY19DUE1fT1RVX1MsIGNyY19DUE1fTUVUQSwgJ0M6Ly9Vc2Vycy8vTWFsaW5hLy9EZXNrdG9wLy9NaWNyb2JpYWwgY29tcG9zaXRpb25hbGl0eS9tYWFzbGluX3Jlc3VsdHNfcmFuZG9tX2VmZmVjdC8nLA0KICBmaXhlZF9lZmZlY3RzID0gYygnRElBR05PU0lTJyksDQogIG5vcm1hbGl6YXRpb24gPSAiTk9ORSIsDQogIHJlZmVyZW5jZSA9IGMoIkRJQUdOT1NJUyxIZWFsdGh5IiksDQogIHJhbmRvbV9lZmZlY3RzID0gYygiQU5PTllNSVpFRF9OQU1FIikNCiAgKQ0KDQoNCiMjIERBIGFuYWx5c2VzIG9mIHRoZSBzYW1lIGRhdGFzZXQgd2l0aCBkZXNlcTINCiMjIGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9waHlsb3NlcS9pbnN0L2RvYy9waHlsb3NlcS1taXh0dXJlLW1vZGVscy5odG1sDQoNCmBgYA0KIyAxMC4gR2V0IGNvLWFidW5kYW5jZSBpbnRlcmFjdGlvbiBuZXR3b3JrIGZvciBlaXRoZXIgVHVtb3Igb3IgSGVhbHRoeSBzYW1wbGVzICANCmBgYHtyLCBlY2hvPSBUUlVFfQ0KDQojIyMgd2lsbCBzdWJzZXQgdGhlIGluaXRpYWwgQ1BNIG5vcm0gcGh5bG9zZXEgb2JqIHRvIGdldCBvbmx5IHRoZSBUdW1vciBzYW1wbGVzIGFuZCB3aWxsDQojIyMgZ2VuZXJhdGUgZ3JhcGggZnJvbSBpdCANCiMjIHdpbGwgc3Vic2V0IHNwZWNpZXMgdGhhdCBhcmUgcHJlc2VudCBhdCBsZWFzdCBpbiA1MCUgb2YgdGhlIHNhbXBsZXMgKGZpbHRlcmluZyBvbiBwcmV2YWxlbmNlKQ0KIyMgdGhyZXNob2xkIGZvciBjb3JyZWxhdGlvbiB3aWxsIGJlIDAuNCAoc3BlYXJtYW4pDQoNCmtvc3RpY19jcmNfQ1BNDQoNCnN1YnNldF9rb3N0aWNfY3JjIDwtIHN1YnNldF9zYW1wbGVzKGtvc3RpY19jcmNfQ1BNLCBESUFHTk9TSVMgPT0gIlR1bW9yIikNCiMgRGVmaW5lIHRoZSBwcmV2YWxlbmNlIHRocmVzaG9sZCAoZS5nLiwgdGF4YSBwcmVzZW50IGluIGF0IGxlYXN0IDUwJSBvZiBzYW1wbGVzKQ0KcHJldmFsZW5jZV90aHJlc2hvbGQgPC0gMC41DQoNCiMgRmlsdGVyIHRheGEgYmFzZWQgb24gcHJldmFsZW5jZQ0Kc3Vic2V0X2tvc3RpY19jcmMgPC0gZmlsdGVyX3RheGEoc3Vic2V0X2tvc3RpY19jcmMgLCBmdW5jdGlvbih4KSBzdW0oeCA+IDApIC8gbGVuZ3RoKHgpID49IHByZXZhbGVuY2VfdGhyZXNob2xkLCBUUlVFKQ0KDQojIyBjcmVhdGUgT1RVIGlucHV0IHRhYmxlIHdpdGggc3BlY2llcyBvciBnZW51cyAod2hlbiBOQSBpcyBwcmVzZW50IGZvciBzcGVjaWVzKSBuYW1lcw0KY3JjX0NQTV9PVFUgPSBkYXRhLmZyYW1lKG90dV90YWJsZShzdWJzZXRfa29zdGljX2NyYykpDQpkaW0oY3JjX0NQTV9PVFUpDQpjcmNfQ1BNX1RBWEEgPSBkYXRhLmZyYW1lKCh0YXhfdGFibGUoc3Vic2V0X2tvc3RpY19jcmMpKSkNCmRpbSh0YXhfdGFibGUoc3Vic2V0X2tvc3RpY19jcmMpKQ0KDQpjcmNfQ1BNX01FVEEgPSBkYXRhLmZyYW1lKHNhbXBsZV9kYXRhKHN1YnNldF9rb3N0aWNfY3JjKSkNCmhlYWQoY3JjX0NQTV9NRVRBKQ0KIyBUaGVzZSBhcmUgaG93IHRoZXkgc2hvdWxkIGJlIA0KY3JjX0NQTV9UQVhBID0gY3JjX0NQTV9UQVhBICU+JSBkcGx5cjo6bXV0YXRlKFNwZWNpZXMxPSBwYXN0ZShyb3duYW1lcyhjcmNfQ1BNX1RBWEEpLCBHZW51cywgU3BlY2llcywgc2VwID0gIl8iKSkNCmNyY19DUE1fVEFYQSA9IGNyY19DUE1fVEFYQSAlPiUgZHBseXI6OnNlbGVjdChTcGVjaWVzMSkNCg0KaGVhZChjcmNfQ1BNX1RBWEEpDQpoZWFkKGNyY19DUE1fT1RVWywgMToyMF0pDQoNCiMgQWRkIHJvdyBuYW1lcyBhcyBhIGNvbHVtbiBpbiBib3RoIGRhdGEgZnJhbWVzDQpjcmNfQ1BNX09UVSA8LSByb3duYW1lc190b19jb2x1bW4oY3JjX0NQTV9PVFUsICJSb3dOYW1lcyIpDQpjcmNfQ1BNX1RBWEEgPC0gcm93bmFtZXNfdG9fY29sdW1uKGNyY19DUE1fVEFYQSwgIlJvd05hbWVzIikNCg0KIyBNZXJnZSB0aGUgdHdvIGRhdGEgZnJhbWVzIGJ5IHRoZSBuZXcgY29sdW1uICJSb3dOYW1lcyINCmNyY19DUE1fT1RVX1MgPC0gbWVyZ2UoY3JjX0NQTV9PVFUsIGNyY19DUE1fVEFYQSwgYnkueCA9ICJSb3dOYW1lcyIsIGJ5LnkgPSAiUm93TmFtZXMiKQ0KaGVhZChjcmNfQ1BNX09UVV9TKQ0KIyBPcHRpb25hbGx5LCByZW1vdmUgdGhlIHJlZHVuZGFudCAiUm93TmFtZXMiIGNvbHVtbiBhZnRlciBtZXJnaW5nDQpjcmNfQ1BNX09UVV9TIDwtIGNyY19DUE1fT1RVX1MgJT4lIGRwbHlyOjpzZWxlY3QoLVJvd05hbWVzKQ0KY3JjX0NQTV9PVFVfUyA9IGNvbHVtbl90b19yb3duYW1lcyhjcmNfQ1BNX09UVV9TLCAiU3BlY2llczEiKQ0KDQpyb3duYW1lcyhjcmNfQ1BNX09UVV9TKSA8LSBzdWIoIlgiLCAiIiwgcm93bmFtZXMoY3JjX0NQTV9PVFVfUykpDQpoZWFkKGNyY19DUE1fT1RVX1NbLDE6MjBdKQ0KDQpkaW0oY3JjX0NQTV9PVFVfUykNCg0KIyMjIGJ1aWxkIHVud2VpZ2h0ZWQgZ3JhcGggDQpjb3JyZWxhdGlvbl9tYXRyaXggPC0gY29yKGRhdGEuZnJhbWUodChjcmNfQ1BNX09UVV9TKSksbWV0aG9kID0gInNwZWFybWFuIikNCmhlYWQoY29ycmVsYXRpb25fbWF0cml4WywxOjVdKQ0KdGhyZXNob2xkIDwtIDAuNA0KDQojIENyZWF0ZSBhbiBhZGphY2VuY3kgbWF0cml4IGJhc2VkIG9uIHRoZSBjb3JyZWxhdGlvbiB0aHJlc2hvbGQNCmFkamFjZW5jeV9tYXRyaXggPC0gaWZlbHNlKGFicyhjb3JyZWxhdGlvbl9tYXRyaXgpID4gdGhyZXNob2xkLCAxLCAwKQ0KaGVhZChhZGphY2VuY3lfbWF0cml4WywxOjVdKQ0KDQojIFJlbW92ZSB0aGUgZGlhZ29uYWwgdmFsdWVzIA0KZGlhZyhhZGphY2VuY3lfbWF0cml4KSA8LSAwDQpoZWFkKGFkamFjZW5jeV9tYXRyaXhbLDE6NV0pDQoNCmdyYXBoIDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeChhZGphY2VuY3lfbWF0cml4ICwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZSA9ICJ1bmRpcmVjdGVkIikNCmdyYXBoDQojcGxvdChncmFwaCkNCg0KI0NsdXN0ZXIgYmFzZWQgb24gZWRnZSBiZXR3ZWVubmVzcw0KY2ViID0gY2x1c3Rlcl9lZGdlX2JldHdlZW5uZXNzKGdyYXBoKQ0KDQojRXh0cmFjdCBjb21tdW5pdHkgbWVtYmVyc2hpcHMgZnJvbSB0aGUgY2x1c3RlcmluZw0KY29tbXVuaXR5X21lbWJlcnNoaXAgPC0gbWVtYmVyc2hpcChjZWIpDQpoZWFkKGNvbW11bml0eV9tZW1iZXJzaGlwKQ0KbGVuZ3RoKGNlYikNCiMgQ291bnQgdGhlIG51bWJlciBvZiBub2RlcyBpbiBlYWNoIGNsdXN0ZXINCmNsdXN0ZXJfc2l6ZXMgPC0gdGFibGUoY29tbXVuaXR5X21lbWJlcnNoaXApDQpjbHVzdGVyX3NpemVzDQoNCiMgQWRkIGNsdXN0ZXIgbWVtYmVyc2hpcHMgYXMgYSB2ZXJ0ZXggYXR0cmlidXRlDQppZ3JhcGg6OlYoZ3JhcGgpJGNsdXN0ZXJfbWVtYmVyc2hpcCA8LSBjb21tdW5pdHlfbWVtYmVyc2hpcA0KDQojIE1hcCBjbHVzdGVyIG1lbWJlcnNoaXBzIHRvIGNvbG9ycyB1c2luZyBhIGNvbG9yIHBhbGV0dGUgZnVuY3Rpb24NCmNsdXN0ZXJfY29sb3JzIDwtIHJhaW5ib3cobWF4KGNvbW11bml0eV9tZW1iZXJzaGlwKSkNCmhlYWQoY2x1c3Rlcl9jb2xvcnMpDQojIENyZWF0ZSBhIGNvbG9yIHZhcmlhYmxlIGJhc2VkIG9uIGNsdXN0ZXIgbWVtYmVyc2hpcHMNClYoZ3JhcGgpJGNvbG9yX3ZhcmlhYmxlIDwtIGNsdXN0ZXJfY29sb3JzW2NvbW11bml0eV9tZW1iZXJzaGlwXQ0KDQojIHZldHJleCBkZWdyZWUNCnZlcnRleF9kZWdyZWVzIDwtIGlncmFwaDo6ZGVncmVlKGdyYXBoKQ0KaGVhZCh2ZXJ0ZXhfZGVncmVlcykNCiMgQWRkIHZlcnRleCBkZWdyZWUgYXMgYSB2ZXJ0ZXggYXR0cmlidXRlDQppZ3JhcGg6OlYoZ3JhcGgpJGRlZ3JlZSA8LSB2ZXJ0ZXhfZGVncmVlcw0KDQoNCnZlcnRleC5hdHRyID0gbGlzdCgNCiAgY2x1c3Rlcl9tZW1iZXJzaGlwID0gVihncmFwaCkkY2x1c3Rlcl9tZW1iZXJzaGlwLA0KICBuYW1lID0gVihncmFwaCkkbmFtZSwNCiAgZGVncmVlcyA9IFYoZ3JhcGgpJGRlZ3JlZSwNCiAgY29sb3IgPSBWKGdyYXBoKSRjb2xvcl92YXJpYWJsZSkNCg0KIyBTcGVjaWZ5IHRoZSBmaWxlIG5hbWUgZm9yIHNhdmluZyB0aGUgR3JhcGhNTCBmaWxlDQpncmFwaG1sX2ZpbGUgPC0gIlQxZ3JhcGhfa29zdGljX0NQTV93aXRoX21ldGFkYXRhX3Vud2VpZ2h0ZWRfdW5kaXJlY3RlZC5ncmFwaG1sIg0KDQojIFdyaXRlIHRoZSBpZ3JhcGggb2JqZWN0IHRvIEdyYXBoTUwgZm9ybWF0IHdpdGggbWV0YWRhdGENCndyaXRlX2dyYXBoKA0KICBncmFwaCwNCiAgZ3JhcGhtbF9maWxlLA0KICBmb3JtYXQgID0gImdyYXBobWwiKQ0KDQojIENvbWJpbmUgdmVydGV4IGF0dHJpYnV0ZXMgaW50byBhIGRhdGEgZnJhbWUNCnZlcnRleF9kYXRhIDwtIGRhdGEuZnJhbWUodmVydGV4LmF0dHIpDQoNCmhlYWQodmVydGV4X2RhdGEpDQp3cml0ZS5jc3YodmVydGV4X2RhdGEsICJUMXZlcnRleF9kYXRhX2tvc3RpY19DUE1fd2l0aF9tZXRhZGF0YV91bndlaWdodGVkX3VuZGlyZWN0ZWQuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQoNCiMjIGNyZWF0ZSBhIGZ1bmN0aW9uIGZvciBidWlsZGluZyBjby1hYnVuZGFuY2UgbmV0d29yaw0KDQpnZXRfaW50ZXJhY3Rpb25fbmV0d29yayA9IGZ1bmN0aW9uKG5vcm1fcGh5bG9zZXFfb2JqLCBncm91cCwgZ3JhcGhtbF9maWxlLCB2ZXJ0ZXhfZGF0YV9maWxlKXsNCiAgDQogIHN1YnNldF9rb3N0aWNfY3JjIDwtIHN1YnNldF9zYW1wbGVzKG5vcm1fcGh5bG9zZXFfb2JqLCBESUFHTk9TSVMgPT0gcGFzdGUoZ3JvdXApKQ0KICAjIERlZmluZSB0aGUgcHJldmFsZW5jZSB0aHJlc2hvbGQgKGUuZy4sIHRheGEgcHJlc2VudCBpbiBhdCBsZWFzdCA1MCUgb2Ygc2FtcGxlcykNCiAgcHJldmFsZW5jZV90aHJlc2hvbGQgPC0gMC41DQogIA0KICAjIEZpbHRlciB0YXhhIGJhc2VkIG9uIHByZXZhbGVuY2UNCiAgc3Vic2V0X2tvc3RpY19jcmMgPC0gZmlsdGVyX3RheGEoc3Vic2V0X2tvc3RpY19jcmMgLCBmdW5jdGlvbih4KSBzdW0oeCA+IDApIC8gbGVuZ3RoKHgpID49IHByZXZhbGVuY2VfdGhyZXNob2xkLCBUUlVFKQ0KICANCiAgIyMgY3JlYXRlIE9UVSBpbnB1dCB0YWJsZSB3aXRoIHNwZWNpZXMgb3IgZ2VudXMgKHdoZW4gTkEgaXMgcHJlc2VudCBmb3Igc3BlY2llcykgbmFtZXMNCiAgY3JjX0NQTV9PVFUgPSBkYXRhLmZyYW1lKG90dV90YWJsZShzdWJzZXRfa29zdGljX2NyYykpDQoNCiAgY3JjX0NQTV9UQVhBID0gZGF0YS5mcmFtZSgodGF4X3RhYmxlKHN1YnNldF9rb3N0aWNfY3JjKSkpDQogDQogIA0KICBjcmNfQ1BNX01FVEEgPSBkYXRhLmZyYW1lKHNhbXBsZV9kYXRhKHN1YnNldF9rb3N0aWNfY3JjKSkNCiAgDQogICMgVGhlc2UgYXJlIGhvdyB0aGV5IHNob3VsZCBiZSANCiAgY3JjX0NQTV9UQVhBID0gY3JjX0NQTV9UQVhBICU+JSBkcGx5cjo6bXV0YXRlKFNwZWNpZXMxPSBwYXN0ZShyb3duYW1lcyhjcmNfQ1BNX1RBWEEpLCBHZW51cywgU3BlY2llcywgc2VwID0gIl8iKSkNCiAgY3JjX0NQTV9UQVhBID0gY3JjX0NQTV9UQVhBICU+JSBkcGx5cjo6c2VsZWN0KFNwZWNpZXMxKQ0KICANCiAgDQogICMgQWRkIHJvdyBuYW1lcyBhcyBhIGNvbHVtbiBpbiBib3RoIGRhdGEgZnJhbWVzDQogIGNyY19DUE1fT1RVIDwtIHJvd25hbWVzX3RvX2NvbHVtbihjcmNfQ1BNX09UVSwgIlJvd05hbWVzIikNCiAgY3JjX0NQTV9UQVhBIDwtIHJvd25hbWVzX3RvX2NvbHVtbihjcmNfQ1BNX1RBWEEsICJSb3dOYW1lcyIpDQogIA0KICAjIE1lcmdlIHRoZSB0d28gZGF0YSBmcmFtZXMgYnkgdGhlIG5ldyBjb2x1bW4gIlJvd05hbWVzIg0KICBjcmNfQ1BNX09UVV9TIDwtIG1lcmdlKGNyY19DUE1fT1RVLCBjcmNfQ1BNX1RBWEEsIGJ5LnggPSAiUm93TmFtZXMiLCBieS55ID0gIlJvd05hbWVzIikNCg0KICAjIE9wdGlvbmFsbHksIHJlbW92ZSB0aGUgcmVkdW5kYW50ICJSb3dOYW1lcyIgY29sdW1uIGFmdGVyIG1lcmdpbmcNCiAgY3JjX0NQTV9PVFVfUyA8LSBjcmNfQ1BNX09UVV9TICU+JSBkcGx5cjo6c2VsZWN0KC1Sb3dOYW1lcykNCiAgY3JjX0NQTV9PVFVfUyA9IGNvbHVtbl90b19yb3duYW1lcyhjcmNfQ1BNX09UVV9TLCAiU3BlY2llczEiKQ0KICANCiAgcm93bmFtZXMoY3JjX0NQTV9PVFVfUykgPC0gc3ViKCJYIiwgIiIsIHJvd25hbWVzKGNyY19DUE1fT1RVX1MpKQ0KICANCiAgIyMjIGJ1aWxkIHVud2VpZ2h0ZWQgZ3JhcGggDQogIGNvcnJlbGF0aW9uX21hdHJpeCA8LSBjb3IoZGF0YS5mcmFtZSh0KGNyY19DUE1fT1RVX1MpKSxtZXRob2QgPSAic3BlYXJtYW4iKQ0KIA0KICB0aHJlc2hvbGQgPC0gMC40DQogIA0KICAjIENyZWF0ZSBhbiBhZGphY2VuY3kgbWF0cml4IGJhc2VkIG9uIHRoZSBjb3JyZWxhdGlvbiB0aHJlc2hvbGQNCiAgYWRqYWNlbmN5X21hdHJpeCA8LSBpZmVsc2UoYWJzKGNvcnJlbGF0aW9uX21hdHJpeCkgPiB0aHJlc2hvbGQsIDEsIDApDQogDQogIA0KICAjIFJlbW92ZSB0aGUgZGlhZ29uYWwgdmFsdWVzIA0KICBkaWFnKGFkamFjZW5jeV9tYXRyaXgpIDwtIDANCiAgDQogIA0KICBncmFwaCA8LSBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoYWRqYWNlbmN5X21hdHJpeCAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZSA9ICJ1bmRpcmVjdGVkIikNCiAgDQogIA0KICAjQ2x1c3RlciBiYXNlZCBvbiBlZGdlIGJldHdlZW5uZXNzDQogIGNlYiA9IGNsdXN0ZXJfZWRnZV9iZXR3ZWVubmVzcyhncmFwaCkNCiAgDQogICNFeHRyYWN0IGNvbW11bml0eSBtZW1iZXJzaGlwcyBmcm9tIHRoZSBjbHVzdGVyaW5nDQogIGNvbW11bml0eV9tZW1iZXJzaGlwIDwtIG1lbWJlcnNoaXAoY2ViKQ0KICANCiAgIyBDb3VudCB0aGUgbnVtYmVyIG9mIG5vZGVzIGluIGVhY2ggY2x1c3Rlcg0KICBjbHVzdGVyX3NpemVzIDwtIHRhYmxlKGNvbW11bml0eV9tZW1iZXJzaGlwKQ0KICANCiAgIyBBZGQgY2x1c3RlciBtZW1iZXJzaGlwcyBhcyBhIHZlcnRleCBhdHRyaWJ1dGUNCiAgaWdyYXBoOjpWKGdyYXBoKSRjbHVzdGVyX21lbWJlcnNoaXAgPC0gY29tbXVuaXR5X21lbWJlcnNoaXANCiAgDQogICMgTWFwIGNsdXN0ZXIgbWVtYmVyc2hpcHMgdG8gY29sb3JzIHVzaW5nIGEgY29sb3IgcGFsZXR0ZSBmdW5jdGlvbg0KICBjbHVzdGVyX2NvbG9ycyA8LSByYWluYm93KG1heChjb21tdW5pdHlfbWVtYmVyc2hpcCkpDQogIA0KICAjIENyZWF0ZSBhIGNvbG9yIHZhcmlhYmxlIGJhc2VkIG9uIGNsdXN0ZXIgbWVtYmVyc2hpcHMNCiAgVihncmFwaCkkY29sb3JfdmFyaWFibGUgPC0gY2x1c3Rlcl9jb2xvcnNbY29tbXVuaXR5X21lbWJlcnNoaXBdDQogIA0KICAjIHZldHJleCBkZWdyZWUNCiAgdmVydGV4X2RlZ3JlZXMgPC0gaWdyYXBoOjpkZWdyZWUoZ3JhcGgpDQogDQogICMgQWRkIHZlcnRleCBkZWdyZWUgYXMgYSB2ZXJ0ZXggYXR0cmlidXRlDQogIGlncmFwaDo6VihncmFwaCkkZGVncmVlIDwtIHZlcnRleF9kZWdyZWVzDQogIA0KICB2ZXJ0ZXguYXR0ciA9IGxpc3QoDQogICAgY2x1c3Rlcl9tZW1iZXJzaGlwID0gVihncmFwaCkkY2x1c3Rlcl9tZW1iZXJzaGlwLA0KICAgIG5hbWUgPSBWKGdyYXBoKSRuYW1lLA0KICAgIGRlZ3JlZXMgPSBWKGdyYXBoKSRkZWdyZWUsDQogICAgY29sb3IgPSBWKGdyYXBoKSRjb2xvcl92YXJpYWJsZSkNCiAgDQogICMgU3BlY2lmeSB0aGUgZmlsZSBuYW1lIGZvciBzYXZpbmcgdGhlIEdyYXBoTUwgZmlsZQ0KICBncmFwaG1sX2ZpbGUgPC0gZ3JhcGhtbF9maWxlDQogIA0KICAjIFdyaXRlIHRoZSBpZ3JhcGggb2JqZWN0IHRvIEdyYXBoTUwgZm9ybWF0IHdpdGggbWV0YWRhdGENCiAgd3JpdGVfZ3JhcGgoDQogICAgZ3JhcGgsDQogICAgZ3JhcGhtbF9maWxlLA0KICAgIGZvcm1hdCAgPSAiZ3JhcGhtbCIpDQogIA0KICAjIENvbWJpbmUgdmVydGV4IGF0dHJpYnV0ZXMgaW50byBhIGRhdGEgZnJhbWUNCiAgdmVydGV4X2RhdGEgPC0gZGF0YS5mcmFtZSh2ZXJ0ZXguYXR0cikNCiAgDQogIHdyaXRlLmNzdih2ZXJ0ZXhfZGF0YSwgdmVydGV4X2RhdGFfZmlsZSwgcm93Lm5hbWVzID0gRkFMU0UpDQogIA0KDQp9DQoNCiMjIHJ1biB0aGUgZnVuY3Rpb24gdG8gb2J0YWluIHRoZSBncmFwaCBmaWxlIGFuZCB2ZXJ0ZXggbWV0YWRhdGEgZm9yIHRoZSBIZWFsdGh5IHNhbXBsZXMNCg0KZ2V0X2ludGVyYWN0aW9uX25ldHdvcmsoDQogIG5vcm1fcGh5bG9zZXFfb2JqID0ga29zdGljX2NyY19DUE0sDQogIGdyb3VwID0gIkhlYWx0aHkiLA0KICBncmFwaG1sX2ZpbGUgPSAiSDFncmFwaF9rb3N0aWNfQ1BNX3dpdGhfbWV0YWRhdGFfdW53ZWlnaHRlZF91bmRpcmVjdGVkLmdyYXBobWwiLA0KICB2ZXJ0ZXhfZGF0YV9maWxlID0gIkgxdmVydGV4X2RhdGFfa29zdGljX0NQTV93aXRoX21ldGFkYXRhX3Vud2VpZ2h0ZWRfdW5kaXJlY3RlZC5jc3YiKQ0KDQpgYGA=