1 Evaluate Previous Result: version: 20171205

1.1 TODO

  1. Replace rnaseq data used with a more appropriate set. (Sauton medium)
  2. Do a linear relationship for only the P/PE proteins vs. rpkm.
  3. Learn how to quantify peptide abundance properly.

1.2 Reading in the existing result

As per Yan’s suggestion, I am using the existing results, found in the file: “2017_0811BrikenMultiConsensus.xlsx”

Unfortunately, this table is in a particularly strange format: it is a triply nested table of tables of tables in one worksheet in Excel. At one level, this is a really novel and interesting use of Excel, but on every other level it is sub-optimal. The primary reason it is sub-optimal is that the column headings end up being repeated once for every new sub-table (874 of the inner tables plus however many middle tables), which makes it nearly unparseable.

I am displeased I need to do this level of data wrangling.

The following function is my first attempt at handling this parsing puzzle.

For the interested, the structure is:

  • all_protein_groups is a list of two elements: group_id => (“summary”, “data”)
  • for every protein group discovered, one of these two element lists is created.
    • The summary slot gets the one-row summary for the protein group from the excel.
  • The data slot is a list of groups: protein => (“summary”, “data”)
    • Once again, the summary is the single row from excel.
  • Again, the data slot is a list of peptides: peptide => data.frame(peptides)
    • Each peptide data frame contains all of the inner-most rows describing the observed peptides.
## I pulled this function into hpgltools and cleaned it up I think.
## I also gave it a less stupid name: read_thermo_xlsx()
parse_difficult_excel <- function(xlsx_file, test_row=92150) {
  old_options <- options(java.parameters="-Xmx20G")
  message(paste0("Reading ", xlsx_file))
  result <- readxl::read_xlsx(path=xlsx_file, sheet=1, col_names=FALSE)
  message("Starting parse.")
  group_data <- list()
  bar <- utils::txtProgressBar(style=3)
  for (r in 1:nrow(result)) {
    row <- as.data.frame(result[r, ])
    row[, is.na(row)] <- ""
    pct_done <- r / nrow(result)
    setTxtProgressBar(bar, pct_done)
    ## The following 3 stanzas handle the creation of the levels of our data structure
    ## The first defines the protein group
    if (row[, 1] == "Checked") {
      group_colnames <- as.character(row)
      group_keepers <- !grepl(pattern="^$", x=group_colnames)
      group_keepers[1] <- FALSE
      group_colnames <- group_colnames[group_keepers]
      next
    }
    ## When the 2nd column is 'Checked', then this defines a new protein in the group.
    if (row[, 2] == "Checked") {
      protein_colnames <- as.character(row)
      protein_keepers <- !grepl(pattern="^$", x=protein_colnames)
      protein_keepers[2] <- FALSE
      protein_colnames <- protein_colnames[protein_keepers]
      next
    }
    ## When the 3rd column is 'Checked', then this starts a peptide definition
    if (row[, 3] == "Checked") {
      peptide_colnames <- as.character(row)
      peptide_keepers <- !grepl(pattern="^$", x=peptide_colnames)
      peptide_keepers[3] <- FALSE
      peptide_colnames <- peptide_colnames[peptide_keepers]
      next
    }
    ## Once the column names for the data are defined, we consider how to
    ## Fill in the actual data, the protein group is probably the least interesting.
    if (row[, 1] == FALSE | row[, 1] == TRUE) {
      group_information <- row[group_keepers]
      colnames(group_information) <- group_colnames
      group_information[["ID"]] <- sub(pattern="^.* GN=(\\w+) .*$",
                                       replacement="\\1",
                                       x=group_information[["Group Description"]])
      group_accession <- group_information[["Protein Group ID"]]
      group_list <- list(
        "summary" = group_information,
        "data" = list())
      group_data[[group_accession]] <- group_list
      next
    }
    ## When the 2nd column is FALSE, then this defined a protein in the group.
    ## The protein data structure is likely the most interesting.
    if (row[, 2] == FALSE | row[, 2] == TRUE) {
      protein_information <- row[protein_keepers]
      colnames(protein_information) <- protein_colnames
      protein_information[["ID"]] <- sub(pattern="^.* GN=(\\w+) .*$",
                                         replacement="\\1",
                                         x=protein_information[["Description"]])
      protein_accession <- protein_information[["Accession"]]
      protein_list <- list(
        "summary" = protein_information,
        "data" = data.frame())
      group_data[[group_accession]][["data"]][[protein_accession]] <- protein_list
      next
    }
    ## When the 3rd group is FALSE, then this adds a peptide.
    ## The peptide data structure is the most detailed, but probably not the most interesting.
    if (row[, 3] == FALSE | row[, 3] == TRUE) {
      peptide_information <- row[peptide_keepers]
      colnames(peptide_information) <- peptide_colnames
      current <- group_data[[group_accession]][["data"]][[protein_accession]][["data"]]
      new <- rbind(current, peptide_information)
      group_data[[group_accession]][["data"]][[protein_accession]][["data"]] <- new
      next
    }
  } ## End iterating over ever row of this unholy stupid data structure.
  close(bar)
  message("Finished parsing, reorganizing the protein data.")
  protein_df <- data.frame()
  peptide_df <- data.frame()
  protein_names <- c()
  message(paste0("Starting to iterate over ", length(group_data),  " groups."))
  bar <- utils::txtProgressBar(style=3)
  for (g in 1:length(group_data)) {
    pct_done <- g / length(group_data)
    setTxtProgressBar(bar, pct_done)
    group <- as.character(names(group_data)[g])
    protein_group <- group_data[[group]][["data"]]
    protein_accessions <- names(protein_group)
    for (p in 1:length(protein_accessions)) {
      protein <- protein_accessions[p]
      protein_names <- c(protein_names, protein)
      protein_summary <- group_data[[group]][["data"]][[protein]][["summary"]]
      protein_df <- rbind(protein_df, protein_summary)
      peptide_data <- group_data[[group]][["data"]][[protein]][["data"]]
      ## peptide_df <- rbind(peptide_df, peptide_data)
    }
  } ## End of the for loop
  close(bar)
  retlist <- list(
    "names" = protein_names,
    "group_data" = group_data,
    "protein_data" = protein_df,
    "peptide_data" = peptide_df)
  return(retlist)
}

##xlsx_file <- "previous_results/2017_0811BrikenMultiConsensus.xlsx"
xlsx_file <- "previous_results/2017_1101BrikenMulti_PE_PPE_Complete.xlsx"
less_difficult <- sm(parse_difficult_excel(xlsx_file))

The function above is not immediately helpful, as it only wrangles the crazy excel into a less-crazy data structure, but if we wish to graph the data or play with it, we need to extract the elements of interest. For the moment those are primarily the peptide fragments and whether they were found in the trypsin digestions, chymotrypsin digestions, or both.

This data frame now contains everything we are likely to want to visualize.

1.3 Cleaning the data frame

Unfortunately, this data structure also suffers from a couple weaknesses:

  1. The protein IDs are not unique (because of the groups)
  2. The column names from Excel are insane (punctuation, special characters, and really long)
  3. Excel left the numeric columns of data as characters, not numbers!

Therefore, I will take a moment to fix these problems before trying any plots.

I am also reasonably certain that I codified this logic in a separate function in hpgltools since writing this.

## Set some reasonable row names
protein_df <- less_difficult[["protein_data"]]
protein_names <- less_difficult[["names"]]
rownames(protein_df) <- make.names(protein_names, unique=TRUE)
## Set the column names to something legible.
## Each line denotes my mental organizational unit
new_colnames <- c("mascot_fdr", "sequest_fdr", "master", "accession", "description",
                  "mascot_q", "sequest_q", "coverage", "contaminant", "marked_as",
                  "num_peptides", "num_psms", "num_unique_pep", "num_prot_groups", "num_aas",
                  "mw", "pi", "mascot_score", "sequest_score", "num_peptides_mascot",
                  "num_peptides_sequest", "bp", "cc", "mf", "pfam_ids",
                  "entrez_gene_id", "ensembl_gene_id", "gene_symbol", "chromosome", "kegg",
                  "wikipathways", "found_trypsin_lysc_cf", "found_trypsin_lysc_wcl", "found_chymotrypsin_cf", "found_chymotrypsin_wcl",
                  "modifications", "id")
colnames(protein_df) <- new_colnames
## Now make sure the columns which should be numeric, are numeric.
numeric_cols <- c("mascot_q", "sequest_q", "coverage", "num_peptides", "num_psms",
                  "num_unique_pep", "num_prot_groups", "num_aas", "mw", "pi",
                  "mascot_score", "sequest_score",
                  "num_peptides_mascot", "num_peptides_sequest")
for (col in numeric_cols) {
  protein_df[[col]] <- as.numeric(protein_df[[col]])
}
factor_cols <- c("found_trypsin_lysc_cf", "found_trypsin_lysc_wcl",
                 "found_chymotrypsin_cf", "found_chymotrypsin_wcl")
for (col in factor_cols) {
  protein_df[[col]] <- as.factor(protein_df[[col]])
}
## Finally, set a 'state' column describing the 4 columns of chymotrypsin/trypsin digestions.
protein_df[["state"]] <- "ambiguous"
## If something is found in one chymo trypsin, but in neither trypsin, set it to chymo_only.
chymo_hits <- (protein_df[["found_chymotrypsin_cf"]] == "High" | protein_df[["found_chymotrypsin_wcl"]] == "High") &
  (protein_df[["found_trypsin_lysc_cf"]] != "High" & protein_df[["found_trypsin_lysc_wcl"]] != "High")
protein_df[chymo_hits, "state"] <- "chymo_only"
## Conversely, if found in trypsin but not chymo, set it to tryp_only.
tryp_hits <- (protein_df[["found_trypsin_lysc_cf"]] == "High" | protein_df[["found_trypsin_lysc_wcl"]] == "High") &
  (protein_df[["found_chymotrypsin_cf"]] != "High" & protein_df[["found_chymotrypsin_wcl"]] != "High")
protein_df[tryp_hits, "state"] <- "tryp_only"
no_hits <- protein_df[["found_trypsin_lysc_cf"]] == "Not Found" &
  protein_df[["found_trypsin_lysc_wcl"]] == "Not Found" &
  protein_df[["found_chymotrypsin_cf"]] == "Not Found" &
  protein_df[["found_chymotrypsin_wcl"]] == "Not Found"
protein_df[no_hits, "state"] <- "None"
protein_df[["state"]] <- as.factor(protein_df[["state"]])

chymo_df <- protein_df[chymo_hits, ]
tryp_df <- protein_df[tryp_hits, ]

1.4 Make a couple plots

Now that the data has been wrangled, we can use the various R plotters to look at the data relatively quickly and easily.

library(ggplot2)
coverage_wrt_pi <- ggplot(protein_df, aes_string(x="pi", y="coverage", colour="state")) +
  geom_point(alpha=0.6, size=2) +
  geom_density_2d()

coverage_wrt_mw <- ggplot(protein_df, aes_string(x="mw", y="coverage", colour="state")) +
  geom_point(alpha=0.6, size=2) +
  geom_density_2d()

protein_df$logmascot <- log2(protein_df$mascot_score)
scores_by_state <- ggplot(protein_df, aes_string(x="state", y="logmascot")) +
  geom_boxplot(aes_string(fill="state"))

1.4.1 Coverage vs. pI

Yan suggested that peptides with low-pIs would be less covered, especially in the chymotrypsin samples.

coverage_wrt_pi
## Warning: Computation failed in `stat_density2d()`:
## bandwidths must be strictly positive

## To me, it looks like there might be something to that idea.

1.4.2 Coverage vs. Molecular Weight

Maybe there is a trend of coverage and molecular weight by sample-type.

coverage_wrt_mw
## Warning: Computation failed in `stat_density2d()`:
## bandwidths must be strictly positive

## It looks to me that the trypsin is slightly better than chymotrypsin.
## It is not clear in the excel, but I presume this is in kDa.

1.4.3 Mascot scores

A quick boxplot showing mascot scores by what was observed.

scores_by_state
## Warning: Removed 96 rows containing non-finite values (stat_boxplot).

2 Bring together previous rnaseq and current peptides

This turned out to be a surprisingly difficult problem because the geneID<->annotation mappings were inconsistent. Therefore I downloaded the uniprot annotations for Mtb at:

http://www.uniprot.org/proteomes/UP000001584

The annotation data used by Keith appears to me to have come from either genbank, microbesonline, or ensembl. All of these match quite easily, but none of them match easily to uniprot.

2.1 Read in the limma data and merge the annotations.

Throughout the following steps I will print the dimensions of the resulting data so that I can see where I am losing information.

all_de <- read.table("limma_result.csv", header=TRUE, sep="\t")
colnames(all_de) <- c("ID", "logFC", "AveExpr", "adj.P.Val", "abbrev_id", "description", "gene_length", "type")
all_de <- merge(all_de, mtb_annotations, by.x="ID", by.y="ID")
dim(all_de)
## [1] 3995   21

2.2 Get separate PE proteins.

We will have occasion to look only at the P/PE proteins later. So lets separately grab that information now.

pe_ids <- read.table("reference/annotated_pe_genes.txt")
colnames(pe_ids) <- "locus_tag"
pe_annotations <- merge(pe_ids, mtb_annotations, by.x="locus_tag", by.y="ID")
## Warning in merge.data.frame(pe_ids, mtb_annotations, by.x = "locus_tag", : column name
## 'locus_tag' is duplicated in the result
rownames(pe_annotations) <- pe_annotations[["ID"]]
colnames(pe_annotations) <- c("locus_tag", "seqnames", "start", "end", "width",
                              "strand", "source", "type", "score", "phase",
                              "locus_tag2", "gene", "description", "function")
dim(pe_annotations)
## [1] 161  14

2.3 Load uniprot data

There is in fact an existing interface for downloading annotation data from uniprot. It is terrible, because it seems to be missing important species and does not provide the mappings for important data. So, I downloaded the reference myself and wrote a parser for it.

if (file.exists("uniprot_annot.csv")) {
  message("Reusing a previous annotation database.")
  uniprot_annot <- read.csv(file="uniprot_annot.csv")
} else {
  uniprot_annot <- load_uniprot_annotations(file="reference/uniprot_3AUP000001584.txt.gz")
  write.csv(file="uniprot_annot.csv", uniprot_annot)
}
## Reusing a previous annotation database.
dim(uniprot_annot)
## [1] 3993   42

2.4 Perform merges

Now we have the relevant data sets, lets bring them together into what is hopefully a coherent whole.

## Start by merging Keith's DE and uniprot annotations.
uniprot_rna <- merge(all_de, uniprot_annot, by.x="ID", by.y="loci")
dim(uniprot_rna)
## [1] 3891   62
## Repeat for the set of 161 PE proteins.
uniprot_pe <- merge(pe_annotations, uniprot_annot, by.x="locus_tag", by.y="loci")
dim(uniprot_pe)
## [1] 157  55
## We appear to have lost 4 PE proteins in this process.
uniprot_pe_peptides <- merge(uniprot_annot, protein_df, by.x="primary_accessions", by.y="accession")
uniprot_pe_peptides <- merge(uniprot_pe_peptides, pe_ids, by.x="loci", by.y="locus_tag")
dim(uniprot_pe_peptides)
## [1] 40 80
uniprot_rna_protein <- merge(uniprot_rna, protein_df, by.x="primary_accessions", by.y="accession")
dim(protein_df)
## [1] 2671   39
dim(uniprot_rna_protein)
## [1] 2511  100
uniprot_rna_pe <- merge(uniprot_pe, uniprot_rna_protein, by="primary_id")
## Warning in merge.data.frame(uniprot_pe, uniprot_rna_protein, by = "primary_id"): column
## names 'description.x', 'description.y' are duplicated in the result
dim(uniprot_rna_pe)
## [1]  40 154

2.5 Use these combined data

Now that I finally got the various data sets to merge, I should be able to visualize how well they correspond to one another (not very).

But first, lets make some shorthand names for them so that this is a bit less obnoxious.

## This is exhausting, I will call these big and little from now on
big <- uniprot_rna_protein
rownames(big) <- make.names(big$ID, unique=TRUE)
ordered <- order(rownames(big))
big <- big[ordered, ]
little <- uniprot_rna_pe
rownames(little) <- make.names(little$ID, unique=TRUE)
ordered <- order(rownames(little))
little <- little[ordered, ]
shared_idx <- rownames(big) %in% rownames(little)
shared <- big[shared_idx, ]

2.6 Make pseudo_rpkm

Our gene-size-normalized expression values have not yet been generated. Do that here.

## pe_de_annot <- merge(pe_de, mtb_annotations, by="locus_tag")
## The contrast is written as wt - delta, so add back the logFC I think
## Hopefully I did not reverse this in my head.
big$ave_minus_log <- big$AveExpr - big$logFC
big$pseudo_rpkm <- (big$ave_minus_log / big$width) * 1000
maximum_rpkm <- max(big$pseudo_rpkm)
big$express_vs_max <- big$pseudo_rpkm / maximum_rpkm
## Since we constant ordered the dataframes above, this is valid.
little$pseudo_rpkm <- big$pseudo_rpkm[shared_idx]
little$express_vs_max <- big$express_vs_max[shared_idx]

write.csv(x=little, file="pe_relative_expression_annotation-20171205.csv")

2.7 Finally plot

Now all the pieces are finally in place, lets look at relationships between our available metrics of protein abundance and rna abundance.

2.8 Histograms of all expression vs. P/PE

library(hpgltools)
library(ggplot2)
test <- list(
  "all_peptides" = big$express_vs_max,
  "pe_peptides" = little$express_vs_max)
multi <- plot_multihistogram(test)
## Used Bon Ferroni corrected t test(s) between columns.
multi$plot

2.9 RPKM vs PSMS

psms_coverage_peptides <- big[, c("num_psms", "coverage", "num_peptides")]

rpkm_vs_psms <- big[, c("pseudo_rpkm", "num_psms")]
little_rpkm_vs_psms <- little[, c("pseudo_rpkm", "num_psms")]
rpkm_vs_psms$num_psms <- log2(rpkm_vs_psms$num_psms + 1)
little_rpkm_vs_psms$num_psms <- log2(little_rpkm_vs_psms$num_psms + 1)
rpkm_psms_linear <- plot_linear_scatter(rpkm_vs_psms)
## Used Bon Ferroni corrected t test(s) between columns.
rpkm_psms_linear$scatter

rpkm_psms_linear$cor
## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 5.8, df = 2500, p-value = 9e-09
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.07575 0.15296
## sample estimates:
##    cor 
## 0.1145
rpkm_psms_linear$lm_rsq
## [1] 0.007207
big_little <- ggplot(data=rpkm_vs_psms, aes_string(x="pseudo_rpkm", y="num_psms")) +
  geom_point()
big_little

big_little <- big_little +
  geom_point(data=little_rpkm_vs_psms, aes_string(x="pseudo_rpkm", y="num_psms"), colour="red")
favorites <- little_rpkm_vs_psms[["num_psms"]] > 3
labeled <- little_rpkm_vs_psms[favorites, ]
big_little +
  ggrepel::geom_text_repel(data=labeled,
                           arrow=arrow(length=unit(0.01, "npc")),
                           nudge_x=-4.0, nudge_y=-0.2,
                           ggplot2::aes(x=pseudo_rpkm, y=num_psms, label=rownames(labeled)))

labeled
##         pseudo_rpkm num_psms
## Rv0285       28.621    5.426
## Rv0442c       3.750    3.585
## Rv1195       34.594    4.807
## Rv1196        9.911    5.931
## Rv1386       20.836    6.895
## Rv1387        6.264    3.907
## Rv1768        3.798    3.700
## Rv2162c       5.764    4.170
## Rv2352c       5.946    3.807
## Rv2430c      15.085    3.907
## Rv2431c      27.463    4.954
## Rv3020c      14.002    4.907
## Rv3477       34.108    7.011
## Rv3478        9.491    4.807

2.10 rpkm vs peptides

rpkm_vs_peptides <- big[, c("pseudo_rpkm", "num_peptides")]
rpkm_peptides_linear <- plot_linear_scatter(rpkm_vs_peptides)
## Used Bon Ferroni corrected t test(s) between columns.
rpkm_peptides_linear$scatter

rpkm_peptides_linear$cor
## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = -4.8, df = 2500, p-value = 2e-06
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  -0.13318 -0.05564
## sample estimates:
##      cor 
## -0.09455
rpkm_psms_linear$lm_rsq
## [1] 0.007207
little_rpkm_vs_peptides <- little[, c("pseudo_rpkm", "num_peptides")]
big_little <- ggplot(data=rpkm_vs_peptides, aes_string(x="pseudo_rpkm", y="num_peptides")) +
  geom_point()
big_little

big_little <- big_little +
  geom_point(data=little_rpkm_vs_peptides, aes_string(x="pseudo_rpkm", y="num_peptides"), colour="red")
big_little

rpkm_vs_coverage <- big[, c("pseudo_rpkm", "coverage")]
rpkm_coverage_linear <- plot_linear_scatter(rpkm_vs_coverage)
## Used Bon Ferroni corrected t test(s) between columns.
rpkm_coverage_linear$scatter

rpkm_coverage_linear$cor
## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 21, df = 2500, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.3532 0.4198
## sample estimates:
##   cor 
## 0.387
rpkm_coverage_linear$lm_rsq
## [1] 0.1618
little_rpkm_vs_coverage <- little[, c("pseudo_rpkm", "coverage")]
big_little <- ggplot(data=rpkm_vs_coverage, aes_string(x="pseudo_rpkm", y="coverage")) +
  geom_point()
big_little

big_little <- big_little +
  geom_point(data=little_rpkm_vs_coverage, aes_string(x="pseudo_rpkm", y="coverage"), colour="red")
big_little

3 PView vs rpkm

I recently spent some time playing with pview and believe it might provide a reasonable proxy for peptide abundance for our data. Lets see if that is true.

I first will need to copy the pview results from scratch/proteomics to here. I copied a test run of pview to preprocessing/pview_20171207.csv

pview_result <- read.csv("preprocessing/pview_20171207.csv", header=TRUE, sep="\t")
small_df <- pview_result[, c("cond.01.median", "cond.02.median")]
rownames(small_df) <- gsub(pattern="\\[mtb_cds\\] ", replacement="", x=pview_result[["protein.group"]])
na_positions <- is.na(small_df)
small_df[na_positions] <- 0
small_log_df <- log2(small_df + 1)

test_df <- small_df
nonzero_rows <- test_df[["cond.02.median"]] != 0
test_df <- test_df[nonzero_rows, ]
test_min <- min(test_df[["cond.02.median"]])
test_df[["cond.02.median"]] <- test_df[["cond.02.median"]] - (test_min - 1)
test_df[["l2"]] <- log2(test_df[["cond.02.median"]])

big_pview <- merge(big, test_df, by="row.names")
rownames(big_pview) <- big_pview[["Row.names"]]
rpkm_vs_pview <- big_pview[, c("AveExpr", "l2")]

rpkm_pview_linear <- plot_linear_scatter(rpkm_vs_pview, cormethod="spearman")
rpkm_pview_linear$scatter
rpkm_pview_linear$cor
rpkm_pview_linear$lm_rsq

little_rpkm_vs_coverage <- little[, c("pseudo_rpkm", "coverage")]
big_little <- ggplot(data=rpkm_vs_coverage, aes_string(x="pseudo_rpkm", y="coverage")) +
  geom_point()
big_little
big_little <- big_little +
  geom_point(data=little_rpkm_vs_coverage, aes_string(x="pseudo_rpkm", y="coverage"), colour="red")
big_little
if (!isTRUE(get0("skip_load"))) {
  pander::pander(sessionInfo())
  message(paste0("This is hpgltools commit: ", get_git_commit()))
  this_save <- paste0(gsub(pattern="\\.Rmd", replace="", x=rmd_file), "-v", ver, ".rda.xz")
  message(paste0("Saving to ", this_save))
  tmp <- sm(saveme(filename=this_save))
}
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset 7de4503f6bb5724c28cce24af5dbee22bb1c0cae
## R> packrat::restore()
## This is hpgltools commit: Thu Apr 12 22:08:53 2018 -0400: 7de4503f6bb5724c28cce24af5dbee22bb1c0cae
## Saving to 02_eval_result_201711-v20171205.rda.xz
LS0tCnRpdGxlOiAiTS50dWJlcmN1bG9zaXMgMjAxNzogRXZhbHVhdGluZyBzb21lIE1TL01TIHBlcHRpZGUgc2VhcmNoIG1ldHJpY3MuIgphdXRob3I6ICJhdGIgYWJlbGV3QGdtYWlsLmNvbSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiBodG1sX2RvY3VtZW50OgogIGNvZGVfZG93bmxvYWQ6IHRydWUKICBjb2RlX2ZvbGRpbmc6IHNob3cKICBmaWdfY2FwdGlvbjogdHJ1ZQogIGZpZ19oZWlnaHQ6IDcKICBmaWdfd2lkdGg6IDcKICBoaWdobGlnaHQ6IGRlZmF1bHQKICBrZWVwX21kOiBmYWxzZQogIG1vZGU6IHNlbGZjb250YWluZWQKICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogIHRoZW1lOiByZWFkYWJsZQogIHRvYzogdHJ1ZQogIHRvY19mbG9hdDoKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKPHN0eWxlPgogIGJvZHkgLm1haW4tY29udGFpbmVyIHsKICAgIG1heC13aWR0aDogMTYwMHB4OwogIH0KPC9zdHlsZT4KCmBgYHtyIG9wdGlvbnMsIGluY2x1ZGU9RkFMU0V9CmlmICghaXNUUlVFKGdldDAoInNraXBfbG9hZCIpKSkgewogIGxpYnJhcnkoaHBnbHRvb2xzKQogIHR0IDwtIGRldnRvb2xzOjpsb2FkX2FsbCgifi9ocGdsdG9vbHMiKQogIGtuaXRyOjpvcHRzX2tuaXQkc2V0KHByb2dyZXNzPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgIHdpZHRoPTkwLAogICAgICAgICAgICAgICAgICAgICAgIGVjaG89VFJVRSkKICBrbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3I9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoPTgsCiAgICAgICAgICAgICAgICAgICAgICAgIGZpZy5oZWlnaHQ9OCwKICAgICAgICAgICAgICAgICAgICAgICAgZHBpPTk2KQogIG9sZF9vcHRpb25zIDwtIG9wdGlvbnMoZGlnaXRzPTQsCiAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAga25pdHIuZHVwbGljYXRlLmxhYmVsPSJhbGxvdyIpCiAgZ2dwbG90Mjo6dGhlbWVfc2V0KGdncGxvdDI6OnRoZW1lX2J3KGJhc2Vfc2l6ZT0xMCkpCiAgc2V0LnNlZWQoMSkKICB2ZXIgPC0gIjIwMTcxMjA1IgogIHByZXZpb3VzX2ZpbGUgPC0gIjAxX2Fubm90YXRpb24uUm1kIgoKICB0bXAgPC0gdHJ5KHNtKGxvYWRtZShmaWxlbmFtZT1wYXN0ZTAoZ3N1YihwYXR0ZXJuPSJcXC5SbWQiLCByZXBsYWNlPSIiLCB4PXByZXZpb3VzX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikpKSkKICBybWRfZmlsZSA8LSAiMDJfZXZhbF9yZXN1bHRfMjAxNzExLlJtZCIKfQpgYGAKCiMgRXZhbHVhdGUgUHJldmlvdXMgUmVzdWx0OiB2ZXJzaW9uOiBgciB2ZXJgCgojIyBUT0RPCgoxLiAgUmVwbGFjZSBybmFzZXEgZGF0YSB1c2VkIHdpdGggYSBtb3JlIGFwcHJvcHJpYXRlIHNldC4gKFNhdXRvbiBtZWRpdW0pCjIuICBEbyBhIGxpbmVhciByZWxhdGlvbnNoaXAgZm9yIG9ubHkgdGhlIFAvUEUgcHJvdGVpbnMgdnMuIHJwa20uCjMuICBMZWFybiBob3cgdG8gcXVhbnRpZnkgcGVwdGlkZSBhYnVuZGFuY2UgcHJvcGVybHkuCgojIyBSZWFkaW5nIGluIHRoZSBleGlzdGluZyByZXN1bHQKCkFzIHBlciBZYW4ncyBzdWdnZXN0aW9uLCBJIGFtIHVzaW5nIHRoZSBleGlzdGluZyByZXN1bHRzLCBmb3VuZCBpbiB0aGUgZmlsZToKIjIwMTdfMDgxMUJyaWtlbk11bHRpQ29uc2Vuc3VzLnhsc3giCgpVbmZvcnR1bmF0ZWx5LCB0aGlzIHRhYmxlIGlzIGluIGEgcGFydGljdWxhcmx5IHN0cmFuZ2UgZm9ybWF0OiBpdCBpcyBhIHRyaXBseQpuZXN0ZWQgdGFibGUgb2YgdGFibGVzIG9mIHRhYmxlcyBpbiBvbmUgd29ya3NoZWV0IGluIEV4Y2VsLiAgQXQgb25lIGxldmVsLCB0aGlzCmlzIGEgcmVhbGx5IG5vdmVsIGFuZCBpbnRlcmVzdGluZyB1c2Ugb2YgRXhjZWwsIGJ1dCBvbiBldmVyeSBvdGhlciBsZXZlbCBpdCBpcwpzdWItb3B0aW1hbC4gIFRoZSBwcmltYXJ5IHJlYXNvbiBpdCBpcyBzdWItb3B0aW1hbCBpcyB0aGF0IHRoZSBjb2x1bW4gaGVhZGluZ3MKZW5kIHVwIGJlaW5nIHJlcGVhdGVkIG9uY2UgZm9yIGV2ZXJ5IG5ldyBzdWItdGFibGUgKDg3NCBvZiB0aGUgaW5uZXIgdGFibGVzIHBsdXMKaG93ZXZlciBtYW55IG1pZGRsZSB0YWJsZXMpLCB3aGljaCBtYWtlcyBpdCBuZWFybHkgdW5wYXJzZWFibGUuCgpJIGFtIGRpc3BsZWFzZWQgSSBuZWVkIHRvIGRvIHRoaXMgbGV2ZWwgb2YgZGF0YSB3cmFuZ2xpbmcuCgpUaGUgZm9sbG93aW5nIGZ1bmN0aW9uIGlzIG15IGZpcnN0IGF0dGVtcHQgYXQgaGFuZGxpbmcgdGhpcyBwYXJzaW5nIHB1enpsZS4KCkZvciB0aGUgaW50ZXJlc3RlZCwgdGhlIHN0cnVjdHVyZSBpczoKCiogIGFsbF9wcm90ZWluX2dyb3VwcyBpcyBhIGxpc3Qgb2YgdHdvIGVsZW1lbnRzOiBncm91cF9pZCA9PiAoInN1bW1hcnkiLCAiZGF0YSIpCiogIGZvciBldmVyeSBwcm90ZWluIGdyb3VwIGRpc2NvdmVyZWQsIG9uZSBvZiB0aGVzZSB0d28gZWxlbWVudCBsaXN0cyBpcyBjcmVhdGVkLgogICAgKiAgVGhlIHN1bW1hcnkgc2xvdCBnZXRzIHRoZSBvbmUtcm93IHN1bW1hcnkgZm9yIHRoZSBwcm90ZWluIGdyb3VwIGZyb20gdGhlIGV4Y2VsLgoqICBUaGUgZGF0YSBzbG90IGlzIGEgbGlzdCBvZiBncm91cHM6IHByb3RlaW4gPT4gKCJzdW1tYXJ5IiwgImRhdGEiKQogICAgKiAgT25jZSBhZ2FpbiwgdGhlIHN1bW1hcnkgaXMgdGhlIHNpbmdsZSByb3cgZnJvbSBleGNlbC4KKiAgQWdhaW4sIHRoZSBkYXRhIHNsb3QgaXMgYSBsaXN0IG9mIHBlcHRpZGVzOiBwZXB0aWRlID0+IGRhdGEuZnJhbWUocGVwdGlkZXMpCiAgICAqICBFYWNoIHBlcHRpZGUgZGF0YSBmcmFtZSBjb250YWlucyBhbGwgb2YgdGhlIGlubmVyLW1vc3Qgcm93cyBkZXNjcmliaW5nIHRoZQogICAgICAgb2JzZXJ2ZWQgcGVwdGlkZXMuCgpgYGB7ciBleHRyYWN0X3BlcHRpZGVzfQojIyBJIHB1bGxlZCB0aGlzIGZ1bmN0aW9uIGludG8gaHBnbHRvb2xzIGFuZCBjbGVhbmVkIGl0IHVwIEkgdGhpbmsuCiMjIEkgYWxzbyBnYXZlIGl0IGEgbGVzcyBzdHVwaWQgbmFtZTogcmVhZF90aGVybW9feGxzeCgpCnBhcnNlX2RpZmZpY3VsdF9leGNlbCA8LSBmdW5jdGlvbih4bHN4X2ZpbGUsIHRlc3Rfcm93PTkyMTUwKSB7CiAgb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhqYXZhLnBhcmFtZXRlcnM9Ii1YbXgyMEciKQogIG1lc3NhZ2UocGFzdGUwKCJSZWFkaW5nICIsIHhsc3hfZmlsZSkpCiAgcmVzdWx0IDwtIHJlYWR4bDo6cmVhZF94bHN4KHBhdGg9eGxzeF9maWxlLCBzaGVldD0xLCBjb2xfbmFtZXM9RkFMU0UpCiAgbWVzc2FnZSgiU3RhcnRpbmcgcGFyc2UuIikKICBncm91cF9kYXRhIDwtIGxpc3QoKQogIGJhciA8LSB1dGlsczo6dHh0UHJvZ3Jlc3NCYXIoc3R5bGU9MykKICBmb3IgKHIgaW4gMTpucm93KHJlc3VsdCkpIHsKICAgIHJvdyA8LSBhcy5kYXRhLmZyYW1lKHJlc3VsdFtyLCBdKQogICAgcm93WywgaXMubmEocm93KV0gPC0gIiIKICAgIHBjdF9kb25lIDwtIHIgLyBucm93KHJlc3VsdCkKICAgIHNldFR4dFByb2dyZXNzQmFyKGJhciwgcGN0X2RvbmUpCiAgICAjIyBUaGUgZm9sbG93aW5nIDMgc3RhbnphcyBoYW5kbGUgdGhlIGNyZWF0aW9uIG9mIHRoZSBsZXZlbHMgb2Ygb3VyIGRhdGEgc3RydWN0dXJlCiAgICAjIyBUaGUgZmlyc3QgZGVmaW5lcyB0aGUgcHJvdGVpbiBncm91cAogICAgaWYgKHJvd1ssIDFdID09ICJDaGVja2VkIikgewogICAgICBncm91cF9jb2xuYW1lcyA8LSBhcy5jaGFyYWN0ZXIocm93KQogICAgICBncm91cF9rZWVwZXJzIDwtICFncmVwbChwYXR0ZXJuPSJeJCIsIHg9Z3JvdXBfY29sbmFtZXMpCiAgICAgIGdyb3VwX2tlZXBlcnNbMV0gPC0gRkFMU0UKICAgICAgZ3JvdXBfY29sbmFtZXMgPC0gZ3JvdXBfY29sbmFtZXNbZ3JvdXBfa2VlcGVyc10KICAgICAgbmV4dAogICAgfQogICAgIyMgV2hlbiB0aGUgMm5kIGNvbHVtbiBpcyAnQ2hlY2tlZCcsIHRoZW4gdGhpcyBkZWZpbmVzIGEgbmV3IHByb3RlaW4gaW4gdGhlIGdyb3VwLgogICAgaWYgKHJvd1ssIDJdID09ICJDaGVja2VkIikgewogICAgICBwcm90ZWluX2NvbG5hbWVzIDwtIGFzLmNoYXJhY3Rlcihyb3cpCiAgICAgIHByb3RlaW5fa2VlcGVycyA8LSAhZ3JlcGwocGF0dGVybj0iXiQiLCB4PXByb3RlaW5fY29sbmFtZXMpCiAgICAgIHByb3RlaW5fa2VlcGVyc1syXSA8LSBGQUxTRQogICAgICBwcm90ZWluX2NvbG5hbWVzIDwtIHByb3RlaW5fY29sbmFtZXNbcHJvdGVpbl9rZWVwZXJzXQogICAgICBuZXh0CiAgICB9CiAgICAjIyBXaGVuIHRoZSAzcmQgY29sdW1uIGlzICdDaGVja2VkJywgdGhlbiB0aGlzIHN0YXJ0cyBhIHBlcHRpZGUgZGVmaW5pdGlvbgogICAgaWYgKHJvd1ssIDNdID09ICJDaGVja2VkIikgewogICAgICBwZXB0aWRlX2NvbG5hbWVzIDwtIGFzLmNoYXJhY3Rlcihyb3cpCiAgICAgIHBlcHRpZGVfa2VlcGVycyA8LSAhZ3JlcGwocGF0dGVybj0iXiQiLCB4PXBlcHRpZGVfY29sbmFtZXMpCiAgICAgIHBlcHRpZGVfa2VlcGVyc1szXSA8LSBGQUxTRQogICAgICBwZXB0aWRlX2NvbG5hbWVzIDwtIHBlcHRpZGVfY29sbmFtZXNbcGVwdGlkZV9rZWVwZXJzXQogICAgICBuZXh0CiAgICB9CiAgICAjIyBPbmNlIHRoZSBjb2x1bW4gbmFtZXMgZm9yIHRoZSBkYXRhIGFyZSBkZWZpbmVkLCB3ZSBjb25zaWRlciBob3cgdG8KICAgICMjIEZpbGwgaW4gdGhlIGFjdHVhbCBkYXRhLCB0aGUgcHJvdGVpbiBncm91cCBpcyBwcm9iYWJseSB0aGUgbGVhc3QgaW50ZXJlc3RpbmcuCiAgICBpZiAocm93WywgMV0gPT0gRkFMU0UgfCByb3dbLCAxXSA9PSBUUlVFKSB7CiAgICAgIGdyb3VwX2luZm9ybWF0aW9uIDwtIHJvd1tncm91cF9rZWVwZXJzXQogICAgICBjb2xuYW1lcyhncm91cF9pbmZvcm1hdGlvbikgPC0gZ3JvdXBfY29sbmFtZXMKICAgICAgZ3JvdXBfaW5mb3JtYXRpb25bWyJJRCJdXSA8LSBzdWIocGF0dGVybj0iXi4qIEdOPShcXHcrKSAuKiQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudD0iXFwxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeD1ncm91cF9pbmZvcm1hdGlvbltbIkdyb3VwIERlc2NyaXB0aW9uIl1dKQogICAgICBncm91cF9hY2Nlc3Npb24gPC0gZ3JvdXBfaW5mb3JtYXRpb25bWyJQcm90ZWluIEdyb3VwIElEIl1dCiAgICAgIGdyb3VwX2xpc3QgPC0gbGlzdCgKICAgICAgICAic3VtbWFyeSIgPSBncm91cF9pbmZvcm1hdGlvbiwKICAgICAgICAiZGF0YSIgPSBsaXN0KCkpCiAgICAgIGdyb3VwX2RhdGFbW2dyb3VwX2FjY2Vzc2lvbl1dIDwtIGdyb3VwX2xpc3QKICAgICAgbmV4dAogICAgfQogICAgIyMgV2hlbiB0aGUgMm5kIGNvbHVtbiBpcyBGQUxTRSwgdGhlbiB0aGlzIGRlZmluZWQgYSBwcm90ZWluIGluIHRoZSBncm91cC4KICAgICMjIFRoZSBwcm90ZWluIGRhdGEgc3RydWN0dXJlIGlzIGxpa2VseSB0aGUgbW9zdCBpbnRlcmVzdGluZy4KICAgIGlmIChyb3dbLCAyXSA9PSBGQUxTRSB8IHJvd1ssIDJdID09IFRSVUUpIHsKICAgICAgcHJvdGVpbl9pbmZvcm1hdGlvbiA8LSByb3dbcHJvdGVpbl9rZWVwZXJzXQogICAgICBjb2xuYW1lcyhwcm90ZWluX2luZm9ybWF0aW9uKSA8LSBwcm90ZWluX2NvbG5hbWVzCiAgICAgIHByb3RlaW5faW5mb3JtYXRpb25bWyJJRCJdXSA8LSBzdWIocGF0dGVybj0iXi4qIEdOPShcXHcrKSAuKiQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50PSJcXDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHg9cHJvdGVpbl9pbmZvcm1hdGlvbltbIkRlc2NyaXB0aW9uIl1dKQogICAgICBwcm90ZWluX2FjY2Vzc2lvbiA8LSBwcm90ZWluX2luZm9ybWF0aW9uW1siQWNjZXNzaW9uIl1dCiAgICAgIHByb3RlaW5fbGlzdCA8LSBsaXN0KAogICAgICAgICJzdW1tYXJ5IiA9IHByb3RlaW5faW5mb3JtYXRpb24sCiAgICAgICAgImRhdGEiID0gZGF0YS5mcmFtZSgpKQogICAgICBncm91cF9kYXRhW1tncm91cF9hY2Nlc3Npb25dXVtbImRhdGEiXV1bW3Byb3RlaW5fYWNjZXNzaW9uXV0gPC0gcHJvdGVpbl9saXN0CiAgICAgIG5leHQKICAgIH0KICAgICMjIFdoZW4gdGhlIDNyZCBncm91cCBpcyBGQUxTRSwgdGhlbiB0aGlzIGFkZHMgYSBwZXB0aWRlLgogICAgIyMgVGhlIHBlcHRpZGUgZGF0YSBzdHJ1Y3R1cmUgaXMgdGhlIG1vc3QgZGV0YWlsZWQsIGJ1dCBwcm9iYWJseSBub3QgdGhlIG1vc3QgaW50ZXJlc3RpbmcuCiAgICBpZiAocm93WywgM10gPT0gRkFMU0UgfCByb3dbLCAzXSA9PSBUUlVFKSB7CiAgICAgIHBlcHRpZGVfaW5mb3JtYXRpb24gPC0gcm93W3BlcHRpZGVfa2VlcGVyc10KICAgICAgY29sbmFtZXMocGVwdGlkZV9pbmZvcm1hdGlvbikgPC0gcGVwdGlkZV9jb2xuYW1lcwogICAgICBjdXJyZW50IDwtIGdyb3VwX2RhdGFbW2dyb3VwX2FjY2Vzc2lvbl1dW1siZGF0YSJdXVtbcHJvdGVpbl9hY2Nlc3Npb25dXVtbImRhdGEiXV0KICAgICAgbmV3IDwtIHJiaW5kKGN1cnJlbnQsIHBlcHRpZGVfaW5mb3JtYXRpb24pCiAgICAgIGdyb3VwX2RhdGFbW2dyb3VwX2FjY2Vzc2lvbl1dW1siZGF0YSJdXVtbcHJvdGVpbl9hY2Nlc3Npb25dXVtbImRhdGEiXV0gPC0gbmV3CiAgICAgIG5leHQKICAgIH0KICB9ICMjIEVuZCBpdGVyYXRpbmcgb3ZlciBldmVyIHJvdyBvZiB0aGlzIHVuaG9seSBzdHVwaWQgZGF0YSBzdHJ1Y3R1cmUuCiAgY2xvc2UoYmFyKQogIG1lc3NhZ2UoIkZpbmlzaGVkIHBhcnNpbmcsIHJlb3JnYW5pemluZyB0aGUgcHJvdGVpbiBkYXRhLiIpCiAgcHJvdGVpbl9kZiA8LSBkYXRhLmZyYW1lKCkKICBwZXB0aWRlX2RmIDwtIGRhdGEuZnJhbWUoKQogIHByb3RlaW5fbmFtZXMgPC0gYygpCiAgbWVzc2FnZShwYXN0ZTAoIlN0YXJ0aW5nIHRvIGl0ZXJhdGUgb3ZlciAiLCBsZW5ndGgoZ3JvdXBfZGF0YSksICAiIGdyb3Vwcy4iKSkKICBiYXIgPC0gdXRpbHM6OnR4dFByb2dyZXNzQmFyKHN0eWxlPTMpCiAgZm9yIChnIGluIDE6bGVuZ3RoKGdyb3VwX2RhdGEpKSB7CiAgICBwY3RfZG9uZSA8LSBnIC8gbGVuZ3RoKGdyb3VwX2RhdGEpCiAgICBzZXRUeHRQcm9ncmVzc0JhcihiYXIsIHBjdF9kb25lKQogICAgZ3JvdXAgPC0gYXMuY2hhcmFjdGVyKG5hbWVzKGdyb3VwX2RhdGEpW2ddKQogICAgcHJvdGVpbl9ncm91cCA8LSBncm91cF9kYXRhW1tncm91cF1dW1siZGF0YSJdXQogICAgcHJvdGVpbl9hY2Nlc3Npb25zIDwtIG5hbWVzKHByb3RlaW5fZ3JvdXApCiAgICBmb3IgKHAgaW4gMTpsZW5ndGgocHJvdGVpbl9hY2Nlc3Npb25zKSkgewogICAgICBwcm90ZWluIDwtIHByb3RlaW5fYWNjZXNzaW9uc1twXQogICAgICBwcm90ZWluX25hbWVzIDwtIGMocHJvdGVpbl9uYW1lcywgcHJvdGVpbikKICAgICAgcHJvdGVpbl9zdW1tYXJ5IDwtIGdyb3VwX2RhdGFbW2dyb3VwXV1bWyJkYXRhIl1dW1twcm90ZWluXV1bWyJzdW1tYXJ5Il1dCiAgICAgIHByb3RlaW5fZGYgPC0gcmJpbmQocHJvdGVpbl9kZiwgcHJvdGVpbl9zdW1tYXJ5KQogICAgICBwZXB0aWRlX2RhdGEgPC0gZ3JvdXBfZGF0YVtbZ3JvdXBdXVtbImRhdGEiXV1bW3Byb3RlaW5dXVtbImRhdGEiXV0KICAgICAgIyMgcGVwdGlkZV9kZiA8LSByYmluZChwZXB0aWRlX2RmLCBwZXB0aWRlX2RhdGEpCiAgICB9CiAgfSAjIyBFbmQgb2YgdGhlIGZvciBsb29wCiAgY2xvc2UoYmFyKQogIHJldGxpc3QgPC0gbGlzdCgKICAgICJuYW1lcyIgPSBwcm90ZWluX25hbWVzLAogICAgImdyb3VwX2RhdGEiID0gZ3JvdXBfZGF0YSwKICAgICJwcm90ZWluX2RhdGEiID0gcHJvdGVpbl9kZiwKICAgICJwZXB0aWRlX2RhdGEiID0gcGVwdGlkZV9kZikKICByZXR1cm4ocmV0bGlzdCkKfQoKIyN4bHN4X2ZpbGUgPC0gInByZXZpb3VzX3Jlc3VsdHMvMjAxN18wODExQnJpa2VuTXVsdGlDb25zZW5zdXMueGxzeCIKeGxzeF9maWxlIDwtICJwcmV2aW91c19yZXN1bHRzLzIwMTdfMTEwMUJyaWtlbk11bHRpX1BFX1BQRV9Db21wbGV0ZS54bHN4IgpsZXNzX2RpZmZpY3VsdCA8LSBzbShwYXJzZV9kaWZmaWN1bHRfZXhjZWwoeGxzeF9maWxlKSkKYGBgCgpUaGUgZnVuY3Rpb24gYWJvdmUgaXMgbm90IGltbWVkaWF0ZWx5IGhlbHBmdWwsIGFzIGl0IG9ubHkgd3JhbmdsZXMgdGhlIGNyYXp5CmV4Y2VsIGludG8gYSBsZXNzLWNyYXp5IGRhdGEgc3RydWN0dXJlLCBidXQgaWYgd2Ugd2lzaCB0byBncmFwaCB0aGUgZGF0YSBvciBwbGF5CndpdGggaXQsIHdlIG5lZWQgdG8gZXh0cmFjdCB0aGUgZWxlbWVudHMgb2YgaW50ZXJlc3QuICBGb3IgdGhlIG1vbWVudCB0aG9zZSBhcmUKcHJpbWFyaWx5IHRoZSBwZXB0aWRlIGZyYWdtZW50cyBhbmQgd2hldGhlciB0aGV5IHdlcmUgZm91bmQgaW4gdGhlIHRyeXBzaW4KZGlnZXN0aW9ucywgY2h5bW90cnlwc2luIGRpZ2VzdGlvbnMsIG9yIGJvdGguCgpUaGlzIGRhdGEgZnJhbWUgbm93IGNvbnRhaW5zIGV2ZXJ5dGhpbmcgd2UgYXJlIGxpa2VseSB0byB3YW50IHRvIHZpc3VhbGl6ZS4KCiMjIENsZWFuaW5nIHRoZSBkYXRhIGZyYW1lCgpVbmZvcnR1bmF0ZWx5LCB0aGlzIGRhdGEgc3RydWN0dXJlIGFsc28gc3VmZmVycyBmcm9tIGEgY291cGxlIHdlYWtuZXNzZXM6CgoxLiAgVGhlIHByb3RlaW4gSURzIGFyZSBub3QgdW5pcXVlIChiZWNhdXNlIG9mIHRoZSBncm91cHMpCjIuICBUaGUgY29sdW1uIG5hbWVzIGZyb20gRXhjZWwgYXJlIGluc2FuZSAocHVuY3R1YXRpb24sIHNwZWNpYWwgY2hhcmFjdGVycywgYW5kIHJlYWxseSBsb25nKQozLiAgRXhjZWwgbGVmdCB0aGUgbnVtZXJpYyBjb2x1bW5zIG9mIGRhdGEgYXMgY2hhcmFjdGVycywgbm90IG51bWJlcnMhCgpUaGVyZWZvcmUsIEkgd2lsbCB0YWtlIGEgbW9tZW50IHRvIGZpeCB0aGVzZSBwcm9ibGVtcyBiZWZvcmUgdHJ5aW5nIGFueSBwbG90cy4KCkkgYW0gYWxzbyByZWFzb25hYmx5IGNlcnRhaW4gdGhhdCBJIGNvZGlmaWVkIHRoaXMgbG9naWMgaW4gYSBzZXBhcmF0ZSBmdW5jdGlvbgppbiBocGdsdG9vbHMgc2luY2Ugd3JpdGluZyB0aGlzLgoKYGBge3IgZGZfY2xlYW5pbmd9CiMjIFNldCBzb21lIHJlYXNvbmFibGUgcm93IG5hbWVzCnByb3RlaW5fZGYgPC0gbGVzc19kaWZmaWN1bHRbWyJwcm90ZWluX2RhdGEiXV0KcHJvdGVpbl9uYW1lcyA8LSBsZXNzX2RpZmZpY3VsdFtbIm5hbWVzIl1dCnJvd25hbWVzKHByb3RlaW5fZGYpIDwtIG1ha2UubmFtZXMocHJvdGVpbl9uYW1lcywgdW5pcXVlPVRSVUUpCiMjIFNldCB0aGUgY29sdW1uIG5hbWVzIHRvIHNvbWV0aGluZyBsZWdpYmxlLgojIyBFYWNoIGxpbmUgZGVub3RlcyBteSBtZW50YWwgb3JnYW5pemF0aW9uYWwgdW5pdApuZXdfY29sbmFtZXMgPC0gYygibWFzY290X2ZkciIsICJzZXF1ZXN0X2ZkciIsICJtYXN0ZXIiLCAiYWNjZXNzaW9uIiwgImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgIm1hc2NvdF9xIiwgInNlcXVlc3RfcSIsICJjb3ZlcmFnZSIsICJjb250YW1pbmFudCIsICJtYXJrZWRfYXMiLAogICAgICAgICAgICAgICAgICAibnVtX3BlcHRpZGVzIiwgIm51bV9wc21zIiwgIm51bV91bmlxdWVfcGVwIiwgIm51bV9wcm90X2dyb3VwcyIsICJudW1fYWFzIiwKICAgICAgICAgICAgICAgICAgIm13IiwgInBpIiwgIm1hc2NvdF9zY29yZSIsICJzZXF1ZXN0X3Njb3JlIiwgIm51bV9wZXB0aWRlc19tYXNjb3QiLAogICAgICAgICAgICAgICAgICAibnVtX3BlcHRpZGVzX3NlcXVlc3QiLCAiYnAiLCAiY2MiLCAibWYiLCAicGZhbV9pZHMiLAogICAgICAgICAgICAgICAgICAiZW50cmV6X2dlbmVfaWQiLCAiZW5zZW1ibF9nZW5lX2lkIiwgImdlbmVfc3ltYm9sIiwgImNocm9tb3NvbWUiLCAia2VnZyIsCiAgICAgICAgICAgICAgICAgICJ3aWtpcGF0aHdheXMiLCAiZm91bmRfdHJ5cHNpbl9seXNjX2NmIiwgImZvdW5kX3RyeXBzaW5fbHlzY193Y2wiLCAiZm91bmRfY2h5bW90cnlwc2luX2NmIiwgImZvdW5kX2NoeW1vdHJ5cHNpbl93Y2wiLAogICAgICAgICAgICAgICAgICAibW9kaWZpY2F0aW9ucyIsICJpZCIpCmNvbG5hbWVzKHByb3RlaW5fZGYpIDwtIG5ld19jb2xuYW1lcwojIyBOb3cgbWFrZSBzdXJlIHRoZSBjb2x1bW5zIHdoaWNoIHNob3VsZCBiZSBudW1lcmljLCBhcmUgbnVtZXJpYy4KbnVtZXJpY19jb2xzIDwtIGMoIm1hc2NvdF9xIiwgInNlcXVlc3RfcSIsICJjb3ZlcmFnZSIsICJudW1fcGVwdGlkZXMiLCAibnVtX3BzbXMiLAogICAgICAgICAgICAgICAgICAibnVtX3VuaXF1ZV9wZXAiLCAibnVtX3Byb3RfZ3JvdXBzIiwgIm51bV9hYXMiLCAibXciLCAicGkiLAogICAgICAgICAgICAgICAgICAibWFzY290X3Njb3JlIiwgInNlcXVlc3Rfc2NvcmUiLAogICAgICAgICAgICAgICAgICAibnVtX3BlcHRpZGVzX21hc2NvdCIsICJudW1fcGVwdGlkZXNfc2VxdWVzdCIpCmZvciAoY29sIGluIG51bWVyaWNfY29scykgewogIHByb3RlaW5fZGZbW2NvbF1dIDwtIGFzLm51bWVyaWMocHJvdGVpbl9kZltbY29sXV0pCn0KZmFjdG9yX2NvbHMgPC0gYygiZm91bmRfdHJ5cHNpbl9seXNjX2NmIiwgImZvdW5kX3RyeXBzaW5fbHlzY193Y2wiLAogICAgICAgICAgICAgICAgICJmb3VuZF9jaHltb3RyeXBzaW5fY2YiLCAiZm91bmRfY2h5bW90cnlwc2luX3djbCIpCmZvciAoY29sIGluIGZhY3Rvcl9jb2xzKSB7CiAgcHJvdGVpbl9kZltbY29sXV0gPC0gYXMuZmFjdG9yKHByb3RlaW5fZGZbW2NvbF1dKQp9CiMjIEZpbmFsbHksIHNldCBhICdzdGF0ZScgY29sdW1uIGRlc2NyaWJpbmcgdGhlIDQgY29sdW1ucyBvZiBjaHltb3RyeXBzaW4vdHJ5cHNpbiBkaWdlc3Rpb25zLgpwcm90ZWluX2RmW1sic3RhdGUiXV0gPC0gImFtYmlndW91cyIKIyMgSWYgc29tZXRoaW5nIGlzIGZvdW5kIGluIG9uZSBjaHltbyB0cnlwc2luLCBidXQgaW4gbmVpdGhlciB0cnlwc2luLCBzZXQgaXQgdG8gY2h5bW9fb25seS4KY2h5bW9faGl0cyA8LSAocHJvdGVpbl9kZltbImZvdW5kX2NoeW1vdHJ5cHNpbl9jZiJdXSA9PSAiSGlnaCIgfCBwcm90ZWluX2RmW1siZm91bmRfY2h5bW90cnlwc2luX3djbCJdXSA9PSAiSGlnaCIpICYKICAocHJvdGVpbl9kZltbImZvdW5kX3RyeXBzaW5fbHlzY19jZiJdXSAhPSAiSGlnaCIgJiBwcm90ZWluX2RmW1siZm91bmRfdHJ5cHNpbl9seXNjX3djbCJdXSAhPSAiSGlnaCIpCnByb3RlaW5fZGZbY2h5bW9faGl0cywgInN0YXRlIl0gPC0gImNoeW1vX29ubHkiCiMjIENvbnZlcnNlbHksIGlmIGZvdW5kIGluIHRyeXBzaW4gYnV0IG5vdCBjaHltbywgc2V0IGl0IHRvIHRyeXBfb25seS4KdHJ5cF9oaXRzIDwtIChwcm90ZWluX2RmW1siZm91bmRfdHJ5cHNpbl9seXNjX2NmIl1dID09ICJIaWdoIiB8IHByb3RlaW5fZGZbWyJmb3VuZF90cnlwc2luX2x5c2Nfd2NsIl1dID09ICJIaWdoIikgJgogIChwcm90ZWluX2RmW1siZm91bmRfY2h5bW90cnlwc2luX2NmIl1dICE9ICJIaWdoIiAmIHByb3RlaW5fZGZbWyJmb3VuZF9jaHltb3RyeXBzaW5fd2NsIl1dICE9ICJIaWdoIikKcHJvdGVpbl9kZlt0cnlwX2hpdHMsICJzdGF0ZSJdIDwtICJ0cnlwX29ubHkiCm5vX2hpdHMgPC0gcHJvdGVpbl9kZltbImZvdW5kX3RyeXBzaW5fbHlzY19jZiJdXSA9PSAiTm90IEZvdW5kIiAmCiAgcHJvdGVpbl9kZltbImZvdW5kX3RyeXBzaW5fbHlzY193Y2wiXV0gPT0gIk5vdCBGb3VuZCIgJgogIHByb3RlaW5fZGZbWyJmb3VuZF9jaHltb3RyeXBzaW5fY2YiXV0gPT0gIk5vdCBGb3VuZCIgJgogIHByb3RlaW5fZGZbWyJmb3VuZF9jaHltb3RyeXBzaW5fd2NsIl1dID09ICJOb3QgRm91bmQiCnByb3RlaW5fZGZbbm9faGl0cywgInN0YXRlIl0gPC0gIk5vbmUiCnByb3RlaW5fZGZbWyJzdGF0ZSJdXSA8LSBhcy5mYWN0b3IocHJvdGVpbl9kZltbInN0YXRlIl1dKQoKY2h5bW9fZGYgPC0gcHJvdGVpbl9kZltjaHltb19oaXRzLCBdCnRyeXBfZGYgPC0gcHJvdGVpbl9kZlt0cnlwX2hpdHMsIF0KYGBgCgojIyBNYWtlIGEgY291cGxlIHBsb3RzCgpOb3cgdGhhdCB0aGUgZGF0YSBoYXMgYmVlbiB3cmFuZ2xlZCwgd2UgY2FuIHVzZSB0aGUgdmFyaW91cyBSIHBsb3R0ZXJzIHRvIGxvb2sKYXQgdGhlIGRhdGEgcmVsYXRpdmVseSBxdWlja2x5IGFuZCBlYXNpbHkuCgpgYGB7ciBwbG90c30KbGlicmFyeShnZ3Bsb3QyKQpjb3ZlcmFnZV93cnRfcGkgPC0gZ2dwbG90KHByb3RlaW5fZGYsIGFlc19zdHJpbmcoeD0icGkiLCB5PSJjb3ZlcmFnZSIsIGNvbG91cj0ic3RhdGUiKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC42LCBzaXplPTIpICsKICBnZW9tX2RlbnNpdHlfMmQoKQoKY292ZXJhZ2Vfd3J0X213IDwtIGdncGxvdChwcm90ZWluX2RmLCBhZXNfc3RyaW5nKHg9Im13IiwgeT0iY292ZXJhZ2UiLCBjb2xvdXI9InN0YXRlIikpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNiwgc2l6ZT0yKSArCiAgZ2VvbV9kZW5zaXR5XzJkKCkKCnByb3RlaW5fZGYkbG9nbWFzY290IDwtIGxvZzIocHJvdGVpbl9kZiRtYXNjb3Rfc2NvcmUpCnNjb3Jlc19ieV9zdGF0ZSA8LSBnZ3Bsb3QocHJvdGVpbl9kZiwgYWVzX3N0cmluZyh4PSJzdGF0ZSIsIHk9ImxvZ21hc2NvdCIpKSArCiAgZ2VvbV9ib3hwbG90KGFlc19zdHJpbmcoZmlsbD0ic3RhdGUiKSkKYGBgCgojIyMgQ292ZXJhZ2UgdnMuIHBJCgpZYW4gc3VnZ2VzdGVkIHRoYXQgcGVwdGlkZXMgd2l0aCBsb3ctcElzIHdvdWxkIGJlIGxlc3MgY292ZXJlZCwgZXNwZWNpYWxseSBpbgp0aGUgY2h5bW90cnlwc2luIHNhbXBsZXMuCgpgYGB7ciBjb3ZfcGl9CmNvdmVyYWdlX3dydF9waQojIyBUbyBtZSwgaXQgbG9va3MgbGlrZSB0aGVyZSBtaWdodCBiZSBzb21ldGhpbmcgdG8gdGhhdCBpZGVhLgpgYGAKCiMjIyBDb3ZlcmFnZSB2cy4gTW9sZWN1bGFyIFdlaWdodAoKTWF5YmUgdGhlcmUgaXMgYSB0cmVuZCBvZiBjb3ZlcmFnZSBhbmQgbW9sZWN1bGFyIHdlaWdodCBieSBzYW1wbGUtdHlwZS4KCmBgYHtyIGNvdl9td30KY292ZXJhZ2Vfd3J0X213CiMjIEl0IGxvb2tzIHRvIG1lIHRoYXQgdGhlIHRyeXBzaW4gaXMgc2xpZ2h0bHkgYmV0dGVyIHRoYW4gY2h5bW90cnlwc2luLgojIyBJdCBpcyBub3QgY2xlYXIgaW4gdGhlIGV4Y2VsLCBidXQgSSBwcmVzdW1lIHRoaXMgaXMgaW4ga0RhLgpgYGAKCiMjIyBNYXNjb3Qgc2NvcmVzCgpBIHF1aWNrIGJveHBsb3Qgc2hvd2luZyBtYXNjb3Qgc2NvcmVzIGJ5IHdoYXQgd2FzIG9ic2VydmVkLgoKYGBge3IgbWFzY290fQpzY29yZXNfYnlfc3RhdGUKYGBgCgojIEJyaW5nIHRvZ2V0aGVyIHByZXZpb3VzIHJuYXNlcSBhbmQgY3VycmVudCBwZXB0aWRlcwoKVGhpcyB0dXJuZWQgb3V0IHRvIGJlIGEgc3VycHJpc2luZ2x5IGRpZmZpY3VsdCBwcm9ibGVtIGJlY2F1c2UgdGhlCmdlbmVJRDwtPmFubm90YXRpb24gbWFwcGluZ3Mgd2VyZSBpbmNvbnNpc3RlbnQuIFRoZXJlZm9yZSBJIGRvd25sb2FkZWQgdGhlCnVuaXByb3QgYW5ub3RhdGlvbnMgZm9yIE10YiBhdDoKCmh0dHA6Ly93d3cudW5pcHJvdC5vcmcvcHJvdGVvbWVzL1VQMDAwMDAxNTg0CgpUaGUgYW5ub3RhdGlvbiBkYXRhIHVzZWQgYnkgS2VpdGggYXBwZWFycyB0byBtZSB0byBoYXZlIGNvbWUgZnJvbSBlaXRoZXIKZ2VuYmFuaywgbWljcm9iZXNvbmxpbmUsIG9yIGVuc2VtYmwuICBBbGwgb2YgdGhlc2UgbWF0Y2ggcXVpdGUgZWFzaWx5LCBidXQgbm9uZQpvZiB0aGVtIG1hdGNoIGVhc2lseSB0byB1bmlwcm90LgoKIyMgUmVhZCBpbiB0aGUgbGltbWEgZGF0YSBhbmQgbWVyZ2UgdGhlIGFubm90YXRpb25zLgoKVGhyb3VnaG91dCB0aGUgZm9sbG93aW5nIHN0ZXBzIEkgd2lsbCBwcmludCB0aGUgZGltZW5zaW9ucyBvZiB0aGUgcmVzdWx0aW5nIGRhdGEKc28gdGhhdCBJIGNhbiBzZWUgd2hlcmUgSSBhbSBsb3NpbmcgaW5mb3JtYXRpb24uCgpgYGB7ciB4cmVmX2Fubm90YXRpb25zfQphbGxfZGUgPC0gcmVhZC50YWJsZSgibGltbWFfcmVzdWx0LmNzdiIsIGhlYWRlcj1UUlVFLCBzZXA9Ilx0IikKY29sbmFtZXMoYWxsX2RlKSA8LSBjKCJJRCIsICJsb2dGQyIsICJBdmVFeHByIiwgImFkai5QLlZhbCIsICJhYmJyZXZfaWQiLCAiZGVzY3JpcHRpb24iLCAiZ2VuZV9sZW5ndGgiLCAidHlwZSIpCmFsbF9kZSA8LSBtZXJnZShhbGxfZGUsIG10Yl9hbm5vdGF0aW9ucywgYnkueD0iSUQiLCBieS55PSJJRCIpCmRpbShhbGxfZGUpCmBgYAoKIyMgR2V0IHNlcGFyYXRlIFBFIHByb3RlaW5zLgoKV2Ugd2lsbCBoYXZlIG9jY2FzaW9uIHRvIGxvb2sgb25seSBhdCB0aGUgUC9QRSBwcm90ZWlucyBsYXRlci4gIFNvIGxldHMKc2VwYXJhdGVseSBncmFiIHRoYXQgaW5mb3JtYXRpb24gbm93LgoKYGBge3IgbXlfcGVfZ2VuZXN9CnBlX2lkcyA8LSByZWFkLnRhYmxlKCJyZWZlcmVuY2UvYW5ub3RhdGVkX3BlX2dlbmVzLnR4dCIpCmNvbG5hbWVzKHBlX2lkcykgPC0gImxvY3VzX3RhZyIKcGVfYW5ub3RhdGlvbnMgPC0gbWVyZ2UocGVfaWRzLCBtdGJfYW5ub3RhdGlvbnMsIGJ5Lng9ImxvY3VzX3RhZyIsIGJ5Lnk9IklEIikKcm93bmFtZXMocGVfYW5ub3RhdGlvbnMpIDwtIHBlX2Fubm90YXRpb25zW1siSUQiXV0KY29sbmFtZXMocGVfYW5ub3RhdGlvbnMpIDwtIGMoImxvY3VzX3RhZyIsICJzZXFuYW1lcyIsICJzdGFydCIsICJlbmQiLCAid2lkdGgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyYW5kIiwgInNvdXJjZSIsICJ0eXBlIiwgInNjb3JlIiwgInBoYXNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY3VzX3RhZzIiLCAiZ2VuZSIsICJkZXNjcmlwdGlvbiIsICJmdW5jdGlvbiIpCmRpbShwZV9hbm5vdGF0aW9ucykKYGBgCgojIyBMb2FkIHVuaXByb3QgZGF0YQoKVGhlcmUgaXMgaW4gZmFjdCBhbiBleGlzdGluZyBpbnRlcmZhY2UgZm9yIGRvd25sb2FkaW5nIGFubm90YXRpb24gZGF0YSBmcm9tCnVuaXByb3QuICBJdCBpcyB0ZXJyaWJsZSwgYmVjYXVzZSBpdCBzZWVtcyB0byBiZSBtaXNzaW5nIGltcG9ydGFudCBzcGVjaWVzIGFuZApkb2VzIG5vdCBwcm92aWRlIHRoZSBtYXBwaW5ncyBmb3IgaW1wb3J0YW50IGRhdGEuICBTbywgSSBkb3dubG9hZGVkIHRoZQpyZWZlcmVuY2UgbXlzZWxmIGFuZCB3cm90ZSBhIHBhcnNlciBmb3IgaXQuCgpgYGB7ciBsb2FkX3VuaXByb3R9CmlmIChmaWxlLmV4aXN0cygidW5pcHJvdF9hbm5vdC5jc3YiKSkgewogIG1lc3NhZ2UoIlJldXNpbmcgYSBwcmV2aW91cyBhbm5vdGF0aW9uIGRhdGFiYXNlLiIpCiAgdW5pcHJvdF9hbm5vdCA8LSByZWFkLmNzdihmaWxlPSJ1bmlwcm90X2Fubm90LmNzdiIpCn0gZWxzZSB7CiAgdW5pcHJvdF9hbm5vdCA8LSBsb2FkX3VuaXByb3RfYW5ub3RhdGlvbnMoZmlsZT0icmVmZXJlbmNlL3VuaXByb3RfM0FVUDAwMDAwMTU4NC50eHQuZ3oiKQogIHdyaXRlLmNzdihmaWxlPSJ1bmlwcm90X2Fubm90LmNzdiIsIHVuaXByb3RfYW5ub3QpCn0KZGltKHVuaXByb3RfYW5ub3QpCmBgYAoKIyMgUGVyZm9ybSBtZXJnZXMKCk5vdyB3ZSBoYXZlIHRoZSByZWxldmFudCBkYXRhIHNldHMsIGxldHMgYnJpbmcgdGhlbSB0b2dldGhlciBpbnRvIHdoYXQgaXMKaG9wZWZ1bGx5IGEgY29oZXJlbnQgd2hvbGUuCgpgYGB7ciBtZXJnZV91bmlwcm90fQojIyBTdGFydCBieSBtZXJnaW5nIEtlaXRoJ3MgREUgYW5kIHVuaXByb3QgYW5ub3RhdGlvbnMuCnVuaXByb3Rfcm5hIDwtIG1lcmdlKGFsbF9kZSwgdW5pcHJvdF9hbm5vdCwgYnkueD0iSUQiLCBieS55PSJsb2NpIikKZGltKHVuaXByb3Rfcm5hKQoKIyMgUmVwZWF0IGZvciB0aGUgc2V0IG9mIDE2MSBQRSBwcm90ZWlucy4KdW5pcHJvdF9wZSA8LSBtZXJnZShwZV9hbm5vdGF0aW9ucywgdW5pcHJvdF9hbm5vdCwgYnkueD0ibG9jdXNfdGFnIiwgYnkueT0ibG9jaSIpCmRpbSh1bmlwcm90X3BlKQojIyBXZSBhcHBlYXIgdG8gaGF2ZSBsb3N0IDQgUEUgcHJvdGVpbnMgaW4gdGhpcyBwcm9jZXNzLgp1bmlwcm90X3BlX3BlcHRpZGVzIDwtIG1lcmdlKHVuaXByb3RfYW5ub3QsIHByb3RlaW5fZGYsIGJ5Lng9InByaW1hcnlfYWNjZXNzaW9ucyIsIGJ5Lnk9ImFjY2Vzc2lvbiIpCnVuaXByb3RfcGVfcGVwdGlkZXMgPC0gbWVyZ2UodW5pcHJvdF9wZV9wZXB0aWRlcywgcGVfaWRzLCBieS54PSJsb2NpIiwgYnkueT0ibG9jdXNfdGFnIikKZGltKHVuaXByb3RfcGVfcGVwdGlkZXMpCgp1bmlwcm90X3JuYV9wcm90ZWluIDwtIG1lcmdlKHVuaXByb3Rfcm5hLCBwcm90ZWluX2RmLCBieS54PSJwcmltYXJ5X2FjY2Vzc2lvbnMiLCBieS55PSJhY2Nlc3Npb24iKQpkaW0ocHJvdGVpbl9kZikKZGltKHVuaXByb3Rfcm5hX3Byb3RlaW4pCgp1bmlwcm90X3JuYV9wZSA8LSBtZXJnZSh1bmlwcm90X3BlLCB1bmlwcm90X3JuYV9wcm90ZWluLCBieT0icHJpbWFyeV9pZCIpCmRpbSh1bmlwcm90X3JuYV9wZSkKYGBgCgojIyBVc2UgdGhlc2UgY29tYmluZWQgZGF0YQoKTm93IHRoYXQgSSBmaW5hbGx5IGdvdCB0aGUgdmFyaW91cyBkYXRhIHNldHMgdG8gbWVyZ2UsIEkgc2hvdWxkIGJlIGFibGUgdG8KdmlzdWFsaXplIGhvdyB3ZWxsIHRoZXkgY29ycmVzcG9uZCB0byBvbmUgYW5vdGhlciAobm90IHZlcnkpLgoKQnV0IGZpcnN0LCBsZXRzIG1ha2Ugc29tZSBzaG9ydGhhbmQgbmFtZXMgZm9yIHRoZW0gc28gdGhhdCB0aGlzIGlzIGEgYml0IGxlc3MKb2Jub3hpb3VzLgoKYGBge3IgdmlzdWFsaXplX3N1YnNldHN9CiMjIFRoaXMgaXMgZXhoYXVzdGluZywgSSB3aWxsIGNhbGwgdGhlc2UgYmlnIGFuZCBsaXR0bGUgZnJvbSBub3cgb24KYmlnIDwtIHVuaXByb3Rfcm5hX3Byb3RlaW4Kcm93bmFtZXMoYmlnKSA8LSBtYWtlLm5hbWVzKGJpZyRJRCwgdW5pcXVlPVRSVUUpCm9yZGVyZWQgPC0gb3JkZXIocm93bmFtZXMoYmlnKSkKYmlnIDwtIGJpZ1tvcmRlcmVkLCBdCmxpdHRsZSA8LSB1bmlwcm90X3JuYV9wZQpyb3duYW1lcyhsaXR0bGUpIDwtIG1ha2UubmFtZXMobGl0dGxlJElELCB1bmlxdWU9VFJVRSkKb3JkZXJlZCA8LSBvcmRlcihyb3duYW1lcyhsaXR0bGUpKQpsaXR0bGUgPC0gbGl0dGxlW29yZGVyZWQsIF0Kc2hhcmVkX2lkeCA8LSByb3duYW1lcyhiaWcpICVpbiUgcm93bmFtZXMobGl0dGxlKQpzaGFyZWQgPC0gYmlnW3NoYXJlZF9pZHgsIF0KYGBgCgojIyBNYWtlIHBzZXVkb19ycGttCgpPdXIgZ2VuZS1zaXplLW5vcm1hbGl6ZWQgZXhwcmVzc2lvbiB2YWx1ZXMgaGF2ZSBub3QgeWV0IGJlZW4gZ2VuZXJhdGVkLiAgRG8gdGhhdCBoZXJlLgoKYGBge3IgZXh0cmFjdF9ybmF9CiMjIHBlX2RlX2Fubm90IDwtIG1lcmdlKHBlX2RlLCBtdGJfYW5ub3RhdGlvbnMsIGJ5PSJsb2N1c190YWciKQojIyBUaGUgY29udHJhc3QgaXMgd3JpdHRlbiBhcyB3dCAtIGRlbHRhLCBzbyBhZGQgYmFjayB0aGUgbG9nRkMgSSB0aGluawojIyBIb3BlZnVsbHkgSSBkaWQgbm90IHJldmVyc2UgdGhpcyBpbiBteSBoZWFkLgpiaWckYXZlX21pbnVzX2xvZyA8LSBiaWckQXZlRXhwciAtIGJpZyRsb2dGQwpiaWckcHNldWRvX3Jwa20gPC0gKGJpZyRhdmVfbWludXNfbG9nIC8gYmlnJHdpZHRoKSAqIDEwMDAKbWF4aW11bV9ycGttIDwtIG1heChiaWckcHNldWRvX3Jwa20pCmJpZyRleHByZXNzX3ZzX21heCA8LSBiaWckcHNldWRvX3Jwa20gLyBtYXhpbXVtX3Jwa20KIyMgU2luY2Ugd2UgY29uc3RhbnQgb3JkZXJlZCB0aGUgZGF0YWZyYW1lcyBhYm92ZSwgdGhpcyBpcyB2YWxpZC4KbGl0dGxlJHBzZXVkb19ycGttIDwtIGJpZyRwc2V1ZG9fcnBrbVtzaGFyZWRfaWR4XQpsaXR0bGUkZXhwcmVzc192c19tYXggPC0gYmlnJGV4cHJlc3NfdnNfbWF4W3NoYXJlZF9pZHhdCgp3cml0ZS5jc3YoeD1saXR0bGUsIGZpbGU9InBlX3JlbGF0aXZlX2V4cHJlc3Npb25fYW5ub3RhdGlvbi0yMDE3MTIwNS5jc3YiKQpgYGAKCiMjIEZpbmFsbHkgcGxvdAoKTm93IGFsbCB0aGUgcGllY2VzIGFyZSBmaW5hbGx5IGluIHBsYWNlLCBsZXRzIGxvb2sgYXQgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIG91cgphdmFpbGFibGUgbWV0cmljcyBvZiBwcm90ZWluIGFidW5kYW5jZSBhbmQgcm5hIGFidW5kYW5jZS4KCiMjIEhpc3RvZ3JhbXMgb2YgYWxsIGV4cHJlc3Npb24gdnMuIFAvUEUKCmBgYHtyIHBsb3RfYWJ1bmRhbmNlfQpsaWJyYXJ5KGhwZ2x0b29scykKbGlicmFyeShnZ3Bsb3QyKQp0ZXN0IDwtIGxpc3QoCiAgImFsbF9wZXB0aWRlcyIgPSBiaWckZXhwcmVzc192c19tYXgsCiAgInBlX3BlcHRpZGVzIiA9IGxpdHRsZSRleHByZXNzX3ZzX21heCkKbXVsdGkgPC0gcGxvdF9tdWx0aWhpc3RvZ3JhbSh0ZXN0KQptdWx0aSRwbG90CmBgYAoKIyMgUlBLTSB2cyBQU01TCgpgYGB7ciBycGttX3ZzX3BzbXN9CnBzbXNfY292ZXJhZ2VfcGVwdGlkZXMgPC0gYmlnWywgYygibnVtX3BzbXMiLCAiY292ZXJhZ2UiLCAibnVtX3BlcHRpZGVzIildCgpycGttX3ZzX3BzbXMgPC0gYmlnWywgYygicHNldWRvX3Jwa20iLCAibnVtX3BzbXMiKV0KbGl0dGxlX3Jwa21fdnNfcHNtcyA8LSBsaXR0bGVbLCBjKCJwc2V1ZG9fcnBrbSIsICJudW1fcHNtcyIpXQpycGttX3ZzX3BzbXMkbnVtX3BzbXMgPC0gbG9nMihycGttX3ZzX3BzbXMkbnVtX3BzbXMgKyAxKQpsaXR0bGVfcnBrbV92c19wc21zJG51bV9wc21zIDwtIGxvZzIobGl0dGxlX3Jwa21fdnNfcHNtcyRudW1fcHNtcyArIDEpCnJwa21fcHNtc19saW5lYXIgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihycGttX3ZzX3BzbXMpCnJwa21fcHNtc19saW5lYXIkc2NhdHRlcgpycGttX3BzbXNfbGluZWFyJGNvcgpycGttX3BzbXNfbGluZWFyJGxtX3JzcQpiaWdfbGl0dGxlIDwtIGdncGxvdChkYXRhPXJwa21fdnNfcHNtcywgYWVzX3N0cmluZyh4PSJwc2V1ZG9fcnBrbSIsIHk9Im51bV9wc21zIikpICsKICBnZW9tX3BvaW50KCkKYmlnX2xpdHRsZQpiaWdfbGl0dGxlIDwtIGJpZ19saXR0bGUgKwogIGdlb21fcG9pbnQoZGF0YT1saXR0bGVfcnBrbV92c19wc21zLCBhZXNfc3RyaW5nKHg9InBzZXVkb19ycGttIiwgeT0ibnVtX3BzbXMiKSwgY29sb3VyPSJyZWQiKQpmYXZvcml0ZXMgPC0gbGl0dGxlX3Jwa21fdnNfcHNtc1tbIm51bV9wc21zIl1dID4gMwpsYWJlbGVkIDwtIGxpdHRsZV9ycGttX3ZzX3BzbXNbZmF2b3JpdGVzLCBdCmJpZ19saXR0bGUgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhPWxhYmVsZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFycm93PWFycm93KGxlbmd0aD11bml0KDAuMDEsICJucGMiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG51ZGdlX3g9LTQuMCwgbnVkZ2VfeT0tMC4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeD1wc2V1ZG9fcnBrbSwgeT1udW1fcHNtcywgbGFiZWw9cm93bmFtZXMobGFiZWxlZCkpKQpsYWJlbGVkCmBgYAoKIyMgcnBrbSB2cyBwZXB0aWRlcwoKYGBge3IgcnBrbV92X3BlcHRpZGVzfQpycGttX3ZzX3BlcHRpZGVzIDwtIGJpZ1ssIGMoInBzZXVkb19ycGttIiwgIm51bV9wZXB0aWRlcyIpXQpycGttX3BlcHRpZGVzX2xpbmVhciA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKHJwa21fdnNfcGVwdGlkZXMpCnJwa21fcGVwdGlkZXNfbGluZWFyJHNjYXR0ZXIKcnBrbV9wZXB0aWRlc19saW5lYXIkY29yCnJwa21fcHNtc19saW5lYXIkbG1fcnNxCmxpdHRsZV9ycGttX3ZzX3BlcHRpZGVzIDwtIGxpdHRsZVssIGMoInBzZXVkb19ycGttIiwgIm51bV9wZXB0aWRlcyIpXQpiaWdfbGl0dGxlIDwtIGdncGxvdChkYXRhPXJwa21fdnNfcGVwdGlkZXMsIGFlc19zdHJpbmcoeD0icHNldWRvX3Jwa20iLCB5PSJudW1fcGVwdGlkZXMiKSkgKwogIGdlb21fcG9pbnQoKQpiaWdfbGl0dGxlCmJpZ19saXR0bGUgPC0gYmlnX2xpdHRsZSArCiAgZ2VvbV9wb2ludChkYXRhPWxpdHRsZV9ycGttX3ZzX3BlcHRpZGVzLCBhZXNfc3RyaW5nKHg9InBzZXVkb19ycGttIiwgeT0ibnVtX3BlcHRpZGVzIiksIGNvbG91cj0icmVkIikKYmlnX2xpdHRsZQoKcnBrbV92c19jb3ZlcmFnZSA8LSBiaWdbLCBjKCJwc2V1ZG9fcnBrbSIsICJjb3ZlcmFnZSIpXQpycGttX2NvdmVyYWdlX2xpbmVhciA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKHJwa21fdnNfY292ZXJhZ2UpCnJwa21fY292ZXJhZ2VfbGluZWFyJHNjYXR0ZXIKcnBrbV9jb3ZlcmFnZV9saW5lYXIkY29yCnJwa21fY292ZXJhZ2VfbGluZWFyJGxtX3JzcQpsaXR0bGVfcnBrbV92c19jb3ZlcmFnZSA8LSBsaXR0bGVbLCBjKCJwc2V1ZG9fcnBrbSIsICJjb3ZlcmFnZSIpXQpiaWdfbGl0dGxlIDwtIGdncGxvdChkYXRhPXJwa21fdnNfY292ZXJhZ2UsIGFlc19zdHJpbmcoeD0icHNldWRvX3Jwa20iLCB5PSJjb3ZlcmFnZSIpKSArCiAgZ2VvbV9wb2ludCgpCmJpZ19saXR0bGUKYmlnX2xpdHRsZSA8LSBiaWdfbGl0dGxlICsKICBnZW9tX3BvaW50KGRhdGE9bGl0dGxlX3Jwa21fdnNfY292ZXJhZ2UsIGFlc19zdHJpbmcoeD0icHNldWRvX3Jwa20iLCB5PSJjb3ZlcmFnZSIpLCBjb2xvdXI9InJlZCIpCmJpZ19saXR0bGUKYGBgCgojIFBWaWV3IHZzIHJwa20KCkkgcmVjZW50bHkgc3BlbnQgc29tZSB0aW1lIHBsYXlpbmcgd2l0aCBwdmlldyBhbmQgYmVsaWV2ZSBpdCBtaWdodCBwcm92aWRlIGEKcmVhc29uYWJsZSBwcm94eSBmb3IgcGVwdGlkZSBhYnVuZGFuY2UgZm9yIG91ciBkYXRhLiAgTGV0cyBzZWUgaWYgdGhhdCBpcyB0cnVlLgoKSSBmaXJzdCB3aWxsIG5lZWQgdG8gY29weSB0aGUgcHZpZXcgcmVzdWx0cyBmcm9tIHNjcmF0Y2gvcHJvdGVvbWljcyB0byBoZXJlLgpJIGNvcGllZCBhIHRlc3QgcnVuIG9mIHB2aWV3IHRvIHByZXByb2Nlc3NpbmcvcHZpZXdfMjAxNzEyMDcuY3N2CgpgYGB7ciBwdmlld19ycGttLCBldmFsPUZBTFNFfQpwdmlld19yZXN1bHQgPC0gcmVhZC5jc3YoInByZXByb2Nlc3NpbmcvcHZpZXdfMjAxNzEyMDcuY3N2IiwgaGVhZGVyPVRSVUUsIHNlcD0iXHQiKQpzbWFsbF9kZiA8LSBwdmlld19yZXN1bHRbLCBjKCJjb25kLjAxLm1lZGlhbiIsICJjb25kLjAyLm1lZGlhbiIpXQpyb3duYW1lcyhzbWFsbF9kZikgPC0gZ3N1YihwYXR0ZXJuPSJcXFttdGJfY2RzXFxdICIsIHJlcGxhY2VtZW50PSIiLCB4PXB2aWV3X3Jlc3VsdFtbInByb3RlaW4uZ3JvdXAiXV0pCm5hX3Bvc2l0aW9ucyA8LSBpcy5uYShzbWFsbF9kZikKc21hbGxfZGZbbmFfcG9zaXRpb25zXSA8LSAwCnNtYWxsX2xvZ19kZiA8LSBsb2cyKHNtYWxsX2RmICsgMSkKCnRlc3RfZGYgPC0gc21hbGxfZGYKbm9uemVyb19yb3dzIDwtIHRlc3RfZGZbWyJjb25kLjAyLm1lZGlhbiJdXSAhPSAwCnRlc3RfZGYgPC0gdGVzdF9kZltub256ZXJvX3Jvd3MsIF0KdGVzdF9taW4gPC0gbWluKHRlc3RfZGZbWyJjb25kLjAyLm1lZGlhbiJdXSkKdGVzdF9kZltbImNvbmQuMDIubWVkaWFuIl1dIDwtIHRlc3RfZGZbWyJjb25kLjAyLm1lZGlhbiJdXSAtICh0ZXN0X21pbiAtIDEpCnRlc3RfZGZbWyJsMiJdXSA8LSBsb2cyKHRlc3RfZGZbWyJjb25kLjAyLm1lZGlhbiJdXSkKCmJpZ19wdmlldyA8LSBtZXJnZShiaWcsIHRlc3RfZGYsIGJ5PSJyb3cubmFtZXMiKQpyb3duYW1lcyhiaWdfcHZpZXcpIDwtIGJpZ19wdmlld1tbIlJvdy5uYW1lcyJdXQpycGttX3ZzX3B2aWV3IDwtIGJpZ19wdmlld1ssIGMoIkF2ZUV4cHIiLCAibDIiKV0KCnJwa21fcHZpZXdfbGluZWFyIDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIocnBrbV92c19wdmlldywgY29ybWV0aG9kPSJzcGVhcm1hbiIpCnJwa21fcHZpZXdfbGluZWFyJHNjYXR0ZXIKcnBrbV9wdmlld19saW5lYXIkY29yCnJwa21fcHZpZXdfbGluZWFyJGxtX3JzcQoKbGl0dGxlX3Jwa21fdnNfY292ZXJhZ2UgPC0gbGl0dGxlWywgYygicHNldWRvX3Jwa20iLCAiY292ZXJhZ2UiKV0KYmlnX2xpdHRsZSA8LSBnZ3Bsb3QoZGF0YT1ycGttX3ZzX2NvdmVyYWdlLCBhZXNfc3RyaW5nKHg9InBzZXVkb19ycGttIiwgeT0iY292ZXJhZ2UiKSkgKwogIGdlb21fcG9pbnQoKQpiaWdfbGl0dGxlCmJpZ19saXR0bGUgPC0gYmlnX2xpdHRsZSArCiAgZ2VvbV9wb2ludChkYXRhPWxpdHRsZV9ycGttX3ZzX2NvdmVyYWdlLCBhZXNfc3RyaW5nKHg9InBzZXVkb19ycGttIiwgeT0iY292ZXJhZ2UiKSwgY29sb3VyPSJyZWQiKQpiaWdfbGl0dGxlCmBgYAoKYGBge3Igc2F2ZW1lfQppZiAoIWlzVFJVRShnZXQwKCJza2lwX2xvYWQiKSkpIHsKICBwYW5kZXI6OnBhbmRlcihzZXNzaW9uSW5mbygpKQogIG1lc3NhZ2UocGFzdGUwKCJUaGlzIGlzIGhwZ2x0b29scyBjb21taXQ6ICIsIGdldF9naXRfY29tbWl0KCkpKQogIHRoaXNfc2F2ZSA8LSBwYXN0ZTAoZ3N1YihwYXR0ZXJuPSJcXC5SbWQiLCByZXBsYWNlPSIiLCB4PXJtZF9maWxlKSwgIi12IiwgdmVyLCAiLnJkYS54eiIpCiAgbWVzc2FnZShwYXN0ZTAoIlNhdmluZyB0byAiLCB0aGlzX3NhdmUpKQogIHRtcCA8LSBzbShzYXZlbWUoZmlsZW5hbWU9dGhpc19zYXZlKSkKfQpgYGAK