1 Setup and Libraries

# Data Processing & Single-Cell Analysis
library(dplyr)
library(tibble)
library(tidyr)
library(Seurat)
library(Matrix)

# Visualization
library(ggplot2)
library(patchwork)
library(ComplexHeatmap)
library(circlize)
library(grid)
library(EnhancedVolcano)

# Create output directories if they do not exist
dir.create("Output_Figures", showWarnings = FALSE)
dir.create("Output_Tables", showWarnings = FALSE)

2 Load Seurat Object and Define Conditions

# Load Seurat Object directly from cluster relative path
seurat_obj <- readRDS("../../0-Seurat_objects/Seurat_Object_With_TF_Activity.rds")
Idents(seurat_obj) <- "seurat_clusters"

# Define Malignant vs Non-Malignant (Clusters 3 & 10 = Normal CD4+ T reference)
non_malignant_clusters <- c(3, 10)
seurat_obj$Condition <- ifelse(seurat_obj$seurat_clusters %in% non_malignant_clusters, 
                               "Non-Malignant", 
                               "Malignant")
seurat_obj$Condition <- factor(seurat_obj$Condition, levels = c("Non-Malignant", "Malignant"))

print("Seurat Object Loaded successfully:")
[1] "Seurat Object Loaded successfully:"
table(seurat_obj$Condition)

Non-Malignant     Malignant 
         7873         41432 

3 Differential Statistics for MAX & Network Partners

# --- 1. Gene Expression (SCT Assay) ---
DefaultAssay(seurat_obj) <- "SCT"
Idents(seurat_obj) <- "Condition"

candidate_genes <- intersect(
  c("MAX", "MYC", "MLX", "MLXIP", "MLXIPL", "MNT", "MXD1", "MXI1", "E2F1", "CLIC1"),
  rownames(seurat_obj[["SCT"]])
)

deg_sct <- FindMarkers(
  seurat_obj,
  features = candidate_genes,
  ident.1 = "Malignant",
  ident.2 = "Non-Malignant",
  logfc.threshold = 0,
  min.pct = 0
)

  |                                                  | 0 % ~calculating  
  |+++++                                             | 10% ~00s          
  |++++++++++                                        | 20% ~00s          
  |+++++++++++++++                                   | 30% ~00s          
  |++++++++++++++++++++                              | 40% ~00s          
  |+++++++++++++++++++++++++                         | 50% ~00s          
  |++++++++++++++++++++++++++++++                    | 60% ~00s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~00s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
deg_sct$Gene <- rownames(deg_sct)
deg_sct$Assay <- "SCT (mRNA Expression)"

# --- 2. TF Activity (dorothea Assay) ---
DefaultAssay(seurat_obj) <- "dorothea"

candidate_tfs <- intersect(
  c("MAX", "MYC", "E2F1", "E2F4", "FOXM1", "FOXO1"),
  rownames(seurat_obj[["dorothea"]])
)

deg_tf <- FindMarkers(
  seurat_obj,
  features = candidate_tfs,
  ident.1 = "Malignant",
  ident.2 = "Non-Malignant",
  logfc.threshold = 0,
  min.pct = 0
)

  |                                                  | 0 % ~calculating  
  |+++++++++++++++++                                 | 33% ~01s          
  |++++++++++++++++++++++++++++++++++                | 67% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
deg_tf$Gene <- rownames(deg_tf)
deg_tf$Assay <- "dorothea (TF Activity)"

# Combine and save table for reviewer response
deg_summary <- bind_rows(deg_sct, deg_tf) %>%
  select(Assay, Gene, avg_log2FC, pct.1, pct.2, p_val, p_val_adj)

write.csv(deg_summary, "Output_Tables/MAX_Network_Differential_Summary.csv", row.names = FALSE)
print("=== Differential Statistics for MAX & Key Network Members ===")
[1] "=== Differential Statistics for MAX & Key Network Members ==="
print(deg_summary)

4 UMAP Feature Plots: MAX TF Activity vs. MAX Gene Expression

# --- Panel 1: MAX TF Activity (Red = dorothea Assay) ---
DefaultAssay(seurat_obj) <- "dorothea"
p_max_act <- FeaturePlot(seurat_obj, features = "MAX", order = TRUE, reduction = "umap") +
  scale_color_gradientn(colors = c("grey90", "firebrick")) +
  ggtitle("MAX Regulon Activity (dorothea)") +
  theme(plot.title = element_text(face = "bold", size = 12))

# --- Panel 2: MAX Gene Expression (Blue = SCT Assay) ---
DefaultAssay(seurat_obj) <- "SCT"
p_max_exp <- FeaturePlot(seurat_obj, features = "MAX", order = TRUE, reduction = "umap") +
  scale_color_gradientn(colors = c("grey90", "darkblue")) +
  ggtitle("MAX Gene Expression (SCT)") +
  theme(plot.title = element_text(face = "bold", size = 12))

# Combine side-by-side
fig_max_umap <- p_max_act | p_max_exp
fig_max_umap <- fig_max_umap + plot_annotation(
  title = "MAX Network Decoupling in Sézary Syndrome (Activity vs. mRNA Expression)",
  theme = theme(plot.title = element_text(face = "bold", size = 14))
)

# Display & Save
print(fig_max_umap)

ggsave("Output_Figures/Figure_MAX_UMAP_Activity_vs_Expression.pdf", plot = fig_max_umap, width = 11, height = 5)
ggsave("Output_Figures/Figure_MAX_UMAP_Activity_vs_Expression.png", plot = fig_max_umap, width = 11, height = 5, dpi = 300)

5 Violin Plots: Malignant vs. Non-Malignant Quantification

# Panel A: MAX Expression (SCT)
vln_exp <- VlnPlot(seurat_obj, features = "MAX", assay = "SCT", group.by = "Condition", pt.size = 0.05) +
  scale_fill_manual(values = c("Non-Malignant" = "#4DAF4A", "Malignant" = "#E41A1C")) +
  ylab("Normalized Expression") +
  ggtitle("MAX mRNA Expression (SCT)") +
  theme(legend.position = "none", plot.title = element_text(face = "bold", size = 11))

# Panel B: MAX Activity (dorothea)
vln_act <- VlnPlot(seurat_obj, features = "MAX", assay = "dorothea", group.by = "Condition", pt.size = 0.05) +
  scale_fill_manual(values = c("Non-Malignant" = "#4DAF4A", "Malignant" = "#E41A1C")) +
  ylab("TF Activity Score") +
  ggtitle("MAX TF Activity (dorothea)") +
  theme(legend.position = "none", plot.title = element_text(face = "bold", size = 11))

# Panel C: MYC Activity (dorothea) for Direct Contrast
vln_myc <- VlnPlot(seurat_obj, features = "MYC", assay = "dorothea", group.by = "Condition", pt.size = 0.05) +
  scale_fill_manual(values = c("Non-Malignant" = "#4DAF4A", "Malignant" = "#E41A1C")) +
  ylab("TF Activity Score") +
  ggtitle("MYC TF Activity (dorothea)") +
  theme(legend.position = "none", plot.title = element_text(face = "bold", size = 11))

fig_vln_max <- vln_exp | vln_act | vln_myc
print(fig_vln_max)


ggsave("Output_Figures/Figure_MAX_MYC_Violin_Comparison.pdf", plot = fig_vln_max, width = 12, height = 4)
ggsave("Output_Figures/Figure_MAX_MYC_Violin_Comparison.png", plot = fig_vln_max, width = 12, height = 4, dpi = 300)

6 Cluster-Level ComplexHeatmap (MAX vs. MYC and Core Regulators)

# Select targeted panel contrasting MYC/E2F oncogenic program with MAX and tumor suppressors
tf_panel <- c(
  # Oncogenic / Proliferative
  "MYC", "E2F1", "E2F4", "FOXM1", "SREBF1", "HIF1A",
  # MAX
  "MAX",
  # Normal Homeostasis & Tumor Suppressors
  "FOXO1", "FOXO4", "TCF7", "TBX21", "RUNX3"
)

# Extract TF Activity matrix (Seurat v4/v5 compatible)
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

avail <- intersect(tf_panel, rownames(mat_use))
mat_use <- mat_use[avail, , drop = FALSE]

# Compute mean per cluster and calculate z-scores
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

# Set column annotations
cluster_status <- ifelse(colnames(avg_mat_z) %in% c("3", "10"), 
                         "Normal CD4 T", 
                         "Malignant CD4 T cells")

ha_col <- HeatmapAnnotation(
  Cell_State = cluster_status,
  col = list(Cell_State = c("Normal CD4 T" = "#4DAF4A", "Malignant CD4 T cells" = "#E41A1C")),
  annotation_name_side = "left"
)

# Color scale: -3 to +3
col_fun <- circlize::colorRamp2(c(-3, 0, 3), c("#313695", "white", "#A50026"))

ht_max <- Heatmap(
  avg_mat_z,
  name = "TF activity (z)",
  col = col_fun,
  top_annotation = ha_col,
  column_split = cluster_status,
  cluster_rows = TRUE,
  cluster_columns = TRUE,
  show_row_dend = TRUE,
  show_column_dend = TRUE,
  row_names_gp = gpar(fontsize = 11, fontface = "bold"),
  column_names_gp = gpar(fontsize = 10),
  column_title = "Cluster-Level TF Activity: MYC/E2F Activation vs. MAX Repression",
  heatmap_legend_param = list(direction = "vertical")
)

# Save and Draw
pdf("Output_Figures/Figure_MAX_MYC_Cluster_Heatmap.pdf", width = 10, height = 7)
draw(ht_max, merge_legend = TRUE)
dev.off()
null device 
          1 
png("Output_Figures/Figure_MAX_MYC_Cluster_Heatmap.png", width = 10 * 300, height = 7 * 300, res = 300)
draw(ht_max, merge_legend = TRUE)
dev.off()
null device 
          1 
draw(ht_max, merge_legend = TRUE)

7 Volcano Plot Highlighting MAX & MYC Inferred Activity

# Differential TF Activity across all TFs
DefaultAssay(seurat_obj) <- "dorothea"
Idents(seurat_obj) <- "Condition"

all_tfs_diff <- FindMarkers(
  seurat_obj, 
  ident.1 = "Malignant", 
  ident.2 = "Non-Malignant", 
  logfc.threshold = 0, 
  min.pct = 0
)

  |                                                  | 0 % ~calculating  
  |+                                                 | 2 % ~10s          
  |++                                                | 3 % ~09s          
  |+++                                               | 5 % ~09s          
  |++++                                              | 7 % ~09s          
  |+++++                                             | 8 % ~09s          
  |++++++                                            | 10% ~09s          
  |++++++                                            | 12% ~09s          
  |+++++++                                           | 14% ~08s          
  |++++++++                                          | 15% ~08s          
  |+++++++++                                         | 17% ~08s          
  |++++++++++                                        | 19% ~08s          
  |+++++++++++                                       | 20% ~08s          
  |++++++++++++                                      | 22% ~08s          
  |++++++++++++                                      | 24% ~08s          
  |+++++++++++++                                     | 25% ~07s          
  |++++++++++++++                                    | 27% ~07s          
  |+++++++++++++++                                   | 29% ~07s          
  |++++++++++++++++                                  | 31% ~07s          
  |+++++++++++++++++                                 | 32% ~07s          
  |+++++++++++++++++                                 | 34% ~07s          
  |++++++++++++++++++                                | 36% ~07s          
  |+++++++++++++++++++                               | 37% ~07s          
  |++++++++++++++++++++                              | 39% ~07s          
  |+++++++++++++++++++++                             | 41% ~06s          
  |++++++++++++++++++++++                            | 42% ~06s          
  |+++++++++++++++++++++++                           | 44% ~06s          
  |+++++++++++++++++++++++                           | 46% ~06s          
  |++++++++++++++++++++++++                          | 47% ~06s          
  |+++++++++++++++++++++++++                         | 49% ~06s          
  |++++++++++++++++++++++++++                        | 51% ~06s          
  |+++++++++++++++++++++++++++                       | 53% ~06s          
  |++++++++++++++++++++++++++++                      | 54% ~05s          
  |++++++++++++++++++++++++++++                      | 56% ~05s          
  |+++++++++++++++++++++++++++++                     | 58% ~05s          
  |++++++++++++++++++++++++++++++                    | 59% ~05s          
  |+++++++++++++++++++++++++++++++                   | 61% ~05s          
  |++++++++++++++++++++++++++++++++                  | 63% ~04s          
  |+++++++++++++++++++++++++++++++++                 | 64% ~04s          
  |++++++++++++++++++++++++++++++++++                | 66% ~04s          
  |++++++++++++++++++++++++++++++++++                | 68% ~04s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~04s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~03s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~03s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~03s          
  |+++++++++++++++++++++++++++++++++++++++           | 76% ~03s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~03s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~02s          
  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~02s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~02s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~02s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 86% ~02s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 88% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 92% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 97% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=12s  
# Key factors to highlight
tfs_to_label <- c("MAX", "MYC", "E2F1", "E2F4", "FOXM1", "FOXO1", "RELA", "IRF1", "STAT3", "RUNX3")

p_volcano_max <- EnhancedVolcano(
  all_tfs_diff,
  lab = rownames(all_tfs_diff),
  selectLab = tfs_to_label,
  x = 'avg_log2FC',
  y = 'p_val_adj',
  title = 'Differential TF Activity: Malignant vs. Non-Malignant CD4+ T Cells',
  subtitle = 'DecoupleR Inferred Activity Highlighting the MAX/MYC Axis',
  pCutoff = 1e-5,
  FCcutoff = 0.5,
  pointSize = 3.0,
  labSize = 4.5,
  drawConnectors = TRUE,
  col = c("grey60", "forestgreen", "royalblue", "firebrick2")
)

print(p_volcano_max)

ggsave("Output_Figures/Figure_MAX_Volcano_TF_Activity.pdf", plot = p_volcano_max, width = 10, height = 8)
ggsave("Output_Figures/Figure_MAX_Volcano_TF_Activity.png", plot = p_volcano_max, width = 10, height = 8, dpi = 300)

8 Completion Summary

print("MAX network analysis complete. All tables exported to Output_Tables/ and figures to Output_Figures/.")
[1] "MAX network analysis complete. All tables exported to Output_Tables/ and figures to Output_Figures/."
LS0tCnRpdGxlOiAiTUFYIE5ldHdvcmsgRGVyZWd1bGF0aW9uIEFuYWx5c2lzIGluIFPDqXphcnkgU3luZHJvbWUiCmF1dGhvcjogIk5hc2lyIE1haG1vb2QgQWJiYXNpIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICB0aGVtZTogam91cm5hbAotLS0KCiMgU2V0dXAgYW5kIExpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIERhdGEgUHJvY2Vzc2luZyAmIFNpbmdsZS1DZWxsIEFuYWx5c2lzCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShNYXRyaXgpCgojIFZpc3VhbGl6YXRpb24KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShncmlkKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKCiMgQ3JlYXRlIG91dHB1dCBkaXJlY3RvcmllcyBpZiB0aGV5IGRvIG5vdCBleGlzdApkaXIuY3JlYXRlKCJPdXRwdXRfRmlndXJlcyIsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQpkaXIuY3JlYXRlKCJPdXRwdXRfVGFibGVzIiwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCmBgYAoKIyBMb2FkIFNldXJhdCBPYmplY3QgYW5kIERlZmluZSBDb25kaXRpb25zCmBgYHtyIGxvYWQtZGF0YSwgbWVzc2FnZT1GQUxTRX0KIyBMb2FkIFNldXJhdCBPYmplY3QgZGlyZWN0bHkgZnJvbSBjbHVzdGVyIHJlbGF0aXZlIHBhdGgKc2V1cmF0X29iaiA8LSByZWFkUkRTKCIuLi8uLi8wLVNldXJhdF9vYmplY3RzL1NldXJhdF9PYmplY3RfV2l0aF9URl9BY3Rpdml0eS5yZHMiKQpJZGVudHMoc2V1cmF0X29iaikgPC0gInNldXJhdF9jbHVzdGVycyIKCiMgRGVmaW5lIE1hbGlnbmFudCB2cyBOb24tTWFsaWduYW50IChDbHVzdGVycyAzICYgMTAgPSBOb3JtYWwgQ0Q0KyBUIHJlZmVyZW5jZSkKbm9uX21hbGlnbmFudF9jbHVzdGVycyA8LSBjKDMsIDEwKQpzZXVyYXRfb2JqJENvbmRpdGlvbiA8LSBpZmVsc2Uoc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMgJWluJSBub25fbWFsaWduYW50X2NsdXN0ZXJzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOb24tTWFsaWduYW50IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWFsaWduYW50IikKc2V1cmF0X29iaiRDb25kaXRpb24gPC0gZmFjdG9yKHNldXJhdF9vYmokQ29uZGl0aW9uLCBsZXZlbHMgPSBjKCJOb24tTWFsaWduYW50IiwgIk1hbGlnbmFudCIpKQoKcHJpbnQoIlNldXJhdCBPYmplY3QgTG9hZGVkIHN1Y2Nlc3NmdWxseToiKQp0YWJsZShzZXVyYXRfb2JqJENvbmRpdGlvbikKYGBgCgojIERpZmZlcmVudGlhbCBTdGF0aXN0aWNzIGZvciBNQVggJiBOZXR3b3JrIFBhcnRuZXJzCmBgYHtyIG1heC1kaWZmZXJlbnRpYWwtc3RhdHMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgLS0tIDEuIEdlbmUgRXhwcmVzc2lvbiAoU0NUIEFzc2F5KSAtLS0KRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJTQ1QiCklkZW50cyhzZXVyYXRfb2JqKSA8LSAiQ29uZGl0aW9uIgoKY2FuZGlkYXRlX2dlbmVzIDwtIGludGVyc2VjdCgKICBjKCJNQVgiLCAiTVlDIiwgIk1MWCIsICJNTFhJUCIsICJNTFhJUEwiLCAiTU5UIiwgIk1YRDEiLCAiTVhJMSIsICJFMkYxIiwgIkNMSUMxIiksCiAgcm93bmFtZXMoc2V1cmF0X29ialtbIlNDVCJdXSkKKQoKZGVnX3NjdCA8LSBGaW5kTWFya2VycygKICBzZXVyYXRfb2JqLAogIGZlYXR1cmVzID0gY2FuZGlkYXRlX2dlbmVzLAogIGlkZW50LjEgPSAiTWFsaWduYW50IiwKICBpZGVudC4yID0gIk5vbi1NYWxpZ25hbnQiLAogIGxvZ2ZjLnRocmVzaG9sZCA9IDAsCiAgbWluLnBjdCA9IDAKKQpkZWdfc2N0JEdlbmUgPC0gcm93bmFtZXMoZGVnX3NjdCkKZGVnX3NjdCRBc3NheSA8LSAiU0NUIChtUk5BIEV4cHJlc3Npb24pIgoKIyAtLS0gMi4gVEYgQWN0aXZpdHkgKGRvcm90aGVhIEFzc2F5KSAtLS0KRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKCmNhbmRpZGF0ZV90ZnMgPC0gaW50ZXJzZWN0KAogIGMoIk1BWCIsICJNWUMiLCAiRTJGMSIsICJFMkY0IiwgIkZPWE0xIiwgIkZPWE8xIiksCiAgcm93bmFtZXMoc2V1cmF0X29ialtbImRvcm90aGVhIl1dKQopCgpkZWdfdGYgPC0gRmluZE1hcmtlcnMoCiAgc2V1cmF0X29iaiwKICBmZWF0dXJlcyA9IGNhbmRpZGF0ZV90ZnMsCiAgaWRlbnQuMSA9ICJNYWxpZ25hbnQiLAogIGlkZW50LjIgPSAiTm9uLU1hbGlnbmFudCIsCiAgbG9nZmMudGhyZXNob2xkID0gMCwKICBtaW4ucGN0ID0gMAopCmRlZ190ZiRHZW5lIDwtIHJvd25hbWVzKGRlZ190ZikKZGVnX3RmJEFzc2F5IDwtICJkb3JvdGhlYSAoVEYgQWN0aXZpdHkpIgoKIyBDb21iaW5lIGFuZCBzYXZlIHRhYmxlIGZvciByZXZpZXdlciByZXNwb25zZQpkZWdfc3VtbWFyeSA8LSBiaW5kX3Jvd3MoZGVnX3NjdCwgZGVnX3RmKSAlPiUKICBzZWxlY3QoQXNzYXksIEdlbmUsIGF2Z19sb2cyRkMsIHBjdC4xLCBwY3QuMiwgcF92YWwsIHBfdmFsX2FkaikKCndyaXRlLmNzdihkZWdfc3VtbWFyeSwgIk91dHB1dF9UYWJsZXMvTUFYX05ldHdvcmtfRGlmZmVyZW50aWFsX1N1bW1hcnkuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCnByaW50KCI9PT0gRGlmZmVyZW50aWFsIFN0YXRpc3RpY3MgZm9yIE1BWCAmIEtleSBOZXR3b3JrIE1lbWJlcnMgPT09IikKcHJpbnQoZGVnX3N1bW1hcnkpCmBgYAoKIyBVTUFQIEZlYXR1cmUgUGxvdHM6IE1BWCBURiBBY3Rpdml0eSB2cy4gTUFYIEdlbmUgRXhwcmVzc2lvbgpgYGB7ciBtYXgtdW1hcC1mZWF0dXJlLXBsb3RzLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMX0KIyAtLS0gUGFuZWwgMTogTUFYIFRGIEFjdGl2aXR5IChSZWQgPSBkb3JvdGhlYSBBc3NheSkgLS0tCkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiZG9yb3RoZWEiCnBfbWF4X2FjdCA8LSBGZWF0dXJlUGxvdChzZXVyYXRfb2JqLCBmZWF0dXJlcyA9ICJNQVgiLCBvcmRlciA9IFRSVUUsIHJlZHVjdGlvbiA9ICJ1bWFwIikgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBjKCJncmV5OTAiLCAiZmlyZWJyaWNrIikpICsKICBnZ3RpdGxlKCJNQVggUmVndWxvbiBBY3Rpdml0eSAoZG9yb3RoZWEpIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEyKSkKCiMgLS0tIFBhbmVsIDI6IE1BWCBHZW5lIEV4cHJlc3Npb24gKEJsdWUgPSBTQ1QgQXNzYXkpIC0tLQpEZWZhdWx0QXNzYXkoc2V1cmF0X29iaikgPC0gIlNDVCIKcF9tYXhfZXhwIDwtIEZlYXR1cmVQbG90KHNldXJhdF9vYmosIGZlYXR1cmVzID0gIk1BWCIsIG9yZGVyID0gVFJVRSwgcmVkdWN0aW9uID0gInVtYXAiKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGMoImdyZXk5MCIsICJkYXJrYmx1ZSIpKSArCiAgZ2d0aXRsZSgiTUFYIEdlbmUgRXhwcmVzc2lvbiAoU0NUKSIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMikpCgojIENvbWJpbmUgc2lkZS1ieS1zaWRlCmZpZ19tYXhfdW1hcCA8LSBwX21heF9hY3QgfCBwX21heF9leHAKZmlnX21heF91bWFwIDwtIGZpZ19tYXhfdW1hcCArIHBsb3RfYW5ub3RhdGlvbigKICB0aXRsZSA9ICJNQVggTmV0d29yayBEZWNvdXBsaW5nIGluIFPDqXphcnkgU3luZHJvbWUgKEFjdGl2aXR5IHZzLiBtUk5BIEV4cHJlc3Npb24pIiwKICB0aGVtZSA9IHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSkKKQoKIyBEaXNwbGF5ICYgU2F2ZQpwcmludChmaWdfbWF4X3VtYXApCmdnc2F2ZSgiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX01BWF9VTUFQX0FjdGl2aXR5X3ZzX0V4cHJlc3Npb24ucGRmIiwgcGxvdCA9IGZpZ19tYXhfdW1hcCwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gNSkKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfTUFYX1VNQVBfQWN0aXZpdHlfdnNfRXhwcmVzc2lvbi5wbmciLCBwbG90ID0gZmlnX21heF91bWFwLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA1LCBkcGkgPSAzMDApCmBgYAoKIyBWaW9saW4gUGxvdHM6IE1hbGlnbmFudCB2cy4gTm9uLU1hbGlnbmFudCBRdWFudGlmaWNhdGlvbgpgYGB7ciBtYXgtdmlvbGluLXBsb3RzLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMn0KIyBQYW5lbCBBOiBNQVggRXhwcmVzc2lvbiAoU0NUKQp2bG5fZXhwIDwtIFZsblBsb3Qoc2V1cmF0X29iaiwgZmVhdHVyZXMgPSAiTUFYIiwgYXNzYXkgPSAiU0NUIiwgZ3JvdXAuYnkgPSAiQ29uZGl0aW9uIiwgcHQuc2l6ZSA9IDAuMDUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJOb24tTWFsaWduYW50IiA9ICIjNERBRjRBIiwgIk1hbGlnbmFudCIgPSAiI0U0MUExQyIpKSArCiAgeWxhYigiTm9ybWFsaXplZCBFeHByZXNzaW9uIikgKwogIGdndGl0bGUoIk1BWCBtUk5BIEV4cHJlc3Npb24gKFNDVCkiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMSkpCgojIFBhbmVsIEI6IE1BWCBBY3Rpdml0eSAoZG9yb3RoZWEpCnZsbl9hY3QgPC0gVmxuUGxvdChzZXVyYXRfb2JqLCBmZWF0dXJlcyA9ICJNQVgiLCBhc3NheSA9ICJkb3JvdGhlYSIsIGdyb3VwLmJ5ID0gIkNvbmRpdGlvbiIsIHB0LnNpemUgPSAwLjA1KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTm9uLU1hbGlnbmFudCIgPSAiIzREQUY0QSIsICJNYWxpZ25hbnQiID0gIiNFNDFBMUMiKSkgKwogIHlsYWIoIlRGIEFjdGl2aXR5IFNjb3JlIikgKwogIGdndGl0bGUoIk1BWCBURiBBY3Rpdml0eSAoZG9yb3RoZWEpIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTEpKQoKIyBQYW5lbCBDOiBNWUMgQWN0aXZpdHkgKGRvcm90aGVhKSBmb3IgRGlyZWN0IENvbnRyYXN0CnZsbl9teWMgPC0gVmxuUGxvdChzZXVyYXRfb2JqLCBmZWF0dXJlcyA9ICJNWUMiLCBhc3NheSA9ICJkb3JvdGhlYSIsIGdyb3VwLmJ5ID0gIkNvbmRpdGlvbiIsIHB0LnNpemUgPSAwLjA1KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTm9uLU1hbGlnbmFudCIgPSAiIzREQUY0QSIsICJNYWxpZ25hbnQiID0gIiNFNDFBMUMiKSkgKwogIHlsYWIoIlRGIEFjdGl2aXR5IFNjb3JlIikgKwogIGdndGl0bGUoIk1ZQyBURiBBY3Rpdml0eSAoZG9yb3RoZWEpIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTEpKQoKZmlnX3Zsbl9tYXggPC0gdmxuX2V4cCB8IHZsbl9hY3QgfCB2bG5fbXljCnByaW50KGZpZ192bG5fbWF4KQoKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfTUFYX01ZQ19WaW9saW5fQ29tcGFyaXNvbi5wZGYiLCBwbG90ID0gZmlnX3Zsbl9tYXgsIHdpZHRoID0gMTIsIGhlaWdodCA9IDQpCmdnc2F2ZSgiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX01BWF9NWUNfVmlvbGluX0NvbXBhcmlzb24ucG5nIiwgcGxvdCA9IGZpZ192bG5fbWF4LCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA0LCBkcGkgPSAzMDApCmBgYAoKIyBDbHVzdGVyLUxldmVsIENvbXBsZXhIZWF0bWFwIChNQVggdnMuIE1ZQyBhbmQgQ29yZSBSZWd1bGF0b3JzKQpgYGB7ciBtYXgtY29tcGxleC1oZWF0bWFwLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMH0KIyBTZWxlY3QgdGFyZ2V0ZWQgcGFuZWwgY29udHJhc3RpbmcgTVlDL0UyRiBvbmNvZ2VuaWMgcHJvZ3JhbSB3aXRoIE1BWCBhbmQgdHVtb3Igc3VwcHJlc3NvcnMKdGZfcGFuZWwgPC0gYygKICAjIE9uY29nZW5pYyAvIFByb2xpZmVyYXRpdmUKICAiTVlDIiwgIkUyRjEiLCAiRTJGNCIsICJGT1hNMSIsICJTUkVCRjEiLCAiSElGMUEiLAogICMgTUFYCiAgIk1BWCIsCiAgIyBOb3JtYWwgSG9tZW9zdGFzaXMgJiBUdW1vciBTdXBwcmVzc29ycwogICJGT1hPMSIsICJGT1hPNCIsICJUQ0Y3IiwgIlRCWDIxIiwgIlJVTlgzIgopCgojIEV4dHJhY3QgVEYgQWN0aXZpdHkgbWF0cml4IChTZXVyYXQgdjQvdjUgY29tcGF0aWJsZSkKbWF0X3NjYWxlZCA8LSB0cnlDYXRjaCgKICBTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheSA9ICJkb3JvdGhlYSIsIGxheWVyID0gInNjYWxlLmRhdGEiKSwKICBlcnJvciA9IGZ1bmN0aW9uKGUpIE5VTEwKKQptYXRfZGF0YSA8LSBTZXVyYXRPYmplY3Q6OkdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheSA9ICJkb3JvdGhlYSIsIGxheWVyID0gImRhdGEiKQptYXRfdXNlIDwtIGlmICghaXMubnVsbChtYXRfc2NhbGVkKSAmJiBucm93KG1hdF9zY2FsZWQpID4gMCkgbWF0X3NjYWxlZCBlbHNlIG1hdF9kYXRhCgphdmFpbCA8LSBpbnRlcnNlY3QodGZfcGFuZWwsIHJvd25hbWVzKG1hdF91c2UpKQptYXRfdXNlIDwtIG1hdF91c2VbYXZhaWwsICwgZHJvcCA9IEZBTFNFXQoKIyBDb21wdXRlIG1lYW4gcGVyIGNsdXN0ZXIgYW5kIGNhbGN1bGF0ZSB6LXNjb3JlcwpjbHVzdGVycyA8LSBhcy5mYWN0b3Ioc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMpCmF2Z19tYXQgPC0gc2FwcGx5KGxldmVscyhjbHVzdGVycyksIGZ1bmN0aW9uKGNsKSB7CiAgTWF0cml4Ojpyb3dNZWFucyhtYXRfdXNlWywgY2x1c3RlcnMgPT0gY2wsIGRyb3AgPSBGQUxTRV0pCn0pCmNvbG5hbWVzKGF2Z19tYXQpIDwtIGxldmVscyhjbHVzdGVycykKCmF2Z19tYXRfeiA8LSB0KHNjYWxlKHQoYXZnX21hdCkpKQphdmdfbWF0X3pbaXMubmEoYXZnX21hdF96KV0gPC0gMAoKIyBTZXQgY29sdW1uIGFubm90YXRpb25zCmNsdXN0ZXJfc3RhdHVzIDwtIGlmZWxzZShjb2xuYW1lcyhhdmdfbWF0X3opICVpbiUgYygiMyIsICIxMCIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICJOb3JtYWwgQ0Q0IFQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICJNYWxpZ25hbnQgQ0Q0IFQgY2VsbHMiKQoKaGFfY29sIDwtIEhlYXRtYXBBbm5vdGF0aW9uKAogIENlbGxfU3RhdGUgPSBjbHVzdGVyX3N0YXR1cywKICBjb2wgPSBsaXN0KENlbGxfU3RhdGUgPSBjKCJOb3JtYWwgQ0Q0IFQiID0gIiM0REFGNEEiLCAiTWFsaWduYW50IENENCBUIGNlbGxzIiA9ICIjRTQxQTFDIikpLAogIGFubm90YXRpb25fbmFtZV9zaWRlID0gImxlZnQiCikKCiMgQ29sb3Igc2NhbGU6IC0zIHRvICszCmNvbF9mdW4gPC0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygtMywgMCwgMyksIGMoIiMzMTM2OTUiLCAid2hpdGUiLCAiI0E1MDAyNiIpKQoKaHRfbWF4IDwtIEhlYXRtYXAoCiAgYXZnX21hdF96LAogIG5hbWUgPSAiVEYgYWN0aXZpdHkgKHopIiwKICBjb2wgPSBjb2xfZnVuLAogIHRvcF9hbm5vdGF0aW9uID0gaGFfY29sLAogIGNvbHVtbl9zcGxpdCA9IGNsdXN0ZXJfc3RhdHVzLAogIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwKICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTEsIGZvbnRmYWNlID0gImJvbGQiKSwKICBjb2x1bW5fbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTApLAogIGNvbHVtbl90aXRsZSA9ICJDbHVzdGVyLUxldmVsIFRGIEFjdGl2aXR5OiBNWUMvRTJGIEFjdGl2YXRpb24gdnMuIE1BWCBSZXByZXNzaW9uIiwKICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QoZGlyZWN0aW9uID0gInZlcnRpY2FsIikKKQoKIyBTYXZlIGFuZCBEcmF3CnBkZigiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX01BWF9NWUNfQ2x1c3Rlcl9IZWF0bWFwLnBkZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDcpCmRyYXcoaHRfbWF4LCBtZXJnZV9sZWdlbmQgPSBUUlVFKQpkZXYub2ZmKCkKCnBuZygiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX01BWF9NWUNfQ2x1c3Rlcl9IZWF0bWFwLnBuZyIsIHdpZHRoID0gMTAgKiAzMDAsIGhlaWdodCA9IDcgKiAzMDAsIHJlcyA9IDMwMCkKZHJhdyhodF9tYXgsIG1lcmdlX2xlZ2VuZCA9IFRSVUUpCmRldi5vZmYoKQoKZHJhdyhodF9tYXgsIG1lcmdlX2xlZ2VuZCA9IFRSVUUpCmBgYAoKIyBWb2xjYW5vIFBsb3QgSGlnaGxpZ2h0aW5nIE1BWCAmIE1ZQyBJbmZlcnJlZCBBY3Rpdml0eQpgYGB7ciBtYXgtdm9sY2Fuby1wbG90LCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMCwgd2FybmluZz1GQUxTRX0KIyBEaWZmZXJlbnRpYWwgVEYgQWN0aXZpdHkgYWNyb3NzIGFsbCBURnMKRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKSWRlbnRzKHNldXJhdF9vYmopIDwtICJDb25kaXRpb24iCgphbGxfdGZzX2RpZmYgPC0gRmluZE1hcmtlcnMoCiAgc2V1cmF0X29iaiwgCiAgaWRlbnQuMSA9ICJNYWxpZ25hbnQiLCAKICBpZGVudC4yID0gIk5vbi1NYWxpZ25hbnQiLCAKICBsb2dmYy50aHJlc2hvbGQgPSAwLCAKICBtaW4ucGN0ID0gMAopCgojIEtleSBmYWN0b3JzIHRvIGhpZ2hsaWdodAp0ZnNfdG9fbGFiZWwgPC0gYygiTUFYIiwgIk1ZQyIsICJFMkYxIiwgIkUyRjQiLCAiRk9YTTEiLCAiRk9YTzEiLCAiUkVMQSIsICJJUkYxIiwgIlNUQVQzIiwgIlJVTlgzIikKCnBfdm9sY2Fub19tYXggPC0gRW5oYW5jZWRWb2xjYW5vKAogIGFsbF90ZnNfZGlmZiwKICBsYWIgPSByb3duYW1lcyhhbGxfdGZzX2RpZmYpLAogIHNlbGVjdExhYiA9IHRmc190b19sYWJlbCwKICB4ID0gJ2F2Z19sb2cyRkMnLAogIHkgPSAncF92YWxfYWRqJywKICB0aXRsZSA9ICdEaWZmZXJlbnRpYWwgVEYgQWN0aXZpdHk6IE1hbGlnbmFudCB2cy4gTm9uLU1hbGlnbmFudCBDRDQrIFQgQ2VsbHMnLAogIHN1YnRpdGxlID0gJ0RlY291cGxlUiBJbmZlcnJlZCBBY3Rpdml0eSBIaWdobGlnaHRpbmcgdGhlIE1BWC9NWUMgQXhpcycsCiAgcEN1dG9mZiA9IDFlLTUsCiAgRkNjdXRvZmYgPSAwLjUsCiAgcG9pbnRTaXplID0gMy4wLAogIGxhYlNpemUgPSA0LjUsCiAgZHJhd0Nvbm5lY3RvcnMgPSBUUlVFLAogIGNvbCA9IGMoImdyZXk2MCIsICJmb3Jlc3RncmVlbiIsICJyb3lhbGJsdWUiLCAiZmlyZWJyaWNrMiIpCikKCnByaW50KHBfdm9sY2Fub19tYXgpCmdnc2F2ZSgiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlX01BWF9Wb2xjYW5vX1RGX0FjdGl2aXR5LnBkZiIsIHBsb3QgPSBwX3ZvbGNhbm9fbWF4LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV9NQVhfVm9sY2Fub19URl9BY3Rpdml0eS5wbmciLCBwbG90ID0gcF92b2xjYW5vX21heCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCwgZHBpID0gMzAwKQpgYGAKCiMgQ29tcGxldGlvbiBTdW1tYXJ5CmBgYHtyIGNvbXBsZXRpb259CnByaW50KCJNQVggbmV0d29yayBhbmFseXNpcyBjb21wbGV0ZS4gQWxsIHRhYmxlcyBleHBvcnRlZCB0byBPdXRwdXRfVGFibGVzLyBhbmQgZmlndXJlcyB0byBPdXRwdXRfRmlndXJlcy8uIikKYGBg