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!")
}

2.2 SCpubr Heatmap Visualization-Heatmap of averaged scores

library(SCpubr)
# General heatmap (Top Variable TFs)
out <- SCpubr::do_TFActivityHeatmap(sample = seurat_obj,
                                 activities = activities)

print(out)

# 1. Save as PDF
pdf("Output_Figures/SCpubr_Heatmap_Default.pdf", width = 10, height = 8)
print(out) # ComplexHeatmap requires explicit print() inside pdf()
dev.off()

# 2. Save as PNG
png("Output_Figures/SCpubr_Heatmap_Default.png", width = 10 * 300, height = 8 * 300, res = 300)
print(out)
dev.off()


print(out)

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("Output_Figures/SCpubr_Heatmap_Scaled.png", width = 10 * 300, height = 8 * 300, res = 300)
print(out)
dev.off()

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("Output_Figures/SCpubr_Heatmap_Symmetric.png", width = 10 * 300, height = 8 * 300, res = 300)
print(out)
dev.off()


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("Output_Figures/SCpubr_Heatmap_Top40.png", width = 14 * 300, height = 6 * 300, res = 300)
print(out)
dev.off()

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("Output_Figures/Figure_3.16A_Global_TF_Heatmap_Top100.png", width = 32 * 300, height = 12 * 300, res = 300)
print(out)
dev.off()

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("Output_Figures/Figure_Top100.png", width = 32 * 300, height = 12 * 300, res = 300)
print(out)
dev.off()

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

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

# 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()

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

png("Output_Figures/Figure_3.16E_Differential_TF_Heatmap.png",
    width = 11 * 300, height = 9 * 300, res = 300)
draw(ht)
dev.off()
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()

png("Output_Figures/Figure_TF_Heatmap.png", width=12*300, height=10*300, res=300)
draw(ht, merge_legend=TRUE)
dev.off()

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

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

draw(ht, merge_legend=TRUE)

13 TEST


# ============================================
# LIBRARIES
# ============================================
library(ComplexHeatmap)
library(circlize)
library(Matrix)
library(grid)
library(SeuratObject)

# ============================================
# 1. Define TF panel metadata
# ============================================
tf_meta <- data.frame(
  TF = c(
    # --- 1. General Malignancy ---
    "MYC","E2F4","TWIST1","IRF4",

    # --- 2. TCR Signaling Triad ---
    "JUNB","FOS","FOSL1",                # AP-1
    "NFATC1","NFATC2",                   # NFAT
    "RELA","REL","NFKB1","IRF1","NCOA2", # NF-kB

    # --- 3. Stress Response ---
    "HSF1","NFE2L2","SREBF2",

    # --- 4. Th2 / JAK-STAT Core ---
    "GATA3","STAT6","BATF","FOXP3","STAT3","STAT5B",

    # --- 5. Differentiation Hierarchy ---
    "TCF7","LEF1","MYB",                 # Stem-like
    "E2F1","FOXM1",                      # Proliferation
    "PRDM1",                             # Terminal Effector

    # --- 6. KEGG Aligned ---
    "EOMES","TBX21","RUNX3",             # NK-like Cytotoxicity
    "RFX5","CREB1",                      # Antigen Presentation
    "KLF4","ETS1","SMAD3",              # Migration / Adhesion

    # --- 7. Microenvironment ---
    "STAT1","STAT2","IRF9",              # IFN Response
    "HIF1A","SREBF1",                    # Metabolism

    # --- 8. Tumor Suppressors / Normal ---
    "FOXO1","FOXO4","ZEB1","BACH2",
    "TCF3","BCL11A","NEUROD1","MEF2B","PBX2","IRF3","BCL6"
  ),
  Condition = c(
    rep("Malignant", 4),   # Oncogenic
    rep("Malignant", 3),   # AP-1
    rep("Malignant", 2),   # NFAT
    rep("Malignant", 5),   # NF-kB
    rep("Malignant", 3),   # Stress Response
    rep("Malignant", 6),   # Th2/JAK-STAT
    rep("Malignant", 3),   # Stem-like
    rep("Malignant", 2),   # Proliferation
    rep("Malignant", 1),   # Terminal Effector
    rep("Malignant", 3),   # NK-like
    rep("Malignant", 2),   # Antigen Presentation
    rep("Malignant", 3),   # Migration
    rep("Malignant", 3),   # IFN Response
    rep("Malignant", 2),   # Metabolism
    rep("Normal", 4),      # Tumor Suppressor
    rep("Normal", 7)       # Normal Homeostasis
  ),
  Function = c(
    rep("Oncogenic", 4),
    rep("AP-1 Signaling", 3),
    rep("NFAT Signaling", 2),
    rep("NF-\u03baB Signaling", 5),  # RENAMED from Inflammatory/NF-kB
    rep("Stress Response", 3),
    rep("Th2 / JAK-STAT Core", 6),
    rep("Stem-like Progenitor", 3),
    rep("Proliferation", 2),
    rep("Terminal Effector", 1),
    rep("NK-like Cytotoxicity", 3),
    rep("Antigen Presentation", 2),
    rep("Migration / Adhesion", 3),
    rep("IFN Response", 3),
    rep("Metabolism", 2),
    rep("Tumor Suppressor", 4),
    rep("Normal Homeostasis", 7)
  ),
  stringsAsFactors = FALSE
)

# Lock in order
desired_order <- c(
  "Oncogenic",
  "AP-1 Signaling",
  "NFAT Signaling",
  "NF-\u03baB Signaling",
  "Stress Response",
  "Th2 / JAK-STAT Core",
  "Stem-like Progenitor",
  "Proliferation",
  "Terminal Effector",
  "NK-like Cytotoxicity",
  "Antigen Presentation",
  "Migration / Adhesion",
  "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. Cluster status & labels
# ============================================
cluster_status <- ifelse(
  colnames(avg_mat_z) %in% c("3", "10"),
  "Normal CD4 T cells",
  "Malignant CD4 T cells"
)

# Cluster biological annotations
cluster_labels <- c(
  "0"  = "MHC-II High",
  "1"  = "NK-like",
  "2"  = "Th2-like",
  "3"  = "Naive/TCM",
  "4"  = "Inflammatory",
  "5"  = "Stem-like",
  "6"  = "Th2 Activated",
  "7"  = "Cycling",
  "8"  = "Glycolytic",
  "9"  = "Cytotoxic",
  "10" = "Central Memory",
  "11" = "Pro-inflammatory",
  "12" = "GZMB-high",
  "13" = "IFN Stimulated"
)
cluster_annotation <- cluster_labels[colnames(avg_mat_z)]

# ============================================
# 5. Column annotation (top)
# ============================================
ha_col <- HeatmapAnnotation(
  Cell_State   = cluster_status,
  Cluster_Type = cluster_annotation,
  col = list(
    Cell_State = c(
      "Normal CD4 T cells"    = "#4DAF4A",
      "Malignant CD4 T cells" = "#C00000"
    ),
    Cluster_Type = c(
      "MHC-II High"      = "#66CDAA",
      "NK-like"          = "#F781BF",
      "Th2-like"         = "#984EA3",
      "Naive/TCM"        = "#4DAF4A",
      "Inflammatory"     = "#8A2BE2",
      "Stem-like"        = "#FF1493",
      "Th2 Activated"    = "#DA70D6",
      "Cycling"          = "#1E90FF",
      "Glycolytic"       = "#A65628",
      "Cytotoxic"        = "#E41A1C",
      "Central Memory"   = "#32CD32",
      "Pro-inflammatory" = "#FF4500",
      "GZMB-high"        = "#B22222",
      "IFN Stimulated"   = "#00CED1"
    )
  ),
  annotation_name_side = "left",
  annotation_name_gp   = gpar(fontsize  = 8,
                               fontface = "bold"),
  simple_anno_size     = unit(4, "mm"),
  border               = TRUE
)

# ============================================
# 6. Row annotation colors
# ============================================
function_colors <- c(
  "Oncogenic"              = "#808080",
  # TCR Signaling Triad — related but distinct
  "AP-1 Signaling"         = "#FF8C00",
  "NFAT Signaling"         = "#FF4500",
  "NF-\u03baB Signaling"  = "#E31A1C",
  "Stress Response"        = "#FFD700",
  "Th2 / JAK-STAT Core"   = "#984EA3",
  "Stem-like Progenitor"   = "#FF1493",
  "Proliferation"          = "#1E90FF",
  "Terminal Effector"      = "#800080",
  "NK-like Cytotoxicity"   = "#F781BF",
  "Antigen Presentation"   = "#66CDAA",
  "Migration / Adhesion"   = "#8A2BE2",
  "IFN Response"           = "#00CED1",
  "Metabolism"             = "#A65628",
  "Tumor Suppressor"       = "#377EB8",
  "Normal Homeostasis"     = "#4DAF4A"
)

# ============================================
# 7. Row annotation (left)
# ============================================
ha_row <- rowAnnotation(
  Condition = tf_meta_filtered$Condition,
  Function  = tf_meta_filtered$Function,
  col = list(
    Condition = c(
      "Normal"    = "#4DAF4A",
      "Malignant" = "#C00000"
    ),
    Function = function_colors
  ),
  annotation_name_side = "bottom",
  annotation_name_gp   = gpar(fontsize  = 8,
                               fontface = "bold"),
  simple_anno_size     = unit(5, "mm"),
  border               = TRUE,
  gap                  = unit(1, "mm")
)

# ============================================
# 8. Heatmap color scale — 5 point gradient
# ============================================
col_fun <- circlize::colorRamp2(
  c(-3, -1.5, 0, 1.5, 3),
  c("#2166AC",   # Deep blue
    "#92C5DE",   # Light blue
    "#F7F7F7",   # Off-white
    "#F4A582",   # Light red
    "#B2182B")   # Deep red
)

# ============================================
# 9. Build heatmap
# ============================================
ht <- Heatmap(
  avg_mat_z,
  name = "TF activity\n(z-score)",
  col  = col_fun,

  # Annotations
  top_annotation  = ha_col,
  left_annotation = ha_row,

  # Splitting
  column_split = cluster_status,
  row_split    = tf_meta_filtered$Function,

  # Clustering
  cluster_row_slices = FALSE,
  cluster_rows       = FALSE,
  cluster_columns    = TRUE,
  show_row_dend      = FALSE,
  show_column_dend   = TRUE,
  column_dend_height = unit(15, "mm"),

  # Cell appearance
  rect_gp = gpar(col = "white", lwd = 0.8),

  # Row names
  row_names_gp   = gpar(fontsize  = 8.5,
                          fontface = "italic",
                          col      = "grey20"),
  row_names_side = "right",

  # Column names
  column_names_gp  = gpar(fontsize  = 10,
                            fontface = "bold",
                            col      = "grey10"),
  column_names_rot = 0,

  # Row title (functional block labels on left)
  row_title_rot  = 0,
  row_title_gp   = gpar(fontsize  = 8,
                          fontface = "bold",
                          col      = "grey10"),
  row_title_side = "left",

  # Gaps between blocks
  row_gap    = unit(2.5, "mm"),
  column_gap = unit(4,   "mm"),

  # Main title
  column_title    = "Functional TF Modules in Sézary Syndrome",
  column_title_gp = gpar(fontsize  = 13,
                           fontface = "bold",
                           col      = "grey5"),

  # Legend
  heatmap_legend_param = list(
    direction     = "vertical",
    title         = "TF activity\n(z-score)",
    title_gp      = gpar(fontsize  = 9,
                           fontface = "bold"),
    labels_gp     = gpar(fontsize  = 8),
    legend_height = unit(4, "cm"),
    border        = "grey50",
    at            = c(-3, -1.5, 0, 1.5, 3),
    labels        = c("-3", "-1.5", "0", "1.5", "3")
  )
)

# ============================================
# 10. Save
# ============================================
# PDF — vector quality for publication
pdf("Output_Figures/Figure_TF_Heatmap_Final.pdf",
    width       = 16,
    height      = 15,
    useDingbats = FALSE)
draw(ht,
     merge_legend           = TRUE,
     heatmap_legend_side    = "right",
     annotation_legend_side = "right",
     padding = unit(c(5, 5, 5, 5), "mm"))
dev.off()
null device 
          1 
# PNG — high resolution for presentations
png("Output_Figures/Figure_TF_Heatmap_Final.png",
    width  = 16 * 300,
    height = 15 * 300,
    res    = 300)
draw(ht,
     merge_legend           = TRUE,
     heatmap_legend_side    = "right",
     annotation_legend_side = "right",
     padding = unit(c(5, 5, 5, 5), "mm"))
dev.off()
null device 
          1 
# Draw in notebook
draw(ht,
     merge_legend           = TRUE,
     heatmap_legend_side    = "right",
     annotation_legend_side = "right",
     padding = unit(c(5, 5, 5, 5), "mm"))

14 Test2

15 Test3


# ============================================
# LIBRARIES
# ============================================
library(ComplexHeatmap)
library(circlize)
library(Matrix)
library(grid)
library(SeuratObject)

# ============================================
# 1. Define TF panel metadata
# ============================================
tf_meta <- data.frame(
  TF = c(
    # --- 1. General Malignancy ---
    "MYC","E2F4","TWIST1","IRF4",

    # --- 2. TCR Signaling Triad ---
    "JUNB","FOS","FOSL1",                # AP-1
    "NFATC1","NFATC2",                   # NFAT
    "RELA","REL","NFKB1","IRF1","NCOA2", # NF-kB

    # --- 3. Stress Response ---
    "HSF1","NFE2L2","SREBF2",

    # --- 4. Th2 / JAK-STAT Core ---
    "GATA3","STAT6","BATF","FOXP3","STAT3","STAT5B",

    # --- 5. Differentiation Hierarchy ---
    "TCF7","LEF1","MYB",                 # Stem-like
    "E2F1","FOXM1",                      # Proliferation
    "PRDM1",                             # Terminal Effector

    # --- 6. KEGG Aligned ---
    "EOMES","TBX21","RUNX3",             # NK-like Cytotoxicity
    "RFX5","CREB1",                      # Antigen Presentation
    "KLF4","ETS1","SMAD3",               # Migration / Adhesion

    # --- 7. Microenvironment ---
    "STAT1","STAT2","IRF9",              # IFN Response
    "HIF1A","SREBF1",                    # Metabolism

    # --- 8. Tumor Suppressors / Normal ---
    "FOXO1","FOXO4","ZEB1","BACH2",
    "TCF3","BCL11A","NEUROD1","MEF2B","PBX2","IRF3","BCL6"
  ),
  Condition = c(
    rep("Malignant", 4),   # Oncogenic
    rep("Malignant", 3),   # AP-1
    rep("Malignant", 2),   # NFAT
    rep("Malignant", 5),   # NF-kB
    rep("Malignant", 3),   # Stress Response
    rep("Malignant", 6),   # Th2/JAK-STAT
    rep("Malignant", 3),   # Stem-like
    rep("Malignant", 2),   # Proliferation
    rep("Malignant", 1),   # Terminal Effector
    rep("Malignant", 3),   # NK-like
    rep("Malignant", 2),   # Antigen Presentation
    rep("Malignant", 3),   # Migration
    rep("Malignant", 3),   # IFN Response
    rep("Malignant", 2),   # Metabolism
    rep("Normal", 4),      # Tumor Suppressor
    rep("Normal", 7)       # Normal Homeostasis
  ),
  Function = c(
    rep("Oncogenic", 4),
    rep("AP-1 Signaling", 3),
    rep("NFAT Signaling", 2),
    rep("NF-\u03baB Signaling", 5),
    rep("Stress Response", 3),
    rep("Th2 / JAK-STAT Core", 6),
    rep("Stem-like Progenitor", 3),
    rep("Proliferation", 2),
    rep("Terminal Effector", 1),
    rep("NK-like Cytotoxicity", 3),
    rep("Antigen Presentation", 2),
    rep("Migration / Adhesion", 3),
    rep("IFN Response", 3),
    rep("Metabolism", 2),
    rep("Tumor Suppressor", 4),
    rep("Normal Homeostasis", 7)
  ),
  stringsAsFactors = FALSE
)

# Lock in order
desired_order <- c(
  "Oncogenic",
  "AP-1 Signaling",
  "NFAT Signaling",
  "NF-\u03baB Signaling",
  "Stress Response",
  "Th2 / JAK-STAT Core",
  "Stem-like Progenitor",
  "Proliferation",
  "Terminal Effector",
  "NK-like Cytotoxicity",
  "Antigen Presentation",
  "Migration / Adhesion",
  "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 orig.ident & z-score
# ============================================
orig_ident <- as.factor(seurat_obj$orig.ident)

avg_mat <- sapply(levels(orig_ident), function(id) {
  Matrix::rowMeans(mat_use[, orig_ident == id,
                            drop = FALSE])
})
colnames(avg_mat) <- levels(orig_ident)

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. Define sample metadata
# FIXED: names match actual orig.ident levels
# ============================================
sample_to_patient <- c(
  "L1"       = "Patient 1",
  "L2"       = "Patient 1",
  "L3"       = "Patient 2",
  "L4"       = "Patient 2",
  "L5"       = "Patient 3",
  "L6"       = "Patient 3",
  "L7"       = "Patient 3",
  "CD4T_lab" = "Normal",
  "CD4T_10x" = "Normal"
)

sample_to_type <- c(
  "L1"       = "Malignant",
  "L2"       = "Malignant",
  "L3"       = "Malignant",
  "L4"       = "Malignant",
  "L5"       = "Malignant",
  "L6"       = "Malignant",
  "L7"       = "Malignant",
  "CD4T_lab" = "Normal",
  "CD4T_10x" = "Normal"
)

# Extract annotations
patient_annotation  <- sample_to_patient[colnames(avg_mat_z)]
celltype_annotation <- sample_to_type[colnames(avg_mat_z)]

# Verify no NAs — should print character(0) for both
cat("Patient NAs:", colnames(avg_mat_z)[is.na(patient_annotation)], "\n")
Patient NAs:  
cat("Type NAs:",    colnames(avg_mat_z)[is.na(celltype_annotation)], "\n")
Type NAs:  
# ============================================
# 5. Column annotation (top)
# ============================================
ha_col <- HeatmapAnnotation(
  Cell_Type = celltype_annotation,
  Patient   = patient_annotation,
  col = list(
    Cell_Type = c(
      "Normal"    = "#4DAF4A",
      "Malignant" = "#C00000"
    ),
    Patient = c(
      "Patient 1" = "#E41A1C",
      "Patient 2" = "#377EB8",
      "Patient 3" = "#FF7F00",
      "Normal"    = "#4DAF4A"
    )
  ),
  annotation_name_side = "left",
  annotation_name_gp   = gpar(fontsize  = 9,
                               fontface = "bold",
                               col      = "black"),
  simple_anno_size     = unit(5, "mm"),
  border               = TRUE
)

# ============================================
# 6. Row annotation colors
# ============================================
function_colors <- c(
  "Oncogenic"             = "#808080",
  "AP-1 Signaling"        = "#FF8C00",
  "NFAT Signaling"        = "#FF4500",
  "NF-\u03baB Signaling" = "#E31A1C",
  "Stress Response"       = "#FFD700",
  "Th2 / JAK-STAT Core"  = "#984EA3",
  "Stem-like Progenitor"  = "#FF1493",
  "Proliferation"         = "#1E90FF",
  "Terminal Effector"     = "#800080",
  "NK-like Cytotoxicity"  = "#F781BF",
  "Antigen Presentation"  = "#66CDAA",
  "Migration / Adhesion"  = "#8A2BE2",
  "IFN Response"          = "#00CED1",
  "Metabolism"            = "#A65628",
  "Tumor Suppressor"      = "#377EB8",
  "Normal Homeostasis"    = "#4DAF4A"
)

# ============================================
# 7. Row annotation (left)
# ============================================
ha_row <- rowAnnotation(
  Condition = tf_meta_filtered$Condition,
  Function  = tf_meta_filtered$Function,
  col = list(
    Condition = c(
      "Normal"    = "#4DAF4A",
      "Malignant" = "#C00000"
    ),
    Function = function_colors
  ),
  annotation_name_side = "bottom",
  annotation_name_gp   = gpar(fontsize  = 9,
                               fontface = "bold",
                               col      = "black"),
  simple_anno_size     = unit(5, "mm"),
  border               = TRUE,
  gap                  = unit(1, "mm")
)

# ============================================
# 8. Heatmap color scale — 5 point gradient
# ============================================
col_fun <- circlize::colorRamp2(
  c(-3, -1.5, 0, 1.5, 3),
  c("#2166AC",   # Deep blue
    "#92C5DE",   # Light blue
    "#F7F7F7",   # Off-white
    "#F4A582",   # Light red
    "#B2182B")   # Deep red
)

# ============================================
# 9. Build heatmap
# ============================================
ht <- Heatmap(
  avg_mat_z,
  name = "TF activity\n(z-score)",
  col  = col_fun,

  # Annotations
  top_annotation  = ha_col,
  left_annotation = ha_row,

  # Splitting — columns by patient, rows by function
  column_split = patient_annotation,
  row_split    = tf_meta_filtered$Function,

  # Clustering
  cluster_row_slices = FALSE,
  cluster_rows       = FALSE,
  cluster_columns    = TRUE,
  show_row_dend      = FALSE,
  show_column_dend   = TRUE,
  column_dend_height = unit(15, "mm"),

  # Cell appearance
  rect_gp = gpar(col = "white", lwd = 0.8),

  # Row names — BOLD for slide visibility
  row_names_gp   = gpar(fontsize  = 12,
                          fontface = "bold",
                          col      = "black"),
  row_names_side = "right",

  # Column names — BOLD for slide visibility
  column_names_gp  = gpar(fontsize  = 12,
                            fontface = "bold",
                            col      = "black"),
  column_names_rot = 45,

  # Row title
  row_title_rot  = 0,
  row_title_gp   = gpar(fontsize  = 11,
                          fontface = "bold",
                          col      = "black"),
  row_title_side = "left",

  # Gaps
  row_gap    = unit(2.5, "mm"),
  column_gap = unit(6,   "mm"),

  # Main title — defined only once
  column_title    = "Functional TF Modules per Cell Line — Sézary Syndrome",
  column_title_gp = gpar(fontsize  = 14,
                           fontface = "bold",
                           col      = "black"),

  # Legend — at/labels removed to avoid version errors
  heatmap_legend_param = list(
    direction     = "vertical",
    title         = "TF activity\n(z-score)",
    title_gp      = gpar(fontsize  = 10,
                           fontface = "bold",
                           col      = "black"),
    labels_gp     = gpar(fontsize  = 9,
                           col      = "black"),
    legend_height = unit(4, "cm"),
    border        = "grey50"
  )
)

# ============================================
# 10. Save
# ============================================
# PDF — vector quality for publication
pdf("Output_Figures/Figure_TF_Heatmap_OrigIdent_Final.pdf",
    width       = 18,
    height      = 16,
    useDingbats = FALSE)
draw(ht,
     merge_legend           = TRUE,
     heatmap_legend_side    = "right",
     annotation_legend_side = "right",
     padding = unit(c(5, 5, 5, 5), "mm"))
dev.off()
null device 
          1 
# PNG — high resolution for presentations
png("Output_Figures/Figure_TF_Heatmap_OrigIdent_Final.png",
    width  = 18 * 300,
    height = 16 * 300,
    res    = 300)
draw(ht,
     merge_legend           = TRUE,
     heatmap_legend_side    = "right",
     annotation_legend_side = "right",
     padding = unit(c(5, 5, 5, 5), "mm"))
dev.off()
null device 
          1 
# Draw in notebook
draw(ht,
     merge_legend           = TRUE,
     heatmap_legend_side    = "right",
     annotation_legend_side = "right",
     padding = unit(c(5, 5, 5, 5), "mm"))

16 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()

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

draw(ht_thelper, merge_legend=TRUE)

Final Save

print("Analysis pipeline complete. All figures and objects saved in Output_Figures folder.")
LS0tCnRpdGxlOiAiVEYgQWN0aXZpdHkgSW5mZXJlbmNlIEFuYWx5c2lzIEhlYXRtYXBzIgphdXRob3I6ICJOYXNpciBNYWhtb29kIEFiYmFzaSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgdGhlbWU6IGpvdXJuYWwKLS0tCgoKIyBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1UUlVFfQojIERhdGEgUHJvY2Vzc2luZwpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoc3RyaW5ncikKCiMgVmlzdWFsaXphdGlvbgpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KFNDcHVicikKCiMgUmVndWxhdG9yeSBOZXR3b3JrIEluZmVyZW5jZQpsaWJyYXJ5KGRlY291cGxlUikKbGlicmFyeShkb3JvdGhlYSkKZGF0YShkb3JvdGhlYV9ocywgcGFja2FnZSA9ICJkb3JvdGhlYSIpCmxpYnJhcnkodGljdG9jKQoKCmBgYAoKIyBMb2FkIFNldXJhdCBPYmplY3QgCmBgYHtyfQoKIyBMb2FkIHlvdXIgU2V1cmF0IE9iamVjdApzZXVyYXRfb2JqIDwtIHJlYWRSRFMoIi4uL091dHB1dF9PYmplY3RzL1NldXJhdF9PYmplY3RfV2l0aF9URl9BY3Rpdml0eS5yZHMiKQoKSWRlbnRzKHNldXJhdF9vYmopIDwtICJzZXVyYXRfY2x1c3RlcnMiCnByaW50KCJPYmplY3QgTG9hZGVkLiIpCmBgYAoKIyMgUnVuIHRoaXMgY29kZSBibG9jayB0byByZXN0b3JlIGFjdGl2aXRpZXMgaW5zdGFudGx5OgpgYGB7cn0KCiMgSWYgJ2FjdGl2aXRpZXMnIGlzIG1pc3NpbmcgYnV0ICdkb3JvdGhlYScgYXNzYXkgZXhpc3RzLCByZWNvbnN0cnVjdCBpdDoKaWYgKCFleGlzdHMoImFjdGl2aXRpZXMiKSAmJiAiZG9yb3RoZWEiICVpbiUgbmFtZXMoc2V1cmF0X29iakBhc3NheXMpKSB7CiAgCiAgcHJpbnQoIlJlY29uc3RydWN0aW5nICdhY3Rpdml0aWVzJyBkYXRhZnJhbWUgZnJvbSBTZXVyYXQgb2JqZWN0Li4uIikKICAKICAjIEV4dHJhY3QgdGhlIG1hdHJpeCAoU2V1cmF0IHY1IHVzZXMgJ2xheWVyJyBpbnN0ZWFkIG9mICdzbG90JykKICAjIFNpbmNlIHlvdSByYW4gU2NhbGVEYXRhLCB3ZSB1c2UgJ3NjYWxlLmRhdGEnCiAgbWF0IDwtIEdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheSA9ICJkb3JvdGhlYSIsIGxheWVyID0gInNjYWxlLmRhdGEiKQogIAogICMgQ29udmVydCB0byBsb25nIGZvcm1hdCAod2hhdCBTQ3B1YnIgbmVlZHMpCiAgYWN0aXZpdGllcyA8LSBhcy5kYXRhLmZyYW1lKG1hdCkgJT4lCiAgICByb3duYW1lc190b19jb2x1bW4oInNvdXJjZSIpICU+JQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtc291cmNlLCBuYW1lc190byA9ICJjb25kaXRpb24iLCB2YWx1ZXNfdG8gPSAic2NvcmUiKSAlPiUKICAgIG11dGF0ZShzdGF0aXN0aWMgPSAibm9ybV93bWVhbiIpICMgU0NwdWJyIHJlcXVpcmVzIHRoaXMgY29sdW1uCiAgICAKICBwcmludCgiQWN0aXZpdGllcyBkYXRhZnJhbWUgcmVzdG9yZWQhIikKfQpgYGAKCgojIyBTQ3B1YnIgSGVhdG1hcCBWaXN1YWxpemF0aW9uLUhlYXRtYXAgb2YgYXZlcmFnZWQgc2NvcmVzCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KbGlicmFyeShTQ3B1YnIpCiMgR2VuZXJhbCBoZWF0bWFwIChUb3AgVmFyaWFibGUgVEZzKQpvdXQgPC0gU0NwdWJyOjpkb19URkFjdGl2aXR5SGVhdG1hcChzYW1wbGUgPSBzZXVyYXRfb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY3Rpdml0aWVzID0gYWN0aXZpdGllcykKCnByaW50KG91dCkKCiMgMS4gU2F2ZSBhcyBQREYKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9EZWZhdWx0LnBkZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCnByaW50KG91dCkgIyBDb21wbGV4SGVhdG1hcCByZXF1aXJlcyBleHBsaWNpdCBwcmludCgpIGluc2lkZSBwZGYoKQpkZXYub2ZmKCkKCiMgMi4gU2F2ZSBhcyBQTkcKcG5nKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9EZWZhdWx0LnBuZyIsIHdpZHRoID0gMTAgKiAzMDAsIGhlaWdodCA9IDggKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQob3V0KQpkZXYub2ZmKCkKCgpwcmludChvdXQpCgpgYGAKCgojIyBTZXQgdGhlIHNjYWxlIGxpbWl0cwpgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CgpvdXQgPC0gU0NwdWJyOjpkb19URkFjdGl2aXR5SGVhdG1hcChzYW1wbGUgPSBzZXVyYXRfb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY3Rpdml0aWVzID0gYWN0aXZpdGllcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmN1dG9mZiA9IC0xLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heC5jdXRvZmYgPSAxLjUpCgpwcmludChvdXQpCgojIFNhdmUgQ29tcGxleEhlYXRtYXAgcHJvcGVybHkKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9TY2FsZWQucGRmIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKcHJpbnQob3V0KQpkZXYub2ZmKCkKCnBuZygiT3V0cHV0X0ZpZ3VyZXMvU0NwdWJyX0hlYXRtYXBfU2NhbGVkLnBuZyIsIHdpZHRoID0gMTAgKiAzMDAsIGhlaWdodCA9IDggKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQob3V0KQpkZXYub2ZmKCkKCgpgYGAKCiMjIEVuZm9yY2UgU3ltbWV0cnkgKEJlc3QgZm9yIE1hbnVzY3JpcHQpCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KCm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlIZWF0bWFwKHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uY3V0b2ZmID0gLTEuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LmN1dG9mZiA9IDEuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5mb3JjZV9zeW1tZXRyeSA9IFRSVUUpCgpwcmludChvdXQpCgpwZGYoIk91dHB1dF9GaWd1cmVzL1NDcHVicl9IZWF0bWFwX1N5bW1ldHJpYy5wZGYiLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQpwcmludChvdXQpCmRldi5vZmYoKQoKcG5nKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9TeW1tZXRyaWMucG5nIiwgd2lkdGggPSAxMCAqIDMwMCwgaGVpZ2h0ID0gOCAqIDMwMCwgcmVzID0gMzAwKQpwcmludChvdXQpCmRldi5vZmYoKQoKCnByaW50KG91dCkKCmBgYAoKIyMgVG9wIDQwIFRGcwpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTR9Cm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlIZWF0bWFwKHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3RmcyA9IDQwKQoKcHJpbnQob3V0KQoKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9Ub3A0MC5wZGYiLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSA2KQpwcmludChvdXQpCmRldi5vZmYoKQoKcG5nKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9Ub3A0MC5wbmciLCB3aWR0aCA9IDE0ICogMzAwLCBoZWlnaHQgPSA2ICogMzAwLCByZXMgPSAzMDApCnByaW50KG91dCkKZGV2Lm9mZigpCmBgYAoKCiMjIFRvcCAxMDAgVEZzIChGaWd1cmUgQSBmb3IgTWFudXNjcmlwdCkKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0zMn0KCm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlIZWF0bWFwKHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl90ZnMgPSAxMDApCgpwcmludChvdXQpCgpwZGYoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2QV9HbG9iYWxfVEZfSGVhdG1hcF9Ub3AxMDAucGRmIiwgd2lkdGggPSAzMiwgaGVpZ2h0ID0gMTIpCnByaW50KG91dCkKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2QV9HbG9iYWxfVEZfSGVhdG1hcF9Ub3AxMDAucG5nIiwgd2lkdGggPSAzMiAqIDMwMCwgaGVpZ2h0ID0gMTIgKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQob3V0KQpkZXYub2ZmKCkKYGBgCiMjIFRvcCAxMDAgVEZzIChGaWd1cmUgQSBmb3IgTWFudXNjcmlwdCkKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0zMn0KCm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlIZWF0bWFwKHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uY3V0b2ZmID0gLTEuNywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LmN1dG9mZiA9IDEuNywgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl90ZnMgPSAxMDApCgpwcmludChvdXQpCgpwZGYoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV9Ub3AxMDAucGRmIiwgd2lkdGggPSAzMiwgaGVpZ2h0ID0gMTIpCnByaW50KG91dCkKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV9Ub3AxMDAucG5nIiwgd2lkdGggPSAzMiAqIDMwMCwgaGVpZ2h0ID0gMTIgKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQob3V0KQpkZXYub2ZmKCkKYGBgCgojIERpZmZlcmVudGlhbCBURiBBY3Rpdml0eSAoTWFsaWduYW50IHZzLiBOb3JtYWwpCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CgojIERlZmluZSBDb21wYXJpc29uOiBDbHVzdGVycyAzICYgMTAgKE5vcm1hbCkgdnMgUmVzdCAoTWFsaWduYW50KQpub25fbWFsaWduYW50X2NsdXN0ZXJzIDwtIGMoMywgMTApCnNldXJhdF9vYmokQ29uZGl0aW9uIDwtIGlmZWxzZShzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycyAlaW4lIG5vbl9tYWxpZ25hbnRfY2x1c3RlcnMsICJOb24tTWFsaWduYW50IiwgIk1hbGlnbmFudCIpCgojIFBlcmZvcm0gRGlmZmVyZW50aWFsIEFuYWx5c2lzIG9uIFRGIEFjdGl2aXR5CkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiZG9yb3RoZWEiCklkZW50cyhzZXVyYXRfb2JqKSA8LSAiQ29uZGl0aW9uIgoKcHJpbnQoIlJ1bm5pbmcgRmluZE1hcmtlcnMgb24gVEYgQWN0aXZpdHkuLi4iKQpkaWZmX3RmcyA8LSBGaW5kTWFya2VycyhzZXVyYXRfb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgaWRlbnQuMSA9ICJNYWxpZ25hbnQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgaWRlbnQuMiA9ICJOb24tTWFsaWduYW50IiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAsICMgR2V0IGFsbCBmb3Igdm9sY2FubwogICAgICAgICAgICAgICAgICAgICAgICBtaW4ucGN0ID0gMCkKCiMgQWRkIGdlbmUgY29sdW1uIGZvciBsYWJlbGluZwpkaWZmX3RmcyRnZW5lIDwtIHJvd25hbWVzKGRpZmZfdGZzKQoKIyBTYXZlIFJlc3VsdHMKd3JpdGUuY3N2KGRpZmZfdGZzLCAiT3V0cHV0X1RhYmxlcy9EaWZmZXJlbnRpYWxfVEZfQWN0aXZpdHlfTWFsaWduYW50X3ZzX05vcm1hbC5jc3YiKQpwcmludCgiRGlmZmVyZW50aWFsIGFuYWx5c2lzIGNvbXBsZXRlLiIpCgpgYGAKCgojIEZpZ3VyZSBDOiBWb2xjYW5vIFBsb3QgKExvc3Mgb2YgSG9tZW9zdGFzaXMpCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD04fQojIEhpZ2hsaWdodCBrZXkgZHJpdmVycyBtZW50aW9uZWQgaW4gdGV4dApoaWdobGlnaHRfdGZzIDwtIGMoIkZPWE8xIiwgIk1ZQyIsICJFMkYxIiwgIkUyRjQiLCAiRk9YTTEiLCAiUkVMQSIsICJJUkYxIiwgIlNUQVQxIikKCnBfdm9sY2FubyA8LSBTQ3B1YnI6OmRvX1ZvbGNhbm9QbG90KHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlX2dlbmVzID0gZGlmZl90ZnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2Q19Wb2xjYW5vX1RGX0FjdGl2aXR5LnBkZiIsIHBsb3QgPSBwX3ZvbGNhbm8sIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfMy4xNkNfVm9sY2Fub19URl9BY3Rpdml0eS5wbmciLCBwbG90ID0gcF92b2xjYW5vLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCkKcHJpbnQocF92b2xjYW5vKQpgYGAKCgojIFVwZGF0ZWQgRmlndXJlIEM6IEVuaGFuY2VkVm9sY2FubwpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCiMgSGlnaGxpZ2h0IGtleSBkcml2ZXJzIG1lbnRpb25lZCBpbiB0ZXh0CmhpZ2hsaWdodF90ZnMgPC0gYygiRk9YTzEiLCAiTVlDIiwgIkUyRjEiLCAiRTJGNCIsICJGT1hNMSIsICJSRUxBIiwgIklSRjEiLCAiU1RBVDEiLCAiVE9YIiwgIkdBVEEzIikKCiMgQ3JlYXRlIHRoZSBFbmhhbmNlZFZvbGNhbm8gUGxvdApwX3ZvbGNhbm8gPC0gRW5oYW5jZWRWb2xjYW5vKGRpZmZfdGZzLAogICAgbGFiID0gcm93bmFtZXMoZGlmZl90ZnMpLAogICAgeCA9ICdhdmdfbG9nMkZDJywKICAgIHkgPSAncF92YWxfYWRqJywKICAgIAogICAgdGl0bGUgPSAnRGlmZmVyZW50aWFsIFRGIEFjdGl2aXR5OiBNYWxpZ25hbnQgdnMuIE5vbi1NYWxpZ25hbnQnLAogICAgc3VidGl0bGUgPSAnRGVjb3VwbGVSIEluZmVycmVkIEFjdGl2aXR5JywKICAgIHBDdXRvZmYgPSAxZS01LAogICAgRkNjdXRvZmYgPSAwLjUsCiAgICBwb2ludFNpemUgPSAzLjAsCiAgICBsYWJTaXplID0gNS4wLAogICAgY29sQWxwaGEgPSAwLjgsCiAgICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCiAgICBsZWdlbmRMYWJTaXplID0gMTIsCiAgICBsZWdlbmRJY29uU2l6ZSA9IDQuMCwKICAgIGRyYXdDb25uZWN0b3JzID0gVFJVRSwgIyBEcmF3IGxpbmVzIHRvIGxhYmVscyB0byBhdm9pZCBvdmVybGFwCiAgICB3aWR0aENvbm5lY3RvcnMgPSAwLjUsCiAgICBjb2xDb25uZWN0b3JzID0gJ2dyZXkzMCcsCiAgICAjIEN1c3RvbSBDb2xvcnM6IERvd24gKEJsdWUpLCBVcCAoUmVkKSwgTlMgKEdyZXkpCiAgICBjb2wgPSBjKCJncmV5MzAiLCAiZm9yZXN0Z3JlZW4iLCAicm95YWxibHVlIiwgImZpcmVicmljazIiKQopCgojIFByaW50CnByaW50KHBfdm9sY2FubykKCiMgU2F2ZQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2Q19FbmhhbmNlZFZvbGNhbm9fVEZfQWN0aXZpdHkucGRmIiwgcGxvdCA9IHBfdm9sY2Fubywgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfMy4xNkNfRW5oYW5jZWRWb2xjYW5vX1RGX0FjdGl2aXR5LnBuZyIsIHBsb3QgPSBwX3ZvbGNhbm8sIHdpZHRoID0gMTAsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKYGBgCgoKCgoKCgoKIyBGaWd1cmUgRDogTWl4ZWQgRmVhdHVyZSBQbG90cyAoQWN0aXZpdHkgdnMgRXhwcmVzc2lvbikKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNn0KCiMgV2UgbWFudWFsbHkgY29uc3RydWN0IHRoaXMgdG8gbWl4IEFzc2F5cwoKIyBQYXJ0IDE6IFRGIEFjdGl2aXR5IFBsb3RzIChBc3NheTogZG9yb3RoZWEpCkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiZG9yb3RoZWEiCgpwMSA8LSBGZWF0dXJlUGxvdChzZXVyYXRfb2JqLCBmZWF0dXJlcyA9ICJGT1hPMSIsIG9yZGVyID0gVCwgcmVkdWN0aW9uID0gInVtYXAiKSArIAogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYygiZ3JleTkwIiwgImZpcmVicmljayIpKSArIGdndGl0bGUoIkZPWE8xIEFjdGl2aXR5IChIb21lb3N0YXNpcykiKQpwMiA8LSBGZWF0dXJlUGxvdChzZXVyYXRfb2JqLCBmZWF0dXJlcyA9ICJSRUxBIiwgb3JkZXIgPSBULCByZWR1Y3Rpb24gPSAidW1hcCIpICsgCiAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBjKCJncmV5OTAiLCAiZmlyZWJyaWNrIikpICsgZ2d0aXRsZSgiUkVMQSBBY3Rpdml0eSAoSW5mbGFtbWF0b3J5KSIpCnAzIDwtIEZlYXR1cmVQbG90KHNldXJhdF9vYmosIGZlYXR1cmVzID0gIklSRjEiLCBvcmRlciA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikgKyAKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGMoImdyZXk5MCIsICJmaXJlYnJpY2siKSkgKyBnZ3RpdGxlKCJJUkYxIEFjdGl2aXR5IChJRk4tUmVzcG9uc2UpIikKcDQgPC0gRmVhdHVyZVBsb3Qoc2V1cmF0X29iaiwgZmVhdHVyZXMgPSAiRk9YTTEiLCBvcmRlciA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikgKyAKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGMoImdyZXk5MCIsICJmaXJlYnJpY2siKSkgKyBnZ3RpdGxlKCJGT1hNMSBBY3Rpdml0eSAoUHJvbGlmZXJhdGlvbikiKQoKIyBQYXJ0IDI6IEdlbmUgRXhwcmVzc2lvbiBQbG90cyAoQXNzYXk6IFNDVC9STkEpCkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiU0NUIgoKcDUgPC0gRmVhdHVyZVBsb3Qoc2V1cmF0X29iaiwgZmVhdHVyZXMgPSAiSE1HQTIiLCBvcmRlciA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikgKyAKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGMoImdyZXk5MCIsICJkYXJrYmx1ZSIpKSArIGdndGl0bGUoIkhNR0EyIEV4cHJlc3Npb24gKFN0ZW0tbGlrZSkiKQpwNiA8LSBGZWF0dXJlUGxvdChzZXVyYXRfb2JqLCBmZWF0dXJlcyA9ICJTT1g0Iiwgb3JkZXIgPSBULCByZWR1Y3Rpb24gPSAidW1hcCIpICsgCiAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBjKCJncmV5OTAiLCAiZGFya2JsdWUiKSkgKyBnZ3RpdGxlKCJTT1g0IEV4cHJlc3Npb24gKFN0ZW0tbGlrZSkiKQoKIyBDb21iaW5lCmZpbmFsX2ZpZ3VyZV9EIDwtIChwMSB8IHAyIHwgcDMpIC8gKHA0IHwgcDUgfCBwNikgKyAKICAgICAgICAgICAgICAgICAgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gIkZpZ3VyZSAzLjE2RDogS2V5IERyaXZlcnMgKFJlZD1BY3Rpdml0eSwgQmx1ZT1FeHByZXNzaW9uKSIpCgpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2RF9NaXhlZF9GZWF0dXJlcy5wZGYiLCBwbG90ID0gZmluYWxfZmlndXJlX0QsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwKQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2RF9NaXhlZF9GZWF0dXJlcy5wbmciLCBwbG90ID0gZmluYWxfZmlndXJlX0QsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCnByaW50KGZpbmFsX2ZpZ3VyZV9EKQpgYGAKCgoKCiMgRmlndXJlIEUgKENvbXBsZXhIZWF0bWFwKSBjaHVuawpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KE1hdHJpeCkKCiMgRXhwYW5kZWQgbGlzdCBvZiBzdGF0ZS1zcGVjaWZpYyBkcml2ZXJzIGJhc2VkIG9uIHlvdXIgcmVndWxvbiBhbmFseXNpcwpsaXRlcmF0dXJlX3RmcyA8LSBjKAogICJHQVRBMyIsICJTVEFUNiIsICJCQVRGIiwgIkZPWFAzIiwgIlNUQVQzIiwgIlNUQVQ1QiIsICJUQ0Y3IiwgIyBDb3JlL01lbW9yeQogICJFMkYxIiwgIk1ZQyIsICJGT1hNMSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBQcm9saWZlcmF0aW9uIChDbCA3KQogICJTVEFUMSIsICJTVEFUMiIsICJJUkYxIiwgIklSRjkiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBJRk4tc3RpbXVsYXRlZCAoQ2wgMTMpCiAgIlJFTEEiLCAiTkZLQjEiLCAiUkVMIiwgIkZPUyIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFByby1pbmZsYW1tYXRvcnkgKENsIDExLCAxMikKICAiVEJYMjEiLCAiUlVOWDMiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQ3l0b3RveGljIChDbCAxLCA5KQogICJISUYxQSIsICJTUkVCRjEiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZXRhYm9saWMgc2hpZnQgKENsIDgpCiAgIlJGWDUiLCAiU1BJMSIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1IQy1JSSBIaWdoIChDbCAwKQopCgoKIyBLZWVwIG9ubHkgVEZzIHByZXNlbnQgaW4gdGhlIGRvcm90aGVhIGFzc2F5CmF2YWlsYWJsZV90ZnMgPC0gaW50ZXJzZWN0KGxpdGVyYXR1cmVfdGZzLCByb3duYW1lcyhzZXVyYXRfb2JqW1siZG9yb3RoZWEiXV0pKQppZiAobGVuZ3RoKGF2YWlsYWJsZV90ZnMpIDwgNSkgc3RvcCgiVG9vIGZldyBURnMgZm91bmQgaW4gZG9yb3RoZWEgYXNzYXkuIENoZWNrIFRGIG5hbWluZyAvIGFzc2F5IGNvbnRlbnQuIikKCiMgRXh0cmFjdCBURiBhY3Rpdml0eSBtYXRyaXggKFRGcyB4IGNlbGxzKQojIFVzZSBzY2FsZS5kYXRhIGlmIGF2YWlsYWJsZTsgb3RoZXJ3aXNlIGZhbGwgYmFjayB0byBkYXRhIGxheWVyLgptYXRfc2NhbGVkIDwtIHRyeUNhdGNoKAogIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAic2NhbGUuZGF0YSIpLAogIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTAopCm1hdF9kYXRhIDwtIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAiZGF0YSIpCgptYXRfdXNlIDwtIGlmICghaXMubnVsbChtYXRfc2NhbGVkKSAmJiBucm93KG1hdF9zY2FsZWQpID4gMCkgbWF0X3NjYWxlZCBlbHNlIG1hdF9kYXRhCm1hdF91c2UgPC0gbWF0X3VzZVthdmFpbGFibGVfdGZzLCAsIGRyb3AgPSBGQUxTRV0KCiMgQXZlcmFnZSBwZXIgY2x1c3RlciAoVEYgeCBjbHVzdGVyKQpjbHVzdGVycyA8LSBhcy5mYWN0b3Ioc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMpCmF2Z19tYXQgPC0gc2FwcGx5KGxldmVscyhjbHVzdGVycyksIGZ1bmN0aW9uKGNsKSB7CiAgTWF0cml4Ojpyb3dNZWFucyhtYXRfdXNlWywgY2x1c3RlcnMgPT0gY2wsIGRyb3AgPSBGQUxTRV0pCn0pCmNvbG5hbWVzKGF2Z19tYXQpIDwtIGxldmVscyhjbHVzdGVycykKCiMgT3B0aW9uYWw6IHotc2NvcmUgYWNyb3NzIGNsdXN0ZXJzIChoZWxwcyByZWFkYWJpbGl0eSBpZiB5b3UgdXNlZCByYXcgJ2RhdGEnIGluc3RlYWQgb2YgJ3NjYWxlLmRhdGEnKQphdmdfbWF0X3ogPC0gdChzY2FsZSh0KGF2Z19tYXQpKSkKYXZnX21hdF96W2lzLm5hKGF2Z19tYXRfeildIDwtIDAKCiMgQ29sb3JzCmNvbF9mdW4gPC0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygtMiwgMCwgMiksIGMoIiMzMTM2OTUiLCAid2hpdGUiLCAiI0E1MDAyNiIpKQoKaHQgPC0gSGVhdG1hcCgKICBhdmdfbWF0X3osCiAgbmFtZSA9ICJURiBhY3Rpdml0eSAoeikiLAogIGNvbCA9IGNvbF9mdW4sCiAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogIHNob3dfcm93X2RlbmQgPSBUUlVFLAogIHNob3dfY29sdW1uX2RlbmQgPSBUUlVFLAogIHJvd19uYW1lc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSAxMCksCiAgY29sdW1uX25hbWVzX2dwID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IDEwKSwKICBjb2x1bW5fdGl0bGUgPSAiTGl0ZXJhdHVyZS12YWxpZGF0ZWQgU8OpemFyeSBURiBtb2R1bGVzIChEb1JvdGhFQS9kZWNvdXBsZVIpIiwKICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QoZGlyZWN0aW9uID0gInZlcnRpY2FsIikKKQoKIyBEcmF3IHRvIG5vdGVib29rCmRyYXcoaHQpCgojIFNhdmUgUERGICh2ZWN0b3IpCnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlXzMuMTZFX0xpdGVyYXR1cmVfVEZfSGVhdG1hcF9Db21wbGV4SGVhdG1hcC5wZGYiLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQpkcmF3KGh0KQpkZXYub2ZmKCkKCiMgU2F2ZSBQTkcgKHJhc3RlciwgcHVibGljYXRpb24tcmVhZHkpCnBuZygiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlXzMuMTZFX0xpdGVyYXR1cmVfVEZfSGVhdG1hcF9Db21wbGV4SGVhdG1hcC5wbmciLAogICAgd2lkdGggPSAxMCAqIDMwMCwgaGVpZ2h0ID0gOCAqIDMwMCwgcmVzID0gMzAwKQpkcmF3KGh0KQpkZXYub2ZmKCkKYGBgCgojIEZpZ3VyZSBGIChDb21wbGV4SGVhdG1hcCkgY2h1bmsKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShNYXRyaXgpCgojIEV4cGFuZGVkIGxpc3QgaW5jbHVkaW5nIEZPWE8xIGFuZCB0dW1vciBzdXBwcmVzc29ycwpsaXRlcmF0dXJlX3RmcyA8LSBjKAogICMgVG9wIE1hbGlnbmFudCBVcHJlZ3VsYXRlZCAoT25jb2dlbmljLCBTdHJlc3MsIFByb2xpZmVyYXRpb24pCiAgIlJGWDUiLCAiTVlDIiwgIkUyRjQiLCAiSFNGMSIsICJTUkVCRjIiLCAiTkZFMkwyIiwgCiAgIlJFTEEiLCAiUkVMIiwgIk5GS0IxIiwgIklSRjEiLCAiTkNPQTIiLAogIAogICMgTWFsaWduYW50IERvd25yZWd1bGF0ZWQgLyBOb3JtYWwgRW5yaWNoZWQgKFR1bW9yIFN1cHByZXNzb3JzICYgSG9tZW9zdGFzaXMpCiAgIkZPWE8xIiwgIkZPWE80IiwgIlJVTlgzIiwgIlRDRjMiLCAiQkNMMTFBIiwgIk5FVVJPRDEiLCAiTUVGMkIiLCAiUEJYMiIsCiAgCiAgIyBVTUFQIFN0YXRlIERyaXZlcnMgKEludHJhLXR1bW9yYWwgaGV0ZXJvZ2VuZWl0eSkKICAiR0FUQTMiLCAiU1RBVDYiLCAiQkFURiIsICJGT1hQMyIsICJTVEFUMyIsICJTVEFUNUIiLCAiVENGNyIsICMgQ29yZS9NZW1vcnkKICAiRTJGMSIsICJGT1hNMSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUHJvbGlmZXJhdGlvbgogICJTVEFUMSIsICJTVEFUMiIsICJJUkY5IiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBJRk4gcmVzcG9uc2UKICAiSElGMUEiLCAiU1JFQkYxIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWV0YWJvbGljCiAgIlRCWDIxIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEN5dG90b3hpYwopCgojIEV4dHJhY3QgVEYgYWN0aXZpdHkgbWF0cml4Cm1hdF9zY2FsZWQgPC0gdHJ5Q2F0Y2goCiAgU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJzY2FsZS5kYXRhIiksCiAgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMCikKbWF0X2RhdGEgPC0gU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJkYXRhIikKCm1hdF91c2UgPC0gaWYgKCFpcy5udWxsKG1hdF9zY2FsZWQpICYmIG5yb3cobWF0X3NjYWxlZCkgPiAwKSBtYXRfc2NhbGVkIGVsc2UgbWF0X2RhdGEKYXZhaWxhYmxlX3RmcyA8LSBpbnRlcnNlY3QobGl0ZXJhdHVyZV90ZnMsIHJvd25hbWVzKG1hdF91c2UpKQppZiAobGVuZ3RoKGF2YWlsYWJsZV90ZnMpIDwgNSkgc3RvcCgiVG9vIGZldyBURnMgZm91bmQuIENoZWNrIGFzc2F5IGRhdGEuIikKbWF0X3VzZSA8LSBtYXRfdXNlW2F2YWlsYWJsZV90ZnMsICwgZHJvcCA9IEZBTFNFXQoKIyBBdmVyYWdlIHBlciBjbHVzdGVyIGFuZCB6LXNjb3JlCmNsdXN0ZXJzIDwtIGFzLmZhY3RvcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykKYXZnX21hdCA8LSBzYXBwbHkobGV2ZWxzKGNsdXN0ZXJzKSwgZnVuY3Rpb24oY2wpIHsKICBNYXRyaXg6OnJvd01lYW5zKG1hdF91c2VbLCBjbHVzdGVycyA9PSBjbCwgZHJvcCA9IEZBTFNFXSkKfSkKY29sbmFtZXMoYXZnX21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQoKYXZnX21hdF96IDwtIHQoc2NhbGUodChhdmdfbWF0KSkpCmF2Z19tYXRfeltpcy5uYShhdmdfbWF0X3opXSA8LSAwCgojIEFubm90YXRlIE1hbGlnbmFudCB2cyBOb3JtYWwgKENsdXN0ZXJzIDMsIDEwID0gTm9ybWFsKQpjbHVzdGVyX3N0YXR1cyA8LSBpZmVsc2UoY29sbmFtZXMoYXZnX21hdF96KSAlaW4lIGMoIjMiLCAiMTAiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiTm9ybWFsIENENCBUIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiTWFsaWduYW50IENENCBUIGNlbGxzIikKCiMgRGVmaW5lIGFubm90YXRpb24KaGEgPC0gSGVhdG1hcEFubm90YXRpb24oCiAgQ2VsbF9TdGF0ZSA9IGNsdXN0ZXJfc3RhdHVzLAogIGNvbCA9IGxpc3QoQ2VsbF9TdGF0ZSA9IGMoIk5vcm1hbCBDRDQgVCIgPSAiIzREQUY0QSIsICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMiID0gIiNFNDFBMUMiKSksCiAgYW5ub3RhdGlvbl9uYW1lX3NpZGUgPSAibGVmdCIKKQoKIyBDb2xvcnMKY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC0zLCAwLCAzKSwgYygiIzMxMzY5NSIsICJ3aGl0ZSIsICIjQTUwMDI2IikpCgojIENyZWF0ZSBoZWF0bWFwIHdpdGggY29sdW1uIHNwbGl0Cmh0IDwtIEhlYXRtYXAoCiAgYXZnX21hdF96LAogIG5hbWUgPSAiVEYgYWN0aXZpdHkgKHopIiwKICBjb2wgPSBjb2xfZnVuLAogIHRvcF9hbm5vdGF0aW9uID0gaGEsCiAgY29sdW1uX3NwbGl0ID0gY2x1c3Rlcl9zdGF0dXMsICMgUGh5c2ljYWxseSBzcGxpdHMgbm9ybWFsIGFuZCBtYWxpZ25hbnQgY29sdW1ucwogIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwKICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKGZvbnRzaXplID0gMTApLAogIGNvbHVtbl9uYW1lc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSAxMCksCiAgY29sdW1uX3RpdGxlID0gIkRpZmZlcmVudGlhbCBURiBNb2R1bGVzIGluIFPDqXphcnkgSGV0ZXJvZ2VuZWl0eSIsCiAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KGRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpCikKCiMgT3V0cHV0CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlXzMuMTZFX0RpZmZlcmVudGlhbF9URl9IZWF0bWFwLnBkZiIsIHdpZHRoID0gMTEsIGhlaWdodCA9IDkpCmRyYXcoaHQpCmRldi5vZmYoKQoKcG5nKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfMy4xNkVfRGlmZmVyZW50aWFsX1RGX0hlYXRtYXAucG5nIiwKICAgIHdpZHRoID0gMTEgKiAzMDAsIGhlaWdodCA9IDkgKiAzMDAsIHJlcyA9IDMwMCkKZHJhdyhodCkKZGV2Lm9mZigpCmRyYXcoaHQpCmBgYAojIEZpZ3VyZSBHIChDb21wbGV4SGVhdG1hcCkgY2h1bmsKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMn0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBMSUJSQVJJRVMKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShncmlkKQpsaWJyYXJ5KFNldXJhdE9iamVjdCkgICMgZm9yIEdldEFzc2F5RGF0YQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDEuIERlZmluZSBURiBwYW5lbCBtZXRhZGF0YSAoNDQtNDcgVEZzKQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnRmX21ldGEgPC0gZGF0YS5mcmFtZSgKICBURiA9IGMoCiAgICAiTVlDIiwiRTJGNCIsIlJGWDUiLCJUV0lTVDEiLCJKVU5CIiwiSVJGNCIsIkNSRUIxIiwKICAgICJGT1MiLCJGT1NMMSIsCiAgICAiSFNGMSIsIk5GRTJMMiIsIlNSRUJGMiIsCiAgICAiUkVMQSIsIlJFTCIsIk5GS0IxIiwiSVJGMSIsIk5DT0EyIiwKICAgICJORkFUQzEiLCJORkFUQzIiLAogICAgIkZPWE8xIiwiRk9YTzQiLCJSVU5YMyIsIlpFQjEiLCJCQUNIMiIsCiAgICAiVENGMyIsIkJDTDExQSIsIk5FVVJPRDEiLCJNRUYyQiIsIlBCWDIiLCJJUkYzIiwiQkNMNiIsCiAgICAiR0FUQTMiLCJTVEFUNiIsIkJBVEYiLCJGT1hQMyIsIlNUQVQzIiwiU1RBVDVCIiwiVENGNyIsCiAgICAiRTJGMSIsIkZPWE0xIiwKICAgICJTVEFUMSIsIlNUQVQyIiwiSVJGOSIsCiAgICAiSElGMUEiLCJTUkVCRjEiLAogICAgIkVPTUVTIiwKICAgICJQUkRNMSIKICApLAogIENvbmRpdGlvbiA9IGMoCiAgICByZXAoIk1hbGlnbmFudCIsIDcpLCAgIyBPbmNvZ2VuaWMKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAjIEFQLTEKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAjIFN0cmVzcwogICAgcmVwKCJNYWxpZ25hbnQiLCA1KSwgICMgTkYta0IKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAjIE5GQVQKICAgIHJlcCgiTm9ybWFsIiwgNSksICAgICAjIFR1bW9yIFN1cHByZXNzb3IKICAgIHJlcCgiTm9ybWFsIiwgNyksICAgICAjIEhvbWVvc3Rhc2lzCiAgICByZXAoIk1hbGlnbmFudCIsIDcpLCAgIyBUaDIvTWVtb3J5IENvcmUKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAjIFByb2xpZmVyYXRpb24KICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAjIElGTgogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICMgTWV0YWJvbGlzbQogICAgcmVwKCJNYWxpZ25hbnQiLCAxKSwgICMgQ3l0b3RveGljCiAgICByZXAoIk1hbGlnbmFudCIsIDEpICAgIyBUZXJtaW5hbCBFZmZlY3RvcgogICksCiAgRnVuY3Rpb24gPSBjKAogICAgcmVwKCJPbmNvZ2VuaWMiLCA3KSwKICAgIHJlcCgiQVAtMSBzaWduYWxpbmciLCAyKSwKICAgIHJlcCgiU3RyZXNzIFJlc3BvbnNlIiwgMyksCiAgICByZXAoIkluZmxhbW1hdG9yeS9ORi1rQiIsIDUpLAogICAgcmVwKCJORkFUIHNpZ25hbGluZyIsIDIpLAogICAgcmVwKCJUdW1vciBTdXBwcmVzc29yIiwgNSksCiAgICByZXAoIk5vcm1hbCBIb21lb3N0YXNpcyIsIDcpLAogICAgcmVwKCJUaDIvTWVtb3J5IENvcmUiLCA3KSwKICAgIHJlcCgiUHJvbGlmZXJhdGlvbiIsIDIpLAogICAgcmVwKCJJRk4gUmVzcG9uc2UiLCAzKSwKICAgIHJlcCgiTWV0YWJvbGlzbSIsIDIpLAogICAgcmVwKCJDeXRvdG94aWMiLCAxKSwKICAgIHJlcCgiVGVybWluYWwgRWZmZWN0b3IiLCAxKQogICksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAyLiBFeHRyYWN0IFRGIGFjdGl2aXR5IG1hdHJpeCBmcm9tIFNldXJhdAojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Cm1hdF9zY2FsZWQgPC0gdHJ5Q2F0Y2goCiAgU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJzY2FsZS5kYXRhIiksCiAgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMCikKbWF0X2RhdGEgPC0gU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJkYXRhIikKbWF0X3VzZSA8LSBpZiAoIWlzLm51bGwobWF0X3NjYWxlZCkgJiYgbnJvdyhtYXRfc2NhbGVkKSA+IDApIG1hdF9zY2FsZWQgZWxzZSBtYXRfZGF0YQoKIyBGaWx0ZXIgZm9yIFRGcyBwcmVzZW50IGluIFNldXJhdAphdmFpbGFibGVfdGZzIDwtIGludGVyc2VjdCh0Zl9tZXRhJFRGLCByb3duYW1lcyhtYXRfdXNlKSkKbWF0X3VzZSA8LSBtYXRfdXNlW2F2YWlsYWJsZV90ZnMsICwgZHJvcCA9IEZBTFNFXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDMuIEF2ZXJhZ2UgcGVyIGNsdXN0ZXIgJiB6LXNjb3JlCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY2x1c3RlcnMgPC0gYXMuZmFjdG9yKHNldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzKQphdmdfbWF0IDwtIHNhcHBseShsZXZlbHMoY2x1c3RlcnMpLCBmdW5jdGlvbihjbCkgewogIE1hdHJpeDo6cm93TWVhbnMobWF0X3VzZVssIGNsdXN0ZXJzID09IGNsLCBkcm9wID0gRkFMU0VdKQp9KQpjb2xuYW1lcyhhdmdfbWF0KSA8LSBsZXZlbHMoY2x1c3RlcnMpCgphdmdfbWF0X3ogPC0gdChzY2FsZSh0KGF2Z19tYXQpKSkKYXZnX21hdF96W2lzLm5hKGF2Z19tYXRfeildIDwtIDAKCiMgQWxpZ24gdGZfbWV0YSBvcmRlcgp0Zl9tZXRhX2ZpbHRlcmVkIDwtIHRmX21ldGFbbWF0Y2gocm93bmFtZXMoYXZnX21hdF96KSwgdGZfbWV0YSRURiksIF0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA0LiBDb2x1bW4gJiByb3cgYW5ub3RhdGlvbnMKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjbHVzdGVyX3N0YXR1cyA8LSBpZmVsc2UoY29sbmFtZXMoYXZnX21hdF96KSAlaW4lIGMoIjMiLCIxMCIpLCAiTm9ybWFsIENENCBUIGNlbGxzIiwgIk1hbGlnbmFudCBDRDQgVCBjZWxscyIpCmhhX2NvbCA8LSBIZWF0bWFwQW5ub3RhdGlvbigKICBDZWxsX1N0YXRlID0gY2x1c3Rlcl9zdGF0dXMsCiAgY29sID0gbGlzdChDZWxsX1N0YXRlID0gYygiTm9ybWFsIENENCBUIGNlbGxzIiA9ICIjNERBRjRBIiwgIk1hbGlnbmFudCBDRDQgVCBjZWxscyIgPSAiI0U0MUExQyIpKSwKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJsZWZ0IgopCgojIFJvdyBhbm5vdGF0aW9uCmZ1bmN0aW9uX2NvbG9ycyA8LSBjKAogICJPbmNvZ2VuaWMiID0gIiNGRjdGMDAiLAogICJBUC0xIHNpZ25hbGluZyIgPSAiI0ZGQTUwMCIsCiAgIlN0cmVzcyBSZXNwb25zZSIgPSAiI0ZGRDcwMCIsCiAgIkluZmxhbW1hdG9yeS9ORi1rQiIgPSAiIzFFOTBGRiIsCiAgIk5GQVQgc2lnbmFsaW5nIiA9ICIjNDE2OUUxIiwKICAiVHVtb3IgU3VwcHJlc3NvciIgPSAiIzM3N0VCOCIsCiAgIk5vcm1hbCBIb21lb3N0YXNpcyIgPSAiIzREQUY0QSIsCiAgIlRoMi9NZW1vcnkgQ29yZSIgPSAiIzk4NEVBMyIsCiAgIlByb2xpZmVyYXRpb24iID0gIiNFNDFBMUMiLAogICJJRk4gUmVzcG9uc2UiID0gIiMwMENFRDEiLAogICJNZXRhYm9saXNtIiA9ICIjQTY1NjI4IiwKICAiQ3l0b3RveGljIiA9ICIjRjc4MUJGIiwKICAiVGVybWluYWwgRWZmZWN0b3IiID0gIiM4MDAwODAiCikKCmhhX3JvdyA8LSByb3dBbm5vdGF0aW9uKAogIENvbmRpdGlvbiA9IHRmX21ldGFfZmlsdGVyZWQkQ29uZGl0aW9uLAogIEZ1bmN0aW9uID0gdGZfbWV0YV9maWx0ZXJlZCRGdW5jdGlvbiwKICBjb2wgPSBsaXN0KAogICAgQ29uZGl0aW9uID0gYygiTm9ybWFsIj0iIzREQUY0QSIsIk1hbGlnbmFudCI9IiNFNDFBMUMiKSwKICAgIEZ1bmN0aW9uID0gZnVuY3Rpb25fY29sb3JzCiAgKSwKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJib3R0b20iCikKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA1LiBIZWF0bWFwIGNvbG9ycyAmIHBsb3R0aW5nCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC0zLDAsMyksIGMoIiMzMTM2OTUiLCJ3aGl0ZSIsIiNBNTAwMjYiKSkKCmh0IDwtIEhlYXRtYXAoCiAgYXZnX21hdF96LAogIG5hbWUgPSAiVEYgYWN0aXZpdHkgKHopIiwKICBjb2wgPSBjb2xfZnVuLAogIHRvcF9hbm5vdGF0aW9uID0gaGFfY29sLAogIGxlZnRfYW5ub3RhdGlvbiA9IGhhX3JvdywKICBjb2x1bW5fc3BsaXQgPSBjbHVzdGVyX3N0YXR1cywKICByb3dfc3BsaXQgPSB0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uLAogIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCAgICAgIyBzaG93IGJpb2xvZ2ljYWwgc3BsaXQKICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwKICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTApLAogIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksCiAgY29sdW1uX3RpdGxlID0gIkZ1bmN0aW9uYWwgVEYgTW9kdWxlcyBpbiBTw6l6YXJ5IFN5bmRyb21lIiwKICByb3dfdGl0bGVfcm90ID0gMCwKICByb3dfdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gOSwgZm9udGZhY2UgPSAiYm9sZCIpLAogIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdChkaXJlY3Rpb24gPSAidmVydGljYWwiKQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNi4gU2F2ZSBwbG90cwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXAucGRmIiwgd2lkdGg9MTIsIGhlaWdodD0xMCkKZHJhdyhodCwgbWVyZ2VfbGVnZW5kPVRSVUUpCmRldi5vZmYoKQoKcG5nKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfVEZfSGVhdG1hcC5wbmciLCB3aWR0aD0xMiozMDAsIGhlaWdodD0xMCozMDAsIHJlcz0zMDApCmRyYXcoaHQsIG1lcmdlX2xlZ2VuZD1UUlVFKQpkZXYub2ZmKCkKCmRyYXcoaHQsIG1lcmdlX2xlZ2VuZD1UUlVFKQoKYGBgCgoKCiMgRmlndXJlIE1hbGlnbmFudCBjb21wbGV4IGhlYXRtYXAgY2h1bmsgKHdpdGggNDQtNDcgVEZzLCBsaXRlcmF0dXJlLWJhc2VkIHBhbmVsKQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEyfQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIExJQlJBUklFUwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDEuIERlZmluZSBURiBwYW5lbCBtZXRhZGF0YSAoS0VHRyBBbGlnbmVkKQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnRmX21ldGEgPC0gZGF0YS5mcmFtZSgKICBURiA9IGMoCiAgICAjIC0tLSAxLiBHZW5lcmFsIE1hbGlnbmFuY3kgJiBTdHJlc3MgLS0tCiAgICAiTVlDIiwiRTJGNCIsIlRXSVNUMSIsIklSRjQiLAogICAgIkhTRjEiLCJORkUyTDIiLCJTUkVCRjIiLAogICAgCiAgICAjIC0tLSAyLiBUQ1IgU2lnbmFsaW5nIFRyaWFkIC0tLQogICAgIkpVTkIiLCJGT1MiLCJGT1NMMSIsICAgICAgICAgICAjIEFQLTEKICAgICJORkFUQzEiLCJORkFUQzIiLCAgICAgICAgICAgICAgIyBORkFUCiAgICAiUkVMQSIsIlJFTCIsIk5GS0IxIiwiSVJGMSIsIk5DT0EyIiwgIyBORi1rQgogICAgCiAgICAjIC0tLSAzLiBUaDIgLyBKQUstU1RBVCBDb3JlIC0tLQogICAgIkdBVEEzIiwiU1RBVDYiLCJCQVRGIiwiRk9YUDMiLCJTVEFUMyIsIlNUQVQ1QiIsCiAgICAKICAgICMgLS0tIDQuIERpZmZlcmVudGlhdGlvbiBIaWVyYXJjaHkgLS0tCiAgICAiVENGNyIsIkxFRjEiLCJNWUIiLCAgICAgICAgICAgICMgU3RlbS1saWtlIFByb2dlbml0b3IKICAgICJFMkYxIiwiRk9YTTEiLCAgICAgICAgICAgICAgICAgIyBQcm9saWZlcmF0aW9uIChDeWNsaW5nKQogICAgIlBSRE0xIiwgICAgICAgICAgICAgICAgICAgICAgICAjIFRlcm1pbmFsIEVmZmVjdG9yCiAgICAKICAgICMgLS0tIDUuIEtFR0cgQUxJR05FRCBDQVRFR09SSUVTIChORVcpIC0tLQogICAgIkVPTUVTIiwiVEJYMjEiLCJSVU5YMyIsICAgICAgICAjIE5LLWxpa2UgQ3l0b3RveGljaXR5IChDbHVzdGVyIDEsOSkKICAgICJSRlg1IiwiQ1JFQjEiLCAgICAgICAgICAgICAgICAgIyBBbnRpZ2VuIFByZXNlbnRhdGlvbiAvIE1IQy1JSSAoQ2x1c3RlciAwKQogICAgIktMRjQiLCJFVFMxIiwiU01BRDMiLCAgICAgICAgICAjIE1pZ3JhdGlvbiAvIENlbGwgQWRoZXNpb24gKENBTXMpCiAgICAKICAgICMgLS0tIDYuIE1pY3JvZW52aXJvbm1lbnQgLS0tCiAgICAiU1RBVDEiLCJTVEFUMiIsIklSRjkiLCAgICAgICAgICMgSUZOIFJlc3BvbnNlCiAgICAiSElGMUEiLCJTUkVCRjEiLCAgICAgICAgICAgICAgICMgTWV0YWJvbGlzbQogICAgCiAgICAjIC0tLSA3LiBOb3JtYWwgQmFzZWxpbmUgLyBUdW1vciBTdXBwcmVzc29ycyAtLS0KICAgICJGT1hPMSIsIkZPWE80IiwiWkVCMSIsIkJBQ0gyIiwKICAgICJUQ0YzIiwiQkNMMTFBIiwiTkVVUk9EMSIsIk1FRjJCIiwiUEJYMiIsIklSRjMiLCJCQ0w2IgogICksCiAgQ29uZGl0aW9uID0gYygKICAgIHJlcCgiTWFsaWduYW50IiwgNCksICAgIyBPbmNvZ2VuaWMKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBTdHJlc3MKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIEFQLTEKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAgIyBORkFUCiAgICByZXAoIk1hbGlnbmFudCIsIDUpLCAgICMgTkYta0IKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCA2KSwgICAjIFRoMiAvIEpBSy1TVEFUIENvcmUKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIFN0ZW0tbGlrZSBQcm9nZW5pdG9yCiAgICByZXAoIk1hbGlnbmFudCIsIDIpLCAgICMgUHJvbGlmZXJhdGlvbgogICAgcmVwKCJNYWxpZ25hbnQiLCAxKSwgICAjIFRlcm1pbmFsIEVmZmVjdG9yCiAgICAKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBOSy1saWtlIEN5dG90b3hpY2l0eQogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIEFudGlnZW4gUHJlc2VudGF0aW9uCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgTWlncmF0aW9uIC8gQWRoZXNpb24KICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIElGTgogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIE1ldGFib2xpc20KICAgIAogICAgcmVwKCJOb3JtYWwiLCA0KSwgICAgICAjIFR1bW9yIFN1cHByZXNzb3IKICAgIHJlcCgiTm9ybWFsIiwgNykgICAgICAgIyBIb21lb3N0YXNpcwogICksCiAgRnVuY3Rpb24gPSBjKAogICAgcmVwKCJPbmNvZ2VuaWMiLCA0KSwKICAgIHJlcCgiU3RyZXNzIFJlc3BvbnNlIiwgMyksCiAgICAKICAgIHJlcCgiQVAtMSBTaWduYWxpbmciLCAzKSwKICAgIHJlcCgiTkZBVCBTaWduYWxpbmciLCAyKSwKICAgIHJlcCgiSW5mbGFtbWF0b3J5L05GLWtCIiwgNSksCiAgICAKICAgIHJlcCgiVGgyIC8gSkFLLVNUQVQgQ29yZSIsIDYpLAogICAgCiAgICByZXAoIlN0ZW0tbGlrZSBQcm9nZW5pdG9yIiwgMyksCiAgICByZXAoIlByb2xpZmVyYXRpb24iLCAyKSwKICAgIHJlcCgiVGVybWluYWwgRWZmZWN0b3IiLCAxKSwKICAgIAogICAgcmVwKCJOSy1saWtlIEN5dG90b3hpY2l0eSIsIDMpLCAgICMgS0VHRyBhbGlnbmVkCiAgICByZXAoIkFudGlnZW4gUHJlc2VudGF0aW9uIiwgMiksICAgIyBLRUdHIGFsaWduZWQKICAgIHJlcCgiTWlncmF0aW9uIC8gQWRoZXNpb24iLCAzKSwgICAjIEtFR0cgYWxpZ25lZAogICAgCiAgICByZXAoIklGTiBSZXNwb25zZSIsIDMpLAogICAgcmVwKCJNZXRhYm9saXNtIiwgMiksCiAgICAKICAgIHJlcCgiVHVtb3IgU3VwcHJlc3NvciIsIDQpLAogICAgcmVwKCJOb3JtYWwgSG9tZW9zdGFzaXMiLCA3KQogICksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKCiMgTG9jayBpbiB0aGUgcHJlY2lzZSBvcmRlciBvZiB0aGUgYmxvY2tzIGZyb20gdG9wIHRvIGJvdHRvbQpkZXNpcmVkX29yZGVyIDwtIGMoCiAgIk9uY29nZW5pYyIsIAogICJTdHJlc3MgUmVzcG9uc2UiLAogICJBUC0xIFNpZ25hbGluZyIsIAogICJORkFUIFNpZ25hbGluZyIsIAogICJJbmZsYW1tYXRvcnkvTkYta0IiLAogICJUaDIgLyBKQUstU1RBVCBDb3JlIiwKICAiU3RlbS1saWtlIFByb2dlbml0b3IiLCAKICAiUHJvbGlmZXJhdGlvbiIsICAgICAgICAKICAiVGVybWluYWwgRWZmZWN0b3IiLAogICJOSy1saWtlIEN5dG90b3hpY2l0eSIsICAgIyBQbGFjZWQgaGVyZSB0byBzaG93IGVmZmVjdG9yIHN0YXRlCiAgIkFudGlnZW4gUHJlc2VudGF0aW9uIiwgICAjIERpcmVjdGx5IGxpbmtzIHRvIE1IQy1JSSBoaWdoIGNsdXN0ZXIKICAiTWlncmF0aW9uIC8gQWRoZXNpb24iLCAgICMgTGlua3MgdG8gQ0FNcyBLRUdHIHBhdGh3YXkKICAiSUZOIFJlc3BvbnNlIiwKICAiTWV0YWJvbGlzbSIsCiAgIlR1bW9yIFN1cHByZXNzb3IiLAogICJOb3JtYWwgSG9tZW9zdGFzaXMiCikKCnRmX21ldGEkRnVuY3Rpb24gPC0gZmFjdG9yKHRmX21ldGEkRnVuY3Rpb24sIGxldmVscyA9IGRlc2lyZWRfb3JkZXIpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMi4gRXh0cmFjdCBURiBhY3Rpdml0eSBtYXRyaXggZnJvbSBTZXVyYXQKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQptYXRfc2NhbGVkIDwtIHRyeUNhdGNoKAogIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAic2NhbGUuZGF0YSIpLAogIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTAopCm1hdF9kYXRhIDwtIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAiZGF0YSIpCm1hdF91c2UgPC0gaWYgKCFpcy5udWxsKG1hdF9zY2FsZWQpICYmIG5yb3cobWF0X3NjYWxlZCkgPiAwKSBtYXRfc2NhbGVkIGVsc2UgbWF0X2RhdGEKCiMgRmlsdGVyIGZvciBURnMgcHJlc2VudCBpbiBTZXVyYXQKYXZhaWxhYmxlX3RmcyA8LSBpbnRlcnNlY3QodGZfbWV0YSRURiwgcm93bmFtZXMobWF0X3VzZSkpCm1hdF91c2UgPC0gbWF0X3VzZVthdmFpbGFibGVfdGZzLCAsIGRyb3AgPSBGQUxTRV0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAzLiBBdmVyYWdlIHBlciBjbHVzdGVyICYgei1zY29yZQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmNsdXN0ZXJzIDwtIGFzLmZhY3RvcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykKYXZnX21hdCA8LSBzYXBwbHkobGV2ZWxzKGNsdXN0ZXJzKSwgZnVuY3Rpb24oY2wpIHsKICBNYXRyaXg6OnJvd01lYW5zKG1hdF91c2VbLCBjbHVzdGVycyA9PSBjbCwgZHJvcCA9IEZBTFNFXSkKfSkKY29sbmFtZXMoYXZnX21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQoKYXZnX21hdF96IDwtIHQoc2NhbGUodChhdmdfbWF0KSkpCmF2Z19tYXRfeltpcy5uYShhdmdfbWF0X3opXSA8LSAwCgojIEFsaWduIHRmX21ldGEgb3JkZXIgdG8gbWF0Y2ggbWF0X3VzZSBwcmVjaXNlbHkKcm93X29yZGVyX2lkeCA8LSBtYXRjaChyb3duYW1lcyhhdmdfbWF0X3opLCB0Zl9tZXRhJFRGKQp0Zl9tZXRhX2ZpbHRlcmVkIDwtIHRmX21ldGFbcm93X29yZGVyX2lkeCwgXQphdmdfbWF0X3ogPC0gYXZnX21hdF96W29yZGVyKHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24pLCBdCnRmX21ldGFfZmlsdGVyZWQgPC0gdGZfbWV0YV9maWx0ZXJlZFtvcmRlcih0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uKSwgXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDQuIENvbHVtbiAmIHJvdyBhbm5vdGF0aW9ucwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmNsdXN0ZXJfc3RhdHVzIDwtIGlmZWxzZShjb2xuYW1lcyhhdmdfbWF0X3opICVpbiUgYygiMyIsIjEwIiksICJOb3JtYWwgQ0Q0IFQgY2VsbHMiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIikKaGFfY29sIDwtIEhlYXRtYXBBbm5vdGF0aW9uKAogIENlbGxfU3RhdGUgPSBjbHVzdGVyX3N0YXR1cywKICBjb2wgPSBsaXN0KENlbGxfU3RhdGUgPSBjKCJOb3JtYWwgQ0Q0IFQgY2VsbHMiID0gIiM0REFGNEEiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIiA9ICIjRTQxQTFDIikpLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImxlZnQiCikKCiMgUm93IGFubm90YXRpb24gY29sb3JzCmZ1bmN0aW9uX2NvbG9ycyA8LSBjKAogICJPbmNvZ2VuaWMiID0gIiM4MDgwODAiLAogICJTdHJlc3MgUmVzcG9uc2UiID0gIiNGRkQ3MDAiLAogIAogICJBUC0xIFNpZ25hbGluZyIgPSAiI0ZGOEMwMCIsCiAgIk5GQVQgU2lnbmFsaW5nIiA9ICIjRkY0NTAwIiwKICAiSW5mbGFtbWF0b3J5L05GLWtCIiA9ICIjRTMxQTFDIiwKICAKICAiVGgyIC8gSkFLLVNUQVQgQ29yZSIgPSAiIzk4NEVBMyIsCiAgCiAgIlN0ZW0tbGlrZSBQcm9nZW5pdG9yIiA9ICIjRkYxNDkzIiwKICAiUHJvbGlmZXJhdGlvbiIgPSAiIzFFOTBGRiIsCiAgIlRlcm1pbmFsIEVmZmVjdG9yIiA9ICIjODAwMDgwIiwKICAKICAjIE5ldyBLRUdHIGNhdGVnb3JpZXMKICAiTkstbGlrZSBDeXRvdG94aWNpdHkiID0gIiNGNzgxQkYiLCAgICMgUGluawogICJBbnRpZ2VuIFByZXNlbnRhdGlvbiIgPSAiIzY2Q0RBQSIsICAgIyBNZWRpdW0gQXF1YW1hcmluZQogICJNaWdyYXRpb24gLyBBZGhlc2lvbiIgPSAiIzhBMkJFMiIsICAgIyBCbHVlIFZpb2xldAogIAogICJJRk4gUmVzcG9uc2UiID0gIiMwMENFRDEiLAogICJNZXRhYm9saXNtIiA9ICIjQTY1NjI4IiwKICAKICAiVHVtb3IgU3VwcHJlc3NvciIgPSAiIzM3N0VCOCIsCiAgIk5vcm1hbCBIb21lb3N0YXNpcyIgPSAiIzREQUY0QSIKKQoKaGFfcm93IDwtIHJvd0Fubm90YXRpb24oCiAgQ29uZGl0aW9uID0gdGZfbWV0YV9maWx0ZXJlZCRDb25kaXRpb24sCiAgRnVuY3Rpb24gPSB0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uLAogIGNvbCA9IGxpc3QoCiAgICBDb25kaXRpb24gPSBjKCJOb3JtYWwiPSIjNERBRjRBIiwiTWFsaWduYW50Ij0iI0U0MUExQyIpLAogICAgRnVuY3Rpb24gPSBmdW5jdGlvbl9jb2xvcnMKICApLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImJvdHRvbSIKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDUuIEhlYXRtYXAgY29sb3JzICYgcGxvdHRpbmcKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjb2xfZnVuIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTMsMCwzKSwgYygiIzMxMzY5NSIsIndoaXRlIiwiI0E1MDAyNiIpKQoKaHQgPC0gSGVhdG1hcCgKICBhdmdfbWF0X3osCiAgbmFtZSA9ICJURiBhY3Rpdml0eSAoeikiLAogIGNvbCA9IGNvbF9mdW4sCiAgdG9wX2Fubm90YXRpb24gPSBoYV9jb2wsCiAgbGVmdF9hbm5vdGF0aW9uID0gaGFfcm93LAogIGNvbHVtbl9zcGxpdCA9IGNsdXN0ZXJfc3RhdHVzLAogIHJvd19zcGxpdCA9IHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24sCiAgY2x1c3Rlcl9yb3dfc2xpY2VzID0gRkFMU0UsICAgIAogIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCAgICAgICAgICAKICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwKICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTApLAogIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksCiAgY29sdW1uX3RpdGxlID0gIkZ1bmN0aW9uYWwgVEYgTW9kdWxlcyBpbiBTw6l6YXJ5IFN5bmRyb21lIiwKICByb3dfdGl0bGVfcm90ID0gMCwKICByb3dfdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gOCwgZm9udGZhY2UgPSAiYm9sZCIpLAogIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdChkaXJlY3Rpb24gPSAidmVydGljYWwiKQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNi4gU2F2ZSBwbG90cwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfS0VHRy5wZGYiLCB3aWR0aD0xNCwgaGVpZ2h0PTE0KQpkcmF3KGh0LCBtZXJnZV9sZWdlbmQ9VFJVRSkKZGV2Lm9mZigpCmRyYXcoaHQsIG1lcmdlX2xlZ2VuZD1UUlVFKQpgYGAKCiMgRmlndXJlIE1hbGlnbmFudCBjb21wbGV4IGhlYXRtYXAgY2h1bmsgKHdpdGggNDQtNDcgVEZzLCBsaXRlcmF0dXJlLWJhc2VkIHBhbmVsKQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEyfQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIExJQlJBUklFUwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDEuIERlZmluZSBURiBwYW5lbCBtZXRhZGF0YSAoS0VHRyBBbGlnbmVkKQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnRmX21ldGEgPC0gZGF0YS5mcmFtZSgKICBURiA9IGMoCiAgICAjIC0tLSAxLiBHZW5lcmFsIE1hbGlnbmFuY3kgJiBTdHJlc3MgLS0tCiAgICAiTVlDIiwiRTJGNCIsIlRXSVNUMSIsIklSRjQiLAogICAgIkhTRjEiLCJORkUyTDIiLCJTUkVCRjIiLAogICAgCiAgICAjIC0tLSAyLiBUQ1IgU2lnbmFsaW5nIFRyaWFkIC0tLQogICAgIkpVTkIiLCJGT1MiLCJGT1NMMSIsICAgICAgICAgICAjIEFQLTEKICAgICJORkFUQzEiLCJORkFUQzIiLCAgICAgICAgICAgICAgIyBORkFUCiAgICAiUkVMQSIsIlJFTCIsIk5GS0IxIiwiSVJGMSIsIk5DT0EyIiwgIyBORi1rQgogICAgCiAgICAjIC0tLSAzLiBUaDIgLyBKQUstU1RBVCBDb3JlIC0tLQogICAgIkdBVEEzIiwiU1RBVDYiLCJCQVRGIiwiRk9YUDMiLCJTVEFUMyIsIlNUQVQ1QiIsCiAgICAKICAgICMgLS0tIDQuIERpZmZlcmVudGlhdGlvbiBIaWVyYXJjaHkgLS0tCiAgICAiVENGNyIsIkxFRjEiLCJNWUIiLCAgICAgICAgICAgICMgU3RlbS1saWtlIFByb2dlbml0b3IKICAgICJFMkYxIiwiRk9YTTEiLCAgICAgICAgICAgICAgICAgIyBQcm9saWZlcmF0aW9uIChDeWNsaW5nKQogICAgIlBSRE0xIiwgICAgICAgICAgICAgICAgICAgICAgICAjIFRlcm1pbmFsIEVmZmVjdG9yCiAgICAKICAgICMgLS0tIDUuIEtFR0cgQUxJR05FRCBDQVRFR09SSUVTIChORVcpIC0tLQogICAgIkVPTUVTIiwiVEJYMjEiLCJSVU5YMyIsICAgICAgICAjIE5LLWxpa2UgQ3l0b3RveGljaXR5IChDbHVzdGVyIDEsOSkKICAgICJSRlg1IiwiQ1JFQjEiLCAgICAgICAgICAgICAgICAgIyBBbnRpZ2VuIFByZXNlbnRhdGlvbiAvIE1IQy1JSSAoQ2x1c3RlciAwKQogICAgIktMRjQiLCJFVFMxIiwiU01BRDMiLCAgICAgICAgICAjIE1pZ3JhdGlvbiAvIENlbGwgQWRoZXNpb24gKENBTXMpCiAgICAKICAgICMgLS0tIDYuIE1pY3JvZW52aXJvbm1lbnQgLS0tCiAgICAiU1RBVDEiLCJTVEFUMiIsIklSRjkiLCAgICAgICAgICMgSUZOIFJlc3BvbnNlCiAgICAiSElGMUEiLCJTUkVCRjEiLCAgICAgICAgICAgICAgICMgTWV0YWJvbGlzbQogICAgCiAgICAjIC0tLSA3LiBOb3JtYWwgQmFzZWxpbmUgLyBUdW1vciBTdXBwcmVzc29ycyAtLS0KICAgICJGT1hPMSIsIkZPWE80IiwiWkVCMSIsIkJBQ0gyIiwKICAgICJUQ0YzIiwiQkNMMTFBIiwiTkVVUk9EMSIsIk1FRjJCIiwiUEJYMiIsIklSRjMiLCJCQ0w2IgogICksCiAgQ29uZGl0aW9uID0gYygKICAgIHJlcCgiTWFsaWduYW50IiwgNCksICAgIyBPbmNvZ2VuaWMKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBTdHJlc3MKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIEFQLTEKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAgIyBORkFUCiAgICByZXAoIk1hbGlnbmFudCIsIDUpLCAgICMgTkYta0IKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCA2KSwgICAjIFRoMiAvIEpBSy1TVEFUIENvcmUKICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIFN0ZW0tbGlrZSBQcm9nZW5pdG9yCiAgICByZXAoIk1hbGlnbmFudCIsIDIpLCAgICMgUHJvbGlmZXJhdGlvbgogICAgcmVwKCJNYWxpZ25hbnQiLCAxKSwgICAjIFRlcm1pbmFsIEVmZmVjdG9yCiAgICAKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBOSy1saWtlIEN5dG90b3hpY2l0eQogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIEFudGlnZW4gUHJlc2VudGF0aW9uCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgTWlncmF0aW9uIC8gQWRoZXNpb24KICAgIAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIElGTgogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIE1ldGFib2xpc20KICAgIAogICAgcmVwKCJOb3JtYWwiLCA0KSwgICAgICAjIFR1bW9yIFN1cHByZXNzb3IKICAgIHJlcCgiTm9ybWFsIiwgNykgICAgICAgIyBIb21lb3N0YXNpcwogICksCiAgRnVuY3Rpb24gPSBjKAogICAgcmVwKCJPbmNvZ2VuaWMiLCA0KSwKICAgIHJlcCgiU3RyZXNzIFJlc3BvbnNlIiwgMyksCiAgICAKICAgIHJlcCgiQVAtMSBTaWduYWxpbmciLCAzKSwKICAgIHJlcCgiTkZBVCBTaWduYWxpbmciLCAyKSwKICAgIHJlcCgiSW5mbGFtbWF0b3J5L05GLWtCIiwgNSksCiAgICAKICAgIHJlcCgiVGgyIC8gSkFLLVNUQVQgQ29yZSIsIDYpLAogICAgCiAgICByZXAoIlN0ZW0tbGlrZSBQcm9nZW5pdG9yIiwgMyksCiAgICByZXAoIlByb2xpZmVyYXRpb24iLCAyKSwKICAgIHJlcCgiVGVybWluYWwgRWZmZWN0b3IiLCAxKSwKICAgIAogICAgcmVwKCJOSy1saWtlIEN5dG90b3hpY2l0eSIsIDMpLCAgICMgS0VHRyBhbGlnbmVkCiAgICByZXAoIkFudGlnZW4gUHJlc2VudGF0aW9uIiwgMiksICAgIyBLRUdHIGFsaWduZWQKICAgIHJlcCgiTWlncmF0aW9uIC8gQWRoZXNpb24iLCAzKSwgICAjIEtFR0cgYWxpZ25lZAogICAgCiAgICByZXAoIklGTiBSZXNwb25zZSIsIDMpLAogICAgcmVwKCJNZXRhYm9saXNtIiwgMiksCiAgICAKICAgIHJlcCgiVHVtb3IgU3VwcHJlc3NvciIsIDQpLAogICAgcmVwKCJOb3JtYWwgSG9tZW9zdGFzaXMiLCA3KQogICksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKCiMgTG9jayBpbiB0aGUgcHJlY2lzZSBvcmRlciBvZiB0aGUgYmxvY2tzIGZyb20gdG9wIHRvIGJvdHRvbQpkZXNpcmVkX29yZGVyIDwtIGMoCiAgIk9uY29nZW5pYyIsIAogICJTdHJlc3MgUmVzcG9uc2UiLAogICJBUC0xIFNpZ25hbGluZyIsIAogICJORkFUIFNpZ25hbGluZyIsIAogICJJbmZsYW1tYXRvcnkvTkYta0IiLAogICJUaDIgLyBKQUstU1RBVCBDb3JlIiwKICAiU3RlbS1saWtlIFByb2dlbml0b3IiLCAKICAiUHJvbGlmZXJhdGlvbiIsICAgICAgICAKICAiVGVybWluYWwgRWZmZWN0b3IiLAogICJOSy1saWtlIEN5dG90b3hpY2l0eSIsICAgIyBQbGFjZWQgaGVyZSB0byBzaG93IGVmZmVjdG9yIHN0YXRlCiAgIkFudGlnZW4gUHJlc2VudGF0aW9uIiwgICAjIERpcmVjdGx5IGxpbmtzIHRvIE1IQy1JSSBoaWdoIGNsdXN0ZXIKICAiTWlncmF0aW9uIC8gQWRoZXNpb24iLCAgICMgTGlua3MgdG8gQ0FNcyBLRUdHIHBhdGh3YXkKICAiSUZOIFJlc3BvbnNlIiwKICAiTWV0YWJvbGlzbSIsCiAgIlR1bW9yIFN1cHByZXNzb3IiLAogICJOb3JtYWwgSG9tZW9zdGFzaXMiCikKCnRmX21ldGEkRnVuY3Rpb24gPC0gZmFjdG9yKHRmX21ldGEkRnVuY3Rpb24sIGxldmVscyA9IGRlc2lyZWRfb3JkZXIpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMi4gRXh0cmFjdCBURiBhY3Rpdml0eSBtYXRyaXggZnJvbSBTZXVyYXQKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQptYXRfc2NhbGVkIDwtIHRyeUNhdGNoKAogIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAic2NhbGUuZGF0YSIpLAogIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTAopCm1hdF9kYXRhIDwtIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAiZGF0YSIpCm1hdF91c2UgPC0gaWYgKCFpcy5udWxsKG1hdF9zY2FsZWQpICYmIG5yb3cobWF0X3NjYWxlZCkgPiAwKSBtYXRfc2NhbGVkIGVsc2UgbWF0X2RhdGEKCiMgRmlsdGVyIGZvciBURnMgcHJlc2VudCBpbiBTZXVyYXQKYXZhaWxhYmxlX3RmcyA8LSBpbnRlcnNlY3QodGZfbWV0YSRURiwgcm93bmFtZXMobWF0X3VzZSkpCm1hdF91c2UgPC0gbWF0X3VzZVthdmFpbGFibGVfdGZzLCAsIGRyb3AgPSBGQUxTRV0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAzLiBBdmVyYWdlIHBlciBjbHVzdGVyICYgei1zY29yZQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmNsdXN0ZXJzIDwtIGFzLmZhY3RvcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykKYXZnX21hdCA8LSBzYXBwbHkobGV2ZWxzKGNsdXN0ZXJzKSwgZnVuY3Rpb24oY2wpIHsKICBNYXRyaXg6OnJvd01lYW5zKG1hdF91c2VbLCBjbHVzdGVycyA9PSBjbCwgZHJvcCA9IEZBTFNFXSkKfSkKY29sbmFtZXMoYXZnX21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQoKYXZnX21hdF96IDwtIHQoc2NhbGUodChhdmdfbWF0KSkpCmF2Z19tYXRfeltpcy5uYShhdmdfbWF0X3opXSA8LSAwCgojIEFsaWduIHRmX21ldGEgb3JkZXIgdG8gbWF0Y2ggbWF0X3VzZSBwcmVjaXNlbHkKcm93X29yZGVyX2lkeCA8LSBtYXRjaChyb3duYW1lcyhhdmdfbWF0X3opLCB0Zl9tZXRhJFRGKQp0Zl9tZXRhX2ZpbHRlcmVkIDwtIHRmX21ldGFbcm93X29yZGVyX2lkeCwgXQphdmdfbWF0X3ogPC0gYXZnX21hdF96W29yZGVyKHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24pLCBdCnRmX21ldGFfZmlsdGVyZWQgPC0gdGZfbWV0YV9maWx0ZXJlZFtvcmRlcih0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uKSwgXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDQuIENvbHVtbiAmIHJvdyBhbm5vdGF0aW9ucwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmNsdXN0ZXJfc3RhdHVzIDwtIGlmZWxzZShjb2xuYW1lcyhhdmdfbWF0X3opICVpbiUgYygiMyIsIjEwIiksICJOb3JtYWwgQ0Q0IFQgY2VsbHMiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIikKaGFfY29sIDwtIEhlYXRtYXBBbm5vdGF0aW9uKAogIENlbGxfU3RhdGUgPSBjbHVzdGVyX3N0YXR1cywKICBjb2wgPSBsaXN0KENlbGxfU3RhdGUgPSBjKCJOb3JtYWwgQ0Q0IFQgY2VsbHMiID0gIiM0REFGNEEiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIiA9ICIjRTQxQTFDIikpLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImxlZnQiCikKCiMgUm93IGFubm90YXRpb24gY29sb3JzCmZ1bmN0aW9uX2NvbG9ycyA8LSBjKAogICJPbmNvZ2VuaWMiID0gIiM4MDgwODAiLAogICJTdHJlc3MgUmVzcG9uc2UiID0gIiNGRkQ3MDAiLAogIAogICJBUC0xIFNpZ25hbGluZyIgPSAiI0ZGOEMwMCIsCiAgIk5GQVQgU2lnbmFsaW5nIiA9ICIjRkY0NTAwIiwKICAiSW5mbGFtbWF0b3J5L05GLWtCIiA9ICIjRTMxQTFDIiwKICAKICAiVGgyIC8gSkFLLVNUQVQgQ29yZSIgPSAiIzk4NEVBMyIsCiAgCiAgIlN0ZW0tbGlrZSBQcm9nZW5pdG9yIiA9ICIjRkYxNDkzIiwKICAiUHJvbGlmZXJhdGlvbiIgPSAiIzFFOTBGRiIsCiAgIlRlcm1pbmFsIEVmZmVjdG9yIiA9ICIjODAwMDgwIiwKICAKICAjIE5ldyBLRUdHIGNhdGVnb3JpZXMKICAiTkstbGlrZSBDeXRvdG94aWNpdHkiID0gIiNGNzgxQkYiLCAgICMgUGluawogICJBbnRpZ2VuIFByZXNlbnRhdGlvbiIgPSAiIzY2Q0RBQSIsICAgIyBNZWRpdW0gQXF1YW1hcmluZQogICJNaWdyYXRpb24gLyBBZGhlc2lvbiIgPSAiIzhBMkJFMiIsICAgIyBCbHVlIFZpb2xldAogIAogICJJRk4gUmVzcG9uc2UiID0gIiMwMENFRDEiLAogICJNZXRhYm9saXNtIiA9ICIjQTY1NjI4IiwKICAKICAiVHVtb3IgU3VwcHJlc3NvciIgPSAiIzM3N0VCOCIsCiAgIk5vcm1hbCBIb21lb3N0YXNpcyIgPSAiIzREQUY0QSIKKQoKaGFfcm93IDwtIHJvd0Fubm90YXRpb24oCiAgQ29uZGl0aW9uID0gdGZfbWV0YV9maWx0ZXJlZCRDb25kaXRpb24sCiAgRnVuY3Rpb24gPSB0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uLAogIGNvbCA9IGxpc3QoCiAgICBDb25kaXRpb24gPSBjKCJOb3JtYWwiPSIjNERBRjRBIiwiTWFsaWduYW50Ij0iI0U0MUExQyIpLAogICAgRnVuY3Rpb24gPSBmdW5jdGlvbl9jb2xvcnMKICApLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImJvdHRvbSIKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDUuIEhlYXRtYXAgY29sb3JzICYgcGxvdHRpbmcKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjb2xfZnVuIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTMsMCwzKSwgYygiIzMxMzY5NSIsIndoaXRlIiwiI0E1MDAyNiIpKQoKaHQgPC0gSGVhdG1hcCgKICBhdmdfbWF0X3osCiAgbmFtZSA9ICJURiBhY3Rpdml0eSAoeikiLAogIGNvbCA9IGNvbF9mdW4sCiAgdG9wX2Fubm90YXRpb24gPSBoYV9jb2wsCiAgbGVmdF9hbm5vdGF0aW9uID0gaGFfcm93LAogIGNvbHVtbl9zcGxpdCA9IGNsdXN0ZXJfc3RhdHVzLAogIHJvd19zcGxpdCA9IHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24sCiAgY2x1c3Rlcl9yb3dfc2xpY2VzID0gRkFMU0UsICAgIAogIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCAgICAgICAgICAKICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwKICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTApLAogIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksCiAgY29sdW1uX3RpdGxlID0gIkZ1bmN0aW9uYWwgVEYgTW9kdWxlcyBpbiBTw6l6YXJ5IFN5bmRyb21lIiwKICByb3dfdGl0bGVfcm90ID0gMCwKICByb3dfdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gOCwgZm9udGZhY2UgPSAiYm9sZCIpLAogIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdChkaXJlY3Rpb24gPSAidmVydGljYWwiKQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNi4gU2F2ZSBwbG90cwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfS0VHRy5wZGYiLCB3aWR0aD0xNCwgaGVpZ2h0PTE0KQpkcmF3KGh0LCBtZXJnZV9sZWdlbmQ9VFJVRSkKZGV2Lm9mZigpCgpkcmF3KGh0LCBtZXJnZV9sZWdlbmQ9VFJVRSkKCmBgYAoKIyBGaWd1cmUgTWFsaWduYW50IGNvbXBsZXggaGVhdG1hcCBjaHVuayAod2l0aCA0NC00NyBURnMsIGxpdGVyYXR1cmUtYmFzZWQgcGFuZWwpCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTJ9CgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgTElCUkFSSUVTCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShTZXVyYXRPYmplY3QpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMS4gRGVmaW5lIFRGIHBhbmVsIG1ldGFkYXRhICgxOjEgVU1BUCBBbGlnbmVkKQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnRmX21ldGEgPC0gZGF0YS5mcmFtZSgKICBURiA9IGMoCiAgICAjIC0tLSBCYXNlbGluZSBNYWxpZ25hbmN5IC0tLSAKICAgICJNWUMiLCJFMkY0IiwiVFdJU1QxIiwiSVJGNCIsCiAgICAKICAgICMgLS0tIENsdXN0ZXJzIDIgJiA2OiBUaDItbGlrZSBDb3JlIC0tLSAKICAgICJHQVRBMyIsIlNUQVQ2IiwiQkFURiIsIkZPWFAzIiwiU1RBVDMiLCJTVEFUNUIiLAogICAgCiAgICAjIC0tLSBDbHVzdGVycyAxMSAmIDEyOiBQcm8taW5mbGFtbWF0b3J5ICYgU3RyZXNzIC0tLSAKICAgICJKVU5CIiwiRk9TIiwiRk9TTDEiLCAgICAgICAgICAgIyBBUC0xCiAgICAiUkVMQSIsIlJFTCIsIk5GS0IxIiwiSVJGMSIsIk5DT0EyIiwgIyBORi1rQgogICAgIk5GQVRDMSIsIk5GQVRDMiIsICAgICAgICAgICAgICAjIFRDUi9ORkFUCiAgICAiSFNGMSIsIk5GRTJMMiIsIlNSRUJGMiIsICAgICAgICMgU3RyZXNzCiAgICAKICAgICMgLS0tIENsdXN0ZXIgNDogSW5mbGFtbWF0b3J5LU1pZ3JhdG9yeSAtLS0KICAgICJLTEY0IiwiRVRTMSIsIlNNQUQzIiwKICAgIAogICAgIyAtLS0gQ2x1c3RlciA1OiBTdGVtLWxpa2UgLS0tIAogICAgIlRDRjciLCJMRUYxIiwiTVlCIiwgICAgICAgICAgICAKICAgIAogICAgIyAtLS0gQ2x1c3RlciA3OiBDeWNsaW5nIChHMi9NKSAtLS0gCiAgICAiRTJGMSIsIkZPWE0xIiwgICAgICAgICAgICAgICAgIAogICAgCiAgICAjIC0tLSBDbHVzdGVycyAxICYgOTogTkstbGlrZSAvIEN5dG90b3hpYyAtLS0gCiAgICAiRU9NRVMiLCJUQlgyMSIsIlJVTlgzIiwiUFJETTEiLCAgICAgICAgCiAgICAKICAgICMgLS0tIENsdXN0ZXIgMDogTUhDLUlJIEhpZ2ggLS0tIAogICAgIlJGWDUiLCJDUkVCMSIsICAgICAgICAgICAgICAgICAKICAgIAogICAgIyAtLS0gQ2x1c3RlciAxMzogSUZOIFN0aW11bGF0ZWQgLS0tIAogICAgIlNUQVQxIiwiU1RBVDIiLCJJUkY5IiwgICAgICAgICAKICAgIAogICAgIyAtLS0gQ2x1c3RlciA4OiBHbHljb2x5dGljL01ldGFib2xpYyAtLS0gCiAgICAiSElGMUEiLCJTUkVCRjEiLCAgICAgICAgICAgICAgIAogICAgCiAgICAjIC0tLSBDbHVzdGVycyAzICYgMTA6IE5vcm1hbCBDRDQgVCAtLS0gCiAgICAiRk9YTzEiLCJGT1hPNCIsIlpFQjEiLCJCQUNIMiIsCiAgICAiVENGMyIsIkJDTDExQSIsIk5FVVJPRDEiLCJNRUYyQiIsIlBCWDIiLCJJUkYzIiwiQkNMNiIKICApLAogIENvbmRpdGlvbiA9IGMoCiAgICByZXAoIk1hbGlnbmFudCIsIDQpLCAgICMgT25jb2dlbmljCiAgICByZXAoIk1hbGlnbmFudCIsIDYpLCAgICMgVGgyCiAgICByZXAoIk1hbGlnbmFudCIsIDEzKSwgICMgUHJvLWluZmxhbW1hdG9yeSAoQVAxLCBORmtCLCBORkFULCBTdHJlc3MpCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgTWlncmF0b3J5CiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgU3RlbS1saWtlCiAgICByZXAoIk1hbGlnbmFudCIsIDIpLCAgICMgQ3ljbGluZwogICAgcmVwKCJNYWxpZ25hbnQiLCA0KSwgICAjIE5LL0N5dG90b3hpYwogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIE1IQy1JSQogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIElGTgogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIEdseWNvbHl0aWMKICAgIHJlcCgiTm9ybWFsIiwgMTEpICAgICAgIyBOb3JtYWwKICApLAogIEZ1bmN0aW9uID0gYygKICAgIHJlcCgiT25jb2dlbmljIENvcmUiLCA0KSwKICAgIHJlcCgiVGgyLWxpa2UgQ29yZSAoQ2wuIDIsIDYpIiwgNiksCiAgICByZXAoIlByby1pbmZsYW1tYXRvcnkgKENsLiAxMSwgMTIpIiwgMTMpLAogICAgcmVwKCJJbmZsYW1tYXRvcnktTWlncmF0b3J5IChDbC4gNCkiLCAzKSwKICAgIHJlcCgiU3RlbS1saWtlIChDbC4gNSkiLCAzKSwKICAgIHJlcCgiQ3ljbGluZyBHMi9NIChDbC4gNykiLCAyKSwKICAgIHJlcCgiTkstbGlrZSBDeXRvdG94aWMgKENsLiAxLCA5KSIsIDQpLCAgIAogICAgcmVwKCJNSEMtSUkgSGlnaCAoQ2wuIDApIiwgMiksICAgCiAgICByZXAoIklGTiBTdGltdWxhdGVkIChDbC4gMTMpIiwgMyksCiAgICByZXAoIkdseWNvbHl0aWMvTWV0YWJvbGljIChDbC4gOCkiLCAyKSwKICAgIHJlcCgiTm9ybWFsIEhvbWVvc3Rhc2lzIChDbC4gMywgMTApIiwgMTEpCiAgKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQoKIyBMb2NrIGluIHRoZSBwcmVjaXNlIG9yZGVyIG9mIHRoZSBibG9ja3MKZGVzaXJlZF9vcmRlciA8LSBjKAogICJPbmNvZ2VuaWMgQ29yZSIsIAogICJUaDItbGlrZSBDb3JlIChDbC4gMiwgNikiLAogICJQcm8taW5mbGFtbWF0b3J5IChDbC4gMTEsIDEyKSIsIAogICJJbmZsYW1tYXRvcnktTWlncmF0b3J5IChDbC4gNCkiLCAKICAiU3RlbS1saWtlIChDbC4gNSkiLCAKICAiQ3ljbGluZyBHMi9NIChDbC4gNykiLCAgICAgICAgCiAgIk5LLWxpa2UgQ3l0b3RveGljIChDbC4gMSwgOSkiLCAgIAogICJNSEMtSUkgSGlnaCAoQ2wuIDApIiwgICAKICAiSUZOIFN0aW11bGF0ZWQgKENsLiAxMykiLAogICJHbHljb2x5dGljL01ldGFib2xpYyAoQ2wuIDgpIiwKICAiTm9ybWFsIEhvbWVvc3Rhc2lzIChDbC4gMywgMTApIgopCgp0Zl9tZXRhJEZ1bmN0aW9uIDwtIGZhY3Rvcih0Zl9tZXRhJEZ1bmN0aW9uLCBsZXZlbHMgPSBkZXNpcmVkX29yZGVyKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDIuIEV4dHJhY3QgVEYgYWN0aXZpdHkgbWF0cml4IGZyb20gU2V1cmF0CiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KbWF0X3NjYWxlZCA8LSB0cnlDYXRjaChTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheSA9ICJkb3JvdGhlYSIsIGxheWVyID0gInNjYWxlLmRhdGEiKSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQptYXRfZGF0YSA8LSBTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheSA9ICJkb3JvdGhlYSIsIGxheWVyID0gImRhdGEiKQptYXRfdXNlIDwtIGlmICghaXMubnVsbChtYXRfc2NhbGVkKSAmJiBucm93KG1hdF9zY2FsZWQpID4gMCkgbWF0X3NjYWxlZCBlbHNlIG1hdF9kYXRhCgphdmFpbGFibGVfdGZzIDwtIGludGVyc2VjdCh0Zl9tZXRhJFRGLCByb3duYW1lcyhtYXRfdXNlKSkKbWF0X3VzZSA8LSBtYXRfdXNlW2F2YWlsYWJsZV90ZnMsICwgZHJvcCA9IEZBTFNFXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDMuIEF2ZXJhZ2UgcGVyIGNsdXN0ZXIgJiB6LXNjb3JlCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY2x1c3RlcnMgPC0gYXMuZmFjdG9yKHNldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzKQphdmdfbWF0IDwtIHNhcHBseShsZXZlbHMoY2x1c3RlcnMpLCBmdW5jdGlvbihjbCkgTWF0cml4Ojpyb3dNZWFucyhtYXRfdXNlWywgY2x1c3RlcnMgPT0gY2wsIGRyb3AgPSBGQUxTRV0pKQpjb2xuYW1lcyhhdmdfbWF0KSA8LSBsZXZlbHMoY2x1c3RlcnMpCgphdmdfbWF0X3ogPC0gdChzY2FsZSh0KGF2Z19tYXQpKSkKYXZnX21hdF96W2lzLm5hKGF2Z19tYXRfeildIDwtIDAKCiMgQWxpZ24gdGZfbWV0YSBvcmRlciB0byBtYXRjaCBtYXRfdXNlIHByZWNpc2VseQpyb3dfb3JkZXJfaWR4IDwtIG1hdGNoKHJvd25hbWVzKGF2Z19tYXRfeiksIHRmX21ldGEkVEYpCnRmX21ldGFfZmlsdGVyZWQgPC0gdGZfbWV0YVtyb3dfb3JkZXJfaWR4LCBdCmF2Z19tYXRfeiA8LSBhdmdfbWF0X3pbb3JkZXIodGZfbWV0YV9maWx0ZXJlZCRGdW5jdGlvbiksIF0KdGZfbWV0YV9maWx0ZXJlZCA8LSB0Zl9tZXRhX2ZpbHRlcmVkW29yZGVyKHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24pLCBdCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNC4gQ29sdW1uICYgcm93IGFubm90YXRpb25zCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY2x1c3Rlcl9zdGF0dXMgPC0gaWZlbHNlKGNvbG5hbWVzKGF2Z19tYXRfeikgJWluJSBjKCIzIiwiMTAiKSwgIk5vcm1hbCBDRDQgVCBjZWxscyIsICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMiKQpoYV9jb2wgPC0gSGVhdG1hcEFubm90YXRpb24oQ2VsbF9TdGF0ZSA9IGNsdXN0ZXJfc3RhdHVzLCBjb2wgPSBsaXN0KENlbGxfU3RhdGUgPSBjKCJOb3JtYWwgQ0Q0IFQgY2VsbHMiID0gIiM0REFGNEEiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIiA9ICIjRTQxQTFDIikpLCBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJsZWZ0IikKCiMgSGFybW9uaXplZCBjb2xvciBwYWxldHRlCmZ1bmN0aW9uX2NvbG9ycyA8LSBjKAogICJPbmNvZ2VuaWMgQ29yZSIgPSAiIzgwODA4MCIsCiAgIlRoMi1saWtlIENvcmUgKENsLiAyLCA2KSIgPSAiIzk4NEVBMyIsCiAgIlByby1pbmZsYW1tYXRvcnkgKENsLiAxMSwgMTIpIiA9ICIjRTMxQTFDIiwKICAiSW5mbGFtbWF0b3J5LU1pZ3JhdG9yeSAoQ2wuIDQpIiA9ICIjOEEyQkUyIiwKICAiU3RlbS1saWtlIChDbC4gNSkiID0gIiNGRjE0OTMiLAogICJDeWNsaW5nIEcyL00gKENsLiA3KSIgPSAiIzFFOTBGRiIsCiAgIk5LLWxpa2UgQ3l0b3RveGljIChDbC4gMSwgOSkiID0gIiNGNzgxQkYiLCAgIAogICJNSEMtSUkgSGlnaCAoQ2wuIDApIiA9ICIjNjZDREFBIiwgICAKICAiSUZOIFN0aW11bGF0ZWQgKENsLiAxMykiID0gIiMwMENFRDEiLAogICJHbHljb2x5dGljL01ldGFib2xpYyAoQ2wuIDgpIiA9ICIjQTY1NjI4IiwKICAiTm9ybWFsIEhvbWVvc3Rhc2lzIChDbC4gMywgMTApIiA9ICIjNERBRjRBIgopCgpoYV9yb3cgPC0gcm93QW5ub3RhdGlvbihDb25kaXRpb24gPSB0Zl9tZXRhX2ZpbHRlcmVkJENvbmRpdGlvbiwgRnVuY3Rpb24gPSB0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uLCBjb2wgPSBsaXN0KENvbmRpdGlvbiA9IGMoIk5vcm1hbCI9IiM0REFGNEEiLCJNYWxpZ25hbnQiPSIjRTQxQTFDIiksIEZ1bmN0aW9uID0gZnVuY3Rpb25fY29sb3JzKSwgYW5ub3RhdGlvbl9uYW1lX3NpZGUgPSAiYm90dG9tIikKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA1LiBIZWF0bWFwIGNvbG9ycyAmIHBsb3R0aW5nCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC0zLDAsMyksIGMoIiMzMTM2OTUiLCJ3aGl0ZSIsIiNBNTAwMjYiKSkKCmh0IDwtIEhlYXRtYXAoCiAgYXZnX21hdF96LCAKICBuYW1lID0gIlRGIGFjdGl2aXR5ICh6KSIsIAogIGNvbCA9IGNvbF9mdW4sIAogIHRvcF9hbm5vdGF0aW9uID0gaGFfY29sLCAKICBsZWZ0X2Fubm90YXRpb24gPSBoYV9yb3csIAogIGNvbHVtbl9zcGxpdCA9IGNsdXN0ZXJfc3RhdHVzLCAKICByb3dfc3BsaXQgPSB0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uLCAKICBjbHVzdGVyX3Jvd19zbGljZXMgPSBGQUxTRSwgCiAgY2x1c3Rlcl9yb3dzID0gRkFMU0UsIAogIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsIAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwgCiAgc2hvd19jb2x1bW5fZGVuZCA9IFRSVUUsIAogIHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksIAogIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksIAogIGNvbHVtbl90aXRsZSA9ICJSZWd1bGF0b3J5IERyaXZlcnMgb2YgU8OpemFyeSBVTUFQIENlbGwgU3RhdGVzIiwgCiAgcm93X3RpdGxlX3JvdCA9IDAsIAogIHJvd190aXRsZV9ncCA9IGdwYXIoZm9udHNpemUgPSA5LCBmb250ZmFjZSA9ICJib2xkIiksIAogIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdChkaXJlY3Rpb24gPSAidmVydGljYWwiKQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNi4gU2F2ZSBwbG90cwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfVU1BUF9GaW5hbC5wZGYiLCB3aWR0aD0xNSwgaGVpZ2h0PTEzKQpkcmF3KGh0LCBtZXJnZV9sZWdlbmQ9VFJVRSkKZGV2Lm9mZigpCgpkcmF3KGh0LCBtZXJnZV9sZWdlbmQ9VFJVRSkKYGBgCgoKCgoKCgoKCiMgVEVTVApgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgTElCUkFSSUVTCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShTZXVyYXRPYmplY3QpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMS4gRGVmaW5lIFRGIHBhbmVsIG1ldGFkYXRhCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KdGZfbWV0YSA8LSBkYXRhLmZyYW1lKAogIFRGID0gYygKICAgICMgLS0tIDEuIEdlbmVyYWwgTWFsaWduYW5jeSAtLS0KICAgICJNWUMiLCJFMkY0IiwiVFdJU1QxIiwiSVJGNCIsCgogICAgIyAtLS0gMi4gVENSIFNpZ25hbGluZyBUcmlhZCAtLS0KICAgICJKVU5CIiwiRk9TIiwiRk9TTDEiLCAgICAgICAgICAgICAgICAjIEFQLTEKICAgICJORkFUQzEiLCJORkFUQzIiLCAgICAgICAgICAgICAgICAgICAjIE5GQVQKICAgICJSRUxBIiwiUkVMIiwiTkZLQjEiLCJJUkYxIiwiTkNPQTIiLCAjIE5GLWtCCgogICAgIyAtLS0gMy4gU3RyZXNzIFJlc3BvbnNlIC0tLQogICAgIkhTRjEiLCJORkUyTDIiLCJTUkVCRjIiLAoKICAgICMgLS0tIDQuIFRoMiAvIEpBSy1TVEFUIENvcmUgLS0tCiAgICAiR0FUQTMiLCJTVEFUNiIsIkJBVEYiLCJGT1hQMyIsIlNUQVQzIiwiU1RBVDVCIiwKCiAgICAjIC0tLSA1LiBEaWZmZXJlbnRpYXRpb24gSGllcmFyY2h5IC0tLQogICAgIlRDRjciLCJMRUYxIiwiTVlCIiwgICAgICAgICAgICAgICAgICMgU3RlbS1saWtlCiAgICAiRTJGMSIsIkZPWE0xIiwgICAgICAgICAgICAgICAgICAgICAgIyBQcm9saWZlcmF0aW9uCiAgICAiUFJETTEiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUZXJtaW5hbCBFZmZlY3RvcgoKICAgICMgLS0tIDYuIEtFR0cgQWxpZ25lZCAtLS0KICAgICJFT01FUyIsIlRCWDIxIiwiUlVOWDMiLCAgICAgICAgICAgICAjIE5LLWxpa2UgQ3l0b3RveGljaXR5CiAgICAiUkZYNSIsIkNSRUIxIiwgICAgICAgICAgICAgICAgICAgICAgIyBBbnRpZ2VuIFByZXNlbnRhdGlvbgogICAgIktMRjQiLCJFVFMxIiwiU01BRDMiLCAgICAgICAgICAgICAgIyBNaWdyYXRpb24gLyBBZGhlc2lvbgoKICAgICMgLS0tIDcuIE1pY3JvZW52aXJvbm1lbnQgLS0tCiAgICAiU1RBVDEiLCJTVEFUMiIsIklSRjkiLCAgICAgICAgICAgICAgIyBJRk4gUmVzcG9uc2UKICAgICJISUYxQSIsIlNSRUJGMSIsICAgICAgICAgICAgICAgICAgICAjIE1ldGFib2xpc20KCiAgICAjIC0tLSA4LiBUdW1vciBTdXBwcmVzc29ycyAvIE5vcm1hbCAtLS0KICAgICJGT1hPMSIsIkZPWE80IiwiWkVCMSIsIkJBQ0gyIiwKICAgICJUQ0YzIiwiQkNMMTFBIiwiTkVVUk9EMSIsIk1FRjJCIiwiUEJYMiIsIklSRjMiLCJCQ0w2IgogICksCiAgQ29uZGl0aW9uID0gYygKICAgIHJlcCgiTWFsaWduYW50IiwgNCksICAgIyBPbmNvZ2VuaWMKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBBUC0xCiAgICByZXAoIk1hbGlnbmFudCIsIDIpLCAgICMgTkZBVAogICAgcmVwKCJNYWxpZ25hbnQiLCA1KSwgICAjIE5GLWtCCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgU3RyZXNzIFJlc3BvbnNlCiAgICByZXAoIk1hbGlnbmFudCIsIDYpLCAgICMgVGgyL0pBSy1TVEFUCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgU3RlbS1saWtlCiAgICByZXAoIk1hbGlnbmFudCIsIDIpLCAgICMgUHJvbGlmZXJhdGlvbgogICAgcmVwKCJNYWxpZ25hbnQiLCAxKSwgICAjIFRlcm1pbmFsIEVmZmVjdG9yCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgTkstbGlrZQogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIEFudGlnZW4gUHJlc2VudGF0aW9uCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgTWlncmF0aW9uCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgSUZOIFJlc3BvbnNlCiAgICByZXAoIk1hbGlnbmFudCIsIDIpLCAgICMgTWV0YWJvbGlzbQogICAgcmVwKCJOb3JtYWwiLCA0KSwgICAgICAjIFR1bW9yIFN1cHByZXNzb3IKICAgIHJlcCgiTm9ybWFsIiwgNykgICAgICAgIyBOb3JtYWwgSG9tZW9zdGFzaXMKICApLAogIEZ1bmN0aW9uID0gYygKICAgIHJlcCgiT25jb2dlbmljIiwgNCksCiAgICByZXAoIkFQLTEgU2lnbmFsaW5nIiwgMyksCiAgICByZXAoIk5GQVQgU2lnbmFsaW5nIiwgMiksCiAgICByZXAoIk5GLVx1MDNiYUIgU2lnbmFsaW5nIiwgNSksICAjIFJFTkFNRUQgZnJvbSBJbmZsYW1tYXRvcnkvTkYta0IKICAgIHJlcCgiU3RyZXNzIFJlc3BvbnNlIiwgMyksCiAgICByZXAoIlRoMiAvIEpBSy1TVEFUIENvcmUiLCA2KSwKICAgIHJlcCgiU3RlbS1saWtlIFByb2dlbml0b3IiLCAzKSwKICAgIHJlcCgiUHJvbGlmZXJhdGlvbiIsIDIpLAogICAgcmVwKCJUZXJtaW5hbCBFZmZlY3RvciIsIDEpLAogICAgcmVwKCJOSy1saWtlIEN5dG90b3hpY2l0eSIsIDMpLAogICAgcmVwKCJBbnRpZ2VuIFByZXNlbnRhdGlvbiIsIDIpLAogICAgcmVwKCJNaWdyYXRpb24gLyBBZGhlc2lvbiIsIDMpLAogICAgcmVwKCJJRk4gUmVzcG9uc2UiLCAzKSwKICAgIHJlcCgiTWV0YWJvbGlzbSIsIDIpLAogICAgcmVwKCJUdW1vciBTdXBwcmVzc29yIiwgNCksCiAgICByZXAoIk5vcm1hbCBIb21lb3N0YXNpcyIsIDcpCiAgKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQoKIyBMb2NrIGluIG9yZGVyCmRlc2lyZWRfb3JkZXIgPC0gYygKICAiT25jb2dlbmljIiwKICAiQVAtMSBTaWduYWxpbmciLAogICJORkFUIFNpZ25hbGluZyIsCiAgIk5GLVx1MDNiYUIgU2lnbmFsaW5nIiwKICAiU3RyZXNzIFJlc3BvbnNlIiwKICAiVGgyIC8gSkFLLVNUQVQgQ29yZSIsCiAgIlN0ZW0tbGlrZSBQcm9nZW5pdG9yIiwKICAiUHJvbGlmZXJhdGlvbiIsCiAgIlRlcm1pbmFsIEVmZmVjdG9yIiwKICAiTkstbGlrZSBDeXRvdG94aWNpdHkiLAogICJBbnRpZ2VuIFByZXNlbnRhdGlvbiIsCiAgIk1pZ3JhdGlvbiAvIEFkaGVzaW9uIiwKICAiSUZOIFJlc3BvbnNlIiwKICAiTWV0YWJvbGlzbSIsCiAgIlR1bW9yIFN1cHByZXNzb3IiLAogICJOb3JtYWwgSG9tZW9zdGFzaXMiCikKCnRmX21ldGEkRnVuY3Rpb24gPC0gZmFjdG9yKHRmX21ldGEkRnVuY3Rpb24sIGxldmVscyA9IGRlc2lyZWRfb3JkZXIpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMi4gRXh0cmFjdCBURiBhY3Rpdml0eSBtYXRyaXggZnJvbSBTZXVyYXQKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQptYXRfc2NhbGVkIDwtIHRyeUNhdGNoKAogIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiZG9yb3RoZWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxheWVyID0gInNjYWxlLmRhdGEiKSwKICBlcnJvciA9IGZ1bmN0aW9uKGUpIE5VTEwKKQptYXRfZGF0YSA8LSBTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShzZXVyYXRfb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJkb3JvdGhlYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxheWVyID0gImRhdGEiKQptYXRfdXNlIDwtIGlmICghaXMubnVsbChtYXRfc2NhbGVkKSAmJgogICAgICAgICAgICAgICBucm93KG1hdF9zY2FsZWQpID4gMCkgbWF0X3NjYWxlZCBlbHNlIG1hdF9kYXRhCgojIEZpbHRlciBmb3IgVEZzIHByZXNlbnQgaW4gU2V1cmF0CmF2YWlsYWJsZV90ZnMgPC0gaW50ZXJzZWN0KHRmX21ldGEkVEYsIHJvd25hbWVzKG1hdF91c2UpKQptYXRfdXNlICAgICAgIDwtIG1hdF91c2VbYXZhaWxhYmxlX3RmcywgLCBkcm9wID0gRkFMU0VdCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMy4gQXZlcmFnZSBwZXIgY2x1c3RlciAmIHotc2NvcmUKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjbHVzdGVycyA8LSBhcy5mYWN0b3Ioc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMpCmF2Z19tYXQgIDwtIHNhcHBseShsZXZlbHMoY2x1c3RlcnMpLCBmdW5jdGlvbihjbCkgewogIE1hdHJpeDo6cm93TWVhbnMobWF0X3VzZVssIGNsdXN0ZXJzID09IGNsLCBkcm9wID0gRkFMU0VdKQp9KQpjb2xuYW1lcyhhdmdfbWF0KSA8LSBsZXZlbHMoY2x1c3RlcnMpCgphdmdfbWF0X3ogICAgICAgICAgICAgIDwtIHQoc2NhbGUodChhdmdfbWF0KSkpCmF2Z19tYXRfeltpcy5uYShhdmdfbWF0X3opXSA8LSAwCgojIEFsaWduIHRmX21ldGEgb3JkZXIgdG8gbWF0Y2ggbWF0X3VzZSBwcmVjaXNlbHkKcm93X29yZGVyX2lkeCAgICA8LSBtYXRjaChyb3duYW1lcyhhdmdfbWF0X3opLCB0Zl9tZXRhJFRGKQp0Zl9tZXRhX2ZpbHRlcmVkIDwtIHRmX21ldGFbcm93X29yZGVyX2lkeCwgXQphdmdfbWF0X3ogICAgICAgIDwtIGF2Z19tYXRfeltvcmRlcih0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uKSwgXQp0Zl9tZXRhX2ZpbHRlcmVkIDwtIHRmX21ldGFfZmlsdGVyZWRbb3JkZXIodGZfbWV0YV9maWx0ZXJlZCRGdW5jdGlvbiksIF0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA0LiBDbHVzdGVyIHN0YXR1cyAmIGxhYmVscwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmNsdXN0ZXJfc3RhdHVzIDwtIGlmZWxzZSgKICBjb2xuYW1lcyhhdmdfbWF0X3opICVpbiUgYygiMyIsICIxMCIpLAogICJOb3JtYWwgQ0Q0IFQgY2VsbHMiLAogICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMiCikKCiMgQ2x1c3RlciBiaW9sb2dpY2FsIGFubm90YXRpb25zCmNsdXN0ZXJfbGFiZWxzIDwtIGMoCiAgIjAiICA9ICJNSEMtSUkgSGlnaCIsCiAgIjEiICA9ICJOSy1saWtlIiwKICAiMiIgID0gIlRoMi1saWtlIiwKICAiMyIgID0gIk5haXZlL1RDTSIsCiAgIjQiICA9ICJJbmZsYW1tYXRvcnkiLAogICI1IiAgPSAiU3RlbS1saWtlIiwKICAiNiIgID0gIlRoMiBBY3RpdmF0ZWQiLAogICI3IiAgPSAiQ3ljbGluZyIsCiAgIjgiICA9ICJHbHljb2x5dGljIiwKICAiOSIgID0gIkN5dG90b3hpYyIsCiAgIjEwIiA9ICJDZW50cmFsIE1lbW9yeSIsCiAgIjExIiA9ICJQcm8taW5mbGFtbWF0b3J5IiwKICAiMTIiID0gIkdaTUItaGlnaCIsCiAgIjEzIiA9ICJJRk4gU3RpbXVsYXRlZCIKKQpjbHVzdGVyX2Fubm90YXRpb24gPC0gY2x1c3Rlcl9sYWJlbHNbY29sbmFtZXMoYXZnX21hdF96KV0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA1LiBDb2x1bW4gYW5ub3RhdGlvbiAodG9wKQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmhhX2NvbCA8LSBIZWF0bWFwQW5ub3RhdGlvbigKICBDZWxsX1N0YXRlICAgPSBjbHVzdGVyX3N0YXR1cywKICBDbHVzdGVyX1R5cGUgPSBjbHVzdGVyX2Fubm90YXRpb24sCiAgY29sID0gbGlzdCgKICAgIENlbGxfU3RhdGUgPSBjKAogICAgICAiTm9ybWFsIENENCBUIGNlbGxzIiAgICA9ICIjNERBRjRBIiwKICAgICAgIk1hbGlnbmFudCBDRDQgVCBjZWxscyIgPSAiI0MwMDAwMCIKICAgICksCiAgICBDbHVzdGVyX1R5cGUgPSBjKAogICAgICAiTUhDLUlJIEhpZ2giICAgICAgPSAiIzY2Q0RBQSIsCiAgICAgICJOSy1saWtlIiAgICAgICAgICA9ICIjRjc4MUJGIiwKICAgICAgIlRoMi1saWtlIiAgICAgICAgID0gIiM5ODRFQTMiLAogICAgICAiTmFpdmUvVENNIiAgICAgICAgPSAiIzREQUY0QSIsCiAgICAgICJJbmZsYW1tYXRvcnkiICAgICA9ICIjOEEyQkUyIiwKICAgICAgIlN0ZW0tbGlrZSIgICAgICAgID0gIiNGRjE0OTMiLAogICAgICAiVGgyIEFjdGl2YXRlZCIgICAgPSAiI0RBNzBENiIsCiAgICAgICJDeWNsaW5nIiAgICAgICAgICA9ICIjMUU5MEZGIiwKICAgICAgIkdseWNvbHl0aWMiICAgICAgID0gIiNBNjU2MjgiLAogICAgICAiQ3l0b3RveGljIiAgICAgICAgPSAiI0U0MUExQyIsCiAgICAgICJDZW50cmFsIE1lbW9yeSIgICA9ICIjMzJDRDMyIiwKICAgICAgIlByby1pbmZsYW1tYXRvcnkiID0gIiNGRjQ1MDAiLAogICAgICAiR1pNQi1oaWdoIiAgICAgICAgPSAiI0IyMjIyMiIsCiAgICAgICJJRk4gU3RpbXVsYXRlZCIgICA9ICIjMDBDRUQxIgogICAgKQogICksCiAgYW5ub3RhdGlvbl9uYW1lX3NpZGUgPSAibGVmdCIsCiAgYW5ub3RhdGlvbl9uYW1lX2dwICAgPSBncGFyKGZvbnRzaXplICA9IDgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiksCiAgc2ltcGxlX2Fubm9fc2l6ZSAgICAgPSB1bml0KDQsICJtbSIpLAogIGJvcmRlciAgICAgICAgICAgICAgID0gVFJVRQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNi4gUm93IGFubm90YXRpb24gY29sb3JzCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KZnVuY3Rpb25fY29sb3JzIDwtIGMoCiAgIk9uY29nZW5pYyIgICAgICAgICAgICAgID0gIiM4MDgwODAiLAogICMgVENSIFNpZ25hbGluZyBUcmlhZCDigJQgcmVsYXRlZCBidXQgZGlzdGluY3QKICAiQVAtMSBTaWduYWxpbmciICAgICAgICAgPSAiI0ZGOEMwMCIsCiAgIk5GQVQgU2lnbmFsaW5nIiAgICAgICAgID0gIiNGRjQ1MDAiLAogICJORi1cdTAzYmFCIFNpZ25hbGluZyIgID0gIiNFMzFBMUMiLAogICJTdHJlc3MgUmVzcG9uc2UiICAgICAgICA9ICIjRkZENzAwIiwKICAiVGgyIC8gSkFLLVNUQVQgQ29yZSIgICA9ICIjOTg0RUEzIiwKICAiU3RlbS1saWtlIFByb2dlbml0b3IiICAgPSAiI0ZGMTQ5MyIsCiAgIlByb2xpZmVyYXRpb24iICAgICAgICAgID0gIiMxRTkwRkYiLAogICJUZXJtaW5hbCBFZmZlY3RvciIgICAgICA9ICIjODAwMDgwIiwKICAiTkstbGlrZSBDeXRvdG94aWNpdHkiICAgPSAiI0Y3ODFCRiIsCiAgIkFudGlnZW4gUHJlc2VudGF0aW9uIiAgID0gIiM2NkNEQUEiLAogICJNaWdyYXRpb24gLyBBZGhlc2lvbiIgICA9ICIjOEEyQkUyIiwKICAiSUZOIFJlc3BvbnNlIiAgICAgICAgICAgPSAiIzAwQ0VEMSIsCiAgIk1ldGFib2xpc20iICAgICAgICAgICAgID0gIiNBNjU2MjgiLAogICJUdW1vciBTdXBwcmVzc29yIiAgICAgICA9ICIjMzc3RUI4IiwKICAiTm9ybWFsIEhvbWVvc3Rhc2lzIiAgICAgPSAiIzREQUY0QSIKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDcuIFJvdyBhbm5vdGF0aW9uIChsZWZ0KQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmhhX3JvdyA8LSByb3dBbm5vdGF0aW9uKAogIENvbmRpdGlvbiA9IHRmX21ldGFfZmlsdGVyZWQkQ29uZGl0aW9uLAogIEZ1bmN0aW9uICA9IHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24sCiAgY29sID0gbGlzdCgKICAgIENvbmRpdGlvbiA9IGMoCiAgICAgICJOb3JtYWwiICAgID0gIiM0REFGNEEiLAogICAgICAiTWFsaWduYW50IiA9ICIjQzAwMDAwIgogICAgKSwKICAgIEZ1bmN0aW9uID0gZnVuY3Rpb25fY29sb3JzCiAgKSwKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJib3R0b20iLAogIGFubm90YXRpb25fbmFtZV9ncCAgID0gZ3Bhcihmb250c2l6ZSAgPSA4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIpLAogIHNpbXBsZV9hbm5vX3NpemUgICAgID0gdW5pdCg1LCAibW0iKSwKICBib3JkZXIgICAgICAgICAgICAgICA9IFRSVUUsCiAgZ2FwICAgICAgICAgICAgICAgICAgPSB1bml0KDEsICJtbSIpCikKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA4LiBIZWF0bWFwIGNvbG9yIHNjYWxlIOKAlCA1IHBvaW50IGdyYWRpZW50CiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMigKICBjKC0zLCAtMS41LCAwLCAxLjUsIDMpLAogIGMoIiMyMTY2QUMiLCAgICMgRGVlcCBibHVlCiAgICAiIzkyQzVERSIsICAgIyBMaWdodCBibHVlCiAgICAiI0Y3RjdGNyIsICAgIyBPZmYtd2hpdGUKICAgICIjRjRBNTgyIiwgICAjIExpZ2h0IHJlZAogICAgIiNCMjE4MkIiKSAgICMgRGVlcCByZWQKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDkuIEJ1aWxkIGhlYXRtYXAKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpodCA8LSBIZWF0bWFwKAogIGF2Z19tYXRfeiwKICBuYW1lID0gIlRGIGFjdGl2aXR5XG4oei1zY29yZSkiLAogIGNvbCAgPSBjb2xfZnVuLAoKICAjIEFubm90YXRpb25zCiAgdG9wX2Fubm90YXRpb24gID0gaGFfY29sLAogIGxlZnRfYW5ub3RhdGlvbiA9IGhhX3JvdywKCiAgIyBTcGxpdHRpbmcKICBjb2x1bW5fc3BsaXQgPSBjbHVzdGVyX3N0YXR1cywKICByb3dfc3BsaXQgICAgPSB0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uLAoKICAjIENsdXN0ZXJpbmcKICBjbHVzdGVyX3Jvd19zbGljZXMgPSBGQUxTRSwKICBjbHVzdGVyX3Jvd3MgICAgICAgPSBGQUxTRSwKICBjbHVzdGVyX2NvbHVtbnMgICAgPSBUUlVFLAogIHNob3dfcm93X2RlbmQgICAgICA9IEZBTFNFLAogIHNob3dfY29sdW1uX2RlbmQgICA9IFRSVUUsCiAgY29sdW1uX2RlbmRfaGVpZ2h0ID0gdW5pdCgxNSwgIm1tIiksCgogICMgQ2VsbCBhcHBlYXJhbmNlCiAgcmVjdF9ncCA9IGdwYXIoY29sID0gIndoaXRlIiwgbHdkID0gMC44KSwKCiAgIyBSb3cgbmFtZXMKICByb3dfbmFtZXNfZ3AgICA9IGdwYXIoZm9udHNpemUgID0gOC41LAogICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gIml0YWxpYyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sICAgICAgPSAiZ3JleTIwIiksCiAgcm93X25hbWVzX3NpZGUgPSAicmlnaHQiLAoKICAjIENvbHVtbiBuYW1lcwogIGNvbHVtbl9uYW1lc19ncCAgPSBncGFyKGZvbnRzaXplICA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgICAgICA9ICJncmV5MTAiKSwKICBjb2x1bW5fbmFtZXNfcm90ID0gMCwKCiAgIyBSb3cgdGl0bGUgKGZ1bmN0aW9uYWwgYmxvY2sgbGFiZWxzIG9uIGxlZnQpCiAgcm93X3RpdGxlX3JvdCAgPSAwLAogIHJvd190aXRsZV9ncCAgID0gZ3Bhcihmb250c2l6ZSAgPSA4LAogICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCAgICAgID0gImdyZXkxMCIpLAogIHJvd190aXRsZV9zaWRlID0gImxlZnQiLAoKICAjIEdhcHMgYmV0d2VlbiBibG9ja3MKICByb3dfZ2FwICAgID0gdW5pdCgyLjUsICJtbSIpLAogIGNvbHVtbl9nYXAgPSB1bml0KDQsICAgIm1tIiksCgogICMgTWFpbiB0aXRsZQogIGNvbHVtbl90aXRsZSAgICA9ICJGdW5jdGlvbmFsIFRGIE1vZHVsZXMgaW4gU8OpemFyeSBTeW5kcm9tZSIsCiAgY29sdW1uX3RpdGxlX2dwID0gZ3Bhcihmb250c2l6ZSAgPSAxMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCAgICAgID0gImdyZXk1IiksCgogICMgTGVnZW5kCiAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KAogICAgZGlyZWN0aW9uICAgICA9ICJ2ZXJ0aWNhbCIsCiAgICB0aXRsZSAgICAgICAgID0gIlRGIGFjdGl2aXR5XG4oei1zY29yZSkiLAogICAgdGl0bGVfZ3AgICAgICA9IGdwYXIoZm9udHNpemUgID0gOSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIpLAogICAgbGFiZWxzX2dwICAgICA9IGdwYXIoZm9udHNpemUgID0gOCksCiAgICBsZWdlbmRfaGVpZ2h0ID0gdW5pdCg0LCAiY20iKSwKICAgIGJvcmRlciAgICAgICAgPSAiZ3JleTUwIiwKICAgIGF0ICAgICAgICAgICAgPSBjKC0zLCAtMS41LCAwLCAxLjUsIDMpLAogICAgbGFiZWxzICAgICAgICA9IGMoIi0zIiwgIi0xLjUiLCAiMCIsICIxLjUiLCAiMyIpCiAgKQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMTAuIFNhdmUKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIFBERiDigJQgdmVjdG9yIHF1YWxpdHkgZm9yIHB1YmxpY2F0aW9uCnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfRmluYWwucGRmIiwKICAgIHdpZHRoICAgICAgID0gMTYsCiAgICBoZWlnaHQgICAgICA9IDE1LAogICAgdXNlRGluZ2JhdHMgPSBGQUxTRSkKZHJhdyhodCwKICAgICBtZXJnZV9sZWdlbmQgICAgICAgICAgID0gVFJVRSwKICAgICBoZWF0bWFwX2xlZ2VuZF9zaWRlICAgID0gInJpZ2h0IiwKICAgICBhbm5vdGF0aW9uX2xlZ2VuZF9zaWRlID0gInJpZ2h0IiwKICAgICBwYWRkaW5nID0gdW5pdChjKDUsIDUsIDUsIDUpLCAibW0iKSkKZGV2Lm9mZigpCgojIFBORyDigJQgaGlnaCByZXNvbHV0aW9uIGZvciBwcmVzZW50YXRpb25zCnBuZygiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfRmluYWwucG5nIiwKICAgIHdpZHRoICA9IDE2ICogMzAwLAogICAgaGVpZ2h0ID0gMTUgKiAzMDAsCiAgICByZXMgICAgPSAzMDApCmRyYXcoaHQsCiAgICAgbWVyZ2VfbGVnZW5kICAgICAgICAgICA9IFRSVUUsCiAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSAgICA9ICJyaWdodCIsCiAgICAgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJyaWdodCIsCiAgICAgcGFkZGluZyA9IHVuaXQoYyg1LCA1LCA1LCA1KSwgIm1tIikpCmRldi5vZmYoKQoKIyBEcmF3IGluIG5vdGVib29rCmRyYXcoaHQsCiAgICAgbWVyZ2VfbGVnZW5kICAgICAgICAgICA9IFRSVUUsCiAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSAgICA9ICJyaWdodCIsCiAgICAgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJyaWdodCIsCiAgICAgcGFkZGluZyA9IHVuaXQoYyg1LCA1LCA1LCA1KSwgIm1tIikpCmBgYAojIFRlc3QyCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CgoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIExJQlJBUklFUwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDEuIERlZmluZSBURiBwYW5lbCBtZXRhZGF0YQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnRmX21ldGEgPC0gZGF0YS5mcmFtZSgKICBURiA9IGMoCiAgICAjIC0tLSAxLiBHZW5lcmFsIE1hbGlnbmFuY3kgLS0tCiAgICAiTVlDIiwiRTJGNCIsIlRXSVNUMSIsIklSRjQiLAoKICAgICMgLS0tIDIuIFRDUiBTaWduYWxpbmcgVHJpYWQgLS0tCiAgICAiSlVOQiIsIkZPUyIsIkZPU0wxIiwgICAgICAgICAgICAgICAgIyBBUC0xCiAgICAiTkZBVEMxIiwiTkZBVEMyIiwgICAgICAgICAgICAgICAgICAgIyBORkFUCiAgICAiUkVMQSIsIlJFTCIsIk5GS0IxIiwiSVJGMSIsIk5DT0EyIiwgIyBORi1rQgoKICAgICMgLS0tIDMuIFN0cmVzcyBSZXNwb25zZSAtLS0KICAgICJIU0YxIiwiTkZFMkwyIiwiU1JFQkYyIiwKCiAgICAjIC0tLSA0LiBUaDIgLyBKQUstU1RBVCBDb3JlIC0tLQogICAgIkdBVEEzIiwiU1RBVDYiLCJCQVRGIiwiRk9YUDMiLCJTVEFUMyIsIlNUQVQ1QiIsCgogICAgIyAtLS0gNS4gRGlmZmVyZW50aWF0aW9uIEhpZXJhcmNoeSAtLS0KICAgICJUQ0Y3IiwiTEVGMSIsIk1ZQiIsICAgICAgICAgICAgICAgICAjIFN0ZW0tbGlrZQogICAgIkUyRjEiLCJGT1hNMSIsICAgICAgICAgICAgICAgICAgICAgICMgUHJvbGlmZXJhdGlvbgogICAgIlBSRE0xIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVGVybWluYWwgRWZmZWN0b3IKCiAgICAjIC0tLSA2LiBLRUdHIEFsaWduZWQgLS0tCiAgICAiRU9NRVMiLCJUQlgyMSIsIlJVTlgzIiwgICAgICAgICAgICAgIyBOSy1saWtlIEN5dG90b3hpY2l0eQogICAgIlJGWDUiLCJDUkVCMSIsICAgICAgICAgICAgICAgICAgICAgICMgQW50aWdlbiBQcmVzZW50YXRpb24KICAgICJLTEY0IiwiRVRTMSIsIlNNQUQzIiwgICAgICAgICAgICAgICMgTWlncmF0aW9uIC8gQWRoZXNpb24KCiAgICAjIC0tLSA3LiBNaWNyb2Vudmlyb25tZW50IC0tLQogICAgIlNUQVQxIiwiU1RBVDIiLCJJUkY5IiwgICAgICAgICAgICAgICMgSUZOIFJlc3BvbnNlCiAgICAiSElGMUEiLCJTUkVCRjEiLCAgICAgICAgICAgICAgICAgICAgIyBNZXRhYm9saXNtCgogICAgIyAtLS0gOC4gVHVtb3IgU3VwcHJlc3NvcnMgLyBOb3JtYWwgLS0tCiAgICAiRk9YTzEiLCJGT1hPNCIsIlpFQjEiLCJCQUNIMiIsCiAgICAiVENGMyIsIkJDTDExQSIsIk5FVVJPRDEiLCJNRUYyQiIsIlBCWDIiLCJJUkYzIiwiQkNMNiIKICApLAogIENvbmRpdGlvbiA9IGMoCiAgICByZXAoIk1hbGlnbmFudCIsIDQpLCAgICMgT25jb2dlbmljCiAgICByZXAoIk1hbGlnbmFudCIsIDMpLCAgICMgQVAtMQogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIE5GQVQKICAgIHJlcCgiTWFsaWduYW50IiwgNSksICAgIyBORi1rQgogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIFN0cmVzcyBSZXNwb25zZQogICAgcmVwKCJNYWxpZ25hbnQiLCA2KSwgICAjIFRoMi9KQUstU1RBVAogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIFN0ZW0tbGlrZQogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIFByb2xpZmVyYXRpb24KICAgIHJlcCgiTWFsaWduYW50IiwgMSksICAgIyBUZXJtaW5hbCBFZmZlY3RvcgogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIE5LLWxpa2UKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAgIyBBbnRpZ2VuIFByZXNlbnRhdGlvbgogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIE1pZ3JhdGlvbgogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIElGTiBSZXNwb25zZQogICAgcmVwKCJNYWxpZ25hbnQiLCAyKSwgICAjIE1ldGFib2xpc20KICAgIHJlcCgiTm9ybWFsIiwgNCksICAgICAgIyBUdW1vciBTdXBwcmVzc29yCiAgICByZXAoIk5vcm1hbCIsIDcpICAgICAgICMgTm9ybWFsIEhvbWVvc3Rhc2lzCiAgKSwKICBGdW5jdGlvbiA9IGMoCiAgICByZXAoIk9uY29nZW5pYyIsIDQpLAogICAgcmVwKCJBUC0xIFNpZ25hbGluZyIsIDMpLAogICAgcmVwKCJORkFUIFNpZ25hbGluZyIsIDIpLAogICAgcmVwKCJORi1cdTAzYmFCIFNpZ25hbGluZyIsIDUpLAogICAgcmVwKCJTdHJlc3MgUmVzcG9uc2UiLCAzKSwKICAgIHJlcCgiVGgyIC8gSkFLLVNUQVQgQ29yZSIsIDYpLAogICAgcmVwKCJTdGVtLWxpa2UgUHJvZ2VuaXRvciIsIDMpLAogICAgcmVwKCJQcm9saWZlcmF0aW9uIiwgMiksCiAgICByZXAoIlRlcm1pbmFsIEVmZmVjdG9yIiwgMSksCiAgICByZXAoIk5LLWxpa2UgQ3l0b3RveGljaXR5IiwgMyksCiAgICByZXAoIkFudGlnZW4gUHJlc2VudGF0aW9uIiwgMiksCiAgICByZXAoIk1pZ3JhdGlvbiAvIEFkaGVzaW9uIiwgMyksCiAgICByZXAoIklGTiBSZXNwb25zZSIsIDMpLAogICAgcmVwKCJNZXRhYm9saXNtIiwgMiksCiAgICByZXAoIlR1bW9yIFN1cHByZXNzb3IiLCA0KSwKICAgIHJlcCgiTm9ybWFsIEhvbWVvc3Rhc2lzIiwgNykKICApLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopCgojIExvY2sgaW4gb3JkZXIKZGVzaXJlZF9vcmRlciA8LSBjKAogICJPbmNvZ2VuaWMiLAogICJBUC0xIFNpZ25hbGluZyIsCiAgIk5GQVQgU2lnbmFsaW5nIiwKICAiTkYtXHUwM2JhQiBTaWduYWxpbmciLAogICJTdHJlc3MgUmVzcG9uc2UiLAogICJUaDIgLyBKQUstU1RBVCBDb3JlIiwKICAiU3RlbS1saWtlIFByb2dlbml0b3IiLAogICJQcm9saWZlcmF0aW9uIiwKICAiVGVybWluYWwgRWZmZWN0b3IiLAogICJOSy1saWtlIEN5dG90b3hpY2l0eSIsCiAgIkFudGlnZW4gUHJlc2VudGF0aW9uIiwKICAiTWlncmF0aW9uIC8gQWRoZXNpb24iLAogICJJRk4gUmVzcG9uc2UiLAogICJNZXRhYm9saXNtIiwKICAiVHVtb3IgU3VwcHJlc3NvciIsCiAgIk5vcm1hbCBIb21lb3N0YXNpcyIKKQoKdGZfbWV0YSRGdW5jdGlvbiA8LSBmYWN0b3IodGZfbWV0YSRGdW5jdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGRlc2lyZWRfb3JkZXIpCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMi4gRXh0cmFjdCBURiBhY3Rpdml0eSBtYXRyaXggZnJvbSBTZXVyYXQKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQptYXRfc2NhbGVkIDwtIHRyeUNhdGNoKAogIFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiZG9yb3RoZWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxheWVyID0gInNjYWxlLmRhdGEiKSwKICBlcnJvciA9IGZ1bmN0aW9uKGUpIE5VTEwKKQptYXRfZGF0YSA8LSBTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShzZXVyYXRfb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJkb3JvdGhlYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxheWVyID0gImRhdGEiKQptYXRfdXNlIDwtIGlmICghaXMubnVsbChtYXRfc2NhbGVkKSAmJgogICAgICAgICAgICAgICBucm93KG1hdF9zY2FsZWQpID4gMCkgbWF0X3NjYWxlZCBlbHNlIG1hdF9kYXRhCgojIEZpbHRlciBmb3IgVEZzIHByZXNlbnQgaW4gU2V1cmF0CmF2YWlsYWJsZV90ZnMgPC0gaW50ZXJzZWN0KHRmX21ldGEkVEYsIHJvd25hbWVzKG1hdF91c2UpKQptYXRfdXNlICAgICAgIDwtIG1hdF91c2VbYXZhaWxhYmxlX3RmcywgLCBkcm9wID0gRkFMU0VdCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMy4gQXZlcmFnZSBwZXIgY2x1c3RlciAmIHotc2NvcmUKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjbHVzdGVycyA8LSBhcy5mYWN0b3Ioc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMpCmF2Z19tYXQgIDwtIHNhcHBseShsZXZlbHMoY2x1c3RlcnMpLCBmdW5jdGlvbihjbCkgewogIE1hdHJpeDo6cm93TWVhbnMobWF0X3VzZVssIGNsdXN0ZXJzID09IGNsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZHJvcCA9IEZBTFNFXSkKfSkKY29sbmFtZXMoYXZnX21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQoKYXZnX21hdF96ICAgICAgICAgICAgICAgPC0gdChzY2FsZSh0KGF2Z19tYXQpKSkKYXZnX21hdF96W2lzLm5hKGF2Z19tYXRfeildIDwtIDAKCiMgQWxpZ24gdGZfbWV0YSBvcmRlciB0byBtYXRjaCBtYXRfdXNlIHByZWNpc2VseQpyb3dfb3JkZXJfaWR4ICAgIDwtIG1hdGNoKHJvd25hbWVzKGF2Z19tYXRfeiksIHRmX21ldGEkVEYpCnRmX21ldGFfZmlsdGVyZWQgPC0gdGZfbWV0YVtyb3dfb3JkZXJfaWR4LCBdCmF2Z19tYXRfeiAgICAgICAgPC0gYXZnX21hdF96W29yZGVyKHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24pLCBdCnRmX21ldGFfZmlsdGVyZWQgPC0gdGZfbWV0YV9maWx0ZXJlZFtvcmRlcih0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uKSwgXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDQuIENsdXN0ZXIgc3RhdHVzICYgYmlvbG9naWNhbCBsYWJlbHMKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjbHVzdGVyX3N0YXR1cyA8LSBpZmVsc2UoCiAgY29sbmFtZXMoYXZnX21hdF96KSAlaW4lIGMoIjMiLCAiMTAiKSwKICAiTm9ybWFsIENENCBUIGNlbGxzIiwKICAiTWFsaWduYW50IENENCBUIGNlbGxzIgopCgpjbHVzdGVyX2xhYmVscyA8LSBjKAogICIwIiAgPSAiTUhDLUlJIEhpZ2giLAogICIxIiAgPSAiTkstbGlrZSIsCiAgIjIiICA9ICJUaDItbGlrZSIsCiAgIjMiICA9ICJOYWl2ZS9UQ00iLAogICI0IiAgPSAiSW5mbGFtbWF0b3J5IiwKICAiNSIgID0gIlN0ZW0tbGlrZSIsCiAgIjYiICA9ICJUaDIgQWN0aXZhdGVkIiwKICAiNyIgID0gIkN5Y2xpbmciLAogICI4IiAgPSAiR2x5Y29seXRpYyIsCiAgIjkiICA9ICJDeXRvdG94aWMiLAogICIxMCIgPSAiQ2VudHJhbCBNZW1vcnkiLAogICIxMSIgPSAiUHJvLWluZmxhbW1hdG9yeSIsCiAgIjEyIiA9ICJHWk1CLWhpZ2giLAogICIxMyIgPSAiSUZOIFN0aW11bGF0ZWQiCikKY2x1c3Rlcl9hbm5vdGF0aW9uIDwtIGNsdXN0ZXJfbGFiZWxzW2NvbG5hbWVzKGF2Z19tYXRfeildCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNS4gQ29sdW1uIGFubm90YXRpb24gKHRvcCkKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpoYV9jb2wgPC0gSGVhdG1hcEFubm90YXRpb24oCiAgQ2VsbF9TdGF0ZSAgID0gY2x1c3Rlcl9zdGF0dXMsCiAgQ2x1c3Rlcl9UeXBlID0gY2x1c3Rlcl9hbm5vdGF0aW9uLAogIGNvbCA9IGxpc3QoCiAgICBDZWxsX1N0YXRlID0gYygKICAgICAgIk5vcm1hbCBDRDQgVCBjZWxscyIgICAgPSAiIzREQUY0QSIsCiAgICAgICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMiID0gIiNDMDAwMDAiCiAgICApLAogICAgQ2x1c3Rlcl9UeXBlID0gYygKICAgICAgIk1IQy1JSSBIaWdoIiAgICAgID0gIiM2NkNEQUEiLAogICAgICAiTkstbGlrZSIgICAgICAgICAgPSAiI0Y3ODFCRiIsCiAgICAgICJUaDItbGlrZSIgICAgICAgICA9ICIjOTg0RUEzIiwKICAgICAgIk5haXZlL1RDTSIgICAgICAgID0gIiM0REFGNEEiLAogICAgICAiSW5mbGFtbWF0b3J5IiAgICAgPSAiIzhBMkJFMiIsCiAgICAgICJTdGVtLWxpa2UiICAgICAgICA9ICIjRkYxNDkzIiwKICAgICAgIlRoMiBBY3RpdmF0ZWQiICAgID0gIiNEQTcwRDYiLAogICAgICAiQ3ljbGluZyIgICAgICAgICAgPSAiIzFFOTBGRiIsCiAgICAgICJHbHljb2x5dGljIiAgICAgICA9ICIjQTY1NjI4IiwKICAgICAgIkN5dG90b3hpYyIgICAgICAgID0gIiNFNDFBMUMiLAogICAgICAiQ2VudHJhbCBNZW1vcnkiICAgPSAiIzMyQ0QzMiIsCiAgICAgICJQcm8taW5mbGFtbWF0b3J5IiA9ICIjRkY0NTAwIiwKICAgICAgIkdaTUItaGlnaCIgICAgICAgID0gIiNCMjIyMjIiLAogICAgICAiSUZOIFN0aW11bGF0ZWQiICAgPSAiIzAwQ0VEMSIKICAgICkKICApLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImxlZnQiLAogIGFubm90YXRpb25fbmFtZV9ncCAgID0gZ3Bhcihmb250c2l6ZSAgPSA5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgICAgICA9ICJibGFjayIpLAogIHNpbXBsZV9hbm5vX3NpemUgICAgID0gdW5pdCg1LCAibW0iKSwKICBib3JkZXIgICAgICAgICAgICAgICA9IFRSVUUKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDYuIFJvdyBhbm5vdGF0aW9uIGNvbG9ycwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmZ1bmN0aW9uX2NvbG9ycyA8LSBjKAogICJPbmNvZ2VuaWMiICAgICAgICAgICAgID0gIiM4MDgwODAiLAogICJBUC0xIFNpZ25hbGluZyIgICAgICAgID0gIiNGRjhDMDAiLAogICJORkFUIFNpZ25hbGluZyIgICAgICAgID0gIiNGRjQ1MDAiLAogICJORi1cdTAzYmFCIFNpZ25hbGluZyIgPSAiI0UzMUExQyIsCiAgIlN0cmVzcyBSZXNwb25zZSIgICAgICAgPSAiI0ZGRDcwMCIsCiAgIlRoMiAvIEpBSy1TVEFUIENvcmUiICA9ICIjOTg0RUEzIiwKICAiU3RlbS1saWtlIFByb2dlbml0b3IiICA9ICIjRkYxNDkzIiwKICAiUHJvbGlmZXJhdGlvbiIgICAgICAgICA9ICIjMUU5MEZGIiwKICAiVGVybWluYWwgRWZmZWN0b3IiICAgICA9ICIjODAwMDgwIiwKICAiTkstbGlrZSBDeXRvdG94aWNpdHkiICA9ICIjRjc4MUJGIiwKICAiQW50aWdlbiBQcmVzZW50YXRpb24iICA9ICIjNjZDREFBIiwKICAiTWlncmF0aW9uIC8gQWRoZXNpb24iICA9ICIjOEEyQkUyIiwKICAiSUZOIFJlc3BvbnNlIiAgICAgICAgICA9ICIjMDBDRUQxIiwKICAiTWV0YWJvbGlzbSIgICAgICAgICAgICA9ICIjQTY1NjI4IiwKICAiVHVtb3IgU3VwcHJlc3NvciIgICAgICA9ICIjMzc3RUI4IiwKICAiTm9ybWFsIEhvbWVvc3Rhc2lzIiAgICA9ICIjNERBRjRBIgopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNy4gUm93IGFubm90YXRpb24gKGxlZnQpCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KaGFfcm93IDwtIHJvd0Fubm90YXRpb24oCiAgQ29uZGl0aW9uID0gdGZfbWV0YV9maWx0ZXJlZCRDb25kaXRpb24sCiAgRnVuY3Rpb24gID0gdGZfbWV0YV9maWx0ZXJlZCRGdW5jdGlvbiwKICBjb2wgPSBsaXN0KAogICAgQ29uZGl0aW9uID0gYygKICAgICAgIk5vcm1hbCIgICAgPSAiIzREQUY0QSIsCiAgICAgICJNYWxpZ25hbnQiID0gIiNDMDAwMDAiCiAgICApLAogICAgRnVuY3Rpb24gPSBmdW5jdGlvbl9jb2xvcnMKICApLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImJvdHRvbSIsCiAgYW5ub3RhdGlvbl9uYW1lX2dwICAgPSBncGFyKGZvbnRzaXplICA9IDksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCAgICAgID0gImJsYWNrIiksCiAgc2ltcGxlX2Fubm9fc2l6ZSAgICAgPSB1bml0KDUsICJtbSIpLAogIGJvcmRlciAgICAgICAgICAgICAgID0gVFJVRSwKICBnYXAgICAgICAgICAgICAgICAgICA9IHVuaXQoMSwgIm1tIikKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDguIEhlYXRtYXAgY29sb3Igc2NhbGUg4oCUIDUgcG9pbnQgZ3JhZGllbnQKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjb2xfZnVuIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKAogIGMoLTMsIC0xLjUsIDAsIDEuNSwgMyksCiAgYygiIzIxNjZBQyIsICAgIyBEZWVwIGJsdWUKICAgICIjOTJDNURFIiwgICAjIExpZ2h0IGJsdWUKICAgICIjRjdGN0Y3IiwgICAjIE9mZi13aGl0ZQogICAgIiNGNEE1ODIiLCAgICMgTGlnaHQgcmVkCiAgICAiI0IyMTgyQiIpICAgIyBEZWVwIHJlZAopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgOS4gQnVpbGQgaGVhdG1hcAojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Cmh0IDwtIEhlYXRtYXAoCiAgYXZnX21hdF96LAogIG5hbWUgPSAiVEYgYWN0aXZpdHlcbih6LXNjb3JlKSIsCiAgY29sICA9IGNvbF9mdW4sCgogICMgQW5ub3RhdGlvbnMKICB0b3BfYW5ub3RhdGlvbiAgPSBoYV9jb2wsCiAgbGVmdF9hbm5vdGF0aW9uID0gaGFfcm93LAoKICAjIFNwbGl0dGluZwogIGNvbHVtbl9zcGxpdCA9IGNsdXN0ZXJfc3RhdHVzLAogIHJvd19zcGxpdCAgICA9IHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24sCgogICMgQ2x1c3RlcmluZwogIGNsdXN0ZXJfcm93X3NsaWNlcyA9IEZBTFNFLAogIGNsdXN0ZXJfcm93cyAgICAgICA9IEZBTFNFLAogIGNsdXN0ZXJfY29sdW1ucyAgICA9IFRSVUUsCiAgc2hvd19yb3dfZGVuZCAgICAgID0gRkFMU0UsCiAgc2hvd19jb2x1bW5fZGVuZCAgID0gVFJVRSwKICBjb2x1bW5fZGVuZF9oZWlnaHQgPSB1bml0KDE1LCAibW0iKSwKCiAgIyBDZWxsIGFwcGVhcmFuY2UKICByZWN0X2dwID0gZ3Bhcihjb2wgPSAid2hpdGUiLCBsd2QgPSAwLjgpLAoKICAjIFJvdyBuYW1lcyDigJQgQk9MRCBhbmQgbGFyZ2VyIGZvciBzbGlkZSB2aXNpYmlsaXR5CiAgcm93X25hbWVzX2dwICAgPSBncGFyKGZvbnRzaXplICA9IDEyLAogICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCAgICAgID0gImJsYWNrIiksCiAgcm93X25hbWVzX3NpZGUgPSAicmlnaHQiLAoKICAjIENvbHVtbiBuYW1lcyDigJQgQk9MRCBhbmQgbGFyZ2VyCiAgY29sdW1uX25hbWVzX2dwICA9IGdwYXIoZm9udHNpemUgID0gMTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCAgICAgID0gImJsYWNrIiksCiAgY29sdW1uX25hbWVzX3JvdCA9IDAsCgogICMgUm93IHRpdGxlIOKAlCBmdW5jdGlvbmFsIGJsb2NrIGxhYmVscwogIHJvd190aXRsZV9yb3QgID0gMCwKICByb3dfdGl0bGVfZ3AgICA9IGdwYXIoZm9udHNpemUgID0gMTEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sICAgICAgPSAiYmxhY2siKSwKICByb3dfdGl0bGVfc2lkZSA9ICJsZWZ0IiwKCiAgIyBHYXBzIGJldHdlZW4gYmxvY2tzCiAgcm93X2dhcCAgICA9IHVuaXQoMi41LCAibW0iKSwKICBjb2x1bW5fZ2FwID0gdW5pdCg0LCAgICJtbSIpLAoKICAjIE1haW4gdGl0bGUKICBjb2x1bW5fdGl0bGUgICAgPSAiRnVuY3Rpb25hbCBURiBNb2R1bGVzIGluIFPDqXphcnkgU3luZHJvbWUiLAogIGNvbHVtbl90aXRsZV9ncCA9IGdwYXIoZm9udHNpemUgID0gMTQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgICAgICA9ICJibGFjayIpLAoKICAjIExlZ2VuZAogIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCgKICAgIGRpcmVjdGlvbiAgICAgPSAidmVydGljYWwiLAogICAgdGl0bGUgICAgICAgICA9ICJURiBhY3Rpdml0eVxuKHotc2NvcmUpIiwKICAgIHRpdGxlX2dwICAgICAgPSBncGFyKGZvbnRzaXplICA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sICAgICAgPSAiYmxhY2siKSwKICAgIGxhYmVsc19ncCAgICAgPSBncGFyKGZvbnRzaXplICA9IDksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCAgICAgID0gImJsYWNrIiksCiAgICBsZWdlbmRfaGVpZ2h0ID0gdW5pdCg0LCAiY20iKSwKICAgIGJvcmRlciAgICAgICAgPSAiZ3JleTUwIiwKICAgIGF0ICAgICAgICAgICAgPSBjKC0zLCAtMS41LCAwLCAxLjUsIDMpLAogICAgbGFiZWxzICAgICAgICA9IGMoIi0zIiwgIi0xLjUiLCAiMCIsICIxLjUiLCAiMyIpCiAgKQopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgMTAuIFNhdmUKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIFBERiDigJQgdmVjdG9yIHF1YWxpdHkgZm9yIHB1YmxpY2F0aW9uCnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfRmluYWwucGRmIiwKICAgIHdpZHRoICAgICAgID0gMTgsCiAgICBoZWlnaHQgICAgICA9IDE2LAogICAgdXNlRGluZ2JhdHMgPSBGQUxTRSkKZHJhdyhodCwKICAgICBtZXJnZV9sZWdlbmQgICAgICAgICAgID0gVFJVRSwKICAgICBoZWF0bWFwX2xlZ2VuZF9zaWRlICAgID0gInJpZ2h0IiwKICAgICBhbm5vdGF0aW9uX2xlZ2VuZF9zaWRlID0gInJpZ2h0IiwKICAgICBwYWRkaW5nID0gdW5pdChjKDUsIDUsIDUsIDUpLCAibW0iKSkKZGV2Lm9mZigpCgojIFBORyDigJQgaGlnaCByZXNvbHV0aW9uIGZvciBwcmVzZW50YXRpb25zCnBuZygiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfRmluYWwucG5nIiwKICAgIHdpZHRoICA9IDE4ICogMzAwLAogICAgaGVpZ2h0ID0gMTYgKiAzMDAsCiAgICByZXMgICAgPSAzMDApCmRyYXcoaHQsCiAgICAgbWVyZ2VfbGVnZW5kICAgICAgICAgICA9IFRSVUUsCiAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSAgICA9ICJyaWdodCIsCiAgICAgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJyaWdodCIsCiAgICAgcGFkZGluZyA9IHVuaXQoYyg1LCA1LCA1LCA1KSwgIm1tIikpCmRldi5vZmYoKQoKIyBEcmF3IGluIG5vdGVib29rCmRyYXcoaHQsCiAgICAgbWVyZ2VfbGVnZW5kICAgICAgICAgICA9IFRSVUUsCiAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSAgICA9ICJyaWdodCIsCiAgICAgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJyaWdodCIsCiAgICAgcGFkZGluZyA9IHVuaXQoYyg1LCA1LCA1LCA1KSwgIm1tIikpCgoKCmBgYAoKIyBUZXN0MwpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIExJQlJBUklFUwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDEuIERlZmluZSBURiBwYW5lbCBtZXRhZGF0YQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnRmX21ldGEgPC0gZGF0YS5mcmFtZSgKICBURiA9IGMoCiAgICAjIC0tLSAxLiBHZW5lcmFsIE1hbGlnbmFuY3kgLS0tCiAgICAiTVlDIiwiRTJGNCIsIlRXSVNUMSIsIklSRjQiLAoKICAgICMgLS0tIDIuIFRDUiBTaWduYWxpbmcgVHJpYWQgLS0tCiAgICAiSlVOQiIsIkZPUyIsIkZPU0wxIiwgICAgICAgICAgICAgICAgIyBBUC0xCiAgICAiTkZBVEMxIiwiTkZBVEMyIiwgICAgICAgICAgICAgICAgICAgIyBORkFUCiAgICAiUkVMQSIsIlJFTCIsIk5GS0IxIiwiSVJGMSIsIk5DT0EyIiwgIyBORi1rQgoKICAgICMgLS0tIDMuIFN0cmVzcyBSZXNwb25zZSAtLS0KICAgICJIU0YxIiwiTkZFMkwyIiwiU1JFQkYyIiwKCiAgICAjIC0tLSA0LiBUaDIgLyBKQUstU1RBVCBDb3JlIC0tLQogICAgIkdBVEEzIiwiU1RBVDYiLCJCQVRGIiwiRk9YUDMiLCJTVEFUMyIsIlNUQVQ1QiIsCgogICAgIyAtLS0gNS4gRGlmZmVyZW50aWF0aW9uIEhpZXJhcmNoeSAtLS0KICAgICJUQ0Y3IiwiTEVGMSIsIk1ZQiIsICAgICAgICAgICAgICAgICAjIFN0ZW0tbGlrZQogICAgIkUyRjEiLCJGT1hNMSIsICAgICAgICAgICAgICAgICAgICAgICMgUHJvbGlmZXJhdGlvbgogICAgIlBSRE0xIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVGVybWluYWwgRWZmZWN0b3IKCiAgICAjIC0tLSA2LiBLRUdHIEFsaWduZWQgLS0tCiAgICAiRU9NRVMiLCJUQlgyMSIsIlJVTlgzIiwgICAgICAgICAgICAgIyBOSy1saWtlIEN5dG90b3hpY2l0eQogICAgIlJGWDUiLCJDUkVCMSIsICAgICAgICAgICAgICAgICAgICAgICMgQW50aWdlbiBQcmVzZW50YXRpb24KICAgICJLTEY0IiwiRVRTMSIsIlNNQUQzIiwgICAgICAgICAgICAgICAjIE1pZ3JhdGlvbiAvIEFkaGVzaW9uCgogICAgIyAtLS0gNy4gTWljcm9lbnZpcm9ubWVudCAtLS0KICAgICJTVEFUMSIsIlNUQVQyIiwiSVJGOSIsICAgICAgICAgICAgICAjIElGTiBSZXNwb25zZQogICAgIkhJRjFBIiwiU1JFQkYxIiwgICAgICAgICAgICAgICAgICAgICMgTWV0YWJvbGlzbQoKICAgICMgLS0tIDguIFR1bW9yIFN1cHByZXNzb3JzIC8gTm9ybWFsIC0tLQogICAgIkZPWE8xIiwiRk9YTzQiLCJaRUIxIiwiQkFDSDIiLAogICAgIlRDRjMiLCJCQ0wxMUEiLCJORVVST0QxIiwiTUVGMkIiLCJQQlgyIiwiSVJGMyIsIkJDTDYiCiAgKSwKICBDb25kaXRpb24gPSBjKAogICAgcmVwKCJNYWxpZ25hbnQiLCA0KSwgICAjIE9uY29nZW5pYwogICAgcmVwKCJNYWxpZ25hbnQiLCAzKSwgICAjIEFQLTEKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAgIyBORkFUCiAgICByZXAoIk1hbGlnbmFudCIsIDUpLCAgICMgTkYta0IKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBTdHJlc3MgUmVzcG9uc2UKICAgIHJlcCgiTWFsaWduYW50IiwgNiksICAgIyBUaDIvSkFLLVNUQVQKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBTdGVtLWxpa2UKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAgIyBQcm9saWZlcmF0aW9uCiAgICByZXAoIk1hbGlnbmFudCIsIDEpLCAgICMgVGVybWluYWwgRWZmZWN0b3IKICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBOSy1saWtlCiAgICByZXAoIk1hbGlnbmFudCIsIDIpLCAgICMgQW50aWdlbiBQcmVzZW50YXRpb24KICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBNaWdyYXRpb24KICAgIHJlcCgiTWFsaWduYW50IiwgMyksICAgIyBJRk4gUmVzcG9uc2UKICAgIHJlcCgiTWFsaWduYW50IiwgMiksICAgIyBNZXRhYm9saXNtCiAgICByZXAoIk5vcm1hbCIsIDQpLCAgICAgICMgVHVtb3IgU3VwcHJlc3NvcgogICAgcmVwKCJOb3JtYWwiLCA3KSAgICAgICAjIE5vcm1hbCBIb21lb3N0YXNpcwogICksCiAgRnVuY3Rpb24gPSBjKAogICAgcmVwKCJPbmNvZ2VuaWMiLCA0KSwKICAgIHJlcCgiQVAtMSBTaWduYWxpbmciLCAzKSwKICAgIHJlcCgiTkZBVCBTaWduYWxpbmciLCAyKSwKICAgIHJlcCgiTkYtXHUwM2JhQiBTaWduYWxpbmciLCA1KSwKICAgIHJlcCgiU3RyZXNzIFJlc3BvbnNlIiwgMyksCiAgICByZXAoIlRoMiAvIEpBSy1TVEFUIENvcmUiLCA2KSwKICAgIHJlcCgiU3RlbS1saWtlIFByb2dlbml0b3IiLCAzKSwKICAgIHJlcCgiUHJvbGlmZXJhdGlvbiIsIDIpLAogICAgcmVwKCJUZXJtaW5hbCBFZmZlY3RvciIsIDEpLAogICAgcmVwKCJOSy1saWtlIEN5dG90b3hpY2l0eSIsIDMpLAogICAgcmVwKCJBbnRpZ2VuIFByZXNlbnRhdGlvbiIsIDIpLAogICAgcmVwKCJNaWdyYXRpb24gLyBBZGhlc2lvbiIsIDMpLAogICAgcmVwKCJJRk4gUmVzcG9uc2UiLCAzKSwKICAgIHJlcCgiTWV0YWJvbGlzbSIsIDIpLAogICAgcmVwKCJUdW1vciBTdXBwcmVzc29yIiwgNCksCiAgICByZXAoIk5vcm1hbCBIb21lb3N0YXNpcyIsIDcpCiAgKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQoKIyBMb2NrIGluIG9yZGVyCmRlc2lyZWRfb3JkZXIgPC0gYygKICAiT25jb2dlbmljIiwKICAiQVAtMSBTaWduYWxpbmciLAogICJORkFUIFNpZ25hbGluZyIsCiAgIk5GLVx1MDNiYUIgU2lnbmFsaW5nIiwKICAiU3RyZXNzIFJlc3BvbnNlIiwKICAiVGgyIC8gSkFLLVNUQVQgQ29yZSIsCiAgIlN0ZW0tbGlrZSBQcm9nZW5pdG9yIiwKICAiUHJvbGlmZXJhdGlvbiIsCiAgIlRlcm1pbmFsIEVmZmVjdG9yIiwKICAiTkstbGlrZSBDeXRvdG94aWNpdHkiLAogICJBbnRpZ2VuIFByZXNlbnRhdGlvbiIsCiAgIk1pZ3JhdGlvbiAvIEFkaGVzaW9uIiwKICAiSUZOIFJlc3BvbnNlIiwKICAiTWV0YWJvbGlzbSIsCiAgIlR1bW9yIFN1cHByZXNzb3IiLAogICJOb3JtYWwgSG9tZW9zdGFzaXMiCikKCnRmX21ldGEkRnVuY3Rpb24gPC0gZmFjdG9yKHRmX21ldGEkRnVuY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBkZXNpcmVkX29yZGVyKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDIuIEV4dHJhY3QgVEYgYWN0aXZpdHkgbWF0cml4IGZyb20gU2V1cmF0CiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KbWF0X3NjYWxlZCA8LSB0cnlDYXRjaCgKICBTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShzZXVyYXRfb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gImRvcm90aGVhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXllciA9ICJzY2FsZS5kYXRhIiksCiAgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMCikKbWF0X2RhdGEgPC0gU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiZG9yb3RoZWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXllciA9ICJkYXRhIikKbWF0X3VzZSA8LSBpZiAoIWlzLm51bGwobWF0X3NjYWxlZCkgJiYKICAgICAgICAgICAgICAgbnJvdyhtYXRfc2NhbGVkKSA+IDApIG1hdF9zY2FsZWQgZWxzZSBtYXRfZGF0YQoKIyBGaWx0ZXIgZm9yIFRGcyBwcmVzZW50IGluIFNldXJhdAphdmFpbGFibGVfdGZzIDwtIGludGVyc2VjdCh0Zl9tZXRhJFRGLCByb3duYW1lcyhtYXRfdXNlKSkKbWF0X3VzZSAgICAgICA8LSBtYXRfdXNlW2F2YWlsYWJsZV90ZnMsICwgZHJvcCA9IEZBTFNFXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDMuIEF2ZXJhZ2UgcGVyIG9yaWcuaWRlbnQgJiB6LXNjb3JlCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0Kb3JpZ19pZGVudCA8LSBhcy5mYWN0b3Ioc2V1cmF0X29iaiRvcmlnLmlkZW50KQoKYXZnX21hdCA8LSBzYXBwbHkobGV2ZWxzKG9yaWdfaWRlbnQpLCBmdW5jdGlvbihpZCkgewogIE1hdHJpeDo6cm93TWVhbnMobWF0X3VzZVssIG9yaWdfaWRlbnQgPT0gaWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcm9wID0gRkFMU0VdKQp9KQpjb2xuYW1lcyhhdmdfbWF0KSA8LSBsZXZlbHMob3JpZ19pZGVudCkKCmF2Z19tYXRfeiAgICAgICAgICAgICAgIDwtIHQoc2NhbGUodChhdmdfbWF0KSkpCmF2Z19tYXRfeltpcy5uYShhdmdfbWF0X3opXSA8LSAwCgojIEFsaWduIHRmX21ldGEgb3JkZXIgdG8gbWF0Y2ggbWF0X3VzZSBwcmVjaXNlbHkKcm93X29yZGVyX2lkeCAgICA8LSBtYXRjaChyb3duYW1lcyhhdmdfbWF0X3opLCB0Zl9tZXRhJFRGKQp0Zl9tZXRhX2ZpbHRlcmVkIDwtIHRmX21ldGFbcm93X29yZGVyX2lkeCwgXQphdmdfbWF0X3ogICAgICAgIDwtIGF2Z19tYXRfeltvcmRlcih0Zl9tZXRhX2ZpbHRlcmVkJEZ1bmN0aW9uKSwgXQp0Zl9tZXRhX2ZpbHRlcmVkIDwtIHRmX21ldGFfZmlsdGVyZWRbb3JkZXIodGZfbWV0YV9maWx0ZXJlZCRGdW5jdGlvbiksIF0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA0LiBEZWZpbmUgc2FtcGxlIG1ldGFkYXRhCiMgRklYRUQ6IG5hbWVzIG1hdGNoIGFjdHVhbCBvcmlnLmlkZW50IGxldmVscwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CnNhbXBsZV90b19wYXRpZW50IDwtIGMoCiAgIkwxIiAgICAgICA9ICJQYXRpZW50IDEiLAogICJMMiIgICAgICAgPSAiUGF0aWVudCAxIiwKICAiTDMiICAgICAgID0gIlBhdGllbnQgMiIsCiAgIkw0IiAgICAgICA9ICJQYXRpZW50IDIiLAogICJMNSIgICAgICAgPSAiUGF0aWVudCAzIiwKICAiTDYiICAgICAgID0gIlBhdGllbnQgMyIsCiAgIkw3IiAgICAgICA9ICJQYXRpZW50IDMiLAogICJDRDRUX2xhYiIgPSAiTm9ybWFsIiwKICAiQ0Q0VF8xMHgiID0gIk5vcm1hbCIKKQoKc2FtcGxlX3RvX3R5cGUgPC0gYygKICAiTDEiICAgICAgID0gIk1hbGlnbmFudCIsCiAgIkwyIiAgICAgICA9ICJNYWxpZ25hbnQiLAogICJMMyIgICAgICAgPSAiTWFsaWduYW50IiwKICAiTDQiICAgICAgID0gIk1hbGlnbmFudCIsCiAgIkw1IiAgICAgICA9ICJNYWxpZ25hbnQiLAogICJMNiIgICAgICAgPSAiTWFsaWduYW50IiwKICAiTDciICAgICAgID0gIk1hbGlnbmFudCIsCiAgIkNENFRfbGFiIiA9ICJOb3JtYWwiLAogICJDRDRUXzEweCIgPSAiTm9ybWFsIgopCgojIEV4dHJhY3QgYW5ub3RhdGlvbnMKcGF0aWVudF9hbm5vdGF0aW9uICA8LSBzYW1wbGVfdG9fcGF0aWVudFtjb2xuYW1lcyhhdmdfbWF0X3opXQpjZWxsdHlwZV9hbm5vdGF0aW9uIDwtIHNhbXBsZV90b190eXBlW2NvbG5hbWVzKGF2Z19tYXRfeildCgojIFZlcmlmeSBubyBOQXMg4oCUIHNob3VsZCBwcmludCBjaGFyYWN0ZXIoMCkgZm9yIGJvdGgKY2F0KCJQYXRpZW50IE5BczoiLCBjb2xuYW1lcyhhdmdfbWF0X3opW2lzLm5hKHBhdGllbnRfYW5ub3RhdGlvbildLCAiXG4iKQpjYXQoIlR5cGUgTkFzOiIsICAgIGNvbG5hbWVzKGF2Z19tYXRfeilbaXMubmEoY2VsbHR5cGVfYW5ub3RhdGlvbildLCAiXG4iKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDUuIENvbHVtbiBhbm5vdGF0aW9uICh0b3ApCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KaGFfY29sIDwtIEhlYXRtYXBBbm5vdGF0aW9uKAogIENlbGxfVHlwZSA9IGNlbGx0eXBlX2Fubm90YXRpb24sCiAgUGF0aWVudCAgID0gcGF0aWVudF9hbm5vdGF0aW9uLAogIGNvbCA9IGxpc3QoCiAgICBDZWxsX1R5cGUgPSBjKAogICAgICAiTm9ybWFsIiAgICA9ICIjNERBRjRBIiwKICAgICAgIk1hbGlnbmFudCIgPSAiI0MwMDAwMCIKICAgICksCiAgICBQYXRpZW50ID0gYygKICAgICAgIlBhdGllbnQgMSIgPSAiI0U0MUExQyIsCiAgICAgICJQYXRpZW50IDIiID0gIiMzNzdFQjgiLAogICAgICAiUGF0aWVudCAzIiA9ICIjRkY3RjAwIiwKICAgICAgIk5vcm1hbCIgICAgPSAiIzREQUY0QSIKICAgICkKICApLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImxlZnQiLAogIGFubm90YXRpb25fbmFtZV9ncCAgID0gZ3Bhcihmb250c2l6ZSAgPSA5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgICAgICA9ICJibGFjayIpLAogIHNpbXBsZV9hbm5vX3NpemUgICAgID0gdW5pdCg1LCAibW0iKSwKICBib3JkZXIgICAgICAgICAgICAgICA9IFRSVUUKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDYuIFJvdyBhbm5vdGF0aW9uIGNvbG9ycwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CmZ1bmN0aW9uX2NvbG9ycyA8LSBjKAogICJPbmNvZ2VuaWMiICAgICAgICAgICAgID0gIiM4MDgwODAiLAogICJBUC0xIFNpZ25hbGluZyIgICAgICAgID0gIiNGRjhDMDAiLAogICJORkFUIFNpZ25hbGluZyIgICAgICAgID0gIiNGRjQ1MDAiLAogICJORi1cdTAzYmFCIFNpZ25hbGluZyIgPSAiI0UzMUExQyIsCiAgIlN0cmVzcyBSZXNwb25zZSIgICAgICAgPSAiI0ZGRDcwMCIsCiAgIlRoMiAvIEpBSy1TVEFUIENvcmUiICA9ICIjOTg0RUEzIiwKICAiU3RlbS1saWtlIFByb2dlbml0b3IiICA9ICIjRkYxNDkzIiwKICAiUHJvbGlmZXJhdGlvbiIgICAgICAgICA9ICIjMUU5MEZGIiwKICAiVGVybWluYWwgRWZmZWN0b3IiICAgICA9ICIjODAwMDgwIiwKICAiTkstbGlrZSBDeXRvdG94aWNpdHkiICA9ICIjRjc4MUJGIiwKICAiQW50aWdlbiBQcmVzZW50YXRpb24iICA9ICIjNjZDREFBIiwKICAiTWlncmF0aW9uIC8gQWRoZXNpb24iICA9ICIjOEEyQkUyIiwKICAiSUZOIFJlc3BvbnNlIiAgICAgICAgICA9ICIjMDBDRUQxIiwKICAiTWV0YWJvbGlzbSIgICAgICAgICAgICA9ICIjQTY1NjI4IiwKICAiVHVtb3IgU3VwcHJlc3NvciIgICAgICA9ICIjMzc3RUI4IiwKICAiTm9ybWFsIEhvbWVvc3Rhc2lzIiAgICA9ICIjNERBRjRBIgopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNy4gUm93IGFubm90YXRpb24gKGxlZnQpCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KaGFfcm93IDwtIHJvd0Fubm90YXRpb24oCiAgQ29uZGl0aW9uID0gdGZfbWV0YV9maWx0ZXJlZCRDb25kaXRpb24sCiAgRnVuY3Rpb24gID0gdGZfbWV0YV9maWx0ZXJlZCRGdW5jdGlvbiwKICBjb2wgPSBsaXN0KAogICAgQ29uZGl0aW9uID0gYygKICAgICAgIk5vcm1hbCIgICAgPSAiIzREQUY0QSIsCiAgICAgICJNYWxpZ25hbnQiID0gIiNDMDAwMDAiCiAgICApLAogICAgRnVuY3Rpb24gPSBmdW5jdGlvbl9jb2xvcnMKICApLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImJvdHRvbSIsCiAgYW5ub3RhdGlvbl9uYW1lX2dwICAgPSBncGFyKGZvbnRzaXplICA9IDksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCAgICAgID0gImJsYWNrIiksCiAgc2ltcGxlX2Fubm9fc2l6ZSAgICAgPSB1bml0KDUsICJtbSIpLAogIGJvcmRlciAgICAgICAgICAgICAgID0gVFJVRSwKICBnYXAgICAgICAgICAgICAgICAgICA9IHVuaXQoMSwgIm1tIikKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDguIEhlYXRtYXAgY29sb3Igc2NhbGUg4oCUIDUgcG9pbnQgZ3JhZGllbnQKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjb2xfZnVuIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKAogIGMoLTMsIC0xLjUsIDAsIDEuNSwgMyksCiAgYygiIzIxNjZBQyIsICAgIyBEZWVwIGJsdWUKICAgICIjOTJDNURFIiwgICAjIExpZ2h0IGJsdWUKICAgICIjRjdGN0Y3IiwgICAjIE9mZi13aGl0ZQogICAgIiNGNEE1ODIiLCAgICMgTGlnaHQgcmVkCiAgICAiI0IyMTgyQiIpICAgIyBEZWVwIHJlZAopCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgOS4gQnVpbGQgaGVhdG1hcAojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Cmh0IDwtIEhlYXRtYXAoCiAgYXZnX21hdF96LAogIG5hbWUgPSAiVEYgYWN0aXZpdHlcbih6LXNjb3JlKSIsCiAgY29sICA9IGNvbF9mdW4sCgogICMgQW5ub3RhdGlvbnMKICB0b3BfYW5ub3RhdGlvbiAgPSBoYV9jb2wsCiAgbGVmdF9hbm5vdGF0aW9uID0gaGFfcm93LAoKICAjIFNwbGl0dGluZyDigJQgY29sdW1ucyBieSBwYXRpZW50LCByb3dzIGJ5IGZ1bmN0aW9uCiAgY29sdW1uX3NwbGl0ID0gcGF0aWVudF9hbm5vdGF0aW9uLAogIHJvd19zcGxpdCAgICA9IHRmX21ldGFfZmlsdGVyZWQkRnVuY3Rpb24sCgogICMgQ2x1c3RlcmluZwogIGNsdXN0ZXJfcm93X3NsaWNlcyA9IEZBTFNFLAogIGNsdXN0ZXJfcm93cyAgICAgICA9IEZBTFNFLAogIGNsdXN0ZXJfY29sdW1ucyAgICA9IFRSVUUsCiAgc2hvd19yb3dfZGVuZCAgICAgID0gRkFMU0UsCiAgc2hvd19jb2x1bW5fZGVuZCAgID0gVFJVRSwKICBjb2x1bW5fZGVuZF9oZWlnaHQgPSB1bml0KDE1LCAibW0iKSwKCiAgIyBDZWxsIGFwcGVhcmFuY2UKICByZWN0X2dwID0gZ3Bhcihjb2wgPSAid2hpdGUiLCBsd2QgPSAwLjgpLAoKICAjIFJvdyBuYW1lcyDigJQgQk9MRCBmb3Igc2xpZGUgdmlzaWJpbGl0eQogIHJvd19uYW1lc19ncCAgID0gZ3Bhcihmb250c2l6ZSAgPSAxMiwKICAgICAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgICAgICA9ICJibGFjayIpLAogIHJvd19uYW1lc19zaWRlID0gInJpZ2h0IiwKCiAgIyBDb2x1bW4gbmFtZXMg4oCUIEJPTEQgZm9yIHNsaWRlIHZpc2liaWxpdHkKICBjb2x1bW5fbmFtZXNfZ3AgID0gZ3Bhcihmb250c2l6ZSAgPSAxMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sICAgICAgPSAiYmxhY2siKSwKICBjb2x1bW5fbmFtZXNfcm90ID0gNDUsCgogICMgUm93IHRpdGxlCiAgcm93X3RpdGxlX3JvdCAgPSAwLAogIHJvd190aXRsZV9ncCAgID0gZ3Bhcihmb250c2l6ZSAgPSAxMSwKICAgICAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgICAgICA9ICJibGFjayIpLAogIHJvd190aXRsZV9zaWRlID0gImxlZnQiLAoKICAjIEdhcHMKICByb3dfZ2FwICAgID0gdW5pdCgyLjUsICJtbSIpLAogIGNvbHVtbl9nYXAgPSB1bml0KDYsICAgIm1tIiksCgogICMgTWFpbiB0aXRsZSDigJQgZGVmaW5lZCBvbmx5IG9uY2UKICBjb2x1bW5fdGl0bGUgICAgPSAiRnVuY3Rpb25hbCBURiBNb2R1bGVzIHBlciBDZWxsIExpbmUg4oCUIFPDqXphcnkgU3luZHJvbWUiLAogIGNvbHVtbl90aXRsZV9ncCA9IGdwYXIoZm9udHNpemUgID0gMTQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgICAgICA9ICJibGFjayIpLAoKICAjIExlZ2VuZCDigJQgYXQvbGFiZWxzIHJlbW92ZWQgdG8gYXZvaWQgdmVyc2lvbiBlcnJvcnMKICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QoCiAgICBkaXJlY3Rpb24gICAgID0gInZlcnRpY2FsIiwKICAgIHRpdGxlICAgICAgICAgPSAiVEYgYWN0aXZpdHlcbih6LXNjb3JlKSIsCiAgICB0aXRsZV9ncCAgICAgID0gZ3Bhcihmb250c2l6ZSAgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCAgICAgID0gImJsYWNrIiksCiAgICBsYWJlbHNfZ3AgICAgID0gZ3Bhcihmb250c2l6ZSAgPSA5LAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgICAgICA9ICJibGFjayIpLAogICAgbGVnZW5kX2hlaWdodCA9IHVuaXQoNCwgImNtIiksCiAgICBib3JkZXIgICAgICAgID0gImdyZXk1MCIKICApCikKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAxMC4gU2F2ZQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgUERGIOKAlCB2ZWN0b3IgcXVhbGl0eSBmb3IgcHVibGljYXRpb24KcGRmKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfVEZfSGVhdG1hcF9PcmlnSWRlbnRfRmluYWwucGRmIiwKICAgIHdpZHRoICAgICAgID0gMTgsCiAgICBoZWlnaHQgICAgICA9IDE2LAogICAgdXNlRGluZ2JhdHMgPSBGQUxTRSkKZHJhdyhodCwKICAgICBtZXJnZV9sZWdlbmQgICAgICAgICAgID0gVFJVRSwKICAgICBoZWF0bWFwX2xlZ2VuZF9zaWRlICAgID0gInJpZ2h0IiwKICAgICBhbm5vdGF0aW9uX2xlZ2VuZF9zaWRlID0gInJpZ2h0IiwKICAgICBwYWRkaW5nID0gdW5pdChjKDUsIDUsIDUsIDUpLCAibW0iKSkKZGV2Lm9mZigpCgojIFBORyDigJQgaGlnaCByZXNvbHV0aW9uIGZvciBwcmVzZW50YXRpb25zCnBuZygiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX1RGX0hlYXRtYXBfT3JpZ0lkZW50X0ZpbmFsLnBuZyIsCiAgICB3aWR0aCAgPSAxOCAqIDMwMCwKICAgIGhlaWdodCA9IDE2ICogMzAwLAogICAgcmVzICAgID0gMzAwKQpkcmF3KGh0LAogICAgIG1lcmdlX2xlZ2VuZCAgICAgICAgICAgPSBUUlVFLAogICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgICAgPSAicmlnaHQiLAogICAgIGFubm90YXRpb25fbGVnZW5kX3NpZGUgPSAicmlnaHQiLAogICAgIHBhZGRpbmcgPSB1bml0KGMoNSwgNSwgNSwgNSksICJtbSIpKQpkZXYub2ZmKCkKCiMgRHJhdyBpbiBub3RlYm9vawpkcmF3KGh0LAogICAgIG1lcmdlX2xlZ2VuZCAgICAgICAgICAgPSBUUlVFLAogICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgICAgPSAicmlnaHQiLAogICAgIGFubm90YXRpb25fbGVnZW5kX3NpZGUgPSAicmlnaHQiLAogICAgIHBhZGRpbmcgPSB1bml0KGMoNSwgNSwgNSwgNSksICJtbSIpKQoKYGBgCgoKCgoKCgoKIyBEZWZpbmUgVGgxL1RoMi9UaDE3L1RoMjIvVHJlZyBNYXN0ZXIgUmVndWxhdG9yIFBhbmVsCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBMSUJSQVJJRVMKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShncmlkKQpsaWJyYXJ5KFNldXJhdE9iamVjdCkKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAxLiBEZWZpbmUgVGgxL1RoMi9UaDE3L1RoMjIvVHJlZyBNYXN0ZXIgUmVndWxhdG9yIFBhbmVsCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KdGZfbWV0YV90aGVscGVyIDwtIGRhdGEuZnJhbWUoCiAgVEYgPSBjKAogICAgIyBUaDEgTWFzdGVyIFJlZ3VsYXRvcnMKICAgICJUQlgyMSIsICJTVEFUMSIsICJTVEFUNCIsICJJUkYxIiwKICAgIAogICAgIyBUaDIgTWFzdGVyIFJlZ3VsYXRvcnMgIAogICAgIkdBVEEzIiwgIlNUQVQ2IiwgIkJBVEYiLCAiSVJGNCIsCiAgICAKICAgICMgVGgxNyAvIFRoMjIgTWFzdGVyIFJlZ3VsYXRvcnMKICAgICJST1JDIiwgIlNUQVQzIiwgIkFIUiIsICJNQUYiLAogICAgCiAgICAjIFRyZWcgTWFzdGVyIFJlZ3VsYXRvcnMKICAgICJGT1hQMyIsICJGT1hPMSIsICJDVExBNCIgICMgQ1RMQTQgcmVndWxvbiBhcyBUcmVnIHByb3h5CiAgKSwKICBGdW5jdGlvbiA9IGMoCiAgICByZXAoIlRoMSIsIDQpLAogICAgcmVwKCJUaDIiLCA0KSwKICAgIHJlcCgiVGgxNy9UaDIyIiwgNCksCiAgICByZXAoIlRyZWciLCAzKQogICksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKCiMgTG9jayBpbiBjYW5vbmljYWwgb3JkZXI6IFRoMSDihpIgVGgyIOKGkiBUaDE3L1RoMjIg4oaSIFRyZWcKdGZfbWV0YV90aGVscGVyJEZ1bmN0aW9uIDwtIGZhY3Rvcih0Zl9tZXRhX3RoZWxwZXIkRnVuY3Rpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIlRoMSIsICJUaDIiLCAiVGgxNy9UaDIyIiwgIlRyZWciKSkKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAyLiBFeHRyYWN0IFRGIGFjdGl2aXR5IG1hdHJpeAojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Cm1hdF9zY2FsZWQgPC0gdHJ5Q2F0Y2goU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJzY2FsZS5kYXRhIiksIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTCkKbWF0X2RhdGEgPC0gU2V1cmF0T2JqZWN0OjpHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJkYXRhIikKbWF0X3VzZSA8LSBpZiAoIWlzLm51bGwobWF0X3NjYWxlZCkgJiYgbnJvdyhtYXRfc2NhbGVkKSA+IDApIG1hdF9zY2FsZWQgZWxzZSBtYXRfZGF0YQoKIyBGaWx0ZXIgZm9yIGF2YWlsYWJsZSBURnMKYXZhaWxhYmxlX3RmcyA8LSBpbnRlcnNlY3QodGZfbWV0YV90aGVscGVyJFRGLCByb3duYW1lcyhtYXRfdXNlKSkKbWF0X3VzZSA8LSBtYXRfdXNlW2F2YWlsYWJsZV90ZnMsICwgZHJvcCA9IEZBTFNFXQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDMuIEF2ZXJhZ2UgcGVyIGNsdXN0ZXIgJiB6LXNjb3JlCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY2x1c3RlcnMgPC0gYXMuZmFjdG9yKHNldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzKQphdmdfbWF0IDwtIHNhcHBseShsZXZlbHMoY2x1c3RlcnMpLCBmdW5jdGlvbihjbCkgTWF0cml4Ojpyb3dNZWFucyhtYXRfdXNlWywgY2x1c3RlcnMgPT0gY2wsIGRyb3AgPSBGQUxTRV0pKQpjb2xuYW1lcyhhdmdfbWF0KSA8LSBsZXZlbHMoY2x1c3RlcnMpCgphdmdfbWF0X3ogPC0gdChzY2FsZSh0KGF2Z19tYXQpKSkKYXZnX21hdF96W2lzLm5hKGF2Z19tYXRfeildIDwtIDAKCiMgQWxpZ24gdG8gb3VyIGRlZmluZWQgaGVscGVyIFQgcGFuZWwgb3JkZXIKcm93X29yZGVyX2lkeCA8LSBtYXRjaChyb3duYW1lcyhhdmdfbWF0X3opLCB0Zl9tZXRhX3RoZWxwZXIkVEYpCnRmX21ldGFfdGhlbHBlcl9maWx0ZXJlZCA8LSB0Zl9tZXRhX3RoZWxwZXJbcm93X29yZGVyX2lkeCwgXQphdmdfbWF0X3ogPC0gYXZnX21hdF96W29yZGVyKHRmX21ldGFfdGhlbHBlcl9maWx0ZXJlZCRGdW5jdGlvbiksIF0KdGZfbWV0YV90aGVscGVyX2ZpbHRlcmVkIDwtIHRmX21ldGFfdGhlbHBlcl9maWx0ZXJlZFtvcmRlcih0Zl9tZXRhX3RoZWxwZXJfZmlsdGVyZWQkRnVuY3Rpb24pLCBdCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgNC4gQ29sdW1uICYgcm93IGFubm90YXRpb25zCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KY2x1c3Rlcl9zdGF0dXMgPC0gaWZlbHNlKGNvbG5hbWVzKGF2Z19tYXRfeikgJWluJSBjKCIzIiwiMTAiKSwgIk5vcm1hbCBDRDQgVCBjZWxscyIsICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMiKQpoYV9jb2wgPC0gSGVhdG1hcEFubm90YXRpb24oCiAgQ2VsbF9TdGF0ZSA9IGNsdXN0ZXJfc3RhdHVzLCAKICBjb2wgPSBsaXN0KENlbGxfU3RhdGUgPSBjKCJOb3JtYWwgQ0Q0IFQgY2VsbHMiID0gIiM0REFGNEEiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIiA9ICIjRTQxQTFDIikpLCAKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJsZWZ0IgopCgojIENsYXNzaWNhbCBUIGhlbHBlciBjb2xvciBzY2hlbWUKaGVscGVyX2NvbG9ycyA8LSBjKAogICJUaDEiID0gIiNFMzFBMUMiLCAgICAgICMgUmVkCiAgIlRoMiIgPSAiIzFGNzhCNCIsICAgICAgIyBCbHVlICAKICAiVGgxNy9UaDIyIiA9ICIjRkY3RjAwIiwgIyBPcmFuZ2UKICAiVHJlZyIgPSAiIzMzQTAyQyIgICAgICAjIEdyZWVuCikKCmhhX3JvdyA8LSByb3dBbm5vdGF0aW9uKAogIEZ1bmN0aW9uID0gdGZfbWV0YV90aGVscGVyX2ZpbHRlcmVkJEZ1bmN0aW9uLCAKICBjb2wgPSBsaXN0KEZ1bmN0aW9uID0gaGVscGVyX2NvbG9ycyksIAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImJvdHRvbSIKKQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIDUuIEhlYXRtYXAgY29sb3JzICYgcGxvdHRpbmcKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpjb2xfZnVuIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTMsMCwzKSwgYygiIzMxMzY5NSIsIndoaXRlIiwiI0E1MDAyNiIpKQoKaHRfdGhlbHBlciA8LSBIZWF0bWFwKAogIGF2Z19tYXRfeiwgCiAgbmFtZSA9ICJURiBhY3Rpdml0eSAoeikiLCAKICBjb2wgPSBjb2xfZnVuLCAKICB0b3BfYW5ub3RhdGlvbiA9IGhhX2NvbCwgCiAgbGVmdF9hbm5vdGF0aW9uID0gaGFfcm93LCAKICBjb2x1bW5fc3BsaXQgPSBjbHVzdGVyX3N0YXR1cywgCiAgcm93X3NwbGl0ID0gdGZfbWV0YV90aGVscGVyX2ZpbHRlcmVkJEZ1bmN0aW9uLCAKICBjbHVzdGVyX3Jvd19zbGljZXMgPSBGQUxTRSwgCiAgY2x1c3Rlcl9yb3dzID0gRkFMU0UsIAogIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsIAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwgCiAgc2hvd19jb2x1bW5fZGVuZCA9IFRSVUUsIAogIHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMSwgZm9udGZhY2UgPSAiYm9sZCIpLCAKICBjb2x1bW5fbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTEpLCAKICBjb2x1bW5fdGl0bGUgPSAiQ0Q0KyBUIEhlbHBlciBMaW5lYWdlIFRyYW5zY3JpcHRpb24gRmFjdG9ycyIsIAogIHJvd190aXRsZV9yb3QgPSAwLCAKICByb3dfdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gMTAsIGZvbnRmYWNlID0gImJvbGQiKSwgCiAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KGRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpCikKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyA2LiBTYXZlIHBsb3RzCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KcGRmKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfVEZfSGVhdG1hcF9USGVscGVyX0xpbmVhZ2VzLnBkZiIsIHdpZHRoPTEyLCBoZWlnaHQ9OCkKZHJhdyhodF90aGVscGVyLCBtZXJnZV9sZWdlbmQ9VFJVRSkKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV9URl9IZWF0bWFwX1RIZWxwZXJfTGluZWFnZXMucG5nIiwgd2lkdGg9MTIqMzAwLCBoZWlnaHQ9OCozMDAsIHJlcz0zMDApCmRyYXcoaHRfdGhlbHBlciwgbWVyZ2VfbGVnZW5kPVRSVUUpCmRldi5vZmYoKQoKZHJhdyhodF90aGVscGVyLCBtZXJnZV9sZWdlbmQ9VFJVRSkKYGBgCgoKCkZpbmFsIFNhdmUKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNn0KcHJpbnQoIkFuYWx5c2lzIHBpcGVsaW5lIGNvbXBsZXRlLiBBbGwgZmlndXJlcyBhbmQgb2JqZWN0cyBzYXZlZCBpbiBPdXRwdXRfRmlndXJlcyBmb2xkZXIuIikKYGBgCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCg==