1. load libraries

2. load seurat object

#Load Seurat Object 
load("/home/bioinfo/23-Harmony_Integration/0-robj/5-Harmony_Integrated_All_samples_Merged_CD4Tcells_final_Resolution_Selected_0.8_ADT_Normalized_cleaned_mt.robj")

pseudobulk_seurat <- All_samples_Merged

# Set the label column with "P1", "P2", "P3", and "Control"
pseudobulk_seurat$label <- factor(
  ifelse(pseudobulk_seurat$orig.ident %in% c("L1", "L2"), "P1", 
         ifelse(pseudobulk_seurat$orig.ident %in% c("L3", "L4"), "P2",
                ifelse(pseudobulk_seurat$orig.ident %in% c("L5", "L6", "L7"), "P3", NA))),
  levels = c("P1", "P2", "P3")
)


# Double-check the reference level
print(levels(pseudobulk_seurat$label))  # Should print "Control" first
[1] "P1" "P2" "P3"
# Verify factor levels
print(levels(pseudobulk_seurat$label))  # Should show Control first
[1] "P1" "P2" "P3"
# Ensure 'replicate' is a factor
pseudobulk_seurat$replicate <- as.factor(pseudobulk_seurat$cell_line)

# Rename the cell type column
pseudobulk_seurat$cell_type <- "CD4T"

3. DE using LIBRA

# write.csv(p2_p3, "../18March_Patient_comparison_Pseudobulk/P2_vs_P3/Psedobulk_Deseq2_P2_vs_P3.csv", row.names = FALSE)
df <- libra_p2_p3[!(libra_p2_p3$P2.exp < 0.20 & libra_p2_p3$P3.exp < 0.20), ]

DE_results_df <- as.data.frame(df)

write.csv(DE_results_df, "../p2_vs_P3/Psedobulk_Deseq2_filtered_on_mean_p2_vs_P3.csv", row.names = FALSE)
Warning: cannot open file '../p2_vs_P3/Psedobulk_Deseq2_filtered_on_mean_p2_vs_P3.csv': No such file or directoryError in file(file, ifelse(append, "a", "w")) : 
  cannot open the connection

Volcano Plot

library(ggplot2)
library(dplyr)

Attaching package: ‘dplyr’

The following object is masked from ‘package:Biobase’:

    combine

The following objects are masked from ‘package:GenomicRanges’:

    intersect, setdiff, union

The following object is masked from ‘package:GenomeInfoDb’:

    intersect

The following objects are masked from ‘package:IRanges’:

    collapse, desc, intersect, setdiff, slice, union

The following objects are masked from ‘package:S4Vectors’:

    first, intersect, rename, setdiff, setequal, union

The following objects are masked from ‘package:BiocGenerics’:

    combine, intersect, setdiff, union

The following object is masked from ‘package:matrixStats’:

    count

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(ggrepel)



# Ensure correct column names
colnames(DE_results_df)
 [1] "cell_type" "gene"      "avg_logFC" "P2.pct"    "P3.pct"    "P2.exp"    "P3.exp"    "p_val"     "p_val_adj" "de_family" "de_method"
[12] "de_type"  
# Define significance categories
volcano_data <- DE_results_df %>%
  mutate(
    significance = case_when(
      p_val_adj < 0.05 & avg_logFC > 2 ~ "Upregulated",
      p_val_adj < 0.05 & avg_logFC < -2 ~ "Downregulated",
      TRUE ~ "Not Significant"
    )
  )

# Select genes to label: p_val_adj < 1e-50 OR logFC > 2 OR logFC < -2
top_genes <- volcano_data %>%
  filter(p_val_adj < 0.05 | avg_logFC > 2 | avg_logFC < -2)

ggplot(volcano_data, aes(x = avg_logFC, y = -log10(p_val_adj), color = significance)) +
  geom_point(alpha = 0.6, size = 2) +  # Main points
  scale_color_manual(values = c("Upregulated" = "red", "Downregulated" = "blue", "Not Significant" = "grey")) +
  theme_minimal() +
  labs(title = "Volcano Plot: Pseudobulk DESeq2 Analysis",
       x = "Log2 Fold Change",
       y = "-Log10 Adjusted P-Value",
       color = "Significance") +

  # Add gene labels WITHOUT any lines connecting them
  geom_text_repel(data = top_genes, 
                  aes(label = gene),  
                  size = 5, box.padding = 0.3, max.overlaps = 15, segment.color = NA) +  

  # Add threshold lines
  geom_vline(xintercept = c(-2, 2), linetype = "dashed", color = "black") +  # logFC thresholds
  geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "black") +  # p-value threshold

  ylim(0, 70)  # Set max y-axis limit to avoid extreme values

NA
NA

Volcano Plot

library(ggplot2)
library(dplyr)
library(ggrepel)


# Ensure correct column names
colnames(DE_results_df)
 [1] "cell_type" "gene"      "avg_logFC" "P2.pct"    "P3.pct"    "P2.exp"    "P3.exp"    "p_val"     "p_val_adj" "de_family" "de_method"
[12] "de_type"  
# Define significance categories
volcano_data <- DE_results_df %>%
  mutate(
    significance = case_when(
      p_val_adj < 1e-20 & avg_logFC > 2 ~ "Most Upregulated",
      p_val_adj < 1e-20 & avg_logFC < -2 ~ "Most Downregulated",
      p_val_adj < 0.05 & avg_logFC > 2 ~ "Upregulated",
      p_val_adj < 0.05 & avg_logFC < -2 ~ "Downregulated",
      TRUE ~ "Not Significant"
    )
  )

# Select only very significant genes for labeling
top_genes <- volcano_data %>%
  filter(p_val_adj < 0.05 & (avg_logFC > 2 | avg_logFC < -2))

ggplot(volcano_data, aes(x = avg_logFC, 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_logFC, 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

4. Summarize Markers

markers <- DE_results_df

# Function to summarize markers
summarize_markers <- function(markers) {
  # Filter out NA values in p_val_adj
  markers <- markers %>% filter(!is.na(p_val_adj))
  
  num_pval0 <- sum(markers$p_val_adj == 0)
  num_pval1 <- sum(markers$p_val_adj == 1)
  num_significant <- sum(markers$p_val_adj < 0.05)
  num_upregulated <- sum(markers$avg_logFC > 1)
  num_downregulated <- sum(markers$avg_logFC < -1)
  
  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 significant genes (p_val_adj < 0.05):", num_significant, "\n")
  cat("Number of upregulated genes (avg_logFC > 1):", num_upregulated, "\n")
  cat("Number of downregulated genes (avg_logFC < 1):", num_downregulated, "\n")
}

cat("Markers1 Summary at 0.05:\n")
Markers1 Summary at 0.05:
summarize_markers(markers)
Number of genes with p_val_adj = 0: 0 
Number of genes with p_val_adj = 1: 22 
Number of significant genes (p_val_adj < 0.05): 1708 
Number of upregulated genes (avg_logFC > 1): 412 
Number of downregulated genes (avg_logFC < 1): 323 
markers2 <- DE_results_df
# Function to summarize markers
summarize_markers <- function(markers) {
  # Filter out NA values in p_val_adj
  markers <- markers %>% filter(!is.na(p_val_adj))
  
  num_pval0 <- sum(markers$p_val_adj == 0)
  num_pval1 <- sum(markers$p_val_adj == 1)
  num_significant <- sum(markers$p_val_adj < 1e-4)
  num_upregulated <- sum(markers$avg_logFC > 1)
  num_downregulated <- sum(markers$avg_logFC < -1)
  
  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 significant genes (p_val_adj < 1e-4):", num_significant, "\n")
  cat("Number of upregulated genes (avg_logFC > 1):", num_upregulated, "\n")
  cat("Number of downregulated genes (avg_logFC < 1):", num_downregulated, "\n")
}

cat("Markers Summary at 1e-4:\n")
Markers Summary at 1e-4:
summarize_markers(markers2)
Number of genes with p_val_adj = 0: 0 
Number of genes with p_val_adj = 1: 22 
Number of significant genes (p_val_adj < 1e-4): 539 
Number of upregulated genes (avg_logFC > 1): 412 
Number of downregulated genes (avg_logFC < 1): 323 

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

# Create the EnhancedVolcano plot with the filtered data
EnhancedVolcano(
  filtered_genes, 
  lab = ifelse(filtered_genes$p_val_adj <= 0.0000905 & abs(filtered_genes$avg_logFC) >= 1.0, filtered_genes$gene, NA),
  x = "avg_logFC", 
  y = "p_val_adj",
  title = "Malignant CD4 T cells(cell lines) vs normal CD4 T cells",
  pCutoff = 1e-4,
  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_logFC) >= 1.0]  # Only label significant genes
)

NA
NA
NA

Create the EnhancedVolcano plot


library(ggplot2)
library(EnhancedVolcano)
library(dplyr)

# Define the output directory
output_dir <- "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_logFC",
  y = "p_val_adj",
  title = "Malignant_CD4Tcells_vs_Normal_CD4Tcells",
  pCutoff = 1e-4,
  FCcutoff = 1.0
)
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_logFC", 
  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
)
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_logFC)))

# 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_logFC) >= 1.0, filtered_genes$gene, NA),
  x = "avg_logFC", 
  y = "p_val_adj",
  title = "Malignant CD4 T cells(cell lines) vs normal CD4 T cells",
  pCutoff = 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_logFC) >= 1.0]
)
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_logFC) >= 1.0, filtered_genes$gene, NA),
  x = "avg_logFC", 
  y = "p_val_adj",
  title = "Malignant CD4 T cells (cell lines) vs Normal CD4 T cells",
  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_logFC) >= 1.0]
)
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 'P2_vs_P3' folder.")
All volcano plots have been displayed and saved successfully in the 'P2_vs_P3' folder.

5. Enrichment Analysis-All_Pathways

# Load necessary libraries
library(clusterProfiler)
library(org.Hs.eg.db)
library(enrichplot)
library(ReactomePA)
library(DOSE) # For GSEA analysis
library(ggplot2) # Ensure ggplot2 is available for plotting

# Define threshold for differential expression selection (modified thresholds)
logFC_up_threshold <- 1          # Upregulated logFC threshold
logFC_down_threshold <- -1       # Downregulated logFC threshold
pval_threshold <- 0.05           # p-value threshold as specified

# Load your differential expression results (modify based on actual data structure)
# Malignant_CD4Tcells_vs_Normal_CD4Tcells <- read.csv("Your_DE_Results_File.csv")

# Select upregulated and downregulated genes
upregulated_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells[
  Malignant_CD4Tcells_vs_Normal_CD4Tcells$avg_logFC > logFC_up_threshold & 
  Malignant_CD4Tcells_vs_Normal_CD4Tcells$p_val_adj < pval_threshold, ]

downregulated_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells[
  Malignant_CD4Tcells_vs_Normal_CD4Tcells$avg_logFC < logFC_down_threshold & 
  Malignant_CD4Tcells_vs_Normal_CD4Tcells$p_val_adj < pval_threshold, ]

# Check for missing genes (NAs) in the gene column and remove them
upregulated_genes <- na.omit(upregulated_genes)
downregulated_genes <- na.omit(downregulated_genes)

# Save upregulated and downregulated gene results to CSV
write.csv(upregulated_genes, "Malignant_vs_Control/upregulated_genes.csv", row.names = FALSE)
write.csv(downregulated_genes, "Malignant_vs_Control/downregulated_genes.csv", row.names = FALSE)

# Convert gene symbols to Entrez IDs for enrichment analysis, with checks for missing values
upregulated_entrez <- bitr(upregulated_genes$gene, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)
'select()' returned 1:1 mapping between keys and columns
Warning: 7.41% of input gene IDs are fail to map...
downregulated_entrez <- bitr(downregulated_genes$gene, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)
'select()' returned 1:1 mapping between keys and columns
Warning: 5.14% of input gene IDs are fail to map...
# Check for missing Entrez IDs
missing_upregulated <- upregulated_genes$gene[is.na(upregulated_entrez$ENTREZID)]
missing_downregulated <- downregulated_genes$gene[is.na(downregulated_entrez$ENTREZID)]

# Print out the missing gene symbols for debugging
cat("Missing upregulated genes:\n", missing_upregulated, "\n")
Missing upregulated genes:
  
cat("Missing downregulated genes:\n", missing_downregulated, "\n")
Missing downregulated genes:
  
# Remove genes that couldn't be mapped to Entrez IDs
upregulated_entrez <- upregulated_entrez$ENTREZID[!is.na(upregulated_entrez$ENTREZID)]
downregulated_entrez <- downregulated_entrez$ENTREZID[!is.na(downregulated_entrez$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)
    if (!is.null(result) && nrow(as.data.frame(result)) > 0) {
      p <- dotplot(result, showCategory = 10, title = title)
      print(p)  
      ggsave(paste0("Malignant_vs_Control/", gsub(".csv", "_dotplot.png", filename)), plot = p, width = 8, height = 6)
      write.csv(as.data.frame(result), file = paste0("Malignant_vs_Control/", 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) {
      p <- dotplot(result, showCategory = 10, title = title)
      print(p)
      ggsave(paste0("Malignant_vs_Control/", gsub(".csv", "_dotplot.png", filename)), plot = p, width = 8, height = 6)
      write.csv(as.data.frame(result), file = paste0("Malignant_vs_Control/", 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) {
      p <- dotplot(result, showCategory = 10, title = title)
      print(p)
      ggsave(paste0("Malignant_vs_Control/", gsub(".csv", "_dotplot.png", filename)), plot = p, width = 8, height = 6)
      write.csv(as.data.frame(result), file = paste0("Malignant_vs_Control/", 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(upregulated_genes$gene, "GO Enrichment for Upregulated Genes", "upregulated_GO_results.csv")

safe_enrichGO(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")
No significant KEGG pathways found for: KEGG Pathway Enrichment for Upregulated Genes
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")

NA
NA

Enrichment Analysis_Hallmark


# Load necessary libraries
library(clusterProfiler)
library(org.Hs.eg.db)
library(msigdbr)
For full functionality, please install the 'msigdbdf' package with:
install.packages('msigdbdf', repos = 'https://igordot.r-universe.dev')
library(enrichplot)

# Load Hallmark gene sets from msigdbr
hallmark_sets <- msigdbr(species = "Homo sapiens", category = "H")  # "H" is for Hallmark gene sets
Warning: The `category` argument of `msigdbr()` is deprecated as of msigdbr 9.0.0.
Please use the `collection` argument instead.The 'msigdbdf' package must be installed to access the full dataset.
# Convert gene symbols to uppercase for consistency
upregulated_genes$gene <- toupper(upregulated_genes$gene)
downregulated_genes$gene <- toupper(downregulated_genes$gene)

# Check for overlap between your upregulated/downregulated genes and Hallmark gene sets
upregulated_in_hallmark <- intersect(upregulated_genes$gene, hallmark_sets$gene_symbol)
downregulated_in_hallmark <- intersect(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: 82 
cat("Number of downregulated genes in Hallmark gene sets:", length(downregulated_in_hallmark), "\n")
Number of downregulated genes in Hallmark gene sets: 84 
# Define the output folder where the results will be saved
output_folder <- "Malignant_vs_Control/"

# 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")
}
No significant enrichment found for upregulated genes.
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
LS0tCnRpdGxlOiAiUHNldWRvQnVsayBBbmFseXNpcyB1c2luZyBMaWJyYSBSTkEgYXNzYXktRGVzZXEyLUxSVF9vbl9saXN0X2ZpbHRyZWRfb25fbWVhbl9QMl92c19QMyIKYXV0aG9yOiBOYXNpciBNYWhtb29kIEFiYmFzaQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAjIHBkZl9kb2N1bWVudDogZGVmYXVsdAogICMgd29yZF9kb2N1bWVudDogZGVmYXVsdAogICMgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogICNybWRmb3JtYXRzOjpyZWFkdGhlZG93bgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQotLS0KCiMgMS4gbG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CgojIExvYWQgbGlicmFyaWVzCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShERVNlcTIpCmxpYnJhcnkoTGlicmEpCgpgYGAKCgojIDIuIGxvYWQgc2V1cmF0IG9iamVjdApgYGB7ciBsb2FkX3NldXJhdH0KI0xvYWQgU2V1cmF0IE9iamVjdCAKbG9hZCgiL2hvbWUvYmlvaW5mby8yMy1IYXJtb255X0ludGVncmF0aW9uLzAtcm9iai81LUhhcm1vbnlfSW50ZWdyYXRlZF9BbGxfc2FtcGxlc19NZXJnZWRfQ0Q0VGNlbGxzX2ZpbmFsX1Jlc29sdXRpb25fU2VsZWN0ZWRfMC44X0FEVF9Ob3JtYWxpemVkX2NsZWFuZWRfbXQucm9iaiIpCgpwc2V1ZG9idWxrX3NldXJhdCA8LSBBbGxfc2FtcGxlc19NZXJnZWQKCiMgU2V0IHRoZSBsYWJlbCBjb2x1bW4gd2l0aCAiUDEiLCAiUDIiLCAiUDMiLCBhbmQgIkNvbnRyb2wiCnBzZXVkb2J1bGtfc2V1cmF0JGxhYmVsIDwtIGZhY3RvcigKICBpZmVsc2UocHNldWRvYnVsa19zZXVyYXQkb3JpZy5pZGVudCAlaW4lIGMoIkwxIiwgIkwyIiksICJQMSIsIAogICAgICAgICBpZmVsc2UocHNldWRvYnVsa19zZXVyYXQkb3JpZy5pZGVudCAlaW4lIGMoIkwzIiwgIkw0IiksICJQMiIsCiAgICAgICAgICAgICAgICBpZmVsc2UocHNldWRvYnVsa19zZXVyYXQkb3JpZy5pZGVudCAlaW4lIGMoIkw1IiwgIkw2IiwgIkw3IiksICJQMyIsIE5BKSkpLAogIGxldmVscyA9IGMoIlAxIiwgIlAyIiwgIlAzIikKKQoKCiMgRG91YmxlLWNoZWNrIHRoZSByZWZlcmVuY2UgbGV2ZWwKcHJpbnQobGV2ZWxzKHBzZXVkb2J1bGtfc2V1cmF0JGxhYmVsKSkgICMgU2hvdWxkIHByaW50ICJDb250cm9sIiBmaXJzdAoKIyBWZXJpZnkgZmFjdG9yIGxldmVscwpwcmludChsZXZlbHMocHNldWRvYnVsa19zZXVyYXQkbGFiZWwpKSAgIyBTaG91bGQgc2hvdyBDb250cm9sIGZpcnN0CgojIEVuc3VyZSAncmVwbGljYXRlJyBpcyBhIGZhY3Rvcgpwc2V1ZG9idWxrX3NldXJhdCRyZXBsaWNhdGUgPC0gYXMuZmFjdG9yKHBzZXVkb2J1bGtfc2V1cmF0JGNlbGxfbGluZSkKCiMgUmVuYW1lIHRoZSBjZWxsIHR5cGUgY29sdW1uCnBzZXVkb2J1bGtfc2V1cmF0JGNlbGxfdHlwZSA8LSAiQ0Q0VCIKYGBgCgojIDMuIERFIHVzaW5nIExJQlJBCmBgYHtyICwgZmlnLmhlaWdodD0xNCwgZmlnLndpZHRoPTE4fQpsaWJyYXJ5KExpYnJhKQoKCgojICMgQ3JlYXRlIHN1YnNldHMgZm9yIGVhY2ggcGFpciBvZiBwYXRpZW50cwojIHAxX3AyIDwtIHN1YnNldChwc2V1ZG9idWxrX3NldXJhdCwgbGFiZWwgJWluJSBjKCJQMSIsICJQMiIpKQojIHAxX3AzIDwtIHN1YnNldChwc2V1ZG9idWxrX3NldXJhdCwgbGFiZWwgJWluJSBjKCJQMSIsICJQMyIpKQpwMl9wMyA8LSBzdWJzZXQocHNldWRvYnVsa19zZXVyYXQsIGxhYmVsICVpbiUgYygiUDIiLCAiUDMiKSkKCiMgIyBSdW4gTGlicmEgZm9yIFAxIHZzIFAyCiMgbGlicmFfcDFfcDIgPC0gcnVuX2RlKAojICAgaW5wdXQgPSBwMV9wMiwKIyAgIGxhYmVsX2NvbCA9ICJsYWJlbCIsCiMgICBjZWxsX3R5cGVfY29sID0gImNlbGxfdHlwZSIsCiMgICByZXBsaWNhdGVfY29sID0gInJlcGxpY2F0ZSIsCiMgICBkZV9mYW1pbHkgPSAicHNldWRvYnVsayIsCiMgICBkZV9tZXRob2QgPSAiREVTZXEyIiwKIyAgIGRlX3R5cGUgPSAiTFJUIgojICkKIyAKIyBwMV9wMiA8LSBhcy5kYXRhLmZyYW1lKGRmKQojIAojIHdyaXRlLmNzdihwMV9wMiwgIi4uLzE4TWFyY2hfUGF0aWVudF9jb21wYXJpc29uX1BzZXVkb2J1bGsvUDFfdnNfUDIvUHNlZG9idWxrX0Rlc2VxMl9QMV92c19QMi5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKIyAKIyAjIFJ1biBMaWJyYSBmb3IgUDEgdnMgUDMKIyBsaWJyYV9wMV9wMyA8LSBydW5fZGUoCiMgICBpbnB1dCA9IHAxX3AzLAojICAgbGFiZWxfY29sID0gImxhYmVsIiwKIyAgIGNlbGxfdHlwZV9jb2wgPSAiY2VsbF90eXBlIiwKIyAgIHJlcGxpY2F0ZV9jb2wgPSAicmVwbGljYXRlIiwKIyAgIGRlX2ZhbWlseSA9ICJwc2V1ZG9idWxrIiwKIyAgIGRlX21ldGhvZCA9ICJERVNlcTIiLAojICAgZGVfdHlwZSA9ICJMUlQiCiMgKQojIAojIHAxX3AzIDwtIGFzLmRhdGEuZnJhbWUoZGYpCiMgCiMgd3JpdGUuY3N2KHAxX3AzLCAiLi4vMThNYXJjaF9QYXRpZW50X2NvbXBhcmlzb25fUHNldWRvYnVsay9QMV92c19QMy9Qc2Vkb2J1bGtfRGVzZXEyX1AxX3ZzX1AzLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBSdW4gTGlicmEgZm9yIFAyIHZzIFAzCmxpYnJhX3AyX3AzIDwtIHJ1bl9kZSgKICBpbnB1dCA9IHAyX3AzLAogIGxhYmVsX2NvbCA9ICJsYWJlbCIsCiAgY2VsbF90eXBlX2NvbCA9ICJjZWxsX3R5cGUiLAogIHJlcGxpY2F0ZV9jb2wgPSAicmVwbGljYXRlIiwKICBkZV9mYW1pbHkgPSAicHNldWRvYnVsayIsCiAgZGVfbWV0aG9kID0gIkRFU2VxMiIsCiAgZGVfdHlwZSA9ICJMUlQiCikKCnAyX3AzIDwtIGFzLmRhdGEuZnJhbWUoZGYpCgojIHdyaXRlLmNzdihwMl9wMywgIi4uLzE4TWFyY2hfUGF0aWVudF9jb21wYXJpc29uX1BzZXVkb2J1bGsvUDJfdnNfUDMvUHNlZG9idWxrX0Rlc2VxMl9QMl92c19QMy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKZGYgPC0gbGlicmFfcDJfcDNbIShsaWJyYV9wMl9wMyRQMi5leHAgPCAwLjIwICYgbGlicmFfcDJfcDMkUDMuZXhwIDwgMC4yMCksIF0KCkRFX3Jlc3VsdHNfZGYgPC0gYXMuZGF0YS5mcmFtZShkZikKCndyaXRlLmNzdihERV9yZXN1bHRzX2RmLCAiLi4vUDJfdnNfUDMvL1BzZWRvYnVsa19EZXNlcTJfZmlsdGVyZWRfb25fbWVhbl9wMl92c19QMy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCmBgYAoKCgoKIyMgVm9sY2FubyBQbG90CmBgYHtyICwgZmlnLmhlaWdodD0xNCwgZmlnLndpZHRoPTE4fQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dyZXBlbCkKCgoKIyBFbnN1cmUgY29ycmVjdCBjb2x1bW4gbmFtZXMKY29sbmFtZXMoREVfcmVzdWx0c19kZikKCiMgRGVmaW5lIHNpZ25pZmljYW5jZSBjYXRlZ29yaWVzCnZvbGNhbm9fZGF0YSA8LSBERV9yZXN1bHRzX2RmICU+JQogIG11dGF0ZSgKICAgIHNpZ25pZmljYW5jZSA9IGNhc2Vfd2hlbigKICAgICAgcF92YWxfYWRqIDwgMC4wNSAmIGF2Z19sb2dGQyA+IDIgfiAiVXByZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAwLjA1ICYgYXZnX2xvZ0ZDIDwgLTIgfiAiRG93bnJlZ3VsYXRlZCIsCiAgICAgIFRSVUUgfiAiTm90IFNpZ25pZmljYW50IgogICAgKQogICkKCiMgU2VsZWN0IGdlbmVzIHRvIGxhYmVsOiBwX3ZhbF9hZGogPCAxZS01MCBPUiBsb2dGQyA+IDIgT1IgbG9nRkMgPCAtMgp0b3BfZ2VuZXMgPC0gdm9sY2Fub19kYXRhICU+JQogIGZpbHRlcihwX3ZhbF9hZGogPCAwLjA1IHwgYXZnX2xvZ0ZDID4gMiB8IGF2Z19sb2dGQyA8IC0yKQoKZ2dwbG90KHZvbGNhbm9fZGF0YSwgYWVzKHggPSBhdmdfbG9nRkMsIHkgPSAtbG9nMTAocF92YWxfYWRqKSwgY29sb3IgPSBzaWduaWZpY2FuY2UpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNiwgc2l6ZSA9IDIpICsgICMgTWFpbiBwb2ludHMKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiVXByZWd1bGF0ZWQiID0gInJlZCIsICJEb3ducmVndWxhdGVkIiA9ICJibHVlIiwgIk5vdCBTaWduaWZpY2FudCIgPSAiZ3JleSIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gIlZvbGNhbm8gUGxvdDogUHNldWRvYnVsayBERVNlcTIgQW5hbHlzaXMiLAogICAgICAgeCA9ICJMb2cyIEZvbGQgQ2hhbmdlIiwKICAgICAgIHkgPSAiLUxvZzEwIEFkanVzdGVkIFAtVmFsdWUiLAogICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKwoKICAjIEFkZCBnZW5lIGxhYmVscyBXSVRIT1VUIGFueSBsaW5lcyBjb25uZWN0aW5nIHRoZW0KICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHRvcF9nZW5lcywgCiAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IGdlbmUpLCAgCiAgICAgICAgICAgICAgICAgIHNpemUgPSA1LCBib3gucGFkZGluZyA9IDAuMywgbWF4Lm92ZXJsYXBzID0gMTUsIHNlZ21lbnQuY29sb3IgPSBOQSkgKyAgCgogICMgQWRkIHRocmVzaG9sZCBsaW5lcwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLTIsIDIpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsgICMgbG9nRkMgdGhyZXNob2xkcwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmxhY2siKSArICAjIHAtdmFsdWUgdGhyZXNob2xkCgogIHlsaW0oMCwgNzApICAjIFNldCBtYXggeS1heGlzIGxpbWl0IHRvIGF2b2lkIGV4dHJlbWUgdmFsdWVzCgoKYGBgCiMjIFZvbGNhbm8gUGxvdApgYGB7ciAsIGZpZy5oZWlnaHQ9MTQsIGZpZy53aWR0aD0xOH0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncmVwZWwpCgoKIyBFbnN1cmUgY29ycmVjdCBjb2x1bW4gbmFtZXMKY29sbmFtZXMoREVfcmVzdWx0c19kZikKCiMgRGVmaW5lIHNpZ25pZmljYW5jZSBjYXRlZ29yaWVzCnZvbGNhbm9fZGF0YSA8LSBERV9yZXN1bHRzX2RmICU+JQogIG11dGF0ZSgKICAgIHNpZ25pZmljYW5jZSA9IGNhc2Vfd2hlbigKICAgICAgcF92YWxfYWRqIDwgMWUtMjAgJiBhdmdfbG9nRkMgPiAyIH4gIk1vc3QgVXByZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAxZS0yMCAmIGF2Z19sb2dGQyA8IC0yIH4gIk1vc3QgRG93bnJlZ3VsYXRlZCIsCiAgICAgIHBfdmFsX2FkaiA8IDAuMDUgJiBhdmdfbG9nRkMgPiAyIH4gIlVwcmVndWxhdGVkIiwKICAgICAgcF92YWxfYWRqIDwgMC4wNSAmIGF2Z19sb2dGQyA8IC0yIH4gIkRvd25yZWd1bGF0ZWQiLAogICAgICBUUlVFIH4gIk5vdCBTaWduaWZpY2FudCIKICAgICkKICApCgojIFNlbGVjdCBvbmx5IHZlcnkgc2lnbmlmaWNhbnQgZ2VuZXMgZm9yIGxhYmVsaW5nCnRvcF9nZW5lcyA8LSB2b2xjYW5vX2RhdGEgJT4lCiAgZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUgJiAoYXZnX2xvZ0ZDID4gMiB8IGF2Z19sb2dGQyA8IC0yKSkKCmdncGxvdCh2b2xjYW5vX2RhdGEsIGFlcyh4ID0gYXZnX2xvZ0ZDLCB5ID0gLWxvZzEwKHBfdmFsX2FkaiksIGNvbG9yID0gc2lnbmlmaWNhbmNlKSkgKwogIAogICMgTWFpbiBwb2ludHMKICBnZW9tX3BvaW50KGFscGhhID0gMC43LCBzaXplID0gMi41KSArCiAgCiAgIyBIaWdobGlnaHQgaGlnaGx5IHNpZ25pZmljYW50IGdlbmVzIHdpdGggbGFyZ2VyIHBvaW50cwogIGdlb21fcG9pbnQoZGF0YSA9IHRvcF9nZW5lcywgYWVzKHggPSBhdmdfbG9nRkMsIHkgPSAtbG9nMTAocF92YWxfYWRqKSksIAogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMywgc2hhcGUgPSAyMSwgZmlsbCA9ICJibGFjayIpICsKCiAgIyBDdXN0b20gY29sb3Igc2NoZW1lCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoCiAgICAiTW9zdCBVcHJlZ3VsYXRlZCIgPSAiZGFya3JlZCIsCiAgICAiTW9zdCBEb3ducmVndWxhdGVkIiA9ICJkYXJrYmx1ZSIsCiAgICAiVXByZWd1bGF0ZWQiID0gInJlZCIsCiAgICAiRG93bnJlZ3VsYXRlZCIgPSAiYmx1ZSIsCiAgICAiTm90IFNpZ25pZmljYW50IiA9ICJncmV5IgogICkpICsKCiAgIyBBZGQgZ2VuZSBsYWJlbHMgKG9ubHkgZm9yIGhpZ2hseSBzaWduaWZpY2FudCBnZW5lcykKICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHRvcF9nZW5lcywgYWVzKGxhYmVsID0gZ2VuZSksICAKICAgICAgICAgICAgICAgICAgc2l6ZSA9IDQsIGJveC5wYWRkaW5nID0gMC41LCBtYXgub3ZlcmxhcHMgPSAxMCwgc2VnbWVudC5jb2xvciA9IE5BKSArCiAgCiAgIyBBZGQgdGhyZXNob2xkIGxpbmVzCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygtMiwgMiksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKyAgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsgIAoKICAjIEltcHJvdmUgdGhlbWUKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE0KSArCiAgbGFicyh0aXRsZSA9ICJWb2xjYW5vIFBsb3Q6IFBzZXVkb2J1bGsgREVTZXEyIEFuYWx5c2lzIiwKICAgICAgIHggPSAiTG9nMiBGb2xkIENoYW5nZSIsCiAgICAgICB5ID0gIi1Mb2cxMCBBZGp1c3RlZCBQLVZhbHVlIiwKICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsKCiAgeWxpbSgwLCA1MCkgICMgQXZvaWQgZXh0cmVtZSBzY2FsaW5nIGlzc3VlcwoKCmBgYAoKIyA0LiBTdW1tYXJpemUgTWFya2VycwpgYGB7ciAsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNH0KbWFya2VycyA8LSBERV9yZXN1bHRzX2RmCgojIEZ1bmN0aW9uIHRvIHN1bW1hcml6ZSBtYXJrZXJzCnN1bW1hcml6ZV9tYXJrZXJzIDwtIGZ1bmN0aW9uKG1hcmtlcnMpIHsKICAjIEZpbHRlciBvdXQgTkEgdmFsdWVzIGluIHBfdmFsX2FkagogIG1hcmtlcnMgPC0gbWFya2VycyAlPiUgZmlsdGVyKCFpcy5uYShwX3ZhbF9hZGopKQogIAogIG51bV9wdmFsMCA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPT0gMCkKICBudW1fcHZhbDEgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqID09IDEpCiAgbnVtX3NpZ25pZmljYW50IDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA8IDAuMDUpCiAgbnVtX3VwcmVndWxhdGVkIDwtIHN1bShtYXJrZXJzJGF2Z19sb2dGQyA+IDEpCiAgbnVtX2Rvd25yZWd1bGF0ZWQgPC0gc3VtKG1hcmtlcnMkYXZnX2xvZ0ZDIDwgLTEpCiAgCiAgY2F0KCJOdW1iZXIgb2YgZ2VuZXMgd2l0aCBwX3ZhbF9hZGogPSAwOiIsIG51bV9wdmFsMCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDE6IiwgbnVtX3B2YWwxLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIHNpZ25pZmljYW50IGdlbmVzIChwX3ZhbF9hZGogPCAwLjA1KToiLCBudW1fc2lnbmlmaWNhbnQsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2dGQyA+IDEpOiIsIG51bV91cHJlZ3VsYXRlZCwgIlxuIikKICBjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIChhdmdfbG9nRkMgPCAxKToiLCBudW1fZG93bnJlZ3VsYXRlZCwgIlxuIikKfQoKY2F0KCJNYXJrZXJzMSBTdW1tYXJ5IGF0IDAuMDU6XG4iKQoKc3VtbWFyaXplX21hcmtlcnMobWFya2VycykKCm1hcmtlcnMyIDwtIERFX3Jlc3VsdHNfZGYKIyBGdW5jdGlvbiB0byBzdW1tYXJpemUgbWFya2VycwpzdW1tYXJpemVfbWFya2VycyA8LSBmdW5jdGlvbihtYXJrZXJzKSB7CiAgIyBGaWx0ZXIgb3V0IE5BIHZhbHVlcyBpbiBwX3ZhbF9hZGoKICBtYXJrZXJzIDwtIG1hcmtlcnMgJT4lIGZpbHRlcighaXMubmEocF92YWxfYWRqKSkKICAKICBudW1fcHZhbDAgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqID09IDApCiAgbnVtX3B2YWwxIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAxKQogIG51bV9zaWduaWZpY2FudCA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPCAxZS00KQogIG51bV91cHJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nRkMgPiAxKQogIG51bV9kb3ducmVndWxhdGVkIDwtIHN1bShtYXJrZXJzJGF2Z19sb2dGQyA8IC0xKQogIAogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMDoiLCBudW1fcHZhbDAsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgZ2VuZXMgd2l0aCBwX3ZhbF9hZGogPSAxOiIsIG51bV9wdmFsMSwgIlxuIikKICBjYXQoIk51bWJlciBvZiBzaWduaWZpY2FudCBnZW5lcyAocF92YWxfYWRqIDwgMWUtNCk6IiwgbnVtX3NpZ25pZmljYW50LCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIHVwcmVndWxhdGVkIGdlbmVzIChhdmdfbG9nRkMgPiAxKToiLCBudW1fdXByZWd1bGF0ZWQsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcyAoYXZnX2xvZ0ZDIDwgMSk6IiwgbnVtX2Rvd25yZWd1bGF0ZWQsICJcbiIpCn0KCmNhdCgiTWFya2VycyBTdW1tYXJ5IGF0IDFlLTQ6XG4iKQoKc3VtbWFyaXplX21hcmtlcnMobWFya2VyczIpCgoKCgpgYGAKCgoKIyMgRW5oYW5jZWRWb2xjYW5vIHBsb3QKYGBge3IgLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCiMgQXNzdW1pbmcgeW91IGhhdmUgYSBkYXRhIGZyYW1lIG5hbWVkIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscwojIEZpbHRlciBnZW5lcyBiYXNlZCBvbiBsb3dlc3QgcC12YWx1ZXMgYnV0IGluY2x1ZGUgYWxsIGdlbmVzCmZpbHRlcmVkX2dlbmVzIDwtIG1hcmtlcnMgJT4lCiAgYXJyYW5nZShwX3ZhbF9hZGosIGRlc2MoYWJzKGF2Z19sb2dGQykpKQoKIyBDcmVhdGUgdGhlIEVuaGFuY2VkVm9sY2FubyBwbG90IHdpdGggdGhlIGZpbHRlcmVkIGRhdGEKRW5oYW5jZWRWb2xjYW5vKAogIGZpbHRlcmVkX2dlbmVzLCAKICBsYWIgPSBpZmVsc2UoZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDAuMDAwMDkwNSAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nRkMpID49IDEuMCwgZmlsdGVyZWRfZ2VuZXMkZ2VuZSwgTkEpLAogIHggPSAiYXZnX2xvZ0ZDIiwgCiAgeSA9ICJwX3ZhbF9hZGoiLAogIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHBDdXRvZmYgPSAxZS00LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywgCiAgbGFiQ29sID0gJ2JsYWNrJywKICBsYWJGYWNlID0gJ2JvbGQnLAogIGJveGVkTGFiZWxzID0gRkFMU0UsICAjIFNldCB0byBGQUxTRSB0byByZW1vdmUgYm94ZWQgbGFiZWxzCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbWl6ZSBwb2ludCBjb2xvcnMKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2dGQykgPj0gMS4wXSAgIyBPbmx5IGxhYmVsIHNpZ25pZmljYW50IGdlbmVzCikKCgoKYGBgCgoKIyMgQ3JlYXRlIHRoZSBFbmhhbmNlZFZvbGNhbm8gcGxvdApgYGB7ciAsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNn0KCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShFbmhhbmNlZFZvbGNhbm8pCmxpYnJhcnkoZHBseXIpCgojIERlZmluZSB0aGUgb3V0cHV0IGRpcmVjdG9yeQpvdXRwdXRfZGlyIDwtICJNYWxpZ25hbnRfdnNfQ29udHJvbCIKZGlyLmNyZWF0ZShvdXRwdXRfZGlyLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKCiBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgPC0gZmlsdGVyZWRfZ2VuZXMKCiMgRmlyc3QgVm9sY2FubyBQbG90CnAxIDwtIEVuaGFuY2VkVm9sY2FubygKICBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMsCiAgbGFiID0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGdlbmUsCiAgeCA9ICJhdmdfbG9nRkMiLAogIHkgPSAicF92YWxfYWRqIiwKICB0aXRsZSA9ICJNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMiLAogIHBDdXRvZmYgPSAxZS00LAogIEZDY3V0b2ZmID0gMS4wCikKcHJpbnQocDEpICAjIERpc3BsYXkgaW4gbm90ZWJvb2sKZ2dzYXZlKGZpbGVuYW1lID0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJWb2xjYW5vUGxvdDEucG5nIiksIHBsb3QgPSBwMSwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCkKCiMgU2Vjb25kIFZvbGNhbm8gUGxvdCB3aXRoIHNlbGVjdGVkIGdlbmVzCnAyIDwtIEVuaGFuY2VkVm9sY2FubygKICBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMsIAogIGxhYiA9IE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRnZW5lLAogIHggPSAiYXZnX2xvZ0ZDIiwgCiAgeSA9ICJwX3ZhbF9hZGoiLAogIHNlbGVjdExhYiA9IGMoJ0VQQ0FNJywgJ0JDQVQxJywgJ0tJUjNETDInLCAnRk9YTTEnLCAnVFdJU1QxJywgJ1RORlNGOScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnQ0Q4MCcsICAnSUwxQicsICdSUFM0WTEnLCAiVE9YIiwgIkNENTIiLCAiVFdJU1QxIiwgIkNDUjQiLCAiQ0NSNyIsIlBEQ0QxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0lMN1InLCAnVENGNycsICAnTUtJNjcnLCAnQ0Q3MCcsICJEUFA0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0lMMlJBJywnVFJCVjYtMicsICdUUkJWMTAtMycsICdUUkJWNC0yJywgJ1RSQlY5JywgJ1RSQlY3LTknLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1RSQVYxMi0xJywgJ0NEOEInLCAnRkNHUjNBJywgJ0dOTFknLCAnRk9YUDMnLCAnU0VMTCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnR0lNQVAxJywgJ1JJUE9SMicsICdMRUYxJywgJ0hPWEM5JywgJ1NQNScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdDQ0wxNycsICdFVFY0JywgJ1RIWTEnLCAnRk9YQTInLCAnSVRHQUQnLCAnUzEwMFAnLCAnVEJYNCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSUQxJywgJ1hDTDEnLCAnU09YMicsICdDRDI3JywgJ0NEMjgnLCdQTFMzJywnQ0Q3MCcsJ1JBQjI1JyAsICdUUkJWMjcnLCAnVFJCVjInKSwKICB0aXRsZSA9ICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMoY2VsbCBsaW5lcykgdnMgbm9ybWFsIENENCBUIGNlbGxzIiwKICB4bGFiID0gYnF1b3RlKH5Mb2dbMl1+ICdmb2xkIGNoYW5nZScpLAogIHBDdXRvZmYgPSAwLjA1LAogIEZDY3V0b2ZmID0gMS41LCAKICBwb2ludFNpemUgPSAzLjAsCiAgbGFiU2l6ZSA9IDUuMCwKICBib3hlZExhYmVscyA9IFRSVUUsCiAgY29sQWxwaGEgPSAwLjUsCiAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLAogIGxlZ2VuZExhYlNpemUgPSAxMCwKICBsZWdlbmRJY29uU2l6ZSA9IDQuMCwKICBkcmF3Q29ubmVjdG9ycyA9IFRSVUUsCiAgd2lkdGhDb25uZWN0b3JzID0gMC41LAogIGNvbENvbm5lY3RvcnMgPSAnZ3JleTUwJywKICBhcnJvd2hlYWRzID0gRkFMU0UsCiAgbWF4Lm92ZXJsYXBzID0gMzAKKQpwcmludChwMikgICMgRGlzcGxheSBpbiBub3RlYm9vawpnZ3NhdmUoZmlsZW5hbWUgPSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlZvbGNhbm9QbG90Mi5wbmciKSwgcGxvdCA9IHAyLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwKQoKIyBGaWx0ZXJpbmcgZ2VuZXMKZmlsdGVyZWRfZ2VuZXMgPC0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzICU+JQogIGFycmFuZ2UocF92YWxfYWRqLCBkZXNjKGFicyhhdmdfbG9nRkMpKSkKCiMgVGhpcmQgVm9sY2FubyBQbG90IC0gRmlsdGVyaW5nIGJ5IHAtdmFsdWUgYW5kIGxvZ0ZDCnAzIDwtIEVuaGFuY2VkVm9sY2FubygKICBmaWx0ZXJlZF9nZW5lcywgCiAgbGFiID0gaWZlbHNlKGZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAxZS00ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2dGQykgPj0gMS4wLCBmaWx0ZXJlZF9nZW5lcyRnZW5lLCBOQSksCiAgeCA9ICJhdmdfbG9nRkMiLCAKICB5ID0gInBfdmFsX2FkaiIsCiAgdGl0bGUgPSAiTWFsaWduYW50IENENCBUIGNlbGxzKGNlbGwgbGluZXMpIHZzIG5vcm1hbCBDRDQgVCBjZWxscyIsCiAgcEN1dG9mZiA9IDFlLTQsCiAgRkNjdXRvZmYgPSAxLjAsCiAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLCAKICBsYWJDb2wgPSAnYmxhY2snLAogIGxhYkZhY2UgPSAnYm9sZCcsCiAgYm94ZWRMYWJlbHMgPSBGQUxTRSwgICMgUmVtb3ZlIGJveGVkIGxhYmVscwogIHBvaW50U2l6ZSA9IDMuMCwKICBsYWJTaXplID0gNS4wLAogIGNvbCA9IGMoJ2dyZXk3MCcsICdibGFjaycsICdibHVlJywgJ3JlZCcpLCAgIyBDdXN0b21pemUgcG9pbnQgY29sb3JzCiAgc2VsZWN0TGFiID0gZmlsdGVyZWRfZ2VuZXMkZ2VuZVtmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMC4wNSAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nRkMpID49IDEuMF0KKQpwcmludChwMykgICMgRGlzcGxheSBpbiBub3RlYm9vawpnZ3NhdmUoZmlsZW5hbWUgPSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlZvbGNhbm9QbG90My5wbmciKSwgcGxvdCA9IHAzLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwKQoKIyBGb3VydGggVm9sY2FubyBQbG90IC0gTW9yZSByZWZpbmVkIGZpbHRlcmluZwpwNCA8LSBFbmhhbmNlZFZvbGNhbm8oCiAgZmlsdGVyZWRfZ2VuZXMsIAogIGxhYiA9IGlmZWxzZShmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMWUtNCAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nRkMpID49IDEuMCwgZmlsdGVyZWRfZ2VuZXMkZ2VuZSwgTkEpLAogIHggPSAiYXZnX2xvZ0ZDIiwgCiAgeSA9ICJwX3ZhbF9hZGoiLAogIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyAoY2VsbCBsaW5lcykgdnMgTm9ybWFsIENENCBUIGNlbGxzIiwKICBzdWJ0aXRsZSA9ICJIaWdobGlnaHRpbmcgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIiwKICBwQ3V0b2ZmID0gMWUtNCwKICBGQ2N1dG9mZiA9IDEuMCwKICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCiAgY29sQWxwaGEgPSAwLjgsICAjIFNsaWdodCB0cmFuc3BhcmVuY3kgZm9yIG5vbi1zaWduaWZpY2FudCBwb2ludHMKICBjb2wgPSBjKCdncmV5NzAnLCAnYmxhY2snLCAnYmx1ZScsICdyZWQnKSwgICMgQ3VzdG9tIGNvbG9yIHNjaGVtZQogIGdyaWRsaW5lcy5tYWpvciA9IFRSVUUsCiAgZ3JpZGxpbmVzLm1pbm9yID0gRkFMU0UsCiAgc2VsZWN0TGFiID0gZmlsdGVyZWRfZ2VuZXMkZ2VuZVtmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMC4wNSAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nRkMpID49IDEuMF0KKQpwcmludChwNCkgICMgRGlzcGxheSBpbiBub3RlYm9vawpnZ3NhdmUoZmlsZW5hbWUgPSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlZvbGNhbm9QbG90NC5wbmciKSwgcGxvdCA9IHA0LCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwKQoKbWVzc2FnZSgiQWxsIHZvbGNhbm8gcGxvdHMgaGF2ZSBiZWVuIGRpc3BsYXllZCBhbmQgc2F2ZWQgc3VjY2Vzc2Z1bGx5IGluIHRoZSAnUDJfdnNfUDMnIGZvbGRlci4iKQoKCgpgYGAKCgojIDUuIEVucmljaG1lbnQgQW5hbHlzaXMtQWxsX1BhdGh3YXlzCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KFJlYWN0b21lUEEpCmxpYnJhcnkoRE9TRSkgIyBGb3IgR1NFQSBhbmFseXNpcwpsaWJyYXJ5KGdncGxvdDIpICMgRW5zdXJlIGdncGxvdDIgaXMgYXZhaWxhYmxlIGZvciBwbG90dGluZwoKIyBEZWZpbmUgdGhyZXNob2xkIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBzZWxlY3Rpb24gKG1vZGlmaWVkIHRocmVzaG9sZHMpCmxvZ0ZDX3VwX3RocmVzaG9sZCA8LSAxICAgICAgICAgICMgVXByZWd1bGF0ZWQgbG9nRkMgdGhyZXNob2xkCmxvZ0ZDX2Rvd25fdGhyZXNob2xkIDwtIC0xICAgICAgICMgRG93bnJlZ3VsYXRlZCBsb2dGQyB0aHJlc2hvbGQKcHZhbF90aHJlc2hvbGQgPC0gMC4wNSAgICAgICAgICAgIyBwLXZhbHVlIHRocmVzaG9sZCBhcyBzcGVjaWZpZWQKCiMgTG9hZCB5b3VyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHMgKG1vZGlmeSBiYXNlZCBvbiBhY3R1YWwgZGF0YSBzdHJ1Y3R1cmUpCiMgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzIDwtIHJlYWQuY3N2KCJZb3VyX0RFX1Jlc3VsdHNfRmlsZS5jc3YiKQoKIyBTZWxlY3QgdXByZWd1bGF0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMKdXByZWd1bGF0ZWRfZ2VuZXMgPC0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzWwogIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRhdmdfbG9nRkMgPiBsb2dGQ191cF90aHJlc2hvbGQgJiAKICBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMkcF92YWxfYWRqIDwgcHZhbF90aHJlc2hvbGQsIF0KCmRvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzWwogIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRhdmdfbG9nRkMgPCBsb2dGQ19kb3duX3RocmVzaG9sZCAmIAogIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRwX3ZhbF9hZGogPCBwdmFsX3RocmVzaG9sZCwgXQoKIyBDaGVjayBmb3IgbWlzc2luZyBnZW5lcyAoTkFzKSBpbiB0aGUgZ2VuZSBjb2x1bW4gYW5kIHJlbW92ZSB0aGVtCnVwcmVndWxhdGVkX2dlbmVzIDwtIG5hLm9taXQodXByZWd1bGF0ZWRfZ2VuZXMpCmRvd25yZWd1bGF0ZWRfZ2VuZXMgPC0gbmEub21pdChkb3ducmVndWxhdGVkX2dlbmVzKQoKIyBTYXZlIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIGdlbmUgcmVzdWx0cyB0byBDU1YKd3JpdGUuY3N2KHVwcmVndWxhdGVkX2dlbmVzLCAiTWFsaWduYW50X3ZzX0NvbnRyb2wvdXByZWd1bGF0ZWRfZ2VuZXMuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihkb3ducmVndWxhdGVkX2dlbmVzLCAiTWFsaWduYW50X3ZzX0NvbnRyb2wvZG93bnJlZ3VsYXRlZF9nZW5lcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgQ29udmVydCBnZW5lIHN5bWJvbHMgdG8gRW50cmV6IElEcyBmb3IgZW5yaWNobWVudCBhbmFseXNpcywgd2l0aCBjaGVja3MgZm9yIG1pc3NpbmcgdmFsdWVzCnVwcmVndWxhdGVkX2VudHJleiA8LSBiaXRyKHVwcmVndWxhdGVkX2dlbmVzJGdlbmUsIGZyb21UeXBlID0gIlNZTUJPTCIsIHRvVHlwZSA9ICJFTlRSRVpJRCIsIE9yZ0RiID0gb3JnLkhzLmVnLmRiKQpkb3ducmVndWxhdGVkX2VudHJleiA8LSBiaXRyKGRvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgZnJvbVR5cGUgPSAiU1lNQk9MIiwgdG9UeXBlID0gIkVOVFJFWklEIiwgT3JnRGIgPSBvcmcuSHMuZWcuZGIpCgojIENoZWNrIGZvciBtaXNzaW5nIEVudHJleiBJRHMKbWlzc2luZ191cHJlZ3VsYXRlZCA8LSB1cHJlZ3VsYXRlZF9nZW5lcyRnZW5lW2lzLm5hKHVwcmVndWxhdGVkX2VudHJleiRFTlRSRVpJRCldCm1pc3NpbmdfZG93bnJlZ3VsYXRlZCA8LSBkb3ducmVndWxhdGVkX2dlbmVzJGdlbmVbaXMubmEoZG93bnJlZ3VsYXRlZF9lbnRyZXokRU5UUkVaSUQpXQoKIyBQcmludCBvdXQgdGhlIG1pc3NpbmcgZ2VuZSBzeW1ib2xzIGZvciBkZWJ1Z2dpbmcKY2F0KCJNaXNzaW5nIHVwcmVndWxhdGVkIGdlbmVzOlxuIiwgbWlzc2luZ191cHJlZ3VsYXRlZCwgIlxuIikKY2F0KCJNaXNzaW5nIGRvd25yZWd1bGF0ZWQgZ2VuZXM6XG4iLCBtaXNzaW5nX2Rvd25yZWd1bGF0ZWQsICJcbiIpCgojIFJlbW92ZSBnZW5lcyB0aGF0IGNvdWxkbid0IGJlIG1hcHBlZCB0byBFbnRyZXogSURzCnVwcmVndWxhdGVkX2VudHJleiA8LSB1cHJlZ3VsYXRlZF9lbnRyZXokRU5UUkVaSURbIWlzLm5hKHVwcmVndWxhdGVkX2VudHJleiRFTlRSRVpJRCldCmRvd25yZWd1bGF0ZWRfZW50cmV6IDwtIGRvd25yZWd1bGF0ZWRfZW50cmV6JEVOVFJFWklEWyFpcy5uYShkb3ducmVndWxhdGVkX2VudHJleiRFTlRSRVpJRCldCgojIERlZmluZSBhIGZ1bmN0aW9uIHRvIHNhZmVseSBydW4gZW5yaWNobWVudCwgcGxvdCByZXN1bHRzLCBhbmQgc2F2ZSB0aGVtCnNhZmVfZW5yaWNoR08gPC0gZnVuY3Rpb24oZ2VuZV9saXN0LCB0aXRsZSwgZmlsZW5hbWUpIHsKICBpZiAobGVuZ3RoKGdlbmVfbGlzdCkgPiAwKSB7CiAgICByZXN1bHQgPC0gZW5yaWNoR08oZ2VuZSA9IGdlbmVfbGlzdCwgT3JnRGIgPSBvcmcuSHMuZWcuZGIsIGtleVR5cGUgPSAiU1lNQk9MIiwKICAgICAgICAgICAgICAgICAgICAgICBvbnQgPSAiQlAiLCBwQWRqdXN0TWV0aG9kID0gIkJIIiwgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKICAgIGlmICghaXMubnVsbChyZXN1bHQpICYmIG5yb3coYXMuZGF0YS5mcmFtZShyZXN1bHQpKSA+IDApIHsKICAgICAgcCA8LSBkb3RwbG90KHJlc3VsdCwgc2hvd0NhdGVnb3J5ID0gMTAsIHRpdGxlID0gdGl0bGUpCiAgICAgIHByaW50KHApICAKICAgICAgZ2dzYXZlKHBhc3RlMCgiTWFsaWduYW50X3ZzX0NvbnRyb2wvIiwgZ3N1YigiLmNzdiIsICJfZG90cGxvdC5wbmciLCBmaWxlbmFtZSkpLCBwbG90ID0gcCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQogICAgICB3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShyZXN1bHQpLCBmaWxlID0gcGFzdGUwKCJNYWxpZ25hbnRfdnNfQ29udHJvbC8iLCBmaWxlbmFtZSksIHJvdy5uYW1lcyA9IEZBTFNFKQogICAgfSBlbHNlIHsKICAgICAgbWVzc2FnZShwYXN0ZSgiTm8gc2lnbmlmaWNhbnQgZW5yaWNobWVudCBmb3VuZCBmb3I6IiwgdGl0bGUpKQogICAgfQogIH0gZWxzZSB7CiAgICBtZXNzYWdlKHBhc3RlKCJObyBnZW5lcyBmb3VuZCBmb3I6IiwgdGl0bGUpKQogIH0KfQoKc2FmZV9lbnJpY2hLRUdHIDwtIGZ1bmN0aW9uKGVudHJlel9saXN0LCB0aXRsZSwgZmlsZW5hbWUpIHsKICBpZiAobGVuZ3RoKGVudHJlel9saXN0KSA+IDApIHsKICAgIHJlc3VsdCA8LSBlbnJpY2hLRUdHKGdlbmUgPSBlbnRyZXpfbGlzdCwgb3JnYW5pc20gPSAiaHNhIiwgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKICAgIGlmICghaXMubnVsbChyZXN1bHQpICYmIG5yb3coYXMuZGF0YS5mcmFtZShyZXN1bHQpKSA+IDApIHsKICAgICAgcCA8LSBkb3RwbG90KHJlc3VsdCwgc2hvd0NhdGVnb3J5ID0gMTAsIHRpdGxlID0gdGl0bGUpCiAgICAgIHByaW50KHApCiAgICAgIGdnc2F2ZShwYXN0ZTAoIk1hbGlnbmFudF92c19Db250cm9sLyIsIGdzdWIoIi5jc3YiLCAiX2RvdHBsb3QucG5nIiwgZmlsZW5hbWUpKSwgcGxvdCA9IHAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKICAgICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUocmVzdWx0KSwgZmlsZSA9IHBhc3RlMCgiTWFsaWduYW50X3ZzX0NvbnRyb2wvIiwgZmlsZW5hbWUpLCByb3cubmFtZXMgPSBGQUxTRSkKICAgIH0gZWxzZSB7CiAgICAgIG1lc3NhZ2UocGFzdGUoIk5vIHNpZ25pZmljYW50IEtFR0cgcGF0aHdheXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICAgIH0KICB9IGVsc2UgewogICAgbWVzc2FnZShwYXN0ZSgiTm8gZ2VuZXMgZm91bmQgZm9yOiIsIHRpdGxlKSkKICB9Cn0KCnNhZmVfZW5yaWNoUmVhY3RvbWUgPC0gZnVuY3Rpb24oZW50cmV6X2xpc3QsIHRpdGxlLCBmaWxlbmFtZSkgewogIGlmIChsZW5ndGgoZW50cmV6X2xpc3QpID4gMCkgewogICAgcmVzdWx0IDwtIGVucmljaFBhdGh3YXkoZ2VuZSA9IGVudHJlel9saXN0LCBvcmdhbmlzbSA9ICJodW1hbiIsIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiAgICBpZiAoIWlzLm51bGwocmVzdWx0KSAmJiBucm93KGFzLmRhdGEuZnJhbWUocmVzdWx0KSkgPiAwKSB7CiAgICAgIHAgPC0gZG90cGxvdChyZXN1bHQsIHNob3dDYXRlZ29yeSA9IDEwLCB0aXRsZSA9IHRpdGxlKQogICAgICBwcmludChwKQogICAgICBnZ3NhdmUocGFzdGUwKCJNYWxpZ25hbnRfdnNfQ29udHJvbC8iLCBnc3ViKCIuY3N2IiwgIl9kb3RwbG90LnBuZyIsIGZpbGVuYW1lKSksIHBsb3QgPSBwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCiAgICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKHJlc3VsdCksIGZpbGUgPSBwYXN0ZTAoIk1hbGlnbmFudF92c19Db250cm9sLyIsIGZpbGVuYW1lKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgICB9IGVsc2UgewogICAgICBtZXNzYWdlKHBhc3RlKCJObyBzaWduaWZpY2FudCBSZWFjdG9tZSBwYXRod2F5cyBmb3VuZCBmb3I6IiwgdGl0bGUpKQogICAgfQogIH0gZWxzZSB7CiAgICBtZXNzYWdlKHBhc3RlKCJObyBnZW5lcyBmb3VuZCBmb3I6IiwgdGl0bGUpKQogIH0KfQoKIyBQZXJmb3JtIGVucmljaG1lbnQgYW5hbHlzZXMsIGdlbmVyYXRlIHBsb3RzLCBhbmQgc2F2ZSByZXN1bHRzCnNhZmVfZW5yaWNoR08odXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgIkdPIEVucmljaG1lbnQgZm9yIFVwcmVndWxhdGVkIEdlbmVzIiwgInVwcmVndWxhdGVkX0dPX3Jlc3VsdHMuY3N2IikKc2FmZV9lbnJpY2hHTyhkb3ducmVndWxhdGVkX2dlbmVzJGdlbmUsICJHTyBFbnJpY2htZW50IGZvciBEb3ducmVndWxhdGVkIEdlbmVzIiwgImRvd25yZWd1bGF0ZWRfR09fcmVzdWx0cy5jc3YiKQoKc2FmZV9lbnJpY2hLRUdHKHVwcmVndWxhdGVkX2VudHJleiwgIktFR0cgUGF0aHdheSBFbnJpY2htZW50IGZvciBVcHJlZ3VsYXRlZCBHZW5lcyIsICJ1cHJlZ3VsYXRlZF9LRUdHX3Jlc3VsdHMuY3N2IikKc2FmZV9lbnJpY2hLRUdHKGRvd25yZWd1bGF0ZWRfZW50cmV6LCAiS0VHRyBQYXRod2F5IEVucmljaG1lbnQgZm9yIERvd25yZWd1bGF0ZWQgR2VuZXMiLCAiZG93bnJlZ3VsYXRlZF9LRUdHX3Jlc3VsdHMuY3N2IikKCnNhZmVfZW5yaWNoUmVhY3RvbWUodXByZWd1bGF0ZWRfZW50cmV6LCAiUmVhY3RvbWUgUGF0aHdheSBFbnJpY2htZW50IGZvciBVcHJlZ3VsYXRlZCBHZW5lcyIsICJ1cHJlZ3VsYXRlZF9SZWFjdG9tZV9yZXN1bHRzLmNzdiIpCnNhZmVfZW5yaWNoUmVhY3RvbWUoZG93bnJlZ3VsYXRlZF9lbnRyZXosICJSZWFjdG9tZSBQYXRod2F5IEVucmljaG1lbnQgZm9yIERvd25yZWd1bGF0ZWQgR2VuZXMiLCAiZG93bnJlZ3VsYXRlZF9SZWFjdG9tZV9yZXN1bHRzLmNzdiIpCgoKYGBgCgoKCgojIyBFbnJpY2htZW50IEFuYWx5c2lzX0hhbGxtYXJrCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CgojIExvYWQgbmVjZXNzYXJ5IGxpYnJhcmllcwpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShvcmcuSHMuZWcuZGIpCmxpYnJhcnkobXNpZ2RicikKbGlicmFyeShlbnJpY2hwbG90KQoKIyBMb2FkIEhhbGxtYXJrIGdlbmUgc2V0cyBmcm9tIG1zaWdkYnIKaGFsbG1hcmtfc2V0cyA8LSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiSCIpICAjICJIIiBpcyBmb3IgSGFsbG1hcmsgZ2VuZSBzZXRzCgojIENvbnZlcnQgZ2VuZSBzeW1ib2xzIHRvIHVwcGVyY2FzZSBmb3IgY29uc2lzdGVuY3kKdXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSA8LSB0b3VwcGVyKHVwcmVndWxhdGVkX2dlbmVzJGdlbmUpCmRvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSA8LSB0b3VwcGVyKGRvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSkKCiMgQ2hlY2sgZm9yIG92ZXJsYXAgYmV0d2VlbiB5b3VyIHVwcmVndWxhdGVkL2Rvd25yZWd1bGF0ZWQgZ2VuZXMgYW5kIEhhbGxtYXJrIGdlbmUgc2V0cwp1cHJlZ3VsYXRlZF9pbl9oYWxsbWFyayA8LSBpbnRlcnNlY3QodXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgaGFsbG1hcmtfc2V0cyRnZW5lX3N5bWJvbCkKZG93bnJlZ3VsYXRlZF9pbl9oYWxsbWFyayA8LSBpbnRlcnNlY3QoZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBoYWxsbWFya19zZXRzJGdlbmVfc3ltYm9sKQoKIyBQcmludCB0aGUgbnVtYmVyIG9mIG92ZXJsYXBwaW5nIGdlbmVzIGZvciBib3RoIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIGdlbmVzCmNhdCgiTnVtYmVyIG9mIHVwcmVndWxhdGVkIGdlbmVzIGluIEhhbGxtYXJrIGdlbmUgc2V0czoiLCBsZW5ndGgodXByZWd1bGF0ZWRfaW5faGFsbG1hcmspLCAiXG4iKQpjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIGluIEhhbGxtYXJrIGdlbmUgc2V0czoiLCBsZW5ndGgoZG93bnJlZ3VsYXRlZF9pbl9oYWxsbWFyayksICJcbiIpCgojIERlZmluZSB0aGUgb3V0cHV0IGZvbGRlciB3aGVyZSB0aGUgcmVzdWx0cyB3aWxsIGJlIHNhdmVkCm91dHB1dF9mb2xkZXIgPC0gIk1hbGlnbmFudF92c19Db250cm9sLyIKCiMgSWYgdGhlcmUgYXJlIGdlbmVzIHRvIGFuYWx5emUsIHByb2NlZWQgd2l0aCBlbnJpY2htZW50IGFuYWx5c2lzCmlmIChsZW5ndGgodXByZWd1bGF0ZWRfaW5faGFsbG1hcmspID4gMCkgewogICMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzIGZvciB1cHJlZ3VsYXRlZCBnZW5lcyB1c2luZyBIYWxsbWFyayBnZW5lIHNldHMKICBoYWxsbWFya191cCA8LSBlbnJpY2hlcihnZW5lID0gdXByZWd1bGF0ZWRfaW5faGFsbG1hcmssIAogICAgICAgICAgICAgICAgICAgICAgICAgIFRFUk0yR0VORSA9IGhhbGxtYXJrX3NldHNbLCBjKCJnc19uYW1lIiwgImdlbmVfc3ltYm9sIildLCAgIyBFbnN1cmUgVEVSTTJHRU5FIHVzZXMgY29ycmVjdCBjb2x1bW5zCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKICAjIENoZWNrIGlmIHJlc3VsdHMgZXhpc3QKICBpZiAoIWlzLm51bGwoaGFsbG1hcmtfdXApICYmIG5yb3coaGFsbG1hcmtfdXApID4gMCkgewogICAgIyBWaXN1YWxpemUgcmVzdWx0cyBpZiBhdmFpbGFibGUKICAgIHVwX2RvdHBsb3QgPC0gZG90cGxvdChoYWxsbWFya191cCwgc2hvd0NhdGVnb3J5ID0gMjAsIHRpdGxlID0gIkhhbGxtYXJrIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgVXByZWd1bGF0ZWQgR2VuZXMiKQogICAgCiAgICAjIERpc3BsYXkgdGhlIHBsb3QgaW4gdGhlIG5vdGVib29rCiAgICBwcmludCh1cF9kb3RwbG90KQogICAgCiAgICAjIFNhdmUgdGhlIGRvdHBsb3QgdG8gYSBQTkcgZmlsZQogICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfdXByZWd1bGF0ZWRfZG90cGxvdC5wbmciKSwgcGxvdCA9IHVwX2RvdHBsb3QsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCiAgICAKICAgICMgT3B0aW9uYWxseSwgc2F2ZSB0aGUgcmVzdWx0cyBhcyBDU1YKICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKGhhbGxtYXJrX3VwKSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfdXByZWd1bGF0ZWRfZW5yaWNobWVudC5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgfSBlbHNlIHsKICAgIGNhdCgiTm8gc2lnbmlmaWNhbnQgZW5yaWNobWVudCBmb3VuZCBmb3IgdXByZWd1bGF0ZWQgZ2VuZXMuXG4iKQogIH0KfSBlbHNlIHsKICBjYXQoIk5vIHVwcmVndWxhdGVkIGdlbmVzIG92ZXJsYXAgd2l0aCBIYWxsbWFyayBnZW5lIHNldHMuXG4iKQp9CgppZiAobGVuZ3RoKGRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmspID4gMCkgewogICMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzIGZvciBkb3ducmVndWxhdGVkIGdlbmVzIHVzaW5nIEhhbGxtYXJrIGdlbmUgc2V0cwogIGhhbGxtYXJrX2Rvd24gPC0gZW5yaWNoZXIoZ2VuZSA9IGRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVEVSTTJHRU5FID0gaGFsbG1hcmtfc2V0c1ssIGMoImdzX25hbWUiLCAiZ2VuZV9zeW1ib2wiKV0sICAjIEVuc3VyZSBURVJNMkdFTkUgdXNlcyBjb3JyZWN0IGNvbHVtbnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiAgIyBDaGVjayBpZiByZXN1bHRzIGV4aXN0CiAgaWYgKCFpcy5udWxsKGhhbGxtYXJrX2Rvd24pICYmIG5yb3coaGFsbG1hcmtfZG93bikgPiAwKSB7CiAgICAjIFZpc3VhbGl6ZSByZXN1bHRzIGlmIGF2YWlsYWJsZQogICAgZG93bl9kb3RwbG90IDwtIGRvdHBsb3QoaGFsbG1hcmtfZG93biwgc2hvd0NhdGVnb3J5ID0gMjAsIHRpdGxlID0gIkhhbGxtYXJrIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgRG93bnJlZ3VsYXRlZCBHZW5lcyIpCiAgICAKICAgICMgRGlzcGxheSB0aGUgcGxvdCBpbiB0aGUgbm90ZWJvb2sKICAgIHByaW50KGRvd25fZG90cGxvdCkKICAgIAogICAgIyBTYXZlIHRoZSBkb3RwbG90IHRvIGEgUE5HIGZpbGUKICAgIGdnc2F2ZShwYXN0ZTAob3V0cHV0X2ZvbGRlciwgImhhbGxtYXJrX2Rvd25yZWd1bGF0ZWRfZG90cGxvdC5wbmciKSwgcGxvdCA9IGRvd25fZG90cGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKICAgIAogICAgIyBPcHRpb25hbGx5LCBzYXZlIHRoZSByZXN1bHRzIGFzIENTVgogICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUoaGFsbG1hcmtfZG93biksIGZpbGUgPSBwYXN0ZTAob3V0cHV0X2ZvbGRlciwgImhhbGxtYXJrX2Rvd25yZWd1bGF0ZWRfZW5yaWNobWVudC5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgfSBlbHNlIHsKICAgIGNhdCgiTm8gc2lnbmlmaWNhbnQgZW5yaWNobWVudCBmb3VuZCBmb3IgZG93bnJlZ3VsYXRlZCBnZW5lcy5cbiIpCiAgfQp9IGVsc2UgewogIGNhdCgiTm8gZG93bnJlZ3VsYXRlZCBnZW5lcyBvdmVybGFwIHdpdGggSGFsbG1hcmsgZ2VuZSBzZXRzLlxuIikKfQoKCmBgYAoKCgoKCgoK