Load data and libraries

##################
# LOAD LIBRARIES #
##################
library(tidyverse)
library(Seurat)
library(tidyseurat)
library(grid)
library(cowplot)
library(harmony)
source("../bin/plotting_functions.R")

#########
# PATHS #
#########
input_dir <- "../results/01_QC_st_data/"
result_dir <- "../results/02_integrate_st_data/"
if( isFALSE(dir.exists(result_dir)) ) { dir.create(result_dir,recursive = TRUE) }

#############
# LODA DATA #
#############
DATA <- readRDS(paste0(input_dir,"seuratObj_filtered.RDS"))

Identify Highly Variable Genes (HVG) across samples

################################
# SPLIT INTO SEPERATE DATASETS #
################################
DATA_nested <- DATA %>%
  mutate(batch = orig.ident) %>%
  nest(data = -batch) %>%
  mutate(data = imap(
    data, ~ .x %>%
      NormalizeData(., normalization.method = "LogNormalize", 
                    verbose = FALSE) %>%
      FindVariableFeatures(selection.method = "vst", 
                           nfeatures = 2000, 
                           verbose = FALSE) )) %>%
  mutate(data = setNames(.[["data"]], .$batch))

#########################################
# FIND HIGLY VARIABLE GENES PER DATASET #
#########################################
hvgs_heat <- DATA_nested %>%
  .$data %>%
  map(., ~ .x@assays$RNA@var.features) %>%
  
  # Flatten and collect all HVGs across samples into one vector 'hvgs_all'
  ( function(x){unique(unlist(x)) ->> hvgs_all; return(x)} ) %>%
  
  # Find intersection of HVGs across samples 
  ( function(x){Reduce(intersect, x) ->> hvgs; return(x)} ) %>% 
  
  # Create a df where each column represents a sample, and each row a gene.
  # The value is TRUE/FALSE depending on whether that gene is an HVG in that sample.
  imap_dfc(., ~hvgs_all %in% .x, .id=.y) %>%
  mutate(rownames = hvgs_all) %>%
  column_to_rownames(var = "rownames")

# choose the hvg present in at least two samples:
hig_var <- rownames(hvgs_heat)[rowSums(hvgs_heat)>2]

# remove all VDJ-genes from list of HVG
remove <- str_subset(hig_var, "^IGH|^IGK|^IGL|^TRA|^TRB|^TRD|^TRG")
hig_var <- setdiff(hig_var, remove)
length(hig_var)
## [1] 4639

Heatmap of HVG in all samples

ph <- pheatmap::pheatmap(
  t(hvgs_heat * 1),
  fontsize_col = 5,
  breaks = c(-0.5, 0.5, 1.5),  
  legend_breaks = c(0, 1),                   # what values to show in legend
  legend_labels = c("No", "Yes"),            # how to label them
  cluster_rows = FALSE, silent = T,
  color = c("grey90", "grey20"),
)

###############################
# SHOW N'TH COLUMN LABELS #
###############################
# Extract column order
col_order <- ph$tree_col$order
labels_ordered <- ph$tree_col$labels[col_order]

# Thin labels: only 1 in 100
labels_thinned <- ifelse(seq_along(labels_ordered) %% 100 == 0, ph$tree_col$labels, "")  # show every 100th label

# Replace column labels in the gtable (this actually affects the plot)
ph[["gtable"]][["grobs"]][[3]][["label"]] <- labels_thinned

# Draw the modified heatmap
#grid.newpage()
grid.draw(ph$gtable)
Showing every 100th column label of the heatmap

Showing every 100th column label of the heatmap

Integration

############
# HARMONY #
###########
DATA <- DATA %>%
  # SCTransform(verbose = FALSE, ) %>%
  NormalizeData(verbose = FALSE) %>%
  FindVariableFeatures(selection.method = "vst",
                      nfeatures = 4000,
                      verbose = FALSE) %>%
  ScaleData(verbose = FALSE, features = hig_var ) %>%
  RunPCA(verbose = FALSE, npcs = 50) %>%
  RunUMAP(dims = 1:50,
          n.components = 2L,
          n.neighbors = 10,
          min.dist = .1,
          spread = .3) 

DATA <- DATA %>%
  RunHarmony(group.by.vars = "orig.ident", 
             reduction.use = "pca",
             dims.use = 1:50, 
             assay.use = "RNA") #%>%

DATA <-   DATA %>%
  RunUMAP(dims = 1:50, 
          n.neighbors = 10,
          min.dist = .1,
          spread = 1,
          repulsion.strength = 1,
          negative.sample.rate = 10,
          n.epochs = 100,
          reduction = "harmony",
          reduction.name = "umapharmony")

Alternative graph based UMAP

integrated <- DATA@reductions$harmony@cell.embeddings
ann <- RcppHNSW::hnsw_build(as.matrix(integrated), distance = "cosine")
knn <- RcppHNSW::hnsw_search(as.matrix(integrated) , ann = ann , k = 15)

UU2 <- uwot::umap(X = NULL,
                 nn_method =  knn,
                 n_components = 2,
                 ret_extra = c("model","fgraph"),
                 verbose = T,
                 min_dist = 0.1,
                 spread = .3,
                 repulsion_strength = 1,
                 negative_sample_rate = 10,
                 n_epochs = 150,
                 n_threads = 8)
dimnames(UU2$embedding) <- list(colnames(DATA),paste0("umap_harmony_knn_", 1:2))
DATA@reductions[["umap_harmony_knn"]] <- CreateDimReducObject(embeddings = UU2$embedding, 
                                                              key = "umap_harmony_knn_")
colnames(DATA@reductions$umap_harmony_knn@cell.embeddings) <- paste0("umap_harmony_knn_", 1:2)
res <- c("umapharmony", "umap_harmony_knn")
p <- map(res, ~plot_clusters.fun(DATA, red=.x, cluster="orig.ident", lable=FALSE, txt_size = 7))
plot_grid(ncol = 2, 
          plotlist = p)

Plot before and after integration

#  dev.new(height=6, width=6.6929133858, noRStudioGD = TRUE)
res <- c("PC", "harmony", "UMAP", "umapharmony")
title <- c("PCA raw data", "PCA Harmony integrated", "UMAP raw data", "UMAP Harmony integrated")
p <- map2(res, title, 
          ~plot_clusters.fun(DATA, 
                             cluster="orig.ident", txt_size = 9,
                             red=.x, lable=FALSE, title=.y))
plot_grid(ncol = 2, 
         plotlist = p)

Plot marker genes

#  dev.new(height=3, width=8, noRStudioGD = TRUE)
################################
# VISUALIZE EXPR. OF KEY GENES #
################################
# col <- c("grey90","grey80","grey60","navy","black")
col <- c("#FFF5F0", "#FEE0D2", "#FCBBA1", "#FC9272", "#FB6A4A", "#EF3B2C", "#CB181D", "#A50F15", "#67000D")
genes <- c("KRT1", "KRT15", "CDH1")
# genes <- c("CD8A", "SFRP2", "CD3E")
# genes <- c("CD8A", "MYOZ2", "CD3E", "EPCAM", "COL6A1", "CD4")

p <- map(genes, ~plot_genes.fun(DATA, .x, col = col, lable = FALSE, red="umapharmony"))
plot_grid(ncol = 3, 
          plotlist = p)

Save seurat object

##################################
# SAVE INTERMEDIATE SEURAT OJECT #
##################################
saveRDS(DATA, paste0(result_dir,"seuratObj_integrated.RDS"))
# DATA <- readRDS(paste0(result_dir,"seuratObj_integrated.RDS"))

Session info

sessionInfo()
## R version 4.3.3 (2024-02-29)
## Platform: x86_64-apple-darwin13.4.0 (64-bit)
## Running under: macOS Big Sur ... 10.16
## 
## Matrix products: default
## BLAS/LAPACK: /Users/vilkal/Applications/miniconda3/envs/hdWGCNA/lib/libopenblasp-r0.3.27.dylib;  LAPACK version 3.12.0
## 
## locale:
## [1] sv_SE.UTF-8/sv_SE.UTF-8/sv_SE.UTF-8/C/sv_SE.UTF-8/sv_SE.UTF-8
## 
## time zone: Europe/Stockholm
## tzcode source: system (macOS)
## 
## attached base packages:
## [1] grid      stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] harmony_1.2.0      Rcpp_1.0.12        cowplot_1.1.3      tidyseurat_0.8.0  
##  [5] ttservice_0.4.1    SeuratObject_5.0.2 Seurat_4.4.0       lubridate_1.9.3   
##  [9] forcats_1.0.0      stringr_1.5.1      dplyr_1.1.4        purrr_1.0.2       
## [13] readr_2.1.5        tidyr_1.3.1        tibble_3.2.1       ggplot2_3.5.2     
## [17] tidyverse_2.0.0   
## 
## loaded via a namespace (and not attached):
##   [1] RColorBrewer_1.1-3     rstudioapi_0.16.0      jsonlite_1.8.8        
##   [4] magrittr_2.0.3         spatstat.utils_3.0-5   farver_2.1.2          
##   [7] rmarkdown_2.27         fs_1.6.4               vctrs_0.6.5           
##  [10] ROCR_1.0-11            spatstat.explore_3.2-7 htmltools_0.5.8.1     
##  [13] sass_0.4.9             sctransform_0.4.1      parallelly_1.37.1     
##  [16] KernSmooth_2.23-24     bslib_0.7.0            htmlwidgets_1.6.4     
##  [19] ica_1.0-3              plyr_1.8.9             plotly_4.10.4         
##  [22] zoo_1.8-12             cachem_1.1.0           igraph_2.0.3          
##  [25] mime_0.12              lifecycle_1.0.4        pkgconfig_2.0.3       
##  [28] Matrix_1.6-5           R6_2.5.1               fastmap_1.2.0         
##  [31] fitdistrplus_1.1-11    future_1.33.2          shiny_1.8.1.1         
##  [34] digest_0.6.36          colorspace_2.1-0       patchwork_1.2.0       
##  [37] tensor_1.5             irlba_2.3.5.1          labeling_0.4.3        
##  [40] progressr_0.14.0       fansi_1.0.6            spatstat.sparse_3.1-0 
##  [43] timechange_0.3.0       httr_1.4.7             polyclip_1.10-6       
##  [46] abind_1.4-5            compiler_4.3.3         withr_3.0.0           
##  [49] highr_0.11             MASS_7.3-60.0.1        tools_4.3.3           
##  [52] lmtest_0.9-40          httpuv_1.6.15          future.apply_1.11.2   
##  [55] goftest_1.2-3          glue_1.8.0             nlme_3.1-165          
##  [58] promises_1.3.0         Rtsne_0.17             cluster_2.1.6         
##  [61] reshape2_1.4.4         generics_0.1.3         gtable_0.3.5          
##  [64] spatstat.data_3.1-2    tzdb_0.4.0             data.table_1.15.4     
##  [67] hms_1.1.3              sp_2.1-4               utf8_1.2.4            
##  [70] spatstat.geom_3.2-9    RcppAnnoy_0.0.22       ggrepel_0.9.5         
##  [73] RANN_2.6.1             pillar_1.9.0           RcppHNSW_0.6.0        
##  [76] spam_2.10-0            later_1.3.2            splines_4.3.3         
##  [79] lattice_0.22-6         survival_3.7-0         deldir_2.0-4          
##  [82] tidyselect_1.2.1       miniUI_0.1.1.1         pbapply_1.7-2         
##  [85] knitr_1.47             gridExtra_2.3          scattermore_1.2       
##  [88] RhpcBLASctl_0.23-42    xfun_0.49              matrixStats_1.3.0     
##  [91] pheatmap_1.0.12        stringi_1.8.4          lazyeval_0.2.2        
##  [94] yaml_2.3.8             evaluate_0.24.0        codetools_0.2-20      
##  [97] cli_3.6.3              uwot_0.2.2             xtable_1.8-4          
## [100] reticulate_1.38.0      munsell_0.5.1          jquerylib_0.1.4       
## [103] globals_0.16.3         spatstat.random_3.2-3  png_0.1-8             
## [106] parallel_4.3.3         ellipsis_0.3.2         dotCall64_1.1-1       
## [109] listenv_0.9.1          viridisLite_0.4.2      scales_1.3.0          
## [112] ggridges_0.5.6         leiden_0.4.3.1         rlang_1.1.4
LS0tCnRpdGxlOiAiSW50ZWdyYXRlIHNwYXRpYWwgZGF0YSIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpmb3JtYXQ6CiAgaHRtbDoKICAgIGVtYmVkLXJlc291cmNlczogdHJ1ZQogICAgY29kZS1mb2xkOiBzaG93CnBhcmFtczoKICBmaWcucGF0aDogImByIHBhc3RlMChwYXJhbXMkZmlnLnBhdGgpYCIgIy4vRmlndXJlcy8KZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCi0tLQoKYGBge3IgYmFja2dyb3VuZC1qb2IsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CnNvdXJjZSgiLi4vYmluL3JlbmRlcl93aXRoX2pvYnMuUiIpCgojIHF1YXJ0bwojIHJlbmRlcl9odG1sX3dpdGhfam9iKG91dF9kaXIgPSBsYWJfZGlyKQojIGZzOjpmaWxlX21vdmUocGF0aCA9IGZpbGUsIG5ld19wYXRoID0gcGFzdGUwKGxhYl9kaXIsIGZpbGUpKQoKIyBjdXJyZW50bHkgdXNpbmcgcXVhcnRvIGZvciBnaXRodWIgYW5kIGtuaXRlciBmb3IgaHRtbCBkdSB0byBzb3VyY2UgY29kZSBvcHRpb24gCnJlbmRlcl9naXRfd2l0aF9qb2IoZmlnX3BhdGggPSAiLi9GaWd1cmVzLzAyLyIpCiMgQ2hhbmdlIHRoZSBmaWd1cmUgcGF0aCBmcm9tIC4vRmlndXJlcy8wMy8gdG8gLi4vRmlndXJlcy8wMy86CnN5c3RlbTIoY29tbWFuZCA9ICJzZWQiLCBzdGRvdXQgPSBUUlVFLAogICAgICAgIGFyZ3MgPSBjKCItaSIsICInJyIsIi1lIiwgJ3Mvc3JjPVxcIlxcLi9zcmM9XFwiXFwuXFwuL2cnLAogICAgICAgICAgICAgICAgIHBhc3RlMCgiLi9tZF9maWxlcy8iLCBiYXNlbmFtZSgiLi8wMl9pbnRlZ3JhdGVfc3RfZGF0YS5tZCIpKSkpCgojIGtuaXRlcgprbml0X2h0bWxfd2l0aF9qb2Iob3V0X2RpciA9ICIuLi9sYWJfYm9vay8wMl9pbnRlZ3JhdGVfc3RfZGF0YS8iLCBmaWdfcGF0aCA9ICIuL0ZpZ3VyZXMvMDIvIikKYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGZpZy53aWR0aCA9IDYuNjkyOTEzMzg1OCwKICBmaWcucGF0aCAgICA9IHBhcmFtcyRmaWcucGF0aCwKICBkZXYgICAgICAgICA9IGMoInBuZyIpLAogIGRwaSAgICAgICAgID0gMzAwLAogIGZpZy5hbGlnbiAgID0gImNlbnRlciIsCiAgbWVzc2FnZSAgICAgPSBGQUxTRSwKICB3YXJuaW5nICAgICA9IEZBTFNFLAogIGZpZy5wcm9jZXNzID0gZnVuY3Rpb24oZmlsZW5hbWUpewogICAgICBuZXdfZmlsZW5hbWUgPC0gc3RyaW5ncjo6c3RyX3JlbW92ZShzdHJpbmcgPSBmaWxlbmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIi0xIikKICAgICAgZnM6OmZpbGVfbW92ZShwYXRoID0gZmlsZW5hbWUsIG5ld19wYXRoID0gbmV3X2ZpbGVuYW1lKQogICAgICBpZmVsc2UoZnM6OmZpbGVfZXhpc3RzKG5ld19maWxlbmFtZSksIG5ld19maWxlbmFtZSwgZmlsZW5hbWUpfSkKCiMgc2V0d2QoIn4vd29yay9Ccm9saWRlbnNfd29yay9Qcm9qZWN0cy9TcGF0aWFsX01pY3JvYmlvdGEvc3JjIikKYGBgCgojIyMgTG9hZCBkYXRhIGFuZCBsaWJyYXJpZXMKCmBgYHtyIExvYWQtTGlicmFyeS1hbmQtZGF0YX0KIyMjIyMjIyMjIyMjIyMjIyMjCiMgTE9BRCBMSUJSQVJJRVMgIwojIyMjIyMjIyMjIyMjIyMjIyMKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KHRpZHlzZXVyYXQpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KGhhcm1vbnkpCnNvdXJjZSgiLi4vYmluL3Bsb3R0aW5nX2Z1bmN0aW9ucy5SIikKCiMjIyMjIyMjIwojIFBBVEhTICMKIyMjIyMjIyMjCmlucHV0X2RpciA8LSAiLi4vcmVzdWx0cy8wMV9RQ19zdF9kYXRhLyIKcmVzdWx0X2RpciA8LSAiLi4vcmVzdWx0cy8wMl9pbnRlZ3JhdGVfc3RfZGF0YS8iCmlmKCBpc0ZBTFNFKGRpci5leGlzdHMocmVzdWx0X2RpcikpICkgeyBkaXIuY3JlYXRlKHJlc3VsdF9kaXIscmVjdXJzaXZlID0gVFJVRSkgfQoKIyMjIyMjIyMjIyMjIwojIExPREEgREFUQSAjCiMjIyMjIyMjIyMjIyMKREFUQSA8LSByZWFkUkRTKHBhc3RlMChpbnB1dF9kaXIsInNldXJhdE9ial9maWx0ZXJlZC5SRFMiKSkKCmBgYAoKIyMjIElkZW50aWZ5IEhpZ2hseSBWYXJpYWJsZSBHZW5lcyAoSFZHKSBhY3Jvc3Mgc2FtcGxlcwoKYGBge3IgRmluZC1IVkd9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU1BMSVQgSU5UTyBTRVBFUkFURSBEQVRBU0VUUyAjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCkRBVEFfbmVzdGVkIDwtIERBVEEgJT4lCiAgbXV0YXRlKGJhdGNoID0gb3JpZy5pZGVudCkgJT4lCiAgbmVzdChkYXRhID0gLWJhdGNoKSAlPiUKICBtdXRhdGUoZGF0YSA9IGltYXAoCiAgICBkYXRhLCB+IC54ICU+JQogICAgICBOb3JtYWxpemVEYXRhKC4sIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIAogICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkgJT4lCiAgICAgIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5mZWF0dXJlcyA9IDIwMDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpICkpICU+JQogIG11dGF0ZShkYXRhID0gc2V0TmFtZXMoLltbImRhdGEiXV0sIC4kYmF0Y2gpKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBGSU5EIEhJR0xZIFZBUklBQkxFIEdFTkVTIFBFUiBEQVRBU0VUICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKaHZnc19oZWF0IDwtIERBVEFfbmVzdGVkICU+JQogIC4kZGF0YSAlPiUKICBtYXAoLiwgfiAueEBhc3NheXMkUk5BQHZhci5mZWF0dXJlcykgJT4lCiAgCiAgIyBGbGF0dGVuIGFuZCBjb2xsZWN0IGFsbCBIVkdzIGFjcm9zcyBzYW1wbGVzIGludG8gb25lIHZlY3RvciAnaHZnc19hbGwnCiAgKCBmdW5jdGlvbih4KXt1bmlxdWUodW5saXN0KHgpKSAtPj4gaHZnc19hbGw7IHJldHVybih4KX0gKSAlPiUKICAKICAjIEZpbmQgaW50ZXJzZWN0aW9uIG9mIEhWR3MgYWNyb3NzIHNhbXBsZXMgCiAgKCBmdW5jdGlvbih4KXtSZWR1Y2UoaW50ZXJzZWN0LCB4KSAtPj4gaHZnczsgcmV0dXJuKHgpfSApICU+JSAKICAKICAjIENyZWF0ZSBhIGRmIHdoZXJlIGVhY2ggY29sdW1uIHJlcHJlc2VudHMgYSBzYW1wbGUsIGFuZCBlYWNoIHJvdyBhIGdlbmUuCiAgIyBUaGUgdmFsdWUgaXMgVFJVRS9GQUxTRSBkZXBlbmRpbmcgb24gd2hldGhlciB0aGF0IGdlbmUgaXMgYW4gSFZHIGluIHRoYXQgc2FtcGxlLgogIGltYXBfZGZjKC4sIH5odmdzX2FsbCAlaW4lIC54LCAuaWQ9LnkpICU+JQogIG11dGF0ZShyb3duYW1lcyA9IGh2Z3NfYWxsKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gInJvd25hbWVzIikKCiMgY2hvb3NlIHRoZSBodmcgcHJlc2VudCBpbiBhdCBsZWFzdCB0d28gc2FtcGxlczoKaGlnX3ZhciA8LSByb3duYW1lcyhodmdzX2hlYXQpW3Jvd1N1bXMoaHZnc19oZWF0KT4yXQoKIyByZW1vdmUgYWxsIFZESi1nZW5lcyBmcm9tIGxpc3Qgb2YgSFZHCnJlbW92ZSA8LSBzdHJfc3Vic2V0KGhpZ192YXIsICJeSUdIfF5JR0t8XklHTHxeVFJBfF5UUkJ8XlRSRHxeVFJHIikKaGlnX3ZhciA8LSBzZXRkaWZmKGhpZ192YXIsIHJlbW92ZSkKbGVuZ3RoKGhpZ192YXIpCmBgYAoKIyMjIEhlYXRtYXAgb2YgSFZHIGluIGFsbCBzYW1wbGVzCgpgYGB7ciAwMmFfSFZHX2hlYXRtYXAsIGZpZy53aWR0aD0xMCwgZmlnLmNhcD0iU2hvd2luZyBldmVyeSAxMDB0aCBjb2x1bW4gbGFiZWwgb2YgdGhlIGhlYXRtYXAifQpwaCA8LSBwaGVhdG1hcDo6cGhlYXRtYXAoCiAgdChodmdzX2hlYXQgKiAxKSwKICBmb250c2l6ZV9jb2wgPSA1LAogIGJyZWFrcyA9IGMoLTAuNSwgMC41LCAxLjUpLCAgCiAgbGVnZW5kX2JyZWFrcyA9IGMoMCwgMSksICAgICAgICAgICAgICAgICAgICMgd2hhdCB2YWx1ZXMgdG8gc2hvdyBpbiBsZWdlbmQKICBsZWdlbmRfbGFiZWxzID0gYygiTm8iLCAiWWVzIiksICAgICAgICAgICAgIyBob3cgdG8gbGFiZWwgdGhlbQogIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBzaWxlbnQgPSBULAogIGNvbG9yID0gYygiZ3JleTkwIiwgImdyZXkyMCIpLAopCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU0hPVyBOJ1RIIENPTFVNTiBMQUJFTFMgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRXh0cmFjdCBjb2x1bW4gb3JkZXIKY29sX29yZGVyIDwtIHBoJHRyZWVfY29sJG9yZGVyCmxhYmVsc19vcmRlcmVkIDwtIHBoJHRyZWVfY29sJGxhYmVsc1tjb2xfb3JkZXJdCgojIFRoaW4gbGFiZWxzOiBvbmx5IDEgaW4gMTAwCmxhYmVsc190aGlubmVkIDwtIGlmZWxzZShzZXFfYWxvbmcobGFiZWxzX29yZGVyZWQpICUlIDEwMCA9PSAwLCBwaCR0cmVlX2NvbCRsYWJlbHMsICIiKSAgIyBzaG93IGV2ZXJ5IDEwMHRoIGxhYmVsCgojIFJlcGxhY2UgY29sdW1uIGxhYmVscyBpbiB0aGUgZ3RhYmxlICh0aGlzIGFjdHVhbGx5IGFmZmVjdHMgdGhlIHBsb3QpCnBoW1siZ3RhYmxlIl1dW1siZ3JvYnMiXV1bWzNdXVtbImxhYmVsIl1dIDwtIGxhYmVsc190aGlubmVkCgojIERyYXcgdGhlIG1vZGlmaWVkIGhlYXRtYXAKI2dyaWQubmV3cGFnZSgpCmdyaWQuZHJhdyhwaCRndGFibGUpCmBgYAoKIyMjIEludGVncmF0aW9uCgpgYGB7ciBJbnRlZ3JhdGlvbn0KIyMjIyMjIyMjIyMjCiMgSEFSTU9OWSAjCiMjIyMjIyMjIyMjCkRBVEEgPC0gREFUQSAlPiUKICAjIFNDVHJhbnNmb3JtKHZlcmJvc2UgPSBGQUxTRSwgKSAlPiUKICBOb3JtYWxpemVEYXRhKHZlcmJvc2UgPSBGQUxTRSkgJT4lCiAgRmluZFZhcmlhYmxlRmVhdHVyZXMoc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLAogICAgICAgICAgICAgICAgICAgICAgbmZlYXR1cmVzID0gNDAwMCwKICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkgJT4lCiAgU2NhbGVEYXRhKHZlcmJvc2UgPSBGQUxTRSwgZmVhdHVyZXMgPSBoaWdfdmFyICkgJT4lCiAgUnVuUENBKHZlcmJvc2UgPSBGQUxTRSwgbnBjcyA9IDUwKSAlPiUKICBSdW5VTUFQKGRpbXMgPSAxOjUwLAogICAgICAgICAgbi5jb21wb25lbnRzID0gMkwsCiAgICAgICAgICBuLm5laWdoYm9ycyA9IDEwLAogICAgICAgICAgbWluLmRpc3QgPSAuMSwKICAgICAgICAgIHNwcmVhZCA9IC4zKSAKCkRBVEEgPC0gREFUQSAlPiUKICBSdW5IYXJtb255KGdyb3VwLmJ5LnZhcnMgPSAib3JpZy5pZGVudCIsIAogICAgICAgICAgICAgcmVkdWN0aW9uLnVzZSA9ICJwY2EiLAogICAgICAgICAgICAgZGltcy51c2UgPSAxOjUwLCAKICAgICAgICAgICAgIGFzc2F5LnVzZSA9ICJSTkEiKSAjJT4lCgpEQVRBIDwtICAgREFUQSAlPiUKICBSdW5VTUFQKGRpbXMgPSAxOjUwLCAKICAgICAgICAgIG4ubmVpZ2hib3JzID0gMTAsCiAgICAgICAgICBtaW4uZGlzdCA9IC4xLAogICAgICAgICAgc3ByZWFkID0gMSwKICAgICAgICAgIHJlcHVsc2lvbi5zdHJlbmd0aCA9IDEsCiAgICAgICAgICBuZWdhdGl2ZS5zYW1wbGUucmF0ZSA9IDEwLAogICAgICAgICAgbi5lcG9jaHMgPSAxMDAsCiAgICAgICAgICByZWR1Y3Rpb24gPSAiaGFybW9ueSIsCiAgICAgICAgICByZWR1Y3Rpb24ubmFtZSA9ICJ1bWFwaGFybW9ueSIpCmBgYAoKIyMjIEFsdGVybmF0aXZlIGdyYXBoIGJhc2VkIFVNQVAKCmBgYHtyIGFsdGVybmF0aXZlLXdheS1vZi1VTUFQfQppbnRlZ3JhdGVkIDwtIERBVEFAcmVkdWN0aW9ucyRoYXJtb255QGNlbGwuZW1iZWRkaW5ncwphbm4gPC0gUmNwcEhOU1c6Omhuc3dfYnVpbGQoYXMubWF0cml4KGludGVncmF0ZWQpLCBkaXN0YW5jZSA9ICJjb3NpbmUiKQprbm4gPC0gUmNwcEhOU1c6Omhuc3dfc2VhcmNoKGFzLm1hdHJpeChpbnRlZ3JhdGVkKSAsIGFubiA9IGFubiAsIGsgPSAxNSkKClVVMiA8LSB1d290Ojp1bWFwKFggPSBOVUxMLAogICAgICAgICAgICAgICAgIG5uX21ldGhvZCA9ICBrbm4sCiAgICAgICAgICAgICAgICAgbl9jb21wb25lbnRzID0gMiwKICAgICAgICAgICAgICAgICByZXRfZXh0cmEgPSBjKCJtb2RlbCIsImZncmFwaCIpLAogICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBULAogICAgICAgICAgICAgICAgIG1pbl9kaXN0ID0gMC4xLAogICAgICAgICAgICAgICAgIHNwcmVhZCA9IC4zLAogICAgICAgICAgICAgICAgIHJlcHVsc2lvbl9zdHJlbmd0aCA9IDEsCiAgICAgICAgICAgICAgICAgbmVnYXRpdmVfc2FtcGxlX3JhdGUgPSAxMCwKICAgICAgICAgICAgICAgICBuX2Vwb2NocyA9IDE1MCwKICAgICAgICAgICAgICAgICBuX3RocmVhZHMgPSA4KQpkaW1uYW1lcyhVVTIkZW1iZWRkaW5nKSA8LSBsaXN0KGNvbG5hbWVzKERBVEEpLHBhc3RlMCgidW1hcF9oYXJtb255X2tubl8iLCAxOjIpKQpEQVRBQHJlZHVjdGlvbnNbWyJ1bWFwX2hhcm1vbnlfa25uIl1dIDwtIENyZWF0ZURpbVJlZHVjT2JqZWN0KGVtYmVkZGluZ3MgPSBVVTIkZW1iZWRkaW5nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXkgPSAidW1hcF9oYXJtb255X2tubl8iKQpjb2xuYW1lcyhEQVRBQHJlZHVjdGlvbnMkdW1hcF9oYXJtb255X2tubkBjZWxsLmVtYmVkZGluZ3MpIDwtIHBhc3RlMCgidW1hcF9oYXJtb255X2tubl8iLCAxOjIpCmBgYAoKYGBge3IsIGluY2x1ZGU9RkFMU0V9CkRBVEEgPC1yZWFkUkRTKCIuLi9yZXN1bHRzLzAyX2ludGVncmF0ZV9zdF9kYXRhL3NldXJhdE9ial9pbnRlZ3JhdGVkLlJEUyIpCmBgYAoKYGBge3IsIDAyYl9VTUFQX29wdGlvbnMsIGZpZy5hc3A9NS8xMH0KcmVzIDwtIGMoInVtYXBoYXJtb255IiwgInVtYXBfaGFybW9ueV9rbm4iKQpwIDwtIG1hcChyZXMsIH5wbG90X2NsdXN0ZXJzLmZ1bihEQVRBLCByZWQ9LngsIGNsdXN0ZXI9Im9yaWcuaWRlbnQiLCBsYWJsZT1GQUxTRSwgdHh0X3NpemUgPSA3KSkKcGxvdF9ncmlkKG5jb2wgPSAyLCAKICAgICAgICAgIHBsb3RsaXN0ID0gcCkKYGBgCgojIyMgUGxvdCBiZWZvcmUgYW5kIGFmdGVyIGludGVncmF0aW9uCgpgYGB7ciAwMmNfUGxvdF9kaW1fcmVkdWN0aW9uLCBmaWcuaGVpZ2h0PTZ9CiMgIGRldi5uZXcoaGVpZ2h0PTYsIHdpZHRoPTYuNjkyOTEzMzg1OCwgbm9SU3R1ZGlvR0QgPSBUUlVFKQpyZXMgPC0gYygiUEMiLCAiaGFybW9ueSIsICJVTUFQIiwgInVtYXBoYXJtb255IikKdGl0bGUgPC0gYygiUENBIHJhdyBkYXRhIiwgIlBDQSBIYXJtb255IGludGVncmF0ZWQiLCAiVU1BUCByYXcgZGF0YSIsICJVTUFQIEhhcm1vbnkgaW50ZWdyYXRlZCIpCnAgPC0gbWFwMihyZXMsIHRpdGxlLCAKICAgICAgICAgIH5wbG90X2NsdXN0ZXJzLmZ1bihEQVRBLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyPSJvcmlnLmlkZW50IiwgdHh0X3NpemUgPSA5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZD0ueCwgbGFibGU9RkFMU0UsIHRpdGxlPS55KSkKcGxvdF9ncmlkKG5jb2wgPSAyLCAKICAgICAgICAgcGxvdGxpc3QgPSBwKQpgYGAKCiMjIyBQbG90IG1hcmtlciBnZW5lcwoKYGBge3IgMDJkX3Bsb3RfbWFya2VyX2dlbmVzLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD04fQojICBkZXYubmV3KGhlaWdodD0zLCB3aWR0aD04LCBub1JTdHVkaW9HRCA9IFRSVUUpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgVklTVUFMSVpFIEVYUFIuIE9GIEtFWSBHRU5FUyAjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgY29sIDwtIGMoImdyZXk5MCIsImdyZXk4MCIsImdyZXk2MCIsIm5hdnkiLCJibGFjayIpCmNvbCA8LSBjKCIjRkZGNUYwIiwgIiNGRUUwRDIiLCAiI0ZDQkJBMSIsICIjRkM5MjcyIiwgIiNGQjZBNEEiLCAiI0VGM0IyQyIsICIjQ0IxODFEIiwgIiNBNTBGMTUiLCAiIzY3MDAwRCIpCmdlbmVzIDwtIGMoIktSVDEiLCAiS1JUMTUiLCAiQ0RIMSIpCiMgZ2VuZXMgPC0gYygiQ0Q4QSIsICJTRlJQMiIsICJDRDNFIikKIyBnZW5lcyA8LSBjKCJDRDhBIiwgIk1ZT1oyIiwgIkNEM0UiLCAiRVBDQU0iLCAiQ09MNkExIiwgIkNENCIpCgpwIDwtIG1hcChnZW5lcywgfnBsb3RfZ2VuZXMuZnVuKERBVEEsIC54LCBjb2wgPSBjb2wsIGxhYmxlID0gRkFMU0UsIHJlZD0idW1hcGhhcm1vbnkiKSkKcGxvdF9ncmlkKG5jb2wgPSAzLCAKICAgICAgICAgIHBsb3RsaXN0ID0gcCkKYGBgCgojIyBTYXZlIHNldXJhdCBvYmplY3QKCmBgYHtyIHNhdmUtU2V1cmF0T2JqLCBldmFsPUZBTFNFfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU0FWRSBJTlRFUk1FRElBVEUgU0VVUkFUIE9KRUNUICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpzYXZlUkRTKERBVEEsIHBhc3RlMChyZXN1bHRfZGlyLCJzZXVyYXRPYmpfaW50ZWdyYXRlZC5SRFMiKSkKIyBEQVRBIDwtIHJlYWRSRFMocGFzdGUwKHJlc3VsdF9kaXIsInNldXJhdE9ial9pbnRlZ3JhdGVkLlJEUyIpKQpgYGAKCiMjIyBTZXNzaW9uIGluZm8KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=