load libraries
Load Object & Set
RNA Assay
Define State-Defining
TFs (Confirmed from Heatmap Panel C)
# orig.ident is set automatically during individual Seurat object creation
# It stores the sample name: L1, L2, L3, L4, L5, L6, L7, PBMC, PBMC-10x
seurat_L1 <- subset(seurat_obj, subset = orig.ident == "L1")
cat("Number of L1 cells:", ncol(seurat_L1), "\n")
Number of L1 cells: 5825
# Sanity check — should show only L1
table(seurat_L1$orig.ident)
L1 L2 L3 L4 L5 L6 L7 CD4T_lab CD4T_10x
5825 0 0 0 0 0 0 0 0
Compute Mean Activity
Score per State per Cell
score_df <- as.data.frame(
sapply(state_tfs, function(tfs) {
tfs_found <- tfs[tfs %in% rownames(tf_mat)]
if (length(tfs_found) == 0) {
warning("No TFs found for this state — returning NA")
return(rep(NA_real_, ncol(tf_mat)))
}
colMeans(tf_mat[tfs_found, , drop = FALSE])
})
)
rownames(score_df) <- colnames(tf_mat)
# Preview raw scores
head(round(score_df, 3))
Scale Scores Across
States
score_df_scaled <- as.data.frame(scale(score_df))
rownames(score_df_scaled) <- rownames(score_df)
# Preview scaled scores
head(round(score_df_scaled, 3))
Assign Dominant State
per Cell
score_df_scaled$DominantState <- apply(
score_df_scaled[, names(state_tfs), drop = FALSE], 1,
function(x) {
if (all(is.na(x))) return(NA_character_)
names(which.max(x))
}
)
seurat_L1$TF_State <- factor(
score_df_scaled$DominantState,
levels = names(state_tfs)
)
Check UMAP is
Present
cat("\nReductions available:", paste(Reductions(seurat_L1), collapse = ", "), "\n")
Reductions available: integrated_dr, ref.umap, pca, umap, harmony
# Only run if UMAP is absent from the subset
if (!"umap" %in% Reductions(seurat_L1)) {
cat("UMAP not found — recomputing from Harmony embeddings...\n")
seurat_L1 <- RunUMAP(seurat_L1,
reduction = "harmony",
dims = 1:20,
seed.use = 42)
}
Panel A — UMAP Colored
by TF State
p_umap <- DimPlot(
seurat_L1,
reduction = "umap",
group.by = "TF_State",
cols = state_colors,
pt.size = 1.0,
label = FALSE,
na.value = "grey85"
) +
ggtitle("Cell Line L1 — TF Regulatory State") +
theme_classic(base_size = 11) +
theme(
plot.title = element_text(face = "bold", size = 11, hjust = 0.5),
legend.title = element_text(size = 9, face = "bold"),
legend.text = element_text(size = 8),
legend.position = "right",
axis.line = element_line(color = "black", linewidth = 0.4),
axis.text = element_text(size = 7)
) +
labs(color = "TF State")
p_umap

Panel B — Stacked Bar
Chart (State Proportions)
state_counts <- seurat_L1@meta.data %>%
filter(!is.na(TF_State)) %>%
count(TF_State) %>%
mutate(
Proportion = n / sum(n) * 100,
TF_State = factor(TF_State, levels = names(state_colors))
)
print(state_counts)
TF_State n Proportion
1 Inflammatory 1917 32.90987
2 Proliferative 2219 38.09442
3 Th1_Cytotoxic 1689 28.99571
p_bar <- ggplot(
state_counts,
aes(x = "L1", y = Proportion, fill = TF_State)
) +
geom_bar(
stat = "identity",
width = 0.45,
color = "white",
linewidth = 0.3
) +
geom_text(
aes(label = paste0(round(Proportion, 1), "%")),
position = position_stack(vjust = 0.5),
size = 3.5,
color = "white",
fontface = "bold"
) +
scale_fill_manual(values = state_colors, drop = FALSE) +
scale_y_continuous(
limits = c(0, 100),
breaks = seq(0, 100, 25),
labels = function(x) paste0(x, "%")
) +
labs(
y = "Proportion of cells (%)",
x = NULL,
fill = "TF State",
title = "State distribution — L1"
) +
theme_classic(base_size = 11) +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
plot.title = element_text(face = "bold", size = 11, hjust = 0.5),
legend.position = "none",
axis.line = element_line(color = "black", linewidth = 0.4)
)
p_bar

# Session Info
sessionInfo()
R version 4.5.2 (2025-10-31)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 24.04.3 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.12.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0 LAPACK version 3.12.0
locale:
[1] LC_CTYPE=en_GB.UTF-8 LC_NUMERIC=C LC_TIME=fr_FR.UTF-8
[4] LC_COLLATE=en_GB.UTF-8 LC_MONETARY=fr_FR.UTF-8 LC_MESSAGES=en_GB.UTF-8
[7] LC_PAPER=fr_FR.UTF-8 LC_NAME=C LC_ADDRESS=C
[10] LC_TELEPHONE=C LC_MEASUREMENT=fr_FR.UTF-8 LC_IDENTIFICATION=C
time zone: Europe/Paris
tzcode source: system (glibc)
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] RColorBrewer_1.1-3 patchwork_1.3.2 dplyr_1.2.0 ggplot2_4.0.2
[5] Seurat_5.4.0 SeuratObject_5.3.0 sp_2.2-1
loaded via a namespace (and not attached):
[1] deldir_2.0-4 pbapply_1.7-4 gridExtra_2.3
[4] rlang_1.1.7 magrittr_2.0.4 RcppAnnoy_0.0.23
[7] otel_0.2.0 spatstat.geom_3.7-0 matrixStats_1.5.0
[10] ggridges_0.5.7 compiler_4.5.2 systemfonts_1.3.1
[13] png_0.1-8 vctrs_0.7.1 reshape2_1.4.5
[16] stringr_1.6.0 pkgconfig_2.0.3 fastmap_1.2.0
[19] labeling_0.4.3 promises_1.5.0 rmarkdown_2.30
[22] ggbeeswarm_0.7.3 ragg_1.5.0 purrr_1.2.1
[25] xfun_0.56 cachem_1.1.0 jsonlite_2.0.0
[28] goftest_1.2-3 later_1.4.5 spatstat.utils_3.2-1
[31] irlba_2.3.7 parallel_4.5.2 cluster_2.1.8.2
[34] R6_2.6.1 ica_1.0-3 spatstat.data_3.1-9
[37] bslib_0.10.0 stringi_1.8.7 reticulate_1.44.1
[40] spatstat.univar_3.1-6 parallelly_1.46.1 lmtest_0.9-40
[43] jquerylib_0.1.4 scattermore_1.2 Rcpp_1.1.1
[46] knitr_1.51 tensor_1.5.1 future.apply_1.20.1
[49] zoo_1.8-15 sctransform_0.4.3 httpuv_1.6.16
[52] Matrix_1.7-4 splines_4.5.2 igraph_2.2.2
[55] tidyselect_1.2.1 abind_1.4-8 rstudioapi_0.18.0
[58] dichromat_2.0-0.1 yaml_2.3.12 spatstat.random_3.4-4
[61] codetools_0.2-20 miniUI_0.1.2 spatstat.explore_3.7-0
[64] listenv_0.10.0 lattice_0.22-9 tibble_3.3.1
[67] plyr_1.8.9 withr_3.0.2 shiny_1.12.1
[70] S7_0.2.1 ROCR_1.0-12 ggrastr_1.0.2
[73] evaluate_1.0.5 Rtsne_0.17 future_1.69.0
[76] fastDummies_1.7.5 survival_3.8-3 polyclip_1.10-7
[79] fitdistrplus_1.2-6 pillar_1.11.1 rsconnect_1.7.0
[82] KernSmooth_2.23-26 plotly_4.12.0 generics_0.1.4
[85] RcppHNSW_0.6.0 scales_1.4.0 globals_0.19.0
[88] xtable_1.8-4 glue_1.8.0 lazyeval_0.2.2
[91] tools_4.5.2 data.table_1.18.2.1 RSpectra_0.16-2
[94] RANN_2.6.2 dotCall64_1.2 cowplot_1.2.0
[97] grid_4.5.2 tidyr_1.3.2 nlme_3.1-168
[100] beeswarm_0.4.0 vipor_0.4.7 cli_3.6.5
[103] spatstat.sparse_3.1-0 textshaping_1.0.4 spam_2.11-3
[106] viridisLite_0.4.3 uwot_0.2.4 gtable_0.3.6
[109] sass_0.4.10 digest_0.6.39 progressr_0.18.0
[112] ggrepel_0.9.6 htmlwidgets_1.6.4 farver_2.1.2
[115] htmltools_0.5.9 lifecycle_1.0.5 httr_1.4.7
[118] mime_0.13 MASS_7.3-65
LS0tCnRpdGxlOiAiRmlndXJlIDhELUwxIEludHJhLUNsb25hbCBSZWd1bGF0b3J5IEhldGVyb2dlbmVpdHkg4oCUIFRGIFN0YXRlIFVNQVAiCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNzczogc3R5bGUuY3NzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgIHRoZW1lOiBqb3VybmFsCi0tLQoKPCEtLSAjID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IC0tPgo8IS0tICMgRmlndXJlIDhEIOKAlCBMMSBJbnRyYS1DbG9uYWwgUmVndWxhdG9yeSBIZXRlcm9nZW5laXR5IC0tPgo8IS0tICMgMyBTdGF0ZXM6IEluZmxhbW1hdG9yeSAoU1RBVDMvUkVMQS9ORktCMSkgfCBQcm9saWZlcmF0aXZlIChNWUMvRTJGMSkgfCAgLS0+CjwhLS0gIyAgICAgICAgICAgVGgxX0N5dG90b3hpYyAoVEJYMjEpIC0tPgo8IS0tICMgU3RlbS1saWtlIHJlZmVyZW5jZWQgaW4gdGV4dCB2aWEgSE1HQTIgKGNsdXN0ZXIgNSwgRmlnLjVCKSAtLT4KPCEtLSAjIFNjcmlwdCBhdXRob3I6IE5hc2lyIE1haG1vb2QgQWJiYXNpIOKAlCBCUklDLCBVbml2ZXJzaXTDqSBkZSBCb3JkZWF1eCAtLT4KPCEtLSAjID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IC0tPgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvICAgICAgID0gVFJVRSwKICB3YXJuaW5nICAgID0gRkFMU0UsCiAgbWVzc2FnZSAgICA9IEZBTFNFLAogIGZpZy53aWR0aCAgPSAxMCwKICBmaWcuaGVpZ2h0ID0gNiwKICBkcGkgICAgICAgID0gMzAwCikKYGBgCgoKCgojIGxvYWQgbGlicmFyaWVzCmBgYHtyICwgaW5jbHVkZT1GQUxTRX0KCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShzY2FsZXMpCgpgYGAKCgojIExvYWQgT2JqZWN0ICYgU2V0IFJOQSBBc3NheQpgYGB7ciAsIGluY2x1ZGU9RkFMU0V9CgpzZXVyYXRfb2JqIDwtIHJlYWRSRFMoIi9ob21lL2Jpb2luZm8vMS1UaGVzaXNfRmluYWxfWWVhcl8yMDI1LzIwMjUtWWVhcjNfQW5hbHlzaXMvMS1zY1JOQV9SRVNVTFRTLTE5LTExLTIwMjUvMTktVEZfQW5hbHlzaXNfRGVjdXBsZVIrRG9yb3RoZWEtRmViMjAyNi90ZW1wX3NldXJhdF9vYmoucmRzIikKCiMgQ29uZmlybSBhdmFpbGFibGUgYXNzYXlzIOKAlCBkb3JvdGhlYSBzaG91bGQgYXBwZWFyIGhlcmUKQXNzYXlzKHNldXJhdF9vYmopCgojIENvbmZpcm0gbWV0YWRhdGEgY29sdW1ucyDigJQgJ2NlbGxsaW5lJyBzaG91bGQgYXBwZWFyIGhlcmUKY29sbmFtZXMoc2V1cmF0X29iakBtZXRhLmRhdGEpCgojIENoZWNrIGNlbGwgbGluZSBsYWJlbHMKdGFibGUoc2V1cmF0X29iaiRvcmlnLmlkZW50KQpgYGAKCgoKCgoKIyBEZWZpbmUgU3RhdGUtRGVmaW5pbmcgVEZzIChDb25maXJtZWQgZnJvbSBIZWF0bWFwIFBhbmVsIEMpCmBgYHtyIH0KIyBvcmlnLmlkZW50IGlzIHNldCBhdXRvbWF0aWNhbGx5IGR1cmluZyBpbmRpdmlkdWFsIFNldXJhdCBvYmplY3QgY3JlYXRpb24KIyBJdCBzdG9yZXMgdGhlIHNhbXBsZSBuYW1lOiBMMSwgTDIsIEwzLCBMNCwgTDUsIEw2LCBMNywgUEJNQywgUEJNQy0xMHgKc2V1cmF0X0wxIDwtIHN1YnNldChzZXVyYXRfb2JqLCBzdWJzZXQgPSBvcmlnLmlkZW50ID09ICJMMSIpCgpjYXQoIk51bWJlciBvZiBMMSBjZWxsczoiLCBuY29sKHNldXJhdF9MMSksICJcbiIpCgojIFNhbml0eSBjaGVjayDigJQgc2hvdWxkIHNob3cgb25seSBMMQp0YWJsZShzZXVyYXRfTDEkb3JpZy5pZGVudCkKYGBgCgojIERlZmluZSAzIFN0YXRlcyAoRXhhY3QgbWF0Y2ggdG8gbWFudXNjcmlwdCBGaWd1cmUgOEQpCmBgYHtyIH0KIyBJbmZsYW1tYXRvcnkgIOKGkiBORi3OukIvQVAtMSBkcml2ZW4gKGNsdXN0ZXJzIDExLCAxMikKIyBQcm9saWZlcmF0aXZlIOKGkiBNWUMvRTJGMSBkcml2ZW4gIChjbHVzdGVycyA0LCA3LCA4KQojIFRoMV9DeXRvdG94aWMg4oaSIFRCWDIxIGRyaXZlbiAgICAgKGNsdXN0ZXIgNTsgc3RlbS1saWtlIHZpYSBITUdBMiBpbiB0ZXh0KQpzdGF0ZV90ZnMgPC0gbGlzdCgKICBJbmZsYW1tYXRvcnkgID0gYygiU1RBVDMiLCAiUkVMQSIsICJORktCMSIpLAogIFByb2xpZmVyYXRpdmUgPSBjKCJNWUMiLCAgICJFMkYxIiksCiAgVGgxX0N5dG90b3hpYyA9IGMoIlRCWDIxIikKKQpgYGAKCiMgRXh0cmFjdCBURiBBY3Rpdml0eSBNYXRyaXggZnJvbSBkb3JvdGhlYSBBc3NheQpgYGB7ciB9CnRmX21hdCA8LSBHZXRBc3NheURhdGEoc2V1cmF0X0wxLCBhc3NheSA9ICJkb3JvdGhlYSIsIGxheWVyID0gICJkYXRhIikKCmNhdCgiVEYgbWF0cml4IGRpbWVuc2lvbnMgKFRGcyB4IGNlbGxzKToiLCBkaW0odGZfbWF0KSwgIlxuIikKCiMgQ2hlY2sgd2hpY2ggc3RhdGUgVEZzIGFyZSBwcmVzZW50IGluIHRoZSBkb3JvdGhlYSBhc3NheQpmb3IgKHN0YXRlIGluIG5hbWVzKHN0YXRlX3RmcykpIHsKICBmb3VuZCAgIDwtIHN0YXRlX3Rmc1tbc3RhdGVdXVtzdGF0ZV90ZnNbW3N0YXRlXV0gJWluJSByb3duYW1lcyh0Zl9tYXQpXQogIG1pc3NpbmcgPC0gc3RhdGVfdGZzW1tzdGF0ZV1dWyFzdGF0ZV90ZnNbW3N0YXRlXV0gJWluJSByb3duYW1lcyh0Zl9tYXQpXQogIGNhdChzcHJpbnRmKCIlLTE1cyBGb3VuZDogJS0zMHMgTWlzc2luZzogJXNcbiIsCiAgICAgICAgICAgICAgc3RhdGUsCiAgICAgICAgICAgICAgcGFzdGUoZm91bmQsICAgY29sbGFwc2UgPSAiLCAiKSwKICAgICAgICAgICAgICBwYXN0ZShtaXNzaW5nLCBjb2xsYXBzZSA9ICIsICIpKSkKfQpgYGAKIyBDb21wdXRlIE1lYW4gQWN0aXZpdHkgU2NvcmUgcGVyIFN0YXRlIHBlciBDZWxsCmBgYHtyIH0Kc2NvcmVfZGYgPC0gYXMuZGF0YS5mcmFtZSgKICBzYXBwbHkoc3RhdGVfdGZzLCBmdW5jdGlvbih0ZnMpIHsKICAgIHRmc19mb3VuZCA8LSB0ZnNbdGZzICVpbiUgcm93bmFtZXModGZfbWF0KV0KICAgIGlmIChsZW5ndGgodGZzX2ZvdW5kKSA9PSAwKSB7CiAgICAgIHdhcm5pbmcoIk5vIFRGcyBmb3VuZCBmb3IgdGhpcyBzdGF0ZSDigJQgcmV0dXJuaW5nIE5BIikKICAgICAgcmV0dXJuKHJlcChOQV9yZWFsXywgbmNvbCh0Zl9tYXQpKSkKICAgIH0KICAgIGNvbE1lYW5zKHRmX21hdFt0ZnNfZm91bmQsICwgZHJvcCA9IEZBTFNFXSkKICB9KQopCgpyb3duYW1lcyhzY29yZV9kZikgPC0gY29sbmFtZXModGZfbWF0KQoKIyBQcmV2aWV3IHJhdyBzY29yZXMKaGVhZChyb3VuZChzY29yZV9kZiwgMykpCmBgYAojICBTY2FsZSBTY29yZXMgQWNyb3NzIFN0YXRlcwpgYGB7ciB9CnNjb3JlX2RmX3NjYWxlZCA8LSBhcy5kYXRhLmZyYW1lKHNjYWxlKHNjb3JlX2RmKSkKcm93bmFtZXMoc2NvcmVfZGZfc2NhbGVkKSA8LSByb3duYW1lcyhzY29yZV9kZikKCiMgUHJldmlldyBzY2FsZWQgc2NvcmVzCmhlYWQocm91bmQoc2NvcmVfZGZfc2NhbGVkLCAzKSkKYGBgCiMgIEFzc2lnbiBEb21pbmFudCBTdGF0ZSBwZXIgQ2VsbApgYGB7ciB9CnNjb3JlX2RmX3NjYWxlZCREb21pbmFudFN0YXRlIDwtIGFwcGx5KAogIHNjb3JlX2RmX3NjYWxlZFssIG5hbWVzKHN0YXRlX3RmcyksIGRyb3AgPSBGQUxTRV0sIDEsCiAgZnVuY3Rpb24oeCkgewogICAgaWYgKGFsbChpcy5uYSh4KSkpIHJldHVybihOQV9jaGFyYWN0ZXJfKQogICAgbmFtZXMod2hpY2gubWF4KHgpKQogIH0KKQoKc2V1cmF0X0wxJFRGX1N0YXRlIDwtIGZhY3RvcigKICBzY29yZV9kZl9zY2FsZWQkRG9taW5hbnRTdGF0ZSwKICBsZXZlbHMgPSBuYW1lcyhzdGF0ZV90ZnMpCikKYGBgCgojIEFsc28gQWRkIFJhdyBTY29yZXMgYXMgSW5kaXZpZHVhbCBNZXRhZGF0YSBDb2x1bW5zCmBgYHtyIH0KY2F0KCJcbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiIpCmNhdCgiICAgICBMMSBURiBTdGF0ZSBEaXN0cmlidXRpb24gKEZpZ3VyZSA4RClcbiIpCmNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuIikKZGlzdF90YWJsZSA8LSBzZXVyYXRfTDFAbWV0YS5kYXRhICU+JQogIGZpbHRlcighaXMubmEoVEZfU3RhdGUpKSAlPiUKICBjb3VudChURl9TdGF0ZSkgJT4lCiAgbXV0YXRlKFBlcmNlbnRhZ2UgPSByb3VuZChuIC8gc3VtKG4pICogMTAwLCAxKSkgJT4lCiAgYXJyYW5nZShmYWN0b3IoVEZfU3RhdGUsIGxldmVscyA9IG5hbWVzKHN0YXRlX3RmcykpKQpwcmludChkaXN0X3RhYmxlKQpjYXQoIkFsbCAzIHN0YXRlcyBtdXN0IGJlID4gMCUgdG8gY29uZmlybSBpbnRyYS1jbG9uYWwgaGV0ZXJvZ2VuZWl0eVxuIikKY2F0KCI9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4iKQoKIyBEZWZpbmUgQ29sb3IgUGFsZXR0ZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKc3RhdGVfY29sb3JzIDwtIGMoCiAgSW5mbGFtbWF0b3J5ICA9ICIjRTYzOTQ2IiwgICAgIyByZWQgICDigJQgTkYtzrpCL1NUQVQzIHN1cnZpdmFsIHByb2dyYW0KICBQcm9saWZlcmF0aXZlID0gIiM0NTdCOUQiLCAgICAjIGJsdWUgIOKAlCBNWUMvRTJGMSBwcm9saWZlcmF0aXZlIHByb2dyYW0KICBUaDFfQ3l0b3RveGljID0gIiMyRDZBNEYiICAgICAjIGdyZWVuIOKAlCBUQlgyMSBUaDEvY3l0b3RveGljIHByb2dyYW0KKQpgYGAKCiMgQ2hlY2sgVU1BUCBpcyBQcmVzZW50CmBgYHtyIH0KY2F0KCJcblJlZHVjdGlvbnMgYXZhaWxhYmxlOiIsIHBhc3RlKFJlZHVjdGlvbnMoc2V1cmF0X0wxKSwgY29sbGFwc2UgPSAiLCAiKSwgIlxuIikKCiMgT25seSBydW4gaWYgVU1BUCBpcyBhYnNlbnQgZnJvbSB0aGUgc3Vic2V0CmlmICghInVtYXAiICVpbiUgUmVkdWN0aW9ucyhzZXVyYXRfTDEpKSB7CiAgY2F0KCJVTUFQIG5vdCBmb3VuZCDigJQgcmVjb21wdXRpbmcgZnJvbSBIYXJtb255IGVtYmVkZGluZ3MuLi5cbiIpCiAgc2V1cmF0X0wxIDwtIFJ1blVNQVAoc2V1cmF0X0wxLAogICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAiaGFybW9ueSIsCiAgICAgICAgICAgICAgICAgICAgICAgIGRpbXMgICAgICA9IDE6MjAsCiAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlICA9IDQyKQp9CmBgYAoKCiMgIFBhbmVsIEEg4oCUIFVNQVAgQ29sb3JlZCBieSBURiBTdGF0ZQpgYGB7cn0KcF91bWFwIDwtIERpbVBsb3QoCiAgc2V1cmF0X0wxLAogIHJlZHVjdGlvbiAgPSAidW1hcCIsCiAgZ3JvdXAuYnkgICA9ICJURl9TdGF0ZSIsCiAgY29scyAgICAgICA9IHN0YXRlX2NvbG9ycywKICBwdC5zaXplICAgID0gMS4wLAogIGxhYmVsICAgICAgPSBGQUxTRSwKICBuYS52YWx1ZSAgID0gImdyZXk4NSIKKSArCiAgZ2d0aXRsZSgiQ2VsbCBMaW5lIEwxIOKAlCBURiBSZWd1bGF0b3J5IFN0YXRlIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpICsKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgICAgICA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTEsIGhqdXN0ID0gMC41KSwKICAgIGxlZ2VuZC50aXRsZSAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgIGZhY2UgPSAiYm9sZCIpLAogICAgbGVnZW5kLnRleHQgICAgID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICBheGlzLmxpbmUgICAgICAgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjQpLAogICAgYXhpcy50ZXh0ICAgICAgID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KQogICkgKwogIGxhYnMoY29sb3IgPSAiVEYgU3RhdGUiKQoKcF91bWFwCmBgYAoKCgoKIyBQYW5lbCBCIOKAlCBTdGFja2VkIEJhciBDaGFydCAoU3RhdGUgUHJvcG9ydGlvbnMpCmBgYHtyIH0Kc3RhdGVfY291bnRzIDwtIHNldXJhdF9MMUBtZXRhLmRhdGEgJT4lCiAgZmlsdGVyKCFpcy5uYShURl9TdGF0ZSkpICU+JQogIGNvdW50KFRGX1N0YXRlKSAlPiUKICBtdXRhdGUoCiAgICBQcm9wb3J0aW9uID0gbiAvIHN1bShuKSAqIDEwMCwKICAgIFRGX1N0YXRlICAgPSBmYWN0b3IoVEZfU3RhdGUsIGxldmVscyA9IG5hbWVzKHN0YXRlX2NvbG9ycykpCiAgKQoKcHJpbnQoc3RhdGVfY291bnRzKQoKcF9iYXIgPC0gZ2dwbG90KAogIHN0YXRlX2NvdW50cywKICBhZXMoeCA9ICJMMSIsIHkgPSBQcm9wb3J0aW9uLCBmaWxsID0gVEZfU3RhdGUpCikgKwogIGdlb21fYmFyKAogICAgc3RhdCAgICAgID0gImlkZW50aXR5IiwKICAgIHdpZHRoICAgICA9IDAuNDUsCiAgICBjb2xvciAgICAgPSAid2hpdGUiLAogICAgbGluZXdpZHRoID0gMC4zCiAgKSArCiAgZ2VvbV90ZXh0KAogICAgYWVzKGxhYmVsID0gcGFzdGUwKHJvdW5kKFByb3BvcnRpb24sIDEpLCAiJSIpKSwKICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLAogICAgc2l6ZSAgICAgPSAzLjUsCiAgICBjb2xvciAgICA9ICJ3aGl0ZSIsCiAgICBmb250ZmFjZSA9ICJib2xkIgogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHN0YXRlX2NvbG9ycywgZHJvcCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbGltaXRzID0gYygwLCAxMDApLAogICAgYnJlYWtzID0gc2VxKDAsIDEwMCwgMjUpLAogICAgbGFiZWxzID0gZnVuY3Rpb24oeCkgcGFzdGUwKHgsICIlIikKICApICsKICBsYWJzKAogICAgeSAgICAgPSAiUHJvcG9ydGlvbiBvZiBjZWxscyAoJSkiLAogICAgeCAgICAgPSBOVUxMLAogICAgZmlsbCAgPSAiVEYgU3RhdGUiLAogICAgdGl0bGUgPSAiU3RhdGUgZGlzdHJpYnV0aW9uIOKAlCBMMSIKICApICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDExKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCAgICAgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggICAgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwbG90LnRpdGxlICAgICAgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDExLCBoanVzdCA9IDAuNSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBheGlzLmxpbmUgICAgICAgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjQpCiAgKQoKcF9iYXIKYGBgCgoKCiMgQ29tYmluZSBQYW5lbHMgYW5kIFNhdmUgLSBGaWd1cmUgOEQKYGBge3IgfQpmaW5hbF9maWcgPC0gcF91bWFwICsgcF9iYXIgKwogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMi41LCAxKSkgKwogIHBsb3RfYW5ub3RhdGlvbigKICAgIHRpdGxlICAgID0gIkludHJhLWNsb25hbCByZWd1bGF0b3J5IGhldGVyb2dlbmVpdHkg4oCUIENlbGwgTGluZSBMMSIsCiAgICBzdWJ0aXRsZSA9ICJNb25vY2xvbmFsIHBvcHVsYXRpb24gc2ltdWx0YW5lb3VzbHkgb2NjdXBpZXMgSW5mbGFtbWF0b3J5LCBQcm9saWZlcmF0aXZlLCBhbmQgVGgxL0N5dG90b3hpYyBURiBzdGF0ZXMiLAogICAgdGhlbWUgICAgPSB0aGVtZSgKICAgICAgcGxvdC50aXRsZSAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIsICBoanVzdCA9IDAuNSksCiAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksICBjb2xvciA9ICJncmV5NDAiLCBoanVzdCA9IDAuNSkKICAgICkKICApCgpmaW5hbF9maWcKCmdnc2F2ZSgiRmlndXJlXzhEX0wxX1RGc3RhdGVfVU1BUF9CYXIucGRmIiwKICAgICAgIHBsb3QgPSBmaW5hbF9maWcsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNC41LCBkcGkgPSAzMDAsIGRldmljZSA9ICJwZGYiKQoKZ2dzYXZlKCJGaWd1cmVfOERfTDFfVEZzdGF0ZV9VTUFQX0Jhci5wbmciLAogICAgICAgcGxvdCA9IGZpbmFsX2ZpZywgd2lkdGggPSA5LCBoZWlnaHQgPSA0LjUsIGRwaSA9IDMwMCkKCmNhdCgiRmlndXJlIDhEIHNhdmVkIHN1Y2Nlc3NmdWxseS5cbiIpCmBgYAoKIyBWYWxpZGF0aW9uIOKAlCBWaW9saW4gUGxvdHMgKFN1cHBsZW1lbnRhcnkgRmlndXJlIFNYKQpgYGB7cn0Kc2NvcmVfZmVhdHVyZXMgPC0gcGFzdGUwKCJTY29yZV8iLCBuYW1lcyhzdGF0ZV90ZnMpKQoKcF92YWwgPC0gVmxuUGxvdCgKICBzZXVyYXRfTDEsCiAgZmVhdHVyZXMgPSBzY29yZV9mZWF0dXJlcywKICBncm91cC5ieSA9ICJURl9TdGF0ZSIsCiAgcHQuc2l6ZSAgPSAwLAogIGNvbHMgICAgID0gc3RhdGVfY29sb3JzLAogIG5jb2wgICAgID0gMwopICYKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDkpICYKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ICAgICA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSA3KSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIHBsb3QudGl0bGUgICAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gOSkKICApCgpwX3ZhbAoKZ2dzYXZlKCJTdXBwX1NYX1RGX3ZhbGlkYXRpb25fdmlvbGlucy5wZGYiLAogICAgICAgcGxvdCA9IHBfdmFsLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA0LjUsIGRwaSA9IDMwMCkKCmdnc2F2ZSgiU3VwcF9TWF9URl92YWxpZGF0aW9uX3Zpb2xpbnMucG5nIiwKICAgICAgIHBsb3QgPSBwX3ZhbCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNC41LCBkcGkgPSAzMDAsIGJnID0gIndoaXRlIikKCmNhdCgiVmFsaWRhdGlvbiB2aW9saW5zIHNhdmVkIGFzIFBERiBhbmQgUE5HLlxuIikKCgpgYGAKCgoKIyAjIFNlc3Npb24gSW5mbwpgYGB7cn0Kc2Vzc2lvbkluZm8oKQoKYGBgCgoKCgoK