index.html preprocessing.html

1 Annotation version: 20170905

There are a few methods of importing annotation data into R. The ‘new’ method is to use the DBI interfaces provided by bioconductor. These were a bit more difficult for me to learn, but now that I have a general idea of how they work, I grudgingly admit that they are far superior.

1.1 OrganismDbi/OrgDb/TxDb

The mouse OrganismDbi interface is contained the library ‘Mus.musculus.’ It brings together the OrgDb and TxDb packages for the mouse: ‘org.Mm.eg.db’ and ‘TxDb.Mmusculus.UCSC.mm10.knownGene’ respectively. I am going to need to figure out how to integrate the miRNA annotations into them, but for the moment I will work just with them and hopefully do them in a way which is cleaner and clearer than before.

## orgDb packages are responsible for providing mapping to other databases,
## like GO/KEGG/Entrez/Ensembl/etc
tt <- sm(require.auto("org.Mm.eg.db"))
tmp <- sm(library("org.Mm.eg.db"))
## In contrast, TxDb packages are responsible for providing information about
## each transcript, thus providing a link between the actual genome sequence
## and the transcripts encoded within.
tt <- sm(require.auto("TxDb.Mmusculus.UCSC.mm10.knownGene"))
tmp <- sm(library("TxDb.Mmusculus.UCSC.mm10.knownGene"))
## the Mus.musculus package actually loads the previous 2, but I want to be explicit.
tt <- sm(require.auto("Mus.musculus"))
tmp <- sm(library("Mus.musculus"))

## The following section gathers annotation data from the 2015 archive of ensembl.
## In this case, all the annotations are explicitly the mirbase data
## along with a subset of other ensembl fields.
ensembl_genes <- biomaRt::useEnsembl("ensembl", host="dec2015.archive.ensembl.org")
genes_datasets <- biomaRt::listDatasets(ensembl_genes)
mouse_genes <- biomaRt::useEnsembl(biomart="ensembl", dataset="mmusculus_gene_ensembl",
                                   host="dec2015.archive.ensembl.org")
mirna_attribs <- c("ensembl_gene_id", "ensembl_transcript_id", "description",
                  "external_gene_name", "mirbase_accession", "mirbase_id",
                  "mgi_transcript_name", "chromosome_name")
mouse_mirna_attribs <- biomaRt::listAttributes(mart=mouse_genes)
mm_mi_genes <- biomaRt::getBM(attributes=mirna_attribs, filters="biotype",
                              values="miRNA", mart=mouse_genes)
## The following is used by miRNAtab.Rmd
mm_mi_genes$ID <- paste0("chr", mm_mi_genes$chromosome_name, "_", mm_mi_genes$ensembl_gene_id)
rownames(mm_mi_genes) <- make.names(mm_mi_genes$ID, unique=TRUE)

tt <- sm(require.auto("mirbase.db"))
library("mirbase.db")
chosen_df <- mm_mi_genes[, c("ensembl_gene_id","mirbase_id")]
chosen_df$fivep_accession <- ""
chosen_df$fivep_id <- ""
chosen_df$threep_accession <- ""
chosen_df$threep_id <- ""
mikey <- "mmu-mir-344h-1"
names <- rownames(chosen_df)
for (minum in 1:length(chosen_df$mirbase_id)) {
    mikey <- chosen_df$mirbase_id[[minum]]
    if (mikey == "") {
        next
    }
    mi_mappings <- try(get(mikey, mirbaseMATURE), silent=TRUE)
    if (class(mi_mappings) == "try-error") {
        next
    }
    mi_accessions <- mi_mappings@matureAccession
    mi_names <- mi_mappings@matureName
    name <- names[[minum]]
    chosen_df[name, "fivep_accession"] <- mi_accessions[1]
    chosen_df[name, "threep_accession"] <- mi_accessions[2]
    chosen_df[name, "fivep_id"] <- mi_names[1]
    chosen_df[name, "threep_id"] <- mi_names[2]
}
write.table(x=chosen_df, file="reference/mi_mappings.tab")

ensembl_new <- biomaRt::useEnsembl("regulation")
new_datasets <- biomaRt::listDatasets(ensembl_new)
mouse_dataset <- biomaRt::useEnsembl(biomart="regulation", dataset="mmusculus_annotated_feature")
mirna_columns <- biomaRt::listAttributes(mouse_dataset)
mirna_columns
##                        name              description              page
## 1                    efo_id       EFO term accession annotated_feature
## 2           chromosome_name Chromosome/scaffold name annotated_feature
## 3          chromosome_start               Start (bp) annotated_feature
## 4            chromosome_end                 End (bp) annotated_feature
## 5         feature_type_name             Feature type annotated_feature
## 6        feature_type_class       Feature type class annotated_feature
## 7  feature_type_description Feature type description annotated_feature
## 8            epigenome_name           Epigenome name annotated_feature
## 9     epigenome_description    Epigenome description annotated_feature
## 10             project_name             Project name annotated_feature
## 11               archive_id SRA experiment accession annotated_feature
## 12             so_accession        SO term accession annotated_feature
## 13                  so_name             SO term name annotated_feature
new_columns <- c("efo_id", "so_accession", "feature_type_name")
## 2016-09-28 This seems to be timing out right now.
new_columns_download <- biomaRt::getBM(attributes=new_columns, mart=mouse_dataset)

## In contrast, when working with the transcripts from mice, we can pull
## everything directly from the OrganismDbi.
mm_tx_org <- sm(load_orgdb_annotations(Mus.musculus, keytype="ensembltrans",
                                 fields=c("cdschrom", "definition", "genename")))
## I think the gene IDs returned by this are already perfect for the transcripts
## in the count tables.
mm_tx_genes <- mm_tx_org$genes
## The inclusion of the 'definition' columns means that we also end up with a bunch
## of redundant entries, like 200,000 of them; therefore I am going to whittle down
## the list.
mm_tx_unique <- !grepl(pattern="\\.", x=rownames(mm_tx_genes))
mm_tx_genes <- mm_tx_genes[mm_tx_unique, ]

The Mus.musculus data does not seem to include the full set of genes, lets compare to what we get when using biomart.

##mmtx_annotations <- get_biomart_annotations(species="mmusculus")
mart <- biomaRt::useMart(biomart="ENSEMBL_MART_ENSEMBL", host="dec2015.archive.ensembl.org")
dataset <- paste0("mmusculus_gene_ensembl")
ensembl <- try(biomaRt::useDataset(dataset, mart=mart))
lots_of_rows <- biomaRt::listAttributes(ensembl)  ## List of possible attributes
## wanted_attributes <- c("ensembl_gene_id", "ensembl_transcript_id", "ensembl_peptide_id", "chromosome_name", "start_position","end_position","description", "entrezgene","hgnc_symbol","hgnc_id","uniprot_sptrembl","uniprot_swissprot","uniprot_genename")  ## attributes Lucia and I chose, but too many
wanted_attributes_global <- c("ensembl_transcript_id", "ensembl_gene_id", "chromosome_name",
                              "start_position", "end_position", "strand", "description")
wanted_attributes_names <- c("ensembl_transcript_id", "entrezgene", "hgnc_symbol", "hgnc_id")
wanted_attributes_uniprot <- c("ensembl_transcript_id", "uniprot_sptrembl", "uniprot_swissprot")

wanted_global_annotations <- biomaRt::getBM(attributes=wanted_attributes_global, mart=ensembl)
dim(wanted_global_annotations)
## [1] 114083      7
wanted_names_annotations <- biomaRt::getBM(attributes=wanted_attributes_names, mart=ensembl)
wanted_uniprot_annotations <- biomaRt::getBM(attributes=wanted_attributes_uniprot, mart=ensembl)
dim(wanted_uniprot_annotations)
## [1] 57040     3
wanted_annotations <- merge(wanted_global_annotations, wanted_uniprot_annotations,
                            by.x="ensembl_transcript_id", by.y="ensembl_transcript_id", all.y=TRUE)

length(unique(wanted_annotations$ensembl_transcript_id))
## [1] 56999
rownames(wanted_annotations) <- make.names(wanted_annotations[["ensembl_transcript_id"]], unique=TRUE)

1.2 Prepare annotation tables

The annotation data given by load_annotations() is nice, but needs a little cleaning to match up with the count tables from the experiment.

## I happen to know that the gene IDs of the miRNA data are of the format
## 'chrx_ENSMUSG' so I will change the rownames of this slightly.
mm_mi_genes[["ID"]] <- paste0("chr", mm_mi_genes[["chromosome_name"]],
                              "_", mm_mi_genes[["ensembl_gene_id"]])
rownames(mm_mi_genes) <- make.names(mm_mi_genes[["ID"]], unique=TRUE)
rownames(mm_tx_genes) <- make.names(mm_tx_genes$ensembltrans, unique=TRUE)

2 Creating an Expressionset

The expressionset is the primary sequencing data format used in R. It brings together the experiment metadata (condition/batch/etc), gene annotations, and observed counts. The excel file containing the metadata also provides filenames containing the appropriate count tables.

mm_mi <- sm(create_expt(metadata="sample_sheets/all_samplesv1.xlsx", gene_info=mm_mi_genes,
                        file_column="mifile"))
## The IDs in the expressionset for the miRNAs are of the format 'chr1_ENSMUSG....'
mm_tx <- sm(create_expt(metadata="sample_sheets/all_samplesv1.xlsx", gene_info=wanted_annotations,
                        file_column="txfile"))
## The IDs in the expressionset for transcripts are of the format 'ENSMUST....'

In the previous block, we created the primary expressionsets used in the following work. However, much of it will need to performed with subsets of this data, so let us create those here.

## Because of the shenanigans I did to change the seed length for alignments, the counts are not
## guaranteed to be in integers, so round them here.
mm_mir <- mm_mi
Biobase::exprs(mm_mir$expressionset) <- round(Biobase::exprs(mm_mir$expressionset))
mm_txr <- mm_tx
Biobase::exprs(mm_txr$expressionset) <- round(Biobase::exprs(mm_txr$expressionset))

## Subsets!
mmmi_small <- expt_subset(mm_mir, subset="librarytype=='small'")
mmmi_large <- expt_subset(mm_mir, subset="librarytype=='large'")
mmtx_small <- expt_subset(mm_txr, subset="librarytype=='small'")
mmtx_large <- expt_subset(mm_txr, subset="librarytype=='large'")

mature_annot <- read.table("reference/mi_mappings.tab")
mature_fivep <- mature_annot[, c("fivep_accession","fivep_id")]
fivep_completed <- as.logical(mature_fivep[[1]] != "")
mature_fivep <- mature_fivep[fivep_completed, ]
mature_threep <- mature_annot[, c("threep_accession","threep_id")]
threep_completed <- as.logical(mature_threep[[1]] != "" & !is.na(mature_threep[[1]]))
mature_threep <- mature_threep[threep_completed, ]
colnames(mature_fivep) <- c("accession","id")
colnames(mature_threep) <- c("accession","id")
all_mature <- as.data.frame(rbind(mature_fivep, mature_threep))
rownames(all_mature) <-  make.names(gsub(pattern="\\-", replacement="\\.", x=all_mature$id), unique=TRUE)

mmmi_mature <- sm(create_expt(metadata="sample_sheets/all_samplesv1.xlsx", gene_info=all_mature,
                              file_column="maturefile"))

At this point, we should have everything necessary to perform the various analyses. So save the current data for reuse elsewhere.

pander::pander(sessionInfo())

R version 3.4.1 (2017-06-30)

**Platform:** x86_64-pc-linux-gnu (64-bit)

locale: LC_CTYPE=en_US.utf8, LC_NUMERIC=C, LC_TIME=en_US.utf8, LC_COLLATE=en_US.utf8, LC_MONETARY=en_US.utf8, LC_MESSAGES=en_US.utf8, LC_PAPER=en_US.utf8, LC_NAME=C, LC_ADDRESS=C, LC_TELEPHONE=C, LC_MEASUREMENT=en_US.utf8 and LC_IDENTIFICATION=C

attached base packages: parallel, stats4, stats, graphics, grDevices, utils, datasets, methods and base

other attached packages: mirbase.db(v.1.2.0), Mus.musculus(v.1.3.1), GO.db(v.3.4.1), OrganismDbi(v.1.18.0), TxDb.Mmusculus.UCSC.mm10.knownGene(v.3.4.0), GenomicFeatures(v.1.28.4), GenomicRanges(v.1.28.4), GenomeInfoDb(v.1.12.2), org.Mm.eg.db(v.3.4.1), AnnotationDbi(v.1.38.2), IRanges(v.2.10.3), S4Vectors(v.0.14.3), Biobase(v.2.36.2), BiocGenerics(v.0.22.0) and hpgltools(v.2017.01)

loaded via a namespace (and not attached): Rcpp(v.0.12.12), lattice(v.0.20-35), Rsamtools(v.1.28.0), Biostrings(v.2.44.2), rprojroot(v.1.2), digest(v.0.6.12), foreach(v.1.4.3), R6(v.2.2.2), plyr(v.1.8.4), backports(v.1.1.0), RSQLite(v.2.0), evaluate(v.0.10.1), ggplot2(v.2.2.1), BiocInstaller(v.1.26.1), zlibbioc(v.1.22.0), rlang(v.0.1.2), lazyeval(v.0.2.0), data.table(v.1.10.4), blob(v.1.1.0), Matrix(v.1.2-11), rmarkdown(v.1.6), devtools(v.1.13.3), BiocParallel(v.1.10.1), pander(v.0.6.1), stringr(v.1.2.0), RCurl(v.1.95-4.8), bit(v.1.1-12), biomaRt(v.2.32.1), munsell(v.0.4.3), DelayedArray(v.0.2.7), compiler(v.3.4.1), rtracklayer(v.1.36.4), pkgconfig(v.2.0.1), base64enc(v.0.1-3), htmltools(v.0.3.6), SummarizedExperiment(v.1.6.3), tibble(v.1.3.4), GenomeInfoDbData(v.0.99.0), roxygen2(v.6.0.1), codetools(v.0.2-15), matrixStats(v.0.52.2), XML(v.3.98-1.9), crayon(v.1.3.2), withr(v.2.0.0), GenomicAlignments(v.1.12.2), bitops(v.1.0-6), commonmark(v.1.4), grid(v.3.4.1), RBGL(v.1.52.0), gtable(v.0.2.0), DBI(v.0.7), magrittr(v.1.5), scales(v.0.5.0), graph(v.1.54.0), stringi(v.1.1.5), XVector(v.0.16.0), testthat(v.1.0.2), xml2(v.1.1.1), openxlsx(v.4.0.17), RColorBrewer(v.1.1-2), iterators(v.1.0.8), tools(v.3.4.1), bit64(v.0.9-7), yaml(v.2.1.14), colorspace(v.1.3-2), memoise(v.1.1.0) and knitr(v.1.17)

message(paste0("This is hpgltools commit: ", get_git_commit()))
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset 739e4a10345a89efb17b37e7de07c5811491ccea
## R> packrat::restore()
## This is hpgltools commit: Thu Aug 31 11:24:06 2017 -0400: 739e4a10345a89efb17b37e7de07c5811491ccea
this_save <- paste0(gsub(pattern="\\.Rmd", replace="", x=rmd_file), "-v", ver, ".rda.xz")
message(paste0("Saving to ", this_save))
## Saving to 01_annotation-v20170905.rda.xz
tmp <- sm(saveme(filename=this_save))
LS0tCnRpdGxlOiAiQW5ub3RhdGlvbiBkYXRhIHVzZWQgZm9yIE0ubXVzY3VsdXMgc2FtcGxlcy4iCmF1dGhvcjogImF0YiBhYmVsZXdAZ21haWwuY29tIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKIGh0bWxfZG9jdW1lbnQ6CiAgY29kZV9kb3dubG9hZDogdHJ1ZQogIGNvZGVfZm9sZGluZzogc2hvdwogIGZpZ19jYXB0aW9uOiB0cnVlCiAgZmlnX2hlaWdodDogNwogIGZpZ193aWR0aDogNwogIGhpZ2hsaWdodDogdGFuZ28KICBrZWVwX21kOiBmYWxzZQogIG1vZGU6IHNlbGZjb250YWluZWQKICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogIHRoZW1lOiBjb3NtbwogIHRvYzogdHJ1ZQogIHRvY19mbG9hdDoKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKPHN0eWxlPgogIGJvZHkgLm1haW4tY29udGFpbmVyIHsKICAgIG1heC13aWR0aDogMTYwMHB4OwogIH0KPC9zdHlsZT4KCmBgYHtyIG9wdGlvbnMsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoaHBnbHRvb2xzKQp0dCA8LSBkZXZ0b29sczo6bG9hZF9hbGwoIn4vaHBnbHRvb2xzIikKa25pdHI6Om9wdHNfa25pdCRzZXQocHJvZ3Jlc3M9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICB3aWR0aD05MCwKICAgICAgICAgICAgICAgICAgICAgZWNobz1UUlVFKQprbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3I9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aD04LAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodD04LAogICAgICAgICAgICAgICAgICAgICAgZHBpPTk2KQpvbGRfb3B0aW9ucyA8LSBvcHRpb25zKGRpZ2l0cz00LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAga25pdHIuZHVwbGljYXRlLmxhYmVsPSJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemU9MTApKQpzZXQuc2VlZCgxKQp2ZXIgPC0gIjIwMTcwOTA1IgpwcmV2aW91c19maWxlIDwtICJpbmRleC5SbWQiCgp0bXAgPC0gdHJ5KHNtKGxvYWRtZShmaWxlbmFtZT1wYXN0ZTAoZ3N1YihwYXR0ZXJuPSJcXC5SbWQiLCByZXBsYWNlPSIiLCB4PXByZXZpb3VzX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikpKSkKCnJtZF9maWxlIDwtICIwMV9hbm5vdGF0aW9uLlJtZCIKYGBgCgpgYGB7ciByZW5kZXJpbmcsIGluY2x1ZGU9RkFMU0UsIGV2YWw9RkFMU0V9CnJtYXJrZG93bjo6cmVuZGVyKHJtZF9maWxlKQoKcm1hcmtkb3duOjpyZW5kZXIocm1kX2ZpbGUsIG91dHB1dF9mb3JtYXQ9InBkZl9kb2N1bWVudCIpCmBgYAoKW2luZGV4Lmh0bWxdKGluZGV4Lmh0bWwpIFtwcmVwcm9jZXNzaW5nLmh0bWxdKHByZXByb2Nlc3NpbmcuaHRtbCkKCiMgQW5ub3RhdGlvbiB2ZXJzaW9uOiBgciB2ZXJgCgpUaGVyZSBhcmUgYSBmZXcgbWV0aG9kcyBvZiBpbXBvcnRpbmcgYW5ub3RhdGlvbiBkYXRhIGludG8gUi4gIFRoZSAnbmV3JyBtZXRob2QgaXMgdG8gdXNlIHRoZSBEQkkKaW50ZXJmYWNlcyBwcm92aWRlZCBieSBiaW9jb25kdWN0b3IuICBUaGVzZSB3ZXJlIGEgYml0IG1vcmUgZGlmZmljdWx0IGZvciBtZSB0byBsZWFybiwgYnV0IG5vdyB0aGF0CkkgaGF2ZSBhIGdlbmVyYWwgaWRlYSBvZiBob3cgdGhleSB3b3JrLCBJIGdydWRnaW5nbHkgYWRtaXQgdGhhdCB0aGV5IGFyZSBmYXIgc3VwZXJpb3IuCgojIyBPcmdhbmlzbURiaS9PcmdEYi9UeERiCgpUaGUgbW91c2UgT3JnYW5pc21EYmkgaW50ZXJmYWNlIGlzIGNvbnRhaW5lZCB0aGUgbGlicmFyeSAnTXVzLm11c2N1bHVzLicgIEl0IGJyaW5ncyB0b2dldGhlciB0aGUKT3JnRGIgYW5kIFR4RGIgcGFja2FnZXMgZm9yIHRoZSBtb3VzZTogJ29yZy5NbS5lZy5kYicgYW5kICdUeERiLk1tdXNjdWx1cy5VQ1NDLm1tMTAua25vd25HZW5lJwpyZXNwZWN0aXZlbHkuICBJIGFtIGdvaW5nIHRvIG5lZWQgdG8gZmlndXJlIG91dCBob3cgdG8gaW50ZWdyYXRlIHRoZSBtaVJOQSBhbm5vdGF0aW9ucyBpbnRvIHRoZW0sCmJ1dCBmb3IgdGhlIG1vbWVudCBJIHdpbGwgd29yayBqdXN0IHdpdGggdGhlbSBhbmQgaG9wZWZ1bGx5IGRvIHRoZW0gaW4gYSB3YXkgd2hpY2ggaXMgY2xlYW5lciBhbmQKY2xlYXJlciB0aGFuIGJlZm9yZS4KCmBgYHtyIG1tdXNjdWx1c19vcmdkYn0KIyMgb3JnRGIgcGFja2FnZXMgYXJlIHJlc3BvbnNpYmxlIGZvciBwcm92aWRpbmcgbWFwcGluZyB0byBvdGhlciBkYXRhYmFzZXMsCiMjIGxpa2UgR08vS0VHRy9FbnRyZXovRW5zZW1ibC9ldGMKdHQgPC0gc20ocmVxdWlyZS5hdXRvKCJvcmcuTW0uZWcuZGIiKSkKdG1wIDwtIHNtKGxpYnJhcnkoIm9yZy5NbS5lZy5kYiIpKQojIyBJbiBjb250cmFzdCwgVHhEYiBwYWNrYWdlcyBhcmUgcmVzcG9uc2libGUgZm9yIHByb3ZpZGluZyBpbmZvcm1hdGlvbiBhYm91dAojIyBlYWNoIHRyYW5zY3JpcHQsIHRodXMgcHJvdmlkaW5nIGEgbGluayBiZXR3ZWVuIHRoZSBhY3R1YWwgZ2Vub21lIHNlcXVlbmNlCiMjIGFuZCB0aGUgdHJhbnNjcmlwdHMgZW5jb2RlZCB3aXRoaW4uCnR0IDwtIHNtKHJlcXVpcmUuYXV0bygiVHhEYi5NbXVzY3VsdXMuVUNTQy5tbTEwLmtub3duR2VuZSIpKQp0bXAgPC0gc20obGlicmFyeSgiVHhEYi5NbXVzY3VsdXMuVUNTQy5tbTEwLmtub3duR2VuZSIpKQojIyB0aGUgTXVzLm11c2N1bHVzIHBhY2thZ2UgYWN0dWFsbHkgbG9hZHMgdGhlIHByZXZpb3VzIDIsIGJ1dCBJIHdhbnQgdG8gYmUgZXhwbGljaXQuCnR0IDwtIHNtKHJlcXVpcmUuYXV0bygiTXVzLm11c2N1bHVzIikpCnRtcCA8LSBzbShsaWJyYXJ5KCJNdXMubXVzY3VsdXMiKSkKCiMjIFRoZSBmb2xsb3dpbmcgc2VjdGlvbiBnYXRoZXJzIGFubm90YXRpb24gZGF0YSBmcm9tIHRoZSAyMDE1IGFyY2hpdmUgb2YgZW5zZW1ibC4KIyMgSW4gdGhpcyBjYXNlLCBhbGwgdGhlIGFubm90YXRpb25zIGFyZSBleHBsaWNpdGx5IHRoZSBtaXJiYXNlIGRhdGEKIyMgYWxvbmcgd2l0aCBhIHN1YnNldCBvZiBvdGhlciBlbnNlbWJsIGZpZWxkcy4KZW5zZW1ibF9nZW5lcyA8LSBiaW9tYVJ0Ojp1c2VFbnNlbWJsKCJlbnNlbWJsIiwgaG9zdD0iZGVjMjAxNS5hcmNoaXZlLmVuc2VtYmwub3JnIikKZ2VuZXNfZGF0YXNldHMgPC0gYmlvbWFSdDo6bGlzdERhdGFzZXRzKGVuc2VtYmxfZ2VuZXMpCm1vdXNlX2dlbmVzIDwtIGJpb21hUnQ6OnVzZUVuc2VtYmwoYmlvbWFydD0iZW5zZW1ibCIsIGRhdGFzZXQ9Im1tdXNjdWx1c19nZW5lX2Vuc2VtYmwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvc3Q9ImRlYzIwMTUuYXJjaGl2ZS5lbnNlbWJsLm9yZyIpCm1pcm5hX2F0dHJpYnMgPC0gYygiZW5zZW1ibF9nZW5lX2lkIiwgImVuc2VtYmxfdHJhbnNjcmlwdF9pZCIsICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJleHRlcm5hbF9nZW5lX25hbWUiLCAibWlyYmFzZV9hY2Nlc3Npb24iLCAibWlyYmFzZV9pZCIsCiAgICAgICAgICAgICAgICAgICJtZ2lfdHJhbnNjcmlwdF9uYW1lIiwgImNocm9tb3NvbWVfbmFtZSIpCm1vdXNlX21pcm5hX2F0dHJpYnMgPC0gYmlvbWFSdDo6bGlzdEF0dHJpYnV0ZXMobWFydD1tb3VzZV9nZW5lcykKbW1fbWlfZ2VuZXMgPC0gYmlvbWFSdDo6Z2V0Qk0oYXR0cmlidXRlcz1taXJuYV9hdHRyaWJzLCBmaWx0ZXJzPSJiaW90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzPSJtaVJOQSIsIG1hcnQ9bW91c2VfZ2VuZXMpCiMjIFRoZSBmb2xsb3dpbmcgaXMgdXNlZCBieSBtaVJOQXRhYi5SbWQKbW1fbWlfZ2VuZXMkSUQgPC0gcGFzdGUwKCJjaHIiLCBtbV9taV9nZW5lcyRjaHJvbW9zb21lX25hbWUsICJfIiwgbW1fbWlfZ2VuZXMkZW5zZW1ibF9nZW5lX2lkKQpyb3duYW1lcyhtbV9taV9nZW5lcykgPC0gbWFrZS5uYW1lcyhtbV9taV9nZW5lcyRJRCwgdW5pcXVlPVRSVUUpCgp0dCA8LSBzbShyZXF1aXJlLmF1dG8oIm1pcmJhc2UuZGIiKSkKbGlicmFyeSgibWlyYmFzZS5kYiIpCmNob3Nlbl9kZiA8LSBtbV9taV9nZW5lc1ssIGMoImVuc2VtYmxfZ2VuZV9pZCIsIm1pcmJhc2VfaWQiKV0KY2hvc2VuX2RmJGZpdmVwX2FjY2Vzc2lvbiA8LSAiIgpjaG9zZW5fZGYkZml2ZXBfaWQgPC0gIiIKY2hvc2VuX2RmJHRocmVlcF9hY2Nlc3Npb24gPC0gIiIKY2hvc2VuX2RmJHRocmVlcF9pZCA8LSAiIgptaWtleSA8LSAibW11LW1pci0zNDRoLTEiCm5hbWVzIDwtIHJvd25hbWVzKGNob3Nlbl9kZikKZm9yIChtaW51bSBpbiAxOmxlbmd0aChjaG9zZW5fZGYkbWlyYmFzZV9pZCkpIHsKICAgIG1pa2V5IDwtIGNob3Nlbl9kZiRtaXJiYXNlX2lkW1ttaW51bV1dCiAgICBpZiAobWlrZXkgPT0gIiIpIHsKICAgICAgICBuZXh0CiAgICB9CiAgICBtaV9tYXBwaW5ncyA8LSB0cnkoZ2V0KG1pa2V5LCBtaXJiYXNlTUFUVVJFKSwgc2lsZW50PVRSVUUpCiAgICBpZiAoY2xhc3MobWlfbWFwcGluZ3MpID09ICJ0cnktZXJyb3IiKSB7CiAgICAgICAgbmV4dAogICAgfQogICAgbWlfYWNjZXNzaW9ucyA8LSBtaV9tYXBwaW5nc0BtYXR1cmVBY2Nlc3Npb24KICAgIG1pX25hbWVzIDwtIG1pX21hcHBpbmdzQG1hdHVyZU5hbWUKICAgIG5hbWUgPC0gbmFtZXNbW21pbnVtXV0KICAgIGNob3Nlbl9kZltuYW1lLCAiZml2ZXBfYWNjZXNzaW9uIl0gPC0gbWlfYWNjZXNzaW9uc1sxXQogICAgY2hvc2VuX2RmW25hbWUsICJ0aHJlZXBfYWNjZXNzaW9uIl0gPC0gbWlfYWNjZXNzaW9uc1syXQogICAgY2hvc2VuX2RmW25hbWUsICJmaXZlcF9pZCJdIDwtIG1pX25hbWVzWzFdCiAgICBjaG9zZW5fZGZbbmFtZSwgInRocmVlcF9pZCJdIDwtIG1pX25hbWVzWzJdCn0Kd3JpdGUudGFibGUoeD1jaG9zZW5fZGYsIGZpbGU9InJlZmVyZW5jZS9taV9tYXBwaW5ncy50YWIiKQoKZW5zZW1ibF9uZXcgPC0gYmlvbWFSdDo6dXNlRW5zZW1ibCgicmVndWxhdGlvbiIpCm5ld19kYXRhc2V0cyA8LSBiaW9tYVJ0OjpsaXN0RGF0YXNldHMoZW5zZW1ibF9uZXcpCm1vdXNlX2RhdGFzZXQgPC0gYmlvbWFSdDo6dXNlRW5zZW1ibChiaW9tYXJ0PSJyZWd1bGF0aW9uIiwgZGF0YXNldD0ibW11c2N1bHVzX2Fubm90YXRlZF9mZWF0dXJlIikKbWlybmFfY29sdW1ucyA8LSBiaW9tYVJ0OjpsaXN0QXR0cmlidXRlcyhtb3VzZV9kYXRhc2V0KQptaXJuYV9jb2x1bW5zCm5ld19jb2x1bW5zIDwtIGMoImVmb19pZCIsICJzb19hY2Nlc3Npb24iLCAiZmVhdHVyZV90eXBlX25hbWUiKQojIyAyMDE2LTA5LTI4IFRoaXMgc2VlbXMgdG8gYmUgdGltaW5nIG91dCByaWdodCBub3cuCm5ld19jb2x1bW5zX2Rvd25sb2FkIDwtIGJpb21hUnQ6OmdldEJNKGF0dHJpYnV0ZXM9bmV3X2NvbHVtbnMsIG1hcnQ9bW91c2VfZGF0YXNldCkKCiMjIEluIGNvbnRyYXN0LCB3aGVuIHdvcmtpbmcgd2l0aCB0aGUgdHJhbnNjcmlwdHMgZnJvbSBtaWNlLCB3ZSBjYW4gcHVsbAojIyBldmVyeXRoaW5nIGRpcmVjdGx5IGZyb20gdGhlIE9yZ2FuaXNtRGJpLgptbV90eF9vcmcgPC0gc20obG9hZF9vcmdkYl9hbm5vdGF0aW9ucyhNdXMubXVzY3VsdXMsIGtleXR5cGU9ImVuc2VtYmx0cmFucyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpZWxkcz1jKCJjZHNjaHJvbSIsICJkZWZpbml0aW9uIiwgImdlbmVuYW1lIikpKQojIyBJIHRoaW5rIHRoZSBnZW5lIElEcyByZXR1cm5lZCBieSB0aGlzIGFyZSBhbHJlYWR5IHBlcmZlY3QgZm9yIHRoZSB0cmFuc2NyaXB0cwojIyBpbiB0aGUgY291bnQgdGFibGVzLgptbV90eF9nZW5lcyA8LSBtbV90eF9vcmckZ2VuZXMKIyMgVGhlIGluY2x1c2lvbiBvZiB0aGUgJ2RlZmluaXRpb24nIGNvbHVtbnMgbWVhbnMgdGhhdCB3ZSBhbHNvIGVuZCB1cCB3aXRoIGEgYnVuY2gKIyMgb2YgcmVkdW5kYW50IGVudHJpZXMsIGxpa2UgMjAwLDAwMCBvZiB0aGVtOyB0aGVyZWZvcmUgSSBhbSBnb2luZyB0byB3aGl0dGxlIGRvd24KIyMgdGhlIGxpc3QuCm1tX3R4X3VuaXF1ZSA8LSAhZ3JlcGwocGF0dGVybj0iXFwuIiwgeD1yb3duYW1lcyhtbV90eF9nZW5lcykpCm1tX3R4X2dlbmVzIDwtIG1tX3R4X2dlbmVzW21tX3R4X3VuaXF1ZSwgXQpgYGAKClRoZSBNdXMubXVzY3VsdXMgZGF0YSBkb2VzIG5vdCBzZWVtIHRvIGluY2x1ZGUgdGhlIGZ1bGwgc2V0IG9mIGdlbmVzLCBsZXRzIGNvbXBhcmUgdG8gd2hhdCB3ZSBnZXQgd2hlbiB1c2luZyBiaW9tYXJ0LgoKYGBge3IgY29tcGFyZV90eF9iaW9tYXJ0fQojI21tdHhfYW5ub3RhdGlvbnMgPC0gZ2V0X2Jpb21hcnRfYW5ub3RhdGlvbnMoc3BlY2llcz0ibW11c2N1bHVzIikKbWFydCA8LSBiaW9tYVJ0Ojp1c2VNYXJ0KGJpb21hcnQ9IkVOU0VNQkxfTUFSVF9FTlNFTUJMIiwgaG9zdD0iZGVjMjAxNS5hcmNoaXZlLmVuc2VtYmwub3JnIikKZGF0YXNldCA8LSBwYXN0ZTAoIm1tdXNjdWx1c19nZW5lX2Vuc2VtYmwiKQplbnNlbWJsIDwtIHRyeShiaW9tYVJ0Ojp1c2VEYXRhc2V0KGRhdGFzZXQsIG1hcnQ9bWFydCkpCmxvdHNfb2Zfcm93cyA8LSBiaW9tYVJ0OjpsaXN0QXR0cmlidXRlcyhlbnNlbWJsKSAgIyMgTGlzdCBvZiBwb3NzaWJsZSBhdHRyaWJ1dGVzCiMjIHdhbnRlZF9hdHRyaWJ1dGVzIDwtIGMoImVuc2VtYmxfZ2VuZV9pZCIsICJlbnNlbWJsX3RyYW5zY3JpcHRfaWQiLCAiZW5zZW1ibF9wZXB0aWRlX2lkIiwgImNocm9tb3NvbWVfbmFtZSIsICJzdGFydF9wb3NpdGlvbiIsImVuZF9wb3NpdGlvbiIsImRlc2NyaXB0aW9uIiwgImVudHJlemdlbmUiLCJoZ25jX3N5bWJvbCIsImhnbmNfaWQiLCJ1bmlwcm90X3NwdHJlbWJsIiwidW5pcHJvdF9zd2lzc3Byb3QiLCJ1bmlwcm90X2dlbmVuYW1lIikgICMjIGF0dHJpYnV0ZXMgTHVjaWEgYW5kIEkgY2hvc2UsIGJ1dCB0b28gbWFueQp3YW50ZWRfYXR0cmlidXRlc19nbG9iYWwgPC0gYygiZW5zZW1ibF90cmFuc2NyaXB0X2lkIiwgImVuc2VtYmxfZ2VuZV9pZCIsICJjaHJvbW9zb21lX25hbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RhcnRfcG9zaXRpb24iLCAiZW5kX3Bvc2l0aW9uIiwgInN0cmFuZCIsICJkZXNjcmlwdGlvbiIpCndhbnRlZF9hdHRyaWJ1dGVzX25hbWVzIDwtIGMoImVuc2VtYmxfdHJhbnNjcmlwdF9pZCIsICJlbnRyZXpnZW5lIiwgImhnbmNfc3ltYm9sIiwgImhnbmNfaWQiKQp3YW50ZWRfYXR0cmlidXRlc191bmlwcm90IDwtIGMoImVuc2VtYmxfdHJhbnNjcmlwdF9pZCIsICJ1bmlwcm90X3NwdHJlbWJsIiwgInVuaXByb3Rfc3dpc3Nwcm90IikKCndhbnRlZF9nbG9iYWxfYW5ub3RhdGlvbnMgPC0gYmlvbWFSdDo6Z2V0Qk0oYXR0cmlidXRlcz13YW50ZWRfYXR0cmlidXRlc19nbG9iYWwsIG1hcnQ9ZW5zZW1ibCkKZGltKHdhbnRlZF9nbG9iYWxfYW5ub3RhdGlvbnMpCndhbnRlZF9uYW1lc19hbm5vdGF0aW9ucyA8LSBiaW9tYVJ0OjpnZXRCTShhdHRyaWJ1dGVzPXdhbnRlZF9hdHRyaWJ1dGVzX25hbWVzLCBtYXJ0PWVuc2VtYmwpCndhbnRlZF91bmlwcm90X2Fubm90YXRpb25zIDwtIGJpb21hUnQ6OmdldEJNKGF0dHJpYnV0ZXM9d2FudGVkX2F0dHJpYnV0ZXNfdW5pcHJvdCwgbWFydD1lbnNlbWJsKQpkaW0od2FudGVkX3VuaXByb3RfYW5ub3RhdGlvbnMpCndhbnRlZF9hbm5vdGF0aW9ucyA8LSBtZXJnZSh3YW50ZWRfZ2xvYmFsX2Fubm90YXRpb25zLCB3YW50ZWRfdW5pcHJvdF9hbm5vdGF0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5Lng9ImVuc2VtYmxfdHJhbnNjcmlwdF9pZCIsIGJ5Lnk9ImVuc2VtYmxfdHJhbnNjcmlwdF9pZCIsIGFsbC55PVRSVUUpCgpsZW5ndGgodW5pcXVlKHdhbnRlZF9hbm5vdGF0aW9ucyRlbnNlbWJsX3RyYW5zY3JpcHRfaWQpKQpyb3duYW1lcyh3YW50ZWRfYW5ub3RhdGlvbnMpIDwtIG1ha2UubmFtZXMod2FudGVkX2Fubm90YXRpb25zW1siZW5zZW1ibF90cmFuc2NyaXB0X2lkIl1dLCB1bmlxdWU9VFJVRSkKYGBgCgojIyBQcmVwYXJlIGFubm90YXRpb24gdGFibGVzCgpUaGUgYW5ub3RhdGlvbiBkYXRhIGdpdmVuIGJ5IGxvYWRfYW5ub3RhdGlvbnMoKSBpcyBuaWNlLCBidXQgbmVlZHMgYSBsaXR0bGUgY2xlYW5pbmcgdG8gbWF0Y2ggdXAKd2l0aCB0aGUgY291bnQgdGFibGVzIGZyb20gdGhlIGV4cGVyaW1lbnQuCgpgYGB7ciBjbGVhbnVwX2Fubm90YXRpb25zfQojIyBJIGhhcHBlbiB0byBrbm93IHRoYXQgdGhlIGdlbmUgSURzIG9mIHRoZSBtaVJOQSBkYXRhIGFyZSBvZiB0aGUgZm9ybWF0CiMjICdjaHJ4X0VOU01VU0cnIHNvIEkgd2lsbCBjaGFuZ2UgdGhlIHJvd25hbWVzIG9mIHRoaXMgc2xpZ2h0bHkuCm1tX21pX2dlbmVzW1siSUQiXV0gPC0gcGFzdGUwKCJjaHIiLCBtbV9taV9nZW5lc1tbImNocm9tb3NvbWVfbmFtZSJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIl8iLCBtbV9taV9nZW5lc1tbImVuc2VtYmxfZ2VuZV9pZCJdXSkKcm93bmFtZXMobW1fbWlfZ2VuZXMpIDwtIG1ha2UubmFtZXMobW1fbWlfZ2VuZXNbWyJJRCJdXSwgdW5pcXVlPVRSVUUpCnJvd25hbWVzKG1tX3R4X2dlbmVzKSA8LSBtYWtlLm5hbWVzKG1tX3R4X2dlbmVzJGVuc2VtYmx0cmFucywgdW5pcXVlPVRSVUUpCmBgYAoKIyBDcmVhdGluZyBhbiBFeHByZXNzaW9uc2V0CgpUaGUgZXhwcmVzc2lvbnNldCBpcyB0aGUgcHJpbWFyeSBzZXF1ZW5jaW5nIGRhdGEgZm9ybWF0IHVzZWQgaW4gUi4gIEl0IGJyaW5ncyB0b2dldGhlciB0aGUKZXhwZXJpbWVudCBtZXRhZGF0YSAoY29uZGl0aW9uL2JhdGNoL2V0YyksIGdlbmUgYW5ub3RhdGlvbnMsIGFuZCBvYnNlcnZlZCBjb3VudHMuICBUaGUgZXhjZWwgZmlsZQpjb250YWluaW5nIHRoZSBtZXRhZGF0YSBhbHNvIHByb3ZpZGVzIGZpbGVuYW1lcyBjb250YWluaW5nIHRoZSBhcHByb3ByaWF0ZSBjb3VudCB0YWJsZXMuCgpgYGB7ciBleHBlcmltZW50YWxfZGVzaWdufQptbV9taSA8LSBzbShjcmVhdGVfZXhwdChtZXRhZGF0YT0ic2FtcGxlX3NoZWV0cy9hbGxfc2FtcGxlc3YxLnhsc3giLCBnZW5lX2luZm89bW1fbWlfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVfY29sdW1uPSJtaWZpbGUiKSkKIyMgVGhlIElEcyBpbiB0aGUgZXhwcmVzc2lvbnNldCBmb3IgdGhlIG1pUk5BcyBhcmUgb2YgdGhlIGZvcm1hdCAnY2hyMV9FTlNNVVNHLi4uLicKbW1fdHggPC0gc20oY3JlYXRlX2V4cHQobWV0YWRhdGE9InNhbXBsZV9zaGVldHMvYWxsX3NhbXBsZXN2MS54bHN4IiwgZ2VuZV9pbmZvPXdhbnRlZF9hbm5vdGF0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgZmlsZV9jb2x1bW49InR4ZmlsZSIpKQojIyBUaGUgSURzIGluIHRoZSBleHByZXNzaW9uc2V0IGZvciB0cmFuc2NyaXB0cyBhcmUgb2YgdGhlIGZvcm1hdCAnRU5TTVVTVC4uLi4nCmBgYAoKSW4gdGhlIHByZXZpb3VzIGJsb2NrLCB3ZSBjcmVhdGVkIHRoZSBwcmltYXJ5IGV4cHJlc3Npb25zZXRzIHVzZWQgaW4gdGhlCmZvbGxvd2luZyB3b3JrLiAgSG93ZXZlciwgbXVjaCBvZiBpdCB3aWxsIG5lZWQgdG8gcGVyZm9ybWVkIHdpdGggc3Vic2V0cyBvZiB0aGlzCmRhdGEsIHNvIGxldCB1cyBjcmVhdGUgdGhvc2UgaGVyZS4KCmBgYHtyIGNyZWF0ZV9zdWJzZXRzfQojIyBCZWNhdXNlIG9mIHRoZSBzaGVuYW5pZ2FucyBJIGRpZCB0byBjaGFuZ2UgdGhlIHNlZWQgbGVuZ3RoIGZvciBhbGlnbm1lbnRzLCB0aGUgY291bnRzIGFyZSBub3QKIyMgZ3VhcmFudGVlZCB0byBiZSBpbiBpbnRlZ2Vycywgc28gcm91bmQgdGhlbSBoZXJlLgptbV9taXIgPC0gbW1fbWkKQmlvYmFzZTo6ZXhwcnMobW1fbWlyJGV4cHJlc3Npb25zZXQpIDwtIHJvdW5kKEJpb2Jhc2U6OmV4cHJzKG1tX21pciRleHByZXNzaW9uc2V0KSkKbW1fdHhyIDwtIG1tX3R4CkJpb2Jhc2U6OmV4cHJzKG1tX3R4ciRleHByZXNzaW9uc2V0KSA8LSByb3VuZChCaW9iYXNlOjpleHBycyhtbV90eHIkZXhwcmVzc2lvbnNldCkpCgojIyBTdWJzZXRzIQptbW1pX3NtYWxsIDwtIGV4cHRfc3Vic2V0KG1tX21pciwgc3Vic2V0PSJsaWJyYXJ5dHlwZT09J3NtYWxsJyIpCm1tbWlfbGFyZ2UgPC0gZXhwdF9zdWJzZXQobW1fbWlyLCBzdWJzZXQ9ImxpYnJhcnl0eXBlPT0nbGFyZ2UnIikKbW10eF9zbWFsbCA8LSBleHB0X3N1YnNldChtbV90eHIsIHN1YnNldD0ibGlicmFyeXR5cGU9PSdzbWFsbCciKQptbXR4X2xhcmdlIDwtIGV4cHRfc3Vic2V0KG1tX3R4ciwgc3Vic2V0PSJsaWJyYXJ5dHlwZT09J2xhcmdlJyIpCgptYXR1cmVfYW5ub3QgPC0gcmVhZC50YWJsZSgicmVmZXJlbmNlL21pX21hcHBpbmdzLnRhYiIpCm1hdHVyZV9maXZlcCA8LSBtYXR1cmVfYW5ub3RbLCBjKCJmaXZlcF9hY2Nlc3Npb24iLCJmaXZlcF9pZCIpXQpmaXZlcF9jb21wbGV0ZWQgPC0gYXMubG9naWNhbChtYXR1cmVfZml2ZXBbWzFdXSAhPSAiIikKbWF0dXJlX2ZpdmVwIDwtIG1hdHVyZV9maXZlcFtmaXZlcF9jb21wbGV0ZWQsIF0KbWF0dXJlX3RocmVlcCA8LSBtYXR1cmVfYW5ub3RbLCBjKCJ0aHJlZXBfYWNjZXNzaW9uIiwidGhyZWVwX2lkIildCnRocmVlcF9jb21wbGV0ZWQgPC0gYXMubG9naWNhbChtYXR1cmVfdGhyZWVwW1sxXV0gIT0gIiIgJiAhaXMubmEobWF0dXJlX3RocmVlcFtbMV1dKSkKbWF0dXJlX3RocmVlcCA8LSBtYXR1cmVfdGhyZWVwW3RocmVlcF9jb21wbGV0ZWQsIF0KY29sbmFtZXMobWF0dXJlX2ZpdmVwKSA8LSBjKCJhY2Nlc3Npb24iLCJpZCIpCmNvbG5hbWVzKG1hdHVyZV90aHJlZXApIDwtIGMoImFjY2Vzc2lvbiIsImlkIikKYWxsX21hdHVyZSA8LSBhcy5kYXRhLmZyYW1lKHJiaW5kKG1hdHVyZV9maXZlcCwgbWF0dXJlX3RocmVlcCkpCnJvd25hbWVzKGFsbF9tYXR1cmUpIDwtICBtYWtlLm5hbWVzKGdzdWIocGF0dGVybj0iXFwtIiwgcmVwbGFjZW1lbnQ9IlxcLiIsIHg9YWxsX21hdHVyZSRpZCksIHVuaXF1ZT1UUlVFKQoKbW1taV9tYXR1cmUgPC0gc20oY3JlYXRlX2V4cHQobWV0YWRhdGE9InNhbXBsZV9zaGVldHMvYWxsX3NhbXBsZXN2MS54bHN4IiwgZ2VuZV9pbmZvPWFsbF9tYXR1cmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVfY29sdW1uPSJtYXR1cmVmaWxlIikpCmBgYAoKQXQgdGhpcyBwb2ludCwgd2Ugc2hvdWxkIGhhdmUgZXZlcnl0aGluZyBuZWNlc3NhcnkgdG8gcGVyZm9ybSB0aGUgdmFyaW91cyBhbmFseXNlcy4gIFNvIHNhdmUgdGhlIGN1cnJlbnQgZGF0YSBmb3IgcmV1c2UgZWxzZXdoZXJlLgoKYGBge3Igc2F2ZW1lfQpwYW5kZXI6OnBhbmRlcihzZXNzaW9uSW5mbygpKQptZXNzYWdlKHBhc3RlMCgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKSkKdGhpc19zYXZlIDwtIHBhc3RlMChnc3ViKHBhdHRlcm49IlxcLlJtZCIsIHJlcGxhY2U9IiIsIHg9cm1kX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikKbWVzc2FnZShwYXN0ZTAoIlNhdmluZyB0byAiLCB0aGlzX3NhdmUpKQp0bXAgPC0gc20oc2F2ZW1lKGZpbGVuYW1lPXRoaXNfc2F2ZSkpCmBgYAo=