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

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

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

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

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) ✓✓✓
SUPPLEMENTARY
FIGURES
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
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.
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
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
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
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
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
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.
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.
LS0tCnRpdGxlOiAiVEYgQWN0aXZpdHkgSW5mZXJlbmNlIEFuYWx5c2lzIChEZWNvdXBsZVIgKyBEb1JvdGhFQSkgVmlzdWFsaXphdGlvbiIKYXV0aG9yOiAiTmFzaXIgTWFobW9vZCBBYmJhc2kiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgIHRoZW1lOiBqb3VybmFsCi0tLQoKCiMgbG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9VFJVRX0KIyBEYXRhIFByb2Nlc3NpbmcKbGlicmFyeShkcGx5cikKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHN0cmluZ3IpCgojIFZpc3VhbGl6YXRpb24KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShTQ3B1YnIpCgojIFJlZ3VsYXRvcnkgTmV0d29yayBJbmZlcmVuY2UKbGlicmFyeShkZWNvdXBsZVIpCmxpYnJhcnkoZG9yb3RoZWEpCmRhdGEoZG9yb3RoZWFfaHMsIHBhY2thZ2UgPSAiZG9yb3RoZWEiKQpsaWJyYXJ5KHRpY3RvYykKCgpgYGAKCiMgTG9hZCBTZXVyYXQgT2JqZWN0IApgYGB7cn0KCiMgTG9hZCB5b3VyIFNldXJhdCBPYmplY3QKc2V1cmF0X29iaiA8LSByZWFkUkRTKCJPdXRwdXRfT2JqZWN0cy9TZXVyYXRfT2JqZWN0X1dpdGhfVEZfQWN0aXZpdHkucmRzIikKCklkZW50cyhzZXVyYXRfb2JqKSA8LSAic2V1cmF0X2NsdXN0ZXJzIgpjYXQoIuKckyBTZXVyYXQgb2JqZWN0IGxvYWRlZFxuIikKY2F0KHNwcmludGYoIiAgLSAlZCBjZWxscyBhY3Jvc3MgJWQgY2x1c3RlcnNcbiIsIAogICAgICAgICAgICBuY29sKHNldXJhdF9vYmopLCAKICAgICAgICAgICAgbGVuZ3RoKHVuaXF1ZShzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykpKSkKYGBgCgojIyBSdW4gdGhpcyBjb2RlIGJsb2NrIHRvIHJlc3RvcmUgYWN0aXZpdGllcyBpbnN0YW50bHk6CmBgYHtyfQoKIyBJZiAnYWN0aXZpdGllcycgaXMgbWlzc2luZyBidXQgJ2Rvcm90aGVhJyBhc3NheSBleGlzdHMsIHJlY29uc3RydWN0IGl0OgppZiAoIWV4aXN0cygiYWN0aXZpdGllcyIpICYmICJkb3JvdGhlYSIgJWluJSBuYW1lcyhzZXVyYXRfb2JqQGFzc2F5cykpIHsKICAKICBwcmludCgiUmVjb25zdHJ1Y3RpbmcgJ2FjdGl2aXRpZXMnIGRhdGFmcmFtZSBmcm9tIFNldXJhdCBvYmplY3QuLi4iKQogIAogICMgRXh0cmFjdCB0aGUgbWF0cml4IChTZXVyYXQgdjUgdXNlcyAnbGF5ZXInIGluc3RlYWQgb2YgJ3Nsb3QnKQogICMgU2luY2UgeW91IHJhbiBTY2FsZURhdGEsIHdlIHVzZSAnc2NhbGUuZGF0YScKICBtYXQgPC0gR2V0QXNzYXlEYXRhKHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIiwgbGF5ZXIgPSAic2NhbGUuZGF0YSIpCiAgCiAgIyBDb252ZXJ0IHRvIGxvbmcgZm9ybWF0ICh3aGF0IFNDcHViciBuZWVkcykKICBhY3Rpdml0aWVzIDwtIGFzLmRhdGEuZnJhbWUobWF0KSAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbigic291cmNlIikgJT4lCiAgICBwaXZvdF9sb25nZXIoY29scyA9IC1zb3VyY2UsIG5hbWVzX3RvID0gImNvbmRpdGlvbiIsIHZhbHVlc190byA9ICJzY29yZSIpICU+JQogICAgbXV0YXRlKHN0YXRpc3RpYyA9ICJub3JtX3dtZWFuIikgIyBTQ3B1YnIgcmVxdWlyZXMgdGhpcyBjb2x1bW4KICAgIAogIHByaW50KCJBY3Rpdml0aWVzIGRhdGFmcmFtZSByZXN0b3JlZCEiKQp9CmBgYAoKIyMgU0NwdWJyIEhlYXRtYXAgVmlzdWFsaXphdGlvbi1IZWF0bWFwIG9mIGF2ZXJhZ2VkIHNjb3JlcwpgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CmxpYnJhcnkoU0NwdWJyKQojIEdlbmVyYWwgaGVhdG1hcCAoVG9wIFZhcmlhYmxlIFRGcykKb3V0IDwtIFNDcHVicjo6ZG9fVEZBY3Rpdml0eVBsb3Qoc2FtcGxlID0gc2V1cmF0X29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWN0aXZpdGllcyA9IGFjdGl2aXRpZXMpCnAxIDwtIG91dCRoZWF0bWFwcyRhdmVyYWdlX3Njb3JlcwpwcmludChwMSkKCiMgMS4gU2F2ZSBhcyBQREYKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9EZWZhdWx0LnBkZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCnByaW50KHAxKSAjIENvbXBsZXhIZWF0bWFwIHJlcXVpcmVzIGV4cGxpY2l0IHByaW50KCkgaW5zaWRlIHBkZigpCmRldi5vZmYoKQoKIyAyLiBTYXZlIGFzIFBORwpwbmcoIk91dHB1dF9GaWd1cmVzL1NDcHVicl9IZWF0bWFwX0RlZmF1bHQucG5nIiwgd2lkdGggPSAxMCAqIDMwMCwgaGVpZ2h0ID0gOCAqIDMwMCwgcmVzID0gMzAwKQpwcmludChwMSkKZGV2Lm9mZigpCmBgYAoKCiMjIFNldCB0aGUgc2NhbGUgbGltaXRzCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KCm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlQbG90KHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uY3V0b2ZmID0gLTEuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LmN1dG9mZiA9IDEuNSkKcDIgPC0gb3V0JGhlYXRtYXBzJGF2ZXJhZ2Vfc2NvcmVzCnByaW50KHAyKQoKIyBTYXZlIENvbXBsZXhIZWF0bWFwIHByb3Blcmx5CnBkZigiT3V0cHV0X0ZpZ3VyZXMvU0NwdWJyX0hlYXRtYXBfU2NhbGVkLnBkZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCnByaW50KHAyKQpkZXYub2ZmKCkKCnBuZygiT3V0cHV0X0ZpZ3VyZXMvU0NwdWJyX0hlYXRtYXBfU2NhbGVkLnBuZyIsIHdpZHRoID0gMTAgKiAzMDAsIGhlaWdodCA9IDggKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQocDIpCmRldi5vZmYoKQoKCmBgYAoKIyMgRW5mb3JjZSBTeW1tZXRyeSAoQmVzdCBmb3IgTWFudXNjcmlwdCkKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwfQoKb3V0IDwtIFNDcHVicjo6ZG9fVEZBY3Rpdml0eVBsb3Qoc2FtcGxlID0gc2V1cmF0X29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWN0aXZpdGllcyA9IGFjdGl2aXRpZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jdXRvZmYgPSAtMS41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXguY3V0b2ZmID0gMS41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmZvcmNlX3N5bW1ldHJ5ID0gVFJVRSkKcDMgPC0gb3V0JGhlYXRtYXBzJGF2ZXJhZ2Vfc2NvcmVzCnByaW50KHAzKQoKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9TeW1tZXRyaWMucGRmIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKcHJpbnQocDMpCmRldi5vZmYoKQoKcG5nKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9TeW1tZXRyaWMucG5nIiwgd2lkdGggPSAxMCAqIDMwMCwgaGVpZ2h0ID0gOCAqIDMwMCwgcmVzID0gMzAwKQpwcmludChwMykKZGV2Lm9mZigpCgpgYGAKCiMjIFRvcCA0MCBURnMKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTE0fQpvdXQgPC0gU0NwdWJyOjpkb19URkFjdGl2aXR5UGxvdChzYW1wbGUgPSBzZXVyYXRfb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY3Rpdml0aWVzID0gYWN0aXZpdGllcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl90ZnMgPSA0MCkKcDQgPC0gb3V0JGhlYXRtYXBzJGF2ZXJhZ2Vfc2NvcmVzCnByaW50KHA0KQoKcGRmKCJPdXRwdXRfRmlndXJlcy9TQ3B1YnJfSGVhdG1hcF9Ub3A0MC5wZGYiLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSA2KQpwcmludChwNCkKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL1NDcHVicl9IZWF0bWFwX1RvcDQwLnBuZyIsIHdpZHRoID0gMTQgKiAzMDAsIGhlaWdodCA9IDYgKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQocDQpCmRldi5vZmYoKQpgYGAKCgojIyBGSUdVUkUgMy4xNkE6IEdsb2JhbCBURiBBY3Rpdml0eSBIZWF0bWFwIChUb3AgMTAwIFRGcykKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0zMn0KCm91dCA8LSBTQ3B1YnI6OmRvX1RGQWN0aXZpdHlQbG90KHNhbXBsZSA9IHNldXJhdF9vYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2aXRpZXMgPSBhY3Rpdml0aWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3RmcyA9IDEwMCkKcDUgPC0gb3V0JGhlYXRtYXBzJGF2ZXJhZ2Vfc2NvcmVzCnByaW50KHA1KQoKcGRmKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfMy4xNkFfR2xvYmFsX1RGX0hlYXRtYXBfVG9wMTAwLnBkZiIsIHdpZHRoID0gMzIsIGhlaWdodCA9IDEyKQpwcmludChwNSkKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2QV9HbG9iYWxfVEZfSGVhdG1hcF9Ub3AxMDAucG5nIiwgd2lkdGggPSAzMiAqIDMwMCwgaGVpZ2h0ID0gMTIgKiAzMDAsIHJlcyA9IDMwMCkKcHJpbnQocDUpCmRldi5vZmYoKQpgYGAKCiMgQ2hlY2sgVEYgQXZhaWxhYmlsaXR5CmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MzJ9CgojIEdldCBURiBuYW1lcyBmcm9tIGRvcm90aGVhIGFzc2F5CkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiZG9yb3RoZWEiCnRmc19pbl9hY3Rpdml0eSA8LSByb3duYW1lcyhzZXVyYXRfb2JqKQoKIyBHZXQgZ2VuZSBuYW1lcyBmcm9tIFNDVCBhc3NheQpEZWZhdWx0QXNzYXkoc2V1cmF0X29iaikgPC0gIlNDVCIKZ2VuZXNfaW5fZXhwcmVzc2lvbiA8LSByb3duYW1lcyhzZXVyYXRfb2JqKQoKIyBGaW5kIFRGcyBwcmVzZW50IGluIEJPVEgKdGZzX2luX2JvdGggPC0gaW50ZXJzZWN0KHRmc19pbl9hY3Rpdml0eSwgZ2VuZXNfaW5fZXhwcmVzc2lvbikKCmNhdCgiXG7ilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZBcbiIpCmNhdChzcHJpbnRmKCLinJMgJWQgVEZzIGF2YWlsYWJsZSBpbiBib3RoIGFzc2F5c1xuIiwgbGVuZ3RoKHRmc19pbl9ib3RoKSkpCmNhdCgi4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQXG4iKQpgYGAKCgojIEZJR1VSRSAzLjE2QjogRXhwcmVzc2lvbi1BY3Rpdml0eSBDb25jb3JkYW5jZSAoMi1Sb3cgR3JpZCkKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTIyfQoKIyBTZWxlY3QgNiBURnMgcmVwcmVzZW50aW5nIGtleSBoZXRlcm9nZW5laXR5IGF4ZXMKc2VsZWN0ZWRfdGZzIDwtIGMoCiAgIkZPWE8xIiwgICAjIEhvbWVvc3Rhc2lzCiAgIk1ZQyIsICAgICAjIE9uY29nZW5pYwogICJUQlgyMSIsICAgIyBUaDEvU3RlbQogICJHQVRBMyIsICAgIyBUaDIKICAiUkVMQSIsICAgICMgSW5mbGFtbWF0b3J5CiAgIklSRjEiICAgICAjIEludGVyZmVyb24KKQoKY2F0KCJcbuKckyBTZWxlY3RlZCBURnMgZm9yIEZpZ3VyZSAzLjE2QjpcbiIpCnByaW50KHNlbGVjdGVkX3RmcykKCiMgLS0tLSBUT1AgUk9XOiBURiBBQ1RJVklUWSAoZG9yb3RoZWEgYXNzYXkpIC0tLS0KRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKCmFjdF9wbG90cyA8LSBsYXBwbHkoc2VsZWN0ZWRfdGZzLCBmdW5jdGlvbih0ZikgewogIEZlYXR1cmVQbG90KHNldXJhdF9vYmosIAogICAgICAgICAgICAgIGZlYXR1cmVzID0gdGYsCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgICAgICAgIG9yZGVyID0gVFJVRSwsbGFiZWwgPSBULAogICAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCAicmVkMyIpLAogICAgICAgICAgICAgIHB0LnNpemUgPSAwLjMpICsKICAgIGdndGl0bGUocGFzdGUwKHRmLCAiIEFjdGl2aXR5IikpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSkKfSkKCmNhdCgi4pyTIFRGIEFjdGl2aXR5IHBsb3RzIGNyZWF0ZWQgKFRPUCBST1cpXG4iKQoKIyAtLS0tIEJPVFRPTSBST1c6IEdlbmUgRVhQUkVTU0lPTiAoU0NUIGFzc2F5KSAtLS0tCkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiU0NUIgoKZXhwcl9wbG90cyA8LSBsYXBwbHkoc2VsZWN0ZWRfdGZzLCBmdW5jdGlvbih0ZikgewogIEZlYXR1cmVQbG90KHNldXJhdF9vYmosIAogICAgICAgICAgICAgIGZlYXR1cmVzID0gdGYsIAogICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwKICAgICAgICAgICAgICBvcmRlciA9IFRSVUUsbGFiZWwgPSBULAogICAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCAiZGFya2JsdWUiKSwKICAgICAgICAgICAgICBwdC5zaXplID0gMC4zKSArCiAgICBnZ3RpdGxlKHBhc3RlMCh0ZiwgIiBFeHByZXNzaW9uIikpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSkKfSkKCmNhdCgi4pyTIEdlbmUgRXhwcmVzc2lvbiBwbG90cyBjcmVhdGVkIChCT1RUT00gUk9XKVxuIikKCiMgLS0tLSBDT01CSU5FOiAyIHJvd3Mgw5cgNiBjb2x1bW5zIC0tLS0KdG9wX3JvdyA8LSB3cmFwX3Bsb3RzKGFjdF9wbG90cywgbnJvdyA9IDEpCmJvdHRvbV9yb3cgPC0gd3JhcF9wbG90cyhleHByX3Bsb3RzLCBucm93ID0gMSkKCnBfMy4xNkIgPC0gdG9wX3JvdyAvIGJvdHRvbV9yb3cgKwogICAgICAgICAgIHBsb3RfYW5ub3RhdGlvbigKICAgICAgICAgICAgIHRpdGxlID0gIlRGIEFjdGl2aXR5IGFuZCBFeHByZXNzaW9uIFBhdHRlcm5zIiwKICAgICAgICAgICAgIHN1YnRpdGxlID0gIlRvcDogSW5mZXJyZWQgVEYgQWN0aXZpdHkgKERvUm90aEVBKSB8IEJvdHRvbTogR2VuZSBFeHByZXNzaW9uIChTQ1QpIiwKICAgICAgICAgICAgIHRoZW1lID0gdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImdyZXkzMCIpKQogICAgICAgICAgICkKCmdnc2F2ZSgiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlXzMuMTZCX0FjdGl2aXR5X3ZzX0V4cHJlc3Npb25fR3JpZC5wZGYiLCAKICAgICAgIHBfMy4xNkIsIAogICAgICAgd2lkdGggPSAyMiwgCiAgICAgICBoZWlnaHQgPSA4LCAKICAgICAgIHVuaXRzID0gImluIikKCmdnc2F2ZSgiT3V0cHV0X0ZpZ3VyZXMvRmlndXJlXzMuMTZCX0FjdGl2aXR5X3ZzX0V4cHJlc3Npb25fR3JpZC5wbmciLCAKICAgICAgIHBfMy4xNkIsIAogICAgICAgd2lkdGggPSAyMiwgCiAgICAgICBoZWlnaHQgPSA4LCAKICAgICAgIHVuaXRzID0gImluIiwgCiAgICAgICBkcGkgPSAzMDApCgpwcmludChwXzMuMTZCKQpjYXQoIlxu4pyT4pyT4pyTIEZJR1VSRSAzLjE2QiBTQVZFRCAoMjDDlzggaW5jaGVzLCAyIHJvd3Mgw5cgNiBjb2x1bW5zKSDinJPinJPinJNcbiIpCgpgYGAKCiMgRklHVVJFIDMuMTZDOiBMaXRlcmF0dXJlLVZhbGlkYXRlZCBURiBNb2R1bGVzCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkgICMgPC0tLSBFc3NlbnRpYWwgZm9yIGNvbG9yUmFtcDIKCiMgQ3VyYXRlZCBsaXN0IG9mIFPDqXphcnktcmVsZXZhbnQgVEZzCmxpdGVyYXR1cmVfdGZzIDwtIGMoIkdBVEEzIiwgIlRCWDIxIiwgIlJPUkMiLCAiRk9YUDMiLCAiVE9YIiwgIlNBVEIxIiwgIklLWkYyIiwKICAgICAgICAgICAgICAgICAgICAiU1RBVDMiLCAiU1RBVDVCIiwgIlNUQVQ2IiwgIlJFTEEiLCAiTkZLQjEiLCAiTVlDIiwgIkUyRjEiKQoKIyBDaGVjayBhdmFpbGFiaWxpdHkKYXZhaWxhYmxlX3RmcyA8LSBpbnRlcnNlY3QobGl0ZXJhdHVyZV90ZnMsIHJvd25hbWVzKHNldXJhdF9vYmpbWyJkb3JvdGhlYSJdXSkpCgojIEV4dHJhY3QgZGF0YQptYXRfc2NhbGVkIDwtIEdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheT0iZG9yb3RoZWEiLCBsYXllcj0ic2NhbGUuZGF0YSIpCm1hdF91c2UgPC0gbWF0X3NjYWxlZFthdmFpbGFibGVfdGZzLCAsIGRyb3A9RkFMU0VdCgojIENhbGN1bGF0ZSBhdmVyYWdlIGFjdGl2aXR5IHBlciBjbHVzdGVyCmNsdXN0ZXJzIDwtIGFzLmZhY3RvcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykKYXZnX21hdF96IDwtIHQoc2NhbGUodChzYXBwbHkobGV2ZWxzKGNsdXN0ZXJzKSwgZnVuY3Rpb24oY2wpIE1hdHJpeDo6cm93TWVhbnMobWF0X3VzZVssIGNsdXN0ZXJzID09IGNsLCBkcm9wPUZBTFNFXSkpKSkpCmF2Z19tYXRfeltpcy5uYShhdmdfbWF0X3opXSA8LSAwCgojIENyZWF0ZSBIZWF0bWFwCmh0XzMuMTZDIDwtIEhlYXRtYXAoYXZnX21hdF96LCAKICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlRGIGFjdGl2aXR5ICh6KSIsIAogICAgICAgICAgICAgICAgICAgIGNvbCA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTIsIDAsIDIpLCBjKCIjMzEzNjk1IiwgIndoaXRlIiwgIiNBNTAwMjYiKSksCiAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gIkxpdGVyYXR1cmUtdmFsaWRhdGVkIFPDqXphcnkgVEYgbW9kdWxlcyIpCgojIFNhdmUKcGRmKCJPdXRwdXRfRmlndXJlcy9GaWd1cmVfMy4xNkNfTGl0ZXJhdHVyZV9URl9IZWF0bWFwLnBkZiIsIHdpZHRoPTEwLCBoZWlnaHQ9NikKZHJhdyhodF8zLjE2QykKZGV2Lm9mZigpCgpwbmcoIk91dHB1dF9GaWd1cmVzL0ZpZ3VyZV8zLjE2Q19MaXRlcmF0dXJlX1RGX0hlYXRtYXAucG5nIiwgd2lkdGg9MTAqMzAwLCBoZWlnaHQ9NiozMDAsIHJlcz0zMDApCmRyYXcoaHRfMy4xNkMpCmRldi5vZmYoKQoKY2F0KCLinJMgRmlndXJlIDMuMTZDIFNhdmVkXG4iKQoKaHRfMy4xNkMKCmBgYAoKIyBTVVBQTEVNRU5UQVJZIEZJR1VSRVMKIyMgU1VQUExFTUVOVEFSWSBGSUdVUkUgMTogRGlmZmVyZW50aWFsIFRGIEFjdGl2aXR5IFZvbGNhbm8gUGxvdApgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKIyAxLiBQZXJmb3JtIERpZmZlcmVudGlhbCBBbmFseXNpcwpub25fbWFsaWduYW50X2NsdXN0ZXJzIDwtIGMoMywgMTApCnNldXJhdF9vYmokQ29uZGl0aW9uIDwtIGlmZWxzZShzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycyAlaW4lIG5vbl9tYWxpZ25hbnRfY2x1c3RlcnMsICJOb24tTWFsaWduYW50IiwgIk1hbGlnbmFudCIpCkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiZG9yb3RoZWEiCklkZW50cyhzZXVyYXRfb2JqKSA8LSAiQ29uZGl0aW9uIgoKY2F0KCJSdW5uaW5nIEZpbmRNYXJrZXJzLi4uXG4iKQpkaWZmX3RmcyA8LSBGaW5kTWFya2VycyhzZXVyYXRfb2JqLCBpZGVudC4xPSJNYWxpZ25hbnQiLCBpZGVudC4yPSJOb24tTWFsaWduYW50IiwgdGVzdC51c2U9InQiLCBsb2dmYy50aHJlc2hvbGQ9MC4xLCBtaW4ucGN0PTApCmRpZmZfdGZzJGdlbmUgPC0gcm93bmFtZXMoZGlmZl90ZnMpCgojIDIuIFBsb3QgRW5oYW5jZWRWb2xjYW5vCnBfdm9sY2FubyA8LSBFbmhhbmNlZFZvbGNhbm8oZGlmZl90ZnMsCiAgICBsYWIgPSByb3duYW1lcyhkaWZmX3RmcyksIHggPSAnYXZnX2xvZzJGQycsIHkgPSAncF92YWxfYWRqJywKICAgIHRpdGxlID0gJ0RpZmZlcmVudGlhbCBURiBBY3Rpdml0eTogTWFsaWduYW50IHZzLiBOb24tTWFsaWduYW50JywKICAgIHN1YnRpdGxlID0gJ0RlY291cGxlUiBJbmZlcnJlZCBBY3Rpdml0eScsCiAgICBwQ3V0b2ZmID0gMWUtNSwgRkNjdXRvZmYgPSAwLjUsIHBvaW50U2l6ZSA9IDMuMCwgbGFiU2l6ZSA9IDUuMCwgY29sQWxwaGEgPSAwLjgsCiAgICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsIGRyYXdDb25uZWN0b3JzID0gVFJVRSwgd2lkdGhDb25uZWN0b3JzID0gMC41LAogICAgY29sID0gYygiZ3JleTMwIiwgImZvcmVzdGdyZWVuIiwgInJveWFsYmx1ZSIsICJmaXJlYnJpY2syIikpCgpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfRGlmZmVyZW50aWFsX1RGX0FjdGl2aXR5X1ZvbGNhbm8ucGRmIiwgcF92b2xjYW5vLCB3aWR0aD0xMiwgaGVpZ2h0PTEwKQpwcmludChwX3ZvbGNhbm8pCmNhdCgi4pyTIFN1cHBsZW1lbnRhcnkgVm9sY2FubyBQbG90IFNhdmVkXG4iKQpgYGAKIyMgU1VQUExFTUVOVEFSWSBGSUdVUkU6ICBDSEVDSyBURiBBVkFJTEFCSUxJVFkKYGBge3IsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xNn0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgQ0hFQ0sgVEYgQVZBSUxBQklMSVRZCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKIyAxLiBEZWZpbmUgRGVzaXJlZCBNb2R1bGVzCm1vZHVsZXMgPC0gbGlzdCgKICAiSG9tZW9zdGFzaXMiICAgICAgICAgID0gYygiRk9YTzEiLCAiVENGNyIsICJMRUYxIiksCiAgIk9uY29nZW5pYy9DZWxsIEN5Y2xlIiA9IGMoIk1ZQyIsICJFMkYxIiwgIkUyRjQiLCAiRk9YTTEiKSwKICAiSW5mbGFtbWF0aW9uIChORi1rQikiID0gYygiUkVMQSIsICJORktCMSIsICJSRUwiLCAiU1RBVDMiKSwKICAiVHJlZyBTaWduYXR1cmUiICAgICAgID0gYygiRk9YUDMiLCAiU1RBVDVCIiwgIklLWkYyIiksCiAgIlRoMiBEaWZmZXJlbnRpYXRpb24iICA9IGMoIkdBVEEzIiwgIlNUQVQ2IiwgIk1BRiIpLAogICJUaDEgRGlmZmVyZW50aWF0aW9uIiAgPSBjKCJUQlgyMSIsICJTVEFUMSIsICJJUkYxIiksCiAgIkV4aGF1c3Rpb24iICAgICAgICAgICA9IGMoIlRPWCIsICJQUkRNMSIsICJCQVRGIikKKQoKIyAyLiBHZXQgYXZhaWxhYmxlIFRGcyBpbiB0aGUgb2JqZWN0CkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiZG9yb3RoZWEiCmF2YWlsYWJsZV90ZnMgPC0gcm93bmFtZXMoc2V1cmF0X29iaikKCiMgMy4gQ2hlY2sgZWFjaCBtb2R1bGUKY2F0KCJDaGVja2luZyBURiBhdmFpbGFiaWxpdHkgaW4gRG9Sb3RoRUEgYXNzYXkuLi5cbiIpCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQoKZmluYWxfbW9kdWxlcyA8LSBsaXN0KCkKCmZvciAobW9kX25hbWUgaW4gbmFtZXMobW9kdWxlcykpIHsKICB0ZnMgPC0gbW9kdWxlc1tbbW9kX25hbWVdXQogIAogICMgRmluZCBpbnRlcnNlY3Rpb24KICBwcmVzZW50IDwtIGludGVyc2VjdCh0ZnMsIGF2YWlsYWJsZV90ZnMpCiAgbWlzc2luZyA8LSBzZXRkaWZmKHRmcywgYXZhaWxhYmxlX3RmcykKICAKICAjIFJlcG9ydAogIGNhdChzcHJpbnRmKCJNb2R1bGU6ICVzXG4iLCBtb2RfbmFtZSkpCiAgY2F0KHNwcmludGYoIiAg4pyTIEZvdW5kICglZCk6ICVzXG4iLCBsZW5ndGgocHJlc2VudCksIHBhc3RlKHByZXNlbnQsIGNvbGxhcHNlPSIsICIpKSkKICAKICBpZihsZW5ndGgobWlzc2luZykgPiAwKSB7CiAgICBjYXQoc3ByaW50ZigiICDimqAgTUlTU0lORyAoJWQpOiAlc1xuIiwgbGVuZ3RoKG1pc3NpbmcpLCBwYXN0ZShtaXNzaW5nLCBjb2xsYXBzZT0iLCAiKSkpCiAgfQogIAogICMgT25seSBrZWVwIG1vZHVsZSBpZiBhdCBsZWFzdCAxIFRGIGV4aXN0cwogIGlmKGxlbmd0aChwcmVzZW50KSA+IDApIHsKICAgIGZpbmFsX21vZHVsZXNbW21vZF9uYW1lXV0gPC0gcHJlc2VudAogIH0KICBjYXQoIlxuIikKfQoKIyA0LiBVcGRhdGUgdGhlICdtb2R1bGVzJyBsaXN0IHRvIG9ubHkgdXNlIHZhbGlkIFRGcwptb2R1bGVzIDwtIGZpbmFsX21vZHVsZXMKY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpCmNhdCgiUmVhZHkgdG8gcGxvdCB3aXRoIHZhbGlkYXRlZCBURnMuXG4iKQpgYGAKCiMjIFNVUFBMRU1FTlRBUlkgRklHVVJFIDI6ICBWYXJpYW5jZSBFeHBsYWluZWQgYnkgS2V5IFJlZ3VsYXRvcnkgTW9kdWxlcwpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoU2V1cmF0KQoKIyAxLiBEZWZpbmUgTW9kdWxlcwptb2R1bGVzIDwtIGxpc3QoCiAgIkhvbWVvc3Rhc2lzIiA9IGMoIkZPWE8xIiwgIlRDRjciLCAiTEVGMSIpLAogICJPbmNvZ2VuaWMvQ2VsbCBDeWNsZSIgPSBjKCJNWUMiLCAiRTJGMSIsICJFMkY0IiwgIkZPWE0xIiksCiAgIkluZmxhbW1hdGlvbiAoTkYta0IpIiA9IGMoIlJFTEEiLCAiTkZLQjEiLCAiUkVMIiwgIlNUQVQzIiksCiAgIlRoMiBEaWZmZXJlbnRpYXRpb24iID0gYygiR0FUQTMiLCAiU1RBVDYiLCAiTUFGIiksCiAgIlRoMSBEaWZmZXJlbnRpYXRpb24iID0gYygiVEJYMjEiLCAiU1RBVDEiLCAiSVJGMSIpLAogICJFeGhhdXN0aW9uIiA9IGMoIlRPWCIsICJQUkRNMSIsICJCQVRGIikKKQoKIyAyLiBDYWxjdWxhdGUgVmFyaWFuY2UgdXNpbmcgJ2RhdGEnIGxheWVyIChOT1Qgc2NhbGUuZGF0YSkKIyBJTVBPUlRBTlQ6IHNjYWxlLmRhdGEgc2V0cyB2YXJpYW5jZSB0byAxLiBXZSBuZWVkICdkYXRhJyBmb3IgYmlvbG9naWNhbCB2YXJpYW5jZS4KRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKbWF0X3JhdyA8LSBHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJkYXRhIikKYWxsX3ZhcnMgPC0gYXBwbHkobWF0X3JhdywgMSwgdmFyKQoKIyAzLiBDYWxjdWxhdGUgTWVhbiBWYXJpYW5jZSBwZXIgTW9kdWxlCm1vZHVsZV92YXJpYW5jZSA8LSBzYXBwbHkobW9kdWxlcywgZnVuY3Rpb24odGZzKSB7CiAgdmFsaWRfdGZzIDwtIGludGVyc2VjdCh0ZnMsIG5hbWVzKGFsbF92YXJzKSkKICBpZihsZW5ndGgodmFsaWRfdGZzKSA+IDApIHsKICAgIG1lYW4oYWxsX3ZhcnNbdmFsaWRfdGZzXSwgbmEucm0gPSBUUlVFKQogIH0gZWxzZSB7CiAgICAwCiAgfQp9KQoKIyA0LiBDcmVhdGUgUGxvdCBEYXRhCnBsb3RfZGF0YSA8LSBkYXRhLmZyYW1lKAogIE1vZHVsZSA9IG5hbWVzKG1vZHVsZV92YXJpYW5jZSksCiAgVmFyaWFuY2UgPSBtb2R1bGVfdmFyaWFuY2UKKSAlPiUgYXJyYW5nZShkZXNjKFZhcmlhbmNlKSkKCiMgNS4gUGxvdApwX3ZhciA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeCA9IHJlb3JkZXIoTW9kdWxlLCBWYXJpYW5jZSksIHkgPSBWYXJpYW5jZSwgZmlsbCA9IE1vZHVsZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjcpICsKICBjb29yZF9mbGlwKCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiRHJpdmVycyBvZiBIZXRlcm9nZW5laXR5OiBWYXJpYW5jZSBFeHBsYWluZWQgYnkgVEYgTW9kdWxlcyIsCiAgICAgICBzdWJ0aXRsZSA9ICJDYWxjdWxhdGVkIG9uIHVuc2NhbGVkIFRGIGFjdGl2aXR5IHNjb3JlcyAoRG9Sb3RoRUEpIiwKICAgICAgIHkgPSAiTWVhbiBWYXJpYW5jZSIsIHggPSAiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpCgpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfTW9kdWxlX1ZhcmlhbmNlLnBkZiIsIHBfdmFyLCB3aWR0aD04LCBoZWlnaHQ9NikKcHJpbnQocF92YXIpCmNhdCgi4pyTIFN1cHBsZW1lbnRhcnkgVmFyaWFuY2UgUGxvdCBTYXZlZFxuIikKYGBgCgojIyBTVVBQTEVNRU5UQVJZIEZJR1VSRSAzOiBSZWd1bGF0b3J5IE1vZHVsZSBBY3Rpdml0eSBIZWF0bWFwCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmxpYnJhcnkoU2V1cmF0KQoKIyAxLiBEZWZpbmUgTW9kdWxlcyAoQWRkZWQgVHJlZykKbW9kdWxlcyA8LSBsaXN0KAogICJIb21lb3N0YXNpcyIgPSBjKCJGT1hPMSIsICJUQ0Y3IiwgIkxFRjEiKSwKICAiT25jb2dlbmljL0NlbGwgQ3ljbGUiID0gYygiTVlDIiwgIkUyRjEiLCAiRTJGNCIsICJGT1hNMSIpLAogICJJbmZsYW1tYXRpb24gKE5GLWtCKSIgPSBjKCJSRUxBIiwgIk5GS0IxIiwgIlJFTCIsICJTVEFUMyIpLAogICJUaDIgRGlmZmVyZW50aWF0aW9uIiA9IGMoIkdBVEEzIiwgIlNUQVQ2IiwgIk1BRiIpLAogICJUaDEgRGlmZmVyZW50aWF0aW9uIiA9IGMoIlRCWDIxIiwgIlNUQVQxIiwgIklSRjEiKSwKICAiRXhoYXVzdGlvbiIgPSBjKCJUT1giLCAiUFJETTEiLCAiQkFURiIpCikKCgojIDIuIEdldCBBdmVyYWdlIEFjdGl2aXR5IG9mIEFMTCBURnMgcGVyIENsdXN0ZXIgKHVzaW5nICdkYXRhJyBsYXllcikKIyBXZSB1c2UgQXZlcmFnZUV4cHJlc3Npb24gdG8gZ2V0IHRoZSBtZWFuIGFjdGl2aXR5IG9mIGV2ZXJ5IFRGIGluIGV2ZXJ5IGNsdXN0ZXIKY2x1c3Rlcl9hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24oc2V1cmF0X29iaiwgYXNzYXlzID0gImRvcm90aGVhIiwgbGF5ZXIgPSAiZGF0YSIpJGRvcm90aGVhCgojIDMuIEFnZ3JlZ2F0ZSBURnMgaW50byBNb2R1bGUgU2NvcmVzCiMgRm9yIGVhY2ggbW9kdWxlLCB3ZSB0YWtlIHRoZSBtZWFuIG9mIGl0cyBURnMnIGF2ZXJhZ2UgYWN0aXZpdHkKbW9kdWxlX21hdCA8LSBzYXBwbHkobmFtZXMobW9kdWxlcyksIGZ1bmN0aW9uKG1vZF9uYW1lKSB7CiAgdGZzIDwtIG1vZHVsZXNbW21vZF9uYW1lXV0KICB2YWxpZF90ZnMgPC0gaW50ZXJzZWN0KHRmcywgcm93bmFtZXMoY2x1c3Rlcl9hdmcpKQogIAogIGlmKGxlbmd0aCh2YWxpZF90ZnMpID4gMCkgewogICAgY29sTWVhbnMoY2x1c3Rlcl9hdmdbdmFsaWRfdGZzLCAsIGRyb3A9RkFMU0VdKQogIH0gZWxzZSB7CiAgICByZXAoMCwgbmNvbChjbHVzdGVyX2F2ZykpCiAgfQp9KQoKIyBSZXN1bHQgaXMgQ2x1c3RlcnMgeCBNb2R1bGVzLiBUcmFuc3Bvc2UgdG8gTW9kdWxlcyB4IENsdXN0ZXJzIGZvciBoZWF0bWFwCm1vZHVsZV9tYXQgPC0gdChtb2R1bGVfbWF0KQoKIyA0LiBTY2FsZSAoWi1zY29yZSkgcm93LXdpc2UgZm9yIHZpc3VhbGl6YXRpb24KIyBUaGlzIGhpZ2hsaWdodHMgcmVsYXRpdmUgZW5yaWNobWVudCAoUmVkID0gSGlnaCBmb3IgdGhhdCBtb2R1bGUsIEJsdWUgPSBMb3cpCm1vZHVsZV9tYXRfeiA8LSB0KHNjYWxlKHQobW9kdWxlX21hdCkpKQptb2R1bGVfbWF0X3pbaXMubmEobW9kdWxlX21hdF96KV0gPC0gMAoKIyA1LiBQbG90IEhlYXRtYXAKY29sX2Z1biA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKC0yLCAwLCAyKSwgYygiIzMxMzY5NSIsICJ3aGl0ZSIsICIjQTUwMDI2IikpCgpodF9tb2QgPC0gSGVhdG1hcChtb2R1bGVfbWF0X3osCiAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQWN0aXZpdHkgKHopIiwKICAgICAgICAgICAgICAgICAgY29sID0gY29sX2Z1biwKICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgIHJlY3RfZ3AgPSBncGFyKGNvbCA9ICJ3aGl0ZSIsIGx3ZCA9IDEpLCAjIEFkZCB3aGl0ZSBib3JkZXJzCiAgICAgICAgICAgICAgICAgIHJvd19uYW1lc19zaWRlID0gImxlZnQiLAogICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiUmVndWxhdG9yeSBNb2R1bGUgQWN0aXZpdHkgQWNyb3NzIENsdXN0ZXJzIiwKICAgICAgICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDExLCBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCkpCgojIERyYXcgYW5kIFNhdmUKZHJhdyhodF9tb2QpCgpwZGYoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfTW9kdWxlX0hlYXRtYXAucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTYpCmRyYXcoaHRfbW9kKQpkZXYub2ZmKCkKCnBuZygiT3V0cHV0X0ZpZ3VyZXMvU3VwcGxlbWVudGFyeV9URl9Nb2R1bGVfSGVhdG1hcC5wbmciLCB3aWR0aD04KjMwMCwgaGVpZ2h0PTYqMzAwLCByZXM9MzAwKQpkcmF3KGh0X21vZCkKZGV2Lm9mZigpCgpjYXQoIuKckyBTdXBwbGVtZW50YXJ5IE1vZHVsZSBIZWF0bWFwIFNhdmVkXG4iKQpgYGAKCiMjIFNVUFBMRU1FTlRBUlkgRklHVVJFIDQ6IFJlZ3VsYXRvcnkgTW9kdWxlIEFjdGl2aXR5IGJ5IENlbGwgTGluZQpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KFNldXJhdCkKCiMgMS4gRGVmaW5lIE1vZHVsZXMKbW9kdWxlcyA8LSBsaXN0KAogICJIb21lb3N0YXNpcyIgPSBjKCJGT1hPMSIsICJUQ0Y3IiwgIkxFRjEiKSwKICAiT25jb2dlbmljL0NlbGwgQ3ljbGUiID0gYygiTVlDIiwgIkUyRjEiLCAiRTJGNCIsICJGT1hNMSIpLAogICJJbmZsYW1tYXRpb24gKE5GLWtCKSIgPSBjKCJSRUxBIiwgIk5GS0IxIiwgIlJFTCIsICJTVEFUMyIpLAogICJUaDIgRGlmZmVyZW50aWF0aW9uIiA9IGMoIkdBVEEzIiwgIlNUQVQ2IiwgIk1BRiIpLAogICJUaDEgRGlmZmVyZW50aWF0aW9uIiA9IGMoIlRCWDIxIiwgIlNUQVQxIiwgIklSRjEiKSwKICAiRXhoYXVzdGlvbiIgPSBjKCJUT1giLCAiUFJETTEiLCAiQkFURiIpCikKCgojIDIuIEdldCBBdmVyYWdlIEFjdGl2aXR5IHBlciBDZWxsIExpbmUgKG9yaWcuaWRlbnQpCmNlbGxfbGluZV9hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24oc2V1cmF0X29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXlzID0gImRvcm90aGVhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXIgPSAiZGF0YSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKSRkb3JvdGhlYQoKIyAzLiBBZ2dyZWdhdGUgVEZzIGludG8gTW9kdWxlIFNjb3Jlcwptb2R1bGVfbWF0IDwtIHNhcHBseShuYW1lcyhtb2R1bGVzKSwgZnVuY3Rpb24obW9kX25hbWUpIHsKICB0ZnMgPC0gbW9kdWxlc1tbbW9kX25hbWVdXQogIHZhbGlkX3RmcyA8LSBpbnRlcnNlY3QodGZzLCByb3duYW1lcyhjZWxsX2xpbmVfYXZnKSkKICAKICBpZihsZW5ndGgodmFsaWRfdGZzKSA+IDApIHsKICAgIGNvbE1lYW5zKGNlbGxfbGluZV9hdmdbdmFsaWRfdGZzLCAsIGRyb3A9RkFMU0VdKQogIH0gZWxzZSB7CiAgICByZXAoMCwgbmNvbChjZWxsX2xpbmVfYXZnKSkKICB9Cn0pCgojIFRyYW5zcG9zZSB0byBbTW9kdWxlcyB4IENlbGwgTGluZXNdCm1vZHVsZV9tYXQgPC0gdChtb2R1bGVfbWF0KQoKIyA0LiBTY2FsZSAoWi1zY29yZSkgcm93LXdpc2UKbW9kdWxlX21hdF96IDwtIHQoc2NhbGUodChtb2R1bGVfbWF0KSkpCm1vZHVsZV9tYXRfeltpcy5uYShtb2R1bGVfbWF0X3opXSA8LSAwCgojIDUuIFBsb3QgSGVhdG1hcApjb2xfZnVuIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTIsIDAsIDIpLCBjKCIjMzEzNjk1IiwgIndoaXRlIiwgIiNBNTAwMjYiKSkKCmh0X21vZCA8LSBIZWF0bWFwKG1vZHVsZV9tYXRfeiwKICAgICAgICAgICAgICAgICAgbmFtZSA9ICJBY3Rpdml0eSAoeikiLAogICAgICAgICAgICAgICAgICBjb2wgPSBjb2xfZnVuLAogICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwgIyBHcm91cCBzaW1pbGFyIGNlbGwgbGluZXMgdG9nZXRoZXIKICAgICAgICAgICAgICAgICAgcmVjdF9ncCA9IGdwYXIoY29sID0gIndoaXRlIiwgbHdkID0gMSksIAogICAgICAgICAgICAgICAgICByb3dfbmFtZXNfc2lkZSA9ICJsZWZ0IiwKICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gIlJlZ3VsYXRvcnkgTGFuZHNjYXBlIEFjcm9zcyBDZWxsIExpbmVzIiwKICAgICAgICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDExLCBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCkpCgojIERyYXcgYW5kIFNhdmUKZHJhdyhodF9tb2QpCgpwZGYoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfTW9kdWxlX0hlYXRtYXBfQ2VsbExpbmUucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTYpCmRyYXcoaHRfbW9kKQpkZXYub2ZmKCkKCnBuZygiT3V0cHV0X0ZpZ3VyZXMvU3VwcGxlbWVudGFyeV9URl9Nb2R1bGVfSGVhdG1hcF9DZWxsTGluZS5wbmciLCB3aWR0aD04KjMwMCwgaGVpZ2h0PTYqMzAwLCByZXM9MzAwKQpkcmF3KGh0X21vZCkKZGV2Lm9mZigpCgpjYXQoIuKckyBTdXBwbGVtZW50YXJ5IENlbGwgTGluZSBIZWF0bWFwIFNhdmVkXG4iKQpgYGAKCiMjIFNVUFBMRU1FTlRBUlkgRklHVVJFIDQ6IFJlZ3VsYXRvcnkgTW9kdWxlIEFjdGl2aXR5IGJ5IENlbGwgTGluZQpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KFNldXJhdCkKCiMgMS4gRGVmaW5lIE1vZHVsZXMKbW9kdWxlcyA8LSBsaXN0KAogICJIb21lb3N0YXNpcyIgPSBjKCJGT1hPMSIsICJUQ0Y3IiwgIkxFRjEiKSwKICAiT25jb2dlbmljL0NlbGwgQ3ljbGUiID0gYygiTVlDIiwgIkUyRjEiLCAiRTJGNCIsICJGT1hNMSIpLAogICJJbmZsYW1tYXRpb24gKE5GLWtCKSIgPSBjKCJSRUxBIiwgIk5GS0IxIiwgIlJFTCIsICJTVEFUMyIpLAogICJUaDIgRGlmZmVyZW50aWF0aW9uIiA9IGMoIkdBVEEzIiwgIlNUQVQ2IiwgIk1BRiIpLAogICJUaDEgRGlmZmVyZW50aWF0aW9uIiA9IGMoIlRCWDIxIiwgIlNUQVQxIiwgIklSRjEiKSwKICAiRXhoYXVzdGlvbiIgPSBjKCJUT1giLCAiUFJETTEiLCAiQkFURiIpCikKCgojIDIuIEdldCBBdmVyYWdlIEFjdGl2aXR5IHBlciBDZWxsIExpbmUgKG9yaWcuaWRlbnQpCmNlbGxfbGluZV9hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24oc2V1cmF0X29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXlzID0gImRvcm90aGVhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXIgPSAiZGF0YSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIpJGRvcm90aGVhCgojIDMuIEFnZ3JlZ2F0ZSBURnMgaW50byBNb2R1bGUgU2NvcmVzCm1vZHVsZV9tYXQgPC0gc2FwcGx5KG5hbWVzKG1vZHVsZXMpLCBmdW5jdGlvbihtb2RfbmFtZSkgewogIHRmcyA8LSBtb2R1bGVzW1ttb2RfbmFtZV1dCiAgdmFsaWRfdGZzIDwtIGludGVyc2VjdCh0ZnMsIHJvd25hbWVzKGNlbGxfbGluZV9hdmcpKQogIAogIGlmKGxlbmd0aCh2YWxpZF90ZnMpID4gMCkgewogICAgY29sTWVhbnMoY2VsbF9saW5lX2F2Z1t2YWxpZF90ZnMsICwgZHJvcD1GQUxTRV0pCiAgfSBlbHNlIHsKICAgIHJlcCgwLCBuY29sKGNlbGxfbGluZV9hdmcpKQogIH0KfSkKCiMgVHJhbnNwb3NlIHRvIFtNb2R1bGVzIHggQ2VsbCBMaW5lc10KbW9kdWxlX21hdCA8LSB0KG1vZHVsZV9tYXQpCgojIDQuIFNjYWxlIChaLXNjb3JlKSByb3ctd2lzZQptb2R1bGVfbWF0X3ogPC0gdChzY2FsZSh0KG1vZHVsZV9tYXQpKSkKbW9kdWxlX21hdF96W2lzLm5hKG1vZHVsZV9tYXRfeildIDwtIDAKCiMgNS4gUGxvdCBIZWF0bWFwCmNvbF9mdW4gPC0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygtMiwgMCwgMiksIGMoIiMzMTM2OTUiLCAid2hpdGUiLCAiI0E1MDAyNiIpKQoKaHRfbW9kIDwtIEhlYXRtYXAobW9kdWxlX21hdF96LAogICAgICAgICAgICAgICAgICBuYW1lID0gIkFjdGl2aXR5ICh6KSIsCiAgICAgICAgICAgICAgICAgIGNvbCA9IGNvbF9mdW4sCiAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLCAjIEdyb3VwIHNpbWlsYXIgY2VsbCBsaW5lcyB0b2dldGhlcgogICAgICAgICAgICAgICAgICByZWN0X2dwID0gZ3Bhcihjb2wgPSAid2hpdGUiLCBsd2QgPSAxKSwgCiAgICAgICAgICAgICAgICAgIHJvd19uYW1lc19zaWRlID0gImxlZnQiLAogICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiUmVndWxhdG9yeSBMYW5kc2NhcGUgQWNyb3NzIENsdXN0ZXJzIiwKICAgICAgICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDExLCBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCkpCgojIERyYXcgYW5kIFNhdmUKZHJhdyhodF9tb2QpCgpwZGYoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfTW9kdWxlX0hlYXRtYXBfQ2VsbExpbmUucGRmIiwgd2lkdGg9OCwgaGVpZ2h0PTYpCmRyYXcoaHRfbW9kKQpkZXYub2ZmKCkKCnBuZygiT3V0cHV0X0ZpZ3VyZXMvU3VwcGxlbWVudGFyeV9URl9Nb2R1bGVfSGVhdG1hcF9DZWxsTGluZS5wbmciLCB3aWR0aD04KjMwMCwgaGVpZ2h0PTYqMzAwLCByZXM9MzAwKQpkcmF3KGh0X21vZCkKZGV2Lm9mZigpCgpjYXQoIuKckyBTdXBwbGVtZW50YXJ5IENlbGwgTGluZSBIZWF0bWFwIFNhdmVkXG4iKQpgYGAKCgoKIyMgTkVXIEZJR1VSRTogUmVndWxhdG9yeSBUcmFqZWN0b3J5IEFuYWx5c2lzIChQQ0Egb24gVEYgQWN0aXZpdHkpCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCgojIDEuIFJ1biBQQ0Egc3BlY2lmaWNhbGx5IG9uIFRGIEFjdGl2aXR5IChEb1JvdGhFQSkKRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKc2V1cmF0X29iaiA8LSBTY2FsZURhdGEoc2V1cmF0X29iaikKc2V1cmF0X29iaiA8LSBSdW5QQ0Eoc2V1cmF0X29iaiwgZmVhdHVyZXMgPSByb3duYW1lcyhzZXVyYXRfb2JqKSwgdmVyYm9zZSA9IEZBTFNFKQoKIyAyLiBFeHRyYWN0IFBDQSBFbWJlZGRpbmdzCnBjYV9kYXRhIDwtIEVtYmVkZGluZ3Moc2V1cmF0X29iaiwgcmVkdWN0aW9uID0gInBjYSIpWywgMToyXSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JSAKICBtdXRhdGUoQ2x1c3RlciA9IHNldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzLAogICAgICAgICBDb25kaXRpb24gPSBpZmVsc2Uoc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMgJWluJSBjKDMsIDEwKSwgIk5vbi1NYWxpZ25hbnQiLCAiTWFsaWduYW50IikpCgojIDMuIENhbGN1bGF0ZSBDbHVzdGVyIENlbnRyb2lkcyAoZm9yIGFycm93cykKY2VudHJvaWRzIDwtIHBjYV9kYXRhICU+JSAKICBncm91cF9ieShDbHVzdGVyKSAlPiUgCiAgc3VtbWFyaXNlKFBDMSA9IG1lYW4oUENfMSksIFBDMiA9IG1lYW4oUENfMikpCgojIDQuIERlZmluZSBLZXkgRHJpdmVycyBmb3IgUEMxIGFuZCBQQzIgKExvYWRpbmdzKQojIFRoaXMgdGVsbHMgdXMgV0hBVCBkcml2ZXMgdGhlIHRyYWplY3RvcnkgKGUuZy4sIFBDMSA9IE1hbGlnbmFuY3ksIFBDMiA9IFRoMSB2cyBUaDIpCmxvYWRpbmdzIDwtIExvYWRpbmdzKHNldXJhdF9vYmosIHJlZHVjdGlvbiA9ICJwY2EiKQpwYzFfZHJpdmVycyA8LSBuYW1lcyhzb3J0KGFicyhsb2FkaW5nc1ssIDFdKSwgZGVjcmVhc2luZyA9IFQpKVsxOjVdCnBjMl9kcml2ZXJzIDwtIG5hbWVzKHNvcnQoYWJzKGxvYWRpbmdzWywgMl0pLCBkZWNyZWFzaW5nID0gVCkpWzE6NV0KCmNhdCgiUEMxIERyaXZlcnMgKFgtYXhpcyk6IiwgcGFzdGUocGMxX2RyaXZlcnMsIGNvbGxhcHNlPSIsICIpLCAiXG4iKQpjYXQoIlBDMiBEcml2ZXJzIChZLWF4aXMpOiIsIHBhc3RlKHBjMl9kcml2ZXJzLCBjb2xsYXBzZT0iLCAiKSwgIlxuIikKCiMgNS4gUGxvdCB0aGUgVHJhamVjdG9yeQpwX3RyYWogPC0gZ2dwbG90KHBjYV9kYXRhLCBhZXMoeCA9IFBDXzEsIHkgPSBQQ18yLCBjb2xvciA9IENsdXN0ZXIpKSArCiAgIyBQb2ludHMKICBnZW9tX3BvaW50KGFscGhhID0gMC42LCBzaXplID0gMS41KSArCiAgCiAgIyBDZW50cm9pZHMgYW5kIExhYmVscwogIGdlb21fcG9pbnQoZGF0YSA9IGNlbnRyb2lkcywgYWVzKHggPSBQQzEsIHkgPSBQQzIpLCBzaXplID0gNSwgY29sb3IgPSAiYmxhY2siLCBzaGFwZSA9IDIxLCBmaWxsID0gIndoaXRlIikgKwogIGdlb21fdGV4dChkYXRhID0gY2VudHJvaWRzLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgbGFiZWwgPSBDbHVzdGVyKSwgY29sb3IgPSAiYmxhY2siLCBmb250ZmFjZSA9ICJib2xkIikgKwogIAogICMgU3R5bGluZwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBTZXVyYXQ6OkRpc2NyZXRlUGFsZXR0ZSgxNCkpICsKICBsYWJzKHRpdGxlID0gIlJlZ3VsYXRvcnkgVHJhamVjdG9yeSBvZiBTw6l6YXJ5IENlbGxzIiwKICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKCJQQzEgZHJpdmVuIGJ5OiAiLCBwYXN0ZShwYzFfZHJpdmVyc1sxOjNdLCBjb2xsYXBzZT0iLCAiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiXG5QQzIgZHJpdmVuIGJ5OiAiLCBwYXN0ZShwYzJfZHJpdmVyc1sxOjNdLCBjb2xsYXBzZT0iLCAiKSksCiAgICAgICB4ID0gIlBDMTogUmVndWxhdG9yeSBBeGlzIDEiLCAKICAgICAgIHkgPSAiUEMyOiBSZWd1bGF0b3J5IEF4aXMgMiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQoKIyBTYXZlCmdnc2F2ZSgiT3V0cHV0X0ZpZ3VyZXMvU3VwcGxlbWVudGFyeV9URl9UcmFqZWN0b3J5X1BDQS5wZGYiLCBwX3RyYWosIHdpZHRoID0gOCwgaGVpZ2h0ID0gNykKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9TdXBwbGVtZW50YXJ5X1RGX1RyYWplY3RvcnlfUENBLnBuZyIsIHBfdHJhaiwgd2lkdGggPSA4LCBoZWlnaHQgPSA3LCBkcGkgPSAzMDApCgpwcmludChwX3RyYWopCmNhdCgi4pyTIFJlZ3VsYXRvcnkgVHJhamVjdG9yeSBQbG90IFNhdmVkXG4iKQpgYGAKCgoKCgoKCgoKCgojIyBSRUZJTkVEIEZJR1VSRTogUmVndWxhdG9yeSBCaS1wbG90IChUcmFqZWN0b3J5ICsgVEYgRHJpdmVycykpCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dyZXBlbCkKCiMgMS4gUnVuIFBDQSBvbiBURiBBY3Rpdml0eQpEZWZhdWx0QXNzYXkoc2V1cmF0X29iaikgPC0gImRvcm90aGVhIgpzZXVyYXRfb2JqIDwtIFNjYWxlRGF0YShzZXVyYXRfb2JqKQpzZXVyYXRfb2JqIDwtIFJ1blBDQShzZXVyYXRfb2JqLCBmZWF0dXJlcyA9IHJvd25hbWVzKHNldXJhdF9vYmopLCB2ZXJib3NlID0gRkFMU0UpCgojIDIuIEV4dHJhY3QgQ2VsbCBFbWJlZGRpbmdzIChQb2ludHMpCnBjYV9kYXRhIDwtIEVtYmVkZGluZ3Moc2V1cmF0X29iaiwgcmVkdWN0aW9uID0gInBjYSIpWywgMToyXSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JSAKICBtdXRhdGUoQ2x1c3RlciA9IGFzLmZhY3RvcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykpCgojIDMuIEV4dHJhY3QgRmVhdHVyZSBMb2FkaW5ncyAoQXJyb3dzKQpsb2FkaW5ncyA8LSBMb2FkaW5ncyhzZXVyYXRfb2JqLCByZWR1Y3Rpb24gPSAicGNhIikKIyBTZWxlY3QgdG9wIDUgZHJpdmVycyBmb3IgUEMxIGFuZCBQQzIKdG9wX3BjMSA8LSBuYW1lcyhzb3J0KGFicyhsb2FkaW5nc1ssIDFdKSwgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjVdCnRvcF9wYzIgPC0gbmFtZXMoc29ydChhYnMobG9hZGluZ3NbLCAyXSksIGRlY3JlYXNpbmcgPSBUUlVFKSlbMTo1XQp0b3BfZHJpdmVycyA8LSB1bmlxdWUoYyh0b3BfcGMxLCB0b3BfcGMyKSkKCmFycm93X2RhdGEgPC0gYXMuZGF0YS5mcmFtZShsb2FkaW5nc1t0b3BfZHJpdmVycywgMToyXSkKYXJyb3dfZGF0YSRURiA8LSByb3duYW1lcyhhcnJvd19kYXRhKQoKIyBTY2FsZSBhcnJvd3MgdG8gbWF0Y2ggcGxvdCBkaW1lbnNpb25zIChzY2FsaW5nIGZhY3RvciBmb3IgdmlzaWJpbGl0eSkKc2NhbGVfZmFjdG9yIDwtIG1heChhYnMocGNhX2RhdGEkUENfMSkpIC8gbWF4KGFicyhhcnJvd19kYXRhJFBDXzEpKSAqIDAuOAphcnJvd19kYXRhJFBDXzEgPC0gYXJyb3dfZGF0YSRQQ18xICogc2NhbGVfZmFjdG9yCmFycm93X2RhdGEkUENfMiA8LSBhcnJvd19kYXRhJFBDXzIgKiBzY2FsZV9mYWN0b3IKCiMgNC4gUGxvdApwX2JpcGxvdCA8LSBnZ3Bsb3QocGNhX2RhdGEsIGFlcyh4ID0gUENfMSwgeSA9IFBDXzIpKSArCiAgIyBDZWxsIFBvaW50cwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gQ2x1c3RlciksIGFscGhhID0gMC41LCBzaXplID0gMS41KSArCiAgCiAgIyBURiBBcnJvd3MKICBnZW9tX3NlZ21lbnQoZGF0YSA9IGFycm93X2RhdGEsIGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBQQ18xLCB5ZW5kID0gUENfMiksCiAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJjbSIpKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjgpICsKICAKICAjIFRGIExhYmVscwogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gYXJyb3dfZGF0YSwgYWVzKHggPSBQQ18xLCB5ID0gUENfMiwgbGFiZWwgPSBURiksCiAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0LCBib3gucGFkZGluZyA9IDAuNSkgKwogIAogICMgU3R5bGluZwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBTZXVyYXQ6OkRpc2NyZXRlUGFsZXR0ZSgxNCkpICsKICBsYWJzKHRpdGxlID0gIlJlZ3VsYXRvcnkgTGFuZHNjYXBlIEJpLXBsb3QiLAogICAgICAgc3VidGl0bGUgPSAiQXJyb3dzIGluZGljYXRlIGRpcmVjdGlvbiBvZiBURiBhY3Rpdml0eSBkcml2aW5nIGhldGVyb2dlbmVpdHkiLAogICAgICAgeCA9ICJQQzE6IE1hbGlnbmFuY3kgQXhpcyAoQ2VsbCBDeWNsZSkiLCAKICAgICAgIHkgPSAiUEMyOiBJZGVudGl0eSBBeGlzIChJbmZsYW1tYXRpb24gdnMuIERpZmZlcmVudGlhdGlvbikiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKCiMgU2F2ZQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfQmlwbG90LnBkZiIsIHBfYmlwbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfQmlwbG90LnBuZyIsIHBfYmlwbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApCgpwcmludChwX2JpcGxvdCkKY2F0KCLinJMgQmktcGxvdCBTYXZlZC4gVGhpcyB2aXN1YWxpemVzIGV4YWN0bHkgV0hJQ0ggVEZzIHB1bGwgY2x1c3RlcnMgYXBhcnQuXG4iKQpgYGAKCiMjIFJFRklORUQgRklHVVJFOiBSZWd1bGF0b3J5IEJpLXBsb3QgKENsZWFuZWQpCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dyZXBlbCkKCiMgMS4gUnVuIFBDQSBvbiBURiBBY3Rpdml0eSAoaWYgbm90IGFscmVhZHkgZG9uZSkKRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKc2V1cmF0X29iaiA8LSBTY2FsZURhdGEoc2V1cmF0X29iaikKc2V1cmF0X29iaiA8LSBSdW5QQ0Eoc2V1cmF0X29iaiwgZmVhdHVyZXMgPSByb3duYW1lcyhzZXVyYXRfb2JqKSwgdmVyYm9zZSA9IEZBTFNFKQoKIyAyLiBFeHRyYWN0IENlbGwgRW1iZWRkaW5ncwpwY2FfZGF0YSA8LSBFbWJlZGRpbmdzKHNldXJhdF9vYmosIHJlZHVjdGlvbiA9ICJwY2EiKVssIDE6Ml0gJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgbXV0YXRlKENsdXN0ZXIgPSBhcy5mYWN0b3Ioc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMpKQoKIyAzLiBFeHRyYWN0IEZlYXR1cmUgTG9hZGluZ3MgKFRoZSAiQXJyb3dzIikKbG9hZGluZ3MgPC0gTG9hZGluZ3Moc2V1cmF0X29iaiwgcmVkdWN0aW9uID0gInBjYSIpCgojIC0tLSBGSUxURVJJTkcgU1RFUCAtLS0KIyBTZWxlY3Qgb25seSB0aGUgdG9wIDMgcG9zaXRpdmUgYW5kIHRvcCAzIG5lZ2F0aXZlIGRyaXZlcnMgZm9yIFBDMSBhbmQgUEMyCnBjMV90b3AgPC0gbmFtZXMoc29ydChsb2FkaW5nc1ssIDFdLCBkZWNyZWFzaW5nID0gVFJVRSkpWzE6M10KcGMxX2JvdHRvbSA8LSBuYW1lcyhzb3J0KGxvYWRpbmdzWywgMV0sIGRlY3JlYXNpbmcgPSBGQUxTRSkpWzE6M10KcGMyX3RvcCA8LSBuYW1lcyhzb3J0KGxvYWRpbmdzWywgMl0sIGRlY3JlYXNpbmcgPSBUUlVFKSlbMTozXQpwYzJfYm90dG9tIDwtIG5hbWVzKHNvcnQobG9hZGluZ3NbLCAyXSwgZGVjcmVhc2luZyA9IEZBTFNFKSlbMTozXQoKdG9wX2RyaXZlcnMgPC0gdW5pcXVlKGMocGMxX3RvcCwgcGMxX2JvdHRvbSwgcGMyX3RvcCwgcGMyX2JvdHRvbSkpCmFycm93X2RhdGEgPC0gYXMuZGF0YS5mcmFtZShsb2FkaW5nc1t0b3BfZHJpdmVycywgMToyXSkKYXJyb3dfZGF0YSRURiA8LSByb3duYW1lcyhhcnJvd19kYXRhKQoKIyBTY2FsZSBhcnJvd3MgdG8gYmUgdmlzaWJsZSBvbiB0aGUgcGxvdCAobXVsdGlwbHkgYnkgZmFjdG9yKQpzY2FsZV9mYWN0b3IgPC0gbWF4KGFicyhwY2FfZGF0YSRQQ18xKSkgLyBtYXgoYWJzKGFycm93X2RhdGEkUENfMSkpICogMC44CmFycm93X2RhdGEkUENfMSA8LSBhcnJvd19kYXRhJFBDXzEgKiBzY2FsZV9mYWN0b3IKYXJyb3dfZGF0YSRQQ18yIDwtIGFycm93X2RhdGEkUENfMiAqIHNjYWxlX2ZhY3RvcgoKIyA0LiBQbG90CnBfYmlwbG90IDwtIGdncGxvdChwY2FfZGF0YSwgYWVzKHggPSBQQ18xLCB5ID0gUENfMikpICsKICAjIFBvaW50cyAoQ2VsbHMpCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBDbHVzdGVyKSwgYWxwaGEgPSAwLjYsIHNpemUgPSAxLjUpICsKICAKICAjIEFycm93cyAoVEZzKQogIGdlb21fc2VnbWVudChkYXRhID0gYXJyb3dfZGF0YSwgYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IFBDXzEsIHllbmQgPSBQQ18yKSwKICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImNtIikpLCBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDAuOCkgKwogIAogICMgTGFiZWxzIChURnMpIC0gdXNpbmcgZ2dyZXBlbCB0byBhdm9pZCBvdmVybGFwCiAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBhcnJvd19kYXRhLCBhZXMoeCA9IFBDXzEsIHkgPSBQQ18yLCBsYWJlbCA9IFRGKSwKICAgICAgICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDQsIAogICAgICAgICAgICAgICAgICBib3gucGFkZGluZyA9IDAuNSwgcG9pbnQucGFkZGluZyA9IDAuMikgKwogIAogICMgU3R5bGluZwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBTZXVyYXQ6OkRpc2NyZXRlUGFsZXR0ZSgxNCkpICsKICBsYWJzKHRpdGxlID0gIlJlZ3VsYXRvcnkgRHJpdmVycyBvZiBIZXRlcm9nZW5laXR5IiwKICAgICAgIHN1YnRpdGxlID0gIkFycm93cyBpbmRpY2F0ZSBURnMgZHJpdmluZyBzZXBhcmF0aW9uIGFsb25nIFBDMSAoTWFsaWduYW5jeSkgYW5kIFBDMiAoSWRlbnRpdHkpIiwKICAgICAgIHggPSAiUEMxOiBNYWxpZ25hbmN5IEF4aXMiLCAKICAgICAgIHkgPSAiUEMyOiBJZGVudGl0eSBBeGlzIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCgojIFNhdmUKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9TdXBwbGVtZW50YXJ5X1RGX0JpcGxvdF9DbGVhbi5wZGYiLCBwX2JpcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkKcHJpbnQocF9iaXBsb3QpCmNhdCgi4pyTIENsZWFuIEJpLXBsb3QgU2F2ZWQuIFNob3dzIG9ubHkgdG9wIGRyaXZlcnMuXG4iKQpgYGAKCiMgU1VQUExFTUVOVEFSWSBGSUdVUkU6IFNsaW5nc2hvdCBUcmFqZWN0b3J5IEFuYWx5c2lzCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CiMgMS4gTG9hZCBMaWJyYXJpZXMKbGlicmFyeShzbGluZ3Nob3QpCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHNjYWxlcykKCiMgMi4gQ29udmVydCBTZXVyYXQgdG8gU2luZ2xlQ2VsbEV4cGVyaW1lbnQKIyBXZSB1c2UgdGhlICdkb3JvdGhlYScgYXNzYXkgZm9yIFRGLWJhc2VkIHRyYWplY3RvcnksIG9yICdTQ1QnIGZvciBnZW5lLWJhc2VkLgojIFRGLWJhc2VkIGlzIGJldHRlciBmb3IgInJlZ3VsYXRvcnkiIHRyYWplY3Rvcmllcy4Kc2NlIDwtIGFzLlNpbmdsZUNlbGxFeHBlcmltZW50KHNldXJhdF9vYmosIGFzc2F5ID0gImRvcm90aGVhIikKCiMgMy4gUnVuIFNsaW5nc2hvdAojIFdlIHNwZWNpZnkgdGhlIHJlZHVjdGlvbiAoVU1BUCkgYW5kIHRoZSBjbHVzdGVyIGxhYmVscwojICdzdGFydC5jbHVzJyA9IDUgc2V0cyB0aGUgcm9vdCBhdCB0aGUgU3RlbS1saWtlIGNsdXN0ZXIKc2NlIDwtIHNsaW5nc2hvdChzY2UsIGNsdXN0ZXJMYWJlbHMgPSBzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycywgCiAgICAgICAgICAgICAgICAgcmVkdWNlZERpbSA9ICdVTUFQJywgc3RhcnQuY2x1cyA9ICc1JykKCiMgNC4gRXh0cmFjdCBUcmFqZWN0b3J5IERhdGEgZm9yIFBsb3R0aW5nIGluIGdncGxvdDIKIyBHZXQgdGhlIGN1cnZlcyAobGluZWFnZXMpCnNsaW5nc2hvdF9jdXJ2ZXMgPC0gU2xpbmdzaG90RGF0YVNldChzY2UpCmN1cnZlX2Nvb3JkcyA8LSBkYXRhLmZyYW1lKCkKCmZvciAoaSBpbiBzZXFfYWxvbmcoc2xpbmdDdXJ2ZXMoc2xpbmdzaG90X2N1cnZlcykpKSB7CiAgYyA8LSBzbGluZ0N1cnZlcyhzbGluZ3Nob3RfY3VydmVzKVtbaV1dCiAgZGYgPC0gYXMuZGF0YS5mcmFtZShjJHNbYyRvcmQsIF0pCiAgZGYkTGluZWFnZSA8LSBwYXN0ZTAoIkxpbmVhZ2UiLCBpKQogIGN1cnZlX2Nvb3JkcyA8LSByYmluZChjdXJ2ZV9jb29yZHMsIGRmKQp9CgojIEdldCBjZWxsIGVtYmVkZGluZ3MKdW1hcF9kYXRhIDwtIGFzLmRhdGEuZnJhbWUoRW1iZWRkaW5ncyhzZXVyYXRfb2JqLCAidW1hcCIpKQp1bWFwX2RhdGEkQ2x1c3RlciA8LSBzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycwoKIyA1LiBQbG90CnBfc2xpbmdzaG90IDwtIGdncGxvdCh1bWFwX2RhdGEsIGFlcyh4ID0gdW1hcF8xLCB5ID0gdW1hcF8yKSkgKwogICMgQ2VsbCBwb2ludHMKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IENsdXN0ZXIpLCBzaXplID0gMC44LCBhbHBoYSA9IDAuNikgKwogIAogICMgVHJhamVjdG9yeSBwYXRocwogIGdlb21fcGF0aChkYXRhID0gY3VydmVfY29vcmRzLCBhZXMoeCA9IHVtYXBfMSwgeSA9IHVtYXBfMiwgZ3JvdXAgPSBMaW5lYWdlKSwgCiAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gMS4yLCBhcnJvdyA9IGFycm93KHR5cGUgPSAiY2xvc2VkIiwgbGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKSkpICsKICAKICAjIFN0eWxpbmcKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gU2V1cmF0OjpEaXNjcmV0ZVBhbGV0dGUoMTQpKSArCiAgbGFicyh0aXRsZSA9ICJSZWd1bGF0b3J5IFRyYWplY3RvcnkgSW5mZXJlbmNlIChTbGluZ3Nob3QpIiwKICAgICAgIHN1YnRpdGxlID0gIlJvb3Qgc2V0IHRvIENsdXN0ZXIgNSAoU3RlbS1saWtlIFN0YXRlKSIsCiAgICAgICB4ID0gIlVNQVAgMSIsIHkgPSAiVU1BUCAyIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpKQoKIyA2LiBTYXZlCmdnc2F2ZSgiT3V0cHV0X0ZpZ3VyZXMvU3VwcGxlbWVudGFyeV9TbGluZ3Nob3RfVHJhamVjdG9yeS5wZGYiLCBwX3NsaW5nc2hvdCwgd2lkdGggPSA4LCBoZWlnaHQgPSA3KQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfU2xpbmdzaG90X1RyYWplY3RvcnkucG5nIiwgcF9zbGluZ3Nob3QsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNywgZHBpID0gMzAwKQoKcHJpbnQocF9zbGluZ3Nob3QpCmNhdCgi4pyTIFNsaW5nc2hvdCBUcmFqZWN0b3J5IFNhdmVkLiBBcnJvd3MgaW5kaWNhdGUgZGV2ZWxvcG1lbnRhbCBmbG93IGZyb20gQ2x1c3RlciA1LlxuIikKYGBgCgoKIyBTVVBQTEVNRU5UQVJZIEZJR1VSRTogIE1vbm9jbGUzIChJZiB5b3UgcHJlZmVyIHBzZXVkb3RpbWUgY29sb3JpbmcpCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTZ9CmxpYnJhcnkobW9ub2NsZTMpCmxpYnJhcnkoU2V1cmF0V3JhcHBlcnMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKCiMgLS0tIDEuIERlZmluZSBIZWxwZXIgRnVuY3Rpb24gKFJlcXVpcmVkIGZvciBNb25vY2xlMykgLS0tCmdldF9lYXJsaWVzdF9wcmluY2lwYWxfbm9kZSA8LSBmdW5jdGlvbihjZHMsIHN0YXJ0X2NlbGxzLCByZWR1Y3Rpb25fbWV0aG9kID0gIlVNQVAiKXsKICAjIEdldCB0aGUgY2xvc2VzdCBwcmluY2lwYWwgbm9kZSB0byB0aGUgZ2VvbWV0cmljIGNlbnRlciBvZiBzdGFydF9jZWxscwogIGNlbGxfaWRzIDwtIGNvbG5hbWVzKGNkcykKICAKICBjbG9zZXN0X3ZlcnRleCA8LSBjZHNAcHJpbmNpcGFsX2dyYXBoX2F1eFtbcmVkdWN0aW9uX21ldGhvZF1dJHByX2dyYXBoX2NlbGxfcHJval9jbG9zZXN0X3ZlcnRleAogIGNsb3Nlc3RfdmVydGV4IDwtIGFzLm1hdHJpeChjbG9zZXN0X3ZlcnRleFtjb2xuYW1lcyhjZHMpLCBdKQogIAogIHJvb3RfcHJfbm9kZXMgPC0gaWdyYXBoOjpWKHByaW5jaXBhbF9ncmFwaChjZHMpW1tyZWR1Y3Rpb25fbWV0aG9kXV0pJG5hbWVbYXMubnVtZXJpYyhuYW1lcyh3aGljaC5tYXgodGFibGUoY2xvc2VzdF92ZXJ0ZXhbc3RhcnRfY2VsbHMsXSkpKSldCiAgCiAgcmV0dXJuKHJvb3RfcHJfbm9kZXMpCn0KCiMgLS0tIDIuIENvbnZlcnQgU2V1cmF0IHRvIENlbGxEYXRhU2V0IChDRFMpIC0tLQojIFdlIHVzZSB0aGUgJ2Rvcm90aGVhJyBhc3NheSBmb3IgVEYtYmFzZWQgdHJhamVjdG9yeSAob3IgJ1NDVCcgZm9yIGdlbmUtYmFzZWQpCmNkcyA8LSBhcy5jZWxsX2RhdGFfc2V0KHNldXJhdF9vYmopCgojIC0tLSAzLiBQcmVwcm9jZXNzICYgTGVhcm4gR3JhcGggLS0tCiMgTW9ub2NsZSBuZWVkcyB0byByZS1jbHVzdGVyIHRvIGxlYXJuIHRoZSBncmFwaCBzdHJ1Y3R1cmUKY2RzIDwtIGNsdXN0ZXJfY2VsbHMoY2RzLCByZWR1Y3Rpb25fbWV0aG9kID0gIlVNQVAiKQpjZHMgPC0gbGVhcm5fZ3JhcGgoY2RzLCB1c2VfcGFydGl0aW9uID0gVFJVRSwgdmVyYm9zZSA9IEZBTFNFKQoKIyAtLS0gNC4gT3JkZXIgQ2VsbHMgKFJvb3QgPSBDbHVzdGVyIDUpIC0tLQojIElkZW50aWZ5IGNlbGxzIGluIENsdXN0ZXIgNSAoU3RlbS1saWtlKQpzdGVtX2NlbGxzIDwtIGNvbG5hbWVzKHNldXJhdF9vYmopW3NldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzID09ICI1Il0KCiMgRmluZCB0aGUgcm9vdCBub2RlCmNsb3Nlc3Rfbm9kZSA8LSBnZXRfZWFybGllc3RfcHJpbmNpcGFsX25vZGUoY2RzLCBzdGFydF9jZWxscyA9IHN0ZW1fY2VsbHMpCgojIE9yZGVyIGNlbGxzIGJhc2VkIG9uIHBzZXVkb3RpbWUgc3RhcnRpbmcgZnJvbSB0aGF0IG5vZGUKY2RzIDwtIG9yZGVyX2NlbGxzKGNkcywgcm9vdF9wcl9ub2RlcyA9IGNsb3Nlc3Rfbm9kZSkKCiMgLS0tIDUuIFBsb3QgUHNldWRvdGltZSAtLS0KcF9tb25vY2xlIDwtIHBsb3RfY2VsbHMoY2RzLAogICAgICAgICAgIGNvbG9yX2NlbGxzX2J5ID0gInBzZXVkb3RpbWUiLAogICAgICAgICAgIGxhYmVsX2NlbGxfZ3JvdXBzID0gRkFMU0UsCiAgICAgICAgICAgbGFiZWxfbGVhdmVzID0gRkFMU0UsCiAgICAgICAgICAgbGFiZWxfYnJhbmNoX3BvaW50cyA9IEZBTFNFLAogICAgICAgICAgIGdyYXBoX2xhYmVsX3NpemUgPSAxLjUsCiAgICAgICAgICAgdHJhamVjdG9yeV9ncmFwaF9jb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgdHJhamVjdG9yeV9ncmFwaF9zZWdtZW50X3NpemUgPSAxLjApICsKICBsYWJzKHRpdGxlID0gIlJlZ3VsYXRvcnkgVHJhamVjdG9yeSAoUHNldWRvdGltZSkiLAogICAgICAgc3VidGl0bGUgPSAiUm9vdDogQ2x1c3RlciA1IChTdGVtLWxpa2UgU3RhdGUpIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSkKCiMgU2F2ZQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfTW9ub2NsZTNfUHNldWRvdGltZS5wZGYiLCBwX21vbm9jbGUsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNykKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9TdXBwbGVtZW50YXJ5X01vbm9jbGUzX1BzZXVkb3RpbWUucG5nIiwgcF9tb25vY2xlLCB3aWR0aCA9IDgsIGhlaWdodCA9IDcsIGRwaSA9IDMwMCkKCnByaW50KHBfbW9ub2NsZSkKY2F0KCLinJMgTW9ub2NsZTMgVHJhamVjdG9yeSBTYXZlZC4gQ2VsbHMgY29sb3JlZCBieSBwc2V1ZG90aW1lIChQdXJwbGUgPSBFYXJseSwgWWVsbG93ID0gTGF0ZSkuXG4iKQoKYGBgCgoKIyBTVVBQTEVNRU5UQVJZIEZJR1VSRTogIFIgQ29kZTogUEFHQS1saWtlIENvbm5lY3Rpdml0eSBHcmFwaCAoVEYgQWN0aXZpdHkpCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBTVVBQTEVNRU5UQVJZIEZJR1VSRTogUEFHQS1saWtlIENvbm5lY3Rpdml0eSBHcmFwaCAoVEYgQWN0aXZpdHkpCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZ2dyYXBoKSAjIE1ha2Ugc3VyZSBnZ3JhcGggaXMgaW5zdGFsbGVkOiBpbnN0YWxsLnBhY2thZ2VzKCJnZ3JhcGgiKQpsaWJyYXJ5KGRwbHlyKQoKIyAxLiBCdWlsZCBhIGstTmVhcmVzdCBOZWlnaGJvciAoS05OKSBHcmFwaCBvbiBURiBBY3Rpdml0eQojIFdlIHVzZSB0aGUgJ2Rvcm90aGVhJyBhc3NheSB0byBlbnN1cmUgY29ubmVjdGlvbnMgYXJlIGJhc2VkIG9uIFJFR1VMQVRJT04KRGVmYXVsdEFzc2F5KHNldXJhdF9vYmopIDwtICJkb3JvdGhlYSIKc2V1cmF0X29iaiA8LSBGaW5kTmVpZ2hib3JzKHNldXJhdF9vYmosIGZlYXR1cmVzID0gcm93bmFtZXMoc2V1cmF0X29iaiksIGsucGFyYW0gPSAyMCwgdmVyYm9zZSA9IEZBTFNFKQoKIyAyLiBDb25zdHJ1Y3QgdGhlIFBBR0EgR3JhcGggKENsdXN0ZXItdG8tQ2x1c3RlciBDb25uZWN0aXZpdHkpCiMgV2UgY2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgZWRnZXMgYmV0d2VlbiBjZWxscyBvZiBkaWZmZXJlbnQgY2x1c3RlcnMKIyBBY2Nlc3MgdGhlIGdyYXBoIGRpcmVjdGx5IHVzaW5nIGlncmFwaCBmdW5jdGlvbnMgKGFscmVhZHkgYXZhaWxhYmxlIHZpYSBTZXVyYXQpCmFkal9tYXQgPC0gc2V1cmF0X29iakBncmFwaHMkZG9yb3RoZWFfc25uCmNsdXN0ZXJzIDwtIHNldXJhdF9vYmokc2V1cmF0X2NsdXN0ZXJzCm5fY2x1c3RlcnMgPC0gbGVuZ3RoKGxldmVscyhjbHVzdGVycykpCgojIEluaXRpYWxpemUgY29ubmVjdGl2aXR5IG1hdHJpeApjb25uZWN0X21hdCA8LSBtYXRyaXgoMCwgbnJvdyA9IG5fY2x1c3RlcnMsIG5jb2wgPSBuX2NsdXN0ZXJzKQpyb3duYW1lcyhjb25uZWN0X21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQpjb2xuYW1lcyhjb25uZWN0X21hdCkgPC0gbGV2ZWxzKGNsdXN0ZXJzKQoKIyBGaWxsIG1hdHJpeCB3aXRoIGVkZ2Ugd2VpZ2h0cyAoc3VtIG9mIHNoYXJlZCBOTiBsaW5rcykKY2x1c3Rlcl9pbmRpY2VzIDwtIHNwbGl0KDE6bmNvbChzZXVyYXRfb2JqKSwgY2x1c3RlcnMpCgpmb3IgKGkgaW4gMTpuX2NsdXN0ZXJzKSB7CiAgZm9yIChqIGluIGk6bl9jbHVzdGVycykgewogICAgaWYgKGkgPT0gaikgbmV4dCAjIFNraXAgc2VsZi1sb29wcwogICAgCiAgICBjZWxsc19pIDwtIGNsdXN0ZXJfaW5kaWNlc1tbaV1dCiAgICBjZWxsc19qIDwtIGNsdXN0ZXJfaW5kaWNlc1tbal1dCiAgICAKICAgICMgQ2FsY3VsYXRlIHRvdGFsIHdlaWdodCBvZiBjb25uZWN0aW9ucyBiZXR3ZWVuIENsdXN0ZXIgaSBhbmQgQ2x1c3RlciBqCiAgICBzdWJfbWF0IDwtIGFkal9tYXRbY2VsbHNfaSwgY2VsbHNfal0KICAgIHdlaWdodCA8LSBzdW0oc3ViX21hdCkKICAgIAogICAgIyBOb3JtYWxpemUgYnkgY2x1c3RlciBzaXplIHRvIGF2b2lkIGJpYXMgdG93YXJkcyBsYXJnZSBjbHVzdGVycwogICAgbm9ybV93ZWlnaHQgPC0gd2VpZ2h0IC8gKGxlbmd0aChjZWxsc19pKSAqIGxlbmd0aChjZWxsc19qKSkKICAgIAogICAgY29ubmVjdF9tYXRbaSwgal0gPC0gbm9ybV93ZWlnaHQKICAgIGNvbm5lY3RfbWF0W2osIGldIDwtIG5vcm1fd2VpZ2h0CiAgfQp9CgojIDMuIENyZWF0ZSBJZ3JhcGggT2JqZWN0CiMgVGhyZXNob2xkIGVkZ2VzIHRvIHNob3cgb25seSBzdHJvbmcgY29ubmVjdGlvbnMgKHRvcCAyMCUpCnRocmVzaG9sZCA8LSBxdWFudGlsZShjb25uZWN0X21hdFtjb25uZWN0X21hdCA+IDBdLCAwLjgwKSAKY29ubmVjdF9tYXRbY29ubmVjdF9tYXQgPCB0aHJlc2hvbGRdIDwtIDAKCiMgVXNlIGlncmFwaDo6Z3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4IGV4cGxpY2l0bHkKZ3JhcGggPC0gaWdyYXBoOjpncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgoY29ubmVjdF9tYXQsIG1vZGUgPSAidW5kaXJlY3RlZCIsIHdlaWdodGVkID0gVFJVRSkKCiMgQWRkIE5vZGUgQXR0cmlidXRlcyAoU2l6ZSA9IE51bWJlciBvZiBDZWxscykKaWdyYXBoOjpWKGdyYXBoKSRzaXplIDwtIGFzLm51bWVyaWModGFibGUoY2x1c3RlcnMpKQppZ3JhcGg6OlYoZ3JhcGgpJGxhYmVsIDwtIGxldmVscyhjbHVzdGVycykKCiMgNC4gUGxvdCBQQUdBIEdyYXBoCnBfcGFnYSA8LSBnZ3JhcGgoZ3JhcGgsIGxheW91dCA9ICJmciIpICsgIyBGcnVjaHRlcm1hbi1SZWluZ29sZCBsYXlvdXQKICAjIEVkZ2VzIChUaGlja25lc3MgPSBDb25uZWN0aXZpdHkgU3RyZW5ndGgpCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gd2VpZ2h0KSwgY29sb3IgPSAiZ3JleTUwIiwgYWxwaGEgPSAwLjYpICsKICBzY2FsZV9lZGdlX3dpZHRoKHJhbmdlID0gYygwLjUsIDMpKSArCiAgCiAgIyBOb2RlcyAoQ2x1c3RlcnMpCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gc2l6ZSwgY29sb3IgPSBsYWJlbCkpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYyg1LCAxNSkpICsKICAKICAjIExhYmVscwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IGxhYmVsKSwgZm9udGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIndoaXRlIikgKwogIAogICMgU3R5bGluZwogICMgVXNlIHNjYWxlczo6aHVlX3BhbCgpIGZvciBjb2xvcnMgdG8gYXZvaWQgZnVuY3Rpb24gZXJyb3JzCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNjYWxlczo6aHVlX3BhbCgpKG5fY2x1c3RlcnMpKSArCiAgbGFicyh0aXRsZSA9ICJSZWd1bGF0b3J5IFBBR0EgR3JhcGggKFRGIENvbm5lY3Rpdml0eSkiLAogICAgICAgc3VidGl0bGUgPSAiTm9kZXMgPSBDbHVzdGVyczsgRWRnZXMgPSBSZWd1bGF0b3J5IFNpbWlsYXJpdHkgKERvUm90aEVBKSIsCiAgICAgICBjYXB0aW9uID0gIlRoaWNrIGxpbmVzIGluZGljYXRlIHN0cm9uZyByZWd1bGF0b3J5IHRyYW5zaXRpb25zIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBzaXplPTE0KSkKCiMgU2F2ZQpnZ3NhdmUoIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfVEZfUEFHQV9HcmFwaC5wZGYiLCBwX3BhZ2EsIHdpZHRoID0gOCwgaGVpZ2h0ID0gOCkKZ2dzYXZlKCJPdXRwdXRfRmlndXJlcy9TdXBwbGVtZW50YXJ5X1RGX1BBR0FfR3JhcGgucG5nIiwgcF9wYWdhLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKCnByaW50KHBfcGFnYSkKY2F0KCLinJMgUEFHQSBHcmFwaCBTYXZlZC4gTG9vayBmb3IgY29ubmVjdGlvbnMgZnJvbSBDbHVzdGVyIDUgKFN0ZW0pIHRvIDExLzQvNy5cbiIpCgpgYGAKCiMgU2F2ZQpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE2fQoKc2F2ZVJEUyhzZXVyYXRfb2JqLCAidGVtcF9zZXVyYXRfb2JqLnJkcyIpCgoKCgpgYGAK