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(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_stem, cluster_to_remove) {
gene_counts_by_cluster <-
t(assay(sce10x_stem)) %>%
as.matrix() %>%
as_tibble() %>%
group_by(cluster = colData(sce10x_stem)$clusters_condition) %>%
summarise_all(list(~ mean(.)))
umi_counts_max_1 <-
gene_counts_by_cluster[c(2, 4, 5), -1] %>%
summarize_all(list(~ max(.))) %>%
mutate(dummy = "dummy") %>%
pivot_longer(-dummy, names_to = "gene_name", values_to = "max_yng") %>%
select(-dummy)
umi_counts_max_2 <-
gene_counts_by_cluster[-5, -1] %>%
summarize_all(list(~ max(.))) %>%
mutate(dummy = "dummy") %>%
pivot_longer(-dummy, names_to = "gene_name", values_to = "max_stem12") %>%
select(-dummy, -gene_name)
stats <-
perFeatureQCMetrics(sce10x_stem,
exprs_values = c("counts"),
flatten = TRUE
) %>%
as_tibble() %>%
bind_cols(., rowData(sce10x_stem) %>%
as_tibble()) %>%
bind_cols(., umi_counts_max_1, umi_counts_max_2)
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_stem,
features = gene,
x = "sample",
exprs_values = "logcounts",
colour_by = "condition",
point_size = 1
) +
facet_wrap(~ colData(sce10x_stem)$clusters, ncol = 1)
}
plot_volcano <- function(var_name, lfc_thresh, svalue_thresh, lfc) {
EnhancedVolcano(lfc,
lab = lfc %>% pull(gene_id),
x = paste0("lfc_", var_name),
xlim = c(-4, 4),
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)
)
)
}
Load data
sce10x <-
readRDS(file.path(
data_dir,
"preprocessed",
"sce10x_filtered_final.rds"
))
sce10x_stem <- sce10x[, colData(sce10x)$celltype == "stem"]
rm(sce10x)
table(colData(sce10x_stem)$clusters, colData(sce10x_stem)$sample)
yng1 yng2 yng3 aged1 aged2 aged3 aged4
stem_1 397 661 541 370 404 185 435
stem_2 549 1030 379 151 278 141 304
stem_3 253 222 129 0 0 0 0
Discard low counts genes
n_exprs_genes <-
nexprs(sce10x_stem,
detection_limit = 5,
byrow = TRUE
)
keep <- n_exprs_genes >= 10
table(keep)
keep
FALSE TRUE
30184 3290
sce10x_stem <- sce10x_stem[keep, ]
batch_genes_remove <- c("Gm12918", "Rps23-ps1", "Diaph3", "Top2a", "Ecrg4", "Rpl9-ps6")
map(batch_genes_remove, plot_expression)
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
sce10x_stem <- sce10x_stem[!(rowData(sce10x_stem)$external_gene_name %in% batch_genes_remove), ]
Discard low cluster mean expression genes
stats <- get_counts_stats(sce10x_stem)
stats
max_thresh <- .3
stats <-
stats %>%
mutate(
highexpr_yng = max_yng > max_thresh,
highexpr_stem12 = max_stem12 > max_thresh,
genes_stem3 = highexpr_yng & !highexpr_stem12,
genes_stem12 = !highexpr_yng & highexpr_stem12,
genes_neither = !highexpr_yng & !highexpr_stem12,
genes_both = highexpr_yng & highexpr_stem12
)
stats %>%
count(highexpr_yng, highexpr_stem12)
ggplot(stats) +
geom_histogram(aes(max_yng), bins = 100) +
scale_x_log10() +
facet_wrap(~top_hvg_stem) +
geom_vline(xintercept = max_thresh)
genes_to_plot <-
stats %>%
filter(genes_stem3) %>%
pull(gene_name) %>%
sort()
genes_to_plot
[1] "Adam12" "Atp2a3" "Bcl2l11" "Ccnb2" "Cd24a" "Cdh20" "Cdkn1c" "Cenpa" "Cenpp" "Ckap2" "Cks2" "Ctnna2" "Dlk1" "Dpp6"
[15] "Efna5" "Ehd4" "Epb41" "Fgd4" "Gm28653" "Gm49397" "H1f10" "H1f2" "Hmox1" "Igfbp3" "Il1rap" "Jpx" "Khdrbs3" "L3mbtl4"
[29] "Lmnb1" "Lmnb2" "Lockd" "Lrrn1" "Mcm3" "Mcm5" "Mcm6" "Mcm7" "Mitf" "Mthfd1l" "Myog" "Nes" "Nr2f2" "Oca2"
[43] "Ophn1" "Pak3" "Pclaf" "Pola1" "Prune2" "Rrm2" "Sdk1" "Slc12a2" "Smc2" "Stmn1" "Whrn" "Ypel2"
map(genes_to_plot[1:5], plot_expression)
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
genes_to_plot <-
stats %>%
filter(genes_stem12) %>%
pull(gene_name) %>%
sort()
genes_to_plot
[1] "0610043K17Rik" "AL954352.1" "Atxn1" "Cadps2" "Ccl11" "Ccn2"
[7] "Cdh4" "Col8a1" "Cped1" "Cyp26a1" "Dab2" "ENSMUSG00000031842.14"
[13] "Eya2" "Frmpd4" "Gm10288" "Gm26834" "Gm29216" "Gm6225"
[19] "Gm7536" "Grid2" "Iigp1" "Macrod2" "Matn4" "Mbd1"
[25] "Mkx" "Myo1d" "Nap1l5" "Nr4a2" "P2ry14" "Pak1"
[31] "Parp14" "Pid1" "Rnf180" "Rnf213" "Samhd1" "Sgip1"
[37] "Slpi" "Spock3" "Thsd7a" "Tshr"
map(genes_to_plot[1:5], plot_expression)
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
genes_keep <-
stats %>%
filter(!genes_neither) %>%
pull(gene_name)
length(genes_keep)
[1] 3196
sce10x_stem <- sce10x_stem[genes_keep, ]
ggplot(
stats,
aes(
mean,
detected
)
) +
scale_x_log10() +
geom_point(size = 0.3, aes(color = top_hvg_stem)) +
geom_text(aes(
label = gene_name
),
check_overlap = TRUE, nudge_y = -0.1, size = 2.5
)
Create Design Matrix
design_stem <-
model.matrix(~ -1 + clusters_condition + sample,
data = colData(sce10x_stem)
)[, -c(8)]
colnames(design_stem) <- str_replace(colnames(design_stem), "clusters_condition|sample", "")
# colnames(design_stem) <- str_replace(colnames(design_stem), ":condition", "")
design_stem[1:3, ]
stem_1_aged stem_1_yng stem_2_aged stem_2_yng stem_3_yng yng2 yng3 aged2 aged3 aged4
CTACAGAGTGGCCCAT_d1 1 0 0 0 0 0 0 0 0 0
CTAACCCCACACCTGG_d1 1 0 0 0 0 0 0 0 0 0
AGGAGGTCACAGGATG_d1 1 0 0 0 0 0 0 0 0 0
Compute Observational Weights
assay(sce10x_stem, "counts") <- round(assay(sce10x_stem, "counts"))
system.time({
zinb <-
zinbFit(sce10x_stem,
K = 0,
X = design_stem,
verbose = TRUE,
BPPARAM = MulticoreParam(3),
epsilon = 1e12
)
})
Create model:
ok
Initialize parameters:
ok
Optimize parameters:
Iteration 1
penalized log-likelihood = -39143625.26738
After dispersion optimization = -71576744.1786462
user system elapsed
203.0 2.7 105.7
After right optimization = -71574036.0998483
After orthogonalization = -71574036.0998483
user system elapsed
122.9 1.5 76.8
After left optimization = -64813825.7211657
After orthogonalization = -64813825.7211657
Iteration 2
penalized log-likelihood = -64813825.7211657
After dispersion optimization = -64813825.7667293
user system elapsed
277.5 3.2 141.0
After right optimization = -64813722.3999213
After orthogonalization = -64813722.3999213
user system elapsed
27.5 1.3 15.2
After left optimization = -64813722.2901419
After orthogonalization = -64813722.2901419
Iteration 3
penalized log-likelihood = -64813722.2901419
ok
user system elapsed
1412 62 669
weights <- computeObservationalWeights(zinb, as.matrix(assay(sce10x_stem)))
dimnames(weights) <- dimnames(sce10x_stem)
assay(sce10x_stem, "weights") <- weights
convert to SCE to DESeqDataSet object
dds_stem <-
convertTo(sce10x_stem, type = c("DESeq2"))
converting counts to integer mode
design(dds_stem) <- design_stem
assay(dds_stem, "weights") <- assay(sce10x_stem, "weights")
dds_stem <- estimateSizeFactors(dds_stem, type = "poscounts")
dds_stem <-
DESeq(dds_stem,
test = "LRT",
useT = TRUE,
reduced = design_stem[, 1:5],
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
final dispersion estimates, fitting model and testing: 3 workers
plotDispEsts(dds_stem)
resultsNames(dds_stem)
[1] "stem_1_aged" "stem_1_yng" "stem_2_aged" "stem_2_yng" "stem_3_yng" "yng2" "yng3" "aged2" "aged3" "aged4"
Plot batch effects
batch_dt <-
rowData(dds_stem) %>%
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
)
batch_genes <- c(
"Cyp26a1", "Cenpa", "Dgkg", "Sorbs2",
"Gm28653", "Pclaf", "Myog", "Prune2"
)
map(batch_genes, plot_expression)
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]
Test contrasts
c1 <- c(1, -1, 1, -1, 0, 0, 0, 0, 0, 0) / 2
c2 <- c(-1, -1, 1, 1, 0, 0, 0, 0, 0, 0) / 2
c3 <- c(1, -1, 0, 0, 0, 0, 0, 0, 0, 0)
c4 <- c(0, 0, 1, -1, 0, 0, 0, 0, 0, 0)
c5 <- c(-1, 0, 1, 0, 0, 0, 0, 0, 0, 0)
c6 <- c(0, -1, 0, 1, 0, 0, 0, 0, 0, 0)
c7 <- c(0, -1 / 2, 0, -1 / 2, 1, 0, 0, 0, 0, 0)
c8 <- c(0, -1, 0, 0, 1, 0, 0, 0, 0, 0)
c9 <- c(0, 0, 0, -1, 1, 0, 0, 0, 0, 0)
aged_yng_contrast_vec <- list(
stem_aging = c1,
stem2_1 = c2,
stem1_aging = c3,
stem2_aging = c4,
stem2_1a = c5,
stem2_1y = c6
)
stem3_contrast_vec <- list(
stem3_12y = c7,
stem3_1y = c8,
stem3_2y = c9
)
genes_stem3_contrasts <-
stats %>%
filter(genes_stem3 | genes_both) %>%
pull(gene_name)
genes_aged_yng_contrasts <-
stats %>%
filter(genes_stem12 | genes_both) %>%
pull(gene_name)
lfc_stem3 <-
imap_dfc(stem3_contrast_vec,
get_lfc,
dds = dds_stem[genes_stem3_contrasts, ]
) %>%
bind_cols(
gene_id = rownames(dds_stem[genes_stem3_contrasts, ]),
.
)
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
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
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_aged_yng <-
imap_dfc(aged_yng_contrast_vec,
get_lfc,
dds = dds_stem[genes_aged_yng_contrasts, ]
) %>%
bind_cols(
gene_id = rownames(dds_stem[genes_aged_yng_contrasts, ]),
.
)
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
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
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
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
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
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 <- full_join(lfc_aged_yng, lfc_stem3, by = "gene_id")
lfc <-
left_join(lfc,
rowData(dds_stem) %>%
as_tibble(rownames = "gene_id"),
by = "gene_id"
)
lfc
Make volcano plots
lfc_thresh <- 1
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
5: In readChar(file, size, TRUE) : truncating string with embedded nuls
svalue_thresh <- 10e-8
volcano_plots <-
map(names(c(aged_yng_contrast_vec, stem3_contrast_vec)),
plot_volcano,
lfc_thresh = lfc_thresh,
svalue_thresh = svalue_thresh,
lfc = lfc
)
One or more p-values is 0. Converting to 10^-1 * current lowest non-zero p-value...
volcano_plots
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]
[[9]]
ggsave(file.path(figures_dir, "volcano_stem_aging_effect.pdf"), volcano_plots[[1]])
Saving 7 x 7 in image
ggsave(file.path(figures_dir, "volcano_stem_cluster2vs1.pdf"), volcano_plots[[2]])
ggsave(file.path(figures_dir, "volcano_stem_cluster3vs1_2.pdf"), volcano_plots[[7]])
Make scatterplots
aging_high_de <- c("Dgkg", "Sorbs2")
map(aging_high_de, plot_expression)
[[1]]
[[2]]
p1 <-
ggplot(
lfc %>% filter(!(gene_id %in% aging_high_de)),
aes(
x = lfc_stem_aging,
y = lfc_stem2_1,
color = lfc_stem3_12y
)
) +
geom_point(
size = .8,
alpha = 0.8
) +
geom_text(aes(
label = gene_id
),
check_overlap = TRUE, nudge_y = -0.15, size = 3
) +
scale_color_gradient2(
midpoint = 0,
low = "yellow",
mid = "black",
high = "blue"
) +
theme_classic() +
xlim(c(-6, 3))
p1
ggsave(file.path(figures_dir, "scatterplot_stem_aging_vs_cluster_effect.pdf"), p1)
Saving 7 x 7 in image
p2 <-
ggplot(
lfc,
aes(
x = lfc_stem2_1,
y = lfc_stem3_12y,
color = lfc_stem_aging
)
) +
geom_point(
size = .8,
alpha = 0.8
) +
geom_text(aes(
label = gene_id
),
check_overlap = TRUE, nudge_y = -0.15, size = 3
) +
scale_color_gradient2(
midpoint = 0,
limits = c(-3, 2),
low = "yellow",
mid = "black",
high = "blue"
) +
ylim(c(-2.2, 1.8)) +
theme_classic()
p2
ggsave(file.path(figures_dir, "scatterplot_stem3_vs_stem21_cluster_effect.pdf"), p2)
Saving 7 x 7 in image
p3 <-
ggplot(
lfc %>% filter(!(gene_id %in% aging_high_de)),
aes(
x = lfc_stem1_aging,
y = lfc_stem2_aging
)
) +
geom_point(
size = .8,
alpha = 0.8
) +
geom_text(aes(
label = gene_id
),
check_overlap = TRUE, nudge_y = -0.15, size = 4
) +
theme_classic()
p3
ggsave(file.path(figures_dir, "scatterplot_stem_1vs2_aging_effect.pdf"), p3)
Saving 7 x 7 in image
Save data
write_tsv(
lfc,
file.path(
data_dir,
"preprocessed",
"stem_scrnaseq_moderated_lfc.txt"
)
)
rowData(sce10x_stem) <-
left_join(rowData(sce10x_stem) %>%
as_tibble(rownames = "gene_id") %>%
select(gene_id), lfc, by = "gene_id") %>%
DataFrame(.)
rownames(rowData(sce10x_stem)) <- rowData(sce10x_stem)$gene_id
saveRDS(
sce10x_stem,
file.path(
data_dir,
"preprocessed",
"sce10x_stem_filtered_final.rds"
)
)
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 DESeq2_1.28.1
[6] forcats_0.5.0 stringr_1.4.0 dplyr_1.0.2 purrr_0.3.4 readr_1.3.1
[11] tidyr_1.1.2 tibble_3.0.3 tidyverse_1.3.0 scran_1.16.0 scater_1.16.2
[16] ggplot2_3.3.2 SingleCellExperiment_1.10.1 SummarizedExperiment_1.18.2 DelayedArray_0.14.1 matrixStats_0.56.0
[21] Biobase_2.48.0 GenomicRanges_1.40.0 GenomeInfoDb_1.24.2 IRanges_2.22.2 S4Vectors_0.26.1
[26] 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] BiocNeighbors_1.6.0 fs_1.5.0 rstudioapi_0.11 farver_2.0.3 bit64_4.0.5
[11] AnnotationDbi_1.50.3 fansi_0.4.1 lubridate_1.7.9 xml2_1.3.2 splines_4.0.2
[16] geneplotter_1.66.0 knitr_1.29 jsonlite_1.7.1 packrat_0.5.0 broom_0.7.0
[21] annotate_1.66.0 ashr_2.2-47 dbplyr_1.4.4 compiler_4.0.2 httr_1.4.2
[26] dqrng_0.2.1 backports_1.1.9 assertthat_0.2.1 Matrix_1.2-18 limma_3.44.3
[31] cli_2.0.2 BiocSingular_1.4.0 tools_4.0.2 rsvd_1.0.3 igraph_1.2.5
[36] gtable_0.3.0 glue_1.4.2 GenomeInfoDbData_1.2.3 Rcpp_1.0.5 softImpute_1.4
[41] cellranger_1.1.0 vctrs_0.3.4 DelayedMatrixStats_1.10.1 xfun_0.17 rvest_0.3.6
[46] lifecycle_0.2.0 irlba_2.3.3 statmod_1.4.34 XML_3.99-0.5 edgeR_3.30.3
[51] zlibbioc_1.34.0 scales_1.1.1 hms_0.5.3 RColorBrewer_1.1-2 memoise_1.1.0
[56] gridExtra_2.3 SQUAREM_2020.4 stringi_1.5.3 RSQLite_2.2.0 genefilter_1.70.0
[61] truncnorm_1.0-8 rlang_0.4.7 pkgconfig_2.0.3 bitops_1.0-6 invgamma_1.1
[66] lattice_0.20-41 labeling_0.3 cowplot_1.1.0 bit_4.0.4 tidyselect_1.1.0
[71] magrittr_1.5 R6_2.4.1 generics_0.0.2 DBI_1.1.0 pillar_1.4.6
[76] haven_2.3.1 withr_2.2.0 mixsqp_0.3-43 survival_3.1-12 RCurl_1.98-1.2
[81] modelr_0.1.8 crayon_1.3.4 viridis_0.5.1 locfit_1.5-9.4 grid_4.0.2
[86] readxl_1.3.1 blob_1.2.1 reprex_0.3.0 digest_0.6.25 xtable_1.8-4
[91] munsell_0.5.0 beeswarm_0.2.3 viridisLite_0.3.0 vipor_0.4.5
LS0tCnRpdGxlOiAiTW91c2UgTXVzY2xlIFN0ZW0gQ2VsbCBQcm9qZWN0ICIKc3VidGl0bGU6ICJQYXJ0IDVhOiBydW4gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgb24gc3RlbSBjZWxscyIKYXV0aG9yOiAKLSBuYW1lOiBSaWNrIEZhcm91bmkKICBhZmZpbGlhdGlvbjoKICAtICZjcnVrIEfDqW5vbWUgUXXDqWJlYyBJbm5vdmF0aW9uIENlbnRyZSwgTWNHaWxsIFVuaXZlcnNpdHksIE1vbnRyZWFsLCBDYW5hZGEKZGF0ZTogJ2ByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJVktJUItJWQiKWAnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIHRvYzogbm8KICAgIHRvY19mbG9hdDogCiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKLS0tCgoKIyBQcmVwYXJlIGFuYWx5c2lzIHdvcmtmbG93CgojIyBTZXQgZmlsZXBhdGhzIGFuZCBwYXJhbWV0ZXJzCgpgYGB7ciBzZXR1cH0Kc2V0LnNlZWQoNDIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gcnByb2pyb290OjpmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpCm9wdGlvbnMoCiAgcmVhZHIuc2hvd19wcm9ncmVzcyA9IEZBTFNFLAogIGRpZ2l0cyA9IDIKKQpgYGAKCiMjIExvYWQgcGFja2FnZXMKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShzY2F0ZXIpCiAgbGlicmFyeShzY3JhbikKICBsaWJyYXJ5KHRpZHl2ZXJzZSkKICBsaWJyYXJ5KERFU2VxMikKICBsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKICBsaWJyYXJ5KEJpb2NQYXJhbGxlbCkKICBsaWJyYXJ5KHppbmJ3YXZlKQogIHRoZW1lX3NldCh0aGVtZV9idygpKQp9KQpgYGAKCiMjIERlZmluZSBmaWxlIHBhdGhzCgpgYGB7cn0KZGF0YV9kaXIgPC0gIi4vZGF0YSIKZmlndXJlc19kaXIgPC0gZmlsZS5wYXRoKCIuL2ZpZ3VyZXMiKQpgYGAKCmBgYHtyfQpnZXRfY291bnRzX3N0YXRzIDwtIGZ1bmN0aW9uKHNjZTEweF9zdGVtLCBjbHVzdGVyX3RvX3JlbW92ZSkgewogIGdlbmVfY291bnRzX2J5X2NsdXN0ZXIgPC0KICAgIHQoYXNzYXkoc2NlMTB4X3N0ZW0pKSAlPiUKICAgIGFzLm1hdHJpeCgpICU+JQogICAgYXNfdGliYmxlKCkgJT4lCiAgICBncm91cF9ieShjbHVzdGVyID0gY29sRGF0YShzY2UxMHhfc3RlbSkkY2x1c3RlcnNfY29uZGl0aW9uKSAlPiUKICAgIHN1bW1hcmlzZV9hbGwobGlzdCh+IG1lYW4oLikpKQoKICB1bWlfY291bnRzX21heF8xIDwtCiAgICBnZW5lX2NvdW50c19ieV9jbHVzdGVyW2MoMiwgNCwgNSksIC0xXSAlPiUKICAgIHN1bW1hcml6ZV9hbGwobGlzdCh+IG1heCguKSkpICU+JQogICAgbXV0YXRlKGR1bW15ID0gImR1bW15IikgJT4lCiAgICBwaXZvdF9sb25nZXIoLWR1bW15LCBuYW1lc190byA9ICJnZW5lX25hbWUiLCB2YWx1ZXNfdG8gPSAibWF4X3luZyIpICU+JQogICAgc2VsZWN0KC1kdW1teSkKCiAgdW1pX2NvdW50c19tYXhfMiA8LQogICAgZ2VuZV9jb3VudHNfYnlfY2x1c3RlclstNSwgLTFdICU+JQogICAgc3VtbWFyaXplX2FsbChsaXN0KH4gbWF4KC4pKSkgJT4lCiAgICBtdXRhdGUoZHVtbXkgPSAiZHVtbXkiKSAlPiUKICAgIHBpdm90X2xvbmdlcigtZHVtbXksIG5hbWVzX3RvID0gImdlbmVfbmFtZSIsIHZhbHVlc190byA9ICJtYXhfc3RlbTEyIikgJT4lCiAgICBzZWxlY3QoLWR1bW15LCAtZ2VuZV9uYW1lKQoKCgogIHN0YXRzIDwtCiAgICBwZXJGZWF0dXJlUUNNZXRyaWNzKHNjZTEweF9zdGVtLAogICAgICBleHByc192YWx1ZXMgPSBjKCJjb3VudHMiKSwKICAgICAgZmxhdHRlbiA9IFRSVUUKICAgICkgJT4lCiAgICBhc190aWJibGUoKSAlPiUKICAgIGJpbmRfY29scyguLCByb3dEYXRhKHNjZTEweF9zdGVtKSAlPiUKICAgICAgYXNfdGliYmxlKCkpICU+JQogICAgYmluZF9jb2xzKC4sIHVtaV9jb3VudHNfbWF4XzEsIHVtaV9jb3VudHNfbWF4XzIpCgogIHJldHVybihzdGF0cykKfQoKZ2V0X2xmYyA8LSBmdW5jdGlvbihjb250cmFzdCwgbmFtZSwgZGRzKSB7CiAgbGZjU2hyaW5rKGRkcywKICAgIGNvbnRyYXN0ID0gY29udHJhc3QsCiAgICB0eXBlID0gImFzaHIiLAogICAgc3ZhbHVlID0gVAogICkgJT4lCiAgICBhc190aWJibGUoKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLWJhc2VNZWFuKSAlPiUKICAgIG11dGF0ZShzdmFsdWUgPSBhYnMoc3ZhbHVlKSkgJT4lCiAgICBzZXRfbmFtZXMocGFzdGUoYygibGZjIiwgImxmY19zZSIsICJzdmFsdWUiKSwgbmFtZSwgc2VwID0gIl8iKSkKfQoKcGxvdF9leHByZXNzaW9uIDwtIGZ1bmN0aW9uKGdlbmUpIHsKICBwbG90RXhwcmVzc2lvbihzY2UxMHhfc3RlbSwKICAgIGZlYXR1cmVzID0gZ2VuZSwKICAgIHggPSAic2FtcGxlIiwKICAgIGV4cHJzX3ZhbHVlcyA9ICJsb2djb3VudHMiLAogICAgY29sb3VyX2J5ID0gImNvbmRpdGlvbiIsCiAgICBwb2ludF9zaXplID0gMQogICkgKwogICAgZmFjZXRfd3JhcCh+IGNvbERhdGEoc2NlMTB4X3N0ZW0pJGNsdXN0ZXJzLCBuY29sID0gMSkKfQpwbG90X3ZvbGNhbm8gPC0gZnVuY3Rpb24odmFyX25hbWUsIGxmY190aHJlc2gsIHN2YWx1ZV90aHJlc2gsIGxmYykgewogIEVuaGFuY2VkVm9sY2FubyhsZmMsCiAgICBsYWIgPSBsZmMgJT4lIHB1bGwoZ2VuZV9pZCksCiAgICB4ID0gcGFzdGUwKCJsZmNfIiwgdmFyX25hbWUpLAogICAgeGxpbSA9IGMoLTQsIDQpLAogICAgeSA9IHBhc3RlMCgic3ZhbHVlXyIsIHZhcl9uYW1lKSwKICAgIHhsYWIgPSBicXVvdGUofiBpdGFsaWMoTW9kZXJhdGVkKSB+IExvZ1syXSB+IEZDKSwKICAgIHlsYWIgPSBicXVvdGUofiAtTG9nWzEwXSB+IGl0YWxpYyhzdmFsdWUpKSwKICAgIGNvbCA9IGMoImdyZXkzMCIsICJmb3Jlc3RncmVlbiIsICJyZWQyIiwgInJveWFsYmx1ZSIpLAogICAgcEN1dG9mZiA9IHN2YWx1ZV90aHJlc2gsCiAgICBGQ2N1dG9mZiA9IGxmY190aHJlc2gsCiAgICBsZWdlbmRMYWJlbHMgPSBjKAogICAgICAiTlMiLAogICAgICBleHByZXNzaW9uKExvZ1syXSB+IEZDKSwKICAgICAgInMtdmFsdWUiLAogICAgICBleHByZXNzaW9uKHMgLSB2YWx1ZSB+IGFuZCB+IGxvZ1syXSB+IEZDKQogICAgKQogICkKfQpgYGAKCgoKIyMgTG9hZCBkYXRhCgoKYGBge3J9CnNjZTEweCA8LQogIHJlYWRSRFMoZmlsZS5wYXRoKAogICAgZGF0YV9kaXIsCiAgICAicHJlcHJvY2Vzc2VkIiwKICAgICJzY2UxMHhfZmlsdGVyZWRfZmluYWwucmRzIgogICkpCmBgYAoKCmBgYHtyfQpzY2UxMHhfc3RlbSA8LSBzY2UxMHhbLCBjb2xEYXRhKHNjZTEweCkkY2VsbHR5cGUgPT0gInN0ZW0iXQpybShzY2UxMHgpCmBgYAoKYGBge3J9CnRhYmxlKGNvbERhdGEoc2NlMTB4X3N0ZW0pJGNsdXN0ZXJzLCBjb2xEYXRhKHNjZTEweF9zdGVtKSRzYW1wbGUpCmBgYAoKCgojIyBEaXNjYXJkIGxvdyBjb3VudHMgZ2VuZXMKCmBgYHtyfQpuX2V4cHJzX2dlbmVzIDwtCiAgbmV4cHJzKHNjZTEweF9zdGVtLAogICAgZGV0ZWN0aW9uX2xpbWl0ID0gNSwKICAgIGJ5cm93ID0gVFJVRQogICkKa2VlcCA8LSBuX2V4cHJzX2dlbmVzID49IDEwCnRhYmxlKGtlZXApCmBgYAoKYGBge3J9CnNjZTEweF9zdGVtIDwtIHNjZTEweF9zdGVtW2tlZXAsIF0KYGBgCgpgYGB7cn0KYmF0Y2hfZ2VuZXNfcmVtb3ZlIDwtIGMoIkdtMTI5MTgiLCAiUnBzMjMtcHMxIiwgIkRpYXBoMyIsICJUb3AyYSIsICJFY3JnNCIsICJScGw5LXBzNiIpCmBgYAoKYGBge3J9Cm1hcChiYXRjaF9nZW5lc19yZW1vdmUsIHBsb3RfZXhwcmVzc2lvbikKYGBgCgpgYGB7cn0Kc2NlMTB4X3N0ZW0gPC0gc2NlMTB4X3N0ZW1bIShyb3dEYXRhKHNjZTEweF9zdGVtKSRleHRlcm5hbF9nZW5lX25hbWUgJWluJSBiYXRjaF9nZW5lc19yZW1vdmUpLCBdCmBgYAoKCiMjICBEaXNjYXJkIGxvdyBjbHVzdGVyIG1lYW4gZXhwcmVzc2lvbiBnZW5lcwoKYGBge3J9CnN0YXRzIDwtIGdldF9jb3VudHNfc3RhdHMoc2NlMTB4X3N0ZW0pCnN0YXRzCmBgYAoKCmBgYHtyfQptYXhfdGhyZXNoIDwtIC4zCnN0YXRzIDwtCiAgc3RhdHMgJT4lCiAgbXV0YXRlKAogICAgaGlnaGV4cHJfeW5nID0gbWF4X3luZyA+IG1heF90aHJlc2gsCiAgICBoaWdoZXhwcl9zdGVtMTIgPSBtYXhfc3RlbTEyID4gbWF4X3RocmVzaCwKICAgIGdlbmVzX3N0ZW0zID0gaGlnaGV4cHJfeW5nICYgIWhpZ2hleHByX3N0ZW0xMiwKICAgIGdlbmVzX3N0ZW0xMiA9ICFoaWdoZXhwcl95bmcgJiBoaWdoZXhwcl9zdGVtMTIsCiAgICBnZW5lc19uZWl0aGVyID0gIWhpZ2hleHByX3luZyAmICFoaWdoZXhwcl9zdGVtMTIsCiAgICBnZW5lc19ib3RoID0gaGlnaGV4cHJfeW5nICYgaGlnaGV4cHJfc3RlbTEyCiAgKQpgYGAKCgoKCmBgYHtyfQpzdGF0cyAlPiUKICBjb3VudChoaWdoZXhwcl95bmcsIGhpZ2hleHByX3N0ZW0xMikKYGBgCgoKCgpgYGB7ciBmaWcud2lkdGg9N30KZ2dwbG90KHN0YXRzKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKG1heF95bmcpLCBiaW5zID0gMTAwKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBmYWNldF93cmFwKH50b3BfaHZnX3N0ZW0pICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtYXhfdGhyZXNoKQpgYGAKCgoKYGBge3J9CmdlbmVzX3RvX3Bsb3QgPC0KICBzdGF0cyAlPiUKICBmaWx0ZXIoZ2VuZXNfc3RlbTMpICU+JQogIHB1bGwoZ2VuZV9uYW1lKSAlPiUKICBzb3J0KCkKZ2VuZXNfdG9fcGxvdApgYGAKCmBgYHtyfQptYXAoZ2VuZXNfdG9fcGxvdFsxOjVdLCBwbG90X2V4cHJlc3Npb24pCmBgYAoKYGBge3J9CmdlbmVzX3RvX3Bsb3QgPC0KICBzdGF0cyAlPiUKICBmaWx0ZXIoZ2VuZXNfc3RlbTEyKSAlPiUKICBwdWxsKGdlbmVfbmFtZSkgJT4lCiAgc29ydCgpCmdlbmVzX3RvX3Bsb3QKYGBgCgpgYGB7cn0KbWFwKGdlbmVzX3RvX3Bsb3RbMTo1XSwgcGxvdF9leHByZXNzaW9uKQpgYGAKCgoKCmBgYHtyfQpnZW5lc19rZWVwIDwtCiAgc3RhdHMgJT4lCiAgZmlsdGVyKCFnZW5lc19uZWl0aGVyKSAlPiUKICBwdWxsKGdlbmVfbmFtZSkKbGVuZ3RoKGdlbmVzX2tlZXApCmBgYAoKYGBge3J9CnNjZTEweF9zdGVtIDwtIHNjZTEweF9zdGVtW2dlbmVzX2tlZXAsIF0KYGBgCgpgYGB7ciBmaWcud2lkdGg9MTJ9CmdncGxvdCgKICBzdGF0cywKICBhZXMoCiAgICBtZWFuLAogICAgZGV0ZWN0ZWQKICApCikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC4zLCBhZXMoY29sb3IgPSB0b3BfaHZnX3N0ZW0pKSArCiAgZ2VvbV90ZXh0KGFlcygKICAgIGxhYmVsID0gZ2VuZV9uYW1lCiAgKSwKICBjaGVja19vdmVybGFwID0gVFJVRSwgbnVkZ2VfeSA9IC0wLjEsIHNpemUgPSAyLjUKICApCmBgYAojIyBDcmVhdGUgRGVzaWduIE1hdHJpeAoKYGBge3J9CmRlc2lnbl9zdGVtIDwtCiAgbW9kZWwubWF0cml4KH4gLTEgKyBjbHVzdGVyc19jb25kaXRpb24gKyBzYW1wbGUsCiAgICBkYXRhID0gY29sRGF0YShzY2UxMHhfc3RlbSkKICApWywgLWMoOCldCmNvbG5hbWVzKGRlc2lnbl9zdGVtKSA8LSBzdHJfcmVwbGFjZShjb2xuYW1lcyhkZXNpZ25fc3RlbSksICJjbHVzdGVyc19jb25kaXRpb258c2FtcGxlIiwgIiIpCiMgY29sbmFtZXMoZGVzaWduX3N0ZW0pIDwtIHN0cl9yZXBsYWNlKGNvbG5hbWVzKGRlc2lnbl9zdGVtKSwgIjpjb25kaXRpb24iLCAiIikKCmRlc2lnbl9zdGVtWzE6MywgXQpgYGAKIyMgQ29tcHV0ZSBPYnNlcnZhdGlvbmFsIFdlaWdodHMKCmBgYHtyfQphc3NheShzY2UxMHhfc3RlbSwgImNvdW50cyIpIDwtIHJvdW5kKGFzc2F5KHNjZTEweF9zdGVtLCAiY291bnRzIikpCmBgYAoKYGBge3J9CnN5c3RlbS50aW1lKHsKICB6aW5iIDwtCiAgICB6aW5iRml0KHNjZTEweF9zdGVtLAogICAgICBLID0gMCwKICAgICAgWCA9IGRlc2lnbl9zdGVtLAogICAgICB2ZXJib3NlID0gVFJVRSwKICAgICAgQlBQQVJBTSA9IE11bHRpY29yZVBhcmFtKDMpLAogICAgICBlcHNpbG9uID0gMWUxMgogICAgKQp9KQpgYGAKCgpgYGB7cn0Kd2VpZ2h0cyA8LSBjb21wdXRlT2JzZXJ2YXRpb25hbFdlaWdodHMoemluYiwgYXMubWF0cml4KGFzc2F5KHNjZTEweF9zdGVtKSkpCmRpbW5hbWVzKHdlaWdodHMpIDwtIGRpbW5hbWVzKHNjZTEweF9zdGVtKQphc3NheShzY2UxMHhfc3RlbSwgIndlaWdodHMiKSA8LSB3ZWlnaHRzCmBgYAoKIyMgY29udmVydCB0byBTQ0UgdG8gREVTZXFEYXRhU2V0IG9iamVjdAoKYGBge3J9CmRkc19zdGVtIDwtCiAgY29udmVydFRvKHNjZTEweF9zdGVtLCB0eXBlID0gYygiREVTZXEyIikpCmRlc2lnbihkZHNfc3RlbSkgPC0gZGVzaWduX3N0ZW0KYXNzYXkoZGRzX3N0ZW0sICJ3ZWlnaHRzIikgPC0gYXNzYXkoc2NlMTB4X3N0ZW0sICJ3ZWlnaHRzIikKYGBgCgoKCmBgYHtyfQpkZHNfc3RlbSA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRkc19zdGVtLCB0eXBlID0gInBvc2NvdW50cyIpCmRkc19zdGVtIDwtCiAgREVTZXEoZGRzX3N0ZW0sCiAgICB0ZXN0ID0gIkxSVCIsCiAgICB1c2VUID0gVFJVRSwKICAgIHJlZHVjZWQgPSBkZXNpZ25fc3RlbVssIDE6NV0sCiAgICBtaW5tdSA9IDFlLTYsCiAgICBwYXJhbGxlbCA9IFRSVUUsCiAgICBCUFBBUkFNID0gTXVsdGljb3JlUGFyYW0oMyksCiAgICBtaW5SZXAgPSBJbmYKICApCmBgYAoKYGBge3J9CnBsb3REaXNwRXN0cyhkZHNfc3RlbSkKYGBgCmBgYHtyfQpyZXN1bHRzTmFtZXMoZGRzX3N0ZW0pCmBgYAoKCiMjIyBQbG90IGJhdGNoIGVmZmVjdHMKCmBgYHtyfQpiYXRjaF9kdCA8LQogIHJvd0RhdGEoZGRzX3N0ZW0pICU+JQogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJnZW5lX2lkIikgJT4lCiAgc2VsZWN0KCJnZW5lX2lkIiwgInluZzIiOiJhZ2VkNCIpCmBgYAoKCmBgYHtyIGZpZy53aWR0aD0xMn0KZ2dwbG90KAogIGJhdGNoX2R0LAogIGFlcygKICAgIHggPSB5bmcyLAogICAgeSA9IHluZzMKICApCikgKwogIGdlb21fcG9pbnQoCiAgICBzaXplID0gLjIsCiAgICBhbHBoYSA9IDAuMwogICkgKwogIGdlb21fdGV4dChhZXMoCiAgICBsYWJlbCA9IGdlbmVfaWQKICApLAogIGNoZWNrX292ZXJsYXAgPSBUUlVFLCBudWRnZV95ID0gLTAuMTUsIHNpemUgPSAzCiAgKQpgYGAKCgoKCmBgYHtyIGZpZy53aWR0aD0xMn0KZ2dwbG90KAogIGJhdGNoX2R0LAogIGFlcygKICAgIHggPSBhZ2VkMiwKICAgIHkgPSBhZ2VkMwogICkKKSArCiAgZ2VvbV9wb2ludCgKICAgIHNpemUgPSAuMiwKICAgIGFscGhhID0gMC4zCiAgKSArCiAgZ2VvbV90ZXh0KGFlcygKICAgIGxhYmVsID0gZ2VuZV9pZAogICksCiAgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIG51ZGdlX3kgPSAtMC4xNSwgc2l6ZSA9IDMKICApCmBgYAoKCmBgYHtyIGZpZy53aWR0aD0xMn0KZ2dwbG90KAogIGJhdGNoX2R0LAogIGFlcygKICAgIHggPSBhZ2VkMiwKICAgIHkgPSBhZ2VkNAogICkKKSArCiAgZ2VvbV9wb2ludCgKICAgIHNpemUgPSAuMiwKICAgIGFscGhhID0gMC4zCiAgKSArCiAgZ2VvbV90ZXh0KGFlcygKICAgIGxhYmVsID0gZ2VuZV9pZAogICksCiAgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIG51ZGdlX3kgPSAtMC4xNSwgc2l6ZSA9IDMKICApCmBgYAoKCgpgYGB7cn0KYmF0Y2hfZ2VuZXMgPC0gYygKICAiQ3lwMjZhMSIsICJDZW5wYSIsICJEZ2tnIiwgIlNvcmJzMiIsCiAgIkdtMjg2NTMiLCAiUGNsYWYiLCAiTXlvZyIsICJQcnVuZTIiCikKbWFwKGJhdGNoX2dlbmVzLCBwbG90X2V4cHJlc3Npb24pCmBgYAoKIyMgVGVzdCBjb250cmFzdHMgCgpgYGB7cn0KYzEgPC0gYygxLCAtMSwgMSwgLTEsIDAsIDAsIDAsIDAsIDAsIDApIC8gMgpjMiA8LSBjKC0xLCAtMSwgMSwgMSwgMCwgMCwgMCwgMCwgMCwgMCkgLyAyCmMzIDwtIGMoMSwgLTEsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDApCmM0IDwtIGMoMCwgMCwgMSwgLTEsIDAsIDAsIDAsIDAsIDAsIDApCmM1IDwtIGMoLTEsIDAsIDEsIDAsIDAsIDAsIDAsIDAsIDAsIDApCmM2IDwtIGMoMCwgLTEsIDAsIDEsIDAsIDAsIDAsIDAsIDAsIDApCmM3IDwtIGMoMCwgLTEgLyAyLCAwLCAtMSAvIDIsIDEsIDAsIDAsIDAsIDAsIDApCmM4IDwtIGMoMCwgLTEsIDAsIDAsIDEsIDAsIDAsIDAsIDAsIDApCmM5IDwtIGMoMCwgMCwgMCwgLTEsIDEsIDAsIDAsIDAsIDAsIDApCgoKYWdlZF95bmdfY29udHJhc3RfdmVjIDwtIGxpc3QoCiAgc3RlbV9hZ2luZyA9IGMxLAogIHN0ZW0yXzEgPSBjMiwKICBzdGVtMV9hZ2luZyA9IGMzLAogIHN0ZW0yX2FnaW5nID0gYzQsCiAgc3RlbTJfMWEgPSBjNSwKICBzdGVtMl8xeSA9IGM2CikKCnN0ZW0zX2NvbnRyYXN0X3ZlYyA8LSBsaXN0KAogIHN0ZW0zXzEyeSA9IGM3LAogIHN0ZW0zXzF5ID0gYzgsCiAgc3RlbTNfMnkgPSBjOQopCmBgYAoKYGBge3J9CmdlbmVzX3N0ZW0zX2NvbnRyYXN0cyA8LQogIHN0YXRzICU+JQogIGZpbHRlcihnZW5lc19zdGVtMyB8IGdlbmVzX2JvdGgpICU+JQogIHB1bGwoZ2VuZV9uYW1lKQoKZ2VuZXNfYWdlZF95bmdfY29udHJhc3RzIDwtCiAgc3RhdHMgJT4lCiAgZmlsdGVyKGdlbmVzX3N0ZW0xMiB8IGdlbmVzX2JvdGgpICU+JQogIHB1bGwoZ2VuZV9uYW1lKQpgYGAKCgpgYGB7cn0KbGZjX3N0ZW0zIDwtCiAgaW1hcF9kZmMoc3RlbTNfY29udHJhc3RfdmVjLAogICAgZ2V0X2xmYywKICAgIGRkcyA9IGRkc19zdGVtW2dlbmVzX3N0ZW0zX2NvbnRyYXN0cywgXQogICkgJT4lCiAgYmluZF9jb2xzKAogICAgZ2VuZV9pZCA9IHJvd25hbWVzKGRkc19zdGVtW2dlbmVzX3N0ZW0zX2NvbnRyYXN0cywgXSksCiAgICAuCiAgKQpgYGAKCmBgYHtyfQpsZmNfYWdlZF95bmcgPC0KICBpbWFwX2RmYyhhZ2VkX3luZ19jb250cmFzdF92ZWMsCiAgICBnZXRfbGZjLAogICAgZGRzID0gZGRzX3N0ZW1bZ2VuZXNfYWdlZF95bmdfY29udHJhc3RzLCBdCiAgKSAlPiUKICBiaW5kX2NvbHMoCiAgICBnZW5lX2lkID0gcm93bmFtZXMoZGRzX3N0ZW1bZ2VuZXNfYWdlZF95bmdfY29udHJhc3RzLCBdKSwKICAgIC4KICApCmBgYAoKYGBge3J9CmxmYyA8LSBmdWxsX2pvaW4obGZjX2FnZWRfeW5nLCBsZmNfc3RlbTMsIGJ5ID0gImdlbmVfaWQiKQpgYGAKCmBgYHtyfQpsZmMgPC0KICBsZWZ0X2pvaW4obGZjLAogICAgcm93RGF0YShkZHNfc3RlbSkgJT4lCiAgICAgIGFzX3RpYmJsZShyb3duYW1lcyA9ICJnZW5lX2lkIiksCiAgICBieSA9ICJnZW5lX2lkIgogICkKYGBgCgpgYGB7cn0KbGZjCmBgYAoKCiMjIyBNYWtlIHZvbGNhbm8gcGxvdHMKCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTR9CmxmY190aHJlc2ggPC0gMQpzdmFsdWVfdGhyZXNoIDwtIDEwZS04CnZvbGNhbm9fcGxvdHMgPC0KICBtYXAobmFtZXMoYyhhZ2VkX3luZ19jb250cmFzdF92ZWMsIHN0ZW0zX2NvbnRyYXN0X3ZlYykpLAogICAgcGxvdF92b2xjYW5vLAogICAgbGZjX3RocmVzaCA9IGxmY190aHJlc2gsCiAgICBzdmFsdWVfdGhyZXNoID0gc3ZhbHVlX3RocmVzaCwKICAgIGxmYyA9IGxmYwogICkKdm9sY2Fub19wbG90cwpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTR9Cmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJ2b2xjYW5vX3N0ZW1fYWdpbmdfZWZmZWN0LnBkZiIpLCB2b2xjYW5vX3Bsb3RzW1sxXV0pCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJ2b2xjYW5vX3N0ZW1fY2x1c3RlcjJ2czEucGRmIiksIHZvbGNhbm9fcGxvdHNbWzJdXSkKZ2dzYXZlKGZpbGUucGF0aChmaWd1cmVzX2RpciwgInZvbGNhbm9fc3RlbV9jbHVzdGVyM3ZzMV8yLnBkZiIpLCB2b2xjYW5vX3Bsb3RzW1s3XV0pCmBgYAoKCiMjIE1ha2Ugc2NhdHRlcnBsb3RzCgpgYGB7cn0KYWdpbmdfaGlnaF9kZSA8LSBjKCJEZ2tnIiwgIlNvcmJzMiIpCmBgYAoKCmBgYHtyIGZpZy53aWR0aD0xMn0KbWFwKGFnaW5nX2hpZ2hfZGUsIHBsb3RfZXhwcmVzc2lvbikKYGBgCgoKCmBgYHtyIGZpZy53aWR0aD0xMn0KcDEgPC0KICBnZ3Bsb3QoCiAgICBsZmMgJT4lIGZpbHRlcighKGdlbmVfaWQgJWluJSBhZ2luZ19oaWdoX2RlKSksCiAgICBhZXMoCiAgICAgIHggPSBsZmNfc3RlbV9hZ2luZywKICAgICAgeSA9IGxmY19zdGVtMl8xLAogICAgICBjb2xvciA9IGxmY19zdGVtM18xMnkKICAgICkKICApICsKICBnZW9tX3BvaW50KAogICAgc2l6ZSA9IC44LAogICAgYWxwaGEgPSAwLjgKICApICsKICBnZW9tX3RleHQoYWVzKAogICAgbGFiZWwgPSBnZW5lX2lkCiAgKSwKICBjaGVja19vdmVybGFwID0gVFJVRSwgbnVkZ2VfeSA9IC0wLjE1LCBzaXplID0gMwogICkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50MigKICAgIG1pZHBvaW50ID0gMCwKICAgIGxvdyA9ICJ5ZWxsb3ciLAogICAgbWlkID0gImJsYWNrIiwKICAgIGhpZ2ggPSAiYmx1ZSIKICApICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHhsaW0oYygtNiwgMykpCnAxCmBgYCAKCgpgYGB7cn0KZ2dzYXZlKGZpbGUucGF0aChmaWd1cmVzX2RpciwgInNjYXR0ZXJwbG90X3N0ZW1fYWdpbmdfdnNfY2x1c3Rlcl9lZmZlY3QucGRmIiksIHAxKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9MTJ9CnAyIDwtCiAgZ2dwbG90KAogICAgbGZjLAogICAgYWVzKAogICAgICB4ID0gbGZjX3N0ZW0yXzEsCiAgICAgIHkgPSBsZmNfc3RlbTNfMTJ5LAogICAgICBjb2xvciA9IGxmY19zdGVtX2FnaW5nCiAgICApCiAgKSArCiAgZ2VvbV9wb2ludCgKICAgIHNpemUgPSAuOCwKICAgIGFscGhhID0gMC44CiAgKSArCiAgZ2VvbV90ZXh0KGFlcygKICAgIGxhYmVsID0gZ2VuZV9pZAogICksCiAgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIG51ZGdlX3kgPSAtMC4xNSwgc2l6ZSA9IDMKICApICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIoCiAgICBtaWRwb2ludCA9IDAsCiAgICBsaW1pdHMgPSBjKC0zLCAyKSwKICAgIGxvdyA9ICJ5ZWxsb3ciLAogICAgbWlkID0gImJsYWNrIiwKICAgIGhpZ2ggPSAiYmx1ZSIKICApICsKICB5bGltKGMoLTIuMiwgMS44KSkgKwogIHRoZW1lX2NsYXNzaWMoKQpwMgpgYGAKCmBgYHtyfQpnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCAic2NhdHRlcnBsb3Rfc3RlbTNfdnNfc3RlbTIxX2NsdXN0ZXJfZWZmZWN0LnBkZiIpLCBwMikKYGBgCmBgYHtyIGZpZy53aWR0aD0xMn0KcDMgPC0KICBnZ3Bsb3QoCiAgICBsZmMgJT4lIGZpbHRlcighKGdlbmVfaWQgJWluJSBhZ2luZ19oaWdoX2RlKSksCiAgICBhZXMoCiAgICAgIHggPSBsZmNfc3RlbTFfYWdpbmcsCiAgICAgIHkgPSBsZmNfc3RlbTJfYWdpbmcKICAgICkKICApICsKICBnZW9tX3BvaW50KAogICAgc2l6ZSA9IC44LAogICAgYWxwaGEgPSAwLjgKICApICsKICBnZW9tX3RleHQoYWVzKAogICAgbGFiZWwgPSBnZW5lX2lkCiAgKSwKICBjaGVja19vdmVybGFwID0gVFJVRSwgbnVkZ2VfeSA9IC0wLjE1LCBzaXplID0gNAogICkgKwogIHRoZW1lX2NsYXNzaWMoKQpwMwpgYGAKCmBgYHtyfQpnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCAic2NhdHRlcnBsb3Rfc3RlbV8xdnMyX2FnaW5nX2VmZmVjdC5wZGYiKSwgcDMpCmBgYAoKCiMjIFNhdmUgZGF0YQoKYGBge3J9CndyaXRlX3RzdigKICBsZmMsCiAgZmlsZS5wYXRoKAogICAgZGF0YV9kaXIsCiAgICAicHJlcHJvY2Vzc2VkIiwKICAgICJzdGVtX3Njcm5hc2VxX21vZGVyYXRlZF9sZmMudHh0IgogICkKKQpgYGAKCgpgYGB7cn0Kcm93RGF0YShzY2UxMHhfc3RlbSkgPC0KICBsZWZ0X2pvaW4ocm93RGF0YShzY2UxMHhfc3RlbSkgJT4lCiAgICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZV9pZCIpICU+JQogICAgc2VsZWN0KGdlbmVfaWQpLCBsZmMsIGJ5ID0gImdlbmVfaWQiKSAlPiUKICBEYXRhRnJhbWUoLikKCnJvd25hbWVzKHJvd0RhdGEoc2NlMTB4X3N0ZW0pKSA8LSByb3dEYXRhKHNjZTEweF9zdGVtKSRnZW5lX2lkCmBgYAoKYGBge3J9CnNhdmVSRFMoCiAgc2NlMTB4X3N0ZW0sCiAgZmlsZS5wYXRoKAogICAgZGF0YV9kaXIsCiAgICAicHJlcHJvY2Vzc2VkIiwKICAgICJzY2UxMHhfc3RlbV9maWx0ZXJlZF9maW5hbC5yZHMiCiAgKQopCmBgYAoKCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=