3. Summarize Markers
markers <- DE_results_df
summarize_markers <- function(markers) {
num_pval0 <- sum(markers$p_val_adj == 0)
num_pval1 <- sum(markers$p_val_adj == 1)
num_upregulated <- sum(markers$avg_log2FC > 1.5)
num_downregulated <- sum(markers$avg_log2FC < -1)
num_significant <- sum(markers$p_val_adj < 0.05)
cat("Number of genes with p_val_adj = 0:", num_pval0, "\n")
cat("Number of genes with p_val_adj = 1:", num_pval1, "\n")
cat("Number of upregulated genes (avg_log2FC > 1.5):", num_upregulated, "\n")
cat("Number of downregulated genes (avg_log2FC < -1):", num_downregulated, "\n")
cat("Number of significant genes (p_val_adj < 0.05):", num_significant, "\n")
}
cat("Markers Summary at 0.05:\n")
Markers Summary at 0.05:
summarize_markers(markers)
Number of genes with p_val_adj = 0: 165
Number of genes with p_val_adj = 1: 520
Number of upregulated genes (avg_log2FC > 1.5): 93
Number of downregulated genes (avg_log2FC < -1): 136
Number of significant genes (p_val_adj < 0.05): 3220
markers2 <- DE_results_df
summarize_markers <- function(markers) {
num_pval0 <- sum(markers$p_val_adj == 0)
num_pval1 <- sum(markers$p_val_adj == 1)
num_upregulated <- sum(markers$avg_log2FC > 1.5)
num_downregulated <- sum(markers$avg_log2FC < -1)
num_significant <- sum(markers$p_val_adj < 1e-4)
cat("Number of genes with p_val_adj = 0:", num_pval0, "\n")
cat("Number of genes with p_val_adj = 1:", num_pval1, "\n")
cat("Number of upregulated genes (avg_log2FC > 1.5):", num_upregulated, "\n")
cat("Number of downregulated genes (avg_log2FC < -1):", num_downregulated, "\n")
cat("Number of significant genes (p_val_adj < 1e-4):", num_significant, "\n")
}
cat("Markers Summary at 1e-4:\n")
Markers Summary at 1e-4:
summarize_markers(markers2)
Number of genes with p_val_adj = 0: 165
Number of genes with p_val_adj = 1: 520
Number of upregulated genes (avg_log2FC > 1.5): 93
Number of downregulated genes (avg_log2FC < -1): 136
Number of significant genes (p_val_adj < 1e-4): 2899
markers3 <- DE_results_df
summarize_markers <- function(markers) {
num_pval0 <- sum(markers$p_val_adj == 0)
num_pval1 <- sum(markers$p_val_adj == 1)
num_upregulated <- sum(markers$avg_log2FC > 1.5)
num_downregulated <- sum(markers$avg_log2FC < -1)
num_significant <- sum(markers$p_val_adj < 1e-6)
cat("Number of genes with p_val_adj = 0:", num_pval0, "\n")
cat("Number of genes with p_val_adj = 1:", num_pval1, "\n")
cat("Number of upregulated genes (avg_log2FC > 1.5):", num_upregulated, "\n")
cat("Number of downregulated genes (avg_log2FC < -1):", num_downregulated, "\n")
cat("Number of significant genes (p_val_adj < 1e-6):", num_significant, "\n")
}
cat("Markers Summary at 1e-6:\n")
Markers Summary at 1e-6:
summarize_markers(markers3)
Number of genes with p_val_adj = 0: 165
Number of genes with p_val_adj = 1: 520
Number of upregulated genes (avg_log2FC > 1.5): 93
Number of downregulated genes (avg_log2FC < -1): 136
Number of significant genes (p_val_adj < 1e-6): 2720
markers4 <- DE_results_df
summarize_markers <- function(markers) {
num_pval0 <- sum(markers$p_val_adj == 0)
num_pval1 <- sum(markers$p_val_adj == 1)
num_upregulated <- sum(markers$avg_log2FC > 1.5)
num_downregulated <- sum(markers$avg_log2FC < -1)
num_significant <- sum(markers$p_val_adj < 1e-10)
cat("Number of genes with p_val_adj = 0:", num_pval0, "\n")
cat("Number of genes with p_val_adj = 1:", num_pval1, "\n")
cat("Number of upregulated genes (avg_log2FC > 1.5):", num_upregulated, "\n")
cat("Number of downregulated genes (avg_log2FC < -1):", num_downregulated, "\n")
cat("Number of significant genes (p_val_adj < 1e-10):", num_significant, "\n")
}
cat("Markers Summary at 1e-10:\n")
Markers Summary at 1e-10:
summarize_markers(markers4)
Number of genes with p_val_adj = 0: 165
Number of genes with p_val_adj = 1: 520
Number of upregulated genes (avg_log2FC > 1.5): 93
Number of downregulated genes (avg_log2FC < -1): 136
Number of significant genes (p_val_adj < 1e-10): 2414
markers5 <- DE_results_df
summarize_markers <- function(markers) {
num_pval0 <- sum(markers$p_val_adj == 0)
num_pval1 <- sum(markers$p_val_adj == 1)
num_upregulated <- sum(markers$avg_log2FC > 1.5)
num_downregulated <- sum(markers$avg_log2FC < -1)
num_significant <- sum(markers$p_val_adj < 1e-15)
cat("Number of genes with p_val_adj = 0:", num_pval0, "\n")
cat("Number of genes with p_val_adj = 1:", num_pval1, "\n")
cat("Number of upregulated genes (avg_log2FC > 1.5):", num_upregulated, "\n")
cat("Number of downregulated genes (avg_log2FC < -1):", num_downregulated, "\n")
cat("Number of significant genes (p_val_adj < 1e-15):", num_significant, "\n")
}
cat("Markers Summary at 1e-15:\n")
Markers Summary at 1e-15:
summarize_markers(markers5)
Number of genes with p_val_adj = 0: 165
Number of genes with p_val_adj = 1: 520
Number of upregulated genes (avg_log2FC > 1.5): 93
Number of downregulated genes (avg_log2FC < -1): 136
Number of significant genes (p_val_adj < 1e-15): 2118
4. Volcano Plots
library(ggplot2)
library(dplyr)
Attachement du package : ‘dplyr’
L'objet suivant est masqué depuis ‘package:Biobase’:
combine
Les objets suivants sont masqués depuis ‘package:GenomicRanges’:
intersect, setdiff, union
L'objet suivant est masqué depuis ‘package:GenomeInfoDb’:
intersect
Les objets suivants sont masqués depuis ‘package:IRanges’:
collapse, desc, intersect, setdiff, slice, union
Les objets suivants sont masqués depuis ‘package:S4Vectors’:
first, intersect, rename, setdiff, setequal, union
Les objets suivants sont masqués depuis ‘package:BiocGenerics’:
combine, intersect, setdiff, union
L'objet suivant est masqué depuis ‘package:matrixStats’:
count
Les objets suivants sont masqués depuis ‘package:stats’:
filter, lag
Les objets suivants sont masqués depuis ‘package:base’:
intersect, setdiff, setequal, union
library(ggrepel)
# Ensure correct column names
colnames(DE_results_df)
[1] "gene" "p_val" "avg_log2FC" "pct.1" "pct.2" "p_val_adj"
[7] "mean_expr_ident1" "mean_expr_ident2"
# Define significance categories
volcano_data <- DE_results_df %>%
mutate(
significance = case_when(
p_val_adj < 1e-20 & avg_log2FC > 2 ~ "Most Upregulated",
p_val_adj < 1e-20 & avg_log2FC < -2 ~ "Most Downregulated",
p_val_adj < 0.05 & avg_log2FC > 2 ~ "Upregulated",
p_val_adj < 0.05 & avg_log2FC < -2 ~ "Downregulated",
TRUE ~ "Not Significant"
)
)
# Select only very significant genes for labeling
top_genes <- volcano_data %>%
filter(p_val_adj < 0.05 & (avg_log2FC > 2 | avg_log2FC < -2))
ggplot(volcano_data, aes(x = avg_log2FC, y = -log10(p_val_adj), color = significance)) +
# Main points
geom_point(alpha = 0.7, size = 2.5) +
# Highlight highly significant genes with larger points
geom_point(data = top_genes, aes(x = avg_log2FC, y = -log10(p_val_adj)),
color = "black", size = 3, shape = 21, fill = "black") +
# Custom color scheme
scale_color_manual(values = c(
"Most Upregulated" = "darkred",
"Most Downregulated" = "darkblue",
"Upregulated" = "red",
"Downregulated" = "blue",
"Not Significant" = "grey"
)) +
# Add gene labels (only for highly significant genes)
geom_text_repel(data = top_genes, aes(label = gene),
size = 4, box.padding = 0.5, max.overlaps = 10, segment.color = NA) +
# Add threshold lines
geom_vline(xintercept = c(-2, 2), linetype = "dashed", color = "black") +
geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "black") +
# Improve theme
theme_minimal(base_size = 14) +
labs(title = "Volcano Plot: Pseudobulk DESeq2 Analysis",
x = "Log2 Fold Change",
y = "-Log10 Adjusted P-Value",
color = "Significance") +
ylim(0, 50) # Avoid extreme scaling issues

NA
NA
EnhancedVolcano plot
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 <- markers %>%
arrange(p_val_adj, desc(abs(avg_log2FC)))
# Create the EnhancedVolcano plot with the filtered data
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 = "Malignant CD4 T cells(cell lines) vs normal CD4 T cells",
pCutoff = 1e-6,
FCcutoff = 1.0,
legendPosition = 'right',
labCol = 'black',
labFace = 'bold',
boxedLabels = FALSE, # Set to FALSE to remove boxed labels
pointSize = 3.0,
labSize = 5.0,
col = c('grey70', 'black', 'blue', 'red'), # Customize point colors
selectLab = filtered_genes$gene[filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_log2FC) >= 1.0] # Only label significant genes
)
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...

EnhancedVolcano plot
library(ggplot2)
library(EnhancedVolcano)
library(dplyr)
# Define the output directory
output_dir <- "Volcano_Plot_Malignant_vs_Control"
dir.create(output_dir, showWarnings = FALSE)
Malignant_CD4Tcells_vs_Normal_CD4Tcells <- filtered_genes
# First Volcano Plot
p1 <- EnhancedVolcano(
Malignant_CD4Tcells_vs_Normal_CD4Tcells,
lab = Malignant_CD4Tcells_vs_Normal_CD4Tcells$gene,
x = "avg_log2FC",
y = "p_val_adj",
title = "Malignant_CD4Tcells_vs_Normal_CD4Tcells",
pCutoff = 1e-4,
FCcutoff = 1.0
)
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...
print(p1) # Display in notebook

ggsave(filename = file.path(output_dir, "VolcanoPlot1.png"), plot = p1, width = 14, height = 10, dpi = 300)
# Second Volcano Plot with selected genes
p2 <- EnhancedVolcano(
Malignant_CD4Tcells_vs_Normal_CD4Tcells,
lab = Malignant_CD4Tcells_vs_Normal_CD4Tcells$gene,
x = "avg_log2FC",
y = "p_val_adj",
selectLab = c('EPCAM', 'BCAT1', 'KIR3DL2', 'FOXM1', 'TWIST1', 'TNFSF9',
'CD80', 'IL1B', 'RPS4Y1', "TOX", "CD52", "TWIST1", "CCR4", "CCR7","PDCD1",
'IL7R', 'TCF7', 'MKI67', 'CD70', "DPP4",
'IL2RA','TRBV6-2', 'TRBV10-3', 'TRBV4-2', 'TRBV9', 'TRBV7-9',
'TRAV12-1', 'CD8B', 'FCGR3A', 'GNLY', 'FOXP3', 'SELL',
'GIMAP1', 'RIPOR2', 'LEF1', 'HOXC9', 'SP5',
'CCL17', 'ETV4', 'THY1', 'FOXA2', 'ITGAD', 'S100P', 'TBX4',
'ID1', 'XCL1', 'SOX2', 'CD27', 'CD28','PLS3','CD70','RAB25' , 'TRBV27', 'TRBV2'),
title = "Malignant CD4 T cells(cell lines) vs normal CD4 T cells",
xlab = bquote(~Log[2]~ 'fold change'),
pCutoff = 0.05,
FCcutoff = 1.5,
pointSize = 3.0,
labSize = 5.0,
boxedLabels = TRUE,
colAlpha = 0.5,
legendPosition = 'right',
legendLabSize = 10,
legendIconSize = 4.0,
drawConnectors = TRUE,
widthConnectors = 0.5,
colConnectors = 'grey50',
arrowheads = FALSE,
max.overlaps = 30
)
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...
print(p2) # Display in notebook

ggsave(filename = file.path(output_dir, "VolcanoPlot2.png"), plot = p2, width = 14, height = 10, dpi = 300)
# Filtering genes
filtered_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells %>%
arrange(p_val_adj, desc(abs(avg_log2FC)))
# Third Volcano Plot - Filtering by p-value and logFC
p3 <- EnhancedVolcano(
filtered_genes,
lab = ifelse(filtered_genes$p_val_adj <= 1e-4 & abs(filtered_genes$avg_log2FC) >= 1.0, filtered_genes$gene, NA),
x = "avg_log2FC",
y = "p_val_adj",
title = "Malignant CD4 T cells(cell lines) vs normal CD4 T cells",
pCutoff = 1e-4,
FCcutoff = 1.0,
legendPosition = 'right',
labCol = 'black',
labFace = 'bold',
boxedLabels = FALSE, # Remove boxed labels
pointSize = 3.0,
labSize = 5.0,
col = c('grey70', 'black', 'blue', 'red'), # Customize point colors
selectLab = filtered_genes$gene[filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_log2FC) >= 1.0]
)
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...
print(p3) # Display in notebook

ggsave(filename = file.path(output_dir, "VolcanoPlot3.png"), plot = p3, width = 14, height = 10, dpi = 300)
# Fourth Volcano Plot - More refined filtering
p4 <- EnhancedVolcano(
filtered_genes,
lab = ifelse(filtered_genes$p_val_adj <= 1e-4 & abs(filtered_genes$avg_log2FC) >= 1.0, filtered_genes$gene, NA),
x = "avg_log2FC",
y = "p_val_adj",
title = "Malignant CD4 T cells (cell lines) vs Normal CD4 T cells",
subtitle = "Highlighting differentially expressed genes",
pCutoff = 1e-4,
FCcutoff = 1.0,
legendPosition = 'right',
colAlpha = 0.8, # Slight transparency for non-significant points
col = c('grey70', 'black', 'blue', 'red'), # Custom color scheme
gridlines.major = TRUE,
gridlines.minor = FALSE,
selectLab = filtered_genes$gene[filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_log2FC) >= 1.0]
)
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...
print(p4) # Display in notebook

ggsave(filename = file.path(output_dir, "VolcanoPlot4.png"), plot = p4, width = 14, height = 10, dpi = 300)
message("All volcano plots have been displayed and saved successfully in the 'Malignant_vs_Control' folder.")
All volcano plots have been displayed and saved successfully in the 'Malignant_vs_Control' folder.
5. Enrichment Analysis-All_Pathways
# Load necessary libraries
library(clusterProfiler)
clusterProfiler v4.6.2 For help: https://yulab-smu.top/biomedical-knowledge-mining-book/
If you use clusterProfiler in published research, please cite:
T Wu, E Hu, S Xu, M Chen, P Guo, Z Dai, T Feng, L Zhou, W Tang, L Zhan, X Fu, S Liu, X Bo, and G Yu. clusterProfiler 4.0: A universal enrichment tool for interpreting omics data. The Innovation. 2021, 2(3):100141
Attachement du package : ‘clusterProfiler’
L'objet suivant est masqué depuis ‘package:IRanges’:
slice
L'objet suivant est masqué depuis ‘package:S4Vectors’:
rename
L'objet suivant est masqué depuis ‘package:stats’:
filter
library(org.Hs.eg.db)
Le chargement a nécessité le package : AnnotationDbi
Attachement du package : ‘AnnotationDbi’
L'objet suivant est masqué depuis ‘package:clusterProfiler’:
select
L'objet suivant est masqué depuis ‘package:dplyr’:
select
library(enrichplot)
library(ReactomePA)
ReactomePA v1.42.0 For help: https://yulab-smu.top/biomedical-knowledge-mining-book/
If you use ReactomePA in published research, please cite:
Guangchuang Yu, Qing-Yu He. ReactomePA: an R/Bioconductor package for reactome pathway analysis and visualization. Molecular BioSystems 2016, 12(2):477-479
library(DOSE) # For GSEA analysis
DOSE v3.24.2 For help: https://yulab-smu.top/biomedical-knowledge-mining-book/
If you use DOSE in published research, please cite:
Guangchuang Yu, Li-Gen Wang, Guang-Rong Yan, Qing-Yu He. DOSE: an R/Bioconductor package for Disease Ontology Semantic and Enrichment analysis. Bioinformatics 2015, 31(4):608-609
library(ggplot2) # Ensure ggplot2 is available for plotting
library(dplyr)
# Define the output folder where the results will be saved
output_folder <- "L6_vs_L7/"
# 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 <- 250
Down_genes <- 250
# Define threshold for differential expression selection (modified thresholds)
logFC_up_threshold <- 1 # Upregulated logFC threshold
logFC_down_threshold <- -1 # 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: 169
p_val_adj value for the last selected upregulated gene: 2.251439e-35
# 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: 136
p_val_adj value for the last selected downregulated gene: 1.404732e-09
# 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
Avis : 6.51% 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
Avis : 2.21% 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:
AL590550.1 AARS GARS MARS AP001783.1 AC099552.1 PALM2-AKAP2 AC010967.1 AC090015.1 AC106729.1 AC114977.1
cat("Missing downregulated genes:\n", missing_downregulated, "\n")
Missing downregulated genes:
TMEM173 AC108865.1 AC011246.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")
Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...

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")
No significant Reactome pathways found for: Reactome Pathway Enrichment for Downregulated Genes
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 <- "L6_vs_L7/"
# 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: 75
cat("Number of downregulated genes in Hallmark gene sets:", length(downregulated_in_hallmark), "\n")
Number of downregulated genes in Hallmark gene sets: 70
# 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
LS0tCnRpdGxlOiAiRW5yaWNobWVudCBBbmFseXNpcyBvZiBMNkw3IGRlcml2ZWQgZnJvbSBQM19maWx0cmVkX29uX21lYW4iCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgIyBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICAjIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKICAjIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICAjcm1kZm9ybWF0czo6cmVhZHRoZWRvd24KICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19jb2xsYXBzZWQ6IHRydWUKLS0tCgojIDEuIGxvYWQgbGlicmFyaWVzCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQoKIyBMb2FkIGxpYnJhcmllcwpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KExpYnJhKQoKYGBgCgojIDIuIExvYWQgdGhlIGZpbHRlcmVkIGxpc3Qgb24gbWVhbiBleHByZXNzaW9uCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CgojIExvYWQgdGhlIERFIHJlc3VsdHMgZnJvbSBDU1YKZGYgPC0gcmVhZC5jc3YoIi4uL2NvbXBhcmlzb25fTDZfdnNfTDdfd2l0aF9tZWFuX2V4cHJlc3Npb25fZmlsdGVyZWQuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKCkRFX3Jlc3VsdHNfZGYgPC0gZGYKCmBgYAoKIyAzLiBTdW1tYXJpemUgTWFya2VycwpgYGB7ciAsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNH0KbWFya2VycyA8LSBERV9yZXN1bHRzX2RmCgpzdW1tYXJpemVfbWFya2VycyA8LSBmdW5jdGlvbihtYXJrZXJzKSB7CiAgbnVtX3B2YWwwIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAwKQogIG51bV9wdmFsMSA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPT0gMSkKICBudW1fdXByZWd1bGF0ZWQgPC0gc3VtKG1hcmtlcnMkYXZnX2xvZzJGQyA+IDEuNSkKICBudW1fZG93bnJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nMkZDIDwgLTEpCiAgbnVtX3NpZ25pZmljYW50IDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA8IDAuMDUpCiAgCiAgCiAgY2F0KCJOdW1iZXIgb2YgZ2VuZXMgd2l0aCBwX3ZhbF9hZGogPSAwOiIsIG51bV9wdmFsMCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDE6IiwgbnVtX3B2YWwxLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIHVwcmVndWxhdGVkIGdlbmVzIChhdmdfbG9nMkZDID4gMS41KToiLCBudW1fdXByZWd1bGF0ZWQsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcyAoYXZnX2xvZzJGQyA8IC0xKToiLCBudW1fZG93bnJlZ3VsYXRlZCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBzaWduaWZpY2FudCBnZW5lcyAocF92YWxfYWRqIDwgMC4wNSk6IiwgbnVtX3NpZ25pZmljYW50LCAiXG4iKQp9CgpjYXQoIk1hcmtlcnMgU3VtbWFyeSBhdCAwLjA1OlxuIikKCnN1bW1hcml6ZV9tYXJrZXJzKG1hcmtlcnMpCgptYXJrZXJzMiA8LSBERV9yZXN1bHRzX2RmCnN1bW1hcml6ZV9tYXJrZXJzIDwtIGZ1bmN0aW9uKG1hcmtlcnMpIHsKICBudW1fcHZhbDAgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqID09IDApCiAgbnVtX3B2YWwxIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAxKQogIG51bV91cHJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nMkZDID4gMS41KQogIG51bV9kb3ducmVndWxhdGVkIDwtIHN1bShtYXJrZXJzJGF2Z19sb2cyRkMgPCAtMSkKICBudW1fc2lnbmlmaWNhbnQgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqIDwgMWUtNCkKICAKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDA6IiwgbnVtX3B2YWwwLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMToiLCBudW1fcHZhbDEsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2cyRkMgPiAxLjUpOiIsIG51bV91cHJlZ3VsYXRlZCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIChhdmdfbG9nMkZDIDwgLTEpOiIsIG51bV9kb3ducmVndWxhdGVkLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIHNpZ25pZmljYW50IGdlbmVzIChwX3ZhbF9hZGogPCAxZS00KToiLCBudW1fc2lnbmlmaWNhbnQsICJcbiIpCn0KCmNhdCgiTWFya2VycyBTdW1tYXJ5IGF0IDFlLTQ6XG4iKQoKc3VtbWFyaXplX21hcmtlcnMobWFya2VyczIpCgptYXJrZXJzMyA8LSBERV9yZXN1bHRzX2RmCnN1bW1hcml6ZV9tYXJrZXJzIDwtIGZ1bmN0aW9uKG1hcmtlcnMpIHsKICBudW1fcHZhbDAgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqID09IDApCiAgbnVtX3B2YWwxIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAxKQogIG51bV91cHJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nMkZDID4gMS41KQogIG51bV9kb3ducmVndWxhdGVkIDwtIHN1bShtYXJrZXJzJGF2Z19sb2cyRkMgPCAtMSkKICBudW1fc2lnbmlmaWNhbnQgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqIDwgMWUtNikKICAKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDA6IiwgbnVtX3B2YWwwLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMToiLCBudW1fcHZhbDEsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2cyRkMgPiAxLjUpOiIsIG51bV91cHJlZ3VsYXRlZCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIChhdmdfbG9nMkZDIDwgLTEpOiIsIG51bV9kb3ducmVndWxhdGVkLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIHNpZ25pZmljYW50IGdlbmVzIChwX3ZhbF9hZGogPCAxZS02KToiLCBudW1fc2lnbmlmaWNhbnQsICJcbiIpCn0KCmNhdCgiTWFya2VycyBTdW1tYXJ5IGF0IDFlLTY6XG4iKQpzdW1tYXJpemVfbWFya2VycyhtYXJrZXJzMykKCm1hcmtlcnM0IDwtIERFX3Jlc3VsdHNfZGYKc3VtbWFyaXplX21hcmtlcnMgPC0gZnVuY3Rpb24obWFya2VycykgewogIG51bV9wdmFsMCA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPT0gMCkKICBudW1fcHZhbDEgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqID09IDEpCiAgbnVtX3VwcmVndWxhdGVkIDwtIHN1bShtYXJrZXJzJGF2Z19sb2cyRkMgPiAxLjUpCiAgbnVtX2Rvd25yZWd1bGF0ZWQgPC0gc3VtKG1hcmtlcnMkYXZnX2xvZzJGQyA8IC0xKQogIG51bV9zaWduaWZpY2FudCA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPCAxZS0xMCkKICAKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDA6IiwgbnVtX3B2YWwwLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMToiLCBudW1fcHZhbDEsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2cyRkMgPiAxLjUpOiIsIG51bV91cHJlZ3VsYXRlZCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIChhdmdfbG9nMkZDIDwgLTEpOiIsIG51bV9kb3ducmVndWxhdGVkLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIHNpZ25pZmljYW50IGdlbmVzIChwX3ZhbF9hZGogPCAxZS0xMCk6IiwgbnVtX3NpZ25pZmljYW50LCAiXG4iKQogIH0KCmNhdCgiTWFya2VycyBTdW1tYXJ5IGF0IDFlLTEwOlxuIikKCnN1bW1hcml6ZV9tYXJrZXJzKG1hcmtlcnM0KQoKbWFya2VyczUgPC0gREVfcmVzdWx0c19kZgpzdW1tYXJpemVfbWFya2VycyA8LSBmdW5jdGlvbihtYXJrZXJzKSB7CiAgbnVtX3B2YWwwIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAwKQogIG51bV9wdmFsMSA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPT0gMSkKICBudW1fdXByZWd1bGF0ZWQgPC0gc3VtKG1hcmtlcnMkYXZnX2xvZzJGQyA+IDEuNSkKICBudW1fZG93bnJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nMkZDIDwgLTEpCiAgbnVtX3NpZ25pZmljYW50IDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA8IDFlLTE1KQogIAogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMDoiLCBudW1fcHZhbDAsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgZ2VuZXMgd2l0aCBwX3ZhbF9hZGogPSAxOiIsIG51bV9wdmFsMSwgIlxuIikKICBjYXQoIk51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcyAoYXZnX2xvZzJGQyA+IDEuNSk6IiwgbnVtX3VwcmVndWxhdGVkLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2cyRkMgPCAtMSk6IiwgbnVtX2Rvd25yZWd1bGF0ZWQsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2Ygc2lnbmlmaWNhbnQgZ2VuZXMgKHBfdmFsX2FkaiA8IDFlLTE1KToiLCBudW1fc2lnbmlmaWNhbnQsICJcbiIpCn0KCmNhdCgiTWFya2VycyBTdW1tYXJ5IGF0IDFlLTE1OlxuIikKCnN1bW1hcml6ZV9tYXJrZXJzKG1hcmtlcnM1KQoKCgpgYGAKCgoKIyA0LiBWb2xjYW5vIFBsb3RzCmBgYHtyICwgZmlnLmhlaWdodD0xNCwgZmlnLndpZHRoPTE4fQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dyZXBlbCkKCgojIEVuc3VyZSBjb3JyZWN0IGNvbHVtbiBuYW1lcwpjb2xuYW1lcyhERV9yZXN1bHRzX2RmKQoKIyBEZWZpbmUgc2lnbmlmaWNhbmNlIGNhdGVnb3JpZXMKdm9sY2Fub19kYXRhIDwtIERFX3Jlc3VsdHNfZGYgJT4lCiAgbXV0YXRlKAogICAgc2lnbmlmaWNhbmNlID0gY2FzZV93aGVuKAogICAgICBwX3ZhbF9hZGogPCAxZS0yMCAmIGF2Z19sb2cyRkMgPiAyIH4gIk1vc3QgVXByZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAxZS0yMCAmIGF2Z19sb2cyRkMgPCAtMiB+ICJNb3N0IERvd25yZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAwLjA1ICYgYXZnX2xvZzJGQyA+IDIgfiAiVXByZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAwLjA1ICYgYXZnX2xvZzJGQyA8IC0yIH4gIkRvd25yZWd1bGF0ZWQiLAogICAgICBUUlVFIH4gIk5vdCBTaWduaWZpY2FudCIKICAgICkKICApCgojIFNlbGVjdCBvbmx5IHZlcnkgc2lnbmlmaWNhbnQgZ2VuZXMgZm9yIGxhYmVsaW5nCnRvcF9nZW5lcyA8LSB2b2xjYW5vX2RhdGEgJT4lCiAgZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUgJiAoYXZnX2xvZzJGQyA+IDIgfCBhdmdfbG9nMkZDIDwgLTIpKQoKZ2dwbG90KHZvbGNhbm9fZGF0YSwgYWVzKHggPSBhdmdfbG9nMkZDLCB5ID0gLWxvZzEwKHBfdmFsX2FkaiksIGNvbG9yID0gc2lnbmlmaWNhbmNlKSkgKwogIAogICMgTWFpbiBwb2ludHMKICBnZW9tX3BvaW50KGFscGhhID0gMC43LCBzaXplID0gMi41KSArCiAgCiAgIyBIaWdobGlnaHQgaGlnaGx5IHNpZ25pZmljYW50IGdlbmVzIHdpdGggbGFyZ2VyIHBvaW50cwogIGdlb21fcG9pbnQoZGF0YSA9IHRvcF9nZW5lcywgYWVzKHggPSBhdmdfbG9nMkZDLCB5ID0gLWxvZzEwKHBfdmFsX2FkaikpLCAKICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMsIHNoYXBlID0gMjEsIGZpbGwgPSAiYmxhY2siKSArCgogICMgQ3VzdG9tIGNvbG9yIHNjaGVtZQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKAogICAgIk1vc3QgVXByZWd1bGF0ZWQiID0gImRhcmtyZWQiLAogICAgIk1vc3QgRG93bnJlZ3VsYXRlZCIgPSAiZGFya2JsdWUiLAogICAgIlVwcmVndWxhdGVkIiA9ICJyZWQiLAogICAgIkRvd25yZWd1bGF0ZWQiID0gImJsdWUiLAogICAgIk5vdCBTaWduaWZpY2FudCIgPSAiZ3JleSIKICApKSArCgogICMgQWRkIGdlbmUgbGFiZWxzIChvbmx5IGZvciBoaWdobHkgc2lnbmlmaWNhbnQgZ2VuZXMpCiAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSB0b3BfZ2VuZXMsIGFlcyhsYWJlbCA9IGdlbmUpLCAgCiAgICAgICAgICAgICAgICAgIHNpemUgPSA0LCBib3gucGFkZGluZyA9IDAuNSwgbWF4Lm92ZXJsYXBzID0gMTAsIHNlZ21lbnQuY29sb3IgPSBOQSkgKwogIAogICMgQWRkIHRocmVzaG9sZCBsaW5lcwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLTIsIDIpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsgIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmxhY2siKSArICAKCiAgIyBJbXByb3ZlIHRoZW1lCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNCkgKwogIGxhYnModGl0bGUgPSAiVm9sY2FubyBQbG90OiBQc2V1ZG9idWxrIERFU2VxMiBBbmFseXNpcyIsCiAgICAgICB4ID0gIkxvZzIgRm9sZCBDaGFuZ2UiLAogICAgICAgeSA9ICItTG9nMTAgQWRqdXN0ZWQgUC1WYWx1ZSIsCiAgICAgICBjb2xvciA9ICJTaWduaWZpY2FuY2UiKSArCgogIHlsaW0oMCwgNTApICAjIEF2b2lkIGV4dHJlbWUgc2NhbGluZyBpc3N1ZXMKCgpgYGAKCgojIyBFbmhhbmNlZFZvbGNhbm8gcGxvdApgYGB7ciAsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNn0KCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQoKIyBBc3N1bWluZyB5b3UgaGF2ZSBhIGRhdGEgZnJhbWUgbmFtZWQgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzCiMgRmlsdGVyIGdlbmVzIGJhc2VkIG9uIGxvd2VzdCBwLXZhbHVlcyBidXQgaW5jbHVkZSBhbGwgZ2VuZXMKZmlsdGVyZWRfZ2VuZXMgPC0gbWFya2VycyAlPiUKICBhcnJhbmdlKHBfdmFsX2FkaiwgZGVzYyhhYnMoYXZnX2xvZzJGQykpKQoKIyBDcmVhdGUgdGhlIEVuaGFuY2VkVm9sY2FubyBwbG90IHdpdGggdGhlIGZpbHRlcmVkIGRhdGEKRW5oYW5jZWRWb2xjYW5vKAogIGZpbHRlcmVkX2dlbmVzLCAKICBsYWIgPSBpZmVsc2UoZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDFlLTYgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZzJGQykgPj0gMS41LCBmaWx0ZXJlZF9nZW5lcyRnZW5lLCBOQSksCiAgeCA9ICJhdmdfbG9nMkZDIiwgCiAgeSA9ICJwX3ZhbF9hZGoiLAogIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHBDdXRvZmYgPSAxZS02LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywgCiAgbGFiQ29sID0gJ2JsYWNrJywKICBsYWJGYWNlID0gJ2JvbGQnLAogIGJveGVkTGFiZWxzID0gRkFMU0UsICAjIFNldCB0byBGQUxTRSB0byByZW1vdmUgYm94ZWQgbGFiZWxzCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbWl6ZSBwb2ludCBjb2xvcnMKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuMF0gICMgT25seSBsYWJlbCBzaWduaWZpY2FudCBnZW5lcwopCgoKCmBgYAoKCiMjIEVuaGFuY2VkVm9sY2FubyBwbG90CmBgYHtyICwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKbGlicmFyeShkcGx5cikKCiMgRGVmaW5lIHRoZSBvdXRwdXQgZGlyZWN0b3J5Cm91dHB1dF9kaXIgPC0gIlZvbGNhbm9fUGxvdF9NYWxpZ25hbnRfdnNfQ29udHJvbCIKZGlyLmNyZWF0ZShvdXRwdXRfZGlyLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKCiBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgPC0gZmlsdGVyZWRfZ2VuZXMKCiMgRmlyc3QgVm9sY2FubyBQbG90CnAxIDwtIEVuaGFuY2VkVm9sY2FubygKICBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMsCiAgbGFiID0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGdlbmUsCiAgeCA9ICJhdmdfbG9nMkZDIiwKICB5ID0gInBfdmFsX2FkaiIsCiAgdGl0bGUgPSAiTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzIiwKICBwQ3V0b2ZmID0gMWUtNCwKICBGQ2N1dG9mZiA9IDEuMAopCnByaW50KHAxKSAgIyBEaXNwbGF5IGluIG5vdGVib29rCmdnc2F2ZShmaWxlbmFtZSA9IGZpbGUucGF0aChvdXRwdXRfZGlyLCAiVm9sY2Fub1Bsb3QxLnBuZyIpLCBwbG90ID0gcDEsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCgojIFNlY29uZCBWb2xjYW5vIFBsb3Qgd2l0aCBzZWxlY3RlZCBnZW5lcwpwMiA8LSBFbmhhbmNlZFZvbGNhbm8oCiAgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzLCAKICBsYWIgPSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMkZ2VuZSwKICB4ID0gImF2Z19sb2cyRkMiLCAKICB5ID0gInBfdmFsX2FkaiIsCiAgc2VsZWN0TGFiID0gYygnRVBDQU0nLCAnQkNBVDEnLCAnS0lSM0RMMicsICdGT1hNMScsICdUV0lTVDEnLCAnVE5GU0Y5JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdDRDgwJywgICdJTDFCJywgJ1JQUzRZMScsICJUT1giLCAiQ0Q1MiIsICJUV0lTVDEiLCAiQ0NSNCIsICJDQ1I3IiwiUERDRDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSUw3UicsICdUQ0Y3JywgICdNS0k2NycsICdDRDcwJywgIkRQUDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSUwyUkEnLCdUUkJWNi0yJywgJ1RSQlYxMC0zJywgJ1RSQlY0LTInLCAnVFJCVjknLCAnVFJCVjctOScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnVFJBVjEyLTEnLCAnQ0Q4QicsICdGQ0dSM0EnLCAnR05MWScsICdGT1hQMycsICdTRUxMJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdHSU1BUDEnLCAnUklQT1IyJywgJ0xFRjEnLCAnSE9YQzknLCAnU1A1JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0NDTDE3JywgJ0VUVjQnLCAnVEhZMScsICdGT1hBMicsICdJVEdBRCcsICdTMTAwUCcsICdUQlg0JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdJRDEnLCAnWENMMScsICdTT1gyJywgJ0NEMjcnLCAnQ0QyOCcsJ1BMUzMnLCdDRDcwJywnUkFCMjUnICwgJ1RSQlYyNycsICdUUkJWMicpLAogIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHhsYWIgPSBicXVvdGUofkxvZ1syXX4gJ2ZvbGQgY2hhbmdlJyksCiAgcEN1dG9mZiA9IDAuMDUsCiAgRkNjdXRvZmYgPSAxLjUsIAogIHBvaW50U2l6ZSA9IDMuMCwKICBsYWJTaXplID0gNS4wLAogIGJveGVkTGFiZWxzID0gVFJVRSwKICBjb2xBbHBoYSA9IDAuNSwKICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCiAgbGVnZW5kTGFiU2l6ZSA9IDEwLAogIGxlZ2VuZEljb25TaXplID0gNC4wLAogIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICB3aWR0aENvbm5lY3RvcnMgPSAwLjUsCiAgY29sQ29ubmVjdG9ycyA9ICdncmV5NTAnLAogIGFycm93aGVhZHMgPSBGQUxTRSwKICBtYXgub3ZlcmxhcHMgPSAzMAopCnByaW50KHAyKSAgIyBEaXNwbGF5IGluIG5vdGVib29rCmdnc2F2ZShmaWxlbmFtZSA9IGZpbGUucGF0aChvdXRwdXRfZGlyLCAiVm9sY2Fub1Bsb3QyLnBuZyIpLCBwbG90ID0gcDIsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCgojIEZpbHRlcmluZyBnZW5lcwpmaWx0ZXJlZF9nZW5lcyA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgJT4lCiAgYXJyYW5nZShwX3ZhbF9hZGosIGRlc2MoYWJzKGF2Z19sb2cyRkMpKSkKCiMgVGhpcmQgVm9sY2FubyBQbG90IC0gRmlsdGVyaW5nIGJ5IHAtdmFsdWUgYW5kIGxvZ0ZDCnAzIDwtIEVuaGFuY2VkVm9sY2FubygKICBmaWx0ZXJlZF9nZW5lcywgCiAgbGFiID0gaWZlbHNlKGZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAxZS00ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuMCwgZmlsdGVyZWRfZ2VuZXMkZ2VuZSwgTkEpLAogIHggPSAiYXZnX2xvZzJGQyIsIAogIHkgPSAicF92YWxfYWRqIiwKICB0aXRsZSA9ICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMoY2VsbCBsaW5lcykgdnMgbm9ybWFsIENENCBUIGNlbGxzIiwKICBwQ3V0b2ZmID0gMWUtNCwKICBGQ2N1dG9mZiA9IDEuMCwKICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsIAogIGxhYkNvbCA9ICdibGFjaycsCiAgbGFiRmFjZSA9ICdib2xkJywKICBib3hlZExhYmVscyA9IEZBTFNFLCAgIyBSZW1vdmUgYm94ZWQgbGFiZWxzCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbWl6ZSBwb2ludCBjb2xvcnMKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuMF0KKQpwcmludChwMykgICMgRGlzcGxheSBpbiBub3RlYm9vawpnZ3NhdmUoZmlsZW5hbWUgPSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlZvbGNhbm9QbG90My5wbmciKSwgcGxvdCA9IHAzLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwKQoKIyBGb3VydGggVm9sY2FubyBQbG90IC0gTW9yZSByZWZpbmVkIGZpbHRlcmluZwpwNCA8LSBFbmhhbmNlZFZvbGNhbm8oCiAgZmlsdGVyZWRfZ2VuZXMsIAogIGxhYiA9IGlmZWxzZShmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMWUtNCAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nMkZDKSA+PSAxLjAsIGZpbHRlcmVkX2dlbmVzJGdlbmUsIE5BKSwKICB4ID0gImF2Z19sb2cyRkMiLCAKICB5ID0gInBfdmFsX2FkaiIsCiAgdGl0bGUgPSAiTWFsaWduYW50IENENCBUIGNlbGxzIChjZWxsIGxpbmVzKSB2cyBOb3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHN1YnRpdGxlID0gIkhpZ2hsaWdodGluZyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMiLAogIHBDdXRvZmYgPSAxZS00LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywKICBjb2xBbHBoYSA9IDAuOCwgICMgU2xpZ2h0IHRyYW5zcGFyZW5jeSBmb3Igbm9uLXNpZ25pZmljYW50IHBvaW50cwogIGNvbCA9IGMoJ2dyZXk3MCcsICdibGFjaycsICdibHVlJywgJ3JlZCcpLCAgIyBDdXN0b20gY29sb3Igc2NoZW1lCiAgZ3JpZGxpbmVzLm1ham9yID0gVFJVRSwKICBncmlkbGluZXMubWlub3IgPSBGQUxTRSwKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuMF0KKQpwcmludChwNCkgICMgRGlzcGxheSBpbiBub3RlYm9vawpnZ3NhdmUoZmlsZW5hbWUgPSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlZvbGNhbm9QbG90NC5wbmciKSwgcGxvdCA9IHA0LCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwKQoKbWVzc2FnZSgiQWxsIHZvbGNhbm8gcGxvdHMgaGF2ZSBiZWVuIGRpc3BsYXllZCBhbmQgc2F2ZWQgc3VjY2Vzc2Z1bGx5IGluIHRoZSAnTWFsaWduYW50X3ZzX0NvbnRyb2wnIGZvbGRlci4iKQoKCgpgYGAKIyA1LiBFbnJpY2htZW50IEFuYWx5c2lzLUFsbF9QYXRod2F5cwpgYGB7ciAsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KFJlYWN0b21lUEEpCmxpYnJhcnkoRE9TRSkgIyBGb3IgR1NFQSBhbmFseXNpcwpsaWJyYXJ5KGdncGxvdDIpICMgRW5zdXJlIGdncGxvdDIgaXMgYXZhaWxhYmxlIGZvciBwbG90dGluZwpsaWJyYXJ5KGRwbHlyKQoKIyBEZWZpbmUgdGhlIG91dHB1dCBmb2xkZXIgd2hlcmUgdGhlIHJlc3VsdHMgd2lsbCBiZSBzYXZlZApvdXRwdXRfZm9sZGVyIDwtICJMNl92c19MNy8iCgojIENyZWF0ZSB0aGUgb3V0cHV0IGZvbGRlciBpZiBpdCBkb2Vzbid0IGV4aXN0CmlmICghZGlyLmV4aXN0cyhvdXRwdXRfZm9sZGVyKSkgewogIGRpci5jcmVhdGUob3V0cHV0X2ZvbGRlcikKfQoKIyBEZWZpbmUgdGhlIG51bWJlciBvZiB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcyB0byBzZWxlY3QKVVBfZ2VuZXMgPC0gMjUwCkRvd25fZ2VuZXMgPC0gMjUwCgojIERlZmluZSB0aHJlc2hvbGQgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHNlbGVjdGlvbiAobW9kaWZpZWQgdGhyZXNob2xkcykKbG9nRkNfdXBfdGhyZXNob2xkIDwtIDEgICAgICAgICAgIyBVcHJlZ3VsYXRlZCBsb2dGQyB0aHJlc2hvbGQKbG9nRkNfZG93bl90aHJlc2hvbGQgPC0gLTEgICAgICAgICAjIERvd25yZWd1bGF0ZWQgbG9nRkMgdGhyZXNob2xkCgojIExvYWQgeW91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzIChtb2RpZnkgYmFzZWQgb24gYWN0dWFsIGRhdGEgc3RydWN0dXJlKQojIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyA8LSByZWFkLmNzdigiWW91cl9ERV9SZXN1bHRzX0ZpbGUuY3N2IikKCiMgRmlsdGVyIHRoZSBnZW5lcyBiYXNlZCBvbiBhdmdfbG9nMkZDIGFuZCBhcnJhbmdlIGJ5IHBfdmFsX2FkagpmaWx0ZXJlZF9nZW5lcyA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgJT4lCiAgZmlsdGVyKGF2Z19sb2cyRkMgPiBsb2dGQ191cF90aHJlc2hvbGQgfCBhdmdfbG9nMkZDIDwgbG9nRkNfZG93bl90aHJlc2hvbGQpICU+JQogIGFycmFuZ2UocF92YWxfYWRqKQoKIyBTZXBhcmF0ZSB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcwp1cHJlZ3VsYXRlZF9nZW5lcyA8LSBmaWx0ZXJlZF9nZW5lcyAlPiUKICBmaWx0ZXIoYXZnX2xvZzJGQyA+IGxvZ0ZDX3VwX3RocmVzaG9sZCkKCmRvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gZmlsdGVyZWRfZ2VuZXMgJT4lCiAgZmlsdGVyKGF2Z19sb2cyRkMgPCBsb2dGQ19kb3duX3RocmVzaG9sZCkKCiMgQ2hlY2sgaWYgdGhlcmUgYXJlIGZld2VyIHRoYW4gdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMKaWYgKG5yb3codXByZWd1bGF0ZWRfZ2VuZXMpIDwgVVBfZ2VuZXMpIHsKICB0b3BfdXByZWd1bGF0ZWRfZ2VuZXMgPC0gdXByZWd1bGF0ZWRfZ2VuZXMKICBjYXQoIk51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcyBzZWxlY3RlZDoiLCBucm93KHRvcF91cHJlZ3VsYXRlZF9nZW5lcyksICJcbiIpCiAgY2F0KCJwX3ZhbF9hZGogdmFsdWUgZm9yIHRoZSBsYXN0IHNlbGVjdGVkIHVwcmVndWxhdGVkIGdlbmU6IiwgdGFpbCh0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkcF92YWxfYWRqLCAxKSwgIlxuIikKfSBlbHNlIHsKICAjIFNlbGVjdCB0aGUgc3BlY2lmaWVkIG51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcwogIHRvcF91cHJlZ3VsYXRlZF9nZW5lcyA8LSB1cHJlZ3VsYXRlZF9nZW5lcyAlPiUKICAgIGhlYWQoVVBfZ2VuZXMpCiAgY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgc2VsZWN0ZWQ6IiwgbnJvdyh0b3BfdXByZWd1bGF0ZWRfZ2VuZXMpLCAiXG4iKQogIGNhdCgicF92YWxfYWRqIHZhbHVlIGZvciB0aGUgbGFzdCBzZWxlY3RlZCB1cHJlZ3VsYXRlZCBnZW5lOiIsIHRhaWwodG9wX3VwcmVndWxhdGVkX2dlbmVzJHBfdmFsX2FkaiwgMSksICJcbiIpCn0KCiMgQ2hlY2sgaWYgdGhlcmUgYXJlIGZld2VyIHRoYW4gdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcwppZiAobnJvdyhkb3ducmVndWxhdGVkX2dlbmVzKSA8IERvd25fZ2VuZXMpIHsKICB0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyA8LSBkb3ducmVndWxhdGVkX2dlbmVzCiAgY2F0KCJOdW1iZXIgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcyBzZWxlY3RlZDoiLCBucm93KHRvcF9kb3ducmVndWxhdGVkX2dlbmVzKSwgIlxuIikKICBjYXQoInBfdmFsX2FkaiB2YWx1ZSBmb3IgdGhlIGxhc3Qgc2VsZWN0ZWQgZG93bnJlZ3VsYXRlZCBnZW5lOiIsIHRhaWwodG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkcF92YWxfYWRqLCAxKSwgIlxuIikKfSBlbHNlIHsKICAjIFNlbGVjdCB0aGUgc3BlY2lmaWVkIG51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzCiAgdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gZG93bnJlZ3VsYXRlZF9nZW5lcyAlPiUKICAgIGhlYWQoRG93bl9nZW5lcykKICBjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIHNlbGVjdGVkOiIsIG5yb3codG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMpLCAiXG4iKQogIGNhdCgicF92YWxfYWRqIHZhbHVlIGZvciB0aGUgbGFzdCBzZWxlY3RlZCBkb3ducmVndWxhdGVkIGdlbmU6IiwgdGFpbCh0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRwX3ZhbF9hZGosIDEpLCAiXG4iKQp9CgojIENvbWJpbmUgdGhlIHRvcCB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcwp0b3BfZ2VuZXMgPC0gYmluZF9yb3dzKHRvcF91cHJlZ3VsYXRlZF9nZW5lcywgdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMpCgojIENoZWNrIGZvciBtaXNzaW5nIGdlbmVzIChOQXMpIGluIHRoZSBnZW5lIGNvbHVtbiBhbmQgcmVtb3ZlIHRoZW0KdG9wX2dlbmVzIDwtIG5hLm9taXQodG9wX2dlbmVzKQoKIyBTYXZlIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIGdlbmUgcmVzdWx0cyB0byBDU1YKd3JpdGUuY3N2KHRvcF91cHJlZ3VsYXRlZF9nZW5lcywgcGFzdGUwKG91dHB1dF9mb2xkZXIsICJ1cHJlZ3VsYXRlZF9nZW5lcy5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdih0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcywgcGFzdGUwKG91dHB1dF9mb2xkZXIsICJkb3ducmVndWxhdGVkX2dlbmVzLmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgQ29udmVydCBnZW5lIHN5bWJvbHMgdG8gRW50cmV6IElEcyBmb3IgZW5yaWNobWVudCBhbmFseXNpcywgd2l0aCBjaGVja3MgZm9yIG1pc3NpbmcgdmFsdWVzCnVwcmVndWxhdGVkX2VudHJleiA8LSBiaXRyKHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IG9yZy5Icy5lZy5kYikKZG93bnJlZ3VsYXRlZF9lbnRyZXogPC0gYml0cih0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IG9yZy5Icy5lZy5kYikKCiMgQ2hlY2sgZm9yIG1pc3NpbmcgRW50cmV6IElEcyBhbmQgcmV0YWluIGdlbmUgbmFtZXMKbWlzc2luZ191cHJlZ3VsYXRlZCA8LSB0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZVshdG9wX3VwcmVndWxhdGVkX2dlbmVzJGdlbmUgJWluJSB1cHJlZ3VsYXRlZF9lbnRyZXokU1lNQk9MXQptaXNzaW5nX2Rvd25yZWd1bGF0ZWQgPC0gdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZVshdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSAlaW4lIGRvd25yZWd1bGF0ZWRfZW50cmV6JFNZTUJPTF0KCiMgUHJpbnQgb3V0IHRoZSBtaXNzaW5nIGdlbmUgc3ltYm9scyBmb3IgZGVidWdnaW5nCmNhdCgiTWlzc2luZyB1cHJlZ3VsYXRlZCBnZW5lczpcbiIsIG1pc3NpbmdfdXByZWd1bGF0ZWQsICJcbiIpCmNhdCgiTWlzc2luZyBkb3ducmVndWxhdGVkIGdlbmVzOlxuIiwgbWlzc2luZ19kb3ducmVndWxhdGVkLCAiXG4iKQoKIyBNZXJnZSB0aGUgRW50cmV6IElEcyBiYWNrIHdpdGggdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWVzIHRvIHJldGFpbiBnZW5lIG5hbWVzCnRvcF91cHJlZ3VsYXRlZF9nZW5lcyA8LSBtZXJnZSh0b3BfdXByZWd1bGF0ZWRfZ2VuZXMsIHVwcmVndWxhdGVkX2VudHJleiwgYnkueCA9ICJnZW5lIiwgYnkueSA9ICJTWU1CT0wiLCBhbGwueCA9IFRSVUUpCnRvcF9kb3ducmVndWxhdGVkX2dlbmVzIDwtIG1lcmdlKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzLCBkb3ducmVndWxhdGVkX2VudHJleiwgYnkueCA9ICJnZW5lIiwgYnkueSA9ICJTWU1CT0wiLCBhbGwueCA9IFRSVUUpCgojIFJlbW92ZSBnZW5lcyB0aGF0IGNvdWxkbid0IGJlIG1hcHBlZCB0byBFbnRyZXogSURzCnRvcF91cHJlZ3VsYXRlZF9nZW5lcyA8LSB0b3BfdXByZWd1bGF0ZWRfZ2VuZXNbIWlzLm5hKHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRFTlRSRVpJRCksIF0KdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXNbIWlzLm5hKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzJEVOVFJFWklEKSwgXQoKIyBFeHRyYWN0IEVudHJleiBJRHMgZm9yIGVucmljaG1lbnQgYW5hbHlzaXMKdXByZWd1bGF0ZWRfZW50cmV6IDwtIHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRFTlRSRVpJRApkb3ducmVndWxhdGVkX2VudHJleiA8LSB0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRFTlRSRVpJRAoKIyBEZWZpbmUgYSBmdW5jdGlvbiB0byBzYWZlbHkgcnVuIGVucmljaG1lbnQsIHBsb3QgcmVzdWx0cywgYW5kIHNhdmUgdGhlbQpzYWZlX2VucmljaEdPIDwtIGZ1bmN0aW9uKGdlbmVfbGlzdCwgdGl0bGUsIGZpbGVuYW1lKSB7CiAgaWYgKGxlbmd0aChnZW5lX2xpc3QpID4gMCkgewogICAgcmVzdWx0IDwtIGVucmljaEdPKGdlbmUgPSBnZW5lX2xpc3QsIE9yZ0RiID0gb3JnLkhzLmVnLmRiLCBrZXlUeXBlID0gIlNZTUJPTCIsCiAgICAgICAgICAgICAgICAgICAgICAgb250ID0gIkJQIiwgcEFkanVzdE1ldGhvZCA9ICJCSCIsIHB2YWx1ZUN1dG9mZiA9IDAuMDUsIHJlYWRhYmxlID0gVFJVRSkKICAgIGlmICghaXMubnVsbChyZXN1bHQpICYmIG5yb3coYXMuZGF0YS5mcmFtZShyZXN1bHQpKSA+IDApIHsKICAgICAgcCA8LSBkb3RwbG90KHJlc3VsdCwgc2hvd0NhdGVnb3J5ID0gMTAsIHRpdGxlID0gdGl0bGUpCiAgICAgIHByaW50KHApICAKICAgICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCBnc3ViKCIuY3N2IiwgIl9kb3RwbG90LnBuZyIsIGZpbGVuYW1lKSksIHBsb3QgPSBwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCiAgICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKHJlc3VsdCksIGZpbGUgPSBwYXN0ZTAob3V0cHV0X2ZvbGRlciwgZmlsZW5hbWUpLCByb3cubmFtZXMgPSBGQUxTRSkKICAgIH0gZWxzZSB7CiAgICAgIG1lc3NhZ2UocGFzdGUoIk5vIHNpZ25pZmljYW50IGVucmljaG1lbnQgZm91bmQgZm9yOiIsIHRpdGxlKSkKICAgIH0KICB9IGVsc2UgewogICAgbWVzc2FnZShwYXN0ZSgiTm8gZ2VuZXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICB9Cn0KCnNhZmVfZW5yaWNoS0VHRyA8LSBmdW5jdGlvbihlbnRyZXpfbGlzdCwgdGl0bGUsIGZpbGVuYW1lKSB7CiAgaWYgKGxlbmd0aChlbnRyZXpfbGlzdCkgPiAwKSB7CiAgICByZXN1bHQgPC0gZW5yaWNoS0VHRyhnZW5lID0gZW50cmV6X2xpc3QsIG9yZ2FuaXNtID0gImhzYSIsIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiAgICBpZiAoIWlzLm51bGwocmVzdWx0KSAmJiBucm93KGFzLmRhdGEuZnJhbWUocmVzdWx0KSkgPiAwKSB7CiAgICAgIHJlc3VsdCA8LSBzZXRSZWFkYWJsZShyZXN1bHQsIE9yZ0RiID0gb3JnLkhzLmVnLmRiLCBrZXlUeXBlID0gIkVOVFJFWklEIikKICAgICAgcCA8LSBkb3RwbG90KHJlc3VsdCwgc2hvd0NhdGVnb3J5ID0gMTAsIHRpdGxlID0gdGl0bGUpCiAgICAgIHByaW50KHApCiAgICAgIGdnc2F2ZShwYXN0ZTAob3V0cHV0X2ZvbGRlciwgZ3N1YigiLmNzdiIsICJfZG90cGxvdC5wbmciLCBmaWxlbmFtZSkpLCBwbG90ID0gcCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQogICAgICB3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShyZXN1bHQpLCBmaWxlID0gcGFzdGUwKG91dHB1dF9mb2xkZXIsIGZpbGVuYW1lKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgICB9IGVsc2UgewogICAgICBtZXNzYWdlKHBhc3RlKCJObyBzaWduaWZpY2FudCBLRUdHIHBhdGh3YXlzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgICB9CiAgfSBlbHNlIHsKICAgIG1lc3NhZ2UocGFzdGUoIk5vIGdlbmVzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgfQp9CgpzYWZlX2VucmljaFJlYWN0b21lIDwtIGZ1bmN0aW9uKGVudHJlel9saXN0LCB0aXRsZSwgZmlsZW5hbWUpIHsKICBpZiAobGVuZ3RoKGVudHJlel9saXN0KSA+IDApIHsKICAgIHJlc3VsdCA8LSBlbnJpY2hQYXRod2F5KGdlbmUgPSBlbnRyZXpfbGlzdCwgb3JnYW5pc20gPSAiaHVtYW4iLCBwdmFsdWVDdXRvZmYgPSAwLjA1KQogICAgaWYgKCFpcy5udWxsKHJlc3VsdCkgJiYgbnJvdyhhcy5kYXRhLmZyYW1lKHJlc3VsdCkpID4gMCkgewogICAgICByZXN1bHQgPC0gc2V0UmVhZGFibGUocmVzdWx0LCBPcmdEYiA9IG9yZy5Icy5lZy5kYiwga2V5VHlwZSA9ICJFTlRSRVpJRCIpCiAgICAgIHAgPC0gZG90cGxvdChyZXN1bHQsIHNob3dDYXRlZ29yeSA9IDEwLCB0aXRsZSA9IHRpdGxlKQogICAgICBwcmludChwKQogICAgICBnZ3NhdmUocGFzdGUwKG91dHB1dF9mb2xkZXIsIGdzdWIoIi5jc3YiLCAiX2RvdHBsb3QucG5nIiwgZmlsZW5hbWUpKSwgcGxvdCA9IHAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKICAgICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUocmVzdWx0KSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCBmaWxlbmFtZSksIHJvdy5uYW1lcyA9IEZBTFNFKQogICAgfSBlbHNlIHsKICAgICAgbWVzc2FnZShwYXN0ZSgiTm8gc2lnbmlmaWNhbnQgUmVhY3RvbWUgcGF0aHdheXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICAgIH0KICB9IGVsc2UgewogICAgbWVzc2FnZShwYXN0ZSgiTm8gZ2VuZXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICB9Cn0KCiMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2VzLCBnZW5lcmF0ZSBwbG90cywgYW5kIHNhdmUgcmVzdWx0cwpzYWZlX2VucmljaEdPKHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRnZW5lLCAiR08gRW5yaWNobWVudCBmb3IgVXByZWd1bGF0ZWQgR2VuZXMiLCAidXByZWd1bGF0ZWRfR09fcmVzdWx0cy5jc3YiKQpzYWZlX2VucmljaEdPKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzJGdlbmUsICJHTyBFbnJpY2htZW50IGZvciBEb3ducmVndWxhdGVkIEdlbmVzIiwgImRvd25yZWd1bGF0ZWRfR09fcmVzdWx0cy5jc3YiKQoKc2FmZV9lbnJpY2hLRUdHKHVwcmVndWxhdGVkX2VudHJleiwgIktFR0cgUGF0aHdheSBFbnJpY2htZW50IGZvciBVcHJlZ3VsYXRlZCBHZW5lcyIsICJ1cHJlZ3VsYXRlZF9LRUdHX3Jlc3VsdHMuY3N2IikKc2FmZV9lbnJpY2hLRUdHKGRvd25yZWd1bGF0ZWRfZW50cmV6LCAiS0VHRyBQYXRod2F5IEVucmljaG1lbnQgZm9yIERvd25yZWd1bGF0ZWQgR2VuZXMiLCAiZG93bnJlZ3VsYXRlZF9LRUdHX3Jlc3VsdHMuY3N2IikKCnNhZmVfZW5yaWNoUmVhY3RvbWUodXByZWd1bGF0ZWRfZW50cmV6LCAiUmVhY3RvbWUgUGF0aHdheSBFbnJpY2htZW50IGZvciBVcHJlZ3VsYXRlZCBHZW5lcyIsICJ1cHJlZ3VsYXRlZF9SZWFjdG9tZV9yZXN1bHRzLmNzdiIpCnNhZmVfZW5yaWNoUmVhY3RvbWUoZG93bnJlZ3VsYXRlZF9lbnRyZXosICJSZWFjdG9tZSBQYXRod2F5IEVucmljaG1lbnQgZm9yIERvd25yZWd1bGF0ZWQgR2VuZXMiLCAiZG93bnJlZ3VsYXRlZF9SZWFjdG9tZV9yZXN1bHRzLmNzdiIpCgpgYGAKCiMjIEVucmljaG1lbnQgQW5hbHlzaXNfSGFsbG1hcmsKYGBge3IgLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD04fQoKIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQpsaWJyYXJ5KG1zaWdkYnIpCmxpYnJhcnkoZW5yaWNocGxvdCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQoKIyBEZWZpbmUgdGhlIG91dHB1dCBmb2xkZXIgd2hlcmUgdGhlIHJlc3VsdHMgd2lsbCBiZSBzYXZlZApvdXRwdXRfZm9sZGVyIDwtICJMNl92c19MNy8iCgojIENyZWF0ZSB0aGUgb3V0cHV0IGZvbGRlciBpZiBpdCBkb2Vzbid0IGV4aXN0CmlmICghZGlyLmV4aXN0cyhvdXRwdXRfZm9sZGVyKSkgewogIGRpci5jcmVhdGUob3V0cHV0X2ZvbGRlcikKfQoKIyBMb2FkIEhhbGxtYXJrIGdlbmUgc2V0cyBmcm9tIG1zaWdkYnIKaGFsbG1hcmtfc2V0cyA8LSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY29sbGVjdGlvbiA9ICJIIikgICMgIkgiIGlzIGZvciBIYWxsbWFyayBnZW5lIHNldHMKCiMgQ29udmVydCBnZW5lIHN5bWJvbHMgdG8gdXBwZXJjYXNlIGZvciBjb25zaXN0ZW5jeQp0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSA8LSB0b3VwcGVyKHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRnZW5lKQp0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lIDwtIHRvdXBwZXIodG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSkKCiMgQ2hlY2sgZm9yIG92ZXJsYXAgYmV0d2VlbiB5b3VyIHVwcmVndWxhdGVkL2Rvd25yZWd1bGF0ZWQgZ2VuZXMgYW5kIEhhbGxtYXJrIGdlbmUgc2V0cwp1cHJlZ3VsYXRlZF9pbl9oYWxsbWFyayA8LSBpbnRlcnNlY3QodG9wX3VwcmVndWxhdGVkX2dlbmVzJGdlbmUsIGhhbGxtYXJrX3NldHMkZ2VuZV9zeW1ib2wpCmRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmsgPC0gaW50ZXJzZWN0KHRvcF9kb3ducmVndWxhdGVkX2dlbmVzJGdlbmUsIGhhbGxtYXJrX3NldHMkZ2VuZV9zeW1ib2wpCgojIFByaW50IHRoZSBudW1iZXIgb2Ygb3ZlcmxhcHBpbmcgZ2VuZXMgZm9yIGJvdGggdXByZWd1bGF0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMKY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgaW4gSGFsbG1hcmsgZ2VuZSBzZXRzOiIsIGxlbmd0aCh1cHJlZ3VsYXRlZF9pbl9oYWxsbWFyayksICJcbiIpCmNhdCgiTnVtYmVyIG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMgaW4gSGFsbG1hcmsgZ2VuZSBzZXRzOiIsIGxlbmd0aChkb3ducmVndWxhdGVkX2luX2hhbGxtYXJrKSwgIlxuIikKCiMgSWYgdGhlcmUgYXJlIGdlbmVzIHRvIGFuYWx5emUsIHByb2NlZWQgd2l0aCBlbnJpY2htZW50IGFuYWx5c2lzCmlmIChsZW5ndGgodXByZWd1bGF0ZWRfaW5faGFsbG1hcmspID4gMCkgewogICMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzIGZvciB1cHJlZ3VsYXRlZCBnZW5lcyB1c2luZyBIYWxsbWFyayBnZW5lIHNldHMKICBoYWxsbWFya191cCA8LSBlbnJpY2hlcihnZW5lID0gdXByZWd1bGF0ZWRfaW5faGFsbG1hcmssIAogICAgICAgICAgICAgICAgICAgICAgICAgIFRFUk0yR0VORSA9IGhhbGxtYXJrX3NldHNbLCBjKCJnc19uYW1lIiwgImdlbmVfc3ltYm9sIildLCAgIyBFbnN1cmUgVEVSTTJHRU5FIHVzZXMgY29ycmVjdCBjb2x1bW5zCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKICAjIENoZWNrIGlmIHJlc3VsdHMgZXhpc3QKICBpZiAoIWlzLm51bGwoaGFsbG1hcmtfdXApICYmIG5yb3coaGFsbG1hcmtfdXApID4gMCkgewogICAgIyBWaXN1YWxpemUgcmVzdWx0cyBpZiBhdmFpbGFibGUKICAgIHVwX2RvdHBsb3QgPC0gZG90cGxvdChoYWxsbWFya191cCwgc2hvd0NhdGVnb3J5ID0gMjAsIHRpdGxlID0gIkhhbGxtYXJrIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgVXByZWd1bGF0ZWQgR2VuZXMiKQogICAgCiAgICAjIERpc3BsYXkgdGhlIHBsb3QgaW4gdGhlIG5vdGVib29rCiAgICBwcmludCh1cF9kb3RwbG90KQogICAgCiAgICAjIFNhdmUgdGhlIGRvdHBsb3QgdG8gYSBQTkcgZmlsZQogICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfdXByZWd1bGF0ZWRfZG90cGxvdC5wbmciKSwgcGxvdCA9IHVwX2RvdHBsb3QsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCiAgICAKICAgICMgT3B0aW9uYWxseSwgc2F2ZSB0aGUgcmVzdWx0cyBhcyBDU1YKICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKGhhbGxtYXJrX3VwKSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfdXByZWd1bGF0ZWRfZW5yaWNobWVudC5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgfSBlbHNlIHsKICAgIGNhdCgiTm8gc2lnbmlmaWNhbnQgZW5yaWNobWVudCBmb3VuZCBmb3IgdXByZWd1bGF0ZWQgZ2VuZXMuXG4iKQogIH0KfSBlbHNlIHsKICBjYXQoIk5vIHVwcmVndWxhdGVkIGdlbmVzIG92ZXJsYXAgd2l0aCBIYWxsbWFyayBnZW5lIHNldHMuXG4iKQp9CgppZiAobGVuZ3RoKGRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmspID4gMCkgewogICMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzIGZvciBkb3ducmVndWxhdGVkIGdlbmVzIHVzaW5nIEhhbGxtYXJrIGdlbmUgc2V0cwogIGhhbGxtYXJrX2Rvd24gPC0gZW5yaWNoZXIoZ2VuZSA9IGRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVEVSTTJHRU5FID0gaGFsbG1hcmtfc2V0c1ssIGMoImdzX25hbWUiLCAiZ2VuZV9zeW1ib2wiKV0sICAjIEVuc3VyZSBURVJNMkdFTkUgdXNlcyBjb3JyZWN0IGNvbHVtbnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiAgIyBDaGVjayBpZiByZXN1bHRzIGV4aXN0CiAgaWYgKCFpcy5udWxsKGhhbGxtYXJrX2Rvd24pICYmIG5yb3coaGFsbG1hcmtfZG93bikgPiAwKSB7CiAgICAjIFZpc3VhbGl6ZSByZXN1bHRzIGlmIGF2YWlsYWJsZQogICAgZG93bl9kb3RwbG90IDwtIGRvdHBsb3QoaGFsbG1hcmtfZG93biwgc2hvd0NhdGVnb3J5ID0gMjAsIHRpdGxlID0gIkhhbGxtYXJrIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgRG93bnJlZ3VsYXRlZCBHZW5lcyIpCiAgICAKICAgICMgRGlzcGxheSB0aGUgcGxvdCBpbiB0aGUgbm90ZWJvb2sKICAgIHByaW50KGRvd25fZG90cGxvdCkKICAgIAogICAgIyBTYXZlIHRoZSBkb3RwbG90IHRvIGEgUE5HIGZpbGUKICAgIGdnc2F2ZShwYXN0ZTAob3V0cHV0X2ZvbGRlciwgImhhbGxtYXJrX2Rvd25yZWd1bGF0ZWRfZG90cGxvdC5wbmciKSwgcGxvdCA9IGRvd25fZG90cGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKICAgIAogICAgIyBPcHRpb25hbGx5LCBzYXZlIHRoZSByZXN1bHRzIGFzIENTVgogICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUoaGFsbG1hcmtfZG93biksIGZpbGUgPSBwYXN0ZTAob3V0cHV0X2ZvbGRlciwgImhhbGxtYXJrX2Rvd25yZWd1bGF0ZWRfZW5yaWNobWVudC5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgfSBlbHNlIHsKICAgIGNhdCgiTm8gc2lnbmlmaWNhbnQgZW5yaWNobWVudCBmb3VuZCBmb3IgZG93bnJlZ3VsYXRlZCBnZW5lcy5cbiIpCiAgfQp9IGVsc2UgewogIGNhdCgiTm8gZG93bnJlZ3VsYXRlZCBnZW5lcyBvdmVybGFwIHdpdGggSGFsbG1hcmsgZ2VuZSBzZXRzLlxuIikKfQoKCmBgYAoKCgo=