1. Install and/or load the required packages


# install.packages("BiocManager")
# BiocManager::install(c("phyloseq", "vegan", "microbiome", "Maaslin2"))
# BiocManager::install(c("microbiomeMarker"))
# BiocManager::install(c("ggplot2", "tidyverse", "dplyr", "data.table"))

library("phyloseq")
library("vegan")
library("ggplot2")
library("microbiomeMarker")
library("tidyverse")
library("dplyr")
library("data.table")
library("microbiome")
library("Maaslin2")
library("igraph")
library("RColorBrewer")
library("nlme")

2. Set the working directory

setwd("C://Users//Malina//Desktop//Microbial compositionality")

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

4. Function to create a phyloseq object from data files


create_phyloseq <- function(otu_file, sample_file, tax_file) {
  # Load OTU table
  otu_data <- read.csv(otu_file, row.names = 1, stringsAsFactors = FALSE)
  otu_matrix <- as.matrix(otu_data)
  
  # Load sample data
  sample_data <- read.csv(sample_file, row.names = 1)
  
  # Load taxonomic table
  tax_data <- read.csv(tax_file, row.names = 1, stringsAsFactors = FALSE)
  tax_table <- tax_data[, 1:7]  # Assuming taxonomic ranks are in columns 1 to 7
  
  # Create phyloseq object
  physeq_obj <- phyloseq(otu_table(otu_matrix, taxa_are_rows = TRUE),
                         sample_data,
                         tax_table)
  
  return(physeq_obj)
}

# this is how the function is run

# # Provide file paths
# otu_file <- "path/to/otu_table.csv"
# sample_file <- "path/to/sample_data.csv"
# tax_file <- "path/to/taxonomic_table.csv"
# 
# # Create phyloseq object
# physeq_obj <- create_phyloseq(otu_file, sample_file, tax_file)

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])

dim(crc_CPM_OTU_S)
[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 
length(ceb)
[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=