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=