1 Supplementary Protocol 4 – perform manual phenotype randomizations using edgeR followed by GSEA and EM

This protocol demonstrates how to code manual phenotype permutation for RNA-seq data analyzed with edgeR and GSEA.

GSEA calculates for each tested gene-set an enrichment score (ES) which quantifies the enrichment of the gene set. A p-value is needed to estimate if this enrichment (ES) is greater than one that could be obtained by chance only. There are two ways to estimate the p-value and FDR within GSEA: gene-set permutation and phenotype permutation.

First, available as gene set permutation in GSEA,a null distribution can be constructed by randomly shuffling the genes in a given gene-sets: each time a shuffled gene-set is tested an ES is calculated. Then the observed ES for an individual gene set is compared to all the ES scores obtained for the same gene set across all the permutations to obtain an estimate of the gene set permutation FDR value.

An alternate way, available as phenotype permutation in GSEA, is to shuffle the sample labels that constitute the 2 groups to be compared. After shuffling the sample labels differential expression is recalculated. With a random shuffle of the samples not many genes should be found significantly differentially expressed and as a result not many pathways should be found significantly enriched. For each tested gene set, the observed ES is compared to all ES obtained with the shuffled samples and an FDR is estimated.

Note about phenotype permutation: In the GSEA interface, there is one option to upload an entire expression matrix and a class definition file (.cls) and perform the phenotype permutation. However the choice of tests are limited to basic statistical measures (signal to noise, t-test) and tests using non negative binomial distribution or linear models such as the ones implemented in edgeR and DEseq and recommended for RNA-seq counts data are not available. Therefore, in this protocol, we are performing the permutations outside GSEA. Differential expression is calculated within edgeR between the ‘immunoreactive’ and ‘mesenchymal’ type and GSEA is run to get the observed ES. Then, 1000 permutations are computed by shuffling the samples. Each permutation run consists of :1) shuffling the samples which results in the formation of 2 random groups. 2) calculate differential expression within edgeR between these 2 groups 3) run GSEA and obtain the ES and NES for this random permutation. Finally, for each gene-set, the p-value is calculated by counting the number of times the observed ES was greater than the ES obtained from the random permutations.

NOTE: Performing this randomization with R is very resource intensive and time consuming. (On a intel i7 computer with 16GB 1000 permutations took more than 9.5 hours)

1.1 Process RNA-seq data

This part of the supplementary protocol demonstrates filtering and scoring RNA-seq data using normalized RNA-seq count data with the edgeR R package. The section prepares the input data for GSEA.

1.1.1 Load required packages

  1. Load required Bioconductor packages into R.
knitr::opts_knit$set(cache = TRUE)

tryCatch(expr = { library("edgeR")}, 
         error = function(e) { 
           source("https://bioconductor.org/biocLite.R")
           biocLite("edgeR")}, 
         finally = library("edgeR"))

working_dir <- file.path(".","data")
rand_working_dir <- file.path(".","data", "Randomizations_round3")

#The field in the class definition file that defines the classes of the data.
data_classes <- "SUBTYPE"

analysis_name <- "Mesen_Vs_Immuno"

1.1.2 Load Expression Data

  1. Load the expression dataset of 296 tumours, with 79 classified as Immunoreactive, 71 classified as Mesenchymal, 67 classified as Differentiated, and 79 classified as Proliferative samples. The TCGA counts data was retrieved from the GDC1 and contained counts per mRNA transcript determined using the RSEM method for 19947 transcripts and 300 samples.
RNASeq <- read.table( 
  file.path(working_dir,"Supplementary_Table10_TCGA_RNASeq_rawcounts.txt"),  
  header = TRUE, sep = "\t", quote="\"", stringsAsFactors = FALSE)

1.1.3 Load subtype information

  1. Load subtype classification of samples. To calculate differential expression, we need to define at least two sample classes. A common experimental design involves cases and controls but any two classes can be used. The current dataset is divided into Mesenchymal and Immunoreactive classes (class definitions were obtained from Verhaak et al.2 Supplementary Table 1, third column). After loading the matrix, check that the column names of the expression matrix and class definitions are equal.
classDefinitions_RNASeq <- read.table( 
  file.path(working_dir, "Supplementary_Table11_RNASeq_classdefinitions.txt"), 
  header = TRUE, sep = "\t", quote="\"", stringsAsFactors = FALSE)

1.1.4 Filter Data

  1. Filter RNA-seq reads. RNA-seq data are processed following the edgeR protocol that filters reads based on the counts per million (CPM) statistic. RNA-seq read counts are converted to CPM values and genes with CPM > 1 in at least 50 of the samples are retained for further study (a gene must have at least 50 measurements with more than 1 CPM in one of the classes to be included in the analysis). This step removes genes with very low read counts that are likely not expressed in the majority of samples and cause noise in the data. Note, CPM filtering is used to remove low counts while differential gene expression analysis is based on normalized read counts which are generated below (step 6).
cpms <- cpm(RNASeq)
keep <- rowSums(cpms > 1) >= 50
counts <- RNASeq[keep,]

1.1.5 Normalization and Dispersion

  1. Data normalization, dispersion analysis is performed on the entire dataset. Created MDS-plot of all patient samples can be seen in Figure 1.
# create data structure to hold counts and subtype information for each sample.
d <- DGEList(counts=counts, group=classDefinitions_RNASeq$SUBTYPE)

#Normalize the data
d <- calcNormFactors(d)

#create multidimensional scaling(MDS) plot.  The command below will automatically 
# generate the plot containing all samples where each subtype is a different color.  
#Ideally there should be a good separation between the different classes.
mds_filename <- file.path(working_dir, "mdsplot_allsamples.png")
png(filename = mds_filename)
mds_output <- plotMDS(d, labels=NULL, pch = 1, 
col= c("darkgreen","blue","red", "orange")[factor(classDefinitions_RNASeq$SUBTYPE)], 
xlim = c(-2.5,4), ylim = c(-2.5,4))


legend("topright", 
       legend=levels(factor(classDefinitions_RNASeq$SUBTYPE)), 
       pch=c(1), col= c("darkgreen","blue","red", "orange"),title="Class",  
       bty = 'n', cex = 0.75)

dev.off()

#calculate dispersion
d <- estimateCommonDisp(d)
d <- estimateTagwiseDisp(d)

1.1.6 Filter unannotated genes

  1. (Optional) Exclude genes with missing symbols or uncharacterized genes. In this example gene entries in the dataset containing ‘?’ or starting with LOC are excluded as they represent non-annotated genes or other loci that are not present in pathway databases. The frequency of these and other non protein coding entries in your dataset will depend on the database used to align your RNA-seq data.
#the below regular expression excludes gene names that are ? or that start with LOC
# any number of additional terms can be added to the regular expresion, for example 
# to exclude any genes that start with "His" add |^His to the regular expression
exclude <- grep("\\?|^LOC", rownames(d), value=T)
d <- d[which(!rownames(d) %in% exclude),]

1.1.7 Calculate Differential expression

  1. Differential expression analysis is performed with a simple design as described in the edgeR protocol3.
#calculate differential expression statistics with a simple design
de <- exactTest(d, pair=c("Immunoreactive","Mesenchymal"))
tt_exact_test <- topTags(de,n=nrow(d))

#alternately you can also use the glm model using contrasts.  
#For a simple 2 class comparison this is not required but if you want to compare
# 1 class to the remaining 3 classes then this sort of model is useful.
classes <- factor(classDefinitions_RNASeq[,data_classes])
modelDesign <- model.matrix(~ 0 + classes)

contrast_mesenvsimmuno <- makeContrasts(
                  mesenvsimmuno ="classesMesenchymal-classesImmunoreactive",
                  levels=modelDesign)
fit_glm <- glmFit(d,modelDesign)
mesenvsimmuno <- glmLRT(fit_glm , contrast = contrast_mesenvsimmuno)
tt_mesenvsimmuno <- topTags(mesenvsimmuno,n=nrow(d))

tt <- tt_exact_test
#calculate ranks
ranks_RNAseq = sign(tt$table$logFC) * -log10(tt$table$PValue)

#gene names from the TCGA set contain gene name and entrez gene ids separated by ‘|’
# for all subsequent enrichment analysis we need to have just one id.  Separate the names 
# into their two ids.
genenames <- unlist(lapply( rownames(tt$table), function(data) 
  {unlist(strsplit(data,"\\|"))[1]}))
geneids <- unlist(lapply( rownames(tt$table),  function(data) 
  {unlist(strsplit(data,"\\|"))[2]})) 

#create ranks file
ranks_RNAseq <- cbind(genenames, ranks_RNAseq)
colnames(ranks_RNAseq) <- c("GeneName","rank")

#sort ranks in decreasing order
ranks_RNAseq <- ranks_RNAseq[order(as.numeric(ranks_RNAseq[,2]),decreasing = TRUE),]
rnk_file <- file.path(rand_working_dir,"MesenvsImmuno_RNASeq_ranks.rnk")
write.table(ranks_RNAseq, rnk_file, 
            col.name = TRUE, sep="\t", row.names = FALSE, quote = FALSE)

1.1.8 Run GSEA

#path to GSEA jar 
# In order to run GSEA automatically you need to speciry the path to the gsea jar file.
gsea_jar <- "./gsea-3.0.jar"

#Gsea takes a long time to run.  If you have already run GSEA manually or previously 
# there is no need to re-run GSEA.  Make sure the gsea results are in the current 
# directory and the notebook will be able to find them and use them.
run_gsea = TRUE

# leave blank if you want the notebook to discover the gsea directory for itself
#gsea_directory = paste(working_dir,"Mesen_vs_Immuno.GseaPreranked.1497635459262",sep="/") 
gsea_directory = ""

#TODO: change this to update to the latest gmt file.
dest_gmt_file <- file.path(working_dir,
          "Supplementary_Table3_Human_GOBP_AllPathways_no_GO_iea_July_01_2017_symbol.gmt" )

num_randomizations <- 1000
  1. Run GSEA with gene set randomization

Even though we are going to perform our own randomization, we still need to run GSEA to get the observed ES. In addition we will use the FDR from this gene randomization run to compare with our manual phenotype permutations.

timestamp()
start_gs_perm <- Sys.time()
if(run_gsea){
  command <- paste("java  -Xmx1G -cp",gsea_jar,  "xtools.gsea.GseaPreranked -gmx", 
      dest_gmt_file, "-rnk" ,rnk_file, 
      "-collapse false -nperm ",num_randomizations, 
      " -permute gene_set -scoring_scheme weighted -rpt_label ",
      paste(analysis_name,"gsrand",sep="_"),
" -num 100 -plot_top_x 20 -rnd_seed 12345  -set_max 200 -set_min 15 -zip_report false -out" ,
      rand_working_dir, "-gui false > gsea_output.txt",sep=" ")
  system(command)
}
stop_gs_perm <- Sys.time()
timestamp()
difftime(stop_gs_perm,start_gs_perm,"mins")

1.1.9 Manual Phenotype Randomizations

  1. Instead of randomizing in GSEA create our own phenotype randomization in R.

Do the following steps:

  • Shuffle the class labels using the R sample function. (Even though that data consists of 4 classes we do not limit the shuffling to just the two classes being compared)
  • Normalize shuffled data
  • Calculate dispersion of shuffled data
  • Exclude genes with LOC names
  • Calculated differential expression using edgeR
  • Run GSEA preranked using resulting ranks (Needed to specify 1 permutation as if you set permutation to 0 GSEA will not calculate the NES or FDR value. We are not interested in the FDR value but we do need the NES value.)
  • Repeat for X randomizations.

*X = 1000 for this analysis

timestamp()
start_rand_perm <- Sys.time()
if(run_gsea){
    library("foreach")
    library("doParallel")
    
    
    cl <- makeCluster(detectCores() - 1)
    registerDoParallel(cl, cores = detectCores() - 1)
    
      
      data = foreach(i = 1:num_randomizations, .packages = c("edgeR"),
                   .combine = rbind) %dopar% {
                     try({
        rand_analysis_name <- paste("Rand",i,sep="_")
    
        rand_class <- sample(classDefinitions_RNASeq$SUBTYPE)
        
        # create data structure to hold counts and subtype information for each sample.
        d <- DGEList(counts=counts, group=rand_class)
        
        #Normalize the data
        d <- calcNormFactors(d)
        
        #calculate dispersion
        d <- estimateCommonDisp(d)
        d <- estimateTagwiseDisp(d)
        
        #the below regular expression excludes gene names that are ? or that start with
        # LOC any number of additional terms can be added to the regular expresion, for  
        # example to exclude any genes that start with "His" add |^His to the 
        # regular expression
        exclude <- grep("\\?|^LOC", rownames(d), value=T)
        d <- d[which(!rownames(d) %in% exclude),]
        
        #calculate differential expression statistics with a simple design
        de <- exactTest(d, pair=c("Immunoreactive","Mesenchymal"))
        tt_exact_test <- topTags(de,n=nrow(d))
        
        tt <- tt_exact_test
        #calculate ranks
        ranks_RNAseq = sign(tt$table$logFC) * -log10(tt$table$PValue)
        
        #gene names from the TCGA set contain gene name and entrez gene ids separated by 
        # ‘|’ for all subsequent enrichment analysis we need to have just one id.  
        # Separate the names into their two ids.
        genenames <- unlist(lapply( rownames(tt$table), function(data) 
          {unlist(strsplit(data,"\\|"))[1]}))
        geneids <- unlist(lapply( rownames(tt$table),  function(data) 
          {unlist(strsplit(data,"\\|"))[2]})) 
        
        #create ranks file
        ranks_RNAseq <- cbind(genenames, ranks_RNAseq)
        colnames(ranks_RNAseq) <- c("GeneName","rank")
        
        #sort ranks in decreasing order
        ranks_RNAseq <- ranks_RNAseq[order(as.numeric(ranks_RNAseq[,2]),decreasing = TRUE),]
        rank_filename <- file.path(rand_working_dir,paste("rand",i,"ranks.rnk",sep="_"))
        write.table(ranks_RNAseq, rank_filename, 
                    col.name = TRUE, sep="\t", row.names = FALSE, quote = FALSE)
        
        #need to set permutation number to 1 in order for the NES to be calculated.  
        # If you set it to zero the results have no p-value, fdr or NES values. 
         command <- paste("java  -Xmx1G -cp",gsea_jar,  "xtools.gsea.GseaPreranked -gmx", 
                          dest_gmt_file, "-rnk" ,rank_filename, 
                          "-collapse false -nperm 1 -permute gene_set",
                          " -scoring_scheme weighted -rpt_label ",
                          rand_analysis_name,
                          " -num 100 -plot_top_x 20 -rnd_seed 12345",
                          " -set_max 200 -set_min 15 -zip_report false -out" ,
                          rand_working_dir, "-gui false > gsea_output.txt",sep=" ")
          system(command)
     })
                   }
      stopCluster(cl)
}
 stop_rand_perm <- Sys.time()
 timestamp()
 difftime(stop_rand_perm,start_rand_perm,"mins")

1.1.10 Calculate FDR

  1. In order to calculate the FDR load in the GSEA results from each of the randomizations. For each gene set collect its ES and NES values. To calculate the FDR for each gene set:
  • For given gene set get all random ES and NES values.
  • If given gene set has a negative ES value, count the number of random ES values are less than it. Divide count by number of randomizations to get the FDR.
  • If given gene set has a positive ES value, count the number of random ES values are greater than it. Divide count by number of randomizations to get the FDR.
  1. Load in Randomization results
rand_directories <- list.files(path = rand_working_dir, pattern = "Rand")
all_rand_es <- c()
all_rand_nes <- c()

for(i in 1:length(rand_directories)){
    current_rand_files <- list.files(path = file.path(rand_working_dir,rand_directories[i]), 
                                     pattern = "gsea_report_for_na_")
    #restrict to just the xls file. 
    current_rand_files <- current_rand_files[grep(current_rand_files, pattern="\\.xls")]
    
    #load the positive files
    for(j in 1:length(current_rand_files)){
          rand_results <- read.table(  file.path(rand_working_dir,rand_directories[i],
                                    current_rand_files[j]),  header = TRUE, 
                                       sep = "\t", quote="\"", stringsAsFactors = FALSE)

          es_values <- data.frame(rand_results$NAME, rand_results$ES)
          nes_values <- data.frame(rand_results$NAME, rand_results$NES)
          colnames(es_values)[2] <- current_rand_files[j]
          colnames(nes_values)[2] <- current_rand_files[j]
          #add es_values to set.
          if(i == 1 && j ==1 ){
            all_rand_es <- es_values
            all_rand_nes <- nes_values
          } else{
            all_rand_es <- merge(all_rand_es, es_values,by.x = 1,by.y = 1, all = TRUE)
            all_rand_nes <- merge(all_rand_nes, nes_values,by.x = 1,by.y = 1, all = TRUE)
          }
      
    }
}
  1. Load in the real rankings
 gsea_directory <- list.files(path = rand_working_dir, 
                              pattern = paste(analysis_name,"gsrand",sep="_"))
gsea_results_files <- list.files(path = file.path(rand_working_dir,
                                      gsea_directory), pattern = "gsea_report_for_na_")
gsea_results_files<- gsea_results_files[grep(gsea_results_files, pattern="\\.xls")]
actual_gsea_results <- c()
    #load the positive files
    for(j in 1:length(gsea_results_files)){
          actual_gsea_results_1 <-  read.table(  file.path(rand_working_dir,
                                              gsea_directory,gsea_results_files[j]),  
                                              header = TRUE, sep = "\t", quote="\"", 
                                              stringsAsFactors = FALSE)
            actual_gsea_results <- rbind(actual_gsea_results , actual_gsea_results_1)
         
}

#replace all the zero fdr values with a more precise measurement
actual_gsea_results$FDR.q.val[which(actual_gsea_results$FDR.q.val == 0)] <- 1/1001
gsrand_gsea_results <- actual_gsea_results
  1. Calculate the nominal p-value using the phenotype permutation results
#calculate the new fdr values
new_fdr <- c()
nominal_pvalues <- c()
for(m in 1:dim(actual_gsea_results)[1]){
  current_data <- as.numeric(all_rand_es[which(all_rand_es$rand_results.NAME == 
                                actual_gsea_results$NAME[m]),2:ncol(all_rand_es)])
  if(actual_gsea_results$ES[m] < 0 ){
    nominal_pvalues <- c(nominal_pvalues, 
    (length( which(current_data < actual_gsea_results$ES[m]))+1)/(num_randomizations + 1))                           
  } else {
    nominal_pvalues <- c(nominal_pvalues, 
    (length( which(current_data > actual_gsea_results$ES[m]))+1)/(num_randomizations + 1))  
  }
}

new_fdr <- p.adjust(nominal_pvalues, method = "BH")
  1. Create a new results file containing our computed FDR values

#create a new fake enrichment results file so we can compare the differences.
new_fdr_gsea_results_files <- actual_gsea_results
new_fdr_gsea_results_files$FDR.q.val <- new_fdr
  
enrichment_results_filename <- file.path(working_dir,
                                         "new_fdr_gsea_results_files_edger_rand.txt ")

write.table(new_fdr_gsea_results_files[,1:11] ,enrichment_results_filename , 
            col.name = TRUE, sep="\t", row.names = FALSE, quote = FALSE)
  1. Plot the FDR saturation curve

saturation_curve <- c()
series_of_randomizations <- c(100,200,300,400,500,600,700,800,900,1000)
fdr_thresh <- 0.01

if(num_randomizations >= 1000){
  for(l in 1:length(series_of_randomizations)){
      saturation_fdr <- c()
      for(m in 1:dim(actual_gsea_results)[1]){
        current_data <- as.numeric(all_rand_es[which(all_rand_es$rand_results.NAME == 
                  actual_gsea_results$NAME[m]),
                  2:(series_of_randomizations[l] *2 + 1)])
        if(actual_gsea_results$ES[m] < 0 ){
          saturation_fdr <- c(saturation_fdr, (length( which(current_data < 
                  actual_gsea_results$ES[m]))+1)/
                    (series_of_randomizations[l] + 1))                           
        } else {
          saturation_fdr <- c(saturation_fdr, (length( which(current_data > 
                  actual_gsea_results$ES[m]))+1)/
                    (series_of_randomizations[l] + 1))  
        }
      }
      
      #get the number of gene sets that are less than fdr value threshold
      saturation_curve <- c(saturation_curve, length(which(saturation_fdr <= fdr_thresh)))
  }
  saturation_curve_filename = file.path(rand_working_dir,"saturation_curve.png")
  png(saturation_curve_filename)
  plot(series_of_randomizations,saturation_curve, 
       xlab="number of randomizations", 
       ylab = "Number of significant( FDR <0.01) gene sets", main = "FDR Saturation curve")
  dev.off()
}

1.1.11 Basic comparison of FDR values generated by GSEA gene set randomization and manual phenotype randomization with edgeR

Plot the comparison of the FDR values using gsrand vs phenotype randomization from R using edgeR

fdr_comparison_filename = file.path(rand_working_dir,"fdr_comparison.png")
png(fdr_comparison_filename)
plot(cbind(gsrand_gsea_results$FDR.q.val, new_fdr), ylab="edgeR randomization FDR", 
     xlab="gs randomizations FDR", main="GSEA gs rand FDR vs edgeR manual rand FDR")
dev.off()

Plot the comparison of the FDR values using gsrand vs phenotype randomization from R using edgeR - log transform the p-values to better see the differences.

log_fdr_comparison_filename = file.path(rand_working_dir,"log_fdr_comparison.png")
png(log_fdr_comparison_filename)
plot(cbind(-log10(gsrand_gsea_results$FDR.q.val), -log10(new_fdr)), 
     ylab="-log10 edgeR randomization FDR", 
     xlab="-log10 gs randomizations FDR", 
     main="-log10 transformed - GSEA gs rand FDR vs \nedgeR manual rand FDR")
dev.off()

Create a subset of the above plot to include any gene sets where the FDR is significant in at least one of the analyses

temp<- cbind(gsrand_gsea_results$FDR.q.val, new_fdr)
temp <- temp[which(temp[,1]<0.01 | temp[,2]<0.01),]

top_fdr_comparison_filename = file.path(rand_working_dir,"top_fdr_comparison.png")
png(top_fdr_comparison_filename)
plot(-log10(temp[,1]), -log10(temp[,2]), ylab="-log10 edgeR randomization FDR", 
     xlab="-log10 gs randomizations FDR",
main=paste("-log10 transformed GSEA gs rand vs edgeR manual rand" ,
           "- \nsignificant in at least one analysis", sep=""))
dev.off()

Venn Diagram of overlaps

library(VennDiagram)
two_method_overlap = file.path(rand_working_dir,"2_results_overlap_v1.png")
png(two_method_overlap)
  a <- gsrand_gsea_results$NAME[which(gsrand_gsea_results$FDR.q.val < 0.01)]
  b <- new_fdr_gsea_results_files$NAME[which(new_fdr_gsea_results_files$FDR.q.val < 0.01)]
results <- draw.pairwise.venn(area1 = length(a), 
                 area2 = length(b), 
                 n12 = length(intersect(a,b)), 
                 category = c("gsrand",  "edgeR_rand"), lty = "blank", 
    fill = c("skyblue", "pink1"),cross.area = length(intersect(a,b)))
dev.off()

Figure 8: Overlap of gsrand and edgeR_rand - Compare the number of significant (FDR < 0.01) gene sets from the two methods, GSEA gs randomization and edgeR manual randomization.

1.2 Launch Cytoscape

Create EM through CyREST interface - make sure you open Cytoscape with a -R 1234 (to enable rest functionality) and allow R to talk directly to Cytoscape.

Launch Cytoscape (by default Cytoscape will automatically enable rest so as long as cytoscape 3.3 or higher is open R should be able to communicate with it)

1.3 Set up connection from R to Cytoscape


#use easy cyRest library to communicate with cytoscape.
tryCatch(expr = { library("RCy3")}, 
         error = function(e) { source("https://bioconductor.org/biocLite.R")
           biocLite("RCy3")}, finally = library("RCy3"))

cytoscape.open = TRUE

tryCatch(expr = { cyrestGET("version")}, 
         error = function(e) { return (cytoscape.open = FALSE)}, 
         finally =function(r){ return(cytoscape.open = TRUE)})
         
if(!cytoscape.open){
  #try and launch cytoscape
 print("Cytoscape is not open.  Please launch cytoscape.")
} else{
  cytoscape.version =  cyrestGET("version")
}

1.4 Create an enrichment map


#defined threshold for GSEA enrichments (need to be strings for cyrest call)
pvalue_gsea_threshold <- "0.01"
qvalue_gsea_threshold <-"0.001"

similarity_threshold <- "0.375"
similarity_metric = "COMBINED"

cur_model_name <- paste(analysis_name, "edgeR_rand",sep="_")


#although there is a gmt file in the gsea edb results directory it have been filtered to 
#contain only genes represented in the expression set.  If you use this fltered file you 
#will get different pathway connectivity depending on the dataset being used.  We recommend 
#using original gmt file used for the gsea analysis and not the filtered 
# one in the results directory.
gmt_gsea_file <- file.path(getwd(),dest_gmt_file)
gsea_ranks_file <- file.path(getwd(),rnk_file)
gsea_results_filename <- file.path(getwd(),enrichment_results_filename) 
gsea_expression_filename <- file.path(getwd(),working_dir, 
                                      "Supplementary_Table6_TCGA_OV_RNAseq_expression.txt")

#######################################
#create EM
current_network_name <- paste(cur_model_name,pvalue_gsea_threshold,
                              qvalue_gsea_threshold,sep="_")

em_command = paste('enrichmentmap build analysisType="gsea" gmtFile=',gmt_gsea_file,
                   'pvalue=',pvalue_gsea_threshold, 'qvalue=',qvalue_gsea_threshold,
                   'similaritycutoff=',similarity_threshold,
                   'coefficients=',similarity_metric,'ranksDataset1=', 
                   gsea_ranks_file,'enrichmentsDataset1=',gsea_results_filename, 
                   'enrichments2Dataset1=',gsea_results_filename, 
                   'filterByExpressions=false',
                   'expressionDataset1=',gsea_expression_filename,
                   sep=" ")

#enrichment map command will return the suid of newly created network.
response <- commandsGET(em_command)

current_network_suid <- 0
#enrichment map command will return the suid of newly created network unless it failed.  
#If it failed it will contain the word failed
if(grepl(pattern="Failed", response)){
  paste(response)
} else {
  current_network_suid <- response
}
response <- renameNetwork(title=current_network_name, 
                          network = as.numeric(current_network_suid))

1.5 Get a screen shot of the initial network.

output_network_file <- file.path(getwd(),"manual_phenotype_randomization.pdf")

if(file.exists(output_network_file)){
  #cytoscape hangs waiting for user response if file already exists.  Remove it first
  file.remove(output_network_file)
  } 

#export the network
response <- exportImage(output_network_file, type = "pdf")

Figure 9: Example enrichment map created from manual phenotype randomization in R using edgeR (FDR threshold<0.01)

1. Grossman, R. L. et al. Toward a shared vision for cancer genomic data. N Engl J Med 375, 1109–12 (2016).

2. Verhaak, R. G. et al. Prognostically relevant gene signatures of high-grade serous ovarian carcinoma. J Clin Invest 123, 517–25 (2013).

3. Robinson, M. D., McCarthy, D. J. & Smyth, G. K. EdgeR: A bioconductor package for differential expression analysis of digital gene expression data. Bioinformatics 26, 139–40 (2010).

LS0tCnRpdGxlOiAiU3VwcGxlbWVudGFyeSBQcm90b2NvbCA0IOKAkyBwZXJmb3JtIG1hbnVhbCBwaGVub3R5cGUgcmFuZG9taXphdGlvbnMgdXNpbmcgZWRnZVIgZm9sbG93ZWQgYnkgR1NFQSBhbmQgRU0iCmF1dGhvcjogIlJ1dGggSXNzZXJsaW4iCmRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSlgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGhpZ2hsaWdoOiBoYWRkb2NrCiAgICBrZWVwX21kOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogcGFwZXIKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBubwogICAgICBzbW9vdGhfc2Nyb2xsOiBubwogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6ICczJwogIGh0bWxfbm90ZWJvb2s6CiAgICBoaWdobGlnaDogaGFkZG9jawogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBwYXBlcgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IG5vCiAgICAgIHNtb290aF9zY3JvbGw6IG5vCmJpYmxpb2dyYXBoeTogc3VwX3Byb3RvY29sMV9yZWZlcmVuY2VzLmJpYgpjc2w6IG5hdHVyZS1wcm90b2NvbHMuY3NsCi0tLQojIFN1cHBsZW1lbnRhcnkgUHJvdG9jb2wgNCDigJMgcGVyZm9ybSBtYW51YWwgcGhlbm90eXBlIHJhbmRvbWl6YXRpb25zIHVzaW5nIGVkZ2VSIGZvbGxvd2VkIGJ5IEdTRUEgYW5kIEVNCgpUaGlzIHByb3RvY29sIGRlbW9uc3RyYXRlcyBob3cgdG8gY29kZSBtYW51YWwgcGhlbm90eXBlIHBlcm11dGF0aW9uIGZvciBSTkEtc2VxIGRhdGEgYW5hbHl6ZWQgd2l0aCBlZGdlUiBhbmQgR1NFQS4KCkdTRUEgY2FsY3VsYXRlcyBmb3IgZWFjaCB0ZXN0ZWQgZ2VuZS1zZXQgYW4gZW5yaWNobWVudCBzY29yZSAoRVMpIHdoaWNoIHF1YW50aWZpZXMgdGhlIGVucmljaG1lbnQgb2YgdGhlIGdlbmUgc2V0LiBBIHAtdmFsdWUgaXMgbmVlZGVkIHRvIGVzdGltYXRlIGlmIHRoaXMgZW5yaWNobWVudCAoRVMpIGlzIGdyZWF0ZXIgdGhhbiBvbmUgdGhhdCBjb3VsZCBiZSBvYnRhaW5lZCBieSBjaGFuY2Ugb25seS4gVGhlcmUgYXJlIHR3byB3YXlzIHRvIGVzdGltYXRlIHRoZSBwLXZhbHVlIGFuZCBGRFIgd2l0aGluIEdTRUE6IGdlbmUtc2V0IHBlcm11dGF0aW9uIGFuZCBwaGVub3R5cGUgcGVybXV0YXRpb24uIAoKRmlyc3QsIGF2YWlsYWJsZSBhcyBnZW5lIHNldCBwZXJtdXRhdGlvbiBpbiBHU0VBLGEgbnVsbCBkaXN0cmlidXRpb24gY2FuIGJlIGNvbnN0cnVjdGVkIGJ5IHJhbmRvbWx5IHNodWZmbGluZyB0aGUgZ2VuZXMgaW4gYSBnaXZlbiBnZW5lLXNldHM6IGVhY2ggdGltZSBhIHNodWZmbGVkIGdlbmUtc2V0IGlzIHRlc3RlZCBhbiBFUyBpcyBjYWxjdWxhdGVkLiBUaGVuIHRoZSBvYnNlcnZlZCBFUyBmb3IgYW4gaW5kaXZpZHVhbCBnZW5lIHNldCBpcyBjb21wYXJlZCB0byBhbGwgdGhlIEVTIHNjb3JlcyBvYnRhaW5lZCBmb3IgdGhlIHNhbWUgZ2VuZSBzZXQgYWNyb3NzIGFsbCB0aGUgcGVybXV0YXRpb25zIHRvIG9idGFpbiBhbiBlc3RpbWF0ZSBvZiB0aGUgZ2VuZSBzZXQgcGVybXV0YXRpb24gRkRSIHZhbHVlLiAgCgpBbiBhbHRlcm5hdGUgd2F5LCBhdmFpbGFibGUgYXMgcGhlbm90eXBlIHBlcm11dGF0aW9uIGluIEdTRUEsIGlzIHRvIHNodWZmbGUgdGhlIHNhbXBsZSBsYWJlbHMgdGhhdCBjb25zdGl0dXRlIHRoZSAyIGdyb3VwcyB0byBiZSBjb21wYXJlZC4gQWZ0ZXIgc2h1ZmZsaW5nIHRoZSBzYW1wbGUgbGFiZWxzIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGlzIHJlY2FsY3VsYXRlZC4gV2l0aCBhIHJhbmRvbSBzaHVmZmxlIG9mIHRoZSBzYW1wbGVzIG5vdCBtYW55IGdlbmVzIHNob3VsZCBiZSBmb3VuZCBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBhbmQgYXMgYSByZXN1bHQgbm90IG1hbnkgcGF0aHdheXMgc2hvdWxkIGJlIGZvdW5kIHNpZ25pZmljYW50bHkgZW5yaWNoZWQuIEZvciBlYWNoIHRlc3RlZCBnZW5lIHNldCwgdGhlIG9ic2VydmVkIEVTIGlzIGNvbXBhcmVkIHRvIGFsbCBFUyBvYnRhaW5lZCB3aXRoIHRoZSBzaHVmZmxlZCBzYW1wbGVzIGFuZCBhbiBGRFIgaXMgZXN0aW1hdGVkLiAKCk5vdGUgYWJvdXQgcGhlbm90eXBlIHBlcm11dGF0aW9uOiBJbiB0aGUgR1NFQSBpbnRlcmZhY2UsIHRoZXJlIGlzIG9uZSBvcHRpb24gdG8gdXBsb2FkIGFuIGVudGlyZSBleHByZXNzaW9uIG1hdHJpeCBhbmQgYSBjbGFzcyBkZWZpbml0aW9uIGZpbGUgKC5jbHMpIGFuZCBwZXJmb3JtIHRoZSBwaGVub3R5cGUgcGVybXV0YXRpb24uIEhvd2V2ZXIgdGhlIGNob2ljZSBvZiB0ZXN0cyBhcmUgbGltaXRlZCB0byBiYXNpYyBzdGF0aXN0aWNhbCBtZWFzdXJlcyAoc2lnbmFsIHRvIG5vaXNlLCB0LXRlc3QpIGFuZCB0ZXN0cyB1c2luZyBub24gbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uIG9yIGxpbmVhciBtb2RlbHMgc3VjaCBhcyB0aGUgb25lcyBpbXBsZW1lbnRlZCBpbiBlZGdlUiBhbmQgREVzZXEgYW5kIHJlY29tbWVuZGVkIGZvciBSTkEtc2VxIGNvdW50cyBkYXRhIGFyZSBub3QgYXZhaWxhYmxlLiBUaGVyZWZvcmUsICBpbiB0aGlzIHByb3RvY29sLCB3ZSBhcmUgcGVyZm9ybWluZyB0aGUgcGVybXV0YXRpb25zIG91dHNpZGUgR1NFQS4gRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gaXMgY2FsY3VsYXRlZCB3aXRoaW4gZWRnZVIgYmV0d2VlbiB0aGUg4oCYaW1tdW5vcmVhY3RpdmXigJkgYW5kIOKAmG1lc2VuY2h5bWFs4oCZIHR5cGUgYW5kIEdTRUEgaXMgcnVuIHRvIGdldCB0aGUgb2JzZXJ2ZWQgRVMuIFRoZW4sIDEwMDAgcGVybXV0YXRpb25zIGFyZSBjb21wdXRlZCBieSBzaHVmZmxpbmcgdGhlIHNhbXBsZXMuIEVhY2ggcGVybXV0YXRpb24gcnVuIGNvbnNpc3RzIG9mIDoxKSBzaHVmZmxpbmcgdGhlIHNhbXBsZXMgd2hpY2ggcmVzdWx0cyBpbiB0aGUgZm9ybWF0aW9uIG9mIDIgcmFuZG9tIGdyb3Vwcy4gMikgY2FsY3VsYXRlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHdpdGhpbiBlZGdlUiBiZXR3ZWVuIHRoZXNlIDIgZ3JvdXBzIDMpIHJ1biBHU0VBIGFuZCBvYnRhaW4gdGhlIEVTIGFuZCBORVMgZm9yIHRoaXMgcmFuZG9tIHBlcm11dGF0aW9uLiAgRmluYWxseSwgZm9yIGVhY2ggZ2VuZS1zZXQsIHRoZSBwLXZhbHVlIGlzIGNhbGN1bGF0ZWQgYnkgY291bnRpbmcgdGhlIG51bWJlciBvZiB0aW1lcyB0aGUgb2JzZXJ2ZWQgRVMgd2FzIGdyZWF0ZXIgdGhhbiB0aGUgRVMgb2J0YWluZWQgZnJvbSB0aGUgcmFuZG9tIHBlcm11dGF0aW9ucy4gCgoqKk5PVEU6IFBlcmZvcm1pbmcgdGhpcyByYW5kb21pemF0aW9uIHdpdGggUiBpcyB2ZXJ5IHJlc291cmNlIGludGVuc2l2ZSBhbmQgdGltZSBjb25zdW1pbmcuICAoT24gYSBpbnRlbCBpNyBjb21wdXRlciB3aXRoIDE2R0IgMTAwMCBwZXJtdXRhdGlvbnMgdG9vayBtb3JlIHRoYW4gOS41IGhvdXJzKSoqCgoKIyMgUHJvY2VzcyBSTkEtc2VxIGRhdGEKVGhpcyBwYXJ0IG9mIHRoZSBzdXBwbGVtZW50YXJ5IHByb3RvY29sIGRlbW9uc3RyYXRlcyBmaWx0ZXJpbmcgYW5kIHNjb3JpbmcgUk5BLXNlcSBkYXRhIHVzaW5nIG5vcm1hbGl6ZWQgUk5BLXNlcSBjb3VudCBkYXRhIHdpdGggdGhlIGVkZ2VSIFIgcGFja2FnZS4gVGhlIHNlY3Rpb24gcHJlcGFyZXMgdGhlIGlucHV0IGRhdGEgZm9yIEdTRUEuIAoKIyMjIExvYWQgcmVxdWlyZWQgcGFja2FnZXMKMS4gTG9hZCByZXF1aXJlZCBCaW9jb25kdWN0b3IgcGFja2FnZXMgaW50byBSLiAKYGBge3IsIHdhcm5pbmc9RkFMU0V9CmtuaXRyOjpvcHRzX2tuaXQkc2V0KGNhY2hlID0gVFJVRSkKCnRyeUNhdGNoKGV4cHIgPSB7IGxpYnJhcnkoImVkZ2VSIil9LCAKICAgICAgICAgZXJyb3IgPSBmdW5jdGlvbihlKSB7IAogICAgICAgICAgIHNvdXJjZSgiaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL2Jpb2NMaXRlLlIiKQogICAgICAgICAgIGJpb2NMaXRlKCJlZGdlUiIpfSwgCiAgICAgICAgIGZpbmFsbHkgPSBsaWJyYXJ5KCJlZGdlUiIpKQoKd29ya2luZ19kaXIgPC0gZmlsZS5wYXRoKCIuIiwiZGF0YSIpCnJhbmRfd29ya2luZ19kaXIgPC0gZmlsZS5wYXRoKCIuIiwiZGF0YSIsICJSYW5kb21pemF0aW9uc19yb3VuZDMiKQoKI1RoZSBmaWVsZCBpbiB0aGUgY2xhc3MgZGVmaW5pdGlvbiBmaWxlIHRoYXQgZGVmaW5lcyB0aGUgY2xhc3NlcyBvZiB0aGUgZGF0YS4KZGF0YV9jbGFzc2VzIDwtICJTVUJUWVBFIgoKYW5hbHlzaXNfbmFtZSA8LSAiTWVzZW5fVnNfSW1tdW5vIgpgYGAKCgojIyMgTG9hZCBFeHByZXNzaW9uIERhdGEKMi4gTG9hZCB0aGUgZXhwcmVzc2lvbiBkYXRhc2V0IG9mIDI5NiB0dW1vdXJzLCB3aXRoIDc5IGNsYXNzaWZpZWQgYXMgSW1tdW5vcmVhY3RpdmUsIDcxIGNsYXNzaWZpZWQgYXMgTWVzZW5jaHltYWwsIDY3IGNsYXNzaWZpZWQgYXMgRGlmZmVyZW50aWF0ZWQsIGFuZCA3OSBjbGFzc2lmaWVkIGFzIFByb2xpZmVyYXRpdmUgc2FtcGxlcy4gVGhlIFRDR0EgY291bnRzIGRhdGEgd2FzIHJldHJpZXZlZCBmcm9tIHRoZSBHRENbQEdEQ10gYW5kIGNvbnRhaW5lZCBjb3VudHMgcGVyIG1STkEgdHJhbnNjcmlwdCBkZXRlcm1pbmVkIHVzaW5nIHRoZSBSU0VNIG1ldGhvZCBmb3IgMTk5NDcgdHJhbnNjcmlwdHMgYW5kIDMwMCBzYW1wbGVzLgoKYGBge3J9ClJOQVNlcSA8LSByZWFkLnRhYmxlKCAKICBmaWxlLnBhdGgod29ya2luZ19kaXIsIlN1cHBsZW1lbnRhcnlfVGFibGUxMF9UQ0dBX1JOQVNlcV9yYXdjb3VudHMudHh0IiksICAKICBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZT0iXCIiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKIyMjIExvYWQgc3VidHlwZSBpbmZvcm1hdGlvbgozLiBMb2FkIHN1YnR5cGUgY2xhc3NpZmljYXRpb24gb2Ygc2FtcGxlcy4gVG8gY2FsY3VsYXRlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLCB3ZSBuZWVkIHRvIGRlZmluZSBhdCBsZWFzdCB0d28gc2FtcGxlIGNsYXNzZXMuIEEgY29tbW9uIGV4cGVyaW1lbnRhbCBkZXNpZ24gaW52b2x2ZXMgY2FzZXMgYW5kIGNvbnRyb2xzIGJ1dCBhbnkgdHdvIGNsYXNzZXMgY2FuIGJlIHVzZWQuIFRoZSBjdXJyZW50IGRhdGFzZXQgaXMgZGl2aWRlZCBpbnRvIE1lc2VuY2h5bWFsIGFuZCBJbW11bm9yZWFjdGl2ZSBjbGFzc2VzIChjbGFzcyBkZWZpbml0aW9ucyB3ZXJlIG9idGFpbmVkIGZyb20gVmVyaGFhayBldCBhbC5bQE9WXSBTdXBwbGVtZW50YXJ5IFRhYmxlIDEsIHRoaXJkIGNvbHVtbikuIEFmdGVyIGxvYWRpbmcgdGhlIG1hdHJpeCwgY2hlY2sgdGhhdCB0aGUgY29sdW1uIG5hbWVzIG9mIHRoZSBleHByZXNzaW9uIG1hdHJpeCBhbmQgY2xhc3MgZGVmaW5pdGlvbnMgYXJlIGVxdWFsLgoKYGBge3J9CmNsYXNzRGVmaW5pdGlvbnNfUk5BU2VxIDwtIHJlYWQudGFibGUoIAogIGZpbGUucGF0aCh3b3JraW5nX2RpciwgIlN1cHBsZW1lbnRhcnlfVGFibGUxMV9STkFTZXFfY2xhc3NkZWZpbml0aW9ucy50eHQiKSwgCiAgaGVhZGVyID0gVFJVRSwgc2VwID0gIlx0IiwgcXVvdGU9IlwiIiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpgYGAKCiMjIyBGaWx0ZXIgRGF0YQo0LiBGaWx0ZXIgUk5BLXNlcSByZWFkcy4gUk5BLXNlcSBkYXRhIGFyZSBwcm9jZXNzZWQgZm9sbG93aW5nIHRoZSBlZGdlUiBwcm90b2NvbCB0aGF0IGZpbHRlcnMgcmVhZHMgYmFzZWQgb24gdGhlIGNvdW50cyBwZXIgbWlsbGlvbiAoQ1BNKSBzdGF0aXN0aWMuIFJOQS1zZXEgcmVhZCBjb3VudHMgYXJlIGNvbnZlcnRlZCB0byBDUE0gdmFsdWVzIGFuZCBnZW5lcyB3aXRoIENQTSA+IDEgaW4gYXQgbGVhc3QgNTAgb2YgdGhlIHNhbXBsZXMgYXJlIHJldGFpbmVkIGZvciBmdXJ0aGVyIHN0dWR5IChhIGdlbmUgbXVzdCBoYXZlIGF0IGxlYXN0IDUwIG1lYXN1cmVtZW50cyB3aXRoIG1vcmUgdGhhbiAxIENQTSBpbiBvbmUgb2YgdGhlIGNsYXNzZXMgdG8gYmUgaW5jbHVkZWQgaW4gdGhlIGFuYWx5c2lzKS4gVGhpcyBzdGVwIHJlbW92ZXMgZ2VuZXMgd2l0aCB2ZXJ5IGxvdyByZWFkIGNvdW50cyB0aGF0IGFyZSBsaWtlbHkgbm90IGV4cHJlc3NlZCBpbiB0aGUgbWFqb3JpdHkgb2Ygc2FtcGxlcyBhbmQgY2F1c2Ugbm9pc2UgaW4gdGhlIGRhdGEuIE5vdGUsIENQTSBmaWx0ZXJpbmcgaXMgdXNlZCB0byByZW1vdmUgbG93IGNvdW50cyB3aGlsZSBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGFuYWx5c2lzIGlzIGJhc2VkIG9uIG5vcm1hbGl6ZWQgcmVhZCBjb3VudHMgd2hpY2ggYXJlIGdlbmVyYXRlZCBiZWxvdyAoc3RlcCA2KS4gCgpgYGB7cn0KY3BtcyA8LSBjcG0oUk5BU2VxKQprZWVwIDwtIHJvd1N1bXMoY3BtcyA+IDEpID49IDUwCmNvdW50cyA8LSBSTkFTZXFba2VlcCxdCmBgYAoKCiMjIyBOb3JtYWxpemF0aW9uIGFuZCBEaXNwZXJzaW9uCiAKNS4gRGF0YSBub3JtYWxpemF0aW9uLCBkaXNwZXJzaW9uIGFuYWx5c2lzIGlzIHBlcmZvcm1lZCBvbiB0aGUgZW50aXJlIGRhdGFzZXQuIENyZWF0ZWQgTURTLXBsb3Qgb2YgYWxsIHBhdGllbnQgc2FtcGxlcyBjYW4gYmUgc2VlbiBpbiBGaWd1cmUgMS4gCmBgYHtyfQojIGNyZWF0ZSBkYXRhIHN0cnVjdHVyZSB0byBob2xkIGNvdW50cyBhbmQgc3VidHlwZSBpbmZvcm1hdGlvbiBmb3IgZWFjaCBzYW1wbGUuCmQgPC0gREdFTGlzdChjb3VudHM9Y291bnRzLCBncm91cD1jbGFzc0RlZmluaXRpb25zX1JOQVNlcSRTVUJUWVBFKQoKI05vcm1hbGl6ZSB0aGUgZGF0YQpkIDwtIGNhbGNOb3JtRmFjdG9ycyhkKQoKI2NyZWF0ZSBtdWx0aWRpbWVuc2lvbmFsIHNjYWxpbmcoTURTKSBwbG90LiAgVGhlIGNvbW1hbmQgYmVsb3cgd2lsbCBhdXRvbWF0aWNhbGx5IAojIGdlbmVyYXRlIHRoZSBwbG90IGNvbnRhaW5pbmcgYWxsIHNhbXBsZXMgd2hlcmUgZWFjaCBzdWJ0eXBlIGlzIGEgZGlmZmVyZW50IGNvbG9yLiAgCiNJZGVhbGx5IHRoZXJlIHNob3VsZCBiZSBhIGdvb2Qgc2VwYXJhdGlvbiBiZXR3ZWVuIHRoZSBkaWZmZXJlbnQgY2xhc3Nlcy4KbWRzX2ZpbGVuYW1lIDwtIGZpbGUucGF0aCh3b3JraW5nX2RpciwgIm1kc3Bsb3RfYWxsc2FtcGxlcy5wbmciKQpwbmcoZmlsZW5hbWUgPSBtZHNfZmlsZW5hbWUpCm1kc19vdXRwdXQgPC0gcGxvdE1EUyhkLCBsYWJlbHM9TlVMTCwgcGNoID0gMSwgCmNvbD0gYygiZGFya2dyZWVuIiwiYmx1ZSIsInJlZCIsICJvcmFuZ2UiKVtmYWN0b3IoY2xhc3NEZWZpbml0aW9uc19STkFTZXEkU1VCVFlQRSldLCAKeGxpbSA9IGMoLTIuNSw0KSwgeWxpbSA9IGMoLTIuNSw0KSkKCgpsZWdlbmQoInRvcHJpZ2h0IiwgCiAgICAgICBsZWdlbmQ9bGV2ZWxzKGZhY3RvcihjbGFzc0RlZmluaXRpb25zX1JOQVNlcSRTVUJUWVBFKSksIAogICAgICAgcGNoPWMoMSksIGNvbD0gYygiZGFya2dyZWVuIiwiYmx1ZSIsInJlZCIsICJvcmFuZ2UiKSx0aXRsZT0iQ2xhc3MiLCAgCiAgICAgICBidHkgPSAnbicsIGNleCA9IDAuNzUpCgpkZXYub2ZmKCkKCiNjYWxjdWxhdGUgZGlzcGVyc2lvbgpkIDwtIGVzdGltYXRlQ29tbW9uRGlzcChkKQpkIDwtIGVzdGltYXRlVGFnd2lzZURpc3AoZCkKYGBgCgpgYGB7ciAgZWNobz1GQUxTRSwgZmlnLmNhcD0iTURTIHBsb3Qgb2YgYWxsIHRoZSBkaWZmZXJlbnQgb3ZhcmlhbiBjYW5jZXIgc3VidHlwZXMiLCBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy5wb3M9IiFodCJ9CmlmKGV4aXN0cygibWRzX2ZpbGVuYW1lIikpewogIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKG1kc19maWxlbmFtZSkKfQpgYGAKCiMjIyBGaWx0ZXIgdW5hbm5vdGF0ZWQgZ2VuZXMKNi4gKE9wdGlvbmFsKSBFeGNsdWRlIGdlbmVzIHdpdGggbWlzc2luZyBzeW1ib2xzIG9yIHVuY2hhcmFjdGVyaXplZCBnZW5lcy4gSW4gdGhpcyBleGFtcGxlIGdlbmUgZW50cmllcyBpbiB0aGUgZGF0YXNldCBjb250YWluaW5nIOKAmD/igJkgb3Igc3RhcnRpbmcgd2l0aCBMT0MgYXJlIGV4Y2x1ZGVkIGFzIHRoZXkgcmVwcmVzZW50IG5vbi1hbm5vdGF0ZWQgZ2VuZXMgb3Igb3RoZXIgbG9jaSB0aGF0IGFyZSBub3QgcHJlc2VudCBpbiBwYXRod2F5IGRhdGFiYXNlcy4gIFRoZSBmcmVxdWVuY3kgb2YgdGhlc2UgYW5kIG90aGVyIG5vbiBwcm90ZWluIGNvZGluZyBlbnRyaWVzIGluIHlvdXIgZGF0YXNldCB3aWxsIGRlcGVuZCBvbiB0aGUgZGF0YWJhc2UgdXNlZCB0byBhbGlnbiB5b3VyIFJOQS1zZXEgZGF0YS4gCmBgYHtyfQojdGhlIGJlbG93IHJlZ3VsYXIgZXhwcmVzc2lvbiBleGNsdWRlcyBnZW5lIG5hbWVzIHRoYXQgYXJlID8gb3IgdGhhdCBzdGFydCB3aXRoIExPQwojIGFueSBudW1iZXIgb2YgYWRkaXRpb25hbCB0ZXJtcyBjYW4gYmUgYWRkZWQgdG8gdGhlIHJlZ3VsYXIgZXhwcmVzaW9uLCBmb3IgZXhhbXBsZSAKIyB0byBleGNsdWRlIGFueSBnZW5lcyB0aGF0IHN0YXJ0IHdpdGggIkhpcyIgYWRkIHxeSGlzIHRvIHRoZSByZWd1bGFyIGV4cHJlc3Npb24KZXhjbHVkZSA8LSBncmVwKCJcXD98XkxPQyIsIHJvd25hbWVzKGQpLCB2YWx1ZT1UKQpkIDwtIGRbd2hpY2goIXJvd25hbWVzKGQpICVpbiUgZXhjbHVkZSksXQpgYGAKCiMjIyBDYWxjdWxhdGUgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24KNy4gRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgaXMgcGVyZm9ybWVkIHdpdGggYSBzaW1wbGUgZGVzaWduIGFzIGRlc2NyaWJlZCBpbiB0aGUgZWRnZVIgcHJvdG9jb2xbQGVkZ2VSXS4KYGBge3J9CiNjYWxjdWxhdGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gc3RhdGlzdGljcyB3aXRoIGEgc2ltcGxlIGRlc2lnbgpkZSA8LSBleGFjdFRlc3QoZCwgcGFpcj1jKCJJbW11bm9yZWFjdGl2ZSIsIk1lc2VuY2h5bWFsIikpCnR0X2V4YWN0X3Rlc3QgPC0gdG9wVGFncyhkZSxuPW5yb3coZCkpCgojYWx0ZXJuYXRlbHkgeW91IGNhbiBhbHNvIHVzZSB0aGUgZ2xtIG1vZGVsIHVzaW5nIGNvbnRyYXN0cy4gIAojRm9yIGEgc2ltcGxlIDIgY2xhc3MgY29tcGFyaXNvbiB0aGlzIGlzIG5vdCByZXF1aXJlZCBidXQgaWYgeW91IHdhbnQgdG8gY29tcGFyZQojIDEgY2xhc3MgdG8gdGhlIHJlbWFpbmluZyAzIGNsYXNzZXMgdGhlbiB0aGlzIHNvcnQgb2YgbW9kZWwgaXMgdXNlZnVsLgpjbGFzc2VzIDwtIGZhY3RvcihjbGFzc0RlZmluaXRpb25zX1JOQVNlcVssZGF0YV9jbGFzc2VzXSkKbW9kZWxEZXNpZ24gPC0gbW9kZWwubWF0cml4KH4gMCArIGNsYXNzZXMpCgpjb250cmFzdF9tZXNlbnZzaW1tdW5vIDwtIG1ha2VDb250cmFzdHMoCiAgICAgICAgICAgICAgICAgIG1lc2VudnNpbW11bm8gPSJjbGFzc2VzTWVzZW5jaHltYWwtY2xhc3Nlc0ltbXVub3JlYWN0aXZlIiwKICAgICAgICAgICAgICAgICAgbGV2ZWxzPW1vZGVsRGVzaWduKQpmaXRfZ2xtIDwtIGdsbUZpdChkLG1vZGVsRGVzaWduKQptZXNlbnZzaW1tdW5vIDwtIGdsbUxSVChmaXRfZ2xtICwgY29udHJhc3QgPSBjb250cmFzdF9tZXNlbnZzaW1tdW5vKQp0dF9tZXNlbnZzaW1tdW5vIDwtIHRvcFRhZ3MobWVzZW52c2ltbXVubyxuPW5yb3coZCkpCgp0dCA8LSB0dF9leGFjdF90ZXN0CiNjYWxjdWxhdGUgcmFua3MKcmFua3NfUk5Bc2VxID0gc2lnbih0dCR0YWJsZSRsb2dGQykgKiAtbG9nMTAodHQkdGFibGUkUFZhbHVlKQoKI2dlbmUgbmFtZXMgZnJvbSB0aGUgVENHQSBzZXQgY29udGFpbiBnZW5lIG5hbWUgYW5kIGVudHJleiBnZW5lIGlkcyBzZXBhcmF0ZWQgYnkg4oCYfOKAmQojIGZvciBhbGwgc3Vic2VxdWVudCBlbnJpY2htZW50IGFuYWx5c2lzIHdlIG5lZWQgdG8gaGF2ZSBqdXN0IG9uZSBpZC4gIFNlcGFyYXRlIHRoZSBuYW1lcyAKIyBpbnRvIHRoZWlyIHR3byBpZHMuCmdlbmVuYW1lcyA8LSB1bmxpc3QobGFwcGx5KCByb3duYW1lcyh0dCR0YWJsZSksIGZ1bmN0aW9uKGRhdGEpIAogIHt1bmxpc3Qoc3Ryc3BsaXQoZGF0YSwiXFx8IikpWzFdfSkpCmdlbmVpZHMgPC0gdW5saXN0KGxhcHBseSggcm93bmFtZXModHQkdGFibGUpLCAgZnVuY3Rpb24oZGF0YSkgCiAge3VubGlzdChzdHJzcGxpdChkYXRhLCJcXHwiKSlbMl19KSkgCgojY3JlYXRlIHJhbmtzIGZpbGUKcmFua3NfUk5Bc2VxIDwtIGNiaW5kKGdlbmVuYW1lcywgcmFua3NfUk5Bc2VxKQpjb2xuYW1lcyhyYW5rc19STkFzZXEpIDwtIGMoIkdlbmVOYW1lIiwicmFuayIpCgojc29ydCByYW5rcyBpbiBkZWNyZWFzaW5nIG9yZGVyCnJhbmtzX1JOQXNlcSA8LSByYW5rc19STkFzZXFbb3JkZXIoYXMubnVtZXJpYyhyYW5rc19STkFzZXFbLDJdKSxkZWNyZWFzaW5nID0gVFJVRSksXQpybmtfZmlsZSA8LSBmaWxlLnBhdGgocmFuZF93b3JraW5nX2RpciwiTWVzZW52c0ltbXVub19STkFTZXFfcmFua3Mucm5rIikKd3JpdGUudGFibGUocmFua3NfUk5Bc2VxLCBybmtfZmlsZSwgCiAgICAgICAgICAgIGNvbC5uYW1lID0gVFJVRSwgc2VwPSJcdCIsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKCmBgYAoKCiMjIyBSdW4gR1NFQQpgYGB7cn0KI3BhdGggdG8gR1NFQSBqYXIgCiMgSW4gb3JkZXIgdG8gcnVuIEdTRUEgYXV0b21hdGljYWxseSB5b3UgbmVlZCB0byBzcGVjaXJ5IHRoZSBwYXRoIHRvIHRoZSBnc2VhIGphciBmaWxlLgpnc2VhX2phciA8LSAiLi9nc2VhLTMuMC5qYXIiCgojR3NlYSB0YWtlcyBhIGxvbmcgdGltZSB0byBydW4uICBJZiB5b3UgaGF2ZSBhbHJlYWR5IHJ1biBHU0VBIG1hbnVhbGx5IG9yIHByZXZpb3VzbHkgCiMgdGhlcmUgaXMgbm8gbmVlZCB0byByZS1ydW4gR1NFQS4gIE1ha2Ugc3VyZSB0aGUgZ3NlYSByZXN1bHRzIGFyZSBpbiB0aGUgY3VycmVudCAKIyBkaXJlY3RvcnkgYW5kIHRoZSBub3RlYm9vayB3aWxsIGJlIGFibGUgdG8gZmluZCB0aGVtIGFuZCB1c2UgdGhlbS4KcnVuX2dzZWEgPSBUUlVFCgojIGxlYXZlIGJsYW5rIGlmIHlvdSB3YW50IHRoZSBub3RlYm9vayB0byBkaXNjb3ZlciB0aGUgZ3NlYSBkaXJlY3RvcnkgZm9yIGl0c2VsZgojZ3NlYV9kaXJlY3RvcnkgPSBwYXN0ZSh3b3JraW5nX2RpciwiTWVzZW5fdnNfSW1tdW5vLkdzZWFQcmVyYW5rZWQuMTQ5NzYzNTQ1OTI2MiIsc2VwPSIvIikgCmdzZWFfZGlyZWN0b3J5ID0gIiIKCiNUT0RPOiBjaGFuZ2UgdGhpcyB0byB1cGRhdGUgdG8gdGhlIGxhdGVzdCBnbXQgZmlsZS4KZGVzdF9nbXRfZmlsZSA8LSBmaWxlLnBhdGgod29ya2luZ19kaXIsCiAgICAgICAgICAiU3VwcGxlbWVudGFyeV9UYWJsZTNfSHVtYW5fR09CUF9BbGxQYXRod2F5c19ub19HT19pZWFfSnVseV8wMV8yMDE3X3N5bWJvbC5nbXQiICkKCm51bV9yYW5kb21pemF0aW9ucyA8LSAxMDAwCgpgYGAKCgo4LiBSdW4gR1NFQSB3aXRoIGdlbmUgc2V0IHJhbmRvbWl6YXRpb24KCkV2ZW4gdGhvdWdoIHdlIGFyZSBnb2luZyB0byBwZXJmb3JtIG91ciBvd24gcmFuZG9taXphdGlvbiwgd2Ugc3RpbGwgbmVlZCB0byBydW4gR1NFQSB0byBnZXQgdGhlIG9ic2VydmVkIEVTLiBJbiBhZGRpdGlvbiB3ZSB3aWxsIHVzZSB0aGUgRkRSIGZyb20gdGhpcyBnZW5lIHJhbmRvbWl6YXRpb24gcnVuIHRvIGNvbXBhcmUgd2l0aCBvdXIgbWFudWFsIHBoZW5vdHlwZSBwZXJtdXRhdGlvbnMuCmBgYHtyfQp0aW1lc3RhbXAoKQpzdGFydF9nc19wZXJtIDwtIFN5cy50aW1lKCkKaWYocnVuX2dzZWEpewogIGNvbW1hbmQgPC0gcGFzdGUoImphdmEgIC1YbXgxRyAtY3AiLGdzZWFfamFyLCAgInh0b29scy5nc2VhLkdzZWFQcmVyYW5rZWQgLWdteCIsIAogICAgICBkZXN0X2dtdF9maWxlLCAiLXJuayIgLHJua19maWxlLCAKICAgICAgIi1jb2xsYXBzZSBmYWxzZSAtbnBlcm0gIixudW1fcmFuZG9taXphdGlvbnMsIAogICAgICAiIC1wZXJtdXRlIGdlbmVfc2V0IC1zY29yaW5nX3NjaGVtZSB3ZWlnaHRlZCAtcnB0X2xhYmVsICIsCiAgICAgIHBhc3RlKGFuYWx5c2lzX25hbWUsImdzcmFuZCIsc2VwPSJfIiksCiIgLW51bSAxMDAgLXBsb3RfdG9wX3ggMjAgLXJuZF9zZWVkIDEyMzQ1ICAtc2V0X21heCAyMDAgLXNldF9taW4gMTUgLXppcF9yZXBvcnQgZmFsc2UgLW91dCIgLAogICAgICByYW5kX3dvcmtpbmdfZGlyLCAiLWd1aSBmYWxzZSA+IGdzZWFfb3V0cHV0LnR4dCIsc2VwPSIgIikKICBzeXN0ZW0oY29tbWFuZCkKfQpzdG9wX2dzX3Blcm0gPC0gU3lzLnRpbWUoKQp0aW1lc3RhbXAoKQpkaWZmdGltZShzdG9wX2dzX3Blcm0sc3RhcnRfZ3NfcGVybSwibWlucyIpCmBgYAoKCgojIyMgTWFudWFsIFBoZW5vdHlwZSBSYW5kb21pemF0aW9ucwo5LiBJbnN0ZWFkIG9mIHJhbmRvbWl6aW5nIGluIEdTRUEgY3JlYXRlIG91ciBvd24gcGhlbm90eXBlIHJhbmRvbWl6YXRpb24gaW4gUi4gIAoKRG8gdGhlIGZvbGxvd2luZyBzdGVwczoKCiAqIFNodWZmbGUgdGhlIGNsYXNzIGxhYmVscyB1c2luZyB0aGUgUiBzYW1wbGUgZnVuY3Rpb24uICAoRXZlbiB0aG91Z2ggdGhhdCBkYXRhIGNvbnNpc3RzIG9mIDQgY2xhc3NlcyB3ZSBkbyBub3QgbGltaXQgdGhlIHNodWZmbGluZyB0byBqdXN0IHRoZSB0d28gY2xhc3NlcyBiZWluZyBjb21wYXJlZCkKICogTm9ybWFsaXplIHNodWZmbGVkIGRhdGEKICogQ2FsY3VsYXRlIGRpc3BlcnNpb24gb2Ygc2h1ZmZsZWQgZGF0YQogKiBFeGNsdWRlIGdlbmVzIHdpdGggTE9DIG5hbWVzCiAqIENhbGN1bGF0ZWQgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gdXNpbmcgZWRnZVIKICogUnVuIEdTRUEgcHJlcmFua2VkIHVzaW5nIHJlc3VsdGluZyByYW5rcyAoKk5lZWRlZCB0byBzcGVjaWZ5IDEgcGVybXV0YXRpb24gYXMgaWYgeW91IHNldCBwZXJtdXRhdGlvbiB0byAwIEdTRUEgd2lsbCBub3QgY2FsY3VsYXRlIHRoZSBORVMgb3IgRkRSIHZhbHVlLiAgV2UgYXJlIG5vdCBpbnRlcmVzdGVkIGluIHRoZSBGRFIgdmFsdWUgYnV0IHdlIGRvIG5lZWQgdGhlIE5FUyB2YWx1ZS4qKQogKiBSZXBlYXQgZm9yIFggcmFuZG9taXphdGlvbnMuCiAKICpYID0gYHIgbnVtX3JhbmRvbWl6YXRpb25zYCBmb3IgdGhpcyBhbmFseXNpcwoKYGBge3J9CnRpbWVzdGFtcCgpCnN0YXJ0X3JhbmRfcGVybSA8LSBTeXMudGltZSgpCmlmKHJ1bl9nc2VhKXsKICAgIGxpYnJhcnkoImZvcmVhY2giKQogICAgbGlicmFyeSgiZG9QYXJhbGxlbCIpCiAgICAKICAgIAogICAgY2wgPC0gbWFrZUNsdXN0ZXIoZGV0ZWN0Q29yZXMoKSAtIDEpCiAgICByZWdpc3RlckRvUGFyYWxsZWwoY2wsIGNvcmVzID0gZGV0ZWN0Q29yZXMoKSAtIDEpCiAgICAKICAgICAgCiAgICAgIGRhdGEgPSBmb3JlYWNoKGkgPSAxOm51bV9yYW5kb21pemF0aW9ucywgLnBhY2thZ2VzID0gYygiZWRnZVIiKSwKICAgICAgICAgICAgICAgICAgIC5jb21iaW5lID0gcmJpbmQpICVkb3BhciUgewogICAgICAgICAgICAgICAgICAgICB0cnkoewogICAgICAgIHJhbmRfYW5hbHlzaXNfbmFtZSA8LSBwYXN0ZSgiUmFuZCIsaSxzZXA9Il8iKQogICAgCiAgICAgICAgcmFuZF9jbGFzcyA8LSBzYW1wbGUoY2xhc3NEZWZpbml0aW9uc19STkFTZXEkU1VCVFlQRSkKICAgICAgICAKICAgICAgICAjIGNyZWF0ZSBkYXRhIHN0cnVjdHVyZSB0byBob2xkIGNvdW50cyBhbmQgc3VidHlwZSBpbmZvcm1hdGlvbiBmb3IgZWFjaCBzYW1wbGUuCiAgICAgICAgZCA8LSBER0VMaXN0KGNvdW50cz1jb3VudHMsIGdyb3VwPXJhbmRfY2xhc3MpCiAgICAgICAgCiAgICAgICAgI05vcm1hbGl6ZSB0aGUgZGF0YQogICAgICAgIGQgPC0gY2FsY05vcm1GYWN0b3JzKGQpCiAgICAgICAgCiAgICAgICAgI2NhbGN1bGF0ZSBkaXNwZXJzaW9uCiAgICAgICAgZCA8LSBlc3RpbWF0ZUNvbW1vbkRpc3AoZCkKICAgICAgICBkIDwtIGVzdGltYXRlVGFnd2lzZURpc3AoZCkKICAgICAgICAKICAgICAgICAjdGhlIGJlbG93IHJlZ3VsYXIgZXhwcmVzc2lvbiBleGNsdWRlcyBnZW5lIG5hbWVzIHRoYXQgYXJlID8gb3IgdGhhdCBzdGFydCB3aXRoCiAgICAgICAgIyBMT0MgYW55IG51bWJlciBvZiBhZGRpdGlvbmFsIHRlcm1zIGNhbiBiZSBhZGRlZCB0byB0aGUgcmVndWxhciBleHByZXNpb24sIGZvciAgCiAgICAgICAgIyBleGFtcGxlIHRvIGV4Y2x1ZGUgYW55IGdlbmVzIHRoYXQgc3RhcnQgd2l0aCAiSGlzIiBhZGQgfF5IaXMgdG8gdGhlIAogICAgICAgICMgcmVndWxhciBleHByZXNzaW9uCiAgICAgICAgZXhjbHVkZSA8LSBncmVwKCJcXD98XkxPQyIsIHJvd25hbWVzKGQpLCB2YWx1ZT1UKQogICAgICAgIGQgPC0gZFt3aGljaCghcm93bmFtZXMoZCkgJWluJSBleGNsdWRlKSxdCiAgICAgICAgCiAgICAgICAgI2NhbGN1bGF0ZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBzdGF0aXN0aWNzIHdpdGggYSBzaW1wbGUgZGVzaWduCiAgICAgICAgZGUgPC0gZXhhY3RUZXN0KGQsIHBhaXI9YygiSW1tdW5vcmVhY3RpdmUiLCJNZXNlbmNoeW1hbCIpKQogICAgICAgIHR0X2V4YWN0X3Rlc3QgPC0gdG9wVGFncyhkZSxuPW5yb3coZCkpCiAgICAgICAgCiAgICAgICAgdHQgPC0gdHRfZXhhY3RfdGVzdAogICAgICAgICNjYWxjdWxhdGUgcmFua3MKICAgICAgICByYW5rc19STkFzZXEgPSBzaWduKHR0JHRhYmxlJGxvZ0ZDKSAqIC1sb2cxMCh0dCR0YWJsZSRQVmFsdWUpCiAgICAgICAgCiAgICAgICAgI2dlbmUgbmFtZXMgZnJvbSB0aGUgVENHQSBzZXQgY29udGFpbiBnZW5lIG5hbWUgYW5kIGVudHJleiBnZW5lIGlkcyBzZXBhcmF0ZWQgYnkgCiAgICAgICAgIyDigJh84oCZIGZvciBhbGwgc3Vic2VxdWVudCBlbnJpY2htZW50IGFuYWx5c2lzIHdlIG5lZWQgdG8gaGF2ZSBqdXN0IG9uZSBpZC4gIAogICAgICAgICMgU2VwYXJhdGUgdGhlIG5hbWVzIGludG8gdGhlaXIgdHdvIGlkcy4KICAgICAgICBnZW5lbmFtZXMgPC0gdW5saXN0KGxhcHBseSggcm93bmFtZXModHQkdGFibGUpLCBmdW5jdGlvbihkYXRhKSAKICAgICAgICAgIHt1bmxpc3Qoc3Ryc3BsaXQoZGF0YSwiXFx8IikpWzFdfSkpCiAgICAgICAgZ2VuZWlkcyA8LSB1bmxpc3QobGFwcGx5KCByb3duYW1lcyh0dCR0YWJsZSksICBmdW5jdGlvbihkYXRhKSAKICAgICAgICAgIHt1bmxpc3Qoc3Ryc3BsaXQoZGF0YSwiXFx8IikpWzJdfSkpIAogICAgICAgIAogICAgICAgICNjcmVhdGUgcmFua3MgZmlsZQogICAgICAgIHJhbmtzX1JOQXNlcSA8LSBjYmluZChnZW5lbmFtZXMsIHJhbmtzX1JOQXNlcSkKICAgICAgICBjb2xuYW1lcyhyYW5rc19STkFzZXEpIDwtIGMoIkdlbmVOYW1lIiwicmFuayIpCiAgICAgICAgCiAgICAgICAgI3NvcnQgcmFua3MgaW4gZGVjcmVhc2luZyBvcmRlcgogICAgICAgIHJhbmtzX1JOQXNlcSA8LSByYW5rc19STkFzZXFbb3JkZXIoYXMubnVtZXJpYyhyYW5rc19STkFzZXFbLDJdKSxkZWNyZWFzaW5nID0gVFJVRSksXQogICAgICAgIHJhbmtfZmlsZW5hbWUgPC0gZmlsZS5wYXRoKHJhbmRfd29ya2luZ19kaXIscGFzdGUoInJhbmQiLGksInJhbmtzLnJuayIsc2VwPSJfIikpCiAgICAgICAgd3JpdGUudGFibGUocmFua3NfUk5Bc2VxLCByYW5rX2ZpbGVuYW1lLCAKICAgICAgICAgICAgICAgICAgICBjb2wubmFtZSA9IFRSVUUsIHNlcD0iXHQiLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKICAgICAgICAKICAgICAgICAjbmVlZCB0byBzZXQgcGVybXV0YXRpb24gbnVtYmVyIHRvIDEgaW4gb3JkZXIgZm9yIHRoZSBORVMgdG8gYmUgY2FsY3VsYXRlZC4gIAogICAgICAgICMgSWYgeW91IHNldCBpdCB0byB6ZXJvIHRoZSByZXN1bHRzIGhhdmUgbm8gcC12YWx1ZSwgZmRyIG9yIE5FUyB2YWx1ZXMuIAogICAgICAgICBjb21tYW5kIDwtIHBhc3RlKCJqYXZhICAtWG14MUcgLWNwIixnc2VhX2phciwgICJ4dG9vbHMuZ3NlYS5Hc2VhUHJlcmFua2VkIC1nbXgiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBkZXN0X2dtdF9maWxlLCAiLXJuayIgLHJhbmtfZmlsZW5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICItY29sbGFwc2UgZmFsc2UgLW5wZXJtIDEgLXBlcm11dGUgZ2VuZV9zZXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICIgLXNjb3Jpbmdfc2NoZW1lIHdlaWdodGVkIC1ycHRfbGFiZWwgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kX2FuYWx5c2lzX25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIiAtbnVtIDEwMCAtcGxvdF90b3BfeCAyMCAtcm5kX3NlZWQgMTIzNDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICIgLXNldF9tYXggMjAwIC1zZXRfbWluIDE1IC16aXBfcmVwb3J0IGZhbHNlIC1vdXQiICwKICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kX3dvcmtpbmdfZGlyLCAiLWd1aSBmYWxzZSA+IGdzZWFfb3V0cHV0LnR4dCIsc2VwPSIgIikKICAgICAgICAgIHN5c3RlbShjb21tYW5kKQogICAgIH0pCiAgICAgICAgICAgICAgICAgICB9CiAgICAgIHN0b3BDbHVzdGVyKGNsKQp9CiBzdG9wX3JhbmRfcGVybSA8LSBTeXMudGltZSgpCiB0aW1lc3RhbXAoKQogZGlmZnRpbWUoc3RvcF9yYW5kX3Blcm0sc3RhcnRfcmFuZF9wZXJtLCJtaW5zIikKYGBgCgoKCiMjIyBDYWxjdWxhdGUgRkRSCjEwLiBJbiBvcmRlciB0byBjYWxjdWxhdGUgdGhlIEZEUiBsb2FkIGluIHRoZSBHU0VBIHJlc3VsdHMgZnJvbSBlYWNoIG9mIHRoZSByYW5kb21pemF0aW9ucy4gIEZvciBlYWNoIGdlbmUgc2V0IGNvbGxlY3QgaXRzIEVTIGFuZCBORVMgdmFsdWVzLiAgVG8gY2FsY3VsYXRlIHRoZSBGRFIgZm9yIGVhY2ggZ2VuZSBzZXQ6CgogKiBGb3IgZ2l2ZW4gZ2VuZSBzZXQgZ2V0IGFsbCByYW5kb20gRVMgYW5kIE5FUyB2YWx1ZXMuCiAqIElmIGdpdmVuIGdlbmUgc2V0IGhhcyBhIG5lZ2F0aXZlIEVTIHZhbHVlLCBjb3VudCB0aGUgbnVtYmVyIG9mIHJhbmRvbSBFUyB2YWx1ZXMgYXJlIGxlc3MgdGhhbiBpdC4gIERpdmlkZSBjb3VudCBieSBudW1iZXIgb2YgcmFuZG9taXphdGlvbnMgdG8gZ2V0IHRoZSBGRFIuCiAqIElmIGdpdmVuIGdlbmUgc2V0IGhhcyBhIHBvc2l0aXZlIEVTIHZhbHVlLCBjb3VudCB0aGUgbnVtYmVyIG9mIHJhbmRvbSBFUyB2YWx1ZXMgYXJlIGdyZWF0ZXIgdGhhbiBpdC4gIERpdmlkZSBjb3VudCBieSBudW1iZXIgb2YgcmFuZG9taXphdGlvbnMgdG8gZ2V0IHRoZSBGRFIuCiAKICBhLiBMb2FkIGluIFJhbmRvbWl6YXRpb24gcmVzdWx0cwpgYGB7cn0KcmFuZF9kaXJlY3RvcmllcyA8LSBsaXN0LmZpbGVzKHBhdGggPSByYW5kX3dvcmtpbmdfZGlyLCBwYXR0ZXJuID0gIlJhbmQiKQphbGxfcmFuZF9lcyA8LSBjKCkKYWxsX3JhbmRfbmVzIDwtIGMoKQoKZm9yKGkgaW4gMTpsZW5ndGgocmFuZF9kaXJlY3RvcmllcykpewogICAgY3VycmVudF9yYW5kX2ZpbGVzIDwtIGxpc3QuZmlsZXMocGF0aCA9IGZpbGUucGF0aChyYW5kX3dvcmtpbmdfZGlyLHJhbmRfZGlyZWN0b3JpZXNbaV0pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiZ3NlYV9yZXBvcnRfZm9yX25hXyIpCiAgICAjcmVzdHJpY3QgdG8ganVzdCB0aGUgeGxzIGZpbGUuIAogICAgY3VycmVudF9yYW5kX2ZpbGVzIDwtIGN1cnJlbnRfcmFuZF9maWxlc1tncmVwKGN1cnJlbnRfcmFuZF9maWxlcywgcGF0dGVybj0iXFwueGxzIildCiAgICAKICAgICNsb2FkIHRoZSBwb3NpdGl2ZSBmaWxlcwogICAgZm9yKGogaW4gMTpsZW5ndGgoY3VycmVudF9yYW5kX2ZpbGVzKSl7CiAgICAgICAgICByYW5kX3Jlc3VsdHMgPC0gcmVhZC50YWJsZSggIGZpbGUucGF0aChyYW5kX3dvcmtpbmdfZGlyLHJhbmRfZGlyZWN0b3JpZXNbaV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRfcmFuZF9maWxlc1tqXSksICBoZWFkZXIgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwgcXVvdGU9IlwiIiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKICAgICAgICAgIGVzX3ZhbHVlcyA8LSBkYXRhLmZyYW1lKHJhbmRfcmVzdWx0cyROQU1FLCByYW5kX3Jlc3VsdHMkRVMpCiAgICAgICAgICBuZXNfdmFsdWVzIDwtIGRhdGEuZnJhbWUocmFuZF9yZXN1bHRzJE5BTUUsIHJhbmRfcmVzdWx0cyRORVMpCiAgICAgICAgICBjb2xuYW1lcyhlc192YWx1ZXMpWzJdIDwtIGN1cnJlbnRfcmFuZF9maWxlc1tqXQogICAgICAgICAgY29sbmFtZXMobmVzX3ZhbHVlcylbMl0gPC0gY3VycmVudF9yYW5kX2ZpbGVzW2pdCiAgICAgICAgICAjYWRkIGVzX3ZhbHVlcyB0byBzZXQuCiAgICAgICAgICBpZihpID09IDEgJiYgaiA9PTEgKXsKICAgICAgICAgICAgYWxsX3JhbmRfZXMgPC0gZXNfdmFsdWVzCiAgICAgICAgICAgIGFsbF9yYW5kX25lcyA8LSBuZXNfdmFsdWVzCiAgICAgICAgICB9IGVsc2V7CiAgICAgICAgICAgIGFsbF9yYW5kX2VzIDwtIG1lcmdlKGFsbF9yYW5kX2VzLCBlc192YWx1ZXMsYnkueCA9IDEsYnkueSA9IDEsIGFsbCA9IFRSVUUpCiAgICAgICAgICAgIGFsbF9yYW5kX25lcyA8LSBtZXJnZShhbGxfcmFuZF9uZXMsIG5lc192YWx1ZXMsYnkueCA9IDEsYnkueSA9IDEsIGFsbCA9IFRSVUUpCiAgICAgICAgICB9CiAgICAgIAogICAgfQp9CmBgYAoKICBiLiBMb2FkIGluIHRoZSByZWFsIHJhbmtpbmdzCmBgYHtyfQogZ3NlYV9kaXJlY3RvcnkgPC0gbGlzdC5maWxlcyhwYXRoID0gcmFuZF93b3JraW5nX2RpciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSBwYXN0ZShhbmFseXNpc19uYW1lLCJnc3JhbmQiLHNlcD0iXyIpKQpnc2VhX3Jlc3VsdHNfZmlsZXMgPC0gbGlzdC5maWxlcyhwYXRoID0gZmlsZS5wYXRoKHJhbmRfd29ya2luZ19kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3NlYV9kaXJlY3RvcnkpLCBwYXR0ZXJuID0gImdzZWFfcmVwb3J0X2Zvcl9uYV8iKQpnc2VhX3Jlc3VsdHNfZmlsZXM8LSBnc2VhX3Jlc3VsdHNfZmlsZXNbZ3JlcChnc2VhX3Jlc3VsdHNfZmlsZXMsIHBhdHRlcm49IlxcLnhscyIpXQphY3R1YWxfZ3NlYV9yZXN1bHRzIDwtIGMoKQogICAgI2xvYWQgdGhlIHBvc2l0aXZlIGZpbGVzCiAgICBmb3IoaiBpbiAxOmxlbmd0aChnc2VhX3Jlc3VsdHNfZmlsZXMpKXsKICAgICAgICAgIGFjdHVhbF9nc2VhX3Jlc3VsdHNfMSA8LSAgcmVhZC50YWJsZSggIGZpbGUucGF0aChyYW5kX3dvcmtpbmdfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3NlYV9kaXJlY3RvcnksZ3NlYV9yZXN1bHRzX2ZpbGVzW2pdKSwgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc2VwID0gIlx0IiwgcXVvdGU9IlwiIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCiAgICAgICAgICAgIGFjdHVhbF9nc2VhX3Jlc3VsdHMgPC0gcmJpbmQoYWN0dWFsX2dzZWFfcmVzdWx0cyAsIGFjdHVhbF9nc2VhX3Jlc3VsdHNfMSkKICAgICAgICAgCn0KCiNyZXBsYWNlIGFsbCB0aGUgemVybyBmZHIgdmFsdWVzIHdpdGggYSBtb3JlIHByZWNpc2UgbWVhc3VyZW1lbnQKYWN0dWFsX2dzZWFfcmVzdWx0cyRGRFIucS52YWxbd2hpY2goYWN0dWFsX2dzZWFfcmVzdWx0cyRGRFIucS52YWwgPT0gMCldIDwtIDEvMTAwMQpnc3JhbmRfZ3NlYV9yZXN1bHRzIDwtIGFjdHVhbF9nc2VhX3Jlc3VsdHMKYGBgCgogIGMuIENhbGN1bGF0ZSB0aGUgbm9taW5hbCBwLXZhbHVlIHVzaW5nIHRoZSBwaGVub3R5cGUgcGVybXV0YXRpb24gcmVzdWx0cwpgYGB7cn0KI2NhbGN1bGF0ZSB0aGUgbmV3IGZkciB2YWx1ZXMKbmV3X2ZkciA8LSBjKCkKbm9taW5hbF9wdmFsdWVzIDwtIGMoKQpmb3IobSBpbiAxOmRpbShhY3R1YWxfZ3NlYV9yZXN1bHRzKVsxXSl7CiAgY3VycmVudF9kYXRhIDwtIGFzLm51bWVyaWMoYWxsX3JhbmRfZXNbd2hpY2goYWxsX3JhbmRfZXMkcmFuZF9yZXN1bHRzLk5BTUUgPT0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWN0dWFsX2dzZWFfcmVzdWx0cyROQU1FW21dKSwyOm5jb2woYWxsX3JhbmRfZXMpXSkKICBpZihhY3R1YWxfZ3NlYV9yZXN1bHRzJEVTW21dIDwgMCApewogICAgbm9taW5hbF9wdmFsdWVzIDwtIGMobm9taW5hbF9wdmFsdWVzLCAKICAgIChsZW5ndGgoIHdoaWNoKGN1cnJlbnRfZGF0YSA8IGFjdHVhbF9nc2VhX3Jlc3VsdHMkRVNbbV0pKSsxKS8obnVtX3JhbmRvbWl6YXRpb25zICsgMSkpICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgfSBlbHNlIHsKICAgIG5vbWluYWxfcHZhbHVlcyA8LSBjKG5vbWluYWxfcHZhbHVlcywgCiAgICAobGVuZ3RoKCB3aGljaChjdXJyZW50X2RhdGEgPiBhY3R1YWxfZ3NlYV9yZXN1bHRzJEVTW21dKSkrMSkvKG51bV9yYW5kb21pemF0aW9ucyArIDEpKSAgCiAgfQp9CgpuZXdfZmRyIDwtIHAuYWRqdXN0KG5vbWluYWxfcHZhbHVlcywgbWV0aG9kID0gIkJIIikKYGBgCgogIGQuIENyZWF0ZSBhIG5ldyByZXN1bHRzIGZpbGUgY29udGFpbmluZyBvdXIgY29tcHV0ZWQgRkRSIHZhbHVlcwpgYGB7cn0KCiNjcmVhdGUgYSBuZXcgZmFrZSBlbnJpY2htZW50IHJlc3VsdHMgZmlsZSBzbyB3ZSBjYW4gY29tcGFyZSB0aGUgZGlmZmVyZW5jZXMuCm5ld19mZHJfZ3NlYV9yZXN1bHRzX2ZpbGVzIDwtIGFjdHVhbF9nc2VhX3Jlc3VsdHMKbmV3X2Zkcl9nc2VhX3Jlc3VsdHNfZmlsZXMkRkRSLnEudmFsIDwtIG5ld19mZHIKICAKZW5yaWNobWVudF9yZXN1bHRzX2ZpbGVuYW1lIDwtIGZpbGUucGF0aCh3b3JraW5nX2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmV3X2Zkcl9nc2VhX3Jlc3VsdHNfZmlsZXNfZWRnZXJfcmFuZC50eHQgIikKCndyaXRlLnRhYmxlKG5ld19mZHJfZ3NlYV9yZXN1bHRzX2ZpbGVzWywxOjExXSAsZW5yaWNobWVudF9yZXN1bHRzX2ZpbGVuYW1lICwgCiAgICAgICAgICAgIGNvbC5uYW1lID0gVFJVRSwgc2VwPSJcdCIsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQpgYGAKCgogIGUuIFBsb3QgdGhlIEZEUiBzYXR1cmF0aW9uIGN1cnZlCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpzYXR1cmF0aW9uX2N1cnZlIDwtIGMoKQpzZXJpZXNfb2ZfcmFuZG9taXphdGlvbnMgPC0gYygxMDAsMjAwLDMwMCw0MDAsNTAwLDYwMCw3MDAsODAwLDkwMCwxMDAwKQpmZHJfdGhyZXNoIDwtIDAuMDEKCmlmKG51bV9yYW5kb21pemF0aW9ucyA+PSAxMDAwKXsKICBmb3IobCBpbiAxOmxlbmd0aChzZXJpZXNfb2ZfcmFuZG9taXphdGlvbnMpKXsKICAgICAgc2F0dXJhdGlvbl9mZHIgPC0gYygpCiAgICAgIGZvcihtIGluIDE6ZGltKGFjdHVhbF9nc2VhX3Jlc3VsdHMpWzFdKXsKICAgICAgICBjdXJyZW50X2RhdGEgPC0gYXMubnVtZXJpYyhhbGxfcmFuZF9lc1t3aGljaChhbGxfcmFuZF9lcyRyYW5kX3Jlc3VsdHMuTkFNRSA9PSAKICAgICAgICAgICAgICAgICAgYWN0dWFsX2dzZWFfcmVzdWx0cyROQU1FW21dKSwKICAgICAgICAgICAgICAgICAgMjooc2VyaWVzX29mX3JhbmRvbWl6YXRpb25zW2xdICoyICsgMSldKQogICAgICAgIGlmKGFjdHVhbF9nc2VhX3Jlc3VsdHMkRVNbbV0gPCAwICl7CiAgICAgICAgICBzYXR1cmF0aW9uX2ZkciA8LSBjKHNhdHVyYXRpb25fZmRyLCAobGVuZ3RoKCB3aGljaChjdXJyZW50X2RhdGEgPCAKICAgICAgICAgICAgICAgICAgYWN0dWFsX2dzZWFfcmVzdWx0cyRFU1ttXSkpKzEpLwogICAgICAgICAgICAgICAgICAgIChzZXJpZXNfb2ZfcmFuZG9taXphdGlvbnNbbF0gKyAxKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICB9IGVsc2UgewogICAgICAgICAgc2F0dXJhdGlvbl9mZHIgPC0gYyhzYXR1cmF0aW9uX2ZkciwgKGxlbmd0aCggd2hpY2goY3VycmVudF9kYXRhID4gCiAgICAgICAgICAgICAgICAgIGFjdHVhbF9nc2VhX3Jlc3VsdHMkRVNbbV0pKSsxKS8KICAgICAgICAgICAgICAgICAgICAoc2VyaWVzX29mX3JhbmRvbWl6YXRpb25zW2xdICsgMSkpICAKICAgICAgICB9CiAgICAgIH0KICAgICAgCiAgICAgICNnZXQgdGhlIG51bWJlciBvZiBnZW5lIHNldHMgdGhhdCBhcmUgbGVzcyB0aGFuIGZkciB2YWx1ZSB0aHJlc2hvbGQKICAgICAgc2F0dXJhdGlvbl9jdXJ2ZSA8LSBjKHNhdHVyYXRpb25fY3VydmUsIGxlbmd0aCh3aGljaChzYXR1cmF0aW9uX2ZkciA8PSBmZHJfdGhyZXNoKSkpCiAgfQogIHNhdHVyYXRpb25fY3VydmVfZmlsZW5hbWUgPSBmaWxlLnBhdGgocmFuZF93b3JraW5nX2Rpciwic2F0dXJhdGlvbl9jdXJ2ZS5wbmciKQogIHBuZyhzYXR1cmF0aW9uX2N1cnZlX2ZpbGVuYW1lKQogIHBsb3Qoc2VyaWVzX29mX3JhbmRvbWl6YXRpb25zLHNhdHVyYXRpb25fY3VydmUsIAogICAgICAgeGxhYj0ibnVtYmVyIG9mIHJhbmRvbWl6YXRpb25zIiwgCiAgICAgICB5bGFiID0gIk51bWJlciBvZiBzaWduaWZpY2FudCggRkRSIDwwLjAxKSBnZW5lIHNldHMiLCBtYWluID0gIkZEUiBTYXR1cmF0aW9uIGN1cnZlIikKICBkZXYub2ZmKCkKfQpgYGAKCgpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuY2FwPSJGRFIgU2F0dXJhdGlvbiBjdXJ2ZSAtIG51bWJlciBvZiBzaWduaWZpbmNhbnQgZ2VuZSBzZXRzIGZvciB0aGUgRkRSIHRocmVzaG9sZCA8IDAuMDEgZm9yIGRpZmZlcmVudCBudW1iZXIgb2YgcmFuZG9taXphdGlvbnMuIixvdXQud2lkdGg9Ijc1JSIgLCBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy5wb3M9IiFodCJ9CmlmKGV4aXN0cygic2F0dXJhdGlvbl9jdXJ2ZV9maWxlbmFtZSIpKXsKICBrbml0cjo6aW5jbHVkZV9ncmFwaGljcyhzYXR1cmF0aW9uX2N1cnZlX2ZpbGVuYW1lKQp9CmBgYAojIyMgQmFzaWMgY29tcGFyaXNvbiBvZiBGRFIgdmFsdWVzIGdlbmVyYXRlZCBieSBHU0VBIGdlbmUgc2V0IHJhbmRvbWl6YXRpb24gYW5kIG1hbnVhbCBwaGVub3R5cGUgcmFuZG9taXphdGlvbiB3aXRoIGVkZ2VSCgpQbG90IHRoZSBjb21wYXJpc29uIG9mIHRoZSBGRFIgdmFsdWVzIHVzaW5nIGdzcmFuZCB2cyBwaGVub3R5cGUgcmFuZG9taXphdGlvbiBmcm9tIFIgdXNpbmcgZWRnZVIKYGBge3J9CmZkcl9jb21wYXJpc29uX2ZpbGVuYW1lID0gZmlsZS5wYXRoKHJhbmRfd29ya2luZ19kaXIsImZkcl9jb21wYXJpc29uLnBuZyIpCnBuZyhmZHJfY29tcGFyaXNvbl9maWxlbmFtZSkKcGxvdChjYmluZChnc3JhbmRfZ3NlYV9yZXN1bHRzJEZEUi5xLnZhbCwgbmV3X2ZkciksIHlsYWI9ImVkZ2VSIHJhbmRvbWl6YXRpb24gRkRSIiwgCiAgICAgeGxhYj0iZ3MgcmFuZG9taXphdGlvbnMgRkRSIiwgbWFpbj0iR1NFQSBncyByYW5kIEZEUiB2cyBlZGdlUiBtYW51YWwgcmFuZCBGRFIiKQpkZXYub2ZmKCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuY2FwPSJDb21wYXJpc29uIG9mIHRoZSBGRFIgdmFsdWVzIHVzaW5nIGdzcmFuZCB2cyBwaGVub3R5cGUgcmFuZG9taXphdGlvbi4iLG91dC53aWR0aD0iNzUlIiwgZmlnLmFsaWduPSJjZW50ZXIiLCBmaWcucG9zPSIhaHQifQppZihleGlzdHMoImZkcl9jb21wYXJpc29uX2ZpbGVuYW1lIikpewogIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGZkcl9jb21wYXJpc29uX2ZpbGVuYW1lKQp9CmBgYAoKUGxvdCB0aGUgY29tcGFyaXNvbiBvZiB0aGUgRkRSIHZhbHVlcyB1c2luZyBnc3JhbmQgdnMgcGhlbm90eXBlIHJhbmRvbWl6YXRpb24gZnJvbSBSIHVzaW5nIGVkZ2VSIC0gbG9nIHRyYW5zZm9ybSB0aGUgcC12YWx1ZXMgdG8gYmV0dGVyIHNlZSB0aGUgZGlmZmVyZW5jZXMuCmBgYHtyfQpsb2dfZmRyX2NvbXBhcmlzb25fZmlsZW5hbWUgPSBmaWxlLnBhdGgocmFuZF93b3JraW5nX2RpciwibG9nX2Zkcl9jb21wYXJpc29uLnBuZyIpCnBuZyhsb2dfZmRyX2NvbXBhcmlzb25fZmlsZW5hbWUpCnBsb3QoY2JpbmQoLWxvZzEwKGdzcmFuZF9nc2VhX3Jlc3VsdHMkRkRSLnEudmFsKSwgLWxvZzEwKG5ld19mZHIpKSwgCiAgICAgeWxhYj0iLWxvZzEwIGVkZ2VSIHJhbmRvbWl6YXRpb24gRkRSIiwgCiAgICAgeGxhYj0iLWxvZzEwIGdzIHJhbmRvbWl6YXRpb25zIEZEUiIsIAogICAgIG1haW49Ii1sb2cxMCB0cmFuc2Zvcm1lZCAtIEdTRUEgZ3MgcmFuZCBGRFIgdnMgXG5lZGdlUiBtYW51YWwgcmFuZCBGRFIiKQpkZXYub2ZmKCkKYGBgCgpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuY2FwPSJDb21wYXJpc29uIG9mIHRoZSBGRFIgdmFsdWVzIHVzaW5nIGdzcmFuZCB2cyBwaGVub3R5cGUgcmFuZG9taXphdGlvbiAoLWxvZzEwIEZEUikuIixvdXQud2lkdGg9Ijc1JSIsIGZpZy5hbGlnbj0iY2VudGVyIiwgZmlnLnBvcz0iIWh0In0KaWYoZXhpc3RzKCJsb2dfZmRyX2NvbXBhcmlzb25fZmlsZW5hbWUiKSl7CiAga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MobG9nX2Zkcl9jb21wYXJpc29uX2ZpbGVuYW1lKQp9CmBgYAoKQ3JlYXRlIGEgc3Vic2V0IG9mIHRoZSBhYm92ZSBwbG90IHRvIGluY2x1ZGUgYW55IGdlbmUgc2V0cyB3aGVyZSB0aGUgRkRSIGlzIHNpZ25pZmljYW50IGluIGF0IGxlYXN0IG9uZSBvZiB0aGUgYW5hbHlzZXMKCmBgYHtyfQp0ZW1wPC0gY2JpbmQoZ3NyYW5kX2dzZWFfcmVzdWx0cyRGRFIucS52YWwsIG5ld19mZHIpCnRlbXAgPC0gdGVtcFt3aGljaCh0ZW1wWywxXTwwLjAxIHwgdGVtcFssMl08MC4wMSksXQoKdG9wX2Zkcl9jb21wYXJpc29uX2ZpbGVuYW1lID0gZmlsZS5wYXRoKHJhbmRfd29ya2luZ19kaXIsInRvcF9mZHJfY29tcGFyaXNvbi5wbmciKQpwbmcodG9wX2Zkcl9jb21wYXJpc29uX2ZpbGVuYW1lKQpwbG90KC1sb2cxMCh0ZW1wWywxXSksIC1sb2cxMCh0ZW1wWywyXSksIHlsYWI9Ii1sb2cxMCBlZGdlUiByYW5kb21pemF0aW9uIEZEUiIsIAogICAgIHhsYWI9Ii1sb2cxMCBncyByYW5kb21pemF0aW9ucyBGRFIiLAptYWluPXBhc3RlKCItbG9nMTAgdHJhbnNmb3JtZWQgR1NFQSBncyByYW5kIHZzIGVkZ2VSIG1hbnVhbCByYW5kIiAsCiAgICAgICAgICAgIi0gXG5zaWduaWZpY2FudCBpbiBhdCBsZWFzdCBvbmUgYW5hbHlzaXMiLCBzZXA9IiIpKQpkZXYub2ZmKCkKYGBgCgoKYGBge3IgZWNobz1GQUxTRSwgZmlnLmNhcD0iQ29tcGFyaXNvbiBvZiB0aGUgRkRSIHZhbHVlcyB1c2luZyBnc3JhbmQgdnMgcGhlbm90eXBlIHJhbmRvbWl6YXRpb24gZm9yIGp1c3QgZ2VuZSBzZXRzIHRoYXQgYXJlIHNpZ25pZmljYW50IGluIGF0IGxlYXN0IG9uZSBhbmFseXNpcyAoLWxvZzEwIEZEUikuIixvdXQud2lkdGg9Ijc1JSIgLCBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy5wb3M9IiFodCJ9CmlmKGV4aXN0cygidG9wX2Zkcl9jb21wYXJpc29uX2ZpbGVuYW1lIikpewogIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHRvcF9mZHJfY29tcGFyaXNvbl9maWxlbmFtZSkKfQpgYGAKClZlbm4gRGlhZ3JhbSBvZiBvdmVybGFwcwpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoVmVubkRpYWdyYW0pCnR3b19tZXRob2Rfb3ZlcmxhcCA9IGZpbGUucGF0aChyYW5kX3dvcmtpbmdfZGlyLCIyX3Jlc3VsdHNfb3ZlcmxhcF92MS5wbmciKQpwbmcodHdvX21ldGhvZF9vdmVybGFwKQogIGEgPC0gZ3NyYW5kX2dzZWFfcmVzdWx0cyROQU1FW3doaWNoKGdzcmFuZF9nc2VhX3Jlc3VsdHMkRkRSLnEudmFsIDwgMC4wMSldCiAgYiA8LSBuZXdfZmRyX2dzZWFfcmVzdWx0c19maWxlcyROQU1FW3doaWNoKG5ld19mZHJfZ3NlYV9yZXN1bHRzX2ZpbGVzJEZEUi5xLnZhbCA8IDAuMDEpXQpyZXN1bHRzIDwtIGRyYXcucGFpcndpc2UudmVubihhcmVhMSA9IGxlbmd0aChhKSwgCiAgICAgICAgICAgICAgICAgYXJlYTIgPSBsZW5ndGgoYiksIAogICAgICAgICAgICAgICAgIG4xMiA9IGxlbmd0aChpbnRlcnNlY3QoYSxiKSksIAogICAgICAgICAgICAgICAgIGNhdGVnb3J5ID0gYygiZ3NyYW5kIiwgICJlZGdlUl9yYW5kIiksIGx0eSA9ICJibGFuayIsIAogICAgZmlsbCA9IGMoInNreWJsdWUiLCAicGluazEiKSxjcm9zcy5hcmVhID0gbGVuZ3RoKGludGVyc2VjdChhLGIpKSkKZGV2Lm9mZigpCmBgYAoKYGBge3IgZWNobz1GQUxTRSxvdXQud2lkdGg9Ijc1JSIsIGZpZy5hbGlnbj0iY2VudGVyIiwgZmlnLnBvcz0iIWh0In0KaWYoZXhpc3RzKCJ0d29fbWV0aG9kX292ZXJsYXAiKSl7CiAga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3ModHdvX21ldGhvZF9vdmVybGFwKQp9CmBgYAoKRmlndXJlIDg6IE92ZXJsYXAgb2YgZ3NyYW5kIGFuZCBlZGdlUl9yYW5kIC0gQ29tcGFyZSB0aGUgbnVtYmVyIG9mIHNpZ25pZmljYW50IChGRFIgPCAwLjAxKSBnZW5lIHNldHMgZnJvbSB0aGUgdHdvIG1ldGhvZHMsIEdTRUEgZ3MgcmFuZG9taXphdGlvbiBhbmQgZWRnZVIgbWFudWFsIHJhbmRvbWl6YXRpb24uCgojIyBMYXVuY2ggQ3l0b3NjYXBlCkNyZWF0ZSBFTSB0aHJvdWdoIEN5UkVTVCBpbnRlcmZhY2UgLSBtYWtlIHN1cmUgeW91IG9wZW4gQ3l0b3NjYXBlIHdpdGggYSAtUiAxMjM0ICh0byBlbmFibGUgcmVzdCBmdW5jdGlvbmFsaXR5KSBhbmQgYWxsb3cgUiB0byB0YWxrIGRpcmVjdGx5IHRvIEN5dG9zY2FwZS4KCkxhdW5jaCBDeXRvc2NhcGUgKGJ5IGRlZmF1bHQgQ3l0b3NjYXBlIHdpbGwgYXV0b21hdGljYWxseSBlbmFibGUgcmVzdCBzbyBhcyBsb25nIGFzIGN5dG9zY2FwZSAzLjMgb3IgaGlnaGVyIGlzIG9wZW4gUiBzaG91bGQgYmUgYWJsZSB0byBjb21tdW5pY2F0ZSB3aXRoIGl0KSAgCgojIyBTZXQgdXAgY29ubmVjdGlvbiBmcm9tIFIgdG8gQ3l0b3NjYXBlIApgYGB7ciBpbml0aWFsaXplIGN5dG9zY2FwZSBjb25uZWN0aW9ufQoKI3VzZSBlYXN5IGN5UmVzdCBsaWJyYXJ5IHRvIGNvbW11bmljYXRlIHdpdGggY3l0b3NjYXBlLgp0cnlDYXRjaChleHByID0geyBsaWJyYXJ5KCJSQ3kzIil9LCAKICAgICAgICAgZXJyb3IgPSBmdW5jdGlvbihlKSB7IHNvdXJjZSgiaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL2Jpb2NMaXRlLlIiKQogICAgICAgICAgIGJpb2NMaXRlKCJSQ3kzIil9LCBmaW5hbGx5ID0gbGlicmFyeSgiUkN5MyIpKQoKY3l0b3NjYXBlLm9wZW4gPSBUUlVFCgp0cnlDYXRjaChleHByID0geyBjeXJlc3RHRVQoInZlcnNpb24iKX0sIAogICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIHsgcmV0dXJuIChjeXRvc2NhcGUub3BlbiA9IEZBTFNFKX0sIAogICAgICAgICBmaW5hbGx5ID1mdW5jdGlvbihyKXsgcmV0dXJuKGN5dG9zY2FwZS5vcGVuID0gVFJVRSl9KQogICAgICAgICAKaWYoIWN5dG9zY2FwZS5vcGVuKXsKICAjdHJ5IGFuZCBsYXVuY2ggY3l0b3NjYXBlCiBwcmludCgiQ3l0b3NjYXBlIGlzIG5vdCBvcGVuLiAgUGxlYXNlIGxhdW5jaCBjeXRvc2NhcGUuIikKfSBlbHNlewogIGN5dG9zY2FwZS52ZXJzaW9uID0gIGN5cmVzdEdFVCgidmVyc2lvbiIpCn0KCmBgYAoKIyMgQ3JlYXRlIGFuIGVucmljaG1lbnQgbWFwCmBgYHtyIGNyZWF0ZSBlbnJpY2htZW50IG1hcH0KCiNkZWZpbmVkIHRocmVzaG9sZCBmb3IgR1NFQSBlbnJpY2htZW50cyAobmVlZCB0byBiZSBzdHJpbmdzIGZvciBjeXJlc3QgY2FsbCkKcHZhbHVlX2dzZWFfdGhyZXNob2xkIDwtICIwLjAxIgpxdmFsdWVfZ3NlYV90aHJlc2hvbGQgPC0iMC4wMDEiCgpzaW1pbGFyaXR5X3RocmVzaG9sZCA8LSAiMC4zNzUiCnNpbWlsYXJpdHlfbWV0cmljID0gIkNPTUJJTkVEIgoKY3VyX21vZGVsX25hbWUgPC0gcGFzdGUoYW5hbHlzaXNfbmFtZSwgImVkZ2VSX3JhbmQiLHNlcD0iXyIpCgoKI2FsdGhvdWdoIHRoZXJlIGlzIGEgZ210IGZpbGUgaW4gdGhlIGdzZWEgZWRiIHJlc3VsdHMgZGlyZWN0b3J5IGl0IGhhdmUgYmVlbiBmaWx0ZXJlZCB0byAKI2NvbnRhaW4gb25seSBnZW5lcyByZXByZXNlbnRlZCBpbiB0aGUgZXhwcmVzc2lvbiBzZXQuICBJZiB5b3UgdXNlIHRoaXMgZmx0ZXJlZCBmaWxlIHlvdSAKI3dpbGwgZ2V0IGRpZmZlcmVudCBwYXRod2F5IGNvbm5lY3Rpdml0eSBkZXBlbmRpbmcgb24gdGhlIGRhdGFzZXQgYmVpbmcgdXNlZC4gIFdlIHJlY29tbWVuZCAKI3VzaW5nIG9yaWdpbmFsIGdtdCBmaWxlIHVzZWQgZm9yIHRoZSBnc2VhIGFuYWx5c2lzIGFuZCBub3QgdGhlIGZpbHRlcmVkIAojIG9uZSBpbiB0aGUgcmVzdWx0cyBkaXJlY3RvcnkuCmdtdF9nc2VhX2ZpbGUgPC0gZmlsZS5wYXRoKGdldHdkKCksZGVzdF9nbXRfZmlsZSkKZ3NlYV9yYW5rc19maWxlIDwtIGZpbGUucGF0aChnZXR3ZCgpLHJua19maWxlKQpnc2VhX3Jlc3VsdHNfZmlsZW5hbWUgPC0gZmlsZS5wYXRoKGdldHdkKCksZW5yaWNobWVudF9yZXN1bHRzX2ZpbGVuYW1lKSAKZ3NlYV9leHByZXNzaW9uX2ZpbGVuYW1lIDwtIGZpbGUucGF0aChnZXR3ZCgpLHdvcmtpbmdfZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU3VwcGxlbWVudGFyeV9UYWJsZTZfVENHQV9PVl9STkFzZXFfZXhwcmVzc2lvbi50eHQiKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiNjcmVhdGUgRU0KY3VycmVudF9uZXR3b3JrX25hbWUgPC0gcGFzdGUoY3VyX21vZGVsX25hbWUscHZhbHVlX2dzZWFfdGhyZXNob2xkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdmFsdWVfZ3NlYV90aHJlc2hvbGQsc2VwPSJfIikKCmVtX2NvbW1hbmQgPSBwYXN0ZSgnZW5yaWNobWVudG1hcCBidWlsZCBhbmFseXNpc1R5cGU9ImdzZWEiIGdtdEZpbGU9JyxnbXRfZ3NlYV9maWxlLAogICAgICAgICAgICAgICAgICAgJ3B2YWx1ZT0nLHB2YWx1ZV9nc2VhX3RocmVzaG9sZCwgJ3F2YWx1ZT0nLHF2YWx1ZV9nc2VhX3RocmVzaG9sZCwKICAgICAgICAgICAgICAgICAgICdzaW1pbGFyaXR5Y3V0b2ZmPScsc2ltaWxhcml0eV90aHJlc2hvbGQsCiAgICAgICAgICAgICAgICAgICAnY29lZmZpY2llbnRzPScsc2ltaWxhcml0eV9tZXRyaWMsJ3JhbmtzRGF0YXNldDE9JywgCiAgICAgICAgICAgICAgICAgICBnc2VhX3JhbmtzX2ZpbGUsJ2VucmljaG1lbnRzRGF0YXNldDE9Jyxnc2VhX3Jlc3VsdHNfZmlsZW5hbWUsIAogICAgICAgICAgICAgICAgICAgJ2VucmljaG1lbnRzMkRhdGFzZXQxPScsZ3NlYV9yZXN1bHRzX2ZpbGVuYW1lLCAKICAgICAgICAgICAgICAgICAgICdmaWx0ZXJCeUV4cHJlc3Npb25zPWZhbHNlJywKICAgICAgICAgICAgICAgICAgICdleHByZXNzaW9uRGF0YXNldDE9Jyxnc2VhX2V4cHJlc3Npb25fZmlsZW5hbWUsCiAgICAgICAgICAgICAgICAgICBzZXA9IiAiKQoKI2VucmljaG1lbnQgbWFwIGNvbW1hbmQgd2lsbCByZXR1cm4gdGhlIHN1aWQgb2YgbmV3bHkgY3JlYXRlZCBuZXR3b3JrLgpyZXNwb25zZSA8LSBjb21tYW5kc0dFVChlbV9jb21tYW5kKQoKY3VycmVudF9uZXR3b3JrX3N1aWQgPC0gMAojZW5yaWNobWVudCBtYXAgY29tbWFuZCB3aWxsIHJldHVybiB0aGUgc3VpZCBvZiBuZXdseSBjcmVhdGVkIG5ldHdvcmsgdW5sZXNzIGl0IGZhaWxlZC4gIAojSWYgaXQgZmFpbGVkIGl0IHdpbGwgY29udGFpbiB0aGUgd29yZCBmYWlsZWQKaWYoZ3JlcGwocGF0dGVybj0iRmFpbGVkIiwgcmVzcG9uc2UpKXsKICBwYXN0ZShyZXNwb25zZSkKfSBlbHNlIHsKICBjdXJyZW50X25ldHdvcmtfc3VpZCA8LSByZXNwb25zZQp9CnJlc3BvbnNlIDwtIHJlbmFtZU5ldHdvcmsodGl0bGU9Y3VycmVudF9uZXR3b3JrX25hbWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgIG5ldHdvcmsgPSBhcy5udW1lcmljKGN1cnJlbnRfbmV0d29ya19zdWlkKSkKCgpgYGAKCgojIyBHZXQgYSBzY3JlZW4gc2hvdCBvZiB0aGUgaW5pdGlhbCBuZXR3b3JrLgpgYGB7ciBnZXQgc2NyZWVuc2hvdCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1UUlVFfQpvdXRwdXRfbmV0d29ya19maWxlIDwtIGZpbGUucGF0aChnZXR3ZCgpLCJtYW51YWxfcGhlbm90eXBlX3JhbmRvbWl6YXRpb24ucGRmIikKCmlmKGZpbGUuZXhpc3RzKG91dHB1dF9uZXR3b3JrX2ZpbGUpKXsKICAjY3l0b3NjYXBlIGhhbmdzIHdhaXRpbmcgZm9yIHVzZXIgcmVzcG9uc2UgaWYgZmlsZSBhbHJlYWR5IGV4aXN0cy4gIFJlbW92ZSBpdCBmaXJzdAogIGZpbGUucmVtb3ZlKG91dHB1dF9uZXR3b3JrX2ZpbGUpCiAgfSAKCiNleHBvcnQgdGhlIG5ldHdvcmsKcmVzcG9uc2UgPC0gZXhwb3J0SW1hZ2Uob3V0cHV0X25ldHdvcmtfZmlsZSwgdHlwZSA9ICJwZGYiKQoKYGBgCgoKYGBge3IgaW5pdGlhbG5ldHdvcmssIGVjaG89RkFMU0UgLCBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy5wb3M9IiFodCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGZpbGUucGF0aChnZXR3ZCgpLCJtYW51YWxfcGhlbm90eXBlX3JhbmRvbWl6YXRpb25fZm9ycGRmLnBkZiIpKQpgYGAKCkZpZ3VyZSA5OiBFeGFtcGxlIGVucmljaG1lbnQgbWFwIGNyZWF0ZWQgZnJvbSBtYW51YWwgcGhlbm90eXBlIHJhbmRvbWl6YXRpb24gaW4gUiB1c2luZyBlZGdlUiAoRkRSIHRocmVzaG9sZDwwLjAxKQoKCmBgYHtyLCBldmFsID0gRkFMU0UsZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShrbml0cikKYXNpc19vdXRwdXQoIiMjIFJlZmVyZW5jZXNcXG4iKSAjIEhlYWRlciB0aGF0IGlzIG9ubHkgc2hvd24gaWYgYWRkX3NldHVwID09IFRSVUUKYGBgCg==