4. Enrichment Analysis-All_Pathways
# Load necessary libraries
library(clusterProfiler)
library(org.Hs.eg.db)
library(enrichplot)
library(ReactomePA)
library(DOSE) # For GSEA analysis
library(ggplot2) # Ensure ggplot2 is available for plotting
library(dplyr)
# Define the output folder where the results will be saved
output_folder <- "HSPC_vs_Control/"
# Create the output folder if it doesn't exist
if (!dir.exists(output_folder)) {
dir.create(output_folder)
}
# Define the number of upregulated and downregulated genes to select
UP_genes <- 100
Down_genes <- 100
# Define threshold for differential expression selection (modified thresholds)
logFC_up_threshold <- 4 # Upregulated logFC threshold
logFC_down_threshold <- -4 # Downregulated logFC threshold
# Load your differential expression results (modify based on actual data structure)
# Malignant_CD4Tcells_vs_Normal_CD4Tcells <- read.csv("Your_DE_Results_File.csv")
# Filter the genes based on avg_log2FC and arrange by p_val_adj
filtered_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells %>%
filter(avg_log2FC > logFC_up_threshold | avg_log2FC < logFC_down_threshold) %>%
arrange(p_val_adj)
# Separate upregulated and downregulated genes
upregulated_genes <- filtered_genes %>%
filter(avg_log2FC > logFC_up_threshold)
downregulated_genes <- filtered_genes %>%
filter(avg_log2FC < logFC_down_threshold)
# Check if there are fewer than the specified number of 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 value for the last selected upregulated gene:", tail(top_upregulated_genes$p_val_adj, 1), "\n")
} else {
# Select the specified number of upregulated genes
top_upregulated_genes <- upregulated_genes %>%
head(UP_genes)
cat("Number of upregulated genes selected:", nrow(top_upregulated_genes), "\n")
cat("p_val_adj value for the last selected upregulated gene:", tail(top_upregulated_genes$p_val_adj, 1), "\n")
}
Number of upregulated genes selected: 100
p_val_adj value for the last selected upregulated gene: 0
# Check if there are fewer than the specified number of 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 value for the last selected downregulated gene:", tail(top_downregulated_genes$p_val_adj, 1), "\n")
} else {
# Select the specified number of downregulated genes
top_downregulated_genes <- downregulated_genes %>%
head(Down_genes)
cat("Number of downregulated genes selected:", nrow(top_downregulated_genes), "\n")
cat("p_val_adj value for the last selected downregulated gene:", tail(top_downregulated_genes$p_val_adj, 1), "\n")
}
Number of downregulated genes selected: 100
p_val_adj value for the last selected downregulated gene: 1.76516e-120
# Combine the top upregulated and downregulated genes
top_genes <- bind_rows(top_upregulated_genes, top_downregulated_genes)
# Check for missing genes (NAs) in the gene column and remove them
top_genes <- na.omit(top_genes)
# Save upregulated and downregulated gene results to CSV
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 for enrichment analysis, with checks for missing values
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: 3% 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: 4% of input gene IDs are fail to map...
# Check for missing Entrez IDs and retain gene names
missing_upregulated <- top_upregulated_genes$gene[!top_upregulated_genes$gene %in% upregulated_entrez$SYMBOL]
missing_downregulated <- top_downregulated_genes$gene[!top_downregulated_genes$gene %in% downregulated_entrez$SYMBOL]
# Print out the missing gene symbols for debugging
cat("Missing upregulated genes:\n", missing_upregulated, "\n")
Missing upregulated genes:
AC068672.2 AL590550.1 AC010967.1
cat("Missing downregulated genes:\n", missing_downregulated, "\n")
Missing downregulated genes:
AC119396.1 AC243960.1 FAM102A AC139720.1
# Merge the Entrez IDs back with the original data frames to retain gene names
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)
# Remove genes that couldn't be mapped to 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 analysis
upregulated_entrez <- top_upregulated_genes$ENTREZID
downregulated_entrez <- top_downregulated_genes$ENTREZID
# Define a function to safely run enrichment, plot results, and save them
safe_enrichGO <- function(gene_list, title, filename) {
if (length(gene_list) > 0) {
result <- enrichGO(gene = gene_list, OrgDb = org.Hs.eg.db, keyType = "SYMBOL",
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 enrichment found 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 found 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 found for:", title))
}
} else {
message(paste("No genes found for:", title))
}
}
# Perform enrichment analyses, generate plots, and save results
safe_enrichGO(top_upregulated_genes$gene, "GO Enrichment for Upregulated Genes", "upregulated_GO_results.csv")

safe_enrichGO(top_downregulated_genes$gene, "GO Enrichment for Downregulated Genes", "downregulated_GO_results.csv")

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

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

safe_enrichReactome(upregulated_entrez, "Reactome Pathway Enrichment for Upregulated Genes", "upregulated_Reactome_results.csv")
safe_enrichReactome(downregulated_entrez, "Reactome Pathway Enrichment for Downregulated Genes", "downregulated_Reactome_results.csv")
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 <- "HSPC_vs_Control/"
# 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: 43
cat("Number of downregulated genes in Hallmark gene sets:", length(downregulated_in_hallmark), "\n")
Number of downregulated genes in Hallmark gene sets: 35
# 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")
# 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")
# 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
LS0tCnRpdGxlOiAiSFNQQyB2cyBDb250cm9sX2ZpbHRyZWRfb25fbWVhbiIKYXV0aG9yOiBOYXNpciBNYWhtb29kIEFiYmFzaQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAjIHBkZl9kb2N1bWVudDogZGVmYXVsdAogICMgd29yZF9kb2N1bWVudDogZGVmYXVsdAogICMgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogICNybWRmb3JtYXRzOjpyZWFkdGhlZG93bgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQotLS0KCiMgMS4gbG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgTG9hZCBsaWJyYXJpZXMKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShMaWJyYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkodGliYmxlKQoKYGBgCgojIDIuIExvYWQgdGhlIGZpbHRlcmVkIGxpc3Qgb24gbWVhbiBleHByZXNzaW9uCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CgojIExvYWQgdGhlIERFIHJlc3VsdHMgZnJvbSBDU1YKZGYgPC0gcmVhZC5jc3YoIjMtUk5BX0hTUENfdnNfQ29udHJvbF9GaWx0ZXJlZF9ieV9NZWFuRXhwLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCgpERV9yZXN1bHRzX2RmIDwtIGRmCgpgYGAKCgoKCgojIDMuIFZvbGNhbm8gUGxvdHMKYGBge3IgLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCiMgQXNzdW1pbmcgeW91IGhhdmUgYSBkYXRhIGZyYW1lIG5hbWVkIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscwojIEZpbHRlciBnZW5lcyBiYXNlZCBvbiBsb3dlc3QgcC12YWx1ZXMgYnV0IGluY2x1ZGUgYWxsIGdlbmVzCmZpbHRlcmVkX2dlbmVzIDwtIG1hcmtlcnMgJT4lCiAgYXJyYW5nZShwX3ZhbF9hZGosIGRlc2MoYWJzKGF2Z19sb2cyRkMpKSkKCiMgQ3JlYXRlIHRoZSBFbmhhbmNlZFZvbGNhbm8gcGxvdCB3aXRoIHRoZSBmaWx0ZXJlZCBkYXRhCkVuaGFuY2VkVm9sY2FubygKICBmaWx0ZXJlZF9nZW5lcywgCiAgbGFiID0gaWZlbHNlKGZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAxZS02ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuNSwgZmlsdGVyZWRfZ2VuZXMkZ2VuZSwgTkEpLAogIHggPSAiYXZnX2xvZzJGQyIsIAogIHkgPSAicF92YWxfYWRqIiwKICB0aXRsZSA9ICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMoY2VsbCBsaW5lcykgdnMgbm9ybWFsIENENCBUIGNlbGxzIiwKICBwQ3V0b2ZmID0gMWUtNiwKICBGQ2N1dG9mZiA9IDEuMCwKICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsIAogIGxhYkNvbCA9ICdibGFjaycsCiAgbGFiRmFjZSA9ICdib2xkJywKICBib3hlZExhYmVscyA9IEZBTFNFLCAgIyBTZXQgdG8gRkFMU0UgdG8gcmVtb3ZlIGJveGVkIGxhYmVscwogIHBvaW50U2l6ZSA9IDMuMCwKICBsYWJTaXplID0gNS4wLAogIGNvbCA9IGMoJ2dyZXk3MCcsICdibGFjaycsICdibHVlJywgJ3JlZCcpLCAgIyBDdXN0b21pemUgcG9pbnQgY29sb3JzCiAgc2VsZWN0TGFiID0gZmlsdGVyZWRfZ2VuZXMkZ2VuZVtmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMC4wNSAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nMkZDKSA+PSAxLjBdICAjIE9ubHkgbGFiZWwgc2lnbmlmaWNhbnQgZ2VuZXMKKQoKCgpgYGAKCiMgNC4gRW5yaWNobWVudCBBbmFseXNpcy1BbGxfUGF0aHdheXMKYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQpsaWJyYXJ5KGVucmljaHBsb3QpCmxpYnJhcnkoUmVhY3RvbWVQQSkKbGlicmFyeShET1NFKSAjIEZvciBHU0VBIGFuYWx5c2lzCmxpYnJhcnkoZ2dwbG90MikgIyBFbnN1cmUgZ2dwbG90MiBpcyBhdmFpbGFibGUgZm9yIHBsb3R0aW5nCmxpYnJhcnkoZHBseXIpCgojIERlZmluZSB0aGUgb3V0cHV0IGZvbGRlciB3aGVyZSB0aGUgcmVzdWx0cyB3aWxsIGJlIHNhdmVkCm91dHB1dF9mb2xkZXIgPC0gIkhTUENfdnNfQ29udHJvbC8iCgojIENyZWF0ZSB0aGUgb3V0cHV0IGZvbGRlciBpZiBpdCBkb2Vzbid0IGV4aXN0CmlmICghZGlyLmV4aXN0cyhvdXRwdXRfZm9sZGVyKSkgewogIGRpci5jcmVhdGUob3V0cHV0X2ZvbGRlcikKfQoKIyBEZWZpbmUgdGhlIG51bWJlciBvZiB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcyB0byBzZWxlY3QKVVBfZ2VuZXMgPC0gMTAwCkRvd25fZ2VuZXMgPC0gMTAwCgojIERlZmluZSB0aHJlc2hvbGQgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHNlbGVjdGlvbiAobW9kaWZpZWQgdGhyZXNob2xkcykKbG9nRkNfdXBfdGhyZXNob2xkIDwtIDQgICAgICAgICAgIyBVcHJlZ3VsYXRlZCBsb2dGQyB0aHJlc2hvbGQKbG9nRkNfZG93bl90aHJlc2hvbGQgPC0gLTQgICAgICAgICAjIERvd25yZWd1bGF0ZWQgbG9nRkMgdGhyZXNob2xkCgojIExvYWQgeW91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzIChtb2RpZnkgYmFzZWQgb24gYWN0dWFsIGRhdGEgc3RydWN0dXJlKQojIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyA8LSByZWFkLmNzdigiWW91cl9ERV9SZXN1bHRzX0ZpbGUuY3N2IikKCiMgRmlsdGVyIHRoZSBnZW5lcyBiYXNlZCBvbiBhdmdfbG9nMkZDIGFuZCBhcnJhbmdlIGJ5IHBfdmFsX2FkagpmaWx0ZXJlZF9nZW5lcyA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgJT4lCiAgZmlsdGVyKGF2Z19sb2cyRkMgPiBsb2dGQ191cF90aHJlc2hvbGQgfCBhdmdfbG9nMkZDIDwgbG9nRkNfZG93bl90aHJlc2hvbGQpICU+JQogIGFycmFuZ2UocF92YWxfYWRqKQoKIyBTZXBhcmF0ZSB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcwp1cHJlZ3VsYXRlZF9nZW5lcyA8LSBmaWx0ZXJlZF9nZW5lcyAlPiUKICBmaWx0ZXIoYXZnX2xvZzJGQyA+IGxvZ0ZDX3VwX3RocmVzaG9sZCkKCmRvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gZmlsdGVyZWRfZ2VuZXMgJT4lCiAgZmlsdGVyKGF2Z19sb2cyRkMgPCBsb2dGQ19kb3duX3RocmVzaG9sZCkKCiMgQ2hlY2sgaWYgdGhlcmUgYXJlIGZld2VyIHRoYW4gdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMKaWYgKG5yb3codXByZWd1bGF0ZWRfZ2VuZXMpIDwgVVBfZ2VuZXMpIHsKICB0b3BfdXByZWd1bGF0ZWRfZ2VuZXMgPC0gdXByZWd1bGF0ZWRfZ2VuZXMKICBjYXQoIk51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcyBzZWxlY3RlZDoiLCBucm93KHRvcF91cHJlZ3VsYXRlZF9nZW5lcyksICJcbiIpCiAgY2F0KCJwX3ZhbF9hZGogdmFsdWUgZm9yIHRoZSBsYXN0IHNlbGVjdGVkIHVwcmVndWxhdGVkIGdlbmU6IiwgdGFpbCh0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkcF92YWxfYWRqLCAxKSwgIlxuIikKfSBlbHNlIHsKICAjIFNlbGVjdCB0aGUgc3BlY2lmaWVkIG51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcwogIHRvcF91cHJlZ3VsYXRlZF9nZW5lcyA8LSB1cHJlZ3VsYXRlZF9nZW5lcyAlPiUKICAgIGhlYWQoVVBfZ2VuZXMpCiAgY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgc2VsZWN0ZWQ6IiwgbnJvdyh0b3BfdXByZWd1bGF0ZWRfZ2VuZXMpLCAiXG4iKQogIGNhdCgicF92YWxfYWRqIHZhbHVlIGZvciB0aGUgbGFzdCBzZWxlY3RlZCB1cHJlZ3VsYXRlZCBnZW5lOiIsIHRhaWwodG9wX3VwcmVndWxhdGVkX2dlbmVzJHBfdmFsX2FkaiwgMSksICJcbiIpCn0KCiMgQ2hlY2sgaWYgdGhlcmUgYXJlIGZld2VyIHRoYW4gdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcwppZiAobnJvdyhkb3ducmVndWxhdGVkX2dlbmVzKSA8IERvd25fZ2VuZXMpIHsKICB0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyA8LSBkb3ducmVndWxhdGVkX2dlbmVzCiAgY2F0KCJOdW1iZXIgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcyBzZWxlY3RlZDoiLCBucm93KHRvcF9kb3ducmVndWxhdGVkX2dlbmVzKSwgIlxuIikKICBjYXQoInBfdmFsX2FkaiB2YWx1ZSBmb3IgdGhlIGxhc3Qgc2VsZWN0ZWQgZG93bnJlZ3VsYXRlZCBnZW5lOiIsIHRhaWwodG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkcF92YWxfYWRqLCAxKSwgIlxuIikKfSBlbHNlIHsKICAjIFNlbGVjdCB0aGUgc3BlY2lmaWVkIG51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzCiAgdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gZG93bnJlZ3VsYXRlZF9nZW5lcyAlPiUKICAgIGhlYWQoRG93bl9nZW5lcykKICBjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIHNlbGVjdGVkOiIsIG5yb3codG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMpLCAiXG4iKQogIGNhdCgicF92YWxfYWRqIHZhbHVlIGZvciB0aGUgbGFzdCBzZWxlY3RlZCBkb3ducmVndWxhdGVkIGdlbmU6IiwgdGFpbCh0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRwX3ZhbF9hZGosIDEpLCAiXG4iKQp9CgojIENvbWJpbmUgdGhlIHRvcCB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcwp0b3BfZ2VuZXMgPC0gYmluZF9yb3dzKHRvcF91cHJlZ3VsYXRlZF9nZW5lcywgdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMpCgojIENoZWNrIGZvciBtaXNzaW5nIGdlbmVzIChOQXMpIGluIHRoZSBnZW5lIGNvbHVtbiBhbmQgcmVtb3ZlIHRoZW0KdG9wX2dlbmVzIDwtIG5hLm9taXQodG9wX2dlbmVzKQoKIyBTYXZlIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIGdlbmUgcmVzdWx0cyB0byBDU1YKd3JpdGUuY3N2KHRvcF91cHJlZ3VsYXRlZF9nZW5lcywgcGFzdGUwKG91dHB1dF9mb2xkZXIsICJ1cHJlZ3VsYXRlZF9nZW5lcy5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdih0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcywgcGFzdGUwKG91dHB1dF9mb2xkZXIsICJkb3ducmVndWxhdGVkX2dlbmVzLmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgQ29udmVydCBnZW5lIHN5bWJvbHMgdG8gRW50cmV6IElEcyBmb3IgZW5yaWNobWVudCBhbmFseXNpcywgd2l0aCBjaGVja3MgZm9yIG1pc3NpbmcgdmFsdWVzCnVwcmVndWxhdGVkX2VudHJleiA8LSBiaXRyKHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IG9yZy5Icy5lZy5kYikKZG93bnJlZ3VsYXRlZF9lbnRyZXogPC0gYml0cih0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IG9yZy5Icy5lZy5kYikKCiMgQ2hlY2sgZm9yIG1pc3NpbmcgRW50cmV6IElEcyBhbmQgcmV0YWluIGdlbmUgbmFtZXMKbWlzc2luZ191cHJlZ3VsYXRlZCA8LSB0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZVshdG9wX3VwcmVndWxhdGVkX2dlbmVzJGdlbmUgJWluJSB1cHJlZ3VsYXRlZF9lbnRyZXokU1lNQk9MXQptaXNzaW5nX2Rvd25yZWd1bGF0ZWQgPC0gdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZVshdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSAlaW4lIGRvd25yZWd1bGF0ZWRfZW50cmV6JFNZTUJPTF0KCiMgUHJpbnQgb3V0IHRoZSBtaXNzaW5nIGdlbmUgc3ltYm9scyBmb3IgZGVidWdnaW5nCmNhdCgiTWlzc2luZyB1cHJlZ3VsYXRlZCBnZW5lczpcbiIsIG1pc3NpbmdfdXByZWd1bGF0ZWQsICJcbiIpCmNhdCgiTWlzc2luZyBkb3ducmVndWxhdGVkIGdlbmVzOlxuIiwgbWlzc2luZ19kb3ducmVndWxhdGVkLCAiXG4iKQoKIyBNZXJnZSB0aGUgRW50cmV6IElEcyBiYWNrIHdpdGggdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWVzIHRvIHJldGFpbiBnZW5lIG5hbWVzCnRvcF91cHJlZ3VsYXRlZF9nZW5lcyA8LSBtZXJnZSh0b3BfdXByZWd1bGF0ZWRfZ2VuZXMsIHVwcmVndWxhdGVkX2VudHJleiwgYnkueCA9ICJnZW5lIiwgYnkueSA9ICJTWU1CT0wiLCBhbGwueCA9IFRSVUUpCnRvcF9kb3ducmVndWxhdGVkX2dlbmVzIDwtIG1lcmdlKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzLCBkb3ducmVndWxhdGVkX2VudHJleiwgYnkueCA9ICJnZW5lIiwgYnkueSA9ICJTWU1CT0wiLCBhbGwueCA9IFRSVUUpCgojIFJlbW92ZSBnZW5lcyB0aGF0IGNvdWxkbid0IGJlIG1hcHBlZCB0byBFbnRyZXogSURzCnRvcF91cHJlZ3VsYXRlZF9nZW5lcyA8LSB0b3BfdXByZWd1bGF0ZWRfZ2VuZXNbIWlzLm5hKHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRFTlRSRVpJRCksIF0KdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXNbIWlzLm5hKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzJEVOVFJFWklEKSwgXQoKIyBFeHRyYWN0IEVudHJleiBJRHMgZm9yIGVucmljaG1lbnQgYW5hbHlzaXMKdXByZWd1bGF0ZWRfZW50cmV6IDwtIHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRFTlRSRVpJRApkb3ducmVndWxhdGVkX2VudHJleiA8LSB0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRFTlRSRVpJRAoKIyBEZWZpbmUgYSBmdW5jdGlvbiB0byBzYWZlbHkgcnVuIGVucmljaG1lbnQsIHBsb3QgcmVzdWx0cywgYW5kIHNhdmUgdGhlbQpzYWZlX2VucmljaEdPIDwtIGZ1bmN0aW9uKGdlbmVfbGlzdCwgdGl0bGUsIGZpbGVuYW1lKSB7CiAgaWYgKGxlbmd0aChnZW5lX2xpc3QpID4gMCkgewogICAgcmVzdWx0IDwtIGVucmljaEdPKGdlbmUgPSBnZW5lX2xpc3QsIE9yZ0RiID0gb3JnLkhzLmVnLmRiLCBrZXlUeXBlID0gIlNZTUJPTCIsCiAgICAgICAgICAgICAgICAgICAgICAgb250ID0gIkJQIiwgcEFkanVzdE1ldGhvZCA9ICJCSCIsIHB2YWx1ZUN1dG9mZiA9IDAuMDUsIHJlYWRhYmxlID0gVFJVRSkKICAgIGlmICghaXMubnVsbChyZXN1bHQpICYmIG5yb3coYXMuZGF0YS5mcmFtZShyZXN1bHQpKSA+IDApIHsKICAgICAgcCA8LSBkb3RwbG90KHJlc3VsdCwgc2hvd0NhdGVnb3J5ID0gMTAsIHRpdGxlID0gdGl0bGUpCiAgICAgIHByaW50KHApICAKICAgICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCBnc3ViKCIuY3N2IiwgIl9kb3RwbG90LnBuZyIsIGZpbGVuYW1lKSksIHBsb3QgPSBwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCiAgICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKHJlc3VsdCksIGZpbGUgPSBwYXN0ZTAob3V0cHV0X2ZvbGRlciwgZmlsZW5hbWUpLCByb3cubmFtZXMgPSBGQUxTRSkKICAgIH0gZWxzZSB7CiAgICAgIG1lc3NhZ2UocGFzdGUoIk5vIHNpZ25pZmljYW50IGVucmljaG1lbnQgZm91bmQgZm9yOiIsIHRpdGxlKSkKICAgIH0KICB9IGVsc2UgewogICAgbWVzc2FnZShwYXN0ZSgiTm8gZ2VuZXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICB9Cn0KCnNhZmVfZW5yaWNoS0VHRyA8LSBmdW5jdGlvbihlbnRyZXpfbGlzdCwgdGl0bGUsIGZpbGVuYW1lKSB7CiAgaWYgKGxlbmd0aChlbnRyZXpfbGlzdCkgPiAwKSB7CiAgICByZXN1bHQgPC0gZW5yaWNoS0VHRyhnZW5lID0gZW50cmV6X2xpc3QsIG9yZ2FuaXNtID0gImhzYSIsIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiAgICBpZiAoIWlzLm51bGwocmVzdWx0KSAmJiBucm93KGFzLmRhdGEuZnJhbWUocmVzdWx0KSkgPiAwKSB7CiAgICAgIHJlc3VsdCA8LSBzZXRSZWFkYWJsZShyZXN1bHQsIE9yZ0RiID0gb3JnLkhzLmVnLmRiLCBrZXlUeXBlID0gIkVOVFJFWklEIikKICAgICAgcCA8LSBkb3RwbG90KHJlc3VsdCwgc2hvd0NhdGVnb3J5ID0gMTAsIHRpdGxlID0gdGl0bGUpCiAgICAgIHByaW50KHApCiAgICAgIGdnc2F2ZShwYXN0ZTAob3V0cHV0X2ZvbGRlciwgZ3N1YigiLmNzdiIsICJfZG90cGxvdC5wbmciLCBmaWxlbmFtZSkpLCBwbG90ID0gcCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQogICAgICB3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShyZXN1bHQpLCBmaWxlID0gcGFzdGUwKG91dHB1dF9mb2xkZXIsIGZpbGVuYW1lKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgICB9IGVsc2UgewogICAgICBtZXNzYWdlKHBhc3RlKCJObyBzaWduaWZpY2FudCBLRUdHIHBhdGh3YXlzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgICB9CiAgfSBlbHNlIHsKICAgIG1lc3NhZ2UocGFzdGUoIk5vIGdlbmVzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgfQp9CgpzYWZlX2VucmljaFJlYWN0b21lIDwtIGZ1bmN0aW9uKGVudHJlel9saXN0LCB0aXRsZSwgZmlsZW5hbWUpIHsKICBpZiAobGVuZ3RoKGVudHJlel9saXN0KSA+IDApIHsKICAgIHJlc3VsdCA8LSBlbnJpY2hQYXRod2F5KGdlbmUgPSBlbnRyZXpfbGlzdCwgb3JnYW5pc20gPSAiaHVtYW4iLCBwdmFsdWVDdXRvZmYgPSAwLjA1KQogICAgaWYgKCFpcy5udWxsKHJlc3VsdCkgJiYgbnJvdyhhcy5kYXRhLmZyYW1lKHJlc3VsdCkpID4gMCkgewogICAgICByZXN1bHQgPC0gc2V0UmVhZGFibGUocmVzdWx0LCBPcmdEYiA9IG9yZy5Icy5lZy5kYiwga2V5VHlwZSA9ICJFTlRSRVpJRCIpCiAgICAgIHAgPC0gZG90cGxvdChyZXN1bHQsIHNob3dDYXRlZ29yeSA9IDEwLCB0aXRsZSA9IHRpdGxlKQogICAgICBwcmludChwKQogICAgICBnZ3NhdmUocGFzdGUwKG91dHB1dF9mb2xkZXIsIGdzdWIoIi5jc3YiLCAiX2RvdHBsb3QucG5nIiwgZmlsZW5hbWUpKSwgcGxvdCA9IHAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKICAgICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUocmVzdWx0KSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCBmaWxlbmFtZSksIHJvdy5uYW1lcyA9IEZBTFNFKQogICAgfSBlbHNlIHsKICAgICAgbWVzc2FnZShwYXN0ZSgiTm8gc2lnbmlmaWNhbnQgUmVhY3RvbWUgcGF0aHdheXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICAgIH0KICB9IGVsc2UgewogICAgbWVzc2FnZShwYXN0ZSgiTm8gZ2VuZXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICB9Cn0KCiMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2VzLCBnZW5lcmF0ZSBwbG90cywgYW5kIHNhdmUgcmVzdWx0cwpzYWZlX2VucmljaEdPKHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRnZW5lLCAiR08gRW5yaWNobWVudCBmb3IgVXByZWd1bGF0ZWQgR2VuZXMiLCAidXByZWd1bGF0ZWRfR09fcmVzdWx0cy5jc3YiKQpzYWZlX2VucmljaEdPKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzJGdlbmUsICJHTyBFbnJpY2htZW50IGZvciBEb3ducmVndWxhdGVkIEdlbmVzIiwgImRvd25yZWd1bGF0ZWRfR09fcmVzdWx0cy5jc3YiKQoKc2FmZV9lbnJpY2hLRUdHKHVwcmVndWxhdGVkX2VudHJleiwgIktFR0cgUGF0aHdheSBFbnJpY2htZW50IGZvciBVcHJlZ3VsYXRlZCBHZW5lcyIsICJ1cHJlZ3VsYXRlZF9LRUdHX3Jlc3VsdHMuY3N2IikKc2FmZV9lbnJpY2hLRUdHKGRvd25yZWd1bGF0ZWRfZW50cmV6LCAiS0VHRyBQYXRod2F5IEVucmljaG1lbnQgZm9yIERvd25yZWd1bGF0ZWQgR2VuZXMiLCAiZG93bnJlZ3VsYXRlZF9LRUdHX3Jlc3VsdHMuY3N2IikKCnNhZmVfZW5yaWNoUmVhY3RvbWUodXByZWd1bGF0ZWRfZW50cmV6LCAiUmVhY3RvbWUgUGF0aHdheSBFbnJpY2htZW50IGZvciBVcHJlZ3VsYXRlZCBHZW5lcyIsICJ1cHJlZ3VsYXRlZF9SZWFjdG9tZV9yZXN1bHRzLmNzdiIpCnNhZmVfZW5yaWNoUmVhY3RvbWUoZG93bnJlZ3VsYXRlZF9lbnRyZXosICJSZWFjdG9tZSBQYXRod2F5IEVucmljaG1lbnQgZm9yIERvd25yZWd1bGF0ZWQgR2VuZXMiLCAiZG93bnJlZ3VsYXRlZF9SZWFjdG9tZV9yZXN1bHRzLmNzdiIpCgpgYGAKCiMjIEVucmljaG1lbnQgQW5hbHlzaXNfSGFsbG1hcmsKYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShtc2lnZGJyKQpsaWJyYXJ5KGVucmljaHBsb3QpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKCiMgRGVmaW5lIHRoZSBvdXRwdXQgZm9sZGVyIHdoZXJlIHRoZSByZXN1bHRzIHdpbGwgYmUgc2F2ZWQKb3V0cHV0X2ZvbGRlciA8LSAiSFNQQ192c19Db250cm9sLyIKCiMgQ3JlYXRlIHRoZSBvdXRwdXQgZm9sZGVyIGlmIGl0IGRvZXNuJ3QgZXhpc3QKaWYgKCFkaXIuZXhpc3RzKG91dHB1dF9mb2xkZXIpKSB7CiAgZGlyLmNyZWF0ZShvdXRwdXRfZm9sZGVyKQp9CgojIExvYWQgSGFsbG1hcmsgZ2VuZSBzZXRzIGZyb20gbXNpZ2RicgpoYWxsbWFya19zZXRzIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjb2xsZWN0aW9uID0gIkgiKSAgIyAiSCIgaXMgZm9yIEhhbGxtYXJrIGdlbmUgc2V0cwoKIyBDb252ZXJ0IGdlbmUgc3ltYm9scyB0byB1cHBlcmNhc2UgZm9yIGNvbnNpc3RlbmN5CnRvcF91cHJlZ3VsYXRlZF9nZW5lcyRnZW5lIDwtIHRvdXBwZXIodG9wX3VwcmVndWxhdGVkX2dlbmVzJGdlbmUpCnRvcF9kb3ducmVndWxhdGVkX2dlbmVzJGdlbmUgPC0gdG91cHBlcih0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lKQoKIyBDaGVjayBmb3Igb3ZlcmxhcCBiZXR3ZWVuIHlvdXIgdXByZWd1bGF0ZWQvZG93bnJlZ3VsYXRlZCBnZW5lcyBhbmQgSGFsbG1hcmsgZ2VuZSBzZXRzCnVwcmVndWxhdGVkX2luX2hhbGxtYXJrIDwtIGludGVyc2VjdCh0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgaGFsbG1hcmtfc2V0cyRnZW5lX3N5bWJvbCkKZG93bnJlZ3VsYXRlZF9pbl9oYWxsbWFyayA8LSBpbnRlcnNlY3QodG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgaGFsbG1hcmtfc2V0cyRnZW5lX3N5bWJvbCkKCiMgUHJpbnQgdGhlIG51bWJlciBvZiBvdmVybGFwcGluZyBnZW5lcyBmb3IgYm90aCB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcwpjYXQoIk51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcyBpbiBIYWxsbWFyayBnZW5lIHNldHM6IiwgbGVuZ3RoKHVwcmVndWxhdGVkX2luX2hhbGxtYXJrKSwgIlxuIikKY2F0KCJOdW1iZXIgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcyBpbiBIYWxsbWFyayBnZW5lIHNldHM6IiwgbGVuZ3RoKGRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmspLCAiXG4iKQoKIyBJZiB0aGVyZSBhcmUgZ2VuZXMgdG8gYW5hbHl6ZSwgcHJvY2VlZCB3aXRoIGVucmljaG1lbnQgYW5hbHlzaXMKaWYgKGxlbmd0aCh1cHJlZ3VsYXRlZF9pbl9oYWxsbWFyaykgPiAwKSB7CiAgIyBQZXJmb3JtIGVucmljaG1lbnQgYW5hbHlzaXMgZm9yIHVwcmVndWxhdGVkIGdlbmVzIHVzaW5nIEhhbGxtYXJrIGdlbmUgc2V0cwogIGhhbGxtYXJrX3VwIDwtIGVucmljaGVyKGdlbmUgPSB1cHJlZ3VsYXRlZF9pbl9oYWxsbWFyaywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgVEVSTTJHRU5FID0gaGFsbG1hcmtfc2V0c1ssIGMoImdzX25hbWUiLCAiZ2VuZV9zeW1ib2wiKV0sICAjIEVuc3VyZSBURVJNMkdFTkUgdXNlcyBjb3JyZWN0IGNvbHVtbnMKICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1KQogICMgQ2hlY2sgaWYgcmVzdWx0cyBleGlzdAogIGlmICghaXMubnVsbChoYWxsbWFya191cCkgJiYgbnJvdyhoYWxsbWFya191cCkgPiAwKSB7CiAgICAjIFZpc3VhbGl6ZSByZXN1bHRzIGlmIGF2YWlsYWJsZQogICAgdXBfZG90cGxvdCA8LSBkb3RwbG90KGhhbGxtYXJrX3VwLCBzaG93Q2F0ZWdvcnkgPSAyMCwgdGl0bGUgPSAiSGFsbG1hcmsgUGF0aHdheSBFbnJpY2htZW50IGZvciBVcHJlZ3VsYXRlZCBHZW5lcyIpCiAgICAKICAgICMgRGlzcGxheSB0aGUgcGxvdCBpbiB0aGUgbm90ZWJvb2sKICAgIHByaW50KHVwX2RvdHBsb3QpCiAgICAKICAgICMgU2F2ZSB0aGUgZG90cGxvdCB0byBhIFBORyBmaWxlCiAgICBnZ3NhdmUocGFzdGUwKG91dHB1dF9mb2xkZXIsICJoYWxsbWFya191cHJlZ3VsYXRlZF9kb3RwbG90LnBuZyIpLCBwbG90ID0gdXBfZG90cGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKICAgIAogICAgIyBPcHRpb25hbGx5LCBzYXZlIHRoZSByZXN1bHRzIGFzIENTVgogICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUoaGFsbG1hcmtfdXApLCBmaWxlID0gcGFzdGUwKG91dHB1dF9mb2xkZXIsICJoYWxsbWFya191cHJlZ3VsYXRlZF9lbnJpY2htZW50LmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKICB9IGVsc2UgewogICAgY2F0KCJObyBzaWduaWZpY2FudCBlbnJpY2htZW50IGZvdW5kIGZvciB1cHJlZ3VsYXRlZCBnZW5lcy5cbiIpCiAgfQp9IGVsc2UgewogIGNhdCgiTm8gdXByZWd1bGF0ZWQgZ2VuZXMgb3ZlcmxhcCB3aXRoIEhhbGxtYXJrIGdlbmUgc2V0cy5cbiIpCn0KCmlmIChsZW5ndGgoZG93bnJlZ3VsYXRlZF9pbl9oYWxsbWFyaykgPiAwKSB7CiAgIyBQZXJmb3JtIGVucmljaG1lbnQgYW5hbHlzaXMgZm9yIGRvd25yZWd1bGF0ZWQgZ2VuZXMgdXNpbmcgSGFsbG1hcmsgZ2VuZSBzZXRzCiAgaGFsbG1hcmtfZG93biA8LSBlbnJpY2hlcihnZW5lID0gZG93bnJlZ3VsYXRlZF9pbl9oYWxsbWFyaywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURVJNMkdFTkUgPSBoYWxsbWFya19zZXRzWywgYygiZ3NfbmFtZSIsICJnZW5lX3N5bWJvbCIpXSwgICMgRW5zdXJlIFRFUk0yR0VORSB1c2VzIGNvcnJlY3QgY29sdW1ucwogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKICAjIENoZWNrIGlmIHJlc3VsdHMgZXhpc3QKICBpZiAoIWlzLm51bGwoaGFsbG1hcmtfZG93bikgJiYgbnJvdyhoYWxsbWFya19kb3duKSA+IDApIHsKICAgICMgVmlzdWFsaXplIHJlc3VsdHMgaWYgYXZhaWxhYmxlCiAgICBkb3duX2RvdHBsb3QgPC0gZG90cGxvdChoYWxsbWFya19kb3duLCBzaG93Q2F0ZWdvcnkgPSAyMCwgdGl0bGUgPSAiSGFsbG1hcmsgUGF0aHdheSBFbnJpY2htZW50IGZvciBEb3ducmVndWxhdGVkIEdlbmVzIikKICAgIAogICAgIyBEaXNwbGF5IHRoZSBwbG90IGluIHRoZSBub3RlYm9vawogICAgcHJpbnQoZG93bl9kb3RwbG90KQogICAgCiAgICAjIFNhdmUgdGhlIGRvdHBsb3QgdG8gYSBQTkcgZmlsZQogICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfZG93bnJlZ3VsYXRlZF9kb3RwbG90LnBuZyIpLCBwbG90ID0gZG93bl9kb3RwbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQogICAgCiAgICAjIE9wdGlvbmFsbHksIHNhdmUgdGhlIHJlc3VsdHMgYXMgQ1NWCiAgICB3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShoYWxsbWFya19kb3duKSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfZG93bnJlZ3VsYXRlZF9lbnJpY2htZW50LmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKICB9IGVsc2UgewogICAgY2F0KCJObyBzaWduaWZpY2FudCBlbnJpY2htZW50IGZvdW5kIGZvciBkb3ducmVndWxhdGVkIGdlbmVzLlxuIikKICB9Cn0gZWxzZSB7CiAgY2F0KCJObyBkb3ducmVndWxhdGVkIGdlbmVzIG92ZXJsYXAgd2l0aCBIYWxsbWFyayBnZW5lIHNldHMuXG4iKQp9CgoKYGBgCgoKCg==