3. DE using LIBRA
libra_test <-pseudobulk_seurat
DE_results <- run_de(
pseudobulk_seurat,
cell_type_col = "cell_type",
replicate_col = "replicate",
label_col = "label",
de_family = "pseudobulk",
de_method = "DESeq2",
de_type = "LRT",
)
Volcano Plot
library(ggplot2)
library(dplyr)
library(ggrepel)
DE_results_df <- df[!(DE_Pseudobulk$Malignant.exp < 0.20 & DE_Pseudobulk$Control.exp < 0.20), ]
DE_results_df <- as.data.frame(df)
write.csv(DE_results_df, "../18-03-25-Deseq2_on_filtered_by_meanEXP/Psedobulk_Deseq2_filtered_on_mean.csv", row.names = FALSE)
# Ensure correct column names
colnames(DE_results_df)
[1] "cell_type" "gene" "avg_logFC" "Malignant.pct" "Control.pct" "Malignant.exp" "Control.exp" "p_val"
[9] "p_val_adj" "de_family" "de_method" "de_type"
# Define significance categories
volcano_data <- DE_results_df %>%
mutate(
significance = case_when(
p_val_adj < 0.05 & avg_logFC > 2 ~ "Upregulated",
p_val_adj < 0.05 & avg_logFC < -2 ~ "Downregulated",
TRUE ~ "Not Significant"
)
)
# Select genes to label: p_val_adj < 1e-50 OR logFC > 2 OR logFC < -2
top_genes <- volcano_data %>%
filter(p_val_adj < 0.05 | avg_logFC > 2 | avg_logFC < -2)
ggplot(volcano_data, aes(x = avg_logFC, y = -log10(p_val_adj), color = significance)) +
geom_point(alpha = 0.6, size = 2) + # Main points
scale_color_manual(values = c("Upregulated" = "red", "Downregulated" = "blue", "Not Significant" = "grey")) +
theme_minimal() +
labs(title = "Volcano Plot: Pseudobulk DESeq2 Analysis",
x = "Log2 Fold Change",
y = "-Log10 Adjusted P-Value",
color = "Significance") +
# Add gene labels WITHOUT any lines connecting them
geom_text_repel(data = top_genes,
aes(label = gene),
size = 5, box.padding = 0.3, max.overlaps = 15, segment.color = NA) +
# Add threshold lines
geom_vline(xintercept = c(-2, 2), linetype = "dashed", color = "black") + # logFC thresholds
geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "black") + # p-value threshold
ylim(0, 70) # Set max y-axis limit to avoid extreme values

NA
NA
Volcano Plot
library(ggplot2)
library(dplyr)
library(ggrepel)
# Ensure correct column names
colnames(DE_results_df)
[1] "cell_type" "gene" "avg_logFC" "Malignant.pct" "Control.pct" "Malignant.exp" "Control.exp" "p_val"
[9] "p_val_adj" "de_family" "de_method" "de_type"
# Define significance categories
volcano_data <- DE_results_df %>%
mutate(
significance = case_when(
p_val_adj < 1e-20 & avg_logFC > 2 ~ "Most Upregulated",
p_val_adj < 1e-20 & avg_logFC < -2 ~ "Most Downregulated",
p_val_adj < 0.05 & avg_logFC > 2 ~ "Upregulated",
p_val_adj < 0.05 & avg_logFC < -2 ~ "Downregulated",
TRUE ~ "Not Significant"
)
)
# Select only very significant genes for labeling
top_genes <- volcano_data %>%
filter(p_val_adj < 0.05 & (avg_logFC > 2 | avg_logFC < -2))
ggplot(volcano_data, aes(x = avg_logFC, y = -log10(p_val_adj), color = significance)) +
# Main points
geom_point(alpha = 0.7, size = 2.5) +
# Highlight highly significant genes with larger points
geom_point(data = top_genes, aes(x = avg_logFC, y = -log10(p_val_adj)),
color = "black", size = 3, shape = 21, fill = "black") +
# Custom color scheme
scale_color_manual(values = c(
"Most Upregulated" = "darkred",
"Most Downregulated" = "darkblue",
"Upregulated" = "red",
"Downregulated" = "blue",
"Not Significant" = "grey"
)) +
# Add gene labels (only for highly significant genes)
geom_text_repel(data = top_genes, aes(label = gene),
size = 4, box.padding = 0.5, max.overlaps = 10, segment.color = NA) +
# Add threshold lines
geom_vline(xintercept = c(-2, 2), linetype = "dashed", color = "black") +
geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "black") +
# Improve theme
theme_minimal(base_size = 14) +
labs(title = "Volcano Plot: Pseudobulk DESeq2 Analysis",
x = "Log2 Fold Change",
y = "-Log10 Adjusted P-Value",
color = "Significance") +
ylim(0, 50) # Avoid extreme scaling issues

NA
NA
4. Summarize Markers
markers <- DE_results_df
summarize_markers <- function(markers) {
num_pval0 <- sum(markers$p_val_adj == 0)
num_pval1 <- sum(markers$p_val_adj == 1)
num_significant <- sum(markers$p_val_adj < 0.05)
num_upregulated <- sum(markers$avg_logFC > 1)
num_downregulated <- sum(markers$avg_logFC < -1)
cat("Number of genes with p_val_adj = 0:", num_pval0, "\n")
cat("Number of genes with p_val_adj = 1:", num_pval1, "\n")
cat("Number of significant genes (p_val_adj < 0.05):", num_significant, "\n")
cat("Number of upregulated genes (avg_logFC > 1):", num_upregulated, "\n")
cat("Number of downregulated genes (avg_logFC < 1):", num_downregulated, "\n")
}
cat("Markers1 Summary at 0.05:\n")
Markers1 Summary at 0.05:
summarize_markers(markers)
Number of genes with p_val_adj = 0: 0
Number of genes with p_val_adj = 1: 12
Number of significant genes (p_val_adj < 0.05): 2929
Number of upregulated genes (avg_logFC > 1): 1332
Number of downregulated genes (avg_logFC < 1): 531
markers2 <- DE_results_df
summarize_markers <- function(markers) {
num_pval0 <- sum(markers$p_val_adj == 0)
num_pval1 <- sum(markers$p_val_adj == 1)
num_significant <- sum(markers$p_val_adj < 1e-4)
num_upregulated <- sum(markers$avg_logFC > 1)
num_downregulated <- sum(markers$avg_logFC < -1)
cat("Number of genes with p_val_adj = 0:", num_pval0, "\n")
cat("Number of genes with p_val_adj = 1:", num_pval1, "\n")
cat("Number of significant genes (p_val_adj < 1e-4):", num_significant, "\n")
cat("Number of upregulated genes (avg_logFC > 1):", num_upregulated, "\n")
cat("Number of downregulated genes (avg_logFC < 1):", num_downregulated, "\n")
}
cat("Markers Summary at 1e-4:\n")
Markers Summary at 1e-4:
summarize_markers(markers2)
Number of genes with p_val_adj = 0: 0
Number of genes with p_val_adj = 1: 12
Number of significant genes (p_val_adj < 1e-4): 975
Number of upregulated genes (avg_logFC > 1): 1332
Number of downregulated genes (avg_logFC < 1): 531
EnhancedVolcano plot
library(dplyr)
library(EnhancedVolcano)
# Assuming you have a data frame named Malignant_CD4Tcells_vs_Normal_CD4Tcells
# Filter genes based on lowest p-values but include all genes
filtered_genes <- markers %>%
arrange(p_val_adj, desc(abs(avg_logFC)))
# Create the EnhancedVolcano plot with the filtered data
EnhancedVolcano(
filtered_genes,
lab = ifelse(filtered_genes$p_val_adj <= 0.0000905 & abs(filtered_genes$avg_logFC) >= 1.0, filtered_genes$gene, NA),
x = "avg_logFC",
y = "p_val_adj",
title = "Malignant CD4 T cells(cell lines) vs normal CD4 T cells",
pCutoff = 1e-4,
FCcutoff = 1.0,
legendPosition = 'right',
labCol = 'black',
labFace = 'bold',
boxedLabels = FALSE, # Set to FALSE to remove boxed labels
pointSize = 3.0,
labSize = 5.0,
col = c('grey70', 'black', 'blue', 'red'), # Customize point colors
selectLab = filtered_genes$gene[filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_logFC) >= 1.0] # Only label significant genes
)

NA
NA
NA
Create the EnhancedVolcano plot
library(ggplot2)
library(EnhancedVolcano)
library(dplyr)
# Define the output directory
output_dir <- "Malignant_vs_Control"
dir.create(output_dir, showWarnings = FALSE)
Malignant_CD4Tcells_vs_Normal_CD4Tcells <- filtered_genes
# First Volcano Plot
p1 <- EnhancedVolcano(
Malignant_CD4Tcells_vs_Normal_CD4Tcells,
lab = Malignant_CD4Tcells_vs_Normal_CD4Tcells$gene,
x = "avg_logFC",
y = "p_val_adj",
title = "Malignant_CD4Tcells_vs_Normal_CD4Tcells",
pCutoff = 1e-4,
FCcutoff = 1.0
)
print(p1) # Display in notebook

ggsave(filename = file.path(output_dir, "VolcanoPlot1.png"), plot = p1, width = 14, height = 10, dpi = 300)
# Second Volcano Plot with selected genes
p2 <- EnhancedVolcano(
Malignant_CD4Tcells_vs_Normal_CD4Tcells,
lab = Malignant_CD4Tcells_vs_Normal_CD4Tcells$gene,
x = "avg_logFC",
y = "p_val_adj",
selectLab = c('EPCAM', 'BCAT1', 'KIR3DL2', 'FOXM1', 'TWIST1', 'TNFSF9',
'CD80', 'IL1B', 'RPS4Y1', "TOX", "CD52", "TWIST1", "CCR4", "CCR7","PDCD1",
'IL7R', 'TCF7', 'MKI67', 'CD70', "DPP4",
'IL2RA','TRBV6-2', 'TRBV10-3', 'TRBV4-2', 'TRBV9', 'TRBV7-9',
'TRAV12-1', 'CD8B', 'FCGR3A', 'GNLY', 'FOXP3', 'SELL',
'GIMAP1', 'RIPOR2', 'LEF1', 'HOXC9', 'SP5',
'CCL17', 'ETV4', 'THY1', 'FOXA2', 'ITGAD', 'S100P', 'TBX4',
'ID1', 'XCL1', 'SOX2', 'CD27', 'CD28','PLS3','CD70','RAB25' , 'TRBV27', 'TRBV2'),
title = "Malignant CD4 T cells(cell lines) vs normal CD4 T cells",
xlab = bquote(~Log[2]~ 'fold change'),
pCutoff = 0.05,
FCcutoff = 1.5,
pointSize = 3.0,
labSize = 5.0,
boxedLabels = TRUE,
colAlpha = 0.5,
legendPosition = 'right',
legendLabSize = 10,
legendIconSize = 4.0,
drawConnectors = TRUE,
widthConnectors = 0.5,
colConnectors = 'grey50',
arrowheads = FALSE,
max.overlaps = 30
)
print(p2) # Display in notebook

ggsave(filename = file.path(output_dir, "VolcanoPlot2.png"), plot = p2, width = 14, height = 10, dpi = 300)
# Filtering genes
filtered_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells %>%
arrange(p_val_adj, desc(abs(avg_logFC)))
# Third Volcano Plot - Filtering by p-value and logFC
p3 <- EnhancedVolcano(
filtered_genes,
lab = ifelse(filtered_genes$p_val_adj <= 1e-4 & abs(filtered_genes$avg_logFC) >= 1.0, filtered_genes$gene, NA),
x = "avg_logFC",
y = "p_val_adj",
title = "Malignant CD4 T cells(cell lines) vs normal CD4 T cells",
pCutoff = 1e-4,
FCcutoff = 1.0,
legendPosition = 'right',
labCol = 'black',
labFace = 'bold',
boxedLabels = FALSE, # Remove boxed labels
pointSize = 3.0,
labSize = 5.0,
col = c('grey70', 'black', 'blue', 'red'), # Customize point colors
selectLab = filtered_genes$gene[filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_logFC) >= 1.0]
)
print(p3) # Display in notebook

ggsave(filename = file.path(output_dir, "VolcanoPlot3.png"), plot = p3, width = 14, height = 10, dpi = 300)
# Fourth Volcano Plot - More refined filtering
p4 <- EnhancedVolcano(
filtered_genes,
lab = ifelse(filtered_genes$p_val_adj <= 1e-4 & abs(filtered_genes$avg_logFC) >= 1.0, filtered_genes$gene, NA),
x = "avg_logFC",
y = "p_val_adj",
title = "Malignant CD4 T cells (cell lines) vs Normal CD4 T cells",
subtitle = "Highlighting differentially expressed genes",
pCutoff = 1e-4,
FCcutoff = 1.0,
legendPosition = 'right',
colAlpha = 0.8, # Slight transparency for non-significant points
col = c('grey70', 'black', 'blue', 'red'), # Custom color scheme
gridlines.major = TRUE,
gridlines.minor = FALSE,
selectLab = filtered_genes$gene[filtered_genes$p_val_adj <= 0.05 & abs(filtered_genes$avg_logFC) >= 1.0]
)
print(p4) # Display in notebook

ggsave(filename = file.path(output_dir, "VolcanoPlot4.png"), plot = p4, width = 14, height = 10, dpi = 300)
message("All volcano plots have been displayed and saved successfully in the 'L1_vs_Control' folder.")
All volcano plots have been displayed and saved successfully in the 'L1_vs_Control' folder.
5. Enrichment Analysis-All_Pathways
# Load necessary libraries
library(clusterProfiler)
library(org.Hs.eg.db)
library(enrichplot)
library(ReactomePA)
library(DOSE) # For GSEA analysis
library(ggplot2) # Ensure ggplot2 is available for plotting
# Define threshold for differential expression selection (modified thresholds)
logFC_up_threshold <- 1.5 # Upregulated logFC threshold
logFC_down_threshold <- -1 # Downregulated logFC threshold
pval_threshold <- 0.05 # p-value threshold as specified
# Load your differential expression results (modify based on actual data structure)
# Malignant_CD4Tcells_vs_Normal_CD4Tcells <- read.csv("Your_DE_Results_File.csv")
# Select upregulated and downregulated genes
upregulated_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells[
Malignant_CD4Tcells_vs_Normal_CD4Tcells$avg_logFC > logFC_up_threshold &
Malignant_CD4Tcells_vs_Normal_CD4Tcells$p_val_adj < pval_threshold, ]
downregulated_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells[
Malignant_CD4Tcells_vs_Normal_CD4Tcells$avg_logFC < logFC_down_threshold &
Malignant_CD4Tcells_vs_Normal_CD4Tcells$p_val_adj < pval_threshold, ]
# Check for missing genes (NAs) in the gene column and remove them
upregulated_genes <- na.omit(upregulated_genes)
downregulated_genes <- na.omit(downregulated_genes)
# Save upregulated and downregulated gene results to CSV
write.csv(upregulated_genes, "Malignant_vs_Control/upregulated_genes.csv", row.names = FALSE)
write.csv(downregulated_genes, "Malignant_vs_Control/downregulated_genes.csv", row.names = FALSE)
# Convert gene symbols to Entrez IDs for enrichment analysis, with checks for missing values
upregulated_entrez <- bitr(upregulated_genes$gene, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)
'select()' returned 1:1 mapping between keys and columns
Avis : 3.62% of input gene IDs are fail to map...
downregulated_entrez <- bitr(downregulated_genes$gene, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)
'select()' returned 1:1 mapping between keys and columns
Avis : 3.89% of input gene IDs are fail to map...
# Check for missing Entrez IDs
missing_upregulated <- upregulated_genes$gene[is.na(upregulated_entrez$ENTREZID)]
missing_downregulated <- downregulated_genes$gene[is.na(downregulated_entrez$ENTREZID)]
# Print out the missing gene symbols for debugging
cat("Missing upregulated genes:\n", missing_upregulated, "\n")
Missing upregulated genes:
cat("Missing downregulated genes:\n", missing_downregulated, "\n")
Missing downregulated genes:
# Remove genes that couldn't be mapped to Entrez IDs
upregulated_entrez <- upregulated_entrez$ENTREZID[!is.na(upregulated_entrez$ENTREZID)]
downregulated_entrez <- downregulated_entrez$ENTREZID[!is.na(downregulated_entrez$ENTREZID)]
# Define a function to safely run enrichment, plot results, and save them
safe_enrichGO <- function(gene_list, title, filename) {
if (length(gene_list) > 0) {
result <- enrichGO(gene = gene_list, OrgDb = org.Hs.eg.db, keyType = "SYMBOL",
ont = "BP", pAdjustMethod = "BH", pvalueCutoff = 0.05)
if (!is.null(result) && nrow(as.data.frame(result)) > 0) {
p <- dotplot(result, showCategory = 10, title = title)
print(p)
ggsave(paste0("Malignant_vs_Control/", gsub(".csv", "_dotplot.png", filename)), plot = p, width = 8, height = 6)
write.csv(as.data.frame(result), file = paste0("Malignant_vs_Control/", filename), row.names = FALSE)
} else {
message(paste("No significant enrichment found for:", title))
}
} else {
message(paste("No genes found for:", title))
}
}
safe_enrichKEGG <- function(entrez_list, title, filename) {
if (length(entrez_list) > 0) {
result <- enrichKEGG(gene = entrez_list, organism = "hsa", pvalueCutoff = 0.05)
if (!is.null(result) && nrow(as.data.frame(result)) > 0) {
p <- dotplot(result, showCategory = 10, title = title)
print(p)
ggsave(paste0("Malignant_vs_Control/", gsub(".csv", "_dotplot.png", filename)), plot = p, width = 8, height = 6)
write.csv(as.data.frame(result), file = paste0("Malignant_vs_Control/", filename), row.names = FALSE)
} else {
message(paste("No significant KEGG pathways found for:", title))
}
} else {
message(paste("No genes found for:", title))
}
}
safe_enrichReactome <- function(entrez_list, title, filename) {
if (length(entrez_list) > 0) {
result <- enrichPathway(gene = entrez_list, organism = "human", pvalueCutoff = 0.05)
if (!is.null(result) && nrow(as.data.frame(result)) > 0) {
p <- dotplot(result, showCategory = 10, title = title)
print(p)
ggsave(paste0("Malignant_vs_Control/", gsub(".csv", "_dotplot.png", filename)), plot = p, width = 8, height = 6)
write.csv(as.data.frame(result), file = paste0("Malignant_vs_Control/", filename), row.names = FALSE)
} else {
message(paste("No significant Reactome pathways found for:", title))
}
} else {
message(paste("No genes found for:", title))
}
}
# Perform enrichment analyses, generate plots, and save results
safe_enrichGO(upregulated_genes$gene, "GO Enrichment for Upregulated Genes", "upregulated_GO_results.csv")

safe_enrichGO(downregulated_genes$gene, "GO Enrichment for Downregulated Genes", "downregulated_GO_results.csv")

safe_enrichKEGG(upregulated_entrez, "KEGG Pathway Enrichment for Upregulated Genes", "upregulated_KEGG_results.csv")
Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...

safe_enrichKEGG(downregulated_entrez, "KEGG Pathway Enrichment for Downregulated Genes", "downregulated_KEGG_results.csv")

safe_enrichReactome(upregulated_entrez, "Reactome Pathway Enrichment for Upregulated Genes", "upregulated_Reactome_results.csv")

safe_enrichReactome(downregulated_entrez, "Reactome Pathway Enrichment for Downregulated Genes", "downregulated_Reactome_results.csv")

NA
NA
Enrichment Analysis_Hallmark
# Load necessary libraries
library(clusterProfiler)
library(org.Hs.eg.db)
library(msigdbr)
library(enrichplot)
# Load Hallmark gene sets from msigdbr
hallmark_sets <- msigdbr(species = "Homo sapiens", category = "H") # "H" is for Hallmark gene sets
# Convert gene symbols to uppercase for consistency
upregulated_genes$gene <- toupper(upregulated_genes$gene)
downregulated_genes$gene <- toupper(downregulated_genes$gene)
# Check for overlap between your upregulated/downregulated genes and Hallmark gene sets
upregulated_in_hallmark <- intersect(upregulated_genes$gene, hallmark_sets$gene_symbol)
downregulated_in_hallmark <- intersect(downregulated_genes$gene, hallmark_sets$gene_symbol)
# Print the number of overlapping genes for both upregulated and downregulated genes
cat("Number of upregulated genes in Hallmark gene sets:", length(upregulated_in_hallmark), "\n")
Number of upregulated genes in Hallmark gene sets: 292
cat("Number of downregulated genes in Hallmark gene sets:", length(downregulated_in_hallmark), "\n")
Number of downregulated genes in Hallmark gene sets: 94
# Define the output folder where the results will be saved
output_folder <- "Malignant_vs_Control/"
# If there are genes to analyze, proceed with enrichment analysis
if (length(upregulated_in_hallmark) > 0) {
# Perform enrichment analysis for upregulated genes using Hallmark gene sets
hallmark_up <- enricher(gene = upregulated_in_hallmark,
TERM2GENE = hallmark_sets[, c("gs_name", "gene_symbol")], # Ensure TERM2GENE uses correct columns
pvalueCutoff = 0.05)
# Check if results exist
if (!is.null(hallmark_up) && nrow(hallmark_up) > 0) {
# Visualize results if available
up_dotplot <- dotplot(hallmark_up, showCategory = 20, title = "Hallmark Pathway Enrichment for Upregulated Genes")
# Display the plot in the notebook
print(up_dotplot)
# Save the dotplot to a PNG file
ggsave(paste0(output_folder, "hallmark_upregulated_dotplot.png"), plot = up_dotplot, width = 10, height = 8)
# Optionally, save the results as CSV
write.csv(as.data.frame(hallmark_up), file = paste0(output_folder, "hallmark_upregulated_enrichment.csv"), row.names = FALSE)
} else {
cat("No significant enrichment found for upregulated genes.\n")
}
} else {
cat("No upregulated genes overlap with Hallmark gene sets.\n")
}

if (length(downregulated_in_hallmark) > 0) {
# Perform enrichment analysis for downregulated genes using Hallmark gene sets
hallmark_down <- enricher(gene = downregulated_in_hallmark,
TERM2GENE = hallmark_sets[, c("gs_name", "gene_symbol")], # Ensure TERM2GENE uses correct columns
pvalueCutoff = 0.05)
# Check if results exist
if (!is.null(hallmark_down) && nrow(hallmark_down) > 0) {
# Visualize results if available
down_dotplot <- dotplot(hallmark_down, showCategory = 20, title = "Hallmark Pathway Enrichment for Downregulated Genes")
# Display the plot in the notebook
print(down_dotplot)
# Save the dotplot to a PNG file
ggsave(paste0(output_folder, "hallmark_downregulated_dotplot.png"), plot = down_dotplot, width = 10, height = 8)
# Optionally, save the results as CSV
write.csv(as.data.frame(hallmark_down), file = paste0(output_folder, "hallmark_downregulated_enrichment.csv"), row.names = FALSE)
} else {
cat("No significant enrichment found for downregulated genes.\n")
}
} else {
cat("No downregulated genes overlap with Hallmark gene sets.\n")
}

NA
NA
LS0tCnRpdGxlOiAiUHNldWRvQnVsayBBbmFseXNpcyB1c2luZyBMaWJyYSBSTkEgYXNzYXktRGVzZXEyLUxSVF9vbl9saXN0X2ZpbHRyZWRfb25fbWVhbiIKYXV0aG9yOiBOYXNpciBNYWhtb29kIEFiYmFzaQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAjIHBkZl9kb2N1bWVudDogZGVmYXVsdAogICMgd29yZF9kb2N1bWVudDogZGVmYXVsdAogICMgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogICNybWRmb3JtYXRzOjpyZWFkdGhlZG93bgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQotLS0KCiMgMS4gbG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CgojIExvYWQgbGlicmFyaWVzCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShERVNlcTIpCmxpYnJhcnkoTGlicmEpCgpgYGAKCgojIDIuIGxvYWQgc2V1cmF0IG9iamVjdApgYGB7ciBsb2FkX3NldXJhdH0KI0xvYWQgU2V1cmF0IE9iamVjdCBMNwpsb2FkKCIvaG9tZS9uYWJiYXNpL2lzaWxvbi9Ub19UcmFuc2Zlcl9iZXR3ZWVuX2NvbXB1dGVycy8yMy1IYXJtb255X0ludGVncmF0aW9uLzAtcm9iai81LUhhcm1vbnlfSW50ZWdyYXRlZF9BbGxfc2FtcGxlc19NZXJnZWRfQ0Q0VGNlbGxzX2ZpbmFsX1Jlc29sdXRpb25fU2VsZWN0ZWRfMC44X0FEVF9Ob3JtYWxpemVkX2NsZWFuZWRfbXQucm9iaiIpCgpwc2V1ZG9idWxrX3NldXJhdCA8LSBBbGxfc2FtcGxlc19NZXJnZWQKCiMgQXNzaWduIGxhYmVscyBhbmQgZW5zdXJlICdDb250cm9sJyBpcyB0aGUgcmVmZXJlbmNlCnBzZXVkb2J1bGtfc2V1cmF0JGxhYmVsIDwtIGZhY3RvcigKICBpZmVsc2UocHNldWRvYnVsa19zZXVyYXQkb3JpZy5pZGVudCAlaW4lIGMoIlBCTUMiLCAiUEJNQzEweCIpLCAKICAgICAgICAgIkNvbnRyb2wiLCAKICAgICAgICAgIk1hbGlnbmFudCIpLAogIGxldmVscyA9IGMoIk1hbGlnbmFudCIsICJDb250cm9sIikgIAopCgoKIyBEb3VibGUtY2hlY2sgdGhlIHJlZmVyZW5jZSBsZXZlbApwcmludChsZXZlbHMocHNldWRvYnVsa19zZXVyYXQkbGFiZWwpKSAgIyBTaG91bGQgcHJpbnQgIkNvbnRyb2wiIGZpcnN0CgojIFZlcmlmeSBmYWN0b3IgbGV2ZWxzCnByaW50KGxldmVscyhwc2V1ZG9idWxrX3NldXJhdCRsYWJlbCkpICAjIFNob3VsZCBzaG93IENvbnRyb2wgZmlyc3QKCiMgRW5zdXJlICdyZXBsaWNhdGUnIGlzIGEgZmFjdG9yCnBzZXVkb2J1bGtfc2V1cmF0JHJlcGxpY2F0ZSA8LSBhcy5mYWN0b3IocHNldWRvYnVsa19zZXVyYXQkY2VsbF9saW5lKQoKIyBSZW5hbWUgdGhlIGNlbGwgdHlwZSBjb2x1bW4KcHNldWRvYnVsa19zZXVyYXQkY2VsbF90eXBlIDwtICJDRDRUIgpgYGAKCiMgMy4gREUgdXNpbmcgTElCUkEKYGBge3IgLCBmaWcuaGVpZ2h0PTE0LCBmaWcud2lkdGg9MTh9CmxpYnJhX3Rlc3QgPC1wc2V1ZG9idWxrX3NldXJhdAoKCgpERV9yZXN1bHRzIDwtIHJ1bl9kZSgKICBwc2V1ZG9idWxrX3NldXJhdCwKICBjZWxsX3R5cGVfY29sID0gImNlbGxfdHlwZSIsIAogIHJlcGxpY2F0ZV9jb2wgPSAicmVwbGljYXRlIiwKICBsYWJlbF9jb2wgPSAibGFiZWwiLAogIGRlX2ZhbWlseSA9ICJwc2V1ZG9idWxrIiwgICAKICBkZV9tZXRob2QgPSAiREVTZXEyIiwgICAgICAgIAogIGRlX3R5cGUgPSAiTFJUIiwKKQoKCgpgYGAKCgoKCiMjIFZvbGNhbm8gUGxvdApgYGB7ciAsIGZpZy5oZWlnaHQ9MTQsIGZpZy53aWR0aD0xOH0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncmVwZWwpCgoKCkRFX3Jlc3VsdHNfZGYgPC0gZGZbIShERV9Qc2V1ZG9idWxrJE1hbGlnbmFudC5leHAgPCAwLjIwICYgREVfUHNldWRvYnVsayRDb250cm9sLmV4cCA8IDAuMjApLCBdCgpERV9yZXN1bHRzX2RmIDwtIGFzLmRhdGEuZnJhbWUoZGYpCgp3cml0ZS5jc3YoREVfcmVzdWx0c19kZiwgIi4uLzE4LTAzLTI1LURlc2VxMl9vbl9maWx0ZXJlZF9ieV9tZWFuRVhQL1BzZWRvYnVsa19EZXNlcTJfZmlsdGVyZWRfb25fbWVhbi5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCgoKIyBFbnN1cmUgY29ycmVjdCBjb2x1bW4gbmFtZXMKY29sbmFtZXMoREVfcmVzdWx0c19kZikKCiMgRGVmaW5lIHNpZ25pZmljYW5jZSBjYXRlZ29yaWVzCnZvbGNhbm9fZGF0YSA8LSBERV9yZXN1bHRzX2RmICU+JQogIG11dGF0ZSgKICAgIHNpZ25pZmljYW5jZSA9IGNhc2Vfd2hlbigKICAgICAgcF92YWxfYWRqIDwgMC4wNSAmIGF2Z19sb2dGQyA+IDIgfiAiVXByZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAwLjA1ICYgYXZnX2xvZ0ZDIDwgLTIgfiAiRG93bnJlZ3VsYXRlZCIsCiAgICAgIFRSVUUgfiAiTm90IFNpZ25pZmljYW50IgogICAgKQogICkKCiMgU2VsZWN0IGdlbmVzIHRvIGxhYmVsOiBwX3ZhbF9hZGogPCAxZS01MCBPUiBsb2dGQyA+IDIgT1IgbG9nRkMgPCAtMgp0b3BfZ2VuZXMgPC0gdm9sY2Fub19kYXRhICU+JQogIGZpbHRlcihwX3ZhbF9hZGogPCAwLjA1IHwgYXZnX2xvZ0ZDID4gMiB8IGF2Z19sb2dGQyA8IC0yKQoKZ2dwbG90KHZvbGNhbm9fZGF0YSwgYWVzKHggPSBhdmdfbG9nRkMsIHkgPSAtbG9nMTAocF92YWxfYWRqKSwgY29sb3IgPSBzaWduaWZpY2FuY2UpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNiwgc2l6ZSA9IDIpICsgICMgTWFpbiBwb2ludHMKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiVXByZWd1bGF0ZWQiID0gInJlZCIsICJEb3ducmVndWxhdGVkIiA9ICJibHVlIiwgIk5vdCBTaWduaWZpY2FudCIgPSAiZ3JleSIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gIlZvbGNhbm8gUGxvdDogUHNldWRvYnVsayBERVNlcTIgQW5hbHlzaXMiLAogICAgICAgeCA9ICJMb2cyIEZvbGQgQ2hhbmdlIiwKICAgICAgIHkgPSAiLUxvZzEwIEFkanVzdGVkIFAtVmFsdWUiLAogICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKwoKICAjIEFkZCBnZW5lIGxhYmVscyBXSVRIT1VUIGFueSBsaW5lcyBjb25uZWN0aW5nIHRoZW0KICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHRvcF9nZW5lcywgCiAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IGdlbmUpLCAgCiAgICAgICAgICAgICAgICAgIHNpemUgPSA1LCBib3gucGFkZGluZyA9IDAuMywgbWF4Lm92ZXJsYXBzID0gMTUsIHNlZ21lbnQuY29sb3IgPSBOQSkgKyAgCgogICMgQWRkIHRocmVzaG9sZCBsaW5lcwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLTIsIDIpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsgICMgbG9nRkMgdGhyZXNob2xkcwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmxhY2siKSArICAjIHAtdmFsdWUgdGhyZXNob2xkCgogIHlsaW0oMCwgNzApICAjIFNldCBtYXggeS1heGlzIGxpbWl0IHRvIGF2b2lkIGV4dHJlbWUgdmFsdWVzCgoKYGBgCiMjIFZvbGNhbm8gUGxvdApgYGB7ciAsIGZpZy5oZWlnaHQ9MTQsIGZpZy53aWR0aD0xOH0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncmVwZWwpCgoKIyBFbnN1cmUgY29ycmVjdCBjb2x1bW4gbmFtZXMKY29sbmFtZXMoREVfcmVzdWx0c19kZikKCiMgRGVmaW5lIHNpZ25pZmljYW5jZSBjYXRlZ29yaWVzCnZvbGNhbm9fZGF0YSA8LSBERV9yZXN1bHRzX2RmICU+JQogIG11dGF0ZSgKICAgIHNpZ25pZmljYW5jZSA9IGNhc2Vfd2hlbigKICAgICAgcF92YWxfYWRqIDwgMWUtMjAgJiBhdmdfbG9nRkMgPiAyIH4gIk1vc3QgVXByZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAxZS0yMCAmIGF2Z19sb2dGQyA8IC0yIH4gIk1vc3QgRG93bnJlZ3VsYXRlZCIsCiAgICAgIHBfdmFsX2FkaiA8IDAuMDUgJiBhdmdfbG9nRkMgPiAyIH4gIlVwcmVndWxhdGVkIiwKICAgICAgcF92YWxfYWRqIDwgMC4wNSAmIGF2Z19sb2dGQyA8IC0yIH4gIkRvd25yZWd1bGF0ZWQiLAogICAgICBUUlVFIH4gIk5vdCBTaWduaWZpY2FudCIKICAgICkKICApCgojIFNlbGVjdCBvbmx5IHZlcnkgc2lnbmlmaWNhbnQgZ2VuZXMgZm9yIGxhYmVsaW5nCnRvcF9nZW5lcyA8LSB2b2xjYW5vX2RhdGEgJT4lCiAgZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUgJiAoYXZnX2xvZ0ZDID4gMiB8IGF2Z19sb2dGQyA8IC0yKSkKCmdncGxvdCh2b2xjYW5vX2RhdGEsIGFlcyh4ID0gYXZnX2xvZ0ZDLCB5ID0gLWxvZzEwKHBfdmFsX2FkaiksIGNvbG9yID0gc2lnbmlmaWNhbmNlKSkgKwogIAogICMgTWFpbiBwb2ludHMKICBnZW9tX3BvaW50KGFscGhhID0gMC43LCBzaXplID0gMi41KSArCiAgCiAgIyBIaWdobGlnaHQgaGlnaGx5IHNpZ25pZmljYW50IGdlbmVzIHdpdGggbGFyZ2VyIHBvaW50cwogIGdlb21fcG9pbnQoZGF0YSA9IHRvcF9nZW5lcywgYWVzKHggPSBhdmdfbG9nRkMsIHkgPSAtbG9nMTAocF92YWxfYWRqKSksIAogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMywgc2hhcGUgPSAyMSwgZmlsbCA9ICJibGFjayIpICsKCiAgIyBDdXN0b20gY29sb3Igc2NoZW1lCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoCiAgICAiTW9zdCBVcHJlZ3VsYXRlZCIgPSAiZGFya3JlZCIsCiAgICAiTW9zdCBEb3ducmVndWxhdGVkIiA9ICJkYXJrYmx1ZSIsCiAgICAiVXByZWd1bGF0ZWQiID0gInJlZCIsCiAgICAiRG93bnJlZ3VsYXRlZCIgPSAiYmx1ZSIsCiAgICAiTm90IFNpZ25pZmljYW50IiA9ICJncmV5IgogICkpICsKCiAgIyBBZGQgZ2VuZSBsYWJlbHMgKG9ubHkgZm9yIGhpZ2hseSBzaWduaWZpY2FudCBnZW5lcykKICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHRvcF9nZW5lcywgYWVzKGxhYmVsID0gZ2VuZSksICAKICAgICAgICAgICAgICAgICAgc2l6ZSA9IDQsIGJveC5wYWRkaW5nID0gMC41LCBtYXgub3ZlcmxhcHMgPSAxMCwgc2VnbWVudC5jb2xvciA9IE5BKSArCiAgCiAgIyBBZGQgdGhyZXNob2xkIGxpbmVzCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygtMiwgMiksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKyAgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsgIAoKICAjIEltcHJvdmUgdGhlbWUKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE0KSArCiAgbGFicyh0aXRsZSA9ICJWb2xjYW5vIFBsb3Q6IFBzZXVkb2J1bGsgREVTZXEyIEFuYWx5c2lzIiwKICAgICAgIHggPSAiTG9nMiBGb2xkIENoYW5nZSIsCiAgICAgICB5ID0gIi1Mb2cxMCBBZGp1c3RlZCBQLVZhbHVlIiwKICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsKCiAgeWxpbSgwLCA1MCkgICMgQXZvaWQgZXh0cmVtZSBzY2FsaW5nIGlzc3VlcwoKCmBgYAoKIyA0LiBTdW1tYXJpemUgTWFya2VycwpgYGB7ciAsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNH0KbWFya2VycyA8LSBERV9yZXN1bHRzX2RmCgpzdW1tYXJpemVfbWFya2VycyA8LSBmdW5jdGlvbihtYXJrZXJzKSB7CiAgbnVtX3B2YWwwIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAwKQogIG51bV9wdmFsMSA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPT0gMSkKICBudW1fc2lnbmlmaWNhbnQgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqIDwgMC4wNSkKICBudW1fdXByZWd1bGF0ZWQgPC0gc3VtKG1hcmtlcnMkYXZnX2xvZ0ZDID4gMSkKICBudW1fZG93bnJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nRkMgPCAtMSkKICAKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDA6IiwgbnVtX3B2YWwwLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMToiLCBudW1fcHZhbDEsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2Ygc2lnbmlmaWNhbnQgZ2VuZXMgKHBfdmFsX2FkaiA8IDAuMDUpOiIsIG51bV9zaWduaWZpY2FudCwgIlxuIikKICBjYXQoIk51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcyAoYXZnX2xvZ0ZDID4gMSk6IiwgbnVtX3VwcmVndWxhdGVkLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2dGQyA8IDEpOiIsIG51bV9kb3ducmVndWxhdGVkLCAiXG4iKQp9CgpjYXQoIk1hcmtlcnMxIFN1bW1hcnkgYXQgMC4wNTpcbiIpCgpzdW1tYXJpemVfbWFya2VycyhtYXJrZXJzKQoKbWFya2VyczIgPC0gREVfcmVzdWx0c19kZgpzdW1tYXJpemVfbWFya2VycyA8LSBmdW5jdGlvbihtYXJrZXJzKSB7CiAgbnVtX3B2YWwwIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAwKQogIG51bV9wdmFsMSA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPT0gMSkKICBudW1fc2lnbmlmaWNhbnQgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqIDwgMWUtNCkKICBudW1fdXByZWd1bGF0ZWQgPC0gc3VtKG1hcmtlcnMkYXZnX2xvZ0ZDID4gMSkKICBudW1fZG93bnJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nRkMgPCAtMSkKICAKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDA6IiwgbnVtX3B2YWwwLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMToiLCBudW1fcHZhbDEsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2Ygc2lnbmlmaWNhbnQgZ2VuZXMgKHBfdmFsX2FkaiA8IDFlLTQpOiIsIG51bV9zaWduaWZpY2FudCwgIlxuIikKICBjYXQoIk51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcyAoYXZnX2xvZ0ZDID4gMSk6IiwgbnVtX3VwcmVndWxhdGVkLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2dGQyA8IDEpOiIsIG51bV9kb3ducmVndWxhdGVkLCAiXG4iKQp9CgpjYXQoIk1hcmtlcnMgU3VtbWFyeSBhdCAxZS00OlxuIikKCnN1bW1hcml6ZV9tYXJrZXJzKG1hcmtlcnMyKQoKCgoKYGBgCgoKCiMjIEVuaGFuY2VkVm9sY2FubyBwbG90CmBgYHtyICwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQoKbGlicmFyeShkcGx5cikKbGlicmFyeShFbmhhbmNlZFZvbGNhbm8pCgojIEFzc3VtaW5nIHlvdSBoYXZlIGEgZGF0YSBmcmFtZSBuYW1lZCBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMKIyBGaWx0ZXIgZ2VuZXMgYmFzZWQgb24gbG93ZXN0IHAtdmFsdWVzIGJ1dCBpbmNsdWRlIGFsbCBnZW5lcwpmaWx0ZXJlZF9nZW5lcyA8LSBtYXJrZXJzICU+JQogIGFycmFuZ2UocF92YWxfYWRqLCBkZXNjKGFicyhhdmdfbG9nRkMpKSkKCiMgQ3JlYXRlIHRoZSBFbmhhbmNlZFZvbGNhbm8gcGxvdCB3aXRoIHRoZSBmaWx0ZXJlZCBkYXRhCkVuaGFuY2VkVm9sY2FubygKICBmaWx0ZXJlZF9nZW5lcywgCiAgbGFiID0gaWZlbHNlKGZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjAwMDA5MDUgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZ0ZDKSA+PSAxLjAsIGZpbHRlcmVkX2dlbmVzJGdlbmUsIE5BKSwKICB4ID0gImF2Z19sb2dGQyIsIAogIHkgPSAicF92YWxfYWRqIiwKICB0aXRsZSA9ICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMoY2VsbCBsaW5lcykgdnMgbm9ybWFsIENENCBUIGNlbGxzIiwKICBwQ3V0b2ZmID0gMWUtNCwKICBGQ2N1dG9mZiA9IDEuMCwKICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsIAogIGxhYkNvbCA9ICdibGFjaycsCiAgbGFiRmFjZSA9ICdib2xkJywKICBib3hlZExhYmVscyA9IEZBTFNFLCAgIyBTZXQgdG8gRkFMU0UgdG8gcmVtb3ZlIGJveGVkIGxhYmVscwogIHBvaW50U2l6ZSA9IDMuMCwKICBsYWJTaXplID0gNS4wLAogIGNvbCA9IGMoJ2dyZXk3MCcsICdibGFjaycsICdibHVlJywgJ3JlZCcpLCAgIyBDdXN0b21pemUgcG9pbnQgY29sb3JzCiAgc2VsZWN0TGFiID0gZmlsdGVyZWRfZ2VuZXMkZ2VuZVtmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMC4wNSAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nRkMpID49IDEuMF0gICMgT25seSBsYWJlbCBzaWduaWZpY2FudCBnZW5lcwopCgoKCmBgYAoKCiMjIENyZWF0ZSB0aGUgRW5oYW5jZWRWb2xjYW5vIHBsb3QKYGBge3IgLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CgpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQpsaWJyYXJ5KGRwbHlyKQoKIyBEZWZpbmUgdGhlIG91dHB1dCBkaXJlY3RvcnkKb3V0cHV0X2RpciA8LSAiTWFsaWduYW50X3ZzX0NvbnRyb2wiCmRpci5jcmVhdGUob3V0cHV0X2Rpciwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCgogTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzIDwtIGZpbHRlcmVkX2dlbmVzCgojIEZpcnN0IFZvbGNhbm8gUGxvdApwMSA8LSBFbmhhbmNlZFZvbGNhbm8oCiAgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzLAogIGxhYiA9IE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRnZW5lLAogIHggPSAiYXZnX2xvZ0ZDIiwKICB5ID0gInBfdmFsX2FkaiIsCiAgdGl0bGUgPSAiTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzIiwKICBwQ3V0b2ZmID0gMWUtNCwKICBGQ2N1dG9mZiA9IDEuMAopCnByaW50KHAxKSAgIyBEaXNwbGF5IGluIG5vdGVib29rCmdnc2F2ZShmaWxlbmFtZSA9IGZpbGUucGF0aChvdXRwdXRfZGlyLCAiVm9sY2Fub1Bsb3QxLnBuZyIpLCBwbG90ID0gcDEsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCgojIFNlY29uZCBWb2xjYW5vIFBsb3Qgd2l0aCBzZWxlY3RlZCBnZW5lcwpwMiA8LSBFbmhhbmNlZFZvbGNhbm8oCiAgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzLCAKICBsYWIgPSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMkZ2VuZSwKICB4ID0gImF2Z19sb2dGQyIsIAogIHkgPSAicF92YWxfYWRqIiwKICBzZWxlY3RMYWIgPSBjKCdFUENBTScsICdCQ0FUMScsICdLSVIzREwyJywgJ0ZPWE0xJywgJ1RXSVNUMScsICdUTkZTRjknLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0NEODAnLCAgJ0lMMUInLCAnUlBTNFkxJywgIlRPWCIsICJDRDUyIiwgIlRXSVNUMSIsICJDQ1I0IiwgIkNDUjciLCJQRENEMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdJTDdSJywgJ1RDRjcnLCAgJ01LSTY3JywgJ0NENzAnLCAiRFBQNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdJTDJSQScsJ1RSQlY2LTInLCAnVFJCVjEwLTMnLCAnVFJCVjQtMicsICdUUkJWOScsICdUUkJWNy05JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdUUkFWMTItMScsICdDRDhCJywgJ0ZDR1IzQScsICdHTkxZJywgJ0ZPWFAzJywgJ1NFTEwnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0dJTUFQMScsICdSSVBPUjInLCAnTEVGMScsICdIT1hDOScsICdTUDUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnQ0NMMTcnLCAnRVRWNCcsICdUSFkxJywgJ0ZPWEEyJywgJ0lUR0FEJywgJ1MxMDBQJywgJ1RCWDQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0lEMScsICdYQ0wxJywgJ1NPWDInLCAnQ0QyNycsICdDRDI4JywnUExTMycsJ0NENzAnLCdSQUIyNScgLCAnVFJCVjI3JywgJ1RSQlYyJyksCiAgdGl0bGUgPSAiTWFsaWduYW50IENENCBUIGNlbGxzKGNlbGwgbGluZXMpIHZzIG5vcm1hbCBDRDQgVCBjZWxscyIsCiAgeGxhYiA9IGJxdW90ZSh+TG9nWzJdfiAnZm9sZCBjaGFuZ2UnKSwKICBwQ3V0b2ZmID0gMC4wNSwKICBGQ2N1dG9mZiA9IDEuNSwgCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgYm94ZWRMYWJlbHMgPSBUUlVFLAogIGNvbEFscGhhID0gMC41LAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywKICBsZWdlbmRMYWJTaXplID0gMTAsCiAgbGVnZW5kSWNvblNpemUgPSA0LjAsCiAgZHJhd0Nvbm5lY3RvcnMgPSBUUlVFLAogIHdpZHRoQ29ubmVjdG9ycyA9IDAuNSwKICBjb2xDb25uZWN0b3JzID0gJ2dyZXk1MCcsCiAgYXJyb3doZWFkcyA9IEZBTFNFLAogIG1heC5vdmVybGFwcyA9IDMwCikKcHJpbnQocDIpICAjIERpc3BsYXkgaW4gbm90ZWJvb2sKZ2dzYXZlKGZpbGVuYW1lID0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJWb2xjYW5vUGxvdDIucG5nIiksIHBsb3QgPSBwMiwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCkKCiMgRmlsdGVyaW5nIGdlbmVzCmZpbHRlcmVkX2dlbmVzIDwtIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyAlPiUKICBhcnJhbmdlKHBfdmFsX2FkaiwgZGVzYyhhYnMoYXZnX2xvZ0ZDKSkpCgojIFRoaXJkIFZvbGNhbm8gUGxvdCAtIEZpbHRlcmluZyBieSBwLXZhbHVlIGFuZCBsb2dGQwpwMyA8LSBFbmhhbmNlZFZvbGNhbm8oCiAgZmlsdGVyZWRfZ2VuZXMsIAogIGxhYiA9IGlmZWxzZShmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMWUtNCAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nRkMpID49IDEuMCwgZmlsdGVyZWRfZ2VuZXMkZ2VuZSwgTkEpLAogIHggPSAiYXZnX2xvZ0ZDIiwgCiAgeSA9ICJwX3ZhbF9hZGoiLAogIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHBDdXRvZmYgPSAxZS00LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywgCiAgbGFiQ29sID0gJ2JsYWNrJywKICBsYWJGYWNlID0gJ2JvbGQnLAogIGJveGVkTGFiZWxzID0gRkFMU0UsICAjIFJlbW92ZSBib3hlZCBsYWJlbHMKICBwb2ludFNpemUgPSAzLjAsCiAgbGFiU2l6ZSA9IDUuMCwKICBjb2wgPSBjKCdncmV5NzAnLCAnYmxhY2snLCAnYmx1ZScsICdyZWQnKSwgICMgQ3VzdG9taXplIHBvaW50IGNvbG9ycwogIHNlbGVjdExhYiA9IGZpbHRlcmVkX2dlbmVzJGdlbmVbZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDAuMDUgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZ0ZDKSA+PSAxLjBdCikKcHJpbnQocDMpICAjIERpc3BsYXkgaW4gbm90ZWJvb2sKZ2dzYXZlKGZpbGVuYW1lID0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJWb2xjYW5vUGxvdDMucG5nIiksIHBsb3QgPSBwMywgd2lkdGggPSAxNCwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCkKCiMgRm91cnRoIFZvbGNhbm8gUGxvdCAtIE1vcmUgcmVmaW5lZCBmaWx0ZXJpbmcKcDQgPC0gRW5oYW5jZWRWb2xjYW5vKAogIGZpbHRlcmVkX2dlbmVzLCAKICBsYWIgPSBpZmVsc2UoZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDFlLTQgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZ0ZDKSA+PSAxLjAsIGZpbHRlcmVkX2dlbmVzJGdlbmUsIE5BKSwKICB4ID0gImF2Z19sb2dGQyIsIAogIHkgPSAicF92YWxfYWRqIiwKICB0aXRsZSA9ICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMgKGNlbGwgbGluZXMpIHZzIE5vcm1hbCBDRDQgVCBjZWxscyIsCiAgc3VidGl0bGUgPSAiSGlnaGxpZ2h0aW5nIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyIsCiAgcEN1dG9mZiA9IDFlLTQsCiAgRkNjdXRvZmYgPSAxLjAsCiAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLAogIGNvbEFscGhhID0gMC44LCAgIyBTbGlnaHQgdHJhbnNwYXJlbmN5IGZvciBub24tc2lnbmlmaWNhbnQgcG9pbnRzCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbSBjb2xvciBzY2hlbWUKICBncmlkbGluZXMubWFqb3IgPSBUUlVFLAogIGdyaWRsaW5lcy5taW5vciA9IEZBTFNFLAogIHNlbGVjdExhYiA9IGZpbHRlcmVkX2dlbmVzJGdlbmVbZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDAuMDUgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZ0ZDKSA+PSAxLjBdCikKcHJpbnQocDQpICAjIERpc3BsYXkgaW4gbm90ZWJvb2sKZ2dzYXZlKGZpbGVuYW1lID0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJWb2xjYW5vUGxvdDQucG5nIiksIHBsb3QgPSBwNCwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCkKCm1lc3NhZ2UoIkFsbCB2b2xjYW5vIHBsb3RzIGhhdmUgYmVlbiBkaXNwbGF5ZWQgYW5kIHNhdmVkIHN1Y2Nlc3NmdWxseSBpbiB0aGUgJ0wxX3ZzX0NvbnRyb2wnIGZvbGRlci4iKQoKCgpgYGAKCgojIDUuIEVucmljaG1lbnQgQW5hbHlzaXMtQWxsX1BhdGh3YXlzCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KFJlYWN0b21lUEEpCmxpYnJhcnkoRE9TRSkgIyBGb3IgR1NFQSBhbmFseXNpcwpsaWJyYXJ5KGdncGxvdDIpICMgRW5zdXJlIGdncGxvdDIgaXMgYXZhaWxhYmxlIGZvciBwbG90dGluZwoKIyBEZWZpbmUgdGhyZXNob2xkIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBzZWxlY3Rpb24gKG1vZGlmaWVkIHRocmVzaG9sZHMpCmxvZ0ZDX3VwX3RocmVzaG9sZCA8LSAxLjUgICAgICAgICAgIyBVcHJlZ3VsYXRlZCBsb2dGQyB0aHJlc2hvbGQKbG9nRkNfZG93bl90aHJlc2hvbGQgPC0gLTEgICAgICAgIyBEb3ducmVndWxhdGVkIGxvZ0ZDIHRocmVzaG9sZApwdmFsX3RocmVzaG9sZCA8LSAwLjA1ICAgICAgICAgICAjIHAtdmFsdWUgdGhyZXNob2xkIGFzIHNwZWNpZmllZAoKIyBMb2FkIHlvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0cyAobW9kaWZ5IGJhc2VkIG9uIGFjdHVhbCBkYXRhIHN0cnVjdHVyZSkKIyBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgPC0gcmVhZC5jc3YoIllvdXJfREVfUmVzdWx0c19GaWxlLmNzdiIpCgojIFNlbGVjdCB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcwp1cHJlZ3VsYXRlZF9nZW5lcyA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHNbCiAgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGF2Z19sb2dGQyA+IGxvZ0ZDX3VwX3RocmVzaG9sZCAmIAogIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRwX3ZhbF9hZGogPCBwdmFsX3RocmVzaG9sZCwgXQoKZG93bnJlZ3VsYXRlZF9nZW5lcyA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHNbCiAgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGF2Z19sb2dGQyA8IGxvZ0ZDX2Rvd25fdGhyZXNob2xkICYgCiAgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJHBfdmFsX2FkaiA8IHB2YWxfdGhyZXNob2xkLCBdCgojIENoZWNrIGZvciBtaXNzaW5nIGdlbmVzIChOQXMpIGluIHRoZSBnZW5lIGNvbHVtbiBhbmQgcmVtb3ZlIHRoZW0KdXByZWd1bGF0ZWRfZ2VuZXMgPC0gbmEub21pdCh1cHJlZ3VsYXRlZF9nZW5lcykKZG93bnJlZ3VsYXRlZF9nZW5lcyA8LSBuYS5vbWl0KGRvd25yZWd1bGF0ZWRfZ2VuZXMpCgojIFNhdmUgdXByZWd1bGF0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZSByZXN1bHRzIHRvIENTVgp3cml0ZS5jc3YodXByZWd1bGF0ZWRfZ2VuZXMsICJNYWxpZ25hbnRfdnNfQ29udHJvbC91cHJlZ3VsYXRlZF9nZW5lcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KGRvd25yZWd1bGF0ZWRfZ2VuZXMsICJNYWxpZ25hbnRfdnNfQ29udHJvbC9kb3ducmVndWxhdGVkX2dlbmVzLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBDb252ZXJ0IGdlbmUgc3ltYm9scyB0byBFbnRyZXogSURzIGZvciBlbnJpY2htZW50IGFuYWx5c2lzLCB3aXRoIGNoZWNrcyBmb3IgbWlzc2luZyB2YWx1ZXMKdXByZWd1bGF0ZWRfZW50cmV6IDwtIGJpdHIodXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgZnJvbVR5cGUgPSAiU1lNQk9MIiwgdG9UeXBlID0gIkVOVFJFWklEIiwgT3JnRGIgPSBvcmcuSHMuZWcuZGIpCmRvd25yZWd1bGF0ZWRfZW50cmV6IDwtIGJpdHIoZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IG9yZy5Icy5lZy5kYikKCiMgQ2hlY2sgZm9yIG1pc3NpbmcgRW50cmV6IElEcwptaXNzaW5nX3VwcmVndWxhdGVkIDwtIHVwcmVndWxhdGVkX2dlbmVzJGdlbmVbaXMubmEodXByZWd1bGF0ZWRfZW50cmV6JEVOVFJFWklEKV0KbWlzc2luZ19kb3ducmVndWxhdGVkIDwtIGRvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZVtpcy5uYShkb3ducmVndWxhdGVkX2VudHJleiRFTlRSRVpJRCldCgojIFByaW50IG91dCB0aGUgbWlzc2luZyBnZW5lIHN5bWJvbHMgZm9yIGRlYnVnZ2luZwpjYXQoIk1pc3NpbmcgdXByZWd1bGF0ZWQgZ2VuZXM6XG4iLCBtaXNzaW5nX3VwcmVndWxhdGVkLCAiXG4iKQpjYXQoIk1pc3NpbmcgZG93bnJlZ3VsYXRlZCBnZW5lczpcbiIsIG1pc3NpbmdfZG93bnJlZ3VsYXRlZCwgIlxuIikKCiMgUmVtb3ZlIGdlbmVzIHRoYXQgY291bGRuJ3QgYmUgbWFwcGVkIHRvIEVudHJleiBJRHMKdXByZWd1bGF0ZWRfZW50cmV6IDwtIHVwcmVndWxhdGVkX2VudHJleiRFTlRSRVpJRFshaXMubmEodXByZWd1bGF0ZWRfZW50cmV6JEVOVFJFWklEKV0KZG93bnJlZ3VsYXRlZF9lbnRyZXogPC0gZG93bnJlZ3VsYXRlZF9lbnRyZXokRU5UUkVaSURbIWlzLm5hKGRvd25yZWd1bGF0ZWRfZW50cmV6JEVOVFJFWklEKV0KCiMgRGVmaW5lIGEgZnVuY3Rpb24gdG8gc2FmZWx5IHJ1biBlbnJpY2htZW50LCBwbG90IHJlc3VsdHMsIGFuZCBzYXZlIHRoZW0Kc2FmZV9lbnJpY2hHTyA8LSBmdW5jdGlvbihnZW5lX2xpc3QsIHRpdGxlLCBmaWxlbmFtZSkgewogIGlmIChsZW5ndGgoZ2VuZV9saXN0KSA+IDApIHsKICAgIHJlc3VsdCA8LSBlbnJpY2hHTyhnZW5lID0gZ2VuZV9saXN0LCBPcmdEYiA9IG9yZy5Icy5lZy5kYiwga2V5VHlwZSA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICAgICAgIG9udCA9ICJCUCIsIHBBZGp1c3RNZXRob2QgPSAiQkgiLCBwdmFsdWVDdXRvZmYgPSAwLjA1KQogICAgaWYgKCFpcy5udWxsKHJlc3VsdCkgJiYgbnJvdyhhcy5kYXRhLmZyYW1lKHJlc3VsdCkpID4gMCkgewogICAgICBwIDwtIGRvdHBsb3QocmVzdWx0LCBzaG93Q2F0ZWdvcnkgPSAxMCwgdGl0bGUgPSB0aXRsZSkKICAgICAgcHJpbnQocCkgIAogICAgICBnZ3NhdmUocGFzdGUwKCJNYWxpZ25hbnRfdnNfQ29udHJvbC8iLCBnc3ViKCIuY3N2IiwgIl9kb3RwbG90LnBuZyIsIGZpbGVuYW1lKSksIHBsb3QgPSBwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCiAgICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKHJlc3VsdCksIGZpbGUgPSBwYXN0ZTAoIk1hbGlnbmFudF92c19Db250cm9sLyIsIGZpbGVuYW1lKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgICB9IGVsc2UgewogICAgICBtZXNzYWdlKHBhc3RlKCJObyBzaWduaWZpY2FudCBlbnJpY2htZW50IGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgICB9CiAgfSBlbHNlIHsKICAgIG1lc3NhZ2UocGFzdGUoIk5vIGdlbmVzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgfQp9CgpzYWZlX2VucmljaEtFR0cgPC0gZnVuY3Rpb24oZW50cmV6X2xpc3QsIHRpdGxlLCBmaWxlbmFtZSkgewogIGlmIChsZW5ndGgoZW50cmV6X2xpc3QpID4gMCkgewogICAgcmVzdWx0IDwtIGVucmljaEtFR0coZ2VuZSA9IGVudHJlel9saXN0LCBvcmdhbmlzbSA9ICJoc2EiLCBwdmFsdWVDdXRvZmYgPSAwLjA1KQogICAgaWYgKCFpcy5udWxsKHJlc3VsdCkgJiYgbnJvdyhhcy5kYXRhLmZyYW1lKHJlc3VsdCkpID4gMCkgewogICAgICBwIDwtIGRvdHBsb3QocmVzdWx0LCBzaG93Q2F0ZWdvcnkgPSAxMCwgdGl0bGUgPSB0aXRsZSkKICAgICAgcHJpbnQocCkKICAgICAgZ2dzYXZlKHBhc3RlMCgiTWFsaWduYW50X3ZzX0NvbnRyb2wvIiwgZ3N1YigiLmNzdiIsICJfZG90cGxvdC5wbmciLCBmaWxlbmFtZSkpLCBwbG90ID0gcCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQogICAgICB3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShyZXN1bHQpLCBmaWxlID0gcGFzdGUwKCJNYWxpZ25hbnRfdnNfQ29udHJvbC8iLCBmaWxlbmFtZSksIHJvdy5uYW1lcyA9IEZBTFNFKQogICAgfSBlbHNlIHsKICAgICAgbWVzc2FnZShwYXN0ZSgiTm8gc2lnbmlmaWNhbnQgS0VHRyBwYXRod2F5cyBmb3VuZCBmb3I6IiwgdGl0bGUpKQogICAgfQogIH0gZWxzZSB7CiAgICBtZXNzYWdlKHBhc3RlKCJObyBnZW5lcyBmb3VuZCBmb3I6IiwgdGl0bGUpKQogIH0KfQoKc2FmZV9lbnJpY2hSZWFjdG9tZSA8LSBmdW5jdGlvbihlbnRyZXpfbGlzdCwgdGl0bGUsIGZpbGVuYW1lKSB7CiAgaWYgKGxlbmd0aChlbnRyZXpfbGlzdCkgPiAwKSB7CiAgICByZXN1bHQgPC0gZW5yaWNoUGF0aHdheShnZW5lID0gZW50cmV6X2xpc3QsIG9yZ2FuaXNtID0gImh1bWFuIiwgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKICAgIGlmICghaXMubnVsbChyZXN1bHQpICYmIG5yb3coYXMuZGF0YS5mcmFtZShyZXN1bHQpKSA+IDApIHsKICAgICAgcCA8LSBkb3RwbG90KHJlc3VsdCwgc2hvd0NhdGVnb3J5ID0gMTAsIHRpdGxlID0gdGl0bGUpCiAgICAgIHByaW50KHApCiAgICAgIGdnc2F2ZShwYXN0ZTAoIk1hbGlnbmFudF92c19Db250cm9sLyIsIGdzdWIoIi5jc3YiLCAiX2RvdHBsb3QucG5nIiwgZmlsZW5hbWUpKSwgcGxvdCA9IHAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKICAgICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUocmVzdWx0KSwgZmlsZSA9IHBhc3RlMCgiTWFsaWduYW50X3ZzX0NvbnRyb2wvIiwgZmlsZW5hbWUpLCByb3cubmFtZXMgPSBGQUxTRSkKICAgIH0gZWxzZSB7CiAgICAgIG1lc3NhZ2UocGFzdGUoIk5vIHNpZ25pZmljYW50IFJlYWN0b21lIHBhdGh3YXlzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgICB9CiAgfSBlbHNlIHsKICAgIG1lc3NhZ2UocGFzdGUoIk5vIGdlbmVzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgfQp9CgojIFBlcmZvcm0gZW5yaWNobWVudCBhbmFseXNlcywgZ2VuZXJhdGUgcGxvdHMsIGFuZCBzYXZlIHJlc3VsdHMKc2FmZV9lbnJpY2hHTyh1cHJlZ3VsYXRlZF9nZW5lcyRnZW5lLCAiR08gRW5yaWNobWVudCBmb3IgVXByZWd1bGF0ZWQgR2VuZXMiLCAidXByZWd1bGF0ZWRfR09fcmVzdWx0cy5jc3YiKQpzYWZlX2VucmljaEdPKGRvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgIkdPIEVucmljaG1lbnQgZm9yIERvd25yZWd1bGF0ZWQgR2VuZXMiLCAiZG93bnJlZ3VsYXRlZF9HT19yZXN1bHRzLmNzdiIpCgpzYWZlX2VucmljaEtFR0codXByZWd1bGF0ZWRfZW50cmV6LCAiS0VHRyBQYXRod2F5IEVucmljaG1lbnQgZm9yIFVwcmVndWxhdGVkIEdlbmVzIiwgInVwcmVndWxhdGVkX0tFR0dfcmVzdWx0cy5jc3YiKQpzYWZlX2VucmljaEtFR0coZG93bnJlZ3VsYXRlZF9lbnRyZXosICJLRUdHIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgRG93bnJlZ3VsYXRlZCBHZW5lcyIsICJkb3ducmVndWxhdGVkX0tFR0dfcmVzdWx0cy5jc3YiKQoKc2FmZV9lbnJpY2hSZWFjdG9tZSh1cHJlZ3VsYXRlZF9lbnRyZXosICJSZWFjdG9tZSBQYXRod2F5IEVucmljaG1lbnQgZm9yIFVwcmVndWxhdGVkIEdlbmVzIiwgInVwcmVndWxhdGVkX1JlYWN0b21lX3Jlc3VsdHMuY3N2IikKc2FmZV9lbnJpY2hSZWFjdG9tZShkb3ducmVndWxhdGVkX2VudHJleiwgIlJlYWN0b21lIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgRG93bnJlZ3VsYXRlZCBHZW5lcyIsICJkb3ducmVndWxhdGVkX1JlYWN0b21lX3Jlc3VsdHMuY3N2IikKCgpgYGAKCgoKCiMjIEVucmljaG1lbnQgQW5hbHlzaXNfSGFsbG1hcmsKYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShtc2lnZGJyKQpsaWJyYXJ5KGVucmljaHBsb3QpCgojIExvYWQgSGFsbG1hcmsgZ2VuZSBzZXRzIGZyb20gbXNpZ2RicgpoYWxsbWFya19zZXRzIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJIIikgICMgIkgiIGlzIGZvciBIYWxsbWFyayBnZW5lIHNldHMKCiMgQ29udmVydCBnZW5lIHN5bWJvbHMgdG8gdXBwZXJjYXNlIGZvciBjb25zaXN0ZW5jeQp1cHJlZ3VsYXRlZF9nZW5lcyRnZW5lIDwtIHRvdXBwZXIodXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSkKZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lIDwtIHRvdXBwZXIoZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lKQoKIyBDaGVjayBmb3Igb3ZlcmxhcCBiZXR3ZWVuIHlvdXIgdXByZWd1bGF0ZWQvZG93bnJlZ3VsYXRlZCBnZW5lcyBhbmQgSGFsbG1hcmsgZ2VuZSBzZXRzCnVwcmVndWxhdGVkX2luX2hhbGxtYXJrIDwtIGludGVyc2VjdCh1cHJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBoYWxsbWFya19zZXRzJGdlbmVfc3ltYm9sKQpkb3ducmVndWxhdGVkX2luX2hhbGxtYXJrIDwtIGludGVyc2VjdChkb3ducmVndWxhdGVkX2dlbmVzJGdlbmUsIGhhbGxtYXJrX3NldHMkZ2VuZV9zeW1ib2wpCgojIFByaW50IHRoZSBudW1iZXIgb2Ygb3ZlcmxhcHBpbmcgZ2VuZXMgZm9yIGJvdGggdXByZWd1bGF0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMKY2F0KCJOdW1iZXIgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgaW4gSGFsbG1hcmsgZ2VuZSBzZXRzOiIsIGxlbmd0aCh1cHJlZ3VsYXRlZF9pbl9oYWxsbWFyayksICJcbiIpCmNhdCgiTnVtYmVyIG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMgaW4gSGFsbG1hcmsgZ2VuZSBzZXRzOiIsIGxlbmd0aChkb3ducmVndWxhdGVkX2luX2hhbGxtYXJrKSwgIlxuIikKCiMgRGVmaW5lIHRoZSBvdXRwdXQgZm9sZGVyIHdoZXJlIHRoZSByZXN1bHRzIHdpbGwgYmUgc2F2ZWQKb3V0cHV0X2ZvbGRlciA8LSAiTWFsaWduYW50X3ZzX0NvbnRyb2wvIgoKIyBJZiB0aGVyZSBhcmUgZ2VuZXMgdG8gYW5hbHl6ZSwgcHJvY2VlZCB3aXRoIGVucmljaG1lbnQgYW5hbHlzaXMKaWYgKGxlbmd0aCh1cHJlZ3VsYXRlZF9pbl9oYWxsbWFyaykgPiAwKSB7CiAgIyBQZXJmb3JtIGVucmljaG1lbnQgYW5hbHlzaXMgZm9yIHVwcmVndWxhdGVkIGdlbmVzIHVzaW5nIEhhbGxtYXJrIGdlbmUgc2V0cwogIGhhbGxtYXJrX3VwIDwtIGVucmljaGVyKGdlbmUgPSB1cHJlZ3VsYXRlZF9pbl9oYWxsbWFyaywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgVEVSTTJHRU5FID0gaGFsbG1hcmtfc2V0c1ssIGMoImdzX25hbWUiLCAiZ2VuZV9zeW1ib2wiKV0sICAjIEVuc3VyZSBURVJNMkdFTkUgdXNlcyBjb3JyZWN0IGNvbHVtbnMKICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1KQogICMgQ2hlY2sgaWYgcmVzdWx0cyBleGlzdAogIGlmICghaXMubnVsbChoYWxsbWFya191cCkgJiYgbnJvdyhoYWxsbWFya191cCkgPiAwKSB7CiAgICAjIFZpc3VhbGl6ZSByZXN1bHRzIGlmIGF2YWlsYWJsZQogICAgdXBfZG90cGxvdCA8LSBkb3RwbG90KGhhbGxtYXJrX3VwLCBzaG93Q2F0ZWdvcnkgPSAyMCwgdGl0bGUgPSAiSGFsbG1hcmsgUGF0aHdheSBFbnJpY2htZW50IGZvciBVcHJlZ3VsYXRlZCBHZW5lcyIpCiAgICAKICAgICMgRGlzcGxheSB0aGUgcGxvdCBpbiB0aGUgbm90ZWJvb2sKICAgIHByaW50KHVwX2RvdHBsb3QpCiAgICAKICAgICMgU2F2ZSB0aGUgZG90cGxvdCB0byBhIFBORyBmaWxlCiAgICBnZ3NhdmUocGFzdGUwKG91dHB1dF9mb2xkZXIsICJoYWxsbWFya191cHJlZ3VsYXRlZF9kb3RwbG90LnBuZyIpLCBwbG90ID0gdXBfZG90cGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKICAgIAogICAgIyBPcHRpb25hbGx5LCBzYXZlIHRoZSByZXN1bHRzIGFzIENTVgogICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUoaGFsbG1hcmtfdXApLCBmaWxlID0gcGFzdGUwKG91dHB1dF9mb2xkZXIsICJoYWxsbWFya191cHJlZ3VsYXRlZF9lbnJpY2htZW50LmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKICB9IGVsc2UgewogICAgY2F0KCJObyBzaWduaWZpY2FudCBlbnJpY2htZW50IGZvdW5kIGZvciB1cHJlZ3VsYXRlZCBnZW5lcy5cbiIpCiAgfQp9IGVsc2UgewogIGNhdCgiTm8gdXByZWd1bGF0ZWQgZ2VuZXMgb3ZlcmxhcCB3aXRoIEhhbGxtYXJrIGdlbmUgc2V0cy5cbiIpCn0KCmlmIChsZW5ndGgoZG93bnJlZ3VsYXRlZF9pbl9oYWxsbWFyaykgPiAwKSB7CiAgIyBQZXJmb3JtIGVucmljaG1lbnQgYW5hbHlzaXMgZm9yIGRvd25yZWd1bGF0ZWQgZ2VuZXMgdXNpbmcgSGFsbG1hcmsgZ2VuZSBzZXRzCiAgaGFsbG1hcmtfZG93biA8LSBlbnJpY2hlcihnZW5lID0gZG93bnJlZ3VsYXRlZF9pbl9oYWxsbWFyaywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURVJNMkdFTkUgPSBoYWxsbWFya19zZXRzWywgYygiZ3NfbmFtZSIsICJnZW5lX3N5bWJvbCIpXSwgICMgRW5zdXJlIFRFUk0yR0VORSB1c2VzIGNvcnJlY3QgY29sdW1ucwogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKICAjIENoZWNrIGlmIHJlc3VsdHMgZXhpc3QKICBpZiAoIWlzLm51bGwoaGFsbG1hcmtfZG93bikgJiYgbnJvdyhoYWxsbWFya19kb3duKSA+IDApIHsKICAgICMgVmlzdWFsaXplIHJlc3VsdHMgaWYgYXZhaWxhYmxlCiAgICBkb3duX2RvdHBsb3QgPC0gZG90cGxvdChoYWxsbWFya19kb3duLCBzaG93Q2F0ZWdvcnkgPSAyMCwgdGl0bGUgPSAiSGFsbG1hcmsgUGF0aHdheSBFbnJpY2htZW50IGZvciBEb3ducmVndWxhdGVkIEdlbmVzIikKICAgIAogICAgIyBEaXNwbGF5IHRoZSBwbG90IGluIHRoZSBub3RlYm9vawogICAgcHJpbnQoZG93bl9kb3RwbG90KQogICAgCiAgICAjIFNhdmUgdGhlIGRvdHBsb3QgdG8gYSBQTkcgZmlsZQogICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfZG93bnJlZ3VsYXRlZF9kb3RwbG90LnBuZyIpLCBwbG90ID0gZG93bl9kb3RwbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQogICAgCiAgICAjIE9wdGlvbmFsbHksIHNhdmUgdGhlIHJlc3VsdHMgYXMgQ1NWCiAgICB3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShoYWxsbWFya19kb3duKSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfZG93bnJlZ3VsYXRlZF9lbnJpY2htZW50LmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKICB9IGVsc2UgewogICAgY2F0KCJObyBzaWduaWZpY2FudCBlbnJpY2htZW50IGZvdW5kIGZvciBkb3ducmVndWxhdGVkIGdlbmVzLlxuIikKICB9Cn0gZWxzZSB7CiAgY2F0KCJObyBkb3ducmVndWxhdGVkIGdlbmVzIG92ZXJsYXAgd2l0aCBIYWxsbWFyayBnZW5lIHNldHMuXG4iKQp9CgoKYGBgCgoKCgoKCgo=