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. 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
Removed 2511 rows containing missing values (`geom_point()`).
# 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