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: 365
Number of genes with p_val_adj = 1: 467
Number of upregulated genes (avg_log2FC > 1.5): 128
Number of downregulated genes (avg_log2FC < -1): 338
Number of significant genes (p_val_adj < 0.05): 3671
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: 365
Number of genes with p_val_adj = 1: 467
Number of upregulated genes (avg_log2FC > 1.5): 128
Number of downregulated genes (avg_log2FC < -1): 338
Number of significant genes (p_val_adj < 1e-4): 3468
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: 365
Number of genes with p_val_adj = 1: 467
Number of upregulated genes (avg_log2FC > 1.5): 128
Number of downregulated genes (avg_log2FC < -1): 338
Number of significant genes (p_val_adj < 1e-6): 3325
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: 365
Number of genes with p_val_adj = 1: 467
Number of upregulated genes (avg_log2FC > 1.5): 128
Number of downregulated genes (avg_log2FC < -1): 338
Number of significant genes (p_val_adj < 1e-10): 3064
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: 365
Number of genes with p_val_adj = 1: 467
Number of upregulated genes (avg_log2FC > 1.5): 128
Number of downregulated genes (avg_log2FC < -1): 338
Number of significant genes (p_val_adj < 1e-15): 2798
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)
Registered S3 methods overwritten by 'treeio':
method from
MRCA.phylo tidytree
MRCA.treedata tidytree
Nnode.treedata tidytree
Ntip.treedata tidytree
ancestor.phylo tidytree
ancestor.treedata tidytree
child.phylo tidytree
child.treedata tidytree
full_join.phylo tidytree
full_join.treedata tidytree
groupClade.phylo tidytree
groupClade.treedata tidytree
groupOTU.phylo tidytree
groupOTU.treedata tidytree
is.rooted.treedata tidytree
nodeid.phylo tidytree
nodeid.treedata tidytree
nodelab.phylo tidytree
nodelab.treedata tidytree
offspring.phylo tidytree
offspring.treedata tidytree
parent.phylo tidytree
parent.treedata tidytree
root.treedata tidytree
rootnode.phylo tidytree
sibling.phylo tidytree
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 <- "L5_vs_L6/"
# 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: 250
p_val_adj value for the last selected upregulated gene: 1.976288e-126
# 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: 250
p_val_adj value for the last selected downregulated gene: 1.091314e-134
# 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 : 3.6% 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 : 3.6% 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:
AC097518.2 AC068672.2 AC022613.1 AC092939.1 KIAA1211 AC004160.1 HIST1H1B MT-ATP8 CCDC58
cat("Missing downregulated genes:\n", missing_downregulated, "\n")
Missing downregulated genes:
AP001783.1 AC114977.1 AL606807.1 PALM2-AKAP2 AC104365.1 AC008875.3 AC099552.1 ARNTL C5orf56
# 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")
No significant Reactome pathways found for: Reactome Pathway Enrichment for Upregulated Genes
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 <- "L5_vs_L6/"
# 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: 103
cat("Number of downregulated genes in Hallmark gene sets:", length(downregulated_in_hallmark), "\n")
Number of downregulated genes in Hallmark gene sets: 102
# 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
LS0tCnRpdGxlOiAiRW5yaWNobWVudCBBbmFseXNpcyBvZiBMNUw2IGRlcml2ZWQgZnJvbSBQM19maWx0cmVkX29uX21lYW4iCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgIyBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICAjIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKICAjIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICAjcm1kZm9ybWF0czo6cmVhZHRoZWRvd24KICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19jb2xsYXBzZWQ6IHRydWUKLS0tCgojIDEuIGxvYWQgbGlicmFyaWVzCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQoKIyBMb2FkIGxpYnJhcmllcwpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KExpYnJhKQoKYGBgCgojIDIuIExvYWQgdGhlIGZpbHRlcmVkIGxpc3Qgb24gbWVhbiBleHByZXNzaW9uCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CgojIExvYWQgdGhlIERFIHJlc3VsdHMgZnJvbSBDU1YKZGYgPC0gcmVhZC5jc3YoIi4uL2NvbXBhcmlzb25fTDVfdnNfTDZfd2l0aF9tZWFuX2V4cHJlc3Npb25fZmlsdGVyZWQuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKCkRFX3Jlc3VsdHNfZGYgPC0gZGYKCmBgYAoKIyAzLiBTdW1tYXJpemUgTWFya2VycwpgYGB7ciAsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNH0KbWFya2VycyA8LSBERV9yZXN1bHRzX2RmCgpzdW1tYXJpemVfbWFya2VycyA8LSBmdW5jdGlvbihtYXJrZXJzKSB7CiAgbnVtX3B2YWwwIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAwKQogIG51bV9wdmFsMSA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPT0gMSkKICBudW1fdXByZWd1bGF0ZWQgPC0gc3VtKG1hcmtlcnMkYXZnX2xvZzJGQyA+IDEuNSkKICBudW1fZG93bnJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nMkZDIDwgLTEpCiAgbnVtX3NpZ25pZmljYW50IDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA8IDAuMDUpCiAgCiAgCiAgY2F0KCJOdW1iZXIgb2YgZ2VuZXMgd2l0aCBwX3ZhbF9hZGogPSAwOiIsIG51bV9wdmFsMCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDE6IiwgbnVtX3B2YWwxLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIHVwcmVndWxhdGVkIGdlbmVzIChhdmdfbG9nMkZDID4gMS41KToiLCBudW1fdXByZWd1bGF0ZWQsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcyAoYXZnX2xvZzJGQyA8IC0xKToiLCBudW1fZG93bnJlZ3VsYXRlZCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBzaWduaWZpY2FudCBnZW5lcyAocF92YWxfYWRqIDwgMC4wNSk6IiwgbnVtX3NpZ25pZmljYW50LCAiXG4iKQp9CgpjYXQoIk1hcmtlcnMgU3VtbWFyeSBhdCAwLjA1OlxuIikKCnN1bW1hcml6ZV9tYXJrZXJzKG1hcmtlcnMpCgptYXJrZXJzMiA8LSBERV9yZXN1bHRzX2RmCnN1bW1hcml6ZV9tYXJrZXJzIDwtIGZ1bmN0aW9uKG1hcmtlcnMpIHsKICBudW1fcHZhbDAgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqID09IDApCiAgbnVtX3B2YWwxIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAxKQogIG51bV91cHJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nMkZDID4gMS41KQogIG51bV9kb3ducmVndWxhdGVkIDwtIHN1bShtYXJrZXJzJGF2Z19sb2cyRkMgPCAtMSkKICBudW1fc2lnbmlmaWNhbnQgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqIDwgMWUtNCkKICAKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDA6IiwgbnVtX3B2YWwwLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMToiLCBudW1fcHZhbDEsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2cyRkMgPiAxLjUpOiIsIG51bV91cHJlZ3VsYXRlZCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIChhdmdfbG9nMkZDIDwgLTEpOiIsIG51bV9kb3ducmVndWxhdGVkLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIHNpZ25pZmljYW50IGdlbmVzIChwX3ZhbF9hZGogPCAxZS00KToiLCBudW1fc2lnbmlmaWNhbnQsICJcbiIpCn0KCmNhdCgiTWFya2VycyBTdW1tYXJ5IGF0IDFlLTQ6XG4iKQoKc3VtbWFyaXplX21hcmtlcnMobWFya2VyczIpCgptYXJrZXJzMyA8LSBERV9yZXN1bHRzX2RmCnN1bW1hcml6ZV9tYXJrZXJzIDwtIGZ1bmN0aW9uKG1hcmtlcnMpIHsKICBudW1fcHZhbDAgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqID09IDApCiAgbnVtX3B2YWwxIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAxKQogIG51bV91cHJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nMkZDID4gMS41KQogIG51bV9kb3ducmVndWxhdGVkIDwtIHN1bShtYXJrZXJzJGF2Z19sb2cyRkMgPCAtMSkKICBudW1fc2lnbmlmaWNhbnQgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqIDwgMWUtNikKICAKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDA6IiwgbnVtX3B2YWwwLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMToiLCBudW1fcHZhbDEsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2cyRkMgPiAxLjUpOiIsIG51bV91cHJlZ3VsYXRlZCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIChhdmdfbG9nMkZDIDwgLTEpOiIsIG51bV9kb3ducmVndWxhdGVkLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIHNpZ25pZmljYW50IGdlbmVzIChwX3ZhbF9hZGogPCAxZS02KToiLCBudW1fc2lnbmlmaWNhbnQsICJcbiIpCn0KCmNhdCgiTWFya2VycyBTdW1tYXJ5IGF0IDFlLTY6XG4iKQpzdW1tYXJpemVfbWFya2VycyhtYXJrZXJzMykKCm1hcmtlcnM0IDwtIERFX3Jlc3VsdHNfZGYKc3VtbWFyaXplX21hcmtlcnMgPC0gZnVuY3Rpb24obWFya2VycykgewogIG51bV9wdmFsMCA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPT0gMCkKICBudW1fcHZhbDEgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqID09IDEpCiAgbnVtX3VwcmVndWxhdGVkIDwtIHN1bShtYXJrZXJzJGF2Z19sb2cyRkMgPiAxLjUpCiAgbnVtX2Rvd25yZWd1bGF0ZWQgPC0gc3VtKG1hcmtlcnMkYXZnX2xvZzJGQyA8IC0xKQogIG51bV9zaWduaWZpY2FudCA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPCAxZS0xMCkKICAKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDA6IiwgbnVtX3B2YWwwLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMToiLCBudW1fcHZhbDEsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2cyRkMgPiAxLjUpOiIsIG51bV91cHJlZ3VsYXRlZCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIChhdmdfbG9nMkZDIDwgLTEpOiIsIG51bV9kb3ducmVndWxhdGVkLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIHNpZ25pZmljYW50IGdlbmVzIChwX3ZhbF9hZGogPCAxZS0xMCk6IiwgbnVtX3NpZ25pZmljYW50LCAiXG4iKQogIH0KCmNhdCgiTWFya2VycyBTdW1tYXJ5IGF0IDFlLTEwOlxuIikKCnN1bW1hcml6ZV9tYXJrZXJzKG1hcmtlcnM0KQoKbWFya2VyczUgPC0gREVfcmVzdWx0c19kZgpzdW1tYXJpemVfbWFya2VycyA8LSBmdW5jdGlvbihtYXJrZXJzKSB7CiAgbnVtX3B2YWwwIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAwKQogIG51bV9wdmFsMSA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPT0gMSkKICBudW1fdXByZWd1bGF0ZWQgPC0gc3VtKG1hcmtlcnMkYXZnX2xvZzJGQyA+IDEuNSkKICBudW1fZG93bnJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nMkZDIDwgLTEpCiAgbnVtX3NpZ25pZmljYW50IDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA8IDFlLTE1KQogIAogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMDoiLCBudW1fcHZhbDAsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgZ2VuZXMgd2l0aCBwX3ZhbF9hZGogPSAxOiIsIG51bV9wdmFsMSwgIlxuIikKICBjYXQoIk51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcyAoYXZnX2xvZzJGQyA+IDEuNSk6IiwgbnVtX3VwcmVndWxhdGVkLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2cyRkMgPCAtMSk6IiwgbnVtX2Rvd25yZWd1bGF0ZWQsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2Ygc2lnbmlmaWNhbnQgZ2VuZXMgKHBfdmFsX2FkaiA8IDFlLTE1KToiLCBudW1fc2lnbmlmaWNhbnQsICJcbiIpCn0KCmNhdCgiTWFya2VycyBTdW1tYXJ5IGF0IDFlLTE1OlxuIikKCnN1bW1hcml6ZV9tYXJrZXJzKG1hcmtlcnM1KQoKCgpgYGAKCgoKIyA0LiBWb2xjYW5vIFBsb3RzCmBgYHtyICwgZmlnLmhlaWdodD0xNCwgZmlnLndpZHRoPTE4fQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dyZXBlbCkKCgojIEVuc3VyZSBjb3JyZWN0IGNvbHVtbiBuYW1lcwpjb2xuYW1lcyhERV9yZXN1bHRzX2RmKQoKIyBEZWZpbmUgc2lnbmlmaWNhbmNlIGNhdGVnb3JpZXMKdm9sY2Fub19kYXRhIDwtIERFX3Jlc3VsdHNfZGYgJT4lCiAgbXV0YXRlKAogICAgc2lnbmlmaWNhbmNlID0gY2FzZV93aGVuKAogICAgICBwX3ZhbF9hZGogPCAxZS0yMCAmIGF2Z19sb2cyRkMgPiAyIH4gIk1vc3QgVXByZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAxZS0yMCAmIGF2Z19sb2cyRkMgPCAtMiB+ICJNb3N0IERvd25yZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAwLjA1ICYgYXZnX2xvZzJGQyA+IDIgfiAiVXByZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAwLjA1ICYgYXZnX2xvZzJGQyA8IC0yIH4gIkRvd25yZWd1bGF0ZWQiLAogICAgICBUUlVFIH4gIk5vdCBTaWduaWZpY2FudCIKICAgICkKICApCgojIFNlbGVjdCBvbmx5IHZlcnkgc2lnbmlmaWNhbnQgZ2VuZXMgZm9yIGxhYmVsaW5nCnRvcF9nZW5lcyA8LSB2b2xjYW5vX2RhdGEgJT4lCiAgZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUgJiAoYXZnX2xvZzJGQyA+IDIgfCBhdmdfbG9nMkZDIDwgLTIpKQoKZ2dwbG90KHZvbGNhbm9fZGF0YSwgYWVzKHggPSBhdmdfbG9nMkZDLCB5ID0gLWxvZzEwKHBfdmFsX2FkaiksIGNvbG9yID0gc2lnbmlmaWNhbmNlKSkgKwogIAogICMgTWFpbiBwb2ludHMKICBnZW9tX3BvaW50KGFscGhhID0gMC43LCBzaXplID0gMi41KSArCiAgCiAgIyBIaWdobGlnaHQgaGlnaGx5IHNpZ25pZmljYW50IGdlbmVzIHdpdGggbGFyZ2VyIHBvaW50cwogIGdlb21fcG9pbnQoZGF0YSA9IHRvcF9nZW5lcywgYWVzKHggPSBhdmdfbG9nMkZDLCB5ID0gLWxvZzEwKHBfdmFsX2FkaikpLCAKICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMsIHNoYXBlID0gMjEsIGZpbGwgPSAiYmxhY2siKSArCgogICMgQ3VzdG9tIGNvbG9yIHNjaGVtZQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKAogICAgIk1vc3QgVXByZWd1bGF0ZWQiID0gImRhcmtyZWQiLAogICAgIk1vc3QgRG93bnJlZ3VsYXRlZCIgPSAiZGFya2JsdWUiLAogICAgIlVwcmVndWxhdGVkIiA9ICJyZWQiLAogICAgIkRvd25yZWd1bGF0ZWQiID0gImJsdWUiLAogICAgIk5vdCBTaWduaWZpY2FudCIgPSAiZ3JleSIKICApKSArCgogICMgQWRkIGdlbmUgbGFiZWxzIChvbmx5IGZvciBoaWdobHkgc2lnbmlmaWNhbnQgZ2VuZXMpCiAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSB0b3BfZ2VuZXMsIGFlcyhsYWJlbCA9IGdlbmUpLCAgCiAgICAgICAgICAgICAgICAgIHNpemUgPSA0LCBib3gucGFkZGluZyA9IDAuNSwgbWF4Lm92ZXJsYXBzID0gMTAsIHNlZ21lbnQuY29sb3IgPSBOQSkgKwogIAogICMgQWRkIHRocmVzaG9sZCBsaW5lcwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLTIsIDIpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsgIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmxhY2siKSArICAKCiAgIyBJbXByb3ZlIHRoZW1lCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNCkgKwogIGxhYnModGl0bGUgPSAiVm9sY2FubyBQbG90OiBQc2V1ZG9idWxrIERFU2VxMiBBbmFseXNpcyIsCiAgICAgICB4ID0gIkxvZzIgRm9sZCBDaGFuZ2UiLAogICAgICAgeSA9ICItTG9nMTAgQWRqdXN0ZWQgUC1WYWx1ZSIsCiAgICAgICBjb2xvciA9ICJTaWduaWZpY2FuY2UiKSArCgogIHlsaW0oMCwgNTApICAjIEF2b2lkIGV4dHJlbWUgc2NhbGluZyBpc3N1ZXMKCgpgYGAKCgojIyBFbmhhbmNlZFZvbGNhbm8gcGxvdApgYGB7ciAsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNn0KCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQoKIyBBc3N1bWluZyB5b3UgaGF2ZSBhIGRhdGEgZnJhbWUgbmFtZWQgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzCiMgRmlsdGVyIGdlbmVzIGJhc2VkIG9uIGxvd2VzdCBwLXZhbHVlcyBidXQgaW5jbHVkZSBhbGwgZ2VuZXMKZmlsdGVyZWRfZ2VuZXMgPC0gbWFya2VycyAlPiUKICBhcnJhbmdlKHBfdmFsX2FkaiwgZGVzYyhhYnMoYXZnX2xvZzJGQykpKQoKIyBDcmVhdGUgdGhlIEVuaGFuY2VkVm9sY2FubyBwbG90IHdpdGggdGhlIGZpbHRlcmVkIGRhdGEKRW5oYW5jZWRWb2xjYW5vKAogIGZpbHRlcmVkX2dlbmVzLCAKICBsYWIgPSBpZmVsc2UoZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDFlLTYgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZzJGQykgPj0gMS41LCBmaWx0ZXJlZF9nZW5lcyRnZW5lLCBOQSksCiAgeCA9ICJhdmdfbG9nMkZDIiwgCiAgeSA9ICJwX3ZhbF9hZGoiLAogIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHBDdXRvZmYgPSAxZS02LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywgCiAgbGFiQ29sID0gJ2JsYWNrJywKICBsYWJGYWNlID0gJ2JvbGQnLAogIGJveGVkTGFiZWxzID0gRkFMU0UsICAjIFNldCB0byBGQUxTRSB0byByZW1vdmUgYm94ZWQgbGFiZWxzCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbWl6ZSBwb2ludCBjb2xvcnMKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuMF0gICMgT25seSBsYWJlbCBzaWduaWZpY2FudCBnZW5lcwopCgoKCmBgYAoKCiMjIEVuaGFuY2VkVm9sY2FubyBwbG90CmBgYHtyICwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKbGlicmFyeShkcGx5cikKCiMgRGVmaW5lIHRoZSBvdXRwdXQgZGlyZWN0b3J5Cm91dHB1dF9kaXIgPC0gIlZvbGNhbm9fUGxvdF9NYWxpZ25hbnRfdnNfQ29udHJvbCIKZGlyLmNyZWF0ZShvdXRwdXRfZGlyLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKCiBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgPC0gZmlsdGVyZWRfZ2VuZXMKCiMgRmlyc3QgVm9sY2FubyBQbG90CnAxIDwtIEVuaGFuY2VkVm9sY2FubygKICBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMsCiAgbGFiID0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGdlbmUsCiAgeCA9ICJhdmdfbG9nMkZDIiwKICB5ID0gInBfdmFsX2FkaiIsCiAgdGl0bGUgPSAiTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzIiwKICBwQ3V0b2ZmID0gMWUtNCwKICBGQ2N1dG9mZiA9IDEuMAopCnByaW50KHAxKSAgIyBEaXNwbGF5IGluIG5vdGVib29rCmdnc2F2ZShmaWxlbmFtZSA9IGZpbGUucGF0aChvdXRwdXRfZGlyLCAiVm9sY2Fub1Bsb3QxLnBuZyIpLCBwbG90ID0gcDEsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCgojIFNlY29uZCBWb2xjYW5vIFBsb3Qgd2l0aCBzZWxlY3RlZCBnZW5lcwpwMiA8LSBFbmhhbmNlZFZvbGNhbm8oCiAgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzLCAKICBsYWIgPSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMkZ2VuZSwKICB4ID0gImF2Z19sb2cyRkMiLCAKICB5ID0gInBfdmFsX2FkaiIsCiAgc2VsZWN0TGFiID0gYygnRVBDQU0nLCAnQkNBVDEnLCAnS0lSM0RMMicsICdGT1hNMScsICdUV0lTVDEnLCAnVE5GU0Y5JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdDRDgwJywgICdJTDFCJywgJ1JQUzRZMScsICJUT1giLCAiQ0Q1MiIsICJUV0lTVDEiLCAiQ0NSNCIsICJDQ1I3IiwiUERDRDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSUw3UicsICdUQ0Y3JywgICdNS0k2NycsICdDRDcwJywgIkRQUDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSUwyUkEnLCdUUkJWNi0yJywgJ1RSQlYxMC0zJywgJ1RSQlY0LTInLCAnVFJCVjknLCAnVFJCVjctOScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnVFJBVjEyLTEnLCAnQ0Q4QicsICdGQ0dSM0EnLCAnR05MWScsICdGT1hQMycsICdTRUxMJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdHSU1BUDEnLCAnUklQT1IyJywgJ0xFRjEnLCAnSE9YQzknLCAnU1A1JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0NDTDE3JywgJ0VUVjQnLCAnVEhZMScsICdGT1hBMicsICdJVEdBRCcsICdTMTAwUCcsICdUQlg0JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdJRDEnLCAnWENMMScsICdTT1gyJywgJ0NEMjcnLCAnQ0QyOCcsJ1BMUzMnLCdDRDcwJywnUkFCMjUnICwgJ1RSQlYyNycsICdUUkJWMicpLAogIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHhsYWIgPSBicXVvdGUofkxvZ1syXX4gJ2ZvbGQgY2hhbmdlJyksCiAgcEN1dG9mZiA9IDAuMDUsCiAgRkNjdXRvZmYgPSAxLjUsIAogIHBvaW50U2l6ZSA9IDMuMCwKICBsYWJTaXplID0gNS4wLAogIGJveGVkTGFiZWxzID0gVFJVRSwKICBjb2xBbHBoYSA9IDAuNSwKICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCiAgbGVnZW5kTGFiU2l6ZSA9IDEwLAogIGxlZ2VuZEljb25TaXplID0gNC4wLAogIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICB3aWR0aENvbm5lY3RvcnMgPSAwLjUsCiAgY29sQ29ubmVjdG9ycyA9ICdncmV5NTAnLAogIGFycm93aGVhZHMgPSBGQUxTRSwKICBtYXgub3ZlcmxhcHMgPSAzMAopCnByaW50KHAyKSAgIyBEaXNwbGF5IGluIG5vdGVib29rCmdnc2F2ZShmaWxlbmFtZSA9IGZpbGUucGF0aChvdXRwdXRfZGlyLCAiVm9sY2Fub1Bsb3QyLnBuZyIpLCBwbG90ID0gcDIsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCgojIEZpbHRlcmluZyBnZW5lcwpmaWx0ZXJlZF9nZW5lcyA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgJT4lCiAgYXJyYW5nZShwX3ZhbF9hZGosIGRlc2MoYWJzKGF2Z19sb2cyRkMpKSkKCiMgVGhpcmQgVm9sY2FubyBQbG90IC0gRmlsdGVyaW5nIGJ5IHAtdmFsdWUgYW5kIGxvZ0ZDCnAzIDwtIEVuaGFuY2VkVm9sY2FubygKICBmaWx0ZXJlZF9nZW5lcywgCiAgbGFiID0gaWZlbHNlKGZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAxZS00ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuMCwgZmlsdGVyZWRfZ2VuZXMkZ2VuZSwgTkEpLAogIHggPSAiYXZnX2xvZzJGQyIsIAogIHkgPSAicF92YWxfYWRqIiwKICB0aXRsZSA9ICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMoY2VsbCBsaW5lcykgdnMgbm9ybWFsIENENCBUIGNlbGxzIiwKICBwQ3V0b2ZmID0gMWUtNCwKICBGQ2N1dG9mZiA9IDEuMCwKICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsIAogIGxhYkNvbCA9ICdibGFjaycsCiAgbGFiRmFjZSA9ICdib2xkJywKICBib3hlZExhYmVscyA9IEZBTFNFLCAgIyBSZW1vdmUgYm94ZWQgbGFiZWxzCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbWl6ZSBwb2ludCBjb2xvcnMKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuMF0KKQpwcmludChwMykgICMgRGlzcGxheSBpbiBub3RlYm9vawpnZ3NhdmUoZmlsZW5hbWUgPSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlZvbGNhbm9QbG90My5wbmciKSwgcGxvdCA9IHAzLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwKQoKIyBGb3VydGggVm9sY2FubyBQbG90IC0gTW9yZSByZWZpbmVkIGZpbHRlcmluZwpwNCA8LSBFbmhhbmNlZFZvbGNhbm8oCiAgZmlsdGVyZWRfZ2VuZXMsIAogIGxhYiA9IGlmZWxzZShmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMWUtNCAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nMkZDKSA+PSAxLjAsIGZpbHRlcmVkX2dlbmVzJGdlbmUsIE5BKSwKICB4ID0gImF2Z19sb2cyRkMiLCAKICB5ID0gInBfdmFsX2FkaiIsCiAgdGl0bGUgPSAiTWFsaWduYW50IENENCBUIGNlbGxzIChjZWxsIGxpbmVzKSB2cyBOb3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHN1YnRpdGxlID0gIkhpZ2hsaWdodGluZyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMiLAogIHBDdXRvZmYgPSAxZS00LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywKICBjb2xBbHBoYSA9IDAuOCwgICMgU2xpZ2h0IHRyYW5zcGFyZW5jeSBmb3Igbm9uLXNpZ25pZmljYW50IHBvaW50cwogIGNvbCA9IGMoJ2dyZXk3MCcsICdibGFjaycsICdibHVlJywgJ3JlZCcpLCAgIyBDdXN0b20gY29sb3Igc2NoZW1lCiAgZ3JpZGxpbmVzLm1ham9yID0gVFJVRSwKICBncmlkbGluZXMubWlub3IgPSBGQUxTRSwKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuMF0KKQpwcmludChwNCkgICMgRGlzcGxheSBpbiBub3RlYm9vawpnZ3NhdmUoZmlsZW5hbWUgPSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlZvbGNhbm9QbG90NC5wbmciKSwgcGxvdCA9IHA0LCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwKQoKbWVzc2FnZSgiQWxsIHZvbGNhbm8gcGxvdHMgaGF2ZSBiZWVuIGRpc3BsYXllZCBhbmQgc2F2ZWQgc3VjY2Vzc2Z1bGx5IGluIHRoZSAnTWFsaWduYW50X3ZzX0NvbnRyb2wnIGZvbGRlci4iKQoKCgpgYGAKIyA1LiBFbnJpY2htZW50IEFuYWx5c2lzLUFsbF9QYXRod2F5cwpgYGB7ciAsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KFJlYWN0b21lUEEpCmxpYnJhcnkoRE9TRSkgIyBGb3IgR1NFQSBhbmFseXNpcwpsaWJyYXJ5KGdncGxvdDIpICMgRW5zdXJlIGdncGxvdDIgaXMgYXZhaWxhYmxlIGZvciBwbG90dGluZwpsaWJyYXJ5KGRwbHlyKQoKIyBEZWZpbmUgdGhlIG91dHB1dCBmb2xkZXIgd2hlcmUgdGhlIHJlc3VsdHMgd2lsbCBiZSBzYXZlZApvdXRwdXRfZm9sZGVyIDwtICJMNV92c19MNi8iCgojIENyZWF0ZSB0aGUgb3V0cHV0IGZvbGRlciBpZiBpdCBkb2Vzbid0IGV4aXN0CmlmICghZGlyLmV4aXN0cyhvdXRwdXRfZm9sZGVyKSkgewogIGRpci5jcmVhdGUob3V0cHV0X2ZvbGRlcikKfQoKIyBEZWZpbmUgdGhlIG51bWJlciBvZiB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcyB0byBzZWxlY3QKVVBfZ2VuZXMgPC0gMjUwCkRvd25fZ2VuZXMgPC0gMjUwCgojIERlZmluZSB0aHJlc2hvbGQgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHNlbGVjdGlvbiAobW9kaWZpZWQgdGhyZXNob2xkcykKbG9nRkNfdXBfdGhyZXNob2xkIDwtIDEgICAgICAgICAgIyBVcHJlZ3VsYXRlZCBsb2dGQyB0aHJlc2hvbGQKbG9nRkNfZG93bl90aHJlc2hvbGQgPC0gLTEgICAgICAgICAjIERvd25yZWd1bGF0ZWQgbG9nRkMgdGhyZXNob2xkCgojIExvYWQgeW91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzIChtb2RpZnkgYmFzZWQgb24gYWN0dWFsIGRhdGEgc3RydWN0dXJlKQojIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyA8LSByZWFkLmNzdigiWW91cl9ERV9SZXN1bHRzX0ZpbGUuY3N2IikKCiMgRmlsdGVyIHRoZSBnZW5lcyBiYXNlZCBvbiBhdmdfbG9nMkZDIGFuZCBhcnJhbmdlIGJ5IHBfdmFsX2FkagpmaWx0ZXJlZF9nZW5lcyA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgJT4lCiAgZmlsdGVyKGF2Z19sb2cyRkMgPiBsb2dGQ191cF90aHJlc2hvbGQgfCBhdmdfbG9nMkZDIDwgbG9nRkNfZG93bl90aHJlc2hvbGQpICU+JQogIGFycmFuZ2UocF92YWxfYWRqKQoKIyBTZXBhcmF0ZSB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcwp1cHJlZ3VsYXRlZF9nZW5lcyA8LSBmaWx0ZXJlZF9nZW5lcyAlPiUKICBmaWx0ZXIoYXZnX2xvZzJGQyA+IGxvZ0ZDX3VwX3RocmVzaG9sZCkKCmRvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gZmlsdGVyZWRfZ2VuZXMgJT4lCiAgZmlsdGVyKGF2Z19sb2cyRkMgPCBsb2dGQ19kb3duX3RocmVzaG9sZCkKCiMgQ2hlY2sgaWYgdGhlcmUgYXJlIGZld2VyIHRoYW4gdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMKaWYgKG5yb3codXByZWd1bGF0ZWRfZ2VuZXMpIDwgVVBfZ2VuZXMpIHsKICB0b3BfdXByZWd1bGF0ZWRfZ2VuZXMgPC0gdXByZWd1bGF0ZWRfZ2VuZXMKICBjYXQoIk51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcyBzZWxlY3RlZDoiLCBucm93KHRvcF91cHJlZ3VsYXRlZF9nZW5lcyksICJcbiIpCiAgY2F0KCJwX3ZhbF9hZGogdmFsdWUgZm9yIHRoZSBsYXN0IHNlbGVjdGVkIHVwcmVndWxhdGVkIGdlbmU6IiwgdGFpbCh0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkcF92YWxfYWRqLCAxKSwgIlxuIikKfSBlbHNlIHsKICAjIFNlbGVjdCB0aGUgc3BlY2lmaWVkIG51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcwogIHRvcF91cHJlZ3VsYXRlZF9nZW5lcyA8LSB1cHJlZ3VsYXRlZF9nZW5lcyAlPiUKICAgIGhlYWQoVVBfZ2VuZXMpCiAgY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgc2VsZWN0ZWQ6IiwgbnJvdyh0b3BfdXByZWd1bGF0ZWRfZ2VuZXMpLCAiXG4iKQogIGNhdCgicF92YWxfYWRqIHZhbHVlIGZvciB0aGUgbGFzdCBzZWxlY3RlZCB1cHJlZ3VsYXRlZCBnZW5lOiIsIHRhaWwodG9wX3VwcmVndWxhdGVkX2dlbmVzJHBfdmFsX2FkaiwgMSksICJcbiIpCn0KCiMgQ2hlY2sgaWYgdGhlcmUgYXJlIGZld2VyIHRoYW4gdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcwppZiAobnJvdyhkb3ducmVndWxhdGVkX2dlbmVzKSA8IERvd25fZ2VuZXMpIHsKICB0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyA8LSBkb3ducmVndWxhdGVkX2dlbmVzCiAgY2F0KCJOdW1iZXIgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcyBzZWxlY3RlZDoiLCBucm93KHRvcF9kb3ducmVndWxhdGVkX2dlbmVzKSwgIlxuIikKICBjYXQoInBfdmFsX2FkaiB2YWx1ZSBmb3IgdGhlIGxhc3Qgc2VsZWN0ZWQgZG93bnJlZ3VsYXRlZCBnZW5lOiIsIHRhaWwodG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkcF92YWxfYWRqLCAxKSwgIlxuIikKfSBlbHNlIHsKICAjIFNlbGVjdCB0aGUgc3BlY2lmaWVkIG51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzCiAgdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gZG93bnJlZ3VsYXRlZF9nZW5lcyAlPiUKICAgIGhlYWQoRG93bl9nZW5lcykKICBjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIHNlbGVjdGVkOiIsIG5yb3codG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMpLCAiXG4iKQogIGNhdCgicF92YWxfYWRqIHZhbHVlIGZvciB0aGUgbGFzdCBzZWxlY3RlZCBkb3ducmVndWxhdGVkIGdlbmU6IiwgdGFpbCh0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRwX3ZhbF9hZGosIDEpLCAiXG4iKQp9CgojIENvbWJpbmUgdGhlIHRvcCB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcwp0b3BfZ2VuZXMgPC0gYmluZF9yb3dzKHRvcF91cHJlZ3VsYXRlZF9nZW5lcywgdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMpCgojIENoZWNrIGZvciBtaXNzaW5nIGdlbmVzIChOQXMpIGluIHRoZSBnZW5lIGNvbHVtbiBhbmQgcmVtb3ZlIHRoZW0KdG9wX2dlbmVzIDwtIG5hLm9taXQodG9wX2dlbmVzKQoKIyBTYXZlIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIGdlbmUgcmVzdWx0cyB0byBDU1YKd3JpdGUuY3N2KHRvcF91cHJlZ3VsYXRlZF9nZW5lcywgcGFzdGUwKG91dHB1dF9mb2xkZXIsICJ1cHJlZ3VsYXRlZF9nZW5lcy5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdih0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcywgcGFzdGUwKG91dHB1dF9mb2xkZXIsICJkb3ducmVndWxhdGVkX2dlbmVzLmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgQ29udmVydCBnZW5lIHN5bWJvbHMgdG8gRW50cmV6IElEcyBmb3IgZW5yaWNobWVudCBhbmFseXNpcywgd2l0aCBjaGVja3MgZm9yIG1pc3NpbmcgdmFsdWVzCnVwcmVndWxhdGVkX2VudHJleiA8LSBiaXRyKHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IG9yZy5Icy5lZy5kYikKZG93bnJlZ3VsYXRlZF9lbnRyZXogPC0gYml0cih0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IG9yZy5Icy5lZy5kYikKCiMgQ2hlY2sgZm9yIG1pc3NpbmcgRW50cmV6IElEcyBhbmQgcmV0YWluIGdlbmUgbmFtZXMKbWlzc2luZ191cHJlZ3VsYXRlZCA8LSB0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZVshdG9wX3VwcmVndWxhdGVkX2dlbmVzJGdlbmUgJWluJSB1cHJlZ3VsYXRlZF9lbnRyZXokU1lNQk9MXQptaXNzaW5nX2Rvd25yZWd1bGF0ZWQgPC0gdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZVshdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSAlaW4lIGRvd25yZWd1bGF0ZWRfZW50cmV6JFNZTUJPTF0KCiMgUHJpbnQgb3V0IHRoZSBtaXNzaW5nIGdlbmUgc3ltYm9scyBmb3IgZGVidWdnaW5nCmNhdCgiTWlzc2luZyB1cHJlZ3VsYXRlZCBnZW5lczpcbiIsIG1pc3NpbmdfdXByZWd1bGF0ZWQsICJcbiIpCmNhdCgiTWlzc2luZyBkb3ducmVndWxhdGVkIGdlbmVzOlxuIiwgbWlzc2luZ19kb3ducmVndWxhdGVkLCAiXG4iKQoKIyBNZXJnZSB0aGUgRW50cmV6IElEcyBiYWNrIHdpdGggdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWVzIHRvIHJldGFpbiBnZW5lIG5hbWVzCnRvcF91cHJlZ3VsYXRlZF9nZW5lcyA8LSBtZXJnZSh0b3BfdXByZWd1bGF0ZWRfZ2VuZXMsIHVwcmVndWxhdGVkX2VudHJleiwgYnkueCA9ICJnZW5lIiwgYnkueSA9ICJTWU1CT0wiLCBhbGwueCA9IFRSVUUpCnRvcF9kb3ducmVndWxhdGVkX2dlbmVzIDwtIG1lcmdlKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzLCBkb3ducmVndWxhdGVkX2VudHJleiwgYnkueCA9ICJnZW5lIiwgYnkueSA9ICJTWU1CT0wiLCBhbGwueCA9IFRSVUUpCgojIFJlbW92ZSBnZW5lcyB0aGF0IGNvdWxkbid0IGJlIG1hcHBlZCB0byBFbnRyZXogSURzCnRvcF91cHJlZ3VsYXRlZF9nZW5lcyA8LSB0b3BfdXByZWd1bGF0ZWRfZ2VuZXNbIWlzLm5hKHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRFTlRSRVpJRCksIF0KdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gdG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXNbIWlzLm5hKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzJEVOVFJFWklEKSwgXQoKIyBFeHRyYWN0IEVudHJleiBJRHMgZm9yIGVucmljaG1lbnQgYW5hbHlzaXMKdXByZWd1bGF0ZWRfZW50cmV6IDwtIHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRFTlRSRVpJRApkb3ducmVndWxhdGVkX2VudHJleiA8LSB0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRFTlRSRVpJRAoKIyBEZWZpbmUgYSBmdW5jdGlvbiB0byBzYWZlbHkgcnVuIGVucmljaG1lbnQsIHBsb3QgcmVzdWx0cywgYW5kIHNhdmUgdGhlbQpzYWZlX2VucmljaEdPIDwtIGZ1bmN0aW9uKGdlbmVfbGlzdCwgdGl0bGUsIGZpbGVuYW1lKSB7CiAgaWYgKGxlbmd0aChnZW5lX2xpc3QpID4gMCkgewogICAgcmVzdWx0IDwtIGVucmljaEdPKGdlbmUgPSBnZW5lX2xpc3QsIE9yZ0RiID0gb3JnLkhzLmVnLmRiLCBrZXlUeXBlID0gIlNZTUJPTCIsCiAgICAgICAgICAgICAgICAgICAgICAgb250ID0gIkJQIiwgcEFkanVzdE1ldGhvZCA9ICJCSCIsIHB2YWx1ZUN1dG9mZiA9IDAuMDUsIHJlYWRhYmxlID0gVFJVRSkKICAgIGlmICghaXMubnVsbChyZXN1bHQpICYmIG5yb3coYXMuZGF0YS5mcmFtZShyZXN1bHQpKSA+IDApIHsKICAgICAgcCA8LSBkb3RwbG90KHJlc3VsdCwgc2hvd0NhdGVnb3J5ID0gMTAsIHRpdGxlID0gdGl0bGUpCiAgICAgIHByaW50KHApICAKICAgICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCBnc3ViKCIuY3N2IiwgIl9kb3RwbG90LnBuZyIsIGZpbGVuYW1lKSksIHBsb3QgPSBwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCiAgICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKHJlc3VsdCksIGZpbGUgPSBwYXN0ZTAob3V0cHV0X2ZvbGRlciwgZmlsZW5hbWUpLCByb3cubmFtZXMgPSBGQUxTRSkKICAgIH0gZWxzZSB7CiAgICAgIG1lc3NhZ2UocGFzdGUoIk5vIHNpZ25pZmljYW50IGVucmljaG1lbnQgZm91bmQgZm9yOiIsIHRpdGxlKSkKICAgIH0KICB9IGVsc2UgewogICAgbWVzc2FnZShwYXN0ZSgiTm8gZ2VuZXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICB9Cn0KCnNhZmVfZW5yaWNoS0VHRyA8LSBmdW5jdGlvbihlbnRyZXpfbGlzdCwgdGl0bGUsIGZpbGVuYW1lKSB7CiAgaWYgKGxlbmd0aChlbnRyZXpfbGlzdCkgPiAwKSB7CiAgICByZXN1bHQgPC0gZW5yaWNoS0VHRyhnZW5lID0gZW50cmV6X2xpc3QsIG9yZ2FuaXNtID0gImhzYSIsIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiAgICBpZiAoIWlzLm51bGwocmVzdWx0KSAmJiBucm93KGFzLmRhdGEuZnJhbWUocmVzdWx0KSkgPiAwKSB7CiAgICAgIHJlc3VsdCA8LSBzZXRSZWFkYWJsZShyZXN1bHQsIE9yZ0RiID0gb3JnLkhzLmVnLmRiLCBrZXlUeXBlID0gIkVOVFJFWklEIikKICAgICAgcCA8LSBkb3RwbG90KHJlc3VsdCwgc2hvd0NhdGVnb3J5ID0gMTAsIHRpdGxlID0gdGl0bGUpCiAgICAgIHByaW50KHApCiAgICAgIGdnc2F2ZShwYXN0ZTAob3V0cHV0X2ZvbGRlciwgZ3N1YigiLmNzdiIsICJfZG90cGxvdC5wbmciLCBmaWxlbmFtZSkpLCBwbG90ID0gcCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQogICAgICB3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShyZXN1bHQpLCBmaWxlID0gcGFzdGUwKG91dHB1dF9mb2xkZXIsIGZpbGVuYW1lKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgICB9IGVsc2UgewogICAgICBtZXNzYWdlKHBhc3RlKCJObyBzaWduaWZpY2FudCBLRUdHIHBhdGh3YXlzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgICB9CiAgfSBlbHNlIHsKICAgIG1lc3NhZ2UocGFzdGUoIk5vIGdlbmVzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgfQp9CgpzYWZlX2VucmljaFJlYWN0b21lIDwtIGZ1bmN0aW9uKGVudHJlel9saXN0LCB0aXRsZSwgZmlsZW5hbWUpIHsKICBpZiAobGVuZ3RoKGVudHJlel9saXN0KSA+IDApIHsKICAgIHJlc3VsdCA8LSBlbnJpY2hQYXRod2F5KGdlbmUgPSBlbnRyZXpfbGlzdCwgb3JnYW5pc20gPSAiaHVtYW4iLCBwdmFsdWVDdXRvZmYgPSAwLjA1KQogICAgaWYgKCFpcy5udWxsKHJlc3VsdCkgJiYgbnJvdyhhcy5kYXRhLmZyYW1lKHJlc3VsdCkpID4gMCkgewogICAgICByZXN1bHQgPC0gc2V0UmVhZGFibGUocmVzdWx0LCBPcmdEYiA9IG9yZy5Icy5lZy5kYiwga2V5VHlwZSA9ICJFTlRSRVpJRCIpCiAgICAgIHAgPC0gZG90cGxvdChyZXN1bHQsIHNob3dDYXRlZ29yeSA9IDEwLCB0aXRsZSA9IHRpdGxlKQogICAgICBwcmludChwKQogICAgICBnZ3NhdmUocGFzdGUwKG91dHB1dF9mb2xkZXIsIGdzdWIoIi5jc3YiLCAiX2RvdHBsb3QucG5nIiwgZmlsZW5hbWUpKSwgcGxvdCA9IHAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKICAgICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUocmVzdWx0KSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCBmaWxlbmFtZSksIHJvdy5uYW1lcyA9IEZBTFNFKQogICAgfSBlbHNlIHsKICAgICAgbWVzc2FnZShwYXN0ZSgiTm8gc2lnbmlmaWNhbnQgUmVhY3RvbWUgcGF0aHdheXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICAgIH0KICB9IGVsc2UgewogICAgbWVzc2FnZShwYXN0ZSgiTm8gZ2VuZXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICB9Cn0KCiMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2VzLCBnZW5lcmF0ZSBwbG90cywgYW5kIHNhdmUgcmVzdWx0cwpzYWZlX2VucmljaEdPKHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRnZW5lLCAiR08gRW5yaWNobWVudCBmb3IgVXByZWd1bGF0ZWQgR2VuZXMiLCAidXByZWd1bGF0ZWRfR09fcmVzdWx0cy5jc3YiKQpzYWZlX2VucmljaEdPKHRvcF9kb3ducmVndWxhdGVkX2dlbmVzJGdlbmUsICJHTyBFbnJpY2htZW50IGZvciBEb3ducmVndWxhdGVkIEdlbmVzIiwgImRvd25yZWd1bGF0ZWRfR09fcmVzdWx0cy5jc3YiKQoKc2FmZV9lbnJpY2hLRUdHKHVwcmVndWxhdGVkX2VudHJleiwgIktFR0cgUGF0aHdheSBFbnJpY2htZW50IGZvciBVcHJlZ3VsYXRlZCBHZW5lcyIsICJ1cHJlZ3VsYXRlZF9LRUdHX3Jlc3VsdHMuY3N2IikKc2FmZV9lbnJpY2hLRUdHKGRvd25yZWd1bGF0ZWRfZW50cmV6LCAiS0VHRyBQYXRod2F5IEVucmljaG1lbnQgZm9yIERvd25yZWd1bGF0ZWQgR2VuZXMiLCAiZG93bnJlZ3VsYXRlZF9LRUdHX3Jlc3VsdHMuY3N2IikKCnNhZmVfZW5yaWNoUmVhY3RvbWUodXByZWd1bGF0ZWRfZW50cmV6LCAiUmVhY3RvbWUgUGF0aHdheSBFbnJpY2htZW50IGZvciBVcHJlZ3VsYXRlZCBHZW5lcyIsICJ1cHJlZ3VsYXRlZF9SZWFjdG9tZV9yZXN1bHRzLmNzdiIpCnNhZmVfZW5yaWNoUmVhY3RvbWUoZG93bnJlZ3VsYXRlZF9lbnRyZXosICJSZWFjdG9tZSBQYXRod2F5IEVucmljaG1lbnQgZm9yIERvd25yZWd1bGF0ZWQgR2VuZXMiLCAiZG93bnJlZ3VsYXRlZF9SZWFjdG9tZV9yZXN1bHRzLmNzdiIpCgpgYGAKCiMjIEVucmljaG1lbnQgQW5hbHlzaXNfSGFsbG1hcmsKYGBge3IgLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD04fQoKIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQpsaWJyYXJ5KG1zaWdkYnIpCmxpYnJhcnkoZW5yaWNocGxvdCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQoKIyBEZWZpbmUgdGhlIG91dHB1dCBmb2xkZXIgd2hlcmUgdGhlIHJlc3VsdHMgd2lsbCBiZSBzYXZlZApvdXRwdXRfZm9sZGVyIDwtICJMNV92c19MNi8iCgojIENyZWF0ZSB0aGUgb3V0cHV0IGZvbGRlciBpZiBpdCBkb2Vzbid0IGV4aXN0CmlmICghZGlyLmV4aXN0cyhvdXRwdXRfZm9sZGVyKSkgewogIGRpci5jcmVhdGUob3V0cHV0X2ZvbGRlcikKfQoKIyBMb2FkIEhhbGxtYXJrIGdlbmUgc2V0cyBmcm9tIG1zaWdkYnIKaGFsbG1hcmtfc2V0cyA8LSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY29sbGVjdGlvbiA9ICJIIikgICMgIkgiIGlzIGZvciBIYWxsbWFyayBnZW5lIHNldHMKCiMgQ29udmVydCBnZW5lIHN5bWJvbHMgdG8gdXBwZXJjYXNlIGZvciBjb25zaXN0ZW5jeQp0b3BfdXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSA8LSB0b3VwcGVyKHRvcF91cHJlZ3VsYXRlZF9nZW5lcyRnZW5lKQp0b3BfZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lIDwtIHRvdXBwZXIodG9wX2Rvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSkKCiMgQ2hlY2sgZm9yIG92ZXJsYXAgYmV0d2VlbiB5b3VyIHVwcmVndWxhdGVkL2Rvd25yZWd1bGF0ZWQgZ2VuZXMgYW5kIEhhbGxtYXJrIGdlbmUgc2V0cwp1cHJlZ3VsYXRlZF9pbl9oYWxsbWFyayA8LSBpbnRlcnNlY3QodG9wX3VwcmVndWxhdGVkX2dlbmVzJGdlbmUsIGhhbGxtYXJrX3NldHMkZ2VuZV9zeW1ib2wpCmRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmsgPC0gaW50ZXJzZWN0KHRvcF9kb3ducmVndWxhdGVkX2dlbmVzJGdlbmUsIGhhbGxtYXJrX3NldHMkZ2VuZV9zeW1ib2wpCgojIFByaW50IHRoZSBudW1iZXIgb2Ygb3ZlcmxhcHBpbmcgZ2VuZXMgZm9yIGJvdGggdXByZWd1bGF0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMKY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgaW4gSGFsbG1hcmsgZ2VuZSBzZXRzOiIsIGxlbmd0aCh1cHJlZ3VsYXRlZF9pbl9oYWxsbWFyayksICJcbiIpCmNhdCgiTnVtYmVyIG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMgaW4gSGFsbG1hcmsgZ2VuZSBzZXRzOiIsIGxlbmd0aChkb3ducmVndWxhdGVkX2luX2hhbGxtYXJrKSwgIlxuIikKCiMgSWYgdGhlcmUgYXJlIGdlbmVzIHRvIGFuYWx5emUsIHByb2NlZWQgd2l0aCBlbnJpY2htZW50IGFuYWx5c2lzCmlmIChsZW5ndGgodXByZWd1bGF0ZWRfaW5faGFsbG1hcmspID4gMCkgewogICMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzIGZvciB1cHJlZ3VsYXRlZCBnZW5lcyB1c2luZyBIYWxsbWFyayBnZW5lIHNldHMKICBoYWxsbWFya191cCA8LSBlbnJpY2hlcihnZW5lID0gdXByZWd1bGF0ZWRfaW5faGFsbG1hcmssIAogICAgICAgICAgICAgICAgICAgICAgICAgIFRFUk0yR0VORSA9IGhhbGxtYXJrX3NldHNbLCBjKCJnc19uYW1lIiwgImdlbmVfc3ltYm9sIildLCAgIyBFbnN1cmUgVEVSTTJHRU5FIHVzZXMgY29ycmVjdCBjb2x1bW5zCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKICAjIENoZWNrIGlmIHJlc3VsdHMgZXhpc3QKICBpZiAoIWlzLm51bGwoaGFsbG1hcmtfdXApICYmIG5yb3coaGFsbG1hcmtfdXApID4gMCkgewogICAgIyBWaXN1YWxpemUgcmVzdWx0cyBpZiBhdmFpbGFibGUKICAgIHVwX2RvdHBsb3QgPC0gZG90cGxvdChoYWxsbWFya191cCwgc2hvd0NhdGVnb3J5ID0gMjAsIHRpdGxlID0gIkhhbGxtYXJrIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgVXByZWd1bGF0ZWQgR2VuZXMiKQogICAgCiAgICAjIERpc3BsYXkgdGhlIHBsb3QgaW4gdGhlIG5vdGVib29rCiAgICBwcmludCh1cF9kb3RwbG90KQogICAgCiAgICAjIFNhdmUgdGhlIGRvdHBsb3QgdG8gYSBQTkcgZmlsZQogICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfdXByZWd1bGF0ZWRfZG90cGxvdC5wbmciKSwgcGxvdCA9IHVwX2RvdHBsb3QsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCiAgICAKICAgICMgT3B0aW9uYWxseSwgc2F2ZSB0aGUgcmVzdWx0cyBhcyBDU1YKICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKGhhbGxtYXJrX3VwKSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfdXByZWd1bGF0ZWRfZW5yaWNobWVudC5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgfSBlbHNlIHsKICAgIGNhdCgiTm8gc2lnbmlmaWNhbnQgZW5yaWNobWVudCBmb3VuZCBmb3IgdXByZWd1bGF0ZWQgZ2VuZXMuXG4iKQogIH0KfSBlbHNlIHsKICBjYXQoIk5vIHVwcmVndWxhdGVkIGdlbmVzIG92ZXJsYXAgd2l0aCBIYWxsbWFyayBnZW5lIHNldHMuXG4iKQp9CgppZiAobGVuZ3RoKGRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmspID4gMCkgewogICMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzIGZvciBkb3ducmVndWxhdGVkIGdlbmVzIHVzaW5nIEhhbGxtYXJrIGdlbmUgc2V0cwogIGhhbGxtYXJrX2Rvd24gPC0gZW5yaWNoZXIoZ2VuZSA9IGRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVEVSTTJHRU5FID0gaGFsbG1hcmtfc2V0c1ssIGMoImdzX25hbWUiLCAiZ2VuZV9zeW1ib2wiKV0sICAjIEVuc3VyZSBURVJNMkdFTkUgdXNlcyBjb3JyZWN0IGNvbHVtbnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiAgIyBDaGVjayBpZiByZXN1bHRzIGV4aXN0CiAgaWYgKCFpcy5udWxsKGhhbGxtYXJrX2Rvd24pICYmIG5yb3coaGFsbG1hcmtfZG93bikgPiAwKSB7CiAgICAjIFZpc3VhbGl6ZSByZXN1bHRzIGlmIGF2YWlsYWJsZQogICAgZG93bl9kb3RwbG90IDwtIGRvdHBsb3QoaGFsbG1hcmtfZG93biwgc2hvd0NhdGVnb3J5ID0gMjAsIHRpdGxlID0gIkhhbGxtYXJrIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgRG93bnJlZ3VsYXRlZCBHZW5lcyIpCiAgICAKICAgICMgRGlzcGxheSB0aGUgcGxvdCBpbiB0aGUgbm90ZWJvb2sKICAgIHByaW50KGRvd25fZG90cGxvdCkKICAgIAogICAgIyBTYXZlIHRoZSBkb3RwbG90IHRvIGEgUE5HIGZpbGUKICAgIGdnc2F2ZShwYXN0ZTAob3V0cHV0X2ZvbGRlciwgImhhbGxtYXJrX2Rvd25yZWd1bGF0ZWRfZG90cGxvdC5wbmciKSwgcGxvdCA9IGRvd25fZG90cGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKICAgIAogICAgIyBPcHRpb25hbGx5LCBzYXZlIHRoZSByZXN1bHRzIGFzIENTVgogICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUoaGFsbG1hcmtfZG93biksIGZpbGUgPSBwYXN0ZTAob3V0cHV0X2ZvbGRlciwgImhhbGxtYXJrX2Rvd25yZWd1bGF0ZWRfZW5yaWNobWVudC5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgfSBlbHNlIHsKICAgIGNhdCgiTm8gc2lnbmlmaWNhbnQgZW5yaWNobWVudCBmb3VuZCBmb3IgZG93bnJlZ3VsYXRlZCBnZW5lcy5cbiIpCiAgfQp9IGVsc2UgewogIGNhdCgiTm8gZG93bnJlZ3VsYXRlZCBnZW5lcyBvdmVybGFwIHdpdGggSGFsbG1hcmsgZ2VuZSBzZXRzLlxuIikKfQoKCmBgYAoKCgo=