1. load libraries

#Differential Expression Analysis

2. load seurat object

#Load Seurat Object L7
load("../../0-IMP-OBJECTS/All_CD4_Tcells_Merged_1-13_res-0.9.Robj")


All_samples_Merged
An object of class Seurat 
62625 features across 46976 samples within 6 assays 
Active assay: SCT (25901 features, 3000 variable features)
 3 layers present: counts, data, scale.data
 5 other assays present: RNA, ADT, prediction.score.celltype.l1, prediction.score.celltype.l2, prediction.score.celltype.l3
 4 dimensional reductions calculated: pca, umap, integrated_dr, ref.umap

#Differential Expression Analysis

3. Find Markers for Each Cell Line


DefaultAssay(All_samples_Merged) <- "SCT"
Idents(All_samples_Merged) <- "cell_line"
all_markers <- FindAllMarkers(All_samples_Merged, only.pos = TRUE, min.pct = 0.25, logfc.threshold = 0.25)
Calculating cluster L1
Calculating cluster L2
Calculating cluster L3
Calculating cluster L4
Calculating cluster L5
Calculating cluster L6
Calculating cluster L7
Calculating cluster PBMC
write.csv(all_markers, "all_markers.csv")
head(all_markers)
NA
NA

4. Heatmap of Top 10 Upregulated Genes (Sorted by avg_log2FC)


top10_markers <- all_markers %>%
  group_by(cluster) %>%
  top_n(n = 10, wt = avg_log2FC) %>%
  arrange(cluster, desc(avg_log2FC))

top10_genes <- unique(top10_markers$gene)

heatmap_data <- GetAssayData(All_samples_Merged, assay = "SCT", slot = "data")[top10_genes, ]
Avis : The `slot` argument of `GetAssayData()` is deprecated as of SeuratObject 5.0.0.
Please use the `layer` argument instead.
heatmap_data <- heatmap_data - rowMeans(heatmap_data)

cell_line_colors <- rainbow(length(unique(All_samples_Merged$cell_line)))
names(cell_line_colors) <- unique(All_samples_Merged$cell_line)
annotation_colors <- list(cell_line = cell_line_colors)

p <- pheatmap(heatmap_data,
         show_rownames = TRUE,
         show_colnames = FALSE,
         annotation_col = All_samples_Merged@meta.data[, "cell_line", drop = FALSE],
         annotation_colors = annotation_colors,
         main = "Top 10 Upregulated Genes per Cell Line (Sorted by avg_log2FC)",
         fontsize_row = 6,
         treeheight_col = 0,
         cluster_rows = FALSE,
         cluster_cols = FALSE)

print(p)
png("heatmap_top10_log2fc_sorted.png", width = 12, height = 8, units = "in", res = 300)
print(p)
dev.off()
png 
  2 

5. Heatmap of Top 10 Markers (Seurat Default)


top10_markers_seurat <- all_markers %>%
  group_by(cluster) %>%
  top_n(n = 10, wt = avg_log2FC)

top10_genes_seurat <- unique(top10_markers_seurat$gene)

heatmap_data_seurat <- GetAssayData(All_samples_Merged, assay = "SCT", slot = "data")[top10_genes_seurat, ]
heatmap_data_seurat <- heatmap_data_seurat - rowMeans(heatmap_data_seurat)

p2 <- pheatmap(heatmap_data_seurat,
         show_rownames = TRUE,
         show_colnames = FALSE,
         annotation_col = All_samples_Merged@meta.data[, "cell_line", drop = FALSE],
         annotation_colors = annotation_colors,
         main = "Top 10 Markers per Cell Line (Seurat Default)",
         fontsize_row = 6,
         treeheight_col = 0)
View(top10_markers)

print(p2)
png("heatmap_top10_seurat.png", width = 12, height = 8, units = "in", res = 300)
print(p2)
dev.off()
png 
  2 

6. Pairwise Comparisons

library(EnhancedVolcano)

perform_comparison_and_volcano <- function(All_samples_Merged, ident1, ident2) {
  Idents(All_samples_Merged) <- "cell_line"
  markers <- FindMarkers(All_samples_Merged, ident.1 = ident1, ident.2 = ident2, assay = "SCT")
  write.csv(markers, paste0("comparison_", ident1, "_vs_", ident2, ".csv"))
  
  # Create volcano plot
  volcano_plot <- EnhancedVolcano(markers,
                                  lab = rownames(markers),
                                  x = 'avg_log2FC',
                                  y = 'p_val_adj',
                                  title = paste(ident1, 'vs', ident2),
                                  pCutoff = 0.05,
                                  FCcutoff = 1,
                                  pointSize = 1.5,
                                  labSize = 4.0,
                                  col = c('grey', 'darkgreen', 'blue', 'red'),
                                  colAlpha = 0.5,
                                  legendPosition = 'right',
                                  legendLabSize = 10,
                                  legendIconSize = 4.0,
                                  drawConnectors = TRUE,
                                  widthConnectors = 0.5)
  
  print(volcano_plot)
  png(paste0("volcano_", ident1, "_vs_", ident2, ".png"), width = 12, height = 10, units = "in", res = 300)
  print(volcano_plot)
  dev.off()
  
  return(markers)
}

# Patient 1
p1_comparison <- perform_comparison_and_volcano(All_samples_Merged, "L1", "L2")
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...

head(p1_comparison)

# Patient 2
p2_comparison <- perform_comparison_and_volcano(All_samples_Merged, "L3", "L4")
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...

head(p2_comparison)

# Patient 3
p3_comparison_L5L6 <- perform_comparison_and_volcano(All_samples_Merged, "L5", "L6")
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...

p3_comparison_L5L7 <- perform_comparison_and_volcano(All_samples_Merged, "L5", "L7")
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...

p3_comparison_L6L7 <- perform_comparison_and_volcano(All_samples_Merged, "L6", "L7")
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...

head(p3_comparison_L5L6)
head(p3_comparison_L5L7)
head(p3_comparison_L6L7)
NA
NA

7. Enrichment Analysis


perform_go_enrichment <- function(gene_list, gene_universe, title) {
  ego <- enrichGO(gene = gene_list,
                  universe = gene_universe,
                  OrgDb = org.Hs.eg.db,
                  keyType = "SYMBOL",
                  ont = "BP",
                  pAdjustMethod = "BH",
                  qvalueCutoff = 0.05,
                  readable = TRUE)
  
  p <- dotplot(ego, showCategory = 20, title = paste("GO -", title)) +
    theme(axis.text.y = element_text(size = 8))
  
  print(p)
  png(paste0("GO_enrichment_", gsub(" ", "_", title), ".png"), width = 12, height = 8, units = "in", res = 300)
  print(p)
  dev.off()
  
  return(ego)
}

perform_kegg_enrichment <- function(gene_list, gene_universe, title) {
  # Convert gene symbols to Entrez IDs
  entrez_ids <- bitr(gene_list, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)$ENTREZID
  universe_entrez <- bitr(gene_universe, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)$ENTREZID
  
  ekegg <- enrichKEGG(gene = entrez_ids,
                      universe = universe_entrez,
                      organism = 'hsa',
                      keyType = "kegg",
                      pvalueCutoff = 0.05,
                      pAdjustMethod = "BH")
  
  p <- dotplot(ekegg, showCategory = 20, title = paste("KEGG -", title)) +
    theme(axis.text.y = element_text(size = 8))
  
  print(p)
  png(paste0("KEGG_enrichment_", gsub(" ", "_", title), ".png"), width = 12, height = 8, units = "in", res = 300)
  print(p)
  dev.off()
  
  return(ekegg)
}

gene_universe <- rownames(All_samples_Merged)

# Patient 1 (P1) comparison: L1 vs L2
upregulated_genes_P1 <- rownames(p1_comparison[p1_comparison$avg_log2FC > 1 & p1_comparison$p_val_adj < 0.05, ])
downregulated_genes_P1 <- rownames(p1_comparison[p1_comparison$avg_log2FC < -1 & p1_comparison$p_val_adj < 0.05, ])

go_up_P1 <- perform_go_enrichment(upregulated_genes_P1, gene_universe, "Upregulated Genes in L1 vs L2")

go_down_P1 <- perform_go_enrichment(downregulated_genes_P1, gene_universe, "Downregulated Genes in L1 vs L2")

kegg_up_P1 <- perform_kegg_enrichment(upregulated_genes_P1, gene_universe, "Upregulated Genes in L1 vs L2")
'select()' returned 1:1 mapping between keys and columns
Avis : 10.42% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 27.64% of input gene IDs are fail to map...Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...

kegg_down_P1 <- perform_kegg_enrichment(downregulated_genes_P1, gene_universe, "Downregulated Genes in L1 vs L2")
'select()' returned 1:many mapping between keys and columns
Avis : 9.97% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 27.64% of input gene IDs are fail to map...

# Patient 2 (P2) comparison: L3 vs L4
upregulated_genes_P2 <- rownames(p2_comparison[p2_comparison$avg_log2FC > 1 & p2_comparison$p_val_adj < 0.05, ])
downregulated_genes_P2 <- rownames(p2_comparison[p2_comparison$avg_log2FC < -1 & p2_comparison$p_val_adj < 0.05, ])

go_up_P2 <- perform_go_enrichment(upregulated_genes_P2, gene_universe, "Upregulated Genes in L3 vs L4")

go_down_P2 <- perform_go_enrichment(downregulated_genes_P2, gene_universe, "Downregulated Genes in L3 vs L4")

kegg_up_P2 <- perform_kegg_enrichment(upregulated_genes_P2, gene_universe, "Upregulated Genes in L3 vs L4")
'select()' returned 1:1 mapping between keys and columns
Avis : 10.94% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 27.64% of input gene IDs are fail to map...

kegg_down_P2 <- perform_kegg_enrichment(downregulated_genes_P2, gene_universe, "Downregulated Genes in L3 vs L4")
'select()' returned 1:many mapping between keys and columns
Avis : 13.7% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 27.64% of input gene IDs are fail to map...

# Patient 3 (P3) comparisons
# L5 vs L6
upregulated_genes_P3_L5L6 <- rownames(p3_comparison_L5L6[p3_comparison_L5L6$avg_log2FC > 1 & p3_comparison_L5L6$p_val_adj < 0.05, ])
downregulated_genes_P3_L5L6 <- rownames(p3_comparison_L5L6[p3_comparison_L5L6$avg_log2FC < -1 & p3_comparison_L5L6$p_val_adj < 0.05, ])

go_up_P3_L5L6 <- perform_go_enrichment(upregulated_genes_P3_L5L6, gene_universe, "Upregulated Genes in L5 vs L6")

go_down_P3_L5L6 <- perform_go_enrichment(downregulated_genes_P3_L5L6, gene_universe, "Downregulated Genes in L5 vs L6")

kegg_up_P3_L5L6 <- perform_kegg_enrichment(upregulated_genes_P3_L5L6, gene_universe, "Upregulated Genes in L5 vs L6")
'select()' returned 1:1 mapping between keys and columns
Avis : 9.53% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 27.64% of input gene IDs are fail to map...

kegg_down_P3_L5L6 <- perform_kegg_enrichment(downregulated_genes_P3_L5L6, gene_universe, "Downregulated Genes in L5 vs L6")
'select()' returned 1:1 mapping between keys and columns
Avis : 15.5% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 27.64% of input gene IDs are fail to map...

# L5 vs L7
upregulated_genes_P3_L5L7 <- rownames(p3_comparison_L5L7[p3_comparison_L5L7$avg_log2FC > 1 & p3_comparison_L5L7$p_val_adj < 0.05, ])
downregulated_genes_P3_L5L7 <- rownames(p3_comparison_L5L7[p3_comparison_L5L7$avg_log2FC < -1 & p3_comparison_L5L7$p_val_adj < 0.05, ])

go_up_P3_L5L7 <- perform_go_enrichment(upregulated_genes_P3_L5L7, gene_universe, "Upregulated Genes in L5 vs L7")

go_down_P3_L5L7 <- perform_go_enrichment(downregulated_genes_P3_L5L7, gene_universe, "Downregulated Genes in L5 vs L7")

kegg_up_P3_L5L7 <- perform_kegg_enrichment(upregulated_genes_P3_L5L7, gene_universe, "Upregulated Genes in L5 vs L7")
'select()' returned 1:1 mapping between keys and columns
Avis : 13.71% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 27.64% of input gene IDs are fail to map...

kegg_down_P3_L5L7 <- perform_kegg_enrichment(downregulated_genes_P3_L5L7, gene_universe, "Downregulated Genes in L5 vs L7")
'select()' returned 1:many mapping between keys and columns
Avis : 12.59% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 27.64% of input gene IDs are fail to map...

# L6 vs L7
upregulated_genes_P3_L6L7 <- rownames(p3_comparison_L6L7[p3_comparison_L6L7$avg_log2FC > 1 & p3_comparison_L6L7$p_val_adj < 0.05, ])
downregulated_genes_P3_L6L7 <- rownames(p3_comparison_L6L7[p3_comparison_L6L7$avg_log2FC < -1 & p3_comparison_L6L7$p_val_adj < 0.05, ])

go_up_P3_L6L7 <- perform_go_enrichment(upregulated_genes_P3_L6L7, gene_universe, "Upregulated Genes in L6 vs L7")

go_down_P3_L6L7 <- perform_go_enrichment(downregulated_genes_P3_L6L7, gene_universe, "Downregulated Genes in L6 vs L7")

kegg_up_P3_L6L7 <- perform_kegg_enrichment(upregulated_genes_P3_L6L7, gene_universe, "Upregulated Genes in L6 vs L7")
'select()' returned 1:1 mapping between keys and columns
Avis : 16.76% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 27.64% of input gene IDs are fail to map...

kegg_down_P3_L6L7 <- perform_kegg_enrichment(downregulated_genes_P3_L6L7, gene_universe, "Downregulated Genes in L6 vs L7")
'select()' returned 1:many mapping between keys and columns
Avis : 8.58% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 27.64% of input gene IDs are fail to map...

8. Network Analysis

# Function to get top genes from a comparison

library(igraph)
library(STRINGdb)
library(ggraph)
library(tidyverse)
library(tibble)

get_top_genes <- function(comparison_result, n = 50) {
  top_genes <- comparison_result %>%
    rownames_to_column("gene") %>%
    arrange(desc(abs(avg_log2FC))) %>%
    head(n) %>%
    pull(gene)
  return(top_genes)
}

# Combine top genes from all comparisons
all_top_genes <- unique(c(
  get_top_genes(p1_comparison),
  get_top_genes(p2_comparison),
  get_top_genes(p3_comparison_L5L6),
  get_top_genes(p3_comparison_L5L7),
  get_top_genes(p3_comparison_L6L7)
))

# Initialize STRINGdb
string_db <- STRINGdb$new(version="11", species=9606, score_threshold=700)

# Map genes to STRING identifiers
mapped_genes <- string_db$map(data.frame(gene=all_top_genes), "gene", removeUnmappedRows = TRUE)
Warning:  we couldn't map to STRING 21% of your identifiers
# Get interactions
interactions <- string_db$get_interactions(mapped_genes$STRING_id)

# Create igraph object
g <- graph_from_data_frame(interactions, directed = FALSE)

# Calculate node degrees
V(g)$degree <- degree(g)

# Calculate betweenness centrality
V(g)$betweenness <- betweenness(g)

# Identify communities
communities <- cluster_louvain(g)
V(g)$community <- communities$membership

# Plot the network
set.seed(123)  # for reproducibility
ggraph(g, layout = "fr") +
  geom_edge_link(aes(edge_alpha = combined_score), show.legend = FALSE) +
  geom_node_point(aes(color = factor(community), size = degree)) +
  geom_node_text(aes(label = name), repel = TRUE, size = 3) +
  scale_color_brewer(palette = "Set1") +
  theme_void() +
  labs(title = "Gene Interaction Network",
       subtitle = "Based on top differentially expressed genes",
       color = "Community",
       size = "Degree")


# Save the plot
ggsave("gene_interaction_network.png", width = 12, height = 10, dpi = 300)

# Identify hub genes
hub_genes <- V(g)$name[order(V(g)$degree, decreasing = TRUE)][1:10]
cat("Top 10 hub genes:\n")
Top 10 hub genes:
print(hub_genes)
 [1] "9606.ENSP00000385675" "9606.ENSP00000362795" "9606.ENSP00000426022" "9606.ENSP00000482259" "9606.ENSP00000229135"
 [6] "9606.ENSP00000216341" "9606.ENSP00000245451" "9606.ENSP00000292301" "9606.ENSP00000398568" "9606.ENSP00000177694"
# Identify genes with high betweenness centrality
high_betweenness_genes <- V(g)$name[order(V(g)$betweenness, decreasing = TRUE)][1:10]
cat("\nTop 10 genes with high betweenness centrality:\n")

Top 10 genes with high betweenness centrality:
print(high_betweenness_genes)
 [1] "9606.ENSP00000385675" "9606.ENSP00000426022" "9606.ENSP00000398568" "9606.ENSP00000362795" "9606.ENSP00000245451"
 [6] "9606.ENSP00000482259" "9606.ENSP00000216341" "9606.ENSP00000381448" "9606.ENSP00000292301" "9606.ENSP00000262768"
# Calculate and print some network statistics
cat("\nNetwork Statistics:\n")

Network Statistics:
cat("Number of nodes:", vcount(g), "\n")
Number of nodes: 57 
cat("Number of edges:", ecount(g), "\n")
Number of edges: 216 
cat("Network density:", edge_density(g), "\n")
Network density: 0.1353383 
cat("Average path length:", mean_distance(g), "\n")
Average path length: 3.02935 
cat("Clustering coefficient:", transitivity(g), "\n")
Clustering coefficient: 0.450727 
# Extract edge information
edges_df <- data.frame(
  from = ends(g, E(g))[,1],
  to = ends(g, E(g))[,2]
)

# Add edge attributes if any
edge_attrs <- edge_attr(g)
for (attr in names(edge_attrs)) {
  edges_df[[attr]] <- edge_attrs[[attr]]
}

# Save the edges data frame
write.csv(edges_df, "gene_network_edges.csv", row.names = FALSE)

# Extract node information
nodes_df <- data.frame(
  id = V(g)$name,
  degree = degree(g),
  betweenness = betweenness(g)
)

# Add other vertex attributes if any
vertex_attrs <- vertex_attr(g)
for (attr in names(vertex_attrs)) {
  if (attr != "name") {  # Skip 'name' as we already have it as 'id'
    nodes_df[[attr]] <- vertex_attrs[[attr]]
  }
}

# Save the nodes data frame
write.csv(nodes_df, "gene_network_nodes.csv", row.names = FALSE)

# Print a summary of the saved data
cat("Saved network data:\n")
Saved network data:
cat("Edges file: gene_network_edges.csv -", nrow(edges_df), "rows\n")
Edges file: gene_network_edges.csv - 216 rows
cat("Nodes file: gene_network_nodes.csv -", nrow(nodes_df), "rows\n")
Nodes file: gene_network_nodes.csv - 57 rows
# Function to get top genes from a comparison

library(igraph)
library(STRINGdb)
library(ggraph)
library(tidyverse)
library(tibble)

get_top_genes <- function(comparison_result, n = 50) {
  top_genes <- comparison_result %>%
    rownames_to_column("gene") %>%
    arrange(desc(abs(avg_log2FC))) %>%
    head(n) %>%
    pull(gene)
  return(top_genes)
}

# Combine top genes from all comparisons
all_top_genes <- unique(c(
  get_top_genes(p1_comparison),
  get_top_genes(p2_comparison),
  get_top_genes(p3_comparison_L5L6),
  get_top_genes(p3_comparison_L5L7),
  get_top_genes(p3_comparison_L6L7)
))

# Initialize STRINGdb
string_db <- STRINGdb$new(version="11", species=9606, score_threshold=700)

# Map genes to STRING identifiers
mapped_genes <- string_db$map(data.frame(gene=all_top_genes), "gene", removeUnmappedRows = TRUE)
Warning:  we couldn't map to STRING 21% of your identifiers
# Get interactions
interactions <- string_db$get_interactions(mapped_genes$STRING_id)

# Map STRING identifiers back to gene symbols
interactions$from <- mapped_genes$gene[match(interactions$from, mapped_genes$STRING_id)]
interactions$to <- mapped_genes$gene[match(interactions$to, mapped_genes$STRING_id)]

# Create igraph object
g <- graph_from_data_frame(interactions, directed = FALSE)

# Calculate node degrees
V(g)$degree <- degree(g)

# Calculate betweenness centrality
V(g)$betweenness <- betweenness(g)

# Identify communities
communities <- cluster_louvain(g)
V(g)$community <- communities$membership

# Plot the network
set.seed(123)  # for reproducibility
ggraph(g, layout = "fr") +
  geom_edge_link(aes(edge_alpha = combined_score), show.legend = FALSE) +
  geom_node_point(aes(color = factor(community), size = degree)) +
  geom_node_text(aes(label = name), repel = TRUE, size = 3) +
  scale_color_brewer(palette = "Set1") +
  theme_void() +
  labs(title = "Gene Interaction Network",
       subtitle = "Based on top differentially expressed genes",
       color = "Community",
       size = "Degree")


# Save the plot
ggsave("gene_interaction_network.png", width = 12, height = 10, dpi = 300)

# Identify hub genes
hub_genes <- V(g)$name[order(V(g)$degree, decreasing = TRUE)][1:10]
cat("Top 10 hub genes:\n")
Top 10 hub genes:
print(hub_genes)
 [1] "IL6"   "CXCR3" "GNGT2" "CCL4"  "IFNG"  "GZMB"  "BMP4"  "CCR2"  "PRF1"  "TBX21"
# Identify genes with high betweenness centrality
high_betweenness_genes <- V(g)$name[order(V(g)$betweenness, decreasing = TRUE)][1:10]
cat("\nTop 10 genes with high betweenness centrality:\n")

Top 10 genes with high betweenness centrality:
print(high_betweenness_genes)
 [1] "IL6"   "GNGT2" "PRF1"  "CXCR3" "BMP4"  "CCL4"  "GZMB"  "CST3"  "CCR2"  "TIMP2"
# Calculate and print some network statistics
cat("\nNetwork Statistics:\n")

Network Statistics:
cat("Number of nodes:", vcount(g), "\n")
Number of nodes: 57 
cat("Number of edges:", ecount(g), "\n")
Number of edges: 216 
cat("Network density:", edge_density(g), "\n")
Network density: 0.1353383 
cat("Average path length:", mean_distance(g), "\n")
Average path length: 3.02935 
cat("Clustering coefficient:", transitivity(g), "\n")
Clustering coefficient: 0.450727 
# Extract edge information
edges_df <- data.frame(
  from = ends(g, E(g))[,1],
  to = ends(g, E(g))[,2]
)

# Add edge attributes if any
edge_attrs <- edge_attr(g)
for (attr in names(edge_attrs)) {
  edges_df[[attr]] <- edge_attrs[[attr]]
}

# Save the edges data frame
write.csv(edges_df, "gene_network_edges.csv", row.names = FALSE)

# Extract node information
nodes_df <- data.frame(
  id = V(g)$name,
  degree = degree(g),
  betweenness = betweenness(g)
)

# Add other vertex attributes if any
vertex_attrs <- vertex_attr(g)
for (attr in names(vertex_attrs)) {
  if (attr != "name") {  # Skip 'name' as we already have it as 'id'
    nodes_df[[attr]] <- vertex_attrs[[attr]]
  }
}

# Save the nodes data frame
write.csv(nodes_df, "gene_network_nodes.csv", row.names = FALSE)

# Print a summary of the saved data
cat("Saved network data:\n")
Saved network data:
cat("Edges file: gene_network_edges.csv -", nrow(edges_df), "rows\n")
Edges file: gene_network_edges.csv - 216 rows
cat("Nodes file: gene_network_nodes.csv -", nrow(nodes_df), "rows\n")
Nodes file: gene_network_nodes.csv - 57 rows

9. Network Analysis-kegg


# Load required libraries
library(igraph)
library(ggraph)
library(tidyverse)
library(tibble)
library(org.Hs.eg.db)
library(GO.db)
library(AnnotationDbi)
library(dplyr)

# Function to get top genes from comparison results
get_top_genes <- function(comparison_result, n = 50) {
  top_genes <- comparison_result %>%
    tibble::rownames_to_column("gene") %>%
    dplyr::arrange(desc(abs(avg_log2FC))) %>%
    head(n) %>%
    dplyr::pull(gene)
  return(top_genes)
}

# Combine top genes from all comparisons
all_top_genes <- unique(c(
  get_top_genes(p1_comparison),
  get_top_genes(p2_comparison),
  get_top_genes(p3_comparison_L5L6),
  get_top_genes(p3_comparison_L5L7),
  get_top_genes(p3_comparison_L6L7)
))

# Get GO terms for all top genes
go_terms <- AnnotationDbi::select(org.Hs.eg.db, keys = all_top_genes, 
                                  columns = c("SYMBOL", "GO", "ONTOLOGY"), 
                                  keytype = "SYMBOL")
'select()' returned 1:many mapping between keys and columns
# Filter for biological process GO terms and remove NA values
go_terms_bp <- go_terms %>%
  dplyr::filter(ONTOLOGY == "BP") %>%
  dplyr::filter(!is.na(GO))

# Create edges dataframe
edges <- go_terms_bp %>%
  dplyr::select(from = SYMBOL, to = GO)

# Print summary of edges
print(paste("Number of edges:", nrow(edges)))
[1] "Number of edges: 2309"
print(head(edges))

# If edges dataframe is empty, stop here
if (nrow(edges) == 0) {
  stop("No GO terms found for any genes. Cannot create network.")
}

# Create graph
g <- igraph::graph_from_data_frame(edges, directed = FALSE)

# Calculate node degrees
V(g)$degree <- igraph::degree(g)

# Calculate betweenness centrality
V(g)$betweenness <- igraph::betweenness(g)

# Identify communities
communities <- igraph::cluster_louvain(g)
V(g)$community <- communities$membership

# Get GO term descriptions
go_terms_desc <- AnnotationDbi::select(GO.db, keys = unique(edges$to), 
                                       columns = "TERM", keytype = "GOID")
'select()' returned 1:1 mapping between keys and columns
# Add GO term descriptions to the graph
V(g)$description <- go_terms_desc$TERM[match(V(g)$name, go_terms_desc$GOID)]

# Plot the network
set.seed(123)  # for reproducibility
p <- ggraph(g, layout = "fr") +
  geom_edge_link(alpha = 0.1) +
  geom_node_point(aes(color = factor(community), size = degree)) +
  geom_node_text(aes(label = ifelse(degree > quantile(degree, 0.95), name, "")), 
                 repel = TRUE, size = 3) +
  scale_color_brewer(palette = "Set1") +
  theme_void() +
  labs(title = "Gene-GO Term Interaction Network",
       subtitle = "Based on top differentially expressed genes",
       color = "Community",
       size = "Degree")

# Save the plot
ggsave("gene_go_network.png", p, width = 12, height = 10, dpi = 300)

# Identify hub genes
hub_genes <- V(g)$name[V(g)$name %in% all_top_genes][order(V(g)$degree[V(g)$name %in% all_top_genes], decreasing = TRUE)][1:10]
cat("Top 10 hub genes:\n")
Top 10 hub genes:
print(hub_genes)
 [1] "BMP4"   "IL6"    "SNCA"   "IFNG"   "NKX2-5" "SIX1"   "WNT11"  "SOX4"   "CCR2"   "IL1A"  
# Identify genes with high betweenness centrality
high_betweenness_genes <- V(g)$name[V(g)$name %in% all_top_genes][order(V(g)$betweenness[V(g)$name %in% all_top_genes], decreasing = TRUE)][1:10]
cat("\nTop 10 genes with high betweenness centrality:\n")

Top 10 genes with high betweenness centrality:
print(high_betweenness_genes)
 [1] "BMP4"   "IL6"    "SNCA"   "IFNG"   "NKX2-5" "SIX1"   "WNT11"  "IL1A"   "HMGA2"  "SOX4"  
# Calculate and print some network statistics
cat("\nNetwork Statistics:\n")

Network Statistics:
cat("Number of nodes:", igraph::vcount(g), "\n")
Number of nodes: 1572 
cat("Number of edges:", igraph::ecount(g), "\n")
Number of edges: 2309 
cat("Network density:", igraph::edge_density(g), "\n")
Network density: 0.001869929 
cat("Average path length:", igraph::mean_distance(g), "\n")
Average path length: 4.994568 
cat("Clustering coefficient:", igraph::transitivity(g), "\n")
Clustering coefficient: 0 
# Extract edge information
edges_df <- igraph::as_data_frame(g, what = "edges")

# Save the edges data frame
write.csv(edges_df, "gene_go_edges.csv", row.names = FALSE)

# Extract node information
nodes_df <- igraph::as_data_frame(g, what = "vertices")

# Save the nodes data frame
write.csv(nodes_df, "gene_go_nodes.csv", row.names = FALSE)

# Print a summary of the saved data
cat("Saved network data:\n")
Saved network data:
cat("Edges file: gene_go_edges.csv -", nrow(edges_df), "rows\n")
Edges file: gene_go_edges.csv - 2309 rows
cat("Nodes file: gene_go_nodes.csv -", nrow(nodes_df), "rows\n")
Nodes file: gene_go_nodes.csv - 1572 rows
# Print top GO terms
top_go_terms <- V(g)$name[!(V(g)$name %in% all_top_genes)][order(V(g)$degree[!(V(g)$name %in% all_top_genes)], decreasing = TRUE)][1:20]
cat("\nTop 20 GO terms:\n")

Top 20 GO terms:
for (term in top_go_terms) {
  cat(term, "-", V(g)$description[V(g)$name == term], "\n")
}
GO:0045944 - positive regulation of transcription by RNA polymerase II 
GO:0000122 - negative regulation of transcription by RNA polymerase II 
GO:0007165 - signal transduction 
GO:0006357 - regulation of transcription by RNA polymerase II 
GO:0045893 - positive regulation of DNA-templated transcription 
GO:0045892 - negative regulation of DNA-templated transcription 
GO:0010628 - positive regulation of gene expression 
GO:0007155 - cell adhesion 
GO:0006955 - immune response 
GO:0007186 - G protein-coupled receptor signaling pathway 
GO:0030154 - cell differentiation 
GO:0006954 - inflammatory response 
GO:0008284 - positive regulation of cell population proliferation 
GO:0006915 - apoptotic process 
GO:0045087 - innate immune response 
GO:0043066 - negative regulation of apoptotic process 
GO:0002250 - adaptive immune response 
GO:0090090 - negative regulation of canonical Wnt signaling pathway 
GO:0070374 - positive regulation of ERK1 and ERK2 cascade 
GO:0098609 - cell-cell adhesion 

10. Save the Seurat object as an Robj file



#save(All_samples_Merged, file = "All_samples_Merged_DE.Robj")
LS0tCnRpdGxlOiAiU8OpemFyeSBTeW5kcm9tZSBDZWxsIExpbmUgQW5hbHlzaXMiCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgIyBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICAjIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKICAjIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICAjcm1kZm9ybWF0czo6cmVhZHRoZWRvd24KICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19jb2xsYXBzZWQ6IHRydWUKLS0tCgojIDEuIGxvYWQgbGlicmFyaWVzCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQoKCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KGVucmljaHBsb3QpCgoKCmBgYAojRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQW5hbHlzaXMKCiMgMi4gbG9hZCBzZXVyYXQgb2JqZWN0CmBgYHtyIGxvYWRfc2V1cmF0fQojTG9hZCBTZXVyYXQgT2JqZWN0IEw3CmxvYWQoIi4uLy4uLzAtSU1QLU9CSkVDVFMvQWxsX0NENF9UY2VsbHNfTWVyZ2VkXzEtMTNfcmVzLTAuOS5Sb2JqIikKCgpBbGxfc2FtcGxlc19NZXJnZWQKCmBgYAoKI0RpZmZlcmVudGlhbCBFeHByZXNzaW9uIEFuYWx5c2lzCgojIDMuIEZpbmQgTWFya2VycyBmb3IgRWFjaCBDZWxsIExpbmUKYGBge3IgZmluZG1hcmtlcnMxLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCkRlZmF1bHRBc3NheShBbGxfc2FtcGxlc19NZXJnZWQpIDwtICJTQ1QiCklkZW50cyhBbGxfc2FtcGxlc19NZXJnZWQpIDwtICJjZWxsX2xpbmUiCmFsbF9tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKEFsbF9zYW1wbGVzX01lcmdlZCwgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4yNSwgbG9nZmMudGhyZXNob2xkID0gMC4yNSkKd3JpdGUuY3N2KGFsbF9tYXJrZXJzLCAiYWxsX21hcmtlcnMuY3N2IikKaGVhZChhbGxfbWFya2VycykKCgpgYGAKCiMgNC4gSGVhdG1hcCBvZiBUb3AgMTAgVXByZWd1bGF0ZWQgR2VuZXMgKFNvcnRlZCBieSBhdmdfbG9nMkZDKQpgYGB7ciB0b3AxMF9IZWF0bWFwLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCnRvcDEwX21hcmtlcnMgPC0gYWxsX21hcmtlcnMgJT4lCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgdG9wX24obiA9IDEwLCB3dCA9IGF2Z19sb2cyRkMpICU+JQogIGFycmFuZ2UoY2x1c3RlciwgZGVzYyhhdmdfbG9nMkZDKSkKCnRvcDEwX2dlbmVzIDwtIHVuaXF1ZSh0b3AxMF9tYXJrZXJzJGdlbmUpCgpoZWF0bWFwX2RhdGEgPC0gR2V0QXNzYXlEYXRhKEFsbF9zYW1wbGVzX01lcmdlZCwgYXNzYXkgPSAiU0NUIiwgc2xvdCA9ICJkYXRhIilbdG9wMTBfZ2VuZXMsIF0KaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAtIHJvd01lYW5zKGhlYXRtYXBfZGF0YSkKCmNlbGxfbGluZV9jb2xvcnMgPC0gcmFpbmJvdyhsZW5ndGgodW5pcXVlKEFsbF9zYW1wbGVzX01lcmdlZCRjZWxsX2xpbmUpKSkKbmFtZXMoY2VsbF9saW5lX2NvbG9ycykgPC0gdW5pcXVlKEFsbF9zYW1wbGVzX01lcmdlZCRjZWxsX2xpbmUpCmFubm90YXRpb25fY29sb3JzIDwtIGxpc3QoY2VsbF9saW5lID0gY2VsbF9saW5lX2NvbG9ycykKCnAgPC0gcGhlYXRtYXAoaGVhdG1hcF9kYXRhLAogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwKICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEZBTFNFLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IEFsbF9zYW1wbGVzX01lcmdlZEBtZXRhLmRhdGFbLCAiY2VsbF9saW5lIiwgZHJvcCA9IEZBTFNFXSwKICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdGF0aW9uX2NvbG9ycywKICAgICAgICAgbWFpbiA9ICJUb3AgMTAgVXByZWd1bGF0ZWQgR2VuZXMgcGVyIENlbGwgTGluZSAoU29ydGVkIGJ5IGF2Z19sb2cyRkMpIiwKICAgICAgICAgZm9udHNpemVfcm93ID0gNiwKICAgICAgICAgdHJlZWhlaWdodF9jb2wgPSAwLAogICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRkFMU0UpCgpwcmludChwKQpwbmcoImhlYXRtYXBfdG9wMTBfbG9nMmZjX3NvcnRlZC5wbmciLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4LCB1bml0cyA9ICJpbiIsIHJlcyA9IDMwMCkKcHJpbnQocCkKZGV2Lm9mZigpCgpgYGAKCiMgNS4gSGVhdG1hcCBvZiBUb3AgMTAgTWFya2VycyAoU2V1cmF0IERlZmF1bHQpCmBgYHtyIGhlYXRtYXAyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCnRvcDEwX21hcmtlcnNfc2V1cmF0IDwtIGFsbF9tYXJrZXJzICU+JQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQogIHRvcF9uKG4gPSAxMCwgd3QgPSBhdmdfbG9nMkZDKQoKdG9wMTBfZ2VuZXNfc2V1cmF0IDwtIHVuaXF1ZSh0b3AxMF9tYXJrZXJzX3NldXJhdCRnZW5lKQoKaGVhdG1hcF9kYXRhX3NldXJhdCA8LSBHZXRBc3NheURhdGEoQWxsX3NhbXBsZXNfTWVyZ2VkLCBhc3NheSA9ICJTQ1QiLCBzbG90ID0gImRhdGEiKVt0b3AxMF9nZW5lc19zZXVyYXQsIF0KaGVhdG1hcF9kYXRhX3NldXJhdCA8LSBoZWF0bWFwX2RhdGFfc2V1cmF0IC0gcm93TWVhbnMoaGVhdG1hcF9kYXRhX3NldXJhdCkKCnAyIDwtIHBoZWF0bWFwKGhlYXRtYXBfZGF0YV9zZXVyYXQsCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLAogICAgICAgICBzaG93X2NvbG5hbWVzID0gRkFMU0UsCiAgICAgICAgIGFubm90YXRpb25fY29sID0gQWxsX3NhbXBsZXNfTWVyZ2VkQG1ldGEuZGF0YVssICJjZWxsX2xpbmUiLCBkcm9wID0gRkFMU0VdLAogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90YXRpb25fY29sb3JzLAogICAgICAgICBtYWluID0gIlRvcCAxMCBNYXJrZXJzIHBlciBDZWxsIExpbmUgKFNldXJhdCBEZWZhdWx0KSIsCiAgICAgICAgIGZvbnRzaXplX3JvdyA9IDYsCiAgICAgICAgIHRyZWVoZWlnaHRfY29sID0gMCkKCnByaW50KHAyKQpwbmcoImhlYXRtYXBfdG9wMTBfc2V1cmF0LnBuZyIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDgsIHVuaXRzID0gImluIiwgcmVzID0gMzAwKQpwcmludChwMikKZGV2Lm9mZigpCgpgYGAKCiMgNi4gUGFpcndpc2UgQ29tcGFyaXNvbnMKYGBge3IgcGFpcndpc2VDb21wLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KbGlicmFyeShFbmhhbmNlZFZvbGNhbm8pCgpwZXJmb3JtX2NvbXBhcmlzb25fYW5kX3ZvbGNhbm8gPC0gZnVuY3Rpb24oQWxsX3NhbXBsZXNfTWVyZ2VkLCBpZGVudDEsIGlkZW50MikgewogIElkZW50cyhBbGxfc2FtcGxlc19NZXJnZWQpIDwtICJjZWxsX2xpbmUiCiAgbWFya2VycyA8LSBGaW5kTWFya2VycyhBbGxfc2FtcGxlc19NZXJnZWQsIGlkZW50LjEgPSBpZGVudDEsIGlkZW50LjIgPSBpZGVudDIsIGFzc2F5ID0gIlNDVCIpCiAgd3JpdGUuY3N2KG1hcmtlcnMsIHBhc3RlMCgiY29tcGFyaXNvbl8iLCBpZGVudDEsICJfdnNfIiwgaWRlbnQyLCAiLmNzdiIpKQogIAogICMgQ3JlYXRlIHZvbGNhbm8gcGxvdAogIHZvbGNhbm9fcGxvdCA8LSBFbmhhbmNlZFZvbGNhbm8obWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYiA9IHJvd25hbWVzKG1hcmtlcnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICdhdmdfbG9nMkZDJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAncF92YWxfYWRqJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gcGFzdGUoaWRlbnQxLCAndnMnLCBpZGVudDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcEN1dG9mZiA9IDAuMDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGQ2N1dG9mZiA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb2ludFNpemUgPSAxLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJTaXplID0gNC4wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gYygnZ3JleScsICdkYXJrZ3JlZW4nLCAnYmx1ZScsICdyZWQnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbEFscGhhID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kTGFiU2l6ZSA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kSWNvblNpemUgPSA0LjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcmF3Q29ubmVjdG9ycyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aENvbm5lY3RvcnMgPSAwLjUpCiAgCiAgcHJpbnQodm9sY2Fub19wbG90KQogIHBuZyhwYXN0ZTAoInZvbGNhbm9fIiwgaWRlbnQxLCAiX3ZzXyIsIGlkZW50MiwgIi5wbmciKSwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTAsIHVuaXRzID0gImluIiwgcmVzID0gMzAwKQogIHByaW50KHZvbGNhbm9fcGxvdCkKICBkZXYub2ZmKCkKICAKICByZXR1cm4obWFya2VycykKfQoKIyBQYXRpZW50IDEKcDFfY29tcGFyaXNvbiA8LSBwZXJmb3JtX2NvbXBhcmlzb25fYW5kX3ZvbGNhbm8oQWxsX3NhbXBsZXNfTWVyZ2VkLCAiTDEiLCAiTDIiKQpoZWFkKHAxX2NvbXBhcmlzb24pCgojIFBhdGllbnQgMgpwMl9jb21wYXJpc29uIDwtIHBlcmZvcm1fY29tcGFyaXNvbl9hbmRfdm9sY2FubyhBbGxfc2FtcGxlc19NZXJnZWQsICJMMyIsICJMNCIpCmhlYWQocDJfY29tcGFyaXNvbikKCiMgUGF0aWVudCAzCnAzX2NvbXBhcmlzb25fTDVMNiA8LSBwZXJmb3JtX2NvbXBhcmlzb25fYW5kX3ZvbGNhbm8oQWxsX3NhbXBsZXNfTWVyZ2VkLCAiTDUiLCAiTDYiKQpwM19jb21wYXJpc29uX0w1TDcgPC0gcGVyZm9ybV9jb21wYXJpc29uX2FuZF92b2xjYW5vKEFsbF9zYW1wbGVzX01lcmdlZCwgIkw1IiwgIkw3IikKcDNfY29tcGFyaXNvbl9MNkw3IDwtIHBlcmZvcm1fY29tcGFyaXNvbl9hbmRfdm9sY2FubyhBbGxfc2FtcGxlc19NZXJnZWQsICJMNiIsICJMNyIpCmhlYWQocDNfY29tcGFyaXNvbl9MNUw2KQpoZWFkKHAzX2NvbXBhcmlzb25fTDVMNykKaGVhZChwM19jb21wYXJpc29uX0w2TDcpCgoKYGBgCgoKIyA3LiBFbnJpY2htZW50IEFuYWx5c2lzCmBgYHtyIGVucmljaG1lbnQsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEyfQoKcGVyZm9ybV9nb19lbnJpY2htZW50IDwtIGZ1bmN0aW9uKGdlbmVfbGlzdCwgZ2VuZV91bml2ZXJzZSwgdGl0bGUpIHsKICBlZ28gPC0gZW5yaWNoR08oZ2VuZSA9IGdlbmVfbGlzdCwKICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSBnZW5lX3VuaXZlcnNlLAogICAgICAgICAgICAgICAgICBPcmdEYiA9IG9yZy5Icy5lZy5kYiwKICAgICAgICAgICAgICAgICAga2V5VHlwZSA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICBvbnQgPSAiQlAiLAogICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwKICAgICAgICAgICAgICAgICAgcXZhbHVlQ3V0b2ZmID0gMC4wNSwKICAgICAgICAgICAgICAgICAgcmVhZGFibGUgPSBUUlVFKQogIAogIHAgPC0gZG90cGxvdChlZ28sIHNob3dDYXRlZ29yeSA9IDIwLCB0aXRsZSA9IHBhc3RlKCJHTyAtIiwgdGl0bGUpKSArCiAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpCiAgCiAgcHJpbnQocCkKICBwbmcocGFzdGUwKCJHT19lbnJpY2htZW50XyIsIGdzdWIoIiAiLCAiXyIsIHRpdGxlKSwgIi5wbmciKSwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gOCwgdW5pdHMgPSAiaW4iLCByZXMgPSAzMDApCiAgcHJpbnQocCkKICBkZXYub2ZmKCkKICAKICByZXR1cm4oZWdvKQp9CgpwZXJmb3JtX2tlZ2dfZW5yaWNobWVudCA8LSBmdW5jdGlvbihnZW5lX2xpc3QsIGdlbmVfdW5pdmVyc2UsIHRpdGxlKSB7CiAgIyBDb252ZXJ0IGdlbmUgc3ltYm9scyB0byBFbnRyZXogSURzCiAgZW50cmV6X2lkcyA8LSBiaXRyKGdlbmVfbGlzdCwgZnJvbVR5cGUgPSAiU1lNQk9MIiwgdG9UeXBlID0gIkVOVFJFWklEIiwgT3JnRGIgPSBvcmcuSHMuZWcuZGIpJEVOVFJFWklECiAgdW5pdmVyc2VfZW50cmV6IDwtIGJpdHIoZ2VuZV91bml2ZXJzZSwgZnJvbVR5cGUgPSAiU1lNQk9MIiwgdG9UeXBlID0gIkVOVFJFWklEIiwgT3JnRGIgPSBvcmcuSHMuZWcuZGIpJEVOVFJFWklECiAgCiAgZWtlZ2cgPC0gZW5yaWNoS0VHRyhnZW5lID0gZW50cmV6X2lkcywKICAgICAgICAgICAgICAgICAgICAgIHVuaXZlcnNlID0gdW5pdmVyc2VfZW50cmV6LAogICAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAnaHNhJywKICAgICAgICAgICAgICAgICAgICAgIGtleVR5cGUgPSAia2VnZyIsCiAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1LAogICAgICAgICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZCA9ICJCSCIpCiAgCiAgcCA8LSBkb3RwbG90KGVrZWdnLCBzaG93Q2F0ZWdvcnkgPSAyMCwgdGl0bGUgPSBwYXN0ZSgiS0VHRyAtIiwgdGl0bGUpKSArCiAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpCiAgCiAgcHJpbnQocCkKICBwbmcocGFzdGUwKCJLRUdHX2VucmljaG1lbnRfIiwgZ3N1YigiICIsICJfIiwgdGl0bGUpLCAiLnBuZyIpLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4LCB1bml0cyA9ICJpbiIsIHJlcyA9IDMwMCkKICBwcmludChwKQogIGRldi5vZmYoKQogIAogIHJldHVybihla2VnZykKfQoKZ2VuZV91bml2ZXJzZSA8LSByb3duYW1lcyhBbGxfc2FtcGxlc19NZXJnZWQpCgojIFBhdGllbnQgMSAoUDEpIGNvbXBhcmlzb246IEwxIHZzIEwyCnVwcmVndWxhdGVkX2dlbmVzX1AxIDwtIHJvd25hbWVzKHAxX2NvbXBhcmlzb25bcDFfY29tcGFyaXNvbiRhdmdfbG9nMkZDID4gMSAmIHAxX2NvbXBhcmlzb24kcF92YWxfYWRqIDwgMC4wNSwgXSkKZG93bnJlZ3VsYXRlZF9nZW5lc19QMSA8LSByb3duYW1lcyhwMV9jb21wYXJpc29uW3AxX2NvbXBhcmlzb24kYXZnX2xvZzJGQyA8IC0xICYgcDFfY29tcGFyaXNvbiRwX3ZhbF9hZGogPCAwLjA1LCBdKQoKZ29fdXBfUDEgPC0gcGVyZm9ybV9nb19lbnJpY2htZW50KHVwcmVndWxhdGVkX2dlbmVzX1AxLCBnZW5lX3VuaXZlcnNlLCAiVXByZWd1bGF0ZWQgR2VuZXMgaW4gTDEgdnMgTDIiKQpnb19kb3duX1AxIDwtIHBlcmZvcm1fZ29fZW5yaWNobWVudChkb3ducmVndWxhdGVkX2dlbmVzX1AxLCBnZW5lX3VuaXZlcnNlLCAiRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBMMSB2cyBMMiIpCmtlZ2dfdXBfUDEgPC0gcGVyZm9ybV9rZWdnX2VucmljaG1lbnQodXByZWd1bGF0ZWRfZ2VuZXNfUDEsIGdlbmVfdW5pdmVyc2UsICJVcHJlZ3VsYXRlZCBHZW5lcyBpbiBMMSB2cyBMMiIpCmtlZ2dfZG93bl9QMSA8LSBwZXJmb3JtX2tlZ2dfZW5yaWNobWVudChkb3ducmVndWxhdGVkX2dlbmVzX1AxLCBnZW5lX3VuaXZlcnNlLCAiRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBMMSB2cyBMMiIpCgojIFBhdGllbnQgMiAoUDIpIGNvbXBhcmlzb246IEwzIHZzIEw0CnVwcmVndWxhdGVkX2dlbmVzX1AyIDwtIHJvd25hbWVzKHAyX2NvbXBhcmlzb25bcDJfY29tcGFyaXNvbiRhdmdfbG9nMkZDID4gMSAmIHAyX2NvbXBhcmlzb24kcF92YWxfYWRqIDwgMC4wNSwgXSkKZG93bnJlZ3VsYXRlZF9nZW5lc19QMiA8LSByb3duYW1lcyhwMl9jb21wYXJpc29uW3AyX2NvbXBhcmlzb24kYXZnX2xvZzJGQyA8IC0xICYgcDJfY29tcGFyaXNvbiRwX3ZhbF9hZGogPCAwLjA1LCBdKQoKZ29fdXBfUDIgPC0gcGVyZm9ybV9nb19lbnJpY2htZW50KHVwcmVndWxhdGVkX2dlbmVzX1AyLCBnZW5lX3VuaXZlcnNlLCAiVXByZWd1bGF0ZWQgR2VuZXMgaW4gTDMgdnMgTDQiKQpnb19kb3duX1AyIDwtIHBlcmZvcm1fZ29fZW5yaWNobWVudChkb3ducmVndWxhdGVkX2dlbmVzX1AyLCBnZW5lX3VuaXZlcnNlLCAiRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBMMyB2cyBMNCIpCmtlZ2dfdXBfUDIgPC0gcGVyZm9ybV9rZWdnX2VucmljaG1lbnQodXByZWd1bGF0ZWRfZ2VuZXNfUDIsIGdlbmVfdW5pdmVyc2UsICJVcHJlZ3VsYXRlZCBHZW5lcyBpbiBMMyB2cyBMNCIpCmtlZ2dfZG93bl9QMiA8LSBwZXJmb3JtX2tlZ2dfZW5yaWNobWVudChkb3ducmVndWxhdGVkX2dlbmVzX1AyLCBnZW5lX3VuaXZlcnNlLCAiRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBMMyB2cyBMNCIpCgojIFBhdGllbnQgMyAoUDMpIGNvbXBhcmlzb25zCiMgTDUgdnMgTDYKdXByZWd1bGF0ZWRfZ2VuZXNfUDNfTDVMNiA8LSByb3duYW1lcyhwM19jb21wYXJpc29uX0w1TDZbcDNfY29tcGFyaXNvbl9MNUw2JGF2Z19sb2cyRkMgPiAxICYgcDNfY29tcGFyaXNvbl9MNUw2JHBfdmFsX2FkaiA8IDAuMDUsIF0pCmRvd25yZWd1bGF0ZWRfZ2VuZXNfUDNfTDVMNiA8LSByb3duYW1lcyhwM19jb21wYXJpc29uX0w1TDZbcDNfY29tcGFyaXNvbl9MNUw2JGF2Z19sb2cyRkMgPCAtMSAmIHAzX2NvbXBhcmlzb25fTDVMNiRwX3ZhbF9hZGogPCAwLjA1LCBdKQoKZ29fdXBfUDNfTDVMNiA8LSBwZXJmb3JtX2dvX2VucmljaG1lbnQodXByZWd1bGF0ZWRfZ2VuZXNfUDNfTDVMNiwgZ2VuZV91bml2ZXJzZSwgIlVwcmVndWxhdGVkIEdlbmVzIGluIEw1IHZzIEw2IikKZ29fZG93bl9QM19MNUw2IDwtIHBlcmZvcm1fZ29fZW5yaWNobWVudChkb3ducmVndWxhdGVkX2dlbmVzX1AzX0w1TDYsIGdlbmVfdW5pdmVyc2UsICJEb3ducmVndWxhdGVkIEdlbmVzIGluIEw1IHZzIEw2IikKa2VnZ191cF9QM19MNUw2IDwtIHBlcmZvcm1fa2VnZ19lbnJpY2htZW50KHVwcmVndWxhdGVkX2dlbmVzX1AzX0w1TDYsIGdlbmVfdW5pdmVyc2UsICJVcHJlZ3VsYXRlZCBHZW5lcyBpbiBMNSB2cyBMNiIpCmtlZ2dfZG93bl9QM19MNUw2IDwtIHBlcmZvcm1fa2VnZ19lbnJpY2htZW50KGRvd25yZWd1bGF0ZWRfZ2VuZXNfUDNfTDVMNiwgZ2VuZV91bml2ZXJzZSwgIkRvd25yZWd1bGF0ZWQgR2VuZXMgaW4gTDUgdnMgTDYiKQoKIyBMNSB2cyBMNwp1cHJlZ3VsYXRlZF9nZW5lc19QM19MNUw3IDwtIHJvd25hbWVzKHAzX2NvbXBhcmlzb25fTDVMN1twM19jb21wYXJpc29uX0w1TDckYXZnX2xvZzJGQyA+IDEgJiBwM19jb21wYXJpc29uX0w1TDckcF92YWxfYWRqIDwgMC4wNSwgXSkKZG93bnJlZ3VsYXRlZF9nZW5lc19QM19MNUw3IDwtIHJvd25hbWVzKHAzX2NvbXBhcmlzb25fTDVMN1twM19jb21wYXJpc29uX0w1TDckYXZnX2xvZzJGQyA8IC0xICYgcDNfY29tcGFyaXNvbl9MNUw3JHBfdmFsX2FkaiA8IDAuMDUsIF0pCgpnb191cF9QM19MNUw3IDwtIHBlcmZvcm1fZ29fZW5yaWNobWVudCh1cHJlZ3VsYXRlZF9nZW5lc19QM19MNUw3LCBnZW5lX3VuaXZlcnNlLCAiVXByZWd1bGF0ZWQgR2VuZXMgaW4gTDUgdnMgTDciKQpnb19kb3duX1AzX0w1TDcgPC0gcGVyZm9ybV9nb19lbnJpY2htZW50KGRvd25yZWd1bGF0ZWRfZ2VuZXNfUDNfTDVMNywgZ2VuZV91bml2ZXJzZSwgIkRvd25yZWd1bGF0ZWQgR2VuZXMgaW4gTDUgdnMgTDciKQprZWdnX3VwX1AzX0w1TDcgPC0gcGVyZm9ybV9rZWdnX2VucmljaG1lbnQodXByZWd1bGF0ZWRfZ2VuZXNfUDNfTDVMNywgZ2VuZV91bml2ZXJzZSwgIlVwcmVndWxhdGVkIEdlbmVzIGluIEw1IHZzIEw3IikKa2VnZ19kb3duX1AzX0w1TDcgPC0gcGVyZm9ybV9rZWdnX2VucmljaG1lbnQoZG93bnJlZ3VsYXRlZF9nZW5lc19QM19MNUw3LCBnZW5lX3VuaXZlcnNlLCAiRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBMNSB2cyBMNyIpCgojIEw2IHZzIEw3CnVwcmVndWxhdGVkX2dlbmVzX1AzX0w2TDcgPC0gcm93bmFtZXMocDNfY29tcGFyaXNvbl9MNkw3W3AzX2NvbXBhcmlzb25fTDZMNyRhdmdfbG9nMkZDID4gMSAmIHAzX2NvbXBhcmlzb25fTDZMNyRwX3ZhbF9hZGogPCAwLjA1LCBdKQpkb3ducmVndWxhdGVkX2dlbmVzX1AzX0w2TDcgPC0gcm93bmFtZXMocDNfY29tcGFyaXNvbl9MNkw3W3AzX2NvbXBhcmlzb25fTDZMNyRhdmdfbG9nMkZDIDwgLTEgJiBwM19jb21wYXJpc29uX0w2TDckcF92YWxfYWRqIDwgMC4wNSwgXSkKCmdvX3VwX1AzX0w2TDcgPC0gcGVyZm9ybV9nb19lbnJpY2htZW50KHVwcmVndWxhdGVkX2dlbmVzX1AzX0w2TDcsIGdlbmVfdW5pdmVyc2UsICJVcHJlZ3VsYXRlZCBHZW5lcyBpbiBMNiB2cyBMNyIpCmdvX2Rvd25fUDNfTDZMNyA8LSBwZXJmb3JtX2dvX2VucmljaG1lbnQoZG93bnJlZ3VsYXRlZF9nZW5lc19QM19MNkw3LCBnZW5lX3VuaXZlcnNlLCAiRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBMNiB2cyBMNyIpCmtlZ2dfdXBfUDNfTDZMNyA8LSBwZXJmb3JtX2tlZ2dfZW5yaWNobWVudCh1cHJlZ3VsYXRlZF9nZW5lc19QM19MNkw3LCBnZW5lX3VuaXZlcnNlLCAiVXByZWd1bGF0ZWQgR2VuZXMgaW4gTDYgdnMgTDciKQprZWdnX2Rvd25fUDNfTDZMNyA8LSBwZXJmb3JtX2tlZ2dfZW5yaWNobWVudChkb3ducmVndWxhdGVkX2dlbmVzX1AzX0w2TDcsIGdlbmVfdW5pdmVyc2UsICJEb3ducmVndWxhdGVkIEdlbmVzIGluIEw2IHZzIEw3IikKCmBgYAoKIyA4LiBOZXR3b3JrIEFuYWx5c2lzCmBgYHtyIG5ldHdvcmtfYW5hbHlzaXMsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMn0KIyBGdW5jdGlvbiB0byBnZXQgdG9wIGdlbmVzIGZyb20gYSBjb21wYXJpc29uCgpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShTVFJJTkdkYikKbGlicmFyeShnZ3JhcGgpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRpYmJsZSkKCmdldF90b3BfZ2VuZXMgPC0gZnVuY3Rpb24oY29tcGFyaXNvbl9yZXN1bHQsIG4gPSA1MCkgewogIHRvcF9nZW5lcyA8LSBjb21wYXJpc29uX3Jlc3VsdCAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZSIpICU+JQogICAgYXJyYW5nZShkZXNjKGFicyhhdmdfbG9nMkZDKSkpICU+JQogICAgaGVhZChuKSAlPiUKICAgIHB1bGwoZ2VuZSkKICByZXR1cm4odG9wX2dlbmVzKQp9CgojIENvbWJpbmUgdG9wIGdlbmVzIGZyb20gYWxsIGNvbXBhcmlzb25zCmFsbF90b3BfZ2VuZXMgPC0gdW5pcXVlKGMoCiAgZ2V0X3RvcF9nZW5lcyhwMV9jb21wYXJpc29uKSwKICBnZXRfdG9wX2dlbmVzKHAyX2NvbXBhcmlzb24pLAogIGdldF90b3BfZ2VuZXMocDNfY29tcGFyaXNvbl9MNUw2KSwKICBnZXRfdG9wX2dlbmVzKHAzX2NvbXBhcmlzb25fTDVMNyksCiAgZ2V0X3RvcF9nZW5lcyhwM19jb21wYXJpc29uX0w2TDcpCikpCgojIEluaXRpYWxpemUgU1RSSU5HZGIKc3RyaW5nX2RiIDwtIFNUUklOR2RiJG5ldyh2ZXJzaW9uPSIxMSIsIHNwZWNpZXM9OTYwNiwgc2NvcmVfdGhyZXNob2xkPTcwMCkKCiMgTWFwIGdlbmVzIHRvIFNUUklORyBpZGVudGlmaWVycwptYXBwZWRfZ2VuZXMgPC0gc3RyaW5nX2RiJG1hcChkYXRhLmZyYW1lKGdlbmU9YWxsX3RvcF9nZW5lcyksICJnZW5lIiwgcmVtb3ZlVW5tYXBwZWRSb3dzID0gVFJVRSkKCiMgR2V0IGludGVyYWN0aW9ucwppbnRlcmFjdGlvbnMgPC0gc3RyaW5nX2RiJGdldF9pbnRlcmFjdGlvbnMobWFwcGVkX2dlbmVzJFNUUklOR19pZCkKCiMgQ3JlYXRlIGlncmFwaCBvYmplY3QKZyA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoaW50ZXJhY3Rpb25zLCBkaXJlY3RlZCA9IEZBTFNFKQoKIyBDYWxjdWxhdGUgbm9kZSBkZWdyZWVzClYoZykkZGVncmVlIDwtIGRlZ3JlZShnKQoKIyBDYWxjdWxhdGUgYmV0d2Vlbm5lc3MgY2VudHJhbGl0eQpWKGcpJGJldHdlZW5uZXNzIDwtIGJldHdlZW5uZXNzKGcpCgojIElkZW50aWZ5IGNvbW11bml0aWVzCmNvbW11bml0aWVzIDwtIGNsdXN0ZXJfbG91dmFpbihnKQpWKGcpJGNvbW11bml0eSA8LSBjb21tdW5pdGllcyRtZW1iZXJzaGlwCgojIFBsb3QgdGhlIG5ldHdvcmsKc2V0LnNlZWQoMTIzKSAgIyBmb3IgcmVwcm9kdWNpYmlsaXR5CmdncmFwaChnLCBsYXlvdXQgPSAiZnIiKSArCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBjb21iaW5lZF9zY29yZSksIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gZmFjdG9yKGNvbW11bml0eSksIHNpemUgPSBkZWdyZWUpKSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsID0gVFJVRSwgc2l6ZSA9IDMpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgbGFicyh0aXRsZSA9ICJHZW5lIEludGVyYWN0aW9uIE5ldHdvcmsiLAogICAgICAgc3VidGl0bGUgPSAiQmFzZWQgb24gdG9wIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyIsCiAgICAgICBjb2xvciA9ICJDb21tdW5pdHkiLAogICAgICAgc2l6ZSA9ICJEZWdyZWUiKQoKIyBTYXZlIHRoZSBwbG90Cmdnc2F2ZSgiZ2VuZV9pbnRlcmFjdGlvbl9uZXR3b3JrLnBuZyIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCgojIElkZW50aWZ5IGh1YiBnZW5lcwpodWJfZ2VuZXMgPC0gVihnKSRuYW1lW29yZGVyKFYoZykkZGVncmVlLCBkZWNyZWFzaW5nID0gVFJVRSldWzE6MTBdCmNhdCgiVG9wIDEwIGh1YiBnZW5lczpcbiIpCnByaW50KGh1Yl9nZW5lcykKCiMgSWRlbnRpZnkgZ2VuZXMgd2l0aCBoaWdoIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkKaGlnaF9iZXR3ZWVubmVzc19nZW5lcyA8LSBWKGcpJG5hbWVbb3JkZXIoVihnKSRiZXR3ZWVubmVzcywgZGVjcmVhc2luZyA9IFRSVUUpXVsxOjEwXQpjYXQoIlxuVG9wIDEwIGdlbmVzIHdpdGggaGlnaCBiZXR3ZWVubmVzcyBjZW50cmFsaXR5OlxuIikKcHJpbnQoaGlnaF9iZXR3ZWVubmVzc19nZW5lcykKCiMgQ2FsY3VsYXRlIGFuZCBwcmludCBzb21lIG5ldHdvcmsgc3RhdGlzdGljcwpjYXQoIlxuTmV0d29yayBTdGF0aXN0aWNzOlxuIikKY2F0KCJOdW1iZXIgb2Ygbm9kZXM6IiwgdmNvdW50KGcpLCAiXG4iKQpjYXQoIk51bWJlciBvZiBlZGdlczoiLCBlY291bnQoZyksICJcbiIpCmNhdCgiTmV0d29yayBkZW5zaXR5OiIsIGVkZ2VfZGVuc2l0eShnKSwgIlxuIikKY2F0KCJBdmVyYWdlIHBhdGggbGVuZ3RoOiIsIG1lYW5fZGlzdGFuY2UoZyksICJcbiIpCmNhdCgiQ2x1c3RlcmluZyBjb2VmZmljaWVudDoiLCB0cmFuc2l0aXZpdHkoZyksICJcbiIpCgojIEV4dHJhY3QgZWRnZSBpbmZvcm1hdGlvbgplZGdlc19kZiA8LSBkYXRhLmZyYW1lKAogIGZyb20gPSBlbmRzKGcsIEUoZykpWywxXSwKICB0byA9IGVuZHMoZywgRShnKSlbLDJdCikKCiMgQWRkIGVkZ2UgYXR0cmlidXRlcyBpZiBhbnkKZWRnZV9hdHRycyA8LSBlZGdlX2F0dHIoZykKZm9yIChhdHRyIGluIG5hbWVzKGVkZ2VfYXR0cnMpKSB7CiAgZWRnZXNfZGZbW2F0dHJdXSA8LSBlZGdlX2F0dHJzW1thdHRyXV0KfQoKIyBTYXZlIHRoZSBlZGdlcyBkYXRhIGZyYW1lCndyaXRlLmNzdihlZGdlc19kZiwgImdlbmVfbmV0d29ya19lZGdlcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgRXh0cmFjdCBub2RlIGluZm9ybWF0aW9uCm5vZGVzX2RmIDwtIGRhdGEuZnJhbWUoCiAgaWQgPSBWKGcpJG5hbWUsCiAgZGVncmVlID0gZGVncmVlKGcpLAogIGJldHdlZW5uZXNzID0gYmV0d2Vlbm5lc3MoZykKKQoKIyBBZGQgb3RoZXIgdmVydGV4IGF0dHJpYnV0ZXMgaWYgYW55CnZlcnRleF9hdHRycyA8LSB2ZXJ0ZXhfYXR0cihnKQpmb3IgKGF0dHIgaW4gbmFtZXModmVydGV4X2F0dHJzKSkgewogIGlmIChhdHRyICE9ICJuYW1lIikgeyAgIyBTa2lwICduYW1lJyBhcyB3ZSBhbHJlYWR5IGhhdmUgaXQgYXMgJ2lkJwogICAgbm9kZXNfZGZbW2F0dHJdXSA8LSB2ZXJ0ZXhfYXR0cnNbW2F0dHJdXQogIH0KfQoKIyBTYXZlIHRoZSBub2RlcyBkYXRhIGZyYW1lCndyaXRlLmNzdihub2Rlc19kZiwgImdlbmVfbmV0d29ya19ub2Rlcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgUHJpbnQgYSBzdW1tYXJ5IG9mIHRoZSBzYXZlZCBkYXRhCmNhdCgiU2F2ZWQgbmV0d29yayBkYXRhOlxuIikKY2F0KCJFZGdlcyBmaWxlOiBnZW5lX25ldHdvcmtfZWRnZXMuY3N2IC0iLCBucm93KGVkZ2VzX2RmKSwgInJvd3NcbiIpCmNhdCgiTm9kZXMgZmlsZTogZ2VuZV9uZXR3b3JrX25vZGVzLmNzdiAtIiwgbnJvdyhub2Rlc19kZiksICJyb3dzXG4iKQpgYGAKCmBgYHtyIG5ldHdvcmtfYW5hbHlzaXMyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTJ9CiMgRnVuY3Rpb24gdG8gZ2V0IHRvcCBnZW5lcyBmcm9tIGEgY29tcGFyaXNvbgoKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkoU1RSSU5HZGIpCmxpYnJhcnkoZ2dyYXBoKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWJibGUpCgpnZXRfdG9wX2dlbmVzIDwtIGZ1bmN0aW9uKGNvbXBhcmlzb25fcmVzdWx0LCBuID0gNTApIHsKICB0b3BfZ2VuZXMgPC0gY29tcGFyaXNvbl9yZXN1bHQgJT4lCiAgICByb3duYW1lc190b19jb2x1bW4oImdlbmUiKSAlPiUKICAgIGFycmFuZ2UoZGVzYyhhYnMoYXZnX2xvZzJGQykpKSAlPiUKICAgIGhlYWQobikgJT4lCiAgICBwdWxsKGdlbmUpCiAgcmV0dXJuKHRvcF9nZW5lcykKfQoKIyBDb21iaW5lIHRvcCBnZW5lcyBmcm9tIGFsbCBjb21wYXJpc29ucwphbGxfdG9wX2dlbmVzIDwtIHVuaXF1ZShjKAogIGdldF90b3BfZ2VuZXMocDFfY29tcGFyaXNvbiksCiAgZ2V0X3RvcF9nZW5lcyhwMl9jb21wYXJpc29uKSwKICBnZXRfdG9wX2dlbmVzKHAzX2NvbXBhcmlzb25fTDVMNiksCiAgZ2V0X3RvcF9nZW5lcyhwM19jb21wYXJpc29uX0w1TDcpLAogIGdldF90b3BfZ2VuZXMocDNfY29tcGFyaXNvbl9MNkw3KQopKQoKIyBJbml0aWFsaXplIFNUUklOR2RiCnN0cmluZ19kYiA8LSBTVFJJTkdkYiRuZXcodmVyc2lvbj0iMTEiLCBzcGVjaWVzPTk2MDYsIHNjb3JlX3RocmVzaG9sZD03MDApCgojIE1hcCBnZW5lcyB0byBTVFJJTkcgaWRlbnRpZmllcnMKbWFwcGVkX2dlbmVzIDwtIHN0cmluZ19kYiRtYXAoZGF0YS5mcmFtZShnZW5lPWFsbF90b3BfZ2VuZXMpLCAiZ2VuZSIsIHJlbW92ZVVubWFwcGVkUm93cyA9IFRSVUUpCgojIEdldCBpbnRlcmFjdGlvbnMKaW50ZXJhY3Rpb25zIDwtIHN0cmluZ19kYiRnZXRfaW50ZXJhY3Rpb25zKG1hcHBlZF9nZW5lcyRTVFJJTkdfaWQpCgojIE1hcCBTVFJJTkcgaWRlbnRpZmllcnMgYmFjayB0byBnZW5lIHN5bWJvbHMKaW50ZXJhY3Rpb25zJGZyb20gPC0gbWFwcGVkX2dlbmVzJGdlbmVbbWF0Y2goaW50ZXJhY3Rpb25zJGZyb20sIG1hcHBlZF9nZW5lcyRTVFJJTkdfaWQpXQppbnRlcmFjdGlvbnMkdG8gPC0gbWFwcGVkX2dlbmVzJGdlbmVbbWF0Y2goaW50ZXJhY3Rpb25zJHRvLCBtYXBwZWRfZ2VuZXMkU1RSSU5HX2lkKV0KCiMgQ3JlYXRlIGlncmFwaCBvYmplY3QKZyA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoaW50ZXJhY3Rpb25zLCBkaXJlY3RlZCA9IEZBTFNFKQoKIyBDYWxjdWxhdGUgbm9kZSBkZWdyZWVzClYoZykkZGVncmVlIDwtIGRlZ3JlZShnKQoKIyBDYWxjdWxhdGUgYmV0d2Vlbm5lc3MgY2VudHJhbGl0eQpWKGcpJGJldHdlZW5uZXNzIDwtIGJldHdlZW5uZXNzKGcpCgojIElkZW50aWZ5IGNvbW11bml0aWVzCmNvbW11bml0aWVzIDwtIGNsdXN0ZXJfbG91dmFpbihnKQpWKGcpJGNvbW11bml0eSA8LSBjb21tdW5pdGllcyRtZW1iZXJzaGlwCgojIFBsb3QgdGhlIG5ldHdvcmsKc2V0LnNlZWQoMTIzKSAgIyBmb3IgcmVwcm9kdWNpYmlsaXR5CmdncmFwaChnLCBsYXlvdXQgPSAiZnIiKSArCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBjb21iaW5lZF9zY29yZSksIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gZmFjdG9yKGNvbW11bml0eSksIHNpemUgPSBkZWdyZWUpKSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsID0gVFJVRSwgc2l6ZSA9IDMpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgbGFicyh0aXRsZSA9ICJHZW5lIEludGVyYWN0aW9uIE5ldHdvcmsiLAogICAgICAgc3VidGl0bGUgPSAiQmFzZWQgb24gdG9wIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyIsCiAgICAgICBjb2xvciA9ICJDb21tdW5pdHkiLAogICAgICAgc2l6ZSA9ICJEZWdyZWUiKQoKIyBTYXZlIHRoZSBwbG90Cmdnc2F2ZSgiZ2VuZV9pbnRlcmFjdGlvbl9uZXR3b3JrLnBuZyIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCgojIElkZW50aWZ5IGh1YiBnZW5lcwpodWJfZ2VuZXMgPC0gVihnKSRuYW1lW29yZGVyKFYoZykkZGVncmVlLCBkZWNyZWFzaW5nID0gVFJVRSldWzE6MTBdCmNhdCgiVG9wIDEwIGh1YiBnZW5lczpcbiIpCnByaW50KGh1Yl9nZW5lcykKCiMgSWRlbnRpZnkgZ2VuZXMgd2l0aCBoaWdoIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkKaGlnaF9iZXR3ZWVubmVzc19nZW5lcyA8LSBWKGcpJG5hbWVbb3JkZXIoVihnKSRiZXR3ZWVubmVzcywgZGVjcmVhc2luZyA9IFRSVUUpXVsxOjEwXQpjYXQoIlxuVG9wIDEwIGdlbmVzIHdpdGggaGlnaCBiZXR3ZWVubmVzcyBjZW50cmFsaXR5OlxuIikKcHJpbnQoaGlnaF9iZXR3ZWVubmVzc19nZW5lcykKCiMgQ2FsY3VsYXRlIGFuZCBwcmludCBzb21lIG5ldHdvcmsgc3RhdGlzdGljcwpjYXQoIlxuTmV0d29yayBTdGF0aXN0aWNzOlxuIikKY2F0KCJOdW1iZXIgb2Ygbm9kZXM6IiwgdmNvdW50KGcpLCAiXG4iKQpjYXQoIk51bWJlciBvZiBlZGdlczoiLCBlY291bnQoZyksICJcbiIpCmNhdCgiTmV0d29yayBkZW5zaXR5OiIsIGVkZ2VfZGVuc2l0eShnKSwgIlxuIikKY2F0KCJBdmVyYWdlIHBhdGggbGVuZ3RoOiIsIG1lYW5fZGlzdGFuY2UoZyksICJcbiIpCmNhdCgiQ2x1c3RlcmluZyBjb2VmZmljaWVudDoiLCB0cmFuc2l0aXZpdHkoZyksICJcbiIpCgojIEV4dHJhY3QgZWRnZSBpbmZvcm1hdGlvbgplZGdlc19kZiA8LSBkYXRhLmZyYW1lKAogIGZyb20gPSBlbmRzKGcsIEUoZykpWywxXSwKICB0byA9IGVuZHMoZywgRShnKSlbLDJdCikKCiMgQWRkIGVkZ2UgYXR0cmlidXRlcyBpZiBhbnkKZWRnZV9hdHRycyA8LSBlZGdlX2F0dHIoZykKZm9yIChhdHRyIGluIG5hbWVzKGVkZ2VfYXR0cnMpKSB7CiAgZWRnZXNfZGZbW2F0dHJdXSA8LSBlZGdlX2F0dHJzW1thdHRyXV0KfQoKIyBTYXZlIHRoZSBlZGdlcyBkYXRhIGZyYW1lCndyaXRlLmNzdihlZGdlc19kZiwgImdlbmVfbmV0d29ya19lZGdlcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgRXh0cmFjdCBub2RlIGluZm9ybWF0aW9uCm5vZGVzX2RmIDwtIGRhdGEuZnJhbWUoCiAgaWQgPSBWKGcpJG5hbWUsCiAgZGVncmVlID0gZGVncmVlKGcpLAogIGJldHdlZW5uZXNzID0gYmV0d2Vlbm5lc3MoZykKKQoKIyBBZGQgb3RoZXIgdmVydGV4IGF0dHJpYnV0ZXMgaWYgYW55CnZlcnRleF9hdHRycyA8LSB2ZXJ0ZXhfYXR0cihnKQpmb3IgKGF0dHIgaW4gbmFtZXModmVydGV4X2F0dHJzKSkgewogIGlmIChhdHRyICE9ICJuYW1lIikgeyAgIyBTa2lwICduYW1lJyBhcyB3ZSBhbHJlYWR5IGhhdmUgaXQgYXMgJ2lkJwogICAgbm9kZXNfZGZbW2F0dHJdXSA8LSB2ZXJ0ZXhfYXR0cnNbW2F0dHJdXQogIH0KfQoKIyBTYXZlIHRoZSBub2RlcyBkYXRhIGZyYW1lCndyaXRlLmNzdihub2Rlc19kZiwgImdlbmVfbmV0d29ya19ub2Rlcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgUHJpbnQgYSBzdW1tYXJ5IG9mIHRoZSBzYXZlZCBkYXRhCmNhdCgiU2F2ZWQgbmV0d29yayBkYXRhOlxuIikKY2F0KCJFZGdlcyBmaWxlOiBnZW5lX25ldHdvcmtfZWRnZXMuY3N2IC0iLCBucm93KGVkZ2VzX2RmKSwgInJvd3NcbiIpCmNhdCgiTm9kZXMgZmlsZTogZ2VuZV9uZXR3b3JrX25vZGVzLmNzdiAtIiwgbnJvdyhub2Rlc19kZiksICJyb3dzXG4iKQoKYGBgCgoKIyA5LiBOZXR3b3JrIEFuYWx5c2lzLWtlZ2cKYGBge3IgbmV0d29ya19hbmFseXNpczMsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMn0KCiMgTG9hZCByZXF1aXJlZCBsaWJyYXJpZXMKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkoZ2dyYXBoKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQpsaWJyYXJ5KEdPLmRiKQpsaWJyYXJ5KEFubm90YXRpb25EYmkpCmxpYnJhcnkoZHBseXIpCgojIEZ1bmN0aW9uIHRvIGdldCB0b3AgZ2VuZXMgZnJvbSBjb21wYXJpc29uIHJlc3VsdHMKZ2V0X3RvcF9nZW5lcyA8LSBmdW5jdGlvbihjb21wYXJpc29uX3Jlc3VsdCwgbiA9IDUwKSB7CiAgdG9wX2dlbmVzIDwtIGNvbXBhcmlzb25fcmVzdWx0ICU+JQogICAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImdlbmUiKSAlPiUKICAgIGRwbHlyOjphcnJhbmdlKGRlc2MoYWJzKGF2Z19sb2cyRkMpKSkgJT4lCiAgICBoZWFkKG4pICU+JQogICAgZHBseXI6OnB1bGwoZ2VuZSkKICByZXR1cm4odG9wX2dlbmVzKQp9CgojIENvbWJpbmUgdG9wIGdlbmVzIGZyb20gYWxsIGNvbXBhcmlzb25zCmFsbF90b3BfZ2VuZXMgPC0gdW5pcXVlKGMoCiAgZ2V0X3RvcF9nZW5lcyhwMV9jb21wYXJpc29uKSwKICBnZXRfdG9wX2dlbmVzKHAyX2NvbXBhcmlzb24pLAogIGdldF90b3BfZ2VuZXMocDNfY29tcGFyaXNvbl9MNUw2KSwKICBnZXRfdG9wX2dlbmVzKHAzX2NvbXBhcmlzb25fTDVMNyksCiAgZ2V0X3RvcF9nZW5lcyhwM19jb21wYXJpc29uX0w2TDcpCikpCgojIEdldCBHTyB0ZXJtcyBmb3IgYWxsIHRvcCBnZW5lcwpnb190ZXJtcyA8LSBBbm5vdGF0aW9uRGJpOjpzZWxlY3Qob3JnLkhzLmVnLmRiLCBrZXlzID0gYWxsX3RvcF9nZW5lcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5zID0gYygiU1lNQk9MIiwgIkdPIiwgIk9OVE9MT0dZIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5dHlwZSA9ICJTWU1CT0wiKQoKIyBGaWx0ZXIgZm9yIGJpb2xvZ2ljYWwgcHJvY2VzcyBHTyB0ZXJtcyBhbmQgcmVtb3ZlIE5BIHZhbHVlcwpnb190ZXJtc19icCA8LSBnb190ZXJtcyAlPiUKICBkcGx5cjo6ZmlsdGVyKE9OVE9MT0dZID09ICJCUCIpICU+JQogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKEdPKSkKCiMgQ3JlYXRlIGVkZ2VzIGRhdGFmcmFtZQplZGdlcyA8LSBnb190ZXJtc19icCAlPiUKICBkcGx5cjo6c2VsZWN0KGZyb20gPSBTWU1CT0wsIHRvID0gR08pCgojIFByaW50IHN1bW1hcnkgb2YgZWRnZXMKcHJpbnQocGFzdGUoIk51bWJlciBvZiBlZGdlczoiLCBucm93KGVkZ2VzKSkpCnByaW50KGhlYWQoZWRnZXMpKQoKIyBJZiBlZGdlcyBkYXRhZnJhbWUgaXMgZW1wdHksIHN0b3AgaGVyZQppZiAobnJvdyhlZGdlcykgPT0gMCkgewogIHN0b3AoIk5vIEdPIHRlcm1zIGZvdW5kIGZvciBhbnkgZ2VuZXMuIENhbm5vdCBjcmVhdGUgbmV0d29yay4iKQp9CgojIENyZWF0ZSBncmFwaApnIDwtIGlncmFwaDo6Z3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGVkZ2VzLCBkaXJlY3RlZCA9IEZBTFNFKQoKIyBDYWxjdWxhdGUgbm9kZSBkZWdyZWVzClYoZykkZGVncmVlIDwtIGlncmFwaDo6ZGVncmVlKGcpCgojIENhbGN1bGF0ZSBiZXR3ZWVubmVzcyBjZW50cmFsaXR5ClYoZykkYmV0d2Vlbm5lc3MgPC0gaWdyYXBoOjpiZXR3ZWVubmVzcyhnKQoKIyBJZGVudGlmeSBjb21tdW5pdGllcwpjb21tdW5pdGllcyA8LSBpZ3JhcGg6OmNsdXN0ZXJfbG91dmFpbihnKQpWKGcpJGNvbW11bml0eSA8LSBjb21tdW5pdGllcyRtZW1iZXJzaGlwCgojIEdldCBHTyB0ZXJtIGRlc2NyaXB0aW9ucwpnb190ZXJtc19kZXNjIDwtIEFubm90YXRpb25EYmk6OnNlbGVjdChHTy5kYiwga2V5cyA9IHVuaXF1ZShlZGdlcyR0byksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5zID0gIlRFUk0iLCBrZXl0eXBlID0gIkdPSUQiKQoKIyBBZGQgR08gdGVybSBkZXNjcmlwdGlvbnMgdG8gdGhlIGdyYXBoClYoZykkZGVzY3JpcHRpb24gPC0gZ29fdGVybXNfZGVzYyRURVJNW21hdGNoKFYoZykkbmFtZSwgZ29fdGVybXNfZGVzYyRHT0lEKV0KCiMgUGxvdCB0aGUgbmV0d29yawpzZXQuc2VlZCgxMjMpICAjIGZvciByZXByb2R1Y2liaWxpdHkKcCA8LSBnZ3JhcGgoZywgbGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rKGFscGhhID0gMC4xKSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2xvciA9IGZhY3Rvcihjb21tdW5pdHkpLCBzaXplID0gZGVncmVlKSkgKwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IGlmZWxzZShkZWdyZWUgPiBxdWFudGlsZShkZWdyZWUsIDAuOTUpLCBuYW1lLCAiIikpLCAKICAgICAgICAgICAgICAgICByZXBlbCA9IFRSVUUsIHNpemUgPSAzKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpICsKICB0aGVtZV92b2lkKCkgKwogIGxhYnModGl0bGUgPSAiR2VuZS1HTyBUZXJtIEludGVyYWN0aW9uIE5ldHdvcmsiLAogICAgICAgc3VidGl0bGUgPSAiQmFzZWQgb24gdG9wIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyIsCiAgICAgICBjb2xvciA9ICJDb21tdW5pdHkiLAogICAgICAgc2l6ZSA9ICJEZWdyZWUiKQoKIyBTYXZlIHRoZSBwbG90Cmdnc2F2ZSgiZ2VuZV9nb19uZXR3b3JrLnBuZyIsIHAsIHdpZHRoID0gMTIsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCgojIElkZW50aWZ5IGh1YiBnZW5lcwpodWJfZ2VuZXMgPC0gVihnKSRuYW1lW1YoZykkbmFtZSAlaW4lIGFsbF90b3BfZ2VuZXNdW29yZGVyKFYoZykkZGVncmVlW1YoZykkbmFtZSAlaW4lIGFsbF90b3BfZ2VuZXNdLCBkZWNyZWFzaW5nID0gVFJVRSldWzE6MTBdCmNhdCgiVG9wIDEwIGh1YiBnZW5lczpcbiIpCnByaW50KGh1Yl9nZW5lcykKCiMgSWRlbnRpZnkgZ2VuZXMgd2l0aCBoaWdoIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkKaGlnaF9iZXR3ZWVubmVzc19nZW5lcyA8LSBWKGcpJG5hbWVbVihnKSRuYW1lICVpbiUgYWxsX3RvcF9nZW5lc11bb3JkZXIoVihnKSRiZXR3ZWVubmVzc1tWKGcpJG5hbWUgJWluJSBhbGxfdG9wX2dlbmVzXSwgZGVjcmVhc2luZyA9IFRSVUUpXVsxOjEwXQpjYXQoIlxuVG9wIDEwIGdlbmVzIHdpdGggaGlnaCBiZXR3ZWVubmVzcyBjZW50cmFsaXR5OlxuIikKcHJpbnQoaGlnaF9iZXR3ZWVubmVzc19nZW5lcykKCiMgQ2FsY3VsYXRlIGFuZCBwcmludCBzb21lIG5ldHdvcmsgc3RhdGlzdGljcwpjYXQoIlxuTmV0d29yayBTdGF0aXN0aWNzOlxuIikKY2F0KCJOdW1iZXIgb2Ygbm9kZXM6IiwgaWdyYXBoOjp2Y291bnQoZyksICJcbiIpCmNhdCgiTnVtYmVyIG9mIGVkZ2VzOiIsIGlncmFwaDo6ZWNvdW50KGcpLCAiXG4iKQpjYXQoIk5ldHdvcmsgZGVuc2l0eToiLCBpZ3JhcGg6OmVkZ2VfZGVuc2l0eShnKSwgIlxuIikKY2F0KCJBdmVyYWdlIHBhdGggbGVuZ3RoOiIsIGlncmFwaDo6bWVhbl9kaXN0YW5jZShnKSwgIlxuIikKY2F0KCJDbHVzdGVyaW5nIGNvZWZmaWNpZW50OiIsIGlncmFwaDo6dHJhbnNpdGl2aXR5KGcpLCAiXG4iKQoKIyBFeHRyYWN0IGVkZ2UgaW5mb3JtYXRpb24KZWRnZXNfZGYgPC0gaWdyYXBoOjphc19kYXRhX2ZyYW1lKGcsIHdoYXQgPSAiZWRnZXMiKQoKIyBTYXZlIHRoZSBlZGdlcyBkYXRhIGZyYW1lCndyaXRlLmNzdihlZGdlc19kZiwgImdlbmVfZ29fZWRnZXMuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojIEV4dHJhY3Qgbm9kZSBpbmZvcm1hdGlvbgpub2Rlc19kZiA8LSBpZ3JhcGg6OmFzX2RhdGFfZnJhbWUoZywgd2hhdCA9ICJ2ZXJ0aWNlcyIpCgojIFNhdmUgdGhlIG5vZGVzIGRhdGEgZnJhbWUKd3JpdGUuY3N2KG5vZGVzX2RmLCAiZ2VuZV9nb19ub2Rlcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgUHJpbnQgYSBzdW1tYXJ5IG9mIHRoZSBzYXZlZCBkYXRhCmNhdCgiU2F2ZWQgbmV0d29yayBkYXRhOlxuIikKY2F0KCJFZGdlcyBmaWxlOiBnZW5lX2dvX2VkZ2VzLmNzdiAtIiwgbnJvdyhlZGdlc19kZiksICJyb3dzXG4iKQpjYXQoIk5vZGVzIGZpbGU6IGdlbmVfZ29fbm9kZXMuY3N2IC0iLCBucm93KG5vZGVzX2RmKSwgInJvd3NcbiIpCgojIFByaW50IHRvcCBHTyB0ZXJtcwp0b3BfZ29fdGVybXMgPC0gVihnKSRuYW1lWyEoVihnKSRuYW1lICVpbiUgYWxsX3RvcF9nZW5lcyldW29yZGVyKFYoZykkZGVncmVlWyEoVihnKSRuYW1lICVpbiUgYWxsX3RvcF9nZW5lcyldLCBkZWNyZWFzaW5nID0gVFJVRSldWzE6MjBdCmNhdCgiXG5Ub3AgMjAgR08gdGVybXM6XG4iKQpmb3IgKHRlcm0gaW4gdG9wX2dvX3Rlcm1zKSB7CiAgY2F0KHRlcm0sICItIiwgVihnKSRkZXNjcmlwdGlvbltWKGcpJG5hbWUgPT0gdGVybV0sICJcbiIpCn0KYGBgCgojIDEwLiBTYXZlIHRoZSBTZXVyYXQgb2JqZWN0IGFzIGFuIFJvYmogZmlsZQpgYGB7ciBzYXZlUk9CSn0KCgojc2F2ZShBbGxfc2FtcGxlc19NZXJnZWQsIGZpbGUgPSAiQWxsX3NhbXBsZXNfTWVyZ2VkX0RFLlJvYmoiKQoKCmBgYAoKCgoK