1. load libraries
3. Create the EnhancedVolcano plot
EnhancedVolcano(Malignant_CD4Tcells_vs_Normal_CD4Tcells,
lab = Malignant_CD4Tcells_vs_Normal_CD4Tcells$gene,
x = "avg_log2FC",
y = "p_val_adj",
title = "MAST with Batch Correction (All Genes)",
pCutoff = 0.05,
FCcutoff = 1.0)
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...

EnhancedVolcano(Malignant_CD4Tcells_vs_Normal_CD4Tcells,
lab = Malignant_CD4Tcells_vs_Normal_CD4Tcells$gene,
x = "avg_log2FC",
y = "p_val_adj",
selectLab = c('EPCAM', 'BCAT1', 'KIR3DL2', 'FOXM1', 'TWIST1', 'TNFSF9',
'CD80', 'IL1B', 'RPS4Y1',
'IL7R', 'TCF7', 'MKI67', 'CD70',
'IL2RA','TRBV6-2', 'TRBV10-3', 'TRBV4-2', 'TRBV9', 'TRBV7-9',
'TRAV12-1', 'CD8B', 'FCGR3A', 'GNLY', 'FOXP3', 'SELL',
'GIMAP1', 'RIPOR2', 'LEF1', 'HOXC9', 'SP5',
'CCL17', 'ETV4', 'THY1', 'FOXA2', 'ITGAD', 'S100P', 'TBX4',
'ID1', 'XCL1', 'SOX2', 'CD27', 'CD28','PLS3','CD70','RAB25' , 'TRBV27', 'TRBV2'),
title = "Malignant CD4 T cells(cell lines) vs normal CD4 T cells",
xlab = bquote(~Log[2]~ 'fold change'),
pCutoff = 0.05,
FCcutoff = 1.5,
pointSize = 3.0,
labSize = 5.0,
boxedLabels = TRUE,
colAlpha = 0.5,
legendPosition = 'right',
legendLabSize = 10,
legendIconSize = 4.0,
drawConnectors = TRUE,
widthConnectors = 0.5,
colConnectors = 'grey50',
arrowheads = FALSE,
max.overlaps = 30)
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...

library(dplyr)
library(EnhancedVolcano)
# Assuming you have a data frame named Malignant_CD4Tcells_vs_Normal_CD4Tcells
# Filter genes based on lowest p-values but include all genes
filtered_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells %>%
arrange(p_val_adj, desc(abs(avg_log2FC)))
# Create the EnhancedVolcano plot with the filtered data
EnhancedVolcano(
filtered_genes,
lab = ifelse(filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_log2FC) >= 1.0, filtered_genes$gene, NA),
x = "avg_log2FC",
y = "p_val_adj",
title = "Malignant CD4 T cells(cell lines) vs normal CD4 T cells",
pCutoff = 0.05,
FCcutoff = 1.0,
legendPosition = 'right',
labCol = 'black',
labFace = 'bold',
boxedLabels = FALSE, # Set to FALSE to remove boxed labels
pointSize = 3.0,
labSize = 5.0,
col = c('grey70', 'black', 'blue', 'red'), # Customize point colors
selectLab = filtered_genes$gene[filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_log2FC) >= 1.0] # Only label significant genes
)
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...

EnhancedVolcano(
filtered_genes,
lab = ifelse(filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_log2FC) >= 1.0, filtered_genes$gene, NA),
x = "avg_log2FC",
y = "p_val_adj",
title = "Malignant CD4 T cells (cell lines) vs Normal CD4 T cells",
subtitle = "Highlighting differentially expressed genes",
pCutoff = 0.05,
FCcutoff = 1.0,
legendPosition = 'right',
colAlpha = 0.8, # Slight transparency for non-significant points
col = c('grey70', 'black', 'blue', 'red'), # Custom color scheme
gridlines.major = TRUE,
gridlines.minor = FALSE,
selectLab = filtered_genes$gene[filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_log2FC) >= 1.0]
)
Avis : One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...

5. Create the Heatmap of fgsea results
library(pheatmap)
# Select the top 50 pathways
top_pathways <- fgsea_result %>%
arrange(padj) %>%
head(50)
# Create a matrix for the heatmap with pathways as rows and NES as the values
heatmap_data <- matrix(top_pathways$NES, nrow = length(top_pathways$pathway), ncol = 1)
rownames(heatmap_data) <- top_pathways$pathway
colnames(heatmap_data) <- c("NES")
# Plot the combined heatmap for the top 50 pathways
pheatmap(heatmap_data,
cluster_rows = TRUE,
cluster_cols = FALSE,
show_rownames = TRUE,
show_colnames = TRUE,
main = "Hallmark Pathways: Malignant CD4 T Cells compared to normal CD4 T cells",
color = colorRampPalette(c("blue", "white", "red"))(50))

6. Obtain KEGG Gene Sets and Perform Fast GSEA Using KEGG
Pathways
library(fgsea)
library(msigdbr)
library(dplyr)
library(pheatmap)
# Obtain KEGG gene sets from msigdbr
kegg_genes <- msigdbr(species = "Homo sapiens", category = "C2", subcategory = "CP:KEGG")
# Convert the gene sets to a list format for fgsea
kegg_list <- kegg_genes %>%
split(x = .$gene_symbol, f = .$gs_name)
# Assuming you have a data frame named Malignant_CD4Tcells_vs_Normal_CD4Tcells
# Create a ranked list based on avg_log2FC and p_val_adj
Malignant_CD4Tcells_vs_Normal_CD4Tcells <- Malignant_CD4Tcells_vs_Normal_CD4Tcells %>%
mutate(rank_metric = avg_log2FC * -log10(p_val_adj))
# Ensure no NA values in rank_metric
Malignant_CD4Tcells_vs_Normal_CD4Tcells <- Malignant_CD4Tcells_vs_Normal_CD4Tcells %>%
filter(!is.na(rank_metric))
# Create a named vector for ranking
gene_list <- Malignant_CD4Tcells_vs_Normal_CD4Tcells$rank_metric
names(gene_list) <- Malignant_CD4Tcells_vs_Normal_CD4Tcells$gene
# Sort the named vector in decreasing order
gene_list <- sort(gene_list, decreasing = TRUE)
gene_list <- gene_list[is.finite(gene_list)]
# Perform fast GSEA using KEGG pathways
fgsea_result_kegg <- fgsea(pathways = kegg_list,
stats = gene_list,
minSize = 10,
maxSize = 500,
nperm = 10000) # Number of permutations
Avis : You are trying to run fgseaSimple. It is recommended to use fgseaMultilevel. To run fgseaMultilevel, you need to remove the nperm argument in the fgsea function call.
# View the fgsea results
head(fgsea_result_kegg)
#Filter the results table to show only the top 10 UP or DOWN regulated processes (Optional)
top10_UP_kegg <- fgsea_result_kegg$pathways[1:10]
#summary Table
plotGseaTable(kegg_list[top10_UP_kegg], gene_list, fgsea_result_kegg, gseaParam = 0.5)

# Separate upregulated (positive NES) and downregulated (negative NES) pathways
upregulated_pathways <- fgsea_result_kegg %>% filter(NES > 0) %>% arrange(padj) %>% head(20)
downregulated_pathways <- fgsea_result_kegg %>% filter(NES < 0) %>% arrange(padj) %>% head(20)
# Combine the top 20 upregulated and top 20 downregulated pathways
combined_pathways <- bind_rows(upregulated_pathways, downregulated_pathways)
# Create a matrix for the heatmap with pathways as rows and NES as the values
heatmap_data_combined <- matrix(combined_pathways$NES, nrow = length(combined_pathways$pathway), ncol = 1)
rownames(heatmap_data_combined) <- combined_pathways$pathway
colnames(heatmap_data_combined) <- c("NES")
# Plot the combined heatmap for the top 20 upregulated and top 20 downregulated pathways
pheatmap(heatmap_data_combined,
cluster_rows = TRUE,
cluster_cols = FALSE,
show_rownames = TRUE,
show_colnames = TRUE,
main = "KEGG Pathways: Malignant CD4 T Cells compared to normal CD4 T Cells",
color = colorRampPalette(c("blue", "white", "red"))(50))

. Visualization-Hallmark
fgseaResTidy <- fgsea_result %>%
as_tibble() %>%
arrange(desc(NES))
# Show in a nice table:
fgseaResTidy %>%
dplyr::select(-leadingEdge, -ES, -nMoreExtreme) %>%
arrange(padj) %>%
DT::datatable()
ggplot(fgseaResTidy, aes(reorder(pathway, NES), NES)) +
geom_col(aes(fill=padj<0.05)) +
coord_flip() +
labs(x="Pathway", y="Normalized Enrichment Score",
title="Hallmark pathways NES from GSEA") +
theme_minimal()+ scale_fill_manual(values = c("TRUE" = "red", "FALSE" = "grey"))

NA
NA
NA
. Visualization-Kegg1
fgseaResTidy <- fgsea_result_kegg %>%
as_tibble() %>%
arrange(desc(NES))
# Show in a nice table:
fgseaResTidy %>%
dplyr::select(-leadingEdge, -ES, -nMoreExtreme) %>%
arrange(padj) %>%
DT::datatable()
ggplot(fgseaResTidy, aes(reorder(pathway, NES), NES)) +
geom_col(aes(fill=padj<0.05)) +
coord_flip() +
labs(x="Pathway", y="Normalized Enrichment Score",
title="KEGG pathways NES from GSEA") +
theme_minimal()

NA
NA
NA
. Visualization-Kegg2
# Arrange by NES and select top 20 up and down pathways
topUp <- fgseaResTidy %>%
dplyr::filter(NES > 0) %>%
dplyr::arrange(desc(NES)) %>%
dplyr::slice_head(n = 20)
topDown <- fgseaResTidy %>%
dplyr::filter(NES < 0) %>%
dplyr::arrange(NES) %>%
dplyr::slice_head(n = 20)
# Combine the top up and down pathways
topPathways <- dplyr::bind_rows(topUp, topDown)
ggplot(topPathways, aes(reorder(pathway, NES), NES)) +
geom_col(aes(fill = padj < 0.05)) +
coord_flip() +
labs(x = "Pathway", y = "Normalized Enrichment Score",
title = "Top 20 Up and Down KEGG Pathways NES from GSEA") +
theme_minimal() +
scale_fill_manual(values = c("TRUE" = "red", "FALSE" = "grey"))

NA
NA
NA
NA
7. Save Hallmark and kegg to CSV
# Assuming you have the results stored in fgsea_result_hallmark and fgsea_result_kegg
# Flatten the list columns into character strings for Hallmark results
fgsea_result_hallmark_flattened <- fgsea_result %>%
mutate(across(where(is.list), ~ sapply(., toString)))
# Write the flattened Hallmark results to a CSV file
write.csv(fgsea_result_hallmark_flattened, "fgsea_results_hallmark.csv", row.names = FALSE)
# Flatten the list columns into character strings for KEGG results
fgsea_result_kegg_flattened <- fgsea_result_kegg %>%
mutate(across(where(is.list), ~ sapply(., toString)))
# Write the flattened KEGG results to a CSV file
write.csv(fgsea_result_kegg_flattened, "fgsea_results_kegg.csv", row.names = FALSE)
LS0tCnRpdGxlOiAiRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQW5hbHlzaXMgb2YgTWFsaWduYW50IENENFRjZWxscyB2cyBDb250cm9sKE5vcm1hbCBDRDQgVGNlbGxzKSIKYXV0aG9yOiBOYXNpciBNYWhtb29kIEFiYmFzaQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAjcm1kZm9ybWF0czo6cmVhZHRoZWRvd24KICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19jb2xsYXBzZWQ6IHRydWUKLS0tCgojIDEuIGxvYWQgbGlicmFyaWVzCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShTZXVyYXRPYmplY3QpCmxpYnJhcnkoU2V1cmF0RGF0YSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoaGFybW9ueSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkocmV0aWN1bGF0ZSkKbGlicmFyeShBemltdXRoKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KFJ0c25lKQpsaWJyYXJ5KGhhcm1vbnkpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKfSkKICAKYGBgCgojIDIuIFBlcmZvcm0gREUgYW5hbHlzaXMgdXNpbmcgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzIGdlbmVzCmBgYHtyIGRhdGExLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCk1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyA8LSByZWFkLmNzdigiMS1NQVNUX3dpdGhfU0NUX2JhdGNoX3BhdGllbnRfY2VsbGxpbmVfYXNfQ292YXJpYXRlX3dpdGhfbWVhbkV4cHJlc3Npb24uY3N2IiwgaGVhZGVyID0gVCkKYGBgCgojIDMuIENyZWF0ZSB0aGUgRW5oYW5jZWRWb2xjYW5vIHBsb3QKYGBge3IgZW5oYW5jZWRWLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CgpFbmhhbmNlZFZvbGNhbm8oTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzLAogICAgICAgICAgICAgICAgbGFiID0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGdlbmUsCiAgICAgICAgICAgICAgICB4ID0gImF2Z19sb2cyRkMiLAogICAgICAgICAgICAgICAgeSA9ICJwX3ZhbF9hZGoiLAogICAgICAgICAgICAgICAgdGl0bGUgPSAiTUFTVCB3aXRoIEJhdGNoIENvcnJlY3Rpb24gKEFsbCBHZW5lcykiLAogICAgICAgICAgICAgICAgcEN1dG9mZiA9IDAuMDUsCiAgICAgICAgICAgICAgICBGQ2N1dG9mZiA9IDEuMCkKCgpFbmhhbmNlZFZvbGNhbm8oTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzLCAKICAgICAgICAgICAgICAgIGxhYiA9IE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRnZW5lLAogICAgICAgICAgICAgICAgeCA9ICJhdmdfbG9nMkZDIiwgCiAgICAgICAgICAgICAgICB5ID0gInBfdmFsX2FkaiIsCiAgICAgICAgICAgICAgICBzZWxlY3RMYWIgPSBjKCdFUENBTScsICdCQ0FUMScsICdLSVIzREwyJywgJ0ZPWE0xJywgJ1RXSVNUMScsICdUTkZTRjknLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0NEODAnLCAgJ0lMMUInLCAnUlBTNFkxJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdJTDdSJywgJ1RDRjcnLCAgJ01LSTY3JywgJ0NENzAnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0lMMlJBJywnVFJCVjYtMicsICdUUkJWMTAtMycsICdUUkJWNC0yJywgJ1RSQlY5JywgJ1RSQlY3LTknLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1RSQVYxMi0xJywgJ0NEOEInLCAnRkNHUjNBJywgJ0dOTFknLCAnRk9YUDMnLCAnU0VMTCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnR0lNQVAxJywgJ1JJUE9SMicsICdMRUYxJywgJ0hPWEM5JywgJ1NQNScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdDQ0wxNycsICdFVFY0JywgJ1RIWTEnLCAnRk9YQTInLCAnSVRHQUQnLCAnUzEwMFAnLCAnVEJYNCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSUQxJywgJ1hDTDEnLCAnU09YMicsICdDRDI3JywgJ0NEMjgnLCdQTFMzJywnQ0Q3MCcsJ1JBQjI1JyAsICdUUkJWMjcnLCAnVFJCVjInKSwKICAgICAgICAgICAgICAgIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogICAgICAgICAgICAgICAgeGxhYiA9IGJxdW90ZSh+TG9nWzJdfiAnZm9sZCBjaGFuZ2UnKSwKICAgICAgICAgICAgICAgIHBDdXRvZmYgPSAwLjA1LAogICAgICAgICAgICAgICAgRkNjdXRvZmYgPSAxLjUsIAogICAgICAgICAgICAgICAgcG9pbnRTaXplID0gMy4wLAogICAgICAgICAgICAgICAgbGFiU2l6ZSA9IDUuMCwKICAgICAgICAgICAgICAgIGJveGVkTGFiZWxzID0gVFJVRSwKICAgICAgICAgICAgICAgIGNvbEFscGhhID0gMC41LAogICAgICAgICAgICAgICAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLAogICAgICAgICAgICAgICAgbGVnZW5kTGFiU2l6ZSA9IDEwLAogICAgICAgICAgICAgICAgbGVnZW5kSWNvblNpemUgPSA0LjAsCiAgICAgICAgICAgICAgICBkcmF3Q29ubmVjdG9ycyA9IFRSVUUsCiAgICAgICAgICAgICAgICB3aWR0aENvbm5lY3RvcnMgPSAwLjUsCiAgICAgICAgICAgICAgICBjb2xDb25uZWN0b3JzID0gJ2dyZXk1MCcsCiAgICAgICAgICAgICAgICBhcnJvd2hlYWRzID0gRkFMU0UsCiAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSAzMCkKCgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCiMgQXNzdW1pbmcgeW91IGhhdmUgYSBkYXRhIGZyYW1lIG5hbWVkIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscwojIEZpbHRlciBnZW5lcyBiYXNlZCBvbiBsb3dlc3QgcC12YWx1ZXMgYnV0IGluY2x1ZGUgYWxsIGdlbmVzCmZpbHRlcmVkX2dlbmVzIDwtIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyAlPiUKICBhcnJhbmdlKHBfdmFsX2FkaiwgZGVzYyhhYnMoYXZnX2xvZzJGQykpKQoKIyBDcmVhdGUgdGhlIEVuaGFuY2VkVm9sY2FubyBwbG90IHdpdGggdGhlIGZpbHRlcmVkIGRhdGEKRW5oYW5jZWRWb2xjYW5vKAogIGZpbHRlcmVkX2dlbmVzLCAKICBsYWIgPSBpZmVsc2UoZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDAuMDUgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZzJGQykgPj0gMS4wLCBmaWx0ZXJlZF9nZW5lcyRnZW5lLCBOQSksCiAgeCA9ICJhdmdfbG9nMkZDIiwgCiAgeSA9ICJwX3ZhbF9hZGoiLAogIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHBDdXRvZmYgPSAwLjA1LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywgCiAgbGFiQ29sID0gJ2JsYWNrJywKICBsYWJGYWNlID0gJ2JvbGQnLAogIGJveGVkTGFiZWxzID0gRkFMU0UsICAjIFNldCB0byBGQUxTRSB0byByZW1vdmUgYm94ZWQgbGFiZWxzCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbWl6ZSBwb2ludCBjb2xvcnMKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuMF0gICMgT25seSBsYWJlbCBzaWduaWZpY2FudCBnZW5lcwopCgoKCkVuaGFuY2VkVm9sY2FubygKICBmaWx0ZXJlZF9nZW5lcywgCiAgbGFiID0gaWZlbHNlKGZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2cyRkMpID49IDEuMCwgZmlsdGVyZWRfZ2VuZXMkZ2VuZSwgTkEpLAogIHggPSAiYXZnX2xvZzJGQyIsIAogIHkgPSAicF92YWxfYWRqIiwKICB0aXRsZSA9ICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMgKGNlbGwgbGluZXMpIHZzIE5vcm1hbCBDRDQgVCBjZWxscyIsCiAgc3VidGl0bGUgPSAiSGlnaGxpZ2h0aW5nIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyIsCiAgcEN1dG9mZiA9IDAuMDUsCiAgRkNjdXRvZmYgPSAxLjAsCiAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLAogIGNvbEFscGhhID0gMC44LCAgIyBTbGlnaHQgdHJhbnNwYXJlbmN5IGZvciBub24tc2lnbmlmaWNhbnQgcG9pbnRzCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbSBjb2xvciBzY2hlbWUKICBncmlkbGluZXMubWFqb3IgPSBUUlVFLAogIGdyaWRsaW5lcy5taW5vciA9IEZBTFNFLAogIHNlbGVjdExhYiA9IGZpbHRlcmVkX2dlbmVzJGdlbmVbZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDAuMDUgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZzJGQykgPj0gMS4wXQopIAoKCmBgYAoKIyA0LiAgUGVyZm9ybSBGYXN0IEdTRUEgdXNpbmcgSGFsbG1hcmsgR2VuZSBTZXRzCmBgYHtyIGRhdGEyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCmxpYnJhcnkoZmdzZWEpCmxpYnJhcnkobXNpZ2RicikKbGlicmFyeShkcGx5cikKCiMgT2J0YWluIEhhbGxtYXJrIGdlbmUgc2V0cyBmcm9tIG1zaWdkYnIKaGFsbG1hcmtfZ2VuZXMgPC0gbXNpZ2RicihzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIsIGNhdGVnb3J5ID0gIkgiKQoKIyBDb252ZXJ0IHRoZSBnZW5lIHNldHMgdG8gYSBsaXN0IGZvcm1hdCBmb3IgZmdzZWEKaGFsbG1hcmtfbGlzdCA8LSBoYWxsbWFya19nZW5lcyAlPiUKICBzcGxpdCh4ID0gLiRnZW5lX3N5bWJvbCwgZiA9IC4kZ3NfbmFtZSkKCiMgQXNzdW1pbmcgeW91IGhhdmUgYSBkYXRhIGZyYW1lIG5hbWVkIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscwojIENyZWF0ZSBhIHJhbmtlZCBsaXN0IGJhc2VkIG9uIGF2Z19sb2cyRkMgYW5kIHBfdmFsX2FkagpNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgPC0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzICU+JQogIG11dGF0ZShyYW5rX21ldHJpYyA9IGF2Z19sb2cyRkMgKiAtbG9nMTAocF92YWxfYWRqKSkKCiMgRW5zdXJlIG5vIE5BIHZhbHVlcyBpbiByYW5rX21ldHJpYwpNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgPC0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzICU+JQogIGZpbHRlcighaXMubmEocmFua19tZXRyaWMpKQoKIyBDcmVhdGUgYSBuYW1lZCB2ZWN0b3IgZm9yIHJhbmtpbmcKZ2VuZV9saXN0IDwtIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRyYW5rX21ldHJpYwpuYW1lcyhnZW5lX2xpc3QpIDwtIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRnZW5lCgojIFNvcnQgdGhlIG5hbWVkIHZlY3RvciBpbiBkZWNyZWFzaW5nIG9yZGVyCmdlbmVfbGlzdCA8LSBzb3J0KGdlbmVfbGlzdCwgZGVjcmVhc2luZyA9IFRSVUUpCgpnZW5lX2xpc3QgPC0gZ2VuZV9saXN0W2lzLmZpbml0ZShnZW5lX2xpc3QpXQoKCiMgUGVyZm9ybSBmYXN0IEdTRUEKZmdzZWFfcmVzdWx0IDwtIGZnc2VhKHBhdGh3YXlzID0gaGFsbG1hcmtfbGlzdCwgCiAgICAgICAgICAgICAgICAgICAgICBzdGF0cyA9IGdlbmVfbGlzdCwgCiAgICAgICAgICAgICAgICAgICAgICBtaW5TaXplID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICBtYXhTaXplID0gNTAwLAogICAgICAgICAgICAgICAgICAgICAgbnBlcm0gPSAxMDAwMCkgICMgTnVtYmVyIG9mIHBlcm11dGF0aW9ucwoKIyBWaWV3IHRoZSBmZ3NlYSByZXN1bHRzCmhlYWQoZmdzZWFfcmVzdWx0KQoKIyBQbG90IHRoZSB0b3AgcGF0aHdheQp0b3BfcGF0aHdheSA8LSBmZ3NlYV9yZXN1bHRbb3JkZXIoZmdzZWFfcmVzdWx0JHBhZGopLCBdWzEsIF0KcGxvdEVucmljaG1lbnQoaGFsbG1hcmtfbGlzdFtbdG9wX3BhdGh3YXkkcGF0aHdheV1dLCBnZW5lX2xpc3QpICsKICBsYWJzKHRpdGxlID0gdG9wX3BhdGh3YXkkcGF0aHdheSkKCiNGaWx0ZXIgdGhlIHJlc3VsdHMgdGFibGUgdG8gc2hvdyBvbmx5IHRoZSB0b3AgMTAgVVAgb3IgRE9XTiByZWd1bGF0ZWQgcHJvY2Vzc2VzIChPcHRpb25hbCkKdG9wMTBfVVAgPC0gZmdzZWFfcmVzdWx0JHBhdGh3YXlzWzE6MTBdCgojc3VtbWFyeSBUYWJsZQpwbG90R3NlYVRhYmxlKGhhbGxtYXJrX2xpc3RbdG9wMTBfVVBdLCBnZW5lX2xpc3QsIGZnc2VhX3Jlc3VsdCwgZ3NlYVBhcmFtID0gMC41KQoKCmBgYAoKIyA1LiBDcmVhdGUgdGhlIEhlYXRtYXAgb2YgZmdzZWEgcmVzdWx0cwpgYGB7ciBkYXRhMywgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CmxpYnJhcnkocGhlYXRtYXApCgojIFNlbGVjdCB0aGUgdG9wIDUwIHBhdGh3YXlzCnRvcF9wYXRod2F5cyA8LSBmZ3NlYV9yZXN1bHQgJT4lCiAgYXJyYW5nZShwYWRqKSAlPiUKICBoZWFkKDUwKQoKIyBDcmVhdGUgYSBtYXRyaXggZm9yIHRoZSBoZWF0bWFwIHdpdGggcGF0aHdheXMgYXMgcm93cyBhbmQgTkVTIGFzIHRoZSB2YWx1ZXMKaGVhdG1hcF9kYXRhIDwtIG1hdHJpeCh0b3BfcGF0aHdheXMkTkVTLCBucm93ID0gbGVuZ3RoKHRvcF9wYXRod2F5cyRwYXRod2F5KSwgbmNvbCA9IDEpCnJvd25hbWVzKGhlYXRtYXBfZGF0YSkgPC0gdG9wX3BhdGh3YXlzJHBhdGh3YXkKY29sbmFtZXMoaGVhdG1hcF9kYXRhKSA8LSBjKCJORVMiKQoKIyBQbG90IHRoZSBjb21iaW5lZCBoZWF0bWFwIGZvciB0aGUgdG9wIDUwIHBhdGh3YXlzCnBoZWF0bWFwKGhlYXRtYXBfZGF0YSwgCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsIAogICAgICAgICBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLCAKICAgICAgICAgc2hvd19jb2xuYW1lcyA9IFRSVUUsCiAgICAgICAgIG1haW4gPSAiSGFsbG1hcmsgUGF0aHdheXM6IE1hbGlnbmFudCBDRDQgVCBDZWxscyBjb21wYXJlZCB0byBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkoNTApKQoKYGBgCgojIDYuIE9idGFpbiBLRUdHIEdlbmUgU2V0cyBhbmQgUGVyZm9ybSBGYXN0IEdTRUEgVXNpbmcgS0VHRyBQYXRod2F5cwpgYGB7ciBkYXRhNCwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CmxpYnJhcnkoZmdzZWEpCmxpYnJhcnkobXNpZ2RicikKbGlicmFyeShkcGx5cikKbGlicmFyeShwaGVhdG1hcCkKCiMgT2J0YWluIEtFR0cgZ2VuZSBzZXRzIGZyb20gbXNpZ2RicgprZWdnX2dlbmVzIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJDMiIsIHN1YmNhdGVnb3J5ID0gIkNQOktFR0ciKQoKIyBDb252ZXJ0IHRoZSBnZW5lIHNldHMgdG8gYSBsaXN0IGZvcm1hdCBmb3IgZmdzZWEKa2VnZ19saXN0IDwtIGtlZ2dfZ2VuZXMgJT4lCiAgc3BsaXQoeCA9IC4kZ2VuZV9zeW1ib2wsIGYgPSAuJGdzX25hbWUpCgojIEFzc3VtaW5nIHlvdSBoYXZlIGEgZGF0YSBmcmFtZSBuYW1lZCBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMKIyBDcmVhdGUgYSByYW5rZWQgbGlzdCBiYXNlZCBvbiBhdmdfbG9nMkZDIGFuZCBwX3ZhbF9hZGoKTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzIDwtIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyAlPiUKICBtdXRhdGUocmFua19tZXRyaWMgPSBhdmdfbG9nMkZDICogLWxvZzEwKHBfdmFsX2FkaikpCgojIEVuc3VyZSBubyBOQSB2YWx1ZXMgaW4gcmFua19tZXRyaWMKTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzIDwtIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyAlPiUKICBmaWx0ZXIoIWlzLm5hKHJhbmtfbWV0cmljKSkKCiMgQ3JlYXRlIGEgbmFtZWQgdmVjdG9yIGZvciByYW5raW5nCmdlbmVfbGlzdCA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMkcmFua19tZXRyaWMKbmFtZXMoZ2VuZV9saXN0KSA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMkZ2VuZQoKIyBTb3J0IHRoZSBuYW1lZCB2ZWN0b3IgaW4gZGVjcmVhc2luZyBvcmRlcgpnZW5lX2xpc3QgPC0gc29ydChnZW5lX2xpc3QsIGRlY3JlYXNpbmcgPSBUUlVFKQoKZ2VuZV9saXN0IDwtIGdlbmVfbGlzdFtpcy5maW5pdGUoZ2VuZV9saXN0KV0KCiMgUGVyZm9ybSBmYXN0IEdTRUEgdXNpbmcgS0VHRyBwYXRod2F5cwpmZ3NlYV9yZXN1bHRfa2VnZyA8LSBmZ3NlYShwYXRod2F5cyA9IGtlZ2dfbGlzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRzID0gZ2VuZV9saXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5TaXplID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heFNpemUgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5wZXJtID0gMTAwMDApICAjIE51bWJlciBvZiBwZXJtdXRhdGlvbnMKCiMgVmlldyB0aGUgZmdzZWEgcmVzdWx0cwpoZWFkKGZnc2VhX3Jlc3VsdF9rZWdnKQoKCiNGaWx0ZXIgdGhlIHJlc3VsdHMgdGFibGUgdG8gc2hvdyBvbmx5IHRoZSB0b3AgMTAgVVAgb3IgRE9XTiByZWd1bGF0ZWQgcHJvY2Vzc2VzIChPcHRpb25hbCkKdG9wMTBfVVBfa2VnZyA8LSBmZ3NlYV9yZXN1bHRfa2VnZyRwYXRod2F5c1sxOjEwXQoKI3N1bW1hcnkgVGFibGUKcGxvdEdzZWFUYWJsZShrZWdnX2xpc3RbdG9wMTBfVVBfa2VnZ10sIGdlbmVfbGlzdCwgZmdzZWFfcmVzdWx0X2tlZ2csIGdzZWFQYXJhbSA9IDAuNSkKCgojIFNlcGFyYXRlIHVwcmVndWxhdGVkIChwb3NpdGl2ZSBORVMpIGFuZCBkb3ducmVndWxhdGVkIChuZWdhdGl2ZSBORVMpIHBhdGh3YXlzCnVwcmVndWxhdGVkX3BhdGh3YXlzIDwtIGZnc2VhX3Jlc3VsdF9rZWdnICU+JSBmaWx0ZXIoTkVTID4gMCkgJT4lIGFycmFuZ2UocGFkaikgJT4lIGhlYWQoMjApCmRvd25yZWd1bGF0ZWRfcGF0aHdheXMgPC0gZmdzZWFfcmVzdWx0X2tlZ2cgJT4lIGZpbHRlcihORVMgPCAwKSAlPiUgYXJyYW5nZShwYWRqKSAlPiUgaGVhZCgyMCkKCiMgQ29tYmluZSB0aGUgdG9wIDIwIHVwcmVndWxhdGVkIGFuZCB0b3AgMjAgZG93bnJlZ3VsYXRlZCBwYXRod2F5cwpjb21iaW5lZF9wYXRod2F5cyA8LSBiaW5kX3Jvd3ModXByZWd1bGF0ZWRfcGF0aHdheXMsIGRvd25yZWd1bGF0ZWRfcGF0aHdheXMpCgojIENyZWF0ZSBhIG1hdHJpeCBmb3IgdGhlIGhlYXRtYXAgd2l0aCBwYXRod2F5cyBhcyByb3dzIGFuZCBORVMgYXMgdGhlIHZhbHVlcwpoZWF0bWFwX2RhdGFfY29tYmluZWQgPC0gbWF0cml4KGNvbWJpbmVkX3BhdGh3YXlzJE5FUywgbnJvdyA9IGxlbmd0aChjb21iaW5lZF9wYXRod2F5cyRwYXRod2F5KSwgbmNvbCA9IDEpCnJvd25hbWVzKGhlYXRtYXBfZGF0YV9jb21iaW5lZCkgPC0gY29tYmluZWRfcGF0aHdheXMkcGF0aHdheQpjb2xuYW1lcyhoZWF0bWFwX2RhdGFfY29tYmluZWQpIDwtIGMoIk5FUyIpCgojIFBsb3QgdGhlIGNvbWJpbmVkIGhlYXRtYXAgZm9yIHRoZSB0b3AgMjAgdXByZWd1bGF0ZWQgYW5kIHRvcCAyMCBkb3ducmVndWxhdGVkIHBhdGh3YXlzCnBoZWF0bWFwKGhlYXRtYXBfZGF0YV9jb21iaW5lZCwgCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsIAogICAgICAgICBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLCAKICAgICAgICAgc2hvd19jb2xuYW1lcyA9IFRSVUUsCiAgICAgICAgIG1haW4gPSAiS0VHRyBQYXRod2F5czogTWFsaWduYW50IENENCBUIENlbGxzIGNvbXBhcmVkIHRvIG5vcm1hbCBDRDQgVCBDZWxscyIsCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKSg1MCkpCmBgYAojIyAuIFZpc3VhbGl6YXRpb24tSGFsbG1hcmsKYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KZmdzZWFSZXNUaWR5IDwtIGZnc2VhX3Jlc3VsdCAlPiUKICBhc190aWJibGUoKSAlPiUKICBhcnJhbmdlKGRlc2MoTkVTKSkKCiMgU2hvdyBpbiBhIG5pY2UgdGFibGU6CmZnc2VhUmVzVGlkeSAlPiUgCiAgZHBseXI6OnNlbGVjdCgtbGVhZGluZ0VkZ2UsIC1FUywgLW5Nb3JlRXh0cmVtZSkgJT4lIAogIGFycmFuZ2UocGFkaikgJT4lIAogIERUOjpkYXRhdGFibGUoKQoKCmdncGxvdChmZ3NlYVJlc1RpZHksIGFlcyhyZW9yZGVyKHBhdGh3YXksIE5FUyksIE5FUykpICsKICBnZW9tX2NvbChhZXMoZmlsbD1wYWRqPDAuMDUpKSArCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKHg9IlBhdGh3YXkiLCB5PSJOb3JtYWxpemVkIEVucmljaG1lbnQgU2NvcmUiLAogICAgICAgdGl0bGU9IkhhbGxtYXJrIHBhdGh3YXlzIE5FUyBmcm9tIEdTRUEiKSArIAogIHRoZW1lX21pbmltYWwoKSsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiVFJVRSIgPSAicmVkIiwgIkZBTFNFIiA9ICJncmV5IikpCgoKCmBgYAoKIyMgLiBWaXN1YWxpemF0aW9uLUtlZ2cxCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CmZnc2VhUmVzVGlkeSA8LSBmZ3NlYV9yZXN1bHRfa2VnZyAlPiUKICBhc190aWJibGUoKSAlPiUKICBhcnJhbmdlKGRlc2MoTkVTKSkKCiMgU2hvdyBpbiBhIG5pY2UgdGFibGU6CmZnc2VhUmVzVGlkeSAlPiUgCiAgZHBseXI6OnNlbGVjdCgtbGVhZGluZ0VkZ2UsIC1FUywgLW5Nb3JlRXh0cmVtZSkgJT4lIAogIGFycmFuZ2UocGFkaikgJT4lIAogIERUOjpkYXRhdGFibGUoKQoKCmdncGxvdChmZ3NlYVJlc1RpZHksIGFlcyhyZW9yZGVyKHBhdGh3YXksIE5FUyksIE5FUykpICsKICBnZW9tX2NvbChhZXMoZmlsbD1wYWRqPDAuMDUpKSArCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKHg9IlBhdGh3YXkiLCB5PSJOb3JtYWxpemVkIEVucmljaG1lbnQgU2NvcmUiLAogICAgICAgdGl0bGU9IktFR0cgcGF0aHdheXMgTkVTIGZyb20gR1NFQSIpICsgCiAgdGhlbWVfbWluaW1hbCgpCgoKCmBgYAoKIyMgLiBWaXN1YWxpemF0aW9uLUtlZ2cyCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CiMgQXJyYW5nZSBieSBORVMgYW5kIHNlbGVjdCB0b3AgMjAgdXAgYW5kIGRvd24gcGF0aHdheXMKdG9wVXAgPC0gZmdzZWFSZXNUaWR5ICU+JQogIGRwbHlyOjpmaWx0ZXIoTkVTID4gMCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhORVMpKSAlPiUKICBkcGx5cjo6c2xpY2VfaGVhZChuID0gMjApCgp0b3BEb3duIDwtIGZnc2VhUmVzVGlkeSAlPiUKICBkcGx5cjo6ZmlsdGVyKE5FUyA8IDApICU+JQogIGRwbHlyOjphcnJhbmdlKE5FUykgJT4lCiAgZHBseXI6OnNsaWNlX2hlYWQobiA9IDIwKQoKIyBDb21iaW5lIHRoZSB0b3AgdXAgYW5kIGRvd24gcGF0aHdheXMKdG9wUGF0aHdheXMgPC0gZHBseXI6OmJpbmRfcm93cyh0b3BVcCwgdG9wRG93bikKCgpnZ3Bsb3QodG9wUGF0aHdheXMsIGFlcyhyZW9yZGVyKHBhdGh3YXksIE5FUyksIE5FUykpICsKICBnZW9tX2NvbChhZXMoZmlsbCA9IHBhZGogPCAwLjA1KSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh4ID0gIlBhdGh3YXkiLCB5ID0gIk5vcm1hbGl6ZWQgRW5yaWNobWVudCBTY29yZSIsCiAgICAgICB0aXRsZSA9ICJUb3AgMjAgVXAgYW5kIERvd24gS0VHRyBQYXRod2F5cyBORVMgZnJvbSBHU0VBIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiVFJVRSIgPSAicmVkIiwgIkZBTFNFIiA9ICJncmV5IikpCgoKCgpgYGAKCiMgNy4gU2F2ZSBIYWxsbWFyayBhbmQga2VnZyB0byBDU1YKYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCiMgQXNzdW1pbmcgeW91IGhhdmUgdGhlIHJlc3VsdHMgc3RvcmVkIGluIGZnc2VhX3Jlc3VsdF9oYWxsbWFyayBhbmQgZmdzZWFfcmVzdWx0X2tlZ2cKCiMgRmxhdHRlbiB0aGUgbGlzdCBjb2x1bW5zIGludG8gY2hhcmFjdGVyIHN0cmluZ3MgZm9yIEhhbGxtYXJrIHJlc3VsdHMKZmdzZWFfcmVzdWx0X2hhbGxtYXJrX2ZsYXR0ZW5lZCA8LSBmZ3NlYV9yZXN1bHQgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5saXN0KSwgfiBzYXBwbHkoLiwgdG9TdHJpbmcpKSkKCiMgV3JpdGUgdGhlIGZsYXR0ZW5lZCBIYWxsbWFyayByZXN1bHRzIHRvIGEgQ1NWIGZpbGUKd3JpdGUuY3N2KGZnc2VhX3Jlc3VsdF9oYWxsbWFya19mbGF0dGVuZWQsICJmZ3NlYV9yZXN1bHRzX2hhbGxtYXJrLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBGbGF0dGVuIHRoZSBsaXN0IGNvbHVtbnMgaW50byBjaGFyYWN0ZXIgc3RyaW5ncyBmb3IgS0VHRyByZXN1bHRzCmZnc2VhX3Jlc3VsdF9rZWdnX2ZsYXR0ZW5lZCA8LSBmZ3NlYV9yZXN1bHRfa2VnZyAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmxpc3QpLCB+IHNhcHBseSguLCB0b1N0cmluZykpKQoKIyBXcml0ZSB0aGUgZmxhdHRlbmVkIEtFR0cgcmVzdWx0cyB0byBhIENTViBmaWxlCndyaXRlLmNzdihmZ3NlYV9yZXN1bHRfa2VnZ19mbGF0dGVuZWQsICJmZ3NlYV9yZXN1bHRzX2tlZ2cuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgoKYGBgCg==