In this part of the analysis we apply Revelio algorithm to explore cell cycle dynamic of pallial and hem domain radial glial cells

Load libraries

library(Seurat)
library(Revelio)
library(princurve)
library(monocle)
library(gprofiler2)
library(orthologsBioMART)
library(Matrix)
library(dplyr)
library(RColorBrewer)
library(ggplot2)
library(ggExtra)
library(cowplot)
library(wesanderson)

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

Load and filter progenitors data

Hem.data <- readRDS("../QC.filtered.clustered.cells.RDS")
DimPlot(object = Hem.data,
        group.by = "Cell_ident",
        reduction = "spring",
        cols = c("#ebcb2e", #"ChP"
                 "#9ec22f", #"ChP_progenitors"
                 "#e7823a", # CR
                 "#cc3a1b", #"Dorso-Medial_pallium" 
                 "#d14c8d", #"Hem" 
                 "#4cabdc", #"Medial_pallium"
                 "#046c9a", # Pallial
                 "#4990c9" #"Thalamic_eminence"
                 )
        )

Fit Pseudotime axis on ChP cells

ChP.data <-  subset(Hem.data, idents = c("ChP", "ChP_progenitors"))

DimPlot(ChP.data,
        reduction = "spring",
        pt.size = 1,
        cols =  c("#83c3b8", "#009fda")) + NoAxes()

Exclude septal cells

FeaturePlot(object = ChP.data ,
            features = c("Fgf8", "Fgf17", "Adamts15", "Fgfbp1"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes() & NoLegend()

ChP.data <- AddModuleScore(ChP.data,
                           features = list(c("Fgf8", "Fgf17", "Adamts15", "Fgfbp1")),
                           ctrl = 10,
                           name = "Septum")

FeaturePlot(object = ChP.data ,
            features = c("Septum1"),
            pt.size = 0.5,
            cols = rev(brewer.pal(10,"Spectral")),
            reduction = "spring",
            order = T) & NoAxes()

hist(ChP.data$Septum1, breaks = 20)

ChP.data$Septal.prog <- ChP.data$Septum1 > 0
p1 <- DimPlot(ChP.data,
        reduction = "spring",
        group.by = "Septal.prog",
        pt.size = 1) + NoAxes()

p2 <- FeaturePlot(object = ChP.data ,
            features = c("Fgf17"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes() & NoLegend()

p1 + p2

ChP.data <- subset(ChP.data,
                   subset = Septal.prog == FALSE & ChP.data$Spring_1 > 1300)
DimPlot(ChP.data,
        reduction = "spring",
        pt.size = 1,
        cols =  c("#83c3b8", "#009fda")) + NoAxes()

Fit principal curve

Trajectories.ChP <- ChP.data@meta.data %>%
                    dplyr::select("Barcodes", "nUMI", "Spring_1", "Spring_2")
fit <- principal_curve(as.matrix(Trajectories.ChP[,c("Spring_1", "Spring_2")]),
                       smoother='lowess',
                       trace=TRUE,
                       f = 0.8, 
                       stretch=2)
## Starting curve---distance^2: 117510509591
## Iteration 1---distance^2: 46558929
## Iteration 2---distance^2: 42504599
## Iteration 3---distance^2: 41393217
## Iteration 4---distance^2: 40991617
## Iteration 5---distance^2: 40890588
## Iteration 6---distance^2: 40826638
## Iteration 7---distance^2: 40830824
#The principal curve smoothed
ChP.pc.line <- as.data.frame(fit$s[order(fit$lambda),]) 

#Pseudotime score
Trajectories.ChP$Pseudotime <- fit$lambda/max(fit$lambda)

#Inverse the score if positive correlation with progenitor marker
if (cor(Trajectories.ChP$Pseudotime, ChP.data@assays$SCT@data['Hmga2', Trajectories.ChP$Barcodes]) > 0) {
  Trajectories.ChP$Pseudotime <- -(Trajectories.ChP$Pseudotime - max(Trajectories.ChP$Pseudotime))
}

ChP.data$Pseudotime <- Trajectories.ChP$Pseudotime
FeaturePlot(object = ChP.data,
            features = "Pseudotime",
            pt.size = 2,
            cols = rev(colorRampPalette(brewer.pal(n =10, name = "Spectral"))(100)),
            reduction = "spring",
            order = T) & NoAxes()

Subset progenitors and fit cell cycle axis

Prog.data <-  subset(ChP.data, idents = c("ChP_progenitors"))

DimPlot(Prog.data,
        reduction = "spring",
        pt.size = 1,
        cols =  c("#009fda")) + NoAxes()

Prog.data <- NormalizeData(Prog.data, normalization.method = "LogNormalize", scale.factor = 10000, assay = "RNA")

Prepare data for revelio input

Export counts matrix

rawCounts <- as.matrix(Prog.data[["RNA"]]@counts)
# Filter genes expressed by less than 10 cells
num.cells <- Matrix::rowSums(rawCounts > 0)
genes.use <- names(x = num.cells[which(x = num.cells >= 10)])
rawCounts <- rawCounts[genes.use, ]
rm(list = ls()[!ls() %in% c("rawCounts", "CCgenes", "ChP.data", "Prog.data")])
gc()
##             used  (Mb) gc trigger   (Mb)  max used   (Mb)
## Ncells   6031711 322.2    8756530  467.7   8756530  467.7
## Vcells 109360801 834.4  603966152 4607.9 620030699 4730.5

Run Revelio

CCgenes <- read.table("CCgenes.csv", sep = ";", header = T)

We can now follow the tutorial form the package github page

myData <- createRevelioObject(rawData = rawCounts,
                              cyclicGenes = CCgenes,
                              lowernGeneCutoff = 0,
                              uppernUMICutoff = Inf,
                              ccPhaseAssignBasedOnIndividualBatches = F)
## 2022-02-24 16:17:15: reading data: 1.63secs
rm("rawCounts")
gc()
##             used  (Mb) gc trigger   (Mb)  max used   (Mb)
## Ncells   6050902 323.2    8756530  467.7   8756530  467.7
## Vcells 109450747 835.1  483172922 3686.4 620030699 4730.5

The getCellCyclePhaseAssignInformation filter “outlier” cells for cell cycle phase assignation. We modified the function to keep all cells as we observed this does not affect the global cell cycle fitting procedure

source("../Functions/functions_InitializationCCPhaseAssignFiltering.R")

myData <- getCellCyclePhaseAssign_allcells(myData)
## 2022-02-24 16:17:20: assigning cell cycle phases: 8.68secs
myData <- getPCAData(dataList = myData)
## 2022-02-24 16:17:35: calculating PCA: 8.72secs
myData <- getOptimalRotation(dataList = myData)
## 2022-02-24 16:17:58: calculating optimal rotation: 1.14secs
gc()
##             used   (Mb) gc trigger   (Mb)  max used   (Mb)
## Ncells   6082646  324.9   11352458  606.3  11352458  606.3
## Vcells 146912240 1120.9  483172922 3686.4 620030699 4730.5

Graphical exploration of the infered cell cycle axis

CellCycledata <- cbind(as.data.frame(t(myData@transformedData$dc$data[1:2,])),
                       nUMI= myData@cellInfo$nUMI,
                       Revelio.phase = factor(myData@cellInfo$ccPhase, levels = c("G1.S", "S", "G2", "G2.M", "M.G1")),
                       Revelio.cc= myData@cellInfo$ccPercentageUniformlySpaced,
                       Seurat.cc= Prog.data@meta.data[myData@cellInfo$cellID,"CC.Difference"])

Cells distribution in the DC1-DC2 space

ggplot(CellCycledata, aes(DC1, DC2)) +
        geom_point(aes(color = Revelio.phase)) +
        scale_color_manual(values= c(wes_palette("FantasticFox1")[1:3],"grey40",wes_palette("FantasticFox1")[5]))

p1 <- ggplot(CellCycledata, aes(DC1, DC2)) +
        geom_point(aes(color = Revelio.phase)) +
        scale_color_manual(values= c(wes_palette("FantasticFox1")[1:3],"grey40",wes_palette("FantasticFox1")[5]))

p2 <- ggplot(CellCycledata, aes(DC1, DC2)) +
        geom_point(aes(color=Revelio.cc), size=2, shape=16) + 
        scale_color_gradientn(colours=rev(colorRampPalette(brewer.pal(n =11, name = "Spectral"))(100)),
                              name='Revelio_cc')


p1 + p2

ggplot(CellCycledata, aes(x= Revelio.cc, y= nUMI/10000)) +
        geom_point(aes(color= Revelio.phase), size=0.5) +
        scale_color_manual(values= c(wes_palette("FantasticFox1")[1:3],"grey40",wes_palette("FantasticFox1")[5])) +
        geom_smooth(method="loess", n= 50, fill="grey") +
        ylim(0,NA)

Import coordinates

Prog.data$Revelio.DC1 <- CellCycledata$DC1
Prog.data$Revelio.DC2 <- CellCycledata$DC2

Prog.data$Revelio.phase <- CellCycledata$Revelio.phase
Prog.data$Revelio.cc <- CellCycledata$Revelio.cc
p1 <- FeaturePlot(object = Prog.data,
            features = "Revelio.cc",
            pt.size = 1,
            cols = rev(brewer.pal(10,"Spectral")),
            reduction = "spring",
            order = T) & NoAxes()

p2 <- DimPlot(object = Prog.data,
        group.by = "Revelio.phase",
        pt.size = 1,
        reduction = "spring",
        cols =  c(wes_palette("FantasticFox1")[1:3],"grey40",wes_palette("FantasticFox1")[5])) & NoAxes()

p3 <- FeaturePlot(object = Prog.data,
            features = "Pseudotime",
            pt.size = 2,
            cols = rev(colorRampPalette(brewer.pal(n =10, name = "Spectral"))(100)),
            reduction = "spring",
            order = T) & NoAxes()

p1 + p2 + p3 

Trajectories.progenitors <- Prog.data@meta.data %>%
                              dplyr::select(Barcodes, nUMI, Spring_1, Spring_2, Pseudotime) %>% 
                              mutate(Cycling.axis= Prog.data$Revelio.cc,
                                     Phase = Prog.data$Revelio.phase,
                                     Gmnc= Prog.data@assays$RNA@data["Gmnc",],
                                     Ttr= Prog.data@assays$RNA@data["Ttr",],
                                     Htr2c= Prog.data@assays$RNA@data["Htr2c",],
                                     Top2a= Prog.data@assays$RNA@data["Top2a",])
p1 <- ggplot(Trajectories.progenitors, aes(x= Pseudotime, y= Cycling.axis)) +
        geom_point(aes(color= Phase), size=1.5) +
        scale_color_manual(values= c(wes_palette("FantasticFox1")[1:3],"grey40",wes_palette("FantasticFox1")[5]))

p2 <- Trajectories.progenitors %>% arrange(Gmnc) %>%
      ggplot(aes(x= Pseudotime, y= Cycling.axis)) +
        geom_point(aes(color=Gmnc), size=1.5) +
        scale_color_gradientn(colours =c("grey90", brewer.pal(9,"YlGnBu")))

p3 <- Trajectories.progenitors %>% arrange(Ttr) %>%
      ggplot(aes(x= Pseudotime, y= Cycling.axis)) +
        geom_point(aes(color=Ttr), size=1.5) +
        scale_color_gradientn(colours =c("grey90", brewer.pal(9,"YlGnBu")))

p4 <- Trajectories.progenitors %>% arrange(Htr2c) %>%
      ggplot(aes(x= Pseudotime, y= Cycling.axis)) +
        geom_point(aes(color=Htr2c), size=1.5) +
        scale_color_gradientn(colours =c("grey90", brewer.pal(9,"YlGnBu")))

p1 + p2 + p3 + p4  + patchwork::plot_layout(ncol = 2)

rm(list = ls()[!ls() %in% c("Trajectories.progenitors", "ChP.data")])
gc()
##            used  (Mb) gc trigger   (Mb)  max used   (Mb)
## Ncells  6109035 326.3   11352458  606.3  11352458  606.3
## Vcells 55782339 425.6  386538338 2949.1 620030699 4730.5

Import progenitors cycling coordinates in the full dataset

ChP.data$Cycling.axis <- sapply(ChP.data$Barcodes,
                              FUN = function(x) {
                                if (x %in% Trajectories.progenitors$Barcodes) {
                                  x = Trajectories.progenitors[x, "Cycling.axis"]
                                } else {
                                  x = NA
                                  }
                              })
FeaturePlot(object = ChP.data,
            features = "Pseudotime",
            pt.size = 2,
            cols = rev(colorRampPalette(brewer.pal(n =10, name = "Spectral"))(100)),
            reduction = "spring",
            order = T) & NoAxes()

Use monocle2 to model gene expression along differentition axis

Initialize a monocle object

# Transfer metadata
meta.data <- data.frame(Barcode= ChP.data$Barcodes,
                        Pseudotime= ChP.data$Pseudotime,
                        Cell.cycle= ChP.data$Phase)

Annot.data  <- new('AnnotatedDataFrame', data = meta.data)

# Transfer counts data
ChP.data <- FindVariableFeatures(ChP.data, selection.method = "vst", nfeatures = 2000)
var.genes <- VariableFeatures(ChP.data)
count.data = data.frame(gene_short_name = rownames(ChP.data[["RNA"]]@data[var.genes,]),
                        row.names = rownames(ChP.data[["RNA"]]@data[var.genes,]))

feature.data <- new('AnnotatedDataFrame', data = count.data)

# Create the CellDataSet object including variable genes only
gbm_cds <- newCellDataSet(ChP.data[["RNA"]]@counts[var.genes,],
                          phenoData = Annot.data,
                          featureData = feature.data,
                          lowerDetectionLimit = 0,
                          expressionFamily = negbinomial())
gbm_cds <- estimateSizeFactors(gbm_cds)
gbm_cds <- estimateDispersions(gbm_cds)
gbm_cds <- detectGenes(gbm_cds, min_expr = 0.1)
rm(list = ls()[!ls() %in% c("ChP.data", "gbm_cds")])
gc()
##            used  (Mb) gc trigger   (Mb)  max used   (Mb)
## Ncells  6163153 329.2   11352458  606.3  11352458  606.3
## Vcells 57165822 436.2  309230671 2359.3 620030699 4730.5

Test each gene trend over pseudotime score

pseudo.maturation.diff <- differentialGeneTest(gbm_cds[fData(gbm_cds)$num_cells_expressed >= 30,], 
                                                 fullModelFormulaStr = "~sm.ns(Pseudotime, df = 3)", 
                                                 reducedModelFormulaStr = "~1", 
                                                 cores = parallel::detectCores() - 2)
# Filter genes based on FDR
pseudo.maturation.diff.filtered <- pseudo.maturation.diff %>% filter(qval < 5e-5)

Smooth expression of significative genes

# Create a new vector of 200 points
nPoints <- 200
new_data <- data.frame(Pseudotime = seq(min(pData(gbm_cds)$Pseudotime), max(pData(gbm_cds)$Pseudotime), length.out = nPoints))

# Smooth gene expression
Smooth.curve.matrix <- genSmoothCurves(gbm_cds[as.character(pseudo.maturation.diff.filtered$gene_short_name),],
                                       trend_formula = "~sm.ns(Pseudotime, df = 3)",
                                       relative_expr = TRUE,
                                       new_data = new_data,
                                       cores= parallel::detectCores() - 2)

Cluster genes and plot heatmap

## Cluster gene by expression profiles
Pseudotime.genes.clusters <- cluster::pam(as.dist((1 - cor(Matrix::t(Smooth.curve.matrix),method = "pearson"))), k= 4)

ChP.Gene.dynamique <- data.frame(Gene= names(Pseudotime.genes.clusters$clustering),
                                 Waves= Pseudotime.genes.clusters$clustering,
                                 Gene.Clusters = Pseudotime.genes.clusters$clustering,
                                 q.val = pseudo.maturation.diff.filtered$qval
                                 ) %>% arrange(Gene.Clusters)

row.names(ChP.Gene.dynamique) <- ChP.Gene.dynamique$Gene
ChP.Gene.dynamique$Gene.Clusters <- paste0("Clust.", ChP.Gene.dynamique$Gene.Clusters)
# Order the rows using seriation
dst <- as.dist((1-cor(scale(t(Smooth.curve.matrix)), method = "spearman")))
row.ser <- seriation::seriate(dst, method ="R2E") #"R2E" #TSP #"GW" "GW_ward"
gene.order <- rownames(Smooth.curve.matrix[seriation::get_order(row.ser),])

pal <- wes_palette("Darjeeling1")
anno.colors <- list(Cell.state = c(Cycling_RG="#046c9a", Differentiating_cells="#ebcb2e"),
                    Gene.Clusters = c(Clust.1 =pal[1] , Clust.2=pal[2], Clust.3=pal[3], Clust.4=pal[4], Clust.5=pal[5]))

pheatmap::pheatmap(Smooth.curve.matrix[gene.order,],
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = ChP.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_colors = anno.colors,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

GO term enrichment in gene clusters using gprofiler2

ChP.gostres <- gost(query = list("Clust.1" = ChP.Gene.dynamique %>% filter(Gene.Clusters == "Clust.1") %>% pull(Gene) %>% as.character(),
                             "Clust.2" = ChP.Gene.dynamique %>% filter(Gene.Clusters == "Clust.2") %>% pull(Gene) %>% as.character(),
                             "Clust.3" = ChP.Gene.dynamique %>% filter(Gene.Clusters == "Clust.3") %>% pull(Gene) %>% as.character(),
                             "Clust.4" = ChP.Gene.dynamique %>% filter(Gene.Clusters == "Clust.4") %>% pull(Gene) %>% as.character(),
                             "Clust.5" = ChP.Gene.dynamique %>% filter(Gene.Clusters == "Clust.5") %>% pull(Gene) %>% as.character()),
                organism = "mmusculus", ordered_query = F, 
                multi_query = F, significant = T, exclude_iea = T, 
                measure_underrepresentation = F, evcodes = T, 
                user_threshold = 0.05, correction_method = "fdr", 
                domain_scope = "annotated", custom_bg = NULL, 
                numeric_ns = "", sources = c("GO:MF", "GO:BP"), as_short_link = F)
ChP.gostres <- gost(query = as.character(ChP.Gene.dynamique$Gene),
                organism = "mmusculus", ordered_query = F, 
                multi_query = F, significant = T, exclude_iea = T, 
                measure_underrepresentation = F, evcodes = T, 
                user_threshold = 0.05, correction_method = "fdr", 
                domain_scope = "annotated", custom_bg = NULL, 
                numeric_ns = "", sources = c("GO:MF", "GO:BP"), as_short_link = F)

Save results

write.table(ChP.Gene.dynamique, "ChP.Gene.dynamique.csv", sep = ";")

write.table(apply(ChP.gostres$result,2,as.character),
            "ChP.gostres.csv", sep = ";", quote = F, row.names = F)

Session Info

#date
format(Sys.time(), "%d %B, %Y, %H,%M")
## [1] "24 février, 2022, 16,21"
#Packages used
sessionInfo()
## R version 4.1.2 (2021-11-01)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.3 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0
## 
## locale:
##  [1] LC_CTYPE=fr_FR.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=fr_FR.UTF-8        LC_COLLATE=fr_FR.UTF-8    
##  [5] LC_MONETARY=fr_FR.UTF-8    LC_MESSAGES=fr_FR.UTF-8   
##  [7] LC_PAPER=fr_FR.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=fr_FR.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] splines   stats4    stats     graphics  grDevices utils     datasets 
## [8] methods   base     
## 
## other attached packages:
##  [1] wesanderson_0.3.6      cowplot_1.1.1          ggExtra_0.9           
##  [4] RColorBrewer_1.1-2     dplyr_1.0.7            orthologsBioMART_0.1.0
##  [7] data.table_1.14.2      biomaRt_2.50.1         gprofiler2_0.2.1      
## [10] monocle_2.22.0         DDRTree_0.1.5          irlba_2.3.3           
## [13] VGAM_1.1-5             ggplot2_3.3.5          Biobase_2.54.0        
## [16] BiocGenerics_0.40.0    Matrix_1.4-0           princurve_2.1.6       
## [19] Revelio_0.1.0          SeuratObject_4.0.4     Seurat_4.0.5          
## 
## loaded via a namespace (and not attached):
##   [1] utf8_1.2.2             reticulate_1.22        tidyselect_1.1.1      
##   [4] RSQLite_2.2.8          AnnotationDbi_1.56.2   htmlwidgets_1.5.4     
##   [7] TSP_1.1-11             grid_4.1.2             combinat_0.0-8        
##  [10] docopt_0.7.1           Rtsne_0.15             munsell_0.5.0         
##  [13] codetools_0.2-18       ica_1.0-2              future_1.23.0         
##  [16] miniUI_0.1.1.1         withr_2.4.3            colorspace_2.0-2      
##  [19] fastICA_1.2-3          filelock_1.0.2         highr_0.9             
##  [22] knitr_1.36             ROCR_1.0-11            tensor_1.5            
##  [25] listenv_0.8.0          labeling_0.4.2         slam_0.1-49           
##  [28] GenomeInfoDbData_1.2.7 polyclip_1.10-0        bit64_4.0.5           
##  [31] farver_2.1.0           pheatmap_1.0.12        parallelly_1.29.0     
##  [34] vctrs_0.3.8            generics_0.1.1         xfun_0.28             
##  [37] BiocFileCache_2.2.0    R6_2.5.1               GenomeInfoDb_1.30.0   
##  [40] seriation_1.3.1        bitops_1.0-7           spatstat.utils_2.2-0  
##  [43] cachem_1.0.6           assertthat_0.2.1       promises_1.2.0.1      
##  [46] scales_1.1.1           gtable_0.3.0           globals_0.14.0        
##  [49] goftest_1.2-3          rlang_0.4.12           lazyeval_0.2.2        
##  [52] spatstat.geom_2.3-0    yaml_2.2.1             reshape2_1.4.4        
##  [55] abind_1.4-5            httpuv_1.6.3           tools_4.1.2           
##  [58] ellipsis_0.3.2         spatstat.core_2.3-1    jquerylib_0.1.4       
##  [61] ggridges_0.5.3         Rcpp_1.0.8             plyr_1.8.6            
##  [64] progress_1.2.2         zlibbioc_1.40.0        purrr_0.3.4           
##  [67] RCurl_1.98-1.5         densityClust_0.3       prettyunits_1.1.1     
##  [70] rpart_4.1.16           deldir_1.0-6           pbapply_1.5-0         
##  [73] viridis_0.6.2          S4Vectors_0.32.3       zoo_1.8-9             
##  [76] ggrepel_0.9.1          cluster_2.1.2          magrittr_2.0.2        
##  [79] scattermore_0.7        lmtest_0.9-39          RANN_2.6.1            
##  [82] fitdistrplus_1.1-6     matrixStats_0.61.0     hms_1.1.1             
##  [85] patchwork_1.1.1        mime_0.12              evaluate_0.14         
##  [88] xtable_1.8-4           XML_3.99-0.8           sparsesvd_0.2         
##  [91] IRanges_2.28.0         gridExtra_2.3          HSMMSingleCell_1.14.0 
##  [94] compiler_4.1.2         tibble_3.1.6           KernSmooth_2.23-20    
##  [97] crayon_1.4.2           htmltools_0.5.2        mgcv_1.8-38           
## [100] later_1.3.0            tidyr_1.1.4            DBI_1.1.1             
## [103] dbplyr_2.1.1           MASS_7.3-55            rappdirs_0.3.3        
## [106] parallel_4.1.2         igraph_1.2.11          pkgconfig_2.0.3       
## [109] registry_0.5-1         plotly_4.10.0          spatstat.sparse_2.0-0 
## [112] foreach_1.5.1          xml2_1.3.3             bslib_0.3.1           
## [115] XVector_0.34.0         stringr_1.4.0          digest_0.6.29         
## [118] sctransform_0.3.2      RcppAnnoy_0.0.19       spatstat.data_2.1-0   
## [121] Biostrings_2.62.0      rmarkdown_2.11         leiden_0.3.9          
## [124] uwot_0.1.10            curl_4.3.2             shiny_1.7.1           
## [127] lifecycle_1.0.1        nlme_3.1-153           jsonlite_1.7.2        
## [130] viridisLite_0.4.0      limma_3.50.0           fansi_0.5.0           
## [133] pillar_1.6.4           lattice_0.20-45        KEGGREST_1.34.0       
## [136] fastmap_1.1.0          httr_1.4.2             survival_3.2-13       
## [139] glue_1.5.1             qlcMatrix_0.9.7        FNN_1.1.3             
## [142] iterators_1.0.13       png_0.1-7              bit_4.0.4             
## [145] stringi_1.7.6          sass_0.4.0             blob_1.2.2            
## [148] memoise_2.0.1          future.apply_1.8.1

  1. Institute of Psychiatry and Neuroscience of Paris, INSERM U1266, 75014, Paris, France, ↩︎

LS0tCnRpdGxlOiAiQ2hvcm9pZCBwbGV4dXMgZGlmZmVyZW50aWF0aW9uIHRyYWplY3RvcnkiCmF1dGhvcjoKICAgLSBNYXR0aGlldSBNb3JlYXVeW0luc3RpdHV0ZSBvZiBQc3ljaGlhdHJ5IGFuZCBOZXVyb3NjaWVuY2Ugb2YgUGFyaXMsIElOU0VSTSBVMTI2NiwgNzUwMTQsIFBhcmlzLCBGcmFuY2UsIG1hdHRoaWV1Lm1vcmVhdUBpbnNlcm0uZnJdIFshW10oaHR0cHM6Ly9vcmNpZC5vcmcvc2l0ZXMvZGVmYXVsdC9maWxlcy9pbWFnZXMvb3JjaWRfMTZ4MTYucG5nKV0oaHR0cHM6Ly9vcmNpZC5vcmcvMDAwMC0wMDAyLTI1OTItMjM3MykKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6IAogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBkZl9wcmludDogdGliYmxlCiAgICBoaWdobGlnaHQ6IGhhZGRvY2sKICAgIHRoZW1lOiBjb3NtbwogICAgY3NzOiAiLi4vc3R5bGUuY3NzIgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHllcwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGZpZy5hbGlnbiA9ICdjZW50ZXInLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZS5sYXp5ID0gRkFMU0UpCgojIFRvIHVzZSBiaW9tYXJ0IApuZXdfY29uZmlnIDwtIGh0dHI6OmNvbmZpZyhzc2xfdmVyaWZ5cGVlciA9IEZBTFNFKQpodHRyOjpzZXRfY29uZmlnKG5ld19jb25maWcsIG92ZXJyaWRlID0gRkFMU0UpCmBgYAoKSW4gdGhpcyBwYXJ0IG9mIHRoZSBhbmFseXNpcyB3ZSBhcHBseSBbUmV2ZWxpb10oaHR0cHM6Ly9naXRodWIuY29tL2RhbmllbHNjaHcxODgvUmV2ZWxpbykgYWxnb3JpdGhtIHRvIGV4cGxvcmUgY2VsbCBjeWNsZSBkeW5hbWljIG9mIHBhbGxpYWwgYW5kIGhlbSBkb21haW4gcmFkaWFsIGdsaWFsIGNlbGxzCgojIExvYWQgbGlicmFyaWVzCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShSZXZlbGlvKQpsaWJyYXJ5KHByaW5jdXJ2ZSkKbGlicmFyeShtb25vY2xlKQpsaWJyYXJ5KGdwcm9maWxlcjIpCmxpYnJhcnkob3J0aG9sb2dzQmlvTUFSVCkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dFeHRyYSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHdlc2FuZGVyc29uKQoKI1NldCBnZ3Bsb3QgdGhlbWUgYXMgY2xhc3NpYwp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQpgYGAKCiMgTG9hZCBhbmQgZmlsdGVyIHByb2dlbml0b3JzIGRhdGEKCmBgYHtyfQpIZW0uZGF0YSA8LSByZWFkUkRTKCIuLi9RQy5maWx0ZXJlZC5jbHVzdGVyZWQuY2VsbHMuUkRTIikKYGBgCgpgYGB7cn0KRGltUGxvdChvYmplY3QgPSBIZW0uZGF0YSwKICAgICAgICBncm91cC5ieSA9ICJDZWxsX2lkZW50IiwKICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICBjb2xzID0gYygiI2ViY2IyZSIsICMiQ2hQIgogICAgICAgICAgICAgICAgICIjOWVjMjJmIiwgIyJDaFBfcHJvZ2VuaXRvcnMiCiAgICAgICAgICAgICAgICAgIiNlNzgyM2EiLCAjIENSCiAgICAgICAgICAgICAgICAgIiNjYzNhMWIiLCAjIkRvcnNvLU1lZGlhbF9wYWxsaXVtIiAKICAgICAgICAgICAgICAgICAiI2QxNGM4ZCIsICMiSGVtIiAKICAgICAgICAgICAgICAgICAiIzRjYWJkYyIsICMiTWVkaWFsX3BhbGxpdW0iCiAgICAgICAgICAgICAgICAgIiMwNDZjOWEiLCAjIFBhbGxpYWwKICAgICAgICAgICAgICAgICAiIzQ5OTBjOSIgIyJUaGFsYW1pY19lbWluZW5jZSIKICAgICAgICAgICAgICAgICApCiAgICAgICAgKQpgYGAKCiMgRml0IFBzZXVkb3RpbWUgYXhpcyBvbiBDaFAgY2VsbHMKCmBgYHtyfQpDaFAuZGF0YSA8LSAgc3Vic2V0KEhlbS5kYXRhLCBpZGVudHMgPSBjKCJDaFAiLCAiQ2hQX3Byb2dlbml0b3JzIikpCgpEaW1QbG90KENoUC5kYXRhLAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIHB0LnNpemUgPSAxLAogICAgICAgIGNvbHMgPSAgYygiIzgzYzNiOCIsICIjMDA5ZmRhIikpICsgTm9BeGVzKCkKYGBgCgojIyBFeGNsdWRlIHNlcHRhbCBjZWxscwoKYGBge3J9CkZlYXR1cmVQbG90KG9iamVjdCA9IENoUC5kYXRhICwKICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJGZ2Y4IiwgIkZnZjE3IiwgIkFkYW10czE1IiwgIkZnZmJwMSIpLAogICAgICAgICAgICBwdC5zaXplID0gMC41LAogICAgICAgICAgICBjb2xzID0gYygiZ3JleTkwIiwgYnJld2VyLnBhbCg5LCJZbEduQnUiKSksCiAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgICAgICBvcmRlciA9IFQpICYgTm9BeGVzKCkgJiBOb0xlZ2VuZCgpCgpgYGAKCmBgYHtyfQpDaFAuZGF0YSA8LSBBZGRNb2R1bGVTY29yZShDaFAuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBsaXN0KGMoIkZnZjgiLCAiRmdmMTciLCAiQWRhbXRzMTUiLCAiRmdmYnAxIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjdHJsID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiU2VwdHVtIikKCkZlYXR1cmVQbG90KG9iamVjdCA9IENoUC5kYXRhICwKICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJTZXB0dW0xIiksCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgIGNvbHMgPSByZXYoYnJld2VyLnBhbCgxMCwiU3BlY3RyYWwiKSksCiAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgICAgICBvcmRlciA9IFQpICYgTm9BeGVzKCkKYGBgCgpgYGB7cn0KaGlzdChDaFAuZGF0YSRTZXB0dW0xLCBicmVha3MgPSAyMCkKCkNoUC5kYXRhJFNlcHRhbC5wcm9nIDwtIENoUC5kYXRhJFNlcHR1bTEgPiAwCmBgYAoKYGBge3J9CnAxIDwtIERpbVBsb3QoQ2hQLmRhdGEsCiAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgZ3JvdXAuYnkgPSAiU2VwdGFsLnByb2ciLAogICAgICAgIHB0LnNpemUgPSAxKSArIE5vQXhlcygpCgpwMiA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBDaFAuZGF0YSAsCiAgICAgICAgICAgIGZlYXR1cmVzID0gYygiRmdmMTciKSwKICAgICAgICAgICAgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgY29scyA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICAgICAgb3JkZXIgPSBUKSAmIE5vQXhlcygpICYgTm9MZWdlbmQoKQoKcDEgKyBwMgpgYGAKCmBgYHtyfQpDaFAuZGF0YSA8LSBzdWJzZXQoQ2hQLmRhdGEsCiAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSBTZXB0YWwucHJvZyA9PSBGQUxTRSAmIENoUC5kYXRhJFNwcmluZ18xID4gMTMwMCkKYGBgCgpgYGB7cn0KRGltUGxvdChDaFAuZGF0YSwKICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICBwdC5zaXplID0gMSwKICAgICAgICBjb2xzID0gIGMoIiM4M2MzYjgiLCAiIzAwOWZkYSIpKSArIE5vQXhlcygpCmBgYAoKIyMgRml0IHByaW5jaXBhbCBjdXJ2ZQoKYGBge3J9ClRyYWplY3Rvcmllcy5DaFAgPC0gQ2hQLmRhdGFAbWV0YS5kYXRhICU+JQogICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoIkJhcmNvZGVzIiwgIm5VTUkiLCAiU3ByaW5nXzEiLCAiU3ByaW5nXzIiKQpgYGAKCmBgYHtyfQpmaXQgPC0gcHJpbmNpcGFsX2N1cnZlKGFzLm1hdHJpeChUcmFqZWN0b3JpZXMuQ2hQWyxjKCJTcHJpbmdfMSIsICJTcHJpbmdfMiIpXSksCiAgICAgICAgICAgICAgICAgICAgICAgc21vb3RoZXI9J2xvd2VzcycsCiAgICAgICAgICAgICAgICAgICAgICAgdHJhY2U9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICBmID0gMC44LCAKICAgICAgICAgICAgICAgICAgICAgICBzdHJldGNoPTIpCgojVGhlIHByaW5jaXBhbCBjdXJ2ZSBzbW9vdGhlZApDaFAucGMubGluZSA8LSBhcy5kYXRhLmZyYW1lKGZpdCRzW29yZGVyKGZpdCRsYW1iZGEpLF0pIAoKI1BzZXVkb3RpbWUgc2NvcmUKVHJhamVjdG9yaWVzLkNoUCRQc2V1ZG90aW1lIDwtIGZpdCRsYW1iZGEvbWF4KGZpdCRsYW1iZGEpCgojSW52ZXJzZSB0aGUgc2NvcmUgaWYgcG9zaXRpdmUgY29ycmVsYXRpb24gd2l0aCBwcm9nZW5pdG9yIG1hcmtlcgppZiAoY29yKFRyYWplY3Rvcmllcy5DaFAkUHNldWRvdGltZSwgQ2hQLmRhdGFAYXNzYXlzJFNDVEBkYXRhWydIbWdhMicsIFRyYWplY3Rvcmllcy5DaFAkQmFyY29kZXNdKSA+IDApIHsKICBUcmFqZWN0b3JpZXMuQ2hQJFBzZXVkb3RpbWUgPC0gLShUcmFqZWN0b3JpZXMuQ2hQJFBzZXVkb3RpbWUgLSBtYXgoVHJhamVjdG9yaWVzLkNoUCRQc2V1ZG90aW1lKSkKfQoKQ2hQLmRhdGEkUHNldWRvdGltZSA8LSBUcmFqZWN0b3JpZXMuQ2hQJFBzZXVkb3RpbWUKYGBgCgpgYGB7cn0KRmVhdHVyZVBsb3Qob2JqZWN0ID0gQ2hQLmRhdGEsCiAgICAgICAgICAgIGZlYXR1cmVzID0gIlBzZXVkb3RpbWUiLAogICAgICAgICAgICBwdC5zaXplID0gMiwKICAgICAgICAgICAgY29scyA9IHJldihjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwobiA9MTAsIG5hbWUgPSAiU3BlY3RyYWwiKSkoMTAwKSksCiAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgICAgICBvcmRlciA9IFQpICYgTm9BeGVzKCkKYGBgCgojIFN1YnNldCBwcm9nZW5pdG9ycyBhbmQgZml0IGNlbGwgY3ljbGUgYXhpcwoKYGBge3J9ClByb2cuZGF0YSA8LSAgc3Vic2V0KENoUC5kYXRhLCBpZGVudHMgPSBjKCJDaFBfcHJvZ2VuaXRvcnMiKSkKCkRpbVBsb3QoUHJvZy5kYXRhLAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIHB0LnNpemUgPSAxLAogICAgICAgIGNvbHMgPSAgYygiIzAwOWZkYSIpKSArIE5vQXhlcygpCmBgYAoKYGBge3J9ClByb2cuZGF0YSA8LSBOb3JtYWxpemVEYXRhKFByb2cuZGF0YSwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMTAwMDAsIGFzc2F5ID0gIlJOQSIpCmBgYAoKCiMjIFByZXBhcmUgZGF0YSBmb3IgcmV2ZWxpbyBpbnB1dAoKIyMjIEV4cG9ydCBjb3VudHMgbWF0cml4CgpgYGB7cn0KcmF3Q291bnRzIDwtIGFzLm1hdHJpeChQcm9nLmRhdGFbWyJSTkEiXV1AY291bnRzKQpgYGAKCmBgYHtyfQojIEZpbHRlciBnZW5lcyBleHByZXNzZWQgYnkgbGVzcyB0aGFuIDEwIGNlbGxzCm51bS5jZWxscyA8LSBNYXRyaXg6OnJvd1N1bXMocmF3Q291bnRzID4gMCkKZ2VuZXMudXNlIDwtIG5hbWVzKHggPSBudW0uY2VsbHNbd2hpY2goeCA9IG51bS5jZWxscyA+PSAxMCldKQpyYXdDb3VudHMgPC0gcmF3Q291bnRzW2dlbmVzLnVzZSwgXQpgYGAKCmBgYHtyfQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIGMoInJhd0NvdW50cyIsICJDQ2dlbmVzIiwgIkNoUC5kYXRhIiwgIlByb2cuZGF0YSIpXSkKZ2MoKQpgYGAKCiMjIFJ1biBSZXZlbGlvCgpgYGB7cn0KQ0NnZW5lcyA8LSByZWFkLnRhYmxlKCJDQ2dlbmVzLmNzdiIsIHNlcCA9ICI7IiwgaGVhZGVyID0gVCkKYGBgCgoKV2UgY2FuIG5vdyBmb2xsb3cgdGhlIHR1dG9yaWFsIGZvcm0gdGhlIFtwYWNrYWdlIGdpdGh1YiBwYWdlXShodHRwczovL2dpdGh1Yi5jb20vZGFuaWVsc2NodzE4OC9SZXZlbGlvKSAKCmBgYHtyIGNhY2hlPVRSVUV9Cm15RGF0YSA8LSBjcmVhdGVSZXZlbGlvT2JqZWN0KHJhd0RhdGEgPSByYXdDb3VudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN5Y2xpY0dlbmVzID0gQ0NnZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG93ZXJuR2VuZUN1dG9mZiA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVwcGVyblVNSUN1dG9mZiA9IEluZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2NQaGFzZUFzc2lnbkJhc2VkT25JbmRpdmlkdWFsQmF0Y2hlcyA9IEYpCgpybSgicmF3Q291bnRzIikKZ2MoKQpgYGAKClRoZSBgZ2V0Q2VsbEN5Y2xlUGhhc2VBc3NpZ25JbmZvcm1hdGlvbmAgZmlsdGVyICJvdXRsaWVyIiBjZWxscyBmb3IgY2VsbCBjeWNsZSBwaGFzZSBhc3NpZ25hdGlvbi4gV2UgbW9kaWZpZWQgdGhlIGZ1bmN0aW9uIHRvIGtlZXAgYWxsIGNlbGxzIGFzIHdlIG9ic2VydmVkIHRoaXMgZG9lcyBub3QgYWZmZWN0IHRoZSBnbG9iYWwgY2VsbCBjeWNsZSBmaXR0aW5nIHByb2NlZHVyZQoKCmBgYHtyIGNhY2hlPVRSVUV9CnNvdXJjZSgiLi4vRnVuY3Rpb25zL2Z1bmN0aW9uc19Jbml0aWFsaXphdGlvbkNDUGhhc2VBc3NpZ25GaWx0ZXJpbmcuUiIpCgpteURhdGEgPC0gZ2V0Q2VsbEN5Y2xlUGhhc2VBc3NpZ25fYWxsY2VsbHMobXlEYXRhKQpgYGAKCmBgYHtyIGNhY2hlPVRSVUV9Cm15RGF0YSA8LSBnZXRQQ0FEYXRhKGRhdGFMaXN0ID0gbXlEYXRhKQpgYGAKCgpgYGB7ciBjYWNoZT1UUlVFfQpteURhdGEgPC0gZ2V0T3B0aW1hbFJvdGF0aW9uKGRhdGFMaXN0ID0gbXlEYXRhKQpnYygpCmBgYAoKCiMjIEdyYXBoaWNhbCBleHBsb3JhdGlvbiBvZiB0aGUgaW5mZXJlZCBjZWxsIGN5Y2xlIGF4aXMKCmBgYHtyfQpDZWxsQ3ljbGVkYXRhIDwtIGNiaW5kKGFzLmRhdGEuZnJhbWUodChteURhdGFAdHJhbnNmb3JtZWREYXRhJGRjJGRhdGFbMToyLF0pKSwKICAgICAgICAgICAgICAgICAgICAgICBuVU1JPSBteURhdGFAY2VsbEluZm8kblVNSSwKICAgICAgICAgICAgICAgICAgICAgICBSZXZlbGlvLnBoYXNlID0gZmFjdG9yKG15RGF0YUBjZWxsSW5mbyRjY1BoYXNlLCBsZXZlbHMgPSBjKCJHMS5TIiwgIlMiLCAiRzIiLCAiRzIuTSIsICJNLkcxIikpLAogICAgICAgICAgICAgICAgICAgICAgIFJldmVsaW8uY2M9IG15RGF0YUBjZWxsSW5mbyRjY1BlcmNlbnRhZ2VVbmlmb3JtbHlTcGFjZWQsCiAgICAgICAgICAgICAgICAgICAgICAgU2V1cmF0LmNjPSBQcm9nLmRhdGFAbWV0YS5kYXRhW215RGF0YUBjZWxsSW5mbyRjZWxsSUQsIkNDLkRpZmZlcmVuY2UiXSkKYGBgCgoKIyMjIENlbGxzIGRpc3RyaWJ1dGlvbiBpbiB0aGUgREMxLURDMiBzcGFjZQoKYGBge3J9CmdncGxvdChDZWxsQ3ljbGVkYXRhLCBhZXMoREMxLCBEQzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBSZXZlbGlvLnBoYXNlKSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMod2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVsxOjNdLCJncmV5NDAiLHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbNV0pKQpgYGAKCmBgYHtyfQpwMSA8LSBnZ3Bsb3QoQ2VsbEN5Y2xlZGF0YSwgYWVzKERDMSwgREMyKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gUmV2ZWxpby5waGFzZSkpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbMTozXSwiZ3JleTQwIix3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzVdKSkKCnAyIDwtIGdncGxvdChDZWxsQ3ljbGVkYXRhLCBhZXMoREMxLCBEQzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9UmV2ZWxpby5jYyksIHNpemU9Miwgc2hhcGU9MTYpICsgCiAgICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnM9cmV2KGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbChuID0xMSwgbmFtZSA9ICJTcGVjdHJhbCIpKSgxMDApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZT0nUmV2ZWxpb19jYycpCgoKcDEgKyBwMgpgYGAKCgpgYGB7cn0KZ2dwbG90KENlbGxDeWNsZWRhdGEsIGFlcyh4PSBSZXZlbGlvLmNjLCB5PSBuVU1JLzEwMDAwKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBSZXZlbGlvLnBoYXNlKSwgc2l6ZT0wLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbMTozXSwiZ3JleTQwIix3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzVdKSkgKwogICAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgZmlsbD0iZ3JleSIpICsKICAgICAgICB5bGltKDAsTkEpCmBgYAoKIyMgSW1wb3J0IGNvb3JkaW5hdGVzCgpgYGB7cn0KUHJvZy5kYXRhJFJldmVsaW8uREMxIDwtIENlbGxDeWNsZWRhdGEkREMxClByb2cuZGF0YSRSZXZlbGlvLkRDMiA8LSBDZWxsQ3ljbGVkYXRhJERDMgoKUHJvZy5kYXRhJFJldmVsaW8ucGhhc2UgPC0gQ2VsbEN5Y2xlZGF0YSRSZXZlbGlvLnBoYXNlClByb2cuZGF0YSRSZXZlbGlvLmNjIDwtIENlbGxDeWNsZWRhdGEkUmV2ZWxpby5jYwpgYGAKCmBgYHtyIGZpZy5kaW09Yyg2LCA5KX0KcDEgPC0gRmVhdHVyZVBsb3Qob2JqZWN0ID0gUHJvZy5kYXRhLAogICAgICAgICAgICBmZWF0dXJlcyA9ICJSZXZlbGlvLmNjIiwKICAgICAgICAgICAgcHQuc2l6ZSA9IDEsCiAgICAgICAgICAgIGNvbHMgPSByZXYoYnJld2VyLnBhbCgxMCwiU3BlY3RyYWwiKSksCiAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgICAgICBvcmRlciA9IFQpICYgTm9BeGVzKCkKCnAyIDwtIERpbVBsb3Qob2JqZWN0ID0gUHJvZy5kYXRhLAogICAgICAgIGdyb3VwLmJ5ID0gIlJldmVsaW8ucGhhc2UiLAogICAgICAgIHB0LnNpemUgPSAxLAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIGNvbHMgPSAgYyh3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzE6M10sImdyZXk0MCIsd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVs1XSkpICYgTm9BeGVzKCkKCnAzIDwtIEZlYXR1cmVQbG90KG9iamVjdCA9IFByb2cuZGF0YSwKICAgICAgICAgICAgZmVhdHVyZXMgPSAiUHNldWRvdGltZSIsCiAgICAgICAgICAgIHB0LnNpemUgPSAyLAogICAgICAgICAgICBjb2xzID0gcmV2KGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbChuID0xMCwgbmFtZSA9ICJTcGVjdHJhbCIpKSgxMDApKSwKICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgIG9yZGVyID0gVCkgJiBOb0F4ZXMoKQoKcDEgKyBwMiArIHAzIApgYGAKCmBgYHtyfQpUcmFqZWN0b3JpZXMucHJvZ2VuaXRvcnMgPC0gUHJvZy5kYXRhQG1ldGEuZGF0YSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChCYXJjb2RlcywgblVNSSwgU3ByaW5nXzEsIFNwcmluZ18yLCBQc2V1ZG90aW1lKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShDeWNsaW5nLmF4aXM9IFByb2cuZGF0YSRSZXZlbGlvLmNjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGhhc2UgPSBQcm9nLmRhdGEkUmV2ZWxpby5waGFzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdtbmM9IFByb2cuZGF0YUBhc3NheXMkUk5BQGRhdGFbIkdtbmMiLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUdHI9IFByb2cuZGF0YUBhc3NheXMkUk5BQGRhdGFbIlR0ciIsXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEh0cjJjPSBQcm9nLmRhdGFAYXNzYXlzJFJOQUBkYXRhWyJIdHIyYyIsXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRvcDJhPSBQcm9nLmRhdGFAYXNzYXlzJFJOQUBkYXRhWyJUb3AyYSIsXSkKYGBgCgpgYGB7cn0KcDEgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5wcm9nZW5pdG9ycywgYWVzKHg9IFBzZXVkb3RpbWUsIHk9IEN5Y2xpbmcuYXhpcykpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gUGhhc2UpLCBzaXplPTEuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMod2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVsxOjNdLCJncmV5NDAiLHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbNV0pKQoKcDIgPC0gVHJhamVjdG9yaWVzLnByb2dlbml0b3JzICU+JSBhcnJhbmdlKEdtbmMpICU+JQogICAgICBnZ3Bsb3QoYWVzKHg9IFBzZXVkb3RpbWUsIHk9IEN5Y2xpbmcuYXhpcykpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1HbW5jKSwgc2l6ZT0xLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3VycyA9YygiZ3JleTkwIiwgYnJld2VyLnBhbCg5LCJZbEduQnUiKSkpCgpwMyA8LSBUcmFqZWN0b3JpZXMucHJvZ2VuaXRvcnMgJT4lIGFycmFuZ2UoVHRyKSAlPiUKICAgICAgZ2dwbG90KGFlcyh4PSBQc2V1ZG90aW1lLCB5PSBDeWNsaW5nLmF4aXMpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9VHRyKSwgc2l6ZT0xLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3VycyA9YygiZ3JleTkwIiwgYnJld2VyLnBhbCg5LCJZbEduQnUiKSkpCgpwNCA8LSBUcmFqZWN0b3JpZXMucHJvZ2VuaXRvcnMgJT4lIGFycmFuZ2UoSHRyMmMpICU+JQogICAgICBnZ3Bsb3QoYWVzKHg9IFBzZXVkb3RpbWUsIHk9IEN5Y2xpbmcuYXhpcykpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1IdHIyYyksIHNpemU9MS41KSArCiAgICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnMgPWMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpKQoKcDEgKyBwMiArIHAzICsgcDQgICsgcGF0Y2h3b3JrOjpwbG90X2xheW91dChuY29sID0gMikKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCJUcmFqZWN0b3JpZXMucHJvZ2VuaXRvcnMiLCAiQ2hQLmRhdGEiKV0pCmdjKCkKYGBgCgpJbXBvcnQgcHJvZ2VuaXRvcnMgY3ljbGluZyBjb29yZGluYXRlcyBpbiB0aGUgZnVsbCBkYXRhc2V0CgpgYGB7cn0KQ2hQLmRhdGEkQ3ljbGluZy5heGlzIDwtIHNhcHBseShDaFAuZGF0YSRCYXJjb2RlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh4ICVpbiUgVHJhamVjdG9yaWVzLnByb2dlbml0b3JzJEJhcmNvZGVzKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gVHJhamVjdG9yaWVzLnByb2dlbml0b3JzW3gsICJDeWNsaW5nLmF4aXMiXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gTkEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSkKYGBgCgpgYGB7cn0KRmVhdHVyZVBsb3Qob2JqZWN0ID0gQ2hQLmRhdGEsCiAgICAgICAgICAgIGZlYXR1cmVzID0gIlBzZXVkb3RpbWUiLAogICAgICAgICAgICBwdC5zaXplID0gMiwKICAgICAgICAgICAgY29scyA9IHJldihjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwobiA9MTAsIG5hbWUgPSAiU3BlY3RyYWwiKSkoMTAwKSksCiAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgICAgICBvcmRlciA9IFQpICYgTm9BeGVzKCkKYGBgCgoKIyBVc2UgbW9ub2NsZTIgdG8gbW9kZWwgZ2VuZSBleHByZXNzaW9uIGFsb25nIGRpZmZlcmVudGl0aW9uIGF4aXMKCiMjIEluaXRpYWxpemUgYSBtb25vY2xlIG9iamVjdAoKYGBge3J9CiMgVHJhbnNmZXIgbWV0YWRhdGEKbWV0YS5kYXRhIDwtIGRhdGEuZnJhbWUoQmFyY29kZT0gQ2hQLmRhdGEkQmFyY29kZXMsCiAgICAgICAgICAgICAgICAgICAgICAgIFBzZXVkb3RpbWU9IENoUC5kYXRhJFBzZXVkb3RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgIENlbGwuY3ljbGU9IENoUC5kYXRhJFBoYXNlKQoKQW5ub3QuZGF0YSAgPC0gbmV3KCdBbm5vdGF0ZWREYXRhRnJhbWUnLCBkYXRhID0gbWV0YS5kYXRhKQoKIyBUcmFuc2ZlciBjb3VudHMgZGF0YQpDaFAuZGF0YSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhDaFAuZGF0YSwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAyMDAwKQp2YXIuZ2VuZXMgPC0gVmFyaWFibGVGZWF0dXJlcyhDaFAuZGF0YSkKY291bnQuZGF0YSA9IGRhdGEuZnJhbWUoZ2VuZV9zaG9ydF9uYW1lID0gcm93bmFtZXMoQ2hQLmRhdGFbWyJSTkEiXV1AZGF0YVt2YXIuZ2VuZXMsXSksCiAgICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IHJvd25hbWVzKENoUC5kYXRhW1siUk5BIl1dQGRhdGFbdmFyLmdlbmVzLF0pKQoKZmVhdHVyZS5kYXRhIDwtIG5ldygnQW5ub3RhdGVkRGF0YUZyYW1lJywgZGF0YSA9IGNvdW50LmRhdGEpCgojIENyZWF0ZSB0aGUgQ2VsbERhdGFTZXQgb2JqZWN0IGluY2x1ZGluZyB2YXJpYWJsZSBnZW5lcyBvbmx5CmdibV9jZHMgPC0gbmV3Q2VsbERhdGFTZXQoQ2hQLmRhdGFbWyJSTkEiXV1AY291bnRzW3Zhci5nZW5lcyxdLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YSA9IEFubm90LmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZURhdGEgPSBmZWF0dXJlLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbG93ZXJEZXRlY3Rpb25MaW1pdCA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwcmVzc2lvbkZhbWlseSA9IG5lZ2Jpbm9taWFsKCkpCmBgYAoKYGBge3J9CmdibV9jZHMgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhnYm1fY2RzKQpnYm1fY2RzIDwtIGVzdGltYXRlRGlzcGVyc2lvbnMoZ2JtX2NkcykKZ2JtX2NkcyA8LSBkZXRlY3RHZW5lcyhnYm1fY2RzLCBtaW5fZXhwciA9IDAuMSkKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCJDaFAuZGF0YSIsICJnYm1fY2RzIildKQpnYygpCmBgYAoKIyMgVGVzdCBlYWNoIGdlbmUgdHJlbmQgb3ZlciBwc2V1ZG90aW1lIHNjb3JlCgpgYGB7ciBjYWNoZT1UUlVFfQpwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmIDwtIGRpZmZlcmVudGlhbEdlbmVUZXN0KGdibV9jZHNbZkRhdGEoZ2JtX2NkcykkbnVtX2NlbGxzX2V4cHJlc3NlZCA+PSAzMCxdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGxNb2RlbEZvcm11bGFTdHIgPSAifnNtLm5zKFBzZXVkb3RpbWUsIGRmID0gMykiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjZWRNb2RlbEZvcm11bGFTdHIgPSAifjEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkgLSAyKQpgYGAKCmBgYHtyfQojIEZpbHRlciBnZW5lcyBiYXNlZCBvbiBGRFIKcHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZCA8LSBwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmICU+JSBmaWx0ZXIocXZhbCA8IDVlLTUpCmBgYAoKIyMgU21vb3RoIGV4cHJlc3Npb24gb2Ygc2lnbmlmaWNhdGl2ZSBnZW5lcwoKYGBge3IgY2FjaGU9VFJVRX0KIyBDcmVhdGUgYSBuZXcgdmVjdG9yIG9mIDIwMCBwb2ludHMKblBvaW50cyA8LSAyMDAKbmV3X2RhdGEgPC0gZGF0YS5mcmFtZShQc2V1ZG90aW1lID0gc2VxKG1pbihwRGF0YShnYm1fY2RzKSRQc2V1ZG90aW1lKSwgbWF4KHBEYXRhKGdibV9jZHMpJFBzZXVkb3RpbWUpLCBsZW5ndGgub3V0ID0gblBvaW50cykpCgojIFNtb290aCBnZW5lIGV4cHJlc3Npb24KU21vb3RoLmN1cnZlLm1hdHJpeCA8LSBnZW5TbW9vdGhDdXJ2ZXMoZ2JtX2Nkc1thcy5jaGFyYWN0ZXIocHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZCRnZW5lX3Nob3J0X25hbWUpLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyZW5kX2Zvcm11bGEgPSAifnNtLm5zKFBzZXVkb3RpbWUsIGRmID0gMykiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGl2ZV9leHByID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3X2RhdGEgPSBuZXdfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yZXM9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpIC0gMikKYGBgCgojIyBDbHVzdGVyIGdlbmVzIGFuZCBwbG90IGhlYXRtYXAKCmBgYHtyfQojIyBDbHVzdGVyIGdlbmUgYnkgZXhwcmVzc2lvbiBwcm9maWxlcwpQc2V1ZG90aW1lLmdlbmVzLmNsdXN0ZXJzIDwtIGNsdXN0ZXI6OnBhbShhcy5kaXN0KCgxIC0gY29yKE1hdHJpeDo6dChTbW9vdGguY3VydmUubWF0cml4KSxtZXRob2QgPSAicGVhcnNvbiIpKSksIGs9IDQpCgpDaFAuR2VuZS5keW5hbWlxdWUgPC0gZGF0YS5mcmFtZShHZW5lPSBuYW1lcyhQc2V1ZG90aW1lLmdlbmVzLmNsdXN0ZXJzJGNsdXN0ZXJpbmcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYXZlcz0gUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW5lLkNsdXN0ZXJzID0gUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxLnZhbCA9IHBzZXVkby5tYXR1cmF0aW9uLmRpZmYuZmlsdGVyZWQkcXZhbAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSBhcnJhbmdlKEdlbmUuQ2x1c3RlcnMpCgpyb3cubmFtZXMoQ2hQLkdlbmUuZHluYW1pcXVlKSA8LSBDaFAuR2VuZS5keW5hbWlxdWUkR2VuZQpDaFAuR2VuZS5keW5hbWlxdWUkR2VuZS5DbHVzdGVycyA8LSBwYXN0ZTAoIkNsdXN0LiIsIENoUC5HZW5lLmR5bmFtaXF1ZSRHZW5lLkNsdXN0ZXJzKQpgYGAKCmBgYHtyfQojIE9yZGVyIHRoZSByb3dzIHVzaW5nIHNlcmlhdGlvbgpkc3QgPC0gYXMuZGlzdCgoMS1jb3Ioc2NhbGUodChTbW9vdGguY3VydmUubWF0cml4KSksIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkKcm93LnNlciA8LSBzZXJpYXRpb246OnNlcmlhdGUoZHN0LCBtZXRob2QgPSJSMkUiKSAjIlIyRSIgI1RTUCAjIkdXIiAiR1dfd2FyZCIKZ2VuZS5vcmRlciA8LSByb3duYW1lcyhTbW9vdGguY3VydmUubWF0cml4W3NlcmlhdGlvbjo6Z2V0X29yZGVyKHJvdy5zZXIpLF0pCgpwYWwgPC0gd2VzX3BhbGV0dGUoIkRhcmplZWxpbmcxIikKYW5uby5jb2xvcnMgPC0gbGlzdChDZWxsLnN0YXRlID0gYyhDeWNsaW5nX1JHPSIjMDQ2YzlhIiwgRGlmZmVyZW50aWF0aW5nX2NlbGxzPSIjZWJjYjJlIiksCiAgICAgICAgICAgICAgICAgICAgR2VuZS5DbHVzdGVycyA9IGMoQ2x1c3QuMSA9cGFsWzFdICwgQ2x1c3QuMj1wYWxbMl0sIENsdXN0LjM9cGFsWzNdLCBDbHVzdC40PXBhbFs0XSwgQ2x1c3QuNT1wYWxbNV0pKQoKcGhlYXRtYXA6OnBoZWF0bWFwKFNtb290aC5jdXJ2ZS5tYXRyaXhbZ2VuZS5vcmRlcixdLAogICAgICAgICAgICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBDaFAuR2VuZS5keW5hbWlxdWUgJT4lIGRwbHlyOjpzZWxlY3QoR2VuZS5DbHVzdGVycyksCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm8uY29sb3JzLAogICAgICAgICAgICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBzaG93X3Jvd25hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIGZvbnRzaXplX3JvdyA9IDgsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICB2aXJpZGlzOjp2aXJpZGlzKDkpLAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKC0yLjUsMi41LCBsZW5ndGgub3V0ID0gOSksCiAgICAgICAgICAgICAgICAgICBtYWluID0gIiIpCmBgYAoKIyBHTyB0ZXJtIGVucmljaG1lbnQgaW4gZ2VuZSBjbHVzdGVycyB1c2luZyBncHJvZmlsZXIyCgpgYGB7cn0KQ2hQLmdvc3RyZXMgPC0gZ29zdChxdWVyeSA9IGxpc3QoIkNsdXN0LjEiID0gQ2hQLkdlbmUuZHluYW1pcXVlICU+JSBmaWx0ZXIoR2VuZS5DbHVzdGVycyA9PSAiQ2x1c3QuMSIpICU+JSBwdWxsKEdlbmUpICU+JSBhcy5jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2x1c3QuMiIgPSBDaFAuR2VuZS5keW5hbWlxdWUgJT4lIGZpbHRlcihHZW5lLkNsdXN0ZXJzID09ICJDbHVzdC4yIikgJT4lIHB1bGwoR2VuZSkgJT4lIGFzLmNoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDbHVzdC4zIiA9IENoUC5HZW5lLmR5bmFtaXF1ZSAlPiUgZmlsdGVyKEdlbmUuQ2x1c3RlcnMgPT0gIkNsdXN0LjMiKSAlPiUgcHVsbChHZW5lKSAlPiUgYXMuY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNsdXN0LjQiID0gQ2hQLkdlbmUuZHluYW1pcXVlICU+JSBmaWx0ZXIoR2VuZS5DbHVzdGVycyA9PSAiQ2x1c3QuNCIpICU+JSBwdWxsKEdlbmUpICU+JSBhcy5jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2x1c3QuNSIgPSBDaFAuR2VuZS5keW5hbWlxdWUgJT4lIGZpbHRlcihHZW5lLkNsdXN0ZXJzID09ICJDbHVzdC41IikgJT4lIHB1bGwoR2VuZSkgJT4lIGFzLmNoYXJhY3RlcigpKSwKICAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gIm1tdXNjdWx1cyIsIG9yZGVyZWRfcXVlcnkgPSBGLCAKICAgICAgICAgICAgICAgIG11bHRpX3F1ZXJ5ID0gRiwgc2lnbmlmaWNhbnQgPSBULCBleGNsdWRlX2llYSA9IFQsIAogICAgICAgICAgICAgICAgbWVhc3VyZV91bmRlcnJlcHJlc2VudGF0aW9uID0gRiwgZXZjb2RlcyA9IFQsIAogICAgICAgICAgICAgICAgdXNlcl90aHJlc2hvbGQgPSAwLjA1LCBjb3JyZWN0aW9uX21ldGhvZCA9ICJmZHIiLCAKICAgICAgICAgICAgICAgIGRvbWFpbl9zY29wZSA9ICJhbm5vdGF0ZWQiLCBjdXN0b21fYmcgPSBOVUxMLCAKICAgICAgICAgICAgICAgIG51bWVyaWNfbnMgPSAiIiwgc291cmNlcyA9IGMoIkdPOk1GIiwgIkdPOkJQIiksIGFzX3Nob3J0X2xpbmsgPSBGKQpgYGAKCmBgYHtyfQpDaFAuZ29zdHJlcyA8LSBnb3N0KHF1ZXJ5ID0gYXMuY2hhcmFjdGVyKENoUC5HZW5lLmR5bmFtaXF1ZSRHZW5lKSwKICAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gIm1tdXNjdWx1cyIsIG9yZGVyZWRfcXVlcnkgPSBGLCAKICAgICAgICAgICAgICAgIG11bHRpX3F1ZXJ5ID0gRiwgc2lnbmlmaWNhbnQgPSBULCBleGNsdWRlX2llYSA9IFQsIAogICAgICAgICAgICAgICAgbWVhc3VyZV91bmRlcnJlcHJlc2VudGF0aW9uID0gRiwgZXZjb2RlcyA9IFQsIAogICAgICAgICAgICAgICAgdXNlcl90aHJlc2hvbGQgPSAwLjA1LCBjb3JyZWN0aW9uX21ldGhvZCA9ICJmZHIiLCAKICAgICAgICAgICAgICAgIGRvbWFpbl9zY29wZSA9ICJhbm5vdGF0ZWQiLCBjdXN0b21fYmcgPSBOVUxMLCAKICAgICAgICAgICAgICAgIG51bWVyaWNfbnMgPSAiIiwgc291cmNlcyA9IGMoIkdPOk1GIiwgIkdPOkJQIiksIGFzX3Nob3J0X2xpbmsgPSBGKQpgYGAKCiMgU2F2ZSByZXN1bHRzCgpgYGB7cn0Kd3JpdGUudGFibGUoQ2hQLkdlbmUuZHluYW1pcXVlLCAiQ2hQLkdlbmUuZHluYW1pcXVlLmNzdiIsIHNlcCA9ICI7IikKCndyaXRlLnRhYmxlKGFwcGx5KENoUC5nb3N0cmVzJHJlc3VsdCwyLGFzLmNoYXJhY3RlciksCiAgICAgICAgICAgICJDaFAuZ29zdHJlcy5jc3YiLCBzZXAgPSAiOyIsIHF1b3RlID0gRiwgcm93Lm5hbWVzID0gRikKYGBgCgoKIyBTZXNzaW9uIEluZm8KCmBgYHtyfQojZGF0ZQpmb3JtYXQoU3lzLnRpbWUoKSwgIiVkICVCLCAlWSwgJUgsJU0iKQoKI1BhY2thZ2VzIHVzZWQKc2Vzc2lvbkluZm8oKQpgYGA=