Prepare analysis workflow

knitr::opts_knit$set(root.dir = rprojroot::find_rstudio_root_file())
options(
  readr.show_progress = FALSE,
  digits = 2,
  scipen = 8,
  future.globals.maxSize = +Inf
)
library(tidyverse)
library(tximport)
library(DESeq2)
library(vsn)
library(cowplot)
library(glmpca)
library(apeglm)
library(EnhancedVolcano)
library(pheatmap)
theme_set(theme_bw())

Auxiliary functions

get_deseq_data <- function(target_tissue, meta_dt, data_dir, tx2knownGene) {
  
  meta_dt <-
  meta_dt %>%
  dplyr::filter(tissue == target_tissue)
  
   samples_dir <- file.path(data_dir, 
                            meta_dt %>% 
                              pull(name))
   cdna_dir <- file.path(samples_dir)
  cdna_files <- file.path(cdna_dir, "abundance.h5")
  names(cdna_files) <- 
    meta_dt %>% 
    pull(sample)
  
  
  txi_scaledTPM <- tximport(cdna_files, 
                            type = "kallisto",
                            tx2gene=tx2knownGene,
                            countsFromAbundance = c("scaledTPM"))
  
  txi <- tximport(cdna_files,
                  type = "kallisto",
                  tx2gene=tx2knownGene, 
                  countsFromAbundance = c("no"))
  scaled_tpm <- 
    txi_scaledTPM$counts %>% 
  as_tibble(rownames = "gene_id")

  gene_counts <- 
    txi$counts %>% 
  as_tibble(rownames = "gene_id")
  
  write_tsv(scaled_tpm, paste0(target_tissue, "_engraftment_scaled_tpm.tsv"))
  write_tsv(gene_counts, paste0(target_tissue, "_engraftment_counts.tsv"))
  
  dds <- DESeqDataSetFromTximport(txi,
  meta_dt %>%
    column_to_rownames("sample"),
  design = ~ time + age + time:age)
  keep <- rowSums(counts(dds) >= 30) >= 2 
  dds <- dds[keep,]
  
  return(dds)
}

#We use apeglm shrinkage to preserve the size of large LFC and compute s-values (the estimated rate of false sign). Shrinkage is useful for ranking and visualization, without the need for arbitrary filters on low count genes. https://doi.org/10.1093/bioinformatics/bty895

#The local false sign rate FSR is defined as the posterior probability that the posterior mode (MAP) of beta (log2FC) is of a biologically significant effect size (i.e. abs(beta) > lfcThreshold )

get_lfc_estimates <- function(dds, lfc_threshold = 1, svalue_thresh = 0.05){
  
niche_lfc <-
  lfcShrink(dds,
    coef = "timeT21.ageold",
    type = "apeglm",
    svalue = T,
    lfcThreshold = lfc_threshold
  )

engrafment_lfc <-
  lfcShrink(dds,
    coef = "time_T21_vs_T0",
    type = "apeglm",
    svalue = T,
    lfcThreshold = lfc_threshold
  )

age_lfc <-
  lfcShrink(dds,
    coef = "age_old_vs_young",
    type = "apeglm",
    svalue = T,
    lfcThreshold = lfc_threshold
  )



plotMA(niche_lfc, ylim = c(-22, 8))
abline(h = c(-1, 1), col = "dodgerblue", lwd = 2)

plotMA(engrafment_lfc, ylim = c(-22, 8))
abline(h = c(-1, 1), col = "dodgerblue", lwd = 2)

plotMA(age_lfc, ylim = c(-22, 8))
abline(h = c(-1, 1), col = "dodgerblue", lwd = 2)

summary(niche_lfc, alpha = svalue_thresh)
summary(engrafment_lfc, alpha = svalue_thresh)
summary(age_lfc, alpha = svalue_thresh)


lfc <-
  bind_cols(
    mcols(dds)[, c("Intercept",
                       "time_T21_vs_T0", 
                       "age_old_vs_young",
                       "timeT21.ageold")] %>%
      as_tibble() %>%
      set_names(c("intercept", "engraf", "age", "niche")),
    engrafment_lfc %>%
      as_tibble(rownames = "gene_id")  %>%
      set_names(c(
        c("gene_id", "base_mean"),
        paste0(
          c("lfc", "lfc_se", "svalue"),
          "_engraf"
        ))),
    age_lfc %>%
      as_tibble(rownames = "gene_id") %>%
      dplyr::select(-baseMean, -gene_id)  %>%
      set_names(paste0(
        c("lfc", "lfc_se", "svalue"), 
        "_age"
      )) ,
    niche_lfc %>%
      as_tibble(rownames = "gene_id") %>%
      dplyr::select(-baseMean, -gene_id)    %>%
      set_names(paste0(
        c("lfc", "lfc_se", "svalue"), 
        "_niche")))  %>%
  mutate(
    keep_niche = (abs(lfc_niche) > lfc_threshold &
      svalue_niche < svalue_thresh),
    keep_age = (abs(lfc_age) > lfc_threshold &
      svalue_age < svalue_thresh),
    keep_engraf = (abs(lfc_engraf) > lfc_threshold &
      svalue_engraf < svalue_thresh)
  )  %>%
  mutate(
    EAN = paste0(
      as.integer(keep_engraf),
      as.integer(keep_age),
      as.integer(keep_niche)
    ))

lfc <-
  lfc %>%
   dplyr::select(gene_id,EAN, base_mean,  
    intercept, engraf, age, niche, 
    lfc_engraf, svalue_engraf, lfc_age, svalue_age,
    lfc_niche, svalue_niche, everything()
  ) 
}


plot_volcano <- function(data, xvar, yvar, title, svalue_thresh) {
  gv <-
    EnhancedVolcano(data,
      lab = data %>% pull(gene_name),
      xlab = bquote(~ italic(Moderated) ~ Log[2] ~ FC),
      ylab = bquote(~ -Log[10] ~ italic(svalue)),
      x = xvar,
      y = yvar,
      col = c("grey30", "forestgreen", "red2", "royalblue"),
      pCutoff = svalue_thresh,
      legend = c("NS", "Log2FC", "svalue", "svalue & Log2FC"),
      legendLabels = c(
        "NS",
        expression(Log[2] ~ FC),
        "svalue",
        expression(s - value ~ and ~ log[2] ~ FC)
      )
    )

  ggsave(file.path(figures_dir, paste0(title, "_volcano.pdf")), gv)
  return(gv)
}

plot_heatmap <- function(target_genes, title, meta_col, data, show_genes = F) {
  df <-
    left_join(target_genes %>%
      dplyr::select(gene_id, gene_name),
    data,
    by = "gene_id"
    ) %>%
    dplyr::select(-gene_id) %>%
    column_to_rownames("gene_name") %>%
    as.matrix()

  gh <-
    pheatmap(df,
      color = colorRampPalette(rev(RColorBrewer::brewer.pal(n = 11, name = "Spectral")))(100),
      cluster_rows = T,
      show_rownames = show_genes,
      treeheight_row = 0,
      fontsize_col = 4,
      fontsize_row = 6,
      angle_col = "45",
      cluster_cols = FALSE,
      annotation_col = meta_col
    )
  ggsave(file.path(figures_dir, paste0(title, "_heatmap_expression.pdf")), gh)

  return(gh)
}

plot_pca <- function(vsd, title) {
  
  pcaData <-
  plotPCA(vsd, 
          intgroup = c( "time", "age", "tissue", "batch", "donor"),
          returnData = TRUE)  
percentVar <- round(100 * attr(pcaData, "percentVar"))

gg_plot <- 
  ggplot(pcaData %>%
  mutate(condition= paste(age, time, sep="_")),
  aes(x = PC1, y = PC2,  color = time, shape=age, label = name )) +
  geom_point( size=4) +
   geom_line(aes(group = donor), colour="grey") +
#  coord_fixed() +
  geom_text(check_overlap=T, angle = 0, size=3, vjust = 3, nudge_y = 1.5) + 
  xlab(paste0("PC1: ", percentVar[1], "% variance")) +
  ylab(paste0("PC2: ", percentVar[2], "% variance")) +
  coord_fixed() +
  ggtitle("PCA with VST data")
ggsave(file.path(figures_dir, paste0(title, "_pca_bulk_rnaseq.pdf")), gg_plot)
gg_plot
}

plot_gene <- function(gene, data) {
  df_plot <-
    data %>%
    dplyr::filter(gene_id == gene)
  
  gene_name <- df_plot$gene_name[1]
  p <- ggplot(
    df_plot,
       aes(x = time, y = log2_scaled_tpm)) +
  geom_point(aes(color = donor)) +
  geom_line(aes(group = donor))  +
    labs(title = gene_name)+ 
  facet_grid(tissue ~ age)
  ggsave(file.path(figures_dir, paste0(gene_name, "_expression_log2_scaled_tpm.pdf")), p)
  return(p)
}

Set filepaths

figures_dir <- "./figures"
data_dir <- "./data/kallisto_bulk_spliced"
gene_metadata_filepath <- "~/Documents/genome_metadata/data/mm10_ens97_gene_meta.txt"
meta_dt <-
  read_tsv(file.path(
    data_dir,
    "meta.tsv"
  )) %>%
  # arrange(tissue, age, time) %>%
  mutate(
    condition = paste(tissue, age, time, sep = "_"),
    age = factor(age, levels = c("young", "old")),
    tissue = factor(tissue, levels = c("leg", "trunk")),
    time = factor(time, levels = c("T0", "T21")),
    batch = factor(batch, levels = c(1, 2))
  )
Parsed with column specification:
cols(
  name = col_character(),
  sample = col_character(),
  time = col_character(),
  tissue = col_character(),
  age = col_character(),
  donor = col_character(),
  type = col_character(),
  batch = col_double()
)

Get tx id versions

library(AnnotationHub)
Loading required package: BiocFileCache
Loading required package: dbplyr

Attaching package: ‘dbplyr’

The following objects are masked from ‘package:dplyr’:

    ident, sql


Attaching package: ‘AnnotationHub’

The following object is masked from ‘package:Biobase’:

    cache
ah <- AnnotationHub()
snapshotDate(): 2019-10-29
edb <- ah[["AH73905"]]
loading from cache
require(“ensembldb”)
seqlevelsStyle(edb) <- "UCSC"
#standard_chroms <- standardChromosomes(edb, species = organism(edb))
tx_meta <- 
transcripts(edb,
            columns=c("tx_id_version",
                      "gene_id",
                     "gene_name",
                     "gene_id_version"),
            return.type="DataFrame")
tx2knownGene <-  
  tx_meta %>% 
  as_tibble() %>%
  dplyr::select(tx_id_version, gene_id)
gene_metadata <-
  read_csv(gene_metadata_filepath) %>%
  dplyr::select(-gene_id_version) 
Parsed with column specification:
cols(
  gene_name = col_character(),
  gene_len = col_double(),
  perc_gene_gc = col_double(),
  gene_id = col_character(),
  seqnames = col_character(),
  gene_biotype = col_character(),
  gene_id_version = col_character(),
  description = col_character()
)
gene_metadata <-left_join(tx_meta %>% as_tibble() %>% 
            group_by(gene_id) %>% 
            dplyr::count( gene_name) %>% 
            dplyr::select(-n),
          gene_metadata %>%
             dplyr::select(-gene_name),
          by="gene_id") %>%
  mutate(gene_name= 
           scater::uniquifyFeatureNames(
             ID= gene_id,
             names=gene_name) )

Load data

dds_leg <- get_deseq_data("leg", meta_dt, data_dir, tx2knownGene)
1 2 3 4 5 6 7 8 9 
summarizing abundance
summarizing counts
summarizing length
1 2 3 4 5 6 7 8 9 
summarizing abundance
summarizing counts
summarizing length
using counts and average transcript lengths from tximport
dds_leg 
class: DESeqDataSet 
dim: 16228 9 
metadata(1): version
assays(2): counts avgTxLength
rownames(16228): ENSMUSG00000000001 ENSMUSG00000000028 ... ENSMUSG00000118458 ENSMUSG00000118461
rowData names(0):
colnames(9): yng_leg_t0_1 yng_leg_t0_aug ... old_leg_t21_396_1 old_leg_t21_396_2
colData names(8): name time ... batch condition
dds_trunk <- get_deseq_data("trunk", meta_dt, data_dir, tx2knownGene)
1 2 3 4 5 6 7 8 9 10 11 12 
summarizing abundance
summarizing counts
summarizing length
1 2 3 4 5 6 7 8 9 10 11 12 
summarizing abundance
summarizing counts
summarizing length
using counts and average transcript lengths from tximport
dds_trunk
class: DESeqDataSet 
dim: 15950 12 
metadata(1): version
assays(2): counts avgTxLength
rownames(15950): ENSMUSG00000000001 ENSMUSG00000000028 ... ENSMUSG00000118423 ENSMUSG00000118461
rowData names(0):
colnames(12): yng_trunk_t0_2 yng_trunk_t0_3 ... old_trunk_t21_94A_br1 old_trunk_t21_94A_br2
colData names(8): name time ... batch condition
summary_dt_leg <-
  colSums(assay(dds_leg)) %>%
  tibble::enframe(name = "sample", value = "counts")
summary_dt_leg
summary_dt_trunk <-
  colSums(assay(dds_trunk)) %>%
  tibble::enframe(name = "sample", value = "counts")
summary_dt_trunk

scaled tpm

scaled_tpm_leg <- read_tsv("leg_engraftment_scaled_tpm.tsv")
Parsed with column specification:
cols(
  gene_id = col_character(),
  yng_leg_t0_1 = col_double(),
  yng_leg_t0_aug = col_double(),
  yng_leg_t0_3 = col_double(),
  yng_leg_t21_1 = col_double(),
  yng_leg_t21_aug = col_double(),
  old_leg_t0_396_1 = col_double(),
  old_leg_t0_396_2 = col_double(),
  old_leg_t21_396_1 = col_double(),
  old_leg_t21_396_2 = col_double()
)
scaled_tpm_trunk <- read_tsv("trunk_engraftment_scaled_tpm.tsv")
Parsed with column specification:
cols(
  gene_id = col_character(),
  yng_trunk_t0_2 = col_double(),
  yng_trunk_t0_3 = col_double(),
  yng_trunk_t21_2 = col_double(),
  yng_trunk_t21_3 = col_double(),
  old_trunk_t0_88A = col_double(),
  old_trunk_t0_89A = col_double(),
  old_trunk_t0_94A = col_double(),
  old_trunk_t21_88A = col_double(),
  old_trunk_t21_89A_tr1 = col_double(),
  old_trunk_t21_89A_tr2 = col_double(),
  old_trunk_t21_94A_br1 = col_double(),
  old_trunk_t21_94A_br2 = col_double()
)
scaled_tpm <-
  full_join(scaled_tpm_leg, 
            scaled_tpm_trunk, 
            "gene_id")

Generalized Principal Component Analysis

set.seed(420)
gpca <- glmpca(scaled_tpm[, 2:22] %>%
  dplyr::filter(rowSums(.[1:21]) > 0), L = 2)
gpca.dat <- gpca$factors
gpca.dat <- cbind(gpca.dat, meta_dt[, c("age", "tissue", "time", "batch", "donor")])
gpca.dat
gg_pca <-
  ggplot(
    gpca.dat %>% as_tibble(rownames = "sample") %>%
      mutate(condition = paste(age, time, sep = "_")),
    aes(x = dim1, y = dim2, color = condition, shape = batch, label = sample)
  ) +
  geom_point(size = 4) +
  geom_line(aes(group = donor), colour = "grey") +
  #  coord_fixed() +
  geom_text(check_overlap = T, angle = 0, size = 3, vjust = 2, nudge_y = 1.5) +
  # facet_grid(~tissue) +
  ggtitle("glmpca - Generalized PCA")
gg_pca

ggsave(file.path(figures_dir, "glmpca_bulk_rnaseq.pdf"), gg_pca)
Saving 12 x 7.41 in image

Fit Model

dds_leg <- DESeq(dds_leg, test = "LRT" , reduced = ~1)
estimating size factors
using 'avgTxLength' from assays(dds), correcting for library size
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
plotDispEsts(dds_leg)

dds_trunk <- DESeq(dds_trunk, test = "LRT" , reduced = ~1)
estimating size factors
using 'avgTxLength' from assays(dds), correcting for library size
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
plotDispEsts(dds_trunk)

Get LFC Shrinkage estimates

lfc_leg <- get_lfc_estimates(dds_leg)
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
computing FSOS 'false sign or small' s-values (T=1)
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
computing FSOS 'false sign or small' s-values (T=1)
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
computing FSOS 'false sign or small' s-values (T=1)
thresholding s-values on alpha=0.005 to color points

thresholding s-values on alpha=0.005 to color points


out of 16228 with nonzero total read count
s-value < 0.05
LFC > 1.00 (up)    : 345, 2.1%
LFC < -1.00 (down) : 1366, 8.4%


out of 16228 with nonzero total read count
s-value < 0.05
LFC > 1.00 (up)    : 622, 3.8%
LFC < -1.00 (down) : 129, 0.79%


out of 16228 with nonzero total read count
s-value < 0.05
LFC > 1.00 (up)    : 583, 3.6%
LFC < -1.00 (down) : 316, 1.9%

lfc_trunk<- get_lfc_estimates(dds_trunk)
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
computing FSOS 'false sign or small' s-values (T=1)
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
computing FSOS 'false sign or small' s-values (T=1)
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
computing FSOS 'false sign or small' s-values (T=1)
thresholding s-values on alpha=0.005 to color points

thresholding s-values on alpha=0.005 to color points


out of 15950 with nonzero total read count
s-value < 0.05
LFC > 1.00 (up)    : 500, 3.1%
LFC < -1.00 (down) : 183, 1.1%


out of 15950 with nonzero total read count
s-value < 0.05
LFC > 1.00 (up)    : 298, 1.9%
LFC < -1.00 (down) : 40, 0.25%


out of 15950 with nonzero total read count
s-value < 0.05
LFC > 1.00 (up)    : 421, 2.6%
LFC < -1.00 (down) : 536, 3.4%

Join tables

res_lfc <-
  full_join(lfc_leg, 
            lfc_trunk,
  by = "gene_id",
  suffix = c("_leg", "_trunk")
  )  

res_lfc <- 
  res_lfc %>%
  left_join(., gene_metadata, by = "gene_id") %>%
  dplyr::select(gene_name, everything()) %>%
  mutate(gene_name= 
           scater::uniquifyFeatureNames(
             ID= gene_id,
             names=gene_name))
res_lfc 
table(res_lfc$EAN_leg)

  000   001   010   011   100   101   110   111 
13868  1048   351   210   258   155    40   298 
table(res_lfc$EAN_trunk)

  000   001   010   011   100   101   110   111 
14336   399   689   188   206    52    36    44 
table(res_lfc$EAN_trunk, res_lfc$EAN_leg)
     
        000   001   010   011   100   101   110   111
  000 11800   859   198   149   158   133    31   241
  001   326    25    10     5    23     0     0     1
  010   542    21    98    11     2     1     2     4
  011   144     8    10    13     7     1     1     2
  100   109     9     5     2    37     6     3    30
  101    25     5     0     2    17     1     2     0
  110    27     0     7     1     1     0     0     0
  111    28     1     5     0     3     2     0     4

Write to file

write_tsv(res_lfc, "leg_trunk_moderated_lfc_1_svalue_05_marked.txt")

Volcano Plots

svalue_thresh <- 0.05
plot_volcano(res_lfc, "lfc_niche_leg", "svalue_niche_leg", "niche_leg", svalue_thresh)
Saving 7 x 7 in image

plot_volcano(res_lfc, "lfc_age_leg", "svalue_age_leg", "age_leg", svalue_thresh)
Saving 10 x 8 in image

plot_volcano(res_lfc, "lfc_engraf_leg", "svalue_engraf_leg", "engraftment_leg", svalue_thresh)
Saving 10 x 8 in image

plot_volcano(res_lfc, "lfc_niche_trunk", "svalue_niche_trunk", "niche_trunk", svalue_thresh)
Saving 7 x 7 in image

plot_volcano(res_lfc, "lfc_age_trunk", "svalue_age_trunk", "age_trunk", svalue_thresh)
One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...Saving 10 x 8 in image

plot_volcano(res_lfc, "lfc_engraf_trunk", "svalue_engraf_trunk", "engraftment_trunk", svalue_thresh)
Saving 10 x 8 in image

res_lfc_hits <-
  res_lfc %>%
  dplyr::filter(EAN_leg %in% c("001", "011", "101", "111", "100", "010", "110") |
    EAN_trunk %in% c("001", "011", "101", "111", "100", "010", "110")) %>%
  arrange(EAN_leg, EAN_trunk)
res_lfc_hits
write_tsv(res_lfc_hits, "leg_trunk_moderated_lfc_1_svalue_05_marked_hits.txt")

VST

vsd_leg <-
  vst(dds_leg,
    blind = T
  )

Is there a dependence of the standard deviation (or variance) on the mean ? If there is no variance-mean dependence, then the line should be approximately horizontal

meanSdPlot(assay(vsd_leg), ranks = F)

vsd_trunk <-
  vst(dds_trunk,
    blind = T
  )
meanSdPlot(assay(vsd_trunk), ranks = F)

PCA

plot_pca(vsd_leg, "leg")
Saving 7 x 7 in image

plot_pca(vsd_trunk, "trunk")
Saving 7 x 7 in image

Hits by group

target_genes_leg_001 <-
  res_lfc %>%
  dplyr::filter(EAN_leg %in% c("001")) %>%
  arrange(svalue_niche_leg)
target_genes_leg_001
target_genes_leg_011 <-
   res_lfc%>%
  dplyr::filter(EAN_leg %in% c("011")) %>%
  arrange(-base_mean_leg)
target_genes_leg_011
target_genes_leg_101 <-
   res_lfc %>%
  dplyr::filter(EAN_leg %in% c("101"))
target_genes_leg_101
target_genes_leg_111 <-
   res_lfc %>%
  dplyr::filter(EAN_leg %in% c("111"))
target_genes_leg_111
target_genes_leg_100 <-
   res_lfc %>%
  dplyr::filter(EAN_leg %in% c("100"))
target_genes_leg_100
target_genes_leg_010 <-
   res_lfc %>%
  dplyr::filter(EAN_leg %in% c("010"))
target_genes_leg_010
target_genes_leg_110 <-
  res_lfc %>%
  dplyr::filter(EAN_leg %in% c("110"))
target_genes_leg_110
target_genes_trunk_001 <-
   res_lfc %>%
  dplyr::filter(EAN_trunk%in% c("001"))
target_genes_trunk_001
target_genes_trunk_011 <-
   res_lfc %>%
  dplyr::filter(EAN_trunk%in% c("011"))
target_genes_trunk_011
target_genes_trunk_101 <-
   res_lfc %>%
  dplyr::filter(EAN_trunk%in% c("101"))
target_genes_trunk_101
target_genes_trunk_111 <-
   res_lfc %>%
  dplyr::filter(EAN_trunk%in% c("111"))
target_genes_trunk_111
target_genes_trunk_100 <-
  res_lfc %>%
  dplyr::filter(EAN_trunk%in% c("100"))
target_genes_trunk_100
target_genes_trunk_010 <-
  res_lfc %>%
  dplyr::filter(EAN_trunk%in% c("010"))
target_genes_trunk_010
target_genes_trunk_110 <-
  res_lfc %>%
  dplyr::filter(EAN_trunk%in% c("110"))
target_genes_trunk_110

Heatmaps

meta_df_leg <- 
  as.data.frame(colData(dds_leg)[, c("age", "time")])

dt_plot_leg <- 
  scaled_tpm_leg %>%
  mutate_if(is.numeric,~ log2( .+1))
plot_heatmap(target_genes_leg_001, title = "log2_scaled_tpm_leg_001", meta_df_leg, dt_plot_leg)

plot_heatmap(target_genes_leg_011, title = "log2_scaled_tpm_leg_011", meta_df_leg, dt_plot_leg, show_genes = T)

plot_heatmap(target_genes_leg_101, title = "log2_scaled_tpm_leg_101", meta_df_leg, dt_plot_leg, show_genes = T)

plot_heatmap(target_genes_leg_111, title = "log2_scaled_tpm_leg_111", meta_df_leg, dt_plot_leg, show_genes = T)

plot_heatmap(target_genes_leg_100, title = "log2_scaled_tpm_leg_100", meta_df_leg, dt_plot_leg)

plot_heatmap(target_genes_leg_010, title = "log2_scaled_tpm_leg_010", meta_df_leg, dt_plot_leg)

plot_heatmap(target_genes_leg_110, title = "log2_scaled_tpm_leg_110", meta_df_leg, dt_plot_leg, show_genes = T)

meta_df_trunk <- 
  as.data.frame(colData(dds_trunk)[, c("age", "time")])

dt_plot_trunk <- 
  scaled_tpm_trunk %>%
  mutate_if(is.numeric,~ log2( .+1))
plot_heatmap(target_genes_trunk_001, title = "log2_scaled_tpm_trunk_001", meta_df_trunk, dt_plot_trunk, show_genes = T)

plot_heatmap(target_genes_trunk_011, title = "log2_scaled_tpm_trunk_011", meta_df_trunk, dt_plot_trunk, show_genes = T)

plot_heatmap(target_genes_trunk_101, title = "log2_scaled_tpm_trunk_101", meta_df_trunk, dt_plot_trunk, show_genes = T)

plot_heatmap(target_genes_trunk_111, title = "log2_scaled_tpm_trunk_111", meta_df_trunk, dt_plot_trunk, show_genes = T)

plot_heatmap(target_genes_trunk_100, title = "log2_scaled_tpm_trunk_100", meta_df_trunk, dt_plot_trunk, show_genes = T)

plot_heatmap(target_genes_trunk_010, title = "log2_scaled_tpm_trunk_010", meta_df_trunk, dt_plot_trunk)

plot_heatmap(target_genes_trunk_110, title = "log2_scaled_tpm_trunk_110", meta_df_trunk, dt_plot_trunk, show_genes = T)

vsd_dt <-
  full_join(
    assay(vsd_leg) %>%
      as_tibble(rownames = "gene_id"),
    assay(vsd_trunk) %>%
      as_tibble(rownames = "gene_id"),
    by="gene_id"
  )

vsd_dt_melted <-
  vsd_dt %>%
  # filter(gene_id %in% rowData(sce10x)$gene_id)  %>%
  gather(sample, vsd, -gene_id)

vsd_dt_melted <-
  left_join(vsd_dt_melted,
    meta_dt  %>% 
    dplyr::select(-name, -type, -batch),
    by = "sample"
  )
vsd_dt_melted

Melt TPM dataframes

scaled_tpm_melted <-
  scaled_tpm %>%
  gather(sample, tpm, -gene_id)

scaled_tpm_melted <- 
  left_join(scaled_tpm_melted,
  meta_dt %>% 
    dplyr::select(-name, -type, -batch),
  by = "sample"
)
scaled_tpm_melted <-
  left_join(scaled_tpm_melted,
    gene_metadata %>%
      dplyr::select(gene_name, gene_id),
    by = "gene_id"
  ) %>%
  mutate(log2_scaled_tpm = log2(tpm+1))
plot_dt <-
  scaled_tpm %>%
  dplyr::select(-gene_id) %>%
  mutate_if(is.numeric, ~ log2(.+1))

df <-
  meta_dt[, c("sample", "tissue", "age", "time")] %>%
  column_to_rownames("sample")

select <- order(rowMeans(plot_dt %>% as.matrix()),
  decreasing = TRUE
)[1:5000]
g_h2 <-
  pheatmap(plot_dt[select, ],
    cluster_rows = T,
    show_rownames = FALSE,
    cluster_cols = FALSE,
    annotation_col = df
  )
g_h2

ggsave(file.path(figures_dir, "heatmap_bulk_rnaseq_log2_scaled_tpm_5000genes.pdf"), g_h2)
Saving 10 x 18 in image

Plot individual genes

plot_gene("ENSMUSG00000026043", scaled_tpm_melted)
Saving 7 x 7 in image

p1<-  ggplot(res_lfc,
    aes(x = lfc_age_leg, y = lfc_niche_leg)
  )  +
  geom_point(size = .7, alpha=0.3) 
ggsave(file.path(figures_dir, "niche_vs_age_lfc_leg.pdf"), p1)
Saving 7 x 7 in image
p1

p2<-  ggplot(res_lfc,
    aes(x = lfc_age_leg, y = lfc_engraf_leg)
  ) +
  geom_point(size = .7, alpha=0.3) 
ggsave(file.path(figures_dir, "engraftment_vs_age_lfc_leg.pdf"), p2)
Saving 7 x 7 in image
p2

p3<-  ggplot(res_lfc,
    aes(x = lfc_niche_leg, y = lfc_engraf_leg)
  ) +
  geom_point(size = .7, alpha=0.3) 

ggsave(file.path(figures_dir, "engraftment_vs_niche_lfc_leg.pdf"), p3)
Saving 7 x 7 in image
p3

p4<-  ggplot(res_lfc,
    aes(x = lfc_age_trunk, y = lfc_niche_trunk)
  )  +
  geom_point(size = .7, alpha=0.3) 
ggsave(file.path(figures_dir, "niche_vs_age_lfc_trunk.pdf"), p4)
Saving 7 x 7 in image
p4

p5<-  ggplot(res_lfc,
    aes(x = lfc_age_trunk, y = lfc_engraf_trunk)
  ) +
  geom_point(size = .7, alpha=0.3) 
ggsave(file.path(figures_dir, "engraftment_vs_age_lfc_trunk.pdf"), p5)
Saving 7 x 7 in image
p5

p6<-  ggplot(res_lfc,
    aes(x = lfc_niche_trunk, y = lfc_engraf_trunk)
  ) +
  geom_point(size = .7, alpha=0.3) 

ggsave(file.path(figures_dir, "engraftment_vs_niche_lfc_trunk.pdf"), p6)
Saving 7 x 7 in image
p6

Save all target genes

p3<-  ggplot(res_lfc,
    aes(x = age_leg, y = engraf_leg)
  ) +
  geom_point(size = .5, alpha=0.1) 

p3

sessionInfo()
R version 3.6.3 (2020-02-29)
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        LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8   
 [6] LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C               LC_TELEPHONE=C            
[11] 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] ensembldb_2.10.2            AnnotationFilter_1.10.0     GenomicFeatures_1.38.2      AnnotationDbi_1.48.0       
 [5] AnnotationHub_2.18.0        BiocFileCache_1.10.2        dbplyr_1.4.2                pheatmap_1.0.12            
 [9] EnhancedVolcano_1.4.0       ggrepel_0.8.2               apeglm_1.8.0                glmpca_0.1.0               
[13] cowplot_1.0.0               vsn_3.54.0                  DESeq2_1.26.0               SummarizedExperiment_1.16.1
[17] DelayedArray_0.12.2         BiocParallel_1.20.1         matrixStats_0.55.0          Biobase_2.46.0             
[21] GenomicRanges_1.38.0        GenomeInfoDb_1.22.0         IRanges_2.20.2              S4Vectors_0.24.3           
[25] BiocGenerics_0.32.0         tximport_1.14.0             forcats_0.5.0               stringr_1.4.0              
[29] dplyr_0.8.5                 purrr_0.3.3                 readr_1.3.1                 tidyr_1.0.2                
[33] tibble_2.1.3                ggplot2_3.3.0               tidyverse_1.3.0            

loaded via a namespace (and not attached):
  [1] tidyselect_1.0.0              RSQLite_2.2.0                 htmlwidgets_1.5.1             grid_3.6.3                   
  [5] munsell_0.5.0                 preprocessCore_1.48.0         withr_2.1.2                   colorspace_1.4-1             
  [9] knitr_1.28                    rstudioapi_0.11               SingleCellExperiment_1.8.0    labeling_0.3                 
 [13] bbmle_1.0.23.1                GenomeInfoDbData_1.2.2        bit64_0.9-7                   farver_2.0.3                 
 [17] rhdf5_2.30.1                  rprojroot_1.3-2               coda_0.19-3                   vctrs_0.2.4                  
 [21] generics_0.0.2                xfun_0.12                     R6_2.4.1                      ggbeeswarm_0.6.0             
 [25] rsvd_1.0.3                    locfit_1.5-9.1                bitops_1.0-6                  assertthat_0.2.1             
 [29] promises_1.1.0                scales_1.1.0                  nnet_7.3-13                   beeswarm_0.2.3               
 [33] gtable_0.3.0                  affy_1.64.0                   rlang_0.4.5                   genefilter_1.68.0            
 [37] splines_3.6.3                 rtracklayer_1.46.0            lazyeval_0.2.2                acepack_1.4.1                
 [41] hexbin_1.28.1                 broom_0.5.5                   checkmate_2.0.0               BiocManager_1.30.10          
 [45] yaml_2.2.1                    modelr_0.1.6                  backports_1.1.5               httpuv_1.5.2                 
 [49] Hmisc_4.3-1                   tools_3.6.3                   affyio_1.56.0                 ellipsis_0.3.0               
 [53] RColorBrewer_1.1-2            Rcpp_1.0.3                    plyr_1.8.6                    base64enc_0.1-3              
 [57] progress_1.2.2                zlibbioc_1.32.0               RCurl_1.98-1.1                prettyunits_1.1.1            
 [61] rpart_4.1-15                  openssl_1.4.1                 viridis_0.5.1                 haven_2.2.0                  
 [65] cluster_2.1.0                 fs_1.3.2                      magrittr_1.5                  data.table_1.12.8            
 [69] reprex_0.3.0                  mvtnorm_1.1-0                 ProtGenerics_1.18.0           hms_0.5.3                    
 [73] mime_0.9                      evaluate_0.14                 xtable_1.8-4                  XML_3.99-0.3                 
 [77] emdbook_1.3.12                jpeg_0.1-8.1                  readxl_1.3.1                  gridExtra_2.3                
 [81] compiler_3.6.3                biomaRt_2.42.0                bdsmatrix_1.3-4               scater_1.14.6                
 [85] crayon_1.3.4                  htmltools_0.4.0               later_1.0.0                   Formula_1.2-3                
 [89] geneplotter_1.64.0            lubridate_1.7.4               DBI_1.1.0                     MASS_7.3-51.5                
 [93] rappdirs_0.3.1                Matrix_1.2-18                 cli_2.0.2                     pkgconfig_2.0.3              
 [97] GenomicAlignments_1.22.1      numDeriv_2016.8-1.1           foreign_0.8-75                xml2_1.2.4                   
[101] annotate_1.64.0               vipor_0.4.5                   XVector_0.26.0                rvest_0.3.5                  
[105] digest_0.6.25                 Biostrings_2.54.0             rmarkdown_2.1                 cellranger_1.1.0             
[109] htmlTable_1.13.3              DelayedMatrixStats_1.8.0      curl_4.3                      shiny_1.4.0                  
[113] Rsamtools_2.2.3               lifecycle_0.2.0               nlme_3.1-144                  jsonlite_1.6.1               
[117] Rhdf5lib_1.8.0                BiocNeighbors_1.4.2           viridisLite_0.3.0             askpass_1.1                  
[121] limma_3.42.2                  fansi_0.4.1                   pillar_1.4.3                  lattice_0.20-40              
[125] fastmap_1.0.1                 httr_1.4.1                    survival_3.1-8                interactiveDisplayBase_1.24.0
[129] glue_1.3.1                    png_0.1-7                     BiocVersion_3.10.1            bit_1.1-15.2                 
[133] stringi_1.4.6                 blob_1.2.1                    BiocSingular_1.2.2            latticeExtra_0.6-29          
[137] memoise_1.1.0                 irlba_2.3.3                  
LS0tCnRpdGxlOiAiTW91c2UgbXVzY2xlIHN0ZW0gY2VsbCBlbmdyYWZ0bWVudDogQnVsayBSTkEtc2VxIGdlbmUgZGlmZmVyZW50aWFsIGFuYWx5c2lzIgphdXRob3I6IAotIG5hbWU6IFJpY2sgRmFyb3VuaQogIGFmZmlsaWF0aW9uOgogIC0gJmNydWsgR8Opbm9tZSBRdcOpYmVjIElubm92YXRpb24gQ2VudHJlLCBNY0dpbGwgVW5pdmVyc2l0eSwgTW9udHJlYWwsIENhbmFkYQpkYXRlOiAnYHIgZm9ybWF0KFN5cy5EYXRlKCksICIlWS0lQi0lZCIpYCcKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdG9jOiBubwogICAgdG9jX2Zsb2F0OiAKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQotLS0KCiMgUHJlcGFyZSBhbmFseXNpcyB3b3JrZmxvdwoKCmBgYHtyIHNldHVwfQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9IHJwcm9qcm9vdDo6ZmluZF9yc3R1ZGlvX3Jvb3RfZmlsZSgpKQpvcHRpb25zKAogIHJlYWRyLnNob3dfcHJvZ3Jlc3MgPSBGQUxTRSwKICBkaWdpdHMgPSAyLAogIHNjaXBlbiA9IDgsCiAgZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9ICtJbmYKKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHR4aW1wb3J0KQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeSh2c24pCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShnbG1wY2EpCmxpYnJhcnkoYXBlZ2xtKQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKbGlicmFyeShwaGVhdG1hcCkKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCmBgYAoKCiMjIEF1eGlsaWFyeSBmdW5jdGlvbnMKCmBgYHtyfQpnZXRfZGVzZXFfZGF0YSA8LSBmdW5jdGlvbih0YXJnZXRfdGlzc3VlLCBtZXRhX2R0LCBkYXRhX2RpciwgdHgya25vd25HZW5lKSB7CiAgCiAgbWV0YV9kdCA8LQogIG1ldGFfZHQgJT4lCiAgZHBseXI6OmZpbHRlcih0aXNzdWUgPT0gdGFyZ2V0X3Rpc3N1ZSkKICAKICAgc2FtcGxlc19kaXIgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFfZHQgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxsKG5hbWUpKQogICBjZG5hX2RpciA8LSBmaWxlLnBhdGgoc2FtcGxlc19kaXIpCiAgY2RuYV9maWxlcyA8LSBmaWxlLnBhdGgoY2RuYV9kaXIsICJhYnVuZGFuY2UuaDUiKQogIG5hbWVzKGNkbmFfZmlsZXMpIDwtIAogICAgbWV0YV9kdCAlPiUgCiAgICBwdWxsKHNhbXBsZSkKICAKICAKICB0eGlfc2NhbGVkVFBNIDwtIHR4aW1wb3J0KGNkbmFfZmlsZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJrYWxsaXN0byIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eDJnZW5lPXR4Mmtub3duR2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50c0Zyb21BYnVuZGFuY2UgPSBjKCJzY2FsZWRUUE0iKSkKICAKICB0eGkgPC0gdHhpbXBvcnQoY2RuYV9maWxlcywKICAgICAgICAgICAgICAgICAgdHlwZSA9ICJrYWxsaXN0byIsCiAgICAgICAgICAgICAgICAgIHR4MmdlbmU9dHgya25vd25HZW5lLCAKICAgICAgICAgICAgICAgICAgY291bnRzRnJvbUFidW5kYW5jZSA9IGMoIm5vIikpCiAgc2NhbGVkX3RwbSA8LSAKICAgIHR4aV9zY2FsZWRUUE0kY291bnRzICU+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZV9pZCIpCgogIGdlbmVfY291bnRzIDwtIAogICAgdHhpJGNvdW50cyAlPiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gImdlbmVfaWQiKQogIAogIHdyaXRlX3RzdihzY2FsZWRfdHBtLCBwYXN0ZTAodGFyZ2V0X3Rpc3N1ZSwgIl9lbmdyYWZ0bWVudF9zY2FsZWRfdHBtLnRzdiIpKQogIHdyaXRlX3RzdihnZW5lX2NvdW50cywgcGFzdGUwKHRhcmdldF90aXNzdWUsICJfZW5ncmFmdG1lbnRfY291bnRzLnRzdiIpKQogIAogIGRkcyA8LSBERVNlcURhdGFTZXRGcm9tVHhpbXBvcnQodHhpLAogIG1ldGFfZHQgJT4lCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoInNhbXBsZSIpLAogIGRlc2lnbiA9IH4gdGltZSArIGFnZSArIHRpbWU6YWdlKQogIGtlZXAgPC0gcm93U3Vtcyhjb3VudHMoZGRzKSA+PSAzMCkgPj0gMiAKICBkZHMgPC0gZGRzW2tlZXAsXQogIAogIHJldHVybihkZHMpCn0KCiNXZSB1c2UgYXBlZ2xtIHNocmlua2FnZSB0byBwcmVzZXJ2ZSB0aGUgc2l6ZSBvZiBsYXJnZSBMRkMgYW5kIGNvbXB1dGUgcy12YWx1ZXMgKHRoZSBlc3RpbWF0ZWQgcmF0ZSBvZiBmYWxzZSBzaWduKS4gU2hyaW5rYWdlIGlzIHVzZWZ1bCBmb3IgcmFua2luZyBhbmQgdmlzdWFsaXphdGlvbiwgd2l0aG91dCB0aGUgbmVlZCBmb3IgYXJiaXRyYXJ5IGZpbHRlcnMgb24gbG93IGNvdW50IGdlbmVzLiBodHRwczovL2RvaS5vcmcvMTAuMTA5My9iaW9pbmZvcm1hdGljcy9idHk4OTUKCiNUaGUgbG9jYWwgZmFsc2Ugc2lnbiByYXRlIEZTUiBpcyBkZWZpbmVkIGFzIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgdGhhdCB0aGUgcG9zdGVyaW9yIG1vZGUgKE1BUCkgb2YgYmV0YSAobG9nMkZDKSBpcyBvZiBhIGJpb2xvZ2ljYWxseSBzaWduaWZpY2FudCBlZmZlY3Qgc2l6ZSAoaS5lLiBhYnMoYmV0YSkgPiBsZmNUaHJlc2hvbGQgKQoKZ2V0X2xmY19lc3RpbWF0ZXMgPC0gZnVuY3Rpb24oZGRzLCBsZmNfdGhyZXNob2xkID0gMSwgc3ZhbHVlX3RocmVzaCA9IDAuMDUpewogIApuaWNoZV9sZmMgPC0KICBsZmNTaHJpbmsoZGRzLAogICAgY29lZiA9ICJ0aW1lVDIxLmFnZW9sZCIsCiAgICB0eXBlID0gImFwZWdsbSIsCiAgICBzdmFsdWUgPSBULAogICAgbGZjVGhyZXNob2xkID0gbGZjX3RocmVzaG9sZAogICkKCmVuZ3JhZm1lbnRfbGZjIDwtCiAgbGZjU2hyaW5rKGRkcywKICAgIGNvZWYgPSAidGltZV9UMjFfdnNfVDAiLAogICAgdHlwZSA9ICJhcGVnbG0iLAogICAgc3ZhbHVlID0gVCwKICAgIGxmY1RocmVzaG9sZCA9IGxmY190aHJlc2hvbGQKICApCgphZ2VfbGZjIDwtCiAgbGZjU2hyaW5rKGRkcywKICAgIGNvZWYgPSAiYWdlX29sZF92c195b3VuZyIsCiAgICB0eXBlID0gImFwZWdsbSIsCiAgICBzdmFsdWUgPSBULAogICAgbGZjVGhyZXNob2xkID0gbGZjX3RocmVzaG9sZAogICkKCgoKcGxvdE1BKG5pY2hlX2xmYywgeWxpbSA9IGMoLTIyLCA4KSkKYWJsaW5lKGggPSBjKC0xLCAxKSwgY29sID0gImRvZGdlcmJsdWUiLCBsd2QgPSAyKQoKcGxvdE1BKGVuZ3JhZm1lbnRfbGZjLCB5bGltID0gYygtMjIsIDgpKQphYmxpbmUoaCA9IGMoLTEsIDEpLCBjb2wgPSAiZG9kZ2VyYmx1ZSIsIGx3ZCA9IDIpCgpwbG90TUEoYWdlX2xmYywgeWxpbSA9IGMoLTIyLCA4KSkKYWJsaW5lKGggPSBjKC0xLCAxKSwgY29sID0gImRvZGdlcmJsdWUiLCBsd2QgPSAyKQoKc3VtbWFyeShuaWNoZV9sZmMsIGFscGhhID0gc3ZhbHVlX3RocmVzaCkKc3VtbWFyeShlbmdyYWZtZW50X2xmYywgYWxwaGEgPSBzdmFsdWVfdGhyZXNoKQpzdW1tYXJ5KGFnZV9sZmMsIGFscGhhID0gc3ZhbHVlX3RocmVzaCkKCgpsZmMgPC0KICBiaW5kX2NvbHMoCiAgICBtY29scyhkZHMpWywgYygiSW50ZXJjZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAidGltZV9UMjFfdnNfVDAiLCAKICAgICAgICAgICAgICAgICAgICAgICAiYWdlX29sZF92c195b3VuZyIsCiAgICAgICAgICAgICAgICAgICAgICAgInRpbWVUMjEuYWdlb2xkIildICU+JQogICAgICBhc190aWJibGUoKSAlPiUKICAgICAgc2V0X25hbWVzKGMoImludGVyY2VwdCIsICJlbmdyYWYiLCAiYWdlIiwgIm5pY2hlIikpLAogICAgZW5ncmFmbWVudF9sZmMgJT4lCiAgICAgIGFzX3RpYmJsZShyb3duYW1lcyA9ICJnZW5lX2lkIikgICU+JQogICAgICBzZXRfbmFtZXMoYygKICAgICAgICBjKCJnZW5lX2lkIiwgImJhc2VfbWVhbiIpLAogICAgICAgIHBhc3RlMCgKICAgICAgICAgIGMoImxmYyIsICJsZmNfc2UiLCAic3ZhbHVlIiksCiAgICAgICAgICAiX2VuZ3JhZiIKICAgICAgICApKSksCiAgICBhZ2VfbGZjICU+JQogICAgICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZV9pZCIpICU+JQogICAgICBkcGx5cjo6c2VsZWN0KC1iYXNlTWVhbiwgLWdlbmVfaWQpICAlPiUKICAgICAgc2V0X25hbWVzKHBhc3RlMCgKICAgICAgICBjKCJsZmMiLCAibGZjX3NlIiwgInN2YWx1ZSIpLCAKICAgICAgICAiX2FnZSIKICAgICAgKSkgLAogICAgbmljaGVfbGZjICU+JQogICAgICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZV9pZCIpICU+JQogICAgICBkcGx5cjo6c2VsZWN0KC1iYXNlTWVhbiwgLWdlbmVfaWQpICAgICU+JQogICAgICBzZXRfbmFtZXMocGFzdGUwKAogICAgICAgIGMoImxmYyIsICJsZmNfc2UiLCAic3ZhbHVlIiksIAogICAgICAgICJfbmljaGUiKSkpICAlPiUKICBtdXRhdGUoCiAgICBrZWVwX25pY2hlID0gKGFicyhsZmNfbmljaGUpID4gbGZjX3RocmVzaG9sZCAmCiAgICAgIHN2YWx1ZV9uaWNoZSA8IHN2YWx1ZV90aHJlc2gpLAogICAga2VlcF9hZ2UgPSAoYWJzKGxmY19hZ2UpID4gbGZjX3RocmVzaG9sZCAmCiAgICAgIHN2YWx1ZV9hZ2UgPCBzdmFsdWVfdGhyZXNoKSwKICAgIGtlZXBfZW5ncmFmID0gKGFicyhsZmNfZW5ncmFmKSA+IGxmY190aHJlc2hvbGQgJgogICAgICBzdmFsdWVfZW5ncmFmIDwgc3ZhbHVlX3RocmVzaCkKICApICAlPiUKICBtdXRhdGUoCiAgICBFQU4gPSBwYXN0ZTAoCiAgICAgIGFzLmludGVnZXIoa2VlcF9lbmdyYWYpLAogICAgICBhcy5pbnRlZ2VyKGtlZXBfYWdlKSwKICAgICAgYXMuaW50ZWdlcihrZWVwX25pY2hlKQogICAgKSkKCmxmYyA8LQogIGxmYyAlPiUKICAgZHBseXI6OnNlbGVjdChnZW5lX2lkLEVBTiwgYmFzZV9tZWFuLCAgCiAgICBpbnRlcmNlcHQsIGVuZ3JhZiwgYWdlLCBuaWNoZSwgCiAgICBsZmNfZW5ncmFmLCBzdmFsdWVfZW5ncmFmLCBsZmNfYWdlLCBzdmFsdWVfYWdlLAogICAgbGZjX25pY2hlLCBzdmFsdWVfbmljaGUsIGV2ZXJ5dGhpbmcoKQogICkgCn0KCgpwbG90X3ZvbGNhbm8gPC0gZnVuY3Rpb24oZGF0YSwgeHZhciwgeXZhciwgdGl0bGUsIHN2YWx1ZV90aHJlc2gpIHsKICBndiA8LQogICAgRW5oYW5jZWRWb2xjYW5vKGRhdGEsCiAgICAgIGxhYiA9IGRhdGEgJT4lIHB1bGwoZ2VuZV9uYW1lKSwKICAgICAgeGxhYiA9IGJxdW90ZSh+IGl0YWxpYyhNb2RlcmF0ZWQpIH4gTG9nWzJdIH4gRkMpLAogICAgICB5bGFiID0gYnF1b3RlKH4gLUxvZ1sxMF0gfiBpdGFsaWMoc3ZhbHVlKSksCiAgICAgIHggPSB4dmFyLAogICAgICB5ID0geXZhciwKICAgICAgY29sID0gYygiZ3JleTMwIiwgImZvcmVzdGdyZWVuIiwgInJlZDIiLCAicm95YWxibHVlIiksCiAgICAgIHBDdXRvZmYgPSBzdmFsdWVfdGhyZXNoLAogICAgICBsZWdlbmQgPSBjKCJOUyIsICJMb2cyRkMiLCAic3ZhbHVlIiwgInN2YWx1ZSAmIExvZzJGQyIpLAogICAgICBsZWdlbmRMYWJlbHMgPSBjKAogICAgICAgICJOUyIsCiAgICAgICAgZXhwcmVzc2lvbihMb2dbMl0gfiBGQyksCiAgICAgICAgInN2YWx1ZSIsCiAgICAgICAgZXhwcmVzc2lvbihzIC0gdmFsdWUgfiBhbmQgfiBsb2dbMl0gfiBGQykKICAgICAgKQogICAgKQoKICBnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCBwYXN0ZTAodGl0bGUsICJfdm9sY2Fuby5wZGYiKSksIGd2KQogIHJldHVybihndikKfQoKcGxvdF9oZWF0bWFwIDwtIGZ1bmN0aW9uKHRhcmdldF9nZW5lcywgdGl0bGUsIG1ldGFfY29sLCBkYXRhLCBzaG93X2dlbmVzID0gRikgewogIGRmIDwtCiAgICBsZWZ0X2pvaW4odGFyZ2V0X2dlbmVzICU+JQogICAgICBkcGx5cjo6c2VsZWN0KGdlbmVfaWQsIGdlbmVfbmFtZSksCiAgICBkYXRhLAogICAgYnkgPSAiZ2VuZV9pZCIKICAgICkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1nZW5lX2lkKSAlPiUKICAgIGNvbHVtbl90b19yb3duYW1lcygiZ2VuZV9uYW1lIikgJT4lCiAgICBhcy5tYXRyaXgoKQoKICBnaCA8LQogICAgcGhlYXRtYXAoZGYsCiAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShyZXYoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG4gPSAxMSwgbmFtZSA9ICJTcGVjdHJhbCIpKSkoMTAwKSwKICAgICAgY2x1c3Rlcl9yb3dzID0gVCwKICAgICAgc2hvd19yb3duYW1lcyA9IHNob3dfZ2VuZXMsCiAgICAgIHRyZWVoZWlnaHRfcm93ID0gMCwKICAgICAgZm9udHNpemVfY29sID0gNCwKICAgICAgZm9udHNpemVfcm93ID0gNiwKICAgICAgYW5nbGVfY29sID0gIjQ1IiwKICAgICAgY2x1c3Rlcl9jb2xzID0gRkFMU0UsCiAgICAgIGFubm90YXRpb25fY29sID0gbWV0YV9jb2wKICAgICkKICBnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCBwYXN0ZTAodGl0bGUsICJfaGVhdG1hcF9leHByZXNzaW9uLnBkZiIpKSwgZ2gpCgogIHJldHVybihnaCkKfQoKcGxvdF9wY2EgPC0gZnVuY3Rpb24odnNkLCB0aXRsZSkgewogIAogIHBjYURhdGEgPC0KICBwbG90UENBKHZzZCwgCiAgICAgICAgICBpbnRncm91cCA9IGMoICJ0aW1lIiwgImFnZSIsICJ0aXNzdWUiLCAiYmF0Y2giLCAiZG9ub3IiKSwKICAgICAgICAgIHJldHVybkRhdGEgPSBUUlVFKSAgCnBlcmNlbnRWYXIgPC0gcm91bmQoMTAwICogYXR0cihwY2FEYXRhLCAicGVyY2VudFZhciIpKQoKZ2dfcGxvdCA8LSAKICBnZ3Bsb3QocGNhRGF0YSAlPiUKICBtdXRhdGUoY29uZGl0aW9uPSBwYXN0ZShhZ2UsIHRpbWUsIHNlcD0iXyIpKSwKICBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgIGNvbG9yID0gdGltZSwgc2hhcGU9YWdlLCBsYWJlbCA9IG5hbWUgKSkgKwogIGdlb21fcG9pbnQoIHNpemU9NCkgKwogICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gZG9ub3IpLCBjb2xvdXI9ImdyZXkiKSArCiMgIGNvb3JkX2ZpeGVkKCkgKwogIGdlb21fdGV4dChjaGVja19vdmVybGFwPVQsIGFuZ2xlID0gMCwgc2l6ZT0zLCB2anVzdCA9IDMsIG51ZGdlX3kgPSAxLjUpICsgCiAgeGxhYihwYXN0ZTAoIlBDMTogIiwgcGVyY2VudFZhclsxXSwgIiUgdmFyaWFuY2UiKSkgKwogIHlsYWIocGFzdGUwKCJQQzI6ICIsIHBlcmNlbnRWYXJbMl0sICIlIHZhcmlhbmNlIikpICsKICBjb29yZF9maXhlZCgpICsKICBnZ3RpdGxlKCJQQ0Egd2l0aCBWU1QgZGF0YSIpCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsIHBhc3RlMCh0aXRsZSwgIl9wY2FfYnVsa19ybmFzZXEucGRmIikpLCBnZ19wbG90KQpnZ19wbG90Cn0KCnBsb3RfZ2VuZSA8LSBmdW5jdGlvbihnZW5lLCBkYXRhKSB7CiAgZGZfcGxvdCA8LQogICAgZGF0YSAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoZ2VuZV9pZCA9PSBnZW5lKQogIAogIGdlbmVfbmFtZSA8LSBkZl9wbG90JGdlbmVfbmFtZVsxXQogIHAgPC0gZ2dwbG90KAogICAgZGZfcGxvdCwKICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IGxvZzJfc2NhbGVkX3RwbSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGRvbm9yKSkgKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBkb25vcikpICArCiAgICBsYWJzKHRpdGxlID0gZ2VuZV9uYW1lKSsgCiAgZmFjZXRfZ3JpZCh0aXNzdWUgfiBhZ2UpCiAgZ2dzYXZlKGZpbGUucGF0aChmaWd1cmVzX2RpciwgcGFzdGUwKGdlbmVfbmFtZSwgIl9leHByZXNzaW9uX2xvZzJfc2NhbGVkX3RwbS5wZGYiKSksIHApCiAgcmV0dXJuKHApCn0KYGBgICAKCiMjIFNldCBmaWxlcGF0aHMKCmBgYHtyfQpmaWd1cmVzX2RpciA8LSAiLi9maWd1cmVzIgpkYXRhX2RpciA8LSAiLi9kYXRhL2thbGxpc3RvX2J1bGtfc3BsaWNlZCIKZ2VuZV9tZXRhZGF0YV9maWxlcGF0aCA8LSAifi9Eb2N1bWVudHMvZ2Vub21lX21ldGFkYXRhL2RhdGEvbW0xMF9lbnM5N19nZW5lX21ldGEudHh0IgpgYGAKCgoKYGBge3J9Cm1ldGFfZHQgPC0KICByZWFkX3RzdihmaWxlLnBhdGgoCiAgICBkYXRhX2RpciwKICAgICJtZXRhLnRzdiIKICApKSAlPiUKICAjIGFycmFuZ2UodGlzc3VlLCBhZ2UsIHRpbWUpICU+JQogIG11dGF0ZSgKICAgIGNvbmRpdGlvbiA9IHBhc3RlKHRpc3N1ZSwgYWdlLCB0aW1lLCBzZXAgPSAiXyIpLAogICAgYWdlID0gZmFjdG9yKGFnZSwgbGV2ZWxzID0gYygieW91bmciLCAib2xkIikpLAogICAgdGlzc3VlID0gZmFjdG9yKHRpc3N1ZSwgbGV2ZWxzID0gYygibGVnIiwgInRydW5rIikpLAogICAgdGltZSA9IGZhY3Rvcih0aW1lLCBsZXZlbHMgPSBjKCJUMCIsICJUMjEiKSksCiAgICBiYXRjaCA9IGZhY3RvcihiYXRjaCwgbGV2ZWxzID0gYygxLCAyKSkKICApCmBgYAoKCiMjIEdldCB0eCBpZCB2ZXJzaW9ucwoKYGBge3J9CmxpYnJhcnkoQW5ub3RhdGlvbkh1YikKYWggPC0gQW5ub3RhdGlvbkh1YigpCmVkYiA8LSBhaFtbIkFINzM5MDUiXV0Kc2VxbGV2ZWxzU3R5bGUoZWRiKSA8LSAiVUNTQyIKI3N0YW5kYXJkX2Nocm9tcyA8LSBzdGFuZGFyZENocm9tb3NvbWVzKGVkYiwgc3BlY2llcyA9IG9yZ2FuaXNtKGVkYikpCnR4X21ldGEgPC0gCnRyYW5zY3JpcHRzKGVkYiwKICAgICAgICAgICAgY29sdW1ucz1jKCJ0eF9pZF92ZXJzaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICJnZW5lX2lkIiwKICAgICAgICAgICAgICAgICAgICAgImdlbmVfbmFtZSIsCiAgICAgICAgICAgICAgICAgICAgICJnZW5lX2lkX3ZlcnNpb24iKSwKICAgICAgICAgICAgcmV0dXJuLnR5cGU9IkRhdGFGcmFtZSIpCnR4Mmtub3duR2VuZSA8LSAgCiAgdHhfbWV0YSAlPiUgCiAgYXNfdGliYmxlKCkgJT4lCiAgZHBseXI6OnNlbGVjdCh0eF9pZF92ZXJzaW9uLCBnZW5lX2lkKQpgYGAKCgpgYGB7cn0KZ2VuZV9tZXRhZGF0YSA8LQogIHJlYWRfY3N2KGdlbmVfbWV0YWRhdGFfZmlsZXBhdGgpICU+JQogIGRwbHlyOjpzZWxlY3QoLWdlbmVfaWRfdmVyc2lvbikgCgpnZW5lX21ldGFkYXRhIDwtbGVmdF9qb2luKHR4X21ldGEgJT4lIGFzX3RpYmJsZSgpICU+JSAKICAgICAgICAgICAgZ3JvdXBfYnkoZ2VuZV9pZCkgJT4lIAogICAgICAgICAgICBkcGx5cjo6Y291bnQoIGdlbmVfbmFtZSkgJT4lIAogICAgICAgICAgICBkcGx5cjo6c2VsZWN0KC1uKSwKICAgICAgICAgIGdlbmVfbWV0YWRhdGEgJT4lCiAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KC1nZW5lX25hbWUpLAogICAgICAgICAgYnk9ImdlbmVfaWQiKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lPSAKICAgICAgICAgICBzY2F0ZXI6OnVuaXF1aWZ5RmVhdHVyZU5hbWVzKAogICAgICAgICAgICAgSUQ9IGdlbmVfaWQsCiAgICAgICAgICAgICBuYW1lcz1nZW5lX25hbWUpICkKYGBgCgoKIyMgTG9hZCBkYXRhCiAgCmBgYHtyfQpkZHNfbGVnIDwtIGdldF9kZXNlcV9kYXRhKCJsZWciLCBtZXRhX2R0LCBkYXRhX2RpciwgdHgya25vd25HZW5lKQpkZHNfbGVnIApgYGAKCmBgYHtyfQpkZHNfdHJ1bmsgPC0gZ2V0X2Rlc2VxX2RhdGEoInRydW5rIiwgbWV0YV9kdCwgZGF0YV9kaXIsIHR4Mmtub3duR2VuZSkKZGRzX3RydW5rCmBgYAoKCmBgYHtyfQpzdW1tYXJ5X2R0X2xlZyA8LQogIGNvbFN1bXMoYXNzYXkoZGRzX2xlZykpICU+JQogIHRpYmJsZTo6ZW5mcmFtZShuYW1lID0gInNhbXBsZSIsIHZhbHVlID0gImNvdW50cyIpCnN1bW1hcnlfZHRfbGVnCmBgYAoKCgpgYGB7cn0Kc3VtbWFyeV9kdF90cnVuayA8LQogIGNvbFN1bXMoYXNzYXkoZGRzX3RydW5rKSkgJT4lCiAgdGliYmxlOjplbmZyYW1lKG5hbWUgPSAic2FtcGxlIiwgdmFsdWUgPSAiY291bnRzIikKc3VtbWFyeV9kdF90cnVuawpgYGAKCgojIyMgc2NhbGVkIHRwbQoKYGBge3J9CnNjYWxlZF90cG1fbGVnIDwtIHJlYWRfdHN2KCJsZWdfZW5ncmFmdG1lbnRfc2NhbGVkX3RwbS50c3YiKQpzY2FsZWRfdHBtX3RydW5rIDwtIHJlYWRfdHN2KCJ0cnVua19lbmdyYWZ0bWVudF9zY2FsZWRfdHBtLnRzdiIpCmBgYAoKYGBge3J9CnNjYWxlZF90cG0gPC0KICBmdWxsX2pvaW4oc2NhbGVkX3RwbV9sZWcsIAogICAgICAgICAgICBzY2FsZWRfdHBtX3RydW5rLCAKICAgICAgICAgICAgImdlbmVfaWQiKQpgYGAKCgojIyMgR2VuZXJhbGl6ZWQgUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcwoKYGBge3J9CnNldC5zZWVkKDQyMCkKZ3BjYSA8LSBnbG1wY2Eoc2NhbGVkX3RwbVssIDI6MjJdICU+JQogIGRwbHlyOjpmaWx0ZXIocm93U3VtcyguWzE6MjFdKSA+IDApLCBMID0gMikKZ3BjYS5kYXQgPC0gZ3BjYSRmYWN0b3JzCmdwY2EuZGF0IDwtIGNiaW5kKGdwY2EuZGF0LCBtZXRhX2R0WywgYygiYWdlIiwgInRpc3N1ZSIsICJ0aW1lIiwgImJhdGNoIiwgImRvbm9yIildKQpncGNhLmRhdApgYGAKCiAgICAKYGBge3IgZmlnLndpZHRoPTEyfQpnZ19wY2EgPC0KICBnZ3Bsb3QoCiAgICBncGNhLmRhdCAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gInNhbXBsZSIpICU+JQogICAgICBtdXRhdGUoY29uZGl0aW9uID0gcGFzdGUoYWdlLCB0aW1lLCBzZXAgPSAiXyIpKSwKICAgIGFlcyh4ID0gZGltMSwgeSA9IGRpbTIsIGNvbG9yID0gY29uZGl0aW9uLCBzaGFwZSA9IGJhdGNoLCBsYWJlbCA9IHNhbXBsZSkKICApICsKICBnZW9tX3BvaW50KHNpemUgPSA0KSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGRvbm9yKSwgY29sb3VyID0gImdyZXkiKSArCiAgIyAgY29vcmRfZml4ZWQoKSArCiAgZ2VvbV90ZXh0KGNoZWNrX292ZXJsYXAgPSBULCBhbmdsZSA9IDAsIHNpemUgPSAzLCB2anVzdCA9IDIsIG51ZGdlX3kgPSAxLjUpICsKICAjIGZhY2V0X2dyaWQofnRpc3N1ZSkgKwogIGdndGl0bGUoImdsbXBjYSAtIEdlbmVyYWxpemVkIFBDQSIpCmdnX3BjYQpnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCAiZ2xtcGNhX2J1bGtfcm5hc2VxLnBkZiIpLCBnZ19wY2EpCmBgYAoKCiMjIEZpdCBNb2RlbAoKCmBgYHtyfQpkZHNfbGVnIDwtIERFU2VxKGRkc19sZWcsIHRlc3QgPSAiTFJUIiAsIHJlZHVjZWQgPSB+MSkKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTB9CnBsb3REaXNwRXN0cyhkZHNfbGVnKQpgYGAKCgpgYGB7cn0KZGRzX3RydW5rIDwtIERFU2VxKGRkc190cnVuaywgdGVzdCA9ICJMUlQiICwgcmVkdWNlZCA9IH4xKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9MTB9CnBsb3REaXNwRXN0cyhkZHNfdHJ1bmspCmBgYAoKCiMjIyBHZXQgTEZDIFNocmlua2FnZSBlc3RpbWF0ZXMKCmBgYHtyfQpsZmNfbGVnIDwtIGdldF9sZmNfZXN0aW1hdGVzKGRkc19sZWcpCmBgYAoKCmBgYHtyfQpsZmNfdHJ1bms8LSBnZXRfbGZjX2VzdGltYXRlcyhkZHNfdHJ1bmspCmBgYAoKIyMjIEpvaW4gdGFibGVzCgpgYGB7cn0KcmVzX2xmYyA8LQogIGZ1bGxfam9pbihsZmNfbGVnLCAKICAgICAgICAgICAgbGZjX3RydW5rLAogIGJ5ID0gImdlbmVfaWQiLAogIHN1ZmZpeCA9IGMoIl9sZWciLCAiX3RydW5rIikKICApICAKCnJlc19sZmMgPC0gCiAgcmVzX2xmYyAlPiUKICBsZWZ0X2pvaW4oLiwgZ2VuZV9tZXRhZGF0YSwgYnkgPSAiZ2VuZV9pZCIpICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZV9uYW1lLCBldmVyeXRoaW5nKCkpICU+JQogIG11dGF0ZShnZW5lX25hbWU9IAogICAgICAgICAgIHNjYXRlcjo6dW5pcXVpZnlGZWF0dXJlTmFtZXMoCiAgICAgICAgICAgICBJRD0gZ2VuZV9pZCwKICAgICAgICAgICAgIG5hbWVzPWdlbmVfbmFtZSkpCnJlc19sZmMgCmBgYAoKCmBgYHtyfQp0YWJsZShyZXNfbGZjJEVBTl9sZWcpCmBgYAoKYGBge3J9CnRhYmxlKHJlc19sZmMkRUFOX3RydW5rKQpgYGAKCgpgYGB7cn0KdGFibGUocmVzX2xmYyRFQU5fdHJ1bmssIHJlc19sZmMkRUFOX2xlZykKYGBgCgojIyMgV3JpdGUgdG8gZmlsZQoKYGBge3J9CndyaXRlX3RzdihyZXNfbGZjLCAibGVnX3RydW5rX21vZGVyYXRlZF9sZmNfMV9zdmFsdWVfMDVfbWFya2VkLnR4dCIpCmBgYAoKCiMjIyBWb2xjYW5vIFBsb3RzCgoKYGBge3J9CnN2YWx1ZV90aHJlc2ggPC0gMC4wNQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwfQpwbG90X3ZvbGNhbm8ocmVzX2xmYywgImxmY19uaWNoZV9sZWciLCAic3ZhbHVlX25pY2hlX2xlZyIsICJuaWNoZV9sZWciLCBzdmFsdWVfdGhyZXNoKQpwbG90X3ZvbGNhbm8ocmVzX2xmYywgImxmY19hZ2VfbGVnIiwgInN2YWx1ZV9hZ2VfbGVnIiwgImFnZV9sZWciLCBzdmFsdWVfdGhyZXNoKQpwbG90X3ZvbGNhbm8ocmVzX2xmYywgImxmY19lbmdyYWZfbGVnIiwgInN2YWx1ZV9lbmdyYWZfbGVnIiwgImVuZ3JhZnRtZW50X2xlZyIsIHN2YWx1ZV90aHJlc2gpCmBgYAoKCgpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KcGxvdF92b2xjYW5vKHJlc19sZmMsICJsZmNfbmljaGVfdHJ1bmsiLCAic3ZhbHVlX25pY2hlX3RydW5rIiwgIm5pY2hlX3RydW5rIiwgc3ZhbHVlX3RocmVzaCkKcGxvdF92b2xjYW5vKHJlc19sZmMsICJsZmNfYWdlX3RydW5rIiwgInN2YWx1ZV9hZ2VfdHJ1bmsiLCAiYWdlX3RydW5rIiwgc3ZhbHVlX3RocmVzaCkKcGxvdF92b2xjYW5vKHJlc19sZmMsICJsZmNfZW5ncmFmX3RydW5rIiwgInN2YWx1ZV9lbmdyYWZfdHJ1bmsiLCAiZW5ncmFmdG1lbnRfdHJ1bmsiLCBzdmFsdWVfdGhyZXNoKQpgYGAKCgoKYGBge3J9CnJlc19sZmNfaGl0cyA8LQogIHJlc19sZmMgJT4lCiAgZHBseXI6OmZpbHRlcihFQU5fbGVnICVpbiUgYygiMDAxIiwgIjAxMSIsICIxMDEiLCAiMTExIiwgIjEwMCIsICIwMTAiLCAiMTEwIikgfAogICAgRUFOX3RydW5rICVpbiUgYygiMDAxIiwgIjAxMSIsICIxMDEiLCAiMTExIiwgIjEwMCIsICIwMTAiLCAiMTEwIikpICU+JQogIGFycmFuZ2UoRUFOX2xlZywgRUFOX3RydW5rKQpyZXNfbGZjX2hpdHMKYGBgCgoKYGBge3J9CndyaXRlX3RzdihyZXNfbGZjX2hpdHMsICJsZWdfdHJ1bmtfbW9kZXJhdGVkX2xmY18xX3N2YWx1ZV8wNV9tYXJrZWRfaGl0cy50eHQiKQpgYGAKCgoKIyMgVlNUCgpgYGB7cn0KdnNkX2xlZyA8LQogIHZzdChkZHNfbGVnLAogICAgYmxpbmQgPSBUCiAgKQpgYGAKCklzIHRoZXJlIGEgZGVwZW5kZW5jZSBvZiB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIChvciB2YXJpYW5jZSkgb24gdGhlIG1lYW4gPyBJZiB0aGVyZSBpcyBubyB2YXJpYW5jZS1tZWFuIGRlcGVuZGVuY2UsIHRoZW4gdGhlIGxpbmUgc2hvdWxkIGJlIGFwcHJveGltYXRlbHkgaG9yaXpvbnRhbAoKYGBge3IgZmlnLndpZHRoPTEyfQptZWFuU2RQbG90KGFzc2F5KHZzZF9sZWcpLCByYW5rcyA9IEYpCmBgYAoKCmBgYHtyfQp2c2RfdHJ1bmsgPC0KICB2c3QoZGRzX3RydW5rLAogICAgYmxpbmQgPSBUCiAgKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMn0KbWVhblNkUGxvdChhc3NheSh2c2RfdHJ1bmspLCByYW5rcyA9IEYpCmBgYAoKIyMgUENBCgpgYGB7ciBmaWcud2lkdGg9MTJ9CnBsb3RfcGNhKHZzZF9sZWcsICJsZWciKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9MTJ9CnBsb3RfcGNhKHZzZF90cnVuaywgInRydW5rIikKYGBgCgoKCgojIyMgSGl0cyBieSBncm91cAoKYGBge3J9CnRhcmdldF9nZW5lc19sZWdfMDAxIDwtCiAgcmVzX2xmYyAlPiUKICBkcGx5cjo6ZmlsdGVyKEVBTl9sZWcgJWluJSBjKCIwMDEiKSkgJT4lCiAgYXJyYW5nZShzdmFsdWVfbmljaGVfbGVnKQp0YXJnZXRfZ2VuZXNfbGVnXzAwMQpgYGAKCgoKCmBgYHtyfQp0YXJnZXRfZ2VuZXNfbGVnXzAxMSA8LQogICByZXNfbGZjJT4lCiAgZHBseXI6OmZpbHRlcihFQU5fbGVnICVpbiUgYygiMDExIikpICU+JQogIGFycmFuZ2UoLWJhc2VfbWVhbl9sZWcpCnRhcmdldF9nZW5lc19sZWdfMDExCmBgYAoKYGBge3J9CnRhcmdldF9nZW5lc19sZWdfMTAxIDwtCiAgIHJlc19sZmMgJT4lCiAgZHBseXI6OmZpbHRlcihFQU5fbGVnICVpbiUgYygiMTAxIikpCnRhcmdldF9nZW5lc19sZWdfMTAxCmBgYAoKYGBge3J9CnRhcmdldF9nZW5lc19sZWdfMTExIDwtCiAgIHJlc19sZmMgJT4lCiAgZHBseXI6OmZpbHRlcihFQU5fbGVnICVpbiUgYygiMTExIikpCnRhcmdldF9nZW5lc19sZWdfMTExCmBgYAoKYGBge3J9CnRhcmdldF9nZW5lc19sZWdfMTAwIDwtCiAgIHJlc19sZmMgJT4lCiAgZHBseXI6OmZpbHRlcihFQU5fbGVnICVpbiUgYygiMTAwIikpCnRhcmdldF9nZW5lc19sZWdfMTAwCmBgYAoKYGBge3J9CnRhcmdldF9nZW5lc19sZWdfMDEwIDwtCiAgIHJlc19sZmMgJT4lCiAgZHBseXI6OmZpbHRlcihFQU5fbGVnICVpbiUgYygiMDEwIikpCnRhcmdldF9nZW5lc19sZWdfMDEwCmBgYAoKYGBge3J9CnRhcmdldF9nZW5lc19sZWdfMTEwIDwtCiAgcmVzX2xmYyAlPiUKICBkcGx5cjo6ZmlsdGVyKEVBTl9sZWcgJWluJSBjKCIxMTAiKSkKdGFyZ2V0X2dlbmVzX2xlZ18xMTAKYGBgCgoKYGBge3J9CnRhcmdldF9nZW5lc190cnVua18wMDEgPC0KICAgcmVzX2xmYyAlPiUKICBkcGx5cjo6ZmlsdGVyKEVBTl90cnVuayVpbiUgYygiMDAxIikpCnRhcmdldF9nZW5lc190cnVua18wMDEKYGBgCgoKYGBge3J9CnRhcmdldF9nZW5lc190cnVua18wMTEgPC0KICAgcmVzX2xmYyAlPiUKICBkcGx5cjo6ZmlsdGVyKEVBTl90cnVuayVpbiUgYygiMDExIikpCnRhcmdldF9nZW5lc190cnVua18wMTEKYGBgCgpgYGB7cn0KdGFyZ2V0X2dlbmVzX3RydW5rXzEwMSA8LQogICByZXNfbGZjICU+JQogIGRwbHlyOjpmaWx0ZXIoRUFOX3RydW5rJWluJSBjKCIxMDEiKSkKdGFyZ2V0X2dlbmVzX3RydW5rXzEwMQpgYGAKCmBgYHtyfQp0YXJnZXRfZ2VuZXNfdHJ1bmtfMTExIDwtCiAgIHJlc19sZmMgJT4lCiAgZHBseXI6OmZpbHRlcihFQU5fdHJ1bmslaW4lIGMoIjExMSIpKQp0YXJnZXRfZ2VuZXNfdHJ1bmtfMTExCmBgYAoKYGBge3J9CnRhcmdldF9nZW5lc190cnVua18xMDAgPC0KICByZXNfbGZjICU+JQogIGRwbHlyOjpmaWx0ZXIoRUFOX3RydW5rJWluJSBjKCIxMDAiKSkKdGFyZ2V0X2dlbmVzX3RydW5rXzEwMApgYGAKCmBgYHtyfQp0YXJnZXRfZ2VuZXNfdHJ1bmtfMDEwIDwtCiAgcmVzX2xmYyAlPiUKICBkcGx5cjo6ZmlsdGVyKEVBTl90cnVuayVpbiUgYygiMDEwIikpCnRhcmdldF9nZW5lc190cnVua18wMTAKYGBgCgpgYGB7cn0KdGFyZ2V0X2dlbmVzX3RydW5rXzExMCA8LQogIHJlc19sZmMgJT4lCiAgZHBseXI6OmZpbHRlcihFQU5fdHJ1bmslaW4lIGMoIjExMCIpKQp0YXJnZXRfZ2VuZXNfdHJ1bmtfMTEwCmBgYAoKCgoKIyMgSGVhdG1hcHMKCmBgYHtyfQptZXRhX2RmX2xlZyA8LSAKICBhcy5kYXRhLmZyYW1lKGNvbERhdGEoZGRzX2xlZylbLCBjKCJhZ2UiLCAidGltZSIpXSkKCmR0X3Bsb3RfbGVnIDwtIAogIHNjYWxlZF90cG1fbGVnICU+JQogIG11dGF0ZV9pZihpcy5udW1lcmljLH4gbG9nMiggLisxKSkKYGBgCgoKYGBge3IgZmlnLmhlaWdodD0xOCwgZmlnLndpZHRoPTEwfQpwbG90X2hlYXRtYXAodGFyZ2V0X2dlbmVzX2xlZ18wMDEsIHRpdGxlID0gImxvZzJfc2NhbGVkX3RwbV9sZWdfMDAxIiwgbWV0YV9kZl9sZWcsIGR0X3Bsb3RfbGVnKQpwbG90X2hlYXRtYXAodGFyZ2V0X2dlbmVzX2xlZ18wMTEsIHRpdGxlID0gImxvZzJfc2NhbGVkX3RwbV9sZWdfMDExIiwgbWV0YV9kZl9sZWcsIGR0X3Bsb3RfbGVnLCBzaG93X2dlbmVzID0gVCkKcGxvdF9oZWF0bWFwKHRhcmdldF9nZW5lc19sZWdfMTAxLCB0aXRsZSA9ICJsb2cyX3NjYWxlZF90cG1fbGVnXzEwMSIsIG1ldGFfZGZfbGVnLCBkdF9wbG90X2xlZywgc2hvd19nZW5lcyA9IFQpCnBsb3RfaGVhdG1hcCh0YXJnZXRfZ2VuZXNfbGVnXzExMSwgdGl0bGUgPSAibG9nMl9zY2FsZWRfdHBtX2xlZ18xMTEiLCBtZXRhX2RmX2xlZywgZHRfcGxvdF9sZWcsIHNob3dfZ2VuZXMgPSBUKQpwbG90X2hlYXRtYXAodGFyZ2V0X2dlbmVzX2xlZ18xMDAsIHRpdGxlID0gImxvZzJfc2NhbGVkX3RwbV9sZWdfMTAwIiwgbWV0YV9kZl9sZWcsIGR0X3Bsb3RfbGVnKQpwbG90X2hlYXRtYXAodGFyZ2V0X2dlbmVzX2xlZ18wMTAsIHRpdGxlID0gImxvZzJfc2NhbGVkX3RwbV9sZWdfMDEwIiwgbWV0YV9kZl9sZWcsIGR0X3Bsb3RfbGVnKQpwbG90X2hlYXRtYXAodGFyZ2V0X2dlbmVzX2xlZ18xMTAsIHRpdGxlID0gImxvZzJfc2NhbGVkX3RwbV9sZWdfMTEwIiwgbWV0YV9kZl9sZWcsIGR0X3Bsb3RfbGVnLCBzaG93X2dlbmVzID0gVCkKYGBgCgoKCmBgYHtyfQptZXRhX2RmX3RydW5rIDwtIAogIGFzLmRhdGEuZnJhbWUoY29sRGF0YShkZHNfdHJ1bmspWywgYygiYWdlIiwgInRpbWUiKV0pCgpkdF9wbG90X3RydW5rIDwtIAogIHNjYWxlZF90cG1fdHJ1bmsgJT4lCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsfiBsb2cyKCAuKzEpKQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTE4LCBmaWcud2lkdGg9MTB9CnBsb3RfaGVhdG1hcCh0YXJnZXRfZ2VuZXNfdHJ1bmtfMDAxLCB0aXRsZSA9ICJsb2cyX3NjYWxlZF90cG1fdHJ1bmtfMDAxIiwgbWV0YV9kZl90cnVuaywgZHRfcGxvdF90cnVuaywgc2hvd19nZW5lcyA9IFQpCnBsb3RfaGVhdG1hcCh0YXJnZXRfZ2VuZXNfdHJ1bmtfMDExLCB0aXRsZSA9ICJsb2cyX3NjYWxlZF90cG1fdHJ1bmtfMDExIiwgbWV0YV9kZl90cnVuaywgZHRfcGxvdF90cnVuaywgc2hvd19nZW5lcyA9IFQpCnBsb3RfaGVhdG1hcCh0YXJnZXRfZ2VuZXNfdHJ1bmtfMTAxLCB0aXRsZSA9ICJsb2cyX3NjYWxlZF90cG1fdHJ1bmtfMTAxIiwgbWV0YV9kZl90cnVuaywgZHRfcGxvdF90cnVuaywgc2hvd19nZW5lcyA9IFQpCnBsb3RfaGVhdG1hcCh0YXJnZXRfZ2VuZXNfdHJ1bmtfMTExLCB0aXRsZSA9ICJsb2cyX3NjYWxlZF90cG1fdHJ1bmtfMTExIiwgbWV0YV9kZl90cnVuaywgZHRfcGxvdF90cnVuaywgc2hvd19nZW5lcyA9IFQpCnBsb3RfaGVhdG1hcCh0YXJnZXRfZ2VuZXNfdHJ1bmtfMTAwLCB0aXRsZSA9ICJsb2cyX3NjYWxlZF90cG1fdHJ1bmtfMTAwIiwgbWV0YV9kZl90cnVuaywgZHRfcGxvdF90cnVuaywgc2hvd19nZW5lcyA9IFQpCnBsb3RfaGVhdG1hcCh0YXJnZXRfZ2VuZXNfdHJ1bmtfMDEwLCB0aXRsZSA9ICJsb2cyX3NjYWxlZF90cG1fdHJ1bmtfMDEwIiwgbWV0YV9kZl90cnVuaywgZHRfcGxvdF90cnVuaykKcGxvdF9oZWF0bWFwKHRhcmdldF9nZW5lc190cnVua18xMTAsIHRpdGxlID0gImxvZzJfc2NhbGVkX3RwbV90cnVua18xMTAiLCBtZXRhX2RmX3RydW5rLCBkdF9wbG90X3RydW5rLCBzaG93X2dlbmVzID0gVCkKYGBgCgoKCgoKCmBgYHtyfQp2c2RfZHQgPC0KICBmdWxsX2pvaW4oCiAgICBhc3NheSh2c2RfbGVnKSAlPiUKICAgICAgYXNfdGliYmxlKHJvd25hbWVzID0gImdlbmVfaWQiKSwKICAgIGFzc2F5KHZzZF90cnVuaykgJT4lCiAgICAgIGFzX3RpYmJsZShyb3duYW1lcyA9ICJnZW5lX2lkIiksCiAgICBieT0iZ2VuZV9pZCIKICApCgp2c2RfZHRfbWVsdGVkIDwtCiAgdnNkX2R0ICU+JQogICMgZmlsdGVyKGdlbmVfaWQgJWluJSByb3dEYXRhKHNjZTEweCkkZ2VuZV9pZCkgICU+JQogIGdhdGhlcihzYW1wbGUsIHZzZCwgLWdlbmVfaWQpCgp2c2RfZHRfbWVsdGVkIDwtCiAgbGVmdF9qb2luKHZzZF9kdF9tZWx0ZWQsCiAgICBtZXRhX2R0ICAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KC1uYW1lLCAtdHlwZSwgLWJhdGNoKSwKICAgIGJ5ID0gInNhbXBsZSIKICApCnZzZF9kdF9tZWx0ZWQKYGBgCgoKCiMjIE1lbHQgVFBNIGRhdGFmcmFtZXMKCgpgYGB7cn0Kc2NhbGVkX3RwbV9tZWx0ZWQgPC0KICBzY2FsZWRfdHBtICU+JQogIGdhdGhlcihzYW1wbGUsIHRwbSwgLWdlbmVfaWQpCgpzY2FsZWRfdHBtX21lbHRlZCA8LSAKICBsZWZ0X2pvaW4oc2NhbGVkX3RwbV9tZWx0ZWQsCiAgbWV0YV9kdCAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KC1uYW1lLCAtdHlwZSwgLWJhdGNoKSwKICBieSA9ICJzYW1wbGUiCikKc2NhbGVkX3RwbV9tZWx0ZWQgPC0KICBsZWZ0X2pvaW4oc2NhbGVkX3RwbV9tZWx0ZWQsCiAgICBnZW5lX21ldGFkYXRhICU+JQogICAgICBkcGx5cjo6c2VsZWN0KGdlbmVfbmFtZSwgZ2VuZV9pZCksCiAgICBieSA9ICJnZW5lX2lkIgogICkgJT4lCiAgbXV0YXRlKGxvZzJfc2NhbGVkX3RwbSA9IGxvZzIodHBtKzEpKQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTE4LCBmaWcud2lkdGg9MTB9CnBsb3RfZHQgPC0KICBzY2FsZWRfdHBtICU+JQogIGRwbHlyOjpzZWxlY3QoLWdlbmVfaWQpICU+JQogIG11dGF0ZV9pZihpcy5udW1lcmljLCB+IGxvZzIoLisxKSkKCmRmIDwtCiAgbWV0YV9kdFssIGMoInNhbXBsZSIsICJ0aXNzdWUiLCAiYWdlIiwgInRpbWUiKV0gJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJzYW1wbGUiKQoKc2VsZWN0IDwtIG9yZGVyKHJvd01lYW5zKHBsb3RfZHQgJT4lIGFzLm1hdHJpeCgpKSwKICBkZWNyZWFzaW5nID0gVFJVRQopWzE6NTAwMF0KZ19oMiA8LQogIHBoZWF0bWFwKHBsb3RfZHRbc2VsZWN0LCBdLAogICAgY2x1c3Rlcl9yb3dzID0gVCwKICAgIHNob3dfcm93bmFtZXMgPSBGQUxTRSwKICAgIGNsdXN0ZXJfY29scyA9IEZBTFNFLAogICAgYW5ub3RhdGlvbl9jb2wgPSBkZgogICkKZ19oMgpnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCAiaGVhdG1hcF9idWxrX3JuYXNlcV9sb2cyX3NjYWxlZF90cG1fNTAwMGdlbmVzLnBkZiIpLCBnX2gyKQpgYGAKCgoKCgojIyMgUGxvdCBpbmRpdmlkdWFsIGdlbmVzCgoKYGBge3IgZmlnLmhlaWdodD05LCBmaWcud2lkdGg9MTF9CnBsb3RfZ2VuZSgiRU5TTVVTRzAwMDAwMDI2MDQzIiwgc2NhbGVkX3RwbV9tZWx0ZWQpCmBgYAoKCgoKCmBgYHtyfQoKYGBgCgoKCgpgYGB7ciBmaWcud2lkdGg9MTJ9CnAxPC0gIGdncGxvdChyZXNfbGZjLAogICAgYWVzKHggPSBsZmNfYWdlX2xlZywgeSA9IGxmY19uaWNoZV9sZWcpCiAgKSAgKwogIGdlb21fcG9pbnQoc2l6ZSA9IC43LCBhbHBoYT0wLjMpIApnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCAibmljaGVfdnNfYWdlX2xmY19sZWcucGRmIiksIHAxKQpwMQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMn0KcDI8LSAgZ2dwbG90KHJlc19sZmMsCiAgICBhZXMoeCA9IGxmY19hZ2VfbGVnLCB5ID0gbGZjX2VuZ3JhZl9sZWcpCiAgKSArCiAgZ2VvbV9wb2ludChzaXplID0gLjcsIGFscGhhPTAuMykgCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJlbmdyYWZ0bWVudF92c19hZ2VfbGZjX2xlZy5wZGYiKSwgcDIpCnAyCmBgYAoKCmBgYHtyIGZpZy53aWR0aD0xMn0KcDM8LSAgZ2dwbG90KHJlc19sZmMsCiAgICBhZXMoeCA9IGxmY19uaWNoZV9sZWcsIHkgPSBsZmNfZW5ncmFmX2xlZykKICApICsKICBnZW9tX3BvaW50KHNpemUgPSAuNywgYWxwaGE9MC4zKSAKCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJlbmdyYWZ0bWVudF92c19uaWNoZV9sZmNfbGVnLnBkZiIpLCBwMykKcDMKYGBgCgoKCmBgYHtyIGZpZy53aWR0aD0xMn0KcDQ8LSAgZ2dwbG90KHJlc19sZmMsCiAgICBhZXMoeCA9IGxmY19hZ2VfdHJ1bmssIHkgPSBsZmNfbmljaGVfdHJ1bmspCiAgKSAgKwogIGdlb21fcG9pbnQoc2l6ZSA9IC43LCBhbHBoYT0wLjMpIApnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCAibmljaGVfdnNfYWdlX2xmY190cnVuay5wZGYiKSwgcDQpCnA0CmBgYAoKYGBge3IgZmlnLndpZHRoPTEyfQpwNTwtICBnZ3Bsb3QocmVzX2xmYywKICAgIGFlcyh4ID0gbGZjX2FnZV90cnVuaywgeSA9IGxmY19lbmdyYWZfdHJ1bmspCiAgKSArCiAgZ2VvbV9wb2ludChzaXplID0gLjcsIGFscGhhPTAuMykgCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJlbmdyYWZ0bWVudF92c19hZ2VfbGZjX3RydW5rLnBkZiIpLCBwNSkKcDUKYGBgCgoKYGBge3IgZmlnLndpZHRoPTEyfQpwNjwtICBnZ3Bsb3QocmVzX2xmYywKICAgIGFlcyh4ID0gbGZjX25pY2hlX3RydW5rLCB5ID0gbGZjX2VuZ3JhZl90cnVuaykKICApICsKICBnZW9tX3BvaW50KHNpemUgPSAuNywgYWxwaGE9MC4zKSAKCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJlbmdyYWZ0bWVudF92c19uaWNoZV9sZmNfdHJ1bmsucGRmIiksIHA2KQpwNgpgYGAKCiMjIyBTYXZlIGFsbCB0YXJnZXQgZ2VuZXMKCmBgYHtyIGZpZy5oZWlnaHQ9OSwgZmlnLndpZHRoPTExfQpwbG90cyA8LSBtYXAocmVzX2xmY19oaXRzJGdlbmVfaWQsIHBsb3RfZ2VuZSwgZGF0YSA9IHNjYWxlZF90cG1fbWVsdGVkKQpgYGAKCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAK