library(dplyr)
library(readr)
library(tibble)
library(ggplot2)
library(writexl)
library(msigdbr)
library(fgsea)
library(BiocParallel)
library(DESeq2)
library(GSVA)
library(ComplexHeatmap)
library(circlize)
library(EnhancedVolcano)
register(SerialParam())
Aim
In this practical we start from the DESeq2 result table and ask which
biological processes and pathways are affected by MEK inhibitor
treatment in Caco-2 cells.
- manual differential gene lists for the Enrichr web interface
- one explicit Fisher exact test
- over-representation analysis (ORA) with
msigdbr gene
sets
- a simple pre-ranked GSEA-style analysis without a hard differential
gene cutoff
- sample-level Hallmark scoring with GSVA
- extension from Hallmark gene sets to Gene Ontology Biological
Process
1. Load DESeq2 Results
res <- read_tsv("DESeq_result_full.tsv", show_col_types = FALSE)
res
The important columns are:
gene_name: gene name
log2FoldChange: positive values are higher in MEKi,
negative values are lower
stat: Wald test statistic, useful for ranking
genes
padj: FDR-adjusted p-value
Before enrichment, remove missing or duplicated gene names. For
duplicated genes, keep the row with the largest absolute test
statistic.
res_clean <- res |>
filter(!is.na(gene_name), gene_name != "", !is.na(stat)) |>
group_by(gene_name) |>
slice_max(order_by = abs(stat), n = 1, with_ties = FALSE) |>
ungroup()
nrow(res)
[1] 17666
nrow(res_clean)
[1] 17666
Define the tested gene universe. This is the background set for ORA:
all genes that had enough expression to enter the DESeq2 analysis and
have a usable gene name.
gene_universe <- res_clean |>
pull(gene_name) |>
unique()
length(gene_universe)
[1] 17666
2. Manual Differential Gene Lists for Enrichr
Web tools such as Enrichr are useful for a first broad look at the
biology. For this exploratory step, select a clear set of differential
genes.
de_genes <- res_clean |>
filter(!is.na(padj), padj < 0.1, abs(log2FoldChange) >= 1)
de_up <- de_genes |>
filter(log2FoldChange > 0) |>
arrange(padj)
de_down <- de_genes |>
filter(log2FoldChange < 0) |>
arrange(padj)
nrow(de_genes)
[1] 625
nrow(de_up)
[1] 432
nrow(de_down)
[1] 193
Write one-gene-per-line lists for upload to Enrichr:
write_lines(de_genes$gene_name, "enrichr_DE_genes_padj0.1_abslog2FC1.txt")
write_lines(de_up$gene_name, "enrichr_up_in_MEKi_padj0.1_abslog2FC1.txt")
write_lines(de_down$gene_name, "enrichr_down_in_MEKi_padj0.1_abslog2FC1.txt")
Task: Upload the three files to Enrichr and compare
the broad biological themes. Do the up- and down-regulated lists tell
the same story?
Check Different Cutoffs
The choice of cutoff changes which genes enter the list. Before
interpreting results, check whether the strongest Enrichr terms are
stable across a few reasonable cutoffs.
# Practice:
# Create six gene tables from res_clean:
# padj < 0.05, up and down
# padj < 0.05, up only
# padj < 0.05, down only
# padj < 0.001, up and down
# padj < 0.001, up only
# padj < 0.001, down only
#
# Use filter(), !is.na(padj), padj, and log2FoldChange.
# Then count the rows in each table with nrow().
# Practice:
# Use write_lines() to save one gene list for each cutoff table.
# Each file should contain one gene name per line.
Task: Upload these six lists to Enrichr. Which terms
are present no matter which cutoff you use? Which terms only appear with
the larger or smaller gene lists? What changes when up- and
down-regulated genes are tested separately?
3. Load MSigDB Gene Sets with msigdbr
msigdbr provides MSigDB gene sets as simple R
tables.
Start with Hallmark gene sets. These are compact, curated, and easier
to interpret than the full Gene Ontology.
hallmark <- msigdbr(species = "Homo sapiens", collection = "H") |>
select(gs_name, gene = 1) |>
distinct()
hallmark
4. One Explicit Fisher Exact Test
ORA asks whether a pathway contains more differential genes than
expected by chance. We can test this directly for one gene set.
Here we test HALLMARK_KRAS_SIGNALING_UP, a useful
example for MEK inhibition because it is connected to the
RAS/RAF/MEK/ERK axis.
example_set_name <- "HALLMARK_KRAS_SIGNALING_UP"
example_set <- hallmark |>
filter(gs_name == example_set_name) |>
pull(gene) |>
intersect(gene_universe)
selected_genes <- de_genes |>
pull(gene_name) |>
unique()
# Practice:
# Count the four groups needed for the Fisher exact test.
#
# Use ?intersect, ?union, and ?setdiff to look up what these functions do.
#
# de_and_in_set: genes that are selected and are in the example gene set
# de_not_in_set: genes that are selected but are not in the example gene set
# not_de_in_set: genes that are not selected but are in the example gene set
# not_de_not_in_set: genes that are not selected and are not in the example gene set
fisher_table <- matrix(
c(de_and_in_set, de_not_in_set, not_de_in_set, not_de_not_in_set),
nrow = 2,
byrow = TRUE,
dimnames = list(
differential = c("yes", "no"),
in_gene_set = c("yes", "no")
)
)
fisher_table
in_gene_set
differential yes no
yes 11 614
no 127 16914
fisher.test(fisher_table, alternative = "greater")
Fisher's Exact Test for Count Data
data: fisher_table
p-value = 0.009961
alternative hypothesis: true odds ratio is greater than 1
95 percent confidence interval:
1.302169 Inf
sample estimates:
odds ratio
2.385792
Task: Interpret the four numbers in the contingency
table. Which cell contains differential genes that are members of the
pathway?
5. ORA with msigdbr
Now run the same idea across all Hallmark gene sets.
hallmark_pathways <- split(hallmark$gene, hallmark$gs_name)
hallmark_ora_tab <- fora(
pathways = hallmark_pathways,
genes = selected_genes,
universe = gene_universe,
minSize = 10,
maxSize = 500
) |>
as_tibble() |>
mutate(overlapGenes = vapply(overlapGenes, paste, character(1), collapse = "/")) |>
arrange(padj)
hallmark_ora_tab
hallmark_ora_tab |>
slice_min(padj, n = 15) |>
ggplot(aes(x = -log10(padj), y = reorder(pathway, -log10(padj)))) +
geom_col() +
labs(
x = "-log10(adjusted p-value)",
y = NULL,
title = "Hallmark ORA from thresholded DE genes"
) +
theme_minimal()

Check whether the ORA result changes when using the same cutoffs as
in the Enrichr exercise.
# Practice:
# Repeat the Hallmark ORA with the same six gene lists from the Enrichr task:
# padj < 0.05, up and down
# padj < 0.05, up only
# padj < 0.05, down only
# padj < 0.001, up and down
# padj < 0.001, up only
# padj < 0.001, down only
#
# For each list:
# pull(gene_name)
# run fora()
# arrange by padj
# compare the top Hallmark terms
Save the ORA table:
write_tsv(hallmark_ora_tab, "ORA_Hallmark_msigdb.tsv")
write_xlsx(hallmark_ora_tab, "ORA_Hallmark_msigdb.xlsx")
Discussion: ORA depends on the cutoff used to select
genes. Which Hallmark terms are stable across the six lists? Which terms
depend on testing up- and down-regulated genes separately?
6. GSEA-Style Analysis Without an Explicit Cutoff
GSEA-style analysis avoids selecting a differential-gene cutoff.
Instead, it uses all genes ranked from most up-regulated in MEKi to most
down-regulated in MEKi.
Here we rank genes by the DESeq2 Wald statistic.
ranked_gene_list <- res_clean |>
arrange(desc(stat)) |>
select(gene_name, stat)
ranks <- ranked_gene_list$stat
names(ranks) <- ranked_gene_list$gene_name
head(ranks)
LAMA1 CYMP PHGR1 RGPD6 KNG1 SELENOP
17.22528 16.29956 15.93474 15.35809 15.26151 14.99842
tail(ranks)
ID1 CLDN2 DUSP6 CEACAM6 EGR1 ANKRD1
-17.34017 -18.34090 -18.56878 -19.19697 -25.28795 -27.30598
set.seed(1)
hallmark_gsea_tab <- fgsea(
pathways = hallmark_pathways,
stats = ranks,
minSize = 10,
maxSize = 500,
eps = 0
) |>
as_tibble() |>
mutate(leadingEdge = vapply(leadingEdge, paste, character(1), collapse = "/")) |>
arrange(padj)
Warning in prepareStats(stats, scoreType, gseaParam): There are ties in the preranked stats (0.03% of the list).
The order of those tied genes will be arbitrary, which may produce unexpected results.
hallmark_gsea_tab
hallmark_gsea_tab |>
slice_min(padj, n = 15) |>
ggplot(aes(x = NES, y = reorder(pathway, NES), fill = NES > 0)) +
geom_col() +
scale_fill_manual(values = c("TRUE" = "firebrick", "FALSE" = "steelblue"), guide = "none") +
labs(
x = "Normalized enrichment score",
y = NULL,
title = "Hallmark GSEA from all ranked genes"
) +
theme_minimal()

Positive NES means enrichment near the top of the ranked list,
i.e. genes tend to be higher in MEKi. Negative NES means enrichment near
the bottom, i.e. genes tend to be lower in MEKi.
write_tsv(hallmark_gsea_tab, "GSEA_Hallmark_msigdb.tsv")
write_xlsx(hallmark_gsea_tab, "GSEA_Hallmark_msigdb.xlsx")
Make a volcano-style plot of the Hallmark GSEA result.
# Practice:
# Use EnhancedVolcano() with hallmark_gsea_tab.
#
# Put NES on the x-axis.
# Put padj on the y-axis.
# Use pathway names as labels.
# A useful cutoff for NES is 1.
Task: Compare ORA and GSEA results. Which biological
themes appear in both? Which only appear when all genes are ranked?
7. Sample-Level Hallmark Scores with GSVA
ORA and GSEA use the differential expression result table. GSVA
answers a different question: how active does each Hallmark gene set
look in each sample?
For this we use variance-stabilized expression values, as in the PCA
workflow.
dds <- readRDS("dds.rds")
sample_info <- colData(dds) |>
as.data.frame() |>
select(sample, condition)
sample_info
Run the same variance-stabilizing transformation used for PCA, then
prepare an expression matrix with gene names.
vst_counts <- vst(dds, blind = FALSE)
expression_matrix <- assay(vst_counts) |>
as.data.frame() |>
rownames_to_column("gene_id") |>
inner_join(res_clean |> select(gene_id, gene_name), by = "gene_id") |>
select(gene_name, all_of(sample_info$sample)) |>
group_by(gene_name) |>
summarize(across(all_of(sample_info$sample), mean), .groups = "drop") |>
column_to_rownames("gene_name") |>
as.matrix()
dim(expression_matrix)
[1] 16634 6
Now calculate one GSVA score per Hallmark gene set and sample.
gsva_parameters <- gsvaParam(
expression_matrix,
hallmark_pathways,
minSize = 10,
maxSize = 500,
kcdf = "Gaussian"
)
hallmark_gsva <- gsva(gsva_parameters)
ℹ GSVA version 2.6.2
ℹ Searching for rows with constant values
ℹ Calculating GSVA ranks
ℹ GSVA dense (classical) algorithm
ℹ Row-wise ECDF estimation with Gaussian kernels
ℹ Calculating row ECDFs
ℹ Calculating column ranks
ℹ GSVA dense (classical) algorithm
ℹ Calculating GSVA scores for 50 gene sets
✔ Calculations finished
dim(hallmark_gsva)
[1] 50 6
hallmark_gsva[1:5, ]
CaCo2_1 CaCo2_2 CaCo2_3 CaCo2_4
HALLMARK_ADIPOGENESIS -0.09536108 0.04799363 -0.004000484 0.02150758
HALLMARK_ALLOGRAFT_REJECTION 0.05645103 0.21113752 -0.064706956 -0.19217295
HALLMARK_ANDROGEN_RESPONSE -0.24280486 0.03984928 0.008465147 0.19013019
HALLMARK_ANGIOGENESIS -0.16997774 -0.07992886 -0.246971939 -0.04243008
HALLMARK_APICAL_JUNCTION -0.11032190 -0.06794653 -0.172646523 -0.13008694
CaCo2_5 CaCo2_6
HALLMARK_ADIPOGENESIS -0.14806896 0.11667805
HALLMARK_ALLOGRAFT_REJECTION 0.02767541 -0.09323591
HALLMARK_ANDROGEN_RESPONSE -0.03611282 0.02739779
HALLMARK_ANGIOGENESIS 0.25628314 0.24506663
HALLMARK_APICAL_JUNCTION 0.30505634 0.16541892
For the heatmap, we scale each Hallmark row. Red means relatively
high activity for that Hallmark in that sample; blue means relatively
low activity.
hallmark_gsva_scaled <- t(scale(t(hallmark_gsva)))
condition_colors <- c(control = "grey70", MEKi = "firebrick")
sample_annotation <- HeatmapAnnotation(
condition = sample_info$condition,
col = list(condition = condition_colors)
)
Heatmap(
hallmark_gsva_scaled,
name = "GSVA\nz-score",
col = colorRamp2(c(-2, 0, 2), c("steelblue", "white", "firebrick")),
top_annotation = sample_annotation,
column_split = sample_info$condition,
cluster_columns = FALSE,
row_names_gp = grid::gpar(fontsize = 7),
column_names_gp = grid::gpar(fontsize = 9)
)

Save the GSVA scores:
hallmark_gsva_tab <- hallmark_gsva |>
as.data.frame() |>
rownames_to_column("hallmark")
write_tsv(hallmark_gsva_tab, "GSVA_Hallmark_sample_scores.tsv")
write_xlsx(hallmark_gsva_tab, "GSVA_Hallmark_sample_scores.xlsx")
Task: Compare the GSVA heatmap to the Hallmark GSEA
result. Do the pathways that change globally also separate the
individual control and MEKi samples?
8. Extend to Gene Ontology Biological Process
GO Biological Process is much larger and more redundant than
Hallmark. It is useful for depth, but interpretation should focus on
themes rather than single near-duplicate term names.
go_bp <- msigdbr(
species = "Homo sapiens",
collection = "C5",
subcollection = "GO:BP"
) |>
select(gs_name, gene = 1) |>
distinct()
go_bp
Run ORA with GO BP:
# Practice:
# Create go_bp_pathways with split(), as for the Hallmark example.
#
# Then create go_bp_ora_tab with fora().
# Use the same genes, universe, minSize, and maxSize as before.
# Convert the result to a tibble, simplify overlapGenes, and arrange by padj.
go_bp_ora_tab |>
slice_min(padj, n = 20) |>
ggplot(aes(x = -log10(padj), y = reorder(pathway, -log10(padj)))) +
geom_col() +
labs(
x = "-log10(adjusted p-value)",
y = NULL,
title = "GO BP ORA from thresholded DE genes"
) +
theme_minimal()

Run GSEA-style analysis with GO BP:
# Practice:
# Create go_bp_gsea_tab with fgsea().
# Use go_bp_pathways and the same ranked list used for Hallmark GSEA.
# Convert the result to a tibble, simplify leadingEdge, and arrange by padj.
go_bp_gsea_plot_tab <- bind_rows(
go_bp_gsea_tab |>
slice_max(NES, n = 15),
go_bp_gsea_tab |>
slice_min(NES, n = 15)
) |>
distinct(pathway, .keep_all = TRUE)
go_bp_gsea_plot_tab |>
ggplot(aes(x = NES, y = reorder(pathway, NES), fill = NES > 0)) +
geom_col() +
scale_fill_manual(values = c("TRUE" = "firebrick", "FALSE" = "steelblue"), guide = "none") +
labs(
x = "Normalized enrichment score",
y = NULL,
title = "GO BP GSEA from all ranked genes"
) +
theme_minimal()

Make a volcano-style plot of the GO BP GSEA result, then compare it
with the Hallmark GSEA volcano plot.
# Practice:
# Use EnhancedVolcano() with go_bp_gsea_tab.
#
# Put NES on the x-axis.
# Put padj on the y-axis.
# Use pathway names as labels.
# Use the same cutoffs as for the Hallmark plot.
#
# Compare the two volcano plots:
# Which broad Hallmark patterns are supported by many GO BP terms?
# Which GO BP terms add more detailed biology?
# Which GO BP terms look redundant with each other?
9. Final GSVA Task With GO BP Terms
As a final task, select the top 20 GO BP categories with positive
GSEA scores and the top 20 GO BP categories with negative GSEA scores.
Then calculate sample-level GSVA scores for these GO BP categories and
make a heatmap.
# Practice:
# Start from go_bp_gsea_tab.
# Select the 20 categories with the largest NES.
# Select the 20 categories with the smallest NES.
# Combine these categories into one table.
#
# Use the selected pathway names to subset go_bp_pathways.
# Run GSVA with expression_matrix and the selected GO BP pathways.
# Make a Heatmap with samples split by condition.
One possible result is shown below.

Save the GO BP tables:
write_tsv(go_bp_ora_tab, "ORA_GO_BP_msigdb.tsv")
write_xlsx(go_bp_ora_tab, "ORA_GO_BP_msigdb.xlsx")
write_tsv(go_bp_gsea_tab, "GSEA_GO_BP_msigdb.tsv")
write_xlsx(go_bp_gsea_tab, "GSEA_GO_BP_msigdb.xlsx")
go_bp_gsva_tab <- go_bp_gsva |>
as.data.frame() |>
rownames_to_column("go_bp")
write_tsv(go_bp_gsva_tab, "GSVA_GO_BP_top_GSEA_sample_scores.tsv")
write_xlsx(go_bp_gsva_tab, "GSVA_GO_BP_top_GSEA_sample_scores.xlsx")
10. Final Interpretation
Use the result tables and plots to write a short interpretation.
Answer these questions:
- Which broad Hallmark pathways change after MEK inhibitor
treatment?
- Which GO Biological Process themes support that interpretation?
- Are the strongest changes mostly higher or lower in MEKi?
- Do the GSVA sample-level scores separate control and MEKi
samples?
- Do the selected GO BP GSVA scores show the same direction as GO BP
GSEA?
- Which conclusions are robust across Enrichr, ORA, GSEA, and
GSVA?
- Which conclusions depend strongly on the differential-gene
cutoff?
LS0tCnRpdGxlOiAiV003IEZ1bmN0aW9uYWwgQW5ub3RhdGlvbiBvZiBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBSZXN1bHRzIgpzdWJ0aXRsZTogIk9SQSwgR1NFQSwgYW5kIHNhbXBsZS1sZXZlbCBwYXRod2F5IHNjb3JpbmciCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKLS0tCgpgYGB7ciBzZXR1cCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShkcGx5cikKbGlicmFyeShyZWFkcikKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh3cml0ZXhsKQpsaWJyYXJ5KG1zaWdkYnIpCmxpYnJhcnkoZmdzZWEpCmxpYnJhcnkoQmlvY1BhcmFsbGVsKQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShHU1ZBKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCnJlZ2lzdGVyKFNlcmlhbFBhcmFtKCkpCmBgYAoKIyMgQWltCgpJbiB0aGlzIHByYWN0aWNhbCB3ZSBzdGFydCBmcm9tIHRoZSBERVNlcTIgcmVzdWx0IHRhYmxlIGFuZCBhc2sgd2hpY2gKYmlvbG9naWNhbCBwcm9jZXNzZXMgYW5kIHBhdGh3YXlzIGFyZSBhZmZlY3RlZCBieSBNRUsgaW5oaWJpdG9yIHRyZWF0bWVudCBpbgpDYWNvLTIgY2VsbHMuCgotIG1hbnVhbCBkaWZmZXJlbnRpYWwgZ2VuZSBsaXN0cyBmb3IgdGhlIEVucmljaHIgd2ViIGludGVyZmFjZQotIG9uZSBleHBsaWNpdCBGaXNoZXIgZXhhY3QgdGVzdAotIG92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMgKE9SQSkgd2l0aCBgbXNpZ2RicmAgZ2VuZSBzZXRzCi0gYSBzaW1wbGUgcHJlLXJhbmtlZCBHU0VBLXN0eWxlIGFuYWx5c2lzIHdpdGhvdXQgYSBoYXJkIGRpZmZlcmVudGlhbCBnZW5lIGN1dG9mZgotIHNhbXBsZS1sZXZlbCBIYWxsbWFyayBzY29yaW5nIHdpdGggR1NWQQotIGV4dGVuc2lvbiBmcm9tIEhhbGxtYXJrIGdlbmUgc2V0cyB0byBHZW5lIE9udG9sb2d5IEJpb2xvZ2ljYWwgUHJvY2VzcwoKIyMgMS4gTG9hZCBERVNlcTIgUmVzdWx0cwoKYGBge3IgbG9hZC1yZXN1bHRzfQpyZXMgPC0gcmVhZF90c3YoIkRFU2VxX3Jlc3VsdF9mdWxsLnRzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCgpyZXMKYGBgCgpUaGUgaW1wb3J0YW50IGNvbHVtbnMgYXJlOgoKLSBgZ2VuZV9uYW1lYDogZ2VuZSBuYW1lCi0gYGxvZzJGb2xkQ2hhbmdlYDogcG9zaXRpdmUgdmFsdWVzIGFyZSBoaWdoZXIgaW4gTUVLaSwgbmVnYXRpdmUgdmFsdWVzIGFyZSBsb3dlcgotIGBzdGF0YDogV2FsZCB0ZXN0IHN0YXRpc3RpYywgdXNlZnVsIGZvciByYW5raW5nIGdlbmVzCi0gYHBhZGpgOiBGRFItYWRqdXN0ZWQgcC12YWx1ZQoKQmVmb3JlIGVucmljaG1lbnQsIHJlbW92ZSBtaXNzaW5nIG9yIGR1cGxpY2F0ZWQgZ2VuZSBuYW1lcy4gRm9yIGR1cGxpY2F0ZWQKZ2VuZXMsIGtlZXAgdGhlIHJvdyB3aXRoIHRoZSBsYXJnZXN0IGFic29sdXRlIHRlc3Qgc3RhdGlzdGljLgoKYGBge3IgY2xlYW4tcmVzdWx0c30KcmVzX2NsZWFuIDwtIHJlcyB8PgogIGZpbHRlcighaXMubmEoZ2VuZV9uYW1lKSwgZ2VuZV9uYW1lICE9ICIiLCAhaXMubmEoc3RhdCkpIHw+CiAgZ3JvdXBfYnkoZ2VuZV9uYW1lKSB8PgogIHNsaWNlX21heChvcmRlcl9ieSA9IGFicyhzdGF0KSwgbiA9IDEsIHdpdGhfdGllcyA9IEZBTFNFKSB8PgogIHVuZ3JvdXAoKQoKbnJvdyhyZXMpCm5yb3cocmVzX2NsZWFuKQpgYGAKCkRlZmluZSB0aGUgdGVzdGVkIGdlbmUgdW5pdmVyc2UuIFRoaXMgaXMgdGhlIGJhY2tncm91bmQgc2V0IGZvciBPUkE6IGFsbCBnZW5lcwp0aGF0IGhhZCBlbm91Z2ggZXhwcmVzc2lvbiB0byBlbnRlciB0aGUgREVTZXEyIGFuYWx5c2lzIGFuZCBoYXZlIGEgdXNhYmxlIGdlbmUKbmFtZS4KCmBgYHtyIHVuaXZlcnNlfQpnZW5lX3VuaXZlcnNlIDwtIHJlc19jbGVhbiB8PgogIHB1bGwoZ2VuZV9uYW1lKSB8PgogIHVuaXF1ZSgpCgpsZW5ndGgoZ2VuZV91bml2ZXJzZSkKYGBgCgojIyAyLiBNYW51YWwgRGlmZmVyZW50aWFsIEdlbmUgTGlzdHMgZm9yIEVucmljaHIKCldlYiB0b29scyBzdWNoIGFzIEVucmljaHIgYXJlIHVzZWZ1bCBmb3IgYSBmaXJzdCBicm9hZCBsb29rIGF0IHRoZSBiaW9sb2d5LgpGb3IgdGhpcyBleHBsb3JhdG9yeSBzdGVwLCBzZWxlY3QgYSBjbGVhciBzZXQgb2YgZGlmZmVyZW50aWFsIGdlbmVzLgoKYGBge3IgZGlmZmVyZW50aWFsLWdlbmUtc2VsZWN0aW9ufQpkZV9nZW5lcyA8LSByZXNfY2xlYW4gfD4KICBmaWx0ZXIoIWlzLm5hKHBhZGopLCBwYWRqIDwgMC4xLCBhYnMobG9nMkZvbGRDaGFuZ2UpID49IDEpCgpkZV91cCA8LSBkZV9nZW5lcyB8PgogIGZpbHRlcihsb2cyRm9sZENoYW5nZSA+IDApIHw+CiAgYXJyYW5nZShwYWRqKQoKZGVfZG93biA8LSBkZV9nZW5lcyB8PgogIGZpbHRlcihsb2cyRm9sZENoYW5nZSA8IDApIHw+CiAgYXJyYW5nZShwYWRqKQoKbnJvdyhkZV9nZW5lcykKbnJvdyhkZV91cCkKbnJvdyhkZV9kb3duKQpgYGAKCldyaXRlIG9uZS1nZW5lLXBlci1saW5lIGxpc3RzIGZvciB1cGxvYWQgdG8gRW5yaWNocjoKCmBgYHtyIHdyaXRlLWVucmljaHItbGlzdHN9CndyaXRlX2xpbmVzKGRlX2dlbmVzJGdlbmVfbmFtZSwgImVucmljaHJfREVfZ2VuZXNfcGFkajAuMV9hYnNsb2cyRkMxLnR4dCIpCndyaXRlX2xpbmVzKGRlX3VwJGdlbmVfbmFtZSwgImVucmljaHJfdXBfaW5fTUVLaV9wYWRqMC4xX2Fic2xvZzJGQzEudHh0IikKd3JpdGVfbGluZXMoZGVfZG93biRnZW5lX25hbWUsICJlbnJpY2hyX2Rvd25faW5fTUVLaV9wYWRqMC4xX2Fic2xvZzJGQzEudHh0IikKYGBgCgoqKlRhc2s6KiogVXBsb2FkIHRoZSB0aHJlZSBmaWxlcyB0byBFbnJpY2hyIGFuZCBjb21wYXJlIHRoZSBicm9hZCBiaW9sb2dpY2FsCnRoZW1lcy4gRG8gdGhlIHVwLSBhbmQgZG93bi1yZWd1bGF0ZWQgbGlzdHMgdGVsbCB0aGUgc2FtZSBzdG9yeT8KCiMjIyBDaGVjayBEaWZmZXJlbnQgQ3V0b2ZmcwoKVGhlIGNob2ljZSBvZiBjdXRvZmYgY2hhbmdlcyB3aGljaCBnZW5lcyBlbnRlciB0aGUgbGlzdC4gQmVmb3JlIGludGVycHJldGluZwpyZXN1bHRzLCBjaGVjayB3aGV0aGVyIHRoZSBzdHJvbmdlc3QgRW5yaWNociB0ZXJtcyBhcmUgc3RhYmxlIGFjcm9zcyBhIGZldwpyZWFzb25hYmxlIGN1dG9mZnMuCgpgYGB7ciBlbnJpY2hyLWN1dG9mZi1saXN0cy1oaWRkZW4sIGluY2x1ZGU9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmRlX3BhZGpfMC4wNV91cF9kb3duIDwtIHJlc19jbGVhbiB8PgogIGZpbHRlcighaXMubmEocGFkaiksIHBhZGogPCAwLjA1KQoKZGVfcGFkal8wLjA1X3VwIDwtIGRlX3BhZGpfMC4wNV91cF9kb3duIHw+CiAgZmlsdGVyKGxvZzJGb2xkQ2hhbmdlID4gMCkKCmRlX3BhZGpfMC4wNV9kb3duIDwtIGRlX3BhZGpfMC4wNV91cF9kb3duIHw+CiAgZmlsdGVyKGxvZzJGb2xkQ2hhbmdlIDwgMCkKCmRlX3BhZGpfMC4wMDFfdXBfZG93biA8LSByZXNfY2xlYW4gfD4KICBmaWx0ZXIoIWlzLm5hKHBhZGopLCBwYWRqIDwgMC4wMDEpCgpkZV9wYWRqXzAuMDAxX3VwIDwtIGRlX3BhZGpfMC4wMDFfdXBfZG93biB8PgogIGZpbHRlcihsb2cyRm9sZENoYW5nZSA+IDApCgpkZV9wYWRqXzAuMDAxX2Rvd24gPC0gZGVfcGFkal8wLjAwMV91cF9kb3duIHw+CiAgZmlsdGVyKGxvZzJGb2xkQ2hhbmdlIDwgMCkKCmN1dG9mZl9zdW1tYXJ5IDwtIHRpYmJsZSgKICBjdXRvZmYgPSBjKCJwYWRqIDwgMC4wNSwgdXAgYW5kIGRvd24iLAogICAgICAgICAgICAgInBhZGogPCAwLjA1LCB1cCIsCiAgICAgICAgICAgICAicGFkaiA8IDAuMDUsIGRvd24iLAogICAgICAgICAgICAgInBhZGogPCAwLjAwMSwgdXAgYW5kIGRvd24iLAogICAgICAgICAgICAgInBhZGogPCAwLjAwMSwgdXAiLAogICAgICAgICAgICAgInBhZGogPCAwLjAwMSwgZG93biIpLAogIG51bWJlcl9vZl9nZW5lcyA9IGMobnJvdyhkZV9wYWRqXzAuMDVfdXBfZG93biksCiAgICAgICAgICAgICAgICAgICAgICBucm93KGRlX3BhZGpfMC4wNV91cCksCiAgICAgICAgICAgICAgICAgICAgICBucm93KGRlX3BhZGpfMC4wNV9kb3duKSwKICAgICAgICAgICAgICAgICAgICAgIG5yb3coZGVfcGFkal8wLjAwMV91cF9kb3duKSwKICAgICAgICAgICAgICAgICAgICAgIG5yb3coZGVfcGFkal8wLjAwMV91cCksCiAgICAgICAgICAgICAgICAgICAgICBucm93KGRlX3BhZGpfMC4wMDFfZG93bikpCikKYGBgCgpgYGB7ciBlbnJpY2hyLWN1dG9mZi1saXN0cywgZXZhbD1GQUxTRX0KIyBQcmFjdGljZToKIyBDcmVhdGUgc2l4IGdlbmUgdGFibGVzIGZyb20gcmVzX2NsZWFuOgojICAgcGFkaiA8IDAuMDUsIHVwIGFuZCBkb3duCiMgICBwYWRqIDwgMC4wNSwgdXAgb25seQojICAgcGFkaiA8IDAuMDUsIGRvd24gb25seQojICAgcGFkaiA8IDAuMDAxLCB1cCBhbmQgZG93bgojICAgcGFkaiA8IDAuMDAxLCB1cCBvbmx5CiMgICBwYWRqIDwgMC4wMDEsIGRvd24gb25seQojCiMgVXNlIGZpbHRlcigpLCAhaXMubmEocGFkaiksIHBhZGosIGFuZCBsb2cyRm9sZENoYW5nZS4KIyBUaGVuIGNvdW50IHRoZSByb3dzIGluIGVhY2ggdGFibGUgd2l0aCBucm93KCkuCmBgYAoKYGBge3IgZW5yaWNoci1jdXRvZmYtc3VtbWFyeSwgZWNobz1GQUxTRX0KY3V0b2ZmX3N1bW1hcnkKYGBgCgpgYGB7ciB3cml0ZS1lbnJpY2hyLWN1dG9mZi1saXN0cy1oaWRkZW4sIGluY2x1ZGU9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CndyaXRlX2xpbmVzKGRlX3BhZGpfMC4wNV91cF9kb3duJGdlbmVfbmFtZSwgImVucmljaHJfREVfcGFkajAuMDVfdXBfZG93bi50eHQiKQp3cml0ZV9saW5lcyhkZV9wYWRqXzAuMDVfdXAkZ2VuZV9uYW1lLCAiZW5yaWNocl9ERV9wYWRqMC4wNV91cC50eHQiKQp3cml0ZV9saW5lcyhkZV9wYWRqXzAuMDVfZG93biRnZW5lX25hbWUsICJlbnJpY2hyX0RFX3BhZGowLjA1X2Rvd24udHh0IikKd3JpdGVfbGluZXMoZGVfcGFkal8wLjAwMV91cF9kb3duJGdlbmVfbmFtZSwgImVucmljaHJfREVfcGFkajAuMDAxX3VwX2Rvd24udHh0IikKd3JpdGVfbGluZXMoZGVfcGFkal8wLjAwMV91cCRnZW5lX25hbWUsICJlbnJpY2hyX0RFX3BhZGowLjAwMV91cC50eHQiKQp3cml0ZV9saW5lcyhkZV9wYWRqXzAuMDAxX2Rvd24kZ2VuZV9uYW1lLCAiZW5yaWNocl9ERV9wYWRqMC4wMDFfZG93bi50eHQiKQpgYGAKCmBgYHtyIHdyaXRlLWVucmljaHItY3V0b2ZmLWxpc3RzLCBldmFsPUZBTFNFfQojIFByYWN0aWNlOgojIFVzZSB3cml0ZV9saW5lcygpIHRvIHNhdmUgb25lIGdlbmUgbGlzdCBmb3IgZWFjaCBjdXRvZmYgdGFibGUuCiMgRWFjaCBmaWxlIHNob3VsZCBjb250YWluIG9uZSBnZW5lIG5hbWUgcGVyIGxpbmUuCmBgYAoKKipUYXNrOioqIFVwbG9hZCB0aGVzZSBzaXggbGlzdHMgdG8gRW5yaWNoci4gV2hpY2ggdGVybXMgYXJlIHByZXNlbnQgbm8gbWF0dGVyCndoaWNoIGN1dG9mZiB5b3UgdXNlPyBXaGljaCB0ZXJtcyBvbmx5IGFwcGVhciB3aXRoIHRoZSBsYXJnZXIgb3Igc21hbGxlciBnZW5lCmxpc3RzPyBXaGF0IGNoYW5nZXMgd2hlbiB1cC0gYW5kIGRvd24tcmVndWxhdGVkIGdlbmVzIGFyZSB0ZXN0ZWQgc2VwYXJhdGVseT8KCiMjIDMuIExvYWQgTVNpZ0RCIEdlbmUgU2V0cyB3aXRoIG1zaWdkYnIKCmBtc2lnZGJyYCBwcm92aWRlcyBNU2lnREIgZ2VuZSBzZXRzIGFzIHNpbXBsZSBSIHRhYmxlcy4KClN0YXJ0IHdpdGggSGFsbG1hcmsgZ2VuZSBzZXRzLiBUaGVzZSBhcmUgY29tcGFjdCwgY3VyYXRlZCwgYW5kIGVhc2llciB0bwppbnRlcnByZXQgdGhhbiB0aGUgZnVsbCBHZW5lIE9udG9sb2d5LgoKYGBge3IgaGFsbG1hcmstc2V0c30KaGFsbG1hcmsgPC0gbXNpZ2RicihzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIsIGNvbGxlY3Rpb24gPSAiSCIpIHw+CiAgc2VsZWN0KGdzX25hbWUsIGdlbmUgPSAxKSB8PgogIGRpc3RpbmN0KCkKCmhhbGxtYXJrCmBgYAoKIyMgNC4gT25lIEV4cGxpY2l0IEZpc2hlciBFeGFjdCBUZXN0CgpPUkEgYXNrcyB3aGV0aGVyIGEgcGF0aHdheSBjb250YWlucyBtb3JlIGRpZmZlcmVudGlhbCBnZW5lcyB0aGFuIGV4cGVjdGVkIGJ5CmNoYW5jZS4gV2UgY2FuIHRlc3QgdGhpcyBkaXJlY3RseSBmb3Igb25lIGdlbmUgc2V0LgoKSGVyZSB3ZSB0ZXN0IGBIQUxMTUFSS19LUkFTX1NJR05BTElOR19VUGAsIGEgdXNlZnVsIGV4YW1wbGUgZm9yIE1FSyBpbmhpYml0aW9uCmJlY2F1c2UgaXQgaXMgY29ubmVjdGVkIHRvIHRoZSBSQVMvUkFGL01FSy9FUksgYXhpcy4KCmBgYHtyIGZpc2hlci1vbmUtc2V0LXNldHVwfQpleGFtcGxlX3NldF9uYW1lIDwtICJIQUxMTUFSS19LUkFTX1NJR05BTElOR19VUCIKCmV4YW1wbGVfc2V0IDwtIGhhbGxtYXJrIHw+CiAgZmlsdGVyKGdzX25hbWUgPT0gZXhhbXBsZV9zZXRfbmFtZSkgfD4KICBwdWxsKGdlbmUpIHw+CiAgaW50ZXJzZWN0KGdlbmVfdW5pdmVyc2UpCgpzZWxlY3RlZF9nZW5lcyA8LSBkZV9nZW5lcyB8PgogIHB1bGwoZ2VuZV9uYW1lKSB8PgogIHVuaXF1ZSgpCmBgYAoKYGBge3IgZmlzaGVyLW9uZS1zZXQtY291bnRzLWhpZGRlbiwgaW5jbHVkZT1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZGVfYW5kX2luX3NldCA8LSBsZW5ndGgoaW50ZXJzZWN0KHNlbGVjdGVkX2dlbmVzLCBleGFtcGxlX3NldCkpCmRlX25vdF9pbl9zZXQgPC0gbGVuZ3RoKHNldGRpZmYoc2VsZWN0ZWRfZ2VuZXMsIGV4YW1wbGVfc2V0KSkKbm90X2RlX2luX3NldCA8LSBsZW5ndGgoc2V0ZGlmZihleGFtcGxlX3NldCwgc2VsZWN0ZWRfZ2VuZXMpKQpub3RfZGVfbm90X2luX3NldCA8LSBsZW5ndGgoc2V0ZGlmZihnZW5lX3VuaXZlcnNlLCB1bmlvbihzZWxlY3RlZF9nZW5lcywgZXhhbXBsZV9zZXQpKSkKYGBgCgpgYGB7ciBmaXNoZXItb25lLXNldC1jb3VudHMsIGV2YWw9RkFMU0V9CiMgUHJhY3RpY2U6CiMgQ291bnQgdGhlIGZvdXIgZ3JvdXBzIG5lZWRlZCBmb3IgdGhlIEZpc2hlciBleGFjdCB0ZXN0LgojCiMgVXNlID9pbnRlcnNlY3QsID91bmlvbiwgYW5kID9zZXRkaWZmIHRvIGxvb2sgdXAgd2hhdCB0aGVzZSBmdW5jdGlvbnMgZG8uCiMKIyBkZV9hbmRfaW5fc2V0OiBnZW5lcyB0aGF0IGFyZSBzZWxlY3RlZCBhbmQgYXJlIGluIHRoZSBleGFtcGxlIGdlbmUgc2V0CiMgZGVfbm90X2luX3NldDogZ2VuZXMgdGhhdCBhcmUgc2VsZWN0ZWQgYnV0IGFyZSBub3QgaW4gdGhlIGV4YW1wbGUgZ2VuZSBzZXQKIyBub3RfZGVfaW5fc2V0OiBnZW5lcyB0aGF0IGFyZSBub3Qgc2VsZWN0ZWQgYnV0IGFyZSBpbiB0aGUgZXhhbXBsZSBnZW5lIHNldAojIG5vdF9kZV9ub3RfaW5fc2V0OiBnZW5lcyB0aGF0IGFyZSBub3Qgc2VsZWN0ZWQgYW5kIGFyZSBub3QgaW4gdGhlIGV4YW1wbGUgZ2VuZSBzZXQKYGBgCgpgYGB7ciBmaXNoZXItb25lLXNldH0KZmlzaGVyX3RhYmxlIDwtIG1hdHJpeCgKICBjKGRlX2FuZF9pbl9zZXQsIGRlX25vdF9pbl9zZXQsIG5vdF9kZV9pbl9zZXQsIG5vdF9kZV9ub3RfaW5fc2V0KSwKICBucm93ID0gMiwKICBieXJvdyA9IFRSVUUsCiAgZGltbmFtZXMgPSBsaXN0KAogICAgZGlmZmVyZW50aWFsID0gYygieWVzIiwgIm5vIiksCiAgICBpbl9nZW5lX3NldCA9IGMoInllcyIsICJubyIpCiAgKQopCgpmaXNoZXJfdGFibGUKZmlzaGVyLnRlc3QoZmlzaGVyX3RhYmxlLCBhbHRlcm5hdGl2ZSA9ICJncmVhdGVyIikKYGBgCgoqKlRhc2s6KiogSW50ZXJwcmV0IHRoZSBmb3VyIG51bWJlcnMgaW4gdGhlIGNvbnRpbmdlbmN5IHRhYmxlLiBXaGljaCBjZWxsCmNvbnRhaW5zIGRpZmZlcmVudGlhbCBnZW5lcyB0aGF0IGFyZSBtZW1iZXJzIG9mIHRoZSBwYXRod2F5PwoKIyMgNS4gT1JBIHdpdGggbXNpZ2RicgoKTm93IHJ1biB0aGUgc2FtZSBpZGVhIGFjcm9zcyBhbGwgSGFsbG1hcmsgZ2VuZSBzZXRzLgoKYGBge3IgaGFsbG1hcmstb3JhfQpoYWxsbWFya19wYXRod2F5cyA8LSBzcGxpdChoYWxsbWFyayRnZW5lLCBoYWxsbWFyayRnc19uYW1lKQoKaGFsbG1hcmtfb3JhX3RhYiA8LSBmb3JhKAogIHBhdGh3YXlzID0gaGFsbG1hcmtfcGF0aHdheXMsCiAgZ2VuZXMgPSBzZWxlY3RlZF9nZW5lcywKICB1bml2ZXJzZSA9IGdlbmVfdW5pdmVyc2UsCiAgbWluU2l6ZSA9IDEwLAogIG1heFNpemUgPSA1MDAKKSB8PgogIGFzX3RpYmJsZSgpIHw+CiAgbXV0YXRlKG92ZXJsYXBHZW5lcyA9IHZhcHBseShvdmVybGFwR2VuZXMsIHBhc3RlLCBjaGFyYWN0ZXIoMSksIGNvbGxhcHNlID0gIi8iKSkgfD4KICBhcnJhbmdlKHBhZGopCgpoYWxsbWFya19vcmFfdGFiCmBgYAoKYGBge3IgaGFsbG1hcmstb3JhLWRvdHBsb3QsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTh9CmhhbGxtYXJrX29yYV90YWIgfD4KICBzbGljZV9taW4ocGFkaiwgbiA9IDE1KSB8PgogIGdncGxvdChhZXMoeCA9IC1sb2cxMChwYWRqKSwgeSA9IHJlb3JkZXIocGF0aHdheSwgLWxvZzEwKHBhZGopKSkpICsKICBnZW9tX2NvbCgpICsKICBsYWJzKAogICAgeCA9ICItbG9nMTAoYWRqdXN0ZWQgcC12YWx1ZSkiLAogICAgeSA9IE5VTEwsCiAgICB0aXRsZSA9ICJIYWxsbWFyayBPUkEgZnJvbSB0aHJlc2hvbGRlZCBERSBnZW5lcyIKICApICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpDaGVjayB3aGV0aGVyIHRoZSBPUkEgcmVzdWx0IGNoYW5nZXMgd2hlbiB1c2luZyB0aGUgc2FtZSBjdXRvZmZzIGFzIGluIHRoZQpFbnJpY2hyIGV4ZXJjaXNlLgoKYGBge3IgaGFsbG1hcmstb3JhLWN1dG9mZnMtaGlkZGVuLCBpbmNsdWRlPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpzZWxlY3RlZF9nZW5lc19wYWRqXzAuMDVfdXBfZG93biA8LSBkZV9wYWRqXzAuMDVfdXBfZG93biB8PgogIHB1bGwoZ2VuZV9uYW1lKSB8PgogIHVuaXF1ZSgpCgpzZWxlY3RlZF9nZW5lc19wYWRqXzAuMDVfdXAgPC0gZGVfcGFkal8wLjA1X3VwIHw+CiAgcHVsbChnZW5lX25hbWUpIHw+CiAgdW5pcXVlKCkKCnNlbGVjdGVkX2dlbmVzX3BhZGpfMC4wNV9kb3duIDwtIGRlX3BhZGpfMC4wNV9kb3duIHw+CiAgcHVsbChnZW5lX25hbWUpIHw+CiAgdW5pcXVlKCkKCnNlbGVjdGVkX2dlbmVzX3BhZGpfMC4wMDFfdXBfZG93biA8LSBkZV9wYWRqXzAuMDAxX3VwX2Rvd24gfD4KICBwdWxsKGdlbmVfbmFtZSkgfD4KICB1bmlxdWUoKQoKc2VsZWN0ZWRfZ2VuZXNfcGFkal8wLjAwMV91cCA8LSBkZV9wYWRqXzAuMDAxX3VwIHw+CiAgcHVsbChnZW5lX25hbWUpIHw+CiAgdW5pcXVlKCkKCnNlbGVjdGVkX2dlbmVzX3BhZGpfMC4wMDFfZG93biA8LSBkZV9wYWRqXzAuMDAxX2Rvd24gfD4KICBwdWxsKGdlbmVfbmFtZSkgfD4KICB1bmlxdWUoKQoKaGFsbG1hcmtfb3JhX3BhZGpfMC4wNV91cF9kb3duIDwtIGZvcmEoCiAgcGF0aHdheXMgPSBoYWxsbWFya19wYXRod2F5cywKICBnZW5lcyA9IHNlbGVjdGVkX2dlbmVzX3BhZGpfMC4wNV91cF9kb3duLAogIHVuaXZlcnNlID0gZ2VuZV91bml2ZXJzZSwKICBtaW5TaXplID0gMTAsCiAgbWF4U2l6ZSA9IDUwMAopIHw+CiAgYXNfdGliYmxlKCkgfD4KICBtdXRhdGUob3ZlcmxhcEdlbmVzID0gdmFwcGx5KG92ZXJsYXBHZW5lcywgcGFzdGUsIGNoYXJhY3RlcigxKSwgY29sbGFwc2UgPSAiLyIpKSB8PgogIGFycmFuZ2UocGFkaikKCmhhbGxtYXJrX29yYV9wYWRqXzAuMDVfdXAgPC0gZm9yYSgKICBwYXRod2F5cyA9IGhhbGxtYXJrX3BhdGh3YXlzLAogIGdlbmVzID0gc2VsZWN0ZWRfZ2VuZXNfcGFkal8wLjA1X3VwLAogIHVuaXZlcnNlID0gZ2VuZV91bml2ZXJzZSwKICBtaW5TaXplID0gMTAsCiAgbWF4U2l6ZSA9IDUwMAopIHw+CiAgYXNfdGliYmxlKCkgfD4KICBtdXRhdGUob3ZlcmxhcEdlbmVzID0gdmFwcGx5KG92ZXJsYXBHZW5lcywgcGFzdGUsIGNoYXJhY3RlcigxKSwgY29sbGFwc2UgPSAiLyIpKSB8PgogIGFycmFuZ2UocGFkaikKCmhhbGxtYXJrX29yYV9wYWRqXzAuMDVfZG93biA8LSBmb3JhKAogIHBhdGh3YXlzID0gaGFsbG1hcmtfcGF0aHdheXMsCiAgZ2VuZXMgPSBzZWxlY3RlZF9nZW5lc19wYWRqXzAuMDVfZG93biwKICB1bml2ZXJzZSA9IGdlbmVfdW5pdmVyc2UsCiAgbWluU2l6ZSA9IDEwLAogIG1heFNpemUgPSA1MDAKKSB8PgogIGFzX3RpYmJsZSgpIHw+CiAgbXV0YXRlKG92ZXJsYXBHZW5lcyA9IHZhcHBseShvdmVybGFwR2VuZXMsIHBhc3RlLCBjaGFyYWN0ZXIoMSksIGNvbGxhcHNlID0gIi8iKSkgfD4KICBhcnJhbmdlKHBhZGopCgpoYWxsbWFya19vcmFfcGFkal8wLjAwMV91cF9kb3duIDwtIGZvcmEoCiAgcGF0aHdheXMgPSBoYWxsbWFya19wYXRod2F5cywKICBnZW5lcyA9IHNlbGVjdGVkX2dlbmVzX3BhZGpfMC4wMDFfdXBfZG93biwKICB1bml2ZXJzZSA9IGdlbmVfdW5pdmVyc2UsCiAgbWluU2l6ZSA9IDEwLAogIG1heFNpemUgPSA1MDAKKSB8PgogIGFzX3RpYmJsZSgpIHw+CiAgbXV0YXRlKG92ZXJsYXBHZW5lcyA9IHZhcHBseShvdmVybGFwR2VuZXMsIHBhc3RlLCBjaGFyYWN0ZXIoMSksIGNvbGxhcHNlID0gIi8iKSkgfD4KICBhcnJhbmdlKHBhZGopCgpoYWxsbWFya19vcmFfcGFkal8wLjAwMV91cCA8LSBmb3JhKAogIHBhdGh3YXlzID0gaGFsbG1hcmtfcGF0aHdheXMsCiAgZ2VuZXMgPSBzZWxlY3RlZF9nZW5lc19wYWRqXzAuMDAxX3VwLAogIHVuaXZlcnNlID0gZ2VuZV91bml2ZXJzZSwKICBtaW5TaXplID0gMTAsCiAgbWF4U2l6ZSA9IDUwMAopIHw+CiAgYXNfdGliYmxlKCkgfD4KICBtdXRhdGUob3ZlcmxhcEdlbmVzID0gdmFwcGx5KG92ZXJsYXBHZW5lcywgcGFzdGUsIGNoYXJhY3RlcigxKSwgY29sbGFwc2UgPSAiLyIpKSB8PgogIGFycmFuZ2UocGFkaikKCmhhbGxtYXJrX29yYV9wYWRqXzAuMDAxX2Rvd24gPC0gZm9yYSgKICBwYXRod2F5cyA9IGhhbGxtYXJrX3BhdGh3YXlzLAogIGdlbmVzID0gc2VsZWN0ZWRfZ2VuZXNfcGFkal8wLjAwMV9kb3duLAogIHVuaXZlcnNlID0gZ2VuZV91bml2ZXJzZSwKICBtaW5TaXplID0gMTAsCiAgbWF4U2l6ZSA9IDUwMAopIHw+CiAgYXNfdGliYmxlKCkgfD4KICBtdXRhdGUob3ZlcmxhcEdlbmVzID0gdmFwcGx5KG92ZXJsYXBHZW5lcywgcGFzdGUsIGNoYXJhY3RlcigxKSwgY29sbGFwc2UgPSAiLyIpKSB8PgogIGFycmFuZ2UocGFkaikKCm9yYV9jdXRvZmZfdG9wX3Rlcm1zIDwtIHRpYmJsZSgKICBjdXRvZmYgPSBjKCJwYWRqIDwgMC4wNSwgdXAgYW5kIGRvd24iLAogICAgICAgICAgICAgInBhZGogPCAwLjA1LCB1cCIsCiAgICAgICAgICAgICAicGFkaiA8IDAuMDUsIGRvd24iLAogICAgICAgICAgICAgInBhZGogPCAwLjAwMSwgdXAgYW5kIGRvd24iLAogICAgICAgICAgICAgInBhZGogPCAwLjAwMSwgdXAiLAogICAgICAgICAgICAgInBhZGogPCAwLjAwMSwgZG93biIpLAogIHRvcF9oYWxsbWFyayA9IGMoaGFsbG1hcmtfb3JhX3BhZGpfMC4wNV91cF9kb3duJHBhdGh3YXlbMV0sCiAgICAgICAgICAgICAgICAgICBoYWxsbWFya19vcmFfcGFkal8wLjA1X3VwJHBhdGh3YXlbMV0sCiAgICAgICAgICAgICAgICAgICBoYWxsbWFya19vcmFfcGFkal8wLjA1X2Rvd24kcGF0aHdheVsxXSwKICAgICAgICAgICAgICAgICAgIGhhbGxtYXJrX29yYV9wYWRqXzAuMDAxX3VwX2Rvd24kcGF0aHdheVsxXSwKICAgICAgICAgICAgICAgICAgIGhhbGxtYXJrX29yYV9wYWRqXzAuMDAxX3VwJHBhdGh3YXlbMV0sCiAgICAgICAgICAgICAgICAgICBoYWxsbWFya19vcmFfcGFkal8wLjAwMV9kb3duJHBhdGh3YXlbMV0pLAogIGFkanVzdGVkX3BfdmFsdWUgPSBjKGhhbGxtYXJrX29yYV9wYWRqXzAuMDVfdXBfZG93biRwYWRqWzFdLAogICAgICAgICAgICAgICAgICAgICAgIGhhbGxtYXJrX29yYV9wYWRqXzAuMDVfdXAkcGFkalsxXSwKICAgICAgICAgICAgICAgICAgICAgICBoYWxsbWFya19vcmFfcGFkal8wLjA1X2Rvd24kcGFkalsxXSwKICAgICAgICAgICAgICAgICAgICAgICBoYWxsbWFya19vcmFfcGFkal8wLjAwMV91cF9kb3duJHBhZGpbMV0sCiAgICAgICAgICAgICAgICAgICAgICAgaGFsbG1hcmtfb3JhX3BhZGpfMC4wMDFfdXAkcGFkalsxXSwKICAgICAgICAgICAgICAgICAgICAgICBoYWxsbWFya19vcmFfcGFkal8wLjAwMV9kb3duJHBhZGpbMV0pCikKYGBgCgpgYGB7ciBoYWxsbWFyay1vcmEtY3V0b2ZmcywgZXZhbD1GQUxTRX0KIyBQcmFjdGljZToKIyBSZXBlYXQgdGhlIEhhbGxtYXJrIE9SQSB3aXRoIHRoZSBzYW1lIHNpeCBnZW5lIGxpc3RzIGZyb20gdGhlIEVucmljaHIgdGFzazoKIyAgIHBhZGogPCAwLjA1LCB1cCBhbmQgZG93bgojICAgcGFkaiA8IDAuMDUsIHVwIG9ubHkKIyAgIHBhZGogPCAwLjA1LCBkb3duIG9ubHkKIyAgIHBhZGogPCAwLjAwMSwgdXAgYW5kIGRvd24KIyAgIHBhZGogPCAwLjAwMSwgdXAgb25seQojICAgcGFkaiA8IDAuMDAxLCBkb3duIG9ubHkKIwojIEZvciBlYWNoIGxpc3Q6CiMgICBwdWxsKGdlbmVfbmFtZSkKIyAgIHJ1biBmb3JhKCkKIyAgIGFycmFuZ2UgYnkgcGFkagojICAgY29tcGFyZSB0aGUgdG9wIEhhbGxtYXJrIHRlcm1zCmBgYAoKYGBge3Igc2hvdy1oYWxsbWFyay1vcmEtY3V0b2ZmcywgZWNobz1GQUxTRX0Kb3JhX2N1dG9mZl90b3BfdGVybXMKYGBgCgpTYXZlIHRoZSBPUkEgdGFibGU6CgpgYGB7ciBzYXZlLWhhbGxtYXJrLW9yYX0Kd3JpdGVfdHN2KGhhbGxtYXJrX29yYV90YWIsICJPUkFfSGFsbG1hcmtfbXNpZ2RiLnRzdiIpCndyaXRlX3hsc3goaGFsbG1hcmtfb3JhX3RhYiwgIk9SQV9IYWxsbWFya19tc2lnZGIueGxzeCIpCmBgYAoKKipEaXNjdXNzaW9uOioqIE9SQSBkZXBlbmRzIG9uIHRoZSBjdXRvZmYgdXNlZCB0byBzZWxlY3QgZ2VuZXMuIFdoaWNoIEhhbGxtYXJrCnRlcm1zIGFyZSBzdGFibGUgYWNyb3NzIHRoZSBzaXggbGlzdHM/IFdoaWNoIHRlcm1zIGRlcGVuZCBvbiB0ZXN0aW5nIHVwLSBhbmQKZG93bi1yZWd1bGF0ZWQgZ2VuZXMgc2VwYXJhdGVseT8KCiMjIDYuIEdTRUEtU3R5bGUgQW5hbHlzaXMgV2l0aG91dCBhbiBFeHBsaWNpdCBDdXRvZmYKCkdTRUEtc3R5bGUgYW5hbHlzaXMgYXZvaWRzIHNlbGVjdGluZyBhIGRpZmZlcmVudGlhbC1nZW5lIGN1dG9mZi4gSW5zdGVhZCwgaXQKdXNlcyBhbGwgZ2VuZXMgcmFua2VkIGZyb20gbW9zdCB1cC1yZWd1bGF0ZWQgaW4gTUVLaSB0byBtb3N0IGRvd24tcmVndWxhdGVkIGluCk1FS2kuCgpIZXJlIHdlIHJhbmsgZ2VuZXMgYnkgdGhlIERFU2VxMiBXYWxkIHN0YXRpc3RpYy4KCmBgYHtyIHJhbmtlZC1saXN0fQpyYW5rZWRfZ2VuZV9saXN0IDwtIHJlc19jbGVhbiB8PgogIGFycmFuZ2UoZGVzYyhzdGF0KSkgfD4KICBzZWxlY3QoZ2VuZV9uYW1lLCBzdGF0KQoKcmFua3MgPC0gcmFua2VkX2dlbmVfbGlzdCRzdGF0Cm5hbWVzKHJhbmtzKSA8LSByYW5rZWRfZ2VuZV9saXN0JGdlbmVfbmFtZQoKaGVhZChyYW5rcykKdGFpbChyYW5rcykKYGBgCgpgYGB7ciBoYWxsbWFyay1nc2VhfQpzZXQuc2VlZCgxKQoKaGFsbG1hcmtfZ3NlYV90YWIgPC0gZmdzZWEoCiAgcGF0aHdheXMgPSBoYWxsbWFya19wYXRod2F5cywKICBzdGF0cyA9IHJhbmtzLAogIG1pblNpemUgPSAxMCwKICBtYXhTaXplID0gNTAwLAogIGVwcyA9IDAKKSB8PgogIGFzX3RpYmJsZSgpIHw+CiAgbXV0YXRlKGxlYWRpbmdFZGdlID0gdmFwcGx5KGxlYWRpbmdFZGdlLCBwYXN0ZSwgY2hhcmFjdGVyKDEpLCBjb2xsYXBzZSA9ICIvIikpIHw+CiAgYXJyYW5nZShwYWRqKQoKaGFsbG1hcmtfZ3NlYV90YWIKYGBgCgpgYGB7ciBoYWxsbWFyay1nc2VhLWRvdHBsb3QsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTl9CmhhbGxtYXJrX2dzZWFfdGFiIHw+CiAgc2xpY2VfbWluKHBhZGosIG4gPSAxNSkgfD4KICBnZ3Bsb3QoYWVzKHggPSBORVMsIHkgPSByZW9yZGVyKHBhdGh3YXksIE5FUyksIGZpbGwgPSBORVMgPiAwKSkgKwogIGdlb21fY29sKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlRSVUUiID0gImZpcmVicmljayIsICJGQUxTRSIgPSAic3RlZWxibHVlIiksIGd1aWRlID0gIm5vbmUiKSArCiAgbGFicygKICAgIHggPSAiTm9ybWFsaXplZCBlbnJpY2htZW50IHNjb3JlIiwKICAgIHkgPSBOVUxMLAogICAgdGl0bGUgPSAiSGFsbG1hcmsgR1NFQSBmcm9tIGFsbCByYW5rZWQgZ2VuZXMiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKUG9zaXRpdmUgTkVTIG1lYW5zIGVucmljaG1lbnQgbmVhciB0aGUgdG9wIG9mIHRoZSByYW5rZWQgbGlzdCwgaS5lLiBnZW5lcyB0ZW5kCnRvIGJlIGhpZ2hlciBpbiBNRUtpLiBOZWdhdGl2ZSBORVMgbWVhbnMgZW5yaWNobWVudCBuZWFyIHRoZSBib3R0b20sIGkuZS4gZ2VuZXMKdGVuZCB0byBiZSBsb3dlciBpbiBNRUtpLgoKYGBge3Igc2F2ZS1oYWxsbWFyay1nc2VhfQp3cml0ZV90c3YoaGFsbG1hcmtfZ3NlYV90YWIsICJHU0VBX0hhbGxtYXJrX21zaWdkYi50c3YiKQp3cml0ZV94bHN4KGhhbGxtYXJrX2dzZWFfdGFiLCAiR1NFQV9IYWxsbWFya19tc2lnZGIueGxzeCIpCmBgYAoKTWFrZSBhIHZvbGNhbm8tc3R5bGUgcGxvdCBvZiB0aGUgSGFsbG1hcmsgR1NFQSByZXN1bHQuCgpgYGB7ciBoYWxsbWFyay1nc2VhLXZvbGNhbm8sIGV2YWw9RkFMU0UsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CiMgUHJhY3RpY2U6CiMgVXNlIEVuaGFuY2VkVm9sY2FubygpIHdpdGggaGFsbG1hcmtfZ3NlYV90YWIuCiMKIyBQdXQgTkVTIG9uIHRoZSB4LWF4aXMuCiMgUHV0IHBhZGogb24gdGhlIHktYXhpcy4KIyBVc2UgcGF0aHdheSBuYW1lcyBhcyBsYWJlbHMuCiMgQSB1c2VmdWwgY3V0b2ZmIGZvciBORVMgaXMgMS4KYGBgCgoqKlRhc2s6KiogQ29tcGFyZSBPUkEgYW5kIEdTRUEgcmVzdWx0cy4gV2hpY2ggYmlvbG9naWNhbCB0aGVtZXMgYXBwZWFyIGluIGJvdGg/CldoaWNoIG9ubHkgYXBwZWFyIHdoZW4gYWxsIGdlbmVzIGFyZSByYW5rZWQ/CgojIyA3LiBTYW1wbGUtTGV2ZWwgSGFsbG1hcmsgU2NvcmVzIHdpdGggR1NWQQoKT1JBIGFuZCBHU0VBIHVzZSB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0IHRhYmxlLiBHU1ZBIGFuc3dlcnMgYQpkaWZmZXJlbnQgcXVlc3Rpb246IGhvdyBhY3RpdmUgZG9lcyBlYWNoIEhhbGxtYXJrIGdlbmUgc2V0IGxvb2sgaW4gZWFjaCBzYW1wbGU/CgpGb3IgdGhpcyB3ZSB1c2UgdmFyaWFuY2Utc3RhYmlsaXplZCBleHByZXNzaW9uIHZhbHVlcywgYXMgaW4gdGhlIFBDQSB3b3JrZmxvdy4KCmBgYHtyIGxvYWQtZGRzLWZvci1nc3ZhfQpkZHMgPC0gcmVhZFJEUygiZGRzLnJkcyIpCgpzYW1wbGVfaW5mbyA8LSBjb2xEYXRhKGRkcykgfD4KICBhcy5kYXRhLmZyYW1lKCkgfD4KICBzZWxlY3Qoc2FtcGxlLCBjb25kaXRpb24pCgpzYW1wbGVfaW5mbwpgYGAKClJ1biB0aGUgc2FtZSB2YXJpYW5jZS1zdGFiaWxpemluZyB0cmFuc2Zvcm1hdGlvbiB1c2VkIGZvciBQQ0EsIHRoZW4gcHJlcGFyZSBhbgpleHByZXNzaW9uIG1hdHJpeCB3aXRoIGdlbmUgbmFtZXMuCgpgYGB7ciBwcmVwYXJlLXZzdC1tYXRyaXh9CnZzdF9jb3VudHMgPC0gdnN0KGRkcywgYmxpbmQgPSBGQUxTRSkKCmV4cHJlc3Npb25fbWF0cml4IDwtIGFzc2F5KHZzdF9jb3VudHMpIHw+CiAgYXMuZGF0YS5mcmFtZSgpIHw+CiAgcm93bmFtZXNfdG9fY29sdW1uKCJnZW5lX2lkIikgfD4KICBpbm5lcl9qb2luKHJlc19jbGVhbiB8PiBzZWxlY3QoZ2VuZV9pZCwgZ2VuZV9uYW1lKSwgYnkgPSAiZ2VuZV9pZCIpIHw+CiAgc2VsZWN0KGdlbmVfbmFtZSwgYWxsX29mKHNhbXBsZV9pbmZvJHNhbXBsZSkpIHw+CiAgZ3JvdXBfYnkoZ2VuZV9uYW1lKSB8PgogIHN1bW1hcml6ZShhY3Jvc3MoYWxsX29mKHNhbXBsZV9pbmZvJHNhbXBsZSksIG1lYW4pLCAuZ3JvdXBzID0gImRyb3AiKSB8PgogIGNvbHVtbl90b19yb3duYW1lcygiZ2VuZV9uYW1lIikgfD4KICBhcy5tYXRyaXgoKQoKZGltKGV4cHJlc3Npb25fbWF0cml4KQpgYGAKCk5vdyBjYWxjdWxhdGUgb25lIEdTVkEgc2NvcmUgcGVyIEhhbGxtYXJrIGdlbmUgc2V0IGFuZCBzYW1wbGUuCgpgYGB7ciBnc3ZhLWhhbGxtYXJrfQpnc3ZhX3BhcmFtZXRlcnMgPC0gZ3N2YVBhcmFtKAogIGV4cHJlc3Npb25fbWF0cml4LAogIGhhbGxtYXJrX3BhdGh3YXlzLAogIG1pblNpemUgPSAxMCwKICBtYXhTaXplID0gNTAwLAogIGtjZGYgPSAiR2F1c3NpYW4iCikKCmhhbGxtYXJrX2dzdmEgPC0gZ3N2YShnc3ZhX3BhcmFtZXRlcnMpCgpkaW0oaGFsbG1hcmtfZ3N2YSkKaGFsbG1hcmtfZ3N2YVsxOjUsIF0KYGBgCgpGb3IgdGhlIGhlYXRtYXAsIHdlIHNjYWxlIGVhY2ggSGFsbG1hcmsgcm93LiBSZWQgbWVhbnMgcmVsYXRpdmVseSBoaWdoIGFjdGl2aXR5CmZvciB0aGF0IEhhbGxtYXJrIGluIHRoYXQgc2FtcGxlOyBibHVlIG1lYW5zIHJlbGF0aXZlbHkgbG93IGFjdGl2aXR5LgoKYGBge3IgZ3N2YS1oZWF0bWFwLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD0xMH0KaGFsbG1hcmtfZ3N2YV9zY2FsZWQgPC0gdChzY2FsZSh0KGhhbGxtYXJrX2dzdmEpKSkKCmNvbmRpdGlvbl9jb2xvcnMgPC0gYyhjb250cm9sID0gImdyZXk3MCIsIE1FS2kgPSAiZmlyZWJyaWNrIikKCnNhbXBsZV9hbm5vdGF0aW9uIDwtIEhlYXRtYXBBbm5vdGF0aW9uKAogIGNvbmRpdGlvbiA9IHNhbXBsZV9pbmZvJGNvbmRpdGlvbiwKICBjb2wgPSBsaXN0KGNvbmRpdGlvbiA9IGNvbmRpdGlvbl9jb2xvcnMpCikKCkhlYXRtYXAoCiAgaGFsbG1hcmtfZ3N2YV9zY2FsZWQsCiAgbmFtZSA9ICJHU1ZBXG56LXNjb3JlIiwKICBjb2wgPSBjb2xvclJhbXAyKGMoLTIsIDAsIDIpLCBjKCJzdGVlbGJsdWUiLCAid2hpdGUiLCAiZmlyZWJyaWNrIikpLAogIHRvcF9hbm5vdGF0aW9uID0gc2FtcGxlX2Fubm90YXRpb24sCiAgY29sdW1uX3NwbGl0ID0gc2FtcGxlX2luZm8kY29uZGl0aW9uLAogIGNsdXN0ZXJfY29sdW1ucyA9IEZBTFNFLAogIHJvd19uYW1lc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSA3KSwKICBjb2x1bW5fbmFtZXNfZ3AgPSBncmlkOjpncGFyKGZvbnRzaXplID0gOSkKKQpgYGAKClNhdmUgdGhlIEdTVkEgc2NvcmVzOgoKYGBge3Igc2F2ZS1nc3ZhfQpoYWxsbWFya19nc3ZhX3RhYiA8LSBoYWxsbWFya19nc3ZhIHw+CiAgYXMuZGF0YS5mcmFtZSgpIHw+CiAgcm93bmFtZXNfdG9fY29sdW1uKCJoYWxsbWFyayIpCgp3cml0ZV90c3YoaGFsbG1hcmtfZ3N2YV90YWIsICJHU1ZBX0hhbGxtYXJrX3NhbXBsZV9zY29yZXMudHN2IikKd3JpdGVfeGxzeChoYWxsbWFya19nc3ZhX3RhYiwgIkdTVkFfSGFsbG1hcmtfc2FtcGxlX3Njb3Jlcy54bHN4IikKYGBgCgoqKlRhc2s6KiogQ29tcGFyZSB0aGUgR1NWQSBoZWF0bWFwIHRvIHRoZSBIYWxsbWFyayBHU0VBIHJlc3VsdC4gRG8gdGhlIHBhdGh3YXlzCnRoYXQgY2hhbmdlIGdsb2JhbGx5IGFsc28gc2VwYXJhdGUgdGhlIGluZGl2aWR1YWwgY29udHJvbCBhbmQgTUVLaSBzYW1wbGVzPwoKIyMgOC4gRXh0ZW5kIHRvIEdlbmUgT250b2xvZ3kgQmlvbG9naWNhbCBQcm9jZXNzCgpHTyBCaW9sb2dpY2FsIFByb2Nlc3MgaXMgbXVjaCBsYXJnZXIgYW5kIG1vcmUgcmVkdW5kYW50IHRoYW4gSGFsbG1hcmsuIEl0IGlzCnVzZWZ1bCBmb3IgZGVwdGgsIGJ1dCBpbnRlcnByZXRhdGlvbiBzaG91bGQgZm9jdXMgb24gdGhlbWVzIHJhdGhlciB0aGFuIHNpbmdsZQpuZWFyLWR1cGxpY2F0ZSB0ZXJtIG5hbWVzLgoKYGBge3IgZ28tYnAtc2V0c30KZ29fYnAgPC0gbXNpZ2RicigKICBzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIsCiAgY29sbGVjdGlvbiA9ICJDNSIsCiAgc3ViY29sbGVjdGlvbiA9ICJHTzpCUCIKKSB8PgogIHNlbGVjdChnc19uYW1lLCBnZW5lID0gMSkgfD4KICBkaXN0aW5jdCgpCgpnb19icApgYGAKClJ1biBPUkEgd2l0aCBHTyBCUDoKCmBgYHtyIGdvLWJwLW9yYS1oaWRkZW4sIGluY2x1ZGU9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmdvX2JwX3BhdGh3YXlzIDwtIHNwbGl0KGdvX2JwJGdlbmUsIGdvX2JwJGdzX25hbWUpCgpnb19icF9vcmFfdGFiIDwtIGZvcmEoCiAgcGF0aHdheXMgPSBnb19icF9wYXRod2F5cywKICBnZW5lcyA9IHNlbGVjdGVkX2dlbmVzLAogIHVuaXZlcnNlID0gZ2VuZV91bml2ZXJzZSwKICBtaW5TaXplID0gMTAsCiAgbWF4U2l6ZSA9IDUwMAopIHw+CiAgYXNfdGliYmxlKCkgfD4KICBtdXRhdGUob3ZlcmxhcEdlbmVzID0gdmFwcGx5KG92ZXJsYXBHZW5lcywgcGFzdGUsIGNoYXJhY3RlcigxKSwgY29sbGFwc2UgPSAiLyIpKSB8PgogIGFycmFuZ2UocGFkaikKYGBgCgpgYGB7ciBnby1icC1vcmEsIGV2YWw9RkFMU0V9CiMgUHJhY3RpY2U6CiMgQ3JlYXRlIGdvX2JwX3BhdGh3YXlzIHdpdGggc3BsaXQoKSwgYXMgZm9yIHRoZSBIYWxsbWFyayBleGFtcGxlLgojCiMgVGhlbiBjcmVhdGUgZ29fYnBfb3JhX3RhYiB3aXRoIGZvcmEoKS4KIyBVc2UgdGhlIHNhbWUgZ2VuZXMsIHVuaXZlcnNlLCBtaW5TaXplLCBhbmQgbWF4U2l6ZSBhcyBiZWZvcmUuCiMgQ29udmVydCB0aGUgcmVzdWx0IHRvIGEgdGliYmxlLCBzaW1wbGlmeSBvdmVybGFwR2VuZXMsIGFuZCBhcnJhbmdlIGJ5IHBhZGouCmBgYAoKYGBge3Igc2hvdy1nby1icC1vcmEsIGVjaG89RkFMU0V9CmdvX2JwX29yYV90YWIKYGBgCgpgYGB7ciBnby1icC1vcmEtZG90cGxvdCwgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9OX0KZ29fYnBfb3JhX3RhYiB8PgogIHNsaWNlX21pbihwYWRqLCBuID0gMjApIHw+CiAgZ2dwbG90KGFlcyh4ID0gLWxvZzEwKHBhZGopLCB5ID0gcmVvcmRlcihwYXRod2F5LCAtbG9nMTAocGFkaikpKSkgKwogIGdlb21fY29sKCkgKwogIGxhYnMoCiAgICB4ID0gIi1sb2cxMChhZGp1c3RlZCBwLXZhbHVlKSIsCiAgICB5ID0gTlVMTCwKICAgIHRpdGxlID0gIkdPIEJQIE9SQSBmcm9tIHRocmVzaG9sZGVkIERFIGdlbmVzIgogICkgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKClJ1biBHU0VBLXN0eWxlIGFuYWx5c2lzIHdpdGggR08gQlA6CgpgYGB7ciBnby1icC1nc2VhLWhpZGRlbiwgaW5jbHVkZT1GQUxTRSwgbWVzc2FnZT1GQUxTRX0Kc2V0LnNlZWQoMSkKCmdvX2JwX2dzZWFfdGFiIDwtIGZnc2VhKAogIHBhdGh3YXlzID0gZ29fYnBfcGF0aHdheXMsCiAgc3RhdHMgPSByYW5rcywKICBtaW5TaXplID0gMTAsCiAgbWF4U2l6ZSA9IDUwMCwKICBlcHMgPSAwCikgfD4KICBhc190aWJibGUoKSB8PgogIG11dGF0ZShsZWFkaW5nRWRnZSA9IHZhcHBseShsZWFkaW5nRWRnZSwgcGFzdGUsIGNoYXJhY3RlcigxKSwgY29sbGFwc2UgPSAiLyIpKSB8PgogIGFycmFuZ2UocGFkaikKYGBgCgpgYGB7ciBnby1icC1nc2VhLCBldmFsPUZBTFNFfQojIFByYWN0aWNlOgojIENyZWF0ZSBnb19icF9nc2VhX3RhYiB3aXRoIGZnc2VhKCkuCiMgVXNlIGdvX2JwX3BhdGh3YXlzIGFuZCB0aGUgc2FtZSByYW5rZWQgbGlzdCB1c2VkIGZvciBIYWxsbWFyayBHU0VBLgojIENvbnZlcnQgdGhlIHJlc3VsdCB0byBhIHRpYmJsZSwgc2ltcGxpZnkgbGVhZGluZ0VkZ2UsIGFuZCBhcnJhbmdlIGJ5IHBhZGouCmBgYAoKYGBge3Igc2hvdy1nby1icC1nc2VhLCBlY2hvPUZBTFNFfQpnb19icF9nc2VhX3RhYgpgYGAKCmBgYHtyIGdvLWJwLWdzZWEtZG90cGxvdCwgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTF9CmdvX2JwX2dzZWFfcGxvdF90YWIgPC0gYmluZF9yb3dzKAogIGdvX2JwX2dzZWFfdGFiIHw+CiAgICBzbGljZV9tYXgoTkVTLCBuID0gMTUpLAogIGdvX2JwX2dzZWFfdGFiIHw+CiAgICBzbGljZV9taW4oTkVTLCBuID0gMTUpCikgfD4KICBkaXN0aW5jdChwYXRod2F5LCAua2VlcF9hbGwgPSBUUlVFKQoKZ29fYnBfZ3NlYV9wbG90X3RhYiB8PgogIGdncGxvdChhZXMoeCA9IE5FUywgeSA9IHJlb3JkZXIocGF0aHdheSwgTkVTKSwgZmlsbCA9IE5FUyA+IDApKSArCiAgZ2VvbV9jb2woKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiVFJVRSIgPSAiZmlyZWJyaWNrIiwgIkZBTFNFIiA9ICJzdGVlbGJsdWUiKSwgZ3VpZGUgPSAibm9uZSIpICsKICBsYWJzKAogICAgeCA9ICJOb3JtYWxpemVkIGVucmljaG1lbnQgc2NvcmUiLAogICAgeSA9IE5VTEwsCiAgICB0aXRsZSA9ICJHTyBCUCBHU0VBIGZyb20gYWxsIHJhbmtlZCBnZW5lcyIKICApICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpNYWtlIGEgdm9sY2Fuby1zdHlsZSBwbG90IG9mIHRoZSBHTyBCUCBHU0VBIHJlc3VsdCwgdGhlbiBjb21wYXJlIGl0IHdpdGggdGhlCkhhbGxtYXJrIEdTRUEgdm9sY2FubyBwbG90LgoKYGBge3IgZ28tYnAtZ3NlYS12b2xjYW5vLCBldmFsPUZBTFNFLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD05fQojIFByYWN0aWNlOgojIFVzZSBFbmhhbmNlZFZvbGNhbm8oKSB3aXRoIGdvX2JwX2dzZWFfdGFiLgojCiMgUHV0IE5FUyBvbiB0aGUgeC1heGlzLgojIFB1dCBwYWRqIG9uIHRoZSB5LWF4aXMuCiMgVXNlIHBhdGh3YXkgbmFtZXMgYXMgbGFiZWxzLgojIFVzZSB0aGUgc2FtZSBjdXRvZmZzIGFzIGZvciB0aGUgSGFsbG1hcmsgcGxvdC4KIwojIENvbXBhcmUgdGhlIHR3byB2b2xjYW5vIHBsb3RzOgojIFdoaWNoIGJyb2FkIEhhbGxtYXJrIHBhdHRlcm5zIGFyZSBzdXBwb3J0ZWQgYnkgbWFueSBHTyBCUCB0ZXJtcz8KIyBXaGljaCBHTyBCUCB0ZXJtcyBhZGQgbW9yZSBkZXRhaWxlZCBiaW9sb2d5PwojIFdoaWNoIEdPIEJQIHRlcm1zIGxvb2sgcmVkdW5kYW50IHdpdGggZWFjaCBvdGhlcj8KYGBgCgojIyA5LiBGaW5hbCBHU1ZBIFRhc2sgV2l0aCBHTyBCUCBUZXJtcwoKQXMgYSBmaW5hbCB0YXNrLCBzZWxlY3QgdGhlIHRvcCAyMCBHTyBCUCBjYXRlZ29yaWVzIHdpdGggcG9zaXRpdmUgR1NFQSBzY29yZXMKYW5kIHRoZSB0b3AgMjAgR08gQlAgY2F0ZWdvcmllcyB3aXRoIG5lZ2F0aXZlIEdTRUEgc2NvcmVzLiBUaGVuIGNhbGN1bGF0ZQpzYW1wbGUtbGV2ZWwgR1NWQSBzY29yZXMgZm9yIHRoZXNlIEdPIEJQIGNhdGVnb3JpZXMgYW5kIG1ha2UgYSBoZWF0bWFwLgoKYGBge3IgZ28tYnAtZ3N2YS10YXNrLCBldmFsPUZBTFNFfQojIFByYWN0aWNlOgojIFN0YXJ0IGZyb20gZ29fYnBfZ3NlYV90YWIuCiMgU2VsZWN0IHRoZSAyMCBjYXRlZ29yaWVzIHdpdGggdGhlIGxhcmdlc3QgTkVTLgojIFNlbGVjdCB0aGUgMjAgY2F0ZWdvcmllcyB3aXRoIHRoZSBzbWFsbGVzdCBORVMuCiMgQ29tYmluZSB0aGVzZSBjYXRlZ29yaWVzIGludG8gb25lIHRhYmxlLgojCiMgVXNlIHRoZSBzZWxlY3RlZCBwYXRod2F5IG5hbWVzIHRvIHN1YnNldCBnb19icF9wYXRod2F5cy4KIyBSdW4gR1NWQSB3aXRoIGV4cHJlc3Npb25fbWF0cml4IGFuZCB0aGUgc2VsZWN0ZWQgR08gQlAgcGF0aHdheXMuCiMgTWFrZSBhIEhlYXRtYXAgd2l0aCBzYW1wbGVzIHNwbGl0IGJ5IGNvbmRpdGlvbi4KYGBgCgpPbmUgcG9zc2libGUgcmVzdWx0IGlzIHNob3duIGJlbG93LgoKYGBge3IgZ28tYnAtZ3N2YS1oZWF0bWFwLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9OX0KZ29fYnBfZ3N2YV90ZXJtcyA8LSBiaW5kX3Jvd3MoCiAgZ29fYnBfZ3NlYV90YWIgfD4KICAgIHNsaWNlX21heChORVMsIG4gPSAyMCksCiAgZ29fYnBfZ3NlYV90YWIgfD4KICAgIHNsaWNlX21pbihORVMsIG4gPSAyMCkKKSB8PgogIGRpc3RpbmN0KHBhdGh3YXksIC5rZWVwX2FsbCA9IFRSVUUpCgpnb19icF9nc3ZhX3BhdGh3YXlzIDwtIGdvX2JwX3BhdGh3YXlzW2dvX2JwX2dzdmFfdGVybXMkcGF0aHdheV0KCmdvX2JwX2dzdmFfcGFyYW1ldGVycyA8LSBnc3ZhUGFyYW0oCiAgZXhwcmVzc2lvbl9tYXRyaXgsCiAgZ29fYnBfZ3N2YV9wYXRod2F5cywKICBtaW5TaXplID0gMTAsCiAgbWF4U2l6ZSA9IDUwMCwKICBrY2RmID0gIkdhdXNzaWFuIgopCgpnb19icF9nc3ZhIDwtIGdzdmEoZ29fYnBfZ3N2YV9wYXJhbWV0ZXJzKQoKSGVhdG1hcCgKICBnb19icF9nc3ZhLAogIG5hbWUgPSAiR1NWQSIsCiAgY29sID0gY29sb3JSYW1wMihjKC0xLCAwLCAxKSwgYygic3RlZWxibHVlIiwgIndoaXRlIiwgImZpcmVicmljayIpKSwKICB0b3BfYW5ub3RhdGlvbiA9IHNhbXBsZV9hbm5vdGF0aW9uLAogIGNvbHVtbl9zcGxpdCA9IHNhbXBsZV9pbmZvJGNvbmRpdGlvbiwKICBjbHVzdGVyX2NvbHVtbnMgPSBGQUxTRSwKICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKGZvbnRzaXplID0gNiksCiAgY29sdW1uX25hbWVzX2dwID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IDkpCikKYGBgCgpTYXZlIHRoZSBHTyBCUCB0YWJsZXM6CgpgYGB7ciBzYXZlLWdvLWJwfQp3cml0ZV90c3YoZ29fYnBfb3JhX3RhYiwgIk9SQV9HT19CUF9tc2lnZGIudHN2IikKd3JpdGVfeGxzeChnb19icF9vcmFfdGFiLCAiT1JBX0dPX0JQX21zaWdkYi54bHN4IikKCndyaXRlX3Rzdihnb19icF9nc2VhX3RhYiwgIkdTRUFfR09fQlBfbXNpZ2RiLnRzdiIpCndyaXRlX3hsc3goZ29fYnBfZ3NlYV90YWIsICJHU0VBX0dPX0JQX21zaWdkYi54bHN4IikKCmdvX2JwX2dzdmFfdGFiIDwtIGdvX2JwX2dzdmEgfD4KICBhcy5kYXRhLmZyYW1lKCkgfD4KICByb3duYW1lc190b19jb2x1bW4oImdvX2JwIikKCndyaXRlX3Rzdihnb19icF9nc3ZhX3RhYiwgIkdTVkFfR09fQlBfdG9wX0dTRUFfc2FtcGxlX3Njb3Jlcy50c3YiKQp3cml0ZV94bHN4KGdvX2JwX2dzdmFfdGFiLCAiR1NWQV9HT19CUF90b3BfR1NFQV9zYW1wbGVfc2NvcmVzLnhsc3giKQpgYGAKCiMjIDEwLiBGaW5hbCBJbnRlcnByZXRhdGlvbgoKVXNlIHRoZSByZXN1bHQgdGFibGVzIGFuZCBwbG90cyB0byB3cml0ZSBhIHNob3J0IGludGVycHJldGF0aW9uLgoKQW5zd2VyIHRoZXNlIHF1ZXN0aW9uczoKCjEuIFdoaWNoIGJyb2FkIEhhbGxtYXJrIHBhdGh3YXlzIGNoYW5nZSBhZnRlciBNRUsgaW5oaWJpdG9yIHRyZWF0bWVudD8KMi4gV2hpY2ggR08gQmlvbG9naWNhbCBQcm9jZXNzIHRoZW1lcyBzdXBwb3J0IHRoYXQgaW50ZXJwcmV0YXRpb24/CjMuIEFyZSB0aGUgc3Ryb25nZXN0IGNoYW5nZXMgbW9zdGx5IGhpZ2hlciBvciBsb3dlciBpbiBNRUtpPwo0LiBEbyB0aGUgR1NWQSBzYW1wbGUtbGV2ZWwgc2NvcmVzIHNlcGFyYXRlIGNvbnRyb2wgYW5kIE1FS2kgc2FtcGxlcz8KNS4gRG8gdGhlIHNlbGVjdGVkIEdPIEJQIEdTVkEgc2NvcmVzIHNob3cgdGhlIHNhbWUgZGlyZWN0aW9uIGFzIEdPIEJQIEdTRUE/CjYuIFdoaWNoIGNvbmNsdXNpb25zIGFyZSByb2J1c3QgYWNyb3NzIEVucmljaHIsIE9SQSwgR1NFQSwgYW5kIEdTVkE/CjcuIFdoaWNoIGNvbmNsdXNpb25zIGRlcGVuZCBzdHJvbmdseSBvbiB0aGUgZGlmZmVyZW50aWFsLWdlbmUgY3V0b2ZmPwo=