1 Materials

1.1 Equipment

1.1.1 Hardware requirements:

  • A personal computer with Internet access and at least 8GB of RAM.

1.1.2 Software requirements:

  • A contemporary web browser (e.g. Chrome, Firefox), for pathway enrichment analysis with g:Profiler (Protocol 1A).
  • Java Standard Edition. Java is required to run GSEA and Cytoscape. It is available at http://java.oracle.com. Version 8 or higher is requiredrecommended, but Java 7 will function.
  • GSEA desktop application for pathway enrichment analysis protocol 1B. Download the latest version of GSEA from http://www.broadinstitute.org/gsea/downloads.jsp. We recommend the javaGSEA desktop application. Free registration is required.
  • Cytoscape desktop application is required for EnrichmentMap visualization. The latest version of Cytoscape can be downloaded at http://www.cytoscape.org.
  • The following Cytoscape apps are installed within Cytoscape.
  • EnrichmentMap, version 3.1 or higher,
  • Clustermaker2, version 0.9.5 or higher,
  • WordCloud, version 3.1.0 or higher,
  • AutoAnnotate, version 1.2.0 or higher

2 Create EnrichmentMap - Automaticallly from R using cyRest

2.1 Load in required libraries

#install required R and bioconductor packages
tryCatch(expr = { library("RCurl")}, 
         error = function(e) {  install.packages("RCurl")}, 
         finally = library("RCurl"))
package ‘RCurl’ was built under R version 3.6.2
#use library
#make sure biocManager is installed
tryCatch(expr = { library("BiocManager")}, 
         error = function(e) { 
           install.packages("BiocManager")}, 
         finally = library("BiocManager"))
tryCatch(expr = { library("ggplot2")}, 
         error = function(e) { install.packages("ggplot2")}, 
         finally = library("ggplot2"))
#use easy cyRest library to communicate with cytoscape.
tryCatch(expr = { library("RCy3")}, 
         error = function(e) { BiocManager::install("RCy3")}, 
         finally = library("RCy3"))

2.2 Configurable Parameters

In order to run GSEA automatically through the notebook you will need to download the gsea jar from here. Specify the exact path to the gsea jar in the parameters in order to automatically compute enrichments using GSEA.

#path to GSEA jar 
# In order to run GSEA automatically you need to speciry the path to the gsea jar file.
#With the latest release of gsea (4.0.2) they no longer release a bundled jar
# and instead release a scriptted way to launch the gsea client.
# specify the java version as 11 if you are using the later version gsea
# the gsea_jar also needs to be the full path to the GSEA 4.0.2 directory that you
# downloaded from GSEA. for example (/Users/johnsmith/GSEA_4.0.2/gsea-cli.sh)
gsea_jar <- params$gsea_jar
java_version <- params$java_version
#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 = params$run_gsea
#navigate to the directory where you put the downloaded protocol files.
working_dir <- params$working_dir
# 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 = params$gsea_directory
analysis_name <- params$analysis_name
rnk_file <- params$rnk_file
expression_file <- params$expression_file
classes_file <- params$classes_file

2.3 Download the latest pathway definition file

Only Human, Mouse and Rat gene set files are currently available on the baderlab downloads site. If you are working with a species other than human (and it is either rat or mouse) change the gmt_url below to correct species. Check here to see all available species.


2.4 Run GSEA

(GSEA)[http://software.broadinstitute.org/gsea/index.jsp] is a stand alone java program with many customizable options. It can be easily run through its integrated user interface. To make this a seemless pipeline we can run GSEA from the command line with a set of options. Any of the supplied options can be customized and there are many additional options that can be specified. For more details see (here)[http://software.broadinstitute.org/gsea/doc/GSEAUserGuideTEXT.htm#_Running_GSEA_from]

In the below command the following options have been specified:

  • rnk - path to the rank file
  • gmx - path to the gene set definition (gmt) file
  • collapse - true/false indicates whether the expression/rnk file needs to be collapsed from probes to gene symbols
  • nperm - number of permutations
  • permute - permute gene sets or phentoypes. For GSEA preranked you can only permute genesets.
  • scoring_scheme -
  • rpt_label - name of the directory with output
  • num - number of results to plot output file for
  • rnd_seed - random seed to use
  • set_max - maximum size for individual gene sets. In GSEA interface this is set to 500 but we prefer to use a more stringent setting of 200.
  • set_min - minimum size for individual gene sets
  • zip_report - true/false to zip output directory
  • out - directory where to place the result directory.
  • gui - true/false. When running GSEA from the commandline this needs to be false.

If you are using GSEA 4.0.2 you will need to update your system to use Java 11. The GSEA command line interface has changed slightly in this version of GSEA. Instead of launching GSEA using java you need to use one of the scripts supplied by GSEA. (gsea-cli.bat for Windows and gsea-cli.sh for Mac or linux systems).

In the GSEA 4.0.2 command the following options can been specified:

  • rnk - path to the rank file
  • gmx - path to the gene set definition (gmt) file
  • collapse - true/false indicates whether the expression/rnk file needs to be collapsed from probes to gene symbols
  • nperm - number of permutations
  • scoring_scheme -
  • rpt_label - name of the directory with output
  • rnd_seed - random seed to use
  • set_max - maximum size for individual gene sets. In GSEA interface this is set to 500 but we prefer to use a more stringent setting of 200.
  • set_min - minimum size for individual gene sets
  • zip_report - true/false to zip output directory
  • out - directory where to place the result directory.

If you have encounter an out of memory error you will need to open you gsea-cli script and manually update your xmx parameter (more information on this can be found in the GSEA_4.0.2/README file)

#if you are using GSEA 4.0.2 then you need to use the command script
# as opposed to launching GSEA through java. 
# in the later version of GSEA command line implementation the following 
# parameters are no longer valid: -permute gene_set,  -num 100, -gui false
# no longer need to specify the whole path to the GseaPreranked package
if(run_gsea && java_version == "11"){
  command <- paste("",gsea_jar,  "GSEAPreRanked -gmx", dest_gmt_file, "-rnk" ,file.path(working_dir,rnk_file), "-collapse false -nperm 1000 -scoring_scheme weighted -rpt_label ",analysis_name,"  -plot_top_x 20 -rnd_seed 12345  -set_max 200 -set_min 15 -zip_report false -out" ,working_dir, " > gsea_output.txt",sep=" ")
  system(command)
} else if (run_gsea) {
  command <- paste("java  -Xmx1G -cp",gsea_jar,  "xtools.gsea.GseaPreranked -gmx", dest_gmt_file, "-rnk" ,file.path(working_dir,rnk_file), "-collapse false -nperm 1000 -permute gene_set -scoring_scheme weighted -rpt_label ",analysis_name,"  -num 100 -plot_top_x 20 -rnd_seed 12345  -set_max 200 -set_min 15 -zip_report false -out" ,working_dir, "-gui false > gsea_output.txt",sep=" ")
  system(command)
}
WARNING: package com.sun.java.swing.plaf.windows not in java.desktop
WARNING: package sun.awt.windows not in java.desktop

2.5 Get the name of the GSEA output directory

Although GSEA allows you to specify the name of the output directory and the destination folder it add additional words and numbers to the folder name. Some are predictable and some are automatically generated. Get all the GSEA results directories found in the current directory. If there are multiple GSEA results folders each will be used to create an enrichment map.

if(gsea_directory == ""){
  gsea_directories <- list.files(path = working_dir, pattern = "\\.GseaPreranked")
  #get the details on the files
  details = file.info(file.path(getwd(),working_dir,gsea_directories))
  #order according to newest to oldest
  details = details[with(details, order(as.POSIXct(mtime),decreasing = TRUE)), ]
  #use the newest file:
  gsea_output_dir <- row.names(details)[1]
} else {
  gsea_output_dir <- gsea_directory
}

2.6 Launch 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). Make sure if you get an message asking you if you want communicate with other apps that you select "Allow".

2.7 Make sure you can connect to Cytoscape

    cytoscapePing ()
[1] "You are connected to Cytoscape!"
    cytoscapeVersionInfo ()
      apiVersion cytoscapeVersion 
            "v1"          "3.8.0" 

2.8 Create an Enrichment map

#defined threshold for GSEA enrichments (need to be strings for cyrest call)
pvalue_gsea_threshold <- params$pval_thresh
qvalue_gsea_threshold <- params$fdr_thresh
similarity_threshold <- "0.375"
similarity_metric = "COMBINED"
cur_model_name <- analysis_name
gsea_results_path <- file.path(gsea_output_dir,"edb")
gsea_results_filename <- file.path(gsea_results_path,"results.edb")
#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(gsea_results_path,list.files(gsea_results_path,pattern=".rnk"))
#######################################
#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, 
                   'filterByExpressions=false',
                   'expressionDataset1=',file.path(getwd(),working_dir,expression_file),
                   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
}
#check to see if the network name is unique
current_names <- getNetworkList()
if(current_network_name %in% current_names){
  #if the name already exists in the network names then put the SUID in front
  # of the name (this does not work if you put the suid at the end of the name)
  current_network_name <- paste(current_network_suid,current_network_name,  sep="_")
}
response <- renameNetwork(title=current_network_name, 
                          network = as.numeric(current_network_suid),base.url)

2.9 Get a screen shot of the initial network.

fitContent()
output_network_file <- file.path(getwd(),"initial_screenshot_network.png")
if(file.exists(output_network_file)){
  #cytoscape hangs waiting for user response if file already exists.  Remove it first
  response <- file.remove(output_network_file)
} 
response <- exportImage(output_network_file, type = "png")
htmltools::img(src = knitr::image_uri(output_network_file), 
               alt = 'Initial Enrichment Map', 
               style = 'margin:0px auto;display:block')
Initial Enrichment Map
LS0tCnRpdGxlOiAiRW5yaWNobWVudCBNYXAgQW5hbHlzaXMgUGlwZWxpbmUiCmF1dGhvcjogIlJ1dGggSXNzZXJsaW4iCmRhdGU6ICdgciBmb3JtYXQoU3lzLkRhdGUoKSlgJwpvdXRwdXQ6CiAgcGRmX2RvY3VtZW50OgogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4CiAgICB0b2M6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICBoaWdobGlnaDogaGFkZG9jawogICAga2VlcF9tZDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHBhcGVyCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IG5vCiAgICAgIHNtb290aF9zY3JvbGw6IG5vCiAgaHRtbF9ub3RlYm9vazoKICAgIGhpZ2hsaWdoOiBoYWRkb2NrCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHBhcGVyCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogbm8KICAgICAgc21vb3RoX3Njcm9sbDogbm8KcGFyYW1zOgogIGFuYWx5c2lzX25hbWU6IE1lc2VuX3ZzX0ltbXVubwogIHdvcmtpbmdfZGlyOiAuL2RhdGEvCiAgcm5rX2ZpbGU6IFN1cHBsZW1lbnRhcnlfVGFibGUyX01lc2VudnNJbW11bm9fUk5BU2VxX3JhbmtzLnJuawogIGNsYXNzZXNfZmlsZTogU3VwcGxlbWVudGFyeV9UYWJsZTlfVENHQV9PVl9STkFzZXFfY2xhc3Nlcy5jbHMKICBleHByZXNzaW9uX2ZpbGU6IFN1cHBsZW1lbnRhcnlfVGFibGU2X1RDR0FfT1ZfUk5Bc2VxX2V4cHJlc3Npb24udHh0CiAgcnVuX2dzZWE6IHllcwogIGdzZWFfamFyOiAvVXNlcnMvcnV0aGlzc2VybGluL0Rvd25sb2Fkcy9HU0VBXzQuMC4zL2dzZWEtY2xpLnNoCiAgZ3NlYV9kaXJlY3Rvcnk6ICcnCiAgZmRyX3RocmVzaDogMC4wMQogIHB2YWxfdGhyZXNoOiAxCiAgamF2YV92ZXJzaW9uOiAxMQotLS0KCiMgTWF0ZXJpYWxzCiMjIEVxdWlwbWVudAojIyMgSGFyZHdhcmUgcmVxdWlyZW1lbnRzOgoKICogQSBwZXJzb25hbCBjb21wdXRlciB3aXRoIEludGVybmV0IGFjY2VzcyBhbmQgYXQgbGVhc3QgOEdCIG9mIFJBTS4KIAojIyMgU29mdHdhcmUgcmVxdWlyZW1lbnRzOgoKICogQSBjb250ZW1wb3Jhcnkgd2ViIGJyb3dzZXIgKGUuZy4gQ2hyb21lLCBGaXJlZm94KSwgZm9yIHBhdGh3YXkgZW5yaWNobWVudCBhbmFseXNpcyB3aXRoIGc6UHJvZmlsZXIgKFByb3RvY29sIDFBKS4KICogSmF2YSBTdGFuZGFyZCBFZGl0aW9uLiBKYXZhIGlzIHJlcXVpcmVkIHRvIHJ1biBHU0VBIGFuZCBDeXRvc2NhcGUuIEl0IGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vamF2YS5vcmFjbGUuY29tLiBWZXJzaW9uIDggb3IgaGlnaGVyIGlzIHJlcXVpcmVkcmVjb21tZW5kZWQsIGJ1dCBKYXZhIDcgd2lsbCBmdW5jdGlvbi4KICogR1NFQSBkZXNrdG9wIGFwcGxpY2F0aW9uIGZvciBwYXRod2F5IGVucmljaG1lbnQgYW5hbHlzaXMgcHJvdG9jb2wgMUIuIERvd25sb2FkIHRoZSBsYXRlc3QgdmVyc2lvbiBvZiBHU0VBIGZyb20gaHR0cDovL3d3dy5icm9hZGluc3RpdHV0ZS5vcmcvZ3NlYS9kb3dubG9hZHMuanNwLiBXZSByZWNvbW1lbmQgdGhlIGphdmFHU0VBIGRlc2t0b3AgYXBwbGljYXRpb24uIEZyZWUgcmVnaXN0cmF0aW9uIGlzIHJlcXVpcmVkLgogKiBDeXRvc2NhcGUgZGVza3RvcCBhcHBsaWNhdGlvbiBpcyByZXF1aXJlZCBmb3IgRW5yaWNobWVudE1hcCB2aXN1YWxpemF0aW9uLiBUaGUgbGF0ZXN0IHZlcnNpb24gb2YgQ3l0b3NjYXBlIGNhbiBiZSBkb3dubG9hZGVkIGF0IGh0dHA6Ly93d3cuY3l0b3NjYXBlLm9yZy4KICogVGhlIGZvbGxvd2luZyBDeXRvc2NhcGUgYXBwcyBhcmUgaW5zdGFsbGVkIHdpdGhpbiBDeXRvc2NhcGUuCiAgICogRW5yaWNobWVudE1hcCwgdmVyc2lvbiAzLjEgb3IgaGlnaGVyLAogICAqIENsdXN0ZXJtYWtlcjIsIHZlcnNpb24gMC45LjUgb3IgaGlnaGVyLAogICAqIFdvcmRDbG91ZCwgdmVyc2lvbiAzLjEuMCBvciBoaWdoZXIsCiAgICogQXV0b0Fubm90YXRlLCB2ZXJzaW9uIDEuMi4wIG9yIGhpZ2hlcgoKIyBDcmVhdGUgRW5yaWNobWVudE1hcCAtIEF1dG9tYXRpY2FsbGx5IGZyb20gUiB1c2luZyBjeVJlc3QKIyMgTG9hZCBpbiByZXF1aXJlZCBsaWJyYXJpZXMKCmBgYHtyIGxvYWQgbGlicmFyaWVzfQojaW5zdGFsbCByZXF1aXJlZCBSIGFuZCBiaW9jb25kdWN0b3IgcGFja2FnZXMKdHJ5Q2F0Y2goZXhwciA9IHsgbGlicmFyeSgiUkN1cmwiKX0sIAogICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIHsgIGluc3RhbGwucGFja2FnZXMoIlJDdXJsIil9LCAKICAgICAgICAgZmluYWxseSA9IGxpYnJhcnkoIlJDdXJsIikpCgojdXNlIGxpYnJhcnkKI21ha2Ugc3VyZSBiaW9jTWFuYWdlciBpcyBpbnN0YWxsZWQKdHJ5Q2F0Y2goZXhwciA9IHsgbGlicmFyeSgiQmlvY01hbmFnZXIiKX0sIAogICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIHsgCiAgICAgICAgICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKX0sIAogICAgICAgICBmaW5hbGx5ID0gbGlicmFyeSgiQmlvY01hbmFnZXIiKSkKCnRyeUNhdGNoKGV4cHIgPSB7IGxpYnJhcnkoImdncGxvdDIiKX0sIAogICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIHsgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpfSwgCiAgICAgICAgIGZpbmFsbHkgPSBsaWJyYXJ5KCJnZ3Bsb3QyIikpCgojdXNlIGVhc3kgY3lSZXN0IGxpYnJhcnkgdG8gY29tbXVuaWNhdGUgd2l0aCBjeXRvc2NhcGUuCnRyeUNhdGNoKGV4cHIgPSB7IGxpYnJhcnkoIlJDeTMiKX0sIAogICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIHsgQmlvY01hbmFnZXI6Omluc3RhbGwoIlJDeTMiKX0sIAogICAgICAgICBmaW5hbGx5ID0gbGlicmFyeSgiUkN5MyIpKQoKCmBgYAoqKioKCiMjIENvbmZpZ3VyYWJsZSBQYXJhbWV0ZXJzCkluIG9yZGVyIHRvIHJ1biBHU0VBIGF1dG9tYXRpY2FsbHkgdGhyb3VnaCB0aGUgbm90ZWJvb2sgeW91IHdpbGwgbmVlZCB0byBkb3dubG9hZCB0aGUgZ3NlYSBqYXIgZnJvbSBbaGVyZV0oaHR0cDovL3NvZnR3YXJlLmJyb2FkaW5zdGl0dXRlLm9yZy9nc2VhL2Rvd25sb2Fkcy5qc3ApLiAgU3BlY2lmeSB0aGUgZXhhY3QgcGF0aCB0byB0aGUgZ3NlYSBqYXIgaW4gdGhlIHBhcmFtZXRlcnMgaW4gb3JkZXIgdG8gYXV0b21hdGljYWxseSBjb21wdXRlIGVucmljaG1lbnRzIHVzaW5nIEdTRUEuCgpgYGB7ciBpbml0aWFsaXplIHBhcmFtZXRlcnN9CiNwYXRoIHRvIEdTRUEgamFyIAojIEluIG9yZGVyIHRvIHJ1biBHU0VBIGF1dG9tYXRpY2FsbHkgeW91IG5lZWQgdG8gc3BlY2lyeSB0aGUgcGF0aCB0byB0aGUgZ3NlYSBqYXIgZmlsZS4KI1dpdGggdGhlIGxhdGVzdCByZWxlYXNlIG9mIGdzZWEgKDQuMC4yKSB0aGV5IG5vIGxvbmdlciByZWxlYXNlIGEgYnVuZGxlZCBqYXIKIyBhbmQgaW5zdGVhZCByZWxlYXNlIGEgc2NyaXB0dGVkIHdheSB0byBsYXVuY2ggdGhlIGdzZWEgY2xpZW50LgojIHNwZWNpZnkgdGhlIGphdmEgdmVyc2lvbiBhcyAxMSBpZiB5b3UgYXJlIHVzaW5nIHRoZSBsYXRlciB2ZXJzaW9uIGdzZWEKIyB0aGUgZ3NlYV9qYXIgYWxzbyBuZWVkcyB0byBiZSB0aGUgZnVsbCBwYXRoIHRvIHRoZSBHU0VBIDQuMC4yIGRpcmVjdG9yeSB0aGF0IHlvdQojIGRvd25sb2FkZWQgZnJvbSBHU0VBLiBmb3IgZXhhbXBsZSAoL1VzZXJzL2pvaG5zbWl0aC9HU0VBXzQuMC4yL2dzZWEtY2xpLnNoKQpnc2VhX2phciA8LSBwYXJhbXMkZ3NlYV9qYXIKamF2YV92ZXJzaW9uIDwtIHBhcmFtcyRqYXZhX3ZlcnNpb24KCiNHc2VhIHRha2VzIGEgbG9uZyB0aW1lIHRvIHJ1bi4gIElmIHlvdSBoYXZlIGFscmVhZHkgcnVuIEdTRUEgbWFudWFsbHkgb3IgcHJldmlvdXNseSB0aGVyZSBpcyBubyBuZWVkIHRvIHJlLXJ1biBHU0VBLiAgTWFrZSBzdXJlIHRoZSAKIyBnc2VhIHJlc3VsdHMgYXJlIGluIHRoZSBjdXJyZW50IGRpcmVjdG9yeSBhbmQgdGhlIG5vdGVib29rIHdpbGwgYmUgYWJsZSB0byBmaW5kIHRoZW0gYW5kIHVzZSB0aGVtLgpydW5fZ3NlYSA9IHBhcmFtcyRydW5fZ3NlYQoKI25hdmlnYXRlIHRvIHRoZSBkaXJlY3Rvcnkgd2hlcmUgeW91IHB1dCB0aGUgZG93bmxvYWRlZCBwcm90b2NvbCBmaWxlcy4Kd29ya2luZ19kaXIgPC0gcGFyYW1zJHdvcmtpbmdfZGlyCgojIGxlYXZlIGJsYW5rIGlmIHlvdSB3YW50IHRoZSBub3RlYm9vayB0byBkaXNjb3ZlciB0aGUgZ3NlYSBkaXJlY3RvcnkgZm9yIGl0c2VsZgojZ3NlYV9kaXJlY3RvcnkgPSBwYXN0ZSh3b3JraW5nX2RpciwiTWVzZW5fdnNfSW1tdW5vLkdzZWFQcmVyYW5rZWQuMTQ5NzYzNTQ1OTI2MiIsc2VwPSIvIikgCmdzZWFfZGlyZWN0b3J5ID0gcGFyYW1zJGdzZWFfZGlyZWN0b3J5CgphbmFseXNpc19uYW1lIDwtIHBhcmFtcyRhbmFseXNpc19uYW1lCnJua19maWxlIDwtIHBhcmFtcyRybmtfZmlsZQpleHByZXNzaW9uX2ZpbGUgPC0gcGFyYW1zJGV4cHJlc3Npb25fZmlsZQpjbGFzc2VzX2ZpbGUgPC0gcGFyYW1zJGNsYXNzZXNfZmlsZQoKYGBgCgojIyBEb3dubG9hZCB0aGUgbGF0ZXN0IHBhdGh3YXkgZGVmaW5pdGlvbiBmaWxlCk9ubHkgSHVtYW4sIE1vdXNlIGFuZCBSYXQgZ2VuZSBzZXQgZmlsZXMgYXJlIGN1cnJlbnRseSBhdmFpbGFibGUgb24gdGhlIGJhZGVybGFiIGRvd25sb2FkcyBzaXRlLiAgSWYgeW91IGFyZSB3b3JraW5nIHdpdGggYSBzcGVjaWVzIG90aGVyIHRoYW4gaHVtYW4gKGFuZCBpdCBpcyBlaXRoZXIgcmF0IG9yIG1vdXNlKSBjaGFuZ2UgdGhlIGdtdF91cmwgYmVsb3cgdG8gY29ycmVjdCBzcGVjaWVzLiBDaGVjayBbaGVyZV0oaHR0cDovL2Rvd25sb2FkLmJhZGVybGFiLm9yZy9FTV9HZW5lc2V0cy9jdXJyZW50X3JlbGVhc2UvKSB0byBzZWUgYWxsIGF2YWlsYWJsZSBzcGVjaWVzLiAKCmBgYHtyIGRvd25sb2FkIGJhZGVybGFiIGdtdCBmaWxlLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpnbXRfdXJsID0gImh0dHA6Ly9kb3dubG9hZC5iYWRlcmxhYi5vcmcvRU1fR2VuZXNldHMvY3VycmVudF9yZWxlYXNlL0h1bWFuL3N5bWJvbC8iCgojbGlzdCBhbGwgdGhlIGZpbGVzIG9uIHRoZSBzZXJ2ZXIKZmlsZW5hbWVzID0gZ2V0VVJMKGdtdF91cmwpCnRjID0gdGV4dENvbm5lY3Rpb24oZmlsZW5hbWVzKQpjb250ZW50cyA9IHJlYWRMaW5lcyh0YykKY2xvc2UodGMpCgojZ2V0IHRoZSBnbXQgdGhhdCBoYXMgYWxsIHRoZSBwYXRod2F5cyBhbmQgZG9lcyBub3QgaW5jbHVkZSB0ZXJtcyBpbmZlcnJlZCBmcm9tIGVsZWN0cm9uaWMgYW5ub3RhdGlvbnMoSUVBKQojc3RhcnQgd2l0aCBnbXQgZmlsZSB0aGF0IGhhcyBwYXRod2F5cyBvbmx5CnJ4ID0gZ3JlZ2V4cHIoIig/PD08YSBocmVmPVwiKSguKi5HT0JQX0FsbFBhdGh3YXlzX25vX0dPX2llYS4qLikoLmdtdCkoPz1cIj4pIiwKICBjb250ZW50cywgcGVybCA9IFRSVUUpCmdtdF9maWxlID0gdW5saXN0KHJlZ21hdGNoZXMoY29udGVudHMsIHJ4KSkKCmRlc3RfZ210X2ZpbGUgPC0gZmlsZS5wYXRoKHdvcmtpbmdfZGlyLHBhc3RlKCJTdXBwbGVtZW50YXJ5X1RhYmxlM18iLGdtdF9maWxlLHNlcD0iIikgKQoKZG93bmxvYWQuZmlsZSgKICAgIHBhc3RlKGdtdF91cmwsZ210X2ZpbGUsc2VwPSIiKSwKICAgIGRlc3RmaWxlPWRlc3RfZ210X2ZpbGUKKQoKCmBgYAoKKioqCiMjIFJ1biBHU0VBCihHU0VBKVtodHRwOi8vc29mdHdhcmUuYnJvYWRpbnN0aXR1dGUub3JnL2dzZWEvaW5kZXguanNwXSBpcyBhIHN0YW5kIGFsb25lIGphdmEgcHJvZ3JhbSB3aXRoIG1hbnkgY3VzdG9taXphYmxlIG9wdGlvbnMuICBJdCBjYW4gYmUgZWFzaWx5IHJ1biB0aHJvdWdoIGl0cyBpbnRlZ3JhdGVkIHVzZXIgaW50ZXJmYWNlLiAgVG8gbWFrZSB0aGlzIGEgc2VlbWxlc3MgcGlwZWxpbmUgd2UgY2FuIHJ1biBHU0VBIGZyb20gdGhlIGNvbW1hbmQgbGluZSB3aXRoIGEgc2V0IG9mIG9wdGlvbnMuICBBbnkgb2YgdGhlIHN1cHBsaWVkIG9wdGlvbnMgY2FuIGJlIGN1c3RvbWl6ZWQgYW5kIHRoZXJlIGFyZSBtYW55IGFkZGl0aW9uYWwgb3B0aW9ucyB0aGF0IGNhbiBiZSBzcGVjaWZpZWQuICBGb3IgbW9yZSBkZXRhaWxzIHNlZSAoaGVyZSlbaHR0cDovL3NvZnR3YXJlLmJyb2FkaW5zdGl0dXRlLm9yZy9nc2VhL2RvYy9HU0VBVXNlckd1aWRlVEVYVC5odG0jX1J1bm5pbmdfR1NFQV9mcm9tXQoKSW4gdGhlIGJlbG93IGNvbW1hbmQgdGhlIGZvbGxvd2luZyBvcHRpb25zIGhhdmUgYmVlbiBzcGVjaWZpZWQ6CgogKiBybmsgLSBwYXRoIHRvIHRoZSByYW5rIGZpbGUKICogZ214IC0gcGF0aCB0byB0aGUgZ2VuZSBzZXQgZGVmaW5pdGlvbiAoZ210KSBmaWxlCiAqIGNvbGxhcHNlIC0gdHJ1ZS9mYWxzZSBpbmRpY2F0ZXMgd2hldGhlciB0aGUgZXhwcmVzc2lvbi9ybmsgZmlsZSBuZWVkcyB0byBiZSBjb2xsYXBzZWQgZnJvbSBwcm9iZXMgdG8gZ2VuZSBzeW1ib2xzCiAqIG5wZXJtIC0gbnVtYmVyIG9mIHBlcm11dGF0aW9ucwogKiBwZXJtdXRlIC0gcGVybXV0ZSBnZW5lIHNldHMgb3IgcGhlbnRveXBlcy4gIEZvciBHU0VBIHByZXJhbmtlZCB5b3UgY2FuIG9ubHkgcGVybXV0ZSBnZW5lc2V0cy4KICogc2NvcmluZ19zY2hlbWUgLSAKICogcnB0X2xhYmVsIC0gbmFtZSBvZiB0aGUgZGlyZWN0b3J5IHdpdGggb3V0cHV0CiAqIG51bSAtIG51bWJlciBvZiByZXN1bHRzIHRvIHBsb3Qgb3V0cHV0IGZpbGUgZm9yCiAqIHJuZF9zZWVkIC0gcmFuZG9tIHNlZWQgdG8gdXNlCiAqIHNldF9tYXggLSBtYXhpbXVtIHNpemUgZm9yIGluZGl2aWR1YWwgZ2VuZSBzZXRzLiAgSW4gR1NFQSBpbnRlcmZhY2UgdGhpcyBpcyBzZXQgdG8gNTAwIGJ1dCB3ZSBwcmVmZXIgdG8gdXNlIGEgbW9yZSBzdHJpbmdlbnQgc2V0dGluZyBvZiAyMDAuIAogKiBzZXRfbWluIC0gbWluaW11bSBzaXplIGZvciBpbmRpdmlkdWFsIGdlbmUgc2V0cyAKICogemlwX3JlcG9ydCAtIHRydWUvZmFsc2UgdG8gemlwIG91dHB1dCBkaXJlY3RvcnkKICogb3V0IC0gZGlyZWN0b3J5IHdoZXJlIHRvIHBsYWNlIHRoZSByZXN1bHQgZGlyZWN0b3J5LgogKiBndWkgLSB0cnVlL2ZhbHNlLiBXaGVuIHJ1bm5pbmcgR1NFQSBmcm9tIHRoZSBjb21tYW5kbGluZSB0aGlzIG5lZWRzIHRvIGJlIGZhbHNlLgoKSWYgeW91IGFyZSB1c2luZyBHU0VBIDQuMC4yIHlvdSB3aWxsIG5lZWQgdG8gdXBkYXRlIHlvdXIgc3lzdGVtIHRvIHVzZSBKYXZhIDExLiAgVGhlIEdTRUEgY29tbWFuZCBsaW5lIGludGVyZmFjZSBoYXMgY2hhbmdlZCBzbGlnaHRseSBpbiB0aGlzIHZlcnNpb24gb2YgR1NFQS4gIEluc3RlYWQgb2YgbGF1bmNoaW5nIEdTRUEgdXNpbmcgamF2YSB5b3UgbmVlZCB0byB1c2Ugb25lIG9mIHRoZSBzY3JpcHRzIHN1cHBsaWVkIGJ5IEdTRUEuICAoZ3NlYS1jbGkuYmF0IGZvciBXaW5kb3dzIGFuZCBnc2VhLWNsaS5zaCBmb3IgTWFjIG9yIGxpbnV4IHN5c3RlbXMpLiAgCgpJbiB0aGUgR1NFQSA0LjAuMiBjb21tYW5kIHRoZSBmb2xsb3dpbmcgb3B0aW9ucyBjYW4gYmVlbiBzcGVjaWZpZWQ6CgogKiBybmsgLSBwYXRoIHRvIHRoZSByYW5rIGZpbGUKICogZ214IC0gcGF0aCB0byB0aGUgZ2VuZSBzZXQgZGVmaW5pdGlvbiAoZ210KSBmaWxlCiAqIGNvbGxhcHNlIC0gdHJ1ZS9mYWxzZSBpbmRpY2F0ZXMgd2hldGhlciB0aGUgZXhwcmVzc2lvbi9ybmsgZmlsZSBuZWVkcyB0byBiZSBjb2xsYXBzZWQgZnJvbSBwcm9iZXMgdG8gZ2VuZSBzeW1ib2xzCiAqIG5wZXJtIC0gbnVtYmVyIG9mIHBlcm11dGF0aW9ucwogKiBzY29yaW5nX3NjaGVtZSAtIAogKiBycHRfbGFiZWwgLSBuYW1lIG9mIHRoZSBkaXJlY3Rvcnkgd2l0aCBvdXRwdXQKICogcm5kX3NlZWQgLSByYW5kb20gc2VlZCB0byB1c2UKICogc2V0X21heCAtIG1heGltdW0gc2l6ZSBmb3IgaW5kaXZpZHVhbCBnZW5lIHNldHMuICBJbiBHU0VBIGludGVyZmFjZSB0aGlzIGlzIHNldCB0byA1MDAgYnV0IHdlIHByZWZlciB0byB1c2UgYSBtb3JlIHN0cmluZ2VudCBzZXR0aW5nIG9mIDIwMC4gCiAqIHNldF9taW4gLSBtaW5pbXVtIHNpemUgZm9yIGluZGl2aWR1YWwgZ2VuZSBzZXRzIAogKiB6aXBfcmVwb3J0IC0gdHJ1ZS9mYWxzZSB0byB6aXAgb3V0cHV0IGRpcmVjdG9yeQogKiBvdXQgLSBkaXJlY3Rvcnkgd2hlcmUgdG8gcGxhY2UgdGhlIHJlc3VsdCBkaXJlY3RvcnkuCgpJZiB5b3UgaGF2ZSBlbmNvdW50ZXIgYW4gb3V0IG9mIG1lbW9yeSBlcnJvciB5b3Ugd2lsbCBuZWVkIHRvIG9wZW4geW91IGdzZWEtY2xpIHNjcmlwdCBhbmQgbWFudWFsbHkgdXBkYXRlIHlvdXIgeG14IHBhcmFtZXRlciAobW9yZSBpbmZvcm1hdGlvbiBvbiB0aGlzIGNhbiBiZSBmb3VuZCBpbiB0aGUgR1NFQV80LjAuMi9SRUFETUUgZmlsZSkKIApgYGB7ciBydW4gR1NFQX0KI2lmIHlvdSBhcmUgdXNpbmcgR1NFQSA0LjAuMiB0aGVuIHlvdSBuZWVkIHRvIHVzZSB0aGUgY29tbWFuZCBzY3JpcHQKIyBhcyBvcHBvc2VkIHRvIGxhdW5jaGluZyBHU0VBIHRocm91Z2ggamF2YS4gCiMgaW4gdGhlIGxhdGVyIHZlcnNpb24gb2YgR1NFQSBjb21tYW5kIGxpbmUgaW1wbGVtZW50YXRpb24gdGhlIGZvbGxvd2luZyAKIyBwYXJhbWV0ZXJzIGFyZSBubyBsb25nZXIgdmFsaWQ6IC1wZXJtdXRlIGdlbmVfc2V0LCAgLW51bSAxMDAsIC1ndWkgZmFsc2UKIyBubyBsb25nZXIgbmVlZCB0byBzcGVjaWZ5IHRoZSB3aG9sZSBwYXRoIHRvIHRoZSBHc2VhUHJlcmFua2VkIHBhY2thZ2UKaWYocnVuX2dzZWEgJiYgamF2YV92ZXJzaW9uID09ICIxMSIpewogIGNvbW1hbmQgPC0gcGFzdGUoIiIsZ3NlYV9qYXIsICAiR1NFQVByZVJhbmtlZCAtZ214IiwgZGVzdF9nbXRfZmlsZSwgIi1ybmsiICxmaWxlLnBhdGgod29ya2luZ19kaXIscm5rX2ZpbGUpLCAiLWNvbGxhcHNlIGZhbHNlIC1ucGVybSAxMDAwIC1zY29yaW5nX3NjaGVtZSB3ZWlnaHRlZCAtcnB0X2xhYmVsICIsYW5hbHlzaXNfbmFtZSwiICAtcGxvdF90b3BfeCAyMCAtcm5kX3NlZWQgMTIzNDUgIC1zZXRfbWF4IDIwMCAtc2V0X21pbiAxNSAtemlwX3JlcG9ydCBmYWxzZSAtb3V0IiAsd29ya2luZ19kaXIsICIgPiBnc2VhX291dHB1dC50eHQiLHNlcD0iICIpCiAgc3lzdGVtKGNvbW1hbmQpCn0gZWxzZSBpZiAocnVuX2dzZWEpIHsKICBjb21tYW5kIDwtIHBhc3RlKCJqYXZhICAtWG14MUcgLWNwIixnc2VhX2phciwgICJ4dG9vbHMuZ3NlYS5Hc2VhUHJlcmFua2VkIC1nbXgiLCBkZXN0X2dtdF9maWxlLCAiLXJuayIgLGZpbGUucGF0aCh3b3JraW5nX2RpcixybmtfZmlsZSksICItY29sbGFwc2UgZmFsc2UgLW5wZXJtIDEwMDAgLXBlcm11dGUgZ2VuZV9zZXQgLXNjb3Jpbmdfc2NoZW1lIHdlaWdodGVkIC1ycHRfbGFiZWwgIixhbmFseXNpc19uYW1lLCIgIC1udW0gMTAwIC1wbG90X3RvcF94IDIwIC1ybmRfc2VlZCAxMjM0NSAgLXNldF9tYXggMjAwIC1zZXRfbWluIDE1IC16aXBfcmVwb3J0IGZhbHNlIC1vdXQiICx3b3JraW5nX2RpciwgIi1ndWkgZmFsc2UgPiBnc2VhX291dHB1dC50eHQiLHNlcD0iICIpCiAgc3lzdGVtKGNvbW1hbmQpCn0KYGBgCgojIyBHZXQgdGhlIG5hbWUgb2YgdGhlIEdTRUEgb3V0cHV0IGRpcmVjdG9yeQpBbHRob3VnaCBHU0VBIGFsbG93cyB5b3UgdG8gc3BlY2lmeSB0aGUgbmFtZSBvZiB0aGUgb3V0cHV0IGRpcmVjdG9yeSBhbmQgdGhlIGRlc3RpbmF0aW9uIGZvbGRlciBpdCBhZGQgYWRkaXRpb25hbCB3b3JkcyBhbmQgbnVtYmVycyB0byB0aGUgZm9sZGVyIG5hbWUuICBTb21lIGFyZSBwcmVkaWN0YWJsZSBhbmQgc29tZSBhcmUgYXV0b21hdGljYWxseSBnZW5lcmF0ZWQuICBHZXQgYWxsIHRoZSBHU0VBIHJlc3VsdHMgZGlyZWN0b3JpZXMgZm91bmQgaW4gdGhlIGN1cnJlbnQgZGlyZWN0b3J5LiAgSWYgdGhlcmUgYXJlIG11bHRpcGxlIEdTRUEgcmVzdWx0cyBmb2xkZXJzIGVhY2ggd2lsbCBiZSB1c2VkIHRvIGNyZWF0ZSBhbiBlbnJpY2htZW50IG1hcC4KCmBgYHtyIGNvbXB1dGUgcmVzdWx0cyBkaXJlY3RvcnkgbmFtZX0KaWYoZ3NlYV9kaXJlY3RvcnkgPT0gIiIpewogIGdzZWFfZGlyZWN0b3JpZXMgPC0gbGlzdC5maWxlcyhwYXRoID0gd29ya2luZ19kaXIsIHBhdHRlcm4gPSAiXFwuR3NlYVByZXJhbmtlZCIpCgogICNnZXQgdGhlIGRldGFpbHMgb24gdGhlIGZpbGVzCiAgZGV0YWlscyA9IGZpbGUuaW5mbyhmaWxlLnBhdGgoZ2V0d2QoKSx3b3JraW5nX2Rpcixnc2VhX2RpcmVjdG9yaWVzKSkKICAjb3JkZXIgYWNjb3JkaW5nIHRvIG5ld2VzdCB0byBvbGRlc3QKICBkZXRhaWxzID0gZGV0YWlsc1t3aXRoKGRldGFpbHMsIG9yZGVyKGFzLlBPU0lYY3QobXRpbWUpLGRlY3JlYXNpbmcgPSBUUlVFKSksIF0KCiAgI3VzZSB0aGUgbmV3ZXN0IGZpbGU6CiAgZ3NlYV9vdXRwdXRfZGlyIDwtIHJvdy5uYW1lcyhkZXRhaWxzKVsxXQp9IGVsc2UgewogIGdzZWFfb3V0cHV0X2RpciA8LSBnc2VhX2RpcmVjdG9yeQp9CgpgYGAKKioqCgojIyBMYXVuY2ggQ3l0b3NjYXBlCgpMYXVuY2ggQ3l0b3NjYXBlIChieSBkZWZhdWx0IGN5dG9zY2FwZSB3aWxsIGF1dG9tYXRpY2FsbHkgZW5hYmxlIHJlc3Qgc28gYXMgbG9uZyBhcyBjeXRvc2NhcGUgMy4zIG9yIGhpZ2hlciBpcyBvcGVuIFIgc2hvdWxkIGJlIGFibGUgdG8gY29tbXVuaWNhdGUgd2l0aCBpdCkuICBNYWtlIHN1cmUgaWYgeW91IGdldCBhbiBtZXNzYWdlIGFza2luZyB5b3UgaWYgeW91IHdhbnQgY29tbXVuaWNhdGUgd2l0aCBvdGhlciBhcHBzIHRoYXQgeW91IHNlbGVjdCAiQWxsb3ciLiAgCgojIyBNYWtlIHN1cmUgeW91IGNhbiBjb25uZWN0IHRvIEN5dG9zY2FwZQpgYGB7ciBpbml0aWFsaXplIGN5dG9zY2FwZSBjb25uZWN0aW9ufQogICAgY3l0b3NjYXBlUGluZyAoKQogICAgY3l0b3NjYXBlVmVyc2lvbkluZm8gKCkKYGBgCioqKgojIyBDcmVhdGUgYW4gRW5yaWNobWVudCBtYXAKYGBge3IgY3JlYXRlIGVucmljaG1lbnQgbWFwfQoKI2RlZmluZWQgdGhyZXNob2xkIGZvciBHU0VBIGVucmljaG1lbnRzIChuZWVkIHRvIGJlIHN0cmluZ3MgZm9yIGN5cmVzdCBjYWxsKQpwdmFsdWVfZ3NlYV90aHJlc2hvbGQgPC0gcGFyYW1zJHB2YWxfdGhyZXNoCnF2YWx1ZV9nc2VhX3RocmVzaG9sZCA8LSBwYXJhbXMkZmRyX3RocmVzaAoKc2ltaWxhcml0eV90aHJlc2hvbGQgPC0gIjAuMzc1IgpzaW1pbGFyaXR5X21ldHJpYyA9ICJDT01CSU5FRCIKCmN1cl9tb2RlbF9uYW1lIDwtIGFuYWx5c2lzX25hbWUKCmdzZWFfcmVzdWx0c19wYXRoIDwtIGZpbGUucGF0aChnc2VhX291dHB1dF9kaXIsImVkYiIpCmdzZWFfcmVzdWx0c19maWxlbmFtZSA8LSBmaWxlLnBhdGgoZ3NlYV9yZXN1bHRzX3BhdGgsInJlc3VsdHMuZWRiIikKCiNhbHRob3VnaCB0aGVyZSBpcyBhIGdtdCBmaWxlIGluIHRoZSBnc2VhIGVkYiByZXN1bHRzIGRpcmVjdG9yeSBpdCBoYXZlIGJlZW4gZmlsdGVyZWQgdG8gCiNjb250YWluIG9ubHkgZ2VuZXMgcmVwcmVzZW50ZWQgaW4gdGhlIGV4cHJlc3Npb24gc2V0LiAgSWYgeW91IHVzZSB0aGlzIGZsdGVyZWQgZmlsZSB5b3UgCiN3aWxsIGdldCBkaWZmZXJlbnQgcGF0aHdheSBjb25uZWN0aXZpdHkgZGVwZW5kaW5nIG9uIHRoZSBkYXRhc2V0IGJlaW5nIHVzZWQuICBXZSByZWNvbW1lbmQgCiN1c2luZyBvcmlnaW5hbCBnbXQgZmlsZSB1c2VkIGZvciB0aGUgZ3NlYSBhbmFseXNpcyBhbmQgbm90IHRoZSBmaWx0ZXJlZCBvbmUgaW4gdGhlIHJlc3VsdHMgZGlyZWN0b3J5LgpnbXRfZ3NlYV9maWxlIDwtIGZpbGUucGF0aChnZXR3ZCgpLGRlc3RfZ210X2ZpbGUpCmdzZWFfcmFua3NfZmlsZSA8LSBmaWxlLnBhdGgoZ3NlYV9yZXN1bHRzX3BhdGgsbGlzdC5maWxlcyhnc2VhX3Jlc3VsdHNfcGF0aCxwYXR0ZXJuPSIucm5rIikpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKI2NyZWF0ZSBFTQpjdXJyZW50X25ldHdvcmtfbmFtZSA8LSBwYXN0ZShjdXJfbW9kZWxfbmFtZSxwdmFsdWVfZ3NlYV90aHJlc2hvbGQscXZhbHVlX2dzZWFfdGhyZXNob2xkLHNlcD0iXyIpCgplbV9jb21tYW5kID0gcGFzdGUoJ2VucmljaG1lbnRtYXAgYnVpbGQgYW5hbHlzaXNUeXBlPSJnc2VhIiBnbXRGaWxlPScsZ210X2dzZWFfZmlsZSwKICAgICAgICAgICAgICAgICAgICdwdmFsdWU9JyxwdmFsdWVfZ3NlYV90aHJlc2hvbGQsICdxdmFsdWU9JyxxdmFsdWVfZ3NlYV90aHJlc2hvbGQsCiAgICAgICAgICAgICAgICAgICAnc2ltaWxhcml0eWN1dG9mZj0nLHNpbWlsYXJpdHlfdGhyZXNob2xkLAogICAgICAgICAgICAgICAgICAgJ2NvZWZmaWNpZW50cz0nLHNpbWlsYXJpdHlfbWV0cmljLCdyYW5rc0RhdGFzZXQxPScsIAogICAgICAgICAgICAgICAgICAgZ3NlYV9yYW5rc19maWxlLCdlbnJpY2htZW50c0RhdGFzZXQxPScsZ3NlYV9yZXN1bHRzX2ZpbGVuYW1lLCAKICAgICAgICAgICAgICAgICAgICdmaWx0ZXJCeUV4cHJlc3Npb25zPWZhbHNlJywKICAgICAgICAgICAgICAgICAgICdleHByZXNzaW9uRGF0YXNldDE9JyxmaWxlLnBhdGgoZ2V0d2QoKSx3b3JraW5nX2RpcixleHByZXNzaW9uX2ZpbGUpLAogICAgICAgICAgICAgICAgICAgc2VwPSIgIikKCiNlbnJpY2htZW50IG1hcCBjb21tYW5kIHdpbGwgcmV0dXJuIHRoZSBzdWlkIG9mIG5ld2x5IGNyZWF0ZWQgbmV0d29yay4KcmVzcG9uc2UgPC0gY29tbWFuZHNHRVQoZW1fY29tbWFuZCkKCmN1cnJlbnRfbmV0d29ya19zdWlkIDwtIDAKI2VucmljaG1lbnQgbWFwIGNvbW1hbmQgd2lsbCByZXR1cm4gdGhlIHN1aWQgb2YgbmV3bHkgY3JlYXRlZCBuZXR3b3JrIHVubGVzcyBpdCBGYWlsZWQuICAKI0lmIGl0IGZhaWxlZCBpdCB3aWxsIGNvbnRhaW4gdGhlIHdvcmQgZmFpbGVkCmlmKGdyZXBsKHBhdHRlcm49IkZhaWxlZCIsIHJlc3BvbnNlKSl7CiAgcGFzdGUocmVzcG9uc2UpCn0gZWxzZSB7CiAgY3VycmVudF9uZXR3b3JrX3N1aWQgPC0gcmVzcG9uc2UKfQoKI2NoZWNrIHRvIHNlZSBpZiB0aGUgbmV0d29yayBuYW1lIGlzIHVuaXF1ZQpjdXJyZW50X25hbWVzIDwtIGdldE5ldHdvcmtMaXN0KCkKaWYoY3VycmVudF9uZXR3b3JrX25hbWUgJWluJSBjdXJyZW50X25hbWVzKXsKICAjaWYgdGhlIG5hbWUgYWxyZWFkeSBleGlzdHMgaW4gdGhlIG5ldHdvcmsgbmFtZXMgdGhlbiBwdXQgdGhlIFNVSUQgaW4gZnJvbnQKICAjIG9mIHRoZSBuYW1lICh0aGlzIGRvZXMgbm90IHdvcmsgaWYgeW91IHB1dCB0aGUgc3VpZCBhdCB0aGUgZW5kIG9mIHRoZSBuYW1lKQogIGN1cnJlbnRfbmV0d29ya19uYW1lIDwtIHBhc3RlKGN1cnJlbnRfbmV0d29ya19zdWlkLGN1cnJlbnRfbmV0d29ya19uYW1lLCAgc2VwPSJfIikKfQpyZXNwb25zZSA8LSByZW5hbWVOZXR3b3JrKHRpdGxlPWN1cnJlbnRfbmV0d29ya19uYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBuZXR3b3JrID0gYXMubnVtZXJpYyhjdXJyZW50X25ldHdvcmtfc3VpZCksYmFzZS51cmwpCgoKYGBgCgojIyBHZXQgYSBzY3JlZW4gc2hvdCBvZiB0aGUgaW5pdGlhbCBuZXR3b3JrLgpgYGB7ciBnZXQgc2NyZWVuc2hvdH0KCmZpdENvbnRlbnQoKQoKb3V0cHV0X25ldHdvcmtfZmlsZSA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwiaW5pdGlhbF9zY3JlZW5zaG90X25ldHdvcmsucG5nIikKCmlmKGZpbGUuZXhpc3RzKG91dHB1dF9uZXR3b3JrX2ZpbGUpKXsKICAjY3l0b3NjYXBlIGhhbmdzIHdhaXRpbmcgZm9yIHVzZXIgcmVzcG9uc2UgaWYgZmlsZSBhbHJlYWR5IGV4aXN0cy4gIFJlbW92ZSBpdCBmaXJzdAogIHJlc3BvbnNlIDwtIGZpbGUucmVtb3ZlKG91dHB1dF9uZXR3b3JrX2ZpbGUpCn0gCgpyZXNwb25zZSA8LSBleHBvcnRJbWFnZShvdXRwdXRfbmV0d29ya19maWxlLCB0eXBlID0gInBuZyIpCgpgYGAKCmBgYHtyfQpodG1sdG9vbHM6OmltZyhzcmMgPSBrbml0cjo6aW1hZ2VfdXJpKG91dHB1dF9uZXR3b3JrX2ZpbGUpLCAKICAgICAgICAgICAgICAgYWx0ID0gJ0luaXRpYWwgRW5yaWNobWVudCBNYXAnLCAKICAgICAgICAgICAgICAgc3R5bGUgPSAnbWFyZ2luOjBweCBhdXRvO2Rpc3BsYXk6YmxvY2snKQpgYGAK