Objectives

This notebook will demonstrate how to:

  • Prepare tabular data of gene-level statistics for use with Gene Set Enrichment Analysis (GSEA), including how to remove duplicate gene identifiers
  • Perform GSEA with the clusterProfiler package
  • Visualize GSEA results with the enrichplot package

In this notebook, we will perform Gene Set Enrichment Analysis (GSEA) on the neuroblastoma cell line differential gene expression (DGE) results we generated during the RNA-seq module.

To refresh our memory, we analyzed data from Harenza et al. (2017) and specifically tested for DGE between with MYCN amplified cell lines and non-amplified cell lines using DESeq2. We have a table of results that contains our log2 fold changes and adjusted p-values.

GSEA is a functional class scoring (FCS) approach to pathway analysis that was first introduced in Subramanian et al. (2005). The rationale behind FCS approaches is that small changes in individual genes that participate in the same biological process or pathway can be significant and of biological interest. FCS methods are better suited for identifying these pathways that show coordinated changes than ORA. In ORA, we pick a cutoff that typically only captures genes with large individual changes.

There are 3 general steps in FCS methods (Khatri et al. 2012):

  1. Calculate a gene-level statistic (we’ll use log2 fold change from DESeq2 here)
  2. Gene-level statistics are aggregated into a pathway-level statistic
  3. Assess the statistical significance of the pathway-level statistic

Other resources

Set up

Libraries

# Package to run GSEA
library(clusterProfiler)
clusterProfiler v4.12.0  For help: https://yulab-smu.top/biomedical-knowledge-mining-book/

If you use clusterProfiler in published research, please cite:
T Wu, E Hu, S Xu, M Chen, P Guo, Z Dai, T Feng, L Zhou, W Tang, L Zhan, X Fu, S Liu, X Bo, and G Yu. clusterProfiler 4.0: A universal enrichment tool for interpreting omics data. The Innovation. 2021, 2(3):100141

Attaching package: 'clusterProfiler'
The following object is masked from 'package:stats':

    filter
# Package that contains the MSigDB gene sets in tidy format
library(msigdbr)

Directories and Files

Directories

# Where the DGE results are stored
input_dir <- file.path("..", "RNA-seq", "results", "NB-cell")

# We will create a directory to specifically hold our GSEA results if it does
# not yet exist
output_dir <- file.path("results", "NB-cell")
if (!dir.exists(output_dir)) {
  dir.create(output_dir, recursive = TRUE)
}

Input files

# DGE results
dge_results_file <- file.path(input_dir,
                             "NB-cell_DESeq_amplified_v_nonamplified_results.tsv")

Output files

# GSEA pathway-level scores and statistics
gsea_results_file <- file.path(output_dir,
                               "NB-cell_gsea_results.tsv")

Gene Set Enrichment Analysis

Adapted from refine.bio examples

Figure 1. Subramanian et al. (2005).

GSEA calculates a pathway-level metric, called an enrichment score (sometimes abbreviated as ES), by ranking genes by a gene-level statistic. This score reflects whether or not a gene set or pathway is over-represented at the top or bottom of the gene rankings (Subramanian et al. 2005; Yu)

Specifically, all genes are ranked from most positive to most negative based on their statistic and a running sum is calculated: Starting with the most highly ranked genes, the running sum increases for each gene in the pathway and decreases for each gene not in the pathway. The enrichment score for a pathway is the running sum’s maximum deviation from zero. GSEA also assesses statistical significance of the scores for each pathway through permutation testing. As a result, each input pathway will have a p-value associated with it that is then corrected for multiple hypothesis testing (Subramanian et al. 2005; Yu).

The implementation of GSEA we use in here examples requires a gene list ordered by some statistic and input gene sets. When you use previously computed gene-level statistics with GSEA, it is called GSEA pre-ranked.

Gene sets

In the previous notebook, we used KEGG pathways for over-representation analysis. We identified pathways that were significantly over-represented and found that the significant pathways shared genes. FCS methods analyze each pathway independently and essentially ignore this overlap between gene sets.

MSigDB includes a collection called Hallmark gene sets (Liberzon et al. 2015) Here’s an excerpt of the collection description:

Hallmark gene sets summarize and represent specific well-defined biological states or processes and display coherent expression. These gene sets were generated by a computational methodology based on identifying gene set overlaps and retaining genes that display coordinate expression. The hallmarks reduce noise and redundancy and provide a better delineated biological space for GSEA.

We’ll use the Hallmark collection for GSEA. Notably, there are only 50 gene sets included in this collection. The fewer gene sets we test, the lower our multiple hypothesis testing burden.

We can retrieve only the Hallmark gene sets by specifying category = "H" to the msigdbr() function.

hs_hallmark_df <- msigdbr(species = "Homo sapiens",
                          category = "H")

Differential gene expression results

# Read in the DGE results
dge_results_df <- readr::read_tsv(dge_results_file)
Rows: 24912 Columns: 7
── Column specification ────────────────────────────────────────────────────────
Delimiter: "\t"
chr (2): gene_id, gene_symbol
dbl (5): baseMean, log2FoldChange, lfcSE, pvalue, padj

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Let’s take a peek at the contents of this file.

head(dge_results_df)

Since this data frame of DGE results includes gene symbols, we do not need to perform any kind of gene identifier conversion. We do, however, need to check for duplicate gene symbols. We can accomplish this with duplicated(), which returns a logical vector (e.g., TRUE or FALSE). The function sum() will count TRUE values as 1s and FALSE as 0s, so using it with duplicated() will count the number of duplicate values.

sum(duplicated(dge_results_df$gene_symbol))
[1] 926

This will cause a problem when we go to run GSEA.

Removing duplicates

The GSEA approach requires on discriminating between genes that are in a gene set and those that are not. Practically speaking, gene sets are just collections of gene identifiers! When the function we use for GSEA pre-ranked gets a list with duplicated gene identifiers, it can produce unexpected results.

Compared to the total number of genes that are in our results, there are not a lot of duplicates but we’ll still need to make a decision about how to handle them.

Let’s get a vector of the duplicated gene symbols so we can use it to explore our filtering steps.

duplicated_gene_symbols <- dge_results_df |>
  dplyr::filter(duplicated(gene_symbol)) |>
  dplyr::pull(gene_symbol)

Now we’ll look at the values for the the duplicated gene symbols.

dge_results_df |>
  dplyr::filter(gene_symbol %in% duplicated_gene_symbols) |>
  dplyr::arrange(gene_symbol)

We can see that the associated values vary for each row.

Let’s keep the gene symbols associated with the higher absolute value of the log2 fold change.

Retaining the instance of the gene symbols with the higher absolute value of a gene-level statistic means that we will retain the value that is likely to be more highly- or lowly-ranked or, put another way, the values less likely to be towards the middle of the ranked gene list. We should keep this decision in mind when interpreting our results. For example, if all the duplicate identifiers happened to be in a particular gene set, we may get an overly optimistic view of how perturbed that gene set is because we preferentially selected instances of the identifier that have a higher absolute value of the statistic used for ranking.

In the next chunk, we are going to filter out the duplicated rows using the dplyr::distinct() function after sorting by absolute value of the log2 fold change. This will keep the first row with the duplicated value thus keeping the row with the largest absolute value.

filtered_dge_df <- dge_results_df |>
  # Sort so that the highest absolute values of the log2 fold change are at the
  # top
  dplyr::arrange(dplyr::desc(abs(log2FoldChange))) |>
  # Filter out the duplicated rows using `dplyr::distinct()`
  dplyr::distinct(gene_symbol, .keep_all = TRUE)

Let’s see what happened to our duplicate identifiers.

# Subset to & arrange by gene symbols that were duplicated in the original
# data frame of results
filtered_dge_df |>
  dplyr::filter(gene_symbol %in% duplicated_gene_symbols) |>
  dplyr::arrange(gene_symbol)

Now we’re ready to prep our pre-ranked list for GSEA.

Pre-ranked list

The GSEA() function takes a pre-ranked (sorted) named vector of statistics, where the names in the vector are gene identifiers. This is step 1 – gene-level statistics.

lfc_vector <- filtered_dge_df |>
  # Extract a vector of `log2FoldChange` named by `gene_symbol`
  dplyr::pull(log2FoldChange, name = gene_symbol)
lfc_vector <- sort(lfc_vector, decreasing = TRUE)

Let’s look at the top ranked values.

# Look at first entries of the log2 fold change vector
head(lfc_vector)
  GAGE12D    GABBR1   FABP5P7  KIAA0355      MNX1    GAGE2A 
23.620487 23.130027 22.682631 22.562940  6.813764  6.801245 

And the bottom of the list.

# Look at the last entries of the log2 fold change vector
tail(lfc_vector)
     MUC15      TFPI2      LENG1       MT1M       DAZ2   CSPG4P4Y 
 -8.017004  -8.768669 -22.754783 -25.318887 -25.444428 -26.095612 

Run GSEA

Now for the analysis!

We can use the GSEA() function to perform GSEA with any generic set of gene sets, but there are several functions for using specific, commonly used gene sets (e.g., gseKEGG()).

gsea_results <- GSEA(geneList = lfc_vector,  # ordered ranked gene list
                     minGSSize = 25,  # minimum gene set size
                     maxGSSize = 500,  # maximum gene set set
                     pvalueCutoff = 0.05,
                     pAdjustMethod = "BH",  # correction for multiple hypothesis testing
                     TERM2GENE = dplyr::select(hs_hallmark_df,
                                               gs_name,
                                               gene_symbol))
using 'fgsea' for GSEA analysis, please cite Korotkevich et al (2019).
preparing geneSet collections...
GSEA analysis...
Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize, gseaParam, : There are ties in the preranked stats (0.1% of the list).
The order of those tied genes will be arbitrary, which may produce unexpected results.
leading edge analysis...
done...

The warning about ties means that there are multiple genes that have the same log2 fold change value. This percentage is small and unlikely to impact our results. A large number of ties might tell us there’s something wrong with our DGE results (Ballereau et al. 2018).

Let’s take a look at the GSEA results.

View(gsea_results@result |>
       dplyr::arrange(dplyr::desc(NES))
)

Normalized enrichment scores (NES) are enrichment scores that are scaled to make gene sets that contain different number of genes comparable.

Let’s write these results to file.

gsea_results@result |> readr::write_tsv(gsea_results_file)

Visualizing GSEA results

We can visualize GSEA results for individual pathways or gene sets using enrichplot::gseaplot(). Let’s take a look at 3 different pathways – one with a highly positive NES, one with a highly negative NES, and one that was not a significant result – to get more insight into how ES are calculated.

Highly Positive NES

The gene set HALLMARK_MYC_TARGETS_V1 had high positive log2 fold changes. Recall a positive log2 fold change means a it had a higher expression value in MYCN amplified cell lines.

enrichplot::gseaplot(gsea_results,
                     geneSetID = "HALLMARK_MYC_TARGETS_V1",
                     title = "HALLMARK_MYC_TARGETS_V1",
                     color.line = "#0066FF")

Notice how the genes that are in the gene set, indicated by the black bars, tend to be on the left side of the graph indicating that they have positive gene-level scores.

Highly Negative NES

The gene set HALLMARK_INTERFERON_ALPHA_RESPONSE had a highly negative NES.

enrichplot::gseaplot(gsea_results,
                     geneSetID = "HALLMARK_INTERFERON_ALPHA_RESPONSE",
                     title = "HALLMARK_INTERFERON_ALPHA_RESPONSE",
                     color.line = "#0066FF")

This gene set shows the opposite pattern – genes in the pathway tend to be on the right side of the graph.

A non-significant result

The @results slot will only show gene sets that pass the pvalueCutoff threshold we supplied to GSEA(), but we can plot any gene set so long as we know its name. Let’s look at HALLMARK_P53_PATHWAY, which was not in the results we viewed earlier.

enrichplot::gseaplot(gsea_results,
                     geneSetID = "HALLMARK_P53_PATHWAY",
                     title = "HALLMARK_P53_PATHWAY",
                     color.line = "#0066FF")

Genes in the pathway are distributed more evenly throughout the ranked list, resulting in a more “middling” score.

Note: The plots returned by enrichplot::gseaplot are ggplots, so we could use ggplot2::ggsave() to save them to file if we wanted to.

Session Info

sessionInfo()
R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 22.04.4 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so;  LAPACK version 3.10.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

time zone: Etc/UTC
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] msigdbr_7.5.1          clusterProfiler_4.12.0 optparse_1.7.5        

loaded via a namespace (and not attached):
  [1] DBI_1.2.2               gson_0.1.0              shadowtext_0.1.3       
  [4] gridExtra_2.3           rlang_1.1.3             magrittr_2.0.3         
  [7] DOSE_3.30.0             compiler_4.4.1          RSQLite_2.3.6          
 [10] png_0.1-8               vctrs_0.6.5             reshape2_1.4.4         
 [13] stringr_1.5.1           pkgconfig_2.0.3         crayon_1.5.2           
 [16] fastmap_1.1.1           XVector_0.44.0          labeling_0.4.3         
 [19] ggraph_2.2.1            utf8_1.2.4              HDO.db_0.99.1          
 [22] rmarkdown_2.26          tzdb_0.4.0              enrichplot_1.24.0      
 [25] UCSC.utils_1.0.0        purrr_1.0.2             bit_4.0.5              
 [28] xfun_0.43               zlibbioc_1.50.0         cachem_1.0.8           
 [31] aplot_0.2.2             GenomeInfoDb_1.40.0     jsonlite_1.8.8         
 [34] blob_1.2.4              highr_0.10              BiocParallel_1.38.0    
 [37] tweenr_2.0.3            parallel_4.4.1          R6_2.5.1               
 [40] bslib_0.7.0             stringi_1.8.3           RColorBrewer_1.1-3     
 [43] jquerylib_0.1.4         GOSemSim_2.30.0         Rcpp_1.0.12            
 [46] knitr_1.46              readr_2.1.5             IRanges_2.38.0         
 [49] Matrix_1.7-0            splines_4.4.1           igraph_2.0.3           
 [52] tidyselect_1.2.1        qvalue_2.36.0           yaml_2.3.8             
 [55] viridis_0.6.5           codetools_0.2-20        lattice_0.22-6         
 [58] tibble_3.2.1            plyr_1.8.9              treeio_1.28.0          
 [61] Biobase_2.64.0          withr_3.0.0             KEGGREST_1.44.0        
 [64] evaluate_0.23           gridGraphics_0.5-1      scatterpie_0.2.2       
 [67] getopt_1.20.4           polyclip_1.10-6         Biostrings_2.72.0      
 [70] ggtree_3.12.0           pillar_1.9.0            stats4_4.4.1           
 [73] ggfun_0.1.4             generics_0.1.3          vroom_1.6.5            
 [76] hms_1.1.3               S4Vectors_0.42.0        ggplot2_3.5.1          
 [79] tidytree_0.4.6          munsell_0.5.1           scales_1.3.0           
 [82] glue_1.7.0              lazyeval_0.2.2          tools_4.4.1            
 [85] data.table_1.15.4       fgsea_1.30.0            babelgene_22.9         
 [88] fs_1.6.4                graphlayouts_1.1.1      fastmatch_1.1-4        
 [91] tidygraph_1.3.1         cowplot_1.1.3           grid_4.4.1             
 [94] ape_5.8                 tidyr_1.3.1             AnnotationDbi_1.66.0   
 [97] colorspace_2.1-0        nlme_3.1-164            patchwork_1.2.0        
[100] GenomeInfoDbData_1.2.12
 [ reached getOption("max.print") -- omitted 20 entries ]
LS0tCnRpdGxlOiAiUGF0aHdheSBhbmFseXNpczogR2VuZSBTZXQgRW5yaWNobWVudCBBbmFseXNpcyAoR1NFQSkiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKYXV0aG9yOiBDQ0RMIGZvciBBTFNGCmRhdGU6IDIwMjAKLS0tCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOgoKLSBQcmVwYXJlIHRhYnVsYXIgZGF0YSBvZiBnZW5lLWxldmVsIHN0YXRpc3RpY3MgZm9yIHVzZSB3aXRoIEdlbmUgU2V0IEVucmljaG1lbnQgQW5hbHlzaXMgKEdTRUEpLCBpbmNsdWRpbmcgaG93IHRvIHJlbW92ZSBkdXBsaWNhdGUgZ2VuZSBpZGVudGlmaWVycwotIFBlcmZvcm0gR1NFQSB3aXRoIHRoZSBgY2x1c3RlclByb2ZpbGVyYCBwYWNrYWdlCi0gVmlzdWFsaXplIEdTRUEgcmVzdWx0cyB3aXRoIHRoZSBgZW5yaWNocGxvdGAgcGFja2FnZQoKLS0tCgpJbiB0aGlzIG5vdGVib29rLCB3ZSB3aWxsIHBlcmZvcm0gR2VuZSBTZXQgRW5yaWNobWVudCBBbmFseXNpcyAoR1NFQSkgb24gdGhlIG5ldXJvYmxhc3RvbWEgY2VsbCBsaW5lIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gKERHRSkgcmVzdWx0cyB3ZSBnZW5lcmF0ZWQgZHVyaW5nIHRoZSBSTkEtc2VxIG1vZHVsZS4KClRvIHJlZnJlc2ggb3VyIG1lbW9yeSwgd2UgYW5hbHl6ZWQgZGF0YSBmcm9tIFtIYXJlbnphICpldCBhbC4qICgyMDE3KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvc2RhdGEuMjAxNy4zMykgYW5kIHNwZWNpZmljYWxseSB0ZXN0ZWQgZm9yIERHRSBiZXR3ZWVuIHdpdGggX01ZQ05fIGFtcGxpZmllZCBjZWxsIGxpbmVzIGFuZCBub24tYW1wbGlmaWVkIGNlbGwgbGluZXMgdXNpbmcgYERFU2VxMmAuCldlIGhhdmUgYSB0YWJsZSBvZiByZXN1bHRzIHRoYXQgY29udGFpbnMgb3VyIGxvZzIgZm9sZCBjaGFuZ2VzIGFuZCBhZGp1c3RlZCBwLXZhbHVlcy4KCkdTRUEgaXMgYSBmdW5jdGlvbmFsIGNsYXNzIHNjb3JpbmcgKEZDUykgYXBwcm9hY2ggdG8gcGF0aHdheSBhbmFseXNpcyB0aGF0IHdhcyBmaXJzdCBpbnRyb2R1Y2VkIGluIFtTdWJyYW1hbmlhbiBfZXQgYWwuXyAoMjAwNSldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDczL3BuYXMuMDUwNjU4MDEwMikuClRoZSByYXRpb25hbGUgYmVoaW5kIEZDUyBhcHByb2FjaGVzIGlzIHRoYXQgc21hbGwgY2hhbmdlcyBpbiBpbmRpdmlkdWFsIGdlbmVzIHRoYXQgcGFydGljaXBhdGUgaW4gdGhlIHNhbWUgYmlvbG9naWNhbCBwcm9jZXNzIG9yIHBhdGh3YXkgY2FuIGJlIHNpZ25pZmljYW50IGFuZCBvZiBiaW9sb2dpY2FsIGludGVyZXN0LgpGQ1MgbWV0aG9kcyBhcmUgYmV0dGVyIHN1aXRlZCBmb3IgaWRlbnRpZnlpbmcgdGhlc2UgcGF0aHdheXMgdGhhdCBzaG93IGNvb3JkaW5hdGVkIGNoYW5nZXMgdGhhbiBPUkEuCkluIE9SQSwgd2UgcGljayBhIGN1dG9mZiB0aGF0IF90eXBpY2FsbHlfIG9ubHkgY2FwdHVyZXMgZ2VuZXMgd2l0aCBsYXJnZSBpbmRpdmlkdWFsIGNoYW5nZXMuCgpUaGVyZSBhcmUgMyBnZW5lcmFsIHN0ZXBzIGluIEZDUyBtZXRob2RzIChbS2hhdHJpIF9ldCBhbC5fIDIwMTJdKCBodHRwczovL2RvaS5vcmcvMTAuMTM3MS9qb3VybmFsLnBjYmkuMTAwMjM3NSkpOgoKMS4gQ2FsY3VsYXRlIGEgZ2VuZS1sZXZlbCBzdGF0aXN0aWMgKHdlJ2xsIHVzZSBsb2cyIGZvbGQgY2hhbmdlIGZyb20gREVTZXEyIGhlcmUpCjIuIEdlbmUtbGV2ZWwgc3RhdGlzdGljcyBhcmUgYWdncmVnYXRlZCBpbnRvIGEgcGF0aHdheS1sZXZlbCBzdGF0aXN0aWMKMy4gQXNzZXNzIHRoZSBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2Ugb2YgdGhlIHBhdGh3YXktbGV2ZWwgc3RhdGlzdGljCgojIyMjIE90aGVyIHJlc291cmNlcwoKKiBGb3IgYW5vdGhlciBleGFtcGxlIHVzaW5nIGBjbHVzdGVyUHJvZmlsZXJgIGZvciBHU0VBLCBzZWUgW19JbnRybyB0byBER0U6IEZ1bmN0aW9uYWwgQW5hbHlzaXMuXyBmcm9tIEhhcnZhcmQgQ2hhbiBCaW9pbmZvcm1hdGljcyBDb3JlIFRyYWluaW5nLl0oaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDlfZnVuY3Rpb25hbF9hbmFseXNpcy5odG1sKQoqIFRoZSB3YXkgd2UnbGwgdXNlIGBjbHVzdGVyUHJvZmlsZXJgIGhlcmUgdXNlcyBgZmdzZWFgIChGYXN0IEdlbmUgU2V0IEVucmljaG1lbnQgQW5hbHlzaXMpIHVuZGVyIHRoZSBob29kLgpZb3UgY2FuIHJlYWQgbW9yZSBhYm91dCBmZ3NlYSBpbiBbS29yb3RrZXZpY2ggX2V0IGFsLl8gKDIwMjEpXShodHRwczovL2RvaS5vcmcvMTAuMTEwMS8wNjAwMTIpLgoqIFtyZWZpbmUuYmlvIGV4YW1wbGVzIEdlbmUgc2V0IGVucmljaG1lbnQgYW5hbHlzaXMgLSBSTkEtc2VxXShodHRwczovL2FsZXhzbGVtb25hZGUuZ2l0aHViLmlvL3JlZmluZWJpby1leGFtcGxlcy8wMy1ybmFzZXEvcGF0aHdheS1hbmFseXNpc19ybmFzZXFfMDJfZ3NlYS5odG1sKSBmcm9tIHdoaWNoIHRoaXMgbWF0ZXJpYWwgaGFzIGJlZW4gYWRhcHRlZC4KCiMjIFNldCB1cAoKIyMjIExpYnJhcmllcwoKYGBge3IgbGlicmFyaWVzfQojIFBhY2thZ2UgdG8gcnVuIEdTRUEKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCiMgUGFja2FnZSB0aGF0IGNvbnRhaW5zIHRoZSBNU2lnREIgZ2VuZSBzZXRzIGluIHRpZHkgZm9ybWF0CmxpYnJhcnkobXNpZ2RicikKYGBgCgojIyMgRGlyZWN0b3JpZXMgYW5kIEZpbGVzCgojIyMjIERpcmVjdG9yaWVzCgpgYGB7cn0KIyBXaGVyZSB0aGUgREdFIHJlc3VsdHMgYXJlIHN0b3JlZAppbnB1dF9kaXIgPC0gZmlsZS5wYXRoKCIuLiIsICJSTkEtc2VxIiwgInJlc3VsdHMiLCAiTkItY2VsbCIpCgojIFdlIHdpbGwgY3JlYXRlIGEgZGlyZWN0b3J5IHRvIHNwZWNpZmljYWxseSBob2xkIG91ciBHU0VBIHJlc3VsdHMgaWYgaXQgZG9lcwojIG5vdCB5ZXQgZXhpc3QKb3V0cHV0X2RpciA8LSBmaWxlLnBhdGgoInJlc3VsdHMiLCAiTkItY2VsbCIpCmlmICghZGlyLmV4aXN0cyhvdXRwdXRfZGlyKSkgewogIGRpci5jcmVhdGUob3V0cHV0X2RpciwgcmVjdXJzaXZlID0gVFJVRSkKfQpgYGAKCiMjIyMgSW5wdXQgZmlsZXMKCmBgYHtyIGlucHV0X2ZpbGVzfQojIERHRSByZXN1bHRzCmRnZV9yZXN1bHRzX2ZpbGUgPC0gZmlsZS5wYXRoKGlucHV0X2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTkItY2VsbF9ERVNlcV9hbXBsaWZpZWRfdl9ub25hbXBsaWZpZWRfcmVzdWx0cy50c3YiKQpgYGAKCiMjIyMgT3V0cHV0IGZpbGVzCgpgYGB7ciBvdXRwdXRfZmlsZXN9CiMgR1NFQSBwYXRod2F5LWxldmVsIHNjb3JlcyBhbmQgc3RhdGlzdGljcwpnc2VhX3Jlc3VsdHNfZmlsZSA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOQi1jZWxsX2dzZWFfcmVzdWx0cy50c3YiKQpgYGAKCiMjIEdlbmUgU2V0IEVucmljaG1lbnQgQW5hbHlzaXMKCl9BZGFwdGVkIGZyb20gW3JlZmluZS5iaW8gZXhhbXBsZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL3JlZmluZWJpby1leGFtcGxlcy9ibG9iLzMzY2RlZmY2NmQ1N2Y5ZmU4ZWU0ZmNiNTE1NmFlYTRhYzJkY2UwN2YvMDMtcm5hc2VxL3BhdGh3YXktYW5hbHlzaXNfcm5hc2VxXzAyX2dzZWEuUm1kKV8KCiFbXShkaWFncmFtcy9zdWJyYW1hbmlhbl9maWcxLmpwZykKCioqRmlndXJlIDEuIFtTdWJyYW1hbmlhbiBfZXQgYWwuXyAoMjAwNSldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDczL3BuYXMuMDUwNjU4MDEwMikuKioKCkdTRUEgY2FsY3VsYXRlcyBhIHBhdGh3YXktbGV2ZWwgbWV0cmljLCBjYWxsZWQgYW4gZW5yaWNobWVudCBzY29yZSAoc29tZXRpbWVzIGFiYnJldmlhdGVkIGFzIEVTKSwgYnkgcmFua2luZyBnZW5lcyBieSBhIGdlbmUtbGV2ZWwgc3RhdGlzdGljLgpUaGlzIHNjb3JlIHJlZmxlY3RzIHdoZXRoZXIgb3Igbm90IGEgZ2VuZSBzZXQgb3IgcGF0aHdheSBpcyBvdmVyLXJlcHJlc2VudGVkIGF0IHRoZSB0b3Agb3IgYm90dG9tIG9mIHRoZSBnZW5lIHJhbmtpbmdzIChbU3VicmFtYW5pYW4gX2V0IGFsLl8gMjAwNV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwNzMvcG5hcy4wNTA2NTgwMTAyKTsgW1l1XShodHRwOi8veXVsYWItc211LnRvcC9jbHVzdGVyUHJvZmlsZXItYm9vay9jaGFwdGVyMi5odG1sI2dlbmUtc2V0LWVucmljaG1lbnQtYW5hbHlzaXMpKQoKU3BlY2lmaWNhbGx5LCBhbGwgZ2VuZXMgYXJlIHJhbmtlZCBmcm9tIG1vc3QgcG9zaXRpdmUgdG8gbW9zdCBuZWdhdGl2ZSBiYXNlZCBvbiB0aGVpciBzdGF0aXN0aWMgYW5kIGEgcnVubmluZyBzdW0gaXMgY2FsY3VsYXRlZDoKU3RhcnRpbmcgd2l0aCB0aGUgbW9zdCBoaWdobHkgcmFua2VkIGdlbmVzLCB0aGUgcnVubmluZyBzdW0gaW5jcmVhc2VzIGZvciBlYWNoIGdlbmUgaW4gdGhlIHBhdGh3YXkgYW5kIGRlY3JlYXNlcyBmb3IgZWFjaCBnZW5lIG5vdCBpbiB0aGUgcGF0aHdheS4KVGhlIGVucmljaG1lbnQgc2NvcmUgZm9yIGEgcGF0aHdheSBpcyB0aGUgcnVubmluZyBzdW0ncyBtYXhpbXVtIGRldmlhdGlvbiBmcm9tIHplcm8uCkdTRUEgYWxzbyBhc3Nlc3NlcyBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2Ugb2YgdGhlIHNjb3JlcyBmb3IgZWFjaCBwYXRod2F5IHRocm91Z2ggcGVybXV0YXRpb24gdGVzdGluZy4KQXMgYSByZXN1bHQsIGVhY2ggaW5wdXQgcGF0aHdheSB3aWxsIGhhdmUgYSBwLXZhbHVlIGFzc29jaWF0ZWQgd2l0aCBpdCB0aGF0IGlzIHRoZW4gY29ycmVjdGVkIGZvciBtdWx0aXBsZSBoeXBvdGhlc2lzIHRlc3RpbmcgKFtTdWJyYW1hbmlhbiBfZXQgYWwuXyAyMDA1XShodHRwczovL2RvaS5vcmcvMTAuMTA3My9wbmFzLjA1MDY1ODAxMDIpOyBbWXVdKGh0dHA6Ly95dWxhYi1zbXUudG9wL2NsdXN0ZXJQcm9maWxlci1ib29rL2NoYXB0ZXIyLmh0bWwjZ2VuZS1zZXQtZW5yaWNobWVudC1hbmFseXNpcykpLgoKVGhlIGltcGxlbWVudGF0aW9uIG9mIEdTRUEgd2UgdXNlIGluIGhlcmUgZXhhbXBsZXMgcmVxdWlyZXMgYSBnZW5lIGxpc3Qgb3JkZXJlZCBieSBzb21lIHN0YXRpc3RpYyBhbmQgaW5wdXQgZ2VuZSBzZXRzLgpXaGVuIHlvdSB1c2UgcHJldmlvdXNseSBjb21wdXRlZCBnZW5lLWxldmVsIHN0YXRpc3RpY3Mgd2l0aCBHU0VBLCBpdCBpcyBjYWxsZWQgR1NFQSBwcmUtcmFua2VkLgoKIyMjIEdlbmUgc2V0cwoKSW4gdGhlIHByZXZpb3VzIG5vdGVib29rLCB3ZSB1c2VkIEtFR0cgcGF0aHdheXMgZm9yIG92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMuCldlIGlkZW50aWZpZWQgcGF0aHdheXMgdGhhdCB3ZXJlIHNpZ25pZmljYW50bHkgb3Zlci1yZXByZXNlbnRlZCBhbmQgZm91bmQgdGhhdCB0aGUgc2lnbmlmaWNhbnQgcGF0aHdheXMgc2hhcmVkIGdlbmVzLgpGQ1MgbWV0aG9kcyBhbmFseXplIGVhY2ggcGF0aHdheSBpbmRlcGVuZGVudGx5IGFuZCBlc3NlbnRpYWxseSBpZ25vcmUgdGhpcyBvdmVybGFwIGJldHdlZW4gZ2VuZSBzZXRzLgoKTVNpZ0RCIGluY2x1ZGVzIGEgY29sbGVjdGlvbiBjYWxsZWQgSGFsbG1hcmsgZ2VuZSBzZXRzIChbTGliZXJ6b24gKmV0IGFsLiogMjAxNV0oaHR0cHM6Ly9keC5kb2kub3JnLzEwLjEwMTYlMkZqLmNlbHMuMjAxNS4xMi4wMDQpKQpIZXJlJ3MgYW4gZXhjZXJwdCBvZiB0aGUgW2NvbGxlY3Rpb24gZGVzY3JpcHRpb25dKGh0dHBzOi8vd3d3LmdzZWEtbXNpZ2RiLm9yZy9nc2VhL21zaWdkYi9jb2xsZWN0aW9uX2RldGFpbHMuanNwI0gpOgoKPiBIYWxsbWFyayBnZW5lIHNldHMgc3VtbWFyaXplIGFuZCByZXByZXNlbnQgc3BlY2lmaWMgd2VsbC1kZWZpbmVkIGJpb2xvZ2ljYWwgc3RhdGVzIG9yIHByb2Nlc3NlcyBhbmQgZGlzcGxheSBjb2hlcmVudCBleHByZXNzaW9uLiBUaGVzZSBnZW5lIHNldHMgd2VyZSBnZW5lcmF0ZWQgYnkgYSBjb21wdXRhdGlvbmFsIG1ldGhvZG9sb2d5IGJhc2VkIG9uIGlkZW50aWZ5aW5nIGdlbmUgc2V0IG92ZXJsYXBzIGFuZCByZXRhaW5pbmcgZ2VuZXMgdGhhdCBkaXNwbGF5IGNvb3JkaW5hdGUgZXhwcmVzc2lvbi4gVGhlIGhhbGxtYXJrcyByZWR1Y2Ugbm9pc2UgYW5kIHJlZHVuZGFuY3kgYW5kIHByb3ZpZGUgYSBiZXR0ZXIgZGVsaW5lYXRlZCBiaW9sb2dpY2FsIHNwYWNlIGZvciBHU0VBLgoKV2UnbGwgdXNlIHRoZSBIYWxsbWFyayBjb2xsZWN0aW9uIGZvciBHU0VBLgpOb3RhYmx5LCB0aGVyZSBhcmUgb25seSA1MCBnZW5lIHNldHMgaW5jbHVkZWQgaW4gdGhpcyBjb2xsZWN0aW9uLgpUaGUgZmV3ZXIgZ2VuZSBzZXRzIHdlIHRlc3QsIHRoZSBsb3dlciBvdXIgbXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0aW5nIGJ1cmRlbi4KCldlIGNhbiByZXRyaWV2ZSBvbmx5IHRoZSBIYWxsbWFyayBnZW5lIHNldHMgYnkgc3BlY2lmeWluZyBgY2F0ZWdvcnkgPSAiSCJgIHRvIHRoZSBgbXNpZ2RicigpYCBmdW5jdGlvbi4KCmBgYHtyIGhhbGxtYXJrX3NldHMsIGxpdmUgPSBUUlVFfQpoc19oYWxsbWFya19kZiA8LSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yeSA9ICJIIikKYGBgCgojIyBEaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIHJlc3VsdHMKCmBgYHtyIHJlYWRfaW5fZGdlLCBsaXZlID0gVFJVRX0KIyBSZWFkIGluIHRoZSBER0UgcmVzdWx0cwpkZ2VfcmVzdWx0c19kZiA8LSByZWFkcjo6cmVhZF90c3YoZGdlX3Jlc3VsdHNfZmlsZSkKYGBgCkxldCdzIHRha2UgYSBwZWVrIGF0IHRoZSBjb250ZW50cyBvZiB0aGlzIGZpbGUuCgpgYGB7ciBoZWFkX2RnZSwgbGl2ZSA9IFRSVUV9CmhlYWQoZGdlX3Jlc3VsdHNfZGYpCmBgYAoKU2luY2UgdGhpcyBkYXRhIGZyYW1lIG9mIERHRSByZXN1bHRzIGluY2x1ZGVzIGdlbmUgc3ltYm9scywgd2UgZG8gbm90IG5lZWQgdG8gcGVyZm9ybSBhbnkga2luZCBvZiBnZW5lIGlkZW50aWZpZXIgY29udmVyc2lvbi4KV2UgZG8sIGhvd2V2ZXIsIG5lZWQgdG8gY2hlY2sgZm9yIGR1cGxpY2F0ZSBnZW5lIHN5bWJvbHMuCldlIGNhbiBhY2NvbXBsaXNoIHRoaXMgd2l0aCBgZHVwbGljYXRlZCgpYCwgd2hpY2ggcmV0dXJucyBhIGxvZ2ljYWwgdmVjdG9yIChlLmcuLCBgVFJVRWAgb3IgYEZBTFNFYCkuClRoZSBmdW5jdGlvbiBgc3VtKClgIHdpbGwgY291bnQgYFRSVUVgIHZhbHVlcyBhcyAxcyBhbmQgYEZBTFNFYCBhcyAwcywgc28gdXNpbmcgaXQgd2l0aCBgZHVwbGljYXRlZCgpYCB3aWxsIGNvdW50IHRoZSBudW1iZXIgb2YgZHVwbGljYXRlIHZhbHVlcy4KCmBgYHtyIGFueV9kdXBsaWNhdGVkLCBsaXZlID0gVFJVRX0Kc3VtKGR1cGxpY2F0ZWQoZGdlX3Jlc3VsdHNfZGYkZ2VuZV9zeW1ib2wpKQpgYGAKClRoaXMgd2lsbCBjYXVzZSBhIHByb2JsZW0gd2hlbiB3ZSBnbyB0byBydW4gR1NFQS4KCiMjIyBSZW1vdmluZyBkdXBsaWNhdGVzCgpUaGUgR1NFQSBhcHByb2FjaCByZXF1aXJlcyBvbiBkaXNjcmltaW5hdGluZyBiZXR3ZWVuIGdlbmVzIHRoYXQgYXJlIGluIGEgZ2VuZSBzZXQgYW5kIHRob3NlIHRoYXQgYXJlIG5vdC4KUHJhY3RpY2FsbHkgc3BlYWtpbmcsIGdlbmUgc2V0cyBhcmUganVzdCBjb2xsZWN0aW9ucyBvZiBnZW5lIGlkZW50aWZpZXJzIQpXaGVuIHRoZSBmdW5jdGlvbiB3ZSB1c2UgZm9yIEdTRUEgcHJlLXJhbmtlZCBnZXRzIGEgbGlzdCB3aXRoIGR1cGxpY2F0ZWQgZ2VuZSBpZGVudGlmaWVycywgaXQgY2FuIHByb2R1Y2UgdW5leHBlY3RlZCByZXN1bHRzLgoKQ29tcGFyZWQgdG8gdGhlIHRvdGFsIG51bWJlciBvZiBnZW5lcyB0aGF0IGFyZSBpbiBvdXIgcmVzdWx0cywgdGhlcmUgYXJlIG5vdCBhIGxvdCBvZiBkdXBsaWNhdGVzIGJ1dCB3ZSdsbCBzdGlsbCBuZWVkIHRvIG1ha2UgYSBkZWNpc2lvbiBhYm91dCBob3cgdG8gaGFuZGxlIHRoZW0uCgpMZXQncyBnZXQgYSB2ZWN0b3Igb2YgdGhlIGR1cGxpY2F0ZWQgZ2VuZSBzeW1ib2xzIHNvIHdlIGNhbiB1c2UgaXQgdG8gZXhwbG9yZSBvdXIgZmlsdGVyaW5nIHN0ZXBzLgoKYGBge3IgZ2VuZV9kdXBzLCBsaXZlID0gVFJVRX0KZHVwbGljYXRlZF9nZW5lX3N5bWJvbHMgPC0gZGdlX3Jlc3VsdHNfZGYgfD4KICBkcGx5cjo6ZmlsdGVyKGR1cGxpY2F0ZWQoZ2VuZV9zeW1ib2wpKSB8PgogIGRwbHlyOjpwdWxsKGdlbmVfc3ltYm9sKQpgYGAKCk5vdyB3ZSdsbCBsb29rIGF0IHRoZSB2YWx1ZXMgZm9yIHRoZSB0aGUgZHVwbGljYXRlZCBnZW5lIHN5bWJvbHMuCgpgYGB7ciBzaG93X2dlbmVfZHVwc30KZGdlX3Jlc3VsdHNfZGYgfD4KICBkcGx5cjo6ZmlsdGVyKGdlbmVfc3ltYm9sICVpbiUgZHVwbGljYXRlZF9nZW5lX3N5bWJvbHMpIHw+CiAgZHBseXI6OmFycmFuZ2UoZ2VuZV9zeW1ib2wpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHRoZSBhc3NvY2lhdGVkIHZhbHVlcyB2YXJ5IGZvciBlYWNoIHJvdy4KCkxldCdzIGtlZXAgdGhlIGdlbmUgc3ltYm9scyBhc3NvY2lhdGVkIHdpdGggdGhlIGhpZ2hlciBhYnNvbHV0ZSB2YWx1ZSBvZiB0aGUgbG9nMiBmb2xkIGNoYW5nZS4KClJldGFpbmluZyB0aGUgaW5zdGFuY2Ugb2YgdGhlIGdlbmUgc3ltYm9scyB3aXRoIHRoZSBoaWdoZXIgYWJzb2x1dGUgdmFsdWUgb2YgYSBnZW5lLWxldmVsIHN0YXRpc3RpYyBtZWFucyB0aGF0IHdlIHdpbGwgcmV0YWluIHRoZSB2YWx1ZSB0aGF0IGlzIGxpa2VseSB0byBiZSBtb3JlIGhpZ2hseS0gb3IgbG93bHktcmFua2VkIG9yLCBwdXQgYW5vdGhlciB3YXksIHRoZSB2YWx1ZXMgbGVzcyBsaWtlbHkgdG8gYmUgdG93YXJkcyB0aGUgbWlkZGxlIG9mIHRoZSByYW5rZWQgZ2VuZSBsaXN0LgpXZSBzaG91bGQga2VlcCB0aGlzIGRlY2lzaW9uIGluIG1pbmQgd2hlbiBpbnRlcnByZXRpbmcgb3VyIHJlc3VsdHMuCkZvciBleGFtcGxlLCBpZiBhbGwgdGhlIGR1cGxpY2F0ZSBpZGVudGlmaWVycyBoYXBwZW5lZCB0byBiZSBpbiBhIHBhcnRpY3VsYXIgZ2VuZSBzZXQsIHdlIG1heSBnZXQgYW4gb3Zlcmx5IG9wdGltaXN0aWMgdmlldyBvZiBob3cgcGVydHVyYmVkIHRoYXQgZ2VuZSBzZXQgaXMgYmVjYXVzZSB3ZSBwcmVmZXJlbnRpYWxseSBzZWxlY3RlZCBpbnN0YW5jZXMgb2YgdGhlIGlkZW50aWZpZXIgdGhhdCBoYXZlIGEgaGlnaGVyIGFic29sdXRlIHZhbHVlIG9mIHRoZSBzdGF0aXN0aWMgdXNlZCBmb3IgcmFua2luZy4KCkluIHRoZSBuZXh0IGNodW5rLCB3ZSBhcmUgZ29pbmcgdG8gZmlsdGVyIG91dCB0aGUgZHVwbGljYXRlZCByb3dzIHVzaW5nIHRoZSBgZHBseXI6OmRpc3RpbmN0KClgIGZ1bmN0aW9uIGFmdGVyIHNvcnRpbmcgYnkgYWJzb2x1dGUgdmFsdWUgb2YgdGhlIGxvZzIgZm9sZCBjaGFuZ2UuClRoaXMgd2lsbCBrZWVwIHRoZSBmaXJzdCByb3cgd2l0aCB0aGUgZHVwbGljYXRlZCB2YWx1ZSB0aHVzIGtlZXBpbmcgdGhlIHJvdyB3aXRoIHRoZSBsYXJnZXN0IGFic29sdXRlIHZhbHVlLgoKYGBge3IgZmlsdGVyX2RnZX0KZmlsdGVyZWRfZGdlX2RmIDwtIGRnZV9yZXN1bHRzX2RmIHw+CiAgIyBTb3J0IHNvIHRoYXQgdGhlIGhpZ2hlc3QgYWJzb2x1dGUgdmFsdWVzIG9mIHRoZSBsb2cyIGZvbGQgY2hhbmdlIGFyZSBhdCB0aGUKICAjIHRvcAogIGRwbHlyOjphcnJhbmdlKGRwbHlyOjpkZXNjKGFicyhsb2cyRm9sZENoYW5nZSkpKSB8PgogICMgRmlsdGVyIG91dCB0aGUgZHVwbGljYXRlZCByb3dzIHVzaW5nIGBkcGx5cjo6ZGlzdGluY3QoKWAKICBkcGx5cjo6ZGlzdGluY3QoZ2VuZV9zeW1ib2wsIC5rZWVwX2FsbCA9IFRSVUUpCmBgYAoKTGV0J3Mgc2VlIHdoYXQgaGFwcGVuZWQgdG8gb3VyIGR1cGxpY2F0ZSBpZGVudGlmaWVycy4KCmBgYHtyIHNob3dfZmlsdGVyZWRfZGdlLCBsaXZlID0gVFJVRX0KIyBTdWJzZXQgdG8gJiBhcnJhbmdlIGJ5IGdlbmUgc3ltYm9scyB0aGF0IHdlcmUgZHVwbGljYXRlZCBpbiB0aGUgb3JpZ2luYWwKIyBkYXRhIGZyYW1lIG9mIHJlc3VsdHMKZmlsdGVyZWRfZGdlX2RmIHw+CiAgZHBseXI6OmZpbHRlcihnZW5lX3N5bWJvbCAlaW4lIGR1cGxpY2F0ZWRfZ2VuZV9zeW1ib2xzKSB8PgogIGRwbHlyOjphcnJhbmdlKGdlbmVfc3ltYm9sKQpgYGAKCk5vdyB3ZSdyZSByZWFkeSB0byBwcmVwIG91ciBwcmUtcmFua2VkIGxpc3QgZm9yIEdTRUEuCgojIyMgUHJlLXJhbmtlZCBsaXN0CgpUaGUgYEdTRUEoKWAgZnVuY3Rpb24gdGFrZXMgYSBwcmUtcmFua2VkIChzb3J0ZWQpIG5hbWVkIHZlY3RvciBvZiBzdGF0aXN0aWNzLCB3aGVyZSB0aGUgbmFtZXMgaW4gdGhlIHZlY3RvciBhcmUgZ2VuZSBpZGVudGlmaWVycy4KVGhpcyBpcyBzdGVwIDEgLS0gZ2VuZS1sZXZlbCBzdGF0aXN0aWNzLgoKYGBge3IgbGZjX3ZlY3Rvcn0KbGZjX3ZlY3RvciA8LSBmaWx0ZXJlZF9kZ2VfZGYgfD4KICAjIEV4dHJhY3QgYSB2ZWN0b3Igb2YgYGxvZzJGb2xkQ2hhbmdlYCBuYW1lZCBieSBgZ2VuZV9zeW1ib2xgCiAgZHBseXI6OnB1bGwobG9nMkZvbGRDaGFuZ2UsIG5hbWUgPSBnZW5lX3N5bWJvbCkKbGZjX3ZlY3RvciA8LSBzb3J0KGxmY192ZWN0b3IsIGRlY3JlYXNpbmcgPSBUUlVFKQpgYGAKCkxldCdzIGxvb2sgYXQgdGhlIHRvcCByYW5rZWQgdmFsdWVzLgoKYGBge3IgaGVhZF9sZmMsIGxpdmUgPSBUUlVFfQojIExvb2sgYXQgZmlyc3QgZW50cmllcyBvZiB0aGUgbG9nMiBmb2xkIGNoYW5nZSB2ZWN0b3IKaGVhZChsZmNfdmVjdG9yKQpgYGAKCkFuZCB0aGUgYm90dG9tIG9mIHRoZSBsaXN0LgoKYGBge3IgdGFpbF9sZmMsIGxpdmUgPSBUUlVFfQojIExvb2sgYXQgdGhlIGxhc3QgZW50cmllcyBvZiB0aGUgbG9nMiBmb2xkIGNoYW5nZSB2ZWN0b3IKdGFpbChsZmNfdmVjdG9yKQpgYGAKCiMjIFJ1biBHU0VBCgpOb3cgZm9yIHRoZSBhbmFseXNpcyEKCldlIGNhbiB1c2UgdGhlIGBHU0VBKClgIGZ1bmN0aW9uIHRvIHBlcmZvcm0gR1NFQSB3aXRoIGFueSBnZW5lcmljIHNldCBvZiBnZW5lIHNldHMsIGJ1dCB0aGVyZSBhcmUgc2V2ZXJhbCBmdW5jdGlvbnMgZm9yIHVzaW5nIHNwZWNpZmljLCBjb21tb25seSB1c2VkIGdlbmUgc2V0cyAoZS5nLiwgYGdzZUtFR0coKWApLgoKYGBge3IgcnVuX2dzZWF9CmdzZWFfcmVzdWx0cyA8LSBHU0VBKGdlbmVMaXN0ID0gbGZjX3ZlY3RvciwgICMgb3JkZXJlZCByYW5rZWQgZ2VuZSBsaXN0CiAgICAgICAgICAgICAgICAgICAgIG1pbkdTU2l6ZSA9IDI1LCAgIyBtaW5pbXVtIGdlbmUgc2V0IHNpemUKICAgICAgICAgICAgICAgICAgICAgbWF4R1NTaXplID0gNTAwLCAgIyBtYXhpbXVtIGdlbmUgc2V0IHNldAogICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1LAogICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwgICMgY29ycmVjdGlvbiBmb3IgbXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0aW5nCiAgICAgICAgICAgICAgICAgICAgIFRFUk0yR0VORSA9IGRwbHlyOjpzZWxlY3QoaHNfaGFsbG1hcmtfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3NfbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX3N5bWJvbCkpCmBgYAoKVGhlIHdhcm5pbmcgYWJvdXQgdGllcyBtZWFucyB0aGF0IHRoZXJlIGFyZSBtdWx0aXBsZSBnZW5lcyB0aGF0IGhhdmUgdGhlIHNhbWUgbG9nMiBmb2xkIGNoYW5nZSB2YWx1ZS4KVGhpcyBwZXJjZW50YWdlIGlzIHNtYWxsIGFuZCB1bmxpa2VseSB0byBpbXBhY3Qgb3VyIHJlc3VsdHMuCkEgbGFyZ2UgbnVtYmVyIG9mIHRpZXMgbWlnaHQgdGVsbCB1cyB0aGVyZSdzIHNvbWV0aGluZyB3cm9uZyB3aXRoIG91ciBER0UgcmVzdWx0cyAoW0JhbGxlcmVhdSBfZXQgYWwuXyAyMDE4XShodHRwczovL2Jpb2luZm9ybWF0aWNzLWNvcmUtc2hhcmVkLXRyYWluaW5nLmdpdGh1Yi5pby9jcnVrLXN1bW1lci1zY2hvb2wtMjAxOC9STkFTZXEyMDE4L2h0bWwvMDZfR2VuZV9zZXRfdGVzdGluZy5uYi5odG1sKSkuCgpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgR1NFQSByZXN1bHRzLgoKYGBge3Igdmlld19nc2VhLCBsaXZlID0gVFJVRSwgZXZhbCA9IEZBTFNFfQpWaWV3KGdzZWFfcmVzdWx0c0ByZXN1bHQgfD4KICAgICAgIGRwbHlyOjphcnJhbmdlKGRwbHlyOjpkZXNjKE5FUykpCikKYGBgCgpOb3JtYWxpemVkIGVucmljaG1lbnQgc2NvcmVzIChORVMpIGFyZSBlbnJpY2htZW50IHNjb3JlcyB0aGF0IGFyZSBzY2FsZWQgdG8gbWFrZSBnZW5lIHNldHMgdGhhdCBjb250YWluIGRpZmZlcmVudCBudW1iZXIgb2YgZ2VuZXMgY29tcGFyYWJsZS4KCkxldCdzIHdyaXRlIHRoZXNlIHJlc3VsdHMgdG8gZmlsZS4KCmBgYHtyIHdyaXRlX2dzZWF9CmdzZWFfcmVzdWx0c0ByZXN1bHQgfD4gcmVhZHI6OndyaXRlX3Rzdihnc2VhX3Jlc3VsdHNfZmlsZSkKYGBgCgojIyMgVmlzdWFsaXppbmcgR1NFQSByZXN1bHRzCgpXZSBjYW4gdmlzdWFsaXplIEdTRUEgcmVzdWx0cyBmb3IgaW5kaXZpZHVhbCBwYXRod2F5cyBvciBnZW5lIHNldHMgdXNpbmcgYGVucmljaHBsb3Q6OmdzZWFwbG90KClgLgpMZXQncyB0YWtlIGEgbG9vayBhdCAzIGRpZmZlcmVudCBwYXRod2F5cyAtLSBvbmUgd2l0aCBhIGhpZ2hseSBwb3NpdGl2ZSBORVMsIG9uZSB3aXRoIGEgaGlnaGx5IG5lZ2F0aXZlIE5FUywgYW5kIG9uZSB0aGF0IHdhcyBub3QgYSBzaWduaWZpY2FudCByZXN1bHQgLS0gdG8gZ2V0IG1vcmUgaW5zaWdodCBpbnRvIGhvdyBFUyBhcmUgY2FsY3VsYXRlZC4KCiMjIyMgSGlnaGx5IFBvc2l0aXZlIE5FUwoKVGhlIGdlbmUgc2V0IGBIQUxMTUFSS19NWUNfVEFSR0VUU19WMWAgaGFkIGhpZ2ggcG9zaXRpdmUgbG9nMiBmb2xkIGNoYW5nZXMuClJlY2FsbCBhIHBvc2l0aXZlIGxvZzIgZm9sZCBjaGFuZ2UgbWVhbnMgYSBpdCBoYWQgYSBoaWdoZXIgZXhwcmVzc2lvbiB2YWx1ZSBpbiBfTVlDTl8gYW1wbGlmaWVkIGNlbGwgbGluZXMuCgpgYGB7ciBteWNfdjF9CmVucmljaHBsb3Q6OmdzZWFwbG90KGdzZWFfcmVzdWx0cywKICAgICAgICAgICAgICAgICAgICAgZ2VuZVNldElEID0gIkhBTExNQVJLX01ZQ19UQVJHRVRTX1YxIiwKICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiSEFMTE1BUktfTVlDX1RBUkdFVFNfVjEiLAogICAgICAgICAgICAgICAgICAgICBjb2xvci5saW5lID0gIiMwMDY2RkYiKQpgYGAKCk5vdGljZSBob3cgdGhlIGdlbmVzIHRoYXQgYXJlIGluIHRoZSBnZW5lIHNldCwgaW5kaWNhdGVkIGJ5IHRoZSBibGFjayBiYXJzLCB0ZW5kIHRvIGJlIG9uIHRoZSBsZWZ0IHNpZGUgb2YgdGhlIGdyYXBoIGluZGljYXRpbmcgdGhhdCB0aGV5IGhhdmUgcG9zaXRpdmUgZ2VuZS1sZXZlbCBzY29yZXMuCgojIyMjIEhpZ2hseSBOZWdhdGl2ZSBORVMKClRoZSBnZW5lIHNldCBgSEFMTE1BUktfSU5URVJGRVJPTl9BTFBIQV9SRVNQT05TRWAgaGFkIGEgaGlnaGx5IG5lZ2F0aXZlIE5FUy4KCmBgYHtyIGluZmxhbW1hdG9yeX0KZW5yaWNocGxvdDo6Z3NlYXBsb3QoZ3NlYV9yZXN1bHRzLAogICAgICAgICAgICAgICAgICAgICBnZW5lU2V0SUQgPSAiSEFMTE1BUktfSU5URVJGRVJPTl9BTFBIQV9SRVNQT05TRSIsCiAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIkhBTExNQVJLX0lOVEVSRkVST05fQUxQSEFfUkVTUE9OU0UiLAogICAgICAgICAgICAgICAgICAgICBjb2xvci5saW5lID0gIiMwMDY2RkYiKQpgYGAKClRoaXMgZ2VuZSBzZXQgc2hvd3MgdGhlIG9wcG9zaXRlIHBhdHRlcm4gLS0gZ2VuZXMgaW4gdGhlIHBhdGh3YXkgdGVuZCB0byBiZSBvbiB0aGUgcmlnaHQgc2lkZSBvZiB0aGUgZ3JhcGguCgojIyMjIEEgbm9uLXNpZ25pZmljYW50IHJlc3VsdAoKVGhlIGBAcmVzdWx0c2Agc2xvdCB3aWxsIG9ubHkgc2hvdyBnZW5lIHNldHMgdGhhdCBwYXNzIHRoZSBgcHZhbHVlQ3V0b2ZmYCB0aHJlc2hvbGQgd2Ugc3VwcGxpZWQgdG8gYEdTRUEoKWAsIGJ1dCB3ZSBjYW4gcGxvdCBhbnkgZ2VuZSBzZXQgc28gbG9uZyBhcyB3ZSBrbm93IGl0cyBuYW1lLgpMZXQncyBsb29rIGF0IGBIQUxMTUFSS19QNTNfUEFUSFdBWWAsIHdoaWNoIHdhcyBub3QgaW4gdGhlIHJlc3VsdHMgd2Ugdmlld2VkIGVhcmxpZXIuCgpgYGB7ciBwNTMsIGxpdmUgPSBUUlVFfQplbnJpY2hwbG90Ojpnc2VhcGxvdChnc2VhX3Jlc3VsdHMsCiAgICAgICAgICAgICAgICAgICAgIGdlbmVTZXRJRCA9ICJIQUxMTUFSS19QNTNfUEFUSFdBWSIsCiAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIkhBTExNQVJLX1A1M19QQVRIV0FZIiwKICAgICAgICAgICAgICAgICAgICAgY29sb3IubGluZSA9ICIjMDA2NkZGIikKYGBgCgpHZW5lcyBpbiB0aGUgcGF0aHdheSBhcmUgZGlzdHJpYnV0ZWQgbW9yZSBldmVubHkgdGhyb3VnaG91dCB0aGUgcmFua2VkIGxpc3QsIHJlc3VsdGluZyBpbiBhIG1vcmUgIm1pZGRsaW5nIiBzY29yZS4KCipOb3RlOiBUaGUgcGxvdHMgcmV0dXJuZWQgYnkgYGVucmljaHBsb3Q6OmdzZWFwbG90YCBhcmUgZ2dwbG90cywgc28gd2UgY291bGQgdXNlIGBnZ3Bsb3QyOjpnZ3NhdmUoKWAgdG8gc2F2ZSB0aGVtIHRvIGZpbGUgaWYgd2Ugd2FudGVkIHRvLioKCiMjIFNlc3Npb24gSW5mbwoKYGBge3Igc2Vzc2lvbl9pbmZvfQpzZXNzaW9uSW5mbygpCmBgYAo=