Prepare analysis workflow
Set filepaths and parameters
set.seed(42)
knitr::opts_knit$set(root.dir = rprojroot::find_rstudio_root_file())
options(
readr.show_progress = FALSE,
digits = 2
)
Load packages
suppressPackageStartupMessages({
library(scater)
library(scran)
library(tidyverse)
library(uwot) # For umap
})
Define file paths
data_dir <- "./data"
figures_dir <- file.path("./figures")
Load data
sce10x <-
readRDS(file.path(
data_dir,
"preprocessed",
"sce10x_filtered_final.rds"
))
table(colData(sce10x)$celltype, colData(sce10x)$sample)
yng1 yng2 yng3 aged1 aged2 aged3 aged4
fap 498 390 637 2020 2009 1568 1027
macrophage 282 274 398 58 245 149 189
stem 1199 1913 1049 521 682 326 739
Lognormalize spliced and spliced using common size factors
sce10x <-
logNormCounts(sce10x,
size_factors = sizeFactors(sce10x),
exprs_values = "spliced",
name = "spliced_logcounts",
center_size_factors = FALSE
)
sce10x <-
logNormCounts(sce10x,
size_factors = sizeFactors(sce10x),
exprs_values = "unspliced",
name = "unspliced_logcounts",
center_size_factors = FALSE
)
UMAP
Subset data
sce10x_stem <- sce10x[, colData(sce10x)$celltype == "stem"]
sce10x_fap <- sce10x[, colData(sce10x)$celltype == "fap"]
sce10x_macrophage <- sce10x[, colData(sce10x)$celltype == "macrophage"]
Run on stem
set.seed(42)
# t-UMAP is equivalent to a = 1, b = 1
# get the nearest neighbor data back
sce10x_stem_tumap <-
tumap(reducedDim(
sce10x_stem,
"mnn_corrected"
),
metric = "cosine",
ret_nn = TRUE
)
set.seed(42)
sce10x_stem_umap <-
umap(reducedDim(
sce10x_stem,
"mnn_corrected"
),
metric = "cosine",
nn_method = sce10x_stem_tumap$nn,
a = .7,
b = .52
)
colnames(sce10x_stem_umap) <- paste0("umap", seq(1, 2))
reducedDim(sce10x_stem, "mnn_umap") <- sce10x_stem_umap
set.seed(42)
g <- buildSNNGraph(sce10x_stem, k = 200, use.dimred = "mnn_umap")
clusters_out <- igraph::cluster_walktrap(g)
table(clusters_out$membership)
1 2 3 4 5 6 7 8 9
963 1132 853 817 798 415 367 480 604
stem_clusters <- igraph::cut_at(clusters_out, n = 3)
stem_clusters <- paste("stem", stem_clusters, sep = "_")
colData(sce10x_stem)$clusters <- stem_clusters
table(stem_clusters)
stem_clusters
stem_1 stem_2 stem_3
2993 2832 604
dt <-
bind_cols(
colData(sce10x_stem) %>%
as_tibble(),
sce10x_stem_umap %>%
as_tibble()
)
p1 <-
ggplot(dt, aes(umap1,
umap2,
colour = clusters
)) +
geom_point(size = 1) +
scale_colour_viridis_d(option = "plasma") +
theme_classic()
p1
ggsave(file.path(figures_dir, "umap_mnn_stem.pdf"), p1)
Saving 14 x 10 in image
p2 <- ggplot(dt, aes(umap1,
umap2,
colour = clusters
)) +
geom_point(size = 1) +
scale_colour_viridis_d(option = "plasma") +
theme_classic() +
facet_wrap(~condition, ncol = 1)
p2
ggsave(file.path(figures_dir,
"umap_stem_clusters_condition.pdf"), p2)
Saving 14 x 10 in image
p3 <- ggplot(dt, aes(umap1,
umap2,
colour = condition
)) +
geom_point(size = 1, alpha = 0.7) +
scale_colour_viridis_d(option = "plasma") +
theme_classic()
p3
ggsave(file.path(figures_dir,
"umap_stem_clusters_condition_color.pdf"), p3)
Saving 14 x 10 in image
Run on fap
set.seed(42)
# t-UMAP is equivalent to a = 1, b = 1
# get the nearest neighbor data back
sce10x_fap_tumap <-
tumap(reducedDim(
sce10x_fap,
"mnn_corrected"
),
metric = "cosine",
ret_nn = TRUE
)
set.seed(42)
sce10x_fap_umap <-
umap(reducedDim(
sce10x_fap,
"mnn_corrected"
),
metric = "cosine",
nn_method = sce10x_fap_tumap$nn,
a = .95,
b = .70
)
colnames(sce10x_fap_umap) <- paste0("umap", seq(1, 2))
reducedDim(sce10x_fap, "mnn_umap") <- sce10x_fap_umap
set.seed(42)
g <- buildSNNGraph(sce10x_fap, k = 100, use.dimred = "mnn_umap")
clusters_out <- igraph::cluster_walktrap(g)
table(clusters_out$membership)
1 2 3 4 5 6 7 8 9 10 11 12 13
718 1085 751 1002 659 880 752 479 686 399 369 155 214
fap_clusters <- igraph::cut_at(clusters_out, n = 2)
fap_clusters <- paste("fap", fap_clusters, sep = "_")
colData(sce10x_fap)$clusters <- fap_clusters
table(fap_clusters)
fap_clusters
fap_1 fap_2
6150 1999
dt <-
bind_cols(
colData(sce10x_fap) %>%
as_tibble(),
sce10x_fap_umap %>%
as_tibble()
)
p4 <-
ggplot(dt, aes(umap1,
umap2,
colour = clusters
)) +
geom_point(size = 1) +
scale_colour_viridis_d(option = "plasma") +
theme_classic()
p4
ggsave(file.path(figures_dir,
"umap_mnn_fap.pdf"), p4)
Saving 14 x 10 in image
p5 <- ggplot(dt, aes(umap1,
umap2,
colour = clusters
)) +
geom_point(size = 1) +
scale_colour_viridis_d(option = "plasma") +
theme_classic() +
facet_wrap(~condition, ncol = 1)
p5
ggsave(file.path(figures_dir,
"umap_fap_clusters_condition.pdf"), p5)
Saving 14 x 10 in image
Run on macrophage
set.seed(42)
# t-UMAP is equivalent to a = 1, b = 1
# get the nearest neighbor data back
sce10x_macrophage_tumap <-
tumap(reducedDim(
sce10x_macrophage,
"mnn_corrected"
),
metric = "cosine",
ret_nn = TRUE
)
set.seed(42)
sce10x_macrophage_umap <-
umap(reducedDim(
sce10x_macrophage,
"mnn_corrected"
),
metric = "cosine",
nn_method = sce10x_macrophage_tumap$nn,
a = 1.2,
b = 1.4
)
colnames(sce10x_macrophage_umap) <- paste0("umap", seq(1, 2))
reducedDim(sce10x_macrophage, "mnn_umap") <- sce10x_macrophage_umap
dt <-
bind_cols(
colData(sce10x_macrophage) %>%
as_tibble(),
sce10x_macrophage_umap %>%
as_tibble()
)
p6 <-
ggplot(dt) +
geom_point(aes(umap1,
umap2,
colour = clusters
),
size = 1,
alpha = 1
) +
theme_classic() +
facet_wrap(~condition)
p6
ggsave(file.path(figures_dir,
"umap_mnn_macrophage.pdf"), p6)
Saving 14 x 10 in image
Combine and save
sce10x <-
cbind(
sce10x_stem,
sce10x_fap,
sce10x_macrophage
)
colData(sce10x)$celltype_condition <-
paste(colData(sce10x)$cell_type, colData(sce10x)$condition, sep = "_")
colData(sce10x)$condition <- factor(colData(sce10x)$condition,
levels = c("yng", "aged"))
colData(sce10x)$sample <- factor(colData(sce10x)$sample,
levels = c("yng1", "yng2", "yng3",
"aged1", "aged2", "aged3", "aged4"))
colData(sce10x)$celltype_sample <- paste(colData(sce10x)$celltype,
colData(sce10x)$sample, sep = "_")
colData(sce10x)$clusters_condition <-
paste(colData(sce10x)$clusters, colData(sce10x)$condition, sep = "_")
colData(sce10x)$clusters_sample <- paste(colData(sce10x)$clusters,
colData(sce10x)$sample, sep = "_")
DF <-
colData(sce10x) %>%
as_tibble() %>%
select(
sizeFactor, sample, condition, celltype, clusters,
celltype_condition, clusters_condition, celltype_sample,
clusters_sample, sum, detected, sum_unspliced, detected_unspliced,
unspliced_ov_spliced, unspliced_ov_total
) %>%
DataFrame(.)
rownames(DF) <- rownames(colData(sce10x))
colData(sce10x) <- DF
colData(sce10x)
DataFrame with 16173 rows and 15 columns
sizeFactor sample condition celltype clusters celltype_condition clusters_condition celltype_sample clusters_sample
<numeric> <factor> <factor> <character> <character> <character> <character> <character> <character>
CTACAGAGTGGCCCAT_d1 1.085628 aged1 aged stem stem_1 stem_aged stem_1_aged stem_aged1 stem_1_aged1
CTAACCCCACACCTGG_d1 0.898127 aged1 aged stem stem_1 stem_aged stem_1_aged stem_aged1 stem_1_aged1
AGGAGGTCACAGGATG_d1 0.854388 aged1 aged stem stem_1 stem_aged stem_1_aged stem_aged1 stem_1_aged1
GTGGCGTTCGCTCCTA_d1 0.908817 aged1 aged stem stem_1 stem_aged stem_1_aged stem_aged1 stem_1_aged1
GAACTGTGTAGACAAT_d1 0.861042 aged1 aged stem stem_1 stem_aged stem_1_aged stem_aged1 stem_1_aged1
... ... ... ... ... ... ... ... ... ...
CACTGTCGTGTCCATA_g3 0.610536 yng3 yng macrophage macrophage macrophage_yng macrophage_yng macrophage_yng3 macrophage_yng3
CTGTGAATCTCCATAT_g3 0.571393 yng3 yng macrophage macrophage macrophage_yng macrophage_yng macrophage_yng3 macrophage_yng3
GTCAGCGAGCGCCTCA_g3 0.436667 yng3 yng macrophage macrophage macrophage_yng macrophage_yng macrophage_yng3 macrophage_yng3
GTCGTTCAGCCATTCA_g3 0.471375 yng3 yng macrophage macrophage macrophage_yng macrophage_yng macrophage_yng3 macrophage_yng3
GATCGTATCGCGTCGA_g3 0.687047 yng3 yng macrophage macrophage macrophage_yng macrophage_yng macrophage_yng3 macrophage_yng3
sum detected sum_unspliced detected_unspliced unspliced_ov_spliced unspliced_ov_total
<numeric> <integer> <numeric> <integer> <numeric> <numeric>
CTACAGAGTGGCCCAT_d1 9953 3042 2363.11 1213 0.311349 0.237427
CTAACCCCACACCTGG_d1 8234 2954 2887.40 1334 0.540044 0.350668
AGGAGGTCACAGGATG_d1 7833 2553 2068.85 1054 0.358918 0.264120
GTGGCGTTCGCTCCTA_d1 8332 2708 2045.49 1072 0.325378 0.245498
GAACTGTGTAGACAAT_d1 7894 3064 2802.43 1452 0.550405 0.355007
... ... ... ... ... ... ...
CACTGTCGTGTCCATA_g3 5506 2053 1196.860 738 0.277749 0.217374
CTGTGAATCTCCATAT_g3 5153 2268 1441.417 810 0.388356 0.279724
GTCAGCGAGCGCCTCA_g3 3938 1869 1178.239 731 0.426935 0.299197
GTCGTTCAGCCATTCA_g3 4251 2148 816.238 570 0.237640 0.192011
GATCGTATCGCGTCGA_g3 6196 2546 1429.636 878 0.299943 0.230735
table(colData(sce10x)$clusters, colData(sce10x)$sample)
yng1 yng2 yng3 aged1 aged2 aged3 aged4
fap_1 295 284 389 1630 1585 1134 833
fap_2 203 106 248 390 424 434 194
macrophage 282 274 398 58 245 149 189
stem_1 397 661 541 370 404 185 435
stem_2 549 1030 379 151 278 141 304
stem_3 253 222 129 0 0 0 0
saveRDS(
sce10x,
file.path(
data_dir,
"preprocessed",
"sce10x_filtered_final.rds"
)
)
sessionInfo()
LS0tCnRpdGxlOiAiTW91c2UgTXVzY2xlIFN0ZW0gQ2VsbCBQcm9qZWN0ICIKc3VidGl0bGU6ICJQYXJ0IDNiOiBjbHVzdGVyIGNlbGx0eXBlcyIKYXV0aG9yOiAKLSBuYW1lOiBSaWNrIEZhcm91bmkKICBhZmZpbGlhdGlvbjoKICAtICZjcnVrIEfDqW5vbWUgUXXDqWJlYyBJbm5vdmF0aW9uIENlbnRyZSwgTWNHaWxsIFVuaXZlcnNpdHksIE1vbnRyZWFsLCBDYW5hZGEKZGF0ZTogJ2ByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJVktJUItJWQiKWAnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIHRvYzogbm8KICAgIHRvY19mbG9hdDogCiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKLS0tCgoKCiMgUHJlcGFyZSBhbmFseXNpcyB3b3JrZmxvdwoKIyMgU2V0IGZpbGVwYXRocyBhbmQgcGFyYW1ldGVycwoKYGBge3Igc2V0dXB9CnNldC5zZWVkKDQyKQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9IHJwcm9qcm9vdDo6ZmluZF9yc3R1ZGlvX3Jvb3RfZmlsZSgpKQpvcHRpb25zKAogIHJlYWRyLnNob3dfcHJvZ3Jlc3MgPSBGQUxTRSwKICBkaWdpdHMgPSAyCikKYGBgCgojIyBMb2FkIHBhY2thZ2VzCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkoc2NhdGVyKQogIGxpYnJhcnkoc2NyYW4pCiAgbGlicmFyeSh0aWR5dmVyc2UpCiAgbGlicmFyeSh1d290KSAjIEZvciB1bWFwCn0pCmBgYAoKIyMgRGVmaW5lIGZpbGUgcGF0aHMKCmBgYHtyfQpkYXRhX2RpciA8LSAiLi9kYXRhIgpmaWd1cmVzX2RpciA8LSBmaWxlLnBhdGgoIi4vZmlndXJlcyIpCmBgYAoKCgojIyBMb2FkIGRhdGEKYGBge3J9CnNjZTEweCA8LQogIHJlYWRSRFMoZmlsZS5wYXRoKAogICAgZGF0YV9kaXIsCiAgICAicHJlcHJvY2Vzc2VkIiwKICAgICJzY2UxMHhfZmlsdGVyZWRfZmluYWwucmRzIgogICkpCmBgYAoKCmBgYHtyfQp0YWJsZShjb2xEYXRhKHNjZTEweCkkY2VsbHR5cGUsIGNvbERhdGEoc2NlMTB4KSRzYW1wbGUpCmBgYAoKCgoKIyMgTG9nbm9ybWFsaXplIHNwbGljZWQgYW5kIHNwbGljZWQgdXNpbmcgY29tbW9uIHNpemUgZmFjdG9ycwoKYGBge3J9CnNjZTEweCA8LQogIGxvZ05vcm1Db3VudHMoc2NlMTB4LAogICAgc2l6ZV9mYWN0b3JzID0gc2l6ZUZhY3RvcnMoc2NlMTB4KSwKICAgIGV4cHJzX3ZhbHVlcyA9ICJzcGxpY2VkIiwKICAgIG5hbWUgPSAic3BsaWNlZF9sb2djb3VudHMiLAogICAgY2VudGVyX3NpemVfZmFjdG9ycyA9IEZBTFNFCiAgKQoKc2NlMTB4IDwtCiAgbG9nTm9ybUNvdW50cyhzY2UxMHgsCiAgICBzaXplX2ZhY3RvcnMgPSBzaXplRmFjdG9ycyhzY2UxMHgpLAogICAgZXhwcnNfdmFsdWVzID0gInVuc3BsaWNlZCIsCiAgICBuYW1lID0gInVuc3BsaWNlZF9sb2djb3VudHMiLAogICAgY2VudGVyX3NpemVfZmFjdG9ycyA9IEZBTFNFCiAgKQpgYGAKCgoKIyBVTUFQCgojIyBTdWJzZXQgZGF0YQoKYGBge3J9CnNjZTEweF9zdGVtIDwtIHNjZTEweFssIGNvbERhdGEoc2NlMTB4KSRjZWxsdHlwZSA9PSAic3RlbSJdCnNjZTEweF9mYXAgPC0gc2NlMTB4WywgY29sRGF0YShzY2UxMHgpJGNlbGx0eXBlID09ICJmYXAiXQpzY2UxMHhfbWFjcm9waGFnZSA8LSBzY2UxMHhbLCBjb2xEYXRhKHNjZTEweCkkY2VsbHR5cGUgPT0gIm1hY3JvcGhhZ2UiXQpgYGAKCiMjIFJ1biBvbiBzdGVtIAoKYGBge3J9CnNldC5zZWVkKDQyKQojIHQtVU1BUCBpcyBlcXVpdmFsZW50IHRvIGEgPSAxLCBiID0gMQojIGdldCB0aGUgbmVhcmVzdCBuZWlnaGJvciBkYXRhIGJhY2sKc2NlMTB4X3N0ZW1fdHVtYXAgPC0KICB0dW1hcChyZWR1Y2VkRGltKAogICAgc2NlMTB4X3N0ZW0sCiAgICAibW5uX2NvcnJlY3RlZCIKICApLAogIG1ldHJpYyA9ICJjb3NpbmUiLAogIHJldF9ubiA9IFRSVUUKICApCmBgYAoKYGBge3J9CnNldC5zZWVkKDQyKQpzY2UxMHhfc3RlbV91bWFwIDwtCiAgdW1hcChyZWR1Y2VkRGltKAogICAgc2NlMTB4X3N0ZW0sCiAgICAibW5uX2NvcnJlY3RlZCIKICApLAogIG1ldHJpYyA9ICJjb3NpbmUiLAogIG5uX21ldGhvZCA9IHNjZTEweF9zdGVtX3R1bWFwJG5uLAogIGEgPSAuNywKICBiID0gLjUyCiAgKQpjb2xuYW1lcyhzY2UxMHhfc3RlbV91bWFwKSA8LSBwYXN0ZTAoInVtYXAiLCBzZXEoMSwgMikpCnJlZHVjZWREaW0oc2NlMTB4X3N0ZW0sICJtbm5fdW1hcCIpIDwtIHNjZTEweF9zdGVtX3VtYXAKYGBgCgoKYGBge3J9CnNldC5zZWVkKDQyKQpnIDwtIGJ1aWxkU05OR3JhcGgoc2NlMTB4X3N0ZW0sIGsgPSAyMDAsIHVzZS5kaW1yZWQgPSAibW5uX3VtYXAiKQoKY2x1c3RlcnNfb3V0IDwtIGlncmFwaDo6Y2x1c3Rlcl93YWxrdHJhcChnKQp0YWJsZShjbHVzdGVyc19vdXQkbWVtYmVyc2hpcCkKYGBgCgoKYGBge3J9CnN0ZW1fY2x1c3RlcnMgPC0gaWdyYXBoOjpjdXRfYXQoY2x1c3RlcnNfb3V0LCBuID0gMykKc3RlbV9jbHVzdGVycyA8LSBwYXN0ZSgic3RlbSIsIHN0ZW1fY2x1c3RlcnMsIHNlcCA9ICJfIikKY29sRGF0YShzY2UxMHhfc3RlbSkkY2x1c3RlcnMgPC0gc3RlbV9jbHVzdGVycwp0YWJsZShzdGVtX2NsdXN0ZXJzKQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTR9CmR0IDwtCiAgYmluZF9jb2xzKAogICAgY29sRGF0YShzY2UxMHhfc3RlbSkgJT4lCiAgICAgIGFzX3RpYmJsZSgpLAogICAgc2NlMTB4X3N0ZW1fdW1hcCAlPiUKICAgICAgYXNfdGliYmxlKCkKICApCnAxIDwtCiAgZ2dwbG90KGR0LCBhZXModW1hcDEsCiAgICB1bWFwMiwKICAgIGNvbG91ciA9IGNsdXN0ZXJzCiAgKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEpICsKICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKG9wdGlvbiA9ICJwbGFzbWEiKSArCiAgdGhlbWVfY2xhc3NpYygpCnAxCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJ1bWFwX21ubl9zdGVtLnBkZiIpLCBwMSkKYGBgCgoKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNH0KcDIgPC0gZ2dwbG90KGR0LCBhZXModW1hcDEsCiAgdW1hcDIsCiAgY29sb3VyID0gY2x1c3RlcnMKKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEpICsKICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKG9wdGlvbiA9ICJwbGFzbWEiKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBmYWNldF93cmFwKH5jb25kaXRpb24sIG5jb2wgPSAxKQpwMgpnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLAogICAgICAgICAgICAgICAgICJ1bWFwX3N0ZW1fY2x1c3RlcnNfY29uZGl0aW9uLnBkZiIpLCBwMikKYGBgCgoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTE0fQpwMyA8LSBnZ3Bsb3QoZHQsIGFlcyh1bWFwMSwKICB1bWFwMiwKICBjb2xvdXIgPSBjb25kaXRpb24KKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGFscGhhID0gMC43KSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChvcHRpb24gPSAicGxhc21hIikgKwogIHRoZW1lX2NsYXNzaWMoKQpwMwpnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLAogICAgICAgICAgICAgICAgICJ1bWFwX3N0ZW1fY2x1c3RlcnNfY29uZGl0aW9uX2NvbG9yLnBkZiIpLCBwMykKYGBgCgoKIyMgUnVuIG9uIGZhcAoKCmBgYHtyfQpzZXQuc2VlZCg0MikKIyB0LVVNQVAgaXMgZXF1aXZhbGVudCB0byBhID0gMSwgYiA9IDEKIyBnZXQgdGhlIG5lYXJlc3QgbmVpZ2hib3IgZGF0YSBiYWNrCnNjZTEweF9mYXBfdHVtYXAgPC0KICB0dW1hcChyZWR1Y2VkRGltKAogICAgc2NlMTB4X2ZhcCwKICAgICJtbm5fY29ycmVjdGVkIgogICksCiAgbWV0cmljID0gImNvc2luZSIsCiAgcmV0X25uID0gVFJVRQogICkKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoNDIpCnNjZTEweF9mYXBfdW1hcCA8LQogIHVtYXAocmVkdWNlZERpbSgKICAgIHNjZTEweF9mYXAsCiAgICAibW5uX2NvcnJlY3RlZCIKICApLAogIG1ldHJpYyA9ICJjb3NpbmUiLAogIG5uX21ldGhvZCA9IHNjZTEweF9mYXBfdHVtYXAkbm4sCiAgYSA9IC45NSwKICBiID0gLjcwCiAgKQpjb2xuYW1lcyhzY2UxMHhfZmFwX3VtYXApIDwtIHBhc3RlMCgidW1hcCIsIHNlcSgxLCAyKSkKcmVkdWNlZERpbShzY2UxMHhfZmFwLCAibW5uX3VtYXAiKSA8LSBzY2UxMHhfZmFwX3VtYXAKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoNDIpCmcgPC0gYnVpbGRTTk5HcmFwaChzY2UxMHhfZmFwLCBrID0gMTAwLCB1c2UuZGltcmVkID0gIm1ubl91bWFwIikKCmNsdXN0ZXJzX291dCA8LSBpZ3JhcGg6OmNsdXN0ZXJfd2Fsa3RyYXAoZykKdGFibGUoY2x1c3RlcnNfb3V0JG1lbWJlcnNoaXApCmBgYAoKCmBgYHtyfQpmYXBfY2x1c3RlcnMgPC0gaWdyYXBoOjpjdXRfYXQoY2x1c3RlcnNfb3V0LCBuID0gMikKZmFwX2NsdXN0ZXJzIDwtIHBhc3RlKCJmYXAiLCBmYXBfY2x1c3RlcnMsIHNlcCA9ICJfIikKY29sRGF0YShzY2UxMHhfZmFwKSRjbHVzdGVycyA8LSBmYXBfY2x1c3RlcnMKdGFibGUoZmFwX2NsdXN0ZXJzKQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTR9CmR0IDwtCiAgYmluZF9jb2xzKAogICAgY29sRGF0YShzY2UxMHhfZmFwKSAlPiUKICAgICAgYXNfdGliYmxlKCksCiAgICBzY2UxMHhfZmFwX3VtYXAgJT4lCiAgICAgIGFzX3RpYmJsZSgpCiAgKQoKcDQgPC0KICBnZ3Bsb3QoZHQsIGFlcyh1bWFwMSwKICAgIHVtYXAyLAogICAgY29sb3VyID0gY2x1c3RlcnMKICApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2Qob3B0aW9uID0gInBsYXNtYSIpICsKICB0aGVtZV9jbGFzc2ljKCkKcDQKZ2dzYXZlKGZpbGUucGF0aChmaWd1cmVzX2RpciwKICAgICAgICAgICAgICAgICAidW1hcF9tbm5fZmFwLnBkZiIpLCBwNCkKYGBgCgoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTE0fQpwNSA8LSBnZ3Bsb3QoZHQsIGFlcyh1bWFwMSwKICB1bWFwMiwKICBjb2xvdXIgPSBjbHVzdGVycwopKSArCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2Qob3B0aW9uID0gInBsYXNtYSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGZhY2V0X3dyYXAofmNvbmRpdGlvbiwgbmNvbCA9IDEpCnA1Cmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsCiAgICAgICAgICAgICAgICAgInVtYXBfZmFwX2NsdXN0ZXJzX2NvbmRpdGlvbi5wZGYiKSwgcDUpCmBgYAoKIyMgUnVuIG9uIG1hY3JvcGhhZ2UKCmBgYHtyfQpzZXQuc2VlZCg0MikKIyB0LVVNQVAgaXMgZXF1aXZhbGVudCB0byBhID0gMSwgYiA9IDEKIyBnZXQgdGhlIG5lYXJlc3QgbmVpZ2hib3IgZGF0YSBiYWNrCnNjZTEweF9tYWNyb3BoYWdlX3R1bWFwIDwtCiAgdHVtYXAocmVkdWNlZERpbSgKICAgIHNjZTEweF9tYWNyb3BoYWdlLAogICAgIm1ubl9jb3JyZWN0ZWQiCiAgKSwKICBtZXRyaWMgPSAiY29zaW5lIiwKICByZXRfbm4gPSBUUlVFCiAgKQpgYGAKCmBgYHtyfQpzZXQuc2VlZCg0MikKc2NlMTB4X21hY3JvcGhhZ2VfdW1hcCA8LQogIHVtYXAocmVkdWNlZERpbSgKICAgIHNjZTEweF9tYWNyb3BoYWdlLAogICAgIm1ubl9jb3JyZWN0ZWQiCiAgKSwKICBtZXRyaWMgPSAiY29zaW5lIiwKICBubl9tZXRob2QgPSBzY2UxMHhfbWFjcm9waGFnZV90dW1hcCRubiwKICBhID0gMS4yLAogIGIgPSAxLjQKICApCmNvbG5hbWVzKHNjZTEweF9tYWNyb3BoYWdlX3VtYXApIDwtIHBhc3RlMCgidW1hcCIsIHNlcSgxLCAyKSkKcmVkdWNlZERpbShzY2UxMHhfbWFjcm9waGFnZSwgIm1ubl91bWFwIikgPC0gc2NlMTB4X21hY3JvcGhhZ2VfdW1hcApgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNH0KZHQgPC0KICBiaW5kX2NvbHMoCiAgICBjb2xEYXRhKHNjZTEweF9tYWNyb3BoYWdlKSAlPiUKICAgICAgYXNfdGliYmxlKCksCiAgICBzY2UxMHhfbWFjcm9waGFnZV91bWFwICU+JQogICAgICBhc190aWJibGUoKQogICkKCnA2IDwtCiAgZ2dwbG90KGR0KSArCiAgZ2VvbV9wb2ludChhZXModW1hcDEsCiAgICB1bWFwMiwKICAgIGNvbG91ciA9IGNsdXN0ZXJzCiAgKSwKICBzaXplID0gMSwKICBhbHBoYSA9IDEKICApICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGZhY2V0X3dyYXAofmNvbmRpdGlvbikKcDYKZ2dzYXZlKGZpbGUucGF0aChmaWd1cmVzX2RpciwKICAgICAgICAgICAgICAgICAidW1hcF9tbm5fbWFjcm9waGFnZS5wZGYiKSwgcDYpCmBgYAoKCgojIENvbWJpbmUgYW5kIHNhdmUKCmBgYHtyfQpzY2UxMHggPC0KICBjYmluZCgKICAgIHNjZTEweF9zdGVtLAogICAgc2NlMTB4X2ZhcCwKICAgIHNjZTEweF9tYWNyb3BoYWdlCiAgKQpgYGAKCgpgYGB7cn0KY29sRGF0YShzY2UxMHgpJGNlbGx0eXBlX2NvbmRpdGlvbiA8LQogIHBhc3RlKGNvbERhdGEoc2NlMTB4KSRjZWxsdHlwZSwgY29sRGF0YShzY2UxMHgpJGNvbmRpdGlvbiwgc2VwID0gIl8iKQpjb2xEYXRhKHNjZTEweCkkY29uZGl0aW9uIDwtIGZhY3Rvcihjb2xEYXRhKHNjZTEweCkkY29uZGl0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJ5bmciLCAiYWdlZCIpKQpjb2xEYXRhKHNjZTEweCkkc2FtcGxlIDwtIGZhY3Rvcihjb2xEYXRhKHNjZTEweCkkc2FtcGxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygieW5nMSIsICJ5bmcyIiwgInluZzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhZ2VkMSIsICJhZ2VkMiIsICJhZ2VkMyIsICJhZ2VkNCIpKQpjb2xEYXRhKHNjZTEweCkkY2VsbHR5cGVfc2FtcGxlIDwtIHBhc3RlKGNvbERhdGEoc2NlMTB4KSRjZWxsdHlwZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YShzY2UxMHgpJHNhbXBsZSwgc2VwID0gIl8iKQpjb2xEYXRhKHNjZTEweCkkY2x1c3RlcnNfY29uZGl0aW9uIDwtCiAgcGFzdGUoY29sRGF0YShzY2UxMHgpJGNsdXN0ZXJzLCBjb2xEYXRhKHNjZTEweCkkY29uZGl0aW9uLCBzZXAgPSAiXyIpCmNvbERhdGEoc2NlMTB4KSRjbHVzdGVyc19zYW1wbGUgPC0gcGFzdGUoY29sRGF0YShzY2UxMHgpJGNsdXN0ZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEoc2NlMTB4KSRzYW1wbGUsIHNlcCA9ICJfIikKYGBgCgpgYGB7cn0KREYgPC0KICBjb2xEYXRhKHNjZTEweCkgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgc2VsZWN0KAogICAgc2l6ZUZhY3Rvciwgc2FtcGxlLCBjb25kaXRpb24sIGNlbGx0eXBlLCBjbHVzdGVycywKICAgIGNlbGx0eXBlX2NvbmRpdGlvbiwgY2x1c3RlcnNfY29uZGl0aW9uLCBjZWxsdHlwZV9zYW1wbGUsIAogICAgY2x1c3RlcnNfc2FtcGxlLCBzdW0sIGRldGVjdGVkLCBzdW1fdW5zcGxpY2VkLCBkZXRlY3RlZF91bnNwbGljZWQsIAogICAgdW5zcGxpY2VkX292X3NwbGljZWQsIHVuc3BsaWNlZF9vdl90b3RhbAogICkgJT4lCiAgRGF0YUZyYW1lKC4pCgpyb3duYW1lcyhERikgPC0gcm93bmFtZXMoY29sRGF0YShzY2UxMHgpKQpjb2xEYXRhKHNjZTEweCkgPC0gREYKYGBgCgoKYGBge3J9CmNvbERhdGEoc2NlMTB4KQpgYGAKCgpgYGB7cn0KdGFibGUoY29sRGF0YShzY2UxMHgpJGNsdXN0ZXJzLCBjb2xEYXRhKHNjZTEweCkkc2FtcGxlKQpgYGAKCgpgYGB7cn0Kc2F2ZVJEUygKICBzY2UxMHgsCiAgZmlsZS5wYXRoKAogICAgZGF0YV9kaXIsCiAgICAicHJlcHJvY2Vzc2VkIiwKICAgICJzY2UxMHhfZmlsdGVyZWRfZmluYWwucmRzIgogICkKKQpgYGAKCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=