load libraries
# Data Processing
library(dplyr)
library(Seurat)
library(tibble)
library(tidyr)
library(stringr)
# Visualization
library(ggplot2)
library(ComplexHeatmap)
library(patchwork)
library(SCpubr)
# Regulatory Network Inference
library(decoupleR)
library(dorothea)
data(dorothea_hs, package = "dorothea")
library(tictoc)
Load Seurat Object
# Load your Seurat Object
seurat_obj <- readRDS("../Output_Objects/Seurat_Object_With_TF_Activity.rds")
Idents(seurat_obj) <- "seurat_clusters"
print("Object Loaded.")
[1] "Object Loaded."
Run this code block
to restore activities instantly:
# If 'activities' is missing but 'dorothea' assay exists, reconstruct it:
if (!exists("activities") && "dorothea" %in% names(seurat_obj@assays)) {
print("Reconstructing 'activities' dataframe from Seurat object...")
# Extract the matrix (Seurat v5 uses 'layer' instead of 'slot')
# Since you ran ScaleData, we use 'scale.data'
mat <- GetAssayData(seurat_obj, assay = "dorothea", layer = "scale.data")
# Convert to long format (what SCpubr needs)
activities <- as.data.frame(mat) %>%
rownames_to_column("source") %>%
pivot_longer(cols = -source, names_to = "condition", values_to = "score") %>%
mutate(statistic = "norm_wmean") # SCpubr requires this column
print("Activities dataframe restored!")
}
[1] "Reconstructing 'activities' dataframe from Seurat object..."
[1] "Activities dataframe restored!"
SCpubr Heatmap
Visualization-Heatmap of averaged scores

Set the scale
limits
out <- SCpubr::do_TFActivityHeatmap(sample = seurat_obj,
activities = activities,
min.cutoff = -1.5,
max.cutoff = 1.5)
print(out)
# Save ComplexHeatmap properly
pdf("Output_Figures/SCpubr_Heatmap_Scaled.pdf", width = 10, height = 8)
print(out)
dev.off()
png
2
png("Output_Figures/SCpubr_Heatmap_Scaled.png", width = 10 * 300, height = 8 * 300, res = 300)
print(out)
dev.off()
png
2

Enforce Symmetry
(Best for Manuscript)
out <- SCpubr::do_TFActivityHeatmap(sample = seurat_obj,
activities = activities,
min.cutoff = -1.5,
max.cutoff = 1.5,
enforce_symmetry = TRUE)
print(out)
pdf("Output_Figures/SCpubr_Heatmap_Symmetric.pdf", width = 10, height = 8)
print(out)
dev.off()
png
2
png("Output_Figures/SCpubr_Heatmap_Symmetric.png", width = 10 * 300, height = 8 * 300, res = 300)
print(out)
dev.off()
png
2

print(out)

Top 40 TFs
out <- SCpubr::do_TFActivityHeatmap(sample = seurat_obj,
activities = activities,
n_tfs = 40)
print(out)
pdf("Output_Figures/SCpubr_Heatmap_Top40.pdf", width = 14, height = 6)
print(out)
dev.off()
png
2
png("Output_Figures/SCpubr_Heatmap_Top40.png", width = 14 * 300, height = 6 * 300, res = 300)
print(out)
dev.off()
png
2

Differential TF
Activity (Malignant vs. Normal)
# Define Comparison: Clusters 3 & 10 (Normal) vs Rest (Malignant)
non_malignant_clusters <- c(3, 10)
seurat_obj$Condition <- ifelse(seurat_obj$seurat_clusters %in% non_malignant_clusters, "Non-Malignant", "Malignant")
# Perform Differential Analysis on TF Activity
DefaultAssay(seurat_obj) <- "dorothea"
Idents(seurat_obj) <- "Condition"
print("Running FindMarkers on TF Activity...")
[1] "Running FindMarkers on TF Activity..."
diff_tfs <- FindMarkers(seurat_obj,
ident.1 = "Malignant",
ident.2 = "Non-Malignant",
logfc.threshold = 0, # Get all for volcano
min.pct = 0)
# Add gene column for labeling
diff_tfs$gene <- rownames(diff_tfs)
# Save Results
write.csv(diff_tfs, "Output_Tables/Differential_TF_Activity_Malignant_vs_Normal.csv")
print("Differential analysis complete.")
[1] "Differential analysis complete."
Updated Figure C:
EnhancedVolcano
library(EnhancedVolcano)
# Highlight key drivers mentioned in text
highlight_tfs <- c("FOXO1", "MYC", "E2F1", "E2F4", "FOXM1", "RELA", "IRF1", "STAT1", "TOX", "GATA3")
# Create the EnhancedVolcano Plot
p_volcano <- EnhancedVolcano(diff_tfs,
lab = rownames(diff_tfs),
x = 'avg_log2FC',
y = 'p_val_adj',
title = 'Differential TF Activity: Malignant vs. Non-Malignant',
subtitle = 'DecoupleR Inferred Activity',
pCutoff = 1e-5,
FCcutoff = 0.5,
pointSize = 3.0,
labSize = 5.0,
colAlpha = 0.8,
legendPosition = 'right',
legendLabSize = 12,
legendIconSize = 4.0,
drawConnectors = TRUE, # Draw lines to labels to avoid overlap
widthConnectors = 0.5,
colConnectors = 'grey30',
# Custom Colors: Down (Blue), Up (Red), NS (Grey)
col = c("grey30", "forestgreen", "royalblue", "firebrick2")
)
# Print
print(p_volcano)

# Save
ggsave("Output_Figures/Figure_3.16C_EnhancedVolcano_TF_Activity.pdf", plot = p_volcano, width = 10, height = 8)
ggsave("Output_Figures/Figure_3.16C_EnhancedVolcano_TF_Activity.png", plot = p_volcano, width = 10, height = 8, dpi = 300)
Figure E
(ComplexHeatmap) chunk
library(ComplexHeatmap)
library(circlize)
library(Matrix)
# Expanded list of state-specific drivers based on your regulon analysis
literature_tfs <- c(
"GATA3", "STAT6", "BATF", "FOXP3", "STAT3", "STAT5B", "TCF7", # Core/Memory
"E2F1", "MYC", "FOXM1", # Proliferation (Cl 7)
"STAT1", "STAT2", "IRF1", "IRF9", # IFN-stimulated (Cl 13)
"RELA", "NFKB1", "REL", "FOS", # Pro-inflammatory (Cl 11, 12)
"TBX21", "RUNX3", # Cytotoxic (Cl 1, 9)
"HIF1A", "SREBF1", # Metabolic shift (Cl 8)
"RFX5", "SPI1" # MHC-II High (Cl 0)
)
# Keep only TFs present in the dorothea assay
available_tfs <- intersect(literature_tfs, rownames(seurat_obj[["dorothea"]]))
if (length(available_tfs) < 5) stop("Too few TFs found in dorothea assay. Check TF naming / assay content.")
# Extract TF activity matrix (TFs x cells)
# Use scale.data if available; otherwise fall back to data layer.
mat_scaled <- tryCatch(
SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "scale.data"),
error = function(e) NULL
)
mat_data <- SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "data")
mat_use <- if (!is.null(mat_scaled) && nrow(mat_scaled) > 0) mat_scaled else mat_data
mat_use <- mat_use[available_tfs, , drop = FALSE]
# Average per cluster (TF x cluster)
clusters <- as.factor(seurat_obj$seurat_clusters)
avg_mat <- sapply(levels(clusters), function(cl) {
Matrix::rowMeans(mat_use[, clusters == cl, drop = FALSE])
})
colnames(avg_mat) <- levels(clusters)
# Optional: z-score across clusters (helps readability if you used raw 'data' instead of 'scale.data')
avg_mat_z <- t(scale(t(avg_mat)))
avg_mat_z[is.na(avg_mat_z)] <- 0
# Colors
col_fun <- circlize::colorRamp2(c(-2, 0, 2), c("#313695", "white", "#A50026"))
ht <- Heatmap(
avg_mat_z,
name = "TF activity (z)",
col = col_fun,
cluster_rows = TRUE,
cluster_columns = TRUE,
show_row_dend = TRUE,
show_column_dend = TRUE,
row_names_gp = grid::gpar(fontsize = 10),
column_names_gp = grid::gpar(fontsize = 10),
column_title = "Literature-validated Sézary TF modules (DoRothEA/decoupleR)",
heatmap_legend_param = list(direction = "vertical")
)
# Draw to notebook
draw(ht)
# Save PDF (vector)
pdf("Output_Figures/Figure_3.16E_Literature_TF_Heatmap_ComplexHeatmap.pdf", width = 10, height = 8)
draw(ht)
dev.off()
png
2
# Save PNG (raster, publication-ready)
png("Output_Figures/Figure_3.16E_Literature_TF_Heatmap_ComplexHeatmap.png",
width = 10 * 300, height = 8 * 300, res = 300)
draw(ht)
dev.off()
png
2

Figure F
(ComplexHeatmap) chunk
library(ComplexHeatmap)
library(circlize)
library(Matrix)
# Expanded list including FOXO1 and tumor suppressors
literature_tfs <- c(
# Top Malignant Upregulated (Oncogenic, Stress, Proliferation)
"RFX5", "MYC", "E2F4", "HSF1", "SREBF2", "NFE2L2",
"RELA", "REL", "NFKB1", "IRF1", "NCOA2",
# Malignant Downregulated / Normal Enriched (Tumor Suppressors & Homeostasis)
"FOXO1", "FOXO4", "RUNX3", "TCF3", "BCL11A", "NEUROD1", "MEF2B", "PBX2",
# UMAP State Drivers (Intra-tumoral heterogeneity)
"GATA3", "STAT6", "BATF", "FOXP3", "STAT3", "STAT5B", "TCF7", # Core/Memory
"E2F1", "FOXM1", # Proliferation
"STAT1", "STAT2", "IRF9", # IFN response
"HIF1A", "SREBF1", # Metabolic
"TBX21" # Cytotoxic
)
# Extract TF activity matrix
mat_scaled <- tryCatch(
SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "scale.data"),
error = function(e) NULL
)
mat_data <- SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "data")
mat_use <- if (!is.null(mat_scaled) && nrow(mat_scaled) > 0) mat_scaled else mat_data
available_tfs <- intersect(literature_tfs, rownames(mat_use))
if (length(available_tfs) < 5) stop("Too few TFs found. Check assay data.")
mat_use <- mat_use[available_tfs, , drop = FALSE]
# Average per cluster and z-score
clusters <- as.factor(seurat_obj$seurat_clusters)
avg_mat <- sapply(levels(clusters), function(cl) {
Matrix::rowMeans(mat_use[, clusters == cl, drop = FALSE])
})
colnames(avg_mat) <- levels(clusters)
avg_mat_z <- t(scale(t(avg_mat)))
avg_mat_z[is.na(avg_mat_z)] <- 0
# Annotate Malignant vs Normal (Clusters 3, 10 = Normal)
cluster_status <- ifelse(colnames(avg_mat_z) %in% c("3", "10"),
"Normal CD4 T",
"Malignant CD4 T cells")
# Define annotation
ha <- HeatmapAnnotation(
Cell_State = cluster_status,
col = list(Cell_State = c("Normal CD4 T" = "#4DAF4A", "Malignant CD4 T cells" = "#E41A1C")),
annotation_name_side = "left"
)
# Colors
col_fun <- circlize::colorRamp2(c(-3, 0, 3), c("#313695", "white", "#A50026"))
# Create heatmap with column split
ht <- Heatmap(
avg_mat_z,
name = "TF activity (z)",
col = col_fun,
top_annotation = ha,
column_split = cluster_status, # Physically splits normal and malignant columns
cluster_rows = TRUE,
cluster_columns = TRUE,
show_row_dend = TRUE,
show_column_dend = TRUE,
row_names_gp = grid::gpar(fontsize = 10),
column_names_gp = grid::gpar(fontsize = 10),
column_title = "Differential TF Modules in Sézary Heterogeneity",
heatmap_legend_param = list(direction = "vertical")
)
# Output
pdf("Output_Figures/Figure_3.16E_Differential_TF_Heatmap.pdf", width = 11, height = 9)
draw(ht)
dev.off()
null device
1
png("Output_Figures/Figure_3.16E_Differential_TF_Heatmap.png",
width = 11 * 300, height = 9 * 300, res = 300)
draw(ht)
dev.off()
null device
1
draw(ht)

Figure G
(ComplexHeatmap) chunk
# ============================================
# LIBRARIES
# ============================================
library(ComplexHeatmap)
library(circlize)
library(Matrix)
library(grid)
library(SeuratObject) # for GetAssayData
# ============================================
# 1. Define TF panel metadata (44-47 TFs)
# ============================================
tf_meta <- data.frame(
TF = c(
"MYC","E2F4","RFX5","TWIST1","JUNB","IRF4","CREB1",
"FOS","FOSL1",
"HSF1","NFE2L2","SREBF2",
"RELA","REL","NFKB1","IRF1","NCOA2",
"NFATC1","NFATC2",
"FOXO1","FOXO4","RUNX3","ZEB1","BACH2",
"TCF3","BCL11A","NEUROD1","MEF2B","PBX2","IRF3","BCL6",
"GATA3","STAT6","BATF","FOXP3","STAT3","STAT5B","TCF7",
"E2F1","FOXM1",
"STAT1","STAT2","IRF9",
"HIF1A","SREBF1",
"EOMES",
"PRDM1"
),
Condition = c(
rep("Malignant", 7), # Oncogenic
rep("Malignant", 2), # AP-1
rep("Malignant", 3), # Stress
rep("Malignant", 5), # NF-kB
rep("Malignant", 2), # NFAT
rep("Normal", 5), # Tumor Suppressor
rep("Normal", 7), # Homeostasis
rep("Malignant", 7), # Th2/Memory Core
rep("Malignant", 2), # Proliferation
rep("Malignant", 3), # IFN
rep("Malignant", 2), # Metabolism
rep("Malignant", 1), # Cytotoxic
rep("Malignant", 1) # Terminal Effector
),
Function = c(
rep("Oncogenic", 7),
rep("AP-1 signaling", 2),
rep("Stress Response", 3),
rep("Inflammatory/NF-kB", 5),
rep("NFAT signaling", 2),
rep("Tumor Suppressor", 5),
rep("Normal Homeostasis", 7),
rep("Th2/Memory Core", 7),
rep("Proliferation", 2),
rep("IFN Response", 3),
rep("Metabolism", 2),
rep("Cytotoxic", 1),
rep("Terminal Effector", 1)
),
stringsAsFactors = FALSE
)
# ============================================
# 2. Extract TF activity matrix from Seurat
# ============================================
mat_scaled <- tryCatch(
SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "scale.data"),
error = function(e) NULL
)
mat_data <- SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "data")
mat_use <- if (!is.null(mat_scaled) && nrow(mat_scaled) > 0) mat_scaled else mat_data
# Filter for TFs present in Seurat
available_tfs <- intersect(tf_meta$TF, rownames(mat_use))
mat_use <- mat_use[available_tfs, , drop = FALSE]
# ============================================
# 3. Average per cluster & z-score
# ============================================
clusters <- as.factor(seurat_obj$seurat_clusters)
avg_mat <- sapply(levels(clusters), function(cl) {
Matrix::rowMeans(mat_use[, clusters == cl, drop = FALSE])
})
colnames(avg_mat) <- levels(clusters)
avg_mat_z <- t(scale(t(avg_mat)))
avg_mat_z[is.na(avg_mat_z)] <- 0
# Align tf_meta order
tf_meta_filtered <- tf_meta[match(rownames(avg_mat_z), tf_meta$TF), ]
# ============================================
# 4. Column & row annotations
# ============================================
cluster_status <- ifelse(colnames(avg_mat_z) %in% c("3","10"), "Normal CD4 T cells", "Malignant CD4 T cells")
ha_col <- HeatmapAnnotation(
Cell_State = cluster_status,
col = list(Cell_State = c("Normal CD4 T cells" = "#4DAF4A", "Malignant CD4 T cells" = "#E41A1C")),
annotation_name_side = "left"
)
# Row annotation
function_colors <- c(
"Oncogenic" = "#FF7F00",
"AP-1 signaling" = "#FFA500",
"Stress Response" = "#FFD700",
"Inflammatory/NF-kB" = "#1E90FF",
"NFAT signaling" = "#4169E1",
"Tumor Suppressor" = "#377EB8",
"Normal Homeostasis" = "#4DAF4A",
"Th2/Memory Core" = "#984EA3",
"Proliferation" = "#E41A1C",
"IFN Response" = "#00CED1",
"Metabolism" = "#A65628",
"Cytotoxic" = "#F781BF",
"Terminal Effector" = "#800080"
)
ha_row <- rowAnnotation(
Condition = tf_meta_filtered$Condition,
Function = tf_meta_filtered$Function,
col = list(
Condition = c("Normal"="#4DAF4A","Malignant"="#E41A1C"),
Function = function_colors
),
annotation_name_side = "bottom"
)
# ============================================
# 5. Heatmap colors & plotting
# ============================================
col_fun <- circlize::colorRamp2(c(-3,0,3), c("#313695","white","#A50026"))
ht <- Heatmap(
avg_mat_z,
name = "TF activity (z)",
col = col_fun,
top_annotation = ha_col,
left_annotation = ha_row,
column_split = cluster_status,
row_split = tf_meta_filtered$Function,
cluster_rows = FALSE, # show biological split
cluster_columns = TRUE,
show_row_dend = FALSE,
show_column_dend = TRUE,
row_names_gp = gpar(fontsize = 10),
column_names_gp = gpar(fontsize = 10),
column_title = "Functional TF Modules in Sézary Syndrome",
row_title_rot = 0,
row_title_gp = gpar(fontsize = 9, fontface = "bold"),
heatmap_legend_param = list(direction = "vertical")
)
# ============================================
# 6. Save plots
# ============================================
pdf("Output_Figures/Figure_TF_Heatmap.pdf", width=12, height=10)
draw(ht, merge_legend=TRUE)
dev.off()
null device
1
png("Output_Figures/Figure_TF_Heatmap.png", width=12*300, height=10*300, res=300)
draw(ht, merge_legend=TRUE)
dev.off()
null device
1
draw(ht, merge_legend=TRUE)

Figure Malignant
complex heatmap chunk (with 44-47 TFs, literature-based panel)
# ============================================
# LIBRARIES
# ============================================
library(ComplexHeatmap)
library(circlize)
library(Matrix)
library(grid)
library(SeuratObject)
# ============================================
# 1. Define TF panel metadata (KEGG Aligned)
# ============================================
tf_meta <- data.frame(
TF = c(
# --- 1. General Malignancy & Stress ---
"MYC","E2F4","TWIST1","IRF4",
"HSF1","NFE2L2","SREBF2",
# --- 2. TCR Signaling Triad ---
"JUNB","FOS","FOSL1", # AP-1
"NFATC1","NFATC2", # NFAT
"RELA","REL","NFKB1","IRF1","NCOA2", # NF-kB
# --- 3. Th2 / JAK-STAT Core ---
"GATA3","STAT6","BATF","FOXP3","STAT3","STAT5B",
# --- 4. Differentiation Hierarchy ---
"TCF7","LEF1","MYB", # Stem-like Progenitor
"E2F1","FOXM1", # Proliferation (Cycling)
"PRDM1", # Terminal Effector
# --- 5. KEGG ALIGNED CATEGORIES (NEW) ---
"EOMES","TBX21","RUNX3", # NK-like Cytotoxicity (Cluster 1,9)
"RFX5","CREB1", # Antigen Presentation / MHC-II (Cluster 0)
"KLF4","ETS1","SMAD3", # Migration / Cell Adhesion (CAMs)
# --- 6. Microenvironment ---
"STAT1","STAT2","IRF9", # IFN Response
"HIF1A","SREBF1", # Metabolism
# --- 7. Normal Baseline / Tumor Suppressors ---
"FOXO1","FOXO4","ZEB1","BACH2",
"TCF3","BCL11A","NEUROD1","MEF2B","PBX2","IRF3","BCL6"
),
Condition = c(
rep("Malignant", 4), # Oncogenic
rep("Malignant", 3), # Stress
rep("Malignant", 3), # AP-1
rep("Malignant", 2), # NFAT
rep("Malignant", 5), # NF-kB
rep("Malignant", 6), # Th2 / JAK-STAT Core
rep("Malignant", 3), # Stem-like Progenitor
rep("Malignant", 2), # Proliferation
rep("Malignant", 1), # Terminal Effector
rep("Malignant", 3), # NK-like Cytotoxicity
rep("Malignant", 2), # Antigen Presentation
rep("Malignant", 3), # Migration / Adhesion
rep("Malignant", 3), # IFN
rep("Malignant", 2), # Metabolism
rep("Normal", 4), # Tumor Suppressor
rep("Normal", 7) # Homeostasis
),
Function = c(
rep("Oncogenic", 4),
rep("Stress Response", 3),
rep("AP-1 Signaling", 3),
rep("NFAT Signaling", 2),
rep("Inflammatory/NF-kB", 5),
rep("Th2 / JAK-STAT Core", 6),
rep("Stem-like Progenitor", 3),
rep("Proliferation", 2),
rep("Terminal Effector", 1),
rep("NK-like Cytotoxicity", 3), # KEGG aligned
rep("Antigen Presentation", 2), # KEGG aligned
rep("Migration / Adhesion", 3), # KEGG aligned
rep("IFN Response", 3),
rep("Metabolism", 2),
rep("Tumor Suppressor", 4),
rep("Normal Homeostasis", 7)
),
stringsAsFactors = FALSE
)
# Lock in the precise order of the blocks from top to bottom
desired_order <- c(
"Oncogenic",
"Stress Response",
"AP-1 Signaling",
"NFAT Signaling",
"Inflammatory/NF-kB",
"Th2 / JAK-STAT Core",
"Stem-like Progenitor",
"Proliferation",
"Terminal Effector",
"NK-like Cytotoxicity", # Placed here to show effector state
"Antigen Presentation", # Directly links to MHC-II high cluster
"Migration / Adhesion", # Links to CAMs KEGG pathway
"IFN Response",
"Metabolism",
"Tumor Suppressor",
"Normal Homeostasis"
)
tf_meta$Function <- factor(tf_meta$Function, levels = desired_order)
# ============================================
# 2. Extract TF activity matrix from Seurat
# ============================================
mat_scaled <- tryCatch(
SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "scale.data"),
error = function(e) NULL
)
mat_data <- SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "data")
mat_use <- if (!is.null(mat_scaled) && nrow(mat_scaled) > 0) mat_scaled else mat_data
# Filter for TFs present in Seurat
available_tfs <- intersect(tf_meta$TF, rownames(mat_use))
mat_use <- mat_use[available_tfs, , drop = FALSE]
# ============================================
# 3. Average per cluster & z-score
# ============================================
clusters <- as.factor(seurat_obj$seurat_clusters)
avg_mat <- sapply(levels(clusters), function(cl) {
Matrix::rowMeans(mat_use[, clusters == cl, drop = FALSE])
})
colnames(avg_mat) <- levels(clusters)
avg_mat_z <- t(scale(t(avg_mat)))
avg_mat_z[is.na(avg_mat_z)] <- 0
# Align tf_meta order to match mat_use precisely
row_order_idx <- match(rownames(avg_mat_z), tf_meta$TF)
tf_meta_filtered <- tf_meta[row_order_idx, ]
avg_mat_z <- avg_mat_z[order(tf_meta_filtered$Function), ]
tf_meta_filtered <- tf_meta_filtered[order(tf_meta_filtered$Function), ]
# ============================================
# 4. Column & row annotations
# ============================================
cluster_status <- ifelse(colnames(avg_mat_z) %in% c("3","10"), "Normal CD4 T cells", "Malignant CD4 T cells")
ha_col <- HeatmapAnnotation(
Cell_State = cluster_status,
col = list(Cell_State = c("Normal CD4 T cells" = "#4DAF4A", "Malignant CD4 T cells" = "#E41A1C")),
annotation_name_side = "left"
)
# Row annotation colors
function_colors <- c(
"Oncogenic" = "#808080",
"Stress Response" = "#FFD700",
"AP-1 Signaling" = "#FF8C00",
"NFAT Signaling" = "#FF4500",
"Inflammatory/NF-kB" = "#E31A1C",
"Th2 / JAK-STAT Core" = "#984EA3",
"Stem-like Progenitor" = "#FF1493",
"Proliferation" = "#1E90FF",
"Terminal Effector" = "#800080",
# New KEGG categories
"NK-like Cytotoxicity" = "#F781BF", # Pink
"Antigen Presentation" = "#66CDAA", # Medium Aquamarine
"Migration / Adhesion" = "#8A2BE2", # Blue Violet
"IFN Response" = "#00CED1",
"Metabolism" = "#A65628",
"Tumor Suppressor" = "#377EB8",
"Normal Homeostasis" = "#4DAF4A"
)
ha_row <- rowAnnotation(
Condition = tf_meta_filtered$Condition,
Function = tf_meta_filtered$Function,
col = list(
Condition = c("Normal"="#4DAF4A","Malignant"="#E41A1C"),
Function = function_colors
),
annotation_name_side = "bottom"
)
# ============================================
# 5. Heatmap colors & plotting
# ============================================
col_fun <- circlize::colorRamp2(c(-3,0,3), c("#313695","white","#A50026"))
ht <- Heatmap(
avg_mat_z,
name = "TF activity (z)",
col = col_fun,
top_annotation = ha_col,
left_annotation = ha_row,
column_split = cluster_status,
row_split = tf_meta_filtered$Function,
cluster_row_slices = FALSE,
cluster_rows = FALSE,
cluster_columns = TRUE,
show_row_dend = FALSE,
show_column_dend = TRUE,
row_names_gp = gpar(fontsize = 10),
column_names_gp = gpar(fontsize = 10),
column_title = "Functional TF Modules in Sézary Syndrome",
row_title_rot = 0,
row_title_gp = gpar(fontsize = 8, fontface = "bold"),
heatmap_legend_param = list(direction = "vertical")
)
# ============================================
# 6. Save plots
# ============================================
pdf("Output_Figures/Figure_TF_Heatmap_KEGG.pdf", width=14, height=14)
draw(ht, merge_legend=TRUE)
dev.off()
null device
1
draw(ht, merge_legend=TRUE)

Figure Malignant
complex heatmap chunk (with 44-47 TFs, literature-based panel)
# ============================================
# LIBRARIES
# ============================================
library(ComplexHeatmap)
library(circlize)
library(Matrix)
library(grid)
library(SeuratObject)
# ============================================
# 1. Define TF panel metadata (KEGG Aligned)
# ============================================
tf_meta <- data.frame(
TF = c(
# --- 1. General Malignancy & Stress ---
"MYC","E2F4","TWIST1","IRF4",
"HSF1","NFE2L2","SREBF2",
# --- 2. TCR Signaling Triad ---
"JUNB","FOS","FOSL1", # AP-1
"NFATC1","NFATC2", # NFAT
"RELA","REL","NFKB1","IRF1","NCOA2", # NF-kB
# --- 3. Th2 / JAK-STAT Core ---
"GATA3","STAT6","BATF","FOXP3","STAT3","STAT5B",
# --- 4. Differentiation Hierarchy ---
"TCF7","LEF1","MYB", # Stem-like Progenitor
"E2F1","FOXM1", # Proliferation (Cycling)
"PRDM1", # Terminal Effector
# --- 5. KEGG ALIGNED CATEGORIES (NEW) ---
"EOMES","TBX21","RUNX3", # NK-like Cytotoxicity (Cluster 1,9)
"RFX5","CREB1", # Antigen Presentation / MHC-II (Cluster 0)
"KLF4","ETS1","SMAD3", # Migration / Cell Adhesion (CAMs)
# --- 6. Microenvironment ---
"STAT1","STAT2","IRF9", # IFN Response
"HIF1A","SREBF1", # Metabolism
# --- 7. Normal Baseline / Tumor Suppressors ---
"FOXO1","FOXO4","ZEB1","BACH2",
"TCF3","BCL11A","NEUROD1","MEF2B","PBX2","IRF3","BCL6"
),
Condition = c(
rep("Malignant", 4), # Oncogenic
rep("Malignant", 3), # Stress
rep("Malignant", 3), # AP-1
rep("Malignant", 2), # NFAT
rep("Malignant", 5), # NF-kB
rep("Malignant", 6), # Th2 / JAK-STAT Core
rep("Malignant", 3), # Stem-like Progenitor
rep("Malignant", 2), # Proliferation
rep("Malignant", 1), # Terminal Effector
rep("Malignant", 3), # NK-like Cytotoxicity
rep("Malignant", 2), # Antigen Presentation
rep("Malignant", 3), # Migration / Adhesion
rep("Malignant", 3), # IFN
rep("Malignant", 2), # Metabolism
rep("Normal", 4), # Tumor Suppressor
rep("Normal", 7) # Homeostasis
),
Function = c(
rep("Oncogenic", 4),
rep("Stress Response", 3),
rep("AP-1 Signaling", 3),
rep("NFAT Signaling", 2),
rep("Inflammatory/NF-kB", 5),
rep("Th2 / JAK-STAT Core", 6),
rep("Stem-like Progenitor", 3),
rep("Proliferation", 2),
rep("Terminal Effector", 1),
rep("NK-like Cytotoxicity", 3), # KEGG aligned
rep("Antigen Presentation", 2), # KEGG aligned
rep("Migration / Adhesion", 3), # KEGG aligned
rep("IFN Response", 3),
rep("Metabolism", 2),
rep("Tumor Suppressor", 4),
rep("Normal Homeostasis", 7)
),
stringsAsFactors = FALSE
)
# Lock in the precise order of the blocks from top to bottom
desired_order <- c(
"Oncogenic",
"Stress Response",
"AP-1 Signaling",
"NFAT Signaling",
"Inflammatory/NF-kB",
"Th2 / JAK-STAT Core",
"Stem-like Progenitor",
"Proliferation",
"Terminal Effector",
"NK-like Cytotoxicity", # Placed here to show effector state
"Antigen Presentation", # Directly links to MHC-II high cluster
"Migration / Adhesion", # Links to CAMs KEGG pathway
"IFN Response",
"Metabolism",
"Tumor Suppressor",
"Normal Homeostasis"
)
tf_meta$Function <- factor(tf_meta$Function, levels = desired_order)
# ============================================
# 2. Extract TF activity matrix from Seurat
# ============================================
mat_scaled <- tryCatch(
SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "scale.data"),
error = function(e) NULL
)
mat_data <- SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "data")
mat_use <- if (!is.null(mat_scaled) && nrow(mat_scaled) > 0) mat_scaled else mat_data
# Filter for TFs present in Seurat
available_tfs <- intersect(tf_meta$TF, rownames(mat_use))
mat_use <- mat_use[available_tfs, , drop = FALSE]
# ============================================
# 3. Average per cluster & z-score
# ============================================
clusters <- as.factor(seurat_obj$seurat_clusters)
avg_mat <- sapply(levels(clusters), function(cl) {
Matrix::rowMeans(mat_use[, clusters == cl, drop = FALSE])
})
colnames(avg_mat) <- levels(clusters)
avg_mat_z <- t(scale(t(avg_mat)))
avg_mat_z[is.na(avg_mat_z)] <- 0
# Align tf_meta order to match mat_use precisely
row_order_idx <- match(rownames(avg_mat_z), tf_meta$TF)
tf_meta_filtered <- tf_meta[row_order_idx, ]
avg_mat_z <- avg_mat_z[order(tf_meta_filtered$Function), ]
tf_meta_filtered <- tf_meta_filtered[order(tf_meta_filtered$Function), ]
# ============================================
# 4. Column & row annotations
# ============================================
cluster_status <- ifelse(colnames(avg_mat_z) %in% c("3","10"), "Normal CD4 T cells", "Malignant CD4 T cells")
ha_col <- HeatmapAnnotation(
Cell_State = cluster_status,
col = list(Cell_State = c("Normal CD4 T cells" = "#4DAF4A", "Malignant CD4 T cells" = "#E41A1C")),
annotation_name_side = "left"
)
# Row annotation colors
function_colors <- c(
"Oncogenic" = "#808080",
"Stress Response" = "#FFD700",
"AP-1 Signaling" = "#FF8C00",
"NFAT Signaling" = "#FF4500",
"Inflammatory/NF-kB" = "#E31A1C",
"Th2 / JAK-STAT Core" = "#984EA3",
"Stem-like Progenitor" = "#FF1493",
"Proliferation" = "#1E90FF",
"Terminal Effector" = "#800080",
# New KEGG categories
"NK-like Cytotoxicity" = "#F781BF", # Pink
"Antigen Presentation" = "#66CDAA", # Medium Aquamarine
"Migration / Adhesion" = "#8A2BE2", # Blue Violet
"IFN Response" = "#00CED1",
"Metabolism" = "#A65628",
"Tumor Suppressor" = "#377EB8",
"Normal Homeostasis" = "#4DAF4A"
)
ha_row <- rowAnnotation(
Condition = tf_meta_filtered$Condition,
Function = tf_meta_filtered$Function,
col = list(
Condition = c("Normal"="#4DAF4A","Malignant"="#E41A1C"),
Function = function_colors
),
annotation_name_side = "bottom"
)
# ============================================
# 5. Heatmap colors & plotting
# ============================================
col_fun <- circlize::colorRamp2(c(-3,0,3), c("#313695","white","#A50026"))
ht <- Heatmap(
avg_mat_z,
name = "TF activity (z)",
col = col_fun,
top_annotation = ha_col,
left_annotation = ha_row,
column_split = cluster_status,
row_split = tf_meta_filtered$Function,
cluster_row_slices = FALSE,
cluster_rows = FALSE,
cluster_columns = TRUE,
show_row_dend = FALSE,
show_column_dend = TRUE,
row_names_gp = gpar(fontsize = 10),
column_names_gp = gpar(fontsize = 10),
column_title = "Functional TF Modules in Sézary Syndrome",
row_title_rot = 0,
row_title_gp = gpar(fontsize = 8, fontface = "bold"),
heatmap_legend_param = list(direction = "vertical")
)
# ============================================
# 6. Save plots
# ============================================
pdf("Output_Figures/Figure_TF_Heatmap_KEGG.pdf", width=14, height=14)
draw(ht, merge_legend=TRUE)
dev.off()
null device
1
draw(ht, merge_legend=TRUE)

Figure Malignant
complex heatmap chunk (with 44-47 TFs, literature-based panel)
# ============================================
# LIBRARIES
# ============================================
library(ComplexHeatmap)
library(circlize)
library(Matrix)
library(grid)
library(SeuratObject)
# ============================================
# 1. Define TF panel metadata (1:1 UMAP Aligned)
# ============================================
tf_meta <- data.frame(
TF = c(
# --- Baseline Malignancy ---
"MYC","E2F4","TWIST1","IRF4",
# --- Clusters 2 & 6: Th2-like Core ---
"GATA3","STAT6","BATF","FOXP3","STAT3","STAT5B",
# --- Clusters 11 & 12: Pro-inflammatory & Stress ---
"JUNB","FOS","FOSL1", # AP-1
"RELA","REL","NFKB1","IRF1","NCOA2", # NF-kB
"NFATC1","NFATC2", # TCR/NFAT
"HSF1","NFE2L2","SREBF2", # Stress
# --- Cluster 4: Inflammatory-Migratory ---
"KLF4","ETS1","SMAD3",
# --- Cluster 5: Stem-like ---
"TCF7","LEF1","MYB",
# --- Cluster 7: Cycling (G2/M) ---
"E2F1","FOXM1",
# --- Clusters 1 & 9: NK-like / Cytotoxic ---
"EOMES","TBX21","RUNX3","PRDM1",
# --- Cluster 0: MHC-II High ---
"RFX5","CREB1",
# --- Cluster 13: IFN Stimulated ---
"STAT1","STAT2","IRF9",
# --- Cluster 8: Glycolytic/Metabolic ---
"HIF1A","SREBF1",
# --- Clusters 3 & 10: Normal CD4 T ---
"FOXO1","FOXO4","ZEB1","BACH2",
"TCF3","BCL11A","NEUROD1","MEF2B","PBX2","IRF3","BCL6"
),
Condition = c(
rep("Malignant", 4), # Oncogenic
rep("Malignant", 6), # Th2
rep("Malignant", 13), # Pro-inflammatory (AP1, NFkB, NFAT, Stress)
rep("Malignant", 3), # Migratory
rep("Malignant", 3), # Stem-like
rep("Malignant", 2), # Cycling
rep("Malignant", 4), # NK/Cytotoxic
rep("Malignant", 2), # MHC-II
rep("Malignant", 3), # IFN
rep("Malignant", 2), # Glycolytic
rep("Normal", 11) # Normal
),
Function = c(
rep("Oncogenic Core", 4),
rep("Th2-like Core (Cl. 2, 6)", 6),
rep("Pro-inflammatory (Cl. 11, 12)", 13),
rep("Inflammatory-Migratory (Cl. 4)", 3),
rep("Stem-like (Cl. 5)", 3),
rep("Cycling G2/M (Cl. 7)", 2),
rep("NK-like Cytotoxic (Cl. 1, 9)", 4),
rep("MHC-II High (Cl. 0)", 2),
rep("IFN Stimulated (Cl. 13)", 3),
rep("Glycolytic/Metabolic (Cl. 8)", 2),
rep("Normal Homeostasis (Cl. 3, 10)", 11)
),
stringsAsFactors = FALSE
)
# Lock in the precise order of the blocks
desired_order <- c(
"Oncogenic Core",
"Th2-like Core (Cl. 2, 6)",
"Pro-inflammatory (Cl. 11, 12)",
"Inflammatory-Migratory (Cl. 4)",
"Stem-like (Cl. 5)",
"Cycling G2/M (Cl. 7)",
"NK-like Cytotoxic (Cl. 1, 9)",
"MHC-II High (Cl. 0)",
"IFN Stimulated (Cl. 13)",
"Glycolytic/Metabolic (Cl. 8)",
"Normal Homeostasis (Cl. 3, 10)"
)
tf_meta$Function <- factor(tf_meta$Function, levels = desired_order)
# ============================================
# 2. Extract TF activity matrix from Seurat
# ============================================
mat_scaled <- tryCatch(SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "scale.data"), error = function(e) NULL)
mat_data <- SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "data")
mat_use <- if (!is.null(mat_scaled) && nrow(mat_scaled) > 0) mat_scaled else mat_data
available_tfs <- intersect(tf_meta$TF, rownames(mat_use))
mat_use <- mat_use[available_tfs, , drop = FALSE]
# ============================================
# 3. Average per cluster & z-score
# ============================================
clusters <- as.factor(seurat_obj$seurat_clusters)
avg_mat <- sapply(levels(clusters), function(cl) Matrix::rowMeans(mat_use[, clusters == cl, drop = FALSE]))
colnames(avg_mat) <- levels(clusters)
avg_mat_z <- t(scale(t(avg_mat)))
avg_mat_z[is.na(avg_mat_z)] <- 0
# Align tf_meta order to match mat_use precisely
row_order_idx <- match(rownames(avg_mat_z), tf_meta$TF)
tf_meta_filtered <- tf_meta[row_order_idx, ]
avg_mat_z <- avg_mat_z[order(tf_meta_filtered$Function), ]
tf_meta_filtered <- tf_meta_filtered[order(tf_meta_filtered$Function), ]
# ============================================
# 4. Column & row annotations
# ============================================
cluster_status <- ifelse(colnames(avg_mat_z) %in% c("3","10"), "Normal CD4 T cells", "Malignant CD4 T cells")
ha_col <- HeatmapAnnotation(Cell_State = cluster_status, col = list(Cell_State = c("Normal CD4 T cells" = "#4DAF4A", "Malignant CD4 T cells" = "#E41A1C")), annotation_name_side = "left")
# Harmonized color palette
function_colors <- c(
"Oncogenic Core" = "#808080",
"Th2-like Core (Cl. 2, 6)" = "#984EA3",
"Pro-inflammatory (Cl. 11, 12)" = "#E31A1C",
"Inflammatory-Migratory (Cl. 4)" = "#8A2BE2",
"Stem-like (Cl. 5)" = "#FF1493",
"Cycling G2/M (Cl. 7)" = "#1E90FF",
"NK-like Cytotoxic (Cl. 1, 9)" = "#F781BF",
"MHC-II High (Cl. 0)" = "#66CDAA",
"IFN Stimulated (Cl. 13)" = "#00CED1",
"Glycolytic/Metabolic (Cl. 8)" = "#A65628",
"Normal Homeostasis (Cl. 3, 10)" = "#4DAF4A"
)
ha_row <- rowAnnotation(Condition = tf_meta_filtered$Condition, Function = tf_meta_filtered$Function, col = list(Condition = c("Normal"="#4DAF4A","Malignant"="#E41A1C"), Function = function_colors), annotation_name_side = "bottom")
# ============================================
# 5. Heatmap colors & plotting
# ============================================
col_fun <- circlize::colorRamp2(c(-3,0,3), c("#313695","white","#A50026"))
ht <- Heatmap(
avg_mat_z,
name = "TF activity (z)",
col = col_fun,
top_annotation = ha_col,
left_annotation = ha_row,
column_split = cluster_status,
row_split = tf_meta_filtered$Function,
cluster_row_slices = FALSE,
cluster_rows = FALSE,
cluster_columns = TRUE,
show_row_dend = FALSE,
show_column_dend = TRUE,
row_names_gp = gpar(fontsize = 10),
column_names_gp = gpar(fontsize = 10),
column_title = "Regulatory Drivers of Sézary UMAP Cell States",
row_title_rot = 0,
row_title_gp = gpar(fontsize = 9, fontface = "bold"),
heatmap_legend_param = list(direction = "vertical")
)
# ============================================
# 6. Save plots
# ============================================
pdf("Output_Figures/Figure_TF_Heatmap_UMAP_Final.pdf", width=15, height=13)
draw(ht, merge_legend=TRUE)
dev.off()
null device
1
draw(ht, merge_legend=TRUE)

TEST
# ============================================
# LIBRARIES
# ============================================
library(ComplexHeatmap)
library(circlize)
library(Matrix)
library(grid)
library(SeuratObject)
# ============================================
# 1. Define Literature-Validated TF panel (Exhaustive)
# ============================================
tf_meta_lit <- data.frame(
TF = c(
# Oncogenic
"MYC", "TWIST1", "IRF4",
# Th2 Core
"GATA3", "BATF", "FOXP3", "STAT3", "STAT5B",
# Hyperactive TCR / Inflammatory
"JUNB", "NFATC1", "NFATC2", "RELA", "NFKB1",
# Canonical Tumor Suppressors
"ZEB1", "BACH2", "FOXO1", "FOXO3"
),
Function = c(
rep("Oncogenic ", 3),
rep("Th2 Core", 5),
rep("Hyperactive TCR / Inflammatory", 5),
rep("Canonical Tumor Suppressors", 4)
),
stringsAsFactors = FALSE
)
# Lock in the order top-to-bottom
tf_meta_lit$Function <- factor(tf_meta_lit$Function,
levels = c("Oncogenic ",
"Th2 Core",
"Hyperactive TCR / Inflammatory",
"Canonical Tumor Suppressors"))
# ============================================
# 2. Extract TF activity matrix
# ============================================
mat_scaled <- tryCatch(SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "scale.data"), error = function(e) NULL)
mat_data <- SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "data")
mat_use <- if (!is.null(mat_scaled) && nrow(mat_scaled) > 0) mat_scaled else mat_data
# Filter for available TFs
available_tfs <- intersect(tf_meta_lit$TF, rownames(mat_use))
mat_use <- mat_use[available_tfs, , drop = FALSE]
# ============================================
# 3. Average per cluster & z-score
# ============================================
clusters <- as.factor(seurat_obj$seurat_clusters)
avg_mat <- sapply(levels(clusters), function(cl) Matrix::rowMeans(mat_use[, clusters == cl, drop = FALSE]))
colnames(avg_mat) <- levels(clusters)
avg_mat_z <- t(scale(t(avg_mat)))
avg_mat_z[is.na(avg_mat_z)] <- 0
# Align to our defined literature list order
row_order_idx <- match(rownames(avg_mat_z), tf_meta_lit$TF)
tf_meta_lit_filtered <- tf_meta_lit[row_order_idx, ]
avg_mat_z <- avg_mat_z[order(tf_meta_lit_filtered$Function), ]
tf_meta_lit_filtered <- tf_meta_lit_filtered[order(tf_meta_lit_filtered$Function), ]
# ============================================
# 4. Column & row annotations
# ============================================
cluster_status <- ifelse(colnames(avg_mat_z) %in% c("3","10"), "Normal CD4 T cells", "Malignant CD4 T cells")
ha_col <- HeatmapAnnotation(
Cell_State = cluster_status,
col = list(Cell_State = c("Normal CD4 T cells" = "#4DAF4A", "Malignant CD4 T cells" = "#E41A1C")),
annotation_name_side = "left"
)
# Colors matching the literature categories
role_colors <- c(
"Oncogenic " = "#808080", # Gray
"Th2 Core" = "#984EA3", # Purple
"Hyperactive TCR / Inflammatory" = "#E31A1C", # Red
"Canonical Tumor Suppressors" = "#377EB8" # Blue
)
ha_row <- rowAnnotation(
Function = tf_meta_lit_filtered$Function,
col = list(Function = role_colors),
annotation_name_side = "bottom"
)
# ============================================
# 5. Heatmap colors & plotting
# ============================================
col_fun <- circlize::colorRamp2(c(-3,0,3), c("#313695","white","#A50026"))
ht_lit <- Heatmap(
avg_mat_z,
name = "TF activity (z)",
col = col_fun,
top_annotation = ha_col,
left_annotation = ha_row,
column_split = cluster_status,
row_split = tf_meta_lit_filtered$Function,
cluster_row_slices = FALSE,
cluster_rows = FALSE,
cluster_columns = TRUE,
show_row_dend = FALSE,
show_column_dend = TRUE,
row_names_gp = gpar(fontsize = 12, fontface = "bold"),
column_names_gp = gpar(fontsize = 12),
column_title = "Literature-Validated Sézary Syndrome Regulators",
row_title_rot = 0,
row_title_gp = gpar(fontsize = 10, fontface = "bold"),
heatmap_legend_param = list(direction = "vertical")
)
# ============================================
# 6. Save plots
# ============================================
pdf("Output_Figures/Figure_TF_Heatmap_Literature_Validated.pdf", width=12, height=8)
draw(ht_lit, merge_legend=TRUE)
dev.off()
null device
1
png("Output_Figures/Figure_TF_Heatmap_Literature_Validated.png", width=12*300, height=8*300, res=300)
draw(ht_lit, merge_legend=TRUE)
dev.off()
null device
1
draw(ht_lit, merge_legend=TRUE)

Define
Th1/Th2/Th17/Th22/Treg Master Regulator Panel
# ============================================
# LIBRARIES
# ============================================
library(ComplexHeatmap)
library(circlize)
library(Matrix)
library(grid)
library(SeuratObject)
# ============================================
# 1. Define Th1/Th2/Th17/Th22/Treg Master Regulator Panel
# ============================================
tf_meta_thelper <- data.frame(
TF = c(
# Th1 Master Regulators
"TBX21", "STAT1", "STAT4", "IRF1",
# Th2 Master Regulators
"GATA3", "STAT6", "BATF", "IRF4",
# Th17 / Th22 Master Regulators
"RORC", "STAT3", "AHR", "MAF",
# Treg Master Regulators
"FOXP3", "FOXO1", "CTLA4" # CTLA4 regulon as Treg proxy
),
Function = c(
rep("Th1", 4),
rep("Th2", 4),
rep("Th17/Th22", 4),
rep("Treg", 3)
),
stringsAsFactors = FALSE
)
# Lock in canonical order: Th1 → Th2 → Th17/Th22 → Treg
tf_meta_thelper$Function <- factor(tf_meta_thelper$Function,
levels = c("Th1", "Th2", "Th17/Th22", "Treg"))
# ============================================
# 2. Extract TF activity matrix
# ============================================
mat_scaled <- tryCatch(SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "scale.data"), error = function(e) NULL)
mat_data <- SeuratObject::GetAssayData(seurat_obj, assay = "dorothea", layer = "data")
mat_use <- if (!is.null(mat_scaled) && nrow(mat_scaled) > 0) mat_scaled else mat_data
# Filter for available TFs
available_tfs <- intersect(tf_meta_thelper$TF, rownames(mat_use))
mat_use <- mat_use[available_tfs, , drop = FALSE]
# ============================================
# 3. Average per cluster & z-score
# ============================================
clusters <- as.factor(seurat_obj$seurat_clusters)
avg_mat <- sapply(levels(clusters), function(cl) Matrix::rowMeans(mat_use[, clusters == cl, drop = FALSE]))
colnames(avg_mat) <- levels(clusters)
avg_mat_z <- t(scale(t(avg_mat)))
avg_mat_z[is.na(avg_mat_z)] <- 0
# Align to our defined helper T panel order
row_order_idx <- match(rownames(avg_mat_z), tf_meta_thelper$TF)
tf_meta_thelper_filtered <- tf_meta_thelper[row_order_idx, ]
avg_mat_z <- avg_mat_z[order(tf_meta_thelper_filtered$Function), ]
tf_meta_thelper_filtered <- tf_meta_thelper_filtered[order(tf_meta_thelper_filtered$Function), ]
# ============================================
# 4. Column & row annotations
# ============================================
cluster_status <- ifelse(colnames(avg_mat_z) %in% c("3","10"), "Normal CD4 T cells", "Malignant CD4 T cells")
ha_col <- HeatmapAnnotation(
Cell_State = cluster_status,
col = list(Cell_State = c("Normal CD4 T cells" = "#4DAF4A", "Malignant CD4 T cells" = "#E41A1C")),
annotation_name_side = "left"
)
# Classical T helper color scheme
helper_colors <- c(
"Th1" = "#E31A1C", # Red
"Th2" = "#1F78B4", # Blue
"Th17/Th22" = "#FF7F00", # Orange
"Treg" = "#33A02C" # Green
)
ha_row <- rowAnnotation(
Function = tf_meta_thelper_filtered$Function,
col = list(Function = helper_colors),
annotation_name_side = "bottom"
)
# ============================================
# 5. Heatmap colors & plotting
# ============================================
col_fun <- circlize::colorRamp2(c(-3,0,3), c("#313695","white","#A50026"))
ht_thelper <- Heatmap(
avg_mat_z,
name = "TF activity (z)",
col = col_fun,
top_annotation = ha_col,
left_annotation = ha_row,
column_split = cluster_status,
row_split = tf_meta_thelper_filtered$Function,
cluster_row_slices = FALSE,
cluster_rows = FALSE,
cluster_columns = TRUE,
show_row_dend = FALSE,
show_column_dend = TRUE,
row_names_gp = gpar(fontsize = 11, fontface = "bold"),
column_names_gp = gpar(fontsize = 11),
column_title = "CD4+ T Helper Lineage Transcription Factors",
row_title_rot = 0,
row_title_gp = gpar(fontsize = 10, fontface = "bold"),
heatmap_legend_param = list(direction = "vertical")
)
# ============================================
# 6. Save plots
# ============================================
pdf("Output_Figures/Figure_TF_Heatmap_THelper_Lineages.pdf", width=12, height=8)
draw(ht_thelper, merge_legend=TRUE)
dev.off()
null device
1
png("Output_Figures/Figure_TF_Heatmap_THelper_Lineages.png", width=12*300, height=8*300, res=300)
draw(ht_thelper, merge_legend=TRUE)
dev.off()
null device
1
draw(ht_thelper, merge_legend=TRUE)

Final Save
print("Analysis pipeline complete. All figures and objects saved in Output_Figures folder.")
[1] "Analysis pipeline complete. All figures and objects saved in Output_Figures folder."
LS0tCnRpdGxlOiAiVEYgQWN0aXZpdHkgSW5mZXJlbmNlIEFuYWx5c2lzIEhlYXRtYXBzIgphdXRob3I6ICJOYXNpciBNYWhtb29kIEFiYmFzaSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgdGhlbWU6IGpvdXJuYWwKLS0tCgoKIyBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1UUlVFfQojIERhdGEgUHJvY2Vzc2luZwpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoc3RyaW5ncikKCiMgVmlzdWFsaXphdGlvbgpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KFNDcHVicikKCiMgUmVndWxhdG9yeSBOZXR3b3JrIEluZmVyZW5jZQpsaWJyYXJ5KGRlY291cGxlUikKbGlicmFyeShkb3JvdGhlYSkKZGF0YShkb3JvdGhlYV9ocywgcGFja2FnZSA9ICJkb3JvdGhlYSIpCmxpYnJhcnkodGljdG9jKQoKCmBgYAoKIyBMb2FkIFNldXJhdCBPYmplY3QgCmBgYHtyfQoKIyBMb2FkIHlvdXIgU2V1cmF0IE9iamVjdApzZXVyYXRfb2JqIDwtIHJlYWRSRFMoIi4uL091dHB1dF9PYmplY3RzL1NldXJhdF9PYmplY3RfV2l0aF9URl9BY3Rpdml0eS5yZHMiKQoKSWRlbnRzKHNldXJhdF9vYmopIDwtICJzZXVyYXRfY2x1c3RlcnMiCnByaW50KCJPYmplY3QgTG9hZGVkLiIpCmBgYAoKIyMgUnVuIHRoaXMgY29kZSBibG9jayB0byByZXN0b3JlIGFjdGl2aXRpZXMgaW5zdGFudGx5OgpgYGB7cn0KCiMgSWYgJ2FjdGl2aXRpZXMnIGlzIG1pc3NpbmcgYnV0ICdkb3JvdGhlYScgYXNzYXkgZXhpc3RzLCByZWNvbnN0cnVjdCBpdDoKaWYgKCFleGlzdHMoImFjdGl2aXRpZXMiKSAmJiAiZG9yb3RoZWEiICVpbiUgbmFtZXMoc2V1cmF0X29iakBhc3NheXMpKSB7CiAgCiAgcHJpbnQoIlJlY29uc3RydWN0aW5nICdhY3Rpdml0aWVzJyBkYXRhZnJhbWUgZnJvbSBTZXVyYXQgb2JqZWN0Li4uIikKICAKICAjIEV4dHJhY3QgdGhlIG1hdHJpeCAoU2V1cmF0IHY1IHVzZXMgJ2xheWVyJyBpbnN0ZWFkIG9mICdzbG90JykKICAjIFNpbmNlIHlvdSByYW4gU2NhbGVEYXRhLCB3ZSB1c2UgJ3NjYWxlLmRhdGEnCiAgbWF0IDwtIEdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheSA9ICJkb3JvdGhlYSIsIGxheWVyID0gInNjYWxlLmRhdGEiKQogIAogICMgQ29udmVydCB0byBsb25nIGZvcm1hdCAod2hhdCBTQ3B1YnIgbmVlZHMpCiAgYWN0aXZpdGllcyA8LSBhcy5kYXRhLmZyYW1lKG1hdCkgJT4lCiAgICByb3duYW1lc190b19jb2x1bW4oInNvdXJjZSIpICU+JQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtc291cmNlLCBuYW1lc190byA9ICJjb25kaXRpb24iLCB2YWx1ZXNfdG8gPSAic2NvcmUiKSAlPiUKICAgIG11dGF0ZShzdGF0aXN0aWMgPSAibm9ybV93bWVhbiIpICMgU0NwdWJyIHJlcXVpcmVzIHRoaXMgY29sdW1uCiAgICAKICBwcmludCgiQWN0aXZpdGllcyBkYXRhZnJhbWUgcmVzdG9yZWQhIikKfQpgYGAKCgojIyBTQ3B1YnIgSGVhdG1hcCBWaXN1YWxpemF0aW9uLUhlYXRtYXAgb2YgYXZlcmFnZWQgc2NvcmVzCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KbGlicmFyeShTQ3B1YnIpCiMgR2VuZXJhbCBoZWF0bWFwIChUb3AgVmFyaWFibGUgVEZzKQpvdXQgPC0gU0NwdWJyOjpkb19URkFjdGl2aXR5SGVhdG1hcChzYW1wbGUgPSBzZXVyYXRfb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY3Rpdml0aWVzID0gYWN0aXZpdGllcykKCnByaW50KG91dCkKCiMgMS4gU2F2ZSBhcyBQREYKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9EZWZhdWx0LnBkZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCnByaW50KG91dCkgIyBDb21wbGV4SGVhdG1hcCByZXF1aXJlcyBleHBsaWNpdCBwcmludCgpIGluc2lkZSBwZGYoKQpkZXYub2ZmKCkKCiMgMi4gU2F2ZSBhcyBQTkcKcG5nKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9EZWZhdWx0LnBuZyIsIHdpZHRoID0gMTAgKiAzMDAsIGhlaWdodCA9IDggKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQob3V0KQpkZXYub2ZmKCkKCgpwcmludChvdXQpCgpgYGAKCgojIyBTZXQgdGhlIHNjYWxlIGxpbWl0cwpgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CgpvdXQgPC0gU0NwdWJyOjpkb19URkFjdGl2aXR5SGVhdG1hcChzYW1wbGUgPSBzZXVyYXRfb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY3Rpdml0aWVzID0gYWN0aXZpdGllcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmN1dG9mZiA9IC0xLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heC5jdXRvZmYgPSAxLjUpCgpwcmludChvdXQpCgojIFNhdmUgQ29tcGxleEhlYXRtYXAgcHJvcGVybHkKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9TY2FsZWQucGRmIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKcHJpbnQob3V0KQpkZXYub2ZmKCkKCnBuZygiT3V0cHV0X0ZpZ3VyZXMvU0NwdWJyX0hlYXRtYXBfU2NhbGVkLnBuZyIsIHdpZHRoID0gMTAgKiAzMDAsIGhlaWdodCA9IDggKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQob3V0KQpkZXYub2ZmKCkKCgpgYGAKCiMjIEVuZm9yY2UgU3ltbWV0cnkgKEJlc3QgZm9yIE1hbnVzY3JpcHQpCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KCm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlIZWF0bWFwKHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uY3V0b2ZmID0gLTEuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LmN1dG9mZiA9IDEuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5mb3JjZV9zeW1tZXRyeSA9IFRSVUUpCgpwcmludChvdXQpCgpwZGYoIk91dHB1dF9GaWd1cmVzL1NDcHVicl9IZWF0bWFwX1N5bW1ldHJpYy5wZGYiLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQpwcmludChvdXQpCmRldi5vZmYoKQoKcG5nKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9TeW1tZXRyaWMucG5nIiwgd2lkdGggPSAxMCAqIDMwMCwgaGVpZ2h0ID0gOCAqIDMwMCwgcmVzID0gMzAwKQpwcmludChvdXQpCmRldi5vZmYoKQoKCnByaW50KG91dCkKCmBgYAoKIyMgVG9wIDQwIFRGcwpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTR9Cm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlIZWF0bWFwKHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3RmcyA9IDQwKQoKcHJpbnQob3V0KQoKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9Ub3A0MC5wZGYiLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSA2KQpwcmludChvdXQpCmRldi5vZmYoKQoKcG5nKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9Ub3A0MC5wbmciLCB3aWR0aCA9IDE0ICogMzAwLCBoZWlnaHQgPSA2ICogMzAwLCByZXMgPSAzMDApCnByaW50KG91dCkKZGV2Lm9mZigpCmBgYAoKCiMjIFRvcCAxMDAgVEZzIChGaWd1cmUgQSBmb3IgTWFudXNjcmlwdCkKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0zMn0KCm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlIZWF0bWFwKHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl90ZnMgPSAxMDApCgpwcmludChvdXQpCgpwZGYoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2QV9HbG9iYWxfVEZfSGVhdG1hcF9Ub3AxMDAucGRmIiwgd2lkdGggPSAzMiwgaGVpZ2h0ID0gMTIpCnByaW50KG91dCkKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2QV9HbG9iYWxfVEZfSGVhdG1hcF9Ub3AxMDAucG5nIiwgd2lkdGggPSAzMiAqIDMwMCwgaGVpZ2h0ID0gMTIgKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQob3V0KQpkZXYub2ZmKCkKYGBgCiMjIFRvcCAxMDAgVEZzIChGaWd1cmUgQSBmb3IgTWFudXNjcmlwdCkKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0zMn0KCm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlIZWF0bWFwKHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uY3V0b2ZmID0gLTEuNywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LmN1dG9mZiA9IDEuNywgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl90ZnMgPSAxMDApCgpwcmludChvdXQpCgpwZGYoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV9Ub3AxMDAucGRmIiwgd2lkdGggPSAzMiwgaGVpZ2h0ID0gMTIpCnByaW50KG91dCkKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV9Ub3AxMDAucG5nIiwgd2lkdGggPSAzMiAqIDMwMCwgaGVpZ2h0ID0gMTIgKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQob3V0KQpkZXYub2ZmKCkKYGBgCgojIERpZmZlcmVudGlhbCBURiBBY3Rpdml0eSAoTWFsaWduYW50IHZzLiBOb3JtYWwpCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CgojIERlZmluZSBDb21wYXJpc29uOiBDbHVzdGVycyAzICYgMTAgKE5vcm1hbCkgdnMgUmVzdCAoTWFsaWduYW50KQpub25fbWFsaWduYW50X2NsdXN0ZXJzIDwtIGMoMywgMTApCnNldXJhdF9vYmokQ29uZGl0aW9uIDwtIGlmZWxzZShzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycyAlaW4lIG5vbl9tYWxpZ25hbnRfY2x1c3RlcnMsICJOb24tTWFsaWduYW50IiwgIk1hbGlnbmFudCIpCgojIFBlcmZvcm0gRGlmZmVyZW50aWFsIEFuYWx5c2lzIG9uIFRGIEFjdGl2aXR5CkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiZG9yb3RoZWEiCklkZW50cyhzZXVyYXRfb2JqKSA8LSAiQ29uZGl0aW9uIgoKcHJpbnQoIlJ1bm5pbmcgRmluZE1hcmtlcnMgb24gVEYgQWN0aXZpdHkuLi4iKQpkaWZmX3RmcyA8LSBGaW5kTWFya2VycyhzZXVyYXRfb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgaWRlbnQuMSA9ICJNYWxpZ25hbnQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgaWRlbnQuMiA9ICJOb24tTWFsaWduYW50IiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAsICMgR2V0IGFsbCBmb3Igdm9sY2FubwogICAgICAgICAgICAgICAgICAgICAgICBtaW4ucGN0ID0gMCkKCiMgQWRkIGdlbmUgY29sdW1uIGZvciBsYWJlbGluZwpkaWZmX3RmcyRnZW5lIDwtIHJvd25hbWVzKGRpZmZfdGZzKQoKIyBTYXZlIFJlc3VsdHMKd3JpdGUuY3N2KGRpZmZfdGZzLCAiT3V0cHV0X1RhYmxlcy9EaWZmZXJlbnRpYWxfVEZfQWN0aXZpdHlfTWFsaWduYW50X3ZzX05vcm1hbC5jc3YiKQpwcmludCgiRGlmZmVyZW50aWFsIGFuYWx5c2lzIGNvbXBsZXRlLiIpCgpgYGAKCgojIEZpZ3VyZSBDOiBWb2xjYW5vIFBsb3QgKExvc3Mgb2YgSG9tZW9zdGFzaXMpCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD04fQojIEhpZ2hsaWdodCBrZXkgZHJpdmVycyBtZW50aW9uZWQgaW4gdGV4dApoaWdobGlnaHRfdGZzIDwtIGMoIkZPWE8xIiwgIk1ZQyIsICJFMkYxIiwgIkUyRjQiLCAiRk9YTTEiLCAiUkVMQSIsICJJUkYxIiwgIlNUQVQxIikKCnBfdm9sY2FubyA8LSBTQ3B1YnI6OmRvX1ZvbGNhbm9QbG90KHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlX2dlbmVzID0gZGlmZl90ZnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2Q19Wb2xjYW5vX1RGX0FjdGl2aXR5LnBkZiIsIHBsb3QgPSBwX3ZvbGNhbm8sIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfMy4xNkNfVm9sY2Fub19URl9BY3Rpdml0eS5wbmciLCBwbG90ID0gcF92b2xjYW5vLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCkKcHJpbnQocF92b2xjYW5vKQpgYGAKCgojIFVwZGF0ZWQgRmlndXJlIEM6IEVuaGFuY2VkVm9sY2FubwpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCiMgSGlnaGxpZ2h0IGtleSBkcml2ZXJzIG1lbnRpb25lZCBpbiB0ZXh0CmhpZ2hsaWdodF90ZnMgPC0gYygiRk9YTzEiLCAiTVlDIiwgIkUyRjEiLCAiRTJGNCIsICJGT1hNMSIsICJSRUxBIiwgIklSRjEiLCAiU1RBVDEiLCAiVE9YIiwgIkdBVEEzIikKCiMgQ3JlYXRlIHRoZSBFbmhhbmNlZFZvbGNhbm8gUGxvdApwX3ZvbGNhbm8gPC0gRW5oYW5jZWRWb2xjYW5vKGRpZmZfdGZzLAogICAgbGFiID0gcm93bmFtZXMoZGlmZl90ZnMpLAogICAgeCA9ICdhdmdfbG9nMkZDJywKICAgIHkgPSAncF92YWxfYWRqJywKICAgIAogICAgdGl0bGUgPSAnRGlmZmVyZW50aWFsIFRGIEFjdGl2aXR5OiBNYWxpZ25hbnQgdnMuIE5vbi1NYWxpZ25hbnQnLAogICAgc3VidGl0bGUgPSAnRGVjb3VwbGVSIEluZmVycmVkIEFjdGl2aXR5JywKICAgIHBDdXRvZmYgPSAxZS01LAogICAgRkNjdXRvZmYgPSAwLjUsCiAgICBwb2ludFNpemUgPSAzLjAsCiAgICBsYWJTaXplID0gNS4wLAogICAgY29sQWxwaGEgPSAwLjgsCiAgICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCiAgICBsZWdlbmRMYWJTaXplID0gMTIsCiAgICBsZWdlbmRJY29uU2l6ZSA9IDQuMCwKICAgIGRyYXdDb25uZWN0b3JzID0gVFJVRSwgIyBEcmF3IGxpbmVzIHRvIGxhYmVscyB0byBhdm9pZCBvdmVybGFwCiAgICB3aWR0aENvbm5lY3RvcnMgPSAwLjUsCiAgICBjb2xDb25uZWN0b3JzID0gJ2dyZXkzMCcsCiAgICAjIEN1c3RvbSBDb2xvcnM6IERvd24gKEJsdWUpLCBVcCAoUmVkKSwgTlMgKEdyZXkpCiAgICBjb2wgPSBjKCJncmV5MzAiLCAiZm9yZXN0Z3JlZW4iLCAicm95YWxibHVlIiwgImZpcmVicmljazIiKQopCgojIFByaW50CnByaW50KHBfdm9sY2FubykKCiMgU2F2ZQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2Q19FbmhhbmNlZFZvbGNhbm9fVEZfQWN0aXZpdHkucGRmIiwgcGxvdCA9IHBfdm9sY2Fubywgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfMy4xNkNfRW5oYW5jZWRWb2xjYW5vX1RGX0FjdGl2aXR5LnBuZyIsIHBsb3QgPSBwX3ZvbGNhbm8sIHdpZHRoID0gMTAsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKYGBgCgoKCgoKCgoKIyBGaWd1cmUgRDogTWl4ZWQgRmVhdHVyZSBQbG90cyAoQWN0aXZpdHkgdnMgRXhwcmVzc2lvbikKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNn0KCiMgV2UgbWFudWFsbHkgY29uc3RydWN0IHRoaXMgdG8gbWl4IEFzc2F5cwoKIyBQYXJ0IDE6IFRGIEFjdGl2aXR5IFBsb3RzIChBc3NheTogZG9yb3RoZWEpCkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiZG9yb3RoZWEiCgpwMSA8LSBGZWF0dXJlUGxvdChzZXVyYXRfb2JqLCBmZWF0dXJlcyA9ICJGT1hPMSIsIG9yZGVyID0gVCwgcmVkdWN0aW9uID0gInVtYXAiKSArIAogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYygiZ3JleTkwIiwgImZpcmVicmljayIpKSArIGdndGl0bGUoIkZPWE8xIEFjdGl2aXR5IChIb21lb3N0YXNpcykiKQpwMiA8LSBGZWF0dXJlUGxvdChzZXVyYXRfb2JqLCBmZWF0dXJlcyA9ICJSRUxBIiwgb3JkZXIgPSBULCByZWR1Y3Rpb24gPSAidW1hcCIpICsgCiAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBjKCJncmV5OTAiLCAiZmlyZWJyaWNrIikpICsgZ2d0aXRsZSgiUkVMQSBBY3Rpdml0eSAoSW5mbGFtbWF0b3J5KSIpCnAzIDwtIEZlYXR1cmVQbG90KHNldXJhdF9vYmosIGZlYXR1cmVzID0gIklSRjEiLCBvcmRlciA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikgKyAKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGMoImdyZXk5MCIsICJmaXJlYnJpY2siKSkgKyBnZ3RpdGxlKCJJUkYxIEFjdGl2aXR5IChJRk4tUmVzcG9uc2UpIikKcDQgPC0gRmVhdHVyZVBsb3Qoc2V1cmF0X29iaiwgZmVhdHVyZXMgPSAiRk9YTTEiLCBvcmRlciA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikgKyAKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGMoImdyZXk5MCIsICJmaXJlYnJpY2siKSkgKyBnZ3RpdGxlKCJGT1hNMSBBY3Rpdml0eSAoUHJvbGlmZXJhdGlvbikiKQoKIyBQYXJ0IDI6IEdlbmUgRXhwcmVzc2lvbiBQbG90cyAoQXNzYXk6IFNDVC9STkEpCkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiU0NUIgoKcDUgPC0gRmVhdHVyZVBsb3Qoc2V1cmF0X29iaiwgZmVhdHVyZXMgPSAiSE1HQTIiLCBvcmRlciA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikgKyAKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGMoImdyZXk5MCIsICJkYXJrYmx1ZSIpKSArIGdndGl0bGUoIkhNR0EyIEV4cHJlc3Npb24gKFN0ZW0tbGlrZSkiKQpwNiA8LSBGZWF0dXJlUGxvdChzZXVyYXRfb2JqLCBmZWF0dXJlcyA9ICJTT1g0Iiwgb3JkZXIgPSBULCByZWR1Y3Rpb24gPSAidW1hcCIpICsgCiAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBjKCJncmV5OTAiLCAiZGFya2JsdWUiKSkgKyBnZ3RpdGxlKCJTT1g0IEV4cHJlc3Npb24gKFN0ZW0tbGlrZSkiKQoKIyBDb21iaW5lCmZpbmFsX2ZpZ3VyZV9EIDwtIChwMSB8IHAyIHwgcDMpIC8gKHA0IHwgcDUgfCBwNikgKyAKICAgICAgICAgICAgICAgICAgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gIkZpZ3VyZSAzLjE2RDogS2V5IERyaXZlcnMgKFJlZD1BY3Rpdml0eSwgQmx1ZT1FeHByZXNzaW9uKSIpCgpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2RF9NaXhlZF9GZWF0dXJlcy5wZGYiLCBwbG90ID0gZmluYWxfZmlndXJlX0QsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwKQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2RF9NaXhlZF9GZWF0dXJlcy5wbmciLCBwbG90ID0gZmluYWxfZmlndXJlX0QsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCnByaW50KGZpbmFsX2ZpZ3VyZV9EKQpgYGAKCgoKCiMgRmlndXJlIEUgKENvbXBsZXhIZWF0bWFwKSBjaHVuawpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KE1hdHJpeCkKCiMgRXhwYW5kZWQgbGlzdCBvZiBzdGF0ZS1zcGVjaWZpYyBkcml2ZXJzIGJhc2VkIG9uIHlvdXIgcmVndWxvbiBhbmFseXNpcwpsaXRlcmF0dXJlX3RmcyA8LSBjKAogICJHQVRBMyIsICJTVEFUNiIsICJCQVRGIiwgIkZPWFAzIiwgIlNUQVQzIiwgIlNUQVQ1QiIsICJUQ0Y3IiwgIyBDb3JlL01lbW9yeQogICJFMkYxIiwgIk1ZQyIsICJGT1hNMSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBQcm9saWZlcmF0aW9uIChDbCA3KQogICJTVEFUMSIsICJTVEFUMiIsICJJUkYxIiwgIklSRjkiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBJRk4tc3RpbXVsYXRlZCAoQ2wgMTMpCiAgIlJFTEEiLCAiTkZLQjEiLCAiUkVMIiwgIkZPUyIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFByby1pbmZsYW1tYXRvcnkgKENsIDExLCAxMikKICAiVEJYMjEiLCAiUlVOWDMiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQ3l0b3RveGljIChDbCAxLCA5KQogICJISUYxQSIsICJTUkVCRjEiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZXRhYm9saWMgc2hpZnQgKENsIDgpCiAgIlJGWDUiLCAiU1BJMSIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1IQy1JSSBIaWdoIChDbCAwKQopCgoKIyBLZWVwIG9ubHkgVEZzIHByZXNlbnQgaW4gdGhlIGRvcm90aGVhIGFzc2F5CmF2YWlsYWJsZV90ZnMgPC0gaW50ZXJzZWN0KGxpdGVyYXR1cmVfdGZzLCByb3duYW1lcyhzZXVyYXRfb2JqW1siZG9yb3RoZWEiXV0pKQppZiAobGVuZ3RoKGF2YWlsYWJsZV90ZnMpIDwgNSkgc3RvcCgiVG9vIGZldyBURnMgZm91bmQgaW4gZG9yb3RoZWEgYXNzYXkuIENoZWNrIFRGIG5hbWluZyAvIGFzc2F5IGNvbnRlbnQuIikKCiMgRXh0cmFjdCBURiBhY3Rpdml0eSBtYXRyaXggKFRGcyB4IGNlbGxzKQojIFVzZSBzY2FsZS5kYXRhIGlmIGF2YWlsYWJsZTsgb3RoZXJ3aXNlIGZhbGwgYmFjayB0byBkYXRhIGxheWVyLgptYXRfc2NhbGVkIDwtIHRyeUNhdGNoKAogIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAic2NhbGUuZGF0YSIpLAogIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTAopCm1hdF9kYXRhIDwtIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAiZGF0YSIpCgptYXRfdXNlIDwtIGlmICghaXMubnVsbChtYXRfc2NhbGVkKSAmJiBucm93KG1hdF9zY2FsZWQpID4gMCkgbWF0X3NjYWxlZCBlbHNlIG1hdF9kYXRhCm1hdF91c2UgPC0gbWF0X3VzZVthdmFpbGFibGVfdGZzLCAsIGRyb3AgPSBGQUxTRV0KCiMgQXZlcmFnZSBwZXIgY2x1c3RlciAoVEYgeCBjbHVzdGVyKQpjbHVzdGVycyA8LSBhcy5mYWN0b3Ioc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMpCmF2Z19tYXQgPC0gc2FwcGx5KGxldmVscyhjbHVzdGVycyksIGZ1bmN0aW9uKGNsKSB7CiAgTWF0cml4Ojpyb3dNZWFucyhtYXRfdXNlWywgY2x1c3RlcnMgPT0gY2wsIGRyb3AgPSBGQUxTRV0pCn0pCmNvbG5hbWVzKGF2Z19tYXQpIDwtIGxldmVscyhjbHVzdGVycykKCiMgT3B0aW9uYWw6IHotc2NvcmUgYWNyb3NzIGNsdXN0ZXJzIChoZWxwcyByZWFkYWJpbGl0eSBpZiB5b3UgdXNlZCByYXcgJ2RhdGEnIGluc3RlYWQgb2YgJ3NjYWxlLmRhdGEnKQphdmdfbWF0X3ogPC0gdChzY2FsZSh0KGF2Z19tYXQpKSkKYXZnX21hdF96W2lzLm5hKGF2Z19tYXRfeildIDwtIDAKCiMgQ29sb3JzCmNvbF9mdW4gPC0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygtMiwgMCwgMiksIGMoIiMzMTM2OTUiLCAid2hpdGUiLCAiI0E1MDAyNiIpKQoKaHQgPC0gSGVhdG1hcCgKICBhdmdfbWF0X3osCiAgbmFtZSA9ICJURiBhY3Rpdml0eSAoeikiLAogIGNvbCA9IGNvbF9mdW4sCiAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogIHNob3dfcm93X2RlbmQgPSBUUlVFLAogIHNob3dfY29sdW1uX2RlbmQgPSBUUlVFLAogIHJvd19uYW1lc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSAxMCksCiAgY29sdW1uX25hbWVzX2dwID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IDEwKSwKICBjb2x1bW5fdGl0bGUgPSAiTGl0ZXJhdHVyZS12YWxpZGF0ZWQgU8OpemFyeSBURiBtb2R1bGVzIChEb1JvdGhFQS9kZWNvdXBsZVIpIiwKICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QoZGlyZWN0aW9uID0gInZlcnRpY2FsIikKKQoKIyBEcmF3IHRvIG5vdGVib29rCmRyYXcoaHQpCgojIFNhdmUgUERGICh2ZWN0b3IpCnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlXzMuMTZFX0xpdGVyYXR1cmVfVEZfSGVhdG1hcF9Db21wbGV4SGVhdG1hcC5wZGYiLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQpkcmF3KGh0KQpkZXYub2ZmKCkKCiMgU2F2ZSBQTkcgKHJhc3RlciwgcHVibGljYXRpb24tcmVhZHkpCnBuZygiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlXzMuMTZFX0xpdGVyYXR1cmVfVEZfSGVhdG1hcF9Db21wbGV4SGVhdG1hcC5wbmciLAogICAgd2lkdGggPSAxMCAqIDMwMCwgaGVpZ2h0ID0gOCAqIDMwMCwgcmVzID0gMzAwKQpkcmF3KGh0KQpkZXYub2ZmKCkKYGBgCgojIEZpZ3VyZSBGIChDb21wbGV4SGVhdG1hcCkgY2h1bmsKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShNYXRyaXgpCgojIEV4cGFuZGVkIGxpc3QgaW5jbHVkaW5nIEZPWE8xIGFuZCB0dW1vciBzdXBwcmVzc29ycwpsaXRlcmF0dXJlX3RmcyA8LSBjKAogICMgVG9wIE1hbGlnbmFudCBVcHJlZ3VsYXRlZCAoT25jb2dlbmljLCBTdHJlc3MsIFByb2xpZmVyYXRpb24pCiAgIlJGWDUiLCAiTVlDIiwgIkUyRjQiLCAiSFNGMSIsICJTUkVCRjIiLCAiTkZFMkwyIiwgCiAgIlJFTEEiLCAiUkVMIiwgIk5GS0IxIiwgIklSRjEiLCAiTkNPQTIiLAogIAogICMgTWFsaWduYW50IERvd25yZWd1bGF0ZWQgLyBOb3JtYWwgRW5yaWNoZWQgKFR1bW9yIFN1cHByZXNzb3JzICYgSG9tZW9zdGFzaXMpCiAgIkZPWE8xIiwgIkZPWE80IiwgIlJVTlgzIiwgIlRDRjMiLCAiQkNMMTFBIiwgIk5FVVJPRDEiLCAiTUVGMkIiLCAiUEJYMiIsCiAgCiAgIyBVTUFQIFN0YXRlIERyaXZlcnMgKEludHJhLXR1bW9yYWwgaGV0ZXJvZ2VuZWl0eSkKICAiR0FUQTMiLCAiU1RBVDYiLCAiQkFURiIsICJGT1hQMyIsICJTVEFUMyIsICJTVEFUNUIiLCAiVENGNyIsICMgQ29yZS9NZW1vcnkKICAiRTJGMSIsICJGT1hNMSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUHJvbGlmZXJhdGlvbgogICJTVEFUMSIsICJTVEFUMiIsICJJUkY5IiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBJRk4gcmVzcG9uc2UKICAiSElGMUEiLCAiU1JFQkYxIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWV0YWJvbGljCiAgIlRCWDIxIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEN5dG90b3hpYwopCgojIEV4dHJhY3QgVEYgYWN0aXZpdHkgbWF0cml4Cm1hdF9zY2FsZWQgPC0gdHJ5Q2F0Y2goCiAgU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJzY2FsZS5kYXRhIiksCiAgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMCikKbWF0X2RhdGEgPC0gU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJkYXRhIikKCm1hdF91c2UgPC0gaWYgKCFpcy5udWxsKG1hdF9zY2FsZWQpICYmIG5yb3cobWF0X3NjYWxlZCkgPiAwKSBtYXRfc2NhbGVkIGVsc2UgbWF0X2RhdGEKYXZhaWxhYmxlX3RmcyA8LSBpbnRlcnNlY3QobGl0ZXJhdHVyZV90ZnMsIHJvd25hbWVzKG1hdF91c2UpKQppZiAobGVuZ3RoKGF2YWlsYWJsZV90ZnMpIDwgNSkgc3RvcCgiVG9vIGZldyBURnMgZm91bmQuIENoZWNrIGFzc2F5IGRhdGEuIikKbWF0X3VzZSA8LSBtYXRfdXNlW2F2YWlsYWJsZV90ZnMsICwgZHJvcCA9IEZBTFNFXQoKIyBBdmVyYWdlIHBlciBjbHVzdGVyIGFuZCB6LXNjb3JlCmNsdXN0ZXJzIDwtIGFzLmZhY3RvcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykKYXZnX21hdCA8LSBzYXBwbHkobGV2ZWxzKGNsdXN0ZXJzKSwgZnVuY3Rpb24oY2wpIHsKICBNYXRyaXg6OnJvd01lYW5zKG1hdF91c2VbLCBjbHVzdGVycyA9PSBjbCwgZHJvcCA9IEZBTFNFXSkKfSkKY29sbmFtZXMoYXZnX21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQoKYXZnX21hdF96IDwtIHQoc2NhbGUodChhdmdfbWF0KSkpCmF2Z19tYXRfeltpcy5uYShhdmdfbWF0X3opXSA8LSAwCgojIEFubm90YXRlIE1hbGlnbmFudCB2cyBOb3JtYWwgKENsdXN0ZXJzIDMsIDEwID0gTm9ybWFsKQpjbHVzdGVyX3N0YXR1cyA8LSBpZmVsc2UoY29sbmFtZXMoYXZnX21hdF96KSAlaW4lIGMoIjMiLCAiMTAiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiTm9ybWFsIENENCBUIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiTWFsaWduYW50IENENCBUIGNlbGxzIikKCiMgRGVmaW5lIGFubm90YXRpb24KaGEgPC0gSGVhdG1hcEFubm90YXRpb24oCiAgQ2VsbF9TdGF0ZSA9IGNsdXN0ZXJfc3RhdHVzLAogIGNvbCA9IGxpc3QoQ2VsbF9TdGF0ZSA9IGMoIk5vcm1hbCBDRDQgVCIgPSAiIzREQUY0QSIsICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMiID0gIiNFNDFBMUMiKSksCiAgYW5ub3RhdGlvbl9uYW1lX3NpZGUgPSAibGVmdCIKKQoKIyBDb2xvcnMKY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC0zLCAwLCAzKSwgYygiIzMxMzY5NSIsICJ3aGl0ZSIsICIjQTUwMDI2IikpCgojIENyZWF0ZSBoZWF0bWFwIHdpdGggY29sdW1uIHNwbGl0Cmh0IDwtIEhlYXRtYXAoCiAgYXZnX21hdF96LAogIG5hbWUgPSAiVEYgYWN0aXZpdHkgKHopIiwKICBjb2wgPSBjb2xfZnVuLAogIHRvcF9hbm5vdGF0aW9uID0gaGEsCiAgY29sdW1uX3NwbGl0ID0gY2x1c3Rlcl9zdGF0dXMsICMgUGh5c2ljYWxseSBzcGxpdHMgbm9ybWFsIGFuZCBtYWxpZ25hbnQgY29sdW1ucwogIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwKICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKGZvbnRzaXplID0gMTApLAogIGNvbHVtbl9uYW1lc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSAxMCksCiAgY29sdW1uX3RpdGxlID0gIkRpZmZlcmVudGlhbCBURiBNb2R1bGVzIGluIFPDqXphcnkgSGV0ZXJvZ2VuZWl0eSIsCiAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KGRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpCikKCiMgT3V0cHV0CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlXzMuMTZFX0RpZmZlcmVudGlhbF9URl9IZWF0bWFwLnBkZiIsIHdpZHRoID0gMTEsIGhlaWdodCA9IDkpCmRyYXcoaHQpCmRldi5vZmYoKQoKcG5nKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfMy4xNkVfRGlmZmVyZW50aWFsX1RGX0hlYXRtYXAucG5nIiwKICAgIHdpZHRoID0gMTEgKiAzMDAsIGhlaWdodCA9IDkgKiAzMDAsIHJlcyA9IDMwMCkKZHJhdyhodCkKZGV2Lm9mZigpCmRyYXcoaHQpCmBgYAojIEZpZ3VyZSBHIChDb21wbGV4SGVhdG1hcCkgY2h1bmsKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMn0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBMSUJSQVJJRVMKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShncmlkKQpsaWJyYXJ5KFNldXJhdE9iamVjdCkgICMgZm9yIEdldEFzc2F5RGF0YQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDEuIERlZmluZSBURiBwYW5lbCBtZXRhZGF0YSAoNDQtNDcgVEZzKQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnRmX21ldGEgPC0gZGF0YS5mcmFtZSgKICBURiA9IGMoCiAgICAiTVlDIiwiRTJGNCIsIlJGWDUiLCJUV0lTVDEiLCJKVU5CIiwiSVJGNCIsIkNSRUIxIiwKICAgICJGT1MiLCJGT1NMMSIsCiAgICAiSFNGMSIsIk5GRTJMMiIsIlNSRUJGMiIsCiAgICAiUkVMQSIsIlJFTCIsIk5GS0IxIiwiSVJGMSIsIk5DT0EyIiwKICAgICJORkFUQzEiLCJORkFUQzIiLAogICAgIkZPWE8xIiwiRk9YTzQiLCJSVU5YMyIsIlpFQjEiLCJCQUNIMiIsCiAgICAiVENGMyIsIkJDTDExQSIsIk5FVVJPRDEiLCJNRUYyQiIsIlBCWDIiLCJJUkYzIiwiQkNMNiIsCiAgICAiR0FUQTMiLCJTVEFUNiIsIkJBVEYiLCJGT1hQMyIsIlNUQVQzIiwiU1RBVDVCIiwiVENGNyIsCiAgICAiRTJGMSIsIkZPWE0xIiwKICAgICJTVEFUMSIsIlNUQVQyIiwiSVJGOSIsCiAgICAiSElGMUEiLCJTUkVCRjEiLAogICAgIkVPTUVTIiwKICAgICJQUkRNMSIKICApLAogIENvbmRpdGlvbiA9IGMoCiAgICByZXAoIk1hbGlnbmFudCIsIDcpLCAgIyBPbmNvZ2VuaWMKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAjIEFQLTEKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAjIFN0cmVzcwogICAgcmVwKCJNYWxpZ25hbnQiLCA1KSwgICMgTkYta0IKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAjIE5GQVQKICAgIHJlcCgiTm9ybWFsIiwgNSksICAgICAjIFR1bW9yIFN1cHByZXNzb3IKICAgIHJlcCgiTm9ybWFsIiwgNyksICAgICAjIEhvbWVvc3Rhc2lzCiAgICByZXAoIk1hbGlnbmFudCIsIDcpLCAgIyBUaDIvTWVtb3J5IENvcmUKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAjIFByb2xpZmVyYXRpb24KICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAjIElGTgogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICMgTWV0YWJvbGlzbQogICAgcmVwKCJNYWxpZ25hbnQiLCAxKSwgICMgQ3l0b3RveGljCiAgICByZXAoIk1hbGlnbmFudCIsIDEpICAgIyBUZXJtaW5hbCBFZmZlY3RvcgogICksCiAgRnVuY3Rpb24gPSBjKAogICAgcmVwKCJPbmNvZ2VuaWMiLCA3KSwKICAgIHJlcCgiQVAtMSBzaWduYWxpbmciLCAyKSwKICAgIHJlcCgiU3RyZXNzIFJlc3BvbnNlIiwgMyksCiAgICByZXAoIkluZmxhbW1hdG9yeS9ORi1rQiIsIDUpLAogICAgcmVwKCJORkFUIHNpZ25hbGluZyIsIDIpLAogICAgcmVwKCJUdW1vciBTdXBwcmVzc29yIiwgNSksCiAgICByZXAoIk5vcm1hbCBIb21lb3N0YXNpcyIsIDcpLAogICAgcmVwKCJUaDIvTWVtb3J5IENvcmUiLCA3KSwKICAgIHJlcCgiUHJvbGlmZXJhdGlvbiIsIDIpLAogICAgcmVwKCJJRk4gUmVzcG9uc2UiLCAzKSwKICAgIHJlcCgiTWV0YWJvbGlzbSIsIDIpLAogICAgcmVwKCJDeXRvdG94aWMiLCAxKSwKICAgIHJlcCgiVGVybWluYWwgRWZmZWN0b3IiLCAxKQogICksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAyLiBFeHRyYWN0IFRGIGFjdGl2aXR5IG1hdHJpeCBmcm9tIFNldXJhdAojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Cm1hdF9zY2FsZWQgPC0gdHJ5Q2F0Y2goCiAgU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJzY2FsZS5kYXRhIiksCiAgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMCikKbWF0X2RhdGEgPC0gU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJkYXRhIikKbWF0X3VzZSA8LSBpZiAoIWlzLm51bGwobWF0X3NjYWxlZCkgJiYgbnJvdyhtYXRfc2NhbGVkKSA+IDApIG1hdF9zY2FsZWQgZWxzZSBtYXRfZGF0YQoKIyBGaWx0ZXIgZm9yIFRGcyBwcmVzZW50IGluIFNldXJhdAphdmFpbGFibGVfdGZzIDwtIGludGVyc2VjdCh0Zl9tZXRhJFRGLCByb3duYW1lcyhtYXRfdXNlKSkKbWF0X3VzZSA8LSBtYXRfdXNlW2F2YWlsYWJsZV90ZnMsICwgZHJvcCA9IEZBTFNFXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDMuIEF2ZXJhZ2UgcGVyIGNsdXN0ZXIgJiB6LXNjb3JlCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY2x1c3RlcnMgPC0gYXMuZmFjdG9yKHNldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzKQphdmdfbWF0IDwtIHNhcHBseShsZXZlbHMoY2x1c3RlcnMpLCBmdW5jdGlvbihjbCkgewogIE1hdHJpeDo6cm93TWVhbnMobWF0X3VzZVssIGNsdXN0ZXJzID09IGNsLCBkcm9wID0gRkFMU0VdKQp9KQpjb2xuYW1lcyhhdmdfbWF0KSA8LSBsZXZlbHMoY2x1c3RlcnMpCgphdmdfbWF0X3ogPC0gdChzY2FsZSh0KGF2Z19tYXQpKSkKYXZnX21hdF96W2lzLm5hKGF2Z19tYXRfeildIDwtIDAKCiMgQWxpZ24gdGZfbWV0YSBvcmRlcgp0Zl9tZXRhX2ZpbHRlcmVkIDwtIHRmX21ldGFbbWF0Y2gocm93bmFtZXMoYXZnX21hdF96KSwgdGZfbWV0YSRURiksIF0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA0LiBDb2x1bW4gJiByb3cgYW5ub3RhdGlvbnMKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjbHVzdGVyX3N0YXR1cyA8LSBpZmVsc2UoY29sbmFtZXMoYXZnX21hdF96KSAlaW4lIGMoIjMiLCIxMCIpLCAiTm9ybWFsIENENCBUIGNlbGxzIiwgIk1hbGlnbmFudCBDRDQgVCBjZWxscyIpCmhhX2NvbCA8LSBIZWF0bWFwQW5ub3RhdGlvbigKICBDZWxsX1N0YXRlID0gY2x1c3Rlcl9zdGF0dXMsCiAgY29sID0gbGlzdChDZWxsX1N0YXRlID0gYygiTm9ybWFsIENENCBUIGNlbGxzIiA9ICIjNERBRjRBIiwgIk1hbGlnbmFudCBDRDQgVCBjZWxscyIgPSAiI0U0MUExQyIpKSwKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJsZWZ0IgopCgojIFJvdyBhbm5vdGF0aW9uCmZ1bmN0aW9uX2NvbG9ycyA8LSBjKAogICJPbmNvZ2VuaWMiID0gIiNGRjdGMDAiLAogICJBUC0xIHNpZ25hbGluZyIgPSAiI0ZGQTUwMCIsCiAgIlN0cmVzcyBSZXNwb25zZSIgPSAiI0ZGRDcwMCIsCiAgIkluZmxhbW1hdG9yeS9ORi1rQiIgPSAiIzFFOTBGRiIsCiAgIk5GQVQgc2lnbmFsaW5nIiA9ICIjNDE2OUUxIiwKICAiVHVtb3IgU3VwcHJlc3NvciIgPSAiIzM3N0VCOCIsCiAgIk5vcm1hbCBIb21lb3N0YXNpcyIgPSAiIzREQUY0QSIsCiAgIlRoMi9NZW1vcnkgQ29yZSIgPSAiIzk4NEVBMyIsCiAgIlByb2xpZmVyYXRpb24iID0gIiNFNDFBMUMiLAogICJJRk4gUmVzcG9uc2UiID0gIiMwMENFRDEiLAogICJNZXRhYm9saXNtIiA9ICIjQTY1NjI4IiwKICAiQ3l0b3RveGljIiA9ICIjRjc4MUJGIiwKICAiVGVybWluYWwgRWZmZWN0b3IiID0gIiM4MDAwODAiCikKCmhhX3JvdyA8LSByb3dBbm5vdGF0aW9uKAogIENvbmRpdGlvbiA9IHRmX21ldGFfZmlsdGVyZWQkQ29uZGl0aW9uLAogIEZ1bmN0aW9uID0gdGZfbWV0YV9maWx0ZXJlZCRGdW5jdGlvbiwKICBjb2wgPSBsaXN0KAogICAgQ29uZGl0aW9uID0gYygiTm9ybWFsIj0iIzREQUY0QSIsIk1hbGlnbmFudCI9IiNFNDFBMUMiKSwKICAgIEZ1bmN0aW9uID0gZnVuY3Rpb25fY29sb3JzCiAgKSwKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJib3R0b20iCikKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA1LiBIZWF0bWFwIGNvbG9ycyAmIHBsb3R0aW5nCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC0zLDAsMyksIGMoIiMzMTM2OTUiLCJ3aGl0ZSIsIiNBNTAwMjYiKSkKCmh0IDwtIEhlYXRtYXAoCiAgYXZnX21hdF96LAogIG5hbWUgPSAiVEYgYWN0aXZpdHkgKHopIiwKICBjb2wgPSBjb2xfZnVuLAogIHRvcF9hbm5vdGF0aW9uID0gaGFfY29sLAogIGxlZnRfYW5ub3RhdGlvbiA9IGhhX3JvdywKICBjb2x1bW5fc3BsaXQgPSBjbHVzdGVyX3N0YXR1cywKICByb3dfc3BsaXQgPSB0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uLAogIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCAgICAgIyBzaG93IGJpb2xvZ2ljYWwgc3BsaXQKICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwKICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTApLAogIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksCiAgY29sdW1uX3RpdGxlID0gIkZ1bmN0aW9uYWwgVEYgTW9kdWxlcyBpbiBTw6l6YXJ5IFN5bmRyb21lIiwKICByb3dfdGl0bGVfcm90ID0gMCwKICByb3dfdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gOSwgZm9udGZhY2UgPSAiYm9sZCIpLAogIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdChkaXJlY3Rpb24gPSAidmVydGljYWwiKQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNi4gU2F2ZSBwbG90cwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXAucGRmIiwgd2lkdGg9MTIsIGhlaWdodD0xMCkKZHJhdyhodCwgbWVyZ2VfbGVnZW5kPVRSVUUpCmRldi5vZmYoKQoKcG5nKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfVEZfSGVhdG1hcC5wbmciLCB3aWR0aD0xMiozMDAsIGhlaWdodD0xMCozMDAsIHJlcz0zMDApCmRyYXcoaHQsIG1lcmdlX2xlZ2VuZD1UUlVFKQpkZXYub2ZmKCkKCmRyYXcoaHQsIG1lcmdlX2xlZ2VuZD1UUlVFKQoKYGBgCgoKCiMgRmlndXJlIE1hbGlnbmFudCBjb21wbGV4IGhlYXRtYXAgY2h1bmsgKHdpdGggNDQtNDcgVEZzLCBsaXRlcmF0dXJlLWJhc2VkIHBhbmVsKQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEyfQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIExJQlJBUklFUwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDEuIERlZmluZSBURiBwYW5lbCBtZXRhZGF0YSAoS0VHRyBBbGlnbmVkKQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnRmX21ldGEgPC0gZGF0YS5mcmFtZSgKICBURiA9IGMoCiAgICAjIC0tLSAxLiBHZW5lcmFsIE1hbGlnbmFuY3kgJiBTdHJlc3MgLS0tCiAgICAiTVlDIiwiRTJGNCIsIlRXSVNUMSIsIklSRjQiLAogICAgIkhTRjEiLCJORkUyTDIiLCJTUkVCRjIiLAogICAgCiAgICAjIC0tLSAyLiBUQ1IgU2lnbmFsaW5nIFRyaWFkIC0tLQogICAgIkpVTkIiLCJGT1MiLCJGT1NMMSIsICAgICAgICAgICAjIEFQLTEKICAgICJORkFUQzEiLCJORkFUQzIiLCAgICAgICAgICAgICAgIyBORkFUCiAgICAiUkVMQSIsIlJFTCIsIk5GS0IxIiwiSVJGMSIsIk5DT0EyIiwgIyBORi1rQgogICAgCiAgICAjIC0tLSAzLiBUaDIgLyBKQUstU1RBVCBDb3JlIC0tLQogICAgIkdBVEEzIiwiU1RBVDYiLCJCQVRGIiwiRk9YUDMiLCJTVEFUMyIsIlNUQVQ1QiIsCiAgICAKICAgICMgLS0tIDQuIERpZmZlcmVudGlhdGlvbiBIaWVyYXJjaHkgLS0tCiAgICAiVENGNyIsIkxFRjEiLCJNWUIiLCAgICAgICAgICAgICMgU3RlbS1saWtlIFByb2dlbml0b3IKICAgICJFMkYxIiwiRk9YTTEiLCAgICAgICAgICAgICAgICAgIyBQcm9saWZlcmF0aW9uIChDeWNsaW5nKQogICAgIlBSRE0xIiwgICAgICAgICAgICAgICAgICAgICAgICAjIFRlcm1pbmFsIEVmZmVjdG9yCiAgICAKICAgICMgLS0tIDUuIEtFR0cgQUxJR05FRCBDQVRFR09SSUVTIChORVcpIC0tLQogICAgIkVPTUVTIiwiVEJYMjEiLCJSVU5YMyIsICAgICAgICAjIE5LLWxpa2UgQ3l0b3RveGljaXR5IChDbHVzdGVyIDEsOSkKICAgICJSRlg1IiwiQ1JFQjEiLCAgICAgICAgICAgICAgICAgIyBBbnRpZ2VuIFByZXNlbnRhdGlvbiAvIE1IQy1JSSAoQ2x1c3RlciAwKQogICAgIktMRjQiLCJFVFMxIiwiU01BRDMiLCAgICAgICAgICAjIE1pZ3JhdGlvbiAvIENlbGwgQWRoZXNpb24gKENBTXMpCiAgICAKICAgICMgLS0tIDYuIE1pY3JvZW52aXJvbm1lbnQgLS0tCiAgICAiU1RBVDEiLCJTVEFUMiIsIklSRjkiLCAgICAgICAgICMgSUZOIFJlc3BvbnNlCiAgICAiSElGMUEiLCJTUkVCRjEiLCAgICAgICAgICAgICAgICMgTWV0YWJvbGlzbQogICAgCiAgICAjIC0tLSA3LiBOb3JtYWwgQmFzZWxpbmUgLyBUdW1vciBTdXBwcmVzc29ycyAtLS0KICAgICJGT1hPMSIsIkZPWE80IiwiWkVCMSIsIkJBQ0gyIiwKICAgICJUQ0YzIiwiQkNMMTFBIiwiTkVVUk9EMSIsIk1FRjJCIiwiUEJYMiIsIklSRjMiLCJCQ0w2IgogICksCiAgQ29uZGl0aW9uID0gYygKICAgIHJlcCgiTWFsaWduYW50IiwgNCksICAgIyBPbmNvZ2VuaWMKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBTdHJlc3MKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIEFQLTEKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAgIyBORkFUCiAgICByZXAoIk1hbGlnbmFudCIsIDUpLCAgICMgTkYta0IKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCA2KSwgICAjIFRoMiAvIEpBSy1TVEFUIENvcmUKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIFN0ZW0tbGlrZSBQcm9nZW5pdG9yCiAgICByZXAoIk1hbGlnbmFudCIsIDIpLCAgICMgUHJvbGlmZXJhdGlvbgogICAgcmVwKCJNYWxpZ25hbnQiLCAxKSwgICAjIFRlcm1pbmFsIEVmZmVjdG9yCiAgICAKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBOSy1saWtlIEN5dG90b3hpY2l0eQogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIEFudGlnZW4gUHJlc2VudGF0aW9uCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgTWlncmF0aW9uIC8gQWRoZXNpb24KICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIElGTgogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIE1ldGFib2xpc20KICAgIAogICAgcmVwKCJOb3JtYWwiLCA0KSwgICAgICAjIFR1bW9yIFN1cHByZXNzb3IKICAgIHJlcCgiTm9ybWFsIiwgNykgICAgICAgIyBIb21lb3N0YXNpcwogICksCiAgRnVuY3Rpb24gPSBjKAogICAgcmVwKCJPbmNvZ2VuaWMiLCA0KSwKICAgIHJlcCgiU3RyZXNzIFJlc3BvbnNlIiwgMyksCiAgICAKICAgIHJlcCgiQVAtMSBTaWduYWxpbmciLCAzKSwKICAgIHJlcCgiTkZBVCBTaWduYWxpbmciLCAyKSwKICAgIHJlcCgiSW5mbGFtbWF0b3J5L05GLWtCIiwgNSksCiAgICAKICAgIHJlcCgiVGgyIC8gSkFLLVNUQVQgQ29yZSIsIDYpLAogICAgCiAgICByZXAoIlN0ZW0tbGlrZSBQcm9nZW5pdG9yIiwgMyksCiAgICByZXAoIlByb2xpZmVyYXRpb24iLCAyKSwKICAgIHJlcCgiVGVybWluYWwgRWZmZWN0b3IiLCAxKSwKICAgIAogICAgcmVwKCJOSy1saWtlIEN5dG90b3hpY2l0eSIsIDMpLCAgICMgS0VHRyBhbGlnbmVkCiAgICByZXAoIkFudGlnZW4gUHJlc2VudGF0aW9uIiwgMiksICAgIyBLRUdHIGFsaWduZWQKICAgIHJlcCgiTWlncmF0aW9uIC8gQWRoZXNpb24iLCAzKSwgICAjIEtFR0cgYWxpZ25lZAogICAgCiAgICByZXAoIklGTiBSZXNwb25zZSIsIDMpLAogICAgcmVwKCJNZXRhYm9saXNtIiwgMiksCiAgICAKICAgIHJlcCgiVHVtb3IgU3VwcHJlc3NvciIsIDQpLAogICAgcmVwKCJOb3JtYWwgSG9tZW9zdGFzaXMiLCA3KQogICksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKCiMgTG9jayBpbiB0aGUgcHJlY2lzZSBvcmRlciBvZiB0aGUgYmxvY2tzIGZyb20gdG9wIHRvIGJvdHRvbQpkZXNpcmVkX29yZGVyIDwtIGMoCiAgIk9uY29nZW5pYyIsIAogICJTdHJlc3MgUmVzcG9uc2UiLAogICJBUC0xIFNpZ25hbGluZyIsIAogICJORkFUIFNpZ25hbGluZyIsIAogICJJbmZsYW1tYXRvcnkvTkYta0IiLAogICJUaDIgLyBKQUstU1RBVCBDb3JlIiwKICAiU3RlbS1saWtlIFByb2dlbml0b3IiLCAKICAiUHJvbGlmZXJhdGlvbiIsICAgICAgICAKICAiVGVybWluYWwgRWZmZWN0b3IiLAogICJOSy1saWtlIEN5dG90b3hpY2l0eSIsICAgIyBQbGFjZWQgaGVyZSB0byBzaG93IGVmZmVjdG9yIHN0YXRlCiAgIkFudGlnZW4gUHJlc2VudGF0aW9uIiwgICAjIERpcmVjdGx5IGxpbmtzIHRvIE1IQy1JSSBoaWdoIGNsdXN0ZXIKICAiTWlncmF0aW9uIC8gQWRoZXNpb24iLCAgICMgTGlua3MgdG8gQ0FNcyBLRUdHIHBhdGh3YXkKICAiSUZOIFJlc3BvbnNlIiwKICAiTWV0YWJvbGlzbSIsCiAgIlR1bW9yIFN1cHByZXNzb3IiLAogICJOb3JtYWwgSG9tZW9zdGFzaXMiCikKCnRmX21ldGEkRnVuY3Rpb24gPC0gZmFjdG9yKHRmX21ldGEkRnVuY3Rpb24sIGxldmVscyA9IGRlc2lyZWRfb3JkZXIpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMi4gRXh0cmFjdCBURiBhY3Rpdml0eSBtYXRyaXggZnJvbSBTZXVyYXQKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQptYXRfc2NhbGVkIDwtIHRyeUNhdGNoKAogIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAic2NhbGUuZGF0YSIpLAogIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTAopCm1hdF9kYXRhIDwtIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAiZGF0YSIpCm1hdF91c2UgPC0gaWYgKCFpcy5udWxsKG1hdF9zY2FsZWQpICYmIG5yb3cobWF0X3NjYWxlZCkgPiAwKSBtYXRfc2NhbGVkIGVsc2UgbWF0X2RhdGEKCiMgRmlsdGVyIGZvciBURnMgcHJlc2VudCBpbiBTZXVyYXQKYXZhaWxhYmxlX3RmcyA8LSBpbnRlcnNlY3QodGZfbWV0YSRURiwgcm93bmFtZXMobWF0X3VzZSkpCm1hdF91c2UgPC0gbWF0X3VzZVthdmFpbGFibGVfdGZzLCAsIGRyb3AgPSBGQUxTRV0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAzLiBBdmVyYWdlIHBlciBjbHVzdGVyICYgei1zY29yZQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmNsdXN0ZXJzIDwtIGFzLmZhY3RvcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykKYXZnX21hdCA8LSBzYXBwbHkobGV2ZWxzKGNsdXN0ZXJzKSwgZnVuY3Rpb24oY2wpIHsKICBNYXRyaXg6OnJvd01lYW5zKG1hdF91c2VbLCBjbHVzdGVycyA9PSBjbCwgZHJvcCA9IEZBTFNFXSkKfSkKY29sbmFtZXMoYXZnX21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQoKYXZnX21hdF96IDwtIHQoc2NhbGUodChhdmdfbWF0KSkpCmF2Z19tYXRfeltpcy5uYShhdmdfbWF0X3opXSA8LSAwCgojIEFsaWduIHRmX21ldGEgb3JkZXIgdG8gbWF0Y2ggbWF0X3VzZSBwcmVjaXNlbHkKcm93X29yZGVyX2lkeCA8LSBtYXRjaChyb3duYW1lcyhhdmdfbWF0X3opLCB0Zl9tZXRhJFRGKQp0Zl9tZXRhX2ZpbHRlcmVkIDwtIHRmX21ldGFbcm93X29yZGVyX2lkeCwgXQphdmdfbWF0X3ogPC0gYXZnX21hdF96W29yZGVyKHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24pLCBdCnRmX21ldGFfZmlsdGVyZWQgPC0gdGZfbWV0YV9maWx0ZXJlZFtvcmRlcih0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uKSwgXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDQuIENvbHVtbiAmIHJvdyBhbm5vdGF0aW9ucwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmNsdXN0ZXJfc3RhdHVzIDwtIGlmZWxzZShjb2xuYW1lcyhhdmdfbWF0X3opICVpbiUgYygiMyIsIjEwIiksICJOb3JtYWwgQ0Q0IFQgY2VsbHMiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIikKaGFfY29sIDwtIEhlYXRtYXBBbm5vdGF0aW9uKAogIENlbGxfU3RhdGUgPSBjbHVzdGVyX3N0YXR1cywKICBjb2wgPSBsaXN0KENlbGxfU3RhdGUgPSBjKCJOb3JtYWwgQ0Q0IFQgY2VsbHMiID0gIiM0REFGNEEiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIiA9ICIjRTQxQTFDIikpLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImxlZnQiCikKCiMgUm93IGFubm90YXRpb24gY29sb3JzCmZ1bmN0aW9uX2NvbG9ycyA8LSBjKAogICJPbmNvZ2VuaWMiID0gIiM4MDgwODAiLAogICJTdHJlc3MgUmVzcG9uc2UiID0gIiNGRkQ3MDAiLAogIAogICJBUC0xIFNpZ25hbGluZyIgPSAiI0ZGOEMwMCIsCiAgIk5GQVQgU2lnbmFsaW5nIiA9ICIjRkY0NTAwIiwKICAiSW5mbGFtbWF0b3J5L05GLWtCIiA9ICIjRTMxQTFDIiwKICAKICAiVGgyIC8gSkFLLVNUQVQgQ29yZSIgPSAiIzk4NEVBMyIsCiAgCiAgIlN0ZW0tbGlrZSBQcm9nZW5pdG9yIiA9ICIjRkYxNDkzIiwKICAiUHJvbGlmZXJhdGlvbiIgPSAiIzFFOTBGRiIsCiAgIlRlcm1pbmFsIEVmZmVjdG9yIiA9ICIjODAwMDgwIiwKICAKICAjIE5ldyBLRUdHIGNhdGVnb3JpZXMKICAiTkstbGlrZSBDeXRvdG94aWNpdHkiID0gIiNGNzgxQkYiLCAgICMgUGluawogICJBbnRpZ2VuIFByZXNlbnRhdGlvbiIgPSAiIzY2Q0RBQSIsICAgIyBNZWRpdW0gQXF1YW1hcmluZQogICJNaWdyYXRpb24gLyBBZGhlc2lvbiIgPSAiIzhBMkJFMiIsICAgIyBCbHVlIFZpb2xldAogIAogICJJRk4gUmVzcG9uc2UiID0gIiMwMENFRDEiLAogICJNZXRhYm9saXNtIiA9ICIjQTY1NjI4IiwKICAKICAiVHVtb3IgU3VwcHJlc3NvciIgPSAiIzM3N0VCOCIsCiAgIk5vcm1hbCBIb21lb3N0YXNpcyIgPSAiIzREQUY0QSIKKQoKaGFfcm93IDwtIHJvd0Fubm90YXRpb24oCiAgQ29uZGl0aW9uID0gdGZfbWV0YV9maWx0ZXJlZCRDb25kaXRpb24sCiAgRnVuY3Rpb24gPSB0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uLAogIGNvbCA9IGxpc3QoCiAgICBDb25kaXRpb24gPSBjKCJOb3JtYWwiPSIjNERBRjRBIiwiTWFsaWduYW50Ij0iI0U0MUExQyIpLAogICAgRnVuY3Rpb24gPSBmdW5jdGlvbl9jb2xvcnMKICApLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImJvdHRvbSIKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDUuIEhlYXRtYXAgY29sb3JzICYgcGxvdHRpbmcKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjb2xfZnVuIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTMsMCwzKSwgYygiIzMxMzY5NSIsIndoaXRlIiwiI0E1MDAyNiIpKQoKaHQgPC0gSGVhdG1hcCgKICBhdmdfbWF0X3osCiAgbmFtZSA9ICJURiBhY3Rpdml0eSAoeikiLAogIGNvbCA9IGNvbF9mdW4sCiAgdG9wX2Fubm90YXRpb24gPSBoYV9jb2wsCiAgbGVmdF9hbm5vdGF0aW9uID0gaGFfcm93LAogIGNvbHVtbl9zcGxpdCA9IGNsdXN0ZXJfc3RhdHVzLAogIHJvd19zcGxpdCA9IHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24sCiAgY2x1c3Rlcl9yb3dfc2xpY2VzID0gRkFMU0UsICAgIAogIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCAgICAgICAgICAKICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwKICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTApLAogIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksCiAgY29sdW1uX3RpdGxlID0gIkZ1bmN0aW9uYWwgVEYgTW9kdWxlcyBpbiBTw6l6YXJ5IFN5bmRyb21lIiwKICByb3dfdGl0bGVfcm90ID0gMCwKICByb3dfdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gOCwgZm9udGZhY2UgPSAiYm9sZCIpLAogIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdChkaXJlY3Rpb24gPSAidmVydGljYWwiKQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNi4gU2F2ZSBwbG90cwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfS0VHRy5wZGYiLCB3aWR0aD0xNCwgaGVpZ2h0PTE0KQpkcmF3KGh0LCBtZXJnZV9sZWdlbmQ9VFJVRSkKZGV2Lm9mZigpCmRyYXcoaHQsIG1lcmdlX2xlZ2VuZD1UUlVFKQpgYGAKCiMgRmlndXJlIE1hbGlnbmFudCBjb21wbGV4IGhlYXRtYXAgY2h1bmsgKHdpdGggNDQtNDcgVEZzLCBsaXRlcmF0dXJlLWJhc2VkIHBhbmVsKQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEyfQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIExJQlJBUklFUwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDEuIERlZmluZSBURiBwYW5lbCBtZXRhZGF0YSAoS0VHRyBBbGlnbmVkKQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnRmX21ldGEgPC0gZGF0YS5mcmFtZSgKICBURiA9IGMoCiAgICAjIC0tLSAxLiBHZW5lcmFsIE1hbGlnbmFuY3kgJiBTdHJlc3MgLS0tCiAgICAiTVlDIiwiRTJGNCIsIlRXSVNUMSIsIklSRjQiLAogICAgIkhTRjEiLCJORkUyTDIiLCJTUkVCRjIiLAogICAgCiAgICAjIC0tLSAyLiBUQ1IgU2lnbmFsaW5nIFRyaWFkIC0tLQogICAgIkpVTkIiLCJGT1MiLCJGT1NMMSIsICAgICAgICAgICAjIEFQLTEKICAgICJORkFUQzEiLCJORkFUQzIiLCAgICAgICAgICAgICAgIyBORkFUCiAgICAiUkVMQSIsIlJFTCIsIk5GS0IxIiwiSVJGMSIsIk5DT0EyIiwgIyBORi1rQgogICAgCiAgICAjIC0tLSAzLiBUaDIgLyBKQUstU1RBVCBDb3JlIC0tLQogICAgIkdBVEEzIiwiU1RBVDYiLCJCQVRGIiwiRk9YUDMiLCJTVEFUMyIsIlNUQVQ1QiIsCiAgICAKICAgICMgLS0tIDQuIERpZmZlcmVudGlhdGlvbiBIaWVyYXJjaHkgLS0tCiAgICAiVENGNyIsIkxFRjEiLCJNWUIiLCAgICAgICAgICAgICMgU3RlbS1saWtlIFByb2dlbml0b3IKICAgICJFMkYxIiwiRk9YTTEiLCAgICAgICAgICAgICAgICAgIyBQcm9saWZlcmF0aW9uIChDeWNsaW5nKQogICAgIlBSRE0xIiwgICAgICAgICAgICAgICAgICAgICAgICAjIFRlcm1pbmFsIEVmZmVjdG9yCiAgICAKICAgICMgLS0tIDUuIEtFR0cgQUxJR05FRCBDQVRFR09SSUVTIChORVcpIC0tLQogICAgIkVPTUVTIiwiVEJYMjEiLCJSVU5YMyIsICAgICAgICAjIE5LLWxpa2UgQ3l0b3RveGljaXR5IChDbHVzdGVyIDEsOSkKICAgICJSRlg1IiwiQ1JFQjEiLCAgICAgICAgICAgICAgICAgIyBBbnRpZ2VuIFByZXNlbnRhdGlvbiAvIE1IQy1JSSAoQ2x1c3RlciAwKQogICAgIktMRjQiLCJFVFMxIiwiU01BRDMiLCAgICAgICAgICAjIE1pZ3JhdGlvbiAvIENlbGwgQWRoZXNpb24gKENBTXMpCiAgICAKICAgICMgLS0tIDYuIE1pY3JvZW52aXJvbm1lbnQgLS0tCiAgICAiU1RBVDEiLCJTVEFUMiIsIklSRjkiLCAgICAgICAgICMgSUZOIFJlc3BvbnNlCiAgICAiSElGMUEiLCJTUkVCRjEiLCAgICAgICAgICAgICAgICMgTWV0YWJvbGlzbQogICAgCiAgICAjIC0tLSA3LiBOb3JtYWwgQmFzZWxpbmUgLyBUdW1vciBTdXBwcmVzc29ycyAtLS0KICAgICJGT1hPMSIsIkZPWE80IiwiWkVCMSIsIkJBQ0gyIiwKICAgICJUQ0YzIiwiQkNMMTFBIiwiTkVVUk9EMSIsIk1FRjJCIiwiUEJYMiIsIklSRjMiLCJCQ0w2IgogICksCiAgQ29uZGl0aW9uID0gYygKICAgIHJlcCgiTWFsaWduYW50IiwgNCksICAgIyBPbmNvZ2VuaWMKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBTdHJlc3MKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIEFQLTEKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAgIyBORkFUCiAgICByZXAoIk1hbGlnbmFudCIsIDUpLCAgICMgTkYta0IKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCA2KSwgICAjIFRoMiAvIEpBSy1TVEFUIENvcmUKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIFN0ZW0tbGlrZSBQcm9nZW5pdG9yCiAgICByZXAoIk1hbGlnbmFudCIsIDIpLCAgICMgUHJvbGlmZXJhdGlvbgogICAgcmVwKCJNYWxpZ25hbnQiLCAxKSwgICAjIFRlcm1pbmFsIEVmZmVjdG9yCiAgICAKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBOSy1saWtlIEN5dG90b3hpY2l0eQogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIEFudGlnZW4gUHJlc2VudGF0aW9uCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgTWlncmF0aW9uIC8gQWRoZXNpb24KICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIElGTgogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIE1ldGFib2xpc20KICAgIAogICAgcmVwKCJOb3JtYWwiLCA0KSwgICAgICAjIFR1bW9yIFN1cHByZXNzb3IKICAgIHJlcCgiTm9ybWFsIiwgNykgICAgICAgIyBIb21lb3N0YXNpcwogICksCiAgRnVuY3Rpb24gPSBjKAogICAgcmVwKCJPbmNvZ2VuaWMiLCA0KSwKICAgIHJlcCgiU3RyZXNzIFJlc3BvbnNlIiwgMyksCiAgICAKICAgIHJlcCgiQVAtMSBTaWduYWxpbmciLCAzKSwKICAgIHJlcCgiTkZBVCBTaWduYWxpbmciLCAyKSwKICAgIHJlcCgiSW5mbGFtbWF0b3J5L05GLWtCIiwgNSksCiAgICAKICAgIHJlcCgiVGgyIC8gSkFLLVNUQVQgQ29yZSIsIDYpLAogICAgCiAgICByZXAoIlN0ZW0tbGlrZSBQcm9nZW5pdG9yIiwgMyksCiAgICByZXAoIlByb2xpZmVyYXRpb24iLCAyKSwKICAgIHJlcCgiVGVybWluYWwgRWZmZWN0b3IiLCAxKSwKICAgIAogICAgcmVwKCJOSy1saWtlIEN5dG90b3hpY2l0eSIsIDMpLCAgICMgS0VHRyBhbGlnbmVkCiAgICByZXAoIkFudGlnZW4gUHJlc2VudGF0aW9uIiwgMiksICAgIyBLRUdHIGFsaWduZWQKICAgIHJlcCgiTWlncmF0aW9uIC8gQWRoZXNpb24iLCAzKSwgICAjIEtFR0cgYWxpZ25lZAogICAgCiAgICByZXAoIklGTiBSZXNwb25zZSIsIDMpLAogICAgcmVwKCJNZXRhYm9saXNtIiwgMiksCiAgICAKICAgIHJlcCgiVHVtb3IgU3VwcHJlc3NvciIsIDQpLAogICAgcmVwKCJOb3JtYWwgSG9tZW9zdGFzaXMiLCA3KQogICksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKCiMgTG9jayBpbiB0aGUgcHJlY2lzZSBvcmRlciBvZiB0aGUgYmxvY2tzIGZyb20gdG9wIHRvIGJvdHRvbQpkZXNpcmVkX29yZGVyIDwtIGMoCiAgIk9uY29nZW5pYyIsIAogICJTdHJlc3MgUmVzcG9uc2UiLAogICJBUC0xIFNpZ25hbGluZyIsIAogICJORkFUIFNpZ25hbGluZyIsIAogICJJbmZsYW1tYXRvcnkvTkYta0IiLAogICJUaDIgLyBKQUstU1RBVCBDb3JlIiwKICAiU3RlbS1saWtlIFByb2dlbml0b3IiLCAKICAiUHJvbGlmZXJhdGlvbiIsICAgICAgICAKICAiVGVybWluYWwgRWZmZWN0b3IiLAogICJOSy1saWtlIEN5dG90b3hpY2l0eSIsICAgIyBQbGFjZWQgaGVyZSB0byBzaG93IGVmZmVjdG9yIHN0YXRlCiAgIkFudGlnZW4gUHJlc2VudGF0aW9uIiwgICAjIERpcmVjdGx5IGxpbmtzIHRvIE1IQy1JSSBoaWdoIGNsdXN0ZXIKICAiTWlncmF0aW9uIC8gQWRoZXNpb24iLCAgICMgTGlua3MgdG8gQ0FNcyBLRUdHIHBhdGh3YXkKICAiSUZOIFJlc3BvbnNlIiwKICAiTWV0YWJvbGlzbSIsCiAgIlR1bW9yIFN1cHByZXNzb3IiLAogICJOb3JtYWwgSG9tZW9zdGFzaXMiCikKCnRmX21ldGEkRnVuY3Rpb24gPC0gZmFjdG9yKHRmX21ldGEkRnVuY3Rpb24sIGxldmVscyA9IGRlc2lyZWRfb3JkZXIpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMi4gRXh0cmFjdCBURiBhY3Rpdml0eSBtYXRyaXggZnJvbSBTZXVyYXQKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQptYXRfc2NhbGVkIDwtIHRyeUNhdGNoKAogIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAic2NhbGUuZGF0YSIpLAogIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTAopCm1hdF9kYXRhIDwtIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAiZGF0YSIpCm1hdF91c2UgPC0gaWYgKCFpcy5udWxsKG1hdF9zY2FsZWQpICYmIG5yb3cobWF0X3NjYWxlZCkgPiAwKSBtYXRfc2NhbGVkIGVsc2UgbWF0X2RhdGEKCiMgRmlsdGVyIGZvciBURnMgcHJlc2VudCBpbiBTZXVyYXQKYXZhaWxhYmxlX3RmcyA8LSBpbnRlcnNlY3QodGZfbWV0YSRURiwgcm93bmFtZXMobWF0X3VzZSkpCm1hdF91c2UgPC0gbWF0X3VzZVthdmFpbGFibGVfdGZzLCAsIGRyb3AgPSBGQUxTRV0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAzLiBBdmVyYWdlIHBlciBjbHVzdGVyICYgei1zY29yZQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmNsdXN0ZXJzIDwtIGFzLmZhY3RvcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykKYXZnX21hdCA8LSBzYXBwbHkobGV2ZWxzKGNsdXN0ZXJzKSwgZnVuY3Rpb24oY2wpIHsKICBNYXRyaXg6OnJvd01lYW5zKG1hdF91c2VbLCBjbHVzdGVycyA9PSBjbCwgZHJvcCA9IEZBTFNFXSkKfSkKY29sbmFtZXMoYXZnX21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQoKYXZnX21hdF96IDwtIHQoc2NhbGUodChhdmdfbWF0KSkpCmF2Z19tYXRfeltpcy5uYShhdmdfbWF0X3opXSA8LSAwCgojIEFsaWduIHRmX21ldGEgb3JkZXIgdG8gbWF0Y2ggbWF0X3VzZSBwcmVjaXNlbHkKcm93X29yZGVyX2lkeCA8LSBtYXRjaChyb3duYW1lcyhhdmdfbWF0X3opLCB0Zl9tZXRhJFRGKQp0Zl9tZXRhX2ZpbHRlcmVkIDwtIHRmX21ldGFbcm93X29yZGVyX2lkeCwgXQphdmdfbWF0X3ogPC0gYXZnX21hdF96W29yZGVyKHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24pLCBdCnRmX21ldGFfZmlsdGVyZWQgPC0gdGZfbWV0YV9maWx0ZXJlZFtvcmRlcih0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uKSwgXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDQuIENvbHVtbiAmIHJvdyBhbm5vdGF0aW9ucwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmNsdXN0ZXJfc3RhdHVzIDwtIGlmZWxzZShjb2xuYW1lcyhhdmdfbWF0X3opICVpbiUgYygiMyIsIjEwIiksICJOb3JtYWwgQ0Q0IFQgY2VsbHMiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIikKaGFfY29sIDwtIEhlYXRtYXBBbm5vdGF0aW9uKAogIENlbGxfU3RhdGUgPSBjbHVzdGVyX3N0YXR1cywKICBjb2wgPSBsaXN0KENlbGxfU3RhdGUgPSBjKCJOb3JtYWwgQ0Q0IFQgY2VsbHMiID0gIiM0REFGNEEiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIiA9ICIjRTQxQTFDIikpLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImxlZnQiCikKCiMgUm93IGFubm90YXRpb24gY29sb3JzCmZ1bmN0aW9uX2NvbG9ycyA8LSBjKAogICJPbmNvZ2VuaWMiID0gIiM4MDgwODAiLAogICJTdHJlc3MgUmVzcG9uc2UiID0gIiNGRkQ3MDAiLAogIAogICJBUC0xIFNpZ25hbGluZyIgPSAiI0ZGOEMwMCIsCiAgIk5GQVQgU2lnbmFsaW5nIiA9ICIjRkY0NTAwIiwKICAiSW5mbGFtbWF0b3J5L05GLWtCIiA9ICIjRTMxQTFDIiwKICAKICAiVGgyIC8gSkFLLVNUQVQgQ29yZSIgPSAiIzk4NEVBMyIsCiAgCiAgIlN0ZW0tbGlrZSBQcm9nZW5pdG9yIiA9ICIjRkYxNDkzIiwKICAiUHJvbGlmZXJhdGlvbiIgPSAiIzFFOTBGRiIsCiAgIlRlcm1pbmFsIEVmZmVjdG9yIiA9ICIjODAwMDgwIiwKICAKICAjIE5ldyBLRUdHIGNhdGVnb3JpZXMKICAiTkstbGlrZSBDeXRvdG94aWNpdHkiID0gIiNGNzgxQkYiLCAgICMgUGluawogICJBbnRpZ2VuIFByZXNlbnRhdGlvbiIgPSAiIzY2Q0RBQSIsICAgIyBNZWRpdW0gQXF1YW1hcmluZQogICJNaWdyYXRpb24gLyBBZGhlc2lvbiIgPSAiIzhBMkJFMiIsICAgIyBCbHVlIFZpb2xldAogIAogICJJRk4gUmVzcG9uc2UiID0gIiMwMENFRDEiLAogICJNZXRhYm9saXNtIiA9ICIjQTY1NjI4IiwKICAKICAiVHVtb3IgU3VwcHJlc3NvciIgPSAiIzM3N0VCOCIsCiAgIk5vcm1hbCBIb21lb3N0YXNpcyIgPSAiIzREQUY0QSIKKQoKaGFfcm93IDwtIHJvd0Fubm90YXRpb24oCiAgQ29uZGl0aW9uID0gdGZfbWV0YV9maWx0ZXJlZCRDb25kaXRpb24sCiAgRnVuY3Rpb24gPSB0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uLAogIGNvbCA9IGxpc3QoCiAgICBDb25kaXRpb24gPSBjKCJOb3JtYWwiPSIjNERBRjRBIiwiTWFsaWduYW50Ij0iI0U0MUExQyIpLAogICAgRnVuY3Rpb24gPSBmdW5jdGlvbl9jb2xvcnMKICApLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImJvdHRvbSIKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDUuIEhlYXRtYXAgY29sb3JzICYgcGxvdHRpbmcKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjb2xfZnVuIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTMsMCwzKSwgYygiIzMxMzY5NSIsIndoaXRlIiwiI0E1MDAyNiIpKQoKaHQgPC0gSGVhdG1hcCgKICBhdmdfbWF0X3osCiAgbmFtZSA9ICJURiBhY3Rpdml0eSAoeikiLAogIGNvbCA9IGNvbF9mdW4sCiAgdG9wX2Fubm90YXRpb24gPSBoYV9jb2wsCiAgbGVmdF9hbm5vdGF0aW9uID0gaGFfcm93LAogIGNvbHVtbl9zcGxpdCA9IGNsdXN0ZXJfc3RhdHVzLAogIHJvd19zcGxpdCA9IHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24sCiAgY2x1c3Rlcl9yb3dfc2xpY2VzID0gRkFMU0UsICAgIAogIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCAgICAgICAgICAKICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwKICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTApLAogIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksCiAgY29sdW1uX3RpdGxlID0gIkZ1bmN0aW9uYWwgVEYgTW9kdWxlcyBpbiBTw6l6YXJ5IFN5bmRyb21lIiwKICByb3dfdGl0bGVfcm90ID0gMCwKICByb3dfdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gOCwgZm9udGZhY2UgPSAiYm9sZCIpLAogIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdChkaXJlY3Rpb24gPSAidmVydGljYWwiKQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNi4gU2F2ZSBwbG90cwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfS0VHRy5wZGYiLCB3aWR0aD0xNCwgaGVpZ2h0PTE0KQpkcmF3KGh0LCBtZXJnZV9sZWdlbmQ9VFJVRSkKZGV2Lm9mZigpCgpkcmF3KGh0LCBtZXJnZV9sZWdlbmQ9VFJVRSkKCmBgYAoKIyBGaWd1cmUgTWFsaWduYW50IGNvbXBsZXggaGVhdG1hcCBjaHVuayAod2l0aCA0NC00NyBURnMsIGxpdGVyYXR1cmUtYmFzZWQgcGFuZWwpCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTJ9CgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgTElCUkFSSUVTCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShTZXVyYXRPYmplY3QpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMS4gRGVmaW5lIFRGIHBhbmVsIG1ldGFkYXRhICgxOjEgVU1BUCBBbGlnbmVkKQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnRmX21ldGEgPC0gZGF0YS5mcmFtZSgKICBURiA9IGMoCiAgICAjIC0tLSBCYXNlbGluZSBNYWxpZ25hbmN5IC0tLSAKICAgICJNWUMiLCJFMkY0IiwiVFdJU1QxIiwiSVJGNCIsCiAgICAKICAgICMgLS0tIENsdXN0ZXJzIDIgJiA2OiBUaDItbGlrZSBDb3JlIC0tLSAKICAgICJHQVRBMyIsIlNUQVQ2IiwiQkFURiIsIkZPWFAzIiwiU1RBVDMiLCJTVEFUNUIiLAogICAgCiAgICAjIC0tLSBDbHVzdGVycyAxMSAmIDEyOiBQcm8taW5mbGFtbWF0b3J5ICYgU3RyZXNzIC0tLSAKICAgICJKVU5CIiwiRk9TIiwiRk9TTDEiLCAgICAgICAgICAgIyBBUC0xCiAgICAiUkVMQSIsIlJFTCIsIk5GS0IxIiwiSVJGMSIsIk5DT0EyIiwgIyBORi1rQgogICAgIk5GQVRDMSIsIk5GQVRDMiIsICAgICAgICAgICAgICAjIFRDUi9ORkFUCiAgICAiSFNGMSIsIk5GRTJMMiIsIlNSRUJGMiIsICAgICAgICMgU3RyZXNzCiAgICAKICAgICMgLS0tIENsdXN0ZXIgNDogSW5mbGFtbWF0b3J5LU1pZ3JhdG9yeSAtLS0KICAgICJLTEY0IiwiRVRTMSIsIlNNQUQzIiwKICAgIAogICAgIyAtLS0gQ2x1c3RlciA1OiBTdGVtLWxpa2UgLS0tIAogICAgIlRDRjciLCJMRUYxIiwiTVlCIiwgICAgICAgICAgICAKICAgIAogICAgIyAtLS0gQ2x1c3RlciA3OiBDeWNsaW5nIChHMi9NKSAtLS0gCiAgICAiRTJGMSIsIkZPWE0xIiwgICAgICAgICAgICAgICAgIAogICAgCiAgICAjIC0tLSBDbHVzdGVycyAxICYgOTogTkstbGlrZSAvIEN5dG90b3hpYyAtLS0gCiAgICAiRU9NRVMiLCJUQlgyMSIsIlJVTlgzIiwiUFJETTEiLCAgICAgICAgCiAgICAKICAgICMgLS0tIENsdXN0ZXIgMDogTUhDLUlJIEhpZ2ggLS0tIAogICAgIlJGWDUiLCJDUkVCMSIsICAgICAgICAgICAgICAgICAKICAgIAogICAgIyAtLS0gQ2x1c3RlciAxMzogSUZOIFN0aW11bGF0ZWQgLS0tIAogICAgIlNUQVQxIiwiU1RBVDIiLCJJUkY5IiwgICAgICAgICAKICAgIAogICAgIyAtLS0gQ2x1c3RlciA4OiBHbHljb2x5dGljL01ldGFib2xpYyAtLS0gCiAgICAiSElGMUEiLCJTUkVCRjEiLCAgICAgICAgICAgICAgIAogICAgCiAgICAjIC0tLSBDbHVzdGVycyAzICYgMTA6IE5vcm1hbCBDRDQgVCAtLS0gCiAgICAiRk9YTzEiLCJGT1hPNCIsIlpFQjEiLCJCQUNIMiIsCiAgICAiVENGMyIsIkJDTDExQSIsIk5FVVJPRDEiLCJNRUYyQiIsIlBCWDIiLCJJUkYzIiwiQkNMNiIKICApLAogIENvbmRpdGlvbiA9IGMoCiAgICByZXAoIk1hbGlnbmFudCIsIDQpLCAgICMgT25jb2dlbmljCiAgICByZXAoIk1hbGlnbmFudCIsIDYpLCAgICMgVGgyCiAgICByZXAoIk1hbGlnbmFudCIsIDEzKSwgICMgUHJvLWluZmxhbW1hdG9yeSAoQVAxLCBORmtCLCBORkFULCBTdHJlc3MpCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgTWlncmF0b3J5CiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgU3RlbS1saWtlCiAgICByZXAoIk1hbGlnbmFudCIsIDIpLCAgICMgQ3ljbGluZwogICAgcmVwKCJNYWxpZ25hbnQiLCA0KSwgICAjIE5LL0N5dG90b3hpYwogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIE1IQy1JSQogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIElGTgogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIEdseWNvbHl0aWMKICAgIHJlcCgiTm9ybWFsIiwgMTEpICAgICAgIyBOb3JtYWwKICApLAogIEZ1bmN0aW9uID0gYygKICAgIHJlcCgiT25jb2dlbmljIENvcmUiLCA0KSwKICAgIHJlcCgiVGgyLWxpa2UgQ29yZSAoQ2wuIDIsIDYpIiwgNiksCiAgICByZXAoIlByby1pbmZsYW1tYXRvcnkgKENsLiAxMSwgMTIpIiwgMTMpLAogICAgcmVwKCJJbmZsYW1tYXRvcnktTWlncmF0b3J5IChDbC4gNCkiLCAzKSwKICAgIHJlcCgiU3RlbS1saWtlIChDbC4gNSkiLCAzKSwKICAgIHJlcCgiQ3ljbGluZyBHMi9NIChDbC4gNykiLCAyKSwKICAgIHJlcCgiTkstbGlrZSBDeXRvdG94aWMgKENsLiAxLCA5KSIsIDQpLCAgIAogICAgcmVwKCJNSEMtSUkgSGlnaCAoQ2wuIDApIiwgMiksICAgCiAgICByZXAoIklGTiBTdGltdWxhdGVkIChDbC4gMTMpIiwgMyksCiAgICByZXAoIkdseWNvbHl0aWMvTWV0YWJvbGljIChDbC4gOCkiLCAyKSwKICAgIHJlcCgiTm9ybWFsIEhvbWVvc3Rhc2lzIChDbC4gMywgMTApIiwgMTEpCiAgKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQoKIyBMb2NrIGluIHRoZSBwcmVjaXNlIG9yZGVyIG9mIHRoZSBibG9ja3MKZGVzaXJlZF9vcmRlciA8LSBjKAogICJPbmNvZ2VuaWMgQ29yZSIsIAogICJUaDItbGlrZSBDb3JlIChDbC4gMiwgNikiLAogICJQcm8taW5mbGFtbWF0b3J5IChDbC4gMTEsIDEyKSIsIAogICJJbmZsYW1tYXRvcnktTWlncmF0b3J5IChDbC4gNCkiLCAKICAiU3RlbS1saWtlIChDbC4gNSkiLCAKICAiQ3ljbGluZyBHMi9NIChDbC4gNykiLCAgICAgICAgCiAgIk5LLWxpa2UgQ3l0b3RveGljIChDbC4gMSwgOSkiLCAgIAogICJNSEMtSUkgSGlnaCAoQ2wuIDApIiwgICAKICAiSUZOIFN0aW11bGF0ZWQgKENsLiAxMykiLAogICJHbHljb2x5dGljL01ldGFib2xpYyAoQ2wuIDgpIiwKICAiTm9ybWFsIEhvbWVvc3Rhc2lzIChDbC4gMywgMTApIgopCgp0Zl9tZXRhJEZ1bmN0aW9uIDwtIGZhY3Rvcih0Zl9tZXRhJEZ1bmN0aW9uLCBsZXZlbHMgPSBkZXNpcmVkX29yZGVyKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDIuIEV4dHJhY3QgVEYgYWN0aXZpdHkgbWF0cml4IGZyb20gU2V1cmF0CiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KbWF0X3NjYWxlZCA8LSB0cnlDYXRjaChTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheSA9ICJkb3JvdGhlYSIsIGxheWVyID0gInNjYWxlLmRhdGEiKSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQptYXRfZGF0YSA8LSBTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheSA9ICJkb3JvdGhlYSIsIGxheWVyID0gImRhdGEiKQptYXRfdXNlIDwtIGlmICghaXMubnVsbChtYXRfc2NhbGVkKSAmJiBucm93KG1hdF9zY2FsZWQpID4gMCkgbWF0X3NjYWxlZCBlbHNlIG1hdF9kYXRhCgphdmFpbGFibGVfdGZzIDwtIGludGVyc2VjdCh0Zl9tZXRhJFRGLCByb3duYW1lcyhtYXRfdXNlKSkKbWF0X3VzZSA8LSBtYXRfdXNlW2F2YWlsYWJsZV90ZnMsICwgZHJvcCA9IEZBTFNFXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDMuIEF2ZXJhZ2UgcGVyIGNsdXN0ZXIgJiB6LXNjb3JlCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY2x1c3RlcnMgPC0gYXMuZmFjdG9yKHNldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzKQphdmdfbWF0IDwtIHNhcHBseShsZXZlbHMoY2x1c3RlcnMpLCBmdW5jdGlvbihjbCkgTWF0cml4Ojpyb3dNZWFucyhtYXRfdXNlWywgY2x1c3RlcnMgPT0gY2wsIGRyb3AgPSBGQUxTRV0pKQpjb2xuYW1lcyhhdmdfbWF0KSA8LSBsZXZlbHMoY2x1c3RlcnMpCgphdmdfbWF0X3ogPC0gdChzY2FsZSh0KGF2Z19tYXQpKSkKYXZnX21hdF96W2lzLm5hKGF2Z19tYXRfeildIDwtIDAKCiMgQWxpZ24gdGZfbWV0YSBvcmRlciB0byBtYXRjaCBtYXRfdXNlIHByZWNpc2VseQpyb3dfb3JkZXJfaWR4IDwtIG1hdGNoKHJvd25hbWVzKGF2Z19tYXRfeiksIHRmX21ldGEkVEYpCnRmX21ldGFfZmlsdGVyZWQgPC0gdGZfbWV0YVtyb3dfb3JkZXJfaWR4LCBdCmF2Z19tYXRfeiA8LSBhdmdfbWF0X3pbb3JkZXIodGZfbWV0YV9maWx0ZXJlZCRGdW5jdGlvbiksIF0KdGZfbWV0YV9maWx0ZXJlZCA8LSB0Zl9tZXRhX2ZpbHRlcmVkW29yZGVyKHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24pLCBdCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNC4gQ29sdW1uICYgcm93IGFubm90YXRpb25zCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY2x1c3Rlcl9zdGF0dXMgPC0gaWZlbHNlKGNvbG5hbWVzKGF2Z19tYXRfeikgJWluJSBjKCIzIiwiMTAiKSwgIk5vcm1hbCBDRDQgVCBjZWxscyIsICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMiKQpoYV9jb2wgPC0gSGVhdG1hcEFubm90YXRpb24oQ2VsbF9TdGF0ZSA9IGNsdXN0ZXJfc3RhdHVzLCBjb2wgPSBsaXN0KENlbGxfU3RhdGUgPSBjKCJOb3JtYWwgQ0Q0IFQgY2VsbHMiID0gIiM0REFGNEEiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIiA9ICIjRTQxQTFDIikpLCBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJsZWZ0IikKCiMgSGFybW9uaXplZCBjb2xvciBwYWxldHRlCmZ1bmN0aW9uX2NvbG9ycyA8LSBjKAogICJPbmNvZ2VuaWMgQ29yZSIgPSAiIzgwODA4MCIsCiAgIlRoMi1saWtlIENvcmUgKENsLiAyLCA2KSIgPSAiIzk4NEVBMyIsCiAgIlByby1pbmZsYW1tYXRvcnkgKENsLiAxMSwgMTIpIiA9ICIjRTMxQTFDIiwKICAiSW5mbGFtbWF0b3J5LU1pZ3JhdG9yeSAoQ2wuIDQpIiA9ICIjOEEyQkUyIiwKICAiU3RlbS1saWtlIChDbC4gNSkiID0gIiNGRjE0OTMiLAogICJDeWNsaW5nIEcyL00gKENsLiA3KSIgPSAiIzFFOTBGRiIsCiAgIk5LLWxpa2UgQ3l0b3RveGljIChDbC4gMSwgOSkiID0gIiNGNzgxQkYiLCAgIAogICJNSEMtSUkgSGlnaCAoQ2wuIDApIiA9ICIjNjZDREFBIiwgICAKICAiSUZOIFN0aW11bGF0ZWQgKENsLiAxMykiID0gIiMwMENFRDEiLAogICJHbHljb2x5dGljL01ldGFib2xpYyAoQ2wuIDgpIiA9ICIjQTY1NjI4IiwKICAiTm9ybWFsIEhvbWVvc3Rhc2lzIChDbC4gMywgMTApIiA9ICIjNERBRjRBIgopCgpoYV9yb3cgPC0gcm93QW5ub3RhdGlvbihDb25kaXRpb24gPSB0Zl9tZXRhX2ZpbHRlcmVkJENvbmRpdGlvbiwgRnVuY3Rpb24gPSB0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uLCBjb2wgPSBsaXN0KENvbmRpdGlvbiA9IGMoIk5vcm1hbCI9IiM0REFGNEEiLCJNYWxpZ25hbnQiPSIjRTQxQTFDIiksIEZ1bmN0aW9uID0gZnVuY3Rpb25fY29sb3JzKSwgYW5ub3RhdGlvbl9uYW1lX3NpZGUgPSAiYm90dG9tIikKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA1LiBIZWF0bWFwIGNvbG9ycyAmIHBsb3R0aW5nCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC0zLDAsMyksIGMoIiMzMTM2OTUiLCJ3aGl0ZSIsIiNBNTAwMjYiKSkKCmh0IDwtIEhlYXRtYXAoCiAgYXZnX21hdF96LCAKICBuYW1lID0gIlRGIGFjdGl2aXR5ICh6KSIsIAogIGNvbCA9IGNvbF9mdW4sIAogIHRvcF9hbm5vdGF0aW9uID0gaGFfY29sLCAKICBsZWZ0X2Fubm90YXRpb24gPSBoYV9yb3csIAogIGNvbHVtbl9zcGxpdCA9IGNsdXN0ZXJfc3RhdHVzLCAKICByb3dfc3BsaXQgPSB0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uLCAKICBjbHVzdGVyX3Jvd19zbGljZXMgPSBGQUxTRSwgCiAgY2x1c3Rlcl9yb3dzID0gRkFMU0UsIAogIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsIAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwgCiAgc2hvd19jb2x1bW5fZGVuZCA9IFRSVUUsIAogIHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksIAogIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksIAogIGNvbHVtbl90aXRsZSA9ICJSZWd1bGF0b3J5IERyaXZlcnMgb2YgU8OpemFyeSBVTUFQIENlbGwgU3RhdGVzIiwgCiAgcm93X3RpdGxlX3JvdCA9IDAsIAogIHJvd190aXRsZV9ncCA9IGdwYXIoZm9udHNpemUgPSA5LCBmb250ZmFjZSA9ICJib2xkIiksIAogIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdChkaXJlY3Rpb24gPSAidmVydGljYWwiKQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNi4gU2F2ZSBwbG90cwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfVU1BUF9GaW5hbC5wZGYiLCB3aWR0aD0xNSwgaGVpZ2h0PTEzKQpkcmF3KGh0LCBtZXJnZV9sZWdlbmQ9VFJVRSkKZGV2Lm9mZigpCgpkcmF3KGh0LCBtZXJnZV9sZWdlbmQ9VFJVRSkKYGBgCgoKCgoKCgoKCiMgVEVTVApgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgTElCUkFSSUVTCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShTZXVyYXRPYmplY3QpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMS4gRGVmaW5lIExpdGVyYXR1cmUtVmFsaWRhdGVkIFRGIHBhbmVsIChFeGhhdXN0aXZlKQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnRmX21ldGFfbGl0IDwtIGRhdGEuZnJhbWUoCiAgVEYgPSBjKAogICAgIyBPbmNvZ2VuaWMgCiAgICAiTVlDIiwgIlRXSVNUMSIsICJJUkY0IiwKICAgIAogICAgIyBUaDIgQ29yZQogICAgIkdBVEEzIiwgIkJBVEYiLCAiRk9YUDMiLCAiU1RBVDMiLCAiU1RBVDVCIiwKICAgIAogICAgIyBIeXBlcmFjdGl2ZSBUQ1IgLyBJbmZsYW1tYXRvcnkKICAgICJKVU5CIiwgIk5GQVRDMSIsICJORkFUQzIiLCAiUkVMQSIsICJORktCMSIsCiAgICAKICAgICMgQ2Fub25pY2FsIFR1bW9yIFN1cHByZXNzb3JzCiAgICAiWkVCMSIsICJCQUNIMiIsICJGT1hPMSIsICJGT1hPMyIKICApLAogIEZ1bmN0aW9uID0gYygKICAgIHJlcCgiT25jb2dlbmljICIsIDMpLAogICAgcmVwKCJUaDIgQ29yZSIsIDUpLAogICAgcmVwKCJIeXBlcmFjdGl2ZSBUQ1IgLyBJbmZsYW1tYXRvcnkiLCA1KSwKICAgIHJlcCgiQ2Fub25pY2FsIFR1bW9yIFN1cHByZXNzb3JzIiwgNCkKICApLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopCgojIExvY2sgaW4gdGhlIG9yZGVyIHRvcC10by1ib3R0b20KdGZfbWV0YV9saXQkRnVuY3Rpb24gPC0gZmFjdG9yKHRmX21ldGFfbGl0JEZ1bmN0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk9uY29nZW5pYyAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVGgyIENvcmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSHlwZXJhY3RpdmUgVENSIC8gSW5mbGFtbWF0b3J5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNhbm9uaWNhbCBUdW1vciBTdXBwcmVzc29ycyIpKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDIuIEV4dHJhY3QgVEYgYWN0aXZpdHkgbWF0cml4CiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KbWF0X3NjYWxlZCA8LSB0cnlDYXRjaChTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheSA9ICJkb3JvdGhlYSIsIGxheWVyID0gInNjYWxlLmRhdGEiKSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQptYXRfZGF0YSA8LSBTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheSA9ICJkb3JvdGhlYSIsIGxheWVyID0gImRhdGEiKQptYXRfdXNlIDwtIGlmICghaXMubnVsbChtYXRfc2NhbGVkKSAmJiBucm93KG1hdF9zY2FsZWQpID4gMCkgbWF0X3NjYWxlZCBlbHNlIG1hdF9kYXRhCgojIEZpbHRlciBmb3IgYXZhaWxhYmxlIFRGcwphdmFpbGFibGVfdGZzIDwtIGludGVyc2VjdCh0Zl9tZXRhX2xpdCRURiwgcm93bmFtZXMobWF0X3VzZSkpCm1hdF91c2UgPC0gbWF0X3VzZVthdmFpbGFibGVfdGZzLCAsIGRyb3AgPSBGQUxTRV0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAzLiBBdmVyYWdlIHBlciBjbHVzdGVyICYgei1zY29yZQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmNsdXN0ZXJzIDwtIGFzLmZhY3RvcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykKYXZnX21hdCA8LSBzYXBwbHkobGV2ZWxzKGNsdXN0ZXJzKSwgZnVuY3Rpb24oY2wpIE1hdHJpeDo6cm93TWVhbnMobWF0X3VzZVssIGNsdXN0ZXJzID09IGNsLCBkcm9wID0gRkFMU0VdKSkKY29sbmFtZXMoYXZnX21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQoKYXZnX21hdF96IDwtIHQoc2NhbGUodChhdmdfbWF0KSkpCmF2Z19tYXRfeltpcy5uYShhdmdfbWF0X3opXSA8LSAwCgojIEFsaWduIHRvIG91ciBkZWZpbmVkIGxpdGVyYXR1cmUgbGlzdCBvcmRlcgpyb3dfb3JkZXJfaWR4IDwtIG1hdGNoKHJvd25hbWVzKGF2Z19tYXRfeiksIHRmX21ldGFfbGl0JFRGKQp0Zl9tZXRhX2xpdF9maWx0ZXJlZCA8LSB0Zl9tZXRhX2xpdFtyb3dfb3JkZXJfaWR4LCBdCmF2Z19tYXRfeiA8LSBhdmdfbWF0X3pbb3JkZXIodGZfbWV0YV9saXRfZmlsdGVyZWQkRnVuY3Rpb24pLCBdCnRmX21ldGFfbGl0X2ZpbHRlcmVkIDwtIHRmX21ldGFfbGl0X2ZpbHRlcmVkW29yZGVyKHRmX21ldGFfbGl0X2ZpbHRlcmVkJEZ1bmN0aW9uKSwgXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDQuIENvbHVtbiAmIHJvdyBhbm5vdGF0aW9ucwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmNsdXN0ZXJfc3RhdHVzIDwtIGlmZWxzZShjb2xuYW1lcyhhdmdfbWF0X3opICVpbiUgYygiMyIsIjEwIiksICJOb3JtYWwgQ0Q0IFQgY2VsbHMiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIikKaGFfY29sIDwtIEhlYXRtYXBBbm5vdGF0aW9uKAogIENlbGxfU3RhdGUgPSBjbHVzdGVyX3N0YXR1cywgCiAgY29sID0gbGlzdChDZWxsX1N0YXRlID0gYygiTm9ybWFsIENENCBUIGNlbGxzIiA9ICIjNERBRjRBIiwgIk1hbGlnbmFudCBDRDQgVCBjZWxscyIgPSAiI0U0MUExQyIpKSwgCiAgYW5ub3RhdGlvbl9uYW1lX3NpZGUgPSAibGVmdCIKKQoKIyBDb2xvcnMgbWF0Y2hpbmcgdGhlIGxpdGVyYXR1cmUgY2F0ZWdvcmllcwpyb2xlX2NvbG9ycyA8LSBjKAogICJPbmNvZ2VuaWMgIiA9ICIjODA4MDgwIiwgICMgR3JheQogICJUaDIgQ29yZSIgPSAiIzk4NEVBMyIsICAgICAgICAgICAgICAgICAgICMgUHVycGxlCiAgIkh5cGVyYWN0aXZlIFRDUiAvIEluZmxhbW1hdG9yeSIgPSAiI0UzMUExQyIsICAgICMgUmVkCiAgIkNhbm9uaWNhbCBUdW1vciBTdXBwcmVzc29ycyIgPSAiIzM3N0VCOCIgICAgICAgICMgQmx1ZQopCgpoYV9yb3cgPC0gcm93QW5ub3RhdGlvbigKICBGdW5jdGlvbiA9IHRmX21ldGFfbGl0X2ZpbHRlcmVkJEZ1bmN0aW9uLCAKICBjb2wgPSBsaXN0KEZ1bmN0aW9uID0gcm9sZV9jb2xvcnMpLCAKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJib3R0b20iCikKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA1LiBIZWF0bWFwIGNvbG9ycyAmIHBsb3R0aW5nCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC0zLDAsMyksIGMoIiMzMTM2OTUiLCJ3aGl0ZSIsIiNBNTAwMjYiKSkKCmh0X2xpdCA8LSBIZWF0bWFwKAogIGF2Z19tYXRfeiwgCiAgbmFtZSA9ICJURiBhY3Rpdml0eSAoeikiLCAKICBjb2wgPSBjb2xfZnVuLCAKICB0b3BfYW5ub3RhdGlvbiA9IGhhX2NvbCwgCiAgbGVmdF9hbm5vdGF0aW9uID0gaGFfcm93LCAKICBjb2x1bW5fc3BsaXQgPSBjbHVzdGVyX3N0YXR1cywgCiAgcm93X3NwbGl0ID0gdGZfbWV0YV9saXRfZmlsdGVyZWQkRnVuY3Rpb24sIAogIGNsdXN0ZXJfcm93X3NsaWNlcyA9IEZBTFNFLCAKICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwgCiAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwgCiAgc2hvd19yb3dfZGVuZCA9IEZBTFNFLCAKICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwgCiAgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDEyLCBmb250ZmFjZSA9ICJib2xkIiksIAogIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMiksIAogIGNvbHVtbl90aXRsZSA9ICJMaXRlcmF0dXJlLVZhbGlkYXRlZCBTw6l6YXJ5IFN5bmRyb21lIFJlZ3VsYXRvcnMiLCAKICByb3dfdGl0bGVfcm90ID0gMCwgCiAgcm93X3RpdGxlX2dwID0gZ3Bhcihmb250c2l6ZSA9IDEwLCBmb250ZmFjZSA9ICJib2xkIiksIAogIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdChkaXJlY3Rpb24gPSAidmVydGljYWwiKQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNi4gU2F2ZSBwbG90cwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfTGl0ZXJhdHVyZV9WYWxpZGF0ZWQucGRmIiwgd2lkdGg9MTIsIGhlaWdodD04KQpkcmF3KGh0X2xpdCwgbWVyZ2VfbGVnZW5kPVRSVUUpCmRldi5vZmYoKQoKcG5nKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfVEZfSGVhdG1hcF9MaXRlcmF0dXJlX1ZhbGlkYXRlZC5wbmciLCB3aWR0aD0xMiozMDAsIGhlaWdodD04KjMwMCwgcmVzPTMwMCkKZHJhdyhodF9saXQsIG1lcmdlX2xlZ2VuZD1UUlVFKQpkZXYub2ZmKCkKCmRyYXcoaHRfbGl0LCBtZXJnZV9sZWdlbmQ9VFJVRSkKYGBgCgoKCgoKIyBEZWZpbmUgVGgxL1RoMi9UaDE3L1RoMjIvVHJlZyBNYXN0ZXIgUmVndWxhdG9yIFBhbmVsCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBMSUJSQVJJRVMKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShncmlkKQpsaWJyYXJ5KFNldXJhdE9iamVjdCkKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAxLiBEZWZpbmUgVGgxL1RoMi9UaDE3L1RoMjIvVHJlZyBNYXN0ZXIgUmVndWxhdG9yIFBhbmVsCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KdGZfbWV0YV90aGVscGVyIDwtIGRhdGEuZnJhbWUoCiAgVEYgPSBjKAogICAgIyBUaDEgTWFzdGVyIFJlZ3VsYXRvcnMKICAgICJUQlgyMSIsICJTVEFUMSIsICJTVEFUNCIsICJJUkYxIiwKICAgIAogICAgIyBUaDIgTWFzdGVyIFJlZ3VsYXRvcnMgIAogICAgIkdBVEEzIiwgIlNUQVQ2IiwgIkJBVEYiLCAiSVJGNCIsCiAgICAKICAgICMgVGgxNyAvIFRoMjIgTWFzdGVyIFJlZ3VsYXRvcnMKICAgICJST1JDIiwgIlNUQVQzIiwgIkFIUiIsICJNQUYiLAogICAgCiAgICAjIFRyZWcgTWFzdGVyIFJlZ3VsYXRvcnMKICAgICJGT1hQMyIsICJGT1hPMSIsICJDVExBNCIgICMgQ1RMQTQgcmVndWxvbiBhcyBUcmVnIHByb3h5CiAgKSwKICBGdW5jdGlvbiA9IGMoCiAgICByZXAoIlRoMSIsIDQpLAogICAgcmVwKCJUaDIiLCA0KSwKICAgIHJlcCgiVGgxNy9UaDIyIiwgNCksCiAgICByZXAoIlRyZWciLCAzKQogICksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKCiMgTG9jayBpbiBjYW5vbmljYWwgb3JkZXI6IFRoMSDihpIgVGgyIOKGkiBUaDE3L1RoMjIg4oaSIFRyZWcKdGZfbWV0YV90aGVscGVyJEZ1bmN0aW9uIDwtIGZhY3Rvcih0Zl9tZXRhX3RoZWxwZXIkRnVuY3Rpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIlRoMSIsICJUaDIiLCAiVGgxNy9UaDIyIiwgIlRyZWciKSkKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAyLiBFeHRyYWN0IFRGIGFjdGl2aXR5IG1hdHJpeAojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Cm1hdF9zY2FsZWQgPC0gdHJ5Q2F0Y2goU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJzY2FsZS5kYXRhIiksIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTCkKbWF0X2RhdGEgPC0gU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJkYXRhIikKbWF0X3VzZSA8LSBpZiAoIWlzLm51bGwobWF0X3NjYWxlZCkgJiYgbnJvdyhtYXRfc2NhbGVkKSA+IDApIG1hdF9zY2FsZWQgZWxzZSBtYXRfZGF0YQoKIyBGaWx0ZXIgZm9yIGF2YWlsYWJsZSBURnMKYXZhaWxhYmxlX3RmcyA8LSBpbnRlcnNlY3QodGZfbWV0YV90aGVscGVyJFRGLCByb3duYW1lcyhtYXRfdXNlKSkKbWF0X3VzZSA8LSBtYXRfdXNlW2F2YWlsYWJsZV90ZnMsICwgZHJvcCA9IEZBTFNFXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDMuIEF2ZXJhZ2UgcGVyIGNsdXN0ZXIgJiB6LXNjb3JlCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY2x1c3RlcnMgPC0gYXMuZmFjdG9yKHNldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzKQphdmdfbWF0IDwtIHNhcHBseShsZXZlbHMoY2x1c3RlcnMpLCBmdW5jdGlvbihjbCkgTWF0cml4Ojpyb3dNZWFucyhtYXRfdXNlWywgY2x1c3RlcnMgPT0gY2wsIGRyb3AgPSBGQUxTRV0pKQpjb2xuYW1lcyhhdmdfbWF0KSA8LSBsZXZlbHMoY2x1c3RlcnMpCgphdmdfbWF0X3ogPC0gdChzY2FsZSh0KGF2Z19tYXQpKSkKYXZnX21hdF96W2lzLm5hKGF2Z19tYXRfeildIDwtIDAKCiMgQWxpZ24gdG8gb3VyIGRlZmluZWQgaGVscGVyIFQgcGFuZWwgb3JkZXIKcm93X29yZGVyX2lkeCA8LSBtYXRjaChyb3duYW1lcyhhdmdfbWF0X3opLCB0Zl9tZXRhX3RoZWxwZXIkVEYpCnRmX21ldGFfdGhlbHBlcl9maWx0ZXJlZCA8LSB0Zl9tZXRhX3RoZWxwZXJbcm93X29yZGVyX2lkeCwgXQphdmdfbWF0X3ogPC0gYXZnX21hdF96W29yZGVyKHRmX21ldGFfdGhlbHBlcl9maWx0ZXJlZCRGdW5jdGlvbiksIF0KdGZfbWV0YV90aGVscGVyX2ZpbHRlcmVkIDwtIHRmX21ldGFfdGhlbHBlcl9maWx0ZXJlZFtvcmRlcih0Zl9tZXRhX3RoZWxwZXJfZmlsdGVyZWQkRnVuY3Rpb24pLCBdCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNC4gQ29sdW1uICYgcm93IGFubm90YXRpb25zCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY2x1c3Rlcl9zdGF0dXMgPC0gaWZlbHNlKGNvbG5hbWVzKGF2Z19tYXRfeikgJWluJSBjKCIzIiwiMTAiKSwgIk5vcm1hbCBDRDQgVCBjZWxscyIsICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMiKQpoYV9jb2wgPC0gSGVhdG1hcEFubm90YXRpb24oCiAgQ2VsbF9TdGF0ZSA9IGNsdXN0ZXJfc3RhdHVzLCAKICBjb2wgPSBsaXN0KENlbGxfU3RhdGUgPSBjKCJOb3JtYWwgQ0Q0IFQgY2VsbHMiID0gIiM0REFGNEEiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIiA9ICIjRTQxQTFDIikpLCAKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJsZWZ0IgopCgojIENsYXNzaWNhbCBUIGhlbHBlciBjb2xvciBzY2hlbWUKaGVscGVyX2NvbG9ycyA8LSBjKAogICJUaDEiID0gIiNFMzFBMUMiLCAgICAgICMgUmVkCiAgIlRoMiIgPSAiIzFGNzhCNCIsICAgICAgIyBCbHVlICAKICAiVGgxNy9UaDIyIiA9ICIjRkY3RjAwIiwgIyBPcmFuZ2UKICAiVHJlZyIgPSAiIzMzQTAyQyIgICAgICAjIEdyZWVuCikKCmhhX3JvdyA8LSByb3dBbm5vdGF0aW9uKAogIEZ1bmN0aW9uID0gdGZfbWV0YV90aGVscGVyX2ZpbHRlcmVkJEZ1bmN0aW9uLCAKICBjb2wgPSBsaXN0KEZ1bmN0aW9uID0gaGVscGVyX2NvbG9ycyksIAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImJvdHRvbSIKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDUuIEhlYXRtYXAgY29sb3JzICYgcGxvdHRpbmcKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjb2xfZnVuIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTMsMCwzKSwgYygiIzMxMzY5NSIsIndoaXRlIiwiI0E1MDAyNiIpKQoKaHRfdGhlbHBlciA8LSBIZWF0bWFwKAogIGF2Z19tYXRfeiwgCiAgbmFtZSA9ICJURiBhY3Rpdml0eSAoeikiLCAKICBjb2wgPSBjb2xfZnVuLCAKICB0b3BfYW5ub3RhdGlvbiA9IGhhX2NvbCwgCiAgbGVmdF9hbm5vdGF0aW9uID0gaGFfcm93LCAKICBjb2x1bW5fc3BsaXQgPSBjbHVzdGVyX3N0YXR1cywgCiAgcm93X3NwbGl0ID0gdGZfbWV0YV90aGVscGVyX2ZpbHRlcmVkJEZ1bmN0aW9uLCAKICBjbHVzdGVyX3Jvd19zbGljZXMgPSBGQUxTRSwgCiAgY2x1c3Rlcl9yb3dzID0gRkFMU0UsIAogIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsIAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwgCiAgc2hvd19jb2x1bW5fZGVuZCA9IFRSVUUsIAogIHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMSwgZm9udGZhY2UgPSAiYm9sZCIpLCAKICBjb2x1bW5fbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTEpLCAKICBjb2x1bW5fdGl0bGUgPSAiQ0Q0KyBUIEhlbHBlciBMaW5lYWdlIFRyYW5zY3JpcHRpb24gRmFjdG9ycyIsIAogIHJvd190aXRsZV9yb3QgPSAwLCAKICByb3dfdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gMTAsIGZvbnRmYWNlID0gImJvbGQiKSwgCiAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KGRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpCikKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA2LiBTYXZlIHBsb3RzCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KcGRmKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfVEZfSGVhdG1hcF9USGVscGVyX0xpbmVhZ2VzLnBkZiIsIHdpZHRoPTEyLCBoZWlnaHQ9OCkKZHJhdyhodF90aGVscGVyLCBtZXJnZV9sZWdlbmQ9VFJVRSkKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV9URl9IZWF0bWFwX1RIZWxwZXJfTGluZWFnZXMucG5nIiwgd2lkdGg9MTIqMzAwLCBoZWlnaHQ9OCozMDAsIHJlcz0zMDApCmRyYXcoaHRfdGhlbHBlciwgbWVyZ2VfbGVnZW5kPVRSVUUpCmRldi5vZmYoKQoKZHJhdyhodF90aGVscGVyLCBtZXJnZV9sZWdlbmQ9VFJVRSkKYGBgCgoKCkZpbmFsIFNhdmUKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNn0KcHJpbnQoIkFuYWx5c2lzIHBpcGVsaW5lIGNvbXBsZXRlLiBBbGwgZmlndXJlcyBhbmQgb2JqZWN0cyBzYXZlZCBpbiBPdXRwdXRfRmlndXJlcyBmb2xkZXIuIikKYGBgCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCg==