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
  library(dbscan)
  library(batchelor)
  library(BiocSingular)
})
theme_set(theme_bw())

Define file paths

data_dir <- "./data"
figures_dir <- file.path("./figures", "qc_cells")

Load data

sce10x <-
  readRDS(file.path(
    data_dir,
    "preprocessed",
    "sce10x_filtered_cells.rds"
  ))
mitotic_dt<-
  perCellQCMetrics(
      sce10x,
      subsets = list(mitotic = which(rowData(sce10x)$gene_name %in% c("Ccna2", "Ccnb1", "Ccnb2"))),
      percent_top =NULL,
      exprs_values = c("counts"),
      flatten = TRUE
    ) %>%
    as_tibble() %>%
    dplyr::select(subsets_mitotic_sum, subsets_mitotic_detected, subsets_mitotic_percent)
mitotic_dt
colData(sce10x) <-
  cbind(colData(sce10x), mitotic_dt)

Cluster cell types

Cluster young cells

sce10x_yng <- sce10x[, colData(sce10x)$condition == "yng"]
n_exprs_genes_yng <-
  nexprs(sce10x_yng,
    detection_limit = 5,
    byrow = TRUE
  )

table(n_exprs_genes_yng)[1:20]
n_exprs_genes_yng
    0     1     2     3     4     5     6     7     8     9    10    11    12    13    14    15    16    17 
24696  1436   778   467   333   269   236   200   175   125   150   129   111   111   112    84    71    88 
   18    19 
   68    74 
set.seed(42)
# t-UMAP is equivalent to a = 1, b = 1
# get the nearest neighbor data back
sce10x_yng_tumap <-
  tumap(t(assay(sce10x_yng[n_exprs_genes_yng >= 3, ], "counts") %>%
    as.matrix()),
  metric = "cosine",
  ret_nn = TRUE
  )
set.seed(42)
sce10x_umap_yng <-
  umap(t(assay(
    sce10x_yng[n_exprs_genes_yng >= 3, ],
    "counts"
  ) %>%
    as.matrix()),
  metric = "cosine",
  nn_method = sce10x_yng_tumap$nn,
  a = .6,
  b = .55
  )
colnames(sce10x_umap_yng) <- paste0("umap", seq(1, 2))
reducedDim(sce10x_yng, "umap") <- sce10x_umap_yng
dt <-
  bind_cols(
    colData(sce10x_yng) %>%
      as_tibble(),
    sce10x_umap_yng %>%
      as_tibble()
  )
p2 <-
  ggplot(dt) +
  geom_point(aes(umap1,
    umap2,
    colour = markers
  ),
  size = 3,
  alpha = 1
  )
p2

# ggsave(file.path(figures_dir, "umap_cosine_a160_b33_17928_cells_markers.pdf"), p2 )
set.seed(42)
g <- buildSNNGraph(sce10x_yng, k = 20, use.dimred = "umap")
clusters_out <- igraph::cluster_walktrap(g)
# colLabels(sce10x) <- factor(igraph::cluster_louvain(g)$membership)
table(clusters_out$membership)

  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27 
147  72 354 370 147 434  88  90  93  87 272 236 250 176 214 404 146 295 493 110 238 151 172  98  69  96 233 
 28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54 
162  78  74  81  63 133 144  62  67 102  61  55  83  64  42  41  38  24  26  29  46  23  35  45  21  38  31 
 55 
 32 
igraph_clusters <- igraph::cut_at(clusters_out, n = 16)
table(igraph_clusters)
igraph_clusters
   1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16 
 404  109 1535  173 1056 1848  890  122  483  291   88   87   41   45   31   32 
cluster_by_marker <-
  table(igraph_clusters, colData(sce10x_yng)$markers)
cluster_by_marker
               
igraph_clusters  fap macrophage none stem
             1     0        402    0    2
             2     0        109    0    0
             3  1457          3   64   11
             4     6         17  126   24
             5     0          3   58  995
             6     5          8  146 1689
             7     7          3   68  812
             8    15          0   99    8
             9     0        480    2    1
             10    1          0   18  272
             11    1          1   25   61
             12    0          1    8   78
             13    4          5   22   10
             14    2          3   39    1
             15    2          2   27    0
             16    0          1   24    7
labels <- apply(cluster_by_marker, 1, which.max)
level_key_1 <- colnames(cluster_by_marker)[labels]
names(level_key_1) <- names(labels)
level_key_1
           1            2            3            4            5            6            7            8 
"macrophage" "macrophage"        "fap"       "none"       "stem"       "stem"       "stem"       "none" 
           9           10           11           12           13           14           15           16 
"macrophage"       "stem"       "stem"       "stem"       "none"       "none"       "none"       "none" 
table(level_key_1)
level_key_1
       fap macrophage       none       stem 
         1          3          6          6 
level_key_2 <- paste(level_key_1, names(level_key_1), sep = "_")
names(level_key_2) <- names(labels)
colData(sce10x_yng)$clusters_level1 <- recode(igraph_clusters, !!!level_key_1)
colData(sce10x_yng)$clusters_level2 <- recode(igraph_clusters, !!!level_key_2)
table(colData(sce10x_yng)$clusters_level1, colData(sce10x_yng)$sample)
            
             yng1 yng2 yng3
  fap         502  393  640
  macrophage  289  289  418
  none        185  158  101
  stem       1261 1936 1063
table(colData(sce10x_yng)$clusters_level2, colData(sce10x_yng)$sample)
              
               yng1 yng2 yng3
  fap_3         502  393  640
  macrophage_1  107  146  151
  macrophage_2    9   31   69
  macrophage_9  173  112  198
  none_13        15    8   18
  none_14        14   22    9
  none_15        23    3    5
  none_16         0    0   32
  none_4         96   74    3
  none_8         37   51   34
  stem_10        16  274    1
  stem_11         0    0   88
  stem_12        60   17   10
  stem_5        111  865   80
  stem_6       1066  766   16
  stem_7          8   14  868
dt <-
  bind_cols(
    colData(sce10x_yng) %>%
      as_tibble(),
    sce10x_umap_yng %>%
      as_tibble()
  )
p3 <-
  ggplot(dt, aes(umap1,
    umap2,
    colour = subsets_mitotic_detected >1
  )) +
  geom_jitter(
    width = .1,
    height = .1,
    size = 3,
    alpha = 1
  )
p3 + facet_wrap(~clusters_level2)

# ggsave(file.path(figures_dir, "umap_cosine_a160_b33_17928_louvain_clusters.pdf"), p3 )
colData(sce10x_yng)$clusters_level1[colData(sce10x_yng)$clusters_level2 =="stem_12"] <- "stem_mitotic"
dt <-
  bind_cols(
    colData(sce10x_yng) %>%
      as_tibble(),
    sce10x_umap_yng %>%
      as_tibble()
  )
p3 <-
  ggplot(dt, aes(umap1,
    umap2,
    colour = clusters_level1
  )) +
  geom_jitter(
    width = .1,
    height = .1,
    size = 3,
    alpha = 1
  )
p3 

# ggsave(file.path(figures_dir, "umap_cosine_a160_b33_17928_louvain_clusters.pdf"), p3 )

Cluster aged cells

sce10x_aged <- sce10x[, colData(sce10x)$condition == "aged"]
n_exprs_genes_aged <-
  nexprs(sce10x_aged,
    detection_limit = 5,
    byrow = TRUE
  )

table(n_exprs_genes_aged)[1:20]
n_exprs_genes_aged
    0     1     2     3     4     5     6     7     8     9    10    11    12    13    14    15    16    17 
25860  1574   714   507   365   253   220   195   175   129   137   108   122    90    80    71    71    83 
   18    19 
   66    71 
# t-UMAP is equivalent to a = 1, b = 1
# get the nearest neighbor data back
sce10x_aged_tumap <-
  tumap(t(assay(
    sce10x_aged[n_exprs_genes_aged >= 3, ],
    "counts"
  ) %>%
    as.matrix()),
  metric = "cosine",
  ret_nn = TRUE
  )
set.seed(42)
sce10x_umap_aged <-
  umap(t(assay(
    sce10x_aged[n_exprs_genes_aged >= 3, ],
    "counts"
  ) %>%
    as.matrix()),
  metric = "cosine",
  nn_method = sce10x_aged_tumap$nn,
  a = .6,
  b = .6
  )
colnames(sce10x_umap_aged) <- paste0("umap", seq(1, 2))
reducedDim(sce10x_aged, "umap") <- sce10x_umap_aged
dt <-
  bind_cols(
    colData(sce10x_aged) %>%
      as_tibble(),
    sce10x_umap_aged %>%
      as_tibble()
  )
p2 <-
  ggplot(dt) +
  geom_point(aes(umap1,
    umap2,
    colour = markers
  ),
  size = 1,
  alpha = 1
  )
p2

# ggsave(file.path(figures_dir, "umap_cosine_a160_b33_17928_cells_markers.pdf"), p2 )
set.seed(42)
g <- buildSNNGraph(sce10x_aged, k = 20, use.dimred = "umap")
clusters_out <- igraph::cluster_walktrap(g)
# colLabels(sce10x) <- factor(igraph::cluster_louvain(g)$membership)
table(clusters_out$membership)

  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27 
788 319 135 715  84 321 239 407 740  58 353 157 191 474 837 286 122 105 325 506 422 200 236 123 279 116 160 
 28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54 
 69 203 187  92  49  54  97  57 106  93  64 180  59  48  67  33  55  68  34  65  41  28  40  56  45  44  37 
 55 
 24 
igraph_clusters <- igraph::cut_at(clusters_out, n = 9)
table(igraph_clusters)
igraph_clusters
   1    2    3    4    5    6    7    8    9 
6639  131  343  674 2335  157   82  295   37 
cluster_by_marker <-
  table(igraph_clusters, colData(sce10x_aged)$markers)
cluster_by_marker
               
igraph_clusters  fap macrophage none stem
              1 5865         37  710   27
              2    0        127    3    1
              3    6          3  332    2
              4    6        664    3    1
              5   14         20  694 1607
              6   10          6  136    5
              7    3          2   74    3
              8   63          5  224    3
              9   32          0    5    0
labels <- apply(cluster_by_marker, 1, which.max)
level_key_1 <- colnames(cluster_by_marker)[labels]
names(level_key_1) <- names(labels)
level_key_1
           1            2            3            4            5            6            7            8 
       "fap" "macrophage"       "none" "macrophage"       "stem"       "none"       "none"       "none" 
           9 
       "fap" 
table(level_key_1)
level_key_1
       fap macrophage       none       stem 
         2          2          4          1 
level_key_2 <- paste(level_key_1, names(level_key_1), sep = "_")
names(level_key_2) <- names(labels)
level_key_2
             1              2              3              4              5              6              7 
       "fap_1" "macrophage_2"       "none_3" "macrophage_4"       "stem_5"       "none_6"       "none_7" 
             8              9 
      "none_8"        "fap_9" 
colData(sce10x_aged)$clusters_level1 <- recode(igraph_clusters, !!!level_key_1)
colData(sce10x_aged)$clusters_level2 <- recode(igraph_clusters, !!!level_key_2)
table(colData(sce10x_aged)$clusters_level1, colData(sce10x_aged)$sample)
            
             aged1 aged2 aged3 aged4
  fap         2027  2022  1583  1044
  macrophage    65   398   149   193
  none         168   345   143   221
  stem         545   692   337   761
table(colData(sce10x_aged)$clusters_level2, colData(sce10x_aged)$sample)
              
               aged1 aged2 aged3 aged4
  fap_1         1993  2021  1582  1043
  fap_9           34     1     1     1
  macrophage_2     0   128     0     3
  macrophage_4    65   270   149   190
  none_3          70    99   100    74
  none_6          36    85    10    26
  none_7          11    27     8    36
  none_8          51   134    25    85
  stem_5         545   692   337   761
dt <-
  bind_cols(
    colData(sce10x_aged) %>%
      as_tibble(),
    sce10x_umap_aged %>%
      as_tibble()
  )
p3 <-
  ggplot(dt, aes(umap1,
    umap2,
    colour = clusters_level1
  )) +
  geom_jitter(
    width = .1,
    height = .1,
    size = .7,
    alpha = 1
  )
p3 #+facet_wrap(~clusters_level2)

# ggsave(file.path(figures_dir, "umap_cosine_a160_b33_17928_louvain_clusters.pdf"), p3 )

Save data

meta_dt <-
  rbind(colData(sce10x_aged), colData(sce10x_yng))
colData(sce10x)$cluster_level2 <- paste(meta_dt$condition, meta_dt$clusters_level1, sep="_")
colData(sce10x) <- meta_dt
saveRDS(sce10x,
        file.path(
  data_dir,
  "preprocessed",
  "sce10x_filtered_cells.rds"))

Normalize and correct for batch effects

Discard non-marked and mitotic cells

sce10x <- sce10x[, colData(sce10x)$clusters_level1 %in% c("macrophage", "fap", "stem") ]
n_exprs_genes <-
  nexprs(sce10x,
    detection_limit = 0,
    byrow = TRUE
  )
table(n_exprs_genes)[1:20]
n_exprs_genes
   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19 
 981 2716 1434 1055  759  591  471  444  357  357  336  333  285  252  226  239  231  190  209  167 
sce10x <- sce10x[n_exprs_genes >0,]
sce10x
class: SingleCellExperiment 
dim: 33578 16520 
metadata(42): tximetaInfo quantInfo ... txomeInfo txdbInfo
assays(3): counts spliced unspliced
rownames(33578): Gm1992 Gm6123 ... mt-Cytb mt-Nd6
rowData names(9): ensembl_gene_id_version external_gene_name ... nexprs_l1_s keep
colnames(16520): GACTATGTCCGGCTTT_d1 ATGGTTGGTTGTAAAG_d1 ... GTCCCTCTCCCTCTAG_g3
  AAACGCTGTCGTTTAC_g3
colData names(22): sum detected ... clusters_level1 clusters_level2
reducedDimNames(0):
altExpNames(0):
colData(sce10x)$clusters_level2 <- 
  paste(colData(sce10x)$condition, 
        colData(sce10x)$clusters_level1, sep="_")

Normalize data across samples

sce10x <-
    multiBatchNorm(sce10x,
      batch = colData(sce10x)$sample
    )
n_exprs_genes <-
  nexprs(sce10x,
    detection_limit = 5,
    byrow = TRUE
  )
table(n_exprs_genes)[1:20]
n_exprs_genes
    0     1     2     3     4     5     6     7     8     9    10    11    12    13    14    15    16    17 
23438  1596   722   483   357   280   220   191   166   164   128   134    95    88   121    68    73    80 
   18    19 
   67    71 

Run fast mutual nearest neighbors correction

sce10x_corrected <-
  fastMNN(sce10x,
    batch = colData(sce10x)$sample,
    k = 20,
    d = 200,
    subset.row = names(n_exprs_genes)[n_exprs_genes >= 3]
  )
reducedDim(sce10x, "mnn_corrected") <- reducedDim(sce10x_corrected, "corrected")

Run UMAP

set.seed(42)
# t-UMAP is equivalent to a = 1, b = 1
# get the nearest neighbor data back
sce10x_tumap <-
  tumap(reducedDim(sce10x, 
                   "mnn_corrected"),
        metric = "cosine",
        ret_nn = TRUE)
set.seed(42)
sce10x_umap <-
  umap(reducedDim(sce10x,
                  "mnn_corrected"),
    metric = "cosine",
    nn_method = sce10x_tumap$nn,
    a = .8,
    b = .55
  )
colnames(sce10x_umap) <- paste0("umap", seq(1, 2))
reducedDim(sce10x, "mnn_umap") <- sce10x_umap
dt <-
  bind_cols(
    colData(sce10x) %>%
      as_tibble(),
    sce10x_umap %>%
      as_tibble()
  )
p2 <-
  ggplot(dt) +
  geom_point(aes(umap1,
    umap2,
    colour = clusters_level1
  ),
  size = .6,
  alpha = 1
  ) #+ facet_wrap(~condition)
p2 

ggsave(file.path(figures_dir, "mnn_umap.pdf"), p2 )
Saving 14 x 8 in image

Cluster stem cells

set.seed(42)
g <- buildSNNGraph(sce10x, k = 100, use.dimred = "mnn_umap")
clusters_out <- igraph::cluster_walktrap(g)
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 = .85,
    b = .43
  )
colnames(sce10x_stem_umap) <- paste0("umap", seq(1, 2))
reducedDim(sce10x_stem, "mnn_umap") <- sce10x_stem_umap
dt <-
  bind_cols(
    colData(sce10x_stem) %>%
      as_tibble(),
    sce10x_stem_umap %>%
      as_tibble()
  )
p2 <-
  ggplot(dt) +
  geom_point(aes(umap1,
    umap2,
    colour = clusters_level2
  ),
  size = 1,
  alpha =.6
  )
p2

# ggsave(file.path(figures_dir, "umap_cosine_a160_b33_17928_cells_markers.pdf"), p2 )
set.seed(42)
g <- buildSNNGraph(sce10x_stem, k = 100, use.dimred = "mnn_umap")
clusters_out <- igraph::cluster_walktrap(g)
igraph_clusters <- factor(clusters_out$membership)
table(igraph_clusters)
igraph_clusters
  1   2   3   4   5   6   7   8   9  10  11  12  13  14 
433 711 773 430 912 852 464 386 234 433 188 157 290 245 
igraph_clusters <- igraph::cut_at(clusters_out, n = 5)
table(igraph_clusters)
igraph_clusters
   1    2    3    4    5 
1931  621 2043 1061  852 
discard_cell <- sce10x_stem_umap[,1] < -10 | sce10x_stem_umap[,1] > 22  | (igraph_clusters ==2 & colData(sce10x_stem)$condition=="aged")
table(discard_cell)
discard_cell
FALSE  TRUE 
 6429    79 
igraph_clusters[discard_cell] <- 6
cluster_by_marker <-
  table(igraph_clusters, colData(sce10x_stem)$sample)
cluster_by_marker
               
igraph_clusters aged1 aged2 aged3 aged4 yng1 yng2 yng3
              1    20    86    52    85  495  902  291
              2     0     0     0     0  250  220  126
              3    98   178    79   196  356  606  530
              4   135   214   102   255   68  141   92
              5   268   204    93   203   30   44   10
              6    24    10    11    22    2    6    4
colData(sce10x_stem)$clusters <- factor(igraph_clusters)
sce10x_stem_subset <-
  sce10x_stem[, colData(sce10x_stem)$clusters!=6]
dt <-
  bind_cols(
    colData(sce10x_stem_subset) %>%
      as_tibble(),
    reducedDim(sce10x_stem_subset, "mnn_umap") %>%
      as_tibble()
  )
p2 <-
  ggplot(dt) +
  geom_point(aes(umap1,
    umap2,
    colour = clusters
  ),
  size = 1.7,
  alpha = 1
  )
p2  # + facet_wrap(~clusters)

ggsave(file.path(figures_dir, "mnn_umap_stem.pdf"), p2 )
Saving 14 x 10 in image

Cluster fap cells

sce10x_fap <- sce10x[,colData(sce10x)$clusters_level1 =="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 = .9,
    b = .78
  )
colnames(sce10x_fap_umap) <- paste0("umap", seq(1, 2))
reducedDim(sce10x_fap, "mnn_umap") <- sce10x_fap_umap
dt <-
  bind_cols(
    colData(sce10x_fap) %>%
      as_tibble(),
    sce10x_fap_umap %>%
      as_tibble()
  )
p2 <-
  ggplot(dt) +
  geom_point(aes(umap1,
    umap2,
    colour = clusters_level2
  ),
  size = 1,
  alpha =.6
  )
p2

# ggsave(file.path(figures_dir, "umap_cosine_a160_b33_17928_cells_markers.pdf"), p2 )
set.seed(42)
g <- buildSNNGraph(sce10x_fap, k = 100, use.dimred = "mnn_umap")
clusters_out <- igraph::cluster_walktrap(g)
igraph_clusters <- factor(clusters_out$membership)
table(igraph_clusters)
igraph_clusters
   1    2    3    4    5    6    7    8    9   10 
1217  889 1246  664 1710 1383  413  304  239  146 
igraph_clusters <- igraph::cut_at(clusters_out, n = 3)
table(igraph_clusters)
igraph_clusters
   1    2    3 
6102 1963  146 
discard_cell <-  sce10x_fap_umap[,1] > 5
table(discard_cell)
discard_cell
FALSE  TRUE 
 8149    62 
igraph_clusters[discard_cell] <- 4
cluster_by_marker <-
  table(igraph_clusters, colData(sce10x_fap)$sample)
cluster_by_marker
               
igraph_clusters aged1 aged2 aged3 aged4 yng1 yng2 yng3
              1  1608  1562  1120   824  289  273  364
              2   369   419   434   189  196  107  249
              3    43    28    14    14   13   10   24
              4     7    13    15    17    4    3    3
colData(sce10x_fap)$clusters <- factor(igraph_clusters)
sce10x_fap_subset <-
  sce10x_fap[, colData(sce10x_fap)$clusters!=4]
dt <-
  bind_cols(
    colData(sce10x_fap_subset) %>%
      as_tibble(),
    reducedDim(sce10x_fap_subset, "mnn_umap") %>%
      as_tibble()
  )
p2 <-
  ggplot(dt) +
  geom_point(aes(umap1,
    umap2,
    colour = clusters
  ),
  size = 1.7,
  alpha = 1
  )
p2  # + facet_wrap(~clusters)

ggsave(file.path(figures_dir, "mnn_umap_fap.pdf"), p2 )
Saving 14 x 10 in image

Cluster macrophage cells

sce10x_macrophage <- sce10x[,colData(sce10x)$clusters_level1 =="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 = 0.75,
    b = 0.65
  )
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()
  )
p2 <-
  ggplot(dt) +
  geom_point(aes(umap1,
    umap2,
    colour = clusters_level2
  ),
  size = 1,
  alpha =.6
  )
p2

# ggsave(file.path(figures_dir, "umap_cosine_a160_b33_17928_cells_markers.pdf"), p2 )
set.seed(42)
g <- buildSNNGraph(sce10x_macrophage, k = 50, use.dimred = "mnn_umap")
clusters_out <- igraph::cluster_walktrap(g)
igraph_clusters <- factor(clusters_out$membership)
table(igraph_clusters)
igraph_clusters
  1   2   3   4   5   6   7   8   9  10  11  12 
224 215 253 193 246 158 108  73  93  78  62  98 
igraph_clusters <- igraph::cut_at(clusters_out, n = 5)
table(igraph_clusters)
igraph_clusters
   1    2    3    4    5 
 134 1156  305  108   98 
cluster_by_marker <-
  table(igraph_clusters, colData(sce10x_macrophage)$sample)
cluster_by_marker
               
igraph_clusters aged1 aged2 aged3 aged4 yng1 yng2 yng3
              1     0    24     4     2   10   69   25
              2    37   138   137    89  265  171  319
              3    21    83     8    98    7   34   54
              4     7    56     0     3    7   15   20
              5     0    97     0     1    0    0    0
colData(sce10x_macrophage)$clusters <- factor(igraph_clusters)
sce10x_macrophage_subset <-
  sce10x_macrophage[, colData(sce10x_macrophage)$clusters %in% c(1,2, 3)]
dt <-
  bind_cols(
    colData(sce10x_macrophage_subset) %>%
      as_tibble(),
    reducedDim(sce10x_macrophage_subset, "mnn_umap") %>%
      as_tibble()
  )
p2 <-
  ggplot(dt) +
  geom_point(aes(umap1,
    umap2,
    colour = clusters
  ),
  size = 1.7,
  alpha = 1
  )
p2  # + facet_wrap(~clusters)

ggsave(file.path(figures_dir, "mnn_umap_macrophage.pdf"), p2 )
Saving 14 x 10 in image

Save data

meta_dt <-
  rbind(colData(sce10x_stem_subset), colData(sce10x_fap_subset), colData(sce10x_macrophage_subset)) %>%
  as_tibble(rownames = "cell") %>%
  mutate(cluster_level1_1= paste(clusters_level1, clusters, sep="_" ),
         cluster_level2_1= paste(clusters_level2, clusters, sep="_" )) %>%
  select(cell, cluster_level1_1, cluster_level2_1 )
meta_dt
colData(sce10x) <-
  left_join(colData(sce10x)%>%
  as_tibble(rownames = "cell"),
  meta_dt,
  by="cell") %>%
  replace_na(list(cluster_level2_1 = "discard", cluster_level1_1 = "discard")) %>%
  select(-clusters) %>%
  column_to_rownames("cell") %>%
  DataFrame(.)

discard_idx <- 
  colData(sce10x)$cluster_level2_1 == "discard"
table(discard_idx)
discard_idx
FALSE  TRUE 
16173   347 
sce10x <- sce10x[, !discard_idx]
n_exprs_genes <-
  nexprs(sce10x,
    detection_limit = 0,
    byrow = TRUE
  )

table(n_exprs_genes)[1:20]
n_exprs_genes
   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19 
 104 2706 1447 1072  738  601  471  433  372  344  344  329  283  236  233  239  218  205  198  171 
sce10x <- sce10x[n_exprs_genes>0, ]
saveRDS(
  sce10x,
  file.path(
    data_dir,
    "preprocessed",
    "sce10x_filtered_final.rds"
  )
)

make plots

dt <-
  bind_cols(
    colData(sce10x) %>%
      as_tibble(),
    reducedDim(sce10x, "mnn_umap") %>%
      as_tibble()
  )
p2 <-
  ggplot(dt) +
  geom_point(aes(umap1,
    umap2,
    colour = cluster_level1_1
  ),
  size = .6,
  alpha = 1
  ) + facet_wrap(~condition)
p2 

ggsave(file.path(figures_dir, "mnn_umap_split.pdf"), p2 )
Saving 14 x 8 in image
sessionInfo()
R version 4.0.0 (2020-04-24)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.4 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] parallel  stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] BiocSingular_1.4.0          batchelor_1.4.0             dbscan_1.1-5               
 [4] uwot_0.1.8                  Matrix_1.2-18               forcats_0.5.0              
 [7] stringr_1.4.0               dplyr_0.8.5                 purrr_0.3.4                
[10] readr_1.3.1                 tidyr_1.1.0                 tibble_3.0.1               
[13] tidyverse_1.3.0             scran_1.16.0                scater_1.16.0              
[16] ggplot2_3.3.0               SingleCellExperiment_1.10.1 SummarizedExperiment_1.18.1
[19] DelayedArray_0.14.0         matrixStats_0.56.0          Biobase_2.48.0             
[22] GenomicRanges_1.40.0        GenomeInfoDb_1.24.0         IRanges_2.22.2             
[25] S4Vectors_0.26.1            BiocGenerics_0.34.0        

loaded via a namespace (and not attached):
 [1] nlme_3.1-147              fs_1.4.1                  bitops_1.0-6              lubridate_1.7.8          
 [5] RcppAnnoy_0.0.16          httr_1.4.1                rprojroot_1.3-2           tools_4.0.0              
 [9] backports_1.1.7           utf8_1.1.4                R6_2.4.1                  irlba_2.3.3              
[13] vipor_0.4.5               DBI_1.1.0                 colorspace_1.4-1          withr_2.2.0              
[17] tidyselect_1.1.0          gridExtra_2.3             compiler_4.0.0            cli_2.0.2                
[21] rvest_0.3.5               BiocNeighbors_1.6.0       xml2_1.3.2                labeling_0.3             
[25] scales_1.1.1              digest_0.6.25             rmarkdown_2.1             XVector_0.28.0           
[29] base64enc_0.1-3           pkgconfig_2.0.3           htmltools_0.4.0           dbplyr_1.4.4             
[33] limma_3.44.1              rlang_0.4.6               readxl_1.3.1              rstudioapi_0.11          
[37] DelayedMatrixStats_1.10.0 farver_2.0.3              generics_0.0.2            jsonlite_1.6.1           
[41] BiocParallel_1.22.0       RCurl_1.98-1.2            magrittr_1.5              GenomeInfoDbData_1.2.3   
[45] fansi_0.4.1               Rcpp_1.0.4.6              ggbeeswarm_0.6.0          munsell_0.5.0            
[49] viridis_0.5.1             lifecycle_0.2.0           stringi_1.4.6             yaml_2.2.1               
[53] edgeR_3.30.0              zlibbioc_1.34.0           grid_4.0.0                blob_1.2.1               
[57] dqrng_0.2.1               crayon_1.3.4              lattice_0.20-41           haven_2.3.0              
[61] hms_0.5.3                 locfit_1.5-9.4            knitr_1.28                pillar_1.4.4             
[65] igraph_1.2.5              codetools_0.2-16          reprex_0.3.0              glue_1.4.1               
[69] evaluate_0.14             modelr_0.1.8              vctrs_0.3.0               cellranger_1.1.0         
[73] gtable_0.3.0              assertthat_0.2.1          xfun_0.14                 rsvd_1.0.3               
[77] broom_0.5.6               RSpectra_0.16-0           viridisLite_0.3.0         beeswarm_0.2.3           
[81] statmod_1.4.34            ellipsis_0.3.1           
LS0tCnRpdGxlOiAiTW91c2UgTXVzY2xlIFN0ZW0gQ2VsbCBQcm9qZWN0ICIKc3VidGl0bGU6ICJQYXJ0IDM6IGNsdXN0ZXIgY2VsbHMiCmF1dGhvcjogCi0gbmFtZTogUmljayBGYXJvdW5pCiAgYWZmaWxpYXRpb246CiAgLSAmY3J1ayBHw6lub21lIFF1w6liZWMgSW5ub3ZhdGlvbiBDZW50cmUsIE1jR2lsbCBVbml2ZXJzaXR5LCBNb250cmVhbCwgQ2FuYWRhCmRhdGU6ICdgciBmb3JtYXQoU3lzLkRhdGUoKSwgIiVZLSVCLSVkIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0b2M6IG5vCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKCgojIFByZXBhcmUgYW5hbHlzaXMgd29ya2Zsb3cKCiMjIFNldCBmaWxlcGF0aHMgYW5kIHBhcmFtZXRlcnMKCmBgYHtyIHNldHVwfQpzZXQuc2VlZCg0MikKa25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBycHJvanJvb3Q6OmZpbmRfcnN0dWRpb19yb290X2ZpbGUoKSkKb3B0aW9ucygKICByZWFkci5zaG93X3Byb2dyZXNzID0gRkFMU0UsCiAgZGlnaXRzID0gMgopCmBgYAoKIyMgTG9hZCBwYWNrYWdlcwpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KHNjYXRlcikKICBsaWJyYXJ5KHNjcmFuKQogIGxpYnJhcnkodGlkeXZlcnNlKQogIGxpYnJhcnkodXdvdCkgIyBGb3IgdW1hcAogIGxpYnJhcnkoZGJzY2FuKQogIGxpYnJhcnkoYmF0Y2hlbG9yKQogIGxpYnJhcnkoQmlvY1Npbmd1bGFyKQp9KQp0aGVtZV9zZXQodGhlbWVfYncoKSkKYGBgCgojIyBEZWZpbmUgZmlsZSBwYXRocwoKYGBge3J9CmRhdGFfZGlyIDwtICIuL2RhdGEiCmZpZ3VyZXNfZGlyIDwtIGZpbGUucGF0aCgiLi9maWd1cmVzIiwgInFjX2NlbGxzIikKYGBgCgoKIyMgTG9hZCBkYXRhCgpgYGB7cn0Kc2NlMTB4IDwtCiAgcmVhZFJEUyhmaWxlLnBhdGgoCiAgICBkYXRhX2RpciwKICAgICJwcmVwcm9jZXNzZWQiLAogICAgInNjZTEweF9maWx0ZXJlZF9jZWxscy5yZHMiCiAgKSkKYGBgCgoKCmBgYHtyfQptaXRvdGljX2R0PC0KICBwZXJDZWxsUUNNZXRyaWNzKAogICAgICBzY2UxMHgsCiAgICAgIHN1YnNldHMgPSBsaXN0KG1pdG90aWMgPSB3aGljaChyb3dEYXRhKHNjZTEweCkkZ2VuZV9uYW1lICVpbiUgYygiQ2NuYTIiLCAiQ2NuYjEiLCAiQ2NuYjIiKSkpLAogICAgICBwZXJjZW50X3RvcCA9TlVMTCwKICAgICAgZXhwcnNfdmFsdWVzID0gYygiY291bnRzIiksCiAgICAgIGZsYXR0ZW4gPSBUUlVFCiAgICApICU+JQogICAgYXNfdGliYmxlKCkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KHN1YnNldHNfbWl0b3RpY19zdW0sIHN1YnNldHNfbWl0b3RpY19kZXRlY3RlZCwgc3Vic2V0c19taXRvdGljX3BlcmNlbnQpCm1pdG90aWNfZHQKYGBgCgoKCmBgYHtyfQpjb2xEYXRhKHNjZTEweCkgPC0KICBjYmluZChjb2xEYXRhKHNjZTEweCksIG1pdG90aWNfZHQpCmBgYAoKIyBDbHVzdGVyIGNlbGwgdHlwZXMKCiMjIENsdXN0ZXIgeW91bmcgY2VsbHMKCmBgYHtyfQpzY2UxMHhfeW5nIDwtIHNjZTEweFssIGNvbERhdGEoc2NlMTB4KSRjb25kaXRpb24gPT0gInluZyJdCmBgYAoKYGBge3J9Cm5fZXhwcnNfZ2VuZXNfeW5nIDwtCiAgbmV4cHJzKHNjZTEweF95bmcsCiAgICBkZXRlY3Rpb25fbGltaXQgPSA1LAogICAgYnlyb3cgPSBUUlVFCiAgKQoKdGFibGUobl9leHByc19nZW5lc195bmcpWzE6MjBdCmBgYAoKCgoKYGBge3J9CnNldC5zZWVkKDQyKQojIHQtVU1BUCBpcyBlcXVpdmFsZW50IHRvIGEgPSAxLCBiID0gMQojIGdldCB0aGUgbmVhcmVzdCBuZWlnaGJvciBkYXRhIGJhY2sKc2NlMTB4X3luZ190dW1hcCA8LQogIHR1bWFwKHQoYXNzYXkoc2NlMTB4X3luZ1tuX2V4cHJzX2dlbmVzX3luZyA+PSAzLCBdLCAiY291bnRzIikgJT4lCiAgICBhcy5tYXRyaXgoKSksCiAgbWV0cmljID0gImNvc2luZSIsCiAgcmV0X25uID0gVFJVRQogICkKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoNDIpCnNjZTEweF91bWFwX3luZyA8LQogIHVtYXAodChhc3NheSgKICAgIHNjZTEweF95bmdbbl9leHByc19nZW5lc195bmcgPj0gMywgXSwKICAgICJjb3VudHMiCiAgKSAlPiUKICAgIGFzLm1hdHJpeCgpKSwKICBtZXRyaWMgPSAiY29zaW5lIiwKICBubl9tZXRob2QgPSBzY2UxMHhfeW5nX3R1bWFwJG5uLAogIGEgPSAuNiwKICBiID0gLjU1CiAgKQpjb2xuYW1lcyhzY2UxMHhfdW1hcF95bmcpIDwtIHBhc3RlMCgidW1hcCIsIHNlcSgxLCAyKSkKcmVkdWNlZERpbShzY2UxMHhfeW5nLCAidW1hcCIpIDwtIHNjZTEweF91bWFwX3luZwpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNH0KZHQgPC0KICBiaW5kX2NvbHMoCiAgICBjb2xEYXRhKHNjZTEweF95bmcpICU+JQogICAgICBhc190aWJibGUoKSwKICAgIHNjZTEweF91bWFwX3luZyAlPiUKICAgICAgYXNfdGliYmxlKCkKICApCnAyIDwtCiAgZ2dwbG90KGR0KSArCiAgZ2VvbV9wb2ludChhZXModW1hcDEsCiAgICB1bWFwMiwKICAgIGNvbG91ciA9IG1hcmtlcnMKICApLAogIHNpemUgPSAzLAogIGFscGhhID0gMQogICkKcDIKIyBnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCAidW1hcF9jb3NpbmVfYTE2MF9iMzNfMTc5MjhfY2VsbHNfbWFya2Vycy5wZGYiKSwgcDIgKQpgYGAKCgpgYGB7cn0Kc2V0LnNlZWQoNDIpCmcgPC0gYnVpbGRTTk5HcmFwaChzY2UxMHhfeW5nLCBrID0gMjAsIHVzZS5kaW1yZWQgPSAidW1hcCIpCmBgYAoKYGBge3J9CmNsdXN0ZXJzX291dCA8LSBpZ3JhcGg6OmNsdXN0ZXJfd2Fsa3RyYXAoZykKIyBjb2xMYWJlbHMoc2NlMTB4KSA8LSBmYWN0b3IoaWdyYXBoOjpjbHVzdGVyX2xvdXZhaW4oZykkbWVtYmVyc2hpcCkKdGFibGUoY2x1c3RlcnNfb3V0JG1lbWJlcnNoaXApCmBgYAoKCmBgYHtyfQppZ3JhcGhfY2x1c3RlcnMgPC0gaWdyYXBoOjpjdXRfYXQoY2x1c3RlcnNfb3V0LCBuID0gMTYpCnRhYmxlKGlncmFwaF9jbHVzdGVycykKYGBgCgoKYGBge3J9CmNsdXN0ZXJfYnlfbWFya2VyIDwtCiAgdGFibGUoaWdyYXBoX2NsdXN0ZXJzLCBjb2xEYXRhKHNjZTEweF95bmcpJG1hcmtlcnMpCmNsdXN0ZXJfYnlfbWFya2VyCmBgYAoKCgpgYGB7cn0KbGFiZWxzIDwtIGFwcGx5KGNsdXN0ZXJfYnlfbWFya2VyLCAxLCB3aGljaC5tYXgpCmxldmVsX2tleV8xIDwtIGNvbG5hbWVzKGNsdXN0ZXJfYnlfbWFya2VyKVtsYWJlbHNdCm5hbWVzKGxldmVsX2tleV8xKSA8LSBuYW1lcyhsYWJlbHMpCmxldmVsX2tleV8xCmBgYApgYGB7cn0KdGFibGUobGV2ZWxfa2V5XzEpCmBgYAoKYGBge3J9CmxldmVsX2tleV8yIDwtIHBhc3RlKGxldmVsX2tleV8xLCBuYW1lcyhsZXZlbF9rZXlfMSksIHNlcCA9ICJfIikKbmFtZXMobGV2ZWxfa2V5XzIpIDwtIG5hbWVzKGxhYmVscykKY29sRGF0YShzY2UxMHhfeW5nKSRjbHVzdGVyc19sZXZlbDEgPC0gcmVjb2RlKGlncmFwaF9jbHVzdGVycywgISEhbGV2ZWxfa2V5XzEpCmNvbERhdGEoc2NlMTB4X3luZykkY2x1c3RlcnNfbGV2ZWwyIDwtIHJlY29kZShpZ3JhcGhfY2x1c3RlcnMsICEhIWxldmVsX2tleV8yKQp0YWJsZShjb2xEYXRhKHNjZTEweF95bmcpJGNsdXN0ZXJzX2xldmVsMSwgY29sRGF0YShzY2UxMHhfeW5nKSRzYW1wbGUpCmBgYAoKYGBge3J9CnRhYmxlKGNvbERhdGEoc2NlMTB4X3luZykkY2x1c3RlcnNfbGV2ZWwyLCBjb2xEYXRhKHNjZTEweF95bmcpJHNhbXBsZSkKYGBgCgoKYGBge3IgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE0fQpkdCA8LQogIGJpbmRfY29scygKICAgIGNvbERhdGEoc2NlMTB4X3luZykgJT4lCiAgICAgIGFzX3RpYmJsZSgpLAogICAgc2NlMTB4X3VtYXBfeW5nICU+JQogICAgICBhc190aWJibGUoKQogICkKcDMgPC0KICBnZ3Bsb3QoZHQsIGFlcyh1bWFwMSwKICAgIHVtYXAyLAogICAgY29sb3VyID0gc3Vic2V0c19taXRvdGljX2RldGVjdGVkID4xCiAgKSkgKwogIGdlb21faml0dGVyKAogICAgd2lkdGggPSAuMSwKICAgIGhlaWdodCA9IC4xLAogICAgc2l6ZSA9IDMsCiAgICBhbHBoYSA9IDEKICApCnAzICsgZmFjZXRfd3JhcCh+Y2x1c3RlcnNfbGV2ZWwyKQojIGdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJ1bWFwX2Nvc2luZV9hMTYwX2IzM18xNzkyOF9sb3V2YWluX2NsdXN0ZXJzLnBkZiIpLCBwMyApCmBgYAoKCmBgYHtyfQpjb2xEYXRhKHNjZTEweF95bmcpJGNsdXN0ZXJzX2xldmVsMVtjb2xEYXRhKHNjZTEweF95bmcpJGNsdXN0ZXJzX2xldmVsMiA9PSJzdGVtXzEyIl0gPC0gInN0ZW1fbWl0b3RpYyIKYGBgCgoKYGBge3IgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTE0fQpkdCA8LQogIGJpbmRfY29scygKICAgIGNvbERhdGEoc2NlMTB4X3luZykgJT4lCiAgICAgIGFzX3RpYmJsZSgpLAogICAgc2NlMTB4X3VtYXBfeW5nICU+JQogICAgICBhc190aWJibGUoKQogICkKcDMgPC0KICBnZ3Bsb3QoZHQsIGFlcyh1bWFwMSwKICAgIHVtYXAyLAogICAgY29sb3VyID0gY2x1c3RlcnNfbGV2ZWwxCiAgKSkgKwogIGdlb21faml0dGVyKAogICAgd2lkdGggPSAuMSwKICAgIGhlaWdodCA9IC4xLAogICAgc2l6ZSA9IDMsCiAgICBhbHBoYSA9IDEKICApCnAzIAojIGdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJ1bWFwX2Nvc2luZV9hMTYwX2IzM18xNzkyOF9sb3V2YWluX2NsdXN0ZXJzLnBkZiIpLCBwMyApCmBgYAoKIyMgQ2x1c3RlciBhZ2VkIGNlbGxzCgpgYGB7cn0Kc2NlMTB4X2FnZWQgPC0gc2NlMTB4WywgY29sRGF0YShzY2UxMHgpJGNvbmRpdGlvbiA9PSAiYWdlZCJdCmBgYAoKYGBge3J9Cm5fZXhwcnNfZ2VuZXNfYWdlZCA8LQogIG5leHBycyhzY2UxMHhfYWdlZCwKICAgIGRldGVjdGlvbl9saW1pdCA9IDUsCiAgICBieXJvdyA9IFRSVUUKICApCgp0YWJsZShuX2V4cHJzX2dlbmVzX2FnZWQpWzE6MjBdCmBgYAoKYGBge3J9CiMgdC1VTUFQIGlzIGVxdWl2YWxlbnQgdG8gYSA9IDEsIGIgPSAxCiMgZ2V0IHRoZSBuZWFyZXN0IG5laWdoYm9yIGRhdGEgYmFjawpzY2UxMHhfYWdlZF90dW1hcCA8LQogIHR1bWFwKHQoYXNzYXkoCiAgICBzY2UxMHhfYWdlZFtuX2V4cHJzX2dlbmVzX2FnZWQgPj0gMywgXSwKICAgICJjb3VudHMiCiAgKSAlPiUKICAgIGFzLm1hdHJpeCgpKSwKICBtZXRyaWMgPSAiY29zaW5lIiwKICByZXRfbm4gPSBUUlVFCiAgKQpgYGAKCgpgYGB7cn0Kc2V0LnNlZWQoNDIpCnNjZTEweF91bWFwX2FnZWQgPC0KICB1bWFwKHQoYXNzYXkoCiAgICBzY2UxMHhfYWdlZFtuX2V4cHJzX2dlbmVzX2FnZWQgPj0gMywgXSwKICAgICJjb3VudHMiCiAgKSAlPiUKICAgIGFzLm1hdHJpeCgpKSwKICBtZXRyaWMgPSAiY29zaW5lIiwKICBubl9tZXRob2QgPSBzY2UxMHhfYWdlZF90dW1hcCRubiwKICBhID0gLjYsCiAgYiA9IC42CiAgKQpjb2xuYW1lcyhzY2UxMHhfdW1hcF9hZ2VkKSA8LSBwYXN0ZTAoInVtYXAiLCBzZXEoMSwgMikpCnJlZHVjZWREaW0oc2NlMTB4X2FnZWQsICJ1bWFwIikgPC0gc2NlMTB4X3VtYXBfYWdlZApgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNH0KZHQgPC0KICBiaW5kX2NvbHMoCiAgICBjb2xEYXRhKHNjZTEweF9hZ2VkKSAlPiUKICAgICAgYXNfdGliYmxlKCksCiAgICBzY2UxMHhfdW1hcF9hZ2VkICU+JQogICAgICBhc190aWJibGUoKQogICkKcDIgPC0KICBnZ3Bsb3QoZHQpICsKICBnZW9tX3BvaW50KGFlcyh1bWFwMSwKICAgIHVtYXAyLAogICAgY29sb3VyID0gbWFya2VycwogICksCiAgc2l6ZSA9IDEsCiAgYWxwaGEgPSAxCiAgKQpwMgojIGdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJ1bWFwX2Nvc2luZV9hMTYwX2IzM18xNzkyOF9jZWxsc19tYXJrZXJzLnBkZiIpLCBwMiApCmBgYAoKCgoKCmBgYHtyfQpzZXQuc2VlZCg0MikKZyA8LSBidWlsZFNOTkdyYXBoKHNjZTEweF9hZ2VkLCBrID0gMjAsIHVzZS5kaW1yZWQgPSAidW1hcCIpCmBgYAoKYGBge3J9CmNsdXN0ZXJzX291dCA8LSBpZ3JhcGg6OmNsdXN0ZXJfd2Fsa3RyYXAoZykKIyBjb2xMYWJlbHMoc2NlMTB4KSA8LSBmYWN0b3IoaWdyYXBoOjpjbHVzdGVyX2xvdXZhaW4oZykkbWVtYmVyc2hpcCkKdGFibGUoY2x1c3RlcnNfb3V0JG1lbWJlcnNoaXApCmBgYAoKYGBge3J9CmlncmFwaF9jbHVzdGVycyA8LSBpZ3JhcGg6OmN1dF9hdChjbHVzdGVyc19vdXQsIG4gPSA5KQp0YWJsZShpZ3JhcGhfY2x1c3RlcnMpCmBgYAoKCmBgYHtyfQpjbHVzdGVyX2J5X21hcmtlciA8LQogIHRhYmxlKGlncmFwaF9jbHVzdGVycywgY29sRGF0YShzY2UxMHhfYWdlZCkkbWFya2VycykKY2x1c3Rlcl9ieV9tYXJrZXIKYGBgCgoKCmBgYHtyfQpsYWJlbHMgPC0gYXBwbHkoY2x1c3Rlcl9ieV9tYXJrZXIsIDEsIHdoaWNoLm1heCkKbGV2ZWxfa2V5XzEgPC0gY29sbmFtZXMoY2x1c3Rlcl9ieV9tYXJrZXIpW2xhYmVsc10KbmFtZXMobGV2ZWxfa2V5XzEpIDwtIG5hbWVzKGxhYmVscykKbGV2ZWxfa2V5XzEKYGBgCmBgYHtyfQp0YWJsZShsZXZlbF9rZXlfMSkKYGBgCgpgYGB7cn0KbGV2ZWxfa2V5XzIgPC0gcGFzdGUobGV2ZWxfa2V5XzEsIG5hbWVzKGxldmVsX2tleV8xKSwgc2VwID0gIl8iKQpuYW1lcyhsZXZlbF9rZXlfMikgPC0gbmFtZXMobGFiZWxzKQpsZXZlbF9rZXlfMgpgYGAKCmBgYHtyfQpjb2xEYXRhKHNjZTEweF9hZ2VkKSRjbHVzdGVyc19sZXZlbDEgPC0gcmVjb2RlKGlncmFwaF9jbHVzdGVycywgISEhbGV2ZWxfa2V5XzEpCmNvbERhdGEoc2NlMTB4X2FnZWQpJGNsdXN0ZXJzX2xldmVsMiA8LSByZWNvZGUoaWdyYXBoX2NsdXN0ZXJzLCAhISFsZXZlbF9rZXlfMikKYGBgCmBgYHtyfQp0YWJsZShjb2xEYXRhKHNjZTEweF9hZ2VkKSRjbHVzdGVyc19sZXZlbDEsIGNvbERhdGEoc2NlMTB4X2FnZWQpJHNhbXBsZSkKYGBgCmBgYHtyfQp0YWJsZShjb2xEYXRhKHNjZTEweF9hZ2VkKSRjbHVzdGVyc19sZXZlbDIsIGNvbERhdGEoc2NlMTB4X2FnZWQpJHNhbXBsZSkKYGBgCgoKCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTR9CmR0IDwtCiAgYmluZF9jb2xzKAogICAgY29sRGF0YShzY2UxMHhfYWdlZCkgJT4lCiAgICAgIGFzX3RpYmJsZSgpLAogICAgc2NlMTB4X3VtYXBfYWdlZCAlPiUKICAgICAgYXNfdGliYmxlKCkKICApCnAzIDwtCiAgZ2dwbG90KGR0LCBhZXModW1hcDEsCiAgICB1bWFwMiwKICAgIGNvbG91ciA9IGNsdXN0ZXJzX2xldmVsMQogICkpICsKICBnZW9tX2ppdHRlcigKICAgIHdpZHRoID0gLjEsCiAgICBoZWlnaHQgPSAuMSwKICAgIHNpemUgPSAuNywKICAgIGFscGhhID0gMQogICkKcDMgIytmYWNldF93cmFwKH5jbHVzdGVyc19sZXZlbDIpCiMgZ2dzYXZlKGZpbGUucGF0aChmaWd1cmVzX2RpciwgInVtYXBfY29zaW5lX2ExNjBfYjMzXzE3OTI4X2xvdXZhaW5fY2x1c3RlcnMucGRmIiksIHAzICkKYGBgCgojIyBTYXZlIGRhdGEKCgpgYGB7cn0KbWV0YV9kdCA8LQogIHJiaW5kKGNvbERhdGEoc2NlMTB4X2FnZWQpLCBjb2xEYXRhKHNjZTEweF95bmcpKQpjb2xEYXRhKHNjZTEweCkgPC0gbWV0YV9kdApgYGAKCgpgYGB7cn0Kc2F2ZVJEUyhzY2UxMHgsCiAgICAgICAgZmlsZS5wYXRoKAogIGRhdGFfZGlyLAogICJwcmVwcm9jZXNzZWQiLAogICJzY2UxMHhfZmlsdGVyZWRfY2VsbHMucmRzIikpCmBgYAoKCiMgTm9ybWFsaXplIGFuZCBjb3JyZWN0IGZvciBiYXRjaCBlZmZlY3RzCgojIyBEaXNjYXJkIG5vbi1tYXJrZWQgYW5kIG1pdG90aWMgY2VsbHMKCgpgYGB7cn0Kc2NlMTB4IDwtIHNjZTEweFssIGNvbERhdGEoc2NlMTB4KSRjbHVzdGVyc19sZXZlbDEgJWluJSBjKCJtYWNyb3BoYWdlIiwgImZhcCIsICJzdGVtIikgXQpgYGAKCmBgYHtyfQpuX2V4cHJzX2dlbmVzIDwtCiAgbmV4cHJzKHNjZTEweCwKICAgIGRldGVjdGlvbl9saW1pdCA9IDAsCiAgICBieXJvdyA9IFRSVUUKICApCnRhYmxlKG5fZXhwcnNfZ2VuZXMpWzE6MjBdCmBgYAoKYGBge3J9CnNjZTEweCA8LSBzY2UxMHhbbl9leHByc19nZW5lcyA+MCxdCnNjZTEweApgYGAKCgoKYGBge3J9CmNvbERhdGEoc2NlMTB4KSRjbHVzdGVyc19sZXZlbDIgPC0gCiAgcGFzdGUoY29sRGF0YShzY2UxMHgpJGNvbmRpdGlvbiwgCiAgICAgICAgY29sRGF0YShzY2UxMHgpJGNsdXN0ZXJzX2xldmVsMSwgc2VwPSJfIikKCmBgYAoKIyMgTm9ybWFsaXplIGRhdGEgYWNyb3NzIHNhbXBsZXMKCmBgYHtyfQpzY2UxMHggPC0KICAgIG11bHRpQmF0Y2hOb3JtKHNjZTEweCwKICAgICAgYmF0Y2ggPSBjb2xEYXRhKHNjZTEweCkkc2FtcGxlCiAgICApCmBgYAoKYGBge3J9Cm5fZXhwcnNfZ2VuZXMgPC0KICBuZXhwcnMoc2NlMTB4LAogICAgZGV0ZWN0aW9uX2xpbWl0ID0gNSwKICAgIGJ5cm93ID0gVFJVRQogICkKdGFibGUobl9leHByc19nZW5lcylbMToyMF0KYGBgCgojIyBSdW4gZmFzdCBtdXR1YWwgbmVhcmVzdCBuZWlnaGJvcnMgY29ycmVjdGlvbgoKYGBge3J9CnNjZTEweF9jb3JyZWN0ZWQgPC0KICBmYXN0TU5OKHNjZTEweCwKICAgIGJhdGNoID0gY29sRGF0YShzY2UxMHgpJHNhbXBsZSwKICAgIGsgPSAyMCwKICAgIGQgPSAyMDAsCiAgICBzdWJzZXQucm93ID0gbmFtZXMobl9leHByc19nZW5lcylbbl9leHByc19nZW5lcyA+PSAzXQogICkKYGBgCgpgYGB7cn0KcmVkdWNlZERpbShzY2UxMHgsICJtbm5fY29ycmVjdGVkIikgPC0gcmVkdWNlZERpbShzY2UxMHhfY29ycmVjdGVkLCAiY29ycmVjdGVkIikKYGBgCgoKIyMgUnVuIFVNQVAKCgoKYGBge3J9CnNldC5zZWVkKDQyKQojIHQtVU1BUCBpcyBlcXVpdmFsZW50IHRvIGEgPSAxLCBiID0gMQojIGdldCB0aGUgbmVhcmVzdCBuZWlnaGJvciBkYXRhIGJhY2sKc2NlMTB4X3R1bWFwIDwtCiAgdHVtYXAocmVkdWNlZERpbShzY2UxMHgsIAogICAgICAgICAgICAgICAgICAgIm1ubl9jb3JyZWN0ZWQiKSwKICAgICAgICBtZXRyaWMgPSAiY29zaW5lIiwKICAgICAgICByZXRfbm4gPSBUUlVFKQpgYGAKCmBgYHtyfQpzZXQuc2VlZCg0MikKc2NlMTB4X3VtYXAgPC0KICB1bWFwKHJlZHVjZWREaW0oc2NlMTB4LAogICAgICAgICAgICAgICAgICAibW5uX2NvcnJlY3RlZCIpLAogICAgbWV0cmljID0gImNvc2luZSIsCiAgICBubl9tZXRob2QgPSBzY2UxMHhfdHVtYXAkbm4sCiAgICBhID0gLjgsCiAgICBiID0gLjU1CiAgKQpjb2xuYW1lcyhzY2UxMHhfdW1hcCkgPC0gcGFzdGUwKCJ1bWFwIiwgc2VxKDEsIDIpKQpyZWR1Y2VkRGltKHNjZTEweCwgIm1ubl91bWFwIikgPC0gc2NlMTB4X3VtYXAKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xNH0KZHQgPC0KICBiaW5kX2NvbHMoCiAgICBjb2xEYXRhKHNjZTEweCkgJT4lCiAgICAgIGFzX3RpYmJsZSgpLAogICAgc2NlMTB4X3VtYXAgJT4lCiAgICAgIGFzX3RpYmJsZSgpCiAgKQpwMiA8LQogIGdncGxvdChkdCkgKwogIGdlb21fcG9pbnQoYWVzKHVtYXAxLAogICAgdW1hcDIsCiAgICBjb2xvdXIgPSBjbHVzdGVyc19sZXZlbDEKICApLAogIHNpemUgPSAuNiwKICBhbHBoYSA9IDEKICApICMrIGZhY2V0X3dyYXAofmNvbmRpdGlvbikKcDIgCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJtbm5fdW1hcC5wZGYiKSwgcDIgKQpgYGAKCgoKCgojIyBDbHVzdGVyIHN0ZW0gY2VsbHMKCmBgYHtyfQpzY2UxMHhfc3RlbSA8LSBzY2UxMHhbLGNvbERhdGEoc2NlMTB4KSRjbHVzdGVyc19sZXZlbDEgPT0ic3RlbSJdCmBgYAoKCgpgYGB7cn0Kc2V0LnNlZWQoNDIpCiMgdC1VTUFQIGlzIGVxdWl2YWxlbnQgdG8gYSA9IDEsIGIgPSAxCiMgZ2V0IHRoZSBuZWFyZXN0IG5laWdoYm9yIGRhdGEgYmFjawpzY2UxMHhfc3RlbV90dW1hcCA8LQogIHR1bWFwKHJlZHVjZWREaW0oc2NlMTB4X3N0ZW0sIAogICAgICAgICAgICAgICAgICAgIm1ubl9jb3JyZWN0ZWQiKSwKICAgICAgICBtZXRyaWMgPSAiY29zaW5lIiwKICAgICAgICByZXRfbm4gPSBUUlVFKQpgYGAKCmBgYHtyfQpzZXQuc2VlZCg0MikKc2NlMTB4X3N0ZW1fdW1hcCA8LQogIHVtYXAocmVkdWNlZERpbShzY2UxMHhfc3RlbSwKICAgICAgICAgICAgICAgICAgIm1ubl9jb3JyZWN0ZWQiKSwKICAgIG1ldHJpYyA9ICJjb3NpbmUiLAogICAgbm5fbWV0aG9kID0gc2NlMTB4X3N0ZW1fdHVtYXAkbm4sCiAgICBhID0gLjg1LAogICAgYiA9IC40MwogICkKY29sbmFtZXMoc2NlMTB4X3N0ZW1fdW1hcCkgPC0gcGFzdGUwKCJ1bWFwIiwgc2VxKDEsIDIpKQpyZWR1Y2VkRGltKHNjZTEweF9zdGVtLCAibW5uX3VtYXAiKSA8LSBzY2UxMHhfc3RlbV91bWFwCmBgYAoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTE0fQpkdCA8LQogIGJpbmRfY29scygKICAgIGNvbERhdGEoc2NlMTB4X3N0ZW0pICU+JQogICAgICBhc190aWJibGUoKSwKICAgIHNjZTEweF9zdGVtX3VtYXAgJT4lCiAgICAgIGFzX3RpYmJsZSgpCiAgKQpwMiA8LQogIGdncGxvdChkdCkgKwogIGdlb21fcG9pbnQoYWVzKHVtYXAxLAogICAgdW1hcDIsCiAgICBjb2xvdXIgPSBjbHVzdGVyc19sZXZlbDIKICApLAogIHNpemUgPSAxLAogIGFscGhhID0uNgogICkKcDIKIyBnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCAidW1hcF9jb3NpbmVfYTE2MF9iMzNfMTc5MjhfY2VsbHNfbWFya2Vycy5wZGYiKSwgcDIgKQpgYGAKCgoKYGBge3J9CnNldC5zZWVkKDQyKQpnIDwtIGJ1aWxkU05OR3JhcGgoc2NlMTB4X3N0ZW0sIGsgPSAxMDAsIHVzZS5kaW1yZWQgPSAibW5uX3VtYXAiKQpjbHVzdGVyc19vdXQgPC0gaWdyYXBoOjpjbHVzdGVyX3dhbGt0cmFwKGcpCmBgYAoKYGBge3J9CmlncmFwaF9jbHVzdGVycyA8LSBmYWN0b3IoY2x1c3RlcnNfb3V0JG1lbWJlcnNoaXApCnRhYmxlKGlncmFwaF9jbHVzdGVycykKYGBgCgpgYGB7cn0KaWdyYXBoX2NsdXN0ZXJzIDwtIGlncmFwaDo6Y3V0X2F0KGNsdXN0ZXJzX291dCwgbiA9IDUpCnRhYmxlKGlncmFwaF9jbHVzdGVycykKYGBgCgoKYGBge3J9CmRpc2NhcmRfY2VsbCA8LSBzY2UxMHhfc3RlbV91bWFwWywxXSA8IC0xMCB8IHNjZTEweF9zdGVtX3VtYXBbLDFdID4gMjIgIHwgKGlncmFwaF9jbHVzdGVycyA9PTIgJiBjb2xEYXRhKHNjZTEweF9zdGVtKSRjb25kaXRpb249PSJhZ2VkIikKdGFibGUoZGlzY2FyZF9jZWxsKQpgYGAKCgoKYGBge3J9CmlncmFwaF9jbHVzdGVyc1tkaXNjYXJkX2NlbGxdIDwtIDYKYGBgCgoKYGBge3J9CmNsdXN0ZXJfYnlfbWFya2VyIDwtCiAgdGFibGUoaWdyYXBoX2NsdXN0ZXJzLCBjb2xEYXRhKHNjZTEweF9zdGVtKSRzYW1wbGUpCmNsdXN0ZXJfYnlfbWFya2VyCmBgYAoKCgpgYGB7cn0KY29sRGF0YShzY2UxMHhfc3RlbSkkY2x1c3RlcnMgPC0gZmFjdG9yKGlncmFwaF9jbHVzdGVycykKYGBgCgpgYGB7cn0Kc2NlMTB4X3N0ZW1fc3Vic2V0IDwtCiAgc2NlMTB4X3N0ZW1bLCBjb2xEYXRhKHNjZTEweF9zdGVtKSRjbHVzdGVycyE9Nl0KYGBgCgoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTE0fQpkdCA8LQogIGJpbmRfY29scygKICAgIGNvbERhdGEoc2NlMTB4X3N0ZW1fc3Vic2V0KSAlPiUKICAgICAgYXNfdGliYmxlKCksCiAgICByZWR1Y2VkRGltKHNjZTEweF9zdGVtX3N1YnNldCwgIm1ubl91bWFwIikgJT4lCiAgICAgIGFzX3RpYmJsZSgpCiAgKQpwMiA8LQogIGdncGxvdChkdCkgKwogIGdlb21fcG9pbnQoYWVzKHVtYXAxLAogICAgdW1hcDIsCiAgICBjb2xvdXIgPSBjbHVzdGVycwogICksCiAgc2l6ZSA9IDEuNywKICBhbHBoYSA9IDEKICApCnAyICAjICsgZmFjZXRfd3JhcCh+Y2x1c3RlcnMpCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJtbm5fdW1hcF9zdGVtLnBkZiIpLCBwMiApCmBgYAoKCgoKCgoKIyMgQ2x1c3RlciBmYXAgY2VsbHMKCmBgYHtyfQpzY2UxMHhfZmFwIDwtIHNjZTEweFssY29sRGF0YShzY2UxMHgpJGNsdXN0ZXJzX2xldmVsMSA9PSJmYXAiXQpgYGAKCgoKYGBge3J9CnNldC5zZWVkKDQyKQojIHQtVU1BUCBpcyBlcXVpdmFsZW50IHRvIGEgPSAxLCBiID0gMQojIGdldCB0aGUgbmVhcmVzdCBuZWlnaGJvciBkYXRhIGJhY2sKc2NlMTB4X2ZhcF90dW1hcCA8LQogIHR1bWFwKHJlZHVjZWREaW0oc2NlMTB4X2ZhcCwgCiAgICAgICAgICAgICAgICAgICAibW5uX2NvcnJlY3RlZCIpLAogICAgICAgIG1ldHJpYyA9ICJjb3NpbmUiLAogICAgICAgIHJldF9ubiA9IFRSVUUpCmBgYAoKYGBge3J9CnNldC5zZWVkKDQyKQpzY2UxMHhfZmFwX3VtYXAgPC0KICB1bWFwKHJlZHVjZWREaW0oc2NlMTB4X2ZhcCwKICAgICAgICAgICAgICAgICAgIm1ubl9jb3JyZWN0ZWQiKSwKICAgIG1ldHJpYyA9ICJjb3NpbmUiLAogICAgbm5fbWV0aG9kID0gc2NlMTB4X2ZhcF90dW1hcCRubiwKICAgIGEgPSAuOSwKICAgIGIgPSAuNzUKICApCmNvbG5hbWVzKHNjZTEweF9mYXBfdW1hcCkgPC0gcGFzdGUwKCJ1bWFwIiwgc2VxKDEsIDIpKQpyZWR1Y2VkRGltKHNjZTEweF9mYXAsICJtbm5fdW1hcCIpIDwtIHNjZTEweF9mYXBfdW1hcApgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNH0KZHQgPC0KICBiaW5kX2NvbHMoCiAgICBjb2xEYXRhKHNjZTEweF9mYXApICU+JQogICAgICBhc190aWJibGUoKSwKICAgIHNjZTEweF9mYXBfdW1hcCAlPiUKICAgICAgYXNfdGliYmxlKCkKICApCnAyIDwtCiAgZ2dwbG90KGR0KSArCiAgZ2VvbV9wb2ludChhZXModW1hcDEsCiAgICB1bWFwMiwKICAgIGNvbG91ciA9IGNsdXN0ZXJzX2xldmVsMgogICksCiAgc2l6ZSA9IDEsCiAgYWxwaGEgPS42CiAgKQpwMgojIGdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJ1bWFwX2Nvc2luZV9hMTYwX2IzM18xNzkyOF9jZWxsc19tYXJrZXJzLnBkZiIpLCBwMiApCmBgYAoKCgpgYGB7cn0Kc2V0LnNlZWQoNDIpCmcgPC0gYnVpbGRTTk5HcmFwaChzY2UxMHhfZmFwLCBrID0gMTAwLCB1c2UuZGltcmVkID0gIm1ubl91bWFwIikKY2x1c3RlcnNfb3V0IDwtIGlncmFwaDo6Y2x1c3Rlcl93YWxrdHJhcChnKQpgYGAKCmBgYHtyfQppZ3JhcGhfY2x1c3RlcnMgPC0gZmFjdG9yKGNsdXN0ZXJzX291dCRtZW1iZXJzaGlwKQp0YWJsZShpZ3JhcGhfY2x1c3RlcnMpCmBgYAoKYGBge3J9CmlncmFwaF9jbHVzdGVycyA8LSBpZ3JhcGg6OmN1dF9hdChjbHVzdGVyc19vdXQsIG4gPSAzKQp0YWJsZShpZ3JhcGhfY2x1c3RlcnMpCmBgYAoKCmBgYHtyfQpkaXNjYXJkX2NlbGwgPC0gIHNjZTEweF9mYXBfdW1hcFssMV0gPiA1CnRhYmxlKGRpc2NhcmRfY2VsbCkKYGBgCgoKCmBgYHtyfQppZ3JhcGhfY2x1c3RlcnNbZGlzY2FyZF9jZWxsXSA8LSA0CmBgYAoKCmBgYHtyfQpjbHVzdGVyX2J5X21hcmtlciA8LQogIHRhYmxlKGlncmFwaF9jbHVzdGVycywgY29sRGF0YShzY2UxMHhfZmFwKSRzYW1wbGUpCmNsdXN0ZXJfYnlfbWFya2VyCmBgYAoKCgpgYGB7cn0KY29sRGF0YShzY2UxMHhfZmFwKSRjbHVzdGVycyA8LSBmYWN0b3IoaWdyYXBoX2NsdXN0ZXJzKQpgYGAKCmBgYHtyfQpzY2UxMHhfZmFwX3N1YnNldCA8LQogIHNjZTEweF9mYXBbLCBjb2xEYXRhKHNjZTEweF9mYXApJGNsdXN0ZXJzIT00XQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTR9CmR0IDwtCiAgYmluZF9jb2xzKAogICAgY29sRGF0YShzY2UxMHhfZmFwX3N1YnNldCkgJT4lCiAgICAgIGFzX3RpYmJsZSgpLAogICAgcmVkdWNlZERpbShzY2UxMHhfZmFwX3N1YnNldCwgIm1ubl91bWFwIikgJT4lCiAgICAgIGFzX3RpYmJsZSgpCiAgKQpwMiA8LQogIGdncGxvdChkdCkgKwogIGdlb21fcG9pbnQoYWVzKHVtYXAxLAogICAgdW1hcDIsCiAgICBjb2xvdXIgPSBjbHVzdGVycwogICksCiAgc2l6ZSA9IDEuNywKICBhbHBoYSA9IDEKICApCnAyICAjICsgZmFjZXRfd3JhcCh+Y2x1c3RlcnMpCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJtbm5fdW1hcF9mYXAucGRmIiksIHAyICkKYGBgCgoKCiMjIENsdXN0ZXIgbWFjcm9waGFnZSBjZWxscwoKYGBge3J9CnNjZTEweF9tYWNyb3BoYWdlIDwtIHNjZTEweFssY29sRGF0YShzY2UxMHgpJGNsdXN0ZXJzX2xldmVsMSA9PSJtYWNyb3BoYWdlIl0KYGBgCgoKCmBgYHtyfQpzZXQuc2VlZCg0MikKIyB0LVVNQVAgaXMgZXF1aXZhbGVudCB0byBhID0gMSwgYiA9IDEKIyBnZXQgdGhlIG5lYXJlc3QgbmVpZ2hib3IgZGF0YSBiYWNrCnNjZTEweF9tYWNyb3BoYWdlX3R1bWFwIDwtCiAgdHVtYXAocmVkdWNlZERpbShzY2UxMHhfbWFjcm9waGFnZSwgCiAgICAgICAgICAgICAgICAgICAibW5uX2NvcnJlY3RlZCIpLAogICAgICAgIG1ldHJpYyA9ICJjb3NpbmUiLAogICAgICAgIHJldF9ubiA9IFRSVUUpCmBgYAoKYGBge3J9CnNldC5zZWVkKDQyKQpzY2UxMHhfbWFjcm9waGFnZV91bWFwIDwtCiAgdW1hcChyZWR1Y2VkRGltKHNjZTEweF9tYWNyb3BoYWdlLAogICAgICAgICAgICAgICAgICAibW5uX2NvcnJlY3RlZCIpLAogICAgbWV0cmljID0gImNvc2luZSIsCiAgICBubl9tZXRob2QgPSBzY2UxMHhfbWFjcm9waGFnZV90dW1hcCRubiwKICAgIGEgPSAwLjc1LAogICAgYiA9IDAuNjUKICApCmNvbG5hbWVzKHNjZTEweF9tYWNyb3BoYWdlX3VtYXApIDwtIHBhc3RlMCgidW1hcCIsIHNlcSgxLCAyKSkKcmVkdWNlZERpbShzY2UxMHhfbWFjcm9waGFnZSwgIm1ubl91bWFwIikgPC0gc2NlMTB4X21hY3JvcGhhZ2VfdW1hcApgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNH0KZHQgPC0KICBiaW5kX2NvbHMoCiAgICBjb2xEYXRhKHNjZTEweF9tYWNyb3BoYWdlKSAlPiUKICAgICAgYXNfdGliYmxlKCksCiAgICBzY2UxMHhfbWFjcm9waGFnZV91bWFwICU+JQogICAgICBhc190aWJibGUoKQogICkKcDIgPC0KICBnZ3Bsb3QoZHQpICsKICBnZW9tX3BvaW50KGFlcyh1bWFwMSwKICAgIHVtYXAyLAogICAgY29sb3VyID0gY2x1c3RlcnNfbGV2ZWwyCiAgKSwKICBzaXplID0gMSwKICBhbHBoYSA9LjYKICApCnAyCiMgZ2dzYXZlKGZpbGUucGF0aChmaWd1cmVzX2RpciwgInVtYXBfY29zaW5lX2ExNjBfYjMzXzE3OTI4X2NlbGxzX21hcmtlcnMucGRmIiksIHAyICkKYGBgCgoKCmBgYHtyfQpzZXQuc2VlZCg0MikKZyA8LSBidWlsZFNOTkdyYXBoKHNjZTEweF9tYWNyb3BoYWdlLCBrID0gNTAsIHVzZS5kaW1yZWQgPSAibW5uX3VtYXAiKQpjbHVzdGVyc19vdXQgPC0gaWdyYXBoOjpjbHVzdGVyX3dhbGt0cmFwKGcpCmBgYAoKYGBge3J9CmlncmFwaF9jbHVzdGVycyA8LSBmYWN0b3IoY2x1c3RlcnNfb3V0JG1lbWJlcnNoaXApCnRhYmxlKGlncmFwaF9jbHVzdGVycykKYGBgCgpgYGB7cn0KaWdyYXBoX2NsdXN0ZXJzIDwtIGlncmFwaDo6Y3V0X2F0KGNsdXN0ZXJzX291dCwgbiA9IDUpCnRhYmxlKGlncmFwaF9jbHVzdGVycykKYGBgCgoKYGBge3J9CmNsdXN0ZXJfYnlfbWFya2VyIDwtCiAgdGFibGUoaWdyYXBoX2NsdXN0ZXJzLCBjb2xEYXRhKHNjZTEweF9tYWNyb3BoYWdlKSRzYW1wbGUpCmNsdXN0ZXJfYnlfbWFya2VyCmBgYAoKCgpgYGB7cn0KY29sRGF0YShzY2UxMHhfbWFjcm9waGFnZSkkY2x1c3RlcnMgPC0gZmFjdG9yKGlncmFwaF9jbHVzdGVycykKYGBgCgpgYGB7cn0Kc2NlMTB4X21hY3JvcGhhZ2Vfc3Vic2V0IDwtCiAgc2NlMTB4X21hY3JvcGhhZ2VbLCBjb2xEYXRhKHNjZTEweF9tYWNyb3BoYWdlKSRjbHVzdGVycyAlaW4lIGMoMSwyLCAzKV0KYGBgCgoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTE0fQpkdCA8LQogIGJpbmRfY29scygKICAgIGNvbERhdGEoc2NlMTB4X21hY3JvcGhhZ2Vfc3Vic2V0KSAlPiUKICAgICAgYXNfdGliYmxlKCksCiAgICByZWR1Y2VkRGltKHNjZTEweF9tYWNyb3BoYWdlX3N1YnNldCwgIm1ubl91bWFwIikgJT4lCiAgICAgIGFzX3RpYmJsZSgpCiAgKQpwMiA8LQogIGdncGxvdChkdCkgKwogIGdlb21fcG9pbnQoYWVzKHVtYXAxLAogICAgdW1hcDIsCiAgICBjb2xvdXIgPSBjbHVzdGVycwogICksCiAgc2l6ZSA9IDEuNywKICBhbHBoYSA9IDEKICApCnAyICAjICsgZmFjZXRfd3JhcCh+Y2x1c3RlcnMpCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJtbm5fdW1hcF9tYWNyb3BoYWdlLnBkZiIpLCBwMiApCmBgYAoKCgojIyBTYXZlIGRhdGEKCgpgYGB7cn0KbWV0YV9kdCA8LQogIHJiaW5kKGNvbERhdGEoc2NlMTB4X3N0ZW1fc3Vic2V0KSwgY29sRGF0YShzY2UxMHhfZmFwX3N1YnNldCksIGNvbERhdGEoc2NlMTB4X21hY3JvcGhhZ2Vfc3Vic2V0KSkgJT4lCiAgYXNfdGliYmxlKHJvd25hbWVzID0gImNlbGwiKSAlPiUKICBtdXRhdGUoY2x1c3Rlcl9sZXZlbDFfMT0gcGFzdGUoY2x1c3RlcnNfbGV2ZWwxLCBjbHVzdGVycywgc2VwPSJfIiApLAogICAgICAgICBjbHVzdGVyX2xldmVsMl8xPSBwYXN0ZShjbHVzdGVyc19sZXZlbDIsIGNsdXN0ZXJzLCBzZXA9Il8iICkpICU+JQogIHNlbGVjdChjZWxsLCBjbHVzdGVyX2xldmVsMV8xLCBjbHVzdGVyX2xldmVsMl8xICkKbWV0YV9kdApgYGAKCmBgYHtyfQpjb2xEYXRhKHNjZTEweCkgPC0KICBsZWZ0X2pvaW4oY29sRGF0YShzY2UxMHgpJT4lCiAgYXNfdGliYmxlKHJvd25hbWVzID0gImNlbGwiKSwKICBtZXRhX2R0LAogIGJ5PSJjZWxsIikgJT4lCiAgcmVwbGFjZV9uYShsaXN0KGNsdXN0ZXJfbGV2ZWwyXzEgPSAiZGlzY2FyZCIsIGNsdXN0ZXJfbGV2ZWwxXzEgPSAiZGlzY2FyZCIpKSAlPiUKICBzZWxlY3QoLWNsdXN0ZXJzKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoImNlbGwiKSAlPiUKICBEYXRhRnJhbWUoLikKCmBgYCAKCmBgYHtyfQpkaXNjYXJkX2lkeCA8LSAKICBjb2xEYXRhKHNjZTEweCkkY2x1c3Rlcl9sZXZlbDJfMSA9PSAiZGlzY2FyZCIKdGFibGUoZGlzY2FyZF9pZHgpCmBgYAoKCmBgYHtyfQpzY2UxMHggPC0gc2NlMTB4WywgIWRpc2NhcmRfaWR4XQpgYGAKCmBgYHtyfQpuX2V4cHJzX2dlbmVzIDwtCiAgbmV4cHJzKHNjZTEweCwKICAgIGRldGVjdGlvbl9saW1pdCA9IDAsCiAgICBieXJvdyA9IFRSVUUKICApCnRhYmxlKG5fZXhwcnNfZ2VuZXMpWzE6MjBdCmBgYAoKYGBge3J9CnNjZTEweCA8LSBzY2UxMHhbbl9leHByc19nZW5lcz4wLCBdCmBgYAoKCgpgYGB7cn0Kc2F2ZVJEUygKICBzY2UxMHgsCiAgZmlsZS5wYXRoKAogICAgZGF0YV9kaXIsCiAgICAicHJlcHJvY2Vzc2VkIiwKICAgICJzY2UxMHhfZmlsdGVyZWRfZmluYWwucmRzIgogICkKKQpgYGAKCiMjIG1ha2UgcGxvdHMKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE0fQpkdCA8LQogIGJpbmRfY29scygKICAgIGNvbERhdGEoc2NlMTB4KSAlPiUKICAgICAgYXNfdGliYmxlKCksCiAgICByZWR1Y2VkRGltKHNjZTEweCwgIm1ubl91bWFwIikgJT4lCiAgICAgIGFzX3RpYmJsZSgpCiAgKQpwMiA8LQogIGdncGxvdChkdCkgKwogIGdlb21fcG9pbnQoYWVzKHVtYXAxLAogICAgdW1hcDIsCiAgICBjb2xvdXIgPSBjbHVzdGVyX2xldmVsMV8xCiAgKSwKICBzaXplID0gLjYsCiAgYWxwaGEgPSAxCiAgKSArIGZhY2V0X3dyYXAofmNvbmRpdGlvbikKcDIgCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJtbm5fdW1hcF9zcGxpdC5wZGYiKSwgcDIgKQpgYGAKCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=