Original Authors: Belinda Phipson, Anna Trigos, Matt Ritchie, Maria Doyle, Harriet Dashnow, Charity Law, Stephane Ballereau, Oscar Rueda, Ashley Sawle Based on the course RNAseq analysis in R delivered on May 11/12th 2016 and modified by Cancer Research Uk Cambridge Centre for the Functional Genomics Autumn School 2017

Gene Set Testing

In the early days of microarray analysis, people were happy if they got a handful of differentially-expressed genes that they could validate or follow-up. However, with later technologies (and depending on the experimental setup) we might have thousands of statistically-significant results, which no-one has the time to follow-up. Also, we might be interested in pathways / mechanisms that are altered and not just individual genes.

In this section we move towards discovering if our results are biologically significant. Are the genes that we have picked statistical flukes, or are there some commonalities.

There are two different approaches one might use, and we will cover the theory behind both

  • There is also a bunch of websites for doing the tests
    • we will show how they are done in Bioconductor so the theory is clear
  • We will assume we have done a differential-expression analysis, but the same techniques can be used for other situations when we have a gene list
    • ChIP-seq
    • RNA-seq

fgsea analysis

The fgsea package is a free implementation of the Broad’s GSEA software and is described in more detail in the package vignette “fast preranked gene set enrichment analysis (GSEA)”:

The GSEA analysis is performed by:

    1. ranking all genes in the data set based on their correlation to the chosen phenotype,
    1. identifying the rank positions of all members of the gene set, and
    1. calculating an enrichment score (ES) that represents the difference between the observed rankings and that which would be expected assuming a random rank distribution.

“After establishing the ES for each gene set across the phenotype, GSEA reiteratively randomizes the sample labels and retests for enrichment across the random classes. By performing repeated class label randomizations, the ES for each gene set across the true classes can be compared to the ES distribution from the random classes. Those gene sets that significantly outperform iterative random class permutations are considered significant.”

The article describing the original software is available here and there is also a commentary on GSEA.

In addition to the GSEA software the Broad also provide a number of very well curated gene sets for testing against your data - the Molecular Signatures Database (MSigDB). Unfortunately, these are collections of human genes. However, these lists have been translated to mouse equivalents by the Walter+Eliza Hall Institutes Bioinformatics service and made avaialble for download. These gene sets use Entrez ID as their identifier.

library(fgsea)
Loading required package: Rcpp
load("Robjects/LvsV_annotated.rdata")
head(results.annotated)
             ENSEMBL   baseMean log2FoldChange     lfcSE     stat        pvalue
1 ENSMUSG00000000381  398943.44       8.722325 0.3815879 22.94608 1.612341e-116
2 ENSMUSG00000061937 1134857.22       7.351405 0.3761569 19.72683  1.268851e-86
3 ENSMUSG00000026417   11320.43       6.078220 0.3332017 18.13304  1.748099e-73
4 ENSMUSG00000022491  514850.04       7.765956 0.4539439 17.96995  3.350319e-72
5 ENSMUSG00000061388   30555.67       8.282907 0.4740455 17.36398  1.546169e-67
6 ENSMUSG00000024903   60039.52       7.312158 0.4522371 16.27868  1.398616e-59
           padj  SYMBOL                                         GENENAME ENTREZID
1 2.785480e-112     Wap                              whey acidic protein    22373
2  1.096033e-82 Csn1s2a                           casein alpha s2-like A    12993
3  1.006672e-69    Pigr                polymeric immunoglobulin receptor    18703
4  1.447003e-68 Glycam1 glycosylation dependent cell adhesion molecule 1    14663
5  5.342324e-64 Csn1s2b                           casein alpha s2-like B    12992
6  4.027083e-56    Lao1                           L-amino acid oxidase 1   100470

An appealing feature of the method is that it does not require us to impose arbitrary cut-offs on the dataset to decide what is differentially-expressed or not. The steps in producing the input required for GSEA are i) retrieving the ranked statistics ii) naming each one according to Entrez ID.

library(dplyr)
gseaInput <- filter(results.annotated, !is.na(ENTREZID)) %>% 
  arrange(stat)
ranks <- pull(gseaInput,stat)
names(ranks) <- gseaInput$ENTREZID

The Walter+Eliza Hall Institutes Bioinformatics service have made mouse versions of the MSigDB datasets available for download. This should already be available in the Robjects folder.

load("Robjects/mouse_H_v5p2.rdata")
pathways <- Mm.H

The analysis is one call to the fgsea function. We can automatically exclude any pathways with too many or too few genes.

library(fgsea)
fgseaRes <- fgsea(pathways, ranks, minSize=15, maxSize = 500, nperm=1000)
There are duplicate gene names, fgsea may produce unexpected results
dim(fgseaRes)
[1] 50  8
#head(fgseaRes)

The results table gives the names of each pathway that was tested and the stats from doing the test. We can make this into a “tidy” object with the following code.

fgseaResTidy <- fgseaRes %>%
  as_tibble() %>%
  arrange(desc(NES))
# Show in a nice table:
fgseaResTidy 
library(ggplot2)
ggplot(fgseaResTidy, aes(reorder(pathway, NES), NES)) +
  geom_col(aes(fill=padj<0.05)) +
  coord_flip() +
  labs(x="Pathway", y="Normalized Enrichment Score",
       title="Hallmark pathways NES from GSEA")

The enrichment plot will show where the genes belonging to a particular gene set are towards the top or the bottom of the genelist, and how the enrichment score is calculated across the dataset.

Here we show the enrichment plot for the pathway with the most positive enrichment score.

plotEnrichment(pathways[["HALLMARK_OXIDATIVE_PHOSPHORYLATION"]],
               ranks)

Challenge 1

  1. What pathway has the most extreme negative enrichment score? How can you identify this pathway from the results table?
  2. Make the enrichment plot for this pathway

Visualising pathways using a heatmap

The names of genes involved in particular pathways can be extracted from the pathways object. We can then use these genes to make a heatmap, or other visualisation, to show how these genes partition the dataset.

### Load the dds object if not present
load("Robjects/preprocessing.Rdata")
Loading required package: DESeq2
Loading required package: S4Vectors
Loading required package: stats4
Loading required package: BiocGenerics
Loading required package: parallel

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:parallel’:

    clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport,
    clusterMap, parApply, parCapply, parLapply, parLapplyLB, parRapply, parSapply,
    parSapplyLB

The following objects are masked from ‘package:dplyr’:

    combine, intersect, setdiff, union

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, append, as.data.frame, basename, cbind, colnames, dirname,
    do.call, duplicated, eval, evalq, Filter, Find, get, grep, grepl, intersect,
    is.unsorted, lapply, Map, mapply, match, mget, order, paste, pmax, pmax.int,
    pmin, pmin.int, Position, rank, rbind, Reduce, rownames, sapply, setdiff, sort,
    table, tapply, union, unique, unsplit, which, which.max, which.min


Attaching package: ‘S4Vectors’

The following objects are masked from ‘package:dplyr’:

    first, rename

The following object is masked from ‘package:base’:

    expand.grid

Loading required package: IRanges

Attaching package: ‘IRanges’

The following objects are masked from ‘package:dplyr’:

    collapse, desc, slice

Loading required package: GenomicRanges
Loading required package: GenomeInfoDb
Loading required package: SummarizedExperiment
Loading required package: Biobase
Welcome to Bioconductor

    Vignettes contain introductory material; view with 'browseVignettes()'. To cite
    Bioconductor, see 'citation("Biobase")', and for packages 'citation("pkgname")'.

Loading required package: DelayedArray
Loading required package: matrixStats

Attaching package: ‘matrixStats’

The following objects are masked from ‘package:Biobase’:

    anyMissing, rowMedians

The following object is masked from ‘package:dplyr’:

    count

Loading required package: BiocParallel

Attaching package: ‘DelayedArray’

The following objects are masked from ‘package:matrixStats’:

    colMaxs, colMins, colRanges, rowMaxs, rowMins, rowRanges

The following objects are masked from ‘package:base’:

    aperm, apply, rowsum
library(pheatmap)
my_genes <- filter(results.annotated, ENTREZID %in% pathways[["HALLMARK_MYC_TARGETS_V2"]]) %>% 
  pull(ENSEMBL)
vsd <- vst(dds)
using 'avgTxLength' from assays(dds), correcting for library size
mat <- assay(vsd)[my_genes,]
mat <- mat - rowMeans(mat)
dim(mat)
[1] 65 12
rownames(sampleinfo) <- sampleinfo$run
pheatmap(mat,
         annotation_col = sampleinfo[,c("Status","CellType")])

Gene Set Testing - competitive gene set tests

GOseq analysis

GOseq is a method to conduct Gene Ontology (GO) analysis suitable for RNA-seq data as it accounts for the gene length bias in detection of over-representation (GOseq article)

From the GOseq vignette:

  • GOseq first needs to quantify the length bias present in the dataset under consideration.
  • This is done by calculating a Probability Weighting Function or PWF which can be thought of as a function which gives the probability that a gene will be differentially expressed (DE), based on its length alone.
  • The PWF is calculated by fitting a monotonic spline to the binary data series of differential expression (1=DE, 0=Not DE) as a function of gene length.
  • The PWF is used to weight the chance of selecting each gene when forming a null distribution for GO category membership.
  • The fact that the PWF is calculated directly from the dataset under consideration makes this approach robust, only correcting for the length bias present in the data.

“GO analysis of RNA-seq data requires the use of random sampling in order to generate a suitable null distribution for GO category membership and calculate each category’s significance for over representation amongst DE genes. … In most cases, the Wallenius distribution can be used to approximate the true null distribution, without any significant loss in accuracy. The goseq package implements this approximation as its default option.”

First, create list of DEGs. We don’t have to be too strict about our criteria for a gene to be differentially-expressed, as too few genes will not give us many enriched pathways.

genes <- results.annotated$padj < 0.05 & !is.na(results.annotated$padj)
names(genes) <- results.annotated$ENSEMBL

Fit the Probability Weighting Function (PWF):

library(goseq)
if(!require(TxDb.Mmusculus.UCSC.mm10.knownGene)) BiocManager::install("TxDb.Mmusculus.UCSC.mm10.knownGene")
#print(supportedGeneIDs())
#print(supportedGenomes())
pwf <- nullp(genes, "mm10","ensGene")
libraries ‘/usr/local/lib/R/site-library’, ‘/usr/lib/R/site-library’ contain no packagesargument 'pattern' has length > 1 and only the first element will be usedcollapsing to unique 'x' valuescollapsing to unique 'x' valuescollapsing to unique 'x' valuescollapsing to unique 'x' valuescollapsing to unique 'x' valuescollapsing to unique 'x' valuescollapsing to unique 'x' valuescollapsing to unique 'x' valuescollapsing to unique 'x' valuescollapsing to unique 'x' valuescollapsing to unique 'x' valuescollapsing to unique 'x' values

Conduct gene set enrichment analysis:

rr #?goseq goseq_res <- goseq(pwf, 10,,test.cats=:BP) r goseq_res

?goseq

Analysis with clusterProfiler

clusterProfiler is another Bioconductor package for over-representation analysis. It’s main advantage is that it provides some nice visualisation methods.

Firstly, we can identify over-represented GO terms and visualise these as a network.

library(clusterProfiler)
universe <- results.annotated %>% pull(ENTREZID)
sigGenes <- results.annotated %>% 
  filter(padj < 0.05, !is.na(ENTREZID)) %>% pull(ENTREZID)

enrich_go <- enrichGO(
  gene= sigGenes,
  OrgDb = org.Mm.eg.db,
  keyType = "ENTREZID",
  ont = "BP",
  universe = universe,
  qvalueCutoff = 0.05,
  readable=TRUE
)

rr dotplot(enrich_go)

A dot plot can show us the most enriched pathways, and the size of each.

dotplot(enrich_go)
emapplot(enrich_go)

We also perform enrichment of KEGG pathways with the clusterProfiler package. We could do this with goseq, but again clusterProfiler has some nice visualisations and uses online resources rather than relying on Bioconductor annotation.

To run the analysis we can use the same list of genes as before, and need to know the kegg code for our given organism.

search_kegg_organism('mouse', by='common_name')
keg_res <- enrichKEGG(gene=sigGenes, organism="mmu")
head(keg_res,n=10)

If we want to view the network for any pathway, we can use the browseKEGG function which will take us to the relevant page on the KEGG website.

We will choose an example of a smaller pathway to make things a bit clearer.

browseKEGG(keg_res, 'mmu03320')

The pathview package will produce a version of this plot (png) in your working directory.

library(pathview)
logFC <- results.annotated$log2FoldChange
names(logFC) <- results.annotated$ENTREZID

pathview(gene.data = logFC, 
         pathway.id = "mmu03320", 
         species = "mmu", 
         limit = list(gene=5, cpd=1))

Creating Gene lists to use with an online tool

There are also many online tools that one could use to perform a gene set or ontology analysis.

The tools generally require your input genes lists to be uploaded as a simple text file. In this final challenge, we will create some files that you might use in one of these tools.

A file containing names of background genes

This file has one column which lists all the gene names present in the analysis. Gene Symbols are commonly used, although a tool may accept Ensembl or Refseq names

A file containing names of significant genes

This file has one column which lists the genes that passed the threshold for statistical significance (e.g. p-value less than 0.05) in your analysis. Gene Symbols are commonly used, although a tool may accept Ensembl or Refseq names

Challenge

Create two text files that can be imported into online tools for further analysis 1. A list of background genes 2. A list of differentially expressed genes 3. Load these files into GOrilla for analysis HINT: the write.table function is able to write a data frame to a txt file in R. You will need to set the appropriate arguments to make sure that a text file with only one column is created.

LS0tCnRpdGxlOiAiUk5BLXNlcSBhbmFseXNpcyBpbiBSIgphdXRob3I6ICJTdGVwaGFuZSBCYWxsZXJlYXUsIE1hcmsgRHVubmluZywgT3NjYXIgUnVlZGEsIEFzaGxleSBTYXdsZSIKZGF0ZTogIkZlYnJ1YXJ5IDIwMjAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwptaW51dGVzOiAzMDAKbGF5b3V0OiBwYWdlCnN1YnRpdGxlOiBHZW5lIFNldCBUZXN0aW5nIGZvciBSTkEtc2VxCgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKKipPcmlnaW5hbCBBdXRob3JzOiBCZWxpbmRhIFBoaXBzb24sIEFubmEgVHJpZ29zLCBNYXR0IFJpdGNoaWUsIE1hcmlhIERveWxlLCBIYXJyaWV0IERhc2hub3csIENoYXJpdHkgTGF3KiosICoqU3RlcGhhbmUgQmFsbGVyZWF1LCBPc2NhciBSdWVkYSwgQXNobGV5IFNhd2xlKioKQmFzZWQgb24gdGhlIGNvdXJzZSBbUk5Bc2VxIGFuYWx5c2lzIGluIFJdKGh0dHA6Ly9jb21iaW5lLWF1c3RyYWxpYS5naXRodWIuaW8vMjAxNi0wNS0xMS1STkFzZXEvKSBkZWxpdmVyZWQgb24gTWF5IDExLzEydGggMjAxNiBhbmQgbW9kaWZpZWQgYnkgQ2FuY2VyIFJlc2VhcmNoIFVrIENhbWJyaWRnZSBDZW50cmUgZm9yIHRoZSBbRnVuY3Rpb25hbCBHZW5vbWljcyBBdXR1bW4gU2Nob29sIDIwMTddKGh0dHBzOi8vYmlvaW5mb3JtYXRpY3MtY29yZS1zaGFyZWQtdHJhaW5pbmcuZ2l0aHViLmlvL2NydWstYXV0dW1uLXNjaG9vbC0yMDE3LykKCiMjIFJlc291cmNlcyBhbmQgZGF0YSBmaWxlcwoKVGhpcyBtYXRlcmlhbCBoYXMgYmVlbiBjcmVhdGVkIHVzaW5nIHRoZSBmb2xsb3dpbmcgcmVzb3VyY2VzOiAgCmh0dHA6Ly93d3cuc3RhdHNjaS5vcmcvc215dGgvcHVicy9RTGVkZ2VSUHJlcHJpbnQucGRmIFtATHVuMjAxNl0gIApodHRwOi8vbW9uYXNoYmlvaW5mb3JtYXRpY3NwbGF0Zm9ybS5naXRodWIuaW8vUk5Bc2VxLURFLWFuYWx5c2lzLXdpdGgtUi85OS1STkFzZXFfREVfYW5hbHlzaXNfd2l0aF9SLmh0bWwgIApodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwKaHR0cHM6Ly9iaW9jb25kdWN0b3IuZ2l0aHViLmlvL0Jpb2NXb3Jrc2hvcHMvcm5hLXNlcS1kYXRhLWFuYWx5c2lzLXdpdGgtZGVzZXEyLmh0bWwKClRoaXMgc2VjdGlvbiBhbHNvIHVzZXMgY29kZSBmcm9tIFN0ZXBoZW4gVHVybmVyJ3MgZ3VpZGUgdG8gZmdzZWEgaHR0cHM6Ly9zdGVwaGVudHVybmVyLmdpdGh1Yi5pby9kZXNlcS10by1mZ3NlYS8KCiMgR2VuZSBTZXQgVGVzdGluZwoKSW4gdGhlIGVhcmx5IGRheXMgb2YgbWljcm9hcnJheSBhbmFseXNpcywgcGVvcGxlIHdlcmUgaGFwcHkgaWYgdGhleSBnb3QgYSBoYW5kZnVsIG9mIGRpZmZlcmVudGlhbGx5LWV4cHJlc3NlZCBnZW5lcyB0aGF0IHRoZXkgY291bGQgdmFsaWRhdGUgb3IgZm9sbG93LXVwLiBIb3dldmVyLCB3aXRoIGxhdGVyIHRlY2hub2xvZ2llcyAoYW5kIGRlcGVuZGluZyBvbiB0aGUgZXhwZXJpbWVudGFsIHNldHVwKSB3ZSBtaWdodCBoYXZlIHRob3VzYW5kcyBvZiBzdGF0aXN0aWNhbGx5LXNpZ25pZmljYW50IHJlc3VsdHMsIHdoaWNoIG5vLW9uZSBoYXMgdGhlIHRpbWUgdG8gZm9sbG93LXVwLiBBbHNvLCB3ZSBtaWdodCBiZSBpbnRlcmVzdGVkIGluIHBhdGh3YXlzIC8gbWVjaGFuaXNtcyB0aGF0IGFyZSBhbHRlcmVkIGFuZCBub3QganVzdCBpbmRpdmlkdWFsIGdlbmVzLgoKSW4gdGhpcyBzZWN0aW9uIHdlIG1vdmUgdG93YXJkcyBkaXNjb3ZlcmluZyBpZiBvdXIgcmVzdWx0cyBhcmUgKioqYmlvbG9naWNhbGx5IHNpZ25pZmljYW50KioqLiBBcmUgdGhlIGdlbmVzIHRoYXQgd2UgaGF2ZSBwaWNrZWQgc3RhdGlzdGljYWwgZmx1a2VzLCBvciBhcmUgdGhlcmUgc29tZSBjb21tb25hbGl0aWVzLiAKClRoZXJlIGFyZSB0d28gZGlmZmVyZW50IGFwcHJvYWNoZXMgb25lIG1pZ2h0IHVzZSwgYW5kIHdlIHdpbGwgY292ZXIgdGhlIHRoZW9yeSBiZWhpbmQgYm90aAoKLSBUaGVyZSBpcyBhbHNvIGEgYnVuY2ggb2Ygd2Vic2l0ZXMgZm9yIGRvaW5nIHRoZSB0ZXN0cwogICAgKyB3ZSB3aWxsIHNob3cgaG93IHRoZXkgYXJlIGRvbmUgaW4gQmlvY29uZHVjdG9yIHNvIHRoZSB0aGVvcnkgaXMgY2xlYXIKLSBXZSB3aWxsIGFzc3VtZSB3ZSBoYXZlIGRvbmUgYSBkaWZmZXJlbnRpYWwtZXhwcmVzc2lvbiBhbmFseXNpcywgYnV0IHRoZSBzYW1lIHRlY2huaXF1ZXMgY2FuIGJlIHVzZWQgZm9yIG90aGVyIHNpdHVhdGlvbnMgd2hlbiB3ZSBoYXZlIGEgZ2VuZSBsaXN0CiAgICArIENoSVAtc2VxCiAgICArIFJOQS1zZXEKICAgIAoKIyMjIGZnc2VhIGFuYWx5c2lzCgpUaGUgZmdzZWEgcGFja2FnZSBpcyBhIGZyZWUgaW1wbGVtZW50YXRpb24gb2YgdGhlIEJyb2FkJ3MgR1NFQSBzb2Z0d2FyZSBhbmQgaXMgZGVzY3JpYmVkIGluIG1vcmUgZGV0YWlsIGluIHRoZSBwYWNrYWdlIFt2aWduZXR0ZV0oaHR0cDovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvZmdzZWEvaW5zdC9kb2MvZmdzZWEtdHV0b3JpYWwuaHRtbCkgImZhc3QgcHJlcmFua2VkIGdlbmUgc2V0IGVucmljaG1lbnQgYW5hbHlzaXMgKEdTRUEpIjoKClRoZSBHU0VBIGFuYWx5c2lzIGlzIHBlcmZvcm1lZCBieToKCi0gKGkpIHJhbmtpbmcgYWxsIGdlbmVzIGluIHRoZSBkYXRhIHNldCBiYXNlZCBvbiB0aGVpciBjb3JyZWxhdGlvbiB0byB0aGUgY2hvc2VuIHBoZW5vdHlwZSwKLSAoaWkpIGlkZW50aWZ5aW5nIHRoZSByYW5rIHBvc2l0aW9ucyBvZiBhbGwgbWVtYmVycyBvZiB0aGUgZ2VuZSBzZXQsIGFuZCAKLSAoaWlpKSBjYWxjdWxhdGluZyBhbiBlbnJpY2htZW50IHNjb3JlIChFUykgdGhhdCByZXByZXNlbnRzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG9ic2VydmVkIHJhbmtpbmdzIGFuZCB0aGF0IHdoaWNoIHdvdWxkIGJlIGV4cGVjdGVkIGFzc3VtaW5nIGEgcmFuZG9tIHJhbmsgZGlzdHJpYnV0aW9uLgoKPiAiQWZ0ZXIgZXN0YWJsaXNoaW5nIHRoZSBFUyBmb3IgZWFjaCBnZW5lIHNldCBhY3Jvc3MgdGhlIHBoZW5vdHlwZSwgR1NFQSByZWl0ZXJhdGl2ZWx5IHJhbmRvbWl6ZXMgdGhlIHNhbXBsZSBsYWJlbHMgYW5kIHJldGVzdHMgZm9yIGVucmljaG1lbnQgYWNyb3NzIHRoZSByYW5kb20gY2xhc3Nlcy4gQnkgcGVyZm9ybWluZyByZXBlYXRlZCBjbGFzcyBsYWJlbCByYW5kb21pemF0aW9ucywgdGhlIEVTIGZvciBlYWNoIGdlbmUgc2V0IGFjcm9zcyB0aGUgdHJ1ZSBjbGFzc2VzIGNhbiBiZSBjb21wYXJlZCB0byB0aGUgRVMgZGlzdHJpYnV0aW9uIGZyb20gdGhlIHJhbmRvbSBjbGFzc2VzLiBUaG9zZSBnZW5lIHNldHMgdGhhdCBzaWduaWZpY2FudGx5IG91dHBlcmZvcm0gaXRlcmF0aXZlIHJhbmRvbSBjbGFzcyBwZXJtdXRhdGlvbnMgYXJlIGNvbnNpZGVyZWQgc2lnbmlmaWNhbnQuIiAKClRoZSBhcnRpY2xlIGRlc2NyaWJpbmcgdGhlIG9yaWdpbmFsIHNvZnR3YXJlIGlzIGF2YWlsYWJsZSBbaGVyZV0oaHR0cDovL3d3dy5wbmFzLm9yZy9jb250ZW50LzEwMi80My8xNTU0NS5sb25nKSBhbmQgdGhlcmUgaXMgYWxzbyBhIFtjb21tZW50YXJ5IG9uIEdTRUFdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzEyNjYxMzEvKS4gCgpJbiBhZGRpdGlvbiB0byB0aGUgR1NFQSBzb2Z0d2FyZSB0aGUgQnJvYWQgYWxzbyBwcm92aWRlIGEgbnVtYmVyIG9mIHZlcnkgd2VsbCBjdXJhdGVkIGdlbmUgc2V0cyBmb3IgdGVzdGluZyBhZ2FpbnN0IHlvdXIgZGF0YSAtIHRoZSBNb2xlY3VsYXIgU2lnbmF0dXJlcyBEYXRhYmFzZSAoTVNpZ0RCKS4gVW5mb3J0dW5hdGVseSwgdGhlc2UgYXJlIGNvbGxlY3Rpb25zIG9mIGh1bWFuIGdlbmVzLiBIb3dldmVyLCB0aGVzZSBsaXN0cyBoYXZlIGJlZW4gdHJhbnNsYXRlZCB0byBtb3VzZSBlcXVpdmFsZW50cyBieSB0aGUgV2FsdGVyK0VsaXphIEhhbGwgSW5zdGl0dXRlcyBCaW9pbmZvcm1hdGljcyBzZXJ2aWNlIGFuZCBtYWRlIGF2YWlhbGJsZSBmb3IgZG93bmxvYWQuIFRoZXNlIGdlbmUgc2V0cyB1c2UgKkVudHJleiBJRCogYXMgdGhlaXIgaWRlbnRpZmllci4KCmBgYHtyfQpsaWJyYXJ5KGZnc2VhKQpgYGAKCgpgYGB7cn0KbG9hZCgiUm9iamVjdHMvTHZzVl9hbm5vdGF0ZWQucmRhdGEiKQpoZWFkKHJlc3VsdHMuYW5ub3RhdGVkKQpgYGAKCkFuIGFwcGVhbGluZyBmZWF0dXJlIG9mIHRoZSBtZXRob2QgaXMgdGhhdCBpdCBkb2VzIG5vdCByZXF1aXJlIHVzIHRvIGltcG9zZSBhcmJpdHJhcnkgY3V0LW9mZnMgb24gdGhlIGRhdGFzZXQgdG8gZGVjaWRlIHdoYXQgaXMgZGlmZmVyZW50aWFsbHktZXhwcmVzc2VkIG9yIG5vdC4gVGhlIHN0ZXBzIGluIHByb2R1Y2luZyB0aGUgaW5wdXQgcmVxdWlyZWQgZm9yIEdTRUEgYXJlIGkpIHJldHJpZXZpbmcgdGhlIHJhbmtlZCBzdGF0aXN0aWNzIGlpKSBuYW1pbmcgZWFjaCBvbmUgYWNjb3JkaW5nIHRvIEVudHJleiBJRC4KCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpnc2VhSW5wdXQgPC0gZmlsdGVyKHJlc3VsdHMuYW5ub3RhdGVkLCAhaXMubmEoRU5UUkVaSUQpKSAlPiUgCiAgYXJyYW5nZShzdGF0KQpyYW5rcyA8LSBwdWxsKGdzZWFJbnB1dCxzdGF0KQpuYW1lcyhyYW5rcykgPC0gZ3NlYUlucHV0JEVOVFJFWklECmBgYAoKClRoZSBXYWx0ZXIrRWxpemEgSGFsbCBJbnN0aXR1dGVzIEJpb2luZm9ybWF0aWNzIHNlcnZpY2UgaGF2ZSBtYWRlIG1vdXNlIHZlcnNpb25zIG9mIHRoZSBNU2lnREIgZGF0YXNldHMgYXZhaWxhYmxlIGZvciBkb3dubG9hZC4gVGhpcyBzaG91bGQgYWxyZWFkeSBiZSBhdmFpbGFibGUgaW4gdGhlIGBSb2JqZWN0c2AgZm9sZGVyLgoKYGBge3J9CmxvYWQoIlJvYmplY3RzL21vdXNlX0hfdjVwMi5yZGF0YSIpCnBhdGh3YXlzIDwtIE1tLkgKYGBgCgpUaGUgYW5hbHlzaXMgaXMgb25lIGNhbGwgdG8gdGhlIGBmZ3NlYWAgZnVuY3Rpb24uIFdlIGNhbiBhdXRvbWF0aWNhbGx5IGV4Y2x1ZGUgYW55IHBhdGh3YXlzIHdpdGggdG9vIG1hbnkgb3IgdG9vIGZldyBnZW5lcy4KCmBgYHtyfQpsaWJyYXJ5KGZnc2VhKQpmZ3NlYVJlcyA8LSBmZ3NlYShwYXRod2F5cywgcmFua3MsIG1pblNpemU9MTUsIG1heFNpemUgPSA1MDAsIG5wZXJtPTEwMDApCmRpbShmZ3NlYVJlcykKI2hlYWQoZmdzZWFSZXMpCmBgYAoKVGhlIHJlc3VsdHMgdGFibGUgZ2l2ZXMgdGhlIG5hbWVzIG9mIGVhY2ggcGF0aHdheSB0aGF0IHdhcyB0ZXN0ZWQgYW5kIHRoZSBzdGF0cyBmcm9tIGRvaW5nIHRoZSB0ZXN0LiBXZSBjYW4gbWFrZSB0aGlzIGludG8gYSAidGlkeSIgb2JqZWN0IHdpdGggdGhlIGZvbGxvd2luZyBjb2RlLgoKYGBge3J9CmZnc2VhUmVzVGlkeSA8LSBmZ3NlYVJlcyAlPiUKICBhc190aWJibGUoKSAlPiUKICBhcnJhbmdlKGRlc2MoTkVTKSkKCiMgU2hvdyBpbiBhIG5pY2UgdGFibGU6CmZnc2VhUmVzVGlkeSAKYGBgCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZmdzZWFSZXNUaWR5LCBhZXMocmVvcmRlcihwYXRod2F5LCBORVMpLCBORVMpKSArCiAgZ2VvbV9jb2woYWVzKGZpbGw9cGFkajwwLjA1KSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh4PSJQYXRod2F5IiwgeT0iTm9ybWFsaXplZCBFbnJpY2htZW50IFNjb3JlIiwKICAgICAgIHRpdGxlPSJIYWxsbWFyayBwYXRod2F5cyBORVMgZnJvbSBHU0VBIikKYGBgCgoKClRoZSBlbnJpY2htZW50IHBsb3Qgd2lsbCBzaG93IHdoZXJlIHRoZSBnZW5lcyBiZWxvbmdpbmcgdG8gYSBwYXJ0aWN1bGFyIGdlbmUgc2V0IGFyZSB0b3dhcmRzIHRoZSB0b3Agb3IgdGhlIGJvdHRvbSBvZiB0aGUgZ2VuZWxpc3QsIGFuZCBob3cgdGhlICplbnJpY2htZW50IHNjb3JlKiBpcyBjYWxjdWxhdGVkIGFjcm9zcyB0aGUgZGF0YXNldC4KCkhlcmUgd2Ugc2hvdyB0aGUgZW5yaWNobWVudCBwbG90IGZvciB0aGUgcGF0aHdheSB3aXRoIHRoZSBtb3N0IHBvc2l0aXZlIGVucmljaG1lbnQgc2NvcmUuCgpgYGB7cn0KcGxvdEVucmljaG1lbnQocGF0aHdheXNbWyJIQUxMTUFSS19PWElEQVRJVkVfUEhPU1BIT1JZTEFUSU9OIl1dLAogICAgICAgICAgICAgICByYW5rcykKYGBgCgo+ICMjIENoYWxsZW5nZSAxey5jaGFsbGVuZ2V9Cj4gMS4gV2hhdCBwYXRod2F5IGhhcyB0aGUgbW9zdCBleHRyZW1lIG5lZ2F0aXZlIGVucmljaG1lbnQgc2NvcmU/IEhvdyBjYW4geW91IGlkZW50aWZ5IHRoaXMgcGF0aHdheSBmcm9tIHRoZSByZXN1bHRzIHRhYmxlPwo+IDIuIE1ha2UgdGhlIGVucmljaG1lbnQgcGxvdCBmb3IgdGhpcyBwYXRod2F5CgojIyMgVmlzdWFsaXNpbmcgcGF0aHdheXMgdXNpbmcgYSBoZWF0bWFwCgpUaGUgbmFtZXMgb2YgZ2VuZXMgaW52b2x2ZWQgaW4gcGFydGljdWxhciBwYXRod2F5cyBjYW4gYmUgZXh0cmFjdGVkIGZyb20gdGhlIGBwYXRod2F5c2Agb2JqZWN0LiBXZSBjYW4gdGhlbiB1c2UgdGhlc2UgZ2VuZXMgdG8gbWFrZSBhIGhlYXRtYXAsIG9yIG90aGVyIHZpc3VhbGlzYXRpb24sIHRvIHNob3cgaG93IHRoZXNlIGdlbmVzIHBhcnRpdGlvbiB0aGUgZGF0YXNldC4KCmBgYHtyfQojIyMgTG9hZCB0aGUgZGRzIG9iamVjdCBpZiBub3QgcHJlc2VudApsb2FkKCJSb2JqZWN0cy9wcmVwcm9jZXNzaW5nLlJkYXRhIikKbGlicmFyeShwaGVhdG1hcCkKbXlfZ2VuZXMgPC0gZmlsdGVyKHJlc3VsdHMuYW5ub3RhdGVkLCBFTlRSRVpJRCAlaW4lIHBhdGh3YXlzW1siSEFMTE1BUktfTVlDX1RBUkdFVFNfVjIiXV0pICU+JSAKICBwdWxsKEVOU0VNQkwpCgp2c2QgPC0gdnN0KGRkcykKbWF0IDwtIGFzc2F5KHZzZClbbXlfZ2VuZXMsXQptYXQgPC0gbWF0IC0gcm93TWVhbnMobWF0KQpkaW0obWF0KQpyb3duYW1lcyhzYW1wbGVpbmZvKSA8LSBzYW1wbGVpbmZvJHJ1bgoKcGhlYXRtYXAobWF0LAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHNhbXBsZWluZm9bLGMoIlN0YXR1cyIsIkNlbGxUeXBlIildKQpgYGAKCiMjIEdlbmUgU2V0IFRlc3RpbmcgLSBjb21wZXRpdGl2ZSBnZW5lIHNldCB0ZXN0cwoKIyMjIEdPc2VxIGFuYWx5c2lzCgpHT3NlcSBpcyBhIG1ldGhvZCB0byBjb25kdWN0IEdlbmUgT250b2xvZ3kgKEdPKSBhbmFseXNpcyBzdWl0YWJsZSBmb3IgUk5BLXNlcSBkYXRhIGFzIGl0IGFjY291bnRzIGZvciB0aGUgZ2VuZSBsZW5ndGggYmlhcyBpbiBkZXRlY3Rpb24gb2Ygb3Zlci1yZXByZXNlbnRhdGlvbiAoW0dPc2VxIGFydGljbGVdKGh0dHBzOi8vZ2Vub21lYmlvbG9neS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L2diLTIwMTAtMTEtMi1yMTQpKQoKRnJvbSB0aGUgW0dPc2VxIHZpZ25ldHRlXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvZ29zZXEvaW5zdC9kb2MvZ29zZXEucGRmKToKCi0gR09zZXEgZmlyc3QgbmVlZHMgdG8gcXVhbnRpZnkgdGhlIGxlbmd0aCBiaWFzIHByZXNlbnQgaW4gdGhlIGRhdGFzZXQgdW5kZXIgY29uc2lkZXJhdGlvbi4KLSBUaGlzIGlzIGRvbmUgYnkgY2FsY3VsYXRpbmcgYSBQcm9iYWJpbGl0eSBXZWlnaHRpbmcgRnVuY3Rpb24gb3IgUFdGIHdoaWNoIGNhbiBiZSB0aG91Z2h0IG9mIGFzIGEgZnVuY3Rpb24gd2hpY2ggZ2l2ZXMgdGhlIHByb2JhYmlsaXR5IHRoYXQgYSBnZW5lIHdpbGwgYmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIChERSksIGJhc2VkIG9uIGl0cyBsZW5ndGggYWxvbmUuCi0gVGhlIFBXRiBpcyBjYWxjdWxhdGVkIGJ5IGZpdHRpbmcgYSBtb25vdG9uaWMgc3BsaW5lIHRvIHRoZSBiaW5hcnkgZGF0YSBzZXJpZXMgb2YgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gKDE9REUsIDA9Tm90IERFKSBhcyBhIGZ1bmN0aW9uIG9mIGdlbmUgbGVuZ3RoLgotIFRoZSBQV0YgaXMgdXNlZCB0byB3ZWlnaHQgdGhlIGNoYW5jZSBvZiBzZWxlY3RpbmcgZWFjaCBnZW5lIHdoZW4gZm9ybWluZyBhIG51bGwgZGlzdHJpYnV0aW9uIGZvciBHTyBjYXRlZ29yeSBtZW1iZXJzaGlwLgotIFRoZSBmYWN0IHRoYXQgdGhlIFBXRiBpcyBjYWxjdWxhdGVkIGRpcmVjdGx5IGZyb20gdGhlIGRhdGFzZXQgdW5kZXIgY29uc2lkZXJhdGlvbiBtYWtlcyB0aGlzIGFwcHJvYWNoIHJvYnVzdCwgb25seSBjb3JyZWN0aW5nIGZvciB0aGUgbGVuZ3RoIGJpYXMgcHJlc2VudCBpbiB0aGUgZGF0YS4KCiJHTyBhbmFseXNpcyBvZiBSTkEtc2VxIGRhdGEgcmVxdWlyZXMgdGhlIHVzZSBvZiByYW5kb20gc2FtcGxpbmcgaW4gb3JkZXIgdG8gZ2VuZXJhdGUgYSBzdWl0YWJsZSBudWxsIGRpc3RyaWJ1dGlvbiBmb3IgR08gY2F0ZWdvcnkgbWVtYmVyc2hpcCBhbmQgY2FsY3VsYXRlIGVhY2ggY2F0ZWdvcnkncyBzaWduaWZpY2FuY2UgZm9yIG92ZXIgcmVwcmVzZW50YXRpb24gYW1vbmdzdCBERSBnZW5lcy4gLi4uIEluICBtb3N0ICBjYXNlcywgIHRoZSAgV2FsbGVuaXVzIGRpc3RyaWJ1dGlvbiBjYW4gYmUgdXNlZCB0byBhcHByb3hpbWF0ZSB0aGUgdHJ1ZSBudWxsIGRpc3RyaWJ1dGlvbiwgd2l0aG91dCBhbnkgc2lnbmlmaWNhbnQgbG9zcyBpbiBhY2N1cmFjeS4gVGhlIGdvc2VxIHBhY2thZ2UgaW1wbGVtZW50cyB0aGlzIGFwcHJveGltYXRpb24gYXMgaXRzIGRlZmF1bHQgb3B0aW9uLiIKCkZpcnN0LCBjcmVhdGUgbGlzdCBvZiBERUdzLiBXZSBkb24ndCBoYXZlIHRvIGJlIHRvbyBzdHJpY3QgYWJvdXQgb3VyIGNyaXRlcmlhIGZvciBhIGdlbmUgdG8gYmUgZGlmZmVyZW50aWFsbHktZXhwcmVzc2VkLCBhcyB0b28gZmV3IGdlbmVzIHdpbGwgbm90IGdpdmUgdXMgbWFueSBlbnJpY2hlZCBwYXRod2F5cy4KCmBgYHtyfQpnZW5lcyA8LSByZXN1bHRzLmFubm90YXRlZCRwYWRqIDwgMC4wNSAmICFpcy5uYShyZXN1bHRzLmFubm90YXRlZCRwYWRqKQpuYW1lcyhnZW5lcykgPC0gcmVzdWx0cy5hbm5vdGF0ZWQkRU5TRU1CTApgYGAKCgpGaXQgdGhlIFByb2JhYmlsaXR5IFdlaWdodGluZyBGdW5jdGlvbiAoUFdGKToKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZ29zZXEpCmlmKCFyZXF1aXJlKFR4RGIuTW11c2N1bHVzLlVDU0MubW0xMC5rbm93bkdlbmUpKSBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiVHhEYi5NbXVzY3VsdXMuVUNTQy5tbTEwLmtub3duR2VuZSIpCiNwcmludChzdXBwb3J0ZWRHZW5lSURzKCkpCiNwcmludChzdXBwb3J0ZWRHZW5vbWVzKCkpCnB3ZiA8LSBudWxscChnZW5lcywgIm1tMTAiLCJlbnNHZW5lIikKYGBgCgpDb25kdWN0IGdlbmUgc2V0IGVucmljaG1lbnQgYW5hbHlzaXM6CgpgYGB7ciByZXN1bHRzPSJoaWRlIn0KIz9nb3NlcQpnb3NlcV9yZXMgPC0gZ29zZXEocHdmLCAibW0xMCIsImVuc0dlbmUiLHRlc3QuY2F0cz0iR086QlAiKQpoZWFkKGdvc2VxX3JlcykKYGBgCgpgYGB7cn0KP2dvc2VxCgpgYGAKCgojIyBBbmFseXNpcyB3aXRoIGNsdXN0ZXJQcm9maWxlcgoKYGNsdXN0ZXJQcm9maWxlcmAgaXMgYW5vdGhlciBCaW9jb25kdWN0b3IgcGFja2FnZSBmb3Igb3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcy4gSXQncyBtYWluIGFkdmFudGFnZSBpcyB0aGF0IGl0IHByb3ZpZGVzIHNvbWUgbmljZSB2aXN1YWxpc2F0aW9uIG1ldGhvZHMuCgpGaXJzdGx5LCB3ZSBjYW4gaWRlbnRpZnkgb3Zlci1yZXByZXNlbnRlZCBHTyB0ZXJtcyBhbmQgdmlzdWFsaXNlIHRoZXNlIGFzIGEgbmV0d29yay4KCmBgYHtyfQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKdW5pdmVyc2UgPC0gcmVzdWx0cy5hbm5vdGF0ZWQgJT4lIHB1bGwoRU5UUkVaSUQpCnNpZ0dlbmVzIDwtIHJlc3VsdHMuYW5ub3RhdGVkICU+JSAKICBmaWx0ZXIocGFkaiA8IDAuMDUsICFpcy5uYShFTlRSRVpJRCkpICU+JSBwdWxsKEVOVFJFWklEKQoKZW5yaWNoX2dvIDwtIGVucmljaEdPKAogIGdlbmU9IHNpZ0dlbmVzLAogIE9yZ0RiID0gb3JnLk1tLmVnLmRiLAogIGtleVR5cGUgPSAiRU5UUkVaSUQiLAogIG9udCA9ICJCUCIsCiAgdW5pdmVyc2UgPSB1bml2ZXJzZSwKICBxdmFsdWVDdXRvZmYgPSAwLjA1LAogIHJlYWRhYmxlPVRSVUUKKQoKYGBgCgpgYGB7cn0KZW5yaWNoX2dvX3RpZHkgPC0gZW5yaWNoX2dvICU+JSAKICBzbG90KCJyZXN1bHQiKSAlPiUgCiAgdGliYmxlOjphcy50aWJibGUoKSAKZW5yaWNoX2dvX3RpZHkKYGBgCgpBIGRvdCBwbG90IGNhbiBzaG93IHVzIHRoZSBtb3N0IGVucmljaGVkIHBhdGh3YXlzLCBhbmQgdGhlIHNpemUgb2YgZWFjaC4KCmBgYHtyfQpkb3RwbG90KGVucmljaF9nbykKYGBgCgpgYGB7cn0KZW1hcHBsb3QoZW5yaWNoX2dvKQpgYGAKCldlIGFsc28gcGVyZm9ybSBlbnJpY2htZW50IG9mIEtFR0cgcGF0aHdheXMgd2l0aCB0aGUgYGNsdXN0ZXJQcm9maWxlcmAgcGFja2FnZS4gV2UgY291bGQgZG8gdGhpcyB3aXRoIGBnb3NlcWAsIGJ1dCBhZ2FpbiBgY2x1c3RlclByb2ZpbGVyYCBoYXMgc29tZSBuaWNlIHZpc3VhbGlzYXRpb25zIGFuZCB1c2VzIG9ubGluZSByZXNvdXJjZXMgcmF0aGVyIHRoYW4gcmVseWluZyBvbiBCaW9jb25kdWN0b3IgYW5ub3RhdGlvbi4KClRvIHJ1biB0aGUgYW5hbHlzaXMgd2UgY2FuIHVzZSB0aGUgc2FtZSBsaXN0IG9mIGdlbmVzIGFzIGJlZm9yZSwgYW5kIG5lZWQgdG8ga25vdyB0aGUga2VnZyBjb2RlIGZvciBvdXIgZ2l2ZW4gb3JnYW5pc20uCgpgYGB7cn0Kc2VhcmNoX2tlZ2dfb3JnYW5pc20oJ21vdXNlJywgYnk9J2NvbW1vbl9uYW1lJykKa2VnX3JlcyA8LSBlbnJpY2hLRUdHKGdlbmU9c2lnR2VuZXMsIG9yZ2FuaXNtPSJtbXUiKQpgYGAKCmBgYHtyfQpoZWFkKGtlZ19yZXMsbj0xMCkKYGBgCgpJZiB3ZSB3YW50IHRvIHZpZXcgdGhlIG5ldHdvcmsgZm9yIGFueSBwYXRod2F5LCB3ZSBjYW4gdXNlIHRoZSBgYnJvd3NlS0VHR2AgZnVuY3Rpb24gd2hpY2ggd2lsbCB0YWtlIHVzIHRvIHRoZSByZWxldmFudCBwYWdlIG9uIHRoZSBLRUdHIHdlYnNpdGUuCgpXZSB3aWxsIGNob29zZSBhbiBleGFtcGxlIG9mIGEgc21hbGxlciBwYXRod2F5IHRvIG1ha2UgdGhpbmdzIGEgYml0IGNsZWFyZXIuCgpgYGB7cn0KYnJvd3NlS0VHRyhrZWdfcmVzLCAnbW11MDMzMjAnKQpgYGAKClRoZSBgcGF0aHZpZXdgIHBhY2thZ2Ugd2lsbCBwcm9kdWNlIGEgdmVyc2lvbiBvZiB0aGlzIHBsb3QgKGBwbmdgKSBpbiB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LgoKYGBge3J9CmxpYnJhcnkocGF0aHZpZXcpCmxvZ0ZDIDwtIHJlc3VsdHMuYW5ub3RhdGVkJGxvZzJGb2xkQ2hhbmdlCm5hbWVzKGxvZ0ZDKSA8LSByZXN1bHRzLmFubm90YXRlZCRFTlRSRVpJRAoKcGF0aHZpZXcoZ2VuZS5kYXRhID0gbG9nRkMsIAogICAgICAgICBwYXRod2F5LmlkID0gIm1tdTAzMzIwIiwgCiAgICAgICAgIHNwZWNpZXMgPSAibW11IiwgCiAgICAgICAgIGxpbWl0ID0gbGlzdChnZW5lPTUsIGNwZD0xKSkKYGBgCgoKIyMgQ3JlYXRpbmcgR2VuZSBsaXN0cyB0byB1c2Ugd2l0aCBhbiBvbmxpbmUgdG9vbAoKVGhlcmUgYXJlIGFsc28gbWFueSBvbmxpbmUgdG9vbHMgdGhhdCBvbmUgY291bGQgdXNlIHRvIHBlcmZvcm0gYSBnZW5lIHNldCBvciBvbnRvbG9neSBhbmFseXNpcy4gCgotIFtEQVZJRF0oaHR0cHM6Ly9kYXZpZC5uY2lmY3JmLmdvdi8pCi0gW0dlbmVUcmFpbF0oaHR0cHM6Ly9nZW5ldHJhaWwyLmJpb2luZi51bmktc2IuZGUvKQotIFtHT1JpbGxhXShodHRwOi8vY2JsLWdvcmlsbGEuY3MudGVjaG5pb24uYWMuaWwvKQoKVGhlIHRvb2xzIGdlbmVyYWxseSByZXF1aXJlIHlvdXIgaW5wdXQgZ2VuZXMgbGlzdHMgdG8gYmUgdXBsb2FkZWQgYXMgYSBzaW1wbGUgdGV4dCBmaWxlLiBJbiB0aGlzIGZpbmFsIGNoYWxsZW5nZSwgd2Ugd2lsbCBjcmVhdGUgc29tZSBmaWxlcyB0aGF0IHlvdSBtaWdodCB1c2UgaW4gb25lIG9mIHRoZXNlIHRvb2xzLgoKIyMjIEEgZmlsZSBjb250YWluaW5nIG5hbWVzIG9mIGJhY2tncm91bmQgZ2VuZXMKClRoaXMgZmlsZSBoYXMgb25lIGNvbHVtbiB3aGljaCBsaXN0cyAqKmFsbCB0aGUgZ2VuZSBuYW1lcyoqIHByZXNlbnQgaW4gdGhlIGFuYWx5c2lzLiBHZW5lIFN5bWJvbHMgYXJlIGNvbW1vbmx5IHVzZWQsIGFsdGhvdWdoIGEgdG9vbCBtYXkgYWNjZXB0IEVuc2VtYmwgb3IgUmVmc2VxIG5hbWVzCgojIyMgQSBmaWxlIGNvbnRhaW5pbmcgbmFtZXMgb2Ygc2lnbmlmaWNhbnQgZ2VuZXMKClRoaXMgZmlsZSBoYXMgb25lIGNvbHVtbiB3aGljaCBsaXN0cyB0aGUgZ2VuZXMgdGhhdCBwYXNzZWQgdGhlIHRocmVzaG9sZCBmb3Igc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIChlLmcuIHAtdmFsdWUgbGVzcyB0aGFuIDAuMDUpIGluIHlvdXIgYW5hbHlzaXMuIEdlbmUgU3ltYm9scyBhcmUgY29tbW9ubHkgdXNlZCwgYWx0aG91Z2ggYSB0b29sIG1heSBhY2NlcHQgRW5zZW1ibCBvciBSZWZzZXEgbmFtZXMKCgo+ICMjIENoYWxsZW5nZSB7LmNoYWxsZW5nZX0KPgo+IENyZWF0ZSB0d28gdGV4dCBmaWxlcyB0aGF0IGNhbiBiZSBpbXBvcnRlZCBpbnRvIG9ubGluZSB0b29scyBmb3IgZnVydGhlciBhbmFseXNpcwo+IDEuIEEgbGlzdCBvZiBiYWNrZ3JvdW5kIGdlbmVzCj4gMi4gQSBsaXN0IG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcwo+IDMuIExvYWQgdGhlc2UgZmlsZXMgaW50byBHT3JpbGxhIGZvciBhbmFseXNpcwo+IEhJTlQ6IHRoZSBgd3JpdGUudGFibGVgIGZ1bmN0aW9uIGlzIGFibGUgdG8gd3JpdGUgYSBkYXRhIGZyYW1lIHRvIGEgdHh0IGZpbGUgaW4gUi4gWW91IHdpbGwgbmVlZCB0byBzZXQgdGhlIGFwcHJvcHJpYXRlIGFyZ3VtZW50cyB0byBtYWtlIHN1cmUgdGhhdCBhIHRleHQgZmlsZSB3aXRoIG9ubHkgb25lIGNvbHVtbiBpcyBjcmVhdGVkLgoKYGBge3J9CgpgYGA=