#make sure the RCy3 is installed. 
if(!requireNamespace("RCy3", quietly = TRUE)){
  if (!requireNamespace("BiocManager", quietly = TRUE)){
      install.packages("BiocManager")
    }
  BiocManager::install("RCy3")
}
if(!requireNamespace("RCurl", quietly = TRUE)){
  install.packages("RCurl")
}
library(RCy3)
library(RCurl)

0.1 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.

working_dir <- file.path(".",params$working_dir)
#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

0.2 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.

gmt_url = "http://download.baderlab.org/EM_Genesets/current_release/Human/symbol/"
#list all the files on the server
filenames = RCurl::getURL(gmt_url)
tc = textConnection(filenames)
Found more than one class "textConnection" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘RJSONIO’
Found more than one class "textConnection" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘RJSONIO’
Found more than one class "textConnection" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘RJSONIO’
Found more than one class "textConnection" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘RJSONIO’
Found more than one class "textConnection" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘RJSONIO’
contents = readLines(tc)
close(tc)
#get the gmt that has all the pathways and does not include terms inferred from electronic annotations(IEA)
#start with gmt file that has pathways only
rx = gregexpr("(?<=<a href=\")(.*.GOBP_AllPathways_no_GO_iea.*.)(.gmt)(?=\">)",
  contents, perl = TRUE)
gmt_file = unlist(regmatches(contents, rx))
dest_gmt_file <- file.path(multi_em_dir,gmt_file )
download.file(
    paste(gmt_url,gmt_file,sep=""),
    destfile=dest_gmt_file
)

Get all the rank files in the working directory

rank_files <- list.files(working_dir)[grep(pattern = "ranks.rnk",list.files(working_dir))]

Create a directory to hold the pathway analysis folders.

** This is an important step. In order for Enrichment map to create a multi dataset map the assumption is that all the results files will be together in a directory where each sub directory is an individual analysis **

multi_em_dir <- file.path(working_dir,"Multi_dataset_EM_analysis")
if(!dir.exists(multi_em_dir )){
  dir.create(multi_em_dir)
} else {
  multi_em_dir <- file.path(working_dir,paste("Multi_dataset_EM_analysis",Sys.Date(),sep = "_"))
  dir.create(multi_em_dir)
}
# copy the rank files to the new directory - if they aren't already there
if(length(list.files(multi_em_dir,pattern=".rnk")) == 0){
  file.copy(file.path(working_dir, rank_files), file.path(multi_em_dir,rank_files))
}
[1] TRUE TRUE TRUE TRUE TRUE
#after the files are copied over change the working dir to the newly 
# create directory
working_dir <- multi_em_dir

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

#run GSEA for each of the rank files.
for(i in 1:length(rank_files)){
    current_rank_file <- rank_files[i]
    
    analysis_name <- unlist(strsplit(rank_files[i],split = "_"))[1]
    #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,current_rank_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 {
      command <- paste("java  -Xmx1G -cp",gsea_jar,  "xtools.gsea.GseaPreranked -gmx", dest_gmt_file, "-rnk" ,file.path(working_dir,current_rank_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
java.io.FileNotFoundException: File not found: /Users/ruthisserlin/Dropbox (Bader Lab)/Ruth Isserlin's files/Sourcecode/Cytoscape_workflows/EnrichmentMapPipeline/./data/Multi_dataset_EM_analysis_2020-07-24/Human_GOBP_AllPathways_no_GO_iea_July_01_2020_symbol.gmt
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.createInputStream(ParserFactory.java:1056)
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.read(ParserFactory.java:799)
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.read(ParserFactory.java:787)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam._getObjects(GeneSetMatrixMultiChooserParam.java:123)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam._getGeneSets(GeneSetMatrixMultiChooserParam.java:167)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam.getGeneSetMatrixCombo(GeneSetMatrixMultiChooserParam.java:62)
    at org.gsea_msigdb.gsea/xtools.gsea.GseaPreranked.execute(GseaPreranked.java:101)
    at org.gsea_msigdb.gsea/xtools.api.AbstractTool.module_main(AbstractTool.java:434)
    at org.gsea_msigdb.gsea/org.genepattern.modules.GseaPrerankedWrapper.main(GseaPrerankedWrapper.java:228)
    at org.gsea_msigdb.gsea/xapps.gsea.CLI.main(CLI.java:31)
WARNING: package com.sun.java.swing.plaf.windows not in java.desktop
WARNING: package sun.awt.windows not in java.desktop
java.io.FileNotFoundException: File not found: /Users/ruthisserlin/Dropbox (Bader Lab)/Ruth Isserlin's files/Sourcecode/Cytoscape_workflows/EnrichmentMapPipeline/./data/Multi_dataset_EM_analysis_2020-07-24/Human_GOBP_AllPathways_no_GO_iea_July_01_2020_symbol.gmt
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.createInputStream(ParserFactory.java:1056)
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.read(ParserFactory.java:799)
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.read(ParserFactory.java:787)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam._getObjects(GeneSetMatrixMultiChooserParam.java:123)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam._getGeneSets(GeneSetMatrixMultiChooserParam.java:167)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam.getGeneSetMatrixCombo(GeneSetMatrixMultiChooserParam.java:62)
    at org.gsea_msigdb.gsea/xtools.gsea.GseaPreranked.execute(GseaPreranked.java:101)
    at org.gsea_msigdb.gsea/xtools.api.AbstractTool.module_main(AbstractTool.java:434)
    at org.gsea_msigdb.gsea/org.genepattern.modules.GseaPrerankedWrapper.main(GseaPrerankedWrapper.java:228)
    at org.gsea_msigdb.gsea/xapps.gsea.CLI.main(CLI.java:31)
WARNING: package com.sun.java.swing.plaf.windows not in java.desktop
WARNING: package sun.awt.windows not in java.desktop
java.io.FileNotFoundException: File not found: /Users/ruthisserlin/Dropbox (Bader Lab)/Ruth Isserlin's files/Sourcecode/Cytoscape_workflows/EnrichmentMapPipeline/./data/Multi_dataset_EM_analysis_2020-07-24/Human_GOBP_AllPathways_no_GO_iea_July_01_2020_symbol.gmt
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.createInputStream(ParserFactory.java:1056)
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.read(ParserFactory.java:799)
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.read(ParserFactory.java:787)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam._getObjects(GeneSetMatrixMultiChooserParam.java:123)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam._getGeneSets(GeneSetMatrixMultiChooserParam.java:167)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam.getGeneSetMatrixCombo(GeneSetMatrixMultiChooserParam.java:62)
    at org.gsea_msigdb.gsea/xtools.gsea.GseaPreranked.execute(GseaPreranked.java:101)
    at org.gsea_msigdb.gsea/xtools.api.AbstractTool.module_main(AbstractTool.java:434)
    at org.gsea_msigdb.gsea/org.genepattern.modules.GseaPrerankedWrapper.main(GseaPrerankedWrapper.java:228)
    at org.gsea_msigdb.gsea/xapps.gsea.CLI.main(CLI.java:31)
WARNING: package com.sun.java.swing.plaf.windows not in java.desktop
WARNING: package sun.awt.windows not in java.desktop
java.io.FileNotFoundException: File not found: /Users/ruthisserlin/Dropbox (Bader Lab)/Ruth Isserlin's files/Sourcecode/Cytoscape_workflows/EnrichmentMapPipeline/./data/Multi_dataset_EM_analysis_2020-07-24/Human_GOBP_AllPathways_no_GO_iea_July_01_2020_symbol.gmt
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.createInputStream(ParserFactory.java:1056)
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.read(ParserFactory.java:799)
    at org.gsea_msigdb.gsea/edu.mit.broad.genome.parsers.ParserFactory.read(ParserFactory.java:787)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam._getObjects(GeneSetMatrixMultiChooserParam.java:123)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam._getGeneSets(GeneSetMatrixMultiChooserParam.java:167)
    at org.gsea_msigdb.gsea/xtools.api.param.GeneSetMatrixMultiChooserParam.getGeneSetMatrixCombo(GeneSetMatrixMultiChooserParam.java:62)
    at org.gsea_msigdb.gsea/xtools.gsea.GseaPreranked.execute(GseaPreranked.java:101)
    at org.gsea_msigdb.gsea/xtools.api.AbstractTool.module_main(AbstractTool.java:434)
    at org.gsea_msigdb.gsea/org.genepattern.modules.GseaPrerankedWrapper.main(GseaPrerankedWrapper.java:228)
    at org.gsea_msigdb.gsea/xapps.gsea.CLI.main(CLI.java:31)

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

0.5 Set up connection from R to cytoscape

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

0.6 Create a multi dataset EM

#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 <- "Multi_dataset_EM"
gsea_results_path <- multi_em_dir
#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_root_dir <- file.path(getwd(),multi_em_dir)
#######################################
#create EM
current_network_name <- paste(cur_model_name,pvalue_gsea_threshold,qvalue_gsea_threshold,sep="_")
em_command = paste('enrichmentmap mastermap commonGMTFile=',gmt_gsea_file,
                   'pvalue=',pvalue_gsea_threshold, 'qvalue=',qvalue_gsea_threshold,
                   'similaritycutoff=',similarity_threshold,
                   'coefficients=',similarity_metric,'rootFolder=', 
                   gsea_root_dir,
                   'filterByExpressions=false',
                   'commonExpressionFile=',file.path(getwd(),params$expression_file),
                   sep=" ")
#enrichment map command will return the suid of newly created network.
response <- RCy3::commandsGET(em_command)
Error in curl::curl_fetch_memory(url, handle = handle) : 
  Operation was aborted by an application callback

0.7 Change the coloring on the network

By default the network will colour each section of the pie using the NES value (GSEA analysis) and FDR value for any other analysis. This doesn't let you see clearly which pathways are significant in each dataset. By changing the colouring type to "DATA_SET" each node will only be coloured if the pathway is significant in the given dataset (by the user defined thresholds).

** The colours are set by default. There is no progrommatic way to change the colours. It can be done manually though.**

response <- RCy3::commandsGET('enrichmentmap chart data="DATA_SET"')

0.8 Define themes

Annotate the Enrichment map in order to calculate the themes

response <- RCy3::commandsGET(paste('autoannotate annotate-clusterBoosted network=',current_network_suid,sep=""))

0.9 Create a summary network

theme_network <- as.numeric(RCy3::commandsGET(paste('autoannotate summary network=', current_network_suid,sep="")))

0.10 Get all the theme information

Depending on the data type of the node attributes when the theme network is created they will be collapsed differently. You can set this behaviour manually in cytoscape from Edit -> Preference -> Group Preferences. For more info on this see here

  default_node_table <- RCy3::getTableColumns(table= "node",network = theme_network)

0.11 Output all Themes found in all the datasets

num_datasets <- length(rank_files)
dataset_chart <- t(sapply(default_node_table$`EnrichmentMap::Dataset_Chart`, function (x) { return (x)}))
rownames(dataset_chart) <- default_node_table$name
#calculate the number of datasets for each theme
dataset_per_theme <- rowSums(dataset_chart)
#output the themes that are found in the most datasets
rownames(dataset_chart)[which(dataset_per_theme == max(dataset_per_theme))]
[1] "axonemal dynein microtubule"

0.12 Output the pathways specific to each dataset

#Go through each index of the dataset chart and output the pathways specific for each group
specific_to_theme_summary <- list()
for(i in 1:dim(dataset_chart)[2]){
  current_dataset <- i
  
  specific_to_theme_summary[[i]] <- rownames(dataset_chart)[intersect(which(dataset_per_theme == 1) , which(dataset_chart[,i]==1))]
}
#unfortunately there is no way currently to get which id corresponds to which dataset.  Guess which according to 
# the order of the columns in the returned table.
names(specific_to_theme_summary) <- unlist(lapply(colnames(default_node_table)[grep(colnames(default_node_table),pattern="pvalue")],FUN=function(x){unlist(strsplit(x, split =" "))[2]}))
specific_to_theme_summary
$`(DiffvsRest.GseaPreranked)`
[1] "signal kinetochores amplification" "chromosome segregation sister"     "pid aurora signaling"             

$`(ImmunovsRest.GseaPreranked)`
 [1] "camera-type eye sensory"           "central nervous neuron"            "limb morphogenesis appendage"     
 [4] "ncam signaling neurite"            "cardiac septum morphogenesis"      "bmp tgf-beta pathway"             
 [7] "positive interleukin-4 production" "nephron epithelium kidney"         "digestive tract development"      
[10] "negative regulation smoothened"    "involved wnt carcinoma"            "growth factor stimulus"           
[13] "lymphocyte activation selection"  

$`(MesenvsRest.GseaPreranked)`
 [1] "gene silencing rna"                              "trna modification processing"                   
 [3] "mitochondrial translational termination"         "glycosaminoglycan chondroitin sulfate"          
 [5] "substrate adhesion-dependent spreading"          "mature transcript cytoplasm"                    
 [7] "coupled electron nadh"                           "negative blood vessel"                          
 [9] "pid avb3 integrin"                               "o-glycosylation domain-containing glycosylation"
[11] "flagellum-dependent motility cilium-dependent"   "assembly collagen formation"                    
[13] "blood vessel morphogenesis"                      "positive vasculature endothelial"               
[15] "protein-dna nucleosome centromere"               "spliceosomal snrnp ribonucleoprotein"           
[17] "pre-mrna splicing u12"                           "regulation chondrocyte cartilage"               
[19] "smooth muscle proliferation"                     "ribosomal subunit biogenesis"                   
[21] "replication dna dna-dependent"                   "regulation igf activity"                        
[23] "nucleotide excision repair"                      "beta3 basement membranes"                       
[25] "primary germ endoderm"                          

$`(ProlifvsRest.GseaPreranked)`
 [1] "erk1 erk2 cascade"                      "complement maturation protein"         
 [3] "platelet activation coagulation"        "blood coagulation hemostasis"          
 [5] "pid il4 2pathway"                       "osteoclast differentiation myeloid"    
 [7] "leukocyte homeostasis number"           "regulation secretion negative"         
 [9] "pid il8 cxcr2"                          "peptidyl-tyrosine phosphorylation stat"
[11] "pid gmcsf pathway"                      "cellular lipoprotein particle"         
[13] "beta2 integrin cell"                    "pattern receptor toll-like"            
LS0tCnRpdGxlOiAiU3VwcGxlbWVudGFyeSBQcm90b2NvbCA1IOKAkyBDcmVhdGUgTXVsdGkgZGF0YXNldCBFbnJpY2htZW50IE1hcCBhbmQgVGhlbWUgaW52ZXN0aWdhdGlvbiIKYXV0aG9yOiAiUnV0aCBJc3NlcmxpbiIKZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpKWAiCnBhcmFtczoKICB3b3JraW5nX2RpcjogZGF0YQogIHJ1bl9nc2VhOiBUUlVFCiAgZ3NlYV9kaXJlY3Rvcnk6ICcnCiAgZXhwcmVzc2lvbl9maWxlOiBkYXRhL1N1cHBsZW1lbnRhcnlfVGFibGU2X1RDR0FfT1ZfUk5Bc2VxX2V4cHJlc3Npb24udHh0CiAgZ3NlYV9qYXI6IC9Vc2Vycy9ydXRoaXNzZXJsaW4vRG93bmxvYWRzL0dTRUFfNC4wLjMvZ3NlYS1jbGkuc2gKICBqYXZhX3ZlcnNpb246IDExCiAgcHZhbF90aHJlc2g6IDEKICBmZHJfdGhyZXNoOiAwLjAwMDEKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBoaWdobGlnaDogaGFkZG9jawogICAga2VlcF9tZDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHBhcGVyCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogbm8KICAgICAgc21vb3RoX3Njcm9sbDogbm8KICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnMycKICBodG1sX25vdGVib29rOgogICAgaGlnaGxpZ2g6IGhhZGRvY2sKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogcGFwZXIKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBubwogICAgICBzbW9vdGhfc2Nyb2xsOiBubwpiaWJsaW9ncmFwaHk6IHN1cF9wcm90b2NvbDFfcmVmZXJlbmNlcy5iaWIKY3NsOiBuYXR1cmUtcHJvdG9jb2xzLmNzbAotLS0KCmBgYHtyfQojbWFrZSBzdXJlIHRoZSBSQ3kzIGlzIGluc3RhbGxlZC4gCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJSQ3kzIiwgcXVpZXRseSA9IFRSVUUpKXsKICBpZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKXsKICAgICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQogICAgfQogIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJSQ3kzIikKfQoKaWYoIXJlcXVpcmVOYW1lc3BhY2UoIlJDdXJsIiwgcXVpZXRseSA9IFRSVUUpKXsKICBpbnN0YWxsLnBhY2thZ2VzKCJSQ3VybCIpCn0KCmxpYnJhcnkoUkN5MykKbGlicmFyeShSQ3VybCkKYGBgCgoKIyMgQ29uZmlndXJhYmxlIFBhcmFtZXRlcnMKSW4gb3JkZXIgdG8gcnVuIEdTRUEgYXV0b21hdGljYWxseSB0aHJvdWdoIHRoZSBub3RlYm9vayB5b3Ugd2lsbCBuZWVkIHRvIGRvd25sb2FkIHRoZSBnc2VhIGphciBmcm9tIFtoZXJlXShodHRwOi8vc29mdHdhcmUuYnJvYWRpbnN0aXR1dGUub3JnL2dzZWEvZG93bmxvYWRzLmpzcCkuICBTcGVjaWZ5IHRoZSBleGFjdCBwYXRoIHRvIHRoZSBnc2VhIGphciBpbiB0aGUgcGFyYW1ldGVycyBpbiBvcmRlciB0byBhdXRvbWF0aWNhbGx5IGNvbXB1dGUgZW5yaWNobWVudHMgdXNpbmcgR1NFQS4KCmBgYHtyIGluaXRpYWxpemUgcGFyYW1ldGVyc30KCndvcmtpbmdfZGlyIDwtIGZpbGUucGF0aCgiLiIscGFyYW1zJHdvcmtpbmdfZGlyKQoKI3BhdGggdG8gR1NFQSBqYXIgCiMgSW4gb3JkZXIgdG8gcnVuIEdTRUEgYXV0b21hdGljYWxseSB5b3UgbmVlZCB0byBzcGVjaXJ5IHRoZSBwYXRoIHRvIHRoZSBnc2VhIGphciBmaWxlLgojV2l0aCB0aGUgbGF0ZXN0IHJlbGVhc2Ugb2YgZ3NlYSAoNC4wLjIpIHRoZXkgbm8gbG9uZ2VyIHJlbGVhc2UgYSBidW5kbGVkIGphcgojIGFuZCBpbnN0ZWFkIHJlbGVhc2UgYSBzY3JpcHR0ZWQgd2F5IHRvIGxhdW5jaCB0aGUgZ3NlYSBjbGllbnQuCiMgc3BlY2lmeSB0aGUgamF2YSB2ZXJzaW9uIGFzIDExIGlmIHlvdSBhcmUgdXNpbmcgdGhlIGxhdGVyIHZlcnNpb24gZ3NlYQojIHRoZSBnc2VhX2phciBhbHNvIG5lZWRzIHRvIGJlIHRoZSBmdWxsIHBhdGggdG8gdGhlIEdTRUEgNC4wLjIgZGlyZWN0b3J5IHRoYXQgeW91CiMgZG93bmxvYWRlZCBmcm9tIEdTRUEuIGZvciBleGFtcGxlICgvVXNlcnMvam9obnNtaXRoL0dTRUFfNC4wLjIvZ3NlYS1jbGkuc2gpCmdzZWFfamFyIDwtIHBhcmFtcyRnc2VhX2phcgpqYXZhX3ZlcnNpb24gPC0gcGFyYW1zJGphdmFfdmVyc2lvbgoKI0dzZWEgdGFrZXMgYSBsb25nIHRpbWUgdG8gcnVuLiAgSWYgeW91IGhhdmUgYWxyZWFkeSBydW4gR1NFQSBtYW51YWxseSBvciBwcmV2aW91c2x5IHRoZXJlIGlzIG5vIG5lZWQgdG8gcmUtcnVuIEdTRUEuICBNYWtlIHN1cmUgdGhlIAojIGdzZWEgcmVzdWx0cyBhcmUgaW4gdGhlIGN1cnJlbnQgZGlyZWN0b3J5IGFuZCB0aGUgbm90ZWJvb2sgd2lsbCBiZSBhYmxlIHRvIGZpbmQgdGhlbSBhbmQgdXNlIHRoZW0uCnJ1bl9nc2VhID0gcGFyYW1zJHJ1bl9nc2VhCmBgYAoKCiMjIERvd25sb2FkIHRoZSBsYXRlc3QgcGF0aHdheSBkZWZpbml0aW9uIGZpbGUKT25seSBIdW1hbiwgTW91c2UgYW5kIFJhdCBnZW5lIHNldCBmaWxlcyBhcmUgY3VycmVudGx5IGF2YWlsYWJsZSBvbiB0aGUgYmFkZXJsYWIgZG93bmxvYWRzIHNpdGUuICBJZiB5b3UgYXJlIHdvcmtpbmcgd2l0aCBhIHNwZWNpZXMgb3RoZXIgdGhhbiBodW1hbiAoYW5kIGl0IGlzIGVpdGhlciByYXQgb3IgbW91c2UpIGNoYW5nZSB0aGUgZ210X3VybCBiZWxvdyB0byBjb3JyZWN0IHNwZWNpZXMuIENoZWNrIFtoZXJlXShodHRwOi8vZG93bmxvYWQuYmFkZXJsYWIub3JnL0VNX0dlbmVzZXRzL2N1cnJlbnRfcmVsZWFzZS8pIHRvIHNlZSBhbGwgYXZhaWxhYmxlIHNwZWNpZXMuIAoKYGBge3IgZG93bmxvYWQgYmFkZXJsYWIgZ210IGZpbGUsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ210X3VybCA9ICJodHRwOi8vZG93bmxvYWQuYmFkZXJsYWIub3JnL0VNX0dlbmVzZXRzL2N1cnJlbnRfcmVsZWFzZS9IdW1hbi9zeW1ib2wvIgoKI2xpc3QgYWxsIHRoZSBmaWxlcyBvbiB0aGUgc2VydmVyCmZpbGVuYW1lcyA9IFJDdXJsOjpnZXRVUkwoZ210X3VybCkKdGMgPSB0ZXh0Q29ubmVjdGlvbihmaWxlbmFtZXMpCmNvbnRlbnRzID0gcmVhZExpbmVzKHRjKQpjbG9zZSh0YykKCiNnZXQgdGhlIGdtdCB0aGF0IGhhcyBhbGwgdGhlIHBhdGh3YXlzIGFuZCBkb2VzIG5vdCBpbmNsdWRlIHRlcm1zIGluZmVycmVkIGZyb20gZWxlY3Ryb25pYyBhbm5vdGF0aW9ucyhJRUEpCiNzdGFydCB3aXRoIGdtdCBmaWxlIHRoYXQgaGFzIHBhdGh3YXlzIG9ubHkKcnggPSBncmVnZXhwcigiKD88PTxhIGhyZWY9XCIpKC4qLkdPQlBfQWxsUGF0aHdheXNfbm9fR09faWVhLiouKSguZ210KSg/PVwiPikiLAogIGNvbnRlbnRzLCBwZXJsID0gVFJVRSkKZ210X2ZpbGUgPSB1bmxpc3QocmVnbWF0Y2hlcyhjb250ZW50cywgcngpKQoKZGVzdF9nbXRfZmlsZSA8LSBmaWxlLnBhdGgod29ya2luZ19kaXIsZ210X2ZpbGUgKQoKZG93bmxvYWQuZmlsZSgKICAgIHBhc3RlKGdtdF91cmwsZ210X2ZpbGUsc2VwPSIiKSwKICAgIGRlc3RmaWxlPWRlc3RfZ210X2ZpbGUKKQoKCmBgYAoKR2V0IGFsbCB0aGUgcmFuayBmaWxlcyBpbiB0aGUgd29ya2luZyBkaXJlY3RvcnkKYGBge3J9CnJhbmtfZmlsZXMgPC0gbGlzdC5maWxlcyh3b3JraW5nX2RpcilbZ3JlcChwYXR0ZXJuID0gInJhbmtzLnJuayIsbGlzdC5maWxlcyh3b3JraW5nX2RpcikpXQpgYGAKCkNyZWF0ZSBhIGRpcmVjdG9yeSB0byBob2xkIHRoZSBwYXRod2F5IGFuYWx5c2lzIGZvbGRlcnMuICAKCioqIFRoaXMgaXMgYW4gaW1wb3J0YW50IHN0ZXAuIEluIG9yZGVyIGZvciBFbnJpY2htZW50IG1hcCB0byBjcmVhdGUgYSBtdWx0aSBkYXRhc2V0IG1hcCB0aGUgYXNzdW1wdGlvbiBpcyB0aGF0IGFsbCB0aGUgcmVzdWx0cyBmaWxlcyB3aWxsIGJlIHRvZ2V0aGVyIGluIGEgZGlyZWN0b3J5IHdoZXJlIGVhY2ggc3ViIGRpcmVjdG9yeSBpcyBhbiBpbmRpdmlkdWFsIGFuYWx5c2lzICoqCgpgYGB7cn0KCm11bHRpX2VtX2RpciA8LSBmaWxlLnBhdGgod29ya2luZ19kaXIsIk11bHRpX2RhdGFzZXRfRU1fYW5hbHlzaXMiKQppZighZGlyLmV4aXN0cyhtdWx0aV9lbV9kaXIgKSl7CiAgZGlyLmNyZWF0ZShtdWx0aV9lbV9kaXIpCn0gZWxzZSB7CiAgbXVsdGlfZW1fZGlyIDwtIGZpbGUucGF0aCh3b3JraW5nX2RpcixwYXN0ZSgiTXVsdGlfZGF0YXNldF9FTV9hbmFseXNpcyIsU3lzLkRhdGUoKSxzZXAgPSAiXyIpKQogIGRpci5jcmVhdGUobXVsdGlfZW1fZGlyKQp9CgojIGNvcHkgdGhlIHJhbmsgZmlsZXMgdG8gdGhlIG5ldyBkaXJlY3RvcnkgLSBpZiB0aGV5IGFyZW4ndCBhbHJlYWR5IHRoZXJlCmlmKGxlbmd0aChsaXN0LmZpbGVzKG11bHRpX2VtX2RpcixwYXR0ZXJuPSIucm5rIikpID09IDApewogIGZpbGUuY29weShmaWxlLnBhdGgod29ya2luZ19kaXIsIHJhbmtfZmlsZXMpLCBmaWxlLnBhdGgobXVsdGlfZW1fZGlyLHJhbmtfZmlsZXMpKQp9CgojYWZ0ZXIgdGhlIGZpbGVzIGFyZSBjb3BpZWQgb3ZlciBjaGFuZ2UgdGhlIHdvcmtpbmcgZGlyIHRvIHRoZSBuZXdseSAKIyBjcmVhdGUgZGlyZWN0b3J5CndvcmtpbmdfZGlyIDwtIG11bHRpX2VtX2RpcgpgYGAKCgoKIyMgUnVuIEdTRUEKKEdTRUEpW2h0dHA6Ly9zb2Z0d2FyZS5icm9hZGluc3RpdHV0ZS5vcmcvZ3NlYS9pbmRleC5qc3BdIGlzIGEgc3RhbmQgYWxvbmUgamF2YSBwcm9ncmFtIHdpdGggbWFueSBjdXN0b21pemFibGUgb3B0aW9ucy4gIEl0IGNhbiBiZSBlYXNpbHkgcnVuIHRocm91Z2ggaXRzIGludGVncmF0ZWQgdXNlciBpbnRlcmZhY2UuICBUbyBtYWtlIHRoaXMgYSBzZWVtbGVzcyBwaXBlbGluZSB3ZSBjYW4gcnVuIEdTRUEgZnJvbSB0aGUgY29tbWFuZCBsaW5lIHdpdGggYSBzZXQgb2Ygb3B0aW9ucy4gIEFueSBvZiB0aGUgc3VwcGxpZWQgb3B0aW9ucyBjYW4gYmUgY3VzdG9taXplZCBhbmQgdGhlcmUgYXJlIG1hbnkgYWRkaXRpb25hbCBvcHRpb25zIHRoYXQgY2FuIGJlIHNwZWNpZmllZC4gIEZvciBtb3JlIGRldGFpbHMgc2VlIChoZXJlKVtodHRwOi8vc29mdHdhcmUuYnJvYWRpbnN0aXR1dGUub3JnL2dzZWEvZG9jL0dTRUFVc2VyR3VpZGVURVhULmh0bSNfUnVubmluZ19HU0VBX2Zyb21dCgpJbiB0aGUgYmVsb3cgY29tbWFuZCB0aGUgZm9sbG93aW5nIG9wdGlvbnMgaGF2ZSBiZWVuIHNwZWNpZmllZDoKCiAqIHJuayAtIHBhdGggdG8gdGhlIHJhbmsgZmlsZQogKiBnbXggLSBwYXRoIHRvIHRoZSBnZW5lIHNldCBkZWZpbml0aW9uIChnbXQpIGZpbGUKICogY29sbGFwc2UgLSB0cnVlL2ZhbHNlIGluZGljYXRlcyB3aGV0aGVyIHRoZSBleHByZXNzaW9uL3JuayBmaWxlIG5lZWRzIHRvIGJlIGNvbGxhcHNlZCBmcm9tIHByb2JlcyB0byBnZW5lIHN5bWJvbHMKICogbnBlcm0gLSBudW1iZXIgb2YgcGVybXV0YXRpb25zCiAqIHBlcm11dGUgLSBwZXJtdXRlIGdlbmUgc2V0cyBvciBwaGVudG95cGVzLiAgRm9yIEdTRUEgcHJlcmFua2VkIHlvdSBjYW4gb25seSBwZXJtdXRlIGdlbmVzZXRzLgogKiBzY29yaW5nX3NjaGVtZSAtIAogKiBycHRfbGFiZWwgLSBuYW1lIG9mIHRoZSBkaXJlY3Rvcnkgd2l0aCBvdXRwdXQKICogbnVtIC0gbnVtYmVyIG9mIHJlc3VsdHMgdG8gcGxvdCBvdXRwdXQgZmlsZSBmb3IKICogcm5kX3NlZWQgLSByYW5kb20gc2VlZCB0byB1c2UKICogc2V0X21heCAtIG1heGltdW0gc2l6ZSBmb3IgaW5kaXZpZHVhbCBnZW5lIHNldHMuICBJbiBHU0VBIGludGVyZmFjZSB0aGlzIGlzIHNldCB0byA1MDAgYnV0IHdlIHByZWZlciB0byB1c2UgYSBtb3JlIHN0cmluZ2VudCBzZXR0aW5nIG9mIDIwMC4gCiAqIHNldF9taW4gLSBtaW5pbXVtIHNpemUgZm9yIGluZGl2aWR1YWwgZ2VuZSBzZXRzIAogKiB6aXBfcmVwb3J0IC0gdHJ1ZS9mYWxzZSB0byB6aXAgb3V0cHV0IGRpcmVjdG9yeQogKiBvdXQgLSBkaXJlY3Rvcnkgd2hlcmUgdG8gcGxhY2UgdGhlIHJlc3VsdCBkaXJlY3RvcnkuCiAqIGd1aSAtIHRydWUvZmFsc2UuIFdoZW4gcnVubmluZyBHU0VBIGZyb20gdGhlIGNvbW1hbmRsaW5lIHRoaXMgbmVlZHMgdG8gYmUgZmFsc2UuCgpJZiB5b3UgYXJlIHVzaW5nIEdTRUEgNC4wLjIgeW91IHdpbGwgbmVlZCB0byB1cGRhdGUgeW91ciBzeXN0ZW0gdG8gdXNlIEphdmEgMTEuICBUaGUgR1NFQSBjb21tYW5kIGxpbmUgaW50ZXJmYWNlIGhhcyBjaGFuZ2VkIHNsaWdodGx5IGluIHRoaXMgdmVyc2lvbiBvZiBHU0VBLiAgSW5zdGVhZCBvZiBsYXVuY2hpbmcgR1NFQSB1c2luZyBqYXZhIHlvdSBuZWVkIHRvIHVzZSBvbmUgb2YgdGhlIHNjcmlwdHMgc3VwcGxpZWQgYnkgR1NFQS4gIChnc2VhLWNsaS5iYXQgZm9yIFdpbmRvd3MgYW5kIGdzZWEtY2xpLnNoIGZvciBNYWMgb3IgbGludXggc3lzdGVtcykuICAKCkluIHRoZSBHU0VBIDQuMC4yIGNvbW1hbmQgdGhlIGZvbGxvd2luZyBvcHRpb25zIGNhbiBiZWVuIHNwZWNpZmllZDoKCiAqIHJuayAtIHBhdGggdG8gdGhlIHJhbmsgZmlsZQogKiBnbXggLSBwYXRoIHRvIHRoZSBnZW5lIHNldCBkZWZpbml0aW9uIChnbXQpIGZpbGUKICogY29sbGFwc2UgLSB0cnVlL2ZhbHNlIGluZGljYXRlcyB3aGV0aGVyIHRoZSBleHByZXNzaW9uL3JuayBmaWxlIG5lZWRzIHRvIGJlIGNvbGxhcHNlZCBmcm9tIHByb2JlcyB0byBnZW5lIHN5bWJvbHMKICogbnBlcm0gLSBudW1iZXIgb2YgcGVybXV0YXRpb25zCiAqIHNjb3Jpbmdfc2NoZW1lIC0gCiAqIHJwdF9sYWJlbCAtIG5hbWUgb2YgdGhlIGRpcmVjdG9yeSB3aXRoIG91dHB1dAogKiBybmRfc2VlZCAtIHJhbmRvbSBzZWVkIHRvIHVzZQogKiBzZXRfbWF4IC0gbWF4aW11bSBzaXplIGZvciBpbmRpdmlkdWFsIGdlbmUgc2V0cy4gIEluIEdTRUEgaW50ZXJmYWNlIHRoaXMgaXMgc2V0IHRvIDUwMCBidXQgd2UgcHJlZmVyIHRvIHVzZSBhIG1vcmUgc3RyaW5nZW50IHNldHRpbmcgb2YgMjAwLiAKICogc2V0X21pbiAtIG1pbmltdW0gc2l6ZSBmb3IgaW5kaXZpZHVhbCBnZW5lIHNldHMgCiAqIHppcF9yZXBvcnQgLSB0cnVlL2ZhbHNlIHRvIHppcCBvdXRwdXQgZGlyZWN0b3J5CiAqIG91dCAtIGRpcmVjdG9yeSB3aGVyZSB0byBwbGFjZSB0aGUgcmVzdWx0IGRpcmVjdG9yeS4KCklmIHlvdSBoYXZlIGVuY291bnRlciBhbiBvdXQgb2YgbWVtb3J5IGVycm9yIHlvdSB3aWxsIG5lZWQgdG8gb3BlbiB5b3UgZ3NlYS1jbGkgc2NyaXB0IGFuZCBtYW51YWxseSB1cGRhdGUgeW91ciB4bXggcGFyYW1ldGVyIChtb3JlIGluZm9ybWF0aW9uIG9uIHRoaXMgY2FuIGJlIGZvdW5kIGluIHRoZSBHU0VBXzQuMC4yL1JFQURNRSBmaWxlKQogCmBgYHtyIHJ1biBHU0VBLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKI3J1biBHU0VBIGZvciBlYWNoIG9mIHRoZSByYW5rIGZpbGVzLgpmb3IoaSBpbiAxOmxlbmd0aChyYW5rX2ZpbGVzKSl7CiAgICBjdXJyZW50X3JhbmtfZmlsZSA8LSByYW5rX2ZpbGVzW2ldCiAgICAKICAgIGFuYWx5c2lzX25hbWUgPC0gdW5saXN0KHN0cnNwbGl0KHJhbmtfZmlsZXNbaV0sc3BsaXQgPSAiXyIpKVsxXQogICAgI2lmIHlvdSBhcmUgdXNpbmcgR1NFQSA0LjAuMiB0aGVuIHlvdSBuZWVkIHRvIHVzZSB0aGUgY29tbWFuZCBzY3JpcHQKICAgICMgYXMgb3Bwb3NlZCB0byBsYXVuY2hpbmcgR1NFQSB0aHJvdWdoIGphdmEuIAogICAgIyBpbiB0aGUgbGF0ZXIgdmVyc2lvbiBvZiBHU0VBIGNvbW1hbmQgbGluZSBpbXBsZW1lbnRhdGlvbiB0aGUgZm9sbG93aW5nIAogICAgIyBwYXJhbWV0ZXJzIGFyZSBubyBsb25nZXIgdmFsaWQ6IC1wZXJtdXRlIGdlbmVfc2V0LCAgLW51bSAxMDAsIC1ndWkgZmFsc2UKICAgICMgbm8gbG9uZ2VyIG5lZWQgdG8gc3BlY2lmeSB0aGUgd2hvbGUgcGF0aCB0byB0aGUgR3NlYVByZXJhbmtlZCBwYWNrYWdlCiAgICBpZihydW5fZ3NlYSAmJiBqYXZhX3ZlcnNpb24gPT0gIjExIil7CiAgICAgIGNvbW1hbmQgPC0gcGFzdGUoIiIsZ3NlYV9qYXIsICAiR1NFQVByZVJhbmtlZCAtZ214ICIsIGRlc3RfZ210X2ZpbGUsICItcm5rIiAsZmlsZS5wYXRoKHdvcmtpbmdfZGlyLGN1cnJlbnRfcmFua19maWxlICksICItY29sbGFwc2UgZmFsc2UgLW5wZXJtIDEwMDAgLXNjb3Jpbmdfc2NoZW1lIHdlaWdodGVkIC1ycHRfbGFiZWwgIixhbmFseXNpc19uYW1lLCIgIC1wbG90X3RvcF94IDIwIC1ybmRfc2VlZCAxMjM0NSAgLXNldF9tYXggMjAwIC1zZXRfbWluIDE1IC16aXBfcmVwb3J0IGZhbHNlIC1vdXQiICx3b3JraW5nX2RpciwgIiA+IGdzZWFfb3V0cHV0LnR4dCIsc2VwPSIgIikKICAgICAgc3lzdGVtKGNvbW1hbmQpCiAgICB9IGVsc2UgewogICAgICBjb21tYW5kIDwtIHBhc3RlKCJqYXZhICAtWG14MUcgLWNwIixnc2VhX2phciwgICJ4dG9vbHMuZ3NlYS5Hc2VhUHJlcmFua2VkIC1nbXgiLCBkZXN0X2dtdF9maWxlLCAiLXJuayIgLGZpbGUucGF0aCh3b3JraW5nX2RpcixjdXJyZW50X3JhbmtfZmlsZSApLCAiLWNvbGxhcHNlIGZhbHNlIC1ucGVybSAxMDAwIC1wZXJtdXRlIGdlbmVfc2V0IC1zY29yaW5nX3NjaGVtZSB3ZWlnaHRlZCAtcnB0X2xhYmVsICIsYW5hbHlzaXNfbmFtZSwiICAtbnVtIDEwMCAtcGxvdF90b3BfeCAyMCAtcm5kX3NlZWQgMTIzNDUgIC1zZXRfbWF4IDIwMCAtc2V0X21pbiAxNSAtemlwX3JlcG9ydCBmYWxzZSAtb3V0IiAsd29ya2luZ19kaXIsICItZ3VpIGZhbHNlID4gZ3NlYV9vdXRwdXQudHh0IixzZXA9IiAiKQogICAgICBzeXN0ZW0oY29tbWFuZCkKICAgIH0KfQpgYGAKCiMjIExhdW5jaCBDeXRvc2NhcGUKQ3JlYXRlIEVNIHRocm91Z2ggQ3lyZXN0IGludGVyZmFjZSAtIG1ha2Ugc3VyZSB5b3Ugb3BlbiBjeXRvc2NhcGUgd2l0aCBhIC1SIDEyMzQgKHRvIGVuYWJsZSByZXN0IGZ1bmN0aW9uYWxpdHkpIGFuZCBhbGxvdyBSIHRvIHRhbGsgZGlyZWN0bHkgdG8gY3l0b3NjYXBlLgoKTGF1bmNoIEN5dG9zY2FwZSAoYnkgZGVmYXVsdCBjeXRvc2NhcGUgd2lsbCBhdXRvbWF0aWNhbGx5IGVuYWJsZSByZXN0IHNvIGFzIGxvbmcgYXMgY3l0b3NjYXBlIDMuMyBvciBoaWdoZXIgaXMgb3BlbiBSIHNob3VsZCBiZSBhYmxlIHRvIGNvbW11bmljYXRlIHdpdGggaXQpICAKCiMjIFNldCB1cCBjb25uZWN0aW9uIGZyb20gUiB0byBjeXRvc2NhcGUgCmBgYHtyIGluaXRpYWxpemUgY3l0b3NjYXBlIGNvbm5lY3Rpb259CiAgIGN5dG9zY2FwZVBpbmcgKCkKICAgIGN5dG9zY2FwZVZlcnNpb25JbmZvICgpCgpgYGAKCmBgYHtyIGN5dG9zY3BhZV9pbml0LCBpbmNsdWRlPUZBTFNFfQojaW5pdGlhbGl6ZSBjeXRvc2NhcGUKI2NyZWF0ZSBhIG5ldyBzZXNzaW9uIGluIEN5dG9zY2FwZS4gCmN5dG9zY2FwZV9zZXNzaW9uX2ZpbGVuYW1lX2Jhc2UgPC0gcGFzdGUoIk11bHRpX2RhdGFzZXRfZW0iLCJjeXRvc2NhcGVfc2Vzc2lvbi5jeXMiLHNlcD0iXyIpCmN5dG9zY2FwZV9zZXNzaW9uX25hbWVfaG9zdCA8LSBmaWxlLnBhdGgobXVsdGlfZW1fZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjeXRvc2NhcGVfc2Vzc2lvbl9maWxlbmFtZV9iYXNlKQoKI2NsZWFyIHRoZSBwcmV2aW91cyBzZXNzaW9uClJDeTM6OmNsb3NlU2Vzc2lvbihzYXZlLmJlZm9yZS5jbG9zaW5nID0gRkFMU0UpCgojc2F2ZSB0aGUgZW1wdHkgc2Vzc2lvbiB3aXRoIHRoZSBhYm92ZSBuYW1lClJDeTM6OnNhdmVTZXNzaW9uKGN5dG9zY2FwZV9zZXNzaW9uX25hbWVfaG9zdCkKCmBgYAoKCiMjIENyZWF0ZSBhIG11bHRpIGRhdGFzZXQgRU0KYGBge3IgY3JlYXRlIGVucmljaG1lbnQgbWFwfQoKI2RlZmluZWQgdGhyZXNob2xkIGZvciBHU0VBIGVucmljaG1lbnRzIChuZWVkIHRvIGJlIHN0cmluZ3MgZm9yIGN5cmVzdCBjYWxsKQpwdmFsdWVfZ3NlYV90aHJlc2hvbGQgPC0gcGFyYW1zJHB2YWxfdGhyZXNoCnF2YWx1ZV9nc2VhX3RocmVzaG9sZCA8LSBwYXJhbXMkZmRyX3RocmVzaAoKc2ltaWxhcml0eV90aHJlc2hvbGQgPC0gIjAuMzc1IgpzaW1pbGFyaXR5X21ldHJpYyA9ICJDT01CSU5FRCIKCmN1cl9tb2RlbF9uYW1lIDwtICJNdWx0aV9kYXRhc2V0X0VNIgoKZ3NlYV9yZXN1bHRzX3BhdGggPC0gbXVsdGlfZW1fZGlyCgojYWx0aG91Z2ggdGhlcmUgaXMgYSBnbXQgZmlsZSBpbiB0aGUgZ3NlYSBlZGIgcmVzdWx0cyBkaXJlY3RvcnkgaXQgaGF2ZSBiZWVuIGZpbHRlcmVkIHRvIAojY29udGFpbiBvbmx5IGdlbmVzIHJlcHJlc2VudGVkIGluIHRoZSBleHByZXNzaW9uIHNldC4gIElmIHlvdSB1c2UgdGhpcyBmbHRlcmVkIGZpbGUgeW91IAojd2lsbCBnZXQgZGlmZmVyZW50IHBhdGh3YXkgY29ubmVjdGl2aXR5IGRlcGVuZGluZyBvbiB0aGUgZGF0YXNldCBiZWluZyB1c2VkLiAgV2UgcmVjb21tZW5kIAojdXNpbmcgb3JpZ2luYWwgZ210IGZpbGUgdXNlZCBmb3IgdGhlIGdzZWEgYW5hbHlzaXMgYW5kIG5vdCB0aGUgZmlsdGVyZWQgb25lIGluIHRoZSByZXN1bHRzIGRpcmVjdG9yeS4KZ210X2dzZWFfZmlsZSA8LSBmaWxlLnBhdGgoZ2V0d2QoKSxkZXN0X2dtdF9maWxlKQpnc2VhX3Jvb3RfZGlyIDwtIGZpbGUucGF0aChnZXR3ZCgpLG11bHRpX2VtX2RpcikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojY3JlYXRlIEVNCmN1cnJlbnRfbmV0d29ya19uYW1lIDwtIHBhc3RlKGN1cl9tb2RlbF9uYW1lLHB2YWx1ZV9nc2VhX3RocmVzaG9sZCxxdmFsdWVfZ3NlYV90aHJlc2hvbGQsc2VwPSJfIikKCmVtX2NvbW1hbmQgPSBwYXN0ZSgnZW5yaWNobWVudG1hcCBtYXN0ZXJtYXAgY29tbW9uR01URmlsZT0nLGdtdF9nc2VhX2ZpbGUsCiAgICAgICAgICAgICAgICAgICAncHZhbHVlPScscHZhbHVlX2dzZWFfdGhyZXNob2xkLCAncXZhbHVlPScscXZhbHVlX2dzZWFfdGhyZXNob2xkLAogICAgICAgICAgICAgICAgICAgJ3NpbWlsYXJpdHljdXRvZmY9JyxzaW1pbGFyaXR5X3RocmVzaG9sZCwKICAgICAgICAgICAgICAgICAgICdjb2VmZmljaWVudHM9JyxzaW1pbGFyaXR5X21ldHJpYywncm9vdEZvbGRlcj0nLCAKICAgICAgICAgICAgICAgICAgIGdzZWFfcm9vdF9kaXIsCiAgICAgICAgICAgICAgICAgICAnZmlsdGVyQnlFeHByZXNzaW9ucz1mYWxzZScsCiAgICAgICAgICAgICAgICAgICAnY29tbW9uRXhwcmVzc2lvbkZpbGU9JyxmaWxlLnBhdGgoZ2V0d2QoKSxwYXJhbXMkZXhwcmVzc2lvbl9maWxlKSwKICAgICAgICAgICAgICAgICAgIHNlcD0iICIpCgojZW5yaWNobWVudCBtYXAgY29tbWFuZCB3aWxsIHJldHVybiB0aGUgc3VpZCBvZiBuZXdseSBjcmVhdGVkIG5ldHdvcmsuCnJlc3BvbnNlIDwtIFJDeTM6OmNvbW1hbmRzR0VUKGVtX2NvbW1hbmQpCgpjdXJyZW50X25ldHdvcmtfc3VpZCA8LSAwCiNlbnJpY2htZW50IG1hcCBjb21tYW5kIHdpbGwgcmV0dXJuIHRoZSBzdWlkIG9mIG5ld2x5IGNyZWF0ZWQgbmV0d29yayB1bmxlc3MgaXQgRmFpbGVkLiAgCiNJZiBpdCBmYWlsZWQgaXQgd2lsbCBjb250YWluIHRoZSB3b3JkIGZhaWxlZAppZihncmVwbChwYXR0ZXJuPSJGYWlsZWQiLCByZXNwb25zZSkpewogIHBhc3RlKHJlc3BvbnNlKQp9IGVsc2UgewogIGN1cnJlbnRfbmV0d29ya19zdWlkIDwtIHJlc3BvbnNlCn0KCiNjaGVjayB0byBzZWUgaWYgdGhlIG5ldHdvcmsgbmFtZSBpcyB1bmlxdWUKY3VycmVudF9uYW1lcyA8LSBSQ3kzOjpnZXROZXR3b3JrTGlzdCgpCmlmKGN1cnJlbnRfbmV0d29ya19uYW1lICVpbiUgY3VycmVudF9uYW1lcyl7CiAgI2lmIHRoZSBuYW1lIGFscmVhZHkgZXhpc3RzIGluIHRoZSBuZXR3b3JrIG5hbWVzIHRoZW4gcHV0IHRoZSBTVUlEIGluIGZyb250CiAgIyBvZiB0aGUgbmFtZSAodGhpcyBkb2VzIG5vdCB3b3JrIGlmIHlvdSBwdXQgdGhlIHN1aWQgYXQgdGhlIGVuZCBvZiB0aGUgbmFtZSkKICBjdXJyZW50X25ldHdvcmtfbmFtZSA8LSBwYXN0ZShjdXJyZW50X25ldHdvcmtfc3VpZCxjdXJyZW50X25ldHdvcmtfbmFtZSwgIHNlcD0iXyIpCn0KcmVzcG9uc2UgPC0gUkN5Mzo6cmVuYW1lTmV0d29yayh0aXRsZT1jdXJyZW50X25ldHdvcmtfbmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV0d29yayA9IGFzLm51bWVyaWMoY3VycmVudF9uZXR3b3JrX3N1aWQpLGJhc2UudXJsKQoKCmBgYAoKIyMgQ2hhbmdlIHRoZSBjb2xvcmluZyBvbiB0aGUgbmV0d29yawoKQnkgZGVmYXVsdCB0aGUgbmV0d29yayB3aWxsIGNvbG91ciBlYWNoIHNlY3Rpb24gb2YgdGhlIHBpZSB1c2luZyB0aGUgTkVTIHZhbHVlIChHU0VBIGFuYWx5c2lzKSBhbmQgRkRSIHZhbHVlIGZvciBhbnkgb3RoZXIgYW5hbHlzaXMuICBUaGlzIGRvZXNuJ3QgbGV0IHlvdSBzZWUgY2xlYXJseSB3aGljaCBwYXRod2F5cyBhcmUgc2lnbmlmaWNhbnQgaW4gZWFjaCBkYXRhc2V0LiAgQnkgY2hhbmdpbmcgdGhlIGNvbG91cmluZyB0eXBlIHRvICJEQVRBX1NFVCIgZWFjaCBub2RlIHdpbGwgb25seSBiZSBjb2xvdXJlZCBpZiB0aGUgcGF0aHdheSBpcyBzaWduaWZpY2FudCBpbiB0aGUgZ2l2ZW4gZGF0YXNldCAoYnkgdGhlIHVzZXIgZGVmaW5lZCB0aHJlc2hvbGRzKS4KCioqIFRoZSBjb2xvdXJzIGFyZSBzZXQgYnkgZGVmYXVsdC4gIFRoZXJlIGlzIG5vIHByb2dyb21tYXRpYyB3YXkgdG8gY2hhbmdlIHRoZSBjb2xvdXJzLiAgSXQgY2FuIGJlIGRvbmUgbWFudWFsbHkgdGhvdWdoLioqCgpgYGB7cn0KcmVzcG9uc2UgPC0gUkN5Mzo6Y29tbWFuZHNHRVQoJ2VucmljaG1lbnRtYXAgY2hhcnQgZGF0YT0iREFUQV9TRVQiJykKYGBgCgoKIyMgRGVmaW5lIHRoZW1lcwoKQW5ub3RhdGUgdGhlIEVucmljaG1lbnQgbWFwIGluIG9yZGVyIHRvIGNhbGN1bGF0ZSB0aGUgdGhlbWVzCgpgYGB7cn0KcmVzcG9uc2UgPC0gUkN5Mzo6Y29tbWFuZHNHRVQocGFzdGUoJ2F1dG9hbm5vdGF0ZSBhbm5vdGF0ZS1jbHVzdGVyQm9vc3RlZCBuZXR3b3JrPScsY3VycmVudF9uZXR3b3JrX3N1aWQsc2VwPSIiKSkKYGBgCgoKIyMgQ3JlYXRlIGEgc3VtbWFyeSBuZXR3b3JrCgpgYGB7cn0KdGhlbWVfbmV0d29yayA8LSBhcy5udW1lcmljKFJDeTM6OmNvbW1hbmRzR0VUKHBhc3RlKCdhdXRvYW5ub3RhdGUgc3VtbWFyeSBuZXR3b3JrPScsIGN1cnJlbnRfbmV0d29ya19zdWlkLHNlcD0iIikpKQpgYGAKCiMjIEdldCBhbGwgdGhlIHRoZW1lIGluZm9ybWF0aW9uCgpEZXBlbmRpbmcgb24gdGhlIGRhdGEgdHlwZSBvZiB0aGUgbm9kZSBhdHRyaWJ1dGVzIHdoZW4gdGhlIHRoZW1lIG5ldHdvcmsgaXMgY3JlYXRlZCB0aGV5IHdpbGwgYmUgY29sbGFwc2VkIGRpZmZlcmVudGx5LiAgWW91IGNhbiBzZXQgdGhpcyBiZWhhdmlvdXIgbWFudWFsbHkgaW4gY3l0b3NjYXBlIGZyb20gRWRpdCAtPiBQcmVmZXJlbmNlIC0+IEdyb3VwIFByZWZlcmVuY2VzLiAgRm9yIG1vcmUgaW5mbyBvbiB0aGlzIHNlZSBbaGVyZV0oaHR0cDovL21hbnVhbC5jeXRvc2NhcGUub3JnL2VuL3N0YWJsZS9DeXRvc2NhcGVfUHJlZmVyZW5jZXMuaHRtbCNtYW5hZ2luZy1ncm91cC1zZXR0aW5ncykKCmBgYHtyfQogIGRlZmF1bHRfbm9kZV90YWJsZSA8LSBSQ3kzOjpnZXRUYWJsZUNvbHVtbnModGFibGU9ICJub2RlIixuZXR3b3JrID0gdGhlbWVfbmV0d29yaykKYGBgCgoKIyMgT3V0cHV0IGFsbCBUaGVtZXMgZm91bmQgaW4gYWxsIHRoZSBkYXRhc2V0cwoKYGBge3J9CgpudW1fZGF0YXNldHMgPC0gbGVuZ3RoKHJhbmtfZmlsZXMpCgpkYXRhc2V0X2NoYXJ0IDwtIHQoc2FwcGx5KGRlZmF1bHRfbm9kZV90YWJsZSRgRW5yaWNobWVudE1hcDo6RGF0YXNldF9DaGFydGAsIGZ1bmN0aW9uICh4KSB7IHJldHVybiAoeCl9KSkKcm93bmFtZXMoZGF0YXNldF9jaGFydCkgPC0gZGVmYXVsdF9ub2RlX3RhYmxlJG5hbWUKCiNjYWxjdWxhdGUgdGhlIG51bWJlciBvZiBkYXRhc2V0cyBmb3IgZWFjaCB0aGVtZQpkYXRhc2V0X3Blcl90aGVtZSA8LSByb3dTdW1zKGRhdGFzZXRfY2hhcnQpCgojb3V0cHV0IHRoZSB0aGVtZXMgdGhhdCBhcmUgZm91bmQgaW4gdGhlIG1vc3QgZGF0YXNldHMKcm93bmFtZXMoZGF0YXNldF9jaGFydClbd2hpY2goZGF0YXNldF9wZXJfdGhlbWUgPT0gbWF4KGRhdGFzZXRfcGVyX3RoZW1lKSldCmBgYAoKIyMgT3V0cHV0IHRoZSBwYXRod2F5cyBzcGVjaWZpYyB0byBlYWNoIGRhdGFzZXQKYGBge3J9CiNHbyB0aHJvdWdoIGVhY2ggaW5kZXggb2YgdGhlIGRhdGFzZXQgY2hhcnQgYW5kIG91dHB1dCB0aGUgcGF0aHdheXMgc3BlY2lmaWMgZm9yIGVhY2ggZ3JvdXAKc3BlY2lmaWNfdG9fdGhlbWVfc3VtbWFyeSA8LSBsaXN0KCkKZm9yKGkgaW4gMTpkaW0oZGF0YXNldF9jaGFydClbMl0pewogIGN1cnJlbnRfZGF0YXNldCA8LSBpCiAgCiAgc3BlY2lmaWNfdG9fdGhlbWVfc3VtbWFyeVtbaV1dIDwtIHJvd25hbWVzKGRhdGFzZXRfY2hhcnQpW2ludGVyc2VjdCh3aGljaChkYXRhc2V0X3Blcl90aGVtZSA9PSAxKSAsIHdoaWNoKGRhdGFzZXRfY2hhcnRbLGldPT0xKSldCn0KCiN1bmZvcnR1bmF0ZWx5IHRoZXJlIGlzIG5vIHdheSBjdXJyZW50bHkgdG8gZ2V0IHdoaWNoIGlkIGNvcnJlc3BvbmRzIHRvIHdoaWNoIGRhdGFzZXQuICBHdWVzcyB3aGljaCBhY2NvcmRpbmcgdG8gCiMgdGhlIG9yZGVyIG9mIHRoZSBjb2x1bW5zIGluIHRoZSByZXR1cm5lZCB0YWJsZS4KbmFtZXMoc3BlY2lmaWNfdG9fdGhlbWVfc3VtbWFyeSkgPC0gdW5saXN0KGxhcHBseShjb2xuYW1lcyhkZWZhdWx0X25vZGVfdGFibGUpW2dyZXAoY29sbmFtZXMoZGVmYXVsdF9ub2RlX3RhYmxlKSxwYXR0ZXJuPSJwdmFsdWUiKV0sRlVOPWZ1bmN0aW9uKHgpe3VubGlzdChzdHJzcGxpdCh4LCBzcGxpdCA9IiAiKSlbMl19KSkKCnNwZWNpZmljX3RvX3RoZW1lX3N1bW1hcnkKCmBgYAoK