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"
cat("✓ Seurat object loaded\n")
✓ Seurat object loaded
cat(sprintf("  - %d cells across %d clusters\n", 
            ncol(seurat_obj), 
            length(unique(seurat_obj$seurat_clusters))))
  - 49305 cells across 14 clusters

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_TFActivityPlot(sample = seurat_obj,
                                 activities = activities)
p1 <- out$heatmaps$average_scores
print(p1)

# 1. Save as PDF
pdf("Output_Figures/SCpubr_Heatmap_Default.pdf", width = 10, height = 8)
print(p1) # ComplexHeatmap requires explicit print() inside pdf()
dev.off()
png 
  2 
# 2. Save as PNG
png("Output_Figures/SCpubr_Heatmap_Default.png", width = 10 * 300, height = 8 * 300, res = 300)
print(p1)
dev.off()
png 
  2 

2.3 Set the scale limits


out <- SCpubr::do_TFActivityPlot(sample = seurat_obj,
                                 activities = activities,
                                 min.cutoff = -1.5,
                                 max.cutoff = 1.5)
p2 <- out$heatmaps$average_scores
print(p2)

# Save ComplexHeatmap properly
pdf("Output_Figures/SCpubr_Heatmap_Scaled.pdf", width = 10, height = 8)
print(p2)
dev.off()
png 
  2 
png("Output_Figures/SCpubr_Heatmap_Scaled.png", width = 10 * 300, height = 8 * 300, res = 300)
print(p2)
dev.off()
png 
  2 

2.4 Enforce Symmetry (Best for Manuscript)


out <- SCpubr::do_TFActivityPlot(sample = seurat_obj,
                                 activities = activities,
                                 min.cutoff = -1.5,
                                 max.cutoff = 1.5,
                                 enforce_symmetry = TRUE)
p3 <- out$heatmaps$average_scores
print(p3)

pdf("Output_Figures/SCpubr_Heatmap_Symmetric.pdf", width = 10, height = 8)
print(p3)
dev.off()
png 
  2 
png("Output_Figures/SCpubr_Heatmap_Symmetric.png", width = 10 * 300, height = 8 * 300, res = 300)
print(p3)
dev.off()
png 
  2 

2.5 Top 40 TFs

out <- SCpubr::do_TFActivityPlot(sample = seurat_obj,
                                 activities = activities,
                                 n_tfs = 40)
p4 <- out$heatmaps$average_scores
print(p4)

pdf("Output_Figures/SCpubr_Heatmap_Top40.pdf", width = 14, height = 6)
print(p4)
dev.off()
png 
  2 
png("Output_Figures/SCpubr_Heatmap_Top40.png", width = 14 * 300, height = 6 * 300, res = 300)
print(p4)
dev.off()
png 
  2 

2.6 FIGURE 3.16A: Global TF Activity Heatmap (Top 100 TFs)


out <- SCpubr::do_TFActivityPlot(sample = seurat_obj,
                                 activities = activities,
                                 n_tfs = 100)
p5 <- out$heatmaps$average_scores
print(p5)

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

3 Check TF Availability


# Get TF names from dorothea assay
DefaultAssay(seurat_obj) <- "dorothea"
tfs_in_activity <- rownames(seurat_obj)

# Get gene names from SCT assay
DefaultAssay(seurat_obj) <- "SCT"
genes_in_expression <- rownames(seurat_obj)

# Find TFs present in BOTH
tfs_in_both <- intersect(tfs_in_activity, genes_in_expression)

cat("\n═══════════════════════════════════════════════════════════\n")

═══════════════════════════════════════════════════════════
cat(sprintf("✓ %d TFs available in both assays\n", length(tfs_in_both)))
✓ 260 TFs available in both assays
cat("═══════════════════════════════════════════════════════════\n")
═══════════════════════════════════════════════════════════

4 FIGURE 3.16B: Expression-Activity Concordance (2-Row Grid)


# Select 6 TFs representing key heterogeneity axes
selected_tfs <- c(
  "FOXO1",   # Homeostasis
  "MYC",     # Oncogenic
  "TBX21",   # Th1/Stem
  "GATA3",   # Th2
  "RELA",    # Inflammatory
  "IRF1"     # Interferon
)

cat("\n✓ Selected TFs for Figure 3.16B:\n")

✓ Selected TFs for Figure 3.16B:
print(selected_tfs)
[1] "FOXO1" "MYC"   "TBX21" "GATA3" "RELA"  "IRF1" 
# ---- TOP ROW: TF ACTIVITY (dorothea assay) ----
DefaultAssay(seurat_obj) <- "dorothea"

act_plots <- lapply(selected_tfs, function(tf) {
  FeaturePlot(seurat_obj, 
              features = tf,
              reduction = "umap",
              order = TRUE,,label = T,
              cols = c("grey90", "red3"),
              pt.size = 0.3) +
    ggtitle(paste0(tf, " Activity")) +
    theme_minimal() +
    theme(plot.title = element_text(size = 11, face = "bold"),
          axis.title = element_text(size = 9),
          axis.text = element_text(size = 7),
          legend.position = "right",
          legend.text = element_text(size = 8),
          legend.title = element_text(size = 9))
})

cat("✓ TF Activity plots created (TOP ROW)\n")
✓ TF Activity plots created (TOP ROW)
# ---- BOTTOM ROW: Gene EXPRESSION (SCT assay) ----
DefaultAssay(seurat_obj) <- "SCT"

expr_plots <- lapply(selected_tfs, function(tf) {
  FeaturePlot(seurat_obj, 
              features = tf, 
              reduction = "umap",
              order = TRUE,label = T,
              cols = c("grey90", "darkblue"),
              pt.size = 0.3) +
    ggtitle(paste0(tf, " Expression")) +
    theme_minimal() +
    theme(plot.title = element_text(size = 11, face = "bold"),
          axis.title = element_text(size = 9),
          axis.text = element_text(size = 7),
          legend.position = "right",
          legend.text = element_text(size = 8),
          legend.title = element_text(size = 9))
})

cat("✓ Gene Expression plots created (BOTTOM ROW)\n")
✓ Gene Expression plots created (BOTTOM ROW)
# ---- COMBINE: 2 rows × 6 columns ----
top_row <- wrap_plots(act_plots, nrow = 1)
bottom_row <- wrap_plots(expr_plots, nrow = 1)

p_3.16B <- top_row / bottom_row +
           plot_annotation(
             title = "TF Activity and Expression Patterns",
             subtitle = "Top: Inferred TF Activity (DoRothEA) | Bottom: Gene Expression (SCT)",
             theme = theme(plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
                          plot.subtitle = element_text(size = 11, hjust = 0.5, color = "grey30"))
           )

ggsave("Output_Figures/Figure_3.16B_Activity_vs_Expression_Grid.pdf", 
       p_3.16B, 
       width = 22, 
       height = 8, 
       units = "in")

ggsave("Output_Figures/Figure_3.16B_Activity_vs_Expression_Grid.png", 
       p_3.16B, 
       width = 22, 
       height = 8, 
       units = "in", 
       dpi = 300)

print(p_3.16B)

cat("\n✓✓✓ FIGURE 3.16B SAVED (20×8 inches, 2 rows × 6 columns) ✓✓✓\n")

✓✓✓ FIGURE 3.16B SAVED (20×8 inches, 2 rows × 6 columns) ✓✓✓

5 FIGURE 3.16C: Literature-Validated TF Modules

6 SUPPLEMENTARY FIGURES

6.1 SUPPLEMENTARY FIGURE 1: Differential TF Activity Volcano Plot

library(EnhancedVolcano)
# 1. Perform Differential Analysis
non_malignant_clusters <- c(3, 10)
seurat_obj$Condition <- ifelse(seurat_obj$seurat_clusters %in% non_malignant_clusters, "Non-Malignant", "Malignant")
DefaultAssay(seurat_obj) <- "dorothea"
Idents(seurat_obj) <- "Condition"

cat("Running FindMarkers...\n")
Running FindMarkers...
diff_tfs <- FindMarkers(seurat_obj, ident.1="Malignant", ident.2="Non-Malignant", test.use="t", logfc.threshold=0.1, min.pct=0)

  |                                                  | 0 % ~calculating  
  |+                                                 | 2 % ~01s          
  |++                                                | 4 % ~01s          
  |+++                                               | 5 % ~01s          
  |++++                                              | 7 % ~01s          
  |+++++                                             | 9 % ~01s          
  |++++++                                            | 11% ~01s          
  |+++++++                                           | 12% ~01s          
  |++++++++                                          | 14% ~01s          
  |+++++++++                                         | 16% ~01s          
  |+++++++++                                         | 18% ~01s          
  |++++++++++                                        | 20% ~01s          
  |+++++++++++                                       | 21% ~01s          
  |++++++++++++                                      | 23% ~01s          
  |+++++++++++++                                     | 25% ~01s          
  |++++++++++++++                                    | 27% ~01s          
  |+++++++++++++++                                   | 29% ~01s          
  |++++++++++++++++                                  | 30% ~01s          
  |+++++++++++++++++                                 | 32% ~01s          
  |+++++++++++++++++                                 | 34% ~01s          
  |++++++++++++++++++                                | 36% ~00s          
  |+++++++++++++++++++                               | 38% ~00s          
  |++++++++++++++++++++                              | 39% ~00s          
  |+++++++++++++++++++++                             | 41% ~00s          
  |++++++++++++++++++++++                            | 43% ~00s          
  |+++++++++++++++++++++++                           | 45% ~00s          
  |++++++++++++++++++++++++                          | 46% ~00s          
  |+++++++++++++++++++++++++                         | 48% ~00s          
  |+++++++++++++++++++++++++                         | 50% ~00s          
  |++++++++++++++++++++++++++                        | 52% ~00s          
  |+++++++++++++++++++++++++++                       | 54% ~00s          
  |++++++++++++++++++++++++++++                      | 55% ~00s          
  |+++++++++++++++++++++++++++++                     | 57% ~00s          
  |++++++++++++++++++++++++++++++                    | 59% ~00s          
  |+++++++++++++++++++++++++++++++                   | 61% ~00s          
  |++++++++++++++++++++++++++++++++                  | 62% ~00s          
  |+++++++++++++++++++++++++++++++++                 | 64% ~00s          
  |++++++++++++++++++++++++++++++++++                | 66% ~00s          
  |++++++++++++++++++++++++++++++++++                | 68% ~00s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~00s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~00s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~00s          
  |++++++++++++++++++++++++++++++++++++++            | 75% ~00s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~00s          
  |++++++++++++++++++++++++++++++++++++++++          | 79% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++         | 80% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 82% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 84% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++      | 88% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 93% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++++ | 96% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 98% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=01s  
diff_tfs$gene <- rownames(diff_tfs)

# 2. Plot EnhancedVolcano
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', drawConnectors = TRUE, widthConnectors = 0.5,
    col = c("grey30", "forestgreen", "royalblue", "firebrick2"))

ggsave("Output_Figures/Supplementary_Differential_TF_Activity_Volcano.pdf", p_volcano, width=12, height=10)
print(p_volcano)

cat("✓ Supplementary Volcano Plot Saved\n")
✓ Supplementary Volcano Plot Saved

6.2 SUPPLEMENTARY FIGURE: CHECK TF AVAILABILITY

# ============================================================================
# CHECK TF AVAILABILITY
# ============================================================================

# 1. Define Desired Modules
modules <- list(
  "Homeostasis"          = c("FOXO1", "TCF7", "LEF1"),
  "Oncogenic/Cell Cycle" = c("MYC", "E2F1", "E2F4", "FOXM1"),
  "Inflammation (NF-kB)" = c("RELA", "NFKB1", "REL", "STAT3"),
  "Treg Signature"       = c("FOXP3", "STAT5B", "IKZF2"),
  "Th2 Differentiation"  = c("GATA3", "STAT6", "MAF"),
  "Th1 Differentiation"  = c("TBX21", "STAT1", "IRF1"),
  "Exhaustion"           = c("TOX", "PRDM1", "BATF")
)

# 2. Get available TFs in the object
DefaultAssay(seurat_obj) <- "dorothea"
available_tfs <- rownames(seurat_obj)

# 3. Check each module
cat("Checking TF availability in DoRothEA assay...\n")
Checking TF availability in DoRothEA assay...
cat("------------------------------------------------\n")
------------------------------------------------
final_modules <- list()

for (mod_name in names(modules)) {
  tfs <- modules[[mod_name]]
  
  # Find intersection
  present <- intersect(tfs, available_tfs)
  missing <- setdiff(tfs, available_tfs)
  
  # Report
  cat(sprintf("Module: %s\n", mod_name))
  cat(sprintf("  ✓ Found (%d): %s\n", length(present), paste(present, collapse=", ")))
  
  if(length(missing) > 0) {
    cat(sprintf("  ⚠ MISSING (%d): %s\n", length(missing), paste(missing, collapse=", ")))
  }
  
  # Only keep module if at least 1 TF exists
  if(length(present) > 0) {
    final_modules[[mod_name]] <- present
  }
  cat("\n")
}
Module: Homeostasis
  ✓ Found (3): FOXO1, TCF7, LEF1

Module: Oncogenic/Cell Cycle
  ✓ Found (4): MYC, E2F1, E2F4, FOXM1

Module: Inflammation (NF-kB)
  ✓ Found (4): RELA, NFKB1, REL, STAT3

Module: Treg Signature
  ✓ Found (1): STAT5B
  ⚠ MISSING (2): FOXP3, IKZF2

Module: Th2 Differentiation
  ✓ Found (3): GATA3, STAT6, MAF

Module: Th1 Differentiation
  ✓ Found (3): TBX21, STAT1, IRF1

Module: Exhaustion
  ✓ Found (2): PRDM1, BATF
  ⚠ MISSING (1): TOX
# 4. Update the 'modules' list to only use valid TFs
modules <- final_modules
cat("------------------------------------------------\n")
------------------------------------------------
cat("Ready to plot with validated TFs.\n")
Ready to plot with validated TFs.

6.3 SUPPLEMENTARY FIGURE 2: Variance Explained by Key Regulatory Modules

library(ggplot2)
library(dplyr)
library(Seurat)

# 1. Define Modules
modules <- list(
  "Homeostasis" = c("FOXO1", "TCF7", "LEF1"),
  "Oncogenic/Cell Cycle" = c("MYC", "E2F1", "E2F4", "FOXM1"),
  "Inflammation (NF-kB)" = c("RELA", "NFKB1", "REL", "STAT3"),
  "Th2 Differentiation" = c("GATA3", "STAT6", "MAF"),
  "Th1 Differentiation" = c("TBX21", "STAT1", "IRF1"),
  "Exhaustion" = c("TOX", "PRDM1", "BATF")
)

# 2. Calculate Variance using 'data' layer (NOT scale.data)
# IMPORTANT: scale.data sets variance to 1. We need 'data' for biological variance.
DefaultAssay(seurat_obj) <- "dorothea"
mat_raw <- GetAssayData(seurat_obj, assay = "dorothea", layer = "data")
all_vars <- apply(mat_raw, 1, var)

# 3. Calculate Mean Variance per Module
module_variance <- sapply(modules, function(tfs) {
  valid_tfs <- intersect(tfs, names(all_vars))
  if(length(valid_tfs) > 0) {
    mean(all_vars[valid_tfs], na.rm = TRUE)
  } else {
    0
  }
})

# 4. Create Plot Data
plot_data <- data.frame(
  Module = names(module_variance),
  Variance = module_variance
) %>% arrange(desc(Variance))

# 5. Plot
p_var <- ggplot(plot_data, aes(x = reorder(Module, Variance), y = Variance, fill = Module)) +
  geom_bar(stat = "identity", width = 0.7) +
  coord_flip() +
  scale_fill_brewer(palette = "Set2") +
  theme_minimal() +
  labs(title = "Drivers of Heterogeneity: Variance Explained by TF Modules",
       subtitle = "Calculated on unscaled TF activity scores (DoRothEA)",
       y = "Mean Variance", x = "") +
  theme(legend.position = "none", plot.title = element_text(face="bold"))

ggsave("Output_Figures/Supplementary_TF_Module_Variance.pdf", p_var, width=8, height=6)
print(p_var)

cat("✓ Supplementary Variance Plot Saved\n")
✓ Supplementary Variance Plot Saved

6.4 SUPPLEMENTARY FIGURE 3: Regulatory Module Activity Heatmap

library(ComplexHeatmap)
library(circlize)
library(Seurat)

# 1. Define Modules (Added Treg)
modules <- list(
  "Homeostasis" = c("FOXO1", "TCF7", "LEF1"),
  "Oncogenic/Cell Cycle" = c("MYC", "E2F1", "E2F4", "FOXM1"),
  "Inflammation (NF-kB)" = c("RELA", "NFKB1", "REL", "STAT3"),
  "Th2 Differentiation" = c("GATA3", "STAT6", "MAF"),
  "Th1 Differentiation" = c("TBX21", "STAT1", "IRF1"),
  "Exhaustion" = c("TOX", "PRDM1", "BATF")
)


# 2. Get Average Activity of ALL TFs per Cluster (using 'data' layer)
# We use AverageExpression to get the mean activity of every TF in every cluster
cluster_avg <- AverageExpression(seurat_obj, assays = "dorothea", layer = "data")$dorothea

# 3. Aggregate TFs into Module Scores
# For each module, we take the mean of its TFs' average activity
module_mat <- sapply(names(modules), function(mod_name) {
  tfs <- modules[[mod_name]]
  valid_tfs <- intersect(tfs, rownames(cluster_avg))
  
  if(length(valid_tfs) > 0) {
    colMeans(cluster_avg[valid_tfs, , drop=FALSE])
  } else {
    rep(0, ncol(cluster_avg))
  }
})

# Result is Clusters x Modules. Transpose to Modules x Clusters for heatmap
module_mat <- t(module_mat)

# 4. Scale (Z-score) row-wise for visualization
# This highlights relative enrichment (Red = High for that module, Blue = Low)
module_mat_z <- t(scale(t(module_mat)))
module_mat_z[is.na(module_mat_z)] <- 0

# 5. Plot Heatmap
col_fun <- circlize::colorRamp2(c(-2, 0, 2), c("#313695", "white", "#A50026"))

ht_mod <- Heatmap(module_mat_z,
                  name = "Activity (z)",
                  col = col_fun,
                  cluster_rows = TRUE, 
                  cluster_columns = TRUE,
                  rect_gp = gpar(col = "white", lwd = 1), # Add white borders
                  row_names_side = "left",
                  column_title = "Regulatory Module Activity Across Clusters",
                  row_names_gp = gpar(fontsize = 11, fontface = "bold"),
                  column_names_gp = gpar(fontsize = 10))

# Draw and Save
draw(ht_mod)

pdf("Output_Figures/Supplementary_TF_Module_Heatmap.pdf", width=8, height=6)
draw(ht_mod)
dev.off()
png 
  2 
png("Output_Figures/Supplementary_TF_Module_Heatmap.png", width=8*300, height=6*300, res=300)
draw(ht_mod)
dev.off()
png 
  2 

cat("✓ Supplementary Module Heatmap Saved\n")
✓ Supplementary Module Heatmap Saved

6.5 SUPPLEMENTARY FIGURE 4: Regulatory Module Activity by Cell Line

library(ComplexHeatmap)
library(circlize)
library(Seurat)

# 1. Define Modules
modules <- list(
  "Homeostasis" = c("FOXO1", "TCF7", "LEF1"),
  "Oncogenic/Cell Cycle" = c("MYC", "E2F1", "E2F4", "FOXM1"),
  "Inflammation (NF-kB)" = c("RELA", "NFKB1", "REL", "STAT3"),
  "Th2 Differentiation" = c("GATA3", "STAT6", "MAF"),
  "Th1 Differentiation" = c("TBX21", "STAT1", "IRF1"),
  "Exhaustion" = c("TOX", "PRDM1", "BATF")
)


# 2. Get Average Activity per Cell Line (orig.ident)
cell_line_avg <- AverageExpression(seurat_obj, 
                                   assays = "dorothea", 
                                   layer = "data", 
                                   group.by = "orig.ident")$dorothea

# 3. Aggregate TFs into Module Scores
module_mat <- sapply(names(modules), function(mod_name) {
  tfs <- modules[[mod_name]]
  valid_tfs <- intersect(tfs, rownames(cell_line_avg))
  
  if(length(valid_tfs) > 0) {
    colMeans(cell_line_avg[valid_tfs, , drop=FALSE])
  } else {
    rep(0, ncol(cell_line_avg))
  }
})

# Transpose to [Modules x Cell Lines]
module_mat <- t(module_mat)

# 4. Scale (Z-score) row-wise
module_mat_z <- t(scale(t(module_mat)))
module_mat_z[is.na(module_mat_z)] <- 0

# 5. Plot Heatmap
col_fun <- circlize::colorRamp2(c(-2, 0, 2), c("#313695", "white", "#A50026"))

ht_mod <- Heatmap(module_mat_z,
                  name = "Activity (z)",
                  col = col_fun,
                  cluster_rows = TRUE, 
                  cluster_columns = TRUE, # Group similar cell lines together
                  rect_gp = gpar(col = "white", lwd = 1), 
                  row_names_side = "left",
                  column_title = "Regulatory Landscape Across Cell Lines",
                  row_names_gp = gpar(fontsize = 11, fontface = "bold"),
                  column_names_gp = gpar(fontsize = 10))

# Draw and Save
draw(ht_mod)

pdf("Output_Figures/Supplementary_TF_Module_Heatmap_CellLine.pdf", width=8, height=6)
draw(ht_mod)
dev.off()
png 
  2 
png("Output_Figures/Supplementary_TF_Module_Heatmap_CellLine.png", width=8*300, height=6*300, res=300)
draw(ht_mod)
dev.off()
png 
  2 

cat("✓ Supplementary Cell Line Heatmap Saved\n")
✓ Supplementary Cell Line Heatmap Saved

6.6 SUPPLEMENTARY FIGURE 4: Regulatory Module Activity by Cell Line

library(ComplexHeatmap)
library(circlize)
library(Seurat)

# 1. Define Modules
modules <- list(
  "Homeostasis" = c("FOXO1", "TCF7", "LEF1"),
  "Oncogenic/Cell Cycle" = c("MYC", "E2F1", "E2F4", "FOXM1"),
  "Inflammation (NF-kB)" = c("RELA", "NFKB1", "REL", "STAT3"),
  "Th2 Differentiation" = c("GATA3", "STAT6", "MAF"),
  "Th1 Differentiation" = c("TBX21", "STAT1", "IRF1"),
  "Exhaustion" = c("TOX", "PRDM1", "BATF")
)


# 2. Get Average Activity per Cell Line (orig.ident)
cell_line_avg <- AverageExpression(seurat_obj, 
                                   assays = "dorothea", 
                                   layer = "data", 
                                   group.by = "seurat_clusters")$dorothea

# 3. Aggregate TFs into Module Scores
module_mat <- sapply(names(modules), function(mod_name) {
  tfs <- modules[[mod_name]]
  valid_tfs <- intersect(tfs, rownames(cell_line_avg))
  
  if(length(valid_tfs) > 0) {
    colMeans(cell_line_avg[valid_tfs, , drop=FALSE])
  } else {
    rep(0, ncol(cell_line_avg))
  }
})

# Transpose to [Modules x Cell Lines]
module_mat <- t(module_mat)

# 4. Scale (Z-score) row-wise
module_mat_z <- t(scale(t(module_mat)))
module_mat_z[is.na(module_mat_z)] <- 0

# 5. Plot Heatmap
col_fun <- circlize::colorRamp2(c(-2, 0, 2), c("#313695", "white", "#A50026"))

ht_mod <- Heatmap(module_mat_z,
                  name = "Activity (z)",
                  col = col_fun,
                  cluster_rows = TRUE, 
                  cluster_columns = TRUE, # Group similar cell lines together
                  rect_gp = gpar(col = "white", lwd = 1), 
                  row_names_side = "left",
                  column_title = "Regulatory Landscape Across Clusters",
                  row_names_gp = gpar(fontsize = 11, fontface = "bold"),
                  column_names_gp = gpar(fontsize = 10))

# Draw and Save
draw(ht_mod)

pdf("Output_Figures/Supplementary_TF_Module_Heatmap_CellLine.pdf", width=8, height=6)
draw(ht_mod)
dev.off()
png 
  2 
png("Output_Figures/Supplementary_TF_Module_Heatmap_CellLine.png", width=8*300, height=6*300, res=300)
draw(ht_mod)
dev.off()
png 
  2 

cat("✓ Supplementary Cell Line Heatmap Saved\n")
✓ Supplementary Cell Line Heatmap Saved

6.7 NEW FIGURE: Regulatory Trajectory Analysis (PCA on TF Activity)

library(Seurat)
library(ggplot2)
library(dplyr)

# 1. Run PCA specifically on TF Activity (DoRothEA)
DefaultAssay(seurat_obj) <- "dorothea"
seurat_obj <- ScaleData(seurat_obj)

  |                                                                                                                 
  |                                                                                                           |   0%
  |                                                                                                                 
  |===========================================================================================================| 100%
seurat_obj <- RunPCA(seurat_obj, features = rownames(seurat_obj), verbose = FALSE)

# 2. Extract PCA Embeddings
pca_data <- Embeddings(seurat_obj, reduction = "pca")[, 1:2] %>% 
  as.data.frame() %>% 
  mutate(Cluster = seurat_obj$seurat_clusters,
         Condition = ifelse(seurat_obj$seurat_clusters %in% c(3, 10), "Non-Malignant", "Malignant"))

# 3. Calculate Cluster Centroids (for arrows)
centroids <- pca_data %>% 
  group_by(Cluster) %>% 
  summarise(PC1 = mean(PC_1), PC2 = mean(PC_2))

# 4. Define Key Drivers for PC1 and PC2 (Loadings)
# This tells us WHAT drives the trajectory (e.g., PC1 = Malignancy, PC2 = Th1 vs Th2)
loadings <- Loadings(seurat_obj, reduction = "pca")
pc1_drivers <- names(sort(abs(loadings[, 1]), decreasing = T))[1:5]
pc2_drivers <- names(sort(abs(loadings[, 2]), decreasing = T))[1:5]

cat("PC1 Drivers (X-axis):", paste(pc1_drivers, collapse=", "), "\n")
PC1 Drivers (X-axis): MYC, TBX21, E2F4, ZNF263, E2F1 
cat("PC2 Drivers (Y-axis):", paste(pc2_drivers, collapse=", "), "\n")
PC2 Drivers (Y-axis): NFKB1, STAT1, RELA, IRF1, HNF4A 
# 5. Plot the Trajectory
p_traj <- ggplot(pca_data, aes(x = PC_1, y = PC_2, color = Cluster)) +
  # Points
  geom_point(alpha = 0.6, size = 1.5) +
  
  # Centroids and Labels
  geom_point(data = centroids, aes(x = PC1, y = PC2), size = 5, color = "black", shape = 21, fill = "white") +
  geom_text(data = centroids, aes(x = PC1, y = PC2, label = Cluster), color = "black", fontface = "bold") +
  
  # Styling
  scale_color_manual(values = Seurat::DiscretePalette(14)) +
  labs(title = "Regulatory Trajectory of Sézary Cells",
       subtitle = paste0("PC1 driven by: ", paste(pc1_drivers[1:3], collapse=", "), 
                         "\nPC2 driven by: ", paste(pc2_drivers[1:3], collapse=", ")),
       x = "PC1: Regulatory Axis 1", 
       y = "PC2: Regulatory Axis 2") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", size = 14),
        legend.position = "right")

# Save
ggsave("Output_Figures/Supplementary_TF_Trajectory_PCA.pdf", p_traj, width = 8, height = 7)
ggsave("Output_Figures/Supplementary_TF_Trajectory_PCA.png", p_traj, width = 8, height = 7, dpi = 300)

print(p_traj)

cat("✓ Regulatory Trajectory Plot Saved\n")
✓ Regulatory Trajectory Plot Saved

6.8 REFINED FIGURE: Regulatory Bi-plot (Trajectory + TF Drivers))

library(Seurat)
library(ggplot2)
library(dplyr)
library(ggrepel)

# 1. Run PCA on TF Activity
DefaultAssay(seurat_obj) <- "dorothea"
seurat_obj <- ScaleData(seurat_obj)

  |                                                                                                                 
  |                                                                                                           |   0%
  |                                                                                                                 
  |===========================================================================================================| 100%
seurat_obj <- RunPCA(seurat_obj, features = rownames(seurat_obj), verbose = FALSE)

# 2. Extract Cell Embeddings (Points)
pca_data <- Embeddings(seurat_obj, reduction = "pca")[, 1:2] %>% 
  as.data.frame() %>% 
  mutate(Cluster = as.factor(seurat_obj$seurat_clusters))

# 3. Extract Feature Loadings (Arrows)
loadings <- Loadings(seurat_obj, reduction = "pca")
# Select top 5 drivers for PC1 and PC2
top_pc1 <- names(sort(abs(loadings[, 1]), decreasing = TRUE))[1:5]
top_pc2 <- names(sort(abs(loadings[, 2]), decreasing = TRUE))[1:5]
top_drivers <- unique(c(top_pc1, top_pc2))

arrow_data <- as.data.frame(loadings[top_drivers, 1:2])
arrow_data$TF <- rownames(arrow_data)

# Scale arrows to match plot dimensions (scaling factor for visibility)
scale_factor <- max(abs(pca_data$PC_1)) / max(abs(arrow_data$PC_1)) * 0.8
arrow_data$PC_1 <- arrow_data$PC_1 * scale_factor
arrow_data$PC_2 <- arrow_data$PC_2 * scale_factor

# 4. Plot
p_biplot <- ggplot(pca_data, aes(x = PC_1, y = PC_2)) +
  # Cell Points
  geom_point(aes(color = Cluster), alpha = 0.5, size = 1.5) +
  
  # TF Arrows
  geom_segment(data = arrow_data, aes(x = 0, y = 0, xend = PC_1, yend = PC_2),
               arrow = arrow(length = unit(0.2, "cm")), color = "black", linewidth = 0.8) +
  
  # TF Labels
  geom_text_repel(data = arrow_data, aes(x = PC_1, y = PC_2, label = TF),
                  fontface = "bold", color = "black", size = 4, box.padding = 0.5) +
  
  # Styling
  scale_color_manual(values = Seurat::DiscretePalette(14)) +
  labs(title = "Regulatory Landscape Bi-plot",
       subtitle = "Arrows indicate direction of TF activity driving heterogeneity",
       x = "PC1: Malignancy Axis (Cell Cycle)", 
       y = "PC2: Identity Axis (Inflammation vs. Differentiation)") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", size = 14),
        legend.position = "right")

# Save
ggsave("Output_Figures/Supplementary_TF_Biplot.pdf", p_biplot, width = 10, height = 8)
ggsave("Output_Figures/Supplementary_TF_Biplot.png", p_biplot, width = 10, height = 8, dpi = 300)

print(p_biplot)

cat("✓ Bi-plot Saved. This visualizes exactly WHICH TFs pull clusters apart.\n")
✓ Bi-plot Saved. This visualizes exactly WHICH TFs pull clusters apart.

6.9 REFINED FIGURE: Regulatory Bi-plot (Cleaned)

library(Seurat)
library(ggplot2)
library(dplyr)
library(ggrepel)

# 1. Run PCA on TF Activity (if not already done)
DefaultAssay(seurat_obj) <- "dorothea"
seurat_obj <- ScaleData(seurat_obj)

  |                                                                                                                 
  |                                                                                                           |   0%
  |                                                                                                                 
  |===========================================================================================================| 100%
seurat_obj <- RunPCA(seurat_obj, features = rownames(seurat_obj), verbose = FALSE)

# 2. Extract Cell Embeddings
pca_data <- Embeddings(seurat_obj, reduction = "pca")[, 1:2] %>% 
  as.data.frame() %>% 
  mutate(Cluster = as.factor(seurat_obj$seurat_clusters))

# 3. Extract Feature Loadings (The "Arrows")
loadings <- Loadings(seurat_obj, reduction = "pca")

# --- FILTERING STEP ---
# Select only the top 3 positive and top 3 negative drivers for PC1 and PC2
pc1_top <- names(sort(loadings[, 1], decreasing = TRUE))[1:3]
pc1_bottom <- names(sort(loadings[, 1], decreasing = FALSE))[1:3]
pc2_top <- names(sort(loadings[, 2], decreasing = TRUE))[1:3]
pc2_bottom <- names(sort(loadings[, 2], decreasing = FALSE))[1:3]

top_drivers <- unique(c(pc1_top, pc1_bottom, pc2_top, pc2_bottom))
arrow_data <- as.data.frame(loadings[top_drivers, 1:2])
arrow_data$TF <- rownames(arrow_data)

# Scale arrows to be visible on the plot (multiply by factor)
scale_factor <- max(abs(pca_data$PC_1)) / max(abs(arrow_data$PC_1)) * 0.8
arrow_data$PC_1 <- arrow_data$PC_1 * scale_factor
arrow_data$PC_2 <- arrow_data$PC_2 * scale_factor

# 4. Plot
p_biplot <- ggplot(pca_data, aes(x = PC_1, y = PC_2)) +
  # Points (Cells)
  geom_point(aes(color = Cluster), alpha = 0.6, size = 1.5) +
  
  # Arrows (TFs)
  geom_segment(data = arrow_data, aes(x = 0, y = 0, xend = PC_1, yend = PC_2),
               arrow = arrow(length = unit(0.2, "cm")), color = "black", linewidth = 0.8) +
  
  # Labels (TFs) - using ggrepel to avoid overlap
  geom_text_repel(data = arrow_data, aes(x = PC_1, y = PC_2, label = TF),
                  fontface = "bold", color = "black", size = 4, 
                  box.padding = 0.5, point.padding = 0.2) +
  
  # Styling
  scale_color_manual(values = Seurat::DiscretePalette(14)) +
  labs(title = "Regulatory Drivers of Heterogeneity",
       subtitle = "Arrows indicate TFs driving separation along PC1 (Malignancy) and PC2 (Identity)",
       x = "PC1: Malignancy Axis", 
       y = "PC2: Identity Axis") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", size = 14),
        legend.position = "right")

# Save
ggsave("Output_Figures/Supplementary_TF_Biplot_Clean.pdf", p_biplot, width = 10, height = 8)
print(p_biplot)

cat("✓ Clean Bi-plot Saved. Shows only top drivers.\n")
✓ Clean Bi-plot Saved. Shows only top drivers.

7 SUPPLEMENTARY FIGURE: Slingshot Trajectory Analysis

# 1. Load Libraries
library(slingshot)
library(SingleCellExperiment)
library(RColorBrewer)
library(scales)

# 2. Convert Seurat to SingleCellExperiment
# We use the 'dorothea' assay for TF-based trajectory, or 'SCT' for gene-based.
# TF-based is better for "regulatory" trajectories.
sce <- as.SingleCellExperiment(seurat_obj, assay = "dorothea")

# 3. Run Slingshot
# We specify the reduction (UMAP) and the cluster labels
# 'start.clus' = 5 sets the root at the Stem-like cluster
sce <- slingshot(sce, clusterLabels = seurat_obj$seurat_clusters, 
                 reducedDim = 'UMAP', start.clus = '5')

# 4. Extract Trajectory Data for Plotting in ggplot2
# Get the curves (lineages)
slingshot_curves <- SlingshotDataSet(sce)
curve_coords <- data.frame()

for (i in seq_along(slingCurves(slingshot_curves))) {
  c <- slingCurves(slingshot_curves)[[i]]
  df <- as.data.frame(c$s[c$ord, ])
  df$Lineage <- paste0("Lineage", i)
  curve_coords <- rbind(curve_coords, df)
}

# Get cell embeddings
umap_data <- as.data.frame(Embeddings(seurat_obj, "umap"))
umap_data$Cluster <- seurat_obj$seurat_clusters

# 5. Plot
p_slingshot <- ggplot(umap_data, aes(x = umap_1, y = umap_2)) +
  # Cell points
  geom_point(aes(color = Cluster), size = 0.8, alpha = 0.6) +
  
  # Trajectory paths
  geom_path(data = curve_coords, aes(x = umap_1, y = umap_2, group = Lineage), 
            color = "black", linewidth = 1.2, arrow = arrow(type = "closed", length = unit(0.1, "inches"))) +
  
  # Styling
  scale_color_manual(values = Seurat::DiscretePalette(14)) +
  labs(title = "Regulatory Trajectory Inference (Slingshot)",
       subtitle = "Root set to Cluster 5 (Stem-like State)",
       x = "UMAP 1", y = "UMAP 2") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", size = 14))

# 6. Save
ggsave("Output_Figures/Supplementary_Slingshot_Trajectory.pdf", p_slingshot, width = 8, height = 7)
ggsave("Output_Figures/Supplementary_Slingshot_Trajectory.png", p_slingshot, width = 8, height = 7, dpi = 300)

print(p_slingshot)

cat("✓ Slingshot Trajectory Saved. Arrows indicate developmental flow from Cluster 5.\n")
✓ Slingshot Trajectory Saved. Arrows indicate developmental flow from Cluster 5.

8 SUPPLEMENTARY FIGURE: Monocle3 (If you prefer pseudotime coloring)

library(monocle3)
library(SeuratWrappers)
library(ggplot2)
library(dplyr)

# --- 1. Define Helper Function (Required for Monocle3) ---
get_earliest_principal_node <- function(cds, start_cells, reduction_method = "UMAP"){
  # Get the closest principal node to the geometric center of start_cells
  cell_ids <- colnames(cds)
  
  closest_vertex <- cds@principal_graph_aux[[reduction_method]]$pr_graph_cell_proj_closest_vertex
  closest_vertex <- as.matrix(closest_vertex[colnames(cds), ])
  
  root_pr_nodes <- igraph::V(principal_graph(cds)[[reduction_method]])$name[as.numeric(names(which.max(table(closest_vertex[start_cells,]))))]
  
  return(root_pr_nodes)
}

# --- 2. Convert Seurat to CellDataSet (CDS) ---
# We use the 'dorothea' assay for TF-based trajectory (or 'SCT' for gene-based)
cds <- as.cell_data_set(seurat_obj)

# --- 3. Preprocess & Learn Graph ---
# Monocle needs to re-cluster to learn the graph structure
cds <- cluster_cells(cds, reduction_method = "UMAP")
cds <- learn_graph(cds, use_partition = TRUE, verbose = FALSE)

  |                                                                                                                 
  |                                                                                                           |   0%
  |                                                                                                                 
  |===========================================================================================================| 100%

  |                                                                                                                 
  |                                                                                                           |   0%
  |                                                                                                                 
  |===========================================================================================================| 100%
# --- 4. Order Cells (Root = Cluster 5) ---
# Identify cells in Cluster 5 (Stem-like)
stem_cells <- colnames(seurat_obj)[seurat_obj$seurat_clusters == "5"]

# Find the root node
closest_node <- get_earliest_principal_node(cds, start_cells = stem_cells)

# Order cells based on pseudotime starting from that node
cds <- order_cells(cds, root_pr_nodes = closest_node)

# --- 5. Plot Pseudotime ---
p_monocle <- plot_cells(cds,
           color_cells_by = "pseudotime",
           label_cell_groups = FALSE,
           label_leaves = FALSE,
           label_branch_points = FALSE,
           graph_label_size = 1.5,
           trajectory_graph_color = "black",
           trajectory_graph_segment_size = 1.0) +
  labs(title = "Regulatory Trajectory (Pseudotime)",
       subtitle = "Root: Cluster 5 (Stem-like State)") +
  theme(plot.title = element_text(face = "bold", size = 14))

# Save
ggsave("Output_Figures/Supplementary_Monocle3_Pseudotime.pdf", p_monocle, width = 8, height = 7)
ggsave("Output_Figures/Supplementary_Monocle3_Pseudotime.png", p_monocle, width = 8, height = 7, dpi = 300)

print(p_monocle)

cat("✓ Monocle3 Trajectory Saved. Cells colored by pseudotime (Purple = Early, Yellow = Late).\n")
✓ Monocle3 Trajectory Saved. Cells colored by pseudotime (Purple = Early, Yellow = Late).

9 SUPPLEMENTARY FIGURE: R Code: PAGA-like Connectivity Graph (TF Activity)

# ============================================================================
# SUPPLEMENTARY FIGURE: PAGA-like Connectivity Graph (TF Activity)
# ============================================================================

library(Seurat)
library(ggraph) # Make sure ggraph is installed: install.packages("ggraph")
library(dplyr)

# 1. Build a k-Nearest Neighbor (KNN) Graph on TF Activity
# We use the 'dorothea' assay to ensure connections are based on REGULATION
DefaultAssay(seurat_obj) <- "dorothea"
seurat_obj <- FindNeighbors(seurat_obj, features = rownames(seurat_obj), k.param = 20, verbose = FALSE)

# 2. Construct the PAGA Graph (Cluster-to-Cluster Connectivity)
# We calculate the number of edges between cells of different clusters
# Access the graph directly using igraph functions (already available via Seurat)
adj_mat <- seurat_obj@graphs$dorothea_snn
clusters <- seurat_obj$seurat_clusters
n_clusters <- length(levels(clusters))

# Initialize connectivity matrix
connect_mat <- matrix(0, nrow = n_clusters, ncol = n_clusters)
rownames(connect_mat) <- levels(clusters)
colnames(connect_mat) <- levels(clusters)

# Fill matrix with edge weights (sum of shared NN links)
cluster_indices <- split(1:ncol(seurat_obj), clusters)

for (i in 1:n_clusters) {
  for (j in i:n_clusters) {
    if (i == j) next # Skip self-loops
    
    cells_i <- cluster_indices[[i]]
    cells_j <- cluster_indices[[j]]
    
    # Calculate total weight of connections between Cluster i and Cluster j
    sub_mat <- adj_mat[cells_i, cells_j]
    weight <- sum(sub_mat)
    
    # Normalize by cluster size to avoid bias towards large clusters
    norm_weight <- weight / (length(cells_i) * length(cells_j))
    
    connect_mat[i, j] <- norm_weight
    connect_mat[j, i] <- norm_weight
  }
}

# 3. Create Igraph Object
# Threshold edges to show only strong connections (top 20%)
threshold <- quantile(connect_mat[connect_mat > 0], 0.80) 
connect_mat[connect_mat < threshold] <- 0

# Use igraph::graph_from_adjacency_matrix explicitly
graph <- igraph::graph_from_adjacency_matrix(connect_mat, mode = "undirected", weighted = TRUE)

# Add Node Attributes (Size = Number of Cells)
igraph::V(graph)$size <- as.numeric(table(clusters))
igraph::V(graph)$label <- levels(clusters)

# 4. Plot PAGA Graph
p_paga <- ggraph(graph, layout = "fr") + # Fruchterman-Reingold layout
  # Edges (Thickness = Connectivity Strength)
  geom_edge_link(aes(width = weight), color = "grey50", alpha = 0.6) +
  scale_edge_width(range = c(0.5, 3)) +
  
  # Nodes (Clusters)
  geom_node_point(aes(size = size, color = label)) +
  scale_size(range = c(5, 15)) +
  
  # Labels
  geom_node_text(aes(label = label), fontface = "bold", color = "white") +
  
  # Styling
  # Use scales::hue_pal() for colors to avoid function errors
  scale_color_manual(values = scales::hue_pal()(n_clusters)) +
  labs(title = "Regulatory PAGA Graph (TF Connectivity)",
       subtitle = "Nodes = Clusters; Edges = Regulatory Similarity (DoRothEA)",
       caption = "Thick lines indicate strong regulatory transitions") +
  theme_void() +
  theme(legend.position = "none", plot.title = element_text(face="bold", size=14))

# Save
ggsave("Output_Figures/Supplementary_TF_PAGA_Graph.pdf", p_paga, width = 8, height = 8)
ggsave("Output_Figures/Supplementary_TF_PAGA_Graph.png", p_paga, width = 8, height = 8, dpi = 300)

print(p_paga)

cat("✓ PAGA Graph Saved. Look for connections from Cluster 5 (Stem) to 11/4/7.\n")
✓ PAGA Graph Saved. Look for connections from Cluster 5 (Stem) to 11/4/7.

10 Save


saveRDS(seurat_obj, "temp_seurat_obj.rds")
LS0tCnRpdGxlOiAiVEYgQWN0aXZpdHkgSW5mZXJlbmNlIEFuYWx5c2lzIChEZWNvdXBsZVIgKyBEb1JvdGhFQSkgVmlzdWFsaXphdGlvbiIKYXV0aG9yOiAiTmFzaXIgTWFobW9vZCBBYmJhc2kiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgIHRoZW1lOiBqb3VybmFsCi0tLQoKCiMgbG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9VFJVRX0KIyBEYXRhIFByb2Nlc3NpbmcKbGlicmFyeShkcGx5cikKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHN0cmluZ3IpCgojIFZpc3VhbGl6YXRpb24KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShTQ3B1YnIpCgojIFJlZ3VsYXRvcnkgTmV0d29yayBJbmZlcmVuY2UKbGlicmFyeShkZWNvdXBsZVIpCmxpYnJhcnkoZG9yb3RoZWEpCmRhdGEoZG9yb3RoZWFfaHMsIHBhY2thZ2UgPSAiZG9yb3RoZWEiKQpsaWJyYXJ5KHRpY3RvYykKCgpgYGAKCiMgTG9hZCBTZXVyYXQgT2JqZWN0IApgYGB7cn0KCiMgTG9hZCB5b3VyIFNldXJhdCBPYmplY3QKc2V1cmF0X29iaiA8LSByZWFkUkRTKCJPdXRwdXRfT2JqZWN0cy9TZXVyYXRfT2JqZWN0X1dpdGhfVEZfQWN0aXZpdHkucmRzIikKCklkZW50cyhzZXVyYXRfb2JqKSA8LSAic2V1cmF0X2NsdXN0ZXJzIgpjYXQoIuKckyBTZXVyYXQgb2JqZWN0IGxvYWRlZFxuIikKY2F0KHNwcmludGYoIiAgLSAlZCBjZWxscyBhY3Jvc3MgJWQgY2x1c3RlcnNcbiIsIAogICAgICAgICAgICBuY29sKHNldXJhdF9vYmopLCAKICAgICAgICAgICAgbGVuZ3RoKHVuaXF1ZShzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykpKSkKYGBgCgojIyBSdW4gdGhpcyBjb2RlIGJsb2NrIHRvIHJlc3RvcmUgYWN0aXZpdGllcyBpbnN0YW50bHk6CmBgYHtyfQoKIyBJZiAnYWN0aXZpdGllcycgaXMgbWlzc2luZyBidXQgJ2Rvcm90aGVhJyBhc3NheSBleGlzdHMsIHJlY29uc3RydWN0IGl0OgppZiAoIWV4aXN0cygiYWN0aXZpdGllcyIpICYmICJkb3JvdGhlYSIgJWluJSBuYW1lcyhzZXVyYXRfb2JqQGFzc2F5cykpIHsKICAKICBwcmludCgiUmVjb25zdHJ1Y3RpbmcgJ2FjdGl2aXRpZXMnIGRhdGFmcmFtZSBmcm9tIFNldXJhdCBvYmplY3QuLi4iKQogIAogICMgRXh0cmFjdCB0aGUgbWF0cml4IChTZXVyYXQgdjUgdXNlcyAnbGF5ZXInIGluc3RlYWQgb2YgJ3Nsb3QnKQogICMgU2luY2UgeW91IHJhbiBTY2FsZURhdGEsIHdlIHVzZSAnc2NhbGUuZGF0YScKICBtYXQgPC0gR2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAic2NhbGUuZGF0YSIpCiAgCiAgIyBDb252ZXJ0IHRvIGxvbmcgZm9ybWF0ICh3aGF0IFNDcHViciBuZWVkcykKICBhY3Rpdml0aWVzIDwtIGFzLmRhdGEuZnJhbWUobWF0KSAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbigic291cmNlIikgJT4lCiAgICBwaXZvdF9sb25nZXIoY29scyA9IC1zb3VyY2UsIG5hbWVzX3RvID0gImNvbmRpdGlvbiIsIHZhbHVlc190byA9ICJzY29yZSIpICU+JQogICAgbXV0YXRlKHN0YXRpc3RpYyA9ICJub3JtX3dtZWFuIikgIyBTQ3B1YnIgcmVxdWlyZXMgdGhpcyBjb2x1bW4KICAgIAogIHByaW50KCJBY3Rpdml0aWVzIGRhdGFmcmFtZSByZXN0b3JlZCEiKQp9CmBgYAoKIyMgU0NwdWJyIEhlYXRtYXAgVmlzdWFsaXphdGlvbi1IZWF0bWFwIG9mIGF2ZXJhZ2VkIHNjb3JlcwpgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CmxpYnJhcnkoU0NwdWJyKQojIEdlbmVyYWwgaGVhdG1hcCAoVG9wIFZhcmlhYmxlIFRGcykKb3V0IDwtIFNDcHVicjo6ZG9fVEZBY3Rpdml0eVBsb3Qoc2FtcGxlID0gc2V1cmF0X29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWN0aXZpdGllcyA9IGFjdGl2aXRpZXMpCnAxIDwtIG91dCRoZWF0bWFwcyRhdmVyYWdlX3Njb3JlcwpwcmludChwMSkKCiMgMS4gU2F2ZSBhcyBQREYKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9EZWZhdWx0LnBkZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCnByaW50KHAxKSAjIENvbXBsZXhIZWF0bWFwIHJlcXVpcmVzIGV4cGxpY2l0IHByaW50KCkgaW5zaWRlIHBkZigpCmRldi5vZmYoKQoKIyAyLiBTYXZlIGFzIFBORwpwbmcoIk91dHB1dF9GaWd1cmVzL1NDcHVicl9IZWF0bWFwX0RlZmF1bHQucG5nIiwgd2lkdGggPSAxMCAqIDMwMCwgaGVpZ2h0ID0gOCAqIDMwMCwgcmVzID0gMzAwKQpwcmludChwMSkKZGV2Lm9mZigpCmBgYAoKCiMjIFNldCB0aGUgc2NhbGUgbGltaXRzCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KCm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlQbG90KHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uY3V0b2ZmID0gLTEuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LmN1dG9mZiA9IDEuNSkKcDIgPC0gb3V0JGhlYXRtYXBzJGF2ZXJhZ2Vfc2NvcmVzCnByaW50KHAyKQoKIyBTYXZlIENvbXBsZXhIZWF0bWFwIHByb3Blcmx5CnBkZigiT3V0cHV0X0ZpZ3VyZXMvU0NwdWJyX0hlYXRtYXBfU2NhbGVkLnBkZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCnByaW50KHAyKQpkZXYub2ZmKCkKCnBuZygiT3V0cHV0X0ZpZ3VyZXMvU0NwdWJyX0hlYXRtYXBfU2NhbGVkLnBuZyIsIHdpZHRoID0gMTAgKiAzMDAsIGhlaWdodCA9IDggKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQocDIpCmRldi5vZmYoKQoKCmBgYAoKIyMgRW5mb3JjZSBTeW1tZXRyeSAoQmVzdCBmb3IgTWFudXNjcmlwdCkKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwfQoKb3V0IDwtIFNDcHVicjo6ZG9fVEZBY3Rpdml0eVBsb3Qoc2FtcGxlID0gc2V1cmF0X29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWN0aXZpdGllcyA9IGFjdGl2aXRpZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jdXRvZmYgPSAtMS41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXguY3V0b2ZmID0gMS41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmZvcmNlX3N5bW1ldHJ5ID0gVFJVRSkKcDMgPC0gb3V0JGhlYXRtYXBzJGF2ZXJhZ2Vfc2NvcmVzCnByaW50KHAzKQoKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9TeW1tZXRyaWMucGRmIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKcHJpbnQocDMpCmRldi5vZmYoKQoKcG5nKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9TeW1tZXRyaWMucG5nIiwgd2lkdGggPSAxMCAqIDMwMCwgaGVpZ2h0ID0gOCAqIDMwMCwgcmVzID0gMzAwKQpwcmludChwMykKZGV2Lm9mZigpCgpgYGAKCiMjIFRvcCA0MCBURnMKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTE0fQpvdXQgPC0gU0NwdWJyOjpkb19URkFjdGl2aXR5UGxvdChzYW1wbGUgPSBzZXVyYXRfb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY3Rpdml0aWVzID0gYWN0aXZpdGllcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl90ZnMgPSA0MCkKcDQgPC0gb3V0JGhlYXRtYXBzJGF2ZXJhZ2Vfc2NvcmVzCnByaW50KHA0KQoKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9Ub3A0MC5wZGYiLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSA2KQpwcmludChwNCkKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL1NDcHVicl9IZWF0bWFwX1RvcDQwLnBuZyIsIHdpZHRoID0gMTQgKiAzMDAsIGhlaWdodCA9IDYgKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQocDQpCmRldi5vZmYoKQpgYGAKCgojIyBGSUdVUkUgMy4xNkE6IEdsb2JhbCBURiBBY3Rpdml0eSBIZWF0bWFwIChUb3AgMTAwIFRGcykKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0zMn0KCm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlQbG90KHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3RmcyA9IDEwMCkKcDUgPC0gb3V0JGhlYXRtYXBzJGF2ZXJhZ2Vfc2NvcmVzCnByaW50KHA1KQoKcGRmKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfMy4xNkFfR2xvYmFsX1RGX0hlYXRtYXBfVG9wMTAwLnBkZiIsIHdpZHRoID0gMzIsIGhlaWdodCA9IDEyKQpwcmludChwNSkKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2QV9HbG9iYWxfVEZfSGVhdG1hcF9Ub3AxMDAucG5nIiwgd2lkdGggPSAzMiAqIDMwMCwgaGVpZ2h0ID0gMTIgKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQocDUpCmRldi5vZmYoKQpgYGAKCiMgQ2hlY2sgVEYgQXZhaWxhYmlsaXR5CmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MzJ9CgojIEdldCBURiBuYW1lcyBmcm9tIGRvcm90aGVhIGFzc2F5CkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiZG9yb3RoZWEiCnRmc19pbl9hY3Rpdml0eSA8LSByb3duYW1lcyhzZXVyYXRfb2JqKQoKIyBHZXQgZ2VuZSBuYW1lcyBmcm9tIFNDVCBhc3NheQpEZWZhdWx0QXNzYXkoc2V1cmF0X29iaikgPC0gIlNDVCIKZ2VuZXNfaW5fZXhwcmVzc2lvbiA8LSByb3duYW1lcyhzZXVyYXRfb2JqKQoKIyBGaW5kIFRGcyBwcmVzZW50IGluIEJPVEgKdGZzX2luX2JvdGggPC0gaW50ZXJzZWN0KHRmc19pbl9hY3Rpdml0eSwgZ2VuZXNfaW5fZXhwcmVzc2lvbikKCmNhdCgiXG7ilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZBcbiIpCmNhdChzcHJpbnRmKCLinJMgJWQgVEZzIGF2YWlsYWJsZSBpbiBib3RoIGFzc2F5c1xuIiwgbGVuZ3RoKHRmc19pbl9ib3RoKSkpCmNhdCgi4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQXG4iKQpgYGAKCgojIEZJR1VSRSAzLjE2QjogRXhwcmVzc2lvbi1BY3Rpdml0eSBDb25jb3JkYW5jZSAoMi1Sb3cgR3JpZCkKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTIyfQoKIyBTZWxlY3QgNiBURnMgcmVwcmVzZW50aW5nIGtleSBoZXRlcm9nZW5laXR5IGF4ZXMKc2VsZWN0ZWRfdGZzIDwtIGMoCiAgIkZPWE8xIiwgICAjIEhvbWVvc3Rhc2lzCiAgIk1ZQyIsICAgICAjIE9uY29nZW5pYwogICJUQlgyMSIsICAgIyBUaDEvU3RlbQogICJHQVRBMyIsICAgIyBUaDIKICAiUkVMQSIsICAgICMgSW5mbGFtbWF0b3J5CiAgIklSRjEiICAgICAjIEludGVyZmVyb24KKQoKY2F0KCJcbuKckyBTZWxlY3RlZCBURnMgZm9yIEZpZ3VyZSAzLjE2QjpcbiIpCnByaW50KHNlbGVjdGVkX3RmcykKCiMgLS0tLSBUT1AgUk9XOiBURiBBQ1RJVklUWSAoZG9yb3RoZWEgYXNzYXkpIC0tLS0KRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKCmFjdF9wbG90cyA8LSBsYXBwbHkoc2VsZWN0ZWRfdGZzLCBmdW5jdGlvbih0ZikgewogIEZlYXR1cmVQbG90KHNldXJhdF9vYmosIAogICAgICAgICAgICAgIGZlYXR1cmVzID0gdGYsCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgICAgICAgIG9yZGVyID0gVFJVRSwsbGFiZWwgPSBULAogICAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCAicmVkMyIpLAogICAgICAgICAgICAgIHB0LnNpemUgPSAwLjMpICsKICAgIGdndGl0bGUocGFzdGUwKHRmLCAiIEFjdGl2aXR5IikpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSkKfSkKCmNhdCgi4pyTIFRGIEFjdGl2aXR5IHBsb3RzIGNyZWF0ZWQgKFRPUCBST1cpXG4iKQoKIyAtLS0tIEJPVFRPTSBST1c6IEdlbmUgRVhQUkVTU0lPTiAoU0NUIGFzc2F5KSAtLS0tCkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiU0NUIgoKZXhwcl9wbG90cyA8LSBsYXBwbHkoc2VsZWN0ZWRfdGZzLCBmdW5jdGlvbih0ZikgewogIEZlYXR1cmVQbG90KHNldXJhdF9vYmosIAogICAgICAgICAgICAgIGZlYXR1cmVzID0gdGYsIAogICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwKICAgICAgICAgICAgICBvcmRlciA9IFRSVUUsbGFiZWwgPSBULAogICAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCAiZGFya2JsdWUiKSwKICAgICAgICAgICAgICBwdC5zaXplID0gMC4zKSArCiAgICBnZ3RpdGxlKHBhc3RlMCh0ZiwgIiBFeHByZXNzaW9uIikpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSkKfSkKCmNhdCgi4pyTIEdlbmUgRXhwcmVzc2lvbiBwbG90cyBjcmVhdGVkIChCT1RUT00gUk9XKVxuIikKCiMgLS0tLSBDT01CSU5FOiAyIHJvd3Mgw5cgNiBjb2x1bW5zIC0tLS0KdG9wX3JvdyA8LSB3cmFwX3Bsb3RzKGFjdF9wbG90cywgbnJvdyA9IDEpCmJvdHRvbV9yb3cgPC0gd3JhcF9wbG90cyhleHByX3Bsb3RzLCBucm93ID0gMSkKCnBfMy4xNkIgPC0gdG9wX3JvdyAvIGJvdHRvbV9yb3cgKwogICAgICAgICAgIHBsb3RfYW5ub3RhdGlvbigKICAgICAgICAgICAgIHRpdGxlID0gIlRGIEFjdGl2aXR5IGFuZCBFeHByZXNzaW9uIFBhdHRlcm5zIiwKICAgICAgICAgICAgIHN1YnRpdGxlID0gIlRvcDogSW5mZXJyZWQgVEYgQWN0aXZpdHkgKERvUm90aEVBKSB8IEJvdHRvbTogR2VuZSBFeHByZXNzaW9uIChTQ1QpIiwKICAgICAgICAgICAgIHRoZW1lID0gdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImdyZXkzMCIpKQogICAgICAgICAgICkKCmdnc2F2ZSgiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlXzMuMTZCX0FjdGl2aXR5X3ZzX0V4cHJlc3Npb25fR3JpZC5wZGYiLCAKICAgICAgIHBfMy4xNkIsIAogICAgICAgd2lkdGggPSAyMiwgCiAgICAgICBoZWlnaHQgPSA4LCAKICAgICAgIHVuaXRzID0gImluIikKCmdnc2F2ZSgiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlXzMuMTZCX0FjdGl2aXR5X3ZzX0V4cHJlc3Npb25fR3JpZC5wbmciLCAKICAgICAgIHBfMy4xNkIsIAogICAgICAgd2lkdGggPSAyMiwgCiAgICAgICBoZWlnaHQgPSA4LCAKICAgICAgIHVuaXRzID0gImluIiwgCiAgICAgICBkcGkgPSAzMDApCgpwcmludChwXzMuMTZCKQpjYXQoIlxu4pyT4pyT4pyTIEZJR1VSRSAzLjE2QiBTQVZFRCAoMjDDlzggaW5jaGVzLCAyIHJvd3Mgw5cgNiBjb2x1bW5zKSDinJPinJPinJNcbiIpCgpgYGAKCiMgRklHVVJFIDMuMTZDOiBMaXRlcmF0dXJlLVZhbGlkYXRlZCBURiBNb2R1bGVzCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkgICMgPC0tLSBFc3NlbnRpYWwgZm9yIGNvbG9yUmFtcDIKCiMgQ3VyYXRlZCBsaXN0IG9mIFPDqXphcnktcmVsZXZhbnQgVEZzCmxpdGVyYXR1cmVfdGZzIDwtIGMoIkdBVEEzIiwgIlRCWDIxIiwgIlJPUkMiLCAiRk9YUDMiLCAiVE9YIiwgIlNBVEIxIiwgIklLWkYyIiwKICAgICAgICAgICAgICAgICAgICAiU1RBVDMiLCAiU1RBVDVCIiwgIlNUQVQ2IiwgIlJFTEEiLCAiTkZLQjEiLCAiTVlDIiwgIkUyRjEiKQoKIyBDaGVjayBhdmFpbGFiaWxpdHkKYXZhaWxhYmxlX3RmcyA8LSBpbnRlcnNlY3QobGl0ZXJhdHVyZV90ZnMsIHJvd25hbWVzKHNldXJhdF9vYmpbWyJkb3JvdGhlYSJdXSkpCgojIEV4dHJhY3QgZGF0YQptYXRfc2NhbGVkIDwtIEdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheT0iZG9yb3RoZWEiLCBsYXllcj0ic2NhbGUuZGF0YSIpCm1hdF91c2UgPC0gbWF0X3NjYWxlZFthdmFpbGFibGVfdGZzLCAsIGRyb3A9RkFMU0VdCgojIENhbGN1bGF0ZSBhdmVyYWdlIGFjdGl2aXR5IHBlciBjbHVzdGVyCmNsdXN0ZXJzIDwtIGFzLmZhY3RvcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykKYXZnX21hdF96IDwtIHQoc2NhbGUodChzYXBwbHkobGV2ZWxzKGNsdXN0ZXJzKSwgZnVuY3Rpb24oY2wpIE1hdHJpeDo6cm93TWVhbnMobWF0X3VzZVssIGNsdXN0ZXJzID09IGNsLCBkcm9wPUZBTFNFXSkpKSkpCmF2Z19tYXRfeltpcy5uYShhdmdfbWF0X3opXSA8LSAwCgojIENyZWF0ZSBIZWF0bWFwCmh0XzMuMTZDIDwtIEhlYXRtYXAoYXZnX21hdF96LCAKICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlRGIGFjdGl2aXR5ICh6KSIsIAogICAgICAgICAgICAgICAgICAgIGNvbCA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTIsIDAsIDIpLCBjKCIjMzEzNjk1IiwgIndoaXRlIiwgIiNBNTAwMjYiKSksCiAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gIkxpdGVyYXR1cmUtdmFsaWRhdGVkIFPDqXphcnkgVEYgbW9kdWxlcyIpCgojIFNhdmUKcGRmKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfMy4xNkNfTGl0ZXJhdHVyZV9URl9IZWF0bWFwLnBkZiIsIHdpZHRoPTEwLCBoZWlnaHQ9NikKZHJhdyhodF8zLjE2QykKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2Q19MaXRlcmF0dXJlX1RGX0hlYXRtYXAucG5nIiwgd2lkdGg9MTAqMzAwLCBoZWlnaHQ9NiozMDAsIHJlcz0zMDApCmRyYXcoaHRfMy4xNkMpCmRldi5vZmYoKQoKY2F0KCLinJMgRmlndXJlIDMuMTZDIFNhdmVkXG4iKQoKaHRfMy4xNkMKCmBgYAoKIyBTVVBQTEVNRU5UQVJZIEZJR1VSRVMKIyMgU1VQUExFTUVOVEFSWSBGSUdVUkUgMTogRGlmZmVyZW50aWFsIFRGIEFjdGl2aXR5IFZvbGNhbm8gUGxvdApgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKIyAxLiBQZXJmb3JtIERpZmZlcmVudGlhbCBBbmFseXNpcwpub25fbWFsaWduYW50X2NsdXN0ZXJzIDwtIGMoMywgMTApCnNldXJhdF9vYmokQ29uZGl0aW9uIDwtIGlmZWxzZShzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycyAlaW4lIG5vbl9tYWxpZ25hbnRfY2x1c3RlcnMsICJOb24tTWFsaWduYW50IiwgIk1hbGlnbmFudCIpCkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiZG9yb3RoZWEiCklkZW50cyhzZXVyYXRfb2JqKSA8LSAiQ29uZGl0aW9uIgoKY2F0KCJSdW5uaW5nIEZpbmRNYXJrZXJzLi4uXG4iKQpkaWZmX3RmcyA8LSBGaW5kTWFya2VycyhzZXVyYXRfb2JqLCBpZGVudC4xPSJNYWxpZ25hbnQiLCBpZGVudC4yPSJOb24tTWFsaWduYW50IiwgdGVzdC51c2U9InQiLCBsb2dmYy50aHJlc2hvbGQ9MC4xLCBtaW4ucGN0PTApCmRpZmZfdGZzJGdlbmUgPC0gcm93bmFtZXMoZGlmZl90ZnMpCgojIDIuIFBsb3QgRW5oYW5jZWRWb2xjYW5vCnBfdm9sY2FubyA8LSBFbmhhbmNlZFZvbGNhbm8oZGlmZl90ZnMsCiAgICBsYWIgPSByb3duYW1lcyhkaWZmX3RmcyksIHggPSAnYXZnX2xvZzJGQycsIHkgPSAncF92YWxfYWRqJywKICAgIHRpdGxlID0gJ0RpZmZlcmVudGlhbCBURiBBY3Rpdml0eTogTWFsaWduYW50IHZzLiBOb24tTWFsaWduYW50JywKICAgIHN1YnRpdGxlID0gJ0RlY291cGxlUiBJbmZlcnJlZCBBY3Rpdml0eScsCiAgICBwQ3V0b2ZmID0gMWUtNSwgRkNjdXRvZmYgPSAwLjUsIHBvaW50U2l6ZSA9IDMuMCwgbGFiU2l6ZSA9IDUuMCwgY29sQWxwaGEgPSAwLjgsCiAgICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsIGRyYXdDb25uZWN0b3JzID0gVFJVRSwgd2lkdGhDb25uZWN0b3JzID0gMC41LAogICAgY29sID0gYygiZ3JleTMwIiwgImZvcmVzdGdyZWVuIiwgInJveWFsYmx1ZSIsICJmaXJlYnJpY2syIikpCgpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfRGlmZmVyZW50aWFsX1RGX0FjdGl2aXR5X1ZvbGNhbm8ucGRmIiwgcF92b2xjYW5vLCB3aWR0aD0xMiwgaGVpZ2h0PTEwKQpwcmludChwX3ZvbGNhbm8pCmNhdCgi4pyTIFN1cHBsZW1lbnRhcnkgVm9sY2FubyBQbG90IFNhdmVkXG4iKQpgYGAKIyMgU1VQUExFTUVOVEFSWSBGSUdVUkU6ICBDSEVDSyBURiBBVkFJTEFCSUxJVFkKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNn0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgQ0hFQ0sgVEYgQVZBSUxBQklMSVRZCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKIyAxLiBEZWZpbmUgRGVzaXJlZCBNb2R1bGVzCm1vZHVsZXMgPC0gbGlzdCgKICAiSG9tZW9zdGFzaXMiICAgICAgICAgID0gYygiRk9YTzEiLCAiVENGNyIsICJMRUYxIiksCiAgIk9uY29nZW5pYy9DZWxsIEN5Y2xlIiA9IGMoIk1ZQyIsICJFMkYxIiwgIkUyRjQiLCAiRk9YTTEiKSwKICAiSW5mbGFtbWF0aW9uIChORi1rQikiID0gYygiUkVMQSIsICJORktCMSIsICJSRUwiLCAiU1RBVDMiKSwKICAiVHJlZyBTaWduYXR1cmUiICAgICAgID0gYygiRk9YUDMiLCAiU1RBVDVCIiwgIklLWkYyIiksCiAgIlRoMiBEaWZmZXJlbnRpYXRpb24iICA9IGMoIkdBVEEzIiwgIlNUQVQ2IiwgIk1BRiIpLAogICJUaDEgRGlmZmVyZW50aWF0aW9uIiAgPSBjKCJUQlgyMSIsICJTVEFUMSIsICJJUkYxIiksCiAgIkV4aGF1c3Rpb24iICAgICAgICAgICA9IGMoIlRPWCIsICJQUkRNMSIsICJCQVRGIikKKQoKIyAyLiBHZXQgYXZhaWxhYmxlIFRGcyBpbiB0aGUgb2JqZWN0CkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiZG9yb3RoZWEiCmF2YWlsYWJsZV90ZnMgPC0gcm93bmFtZXMoc2V1cmF0X29iaikKCiMgMy4gQ2hlY2sgZWFjaCBtb2R1bGUKY2F0KCJDaGVja2luZyBURiBhdmFpbGFiaWxpdHkgaW4gRG9Sb3RoRUEgYXNzYXkuLi5cbiIpCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQoKZmluYWxfbW9kdWxlcyA8LSBsaXN0KCkKCmZvciAobW9kX25hbWUgaW4gbmFtZXMobW9kdWxlcykpIHsKICB0ZnMgPC0gbW9kdWxlc1tbbW9kX25hbWVdXQogIAogICMgRmluZCBpbnRlcnNlY3Rpb24KICBwcmVzZW50IDwtIGludGVyc2VjdCh0ZnMsIGF2YWlsYWJsZV90ZnMpCiAgbWlzc2luZyA8LSBzZXRkaWZmKHRmcywgYXZhaWxhYmxlX3RmcykKICAKICAjIFJlcG9ydAogIGNhdChzcHJpbnRmKCJNb2R1bGU6ICVzXG4iLCBtb2RfbmFtZSkpCiAgY2F0KHNwcmludGYoIiAg4pyTIEZvdW5kICglZCk6ICVzXG4iLCBsZW5ndGgocHJlc2VudCksIHBhc3RlKHByZXNlbnQsIGNvbGxhcHNlPSIsICIpKSkKICAKICBpZihsZW5ndGgobWlzc2luZykgPiAwKSB7CiAgICBjYXQoc3ByaW50ZigiICDimqAgTUlTU0lORyAoJWQpOiAlc1xuIiwgbGVuZ3RoKG1pc3NpbmcpLCBwYXN0ZShtaXNzaW5nLCBjb2xsYXBzZT0iLCAiKSkpCiAgfQogIAogICMgT25seSBrZWVwIG1vZHVsZSBpZiBhdCBsZWFzdCAxIFRGIGV4aXN0cwogIGlmKGxlbmd0aChwcmVzZW50KSA+IDApIHsKICAgIGZpbmFsX21vZHVsZXNbW21vZF9uYW1lXV0gPC0gcHJlc2VudAogIH0KICBjYXQoIlxuIikKfQoKIyA0LiBVcGRhdGUgdGhlICdtb2R1bGVzJyBsaXN0IHRvIG9ubHkgdXNlIHZhbGlkIFRGcwptb2R1bGVzIDwtIGZpbmFsX21vZHVsZXMKY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpCmNhdCgiUmVhZHkgdG8gcGxvdCB3aXRoIHZhbGlkYXRlZCBURnMuXG4iKQpgYGAKCiMjIFNVUFBMRU1FTlRBUlkgRklHVVJFIDI6ICBWYXJpYW5jZSBFeHBsYWluZWQgYnkgS2V5IFJlZ3VsYXRvcnkgTW9kdWxlcwpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoU2V1cmF0KQoKIyAxLiBEZWZpbmUgTW9kdWxlcwptb2R1bGVzIDwtIGxpc3QoCiAgIkhvbWVvc3Rhc2lzIiA9IGMoIkZPWE8xIiwgIlRDRjciLCAiTEVGMSIpLAogICJPbmNvZ2VuaWMvQ2VsbCBDeWNsZSIgPSBjKCJNWUMiLCAiRTJGMSIsICJFMkY0IiwgIkZPWE0xIiksCiAgIkluZmxhbW1hdGlvbiAoTkYta0IpIiA9IGMoIlJFTEEiLCAiTkZLQjEiLCAiUkVMIiwgIlNUQVQzIiksCiAgIlRoMiBEaWZmZXJlbnRpYXRpb24iID0gYygiR0FUQTMiLCAiU1RBVDYiLCAiTUFGIiksCiAgIlRoMSBEaWZmZXJlbnRpYXRpb24iID0gYygiVEJYMjEiLCAiU1RBVDEiLCAiSVJGMSIpLAogICJFeGhhdXN0aW9uIiA9IGMoIlRPWCIsICJQUkRNMSIsICJCQVRGIikKKQoKIyAyLiBDYWxjdWxhdGUgVmFyaWFuY2UgdXNpbmcgJ2RhdGEnIGxheWVyIChOT1Qgc2NhbGUuZGF0YSkKIyBJTVBPUlRBTlQ6IHNjYWxlLmRhdGEgc2V0cyB2YXJpYW5jZSB0byAxLiBXZSBuZWVkICdkYXRhJyBmb3IgYmlvbG9naWNhbCB2YXJpYW5jZS4KRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKbWF0X3JhdyA8LSBHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJkYXRhIikKYWxsX3ZhcnMgPC0gYXBwbHkobWF0X3JhdywgMSwgdmFyKQoKIyAzLiBDYWxjdWxhdGUgTWVhbiBWYXJpYW5jZSBwZXIgTW9kdWxlCm1vZHVsZV92YXJpYW5jZSA8LSBzYXBwbHkobW9kdWxlcywgZnVuY3Rpb24odGZzKSB7CiAgdmFsaWRfdGZzIDwtIGludGVyc2VjdCh0ZnMsIG5hbWVzKGFsbF92YXJzKSkKICBpZihsZW5ndGgodmFsaWRfdGZzKSA+IDApIHsKICAgIG1lYW4oYWxsX3ZhcnNbdmFsaWRfdGZzXSwgbmEucm0gPSBUUlVFKQogIH0gZWxzZSB7CiAgICAwCiAgfQp9KQoKIyA0LiBDcmVhdGUgUGxvdCBEYXRhCnBsb3RfZGF0YSA8LSBkYXRhLmZyYW1lKAogIE1vZHVsZSA9IG5hbWVzKG1vZHVsZV92YXJpYW5jZSksCiAgVmFyaWFuY2UgPSBtb2R1bGVfdmFyaWFuY2UKKSAlPiUgYXJyYW5nZShkZXNjKFZhcmlhbmNlKSkKCiMgNS4gUGxvdApwX3ZhciA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeCA9IHJlb3JkZXIoTW9kdWxlLCBWYXJpYW5jZSksIHkgPSBWYXJpYW5jZSwgZmlsbCA9IE1vZHVsZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjcpICsKICBjb29yZF9mbGlwKCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiRHJpdmVycyBvZiBIZXRlcm9nZW5laXR5OiBWYXJpYW5jZSBFeHBsYWluZWQgYnkgVEYgTW9kdWxlcyIsCiAgICAgICBzdWJ0aXRsZSA9ICJDYWxjdWxhdGVkIG9uIHVuc2NhbGVkIFRGIGFjdGl2aXR5IHNjb3JlcyAoRG9Sb3RoRUEpIiwKICAgICAgIHkgPSAiTWVhbiBWYXJpYW5jZSIsIHggPSAiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpCgpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfTW9kdWxlX1ZhcmlhbmNlLnBkZiIsIHBfdmFyLCB3aWR0aD04LCBoZWlnaHQ9NikKcHJpbnQocF92YXIpCmNhdCgi4pyTIFN1cHBsZW1lbnRhcnkgVmFyaWFuY2UgUGxvdCBTYXZlZFxuIikKYGBgCgojIyBTVVBQTEVNRU5UQVJZIEZJR1VSRSAzOiBSZWd1bGF0b3J5IE1vZHVsZSBBY3Rpdml0eSBIZWF0bWFwCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmxpYnJhcnkoU2V1cmF0KQoKIyAxLiBEZWZpbmUgTW9kdWxlcyAoQWRkZWQgVHJlZykKbW9kdWxlcyA8LSBsaXN0KAogICJIb21lb3N0YXNpcyIgPSBjKCJGT1hPMSIsICJUQ0Y3IiwgIkxFRjEiKSwKICAiT25jb2dlbmljL0NlbGwgQ3ljbGUiID0gYygiTVlDIiwgIkUyRjEiLCAiRTJGNCIsICJGT1hNMSIpLAogICJJbmZsYW1tYXRpb24gKE5GLWtCKSIgPSBjKCJSRUxBIiwgIk5GS0IxIiwgIlJFTCIsICJTVEFUMyIpLAogICJUaDIgRGlmZmVyZW50aWF0aW9uIiA9IGMoIkdBVEEzIiwgIlNUQVQ2IiwgIk1BRiIpLAogICJUaDEgRGlmZmVyZW50aWF0aW9uIiA9IGMoIlRCWDIxIiwgIlNUQVQxIiwgIklSRjEiKSwKICAiRXhoYXVzdGlvbiIgPSBjKCJUT1giLCAiUFJETTEiLCAiQkFURiIpCikKCgojIDIuIEdldCBBdmVyYWdlIEFjdGl2aXR5IG9mIEFMTCBURnMgcGVyIENsdXN0ZXIgKHVzaW5nICdkYXRhJyBsYXllcikKIyBXZSB1c2UgQXZlcmFnZUV4cHJlc3Npb24gdG8gZ2V0IHRoZSBtZWFuIGFjdGl2aXR5IG9mIGV2ZXJ5IFRGIGluIGV2ZXJ5IGNsdXN0ZXIKY2x1c3Rlcl9hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24oc2V1cmF0X29iaiwgYXNzYXlzID0gImRvcm90aGVhIiwgbGF5ZXIgPSAiZGF0YSIpJGRvcm90aGVhCgojIDMuIEFnZ3JlZ2F0ZSBURnMgaW50byBNb2R1bGUgU2NvcmVzCiMgRm9yIGVhY2ggbW9kdWxlLCB3ZSB0YWtlIHRoZSBtZWFuIG9mIGl0cyBURnMnIGF2ZXJhZ2UgYWN0aXZpdHkKbW9kdWxlX21hdCA8LSBzYXBwbHkobmFtZXMobW9kdWxlcyksIGZ1bmN0aW9uKG1vZF9uYW1lKSB7CiAgdGZzIDwtIG1vZHVsZXNbW21vZF9uYW1lXV0KICB2YWxpZF90ZnMgPC0gaW50ZXJzZWN0KHRmcywgcm93bmFtZXMoY2x1c3Rlcl9hdmcpKQogIAogIGlmKGxlbmd0aCh2YWxpZF90ZnMpID4gMCkgewogICAgY29sTWVhbnMoY2x1c3Rlcl9hdmdbdmFsaWRfdGZzLCAsIGRyb3A9RkFMU0VdKQogIH0gZWxzZSB7CiAgICByZXAoMCwgbmNvbChjbHVzdGVyX2F2ZykpCiAgfQp9KQoKIyBSZXN1bHQgaXMgQ2x1c3RlcnMgeCBNb2R1bGVzLiBUcmFuc3Bvc2UgdG8gTW9kdWxlcyB4IENsdXN0ZXJzIGZvciBoZWF0bWFwCm1vZHVsZV9tYXQgPC0gdChtb2R1bGVfbWF0KQoKIyA0LiBTY2FsZSAoWi1zY29yZSkgcm93LXdpc2UgZm9yIHZpc3VhbGl6YXRpb24KIyBUaGlzIGhpZ2hsaWdodHMgcmVsYXRpdmUgZW5yaWNobWVudCAoUmVkID0gSGlnaCBmb3IgdGhhdCBtb2R1bGUsIEJsdWUgPSBMb3cpCm1vZHVsZV9tYXRfeiA8LSB0KHNjYWxlKHQobW9kdWxlX21hdCkpKQptb2R1bGVfbWF0X3pbaXMubmEobW9kdWxlX21hdF96KV0gPC0gMAoKIyA1LiBQbG90IEhlYXRtYXAKY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC0yLCAwLCAyKSwgYygiIzMxMzY5NSIsICJ3aGl0ZSIsICIjQTUwMDI2IikpCgpodF9tb2QgPC0gSGVhdG1hcChtb2R1bGVfbWF0X3osCiAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQWN0aXZpdHkgKHopIiwKICAgICAgICAgICAgICAgICAgY29sID0gY29sX2Z1biwKICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgIHJlY3RfZ3AgPSBncGFyKGNvbCA9ICJ3aGl0ZSIsIGx3ZCA9IDEpLCAjIEFkZCB3aGl0ZSBib3JkZXJzCiAgICAgICAgICAgICAgICAgIHJvd19uYW1lc19zaWRlID0gImxlZnQiLAogICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiUmVndWxhdG9yeSBNb2R1bGUgQWN0aXZpdHkgQWNyb3NzIENsdXN0ZXJzIiwKICAgICAgICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDExLCBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCkpCgojIERyYXcgYW5kIFNhdmUKZHJhdyhodF9tb2QpCgpwZGYoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfTW9kdWxlX0hlYXRtYXAucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTYpCmRyYXcoaHRfbW9kKQpkZXYub2ZmKCkKCnBuZygiT3V0cHV0X0ZpZ3VyZXMvU3VwcGxlbWVudGFyeV9URl9Nb2R1bGVfSGVhdG1hcC5wbmciLCB3aWR0aD04KjMwMCwgaGVpZ2h0PTYqMzAwLCByZXM9MzAwKQpkcmF3KGh0X21vZCkKZGV2Lm9mZigpCgpjYXQoIuKckyBTdXBwbGVtZW50YXJ5IE1vZHVsZSBIZWF0bWFwIFNhdmVkXG4iKQpgYGAKCiMjIFNVUFBMRU1FTlRBUlkgRklHVVJFIDQ6IFJlZ3VsYXRvcnkgTW9kdWxlIEFjdGl2aXR5IGJ5IENlbGwgTGluZQpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KFNldXJhdCkKCiMgMS4gRGVmaW5lIE1vZHVsZXMKbW9kdWxlcyA8LSBsaXN0KAogICJIb21lb3N0YXNpcyIgPSBjKCJGT1hPMSIsICJUQ0Y3IiwgIkxFRjEiKSwKICAiT25jb2dlbmljL0NlbGwgQ3ljbGUiID0gYygiTVlDIiwgIkUyRjEiLCAiRTJGNCIsICJGT1hNMSIpLAogICJJbmZsYW1tYXRpb24gKE5GLWtCKSIgPSBjKCJSRUxBIiwgIk5GS0IxIiwgIlJFTCIsICJTVEFUMyIpLAogICJUaDIgRGlmZmVyZW50aWF0aW9uIiA9IGMoIkdBVEEzIiwgIlNUQVQ2IiwgIk1BRiIpLAogICJUaDEgRGlmZmVyZW50aWF0aW9uIiA9IGMoIlRCWDIxIiwgIlNUQVQxIiwgIklSRjEiKSwKICAiRXhoYXVzdGlvbiIgPSBjKCJUT1giLCAiUFJETTEiLCAiQkFURiIpCikKCgojIDIuIEdldCBBdmVyYWdlIEFjdGl2aXR5IHBlciBDZWxsIExpbmUgKG9yaWcuaWRlbnQpCmNlbGxfbGluZV9hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24oc2V1cmF0X29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXlzID0gImRvcm90aGVhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXIgPSAiZGF0YSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKSRkb3JvdGhlYQoKIyAzLiBBZ2dyZWdhdGUgVEZzIGludG8gTW9kdWxlIFNjb3Jlcwptb2R1bGVfbWF0IDwtIHNhcHBseShuYW1lcyhtb2R1bGVzKSwgZnVuY3Rpb24obW9kX25hbWUpIHsKICB0ZnMgPC0gbW9kdWxlc1tbbW9kX25hbWVdXQogIHZhbGlkX3RmcyA8LSBpbnRlcnNlY3QodGZzLCByb3duYW1lcyhjZWxsX2xpbmVfYXZnKSkKICAKICBpZihsZW5ndGgodmFsaWRfdGZzKSA+IDApIHsKICAgIGNvbE1lYW5zKGNlbGxfbGluZV9hdmdbdmFsaWRfdGZzLCAsIGRyb3A9RkFMU0VdKQogIH0gZWxzZSB7CiAgICByZXAoMCwgbmNvbChjZWxsX2xpbmVfYXZnKSkKICB9Cn0pCgojIFRyYW5zcG9zZSB0byBbTW9kdWxlcyB4IENlbGwgTGluZXNdCm1vZHVsZV9tYXQgPC0gdChtb2R1bGVfbWF0KQoKIyA0LiBTY2FsZSAoWi1zY29yZSkgcm93LXdpc2UKbW9kdWxlX21hdF96IDwtIHQoc2NhbGUodChtb2R1bGVfbWF0KSkpCm1vZHVsZV9tYXRfeltpcy5uYShtb2R1bGVfbWF0X3opXSA8LSAwCgojIDUuIFBsb3QgSGVhdG1hcApjb2xfZnVuIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTIsIDAsIDIpLCBjKCIjMzEzNjk1IiwgIndoaXRlIiwgIiNBNTAwMjYiKSkKCmh0X21vZCA8LSBIZWF0bWFwKG1vZHVsZV9tYXRfeiwKICAgICAgICAgICAgICAgICAgbmFtZSA9ICJBY3Rpdml0eSAoeikiLAogICAgICAgICAgICAgICAgICBjb2wgPSBjb2xfZnVuLAogICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwgIyBHcm91cCBzaW1pbGFyIGNlbGwgbGluZXMgdG9nZXRoZXIKICAgICAgICAgICAgICAgICAgcmVjdF9ncCA9IGdwYXIoY29sID0gIndoaXRlIiwgbHdkID0gMSksIAogICAgICAgICAgICAgICAgICByb3dfbmFtZXNfc2lkZSA9ICJsZWZ0IiwKICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gIlJlZ3VsYXRvcnkgTGFuZHNjYXBlIEFjcm9zcyBDZWxsIExpbmVzIiwKICAgICAgICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDExLCBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCkpCgojIERyYXcgYW5kIFNhdmUKZHJhdyhodF9tb2QpCgpwZGYoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfTW9kdWxlX0hlYXRtYXBfQ2VsbExpbmUucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTYpCmRyYXcoaHRfbW9kKQpkZXYub2ZmKCkKCnBuZygiT3V0cHV0X0ZpZ3VyZXMvU3VwcGxlbWVudGFyeV9URl9Nb2R1bGVfSGVhdG1hcF9DZWxsTGluZS5wbmciLCB3aWR0aD04KjMwMCwgaGVpZ2h0PTYqMzAwLCByZXM9MzAwKQpkcmF3KGh0X21vZCkKZGV2Lm9mZigpCgpjYXQoIuKckyBTdXBwbGVtZW50YXJ5IENlbGwgTGluZSBIZWF0bWFwIFNhdmVkXG4iKQpgYGAKCiMjIFNVUFBMRU1FTlRBUlkgRklHVVJFIDQ6IFJlZ3VsYXRvcnkgTW9kdWxlIEFjdGl2aXR5IGJ5IENlbGwgTGluZQpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KFNldXJhdCkKCiMgMS4gRGVmaW5lIE1vZHVsZXMKbW9kdWxlcyA8LSBsaXN0KAogICJIb21lb3N0YXNpcyIgPSBjKCJGT1hPMSIsICJUQ0Y3IiwgIkxFRjEiKSwKICAiT25jb2dlbmljL0NlbGwgQ3ljbGUiID0gYygiTVlDIiwgIkUyRjEiLCAiRTJGNCIsICJGT1hNMSIpLAogICJJbmZsYW1tYXRpb24gKE5GLWtCKSIgPSBjKCJSRUxBIiwgIk5GS0IxIiwgIlJFTCIsICJTVEFUMyIpLAogICJUaDIgRGlmZmVyZW50aWF0aW9uIiA9IGMoIkdBVEEzIiwgIlNUQVQ2IiwgIk1BRiIpLAogICJUaDEgRGlmZmVyZW50aWF0aW9uIiA9IGMoIlRCWDIxIiwgIlNUQVQxIiwgIklSRjEiKSwKICAiRXhoYXVzdGlvbiIgPSBjKCJUT1giLCAiUFJETTEiLCAiQkFURiIpCikKCgojIDIuIEdldCBBdmVyYWdlIEFjdGl2aXR5IHBlciBDZWxsIExpbmUgKG9yaWcuaWRlbnQpCmNlbGxfbGluZV9hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24oc2V1cmF0X29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXlzID0gImRvcm90aGVhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXIgPSAiZGF0YSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIpJGRvcm90aGVhCgojIDMuIEFnZ3JlZ2F0ZSBURnMgaW50byBNb2R1bGUgU2NvcmVzCm1vZHVsZV9tYXQgPC0gc2FwcGx5KG5hbWVzKG1vZHVsZXMpLCBmdW5jdGlvbihtb2RfbmFtZSkgewogIHRmcyA8LSBtb2R1bGVzW1ttb2RfbmFtZV1dCiAgdmFsaWRfdGZzIDwtIGludGVyc2VjdCh0ZnMsIHJvd25hbWVzKGNlbGxfbGluZV9hdmcpKQogIAogIGlmKGxlbmd0aCh2YWxpZF90ZnMpID4gMCkgewogICAgY29sTWVhbnMoY2VsbF9saW5lX2F2Z1t2YWxpZF90ZnMsICwgZHJvcD1GQUxTRV0pCiAgfSBlbHNlIHsKICAgIHJlcCgwLCBuY29sKGNlbGxfbGluZV9hdmcpKQogIH0KfSkKCiMgVHJhbnNwb3NlIHRvIFtNb2R1bGVzIHggQ2VsbCBMaW5lc10KbW9kdWxlX21hdCA8LSB0KG1vZHVsZV9tYXQpCgojIDQuIFNjYWxlIChaLXNjb3JlKSByb3ctd2lzZQptb2R1bGVfbWF0X3ogPC0gdChzY2FsZSh0KG1vZHVsZV9tYXQpKSkKbW9kdWxlX21hdF96W2lzLm5hKG1vZHVsZV9tYXRfeildIDwtIDAKCiMgNS4gUGxvdCBIZWF0bWFwCmNvbF9mdW4gPC0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygtMiwgMCwgMiksIGMoIiMzMTM2OTUiLCAid2hpdGUiLCAiI0E1MDAyNiIpKQoKaHRfbW9kIDwtIEhlYXRtYXAobW9kdWxlX21hdF96LAogICAgICAgICAgICAgICAgICBuYW1lID0gIkFjdGl2aXR5ICh6KSIsCiAgICAgICAgICAgICAgICAgIGNvbCA9IGNvbF9mdW4sCiAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLCAjIEdyb3VwIHNpbWlsYXIgY2VsbCBsaW5lcyB0b2dldGhlcgogICAgICAgICAgICAgICAgICByZWN0X2dwID0gZ3Bhcihjb2wgPSAid2hpdGUiLCBsd2QgPSAxKSwgCiAgICAgICAgICAgICAgICAgIHJvd19uYW1lc19zaWRlID0gImxlZnQiLAogICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiUmVndWxhdG9yeSBMYW5kc2NhcGUgQWNyb3NzIENsdXN0ZXJzIiwKICAgICAgICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDExLCBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCkpCgojIERyYXcgYW5kIFNhdmUKZHJhdyhodF9tb2QpCgpwZGYoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfTW9kdWxlX0hlYXRtYXBfQ2VsbExpbmUucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTYpCmRyYXcoaHRfbW9kKQpkZXYub2ZmKCkKCnBuZygiT3V0cHV0X0ZpZ3VyZXMvU3VwcGxlbWVudGFyeV9URl9Nb2R1bGVfSGVhdG1hcF9DZWxsTGluZS5wbmciLCB3aWR0aD04KjMwMCwgaGVpZ2h0PTYqMzAwLCByZXM9MzAwKQpkcmF3KGh0X21vZCkKZGV2Lm9mZigpCgpjYXQoIuKckyBTdXBwbGVtZW50YXJ5IENlbGwgTGluZSBIZWF0bWFwIFNhdmVkXG4iKQpgYGAKCgoKIyMgTkVXIEZJR1VSRTogUmVndWxhdG9yeSBUcmFqZWN0b3J5IEFuYWx5c2lzIChQQ0Egb24gVEYgQWN0aXZpdHkpCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCgojIDEuIFJ1biBQQ0Egc3BlY2lmaWNhbGx5IG9uIFRGIEFjdGl2aXR5IChEb1JvdGhFQSkKRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKc2V1cmF0X29iaiA8LSBTY2FsZURhdGEoc2V1cmF0X29iaikKc2V1cmF0X29iaiA8LSBSdW5QQ0Eoc2V1cmF0X29iaiwgZmVhdHVyZXMgPSByb3duYW1lcyhzZXVyYXRfb2JqKSwgdmVyYm9zZSA9IEZBTFNFKQoKIyAyLiBFeHRyYWN0IFBDQSBFbWJlZGRpbmdzCnBjYV9kYXRhIDwtIEVtYmVkZGluZ3Moc2V1cmF0X29iaiwgcmVkdWN0aW9uID0gInBjYSIpWywgMToyXSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JSAKICBtdXRhdGUoQ2x1c3RlciA9IHNldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzLAogICAgICAgICBDb25kaXRpb24gPSBpZmVsc2Uoc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMgJWluJSBjKDMsIDEwKSwgIk5vbi1NYWxpZ25hbnQiLCAiTWFsaWduYW50IikpCgojIDMuIENhbGN1bGF0ZSBDbHVzdGVyIENlbnRyb2lkcyAoZm9yIGFycm93cykKY2VudHJvaWRzIDwtIHBjYV9kYXRhICU+JSAKICBncm91cF9ieShDbHVzdGVyKSAlPiUgCiAgc3VtbWFyaXNlKFBDMSA9IG1lYW4oUENfMSksIFBDMiA9IG1lYW4oUENfMikpCgojIDQuIERlZmluZSBLZXkgRHJpdmVycyBmb3IgUEMxIGFuZCBQQzIgKExvYWRpbmdzKQojIFRoaXMgdGVsbHMgdXMgV0hBVCBkcml2ZXMgdGhlIHRyYWplY3RvcnkgKGUuZy4sIFBDMSA9IE1hbGlnbmFuY3ksIFBDMiA9IFRoMSB2cyBUaDIpCmxvYWRpbmdzIDwtIExvYWRpbmdzKHNldXJhdF9vYmosIHJlZHVjdGlvbiA9ICJwY2EiKQpwYzFfZHJpdmVycyA8LSBuYW1lcyhzb3J0KGFicyhsb2FkaW5nc1ssIDFdKSwgZGVjcmVhc2luZyA9IFQpKVsxOjVdCnBjMl9kcml2ZXJzIDwtIG5hbWVzKHNvcnQoYWJzKGxvYWRpbmdzWywgMl0pLCBkZWNyZWFzaW5nID0gVCkpWzE6NV0KCmNhdCgiUEMxIERyaXZlcnMgKFgtYXhpcyk6IiwgcGFzdGUocGMxX2RyaXZlcnMsIGNvbGxhcHNlPSIsICIpLCAiXG4iKQpjYXQoIlBDMiBEcml2ZXJzIChZLWF4aXMpOiIsIHBhc3RlKHBjMl9kcml2ZXJzLCBjb2xsYXBzZT0iLCAiKSwgIlxuIikKCiMgNS4gUGxvdCB0aGUgVHJhamVjdG9yeQpwX3RyYWogPC0gZ2dwbG90KHBjYV9kYXRhLCBhZXMoeCA9IFBDXzEsIHkgPSBQQ18yLCBjb2xvciA9IENsdXN0ZXIpKSArCiAgIyBQb2ludHMKICBnZW9tX3BvaW50KGFscGhhID0gMC42LCBzaXplID0gMS41KSArCiAgCiAgIyBDZW50cm9pZHMgYW5kIExhYmVscwogIGdlb21fcG9pbnQoZGF0YSA9IGNlbnRyb2lkcywgYWVzKHggPSBQQzEsIHkgPSBQQzIpLCBzaXplID0gNSwgY29sb3IgPSAiYmxhY2siLCBzaGFwZSA9IDIxLCBmaWxsID0gIndoaXRlIikgKwogIGdlb21fdGV4dChkYXRhID0gY2VudHJvaWRzLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgbGFiZWwgPSBDbHVzdGVyKSwgY29sb3IgPSAiYmxhY2siLCBmb250ZmFjZSA9ICJib2xkIikgKwogIAogICMgU3R5bGluZwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBTZXVyYXQ6OkRpc2NyZXRlUGFsZXR0ZSgxNCkpICsKICBsYWJzKHRpdGxlID0gIlJlZ3VsYXRvcnkgVHJhamVjdG9yeSBvZiBTw6l6YXJ5IENlbGxzIiwKICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKCJQQzEgZHJpdmVuIGJ5OiAiLCBwYXN0ZShwYzFfZHJpdmVyc1sxOjNdLCBjb2xsYXBzZT0iLCAiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiXG5QQzIgZHJpdmVuIGJ5OiAiLCBwYXN0ZShwYzJfZHJpdmVyc1sxOjNdLCBjb2xsYXBzZT0iLCAiKSksCiAgICAgICB4ID0gIlBDMTogUmVndWxhdG9yeSBBeGlzIDEiLCAKICAgICAgIHkgPSAiUEMyOiBSZWd1bGF0b3J5IEF4aXMgMiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQoKIyBTYXZlCmdnc2F2ZSgiT3V0cHV0X0ZpZ3VyZXMvU3VwcGxlbWVudGFyeV9URl9UcmFqZWN0b3J5X1BDQS5wZGYiLCBwX3RyYWosIHdpZHRoID0gOCwgaGVpZ2h0ID0gNykKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9TdXBwbGVtZW50YXJ5X1RGX1RyYWplY3RvcnlfUENBLnBuZyIsIHBfdHJhaiwgd2lkdGggPSA4LCBoZWlnaHQgPSA3LCBkcGkgPSAzMDApCgpwcmludChwX3RyYWopCmNhdCgi4pyTIFJlZ3VsYXRvcnkgVHJhamVjdG9yeSBQbG90IFNhdmVkXG4iKQpgYGAKCgoKCgoKCgoKCgojIyBSRUZJTkVEIEZJR1VSRTogUmVndWxhdG9yeSBCaS1wbG90IChUcmFqZWN0b3J5ICsgVEYgRHJpdmVycykpCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dyZXBlbCkKCiMgMS4gUnVuIFBDQSBvbiBURiBBY3Rpdml0eQpEZWZhdWx0QXNzYXkoc2V1cmF0X29iaikgPC0gImRvcm90aGVhIgpzZXVyYXRfb2JqIDwtIFNjYWxlRGF0YShzZXVyYXRfb2JqKQpzZXVyYXRfb2JqIDwtIFJ1blBDQShzZXVyYXRfb2JqLCBmZWF0dXJlcyA9IHJvd25hbWVzKHNldXJhdF9vYmopLCB2ZXJib3NlID0gRkFMU0UpCgojIDIuIEV4dHJhY3QgQ2VsbCBFbWJlZGRpbmdzIChQb2ludHMpCnBjYV9kYXRhIDwtIEVtYmVkZGluZ3Moc2V1cmF0X29iaiwgcmVkdWN0aW9uID0gInBjYSIpWywgMToyXSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JSAKICBtdXRhdGUoQ2x1c3RlciA9IGFzLmZhY3RvcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykpCgojIDMuIEV4dHJhY3QgRmVhdHVyZSBMb2FkaW5ncyAoQXJyb3dzKQpsb2FkaW5ncyA8LSBMb2FkaW5ncyhzZXVyYXRfb2JqLCByZWR1Y3Rpb24gPSAicGNhIikKIyBTZWxlY3QgdG9wIDUgZHJpdmVycyBmb3IgUEMxIGFuZCBQQzIKdG9wX3BjMSA8LSBuYW1lcyhzb3J0KGFicyhsb2FkaW5nc1ssIDFdKSwgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjVdCnRvcF9wYzIgPC0gbmFtZXMoc29ydChhYnMobG9hZGluZ3NbLCAyXSksIGRlY3JlYXNpbmcgPSBUUlVFKSlbMTo1XQp0b3BfZHJpdmVycyA8LSB1bmlxdWUoYyh0b3BfcGMxLCB0b3BfcGMyKSkKCmFycm93X2RhdGEgPC0gYXMuZGF0YS5mcmFtZShsb2FkaW5nc1t0b3BfZHJpdmVycywgMToyXSkKYXJyb3dfZGF0YSRURiA8LSByb3duYW1lcyhhcnJvd19kYXRhKQoKIyBTY2FsZSBhcnJvd3MgdG8gbWF0Y2ggcGxvdCBkaW1lbnNpb25zIChzY2FsaW5nIGZhY3RvciBmb3IgdmlzaWJpbGl0eSkKc2NhbGVfZmFjdG9yIDwtIG1heChhYnMocGNhX2RhdGEkUENfMSkpIC8gbWF4KGFicyhhcnJvd19kYXRhJFBDXzEpKSAqIDAuOAphcnJvd19kYXRhJFBDXzEgPC0gYXJyb3dfZGF0YSRQQ18xICogc2NhbGVfZmFjdG9yCmFycm93X2RhdGEkUENfMiA8LSBhcnJvd19kYXRhJFBDXzIgKiBzY2FsZV9mYWN0b3IKCiMgNC4gUGxvdApwX2JpcGxvdCA8LSBnZ3Bsb3QocGNhX2RhdGEsIGFlcyh4ID0gUENfMSwgeSA9IFBDXzIpKSArCiAgIyBDZWxsIFBvaW50cwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gQ2x1c3RlciksIGFscGhhID0gMC41LCBzaXplID0gMS41KSArCiAgCiAgIyBURiBBcnJvd3MKICBnZW9tX3NlZ21lbnQoZGF0YSA9IGFycm93X2RhdGEsIGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBQQ18xLCB5ZW5kID0gUENfMiksCiAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJjbSIpKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjgpICsKICAKICAjIFRGIExhYmVscwogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gYXJyb3dfZGF0YSwgYWVzKHggPSBQQ18xLCB5ID0gUENfMiwgbGFiZWwgPSBURiksCiAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0LCBib3gucGFkZGluZyA9IDAuNSkgKwogIAogICMgU3R5bGluZwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBTZXVyYXQ6OkRpc2NyZXRlUGFsZXR0ZSgxNCkpICsKICBsYWJzKHRpdGxlID0gIlJlZ3VsYXRvcnkgTGFuZHNjYXBlIEJpLXBsb3QiLAogICAgICAgc3VidGl0bGUgPSAiQXJyb3dzIGluZGljYXRlIGRpcmVjdGlvbiBvZiBURiBhY3Rpdml0eSBkcml2aW5nIGhldGVyb2dlbmVpdHkiLAogICAgICAgeCA9ICJQQzE6IE1hbGlnbmFuY3kgQXhpcyAoQ2VsbCBDeWNsZSkiLCAKICAgICAgIHkgPSAiUEMyOiBJZGVudGl0eSBBeGlzIChJbmZsYW1tYXRpb24gdnMuIERpZmZlcmVudGlhdGlvbikiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKCiMgU2F2ZQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfQmlwbG90LnBkZiIsIHBfYmlwbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfQmlwbG90LnBuZyIsIHBfYmlwbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApCgpwcmludChwX2JpcGxvdCkKY2F0KCLinJMgQmktcGxvdCBTYXZlZC4gVGhpcyB2aXN1YWxpemVzIGV4YWN0bHkgV0hJQ0ggVEZzIHB1bGwgY2x1c3RlcnMgYXBhcnQuXG4iKQpgYGAKCiMjIFJFRklORUQgRklHVVJFOiBSZWd1bGF0b3J5IEJpLXBsb3QgKENsZWFuZWQpCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dyZXBlbCkKCiMgMS4gUnVuIFBDQSBvbiBURiBBY3Rpdml0eSAoaWYgbm90IGFscmVhZHkgZG9uZSkKRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKc2V1cmF0X29iaiA8LSBTY2FsZURhdGEoc2V1cmF0X29iaikKc2V1cmF0X29iaiA8LSBSdW5QQ0Eoc2V1cmF0X29iaiwgZmVhdHVyZXMgPSByb3duYW1lcyhzZXVyYXRfb2JqKSwgdmVyYm9zZSA9IEZBTFNFKQoKIyAyLiBFeHRyYWN0IENlbGwgRW1iZWRkaW5ncwpwY2FfZGF0YSA8LSBFbWJlZGRpbmdzKHNldXJhdF9vYmosIHJlZHVjdGlvbiA9ICJwY2EiKVssIDE6Ml0gJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgbXV0YXRlKENsdXN0ZXIgPSBhcy5mYWN0b3Ioc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMpKQoKIyAzLiBFeHRyYWN0IEZlYXR1cmUgTG9hZGluZ3MgKFRoZSAiQXJyb3dzIikKbG9hZGluZ3MgPC0gTG9hZGluZ3Moc2V1cmF0X29iaiwgcmVkdWN0aW9uID0gInBjYSIpCgojIC0tLSBGSUxURVJJTkcgU1RFUCAtLS0KIyBTZWxlY3Qgb25seSB0aGUgdG9wIDMgcG9zaXRpdmUgYW5kIHRvcCAzIG5lZ2F0aXZlIGRyaXZlcnMgZm9yIFBDMSBhbmQgUEMyCnBjMV90b3AgPC0gbmFtZXMoc29ydChsb2FkaW5nc1ssIDFdLCBkZWNyZWFzaW5nID0gVFJVRSkpWzE6M10KcGMxX2JvdHRvbSA8LSBuYW1lcyhzb3J0KGxvYWRpbmdzWywgMV0sIGRlY3JlYXNpbmcgPSBGQUxTRSkpWzE6M10KcGMyX3RvcCA8LSBuYW1lcyhzb3J0KGxvYWRpbmdzWywgMl0sIGRlY3JlYXNpbmcgPSBUUlVFKSlbMTozXQpwYzJfYm90dG9tIDwtIG5hbWVzKHNvcnQobG9hZGluZ3NbLCAyXSwgZGVjcmVhc2luZyA9IEZBTFNFKSlbMTozXQoKdG9wX2RyaXZlcnMgPC0gdW5pcXVlKGMocGMxX3RvcCwgcGMxX2JvdHRvbSwgcGMyX3RvcCwgcGMyX2JvdHRvbSkpCmFycm93X2RhdGEgPC0gYXMuZGF0YS5mcmFtZShsb2FkaW5nc1t0b3BfZHJpdmVycywgMToyXSkKYXJyb3dfZGF0YSRURiA8LSByb3duYW1lcyhhcnJvd19kYXRhKQoKIyBTY2FsZSBhcnJvd3MgdG8gYmUgdmlzaWJsZSBvbiB0aGUgcGxvdCAobXVsdGlwbHkgYnkgZmFjdG9yKQpzY2FsZV9mYWN0b3IgPC0gbWF4KGFicyhwY2FfZGF0YSRQQ18xKSkgLyBtYXgoYWJzKGFycm93X2RhdGEkUENfMSkpICogMC44CmFycm93X2RhdGEkUENfMSA8LSBhcnJvd19kYXRhJFBDXzEgKiBzY2FsZV9mYWN0b3IKYXJyb3dfZGF0YSRQQ18yIDwtIGFycm93X2RhdGEkUENfMiAqIHNjYWxlX2ZhY3RvcgoKIyA0LiBQbG90CnBfYmlwbG90IDwtIGdncGxvdChwY2FfZGF0YSwgYWVzKHggPSBQQ18xLCB5ID0gUENfMikpICsKICAjIFBvaW50cyAoQ2VsbHMpCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBDbHVzdGVyKSwgYWxwaGEgPSAwLjYsIHNpemUgPSAxLjUpICsKICAKICAjIEFycm93cyAoVEZzKQogIGdlb21fc2VnbWVudChkYXRhID0gYXJyb3dfZGF0YSwgYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IFBDXzEsIHllbmQgPSBQQ18yKSwKICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImNtIikpLCBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDAuOCkgKwogIAogICMgTGFiZWxzIChURnMpIC0gdXNpbmcgZ2dyZXBlbCB0byBhdm9pZCBvdmVybGFwCiAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBhcnJvd19kYXRhLCBhZXMoeCA9IFBDXzEsIHkgPSBQQ18yLCBsYWJlbCA9IFRGKSwKICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDQsIAogICAgICAgICAgICAgICAgICBib3gucGFkZGluZyA9IDAuNSwgcG9pbnQucGFkZGluZyA9IDAuMikgKwogIAogICMgU3R5bGluZwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBTZXVyYXQ6OkRpc2NyZXRlUGFsZXR0ZSgxNCkpICsKICBsYWJzKHRpdGxlID0gIlJlZ3VsYXRvcnkgRHJpdmVycyBvZiBIZXRlcm9nZW5laXR5IiwKICAgICAgIHN1YnRpdGxlID0gIkFycm93cyBpbmRpY2F0ZSBURnMgZHJpdmluZyBzZXBhcmF0aW9uIGFsb25nIFBDMSAoTWFsaWduYW5jeSkgYW5kIFBDMiAoSWRlbnRpdHkpIiwKICAgICAgIHggPSAiUEMxOiBNYWxpZ25hbmN5IEF4aXMiLCAKICAgICAgIHkgPSAiUEMyOiBJZGVudGl0eSBBeGlzIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCgojIFNhdmUKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9TdXBwbGVtZW50YXJ5X1RGX0JpcGxvdF9DbGVhbi5wZGYiLCBwX2JpcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKcHJpbnQocF9iaXBsb3QpCmNhdCgi4pyTIENsZWFuIEJpLXBsb3QgU2F2ZWQuIFNob3dzIG9ubHkgdG9wIGRyaXZlcnMuXG4iKQpgYGAKCiMgU1VQUExFTUVOVEFSWSBGSUdVUkU6IFNsaW5nc2hvdCBUcmFqZWN0b3J5IEFuYWx5c2lzCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CiMgMS4gTG9hZCBMaWJyYXJpZXMKbGlicmFyeShzbGluZ3Nob3QpCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHNjYWxlcykKCiMgMi4gQ29udmVydCBTZXVyYXQgdG8gU2luZ2xlQ2VsbEV4cGVyaW1lbnQKIyBXZSB1c2UgdGhlICdkb3JvdGhlYScgYXNzYXkgZm9yIFRGLWJhc2VkIHRyYWplY3RvcnksIG9yICdTQ1QnIGZvciBnZW5lLWJhc2VkLgojIFRGLWJhc2VkIGlzIGJldHRlciBmb3IgInJlZ3VsYXRvcnkiIHRyYWplY3Rvcmllcy4Kc2NlIDwtIGFzLlNpbmdsZUNlbGxFeHBlcmltZW50KHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIikKCiMgMy4gUnVuIFNsaW5nc2hvdAojIFdlIHNwZWNpZnkgdGhlIHJlZHVjdGlvbiAoVU1BUCkgYW5kIHRoZSBjbHVzdGVyIGxhYmVscwojICdzdGFydC5jbHVzJyA9IDUgc2V0cyB0aGUgcm9vdCBhdCB0aGUgU3RlbS1saWtlIGNsdXN0ZXIKc2NlIDwtIHNsaW5nc2hvdChzY2UsIGNsdXN0ZXJMYWJlbHMgPSBzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycywgCiAgICAgICAgICAgICAgICAgcmVkdWNlZERpbSA9ICdVTUFQJywgc3RhcnQuY2x1cyA9ICc1JykKCiMgNC4gRXh0cmFjdCBUcmFqZWN0b3J5IERhdGEgZm9yIFBsb3R0aW5nIGluIGdncGxvdDIKIyBHZXQgdGhlIGN1cnZlcyAobGluZWFnZXMpCnNsaW5nc2hvdF9jdXJ2ZXMgPC0gU2xpbmdzaG90RGF0YVNldChzY2UpCmN1cnZlX2Nvb3JkcyA8LSBkYXRhLmZyYW1lKCkKCmZvciAoaSBpbiBzZXFfYWxvbmcoc2xpbmdDdXJ2ZXMoc2xpbmdzaG90X2N1cnZlcykpKSB7CiAgYyA8LSBzbGluZ0N1cnZlcyhzbGluZ3Nob3RfY3VydmVzKVtbaV1dCiAgZGYgPC0gYXMuZGF0YS5mcmFtZShjJHNbYyRvcmQsIF0pCiAgZGYkTGluZWFnZSA8LSBwYXN0ZTAoIkxpbmVhZ2UiLCBpKQogIGN1cnZlX2Nvb3JkcyA8LSByYmluZChjdXJ2ZV9jb29yZHMsIGRmKQp9CgojIEdldCBjZWxsIGVtYmVkZGluZ3MKdW1hcF9kYXRhIDwtIGFzLmRhdGEuZnJhbWUoRW1iZWRkaW5ncyhzZXVyYXRfb2JqLCAidW1hcCIpKQp1bWFwX2RhdGEkQ2x1c3RlciA8LSBzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycwoKIyA1LiBQbG90CnBfc2xpbmdzaG90IDwtIGdncGxvdCh1bWFwX2RhdGEsIGFlcyh4ID0gdW1hcF8xLCB5ID0gdW1hcF8yKSkgKwogICMgQ2VsbCBwb2ludHMKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IENsdXN0ZXIpLCBzaXplID0gMC44LCBhbHBoYSA9IDAuNikgKwogIAogICMgVHJhamVjdG9yeSBwYXRocwogIGdlb21fcGF0aChkYXRhID0gY3VydmVfY29vcmRzLCBhZXMoeCA9IHVtYXBfMSwgeSA9IHVtYXBfMiwgZ3JvdXAgPSBMaW5lYWdlKSwgCiAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gMS4yLCBhcnJvdyA9IGFycm93KHR5cGUgPSAiY2xvc2VkIiwgbGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKSkpICsKICAKICAjIFN0eWxpbmcKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gU2V1cmF0OjpEaXNjcmV0ZVBhbGV0dGUoMTQpKSArCiAgbGFicyh0aXRsZSA9ICJSZWd1bGF0b3J5IFRyYWplY3RvcnkgSW5mZXJlbmNlIChTbGluZ3Nob3QpIiwKICAgICAgIHN1YnRpdGxlID0gIlJvb3Qgc2V0IHRvIENsdXN0ZXIgNSAoU3RlbS1saWtlIFN0YXRlKSIsCiAgICAgICB4ID0gIlVNQVAgMSIsIHkgPSAiVU1BUCAyIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpKQoKIyA2LiBTYXZlCmdnc2F2ZSgiT3V0cHV0X0ZpZ3VyZXMvU3VwcGxlbWVudGFyeV9TbGluZ3Nob3RfVHJhamVjdG9yeS5wZGYiLCBwX3NsaW5nc2hvdCwgd2lkdGggPSA4LCBoZWlnaHQgPSA3KQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfU2xpbmdzaG90X1RyYWplY3RvcnkucG5nIiwgcF9zbGluZ3Nob3QsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNywgZHBpID0gMzAwKQoKcHJpbnQocF9zbGluZ3Nob3QpCmNhdCgi4pyTIFNsaW5nc2hvdCBUcmFqZWN0b3J5IFNhdmVkLiBBcnJvd3MgaW5kaWNhdGUgZGV2ZWxvcG1lbnRhbCBmbG93IGZyb20gQ2x1c3RlciA1LlxuIikKYGBgCgoKIyBTVVBQTEVNRU5UQVJZIEZJR1VSRTogIE1vbm9jbGUzIChJZiB5b3UgcHJlZmVyIHBzZXVkb3RpbWUgY29sb3JpbmcpCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CmxpYnJhcnkobW9ub2NsZTMpCmxpYnJhcnkoU2V1cmF0V3JhcHBlcnMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKCiMgLS0tIDEuIERlZmluZSBIZWxwZXIgRnVuY3Rpb24gKFJlcXVpcmVkIGZvciBNb25vY2xlMykgLS0tCmdldF9lYXJsaWVzdF9wcmluY2lwYWxfbm9kZSA8LSBmdW5jdGlvbihjZHMsIHN0YXJ0X2NlbGxzLCByZWR1Y3Rpb25fbWV0aG9kID0gIlVNQVAiKXsKICAjIEdldCB0aGUgY2xvc2VzdCBwcmluY2lwYWwgbm9kZSB0byB0aGUgZ2VvbWV0cmljIGNlbnRlciBvZiBzdGFydF9jZWxscwogIGNlbGxfaWRzIDwtIGNvbG5hbWVzKGNkcykKICAKICBjbG9zZXN0X3ZlcnRleCA8LSBjZHNAcHJpbmNpcGFsX2dyYXBoX2F1eFtbcmVkdWN0aW9uX21ldGhvZF1dJHByX2dyYXBoX2NlbGxfcHJval9jbG9zZXN0X3ZlcnRleAogIGNsb3Nlc3RfdmVydGV4IDwtIGFzLm1hdHJpeChjbG9zZXN0X3ZlcnRleFtjb2xuYW1lcyhjZHMpLCBdKQogIAogIHJvb3RfcHJfbm9kZXMgPC0gaWdyYXBoOjpWKHByaW5jaXBhbF9ncmFwaChjZHMpW1tyZWR1Y3Rpb25fbWV0aG9kXV0pJG5hbWVbYXMubnVtZXJpYyhuYW1lcyh3aGljaC5tYXgodGFibGUoY2xvc2VzdF92ZXJ0ZXhbc3RhcnRfY2VsbHMsXSkpKSldCiAgCiAgcmV0dXJuKHJvb3RfcHJfbm9kZXMpCn0KCiMgLS0tIDIuIENvbnZlcnQgU2V1cmF0IHRvIENlbGxEYXRhU2V0IChDRFMpIC0tLQojIFdlIHVzZSB0aGUgJ2Rvcm90aGVhJyBhc3NheSBmb3IgVEYtYmFzZWQgdHJhamVjdG9yeSAob3IgJ1NDVCcgZm9yIGdlbmUtYmFzZWQpCmNkcyA8LSBhcy5jZWxsX2RhdGFfc2V0KHNldXJhdF9vYmopCgojIC0tLSAzLiBQcmVwcm9jZXNzICYgTGVhcm4gR3JhcGggLS0tCiMgTW9ub2NsZSBuZWVkcyB0byByZS1jbHVzdGVyIHRvIGxlYXJuIHRoZSBncmFwaCBzdHJ1Y3R1cmUKY2RzIDwtIGNsdXN0ZXJfY2VsbHMoY2RzLCByZWR1Y3Rpb25fbWV0aG9kID0gIlVNQVAiKQpjZHMgPC0gbGVhcm5fZ3JhcGgoY2RzLCB1c2VfcGFydGl0aW9uID0gVFJVRSwgdmVyYm9zZSA9IEZBTFNFKQoKIyAtLS0gNC4gT3JkZXIgQ2VsbHMgKFJvb3QgPSBDbHVzdGVyIDUpIC0tLQojIElkZW50aWZ5IGNlbGxzIGluIENsdXN0ZXIgNSAoU3RlbS1saWtlKQpzdGVtX2NlbGxzIDwtIGNvbG5hbWVzKHNldXJhdF9vYmopW3NldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzID09ICI1Il0KCiMgRmluZCB0aGUgcm9vdCBub2RlCmNsb3Nlc3Rfbm9kZSA8LSBnZXRfZWFybGllc3RfcHJpbmNpcGFsX25vZGUoY2RzLCBzdGFydF9jZWxscyA9IHN0ZW1fY2VsbHMpCgojIE9yZGVyIGNlbGxzIGJhc2VkIG9uIHBzZXVkb3RpbWUgc3RhcnRpbmcgZnJvbSB0aGF0IG5vZGUKY2RzIDwtIG9yZGVyX2NlbGxzKGNkcywgcm9vdF9wcl9ub2RlcyA9IGNsb3Nlc3Rfbm9kZSkKCiMgLS0tIDUuIFBsb3QgUHNldWRvdGltZSAtLS0KcF9tb25vY2xlIDwtIHBsb3RfY2VsbHMoY2RzLAogICAgICAgICAgIGNvbG9yX2NlbGxzX2J5ID0gInBzZXVkb3RpbWUiLAogICAgICAgICAgIGxhYmVsX2NlbGxfZ3JvdXBzID0gRkFMU0UsCiAgICAgICAgICAgbGFiZWxfbGVhdmVzID0gRkFMU0UsCiAgICAgICAgICAgbGFiZWxfYnJhbmNoX3BvaW50cyA9IEZBTFNFLAogICAgICAgICAgIGdyYXBoX2xhYmVsX3NpemUgPSAxLjUsCiAgICAgICAgICAgdHJhamVjdG9yeV9ncmFwaF9jb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgdHJhamVjdG9yeV9ncmFwaF9zZWdtZW50X3NpemUgPSAxLjApICsKICBsYWJzKHRpdGxlID0gIlJlZ3VsYXRvcnkgVHJhamVjdG9yeSAoUHNldWRvdGltZSkiLAogICAgICAgc3VidGl0bGUgPSAiUm9vdDogQ2x1c3RlciA1IChTdGVtLWxpa2UgU3RhdGUpIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSkKCiMgU2F2ZQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfTW9ub2NsZTNfUHNldWRvdGltZS5wZGYiLCBwX21vbm9jbGUsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNykKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9TdXBwbGVtZW50YXJ5X01vbm9jbGUzX1BzZXVkb3RpbWUucG5nIiwgcF9tb25vY2xlLCB3aWR0aCA9IDgsIGhlaWdodCA9IDcsIGRwaSA9IDMwMCkKCnByaW50KHBfbW9ub2NsZSkKY2F0KCLinJMgTW9ub2NsZTMgVHJhamVjdG9yeSBTYXZlZC4gQ2VsbHMgY29sb3JlZCBieSBwc2V1ZG90aW1lIChQdXJwbGUgPSBFYXJseSwgWWVsbG93ID0gTGF0ZSkuXG4iKQoKYGBgCgoKIyBTVVBQTEVNRU5UQVJZIEZJR1VSRTogIFIgQ29kZTogUEFHQS1saWtlIENvbm5lY3Rpdml0eSBHcmFwaCAoVEYgQWN0aXZpdHkpCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBTVVBQTEVNRU5UQVJZIEZJR1VSRTogUEFHQS1saWtlIENvbm5lY3Rpdml0eSBHcmFwaCAoVEYgQWN0aXZpdHkpCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZ2dyYXBoKSAjIE1ha2Ugc3VyZSBnZ3JhcGggaXMgaW5zdGFsbGVkOiBpbnN0YWxsLnBhY2thZ2VzKCJnZ3JhcGgiKQpsaWJyYXJ5KGRwbHlyKQoKIyAxLiBCdWlsZCBhIGstTmVhcmVzdCBOZWlnaGJvciAoS05OKSBHcmFwaCBvbiBURiBBY3Rpdml0eQojIFdlIHVzZSB0aGUgJ2Rvcm90aGVhJyBhc3NheSB0byBlbnN1cmUgY29ubmVjdGlvbnMgYXJlIGJhc2VkIG9uIFJFR1VMQVRJT04KRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKc2V1cmF0X29iaiA8LSBGaW5kTmVpZ2hib3JzKHNldXJhdF9vYmosIGZlYXR1cmVzID0gcm93bmFtZXMoc2V1cmF0X29iaiksIGsucGFyYW0gPSAyMCwgdmVyYm9zZSA9IEZBTFNFKQoKIyAyLiBDb25zdHJ1Y3QgdGhlIFBBR0EgR3JhcGggKENsdXN0ZXItdG8tQ2x1c3RlciBDb25uZWN0aXZpdHkpCiMgV2UgY2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgZWRnZXMgYmV0d2VlbiBjZWxscyBvZiBkaWZmZXJlbnQgY2x1c3RlcnMKIyBBY2Nlc3MgdGhlIGdyYXBoIGRpcmVjdGx5IHVzaW5nIGlncmFwaCBmdW5jdGlvbnMgKGFscmVhZHkgYXZhaWxhYmxlIHZpYSBTZXVyYXQpCmFkal9tYXQgPC0gc2V1cmF0X29iakBncmFwaHMkZG9yb3RoZWFfc25uCmNsdXN0ZXJzIDwtIHNldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzCm5fY2x1c3RlcnMgPC0gbGVuZ3RoKGxldmVscyhjbHVzdGVycykpCgojIEluaXRpYWxpemUgY29ubmVjdGl2aXR5IG1hdHJpeApjb25uZWN0X21hdCA8LSBtYXRyaXgoMCwgbnJvdyA9IG5fY2x1c3RlcnMsIG5jb2wgPSBuX2NsdXN0ZXJzKQpyb3duYW1lcyhjb25uZWN0X21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQpjb2xuYW1lcyhjb25uZWN0X21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQoKIyBGaWxsIG1hdHJpeCB3aXRoIGVkZ2Ugd2VpZ2h0cyAoc3VtIG9mIHNoYXJlZCBOTiBsaW5rcykKY2x1c3Rlcl9pbmRpY2VzIDwtIHNwbGl0KDE6bmNvbChzZXVyYXRfb2JqKSwgY2x1c3RlcnMpCgpmb3IgKGkgaW4gMTpuX2NsdXN0ZXJzKSB7CiAgZm9yIChqIGluIGk6bl9jbHVzdGVycykgewogICAgaWYgKGkgPT0gaikgbmV4dCAjIFNraXAgc2VsZi1sb29wcwogICAgCiAgICBjZWxsc19pIDwtIGNsdXN0ZXJfaW5kaWNlc1tbaV1dCiAgICBjZWxsc19qIDwtIGNsdXN0ZXJfaW5kaWNlc1tbal1dCiAgICAKICAgICMgQ2FsY3VsYXRlIHRvdGFsIHdlaWdodCBvZiBjb25uZWN0aW9ucyBiZXR3ZWVuIENsdXN0ZXIgaSBhbmQgQ2x1c3RlciBqCiAgICBzdWJfbWF0IDwtIGFkal9tYXRbY2VsbHNfaSwgY2VsbHNfal0KICAgIHdlaWdodCA8LSBzdW0oc3ViX21hdCkKICAgIAogICAgIyBOb3JtYWxpemUgYnkgY2x1c3RlciBzaXplIHRvIGF2b2lkIGJpYXMgdG93YXJkcyBsYXJnZSBjbHVzdGVycwogICAgbm9ybV93ZWlnaHQgPC0gd2VpZ2h0IC8gKGxlbmd0aChjZWxsc19pKSAqIGxlbmd0aChjZWxsc19qKSkKICAgIAogICAgY29ubmVjdF9tYXRbaSwgal0gPC0gbm9ybV93ZWlnaHQKICAgIGNvbm5lY3RfbWF0W2osIGldIDwtIG5vcm1fd2VpZ2h0CiAgfQp9CgojIDMuIENyZWF0ZSBJZ3JhcGggT2JqZWN0CiMgVGhyZXNob2xkIGVkZ2VzIHRvIHNob3cgb25seSBzdHJvbmcgY29ubmVjdGlvbnMgKHRvcCAyMCUpCnRocmVzaG9sZCA8LSBxdWFudGlsZShjb25uZWN0X21hdFtjb25uZWN0X21hdCA+IDBdLCAwLjgwKSAKY29ubmVjdF9tYXRbY29ubmVjdF9tYXQgPCB0aHJlc2hvbGRdIDwtIDAKCiMgVXNlIGlncmFwaDo6Z3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4IGV4cGxpY2l0bHkKZ3JhcGggPC0gaWdyYXBoOjpncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoY29ubmVjdF9tYXQsIG1vZGUgPSAidW5kaXJlY3RlZCIsIHdlaWdodGVkID0gVFJVRSkKCiMgQWRkIE5vZGUgQXR0cmlidXRlcyAoU2l6ZSA9IE51bWJlciBvZiBDZWxscykKaWdyYXBoOjpWKGdyYXBoKSRzaXplIDwtIGFzLm51bWVyaWModGFibGUoY2x1c3RlcnMpKQppZ3JhcGg6OlYoZ3JhcGgpJGxhYmVsIDwtIGxldmVscyhjbHVzdGVycykKCiMgNC4gUGxvdCBQQUdBIEdyYXBoCnBfcGFnYSA8LSBnZ3JhcGgoZ3JhcGgsIGxheW91dCA9ICJmciIpICsgIyBGcnVjaHRlcm1hbi1SZWluZ29sZCBsYXlvdXQKICAjIEVkZ2VzIChUaGlja25lc3MgPSBDb25uZWN0aXZpdHkgU3RyZW5ndGgpCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gd2VpZ2h0KSwgY29sb3IgPSAiZ3JleTUwIiwgYWxwaGEgPSAwLjYpICsKICBzY2FsZV9lZGdlX3dpZHRoKHJhbmdlID0gYygwLjUsIDMpKSArCiAgCiAgIyBOb2RlcyAoQ2x1c3RlcnMpCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gc2l6ZSwgY29sb3IgPSBsYWJlbCkpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYyg1LCAxNSkpICsKICAKICAjIExhYmVscwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IGxhYmVsKSwgZm9udGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIndoaXRlIikgKwogIAogICMgU3R5bGluZwogICMgVXNlIHNjYWxlczo6aHVlX3BhbCgpIGZvciBjb2xvcnMgdG8gYXZvaWQgZnVuY3Rpb24gZXJyb3JzCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNjYWxlczo6aHVlX3BhbCgpKG5fY2x1c3RlcnMpKSArCiAgbGFicyh0aXRsZSA9ICJSZWd1bGF0b3J5IFBBR0EgR3JhcGggKFRGIENvbm5lY3Rpdml0eSkiLAogICAgICAgc3VidGl0bGUgPSAiTm9kZXMgPSBDbHVzdGVyczsgRWRnZXMgPSBSZWd1bGF0b3J5IFNpbWlsYXJpdHkgKERvUm90aEVBKSIsCiAgICAgICBjYXB0aW9uID0gIlRoaWNrIGxpbmVzIGluZGljYXRlIHN0cm9uZyByZWd1bGF0b3J5IHRyYW5zaXRpb25zIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBzaXplPTE0KSkKCiMgU2F2ZQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfUEFHQV9HcmFwaC5wZGYiLCBwX3BhZ2EsIHdpZHRoID0gOCwgaGVpZ2h0ID0gOCkKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9TdXBwbGVtZW50YXJ5X1RGX1BBR0FfR3JhcGgucG5nIiwgcF9wYWdhLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKCnByaW50KHBfcGFnYSkKY2F0KCLinJMgUEFHQSBHcmFwaCBTYXZlZC4gTG9vayBmb3IgY29ubmVjdGlvbnMgZnJvbSBDbHVzdGVyIDUgKFN0ZW0pIHRvIDExLzQvNy5cbiIpCgpgYGAKCiMgU2F2ZQpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQoKc2F2ZVJEUyhzZXVyYXRfb2JqLCAidGVtcF9zZXVyYXRfb2JqLnJkcyIpCgoKCgpgYGAK