title: “Proteomic Analysis pipeline” output: html_notebook

# Install packages from CRAN:
install.packages(c("dplyr", "ggplot2", "tidyr", "BiocManager"))
# Install Bioconductor packages:
BiocManager::install(c("DEqMS", "fgsea"))
library(dplyr)
library(ggplot2)
library(tidyr)
library(DEqMS)
library(fgsea)
all_proteins <- read.table("MS.Proteins_data.txt", sep = '\t', header = TRUE)
dim(all_proteins)
#We then filter out the contaminant proteins, add a separate column with gene names, as well as automatically detect and rename the columns with quantitative values, identifying them based on the “Abundance.Ratio” string in the name as follows:
  rename_ratios <- function(df) {
  #Initialize the vector of ratio columns
  ratio_columns <- character()
  for ( c in colnames(df) ) {
    #check the columns one by one
    if ( grepl("Abundance.Ratio", c) ) {
      new_colname <- strsplit(c, "\\.")[[1]][[3]]
      names(df)[names(df) == c] <- new_colname
      #all_proteins <- rename(all_proteins, new_colname = c)
      ratio_columns <- c(ratio_columns, new_colname)
    }
  }
  list(df, ratio_columns)
}

res <- rename_ratios(all_proteins)
all_proteins <- res[[1]]
quan_columns <- res[[2]]
quan_columns
#we can check distributions of quantitative values in each sample after the filtering, formatting and log-transformation
dfWide <- all_proteins %>%
  filter(!grepl("cont_",Accession)) %>%
  subset (select=c("Accession", quan_columns) ) %>%
  na.omit()

rownames(dfWide) <- dfWide$Accession
dfWide$Accession <- NULL
dfWide <- log2(dfWide)

#Box Plot
boxplot(
  Log2_Abund~Sample, data = gather(dfWide, Sample, Log2_Abund),
  main = "Original Log2 Ratios"
  )
  #get median 
  #For each column, subtract the median of the column from all of it's values
dfNorm <- mapply('-', dfWide, apply(dfWide,2,median))
#Transform into a dataframe
dfNorm <- as.data.frame(dfNorm, row.names = row.names(dfWide))
boxplot(
  Log2_Abund~Sample, data = gather(dfNorm, Sample, Log2_Abund),
  main = "Normalized Log2 Ratios"
  )
  ## PCA analysis
  dfWide.t <-  t(dfWide)
dfWide.pca <- prcomp(dfWide.t, center = TRUE, scale. = FALSE)

#Plot the first and the second PCs
dfWide.pca <- as.data.frame(dfWide.pca$x)
dfWide.pca$Group <- sapply(
  as.character( row.names(dfWide.pca) ),
  function(x) {
    strsplit(x, "_")[[1]][[1]]
  }
)
#Principal components 1 and 2
ggplot(
  dfWide.pca,
  aes(x = PC1, y = PC2, colour = Group )
  ) +
  geom_point(shape=19, size=4, alpha = 0.7)+
  geom_hline(yintercept = 0, colour = "gray65") +
  geom_vline(xintercept = 0, colour = "gray65") +
  ggtitle("PCA On Proteins") +
  theme_classic()
  #to analyze differential abundances using ANOVA and T-test
  dfANOVA <- dfWide
dfANOVA$anovaPval <- apply(dfANOVA, 1, function(x) {
  df <- as.data.frame(x)
  #Select the samples for ANOVA
  #Important if you need to exclude some of the samples from the calculation
  #cols_anova <- c("his4_1", "his4_2", "his4_3", "met6_1", "met6_2", "met6_3", "ura2_1", "ura2_2", "ura2_3")
  df$Sample <- rownames(df)
  df <- df[ df$Sample %in% cols_anova, ]
  
  #Define groups in sync with the selected columns
  #OBS: format-dependent
  df$Group <- as.vector(
    sapply(
      cols_anova,
      function(x) { strsplit(x, "_")[[1]][[1]] }
      )
    )
  anovaResults <- aov(x ~ Group, data = df)
  #This Very exciting expression is how to extract the p-value from the aov summary
  return(summary(anovaResults)[[1]]["Pr(>F)"][[1]][[1]])
})
#Benjamini-Hochberg correction for multiple testing
dfANOVA$adjPval <- p.adjust(dfANOVA$anovaPval, method = "BH")
#Add group averages
for ( i in names(groups) ) {
  dfANOVA[i] <- apply(
    dfANOVA, 1, function(x) {
      #print(x)
      #print(typeof(x))
      mean( x[ groups[[i]] ] )
    }
  )
}
dfANOVA <- dfWide
dfANOVA$anovaPval <- apply(dfANOVA, 1, function(x) {
  df <- as.data.frame(x)
  #Select the samples for ANOVA
  #Important if you need to exclude some of the samples from the calculation
  #cols_anova <- c("his4_1", "his4_2", "his4_3", "met6_1", "met6_2", "met6_3", "ura2_1", "ura2_2", "ura2_3")
  df$Sample <- rownames(df)
  df <- df[ df$Sample %in% cols_anova, ]
  
  #Define groups in sync with the selected columns
  #OBS: format-dependent
  df$Group <- as.vector(
    sapply(
      cols_anova,
      function(x) { strsplit(x, "_")[[1]][[1]] }
      )
    )
  anovaResults <- aov(x ~ Group, data = df)
  #This Very exciting expression is how to extract the p-value from the aov summary
  return(summary(anovaResults)[[1]]["Pr(>F)"][[1]][[1]])
})
#Benjamini-Hochberg correction for multiple testing
dfANOVA$adjPval <- p.adjust(dfANOVA$anovaPval, method = "BH")
#Add group averages
for ( i in names(groups) ) {
  dfANOVA[i] <- apply(
    dfANOVA, 1, function(x) {
      #print(x)
      #print(typeof(x))
      mean( x[ groups[[i]] ] )
    }
  )
}
#check how many proteins pass these filtering conditions
dfANOVA.Sign <- dfANOVA %>%
  filter(adjPval <= 0.05 & MaxLog2FC >= log2(1.3) ) %>%
  select(cols_anova)
dim(dfANOVA.Sign)
#t-test
calc_ttest <- function(df, groupping, gr1, gr2, maxAdjP, minFC) {
  df <- df[ c( groupping[[gr1]], groupping[[gr2]]  ) ]
  #Log2 fold change group2 - group1
  df$Log2FC <- apply(
    df, 1, function(x) {
      mean( x[ groupping[[gr2]] ] ) - mean( x[ groupping[[gr1]] ] )
    }
  )
  
  #T-test with equal variance
  df$T_Pval <- apply(
    df, 1, function(x) {
      res <- t.test(
        x[ groupping[[gr2]] ], x[ groupping[[gr1]] ],
        alternative = "two.sided", var.equal = TRUE
        )
      mean( x[ groupping[[gr2]] ] ) - mean( x[ groupping[[gr1]] ] )
      res$p.value
    }
  )
  #Benjamini-Hochberg correction for multiple testing
  df$adjPval <- p.adjust(df$T_Pval, method = "BH")
  df$Log10adjPval <- -1*log10(df$adjPval)
  #Add the categorical column for easier visualization
  df$Diff_Abund <- apply(
    df, 1, function(x) {
      if (x[["adjPval"]] <= maxAdjP & x[["Log2FC"]] >= minFC) {
        return( paste("Up in", gr2) )
      } else if (x[["adjPval"]] <= maxAdjP & x[["Log2FC"]] <= -1*minFC) {
        return( paste("Up in", gr1) )
      } else {
        return('Non-significant')
      }
    }
  )
  df
}

maxAdjP <- 0.05
minLog2FC <- round(log2(1.3), 3)
gr1 <- "met6"
gr2 <- "his4"
dfTtest <- calc_ttest(dfWide, groups, gr1, gr2, maxAdjP, minLog2FC )
##Volcano plot
ggplot(
  dfTtest,
  aes(x = Log2FC, y = Log10adjPval, colour = Diff_Abund )
) +
  geom_point(shape=19, size=2, alpha = 0.6)+
  geom_hline(yintercept = -1*log10(maxAdjP), colour = "gray65") +
  geom_vline(xintercept = 0, colour = "gray65") +
  geom_vline(xintercept = -1*minLog2FC, colour = "gray65") +
  geom_vline(xintercept = minLog2FC, colour = "gray65") +
  ggtitle(
    paste(
      "T-test ", gr1, " vs ", gr2,
      " Adjusted P-value<=", maxAdjP, " Log2 FC>=", minLog2FC,
      sep=""
      )
    ) +
  theme_classic() +
  theme(
    legend.title = element_blank(), legend.text = element_text(size=12),
    plot.title = element_text(size=16)
    ) +
  labs(x = paste("Log2 FC", gr2, "-", gr1), y = "-Log10 Adj. P-value" ) +
  geom_text(
    data = subset(dfTtest, Log2FC >=0.9 | Log2FC <= -0.8),
    aes( Log2FC, Log10adjPval, label = Gene),
    alpha = 0.6, hjust = 0.5, vjust = -0.6
    )
   ##Apply Limma and DEqMS
    dfD <- dfWide[cols_anova]
#Define the design vector
cond = as.factor(
  c("his4", "his4", "his4", "met6", "met6", "met6", "ura2", "ura2", "ura2")
)
design = model.matrix(~0+cond)
colnames(design) = gsub("cond","",colnames(design))
#Make contrasts
x <- c(
  "his4-met6", "his4-ura2", "ura2-met6" 
  )
contrast =  makeContrasts(contrasts=x,levels=design)
fit1 <- lmFit(dfD, design)
fit2 <- contrasts.fit(fit1,contrasts = contrast)
fit3 <- eBayes(fit2)
#Extract PSM count information
psm_count_table <- dfD %>%
  merge(
    all_proteins[c("Accession", "Number.of.PSMs")],
    by.x="row.names", by.y="Accession",  suffixes=c("", "_"), sort=FALSE
    )
row.names(psm_count_table) <- psm_count_table$Row.names
psm_count_table <- psm_count_table[c("Number.of.PSMs")]
fit3$count = psm_count_table[rownames(fit3$coefficients),"Number.of.PSMs"]
fit4 = spectraCounteBayes(fit3)
#The function spectraCounteBayes is the part that is specific to DEqMS. We extract the information about the number of PSMs per protein that we had in our original data, and spectraCounteBayes uses it to calculate the data-dependent variance in the fit4. Let’s look at the resulting relationship between the number of PSMs and variance
VarianceBoxplot(
  fit4, n=30, main="TKO Variance according to DEqMS", xlab="PSM count"
  )
  #Gene Set Enrichment Analysis
Fgsea requires a ranked gene list to perform the enrichment analysis. To incorporate the statistical results and the fold changes between strains, we will calculate a product of the adjusted p-value from DEqMS and log-FC, and use it for ranking.
 dfGSEA <- res_DEqMS[c("Gene", "logFC", "log.sca.adj.pval")]
dfGSEA$rank <- apply(
  dfGSEA, 1, function(x) {
    as.numeric(x[[2]]) * as.numeric(x[[3]])
  }
)

ranks <- as.numeric(dfGSEA$rank)
names(ranks) <- dfGSEA$Gene
ranks <- sort(ranks)
ranks[1:10]
annotations <- gmtPathways("go-bp_gene-symbols_uniprot.gmt")

res_gsea <- fgseaMultilevel(annotations, ranks, minSize=15, maxSize=500)
#The pathways in the result file get an Enrichment Score (ES) and Normalized Enrichment Score (NES), those point to the pathways with the most significant over-representation among the differentially abundant genes.
res_gsea[ES < 0][head(order(pval), n=10),]
#most significant negative ES:
  

  
  
LS0tCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAotLS0KLS0tCnRpdGxlOiAiUHJvdGVvbWljIEFuYWx5c2lzIHBpcGVsaW5lIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKCmBgYHtyfQojIEluc3RhbGwgcGFja2FnZXMgZnJvbSBDUkFOOgppbnN0YWxsLnBhY2thZ2VzKGMoImRwbHlyIiwgImdncGxvdDIiLCAidGlkeXIiLCAiQmlvY01hbmFnZXIiKSkKIyBJbnN0YWxsIEJpb2NvbmR1Y3RvciBwYWNrYWdlczoKQmlvY01hbmFnZXI6Omluc3RhbGwoYygiREVxTVMiLCAiZmdzZWEiKSkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KERFcU1TKQpsaWJyYXJ5KGZnc2VhKQphbGxfcHJvdGVpbnMgPC0gcmVhZC50YWJsZSgiTVMuUHJvdGVpbnNfZGF0YS50eHQiLCBzZXAgPSAnXHQnLCBoZWFkZXIgPSBUUlVFKQpkaW0oYWxsX3Byb3RlaW5zKQojV2UgdGhlbiBmaWx0ZXIgb3V0IHRoZSBjb250YW1pbmFudCBwcm90ZWlucywgYWRkIGEgc2VwYXJhdGUgY29sdW1uIHdpdGggZ2VuZSBuYW1lcywgYXMgd2VsbCBhcyBhdXRvbWF0aWNhbGx5IGRldGVjdCBhbmQgcmVuYW1lIHRoZSBjb2x1bW5zIHdpdGggcXVhbnRpdGF0aXZlIHZhbHVlcywgaWRlbnRpZnlpbmcgdGhlbSBiYXNlZCBvbiB0aGUg4oCcQWJ1bmRhbmNlLlJhdGlv4oCdIHN0cmluZyBpbiB0aGUgbmFtZSBhcyBmb2xsb3dzOgogIHJlbmFtZV9yYXRpb3MgPC0gZnVuY3Rpb24oZGYpIHsKICAjSW5pdGlhbGl6ZSB0aGUgdmVjdG9yIG9mIHJhdGlvIGNvbHVtbnMKICByYXRpb19jb2x1bW5zIDwtIGNoYXJhY3RlcigpCiAgZm9yICggYyBpbiBjb2xuYW1lcyhkZikgKSB7CiAgICAjY2hlY2sgdGhlIGNvbHVtbnMgb25lIGJ5IG9uZQogICAgaWYgKCBncmVwbCgiQWJ1bmRhbmNlLlJhdGlvIiwgYykgKSB7CiAgICAgIG5ld19jb2xuYW1lIDwtIHN0cnNwbGl0KGMsICJcXC4iKVtbMV1dW1szXV0KICAgICAgbmFtZXMoZGYpW25hbWVzKGRmKSA9PSBjXSA8LSBuZXdfY29sbmFtZQogICAgICAjYWxsX3Byb3RlaW5zIDwtIHJlbmFtZShhbGxfcHJvdGVpbnMsIG5ld19jb2xuYW1lID0gYykKICAgICAgcmF0aW9fY29sdW1ucyA8LSBjKHJhdGlvX2NvbHVtbnMsIG5ld19jb2xuYW1lKQogICAgfQogIH0KICBsaXN0KGRmLCByYXRpb19jb2x1bW5zKQp9CgpyZXMgPC0gcmVuYW1lX3JhdGlvcyhhbGxfcHJvdGVpbnMpCmFsbF9wcm90ZWlucyA8LSByZXNbWzFdXQpxdWFuX2NvbHVtbnMgPC0gcmVzW1syXV0KcXVhbl9jb2x1bW5zCiN3ZSBjYW4gY2hlY2sgZGlzdHJpYnV0aW9ucyBvZiBxdWFudGl0YXRpdmUgdmFsdWVzIGluIGVhY2ggc2FtcGxlIGFmdGVyIHRoZSBmaWx0ZXJpbmcsIGZvcm1hdHRpbmcgYW5kIGxvZy10cmFuc2Zvcm1hdGlvbgpkZldpZGUgPC0gYWxsX3Byb3RlaW5zICU+JQogIGZpbHRlcighZ3JlcGwoImNvbnRfIixBY2Nlc3Npb24pKSAlPiUKICBzdWJzZXQgKHNlbGVjdD1jKCJBY2Nlc3Npb24iLCBxdWFuX2NvbHVtbnMpICkgJT4lCiAgbmEub21pdCgpCgpyb3duYW1lcyhkZldpZGUpIDwtIGRmV2lkZSRBY2Nlc3Npb24KZGZXaWRlJEFjY2Vzc2lvbiA8LSBOVUxMCmRmV2lkZSA8LSBsb2cyKGRmV2lkZSkKCiNCb3ggUGxvdApib3hwbG90KAogIExvZzJfQWJ1bmR+U2FtcGxlLCBkYXRhID0gZ2F0aGVyKGRmV2lkZSwgU2FtcGxlLCBMb2cyX0FidW5kKSwKICBtYWluID0gIk9yaWdpbmFsIExvZzIgUmF0aW9zIgogICkKICAjZ2V0IG1lZGlhbiAKICAjRm9yIGVhY2ggY29sdW1uLCBzdWJ0cmFjdCB0aGUgbWVkaWFuIG9mIHRoZSBjb2x1bW4gZnJvbSBhbGwgb2YgaXQncyB2YWx1ZXMKZGZOb3JtIDwtIG1hcHBseSgnLScsIGRmV2lkZSwgYXBwbHkoZGZXaWRlLDIsbWVkaWFuKSkKI1RyYW5zZm9ybSBpbnRvIGEgZGF0YWZyYW1lCmRmTm9ybSA8LSBhcy5kYXRhLmZyYW1lKGRmTm9ybSwgcm93Lm5hbWVzID0gcm93Lm5hbWVzKGRmV2lkZSkpCmJveHBsb3QoCiAgTG9nMl9BYnVuZH5TYW1wbGUsIGRhdGEgPSBnYXRoZXIoZGZOb3JtLCBTYW1wbGUsIExvZzJfQWJ1bmQpLAogIG1haW4gPSAiTm9ybWFsaXplZCBMb2cyIFJhdGlvcyIKICApCiAgIyMgUENBIGFuYWx5c2lzCiAgZGZXaWRlLnQgPC0gIHQoZGZXaWRlKQpkZldpZGUucGNhIDwtIHByY29tcChkZldpZGUudCwgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gRkFMU0UpCgojUGxvdCB0aGUgZmlyc3QgYW5kIHRoZSBzZWNvbmQgUENzCmRmV2lkZS5wY2EgPC0gYXMuZGF0YS5mcmFtZShkZldpZGUucGNhJHgpCmRmV2lkZS5wY2EkR3JvdXAgPC0gc2FwcGx5KAogIGFzLmNoYXJhY3Rlciggcm93Lm5hbWVzKGRmV2lkZS5wY2EpICksCiAgZnVuY3Rpb24oeCkgewogICAgc3Ryc3BsaXQoeCwgIl8iKVtbMV1dW1sxXV0KICB9CikKI1ByaW5jaXBhbCBjb21wb25lbnRzIDEgYW5kIDIKZ2dwbG90KAogIGRmV2lkZS5wY2EsCiAgYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG91ciA9IEdyb3VwICkKICApICsKICBnZW9tX3BvaW50KHNoYXBlPTE5LCBzaXplPTQsIGFscGhhID0gMC43KSsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBjb2xvdXIgPSAiZ3JheTY1IikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGNvbG91ciA9ICJncmF5NjUiKSArCiAgZ2d0aXRsZSgiUENBIE9uIFByb3RlaW5zIikgKwogIHRoZW1lX2NsYXNzaWMoKQogICN0byBhbmFseXplIGRpZmZlcmVudGlhbCBhYnVuZGFuY2VzIHVzaW5nIEFOT1ZBIGFuZCBULXRlc3QKICBkZkFOT1ZBIDwtIGRmV2lkZQpkZkFOT1ZBJGFub3ZhUHZhbCA8LSBhcHBseShkZkFOT1ZBLCAxLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogICNTZWxlY3QgdGhlIHNhbXBsZXMgZm9yIEFOT1ZBCiAgI0ltcG9ydGFudCBpZiB5b3UgbmVlZCB0byBleGNsdWRlIHNvbWUgb2YgdGhlIHNhbXBsZXMgZnJvbSB0aGUgY2FsY3VsYXRpb24KICAjY29sc19hbm92YSA8LSBjKCJoaXM0XzEiLCAiaGlzNF8yIiwgImhpczRfMyIsICJtZXQ2XzEiLCAibWV0Nl8yIiwgIm1ldDZfMyIsICJ1cmEyXzEiLCAidXJhMl8yIiwgInVyYTJfMyIpCiAgZGYkU2FtcGxlIDwtIHJvd25hbWVzKGRmKQogIGRmIDwtIGRmWyBkZiRTYW1wbGUgJWluJSBjb2xzX2Fub3ZhLCBdCiAgCiAgI0RlZmluZSBncm91cHMgaW4gc3luYyB3aXRoIHRoZSBzZWxlY3RlZCBjb2x1bW5zCiAgI09CUzogZm9ybWF0LWRlcGVuZGVudAogIGRmJEdyb3VwIDwtIGFzLnZlY3RvcigKICAgIHNhcHBseSgKICAgICAgY29sc19hbm92YSwKICAgICAgZnVuY3Rpb24oeCkgeyBzdHJzcGxpdCh4LCAiXyIpW1sxXV1bWzFdXSB9CiAgICAgICkKICAgICkKICBhbm92YVJlc3VsdHMgPC0gYW92KHggfiBHcm91cCwgZGF0YSA9IGRmKQogICNUaGlzIFZlcnkgZXhjaXRpbmcgZXhwcmVzc2lvbiBpcyBob3cgdG8gZXh0cmFjdCB0aGUgcC12YWx1ZSBmcm9tIHRoZSBhb3Ygc3VtbWFyeQogIHJldHVybihzdW1tYXJ5KGFub3ZhUmVzdWx0cylbWzFdXVsiUHIoPkYpIl1bWzFdXVtbMV1dKQp9KQojQmVuamFtaW5pLUhvY2hiZXJnIGNvcnJlY3Rpb24gZm9yIG11bHRpcGxlIHRlc3RpbmcKZGZBTk9WQSRhZGpQdmFsIDwtIHAuYWRqdXN0KGRmQU5PVkEkYW5vdmFQdmFsLCBtZXRob2QgPSAiQkgiKQojQWRkIGdyb3VwIGF2ZXJhZ2VzCmZvciAoIGkgaW4gbmFtZXMoZ3JvdXBzKSApIHsKICBkZkFOT1ZBW2ldIDwtIGFwcGx5KAogICAgZGZBTk9WQSwgMSwgZnVuY3Rpb24oeCkgewogICAgICAjcHJpbnQoeCkKICAgICAgI3ByaW50KHR5cGVvZih4KSkKICAgICAgbWVhbiggeFsgZ3JvdXBzW1tpXV0gXSApCiAgICB9CiAgKQp9CmRmQU5PVkEgPC0gZGZXaWRlCmRmQU5PVkEkYW5vdmFQdmFsIDwtIGFwcGx5KGRmQU5PVkEsIDEsIGZ1bmN0aW9uKHgpIHsKICBkZiA8LSBhcy5kYXRhLmZyYW1lKHgpCiAgI1NlbGVjdCB0aGUgc2FtcGxlcyBmb3IgQU5PVkEKICAjSW1wb3J0YW50IGlmIHlvdSBuZWVkIHRvIGV4Y2x1ZGUgc29tZSBvZiB0aGUgc2FtcGxlcyBmcm9tIHRoZSBjYWxjdWxhdGlvbgogICNjb2xzX2Fub3ZhIDwtIGMoImhpczRfMSIsICJoaXM0XzIiLCAiaGlzNF8zIiwgIm1ldDZfMSIsICJtZXQ2XzIiLCAibWV0Nl8zIiwgInVyYTJfMSIsICJ1cmEyXzIiLCAidXJhMl8zIikKICBkZiRTYW1wbGUgPC0gcm93bmFtZXMoZGYpCiAgZGYgPC0gZGZbIGRmJFNhbXBsZSAlaW4lIGNvbHNfYW5vdmEsIF0KICAKICAjRGVmaW5lIGdyb3VwcyBpbiBzeW5jIHdpdGggdGhlIHNlbGVjdGVkIGNvbHVtbnMKICAjT0JTOiBmb3JtYXQtZGVwZW5kZW50CiAgZGYkR3JvdXAgPC0gYXMudmVjdG9yKAogICAgc2FwcGx5KAogICAgICBjb2xzX2Fub3ZhLAogICAgICBmdW5jdGlvbih4KSB7IHN0cnNwbGl0KHgsICJfIilbWzFdXVtbMV1dIH0KICAgICAgKQogICAgKQogIGFub3ZhUmVzdWx0cyA8LSBhb3YoeCB+IEdyb3VwLCBkYXRhID0gZGYpCiAgI1RoaXMgVmVyeSBleGNpdGluZyBleHByZXNzaW9uIGlzIGhvdyB0byBleHRyYWN0IHRoZSBwLXZhbHVlIGZyb20gdGhlIGFvdiBzdW1tYXJ5CiAgcmV0dXJuKHN1bW1hcnkoYW5vdmFSZXN1bHRzKVtbMV1dWyJQcig+RikiXVtbMV1dW1sxXV0pCn0pCiNCZW5qYW1pbmktSG9jaGJlcmcgY29ycmVjdGlvbiBmb3IgbXVsdGlwbGUgdGVzdGluZwpkZkFOT1ZBJGFkalB2YWwgPC0gcC5hZGp1c3QoZGZBTk9WQSRhbm92YVB2YWwsIG1ldGhvZCA9ICJCSCIpCiNBZGQgZ3JvdXAgYXZlcmFnZXMKZm9yICggaSBpbiBuYW1lcyhncm91cHMpICkgewogIGRmQU5PVkFbaV0gPC0gYXBwbHkoCiAgICBkZkFOT1ZBLCAxLCBmdW5jdGlvbih4KSB7CiAgICAgICNwcmludCh4KQogICAgICAjcHJpbnQodHlwZW9mKHgpKQogICAgICBtZWFuKCB4WyBncm91cHNbW2ldXSBdICkKICAgIH0KICApCn0KI2NoZWNrIGhvdyBtYW55IHByb3RlaW5zIHBhc3MgdGhlc2UgZmlsdGVyaW5nIGNvbmRpdGlvbnMKZGZBTk9WQS5TaWduIDwtIGRmQU5PVkEgJT4lCiAgZmlsdGVyKGFkalB2YWwgPD0gMC4wNSAmIE1heExvZzJGQyA+PSBsb2cyKDEuMykgKSAlPiUKICBzZWxlY3QoY29sc19hbm92YSkKZGltKGRmQU5PVkEuU2lnbikKI3QtdGVzdApjYWxjX3R0ZXN0IDwtIGZ1bmN0aW9uKGRmLCBncm91cHBpbmcsIGdyMSwgZ3IyLCBtYXhBZGpQLCBtaW5GQykgewogIGRmIDwtIGRmWyBjKCBncm91cHBpbmdbW2dyMV1dLCBncm91cHBpbmdbW2dyMl1dICApIF0KICAjTG9nMiBmb2xkIGNoYW5nZSBncm91cDIgLSBncm91cDEKICBkZiRMb2cyRkMgPC0gYXBwbHkoCiAgICBkZiwgMSwgZnVuY3Rpb24oeCkgewogICAgICBtZWFuKCB4WyBncm91cHBpbmdbW2dyMl1dIF0gKSAtIG1lYW4oIHhbIGdyb3VwcGluZ1tbZ3IxXV0gXSApCiAgICB9CiAgKQogIAogICNULXRlc3Qgd2l0aCBlcXVhbCB2YXJpYW5jZQogIGRmJFRfUHZhbCA8LSBhcHBseSgKICAgIGRmLCAxLCBmdW5jdGlvbih4KSB7CiAgICAgIHJlcyA8LSB0LnRlc3QoCiAgICAgICAgeFsgZ3JvdXBwaW5nW1tncjJdXSBdLCB4WyBncm91cHBpbmdbW2dyMV1dIF0sCiAgICAgICAgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIiwgdmFyLmVxdWFsID0gVFJVRQogICAgICAgICkKICAgICAgbWVhbiggeFsgZ3JvdXBwaW5nW1tncjJdXSBdICkgLSBtZWFuKCB4WyBncm91cHBpbmdbW2dyMV1dIF0gKQogICAgICByZXMkcC52YWx1ZQogICAgfQogICkKICAjQmVuamFtaW5pLUhvY2hiZXJnIGNvcnJlY3Rpb24gZm9yIG11bHRpcGxlIHRlc3RpbmcKICBkZiRhZGpQdmFsIDwtIHAuYWRqdXN0KGRmJFRfUHZhbCwgbWV0aG9kID0gIkJIIikKICBkZiRMb2cxMGFkalB2YWwgPC0gLTEqbG9nMTAoZGYkYWRqUHZhbCkKICAjQWRkIHRoZSBjYXRlZ29yaWNhbCBjb2x1bW4gZm9yIGVhc2llciB2aXN1YWxpemF0aW9uCiAgZGYkRGlmZl9BYnVuZCA8LSBhcHBseSgKICAgIGRmLCAxLCBmdW5jdGlvbih4KSB7CiAgICAgIGlmICh4W1siYWRqUHZhbCJdXSA8PSBtYXhBZGpQICYgeFtbIkxvZzJGQyJdXSA+PSBtaW5GQykgewogICAgICAgIHJldHVybiggcGFzdGUoIlVwIGluIiwgZ3IyKSApCiAgICAgIH0gZWxzZSBpZiAoeFtbImFkalB2YWwiXV0gPD0gbWF4QWRqUCAmIHhbWyJMb2cyRkMiXV0gPD0gLTEqbWluRkMpIHsKICAgICAgICByZXR1cm4oIHBhc3RlKCJVcCBpbiIsIGdyMSkgKQogICAgICB9IGVsc2UgewogICAgICAgIHJldHVybignTm9uLXNpZ25pZmljYW50JykKICAgICAgfQogICAgfQogICkKICBkZgp9CgptYXhBZGpQIDwtIDAuMDUKbWluTG9nMkZDIDwtIHJvdW5kKGxvZzIoMS4zKSwgMykKZ3IxIDwtICJtZXQ2IgpncjIgPC0gImhpczQiCmRmVHRlc3QgPC0gY2FsY190dGVzdChkZldpZGUsIGdyb3VwcywgZ3IxLCBncjIsIG1heEFkalAsIG1pbkxvZzJGQyApCiMjVm9sY2FubyBwbG90CmdncGxvdCgKICBkZlR0ZXN0LAogIGFlcyh4ID0gTG9nMkZDLCB5ID0gTG9nMTBhZGpQdmFsLCBjb2xvdXIgPSBEaWZmX0FidW5kICkKKSArCiAgZ2VvbV9wb2ludChzaGFwZT0xOSwgc2l6ZT0yLCBhbHBoYSA9IDAuNikrCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLTEqbG9nMTAobWF4QWRqUCksIGNvbG91ciA9ICJncmF5NjUiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgY29sb3VyID0gImdyYXk2NSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAtMSptaW5Mb2cyRkMsIGNvbG91ciA9ICJncmF5NjUiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWluTG9nMkZDLCBjb2xvdXIgPSAiZ3JheTY1IikgKwogIGdndGl0bGUoCiAgICBwYXN0ZSgKICAgICAgIlQtdGVzdCAiLCBncjEsICIgdnMgIiwgZ3IyLAogICAgICAiIEFkanVzdGVkIFAtdmFsdWU8PSIsIG1heEFkalAsICIgTG9nMiBGQz49IiwgbWluTG9nMkZDLAogICAgICBzZXA9IiIKICAgICAgKQogICAgKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZSgKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTYpCiAgICApICsKICBsYWJzKHggPSBwYXN0ZSgiTG9nMiBGQyIsIGdyMiwgIi0iLCBncjEpLCB5ID0gIi1Mb2cxMCBBZGouIFAtdmFsdWUiICkgKwogIGdlb21fdGV4dCgKICAgIGRhdGEgPSBzdWJzZXQoZGZUdGVzdCwgTG9nMkZDID49MC45IHwgTG9nMkZDIDw9IC0wLjgpLAogICAgYWVzKCBMb2cyRkMsIExvZzEwYWRqUHZhbCwgbGFiZWwgPSBHZW5lKSwKICAgIGFscGhhID0gMC42LCBoanVzdCA9IDAuNSwgdmp1c3QgPSAtMC42CiAgICApCiAgICMjQXBwbHkgTGltbWEgYW5kIERFcU1TCiAgICBkZkQgPC0gZGZXaWRlW2NvbHNfYW5vdmFdCiNEZWZpbmUgdGhlIGRlc2lnbiB2ZWN0b3IKY29uZCA9IGFzLmZhY3RvcigKICBjKCJoaXM0IiwgImhpczQiLCAiaGlzNCIsICJtZXQ2IiwgIm1ldDYiLCAibWV0NiIsICJ1cmEyIiwgInVyYTIiLCAidXJhMiIpCikKZGVzaWduID0gbW9kZWwubWF0cml4KH4wK2NvbmQpCmNvbG5hbWVzKGRlc2lnbikgPSBnc3ViKCJjb25kIiwiIixjb2xuYW1lcyhkZXNpZ24pKQojTWFrZSBjb250cmFzdHMKeCA8LSBjKAogICJoaXM0LW1ldDYiLCAiaGlzNC11cmEyIiwgInVyYTItbWV0NiIgCiAgKQpjb250cmFzdCA9ICBtYWtlQ29udHJhc3RzKGNvbnRyYXN0cz14LGxldmVscz1kZXNpZ24pCmZpdDEgPC0gbG1GaXQoZGZELCBkZXNpZ24pCmZpdDIgPC0gY29udHJhc3RzLmZpdChmaXQxLGNvbnRyYXN0cyA9IGNvbnRyYXN0KQpmaXQzIDwtIGVCYXllcyhmaXQyKQojRXh0cmFjdCBQU00gY291bnQgaW5mb3JtYXRpb24KcHNtX2NvdW50X3RhYmxlIDwtIGRmRCAlPiUKICBtZXJnZSgKICAgIGFsbF9wcm90ZWluc1tjKCJBY2Nlc3Npb24iLCAiTnVtYmVyLm9mLlBTTXMiKV0sCiAgICBieS54PSJyb3cubmFtZXMiLCBieS55PSJBY2Nlc3Npb24iLCAgc3VmZml4ZXM9YygiIiwgIl8iKSwgc29ydD1GQUxTRQogICAgKQpyb3cubmFtZXMocHNtX2NvdW50X3RhYmxlKSA8LSBwc21fY291bnRfdGFibGUkUm93Lm5hbWVzCnBzbV9jb3VudF90YWJsZSA8LSBwc21fY291bnRfdGFibGVbYygiTnVtYmVyLm9mLlBTTXMiKV0KZml0MyRjb3VudCA9IHBzbV9jb3VudF90YWJsZVtyb3duYW1lcyhmaXQzJGNvZWZmaWNpZW50cyksIk51bWJlci5vZi5QU01zIl0KZml0NCA9IHNwZWN0cmFDb3VudGVCYXllcyhmaXQzKQojVGhlIGZ1bmN0aW9uIHNwZWN0cmFDb3VudGVCYXllcyBpcyB0aGUgcGFydCB0aGF0IGlzIHNwZWNpZmljIHRvIERFcU1TLiBXZSBleHRyYWN0IHRoZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbnVtYmVyIG9mIFBTTXMgcGVyIHByb3RlaW4gdGhhdCB3ZSBoYWQgaW4gb3VyIG9yaWdpbmFsIGRhdGEsIGFuZCBzcGVjdHJhQ291bnRlQmF5ZXMgdXNlcyBpdCB0byBjYWxjdWxhdGUgdGhlIGRhdGEtZGVwZW5kZW50IHZhcmlhbmNlIGluIHRoZSBmaXQ0LiBMZXTigJlzIGxvb2sgYXQgdGhlIHJlc3VsdGluZyByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgbnVtYmVyIG9mIFBTTXMgYW5kIHZhcmlhbmNlClZhcmlhbmNlQm94cGxvdCgKICBmaXQ0LCBuPTMwLCBtYWluPSJUS08gVmFyaWFuY2UgYWNjb3JkaW5nIHRvIERFcU1TIiwgeGxhYj0iUFNNIGNvdW50IgogICkKICAjR2VuZSBTZXQgRW5yaWNobWVudCBBbmFseXNpcwpGZ3NlYSByZXF1aXJlcyBhIHJhbmtlZCBnZW5lIGxpc3QgdG8gcGVyZm9ybSB0aGUgZW5yaWNobWVudCBhbmFseXNpcy4gVG8gaW5jb3Jwb3JhdGUgdGhlIHN0YXRpc3RpY2FsIHJlc3VsdHMgYW5kIHRoZSBmb2xkIGNoYW5nZXMgYmV0d2VlbiBzdHJhaW5zLCB3ZSB3aWxsIGNhbGN1bGF0ZSBhIHByb2R1Y3Qgb2YgdGhlIGFkanVzdGVkIHAtdmFsdWUgZnJvbSBERXFNUyBhbmQgbG9nLUZDLCBhbmQgdXNlIGl0IGZvciByYW5raW5nLgogZGZHU0VBIDwtIHJlc19ERXFNU1tjKCJHZW5lIiwgImxvZ0ZDIiwgImxvZy5zY2EuYWRqLnB2YWwiKV0KZGZHU0VBJHJhbmsgPC0gYXBwbHkoCiAgZGZHU0VBLCAxLCBmdW5jdGlvbih4KSB7CiAgICBhcy5udW1lcmljKHhbWzJdXSkgKiBhcy5udW1lcmljKHhbWzNdXSkKICB9CikKCnJhbmtzIDwtIGFzLm51bWVyaWMoZGZHU0VBJHJhbmspCm5hbWVzKHJhbmtzKSA8LSBkZkdTRUEkR2VuZQpyYW5rcyA8LSBzb3J0KHJhbmtzKQpyYW5rc1sxOjEwXQphbm5vdGF0aW9ucyA8LSBnbXRQYXRod2F5cygiZ28tYnBfZ2VuZS1zeW1ib2xzX3VuaXByb3QuZ210IikKCnJlc19nc2VhIDwtIGZnc2VhTXVsdGlsZXZlbChhbm5vdGF0aW9ucywgcmFua3MsIG1pblNpemU9MTUsIG1heFNpemU9NTAwKQojVGhlIHBhdGh3YXlzIGluIHRoZSByZXN1bHQgZmlsZSBnZXQgYW4gRW5yaWNobWVudCBTY29yZSAoRVMpIGFuZCBOb3JtYWxpemVkIEVucmljaG1lbnQgU2NvcmUgKE5FUyksIHRob3NlIHBvaW50IHRvIHRoZSBwYXRod2F5cyB3aXRoIHRoZSBtb3N0IHNpZ25pZmljYW50IG92ZXItcmVwcmVzZW50YXRpb24gYW1vbmcgdGhlIGRpZmZlcmVudGlhbGx5IGFidW5kYW50IGdlbmVzLgpyZXNfZ3NlYVtFUyA8IDBdW2hlYWQob3JkZXIocHZhbCksIG49MTApLF0KI21vc3Qgc2lnbmlmaWNhbnQgbmVnYXRpdmUgRVM6CiAgCgogIAogIApgYGAKCg==