1 Supplementary Protocol 3 – Pathway Enrichment Analysis in R using ROAST and Camera

Bioinformatics analysis is often carried out in R. Pathway enrichment analysis can be also performed directly in R. This protocol will demonstrate the use of R packages ROAST1 and Camera2. Each method requires an expressionSet that minimally contains a matrix of expression values for a set of genes and conditions. The expression matrix generated in supplementary protocol part 1 or 2 is suitable for the analysis.

1.1 Pathway Enrichment Analysis in R

1.1.1 Load required packages

  1. Load required Bioconductor packages into R and set working folder to the location of Supplementary Files 1-4.
#working_dir <- “path/to/data”
working_dir <- "./data"

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

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

tryCatch(expr = { library("RCurl")}, 
         error = function(e) { 
          install.packages("RCurl")}, 
         finally = library("RCurl"))

# This protocol can use RNA-seq expression data or microarray expression data. 
#Specify which you would like to use
dataType_rnaseq <- TRUE

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

#string to name the analysis
analysis_name <- "Mesen_vs_Immuno"

#from Supplementary protocol 1
expression_file <- "Supplementary_Table6_TCGA_OV_RNAseq_expression.txt"
  1. Load in the gene sets from a GMT file.

1.1.2 Download the latest pathway definition file from the updated baderlab gene set files

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.

This can be done automatically through R or can be done manually through the website. To download it programmatically follow below:

gmt_url = "http://download.baderlab.org/EM_Genesets/current_release/Human/symbol/"

#list all the files on the server
filenames = getURL(gmt_url)
tc = textConnection(filenames)
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(working_dir,paste("Supplementary_Table3_",gmt_file,sep="") )

download.file(
    paste(gmt_url,gmt_file,sep=""),
    destfile=dest_gmt_file
)

Load in the newly downloaded GMT file

#if you haven't automatically downloaded the gmt file set 
#the path to the gmt file below.
gmt_file <- dest_gmt_file

capture.output(
   genesets <- GSA.read.gmt(gmt_file),
    file="./gsea_load_output.txt"
   )

1.1.3 Create dataset to use with Camera2 and ROAST1

  1. Camera2 and ROAST1 expect the gene sets to be a list of vectors where the slot name of each vector corresponds to the gene-set identifier, i.e. the name of the gene set however the GSA.read.gmt method loads the GMT file as an object with a list of gene set names and a list of gene sets. Add the gene set names to the gene sets vector to create a list of vectors required by ROAST1 and Camera2.
names(genesets$genesets) <- genesets$geneset.names
  1. Specify the expression dataset to be used for the analysis. You can use the MinimalSet from Supplementary protocol 1A or the DGEList variable d from Supplementary protocol 1B. For our RNA-seq dataset, each row of the expression set is annotated with gene symbol and EntrezGene ID separated by “|”. To match the gene set file we need to remove the EntrezGene IDs from the row names. We choose to use gene symbols to simplify interpretation of enriched pathways and associated genes.

(optional) Recreate the DGEList from supplementary protocol rnaseq (Steps 1 -5 in Supplementary protocol 1 rnaseq) or if you are within the same sesison you can use the d variable.

if(dataType_rnaseq){
      tryCatch(expr = { library("edgeR")}, 
               error = function(e) { source("https://bioconductor.org/biocLite.R")
                 biocLite("edgeR")}, 
               finally = library("edgeR"))
      
      RNAseq <- read.table( 
        file.path(working_dir,"Supplementary_Table10_TCGA_RNASeq_rawcounts.txt"), 
        header = TRUE, sep = "\t", quote="\"", stringsAsFactors = FALSE)
      classDefinitions_RNAseq <-read.table(
        file.path(working_dir,"Supplementary_Table11_RNASeq_classdefinitions.txt"),
        header = TRUE,  sep = "\t", quote="\"", stringsAsFactors = FALSE)
      
      cpms <- cpm(RNAseq)
      keep <- rowSums(cpms > 1) >= 50
      counts <- RNAseq [keep,]
      
      #round counts to create whole numbers
      counts <- round(counts)
      
      exclude <- rownames(counts)[union(grep("\\?",rownames(counts)), grep("^LOC", 
                                                              rownames(counts)))]
      counts <- counts[which(!rownames(counts) %in% exclude),]
      
      d <- DGEList(counts=counts, group=classDefinitions_RNAseq[,data_classes])
      d <- calcNormFactors(d)
      
      d <- estimateCommonDisp(d)
      d <- estimateTagwiseDisp(d)
      
      temp_names <- rownames(d)
      rownames(d) <- 
      unlist(lapply(temp_names,function(x){ unlist(strsplit(x,"\\|"))[1]}))
      
      data_for_gs_analysis <- d
      
      classes <- data_for_gs_analysis$samples$group
}

Or alternately load data as a MinimalSet from microarray data (Steps 9 - 14 in Supplementary protocol 2 microarray)

if(!dataType_rnaseq){
  tryCatch(expr = { library("Biobase")}, 
         error = function(e) { 
           source("https://bioconductor.org/biocLite.R")
           biocLite("Biobase")}, 
         finally = library("Biobase"))
  
    #load in files
    expressionMatrix <- as.matrix(read.table(
      file.path(working_dir, "Supplementary_Table12_TCGA_Microarray_rmanormalized.txt"), 
      header = TRUE, sep = "\t", quote="\"", stringsAsFactors = FALSE))
    classDefinitions <- read.table( 
      file.path(working_dir,"Supplementary_Table13_Microarray_classdefinitions.txt"), 
      header = TRUE, sep = "\t", quote="\"", stringsAsFactors = FALSE)
    identical(colnames(expressionMatrix), classDefinitions$patient)
    
    #create minimal set
    minimalSet <- ExpressionSet(assayData=expressionMatrix)
    classes <- factor(classDefinitions[,data_classes])
    
    #create model
    modelDesign <- model.matrix(~ 0 + classes)
        
    #assign data set for the analysis
    data_for_gs_analysis <- minimalSet
}

If you haven’t defined the data above using RNA-seq data or microarray data, Set data to be your DGEList of MinimalSet.

#Should be a minimalSet or DGEList
#data_for_gs_analysis <- data you want to use for the analysis.  
#classes <- class definitions for each of the samples in the dataset

1.1.4 Filter gene sets to only contain genes found in the analysis set

  1. Camera2 and ROAST1 require that the gene sets are filtered such that all genes in gene sets have expression values in the dataset. Use function in limma (ids2indices) to convert gene identifiers in the geneset to indices in the dataset.
genesets_filtered <- ids2indices(genesets$genesets, rownames(data_for_gs_analysis), 
remove.empty=TRUE)
  1. Filter the gene sets according to their size, following the previous step of filtering by availability of expression data. Here we only include sets with more than 10 and less than 500 genes.
geneset_sizes <- unlist(lapply(genesets_filtered, length))
geneset_indices <- which(geneset_sizes>=15 & geneset_sizes<200)

1.1.5 Create design matrix and contrast

  1. Create the design matrix and contrast we want to test for. In this example we are looking for pathways differential between the Mesenchymal and Immunoreactive subtypes.

design <- model.matrix(~ 0 + classes)

contrast_mesenvsimmuno <- makeContrasts(
  mesenvsimmuno ="classesImmunoreactive-classesMesenchymal",levels=design)

1.1.6 Run ROAST1

  1. Run enrichment analysis and format the results to the ‘generic’ file format of Enrichment Map. This is a tab-delimited file that includes a gene set name, gene set description, p-value, FDR, phenotype and a comma-separated list of associated genes for every detected pathway. Depending on your dataset and computer, this command could take from a few minutes to an hour to run. If you receive the warning “In dnbinom(q, size = size, mu = mu, log = TRUE) : non-integer x”, the software has encountered unexpected non-integer values of gene expression, often indicating problems with upstream analysis such as sub optimal pre-processing or normalization procedures. A simple fix of rounding gene expression values may fix the error (data_for_gs_analysis\(counts <- round(data_for_gs_analysis\)counts)), however it should be investigated further.
mroast_results <- mroast(data_for_gs_analysis, genesets_filtered[geneset_indices],
                         design,contrast=contrast_mesenvsimmuno, nrot=10000)
mroast_descr <- unlist(lapply(rownames(mroast_results), 
                              function(x){unlist(strsplit(x,"\\%"))[1]}))

1.1.7 Create generic enrichment map file from ROAST1 results

In order to create an enrichment map from the results of the ROAST analysis we need to create a file that can be uploaded into the EnrichmentMap and translated into a network. EnrichmentMap requires a file that contains the pathway name, description, p-value associated with the enrichment, corrected p-value associated with the enrichment, a phenotype and a list of genes associated with the given pathway. The list of genes is optional bu in the absence of a list of genes a gmt file must be provided. If any of the required columns are missig from your analysis you can supply fake values in order to build an enrichment map.

  1. Inspect the results returned from ROAST1. The column “Direction” shows whether the gene set is enriched for up-regulated genes or down-regulated genes. To ensure compatibility with EnrichmentMap, convert these values such that 1 represents up-regulated -1 represents down-regulated genes.
mroast_results_file <- "mroast_results_generic_em.txt"

Phenotype <- unlist(lapply(mroast_results[,"Direction"],function(x)
    {if(x=="Up"){1}else{(-1)}}))
genes <- c()
for(i in 1:length(rownames(mroast_results))){
    current_geneset <- unlist(genesets_filtered
        [ which( names(genesets_filtered) %in% rownames(mroast_results)[i])])
    current_genes <- c()
        for(j in 1:length(current_geneset)){
            if(j==length(current_geneset)){
                current_genes <- paste(current_genes, 
                    rownames(data_for_gs_analysis)[current_geneset[j]], 
sep="")
            } else {
                current_genes <- paste(current_genes, 
                    rownames(data_for_gs_analysis)[current_geneset[j]],
",", sep="")
            }
        }
        genes <- rbind(genes, current_genes)
    }
rownames(genes) <- rownames(mroast_results)

mroast_results_generic_em <- data.frame( rownames(mroast_results), mroast_descr, 
    PValue=mroast_results[,"PValue"], FDR=mroast_results[,"FDR"], Phenotype, genes)
write.table(mroast_results_generic_em, file.path(working_dir,mroast_results_file), 
    col.name=TRUE, sep="\t", row.names=FALSE, quote=FALSE) 

1.1.8 Run Camera2

  1. Run pathway enrichment analysis with the Camera2 R package. The analysis starts with the same files as ROAST1 (see first four first steps of Supplementary Protocol 1C).
camera_results_file <- "camera_results_generic_em.txt"

1.1.9 Create generic enrichment map file from Camera2 results

camera_results <- camera(data_for_gs_analysis, 
    genesets_filtered[geneset_indices], design, contrast=contrast_mesenvsimmuno)
camera_descr <- unlist(lapply(rownames(camera_results), 
    function(x){unlist(strsplit(x,"\\%"))[1]}))
camera_Phenotype <- unlist(lapply(camera_results[,"Direction"], 
    function(x){if(x=="Up"){1}else{(-1)}}))


camera_genes <- c()
for(i in 1:length(rownames(camera_results))){
    current_geneset <- unlist( 
        genesets_filtered[ which( names( genesets_filtered ) %in% 
                                    rownames(camera_results)[i])])
    current_genes <- c()
    for(j in 1:length(current_geneset)){
        if(j==length(current_geneset)){
            current_genes <- paste( current_genes, 
                rownames(data_for_gs_analysis) [current_geneset[j]],
sep="")
        } else {
            current_genes <- paste( current_genes, 
                rownames(data_for_gs_analysis)[ current_geneset[j]], ",", 
sep="")
        }
    }
    camera_genes <- rbind(camera_genes, current_genes)
}
rownames(camera_genes) <- rownames(camera_results)

camera_results_generic_em <- data.frame(rownames(camera_results), camera_descr, 
    PValue = camera_results[,"PValue"], FDR=camera_results[,"FDR"], Phenotype, genes )
write.table(camera_results_generic_em, file.path(working_dir,camera_results_file), 
    col.name=TRUE, sep="\t", row.names=FALSE, quote=FALSE)

1.1.10 Create Enrichment Map

The results from Camera2 or ROAST1 can be input to Enrichment Map, following the protocol(start from step 6) in the main text.

1.1.11 (Optional) Create an Enrichment map directly from R

Optional: Build Enrichment map from the above results

Instead of creating an enrichment map through the Cytoscape user interface it is also possible to create it directly from R using cyrest commands. Below is an example of how to create an enrichment map directly from R using the Camera2 and ROAST1 results that have been created in this protocol.

Make sure that you have launch Cytoscape and installed all required apps as listed in the main Protocol (Step 1 - 4)

Build a network with Camera2 results:

#use easy cyRest library to communicate with cytoscape.

tryCatch(expr = { library(RCy3)}, 
         error = function(e) { install_github("cytoscape/RCy3")}, finally = library(RCy3))

#defined threshold for GSEA enrichments (need to be strings for cyrest call)
pvalue_threshold <- "0.05"
qvalue_threshold <- "0.001"

similarity_threshold <- "0.25"
similarity_metric = "JACCARD"

generic_gmt_file <- file.path(getwd(),gmt_file)

cur_model_name <- paste("camera",analysis_name,sep="_")
results_filename <- file.path(getwd(),working_dir,camera_results_file)

#######################################
#create EM - camera results
#######################################
current_network_name <- paste(cur_model_name,pvalue_threshold,qvalue_threshold,sep="_")

em_command = paste('enrichmentmap build analysisType="generic"',
                   'gmtFile=',generic_gmt_file,
                   'pvalue=',pvalue_threshold,
                   'qvalue=',qvalue_threshold,
                   'similaritycutoff=',similarity_threshold,
                   'coefficients=',similarity_metric,
                   'enrichmentsDataset1=',results_filename,
                   '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
}

response <- renameNetwork(current_network_name, as.numeric(current_network_suid))

When building a network if the commandsGet returns an error similar to this:

“RCy3::commandsGET, HTTP Error Code: 500

url=http://localhost:1234/v1/commands/enrichmentmap/build?analysisType=generic

&gmtFile=[path/to/file]/data/Supplementary_Table3_Human_GOBP_

AllPathways_no_GO_iea_March_01_2018_symbol.gmt

&pvalue=%200.05&qvalue=%200.0001&similaritycutoff=%200.25&coefficients=%20JACCARD

&enrichmentsDataset1=[path/to/file]/data/mroast_results_generic_em.txt

&expressionDataset1=[path/to/file]/data/Supplementary_Table6_TCGA_OV_RNAseq_expression.txt

Error in commandsGET(em_command) : "

Copy the url (for example, from the above error use:

http://localhost:1234/v1/commands/enrichmentmap/build?analysisType=generic

&gmtFile=[path/to/file]/data/Supplementary_Table3_Human_GOBP_

AllPathways_no_GO_iea_March_01_2018_symbol.gmt

&pvalue=%200.05&qvalue=%200.0001&similaritycutoff=%200.25&coefficients=%20JACCARD

&enrichmentsDataset1=[path/to/file]/data/mroast_results_generic_em.txt

&expressionDataset1=[path/to/file]/data/Supplementary_Table6_TCGA_OV_RNAseq_expression.txt“)

and paste it into a web browser to get a more descriptive error message. Do not copy the above link to your web browser. The url is specific to the machine you have run the notebook on. copy the url from your error message.

Sometimes the above error will come back when everything is fine. If there are no results returned because nothing passes the thresholds you specified the above error with appear in R. In the web browser after following the above link will show the error “Failed: None of the gene sets have passed the filter. Try relaxing the gene set filter parameters.”

Build a network with the ROAST1 results


cur_model_name <- paste("roast",analysis_name,sep="_")
results_filename <- file.path(getwd(),working_dir,mroast_results_file)

#######################################
#create EM -roast results
#######################################
current_network_name <- paste(cur_model_name,pvalue_threshold,qvalue_threshold,sep="_")

em_command = paste('enrichmentmap build analysisType="generic"',
                   'gmtFile=',generic_gmt_file,
                   'pvalue=',pvalue_threshold, 
                   'qvalue=',qvalue_threshold,
                   'similaritycutoff=',similarity_threshold,
                   'coefficients=',similarity_metric,
                   'enrichmentsDataset1=',results_filename,
                   '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
}

response <- renameNetwork(current_network_name, network = as.numeric(current_network_suid))

1. Wu, D. et al. ROAST: Rotation gene set tests for complex microarray experiments. Bioinformatics 26, 2176–82 (2010).

2. Wu, D. & Smyth, G. K. Camera: A competitive gene set test accounting for inter-gene correlation. Nucleic Acids Res 40, e133 (2012).

LS0tCnRpdGxlOiAiU3VwcGxlbWVudGFyeSBQcm90b2NvbCAzIOKAkyBQYXRod2F5IEVucmljaG1lbnQgQW5hbHlzaXMgaW4gUiB1c2luZyBST0FTVCBhbmQgQ2FtZXJhIgphdXRob3I6ICJSdXRoIElzc2VybGluIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6ICczJwogIGh0bWxfZG9jdW1lbnQ6CiAgICBoaWdobGlnaDogaGFkZG9jawogICAga2VlcF9tZDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHBhcGVyCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogbm8KICAgICAgc21vb3RoX3Njcm9sbDogbm8KICBodG1sX25vdGVib29rOgogICAgaGlnaGxpZ2g6IGhhZGRvY2sKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogcGFwZXIKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBubwogICAgICBzbW9vdGhfc2Nyb2xsOiBubwpiaWJsaW9ncmFwaHk6IHN1cF9wcm90b2NvbDFfcmVmZXJlbmNlcy5iaWIKY3NsOiBuYXR1cmUtcHJvdG9jb2xzLmNzbAotLS0KIyBTdXBwbGVtZW50YXJ5IFByb3RvY29sIDMg4oCTIFBhdGh3YXkgRW5yaWNobWVudCBBbmFseXNpcyBpbiBSIHVzaW5nIFJPQVNUIGFuZCBDYW1lcmEKCkJpb2luZm9ybWF0aWNzIGFuYWx5c2lzIGlzIG9mdGVuIGNhcnJpZWQgb3V0IGluIFIuIFBhdGh3YXkgZW5yaWNobWVudCBhbmFseXNpcyBjYW4gYmUgYWxzbyBwZXJmb3JtZWQgZGlyZWN0bHkgaW4gUi4gVGhpcyBwcm90b2NvbCB3aWxsIGRlbW9uc3RyYXRlIHRoZSB1c2Ugb2YgUiBwYWNrYWdlcyBST0FTVFtAcm9hc3RdIGFuZCBDYW1lcmFbQGNhbWVyYV0uIEVhY2ggbWV0aG9kIHJlcXVpcmVzIGFuIGV4cHJlc3Npb25TZXQgdGhhdCBtaW5pbWFsbHkgY29udGFpbnMgYSBtYXRyaXggb2YgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGEgc2V0IG9mIGdlbmVzIGFuZCBjb25kaXRpb25zLiBUaGUgZXhwcmVzc2lvbiBtYXRyaXggZ2VuZXJhdGVkIGluIHN1cHBsZW1lbnRhcnkgcHJvdG9jb2wgcGFydCAxIG9yIDIgaXMgc3VpdGFibGUgZm9yIHRoZSBhbmFseXNpcy4gCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpjaGVjaz1mdW5jdGlvbih4KSB0cnlDYXRjaChpZihjbGFzcyh4KSA9PSAnbG9naWNhbCcpIDEgZWxzZSAxLCBlcnJvcj1mdW5jdGlvbihlKSAwKSAKaWYoY2hlY2soYWRkX3NldHVwKSA9PSAwKXsKICBhZGRfc2V0dXAgPSBUUlVFCn0KYGBgCgoKYGBge3IgYXV0b2RvYywgY2hpbGQ9J3N1cHBsZW1lbnRhcnlfcHJvdG9jb2xzMTIzX3NldHVwLlJtZCcsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9CmBgYCAKCgojIyBQYXRod2F5IEVucmljaG1lbnQgQW5hbHlzaXMgaW4gUgoKIyMjIExvYWQgcmVxdWlyZWQgcGFja2FnZXMKCjEuIExvYWQgcmVxdWlyZWQgQmlvY29uZHVjdG9yIHBhY2thZ2VzIGludG8gUiBhbmQgc2V0IHdvcmtpbmcgZm9sZGVyIHRvIHRoZSBsb2NhdGlvbiBvZiBTdXBwbGVtZW50YXJ5IEZpbGVzIDEtNC4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojd29ya2luZ19kaXIgPC0g4oCccGF0aC90by9kYXRh4oCdCndvcmtpbmdfZGlyIDwtICIuL2RhdGEiCgp0cnlDYXRjaChleHByID0geyBsaWJyYXJ5KCJsaW1tYSIpfSwgCiAgICAgICAgIGVycm9yID0gZnVuY3Rpb24oZSkgeyBzb3VyY2UoImh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9iaW9jTGl0ZS5SIikKICAgICAgICAgICBiaW9jTGl0ZSgibGltbWEiKX0sIAogICAgICAgICBmaW5hbGx5ID0gbGlicmFyeSgibGltbWEiKSkKCnRyeUNhdGNoKGV4cHIgPSB7IGxpYnJhcnkoIkdTQSIpfSwgCiAgICAgICAgIGVycm9yID0gZnVuY3Rpb24oZSkgeyBzb3VyY2UoImh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9iaW9jTGl0ZS5SIikKICAgICAgICAgICBiaW9jTGl0ZSgiR1NBIil9LCAKICAgICAgICAgZmluYWxseSA9IGxpYnJhcnkoIkdTQSIpKQoKdHJ5Q2F0Y2goZXhwciA9IHsgbGlicmFyeSgiUkN1cmwiKX0sIAogICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIHsgCiAgICAgICAgICBpbnN0YWxsLnBhY2thZ2VzKCJSQ3VybCIpfSwgCiAgICAgICAgIGZpbmFsbHkgPSBsaWJyYXJ5KCJSQ3VybCIpKQoKIyBUaGlzIHByb3RvY29sIGNhbiB1c2UgUk5BLXNlcSBleHByZXNzaW9uIGRhdGEgb3IgbWljcm9hcnJheSBleHByZXNzaW9uIGRhdGEuIAojU3BlY2lmeSB3aGljaCB5b3Ugd291bGQgbGlrZSB0byB1c2UKZGF0YVR5cGVfcm5hc2VxIDwtIFRSVUUKCiNUaGUgZmllbGQgaW4gdGhlIGNsYXNzIGRlZmluaXRpb24gZmlsZSB0aGF0IGRlZmluZXMgdGhlIGNsYXNzZXMgb2YgdGhlIGRhdGEuCmRhdGFfY2xhc3NlcyA8LSAiU1VCVFlQRSIKCiNzdHJpbmcgdG8gbmFtZSB0aGUgYW5hbHlzaXMKYW5hbHlzaXNfbmFtZSA8LSAiTWVzZW5fdnNfSW1tdW5vIgoKI2Zyb20gU3VwcGxlbWVudGFyeSBwcm90b2NvbCAxCmV4cHJlc3Npb25fZmlsZSA8LSAiU3VwcGxlbWVudGFyeV9UYWJsZTZfVENHQV9PVl9STkFzZXFfZXhwcmVzc2lvbi50eHQiCgpgYGAKCjIuIExvYWQgaW4gdGhlIGdlbmUgc2V0cyBmcm9tIGEgR01UIGZpbGUuCgojIyMgRG93bmxvYWQgdGhlIGxhdGVzdCBwYXRod2F5IGRlZmluaXRpb24gZmlsZSBmcm9tIHRoZSB1cGRhdGVkIGJhZGVybGFiIGdlbmUgc2V0IGZpbGVzCk9ubHkgSHVtYW4sIE1vdXNlIGFuZCBSYXQgZ2VuZSBzZXQgZmlsZXMgYXJlIGN1cnJlbnRseSBhdmFpbGFibGUgb24gdGhlIEJhZGVybGFiIGRvd25sb2FkcyBzaXRlLiAgSWYgeW91IGFyZSB3b3JraW5nIHdpdGggYSBzcGVjaWVzIG90aGVyIHRoYW4gaHVtYW4gKGFuZCBpdCBpcyBlaXRoZXIgcmF0IG9yIG1vdXNlKSBjaGFuZ2UgdGhlIGdtdF91cmwgYmVsb3cgdG8gY29ycmVjdCBzcGVjaWVzLiBDaGVjayBbaGVyZV0oaHR0cDovL2Rvd25sb2FkLmJhZGVybGFiLm9yZy9FTV9HZW5lc2V0cy9jdXJyZW50X3JlbGVhc2UvKSB0byBzZWUgYWxsIGF2YWlsYWJsZSBzcGVjaWVzLiAKClRoaXMgY2FuIGJlIGRvbmUgYXV0b21hdGljYWxseSB0aHJvdWdoIFIgb3IgY2FuIGJlIGRvbmUgbWFudWFsbHkgdGhyb3VnaCB0aGUgd2Vic2l0ZS4KVG8gZG93bmxvYWQgaXQgcHJvZ3JhbW1hdGljYWxseSBmb2xsb3cgYmVsb3c6CgpgYGB7cn0KZ210X3VybCA9ICJodHRwOi8vZG93bmxvYWQuYmFkZXJsYWIub3JnL0VNX0dlbmVzZXRzL2N1cnJlbnRfcmVsZWFzZS9IdW1hbi9zeW1ib2wvIgoKI2xpc3QgYWxsIHRoZSBmaWxlcyBvbiB0aGUgc2VydmVyCmZpbGVuYW1lcyA9IGdldFVSTChnbXRfdXJsKQp0YyA9IHRleHRDb25uZWN0aW9uKGZpbGVuYW1lcykKY29udGVudHMgPSByZWFkTGluZXModGMpCmNsb3NlKHRjKQoKI2dldCB0aGUgZ210IHRoYXQgaGFzIGFsbCB0aGUgcGF0aHdheXMgYW5kIGRvZXMgbm90IGluY2x1ZGUgdGVybXMgCiNpbmZlcnJlZCBmcm9tIGVsZWN0cm9uaWMgYW5ub3RhdGlvbnMoSUVBKQojc3RhcnQgd2l0aCBnbXQgZmlsZSB0aGF0IGhhcyBwYXRod2F5cyBvbmx5CnJ4ID0gZ3JlZ2V4cHIoIig/PD08YSBocmVmPVwiKSguKi5HT0JQX0FsbFBhdGh3YXlzX25vX0dPX2llYS4qLikoLmdtdCkoPz1cIj4pIiwKICBjb250ZW50cywgcGVybCA9IFRSVUUpCmdtdF9maWxlID0gdW5saXN0KHJlZ21hdGNoZXMoY29udGVudHMsIHJ4KSkKCmRlc3RfZ210X2ZpbGUgPC0gZmlsZS5wYXRoKHdvcmtpbmdfZGlyLHBhc3RlKCJTdXBwbGVtZW50YXJ5X1RhYmxlM18iLGdtdF9maWxlLHNlcD0iIikgKQoKZG93bmxvYWQuZmlsZSgKICAgIHBhc3RlKGdtdF91cmwsZ210X2ZpbGUsc2VwPSIiKSwKICAgIGRlc3RmaWxlPWRlc3RfZ210X2ZpbGUKKQoKYGBgCkxvYWQgaW4gdGhlIG5ld2x5IGRvd25sb2FkZWQgR01UIGZpbGUKYGBge3J9CiNpZiB5b3UgaGF2ZW4ndCBhdXRvbWF0aWNhbGx5IGRvd25sb2FkZWQgdGhlIGdtdCBmaWxlIHNldCAKI3RoZSBwYXRoIHRvIHRoZSBnbXQgZmlsZSBiZWxvdy4KZ210X2ZpbGUgPC0gZGVzdF9nbXRfZmlsZQoKY2FwdHVyZS5vdXRwdXQoCiAgIGdlbmVzZXRzIDwtIEdTQS5yZWFkLmdtdChnbXRfZmlsZSksCiAgICBmaWxlPSIuL2dzZWFfbG9hZF9vdXRwdXQudHh0IgogICApCgpgYGAKCiMjIyBDcmVhdGUgZGF0YXNldCB0byB1c2Ugd2l0aCBDYW1lcmFbQGNhbWVyYV0gYW5kIFJPQVNUW0Byb2FzdF0KMy4gQ2FtZXJhW0BjYW1lcmFdIGFuZCBST0FTVFtAcm9hc3RdIGV4cGVjdCB0aGUgZ2VuZSBzZXRzIHRvIGJlIGEgbGlzdCBvZiB2ZWN0b3JzIHdoZXJlIHRoZSBzbG90IG5hbWUgb2YgZWFjaCB2ZWN0b3IgY29ycmVzcG9uZHMgdG8gdGhlIGdlbmUtc2V0IGlkZW50aWZpZXIsIGkuZS4gdGhlIG5hbWUgb2YgdGhlIGdlbmUgc2V0IGhvd2V2ZXIgdGhlIEdTQS5yZWFkLmdtdCBtZXRob2QgbG9hZHMgdGhlIEdNVCBmaWxlIGFzIGFuIG9iamVjdCB3aXRoIGEgbGlzdCBvZiBnZW5lIHNldCBuYW1lcyBhbmQgYSBsaXN0IG9mIGdlbmUgc2V0cy4gQWRkIHRoZSBnZW5lIHNldCBuYW1lcyB0byB0aGUgZ2VuZSBzZXRzIHZlY3RvciB0byBjcmVhdGUgYSBsaXN0IG9mIHZlY3RvcnMgcmVxdWlyZWQgYnkgUk9BU1RbQHJvYXN0XSBhbmQgQ2FtZXJhW0BjYW1lcmFdLgoKYGBge3J9Cm5hbWVzKGdlbmVzZXRzJGdlbmVzZXRzKSA8LSBnZW5lc2V0cyRnZW5lc2V0Lm5hbWVzCmBgYAoKNC4gU3BlY2lmeSB0aGUgZXhwcmVzc2lvbiBkYXRhc2V0IHRvIGJlIHVzZWQgZm9yIHRoZSBhbmFseXNpcy4gWW91IGNhbiB1c2UgdGhlIE1pbmltYWxTZXQgZnJvbSBTdXBwbGVtZW50YXJ5IHByb3RvY29sIDFBIG9yIHRoZSBER0VMaXN0IHZhcmlhYmxlIGQgZnJvbSBTdXBwbGVtZW50YXJ5IHByb3RvY29sIDFCLiBGb3Igb3VyIFJOQS1zZXEgZGF0YXNldCwgZWFjaCByb3cgb2YgdGhlIGV4cHJlc3Npb24gc2V0IGlzIGFubm90YXRlZCB3aXRoIGdlbmUgc3ltYm9sIGFuZCBFbnRyZXpHZW5lIElEIHNlcGFyYXRlZCBieSDigJx84oCdLiBUbyBtYXRjaCB0aGUgZ2VuZSBzZXQgZmlsZSB3ZSBuZWVkIHRvIHJlbW92ZSB0aGUgRW50cmV6R2VuZSBJRHMgZnJvbSB0aGUgcm93IG5hbWVzLiBXZSBjaG9vc2UgdG8gdXNlIGdlbmUgc3ltYm9scyB0byBzaW1wbGlmeSBpbnRlcnByZXRhdGlvbiBvZiBlbnJpY2hlZCBwYXRod2F5cyBhbmQgYXNzb2NpYXRlZCBnZW5lcy4KCioqKG9wdGlvbmFsKSBSZWNyZWF0ZSB0aGUgREdFTGlzdCBmcm9tIHN1cHBsZW1lbnRhcnkgcHJvdG9jb2wgcm5hc2VxIChTdGVwcyAxIC01IGluIFN1cHBsZW1lbnRhcnkgcHJvdG9jb2wgMSBybmFzZXEpIG9yIGlmIHlvdSBhcmUgd2l0aGluIHRoZSBzYW1lIHNlc2lzb24geW91IGNhbiB1c2UgdGhlIGQgdmFyaWFibGUuKioKYGBge3J9CmlmKGRhdGFUeXBlX3JuYXNlcSl7CiAgICAgIHRyeUNhdGNoKGV4cHIgPSB7IGxpYnJhcnkoImVkZ2VSIil9LCAKICAgICAgICAgICAgICAgZXJyb3IgPSBmdW5jdGlvbihlKSB7IHNvdXJjZSgiaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL2Jpb2NMaXRlLlIiKQogICAgICAgICAgICAgICAgIGJpb2NMaXRlKCJlZGdlUiIpfSwgCiAgICAgICAgICAgICAgIGZpbmFsbHkgPSBsaWJyYXJ5KCJlZGdlUiIpKQogICAgICAKICAgICAgUk5Bc2VxIDwtIHJlYWQudGFibGUoIAogICAgICAgIGZpbGUucGF0aCh3b3JraW5nX2RpciwiU3VwcGxlbWVudGFyeV9UYWJsZTEwX1RDR0FfUk5BU2VxX3Jhd2NvdW50cy50eHQiKSwgCiAgICAgICAgaGVhZGVyID0gVFJVRSwgc2VwID0gIlx0IiwgcXVvdGU9IlwiIiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQogICAgICBjbGFzc0RlZmluaXRpb25zX1JOQXNlcSA8LXJlYWQudGFibGUoCiAgICAgICAgZmlsZS5wYXRoKHdvcmtpbmdfZGlyLCJTdXBwbGVtZW50YXJ5X1RhYmxlMTFfUk5BU2VxX2NsYXNzZGVmaW5pdGlvbnMudHh0IiksCiAgICAgICAgaGVhZGVyID0gVFJVRSwgIHNlcCA9ICJcdCIsIHF1b3RlPSJcIiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKICAgICAgCiAgICAgIGNwbXMgPC0gY3BtKFJOQXNlcSkKICAgICAga2VlcCA8LSByb3dTdW1zKGNwbXMgPiAxKSA+PSA1MAogICAgICBjb3VudHMgPC0gUk5Bc2VxIFtrZWVwLF0KICAgICAgCiAgICAgICNyb3VuZCBjb3VudHMgdG8gY3JlYXRlIHdob2xlIG51bWJlcnMKICAgICAgY291bnRzIDwtIHJvdW5kKGNvdW50cykKICAgICAgCiAgICAgIGV4Y2x1ZGUgPC0gcm93bmFtZXMoY291bnRzKVt1bmlvbihncmVwKCJcXD8iLHJvd25hbWVzKGNvdW50cykpLCBncmVwKCJeTE9DIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXMoY291bnRzKSkpXQogICAgICBjb3VudHMgPC0gY291bnRzW3doaWNoKCFyb3duYW1lcyhjb3VudHMpICVpbiUgZXhjbHVkZSksXQogICAgICAKICAgICAgZCA8LSBER0VMaXN0KGNvdW50cz1jb3VudHMsIGdyb3VwPWNsYXNzRGVmaW5pdGlvbnNfUk5Bc2VxWyxkYXRhX2NsYXNzZXNdKQogICAgICBkIDwtIGNhbGNOb3JtRmFjdG9ycyhkKQogICAgICAKICAgICAgZCA8LSBlc3RpbWF0ZUNvbW1vbkRpc3AoZCkKICAgICAgZCA8LSBlc3RpbWF0ZVRhZ3dpc2VEaXNwKGQpCiAgICAgIAogICAgICB0ZW1wX25hbWVzIDwtIHJvd25hbWVzKGQpCiAgICAgIHJvd25hbWVzKGQpIDwtIAogICAgICB1bmxpc3QobGFwcGx5KHRlbXBfbmFtZXMsZnVuY3Rpb24oeCl7IHVubGlzdChzdHJzcGxpdCh4LCJcXHwiKSlbMV19KSkKICAgICAgCiAgICAgIGRhdGFfZm9yX2dzX2FuYWx5c2lzIDwtIGQKICAgICAgCiAgICAgIGNsYXNzZXMgPC0gZGF0YV9mb3JfZ3NfYW5hbHlzaXMkc2FtcGxlcyRncm91cAp9CmBgYAoKT3IgYWx0ZXJuYXRlbHkgbG9hZCBkYXRhIGFzIGEgTWluaW1hbFNldCBmcm9tIG1pY3JvYXJyYXkgZGF0YSAoU3RlcHMgOSAtIDE0IGluIFN1cHBsZW1lbnRhcnkgcHJvdG9jb2wgMiBtaWNyb2FycmF5KQoKYGBge3J9CmlmKCFkYXRhVHlwZV9ybmFzZXEpewogIHRyeUNhdGNoKGV4cHIgPSB7IGxpYnJhcnkoIkJpb2Jhc2UiKX0sIAogICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIHsgCiAgICAgICAgICAgc291cmNlKCJodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYmlvY0xpdGUuUiIpCiAgICAgICAgICAgYmlvY0xpdGUoIkJpb2Jhc2UiKX0sIAogICAgICAgICBmaW5hbGx5ID0gbGlicmFyeSgiQmlvYmFzZSIpKQogIAogICAgI2xvYWQgaW4gZmlsZXMKICAgIGV4cHJlc3Npb25NYXRyaXggPC0gYXMubWF0cml4KHJlYWQudGFibGUoCiAgICAgIGZpbGUucGF0aCh3b3JraW5nX2RpciwgIlN1cHBsZW1lbnRhcnlfVGFibGUxMl9UQ0dBX01pY3JvYXJyYXlfcm1hbm9ybWFsaXplZC50eHQiKSwgCiAgICAgIGhlYWRlciA9IFRSVUUsIHNlcCA9ICJcdCIsIHF1b3RlPSJcIiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkpCiAgICBjbGFzc0RlZmluaXRpb25zIDwtIHJlYWQudGFibGUoIAogICAgICBmaWxlLnBhdGgod29ya2luZ19kaXIsIlN1cHBsZW1lbnRhcnlfVGFibGUxM19NaWNyb2FycmF5X2NsYXNzZGVmaW5pdGlvbnMudHh0IiksIAogICAgICBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZT0iXCIiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCiAgICBpZGVudGljYWwoY29sbmFtZXMoZXhwcmVzc2lvbk1hdHJpeCksIGNsYXNzRGVmaW5pdGlvbnMkcGF0aWVudCkKICAgIAogICAgI2NyZWF0ZSBtaW5pbWFsIHNldAogICAgbWluaW1hbFNldCA8LSBFeHByZXNzaW9uU2V0KGFzc2F5RGF0YT1leHByZXNzaW9uTWF0cml4KQogICAgY2xhc3NlcyA8LSBmYWN0b3IoY2xhc3NEZWZpbml0aW9uc1ssZGF0YV9jbGFzc2VzXSkKICAgIAogICAgI2NyZWF0ZSBtb2RlbAogICAgbW9kZWxEZXNpZ24gPC0gbW9kZWwubWF0cml4KH4gMCArIGNsYXNzZXMpCiAgICAgICAgCiAgICAjYXNzaWduIGRhdGEgc2V0IGZvciB0aGUgYW5hbHlzaXMKICAgIGRhdGFfZm9yX2dzX2FuYWx5c2lzIDwtIG1pbmltYWxTZXQKfQoKYGBgCgpJZiB5b3UgaGF2ZW4ndCBkZWZpbmVkIHRoZSBkYXRhIGFib3ZlIHVzaW5nIFJOQS1zZXEgZGF0YSBvciBtaWNyb2FycmF5IGRhdGEsIFNldCBkYXRhIHRvIGJlIHlvdXIgREdFTGlzdCBvZiBNaW5pbWFsU2V0LgpgYGB7cn0KI1Nob3VsZCBiZSBhIG1pbmltYWxTZXQgb3IgREdFTGlzdAojZGF0YV9mb3JfZ3NfYW5hbHlzaXMgPC0gZGF0YSB5b3Ugd2FudCB0byB1c2UgZm9yIHRoZSBhbmFseXNpcy4gIAojY2xhc3NlcyA8LSBjbGFzcyBkZWZpbml0aW9ucyBmb3IgZWFjaCBvZiB0aGUgc2FtcGxlcyBpbiB0aGUgZGF0YXNldApgYGAKCiMjIyBGaWx0ZXIgZ2VuZSBzZXRzIHRvIG9ubHkgY29udGFpbiBnZW5lcyBmb3VuZCBpbiB0aGUgYW5hbHlzaXMgc2V0CjUuIENhbWVyYVtAY2FtZXJhXSBhbmQgUk9BU1RbQHJvYXN0XSByZXF1aXJlIHRoYXQgdGhlIGdlbmUgc2V0cyBhcmUgZmlsdGVyZWQgc3VjaCB0aGF0IGFsbCBnZW5lcyBpbiBnZW5lIHNldHMgaGF2ZSBleHByZXNzaW9uIHZhbHVlcyBpbiB0aGUgZGF0YXNldC4gVXNlIGZ1bmN0aW9uIGluIGxpbW1hIChpZHMyaW5kaWNlcykgdG8gY29udmVydCBnZW5lIGlkZW50aWZpZXJzIGluIHRoZSBnZW5lc2V0IHRvIGluZGljZXMgaW4gdGhlIGRhdGFzZXQuCmBgYHtyfQpnZW5lc2V0c19maWx0ZXJlZCA8LSBpZHMyaW5kaWNlcyhnZW5lc2V0cyRnZW5lc2V0cywgcm93bmFtZXMoZGF0YV9mb3JfZ3NfYW5hbHlzaXMpLCAKcmVtb3ZlLmVtcHR5PVRSVUUpCmBgYAoKCjYuIEZpbHRlciB0aGUgZ2VuZSBzZXRzIGFjY29yZGluZyB0byB0aGVpciBzaXplLCBmb2xsb3dpbmcgdGhlIHByZXZpb3VzIHN0ZXAgb2YgZmlsdGVyaW5nIGJ5IGF2YWlsYWJpbGl0eSBvZiBleHByZXNzaW9uIGRhdGEuIEhlcmUgd2Ugb25seSBpbmNsdWRlIHNldHMgd2l0aCBtb3JlIHRoYW4gMTAgYW5kIGxlc3MgdGhhbiA1MDAgZ2VuZXMuCmBgYHtyfQpnZW5lc2V0X3NpemVzIDwtIHVubGlzdChsYXBwbHkoZ2VuZXNldHNfZmlsdGVyZWQsIGxlbmd0aCkpCmdlbmVzZXRfaW5kaWNlcyA8LSB3aGljaChnZW5lc2V0X3NpemVzPj0xNSAmIGdlbmVzZXRfc2l6ZXM8MjAwKQpgYGAKCiMjIyBDcmVhdGUgZGVzaWduIG1hdHJpeCBhbmQgY29udHJhc3QKNy4gQ3JlYXRlIHRoZSBkZXNpZ24gbWF0cml4IGFuZCBjb250cmFzdCB3ZSB3YW50IHRvIHRlc3QgZm9yLiBJbiB0aGlzIGV4YW1wbGUgd2UgYXJlIGxvb2tpbmcgZm9yIHBhdGh3YXlzIGRpZmZlcmVudGlhbCBiZXR3ZWVuIHRoZSBNZXNlbmNoeW1hbCBhbmQgSW1tdW5vcmVhY3RpdmUgc3VidHlwZXMuCgpgYGB7cn0KCmRlc2lnbiA8LSBtb2RlbC5tYXRyaXgofiAwICsgY2xhc3NlcykKCmNvbnRyYXN0X21lc2VudnNpbW11bm8gPC0gbWFrZUNvbnRyYXN0cygKICBtZXNlbnZzaW1tdW5vID0iY2xhc3Nlc0ltbXVub3JlYWN0aXZlLWNsYXNzZXNNZXNlbmNoeW1hbCIsbGV2ZWxzPWRlc2lnbikKYGBgCgojIyMgUnVuIFJPQVNUW0Byb2FzdF0KOC4gUnVuIGVucmljaG1lbnQgYW5hbHlzaXMgYW5kIGZvcm1hdCB0aGUgcmVzdWx0cyB0byB0aGUg4oCYZ2VuZXJpY+KAmSBmaWxlIGZvcm1hdCBvZiBFbnJpY2htZW50IE1hcC4gVGhpcyBpcyBhIHRhYi1kZWxpbWl0ZWQgZmlsZSB0aGF0IGluY2x1ZGVzIGEgZ2VuZSBzZXQgbmFtZSwgZ2VuZSBzZXQgZGVzY3JpcHRpb24sIHAtdmFsdWUsIEZEUiwgcGhlbm90eXBlIGFuZCBhIGNvbW1hLXNlcGFyYXRlZCBsaXN0IG9mIGFzc29jaWF0ZWQgZ2VuZXMgZm9yIGV2ZXJ5IGRldGVjdGVkIHBhdGh3YXkuIERlcGVuZGluZyBvbiB5b3VyIGRhdGFzZXQgYW5kIGNvbXB1dGVyLCB0aGlzIGNvbW1hbmQgY291bGQgdGFrZSBmcm9tIGEgZmV3IG1pbnV0ZXMgdG8gYW4gaG91ciB0byBydW4uIElmIHlvdSByZWNlaXZlIHRoZSB3YXJuaW5nIOKAnEluIGRuYmlub20ocSwgc2l6ZSA9IHNpemUsIG11ID0gbXUsIGxvZyA9IFRSVUUpIDogbm9uLWludGVnZXIgeOKAnSwgdGhlIHNvZnR3YXJlIGhhcyBlbmNvdW50ZXJlZCB1bmV4cGVjdGVkIG5vbi1pbnRlZ2VyIHZhbHVlcyBvZiBnZW5lIGV4cHJlc3Npb24sIG9mdGVuIGluZGljYXRpbmcgcHJvYmxlbXMgd2l0aCB1cHN0cmVhbSBhbmFseXNpcyBzdWNoIGFzIHN1YiBvcHRpbWFsIHByZS1wcm9jZXNzaW5nIG9yIG5vcm1hbGl6YXRpb24gcHJvY2VkdXJlcy4gQSBzaW1wbGUgZml4IG9mIHJvdW5kaW5nIGdlbmUgZXhwcmVzc2lvbiB2YWx1ZXMgbWF5IGZpeCB0aGUgZXJyb3IgKGRhdGFfZm9yX2dzX2FuYWx5c2lzJGNvdW50cyA8LSByb3VuZChkYXRhX2Zvcl9nc19hbmFseXNpcyRjb3VudHMpKSwgaG93ZXZlciBpdCBzaG91bGQgYmUgaW52ZXN0aWdhdGVkIGZ1cnRoZXIuIApgYGB7cn0KbXJvYXN0X3Jlc3VsdHMgPC0gbXJvYXN0KGRhdGFfZm9yX2dzX2FuYWx5c2lzLCBnZW5lc2V0c19maWx0ZXJlZFtnZW5lc2V0X2luZGljZXNdLAogICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduLGNvbnRyYXN0PWNvbnRyYXN0X21lc2VudnNpbW11bm8sIG5yb3Q9MTAwMDApCm1yb2FzdF9kZXNjciA8LSB1bmxpc3QobGFwcGx5KHJvd25hbWVzKG1yb2FzdF9yZXN1bHRzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpe3VubGlzdChzdHJzcGxpdCh4LCJcXCUiKSlbMV19KSkKYGBgCgoKIyMjIENyZWF0ZSBnZW5lcmljIGVucmljaG1lbnQgbWFwIGZpbGUgZnJvbSBST0FTVFtAcm9hc3RdIHJlc3VsdHMKSW4gb3JkZXIgdG8gY3JlYXRlIGFuIGVucmljaG1lbnQgbWFwIGZyb20gdGhlIHJlc3VsdHMgb2YgdGhlIFJPQVNUIGFuYWx5c2lzIHdlIG5lZWQgdG8gY3JlYXRlIGEgZmlsZSB0aGF0IGNhbiBiZSB1cGxvYWRlZCBpbnRvIHRoZSBFbnJpY2htZW50TWFwIGFuZCB0cmFuc2xhdGVkIGludG8gYSBuZXR3b3JrLiAgRW5yaWNobWVudE1hcCByZXF1aXJlcyBhIGZpbGUgdGhhdCBjb250YWlucyB0aGUgcGF0aHdheSBuYW1lLCBkZXNjcmlwdGlvbiwgcC12YWx1ZSBhc3NvY2lhdGVkIHdpdGggdGhlIGVucmljaG1lbnQsIGNvcnJlY3RlZCBwLXZhbHVlIGFzc29jaWF0ZWQgd2l0aCB0aGUgZW5yaWNobWVudCwgYSBwaGVub3R5cGUgYW5kIGEgbGlzdCBvZiBnZW5lcyBhc3NvY2lhdGVkIHdpdGggdGhlIGdpdmVuIHBhdGh3YXkuICBUaGUgbGlzdCBvZiBnZW5lcyBpcyBvcHRpb25hbCBidSBpbiB0aGUgYWJzZW5jZSBvZiBhIGxpc3Qgb2YgZ2VuZXMgYSBnbXQgZmlsZSBtdXN0IGJlIHByb3ZpZGVkLiAgSWYgYW55IG9mIHRoZSByZXF1aXJlZCBjb2x1bW5zIGFyZSBtaXNzaWcgZnJvbSB5b3VyIGFuYWx5c2lzIHlvdSBjYW4gc3VwcGx5IGZha2UgdmFsdWVzIGluIG9yZGVyIHRvIGJ1aWxkIGFuIGVucmljaG1lbnQgbWFwLiAKCjkuIEluc3BlY3QgdGhlIHJlc3VsdHMgcmV0dXJuZWQgZnJvbSBST0FTVFtAcm9hc3RdLiBUaGUgY29sdW1uICJEaXJlY3Rpb24iIHNob3dzIHdoZXRoZXIgdGhlIGdlbmUgc2V0IGlzIGVucmljaGVkIGZvciB1cC1yZWd1bGF0ZWQgZ2VuZXMgb3IgZG93bi1yZWd1bGF0ZWQgZ2VuZXMuIFRvIGVuc3VyZSBjb21wYXRpYmlsaXR5IHdpdGggRW5yaWNobWVudE1hcCwgY29udmVydCB0aGVzZSB2YWx1ZXMgc3VjaCB0aGF0IDEgcmVwcmVzZW50cyB1cC1yZWd1bGF0ZWQgLTEgcmVwcmVzZW50cyBkb3duLXJlZ3VsYXRlZCBnZW5lcy4KYGBge3J9Cm1yb2FzdF9yZXN1bHRzX2ZpbGUgPC0gIm1yb2FzdF9yZXN1bHRzX2dlbmVyaWNfZW0udHh0IgoKUGhlbm90eXBlIDwtIHVubGlzdChsYXBwbHkobXJvYXN0X3Jlc3VsdHNbLCJEaXJlY3Rpb24iXSxmdW5jdGlvbih4KQoJe2lmKHg9PSJVcCIpezF9ZWxzZXsoLTEpfX0pKQpnZW5lcyA8LSBjKCkKZm9yKGkgaW4gMTpsZW5ndGgocm93bmFtZXMobXJvYXN0X3Jlc3VsdHMpKSl7CgljdXJyZW50X2dlbmVzZXQgPC0gdW5saXN0KGdlbmVzZXRzX2ZpbHRlcmVkCgkJWyB3aGljaCggbmFtZXMoZ2VuZXNldHNfZmlsdGVyZWQpICVpbiUgcm93bmFtZXMobXJvYXN0X3Jlc3VsdHMpW2ldKV0pCgljdXJyZW50X2dlbmVzIDwtIGMoKQoJCWZvcihqIGluIDE6bGVuZ3RoKGN1cnJlbnRfZ2VuZXNldCkpewoJCQlpZihqPT1sZW5ndGgoY3VycmVudF9nZW5lc2V0KSl7CgkJCQljdXJyZW50X2dlbmVzIDwtIHBhc3RlKGN1cnJlbnRfZ2VuZXMsIAoJCQkJCXJvd25hbWVzKGRhdGFfZm9yX2dzX2FuYWx5c2lzKVtjdXJyZW50X2dlbmVzZXRbal1dLCAKc2VwPSIiKQoJCQl9IGVsc2UgewoJCQkJY3VycmVudF9nZW5lcyA8LSBwYXN0ZShjdXJyZW50X2dlbmVzLCAKCQkJCQlyb3duYW1lcyhkYXRhX2Zvcl9nc19hbmFseXNpcylbY3VycmVudF9nZW5lc2V0W2pdXSwKIiwiLCBzZXA9IiIpCgkJCX0KCQl9CgkJZ2VuZXMgPC0gcmJpbmQoZ2VuZXMsIGN1cnJlbnRfZ2VuZXMpCgl9CnJvd25hbWVzKGdlbmVzKSA8LSByb3duYW1lcyhtcm9hc3RfcmVzdWx0cykKCm1yb2FzdF9yZXN1bHRzX2dlbmVyaWNfZW0gPC0gZGF0YS5mcmFtZSggcm93bmFtZXMobXJvYXN0X3Jlc3VsdHMpLCBtcm9hc3RfZGVzY3IsIAoJUFZhbHVlPW1yb2FzdF9yZXN1bHRzWywiUFZhbHVlIl0sIEZEUj1tcm9hc3RfcmVzdWx0c1ssIkZEUiJdLCBQaGVub3R5cGUsIGdlbmVzKQp3cml0ZS50YWJsZShtcm9hc3RfcmVzdWx0c19nZW5lcmljX2VtLCBmaWxlLnBhdGgod29ya2luZ19kaXIsbXJvYXN0X3Jlc3VsdHNfZmlsZSksIAoJY29sLm5hbWU9VFJVRSwgc2VwPSJcdCIsIHJvdy5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UpIAoKYGBgCgoKIyMjIFJ1biBDYW1lcmFbQGNhbWVyYV0KMTAuIFJ1biBwYXRod2F5IGVucmljaG1lbnQgYW5hbHlzaXMgd2l0aCB0aGUgQ2FtZXJhW0BjYW1lcmFdIFIgcGFja2FnZS4gVGhlIGFuYWx5c2lzIHN0YXJ0cyB3aXRoIHRoZSBzYW1lIGZpbGVzIGFzIFJPQVNUW0Byb2FzdF0gKHNlZSBmaXJzdCBmb3VyIGZpcnN0IHN0ZXBzIG9mIFN1cHBsZW1lbnRhcnkgUHJvdG9jb2wgMUMpLgpgYGB7cn0KY2FtZXJhX3Jlc3VsdHNfZmlsZSA8LSAiY2FtZXJhX3Jlc3VsdHNfZ2VuZXJpY19lbS50eHQiCgoKYGBgCgojIyMgQ3JlYXRlIGdlbmVyaWMgZW5yaWNobWVudCBtYXAgZmlsZSBmcm9tIENhbWVyYVtAY2FtZXJhXSByZXN1bHRzCgpgYGB7cn0KY2FtZXJhX3Jlc3VsdHMgPC0gY2FtZXJhKGRhdGFfZm9yX2dzX2FuYWx5c2lzLCAKCWdlbmVzZXRzX2ZpbHRlcmVkW2dlbmVzZXRfaW5kaWNlc10sIGRlc2lnbiwgY29udHJhc3Q9Y29udHJhc3RfbWVzZW52c2ltbXVubykKY2FtZXJhX2Rlc2NyIDwtIHVubGlzdChsYXBwbHkocm93bmFtZXMoY2FtZXJhX3Jlc3VsdHMpLCAKCWZ1bmN0aW9uKHgpe3VubGlzdChzdHJzcGxpdCh4LCJcXCUiKSlbMV19KSkKY2FtZXJhX1BoZW5vdHlwZSA8LSB1bmxpc3QobGFwcGx5KGNhbWVyYV9yZXN1bHRzWywiRGlyZWN0aW9uIl0sIAoJZnVuY3Rpb24oeCl7aWYoeD09IlVwIil7MX1lbHNleygtMSl9fSkpCgoKY2FtZXJhX2dlbmVzIDwtIGMoKQpmb3IoaSBpbiAxOmxlbmd0aChyb3duYW1lcyhjYW1lcmFfcmVzdWx0cykpKXsKCWN1cnJlbnRfZ2VuZXNldCA8LSB1bmxpc3QoIAoJCWdlbmVzZXRzX2ZpbHRlcmVkWyB3aGljaCggbmFtZXMoIGdlbmVzZXRzX2ZpbHRlcmVkICkgJWluJSAKCQkgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXMoY2FtZXJhX3Jlc3VsdHMpW2ldKV0pCiAJY3VycmVudF9nZW5lcyA8LSBjKCkKCWZvcihqIGluIDE6bGVuZ3RoKGN1cnJlbnRfZ2VuZXNldCkpewoJCWlmKGo9PWxlbmd0aChjdXJyZW50X2dlbmVzZXQpKXsKCQkJY3VycmVudF9nZW5lcyA8LSBwYXN0ZSggY3VycmVudF9nZW5lcywgCgkJCQlyb3duYW1lcyhkYXRhX2Zvcl9nc19hbmFseXNpcykgW2N1cnJlbnRfZ2VuZXNldFtqXV0sCnNlcD0iIikKCQl9IGVsc2UgewoJCQljdXJyZW50X2dlbmVzIDwtIHBhc3RlKCBjdXJyZW50X2dlbmVzLCAKCQkJCXJvd25hbWVzKGRhdGFfZm9yX2dzX2FuYWx5c2lzKVsgY3VycmVudF9nZW5lc2V0W2pdXSwgIiwiLCAKc2VwPSIiKQoJCX0KCX0KCWNhbWVyYV9nZW5lcyA8LSByYmluZChjYW1lcmFfZ2VuZXMsIGN1cnJlbnRfZ2VuZXMpCn0Kcm93bmFtZXMoY2FtZXJhX2dlbmVzKSA8LSByb3duYW1lcyhjYW1lcmFfcmVzdWx0cykKCmNhbWVyYV9yZXN1bHRzX2dlbmVyaWNfZW0gPC0gZGF0YS5mcmFtZShyb3duYW1lcyhjYW1lcmFfcmVzdWx0cyksIGNhbWVyYV9kZXNjciwgCglQVmFsdWUgPSBjYW1lcmFfcmVzdWx0c1ssIlBWYWx1ZSJdLCBGRFI9Y2FtZXJhX3Jlc3VsdHNbLCJGRFIiXSwgUGhlbm90eXBlLCBnZW5lcyApCndyaXRlLnRhYmxlKGNhbWVyYV9yZXN1bHRzX2dlbmVyaWNfZW0sIGZpbGUucGF0aCh3b3JraW5nX2RpcixjYW1lcmFfcmVzdWx0c19maWxlKSwgCgljb2wubmFtZT1UUlVFLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKYGBgCgojIyMgQ3JlYXRlIEVucmljaG1lbnQgTWFwClRoZSByZXN1bHRzIGZyb20gQ2FtZXJhW0BjYW1lcmFdIG9yIFJPQVNUW0Byb2FzdF0gY2FuIGJlIGlucHV0IHRvIEVucmljaG1lbnQgTWFwLCBmb2xsb3dpbmcgdGhlIHByb3RvY29sKHN0YXJ0IGZyb20gc3RlcCA2KSBpbiB0aGUgbWFpbiB0ZXh0LgoKCiMjIyAoT3B0aW9uYWwpIENyZWF0ZSBhbiBFbnJpY2htZW50IG1hcCBkaXJlY3RseSBmcm9tIFIKT3B0aW9uYWw6IEJ1aWxkIEVucmljaG1lbnQgbWFwIGZyb20gdGhlIGFib3ZlIHJlc3VsdHMKCkluc3RlYWQgb2YgY3JlYXRpbmcgYW4gZW5yaWNobWVudCBtYXAgdGhyb3VnaCB0aGUgQ3l0b3NjYXBlIHVzZXIgaW50ZXJmYWNlIGl0IGlzIGFsc28gcG9zc2libGUgdG8gY3JlYXRlIGl0IGRpcmVjdGx5IGZyb20gUiB1c2luZyBjeXJlc3QgY29tbWFuZHMuICBCZWxvdyBpcyBhbiBleGFtcGxlIG9mIGhvdyB0byBjcmVhdGUgYW4gZW5yaWNobWVudCBtYXAgZGlyZWN0bHkgZnJvbSBSIHVzaW5nIHRoZSBDYW1lcmFbQGNhbWVyYV0gYW5kIFJPQVNUW0Byb2FzdF0gcmVzdWx0cyB0aGF0IGhhdmUgYmVlbiBjcmVhdGVkIGluIHRoaXMgcHJvdG9jb2wuCgpNYWtlIHN1cmUgdGhhdCB5b3UgaGF2ZSBsYXVuY2ggQ3l0b3NjYXBlIGFuZCBpbnN0YWxsZWQgYWxsIHJlcXVpcmVkIGFwcHMgYXMgbGlzdGVkIGluIHRoZSBtYWluIFByb3RvY29sIChTdGVwIDEgLSA0KQoKQnVpbGQgYSBuZXR3b3JrIHdpdGggQ2FtZXJhW0BjYW1lcmFdIHJlc3VsdHM6CmBgYHtyfQojdXNlIGVhc3kgY3lSZXN0IGxpYnJhcnkgdG8gY29tbXVuaWNhdGUgd2l0aCBjeXRvc2NhcGUuCgp0cnlDYXRjaChleHByID0geyBsaWJyYXJ5KFJDeTMpfSwgCiAgICAgICAgIGVycm9yID0gZnVuY3Rpb24oZSkgeyBpbnN0YWxsX2dpdGh1YigiY3l0b3NjYXBlL1JDeTMiKX0sIGZpbmFsbHkgPSBsaWJyYXJ5KFJDeTMpKQoKI2RlZmluZWQgdGhyZXNob2xkIGZvciBHU0VBIGVucmljaG1lbnRzIChuZWVkIHRvIGJlIHN0cmluZ3MgZm9yIGN5cmVzdCBjYWxsKQpwdmFsdWVfdGhyZXNob2xkIDwtICIwLjA1IgpxdmFsdWVfdGhyZXNob2xkIDwtICIwLjAwMSIKCnNpbWlsYXJpdHlfdGhyZXNob2xkIDwtICIwLjI1IgpzaW1pbGFyaXR5X21ldHJpYyA9ICJKQUNDQVJEIgoKZ2VuZXJpY19nbXRfZmlsZSA8LSBmaWxlLnBhdGgoZ2V0d2QoKSxnbXRfZmlsZSkKCmN1cl9tb2RlbF9uYW1lIDwtIHBhc3RlKCJjYW1lcmEiLGFuYWx5c2lzX25hbWUsc2VwPSJfIikKcmVzdWx0c19maWxlbmFtZSA8LSBmaWxlLnBhdGgoZ2V0d2QoKSx3b3JraW5nX2RpcixjYW1lcmFfcmVzdWx0c19maWxlKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiNjcmVhdGUgRU0gLSBjYW1lcmEgcmVzdWx0cwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKY3VycmVudF9uZXR3b3JrX25hbWUgPC0gcGFzdGUoY3VyX21vZGVsX25hbWUscHZhbHVlX3RocmVzaG9sZCxxdmFsdWVfdGhyZXNob2xkLHNlcD0iXyIpCgplbV9jb21tYW5kID0gcGFzdGUoJ2VucmljaG1lbnRtYXAgYnVpbGQgYW5hbHlzaXNUeXBlPSJnZW5lcmljIicsCiAgICAgICAgICAgICAgICAgICAnZ210RmlsZT0nLGdlbmVyaWNfZ210X2ZpbGUsCiAgICAgICAgICAgICAgICAgICAncHZhbHVlPScscHZhbHVlX3RocmVzaG9sZCwKICAgICAgICAgICAgICAgICAgICdxdmFsdWU9JyxxdmFsdWVfdGhyZXNob2xkLAogICAgICAgICAgICAgICAgICAgJ3NpbWlsYXJpdHljdXRvZmY9JyxzaW1pbGFyaXR5X3RocmVzaG9sZCwKICAgICAgICAgICAgICAgICAgICdjb2VmZmljaWVudHM9JyxzaW1pbGFyaXR5X21ldHJpYywKICAgICAgICAgICAgICAgICAgICdlbnJpY2htZW50c0RhdGFzZXQxPScscmVzdWx0c19maWxlbmFtZSwKICAgICAgICAgICAgICAgICAgICdleHByZXNzaW9uRGF0YXNldDE9JyxmaWxlLnBhdGgoZ2V0d2QoKSx3b3JraW5nX2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHByZXNzaW9uX2ZpbGUpLAogICAgICAgICAgICAgICAgICAgc2VwPSIgIikKCiNlbnJpY2htZW50IG1hcCBjb21tYW5kIHdpbGwgcmV0dXJuIHRoZSBzdWlkIG9mIG5ld2x5IGNyZWF0ZWQgbmV0d29yay4KcmVzcG9uc2UgPC0gY29tbWFuZHNHRVQoZW1fY29tbWFuZCkKCgpjdXJyZW50X25ldHdvcmtfc3VpZCA8LSAwCiNlbnJpY2htZW50IG1hcCBjb21tYW5kIHdpbGwgcmV0dXJuIHRoZSBzdWlkIG9mIG5ld2x5IGNyZWF0ZWQgbmV0d29yayB1bmxlc3MgaXQgRmFpbGVkLiAgCiNJZiBpdCBmYWlsZWQgaXQgd2lsbCBjb250YWluIHRoZSB3b3JkIGZhaWxlZAppZihncmVwbChwYXR0ZXJuPSJGYWlsZWQiLCByZXNwb25zZSkpewogIHBhc3RlKHJlc3BvbnNlKQp9IGVsc2UgewogIGN1cnJlbnRfbmV0d29ya19zdWlkIDwtIHJlc3BvbnNlCn0KCnJlc3BvbnNlIDwtIHJlbmFtZU5ldHdvcmsoY3VycmVudF9uZXR3b3JrX25hbWUsIGFzLm51bWVyaWMoY3VycmVudF9uZXR3b3JrX3N1aWQpKQpgYGAKCldoZW4gYnVpbGRpbmcgYSBuZXR3b3JrIGlmIHRoZSBjb21tYW5kc0dldCByZXR1cm5zIGFuIGVycm9yIHNpbWlsYXIgdG8gdGhpczoKCiJSQ3kzOjpjb21tYW5kc0dFVCwgSFRUUCBFcnJvciBDb2RlOiA1MDAKCnVybD1odHRwOi8vbG9jYWxob3N0OjEyMzQvdjEvY29tbWFuZHMvZW5yaWNobWVudG1hcC9idWlsZD9hbmFseXNpc1R5cGU9Z2VuZXJpYwogCiZnbXRGaWxlPVsqKnBhdGgvdG8vZmlsZSoqXS9kYXRhL1N1cHBsZW1lbnRhcnlfVGFibGUzX0h1bWFuX0dPQlBfCgpBbGxQYXRod2F5c19ub19HT19pZWFfTWFyY2hfMDFfMjAxOF9zeW1ib2wuZ210CgomcHZhbHVlPSUyMDAuMDUmcXZhbHVlPSUyMDAuMDAwMSZzaW1pbGFyaXR5Y3V0b2ZmPSUyMDAuMjUmY29lZmZpY2llbnRzPSUyMEpBQ0NBUkQKCiZlbnJpY2htZW50c0RhdGFzZXQxPVsqKnBhdGgvdG8vZmlsZSoqXS9kYXRhL21yb2FzdF9yZXN1bHRzX2dlbmVyaWNfZW0udHh0CgomZXhwcmVzc2lvbkRhdGFzZXQxPVsqKnBhdGgvdG8vZmlsZSoqXS9kYXRhL1N1cHBsZW1lbnRhcnlfVGFibGU2X1RDR0FfT1ZfUk5Bc2VxX2V4cHJlc3Npb24udHh0CgpFcnJvciBpbiBjb21tYW5kc0dFVChlbV9jb21tYW5kKSA6ICIKCgpDb3B5IHRoZSB1cmwgKGZvciBleGFtcGxlLCBmcm9tIHRoZSBhYm92ZSBlcnJvciB1c2U6CgoiaHR0cDovL2xvY2FsaG9zdDoxMjM0L3YxL2NvbW1hbmRzL2VucmljaG1lbnRtYXAvYnVpbGQ/YW5hbHlzaXNUeXBlPWdlbmVyaWMKCiZnbXRGaWxlPVsqKnBhdGgvdG8vZmlsZSoqXS9kYXRhL1N1cHBsZW1lbnRhcnlfVGFibGUzX0h1bWFuX0dPQlBfIAoKQWxsUGF0aHdheXNfbm9fR09faWVhX01hcmNoXzAxXzIwMThfc3ltYm9sLmdtdCAKCiZwdmFsdWU9JTIwMC4wNSZxdmFsdWU9JTIwMC4wMDAxJnNpbWlsYXJpdHljdXRvZmY9JTIwMC4yNSZjb2VmZmljaWVudHM9JTIwSkFDQ0FSRAoKJmVucmljaG1lbnRzRGF0YXNldDE9WyoqcGF0aC90by9maWxlKipdL2RhdGEvbXJvYXN0X3Jlc3VsdHNfZ2VuZXJpY19lbS50eHQKCiZleHByZXNzaW9uRGF0YXNldDE9WyoqcGF0aC90by9maWxlKipdL2RhdGEvU3VwcGxlbWVudGFyeV9UYWJsZTZfVENHQV9PVl9STkFzZXFfZXhwcmVzc2lvbi50eHQiKSAKCmFuZCBwYXN0ZSBpdCBpbnRvIGEgd2ViIGJyb3dzZXIgdG8gZ2V0IGEgbW9yZSBkZXNjcmlwdGl2ZSBlcnJvciBtZXNzYWdlLiAgKipEbyBub3QgY29weSB0aGUgYWJvdmUgbGluayB0byB5b3VyIHdlYiBicm93c2VyLiAgVGhlIHVybCBpcyBzcGVjaWZpYyB0byB0aGUgbWFjaGluZSB5b3UgaGF2ZSBydW4gdGhlIG5vdGVib29rIG9uLiBjb3B5IHRoZSB1cmwgZnJvbSB5b3VyIGVycm9yIG1lc3NhZ2UuKioKClNvbWV0aW1lcyB0aGUgYWJvdmUgZXJyb3Igd2lsbCBjb21lIGJhY2sgd2hlbiBldmVyeXRoaW5nIGlzIGZpbmUuICBJZiB0aGVyZSBhcmUgbm8gcmVzdWx0cyByZXR1cm5lZCBiZWNhdXNlIG5vdGhpbmcgcGFzc2VzIHRoZSB0aHJlc2hvbGRzIHlvdSBzcGVjaWZpZWQgdGhlIGFib3ZlIGVycm9yIHdpdGggYXBwZWFyIGluIFIuICBJbiB0aGUgd2ViIGJyb3dzZXIgYWZ0ZXIgZm9sbG93aW5nIHRoZSBhYm92ZSBsaW5rIHdpbGwgc2hvdyB0aGUgZXJyb3IgIkZhaWxlZDogTm9uZSBvZiB0aGUgZ2VuZSBzZXRzIGhhdmUgcGFzc2VkIHRoZSBmaWx0ZXIuIFRyeSByZWxheGluZyB0aGUgZ2VuZSBzZXQgZmlsdGVyIHBhcmFtZXRlcnMuIgoKCkJ1aWxkIGEgbmV0d29yayB3aXRoIHRoZSBST0FTVFtAcm9hc3RdIHJlc3VsdHMKYGBge3J9CgpjdXJfbW9kZWxfbmFtZSA8LSBwYXN0ZSgicm9hc3QiLGFuYWx5c2lzX25hbWUsc2VwPSJfIikKcmVzdWx0c19maWxlbmFtZSA8LSBmaWxlLnBhdGgoZ2V0d2QoKSx3b3JraW5nX2Rpcixtcm9hc3RfcmVzdWx0c19maWxlKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiNjcmVhdGUgRU0gLXJvYXN0IHJlc3VsdHMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmN1cnJlbnRfbmV0d29ya19uYW1lIDwtIHBhc3RlKGN1cl9tb2RlbF9uYW1lLHB2YWx1ZV90aHJlc2hvbGQscXZhbHVlX3RocmVzaG9sZCxzZXA9Il8iKQoKZW1fY29tbWFuZCA9IHBhc3RlKCdlbnJpY2htZW50bWFwIGJ1aWxkIGFuYWx5c2lzVHlwZT0iZ2VuZXJpYyInLAogICAgICAgICAgICAgICAgICAgJ2dtdEZpbGU9JyxnZW5lcmljX2dtdF9maWxlLAogICAgICAgICAgICAgICAgICAgJ3B2YWx1ZT0nLHB2YWx1ZV90aHJlc2hvbGQsIAogICAgICAgICAgICAgICAgICAgJ3F2YWx1ZT0nLHF2YWx1ZV90aHJlc2hvbGQsCiAgICAgICAgICAgICAgICAgICAnc2ltaWxhcml0eWN1dG9mZj0nLHNpbWlsYXJpdHlfdGhyZXNob2xkLAogICAgICAgICAgICAgICAgICAgJ2NvZWZmaWNpZW50cz0nLHNpbWlsYXJpdHlfbWV0cmljLAogICAgICAgICAgICAgICAgICAgJ2VucmljaG1lbnRzRGF0YXNldDE9JyxyZXN1bHRzX2ZpbGVuYW1lLAogICAgICAgICAgICAgICAgICAgJ2V4cHJlc3Npb25EYXRhc2V0MT0nLGZpbGUucGF0aChnZXR3ZCgpLHdvcmtpbmdfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cHJlc3Npb25fZmlsZSksCiAgICAgICAgICAgICAgICAgICBzZXA9IiAiKQoKI2VucmljaG1lbnQgbWFwIGNvbW1hbmQgd2lsbCByZXR1cm4gdGhlIHN1aWQgb2YgbmV3bHkgY3JlYXRlZCBuZXR3b3JrLgpyZXNwb25zZSA8LSBjb21tYW5kc0dFVChlbV9jb21tYW5kKQoKCmN1cnJlbnRfbmV0d29ya19zdWlkIDwtIDAKI2VucmljaG1lbnQgbWFwIGNvbW1hbmQgd2lsbCByZXR1cm4gdGhlIHN1aWQgb2YgbmV3bHkgY3JlYXRlZCBuZXR3b3JrIHVubGVzcyBpdCBGYWlsZWQuICAKIyBJZiBpdCBmYWlsZWQgaXQgd2lsbCBjb250YWluIHRoZSB3b3JkIGZhaWxlZAppZihncmVwbChwYXR0ZXJuPSJGYWlsZWQiLCByZXNwb25zZSkpewogIHBhc3RlKHJlc3BvbnNlKQp9IGVsc2UgewogIGN1cnJlbnRfbmV0d29ya19zdWlkIDwtIHJlc3BvbnNlCn0KCnJlc3BvbnNlIDwtIHJlbmFtZU5ldHdvcmsoY3VycmVudF9uZXR3b3JrX25hbWUsIG5ldHdvcmsgPSBhcy5udW1lcmljKGN1cnJlbnRfbmV0d29ya19zdWlkKSkKCgpgYGAKCgpgYGB7ciwgZXZhbCA9IEZBTFNFLGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoa25pdHIpCmFzaXNfb3V0cHV0KCIjIyBSZWZlcmVuY2VzXFxuIikgIyBIZWFkZXIgdGhhdCBpcyBvbmx5IHNob3duIGlmIGFkZF9zZXR1cCA9PSBUUlVFCmBgYA==