load libraries
Download required
cisTarget motif databases
# Load Bioconductor motif rankings (replaces feather files)
data(hg19_500bpUpstream_motifRanking_cispbOnly)
motifRankings <- hg19_500bpUpstream_motifRanking_cispbOnly
# Load motif annotations (motif -> TF mapping)
data(hg19_motifAnnotation_cisbpOnly)
motifAnnotations <- hg19_motifAnnotation_cisbpOnly
cat("Motif database loaded successfully\n")
Load Seurat Object
All_samples_Merged <- readRDS("/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/0-Seurat_RDS_Final_OBJECT/All_samples_Merged_with_Renamed_Clusters_Cell_state-03-12-2025.rds.rds")
DefaultAssay(All_samples_Merged) <- "RNA"
Idents(All_samples_Merged) <- "seurat_clusters"
cat("Seurat object loaded:", ncol(All_samples_Merged), "cells,", nrow(All_samples_Merged), "genes\n")
Step 1: Gene
filtering
# SCENIC-style gene filtering
minCountsPerGene <- 3 * 0.01 * ncol(expr_mat)
genesLeft <- rownames(expr_mat)[rowSums(expr_mat > 0) >= minCountsPerGene]
cat("Genes before filtering:", nrow(expr_mat), "\n")
cat("Genes after filtering:", length(genesLeft), "\n")
expr_mat_filtered <- expr_mat[genesLeft, ]
expr_mat_filtered_log <- log2(expr_mat_filtered + 1)
Step 2: Co-expression
network with GENIE3
# Run GENIE3 (this may take 30-60 minutes for large datasets)
cat("Running GENIE3 co-expression network inference...\n")
cat("This may take 30-60 minutes. Please wait...\n")
set.seed(123)
weightMatrix <- GENIE3(
as.matrix(expr_mat_filtered_log),
nCores = 10,
verbose = TRUE
)
# Convert to link list
linkList <- getLinkList(weightMatrix)
colnames(linkList) <- c("TF", "Target", "weight")
# Filter for meaningful links
linkList <- linkList[linkList$weight > 0.001, ]
cat("GENIE3 complete:", nrow(linkList), "TF-target links identified\n")
Step 3: Create TF
modules and run motif enrichment
# Create TF modules (co-expression clusters)
tfModules <- list()
for (tf in unique(linkList$TF)) {
targets <- linkList$Target[linkList$TF == tf]
if (length(targets) >= 10) { # minimum 10 targets per TF
tfModules[[tf]] <- targets
}
}
cat("TF modules created:", length(tfModules), "\n")
# Motif enrichment using Bioconductor rankings
cat("Running motif enrichment analysis...\n")
motif_enrichment_results <- cisTarget(
tfModules,
motifRankings = motifRankings,
motifAnnot = motifAnnotations,
nesThreshold = 3.0,
geneErrThreshold = 0.01,
nCores = 10
)
# Extract regulons (TF + high-confidence targets)
regulons <- list()
for (tf in names(tfModules)) {
if (tf %in% names(motif_enrichment_results)) {
# Use motif-enriched targets
regulons[[tf]] <- motif_enrichment_results[[tf]]
} else {
# Keep top co-expressed targets if no motif enrichment
regulons[[tf]] <- head(tfModules[[tf]], 50)
}
}
cat("Regulons created:", length(regulons), "\n")
Step 4: Score cells
with AUCell
# Build cell rankings
cat("Building cell rankings for AUCell...\n")
cells_rankings <- AUCell_buildRankings(
as.matrix(expr_mat_log),
nCores = 10,
plotStats = FALSE
)
# Calculate AUC per regulon per cell
cat("Calculating regulon activity (AUC) per cell...\n")
cells_AUC <- AUCell_calcAUC(
regulons,
cells_rankings,
nCores = 10
)
# Extract AUC matrix (regulons x cells)
regulonAUC <- getAUC(cells_AUC)
cat("AUCell complete:", nrow(regulonAUC), "regulons scored across", ncol(regulonAUC), "cells\n")
Add regulon activity to
Seurat object
# Add as new assay
All_samples_Merged[["Regulons"]] <- CreateAssayObject(
counts = regulonAUC,
data = regulonAUC
)
# Store regulon definitions
All_samples_Merged@misc$SCENIC_Regulons <- regulons
# Set as default assay
DefaultAssay(All_samples_Merged) <- "Regulons"
cat("Regulon activity added to Seurat object\n")
Define key Sézary
syndrome TFs
# Comprehensive list of Sézary-relevant TFs from literature + your heatmap
sezary_tfs <- c(
# Core malignant drivers
"MYC", "JUNB", "IRF4", "BCL11B", "RUNX3",
# Th2/cytokine signaling
"GATA3", "STAT3", "STAT6", "STAT5A", "STAT5B",
# T-cell identity & stemness
"TCF7", "LEF1", "TOX", "BCL6",
# Exhaustion & evasion
"NR4A1", "FOXP3", "IKZF2", "BATF",
# Th1/cytotoxic
"TBX21", "EOMES", "STAT4", "IRF8",
# Migration & homing
"KLF2", "KLF10", "ZEB1",
# Additional SS-specific
"AIRE", "CCND2", "BCL2"
)
# Check which TFs are available in your regulons
existing_tfs <- intersect(sezary_tfs, rownames(All_samples_Merged[["Regulons"]]))
cat("Available TFs in regulons:", length(existing_tfs), "/", length(sezary_tfs), "\n")
cat("TFs found:", paste(head(existing_tfs, 10), collapse = ", "), "...\n")
Visualization: Violin
plots
# Violin plots for key TFs (up to 16 at a time)
n_plot <- min(16, length(existing_tfs))
VlnPlot(
All_samples_Merged,
features = existing_tfs[1:n_plot],
ncol = 4,
pt.size = 0
) +
plot_annotation(title = "Regulon Activity by Cluster (Top Sézary TFs)")
Visualization: UMAP
FeaturePlots
# FeaturePlot for top 12 TFs
top_12_tfs <- head(existing_tfs, 12)
FeaturePlot(
All_samples_Merged,
features = top_12_tfs,
ncol = 3,
pt.size = 0.1,
cols = c("lightgrey", "red")
) +
plot_annotation(title = "Regulon Activity on UMAP")
Visualization: Heatmap
of regulon activity by cluster
# Aggregate regulon activity by cluster
cluster_regulons <- AggregateExpression(
All_samples_Merged,
assays = "Regulons",
group.by = "seurat_clusters",
return.seurat = FALSE
)
# Select available TFs
plot_tfs <- intersect(existing_tfs, rownames(cluster_regulons$Regulons))
cluster_regulons_subset <- cluster_regulons$Regulons[plot_tfs, ]
# Scale regulons (z-score across clusters)
cluster_scaled <- t(scale(t(cluster_regulons_subset)))
# Heatmap
pheatmap(
cluster_scaled,
cluster_rows = TRUE,
cluster_cols = FALSE,
breaks = seq(-3, 3, length.out = 101),
color = viridis(100),
main = "Sézary syndrome regulons by cluster (z-scored)",
fontsize_row = 7,
fontsize_col = 9,
angle_col = 45
)
Visualization:
DoHeatmap for top 15 regulons
# Get top 15 regulons by max cluster mean
cluster_means <- AggregateExpression(
All_samples_Merged,
assays = "Regulons",
group.by = "seurat_clusters"
)$Regulons
top_15 <- names(sort(apply(cluster_means, 1, max), decreasing = TRUE)[1:15])
DoHeatmap(
All_samples_Merged,
features = top_15,
group.by = "seurat_clusters"
) +
NoLegend() +
ggtitle("Top 15 Regulons by Maximum Cluster Activity")
Differential regulon
activity analysis
# Check your cluster IDs first
cat("Available clusters:\n")
print(table(Idents(All_samples_Merged)))
# Clusters 3 and 10 are non-malignant, rest (0-13) are malignant
All_samples_Merged$malignancy_group <- ifelse(
Idents(All_samples_Merged) %in% c("3", "10"), # Non-malignant clusters
"Non_Malignant",
"Malignant"
)
# Verify assignments
cat("\nMalignancy group distribution:\n")
print(table(All_samples_Merged$malignancy_group, Idents(All_samples_Merged)))
# Set identity to malignancy group
Idents(All_samples_Merged) <- "malignancy_group"
# Find differential regulons
de_regulons <- FindMarkers(
All_samples_Merged,
assay = "Regulons",
ident.1 = "Malignant",
ident.2 = "Non_Malignant",
test.use = "wilcox",
min.pct = 0.1,
logfc.threshold = 0.1
)
# Add gene names as column
de_regulons$gene <- rownames(de_regulons)
# Show top 20 differential regulons
cat("\nTop 20 differential regulons:\n")
top_de <- head(de_regulons[order(de_regulons$p_val_adj), ], 20)
print(top_de[, c("gene", "avg_log2FC", "p_val_adj", "pct.1", "pct.2")])
# Volcano plot
ggplot(de_regulons, aes(x = avg_log2FC, y = -log10(p_val_adj))) +
geom_point(aes(color = p_val_adj < 0.05 & abs(avg_log2FC) > 0.25), size = 2, alpha = 0.6) +
scale_color_manual(values = c("TRUE" = "red", "FALSE" = "gray50"), name = "Significant") +
geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "red", alpha = 0.5) +
geom_vline(xintercept = c(-0.25, 0.25), linetype = "dashed", color = "blue", alpha = 0.5) +
geom_text_repel(
data = head(de_regulons[order(de_regulons$p_val_adj), ], 10),
aes(label = gene),
size = 3,
max.overlaps = 10
) +
theme_minimal() +
theme(legend.position = "top") +
labs(
title = "Differential Regulon Activity: Malignant vs Non-Malignant",
subtitle = "Malignant: clusters 0,1,2,4,5,6,7,8,9,11,12,13 | Non-malignant: clusters 3,10",
x = "Log2 Fold Change (Regulon AUC)",
y = "-log10(Adjusted P-value)"
)
# Reset identity to clusters
Idents(All_samples_Merged) <- "seurat_clusters"
Top regulon per
cluster
# Find top regulon per cluster
top_per_cluster <- data.frame()
for (cluster in unique(Idents(All_samples_Merged))) {
cluster_cells <- WhichCells(All_samples_Merged, idents = cluster)
cluster_auc <- regulonAUC[, cluster_cells, drop = FALSE]
# Calculate mean AUC per regulon in this cluster
mean_auc <- rowMeans(cluster_auc)
top_tf <- names(which.max(mean_auc))
top_auc <- max(mean_auc)
top_per_cluster <- rbind(
top_per_cluster,
data.frame(
cluster = cluster,
top_regulon = top_tf,
mean_AUC = round(top_auc, 3)
)
)
}
cat("\nTop regulon per cluster:\n")
print(top_per_cluster)
Save all results
# Save Seurat object with regulons
saveRDS(All_samples_Merged, "All_samples_Merged_with_Regulons_SCENIC.rds")
cat("Seurat object saved: All_samples_Merged_with_Regulons_SCENIC.rds\n")
# Export regulon activity matrix
write.csv(as.data.frame(regulonAUC), "Regulon_AUC_matrix.csv")
cat("AUC matrix saved: Regulon_AUC_matrix.csv\n")
# Export regulon definitions (TF -> target genes)
regulon_df <- data.frame(
TF = rep(names(regulons), sapply(regulons, length)),
Target = unlist(regulons),
row.names = NULL
)
write.csv(regulon_df, "Regulon_TF_Target_List.csv", row.names = FALSE)
cat("Regulon definitions saved: Regulon_TF_Target_List.csv\n")
# Export differential regulon analysis
if (exists("de_regulons")) {
write.csv(de_regulons, "Differential_Regulons_Malignant_vs_NonMalignant.csv")
cat("Differential analysis saved: Differential_Regulons_Malignant_vs_NonMalignant.csv\n")
}
# Export top regulon per cluster
write.csv(top_per_cluster, "Top_Regulon_Per_Cluster.csv", row.names = FALSE)
cat("Top regulons per cluster saved: Top_Regulon_Per_Cluster.csv\n")
cat("\n=== SCENIC Analysis Complete ===\n")
cat("Total regulons identified:", nrow(regulonAUC), "\n")
cat("Cells analyzed:", ncol(regulonAUC), "\n")
cat("Key Sézary TFs available:", length(existing_tfs), "\n")
LS0tCnRpdGxlOiAiR1JOIEFuYWx5c2lzIChTQ0VOSUMpIgphdXRob3I6ICJOYXNpciBNYWhtb29kIEFiYmFzaSIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgdGhlbWU6IGpvdXJuYWwKLS0tCgoKIyBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBMb2FkIGJlbG93IGxpYnJhcmllcwpsaWJyYXJ5KFNDRU5JQykKbGlicmFyeShBVUNlbGwpCmxpYnJhcnkoUmNpc1RhcmdldCkKbGlicmFyeShSY2lzVGFyZ2V0LmhnMTkubW90aWZEQnMuY2lzYnBPbmx5LjUwMGJwKQpsaWJyYXJ5KEdFTklFMykKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKSAgIyBBZGRlZCBmb3Igdm9sY2FubyBwbG90IGxhYmVscwpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh2aXJpZGlzKQpgYGAKCiMgRG93bmxvYWQgcmVxdWlyZWQgY2lzVGFyZ2V0IG1vdGlmIGRhdGFiYXNlcwpgYGB7cn0KIyBMb2FkIEJpb2NvbmR1Y3RvciBtb3RpZiByYW5raW5ncyAocmVwbGFjZXMgZmVhdGhlciBmaWxlcykKZGF0YShoZzE5XzUwMGJwVXBzdHJlYW1fbW90aWZSYW5raW5nX2Npc3BiT25seSkKbW90aWZSYW5raW5ncyA8LSBoZzE5XzUwMGJwVXBzdHJlYW1fbW90aWZSYW5raW5nX2Npc3BiT25seQoKIyBMb2FkIG1vdGlmIGFubm90YXRpb25zIChtb3RpZiAtPiBURiBtYXBwaW5nKQpkYXRhKGhnMTlfbW90aWZBbm5vdGF0aW9uX2Npc2JwT25seSkKbW90aWZBbm5vdGF0aW9ucyA8LSBoZzE5X21vdGlmQW5ub3RhdGlvbl9jaXNicE9ubHkKCmNhdCgiTW90aWYgZGF0YWJhc2UgbG9hZGVkIHN1Y2Nlc3NmdWxseVxuIikKYGBgCgojIExvYWQgU2V1cmF0IE9iamVjdCAKYGBge3J9CgpBbGxfc2FtcGxlc19NZXJnZWQgPC0gcmVhZFJEUygiL2hvbWUvYmlvaW5mby8xLVRoZXNpc19GaW5hbF9ZZWFyXzIwMjUvMjAyNS1ZZWFyM19BbmFseXNpcy8xLXNjUk5BX1JFU1VMVFMtMTktMTEtMjAyNS8wLVNldXJhdF9SRFNfRmluYWxfT0JKRUNUL0FsbF9zYW1wbGVzX01lcmdlZF93aXRoX1JlbmFtZWRfQ2x1c3RlcnNfQ2VsbF9zdGF0ZS0wMy0xMi0yMDI1LnJkcy5yZHMiKQoKRGVmYXVsdEFzc2F5KEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gIlJOQSIKSWRlbnRzKEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gInNldXJhdF9jbHVzdGVycyIKCmNhdCgiU2V1cmF0IG9iamVjdCBsb2FkZWQ6IiwgbmNvbChBbGxfc2FtcGxlc19NZXJnZWQpLCAiY2VsbHMsIiwgbnJvdyhBbGxfc2FtcGxlc19NZXJnZWQpLCAiZ2VuZXNcbiIpICAKYGBgCgojIEV4dHJhY3QgZXhwcmVzc2lvbiBtYXRyaXggCmBgYHtyfQoKIyBTZXVyYXQgdjU6IEV4dHJhY3QgY291bnRzIHVzaW5nIExheWVyRGF0YQpleHByX21hdCA8LSBMYXllckRhdGEoQWxsX3NhbXBsZXNfTWVyZ2VkW1siUk5BIl1dLCBsYXllciA9ICJjb3VudHMiKQpkaW0oZXhwcl9tYXQpCgojIExvZy10cmFuc2Zvcm0KZXhwcl9tYXRfbG9nIDwtIGxvZzIoZXhwcl9tYXQgKyAxKQoKY2F0KCJFeHByZXNzaW9uIG1hdHJpeCBleHRyYWN0ZWQ6IiwgbnJvdyhleHByX21hdCksICJnZW5lcywiLCBuY29sKGV4cHJfbWF0KSwgImNlbGxzXG4iKQoKYGBgCgojIFN0ZXAgMTogR2VuZSBmaWx0ZXJpbmcKYGBge3J9CgojIFNDRU5JQy1zdHlsZSBnZW5lIGZpbHRlcmluZwptaW5Db3VudHNQZXJHZW5lIDwtIDMgKiAwLjAxICogbmNvbChleHByX21hdCkKZ2VuZXNMZWZ0IDwtIHJvd25hbWVzKGV4cHJfbWF0KVtyb3dTdW1zKGV4cHJfbWF0ID4gMCkgPj0gbWluQ291bnRzUGVyR2VuZV0KCmNhdCgiR2VuZXMgYmVmb3JlIGZpbHRlcmluZzoiLCBucm93KGV4cHJfbWF0KSwgIlxuIikKY2F0KCJHZW5lcyBhZnRlciBmaWx0ZXJpbmc6IiwgbGVuZ3RoKGdlbmVzTGVmdCksICJcbiIpCgpleHByX21hdF9maWx0ZXJlZCA8LSBleHByX21hdFtnZW5lc0xlZnQsIF0KZXhwcl9tYXRfZmlsdGVyZWRfbG9nIDwtIGxvZzIoZXhwcl9tYXRfZmlsdGVyZWQgKyAxKQoKCmBgYAoKIyBTdGVwIDI6IENvLWV4cHJlc3Npb24gbmV0d29yayB3aXRoIEdFTklFMwpgYGB7cn0KIyBSdW4gR0VOSUUzICh0aGlzIG1heSB0YWtlIDMwLTYwIG1pbnV0ZXMgZm9yIGxhcmdlIGRhdGFzZXRzKQpjYXQoIlJ1bm5pbmcgR0VOSUUzIGNvLWV4cHJlc3Npb24gbmV0d29yayBpbmZlcmVuY2UuLi5cbiIpCmNhdCgiVGhpcyBtYXkgdGFrZSAzMC02MCBtaW51dGVzLiBQbGVhc2Ugd2FpdC4uLlxuIikKCnNldC5zZWVkKDEyMykKd2VpZ2h0TWF0cml4IDwtIEdFTklFMygKICBhcy5tYXRyaXgoZXhwcl9tYXRfZmlsdGVyZWRfbG9nKSwKICBuQ29yZXMgPSAxMCwKICB2ZXJib3NlID0gVFJVRQopCgojIENvbnZlcnQgdG8gbGluayBsaXN0CmxpbmtMaXN0IDwtIGdldExpbmtMaXN0KHdlaWdodE1hdHJpeCkKY29sbmFtZXMobGlua0xpc3QpIDwtIGMoIlRGIiwgIlRhcmdldCIsICJ3ZWlnaHQiKQoKIyBGaWx0ZXIgZm9yIG1lYW5pbmdmdWwgbGlua3MKbGlua0xpc3QgPC0gbGlua0xpc3RbbGlua0xpc3Qkd2VpZ2h0ID4gMC4wMDEsIF0KCmNhdCgiR0VOSUUzIGNvbXBsZXRlOiIsIG5yb3cobGlua0xpc3QpLCAiVEYtdGFyZ2V0IGxpbmtzIGlkZW50aWZpZWRcbiIpCgpgYGAKCiMgU3RlcCAzOiBDcmVhdGUgVEYgbW9kdWxlcyBhbmQgcnVuIG1vdGlmIGVucmljaG1lbnQKYGBge3J9CgojIENyZWF0ZSBURiBtb2R1bGVzIChjby1leHByZXNzaW9uIGNsdXN0ZXJzKQp0Zk1vZHVsZXMgPC0gbGlzdCgpCmZvciAodGYgaW4gdW5pcXVlKGxpbmtMaXN0JFRGKSkgewogIHRhcmdldHMgPC0gbGlua0xpc3QkVGFyZ2V0W2xpbmtMaXN0JFRGID09IHRmXQogIGlmIChsZW5ndGgodGFyZ2V0cykgPj0gMTApIHsgICMgbWluaW11bSAxMCB0YXJnZXRzIHBlciBURgogICAgdGZNb2R1bGVzW1t0Zl1dIDwtIHRhcmdldHMKICB9Cn0KCmNhdCgiVEYgbW9kdWxlcyBjcmVhdGVkOiIsIGxlbmd0aCh0Zk1vZHVsZXMpLCAiXG4iKQoKIyBNb3RpZiBlbnJpY2htZW50IHVzaW5nIEJpb2NvbmR1Y3RvciByYW5raW5ncwpjYXQoIlJ1bm5pbmcgbW90aWYgZW5yaWNobWVudCBhbmFseXNpcy4uLlxuIikKCm1vdGlmX2VucmljaG1lbnRfcmVzdWx0cyA8LSBjaXNUYXJnZXQoCiAgdGZNb2R1bGVzLAogIG1vdGlmUmFua2luZ3MgPSBtb3RpZlJhbmtpbmdzLAogIG1vdGlmQW5ub3QgPSBtb3RpZkFubm90YXRpb25zLAogIG5lc1RocmVzaG9sZCA9IDMuMCwKICBnZW5lRXJyVGhyZXNob2xkID0gMC4wMSwKICBuQ29yZXMgPSAxMAopCgojIEV4dHJhY3QgcmVndWxvbnMgKFRGICsgaGlnaC1jb25maWRlbmNlIHRhcmdldHMpCnJlZ3Vsb25zIDwtIGxpc3QoKQpmb3IgKHRmIGluIG5hbWVzKHRmTW9kdWxlcykpIHsKICBpZiAodGYgJWluJSBuYW1lcyhtb3RpZl9lbnJpY2htZW50X3Jlc3VsdHMpKSB7CiAgICAjIFVzZSBtb3RpZi1lbnJpY2hlZCB0YXJnZXRzCiAgICByZWd1bG9uc1tbdGZdXSA8LSBtb3RpZl9lbnJpY2htZW50X3Jlc3VsdHNbW3RmXV0KICB9IGVsc2UgewogICAgIyBLZWVwIHRvcCBjby1leHByZXNzZWQgdGFyZ2V0cyBpZiBubyBtb3RpZiBlbnJpY2htZW50CiAgICByZWd1bG9uc1tbdGZdXSA8LSBoZWFkKHRmTW9kdWxlc1tbdGZdXSwgNTApCiAgfQp9CgpjYXQoIlJlZ3Vsb25zIGNyZWF0ZWQ6IiwgbGVuZ3RoKHJlZ3Vsb25zKSwgIlxuIikKCmBgYAoKIyBTdGVwIDQ6IFNjb3JlIGNlbGxzIHdpdGggQVVDZWxsCmBgYHtyfQoKIyBCdWlsZCBjZWxsIHJhbmtpbmdzCmNhdCgiQnVpbGRpbmcgY2VsbCByYW5raW5ncyBmb3IgQVVDZWxsLi4uXG4iKQpjZWxsc19yYW5raW5ncyA8LSBBVUNlbGxfYnVpbGRSYW5raW5ncygKICBhcy5tYXRyaXgoZXhwcl9tYXRfbG9nKSwKICBuQ29yZXMgPSAxMCwKICBwbG90U3RhdHMgPSBGQUxTRQopCgojIENhbGN1bGF0ZSBBVUMgcGVyIHJlZ3Vsb24gcGVyIGNlbGwKY2F0KCJDYWxjdWxhdGluZyByZWd1bG9uIGFjdGl2aXR5IChBVUMpIHBlciBjZWxsLi4uXG4iKQpjZWxsc19BVUMgPC0gQVVDZWxsX2NhbGNBVUMoCiAgcmVndWxvbnMsCiAgY2VsbHNfcmFua2luZ3MsCiAgbkNvcmVzID0gMTAKKQoKIyBFeHRyYWN0IEFVQyBtYXRyaXggKHJlZ3Vsb25zIHggY2VsbHMpCnJlZ3Vsb25BVUMgPC0gZ2V0QVVDKGNlbGxzX0FVQykKCmNhdCgiQVVDZWxsIGNvbXBsZXRlOiIsIG5yb3cocmVndWxvbkFVQyksICJyZWd1bG9ucyBzY29yZWQgYWNyb3NzIiwgbmNvbChyZWd1bG9uQVVDKSwgImNlbGxzXG4iKQoKYGBgCgoKIyBBZGQgcmVndWxvbiBhY3Rpdml0eSB0byBTZXVyYXQgb2JqZWN0CmBgYHtyfQoKCgojIEFkZCBhcyBuZXcgYXNzYXkKQWxsX3NhbXBsZXNfTWVyZ2VkW1siUmVndWxvbnMiXV0gPC0gQ3JlYXRlQXNzYXlPYmplY3QoCiAgY291bnRzID0gcmVndWxvbkFVQywKICBkYXRhID0gcmVndWxvbkFVQwopCgojIFN0b3JlIHJlZ3Vsb24gZGVmaW5pdGlvbnMKQWxsX3NhbXBsZXNfTWVyZ2VkQG1pc2MkU0NFTklDX1JlZ3Vsb25zIDwtIHJlZ3Vsb25zCgojIFNldCBhcyBkZWZhdWx0IGFzc2F5CkRlZmF1bHRBc3NheShBbGxfc2FtcGxlc19NZXJnZWQpIDwtICJSZWd1bG9ucyIKCmNhdCgiUmVndWxvbiBhY3Rpdml0eSBhZGRlZCB0byBTZXVyYXQgb2JqZWN0XG4iKSAgCgoKYGBgCgoKIyBEZWZpbmUga2V5IFPDqXphcnkgc3luZHJvbWUgVEZzCmBgYHtyfQoKIyBDb21wcmVoZW5zaXZlIGxpc3Qgb2YgU8OpemFyeS1yZWxldmFudCBURnMgZnJvbSBsaXRlcmF0dXJlICsgeW91ciBoZWF0bWFwCnNlemFyeV90ZnMgPC0gYygKICAjIENvcmUgbWFsaWduYW50IGRyaXZlcnMKICAiTVlDIiwgIkpVTkIiLCAiSVJGNCIsICJCQ0wxMUIiLCAiUlVOWDMiLCAKICAKICAjIFRoMi9jeXRva2luZSBzaWduYWxpbmcKICAiR0FUQTMiLCAiU1RBVDMiLCAiU1RBVDYiLCAiU1RBVDVBIiwgIlNUQVQ1QiIsCiAgCiAgIyBULWNlbGwgaWRlbnRpdHkgJiBzdGVtbmVzcwogICJUQ0Y3IiwgIkxFRjEiLCAiVE9YIiwgIkJDTDYiLAogIAogICMgRXhoYXVzdGlvbiAmIGV2YXNpb24KICAiTlI0QTEiLCAiRk9YUDMiLCAiSUtaRjIiLCAiQkFURiIsCiAgCiAgIyBUaDEvY3l0b3RveGljCiAgIlRCWDIxIiwgIkVPTUVTIiwgIlNUQVQ0IiwgIklSRjgiLAogIAogICMgTWlncmF0aW9uICYgaG9taW5nCiAgIktMRjIiLCAiS0xGMTAiLCAiWkVCMSIsCiAgCiAgIyBBZGRpdGlvbmFsIFNTLXNwZWNpZmljCiAgIkFJUkUiLCAiQ0NORDIiLCAiQkNMMiIKKQoKIyBDaGVjayB3aGljaCBURnMgYXJlIGF2YWlsYWJsZSBpbiB5b3VyIHJlZ3Vsb25zCmV4aXN0aW5nX3RmcyA8LSBpbnRlcnNlY3Qoc2V6YXJ5X3Rmcywgcm93bmFtZXMoQWxsX3NhbXBsZXNfTWVyZ2VkW1siUmVndWxvbnMiXV0pKQoKY2F0KCJBdmFpbGFibGUgVEZzIGluIHJlZ3Vsb25zOiIsIGxlbmd0aChleGlzdGluZ190ZnMpLCAiLyIsIGxlbmd0aChzZXphcnlfdGZzKSwgIlxuIikKY2F0KCJURnMgZm91bmQ6IiwgcGFzdGUoaGVhZChleGlzdGluZ190ZnMsIDEwKSwgY29sbGFwc2UgPSAiLCAiKSwgIi4uLlxuIikKCgpgYGAKCgojIFZpc3VhbGl6YXRpb246IFZpb2xpbiBwbG90cwpgYGB7cn0KCiMgVmlvbGluIHBsb3RzIGZvciBrZXkgVEZzICh1cCB0byAxNiBhdCBhIHRpbWUpCm5fcGxvdCA8LSBtaW4oMTYsIGxlbmd0aChleGlzdGluZ190ZnMpKQoKVmxuUGxvdCgKICBBbGxfc2FtcGxlc19NZXJnZWQsIAogIGZlYXR1cmVzID0gZXhpc3RpbmdfdGZzWzE6bl9wbG90XSwgCiAgbmNvbCA9IDQsIAogIHB0LnNpemUgPSAwCikgKyAKICBwbG90X2Fubm90YXRpb24odGl0bGUgPSAiUmVndWxvbiBBY3Rpdml0eSBieSBDbHVzdGVyIChUb3AgU8OpemFyeSBURnMpIikKCmBgYAoKIyBWaXN1YWxpemF0aW9uOiBVTUFQIEZlYXR1cmVQbG90cwpgYGB7cn0KIyBGZWF0dXJlUGxvdCBmb3IgdG9wIDEyIFRGcwp0b3BfMTJfdGZzIDwtIGhlYWQoZXhpc3RpbmdfdGZzLCAxMikKCkZlYXR1cmVQbG90KAogIEFsbF9zYW1wbGVzX01lcmdlZCwgCiAgZmVhdHVyZXMgPSB0b3BfMTJfdGZzLCAKICBuY29sID0gMywgCiAgcHQuc2l6ZSA9IDAuMSwKICBjb2xzID0gYygibGlnaHRncmV5IiwgInJlZCIpCikgKyAKICBwbG90X2Fubm90YXRpb24odGl0bGUgPSAiUmVndWxvbiBBY3Rpdml0eSBvbiBVTUFQIikKYGBgCgojIFZpc3VhbGl6YXRpb246IEhlYXRtYXAgb2YgcmVndWxvbiBhY3Rpdml0eSBieSBjbHVzdGVyCmBgYHtyfQoKIyBBZ2dyZWdhdGUgcmVndWxvbiBhY3Rpdml0eSBieSBjbHVzdGVyCmNsdXN0ZXJfcmVndWxvbnMgPC0gQWdncmVnYXRlRXhwcmVzc2lvbigKICBBbGxfc2FtcGxlc19NZXJnZWQsCiAgYXNzYXlzID0gIlJlZ3Vsb25zIiwKICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogIHJldHVybi5zZXVyYXQgPSBGQUxTRQopCgojIFNlbGVjdCBhdmFpbGFibGUgVEZzCnBsb3RfdGZzIDwtIGludGVyc2VjdChleGlzdGluZ190ZnMsIHJvd25hbWVzKGNsdXN0ZXJfcmVndWxvbnMkUmVndWxvbnMpKQpjbHVzdGVyX3JlZ3Vsb25zX3N1YnNldCA8LSBjbHVzdGVyX3JlZ3Vsb25zJFJlZ3Vsb25zW3Bsb3RfdGZzLCBdCgojIFNjYWxlIHJlZ3Vsb25zICh6LXNjb3JlIGFjcm9zcyBjbHVzdGVycykKY2x1c3Rlcl9zY2FsZWQgPC0gdChzY2FsZSh0KGNsdXN0ZXJfcmVndWxvbnNfc3Vic2V0KSkpCgojIEhlYXRtYXAKcGhlYXRtYXAoCiAgY2x1c3Rlcl9zY2FsZWQsCiAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICBjbHVzdGVyX2NvbHMgPSBGQUxTRSwKICBicmVha3MgPSBzZXEoLTMsIDMsIGxlbmd0aC5vdXQgPSAxMDEpLAogIGNvbG9yID0gdmlyaWRpcygxMDApLAogIG1haW4gPSAiU8OpemFyeSBzeW5kcm9tZSByZWd1bG9ucyBieSBjbHVzdGVyICh6LXNjb3JlZCkiLAogIGZvbnRzaXplX3JvdyA9IDcsCiAgZm9udHNpemVfY29sID0gOSwKICBhbmdsZV9jb2wgPSA0NQopCgpgYGAKCgojIFZpc3VhbGl6YXRpb246IERvSGVhdG1hcCBmb3IgdG9wIDE1IHJlZ3Vsb25zCmBgYHtyfQojIEdldCB0b3AgMTUgcmVndWxvbnMgYnkgbWF4IGNsdXN0ZXIgbWVhbgpjbHVzdGVyX21lYW5zIDwtIEFnZ3JlZ2F0ZUV4cHJlc3Npb24oCiAgQWxsX3NhbXBsZXNfTWVyZ2VkLCAKICBhc3NheXMgPSAiUmVndWxvbnMiLCAKICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiCikkUmVndWxvbnMKCnRvcF8xNSA8LSBuYW1lcyhzb3J0KGFwcGx5KGNsdXN0ZXJfbWVhbnMsIDEsIG1heCksIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjE1XSkKCkRvSGVhdG1hcCgKICBBbGxfc2FtcGxlc19NZXJnZWQsIAogIGZlYXR1cmVzID0gdG9wXzE1LCAKICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiCikgKyAKICBOb0xlZ2VuZCgpICsKICBnZ3RpdGxlKCJUb3AgMTUgUmVndWxvbnMgYnkgTWF4aW11bSBDbHVzdGVyIEFjdGl2aXR5IikKCmBgYAoKCiMgRGlmZmVyZW50aWFsIHJlZ3Vsb24gYWN0aXZpdHkgYW5hbHlzaXMKYGBge3J9CgojIENoZWNrIHlvdXIgY2x1c3RlciBJRHMgZmlyc3QKY2F0KCJBdmFpbGFibGUgY2x1c3RlcnM6XG4iKQpwcmludCh0YWJsZShJZGVudHMoQWxsX3NhbXBsZXNfTWVyZ2VkKSkpCgojIENsdXN0ZXJzIDMgYW5kIDEwIGFyZSBub24tbWFsaWduYW50LCByZXN0ICgwLTEzKSBhcmUgbWFsaWduYW50CkFsbF9zYW1wbGVzX01lcmdlZCRtYWxpZ25hbmN5X2dyb3VwIDwtIGlmZWxzZSgKICBJZGVudHMoQWxsX3NhbXBsZXNfTWVyZ2VkKSAlaW4lIGMoIjMiLCAiMTAiKSwgICMgTm9uLW1hbGlnbmFudCBjbHVzdGVycwogICJOb25fTWFsaWduYW50IiwgCiAgIk1hbGlnbmFudCIKKQoKIyBWZXJpZnkgYXNzaWdubWVudHMKY2F0KCJcbk1hbGlnbmFuY3kgZ3JvdXAgZGlzdHJpYnV0aW9uOlxuIikKcHJpbnQodGFibGUoQWxsX3NhbXBsZXNfTWVyZ2VkJG1hbGlnbmFuY3lfZ3JvdXAsIElkZW50cyhBbGxfc2FtcGxlc19NZXJnZWQpKSkKCiMgU2V0IGlkZW50aXR5IHRvIG1hbGlnbmFuY3kgZ3JvdXAKSWRlbnRzKEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gIm1hbGlnbmFuY3lfZ3JvdXAiCgojIEZpbmQgZGlmZmVyZW50aWFsIHJlZ3Vsb25zCmRlX3JlZ3Vsb25zIDwtIEZpbmRNYXJrZXJzKAogIEFsbF9zYW1wbGVzX01lcmdlZCwKICBhc3NheSA9ICJSZWd1bG9ucyIsCiAgaWRlbnQuMSA9ICJNYWxpZ25hbnQiLAogIGlkZW50LjIgPSAiTm9uX01hbGlnbmFudCIsCiAgdGVzdC51c2UgPSAid2lsY294IiwKICBtaW4ucGN0ID0gMC4xLAogIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMQopCgojIEFkZCBnZW5lIG5hbWVzIGFzIGNvbHVtbgpkZV9yZWd1bG9ucyRnZW5lIDwtIHJvd25hbWVzKGRlX3JlZ3Vsb25zKQoKIyBTaG93IHRvcCAyMCBkaWZmZXJlbnRpYWwgcmVndWxvbnMKY2F0KCJcblRvcCAyMCBkaWZmZXJlbnRpYWwgcmVndWxvbnM6XG4iKQp0b3BfZGUgPC0gaGVhZChkZV9yZWd1bG9uc1tvcmRlcihkZV9yZWd1bG9ucyRwX3ZhbF9hZGopLCBdLCAyMCkKcHJpbnQodG9wX2RlWywgYygiZ2VuZSIsICJhdmdfbG9nMkZDIiwgInBfdmFsX2FkaiIsICJwY3QuMSIsICJwY3QuMiIpXSkKCiMgVm9sY2FubyBwbG90CmdncGxvdChkZV9yZWd1bG9ucywgYWVzKHggPSBhdmdfbG9nMkZDLCB5ID0gLWxvZzEwKHBfdmFsX2FkaikpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBwX3ZhbF9hZGogPCAwLjA1ICYgYWJzKGF2Z19sb2cyRkMpID4gMC4yNSksIHNpemUgPSAyLCBhbHBoYSA9IDAuNikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJUUlVFIiA9ICJyZWQiLCAiRkFMU0UiID0gImdyYXk1MCIpLCBuYW1lID0gIlNpZ25pZmljYW50IikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIiwgYWxwaGEgPSAwLjUpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKC0wLjI1LCAwLjI1KSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArCiAgZ2VvbV90ZXh0X3JlcGVsKAogICAgZGF0YSA9IGhlYWQoZGVfcmVndWxvbnNbb3JkZXIoZGVfcmVndWxvbnMkcF92YWxfYWRqKSwgXSwgMTApLAogICAgYWVzKGxhYmVsID0gZ2VuZSksCiAgICBzaXplID0gMywKICAgIG1heC5vdmVybGFwcyA9IDEwCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJEaWZmZXJlbnRpYWwgUmVndWxvbiBBY3Rpdml0eTogTWFsaWduYW50IHZzIE5vbi1NYWxpZ25hbnQiLAogICAgc3VidGl0bGUgPSAiTWFsaWduYW50OiBjbHVzdGVycyAwLDEsMiw0LDUsNiw3LDgsOSwxMSwxMiwxMyB8IE5vbi1tYWxpZ25hbnQ6IGNsdXN0ZXJzIDMsMTAiLAogICAgeCA9ICJMb2cyIEZvbGQgQ2hhbmdlIChSZWd1bG9uIEFVQykiLCAKICAgIHkgPSAiLWxvZzEwKEFkanVzdGVkIFAtdmFsdWUpIgogICkKCiMgUmVzZXQgaWRlbnRpdHkgdG8gY2x1c3RlcnMKSWRlbnRzKEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gInNldXJhdF9jbHVzdGVycyIKCmBgYAoKIyBUb3AgcmVndWxvbiBwZXIgY2x1c3RlcgpgYGB7cn0KIyBGaW5kIHRvcCByZWd1bG9uIHBlciBjbHVzdGVyCnRvcF9wZXJfY2x1c3RlciA8LSBkYXRhLmZyYW1lKCkKCmZvciAoY2x1c3RlciBpbiB1bmlxdWUoSWRlbnRzKEFsbF9zYW1wbGVzX01lcmdlZCkpKSB7CiAgY2x1c3Rlcl9jZWxscyA8LSBXaGljaENlbGxzKEFsbF9zYW1wbGVzX01lcmdlZCwgaWRlbnRzID0gY2x1c3RlcikKICBjbHVzdGVyX2F1YyA8LSByZWd1bG9uQVVDWywgY2x1c3Rlcl9jZWxscywgZHJvcCA9IEZBTFNFXQogIAogICMgQ2FsY3VsYXRlIG1lYW4gQVVDIHBlciByZWd1bG9uIGluIHRoaXMgY2x1c3RlcgogIG1lYW5fYXVjIDwtIHJvd01lYW5zKGNsdXN0ZXJfYXVjKQogIHRvcF90ZiA8LSBuYW1lcyh3aGljaC5tYXgobWVhbl9hdWMpKQogIHRvcF9hdWMgPC0gbWF4KG1lYW5fYXVjKQogIAogIHRvcF9wZXJfY2x1c3RlciA8LSByYmluZCgKICAgIHRvcF9wZXJfY2x1c3RlciwgCiAgICBkYXRhLmZyYW1lKAogICAgICBjbHVzdGVyID0gY2x1c3RlciwgCiAgICAgIHRvcF9yZWd1bG9uID0gdG9wX3RmLAogICAgICBtZWFuX0FVQyA9IHJvdW5kKHRvcF9hdWMsIDMpCiAgICApCiAgKQp9CgpjYXQoIlxuVG9wIHJlZ3Vsb24gcGVyIGNsdXN0ZXI6XG4iKQpwcmludCh0b3BfcGVyX2NsdXN0ZXIpCgpgYGAKCiMgTXVsdGktcGFuZWwgcHVibGljYXRpb24gZmlndXJlCmBgYHtyfQojIFBhbmVsIDE6IFRvcCA2IFRGcyBGZWF0dXJlUGxvdApwMSA8LSBGZWF0dXJlUGxvdCgKICBBbGxfc2FtcGxlc19NZXJnZWQsIAogIGZlYXR1cmVzID0gaGVhZChleGlzdGluZ190ZnMsIDYpLCAKICBuY29sID0gMiwgCiAgcHQuc2l6ZSA9IDAuMQopICsgCiAgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gIkEuIFJlZ3Vsb24gQWN0aXZpdHkgb24gVU1BUCIpCgojIFBhbmVsIDI6IFRvcCA2IFRGcyB2aW9saW4gYnkgY2x1c3RlcgpwMiA8LSBWbG5QbG90KAogIEFsbF9zYW1wbGVzX01lcmdlZCwgCiAgZmVhdHVyZXMgPSBoZWFkKGV4aXN0aW5nX3RmcywgNiksIAogIG5jb2wgPSAyLCAKICBwdC5zaXplID0gMAopICsgCiAgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gIkIuIFJlZ3Vsb24gQWN0aXZpdHkgYnkgQ2x1c3RlciIpCgojIFBhbmVsIDM6IEhlYXRtYXAgb2YgdG9wIDE1IHJlZ3Vsb25zCnAzIDwtIERvSGVhdG1hcCgKICBBbGxfc2FtcGxlc19NZXJnZWQsIAogIGZlYXR1cmVzID0gdG9wXzE1LCAKICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiCikgKyAKICBOb0xlZ2VuZCgpICsKICBnZ3RpdGxlKCJDLiBUb3AgMTUgUmVndWxvbnMgSGVhdG1hcCIpCgojIENvbWJpbmUgcGFuZWxzCmNvbWJpbmVkX3Bsb3QgPC0gcDEgLyBwMiAvIHAzCnByaW50KGNvbWJpbmVkX3Bsb3QpCgojIFNhdmUgZmlndXJlCmdnc2F2ZSgKICAiU2V6YXJ5X1NDRU5JQ19GaWd1cmUucGRmIiwgCiAgcGxvdCA9IGNvbWJpbmVkX3Bsb3QsIAogIHdpZHRoID0gMTYsIAogIGhlaWdodCA9IDIyLAogIGxpbWl0c2l6ZSA9IEZBTFNFCikKCmNhdCgiXG5GaWd1cmUgc2F2ZWQgYXM6IFNlemFyeV9TQ0VOSUNfRmlndXJlLnBkZlxuIikKCmBgYAoKIyBTYXZlIGFsbCByZXN1bHRzCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0KIyBTYXZlIFNldXJhdCBvYmplY3Qgd2l0aCByZWd1bG9ucwpzYXZlUkRTKEFsbF9zYW1wbGVzX01lcmdlZCwgIkFsbF9zYW1wbGVzX01lcmdlZF93aXRoX1JlZ3Vsb25zX1NDRU5JQy5yZHMiKQpjYXQoIlNldXJhdCBvYmplY3Qgc2F2ZWQ6IEFsbF9zYW1wbGVzX01lcmdlZF93aXRoX1JlZ3Vsb25zX1NDRU5JQy5yZHNcbiIpCgojIEV4cG9ydCByZWd1bG9uIGFjdGl2aXR5IG1hdHJpeAp3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShyZWd1bG9uQVVDKSwgIlJlZ3Vsb25fQVVDX21hdHJpeC5jc3YiKQpjYXQoIkFVQyBtYXRyaXggc2F2ZWQ6IFJlZ3Vsb25fQVVDX21hdHJpeC5jc3ZcbiIpCgojIEV4cG9ydCByZWd1bG9uIGRlZmluaXRpb25zIChURiAtPiB0YXJnZXQgZ2VuZXMpCnJlZ3Vsb25fZGYgPC0gZGF0YS5mcmFtZSgKICBURiA9IHJlcChuYW1lcyhyZWd1bG9ucyksIHNhcHBseShyZWd1bG9ucywgbGVuZ3RoKSksCiAgVGFyZ2V0ID0gdW5saXN0KHJlZ3Vsb25zKSwKICByb3cubmFtZXMgPSBOVUxMCikKd3JpdGUuY3N2KHJlZ3Vsb25fZGYsICJSZWd1bG9uX1RGX1RhcmdldF9MaXN0LmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpjYXQoIlJlZ3Vsb24gZGVmaW5pdGlvbnMgc2F2ZWQ6IFJlZ3Vsb25fVEZfVGFyZ2V0X0xpc3QuY3N2XG4iKQoKIyBFeHBvcnQgZGlmZmVyZW50aWFsIHJlZ3Vsb24gYW5hbHlzaXMKaWYgKGV4aXN0cygiZGVfcmVndWxvbnMiKSkgewogIHdyaXRlLmNzdihkZV9yZWd1bG9ucywgIkRpZmZlcmVudGlhbF9SZWd1bG9uc19NYWxpZ25hbnRfdnNfTm9uTWFsaWduYW50LmNzdiIpCiAgY2F0KCJEaWZmZXJlbnRpYWwgYW5hbHlzaXMgc2F2ZWQ6IERpZmZlcmVudGlhbF9SZWd1bG9uc19NYWxpZ25hbnRfdnNfTm9uTWFsaWduYW50LmNzdlxuIikKfQoKIyBFeHBvcnQgdG9wIHJlZ3Vsb24gcGVyIGNsdXN0ZXIKd3JpdGUuY3N2KHRvcF9wZXJfY2x1c3RlciwgIlRvcF9SZWd1bG9uX1Blcl9DbHVzdGVyLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpjYXQoIlRvcCByZWd1bG9ucyBwZXIgY2x1c3RlciBzYXZlZDogVG9wX1JlZ3Vsb25fUGVyX0NsdXN0ZXIuY3N2XG4iKQoKY2F0KCJcbj09PSBTQ0VOSUMgQW5hbHlzaXMgQ29tcGxldGUgPT09XG4iKQpjYXQoIlRvdGFsIHJlZ3Vsb25zIGlkZW50aWZpZWQ6IiwgbnJvdyhyZWd1bG9uQVVDKSwgIlxuIikKY2F0KCJDZWxscyBhbmFseXplZDoiLCBuY29sKHJlZ3Vsb25BVUMpLCAiXG4iKQpjYXQoIktleSBTw6l6YXJ5IFRGcyBhdmFpbGFibGU6IiwgbGVuZ3RoKGV4aXN0aW5nX3RmcyksICJcbiIpCgpgYGAKCgoK