1 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)

2 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."

2.1 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!"

2.2 SCpubr Heatmap Visualization-Heatmap of averaged scores

2.3 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 

2.4 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)

2.5 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 

2.6 Top 100 TFs (Figure A for Manuscript)


out <- SCpubr::do_TFActivityHeatmap(sample = seurat_obj,
                                 activities = activities,
                                 n_tfs = 100)

print(out)

pdf("Output_Figures/Figure_3.16A_Global_TF_Heatmap_Top100.pdf", width = 32, height = 12)
print(out)
dev.off()
png 
  2 
png("Output_Figures/Figure_3.16A_Global_TF_Heatmap_Top100.png", width = 32 * 300, height = 12 * 300, res = 300)
print(out)
dev.off()
png 
  2 

2.7 Top 100 TFs (Figure A for Manuscript)


out <- SCpubr::do_TFActivityHeatmap(sample = seurat_obj,
                                 activities = activities,
                                 min.cutoff = -1.7,
                                 max.cutoff = 1.7, group.by = "seurat_clusters",
                                 n_tfs = 100)

print(out)

pdf("Output_Figures/Figure_Top100.pdf", width = 32, height = 12)
print(out)
dev.off()
png 
  2 
png("Output_Figures/Figure_Top100.png", width = 32 * 300, height = 12 * 300, res = 300)
print(out)
dev.off()
png 
  2 

3 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."

4 Figure C: Volcano Plot (Loss of Homeostasis)

# Highlight key drivers mentioned in text
highlight_tfs <- c("FOXO1", "MYC", "E2F1", "E2F4", "FOXM1", "RELA", "IRF1", "STAT1")

p_volcano <- SCpubr::do_VolcanoPlot(sample = seurat_obj,
                                    de_genes = diff_tfs
                                   )

ggsave("Output_Figures/Figure_3.16C_Volcano_TF_Activity.pdf", plot = p_volcano, width = 8, height = 6)
ggsave("Output_Figures/Figure_3.16C_Volcano_TF_Activity.png", plot = p_volcano, width = 8, height = 6, dpi = 300)
print(p_volcano)

5 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)

6 Figure D: Mixed Feature Plots (Activity vs Expression)


# We manually construct this to mix Assays

# Part 1: TF Activity Plots (Assay: dorothea)
DefaultAssay(seurat_obj) <- "dorothea"

p1 <- FeaturePlot(seurat_obj, features = "FOXO1", order = T, reduction = "umap") + 
      scale_color_gradientn(colors = c("grey90", "firebrick")) + ggtitle("FOXO1 Activity (Homeostasis)")
p2 <- FeaturePlot(seurat_obj, features = "RELA", order = T, reduction = "umap") + 
      scale_color_gradientn(colors = c("grey90", "firebrick")) + ggtitle("RELA Activity (Inflammatory)")
p3 <- FeaturePlot(seurat_obj, features = "IRF1", order = T, reduction = "umap") + 
      scale_color_gradientn(colors = c("grey90", "firebrick")) + ggtitle("IRF1 Activity (IFN-Response)")
p4 <- FeaturePlot(seurat_obj, features = "FOXM1", order = T, reduction = "umap") + 
      scale_color_gradientn(colors = c("grey90", "firebrick")) + ggtitle("FOXM1 Activity (Proliferation)")

# Part 2: Gene Expression Plots (Assay: SCT/RNA)
DefaultAssay(seurat_obj) <- "SCT"

p5 <- FeaturePlot(seurat_obj, features = "HMGA2", order = T, reduction = "umap") + 
      scale_color_gradientn(colors = c("grey90", "darkblue")) + ggtitle("HMGA2 Expression (Stem-like)")
p6 <- FeaturePlot(seurat_obj, features = "SOX4", order = T, reduction = "umap") + 
      scale_color_gradientn(colors = c("grey90", "darkblue")) + ggtitle("SOX4 Expression (Stem-like)")

# Combine
final_figure_D <- (p1 | p2 | p3) / (p4 | p5 | p6) + 
                  plot_annotation(title = "Figure 3.16D: Key Drivers (Red=Activity, Blue=Expression)")

ggsave("Output_Figures/Figure_3.16D_Mixed_Features.pdf", plot = final_figure_D, width = 14, height = 10)
ggsave("Output_Figures/Figure_3.16D_Mixed_Features.png", plot = final_figure_D, width = 14, height = 10, dpi = 300)
print(final_figure_D)

7 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 

8 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)

9 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)

10 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)

11 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)

12 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)

13 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)

14 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==