Original Authors: Belinda Phipson, Anna Trigos, Matt Ritchie, Maria Doyle, Harriet Dashnow, Charity Law Based on the course RNAseq analysis in R delivered on May 11/12th 2016 ## Resources and data files

This material has been created using the following resources:
http://www.statsci.org/smyth/pubs/QLedgeRPreprint.pdf (Lun, Chen, and Smyth 2016)
https://github.com/MonashBioinformaticsPlatform/RNAseq-DE-analysis-with-R

Data files:
https://figshare.com/s/1d788fd384d33e913a2a : mouse_c2_v5.rdata mouse_H_v5.rdata

Gene Set Testing

The list of differentially expressed genes is sometimes so long that its interpretation becomes cumbersome and time consuming. A common downstream procedure is gene set testing. It aims to understand which pathways or gene networks the differentially expressed genes are implicated in.

Various ways exist to test for enrichment of biological pathways. Two types of test answer two different questions: competitive and self-contained gene set tests.

Competitive gene set tests, such as those implemented in GOseq and camera, ask the question whether the differentially expressed genes tend to be over-represented in the gene set, compared to all the other genes in the experiment.

Self-contained tests, which include the ROAST procedure, ask the question “Are the genes in the set/pathway differentially expressed as a whole?”

load("Robjects/DE.Rdata")

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.”

Create list of DEGs:

# Retrieve list of all genes tested:
results <- as.data.frame(topTags(lrt_BvsL, n = Inf))
print(head(results))
# Derive list of DEGs by filtering on FDR:
genes <- results$FDR < 0.01
# Add gene names to that list:
names(genes) <- rownames(results)
print(head(genes))
110308  50916  12293  56069  24117  12818 
  TRUE   TRUE   TRUE   TRUE   TRUE   TRUE 
library(goseq)
library(TxDb.Mmusculus.UCSC.mm10.knownGene)
#print(supportedGeneIDs())
#print(supportedGenomes())

Fit the Probability Weighting Function (PWF):

pwf <- nullp(genes, "mm10","knownGene")
Can't find mm10/knownGene length data in genLenDataBase...
Found the annotaion package, TxDb.Mmusculus.UCSC.mm10.knownGene
Trying to get the gene lengths from it.
initial point very close to some inequality constraints

Conduct gene set enrichment analysis:

#?goseq
go.results <- goseq(pwf, "mm10","knownGene")
Fetching GO annotations...

For 1098 genes, we could not find any categories. These genes will be excluded.
To force their use, please run with use_genes_without_cat=TRUE (see documentation).
This was the default behavior for version 1.15.1 and earlier.
Calculating the p-values...
'select()' returned 1:1 mapping between keys and columns
go.results

fgsea analysis

From the fgsea vignette “fast preranked gene set enrichment analysis (GSEA)”:

This 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.” commentary on GSEA. The article describing the original software is available here.

library(fgsea)
Loading required package: Rcpp

Create ranks

Rank all genes based on their fold change.

results.ord <- results[ order(-results[,"logFC"]), ]
head(results.ord)
ranks <- results.ord$logFC
names(ranks) <- rownames(results.ord)
head(ranks)
  791407   382036   192200    21990   246133    12310 
8.197169 7.947515 7.894963 7.794611 7.708981 7.660325 

Plot the ranked fold changes.

#plot(ranks)
barplot(ranks)

Load pathways

load("data/mouse_H_v5.rdata")
pathways <- Mm.H

Conduct analysis

?fgsea
fgseaRes <- fgsea(pathways, ranks, minSize=15, maxSize = 500, nperm=1000)
class(fgseaRes)
[1] "data.table" "data.frame"
dim(fgseaRes)
[1] 50  8
#head(fgseaRes)

Table of results

head(fgseaRes[order(padj), ])

Plot outcome for the ‘HALLMARK_MYOGENESIS’ pathway

First find rank of the ‘HALLMARK_MYOGENESIS’ pathway genes in the sorted genes. Then plot as before.

# We will create a barplot of logFC for the sorted genes and add one vertical red bar for each gene in the 'HALLMARK_MYOGENESIS' pathway
tmpInd <- match(pathways[["HALLMARK_MYOGENESIS"]],names(ranks))
tmpInd <- tmpInd[!is.na(tmpInd)]
ranks2 <- rep(0,length(ranks))
ranks2[tmpInd] <- ranks[tmpInd]
barplot(ranks2)

Enrichment score plot

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

Remember to check the GSEA article for the complete explanation.

GSEA table plot

Select top pathways and plot outcome for all these

pathwaysUp <- fgseaRes[ES > 0]
topPathwaysUp <- head( pathwaysUp$pathway[ order(pathwaysUp$pval) ], n=10 )
pathwaysDown <- fgseaRes[ES < 0]
topPathwaysDown <- head( pathwaysDown$pathway[ order( pathwaysDown$pval ) ], n=10 )
topPathways <- c(topPathwaysUp, rev(topPathwaysDown))
plotGseaTable(pathways[topPathways], ranks, fgseaRes, gseaParam = 0.5)

?plotGseaTable

Gene Set Testing - self-contained gene set tests

ROAST gene set testing

ROAST is an example of a self-contained gene set test (Wu et al. 2010). It asks the question, “Do the genes in my set tend to be differentially expressed between my conditions of interest?”. ROAST does not use information on the other genes in the experiment, unlike camera (see below). ROAST is a good option for when you’re interested in a specific set, or a few sets. It is not really used to test thousands of sets at one time.

Here we will be using the C2 gene sets for mouse, available as .rdata files from the WEHI bioinformatics page http://bioinf.wehi.edu.au/software/MSigDB/index.html. The C2 gene sets contain 4725 curated gene sets collected from a variety of places: BioCarta, KEGG, Pathway Interaction Database, Reactome as well as some published studies. It doesn’t include GO terms.

# Load in the mouse c2 gene sets
# The R object is called Mm.c2
load("data/mouse_c2_v5.rdata")
# Have a look at the first few gene sets
names(Mm.c2)[1:5]
[1] "KEGG_GLYCOLYSIS_GLUCONEOGENESIS"               "KEGG_CITRATE_CYCLE_TCA_CYCLE"                 
[3] "KEGG_PENTOSE_PHOSPHATE_PATHWAY"                "KEGG_PENTOSE_AND_GLUCURONATE_INTERCONVERSIONS"
[5] "KEGG_FRUCTOSE_AND_MANNOSE_METABOLISM"         
# Number of gene sets in C2
length(Mm.c2)
[1] 4725

The gene identifiers are Entrez Gene ID, as are the rownames of our DGEList object ‘dgeObj’. We need to map the Entrez gene ids between the list of gene sets and our DGEList object. We can do this using the ids2indices function.

c2.ind <- ids2indices(Mm.c2, rownames(dgeObj$counts))

Let’s see if there are any MYC signalling pathways in MsigDB C2 collection. We can do this with the grep command on the names of the gene sets.

grep("MYC_", names(c2.ind))
 [1]  496  544  621 1591 1592 1783 1784 2029 2030 2031 2032 2194 2195 2203 2204 2435 2436 2506 2507 2600 2609 2620 2621 2629 2676
[26] 2694 2706 2716 2727 2744 2786 2824 3311 3401 3817 3943 3944 4204 4205 4206 4207 4433 4434 4616
# Let's save these so that we can subset c2.ind to test all gene sets with MYC in the name
myc <- grep("MYC_",names(c2.ind))
# What are these pathways called?
names(c2.ind)[myc]
 [1] "PID_MYC_ACTIV_PATHWAY"                           "PID_MYC_PATHWAY"                                
 [3] "PID_MYC_REPRESS_PATHWAY"                         "ODONNELL_TARGETS_OF_MYC_AND_TFRC_UP"            
 [5] "ODONNELL_TARGETS_OF_MYC_AND_TFRC_DN"             "CAIRO_PML_TARGETS_BOUND_BY_MYC_UP"              
 [7] "CAIRO_PML_TARGETS_BOUND_BY_MYC_DN"               "SCHLOSSER_MYC_AND_SERUM_RESPONSE_SYNERGY"       
 [9] "SCHLOSSER_MYC_TARGETS_REPRESSED_BY_SERUM"        "SCHLOSSER_MYC_TARGETS_AND_SERUM_RESPONSE_DN"    
[11] "SCHLOSSER_MYC_TARGETS_AND_SERUM_RESPONSE_UP"     "CEBALLOS_TARGETS_OF_TP53_AND_MYC_UP"            
[13] "CEBALLOS_TARGETS_OF_TP53_AND_MYC_DN"             "KIM_MYC_AMPLIFICATION_TARGETS_UP"               
[15] "KIM_MYC_AMPLIFICATION_TARGETS_DN"                "BENPORATH_MYC_TARGETS_WITH_EBOX"                
[17] "BENPORATH_MYC_MAX_TARGETS"                       "MORI_EMU_MYC_LYMPHOMA_BY_ONSET_TIME_UP"         
[19] "MORI_EMU_MYC_LYMPHOMA_BY_ONSET_TIME_DN"          "SCHUHMACHER_MYC_TARGETS_DN"                     
[21] "LEE_LIVER_CANCER_MYC_TGFA_DN"                    "LEE_LIVER_CANCER_MYC_UP"                        
[23] "MENSSEN_MYC_TARGETS"                             "LEE_LIVER_CANCER_MYC_TGFA_UP"                   
[25] "LEE_LIVER_CANCER_MYC_E2F1_DN"                    "LEE_LIVER_CANCER_MYC_E2F1_UP"                   
[27] "SCHUHMACHER_MYC_TARGETS_UP"                      "LEE_LIVER_CANCER_MYC_DN"                        
[29] "COLLER_MYC_TARGETS_UP"                           "COLLER_MYC_TARGETS_DN"                          
[31] "YU_MYC_TARGETS_UP"                               "YU_MYC_TARGETS_DN"                              
[33] "BILD_MYC_ONCOGENIC_SIGNATURE"                    "SANSOM_APC_MYC_TARGETS"                         
[35] "SOUCEK_MYC_TARGETS"                              "ELLWOOD_MYC_TARGETS_UP"                         
[37] "ELLWOOD_MYC_TARGETS_DN"                          "DANG_REGULATED_BY_MYC_UP"                       
[39] "DANG_REGULATED_BY_MYC_DN"                        "DANG_MYC_TARGETS_UP"                            
[41] "DANG_MYC_TARGETS_DN"                             "ACOSTA_PROLIFERATION_INDEPENDENT_MYC_TARGETS_UP"
[43] "ACOSTA_PROLIFERATION_INDEPENDENT_MYC_TARGETS_DN" "ALFANO_MYC_TARGETS"                             

Let’s use ROAST to see if these MYC related gene sets tend to be differentially expressed.

# Specify a design matrix without an intercept term
design <- model.matrix(~ CellType + Status, data = sampleinfo)
# Run ROAST
myc.rst <- roast(dgeObj,
                 index=c2.ind[myc],
                 design=design,
                 contrast=3,
                 nrot=999)
myc.rst[1:15,]

Each row corresponds to a single gene set.

The NGenes column gives the number of genes in each set.

The PropDown and PropUp columns contain the proportions of genes in the set that are down- and up-regulated, respectively, with absolute fold changes greater than 2.

The net direction of change is determined from the significance of changes in each direction, and is shown in the Direction column.

The PValue provides evidence for whether the majority of genes in the set are DE in the specified direction

The PValue.Mixed provides evidence for whether the majority of genes in the set are DE in any direction.

FDRs are computed from the corresponding p-values across all sets.

Challenge 1

  1. Test whether the MYC signalling pathways tend to be differentially expressed between basal virgin vs lactating.
  2. Look for gene sets containing “WNT” in the name and see whether they tend to be differentially expressed in basal pregnant vs lactating.

Notes

  • A common application of ROAST is to use a set of DE genes that was defined from an analysis of an independent data set. ROAST can then determine whether similar changes are observed in the contrast of interest for the current data set.
  • ROAST estimates p-values by simulation, so the results may change slightly between runs. More precise p-values can be obtained by increasing the number of rotations, albeit at the cost of increased computational time.
  • The smallest p-value that can be reported is 1/(2nrot + 1) where nrot is the number of rotations. This lower bound can be decreased by increasing nrot.

References

Supplementary Material - CAMERA

CAMERA gene set testing using the Broad’s curated gene sets

Other databases of gene sets that are available come from the Broad Institute’s Molecular Signatures Database (MSigDB). CAMERA is good option for testing a very large number of gene sets such as the MSigDB sets, as it is very fast. It has the advantage of accounting for inter-gene correlation within each gene set (Wu and Smyth 2012).

CAMERA takes as input the DGEList object dgeObj, the indexed list of gene sets c2.ind, the design matrix, the contrast being tested, as well as some other arguments. By default, CAMERA can estimate the correlation for each gene set separately. However, in practise, it works well to set a small inter-gene correlation of about 0.05 using the inter.gene.cor argument.

# Specify a design matrix without an intercept term
design <- model.matrix(~ CellType + Status, data = sampleinfo)
#design
# Check contrasts:
print(colnames(design))
[1] "(Intercept)"     "CellTypeluminal" "Statuspregnant"  "Statusvirgin"   
# Run analysis:
gst.camera <- camera.DGEList(dgeObj,index=c2.ind,design=design,contrast=2,inter.gene.cor=0.05)

CAMERA outputs a dataframe of the resulting statistics, with each row denoting a different gene set. The output is ordered by p-value so that the most significant should be at the top. Let’s look at the top 5 gene sets:

gst.camera[1:5,]

The total number of significant gene sets at 5% FDR is:

table(gst.camera$FDR < 0.05)

FALSE  TRUE 
 4699    23 

You can write out the camera results to a csv file to open in excel.

write.csv(gst.camera,file="gst_LumVsBas.csv")

Challenge 2

  1. Run camera on the pregnant vs lactating contrast.
  2. Run camera on a different set of MSigDB gene sets, the hallmark datasets, mouse_H_v5.rdata. You will need to load in the hallmark gene sets, and the object will be called Mm.H in R.

Lun, Aaron T L, Yunshun Chen, and Gordon K Smyth. 2016. “It’s DE-licious: A Recipe for Differential Expression Analyses of RNA-seq Experiments Using Quasi-Likelihood Methods in edgeR.” Methods in Molecular Biology (Clifton, N.J.) 1418 (January): 391–416. doi:10.1007/978-1-4939-3578-9\_19.

Wu, D, and G K Smyth. 2012. “Camera: a competitive gene set test accounting for inter-gene correlation.” Nucleic Acids Research 40 (17). Oxford University Press: e133—–e133.

Wu, D, E Lim, F Vaillant, M L Asselin-Labat, J E Visvader, and G K Smyth. 2010. “ROAST: rotation gene set tests for complex microarray experiments.” Bioinformatics 26 (17). Oxford Univ Press: 2176–82.

LS0tCnRpdGxlOiAiUk5BLXNlcSBhbmFseXNpcyBpbiBSIgphdXRob3I6ICJTdGVwaGFuZSBCYWxsZXJlYXUsIE1hcmsgRHVubmluZywgT3NjYXIgUnVlZGEsIEFzaGxleSBTYXdsZSIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKbWludXRlczogMzAwCmxheW91dDogcGFnZQpzdWJ0aXRsZTogR2VuZSBTZXQgVGVzdGluZyBmb3IgUk5BLXNlcQpiaWJsaW9ncmFwaHk6IHJlZi5iaWIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShlZGdlUikKYGBgCgoqKk9yaWdpbmFsIEF1dGhvcnM6IEJlbGluZGEgUGhpcHNvbiwgQW5uYSBUcmlnb3MsIE1hdHQgUml0Y2hpZSwgTWFyaWEgRG95bGUsIEhhcnJpZXQgRGFzaG5vdywgQ2hhcml0eSBMYXcqKgpCYXNlZCBvbiB0aGUgY291cnNlIFtSTkFzZXEgYW5hbHlzaXMgaW4gUl0oaHR0cDovL2NvbWJpbmUtYXVzdHJhbGlhLmdpdGh1Yi5pby8yMDE2LTA1LTExLVJOQXNlcS8pIGRlbGl2ZXJlZCBvbiBNYXkgMTEvMTJ0aCAyMDE2CiMjIFJlc291cmNlcyBhbmQgZGF0YSBmaWxlcwoKVGhpcyBtYXRlcmlhbCBoYXMgYmVlbiBjcmVhdGVkIHVzaW5nIHRoZSBmb2xsb3dpbmcgcmVzb3VyY2VzOiAgCmh0dHA6Ly93d3cuc3RhdHNjaS5vcmcvc215dGgvcHVicy9RTGVkZ2VSUHJlcHJpbnQucGRmIFtATHVuMjAxNl0gIApodHRwczovL2dpdGh1Yi5jb20vTW9uYXNoQmlvaW5mb3JtYXRpY3NQbGF0Zm9ybS9STkFzZXEtREUtYW5hbHlzaXMtd2l0aC1SCgpEYXRhIGZpbGVzOiAgCmh0dHBzOi8vZmlnc2hhcmUuY29tL3MvMWQ3ODhmZDM4NGQzM2U5MTNhMmEgOgogICAgbW91c2VfYzJfdjUucmRhdGEKICAgIG1vdXNlX0hfdjUucmRhdGEKCiMgR2VuZSBTZXQgVGVzdGluZwoKVGhlIGxpc3Qgb2YgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGlzIHNvbWV0aW1lcyBzbyBsb25nIHRoYXQgaXRzIGludGVycHJldGF0aW9uIGJlY29tZXMgY3VtYmVyc29tZSBhbmQgdGltZSBjb25zdW1pbmcuIEEgY29tbW9uIGRvd25zdHJlYW0gcHJvY2VkdXJlIGlzIGdlbmUgc2V0IHRlc3RpbmcuIEl0IGFpbXMgdG8gdW5kZXJzdGFuZCB3aGljaCBwYXRod2F5cyBvciBnZW5lIG5ldHdvcmtzIHRoZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYXJlIGltcGxpY2F0ZWQgaW4uCgpWYXJpb3VzIHdheXMgZXhpc3QgdG8gdGVzdCBmb3IgZW5yaWNobWVudCBvZiBiaW9sb2dpY2FsIHBhdGh3YXlzLiBUd28gdHlwZXMgb2YgdGVzdCBhbnN3ZXIgdHdvIGRpZmZlcmVudCBxdWVzdGlvbnM6IGNvbXBldGl0aXZlIGFuZCBzZWxmLWNvbnRhaW5lZCBnZW5lIHNldCB0ZXN0cy4KCkNvbXBldGl0aXZlIGdlbmUgc2V0IHRlc3RzLCBzdWNoIGFzIHRob3NlIGltcGxlbWVudGVkIGluIGBHT3NlcWAgYW5kIGBjYW1lcmFgLCBhc2sgdGhlIHF1ZXN0aW9uIHdoZXRoZXIgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyB0ZW5kIHRvIGJlIG92ZXItcmVwcmVzZW50ZWQgaW4gdGhlIGdlbmUgc2V0LCBjb21wYXJlZCB0byBhbGwgdGhlIG90aGVyIGdlbmVzIGluIHRoZSBleHBlcmltZW50LgoKU2VsZi1jb250YWluZWQgdGVzdHMsIHdoaWNoIGluY2x1ZGUgdGhlIGBST0FTVGAgcHJvY2VkdXJlLCBhc2sgdGhlIHF1ZXN0aW9uICJBcmUgdGhlIGdlbmVzIGluIHRoZSBzZXQvcGF0aHdheSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYXMgYSB3aG9sZT8iCgpgYGB7ciBsb2FkRGF0YX0KbG9hZCgiUm9iamVjdHMvREUuUmRhdGEiKQpgYGAKCiMjIEdlbmUgU2V0IFRlc3RpbmcgLSBjb21wZXRpdGl2ZSBnZW5lIHNldCB0ZXN0cwoKIyMjIEdPc2VxIGFuYWx5c2lzCgpHT3NlcSBpcyBhIG1ldGhvZCB0byBjb25kdWN0IEdlbmUgT250b2xvZ3kgKEdPKSBhbmFseXNpcyBzdWl0YWJsZSBmb3IgUk5BLXNlcSBkYXRhIGFzIGl0IGFjY291bnRzIGZvciB0aGUgZ2VuZSBsZW5ndGggYmlhcyBpbiBkZXRlY3Rpb24gb2Ygb3Zlci1yZXByZXNlbnRhdGlvbiAoW0dPc2VxIGFydGljbGVdKGh0dHBzOi8vZ2Vub21lYmlvbG9neS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L2diLTIwMTAtMTEtMi1yMTQpKQoKRnJvbSB0aGUgW0dPc2VxIHZpZ25ldHRlXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvZ29zZXEvaW5zdC9kb2MvZ29zZXEucGRmKToKCi0gR09zZXEgZmlyc3QgbmVlZHMgdG8gcXVhbnRpZnkgdGhlIGxlbmd0aCBiaWFzIHByZXNlbnQgaW4gdGhlIGRhdGFzZXQgdW5kZXIgY29uc2lkZXJhdGlvbi4KLSBUaGlzIGlzIGRvbmUgYnkgY2FsY3VsYXRpbmcgYSBQcm9iYWJpbGl0eSBXZWlnaHRpbmcgRnVuY3Rpb24gb3IgUFdGIHdoaWNoIGNhbiBiZSB0aG91Z2h0IG9mIGFzIGEgZnVuY3Rpb24gd2hpY2ggZ2l2ZXMgdGhlIHByb2JhYmlsaXR5IHRoYXQgYSBnZW5lIHdpbGwgYmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIChERSksIGJhc2VkIG9uIGl0cyBsZW5ndGggYWxvbmUuCi0gVGhlIFBXRiBpcyBjYWxjdWxhdGVkIGJ5IGZpdHRpbmcgYSBtb25vdG9uaWMgc3BsaW5lIHRvIHRoZSBiaW5hcnkgZGF0YSBzZXJpZXMgb2YgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gKDE9REUsIDA9Tm90IERFKSBhcyBhIGZ1bmN0aW9uIG9mIGdlbmUgbGVuZ3RoLgotIFRoZSBQV0YgaXMgdXNlZCB0byB3ZWlnaHQgdGhlIGNoYW5jZSBvZiBzZWxlY3RpbmcgZWFjaCBnZW5lIHdoZW4gZm9ybWluZyBhIG51bGwgZGlzdHJpYnV0aW9uIGZvciBHTyBjYXRlZ29yeSBtZW1iZXJzaGlwLgotIFRoZSBmYWN0IHRoYXQgdGhlIFBXRiBpcyBjYWxjdWxhdGVkIGRpcmVjdGx5IGZyb20gdGhlIGRhdGFzZXQgdW5kZXIgY29uc2lkZXJhdGlvbiBtYWtlcyB0aGlzIGFwcHJvYWNoIHJvYnVzdCwgb25seSBjb3JyZWN0aW5nIGZvciB0aGUgbGVuZ3RoIGJpYXMgcHJlc2VudCBpbiB0aGUgZGF0YS4KCiJHTyBhbmFseXNpcyBvZiBSTkEtc2VxIGRhdGEgcmVxdWlyZXMgdGhlIHVzZSBvZiByYW5kb20gc2FtcGxpbmcgaW4gb3JkZXIgdG8gZ2VuZXJhdGUgYSBzdWl0YWJsZSBudWxsIGRpc3RyaWJ1dGlvbiBmb3IgR08gY2F0ZWdvcnkgbWVtYmVyc2hpcCBhbmQgY2FsY3VsYXRlIGVhY2ggY2F0ZWdvcnkncyBzaWduaWZpY2FuY2UgZm9yIG92ZXIgcmVwcmVzZW50YXRpb24gYW1vbmdzdCBERSBnZW5lcy4gLi4uIEluICBtb3N0ICBjYXNlcywgIHRoZSAgV2FsbGVuaXVzIGRpc3RyaWJ1dGlvbiBjYW4gYmUgdXNlZCB0byBhcHByb3hpbWF0ZSB0aGUgdHJ1ZSBudWxsIGRpc3RyaWJ1dGlvbiwgd2l0aG91dCBhbnkgc2lnbmlmaWNhbnQgbG9zcyBpbiBhY2N1cmFjeS4gVGhlIGdvc2VxIHBhY2thZ2UgaW1wbGVtZW50cyB0aGlzIGFwcHJveGltYXRpb24gYXMgaXRzIGRlZmF1bHQgb3B0aW9uLiIKCkNyZWF0ZSBsaXN0IG9mIERFR3M6CgpgYGB7cn0KCiMgUmV0cmlldmUgbGlzdCBvZiBhbGwgZ2VuZXMgdGVzdGVkOgpyZXN1bHRzIDwtIGFzLmRhdGEuZnJhbWUodG9wVGFncyhscnRfQnZzTCwgbiA9IEluZikpCnByaW50KGhlYWQocmVzdWx0cykpCgojIERlcml2ZSBsaXN0IG9mIERFR3MgYnkgZmlsdGVyaW5nIG9uIEZEUjoKZ2VuZXMgPC0gcmVzdWx0cyRGRFIgPCAwLjAxCgojIEFkZCBnZW5lIG5hbWVzIHRvIHRoYXQgbGlzdDoKbmFtZXMoZ2VuZXMpIDwtIHJvd25hbWVzKHJlc3VsdHMpCgpwcmludChoZWFkKGdlbmVzKSkKYGBgCgpgYGB7ciBnb1NlcVBhY2thZ2UsIG1lc3NhZ2U9Rn0KbGlicmFyeShnb3NlcSkKbGlicmFyeShUeERiLk1tdXNjdWx1cy5VQ1NDLm1tMTAua25vd25HZW5lKQojcHJpbnQoc3VwcG9ydGVkR2VuZUlEcygpKQojcHJpbnQoc3VwcG9ydGVkR2Vub21lcygpKQpgYGAKCkZpdCB0aGUgUHJvYmFiaWxpdHkgV2VpZ2h0aW5nIEZ1bmN0aW9uIChQV0YpOgpgYGB7ciBwd0Z1bmN0aW9ufQpwd2YgPC0gbnVsbHAoZ2VuZXMsICJtbTEwIiwgImtub3duR2VuZSIpCmBgYAoKQ29uZHVjdCBnZW5lIHNldCBlbnJpY2htZW50IGFuYWx5c2lzOgoKYGBge3J9CiM/Z29zZXEKZ28ucmVzdWx0cyA8LSBnb3NlcShwd2YsICJtbTEwIiwia25vd25HZW5lIikKZ28ucmVzdWx0cwpgYGAKCiMjIyBmZ3NlYSBhbmFseXNpcwoKRnJvbSB0aGUgZmdzZWEgW3ZpZ25ldHRlXShodHRwOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9mZ3NlYS9pbnN0L2RvYy9mZ3NlYS10dXRvcmlhbC5odG1sKSAiZmFzdCBwcmVyYW5rZWQgZ2VuZSBzZXQgZW5yaWNobWVudCBhbmFseXNpcyAoR1NFQSkiOgoKVGhpcyBhbmFseXNpcyBpcyBwZXJmb3JtZWQgYnk6CgotIChpKSByYW5raW5nIGFsbCBnZW5lcyBpbiB0aGUgZGF0YSBzZXQgYmFzZWQgb24gdGhlaXIgY29ycmVsYXRpb24gdG8gdGhlIGNob3NlbiBwaGVub3R5cGUsCi0gKGlpKSBpZGVudGlmeWluZyB0aGUgcmFuayBwb3NpdGlvbnMgb2YgYWxsIG1lbWJlcnMgb2YgdGhlIGdlbmUgc2V0LCBhbmQgCi0gKGlpaSkgY2FsY3VsYXRpbmcgYW4gZW5yaWNobWVudCBzY29yZSAoRVMpIHRoYXQgcmVwcmVzZW50cyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBvYnNlcnZlZCByYW5raW5ncyBhbmQgdGhhdCB3aGljaCB3b3VsZCBiZSBleHBlY3RlZCBhc3N1bWluZyBhIHJhbmRvbSByYW5rIGRpc3RyaWJ1dGlvbi4KCiJBZnRlciBlc3RhYmxpc2hpbmcgdGhlIEVTIGZvciBlYWNoIGdlbmUgc2V0IGFjcm9zcyB0aGUgcGhlbm90eXBlLCBHU0VBIHJlaXRlcmF0aXZlbHkgcmFuZG9taXplcyB0aGUgc2FtcGxlIGxhYmVscyBhbmQgcmV0ZXN0cyBmb3IgZW5yaWNobWVudCBhY3Jvc3MgdGhlIHJhbmRvbSBjbGFzc2VzLiBCeSBwZXJmb3JtaW5nIHJlcGVhdGVkIGNsYXNzIGxhYmVsIHJhbmRvbWl6YXRpb25zLCB0aGUgRVMgZm9yIGVhY2ggZ2VuZSBzZXQgYWNyb3NzIHRoZSB0cnVlIGNsYXNzZXMgY2FuIGJlIGNvbXBhcmVkIHRvIHRoZSBFUyBkaXN0cmlidXRpb24gZnJvbSB0aGUgcmFuZG9tIGNsYXNzZXMuIFRob3NlIGdlbmUgc2V0cyB0aGF0IHNpZ25pZmljYW50bHkgb3V0cGVyZm9ybSBpdGVyYXRpdmUgcmFuZG9tIGNsYXNzIHBlcm11dGF0aW9ucyBhcmUgY29uc2lkZXJlZCBzaWduaWZpY2FudC4iIFtjb21tZW50YXJ5IG9uIEdTRUFdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzEyNjYxMzEvKS4gVGhlIGFydGljbGUgZGVzY3JpYmluZyB0aGUgb3JpZ2luYWwgc29mdHdhcmUgaXMgYXZhaWxhYmxlIFtoZXJlXShodHRwOi8vd3d3LnBuYXMub3JnL2NvbnRlbnQvMTAyLzQzLzE1NTQ1LmxvbmcpLgoKYGBge3J9CmxpYnJhcnkoZmdzZWEpCmBgYAoKIyMjIyBDcmVhdGUgcmFua3MKClJhbmsgYWxsIGdlbmVzIGJhc2VkIG9uIHRoZWlyIGZvbGQgY2hhbmdlLgoKYGBge3J9CnJlc3VsdHMub3JkIDwtIHJlc3VsdHNbIG9yZGVyKC1yZXN1bHRzWywibG9nRkMiXSksIF0KaGVhZChyZXN1bHRzLm9yZCkKcmFua3MgPC0gcmVzdWx0cy5vcmQkbG9nRkMKbmFtZXMocmFua3MpIDwtIHJvd25hbWVzKHJlc3VsdHMub3JkKQpoZWFkKHJhbmtzKQpgYGAKClBsb3QgdGhlIHJhbmtlZCBmb2xkIGNoYW5nZXMuCgpgYGB7cn0KI3Bsb3QocmFua3MpCmJhcnBsb3QocmFua3MpCmBgYAoKIyMjIyBMb2FkIHBhdGh3YXlzCgpgYGB7cn0KbG9hZCgiZGF0YS9tb3VzZV9IX3Y1LnJkYXRhIikKcGF0aHdheXMgPC0gTW0uSApgYGAKCiMjIyMgQ29uZHVjdCBhbmFseXNpcwoKYGBge3J9Cj9mZ3NlYQpmZ3NlYVJlcyA8LSBmZ3NlYShwYXRod2F5cywgcmFua3MsIG1pblNpemU9MTUsIG1heFNpemUgPSA1MDAsIG5wZXJtPTEwMDApCmNsYXNzKGZnc2VhUmVzKQpkaW0oZmdzZWFSZXMpCiNoZWFkKGZnc2VhUmVzKQpgYGAKCiMjIyMgVGFibGUgb2YgcmVzdWx0cwpgYGB7cn0KaGVhZChmZ3NlYVJlc1tvcmRlcihwYWRqKSwgXSkKYGBgCgojIyMjIFBsb3Qgb3V0Y29tZSBmb3IgdGhlICdIQUxMTUFSS19NWU9HRU5FU0lTJyBwYXRod2F5CgpGaXJzdCBmaW5kIHJhbmsgb2YgdGhlICdIQUxMTUFSS19NWU9HRU5FU0lTJyBwYXRod2F5IGdlbmVzIGluIHRoZSBzb3J0ZWQgZ2VuZXMuClRoZW4gcGxvdCBhcyBiZWZvcmUuCgpgYGB7cn0KIyBXZSB3aWxsIGNyZWF0ZSBhIGJhcnBsb3Qgb2YgbG9nRkMgZm9yIHRoZSBzb3J0ZWQgZ2VuZXMgYW5kIGFkZCBvbmUgdmVydGljYWwgcmVkIGJhciBmb3IgZWFjaCBnZW5lIGluIHRoZSAnSEFMTE1BUktfTVlPR0VORVNJUycgcGF0aHdheQp0bXBJbmQgPC0gbWF0Y2gocGF0aHdheXNbWyJIQUxMTUFSS19NWU9HRU5FU0lTIl1dLG5hbWVzKHJhbmtzKSkKdG1wSW5kIDwtIHRtcEluZFshaXMubmEodG1wSW5kKV0KCnJhbmtzMiA8LSByZXAoMCxsZW5ndGgocmFua3MpKQpyYW5rczJbdG1wSW5kXSA8LSByYW5rc1t0bXBJbmRdCgpiYXJwbG90KHJhbmtzMikKYGBgCgojIyMjIEVucmljaG1lbnQgc2NvcmUgcGxvdAoKYGBge3J9CnBsb3RFbnJpY2htZW50KHBhdGh3YXlzW1siSEFMTE1BUktfTVlPR0VORVNJUyJdXSwKICAgICAgICAgICAgICAgcmFua3MpCmBgYAoKUmVtZW1iZXIgdG8gY2hlY2sgdGhlIFtHU0VBIGFydGljbGVdKGh0dHA6Ly93d3cucG5hcy5vcmcvY29udGVudC8xMDIvNDMvMTU1NDUuZnVsbCkgZm9yIHRoZSBjb21wbGV0ZSBleHBsYW5hdGlvbi4KCiMgR1NFQSB0YWJsZSBwbG90CgpTZWxlY3QgdG9wIHBhdGh3YXlzIGFuZCBwbG90IG91dGNvbWUgZm9yIGFsbCB0aGVzZQoKYGBge3J9CnBhdGh3YXlzVXAgPC0gZmdzZWFSZXNbRVMgPiAwXQp0b3BQYXRod2F5c1VwIDwtIGhlYWQoIHBhdGh3YXlzVXAkcGF0aHdheVsgb3JkZXIocGF0aHdheXNVcCRwdmFsKSBdLCBuPTEwICkKcGF0aHdheXNEb3duIDwtIGZnc2VhUmVzW0VTIDwgMF0KdG9wUGF0aHdheXNEb3duIDwtIGhlYWQoIHBhdGh3YXlzRG93biRwYXRod2F5WyBvcmRlciggcGF0aHdheXNEb3duJHB2YWwgKSBdLCBuPTEwICkKdG9wUGF0aHdheXMgPC0gYyh0b3BQYXRod2F5c1VwLCByZXYodG9wUGF0aHdheXNEb3duKSkKCgpwbG90R3NlYVRhYmxlKHBhdGh3YXlzW3RvcFBhdGh3YXlzXSwgcmFua3MsIGZnc2VhUmVzLCBnc2VhUGFyYW0gPSAwLjUpCj9wbG90R3NlYVRhYmxlCmBgYAoKIyMgR2VuZSBTZXQgVGVzdGluZyAtIHNlbGYtY29udGFpbmVkIGdlbmUgc2V0IHRlc3RzCgojIyMgUk9BU1QgZ2VuZSBzZXQgdGVzdGluZwoKW1JPQVNUXShodHRwczovL2FjYWRlbWljLm91cC5jb20vYmlvaW5mb3JtYXRpY3MvYXJ0aWNsZS1sb29rdXAvZG9pLzEwLjEwOTMvYmlvaW5mb3JtYXRpY3MvYnRxNDAxKSBpcyBhbiBleGFtcGxlIG9mIGEgc2VsZi1jb250YWluZWQgZ2VuZSBzZXQgdGVzdCBbQHd1MjAxMHJvYXN0XS4gSXQgYXNrcyB0aGUgcXVlc3Rpb24sICJEbyB0aGUgZ2VuZXMgaW4gbXkgc2V0IHRlbmQgdG8gYmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gbXkgY29uZGl0aW9ucyBvZiBpbnRlcmVzdD8iLiBST0FTVCBkb2VzIG5vdCB1c2UgaW5mb3JtYXRpb24gb24gdGhlIG90aGVyIGdlbmVzIGluIHRoZSBleHBlcmltZW50LCB1bmxpa2UgYGNhbWVyYWAgKHNlZSBiZWxvdykuIFJPQVNUIGlzIGEgZ29vZCBvcHRpb24gZm9yIHdoZW4geW91J3JlIGludGVyZXN0ZWQgaW4gYSBzcGVjaWZpYyBzZXQsIG9yIGEgZmV3IHNldHMuIEl0IGlzIG5vdCByZWFsbHkgdXNlZCB0byB0ZXN0IHRob3VzYW5kcyBvZiBzZXRzIGF0IG9uZSB0aW1lLgoKSGVyZSB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBDMiBnZW5lIHNldHMgZm9yIG1vdXNlLCBhdmFpbGFibGUgYXMgLnJkYXRhIGZpbGVzIGZyb20gdGhlIFdFSEkgYmlvaW5mb3JtYXRpY3MgcGFnZSBbaHR0cDovL2Jpb2luZi53ZWhpLmVkdS5hdS9zb2Z0d2FyZS9NU2lnREIvaW5kZXguaHRtbF0oaHR0cDovL2Jpb2luZi53ZWhpLmVkdS5hdS9zb2Z0d2FyZS9NU2lnREIvaW5kZXguaHRtbCkuIFRoZSBDMiBnZW5lIHNldHMgY29udGFpbiA0NzI1IGN1cmF0ZWQgZ2VuZSBzZXRzIGNvbGxlY3RlZCBmcm9tIGEgdmFyaWV0eSBvZiBwbGFjZXM6IEJpb0NhcnRhLCBLRUdHLCBQYXRod2F5IEludGVyYWN0aW9uIERhdGFiYXNlLCBSZWFjdG9tZSBhcyB3ZWxsIGFzIHNvbWUgcHVibGlzaGVkIHN0dWRpZXMuIEl0IGRvZXNuJ3QgaW5jbHVkZSBHTyB0ZXJtcy4KCmBgYHtyfQojIExvYWQgaW4gdGhlIG1vdXNlIGMyIGdlbmUgc2V0cwojIFRoZSBSIG9iamVjdCBpcyBjYWxsZWQgTW0uYzIKbG9hZCgiZGF0YS9tb3VzZV9jMl92NS5yZGF0YSIpCgojIEhhdmUgYSBsb29rIGF0IHRoZSBmaXJzdCBmZXcgZ2VuZSBzZXRzCm5hbWVzKE1tLmMyKVsxOjVdCgojIE51bWJlciBvZiBnZW5lIHNldHMgaW4gQzIKbGVuZ3RoKE1tLmMyKQpgYGAKClRoZSBnZW5lIGlkZW50aWZpZXJzIGFyZSBFbnRyZXogR2VuZSBJRCwgYXMgYXJlIHRoZSByb3duYW1lcyBvZiBvdXIgREdFTGlzdCBvYmplY3QgJ2RnZU9iaicuIFdlIG5lZWQgdG8gbWFwIHRoZSBFbnRyZXogZ2VuZSBpZHMgYmV0d2VlbiB0aGUgbGlzdCBvZiBnZW5lIHNldHMgYW5kIG91ciBER0VMaXN0IG9iamVjdC4gV2UgY2FuIGRvIHRoaXMgdXNpbmcgdGhlIGBpZHMyaW5kaWNlc2AgZnVuY3Rpb24uCgpgYGB7cn0KYzIuaW5kIDwtIGlkczJpbmRpY2VzKE1tLmMyLCByb3duYW1lcyhkZ2VPYmokY291bnRzKSkKYGBgCgoKTGV0J3Mgc2VlIGlmIHRoZXJlIGFyZSBhbnkgTVlDIHNpZ25hbGxpbmcgcGF0aHdheXMgaW4gTXNpZ0RCIEMyIGNvbGxlY3Rpb24uIFdlIGNhbiBkbyB0aGlzIHdpdGggdGhlIGBncmVwYCBjb21tYW5kIG9uIHRoZSBuYW1lcyBvZiB0aGUgZ2VuZSBzZXRzLgoKYGBge3J9CmdyZXAoIk1ZQ18iLCBuYW1lcyhjMi5pbmQpKQoKIyBMZXQncyBzYXZlIHRoZXNlIHNvIHRoYXQgd2UgY2FuIHN1YnNldCBjMi5pbmQgdG8gdGVzdCBhbGwgZ2VuZSBzZXRzIHdpdGggTVlDIGluIHRoZSBuYW1lCm15YyA8LSBncmVwKCJNWUNfIixuYW1lcyhjMi5pbmQpKQoKIyBXaGF0IGFyZSB0aGVzZSBwYXRod2F5cyBjYWxsZWQ/Cm5hbWVzKGMyLmluZClbbXljXQpgYGAKCkxldCdzIHVzZSBST0FTVCB0byBzZWUgaWYgdGhlc2UgTVlDIHJlbGF0ZWQgZ2VuZSBzZXRzIHRlbmQgdG8gYmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLiAKCmBgYHtyfQojIFNwZWNpZnkgYSBkZXNpZ24gbWF0cml4IHdpdGhvdXQgYW4gaW50ZXJjZXB0IHRlcm0KZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+IENlbGxUeXBlICsgU3RhdHVzLCBkYXRhID0gc2FtcGxlaW5mbykKIyBSdW4gUk9BU1QKbXljLnJzdCA8LSByb2FzdChkZ2VPYmosCiAgICAgICAgICAgICAgICAgaW5kZXg9YzIuaW5kW215Y10sCiAgICAgICAgICAgICAgICAgZGVzaWduPWRlc2lnbiwKICAgICAgICAgICAgICAgICBjb250cmFzdD0zLAogICAgICAgICAgICAgICAgIG5yb3Q9OTk5KQpteWMucnN0WzE6MTUsXQpgYGAKCkVhY2ggcm93IGNvcnJlc3BvbmRzIHRvIGEgc2luZ2xlIGdlbmUgc2V0LgoKVGhlIE5HZW5lcyBjb2x1bW4gZ2l2ZXMgdGhlIG51bWJlciBvZiBnZW5lcyBpbiBlYWNoIHNldC4KClRoZSBQcm9wRG93biBhbmQgUHJvcFVwIGNvbHVtbnMgY29udGFpbiB0aGUgcHJvcG9ydGlvbnMgb2YgZ2VuZXMgaW4gdGhlIHNldCB0aGF0IGFyZSBkb3duLSBhbmQgdXAtcmVndWxhdGVkLCByZXNwZWN0aXZlbHksIHdpdGggYWJzb2x1dGUgZm9sZCBjaGFuZ2VzIGdyZWF0ZXIgdGhhbiAyLgoKVGhlIG5ldCBkaXJlY3Rpb24gb2YgY2hhbmdlIGlzIGRldGVybWluZWQgZnJvbSB0aGUgc2lnbmlmaWNhbmNlIG9mIGNoYW5nZXMgaW4gZWFjaCBkaXJlY3Rpb24sIGFuZCBpcyBzaG93biBpbiB0aGUgRGlyZWN0aW9uIGNvbHVtbi4KClRoZSBQVmFsdWUgcHJvdmlkZXMgZXZpZGVuY2UgZm9yIHdoZXRoZXIgdGhlIG1ham9yaXR5IG9mIGdlbmVzIGluIHRoZSBzZXQgYXJlIERFIGluIHRoZSBzcGVjaWZpZWQgZGlyZWN0aW9uCgpUaGUgUFZhbHVlLk1peGVkIHByb3ZpZGVzIGV2aWRlbmNlIGZvciB3aGV0aGVyIHRoZSBtYWpvcml0eSBvZiBnZW5lcyBpbiB0aGUgc2V0IGFyZSBERSBpbiBhbnkgZGlyZWN0aW9uLgoKRkRScyBhcmUgY29tcHV0ZWQgZnJvbSB0aGUgY29ycmVzcG9uZGluZyBwLXZhbHVlcyBhY3Jvc3MgYWxsIHNldHMuCgo+ICMjIENoYWxsZW5nZSAxIHsuY2hhbGxlbmdlfQo+Cj4gMS4gVGVzdCB3aGV0aGVyIHRoZSBNWUMgc2lnbmFsbGluZyBwYXRod2F5cyB0ZW5kIHRvIGJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBiZXR3ZWVuIGJhc2FsIHZpcmdpbiB2cyBsYWN0YXRpbmcuCj4gMS4gTG9vayBmb3IgZ2VuZSBzZXRzIGNvbnRhaW5pbmcgIldOVCIgaW4gdGhlIG5hbWUgYW5kIHNlZSB3aGV0aGVyIHRoZXkgdGVuZCB0byBiZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gYmFzYWwgcHJlZ25hbnQgdnMgbGFjdGF0aW5nLgo+CgpgYGB7ciBzb2x1dGlvbkNoYWxsZW5nZTF9CgoKYGBgCgpOb3RlcwoKKiBBIGNvbW1vbiBhcHBsaWNhdGlvbiBvZiBST0FTVCBpcyB0byB1c2UgYSBzZXQgb2YgREUgZ2VuZXMgdGhhdCB3YXMgZGVmaW5lZCBmcm9tIGFuIGFuYWx5c2lzIG9mIGFuIGluZGVwZW5kZW50IGRhdGEgc2V0LiBST0FTVCBjYW4gdGhlbiBkZXRlcm1pbmUgd2hldGhlciBzaW1pbGFyIGNoYW5nZXMgYXJlIG9ic2VydmVkIGluIHRoZSBjb250cmFzdCBvZiBpbnRlcmVzdCBmb3IgdGhlIGN1cnJlbnQgZGF0YSBzZXQuCiogUk9BU1QgZXN0aW1hdGVzIHAtdmFsdWVzIGJ5IHNpbXVsYXRpb24sIHNvIHRoZSByZXN1bHRzIG1heSBjaGFuZ2Ugc2xpZ2h0bHkgYmV0d2VlbiBydW5zLiBNb3JlIHByZWNpc2UgcC12YWx1ZXMgY2FuIGJlIG9idGFpbmVkIGJ5IGluY3JlYXNpbmcgdGhlIG51bWJlciBvZiByb3RhdGlvbnMsIGFsYmVpdCBhdCB0aGUgY29zdCBvZiBpbmNyZWFzZWQgY29tcHV0YXRpb25hbCB0aW1lLgoqIFRoZSBzbWFsbGVzdCBwLXZhbHVlIHRoYXQgY2FuIGJlIHJlcG9ydGVkIGlzIDEvKDJucm90ICsgMSkgd2hlcmUgbnJvdCBpcyB0aGUgbnVtYmVyIG9mIHJvdGF0aW9ucy4gVGhpcyBsb3dlciBib3VuZCBjYW4gYmUgZGVjcmVhc2VkIGJ5IGluY3JlYXNpbmcgbnJvdC4KClJlZmVyZW5jZXMKClN1cHBsZW1lbnRhcnkgTWF0ZXJpYWwgLSBDQU1FUkEKPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKIyMjIENBTUVSQSBnZW5lIHNldCB0ZXN0aW5nIHVzaW5nIHRoZSBCcm9hZCdzIGN1cmF0ZWQgZ2VuZSBzZXRzCgpPdGhlciBkYXRhYmFzZXMgb2YgZ2VuZSBzZXRzIHRoYXQgYXJlIGF2YWlsYWJsZSBjb21lIGZyb20gdGhlIEJyb2FkIEluc3RpdHV0ZSdzIE1vbGVjdWxhciBTaWduYXR1cmVzIERhdGFiYXNlIChbTVNpZ0RCXShodHRwOi8vc29mdHdhcmUuYnJvYWRpbnN0aXR1dGUub3JnL2dzZWEvbXNpZ2RiKSkuIFtDQU1FUkFdKGh0dHBzOi8vYWNhZGVtaWMub3VwLmNvbS9uYXIvYXJ0aWNsZS80MC8xNy9lMTMzLzI0MTExNTEvQ2FtZXJhLWEtY29tcGV0aXRpdmUtZ2VuZS1zZXQtdGVzdC1hY2NvdW50aW5nLWZvcikgaXMgZ29vZCBvcHRpb24gZm9yIHRlc3RpbmcgYSB2ZXJ5IGxhcmdlIG51bWJlciBvZiBnZW5lIHNldHMgc3VjaCBhcyB0aGUgTVNpZ0RCIHNldHMsIGFzIGl0IGlzIHZlcnkgZmFzdC4gSXQgaGFzIHRoZSBhZHZhbnRhZ2Ugb2YgYWNjb3VudGluZyBmb3IgaW50ZXItZ2VuZSBjb3JyZWxhdGlvbiB3aXRoaW4gZWFjaCBnZW5lIHNldCBbQHd1MjAxMmNhbWVyYV0uCgpDQU1FUkEgdGFrZXMgYXMgaW5wdXQgdGhlIERHRUxpc3Qgb2JqZWN0IGBkZ2VPYmpgLCB0aGUgaW5kZXhlZCBsaXN0IG9mIGdlbmUgc2V0cyBgYzIuaW5kYCwgdGhlIGRlc2lnbiBtYXRyaXgsIHRoZSBjb250cmFzdCBiZWluZyB0ZXN0ZWQsIGFzIHdlbGwgYXMgc29tZSBvdGhlciBhcmd1bWVudHMuIEJ5IGRlZmF1bHQsIENBTUVSQSBjYW4gZXN0aW1hdGUgdGhlIGNvcnJlbGF0aW9uIGZvciBlYWNoIGdlbmUgc2V0IHNlcGFyYXRlbHkuIEhvd2V2ZXIsIGluIHByYWN0aXNlLCBpdCB3b3JrcyB3ZWxsIHRvIHNldCBhIHNtYWxsIGludGVyLWdlbmUgY29ycmVsYXRpb24gb2YgYWJvdXQgMC4wNSB1c2luZyB0aGUgYGludGVyLmdlbmUuY29yYCBhcmd1bWVudC4KCmBgYHtyfQojIFNwZWNpZnkgYSBkZXNpZ24gbWF0cml4IHdpdGhvdXQgYW4gaW50ZXJjZXB0IHRlcm0KZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+IENlbGxUeXBlICsgU3RhdHVzLCBkYXRhID0gc2FtcGxlaW5mbykKI2Rlc2lnbgoKIyBDaGVjayBjb250cmFzdHM6CnByaW50KGNvbG5hbWVzKGRlc2lnbikpCgojIFJ1biBhbmFseXNpczoKZ3N0LmNhbWVyYSA8LSBjYW1lcmEuREdFTGlzdChkZ2VPYmosaW5kZXg9YzIuaW5kLGRlc2lnbj1kZXNpZ24sY29udHJhc3Q9MixpbnRlci5nZW5lLmNvcj0wLjA1KQpgYGAKCkNBTUVSQSBvdXRwdXRzIGEgZGF0YWZyYW1lIG9mIHRoZSByZXN1bHRpbmcgc3RhdGlzdGljcywgd2l0aCBlYWNoIHJvdyBkZW5vdGluZyBhIGRpZmZlcmVudCBnZW5lIHNldC4gVGhlIG91dHB1dCBpcyBvcmRlcmVkIGJ5IHAtdmFsdWUgc28gdGhhdCB0aGUgbW9zdCBzaWduaWZpY2FudCBzaG91bGQgYmUgYXQgdGhlIHRvcC4gTGV0J3MgbG9vayBhdCB0aGUgdG9wIDUgZ2VuZSBzZXRzOgoKYGBge3J9CmdzdC5jYW1lcmFbMTo1LF0KYGBgCgpUaGUgdG90YWwgbnVtYmVyIG9mIHNpZ25pZmljYW50IGdlbmUgc2V0cyBhdCA1XCUgRkRSIGlzOgoKYGBge3J9CnRhYmxlKGdzdC5jYW1lcmEkRkRSIDwgMC4wNSkKYGBgCgpZb3UgY2FuIHdyaXRlIG91dCB0aGUgY2FtZXJhIHJlc3VsdHMgdG8gYSBjc3YgZmlsZSB0byBvcGVuIGluIGV4Y2VsLgoKYGBge3J9CndyaXRlLmNzdihnc3QuY2FtZXJhLGZpbGU9ImdzdF9MdW1Wc0Jhcy5jc3YiKQpgYGAKCj4gIyMgQ2hhbGxlbmdlIDIgey5jaGFsbGVuZ2V9Cj4KPiAxLiBSdW4gYGNhbWVyYWAgb24gdGhlIHByZWduYW50IHZzIGxhY3RhdGluZyBjb250cmFzdC4KPiAxLiBSdW4gYGNhbWVyYWAgb24gYSBkaWZmZXJlbnQgc2V0IG9mIE1TaWdEQiBnZW5lIHNldHMsIHRoZSBoYWxsbWFyayBkYXRhc2V0cywgYG1vdXNlX0hfdjUucmRhdGFgLgo+IFlvdSB3aWxsIG5lZWQgdG8gbG9hZCBpbiB0aGUgaGFsbG1hcmsgZ2VuZSBzZXRzLCBhbmQgdGhlIG9iamVjdCB3aWxsIGJlIGNhbGxlZCBgTW0uSGAgaW4gUi4KPgoKCmBgYHtyIHNvbHV0aW9uQ2hhbGxlbmdlMn0KCgpgYGAK