1. load libraries

2. Load the filtered list on mean expression


load("/home/bioinfo/2025_NewHarmony_Integrated_Files/0-imp_Robj/0-robj/5-Harmony_Integrated_All_samples_Merged_CD4Tcells_final_Resolution_Selected_0.8_ADT_Normalized_cleaned_mt.robj")

3. Distribution of cells in malignant Clusters


# Load necessary libraries
library(Seurat)
library(ggplot2)
library(viridis)
library(RColorBrewer)

# Step 1: Subset malignant cells (exclude PBMC and PBMC_10x)
malignant_cells <- WhichCells(All_samples_Merged, expression = !(cell_line %in% c("PBMC", "PBMC_10x")))

# Step 2: Extract predicted.celltype.l2 values for malignant cells
malignant_celltypes <- All_samples_Merged@meta.data[malignant_cells, "predicted.celltype.l2", drop = FALSE]

# Step 3: Create a frequency table
celltype_table <- as.data.frame(table(malignant_celltypes$predicted.celltype.l2))
colnames(celltype_table) <- c("cell_type", "nCells")

# Step 4: Generate a soft color palette (Set2 supports up to 8 by default)
n_colors <- length(unique(celltype_table$cell_type))
base_palette <- brewer.pal(min(n_colors, 8), "Set2")

# If >8 colors, extend with "Paired" or loop existing palette
if (n_colors > 8) {
  extra_colors <- brewer.pal(min(n_colors - 8, 12), "Paired")
  color_palette <- c(base_palette, extra_colors)
} else {
  color_palette <- base_palette
}

# Repeat if not enough (fallback)
if (length(color_palette) < n_colors) {
  color_palette <- rep(color_palette, length.out = n_colors)
}

# Step 5: Plot
celltype_barplot <- ggplot(celltype_table, aes(x = reorder(cell_type, -nCells), y = nCells, fill = cell_type)) +
  geom_col() +
  theme_classic() +
  geom_text(aes(label = nCells), vjust = -0.25) +
  scale_fill_manual(values = color_palette) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        plot.title = element_text(hjust = 0.5),
        legend.position = "none") +
  ggtitle("Cell Type Composition in Malignant Cell Lines") +
  xlab("Predicted Cell Type (Azimuth)") +
  ylab("Number of Cells")

# Step 6: Print the plot
print(celltype_barplot)

4. Distribution of cells in malignant Clusters


library(Seurat)
library(ggplot2)
library(RColorBrewer)

# Step 1: Identify HSPC cells based on Azimuth annotation
cDC2_cells <- WhichCells(All_samples_Merged, expression = predicted.celltype.l2 == "cDC2")

# Step 2: Get cluster assignments of HSPC cells
cDC2_clusters <- All_samples_Merged@meta.data[cDC2_cells, "seurat_clusters", drop = FALSE]

# Step 3: Create a frequency table
cDC2_cluster_counts <- as.data.frame(table(cDC2_clusters$seurat_clusters))
colnames(cDC2_cluster_counts) <- c("Cluster", "ncDC2")

# Step 4: Generate color palette (adjustable if you have many clusters)
n_colors <- length(unique(cDC2_cluster_counts$Cluster))
bar_colors <- brewer.pal(min(n_colors, 8), "Set2")
if (n_colors > length(bar_colors)) {
  bar_colors <- rep(bar_colors, length.out = n_colors)
}

# Step 5: Plot
cDC2_cluster_plot <- ggplot(cDC2_cluster_counts, aes(x = reorder(Cluster, -ncDC2), y = ncDC2, fill = Cluster)) +
  geom_col() +
  geom_text(aes(label = ncDC2), vjust = -0.25) +
  scale_fill_manual(values = bar_colors) +
  theme_classic() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        plot.title = element_text(hjust = 0.5),
        legend.position = "none") +
  ggtitle("cDC2 Cells per Seurat Cluster") +
  xlab("Seurat Cluster") +
  ylab("Number of cDC2 Cells")

print(cDC2_cluster_plot)

5. Subset the cDC2 Cells (Based on Azimuth Annotation)


# Subset cDC2 cells
cDC2_cells <- subset(All_samples_Merged, subset = predicted.celltype.l2 == "cDC2")

6. Subset Controls


CD4_normal <- subset(
  All_samples_Merged,
  subset = cell_line %in% c("PBMC", "PBMC_10x")
)

7. label them in the full object for DE analysis:


# 1. Set RNA as default assay (if not already)
DefaultAssay(All_samples_Merged) <- "RNA"

# 2. Normalize (only if not already normalized)
All_samples_Merged <- NormalizeData(
  All_samples_Merged,
  normalization.method = "LogNormalize",
  scale.factor = 10000
)
Normalizing layer: counts
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
# 3. Get cell names from subset objects
cDC2_cells_names <- colnames(cDC2_cells)
CD4_normal_cells_names <- colnames(CD4_normal)

# 4. Label groups in the full object
All_samples_Merged$cell_type_group <- NA
All_samples_Merged$cell_type_group[cDC2_cells_names] <- "cDC2"
All_samples_Merged$cell_type_group[CD4_normal_cells_names] <- "Normal_CD4"

# Set identities for DE
Idents(All_samples_Merged) <- "cell_type_group"

# 5. Perform DE analysis
de_cDC2_vs_CD4 <- FindMarkers(
  object = All_samples_Merged,
  ident.1 = "cDC2",
  ident.2 = "Normal_CD4",
  assay = "RNA",
  test.use = "wilcox",
  only.pos = FALSE
)
Warning: The `slot` argument of `GetAssayData()` is deprecated as of SeuratObject 5.0.0.
Please use the `layer` argument instead.Warning: `PackageCheck()` was deprecated in SeuratObject 5.0.0.
Please use `rlang::check_installed()` instead.
# 6. Add mean expression per group
rna_data <- GetAssayData(All_samples_Merged, assay = "RNA", slot = "data")
de_cDC2_vs_CD4$mean_expr_cDC2 <- rowMeans(rna_data[rownames(de_cDC2_vs_CD4), cDC2_cells_names])
de_cDC2_vs_CD4$mean_expr_NormalCD4 <- rowMeans(rna_data[rownames(de_cDC2_vs_CD4), CD4_normal_cells_names])

# 7. Save results
write.csv(de_cDC2_vs_CD4, file = "DE_cDC2_vs_NormalCD4.csv", row.names = TRUE)

8. Filter cDC2 Data Based on Mean Expression


# Apply the filter to the cDC2 DE results to remove genes with mean expression < 0.2 in both groups
cDC2_filtered <- de_cDC2_vs_CD4 %>%
  filter(!(mean_expr_cDC2 < 0.2 & mean_expr_NormalCD4 < 0.2))

# Save the filtered DE results
write.csv(cDC2_filtered, file = "cDC2_vs_NormalCD4_filtered_mean_expression.csv", row.names = TRUE)

9. Volcano Plots


library(dplyr)
library(EnhancedVolcano)

# Sort genes by adjusted p-value and absolute log2 fold change
filtered_genes <- de_cDC2_vs_CD4 %>%
  arrange(p_val_adj, desc(abs(avg_log2FC)))

# Add gene names as a column for labeling
filtered_genes$gene <- rownames(filtered_genes)


# Create the volcano plot for cDC2 vs normal CD4 T cells
EnhancedVolcano(
  filtered_genes,
  lab = ifelse(filtered_genes$p_val_adj <= 1e-6 & abs(filtered_genes$avg_log2FC) >= 1.5,
               filtered_genes$gene, NA),
  x = "avg_log2FC",
  y = "p_val_adj",
  title = "cDC2 vs Normal CD4 T cells",
  pCutoff = 1e-6,
  FCcutoff = 1.0,
  legendPosition = 'right',
  labCol = 'black',
  labFace = 'bold',
  boxedLabels = FALSE,
  pointSize = 3.0,
  labSize = 5.0,
  col = c('grey70', 'black', 'blue', 'red'),
  selectLab = filtered_genes$gene[filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_log2FC) >= 1.0]
)
Warning: One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...

Volcano Plots


filtered_genes <- de_cDC2_vs_CD4 %>%
  arrange(p_val_adj, desc(abs(avg_log2FC)))

# Add gene names as a column for labeling
filtered_genes$gene <- rownames(filtered_genes)

# Fix zero p-values
min_nonzero_pval <- min(filtered_genes$p_val_adj[filtered_genes$p_val_adj > 0])
filtered_genes$p_val_adj[filtered_genes$p_val_adj == 0] <- min_nonzero_pval * 0.1

# Create safe labels
labels <- ifelse(filtered_genes$p_val_adj <= 1e-6 & abs(filtered_genes$avg_log2FC) >= 1.5,
                 filtered_genes$gene, NA)

selected_labels <- filtered_genes$gene[filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_log2FC) >= 1.0]

if (length(selected_labels) == 0) selected_labels <- NA

EnhancedVolcano(
  filtered_genes,
  lab = labels,
  x = "avg_log2FC",
  y = "p_val_adj",
  title = "cDC2 vs Normal CD4 T cells",
  pCutoff = 1e-6,
  FCcutoff = 1.0,
  legendPosition = 'right',
  labCol = 'black',
  labFace = 'bold',
  boxedLabels = FALSE,
  pointSize = 3.0,
  labSize = 5.0,
  col = c('grey70', 'black', 'blue', 'red'),
  selectLab = selected_labels
)

NA
NA
NA

10. Enrichment Analysis-All_Pathways



# Define the output folder for cDC2 analysis results
output_folder <- "cDC2_250UP_vs_Control_250DOWN/"

# Create the output folder if it doesn't exist
if (!dir.exists(output_folder)) {
  dir.create(output_folder)
}

# Number of genes to select
UP_genes <- 250
Down_genes <- 250

# Thresholds for log fold change
logFC_up_threshold <- 1.5          # Upregulated
logFC_down_threshold <- -1         # Downregulated


cDC2_filtered$gene <- rownames(cDC2_filtered)

# Filter and arrange genes based on thresholds and adjusted p-value
filtered_genes <- cDC2_filtered %>%
  filter(avg_log2FC > logFC_up_threshold | avg_log2FC < logFC_down_threshold) %>%
  arrange(p_val_adj) %>%
  select(gene, everything())  # change 'gene' to your actual gene symbol column name


# Separate up- and downregulated genes
upregulated_genes <- filtered_genes %>% filter(avg_log2FC > logFC_up_threshold)
downregulated_genes <- filtered_genes %>% filter(avg_log2FC < logFC_down_threshold)

# Select top upregulated genes
if (nrow(upregulated_genes) < UP_genes) {
  top_upregulated_genes <- upregulated_genes
  cat("Number of upregulated genes selected:", nrow(top_upregulated_genes), "\n")
  cat("p_val_adj for last upregulated gene:", tail(top_upregulated_genes$p_val_adj, 1), "\n")
} else {
  top_upregulated_genes <- head(upregulated_genes, UP_genes)
  cat("Number of upregulated genes selected:", nrow(top_upregulated_genes), "\n")
  cat("p_val_adj for last upregulated gene:", tail(top_upregulated_genes$p_val_adj, 1), "\n")
}
Number of upregulated genes selected: 250 
p_val_adj for last upregulated gene: 0 
# Select top downregulated genes
if (nrow(downregulated_genes) < Down_genes) {
  top_downregulated_genes <- downregulated_genes
  cat("Number of downregulated genes selected:", nrow(top_downregulated_genes), "\n")
  cat("p_val_adj for last downregulated gene:", tail(top_downregulated_genes$p_val_adj, 1), "\n")
} else {
  top_downregulated_genes <- head(downregulated_genes, Down_genes)
  cat("Number of downregulated genes selected:", nrow(top_downregulated_genes), "\n")
  cat("p_val_adj for last downregulated gene:", tail(top_downregulated_genes$p_val_adj, 1), "\n")
}
Number of downregulated genes selected: 250 
p_val_adj for last downregulated gene: 6.961907e-97 
# Combine top genes for reference
top_genes <- bind_rows(top_upregulated_genes, top_downregulated_genes) %>% na.omit()

# Save gene lists
write.csv(top_upregulated_genes, paste0(output_folder, "upregulated_genes.csv"), row.names = FALSE)
write.csv(top_downregulated_genes, paste0(output_folder, "downregulated_genes.csv"), row.names = FALSE)

# Convert gene symbols to Entrez IDs
upregulated_entrez <- bitr(top_upregulated_genes$gene, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)
'select()' returned 1:1 mapping between keys and columns
Warning: 2.8% of input gene IDs are fail to map...
downregulated_entrez <- bitr(top_downregulated_genes$gene, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)
'select()' returned 1:1 mapping between keys and columns
Warning: 3.2% of input gene IDs are fail to map...
# Report missing gene symbols
missing_upregulated <- setdiff(top_upregulated_genes$gene, upregulated_entrez$SYMBOL)
missing_downregulated <- setdiff(top_downregulated_genes$gene, downregulated_entrez$SYMBOL)

cat("Missing upregulated genes:\n", missing_upregulated, "\n")
Missing upregulated genes:
 WARS AL590550.1 H2AFY C3orf14 PHB WDR34 PALM2-AKAP2 
cat("Missing downregulated genes:\n", missing_downregulated, "\n")
Missing downregulated genes:
 LINC01578 MT-ND3 HIST1H4C AC119396.1 FAM102A AL138963.4 AC243960.1 AC139720.1 
# Merge Entrez IDs back to gene tables
top_upregulated_genes <- merge(top_upregulated_genes, upregulated_entrez, by.x = "gene", by.y = "SYMBOL", all.x = TRUE)
top_downregulated_genes <- merge(top_downregulated_genes, downregulated_entrez, by.x = "gene", by.y = "SYMBOL", all.x = TRUE)

# Filter genes without Entrez IDs
top_upregulated_genes <- top_upregulated_genes[!is.na(top_upregulated_genes$ENTREZID), ]
top_downregulated_genes <- top_downregulated_genes[!is.na(top_downregulated_genes$ENTREZID), ]

# Extract Entrez IDs for enrichment
upregulated_entrez <- top_upregulated_genes$ENTREZID
downregulated_entrez <- top_downregulated_genes$ENTREZID

# Functions for enrichment analysis and plotting
safe_enrichGO <- function(gene_list, title, filename) {
  if (length(gene_list) > 0) {
    result <- enrichGO(gene = gene_list, OrgDb = org.Hs.eg.db, keyType = "ENTREZID",
                       ont = "BP", pAdjustMethod = "BH", pvalueCutoff = 0.05, readable = TRUE)
    if (!is.null(result) && nrow(as.data.frame(result)) > 0) {
      p <- dotplot(result, showCategory = 10, title = title)
      print(p)  
      ggsave(paste0(output_folder, gsub(".csv", "_dotplot.png", filename)), plot = p, width = 8, height = 6)
      write.csv(as.data.frame(result), file = paste0(output_folder, filename), row.names = FALSE)
    } else {
      message(paste("No significant GO enrichment for:", title))
    }
  } else {
    message(paste("No genes found for:", title))
  }
}

safe_enrichKEGG <- function(entrez_list, title, filename) {
  if (length(entrez_list) > 0) {
    result <- enrichKEGG(gene = entrez_list, organism = "hsa", pvalueCutoff = 0.05)
    if (!is.null(result) && nrow(as.data.frame(result)) > 0) {
      result <- setReadable(result, OrgDb = org.Hs.eg.db, keyType = "ENTREZID")
      p <- dotplot(result, showCategory = 10, title = title)
      print(p)
      ggsave(paste0(output_folder, gsub(".csv", "_dotplot.png", filename)), plot = p, width = 8, height = 6)
      write.csv(as.data.frame(result), file = paste0(output_folder, filename), row.names = FALSE)
    } else {
      message(paste("No significant KEGG pathways for:", title))
    }
  } else {
    message(paste("No genes found for:", title))
  }
}

safe_enrichReactome <- function(entrez_list, title, filename) {
  if (length(entrez_list) > 0) {
    result <- enrichPathway(gene = entrez_list, organism = "human", pvalueCutoff = 0.05)
    if (!is.null(result) && nrow(as.data.frame(result)) > 0) {
      result <- setReadable(result, OrgDb = org.Hs.eg.db, keyType = "ENTREZID")
      p <- dotplot(result, showCategory = 10, title = title)
      print(p)
      ggsave(paste0(output_folder, gsub(".csv", "_dotplot.png", filename)), plot = p, width = 8, height = 6)
      write.csv(as.data.frame(result), file = paste0(output_folder, filename), row.names = FALSE)
    } else {
      message(paste("No significant Reactome pathways for:", title))
    }
  } else {
    message(paste("No genes found for:", title))
  }
}

# Run enrichment analyses and save results
safe_enrichGO(top_upregulated_genes$ENTREZID, "GO Enrichment for Upregulated Genes in cDC2", "upregulated_GO_results.csv")

safe_enrichGO(top_downregulated_genes$ENTREZID, "GO Enrichment for Downregulated Genes in cDC2", "downregulated_GO_results.csv")



safe_enrichKEGG(upregulated_entrez, "KEGG Pathway Enrichment for Upregulated Genes in cDC2", "upregulated_KEGG_results.csv")

safe_enrichKEGG(downregulated_entrez, "KEGG Pathway Enrichment for Downregulated Genes in cDC2", "downregulated_KEGG_results.csv")


safe_enrichReactome(upregulated_entrez, "Reactome Pathway Enrichment for Upregulated Genes in cDC2", "upregulated_Reactome_results.csv")

safe_enrichReactome(downregulated_entrez, "Reactome Pathway Enrichment for Downregulated Genes in cDC2", "downregulated_Reactome_results.csv")

NA
NA

Enrichment Analysis_Hallmark


# Load necessary libraries
library(clusterProfiler)
library(org.Hs.eg.db)
library(msigdbr)
library(enrichplot)
library(ggplot2)
library(dplyr)

# Define the output folder where the results will be saved
output_folder <- "cDC2_250UP_vs_Control_250DOWN/"

# Create the output folder if it doesn't exist
if (!dir.exists(output_folder)) {
  dir.create(output_folder)
}

# Load Hallmark gene sets from msigdbr
hallmark_sets <- msigdbr(species = "Homo sapiens", collection = "H")  # "H" is for Hallmark gene sets

# Convert gene symbols to uppercase for consistency
top_upregulated_genes$gene <- toupper(top_upregulated_genes$gene)
top_downregulated_genes$gene <- toupper(top_downregulated_genes$gene)

# Check for overlap between your upregulated/downregulated genes and Hallmark gene sets
upregulated_in_hallmark <- intersect(top_upregulated_genes$gene, hallmark_sets$gene_symbol)
downregulated_in_hallmark <- intersect(top_downregulated_genes$gene, hallmark_sets$gene_symbol)

# Print the number of overlapping genes for both upregulated and downregulated genes
cat("Number of upregulated genes in Hallmark gene sets:", length(upregulated_in_hallmark), "\n")
Number of upregulated genes in Hallmark gene sets: 156 
cat("Number of downregulated genes in Hallmark gene sets:", length(downregulated_in_hallmark), "\n")
Number of downregulated genes in Hallmark gene sets: 84 
# If there are genes to analyze, proceed with enrichment analysis
if (length(upregulated_in_hallmark) > 0) {
  # Perform enrichment analysis for upregulated genes using Hallmark gene sets
  hallmark_up <- enricher(gene = upregulated_in_hallmark, 
                          TERM2GENE = hallmark_sets[, c("gs_name", "gene_symbol")],  # Ensure TERM2GENE uses correct columns
                          pvalueCutoff = 0.05)
  # Check if results exist
  if (!is.null(hallmark_up) && nrow(hallmark_up) > 0) {
    # Visualize results if available
    up_dotplot <- dotplot(hallmark_up, showCategory = 20, title = "Hallmark Pathway Enrichment for Upregulated Genes in cDC2")
    
    # Display the plot in the notebook
    print(up_dotplot)
    
    # Save the dotplot to a PNG file
    ggsave(paste0(output_folder, "hallmark_upregulated_dotplot.png"), plot = up_dotplot, width = 10, height = 8)
    
    # Optionally, save the results as CSV
    write.csv(as.data.frame(hallmark_up), file = paste0(output_folder, "hallmark_upregulated_enrichment.csv"), row.names = FALSE)
  } else {
    cat("No significant enrichment found for upregulated genes.\n")
  }
} else {
  cat("No upregulated genes overlap with Hallmark gene sets.\n")
}


if (length(downregulated_in_hallmark) > 0) {
  # Perform enrichment analysis for downregulated genes using Hallmark gene sets
  hallmark_down <- enricher(gene = downregulated_in_hallmark, 
                            TERM2GENE = hallmark_sets[, c("gs_name", "gene_symbol")],  # Ensure TERM2GENE uses correct columns
                            pvalueCutoff = 0.05)
  # Check if results exist
  if (!is.null(hallmark_down) && nrow(hallmark_down) > 0) {
    # Visualize results if available
    down_dotplot <- dotplot(hallmark_down, showCategory = 20, title = "Hallmark Pathway Enrichment for Downregulated Genes in cDC2")
    
    # Display the plot in the notebook
    print(down_dotplot)
    
    # Save the dotplot to a PNG file
    ggsave(paste0(output_folder, "hallmark_downregulated_dotplot.png"), plot = down_dotplot, width = 10, height = 8)
    
    # Optionally, save the results as CSV
    write.csv(as.data.frame(hallmark_down), file = paste0(output_folder, "hallmark_downregulated_enrichment.csv"), row.names = FALSE)
  } else {
    cat("No significant enrichment found for downregulated genes.\n")
  }
} else {
  cat("No downregulated genes overlap with Hallmark gene sets.\n")
}

NA
NA
NA
LS0tCnRpdGxlOiAiY0RDMiB2cyBDb250cm9sX2ZpbHRyZWRfb25fbWVhbiIKYXV0aG9yOiBOYXNpciBNYWhtb29kIEFiYmFzaQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAjIHBkZl9kb2N1bWVudDogZGVmYXVsdAogICMgd29yZF9kb2N1bWVudDogZGVmYXVsdAogICMgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogICNybWRmb3JtYXRzOjpyZWFkdGhlZG93bgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQotLS0KCiMgMS4gbG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgTG9hZCBsaWJyYXJpZXMKbGlicmFyeShTZXVyYXQpCgoKIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQpsaWJyYXJ5KGVucmljaHBsb3QpCmxpYnJhcnkoUmVhY3RvbWVQQSkKbGlicmFyeShET1NFKSAjIEZvciBHU0VBIGFuYWx5c2lzCmxpYnJhcnkoZ2dwbG90MikgIyBFbnN1cmUgZ2dwbG90MiBpcyBhdmFpbGFibGUgZm9yIHBsb3R0aW5nCmxpYnJhcnkoZHBseXIpCgpgYGAKCiMgMi4gTG9hZCB0aGUgZmlsdGVyZWQgbGlzdCBvbiBtZWFuIGV4cHJlc3Npb24KYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KCmxvYWQoIi9ob21lL2Jpb2luZm8vMjAyNV9OZXdIYXJtb255X0ludGVncmF0ZWRfRmlsZXMvMC1pbXBfUm9iai8wLXJvYmovNS1IYXJtb255X0ludGVncmF0ZWRfQWxsX3NhbXBsZXNfTWVyZ2VkX0NENFRjZWxsc19maW5hbF9SZXNvbHV0aW9uX1NlbGVjdGVkXzAuOF9BRFRfTm9ybWFsaXplZF9jbGVhbmVkX210LnJvYmoiKQoKYGBgCgojIDMuIERpc3RyaWJ1dGlvbiBvZiBjZWxscyBpbiBtYWxpZ25hbnQgQ2x1c3RlcnMKYGBge3IgLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD04fQoKIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKCiMgU3RlcCAxOiBTdWJzZXQgbWFsaWduYW50IGNlbGxzIChleGNsdWRlIFBCTUMgYW5kIFBCTUNfMTB4KQptYWxpZ25hbnRfY2VsbHMgPC0gV2hpY2hDZWxscyhBbGxfc2FtcGxlc19NZXJnZWQsIGV4cHJlc3Npb24gPSAhKGNlbGxfbGluZSAlaW4lIGMoIlBCTUMiLCAiUEJNQ18xMHgiKSkpCgojIFN0ZXAgMjogRXh0cmFjdCBwcmVkaWN0ZWQuY2VsbHR5cGUubDIgdmFsdWVzIGZvciBtYWxpZ25hbnQgY2VsbHMKbWFsaWduYW50X2NlbGx0eXBlcyA8LSBBbGxfc2FtcGxlc19NZXJnZWRAbWV0YS5kYXRhW21hbGlnbmFudF9jZWxscywgInByZWRpY3RlZC5jZWxsdHlwZS5sMiIsIGRyb3AgPSBGQUxTRV0KCiMgU3RlcCAzOiBDcmVhdGUgYSBmcmVxdWVuY3kgdGFibGUKY2VsbHR5cGVfdGFibGUgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShtYWxpZ25hbnRfY2VsbHR5cGVzJHByZWRpY3RlZC5jZWxsdHlwZS5sMikpCmNvbG5hbWVzKGNlbGx0eXBlX3RhYmxlKSA8LSBjKCJjZWxsX3R5cGUiLCAibkNlbGxzIikKCiMgU3RlcCA0OiBHZW5lcmF0ZSBhIHNvZnQgY29sb3IgcGFsZXR0ZSAoU2V0MiBzdXBwb3J0cyB1cCB0byA4IGJ5IGRlZmF1bHQpCm5fY29sb3JzIDwtIGxlbmd0aCh1bmlxdWUoY2VsbHR5cGVfdGFibGUkY2VsbF90eXBlKSkKYmFzZV9wYWxldHRlIDwtIGJyZXdlci5wYWwobWluKG5fY29sb3JzLCA4KSwgIlNldDIiKQoKIyBJZiA+OCBjb2xvcnMsIGV4dGVuZCB3aXRoICJQYWlyZWQiIG9yIGxvb3AgZXhpc3RpbmcgcGFsZXR0ZQppZiAobl9jb2xvcnMgPiA4KSB7CiAgZXh0cmFfY29sb3JzIDwtIGJyZXdlci5wYWwobWluKG5fY29sb3JzIC0gOCwgMTIpLCAiUGFpcmVkIikKICBjb2xvcl9wYWxldHRlIDwtIGMoYmFzZV9wYWxldHRlLCBleHRyYV9jb2xvcnMpCn0gZWxzZSB7CiAgY29sb3JfcGFsZXR0ZSA8LSBiYXNlX3BhbGV0dGUKfQoKIyBSZXBlYXQgaWYgbm90IGVub3VnaCAoZmFsbGJhY2spCmlmIChsZW5ndGgoY29sb3JfcGFsZXR0ZSkgPCBuX2NvbG9ycykgewogIGNvbG9yX3BhbGV0dGUgPC0gcmVwKGNvbG9yX3BhbGV0dGUsIGxlbmd0aC5vdXQgPSBuX2NvbG9ycykKfQoKIyBTdGVwIDU6IFBsb3QKY2VsbHR5cGVfYmFycGxvdCA8LSBnZ3Bsb3QoY2VsbHR5cGVfdGFibGUsIGFlcyh4ID0gcmVvcmRlcihjZWxsX3R5cGUsIC1uQ2VsbHMpLCB5ID0gbkNlbGxzLCBmaWxsID0gY2VsbF90eXBlKSkgKwogIGdlb21fY29sKCkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG5DZWxscyksIHZqdXN0ID0gLTAuMjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcl9wYWxldHRlKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBnZ3RpdGxlKCJDZWxsIFR5cGUgQ29tcG9zaXRpb24gaW4gTWFsaWduYW50IENlbGwgTGluZXMiKSArCiAgeGxhYigiUHJlZGljdGVkIENlbGwgVHlwZSAoQXppbXV0aCkiKSArCiAgeWxhYigiTnVtYmVyIG9mIENlbGxzIikKCiMgU3RlcCA2OiBQcmludCB0aGUgcGxvdApwcmludChjZWxsdHlwZV9iYXJwbG90KQpgYGAKIyA0LiBEaXN0cmlidXRpb24gb2YgY2VsbHMgaW4gbWFsaWduYW50IENsdXN0ZXJzCmBgYHtyICwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQoKIyBTdGVwIDE6IElkZW50aWZ5IEhTUEMgY2VsbHMgYmFzZWQgb24gQXppbXV0aCBhbm5vdGF0aW9uCmNEQzJfY2VsbHMgPC0gV2hpY2hDZWxscyhBbGxfc2FtcGxlc19NZXJnZWQsIGV4cHJlc3Npb24gPSBwcmVkaWN0ZWQuY2VsbHR5cGUubDIgPT0gImNEQzIiKQoKIyBTdGVwIDI6IEdldCBjbHVzdGVyIGFzc2lnbm1lbnRzIG9mIEhTUEMgY2VsbHMKY0RDMl9jbHVzdGVycyA8LSBBbGxfc2FtcGxlc19NZXJnZWRAbWV0YS5kYXRhW2NEQzJfY2VsbHMsICJzZXVyYXRfY2x1c3RlcnMiLCBkcm9wID0gRkFMU0VdCgojIFN0ZXAgMzogQ3JlYXRlIGEgZnJlcXVlbmN5IHRhYmxlCmNEQzJfY2x1c3Rlcl9jb3VudHMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShjREMyX2NsdXN0ZXJzJHNldXJhdF9jbHVzdGVycykpCmNvbG5hbWVzKGNEQzJfY2x1c3Rlcl9jb3VudHMpIDwtIGMoIkNsdXN0ZXIiLCAibmNEQzIiKQoKIyBTdGVwIDQ6IEdlbmVyYXRlIGNvbG9yIHBhbGV0dGUgKGFkanVzdGFibGUgaWYgeW91IGhhdmUgbWFueSBjbHVzdGVycykKbl9jb2xvcnMgPC0gbGVuZ3RoKHVuaXF1ZShjREMyX2NsdXN0ZXJfY291bnRzJENsdXN0ZXIpKQpiYXJfY29sb3JzIDwtIGJyZXdlci5wYWwobWluKG5fY29sb3JzLCA4KSwgIlNldDIiKQppZiAobl9jb2xvcnMgPiBsZW5ndGgoYmFyX2NvbG9ycykpIHsKICBiYXJfY29sb3JzIDwtIHJlcChiYXJfY29sb3JzLCBsZW5ndGgub3V0ID0gbl9jb2xvcnMpCn0KCiMgU3RlcCA1OiBQbG90CmNEQzJfY2x1c3Rlcl9wbG90IDwtIGdncGxvdChjREMyX2NsdXN0ZXJfY291bnRzLCBhZXMoeCA9IHJlb3JkZXIoQ2x1c3RlciwgLW5jREMyKSwgeSA9IG5jREMyLCBmaWxsID0gQ2x1c3RlcikpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbmNEQzIpLCB2anVzdCA9IC0wLjI1KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYmFyX2NvbG9ycykgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBnZ3RpdGxlKCJjREMyIENlbGxzIHBlciBTZXVyYXQgQ2x1c3RlciIpICsKICB4bGFiKCJTZXVyYXQgQ2x1c3RlciIpICsKICB5bGFiKCJOdW1iZXIgb2YgY0RDMiBDZWxscyIpCgpwcmludChjREMyX2NsdXN0ZXJfcGxvdCkKCmBgYAojIDUuIFN1YnNldCB0aGUgY0RDMiBDZWxscyAoQmFzZWQgb24gQXppbXV0aCBBbm5vdGF0aW9uKQpgYGB7ciAsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwfQoKIyBTdWJzZXQgY0RDMiBjZWxscwpjREMyX2NlbGxzIDwtIHN1YnNldChBbGxfc2FtcGxlc19NZXJnZWQsIHN1YnNldCA9IHByZWRpY3RlZC5jZWxsdHlwZS5sMiA9PSAiY0RDMiIpCgoKYGBgCgoKIyA2LiBTdWJzZXQgQ29udHJvbHMKYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KCkNENF9ub3JtYWwgPC0gc3Vic2V0KAogIEFsbF9zYW1wbGVzX01lcmdlZCwKICBzdWJzZXQgPSBjZWxsX2xpbmUgJWluJSBjKCJQQk1DIiwgIlBCTUNfMTB4IikKKQoKCmBgYAoKIyA3LiBsYWJlbCB0aGVtIGluIHRoZSBmdWxsIG9iamVjdCBmb3IgREUgYW5hbHlzaXM6CmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CgojIDEuIFNldCBSTkEgYXMgZGVmYXVsdCBhc3NheSAoaWYgbm90IGFscmVhZHkpCkRlZmF1bHRBc3NheShBbGxfc2FtcGxlc19NZXJnZWQpIDwtICJSTkEiCgojIDIuIE5vcm1hbGl6ZSAob25seSBpZiBub3QgYWxyZWFkeSBub3JtYWxpemVkKQpBbGxfc2FtcGxlc19NZXJnZWQgPC0gTm9ybWFsaXplRGF0YSgKICBBbGxfc2FtcGxlc19NZXJnZWQsCiAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwKICBzY2FsZS5mYWN0b3IgPSAxMDAwMAopCgojIDMuIEdldCBjZWxsIG5hbWVzIGZyb20gc3Vic2V0IG9iamVjdHMKY0RDMl9jZWxsc19uYW1lcyA8LSBjb2xuYW1lcyhjREMyX2NlbGxzKQpDRDRfbm9ybWFsX2NlbGxzX25hbWVzIDwtIGNvbG5hbWVzKENENF9ub3JtYWwpCgojIDQuIExhYmVsIGdyb3VwcyBpbiB0aGUgZnVsbCBvYmplY3QKQWxsX3NhbXBsZXNfTWVyZ2VkJGNlbGxfdHlwZV9ncm91cCA8LSBOQQpBbGxfc2FtcGxlc19NZXJnZWQkY2VsbF90eXBlX2dyb3VwW2NEQzJfY2VsbHNfbmFtZXNdIDwtICJjREMyIgpBbGxfc2FtcGxlc19NZXJnZWQkY2VsbF90eXBlX2dyb3VwW0NENF9ub3JtYWxfY2VsbHNfbmFtZXNdIDwtICJOb3JtYWxfQ0Q0IgoKIyBTZXQgaWRlbnRpdGllcyBmb3IgREUKSWRlbnRzKEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gImNlbGxfdHlwZV9ncm91cCIKCiMgNS4gUGVyZm9ybSBERSBhbmFseXNpcwpkZV9jREMyX3ZzX0NENCA8LSBGaW5kTWFya2VycygKICBvYmplY3QgPSBBbGxfc2FtcGxlc19NZXJnZWQsCiAgaWRlbnQuMSA9ICJjREMyIiwKICBpZGVudC4yID0gIk5vcm1hbF9DRDQiLAogIGFzc2F5ID0gIlJOQSIsCiAgdGVzdC51c2UgPSAid2lsY294IiwKICBvbmx5LnBvcyA9IEZBTFNFCikKCiMgNi4gQWRkIG1lYW4gZXhwcmVzc2lvbiBwZXIgZ3JvdXAKcm5hX2RhdGEgPC0gR2V0QXNzYXlEYXRhKEFsbF9zYW1wbGVzX01lcmdlZCwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJkYXRhIikKZGVfY0RDMl92c19DRDQkbWVhbl9leHByX2NEQzIgPC0gcm93TWVhbnMocm5hX2RhdGFbcm93bmFtZXMoZGVfY0RDMl92c19DRDQpLCBjREMyX2NlbGxzX25hbWVzXSkKZGVfY0RDMl92c19DRDQkbWVhbl9leHByX05vcm1hbENENCA8LSByb3dNZWFucyhybmFfZGF0YVtyb3duYW1lcyhkZV9jREMyX3ZzX0NENCksIENENF9ub3JtYWxfY2VsbHNfbmFtZXNdKQoKIyA3LiBTYXZlIHJlc3VsdHMKd3JpdGUuY3N2KGRlX2NEQzJfdnNfQ0Q0LCBmaWxlID0gIkRFX2NEQzJfdnNfTm9ybWFsQ0Q0LmNzdiIsIHJvdy5uYW1lcyA9IFRSVUUpCgpgYGAKCiMgOC4gRmlsdGVyIGNEQzIgRGF0YSBCYXNlZCBvbiBNZWFuIEV4cHJlc3Npb24KYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KCiMgQXBwbHkgdGhlIGZpbHRlciB0byB0aGUgY0RDMiBERSByZXN1bHRzIHRvIHJlbW92ZSBnZW5lcyB3aXRoIG1lYW4gZXhwcmVzc2lvbiA8IDAuMiBpbiBib3RoIGdyb3VwcwpjREMyX2ZpbHRlcmVkIDwtIGRlX2NEQzJfdnNfQ0Q0ICU+JQogIGZpbHRlcighKG1lYW5fZXhwcl9jREMyIDwgMC4yICYgbWVhbl9leHByX05vcm1hbENENCA8IDAuMikpCgojIFNhdmUgdGhlIGZpbHRlcmVkIERFIHJlc3VsdHMKd3JpdGUuY3N2KGNEQzJfZmlsdGVyZWQsIGZpbGUgPSAiY0RDMl92c19Ob3JtYWxDRDRfZmlsdGVyZWRfbWVhbl9leHByZXNzaW9uLmNzdiIsIHJvdy5uYW1lcyA9IFRSVUUpCgoKCgpgYGAKCgojIDkuIFZvbGNhbm8gUGxvdHMKYGBge3IgLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCiMgU29ydCBnZW5lcyBieSBhZGp1c3RlZCBwLXZhbHVlIGFuZCBhYnNvbHV0ZSBsb2cyIGZvbGQgY2hhbmdlCmZpbHRlcmVkX2dlbmVzIDwtIGRlX2NEQzJfdnNfQ0Q0ICU+JQogIGFycmFuZ2UocF92YWxfYWRqLCBkZXNjKGFicyhhdmdfbG9nMkZDKSkpCgojIEFkZCBnZW5lIG5hbWVzIGFzIGEgY29sdW1uIGZvciBsYWJlbGluZwpmaWx0ZXJlZF9nZW5lcyRnZW5lIDwtIHJvd25hbWVzKGZpbHRlcmVkX2dlbmVzKQoKCiMgQ3JlYXRlIHRoZSB2b2xjYW5vIHBsb3QgZm9yIGNEQzIgdnMgbm9ybWFsIENENCBUIGNlbGxzCkVuaGFuY2VkVm9sY2FubygKICBmaWx0ZXJlZF9nZW5lcywKICBsYWIgPSBpZmVsc2UoZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDFlLTYgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZzJGQykgPj0gMS41LAogICAgICAgICAgICAgICBmaWx0ZXJlZF9nZW5lcyRnZW5lLCBOQSksCiAgeCA9ICJhdmdfbG9nMkZDIiwKICB5ID0gInBfdmFsX2FkaiIsCiAgdGl0bGUgPSAiY0RDMiB2cyBOb3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHBDdXRvZmYgPSAxZS02LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywKICBsYWJDb2wgPSAnYmxhY2snLAogIGxhYkZhY2UgPSAnYm9sZCcsCiAgYm94ZWRMYWJlbHMgPSBGQUxTRSwKICBwb2ludFNpemUgPSAzLjAsCiAgbGFiU2l6ZSA9IDUuMCwKICBjb2wgPSBjKCdncmV5NzAnLCAnYmxhY2snLCAnYmx1ZScsICdyZWQnKSwKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuMF0KKQoKCgpgYGAKIyMgVm9sY2FubyBQbG90cwpgYGB7ciAsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNn0KCmZpbHRlcmVkX2dlbmVzIDwtIGRlX2NEQzJfdnNfQ0Q0ICU+JQogIGFycmFuZ2UocF92YWxfYWRqLCBkZXNjKGFicyhhdmdfbG9nMkZDKSkpCgojIEFkZCBnZW5lIG5hbWVzIGFzIGEgY29sdW1uIGZvciBsYWJlbGluZwpmaWx0ZXJlZF9nZW5lcyRnZW5lIDwtIHJvd25hbWVzKGZpbHRlcmVkX2dlbmVzKQoKIyBGaXggemVybyBwLXZhbHVlcwptaW5fbm9uemVyb19wdmFsIDwtIG1pbihmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGpbZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqID4gMF0pCmZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaltmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPT0gMF0gPC0gbWluX25vbnplcm9fcHZhbCAqIDAuMQoKIyBDcmVhdGUgc2FmZSBsYWJlbHMKbGFiZWxzIDwtIGlmZWxzZShmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMWUtNiAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nMkZDKSA+PSAxLjUsCiAgICAgICAgICAgICAgICAgZmlsdGVyZWRfZ2VuZXMkZ2VuZSwgTkEpCgpzZWxlY3RlZF9sYWJlbHMgPC0gZmlsdGVyZWRfZ2VuZXMkZ2VuZVtmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMC4wNSAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nMkZDKSA+PSAxLjBdCgppZiAobGVuZ3RoKHNlbGVjdGVkX2xhYmVscykgPT0gMCkgc2VsZWN0ZWRfbGFiZWxzIDwtIE5BCgpFbmhhbmNlZFZvbGNhbm8oCiAgZmlsdGVyZWRfZ2VuZXMsCiAgbGFiID0gbGFiZWxzLAogIHggPSAiYXZnX2xvZzJGQyIsCiAgeSA9ICJwX3ZhbF9hZGoiLAogIHRpdGxlID0gImNEQzIgdnMgTm9ybWFsIENENCBUIGNlbGxzIiwKICBwQ3V0b2ZmID0gMWUtNiwKICBGQ2N1dG9mZiA9IDEuMCwKICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCiAgbGFiQ29sID0gJ2JsYWNrJywKICBsYWJGYWNlID0gJ2JvbGQnLAogIGJveGVkTGFiZWxzID0gRkFMU0UsCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksCiAgc2VsZWN0TGFiID0gc2VsZWN0ZWRfbGFiZWxzCikKCgoKYGBgCgojIDEwLiBFbnJpY2htZW50IEFuYWx5c2lzLUFsbF9QYXRod2F5cwpgYGB7ciAsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEyfQoKCiMgRGVmaW5lIHRoZSBvdXRwdXQgZm9sZGVyIGZvciBjREMyIGFuYWx5c2lzIHJlc3VsdHMKb3V0cHV0X2ZvbGRlciA8LSAiY0RDMl8yNTBVUF92c19Db250cm9sXzI1MERPV04vIgoKIyBDcmVhdGUgdGhlIG91dHB1dCBmb2xkZXIgaWYgaXQgZG9lc24ndCBleGlzdAppZiAoIWRpci5leGlzdHMob3V0cHV0X2ZvbGRlcikpIHsKICBkaXIuY3JlYXRlKG91dHB1dF9mb2xkZXIpCn0KCiMgTnVtYmVyIG9mIGdlbmVzIHRvIHNlbGVjdApVUF9nZW5lcyA8LSAyNTAKRG93bl9nZW5lcyA8LSAyNTAKCiMgVGhyZXNob2xkcyBmb3IgbG9nIGZvbGQgY2hhbmdlCmxvZ0ZDX3VwX3RocmVzaG9sZCA8LSAxLjUgICAgICAgICAgIyBVcHJlZ3VsYXRlZApsb2dGQ19kb3duX3RocmVzaG9sZCA8LSAtMSAgICAgICAgICMgRG93bnJlZ3VsYXRlZAoKCmNEQzJfZmlsdGVyZWQkZ2VuZSA8LSByb3duYW1lcyhjREMyX2ZpbHRlcmVkKQoKIyBGaWx0ZXIgYW5kIGFycmFuZ2UgZ2VuZXMgYmFzZWQgb24gdGhyZXNob2xkcyBhbmQgYWRqdXN0ZWQgcC12YWx1ZQpmaWx0ZXJlZF9nZW5lcyA8LSBjREMyX2ZpbHRlcmVkICU+JQogIGZpbHRlcihhdmdfbG9nMkZDID4gbG9nRkNfdXBfdGhyZXNob2xkIHwgYXZnX2xvZzJGQyA8IGxvZ0ZDX2Rvd25fdGhyZXNob2xkKSAlPiUKICBhcnJhbmdlKHBfdmFsX2FkaikgJT4lCiAgc2VsZWN0KGdlbmUsIGV2ZXJ5dGhpbmcoKSkgICMgY2hhbmdlICdnZW5lJyB0byB5b3VyIGFjdHVhbCBnZW5lIHN5bWJvbCBjb2x1bW4gbmFtZQoKCiMgU2VwYXJhdGUgdXAtIGFuZCBkb3ducmVndWxhdGVkIGdlbmVzCnVwcmVndWxhdGVkX2dlbmVzIDwtIGZpbHRlcmVkX2dlbmVzICU+JSBmaWx0ZXIoYXZnX2xvZzJGQyA+IGxvZ0ZDX3VwX3RocmVzaG9sZCkKZG93bnJlZ3VsYXRlZF9nZW5lcyA8LSBmaWx0ZXJlZF9nZW5lcyAlPiUgZmlsdGVyKGF2Z19sb2cyRkMgPCBsb2dGQ19kb3duX3RocmVzaG9sZCkKCiMgU2VsZWN0IHRvcCB1cHJlZ3VsYXRlZCBnZW5lcwppZiAobnJvdyh1cHJlZ3VsYXRlZF9nZW5lcykgPCBVUF9nZW5lcykgewogIHRvcF91cHJlZ3VsYXRlZF9nZW5lcyA8LSB1cHJlZ3VsYXRlZF9nZW5lcwogIGNhdCgiTnVtYmVyIG9mIHVwcmVndWxhdGVkIGdlbmVzIHNlbGVjdGVkOiIsIG5yb3codG9wX3VwcmVndWxhdGVkX2dlbmVzKSwgIlxuIikKICBjYXQoInBfdmFsX2FkaiBmb3IgbGFzdCB1cHJlZ3VsYXRlZCBnZW5lOiIsIHRhaWwodG9wX3VwcmVndWxhdGVkX2dlbmVzJHBfdmFsX2FkaiwgMSksICJcbiIpCn0gZWxzZSB7CiAgdG9wX3VwcmVndWxhdGVkX2dlbmVzIDwtIGhlYWQodXByZWd1bGF0ZWRfZ2VuZXMsIFVQX2dlbmVzKQogIGNhdCgiTnVtYmVyIG9mIHVwcmVndWxhdGVkIGdlbmVzIHNlbGVjdGVkOiIsIG5yb3codG9wX3VwcmVndWxhdGVkX2dlbmVzKSwgIlxuIikKICBjYXQoInBfdmFsX2FkaiBmb3IgbGFzdCB1cHJlZ3VsYXRlZCBnZW5lOiIsIHRhaWwodG9wX3VwcmVndWxhdGVkX2dlbmVzJHBfdmFsX2FkaiwgMSksICJcbiIpCn0KCiMgU2VsZWN0IHRvcCBkb3ducmVndWxhdGVkIGdlbmVzCmlmIChucm93KGRvd25yZWd1bGF0ZWRfZ2VuZXMpIDwgRG93bl9nZW5lcykgewogIHRvcF9kb3ducmVndWxhdGVkX2dlbmVzIDwtIGRvd25yZWd1bGF0ZWRfZ2VuZXMKICBjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIHNlbGVjdGVkOiIsIG5yb3codG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMpLCAiXG4iKQogIGNhdCgicF92YWxfYWRqIGZvciBsYXN0IGRvd25yZWd1bGF0ZWQgZ2VuZToiLCB0YWlsKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzJHBfdmFsX2FkaiwgMSksICJcbiIpCn0gZWxzZSB7CiAgdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gaGVhZChkb3ducmVndWxhdGVkX2dlbmVzLCBEb3duX2dlbmVzKQogIGNhdCgiTnVtYmVyIG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMgc2VsZWN0ZWQ6IiwgbnJvdyh0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyksICJcbiIpCiAgY2F0KCJwX3ZhbF9hZGogZm9yIGxhc3QgZG93bnJlZ3VsYXRlZCBnZW5lOiIsIHRhaWwodG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkcF92YWxfYWRqLCAxKSwgIlxuIikKfQoKIyBDb21iaW5lIHRvcCBnZW5lcyBmb3IgcmVmZXJlbmNlCnRvcF9nZW5lcyA8LSBiaW5kX3Jvd3ModG9wX3VwcmVndWxhdGVkX2dlbmVzLCB0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcykgJT4lIG5hLm9taXQoKQoKIyBTYXZlIGdlbmUgbGlzdHMKd3JpdGUuY3N2KHRvcF91cHJlZ3VsYXRlZF9nZW5lcywgcGFzdGUwKG91dHB1dF9mb2xkZXIsICJ1cHJlZ3VsYXRlZF9nZW5lcy5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdih0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcywgcGFzdGUwKG91dHB1dF9mb2xkZXIsICJkb3ducmVndWxhdGVkX2dlbmVzLmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgQ29udmVydCBnZW5lIHN5bWJvbHMgdG8gRW50cmV6IElEcwp1cHJlZ3VsYXRlZF9lbnRyZXogPC0gYml0cih0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgZnJvbVR5cGUgPSAiU1lNQk9MIiwgdG9UeXBlID0gIkVOVFJFWklEIiwgT3JnRGIgPSBvcmcuSHMuZWcuZGIpCmRvd25yZWd1bGF0ZWRfZW50cmV6IDwtIGJpdHIodG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgZnJvbVR5cGUgPSAiU1lNQk9MIiwgdG9UeXBlID0gIkVOVFJFWklEIiwgT3JnRGIgPSBvcmcuSHMuZWcuZGIpCgojIFJlcG9ydCBtaXNzaW5nIGdlbmUgc3ltYm9scwptaXNzaW5nX3VwcmVndWxhdGVkIDwtIHNldGRpZmYodG9wX3VwcmVndWxhdGVkX2dlbmVzJGdlbmUsIHVwcmVndWxhdGVkX2VudHJleiRTWU1CT0wpCm1pc3NpbmdfZG93bnJlZ3VsYXRlZCA8LSBzZXRkaWZmKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzJGdlbmUsIGRvd25yZWd1bGF0ZWRfZW50cmV6JFNZTUJPTCkKCmNhdCgiTWlzc2luZyB1cHJlZ3VsYXRlZCBnZW5lczpcbiIsIG1pc3NpbmdfdXByZWd1bGF0ZWQsICJcbiIpCmNhdCgiTWlzc2luZyBkb3ducmVndWxhdGVkIGdlbmVzOlxuIiwgbWlzc2luZ19kb3ducmVndWxhdGVkLCAiXG4iKQoKIyBNZXJnZSBFbnRyZXogSURzIGJhY2sgdG8gZ2VuZSB0YWJsZXMKdG9wX3VwcmVndWxhdGVkX2dlbmVzIDwtIG1lcmdlKHRvcF91cHJlZ3VsYXRlZF9nZW5lcywgdXByZWd1bGF0ZWRfZW50cmV6LCBieS54ID0gImdlbmUiLCBieS55ID0gIlNZTUJPTCIsIGFsbC54ID0gVFJVRSkKdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gbWVyZ2UodG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMsIGRvd25yZWd1bGF0ZWRfZW50cmV6LCBieS54ID0gImdlbmUiLCBieS55ID0gIlNZTUJPTCIsIGFsbC54ID0gVFJVRSkKCiMgRmlsdGVyIGdlbmVzIHdpdGhvdXQgRW50cmV6IElEcwp0b3BfdXByZWd1bGF0ZWRfZ2VuZXMgPC0gdG9wX3VwcmVndWxhdGVkX2dlbmVzWyFpcy5uYSh0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkRU5UUkVaSUQpLCBdCnRvcF9kb3ducmVndWxhdGVkX2dlbmVzIDwtIHRvcF9kb3ducmVndWxhdGVkX2dlbmVzWyFpcy5uYSh0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRFTlRSRVpJRCksIF0KCiMgRXh0cmFjdCBFbnRyZXogSURzIGZvciBlbnJpY2htZW50CnVwcmVndWxhdGVkX2VudHJleiA8LSB0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkRU5UUkVaSUQKZG93bnJlZ3VsYXRlZF9lbnRyZXogPC0gdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkRU5UUkVaSUQKCiMgRnVuY3Rpb25zIGZvciBlbnJpY2htZW50IGFuYWx5c2lzIGFuZCBwbG90dGluZwpzYWZlX2VucmljaEdPIDwtIGZ1bmN0aW9uKGdlbmVfbGlzdCwgdGl0bGUsIGZpbGVuYW1lKSB7CiAgaWYgKGxlbmd0aChnZW5lX2xpc3QpID4gMCkgewogICAgcmVzdWx0IDwtIGVucmljaEdPKGdlbmUgPSBnZW5lX2xpc3QsIE9yZ0RiID0gb3JnLkhzLmVnLmRiLCBrZXlUeXBlID0gIkVOVFJFWklEIiwKICAgICAgICAgICAgICAgICAgICAgICBvbnQgPSAiQlAiLCBwQWRqdXN0TWV0aG9kID0gIkJIIiwgcHZhbHVlQ3V0b2ZmID0gMC4wNSwgcmVhZGFibGUgPSBUUlVFKQogICAgaWYgKCFpcy5udWxsKHJlc3VsdCkgJiYgbnJvdyhhcy5kYXRhLmZyYW1lKHJlc3VsdCkpID4gMCkgewogICAgICBwIDwtIGRvdHBsb3QocmVzdWx0LCBzaG93Q2F0ZWdvcnkgPSAxMCwgdGl0bGUgPSB0aXRsZSkKICAgICAgcHJpbnQocCkgIAogICAgICBnZ3NhdmUocGFzdGUwKG91dHB1dF9mb2xkZXIsIGdzdWIoIi5jc3YiLCAiX2RvdHBsb3QucG5nIiwgZmlsZW5hbWUpKSwgcGxvdCA9IHAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKICAgICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUocmVzdWx0KSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCBmaWxlbmFtZSksIHJvdy5uYW1lcyA9IEZBTFNFKQogICAgfSBlbHNlIHsKICAgICAgbWVzc2FnZShwYXN0ZSgiTm8gc2lnbmlmaWNhbnQgR08gZW5yaWNobWVudCBmb3I6IiwgdGl0bGUpKQogICAgfQogIH0gZWxzZSB7CiAgICBtZXNzYWdlKHBhc3RlKCJObyBnZW5lcyBmb3VuZCBmb3I6IiwgdGl0bGUpKQogIH0KfQoKc2FmZV9lbnJpY2hLRUdHIDwtIGZ1bmN0aW9uKGVudHJlel9saXN0LCB0aXRsZSwgZmlsZW5hbWUpIHsKICBpZiAobGVuZ3RoKGVudHJlel9saXN0KSA+IDApIHsKICAgIHJlc3VsdCA8LSBlbnJpY2hLRUdHKGdlbmUgPSBlbnRyZXpfbGlzdCwgb3JnYW5pc20gPSAiaHNhIiwgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKICAgIGlmICghaXMubnVsbChyZXN1bHQpICYmIG5yb3coYXMuZGF0YS5mcmFtZShyZXN1bHQpKSA+IDApIHsKICAgICAgcmVzdWx0IDwtIHNldFJlYWRhYmxlKHJlc3VsdCwgT3JnRGIgPSBvcmcuSHMuZWcuZGIsIGtleVR5cGUgPSAiRU5UUkVaSUQiKQogICAgICBwIDwtIGRvdHBsb3QocmVzdWx0LCBzaG93Q2F0ZWdvcnkgPSAxMCwgdGl0bGUgPSB0aXRsZSkKICAgICAgcHJpbnQocCkKICAgICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCBnc3ViKCIuY3N2IiwgIl9kb3RwbG90LnBuZyIsIGZpbGVuYW1lKSksIHBsb3QgPSBwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCiAgICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKHJlc3VsdCksIGZpbGUgPSBwYXN0ZTAob3V0cHV0X2ZvbGRlciwgZmlsZW5hbWUpLCByb3cubmFtZXMgPSBGQUxTRSkKICAgIH0gZWxzZSB7CiAgICAgIG1lc3NhZ2UocGFzdGUoIk5vIHNpZ25pZmljYW50IEtFR0cgcGF0aHdheXMgZm9yOiIsIHRpdGxlKSkKICAgIH0KICB9IGVsc2UgewogICAgbWVzc2FnZShwYXN0ZSgiTm8gZ2VuZXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICB9Cn0KCnNhZmVfZW5yaWNoUmVhY3RvbWUgPC0gZnVuY3Rpb24oZW50cmV6X2xpc3QsIHRpdGxlLCBmaWxlbmFtZSkgewogIGlmIChsZW5ndGgoZW50cmV6X2xpc3QpID4gMCkgewogICAgcmVzdWx0IDwtIGVucmljaFBhdGh3YXkoZ2VuZSA9IGVudHJlel9saXN0LCBvcmdhbmlzbSA9ICJodW1hbiIsIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiAgICBpZiAoIWlzLm51bGwocmVzdWx0KSAmJiBucm93KGFzLmRhdGEuZnJhbWUocmVzdWx0KSkgPiAwKSB7CiAgICAgIHJlc3VsdCA8LSBzZXRSZWFkYWJsZShyZXN1bHQsIE9yZ0RiID0gb3JnLkhzLmVnLmRiLCBrZXlUeXBlID0gIkVOVFJFWklEIikKICAgICAgcCA8LSBkb3RwbG90KHJlc3VsdCwgc2hvd0NhdGVnb3J5ID0gMTAsIHRpdGxlID0gdGl0bGUpCiAgICAgIHByaW50KHApCiAgICAgIGdnc2F2ZShwYXN0ZTAob3V0cHV0X2ZvbGRlciwgZ3N1YigiLmNzdiIsICJfZG90cGxvdC5wbmciLCBmaWxlbmFtZSkpLCBwbG90ID0gcCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQogICAgICB3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShyZXN1bHQpLCBmaWxlID0gcGFzdGUwKG91dHB1dF9mb2xkZXIsIGZpbGVuYW1lKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgICB9IGVsc2UgewogICAgICBtZXNzYWdlKHBhc3RlKCJObyBzaWduaWZpY2FudCBSZWFjdG9tZSBwYXRod2F5cyBmb3I6IiwgdGl0bGUpKQogICAgfQogIH0gZWxzZSB7CiAgICBtZXNzYWdlKHBhc3RlKCJObyBnZW5lcyBmb3VuZCBmb3I6IiwgdGl0bGUpKQogIH0KfQoKIyBSdW4gZW5yaWNobWVudCBhbmFseXNlcyBhbmQgc2F2ZSByZXN1bHRzCnNhZmVfZW5yaWNoR08odG9wX3VwcmVndWxhdGVkX2dlbmVzJEVOVFJFWklELCAiR08gRW5yaWNobWVudCBmb3IgVXByZWd1bGF0ZWQgR2VuZXMgaW4gY0RDMiIsICJ1cHJlZ3VsYXRlZF9HT19yZXN1bHRzLmNzdiIpCnNhZmVfZW5yaWNoR08odG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkRU5UUkVaSUQsICJHTyBFbnJpY2htZW50IGZvciBEb3ducmVndWxhdGVkIEdlbmVzIGluIGNEQzIiLCAiZG93bnJlZ3VsYXRlZF9HT19yZXN1bHRzLmNzdiIpCgoKc2FmZV9lbnJpY2hLRUdHKHVwcmVndWxhdGVkX2VudHJleiwgIktFR0cgUGF0aHdheSBFbnJpY2htZW50IGZvciBVcHJlZ3VsYXRlZCBHZW5lcyBpbiBjREMyIiwgInVwcmVndWxhdGVkX0tFR0dfcmVzdWx0cy5jc3YiKQpzYWZlX2VucmljaEtFR0coZG93bnJlZ3VsYXRlZF9lbnRyZXosICJLRUdHIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBjREMyIiwgImRvd25yZWd1bGF0ZWRfS0VHR19yZXN1bHRzLmNzdiIpCgpzYWZlX2VucmljaFJlYWN0b21lKHVwcmVndWxhdGVkX2VudHJleiwgIlJlYWN0b21lIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgVXByZWd1bGF0ZWQgR2VuZXMgaW4gY0RDMiIsICJ1cHJlZ3VsYXRlZF9SZWFjdG9tZV9yZXN1bHRzLmNzdiIpCnNhZmVfZW5yaWNoUmVhY3RvbWUoZG93bnJlZ3VsYXRlZF9lbnRyZXosICJSZWFjdG9tZSBQYXRod2F5IEVucmljaG1lbnQgZm9yIERvd25yZWd1bGF0ZWQgR2VuZXMgaW4gY0RDMiIsICJkb3ducmVndWxhdGVkX1JlYWN0b21lX3Jlc3VsdHMuY3N2IikKCgpgYGAKCiMjIEVucmljaG1lbnQgQW5hbHlzaXNfSGFsbG1hcmsKYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShtc2lnZGJyKQpsaWJyYXJ5KGVucmljaHBsb3QpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKCiMgRGVmaW5lIHRoZSBvdXRwdXQgZm9sZGVyIHdoZXJlIHRoZSByZXN1bHRzIHdpbGwgYmUgc2F2ZWQKb3V0cHV0X2ZvbGRlciA8LSAiY0RDMl8yNTBVUF92c19Db250cm9sXzI1MERPV04vIgoKIyBDcmVhdGUgdGhlIG91dHB1dCBmb2xkZXIgaWYgaXQgZG9lc24ndCBleGlzdAppZiAoIWRpci5leGlzdHMob3V0cHV0X2ZvbGRlcikpIHsKICBkaXIuY3JlYXRlKG91dHB1dF9mb2xkZXIpCn0KCiMgTG9hZCBIYWxsbWFyayBnZW5lIHNldHMgZnJvbSBtc2lnZGJyCmhhbGxtYXJrX3NldHMgPC0gbXNpZ2RicihzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIsIGNvbGxlY3Rpb24gPSAiSCIpICAjICJIIiBpcyBmb3IgSGFsbG1hcmsgZ2VuZSBzZXRzCgojIENvbnZlcnQgZ2VuZSBzeW1ib2xzIHRvIHVwcGVyY2FzZSBmb3IgY29uc2lzdGVuY3kKdG9wX3VwcmVndWxhdGVkX2dlbmVzJGdlbmUgPC0gdG91cHBlcih0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSkKdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSA8LSB0b3VwcGVyKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzJGdlbmUpCgojIENoZWNrIGZvciBvdmVybGFwIGJldHdlZW4geW91ciB1cHJlZ3VsYXRlZC9kb3ducmVndWxhdGVkIGdlbmVzIGFuZCBIYWxsbWFyayBnZW5lIHNldHMKdXByZWd1bGF0ZWRfaW5faGFsbG1hcmsgPC0gaW50ZXJzZWN0KHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBoYWxsbWFya19zZXRzJGdlbmVfc3ltYm9sKQpkb3ducmVndWxhdGVkX2luX2hhbGxtYXJrIDwtIGludGVyc2VjdCh0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBoYWxsbWFya19zZXRzJGdlbmVfc3ltYm9sKQoKIyBQcmludCB0aGUgbnVtYmVyIG9mIG92ZXJsYXBwaW5nIGdlbmVzIGZvciBib3RoIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIGdlbmVzCmNhdCgiTnVtYmVyIG9mIHVwcmVndWxhdGVkIGdlbmVzIGluIEhhbGxtYXJrIGdlbmUgc2V0czoiLCBsZW5ndGgodXByZWd1bGF0ZWRfaW5faGFsbG1hcmspLCAiXG4iKQpjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIGluIEhhbGxtYXJrIGdlbmUgc2V0czoiLCBsZW5ndGgoZG93bnJlZ3VsYXRlZF9pbl9oYWxsbWFyayksICJcbiIpCgojIElmIHRoZXJlIGFyZSBnZW5lcyB0byBhbmFseXplLCBwcm9jZWVkIHdpdGggZW5yaWNobWVudCBhbmFseXNpcwppZiAobGVuZ3RoKHVwcmVndWxhdGVkX2luX2hhbGxtYXJrKSA+IDApIHsKICAjIFBlcmZvcm0gZW5yaWNobWVudCBhbmFseXNpcyBmb3IgdXByZWd1bGF0ZWQgZ2VuZXMgdXNpbmcgSGFsbG1hcmsgZ2VuZSBzZXRzCiAgaGFsbG1hcmtfdXAgPC0gZW5yaWNoZXIoZ2VuZSA9IHVwcmVndWxhdGVkX2luX2hhbGxtYXJrLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBURVJNMkdFTkUgPSBoYWxsbWFya19zZXRzWywgYygiZ3NfbmFtZSIsICJnZW5lX3N5bWJvbCIpXSwgICMgRW5zdXJlIFRFUk0yR0VORSB1c2VzIGNvcnJlY3QgY29sdW1ucwogICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiAgIyBDaGVjayBpZiByZXN1bHRzIGV4aXN0CiAgaWYgKCFpcy5udWxsKGhhbGxtYXJrX3VwKSAmJiBucm93KGhhbGxtYXJrX3VwKSA+IDApIHsKICAgICMgVmlzdWFsaXplIHJlc3VsdHMgaWYgYXZhaWxhYmxlCiAgICB1cF9kb3RwbG90IDwtIGRvdHBsb3QoaGFsbG1hcmtfdXAsIHNob3dDYXRlZ29yeSA9IDIwLCB0aXRsZSA9ICJIYWxsbWFyayBQYXRod2F5IEVucmljaG1lbnQgZm9yIFVwcmVndWxhdGVkIEdlbmVzIGluIGNEQzIiKQogICAgCiAgICAjIERpc3BsYXkgdGhlIHBsb3QgaW4gdGhlIG5vdGVib29rCiAgICBwcmludCh1cF9kb3RwbG90KQogICAgCiAgICAjIFNhdmUgdGhlIGRvdHBsb3QgdG8gYSBQTkcgZmlsZQogICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfdXByZWd1bGF0ZWRfZG90cGxvdC5wbmciKSwgcGxvdCA9IHVwX2RvdHBsb3QsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCiAgICAKICAgICMgT3B0aW9uYWxseSwgc2F2ZSB0aGUgcmVzdWx0cyBhcyBDU1YKICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKGhhbGxtYXJrX3VwKSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfdXByZWd1bGF0ZWRfZW5yaWNobWVudC5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgfSBlbHNlIHsKICAgIGNhdCgiTm8gc2lnbmlmaWNhbnQgZW5yaWNobWVudCBmb3VuZCBmb3IgdXByZWd1bGF0ZWQgZ2VuZXMuXG4iKQogIH0KfSBlbHNlIHsKICBjYXQoIk5vIHVwcmVndWxhdGVkIGdlbmVzIG92ZXJsYXAgd2l0aCBIYWxsbWFyayBnZW5lIHNldHMuXG4iKQp9CgppZiAobGVuZ3RoKGRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmspID4gMCkgewogICMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzIGZvciBkb3ducmVndWxhdGVkIGdlbmVzIHVzaW5nIEhhbGxtYXJrIGdlbmUgc2V0cwogIGhhbGxtYXJrX2Rvd24gPC0gZW5yaWNoZXIoZ2VuZSA9IGRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVEVSTTJHRU5FID0gaGFsbG1hcmtfc2V0c1ssIGMoImdzX25hbWUiLCAiZ2VuZV9zeW1ib2wiKV0sICAjIEVuc3VyZSBURVJNMkdFTkUgdXNlcyBjb3JyZWN0IGNvbHVtbnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiAgIyBDaGVjayBpZiByZXN1bHRzIGV4aXN0CiAgaWYgKCFpcy5udWxsKGhhbGxtYXJrX2Rvd24pICYmIG5yb3coaGFsbG1hcmtfZG93bikgPiAwKSB7CiAgICAjIFZpc3VhbGl6ZSByZXN1bHRzIGlmIGF2YWlsYWJsZQogICAgZG93bl9kb3RwbG90IDwtIGRvdHBsb3QoaGFsbG1hcmtfZG93biwgc2hvd0NhdGVnb3J5ID0gMjAsIHRpdGxlID0gIkhhbGxtYXJrIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBjREMyIikKICAgIAogICAgIyBEaXNwbGF5IHRoZSBwbG90IGluIHRoZSBub3RlYm9vawogICAgcHJpbnQoZG93bl9kb3RwbG90KQogICAgCiAgICAjIFNhdmUgdGhlIGRvdHBsb3QgdG8gYSBQTkcgZmlsZQogICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfZG93bnJlZ3VsYXRlZF9kb3RwbG90LnBuZyIpLCBwbG90ID0gZG93bl9kb3RwbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQogICAgCiAgICAjIE9wdGlvbmFsbHksIHNhdmUgdGhlIHJlc3VsdHMgYXMgQ1NWCiAgICB3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShoYWxsbWFya19kb3duKSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfZG93bnJlZ3VsYXRlZF9lbnJpY2htZW50LmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKICB9IGVsc2UgewogICAgY2F0KCJObyBzaWduaWZpY2FudCBlbnJpY2htZW50IGZvdW5kIGZvciBkb3ducmVndWxhdGVkIGdlbmVzLlxuIikKICB9Cn0gZWxzZSB7CiAgY2F0KCJObyBkb3ducmVndWxhdGVkIGdlbmVzIG92ZXJsYXAgd2l0aCBIYWxsbWFyayBnZW5lIHNldHMuXG4iKQp9CgoKCmBgYAoKCgo=