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.

1. Load DESeq2 Results

res <- read_tsv("DESeq_result_full.tsv", show_col_types = FALSE)

res

The important columns are:

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:

  1. Which broad Hallmark pathways change after MEK inhibitor treatment?
  2. Which GO Biological Process themes support that interpretation?
  3. Are the strongest changes mostly higher or lower in MEKi?
  4. Do the GSVA sample-level scores separate control and MEKi samples?
  5. Do the selected GO BP GSVA scores show the same direction as GO BP GSEA?
  6. Which conclusions are robust across Enrichr, ORA, GSEA, and GSVA?
  7. 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=