Generation of SPRING coordinates for an object previously generated containing WT and Gmnc KO cells from P0.

Load libraries

library(Seurat)
library(cowplot)
library(dplyr)
library(ggplot2)
library(ggExtra)
library(ggrepel)
library(reticulate)
library(Matrix)
library(viridis)
library(RColorBrewer)
library(MetBrewer)
library(wesanderson)
library(R.utils)

# Set ggplot theme as classic
theme_set(theme_classic())

Load the dataset and calculate QC metrics

Gmnc.combined <- readRDS("Gmnc.combined.RDS")

DimPlot(Gmnc.combined, reduction = "umap", label = T, label.size = 4, pt.size = 0.1, group.by = "seurat_clusters",  cols = met.brewer("Klimt", 23)) + NoAxes()

Export files for Spring

Export raw expression matrix and gene list for spring plot generation

ExprsMatrix <- as.matrix(GetAssayData(Gmnc.combined))
exprData <- Matrix(ExprsMatrix, sparse = TRUE)
writeMM(exprData, "Spring_files/ExprData.mtx")
## NULL
gzip("Spring_files/ExprData.mtx", overwrite=T)
Genelist <- row.names(ExprsMatrix)
write.table(Genelist, "Spring_files/Genelist.csv", sep="\t", col.names = F, row.names = F)

rm(ExprsMatrix, exprData, Genelist)

Export continuous metadata

S.Score <- c("S.Score",Gmnc.combined$S.Score)
S.Score <- paste(S.Score, sep=",", collapse=",")
G2M.Score <- c("G2M.Score",Gmnc.combined$G2M.Score)
G2M.Score <- paste(G2M.Score, sep=",", collapse=",")
Percent.mt <- c("Percent.mt", Gmnc.combined$percent.mt)
Percent.mt <- paste(Percent.mt, sep = ",", collapse = ",")
Percent.rb <- c("Percent.rb", Gmnc.combined$percent.rb)
Percent.rb <- paste(Percent.rb, sep = ",", collapse = ",")
nCount <- c("nCount", Gmnc.combined$nCount_RNA)
nCount <- paste(nCount, sep = ",", collapse = ",") 
nFeature <- c("nFeature", Gmnc.combined$nFeature_RNA)
nFeature <- paste(nFeature, sep = ",", collapse = ",")

ColorTrack <- rbind(S.Score, G2M.Score, Percent.mt, Percent.rb, nCount, nFeature)
write.table(ColorTrack, "Spring_files/ColorTrack.csv", quote =F, row.names = F, col.names = F)

rm(S.Score, G2M.Score, Percent.mt, Percent.rb, nCount, nFeature, ColorTrack)

Export discrete metadata

Seurat.clusters <- c("Seurat Clusters", paste0("Cluster",as.character(Gmnc.combined$seurat_clusters)))
Seurat.clusters <- paste(Seurat.clusters, sep=",", collapse=",")
Phase <- c("Phase", Gmnc.combined$Phase)
Phase <- paste(Phase, sep=",", collapse=",")
orig.ident <- c("orig.ident", Gmnc.combined$orig.ident) 
orig.ident <- paste(orig.ident, sep = ",", collapse = ",") 
Cellgrouping <- rbind(Seurat.clusters, Phase, orig.ident)
write.table(Cellgrouping, "Spring_files/Cellgrouping.csv", quote =F, row.names = F, col.names = F)

rm(Cellgrouping, Seurat.clusters, Phase, orig.ident)

Export UMAP coordinates

Coordinates <- cbind(c(0:dim(Gmnc.combined)[2]-1),Gmnc.combined@reductions[["umap"]]@cell.embeddings)

# Symmetry transform of the coordinates
Spring.Sym <- function(x){
  x = abs(max(Coordinates[,3])-x)
 }

Coordinates[,3] <- sapply(Coordinates[,3] , function(x) Spring.Sym(x))

# Change x and y axis values for a better display in SPRING
Coordinates <- Coordinates * 100

write.table(Coordinates, "Spring_files/umap.txt", quote =F, row.names = F, col.names = F, sep = ",")
rm(Coordinates)

ExprData.mtx, Genelist.csv, ColorTrack.csv, Cellgrouping.csv and umap.txt are then used as input for the Spring App

A doublet score is calculated using the SPRING app built-in function (based on scrublet) using default parameters (k=50, r=2, f=0.1)

Import doublet score (scrublet)

doublet.score <- read.table("Spring_files/doublet_results.tsv", header = T)
doublet.score <- filter(doublet.score, Observed_or_Simulated == "Observed")
Gmnc.combined$doublet.score <-doublet.score$Score

FeaturePlot(object = Gmnc.combined,
            features = "doublet.score",
            reduction = "umap",
            pt.size = 0.4) +  scale_color_gradientn(colors= c("grey90", brewer.pal(9,"RdPu"))) + NoAxes()

ggplot(doublet.score, aes(x = Score, stat(ndensity))) +
    geom_histogram(bins = 200, colour ="lightgrey")+
    geom_vline(xintercept = 0.25, colour = "red", linetype = 2)

rm(doublet.score)

Cell coordinates of the Spring dimensionality reduction are generated using the following parameters (excluding cells with a doublet score >0.25): Min expressing cells (gene filtering): 3 Min number of UMIs (gene filtering): 3 Gene variability %ile (gene filtering): 90 Number of principal components: 20 Number of nearest neighbors: 8 Number of force layout iterations: 500

Import Spring coordinates

Spring.Sym <- function(x){x = abs(max(Coordinates[,2])-x)} # Symmetry transform of the coordinates

Coordinates <- read.table("Spring_files/coordinates.txt", sep = ",", header = F)[,c(2,3)]
Gmnc.cleanup <- subset(Gmnc.combined, doublet.score < 0.25)
rownames(Coordinates) <- colnames(Gmnc.cleanup)
colnames(Coordinates) <- paste0("Spring_", 1:2)
Coordinates[,2] <- sapply(Coordinates[,2] , function(x) Spring.Sym(x))
Gmnc.cleanup[["Spring"]] <- CreateDimReducObject(embeddings = as.matrix(Coordinates), key = "Spring_")

rm(Coordinates)

# Spring visualization 
DimPlot(Gmnc.cleanup, reduction = "Spring", pt.size = 0.2, label = T, label.size = 3, cols = met.brewer("Klimt", 23)) + NoAxes() + NoLegend()

DimPlot(Gmnc.cleanup, reduction = "Spring", pt.size = 0.2, label = F, group.by = "orig.ident", cols = met.brewer("Egypt", 2)) + NoAxes() 

Save Seurat objects

saveRDS(Gmnc.cleanup, "Gmnc.cleanup.RDS")

Session Info

#date
format(Sys.time(), "%d %B, %Y, %:%M")
## [1] "09 January, 2024, %:44"
#Packages used
sessionInfo()
## R version 4.1.1 (2021-08-10)
## Platform: x86_64-conda-linux-gnu (64-bit)
## Running under: CentOS Linux 7 (Core)
## 
## Matrix products: default
## BLAS/LAPACK: /shared/ifbstor1/software/miniconda/envs/r-4.1.1/lib/libopenblasp-r0.3.18.so
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] R.utils_2.11.0     R.oo_1.24.0        R.methodsS3_1.8.1  wesanderson_0.3.6 
##  [5] MetBrewer_0.2.0    RColorBrewer_1.1-3 viridis_0.6.2      viridisLite_0.4.1 
##  [9] Matrix_1.6-4       reticulate_1.24    ggrepel_0.9.1      ggExtra_0.9       
## [13] ggplot2_3.3.6      dplyr_1.0.10       cowplot_1.1.1      Seurat_5.0.1      
## [17] SeuratObject_5.0.1 sp_2.1-2          
## 
## loaded via a namespace (and not attached):
##   [1] Rtsne_0.16             colorspace_2.0-3       deldir_1.0-6          
##   [4] ellipsis_0.3.2         ggridges_0.5.3         RcppHNSW_0.3.0        
##   [7] spatstat.data_3.0-3    rstudioapi_0.13        farver_2.1.1          
##  [10] leiden_0.3.10          listenv_0.8.0          RSpectra_0.16-1       
##  [13] fansi_1.0.3            codetools_0.2-18       splines_4.1.1         
##  [16] knitr_1.40             polyclip_1.10-0        spam_2.7-0            
##  [19] jsonlite_1.8.2         ica_1.0-2              cluster_2.1.2         
##  [22] png_0.1-7              uwot_0.1.11            spatstat.sparse_3.0-3 
##  [25] shiny_1.7.1            sctransform_0.4.1      compiler_4.1.1        
##  [28] httr_1.4.4             assertthat_0.2.1       fastmap_1.1.0         
##  [31] lazyeval_0.2.2         cli_3.4.1              later_1.3.0           
##  [34] htmltools_0.5.2        tools_4.1.1            igraph_1.3.1          
##  [37] dotCall64_1.0-1        gtable_0.3.1           glue_1.6.2            
##  [40] RANN_2.6.1             reshape2_1.4.4         Rcpp_1.0.9            
##  [43] scattermore_1.2        jquerylib_0.1.4        vctrs_0.4.2           
##  [46] nlme_3.1-153           spatstat.explore_3.2-5 progressr_0.10.0      
##  [49] lmtest_0.9-40          spatstat.random_3.2-2  xfun_0.34             
##  [52] stringr_1.4.1          globals_0.14.0         mime_0.12             
##  [55] miniUI_0.1.1.1         lifecycle_1.0.3        irlba_2.3.5.1         
##  [58] goftest_1.2-3          future_1.25.0          MASS_7.3-54           
##  [61] zoo_1.8-10             scales_1.2.1           spatstat.utils_3.0-4  
##  [64] promises_1.2.0.1       parallel_4.1.1         yaml_2.3.6            
##  [67] pbapply_1.5-0          gridExtra_2.3          sass_0.4.1            
##  [70] stringi_1.7.8          highr_0.9              fastDummies_1.7.3     
##  [73] rlang_1.0.6            pkgconfig_2.0.3        matrixStats_0.62.0    
##  [76] evaluate_0.17          lattice_0.20-45        tensor_1.5            
##  [79] ROCR_1.0-11            purrr_0.3.5            labeling_0.4.2        
##  [82] patchwork_1.1.1        htmlwidgets_1.5.4      tidyselect_1.2.0      
##  [85] parallelly_1.31.1      RcppAnnoy_0.0.19       plyr_1.8.7            
##  [88] magrittr_2.0.3         R6_2.5.1               generics_0.1.3        
##  [91] DBI_1.1.2              withr_2.5.0            pillar_1.8.1          
##  [94] fitdistrplus_1.1-8     abind_1.4-5            survival_3.2-13       
##  [97] tibble_3.1.8           future.apply_1.9.0     crayon_1.5.2          
## [100] KernSmooth_2.23-20     utf8_1.2.2             spatstat.geom_3.2-7   
## [103] plotly_4.10.0          rmarkdown_2.11         grid_4.1.1            
## [106] data.table_1.14.2      digest_0.6.30          xtable_1.8-4          
## [109] tidyr_1.2.1            httpuv_1.6.5           munsell_0.5.0         
## [112] bslib_0.3.1

  1. IPNP & Imagine Institute, Paris, France, ↩︎

LS0tCnRpdGxlOiAiU1BSSU5HIFAwIEdtbmMgS08iCmF1dGhvcjogCiAgLSBGcsOpZMOpcmljIENhdXNlcmV0XltJUE5QICYgSW1hZ2luZSBJbnN0aXR1dGUsIFBhcmlzLCBGcmFuY2UsIGZyZWRlcmljLmNhdXNlcmV0QGluc2VybS5mcl0gWyFbXShodHRwczovL29yY2lkLm9yZy9zaXRlcy9kZWZhdWx0L2ZpbGVzL2ltYWdlcy9vcmNpZF8xNngxNi5wbmcpXShodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItMDU0My00OTM4KQogCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogICAgaGlnaGxpZ2h0OiBoYWRkb2NrCiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB5ZXMKLS0tCgpgYGB7Y3NzLCBlY2hvPUZBTFNFfQpoMSB7CiAgZm9udC1zaXplOiAzNHB4OwogIG1hcmdpbi10b3A6IDJyZW07CiAgbWFyZ2luLWJvdHRvbTogMXJlbTsKICBjb2xvcjogI2U2NGQwMDsKICB0ZXh0LWRlY29yYXRpb246IG5vbmU7Cn0KaDEudGl0bGUgewogIGZvbnQtc2l6ZTogNDBweDsKICBtYXJnaW4tdG9wOiAycmVtOwogIG1hcmdpbi1ib3R0b206IDFyZW07CiAgdGV4dC1hbGlnbjogY2VudGVyOwogIHRleHQtZGVjb3JhdGlvbjogbm9uZTsKICBjb2xvcjogIzAwMDAwMDsKfQpoMiB7CiAgZm9udC1zaXplOiAzMHB4OwogIG1hcmdpbi10b3A6IDJyZW07CiAgbWFyZ2luLWJvdHRvbTogMXJlbTsKICBjb2xvcjogIzAwMDAwMDsKfQpoMyB7CiAgZm9udC1zaXplOiAyNHB4OwogIG1hcmdpbi10b3A6IDJyZW07CiAgbWFyZ2luLWJvdHRvbTogMXJlbTsKICBjb2xvcjogIzAwMDAwMDsKfQpoNCB7CiAgZm9udC1zaXplOiAxOHB4OwogIG1hcmdpbi10b3A6IDJyZW07CiAgbWFyZ2luLWJvdHRvbTogMXJlbTsKICBjb2xvcjogIzAwMDAwMDsKfQpoNSB7CiAgZm9udC1zaXplOiAxNnB4OwogIG1hcmdpbi10b3A6IDJyZW07CiAgbWFyZ2luLWJvdHRvbTogMXJlbTsKICBjb2xvcjogIzAwMDAwMDsKfQoKcCB7CiAgZm9udC1zaXplOiAxNnB4Owp9CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZmlnLmFsaWduID0gJ2NlbnRlcicsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpCmBgYAoKR2VuZXJhdGlvbiBvZiBTUFJJTkcgY29vcmRpbmF0ZXMgZm9yIGFuIG9iamVjdCBwcmV2aW91c2x5IGdlbmVyYXRlZCBjb250YWluaW5nIFdUIGFuZCBHbW5jIEtPIGNlbGxzIGZyb20gUDAuIAoKCiMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dFeHRyYSkKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHJldGljdWxhdGUpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KE1ldEJyZXdlcikKbGlicmFyeSh3ZXNhbmRlcnNvbikKbGlicmFyeShSLnV0aWxzKQoKIyBTZXQgZ2dwbG90IHRoZW1lIGFzIGNsYXNzaWMKdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkKYGBgCgojIExvYWQgdGhlIGRhdGFzZXQgYW5kIGNhbGN1bGF0ZSBRQyBtZXRyaWNzCgpgYGB7cn0KCkdtbmMuY29tYmluZWQgPC0gcmVhZFJEUygiR21uYy5jb21iaW5lZC5SRFMiKQoKRGltUGxvdChHbW5jLmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVCwgbGFiZWwuc2l6ZSA9IDQsIHB0LnNpemUgPSAwLjEsIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsICBjb2xzID0gbWV0LmJyZXdlcigiS2xpbXQiLCAyMykpICsgTm9BeGVzKCkKCmBgYAoKIyBFeHBvcnQgZmlsZXMgZm9yIFNwcmluZwoKIyMgRXhwb3J0IHJhdyBleHByZXNzaW9uIG1hdHJpeCBhbmQgZ2VuZSBsaXN0IGZvciBzcHJpbmcgcGxvdCBnZW5lcmF0aW9uCmBgYHtyfQpFeHByc01hdHJpeCA8LSBhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKEdtbmMuY29tYmluZWQpKQpleHByRGF0YSA8LSBNYXRyaXgoRXhwcnNNYXRyaXgsIHNwYXJzZSA9IFRSVUUpCndyaXRlTU0oZXhwckRhdGEsICJTcHJpbmdfZmlsZXMvRXhwckRhdGEubXR4IikKZ3ppcCgiU3ByaW5nX2ZpbGVzL0V4cHJEYXRhLm10eCIsIG92ZXJ3cml0ZT1UKQpHZW5lbGlzdCA8LSByb3cubmFtZXMoRXhwcnNNYXRyaXgpCndyaXRlLnRhYmxlKEdlbmVsaXN0LCAiU3ByaW5nX2ZpbGVzL0dlbmVsaXN0LmNzdiIsIHNlcD0iXHQiLCBjb2wubmFtZXMgPSBGLCByb3cubmFtZXMgPSBGKQoKcm0oRXhwcnNNYXRyaXgsIGV4cHJEYXRhLCBHZW5lbGlzdCkKYGBgCgojIyBFeHBvcnQgY29udGludW91cyBtZXRhZGF0YQpgYGB7cn0KUy5TY29yZSA8LSBjKCJTLlNjb3JlIixHbW5jLmNvbWJpbmVkJFMuU2NvcmUpClMuU2NvcmUgPC0gcGFzdGUoUy5TY29yZSwgc2VwPSIsIiwgY29sbGFwc2U9IiwiKQpHMk0uU2NvcmUgPC0gYygiRzJNLlNjb3JlIixHbW5jLmNvbWJpbmVkJEcyTS5TY29yZSkKRzJNLlNjb3JlIDwtIHBhc3RlKEcyTS5TY29yZSwgc2VwPSIsIiwgY29sbGFwc2U9IiwiKQpQZXJjZW50Lm10IDwtIGMoIlBlcmNlbnQubXQiLCBHbW5jLmNvbWJpbmVkJHBlcmNlbnQubXQpClBlcmNlbnQubXQgPC0gcGFzdGUoUGVyY2VudC5tdCwgc2VwID0gIiwiLCBjb2xsYXBzZSA9ICIsIikKUGVyY2VudC5yYiA8LSBjKCJQZXJjZW50LnJiIiwgR21uYy5jb21iaW5lZCRwZXJjZW50LnJiKQpQZXJjZW50LnJiIDwtIHBhc3RlKFBlcmNlbnQucmIsIHNlcCA9ICIsIiwgY29sbGFwc2UgPSAiLCIpCm5Db3VudCA8LSBjKCJuQ291bnQiLCBHbW5jLmNvbWJpbmVkJG5Db3VudF9STkEpCm5Db3VudCA8LSBwYXN0ZShuQ291bnQsIHNlcCA9ICIsIiwgY29sbGFwc2UgPSAiLCIpIApuRmVhdHVyZSA8LSBjKCJuRmVhdHVyZSIsIEdtbmMuY29tYmluZWQkbkZlYXR1cmVfUk5BKQpuRmVhdHVyZSA8LSBwYXN0ZShuRmVhdHVyZSwgc2VwID0gIiwiLCBjb2xsYXBzZSA9ICIsIikKCkNvbG9yVHJhY2sgPC0gcmJpbmQoUy5TY29yZSwgRzJNLlNjb3JlLCBQZXJjZW50Lm10LCBQZXJjZW50LnJiLCBuQ291bnQsIG5GZWF0dXJlKQp3cml0ZS50YWJsZShDb2xvclRyYWNrLCAiU3ByaW5nX2ZpbGVzL0NvbG9yVHJhY2suY3N2IiwgcXVvdGUgPUYsIHJvdy5uYW1lcyA9IEYsIGNvbC5uYW1lcyA9IEYpCgpybShTLlNjb3JlLCBHMk0uU2NvcmUsIFBlcmNlbnQubXQsIFBlcmNlbnQucmIsIG5Db3VudCwgbkZlYXR1cmUsIENvbG9yVHJhY2spCmBgYAoKIyMgRXhwb3J0IGRpc2NyZXRlIG1ldGFkYXRhCmBgYHtyfQoKU2V1cmF0LmNsdXN0ZXJzIDwtIGMoIlNldXJhdCBDbHVzdGVycyIsIHBhc3RlMCgiQ2x1c3RlciIsYXMuY2hhcmFjdGVyKEdtbmMuY29tYmluZWQkc2V1cmF0X2NsdXN0ZXJzKSkpClNldXJhdC5jbHVzdGVycyA8LSBwYXN0ZShTZXVyYXQuY2x1c3RlcnMsIHNlcD0iLCIsIGNvbGxhcHNlPSIsIikKUGhhc2UgPC0gYygiUGhhc2UiLCBHbW5jLmNvbWJpbmVkJFBoYXNlKQpQaGFzZSA8LSBwYXN0ZShQaGFzZSwgc2VwPSIsIiwgY29sbGFwc2U9IiwiKQpvcmlnLmlkZW50IDwtIGMoIm9yaWcuaWRlbnQiLCBHbW5jLmNvbWJpbmVkJG9yaWcuaWRlbnQpIApvcmlnLmlkZW50IDwtIHBhc3RlKG9yaWcuaWRlbnQsIHNlcCA9ICIsIiwgY29sbGFwc2UgPSAiLCIpIApDZWxsZ3JvdXBpbmcgPC0gcmJpbmQoU2V1cmF0LmNsdXN0ZXJzLCBQaGFzZSwgb3JpZy5pZGVudCkKd3JpdGUudGFibGUoQ2VsbGdyb3VwaW5nLCAiU3ByaW5nX2ZpbGVzL0NlbGxncm91cGluZy5jc3YiLCBxdW90ZSA9Riwgcm93Lm5hbWVzID0gRiwgY29sLm5hbWVzID0gRikKCnJtKENlbGxncm91cGluZywgU2V1cmF0LmNsdXN0ZXJzLCBQaGFzZSwgb3JpZy5pZGVudCkKYGBgCgojIyBFeHBvcnQgVU1BUCBjb29yZGluYXRlcwpgYGB7cn0KQ29vcmRpbmF0ZXMgPC0gY2JpbmQoYygwOmRpbShHbW5jLmNvbWJpbmVkKVsyXS0xKSxHbW5jLmNvbWJpbmVkQHJlZHVjdGlvbnNbWyJ1bWFwIl1dQGNlbGwuZW1iZWRkaW5ncykKCiMgU3ltbWV0cnkgdHJhbnNmb3JtIG9mIHRoZSBjb29yZGluYXRlcwpTcHJpbmcuU3ltIDwtIGZ1bmN0aW9uKHgpewogIHggPSBhYnMobWF4KENvb3JkaW5hdGVzWywzXSkteCkKIH0KCkNvb3JkaW5hdGVzWywzXSA8LSBzYXBwbHkoQ29vcmRpbmF0ZXNbLDNdICwgZnVuY3Rpb24oeCkgU3ByaW5nLlN5bSh4KSkKCiMgQ2hhbmdlIHggYW5kIHkgYXhpcyB2YWx1ZXMgZm9yIGEgYmV0dGVyIGRpc3BsYXkgaW4gU1BSSU5HCkNvb3JkaW5hdGVzIDwtIENvb3JkaW5hdGVzICogMTAwCgp3cml0ZS50YWJsZShDb29yZGluYXRlcywgIlNwcmluZ19maWxlcy91bWFwLnR4dCIsIHF1b3RlID1GLCByb3cubmFtZXMgPSBGLCBjb2wubmFtZXMgPSBGLCBzZXAgPSAiLCIpCnJtKENvb3JkaW5hdGVzKQpgYGAKCkV4cHJEYXRhLm10eCwgR2VuZWxpc3QuY3N2LCBDb2xvclRyYWNrLmNzdiwgIENlbGxncm91cGluZy5jc3YgYW5kIHVtYXAudHh0IGFyZSB0aGVuIHVzZWQgYXMgaW5wdXQgZm9yIHRoZSBbU3ByaW5nIEFwcF0oaHR0cHM6Ly9rbGVpbnRvb2xzLmhtcy5oYXJ2YXJkLmVkdS90b29scy9zcHJpbmcuaHRtbCkgIAoKQSBkb3VibGV0IHNjb3JlIGlzIGNhbGN1bGF0ZWQgdXNpbmcgdGhlIFNQUklORyBhcHAgYnVpbHQtaW4gZnVuY3Rpb24gKGJhc2VkIG9uIHNjcnVibGV0KSB1c2luZyBkZWZhdWx0IHBhcmFtZXRlcnMgKGs9NTAsIHI9MiwgZj0wLjEpCgojIEltcG9ydCBkb3VibGV0IHNjb3JlIChzY3J1YmxldCkKYGBge3IgZmlnLmRpbT1jKDYsIDYpfQpkb3VibGV0LnNjb3JlIDwtIHJlYWQudGFibGUoIlNwcmluZ19maWxlcy9kb3VibGV0X3Jlc3VsdHMudHN2IiwgaGVhZGVyID0gVCkKZG91YmxldC5zY29yZSA8LSBmaWx0ZXIoZG91YmxldC5zY29yZSwgT2JzZXJ2ZWRfb3JfU2ltdWxhdGVkID09ICJPYnNlcnZlZCIpCkdtbmMuY29tYmluZWQkZG91YmxldC5zY29yZSA8LWRvdWJsZXQuc2NvcmUkU2NvcmUKCkZlYXR1cmVQbG90KG9iamVjdCA9IEdtbmMuY29tYmluZWQsCiAgICAgICAgICAgIGZlYXR1cmVzID0gImRvdWJsZXQuc2NvcmUiLAogICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjQpICsgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnM9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiUmRQdSIpKSkgKyBOb0F4ZXMoKQoKZ2dwbG90KGRvdWJsZXQuc2NvcmUsIGFlcyh4ID0gU2NvcmUsIHN0YXQobmRlbnNpdHkpKSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDIwMCwgY29sb3VyID0ibGlnaHRncmV5IikrCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjI1LCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAyKQoKcm0oZG91YmxldC5zY29yZSkKYGBgCgoKQ2VsbCBjb29yZGluYXRlcyBvZiB0aGUgU3ByaW5nIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBhcmUgZ2VuZXJhdGVkIHVzaW5nIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyAoZXhjbHVkaW5nIGNlbGxzIHdpdGggYSBkb3VibGV0IHNjb3JlID4wLjI1KToKTWluIGV4cHJlc3NpbmcgY2VsbHMgKGdlbmUgZmlsdGVyaW5nKTogMwpNaW4gbnVtYmVyIG9mIFVNSXMgKGdlbmUgZmlsdGVyaW5nKTogMwpHZW5lIHZhcmlhYmlsaXR5ICVpbGUgKGdlbmUgZmlsdGVyaW5nKTogOTAKTnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzOiAyMApOdW1iZXIgb2YgbmVhcmVzdCBuZWlnaGJvcnM6IDgKTnVtYmVyIG9mIGZvcmNlIGxheW91dCBpdGVyYXRpb25zOiA1MDAKCiMgSW1wb3J0IFNwcmluZyBjb29yZGluYXRlcwpgYGB7ciBmaWcuZGltPWMoNiwgNil9ClNwcmluZy5TeW0gPC0gZnVuY3Rpb24oeCl7eCA9IGFicyhtYXgoQ29vcmRpbmF0ZXNbLDJdKS14KX0gIyBTeW1tZXRyeSB0cmFuc2Zvcm0gb2YgdGhlIGNvb3JkaW5hdGVzCgpDb29yZGluYXRlcyA8LSByZWFkLnRhYmxlKCJTcHJpbmdfZmlsZXMvY29vcmRpbmF0ZXMudHh0Iiwgc2VwID0gIiwiLCBoZWFkZXIgPSBGKVssYygyLDMpXQpHbW5jLmNsZWFudXAgPC0gc3Vic2V0KEdtbmMuY29tYmluZWQsIGRvdWJsZXQuc2NvcmUgPCAwLjI1KQpyb3duYW1lcyhDb29yZGluYXRlcykgPC0gY29sbmFtZXMoR21uYy5jbGVhbnVwKQpjb2xuYW1lcyhDb29yZGluYXRlcykgPC0gcGFzdGUwKCJTcHJpbmdfIiwgMToyKQpDb29yZGluYXRlc1ssMl0gPC0gc2FwcGx5KENvb3JkaW5hdGVzWywyXSAsIGZ1bmN0aW9uKHgpIFNwcmluZy5TeW0oeCkpCkdtbmMuY2xlYW51cFtbIlNwcmluZyJdXSA8LSBDcmVhdGVEaW1SZWR1Y09iamVjdChlbWJlZGRpbmdzID0gYXMubWF0cml4KENvb3JkaW5hdGVzKSwga2V5ID0gIlNwcmluZ18iKQoKcm0oQ29vcmRpbmF0ZXMpCgojIFNwcmluZyB2aXN1YWxpemF0aW9uIApEaW1QbG90KEdtbmMuY2xlYW51cCwgcmVkdWN0aW9uID0gIlNwcmluZyIsIHB0LnNpemUgPSAwLjIsIGxhYmVsID0gVCwgbGFiZWwuc2l6ZSA9IDMsIGNvbHMgPSBtZXQuYnJld2VyKCJLbGltdCIsIDIzKSkgKyBOb0F4ZXMoKSArIE5vTGVnZW5kKCkKCkRpbVBsb3QoR21uYy5jbGVhbnVwLCByZWR1Y3Rpb24gPSAiU3ByaW5nIiwgcHQuc2l6ZSA9IDAuMiwgbGFiZWwgPSBGLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwgY29scyA9IG1ldC5icmV3ZXIoIkVneXB0IiwgMikpICsgTm9BeGVzKCkgCgpgYGAKCgojIFNhdmUgU2V1cmF0IG9iamVjdHMKCmBgYHtyfQpzYXZlUkRTKEdtbmMuY2xlYW51cCwgIkdtbmMuY2xlYW51cC5SRFMiKQoKYGBgCgoKCgoKIyBTZXNzaW9uIEluZm8KCmBgYHtyfQojZGF0ZQpmb3JtYXQoU3lzLnRpbWUoKSwgIiVkICVCLCAlWSwgJTolTSIpCgojUGFja2FnZXMgdXNlZApzZXNzaW9uSW5mbygpCmBgYAo=