load libraries
Load DE results
de_file <- "1-LIBRA_Pseudobulk_Deseq2_LRT_filtered_on_mean.csv"
de <- read.csv(de_file, stringsAsFactors = FALSE)
head(de)
sample <- readRDS("../../0-Seurat_RDS_Final_OBJECT/All_samples_Merged_with_Renamed_Clusters_Cell_state-03-12-2025.rds")
Volcano plot
Malignant CD4 T cells(cell lines) vs normal CD4 T cells
library(dplyr)
library(EnhancedVolcano)
# Assuming you have a data frame named Malignant_CD4Tcells_vs_Normal_CD4Tcells
# Filter genes based on lowest p-values but include all genes
filtered_genes <- de %>%
arrange(p_val_adj, desc(abs(avg_logFC)))
# Create the EnhancedVolcano plot with the filtered data
EnhancedVolcano(
filtered_genes,
lab = ifelse(filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_logFC) >= 1.0, filtered_genes$gene, NA),
x = "avg_logFC",
y = "p_val_adj",
title = "Malignant CD4 T cells(cell lines) vs normal CD4 T cells",
pCutoff = 0.05,
FCcutoff = 1.0,
legendPosition = 'right',
labCol = 'black',
labFace = 'bold',
boxedLabels = FALSE, # Set to FALSE to remove boxed labels
pointSize = 5.0,
labSize = 5.0,
col = c('grey70', 'black', 'grey', 'red'),
selectLab = filtered_genes$gene[filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_logFC) >= 1.0] # Only label significant genes
)

Prepare preranked list
for GSEA (fgsea)
# A1. IMPORTANT: Re-calculate ranking statistic using P-value (Not P-adj)
# This improves granularity and prevents ties
de <- de %>% mutate(signed_stat = avg_logFC * (-log10(p_val + 1e-300)))
# B. Create Rank Vector
ranks <- de %>% arrange(desc(signed_stat)) %>% distinct(gene, .keep_all=TRUE)
ranks_vec <- ranks$signed_stat; names(ranks_vec) <- ranks$gene
ranks_vec <- ranks_vec[!is.na(names(ranks_vec)) & !duplicated(names(ranks_vec))]
# Load MSigDB gene sets
# Hallmark
msig_h <- msigdbr(species = "Homo sapiens", category = "H") %>%
dplyr::select(gs_name, gene_symbol)
pathways_h <- split(msig_h$gene_symbol, msig_h$gs_name)
# C2 canonical pathways: KEGG + Reactome
msig_c2 <- msigdbr(species = "Homo sapiens", category = "C2") %>%
dplyr::select(gs_name, gs_subcollection, gene_symbol)
# KEGG - choose Medicus or Legacy
msig_kegg <- msigdbr(
species = "Homo sapiens",
category = "C2",
subcategory = "CP:KEGG_LEGACY" # or "CP:KEGG_LEGACY"
) %>%
select(gs_name, gene_symbol)
pathways_kegg <- split(msig_kegg$gene_symbol, msig_kegg$gs_name)
# Reactome
msig_react <- msig_c2 %>% filter(gs_subcollection == "CP:REACTOME") %>% select(gs_name, gene_symbol)
pathways_react <- split(msig_react$gene_symbol, msig_react$gs_name)
# GO Biological Process
msig_bp <- msigdbr(species = "Homo sapiens", category = "C5", subcategory = "BP") %>%
dplyr::select(gs_name, gene_symbol)
pathways_bp <- split(msig_bp$gene_symbol, msig_bp$gs_name)
# fgsea function
run_fgsea <- function(pathways, ranks_vec, label){
set.seed(42)
fg <- fgseaMultilevel(pathways = pathways, stats = ranks_vec)
fg <- as_tibble(fg) %>%
arrange(padj) %>%
mutate(
dataset = label,
leadingEdgeCount = lengths(leadingEdge),
leadingEdge = sapply(leadingEdge, function(x) paste(x, collapse = ";"))
) %>%
dplyr::select(dataset, pathway, NES, padj, pval, size, leadingEdge, leadingEdgeCount)
#write.csv(fg, paste0("fgsea_", label, "_results.csv"), row.names = FALSE)
return(fg)
}
# Run GSEA for each dataset
fg_h <- run_fgsea(pathways_h, ranks_vec, "hallmark")
fg_kegg <- run_fgsea(pathways_kegg, ranks_vec, "kegg")
fg_react <- run_fgsea(pathways_react, ranks_vec, "reactome")
fg_bp <- run_fgsea(pathways_bp, ranks_vec, "go_bp")
Define Exclusion List
(Strict)
# Expanded Proliferation Keywords
# STRICT EXCLUSION LIST (Updated)
prolif_terms <- c(
"CELL_CYCLE", "MITOTIC", "G2M", "E2F", "SPINDLE",
"CHROMOSOME", "DNA_REPLICATION", "NUCLEAR_DIVISION",
"ORGANELLE_FISSION", "KINETOCHORE", "CENTROSOME",
"REPLICATION", "SEGREGATION", "DIVISION", "M_PHASE",
"KINESINS", "MEIOSIS", "OOCYTE",
"MICROTUBULE", "CYTOSKELETON", "TRAFFIC", "GOLGI", "CYCLIN",
"RECOMBINATION", "REPAIR", "REPLICATIVE", "POLO_LIKE"
)
DATA PREPARATION
FUNCTION
prepare_data <- function(fg_tbl, topN = 5, exclude_prolif = FALSE) {
if (exclude_prolif) {
fg_tbl <- fg_tbl %>% filter(!grepl(paste(prolif_terms, collapse = "|"), pathway, ignore.case = TRUE))
}
# Get top N up and down per database
fg_plot <- bind_rows(
fg_tbl %>% filter(dataset == "hallmark") %>% { bind_rows(slice_max(., NES, n = topN), slice_min(., NES, n = topN)) },
fg_tbl %>% filter(dataset == "kegg") %>% { bind_rows(slice_max(., NES, n = topN), slice_min(., NES, n = topN)) },
fg_tbl %>% filter(dataset == "reactome") %>% { bind_rows(slice_max(., NES, n = topN), slice_min(., NES, n = topN)) },
fg_tbl %>% filter(dataset == "go_bp") %>% { bind_rows(slice_max(., NES, n = topN), slice_min(., NES, n = topN)) }
)
# Format Labels
fg_plot %>%
mutate(
db_prefix = case_when(
dataset == "hallmark" ~ "HALLMARK",
dataset == "kegg" ~ "KEGG",
dataset == "reactome" ~ "REACTOME",
dataset == "go_bp" ~ "GOBP"
),
clean_pathway = gsub("^HALLMARK_|^KEGG_|^REACTOME_|^GOBP_", "", pathway),
plot_label = paste0(db_prefix, "_", clean_pathway)
) %>%
arrange(NES) %>%
mutate(plot_label = factor(plot_label, levels = unique(plot_label)))
}
PLOTTING FUNCTION
create_plot <- function(data, color_var, color_label, title_text) {
ggplot(data, aes(x = NES, y = plot_label)) +
geom_point(aes(shape = dataset, size = leadingEdgeCount, color = !!sym(color_var)), alpha = 0.9) +
geom_vline(xintercept = 0, linetype = "solid", color = "gray80", linewidth = 0.5) +
scale_color_gradientn(
colors = c("red", "orange", "blue"),
trans = "log10",
name = color_label,
guide = guide_colorbar(reverse = TRUE)
) +
scale_shape_manual(
values = c("hallmark" = 17, "kegg" = 15, "reactome" = 3, "go_bp" = 16),
guide = "none"
) +
scale_size_continuous(range = c(3, 8), name = "Leading edge genes") +
theme_minimal() +
labs(x = "Normalized Enrichment Score (NES)", y = NULL, title = title_text) +
theme(
axis.text.y = element_text(size = 14, face = "bold", color = "black"),
# X-axis labels (Numbers) - BOLD & BIGGER
axis.text.x = element_text(size = 10, face = "bold", color = "black"),
# X-axis TITLE (The Text "Normalized Enrichment Score...") - BOLD
axis.title.x = element_text(size = 14, face = "bold", color = "black", margin = margin(t = 10)),
# Plot Title
plot.title = element_text(face = "bold", size = 13, hjust = 0.5),
# Legend
legend.position = "right",
legend.box = "vertical",
legend.title = element_text(face = "bold", size = 10),
panel.grid.major.y = element_line(color = "gray95")
)
}
GENERATE 4 PLOTS
# A) All Pathways (padj)
df_all <- prepare_data(fg_all, exclude_prolif = FALSE)
p1 <- create_plot(df_all, "padj", "FDR (padj)", "Global Pathway Alterations (Malignant vs. Normal CD4+)")
ggsave("Fig1_All_padj.png", p1, width = 16, height = 8, dpi = 300)
ggsave("Fig1_All_padj.pdf", p1, width = 16, height = 8)
# B) All Pathways (p-value)
p2 <- create_plot(df_all, "pval", "P-value", "Nominally Significant Pathway Alterations")
ggsave("Fig2_All_pval.png", p2, width = 16, height = 8, dpi = 300)
ggsave("Fig2_All_pval.pdf", p2, width = 16, height = 8)
# C) Non-Proliferation (padj)
df_no_prolif <- prepare_data(fg_all, exclude_prolif = TRUE)
p3 <- create_plot(df_no_prolif, "padj", "FDR (padj)", "Functional & Immune Signatures (Non-Proliferative)")
ggsave("Fig3_NonProlif_padj.png", p3, width = 16, height = 8, dpi = 300)
ggsave("Fig3_NonProlif_padj.pdf", p3, width = 16, height = 8)
# D) Non-Proliferation (p-value)
p4 <- create_plot(df_no_prolif, "pval", "P-value", "Exploratory Functional Signatures (Non-Proliferative)")
ggsave("Fig4_NonProlif_pval.png", p4, width = 16, height = 8, dpi = 300)
ggsave("Fig4_NonProlif_pval.pdf", p4, width = 16, height = 8)
print("Created 4 figures: Fig1..Fig4 (.png and .pdf)")
[1] "Created 4 figures: Fig1..Fig4 (.png and .pdf)"
p1

p2

p3

p4

Enrichment map_cnetplot
for leading-edge genes
#### Enrichment map / cnetplot for leading-edge genes ####
library(clusterProfiler)
library(enrichplot)
library(tidyverse)
# ---- Function ----
make_cnet_emap <- function(fgsea_res,
pathways,
dataset_name,
padj_cutoff = 0.25,
topN_fallback = 10,
showCategory = 15,
fold_change_vec = NULL,
save_plots = TRUE) {
message("Processing dataset: ", dataset_name)
# 1) Try to filter by padj
leading_list <- fgsea_res %>%
filter(padj < padj_cutoff) %>%
select(pathway, leadingEdge) %>%
mutate(leadingEdge = map(leadingEdge, as.character)) %>%
deframe()
# 2) Fallback: if too few, take topN pathways by NES
if (length(leading_list) < 2) {
message("⚠ No pathways pass padj <", padj_cutoff,
" for ", dataset_name, ". Using top ", topN_fallback, " pathways by NES.")
leading_list <- fgsea_res %>%
arrange(padj, desc(abs(NES))) %>%
slice_head(n = topN_fallback) %>%
select(pathway, leadingEdge) %>%
mutate(leadingEdge = map(leadingEdge, as.character)) %>%
deframe()
}
if (length(leading_list) < 2) {
message("❌ Still too few pathways for ", dataset_name, " — skipping.")
return(NULL)
}
# TERM2GENE conversion
lead_term2gene <- enframe(leading_list, name = "TERM", value = "GENE") %>%
unnest(cols = c(GENE))
# Union of leading-edge genes
union_leading_genes <- unique(unlist(leading_list))
if (length(union_leading_genes) < 2) {
message("❌ Too few leading-edge genes for ", dataset_name, " — skipping.")
return(NULL)
}
# Run enrichment (very relaxed min/max sizes)
enr <- enricher(
union_leading_genes,
TERM2GENE = lead_term2gene,
minGSSize = 1,
maxGSSize = 5000
)
if (is.null(enr) || nrow(enr) == 0) {
message("❌ No enrichment result for ", dataset_name)
return(NULL)
}
# ---- cnetplot ----
cnet <- cnetplot(
enr,
foldChange = fold_change_vec,
showCategory = showCategory,
circular = FALSE,
colorEdge = TRUE
) + ggtitle(paste("Cnetplot —", dataset_name))
if (save_plots) {
ggsave(
paste0("cnetplot_leadingEdge_", dataset_name, ".png"),
cnet,
width = 12, height = 8, dpi = 300
)
}
# ---- emapplot ----
emap <- pairwise_termsim(enr)
emap_plot <- emapplot(emap, showCategory = showCategory) +
ggtitle(paste("Enrichment Map —", dataset_name))
if (save_plots) {
ggsave(
paste0("emapplot_leadingEdge_", dataset_name, ".png"),
emap_plot,
width = 12, height = 8, dpi = 300
)
}
return(list(enrich_res = enr, cnet = cnet, emap = emap_plot))
}
# ---- Run for all datasets ----
# Optional: fold-change vector from DE results
fold_change_vec <- setNames(de$avg_logFC, de$gene)
res_h <- make_cnet_emap(fg_h, pathways_h, "Hallmark", fold_change_vec = fold_change_vec)
res_kegg <- make_cnet_emap(fg_kegg, pathways_kegg, "KEGG", fold_change_vec = fold_change_vec)
res_react <- make_cnet_emap(fg_react, pathways_react, "Reactome", fold_change_vec = fold_change_vec)
res_bp <- make_cnet_emap(fg_bp, pathways_bp, "GO_BP", fold_change_vec = fold_change_vec)
message("✅ Finished Cnet/Enrichment map plots for all datasets")
Enrichment map_cnetplot
for leading-edge genes
#### Enrichment map / cnetplot for leading-edge genes ####
library(clusterProfiler)
library(enrichplot)
library(tidyverse)
# ---- Function ----
make_cnet_emap <- function(fgsea_res,
pathways,
dataset_name,
padj_cutoff = 0.25,
topN_fallback = 10,
showCategory = 15,
fold_change_vec = NULL,
save_plots = TRUE) {
message("Processing dataset: ", dataset_name)
# 1) Try to filter by padj
leading_list <- fgsea_res %>%
filter(padj < padj_cutoff) %>%
select(pathway, leadingEdge) %>%
mutate(leadingEdge = map(leadingEdge, as.character)) %>%
deframe()
# 2) Fallback: if too few, take topN pathways by NES
if (length(leading_list) < 2) {
message("⚠ No pathways pass padj <", padj_cutoff,
" for ", dataset_name, ". Using top ", topN_fallback, " pathways by NES.")
leading_list <- fgsea_res %>%
arrange(padj, desc(abs(NES))) %>%
slice_head(n = topN_fallback) %>%
select(pathway, leadingEdge) %>%
mutate(leadingEdge = map(leadingEdge, as.character)) %>%
deframe()
}
if (length(leading_list) < 2) {
message("❌ Still too few pathways for ", dataset_name, " — skipping.")
return(NULL)
}
# TERM2GENE conversion
lead_term2gene <- enframe(leading_list, name = "TERM", value = "GENE") %>%
unnest(cols = c(GENE))
# Union of leading-edge genes
union_leading_genes <- unique(unlist(leading_list))
if (length(union_leading_genes) < 2) {
message("❌ Too few leading-edge genes for ", dataset_name, " — skipping.")
return(NULL)
}
# Run enrichment (very relaxed min/max sizes)
enr <- enricher(
union_leading_genes,
TERM2GENE = lead_term2gene,
minGSSize = 1,
maxGSSize = 5000
)
if (is.null(enr) || nrow(enr) == 0) {
message("❌ No enrichment result for ", dataset_name)
return(NULL)
}
# ---- cnetplot ----
cnet <- cnetplot(
enr,
foldChange = fold_change_vec,
showCategory = showCategory,
circular = FALSE,
colorEdge = TRUE
) + ggtitle(paste("Cnetplot —", dataset_name))
if (save_plots) {
ggsave(
paste0("cnetplot_leadingEdge_", dataset_name, ".png"),
cnet,
width = 12, height = 8, dpi = 300
)
}
# ---- emapplot ----
emap <- pairwise_termsim(enr)
emap_plot <- emapplot(emap, showCategory = showCategory) +
ggtitle(paste("Enrichment Map —", dataset_name))
if (save_plots) {
ggsave(
paste0("emapplot_leadingEdge_", dataset_name, ".png"),
emap_plot,
width = 12, height = 8, dpi = 300
)
}
return(list(enrich_res = enr, cnet = cnet, emap = emap_plot))
}
# ---- Run for all datasets ----
# Optional: fold-change vector from DE results
fold_change_vec <- setNames(de$avg_logFC, de$gene)
res_h <- make_cnet_emap(fg_h, pathways_h, "Hallmark", fold_change_vec = fold_change_vec)
res_kegg <- make_cnet_emap(fg_kegg, pathways_kegg, "KEGG", fold_change_vec = fold_change_vec)
res_react <- make_cnet_emap(fg_react, pathways_react, "Reactome", fold_change_vec = fold_change_vec)
res_bp <- make_cnet_emap(fg_bp, pathways_bp, "GO_BP", fold_change_vec = fold_change_vec)
message("✅ Finished Cnet/Enrichment map plots for all datasets")
Pathways Expression on
UMAP
library(Seurat)
library(cowplot)
library(SeuratObject)
topPathways <- fg_h[["pathway"]] %>% head(10)
titles <- sub("HALLMARK_", "", topPathways)
# Make sure topPathways are in the Hallmark list
topPathways <- intersect(topPathways, names(pathways_h))
# Titles for plots
titles <- sub("HALLMARK_", "", topPathways)
obj <- sample
# Use SCT assay explicitly
DefaultAssay(obj) <- "SCT"
# Run plotCoregulationProfileReduction
ps <- plotCoregulationProfileReduction(
pathways_h[topPathways],
obj,
assay = "SCT", # explicitly use SCT assay
title = titles,
reduction = "umap"
)
# Combine plots
cowplot::plot_grid(plotlist = ps[1:length(ps)], ncol = 2)

NA
NA
NA
Pathways Expression
on UMAP
library(dplyr)
library(Seurat)
library(cowplot)
library(SeuratObject)
# 1️⃣ Sort the Hallmark FGSEA table by NES
fg_h_sorted <- fg_h %>% arrange(desc(NES)) # descending NES
# 2️⃣ Get top 10 up and top 10 down pathways
top_up <- fg_h_sorted$pathway[1:10] # top 10 up by NES
top_down <- fg_h_sorted %>% arrange(NES) %>% slice(1:10) %>% pull(pathway) # top 10 down
# 3️⃣ Combine up and down
topPathways <- c(top_up, top_down)
# Keep only pathways that exist in the Hallmark gene sets
topPathways <- intersect(topPathways, names(pathways_h))
# Titles for plotting
titles <- sub("HALLMARK_", "", topPathways)
# Use SCT assay explicitly
DefaultAssay(obj) <- "SCT"
# 4️⃣ Run co-regulation UMAP
ps <- plotCoregulationProfileReduction(
pathways_h[topPathways],
obj,
assay = "SCT",
title = titles,
reduction = "umap"
)
# 5️⃣ Combine plots in a grid
cowplot::plot_grid(plotlist = ps[1:length(ps)], ncol = 4)

NA
NA
Pathways Expression
on UMAP
library(Seurat)
library(cowplot)
library(SeuratObject)
topPathways <- fg_kegg[["pathway"]] %>% head(10)
titles <- sub("KEGG_", "", topPathways)
# Make sure topPathways are in the Hallmark list
topPathways <- intersect(topPathways, names(pathways_kegg))
# Titles for plots
titles <- sub("KEGG_", "", topPathways)
# Use SCT assay explicitly
DefaultAssay(obj) <- "SCT"
# Run plotCoregulationProfileReduction
ps <- plotCoregulationProfileReduction(
pathways_kegg[topPathways],
obj,
assay = "SCT", # explicitly use SCT assay
title = titles,
reduction = "umap"
)
# Combine plots
cowplot::plot_grid(plotlist = ps[1:length(ps)], ncol = 2)

NA
NA
NA
Pathways Expression
on UMAP
library(Seurat)
library(cowplot)
library(SeuratObject)
topPathways <- fg_react[["pathway"]] %>% head(10)
titles <- sub("REACTOME_", "", topPathways)
# Make sure topPathways are in the Hallmark list
topPathways <- intersect(topPathways, names(pathways_react))
# Titles for plots
titles <- sub("REACTOME_", "", topPathways)
# Use SCT assay explicitly
DefaultAssay(obj) <- "SCT"
# Run plotCoregulationProfileReduction
ps <- plotCoregulationProfileReduction(
pathways_react[topPathways],
obj,
assay = "SCT", # explicitly use SCT assay
title = titles,
reduction = "umap"
)
# Combine plots
cowplot::plot_grid(plotlist = ps[1:length(ps)], ncol = 2)

NA
NA
NA
Pathways Expression
on UMAP
# Sort Reactome FGSEA table by NES
fg_react_sorted <- fg_react %>% arrange(desc(NES))
# Top 10 up and top 10 down
top_up <- fg_react_sorted$pathway[1:10]
top_down <- fg_react_sorted %>% arrange(NES) %>% slice(1:10) %>% pull(pathway)
# Combine and keep only pathways present in Reactome gene sets
topPathways <- intersect(c(top_up, top_down), names(pathways_react))
# Titles
titles <- sub("REACTOME_", "", topPathways)
# SCT assay
DefaultAssay(obj) <- "SCT"
# Run co-regulation UMAP
ps <- plotCoregulationProfileReduction(
pathways_react[topPathways],
obj,
assay = "SCT",
title = titles,
reduction = "umap"
)
# Combine plots
cowplot::plot_grid(plotlist = ps[1:length(ps)], ncol = 2)

NA
NA
NA
Pathways Expression
on UMAP
library(Seurat)
library(cowplot)
library(SeuratObject)
topPathways <- fg_bp[["pathway"]] %>% head(10)
titles <- sub("GO_", "", topPathways)
# Make sure topPathways are in the Hallmark list
topPathways <- intersect(topPathways, names(pathways_bp))
# Titles for plots
titles <- sub("GO_", "", topPathways)
# Use SCT assay explicitly
DefaultAssay(obj) <- "SCT"
# Run plotCoregulationProfileReduction
ps <- plotCoregulationProfileReduction(
pathways_bp[topPathways],
obj,
assay = "SCT", # explicitly use SCT assay
title = titles,
reduction = "umap"
)
# Combine plots
cowplot::plot_grid(plotlist = ps[1:length(ps)], ncol = 2)

NA
NA
NA
Pathways Expression
on UMAP
# Sort GO:BP FGSEA table by NES
fg_bp_sorted <- fg_bp %>% arrange(desc(NES))
# Top 10 up and top 10 down
top_up <- fg_bp_sorted$pathway[1:10]
top_down <- fg_bp_sorted %>% arrange(NES) %>% slice(1:10) %>% pull(pathway)
# Combine and keep only pathways present in GO:BP gene sets
topPathways <- intersect(c(top_up, top_down), names(pathways_bp))
# Titles
titles <- sub("GO_BP_", "", topPathways)
# SCT assay
DefaultAssay(obj) <- "SCT"
# Run co-regulation UMAP
ps <- plotCoregulationProfileReduction(
pathways_bp[topPathways],
obj,
assay = "SCT",
title = titles,
reduction = "umap"
)
# Combine plots
cowplot::plot_grid(plotlist = ps[1:length(ps)], ncol = 2)

NA
NA
LS0tCnRpdGxlOiAiZmdzZWEgQ29tcGxldGUgQW5hbHlzaXMgZm9yIE1hbnVTY3JpcHRfRmViMjAyNi1wX3ZhbF91c2VkX3JhbmtpbmciCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgdGhlbWU6IGpvdXJuYWwKLS0tCgoKIyBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KCiMgMS4gTG9hZCBsaWJyYXJpZXMKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZmdzZWEpCmxpYnJhcnkobXNpZ2RicikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShTZXVyYXRPYmplY3QpCmxpYnJhcnkoY293cGxvdCkKYGBgCgojIExvYWQgREUgcmVzdWx0cwpgYGB7ciB9CgpkZV9maWxlIDwtICIxLUxJQlJBX1BzZXVkb2J1bGtfRGVzZXEyX0xSVF9maWx0ZXJlZF9vbl9tZWFuLmNzdiIKZGUgPC0gcmVhZC5jc3YoZGVfZmlsZSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpoZWFkKGRlKQoKCnNhbXBsZSA8LSByZWFkUkRTKCIuLi8uLi8wLVNldXJhdF9SRFNfRmluYWxfT0JKRUNUL0FsbF9zYW1wbGVzX01lcmdlZF93aXRoX1JlbmFtZWRfQ2x1c3RlcnNfQ2VsbF9zdGF0ZS0wMy0xMi0yMDI1LnJkcyIpCgpgYGAKIyMgVm9sY2FubyBwbG90IE1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMKYGBge3IsIGZpZy5oZWlnaHQ9IDEyLCBmaWcud2lkdGg9IDE0fQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCiMgQXNzdW1pbmcgeW91IGhhdmUgYSBkYXRhIGZyYW1lIG5hbWVkIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscwojIEZpbHRlciBnZW5lcyBiYXNlZCBvbiBsb3dlc3QgcC12YWx1ZXMgYnV0IGluY2x1ZGUgYWxsIGdlbmVzCmZpbHRlcmVkX2dlbmVzIDwtIGRlICU+JQogIGFycmFuZ2UocF92YWxfYWRqLCBkZXNjKGFicyhhdmdfbG9nRkMpKSkKCiMgQ3JlYXRlIHRoZSBFbmhhbmNlZFZvbGNhbm8gcGxvdCB3aXRoIHRoZSBmaWx0ZXJlZCBkYXRhCkVuaGFuY2VkVm9sY2FubygKICBmaWx0ZXJlZF9nZW5lcywgCiAgbGFiID0gaWZlbHNlKGZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2dGQykgPj0gMS4wLCBmaWx0ZXJlZF9nZW5lcyRnZW5lLCBOQSksCiAgeCA9ICJhdmdfbG9nRkMiLCAKICB5ID0gInBfdmFsX2FkaiIsCiAgdGl0bGUgPSAiTWFsaWduYW50IENENCBUIGNlbGxzKGNlbGwgbGluZXMpIHZzIG5vcm1hbCBDRDQgVCBjZWxscyIsCiAgcEN1dG9mZiA9IDAuMDUsCiAgRkNjdXRvZmYgPSAxLjAsCiAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLCAKICBsYWJDb2wgPSAnYmxhY2snLAogIGxhYkZhY2UgPSAnYm9sZCcsCiAgYm94ZWRMYWJlbHMgPSBGQUxTRSwgICMgU2V0IHRvIEZBTFNFIHRvIHJlbW92ZSBib3hlZCBsYWJlbHMKICBwb2ludFNpemUgPSA1LjAsCiAgbGFiU2l6ZSA9IDUuMCwKICBjb2wgPSBjKCdncmV5NzAnLCAnYmxhY2snLCAnZ3JleScsICdyZWQnKSwgCiAgc2VsZWN0TGFiID0gZmlsdGVyZWRfZ2VuZXMkZ2VuZVtmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMC4wNSAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nRkMpID49IDEuMF0gICMgT25seSBsYWJlbCBzaWduaWZpY2FudCBnZW5lcwopCmBgYAoKCiMgUHJlcGFyZSBwcmVyYW5rZWQgbGlzdCBmb3IgR1NFQSAoZmdzZWEpCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9IDE2fQoKIyBBMS4gSU1QT1JUQU5UOiBSZS1jYWxjdWxhdGUgcmFua2luZyBzdGF0aXN0aWMgdXNpbmcgUC12YWx1ZSAoTm90IFAtYWRqKQojIFRoaXMgaW1wcm92ZXMgZ3JhbnVsYXJpdHkgYW5kIHByZXZlbnRzIHRpZXMKZGUgPC0gZGUgJT4lIG11dGF0ZShzaWduZWRfc3RhdCA9IGF2Z19sb2dGQyAqICgtbG9nMTAocF92YWwgKyAxZS0zMDApKSkKCiMgQi4gQ3JlYXRlIFJhbmsgVmVjdG9yCnJhbmtzIDwtIGRlICU+JSBhcnJhbmdlKGRlc2Moc2lnbmVkX3N0YXQpKSAlPiUgZGlzdGluY3QoZ2VuZSwgLmtlZXBfYWxsPVRSVUUpCnJhbmtzX3ZlYyA8LSByYW5rcyRzaWduZWRfc3RhdDsgbmFtZXMocmFua3NfdmVjKSA8LSByYW5rcyRnZW5lCnJhbmtzX3ZlYyA8LSByYW5rc192ZWNbIWlzLm5hKG5hbWVzKHJhbmtzX3ZlYykpICYgIWR1cGxpY2F0ZWQobmFtZXMocmFua3NfdmVjKSldCgojIExvYWQgTVNpZ0RCIGdlbmUgc2V0cwojIEhhbGxtYXJrCm1zaWdfaCA8LSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiSCIpICU+JQogIGRwbHlyOjpzZWxlY3QoZ3NfbmFtZSwgZ2VuZV9zeW1ib2wpCnBhdGh3YXlzX2ggPC0gc3BsaXQobXNpZ19oJGdlbmVfc3ltYm9sLCBtc2lnX2gkZ3NfbmFtZSkKCiMgQzIgY2Fub25pY2FsIHBhdGh3YXlzOiBLRUdHICsgUmVhY3RvbWUKbXNpZ19jMiA8LSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiQzIiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGdzX25hbWUsIGdzX3N1YmNvbGxlY3Rpb24sIGdlbmVfc3ltYm9sKQoKIyBLRUdHIC0gY2hvb3NlIE1lZGljdXMgb3IgTGVnYWN5Cm1zaWdfa2VnZyA8LSBtc2lnZGJyKAogIHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwKICBjYXRlZ29yeSA9ICJDMiIsCiAgc3ViY2F0ZWdvcnkgPSAiQ1A6S0VHR19MRUdBQ1kiICAgIyBvciAiQ1A6S0VHR19MRUdBQ1kiCikgJT4lCiAgc2VsZWN0KGdzX25hbWUsIGdlbmVfc3ltYm9sKQoKcGF0aHdheXNfa2VnZyA8LSBzcGxpdChtc2lnX2tlZ2ckZ2VuZV9zeW1ib2wsIG1zaWdfa2VnZyRnc19uYW1lKQoKIyBSZWFjdG9tZQptc2lnX3JlYWN0IDwtIG1zaWdfYzIgJT4lIGZpbHRlcihnc19zdWJjb2xsZWN0aW9uID09ICJDUDpSRUFDVE9NRSIpICU+JSBzZWxlY3QoZ3NfbmFtZSwgZ2VuZV9zeW1ib2wpCnBhdGh3YXlzX3JlYWN0IDwtIHNwbGl0KG1zaWdfcmVhY3QkZ2VuZV9zeW1ib2wsIG1zaWdfcmVhY3QkZ3NfbmFtZSkKCiMgR08gQmlvbG9naWNhbCBQcm9jZXNzCm1zaWdfYnAgPC0gbXNpZ2RicihzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIsIGNhdGVnb3J5ID0gIkM1Iiwgc3ViY2F0ZWdvcnkgPSAiQlAiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGdzX25hbWUsIGdlbmVfc3ltYm9sKQpwYXRod2F5c19icCA8LSBzcGxpdChtc2lnX2JwJGdlbmVfc3ltYm9sLCBtc2lnX2JwJGdzX25hbWUpCgojIGZnc2VhIGZ1bmN0aW9uCnJ1bl9mZ3NlYSA8LSBmdW5jdGlvbihwYXRod2F5cywgcmFua3NfdmVjLCBsYWJlbCl7CiAgc2V0LnNlZWQoNDIpCiAgZmcgPC0gZmdzZWFNdWx0aWxldmVsKHBhdGh3YXlzID0gcGF0aHdheXMsIHN0YXRzID0gcmFua3NfdmVjKQogIGZnIDwtIGFzX3RpYmJsZShmZykgJT4lCiAgICBhcnJhbmdlKHBhZGopICU+JQogICAgbXV0YXRlKAogICAgICBkYXRhc2V0ID0gbGFiZWwsCiAgICAgIGxlYWRpbmdFZGdlQ291bnQgPSBsZW5ndGhzKGxlYWRpbmdFZGdlKSwKICAgICAgbGVhZGluZ0VkZ2UgPSBzYXBwbHkobGVhZGluZ0VkZ2UsIGZ1bmN0aW9uKHgpIHBhc3RlKHgsIGNvbGxhcHNlID0gIjsiKSkKICAgICkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGRhdGFzZXQsIHBhdGh3YXksIE5FUywgcGFkaiwgcHZhbCwgc2l6ZSwgbGVhZGluZ0VkZ2UsIGxlYWRpbmdFZGdlQ291bnQpCiAgI3dyaXRlLmNzdihmZywgcGFzdGUwKCJmZ3NlYV8iLCBsYWJlbCwgIl9yZXN1bHRzLmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKICByZXR1cm4oZmcpCn0KCiMgUnVuIEdTRUEgZm9yIGVhY2ggZGF0YXNldApmZ19oICAgICA8LSBydW5fZmdzZWEocGF0aHdheXNfaCwgcmFua3NfdmVjLCAiaGFsbG1hcmsiKQpmZ19rZWdnICA8LSBydW5fZmdzZWEocGF0aHdheXNfa2VnZywgcmFua3NfdmVjLCAia2VnZyIpCmZnX3JlYWN0IDwtIHJ1bl9mZ3NlYShwYXRod2F5c19yZWFjdCwgcmFua3NfdmVjLCAicmVhY3RvbWUiKQpmZ19icCAgICA8LSBydW5fZmdzZWEocGF0aHdheXNfYnAsIHJhbmtzX3ZlYywgImdvX2JwIikKCmBgYAoKIyBEZWZpbmUgRXhjbHVzaW9uIExpc3QgKFN0cmljdCkKYGBge3IgfQoKIyBFeHBhbmRlZCBQcm9saWZlcmF0aW9uIEtleXdvcmRzCiMgU1RSSUNUIEVYQ0xVU0lPTiBMSVNUIChVcGRhdGVkKQpwcm9saWZfdGVybXMgPC0gYygKICAiQ0VMTF9DWUNMRSIsICJNSVRPVElDIiwgIkcyTSIsICJFMkYiLCAiU1BJTkRMRSIsIAogICJDSFJPTU9TT01FIiwgIkROQV9SRVBMSUNBVElPTiIsICJOVUNMRUFSX0RJVklTSU9OIiwKICAiT1JHQU5FTExFX0ZJU1NJT04iLCAiS0lORVRPQ0hPUkUiLCAiQ0VOVFJPU09NRSIsCiAgIlJFUExJQ0FUSU9OIiwgIlNFR1JFR0FUSU9OIiwgIkRJVklTSU9OIiwgIk1fUEhBU0UiLCAKICAiS0lORVNJTlMiLCAiTUVJT1NJUyIsICJPT0NZVEUiLCAKICAiTUlDUk9UVUJVTEUiLCAiQ1lUT1NLRUxFVE9OIiwgIlRSQUZGSUMiLCAiR09MR0kiLCAiQ1lDTElOIiwKICAiUkVDT01CSU5BVElPTiIsICJSRVBBSVIiLCAiUkVQTElDQVRJVkUiLCAiUE9MT19MSUtFIgopCmBgYAoKCiMgREFUQSBQUkVQQVJBVElPTiBGVU5DVElPTgpgYGB7ciwgZmlnLmhlaWdodD0gNiwgZmlnLndpZHRoPSAxMH0KcHJlcGFyZV9kYXRhIDwtIGZ1bmN0aW9uKGZnX3RibCwgdG9wTiA9IDUsIGV4Y2x1ZGVfcHJvbGlmID0gRkFMU0UpIHsKICBpZiAoZXhjbHVkZV9wcm9saWYpIHsKICAgIGZnX3RibCA8LSBmZ190YmwgJT4lIGZpbHRlcighZ3JlcGwocGFzdGUocHJvbGlmX3Rlcm1zLCBjb2xsYXBzZSA9ICJ8IiksIHBhdGh3YXksIGlnbm9yZS5jYXNlID0gVFJVRSkpCiAgfQoKICAjIEdldCB0b3AgTiB1cCBhbmQgZG93biBwZXIgZGF0YWJhc2UKICBmZ19wbG90IDwtIGJpbmRfcm93cygKICAgIGZnX3RibCAlPiUgZmlsdGVyKGRhdGFzZXQgPT0gImhhbGxtYXJrIikgJT4lIHsgYmluZF9yb3dzKHNsaWNlX21heCguLCBORVMsIG4gPSB0b3BOKSwgc2xpY2VfbWluKC4sIE5FUywgbiA9IHRvcE4pKSB9LAogICAgZmdfdGJsICU+JSBmaWx0ZXIoZGF0YXNldCA9PSAia2VnZyIpICU+JSB7IGJpbmRfcm93cyhzbGljZV9tYXgoLiwgTkVTLCBuID0gdG9wTiksIHNsaWNlX21pbiguLCBORVMsIG4gPSB0b3BOKSkgfSwKICAgIGZnX3RibCAlPiUgZmlsdGVyKGRhdGFzZXQgPT0gInJlYWN0b21lIikgJT4lIHsgYmluZF9yb3dzKHNsaWNlX21heCguLCBORVMsIG4gPSB0b3BOKSwgc2xpY2VfbWluKC4sIE5FUywgbiA9IHRvcE4pKSB9LAogICAgZmdfdGJsICU+JSBmaWx0ZXIoZGF0YXNldCA9PSAiZ29fYnAiKSAlPiUgeyBiaW5kX3Jvd3Moc2xpY2VfbWF4KC4sIE5FUywgbiA9IHRvcE4pLCBzbGljZV9taW4oLiwgTkVTLCBuID0gdG9wTikpIH0KICApCgogICMgRm9ybWF0IExhYmVscwogIGZnX3Bsb3QgJT4lCiAgICBtdXRhdGUoCiAgICAgIGRiX3ByZWZpeCA9IGNhc2Vfd2hlbigKICAgICAgICBkYXRhc2V0ID09ICJoYWxsbWFyayIgfiAiSEFMTE1BUksiLAogICAgICAgIGRhdGFzZXQgPT0gImtlZ2ciIH4gIktFR0ciLAogICAgICAgIGRhdGFzZXQgPT0gInJlYWN0b21lIiB+ICJSRUFDVE9NRSIsCiAgICAgICAgZGF0YXNldCA9PSAiZ29fYnAiIH4gIkdPQlAiCiAgICAgICksCiAgICAgIGNsZWFuX3BhdGh3YXkgPSBnc3ViKCJeSEFMTE1BUktffF5LRUdHX3xeUkVBQ1RPTUVffF5HT0JQXyIsICIiLCBwYXRod2F5KSwKICAgICAgcGxvdF9sYWJlbCA9IHBhc3RlMChkYl9wcmVmaXgsICJfIiwgY2xlYW5fcGF0aHdheSkKICAgICkgJT4lCiAgICBhcnJhbmdlKE5FUykgJT4lCiAgICBtdXRhdGUocGxvdF9sYWJlbCA9IGZhY3RvcihwbG90X2xhYmVsLCBsZXZlbHMgPSB1bmlxdWUocGxvdF9sYWJlbCkpKQp9CgpgYGAKCiMgUExPVFRJTkcgRlVOQ1RJT04gCmBgYHtyLCBmaWcuaGVpZ2h0PSA2LCBmaWcud2lkdGg9IDEwfQpjcmVhdGVfcGxvdCA8LSBmdW5jdGlvbihkYXRhLCBjb2xvcl92YXIsIGNvbG9yX2xhYmVsLCB0aXRsZV90ZXh0KSB7CiAgZ2dwbG90KGRhdGEsIGFlcyh4ID0gTkVTLCB5ID0gcGxvdF9sYWJlbCkpICsKICAgIGdlb21fcG9pbnQoYWVzKHNoYXBlID0gZGF0YXNldCwgc2l6ZSA9IGxlYWRpbmdFZGdlQ291bnQsIGNvbG9yID0gISFzeW0oY29sb3JfdmFyKSksIGFscGhhID0gMC45KSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJzb2xpZCIsIGNvbG9yID0gImdyYXk4MCIsIGxpbmV3aWR0aCA9IDAuNSkgKwoKICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bigKICAgICAgY29sb3JzID0gYygicmVkIiwgIm9yYW5nZSIsICJibHVlIiksCiAgICAgIHRyYW5zID0gImxvZzEwIiwKICAgICAgbmFtZSA9IGNvbG9yX2xhYmVsLAogICAgICBndWlkZSA9IGd1aWRlX2NvbG9yYmFyKHJldmVyc2UgPSBUUlVFKQogICAgKSArCgogICAgc2NhbGVfc2hhcGVfbWFudWFsKAogICAgICB2YWx1ZXMgPSBjKCJoYWxsbWFyayIgPSAxNywgImtlZ2ciID0gMTUsICJyZWFjdG9tZSIgPSAzLCAiZ29fYnAiID0gMTYpLAogICAgICBndWlkZSA9ICJub25lIgogICAgKSArCgogICAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygzLCA4KSwgbmFtZSA9ICJMZWFkaW5nIGVkZ2UgZ2VuZXMiKSArCgogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGxhYnMoeCA9ICJOb3JtYWxpemVkIEVucmljaG1lbnQgU2NvcmUgKE5FUykiLCB5ID0gTlVMTCwgdGl0bGUgPSB0aXRsZV90ZXh0KSArCiAgICB0aGVtZSgKICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICJibGFjayIpLAogICAgICAjIFgtYXhpcyBsYWJlbHMgKE51bWJlcnMpIC0gQk9MRCAmIEJJR0dFUgogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gImJsYWNrIiksCiAgICAgICMgWC1heGlzIFRJVExFIChUaGUgVGV4dCAiTm9ybWFsaXplZCBFbnJpY2htZW50IFNjb3JlLi4uIikgLSBCT0xECiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gImJsYWNrIiwgbWFyZ2luID0gbWFyZ2luKHQgPSAxMCkpLAogICAgICAKICAgICAgIyBQbG90IFRpdGxlCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEzLCBoanVzdCA9IDAuNSksCiAgICAgIAogICAgICAjIExlZ2VuZAogICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICBsZWdlbmQuYm94ID0gInZlcnRpY2FsIiwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMCksCgogICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTk1IikKICAgICkKfQoKYGBgCgoKIyBHRU5FUkFURSA0IFBMT1RTIApgYGB7ciwgZmlnLmhlaWdodD0gMTIsIGZpZy53aWR0aD0gMTZ9CiMgQSkgQWxsIFBhdGh3YXlzIChwYWRqKQpkZl9hbGwgPC0gcHJlcGFyZV9kYXRhKGZnX2FsbCwgZXhjbHVkZV9wcm9saWYgPSBGQUxTRSkKcDEgPC0gY3JlYXRlX3Bsb3QoZGZfYWxsLCAicGFkaiIsICJGRFIgKHBhZGopIiwgIkdsb2JhbCBQYXRod2F5IEFsdGVyYXRpb25zIChNYWxpZ25hbnQgdnMuIE5vcm1hbCBDRDQrKSIpCmdnc2F2ZSgiRmlnMV9BbGxfcGFkai5wbmciLCBwMSwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gOCwgZHBpID0gMzAwKQpnZ3NhdmUoIkZpZzFfQWxsX3BhZGoucGRmIiwgcDEsIHdpZHRoID0gMTYsIGhlaWdodCA9IDgpCgojIEIpIEFsbCBQYXRod2F5cyAocC12YWx1ZSkKcDIgPC0gY3JlYXRlX3Bsb3QoZGZfYWxsLCAicHZhbCIsICJQLXZhbHVlIiwgIk5vbWluYWxseSBTaWduaWZpY2FudCBQYXRod2F5IEFsdGVyYXRpb25zIikKZ2dzYXZlKCJGaWcyX0FsbF9wdmFsLnBuZyIsIHAyLCB3aWR0aCA9IDE2LCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApCmdnc2F2ZSgiRmlnMl9BbGxfcHZhbC5wZGYiLCBwMiwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gOCkKCiMgQykgTm9uLVByb2xpZmVyYXRpb24gKHBhZGopCmRmX25vX3Byb2xpZiA8LSBwcmVwYXJlX2RhdGEoZmdfYWxsLCBleGNsdWRlX3Byb2xpZiA9IFRSVUUpCnAzIDwtIGNyZWF0ZV9wbG90KGRmX25vX3Byb2xpZiwgInBhZGoiLCAiRkRSIChwYWRqKSIsICJGdW5jdGlvbmFsICYgSW1tdW5lIFNpZ25hdHVyZXMgKE5vbi1Qcm9saWZlcmF0aXZlKSIpCmdnc2F2ZSgiRmlnM19Ob25Qcm9saWZfcGFkai5wbmciLCBwMywgd2lkdGggPSAxNiwgaGVpZ2h0ID0gOCwgZHBpID0gMzAwKQpnZ3NhdmUoIkZpZzNfTm9uUHJvbGlmX3BhZGoucGRmIiwgcDMsIHdpZHRoID0gMTYsIGhlaWdodCA9IDgpCgojIEQpIE5vbi1Qcm9saWZlcmF0aW9uIChwLXZhbHVlKQpwNCA8LSBjcmVhdGVfcGxvdChkZl9ub19wcm9saWYsICJwdmFsIiwgIlAtdmFsdWUiLCAiRXhwbG9yYXRvcnkgRnVuY3Rpb25hbCBTaWduYXR1cmVzIChOb24tUHJvbGlmZXJhdGl2ZSkiKQpnZ3NhdmUoIkZpZzRfTm9uUHJvbGlmX3B2YWwucG5nIiwgcDQsIHdpZHRoID0gMTYsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKZ2dzYXZlKCJGaWc0X05vblByb2xpZl9wdmFsLnBkZiIsIHA0LCB3aWR0aCA9IDE2LCBoZWlnaHQgPSA4KQoKcHJpbnQoIkNyZWF0ZWQgNCBmaWd1cmVzOiBGaWcxLi5GaWc0ICgucG5nIGFuZCAucGRmKSIpCgpwMQpwMgpwMwpwNApgYGAKIyBFbnJpY2htZW50IG1hcF9jbmV0cGxvdCBmb3IgbGVhZGluZy1lZGdlIGdlbmVzCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9IDE2fQoKIyMjIyBFbnJpY2htZW50IG1hcCAvIGNuZXRwbG90IGZvciBsZWFkaW5nLWVkZ2UgZ2VuZXMgIyMjIwpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMgLS0tLSBGdW5jdGlvbiAtLS0tCm1ha2VfY25ldF9lbWFwIDwtIGZ1bmN0aW9uKGZnc2VhX3JlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0aHdheXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFzZXRfbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFkal9jdXRvZmYgPSAwLjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICB0b3BOX2ZhbGxiYWNrID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dDYXRlZ29yeSA9IDE1LAogICAgICAgICAgICAgICAgICAgICAgICAgICBmb2xkX2NoYW5nZV92ZWMgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzYXZlX3Bsb3RzID0gVFJVRSkgewogIAogIG1lc3NhZ2UoIlByb2Nlc3NpbmcgZGF0YXNldDogIiwgZGF0YXNldF9uYW1lKQogIAogICMgMSkgVHJ5IHRvIGZpbHRlciBieSBwYWRqCiAgbGVhZGluZ19saXN0IDwtIGZnc2VhX3JlcyAlPiUKICAgIGZpbHRlcihwYWRqIDwgcGFkal9jdXRvZmYpICU+JQogICAgc2VsZWN0KHBhdGh3YXksIGxlYWRpbmdFZGdlKSAlPiUKICAgIG11dGF0ZShsZWFkaW5nRWRnZSA9IG1hcChsZWFkaW5nRWRnZSwgYXMuY2hhcmFjdGVyKSkgJT4lCiAgICBkZWZyYW1lKCkKICAKICAjIDIpIEZhbGxiYWNrOiBpZiB0b28gZmV3LCB0YWtlIHRvcE4gcGF0aHdheXMgYnkgTkVTCiAgaWYgKGxlbmd0aChsZWFkaW5nX2xpc3QpIDwgMikgewogICAgbWVzc2FnZSgi4pqgIE5vIHBhdGh3YXlzIHBhc3MgcGFkaiA8IiwgcGFkal9jdXRvZmYsIAogICAgICAgICAgICAiIGZvciAiLCBkYXRhc2V0X25hbWUsICIuIFVzaW5nIHRvcCAiLCB0b3BOX2ZhbGxiYWNrLCAiIHBhdGh3YXlzIGJ5IE5FUy4iKQogICAgCiAgICBsZWFkaW5nX2xpc3QgPC0gZmdzZWFfcmVzICU+JQogICAgICBhcnJhbmdlKHBhZGosIGRlc2MoYWJzKE5FUykpKSAlPiUKICAgICAgc2xpY2VfaGVhZChuID0gdG9wTl9mYWxsYmFjaykgJT4lCiAgICAgIHNlbGVjdChwYXRod2F5LCBsZWFkaW5nRWRnZSkgJT4lCiAgICAgIG11dGF0ZShsZWFkaW5nRWRnZSA9IG1hcChsZWFkaW5nRWRnZSwgYXMuY2hhcmFjdGVyKSkgJT4lCiAgICAgIGRlZnJhbWUoKQogIH0KICAKICBpZiAobGVuZ3RoKGxlYWRpbmdfbGlzdCkgPCAyKSB7CiAgICBtZXNzYWdlKCLinYwgU3RpbGwgdG9vIGZldyBwYXRod2F5cyBmb3IgIiwgZGF0YXNldF9uYW1lLCAiIOKAlCBza2lwcGluZy4iKQogICAgcmV0dXJuKE5VTEwpCiAgfQogIAogICMgVEVSTTJHRU5FIGNvbnZlcnNpb24KICBsZWFkX3Rlcm0yZ2VuZSA8LSBlbmZyYW1lKGxlYWRpbmdfbGlzdCwgbmFtZSA9ICJURVJNIiwgdmFsdWUgPSAiR0VORSIpICU+JQogICAgdW5uZXN0KGNvbHMgPSBjKEdFTkUpKQogIAogICMgVW5pb24gb2YgbGVhZGluZy1lZGdlIGdlbmVzCiAgdW5pb25fbGVhZGluZ19nZW5lcyA8LSB1bmlxdWUodW5saXN0KGxlYWRpbmdfbGlzdCkpCiAgCiAgaWYgKGxlbmd0aCh1bmlvbl9sZWFkaW5nX2dlbmVzKSA8IDIpIHsKICAgIG1lc3NhZ2UoIuKdjCBUb28gZmV3IGxlYWRpbmctZWRnZSBnZW5lcyBmb3IgIiwgZGF0YXNldF9uYW1lLCAiIOKAlCBza2lwcGluZy4iKQogICAgcmV0dXJuKE5VTEwpCiAgfQogIAogICMgUnVuIGVucmljaG1lbnQgKHZlcnkgcmVsYXhlZCBtaW4vbWF4IHNpemVzKQogIGVuciA8LSBlbnJpY2hlcigKICAgIHVuaW9uX2xlYWRpbmdfZ2VuZXMsCiAgICBURVJNMkdFTkUgPSBsZWFkX3Rlcm0yZ2VuZSwKICAgIG1pbkdTU2l6ZSA9IDEsCiAgICBtYXhHU1NpemUgPSA1MDAwCiAgKQogIAogIGlmIChpcy5udWxsKGVucikgfHwgbnJvdyhlbnIpID09IDApIHsKICAgIG1lc3NhZ2UoIuKdjCBObyBlbnJpY2htZW50IHJlc3VsdCBmb3IgIiwgZGF0YXNldF9uYW1lKQogICAgcmV0dXJuKE5VTEwpCiAgfQogIAogICMgLS0tLSBjbmV0cGxvdCAtLS0tCiAgY25ldCA8LSBjbmV0cGxvdCgKICAgIGVuciwKICAgIGZvbGRDaGFuZ2UgPSBmb2xkX2NoYW5nZV92ZWMsCiAgICBzaG93Q2F0ZWdvcnkgPSBzaG93Q2F0ZWdvcnksCiAgICBjaXJjdWxhciA9IEZBTFNFLAogICAgY29sb3JFZGdlID0gVFJVRQogICkgKyBnZ3RpdGxlKHBhc3RlKCJDbmV0cGxvdCDigJQiLCBkYXRhc2V0X25hbWUpKQogIAogIGlmIChzYXZlX3Bsb3RzKSB7CiAgICBnZ3NhdmUoCiAgICAgIHBhc3RlMCgiY25ldHBsb3RfbGVhZGluZ0VkZ2VfIiwgZGF0YXNldF9uYW1lLCAiLnBuZyIpLAogICAgICBjbmV0LAogICAgICB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDAKICAgICkKICB9CiAgCiAgIyAtLS0tIGVtYXBwbG90IC0tLS0KICBlbWFwIDwtIHBhaXJ3aXNlX3Rlcm1zaW0oZW5yKQogIGVtYXBfcGxvdCA8LSBlbWFwcGxvdChlbWFwLCBzaG93Q2F0ZWdvcnkgPSBzaG93Q2F0ZWdvcnkpICsKICAgIGdndGl0bGUocGFzdGUoIkVucmljaG1lbnQgTWFwIOKAlCIsIGRhdGFzZXRfbmFtZSkpCiAgCiAgaWYgKHNhdmVfcGxvdHMpIHsKICAgIGdnc2F2ZSgKICAgICAgcGFzdGUwKCJlbWFwcGxvdF9sZWFkaW5nRWRnZV8iLCBkYXRhc2V0X25hbWUsICIucG5nIiksCiAgICAgIGVtYXBfcGxvdCwKICAgICAgd2lkdGggPSAxMiwgaGVpZ2h0ID0gOCwgZHBpID0gMzAwCiAgICApCiAgfQogIAogIHJldHVybihsaXN0KGVucmljaF9yZXMgPSBlbnIsIGNuZXQgPSBjbmV0LCBlbWFwID0gZW1hcF9wbG90KSkKfQoKIyAtLS0tIFJ1biBmb3IgYWxsIGRhdGFzZXRzIC0tLS0KIyBPcHRpb25hbDogZm9sZC1jaGFuZ2UgdmVjdG9yIGZyb20gREUgcmVzdWx0cwpmb2xkX2NoYW5nZV92ZWMgPC0gc2V0TmFtZXMoZGUkYXZnX2xvZ0ZDLCBkZSRnZW5lKQoKcmVzX2ggICAgIDwtIG1ha2VfY25ldF9lbWFwKGZnX2gsICAgICBwYXRod2F5c19oLCAgICAgIkhhbGxtYXJrIiwgZm9sZF9jaGFuZ2VfdmVjID0gZm9sZF9jaGFuZ2VfdmVjKQpyZXNfa2VnZyAgPC0gbWFrZV9jbmV0X2VtYXAoZmdfa2VnZywgIHBhdGh3YXlzX2tlZ2csICAiS0VHRyIsICAgICBmb2xkX2NoYW5nZV92ZWMgPSBmb2xkX2NoYW5nZV92ZWMpCnJlc19yZWFjdCA8LSBtYWtlX2NuZXRfZW1hcChmZ19yZWFjdCwgcGF0aHdheXNfcmVhY3QsICJSZWFjdG9tZSIsIGZvbGRfY2hhbmdlX3ZlYyA9IGZvbGRfY2hhbmdlX3ZlYykKcmVzX2JwICAgIDwtIG1ha2VfY25ldF9lbWFwKGZnX2JwLCAgICBwYXRod2F5c19icCwgICAgIkdPX0JQIiwgICAgZm9sZF9jaGFuZ2VfdmVjID0gZm9sZF9jaGFuZ2VfdmVjKQoKbWVzc2FnZSgi4pyFIEZpbmlzaGVkIENuZXQvRW5yaWNobWVudCBtYXAgcGxvdHMgZm9yIGFsbCBkYXRhc2V0cyIpCgpgYGAKCgoKCgojIEVucmljaG1lbnQgbWFwX2NuZXRwbG90IGZvciBsZWFkaW5nLWVkZ2UgZ2VuZXMKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0gMTZ9CgojIyMjIEVucmljaG1lbnQgbWFwIC8gY25ldHBsb3QgZm9yIGxlYWRpbmctZWRnZSBnZW5lcyAjIyMjCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KGVucmljaHBsb3QpCmxpYnJhcnkodGlkeXZlcnNlKQoKIyAtLS0tIEZ1bmN0aW9uIC0tLS0KbWFrZV9jbmV0X2VtYXAgPC0gZnVuY3Rpb24oZmdzZWFfcmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwYXRod2F5cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YXNldF9uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwYWRqX2N1dG9mZiA9IDAuMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcE5fZmFsbGJhY2sgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd0NhdGVnb3J5ID0gMTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbGRfY2hhbmdlX3ZlYyA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhdmVfcGxvdHMgPSBUUlVFKSB7CiAgCiAgbWVzc2FnZSgiUHJvY2Vzc2luZyBkYXRhc2V0OiAiLCBkYXRhc2V0X25hbWUpCiAgCiAgIyAxKSBUcnkgdG8gZmlsdGVyIGJ5IHBhZGoKICBsZWFkaW5nX2xpc3QgPC0gZmdzZWFfcmVzICU+JQogICAgZmlsdGVyKHBhZGogPCBwYWRqX2N1dG9mZikgJT4lCiAgICBzZWxlY3QocGF0aHdheSwgbGVhZGluZ0VkZ2UpICU+JQogICAgbXV0YXRlKGxlYWRpbmdFZGdlID0gbWFwKGxlYWRpbmdFZGdlLCBhcy5jaGFyYWN0ZXIpKSAlPiUKICAgIGRlZnJhbWUoKQogIAogICMgMikgRmFsbGJhY2s6IGlmIHRvbyBmZXcsIHRha2UgdG9wTiBwYXRod2F5cyBieSBORVMKICBpZiAobGVuZ3RoKGxlYWRpbmdfbGlzdCkgPCAyKSB7CiAgICBtZXNzYWdlKCLimqAgTm8gcGF0aHdheXMgcGFzcyBwYWRqIDwiLCBwYWRqX2N1dG9mZiwgCiAgICAgICAgICAgICIgZm9yICIsIGRhdGFzZXRfbmFtZSwgIi4gVXNpbmcgdG9wICIsIHRvcE5fZmFsbGJhY2ssICIgcGF0aHdheXMgYnkgTkVTLiIpCiAgICAKICAgIGxlYWRpbmdfbGlzdCA8LSBmZ3NlYV9yZXMgJT4lCiAgICAgIGFycmFuZ2UocGFkaiwgZGVzYyhhYnMoTkVTKSkpICU+JQogICAgICBzbGljZV9oZWFkKG4gPSB0b3BOX2ZhbGxiYWNrKSAlPiUKICAgICAgc2VsZWN0KHBhdGh3YXksIGxlYWRpbmdFZGdlKSAlPiUKICAgICAgbXV0YXRlKGxlYWRpbmdFZGdlID0gbWFwKGxlYWRpbmdFZGdlLCBhcy5jaGFyYWN0ZXIpKSAlPiUKICAgICAgZGVmcmFtZSgpCiAgfQogIAogIGlmIChsZW5ndGgobGVhZGluZ19saXN0KSA8IDIpIHsKICAgIG1lc3NhZ2UoIuKdjCBTdGlsbCB0b28gZmV3IHBhdGh3YXlzIGZvciAiLCBkYXRhc2V0X25hbWUsICIg4oCUIHNraXBwaW5nLiIpCiAgICByZXR1cm4oTlVMTCkKICB9CiAgCiAgIyBURVJNMkdFTkUgY29udmVyc2lvbgogIGxlYWRfdGVybTJnZW5lIDwtIGVuZnJhbWUobGVhZGluZ19saXN0LCBuYW1lID0gIlRFUk0iLCB2YWx1ZSA9ICJHRU5FIikgJT4lCiAgICB1bm5lc3QoY29scyA9IGMoR0VORSkpCiAgCiAgIyBVbmlvbiBvZiBsZWFkaW5nLWVkZ2UgZ2VuZXMKICB1bmlvbl9sZWFkaW5nX2dlbmVzIDwtIHVuaXF1ZSh1bmxpc3QobGVhZGluZ19saXN0KSkKICAKICBpZiAobGVuZ3RoKHVuaW9uX2xlYWRpbmdfZ2VuZXMpIDwgMikgewogICAgbWVzc2FnZSgi4p2MIFRvbyBmZXcgbGVhZGluZy1lZGdlIGdlbmVzIGZvciAiLCBkYXRhc2V0X25hbWUsICIg4oCUIHNraXBwaW5nLiIpCiAgICByZXR1cm4oTlVMTCkKICB9CiAgCiAgIyBSdW4gZW5yaWNobWVudCAodmVyeSByZWxheGVkIG1pbi9tYXggc2l6ZXMpCiAgZW5yIDwtIGVucmljaGVyKAogICAgdW5pb25fbGVhZGluZ19nZW5lcywKICAgIFRFUk0yR0VORSA9IGxlYWRfdGVybTJnZW5lLAogICAgbWluR1NTaXplID0gMSwKICAgIG1heEdTU2l6ZSA9IDUwMDAKICApCiAgCiAgaWYgKGlzLm51bGwoZW5yKSB8fCBucm93KGVucikgPT0gMCkgewogICAgbWVzc2FnZSgi4p2MIE5vIGVucmljaG1lbnQgcmVzdWx0IGZvciAiLCBkYXRhc2V0X25hbWUpCiAgICByZXR1cm4oTlVMTCkKICB9CiAgCiAgIyAtLS0tIGNuZXRwbG90IC0tLS0KICBjbmV0IDwtIGNuZXRwbG90KAogICAgZW5yLAogICAgZm9sZENoYW5nZSA9IGZvbGRfY2hhbmdlX3ZlYywKICAgIHNob3dDYXRlZ29yeSA9IHNob3dDYXRlZ29yeSwKICAgIGNpcmN1bGFyID0gRkFMU0UsCiAgICBjb2xvckVkZ2UgPSBUUlVFCiAgKSArIGdndGl0bGUocGFzdGUoIkNuZXRwbG90IOKAlCIsIGRhdGFzZXRfbmFtZSkpCiAgCiAgaWYgKHNhdmVfcGxvdHMpIHsKICAgIGdnc2F2ZSgKICAgICAgcGFzdGUwKCJjbmV0cGxvdF9sZWFkaW5nRWRnZV8iLCBkYXRhc2V0X25hbWUsICIucG5nIiksCiAgICAgIGNuZXQsCiAgICAgIHdpZHRoID0gMTIsIGhlaWdodCA9IDgsIGRwaSA9IDMwMAogICAgKQogIH0KICAKICAjIC0tLS0gZW1hcHBsb3QgLS0tLQogIGVtYXAgPC0gcGFpcndpc2VfdGVybXNpbShlbnIpCiAgZW1hcF9wbG90IDwtIGVtYXBwbG90KGVtYXAsIHNob3dDYXRlZ29yeSA9IHNob3dDYXRlZ29yeSkgKwogICAgZ2d0aXRsZShwYXN0ZSgiRW5yaWNobWVudCBNYXAg4oCUIiwgZGF0YXNldF9uYW1lKSkKICAKICBpZiAoc2F2ZV9wbG90cykgewogICAgZ2dzYXZlKAogICAgICBwYXN0ZTAoImVtYXBwbG90X2xlYWRpbmdFZGdlXyIsIGRhdGFzZXRfbmFtZSwgIi5wbmciKSwKICAgICAgZW1hcF9wbG90LAogICAgICB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDAKICAgICkKICB9CiAgCiAgcmV0dXJuKGxpc3QoZW5yaWNoX3JlcyA9IGVuciwgY25ldCA9IGNuZXQsIGVtYXAgPSBlbWFwX3Bsb3QpKQp9CgojIC0tLS0gUnVuIGZvciBhbGwgZGF0YXNldHMgLS0tLQojIE9wdGlvbmFsOiBmb2xkLWNoYW5nZSB2ZWN0b3IgZnJvbSBERSByZXN1bHRzCmZvbGRfY2hhbmdlX3ZlYyA8LSBzZXROYW1lcyhkZSRhdmdfbG9nRkMsIGRlJGdlbmUpCgpyZXNfaCAgICAgPC0gbWFrZV9jbmV0X2VtYXAoZmdfaCwgICAgIHBhdGh3YXlzX2gsICAgICAiSGFsbG1hcmsiLCBmb2xkX2NoYW5nZV92ZWMgPSBmb2xkX2NoYW5nZV92ZWMpCnJlc19rZWdnICA8LSBtYWtlX2NuZXRfZW1hcChmZ19rZWdnLCAgcGF0aHdheXNfa2VnZywgICJLRUdHIiwgICAgIGZvbGRfY2hhbmdlX3ZlYyA9IGZvbGRfY2hhbmdlX3ZlYykKcmVzX3JlYWN0IDwtIG1ha2VfY25ldF9lbWFwKGZnX3JlYWN0LCBwYXRod2F5c19yZWFjdCwgIlJlYWN0b21lIiwgZm9sZF9jaGFuZ2VfdmVjID0gZm9sZF9jaGFuZ2VfdmVjKQpyZXNfYnAgICAgPC0gbWFrZV9jbmV0X2VtYXAoZmdfYnAsICAgIHBhdGh3YXlzX2JwLCAgICAiR09fQlAiLCAgICBmb2xkX2NoYW5nZV92ZWMgPSBmb2xkX2NoYW5nZV92ZWMpCgptZXNzYWdlKCLinIUgRmluaXNoZWQgQ25ldC9FbnJpY2htZW50IG1hcCBwbG90cyBmb3IgYWxsIGRhdGFzZXRzIikKCmBgYAoKIyBQYXRod2F5cyBFeHByZXNzaW9uIG9uIFVNQVAKYGBge3IsICBmaWcuaGVpZ2h0PTIyLCBmaWcud2lkdGg9IDI2fQoKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShTZXVyYXRPYmplY3QpCgoKdG9wUGF0aHdheXMgPC0gZmdfaFtbInBhdGh3YXkiXV0gJT4lIGhlYWQoMTApCnRpdGxlcyA8LSBzdWIoIkhBTExNQVJLXyIsICIiLCB0b3BQYXRod2F5cykKCgojIE1ha2Ugc3VyZSB0b3BQYXRod2F5cyBhcmUgaW4gdGhlIEhhbGxtYXJrIGxpc3QKdG9wUGF0aHdheXMgPC0gaW50ZXJzZWN0KHRvcFBhdGh3YXlzLCBuYW1lcyhwYXRod2F5c19oKSkKCiMgVGl0bGVzIGZvciBwbG90cwp0aXRsZXMgPC0gc3ViKCJIQUxMTUFSS18iLCAiIiwgdG9wUGF0aHdheXMpCgpvYmogPC0gc2FtcGxlCiMgVXNlIFNDVCBhc3NheSBleHBsaWNpdGx5CkRlZmF1bHRBc3NheShvYmopIDwtICJTQ1QiCgojIFJ1biBwbG90Q29yZWd1bGF0aW9uUHJvZmlsZVJlZHVjdGlvbgpwcyA8LSBwbG90Q29yZWd1bGF0aW9uUHJvZmlsZVJlZHVjdGlvbigKICBwYXRod2F5c19oW3RvcFBhdGh3YXlzXSwKICBvYmosCiAgYXNzYXkgPSAiU0NUIiwgICAgICAgICAjIGV4cGxpY2l0bHkgdXNlIFNDVCBhc3NheQogIHRpdGxlID0gdGl0bGVzLAogIHJlZHVjdGlvbiA9ICJ1bWFwIgopCgojIENvbWJpbmUgcGxvdHMKY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcHNbMTpsZW5ndGgocHMpXSwgbmNvbCA9IDIpCgoKCmBgYAoKIyMgUGF0aHdheXMgRXhwcmVzc2lvbiBvbiBVTUFQCmBgYHtyLCAgZmlnLmhlaWdodD0yMiwgZmlnLndpZHRoPSAyNn0KCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQoKIyAx77iP4oOjIFNvcnQgdGhlIEhhbGxtYXJrIEZHU0VBIHRhYmxlIGJ5IE5FUwpmZ19oX3NvcnRlZCA8LSBmZ19oICU+JSBhcnJhbmdlKGRlc2MoTkVTKSkgICMgZGVzY2VuZGluZyBORVMKCiMgMu+4j+KDoyBHZXQgdG9wIDEwIHVwIGFuZCB0b3AgMTAgZG93biBwYXRod2F5cwp0b3BfdXAgICA8LSBmZ19oX3NvcnRlZCRwYXRod2F5WzE6MTBdICAgICAgICMgdG9wIDEwIHVwIGJ5IE5FUwp0b3BfZG93biA8LSBmZ19oX3NvcnRlZCAlPiUgYXJyYW5nZShORVMpICU+JSBzbGljZSgxOjEwKSAlPiUgcHVsbChwYXRod2F5KSAgIyB0b3AgMTAgZG93bgoKIyAz77iP4oOjIENvbWJpbmUgdXAgYW5kIGRvd24KdG9wUGF0aHdheXMgPC0gYyh0b3BfdXAsIHRvcF9kb3duKQoKIyBLZWVwIG9ubHkgcGF0aHdheXMgdGhhdCBleGlzdCBpbiB0aGUgSGFsbG1hcmsgZ2VuZSBzZXRzCnRvcFBhdGh3YXlzIDwtIGludGVyc2VjdCh0b3BQYXRod2F5cywgbmFtZXMocGF0aHdheXNfaCkpCgojIFRpdGxlcyBmb3IgcGxvdHRpbmcKdGl0bGVzIDwtIHN1YigiSEFMTE1BUktfIiwgIiIsIHRvcFBhdGh3YXlzKQoKIyBVc2UgU0NUIGFzc2F5IGV4cGxpY2l0bHkKRGVmYXVsdEFzc2F5KG9iaikgPC0gIlNDVCIKCiMgNO+4j+KDoyBSdW4gY28tcmVndWxhdGlvbiBVTUFQCnBzIDwtIHBsb3RDb3JlZ3VsYXRpb25Qcm9maWxlUmVkdWN0aW9uKAogIHBhdGh3YXlzX2hbdG9wUGF0aHdheXNdLAogIG9iaiwKICBhc3NheSA9ICJTQ1QiLAogIHRpdGxlID0gdGl0bGVzLAogIHJlZHVjdGlvbiA9ICJ1bWFwIgopCgojIDXvuI/ig6MgQ29tYmluZSBwbG90cyBpbiBhIGdyaWQKY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcHNbMTpsZW5ndGgocHMpXSwgbmNvbCA9IDQpCgoKYGBgCiMjIFBhdGh3YXlzIEV4cHJlc3Npb24gb24gVU1BUApgYGB7ciwgIGZpZy5oZWlnaHQ9MjIsIGZpZy53aWR0aD0gMjZ9CgpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KFNldXJhdE9iamVjdCkKCgp0b3BQYXRod2F5cyA8LSBmZ19rZWdnW1sicGF0aHdheSJdXSAlPiUgaGVhZCgxMCkKdGl0bGVzIDwtIHN1YigiS0VHR18iLCAiIiwgdG9wUGF0aHdheXMpCgoKIyBNYWtlIHN1cmUgdG9wUGF0aHdheXMgYXJlIGluIHRoZSBIYWxsbWFyayBsaXN0CnRvcFBhdGh3YXlzIDwtIGludGVyc2VjdCh0b3BQYXRod2F5cywgbmFtZXMocGF0aHdheXNfa2VnZykpCgojIFRpdGxlcyBmb3IgcGxvdHMKdGl0bGVzIDwtIHN1YigiS0VHR18iLCAiIiwgdG9wUGF0aHdheXMpCgojIFVzZSBTQ1QgYXNzYXkgZXhwbGljaXRseQpEZWZhdWx0QXNzYXkob2JqKSA8LSAiU0NUIgoKIyBSdW4gcGxvdENvcmVndWxhdGlvblByb2ZpbGVSZWR1Y3Rpb24KcHMgPC0gcGxvdENvcmVndWxhdGlvblByb2ZpbGVSZWR1Y3Rpb24oCiAgcGF0aHdheXNfa2VnZ1t0b3BQYXRod2F5c10sCiAgb2JqLAogIGFzc2F5ID0gIlNDVCIsICAgICAgICAgIyBleHBsaWNpdGx5IHVzZSBTQ1QgYXNzYXkKICB0aXRsZSA9IHRpdGxlcywKICByZWR1Y3Rpb24gPSAidW1hcCIKKQoKIyBDb21iaW5lIHBsb3RzCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHBzWzE6bGVuZ3RoKHBzKV0sIG5jb2wgPSAyKQoKCgpgYGAKCgojIyBQYXRod2F5cyBFeHByZXNzaW9uIG9uIFVNQVAKYGBge3IsICBmaWcuaGVpZ2h0PTIyLCBmaWcud2lkdGg9IDI2fQoKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShTZXVyYXRPYmplY3QpCgoKdG9wUGF0aHdheXMgPC0gZmdfcmVhY3RbWyJwYXRod2F5Il1dICU+JSBoZWFkKDEwKQp0aXRsZXMgPC0gc3ViKCJSRUFDVE9NRV8iLCAiIiwgdG9wUGF0aHdheXMpCgoKIyBNYWtlIHN1cmUgdG9wUGF0aHdheXMgYXJlIGluIHRoZSBIYWxsbWFyayBsaXN0CnRvcFBhdGh3YXlzIDwtIGludGVyc2VjdCh0b3BQYXRod2F5cywgbmFtZXMocGF0aHdheXNfcmVhY3QpKQoKIyBUaXRsZXMgZm9yIHBsb3RzCnRpdGxlcyA8LSBzdWIoIlJFQUNUT01FXyIsICIiLCB0b3BQYXRod2F5cykKCiMgVXNlIFNDVCBhc3NheSBleHBsaWNpdGx5CkRlZmF1bHRBc3NheShvYmopIDwtICJTQ1QiCgojIFJ1biBwbG90Q29yZWd1bGF0aW9uUHJvZmlsZVJlZHVjdGlvbgpwcyA8LSBwbG90Q29yZWd1bGF0aW9uUHJvZmlsZVJlZHVjdGlvbigKICBwYXRod2F5c19yZWFjdFt0b3BQYXRod2F5c10sCiAgb2JqLAogIGFzc2F5ID0gIlNDVCIsICAgICAgICAgIyBleHBsaWNpdGx5IHVzZSBTQ1QgYXNzYXkKICB0aXRsZSA9IHRpdGxlcywKICByZWR1Y3Rpb24gPSAidW1hcCIKKQoKIyBDb21iaW5lIHBsb3RzCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHBzWzE6bGVuZ3RoKHBzKV0sIG5jb2wgPSAyKQoKCgpgYGAKIyMgUGF0aHdheXMgRXhwcmVzc2lvbiBvbiBVTUFQCmBgYHtyLCAgZmlnLmhlaWdodD0zMCwgZmlnLndpZHRoPSAyMH0KCiMgU29ydCBSZWFjdG9tZSBGR1NFQSB0YWJsZSBieSBORVMKZmdfcmVhY3Rfc29ydGVkIDwtIGZnX3JlYWN0ICU+JSBhcnJhbmdlKGRlc2MoTkVTKSkKCiMgVG9wIDEwIHVwIGFuZCB0b3AgMTAgZG93bgp0b3BfdXAgICA8LSBmZ19yZWFjdF9zb3J0ZWQkcGF0aHdheVsxOjEwXQp0b3BfZG93biA8LSBmZ19yZWFjdF9zb3J0ZWQgJT4lIGFycmFuZ2UoTkVTKSAlPiUgc2xpY2UoMToxMCkgJT4lIHB1bGwocGF0aHdheSkKCiMgQ29tYmluZSBhbmQga2VlcCBvbmx5IHBhdGh3YXlzIHByZXNlbnQgaW4gUmVhY3RvbWUgZ2VuZSBzZXRzCnRvcFBhdGh3YXlzIDwtIGludGVyc2VjdChjKHRvcF91cCwgdG9wX2Rvd24pLCBuYW1lcyhwYXRod2F5c19yZWFjdCkpCgojIFRpdGxlcwp0aXRsZXMgPC0gc3ViKCJSRUFDVE9NRV8iLCAiIiwgdG9wUGF0aHdheXMpCgojIFNDVCBhc3NheQpEZWZhdWx0QXNzYXkob2JqKSA8LSAiU0NUIgoKIyBSdW4gY28tcmVndWxhdGlvbiBVTUFQCnBzIDwtIHBsb3RDb3JlZ3VsYXRpb25Qcm9maWxlUmVkdWN0aW9uKAogIHBhdGh3YXlzX3JlYWN0W3RvcFBhdGh3YXlzXSwKICBvYmosCiAgYXNzYXkgPSAiU0NUIiwKICB0aXRsZSA9IHRpdGxlcywKICByZWR1Y3Rpb24gPSAidW1hcCIKKQoKIyBDb21iaW5lIHBsb3RzCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHBzWzE6bGVuZ3RoKHBzKV0sIG5jb2wgPSAyKQoKCgpgYGAKCgojIyBQYXRod2F5cyBFeHByZXNzaW9uIG9uIFVNQVAKYGBge3IsICBmaWcuaGVpZ2h0PTIyLCBmaWcud2lkdGg9IDI2fQoKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShTZXVyYXRPYmplY3QpCgoKdG9wUGF0aHdheXMgPC0gZmdfYnBbWyJwYXRod2F5Il1dICU+JSBoZWFkKDEwKQp0aXRsZXMgPC0gc3ViKCJHT18iLCAiIiwgdG9wUGF0aHdheXMpCgoKIyBNYWtlIHN1cmUgdG9wUGF0aHdheXMgYXJlIGluIHRoZSBIYWxsbWFyayBsaXN0CnRvcFBhdGh3YXlzIDwtIGludGVyc2VjdCh0b3BQYXRod2F5cywgbmFtZXMocGF0aHdheXNfYnApKQoKIyBUaXRsZXMgZm9yIHBsb3RzCnRpdGxlcyA8LSBzdWIoIkdPXyIsICIiLCB0b3BQYXRod2F5cykKCiMgVXNlIFNDVCBhc3NheSBleHBsaWNpdGx5CkRlZmF1bHRBc3NheShvYmopIDwtICJTQ1QiCgojIFJ1biBwbG90Q29yZWd1bGF0aW9uUHJvZmlsZVJlZHVjdGlvbgpwcyA8LSBwbG90Q29yZWd1bGF0aW9uUHJvZmlsZVJlZHVjdGlvbigKICBwYXRod2F5c19icFt0b3BQYXRod2F5c10sCiAgb2JqLAogIGFzc2F5ID0gIlNDVCIsICAgICAgICAgIyBleHBsaWNpdGx5IHVzZSBTQ1QgYXNzYXkKICB0aXRsZSA9IHRpdGxlcywKICByZWR1Y3Rpb24gPSAidW1hcCIKKQoKIyBDb21iaW5lIHBsb3RzCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHBzWzE6bGVuZ3RoKHBzKV0sIG5jb2wgPSAyKQoKCgpgYGAKIyMgUGF0aHdheXMgRXhwcmVzc2lvbiBvbiBVTUFQCmBgYHtyLCBmaWcuaGVpZ2h0PTMwLCBmaWcud2lkdGg9IDIwfQoKIyBTb3J0IEdPOkJQIEZHU0VBIHRhYmxlIGJ5IE5FUwpmZ19icF9zb3J0ZWQgPC0gZmdfYnAgJT4lIGFycmFuZ2UoZGVzYyhORVMpKQoKIyBUb3AgMTAgdXAgYW5kIHRvcCAxMCBkb3duCnRvcF91cCAgIDwtIGZnX2JwX3NvcnRlZCRwYXRod2F5WzE6MTBdCnRvcF9kb3duIDwtIGZnX2JwX3NvcnRlZCAlPiUgYXJyYW5nZShORVMpICU+JSBzbGljZSgxOjEwKSAlPiUgcHVsbChwYXRod2F5KQoKIyBDb21iaW5lIGFuZCBrZWVwIG9ubHkgcGF0aHdheXMgcHJlc2VudCBpbiBHTzpCUCBnZW5lIHNldHMKdG9wUGF0aHdheXMgPC0gaW50ZXJzZWN0KGModG9wX3VwLCB0b3BfZG93biksIG5hbWVzKHBhdGh3YXlzX2JwKSkKCiMgVGl0bGVzCnRpdGxlcyA8LSBzdWIoIkdPX0JQXyIsICIiLCB0b3BQYXRod2F5cykKCiMgU0NUIGFzc2F5CkRlZmF1bHRBc3NheShvYmopIDwtICJTQ1QiCgojIFJ1biBjby1yZWd1bGF0aW9uIFVNQVAKcHMgPC0gcGxvdENvcmVndWxhdGlvblByb2ZpbGVSZWR1Y3Rpb24oCiAgcGF0aHdheXNfYnBbdG9wUGF0aHdheXNdLAogIG9iaiwKICBhc3NheSA9ICJTQ1QiLAogIHRpdGxlID0gdGl0bGVzLAogIHJlZHVjdGlvbiA9ICJ1bWFwIgopCgojIENvbWJpbmUgcGxvdHMKY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcHNbMTpsZW5ndGgocHMpXSwgbmNvbCA9IDIpCgoKYGBgCg==