Objectives:

  • Understand advantages of using gene ids when analyzing data.
  • Given a list of ENSEMBL gene ids, add gene symbols and Entrez accessions.
  • Generate common visualizations for differential expression comparisons
  • Understand choosing thresholds for calling differentially expressed genes
  • Discuss options for functional enrichments

1 Differential Expression Workflow

Here we will generate summary figures for our results and annotate our DE tables.

TODO: double check references to annotated table prior to running annotations


2 Visualizing DE results

Part of differential expression analysis is generating visualizations to share our results. While the DESeq2 tutorial provides examples of other visualizations, a common visualization to summarize DE comparisons are volcano plots.

2.1 Generating Volcano plots

As described by this Galaxy project tutorial, a volcano plot is a type of scatterplot that shows statistical significance (adjusted p-value) versus magnitude of change (fold change). In a volcano plot, the most upregulated genes are towards the right, the most downregulated genes are towards the left, and the most statistically significant genes are towards the top.

First, we need to set thresholds for determining significant genes. A reasonable threshold would be a fold-change of less than -1.5 or greater than 1.5 and an adjusted pvalue less than 0.05.

fc <- 1.5
pval <- 0.05

Then, we need to sep up some objects to run our plotting code, creating a new object that will have the right shape for plotting, labeling our comparison of interest, and setting up our output directory

df<- res_WT[order(res_WT$padj),] #select our data of interest
df <- as.data.frame(df) #convert our object type
df <- cbind("id" = row.names(df), df) #set rownames to valid column
str(df)
## 'data.frame':    24745 obs. of  7 variables:
##  $ id            : chr  "ENSMUSG00000105263" "ENSMUSG00000069306" "ENSMUSG00000069045" "ENSMUSG00000086503" ...
##  $ baseMean      : num  4505 7456 876 29135 650 ...
##  $ log2FoldChange: num  -1.23 6.49 9.86 -7.64 8.78 ...
##  $ lfcSE         : num  0.089 0.475 0.76 0.608 0.701 ...
##  $ stat          : num  -13.9 13.7 13 -12.6 12.5 ...
##  $ pvalue        : num  1.25e-43 1.58e-42 1.93e-38 3.15e-36 5.06e-36 ...
##  $ padj          : num  2.18e-39 1.39e-38 1.13e-34 1.38e-32 1.77e-32 ...
Comparison <- "ko.control_v_wt.control"

Once we’ve setup the annotations we need, we can proceed with generating the volcano plot, including our thresholds.

pdf(file = paste0(plotPath,'VolcanoPlot_', Comparison, '.pdf'), onefile = FALSE)
# Initialize the plot, saving as object 'p' and specifying the plot type as 'geom_point'

p <- ggplot(df, aes(x = log2FoldChange, y = -log10(padj))) +
    geom_point(shape = 21, colour= 'darkgrey', size = 1) +
    theme_classic() +
    xlab('Log2 fold-change') + ylab('-Log10 adjusted p-value')

# Add threshold lines
p <- p +
    geom_vline(
        xintercept = c(0, -log2(fc), log2(fc)),
        linetype = c(1, 2, 2)) +
    geom_hline(
        yintercept = -log10(pval),
        linetype = 2)

# Add Title that includes comparison name
p <- p + ggtitle(as.character(Comparison))

print(p)
dev.off()
## quartz_off_screen 
##                 2
p


Click for color coded volcano plot

Now we need to subset our data to label the datapoints (genes) that pass our thresholds.

df$dot <- rep(3, nrow(df))
df$dot[which(df$padj <= pval & df$log2FoldChange < 0 & abs(df$log2FoldChange) >= log2(fc))] = 2
df$dot[which(df$padj <= pval & df$log2FoldChange > 0 & abs(df$log2FoldChange) >= log2(fc))] = 1
df$sig <- df$dot

#take top 5 up, down, then combine, assign label
top <- rbind(head(subset(df, df$dot == 1), 5),head(subset(df, df$dot == 2), 5))
top$label <- top$id
df <- merge(x = df, y = top[,c('id','label')], by = "id", all.x = TRUE)

#count the number of significan up and down genes, assign value for legend
df$dot <- factor(df$dot,levels = c(1,2,3), labels = c(paste0('Up: ', sum(df$dot == 1)),paste0('Down: ', sum(df$dot == 2)),'NS'))

Once we’ve setup the annotations we need, we can proceed with generating the volcano plot.

pdf(file = paste0(plotPath,'VolcanoPlot_Fancier', Comparison, '.pdf'), onefile = FALSE)

p <- ggplot(df, aes(x = log2FoldChange, y = -log10(padj))) +
    geom_point(aes(color = df$dot), size = 1) +
    theme_classic() +
    xlab('Log2 fold-change') + ylab('-Log10 adjusted p-value')
# specify colors for up-/down-/nonsignificant genes
p <- p + scale_color_manual(name = '', values=c('#B31B21', '#1465AC', 'darkgray'))
# add threshol lines
p <- p +
    geom_vline(
        xintercept = c(0, -log2(fc), log2(fc)),
        linetype = c(1, 2, 2),
        color = c('black', 'black', 'black')) +
    geom_hline(
        yintercept = -log10(pval),
        linetype = 2,
        color = 'black')
# Add Title that includes comparison name
p <- p + ggtitle(as.character(Comparison))

print(p)
dev.off()
## quartz_off_screen 
##                 2
p


For additional visualizations for our DE results, we included some example code in the Bonus Content module and this HBC tutorial includes some nice examples.

2.2 Summarizing our DE results

To generate a general summary of the DE results, we can use the summary function to generate a basic summary by DESeq2.

However, we can also use conditional statements to determine the number of genes that pass our thresholds for each comparison, which might be more informative.

[Question]: How would we identify the number of genes with adjusted p-values < 0.05 and a fold-change above 1.5 (or below -1.5)?

#summary(res_WT)
sum(res_WT$padj < 0.05 & abs(res_WT$log2FoldChange) >= log2(1.5), na.rm = TRUE)
## [1] 735
sum(res_Tx$padj < 0.05 & abs(res_Tx$log2FoldChange) >= log2(1.5), na.rm = TRUE)
## [1] 1151

How do the number of DE genes compare to what we observed from the PCA plots?

2.2.0.1 Choosing thresholds

Thresholding on adjusted p-values < 0.05 is a standard threshold, but depending on the research question and/or how the results will be used, other thresholds might be reasonable.

There is a nice Biostart post that discusses choosing adjusted p-value thresholds, including cases where a more relaxed threshold might be appropriate and (some heated) discussion of the dangers of adjusting the choosen threshold after running an analysis. Additionally, there is a Dalmon et al 2012 paper about p-value and fold-change thresholds for microarray data that may help provide some context.

2.2.1 Subsetting significant genes

It can be useful to annotate our results table with additional information to make them easier to interpret, such as adding additional gene information or better summarizing our DE results.

Click for code to generate a table of only DE genes

You may be interested in identifying only the genes that pass your significance thresholds. A useful way to do this is to conditionally subset your results.

Note: The tidyverse functions you learned in Software Carpentry could also be alternatively used here.

res_sig <- na.omit(res_WT)
res_sig <- res_sig[which(res_sig$padj < 0.05 & abs(res_sig$log2FoldChange) >= log2(1.5)), ]
head(res_sig)
For more details about sub-setting tables in R, we recommend reviewing the Data Carpentry manipulating and analyzing data module. We can also annotate our results to include a column that identifies our significant genes.


We can also add an annotation column to indicate if a gene passes the padj and log2FoldChange thresholds we set to call DE genes.

res_WT_DE <- res_WT # copy table

# add a column and assign all genes are non-significance
res_WT_DE$Call <- rep(FALSE, length(res_WT$baseMean))

# change 'Call' column to TRUE if meets conditions for significant differences
res_WT_DE[which(!is.na(res_WT_DE$padj) & res_WT_DE$padj < 0.05 & abs(res_WT_DE$log2FoldChange) >= log2(1.5)), ]$Call <- TRUE

# reorder table to rank significant genes at the top
res_WT_DE <- res_WT_DE[order(-res_WT_DE$Call),]
head(res_WT_DE)

2.3 Adding genome annotations

Since, gene symbols can change over time or be ambiguous we use and recommend using the EMSEMBL reference genome and ENSEMBL ids for alignmentsand we’ve been working with tables and data where all genes are labeled only by their long ENSEML ID. However, this can make it difficult to quickly look at results for genes of interest.

2.3.1 Gene symbols versus Gene IDs

Luckily, Bioconductor provides many tools and resources to facilitate access to genomic annotation resources.

To start, we will first load the biomaRt library & choose what reference we want to access. For a more detailed walk through of using biomaRt, this training module might be useful, including what to do when annotations are not 1:1 mappings.

2.4 [Breakout Exercise 2] - Adding gene symbol annotations

First, we’ll load the biomaRt library and then specify the dataset and biomart we want to use, where the values for dataset and biomart are based on the biomaRt vignette on how to build a query and the biomaRt vignette on accessing ensembl

library("biomaRt")

ensembl <- useEnsembl(dataset = "mmusculus_gene_ensembl", biomart='ensembl')

Note that this process takes some time and will take up a larger amount of working memory so proceed with caution if you try to run these commands on a laptop with less than 4G of memory

To identify possible filters to restrict our data, we can use the listFilters function. To identify the attributes we want to retrive, we can use the listAttributes function. The best approach is to use list or search functions to help narrow down the available options.

head(listFilters(mart = ensembl), n = 20)
head(listAttributes(ensembl), n = 30)

We can access additional genomic annotations using the bioMart package. To identify we’ll structure our ‘query’ or search of the bioMart resources to use the ENSEMBL id from our alignment to add the gene symbols and gene description for each gene.

## wasn't working for my version of biomaRt but might work for you
GeneKey <- getBM(attributes=c('ensembl_gene_id', 'external_gene_name'),
      filters = 'ensembl_gene_id',
      values = row.names(assay(dds)),
      mart = ensembl) # will take some time to run

## check the key
head(GeneKey)
##      ensembl_gene_id external_gene_name
## 1 ENSMUSG00000000001              Gnai3
## 2 ENSMUSG00000000028              Cdc45
## 3 ENSMUSG00000000031                H19
## 4 ENSMUSG00000000037              Scml2
## 5 ENSMUSG00000000049               Apoh
## 6 ENSMUSG00000000056               Narf

Now that we have the ENSEMBL information and a gene symbol to match to our results, we can procced to the breakout exercise to annotate our DE table(s)

head(res_WT_anno)
##                genes external_gene_name    baseMean log2FoldChange      lfcSE        stat
## 1 ENSMUSG00000000001              Gnai3 6255.632164   -0.014024321 0.09301588 -0.15077341
## 2 ENSMUSG00000000028              Cdc45 1337.874474    0.522421732 0.13599465  3.84148730
## 3 ENSMUSG00000000031                H19    3.773571   -1.156597290 1.60080258 -0.72251088
## 4 ENSMUSG00000000037              Scml2   27.563275   -0.279611867 0.47655119 -0.58674046
## 5 ENSMUSG00000000049               Apoh    2.256350    4.010718329 1.88912289  2.12305846
## 6 ENSMUSG00000000056               Narf 2194.251314   -0.008544091 0.17722204 -0.04821122
##         pvalue        padj
## 1 0.8801544640 0.960702559
## 2 0.0001222911 0.003948693
## 3 0.4699804359          NA
## 4 0.5573780290 0.804647138
## 5 0.0337489531          NA
## 6 0.9615479086 0.987888734
Notice that not all genes were annotated with an ENSEMBl gene id or gene description. While we are able to annotate our results, we should be very cautious as the gene symbol is not a good unique identifier plus we did not use a UCSC annotation resource so the HUGO gene symbol may not always match. However, this code is similar to the steps needed to annotate ENSEMBL id based results, like what would have been generated from yesterday's alignments, with more interpretable gene symbols.

Note: For additional information regarding bioMart, please consult the ENSEMBL bioMart vignette or the broader Bioconductor Annotation Resources vignette.

3 Outputting results to file

A key aspect of our analysis is preserving the relevant datasets for both our records and for downstream applications, such as functional enrichments.

First, we’ll setup a new directory for our output tables.

dir.create("tables", showWarnings = FALSE)

3.1 DE results table

Next we’ll write out our DE results for the KD comparison to file, since we added additional information to that table.

write.csv(res_WT_anno,
          row.names = FALSE,
          na = ".",
          file="tables/DEResults_ko.control_v_wt.control_annotated.csv")

If we generated another comparision, we could repeat our annotations or write the DE results directly to file.

write.csv(res_Tx,
          row.names = FALSE,
          na = ".",
          file="tables/DEResults_ko.Tx_v_wt.Tx.csv")

3.2 Count tables

The most relevant count tables are the raw, filtered count table that we used as the input for our analysis and the rlog normalized count table that we used for our quality control visualizations.

To output the raw counts, we will need to use the counts function to access the count table from within its larger DESeqDataSet object.

write.csv(counts(dds, normalized = FALSE), file="tables/DESeq2_raw_counts.csv")

Then we’ll output the rlog count table, using the assay function to access the normalized count table from within its larger DESeqDataSet object.

write.csv(assay(rld), file="tables/DESeq2_rlogNormalized_counts.csv")

4 Summary

In this section, we:

  • Generated a volcano plot for our differential expression results
  • Summarized our differential expression results
  • Discussed choosing thresholds
  • Annotated our tables of results to map gene IDs to gene symbols
  • Saved our results to file

5 Key takeaways

Overall, we’ve run through most of the building blocks needed to run a differential expression analysis and hopefully built up a better understanding of how differential expression comparisons work, particularly how experimental design can impact our results.

What to consider moving forward:

  • How can I control for technical variation in my experimental design?
  • How much variation is expected with a treatment group?
  • What is my RNA quality, and how can that be optimized?
  • Are there quality concerns for my sequencing data?
  • What comparisons are relevant to my biological question?
  • Are there covariates that should be considered?
  • What will a differential expression analysis tell me?

Let’s pause here for general questions


6 Extension - what can we do with our DE results?

Now that we have our DE results, have we address the biological question relevant to the authors of the original paper? On the one hand, yes - we now have two tables of genes that are impacted by change in Mov10 expression. But with two lists of genes alone, it can be difficult to find patterns or understand broader biological impacts.

What if we wanted to find out what genes were signifant in both comparisons? The intersect function, such as implimented as part of the dplyr package would be useful to identify shared significant genes. A venn diagram could be a way to visualize these overlaps.

A way to determine possible broader biological interpretations from the observed DE results, is functional enrichments. There are many options, such as some included in this discussion thread. Other common functional enrichments approaches are gene set enrichment analysis, aka GSEA, Database for Annotation, Visualization and Integrated Discovery, aka DAVID, Ingenity, and [iPathway Guide]

The University of Michigan has license and support for additional tools, such as Cytoscape, so we recommend reaching out to staff with Taubman Library to learn more about resources that might be application toyour research.


9 Session Info

sessionInfo()
## R version 4.1.1 (2021-08-10)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Catalina 10.15.7
## 
## Matrix products: default
## BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] biomaRt_2.50.0              rmarkdown_2.11              data.table_1.14.2          
##  [4] RColorBrewer_1.1-2          pheatmap_1.0.12             ggrepel_0.9.1              
##  [7] dplyr_1.0.7                 tidyr_1.1.4                 ggplot2_3.3.5              
## [10] DESeq2_1.34.0               SummarizedExperiment_1.24.0 Biobase_2.54.0             
## [13] MatrixGenerics_1.6.0        matrixStats_0.61.0          GenomicRanges_1.46.0       
## [16] GenomeInfoDb_1.30.0         IRanges_2.28.0              S4Vectors_0.32.2           
## [19] BiocGenerics_0.40.0        
## 
## loaded via a namespace (and not attached):
##  [1] bitops_1.0-7           bit64_4.0.5            filelock_1.0.2         progress_1.2.2        
##  [5] httr_1.4.2             tools_4.1.1            utf8_1.2.2             R6_2.5.1              
##  [9] DBI_1.1.1              colorspace_2.0-2       withr_2.4.2            tidyselect_1.1.1      
## [13] prettyunits_1.1.1      bit_4.0.4              curl_4.3.2             compiler_4.1.1        
## [17] cli_3.0.1              xml2_1.3.2             DelayedArray_0.20.0    labeling_0.4.2        
## [21] scales_1.1.1           genefilter_1.76.0      rappdirs_0.3.3         stringr_1.4.0         
## [25] digest_0.6.28          XVector_0.34.0         pkgconfig_2.0.3        htmltools_0.5.2       
## [29] dbplyr_2.1.1           fastmap_1.1.0          highr_0.9              rlang_0.4.11          
## [33] rstudioapi_0.13        RSQLite_2.2.8          jquerylib_0.1.4        generics_0.1.0        
## [37] farver_2.1.0           BiocParallel_1.28.0    RCurl_1.98-1.5         magrittr_2.0.1        
## [41] GenomeInfoDbData_1.2.7 Matrix_1.3-4           Rcpp_1.0.7             munsell_0.5.0         
## [45] fansi_0.5.0            lifecycle_1.0.1        stringi_1.7.5          yaml_2.2.1            
## [49] zlibbioc_1.40.0        BiocFileCache_2.2.0    grid_4.1.1             blob_1.2.2            
## [53] parallel_4.1.1         crayon_1.4.1           lattice_0.20-44        Biostrings_2.62.0     
## [57] splines_4.1.1          annotate_1.72.0        hms_1.1.1              KEGGREST_1.34.0       
## [61] locfit_1.5-9.4         knitr_1.36             pillar_1.6.3           geneplotter_1.72.0    
## [65] XML_3.99-0.8           glue_1.4.2             evaluate_0.14          png_0.1-7             
## [69] vctrs_0.3.8            gtable_0.3.0           purrr_0.3.4            assertthat_0.2.1      
## [73] cachem_1.0.6           xfun_0.26              xtable_1.8-4           survival_3.2-11       
## [77] tibble_3.1.5           AnnotationDbi_1.56.2   memoise_2.0.0          ellipsis_0.3.2

These materials have been adapted and extended from materials listed above. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.

LS0tCnRpdGxlOiAiRGF5IDMgLSBNb2R1bGUgMTE6IERFIFZpc3VhbGl6YXRpb25zIGFuZCBHZW5lIEFubm90YXRpb25zIgphdXRob3I6ICJVTSBCaW9pbmZvcm1hdGljcyBDb3JlIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAgICAgICBodG1sX2RvY3VtZW50OgogICAgICAgICAgICBpbmNsdWRlczoKICAgICAgICAgICAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgICAgICAgICAgdGhlbWU6IHBhcGVyCiAgICAgICAgICAgIHRvYzogdHJ1ZQogICAgICAgICAgICB0b2NfZGVwdGg6IDQKICAgICAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgICAgICAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgICAgICAgICBtYXJrZG93bjogR0ZNCiAgICAgICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHl7IC8qIE5vcm1hbCAgKi8KICAgICAgZm9udC1zaXplOiAxNHB0OwogIH0KcHJlIHsKICBmb250LXNpemU6IDEycHQKfQpjb2RlLnJ7CiAgZm9udC1zaXplOiAxMnB0Owp9Cjwvc3R5bGU+Cgo8IS0tLSBBbGxvdyB0aGUgcGFnZSB0byBiZSB3aWRlciAtLS0+CjxzdHlsZT4KICAgIGJvZHkgLm1haW4tY29udGFpbmVyIHsKICAgICAgICBtYXgtd2lkdGg6IDEyMDBweDsKICAgIH0KPC9zdHlsZT4KPiAjIE9iamVjdGl2ZXM6ICAgIAo+ICogVW5kZXJzdGFuZCBhZHZhbnRhZ2VzIG9mIHVzaW5nIGdlbmUgaWRzIHdoZW4gYW5hbHl6aW5nIGRhdGEuICAgIAo+ICogR2l2ZW4gYSBsaXN0IG9mIEVOU0VNQkwgZ2VuZSBpZHMsIGFkZCBnZW5lIHN5bWJvbHMgYW5kIEVudHJleiBhY2Nlc3Npb25zLiAgICAKPiAqIEdlbmVyYXRlIGNvbW1vbiB2aXN1YWxpemF0aW9ucyBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gY29tcGFyaXNvbnMgICAgCj4gKiBVbmRlcnN0YW5kIGNob29zaW5nIHRocmVzaG9sZHMgZm9yIGNhbGxpbmcgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzICAgIAo+ICogRGlzY3VzcyBvcHRpb25zIGZvciBmdW5jdGlvbmFsIGVucmljaG1lbnRzICAgIAoKYGBge3IgTW9kdWxlcywgZXZhbD1UUlVFLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsb2FkKCJyZGF0YS9SdW5uaW5nRGF0YS5SRGF0YSIpCmBgYAoKIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBXb3JrZmxvdwoKSGVyZSB3ZSB3aWxsIGdlbmVyYXRlIHN1bW1hcnkgZmlndXJlcyBmb3Igb3VyIHJlc3VsdHMgYW5kIGFubm90YXRlIG91ciBERSB0YWJsZXMuCgohW10oLi9pbWFnZXMvd2F5ZmluZGVyL3dheWZpbmRlci0wNi5wbmcpe3dpZHRoPTc1JX0KCioqVE9ETzogZG91YmxlIGNoZWNrIHJlZmVyZW5jZXMgdG8gYW5ub3RhdGVkIHRhYmxlIHByaW9yIHRvIHJ1bm5pbmcgYW5ub3RhdGlvbnMqKgoKLS0tCgojIFZpc3VhbGl6aW5nIERFIHJlc3VsdHMKClBhcnQgb2YgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgaXMgZ2VuZXJhdGluZyB2aXN1YWxpemF0aW9ucyB0byBzaGFyZSBvdXIgcmVzdWx0cy4gV2hpbGUgdGhlIERFU2VxMiB0dXRvcmlhbCBwcm92aWRlcyBbZXhhbXBsZXMgb2Ygb3RoZXIgdmlzdWFsaXphdGlvbnNdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCNleHBsb3JpbmctYW5kLWV4cG9ydGluZy1yZXN1bHRzKSwgYSBjb21tb24gdmlzdWFsaXphdGlvbiB0byBzdW1tYXJpemUgREUgY29tcGFyaXNvbnMgYXJlIFt2b2xjYW5vIHBsb3RzXShodHRwOi8vcmVzb3VyY2VzLnFpYWdlbmJpb2luZm9ybWF0aWNzLmNvbS9tYW51YWxzL2NsY2dlbm9taWNzd29ya2JlbmNoLzc1Mi9pbmRleC5waHA/bWFudWFsPVZvbGNhbm9fcGxvdHNfaW5zcGVjdGluZ19yZXN1bHRfc3RhdGlzdGljYWxfYW5hbHlzaXMuaHRtbCkuCgojIyBHZW5lcmF0aW5nIFZvbGNhbm8gcGxvdHMKCkFzIGRlc2NyaWJlZCBieSB0aGlzIFtHYWxheHkgcHJvamVjdCB0dXRvcmlhbF0oaHR0cHM6Ly9nYWxheHlwcm9qZWN0LmdpdGh1Yi5pby90cmFpbmluZy1tYXRlcmlhbC90b3BpY3MvdHJhbnNjcmlwdG9taWNzL3R1dG9yaWFscy9ybmEtc2VxLXZpei13aXRoLXZvbGNhbm9wbG90L3R1dG9yaWFsLmh0bWwpLCBhIHZvbGNhbm8gcGxvdCBpcyBhIHR5cGUgb2Ygc2NhdHRlcnBsb3QgdGhhdCBzaG93cyBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UgKGFkanVzdGVkIHAtdmFsdWUpIHZlcnN1cyBtYWduaXR1ZGUgb2YgY2hhbmdlIChmb2xkIGNoYW5nZSkuIEluIGEgdm9sY2FubyBwbG90LCB0aGUgbW9zdCB1cHJlZ3VsYXRlZCBnZW5lcyBhcmUgdG93YXJkcyB0aGUgcmlnaHQsIHRoZSBtb3N0IGRvd25yZWd1bGF0ZWQgZ2VuZXMgYXJlIHRvd2FyZHMgdGhlIGxlZnQsIGFuZCB0aGUgbW9zdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGdlbmVzIGFyZSB0b3dhcmRzIHRoZSB0b3AuCgpGaXJzdCwgd2UgbmVlZCB0byBzZXQgdGhyZXNob2xkcyBmb3IgZGV0ZXJtaW5pbmcgc2lnbmlmaWNhbnQgZ2VuZXMuIEEgcmVhc29uYWJsZSB0aHJlc2hvbGQgd291bGQgYmUgYSBmb2xkLWNoYW5nZSBvZiBsZXNzIHRoYW4gLTEuNSBvciBncmVhdGVyIHRoYW4gMS41IGFuZCBhbiBhZGp1c3RlZCBwdmFsdWUgbGVzcyB0aGFuIDAuMDUuCmBgYHtyIFBsb3RTZXR1cDF9CmZjIDwtIDEuNQpwdmFsIDwtIDAuMDUKYGBgCgpUaGVuLCB3ZSBuZWVkIHRvIHNlcCB1cCBzb21lIG9iamVjdHMgdG8gcnVuIG91ciBwbG90dGluZyBjb2RlLCBjcmVhdGluZyBhIG5ldyBvYmplY3QgdGhhdCB3aWxsIGhhdmUgdGhlIHJpZ2h0IHNoYXBlIGZvciBwbG90dGluZywgbGFiZWxpbmcgb3VyIGNvbXBhcmlzb24gb2YgaW50ZXJlc3QsIGFuZCBzZXR0aW5nIHVwIG91ciBvdXRwdXQgZGlyZWN0b3J5CmBgYHtyIFBsb3RTZXR1cDJ9CmRmPC0gcmVzX1dUW29yZGVyKHJlc19XVCRwYWRqKSxdICNzZWxlY3Qgb3VyIGRhdGEgb2YgaW50ZXJlc3QKZGYgPC0gYXMuZGF0YS5mcmFtZShkZikgI2NvbnZlcnQgb3VyIG9iamVjdCB0eXBlCmRmIDwtIGNiaW5kKCJpZCIgPSByb3cubmFtZXMoZGYpLCBkZikgI3NldCByb3duYW1lcyB0byB2YWxpZCBjb2x1bW4Kc3RyKGRmKQoKQ29tcGFyaXNvbiA8LSAia28uY29udHJvbF92X3d0LmNvbnRyb2wiCgpgYGAKCk9uY2Ugd2UndmUgc2V0dXAgdGhlIGFubm90YXRpb25zIHdlIG5lZWQsIHdlIGNhbiBwcm9jZWVkIHdpdGggZ2VuZXJhdGluZyB0aGUgdm9sY2FubyBwbG90LCBpbmNsdWRpbmcgb3VyIHRocmVzaG9sZHMuCmBgYHtyIFZvbGNhbm9QbG90LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwZGYoZmlsZSA9IHBhc3RlMChwbG90UGF0aCwnVm9sY2Fub1Bsb3RfJywgQ29tcGFyaXNvbiwgJy5wZGYnKSwgb25lZmlsZSA9IEZBTFNFKQojIEluaXRpYWxpemUgdGhlIHBsb3QsIHNhdmluZyBhcyBvYmplY3QgJ3AnIGFuZCBzcGVjaWZ5aW5nIHRoZSBwbG90IHR5cGUgYXMgJ2dlb21fcG9pbnQnCgpwIDwtIGdncGxvdChkZiwgYWVzKHggPSBsb2cyRm9sZENoYW5nZSwgeSA9IC1sb2cxMChwYWRqKSkpICsKICAgIGdlb21fcG9pbnQoc2hhcGUgPSAyMSwgY29sb3VyPSAnZGFya2dyZXknLCBzaXplID0gMSkgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIHhsYWIoJ0xvZzIgZm9sZC1jaGFuZ2UnKSArIHlsYWIoJy1Mb2cxMCBhZGp1c3RlZCBwLXZhbHVlJykKCiMgQWRkIHRocmVzaG9sZCBsaW5lcwpwIDwtIHAgKwogICAgZ2VvbV92bGluZSgKICAgICAgICB4aW50ZXJjZXB0ID0gYygwLCAtbG9nMihmYyksIGxvZzIoZmMpKSwKICAgICAgICBsaW5ldHlwZSA9IGMoMSwgMiwgMikpICsKICAgIGdlb21faGxpbmUoCiAgICAgICAgeWludGVyY2VwdCA9IC1sb2cxMChwdmFsKSwKICAgICAgICBsaW5ldHlwZSA9IDIpCgojIEFkZCBUaXRsZSB0aGF0IGluY2x1ZGVzIGNvbXBhcmlzb24gbmFtZQpwIDwtIHAgKyBnZ3RpdGxlKGFzLmNoYXJhY3RlcihDb21wYXJpc29uKSkKCnByaW50KHApCmRldi5vZmYoKQpwCmBgYAoKPGJyPgoKPGRldGFpbHM+CiAgICA8c3VtbWFyeT4qQ2xpY2sgZm9yIGNvbG9yIGNvZGVkIHZvbGNhbm8gcGxvdCo8L3N1bW1hcnk+CiAgICBOb3cgd2UgbmVlZCB0byBbc3Vic2V0IG91ciBkYXRhXShodHRwczovL3d3dy5zdGF0bWV0aG9kcy5uZXQvbWFuYWdlbWVudC9zdWJzZXQuaHRtbCkgdG8gbGFiZWwgdGhlIGRhdGFwb2ludHMgKGdlbmVzKSB0aGF0IHBhc3Mgb3VyIHRocmVzaG9sZHMuCmBgYHtyIFBsb3RTZXR1cDJhLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkZiRkb3QgPC0gcmVwKDMsIG5yb3coZGYpKQpkZiRkb3Rbd2hpY2goZGYkcGFkaiA8PSBwdmFsICYgZGYkbG9nMkZvbGRDaGFuZ2UgPCAwICYgYWJzKGRmJGxvZzJGb2xkQ2hhbmdlKSA+PSBsb2cyKGZjKSldID0gMgpkZiRkb3Rbd2hpY2goZGYkcGFkaiA8PSBwdmFsICYgZGYkbG9nMkZvbGRDaGFuZ2UgPiAwICYgYWJzKGRmJGxvZzJGb2xkQ2hhbmdlKSA+PSBsb2cyKGZjKSldID0gMQpkZiRzaWcgPC0gZGYkZG90CgojdGFrZSB0b3AgNSB1cCwgZG93biwgdGhlbiBjb21iaW5lLCBhc3NpZ24gbGFiZWwKdG9wIDwtIHJiaW5kKGhlYWQoc3Vic2V0KGRmLCBkZiRkb3QgPT0gMSksIDUpLGhlYWQoc3Vic2V0KGRmLCBkZiRkb3QgPT0gMiksIDUpKQp0b3AkbGFiZWwgPC0gdG9wJGlkCmRmIDwtIG1lcmdlKHggPSBkZiwgeSA9IHRvcFssYygnaWQnLCdsYWJlbCcpXSwgYnkgPSAiaWQiLCBhbGwueCA9IFRSVUUpCgojY291bnQgdGhlIG51bWJlciBvZiBzaWduaWZpY2FuIHVwIGFuZCBkb3duIGdlbmVzLCBhc3NpZ24gdmFsdWUgZm9yIGxlZ2VuZApkZiRkb3QgPC0gZmFjdG9yKGRmJGRvdCxsZXZlbHMgPSBjKDEsMiwzKSwgbGFiZWxzID0gYyhwYXN0ZTAoJ1VwOiAnLCBzdW0oZGYkZG90ID09IDEpKSxwYXN0ZTAoJ0Rvd246ICcsIHN1bShkZiRkb3QgPT0gMikpLCdOUycpKQpgYGAKCiAgICBPbmNlIHdlJ3ZlIHNldHVwIHRoZSBhbm5vdGF0aW9ucyB3ZSBuZWVkLCB3ZSBjYW4gcHJvY2VlZCB3aXRoIGdlbmVyYXRpbmcgdGhlIHZvbGNhbm8gcGxvdC4KCmBgYHtyIFZvbGNhbm9QbG90MiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGRmKGZpbGUgPSBwYXN0ZTAocGxvdFBhdGgsJ1ZvbGNhbm9QbG90X0ZhbmNpZXInLCBDb21wYXJpc29uLCAnLnBkZicpLCBvbmVmaWxlID0gRkFMU0UpCgpwIDwtIGdncGxvdChkZiwgYWVzKHggPSBsb2cyRm9sZENoYW5nZSwgeSA9IC1sb2cxMChwYWRqKSkpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZGYkZG90KSwgc2l6ZSA9IDEpICsKICAgIHRoZW1lX2NsYXNzaWMoKSArCiAgICB4bGFiKCdMb2cyIGZvbGQtY2hhbmdlJykgKyB5bGFiKCctTG9nMTAgYWRqdXN0ZWQgcC12YWx1ZScpCiMgc3BlY2lmeSBjb2xvcnMgZm9yIHVwLS9kb3duLS9ub25zaWduaWZpY2FudCBnZW5lcwpwIDwtIHAgKyBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICcnLCB2YWx1ZXM9YygnI0IzMUIyMScsICcjMTQ2NUFDJywgJ2RhcmtncmF5JykpCiMgYWRkIHRocmVzaG9sIGxpbmVzCnAgPC0gcCArCiAgICBnZW9tX3ZsaW5lKAogICAgICAgIHhpbnRlcmNlcHQgPSBjKDAsIC1sb2cyKGZjKSwgbG9nMihmYykpLAogICAgICAgIGxpbmV0eXBlID0gYygxLCAyLCAyKSwKICAgICAgICBjb2xvciA9IGMoJ2JsYWNrJywgJ2JsYWNrJywgJ2JsYWNrJykpICsKICAgIGdlb21faGxpbmUoCiAgICAgICAgeWludGVyY2VwdCA9IC1sb2cxMChwdmFsKSwKICAgICAgICBsaW5ldHlwZSA9IDIsCiAgICAgICAgY29sb3IgPSAnYmxhY2snKQojIEFkZCBUaXRsZSB0aGF0IGluY2x1ZGVzIGNvbXBhcmlzb24gbmFtZQpwIDwtIHAgKyBnZ3RpdGxlKGFzLmNoYXJhY3RlcihDb21wYXJpc29uKSkKCnByaW50KHApCmRldi5vZmYoKQpwCmBgYAoKPC9kZXRhaWxzPgoKPGJyPgoKRm9yIGFkZGl0aW9uYWwgdmlzdWFsaXphdGlvbnMgZm9yIG91ciBERSByZXN1bHRzLCB3ZSBpbmNsdWRlZCBzb21lIGV4YW1wbGUgY29kZSBpbiB0aGUgQm9udXMgQ29udGVudCBtb2R1bGUgYW5kIHRoaXMgW0hCQyB0dXRvcmlhbF0oaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDZfREdFX3Zpc3VhbGl6aW5nX3Jlc3VsdHMuaHRtbCkgaW5jbHVkZXMgc29tZSBuaWNlIGV4YW1wbGVzLgoKIyMgU3VtbWFyaXppbmcgb3VyIERFIHJlc3VsdHMKClRvIGdlbmVyYXRlIGEgZ2VuZXJhbCBzdW1tYXJ5IG9mIHRoZSBERSByZXN1bHRzLCB3ZSBjYW4gdXNlIHRoZSBgc3VtbWFyeWAgZnVuY3Rpb24gdG8gZ2VuZXJhdGUgYSBiYXNpYyBzdW1tYXJ5IGJ5IERFU2VxMi4KCkhvd2V2ZXIsIHdlIGNhbiBhbHNvIHVzZSBjb25kaXRpb25hbCBzdGF0ZW1lbnRzIHRvIGRldGVybWluZSB0aGUgbnVtYmVyIG9mIGdlbmVzIHRoYXQgcGFzcyBvdXIgdGhyZXNob2xkcyBmb3IgZWFjaCBjb21wYXJpc29uLCB3aGljaCBtaWdodCBiZSBtb3JlIGluZm9ybWF0aXZlLgoKKltRdWVzdGlvbl06IEhvdyB3b3VsZCB3ZSBpZGVudGlmeSB0aGUgbnVtYmVyIG9mIGdlbmVzIHdpdGggYWRqdXN0ZWQgcC12YWx1ZXMgPCAwLjA1IGFuZCBhIGZvbGQtY2hhbmdlIGFib3ZlIDEuNSAob3IgYmVsb3cgLTEuNSk/KgoKYGBge3IgU3RhdFNpZ0dlbmVzMn0KI3N1bW1hcnkocmVzX1dUKQpzdW0ocmVzX1dUJHBhZGogPCAwLjA1ICYgYWJzKHJlc19XVCRsb2cyRm9sZENoYW5nZSkgPj0gbG9nMigxLjUpLCBuYS5ybSA9IFRSVUUpCnN1bShyZXNfVHgkcGFkaiA8IDAuMDUgJiBhYnMocmVzX1R4JGxvZzJGb2xkQ2hhbmdlKSA+PSBsb2cyKDEuNSksIG5hLnJtID0gVFJVRSkKYGBgCkhvdyBkbyB0aGUgbnVtYmVyIG9mIERFIGdlbmVzIGNvbXBhcmUgdG8gd2hhdCB3ZSBvYnNlcnZlZCBmcm9tIHRoZSBQQ0EgcGxvdHM/CgojIyMjIENob29zaW5nIHRocmVzaG9sZHMKClRocmVzaG9sZGluZyBvbiBhZGp1c3RlZCBwLXZhbHVlcyA8IDAuMDUgaXMgYSBzdGFuZGFyZCB0aHJlc2hvbGQsIGJ1dCBkZXBlbmRpbmcgb24gdGhlIHJlc2VhcmNoIHF1ZXN0aW9uIGFuZC9vciBob3cgdGhlIHJlc3VsdHMgd2lsbCBiZSB1c2VkLCBvdGhlciB0aHJlc2hvbGRzIG1pZ2h0IGJlIHJlYXNvbmFibGUuIAoKVGhlcmUgaXMgYSBuaWNlIFtCaW9zdGFydCBwb3N0IHRoYXQgZGlzY3Vzc2VzIGNob29zaW5nIGFkanVzdGVkIHAtdmFsdWUgdGhyZXNob2xkc10oaHR0cHM6Ly93d3cuYmlvc3RhcnMub3JnL3AvMjA5MTE4LyksIGluY2x1ZGluZyBjYXNlcyB3aGVyZSBhIG1vcmUgcmVsYXhlZCB0aHJlc2hvbGQgbWlnaHQgYmUgYXBwcm9wcmlhdGUgYW5kIChzb21lIGhlYXRlZCkgZGlzY3Vzc2lvbiBvZiB0aGUgZGFuZ2VycyBvZiBhZGp1c3RpbmcgdGhlIGNob29zZW4gdGhyZXNob2xkIGFmdGVyIHJ1bm5pbmcgYW4gYW5hbHlzaXMuIEFkZGl0aW9uYWxseSwgdGhlcmUgaXMgYSBbRGFsbW9uIGV0IGFsIDIwMTJdKGh0dHBzOi8vYm1jYmlvaW5mb3JtYXRpY3MuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni8xNDcxLTIxMDUtMTMtUzItUzExKSBwYXBlciBhYm91dCBwLXZhbHVlIGFuZCBmb2xkLWNoYW5nZSB0aHJlc2hvbGRzIGZvciBtaWNyb2FycmF5IGRhdGEgdGhhdCBtYXkgaGVscCBwcm92aWRlIHNvbWUgY29udGV4dC4KCgojIyMgU3Vic2V0dGluZyBzaWduaWZpY2FudCBnZW5lcwoKSXQgY2FuIGJlIHVzZWZ1bCB0byBhbm5vdGF0ZSBvdXIgcmVzdWx0cyB0YWJsZSB3aXRoIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gdG8gbWFrZSB0aGVtIGVhc2llciB0byBpbnRlcnByZXQsIHN1Y2ggYXMgYWRkaW5nIGFkZGl0aW9uYWwgZ2VuZSBpbmZvcm1hdGlvbiBvciBiZXR0ZXIgc3VtbWFyaXppbmcgb3VyIERFIHJlc3VsdHMuCgoKPGRldGFpbHM+CiAgICA8c3VtbWFyeT4qQ2xpY2sgZm9yIGNvZGUgdG8gZ2VuZXJhdGUgYSB0YWJsZSBvZiBvbmx5IERFIGdlbmVzKjwvc3VtbWFyeT4KICAgIFlvdSBtYXkgYmUgaW50ZXJlc3RlZCBpbiBpZGVudGlmeWluZyBvbmx5IHRoZSBnZW5lcyB0aGF0IHBhc3MgeW91ciBzaWduaWZpY2FuY2UgdGhyZXNob2xkcy4gQSB1c2VmdWwgd2F5IHRvIGRvIHRoaXMgaXMgdG8gY29uZGl0aW9uYWxseSBzdWJzZXQgeW91ciByZXN1bHRzLgoKICAgICpOb3RlOiBUaGUgdGlkeXZlcnNlIGZ1bmN0aW9ucyB5b3UgbGVhcm5lZCBpbiBTb2Z0d2FyZSBDYXJwZW50cnkgY291bGQgYWxzbyBiZSBhbHRlcm5hdGl2ZWx5IHVzZWQgaGVyZS4qCgpgYGB7ciBDb25kaXRpb25hbFN1YnNldCwgZXZhbD1GQUxTRX0KcmVzX3NpZyA8LSBuYS5vbWl0KHJlc19XVCkKcmVzX3NpZyA8LSByZXNfc2lnW3doaWNoKHJlc19zaWckcGFkaiA8IDAuMDUgJiBhYnMocmVzX3NpZyRsb2cyRm9sZENoYW5nZSkgPj0gbG9nMigxLjUpKSwgXQpoZWFkKHJlc19zaWcpCmBgYAogICAgRm9yIG1vcmUgZGV0YWlscyBhYm91dCBzdWItc2V0dGluZyB0YWJsZXMgaW4gUiwgd2UgcmVjb21tZW5kIHJldmlld2luZyB0aGUgW0RhdGEgQ2FycGVudHJ5IG1hbmlwdWxhdGluZyBhbmQgYW5hbHl6aW5nIGRhdGEgbW9kdWxlXShodHRwczovL2RhdGFjYXJwZW50cnkub3JnL1ItZWNvbG9neS1sZXNzb24vMDMtZHBseXIuaHRtbCkuCiAgICBXZSBjYW4gYWxzbyBhbm5vdGF0ZSBvdXIgcmVzdWx0cyB0byBpbmNsdWRlIGEgY29sdW1uIHRoYXQgaWRlbnRpZmllcyBvdXIgc2lnbmlmaWNhbnQgZ2VuZXMuCjwvZGV0YWlscz4KCjxicj4KCldlIGNhbiBhbHNvIGFkZCBhbiBhbm5vdGF0aW9uIGNvbHVtbiB0byBpbmRpY2F0ZSBpZiBhIGdlbmUgcGFzc2VzIHRoZSBgcGFkamAgYW5kIGBsb2cyRm9sZENoYW5nZWAgdGhyZXNob2xkcyB3ZSBzZXQgdG8gY2FsbCBERSBnZW5lcy4KYGBge3IgQ29uZGl0aW9uYWxBbm5vLCBldmFsPUZBTFNFfQpyZXNfV1RfREUgPC0gcmVzX1dUICMgY29weSB0YWJsZQoKIyBhZGQgYSBjb2x1bW4gYW5kIGFzc2lnbiBhbGwgZ2VuZXMgYXJlIG5vbi1zaWduaWZpY2FuY2UKcmVzX1dUX0RFJENhbGwgPC0gcmVwKEZBTFNFLCBsZW5ndGgocmVzX1dUJGJhc2VNZWFuKSkKCiMgY2hhbmdlICdDYWxsJyBjb2x1bW4gdG8gVFJVRSBpZiBtZWV0cyBjb25kaXRpb25zIGZvciBzaWduaWZpY2FudCBkaWZmZXJlbmNlcwpyZXNfV1RfREVbd2hpY2goIWlzLm5hKHJlc19XVF9ERSRwYWRqKSAmIHJlc19XVF9ERSRwYWRqIDwgMC4wNSAmIGFicyhyZXNfV1RfREUkbG9nMkZvbGRDaGFuZ2UpID49IGxvZzIoMS41KSksIF0kQ2FsbCA8LSBUUlVFCgojIHJlb3JkZXIgdGFibGUgdG8gcmFuayBzaWduaWZpY2FudCBnZW5lcyBhdCB0aGUgdG9wCnJlc19XVF9ERSA8LSByZXNfV1RfREVbb3JkZXIoLXJlc19XVF9ERSRDYWxsKSxdCmhlYWQocmVzX1dUX0RFKQpgYGAKCgojIyBBZGRpbmcgZ2Vub21lIGFubm90YXRpb25zCgpTaW5jZSwgZ2VuZSBzeW1ib2xzIGNhbiBjaGFuZ2Ugb3ZlciB0aW1lIG9yIGJlIGFtYmlndW91cyB3ZSB1c2UgYW5kIHJlY29tbWVuZCB1c2luZyB0aGUgRU1TRU1CTCByZWZlcmVuY2UgZ2Vub21lIGFuZCBFTlNFTUJMIGlkcyBmb3IgYWxpZ25tZW50c2FuZCB3ZSd2ZSBiZWVuIHdvcmtpbmcgd2l0aCB0YWJsZXMgYW5kIGRhdGEgd2hlcmUgYWxsIGdlbmVzIGFyZSBsYWJlbGVkIG9ubHkgYnkgdGhlaXIgbG9uZyBFTlNFTUwgSUQuIEhvd2V2ZXIsIHRoaXMgY2FuIG1ha2UgaXQgZGlmZmljdWx0IHRvIHF1aWNrbHkgbG9vayBhdCByZXN1bHRzIGZvciBnZW5lcyBvZiBpbnRlcmVzdC4KCiMjIyBHZW5lIHN5bWJvbHMgdmVyc3VzIEdlbmUgSURzCgpMdWNraWx5LCBCaW9jb25kdWN0b3IgcHJvdmlkZXMgbWFueSB0b29scyBhbmQgcmVzb3VyY2VzIHRvIGZhY2lsaXRhdGUgYWNjZXNzIHRvIFtnZW5vbWljIGFubm90YXRpb24gcmVzb3VyY2VzXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC93b3JrZmxvd3MvdmlnbmV0dGVzL2Fubm90YXRpb24vaW5zdC9kb2MvQW5ub3RhdGlvbl9SZXNvdXJjZXMuaHRtbCkuCgpUbyBzdGFydCwgd2Ugd2lsbCBmaXJzdCBsb2FkIHRoZSBbYmlvbWFSdCBsaWJyYXJ5XShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvMy4xNC9iaW9jL2h0bWwvYmlvbWFSdC5odG1sKSAmIGNob29zZSB3aGF0IHJlZmVyZW5jZSB3ZSB3YW50IHRvIGFjY2Vzcy4gRm9yIGEgbW9yZSBkZXRhaWxlZCB3YWxrIHRocm91Z2ggb2YgdXNpbmcgYmlvbWFSdCwgW3RoaXMgdHJhaW5pbmcgbW9kdWxlXShodHRwczovL2Jpb2luZm9ybWF0aWNzLWNvcmUtc2hhcmVkLXRyYWluaW5nLmdpdGh1Yi5pby9jcnVrLXN1bW1lci1zY2hvb2wtMjAxOS9STkFzZXEvaHRtbC8wNV9Bbm5vdGF0aW9uX2FuZF9WaXN1YWxpc2F0aW9uLmh0bWwpIG1pZ2h0IGJlIHVzZWZ1bCwgaW5jbHVkaW5nIHdoYXQgdG8gZG8gd2hlbiBhbm5vdGF0aW9ucyBhcmUgbm90IDE6MSBtYXBwaW5ncy4KCiMjIFtCcmVha291dCBFeGVyY2lzZSAyXSAtIEFkZGluZyBnZW5lIHN5bWJvbCBhbm5vdGF0aW9ucwoKRmlyc3QsIHdlJ2xsIGxvYWQgdGhlIGBiaW9tYVJ0YCBsaWJyYXJ5IGFuZCB0aGVuIHNwZWNpZnkgdGhlIGRhdGFzZXQgYW5kIGJpb21hcnQgd2Ugd2FudCB0byB1c2UsIHdoZXJlIHRoZSB2YWx1ZXMgZm9yIGBkYXRhc2V0YCBhbmQgYGJpb21hcnRgIGFyZSBiYXNlZCBvbiB0aGUgW2Jpb21hUnQgdmlnbmV0dGUgb24gaG93IHRvIGJ1aWxkIGEgcXVlcnldKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL2Jpb21hUnQpIGFuZCB0aGUgW2Jpb21hUnQgdmlnbmV0dGUgb24gYWNjZXNzaW5nIGVuc2VtYmxdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy8zLjE0L2Jpb2MvdmlnbmV0dGVzL2Jpb21hUnQvaW5zdC9kb2MvYWNjZXNzaW5nX2Vuc2VtYmwuaHRtbCkKCmBgYHtyIFB1bGxtYXJ0LCB3YXJuaW5nPUZBTFNFIH0KbGlicmFyeSgiYmlvbWFSdCIpCgplbnNlbWJsIDwtIHVzZUVuc2VtYmwoZGF0YXNldCA9ICJtbXVzY3VsdXNfZ2VuZV9lbnNlbWJsIiwgYmlvbWFydD0nZW5zZW1ibCcpCgpgYGAKCgoqTm90ZSB0aGF0IHRoaXMgcHJvY2VzcyB0YWtlcyBzb21lIHRpbWUgYW5kIHdpbGwgdGFrZSB1cCBhIGxhcmdlciBhbW91bnQgb2Ygd29ya2luZyBtZW1vcnkgc28gcHJvY2VlZCB3aXRoIGNhdXRpb24gaWYgeW91IHRyeSB0byBydW4gdGhlc2UgY29tbWFuZHMgb24gYSBsYXB0b3Agd2l0aCBsZXNzIHRoYW4gNEcgb2YgbWVtb3J5KgoKClRvIGlkZW50aWZ5IHBvc3NpYmxlICoqZmlsdGVycyoqIHRvIHJlc3RyaWN0IG91ciBkYXRhLCB3ZSBjYW4gdXNlIHRoZSBgbGlzdEZpbHRlcnNgIGZ1bmN0aW9uLiBUbyBpZGVudGlmeSB0aGUgKiphdHRyaWJ1dGVzKiogd2Ugd2FudCB0byByZXRyaXZlLCB3ZSBjYW4gdXNlIHRoZSBgbGlzdEF0dHJpYnV0ZXNgIGZ1bmN0aW9uLiBUaGUgYmVzdCBhcHByb2FjaCBpcyB0byB1c2UgW2xpc3Qgb3Igc2VhcmNoIGZ1bmN0aW9uc10oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvYmlvbWFSdC9pbnN0L2RvYy9hY2Nlc3NpbmdfZW5zZW1ibC5odG1sI2hvdy10by1idWlsZC1hLWJpb21hcnQtcXVlcnkpIHRvIGhlbHAgbmFycm93IGRvd24gdGhlIGF2YWlsYWJsZSBvcHRpb25zLgpgYGB7ciBBZGRBbm5vdGF0aW9uczIsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0V9CmhlYWQobGlzdEZpbHRlcnMobWFydCA9IGVuc2VtYmwpLCBuID0gMjApCmhlYWQobGlzdEF0dHJpYnV0ZXMoZW5zZW1ibCksIG4gPSAzMCkKYGBgCgpXZSBjYW4gYWNjZXNzIGFkZGl0aW9uYWwgZ2Vub21pYyBhbm5vdGF0aW9ucyB1c2luZyB0aGUgW2BiaW9NYXJ0YCBwYWNrYWdlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvYmlvbWFSdC5odG1sKS4gVG8gaWRlbnRpZnkgIHdlJ2xsIHN0cnVjdHVyZSBvdXIgJ3F1ZXJ5JyBvciBzZWFyY2ggb2YgdGhlIGJpb01hcnQgcmVzb3VyY2VzIHRvIHVzZSB0aGUgW0VOU0VNQkwgaWRdKGh0dHBzOi8vbS5lbnNlbWJsLm9yZy9pbmZvL2dlbm9tZS9nZW5lYnVpbGQvZ2VuZV9uYW1lcy5odG1sKSBmcm9tIG91ciBhbGlnbm1lbnQgdG8gYWRkIHRoZSBnZW5lIHN5bWJvbHMgYW5kIGdlbmUgZGVzY3JpcHRpb24gZm9yIGVhY2ggZ2VuZS4KCmBgYHtyIEFkZEFub3RhdGlvbjMsIHdhcm5pbmc9RkFMU0V9CiMjIHdhc24ndCB3b3JraW5nIGZvciBteSB2ZXJzaW9uIG9mIGJpb21hUnQgYnV0IG1pZ2h0IHdvcmsgZm9yIHlvdQpHZW5lS2V5IDwtIGdldEJNKGF0dHJpYnV0ZXM9YygnZW5zZW1ibF9nZW5lX2lkJywgJ2V4dGVybmFsX2dlbmVfbmFtZScpLAogICAgICBmaWx0ZXJzID0gJ2Vuc2VtYmxfZ2VuZV9pZCcsCiAgICAgIHZhbHVlcyA9IHJvdy5uYW1lcyhhc3NheShkZHMpKSwKICAgICAgbWFydCA9IGVuc2VtYmwpICMgd2lsbCB0YWtlIHNvbWUgdGltZSB0byBydW4KCiMjIGNoZWNrIHRoZSBrZXkKaGVhZChHZW5lS2V5KQpgYGAKCgpOb3cgdGhhdCB3ZSBoYXZlIHRoZSBFTlNFTUJMIGluZm9ybWF0aW9uIGFuZCBhIGdlbmUgc3ltYm9sIHRvIG1hdGNoIHRvIG91ciByZXN1bHRzLCB3ZSBjYW4gcHJvY2NlZCB0byB0aGUgW2JyZWFrb3V0IGV4ZXJjaXNlIHRvIGFubm90YXRlIG91ciBERSB0YWJsZShzKV0oaHR0cHM6Ly91bWljaC1icmNmLWJpb2luZi5naXRodWIuaW8vMjAyMi0wMy0xNC11bWljaC1ybmFzZXEtZGVteXN0aWZpZWQvaHRtbC9Nb2R1bGUxMWFfYnJlYWtvdXQuaHRtbCkKCgpgYGB7ciBjaGVja0Fubm9zfQpoZWFkKHJlc19XVF9hbm5vKQpgYGAKICAgIE5vdGljZSB0aGF0IG5vdCBhbGwgZ2VuZXMgd2VyZSBhbm5vdGF0ZWQgd2l0aCBhbiBFTlNFTUJsIGdlbmUgaWQgb3IgZ2VuZSBkZXNjcmlwdGlvbi4gV2hpbGUgd2UgYXJlIGFibGUgdG8gYW5ub3RhdGUgb3VyIHJlc3VsdHMsIHdlIHNob3VsZCBiZSB2ZXJ5IGNhdXRpb3VzIGFzIHRoZSBnZW5lIHN5bWJvbCBpcyBub3QgYSBnb29kIHVuaXF1ZSBpZGVudGlmaWVyIHBsdXMgd2UgZGlkIG5vdCB1c2UgYSBVQ1NDIGFubm90YXRpb24gcmVzb3VyY2Ugc28gdGhlIEhVR08gZ2VuZSBzeW1ib2wgbWF5IG5vdCBhbHdheXMgbWF0Y2guIEhvd2V2ZXIsIHRoaXMgY29kZSBpcyBzaW1pbGFyIHRvIHRoZSBzdGVwcyBuZWVkZWQgdG8gYW5ub3RhdGUgRU5TRU1CTCBpZCBiYXNlZCByZXN1bHRzLCBsaWtlIHdoYXQgd291bGQgaGF2ZSBiZWVuIGdlbmVyYXRlZCBmcm9tIHllc3RlcmRheSdzIGFsaWdubWVudHMsIHdpdGggbW9yZSBpbnRlcnByZXRhYmxlIGdlbmUgc3ltYm9scy4KCgoKKipOb3RlKio6IEZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIHJlZ2FyZGluZyBiaW9NYXJ0LCBwbGVhc2UgY29uc3VsdCB0aGUgW0VOU0VNQkwgYmlvTWFydCB2aWduZXR0ZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvYmlvbWFSdC9pbnN0L2RvYy9hY2Nlc3NpbmdfZW5zZW1ibC5odG1sKSBvciB0aGUgYnJvYWRlciBbQmlvY29uZHVjdG9yIEFubm90YXRpb24gUmVzb3VyY2VzIHZpZ25ldHRlIF0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvd29ya2Zsb3dzL3ZpZ25ldHRlcy9hbm5vdGF0aW9uL2luc3QvZG9jL0Fubm90YXRpb25fUmVzb3VyY2VzLmh0bWwpLgoKIyBPdXRwdXR0aW5nIHJlc3VsdHMgdG8gZmlsZQoKQSBrZXkgYXNwZWN0IG9mIG91ciBhbmFseXNpcyBpcyBwcmVzZXJ2aW5nIHRoZSByZWxldmFudCBkYXRhc2V0cyBmb3IgYm90aCBvdXIgcmVjb3JkcyBhbmQgZm9yIGRvd25zdHJlYW0gYXBwbGljYXRpb25zLCBzdWNoIGFzIGZ1bmN0aW9uYWwgZW5yaWNobWVudHMuCgpGaXJzdCwgd2UnbGwgc2V0dXAgYSBuZXcgZGlyZWN0b3J5IGZvciBvdXIgb3V0cHV0IHRhYmxlcy4KYGBge3IgT3V0cHV0VGFibGVEaXJ9CmRpci5jcmVhdGUoInRhYmxlcyIsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQpgYGAKCiMjIERFIHJlc3VsdHMgdGFibGUKCk5leHQgd2UnbGwgd3JpdGUgb3V0IG91ciBERSByZXN1bHRzIGZvciB0aGUgS0QgY29tcGFyaXNvbiB0byBmaWxlLCBzaW5jZSB3ZSBhZGRlZCBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIHRvIHRoYXQgdGFibGUuCmBgYHtyIERFUmVzdWx0c091cHV0fQp3cml0ZS5jc3YocmVzX1dUX2Fubm8sCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSwKICAgICAgICAgIG5hID0gIi4iLAogICAgICAgICAgZmlsZT0idGFibGVzL0RFUmVzdWx0c19rby5jb250cm9sX3Zfd3QuY29udHJvbF9hbm5vdGF0ZWQuY3N2IikKYGBgCgpJZiB3ZSBnZW5lcmF0ZWQgYW5vdGhlciBjb21wYXJpc2lvbiwgd2UgY291bGQgcmVwZWF0IG91ciBhbm5vdGF0aW9ucyBvciB3cml0ZSB0aGUgREUgcmVzdWx0cyBkaXJlY3RseSB0byBmaWxlLgoKYGBge3IgREVSZXN1bHRzT3VwdXQyfQp3cml0ZS5jc3YocmVzX1R4LAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UsCiAgICAgICAgICBuYSA9ICIuIiwKICAgICAgICAgIGZpbGU9InRhYmxlcy9ERVJlc3VsdHNfa28uVHhfdl93dC5UeC5jc3YiKQpgYGAKCgojIyBDb3VudCB0YWJsZXMKClRoZSBtb3N0IHJlbGV2YW50IGNvdW50IHRhYmxlcyBhcmUgdGhlIHJhdywgZmlsdGVyZWQgY291bnQgdGFibGUgdGhhdCB3ZSB1c2VkIGFzIHRoZSBpbnB1dCBmb3Igb3VyIGFuYWx5c2lzIGFuZCB0aGUgcmxvZyBub3JtYWxpemVkIGNvdW50IHRhYmxlIHRoYXQgd2UgdXNlZCBmb3Igb3VyIHF1YWxpdHkgY29udHJvbCB2aXN1YWxpemF0aW9ucy4KCgpUbyBvdXRwdXQgdGhlIHJhdyBjb3VudHMsIHdlIHdpbGwgbmVlZCB0byB1c2UgdGhlIGBjb3VudHNgIGZ1bmN0aW9uIHRvIGFjY2VzcyB0aGUgY291bnQgdGFibGUgZnJvbSB3aXRoaW4gaXRzIGxhcmdlciBgREVTZXFEYXRhU2V0YCBvYmplY3QuCmBgYHtyIE91dHB1dENvdW50c1Jhd30Kd3JpdGUuY3N2KGNvdW50cyhkZHMsIG5vcm1hbGl6ZWQgPSBGQUxTRSksIGZpbGU9InRhYmxlcy9ERVNlcTJfcmF3X2NvdW50cy5jc3YiKQpgYGAKClRoZW4gd2UnbGwgb3V0cHV0IHRoZSBybG9nIGNvdW50IHRhYmxlLCB1c2luZyB0aGUgYGFzc2F5YCBmdW5jdGlvbiB0byBhY2Nlc3MgdGhlIG5vcm1hbGl6ZWQgY291bnQgdGFibGUgZnJvbSB3aXRoaW4gaXRzIGxhcmdlciBgREVTZXFEYXRhU2V0YCBvYmplY3QuCmBgYHtyIE91dHB1dENvdW50c1Jsb2d9CndyaXRlLmNzdihhc3NheShybGQpLCBmaWxlPSJ0YWJsZXMvREVTZXEyX3Jsb2dOb3JtYWxpemVkX2NvdW50cy5jc3YiKQpgYGAKCgoKIyBTdW1tYXJ5CgpJbiB0aGlzIHNlY3Rpb24sIHdlOiAgIAoKKiBHZW5lcmF0ZWQgYSB2b2xjYW5vIHBsb3QgZm9yIG91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzICAgCiogU3VtbWFyaXplZCBvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0cyAgICAKKiBEaXNjdXNzZWQgY2hvb3NpbmcgdGhyZXNob2xkcyAgICAKKiBBbm5vdGF0ZWQgb3VyIHRhYmxlcyBvZiByZXN1bHRzIHRvIG1hcCBnZW5lIElEcyB0byBnZW5lIHN5bWJvbHMgICAgCiogU2F2ZWQgb3VyIHJlc3VsdHMgdG8gZmlsZSAgICAKCgojIEtleSB0YWtlYXdheXMKCk92ZXJhbGwsIHdlJ3ZlIHJ1biB0aHJvdWdoIG1vc3Qgb2YgdGhlIGJ1aWxkaW5nIGJsb2NrcyBuZWVkZWQgdG8gcnVuIGEgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgYW5kIGhvcGVmdWxseSBidWlsdCB1cCBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mIGhvdyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBjb21wYXJpc29ucyB3b3JrLCBwYXJ0aWN1bGFybHkgaG93IGV4cGVyaW1lbnRhbCBkZXNpZ24gY2FuIGltcGFjdCBvdXIgcmVzdWx0cy4gIAoKV2hhdCB0byBjb25zaWRlciBtb3ZpbmcgZm9yd2FyZDogICAgCgoqIEhvdyBjYW4gSSBjb250cm9sIGZvciB0ZWNobmljYWwgdmFyaWF0aW9uIGluIG15IGV4cGVyaW1lbnRhbCBkZXNpZ24/ICAgICAgCiogSG93IG11Y2ggdmFyaWF0aW9uIGlzIGV4cGVjdGVkIHdpdGggYSB0cmVhdG1lbnQgZ3JvdXA/ICAgICAKKiBXaGF0IGlzIG15IFJOQSBxdWFsaXR5LCBhbmQgaG93IGNhbiB0aGF0IGJlIG9wdGltaXplZD8gICAgCiogQXJlIHRoZXJlIHF1YWxpdHkgY29uY2VybnMgZm9yIG15IHNlcXVlbmNpbmcgZGF0YT8KKiBXaGF0IGNvbXBhcmlzb25zIGFyZSByZWxldmFudCB0byBteSBiaW9sb2dpY2FsIHF1ZXN0aW9uPyAgICAKKiBBcmUgdGhlcmUgY292YXJpYXRlcyB0aGF0IHNob3VsZCBiZSBjb25zaWRlcmVkPyAgICAgCiogV2hhdCB3aWxsIGEgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgdGVsbCBtZT8KCgoqKkxldCdzIHBhdXNlIGhlcmUgZm9yIGdlbmVyYWwgcXVlc3Rpb25zKioKCi0tLQoKIyBFeHRlbnNpb24gLSB3aGF0IGNhbiB3ZSBkbyB3aXRoIG91ciBERSByZXN1bHRzPwoKTm93IHRoYXQgd2UgaGF2ZSBvdXIgREUgcmVzdWx0cywgaGF2ZSB3ZSBhZGRyZXNzIHRoZSBiaW9sb2dpY2FsIHF1ZXN0aW9uIHJlbGV2YW50IHRvIHRoZSBhdXRob3JzIG9mIHRoZSBbb3JpZ2luYWwgcGFwZXJdKGh0dHA6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wdWJtZWQvMjU0NjQ4NDkpPyBPbiB0aGUgb25lIGhhbmQsIHllcyAtIHdlIG5vdyBoYXZlIHR3byB0YWJsZXMgb2YgZ2VuZXMgdGhhdCBhcmUgaW1wYWN0ZWQgYnkgY2hhbmdlIGluIE1vdjEwIGV4cHJlc3Npb24uIEJ1dCB3aXRoIHR3byBsaXN0cyBvZiBnZW5lcyBhbG9uZSwgaXQgY2FuIGJlIGRpZmZpY3VsdCB0byBmaW5kIHBhdHRlcm5zIG9yIHVuZGVyc3RhbmQgYnJvYWRlciBiaW9sb2dpY2FsIGltcGFjdHMuCgpXaGF0IGlmIHdlIHdhbnRlZCB0byBmaW5kIG91dCB3aGF0IGdlbmVzIHdlcmUgc2lnbmlmYW50IGluICpib3RoKiBjb21wYXJpc29ucz8gVGhlIGBpbnRlcnNlY3RgIGZ1bmN0aW9uLCBzdWNoIGFzIGltcGxpbWVudGVkIGFzIFtwYXJ0IG9mIHRoZSBkcGx5ciBwYWNrYWdlXShodHRwczovL3d3dy5kYXRhc2NpZW5jZW1hZGVzaW1wbGUuY29tL2ludGVyc2VjdC1mdW5jdGlvbi1yLXVzaW5nLWRwbHlyLWludGVyc2VjdGlvbi1kYXRhLWZyYW1lcy8pIHdvdWxkIGJlIHVzZWZ1bCB0byBpZGVudGlmeSBzaGFyZWQgc2lnbmlmaWNhbnQgZ2VuZXMuIEEgW3Zlbm4gZGlhZ3JhbV0oaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS8xNC12ZW5uLWRpYWdyYW1tLmh0bWwpIGNvdWxkIGJlIGEgd2F5IHRvIHZpc3VhbGl6ZSB0aGVzZSBvdmVybGFwcy4KCkEgd2F5IHRvIGRldGVybWluZSBwb3NzaWJsZSBbYnJvYWRlciBiaW9sb2dpY2FsIGludGVycHJldGF0aW9uc10oaHR0cHM6Ly93d3cuZWJpLmFjLnVrL3RyYWluaW5nLWJldGEvb25saW5lL2NvdXJzZXMvZnVuY3Rpb25hbC1nZW5vbWljcy1paS1jb21tb24tdGVjaG5vbG9naWVzLWFuZC1kYXRhLWFuYWx5c2lzLW1ldGhvZHMvYmlvbG9naWNhbC1pbnRlcnByZXRhdGlvbi1vZi1nZW5lLWV4cHJlc3Npb24tZGF0YS0yLykgZnJvbSB0aGUgb2JzZXJ2ZWQgREUgcmVzdWx0cywgaXMgZnVuY3Rpb25hbCBlbnJpY2htZW50cy4gVGhlcmUgYXJlIG1hbnkgb3B0aW9ucywgc3VjaCBhcyBzb21lIGluY2x1ZGVkIGluIHRoaXMgW2Rpc2N1c3Npb24gdGhyZWFkXShodHRwczovL3d3dy5yZXNlYXJjaGdhdGUubmV0L3Bvc3QvSG93X2Nhbl9JX2FuYWx5emVfYV9zZXRfb2ZfREVHc19kaWZmZXJlbnRpYWxseV9leHByZXNzZWRfZ2VuZXNfdG9fb2J0YWluX2luZm9ybWF0aW9uX2Zyb21fdGhlbSkuIE90aGVyIGNvbW1vbiBmdW5jdGlvbmFsIGVucmljaG1lbnRzIGFwcHJvYWNoZXMgYXJlIGdlbmUgc2V0IGVucmljaG1lbnQgYW5hbHlzaXMsIGFrYSBbR1NFQV0oaHR0cDovL3NvZnR3YXJlLmJyb2FkaW5zdGl0dXRlLm9yZy9nc2VhL2luZGV4LmpzcCksIERhdGFiYXNlIGZvciBBbm5vdGF0aW9uLCBWaXN1YWxpemF0aW9uIGFuZCBJbnRlZ3JhdGVkIERpc2NvdmVyeSwgYWthIFtEQVZJRF0oaHR0cHM6Ly9kYXZpZC5uY2lmY3JmLmdvdi8pLCBbSW5nZW5pdHldKGh0dHBzOi8vZGlnaXRhbGluc2lnaHRzLnFpYWdlbi5jb20vKSwgYW5kIFtpUGF0aHdheSBHdWlkZV0KClRoZSBVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIGhhcyBsaWNlbnNlIGFuZCBzdXBwb3J0IGZvciBhZGRpdGlvbmFsIHRvb2xzLCBzdWNoIGFzIEN5dG9zY2FwZSwgc28gd2UgcmVjb21tZW5kIHJlYWNoaW5nIG91dCB0byBzdGFmZiB3aXRoIFtUYXVibWFuIExpYnJhcnldKGh0dHBzOi8vd3d3LmxpYi51bWljaC5lZHUvbG9jYXRpb25zLWFuZC1ob3Vycy90YXVibWFuLWhlYWx0aC1zY2llbmNlcy1saWJyYXJ5L3Jlc2VhcmNoLWFuZC1jbGluaWNhbC1zdXBwb3J0KSB0byBsZWFybiBtb3JlIGFib3V0IHJlc291cmNlcyB0aGF0IG1pZ2h0IGJlIGFwcGxpY2F0aW9uIHRveW91ciByZXNlYXJjaC4KCgoKCi0tLQoKIyBTb3VyY2VzIFVzZWQgICAgICAKKiBIQkMgREdFIHRyYWluaW5nIG1vZHVsZSwgcGFydCAxOiBodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wNF9ER0VfREVTZXEyX2FuYWx5c2lzLmh0bWwgICAgCiogSEJDIERHRSB0cmFpbmluZyBtb2R1bGUsIHBhcnQgMjogaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDVfREdFX0RFU2VxMl9hbmFseXNpczIuaHRtbCAgICAKKiBERVNlcTIgdmlnbmV0dGU6IGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCNkaWZmZXJlbnRpYWwtZXhwcmVzc2lvbi1hbmFseXNpcyAgICAKKiBCaW9jb25kdWN0b3IgR2Vub21pYyBBbm5vdGF0aW9uIHJlc291cmNlczogaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvd29ya2Zsb3dzL3ZpZ25ldHRlcy9hbm5vdGF0aW9uL2luc3QvZG9jL0Fubm90YXRpb25fUmVzb3VyY2VzLmh0bWwgICAgCiogQmlvTWFydCB2aWduZXR0ZTogaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvYmlvbWFSdC9pbnN0L2RvYy9hY2Nlc3NpbmdfZW5zZW1ibC5odG1sICAgIAoKCgpgYGB7ciBXcml0ZU91dC5SRGF0YSwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojSGlkZGVuIGNvZGUgYmxvY2sgdG8gd3JpdGUgb3V0IGRhdGEgZm9yIGtuaXR0aW5nCnNhdmUuaW1hZ2UoZmlsZSA9ICJyZGF0YS9SdW5uaW5nRGF0YV9GdWxsLlJEYXRhIikKYGBgCgojIEFkZGl0aW9uYWwgUmVzb3VyY2VzCiogTUlEQVMgUmVwcm9kdWNpYmxpdHkgSHViOiBodHRwczovL21pZGFzLnVtaWNoLmVkdS9yZXByb2R1Y2liaWxpdHktb3ZlcnZpZXcvCiogQVJDIHJlc291cmNlczogaHR0cHM6Ly9hcmMtdHMudW1pY2guZWR1LwoqIEdlbmUgU2V0IEVucmljaG1lbnQgUmVzb3VyY2VzIGZyb20gQmlvY29uZHVjdG9yOiBodHRwczovL2Jpb2luZm9ybWF0aWNzLWNvcmUtc2hhcmVkLXRyYWluaW5nLmdpdGh1Yi5pby9jcnVrLXN1bW1lci1zY2hvb2wtMjAxOC9STkFTZXEyMDE4L2h0bWwvMDZfR2VuZV9zZXRfdGVzdGluZy5uYi5odG1sCiogVXNpbmcgSFRTZXEgZGF0YSB3aXRoIERFU2VxMjogaHR0cHM6Ly9hbmd1cy5yZWFkdGhlZG9jcy5pby9lbi8yMDE5L2RpZmYtZXgtYW5kLXZpei5odG1sCiogRGV0YWlsZWQgUk5BLXNlcSBhbmFseXNpcyBwYXBlcjogaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNjA5NjM0Ni8KKiBPdmVydmlldyBvZiBSTkEtc2VxIGFuYWx5c2lzIGNvbnNpZGVyYXRpb25zOiBodHRwczovL2FjYWRlbWljLW91cC1jb20ucHJveHkubGliLnVtaWNoLmVkdS9iZmcvYXJ0aWNsZS8xNC8yLzEzMC8yNTczNzAKKiBBbHRlcm5hdGl2ZSBvdmVydmlldyBvZiBERVNlcTIsIGluY2x1ZGluZyB2aXN1YWxpemF0aW9ucyBhbmQgZnVuY3Rpb25hbCBlbnJpY2htZW50czogaHR0cDovL2RwdXRoaWVyLmdpdGh1Yi5pby9qZ2I3MWUtcG9seXRlY2gtYmlvaW5mby1hcHAvcHJhY3RpY2FsL3JuYS1zZXFfUi9ybmFzZXFfZGlmZl9TbmYyLmh0bWwKCi0tLQoKCiMgU2Vzc2lvbiBJbmZvCmBgYHtyIFNlc3Npb25JbmZvfQpzZXNzaW9uSW5mbygpCmBgYAoKLS0tCgpUaGVzZSBtYXRlcmlhbHMgaGF2ZSBiZWVuIGFkYXB0ZWQgYW5kIGV4dGVuZGVkIGZyb20gbWF0ZXJpYWxzIGxpc3RlZCBhYm92ZS4gVGhlc2UgYXJlIG9wZW4gYWNjZXNzIG1hdGVyaWFscyBkaXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIGxpY2Vuc2UgKENDIEJZIDQuMCldKGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8pLCB3aGljaCBwZXJtaXRzIHVucmVzdHJpY3RlZCB1c2UsIGRpc3RyaWJ1dGlvbiwgYW5kIHJlcHJvZHVjdGlvbiBpbiBhbnkgbWVkaXVtLCBwcm92aWRlZCB0aGUgb3JpZ2luYWwgYXV0aG9yIGFuZCBzb3VyY2UgYXJlIGNyZWRpdGVkLgo=