1 Supplementary Protocol 1 – create a gene list by analyzing gene expression data from RNA-seq with edgeR

This protocol processes RNA-seq data using the R programming environment and specialized packages from Bioconductor to create genes lists. Novice users can copy and paste commands into the R console. To create gene expression data for Protocol step 6B, we downloaded gene expression data from the Ovarian Serous Cystadenocarcinoma project of The Cancer Genome Atlas (TCGA)1, http://cancergenome.nih.gov via the Genomic Data Commons (GDC) portal2 on 2017-06-14 using TCGABiolinks R package3. The data includes 544 samples available as RMA-normalized microarray data (Affymetrix HG-U133A), and 309 samples available as RNA-seq data, with reads mapped to a reference genome using MapSplice4 and read counts per transcript determined using the RSEM method5. RNA-seq data are labeled as ‘RNA-seq V2’, see details at: https://wiki.nci.nih.gov/display/TCGA/RNASeq+Version+2). The RNA-seqV2 data consists of raw counts similar to regular RNA-seq but RSEM (RNA-seq by Expectation Maximization) data can be used with the edgeR method.

1.1 Process RNA-seq data

This part of the supplementary protocol demonstrates filtering and scoring RNA-seq data using normalized RNA-seq count data with the edgeR R package. The protocol can be used to produce input data for pathway enrichment methods like g:Profiler, GSEA and others. This RNA-seq analysis protocol follows conceptually similar steps to microarray analysis shown above.

1.1.1 Load required packages

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

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

working_dir <- file.path(getwd(),"data")

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

1.1.2 Load Expression Data

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

1.1.3 Load subtype information

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

1.1.4 Filter Data

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

1.1.5 Normalization and Dispersion

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

#Normalize the data
d <- calcNormFactors(d)

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


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

dev.off()

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

1.1.6 Filter unannotated genes

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

1.1.7 Calculate Differential expression

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

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

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

Examples of different designs that can be used for this dataset. Instead of simple two class design you can also compare one class to the remaining three classes.

classes <- factor(classDefinitions_RNASeq[,data_classes])
modelDesign <- model.matrix(~ 0 + classes)

contrast_immuno <- makeContrasts(
  immunovsrest ="classesImmunoreactive-(classesMesenchymal + 
  classesProliferative +classesDifferentiated)/3",
  levels=modelDesign)
fit_glm <- glmFit(d,modelDesign)
immunovsrest <- glmLRT(fit_glm , contrast = contrast_immuno)
tt_immunovsrest <- topTags(immunovsrest,n=nrow(d))

contrast_mesen <- makeContrasts( 
  mesenvsrest = "classesMesenchymal-(classesImmunoreactive + 
  classesProliferative +classesDifferentiated)/3",
  levels=modelDesign)
fit_glm <- glmFit(d,modelDesign)
mesenvsrest <- glmLRT(fit_glm , contrast = contrast_mesen)
tt_mesenvsrest <- topTags(mesenvsrest,n=nrow(d))

contrast_prolif <- makeContrasts( 
  prolifvsrest = "classesProliferative-(classesMesenchymal + 
  classesImmunoreactive +classesDifferentiated)/3",
  levels=modelDesign)
fit_glm <- glmFit(d,modelDesign)
prolifvsrest <- glmLRT(fit_glm , contrast = contrast_prolif)
tt_prolifvsrest <- topTags(prolifvsrest,n=nrow(d))

contrast_diff <- makeContrasts( 
  diffvsrest = "classesDifferentiated-(classesMesenchymal + 
  classesImmunoreactive +classesProliferative)/3",
  levels=modelDesign)
fit_glm <- glmFit(d,modelDesign)
diffvsrest <- glmLRT(fit_glm , contrast = contrast_diff)
tt_diffvsrest <- topTags(diffvsrest,n=nrow(d))

1.1.8 Create g:Profiler input list

8a. Create the gene list for use in g:Profiler or another thresholded enrichment tool. The list may comprise all genes that have a significant FDR-corrected p-value (code shown below), all significant and FDR-corrected up-regulated genes and all down-regulated genes separately, or some other combination of thresholds. Also see analogous step in the microarray protocol.

tt <- tt_exact_test

#get the indices of scored dataset that have FDR < 0.05
select_genes = which(tt$table$FDR < 0.05)

#output how many genes there are in the set that have FDR < 0.05
length(select_genes)

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

#output the top 5 entries in the list of top genes
head(topgenes_qvalue005)

#write results out to the file.  This is an example of a set that can be used for
# Protocol 1
write.table(topgenes_qvalue005, 
            file.path(working_dir,"MesenchymalvsImmunoreactive_RNAseq_allsignificantgenes.txt"), 
            col.names=FALSE, sep="\t", row.names=FALSE, quote=FALSE)

1.1.9 Create GSEA input list

8b. Create a two-column rank (.RNK) file of all gene IDs and corresponding scores to for GSEA pre-ranked analysis. To run GSEA in pre-ranked mode, you need a two column RNK file with gene/protein/probe name (column 1) and the associated score (column 2). The first column should contain the same type of gene IDs used in the pathway gene-set (GMT) file. GSEA will look for enrichment in the set of most differentially expressed genes at the top of the list as well as those at the bottom of the list. Genes at the top of the list are more highly expressed in class A of samples (e.g., Mesenchymal) while genes at the bottom are highly expressed in class B (e.g., Immunoreactive). A score can be computed by multiplying direction (sign) of fold change and logarithm of p-value for each gene.

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

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

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

#sort ranks in decreasing order
ranks_RNAseq <- ranks_RNAseq[order(as.numeric(ranks_RNAseq[,2]),decreasing = TRUE),]

write.table(ranks_RNAseq, file.path(working_dir,
                "Supplementary_Table2_MesenvsImmuno_RNASeq_ranks.rnk"), 
            col.name = TRUE, sep="\t", row.names = FALSE, quote = FALSE)

Small section of the top and bottom of the resulting rank file:

head(ranks_RNAseq)
tail(ranks_RNAseq)

1.1.10 Create expression file

9a. (Optional) Create an expression file for the enrichment map and save it to a file in the working folder. The optional expression file is created from the original RNA-seq expression matrix used for tha anlysis (variable d above) with the addition of a column on the left edge of the matrix. The additional field often contains a gene description however any text value can be added.

#fix issue with biomart not working because of url redirection
options(RCurlOptions=list(followlocation=TRUE, postredir=2L))

normalized_expression_RNAseq <- cpm(d, normalized.lib.size=TRUE)

#From the rownames parse out the gene name and the geneids
genenames <- unlist(lapply( rownames(normalized_expression_RNAseq), 
function(data) {unlist(strsplit(data,"\\|"))[1]}))
geneids <- unlist(lapply( rownames(normalized_expression_RNAseq), 
function(data) {unlist(strsplit(data,"\\|"))[2]}))

EM_expressionFile_RNAseq <- data.frame(Name = genenames, normalized_expression_RNAseq)
rownames(EM_expressionFile_RNAseq) <- rownames(normalized_expression_RNAseq)
colnames(EM_expressionFile_RNAseq) <- substring(colnames(EM_expressionFile_RNAseq),1,12)

#Add descriptions instead of geneids
tryCatch(expr = { library("biomaRt")}, 
         error = function(e) { 
           source("https://bioconductor.org/biocLite.R")
           biocLite("biomaRt")}, 
         finally = library("biomaRt"))
mart = useMart(biomart="ENSEMBL_MART_ENSEMBL")
mart = useDataset(mart, dataset="hsapiens_gene_ensembl" )

genes = getBM(attributes = c( 'hgnc_symbol', 'description'), filters='hgnc_symbol', 
              values=genenames, mart=mart);
genes$description = gsub("\\[Source.*", "", genes$description);

EM_expressionFile_RNAseq <- merge(genes,EM_expressionFile_RNAseq,  
                                  all.y=TRUE,by.x=1, by.y=1)
colnames(EM_expressionFile_RNAseq)[1] <- "Name"
colnames(EM_expressionFile_RNAseq)[2] <- "Description"

write.table(EM_expressionFile_RNAseq, 
            file.path(working_dir,
                  "Supplementary_Table6_TCGA_OV_RNAseq_expression.txt"),
            col.name=TRUE,sep="\t", row.names=FALSE, quote=FALSE)

1.1.11 Create a GSEA class file

9b. (Optional) GSEA CLS file defining the phenotype (i.e. biological conditions) of each sample in the expression file, for example, see Supplementary_Table7_TCGA_OV_RNAseq_classes.cls. This file is only required for phenotype randomization in GSEA, however providing it to EnrichmentMap will label the columns of the expression file in the EnrichmentMap heat map viewer by phenotype.

#write out a GSEA classes file. (optional)
fileConn <- file(
  file.path(working_dir,"Supplementary_Table7_TCGA_OV_RNAseq_classes.cls"))
writeLines(c(paste(length(classDefinitions_RNASeq[,data_classes]), "4 1"), 
             paste("# ", unique(classDefinitions_RNASeq[,data_classes])[1], " ",
                   unique(classDefinitions_RNASeq[,data_classes])[2], " ",
                   unique(classDefinitions_RNASeq[,data_classes])[3], " ",
                   unique(classDefinitions_RNASeq[,data_classes])[4])), fileConn)
write.table(t(classDefinitions_RNASeq[,data_classes]), 
            file.path(working_dir,"Supplementary_Table7_TCGA_OV_RNAseq_classes.cls"), 
            col.name=FALSE, sep="\t",
            row.names=FALSE, quote=FALSE, append=TRUE)
close(fileConn)

1.1.12 Examine results

9c. (Optional) Examine gene expression data using heat maps. Heat maps can easily show the separation between sample classes, labeled by colors in the heat map header. By limiting to the most significantly differentially expressed list of genes (FDR-corrected p<0.05) we can verify whether the scoring accurately separates class A from class B. Resulting heatmap is shown in Figure 2.

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

annotation_col <- data.frame(SUBTYPE=factor(classDefinitions_RNASeq[,data_classes]))
rownames(annotation_col) <- substr(classDefinitions_RNASeq[,2],1,12)

ann_colors = list(SUBTYPE = c(Immunoreactive="blue", Mesenchymal="red",
                              Proliferative = "orange",Differentiated="darkgreen"))
col.pal <- rev(brewer.pal(11, "RdBu"))

genes_to_select <- unlist(lapply( rownames(tt$table)[which(tt$table$FDR<0.05)], 
                                  function(data) {unlist(strsplit(data,"\\|"))[1]}))

matrix_for_heatmap <- as.matrix(EM_expressionFile_RNAseq[which(EM_expressionFile_RNAseq[,1] 
                            %in% genes_to_select ),3:dim(EM_expressionFile_RNAseq)[2] ])

class(matrix_for_heatmap) <- "numeric"
matrix_for_heatmap[matrix_for_heatmap == 0] <- 0.0000001

1.1.13 Example of other comparisons

Example of other comparison that can be done with this dataset. Rank files for each are also created

#Immuno vs rest
tt <- tt_immunovsrest
select_genes = which(tt$table$FDR<0.05)
length(select_genes)
topgenes_qvalue005 <- unlist(lapply( rownames(tt$table)[select_genes], 
function(data) {unlist(strsplit(data,"\\|"))[1]}))
head(topgenes_qvalue005)
write.table(topgenes_qvalue005, 
file.path(working_dir,"ImmunovsRest_RNAseq_allsignificantgenes.txt"), 
col.names=FALSE, sep="\t", row.names=FALSE, quote=FALSE)

ranks_RNAseq = sign(tt$table$logFC) * -log10(tt$table$PValue)
genenames <- unlist(lapply( rownames(tt$table), function(data) 
  {unlist(strsplit(data,"\\|"))[1]}))
geneids <- unlist(lapply( rownames(tt$table),  
                          function(data) {unlist(strsplit(data,"\\|"))[2]})) 
ranks_RNAseq <- cbind(genenames, ranks_RNAseq)
colnames(ranks_RNAseq) <- c("GeneName","rank")
write.table(ranks_RNAseq, file.path(working_dir,"ImmunovsRest_RNASeq_ranks.rnk"), 
col.name = TRUE, sep="\t", row.names = FALSE, quote = FALSE)


#Mesen vs rest data
tt <- tt_mesenvsrest
select_genes = which(tt$table$FDR<0.05)
length(select_genes)
topgenes_qvalue005 <- unlist(lapply( rownames(tt$table)[select_genes], 
function(data) {unlist(strsplit(data,"\\|"))[1]}))
head(topgenes_qvalue005)
write.table(topgenes_qvalue005, 
file.path(working_dir,"MesenvsRest_RNAseq_allsignificantgenes.txt"), 
col.names=FALSE, sep="\t", row.names=FALSE, quote=FALSE)

ranks_RNAseq = sign(tt$table$logFC) * -log10(tt$table$PValue)
genenames <- unlist(lapply( rownames(tt$table), function(data) 
  {unlist(strsplit(data,"\\|"))[1]}))
geneids <- unlist(lapply( rownames(tt$table),  
                          function(data) {unlist(strsplit(data,"\\|"))[2]})) 
ranks_RNAseq <- cbind(genenames, ranks_RNAseq)
colnames(ranks_RNAseq) <- c("GeneName","rank")
write.table(ranks_RNAseq, file.path(working_dir,"MesenvsRest_RNASeq_ranks.rnk"), 
col.name = TRUE, sep="\t", row.names = FALSE, quote = FALSE)

#Differentiated vs rest data
tt <- tt_diffvsrest
select_genes = which(tt$table$FDR<0.05)
length(select_genes)
topgenes_qvalue005 <- unlist(lapply( rownames(tt$table)[select_genes], 
function(data) {unlist(strsplit(data,"\\|"))[1]}))
head(topgenes_qvalue005)
write.table(topgenes_qvalue005, 
file.path(working_dir,"DiffvsRest_RNAseq_allsignificantgenes.txt"), 
col.names=FALSE, sep="\t", row.names=FALSE, quote=FALSE)

ranks_RNAseq = sign(tt$table$logFC) * -log10(tt$table$PValue)
genenames <- unlist(lapply( rownames(tt$table), function(data) 
  {unlist(strsplit(data,"\\|"))[1]}))
geneids <- unlist(lapply( rownames(tt$table),  
                          function(data) {unlist(strsplit(data,"\\|"))[2]})) 
ranks_RNAseq <- cbind(genenames, ranks_RNAseq)
colnames(ranks_RNAseq) <- c("GeneName","rank")
write.table(ranks_RNAseq, file.path(working_dir,"DiffvsRest_RNASeq_ranks.rnk"), 
col.name = TRUE, sep="\t", row.names = FALSE, quote = FALSE)

#Proliferative vs rest data
tt <- tt_prolifvsrest
select_genes = which(tt$table$FDR<0.05)
length(select_genes)
topgenes_qvalue005 <- unlist(lapply( rownames(tt$table)[select_genes], 
function(data) {unlist(strsplit(data,"\\|"))[1]}))
head(topgenes_qvalue005)
write.table(topgenes_qvalue005, 
file.path(working_dir,"ProlifvsRest_RNAseq_allsignificantgenes.txt"), 
col.names=FALSE, sep="\t", row.names=FALSE, quote=FALSE)

ranks_RNAseq = sign(tt$table$logFC) * -log10(tt$table$PValue)
genenames <- unlist(lapply( rownames(tt$table), function(data) 
  {unlist(strsplit(data,"\\|"))[1]}))
geneids <- unlist(lapply( rownames(tt$table),  
                          function(data) {unlist(strsplit(data,"\\|"))[2]})) 
ranks_RNAseq <- cbind(genenames, ranks_RNAseq)
colnames(ranks_RNAseq) <- c("GeneName","rank")
write.table(ranks_RNAseq, file.path(working_dir,"ProlifvsRest_RNASeq_ranks.rnk"), 
col.name = TRUE, sep="\t", row.names = FALSE, quote = FALSE)

1. International Cancer Genome, C. et al. International network of cancer genome projects. Nature 464, 993–8 (2010).

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

4. Wang, K. et al. MapSplice: Accurate mapping of rna-seq reads for splice junction discovery. Nucleic Acids Res 38, e178 (2010).

5. Li, B. & Dewey, C. N. RSEM: Accurate transcript quantification from rna-seq data with or without a reference genome. BMC Bioinformatics 12, 323 (2011).

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

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

LS0tCnRpdGxlOiBTdXBwbGVtZW50YXJ5IFByb3RvY29sIDEg4oCTIGNyZWF0ZSBhIGdlbmUgbGlzdCBieSBhbmFseXppbmcgZ2VuZSBleHByZXNzaW9uCiAgZGF0YSBmcm9tIFJOQS1zZXEgd2l0aCBlZGdlUgphdXRob3I6ICJSdXRoIElzc2VybGluIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBoaWdobGlnaDogaGFkZG9jawogICAga2VlcF9tZDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHBhcGVyCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogbm8KICAgICAgc21vb3RoX3Njcm9sbDogbm8KICBodG1sX25vdGVib29rOgogICAgaGlnaGxpZ2g6IGhhZGRvY2sKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogcGFwZXIKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBubwogICAgICBzbW9vdGhfc2Nyb2xsOiBubwogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6ICczJwpiaWJsaW9ncmFwaHk6IHN1cF9wcm90b2NvbDFfcmVmZXJlbmNlcy5iaWIKY3NsOiBuYXR1cmUtcHJvdG9jb2xzLmNzbAotLS0KCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmNoZWNrPWZ1bmN0aW9uKHgpIHRyeUNhdGNoKGlmKGNsYXNzKHgpID09ICdsb2dpY2FsJykgMSBlbHNlIDEsIGVycm9yPWZ1bmN0aW9uKGUpIDApIAppZihjaGVjayhhZGRfc2V0dXApID09IDApewogIGFkZF9zZXR1cCA9IFRSVUUKfQpgYGAKCiMgU3VwcGxlbWVudGFyeSBQcm90b2NvbCAxIOKAkyBjcmVhdGUgYSBnZW5lIGxpc3QgYnkgYW5hbHl6aW5nIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGZyb20gUk5BLXNlcSB3aXRoIGVkZ2VSClRoaXMgcHJvdG9jb2wgcHJvY2Vzc2VzIFJOQS1zZXEgZGF0YSB1c2luZyB0aGUgUiBwcm9ncmFtbWluZyBlbnZpcm9ubWVudCBhbmQgc3BlY2lhbGl6ZWQgcGFja2FnZXMgZnJvbSBCaW9jb25kdWN0b3IgdG8gY3JlYXRlIGdlbmVzIGxpc3RzLiBOb3ZpY2UgdXNlcnMgY2FuIGNvcHkgYW5kIHBhc3RlIGNvbW1hbmRzIGludG8gdGhlIFIgY29uc29sZS4gVG8gY3JlYXRlIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGZvciAqKlByb3RvY29sIHN0ZXAgNkIqKiwgd2UgZG93bmxvYWRlZCBnZW5lIGV4cHJlc3Npb24gZGF0YSBmcm9tIHRoZSBPdmFyaWFuIFNlcm91cyBDeXN0YWRlbm9jYXJjaW5vbWEgcHJvamVjdCBvZiBUaGUgQ2FuY2VyIEdlbm9tZSBBdGxhcyAoVENHQSlbQFRDR0FdLCBodHRwOi8vY2FuY2VyZ2Vub21lLm5paC5nb3YgdmlhIHRoZSBHZW5vbWljIERhdGEgQ29tbW9ucyAoR0RDKSBwb3J0YWxbQEdEQ10gb24gMjAxNy0wNi0xNCB1c2luZyBUQ0dBQmlvbGlua3MgUiBwYWNrYWdlW0BUQ0dBQmlvbGlua3NdLiBUaGUgZGF0YSBpbmNsdWRlcyA1NDQgc2FtcGxlcyBhdmFpbGFibGUgYXMgUk1BLW5vcm1hbGl6ZWQgbWljcm9hcnJheSBkYXRhIChBZmZ5bWV0cml4IEhHLVUxMzNBKSwgYW5kIDMwOSBzYW1wbGVzIGF2YWlsYWJsZSBhcyBSTkEtc2VxIGRhdGEsIHdpdGggcmVhZHMgbWFwcGVkIHRvIGEgcmVmZXJlbmNlIGdlbm9tZSB1c2luZyBNYXBTcGxpY2VbQE1hcFNwbGljZV0gYW5kIHJlYWQgY291bnRzIHBlciB0cmFuc2NyaXB0IGRldGVybWluZWQgdXNpbmcgdGhlIFJTRU0gbWV0aG9kW0BSU0VNXS4gUk5BLXNlcSBkYXRhIGFyZSBsYWJlbGVkIGFzIOKAmFJOQS1zZXEgVjLigJksIHNlZSBkZXRhaWxzIGF0OiBodHRwczovL3dpa2kubmNpLm5paC5nb3YvZGlzcGxheS9UQ0dBL1JOQVNlcStWZXJzaW9uKzIpLiBUaGUgUk5BLXNlcVYyIGRhdGEgY29uc2lzdHMgb2YgcmF3IGNvdW50cyBzaW1pbGFyIHRvIHJlZ3VsYXIgUk5BLXNlcSBidXQgUlNFTSAoUk5BLXNlcSBieSBFeHBlY3RhdGlvbiBNYXhpbWl6YXRpb24pIGRhdGEgY2FuIGJlIHVzZWQgd2l0aCB0aGUgZWRnZVIgbWV0aG9kLgoKYGBge3IgYXV0b2RvYywgY2hpbGQ9J3N1cHBsZW1lbnRhcnlfcHJvdG9jb2xzMTIzX3NldHVwLlJtZCcsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9CmBgYCAKCgojIyBQcm9jZXNzIFJOQS1zZXEgZGF0YQpUaGlzIHBhcnQgb2YgdGhlIHN1cHBsZW1lbnRhcnkgcHJvdG9jb2wgZGVtb25zdHJhdGVzIGZpbHRlcmluZyBhbmQgc2NvcmluZyBSTkEtc2VxIGRhdGEgdXNpbmcgbm9ybWFsaXplZCBSTkEtc2VxIGNvdW50IGRhdGEgd2l0aCB0aGUgZWRnZVIgUiBwYWNrYWdlLiBUaGUgcHJvdG9jb2wgY2FuIGJlIHVzZWQgdG8gcHJvZHVjZSBpbnB1dCBkYXRhIGZvciBwYXRod2F5IGVucmljaG1lbnQgbWV0aG9kcyBsaWtlIGc6UHJvZmlsZXIsIEdTRUEgYW5kIG90aGVycy4gVGhpcyBSTkEtc2VxIGFuYWx5c2lzIHByb3RvY29sIGZvbGxvd3MgY29uY2VwdHVhbGx5IHNpbWlsYXIgc3RlcHMgdG8gbWljcm9hcnJheSBhbmFseXNpcyBzaG93biBhYm92ZS4gCgojIyMgTG9hZCByZXF1aXJlZCBwYWNrYWdlcwoxLiBMb2FkIHJlcXVpcmVkIEJpb2NvbmR1Y3RvciBwYWNrYWdlcyBpbnRvIFIuIApgYGB7ciwgd2FybmluZz1GQUxTRX0Ka25pdHI6Om9wdHNfa25pdCRzZXQoY2FjaGUgPSBUUlVFKQoKdHJ5Q2F0Y2goZXhwciA9IHsgbGlicmFyeSgiZWRnZVIiKX0sIAogICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIHsgCiAgICAgICAgICAgc291cmNlKCJodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYmlvY0xpdGUuUiIpCiAgICAgICAgICAgYmlvY0xpdGUoImVkZ2VSIil9LCAKICAgICAgICAgZmluYWxseSA9IGxpYnJhcnkoImVkZ2VSIikpCgp3b3JraW5nX2RpciA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwiZGF0YSIpCgojVGhlIGZpZWxkIGluIHRoZSBjbGFzcyBkZWZpbml0aW9uIGZpbGUgdGhhdCBkZWZpbmVzIHRoZSBjbGFzc2VzIG9mIHRoZSBkYXRhLgpkYXRhX2NsYXNzZXMgPC0gIlNVQlRZUEUiCmBgYAoKCiMjIyBMb2FkIEV4cHJlc3Npb24gRGF0YQoyLiBMb2FkIHRoZSBleHByZXNzaW9uIGRhdGFzZXQgb2YgMjk2IHR1bW91cnMsIHdpdGggNzkgY2xhc3NpZmllZCBhcyBJbW11bm9yZWFjdGl2ZSwgNzEgY2xhc3NpZmllZCBhcyBNZXNlbmNoeW1hbCwgNjcgY2xhc3NpZmllZCBhcyBEaWZmZXJlbnRpYXRlZCwgYW5kIDc5IGNsYXNzaWZpZWQgYXMgUHJvbGlmZXJhdGl2ZSBzYW1wbGVzLiBUaGUgVENHQSBjb3VudHMgZGF0YSB3YXMgcmV0cmlldmVkIGZyb20gdGhlIEdEQ1tAR0RDXSBhbmQgY29udGFpbmVkIGNvdW50cyBwZXIgbVJOQSB0cmFuc2NyaXB0IGRldGVybWluZWQgdXNpbmcgdGhlIFJTRU0gbWV0aG9kIGZvciAxOTk0NyB0cmFuc2NyaXB0cyBhbmQgMzAwIHNhbXBsZXMuCgpgYGB7cn0KUk5BU2VxIDwtIHJlYWQudGFibGUoIAogIGZpbGUucGF0aCh3b3JraW5nX2RpciwiU3VwcGxlbWVudGFyeV9UYWJsZTEwX1RDR0FfUk5BU2VxX3Jhd2NvdW50cy50eHQiKSwgIAogIGhlYWRlciA9IFRSVUUsIHNlcCA9ICJcdCIsIHF1b3RlPSJcIiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKYGBgCgojIyMgTG9hZCBzdWJ0eXBlIGluZm9ybWF0aW9uCjMuIExvYWQgc3VidHlwZSBjbGFzc2lmaWNhdGlvbiBvZiBzYW1wbGVzLiBUbyBjYWxjdWxhdGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24sIHdlIG5lZWQgdG8gZGVmaW5lIGF0IGxlYXN0IHR3byBzYW1wbGUgY2xhc3Nlcy4gQSBjb21tb24gZXhwZXJpbWVudGFsIGRlc2lnbiBpbnZvbHZlcyBjYXNlcyBhbmQgY29udHJvbHMgYnV0IGFueSB0d28gY2xhc3NlcyBjYW4gYmUgdXNlZC4gVGhlIGN1cnJlbnQgZGF0YXNldCBpcyBkaXZpZGVkIGludG8gTWVzZW5jaHltYWwgYW5kIEltbXVub3JlYWN0aXZlIGNsYXNzZXMgKGNsYXNzIGRlZmluaXRpb25zIHdlcmUgb2J0YWluZWQgZnJvbSBWZXJoYWFrIGV0IGFsLltAT1ZdIFN1cHBsZW1lbnRhcnkgVGFibGUgMSwgdGhpcmQgY29sdW1uKS4gQWZ0ZXIgbG9hZGluZyB0aGUgbWF0cml4LCBjaGVjayB0aGF0IHRoZSBjb2x1bW4gbmFtZXMgb2YgdGhlIGV4cHJlc3Npb24gbWF0cml4IGFuZCBjbGFzcyBkZWZpbml0aW9ucyBhcmUgZXF1YWwuCgpgYGB7cn0KY2xhc3NEZWZpbml0aW9uc19STkFTZXEgPC0gcmVhZC50YWJsZSggCiAgZmlsZS5wYXRoKHdvcmtpbmdfZGlyLCAiU3VwcGxlbWVudGFyeV9UYWJsZTExX1JOQVNlcV9jbGFzc2RlZmluaXRpb25zLnR4dCIpLCAKICBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZT0iXCIiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKIyMjIEZpbHRlciBEYXRhCjQuIEZpbHRlciBSTkEtc2VxIHJlYWRzLiBSTkEtc2VxIGRhdGEgYXJlIHByb2Nlc3NlZCBmb2xsb3dpbmcgdGhlIGVkZ2VSIHByb3RvY29sMTUgdGhhdCBmaWx0ZXJzIHJlYWRzIGJhc2VkIG9uIHRoZSBjb3VudHMgcGVyIG1pbGxpb24gKENQTSkgc3RhdGlzdGljLiBSTkEtc2VxIHJlYWQgY291bnRzIGFyZSBjb252ZXJ0ZWQgdG8gQ1BNIHZhbHVlcyBhbmQgZ2VuZXMgd2l0aCBDUE0gPiAxIGluIGF0IGxlYXN0IDUwIG9mIHRoZSBzYW1wbGVzIGFyZSByZXRhaW5lZCBmb3IgZnVydGhlciBzdHVkeSAoYSBnZW5lIG11c2ggaGF2ZSBhdCBsZWFzdCA1MCBtZWFzdXJlbWVudHMgd2l0aCBtb3JlIHRoYW4gMSBDUE0gaW4gb25lIG9mIHRoZSBjbGFzc2VzIHRvIGJlIGluY2x1ZGVkIGluIHRoZSBhbmFseXNpcykuIFRoaXMgc3RlcCByZW1vdmVzIGdlbmVzIHdpdGggdmVyeSBsb3cgcmVhZCBjb3VudHMgdGhhdCBhcmUgbGlrZWx5IG5vdCBleHByZXNzZWQgaW4gdGhlIG1ham9yaXR5IG9mIHNhbXBsZXMgYW5kIGNhdXNlIG5vaXNlIGluIHRoZSBkYXRhLiBOb3RlLCBDUE0gZmlsdGVyaW5nIGlzIHVzZWQgdG8gcmVtb3ZlIGxvdyBjb3VudHMgd2hpbGUgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmFseXNpcyBpcyBiYXNlZCBvbiBub3JtYWxpemVkIHJlYWQgY291bnRzIHdoaWNoIGFyZSBnZW5lcmF0ZWQgYmVsb3cgKHN0ZXAgNikuIAoKYGBge3J9CmNwbXMgPC0gY3BtKFJOQVNlcSkKa2VlcCA8LSByb3dTdW1zKGNwbXMgPiAxKSA+PSA1MApjb3VudHMgPC0gUk5BU2VxW2tlZXAsXQpgYGAKCgojIyMgTm9ybWFsaXphdGlvbiBhbmQgRGlzcGVyc2lvbgogCjUuIERhdGEgbm9ybWFsaXphdGlvbiwgZGlzcGVyc2lvbiBhbmFseXNpcyBpcyBwZXJmb3JtZWQgb24gdGhlIGVudGlyZSBkYXRhc2V0LiBDcmVhdGVkIE1EUy1wbG90IG9mIGFsbCBwYXRpZW50IHNhbXBsZXMgY2FuIGJlIHNlZW4gaW4gRmlndXJlIDEuIApgYGB7cn0KIyBjcmVhdGUgZGF0YSBzdHJ1Y3R1cmUgdG8gaG9sZCBjb3VudHMgYW5kIHN1YnR5cGUgaW5mb3JtYXRpb24gZm9yIGVhY2ggc2FtcGxlLgpkIDwtIERHRUxpc3QoY291bnRzPWNvdW50cywgZ3JvdXA9Y2xhc3NEZWZpbml0aW9uc19STkFTZXEkU1VCVFlQRSkKCiNOb3JtYWxpemUgdGhlIGRhdGEKZCA8LSBjYWxjTm9ybUZhY3RvcnMoZCkKCiNjcmVhdGUgbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nKE1EUykgcGxvdC4gIFRoZSBjb21tYW5kIGJlbG93IHdpbGwgYXV0b21hdGljYWxseSAKIyBnZW5lcmF0ZSB0aGUgcGxvdCBjb250YWluaW5nIGFsbCBzYW1wbGVzIHdoZXJlIGVhY2ggc3VidHlwZSBpcyBhIGRpZmZlcmVudCBjb2xvci4gIAojSWRlYWxseSB0aGVyZSBzaG91bGQgYmUgYSBnb29kIHNlcGFyYXRpb24gYmV0d2VlbiB0aGUgZGlmZmVyZW50IGNsYXNzZXMuCm1kc19maWxlbmFtZSA8LSBmaWxlLnBhdGgod29ya2luZ19kaXIsICJtZHNwbG90X2FsbHNhbXBsZXMucG5nIikKcG5nKGZpbGVuYW1lID0gbWRzX2ZpbGVuYW1lKQptZHNfb3V0cHV0IDwtIHBsb3RNRFMoZCwgbGFiZWxzPU5VTEwsIHBjaCA9IDEsIApjb2w9IGMoImRhcmtncmVlbiIsImJsdWUiLCJyZWQiLCAib3JhbmdlIilbZmFjdG9yKGNsYXNzRGVmaW5pdGlvbnNfUk5BU2VxJFNVQlRZUEUpXSwgCnhsaW0gPSBjKC0yLjUsNCksIHlsaW0gPSBjKC0yLjUsNCkpCgoKbGVnZW5kKCJ0b3ByaWdodCIsIAogICAgICAgbGVnZW5kPWxldmVscyhmYWN0b3IoY2xhc3NEZWZpbml0aW9uc19STkFTZXEkU1VCVFlQRSkpLCAKICAgICAgIHBjaD1jKDEpLCBjb2w9IGMoImRhcmtncmVlbiIsImJsdWUiLCJyZWQiLCAib3JhbmdlIiksdGl0bGU9IkNsYXNzIiwgIAogICAgICAgYnR5ID0gJ24nLCBjZXggPSAwLjc1KQoKZGV2Lm9mZigpCgojY2FsY3VsYXRlIGRpc3BlcnNpb24KZCA8LSBlc3RpbWF0ZUNvbW1vbkRpc3AoZCkKZCA8LSBlc3RpbWF0ZVRhZ3dpc2VEaXNwKGQpCmBgYAoKYGBge3IgIGVjaG89RkFMU0UsIGZpZy5jYXA9Ik1EUyBwbG90IG9mIGFsbCB0aGUgZGlmZmVyZW50IG92YXJpYW4gY2FuY2VyIHN1YnR5cGVzIiwgZmlnLmFsaWduPSJjZW50ZXIiLCBmaWcucG9zPSIhaHQifQppZihleGlzdHMoIm1kc19maWxlbmFtZSIpKXsKICBrbml0cjo6aW5jbHVkZV9ncmFwaGljcyhtZHNfZmlsZW5hbWUpCn0KYGBgCgojIyMgRmlsdGVyIHVuYW5ub3RhdGVkIGdlbmVzCjYuIChPcHRpb25hbCkgRXhjbHVkZSBnZW5lcyB3aXRoIG1pc3Npbmcgc3ltYm9scyBvciB1bmNoYXJhY3Rlcml6ZWQgZ2VuZXMuIEluIHRoaXMgZXhhbXBsZSBnZW5lIGVudHJpZXMgaW4gdGhlIGRhdGFzZXQgY29udGFpbmluZyDigJg/4oCZIG9yIHN0YXJ0aW5nIHdpdGggTE9DIGFyZSBleGNsdWRlZCBhcyB0aGV5IHJlcHJlc2VudCBub24tYW5ub3RhdGVkIGdlbmVzIG9yIG90aGVyIGxvY2kgdGhhdCBhcmUgbm90IHByZXNlbnQgaW4gcGF0aHdheSBkYXRhYmFzZXMuICBUaGUgZnJlcXVlbmN5IG9mIHRoZXNlIGFuZCBvdGhlciBub24gcHJvdGVpbiBjb2RpbmcgZW50cmllcyBpbiB5b3VyIGRhdGFzZXQgd2lsbCBkZXBlbmQgb24gdGhlIGRhdGFiYXNlIHVzZWQgdG8gYWxpZ24geW91ciBSTkFTZXEgZGF0YS4gCmBgYHtyfQojdGhlIGJlbG93IHJlZ3VsYXIgZXhwcmVzc2lvbiBleGNsdWRlcyBnZW5lIG5hbWVzIHRoYXQgYXJlID8gb3IgdGhhdCBzdGFydCB3aXRoIExPQwojIGFueSBudW1iZXIgb2YgYWRkaXRpb25hbCB0ZXJtcyBjYW4gYmUgYWRkZWQgdG8gdGhlIHJlZ3VsYXIgZXhwcmVzaW9uLCBmb3IgZXhhbXBsZSAKIyB0byBleGNsdWRlIGFueSBnZW5lcyB0aGF0IHN0YXJ0IHdpdGggIkhpcyIgYWRkIHxeSGlzIHRvIHRoZSByZWd1bGFyIGV4cHJlc3Npb24KZXhjbHVkZSA8LSBncmVwKCJcXD98XkxPQyIsIHJvd25hbWVzKGQpLCB2YWx1ZT1UKQpkIDwtIGRbd2hpY2goIXJvd25hbWVzKGQpICVpbiUgZXhjbHVkZSksXQpgYGAKCiMjIyBDYWxjdWxhdGUgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24KNy4gRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgaXMgcGVyZm9ybWVkIHdpdGggYSBzaW1wbGUgZGVzaWduIGFzIGRlc2NyaWJlZCBpbiB0aGUgZWRnZVIgcHJvdG9jb2xbQGVkZ2VSXS4KYGBge3J9CiNjYWxjdWxhdGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gc3RhdGlzdGljcyB3aXRoIGEgc2ltcGxlIGRlc2lnbgpkZSA8LSBleGFjdFRlc3QoZCwgcGFpcj1jKCJJbW11bm9yZWFjdGl2ZSIsIk1lc2VuY2h5bWFsIikpCnR0X2V4YWN0X3Rlc3QgPC0gdG9wVGFncyhkZSxuPW5yb3coZCkpCgojYWx0ZXJuYXRlbHkgeW91IGNhbiBhbHNvIHVzZSB0aGUgZ2xtIG1vZGVsIHVzaW5nIGNvbnRyYXN0cy4gIAojRm9yIGEgc2ltcGxlIDIgY2xhc3MgY29tcGFyaXNvbiB0aGlzIGlzIG5vdCByZXF1aXJlZCBidXQgaWYgeW91IHdhbnQgdG8gY29tcGFyZQojIDEgY2xhc3MgdG8gdGhlIHJlbWFpbmluZyAzIGNsYXNzZXMgdGhlbiB0aGlzIHNvcnQgb2YgbW9kZWwgaXMgdXNlZnVsLgpjbGFzc2VzIDwtIGZhY3RvcihjbGFzc0RlZmluaXRpb25zX1JOQVNlcVssZGF0YV9jbGFzc2VzXSkKbW9kZWxEZXNpZ24gPC0gbW9kZWwubWF0cml4KH4gMCArIGNsYXNzZXMpCgpjb250cmFzdF9tZXNlbnZzaW1tdW5vIDwtIG1ha2VDb250cmFzdHMoCiAgICAgICAgICAgICAgICAgIG1lc2VudnNpbW11bm8gPSJjbGFzc2VzTWVzZW5jaHltYWwtY2xhc3Nlc0ltbXVub3JlYWN0aXZlIiwKICAgICAgICAgICAgICAgICAgbGV2ZWxzPW1vZGVsRGVzaWduKQpmaXRfZ2xtIDwtIGdsbUZpdChkLG1vZGVsRGVzaWduKQptZXNlbnZzaW1tdW5vIDwtIGdsbUxSVChmaXRfZ2xtICwgY29udHJhc3QgPSBjb250cmFzdF9tZXNlbnZzaW1tdW5vKQp0dF9tZXNlbnZzaW1tdW5vIDwtIHRvcFRhZ3MobWVzZW52c2ltbXVubyxuPW5yb3coZCkpCgpgYGAKCkV4YW1wbGVzIG9mIGRpZmZlcmVudCBkZXNpZ25zIHRoYXQgY2FuIGJlIHVzZWQgZm9yIHRoaXMgZGF0YXNldC4gIEluc3RlYWQgb2Ygc2ltcGxlIHR3byBjbGFzcyBkZXNpZ24geW91IGNhbiBhbHNvIGNvbXBhcmUgb25lIGNsYXNzIHRvIHRoZSByZW1haW5pbmcgdGhyZWUgY2xhc3Nlcy4KCmBgYHtyfQpjbGFzc2VzIDwtIGZhY3RvcihjbGFzc0RlZmluaXRpb25zX1JOQVNlcVssZGF0YV9jbGFzc2VzXSkKbW9kZWxEZXNpZ24gPC0gbW9kZWwubWF0cml4KH4gMCArIGNsYXNzZXMpCgpjb250cmFzdF9pbW11bm8gPC0gbWFrZUNvbnRyYXN0cygKICBpbW11bm92c3Jlc3QgPSJjbGFzc2VzSW1tdW5vcmVhY3RpdmUtKGNsYXNzZXNNZXNlbmNoeW1hbCArIAogIGNsYXNzZXNQcm9saWZlcmF0aXZlICtjbGFzc2VzRGlmZmVyZW50aWF0ZWQpLzMiLAogIGxldmVscz1tb2RlbERlc2lnbikKZml0X2dsbSA8LSBnbG1GaXQoZCxtb2RlbERlc2lnbikKaW1tdW5vdnNyZXN0IDwtIGdsbUxSVChmaXRfZ2xtICwgY29udHJhc3QgPSBjb250cmFzdF9pbW11bm8pCnR0X2ltbXVub3ZzcmVzdCA8LSB0b3BUYWdzKGltbXVub3ZzcmVzdCxuPW5yb3coZCkpCgpjb250cmFzdF9tZXNlbiA8LSBtYWtlQ29udHJhc3RzKCAKICBtZXNlbnZzcmVzdCA9ICJjbGFzc2VzTWVzZW5jaHltYWwtKGNsYXNzZXNJbW11bm9yZWFjdGl2ZSArIAogIGNsYXNzZXNQcm9saWZlcmF0aXZlICtjbGFzc2VzRGlmZmVyZW50aWF0ZWQpLzMiLAogIGxldmVscz1tb2RlbERlc2lnbikKZml0X2dsbSA8LSBnbG1GaXQoZCxtb2RlbERlc2lnbikKbWVzZW52c3Jlc3QgPC0gZ2xtTFJUKGZpdF9nbG0gLCBjb250cmFzdCA9IGNvbnRyYXN0X21lc2VuKQp0dF9tZXNlbnZzcmVzdCA8LSB0b3BUYWdzKG1lc2VudnNyZXN0LG49bnJvdyhkKSkKCmNvbnRyYXN0X3Byb2xpZiA8LSBtYWtlQ29udHJhc3RzKCAKICBwcm9saWZ2c3Jlc3QgPSAiY2xhc3Nlc1Byb2xpZmVyYXRpdmUtKGNsYXNzZXNNZXNlbmNoeW1hbCArIAogIGNsYXNzZXNJbW11bm9yZWFjdGl2ZSArY2xhc3Nlc0RpZmZlcmVudGlhdGVkKS8zIiwKICBsZXZlbHM9bW9kZWxEZXNpZ24pCmZpdF9nbG0gPC0gZ2xtRml0KGQsbW9kZWxEZXNpZ24pCnByb2xpZnZzcmVzdCA8LSBnbG1MUlQoZml0X2dsbSAsIGNvbnRyYXN0ID0gY29udHJhc3RfcHJvbGlmKQp0dF9wcm9saWZ2c3Jlc3QgPC0gdG9wVGFncyhwcm9saWZ2c3Jlc3Qsbj1ucm93KGQpKQoKY29udHJhc3RfZGlmZiA8LSBtYWtlQ29udHJhc3RzKCAKICBkaWZmdnNyZXN0ID0gImNsYXNzZXNEaWZmZXJlbnRpYXRlZC0oY2xhc3Nlc01lc2VuY2h5bWFsICsgCiAgY2xhc3Nlc0ltbXVub3JlYWN0aXZlICtjbGFzc2VzUHJvbGlmZXJhdGl2ZSkvMyIsCiAgbGV2ZWxzPW1vZGVsRGVzaWduKQpmaXRfZ2xtIDwtIGdsbUZpdChkLG1vZGVsRGVzaWduKQpkaWZmdnNyZXN0IDwtIGdsbUxSVChmaXRfZ2xtICwgY29udHJhc3QgPSBjb250cmFzdF9kaWZmKQp0dF9kaWZmdnNyZXN0IDwtIHRvcFRhZ3MoZGlmZnZzcmVzdCxuPW5yb3coZCkpCmBgYAoKIyMjIENyZWF0ZSBnOlByb2ZpbGVyIGlucHV0IGxpc3QKOGEuIENyZWF0ZSB0aGUgZ2VuZSBsaXN0IGZvciB1c2UgaW4gZzpQcm9maWxlciBvciBhbm90aGVyIHRocmVzaG9sZGVkIGVucmljaG1lbnQgdG9vbC4gVGhlIGxpc3QgbWF5IGNvbXByaXNlIGFsbCBnZW5lcyB0aGF0IGhhdmUgYSBzaWduaWZpY2FudCBGRFItY29ycmVjdGVkIHAtdmFsdWUgKGNvZGUgc2hvd24gYmVsb3cpLCBhbGwgc2lnbmlmaWNhbnQgYW5kIEZEUi1jb3JyZWN0ZWQgdXAtcmVndWxhdGVkIGdlbmVzIGFuZCBhbGwgZG93bi1yZWd1bGF0ZWQgZ2VuZXMgc2VwYXJhdGVseSwgb3Igc29tZSBvdGhlciBjb21iaW5hdGlvbiBvZiB0aHJlc2hvbGRzLiBBbHNvIHNlZSBhbmFsb2dvdXMgc3RlcCBpbiB0aGUgbWljcm9hcnJheSBwcm90b2NvbC4KYGBge3J9CnR0IDwtIHR0X2V4YWN0X3Rlc3QKCiNnZXQgdGhlIGluZGljZXMgb2Ygc2NvcmVkIGRhdGFzZXQgdGhhdCBoYXZlIEZEUiA8IDAuMDUKc2VsZWN0X2dlbmVzID0gd2hpY2godHQkdGFibGUkRkRSIDwgMC4wNSkKCiNvdXRwdXQgaG93IG1hbnkgZ2VuZXMgdGhlcmUgYXJlIGluIHRoZSBzZXQgdGhhdCBoYXZlIEZEUiA8IDAuMDUKbGVuZ3RoKHNlbGVjdF9nZW5lcykKCiNnZW5lIG5hbWVzIGZyb20gdGhlIFRDR0Egc2V0IGNvbnRhaW4gZ2VuZSBuYW1lIGFuZCBlbnRyZXogZ2VuZSBpZHMgc2VwYXJhdGVkIGJ5IOKAmHzigJkKIyBmb3IgYWxsIHN1YnNlcXVlbnQgZW5yaWNobWVudCBhbmFseXNpcyB3ZSBuZWVkIHRvIGhhdmUganVzdCBvbmUgaWQuICBTZXBhcmF0ZSB0aGUgbmFtZXMgCiMgaW50byB0aGVpciB0d28gaWRzIGFuZCBrZWVwIHRoZSBnZW5lIHN5bWJvbHMKdG9wZ2VuZXNfcXZhbHVlMDA1IDwtIHVubGlzdChsYXBwbHkocm93bmFtZXModHQkdGFibGUpW3NlbGVjdF9nZW5lc10sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGRhdGEpIHt1bmxpc3Qoc3Ryc3BsaXQoZGF0YSwiXFx8IikpWzFdfSkpCgojb3V0cHV0IHRoZSB0b3AgNSBlbnRyaWVzIGluIHRoZSBsaXN0IG9mIHRvcCBnZW5lcwpoZWFkKHRvcGdlbmVzX3F2YWx1ZTAwNSkKCiN3cml0ZSByZXN1bHRzIG91dCB0byB0aGUgZmlsZS4gIFRoaXMgaXMgYW4gZXhhbXBsZSBvZiBhIHNldCB0aGF0IGNhbiBiZSB1c2VkIGZvcgojIFByb3RvY29sIDEKd3JpdGUudGFibGUodG9wZ2VuZXNfcXZhbHVlMDA1LCAKICAgICAgICAgICAgZmlsZS5wYXRoKHdvcmtpbmdfZGlyLCJNZXNlbmNoeW1hbHZzSW1tdW5vcmVhY3RpdmVfUk5Bc2VxX2FsbHNpZ25pZmljYW50Z2VuZXMudHh0IiksIAogICAgICAgICAgICBjb2wubmFtZXM9RkFMU0UsIHNlcD0iXHQiLCByb3cubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFKQoKYGBgCgojIyMgQ3JlYXRlIEdTRUEgaW5wdXQgbGlzdAo4Yi4gQ3JlYXRlIGEgdHdvLWNvbHVtbiByYW5rICguUk5LKSBmaWxlIG9mIGFsbCBnZW5lIElEcyBhbmQgY29ycmVzcG9uZGluZyBzY29yZXMgdG8gZm9yIEdTRUEgcHJlLXJhbmtlZCBhbmFseXNpcy4gVG8gcnVuIEdTRUEgaW4gcHJlLXJhbmtlZCBtb2RlLCB5b3UgbmVlZCBhIHR3byBjb2x1bW4gUk5LIGZpbGUgd2l0aCBnZW5lL3Byb3RlaW4vcHJvYmUgbmFtZSAoY29sdW1uIDEpIGFuZCB0aGUgYXNzb2NpYXRlZCBzY29yZSAoY29sdW1uIDIpLiBUaGUgZmlyc3QgY29sdW1uIHNob3VsZCBjb250YWluIHRoZSBzYW1lIHR5cGUgb2YgZ2VuZSBJRHMgdXNlZCBpbiB0aGUgcGF0aHdheSBnZW5lLXNldCAoR01UKSBmaWxlLiAgR1NFQSB3aWxsIGxvb2sgZm9yIGVucmljaG1lbnQgaW4gdGhlIHNldCBvZiBtb3N0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBhdCB0aGUgdG9wIG9mIHRoZSBsaXN0IGFzIHdlbGwgYXMgdGhvc2UgYXQgdGhlIGJvdHRvbSBvZiB0aGUgbGlzdC4gR2VuZXMgYXQgdGhlIHRvcCBvZiB0aGUgbGlzdCBhcmUgbW9yZSBoaWdobHkgZXhwcmVzc2VkIGluIGNsYXNzIEEgb2Ygc2FtcGxlcyAoZS5nLiwgTWVzZW5jaHltYWwpIHdoaWxlIGdlbmVzIGF0IHRoZSBib3R0b20gYXJlIGhpZ2hseSBleHByZXNzZWQgaW4gY2xhc3MgQiAoZS5nLiwgSW1tdW5vcmVhY3RpdmUpLiBBIHNjb3JlIGNhbiBiZSBjb21wdXRlZCBieSBtdWx0aXBseWluZyBkaXJlY3Rpb24gKHNpZ24pIG9mIGZvbGQgY2hhbmdlIGFuZCBsb2dhcml0aG0gb2YgcC12YWx1ZSBmb3IgZWFjaCBnZW5lLgpgYGB7cn0KI2NhbGN1bGF0ZSByYW5rcwpyYW5rc19STkFzZXEgPSBzaWduKHR0JHRhYmxlJGxvZ0ZDKSAqIC1sb2cxMCh0dCR0YWJsZSRQVmFsdWUpCgojZ2VuZSBuYW1lcyBmcm9tIHRoZSBUQ0dBIHNldCBjb250YWluIGdlbmUgbmFtZSBhbmQgZW50cmV6IGdlbmUgaWRzIHNlcGFyYXRlZCBieSDigJh84oCZCiMgZm9yIGFsbCBzdWJzZXF1ZW50IGVucmljaG1lbnQgYW5hbHlzaXMgd2UgbmVlZCB0byBoYXZlIGp1c3Qgb25lIGlkLiAgU2VwYXJhdGUgdGhlIG5hbWVzIAojIGludG8gdGhlaXIgdHdvIGlkcy4KZ2VuZW5hbWVzIDwtIHVubGlzdChsYXBwbHkoIHJvd25hbWVzKHR0JHRhYmxlKSwgZnVuY3Rpb24oZGF0YSkgCiAge3VubGlzdChzdHJzcGxpdChkYXRhLCJcXHwiKSlbMV19KSkKZ2VuZWlkcyA8LSB1bmxpc3QobGFwcGx5KCByb3duYW1lcyh0dCR0YWJsZSksICAKICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihkYXRhKSB7dW5saXN0KHN0cnNwbGl0KGRhdGEsIlxcfCIpKVsyXX0pKSAKCiNjcmVhdGUgcmFua3MgZmlsZQpyYW5rc19STkFzZXEgPC0gY2JpbmQoZ2VuZW5hbWVzLCByYW5rc19STkFzZXEpCmNvbG5hbWVzKHJhbmtzX1JOQXNlcSkgPC0gYygiR2VuZU5hbWUiLCJyYW5rIikKCiNzb3J0IHJhbmtzIGluIGRlY3JlYXNpbmcgb3JkZXIKcmFua3NfUk5Bc2VxIDwtIHJhbmtzX1JOQXNlcVtvcmRlcihhcy5udW1lcmljKHJhbmtzX1JOQXNlcVssMl0pLGRlY3JlYXNpbmcgPSBUUlVFKSxdCgp3cml0ZS50YWJsZShyYW5rc19STkFzZXEsIGZpbGUucGF0aCh3b3JraW5nX2RpciwKICAgICAgICAgICAgICAgICJTdXBwbGVtZW50YXJ5X1RhYmxlMl9NZXNlbnZzSW1tdW5vX1JOQVNlcV9yYW5rcy5ybmsiKSwgCiAgICAgICAgICAgIGNvbC5uYW1lID0gVFJVRSwgc2VwPSJcdCIsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQpgYGAKClNtYWxsIHNlY3Rpb24gb2YgdGhlIHRvcCBhbmQgYm90dG9tIG9mIHRoZSByZXN1bHRpbmcgcmFuayBmaWxlOgpgYGB7cn0KaGVhZChyYW5rc19STkFzZXEpCnRhaWwocmFua3NfUk5Bc2VxKQpgYGAKCgojIyMgQ3JlYXRlIGV4cHJlc3Npb24gZmlsZQoKOWEuIChPcHRpb25hbCkgQ3JlYXRlIGFuIGV4cHJlc3Npb24gZmlsZSBmb3IgdGhlIGVucmljaG1lbnQgbWFwIGFuZCBzYXZlIGl0IHRvIGEgZmlsZSBpbiB0aGUgd29ya2luZyBmb2xkZXIuIFRoZSBvcHRpb25hbCBleHByZXNzaW9uIGZpbGUgaXMgY3JlYXRlZCBmcm9tIHRoZSBvcmlnaW5hbCBSTkEtc2VxIGV4cHJlc3Npb24gbWF0cml4IHVzZWQgZm9yIHRoYSBhbmx5c2lzICh2YXJpYWJsZSBkIGFib3ZlKSB3aXRoIHRoZSBhZGRpdGlvbiBvZiBhIGNvbHVtbiBvbiB0aGUgbGVmdCBlZGdlIG9mIHRoZSBtYXRyaXguIFRoZSBhZGRpdGlvbmFsIGZpZWxkIG9mdGVuIGNvbnRhaW5zIGEgZ2VuZSBkZXNjcmlwdGlvbiBob3dldmVyIGFueSB0ZXh0IHZhbHVlIGNhbiBiZSBhZGRlZC4KYGBge3IsIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojZml4IGlzc3VlIHdpdGggYmlvbWFydCBub3Qgd29ya2luZyBiZWNhdXNlIG9mIHVybCByZWRpcmVjdGlvbgpvcHRpb25zKFJDdXJsT3B0aW9ucz1saXN0KGZvbGxvd2xvY2F0aW9uPVRSVUUsIHBvc3RyZWRpcj0yTCkpCgpub3JtYWxpemVkX2V4cHJlc3Npb25fUk5Bc2VxIDwtIGNwbShkLCBub3JtYWxpemVkLmxpYi5zaXplPVRSVUUpCgojRnJvbSB0aGUgcm93bmFtZXMgcGFyc2Ugb3V0IHRoZSBnZW5lIG5hbWUgYW5kIHRoZSBnZW5laWRzCmdlbmVuYW1lcyA8LSB1bmxpc3QobGFwcGx5KCByb3duYW1lcyhub3JtYWxpemVkX2V4cHJlc3Npb25fUk5Bc2VxKSwgCmZ1bmN0aW9uKGRhdGEpIHt1bmxpc3Qoc3Ryc3BsaXQoZGF0YSwiXFx8IikpWzFdfSkpCmdlbmVpZHMgPC0gdW5saXN0KGxhcHBseSggcm93bmFtZXMobm9ybWFsaXplZF9leHByZXNzaW9uX1JOQXNlcSksIApmdW5jdGlvbihkYXRhKSB7dW5saXN0KHN0cnNwbGl0KGRhdGEsIlxcfCIpKVsyXX0pKQoKRU1fZXhwcmVzc2lvbkZpbGVfUk5Bc2VxIDwtIGRhdGEuZnJhbWUoTmFtZSA9IGdlbmVuYW1lcywgbm9ybWFsaXplZF9leHByZXNzaW9uX1JOQXNlcSkKcm93bmFtZXMoRU1fZXhwcmVzc2lvbkZpbGVfUk5Bc2VxKSA8LSByb3duYW1lcyhub3JtYWxpemVkX2V4cHJlc3Npb25fUk5Bc2VxKQpjb2xuYW1lcyhFTV9leHByZXNzaW9uRmlsZV9STkFzZXEpIDwtIHN1YnN0cmluZyhjb2xuYW1lcyhFTV9leHByZXNzaW9uRmlsZV9STkFzZXEpLDEsMTIpCgojQWRkIGRlc2NyaXB0aW9ucyBpbnN0ZWFkIG9mIGdlbmVpZHMKdHJ5Q2F0Y2goZXhwciA9IHsgbGlicmFyeSgiYmlvbWFSdCIpfSwgCiAgICAgICAgIGVycm9yID0gZnVuY3Rpb24oZSkgeyAKICAgICAgICAgICBzb3VyY2UoImh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9iaW9jTGl0ZS5SIikKICAgICAgICAgICBiaW9jTGl0ZSgiYmlvbWFSdCIpfSwgCiAgICAgICAgIGZpbmFsbHkgPSBsaWJyYXJ5KCJiaW9tYVJ0IikpCm1hcnQgPSB1c2VNYXJ0KGJpb21hcnQ9IkVOU0VNQkxfTUFSVF9FTlNFTUJMIikKbWFydCA9IHVzZURhdGFzZXQobWFydCwgZGF0YXNldD0iaHNhcGllbnNfZ2VuZV9lbnNlbWJsIiApCgpnZW5lcyA9IGdldEJNKGF0dHJpYnV0ZXMgPSBjKCAnaGduY19zeW1ib2wnLCAnZGVzY3JpcHRpb24nKSwgZmlsdGVycz0naGduY19zeW1ib2wnLCAKICAgICAgICAgICAgICB2YWx1ZXM9Z2VuZW5hbWVzLCBtYXJ0PW1hcnQpOwpnZW5lcyRkZXNjcmlwdGlvbiA9IGdzdWIoIlxcW1NvdXJjZS4qIiwgIiIsIGdlbmVzJGRlc2NyaXB0aW9uKTsKCkVNX2V4cHJlc3Npb25GaWxlX1JOQXNlcSA8LSBtZXJnZShnZW5lcyxFTV9leHByZXNzaW9uRmlsZV9STkFzZXEsICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbC55PVRSVUUsYnkueD0xLCBieS55PTEpCmNvbG5hbWVzKEVNX2V4cHJlc3Npb25GaWxlX1JOQXNlcSlbMV0gPC0gIk5hbWUiCmNvbG5hbWVzKEVNX2V4cHJlc3Npb25GaWxlX1JOQXNlcSlbMl0gPC0gIkRlc2NyaXB0aW9uIgoKd3JpdGUudGFibGUoRU1fZXhwcmVzc2lvbkZpbGVfUk5Bc2VxLCAKICAgICAgICAgICAgZmlsZS5wYXRoKHdvcmtpbmdfZGlyLAogICAgICAgICAgICAgICAgICAiU3VwcGxlbWVudGFyeV9UYWJsZTZfVENHQV9PVl9STkFzZXFfZXhwcmVzc2lvbi50eHQiKSwKICAgICAgICAgICAgY29sLm5hbWU9VFJVRSxzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCmBgYAojIyMgQ3JlYXRlIGEgR1NFQSBjbGFzcyBmaWxlCjliLiAoT3B0aW9uYWwpIEdTRUEgQ0xTIGZpbGUgZGVmaW5pbmcgdGhlIHBoZW5vdHlwZSAoaS5lLiBiaW9sb2dpY2FsIGNvbmRpdGlvbnMpIG9mIGVhY2ggc2FtcGxlIGluIHRoZSBleHByZXNzaW9uIGZpbGUsIGZvciBleGFtcGxlLCBzZWUgU3VwcGxlbWVudGFyeV9UYWJsZTdfVENHQV9PVl9STkFzZXFfY2xhc3Nlcy5jbHMuIFRoaXMgZmlsZSBpcyBvbmx5IHJlcXVpcmVkIGZvciBwaGVub3R5cGUgcmFuZG9taXphdGlvbiBpbiBHU0VBLCBob3dldmVyIHByb3ZpZGluZyBpdCB0byBFbnJpY2htZW50TWFwIHdpbGwgbGFiZWwgdGhlIGNvbHVtbnMgb2YgdGhlIGV4cHJlc3Npb24gZmlsZSBpbiB0aGUgRW5yaWNobWVudE1hcCBoZWF0IG1hcCB2aWV3ZXIgYnkgcGhlbm90eXBlLgpgYGB7cn0KI3dyaXRlIG91dCBhIEdTRUEgY2xhc3NlcyBmaWxlLiAob3B0aW9uYWwpCmZpbGVDb25uIDwtIGZpbGUoCiAgZmlsZS5wYXRoKHdvcmtpbmdfZGlyLCJTdXBwbGVtZW50YXJ5X1RhYmxlN19UQ0dBX09WX1JOQXNlcV9jbGFzc2VzLmNscyIpKQp3cml0ZUxpbmVzKGMocGFzdGUobGVuZ3RoKGNsYXNzRGVmaW5pdGlvbnNfUk5BU2VxWyxkYXRhX2NsYXNzZXNdKSwgIjQgMSIpLCAKICAgICAgICAgICAgIHBhc3RlKCIjICIsIHVuaXF1ZShjbGFzc0RlZmluaXRpb25zX1JOQVNlcVssZGF0YV9jbGFzc2VzXSlbMV0sICIgIiwKICAgICAgICAgICAgICAgICAgIHVuaXF1ZShjbGFzc0RlZmluaXRpb25zX1JOQVNlcVssZGF0YV9jbGFzc2VzXSlbMl0sICIgIiwKICAgICAgICAgICAgICAgICAgIHVuaXF1ZShjbGFzc0RlZmluaXRpb25zX1JOQVNlcVssZGF0YV9jbGFzc2VzXSlbM10sICIgIiwKICAgICAgICAgICAgICAgICAgIHVuaXF1ZShjbGFzc0RlZmluaXRpb25zX1JOQVNlcVssZGF0YV9jbGFzc2VzXSlbNF0pKSwgZmlsZUNvbm4pCndyaXRlLnRhYmxlKHQoY2xhc3NEZWZpbml0aW9uc19STkFTZXFbLGRhdGFfY2xhc3Nlc10pLCAKICAgICAgICAgICAgZmlsZS5wYXRoKHdvcmtpbmdfZGlyLCJTdXBwbGVtZW50YXJ5X1RhYmxlN19UQ0dBX09WX1JOQXNlcV9jbGFzc2VzLmNscyIpLCAKICAgICAgICAgICAgY29sLm5hbWU9RkFMU0UsIHNlcD0iXHQiLAogICAgICAgICAgICByb3cubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFLCBhcHBlbmQ9VFJVRSkKY2xvc2UoZmlsZUNvbm4pCmBgYAoKCiMjIyBFeGFtaW5lIHJlc3VsdHMKOWMuIChPcHRpb25hbCkgRXhhbWluZSBnZW5lIGV4cHJlc3Npb24gZGF0YSB1c2luZyBoZWF0IG1hcHMuIEhlYXQgbWFwcyBjYW4gZWFzaWx5IHNob3cgdGhlIHNlcGFyYXRpb24gYmV0d2VlbiBzYW1wbGUgY2xhc3NlcywgbGFiZWxlZCBieSBjb2xvcnMgaW4gdGhlIGhlYXQgbWFwIGhlYWRlci4gQnkgbGltaXRpbmcgdG8gdGhlIG1vc3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgbGlzdCBvZiBnZW5lcyAoRkRSLWNvcnJlY3RlZCBwPDAuMDUpIHdlIGNhbiB2ZXJpZnkgd2hldGhlciB0aGUgc2NvcmluZyBhY2N1cmF0ZWx5IHNlcGFyYXRlcyBjbGFzcyBBIGZyb20gY2xhc3MgQi4gUmVzdWx0aW5nIGhlYXRtYXAgaXMgc2hvd24gaW4gRmlndXJlIDIuCmBgYHtyfQp0cnlDYXRjaChleHByID0geyBsaWJyYXJ5KCJwaGVhdG1hcCIpfSwgCiAgICAgICAgIGVycm9yID0gZnVuY3Rpb24oZSkgeyAKICAgICAgICAgIGluc3RhbGwucGFja2FnZXMoInBoZWF0bWFwIil9LCAKICAgICAgICAgZmluYWxseSA9IGxpYnJhcnkoInBoZWF0bWFwIikpCnRyeUNhdGNoKGV4cHIgPSB7IGxpYnJhcnkoIlJDb2xvckJyZXdlciIpfSwgCiAgICAgICAgIGVycm9yID0gZnVuY3Rpb24oZSkgeyAKICAgICAgICAgIGluc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpfSwgCiAgICAgICAgIGZpbmFsbHkgPSBsaWJyYXJ5KCJSQ29sb3JCcmV3ZXIiKSkKCmFubm90YXRpb25fY29sIDwtIGRhdGEuZnJhbWUoU1VCVFlQRT1mYWN0b3IoY2xhc3NEZWZpbml0aW9uc19STkFTZXFbLGRhdGFfY2xhc3Nlc10pKQpyb3duYW1lcyhhbm5vdGF0aW9uX2NvbCkgPC0gc3Vic3RyKGNsYXNzRGVmaW5pdGlvbnNfUk5BU2VxWywyXSwxLDEyKQoKYW5uX2NvbG9ycyA9IGxpc3QoU1VCVFlQRSA9IGMoSW1tdW5vcmVhY3RpdmU9ImJsdWUiLCBNZXNlbmNoeW1hbD0icmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJvbGlmZXJhdGl2ZSA9ICJvcmFuZ2UiLERpZmZlcmVudGlhdGVkPSJkYXJrZ3JlZW4iKSkKY29sLnBhbCA8LSByZXYoYnJld2VyLnBhbCgxMSwgIlJkQnUiKSkKCmdlbmVzX3RvX3NlbGVjdCA8LSB1bmxpc3QobGFwcGx5KCByb3duYW1lcyh0dCR0YWJsZSlbd2hpY2godHQkdGFibGUkRkRSPDAuMDUpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihkYXRhKSB7dW5saXN0KHN0cnNwbGl0KGRhdGEsIlxcfCIpKVsxXX0pKQoKbWF0cml4X2Zvcl9oZWF0bWFwIDwtIGFzLm1hdHJpeChFTV9leHByZXNzaW9uRmlsZV9STkFzZXFbd2hpY2goRU1fZXhwcmVzc2lvbkZpbGVfUk5Bc2VxWywxXSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICVpbiUgZ2VuZXNfdG9fc2VsZWN0ICksMzpkaW0oRU1fZXhwcmVzc2lvbkZpbGVfUk5Bc2VxKVsyXSBdKQoKY2xhc3MobWF0cml4X2Zvcl9oZWF0bWFwKSA8LSAibnVtZXJpYyIKbWF0cml4X2Zvcl9oZWF0bWFwW21hdHJpeF9mb3JfaGVhdG1hcCA9PSAwXSA8LSAwLjAwMDAwMDEKCgoKYGBgCgpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpoZWF0bWFwX2ZpbGVuYW1lIDwtIGZpbGUucGF0aCh3b3JraW5nX2RpciwgImhlYXRtYXBfYWxsc2FtcGxlcy5wbmciKQpwbmcoZmlsZW5hbWUgPSBoZWF0bWFwX2ZpbGVuYW1lKQpwaGVhdG1hcChtYXRyaXhfZm9yX2hlYXRtYXAsIGNvbG9yPWNvbC5wYWwsIHNjYWxlPSJyb3ciLCAKICAgICAgICAga21lYW5zX2s9TkEsIHNob3dfcm93bmFtZXM9RkFMU0UsIHNob3dfY29sbmFtZXM9RkFMU0UsIAogICAgICAgICBtYWluPSJoZWF0bWFwIHRvcCBnZW5lcyhNZXNlbiB2cyBJbW11bm8pIiwgY2x1c3Rlcl9yb3dzPVRSVUUsIAogICAgICAgICBjbHVzdGVyX2NvbHM9RkFMU0UsIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cz0iY29ycmVsYXRpb24iLAogICAgICAgICBhbm5vdGF0aW9uX2NvbD1hbm5vdGF0aW9uX2NvbCwgYW5ub3RhdGlvbl9jb2xvcnM9YW5uX2NvbG9ycykKZGV2Lm9mZigpCmBgYAoKCgpgYGB7ciAgZWNobz1GQUxTRSwgZmlnLmNhcD0iSGVhdG1hcCBvZiBhbGwgZ2VuZXMgYW5kIGFsbCBwYXRpZW50IHNhbXBsZXMuICBEaWZmZXJlbnQgcGF0aWVudCBjYW5jZXIgdHlwZXMgYXJlIG1hcmtlZCBvbiB0aGUgeC1heGlzLiAgR2VuZXMgYXJlIGNsdXN0ZXJlZCBhY2NvcmRpbmcgdG8gZXhwcmVzc2lvbiBhbmQgcGF0aWVudHMgYXJlIG9yZGVyZWQgYWNjb3JkaW5nIHRvIHN1YnR5cGUuIiwgZmlnLmFsaWduPSJjZW50ZXIiLCBmaWcucG9zPSIhaHQifQppZihleGlzdHMoImhlYXRtYXBfZmlsZW5hbWUiKSl7CiAga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVhdG1hcF9maWxlbmFtZSkKfQpgYGAKCgojIyMgRXhhbXBsZSBvZiBvdGhlciBjb21wYXJpc29ucwoKRXhhbXBsZSBvZiBvdGhlciBjb21wYXJpc29uIHRoYXQgY2FuIGJlIGRvbmUgd2l0aCB0aGlzIGRhdGFzZXQuIFJhbmsgZmlsZXMgZm9yIGVhY2ggYXJlIGFsc28gY3JlYXRlZApgYGB7cn0KI0ltbXVubyB2cyByZXN0CnR0IDwtIHR0X2ltbXVub3ZzcmVzdApzZWxlY3RfZ2VuZXMgPSB3aGljaCh0dCR0YWJsZSRGRFI8MC4wNSkKbGVuZ3RoKHNlbGVjdF9nZW5lcykKdG9wZ2VuZXNfcXZhbHVlMDA1IDwtIHVubGlzdChsYXBwbHkoIHJvd25hbWVzKHR0JHRhYmxlKVtzZWxlY3RfZ2VuZXNdLCAKZnVuY3Rpb24oZGF0YSkge3VubGlzdChzdHJzcGxpdChkYXRhLCJcXHwiKSlbMV19KSkKaGVhZCh0b3BnZW5lc19xdmFsdWUwMDUpCndyaXRlLnRhYmxlKHRvcGdlbmVzX3F2YWx1ZTAwNSwgCmZpbGUucGF0aCh3b3JraW5nX2RpciwiSW1tdW5vdnNSZXN0X1JOQXNlcV9hbGxzaWduaWZpY2FudGdlbmVzLnR4dCIpLCAKY29sLm5hbWVzPUZBTFNFLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCnJhbmtzX1JOQXNlcSA9IHNpZ24odHQkdGFibGUkbG9nRkMpICogLWxvZzEwKHR0JHRhYmxlJFBWYWx1ZSkKZ2VuZW5hbWVzIDwtIHVubGlzdChsYXBwbHkoIHJvd25hbWVzKHR0JHRhYmxlKSwgZnVuY3Rpb24oZGF0YSkgCiAge3VubGlzdChzdHJzcGxpdChkYXRhLCJcXHwiKSlbMV19KSkKZ2VuZWlkcyA8LSB1bmxpc3QobGFwcGx5KCByb3duYW1lcyh0dCR0YWJsZSksICAKICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihkYXRhKSB7dW5saXN0KHN0cnNwbGl0KGRhdGEsIlxcfCIpKVsyXX0pKSAKcmFua3NfUk5Bc2VxIDwtIGNiaW5kKGdlbmVuYW1lcywgcmFua3NfUk5Bc2VxKQpjb2xuYW1lcyhyYW5rc19STkFzZXEpIDwtIGMoIkdlbmVOYW1lIiwicmFuayIpCndyaXRlLnRhYmxlKHJhbmtzX1JOQXNlcSwgZmlsZS5wYXRoKHdvcmtpbmdfZGlyLCJJbW11bm92c1Jlc3RfUk5BU2VxX3JhbmtzLnJuayIpLCAKY29sLm5hbWUgPSBUUlVFLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgoKI01lc2VuIHZzIHJlc3QgZGF0YQp0dCA8LSB0dF9tZXNlbnZzcmVzdApzZWxlY3RfZ2VuZXMgPSB3aGljaCh0dCR0YWJsZSRGRFI8MC4wNSkKbGVuZ3RoKHNlbGVjdF9nZW5lcykKdG9wZ2VuZXNfcXZhbHVlMDA1IDwtIHVubGlzdChsYXBwbHkoIHJvd25hbWVzKHR0JHRhYmxlKVtzZWxlY3RfZ2VuZXNdLCAKZnVuY3Rpb24oZGF0YSkge3VubGlzdChzdHJzcGxpdChkYXRhLCJcXHwiKSlbMV19KSkKaGVhZCh0b3BnZW5lc19xdmFsdWUwMDUpCndyaXRlLnRhYmxlKHRvcGdlbmVzX3F2YWx1ZTAwNSwgCmZpbGUucGF0aCh3b3JraW5nX2RpciwiTWVzZW52c1Jlc3RfUk5Bc2VxX2FsbHNpZ25pZmljYW50Z2VuZXMudHh0IiksIApjb2wubmFtZXM9RkFMU0UsIHNlcD0iXHQiLCByb3cubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFKQoKcmFua3NfUk5Bc2VxID0gc2lnbih0dCR0YWJsZSRsb2dGQykgKiAtbG9nMTAodHQkdGFibGUkUFZhbHVlKQpnZW5lbmFtZXMgPC0gdW5saXN0KGxhcHBseSggcm93bmFtZXModHQkdGFibGUpLCBmdW5jdGlvbihkYXRhKSAKICB7dW5saXN0KHN0cnNwbGl0KGRhdGEsIlxcfCIpKVsxXX0pKQpnZW5laWRzIDwtIHVubGlzdChsYXBwbHkoIHJvd25hbWVzKHR0JHRhYmxlKSwgIAogICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGRhdGEpIHt1bmxpc3Qoc3Ryc3BsaXQoZGF0YSwiXFx8IikpWzJdfSkpIApyYW5rc19STkFzZXEgPC0gY2JpbmQoZ2VuZW5hbWVzLCByYW5rc19STkFzZXEpCmNvbG5hbWVzKHJhbmtzX1JOQXNlcSkgPC0gYygiR2VuZU5hbWUiLCJyYW5rIikKd3JpdGUudGFibGUocmFua3NfUk5Bc2VxLCBmaWxlLnBhdGgod29ya2luZ19kaXIsIk1lc2VudnNSZXN0X1JOQVNlcV9yYW5rcy5ybmsiKSwgCmNvbC5uYW1lID0gVFJVRSwgc2VwPSJcdCIsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKI0RpZmZlcmVudGlhdGVkIHZzIHJlc3QgZGF0YQp0dCA8LSB0dF9kaWZmdnNyZXN0CnNlbGVjdF9nZW5lcyA9IHdoaWNoKHR0JHRhYmxlJEZEUjwwLjA1KQpsZW5ndGgoc2VsZWN0X2dlbmVzKQp0b3BnZW5lc19xdmFsdWUwMDUgPC0gdW5saXN0KGxhcHBseSggcm93bmFtZXModHQkdGFibGUpW3NlbGVjdF9nZW5lc10sIApmdW5jdGlvbihkYXRhKSB7dW5saXN0KHN0cnNwbGl0KGRhdGEsIlxcfCIpKVsxXX0pKQpoZWFkKHRvcGdlbmVzX3F2YWx1ZTAwNSkKd3JpdGUudGFibGUodG9wZ2VuZXNfcXZhbHVlMDA1LCAKZmlsZS5wYXRoKHdvcmtpbmdfZGlyLCJEaWZmdnNSZXN0X1JOQXNlcV9hbGxzaWduaWZpY2FudGdlbmVzLnR4dCIpLCAKY29sLm5hbWVzPUZBTFNFLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCnJhbmtzX1JOQXNlcSA9IHNpZ24odHQkdGFibGUkbG9nRkMpICogLWxvZzEwKHR0JHRhYmxlJFBWYWx1ZSkKZ2VuZW5hbWVzIDwtIHVubGlzdChsYXBwbHkoIHJvd25hbWVzKHR0JHRhYmxlKSwgZnVuY3Rpb24oZGF0YSkgCiAge3VubGlzdChzdHJzcGxpdChkYXRhLCJcXHwiKSlbMV19KSkKZ2VuZWlkcyA8LSB1bmxpc3QobGFwcGx5KCByb3duYW1lcyh0dCR0YWJsZSksICAKICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihkYXRhKSB7dW5saXN0KHN0cnNwbGl0KGRhdGEsIlxcfCIpKVsyXX0pKSAKcmFua3NfUk5Bc2VxIDwtIGNiaW5kKGdlbmVuYW1lcywgcmFua3NfUk5Bc2VxKQpjb2xuYW1lcyhyYW5rc19STkFzZXEpIDwtIGMoIkdlbmVOYW1lIiwicmFuayIpCndyaXRlLnRhYmxlKHJhbmtzX1JOQXNlcSwgZmlsZS5wYXRoKHdvcmtpbmdfZGlyLCJEaWZmdnNSZXN0X1JOQVNlcV9yYW5rcy5ybmsiKSwgCmNvbC5uYW1lID0gVFJVRSwgc2VwPSJcdCIsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKI1Byb2xpZmVyYXRpdmUgdnMgcmVzdCBkYXRhCnR0IDwtIHR0X3Byb2xpZnZzcmVzdApzZWxlY3RfZ2VuZXMgPSB3aGljaCh0dCR0YWJsZSRGRFI8MC4wNSkKbGVuZ3RoKHNlbGVjdF9nZW5lcykKdG9wZ2VuZXNfcXZhbHVlMDA1IDwtIHVubGlzdChsYXBwbHkoIHJvd25hbWVzKHR0JHRhYmxlKVtzZWxlY3RfZ2VuZXNdLCAKZnVuY3Rpb24oZGF0YSkge3VubGlzdChzdHJzcGxpdChkYXRhLCJcXHwiKSlbMV19KSkKaGVhZCh0b3BnZW5lc19xdmFsdWUwMDUpCndyaXRlLnRhYmxlKHRvcGdlbmVzX3F2YWx1ZTAwNSwgCmZpbGUucGF0aCh3b3JraW5nX2RpciwiUHJvbGlmdnNSZXN0X1JOQXNlcV9hbGxzaWduaWZpY2FudGdlbmVzLnR4dCIpLCAKY29sLm5hbWVzPUZBTFNFLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCnJhbmtzX1JOQXNlcSA9IHNpZ24odHQkdGFibGUkbG9nRkMpICogLWxvZzEwKHR0JHRhYmxlJFBWYWx1ZSkKZ2VuZW5hbWVzIDwtIHVubGlzdChsYXBwbHkoIHJvd25hbWVzKHR0JHRhYmxlKSwgZnVuY3Rpb24oZGF0YSkgCiAge3VubGlzdChzdHJzcGxpdChkYXRhLCJcXHwiKSlbMV19KSkKZ2VuZWlkcyA8LSB1bmxpc3QobGFwcGx5KCByb3duYW1lcyh0dCR0YWJsZSksICAKICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbihkYXRhKSB7dW5saXN0KHN0cnNwbGl0KGRhdGEsIlxcfCIpKVsyXX0pKSAKcmFua3NfUk5Bc2VxIDwtIGNiaW5kKGdlbmVuYW1lcywgcmFua3NfUk5Bc2VxKQpjb2xuYW1lcyhyYW5rc19STkFzZXEpIDwtIGMoIkdlbmVOYW1lIiwicmFuayIpCndyaXRlLnRhYmxlKHJhbmtzX1JOQXNlcSwgZmlsZS5wYXRoKHdvcmtpbmdfZGlyLCJQcm9saWZ2c1Jlc3RfUk5BU2VxX3JhbmtzLnJuayIpLCAKY29sLm5hbWUgPSBUUlVFLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCmBgYAoKYGBge3IsIGV2YWwgPSBGQUxTRSxlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGtuaXRyKQphc2lzX291dHB1dCgiIyMgUmVmZXJlbmNlc1xcbiIpICMgSGVhZGVyIHRoYXQgaXMgb25seSBzaG93biBpZiBhZGRfc2V0dXAgPT0gVFJVRQpgYGAK