4. Volcano Plot
library(ggplot2)
library(dplyr)
Attachement du package : ‘dplyr’
L'objet suivant est masqué depuis ‘package:Biobase’:
combine
Les objets suivants sont masqués depuis ‘package:GenomicRanges’:
intersect, setdiff, union
L'objet suivant est masqué depuis ‘package:GenomeInfoDb’:
intersect
Les objets suivants sont masqués depuis ‘package:IRanges’:
collapse, desc, intersect, setdiff, slice, union
Les objets suivants sont masqués depuis ‘package:S4Vectors’:
first, intersect, rename, setdiff, setequal, union
Les objets suivants sont masqués depuis ‘package:BiocGenerics’:
combine, intersect, setdiff, union
L'objet suivant est masqué depuis ‘package:matrixStats’:
count
Les objets suivants sont masqués depuis ‘package:stats’:
filter, lag
Les objets suivants sont masqués depuis ‘package:base’:
intersect, setdiff, setequal, union
# Extract results from Libra
DE_results_df <- as.data.frame(DE_results)
# Save results to CSV
write.csv(DE_results_df, file = "1-Pseudobulk_DEseq2_LRT_DE_with_libra.csv", row.names = FALSE)
# Ensure logFC and p-value columns are named correctly
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"
# Filtering for significant genes:
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"
)
)
ggplot(volcano_data, aes(x = avg_logFC, y = -log10(p_val_adj), color = significance)) +
geom_point(alpha = 0.6, size = 2) +
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") +
geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "black") +
geom_vline(xintercept = c(-2, 2), linetype = "dashed", color = "black")

NA
NA
Volcano Plot
library(EnhancedVolcano)
Le chargement a nécessité le package : ggrepel
EnhancedVolcano(
DE_results,
lab = DE_results$gene, # Labels for genes
x = 'avg_logFC', # Log2 Fold Change (corrected name)
y = 'p_val_adj', # Adjusted P-value
title = 'Volcano Plot: Pseudobulk DESeq2 Analysis',
subtitle = 'Malignant vs Normal CD4T Cells',
pCutoff = 0.05, # Adjusted p-value threshold
FCcutoff = 2, # Fold Change threshold
pointSize = 2.0, # Adjust point size
labSize = 4.0, # Adjust label size
col = c("black", "grey", "blue", "red"), # Color coding
colAlpha = 0.6, # Transparency for points
legendLabels = c("NS", "Log2FC", "P-value", "Both"),
legendPosition = "right",
legendLabSize = 10,
legendIconSize = 4.0,
drawConnectors = TRUE, # Connect gene labels
widthConnectors = 0.5,
vline = c(-2, 2), # Vertical lines at logFC ±2
hline = -log10(0.05), # Horizontal line at adjusted p-value 0.05
border = "full"
)

Volcano Plot
library(ggplot2)
library(dplyr)
library(ggrepel)
# Extract results from Libra
DE_results_df <- as.data.frame(DE_results)
# 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 = 3, 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)
# Extract results from Libra
DE_results_df <- as.data.frame(DE_results)
# 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 ~ "Highly Upregulated",
p_val_adj < 1e-20 & avg_logFC < -2 ~ "Highly 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(
"Highly Upregulated" = "darkred",
"Highly 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
9. 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 = 0.0000905,
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', 'lightblue', '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
)

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
Malignant_CD4Tcells_vs_Normal_CD4Tcells <- markers
EnhancedVolcano(Malignant_CD4Tcells_vs_Normal_CD4Tcells,
lab = Malignant_CD4Tcells_vs_Normal_CD4Tcells$gene,
x = "avg_logFC",
y = "p_val_adj",
title = "MAST with Batch Correction (All Genes)",
pCutoff = 0.05,
FCcutoff = 1.0)

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',
'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)

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_logFC)))
# Create the EnhancedVolcano plot with the filtered data
EnhancedVolcano(
filtered_genes,
lab = ifelse(filtered_genes$p_val_adj <= 0.05 & 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 = 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_logFC) >= 1.0] # Only label significant genes
)

EnhancedVolcano(
filtered_genes,
lab = ifelse(filtered_genes$p_val_adj <= 0.05 & 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 = 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_logFC) >= 1.0]
)

NA
NA
ggplot2 for Volcano
library(ggplot2)
library(ggrepel)
# Identify top and bottom genes
top_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells[Malignant_CD4Tcells_vs_Normal_CD4Tcells$p_val_adj < 0.05 & Malignant_CD4Tcells_vs_Normal_CD4Tcells$avg_logFC > 0.5, ]
bottom_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells[Malignant_CD4Tcells_vs_Normal_CD4Tcells$p_val_adj < 0.05 & Malignant_CD4Tcells_vs_Normal_CD4Tcells$avg_logFC < -0.5, ]
# Create a new column for color based on significance
Malignant_CD4Tcells_vs_Normal_CD4Tcells$color <- ifelse(Malignant_CD4Tcells_vs_Normal_CD4Tcells$avg_logFC > 0.5, "Upregulated genes",
ifelse(Malignant_CD4Tcells_vs_Normal_CD4Tcells$avg_logFC < -0.5, "Downregulated genes", "Nonsignificant"))
# Create a volcano plot
ggplot(Malignant_CD4Tcells_vs_Normal_CD4Tcells, aes(x = avg_logFC, y = -log10(p_val_adj))) +
geom_point(aes(color = color), alpha = 0.7, size = 2) +
# Add labels for top and bottom genes
geom_text_repel(data = top_genes, aes(label = gene), color = "black", vjust = 1, fontface = "bold") +
geom_text_repel(data = bottom_genes, aes(label = gene), color = "black", vjust = -1, fontface = "bold") +
# Customize labels and title
labs(title = "Volcano Plot",
x = "log2 Fold Change",
y = "-log10(p-value)") +
# # Add significance threshold lines
geom_hline(yintercept = -log10(0.00001), linetype = "dashed", color = "black") +
geom_vline(xintercept = c(-0.5, 0.5), linetype = "dashed", color = "black") +
# Set colors for top and bottom genes
scale_color_manual(values = c("Upregulated genes" = "red", "Downregulated genes" = "blue", "Nonsignificant" = "darkgrey")) +
# Customize theme if needed
theme_minimal()

NA
NA
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)
# 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',
'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
)
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.
10. Enrichment Analysis-1
# Load necessary libraries
library(clusterProfiler)
Registered S3 methods overwritten by 'treeio':
method from
MRCA.phylo tidytree
MRCA.treedata tidytree
Nnode.treedata tidytree
Ntip.treedata tidytree
ancestor.phylo tidytree
ancestor.treedata tidytree
child.phylo tidytree
child.treedata tidytree
full_join.phylo tidytree
full_join.treedata tidytree
groupClade.phylo tidytree
groupClade.treedata tidytree
groupOTU.phylo tidytree
groupOTU.treedata tidytree
is.rooted.treedata tidytree
nodeid.phylo tidytree
nodeid.treedata tidytree
nodelab.phylo tidytree
nodelab.treedata tidytree
offspring.phylo tidytree
offspring.treedata tidytree
parent.phylo tidytree
parent.treedata tidytree
root.treedata tidytree
rootnode.phylo tidytree
sibling.phylo tidytree
clusterProfiler v4.6.2 For help: https://yulab-smu.top/biomedical-knowledge-mining-book/
If you use clusterProfiler in published research, please cite:
T Wu, E Hu, S Xu, M Chen, P Guo, Z Dai, T Feng, L Zhou, W Tang, L Zhan, X Fu, S Liu, X Bo, and G Yu. clusterProfiler 4.0: A universal enrichment tool for interpreting omics data. The Innovation. 2021, 2(3):100141
Attachement du package : ‘clusterProfiler’
L'objet suivant est masqué depuis ‘package:IRanges’:
slice
L'objet suivant est masqué depuis ‘package:S4Vectors’:
rename
L'objet suivant est masqué depuis ‘package:stats’:
filter
library(org.Hs.eg.db)
Le chargement a nécessité le package : AnnotationDbi
Attachement du package : ‘AnnotationDbi’
L'objet suivant est masqué depuis ‘package:clusterProfiler’:
select
L'objet suivant est masqué depuis ‘package:dplyr’:
select
library(enrichplot)
library(ReactomePA)
ReactomePA v1.42.0 For help: https://yulab-smu.top/biomedical-knowledge-mining-book/
If you use ReactomePA in published research, please cite:
Guangchuang Yu, Qing-Yu He. ReactomePA: an R/Bioconductor package for reactome pathway analysis and visualization. Molecular BioSystems 2016, 12(2):477-479
library(DOSE) # For GSEA analysis
DOSE v3.24.2 For help: https://yulab-smu.top/biomedical-knowledge-mining-book/
If you use DOSE in published research, please cite:
Guangchuang Yu, Li-Gen Wang, Guang-Rong Yan, Qing-Yu He. DOSE: an R/Bioconductor package for Disease Ontology Semantic and Enrichment analysis. Bioinformatics 2015, 31(4):608-609
library(ggplot2) # Ensure ggplot2 is available for plotting
# Define threshold for differential expression selection (modified thresholds)
logFC_up_threshold <- 1 # Upregulated logFC threshold
logFC_down_threshold <- -1 # Downregulated logFC threshold
pval_threshold <- 0.05 # p-value threshold as specified
# Load your differential expression results (modify based on actual data structure)
# Malignant_CD4Tcells_vs_Normal_CD4Tcells <- read.csv("Your_DE_Results_File.csv")
# Select upregulated and downregulated genes
upregulated_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells[
Malignant_CD4Tcells_vs_Normal_CD4Tcells$avg_logFC > logFC_up_threshold &
Malignant_CD4Tcells_vs_Normal_CD4Tcells$p_val_adj < pval_threshold, ]
downregulated_genes <- Malignant_CD4Tcells_vs_Normal_CD4Tcells[
Malignant_CD4Tcells_vs_Normal_CD4Tcells$avg_logFC < logFC_down_threshold &
Malignant_CD4Tcells_vs_Normal_CD4Tcells$p_val_adj < pval_threshold, ]
# Check for missing genes (NAs) in the gene column and remove them
upregulated_genes <- na.omit(upregulated_genes)
downregulated_genes <- na.omit(downregulated_genes)
# Save upregulated and downregulated gene results to CSV
write.csv(upregulated_genes, "Malignant_vs_Control/upregulated_genes.csv", row.names = FALSE)
write.csv(downregulated_genes, "Malignant_vs_Control/downregulated_genes.csv", row.names = FALSE)
# Convert gene symbols to Entrez IDs for enrichment analysis, with checks for missing values
upregulated_entrez <- bitr(upregulated_genes$gene, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)
'select()' returned 1:1 mapping between keys and columns
Avis : 14.39% 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 : 26.94% 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")
View(upregulated_genes)
View(downregulated_genes)

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")
No significant Reactome pathways found for: Reactome Pathway Enrichment for Downregulated Genes
Enrichment Analysis-2-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: 588
cat("Number of downregulated genes in Hallmark gene sets:", length(downregulated_in_hallmark), "\n")
Number of downregulated genes in Hallmark gene sets: 264
# 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
LS0tCnRpdGxlOiAiUHNldWRvQnVsayBBbmFseXNpcyB1c2luZyBMaWJyYSBSTkEgYXNzYXktRGVzZXEyLUxSVCIKYXV0aG9yOiBOYXNpciBNYWhtb29kIEFiYmFzaQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAjIHBkZl9kb2N1bWVudDogZGVmYXVsdAogICMgd29yZF9kb2N1bWVudDogZGVmYXVsdAogICMgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogICNybWRmb3JtYXRzOjpyZWFkdGhlZG93bgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQotLS0KCiMgMS4gbG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CgojIExvYWQgbGlicmFyaWVzCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShERVNlcTIpCmxpYnJhcnkoTGlicmEpCgpgYGAKCgojIDIuIGxvYWQgc2V1cmF0IG9iamVjdApgYGB7ciBsb2FkX3NldXJhdH0KI0xvYWQgU2V1cmF0IE9iamVjdCBMNwpsb2FkKCIvaG9tZS9uYWJiYXNpL2lzaWxvbi9Ub19UcmFuc2Zlcl9iZXR3ZWVuX2NvbXB1dGVycy8yMy1IYXJtb255X0ludGVncmF0aW9uLzAtcm9iai81LUhhcm1vbnlfSW50ZWdyYXRlZF9BbGxfc2FtcGxlc19NZXJnZWRfQ0Q0VGNlbGxzX2ZpbmFsX1Jlc29sdXRpb25fU2VsZWN0ZWRfMC44X0FEVF9Ob3JtYWxpemVkX2NsZWFuZWRfbXQucm9iaiIpCgpwc2V1ZG9idWxrX3NldXJhdCA8LSBBbGxfc2FtcGxlc19NZXJnZWQKCiMgQXNzaWduIGxhYmVscyBhbmQgZW5zdXJlICdDb250cm9sJyBpcyB0aGUgcmVmZXJlbmNlCnBzZXVkb2J1bGtfc2V1cmF0JGxhYmVsIDwtIGZhY3RvcigKICBpZmVsc2UocHNldWRvYnVsa19zZXVyYXQkb3JpZy5pZGVudCAlaW4lIGMoIlBCTUMiLCAiUEJNQzEweCIpLCAKICAgICAgICAgIkNvbnRyb2wiLCAKICAgICAgICAgIk1hbGlnbmFudCIpLAogIGxldmVscyA9IGMoIk1hbGlnbmFudCIsICJDb250cm9sIikgIAopCgoKIyBEb3VibGUtY2hlY2sgdGhlIHJlZmVyZW5jZSBsZXZlbApwcmludChsZXZlbHMocHNldWRvYnVsa19zZXVyYXQkbGFiZWwpKSAgIyBTaG91bGQgcHJpbnQgIkNvbnRyb2wiIGZpcnN0CgojIFZlcmlmeSBmYWN0b3IgbGV2ZWxzCnByaW50KGxldmVscyhwc2V1ZG9idWxrX3NldXJhdCRsYWJlbCkpICAjIFNob3VsZCBzaG93IENvbnRyb2wgZmlyc3QKCiMgRW5zdXJlICdyZXBsaWNhdGUnIGlzIGEgZmFjdG9yCnBzZXVkb2J1bGtfc2V1cmF0JHJlcGxpY2F0ZSA8LSBhcy5mYWN0b3IocHNldWRvYnVsa19zZXVyYXQkY2VsbF9saW5lKQoKIyBSZW5hbWUgdGhlIGNlbGwgdHlwZSBjb2x1bW4KcHNldWRvYnVsa19zZXVyYXQkY2VsbF90eXBlIDwtICJDRDRUIgpgYGAKCiMgMy4gREUgdXNpbmcgTElCUkEKYGBge3IgLCBmaWcuaGVpZ2h0PTE0LCBmaWcud2lkdGg9MTh9CmxpYnJhX3Rlc3QgPC1wc2V1ZG9idWxrX3NldXJhdAoKCgpERV9yZXN1bHRzIDwtIHJ1bl9kZSgKICBwc2V1ZG9idWxrX3NldXJhdCwKICBjZWxsX3R5cGVfY29sID0gImNlbGxfdHlwZSIsIAogIHJlcGxpY2F0ZV9jb2wgPSAicmVwbGljYXRlIiwKICBsYWJlbF9jb2wgPSAibGFiZWwiLAogIGRlX2ZhbWlseSA9ICJwc2V1ZG9idWxrIiwgICAKICBkZV9tZXRob2QgPSAiREVTZXEyIiwgICAgICAgIAogIGRlX3R5cGUgPSAiTFJUIiwKKQoKYGBgCgoKIyA0LiBWb2xjYW5vIFBsb3QKYGBge3IgLCBmaWcuaGVpZ2h0PTE0LCBmaWcud2lkdGg9MTh9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKCiMgRXh0cmFjdCByZXN1bHRzIGZyb20gTGlicmEKREVfcmVzdWx0c19kZiA8LSBhcy5kYXRhLmZyYW1lKERFX3Jlc3VsdHMpCgojIFNhdmUgcmVzdWx0cyB0byBDU1YKd3JpdGUuY3N2KERFX3Jlc3VsdHNfZGYsIGZpbGUgPSAiMS1Qc2V1ZG9idWxrX0RFc2VxMl9MUlRfREVfd2l0aF9saWJyYS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMgRW5zdXJlIGxvZ0ZDIGFuZCBwLXZhbHVlIGNvbHVtbnMgYXJlIG5hbWVkIGNvcnJlY3RseQpjb2xuYW1lcyhERV9yZXN1bHRzX2RmKQoKIyBGaWx0ZXJpbmcgZm9yIHNpZ25pZmljYW50IGdlbmVzOgp2b2xjYW5vX2RhdGEgPC0gREVfcmVzdWx0c19kZiAlPiUKICBtdXRhdGUoCiAgICBzaWduaWZpY2FuY2UgPSBjYXNlX3doZW4oCiAgICAgIHBfdmFsX2FkaiA8IDAuMDUgJiBhdmdfbG9nRkMgPiAyIH4gIlVwcmVndWxhdGVkIiwKICAgICAgcF92YWxfYWRqIDwgMC4wNSAmIGF2Z19sb2dGQyA8IC0yIH4gIkRvd25yZWd1bGF0ZWQiLAogICAgICBUUlVFIH4gIk5vdCBTaWduaWZpY2FudCIKICAgICkKICApCmdncGxvdCh2b2xjYW5vX2RhdGEsIGFlcyh4ID0gYXZnX2xvZ0ZDLCB5ID0gLWxvZzEwKHBfdmFsX2FkaiksIGNvbG9yID0gc2lnbmlmaWNhbmNlKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYsIHNpemUgPSAyKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlVwcmVndWxhdGVkIiA9ICJyZWQiLCAiRG93bnJlZ3VsYXRlZCIgPSAiYmx1ZSIsICJOb3QgU2lnbmlmaWNhbnQiID0gImdyZXkiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJWb2xjYW5vIFBsb3Q6IFBzZXVkb2J1bGsgREVTZXEyIEFuYWx5c2lzIiwKICAgICAgIHggPSAiTG9nMiBGb2xkIENoYW5nZSIsCiAgICAgICB5ID0gIi1Mb2cxMCBBZGp1c3RlZCBQLVZhbHVlIiwKICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoMC4wNSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLTIsIDIpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpCgoKYGBgCgoKCgojIyBWb2xjYW5vIFBsb3QKYGBge3IgLCBmaWcuaGVpZ2h0PTE0LCBmaWcud2lkdGg9MTh9CmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQoKRW5oYW5jZWRWb2xjYW5vKAogIERFX3Jlc3VsdHMsIAogIGxhYiA9IERFX3Jlc3VsdHMkZ2VuZSwgICMgTGFiZWxzIGZvciBnZW5lcwogIHggPSAnYXZnX2xvZ0ZDJywgICAgICAgICAgICAgIyBMb2cyIEZvbGQgQ2hhbmdlIChjb3JyZWN0ZWQgbmFtZSkKICB5ID0gJ3BfdmFsX2FkaicsICAgICAgICAgICAgICMgQWRqdXN0ZWQgUC12YWx1ZQogIHRpdGxlID0gJ1ZvbGNhbm8gUGxvdDogUHNldWRvYnVsayBERVNlcTIgQW5hbHlzaXMnLAogIHN1YnRpdGxlID0gJ01hbGlnbmFudCB2cyBOb3JtYWwgQ0Q0VCBDZWxscycsCiAgcEN1dG9mZiA9IDAuMDUsICAgICAgICAgICAgICAjIEFkanVzdGVkIHAtdmFsdWUgdGhyZXNob2xkCiAgRkNjdXRvZmYgPSAyLCAgICAgICAgICAgICAgICAjIEZvbGQgQ2hhbmdlIHRocmVzaG9sZAogIHBvaW50U2l6ZSA9IDIuMCwgICAgICAgICAgICAgIyBBZGp1c3QgcG9pbnQgc2l6ZQogIGxhYlNpemUgPSA0LjAsICAgICAgICAgICAgICAgIyBBZGp1c3QgbGFiZWwgc2l6ZQogIGNvbCA9IGMoImJsYWNrIiwgImdyZXkiLCAiYmx1ZSIsICJyZWQiKSwgIyBDb2xvciBjb2RpbmcKICBjb2xBbHBoYSA9IDAuNiwgICAgICAgICAgICAgICMgVHJhbnNwYXJlbmN5IGZvciBwb2ludHMKICBsZWdlbmRMYWJlbHMgPSBjKCJOUyIsICJMb2cyRkMiLCAiUC12YWx1ZSIsICJCb3RoIiksCiAgbGVnZW5kUG9zaXRpb24gPSAicmlnaHQiLAogIGxlZ2VuZExhYlNpemUgPSAxMCwKICBsZWdlbmRJY29uU2l6ZSA9IDQuMCwKICBkcmF3Q29ubmVjdG9ycyA9IFRSVUUsICAgICAgICMgQ29ubmVjdCBnZW5lIGxhYmVscwogIHdpZHRoQ29ubmVjdG9ycyA9IDAuNSwKICB2bGluZSA9IGMoLTIsIDIpLCAgICAgICAgICAgICMgVmVydGljYWwgbGluZXMgYXQgbG9nRkMgwrEyCiAgaGxpbmUgPSAtbG9nMTAoMC4wNSksICAgICAgICAjIEhvcml6b250YWwgbGluZSBhdCBhZGp1c3RlZCBwLXZhbHVlIDAuMDUKICBib3JkZXIgPSAiZnVsbCIKKQoKYGBgCgoKCgojIyBWb2xjYW5vIFBsb3QKYGBge3IgLCBmaWcuaGVpZ2h0PTE0LCBmaWcud2lkdGg9MTh9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3JlcGVsKQoKIyBFeHRyYWN0IHJlc3VsdHMgZnJvbSBMaWJyYQpERV9yZXN1bHRzX2RmIDwtIGFzLmRhdGEuZnJhbWUoREVfcmVzdWx0cykKCiMgRW5zdXJlIGNvcnJlY3QgY29sdW1uIG5hbWVzCmNvbG5hbWVzKERFX3Jlc3VsdHNfZGYpCgojIERlZmluZSBzaWduaWZpY2FuY2UgY2F0ZWdvcmllcwp2b2xjYW5vX2RhdGEgPC0gREVfcmVzdWx0c19kZiAlPiUKICBtdXRhdGUoCiAgICBzaWduaWZpY2FuY2UgPSBjYXNlX3doZW4oCiAgICAgIHBfdmFsX2FkaiA8IDAuMDUgJiBhdmdfbG9nRkMgPiAyIH4gIlVwcmVndWxhdGVkIiwKICAgICAgcF92YWxfYWRqIDwgMC4wNSAmIGF2Z19sb2dGQyA8IC0yIH4gIkRvd25yZWd1bGF0ZWQiLAogICAgICBUUlVFIH4gIk5vdCBTaWduaWZpY2FudCIKICAgICkKICApCgojIFNlbGVjdCBnZW5lcyB0byBsYWJlbDogcF92YWxfYWRqIDwgMWUtNTAgT1IgbG9nRkMgPiAyIE9SIGxvZ0ZDIDwgLTIKdG9wX2dlbmVzIDwtIHZvbGNhbm9fZGF0YSAlPiUKICBmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSB8IGF2Z19sb2dGQyA+IDIgfCBhdmdfbG9nRkMgPCAtMikKCmdncGxvdCh2b2xjYW5vX2RhdGEsIGFlcyh4ID0gYXZnX2xvZ0ZDLCB5ID0gLWxvZzEwKHBfdmFsX2FkaiksIGNvbG9yID0gc2lnbmlmaWNhbmNlKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYsIHNpemUgPSAyKSArICAjIE1haW4gcG9pbnRzCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlVwcmVndWxhdGVkIiA9ICJyZWQiLCAiRG93bnJlZ3VsYXRlZCIgPSAiYmx1ZSIsICJOb3QgU2lnbmlmaWNhbnQiID0gImdyZXkiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJWb2xjYW5vIFBsb3Q6IFBzZXVkb2J1bGsgREVTZXEyIEFuYWx5c2lzIiwKICAgICAgIHggPSAiTG9nMiBGb2xkIENoYW5nZSIsCiAgICAgICB5ID0gIi1Mb2cxMCBBZGp1c3RlZCBQLVZhbHVlIiwKICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsKCiAgIyBBZGQgZ2VuZSBsYWJlbHMgV0lUSE9VVCBhbnkgbGluZXMgY29ubmVjdGluZyB0aGVtCiAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSB0b3BfZ2VuZXMsIAogICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBnZW5lKSwgIAogICAgICAgICAgICAgICAgICBzaXplID0gMywgYm94LnBhZGRpbmcgPSAwLjMsIG1heC5vdmVybGFwcyA9IDE1LCBzZWdtZW50LmNvbG9yID0gTkEpICsgIAoKICAjIEFkZCB0aHJlc2hvbGQgbGluZXMKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKC0yLCAyKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmxhY2siKSArICAjIGxvZ0ZDIHRocmVzaG9sZHMKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoMC4wNSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKyAgIyBwLXZhbHVlIHRocmVzaG9sZAoKICB5bGltKDAsIDcwKSAgIyBTZXQgbWF4IHktYXhpcyBsaW1pdCB0byBhdm9pZCBleHRyZW1lIHZhbHVlcwoKCmBgYAojIyBWb2xjYW5vIFBsb3QKYGBge3IgLCBmaWcuaGVpZ2h0PTE0LCBmaWcud2lkdGg9MTh9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3JlcGVsKQoKIyBFeHRyYWN0IHJlc3VsdHMgZnJvbSBMaWJyYQpERV9yZXN1bHRzX2RmIDwtIGFzLmRhdGEuZnJhbWUoREVfcmVzdWx0cykKCiMgRW5zdXJlIGNvcnJlY3QgY29sdW1uIG5hbWVzCmNvbG5hbWVzKERFX3Jlc3VsdHNfZGYpCgojIERlZmluZSBzaWduaWZpY2FuY2UgY2F0ZWdvcmllcwp2b2xjYW5vX2RhdGEgPC0gREVfcmVzdWx0c19kZiAlPiUKICBtdXRhdGUoCiAgICBzaWduaWZpY2FuY2UgPSBjYXNlX3doZW4oCiAgICAgIHBfdmFsX2FkaiA8IDFlLTIwICYgYXZnX2xvZ0ZDID4gMiB+ICJIaWdobHkgVXByZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAxZS0yMCAmIGF2Z19sb2dGQyA8IC0yIH4gIkhpZ2hseSBEb3ducmVndWxhdGVkIiwKICAgICAgcF92YWxfYWRqIDwgMC4wNSAmIGF2Z19sb2dGQyA+IDIgfiAiVXByZWd1bGF0ZWQiLAogICAgICBwX3ZhbF9hZGogPCAwLjA1ICYgYXZnX2xvZ0ZDIDwgLTIgfiAiRG93bnJlZ3VsYXRlZCIsCiAgICAgIFRSVUUgfiAiTm90IFNpZ25pZmljYW50IgogICAgKQogICkKCiMgU2VsZWN0IG9ubHkgdmVyeSBzaWduaWZpY2FudCBnZW5lcyBmb3IgbGFiZWxpbmcKdG9wX2dlbmVzIDwtIHZvbGNhbm9fZGF0YSAlPiUKICBmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSAmIChhdmdfbG9nRkMgPiAyIHwgYXZnX2xvZ0ZDIDwgLTIpKQoKZ2dwbG90KHZvbGNhbm9fZGF0YSwgYWVzKHggPSBhdmdfbG9nRkMsIHkgPSAtbG9nMTAocF92YWxfYWRqKSwgY29sb3IgPSBzaWduaWZpY2FuY2UpKSArCiAgCiAgIyBNYWluIHBvaW50cwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIHNpemUgPSAyLjUpICsKICAKICAjIEhpZ2hsaWdodCBoaWdobHkgc2lnbmlmaWNhbnQgZ2VuZXMgd2l0aCBsYXJnZXIgcG9pbnRzCiAgZ2VvbV9wb2ludChkYXRhID0gdG9wX2dlbmVzLCBhZXMoeCA9IGF2Z19sb2dGQywgeSA9IC1sb2cxMChwX3ZhbF9hZGopKSwgCiAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzLCBzaGFwZSA9IDIxLCBmaWxsID0gImJsYWNrIikgKwoKICAjIEN1c3RvbSBjb2xvciBzY2hlbWUKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygKICAgICJIaWdobHkgVXByZWd1bGF0ZWQiID0gImRhcmtyZWQiLAogICAgIkhpZ2hseSBEb3ducmVndWxhdGVkIiA9ICJkYXJrYmx1ZSIsCiAgICAiVXByZWd1bGF0ZWQiID0gInJlZCIsCiAgICAiRG93bnJlZ3VsYXRlZCIgPSAiYmx1ZSIsCiAgICAiTm90IFNpZ25pZmljYW50IiA9ICJncmV5IgogICkpICsKCiAgIyBBZGQgZ2VuZSBsYWJlbHMgKG9ubHkgZm9yIGhpZ2hseSBzaWduaWZpY2FudCBnZW5lcykKICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHRvcF9nZW5lcywgYWVzKGxhYmVsID0gZ2VuZSksICAKICAgICAgICAgICAgICAgICAgc2l6ZSA9IDQsIGJveC5wYWRkaW5nID0gMC41LCBtYXgub3ZlcmxhcHMgPSAxMCwgc2VnbWVudC5jb2xvciA9IE5BKSArCiAgCiAgIyBBZGQgdGhyZXNob2xkIGxpbmVzCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygtMiwgMiksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKyAgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsgIAoKICAjIEltcHJvdmUgdGhlbWUKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE0KSArCiAgbGFicyh0aXRsZSA9ICJWb2xjYW5vIFBsb3Q6IFBzZXVkb2J1bGsgREVTZXEyIEFuYWx5c2lzIiwKICAgICAgIHggPSAiTG9nMiBGb2xkIENoYW5nZSIsCiAgICAgICB5ID0gIi1Mb2cxMCBBZGp1c3RlZCBQLVZhbHVlIiwKICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsKCiAgeWxpbSgwLCA1MCkgICMgQXZvaWQgZXh0cmVtZSBzY2FsaW5nIGlzc3VlcwoKCmBgYAoKIyA1LiBTdW1tYXJpemUgTWFya2VycwpgYGB7ciAsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNH0KbWFya2VycyA8LSBERV9yZXN1bHRzX2RmCgpzdW1tYXJpemVfbWFya2VycyA8LSBmdW5jdGlvbihtYXJrZXJzKSB7CiAgbnVtX3B2YWwwIDwtIHN1bShtYXJrZXJzJHBfdmFsX2FkaiA9PSAwKQogIG51bV9wdmFsMSA8LSBzdW0obWFya2VycyRwX3ZhbF9hZGogPT0gMSkKICBudW1fc2lnbmlmaWNhbnQgPC0gc3VtKG1hcmtlcnMkcF92YWxfYWRqIDwgMC4wNSkKICBudW1fdXByZWd1bGF0ZWQgPC0gc3VtKG1hcmtlcnMkYXZnX2xvZ0ZDID4gMSkKICBudW1fZG93bnJlZ3VsYXRlZCA8LSBzdW0obWFya2VycyRhdmdfbG9nRkMgPCAtMSkKICAKICBjYXQoIk51bWJlciBvZiBnZW5lcyB3aXRoIHBfdmFsX2FkaiA9IDA6IiwgbnVtX3B2YWwwLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGdlbmVzIHdpdGggcF92YWxfYWRqID0gMToiLCBudW1fcHZhbDEsICJcbiIpCiAgY2F0KCJOdW1iZXIgb2Ygc2lnbmlmaWNhbnQgZ2VuZXMgKHBfdmFsX2FkaiA8IDAuMDUpOiIsIG51bV9zaWduaWZpY2FudCwgIlxuIikKICBjYXQoIk51bWJlciBvZiB1cHJlZ3VsYXRlZCBnZW5lcyAoYXZnX2xvZ0ZDID4gMSk6IiwgbnVtX3VwcmVndWxhdGVkLCAiXG4iKQogIGNhdCgiTnVtYmVyIG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMgKGF2Z19sb2dGQyA8IDEpOiIsIG51bV9kb3ducmVndWxhdGVkLCAiXG4iKQp9CgpjYXQoIk1hcmtlcnMxIFN1bW1hcnkgKE1BU1Qgd2l0aCBCYXRjaCBDb3JyZWN0aW9uKTpcbiIpCgpzdW1tYXJpemVfbWFya2VycyhtYXJrZXJzKQpgYGAKCgojIDkuIEVuaGFuY2VkVm9sY2FubyBwbG90CmBgYHtyICwgZmlnLmhlaWdodD0xNiwgZmlnLndpZHRoPTIwfQoKbGlicmFyeShkcGx5cikKbGlicmFyeShFbmhhbmNlZFZvbGNhbm8pCgojIEFzc3VtaW5nIHlvdSBoYXZlIGEgZGF0YSBmcmFtZSBuYW1lZCBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMKIyBGaWx0ZXIgZ2VuZXMgYmFzZWQgb24gbG93ZXN0IHAtdmFsdWVzIGJ1dCBpbmNsdWRlIGFsbCBnZW5lcwpmaWx0ZXJlZF9nZW5lcyA8LSBtYXJrZXJzICU+JQogIGFycmFuZ2UocF92YWxfYWRqLCBkZXNjKGFicyhhdmdfbG9nRkMpKSkKCiMgQ3JlYXRlIHRoZSBFbmhhbmNlZFZvbGNhbm8gcGxvdCB3aXRoIHRoZSBmaWx0ZXJlZCBkYXRhCkVuaGFuY2VkVm9sY2FubygKICBmaWx0ZXJlZF9nZW5lcywgCiAgbGFiID0gaWZlbHNlKGZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjAwMDA5MDUgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZ0ZDKSA+PSAxLjAsIGZpbHRlcmVkX2dlbmVzJGdlbmUsIE5BKSwKICB4ID0gImF2Z19sb2dGQyIsIAogIHkgPSAicF92YWxfYWRqIiwKICB0aXRsZSA9ICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMoY2VsbCBsaW5lcykgdnMgbm9ybWFsIENENCBUIGNlbGxzIiwKICBwQ3V0b2ZmID0gMC4wMDAwOTA1LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywgCiAgbGFiQ29sID0gJ2JsYWNrJywKICBsYWJGYWNlID0gJ2JvbGQnLAogIGJveGVkTGFiZWxzID0gRkFMU0UsICAjIFNldCB0byBGQUxTRSB0byByZW1vdmUgYm94ZWQgbGFiZWxzCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgY29sID0gYygnZ3JleTcwJywgJ2xpZ2h0Ymx1ZScsICdibHVlJywgJ3JlZCcpLCAgIyBDdXN0b21pemUgcG9pbnQgY29sb3JzCiAgc2VsZWN0TGFiID0gZmlsdGVyZWRfZ2VuZXMkZ2VuZVtmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMC4wNSAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nRkMpID49IDEuMF0gICMgT25seSBsYWJlbCBzaWduaWZpY2FudCBnZW5lcwopCgpgYGAKCgoKIyMgRW5oYW5jZWRWb2xjYW5vIHBsb3QKYGBge3IgLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCiMgQXNzdW1pbmcgeW91IGhhdmUgYSBkYXRhIGZyYW1lIG5hbWVkIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscwojIEZpbHRlciBnZW5lcyBiYXNlZCBvbiBsb3dlc3QgcC12YWx1ZXMgYnV0IGluY2x1ZGUgYWxsIGdlbmVzCmZpbHRlcmVkX2dlbmVzIDwtIG1hcmtlcnMgJT4lCiAgYXJyYW5nZShwX3ZhbF9hZGosIGRlc2MoYWJzKGF2Z19sb2dGQykpKQoKIyBDcmVhdGUgdGhlIEVuaGFuY2VkVm9sY2FubyBwbG90IHdpdGggdGhlIGZpbHRlcmVkIGRhdGEKRW5oYW5jZWRWb2xjYW5vKAogIGZpbHRlcmVkX2dlbmVzLCAKICBsYWIgPSBpZmVsc2UoZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDAuMDAwMDkwNSAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nRkMpID49IDEuMCwgZmlsdGVyZWRfZ2VuZXMkZ2VuZSwgTkEpLAogIHggPSAiYXZnX2xvZ0ZDIiwgCiAgeSA9ICJwX3ZhbF9hZGoiLAogIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHBDdXRvZmYgPSAxZS00LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywgCiAgbGFiQ29sID0gJ2JsYWNrJywKICBsYWJGYWNlID0gJ2JvbGQnLAogIGJveGVkTGFiZWxzID0gRkFMU0UsICAjIFNldCB0byBGQUxTRSB0byByZW1vdmUgYm94ZWQgbGFiZWxzCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbWl6ZSBwb2ludCBjb2xvcnMKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2dGQykgPj0gMS4wXSAgIyBPbmx5IGxhYmVsIHNpZ25pZmljYW50IGdlbmVzCikKCgoKYGBgCgojIyBDcmVhdGUgdGhlIEVuaGFuY2VkVm9sY2FubyBwbG90CmBgYHtyIGVuaGFuY2VkViwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQogTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzIDwtIG1hcmtlcnMKCkVuaGFuY2VkVm9sY2FubyhNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMsCiAgICAgICAgICAgICAgICBsYWIgPSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMkZ2VuZSwKICAgICAgICAgICAgICAgIHggPSAiYXZnX2xvZ0ZDIiwKICAgICAgICAgICAgICAgIHkgPSAicF92YWxfYWRqIiwKICAgICAgICAgICAgICAgIHRpdGxlID0gIk1BU1Qgd2l0aCBCYXRjaCBDb3JyZWN0aW9uIChBbGwgR2VuZXMpIiwKICAgICAgICAgICAgICAgIHBDdXRvZmYgPSAwLjA1LAogICAgICAgICAgICAgICAgRkNjdXRvZmYgPSAxLjApCgoKRW5oYW5jZWRWb2xjYW5vKE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscywgCiAgICAgICAgICAgICAgICBsYWIgPSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMkZ2VuZSwKICAgICAgICAgICAgICAgIHggPSAiYXZnX2xvZ0ZDIiwgCiAgICAgICAgICAgICAgICB5ID0gInBfdmFsX2FkaiIsCiAgICAgICAgICAgICAgICBzZWxlY3RMYWIgPSBjKCdFUENBTScsICdCQ0FUMScsICdLSVIzREwyJywgJ0ZPWE0xJywgJ1RXSVNUMScsICdUTkZTRjknLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0NEODAnLCAgJ0lMMUInLCAnUlBTNFkxJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdJTDdSJywgJ1RDRjcnLCAgJ01LSTY3JywgJ0NENzAnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0lMMlJBJywnVFJCVjYtMicsICdUUkJWMTAtMycsICdUUkJWNC0yJywgJ1RSQlY5JywgJ1RSQlY3LTknLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1RSQVYxMi0xJywgJ0NEOEInLCAnRkNHUjNBJywgJ0dOTFknLCAnRk9YUDMnLCAnU0VMTCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnR0lNQVAxJywgJ1JJUE9SMicsICdMRUYxJywgJ0hPWEM5JywgJ1NQNScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdDQ0wxNycsICdFVFY0JywgJ1RIWTEnLCAnRk9YQTInLCAnSVRHQUQnLCAnUzEwMFAnLCAnVEJYNCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSUQxJywgJ1hDTDEnLCAnU09YMicsICdDRDI3JywgJ0NEMjgnLCdQTFMzJywnQ0Q3MCcsJ1JBQjI1JyAsICdUUkJWMjcnLCAnVFJCVjInKSwKICAgICAgICAgICAgICAgIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogICAgICAgICAgICAgICAgeGxhYiA9IGJxdW90ZSh+TG9nWzJdfiAnZm9sZCBjaGFuZ2UnKSwKICAgICAgICAgICAgICAgIHBDdXRvZmYgPSAwLjA1LAogICAgICAgICAgICAgICAgRkNjdXRvZmYgPSAxLjUsIAogICAgICAgICAgICAgICAgcG9pbnRTaXplID0gMy4wLAogICAgICAgICAgICAgICAgbGFiU2l6ZSA9IDUuMCwKICAgICAgICAgICAgICAgIGJveGVkTGFiZWxzID0gVFJVRSwKICAgICAgICAgICAgICAgIGNvbEFscGhhID0gMC41LAogICAgICAgICAgICAgICAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLAogICAgICAgICAgICAgICAgbGVnZW5kTGFiU2l6ZSA9IDEwLAogICAgICAgICAgICAgICAgbGVnZW5kSWNvblNpemUgPSA0LjAsCiAgICAgICAgICAgICAgICBkcmF3Q29ubmVjdG9ycyA9IFRSVUUsCiAgICAgICAgICAgICAgICB3aWR0aENvbm5lY3RvcnMgPSAwLjUsCiAgICAgICAgICAgICAgICBjb2xDb25uZWN0b3JzID0gJ2dyZXk1MCcsCiAgICAgICAgICAgICAgICBhcnJvd2hlYWRzID0gRkFMU0UsCiAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSAzMCkKCgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCiMgQXNzdW1pbmcgeW91IGhhdmUgYSBkYXRhIGZyYW1lIG5hbWVkIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscwojIEZpbHRlciBnZW5lcyBiYXNlZCBvbiBsb3dlc3QgcC12YWx1ZXMgYnV0IGluY2x1ZGUgYWxsIGdlbmVzCmZpbHRlcmVkX2dlbmVzIDwtIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyAlPiUKICBhcnJhbmdlKHBfdmFsX2FkaiwgZGVzYyhhYnMoYXZnX2xvZ0ZDKSkpCgojIENyZWF0ZSB0aGUgRW5oYW5jZWRWb2xjYW5vIHBsb3Qgd2l0aCB0aGUgZmlsdGVyZWQgZGF0YQpFbmhhbmNlZFZvbGNhbm8oCiAgZmlsdGVyZWRfZ2VuZXMsIAogIGxhYiA9IGlmZWxzZShmaWx0ZXJlZF9nZW5lcyRwX3ZhbF9hZGogPD0gMC4wNSAmIGFicyhmaWx0ZXJlZF9nZW5lcyRhdmdfbG9nRkMpID49IDEuMCwgZmlsdGVyZWRfZ2VuZXMkZ2VuZSwgTkEpLAogIHggPSAiYXZnX2xvZ0ZDIiwgCiAgeSA9ICJwX3ZhbF9hZGoiLAogIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHBDdXRvZmYgPSAwLjA1LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywgCiAgbGFiQ29sID0gJ2JsYWNrJywKICBsYWJGYWNlID0gJ2JvbGQnLAogIGJveGVkTGFiZWxzID0gRkFMU0UsICAjIFNldCB0byBGQUxTRSB0byByZW1vdmUgYm94ZWQgbGFiZWxzCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbWl6ZSBwb2ludCBjb2xvcnMKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2dGQykgPj0gMS4wXSAgIyBPbmx5IGxhYmVsIHNpZ25pZmljYW50IGdlbmVzCikKCgoKRW5oYW5jZWRWb2xjYW5vKAogIGZpbHRlcmVkX2dlbmVzLCAKICBsYWIgPSBpZmVsc2UoZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDAuMDUgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZ0ZDKSA+PSAxLjAsIGZpbHRlcmVkX2dlbmVzJGdlbmUsIE5BKSwKICB4ID0gImF2Z19sb2dGQyIsIAogIHkgPSAicF92YWxfYWRqIiwKICB0aXRsZSA9ICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMgKGNlbGwgbGluZXMpIHZzIE5vcm1hbCBDRDQgVCBjZWxscyIsCiAgc3VidGl0bGUgPSAiSGlnaGxpZ2h0aW5nIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyIsCiAgcEN1dG9mZiA9IDAuMDUsCiAgRkNjdXRvZmYgPSAxLjAsCiAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLAogIGNvbEFscGhhID0gMC44LCAgIyBTbGlnaHQgdHJhbnNwYXJlbmN5IGZvciBub24tc2lnbmlmaWNhbnQgcG9pbnRzCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbSBjb2xvciBzY2hlbWUKICBncmlkbGluZXMubWFqb3IgPSBUUlVFLAogIGdyaWRsaW5lcy5taW5vciA9IEZBTFNFLAogIHNlbGVjdExhYiA9IGZpbHRlcmVkX2dlbmVzJGdlbmVbZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDAuMDUgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZ0ZDKSA+PSAxLjBdCikgCgoKYGBgCgojIyBnZ3Bsb3QyIGZvciBWb2xjYW5vCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQoKIyBJZGVudGlmeSB0b3AgYW5kIGJvdHRvbSBnZW5lcwp0b3BfZ2VuZXMgPC0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzW01hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRwX3ZhbF9hZGogPCAwLjA1ICYgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGF2Z19sb2dGQyA+IDAuNSwgXQpib3R0b21fZ2VuZXMgPC0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzW01hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRwX3ZhbF9hZGogPCAwLjA1ICYgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGF2Z19sb2dGQyA8IC0wLjUsIF0KCiMgQ3JlYXRlIGEgbmV3IGNvbHVtbiBmb3IgY29sb3IgYmFzZWQgb24gc2lnbmlmaWNhbmNlCk1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRjb2xvciA8LSBpZmVsc2UoTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGF2Z19sb2dGQyA+IDAuNSwgIlVwcmVndWxhdGVkIGdlbmVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRhdmdfbG9nRkMgPCAtMC41LCAiRG93bnJlZ3VsYXRlZCBnZW5lcyIsICJOb25zaWduaWZpY2FudCIpKQoKIyBDcmVhdGUgYSB2b2xjYW5vIHBsb3QKZ2dwbG90KE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscywgYWVzKHggPSBhdmdfbG9nRkMsIHkgPSAtbG9nMTAocF92YWxfYWRqKSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNvbG9yKSwgYWxwaGEgPSAwLjcsIHNpemUgPSAyKSArCiAgCiAgIyBBZGQgbGFiZWxzIGZvciB0b3AgYW5kIGJvdHRvbSBnZW5lcwogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gdG9wX2dlbmVzLCBhZXMobGFiZWwgPSBnZW5lKSwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBib3R0b21fZ2VuZXMsIGFlcyhsYWJlbCA9IGdlbmUpLCBjb2xvciA9ICJibGFjayIsIHZqdXN0ID0gLTEsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgCiAgIyBDdXN0b21pemUgbGFiZWxzIGFuZCB0aXRsZQogIGxhYnModGl0bGUgPSAiVm9sY2FubyBQbG90IiwKICAgICAgIHggPSAibG9nMiBGb2xkIENoYW5nZSIsCiAgICAgICB5ID0gIi1sb2cxMChwLXZhbHVlKSIpICsKICAKICAjICMgQWRkIHNpZ25pZmljYW5jZSB0aHJlc2hvbGQgbGluZXMKICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDAwMDEpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsKICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygtMC41LCAwLjUpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsKICAKICAjIFNldCBjb2xvcnMgZm9yIHRvcCBhbmQgYm90dG9tIGdlbmVzCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlVwcmVndWxhdGVkIGdlbmVzIiA9ICJyZWQiLCAiRG93bnJlZ3VsYXRlZCBnZW5lcyIgPSAiYmx1ZSIsICJOb25zaWduaWZpY2FudCIgPSAiZGFya2dyZXkiKSkgKwogIAogICMgQ3VzdG9taXplIHRoZW1lIGlmIG5lZWRlZAogIHRoZW1lX21pbmltYWwoKQoKCgoKCmBgYAoKIyMgQ3JlYXRlIHRoZSBFbmhhbmNlZFZvbGNhbm8gcGxvdApgYGB7ciAsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEyfQoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKbGlicmFyeShkcGx5cikKCiMgRGVmaW5lIHRoZSBvdXRwdXQgZGlyZWN0b3J5Cm91dHB1dF9kaXIgPC0gIk1hbGlnbmFudF92c19Db250cm9sIgpkaXIuY3JlYXRlKG91dHB1dF9kaXIsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQoKIyBGaXJzdCBWb2xjYW5vIFBsb3QKcDEgPC0gRW5oYW5jZWRWb2xjYW5vKAogIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscywKICBsYWIgPSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMkZ2VuZSwKICB4ID0gImF2Z19sb2dGQyIsCiAgeSA9ICJwX3ZhbF9hZGoiLAogIHRpdGxlID0gIk1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyIsCiAgcEN1dG9mZiA9IDFlLTQsCiAgRkNjdXRvZmYgPSAxLjAKKQpwcmludChwMSkgICMgRGlzcGxheSBpbiBub3RlYm9vawpnZ3NhdmUoZmlsZW5hbWUgPSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlZvbGNhbm9QbG90MS5wbmciKSwgcGxvdCA9IHAxLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwKQoKIyBTZWNvbmQgVm9sY2FubyBQbG90IHdpdGggc2VsZWN0ZWQgZ2VuZXMKcDIgPC0gRW5oYW5jZWRWb2xjYW5vKAogIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscywgCiAgbGFiID0gTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGdlbmUsCiAgeCA9ICJhdmdfbG9nRkMiLCAKICB5ID0gInBfdmFsX2FkaiIsCiAgc2VsZWN0TGFiID0gYygnRVBDQU0nLCAnQkNBVDEnLCAnS0lSM0RMMicsICdGT1hNMScsICdUV0lTVDEnLCAnVE5GU0Y5JywgCiAgICAgICAgICAgICAgICAnQ0Q4MCcsICAnSUwxQicsICdSUFM0WTEnLCAKICAgICAgICAgICAgICAgICdJTDdSJywgJ1RDRjcnLCAgJ01LSTY3JywgJ0NENzAnLCAKICAgICAgICAgICAgICAgICdJTDJSQScsJ1RSQlY2LTInLCAnVFJCVjEwLTMnLCAnVFJCVjQtMicsICdUUkJWOScsICdUUkJWNy05JywgCiAgICAgICAgICAgICAgICAnVFJBVjEyLTEnLCAnQ0Q4QicsICdGQ0dSM0EnLCAnR05MWScsICdGT1hQMycsICdTRUxMJywgCiAgICAgICAgICAgICAgICAnR0lNQVAxJywgJ1JJUE9SMicsICdMRUYxJywgJ0hPWEM5JywgJ1NQNScsCiAgICAgICAgICAgICAgICAnQ0NMMTcnLCAnRVRWNCcsICdUSFkxJywgJ0ZPWEEyJywgJ0lUR0FEJywgJ1MxMDBQJywgJ1RCWDQnLCAKICAgICAgICAgICAgICAgICdJRDEnLCAnWENMMScsICdTT1gyJywgJ0NEMjcnLCAnQ0QyOCcsJ1BMUzMnLCdDRDcwJywnUkFCMjUnICwgJ1RSQlYyNycsICdUUkJWMicpLAogIHRpdGxlID0gIk1hbGlnbmFudCBDRDQgVCBjZWxscyhjZWxsIGxpbmVzKSB2cyBub3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHhsYWIgPSBicXVvdGUofkxvZ1syXX4gJ2ZvbGQgY2hhbmdlJyksCiAgcEN1dG9mZiA9IDAuMDUsCiAgRkNjdXRvZmYgPSAxLjUsIAogIHBvaW50U2l6ZSA9IDMuMCwKICBsYWJTaXplID0gNS4wLAogIGJveGVkTGFiZWxzID0gVFJVRSwKICBjb2xBbHBoYSA9IDAuNSwKICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCiAgbGVnZW5kTGFiU2l6ZSA9IDEwLAogIGxlZ2VuZEljb25TaXplID0gNC4wLAogIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICB3aWR0aENvbm5lY3RvcnMgPSAwLjUsCiAgY29sQ29ubmVjdG9ycyA9ICdncmV5NTAnLAogIGFycm93aGVhZHMgPSBGQUxTRSwKICBtYXgub3ZlcmxhcHMgPSAzMAopCnByaW50KHAyKSAgIyBEaXNwbGF5IGluIG5vdGVib29rCmdnc2F2ZShmaWxlbmFtZSA9IGZpbGUucGF0aChvdXRwdXRfZGlyLCAiVm9sY2Fub1Bsb3QyLnBuZyIpLCBwbG90ID0gcDIsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCgojIEZpbHRlcmluZyBnZW5lcwpmaWx0ZXJlZF9nZW5lcyA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgJT4lCiAgYXJyYW5nZShwX3ZhbF9hZGosIGRlc2MoYWJzKGF2Z19sb2dGQykpKQoKIyBUaGlyZCBWb2xjYW5vIFBsb3QgLSBGaWx0ZXJpbmcgYnkgcC12YWx1ZSBhbmQgbG9nRkMKcDMgPC0gRW5oYW5jZWRWb2xjYW5vKAogIGZpbHRlcmVkX2dlbmVzLCAKICBsYWIgPSBpZmVsc2UoZmlsdGVyZWRfZ2VuZXMkcF92YWxfYWRqIDw9IDFlLTQgJiBhYnMoZmlsdGVyZWRfZ2VuZXMkYXZnX2xvZ0ZDKSA+PSAxLjAsIGZpbHRlcmVkX2dlbmVzJGdlbmUsIE5BKSwKICB4ID0gImF2Z19sb2dGQyIsIAogIHkgPSAicF92YWxfYWRqIiwKICB0aXRsZSA9ICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMoY2VsbCBsaW5lcykgdnMgbm9ybWFsIENENCBUIGNlbGxzIiwKICBwQ3V0b2ZmID0gMWUtNCwKICBGQ2N1dG9mZiA9IDEuMCwKICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsIAogIGxhYkNvbCA9ICdibGFjaycsCiAgbGFiRmFjZSA9ICdib2xkJywKICBib3hlZExhYmVscyA9IEZBTFNFLCAgIyBSZW1vdmUgYm94ZWQgbGFiZWxzCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA1LjAsCiAgY29sID0gYygnZ3JleTcwJywgJ2JsYWNrJywgJ2JsdWUnLCAncmVkJyksICAjIEN1c3RvbWl6ZSBwb2ludCBjb2xvcnMKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2dGQykgPj0gMS4wXQopCnByaW50KHAzKSAgIyBEaXNwbGF5IGluIG5vdGVib29rCmdnc2F2ZShmaWxlbmFtZSA9IGZpbGUucGF0aChvdXRwdXRfZGlyLCAiVm9sY2Fub1Bsb3QzLnBuZyIpLCBwbG90ID0gcDMsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCgojIEZvdXJ0aCBWb2xjYW5vIFBsb3QgLSBNb3JlIHJlZmluZWQgZmlsdGVyaW5nCnA0IDwtIEVuaGFuY2VkVm9sY2FubygKICBmaWx0ZXJlZF9nZW5lcywgCiAgbGFiID0gaWZlbHNlKGZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAxZS00ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2dGQykgPj0gMS4wLCBmaWx0ZXJlZF9nZW5lcyRnZW5lLCBOQSksCiAgeCA9ICJhdmdfbG9nRkMiLCAKICB5ID0gInBfdmFsX2FkaiIsCiAgdGl0bGUgPSAiTWFsaWduYW50IENENCBUIGNlbGxzIChjZWxsIGxpbmVzKSB2cyBOb3JtYWwgQ0Q0IFQgY2VsbHMiLAogIHN1YnRpdGxlID0gIkhpZ2hsaWdodGluZyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMiLAogIHBDdXRvZmYgPSAxZS00LAogIEZDY3V0b2ZmID0gMS4wLAogIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywKICBjb2xBbHBoYSA9IDAuOCwgICMgU2xpZ2h0IHRyYW5zcGFyZW5jeSBmb3Igbm9uLXNpZ25pZmljYW50IHBvaW50cwogIGNvbCA9IGMoJ2dyZXk3MCcsICdibGFjaycsICdibHVlJywgJ3JlZCcpLCAgIyBDdXN0b20gY29sb3Igc2NoZW1lCiAgZ3JpZGxpbmVzLm1ham9yID0gVFJVRSwKICBncmlkbGluZXMubWlub3IgPSBGQUxTRSwKICBzZWxlY3RMYWIgPSBmaWx0ZXJlZF9nZW5lcyRnZW5lW2ZpbHRlcmVkX2dlbmVzJHBfdmFsX2FkaiA8PSAwLjA1ICYgYWJzKGZpbHRlcmVkX2dlbmVzJGF2Z19sb2dGQykgPj0gMS4wXQopCnByaW50KHA0KSAgIyBEaXNwbGF5IGluIG5vdGVib29rCmdnc2F2ZShmaWxlbmFtZSA9IGZpbGUucGF0aChvdXRwdXRfZGlyLCAiVm9sY2Fub1Bsb3Q0LnBuZyIpLCBwbG90ID0gcDQsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCgptZXNzYWdlKCJBbGwgdm9sY2FubyBwbG90cyBoYXZlIGJlZW4gZGlzcGxheWVkIGFuZCBzYXZlZCBzdWNjZXNzZnVsbHkgaW4gdGhlICdMMV92c19Db250cm9sJyBmb2xkZXIuIikKCgoKYGBgCgoKIyAxMC4gRW5yaWNobWVudCBBbmFseXNpcy0xCmBgYHtyICwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQpsaWJyYXJ5KGVucmljaHBsb3QpCmxpYnJhcnkoUmVhY3RvbWVQQSkKbGlicmFyeShET1NFKSAjIEZvciBHU0VBIGFuYWx5c2lzCmxpYnJhcnkoZ2dwbG90MikgIyBFbnN1cmUgZ2dwbG90MiBpcyBhdmFpbGFibGUgZm9yIHBsb3R0aW5nCgojIERlZmluZSB0aHJlc2hvbGQgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHNlbGVjdGlvbiAobW9kaWZpZWQgdGhyZXNob2xkcykKbG9nRkNfdXBfdGhyZXNob2xkIDwtIDEgICAgICAgICAgIyBVcHJlZ3VsYXRlZCBsb2dGQyB0aHJlc2hvbGQKbG9nRkNfZG93bl90aHJlc2hvbGQgPC0gLTEgICAgICAgIyBEb3ducmVndWxhdGVkIGxvZ0ZDIHRocmVzaG9sZApwdmFsX3RocmVzaG9sZCA8LSAwLjA1ICAjIHAtdmFsdWUgdGhyZXNob2xkIGFzIHNwZWNpZmllZAoKIyBMb2FkIHlvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0cyAobW9kaWZ5IGJhc2VkIG9uIGFjdHVhbCBkYXRhIHN0cnVjdHVyZSkKIyBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHMgPC0gcmVhZC5jc3YoIllvdXJfREVfUmVzdWx0c19GaWxlLmNzdiIpCgojIFNlbGVjdCB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcwp1cHJlZ3VsYXRlZF9nZW5lcyA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHNbCiAgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGF2Z19sb2dGQyA+IGxvZ0ZDX3VwX3RocmVzaG9sZCAmIAogIE1hbGlnbmFudF9DRDRUY2VsbHNfdnNfTm9ybWFsX0NENFRjZWxscyRwX3ZhbF9hZGogPCBwdmFsX3RocmVzaG9sZCwgXQoKZG93bnJlZ3VsYXRlZF9nZW5lcyA8LSBNYWxpZ25hbnRfQ0Q0VGNlbGxzX3ZzX05vcm1hbF9DRDRUY2VsbHNbCiAgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJGF2Z19sb2dGQyA8IGxvZ0ZDX2Rvd25fdGhyZXNob2xkICYgCiAgTWFsaWduYW50X0NENFRjZWxsc192c19Ob3JtYWxfQ0Q0VGNlbGxzJHBfdmFsX2FkaiA8IHB2YWxfdGhyZXNob2xkLCBdCgojIENoZWNrIGZvciBtaXNzaW5nIGdlbmVzIChOQXMpIGluIHRoZSBnZW5lIGNvbHVtbiBhbmQgcmVtb3ZlIHRoZW0KdXByZWd1bGF0ZWRfZ2VuZXMgPC0gbmEub21pdCh1cHJlZ3VsYXRlZF9nZW5lcykKZG93bnJlZ3VsYXRlZF9nZW5lcyA8LSBuYS5vbWl0KGRvd25yZWd1bGF0ZWRfZ2VuZXMpCgojIFNhdmUgdXByZWd1bGF0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZSByZXN1bHRzIHRvIENTVgp3cml0ZS5jc3YodXByZWd1bGF0ZWRfZ2VuZXMsICJNYWxpZ25hbnRfdnNfQ29udHJvbC91cHJlZ3VsYXRlZF9nZW5lcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KGRvd25yZWd1bGF0ZWRfZ2VuZXMsICJNYWxpZ25hbnRfdnNfQ29udHJvbC9kb3ducmVndWxhdGVkX2dlbmVzLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyBDb252ZXJ0IGdlbmUgc3ltYm9scyB0byBFbnRyZXogSURzIGZvciBlbnJpY2htZW50IGFuYWx5c2lzLCB3aXRoIGNoZWNrcyBmb3IgbWlzc2luZyB2YWx1ZXMKdXByZWd1bGF0ZWRfZW50cmV6IDwtIGJpdHIodXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgZnJvbVR5cGUgPSAiU1lNQk9MIiwgdG9UeXBlID0gIkVOVFJFWklEIiwgT3JnRGIgPSBvcmcuSHMuZWcuZGIpCmRvd25yZWd1bGF0ZWRfZW50cmV6IDwtIGJpdHIoZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IG9yZy5Icy5lZy5kYikKCiMgQ2hlY2sgZm9yIG1pc3NpbmcgRW50cmV6IElEcwptaXNzaW5nX3VwcmVndWxhdGVkIDwtIHVwcmVndWxhdGVkX2dlbmVzJGdlbmVbaXMubmEodXByZWd1bGF0ZWRfZW50cmV6JEVOVFJFWklEKV0KbWlzc2luZ19kb3ducmVndWxhdGVkIDwtIGRvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZVtpcy5uYShkb3ducmVndWxhdGVkX2VudHJleiRFTlRSRVpJRCldCgojIFByaW50IG91dCB0aGUgbWlzc2luZyBnZW5lIHN5bWJvbHMgZm9yIGRlYnVnZ2luZwpjYXQoIk1pc3NpbmcgdXByZWd1bGF0ZWQgZ2VuZXM6XG4iLCBtaXNzaW5nX3VwcmVndWxhdGVkLCAiXG4iKQpjYXQoIk1pc3NpbmcgZG93bnJlZ3VsYXRlZCBnZW5lczpcbiIsIG1pc3NpbmdfZG93bnJlZ3VsYXRlZCwgIlxuIikKCiMgUmVtb3ZlIGdlbmVzIHRoYXQgY291bGRuJ3QgYmUgbWFwcGVkIHRvIEVudHJleiBJRHMKdXByZWd1bGF0ZWRfZW50cmV6IDwtIHVwcmVndWxhdGVkX2VudHJleiRFTlRSRVpJRFshaXMubmEodXByZWd1bGF0ZWRfZW50cmV6JEVOVFJFWklEKV0KZG93bnJlZ3VsYXRlZF9lbnRyZXogPC0gZG93bnJlZ3VsYXRlZF9lbnRyZXokRU5UUkVaSURbIWlzLm5hKGRvd25yZWd1bGF0ZWRfZW50cmV6JEVOVFJFWklEKV0KCiMgRGVmaW5lIGEgZnVuY3Rpb24gdG8gc2FmZWx5IHJ1biBlbnJpY2htZW50LCBwbG90IHJlc3VsdHMsIGFuZCBzYXZlIHRoZW0Kc2FmZV9lbnJpY2hHTyA8LSBmdW5jdGlvbihnZW5lX2xpc3QsIHRpdGxlLCBmaWxlbmFtZSkgewogIGlmIChsZW5ndGgoZ2VuZV9saXN0KSA+IDApIHsKICAgIHJlc3VsdCA8LSBlbnJpY2hHTyhnZW5lID0gZ2VuZV9saXN0LCBPcmdEYiA9IG9yZy5Icy5lZy5kYiwga2V5VHlwZSA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICAgICAgIG9udCA9ICJCUCIsIHBBZGp1c3RNZXRob2QgPSAiQkgiLCBwdmFsdWVDdXRvZmYgPSAwLjA1KQogICAgaWYgKCFpcy5udWxsKHJlc3VsdCkgJiYgbnJvdyhhcy5kYXRhLmZyYW1lKHJlc3VsdCkpID4gMCkgewogICAgICBwIDwtIGRvdHBsb3QocmVzdWx0LCBzaG93Q2F0ZWdvcnkgPSAxMCwgdGl0bGUgPSB0aXRsZSkKICAgICAgcHJpbnQocCkgIAogICAgICBnZ3NhdmUocGFzdGUwKCJNYWxpZ25hbnRfdnNfQ29udHJvbC8iLCBnc3ViKCIuY3N2IiwgIl9kb3RwbG90LnBuZyIsIGZpbGVuYW1lKSksIHBsb3QgPSBwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCiAgICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKHJlc3VsdCksIGZpbGUgPSBwYXN0ZTAoIk1hbGlnbmFudF92c19Db250cm9sLyIsIGZpbGVuYW1lKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgICB9IGVsc2UgewogICAgICBtZXNzYWdlKHBhc3RlKCJObyBzaWduaWZpY2FudCBlbnJpY2htZW50IGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgICB9CiAgfSBlbHNlIHsKICAgIG1lc3NhZ2UocGFzdGUoIk5vIGdlbmVzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgfQp9CgpzYWZlX2VucmljaEtFR0cgPC0gZnVuY3Rpb24oZW50cmV6X2xpc3QsIHRpdGxlLCBmaWxlbmFtZSkgewogIGlmIChsZW5ndGgoZW50cmV6X2xpc3QpID4gMCkgewogICAgcmVzdWx0IDwtIGVucmljaEtFR0coZ2VuZSA9IGVudHJlel9saXN0LCBvcmdhbmlzbSA9ICJoc2EiLCBwdmFsdWVDdXRvZmYgPSAwLjA1KQogICAgaWYgKCFpcy5udWxsKHJlc3VsdCkgJiYgbnJvdyhhcy5kYXRhLmZyYW1lKHJlc3VsdCkpID4gMCkgewogICAgICBwIDwtIGRvdHBsb3QocmVzdWx0LCBzaG93Q2F0ZWdvcnkgPSAxMCwgdGl0bGUgPSB0aXRsZSkKICAgICAgcHJpbnQocCkKICAgICAgZ2dzYXZlKHBhc3RlMCgiTWFsaWduYW50X3ZzX0NvbnRyb2wvIiwgZ3N1YigiLmNzdiIsICJfZG90cGxvdC5wbmciLCBmaWxlbmFtZSkpLCBwbG90ID0gcCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQogICAgICB3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShyZXN1bHQpLCBmaWxlID0gcGFzdGUwKCJNYWxpZ25hbnRfdnNfQ29udHJvbC8iLCBmaWxlbmFtZSksIHJvdy5uYW1lcyA9IEZBTFNFKQogICAgfSBlbHNlIHsKICAgICAgbWVzc2FnZShwYXN0ZSgiTm8gc2lnbmlmaWNhbnQgS0VHRyBwYXRod2F5cyBmb3VuZCBmb3I6IiwgdGl0bGUpKQogICAgfQogIH0gZWxzZSB7CiAgICBtZXNzYWdlKHBhc3RlKCJObyBnZW5lcyBmb3VuZCBmb3I6IiwgdGl0bGUpKQogIH0KfQoKc2FmZV9lbnJpY2hSZWFjdG9tZSA8LSBmdW5jdGlvbihlbnRyZXpfbGlzdCwgdGl0bGUsIGZpbGVuYW1lKSB7CiAgaWYgKGxlbmd0aChlbnRyZXpfbGlzdCkgPiAwKSB7CiAgICByZXN1bHQgPC0gZW5yaWNoUGF0aHdheShnZW5lID0gZW50cmV6X2xpc3QsIG9yZ2FuaXNtID0gImh1bWFuIiwgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKICAgIGlmICghaXMubnVsbChyZXN1bHQpICYmIG5yb3coYXMuZGF0YS5mcmFtZShyZXN1bHQpKSA+IDApIHsKICAgICAgcCA8LSBkb3RwbG90KHJlc3VsdCwgc2hvd0NhdGVnb3J5ID0gMTAsIHRpdGxlID0gdGl0bGUpCiAgICAgIHByaW50KHApCiAgICAgIGdnc2F2ZShwYXN0ZTAoIk1hbGlnbmFudF92c19Db250cm9sLyIsIGdzdWIoIi5jc3YiLCAiX2RvdHBsb3QucG5nIiwgZmlsZW5hbWUpKSwgcGxvdCA9IHAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKICAgICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUocmVzdWx0KSwgZmlsZSA9IHBhc3RlMCgiTWFsaWduYW50X3ZzX0NvbnRyb2wvIiwgZmlsZW5hbWUpLCByb3cubmFtZXMgPSBGQUxTRSkKICAgIH0gZWxzZSB7CiAgICAgIG1lc3NhZ2UocGFzdGUoIk5vIHNpZ25pZmljYW50IFJlYWN0b21lIHBhdGh3YXlzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgICB9CiAgfSBlbHNlIHsKICAgIG1lc3NhZ2UocGFzdGUoIk5vIGdlbmVzIGZvdW5kIGZvcjoiLCB0aXRsZSkpCiAgfQp9CgojIFBlcmZvcm0gZW5yaWNobWVudCBhbmFseXNlcywgZ2VuZXJhdGUgcGxvdHMsIGFuZCBzYXZlIHJlc3VsdHMKc2FmZV9lbnJpY2hHTyh1cHJlZ3VsYXRlZF9nZW5lcyRnZW5lLCAiR08gRW5yaWNobWVudCBmb3IgVXByZWd1bGF0ZWQgR2VuZXMiLCAidXByZWd1bGF0ZWRfR09fcmVzdWx0cy5jc3YiKQpzYWZlX2VucmljaEdPKGRvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgIkdPIEVucmljaG1lbnQgZm9yIERvd25yZWd1bGF0ZWQgR2VuZXMiLCAiZG93bnJlZ3VsYXRlZF9HT19yZXN1bHRzLmNzdiIpCgpzYWZlX2VucmljaEtFR0codXByZWd1bGF0ZWRfZW50cmV6LCAiS0VHRyBQYXRod2F5IEVucmljaG1lbnQgZm9yIFVwcmVndWxhdGVkIEdlbmVzIiwgInVwcmVndWxhdGVkX0tFR0dfcmVzdWx0cy5jc3YiKQpzYWZlX2VucmljaEtFR0coZG93bnJlZ3VsYXRlZF9lbnRyZXosICJLRUdHIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgRG93bnJlZ3VsYXRlZCBHZW5lcyIsICJkb3ducmVndWxhdGVkX0tFR0dfcmVzdWx0cy5jc3YiKQoKc2FmZV9lbnJpY2hSZWFjdG9tZSh1cHJlZ3VsYXRlZF9lbnRyZXosICJSZWFjdG9tZSBQYXRod2F5IEVucmljaG1lbnQgZm9yIFVwcmVndWxhdGVkIEdlbmVzIiwgInVwcmVndWxhdGVkX1JlYWN0b21lX3Jlc3VsdHMuY3N2IikKc2FmZV9lbnJpY2hSZWFjdG9tZShkb3ducmVndWxhdGVkX2VudHJleiwgIlJlYWN0b21lIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgRG93bnJlZ3VsYXRlZCBHZW5lcyIsICJkb3ducmVndWxhdGVkX1JlYWN0b21lX3Jlc3VsdHMuY3N2IikKCgpgYGAKCgoKCiMjIEVucmljaG1lbnQgQW5hbHlzaXMtMi1IYWxsbWFyawpgYGB7ciAsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CgojIExvYWQgbmVjZXNzYXJ5IGxpYnJhcmllcwpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShvcmcuSHMuZWcuZGIpCmxpYnJhcnkobXNpZ2RicikKbGlicmFyeShlbnJpY2hwbG90KQoKIyBMb2FkIEhhbGxtYXJrIGdlbmUgc2V0cyBmcm9tIG1zaWdkYnIKaGFsbG1hcmtfc2V0cyA8LSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiSCIpICAjICJIIiBpcyBmb3IgSGFsbG1hcmsgZ2VuZSBzZXRzCgojIENvbnZlcnQgZ2VuZSBzeW1ib2xzIHRvIHVwcGVyY2FzZSBmb3IgY29uc2lzdGVuY3kKdXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSA8LSB0b3VwcGVyKHVwcmVndWxhdGVkX2dlbmVzJGdlbmUpCmRvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSA8LSB0b3VwcGVyKGRvd25yZWd1bGF0ZWRfZ2VuZXMkZ2VuZSkKCiMgQ2hlY2sgZm9yIG92ZXJsYXAgYmV0d2VlbiB5b3VyIHVwcmVndWxhdGVkL2Rvd25yZWd1bGF0ZWQgZ2VuZXMgYW5kIEhhbGxtYXJrIGdlbmUgc2V0cwp1cHJlZ3VsYXRlZF9pbl9oYWxsbWFyayA8LSBpbnRlcnNlY3QodXByZWd1bGF0ZWRfZ2VuZXMkZ2VuZSwgaGFsbG1hcmtfc2V0cyRnZW5lX3N5bWJvbCkKZG93bnJlZ3VsYXRlZF9pbl9oYWxsbWFyayA8LSBpbnRlcnNlY3QoZG93bnJlZ3VsYXRlZF9nZW5lcyRnZW5lLCBoYWxsbWFya19zZXRzJGdlbmVfc3ltYm9sKQoKIyBQcmludCB0aGUgbnVtYmVyIG9mIG92ZXJsYXBwaW5nIGdlbmVzIGZvciBib3RoIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIGdlbmVzCmNhdCgiTnVtYmVyIG9mIHVwcmVndWxhdGVkIGdlbmVzIGluIEhhbGxtYXJrIGdlbmUgc2V0czoiLCBsZW5ndGgodXByZWd1bGF0ZWRfaW5faGFsbG1hcmspLCAiXG4iKQpjYXQoIk51bWJlciBvZiBkb3ducmVndWxhdGVkIGdlbmVzIGluIEhhbGxtYXJrIGdlbmUgc2V0czoiLCBsZW5ndGgoZG93bnJlZ3VsYXRlZF9pbl9oYWxsbWFyayksICJcbiIpCgojIERlZmluZSB0aGUgb3V0cHV0IGZvbGRlciB3aGVyZSB0aGUgcmVzdWx0cyB3aWxsIGJlIHNhdmVkCm91dHB1dF9mb2xkZXIgPC0gIk1hbGlnbmFudF92c19Db250cm9sLyIKCiMgSWYgdGhlcmUgYXJlIGdlbmVzIHRvIGFuYWx5emUsIHByb2NlZWQgd2l0aCBlbnJpY2htZW50IGFuYWx5c2lzCmlmIChsZW5ndGgodXByZWd1bGF0ZWRfaW5faGFsbG1hcmspID4gMCkgewogICMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzIGZvciB1cHJlZ3VsYXRlZCBnZW5lcyB1c2luZyBIYWxsbWFyayBnZW5lIHNldHMKICBoYWxsbWFya191cCA8LSBlbnJpY2hlcihnZW5lID0gdXByZWd1bGF0ZWRfaW5faGFsbG1hcmssIAogICAgICAgICAgICAgICAgICAgICAgICAgIFRFUk0yR0VORSA9IGhhbGxtYXJrX3NldHNbLCBjKCJnc19uYW1lIiwgImdlbmVfc3ltYm9sIildLCAgIyBFbnN1cmUgVEVSTTJHRU5FIHVzZXMgY29ycmVjdCBjb2x1bW5zCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSkKICAjIENoZWNrIGlmIHJlc3VsdHMgZXhpc3QKICBpZiAoIWlzLm51bGwoaGFsbG1hcmtfdXApICYmIG5yb3coaGFsbG1hcmtfdXApID4gMCkgewogICAgIyBWaXN1YWxpemUgcmVzdWx0cyBpZiBhdmFpbGFibGUKICAgIHVwX2RvdHBsb3QgPC0gZG90cGxvdChoYWxsbWFya191cCwgc2hvd0NhdGVnb3J5ID0gMjAsIHRpdGxlID0gIkhhbGxtYXJrIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgVXByZWd1bGF0ZWQgR2VuZXMiKQogICAgCiAgICAjIERpc3BsYXkgdGhlIHBsb3QgaW4gdGhlIG5vdGVib29rCiAgICBwcmludCh1cF9kb3RwbG90KQogICAgCiAgICAjIFNhdmUgdGhlIGRvdHBsb3QgdG8gYSBQTkcgZmlsZQogICAgZ2dzYXZlKHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfdXByZWd1bGF0ZWRfZG90cGxvdC5wbmciKSwgcGxvdCA9IHVwX2RvdHBsb3QsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCiAgICAKICAgICMgT3B0aW9uYWxseSwgc2F2ZSB0aGUgcmVzdWx0cyBhcyBDU1YKICAgIHdyaXRlLmNzdihhcy5kYXRhLmZyYW1lKGhhbGxtYXJrX3VwKSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZm9sZGVyLCAiaGFsbG1hcmtfdXByZWd1bGF0ZWRfZW5yaWNobWVudC5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgfSBlbHNlIHsKICAgIGNhdCgiTm8gc2lnbmlmaWNhbnQgZW5yaWNobWVudCBmb3VuZCBmb3IgdXByZWd1bGF0ZWQgZ2VuZXMuXG4iKQogIH0KfSBlbHNlIHsKICBjYXQoIk5vIHVwcmVndWxhdGVkIGdlbmVzIG92ZXJsYXAgd2l0aCBIYWxsbWFyayBnZW5lIHNldHMuXG4iKQp9CgppZiAobGVuZ3RoKGRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmspID4gMCkgewogICMgUGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzIGZvciBkb3ducmVndWxhdGVkIGdlbmVzIHVzaW5nIEhhbGxtYXJrIGdlbmUgc2V0cwogIGhhbGxtYXJrX2Rvd24gPC0gZW5yaWNoZXIoZ2VuZSA9IGRvd25yZWd1bGF0ZWRfaW5faGFsbG1hcmssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVEVSTTJHRU5FID0gaGFsbG1hcmtfc2V0c1ssIGMoImdzX25hbWUiLCAiZ2VuZV9zeW1ib2wiKV0sICAjIEVuc3VyZSBURVJNMkdFTkUgdXNlcyBjb3JyZWN0IGNvbHVtbnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiAgIyBDaGVjayBpZiByZXN1bHRzIGV4aXN0CiAgaWYgKCFpcy5udWxsKGhhbGxtYXJrX2Rvd24pICYmIG5yb3coaGFsbG1hcmtfZG93bikgPiAwKSB7CiAgICAjIFZpc3VhbGl6ZSByZXN1bHRzIGlmIGF2YWlsYWJsZQogICAgZG93bl9kb3RwbG90IDwtIGRvdHBsb3QoaGFsbG1hcmtfZG93biwgc2hvd0NhdGVnb3J5ID0gMjAsIHRpdGxlID0gIkhhbGxtYXJrIFBhdGh3YXkgRW5yaWNobWVudCBmb3IgRG93bnJlZ3VsYXRlZCBHZW5lcyIpCiAgICAKICAgICMgRGlzcGxheSB0aGUgcGxvdCBpbiB0aGUgbm90ZWJvb2sKICAgIHByaW50KGRvd25fZG90cGxvdCkKICAgIAogICAgIyBTYXZlIHRoZSBkb3RwbG90IHRvIGEgUE5HIGZpbGUKICAgIGdnc2F2ZShwYXN0ZTAob3V0cHV0X2ZvbGRlciwgImhhbGxtYXJrX2Rvd25yZWd1bGF0ZWRfZG90cGxvdC5wbmciKSwgcGxvdCA9IGRvd25fZG90cGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKICAgIAogICAgIyBPcHRpb25hbGx5LCBzYXZlIHRoZSByZXN1bHRzIGFzIENTVgogICAgd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUoaGFsbG1hcmtfZG93biksIGZpbGUgPSBwYXN0ZTAob3V0cHV0X2ZvbGRlciwgImhhbGxtYXJrX2Rvd25yZWd1bGF0ZWRfZW5yaWNobWVudC5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgfSBlbHNlIHsKICAgIGNhdCgiTm8gc2lnbmlmaWNhbnQgZW5yaWNobWVudCBmb3VuZCBmb3IgZG93bnJlZ3VsYXRlZCBnZW5lcy5cbiIpCiAgfQp9IGVsc2UgewogIGNhdCgiTm8gZG93bnJlZ3VsYXRlZCBnZW5lcyBvdmVybGFwIHdpdGggSGFsbG1hcmsgZ2VuZSBzZXRzLlxuIikKfQoKCmBgYAoKCgoKCgoK