1. load libraries

#Differential Expression Analysis

2. load seurat object

#Load Seurat Object L7
load("../../0-IMP-OBJECTS/All_Samples_Merged_with_10x_Azitmuth_Annotated_SCT_HPC.robj")


All_samples_Merged
An object of class Seurat 
64169 features across 59355 samples within 6 assays 
Active assay: SCT (27417 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
 5 dimensional reductions calculated: integrated_dr, ref.umap, pca, umap, harmony

#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
Calculating cluster PBMC_10x
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)

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)
Le chargement a nécessité le package : ggrepel
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 : 14.13% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 28.75% 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 : 12.15% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 28.75% 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 : 16.94% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 28.75% 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.02% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 28.75% 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 : 11.77% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 28.75% 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 : 18.73% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 28.75% 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 : 16.38% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 28.75% 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 : 14.49% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 28.75% 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 : 19.91% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 28.75% 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 : 11.42% of input gene IDs are fail to map...'select()' returned 1:many mapping between keys and columns
Avis : 28.75% of input gene IDs are fail to map...

8. Network Analysis

# Function to get top genes from a comparison

library(igraph)

Attachement du package : ‘igraph’

L'objet suivant est masqué depuis ‘package:IRanges’:

    union

L'objet suivant est masqué depuis ‘package:S4Vectors’:

    union

Les objets suivants sont masqués depuis ‘package:BiocGenerics’:

    normalize, path, union

L'objet suivant est masqué depuis ‘package:clusterProfiler’:

    simplify

Les objets suivants sont masqués depuis ‘package:dplyr’:

    as_data_frame, groups, union

L'objet suivant est masqué depuis ‘package:Seurat’:

    components

Les objets suivants sont masqués depuis ‘package:stats’:

    decompose, spectrum

L'objet suivant est masqué depuis ‘package:base’:

    union
library(STRINGdb)
library(ggraph)

Attachement du package : ‘ggraph’

L'objet suivant est masqué depuis ‘package:sp’:

    geometry
library(tidyverse)
── Attaching core tidyverse packages ───────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ lubridate 1.9.3     ✔ tibble    3.2.1
✔ purrr     1.0.2     ✔ tidyr     1.3.1
✔ readr     2.1.5     ── Conflicts ─────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ lubridate::%--%()         masks igraph::%--%()
✖ lubridate::%within%()     masks IRanges::%within%()
✖ tibble::as_data_frame()   masks igraph::as_data_frame(), dplyr::as_data_frame()
✖ IRanges::collapse()       masks dplyr::collapse()
✖ Biobase::combine()        masks BiocGenerics::combine(), dplyr::combine()
✖ purrr::compose()          masks igraph::compose()
✖ tidyr::crossing()         masks igraph::crossing()
✖ IRanges::desc()           masks dplyr::desc()
✖ tidyr::expand()           masks S4Vectors::expand()
✖ clusterProfiler::filter() masks dplyr::filter(), stats::filter()
✖ S4Vectors::first()        masks dplyr::first()
✖ dplyr::lag()              masks stats::lag()
✖ BiocGenerics::Position()  masks ggplot2::Position(), base::Position()
✖ purrr::reduce()           masks IRanges::reduce()
✖ S4Vectors::rename()       masks clusterProfiler::rename(), dplyr::rename()
✖ lubridate::second()       masks S4Vectors::second()
✖ lubridate::second<-()     masks S4Vectors::second<-()
✖ AnnotationDbi::select()   masks clusterProfiler::select(), dplyr::select()
✖ purrr::simplify()         masks igraph::simplify(), clusterProfiler::simplify()
✖ IRanges::slice()          masks clusterProfiler::slice(), dplyr::slice()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
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)
essai de l'URL 'https://stringdb-static.org/download/protein.aliases.v11.0/9606.protein.aliases.v11.0.txt.gz'
Content type 'application/octet-stream' length 13253303 bytes (12.6 MB)
==================================================
downloaded 12.6 MB

essai de l'URL 'https://stringdb-static.org/download/protein.info.v11.0/9606.protein.info.v11.0.txt.gz'
Content type 'application/octet-stream' length 1894048 bytes (1.8 MB)
==================================================
downloaded 1.8 MB
Warning:  we couldn't map to STRING 24% of your identifiers
# Get interactions
interactions <- string_db$get_interactions(mapped_genes$STRING_id)
essai de l'URL 'https://stringdb-static.org/download/protein.links.v11.0/9606.protein.links.v11.0.txt.gz'
Content type 'application/octet-stream' length 71241372 bytes (67.9 MB)
==================================================
downloaded 67.9 MB
# 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.ENSP00000248564" "9606.ENSP00000426022"
 [5] "9606.ENSP00000482259" "9606.ENSP00000292301" "9606.ENSP00000216341" "9606.ENSP00000229135"
 [9] "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.ENSP00000362795" "9606.ENSP00000398568" "9606.ENSP00000245451"
 [5] "9606.ENSP00000216341" "9606.ENSP00000482259" "9606.ENSP00000292301" "9606.ENSP00000248564"
 [9] "9606.ENSP00000426022" "9606.ENSP00000228280"
# Calculate and print some network statistics
cat("\nNetwork Statistics:\n")

Network Statistics:
cat("Number of nodes:", vcount(g), "\n")
Number of nodes: 60 
cat("Number of edges:", ecount(g), "\n")
Number of edges: 228 
cat("Network density:", edge_density(g), "\n")
Network density: 0.1288136 
cat("Average path length:", mean_distance(g), "\n")
Average path length: 3.090728 
cat("Clustering coefficient:", transitivity(g), "\n")
Clustering coefficient: 0.4502229 
# 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 - 228 rows
cat("Nodes file: gene_network_nodes.csv -", nrow(nodes_df), "rows\n")
Nodes file: gene_network_nodes.csv - 60 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 24% 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" "GNG11" "GNGT2" "CCL4"  "CCR2"  "GZMB"  "IFNG"  "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"   "CXCR3" "PRF1"  "BMP4"  "GZMB"  "CCL4"  "CCR2"  "GNG11" "GNGT2" "KITLG"
# Calculate and print some network statistics
cat("\nNetwork Statistics:\n")

Network Statistics:
cat("Number of nodes:", vcount(g), "\n")
Number of nodes: 60 
cat("Number of edges:", ecount(g), "\n")
Number of edges: 228 
cat("Network density:", edge_density(g), "\n")
Network density: 0.1288136 
cat("Average path length:", mean_distance(g), "\n")
Average path length: 3.090728 
cat("Clustering coefficient:", transitivity(g), "\n")
Clustering coefficient: 0.4502229 
# 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 - 228 rows
cat("Nodes file: gene_network_nodes.csv -", nrow(nodes_df), "rows\n")
Nodes file: gene_network_nodes.csv - 60 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: 2401"
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"   "FGFR2"  "IL6"    "SNCA"   "IFNG"   "NKX2-5" "SIX1"   "WNT11"  "TWIST1" "AR"    
# 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"   "FGFR2"  "IFNG"   "NKX2-5" "AR"     "SIX1"   "WNT11"  "TWIST1"
# Calculate and print some network statistics
cat("\nNetwork Statistics:\n")

Network Statistics:
cat("Number of nodes:", igraph::vcount(g), "\n")
Number of nodes: 1609 
cat("Number of edges:", igraph::ecount(g), "\n")
Number of edges: 2401 
cat("Network density:", igraph::edge_density(g), "\n")
Network density: 0.001856009 
cat("Average path length:", igraph::mean_distance(g), "\n")
Average path length: 4.87717 
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 - 2401 rows
cat("Nodes file: gene_go_nodes.csv -", nrow(nodes_df), "rows\n")
Nodes file: gene_go_nodes.csv - 1609 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:0045893 - positive regulation of DNA-templated transcription 
GO:0006357 - regulation of transcription by RNA polymerase II 
GO:0008284 - positive regulation of cell population proliferation 
GO:0010628 - positive regulation of gene expression 
GO:0045892 - negative regulation of DNA-templated transcription 
GO:0007155 - cell adhesion 
GO:0007186 - G protein-coupled receptor signaling pathway 
GO:0006955 - immune response 
GO:0030154 - cell differentiation 
GO:0043066 - negative regulation of apoptotic process 
GO:0006915 - apoptotic process 
GO:0006954 - inflammatory response 
GO:0045087 - innate immune response 
GO:0002250 - adaptive immune response 
GO:0090090 - negative regulation of canonical Wnt signaling pathway 
GO:0098609 - cell-cell adhesion 
GO:0070374 - positive regulation of ERK1 and ERK2 cascade 

10. Save the Seurat object as an Robj file



#save(All_samples_Merged, file = "All_samples_Merged_DE.Robj")
LS0tCnRpdGxlOiAiU8OpemFyeSBTeW5kcm9tZSBDZWxsIExpbmUgQW5hbHlzaXMiCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgIyBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICAjIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKICAjIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICAjcm1kZm9ybWF0czo6cmVhZHRoZWRvd24KICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19jb2xsYXBzZWQ6IHRydWUKLS0tCgojIDEuIGxvYWQgbGlicmFyaWVzCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQoKCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KGVucmljaHBsb3QpCgoKCmBgYAojRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQW5hbHlzaXMKCiMgMi4gbG9hZCBzZXVyYXQgb2JqZWN0CmBgYHtyIGxvYWRfc2V1cmF0fQojTG9hZCBTZXVyYXQgT2JqZWN0IEw3CmxvYWQoIi4uLy4uLzAtSU1QLU9CSkVDVFMvQWxsX1NhbXBsZXNfTWVyZ2VkX3dpdGhfMTB4X0F6aXRtdXRoX0Fubm90YXRlZF9TQ1RfSFBDLnJvYmoiKQoKCkFsbF9zYW1wbGVzX01lcmdlZAoKYGBgCgojRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQW5hbHlzaXMKCiMgMy4gRmluZCBNYXJrZXJzIGZvciBFYWNoIENlbGwgTGluZQpgYGB7ciBmaW5kbWFya2VyczEsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEyfQoKRGVmYXVsdEFzc2F5KEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gIlNDVCIKSWRlbnRzKEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gImNlbGxfbGluZSIKYWxsX21hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoQWxsX3NhbXBsZXNfTWVyZ2VkLCBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjI1LCBsb2dmYy50aHJlc2hvbGQgPSAwLjI1KQp3cml0ZS5jc3YoYWxsX21hcmtlcnMsICJhbGxfbWFya2Vycy5jc3YiKQpoZWFkKGFsbF9tYXJrZXJzKQoKCmBgYAoKIyA0LiBIZWF0bWFwIG9mIFRvcCAxMCBVcHJlZ3VsYXRlZCBHZW5lcyAoU29ydGVkIGJ5IGF2Z19sb2cyRkMpCmBgYHtyIHRvcDEwX0hlYXRtYXAsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEyfQoKdG9wMTBfbWFya2VycyA8LSBhbGxfbWFya2VycyAlPiUKICBncm91cF9ieShjbHVzdGVyKSAlPiUKICB0b3BfbihuID0gMTAsIHd0ID0gYXZnX2xvZzJGQykgJT4lCiAgYXJyYW5nZShjbHVzdGVyLCBkZXNjKGF2Z19sb2cyRkMpKQoKdG9wMTBfZ2VuZXMgPC0gdW5pcXVlKHRvcDEwX21hcmtlcnMkZ2VuZSkKCmhlYXRtYXBfZGF0YSA8LSBHZXRBc3NheURhdGEoQWxsX3NhbXBsZXNfTWVyZ2VkLCBhc3NheSA9ICJTQ1QiLCBzbG90ID0gImRhdGEiKVt0b3AxMF9nZW5lcywgXQpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhIC0gcm93TWVhbnMoaGVhdG1hcF9kYXRhKQoKY2VsbF9saW5lX2NvbG9ycyA8LSByYWluYm93KGxlbmd0aCh1bmlxdWUoQWxsX3NhbXBsZXNfTWVyZ2VkJGNlbGxfbGluZSkpKQpuYW1lcyhjZWxsX2xpbmVfY29sb3JzKSA8LSB1bmlxdWUoQWxsX3NhbXBsZXNfTWVyZ2VkJGNlbGxfbGluZSkKYW5ub3RhdGlvbl9jb2xvcnMgPC0gbGlzdChjZWxsX2xpbmUgPSBjZWxsX2xpbmVfY29sb3JzKQoKcCA8LSBwaGVhdG1hcChoZWF0bWFwX2RhdGEsCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLAogICAgICAgICBzaG93X2NvbG5hbWVzID0gRkFMU0UsCiAgICAgICAgIGFubm90YXRpb25fY29sID0gQWxsX3NhbXBsZXNfTWVyZ2VkQG1ldGEuZGF0YVssICJjZWxsX2xpbmUiLCBkcm9wID0gRkFMU0VdLAogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90YXRpb25fY29sb3JzLAogICAgICAgICBtYWluID0gIlRvcCAxMCBVcHJlZ3VsYXRlZCBHZW5lcyBwZXIgQ2VsbCBMaW5lIChTb3J0ZWQgYnkgYXZnX2xvZzJGQykiLAogICAgICAgICBmb250c2l6ZV9yb3cgPSA2LAogICAgICAgICB0cmVlaGVpZ2h0X2NvbCA9IDAsCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLAogICAgICAgICBjbHVzdGVyX2NvbHMgPSBGQUxTRSkKCnByaW50KHApCnBuZygiaGVhdG1hcF90b3AxMF9sb2cyZmNfc29ydGVkLnBuZyIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDgsIHVuaXRzID0gImluIiwgcmVzID0gMzAwKQpwcmludChwKQpkZXYub2ZmKCkKCmBgYAoKIyA1LiBIZWF0bWFwIG9mIFRvcCAxMCBNYXJrZXJzIChTZXVyYXQgRGVmYXVsdCkKYGBge3IgaGVhdG1hcDIsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEyfQoKdG9wMTBfbWFya2Vyc19zZXVyYXQgPC0gYWxsX21hcmtlcnMgJT4lCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgdG9wX24obiA9IDEwLCB3dCA9IGF2Z19sb2cyRkMpCgp0b3AxMF9nZW5lc19zZXVyYXQgPC0gdW5pcXVlKHRvcDEwX21hcmtlcnNfc2V1cmF0JGdlbmUpCgpoZWF0bWFwX2RhdGFfc2V1cmF0IDwtIEdldEFzc2F5RGF0YShBbGxfc2FtcGxlc19NZXJnZWQsIGFzc2F5ID0gIlNDVCIsIHNsb3QgPSAiZGF0YSIpW3RvcDEwX2dlbmVzX3NldXJhdCwgXQpoZWF0bWFwX2RhdGFfc2V1cmF0IDwtIGhlYXRtYXBfZGF0YV9zZXVyYXQgLSByb3dNZWFucyhoZWF0bWFwX2RhdGFfc2V1cmF0KQoKcDIgPC0gcGhlYXRtYXAoaGVhdG1hcF9kYXRhX3NldXJhdCwKICAgICAgICAgc2hvd19yb3duYW1lcyA9IFRSVUUsCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGQUxTRSwKICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBBbGxfc2FtcGxlc19NZXJnZWRAbWV0YS5kYXRhWywgImNlbGxfbGluZSIsIGRyb3AgPSBGQUxTRV0sCiAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5ub3RhdGlvbl9jb2xvcnMsCiAgICAgICAgIG1haW4gPSAiVG9wIDEwIE1hcmtlcnMgcGVyIENlbGwgTGluZSAoU2V1cmF0IERlZmF1bHQpIiwKICAgICAgICAgZm9udHNpemVfcm93ID0gNiwKICAgICAgICAgdHJlZWhlaWdodF9jb2wgPSAwKQoKcHJpbnQocDIpCnBuZygiaGVhdG1hcF90b3AxMF9zZXVyYXQucG5nIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gOCwgdW5pdHMgPSAiaW4iLCByZXMgPSAzMDApCnByaW50KHAyKQpkZXYub2ZmKCkKCmBgYAoKIyA2LiBQYWlyd2lzZSBDb21wYXJpc29ucwpgYGB7ciBwYWlyd2lzZUNvbXAsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEyfQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCnBlcmZvcm1fY29tcGFyaXNvbl9hbmRfdm9sY2FubyA8LSBmdW5jdGlvbihBbGxfc2FtcGxlc19NZXJnZWQsIGlkZW50MSwgaWRlbnQyKSB7CiAgSWRlbnRzKEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gImNlbGxfbGluZSIKICBtYXJrZXJzIDwtIEZpbmRNYXJrZXJzKEFsbF9zYW1wbGVzX01lcmdlZCwgaWRlbnQuMSA9IGlkZW50MSwgaWRlbnQuMiA9IGlkZW50MiwgYXNzYXkgPSAiU0NUIikKICB3cml0ZS5jc3YobWFya2VycywgcGFzdGUwKCJjb21wYXJpc29uXyIsIGlkZW50MSwgIl92c18iLCBpZGVudDIsICIuY3N2IikpCiAgCiAgIyBDcmVhdGUgdm9sY2FubyBwbG90CiAgdm9sY2Fub19wbG90IDwtIEVuaGFuY2VkVm9sY2FubyhtYXJrZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiID0gcm93bmFtZXMobWFya2VycyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gJ2F2Z19sb2cyRkMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9ICdwX3ZhbF9hZGonLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSBwYXN0ZShpZGVudDEsICd2cycsIGlkZW50MiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwQ3V0b2ZmID0gMC4wNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZDY3V0b2ZmID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50U2l6ZSA9IDEuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYlNpemUgPSA0LjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBjKCdncmV5JywgJ2RhcmtncmVlbicsICdibHVlJywgJ3JlZCcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sQWxwaGEgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmRMYWJTaXplID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmRJY29uU2l6ZSA9IDQuMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdpZHRoQ29ubmVjdG9ycyA9IDAuNSkKICAKICBwcmludCh2b2xjYW5vX3Bsb3QpCiAgcG5nKHBhc3RlMCgidm9sY2Fub18iLCBpZGVudDEsICJfdnNfIiwgaWRlbnQyLCAiLnBuZyIpLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMCwgdW5pdHMgPSAiaW4iLCByZXMgPSAzMDApCiAgcHJpbnQodm9sY2Fub19wbG90KQogIGRldi5vZmYoKQogIAogIHJldHVybihtYXJrZXJzKQp9CgojIFBhdGllbnQgMQpwMV9jb21wYXJpc29uIDwtIHBlcmZvcm1fY29tcGFyaXNvbl9hbmRfdm9sY2FubyhBbGxfc2FtcGxlc19NZXJnZWQsICJMMSIsICJMMiIpCmhlYWQocDFfY29tcGFyaXNvbikKCiMgUGF0aWVudCAyCnAyX2NvbXBhcmlzb24gPC0gcGVyZm9ybV9jb21wYXJpc29uX2FuZF92b2xjYW5vKEFsbF9zYW1wbGVzX01lcmdlZCwgIkwzIiwgIkw0IikKaGVhZChwMl9jb21wYXJpc29uKQoKIyBQYXRpZW50IDMKcDNfY29tcGFyaXNvbl9MNUw2IDwtIHBlcmZvcm1fY29tcGFyaXNvbl9hbmRfdm9sY2FubyhBbGxfc2FtcGxlc19NZXJnZWQsICJMNSIsICJMNiIpCnAzX2NvbXBhcmlzb25fTDVMNyA8LSBwZXJmb3JtX2NvbXBhcmlzb25fYW5kX3ZvbGNhbm8oQWxsX3NhbXBsZXNfTWVyZ2VkLCAiTDUiLCAiTDciKQpwM19jb21wYXJpc29uX0w2TDcgPC0gcGVyZm9ybV9jb21wYXJpc29uX2FuZF92b2xjYW5vKEFsbF9zYW1wbGVzX01lcmdlZCwgIkw2IiwgIkw3IikKaGVhZChwM19jb21wYXJpc29uX0w1TDYpCmhlYWQocDNfY29tcGFyaXNvbl9MNUw3KQpoZWFkKHAzX2NvbXBhcmlzb25fTDZMNykKCgpgYGAKCgojIDcuIEVucmljaG1lbnQgQW5hbHlzaXMKYGBge3IgZW5yaWNobWVudCwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CgpwZXJmb3JtX2dvX2VucmljaG1lbnQgPC0gZnVuY3Rpb24oZ2VuZV9saXN0LCBnZW5lX3VuaXZlcnNlLCB0aXRsZSkgewogIGVnbyA8LSBlbnJpY2hHTyhnZW5lID0gZ2VuZV9saXN0LAogICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IGdlbmVfdW5pdmVyc2UsCiAgICAgICAgICAgICAgICAgIE9yZ0RiID0gb3JnLkhzLmVnLmRiLAogICAgICAgICAgICAgICAgICBrZXlUeXBlID0gIlNZTUJPTCIsCiAgICAgICAgICAgICAgICAgIG9udCA9ICJCUCIsCiAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICBxdmFsdWVDdXRvZmYgPSAwLjA1LAogICAgICAgICAgICAgICAgICByZWFkYWJsZSA9IFRSVUUpCiAgCiAgcCA8LSBkb3RwbG90KGVnbywgc2hvd0NhdGVnb3J5ID0gMjAsIHRpdGxlID0gcGFzdGUoIkdPIC0iLCB0aXRsZSkpICsKICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkKICAKICBwcmludChwKQogIHBuZyhwYXN0ZTAoIkdPX2VucmljaG1lbnRfIiwgZ3N1YigiICIsICJfIiwgdGl0bGUpLCAiLnBuZyIpLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4LCB1bml0cyA9ICJpbiIsIHJlcyA9IDMwMCkKICBwcmludChwKQogIGRldi5vZmYoKQogIAogIHJldHVybihlZ28pCn0KCnBlcmZvcm1fa2VnZ19lbnJpY2htZW50IDwtIGZ1bmN0aW9uKGdlbmVfbGlzdCwgZ2VuZV91bml2ZXJzZSwgdGl0bGUpIHsKICAjIENvbnZlcnQgZ2VuZSBzeW1ib2xzIHRvIEVudHJleiBJRHMKICBlbnRyZXpfaWRzIDwtIGJpdHIoZ2VuZV9saXN0LCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IG9yZy5Icy5lZy5kYikkRU5UUkVaSUQKICB1bml2ZXJzZV9lbnRyZXogPC0gYml0cihnZW5lX3VuaXZlcnNlLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IG9yZy5Icy5lZy5kYikkRU5UUkVaSUQKICAKICBla2VnZyA8LSBlbnJpY2hLRUdHKGdlbmUgPSBlbnRyZXpfaWRzLAogICAgICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSB1bml2ZXJzZV9lbnRyZXosCiAgICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICdoc2EnLAogICAgICAgICAgICAgICAgICAgICAga2V5VHlwZSA9ICJrZWdnIiwKICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUsCiAgICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIikKICAKICBwIDwtIGRvdHBsb3QoZWtlZ2csIHNob3dDYXRlZ29yeSA9IDIwLCB0aXRsZSA9IHBhc3RlKCJLRUdHIC0iLCB0aXRsZSkpICsKICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkKICAKICBwcmludChwKQogIHBuZyhwYXN0ZTAoIktFR0dfZW5yaWNobWVudF8iLCBnc3ViKCIgIiwgIl8iLCB0aXRsZSksICIucG5nIiksIHdpZHRoID0gMTIsIGhlaWdodCA9IDgsIHVuaXRzID0gImluIiwgcmVzID0gMzAwKQogIHByaW50KHApCiAgZGV2Lm9mZigpCiAgCiAgcmV0dXJuKGVrZWdnKQp9CgpnZW5lX3VuaXZlcnNlIDwtIHJvd25hbWVzKEFsbF9zYW1wbGVzX01lcmdlZCkKCiMgUGF0aWVudCAxIChQMSkgY29tcGFyaXNvbjogTDEgdnMgTDIKdXByZWd1bGF0ZWRfZ2VuZXNfUDEgPC0gcm93bmFtZXMocDFfY29tcGFyaXNvbltwMV9jb21wYXJpc29uJGF2Z19sb2cyRkMgPiAxICYgcDFfY29tcGFyaXNvbiRwX3ZhbF9hZGogPCAwLjA1LCBdKQpkb3ducmVndWxhdGVkX2dlbmVzX1AxIDwtIHJvd25hbWVzKHAxX2NvbXBhcmlzb25bcDFfY29tcGFyaXNvbiRhdmdfbG9nMkZDIDwgLTEgJiBwMV9jb21wYXJpc29uJHBfdmFsX2FkaiA8IDAuMDUsIF0pCgpnb191cF9QMSA8LSBwZXJmb3JtX2dvX2VucmljaG1lbnQodXByZWd1bGF0ZWRfZ2VuZXNfUDEsIGdlbmVfdW5pdmVyc2UsICJVcHJlZ3VsYXRlZCBHZW5lcyBpbiBMMSB2cyBMMiIpCmdvX2Rvd25fUDEgPC0gcGVyZm9ybV9nb19lbnJpY2htZW50KGRvd25yZWd1bGF0ZWRfZ2VuZXNfUDEsIGdlbmVfdW5pdmVyc2UsICJEb3ducmVndWxhdGVkIEdlbmVzIGluIEwxIHZzIEwyIikKa2VnZ191cF9QMSA8LSBwZXJmb3JtX2tlZ2dfZW5yaWNobWVudCh1cHJlZ3VsYXRlZF9nZW5lc19QMSwgZ2VuZV91bml2ZXJzZSwgIlVwcmVndWxhdGVkIEdlbmVzIGluIEwxIHZzIEwyIikKa2VnZ19kb3duX1AxIDwtIHBlcmZvcm1fa2VnZ19lbnJpY2htZW50KGRvd25yZWd1bGF0ZWRfZ2VuZXNfUDEsIGdlbmVfdW5pdmVyc2UsICJEb3ducmVndWxhdGVkIEdlbmVzIGluIEwxIHZzIEwyIikKCiMgUGF0aWVudCAyIChQMikgY29tcGFyaXNvbjogTDMgdnMgTDQKdXByZWd1bGF0ZWRfZ2VuZXNfUDIgPC0gcm93bmFtZXMocDJfY29tcGFyaXNvbltwMl9jb21wYXJpc29uJGF2Z19sb2cyRkMgPiAxICYgcDJfY29tcGFyaXNvbiRwX3ZhbF9hZGogPCAwLjA1LCBdKQpkb3ducmVndWxhdGVkX2dlbmVzX1AyIDwtIHJvd25hbWVzKHAyX2NvbXBhcmlzb25bcDJfY29tcGFyaXNvbiRhdmdfbG9nMkZDIDwgLTEgJiBwMl9jb21wYXJpc29uJHBfdmFsX2FkaiA8IDAuMDUsIF0pCgpnb191cF9QMiA8LSBwZXJmb3JtX2dvX2VucmljaG1lbnQodXByZWd1bGF0ZWRfZ2VuZXNfUDIsIGdlbmVfdW5pdmVyc2UsICJVcHJlZ3VsYXRlZCBHZW5lcyBpbiBMMyB2cyBMNCIpCmdvX2Rvd25fUDIgPC0gcGVyZm9ybV9nb19lbnJpY2htZW50KGRvd25yZWd1bGF0ZWRfZ2VuZXNfUDIsIGdlbmVfdW5pdmVyc2UsICJEb3ducmVndWxhdGVkIEdlbmVzIGluIEwzIHZzIEw0IikKa2VnZ191cF9QMiA8LSBwZXJmb3JtX2tlZ2dfZW5yaWNobWVudCh1cHJlZ3VsYXRlZF9nZW5lc19QMiwgZ2VuZV91bml2ZXJzZSwgIlVwcmVndWxhdGVkIEdlbmVzIGluIEwzIHZzIEw0IikKa2VnZ19kb3duX1AyIDwtIHBlcmZvcm1fa2VnZ19lbnJpY2htZW50KGRvd25yZWd1bGF0ZWRfZ2VuZXNfUDIsIGdlbmVfdW5pdmVyc2UsICJEb3ducmVndWxhdGVkIEdlbmVzIGluIEwzIHZzIEw0IikKCiMgUGF0aWVudCAzIChQMykgY29tcGFyaXNvbnMKIyBMNSB2cyBMNgp1cHJlZ3VsYXRlZF9nZW5lc19QM19MNUw2IDwtIHJvd25hbWVzKHAzX2NvbXBhcmlzb25fTDVMNltwM19jb21wYXJpc29uX0w1TDYkYXZnX2xvZzJGQyA+IDEgJiBwM19jb21wYXJpc29uX0w1TDYkcF92YWxfYWRqIDwgMC4wNSwgXSkKZG93bnJlZ3VsYXRlZF9nZW5lc19QM19MNUw2IDwtIHJvd25hbWVzKHAzX2NvbXBhcmlzb25fTDVMNltwM19jb21wYXJpc29uX0w1TDYkYXZnX2xvZzJGQyA8IC0xICYgcDNfY29tcGFyaXNvbl9MNUw2JHBfdmFsX2FkaiA8IDAuMDUsIF0pCgpnb191cF9QM19MNUw2IDwtIHBlcmZvcm1fZ29fZW5yaWNobWVudCh1cHJlZ3VsYXRlZF9nZW5lc19QM19MNUw2LCBnZW5lX3VuaXZlcnNlLCAiVXByZWd1bGF0ZWQgR2VuZXMgaW4gTDUgdnMgTDYiKQpnb19kb3duX1AzX0w1TDYgPC0gcGVyZm9ybV9nb19lbnJpY2htZW50KGRvd25yZWd1bGF0ZWRfZ2VuZXNfUDNfTDVMNiwgZ2VuZV91bml2ZXJzZSwgIkRvd25yZWd1bGF0ZWQgR2VuZXMgaW4gTDUgdnMgTDYiKQprZWdnX3VwX1AzX0w1TDYgPC0gcGVyZm9ybV9rZWdnX2VucmljaG1lbnQodXByZWd1bGF0ZWRfZ2VuZXNfUDNfTDVMNiwgZ2VuZV91bml2ZXJzZSwgIlVwcmVndWxhdGVkIEdlbmVzIGluIEw1IHZzIEw2IikKa2VnZ19kb3duX1AzX0w1TDYgPC0gcGVyZm9ybV9rZWdnX2VucmljaG1lbnQoZG93bnJlZ3VsYXRlZF9nZW5lc19QM19MNUw2LCBnZW5lX3VuaXZlcnNlLCAiRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBMNSB2cyBMNiIpCgojIEw1IHZzIEw3CnVwcmVndWxhdGVkX2dlbmVzX1AzX0w1TDcgPC0gcm93bmFtZXMocDNfY29tcGFyaXNvbl9MNUw3W3AzX2NvbXBhcmlzb25fTDVMNyRhdmdfbG9nMkZDID4gMSAmIHAzX2NvbXBhcmlzb25fTDVMNyRwX3ZhbF9hZGogPCAwLjA1LCBdKQpkb3ducmVndWxhdGVkX2dlbmVzX1AzX0w1TDcgPC0gcm93bmFtZXMocDNfY29tcGFyaXNvbl9MNUw3W3AzX2NvbXBhcmlzb25fTDVMNyRhdmdfbG9nMkZDIDwgLTEgJiBwM19jb21wYXJpc29uX0w1TDckcF92YWxfYWRqIDwgMC4wNSwgXSkKCmdvX3VwX1AzX0w1TDcgPC0gcGVyZm9ybV9nb19lbnJpY2htZW50KHVwcmVndWxhdGVkX2dlbmVzX1AzX0w1TDcsIGdlbmVfdW5pdmVyc2UsICJVcHJlZ3VsYXRlZCBHZW5lcyBpbiBMNSB2cyBMNyIpCmdvX2Rvd25fUDNfTDVMNyA8LSBwZXJmb3JtX2dvX2VucmljaG1lbnQoZG93bnJlZ3VsYXRlZF9nZW5lc19QM19MNUw3LCBnZW5lX3VuaXZlcnNlLCAiRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBMNSB2cyBMNyIpCmtlZ2dfdXBfUDNfTDVMNyA8LSBwZXJmb3JtX2tlZ2dfZW5yaWNobWVudCh1cHJlZ3VsYXRlZF9nZW5lc19QM19MNUw3LCBnZW5lX3VuaXZlcnNlLCAiVXByZWd1bGF0ZWQgR2VuZXMgaW4gTDUgdnMgTDciKQprZWdnX2Rvd25fUDNfTDVMNyA8LSBwZXJmb3JtX2tlZ2dfZW5yaWNobWVudChkb3ducmVndWxhdGVkX2dlbmVzX1AzX0w1TDcsIGdlbmVfdW5pdmVyc2UsICJEb3ducmVndWxhdGVkIEdlbmVzIGluIEw1IHZzIEw3IikKCiMgTDYgdnMgTDcKdXByZWd1bGF0ZWRfZ2VuZXNfUDNfTDZMNyA8LSByb3duYW1lcyhwM19jb21wYXJpc29uX0w2TDdbcDNfY29tcGFyaXNvbl9MNkw3JGF2Z19sb2cyRkMgPiAxICYgcDNfY29tcGFyaXNvbl9MNkw3JHBfdmFsX2FkaiA8IDAuMDUsIF0pCmRvd25yZWd1bGF0ZWRfZ2VuZXNfUDNfTDZMNyA8LSByb3duYW1lcyhwM19jb21wYXJpc29uX0w2TDdbcDNfY29tcGFyaXNvbl9MNkw3JGF2Z19sb2cyRkMgPCAtMSAmIHAzX2NvbXBhcmlzb25fTDZMNyRwX3ZhbF9hZGogPCAwLjA1LCBdKQoKZ29fdXBfUDNfTDZMNyA8LSBwZXJmb3JtX2dvX2VucmljaG1lbnQodXByZWd1bGF0ZWRfZ2VuZXNfUDNfTDZMNywgZ2VuZV91bml2ZXJzZSwgIlVwcmVndWxhdGVkIEdlbmVzIGluIEw2IHZzIEw3IikKZ29fZG93bl9QM19MNkw3IDwtIHBlcmZvcm1fZ29fZW5yaWNobWVudChkb3ducmVndWxhdGVkX2dlbmVzX1AzX0w2TDcsIGdlbmVfdW5pdmVyc2UsICJEb3ducmVndWxhdGVkIEdlbmVzIGluIEw2IHZzIEw3IikKa2VnZ191cF9QM19MNkw3IDwtIHBlcmZvcm1fa2VnZ19lbnJpY2htZW50KHVwcmVndWxhdGVkX2dlbmVzX1AzX0w2TDcsIGdlbmVfdW5pdmVyc2UsICJVcHJlZ3VsYXRlZCBHZW5lcyBpbiBMNiB2cyBMNyIpCmtlZ2dfZG93bl9QM19MNkw3IDwtIHBlcmZvcm1fa2VnZ19lbnJpY2htZW50KGRvd25yZWd1bGF0ZWRfZ2VuZXNfUDNfTDZMNywgZ2VuZV91bml2ZXJzZSwgIkRvd25yZWd1bGF0ZWQgR2VuZXMgaW4gTDYgdnMgTDciKQoKYGBgCgojIDguIE5ldHdvcmsgQW5hbHlzaXMKYGBge3IgbmV0d29ya19hbmFseXNpcywgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEyfQojIEZ1bmN0aW9uIHRvIGdldCB0b3AgZ2VuZXMgZnJvbSBhIGNvbXBhcmlzb24KCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KFNUUklOR2RiKQpsaWJyYXJ5KGdncmFwaCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGliYmxlKQoKZ2V0X3RvcF9nZW5lcyA8LSBmdW5jdGlvbihjb21wYXJpc29uX3Jlc3VsdCwgbiA9IDUwKSB7CiAgdG9wX2dlbmVzIDwtIGNvbXBhcmlzb25fcmVzdWx0ICU+JQogICAgcm93bmFtZXNfdG9fY29sdW1uKCJnZW5lIikgJT4lCiAgICBhcnJhbmdlKGRlc2MoYWJzKGF2Z19sb2cyRkMpKSkgJT4lCiAgICBoZWFkKG4pICU+JQogICAgcHVsbChnZW5lKQogIHJldHVybih0b3BfZ2VuZXMpCn0KCiMgQ29tYmluZSB0b3AgZ2VuZXMgZnJvbSBhbGwgY29tcGFyaXNvbnMKYWxsX3RvcF9nZW5lcyA8LSB1bmlxdWUoYygKICBnZXRfdG9wX2dlbmVzKHAxX2NvbXBhcmlzb24pLAogIGdldF90b3BfZ2VuZXMocDJfY29tcGFyaXNvbiksCiAgZ2V0X3RvcF9nZW5lcyhwM19jb21wYXJpc29uX0w1TDYpLAogIGdldF90b3BfZ2VuZXMocDNfY29tcGFyaXNvbl9MNUw3KSwKICBnZXRfdG9wX2dlbmVzKHAzX2NvbXBhcmlzb25fTDZMNykKKSkKCiMgSW5pdGlhbGl6ZSBTVFJJTkdkYgpzdHJpbmdfZGIgPC0gU1RSSU5HZGIkbmV3KHZlcnNpb249IjExIiwgc3BlY2llcz05NjA2LCBzY29yZV90aHJlc2hvbGQ9NzAwKQoKIyBNYXAgZ2VuZXMgdG8gU1RSSU5HIGlkZW50aWZpZXJzCm1hcHBlZF9nZW5lcyA8LSBzdHJpbmdfZGIkbWFwKGRhdGEuZnJhbWUoZ2VuZT1hbGxfdG9wX2dlbmVzKSwgImdlbmUiLCByZW1vdmVVbm1hcHBlZFJvd3MgPSBUUlVFKQoKIyBHZXQgaW50ZXJhY3Rpb25zCmludGVyYWN0aW9ucyA8LSBzdHJpbmdfZGIkZ2V0X2ludGVyYWN0aW9ucyhtYXBwZWRfZ2VuZXMkU1RSSU5HX2lkKQoKIyBDcmVhdGUgaWdyYXBoIG9iamVjdApnIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShpbnRlcmFjdGlvbnMsIGRpcmVjdGVkID0gRkFMU0UpCgojIENhbGN1bGF0ZSBub2RlIGRlZ3JlZXMKVihnKSRkZWdyZWUgPC0gZGVncmVlKGcpCgojIENhbGN1bGF0ZSBiZXR3ZWVubmVzcyBjZW50cmFsaXR5ClYoZykkYmV0d2Vlbm5lc3MgPC0gYmV0d2Vlbm5lc3MoZykKCiMgSWRlbnRpZnkgY29tbXVuaXRpZXMKY29tbXVuaXRpZXMgPC0gY2x1c3Rlcl9sb3V2YWluKGcpClYoZykkY29tbXVuaXR5IDwtIGNvbW11bml0aWVzJG1lbWJlcnNoaXAKCiMgUGxvdCB0aGUgbmV0d29yawpzZXQuc2VlZCgxMjMpICAjIGZvciByZXByb2R1Y2liaWxpdHkKZ2dyYXBoKGcsIGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IGNvbWJpbmVkX3Njb3JlKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBmYWN0b3IoY29tbXVuaXR5KSwgc2l6ZSA9IGRlZ3JlZSkpICsKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWwgPSBUUlVFLCBzaXplID0gMykgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArCiAgdGhlbWVfdm9pZCgpICsKICBsYWJzKHRpdGxlID0gIkdlbmUgSW50ZXJhY3Rpb24gTmV0d29yayIsCiAgICAgICBzdWJ0aXRsZSA9ICJCYXNlZCBvbiB0b3AgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIiwKICAgICAgIGNvbG9yID0gIkNvbW11bml0eSIsCiAgICAgICBzaXplID0gIkRlZ3JlZSIpCgojIFNhdmUgdGhlIHBsb3QKZ2dzYXZlKCJnZW5lX2ludGVyYWN0aW9uX25ldHdvcmsucG5nIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCkKCiMgSWRlbnRpZnkgaHViIGdlbmVzCmh1Yl9nZW5lcyA8LSBWKGcpJG5hbWVbb3JkZXIoVihnKSRkZWdyZWUsIGRlY3JlYXNpbmcgPSBUUlVFKV1bMToxMF0KY2F0KCJUb3AgMTAgaHViIGdlbmVzOlxuIikKcHJpbnQoaHViX2dlbmVzKQoKIyBJZGVudGlmeSBnZW5lcyB3aXRoIGhpZ2ggYmV0d2Vlbm5lc3MgY2VudHJhbGl0eQpoaWdoX2JldHdlZW5uZXNzX2dlbmVzIDwtIFYoZykkbmFtZVtvcmRlcihWKGcpJGJldHdlZW5uZXNzLCBkZWNyZWFzaW5nID0gVFJVRSldWzE6MTBdCmNhdCgiXG5Ub3AgMTAgZ2VuZXMgd2l0aCBoaWdoIGJldHdlZW5uZXNzIGNlbnRyYWxpdHk6XG4iKQpwcmludChoaWdoX2JldHdlZW5uZXNzX2dlbmVzKQoKIyBDYWxjdWxhdGUgYW5kIHByaW50IHNvbWUgbmV0d29yayBzdGF0aXN0aWNzCmNhdCgiXG5OZXR3b3JrIFN0YXRpc3RpY3M6XG4iKQpjYXQoIk51bWJlciBvZiBub2RlczoiLCB2Y291bnQoZyksICJcbiIpCmNhdCgiTnVtYmVyIG9mIGVkZ2VzOiIsIGVjb3VudChnKSwgIlxuIikKY2F0KCJOZXR3b3JrIGRlbnNpdHk6IiwgZWRnZV9kZW5zaXR5KGcpLCAiXG4iKQpjYXQoIkF2ZXJhZ2UgcGF0aCBsZW5ndGg6IiwgbWVhbl9kaXN0YW5jZShnKSwgIlxuIikKY2F0KCJDbHVzdGVyaW5nIGNvZWZmaWNpZW50OiIsIHRyYW5zaXRpdml0eShnKSwgIlxuIikKCiMgRXh0cmFjdCBlZGdlIGluZm9ybWF0aW9uCmVkZ2VzX2RmIDwtIGRhdGEuZnJhbWUoCiAgZnJvbSA9IGVuZHMoZywgRShnKSlbLDFdLAogIHRvID0gZW5kcyhnLCBFKGcpKVssMl0KKQoKIyBBZGQgZWRnZSBhdHRyaWJ1dGVzIGlmIGFueQplZGdlX2F0dHJzIDwtIGVkZ2VfYXR0cihnKQpmb3IgKGF0dHIgaW4gbmFtZXMoZWRnZV9hdHRycykpIHsKICBlZGdlc19kZltbYXR0cl1dIDwtIGVkZ2VfYXR0cnNbW2F0dHJdXQp9CgojIFNhdmUgdGhlIGVkZ2VzIGRhdGEgZnJhbWUKd3JpdGUuY3N2KGVkZ2VzX2RmLCAiZ2VuZV9uZXR3b3JrX2VkZ2VzLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBFeHRyYWN0IG5vZGUgaW5mb3JtYXRpb24Kbm9kZXNfZGYgPC0gZGF0YS5mcmFtZSgKICBpZCA9IFYoZykkbmFtZSwKICBkZWdyZWUgPSBkZWdyZWUoZyksCiAgYmV0d2Vlbm5lc3MgPSBiZXR3ZWVubmVzcyhnKQopCgojIEFkZCBvdGhlciB2ZXJ0ZXggYXR0cmlidXRlcyBpZiBhbnkKdmVydGV4X2F0dHJzIDwtIHZlcnRleF9hdHRyKGcpCmZvciAoYXR0ciBpbiBuYW1lcyh2ZXJ0ZXhfYXR0cnMpKSB7CiAgaWYgKGF0dHIgIT0gIm5hbWUiKSB7ICAjIFNraXAgJ25hbWUnIGFzIHdlIGFscmVhZHkgaGF2ZSBpdCBhcyAnaWQnCiAgICBub2Rlc19kZltbYXR0cl1dIDwtIHZlcnRleF9hdHRyc1tbYXR0cl1dCiAgfQp9CgojIFNhdmUgdGhlIG5vZGVzIGRhdGEgZnJhbWUKd3JpdGUuY3N2KG5vZGVzX2RmLCAiZ2VuZV9uZXR3b3JrX25vZGVzLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBQcmludCBhIHN1bW1hcnkgb2YgdGhlIHNhdmVkIGRhdGEKY2F0KCJTYXZlZCBuZXR3b3JrIGRhdGE6XG4iKQpjYXQoIkVkZ2VzIGZpbGU6IGdlbmVfbmV0d29ya19lZGdlcy5jc3YgLSIsIG5yb3coZWRnZXNfZGYpLCAicm93c1xuIikKY2F0KCJOb2RlcyBmaWxlOiBnZW5lX25ldHdvcmtfbm9kZXMuY3N2IC0iLCBucm93KG5vZGVzX2RmKSwgInJvd3NcbiIpCmBgYAoKYGBge3IgbmV0d29ya19hbmFseXNpczIsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMn0KIyBGdW5jdGlvbiB0byBnZXQgdG9wIGdlbmVzIGZyb20gYSBjb21wYXJpc29uCgpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShTVFJJTkdkYikKbGlicmFyeShnZ3JhcGgpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRpYmJsZSkKCmdldF90b3BfZ2VuZXMgPC0gZnVuY3Rpb24oY29tcGFyaXNvbl9yZXN1bHQsIG4gPSA1MCkgewogIHRvcF9nZW5lcyA8LSBjb21wYXJpc29uX3Jlc3VsdCAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZSIpICU+JQogICAgYXJyYW5nZShkZXNjKGFicyhhdmdfbG9nMkZDKSkpICU+JQogICAgaGVhZChuKSAlPiUKICAgIHB1bGwoZ2VuZSkKICByZXR1cm4odG9wX2dlbmVzKQp9CgojIENvbWJpbmUgdG9wIGdlbmVzIGZyb20gYWxsIGNvbXBhcmlzb25zCmFsbF90b3BfZ2VuZXMgPC0gdW5pcXVlKGMoCiAgZ2V0X3RvcF9nZW5lcyhwMV9jb21wYXJpc29uKSwKICBnZXRfdG9wX2dlbmVzKHAyX2NvbXBhcmlzb24pLAogIGdldF90b3BfZ2VuZXMocDNfY29tcGFyaXNvbl9MNUw2KSwKICBnZXRfdG9wX2dlbmVzKHAzX2NvbXBhcmlzb25fTDVMNyksCiAgZ2V0X3RvcF9nZW5lcyhwM19jb21wYXJpc29uX0w2TDcpCikpCgojIEluaXRpYWxpemUgU1RSSU5HZGIKc3RyaW5nX2RiIDwtIFNUUklOR2RiJG5ldyh2ZXJzaW9uPSIxMSIsIHNwZWNpZXM9OTYwNiwgc2NvcmVfdGhyZXNob2xkPTcwMCkKCiMgTWFwIGdlbmVzIHRvIFNUUklORyBpZGVudGlmaWVycwptYXBwZWRfZ2VuZXMgPC0gc3RyaW5nX2RiJG1hcChkYXRhLmZyYW1lKGdlbmU9YWxsX3RvcF9nZW5lcyksICJnZW5lIiwgcmVtb3ZlVW5tYXBwZWRSb3dzID0gVFJVRSkKCiMgR2V0IGludGVyYWN0aW9ucwppbnRlcmFjdGlvbnMgPC0gc3RyaW5nX2RiJGdldF9pbnRlcmFjdGlvbnMobWFwcGVkX2dlbmVzJFNUUklOR19pZCkKCiMgTWFwIFNUUklORyBpZGVudGlmaWVycyBiYWNrIHRvIGdlbmUgc3ltYm9scwppbnRlcmFjdGlvbnMkZnJvbSA8LSBtYXBwZWRfZ2VuZXMkZ2VuZVttYXRjaChpbnRlcmFjdGlvbnMkZnJvbSwgbWFwcGVkX2dlbmVzJFNUUklOR19pZCldCmludGVyYWN0aW9ucyR0byA8LSBtYXBwZWRfZ2VuZXMkZ2VuZVttYXRjaChpbnRlcmFjdGlvbnMkdG8sIG1hcHBlZF9nZW5lcyRTVFJJTkdfaWQpXQoKIyBDcmVhdGUgaWdyYXBoIG9iamVjdApnIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShpbnRlcmFjdGlvbnMsIGRpcmVjdGVkID0gRkFMU0UpCgojIENhbGN1bGF0ZSBub2RlIGRlZ3JlZXMKVihnKSRkZWdyZWUgPC0gZGVncmVlKGcpCgojIENhbGN1bGF0ZSBiZXR3ZWVubmVzcyBjZW50cmFsaXR5ClYoZykkYmV0d2Vlbm5lc3MgPC0gYmV0d2Vlbm5lc3MoZykKCiMgSWRlbnRpZnkgY29tbXVuaXRpZXMKY29tbXVuaXRpZXMgPC0gY2x1c3Rlcl9sb3V2YWluKGcpClYoZykkY29tbXVuaXR5IDwtIGNvbW11bml0aWVzJG1lbWJlcnNoaXAKCiMgUGxvdCB0aGUgbmV0d29yawpzZXQuc2VlZCgxMjMpICAjIGZvciByZXByb2R1Y2liaWxpdHkKZ2dyYXBoKGcsIGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IGNvbWJpbmVkX3Njb3JlKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBmYWN0b3IoY29tbXVuaXR5KSwgc2l6ZSA9IGRlZ3JlZSkpICsKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWwgPSBUUlVFLCBzaXplID0gMykgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArCiAgdGhlbWVfdm9pZCgpICsKICBsYWJzKHRpdGxlID0gIkdlbmUgSW50ZXJhY3Rpb24gTmV0d29yayIsCiAgICAgICBzdWJ0aXRsZSA9ICJCYXNlZCBvbiB0b3AgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIiwKICAgICAgIGNvbG9yID0gIkNvbW11bml0eSIsCiAgICAgICBzaXplID0gIkRlZ3JlZSIpCgojIFNhdmUgdGhlIHBsb3QKZ2dzYXZlKCJnZW5lX2ludGVyYWN0aW9uX25ldHdvcmsucG5nIiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCkKCiMgSWRlbnRpZnkgaHViIGdlbmVzCmh1Yl9nZW5lcyA8LSBWKGcpJG5hbWVbb3JkZXIoVihnKSRkZWdyZWUsIGRlY3JlYXNpbmcgPSBUUlVFKV1bMToxMF0KY2F0KCJUb3AgMTAgaHViIGdlbmVzOlxuIikKcHJpbnQoaHViX2dlbmVzKQoKIyBJZGVudGlmeSBnZW5lcyB3aXRoIGhpZ2ggYmV0d2Vlbm5lc3MgY2VudHJhbGl0eQpoaWdoX2JldHdlZW5uZXNzX2dlbmVzIDwtIFYoZykkbmFtZVtvcmRlcihWKGcpJGJldHdlZW5uZXNzLCBkZWNyZWFzaW5nID0gVFJVRSldWzE6MTBdCmNhdCgiXG5Ub3AgMTAgZ2VuZXMgd2l0aCBoaWdoIGJldHdlZW5uZXNzIGNlbnRyYWxpdHk6XG4iKQpwcmludChoaWdoX2JldHdlZW5uZXNzX2dlbmVzKQoKIyBDYWxjdWxhdGUgYW5kIHByaW50IHNvbWUgbmV0d29yayBzdGF0aXN0aWNzCmNhdCgiXG5OZXR3b3JrIFN0YXRpc3RpY3M6XG4iKQpjYXQoIk51bWJlciBvZiBub2RlczoiLCB2Y291bnQoZyksICJcbiIpCmNhdCgiTnVtYmVyIG9mIGVkZ2VzOiIsIGVjb3VudChnKSwgIlxuIikKY2F0KCJOZXR3b3JrIGRlbnNpdHk6IiwgZWRnZV9kZW5zaXR5KGcpLCAiXG4iKQpjYXQoIkF2ZXJhZ2UgcGF0aCBsZW5ndGg6IiwgbWVhbl9kaXN0YW5jZShnKSwgIlxuIikKY2F0KCJDbHVzdGVyaW5nIGNvZWZmaWNpZW50OiIsIHRyYW5zaXRpdml0eShnKSwgIlxuIikKCiMgRXh0cmFjdCBlZGdlIGluZm9ybWF0aW9uCmVkZ2VzX2RmIDwtIGRhdGEuZnJhbWUoCiAgZnJvbSA9IGVuZHMoZywgRShnKSlbLDFdLAogIHRvID0gZW5kcyhnLCBFKGcpKVssMl0KKQoKIyBBZGQgZWRnZSBhdHRyaWJ1dGVzIGlmIGFueQplZGdlX2F0dHJzIDwtIGVkZ2VfYXR0cihnKQpmb3IgKGF0dHIgaW4gbmFtZXMoZWRnZV9hdHRycykpIHsKICBlZGdlc19kZltbYXR0cl1dIDwtIGVkZ2VfYXR0cnNbW2F0dHJdXQp9CgojIFNhdmUgdGhlIGVkZ2VzIGRhdGEgZnJhbWUKd3JpdGUuY3N2KGVkZ2VzX2RmLCAiZ2VuZV9uZXR3b3JrX2VkZ2VzLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBFeHRyYWN0IG5vZGUgaW5mb3JtYXRpb24Kbm9kZXNfZGYgPC0gZGF0YS5mcmFtZSgKICBpZCA9IFYoZykkbmFtZSwKICBkZWdyZWUgPSBkZWdyZWUoZyksCiAgYmV0d2Vlbm5lc3MgPSBiZXR3ZWVubmVzcyhnKQopCgojIEFkZCBvdGhlciB2ZXJ0ZXggYXR0cmlidXRlcyBpZiBhbnkKdmVydGV4X2F0dHJzIDwtIHZlcnRleF9hdHRyKGcpCmZvciAoYXR0ciBpbiBuYW1lcyh2ZXJ0ZXhfYXR0cnMpKSB7CiAgaWYgKGF0dHIgIT0gIm5hbWUiKSB7ICAjIFNraXAgJ25hbWUnIGFzIHdlIGFscmVhZHkgaGF2ZSBpdCBhcyAnaWQnCiAgICBub2Rlc19kZltbYXR0cl1dIDwtIHZlcnRleF9hdHRyc1tbYXR0cl1dCiAgfQp9CgojIFNhdmUgdGhlIG5vZGVzIGRhdGEgZnJhbWUKd3JpdGUuY3N2KG5vZGVzX2RmLCAiZ2VuZV9uZXR3b3JrX25vZGVzLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBQcmludCBhIHN1bW1hcnkgb2YgdGhlIHNhdmVkIGRhdGEKY2F0KCJTYXZlZCBuZXR3b3JrIGRhdGE6XG4iKQpjYXQoIkVkZ2VzIGZpbGU6IGdlbmVfbmV0d29ya19lZGdlcy5jc3YgLSIsIG5yb3coZWRnZXNfZGYpLCAicm93c1xuIikKY2F0KCJOb2RlcyBmaWxlOiBnZW5lX25ldHdvcmtfbm9kZXMuY3N2IC0iLCBucm93KG5vZGVzX2RmKSwgInJvd3NcbiIpCgpgYGAKCgojIDkuIE5ldHdvcmsgQW5hbHlzaXMta2VnZwpgYGB7ciBuZXR3b3JrX2FuYWx5c2lzMywgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEyfQoKIyBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcwpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShnZ3JhcGgpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShvcmcuSHMuZWcuZGIpCmxpYnJhcnkoR08uZGIpCmxpYnJhcnkoQW5ub3RhdGlvbkRiaSkKbGlicmFyeShkcGx5cikKCiMgRnVuY3Rpb24gdG8gZ2V0IHRvcCBnZW5lcyBmcm9tIGNvbXBhcmlzb24gcmVzdWx0cwpnZXRfdG9wX2dlbmVzIDwtIGZ1bmN0aW9uKGNvbXBhcmlzb25fcmVzdWx0LCBuID0gNTApIHsKICB0b3BfZ2VuZXMgPC0gY29tcGFyaXNvbl9yZXN1bHQgJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZSIpICU+JQogICAgZHBseXI6OmFycmFuZ2UoZGVzYyhhYnMoYXZnX2xvZzJGQykpKSAlPiUKICAgIGhlYWQobikgJT4lCiAgICBkcGx5cjo6cHVsbChnZW5lKQogIHJldHVybih0b3BfZ2VuZXMpCn0KCiMgQ29tYmluZSB0b3AgZ2VuZXMgZnJvbSBhbGwgY29tcGFyaXNvbnMKYWxsX3RvcF9nZW5lcyA8LSB1bmlxdWUoYygKICBnZXRfdG9wX2dlbmVzKHAxX2NvbXBhcmlzb24pLAogIGdldF90b3BfZ2VuZXMocDJfY29tcGFyaXNvbiksCiAgZ2V0X3RvcF9nZW5lcyhwM19jb21wYXJpc29uX0w1TDYpLAogIGdldF90b3BfZ2VuZXMocDNfY29tcGFyaXNvbl9MNUw3KSwKICBnZXRfdG9wX2dlbmVzKHAzX2NvbXBhcmlzb25fTDZMNykKKSkKCiMgR2V0IEdPIHRlcm1zIGZvciBhbGwgdG9wIGdlbmVzCmdvX3Rlcm1zIDwtIEFubm90YXRpb25EYmk6OnNlbGVjdChvcmcuSHMuZWcuZGIsIGtleXMgPSBhbGxfdG9wX2dlbmVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbnMgPSBjKCJTWU1CT0wiLCAiR08iLCAiT05UT0xPR1kiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXl0eXBlID0gIlNZTUJPTCIpCgojIEZpbHRlciBmb3IgYmlvbG9naWNhbCBwcm9jZXNzIEdPIHRlcm1zIGFuZCByZW1vdmUgTkEgdmFsdWVzCmdvX3Rlcm1zX2JwIDwtIGdvX3Rlcm1zICU+JQogIGRwbHlyOjpmaWx0ZXIoT05UT0xPR1kgPT0gIkJQIikgJT4lCiAgZHBseXI6OmZpbHRlcighaXMubmEoR08pKQoKIyBDcmVhdGUgZWRnZXMgZGF0YWZyYW1lCmVkZ2VzIDwtIGdvX3Rlcm1zX2JwICU+JQogIGRwbHlyOjpzZWxlY3QoZnJvbSA9IFNZTUJPTCwgdG8gPSBHTykKCiMgUHJpbnQgc3VtbWFyeSBvZiBlZGdlcwpwcmludChwYXN0ZSgiTnVtYmVyIG9mIGVkZ2VzOiIsIG5yb3coZWRnZXMpKSkKcHJpbnQoaGVhZChlZGdlcykpCgojIElmIGVkZ2VzIGRhdGFmcmFtZSBpcyBlbXB0eSwgc3RvcCBoZXJlCmlmIChucm93KGVkZ2VzKSA9PSAwKSB7CiAgc3RvcCgiTm8gR08gdGVybXMgZm91bmQgZm9yIGFueSBnZW5lcy4gQ2Fubm90IGNyZWF0ZSBuZXR3b3JrLiIpCn0KCiMgQ3JlYXRlIGdyYXBoCmcgPC0gaWdyYXBoOjpncmFwaF9mcm9tX2RhdGFfZnJhbWUoZWRnZXMsIGRpcmVjdGVkID0gRkFMU0UpCgojIENhbGN1bGF0ZSBub2RlIGRlZ3JlZXMKVihnKSRkZWdyZWUgPC0gaWdyYXBoOjpkZWdyZWUoZykKCiMgQ2FsY3VsYXRlIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkKVihnKSRiZXR3ZWVubmVzcyA8LSBpZ3JhcGg6OmJldHdlZW5uZXNzKGcpCgojIElkZW50aWZ5IGNvbW11bml0aWVzCmNvbW11bml0aWVzIDwtIGlncmFwaDo6Y2x1c3Rlcl9sb3V2YWluKGcpClYoZykkY29tbXVuaXR5IDwtIGNvbW11bml0aWVzJG1lbWJlcnNoaXAKCiMgR2V0IEdPIHRlcm0gZGVzY3JpcHRpb25zCmdvX3Rlcm1zX2Rlc2MgPC0gQW5ub3RhdGlvbkRiaTo6c2VsZWN0KEdPLmRiLCBrZXlzID0gdW5pcXVlKGVkZ2VzJHRvKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbnMgPSAiVEVSTSIsIGtleXR5cGUgPSAiR09JRCIpCgojIEFkZCBHTyB0ZXJtIGRlc2NyaXB0aW9ucyB0byB0aGUgZ3JhcGgKVihnKSRkZXNjcmlwdGlvbiA8LSBnb190ZXJtc19kZXNjJFRFUk1bbWF0Y2goVihnKSRuYW1lLCBnb190ZXJtc19kZXNjJEdPSUQpXQoKIyBQbG90IHRoZSBuZXR3b3JrCnNldC5zZWVkKDEyMykgICMgZm9yIHJlcHJvZHVjaWJpbGl0eQpwIDwtIGdncmFwaChnLCBsYXlvdXQgPSAiZnIiKSArCiAgZ2VvbV9lZGdlX2xpbmsoYWxwaGEgPSAwLjEpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gZmFjdG9yKGNvbW11bml0eSksIHNpemUgPSBkZWdyZWUpKSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gaWZlbHNlKGRlZ3JlZSA+IHF1YW50aWxlKGRlZ3JlZSwgMC45NSksIG5hbWUsICIiKSksIAogICAgICAgICAgICAgICAgIHJlcGVsID0gVFJVRSwgc2l6ZSA9IDMpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgbGFicyh0aXRsZSA9ICJHZW5lLUdPIFRlcm0gSW50ZXJhY3Rpb24gTmV0d29yayIsCiAgICAgICBzdWJ0aXRsZSA9ICJCYXNlZCBvbiB0b3AgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIiwKICAgICAgIGNvbG9yID0gIkNvbW11bml0eSIsCiAgICAgICBzaXplID0gIkRlZ3JlZSIpCgojIFNhdmUgdGhlIHBsb3QKZ2dzYXZlKCJnZW5lX2dvX25ldHdvcmsucG5nIiwgcCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCkKCiMgSWRlbnRpZnkgaHViIGdlbmVzCmh1Yl9nZW5lcyA8LSBWKGcpJG5hbWVbVihnKSRuYW1lICVpbiUgYWxsX3RvcF9nZW5lc11bb3JkZXIoVihnKSRkZWdyZWVbVihnKSRuYW1lICVpbiUgYWxsX3RvcF9nZW5lc10sIGRlY3JlYXNpbmcgPSBUUlVFKV1bMToxMF0KY2F0KCJUb3AgMTAgaHViIGdlbmVzOlxuIikKcHJpbnQoaHViX2dlbmVzKQoKIyBJZGVudGlmeSBnZW5lcyB3aXRoIGhpZ2ggYmV0d2Vlbm5lc3MgY2VudHJhbGl0eQpoaWdoX2JldHdlZW5uZXNzX2dlbmVzIDwtIFYoZykkbmFtZVtWKGcpJG5hbWUgJWluJSBhbGxfdG9wX2dlbmVzXVtvcmRlcihWKGcpJGJldHdlZW5uZXNzW1YoZykkbmFtZSAlaW4lIGFsbF90b3BfZ2VuZXNdLCBkZWNyZWFzaW5nID0gVFJVRSldWzE6MTBdCmNhdCgiXG5Ub3AgMTAgZ2VuZXMgd2l0aCBoaWdoIGJldHdlZW5uZXNzIGNlbnRyYWxpdHk6XG4iKQpwcmludChoaWdoX2JldHdlZW5uZXNzX2dlbmVzKQoKIyBDYWxjdWxhdGUgYW5kIHByaW50IHNvbWUgbmV0d29yayBzdGF0aXN0aWNzCmNhdCgiXG5OZXR3b3JrIFN0YXRpc3RpY3M6XG4iKQpjYXQoIk51bWJlciBvZiBub2RlczoiLCBpZ3JhcGg6OnZjb3VudChnKSwgIlxuIikKY2F0KCJOdW1iZXIgb2YgZWRnZXM6IiwgaWdyYXBoOjplY291bnQoZyksICJcbiIpCmNhdCgiTmV0d29yayBkZW5zaXR5OiIsIGlncmFwaDo6ZWRnZV9kZW5zaXR5KGcpLCAiXG4iKQpjYXQoIkF2ZXJhZ2UgcGF0aCBsZW5ndGg6IiwgaWdyYXBoOjptZWFuX2Rpc3RhbmNlKGcpLCAiXG4iKQpjYXQoIkNsdXN0ZXJpbmcgY29lZmZpY2llbnQ6IiwgaWdyYXBoOjp0cmFuc2l0aXZpdHkoZyksICJcbiIpCgojIEV4dHJhY3QgZWRnZSBpbmZvcm1hdGlvbgplZGdlc19kZiA8LSBpZ3JhcGg6OmFzX2RhdGFfZnJhbWUoZywgd2hhdCA9ICJlZGdlcyIpCgojIFNhdmUgdGhlIGVkZ2VzIGRhdGEgZnJhbWUKd3JpdGUuY3N2KGVkZ2VzX2RmLCAiZ2VuZV9nb19lZGdlcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgRXh0cmFjdCBub2RlIGluZm9ybWF0aW9uCm5vZGVzX2RmIDwtIGlncmFwaDo6YXNfZGF0YV9mcmFtZShnLCB3aGF0ID0gInZlcnRpY2VzIikKCiMgU2F2ZSB0aGUgbm9kZXMgZGF0YSBmcmFtZQp3cml0ZS5jc3Yobm9kZXNfZGYsICJnZW5lX2dvX25vZGVzLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBQcmludCBhIHN1bW1hcnkgb2YgdGhlIHNhdmVkIGRhdGEKY2F0KCJTYXZlZCBuZXR3b3JrIGRhdGE6XG4iKQpjYXQoIkVkZ2VzIGZpbGU6IGdlbmVfZ29fZWRnZXMuY3N2IC0iLCBucm93KGVkZ2VzX2RmKSwgInJvd3NcbiIpCmNhdCgiTm9kZXMgZmlsZTogZ2VuZV9nb19ub2Rlcy5jc3YgLSIsIG5yb3cobm9kZXNfZGYpLCAicm93c1xuIikKCiMgUHJpbnQgdG9wIEdPIHRlcm1zCnRvcF9nb190ZXJtcyA8LSBWKGcpJG5hbWVbIShWKGcpJG5hbWUgJWluJSBhbGxfdG9wX2dlbmVzKV1bb3JkZXIoVihnKSRkZWdyZWVbIShWKGcpJG5hbWUgJWluJSBhbGxfdG9wX2dlbmVzKV0sIGRlY3JlYXNpbmcgPSBUUlVFKV1bMToyMF0KY2F0KCJcblRvcCAyMCBHTyB0ZXJtczpcbiIpCmZvciAodGVybSBpbiB0b3BfZ29fdGVybXMpIHsKICBjYXQodGVybSwgIi0iLCBWKGcpJGRlc2NyaXB0aW9uW1YoZykkbmFtZSA9PSB0ZXJtXSwgIlxuIikKfQpgYGAKCiMgMTAuIFNhdmUgdGhlIFNldXJhdCBvYmplY3QgYXMgYW4gUm9iaiBmaWxlCmBgYHtyIHNhdmVST0JKfQoKCiNzYXZlKEFsbF9zYW1wbGVzX01lcmdlZCwgZmlsZSA9ICJBbGxfc2FtcGxlc19NZXJnZWRfREUuUm9iaiIpCgoKYGBgCgoKCgo=