Objectives
This notebook will demonstrate how to:
- Prepare tabular data of gene-level statistics for use with Gene Set
Enrichment Analysis (GSEA)
- Access Molecular
Signatures Database gene set collections via the
msigdbr
package
- Perform GSEA with the
clusterProfiler
package
- Visualize GSEA results with the
enrichplot
package
In this notebook, we’ll analyze the differential expression results
from the last notebook.
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.
There are 3 general steps in FCS methods (Khatri et
al. 2012):
- Calculate a gene-level statistic (here, we’ll use the summary log
fold changes in our DESeq2 results)
- Aggregate gene-level statistics into a pathway-level statistic
- Assess the statistical significance of the pathway-level
statistic
Set up
Libraries
# set seed for reproducibility
set.seed(2025)
# 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
# We'll use the differential expression results as GSEA input
rms_analysis_dir <- file.path("analysis", "rms")
# We'll create a directory to specifically hold the pathway results if it doesn't
# exist yet
results_dir <- file.path(rms_analysis_dir, "pathway-analysis")
fs::dir_create(results_dir)
Output files
We’ll save our table of GSEA results as a TSV.
output_file <- file.path(results_dir,
"rms_myoblast_gsea_results.tsv")
Gene sets
We will use gene sets from the Molecular
Signatures Database (MSigDB) from the Broad Institute (Subramanian, Tamayo
et al. 2005). The msigdbr
package contains MSigDB datasets already in the tidy format required by
clusterProfiler
and supports multiple organisms.
Let’s take a look at what organisms the package supports.
msigdbr_species()
MSigDB contains 8 different gene set collections.
H: hallmark gene sets
C1: positional gene sets
C2: curated gene sets
C3: motif gene sets
C4: computational gene sets
C5: GO gene sets
C6: oncogenic signatures
C7: immunologic signatures
We’ll use the Hallmark collection for GSEA. 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.
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_hallmarks_df <- msigdbr(species = "Homo sapiens",
category = "H")
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.
DESeq2 results
deseq_df <- readr::read_tsv(input_file)
Rows: 60319 Columns: 27
── Column specification ────────────────────────────────────────────────────────
Delimiter: "\t"
chr (2): ensembl_id, gene_symbol
dbl (25): baseMean, log2FoldChange, lfcSE, pvalue, padj, SCPCL000478.mean, S...
ℹ 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.
head(deseq_df)
This data frame uses Ensembl gene identifiers. We’ll need to make
sure our gene sets use the same identifiers. Let’s take a look at the
first few rows of the data frame that contains the hallmark gene
sets.
head(hs_hallmarks_df)
We can see that the gene sets from msigdbr
have Ensembl
gene identifiers associated with them, so we don’t need to do any
conversion. However, we’ll need to pass the correct column to the
function that runs GSEA.
If we needed to do gene identifier conversion, we would likely use
the AnnotationDbi
package. You can see an example in this
Harvard Chan Bioinformatics Core Training material: https://hbctraining.github.io/DGE_workshop_salmon_online/lessons/AnnotationDbi_lesson.html
One good thing about Ensembl gene identifiers is that they are less
likely to be duplicated than, for example, gene symbols. (Multiple
Ensembl gene identifiers can map to the same symbol.)
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. So, let’s check for duplicates in the data
frame of DESeq2 results.
any(duplicated(deseq_df$ensembl_id))
[1] FALSE
There are no duplicates for us to worry about!
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 <- deseq_df |>
# Extract a vector of `log2FoldChange` named by `ensembl_id`
dplyr::pull(log2FoldChange, name = ensembl_id)
lfc_vector <- sort(lfc_vector, decreasing = TRUE)
Let’s look at the top ranked values.
# Look at first entries of the log fold change vector
head(lfc_vector)
ENSG00000263366 ENSG00000223760 ENSG00000253377 ENSG00000265843 ENSG00000104722
11.364714 10.573752 10.476990 10.199449 10.019651
ENSG00000228835
9.898818
And the bottom of the list.
# Look at the last entries of the log fold change vector
tail(lfc_vector)
ENSG00000269186 ENSG00000268388 ENSG00000165606 ENSG00000285640 ENSG00000118432
-10.93216 -11.35119 -11.36925 -11.90034 -11.92082
ENSG00000184221
-12.12577
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_hallmarks_df,
gs_name,
ensembl_gene)) # pass the correct identifier column
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 (20.32% of the list).
The order of those tied genes will be arbitrary, which may produce unexpected results.
leading edge analysis...
done...
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.
Pathways with significant, highly positive NES are enriched in ERMS
myoblasts, whereas pathways with significant, highly negative NES are
enriched in ARMS myoblasts.
Let’s write these results to file.
gsea_results@result |> readr::write_tsv(output_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
Let’s take look at a pathway with a highly positive NES
(HALLMARK_MYC_TARGETS_V2
) using a GSEA plot.
enrichplot::gseaplot(gsea_results,
geneSetID = "HALLMARK_MYC_TARGETS_V2",
title = "HALLMARK_MYC_TARGETS_V2",
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_MYOGENESIS
had a highly negative
NES.
enrichplot::gseaplot(gsea_results,
geneSetID = "HALLMARK_MYOGENESIS",
title = "HALLMARK_MYOGENESIS",
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 ]
LS0tCnRpdGxlOiAiUGF0aHdheSBhbmFseXNpczogR2VuZSBTZXQgRW5yaWNobWVudCBBbmFseXNpcyAoR1NFQSkiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKYXV0aG9yOiBDQ0RMIGZvciBBTFNGCmRhdGU6IDIwMjQKLS0tCgojIyBPYmplY3RpdmVzCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOgoKLSBQcmVwYXJlIHRhYnVsYXIgZGF0YSBvZiBnZW5lLWxldmVsIHN0YXRpc3RpY3MgZm9yIHVzZSB3aXRoIEdlbmUgU2V0IEVucmljaG1lbnQgQW5hbHlzaXMgKEdTRUEpCi0gQWNjZXNzIFtNb2xlY3VsYXIgU2lnbmF0dXJlcyBEYXRhYmFzZSBnZW5lIHNldCBjb2xsZWN0aW9uc10oaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2NvbGxlY3Rpb25zLmpzcCkgdmlhIHRoZSBgbXNpZ2RicmAgcGFja2FnZQotIFBlcmZvcm0gR1NFQSB3aXRoIHRoZSBgY2x1c3RlclByb2ZpbGVyYCBwYWNrYWdlCi0gVmlzdWFsaXplIEdTRUEgcmVzdWx0cyB3aXRoIHRoZSBgZW5yaWNocGxvdGAgcGFja2FnZQoKLS0tCgpJbiB0aGlzIG5vdGVib29rLCB3ZSdsbCBhbmFseXplIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzIGZyb20gdGhlIGxhc3Qgbm90ZWJvb2suCgpHU0VBIGlzIGEgZnVuY3Rpb25hbCBjbGFzcyBzY29yaW5nIChGQ1MpIGFwcHJvYWNoIHRvIHBhdGh3YXkgYW5hbHlzaXMgdGhhdCB3YXMgZmlyc3QgaW50cm9kdWNlZCBpbiBbU3VicmFtYW5pYW4gX2V0IGFsLl8gKDIwMDUpXShodHRwczovL2RvaS5vcmcvMTAuMTA3My9wbmFzLjA1MDY1ODAxMDIpLgpUaGUgcmF0aW9uYWxlIGJlaGluZCBGQ1MgYXBwcm9hY2hlcyBpcyB0aGF0IHNtYWxsIGNoYW5nZXMgaW4gaW5kaXZpZHVhbCBnZW5lcyB0aGF0IHBhcnRpY2lwYXRlIGluIHRoZSBzYW1lIGJpb2xvZ2ljYWwgcHJvY2VzcyBvciBwYXRod2F5IGNhbiBiZSBzaWduaWZpY2FudCBhbmQgb2YgYmlvbG9naWNhbCBpbnRlcmVzdC4KClRoZXJlIGFyZSAzIGdlbmVyYWwgc3RlcHMgaW4gRkNTIG1ldGhvZHMgKFtLaGF0cmkgX2V0IGFsLl8gMjAxMl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEzNzEvam91cm5hbC5wY2JpLjEwMDIzNzUpKToKCjEuIENhbGN1bGF0ZSBhIGdlbmUtbGV2ZWwgc3RhdGlzdGljIChoZXJlLCB3ZSdsbCB1c2UgdGhlIHN1bW1hcnkgbG9nIGZvbGQgY2hhbmdlcyBpbiBvdXIgREVTZXEyIHJlc3VsdHMpCjIuIEFnZ3JlZ2F0ZSBnZW5lLWxldmVsIHN0YXRpc3RpY3MgaW50byBhIHBhdGh3YXktbGV2ZWwgc3RhdGlzdGljCjMuIEFzc2VzcyB0aGUgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIG9mIHRoZSBwYXRod2F5LWxldmVsIHN0YXRpc3RpYwoKIyMjIyBPdGhlciByZXNvdXJjZXMKCiogRm9yIGFub3RoZXIgZXhhbXBsZSB1c2luZyBgY2x1c3RlclByb2ZpbGVyYCBmb3IgR1NFQSwgc2VlIFtfSW50cm8gdG8gREdFOiBGdW5jdGlvbmFsIEFuYWx5c2lzLl8gZnJvbSBIYXJ2YXJkIENoYW4gQmlvaW5mb3JtYXRpY3MgQ29yZSBUcmFpbmluZy5dKGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL1RyYWluaW5nLW1vZHVsZXMvREdFLWZ1bmN0aW9uYWwtYW5hbHlzaXMvbGVzc29ucy8wMl9mdW5jdGlvbmFsX2FuYWx5c2lzLmh0bWwpCiogVGhlIHdheSB3ZSdsbCB1c2UgYGNsdXN0ZXJQcm9maWxlcmAgaGVyZSB1c2VzIGBmZ3NlYWAgKEZhc3QgR2VuZSBTZXQgRW5yaWNobWVudCBBbmFseXNpcykgdW5kZXIgdGhlIGhvb2QuCllvdSBjYW4gcmVhZCBtb3JlIGFib3V0IGBmZ3NlYWAgaW4gW0tvcm90a2V2aWNoIF9ldCBhbC5fICgyMDIxKV0oaHR0cHM6Ly9kb2kub3JnLzEwLjExMDEvMDYwMDEyKS4KKiBTZWUgdGhlIFtyZWZpbmUuYmlvIGV4YW1wbGVzIGZvciAiR2VuZSBzZXQgZW5yaWNobWVudCBhbmFseXNpcyAtIFJOQS1zZXEiXShodHRwczovL2FsZXhzbGVtb25hZGUuZ2l0aHViLmlvL3JlZmluZWJpby1leGFtcGxlcy8wMy1ybmFzZXEvcGF0aHdheS1hbmFseXNpc19ybmFzZXFfMDJfZ3NlYS5odG1sKSBmcm9tIHdoaWNoIHRoaXMgbWF0ZXJpYWwgaGFzIGJlZW4gYWRhcHRlZC4KCiMjIFNldCB1cAoKIyMjIExpYnJhcmllcwoKYGBge3Igc2V0dXB9CiMgc2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgyMDI1KQoKIyBQYWNrYWdlIHRvIHJ1biBHU0VBCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQojIFBhY2thZ2UgdGhhdCBjb250YWlucyB0aGUgTVNpZ0RCIGdlbmUgc2V0cyBpbiB0aWR5IGZvcm1hdApsaWJyYXJ5KG1zaWdkYnIpCmBgYAoKIyMjIERpcmVjdG9yaWVzIGFuZCBGaWxlcwoKIyMjIyBEaXJlY3RvcmllcwoKYGBge3IgY3JlYXRlX2RpciwgbGl2ZSA9IFRSVUV9CiMgV2UnbGwgdXNlIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzIGFzIEdTRUEgaW5wdXQKcm1zX2FuYWx5c2lzX2RpciA8LSBmaWxlLnBhdGgoImFuYWx5c2lzIiwgInJtcyIpCgojIFdlJ2xsIGNyZWF0ZSBhIGRpcmVjdG9yeSB0byBzcGVjaWZpY2FsbHkgaG9sZCB0aGUgcGF0aHdheSByZXN1bHRzIGlmIGl0IGRvZXNuJ3QKIyBleGlzdCB5ZXQKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKHJtc19hbmFseXNpc19kaXIsICJwYXRod2F5LWFuYWx5c2lzIikKZnM6OmRpcl9jcmVhdGUocmVzdWx0c19kaXIpCmBgYAoKIyMjIyBJbnB1dCBmaWxlcwoKCmBgYHtyIGlucHV0X2ZpbGVzfQppbnB1dF9maWxlIDwtIGZpbGUucGF0aChybXNfYW5hbHlzaXNfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAiZGVzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAicm1zX215b2JsYXN0X2Rlc2VxX3Jlc3VsdHMudHN2IikKYGBgCgojIyMjIE91dHB1dCBmaWxlcwoKV2UnbGwgc2F2ZSBvdXIgdGFibGUgb2YgR1NFQSByZXN1bHRzIGFzIGEgVFNWLgoKYGBge3Igb3V0cHV0X2ZpbGVzfQpvdXRwdXRfZmlsZSA8LSBmaWxlLnBhdGgocmVzdWx0c19kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAicm1zX215b2JsYXN0X2dzZWFfcmVzdWx0cy50c3YiKQpgYGAKCiMjIEdlbmUgc2V0cwoKV2Ugd2lsbCB1c2UgZ2VuZSBzZXRzIGZyb20gdGhlIFtNb2xlY3VsYXIgU2lnbmF0dXJlcyBEYXRhYmFzZSAoTVNpZ0RCKV0oaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2luZGV4LmpzcCkgZnJvbSB0aGUgQnJvYWQgSW5zdGl0dXRlIChbU3VicmFtYW5pYW4sIFRhbWF5byAqZXQgYWwuKiAyMDA1XShodHRwczovL2RvaS5vcmcvMTAuMTA3My9wbmFzLjA1MDY1ODAxMDIpKS4KVGhlIFtgbXNpZ2RicmBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tc2lnZGJyL2luZGV4Lmh0bWwpIHBhY2thZ2UgY29udGFpbnMgTVNpZ0RCIGRhdGFzZXRzIGFscmVhZHkgaW4gdGhlIHRpZHkgZm9ybWF0IHJlcXVpcmVkIGJ5IGBjbHVzdGVyUHJvZmlsZXJgIGFuZCBzdXBwb3J0cyBtdWx0aXBsZSBvcmdhbmlzbXMuCgpMZXQncyB0YWtlIGEgbG9vayBhdCB3aGF0IG9yZ2FuaXNtcyB0aGUgcGFja2FnZSBzdXBwb3J0cy4KCmBgYHtyIHNob3dfc3BlY2llc30KbXNpZ2Ricl9zcGVjaWVzKCkKYGBgCgpNU2lnREIgY29udGFpbnMgOCBkaWZmZXJlbnQgZ2VuZSBzZXQgY29sbGVjdGlvbnMuCgogICAgSDogaGFsbG1hcmsgZ2VuZSBzZXRzCiAgICBDMTogcG9zaXRpb25hbCBnZW5lIHNldHMKICAgIEMyOiBjdXJhdGVkIGdlbmUgc2V0cwogICAgQzM6IG1vdGlmIGdlbmUgc2V0cwogICAgQzQ6IGNvbXB1dGF0aW9uYWwgZ2VuZSBzZXRzCiAgICBDNTogR08gZ2VuZSBzZXRzCiAgICBDNjogb25jb2dlbmljIHNpZ25hdHVyZXMKICAgIEM3OiBpbW11bm9sb2dpYyBzaWduYXR1cmVzCgpXZSdsbCB1c2UgdGhlIEhhbGxtYXJrIGNvbGxlY3Rpb24gZm9yIEdTRUEuCkhlcmUncyBhbiBleGNlcnB0IG9mIHRoZSBbY29sbGVjdGlvbiBkZXNjcmlwdGlvbl0oaHR0cHM6Ly93d3cuZ3NlYS1tc2lnZGIub3JnL2dzZWEvbXNpZ2RiL2NvbGxlY3Rpb25fZGV0YWlscy5qc3AjSCk6Cgo+IEhhbGxtYXJrIGdlbmUgc2V0cyBzdW1tYXJpemUgYW5kIHJlcHJlc2VudCBzcGVjaWZpYyB3ZWxsLWRlZmluZWQgYmlvbG9naWNhbCBzdGF0ZXMgb3IgcHJvY2Vzc2VzIGFuZCBkaXNwbGF5IGNvaGVyZW50IGV4cHJlc3Npb24uIFRoZXNlIGdlbmUgc2V0cyB3ZXJlIGdlbmVyYXRlZCBieSBhIGNvbXB1dGF0aW9uYWwgbWV0aG9kb2xvZ3kgYmFzZWQgb24gaWRlbnRpZnlpbmcgZ2VuZSBzZXQgb3ZlcmxhcHMgYW5kIHJldGFpbmluZyBnZW5lcyB0aGF0IGRpc3BsYXkgY29vcmRpbmF0ZSBleHByZXNzaW9uLiBUaGUgaGFsbG1hcmtzIHJlZHVjZSBub2lzZSBhbmQgcmVkdW5kYW5jeSBhbmQgcHJvdmlkZSBhIGJldHRlciBkZWxpbmVhdGVkIGJpb2xvZ2ljYWwgc3BhY2UgZm9yIEdTRUEuCgpOb3RhYmx5LCB0aGVyZSBhcmUgb25seSA1MCBnZW5lIHNldHMgaW5jbHVkZWQgaW4gdGhpcyBjb2xsZWN0aW9uLgpUaGUgZmV3ZXIgZ2VuZSBzZXRzIHdlIHRlc3QsIHRoZSBsb3dlciBvdXIgbXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0aW5nIGJ1cmRlbi4KCldlIGNhbiByZXRyaWV2ZSBvbmx5IHRoZSBIYWxsbWFyayBnZW5lIHNldHMgYnkgc3BlY2lmeWluZyBgY2F0ZWdvcnkgPSAiSCJgIHRvIHRoZSBgbXNpZ2RicigpYCBmdW5jdGlvbi4KCmBgYHtyIGltbXVub2xvZ2ljX3NldHMsIGxpdmUgPSBUUlVFfQpoc19oYWxsbWFya3NfZGYgPC0gbXNpZ2RicihzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGVnb3J5ID0gIkgiKQpgYGAKCiMjIEdlbmUgU2V0IEVucmljaG1lbnQgQW5hbHlzaXMKCl9BZGFwdGVkIGZyb20gW3JlZmluZS5iaW8gZXhhbXBsZXNdKGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL3JlZmluZWJpby1leGFtcGxlcy9ibG9iLzMzY2RlZmY2NmQ1N2Y5ZmU4ZWU0ZmNiNTE1NmFlYTRhYzJkY2UwN2YvMDMtcm5hc2VxL3BhdGh3YXktYW5hbHlzaXNfcm5hc2VxXzAyX2dzZWEuUm1kKV8KCiFbXShkaWFncmFtcy9zdWJyYW1hbmlhbl9maWcxLmpwZykKCioqRmlndXJlIDEuIFtTdWJyYW1hbmlhbiBfZXQgYWwuXyAoMjAwNSldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDczL3BuYXMuMDUwNjU4MDEwMikuKioKCkdTRUEgY2FsY3VsYXRlcyBhIHBhdGh3YXktbGV2ZWwgbWV0cmljLCBjYWxsZWQgYW4gZW5yaWNobWVudCBzY29yZSAoc29tZXRpbWVzIGFiYnJldmlhdGVkIGFzIEVTKSwgYnkgcmFua2luZyBnZW5lcyBieSBhIGdlbmUtbGV2ZWwgc3RhdGlzdGljLgpUaGlzIHNjb3JlIHJlZmxlY3RzIHdoZXRoZXIgb3Igbm90IGEgZ2VuZSBzZXQgb3IgcGF0aHdheSBpcyBvdmVyLXJlcHJlc2VudGVkIGF0IHRoZSB0b3Agb3IgYm90dG9tIG9mIHRoZSBnZW5lIHJhbmtpbmdzIChbU3VicmFtYW5pYW4gX2V0IGFsLl8gMjAwNV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwNzMvcG5hcy4wNTA2NTgwMTAyKTsgW1l1XShodHRwOi8veXVsYWItc211LnRvcC9jbHVzdGVyUHJvZmlsZXItYm9vay9jaGFwdGVyMi5odG1sI2dlbmUtc2V0LWVucmljaG1lbnQtYW5hbHlzaXMpKQoKU3BlY2lmaWNhbGx5LCBhbGwgZ2VuZXMgYXJlIHJhbmtlZCBmcm9tIG1vc3QgcG9zaXRpdmUgdG8gbW9zdCBuZWdhdGl2ZSBiYXNlZCBvbiB0aGVpciBzdGF0aXN0aWMgYW5kIGEgcnVubmluZyBzdW0gaXMgY2FsY3VsYXRlZDoKU3RhcnRpbmcgd2l0aCB0aGUgbW9zdCBoaWdobHkgcmFua2VkIGdlbmVzLCB0aGUgcnVubmluZyBzdW0gaW5jcmVhc2VzIGZvciBlYWNoIGdlbmUgaW4gdGhlIHBhdGh3YXkgYW5kIGRlY3JlYXNlcyBmb3IgZWFjaCBnZW5lIG5vdCBpbiB0aGUgcGF0aHdheS4KVGhlIGVucmljaG1lbnQgc2NvcmUgZm9yIGEgcGF0aHdheSBpcyB0aGUgcnVubmluZyBzdW0ncyBtYXhpbXVtIGRldmlhdGlvbiBmcm9tIHplcm8uCkdTRUEgYWxzbyBhc3Nlc3NlcyBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2Ugb2YgdGhlIHNjb3JlcyBmb3IgZWFjaCBwYXRod2F5IHRocm91Z2ggcGVybXV0YXRpb24gdGVzdGluZy4KQXMgYSByZXN1bHQsIGVhY2ggaW5wdXQgcGF0aHdheSB3aWxsIGhhdmUgYSBwLXZhbHVlIGFzc29jaWF0ZWQgd2l0aCBpdCB0aGF0IGlzIHRoZW4gY29ycmVjdGVkIGZvciBtdWx0aXBsZSBoeXBvdGhlc2lzIHRlc3RpbmcgKFtTdWJyYW1hbmlhbiBfZXQgYWwuXyAyMDA1XShodHRwczovL2RvaS5vcmcvMTAuMTA3My9wbmFzLjA1MDY1ODAxMDIpOyBbWXVdKGh0dHA6Ly95dWxhYi1zbXUudG9wL2NsdXN0ZXJQcm9maWxlci1ib29rL2NoYXB0ZXIyLmh0bWwjZ2VuZS1zZXQtZW5yaWNobWVudC1hbmFseXNpcykpLgoKVGhlIGltcGxlbWVudGF0aW9uIG9mIEdTRUEgd2UgdXNlIGluIGhlcmUgZXhhbXBsZXMgcmVxdWlyZXMgYSBnZW5lIGxpc3Qgb3JkZXJlZCBieSBzb21lIHN0YXRpc3RpYyBhbmQgaW5wdXQgZ2VuZSBzZXRzLgpXaGVuIHlvdSB1c2UgcHJldmlvdXNseSBjb21wdXRlZCBnZW5lLWxldmVsIHN0YXRpc3RpY3Mgd2l0aCBHU0VBLCBpdCBpcyBjYWxsZWQgR1NFQSBwcmUtcmFua2VkLgoKIyMgREVTZXEyIHJlc3VsdHMKCmBgYHtyIHJlYWRfaW5fbWFya2VycywgbGl2ZSA9IFRSVUV9CmRlc2VxX2RmIDwtIHJlYWRyOjpyZWFkX3RzdihpbnB1dF9maWxlKQpgYGAKCmBgYHtyIGRlc2VxX2hlYWR9CmhlYWQoZGVzZXFfZGYpCmBgYAoKVGhpcyBkYXRhIGZyYW1lIHVzZXMgRW5zZW1ibCBnZW5lIGlkZW50aWZpZXJzLgpXZSdsbCBuZWVkIHRvIG1ha2Ugc3VyZSBvdXIgZ2VuZSBzZXRzIHVzZSB0aGUgc2FtZSBpZGVudGlmaWVycy4KTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBkYXRhIGZyYW1lIHRoYXQgY29udGFpbnMgdGhlIGhhbGxtYXJrIGdlbmUgc2V0cy4KCmBgYHtyIGhhbGxtYXJrX2hlYWQsIGxpdmUgPSBUUlVFfQpoZWFkKGhzX2hhbGxtYXJrc19kZikKYGBgCgpXZSBjYW4gc2VlIHRoYXQgdGhlIGdlbmUgc2V0cyBmcm9tIGBtc2lnZGJyYCBoYXZlIEVuc2VtYmwgZ2VuZSBpZGVudGlmaWVycyBhc3NvY2lhdGVkIHdpdGggdGhlbSwgc28gd2UgZG9uJ3QgbmVlZCB0byBkbyBhbnkgY29udmVyc2lvbi4KSG93ZXZlciwgd2UnbGwgbmVlZCB0byBwYXNzIHRoZSBjb3JyZWN0IGNvbHVtbiB0byB0aGUgZnVuY3Rpb24gdGhhdCBydW5zIEdTRUEuCgpJZiB3ZSBuZWVkZWQgdG8gZG8gZ2VuZSBpZGVudGlmaWVyIGNvbnZlcnNpb24sIHdlIHdvdWxkIGxpa2VseSB1c2UgdGhlIGBBbm5vdGF0aW9uRGJpYCBwYWNrYWdlLgpZb3UgY2FuIHNlZSBhbiBleGFtcGxlIGluIHRoaXMgSGFydmFyZCBDaGFuIEJpb2luZm9ybWF0aWNzIENvcmUgVHJhaW5pbmcgbWF0ZXJpYWw6IDxodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3Bfc2FsbW9uX29ubGluZS9sZXNzb25zL0Fubm90YXRpb25EYmlfbGVzc29uLmh0bWw+CgpPbmUgZ29vZCB0aGluZyBhYm91dCBFbnNlbWJsIGdlbmUgaWRlbnRpZmllcnMgaXMgdGhhdCB0aGV5IGFyZSBsZXNzIGxpa2VseSB0byBiZSBkdXBsaWNhdGVkIHRoYW4sIGZvciBleGFtcGxlLCBnZW5lIHN5bWJvbHMuCihNdWx0aXBsZSBFbnNlbWJsIGdlbmUgaWRlbnRpZmllcnMgY2FuIG1hcCB0byB0aGUgc2FtZSBzeW1ib2wuKQoKVGhlIEdTRUEgYXBwcm9hY2ggcmVxdWlyZXMgb24gZGlzY3JpbWluYXRpbmcgYmV0d2VlbiBnZW5lcyB0aGF0IGFyZSBpbiBhIGdlbmUgc2V0IGFuZCB0aG9zZSB0aGF0IGFyZSBub3QuClByYWN0aWNhbGx5IHNwZWFraW5nLCBnZW5lIHNldHMgYXJlIGp1c3QgY29sbGVjdGlvbnMgb2YgZ2VuZSBpZGVudGlmaWVycyEKV2hlbiB0aGUgZnVuY3Rpb24gd2UgdXNlIGZvciBHU0VBIHByZS1yYW5rZWQgZ2V0cyBhIGxpc3Qgd2l0aCBkdXBsaWNhdGVkIGdlbmUgaWRlbnRpZmllcnMsIGl0IGNhbiBwcm9kdWNlIHVuZXhwZWN0ZWQgcmVzdWx0cy4KU28sIGxldCdzIGNoZWNrIGZvciBkdXBsaWNhdGVzIGluIHRoZSBkYXRhIGZyYW1lIG9mIERFU2VxMiByZXN1bHRzLgoKYGBge3IgY2hlY2tfZHVwbGljYXRlcywgbGl2ZSA9IFRSVUV9CmFueShkdXBsaWNhdGVkKGRlc2VxX2RmJGVuc2VtYmxfaWQpKQpgYGAKClRoZXJlIGFyZSBubyBkdXBsaWNhdGVzIGZvciB1cyB0byB3b3JyeSBhYm91dCEKCiMjIyBQcmUtcmFua2VkIGxpc3QKClRoZSBgR1NFQSgpYCBmdW5jdGlvbiB0YWtlcyBhIHByZS1yYW5rZWQgKHNvcnRlZCkgbmFtZWQgdmVjdG9yIG9mIHN0YXRpc3RpY3MsIHdoZXJlIHRoZSBuYW1lcyBpbiB0aGUgdmVjdG9yIGFyZSBnZW5lIGlkZW50aWZpZXJzLgpUaGlzIGlzIHN0ZXAgMSAtLSBnZW5lLWxldmVsIHN0YXRpc3RpY3MuCgpgYGB7ciBsZmNfdmVjdG9yfQpsZmNfdmVjdG9yIDwtIGRlc2VxX2RmIHw+CiAgIyBFeHRyYWN0IGEgdmVjdG9yIG9mIGBsb2cyRm9sZENoYW5nZWAgbmFtZWQgYnkgYGVuc2VtYmxfaWRgCiAgZHBseXI6OnB1bGwobG9nMkZvbGRDaGFuZ2UsIG5hbWUgPSBlbnNlbWJsX2lkKQpsZmNfdmVjdG9yIDwtIHNvcnQobGZjX3ZlY3RvciwgZGVjcmVhc2luZyA9IFRSVUUpCmBgYAoKTGV0J3MgbG9vayBhdCB0aGUgdG9wIHJhbmtlZCB2YWx1ZXMuCgpgYGB7ciBoZWFkX2xmYywgbGl2ZSA9IFRSVUV9CiMgTG9vayBhdCBmaXJzdCBlbnRyaWVzIG9mIHRoZSBsb2cgZm9sZCBjaGFuZ2UgdmVjdG9yCmhlYWQobGZjX3ZlY3RvcikKYGBgCgpBbmQgdGhlIGJvdHRvbSBvZiB0aGUgbGlzdC4KCmBgYHtyIHRhaWxfbGZjLCBsaXZlID0gVFJVRX0KIyBMb29rIGF0IHRoZSBsYXN0IGVudHJpZXMgb2YgdGhlIGxvZyBmb2xkIGNoYW5nZSB2ZWN0b3IKdGFpbChsZmNfdmVjdG9yKQpgYGAKCiMjIFJ1biBHU0VBCgpOb3cgZm9yIHRoZSBhbmFseXNpcyEKCldlIGNhbiB1c2UgdGhlIGBHU0VBKClgIGZ1bmN0aW9uIHRvIHBlcmZvcm0gR1NFQSB3aXRoIGFueSBnZW5lcmljIHNldCBvZiBnZW5lIHNldHMsIGJ1dCB0aGVyZSBhcmUgc2V2ZXJhbCBmdW5jdGlvbnMgZm9yIHVzaW5nIHNwZWNpZmljLCBjb21tb25seSB1c2VkIGdlbmUgc2V0cyAoZS5nLiwgYGdzZUtFR0coKWApLgoKYGBge3IgcnVuX2dzZWF9CmdzZWFfcmVzdWx0cyA8LSBHU0VBKGdlbmVMaXN0ID0gbGZjX3ZlY3RvciwgICMgb3JkZXJlZCByYW5rZWQgZ2VuZSBsaXN0CiAgICAgICAgICAgICAgICAgICAgIG1pbkdTU2l6ZSA9IDI1LCAgIyBtaW5pbXVtIGdlbmUgc2V0IHNpemUKICAgICAgICAgICAgICAgICAgICAgbWF4R1NTaXplID0gNTAwLCAgIyBtYXhpbXVtIGdlbmUgc2V0IHNldAogICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1LAogICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwgICMgY29ycmVjdGlvbiBmb3IgbXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0aW5nCiAgICAgICAgICAgICAgICAgICAgIFRFUk0yR0VORSA9IGRwbHlyOjpzZWxlY3QoaHNfaGFsbG1hcmtzX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdzX25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5zZW1ibF9nZW5lKSkgICMgcGFzcyB0aGUgY29ycmVjdCBpZGVudGlmaWVyIGNvbHVtbgpgYGAKTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIEdTRUEgcmVzdWx0cy4KCmBgYHtyIHZpZXdfZ3NlYSwgbGl2ZSA9IFRSVUUsIGV2YWwgPSBGQUxTRX0KVmlldyhnc2VhX3Jlc3VsdHNAcmVzdWx0IHw+CiAgICAgICBkcGx5cjo6YXJyYW5nZShkcGx5cjo6ZGVzYyhORVMpKQopCmBgYAoKTm9ybWFsaXplZCBlbnJpY2htZW50IHNjb3JlcyAoTkVTKSBhcmUgZW5yaWNobWVudCBzY29yZXMgdGhhdCBhcmUgc2NhbGVkIHRvIG1ha2UgZ2VuZSBzZXRzIHRoYXQgY29udGFpbiBkaWZmZXJlbnQgbnVtYmVyIG9mIGdlbmVzIGNvbXBhcmFibGUuCgpQYXRod2F5cyB3aXRoIHNpZ25pZmljYW50LCBoaWdobHkgcG9zaXRpdmUgTkVTIGFyZSBlbnJpY2hlZCBpbiBFUk1TIG15b2JsYXN0cywgd2hlcmVhcyBwYXRod2F5cyB3aXRoIHNpZ25pZmljYW50LCBoaWdobHkgbmVnYXRpdmUgTkVTIGFyZSBlbnJpY2hlZCBpbiBBUk1TIG15b2JsYXN0cy4KCkxldCdzIHdyaXRlIHRoZXNlIHJlc3VsdHMgdG8gZmlsZS4KCmBgYHtyIHdyaXRlX2dzZWF9CmdzZWFfcmVzdWx0c0ByZXN1bHQgfD4gcmVhZHI6OndyaXRlX3RzdihvdXRwdXRfZmlsZSkKYGBgCgojIyMgVmlzdWFsaXppbmcgR1NFQSByZXN1bHRzCgpXZSBjYW4gdmlzdWFsaXplIEdTRUEgcmVzdWx0cyBmb3IgaW5kaXZpZHVhbCBwYXRod2F5cyBvciBnZW5lIHNldHMgdXNpbmcgYGVucmljaHBsb3Q6OmdzZWFwbG90KClgLgpMZXQncyB0YWtlIGEgbG9vayBhdCAzIGRpZmZlcmVudCBwYXRod2F5cyAtLSBvbmUgd2l0aCBhIGhpZ2hseSBwb3NpdGl2ZSBORVMsIG9uZSB3aXRoIGEgaGlnaGx5IG5lZ2F0aXZlIE5FUywgYW5kIG9uZSB0aGF0IHdhcyBub3QgYSBzaWduaWZpY2FudCByZXN1bHQgLS0gdG8gZ2V0IG1vcmUgaW5zaWdodCBpbnRvIGhvdyBFUyBhcmUgY2FsY3VsYXRlZC4KCiMjIyMgSGlnaGx5IFBvc2l0aXZlIE5FUwoKTGV0J3MgdGFrZSBsb29rIGF0IGEgcGF0aHdheSB3aXRoIGEgaGlnaGx5IHBvc2l0aXZlIE5FUyAoYEhBTExNQVJLX01ZQ19UQVJHRVRTX1YyYCkgdXNpbmcgYSBHU0VBIHBsb3QuCgpgYGB7ciBoaWdobHlfcG9zfQplbnJpY2hwbG90Ojpnc2VhcGxvdChnc2VhX3Jlc3VsdHMsCiAgICAgICAgICAgICAgICAgICAgIGdlbmVTZXRJRCA9ICJIQUxMTUFSS19NWUNfVEFSR0VUU19WMiIsCiAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIkhBTExNQVJLX01ZQ19UQVJHRVRTX1YyIiwKICAgICAgICAgICAgICAgICAgICAgY29sb3IubGluZSA9ICIjMDA2NkZGIikKYGBgCgpOb3RpY2UgaG93IHRoZSBnZW5lcyB0aGF0IGFyZSBpbiB0aGUgZ2VuZSBzZXQsIGluZGljYXRlZCBieSB0aGUgYmxhY2sgYmFycywgdGVuZCB0byBiZSBvbiB0aGUgbGVmdCBzaWRlIG9mIHRoZSBncmFwaCBpbmRpY2F0aW5nIHRoYXQgdGhleSBoYXZlIHBvc2l0aXZlIGdlbmUtbGV2ZWwgc2NvcmVzLgoKIyMjIyBIaWdobHkgTmVnYXRpdmUgTkVTCgpUaGUgZ2VuZSBzZXQgYEhBTExNQVJLX01ZT0dFTkVTSVNgIGhhZCBhIGhpZ2hseSBuZWdhdGl2ZSBORVMuCgpgYGB7ciBoaWdobHlfbmVnfQplbnJpY2hwbG90Ojpnc2VhcGxvdChnc2VhX3Jlc3VsdHMsCiAgICAgICAgICAgICAgICAgICAgIGdlbmVTZXRJRCA9ICJIQUxMTUFSS19NWU9HRU5FU0lTIiwKICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiSEFMTE1BUktfTVlPR0VORVNJUyIsCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yLmxpbmUgPSAiIzAwNjZGRiIpCmBgYAoKVGhpcyBnZW5lIHNldCBzaG93cyB0aGUgb3Bwb3NpdGUgcGF0dGVybiAtLSBnZW5lcyBpbiB0aGUgcGF0aHdheSB0ZW5kIHRvIGJlIG9uIHRoZSByaWdodCBzaWRlIG9mIHRoZSBncmFwaC4KCiMjIyMgQSBub24tc2lnbmlmaWNhbnQgcmVzdWx0CgpUaGUgYEByZXN1bHRzYCBzbG90IHdpbGwgb25seSBzaG93IGdlbmUgc2V0cyB0aGF0IHBhc3MgdGhlIGBwdmFsdWVDdXRvZmZgIHRocmVzaG9sZCB3ZSBzdXBwbGllZCB0byBgR1NFQSgpYCwgYnV0IHdlIGNhbiBwbG90IGFueSBnZW5lIHNldCBzbyBsb25nIGFzIHdlIGtub3cgaXRzIG5hbWUuCkxldCdzIGxvb2sgYXQgYEhBTExNQVJLX1A1M19QQVRIV0FZYCwgd2hpY2ggd2FzIG5vdCBpbiB0aGUgcmVzdWx0cyB3ZSB2aWV3ZWQgZWFybGllci4KCmBgYHtyIHA1MywgbGl2ZSA9IFRSVUV9CmVucmljaHBsb3Q6OmdzZWFwbG90KGdzZWFfcmVzdWx0cywKICAgICAgICAgICAgICAgICAgICAgZ2VuZVNldElEID0gIkhBTExNQVJLX1A1M19QQVRIV0FZIiwKICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiSEFMTE1BUktfUDUzX1BBVEhXQVkiLAogICAgICAgICAgICAgICAgICAgICBjb2xvci5saW5lID0gIiMwMDY2RkYiKQpgYGAKCkdlbmVzIGluIHRoZSBwYXRod2F5IGFyZSBkaXN0cmlidXRlZCBtb3JlIGV2ZW5seSB0aHJvdWdob3V0IHRoZSByYW5rZWQgbGlzdCwgcmVzdWx0aW5nIGluIGEgbW9yZSAibWlkZGxpbmciIHNjb3JlLgoKKk5vdGU6IFRoZSBwbG90cyByZXR1cm5lZCBieSBgZW5yaWNocGxvdDo6Z3NlYXBsb3RgIGFyZSBnZ3Bsb3RzLCBzbyB3ZSBjb3VsZCB1c2UgYGdncGxvdDI6Omdnc2F2ZSgpYCB0byBzYXZlIHRoZW0gdG8gZmlsZSBpZiB3ZSB3YW50ZWQgdG8uKgoKIyMgU2Vzc2lvbiBJbmZvCgpgYGB7ciBzZXNzaW9uX2luZm99CnNlc3Npb25JbmZvKCkKYGBgCg==