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(DESeq2)
library(ashr)
library(EnhancedVolcano)
library(BiocParallel)
library(zinbwave)
theme_set(theme_bw())
})
Define file paths
data_dir <- "./data"
figures_dir <- file.path("./figures")
get_counts_stats <- function(sce10x_macrophage, cluster_to_remove) {
gene_counts_by_cluster <-
t(assay(sce10x_macrophage)) %>%
as.matrix() %>%
as_tibble() %>%
group_by(cluster = colData(sce10x_macrophage)$clusters_condition) %>%
summarise_all(list(~ mean(.)))
umi_counts_max <-
gene_counts_by_cluster[, -1] %>%
summarize_all(list(~ max(.))) %>%
mutate(dummy = "dummy") %>%
pivot_longer(-dummy, names_to = "gene_name", values_to = "max_expr") %>%
select(-dummy)
stats <-
perFeatureQCMetrics(sce10x_macrophage,
exprs_values = c("counts"),
flatten = TRUE
) %>%
as_tibble() %>%
bind_cols(., rowData(sce10x_macrophage) %>%
as_tibble()) %>%
bind_cols(., umi_counts_max)
return(stats)
}
get_lfc <- function(contrast, name, dds) {
lfcShrink(dds,
contrast = contrast,
type = "ashr",
svalue = T
) %>%
as_tibble() %>%
dplyr::select(-baseMean) %>%
mutate(svalue = abs(svalue)) %>%
set_names(paste(c("lfc", "lfc_se", "svalue"), name, sep = "_"))
}
plot_expression <- function(gene) {
plotExpression(sce10x_macrophage,
features = gene,
x = "sample",
exprs_values = "logcounts",
colour_by = "condition",
point_size = 1
) +
facet_wrap(~ colData(sce10x_macrophage)$clusters, ncol = 1)
}
plot_volcano <- function(var_name, lfc_thresh, svalue_thresh, lfc, suffix, label = NULL) {
p <- EnhancedVolcano(lfc,
lab = lfc %>% pull(gene_id),
x = paste0("lfc_", var_name),
selectLab = label,
y = paste0("svalue_", var_name),
xlab = bquote(~ italic(Moderated) ~ Log[2] ~ FC),
ylab = bquote(~ -Log[10] ~ italic(svalue)),
col = c("grey30", "forestgreen", "red2", "royalblue"),
pCutoff = svalue_thresh,
FCcutoff = lfc_thresh,
legendLabels = c(
"NS",
expression(Log[2] ~ FC),
"s-value",
expression(s - value ~ and ~ log[2] ~ FC)
)
)
ggsave(file.path(figures_dir, paste0("volcano_sc", var_name, "_effect_", suffix, ".pdf")), p)
return(p)
}
Load data
sce10x <-
readRDS(file.path(
data_dir,
"preprocessed",
"sce10x_filtered_final.rds"
))
sce10x_macrophage <- sce10x[, colData(sce10x)$celltype == "macrophage"]
rm(sce10x)
table(colData(sce10x_macrophage)$clusters, colData(sce10x_macrophage)$sample)
yng1 yng2 yng3 aged1 aged2 aged3 aged4
macrophage 282 274 398 58 245 149 189
Discard low counts genes
n_exprs_genes <-
nexprs(sce10x_macrophage,
detection_limit = 5,
byrow = TRUE
)
keep <- n_exprs_genes >= 10
table(keep)
keep
FALSE TRUE
30428 3046
sce10x_macrophage <- sce10x_macrophage[keep, ]
batch_genes_remove <-
c(
"Ccr7", "Cd209c", "Ifit1", "Slc40a1",
"Fgf13", "Igfbp5", "Gpnmb", "Upp1", "Irgm2",
"S100a8", "Chil3", "Saa3", "S100a9", "Apol7c", "F5", "Upp1"
)
map(batch_genes_remove, plot_expression)
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]
[[9]]
[[10]]
[[11]]
[[12]]
[[13]]
[[14]]
[[15]]
[[16]]
sce10x_macrophage <- sce10x_macrophage[!(rownames(sce10x_macrophage) %in% batch_genes_remove), ]
Discard low cluster mean expression genes
stats <- get_counts_stats(sce10x_macrophage)
stats
max_thresh <- .3
stats %>%
count(max_expr > max_thresh)
ggplot(stats) +
geom_histogram(aes(max_expr), bins = 100) +
scale_x_log10() +
facet_wrap(~top_hvg_macrophage) +
geom_vline(xintercept = max_thresh)
genes_to_plot <-
stats %>%
filter(max_expr <= max_thresh) %>%
arrange(-max_expr) %>%
pull(gene_name)
map(genes_to_plot[1:5], plot_expression)
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
genes_keep <-
stats %>%
filter(max_expr > max_thresh) %>%
pull(gene_name)
length(genes_keep)
[1] 2977
sce10x_macrophage <- sce10x_macrophage[genes_keep, ]
ggplot(
stats,
aes(
mean,
detected
)
) +
scale_x_log10() +
geom_point(size = 0.3, aes(color = max_expr < 1)) +
geom_text(aes(
label = gene_name
),
check_overlap = TRUE, nudge_y = -0.1, size = 2.5
)
Create Design Matrix
design_macrophage <-
model.matrix(~ -1 + condition + sample,
data = colData(sce10x_macrophage)
)[, -c(5)]
colnames(design_macrophage) <- str_replace(colnames(design_macrophage), "condition|sample", "")
design_macrophage[1:3, ]
yng aged yng2 yng3 aged2 aged3 aged4
ATGGTTGGTTGTAAAG_d1 0 1 0 0 0 0 0
GATGAGGGTTCAGTAC_d1 0 1 0 0 0 0 0
GCAACCGTCACCTTGC_d1 0 1 0 0 0 0 0
Compute Observational Weights
assay(sce10x_macrophage, "counts") <- round(assay(sce10x_macrophage, "counts"))
system.time({
zinb <-
zinbFit(sce10x_macrophage,
K = 0,
X = design_macrophage,
verbose = TRUE,
BPPARAM = MulticoreParam(3),
epsilon = 1e12
)
})
Create model:
ok
Initialize parameters:
ok
Optimize parameters:
Iteration 1
penalized log-likelihood = -10786111.760099
After dispersion optimization = -23318712.4269814
user system elapsed
45.7 2.2 24.6
After right optimization = -23318260.1799767
After orthogonalization = -23318260.1799767
user system elapsed
30.8 1.3 17.4
After left optimization = -20653340.6726067
After orthogonalization = -20653340.6726067
Iteration 2
penalized log-likelihood = -20653340.6726067
After dispersion optimization = -20653340.6757374
user system elapsed
32.3 1.1 33.8
After right optimization = -20653321.6706135
After orthogonalization = -20653321.6706135
user system elapsed
6.78 0.74 3.76
After left optimization = -20653321.6429825
After orthogonalization = -20653321.6429825
Iteration 3
penalized log-likelihood = -20653321.6429825
ok
user system elapsed
333 22 158
weights <- computeObservationalWeights(zinb, as.matrix(assay(sce10x_macrophage)))
dimnames(weights) <- dimnames(sce10x_macrophage)
assay(sce10x_macrophage, "weights") <- weights
convert to SCE to DESeqDataSet object
dds_macrophage <-
convertTo(sce10x_macrophage, type = c("DESeq2"))
converting counts to integer mode
design(dds_macrophage) <- design_macrophage
assay(dds_macrophage, "weights") <- assay(sce10x_macrophage, "weights")
dds_macrophage <- estimateSizeFactors(dds_macrophage, type = "poscounts")
dds_macrophage <-
DESeq(dds_macrophage,
test = "LRT",
useT = TRUE,
reduced = design_macrophage[, 1:2],
minmu = 1e-6,
parallel = TRUE,
BPPARAM = MulticoreParam(3),
minRep = Inf
)
using supplied model matrix
using pre-existing size factors
estimating dispersions
gene-wise dispersion estimates: 3 workers
mean-dispersion relationship
-- note: fitType='parametric', but the dispersion trend was not well captured by the
function: y = a/x + b, and a local regression fit was automatically substituted.
specify fitType='local' or 'mean' to avoid this message next time.
final dispersion estimates, fitting model and testing: 3 workers
plotDispEsts(dds_macrophage)
resultsNames(dds_macrophage)
[1] "yng" "aged" "yng2" "yng3" "aged2" "aged3" "aged4"
Plot batch effects
batch_dt <-
rowData(dds_macrophage) %>%
as_tibble(rownames = "gene_id") %>%
select("gene_id", "yng2":"aged4")
ggplot(
batch_dt,
aes(
x = yng2,
y = yng3
)
) +
geom_point(
size = .2,
alpha = 0.3
) +
geom_text(aes(
label = gene_id
),
check_overlap = TRUE, nudge_y = -0.15, size = 3
)
ggplot(
batch_dt,
aes(
x = aged2,
y = aged3
)
) +
geom_point(
size = .2,
alpha = 0.3
) +
geom_text(aes(
label = gene_id
),
check_overlap = TRUE, nudge_y = -0.15, size = 3
)
ggplot(
batch_dt,
aes(
x = aged2,
y = aged4
)
) +
geom_point(
size = .2,
alpha = 0.3
) +
geom_text(aes(
label = gene_id
),
check_overlap = TRUE, nudge_y = -0.15, size = 3
)
Test contrasts
c1 <- c(1, -1, 0, 0, 0, 0, 0)
macrophage_contrast_vec <- list(
macrophage_aging = c1
)
lfc_macrophage <-
imap_dfc(macrophage_contrast_vec,
get_lfc,
dds = dds_macrophage
) %>%
bind_cols(
gene_id = rownames(dds_macrophage),
.
)
using 'ashr' for LFC shrinkage. If used in published research, please cite:
Stephens, M. (2016) False discovery rates: a new deal. Biostatistics, 18:2.
https://doi.org/10.1093/biostatistics/kxw041
lfc_macrophage <-
left_join(lfc_macrophage,
rowData(dds_macrophage) %>%
as_tibble(rownames = "gene_id"),
by = "gene_id"
)
lfc_macrophage
Save data
write_tsv(
lfc_macrophage,
file.path(
data_dir,
"preprocessed",
"macrophage_scrnaseq_moderated_lfc.txt"
)
)
rowData(sce10x_macrophage) <-
left_join(rowData(sce10x_macrophage) %>%
as_tibble(rownames = "gene_id") %>%
select(gene_id), lfc_macrophage, by = "gene_id") %>%
DataFrame(.)
rownames(rowData(sce10x_macrophage)) <- rowData(sce10x_macrophage)$gene_id
saveRDS(
sce10x_macrophage,
file.path(
data_dir,
"preprocessed",
"sce10x_macrophage_filtered_final.rds"
)
)
Make volcano plots
lfc_thresh <- 1
svalue_thresh <- 10e-8
volcano_plots <-
map(names(macrophage_contrast_vec),
plot_volcano,
lfc_thresh = lfc_thresh,
svalue_thresh = svalue_thresh,
lfc = lfc_macrophage,
suffix = "macrophage_labeled"
)
One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...Saving 7 x 7 in image
volcano_plots
[[1]]
lfc_thresh <- 1
svalue_thresh <- 10e-8
volcano_plots_unlabeled <-
map(names(macrophage_contrast_vec),
plot_volcano,
lfc_thresh = lfc_thresh,
svalue_thresh = svalue_thresh,
lfc = lfc_macrophage,
suffix = "macrophage_unlabeled",
label = c("")
)
One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...Saving 7 x 7 in image
volcano_plots_unlabeled
[[1]]
genes_hits_aging_up <-
lfc_macrophage %>%
filter(lfc_macrophage_aging > 1) %>%
arrange(-lfc_macrophage_aging) %>%
pull(gene_id)
genes_hits_aging_up
[1] "mt-Nd3" "Zfp36" "Ifi27l2a" "Rnase4" "Folr2" "Apoe"
[7] "F13a1" "mt-Co2" "Cst3" "Cd83" "ENSMUSG00000034708.11" "Rps23"
map(genes_hits_aging_up, plot_expression)
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]
[[9]]
[[10]]
[[11]]
[[12]]
genes_hits_aging_down <-
lfc_macrophage %>%
filter(lfc_macrophage_aging < -1) %>%
arrange(lfc_macrophage_aging) %>%
pull(gene_id)
genes_hits_aging_down
[1] "Plcb1" "Lyz1" "Gsr" "Tmsb10"
[5] "Vcan" "Msrb1" "Thbs1" "Prdx5"
[9] "Smpdl3a" "Grk5" "Slc41a2" "Arhgap26_ENSMUSG00000036452.18"
[13] "Adgre4" "Rbm3" "Ern1" "Samhd1"
[17] "Cytip" "Emilin2" "Atp11b" "Sipa1l1"
[21] "Cers6" "Pbx1" "Abtb2" "Gda"
[25] "Jarid2" "Nrg1" "Runx2"
map(genes_hits_aging_down, plot_expression)
[[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]]
sessionInfo()
R version 4.0.2 (2020-06-22)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.5 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] zinbwave_1.10.0 BiocParallel_1.22.0 EnhancedVolcano_1.6.0 ggrepel_0.8.2 ashr_2.2-47
[6] DESeq2_1.28.1 forcats_0.5.0 stringr_1.4.0 dplyr_1.0.2 purrr_0.3.4
[11] readr_1.3.1 tidyr_1.1.2 tibble_3.0.3 tidyverse_1.3.0 scran_1.16.0
[16] scater_1.16.2 ggplot2_3.3.2 SingleCellExperiment_1.10.1 SummarizedExperiment_1.18.2 DelayedArray_0.14.1
[21] matrixStats_0.56.0 Biobase_2.48.0 GenomicRanges_1.40.0 GenomeInfoDb_1.24.2 IRanges_2.22.2
[26] S4Vectors_0.26.1 BiocGenerics_0.34.0
loaded via a namespace (and not attached):
[1] ggbeeswarm_0.6.0 colorspace_1.4-1 ellipsis_0.3.1 rprojroot_1.3-2 XVector_0.28.0
[6] base64enc_0.1-3 BiocNeighbors_1.6.0 fs_1.5.0 rstudioapi_0.11 farver_2.0.3
[11] bit64_4.0.5 AnnotationDbi_1.50.3 fansi_0.4.1 lubridate_1.7.9 xml2_1.3.2
[16] splines_4.0.2 R.methodsS3_1.8.1 geneplotter_1.66.0 knitr_1.29 jsonlite_1.7.1
[21] packrat_0.5.0 broom_0.7.0 annotate_1.66.0 dbplyr_1.4.4 R.oo_1.24.0
[26] compiler_4.0.2 httr_1.4.2 dqrng_0.2.1 backports_1.1.9 assertthat_0.2.1
[31] Matrix_1.2-18 limma_3.44.3 cli_2.0.2 BiocSingular_1.4.0 htmltools_0.5.0
[36] tools_4.0.2 rsvd_1.0.3 igraph_1.2.5 gtable_0.3.0 glue_1.4.2
[41] GenomeInfoDbData_1.2.3 Rcpp_1.0.5 softImpute_1.4 cellranger_1.1.0 styler_1.3.2
[46] vctrs_0.3.4 DelayedMatrixStats_1.10.1 xfun_0.17 rvest_0.3.6 lifecycle_0.2.0
[51] irlba_2.3.3 statmod_1.4.34 XML_3.99-0.5 edgeR_3.30.3 zlibbioc_1.34.0
[56] scales_1.1.1 hms_0.5.3 RColorBrewer_1.1-2 rematch2_2.1.2 yaml_2.2.1
[61] memoise_1.1.0 gridExtra_2.3 SQUAREM_2020.4 stringi_1.5.3 RSQLite_2.2.0
[66] genefilter_1.70.0 truncnorm_1.0-8 rlang_0.4.7 pkgconfig_2.0.3 bitops_1.0-6
[71] invgamma_1.1 evaluate_0.14 lattice_0.20-41 labeling_0.3 cowplot_1.1.0
[76] bit_4.0.4 tidyselect_1.1.0 magrittr_1.5 R6_2.4.1 generics_0.0.2
[81] DBI_1.1.0 pillar_1.4.6 haven_2.3.1 withr_2.2.0 mixsqp_0.3-43
[86] survival_3.1-12 RCurl_1.98-1.2 modelr_0.1.8 crayon_1.3.4 rmarkdown_2.3
[91] viridis_0.5.1 locfit_1.5-9.4 grid_4.0.2 readxl_1.3.1 blob_1.2.1
[96] reprex_0.3.0 digest_0.6.25 xtable_1.8-4 R.cache_0.14.0 R.utils_2.10.1
[101] munsell_0.5.0 beeswarm_0.2.3 viridisLite_0.3.0 vipor_0.4.5
LS0tCnRpdGxlOiAiTW91c2UgTXVzY2xlIFN0ZW0gQ2VsbCBQcm9qZWN0ICIKc3VidGl0bGU6ICJQYXJ0IDVjOiBydW4gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgb24gbWFjcm9waGFnZSBjZWxscyIKYXV0aG9yOiAKLSBuYW1lOiBSaWNrIEZhcm91bmkKICBhZmZpbGlhdGlvbjoKICAtICZjcnVrIEfDqW5vbWUgUXXDqWJlYyBJbm5vdmF0aW9uIENlbnRyZSwgTWNHaWxsIFVuaXZlcnNpdHksIE1vbnRyZWFsLCBDYW5hZGEKZGF0ZTogJ2ByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJVktJUItJWQiKWAnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIHRvYzogbm8KICAgIHRvY19mbG9hdDogCiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKLS0tCgoKIyBQcmVwYXJlIGFuYWx5c2lzIHdvcmtmbG93CgojIyBTZXQgZmlsZXBhdGhzIGFuZCBwYXJhbWV0ZXJzCgpgYGB7ciBzZXR1cH0Kc2V0LnNlZWQoNDIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gcnByb2pyb290OjpmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpCm9wdGlvbnMoCiAgcmVhZHIuc2hvd19wcm9ncmVzcyA9IEZBTFNFLAogIGRpZ2l0cyA9IDIKKQpgYGAKCiMjIExvYWQgcGFja2FnZXMKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShzY2F0ZXIpCiAgbGlicmFyeShzY3JhbikKICBsaWJyYXJ5KHRpZHl2ZXJzZSkKICBsaWJyYXJ5KERFU2VxMikKICBsaWJyYXJ5KGFzaHIpCiAgbGlicmFyeShFbmhhbmNlZFZvbGNhbm8pCiAgbGlicmFyeShCaW9jUGFyYWxsZWwpCiAgbGlicmFyeSh6aW5id2F2ZSkKICB0aGVtZV9zZXQodGhlbWVfYncoKSkKfSkKYGBgCgojIyBEZWZpbmUgZmlsZSBwYXRocwoKYGBge3J9CmRhdGFfZGlyIDwtICIuL2RhdGEiCmZpZ3VyZXNfZGlyIDwtIGZpbGUucGF0aCgiLi9maWd1cmVzIikKYGBgCgpgYGB7cn0KZ2V0X2NvdW50c19zdGF0cyA8LSBmdW5jdGlvbihzY2UxMHhfbWFjcm9waGFnZSwgY2x1c3Rlcl90b19yZW1vdmUpIHsKICBnZW5lX2NvdW50c19ieV9jbHVzdGVyIDwtCiAgICB0KGFzc2F5KHNjZTEweF9tYWNyb3BoYWdlKSkgJT4lCiAgICBhcy5tYXRyaXgoKSAlPiUKICAgIGFzX3RpYmJsZSgpICU+JQogICAgZ3JvdXBfYnkoY2x1c3RlciA9IGNvbERhdGEoc2NlMTB4X21hY3JvcGhhZ2UpJGNsdXN0ZXJzX2NvbmRpdGlvbikgJT4lCiAgICBzdW1tYXJpc2VfYWxsKGxpc3QofiBtZWFuKC4pKSkKCiAgdW1pX2NvdW50c19tYXggPC0KICAgIGdlbmVfY291bnRzX2J5X2NsdXN0ZXJbLCAtMV0gJT4lCiAgICBzdW1tYXJpemVfYWxsKGxpc3QofiBtYXgoLikpKSAlPiUKICAgIG11dGF0ZShkdW1teSA9ICJkdW1teSIpICU+JQogICAgcGl2b3RfbG9uZ2VyKC1kdW1teSwgbmFtZXNfdG8gPSAiZ2VuZV9uYW1lIiwgdmFsdWVzX3RvID0gIm1heF9leHByIikgJT4lCiAgICBzZWxlY3QoLWR1bW15KQoKICBzdGF0cyA8LQogICAgcGVyRmVhdHVyZVFDTWV0cmljcyhzY2UxMHhfbWFjcm9waGFnZSwKICAgICAgZXhwcnNfdmFsdWVzID0gYygiY291bnRzIiksCiAgICAgIGZsYXR0ZW4gPSBUUlVFCiAgICApICU+JQogICAgYXNfdGliYmxlKCkgJT4lCiAgICBiaW5kX2NvbHMoLiwgcm93RGF0YShzY2UxMHhfbWFjcm9waGFnZSkgJT4lCiAgICAgIGFzX3RpYmJsZSgpKSAlPiUKICAgIGJpbmRfY29scyguLCB1bWlfY291bnRzX21heCkKCiAgcmV0dXJuKHN0YXRzKQp9CgpnZXRfbGZjIDwtIGZ1bmN0aW9uKGNvbnRyYXN0LCBuYW1lLCBkZHMpIHsKICBsZmNTaHJpbmsoZGRzLAogICAgY29udHJhc3QgPSBjb250cmFzdCwKICAgIHR5cGUgPSAiYXNociIsCiAgICBzdmFsdWUgPSBUCiAgKSAlPiUKICAgIGFzX3RpYmJsZSgpICU+JQogICAgZHBseXI6OnNlbGVjdCgtYmFzZU1lYW4pICU+JQogICAgbXV0YXRlKHN2YWx1ZSA9IGFicyhzdmFsdWUpKSAlPiUKICAgIHNldF9uYW1lcyhwYXN0ZShjKCJsZmMiLCAibGZjX3NlIiwgInN2YWx1ZSIpLCBuYW1lLCBzZXAgPSAiXyIpKQp9CgpwbG90X2V4cHJlc3Npb24gPC0gZnVuY3Rpb24oZ2VuZSkgewogIHBsb3RFeHByZXNzaW9uKHNjZTEweF9tYWNyb3BoYWdlLAogICAgZmVhdHVyZXMgPSBnZW5lLAogICAgeCA9ICJzYW1wbGUiLAogICAgZXhwcnNfdmFsdWVzID0gImxvZ2NvdW50cyIsCiAgICBjb2xvdXJfYnkgPSAiY29uZGl0aW9uIiwKICAgIHBvaW50X3NpemUgPSAxCiAgKSArCiAgICBmYWNldF93cmFwKH4gY29sRGF0YShzY2UxMHhfbWFjcm9waGFnZSkkY2x1c3RlcnMsIG5jb2wgPSAxKQp9CgpwbG90X3ZvbGNhbm8gPC0gZnVuY3Rpb24odmFyX25hbWUsIGxmY190aHJlc2gsIHN2YWx1ZV90aHJlc2gsIGxmYywgc3VmZml4LCBsYWJlbCA9IE5VTEwpIHsKICBwIDwtIEVuaGFuY2VkVm9sY2FubyhsZmMsCiAgICBsYWIgPSBsZmMgJT4lIHB1bGwoZ2VuZV9pZCksCiAgICB4ID0gcGFzdGUwKCJsZmNfIiwgdmFyX25hbWUpLAogICAgc2VsZWN0TGFiID0gbGFiZWwsCiAgICB5ID0gcGFzdGUwKCJzdmFsdWVfIiwgdmFyX25hbWUpLAogICAgeGxhYiA9IGJxdW90ZSh+IGl0YWxpYyhNb2RlcmF0ZWQpIH4gTG9nWzJdIH4gRkMpLAogICAgeWxhYiA9IGJxdW90ZSh+IC1Mb2dbMTBdIH4gaXRhbGljKHN2YWx1ZSkpLAogICAgY29sID0gYygiZ3JleTMwIiwgImZvcmVzdGdyZWVuIiwgInJlZDIiLCAicm95YWxibHVlIiksCiAgICBwQ3V0b2ZmID0gc3ZhbHVlX3RocmVzaCwKICAgIEZDY3V0b2ZmID0gbGZjX3RocmVzaCwKICAgIGxlZ2VuZExhYmVscyA9IGMoCiAgICAgICJOUyIsCiAgICAgIGV4cHJlc3Npb24oTG9nWzJdIH4gRkMpLAogICAgICAicy12YWx1ZSIsCiAgICAgIGV4cHJlc3Npb24ocyAtIHZhbHVlIH4gYW5kIH4gbG9nWzJdIH4gRkMpCiAgICApCiAgKQoKICBnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCBwYXN0ZTAoInZvbGNhbm9fc2MiLCB2YXJfbmFtZSwgIl9lZmZlY3RfIiwgc3VmZml4LCAiLnBkZiIpKSwgcCkKICByZXR1cm4ocCkKfQpgYGAKCgoKIyMgTG9hZCBkYXRhCgoKYGBge3J9CnNjZTEweCA8LQogIHJlYWRSRFMoZmlsZS5wYXRoKAogICAgZGF0YV9kaXIsCiAgICAicHJlcHJvY2Vzc2VkIiwKICAgICJzY2UxMHhfZmlsdGVyZWRfZmluYWwucmRzIgogICkpCmBgYAoKCmBgYHtyfQpzY2UxMHhfbWFjcm9waGFnZSA8LSBzY2UxMHhbLCBjb2xEYXRhKHNjZTEweCkkY2VsbHR5cGUgPT0gIm1hY3JvcGhhZ2UiXQpybShzY2UxMHgpCmBgYAoKYGBge3J9CnRhYmxlKGNvbERhdGEoc2NlMTB4X21hY3JvcGhhZ2UpJGNsdXN0ZXJzLCBjb2xEYXRhKHNjZTEweF9tYWNyb3BoYWdlKSRzYW1wbGUpCmBgYAoKCgojIyBEaXNjYXJkIGxvdyBjb3VudHMgZ2VuZXMKCmBgYHtyfQpuX2V4cHJzX2dlbmVzIDwtCiAgbmV4cHJzKHNjZTEweF9tYWNyb3BoYWdlLAogICAgZGV0ZWN0aW9uX2xpbWl0ID0gNSwKICAgIGJ5cm93ID0gVFJVRQogICkKa2VlcCA8LSBuX2V4cHJzX2dlbmVzID49IDEwCnRhYmxlKGtlZXApCmBgYAoKYGBge3J9CnNjZTEweF9tYWNyb3BoYWdlIDwtIHNjZTEweF9tYWNyb3BoYWdlW2tlZXAsIF0KYGBgCgpgYGB7cn0KYmF0Y2hfZ2VuZXNfcmVtb3ZlIDwtCiAgYygKICAgICJDY3I3IiwgIkNkMjA5YyIsICJJZml0MSIsICJTbGM0MGExIiwKICAgICJGZ2YxMyIsICJJZ2ZicDUiLCAiR3BubWIiLCAiVXBwMSIsICJJcmdtMiIsCiAgICAiUzEwMGE4IiwgIkNoaWwzIiwgIlNhYTMiLCAiUzEwMGE5IiwgIkFwb2w3YyIsICJGNSIsICJVcHAxIgogICkKYGBgCgoKYGBge3J9Cm1hcChiYXRjaF9nZW5lc19yZW1vdmUsIHBsb3RfZXhwcmVzc2lvbikKYGBgCgpgYGB7cn0Kc2NlMTB4X21hY3JvcGhhZ2UgPC0gc2NlMTB4X21hY3JvcGhhZ2VbIShyb3duYW1lcyhzY2UxMHhfbWFjcm9waGFnZSkgJWluJSBiYXRjaF9nZW5lc19yZW1vdmUpLCBdCmBgYAoKIyMgIERpc2NhcmQgbG93IGNsdXN0ZXIgbWVhbiBleHByZXNzaW9uIGdlbmVzCgpgYGB7cn0Kc3RhdHMgPC0gZ2V0X2NvdW50c19zdGF0cyhzY2UxMHhfbWFjcm9waGFnZSkKc3RhdHMKYGBgCgoKCgpgYGB7cn0KbWF4X3RocmVzaCA8LSAuMwpzdGF0cyAlPiUKICBjb3VudChtYXhfZXhwciA+IG1heF90aHJlc2gpCmBgYAoKCgpgYGB7ciBmaWcud2lkdGg9N30KZ2dwbG90KHN0YXRzKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKG1heF9leHByKSwgYmlucyA9IDEwMCkgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgZmFjZXRfd3JhcCh+dG9wX2h2Z19tYWNyb3BoYWdlKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWF4X3RocmVzaCkKYGBgCgoKCmBgYHtyfQpnZW5lc190b19wbG90IDwtCiAgc3RhdHMgJT4lCiAgZmlsdGVyKG1heF9leHByIDw9IG1heF90aHJlc2gpICU+JQogIGFycmFuZ2UoLW1heF9leHByKSAlPiUKICBwdWxsKGdlbmVfbmFtZSkKYGBgCgpgYGB7cn0KbWFwKGdlbmVzX3RvX3Bsb3RbMTo1XSwgcGxvdF9leHByZXNzaW9uKQpgYGAKCgoKYGBge3J9CmdlbmVzX2tlZXAgPC0KICBzdGF0cyAlPiUKICBmaWx0ZXIobWF4X2V4cHIgPiBtYXhfdGhyZXNoKSAlPiUKICBwdWxsKGdlbmVfbmFtZSkKbGVuZ3RoKGdlbmVzX2tlZXApCmBgYAoKYGBge3J9CnNjZTEweF9tYWNyb3BoYWdlIDwtIHNjZTEweF9tYWNyb3BoYWdlW2dlbmVzX2tlZXAsIF0KYGBgCgpgYGB7ciBmaWcud2lkdGg9MTJ9CmdncGxvdCgKICBzdGF0cywKICBhZXMoCiAgICBtZWFuLAogICAgZGV0ZWN0ZWQKICApCikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC4zLCBhZXMoY29sb3IgPSBtYXhfZXhwciA8IDEpKSArCiAgZ2VvbV90ZXh0KGFlcygKICAgIGxhYmVsID0gZ2VuZV9uYW1lCiAgKSwKICBjaGVja19vdmVybGFwID0gVFJVRSwgbnVkZ2VfeSA9IC0wLjEsIHNpemUgPSAyLjUKICApCmBgYAojIyBDcmVhdGUgRGVzaWduIE1hdHJpeAoKYGBge3J9CmRlc2lnbl9tYWNyb3BoYWdlIDwtCiAgbW9kZWwubWF0cml4KH4gLTEgKyBjb25kaXRpb24gKyBzYW1wbGUsCiAgICBkYXRhID0gY29sRGF0YShzY2UxMHhfbWFjcm9waGFnZSkKICApWywgLWMoNSldCmNvbG5hbWVzKGRlc2lnbl9tYWNyb3BoYWdlKSA8LSBzdHJfcmVwbGFjZShjb2xuYW1lcyhkZXNpZ25fbWFjcm9waGFnZSksICJjb25kaXRpb258c2FtcGxlIiwgIiIpCgpkZXNpZ25fbWFjcm9waGFnZVsxOjMsIF0KYGBgCiMjIENvbXB1dGUgT2JzZXJ2YXRpb25hbCBXZWlnaHRzCgpgYGB7cn0KYXNzYXkoc2NlMTB4X21hY3JvcGhhZ2UsICJjb3VudHMiKSA8LSByb3VuZChhc3NheShzY2UxMHhfbWFjcm9waGFnZSwgImNvdW50cyIpKQpgYGAKCmBgYHtyfQpzeXN0ZW0udGltZSh7CiAgemluYiA8LQogICAgemluYkZpdChzY2UxMHhfbWFjcm9waGFnZSwKICAgICAgSyA9IDAsCiAgICAgIFggPSBkZXNpZ25fbWFjcm9waGFnZSwKICAgICAgdmVyYm9zZSA9IFRSVUUsCiAgICAgIEJQUEFSQU0gPSBNdWx0aWNvcmVQYXJhbSgzKSwKICAgICAgZXBzaWxvbiA9IDFlMTIKICAgICkKfSkKYGBgCgoKYGBge3J9CndlaWdodHMgPC0gY29tcHV0ZU9ic2VydmF0aW9uYWxXZWlnaHRzKHppbmIsIGFzLm1hdHJpeChhc3NheShzY2UxMHhfbWFjcm9waGFnZSkpKQpkaW1uYW1lcyh3ZWlnaHRzKSA8LSBkaW1uYW1lcyhzY2UxMHhfbWFjcm9waGFnZSkKYXNzYXkoc2NlMTB4X21hY3JvcGhhZ2UsICJ3ZWlnaHRzIikgPC0gd2VpZ2h0cwpgYGAKCiMjIGNvbnZlcnQgdG8gU0NFIHRvIERFU2VxRGF0YVNldCBvYmplY3QKCmBgYHtyfQpkZHNfbWFjcm9waGFnZSA8LQogIGNvbnZlcnRUbyhzY2UxMHhfbWFjcm9waGFnZSwgdHlwZSA9IGMoIkRFU2VxMiIpKQpkZXNpZ24oZGRzX21hY3JvcGhhZ2UpIDwtIGRlc2lnbl9tYWNyb3BoYWdlCmFzc2F5KGRkc19tYWNyb3BoYWdlLCAid2VpZ2h0cyIpIDwtIGFzc2F5KHNjZTEweF9tYWNyb3BoYWdlLCAid2VpZ2h0cyIpCmBgYAoKCgpgYGB7cn0KZGRzX21hY3JvcGhhZ2UgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhkZHNfbWFjcm9waGFnZSwgdHlwZSA9ICJwb3Njb3VudHMiKQpkZHNfbWFjcm9waGFnZSA8LQogIERFU2VxKGRkc19tYWNyb3BoYWdlLAogICAgdGVzdCA9ICJMUlQiLAogICAgdXNlVCA9IFRSVUUsCiAgICByZWR1Y2VkID0gZGVzaWduX21hY3JvcGhhZ2VbLCAxOjJdLAogICAgbWlubXUgPSAxZS02LAogICAgcGFyYWxsZWwgPSBUUlVFLAogICAgQlBQQVJBTSA9IE11bHRpY29yZVBhcmFtKDMpLAogICAgbWluUmVwID0gSW5mCiAgKQpgYGAKCmBgYHtyfQpwbG90RGlzcEVzdHMoZGRzX21hY3JvcGhhZ2UpCmBgYApgYGB7cn0KcmVzdWx0c05hbWVzKGRkc19tYWNyb3BoYWdlKQpgYGAKCgojIyMgUGxvdCBiYXRjaCBlZmZlY3RzCgpgYGB7cn0KYmF0Y2hfZHQgPC0KICByb3dEYXRhKGRkc19tYWNyb3BoYWdlKSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZV9pZCIpICU+JQogIHNlbGVjdCgiZ2VuZV9pZCIsICJ5bmcyIjoiYWdlZDQiKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9MTJ9CmdncGxvdCgKICBiYXRjaF9kdCwKICBhZXMoCiAgICB4ID0geW5nMiwKICAgIHkgPSB5bmczCiAgKQopICsKICBnZW9tX3BvaW50KAogICAgc2l6ZSA9IC4yLAogICAgYWxwaGEgPSAwLjMKICApICsKICBnZW9tX3RleHQoYWVzKAogICAgbGFiZWwgPSBnZW5lX2lkCiAgKSwKICBjaGVja19vdmVybGFwID0gVFJVRSwgbnVkZ2VfeSA9IC0wLjE1LCBzaXplID0gMwogICkKYGBgCgoKYGBge3IgZmlnLndpZHRoPTEyfQpnZ3Bsb3QoCiAgYmF0Y2hfZHQsCiAgYWVzKAogICAgeCA9IGFnZWQyLAogICAgeSA9IGFnZWQzCiAgKQopICsKICBnZW9tX3BvaW50KAogICAgc2l6ZSA9IC4yLAogICAgYWxwaGEgPSAwLjMKICApICsKICBnZW9tX3RleHQoYWVzKAogICAgbGFiZWwgPSBnZW5lX2lkCiAgKSwKICBjaGVja19vdmVybGFwID0gVFJVRSwgbnVkZ2VfeSA9IC0wLjE1LCBzaXplID0gMwogICkKYGBgCgoKYGBge3IgZmlnLndpZHRoPTEyfQpnZ3Bsb3QoCiAgYmF0Y2hfZHQsCiAgYWVzKAogICAgeCA9IGFnZWQyLAogICAgeSA9IGFnZWQ0CiAgKQopICsKICBnZW9tX3BvaW50KAogICAgc2l6ZSA9IC4yLAogICAgYWxwaGEgPSAwLjMKICApICsKICBnZW9tX3RleHQoYWVzKAogICAgbGFiZWwgPSBnZW5lX2lkCiAgKSwKICBjaGVja19vdmVybGFwID0gVFJVRSwgbnVkZ2VfeSA9IC0wLjE1LCBzaXplID0gMwogICkKYGBgCgojIyBUZXN0IGNvbnRyYXN0cyAKCmBgYHtyfQpjMSA8LSBjKDEsIC0xLCAwLCAwLCAwLCAwLCAwKQoKbWFjcm9waGFnZV9jb250cmFzdF92ZWMgPC0gbGlzdCgKICBtYWNyb3BoYWdlX2FnaW5nID0gYzEKKQpgYGAKCmBgYHtyfQpsZmNfbWFjcm9waGFnZSA8LQogIGltYXBfZGZjKG1hY3JvcGhhZ2VfY29udHJhc3RfdmVjLAogICAgZ2V0X2xmYywKICAgIGRkcyA9IGRkc19tYWNyb3BoYWdlCiAgKSAlPiUKICBiaW5kX2NvbHMoCiAgICBnZW5lX2lkID0gcm93bmFtZXMoZGRzX21hY3JvcGhhZ2UpLAogICAgLgogICkKYGBgCgpgYGB7cn0KbGZjX21hY3JvcGhhZ2UgPC0KICBsZWZ0X2pvaW4obGZjX21hY3JvcGhhZ2UsCiAgICByb3dEYXRhKGRkc19tYWNyb3BoYWdlKSAlPiUKICAgICAgYXNfdGliYmxlKHJvd25hbWVzID0gImdlbmVfaWQiKSwKICAgIGJ5ID0gImdlbmVfaWQiCiAgKQpgYGAKCmBgYHtyfQpsZmNfbWFjcm9waGFnZQpgYGAKCiMjIFNhdmUgZGF0YQoKYGBge3J9CndyaXRlX3RzdigKICBsZmNfbWFjcm9waGFnZSwKICBmaWxlLnBhdGgoCiAgICBkYXRhX2RpciwKICAgICJwcmVwcm9jZXNzZWQiLAogICAgIm1hY3JvcGhhZ2Vfc2NybmFzZXFfbW9kZXJhdGVkX2xmYy50eHQiCiAgKQopCmBgYAoKYGBge3J9CnJvd0RhdGEoc2NlMTB4X21hY3JvcGhhZ2UpIDwtCiAgbGVmdF9qb2luKHJvd0RhdGEoc2NlMTB4X21hY3JvcGhhZ2UpICU+JQogICAgYXNfdGliYmxlKHJvd25hbWVzID0gImdlbmVfaWQiKSAlPiUKICAgIHNlbGVjdChnZW5lX2lkKSwgbGZjX21hY3JvcGhhZ2UsIGJ5ID0gImdlbmVfaWQiKSAlPiUKICBEYXRhRnJhbWUoLikKCnJvd25hbWVzKHJvd0RhdGEoc2NlMTB4X21hY3JvcGhhZ2UpKSA8LSByb3dEYXRhKHNjZTEweF9tYWNyb3BoYWdlKSRnZW5lX2lkCmBgYAoKYGBge3J9CnNhdmVSRFMoCiAgc2NlMTB4X21hY3JvcGhhZ2UsCiAgZmlsZS5wYXRoKAogICAgZGF0YV9kaXIsCiAgICAicHJlcHJvY2Vzc2VkIiwKICAgICJzY2UxMHhfbWFjcm9waGFnZV9maWx0ZXJlZF9maW5hbC5yZHMiCiAgKQopCmBgYAoKCgojIyMgTWFrZSB2b2xjYW5vIHBsb3RzCgoKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNH0KbGZjX3RocmVzaCA8LSAxCnN2YWx1ZV90aHJlc2ggPC0gMTBlLTgKdm9sY2Fub19wbG90cyA8LQogIG1hcChuYW1lcyhtYWNyb3BoYWdlX2NvbnRyYXN0X3ZlYyksCiAgICBwbG90X3ZvbGNhbm8sCiAgICBsZmNfdGhyZXNoID0gbGZjX3RocmVzaCwKICAgIHN2YWx1ZV90aHJlc2ggPSBzdmFsdWVfdGhyZXNoLAogICAgbGZjID0gbGZjX21hY3JvcGhhZ2UsCiAgICBzdWZmaXggPSAibWFjcm9waGFnZV9sYWJlbGVkIgogICkKdm9sY2Fub19wbG90cwpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNH0KbGZjX3RocmVzaCA8LSAxCnN2YWx1ZV90aHJlc2ggPC0gMTBlLTgKdm9sY2Fub19wbG90c191bmxhYmVsZWQgPC0KICBtYXAobmFtZXMobWFjcm9waGFnZV9jb250cmFzdF92ZWMpLAogICAgcGxvdF92b2xjYW5vLAogICAgbGZjX3RocmVzaCA9IGxmY190aHJlc2gsCiAgICBzdmFsdWVfdGhyZXNoID0gc3ZhbHVlX3RocmVzaCwKICAgIGxmYyA9IGxmY19tYWNyb3BoYWdlLAogICAgc3VmZml4ID0gIm1hY3JvcGhhZ2VfdW5sYWJlbGVkIiwKICAgIGxhYmVsID0gYygiIikKICApCnZvbGNhbm9fcGxvdHNfdW5sYWJlbGVkCmBgYAoKYGBge3J9CmdlbmVzX2hpdHNfYWdpbmdfdXAgPC0KICBsZmNfbWFjcm9waGFnZSAlPiUKICBmaWx0ZXIobGZjX21hY3JvcGhhZ2VfYWdpbmcgPiAxKSAlPiUKICBhcnJhbmdlKC1sZmNfbWFjcm9waGFnZV9hZ2luZykgJT4lCiAgcHVsbChnZW5lX2lkKQpnZW5lc19oaXRzX2FnaW5nX3VwCmBgYAoKYGBge3J9Cm1hcChnZW5lc19oaXRzX2FnaW5nX3VwLCBwbG90X2V4cHJlc3Npb24pCmBgYAoKYGBge3J9CmdlbmVzX2hpdHNfYWdpbmdfZG93biA8LQogIGxmY19tYWNyb3BoYWdlICU+JQogIGZpbHRlcihsZmNfbWFjcm9waGFnZV9hZ2luZyA8IC0xKSAlPiUKICBhcnJhbmdlKGxmY19tYWNyb3BoYWdlX2FnaW5nKSAlPiUKICBwdWxsKGdlbmVfaWQpCmdlbmVzX2hpdHNfYWdpbmdfZG93bgpgYGAKCmBgYHtyfQptYXAoZ2VuZXNfaGl0c19hZ2luZ19kb3duLCBwbG90X2V4cHJlc3Npb24pCmBgYAoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCg==