Load data
An object of class Seurat
14491 features across 83353 samples within 2 assays
Active assay: integrated (2000 features)
1 other assay present: RNA
2 dimensional reductions calculated: pca, tsne
Fig. 2b. Astrocytes saline vs LPS
Fig. 2c. Genes statistically enriched in each cluster
Subset of genes used for plotting
# Expression matrix
cells_to_clusters <- data.frame(
cluster = so.astro$integrated_snn_res.0.2
) %>% rownames_to_column("cell")
geneset <- unique(astro.markers.subset$gene)
mat.scale.data <- so.astro@assays$integrated@scale.data[geneset, cells_to_clusters$cell]
cluster_indices <- split(cells_to_clusters$cell, cells_to_clusters$cluster)
cluster_means <- sapply(cluster_indices, function(indices) {
Matrix::rowMeans(mat.scale.data[, indices, drop = FALSE])
})
rownames(cluster_means) <- word(rownames(cluster_means), 2, sep = "\\.")
# Gene cluster color bar annotation
# by cluster order
genes.clusters.cols <- astro.markers.subset %>%
select(gene, cluster) %>%
mutate(gene.full = gene,
gene = word(gene.full, 2, sep = "\\.")) %>%
group_by(gene) %>%
slice_min(n = 1, order_by = cluster) %>%
ungroup() %>%
arrange(cluster) %>%
left_join(., enframe(cluster.cols, name = "cluster", value = "colors"))
rownames(genes.clusters.cols) <- genes.clusters.cols$gene
genes.clusters.cols <- genes.clusters.cols[rownames(cluster_means), ]
# calculated via logFC
genes.clusters.cols.lfc <- astro.markers %>%
mutate(gene.full = gene,
gene = word(gene.full, 2, sep = "\\.")) %>%
filter(gene %in% rownames(cluster_means)) %>%
select(gene, gene.full, cluster, avg_logFC) %>%
arrange(gene, desc(avg_logFC), cluster) %>%
group_by(gene) %>%
slice_max(n = 1, order_by = avg_logFC) %>%
mutate(avg_logFC = NULL) %>%
left_join(., enframe(cluster.cols, name = "cluster", value = "colors")) %>%
as.data.frame()
rownames(genes.clusters.cols.lfc) <- genes.clusters.cols$gene
genes.clusters.cols.lfc <- genes.clusters.cols.lfc[rownames(cluster_means), ]
# calculated via scaled expression values
genes.clusters.cols.scaled <- cluster_means %>%
as.data.frame() %>%
rownames_to_column("gene") %>%
pivot_longer(names_to = "cluster", cols = as.character(clusters), values_to = "expression") %>%
group_by(gene) %>%
slice_max(n = 1, order_by = expression) %>%
select(-expression) %>%
left_join(., enframe(cluster.cols, name = "cluster", value = "colors")) %>%
as.data.frame()
rownames(genes.clusters.cols.scaled) <- genes.clusters.cols.scaled$gene
genes.clusters.cols.scaled <- genes.clusters.cols.scaled[rownames(cluster_means), ]
row.anno.genes <-
rowAnnotation("Cluster genes" = genes.clusters.cols$cluster,
col = list("Cluster genes" = cluster.cols),
show_annotation_name = FALSE
)
# Define the range and breaks
breaks <- seq(0, 1, length.out = 11) # This creates 11 points between 0 and 1
colors <- plasma(11)
# Create a color function that maps the range [0, 1] to the viridis palette
color_mapping <- colorRamp2(breaks, colors)
# Use the adjusted col argument
Heatmap(
cluster_means,
name = "Normal. expression",
col = color_mapping,
cluster_rows = FALSE,
show_row_dend = FALSE,
cluster_columns = FALSE,
column_names_side = "bottom",
row_names_side = "left",
column_names_rot = 0,
column_names_centered = TRUE,
row_names_gp = gpar(fontsize = 10),
left_annotation = row.anno.genes
)
'giveCsparse' is deprecated; setting repr="T" for you
The 'show_progress' argument is deprecated as of v0.3. Use 'verbosity' instead. (in sctransform::vst)Calculating cell attributes from input UMI matrix: log_umi
Variance stabilizing transformation of count matrix of size 18288 by 2511
Model formula is y ~ log_umi
Get Negative Binomial regression parameters per gene
Using 2000 genes, 2511 cells
Found 90 outliers - those will be ignored in fitting/regularization step
Second step: Get residuals using fitted parameters for 18288 genes
Computing corrected count matrix for 18288 genes
Calculating gene attributes
Wall clock passed: Time difference of 1.147075 mins
astrocyte_markers <- c("Aldh1l1", "Slc1a2", "S100b", "Gfap", "Myoc")
cluster_0.module <- list(c("Gm42418", "Arhgap31", "Kcnq1ot1", "Rmst", "Cit", "Gli1", "Ttll3", "Neat1", "Gm26917"))
cluster_1.module <- list(c("Mfge8", "Igfbp2", "Btbd17"))
cluster_2.module <- list(c("Dbi", "Agt", "Nnat", "Ppia", "Tmsb4x", "Scrg1", "Mt3", "Gstm1", "Nkain4", "S100a1"))
cluster_3.module <- list(c("Thrsp", "Actb", "Chchd10", "Hint1", "Mrpl41", "Mpc1", "Camk2n1", "Ndufc2", "Nupr1", "Igfbp2"))
cluster_4.module <- list(c("Lcn2", "Ifitm3", "Timp1", "B2m", "Vim", "Gfap", "Psmb8", "Bst2"))
cluster_5.module <- list(c("Crym", "Prss56", "Slc43a3", "Mgst1", "Slc1a5", "Tuba1a", "Itm2b", "Chil1", "Ptn"))
cluster_6.module <- list(c("Thbs4", "Igfbp5", "Fxyd6", "Gsn", "Fxyd1", "Cd9", "Meg3"))
cluster_7.module <- list(c("Itih3", "Sparc", "Nkx6-2", "Etnppl", "Gria1", "Spon1", "5031439G07Rik"))
cluster_8.module <- list(c("Igtp", "Gm4951", "Ifit3", "Iigp1", "Irgm1", "Tap1", "Gbp2", "Ifit1", "Isg15", "Cxcl10"))
cluster_9.module <- list(c("Zic1", "Slc38a1", "Col1a2", "Kcnk2", "Luzp2", "Mgll", "Zic4", "Hopx"))
for (gene in astrocyte_markers) {
GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat,
features = list(gene),
name = gene)
}
GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_0.module, name = 'Cluster0_ModuleScore')
GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_1.module, name = "Cluster1_ModuleScore")
GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_2.module, name = "Cluster2_ModuleScore")
GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_3.module, name = "Cluster3_ModuleScore")
GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_4.module, name = "Cluster4_ModuleScore")
GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_5.module, name = "Cluster5_ModuleScore")
GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_6.module, name = "Cluster6_ModuleScore")
GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_7.module, name = "Cluster7_ModuleScore")
GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_8.module, name = "Cluster8_ModuleScore")
GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_9.module, name = "Cluster9_ModuleScore")
#module_scores <- [email protected] %>%
# dplyr::select(starts_with("Cluster"))
module_scores <- GSM5026144_S7_Seurat@meta.data %>%
dplyr::select(starts_with("Cluster"), starts_with("Aldh1l1"), starts_with("Slc1a2"),
starts_with("S100b"), starts_with("Gfap"), starts_with("Myoc"))
# Create a new data frame with a column 'V1' for the row names
module_scores_new <- data.frame(V1 = rownames(module_scores), module_scores)
# Now check the result
head(module_scores_new)
[1] 2000 1865 1 3
# Create a raster grob with the rotated image
GSM5026144_S7_he_img <- rasterGrob(img_rotated, interpolate=TRUE)
# Extract the underlying matrix or array from the rasterGrob
img_data <- GSM5026144_S7_he_img$raster
# Get the dimensions
img_dims <- dim(img_data)
img_width <- img_dims[2] # The second element represents the width
img_height <- img_dims[1] # The first element represents the height
# Define the scale factor
scale_factor <- 0.54555374
# Adjust the coordinates
data$V5_scaled <- data$V5 * scale_factor
data$V6_scaled <- data$V6 * scale_factor
# Create the main plot
color_func <- colorRamp2(seq(0, 1, length.out = 5), c("#6256a8", "#8acda3", "#faf6c0", "#f48f54", "#a11944"))
legend_only <- ggplot(data, aes(x = V5_scaled, y = V6_scaled, fill = Myoc1)) +
geom_point(aes(fill = Myoc1), color = NA, stroke = 0.2, shape = 21) +
scale_fill_gradientn(colors = color_func(seq(0, 1, length.out = 100)),
breaks = c(min(data$Myoc1), max(data$Myoc1)),
labels = c("Lo", "Hi"),
guide = "colourbar") +
labs(fill = "") +
theme_minimal()
gt <- ggplotGrob(legend_only)
[38;5;232mRemoved 2511 rows containing missing values (`geom_point()`).[39m
# Extract the legend as its own gtable
legend_gtable <- gtable::gtable_filter(gt, "guide-box")
# Convert the gtable legend to a grob
legend_grob <- grid::grobTree(legend_gtable)
# List to store individual plots
plots <- list()
plots$Legend <- legend_grob
# Create an image-only ggplot
image_only <- ggplot(data, aes(x = V5_scaled, y = V6_scaled, fill = Myoc1)) +
# Use the defined image dimensions
annotation_custom(GSM5026144_S7_he_img,
xmin = 0, xmax = img_width,
ymin = 0, ymax = img_height) +
geom_point(aes(fill = Myoc1), color = NA, stroke = 0.2, shape = 21) +
scale_fill_gradientn(colors = color_func(seq(0, 1, length.out = 100)),
guide = FALSE) + # Removes the legend
theme_minimal() +
theme(
axis.title.x = element_blank(),
axis.title.y = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks.x = element_blank(),
axis.ticks.y = element_blank(),
legend.title = element_blank()
) +
# Adding the "H&E" label to the top-left corner
annotate("text", x = min(data$V5_scaled), y = max(data$V6_scaled),
label = "H&E", hjust = 0, vjust = 1, fontface = "bold", size = 5)
# Add the image-only plot to the beginning of the plots list
plots[[length(plots) + 1]] <- image_only
label_list <- list(
"Aldh1l11" = "Aldh1l1",
"Slc1a21" = "Slc1a2",
"S100b1" = "S100b",
"Gfap1" = "Gfap",
"Myoc1" = "Myoc",
"Cluster0_ModuleScore1" = "0",
"Cluster1_ModuleScore1" = "1",
"Cluster2_ModuleScore1" = "2",
"Cluster3_ModuleScore1" = "3",
"Cluster4_ModuleScore1" = "4",
"Cluster5_ModuleScore1" = "5",
"Cluster6_ModuleScore1" = "6",
"Cluster7_ModuleScore1" = "7",
"Cluster8_ModuleScore1" = "8",
"Cluster9_ModuleScore1" = "9"
)
for (col in names(label_list)) {
p <- ggplot(data, aes(x = V5_scaled, y = V6_scaled, fill = !!sym(col))) +
annotation_custom(GSM5026144_S7_he_img,
xmin = 0, xmax = img_width,
ymin = 0, ymax = img_height) +
geom_point(color = "black", stroke = 0.2, shape = 21) +
scale_fill_gradientn(colors = color_func(seq(0, 1, length.out = 100))) +
theme_minimal() +
theme(
axis.title.x = element_blank(),
axis.title.y = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks.x = element_blank(),
axis.ticks.y = element_blank(),
legend.position = "none"
) +
# Adding the label annotation to the top-left corner
annotate("text", x = min(data$V5_scaled), y = max(data$V6_scaled),
label = label_list[[col]], hjust = 0, vjust = 1, fontface = "bold", size = 5)
plots[[col]] <- p
}
Fig. 3a. Astrocytes saline vs LPS
R version 3.6.2 (2019-12-12)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 10 (buster)
Matrix products: default
BLAS/LAPACK: /usr/lib/x86_64-linux-gnu/libopenblasp-r0.3.5.so
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8
[4] LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8 LC_MESSAGES=C
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C LC_ADDRESS=C
[10] LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] parallel stats4 grid stats graphics grDevices utils datasets
[9] methods base
other attached packages:
[1] ComplexHeatmap_2.2.0 UpSetR_1.4.0 muscat_1.0.1
[4] scater_1.14.6 SingleCellExperiment_1.8.0 SummarizedExperiment_1.16.1
[7] DelayedArray_0.12.3 BiocParallel_1.20.1 matrixStats_1.0.0
[10] Biobase_2.46.0 GenomicRanges_1.38.0 GenomeInfoDb_1.22.1
[13] IRanges_2.20.2 S4Vectors_0.24.4 BiocGenerics_0.32.0
[16] cowplot_1.1.1 viridis_0.5.1 viridisLite_0.4.2
[19] RColorBrewer_1.1-3 circlize_0.4.8 imager_0.42.1
[22] magrittr_2.0.3 gridExtra_2.3 ggridges_0.5.4
[25] forcats_1.0.0 stringr_1.5.0 dplyr_1.1.3
[28] purrr_1.0.2 readr_2.1.4 tidyr_1.3.0
[31] tibble_3.2.1 ggplot2_3.4.3 tidyverse_1.3.0
[34] Seurat_3.1.0
loaded via a namespace (and not attached):
[1] R.methodsS3_1.8.2 acepack_1.4.2 bit64_4.0.5
[4] knitr_1.43 irlba_2.3.5.1 multcomp_1.4-25
[7] R.utils_2.12.2 data.table_1.14.8 rpart_4.1-15
[10] RCurl_1.98-1.12 doParallel_1.0.17 generics_0.1.3
[13] metap_1.8 TH.data_1.1-2 RSQLite_2.3.1
[16] RANN_2.6.1 future_1.33.0 bit_4.0.5
[19] tzdb_0.4.0 mutoss_0.1-13 xml2_1.3.5
[22] lubridate_1.9.2 xfun_0.40 jquerylib_0.1.4
[25] hms_1.1.3 evaluate_0.21 progress_1.2.2
[28] fansi_1.0.4 caTools_1.18.2 dbplyr_2.3.3
[31] readxl_1.4.3 igraph_1.5.1 DBI_1.1.3
[34] geneplotter_1.64.0 htmlwidgets_1.6.2 backports_1.4.1
[37] annotate_1.64.0 deldir_1.0-9 vctrs_0.6.3
[40] ROCR_1.0-11 cachem_1.0.8 withr_2.5.0
[43] checkmate_2.2.0 sctransform_0.3.5 prettyunits_1.1.1
[46] mnormt_2.1.1 cluster_2.1.0 ape_5.7-1
[49] lazyeval_0.2.2 crayon_1.5.2 genefilter_1.68.0
[52] hdf5r_1.3.0 edgeR_3.28.1 pkgconfig_2.0.3
[55] labeling_0.4.3 qqconf_1.1.1 nlme_3.1-142
[58] vipor_0.4.5 blme_1.0-5 nnet_7.3-12
[61] rlang_1.1.1 globals_0.16.2 lifecycle_1.0.3
[64] sandwich_3.0-2 mathjaxr_1.6-0 modelr_0.1.11
[67] rsvd_1.0.3 cellranger_1.1.0 bmp_0.3
[70] lmtest_0.9-40 tiff_0.1-5 Matrix_1.6-1
[73] boot_1.3-23 zoo_1.8-12 reprex_2.0.2
[76] base64enc_0.1-3 beeswarm_0.4.0 GlobalOptions_0.1.2
[79] png_0.1-8 rjson_0.2.9 bitops_1.0-7
[82] R.oo_1.25.0 KernSmooth_2.23-16 blob_1.2.4
[85] DelayedMatrixStats_1.8.0 shape_1.4.6 parallelly_1.36.0
[88] jpeg_0.1-10 scales_1.2.1 memoise_2.0.1
[91] plyr_1.8.8 ica_1.0-3 gplots_3.1.3
[94] zlibbioc_1.32.0 compiler_3.6.2 plotrix_3.8-2
[97] clue_0.3-64 lme4_1.1-34 DESeq2_1.26.0
[100] fitdistrplus_1.1-11 cli_3.6.1 XVector_0.26.0
[103] lmerTest_3.1-3 listenv_0.9.0 TMB_1.9.6
[106] pbapply_1.7-2 htmlTable_2.4.1 Formula_1.2-5
[109] MASS_7.3-51.4 tidyselect_1.2.0 stringi_1.7.12
[112] yaml_2.3.7 BiocSingular_1.2.2 locfit_1.5-9.4
[115] latticeExtra_0.6-30 ggrepel_0.9.3 sass_0.4.7
[118] tools_3.6.2 timechange_0.2.0 future.apply_1.11.0
[121] rstudioapi_0.15.0 foreach_1.5.2 foreign_0.8-72
[124] farver_2.1.1 Rtsne_0.16 digest_0.6.33
[127] Rcpp_1.0.11 broom_1.0.5 SDMTools_1.1-221.2
[130] RcppAnnoy_0.0.21 httr_1.4.7 readbitmap_0.1.5
[133] AnnotationDbi_1.48.0 Rdpack_2.5 colorspace_2.1-0
[136] rvest_1.0.3 XML_3.99-0.3 fs_1.6.3
[139] reticulate_1.31 splines_3.6.2 uwot_0.1.16
[142] sn_2.1.1 multtest_2.42.0 plotly_4.10.2
[145] xtable_1.8-4 jsonlite_1.8.7 nloptr_2.0.3
[148] R6_2.5.1 TFisher_0.2.0 Hmisc_4.4-0
[151] pillar_1.9.0 htmltools_0.5.6 glue_1.6.2
[154] fastmap_1.1.1 minqa_1.2.5 BiocNeighbors_1.4.2
[157] codetools_0.2-16 tsne_0.1-3.1 mvtnorm_1.2-3
[160] utf8_1.2.3 bslib_0.5.1 lattice_0.20-38
[163] pbkrtest_0.4-8.6 numDeriv_2016.8-1.1 ggbeeswarm_0.7.2
[166] colorRamps_2.3.1 leiden_0.4.3 gtools_3.9.4
[169] interp_1.1-4 glmmTMB_1.1.7 survival_3.1-8
[172] limma_3.42.2 rmarkdown_2.24 munsell_0.5.0
[175] GetoptLong_1.0.5 GenomeInfoDbData_1.2.2 iterators_1.0.14
[178] variancePartition_1.16.1 haven_2.5.3 reshape2_1.4.4
[181] gtable_0.3.4 rbibutils_2.2.15
LS0tCnRpdGxlOiAiQXN0cm9jeXRlcyAtIGNsdXN0ZXIgYW5hbHlzaXMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHllcwogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGhpZ2hsaWdodDoga2F0ZQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDoKICAgICAgdG9jX2NvbGxhcHNlZDogbm8KLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KG11c2NhdCkKbGlicmFyeShwdXJycikKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShVcFNldFIpCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoaW1hZ2VyKQoKCndkIDwtICIvaG9tZS9yc3R1ZGlvL2RhdGEiCm9iamVjdHMuZGlyIDwtICJzYy1ybmFzZXEvb2JqZWN0cy9vYmplY3RzXzIzMTAwNyIKYGBgCgojIExvYWQgZGF0YQoKYGBge3IgbG9hZC1kYXRhfQpzby5hc3RybyA8LSByZWFkUkRTKGZpbGUucGF0aCh3ZCwgb2JqZWN0cy5kaXIsICJTT19hc3Ryb2N5dGVzX2NsdXN0ZXJlZC5yZHMiKSkKCiMgUHJlcGFyZSBhIHZlY3RvciBvZiBjb2xvcnMgZm9yIHRoZSBjbHVzdGVycwpjbHVzdGVycyA8LSBsZXZlbHMoSWRlbnRzKHNvLmFzdHJvKSkgJT4lIGFzLm51bWVyaWMoKQpjbHVzdGVyLmNvbHMgPC0gbXVzY2F0Ojo6LmNsdXN0ZXJfY29sb3JzW3NlcV9hbG9uZyhjbHVzdGVycyldCm5hbWVzKGNsdXN0ZXIuY29scykgPC0gY2x1c3RlcnMKCnNvLmFzdHJvCmBgYAoKYGBge3J9CmFzdHJvLm1hcmtlcnMgPC0gcmVhZFJEUyhmaWxlLnBhdGgod2QsIG9iamVjdHMuZGlyLCAiYXN0cm8ubWFya2Vycy5yZHMiKSkKaGVhZChhc3Ryby5tYXJrZXJzKQpgYGAKCiMgRmlnLiAyYi4gQXN0cm9jeXRlcyBzYWxpbmUgdnMgTFBTCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9CkRpbVBsb3Qoc28uYXN0cm8sIHJlZHVjdGlvbiA9ICJ0c25lIiwgZ3JvdXAuYnkgPSAiaWRlbnQiLCBzcGxpdC5ieSA9ICJncm91cF9pZCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2x1c3Rlci5jb2xzKQpgYGAKCiMgRmlnLiAyYy4gR2VuZXMgc3RhdGlzdGljYWxseSBlbnJpY2hlZCBpbiBlYWNoIGNsdXN0ZXIKCiMjIyBTdWJzZXQgb2YgZ2VuZXMgdXNlZCBmb3IgcGxvdHRpbmcKCmBgYHtyIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD01LCBtZXNzYWdlPUZBTFNFfQphc3Ryby5tYXJrZXJzLnN1YnNldCA8LSBhc3Ryby5tYXJrZXJzICU+JSAKICBncm91cF9ieShjbHVzdGVyKSAlPiUKICBzbGljZV9tYXgobiA9IDEwLCBvcmRlcl9ieSA9IGF2Z19sb2dGQykKYXN0cm8ubWFya2Vycy5zdWJzZXQKYGBgCgpgYGB7cn0KYXN0cm8ubWFya2Vycy5zdWJzZXQgJT4lIAogIHNlbGVjdChnZW5lLCBjbHVzdGVyKSAlPiUgCiAgZ3JvdXBfYnkoZ2VuZSkgJT4lCiAgc2xpY2VfbWluKG4gPSAxLCBvcmRlcl9ieSA9IGNsdXN0ZXIpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGFycmFuZ2UoY2x1c3RlcikKYGBgCgoKYGBge3IgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgRXhwcmVzc2lvbiBtYXRyaXgKY2VsbHNfdG9fY2x1c3RlcnMgPC0gZGF0YS5mcmFtZSgKICBjbHVzdGVyID0gc28uYXN0cm8kaW50ZWdyYXRlZF9zbm5fcmVzLjAuMgopICU+JSByb3duYW1lc190b19jb2x1bW4oImNlbGwiKQoKZ2VuZXNldCA8LSB1bmlxdWUoYXN0cm8ubWFya2Vycy5zdWJzZXQkZ2VuZSkKCm1hdC5zY2FsZS5kYXRhIDwtIHNvLmFzdHJvQGFzc2F5cyRpbnRlZ3JhdGVkQHNjYWxlLmRhdGFbZ2VuZXNldCwgY2VsbHNfdG9fY2x1c3RlcnMkY2VsbF0KCmNsdXN0ZXJfaW5kaWNlcyA8LSBzcGxpdChjZWxsc190b19jbHVzdGVycyRjZWxsLCBjZWxsc190b19jbHVzdGVycyRjbHVzdGVyKQoKY2x1c3Rlcl9tZWFucyA8LSBzYXBwbHkoY2x1c3Rlcl9pbmRpY2VzLCBmdW5jdGlvbihpbmRpY2VzKSB7CiAgTWF0cml4Ojpyb3dNZWFucyhtYXQuc2NhbGUuZGF0YVssIGluZGljZXMsIGRyb3AgPSBGQUxTRV0pCn0pCgpyb3duYW1lcyhjbHVzdGVyX21lYW5zKSA8LSB3b3JkKHJvd25hbWVzKGNsdXN0ZXJfbWVhbnMpLCAyLCBzZXAgPSAiXFwuIikKCiMgR2VuZSBjbHVzdGVyIGNvbG9yIGJhciBhbm5vdGF0aW9uCiAjIGJ5IGNsdXN0ZXIgb3JkZXIKZ2VuZXMuY2x1c3RlcnMuY29scyA8LSBhc3Ryby5tYXJrZXJzLnN1YnNldCAlPiUgCiAgc2VsZWN0KGdlbmUsIGNsdXN0ZXIpICU+JSAKICBtdXRhdGUoZ2VuZS5mdWxsID0gZ2VuZSwKICAgICAgICAgZ2VuZSA9IHdvcmQoZ2VuZS5mdWxsLCAyLCBzZXAgPSAiXFwuIikpICU+JQogIGdyb3VwX2J5KGdlbmUpICU+JQogIHNsaWNlX21pbihuID0gMSwgb3JkZXJfYnkgPSBjbHVzdGVyKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBhcnJhbmdlKGNsdXN0ZXIpICU+JSAKICBsZWZ0X2pvaW4oLiwgZW5mcmFtZShjbHVzdGVyLmNvbHMsIG5hbWUgPSAiY2x1c3RlciIsIHZhbHVlID0gImNvbG9ycyIpKQpyb3duYW1lcyhnZW5lcy5jbHVzdGVycy5jb2xzKSA8LSBnZW5lcy5jbHVzdGVycy5jb2xzJGdlbmUKZ2VuZXMuY2x1c3RlcnMuY29scyA8LSBnZW5lcy5jbHVzdGVycy5jb2xzW3Jvd25hbWVzKGNsdXN0ZXJfbWVhbnMpLCBdCgogIyBjYWxjdWxhdGVkIHZpYSBsb2dGQwpnZW5lcy5jbHVzdGVycy5jb2xzLmxmYyA8LSBhc3Ryby5tYXJrZXJzICU+JQogIG11dGF0ZShnZW5lLmZ1bGwgPSBnZW5lLAogICAgICAgICBnZW5lID0gd29yZChnZW5lLmZ1bGwsIDIsIHNlcCA9ICJcXC4iKSkgJT4lCiAgZmlsdGVyKGdlbmUgJWluJSByb3duYW1lcyhjbHVzdGVyX21lYW5zKSkgJT4lCiAgc2VsZWN0KGdlbmUsIGdlbmUuZnVsbCwgY2x1c3RlciwgYXZnX2xvZ0ZDKSAlPiUKICBhcnJhbmdlKGdlbmUsIGRlc2MoYXZnX2xvZ0ZDKSwgY2x1c3RlcikgJT4lCiAgZ3JvdXBfYnkoZ2VuZSkgJT4lCiAgc2xpY2VfbWF4KG4gPSAxLCBvcmRlcl9ieSA9IGF2Z19sb2dGQykgJT4lCiAgbXV0YXRlKGF2Z19sb2dGQyA9IE5VTEwpICU+JQogIGxlZnRfam9pbiguLCBlbmZyYW1lKGNsdXN0ZXIuY29scywgbmFtZSA9ICJjbHVzdGVyIiwgdmFsdWUgPSAiY29sb3JzIikpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkKcm93bmFtZXMoZ2VuZXMuY2x1c3RlcnMuY29scy5sZmMpIDwtIGdlbmVzLmNsdXN0ZXJzLmNvbHMkZ2VuZQpnZW5lcy5jbHVzdGVycy5jb2xzLmxmYyA8LSBnZW5lcy5jbHVzdGVycy5jb2xzLmxmY1tyb3duYW1lcyhjbHVzdGVyX21lYW5zKSwgXQogCiAjIGNhbGN1bGF0ZWQgdmlhIHNjYWxlZCBleHByZXNzaW9uIHZhbHVlcwpnZW5lcy5jbHVzdGVycy5jb2xzLnNjYWxlZCA8LSBjbHVzdGVyX21lYW5zICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZSIpICU+JSAKICBwaXZvdF9sb25nZXIobmFtZXNfdG8gPSAiY2x1c3RlciIsIGNvbHMgPSBhcy5jaGFyYWN0ZXIoY2x1c3RlcnMpLCB2YWx1ZXNfdG8gPSAiZXhwcmVzc2lvbiIpICU+JSAKICBncm91cF9ieShnZW5lKSAlPiUgCiAgc2xpY2VfbWF4KG4gPSAxLCBvcmRlcl9ieSA9IGV4cHJlc3Npb24pICU+JSAKICBzZWxlY3QoLWV4cHJlc3Npb24pICU+JSAKICBsZWZ0X2pvaW4oLiwgZW5mcmFtZShjbHVzdGVyLmNvbHMsIG5hbWUgPSAiY2x1c3RlciIsIHZhbHVlID0gImNvbG9ycyIpKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCnJvd25hbWVzKGdlbmVzLmNsdXN0ZXJzLmNvbHMuc2NhbGVkKSA8LSBnZW5lcy5jbHVzdGVycy5jb2xzLnNjYWxlZCRnZW5lCmdlbmVzLmNsdXN0ZXJzLmNvbHMuc2NhbGVkIDwtIGdlbmVzLmNsdXN0ZXJzLmNvbHMuc2NhbGVkW3Jvd25hbWVzKGNsdXN0ZXJfbWVhbnMpLCBdCgpyb3cuYW5uby5nZW5lcyA8LQogIHJvd0Fubm90YXRpb24oIkNsdXN0ZXIgZ2VuZXMiID0gZ2VuZXMuY2x1c3RlcnMuY29scyRjbHVzdGVyLAogICAgICAgICAgICAgICAgY29sID0gbGlzdCgiQ2x1c3RlciBnZW5lcyIgPSBjbHVzdGVyLmNvbHMpLAogICAgICAgICAgICAgICAgc2hvd19hbm5vdGF0aW9uX25hbWUgPSBGQUxTRQogICAgICAgICAgICAgICAgKQoKIyBEZWZpbmUgdGhlIHJhbmdlIGFuZCBicmVha3MKYnJlYWtzIDwtIHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gMTEpICAjIFRoaXMgY3JlYXRlcyAxMSBwb2ludHMgYmV0d2VlbiAwIGFuZCAxCgpjb2xvcnMgPC0gcGxhc21hKDExKQoKIyBDcmVhdGUgYSBjb2xvciBmdW5jdGlvbiB0aGF0IG1hcHMgdGhlIHJhbmdlIFswLCAxXSB0byB0aGUgdmlyaWRpcyBwYWxldHRlCmNvbG9yX21hcHBpbmcgPC0gY29sb3JSYW1wMihicmVha3MsIGNvbG9ycykKCiMgVXNlIHRoZSBhZGp1c3RlZCBjb2wgYXJndW1lbnQKSGVhdG1hcCgKICBjbHVzdGVyX21lYW5zLAogIG5hbWUgPSAiTm9ybWFsLiBleHByZXNzaW9uIiwKICBjb2wgPSBjb2xvcl9tYXBwaW5nLAogIGNsdXN0ZXJfcm93cyA9IEZBTFNFLAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwKICBjbHVzdGVyX2NvbHVtbnMgPSBGQUxTRSwKICBjb2x1bW5fbmFtZXNfc2lkZSA9ICJib3R0b20iLAogIHJvd19uYW1lc19zaWRlID0gImxlZnQiLAogIGNvbHVtbl9uYW1lc19yb3QgPSAwLAogIGNvbHVtbl9uYW1lc19jZW50ZXJlZCA9IFRSVUUsCiAgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDEwKSwKICBsZWZ0X2Fubm90YXRpb24gPSByb3cuYW5uby5nZW5lcwopCgoKYGBgCgoKYGBge3J9CkdTTTUwMjYxNDQuZGdDTSA8LSBSZWFkMTBYX2g1KCJ+L2RhdGEvc3BhdGlhbC9TcGFjZVJhbmdlcl9vdXRwdXRzL0dTTTUwMjYxNDRfUzdfZmlsdGVyZWRfZmVhdHVyZV9iY19tYXRyaXguaDUiKQoKR1NNNTAyNjE0NF9TN19TZXVyYXQgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IEdTTTUwMjYxNDQuZGdDTSwgcHJvamVjdCA9ICJHU001MDI2MTQ0X1M3X1NwYXRpYWwiKQoKR1NNNTAyNjE0NF9TN19TZXVyYXQgPC0gU0NUcmFuc2Zvcm0ob2JqZWN0ID0gR1NNNTAyNjE0NF9TN19TZXVyYXQsIHJldHVybi5vbmx5LnZhci5nZW5lcyA9IEYsIHZlcmJvc2UgPSBGQUxTRSkKCmFzdHJvY3l0ZV9tYXJrZXJzIDwtIGMoIkFsZGgxbDEiLCAiU2xjMWEyIiwgIlMxMDBiIiwgIkdmYXAiLCAiTXlvYyIpCgpjbHVzdGVyXzAubW9kdWxlIDwtIGxpc3QoYygiR200MjQxOCIsICJBcmhnYXAzMSIsICJLY25xMW90MSIsICJSbXN0IiwgIkNpdCIsICJHbGkxIiwgIlR0bGwzIiwgIk5lYXQxIiwgIkdtMjY5MTciKSkKY2x1c3Rlcl8xLm1vZHVsZSA8LSBsaXN0KGMoIk1mZ2U4IiwgIklnZmJwMiIsICJCdGJkMTciKSkKY2x1c3Rlcl8yLm1vZHVsZSA8LSBsaXN0KGMoIkRiaSIsICJBZ3QiLCAiTm5hdCIsICJQcGlhIiwgIlRtc2I0eCIsICJTY3JnMSIsICJNdDMiLCAiR3N0bTEiLCAiTmthaW40IiwgIlMxMDBhMSIpKQpjbHVzdGVyXzMubW9kdWxlIDwtIGxpc3QoYygiVGhyc3AiLCAiQWN0YiIsICJDaGNoZDEwIiwgIkhpbnQxIiwgIk1ycGw0MSIsICJNcGMxIiwgIkNhbWsybjEiLCAiTmR1ZmMyIiwgIk51cHIxIiwgIklnZmJwMiIpKQpjbHVzdGVyXzQubW9kdWxlIDwtIGxpc3QoYygiTGNuMiIsICJJZml0bTMiLCAiVGltcDEiLCAiQjJtIiwgIlZpbSIsICJHZmFwIiwgIlBzbWI4IiwgIkJzdDIiKSkKY2x1c3Rlcl81Lm1vZHVsZSA8LSBsaXN0KGMoIkNyeW0iLCAiUHJzczU2IiwgIlNsYzQzYTMiLCAiTWdzdDEiLCAiU2xjMWE1IiwgIlR1YmExYSIsICJJdG0yYiIsICJDaGlsMSIsICJQdG4iKSkKY2x1c3Rlcl82Lm1vZHVsZSA8LSBsaXN0KGMoIlRoYnM0IiwgIklnZmJwNSIsICJGeHlkNiIsICJHc24iLCAiRnh5ZDEiLCAiQ2Q5IiwgIk1lZzMiKSkKY2x1c3Rlcl83Lm1vZHVsZSA8LSBsaXN0KGMoIkl0aWgzIiwgIlNwYXJjIiwgIk5reDYtMiIsICJFdG5wcGwiLCAiR3JpYTEiLCAiU3BvbjEiLCAiNTAzMTQzOUcwN1JpayIpKQpjbHVzdGVyXzgubW9kdWxlIDwtIGxpc3QoYygiSWd0cCIsICJHbTQ5NTEiLCAiSWZpdDMiLCAiSWlncDEiLCAiSXJnbTEiLCAiVGFwMSIsICJHYnAyIiwgIklmaXQxIiwgIklzZzE1IiwgIkN4Y2wxMCIpKQpjbHVzdGVyXzkubW9kdWxlIDwtIGxpc3QoYygiWmljMSIsICJTbGMzOGExIiwgIkNvbDFhMiIsICJLY25rMiIsICJMdXpwMiIsICJNZ2xsIiwgIlppYzQiLCAiSG9weCIpKQoKCmZvciAoZ2VuZSBpbiBhc3Ryb2N5dGVfbWFya2VycykgewogIEdTTTUwMjYxNDRfUzdfU2V1cmF0IDwtIEFkZE1vZHVsZVNjb3JlKG9iamVjdCA9IEdTTTUwMjYxNDRfUzdfU2V1cmF0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGxpc3QoZ2VuZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSBnZW5lKQp9CgpHU001MDI2MTQ0X1M3X1NldXJhdCA8LSBBZGRNb2R1bGVTY29yZShvYmplY3QgPSBHU001MDI2MTQ0X1M3X1NldXJhdCwgZmVhdHVyZXMgPSBjbHVzdGVyXzAubW9kdWxlLCBuYW1lID0gJ0NsdXN0ZXIwX01vZHVsZVNjb3JlJykKCkdTTTUwMjYxNDRfUzdfU2V1cmF0IDwtIEFkZE1vZHVsZVNjb3JlKG9iamVjdCA9IEdTTTUwMjYxNDRfUzdfU2V1cmF0LCBmZWF0dXJlcyA9IGNsdXN0ZXJfMS5tb2R1bGUsIG5hbWUgPSAiQ2x1c3RlcjFfTW9kdWxlU2NvcmUiKQoKR1NNNTAyNjE0NF9TN19TZXVyYXQgPC0gQWRkTW9kdWxlU2NvcmUob2JqZWN0ID0gR1NNNTAyNjE0NF9TN19TZXVyYXQsIGZlYXR1cmVzID0gY2x1c3Rlcl8yLm1vZHVsZSwgbmFtZSA9ICJDbHVzdGVyMl9Nb2R1bGVTY29yZSIpCgpHU001MDI2MTQ0X1M3X1NldXJhdCA8LSBBZGRNb2R1bGVTY29yZShvYmplY3QgPSBHU001MDI2MTQ0X1M3X1NldXJhdCwgZmVhdHVyZXMgPSBjbHVzdGVyXzMubW9kdWxlLCBuYW1lID0gIkNsdXN0ZXIzX01vZHVsZVNjb3JlIikKCkdTTTUwMjYxNDRfUzdfU2V1cmF0IDwtIEFkZE1vZHVsZVNjb3JlKG9iamVjdCA9IEdTTTUwMjYxNDRfUzdfU2V1cmF0LCBmZWF0dXJlcyA9IGNsdXN0ZXJfNC5tb2R1bGUsIG5hbWUgPSAiQ2x1c3RlcjRfTW9kdWxlU2NvcmUiKQoKR1NNNTAyNjE0NF9TN19TZXVyYXQgPC0gQWRkTW9kdWxlU2NvcmUob2JqZWN0ID0gR1NNNTAyNjE0NF9TN19TZXVyYXQsIGZlYXR1cmVzID0gY2x1c3Rlcl81Lm1vZHVsZSwgbmFtZSA9ICJDbHVzdGVyNV9Nb2R1bGVTY29yZSIpCgpHU001MDI2MTQ0X1M3X1NldXJhdCA8LSBBZGRNb2R1bGVTY29yZShvYmplY3QgPSBHU001MDI2MTQ0X1M3X1NldXJhdCwgZmVhdHVyZXMgPSBjbHVzdGVyXzYubW9kdWxlLCBuYW1lID0gIkNsdXN0ZXI2X01vZHVsZVNjb3JlIikKCkdTTTUwMjYxNDRfUzdfU2V1cmF0IDwtIEFkZE1vZHVsZVNjb3JlKG9iamVjdCA9IEdTTTUwMjYxNDRfUzdfU2V1cmF0LCBmZWF0dXJlcyA9IGNsdXN0ZXJfNy5tb2R1bGUsIG5hbWUgPSAiQ2x1c3RlcjdfTW9kdWxlU2NvcmUiKQoKR1NNNTAyNjE0NF9TN19TZXVyYXQgPC0gQWRkTW9kdWxlU2NvcmUob2JqZWN0ID0gR1NNNTAyNjE0NF9TN19TZXVyYXQsIGZlYXR1cmVzID0gY2x1c3Rlcl84Lm1vZHVsZSwgbmFtZSA9ICJDbHVzdGVyOF9Nb2R1bGVTY29yZSIpCgpHU001MDI2MTQ0X1M3X1NldXJhdCA8LSBBZGRNb2R1bGVTY29yZShvYmplY3QgPSBHU001MDI2MTQ0X1M3X1NldXJhdCwgZmVhdHVyZXMgPSBjbHVzdGVyXzkubW9kdWxlLCBuYW1lID0gIkNsdXN0ZXI5X01vZHVsZVNjb3JlIikKCiNtb2R1bGVfc2NvcmVzIDwtIEdTTTUwMjYxNDRfUzdfU2V1cmF0QG1ldGEuZGF0YSAlPiUKIyBkcGx5cjo6c2VsZWN0KHN0YXJ0c193aXRoKCJDbHVzdGVyIikpCgptb2R1bGVfc2NvcmVzIDwtIEdTTTUwMjYxNDRfUzdfU2V1cmF0QG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6c2VsZWN0KHN0YXJ0c193aXRoKCJDbHVzdGVyIiksIHN0YXJ0c193aXRoKCJBbGRoMWwxIiksIHN0YXJ0c193aXRoKCJTbGMxYTIiKSwgCiAgICAgICAgICAgICAgICBzdGFydHNfd2l0aCgiUzEwMGIiKSwgc3RhcnRzX3dpdGgoIkdmYXAiKSwgc3RhcnRzX3dpdGgoIk15b2MiKSkKCiMgQ3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgd2l0aCBhIGNvbHVtbiAnVjEnIGZvciB0aGUgcm93IG5hbWVzCm1vZHVsZV9zY29yZXNfbmV3IDwtIGRhdGEuZnJhbWUoVjEgPSByb3duYW1lcyhtb2R1bGVfc2NvcmVzKSwgbW9kdWxlX3Njb3JlcykKCiMgTm93IGNoZWNrIHRoZSByZXN1bHQKaGVhZChtb2R1bGVfc2NvcmVzX25ldykKCgoKc3BhdGlhbF9jb29yZHMgPC0gcmVhZC5jc3YoIn4vZGF0YS9zcGF0aWFsL1NwYWNlUmFuZ2VyX291dHB1dHMvR1NNNTAyNjE0NF90aXNzdWVfcG9zaXRpb25zX2xpc3QuY3N2Lmd6IiwgaGVhZGVyID0gRkFMU0UpCgoKCmRhdGEgPC0gZHBseXI6OmlubmVyX2pvaW4oc3BhdGlhbF9jb29yZHMsIG1vZHVsZV9zY29yZXNfbmV3LCBieSA9ICJWMSIpCgppbWcgPC0gaW1hZ2VyOjpsb2FkLmltYWdlKCJ+L0dTTTUwMjYxNDRfUzdfdGlzc3VlX2hpcmVzX2ltYWdlLnBuZyIpCgppbWdfcm90YXRlZCA8LSBpbWFnZXI6Omltcm90YXRlKGltZywgLTkwKSAgIyBSb3RhdGVzIHRoZSBpbWFnZSA5MCBkZWdyZWVzIGNvdW50ZXJjbG9ja3dpc2UKCmRpbShpbWdfcm90YXRlZCkKCiMgQ3JlYXRlIGEgcmFzdGVyIGdyb2Igd2l0aCB0aGUgcm90YXRlZCBpbWFnZQpHU001MDI2MTQ0X1M3X2hlX2ltZyA8LSByYXN0ZXJHcm9iKGltZ19yb3RhdGVkLCBpbnRlcnBvbGF0ZT1UUlVFKQoKIyBFeHRyYWN0IHRoZSB1bmRlcmx5aW5nIG1hdHJpeCBvciBhcnJheSBmcm9tIHRoZSByYXN0ZXJHcm9iCmltZ19kYXRhIDwtIEdTTTUwMjYxNDRfUzdfaGVfaW1nJHJhc3RlcgoKIyBHZXQgdGhlIGRpbWVuc2lvbnMKaW1nX2RpbXMgPC0gZGltKGltZ19kYXRhKQppbWdfd2lkdGggPC0gaW1nX2RpbXNbMl0gICMgVGhlIHNlY29uZCBlbGVtZW50IHJlcHJlc2VudHMgdGhlIHdpZHRoCmltZ19oZWlnaHQgPC0gaW1nX2RpbXNbMV0gICMgVGhlIGZpcnN0IGVsZW1lbnQgcmVwcmVzZW50cyB0aGUgaGVpZ2h0CgojIERlZmluZSB0aGUgc2NhbGUgZmFjdG9yCnNjYWxlX2ZhY3RvciA8LSAwLjU0NTU1Mzc0CgojIEFkanVzdCB0aGUgY29vcmRpbmF0ZXMKZGF0YSRWNV9zY2FsZWQgPC0gZGF0YSRWNSAqIHNjYWxlX2ZhY3RvcgpkYXRhJFY2X3NjYWxlZCA8LSBkYXRhJFY2ICogc2NhbGVfZmFjdG9yCgojIENyZWF0ZSB0aGUgbWFpbiBwbG90Cgpjb2xvcl9mdW5jIDwtIGNvbG9yUmFtcDIoc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSA1KSwgYygiIzYyNTZhOCIsICIjOGFjZGEzIiwgIiNmYWY2YzAiLCAiI2Y0OGY1NCIsICIjYTExOTQ0IikpCgpsZWdlbmRfb25seSA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBWNV9zY2FsZWQsIHkgPSBWNl9zY2FsZWQsIGZpbGwgPSBNeW9jMSkpICsKICBnZW9tX3BvaW50KGFlcyhmaWxsID0gTXlvYzEpLCBjb2xvciA9IE5BLCBzdHJva2UgPSAwLjIsIHNoYXBlID0gMjEpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvcnMgPSBjb2xvcl9mdW5jKHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gMTAwKSksIAogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMobWluKGRhdGEkTXlvYzEpLCBtYXgoZGF0YSRNeW9jMSkpLAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkxvIiwgIkhpIiksCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSAiY29sb3VyYmFyIikgKwogIGxhYnMoZmlsbCA9ICIiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpndCA8LSBnZ3Bsb3RHcm9iKGxlZ2VuZF9vbmx5KQoKIyBFeHRyYWN0IHRoZSBsZWdlbmQgYXMgaXRzIG93biBndGFibGUKbGVnZW5kX2d0YWJsZSA8LSBndGFibGU6Omd0YWJsZV9maWx0ZXIoZ3QsICJndWlkZS1ib3giKQoKIyBDb252ZXJ0IHRoZSBndGFibGUgbGVnZW5kIHRvIGEgZ3JvYgpsZWdlbmRfZ3JvYiA8LSBncmlkOjpncm9iVHJlZShsZWdlbmRfZ3RhYmxlKQoKCiMgTGlzdCB0byBzdG9yZSBpbmRpdmlkdWFsIHBsb3RzCnBsb3RzIDwtIGxpc3QoKQoKcGxvdHMkTGVnZW5kIDwtIGxlZ2VuZF9ncm9iCgojIENyZWF0ZSBhbiBpbWFnZS1vbmx5IGdncGxvdAppbWFnZV9vbmx5IDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IFY1X3NjYWxlZCwgeSA9IFY2X3NjYWxlZCwgZmlsbCA9IE15b2MxKSkgKwogICMgVXNlIHRoZSBkZWZpbmVkIGltYWdlIGRpbWVuc2lvbnMKICBhbm5vdGF0aW9uX2N1c3RvbShHU001MDI2MTQ0X1M3X2hlX2ltZywgCiAgICAgICAgICAgICAgICAgICAgeG1pbiA9IDAsIHhtYXggPSBpbWdfd2lkdGgsIAogICAgICAgICAgICAgICAgICAgIHltaW4gPSAwLCB5bWF4ID0gaW1nX2hlaWdodCkgKwogIGdlb21fcG9pbnQoYWVzKGZpbGwgPSBNeW9jMSksIGNvbG9yID0gTkEsIHN0cm9rZSA9IDAuMiwgc2hhcGUgPSAyMSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG9ycyA9IGNvbG9yX2Z1bmMoc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSAxMDApKSwgCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBGQUxTRSkgKyAgIyBSZW1vdmVzIHRoZSBsZWdlbmQKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKQogICkgKwogICMgQWRkaW5nIHRoZSAiSCZFIiBsYWJlbCB0byB0aGUgdG9wLWxlZnQgY29ybmVyCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWluKGRhdGEkVjVfc2NhbGVkKSwgeSA9IG1heChkYXRhJFY2X3NjYWxlZCksIAogICAgICAgICAgIGxhYmVsID0gIkgmRSIsIGhqdXN0ID0gMCwgdmp1c3QgPSAxLCBmb250ZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDUpCgoKIyBBZGQgdGhlIGltYWdlLW9ubHkgcGxvdCB0byB0aGUgYmVnaW5uaW5nIG9mIHRoZSBwbG90cyBsaXN0CnBsb3RzW1tsZW5ndGgocGxvdHMpICsgMV1dIDwtIGltYWdlX29ubHkKCmxhYmVsX2xpc3QgPC0gbGlzdCgKICAiQWxkaDFsMTEiID0gIkFsZGgxbDEiLAogICJTbGMxYTIxIiA9ICJTbGMxYTIiLAogICJTMTAwYjEiID0gIlMxMDBiIiwKICAiR2ZhcDEiID0gIkdmYXAiLAogICJNeW9jMSIgPSAiTXlvYyIsCiAgIkNsdXN0ZXIwX01vZHVsZVNjb3JlMSIgPSAiMCIsCiAgIkNsdXN0ZXIxX01vZHVsZVNjb3JlMSIgPSAiMSIsCiAgIkNsdXN0ZXIyX01vZHVsZVNjb3JlMSIgPSAiMiIsCiAgIkNsdXN0ZXIzX01vZHVsZVNjb3JlMSIgPSAiMyIsCiAgIkNsdXN0ZXI0X01vZHVsZVNjb3JlMSIgPSAiNCIsCiAgIkNsdXN0ZXI1X01vZHVsZVNjb3JlMSIgPSAiNSIsCiAgIkNsdXN0ZXI2X01vZHVsZVNjb3JlMSIgPSAiNiIsCiAgIkNsdXN0ZXI3X01vZHVsZVNjb3JlMSIgPSAiNyIsCiAgIkNsdXN0ZXI4X01vZHVsZVNjb3JlMSIgPSAiOCIsCiAgIkNsdXN0ZXI5X01vZHVsZVNjb3JlMSIgPSAiOSIKKQoKZm9yIChjb2wgaW4gbmFtZXMobGFiZWxfbGlzdCkpIHsKICBwIDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IFY1X3NjYWxlZCwgeSA9IFY2X3NjYWxlZCwgZmlsbCA9ICEhc3ltKGNvbCkpKSArCiAgICBhbm5vdGF0aW9uX2N1c3RvbShHU001MDI2MTQ0X1M3X2hlX2ltZywgCiAgICAgICAgICAgICAgICAgICAgICB4bWluID0gMCwgeG1heCA9IGltZ193aWR0aCwgCiAgICAgICAgICAgICAgICAgICAgICB5bWluID0gMCwgeW1heCA9IGltZ19oZWlnaHQpICsKICAgIGdlb21fcG9pbnQoY29sb3IgPSAiYmxhY2siLCBzdHJva2UgPSAwLjIsIHNoYXBlID0gMjEpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG9ycyA9IGNvbG9yX2Z1bmMoc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSAxMDApKSkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIHRoZW1lKAogICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiICAKICAgICkgKwogICAgIyBBZGRpbmcgdGhlIGxhYmVsIGFubm90YXRpb24gdG8gdGhlIHRvcC1sZWZ0IGNvcm5lcgogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWluKGRhdGEkVjVfc2NhbGVkKSwgeSA9IG1heChkYXRhJFY2X3NjYWxlZCksIAogICAgICAgICAgICAgbGFiZWwgPSBsYWJlbF9saXN0W1tjb2xdXSwgaGp1c3QgPSAwLCB2anVzdCA9IDEsIGZvbnRmYWNlID0gImJvbGQiLCBzaXplID0gNSkKICAKICBwbG90c1tbY29sXV0gPC0gcAp9CmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xMC44MywgZmlnLmhlaWdodD05LjQ5NX0KIyBBcnJhbmdlIHBsb3RzIGluIGEgZ3JpZApncmlkLmFycmFuZ2UoZ3JvYnMgPSBwbG90cywgbmNvbCA9IDUpCmBgYAoKIyBGaWcuIDNhLiBBc3Ryb2N5dGVzIHNhbGluZSB2cyBMUFMKCmBgYHtyfQpEaW1QbG90KHNvLmFzdHJvLCBncm91cC5ieSA9ICJpbnRlZ3JhdGVkX3Nubl9yZXMuMC4yIiwgc3BsaXQuYnkgPSAidHJlYXRtZW50IikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjbHVzdGVyLmNvbHMpCmBgYAoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgoKCgoK