1 load libraries

2 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")

2.1 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
)

3 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")

4 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"
)

5 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)))
}

6 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")
    )
}

7 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

8 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")

9 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")

10 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

10.1 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

10.2 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

10.3 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

10.4 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

10.5 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

10.6 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==