Load libraries

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

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

Load integrated datasets

WT.KO.integrated <- readRDS("WT_KO.integrated.RDS")
DimPlot(WT.KO.integrated,
        reduction = "integrated_spring",
        group.by = "Lineage",
        pt.size = 1,
        cols =   c("#cc391b","#e7823a","#969696","#026c9a")
        ) + NoAxes()

Pseudotime in the WT

Extract CR and CP neurons

Neurons.data <-  subset(WT.KO.integrated,
                        subset = Lineage %in% c("Cajal-Retzius_neurons", "Pallial_neurons") & orig.ident == "Hem1" & Cell.ident.WT %in% c("Cajal-Retzius_neurons", "Pallial_neurons"))

DimPlot(Neurons.data,
        group.by = "Lineage",
        reduction = "integrated_spring",
        pt.size = 1,
        cols =  c("#cc391b","#026c9a")
        ) + NoAxes()

rm(WT.KO.integrated)
gc()
##            used  (Mb) gc trigger   (Mb)   max used   (Mb)
## Ncells  3376158 180.4    6140847  328.0    5661690  302.4
## Vcells 85680579 653.7  997799792 7612.7 1064977766 8125.2

Fit principale curve on the two lineages

Cajal-Retzius cells

Trajectories.Hem <- Neurons.data@meta.data %>%
                    select("Barcodes", "nCount_RNA", "Spring_1", "Spring_2", "AP_signature","BP_signature", "EN_signature", "LN_signature", "Lineage") %>%
                    filter(Lineage == "Cajal-Retzius_neurons")
fit <- principal_curve(as.matrix(Trajectories.Hem[,c("Spring_1", "Spring_2")]),
                       smoother='lowess',
                       trace=TRUE,
                       f = .7,
                       stretch=0)
## Starting curve---distance^2: 11526697569
## Iteration 1---distance^2: 13397620
## Iteration 2---distance^2: 13420664
## Iteration 3---distance^2: 13422248
#The principal curve smoothed
Hem.pc.line <- as.data.frame(fit$s[order(fit$lambda),]) 

#Pseudotime score
Trajectories.Hem$PseudotimeScore <- fit$lambda/max(fit$lambda)
if (cor(Trajectories.Hem$PseudotimeScore, Neurons.data@assays$RNA@data['Hmga2', Trajectories.Hem$Barcodes]) > 0) {
  Trajectories.Hem$PseudotimeScore <- -(Trajectories.Hem$PseudotimeScore - max(Trajectories.Hem$PseudotimeScore))
}

Pallial neurons

Trajectories.Pallial <- Neurons.data@meta.data %>%
                        select("Barcodes", "nCount_RNA", "Spring_1", "Spring_2", "AP_signature","BP_signature", "EN_signature", "LN_signature", "Lineage") %>%
                        filter(Lineage == "Pallial_neurons")
fit <- principal_curve(as.matrix(Trajectories.Pallial[,c("Spring_1", "Spring_2")]),
                       smoother='lowess',
                       trace=TRUE,
                       f = .7,
                       stretch=0)
## Starting curve---distance^2: 6447222830
## Iteration 1---distance^2: 10179991
## Iteration 2---distance^2: 10187204
#The principal curve smoothed
Pallial.pc.line <- as.data.frame(fit$s[order(fit$lambda),])

#Pseudotime score
Trajectories.Pallial$PseudotimeScore <- fit$lambda/max(fit$lambda)
if (cor(Trajectories.Pallial$PseudotimeScore, Neurons.data@assays$RNA@data['Hmga2', Trajectories.Pallial$Barcodes]) > 0) {
  Trajectories.Pallial$PseudotimeScore <- -(Trajectories.Pallial$PseudotimeScore - max(Trajectories.Pallial$PseudotimeScore))
}

Combine the two trajectories’ data

Trajectories.neurons <- rbind(Trajectories.Pallial, Trajectories.Hem)
cols <- brewer.pal(n =11, name = "Spectral")

ggplot(Trajectories.neurons, aes(Spring_1, Spring_2)) +
  geom_point(aes(color=PseudotimeScore), size=2, shape=16) + 
  scale_color_gradientn(colours=rev(cols), name='Speudotime score') +
  geom_line(data=Pallial.pc.line, color="#026c9a", size=0.77) +
  geom_line(data=Hem.pc.line, color="#cc391b", size=0.77)

Shift Pseudotime in both lineage

Since we observe the first 25% of both trajectories are occupied by few, likely progenitor cells, we shift this cell along the axis

Pseudotime.intervals <- Trajectories.neurons%>%
                          select(Lineage, PseudotimeScore) %>%
                          mutate(Pseudotime.bins = cut(Trajectories.neurons$PseudotimeScore, seq(0, max(Trajectories.neurons$PseudotimeScore) + 0.05, 0.05), dig.lab = 2, right = FALSE)) %>%
                          group_by(Lineage, Pseudotime.bins) %>%
                          summarise(n=n())

ggplot(Pseudotime.intervals, aes(x=Pseudotime.bins, y=n, fill=Lineage)) +
        geom_bar(stat = "identity", width = 0.90) +
        theme(axis.text.x = element_text(angle = 45, hjust=1))+
        scale_fill_manual(values= c("#cc391b", "#026c9a"))

score <- sapply(Trajectories.neurons$PseudotimeScore,
                FUN = function(x) if (x <= 0.2) {x= 0.2} else { x=x })

Trajectories.neurons$PseudotimeScore.shifted <- (score - min(score)) / (max(score) - min(score))
ggplot(Trajectories.neurons, aes(x= PseudotimeScore.shifted, y= nCount_RNA/10000)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(method="loess", n= 50, aes(color= Lineage)) +
        ylim(0,NA)

rm(list = ls()[!ls() %in% c("Trajectories.neurons")])

Load progenitors with cell cycle trajectory fitted

Progenitors.data <- readRDS("../ProgenitorsDiversity/Progenitors.RDS")
table(Progenitors.data$Cell_ident)
## 
## Dorso-Medial_pallium                  Hem       Medial_pallium 
##                 3451                 1954                 2719

To balance the number of progenitors in both domain we will only work with Hem and Medial_pallium annotated cells. Since we are using pallial cell to contrast CR specific trajectory we think this approximation will not significantly affect our analysis.

Progenitors.data <-  subset(Progenitors.data, subset = Cell_ident %in% c("Hem", "Medial_pallium") & orig.ident == "Hem1")
p1 <- DimPlot(Progenitors.data,
        reduction = "spring",
        pt.size = 0.5,
        cols =  c("#e3c148", "#e46b6b")) + NoAxes()

p2 <- FeaturePlot(object = Progenitors.data,
            features = "Revelio.cc",
            pt.size = 0.5,
            cols = rev(brewer.pal(10,"Spectral")),
            reduction = "spring",
            order = T) & NoAxes()

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

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

Combined progenitors and neurons along Pseudotime

# Start with neurons data
Trajectories.all <- Trajectories.neurons %>% select(Barcodes, nCount_RNA, Spring_1, Spring_2, Lineage)

Trajectories.all$Pseudotime <- Trajectories.neurons$PseudotimeScore.shifted + 0.5
Trajectories.all$Phase <- NA
# Add progenitors data
Trajectories.progenitors <- Progenitors.data@meta.data %>%
                              select(Barcodes, nCount_RNA, Spring_1, Spring_2) %>% 
                              mutate(Lineage= ifelse(Progenitors.data$Cell_ident == "Medial_pallium", "Pallial_neurons", "Cajal-Retzius_neurons") ,
                                     Pseudotime= Progenitors.data$Revelio.cc/2,
                                     Phase = Progenitors.data$Revelio.phase)
Trajectories.WT<- rbind(Trajectories.all, Trajectories.progenitors)

Trajectories.WT$Phase <- factor(Trajectories.WT$Phase, levels = c("G1.S", "S", "G2", "G2.M", "M.G1"))
p1 <- ggplot(Trajectories.WT, aes(Spring_1, Spring_2)) +
        geom_point(aes(color=Pseudotime), size=0.5) + 
        scale_color_gradientn(colours=rev(brewer.pal(n =11, name = "Spectral")), name='Pseudotime score')

p2 <- ggplot(Trajectories.WT, aes(Spring_1, Spring_2)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a"))

p1 + p2

p1 <- ggplot(Trajectories.WT, aes(x= Pseudotime, y= nCount_RNA/10000)) +
        geom_point(aes(color= 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)

p2 <- ggplot(Trajectories.WT, aes(x= Pseudotime, y= nCount_RNA/10000)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(method="loess", n= 50, fill="grey") +
        ylim(0,NA)

p1 / p2

rm(list = ls()[!ls() %in% c("Trajectories.WT")])
gc()
##           used (Mb) gc trigger   (Mb)   max used   (Mb)
## Ncells 3462712  185    6140847  328.0    6140847  328.0
## Vcells 6021756   46  798239834 6090.1 1064977766 8125.2

Pseudotime in the KO

WT.KO.integrated <- readRDS("WT_KO.integrated.RDS")

Extract CR and pallial neurons

Neurons.data <-  subset(WT.KO.integrated,
                        subset = Lineage %in% c("Cajal-Retzius_neurons", "Pallial_neurons") & orig.ident == "Gmnc_KO" & Cell.ident.KO %in% c("Neuron_prob.2", "Neuron_prob.3"))

DimPlot(Neurons.data,
        group.by = "Lineage",
        reduction = "integrated_spring",
        pt.size = 1,
        cols =  c("#cc391b","#026c9a")
        ) + NoAxes()

Fit principale curve on the two lineages

fit <- principal_curve(as.matrix(Neurons.data@meta.data[,c("Spring_1", "Spring_2")]),
                       smoother='lowess',
                       trace=TRUE,
                       f = 1,
                       stretch=0)
## Starting curve---distance^2: 76242448164
## Iteration 1---distance^2: 48308567
## Iteration 2---distance^2: 49237931
## Iteration 3---distance^2: 50119026
## Iteration 4---distance^2: 51105768
## Iteration 5---distance^2: 51819999
## Iteration 6---distance^2: 52369574
## Iteration 7---distance^2: 52732675
## Iteration 8---distance^2: 52936995
## Iteration 9---distance^2: 53060989
## Iteration 10---distance^2: 53117718
#Pseudotime score
PseudotimeScore <- fit$lambda/max(fit$lambda)

if (cor(PseudotimeScore, Neurons.data@assays$RNA@data['Hmga2', ]) > 0) {
  Neurons.data$PseudotimeScore <- -(PseudotimeScore - max(PseudotimeScore))
}

cols <- brewer.pal(n =11, name = "Spectral")

ggplot(Neurons.data@meta.data, aes(Spring_1, Spring_2)) +
  geom_point(aes(color=PseudotimeScore), size=2, shape=16) + 
  scale_color_gradientn(colours=rev(cols), name='Pseudotime score')

Shift pseudotime score

score <- sapply(Neurons.data$PseudotimeScore,
                FUN = function(x) if (x <= 0.25) {x= 0.25} else { x=x })

Neurons.data$PseudotimeScore.shifted <- (score - min(score)) / (max(score) - min(score))
ggplot(Neurons.data@meta.data, aes(x= PseudotimeScore.shifted, y= nCount_RNA/10000)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(method="loess", n= 50, aes(color= Lineage)) +
        ylim(0,NA)

Fit cell cycle trajectory on progenitors

To balance the number of progenitors in both domain we will only work with Hem and Medial_pallium annotated cells. Since we are using pallial cell to contrast CR specific trajectory we think this approximation will not significantly affect our analysis.

Progenitors.data <-  subset(WT.KO.integrated,
                            subset = Lineage %in% c("Cajal-Retzius_neurons", "Pallial_neurons") & orig.ident == "Gmnc_KO" & Cell.ident.KO %in% c("Hem", "Medial_pallium"))

DimPlot(Progenitors.data,
        reduction = "integrated_spring",
        group.by = "Cell.ident.KO",
        pt.size = 1,
        cols =  c("#cc391b","#026c9a")
        ) + NoAxes()

rm(WT.KO.integrated)
gc()
##             used   (Mb) gc trigger   (Mb)   max used  (Mb)
## Ncells   3510726  187.5    6140847  328.0    6140847   328
## Vcells 298010590 2273.7 1144880851 8734.8 1331680305 10160

Prepare data for Revelio

rawCounts <- as.matrix(Progenitors.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, ]

Run Revelio

CCgenes <- read.table("../ChoroidPlexus_trajectory/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-06-13 15:19:40: reading data: 5.59secs
rm("rawCounts")
gc()
##             used   (Mb) gc trigger   (Mb)   max used    (Mb)
## Ncells   3503173  187.1    6126737  327.3    6126737   327.3
## Vcells 357827865 2730.1 1144872590 8734.7 1331671700 10159.9

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-06-13 15:19:55: assigning cell cycle phases: 32.85secs
myData <- getPCAData(dataList = myData)
## 2022-06-13 15:20:45: calculating PCA: 25.35secs
myData <- getOptimalRotation(dataList = myData)
## 2022-06-13 15:21:10: calculating optimal rotation: 14.21secs

Graphical assesment of cell cycle fitting

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,
                       Domain= Progenitors.data$Cell.ident.KO)
p1 <- ggplot(CellCycledata, aes(DC1, DC2)) +
        geom_point(aes(color = Revelio.phase), size= 0.5) +
        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 = Domain), size = 0.5) +
        scale_color_manual(values= c("#cc391b","#026c9a"))

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

p4 <- 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)

(p1 + p2) /(p3 + p4)

Progenitors.data$Revelio.phase <- CellCycledata$Revelio.phase
Progenitors.data$Revelio.cc <- CellCycledata$Revelio.cc

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

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

p1 / p2

Transfert learn cell cycle axis

Progenitors <- Progenitors.data@meta.data %>% select(Barcodes, nCount_RNA, Spring_1, Spring_2, Lineage)

Progenitors$PseudotimeScore <- CellCycledata$Revelio.cc
Progenitors$nUMI <- Progenitors.data$nCount_RNA

Combine Progenitors and differentiating neurons data

# Start with neurons data
Trajectories.all <- Neurons.data@meta.data %>% select(Barcodes, nCount_RNA, Spring_1, Spring_2, Lineage)

Trajectories.all$Pseudotime <- Neurons.data$PseudotimeScore.shifted + 0.5
Trajectories.all$Phase <- NA
# Add progenitors data
Trajectories.progenitors <- Progenitors %>%
                              select(Barcodes, nCount_RNA, Spring_1, Spring_2, Lineage) %>% 
                              mutate(Pseudotime= Progenitors.data$Revelio.cc/2,
                                     Phase = Progenitors.data$Revelio.phase)
Trajectories.KO <- rbind(Trajectories.all, Trajectories.progenitors)

Trajectories.KO$Phase <- factor(Trajectories.KO$Phase, levels = c("G1.S", "S", "G2", "G2.M", "M.G1"))
p1 <- ggplot(Trajectories.KO, aes(Spring_1, Spring_2)) +
        geom_point(aes(color=Pseudotime), size=0.5) + 
        scale_color_gradientn(colours=rev(brewer.pal(n =11, name = "Spectral")), name='Pseudotime score')

p2 <- ggplot(Trajectories.KO, aes(Spring_1, Spring_2)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a"))

p1 + p2

p1 <- ggplot(Trajectories.KO, aes(x= Pseudotime, y= nCount_RNA/10000)) +
        geom_point(aes(color= 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)

p2 <- ggplot(Trajectories.KO, aes(x= Pseudotime, y= nCount_RNA/10000)) +
        geom_point(aes(color= Lineage), size=0.5) +
        scale_color_manual(values= c("#cc391b", "#026c9a")) +
        geom_smooth(aes(color= Lineage), method="loess", n= 50, fill="grey") +
        ylim(0,NA)

p1 / p2

Trajectories <- rbind(Trajectories.KO, Trajectories.WT)

rm(list = ls()[!ls() %in% c("Trajectories")])

Subset the full integrated dataset

WT.KO.integrated <- readRDS("WT_KO.integrated.RDS")

meta.data <- WT.KO.integrated@meta.data[Trajectories$Barcodes,]
meta.data$Pseudotime <- Trajectories$Pseudotime
meta.data$Phase <- Trajectories$Phase
WT.KO.integrated <- CreateSeuratObject(counts = WT.KO.integrated@assays$RNA@counts[, Trajectories$Barcodes],
                                       meta.data = meta.data)

spring <- as.matrix(WT.KO.integrated@meta.data %>% select("Integrated_Spring_1", "Integrated_Spring_2"))
  
WT.KO.integrated[["integrated_spring"]] <- CreateDimReducObject(embeddings = spring, key = "Spring_", assay = DefaultAssay(WT.KO.integrated))
p1 <- FeaturePlot(object = WT.KO.integrated,
            features = "Pseudotime",
            pt.size = 0.5,
            cols = rev(colorRampPalette(brewer.pal(n =11, name = "Spectral"))(100)),
            reduction = "integrated_spring",
            order = T) & NoAxes()

p2 <- DimPlot(object = WT.KO.integrated,
        group.by = "Lineage",
        pt.size = 0.5,
        reduction = "integrated_spring",
        cols =  c("#cc391b", "#026c9a")) & NoAxes()


p3 <- DimPlot(object = WT.KO.integrated,
        group.by = "Phase",
        pt.size = 0.5,
        reduction = "integrated_spring",
        cols =  c(wes_palette("FantasticFox1")[1:3],"grey40",wes_palette("FantasticFox1")[5])) & NoAxes()

p1 + p2 + p3

ggsave("~/Bureau/Fig4_MM/Spring_WT_KO_Pseudotime.pdf",
       device = "pdf",
       dpi = "retina")

Normalization

WT.KO.integrated <- NormalizeData(WT.KO.integrated, normalization.method = "LogNormalize", scale.factor = 10000, assay = "RNA")
WT.KO.integrated <- FindVariableFeatures(WT.KO.integrated, selection.method = "disp", nfeatures = 6500, assay = "RNA")

Plot some genes along pseudotime

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

Plot.Genes.trend(Seurat.data= WT.KO.integrated,
                 group.by = "Genotype",
                 genes= c("Gas1","Sox2",
                          "Neurog2", "Btg2",
                          "Tbr1", "Mapt",
                          "Trp73", "Foxg1"))

ggsave("~/Bureau/Fig4_MM/Neuro-Genes_trend_WT-KO.pdf",
       width = 15, height = 9,
       device = "pdf",
       dpi = "retina")
Plot.Genes.trend(Seurat.data= WT.KO.integrated,
                 group.by = "Genotype",
                 genes= c("Gmnc", "Mcidas",
                          "Ccno", "Ccdc67",
                          "Foxj1", "Trp73",
                          "Lhx1", "Cdkn1a"))

ggsave("~/Bureau/Fig4_MM/Gmnc-Genes_trend_WT-KO.pdf",
       width = 15, height = 9,
       device = "pdf",
       dpi = "retina")
CR <- WT.KO.integrated@meta.data %>% filter(Lineage == "Cajal-Retzius_neurons") %>% select(Barcodes,Pseudotime,orig.ident)

CR.genes <- as.data.frame(t(WT.KO.integrated@assays$RNA@data[c("Cacna2d2","Reln", "Nhlh2", "Ebf3"),CR$Barcodes]))
CR.genes$Pseudotime <- CR$Pseudotime
CR.genes$Genotype <- factor(CR$orig.ident, levels = c("Hem1", "Gmnc_KO") ) 

CR.genes  <- reshape2::melt(CR.genes, id = c("Pseudotime", "Genotype"))


ggplot(CR.genes, aes(x= Pseudotime, y= value)) +
  geom_smooth(method="loess", n= 50, aes(color= variable, linetype= Genotype)) +
  scale_color_manual(values= c(wes_palette("FantasticFox1")[1:3],wes_palette("FantasticFox1")[5])) +
  ylim(0,NA)

ggsave("~/Bureau/Fig4_MM/CR-Genes_trend_WT-KO.pdf",
       width = 5, height = 2,
       device = "pdf",
       dpi = "retina")

Use monocle2 to model gene expression along cycling axis

rm(list = ls()[!ls() %in% c("WT.KO.integrated")])
gc()
##             used  (Mb) gc trigger   (Mb)   max used  (Mb)
## Ncells   3533451 188.8    6140847  328.0    6140847   328
## Vcells 106898580 815.6  879319694 6708.7 1331680305 10160

Initialize a monocle object

# Transfer metadata
Annot.data  <- new('AnnotatedDataFrame',
                   data = data.frame(Barcode= WT.KO.integrated$Barcodes,
                                     Lineage= WT.KO.integrated$Lineage,
                                     Pseudotime= WT.KO.integrated$Pseudotime,
                                     Phase= WT.KO.integrated$Phase,
                                     Genotype= WT.KO.integrated$orig.ident))

# Transfer counts data
var.genes <- WT.KO.integrated[["RNA"]]@var.features

feature.data <- new('AnnotatedDataFrame',
                    data = data.frame(gene_short_name = rownames(WT.KO.integrated[["RNA"]]@counts[var.genes,]),
                                      row.names = rownames(WT.KO.integrated[["RNA"]]@counts[var.genes,])))

# Create the CellDataSet object including variable genes only
gbm_cds <- newCellDataSet(WT.KO.integrated[["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("WT.KO.integrated", "gbm_cds")])
gc()
##             used  (Mb) gc trigger (Mb)   max used  (Mb)
## Ncells   3607336 192.7    6140847  328    6140847   328
## Vcells 129862416 990.8  703455756 5367 1331680305 10160

DEG genes in the KO

Find DEG in the KO

KO.pData <- pData(gbm_cds) %>% subset(Genotype == "Gmnc_KO")

pseudo.maturation.diff <- differentialGeneTest(gbm_cds[fData(gbm_cds)$num_cells_expressed > 80, KO.pData$Barcode], 
                                                 fullModelFormulaStr = "~sm.ns(Pseudotime, df = 3)*Lineage", 
                                                 reducedModelFormulaStr = "~sm.ns(Pseudotime, df = 3)", 
                                                 cores = parallel::detectCores() - 2)

# Filter genes based on FDR
pseudo.maturation.diff.filtered <- pseudo.maturation.diff %>% filter(qval < 1e-40)
# Create a new pseudo-DV vector of 300 points
nPoints <- 300

new_data = list()
for (Lineage in unique(KO.pData$Lineage)){
  new_data[[length(new_data) + 1]] = data.frame(Pseudotime = seq(min(KO.pData$Pseudotime), max(KO.pData$Pseudotime), length.out = nPoints), Lineage=Lineage)
}

new_data = do.call(rbind, new_data)

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

Compute the ABC for each gene

# Extract matrix containing smoothed curves for each lineages
Pal_curve_matrix <- Diff.curve_matrix[, 1:nPoints] #Pallial points
CR_curve_matrix <- Diff.curve_matrix[, (nPoints + 1):(2 * nPoints)] #CR points

# Direction of the comparison : postive ABCs <=> Upregulated in CR lineage
ABCs_res <- CR_curve_matrix - Pal_curve_matrix

# Average logFC between the 2 curves
ILR_res <- log2(CR_curve_matrix/ (Pal_curve_matrix + 0.1)) 
  
ABCs_res <- apply(ABCs_res, 1, function(x, nPoints) {
                  avg_delta_x <- (x[1:(nPoints - 1)] + x[2:(nPoints)])/2
                  step <- (100/(nPoints - 1))
                  res <- round(sum(avg_delta_x * step), 3)
                  return(res)},
                  nPoints = nPoints) # Compute the area below the curve
  
ABCs_res <- cbind(ABCs_res, ILR_res[,ncol(ILR_res)])
colnames(ABCs_res)<- c("ABCs", "Endpoint_ILR")

# Import ABC values into the DE test results table
pseudo.maturation.diff.filtered <- cbind(pseudo.maturation.diff.filtered[,1:4],
                                         ABCs_res,
                                         pseudo.maturation.diff.filtered[,5:6])

KO Cajal-Retzius cells specific trajectory analysis

# Extract Cajal-Retzius expressed genes
CR.res <- as.data.frame(pseudo.maturation.diff.filtered[pseudo.maturation.diff.filtered$ABCs > 0,])
CR.genes <- row.names(CR.res)

CR_curve_matrix <- CR_curve_matrix[CR.genes, ]
Pseudotime.genes.clusters <- cluster::pam(as.dist((1 - cor(Matrix::t(CR_curve_matrix),method = "pearson"))), k= 5)

CR.Gene.dynamique <- data.frame(Gene= names(Pseudotime.genes.clusters$clustering),
                                 Waves= Pseudotime.genes.clusters$clustering,
                                 Gene.Clusters = Pseudotime.genes.clusters$clustering,
                                 q.val = CR.res$qval,
                                 ABCs= CR.res$ABCs
                                 ) %>% arrange(Gene.Clusters)

row.names(CR.Gene.dynamique) <- CR.Gene.dynamique$Gene
CR.Gene.dynamique$Gene.Clusters <- paste0("Clust.", CR.Gene.dynamique$Gene.Clusters)

write.table(CR.Gene.dynamique, "KO_CR_dynamic_genes.csv", sep = ";", quote = F, row.names = F)
# Order the rows using seriation
dst <- as.dist((1-cor(scale(t(CR_curve_matrix)), method = "pearson")))
row.ser <- seriation::seriate(dst, method ="R2E") #"R2E" #TSP #"GW" "GW_ward"
gene.order <- rownames(CR_curve_matrix[get_order(row.ser),])

# Set annotation colors
pal <- wes_palette("Darjeeling1")
anno.colors <- list(lineage = c(KO_Pallial_neurons="#026c9a", KO_Cajal_Retzius="#cc391b"),
                    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(Diff.curve_matrix[gene.order[c(235:1,292:236)],
                                c(300:1,#Pal 
                                  301:600)], #CR
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = CR.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_col = data.frame(lineage = rep(c("KO_Pallial_neurons","KO_Cajal_Retzius"), each=300)),
                   annotation_colors = anno.colors,
                   gaps_col = c(200,400),
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

Intersect dynamical genes in CPx and CR in WT and KO

WT.CR <- read.table("../CajalRetzius_trajectory/CR_dynamic_genes.csv", sep = ";", header = T)
WT.CPx <- read.table("../ChoroidPlexus_trajectory/ChP.Gene.dynamique.csv", sep = ";", header = T)

KO.CR <- read.table("./KO_CR_dynamic_genes.csv", sep = ";", header = T)
KO.CPx <- read.table("./KO_CPx_dynamic_genes.csv", sep = ";", header = T)

listInput <- list(WT.CR= WT.CR$Gene, WT.CPx= WT.CPx$Gene, KO.CR= KO.CR$Gene, KO.CPx= KO.CPx$Gene)
upset(fromList(listInput),
      order.by = "freq",
      queries = list(list(query = intersects, params = list("WT.CR", "WT.CPx"), color = "orange", active = T),
                     list(query = intersects, params = list("WT.CPx", "KO.CR", "WT.CR"), color = "red", active = T),
                     list(query = intersects, params = list("KO.CR", "WT.CR"), color = "blue", active = T)))

upset(fromList(listInput),
      order.by = "freq",
      intersections = list(list("WT.CR", "WT.CPx"),
                           list("KO.CR", "WT.CR"),
                           list("WT.CPx", "KO.CR", "WT.CR")),
      queries = list(list(query = intersects, params = list("WT.CR", "WT.CPx"), color = "orange", active = T),
                     list(query = intersects, params = list("WT.CPx", "KO.CR", "WT.CR"), color = "red", active = T),
                     list(query = intersects, params = list("KO.CR", "WT.CR"), color = "blue", active = T))
      )

# Shared genes before Gmnc induction
WT.CR.anteGmnc <- WT.CR %>% filter(Waves %in% c(1,3)) %>% pull(Gene)
KO.CR.anteGmnc <- KO.CR %>% filter(Waves %in% c(2,3,5)) %>% pull(Gene)

gene.list <- list(WT.CR = WT.CR.anteGmnc, KO.CR = KO.CR.anteGmnc)
ggvenn::ggvenn(gene.list)

# Shared genes after Gmnc induction
WT.CR.postGmnc  <- WT.CR %>% filter(Waves %in% c(2,5,4)) %>% pull(Gene)
KO.CR.postGmnc <- KO.CR %>% filter(Waves %in% c(1,4)) %>% pull(Gene)


gene.list <- list(WT.CR = WT.CR.postGmnc, KO.CR = KO.CR.postGmnc)
ggvenn::ggvenn(gene.list)

Plot WT CR dynamic genes along WT and KO trajectories

All CR enriched genes identified in the WT dataset

# Load WT CR variable genes along pseudotime
WT.CR.genes <- read.table("../CajalRetzius_trajectory/CR_dynamic_genes.csv", sep = ";", header = T)

# Extract CR lineage in both genotypes
CR.pData <- pData(gbm_cds) %>% subset(Lineage == "Cajal-Retzius_neurons")

# Create a new pseudotime vector of 300 points
nPoints <- 300

new_data = list()
for (Genotype in unique(CR.pData$Genotype)){
  new_data[[length(new_data) + 1]] = data.frame(Pseudotime = seq(min(CR.pData$Pseudotime), max(CR.pData$Pseudotime), length.out = nPoints), Genotype= Genotype)
}

new_data = do.call(rbind, new_data)

# Smooth gene expression
Diff.curve_matrix <- genSmoothCurves(gbm_cds[WT.CR.genes$Gene, CR.pData$Barcode],
                                      trend_formula = "~sm.ns(Pseudotime, df = 3)*Genotype",
                                      relative_expr = TRUE,
                                      new_data = new_data,
                                      cores= parallel::detectCores() - 2)
Pseudotime.genes.clusters <- cluster::pam(as.dist((1 - cor(Matrix::t(Diff.curve_matrix[,301:600]),method = "pearson"))), k= 5)

CR.Gene.dynamique <- data.frame(Gene= names(Pseudotime.genes.clusters$clustering),
                                Waves= Pseudotime.genes.clusters$clustering,
                                Gene.Clusters = Pseudotime.genes.clusters$clustering,
                                WT.CR_and_KO.CR = ifelse(listInput$WT.CR %in% listInput$KO.CR,
                                                         "Shared", "Not.Shared"),
                                WT.CR_and_WT.CPx_only = ifelse(listInput$WT.CR %in% listInput$WT.CPx & !listInput$WT.CR %in% listInput$KO.CR,
                                                               "Shared", "Not.Shared")
                                ) %>% arrange(Gene.Clusters)

row.names(CR.Gene.dynamique) <- CR.Gene.dynamique$Gene
CR.Gene.dynamique$Gene.Clusters <- paste0("Clust.", CR.Gene.dynamique$Gene.Clusters)
# Order the rows using seriation
dst <- as.dist((1-cor(scale(t(Diff.curve_matrix[,301:600])), method = "pearson")))
row.ser <- seriation::seriate(dst, method ="R2E") #"R2E" #TSP #"GW" "GW_ward"
gene.order <- rownames(Diff.curve_matrix[,301:600][get_order(row.ser),])

# Set annotation colors
pal <- wes_palette("Darjeeling1")
anno.colors <- list(lineage = c(Gmnc_KO="#026c9a", Gmnc_WT="#cc391b"),
                    Gene.Clusters = c(Clust.1 =pal[1] , Clust.2=pal[2], Clust.3=pal[3], Clust.4=pal[4], Clust.5=pal[5]),
                    WT.CR_and_WT.CPx = c(Shared = pal[2], Not.Shared=pal[3]),
                    WT.CR_and_WT.CPx_only= c(Shared = pal[2], Not.Shared=pal[3]))

gene.order <- gene.order[c(382:622,1:381)]
heatmap.gene <- pheatmap::pheatmap(Diff.curve_matrix[gene.order,
                                    c(300:1,#Pal 
                                      301:600)], #CR
                       scale = "row",
                       cluster_rows = F,
                       cluster_cols = F,
                       annotation_row = CR.Gene.dynamique %>% dplyr::select(Gene.Clusters, WT.CR_and_KO.CR, WT.CR_and_WT.CPx_only),
                       annotation_col = data.frame(lineage = rep(c("Gmnc_KO","Gmnc_WT"), each=300)),
                       annotation_colors = anno.colors,
                       show_colnames = F,
                       gaps_col = c(200,400),
                       gaps_row = c(233),
                       show_rownames = T,
                       fontsize_row = 8,
                       border_color = NA,
                       color =  viridis::viridis(9),
                       breaks = seq(-2.5,2.5, length.out = 9),
                       main = "WT CR enriched genes along GmncKO and GmncWT CR trajectories")

pdf("~/Bureau/Fig4_MM/heatmap_Fig4Genes_WT-KO.pdf", width=10, height=5)
grid::grid.newpage()
grid::grid.draw(heatmap.gene$gtable)
dev.off()
## png 
##   2
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]))

col.anno <- data.frame(Cell.state = rep(c("Cycling_RG","Differentiating_cells"), c(100,200)))
rownames(col.anno) <- 301:600

pheatmap::pheatmap(Diff.curve_matrix[gene.order,
                                c(301:600)], #CR
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = CR.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_col = col.anno,
                   annotation_colors = anno.colors,
                   gaps_col = 100,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   border_color = NA,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "WT CR enriched genes along GmncKO and GmncWT CR trajectories")

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(Diff.curve_matrix[gene.order,
                                c(1:300)], #CR
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = CR.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_col = data.frame(Cell.state = rep(c("Cycling_RG","Differentiating_cells"), c(100,200))),
                   annotation_colors = anno.colors,
                   show_colnames = F,
                   show_rownames = F,
                   gaps_col = 100,
                   fontsize_row = 8,
                   border_color = NA,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "WT CR enriched genes along GmncKO CR trajectories")

CR <- WT.KO.integrated@meta.data %>% filter(Lineage == "Cajal-Retzius_neurons") %>% select(Barcodes,Pseudotime,orig.ident)

CR.genes <- as.data.frame(t(WT.KO.integrated@assays$RNA@data[c("Gmnc","Trp73", "Lhx1", "Barhl2"),CR$Barcodes]))
CR.genes$Pseudotime <- CR$Pseudotime
CR.genes$Genotype <- factor(CR$orig.ident, levels = c("Hem1", "Gmnc_KO") ) 

CR.genes  <- reshape2::melt(CR.genes, id = c("Pseudotime", "Genotype"))


ggplot(CR.genes, aes(x= Pseudotime, y= value)) +
  geom_smooth(method="loess", n= 50, aes(color= variable, linetype= Genotype)) +
  scale_color_manual(values= c(wes_palette("FantasticFox1")[1:3],wes_palette("FantasticFox1")[5])) +
  ylim(0,NA)

ggsave("~/Bureau/Fig4_MM/Medial-CR_Genes-trend_WT-KO.pdf",
       width = 5, height = 2,
       device = "pdf",
       dpi = "retina")

TF only

# Set annotation colors
pal <- wes_palette("Darjeeling1")
anno.colors <- list(lineage = c(Gmnc_KO="#026c9a", Gmnc_WT="#cc391b"),
                    Gene.Clusters = c(Clust.1 =pal[1] , Clust.2=pal[2], Clust.3=pal[3], Clust.4=pal[4], Clust.5=pal[5]))

TFs <- read.table("TF.csv", sep = ";")[,1]

gene.order <- gene.order[gene.order %in% TFs]

heatmap.gene <- pheatmap::pheatmap(Diff.curve_matrix[gene.order,
                                c(300:1,#KO 
                                  301:600)], #WT
                       scale = "row",
                       cluster_rows = F,
                       cluster_cols = F,
                       annotation_row = CR.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                       annotation_col = data.frame(lineage = rep(c("Gmnc_KO","Gmnc_WT"), each=300)),
                       annotation_colors = anno.colors,
                       show_colnames = F,
                       show_rownames = T,
                       fontsize_row = 8,
                       border_color = NA,
                       color =  viridis::viridis(9),
                       breaks = seq(-2.5,2.5, length.out = 9),
                       main = "WT CR dynamic TFs along GmncWT and KO trajectories")

# Which WT CR enriched genes are still expressed in the KO CR

Find DEG in the KO

KO.pData <- pData(gbm_cds) %>% subset(Genotype == "Gmnc_KO" & Pseudotime > 0.5)

WT.CR.postGmnc <- read.table("../CajalRetzius_trajectory/CR_dynamic_genes.csv", sep = ";", header = T) 
WT.CR.postGmnc <- WT.CR.postGmnc %>% filter(Waves %in% c(2,3,4,5)) %>% pull(Gene)

pseudo.maturation.diff <- differentialGeneTest(gbm_cds[WT.CR.postGmnc, KO.pData$Barcode], 
                                                 fullModelFormulaStr = "~sm.ns(Pseudotime, df = 3)*Lineage", 
                                                 reducedModelFormulaStr = "~sm.ns(Pseudotime, df = 3)", 
                                                 cores = parallel::detectCores() - 2)

# Filter genes based on FDR
pseudo.maturation.diff.filtered <- pseudo.maturation.diff %>% filter(qval < 1e-11)
# Create a new pseudo-DV vector of 300 points
nPoints <- 100

new_data = list()
for (Lineage in unique(KO.pData$Lineage)){
  new_data[[length(new_data) + 1]] = data.frame(Pseudotime = seq(min(KO.pData$Pseudotime), max(KO.pData$Pseudotime), length.out = nPoints), Lineage=Lineage)
}

new_data = do.call(rbind, new_data)

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

Compute the ABC for each gene

# Extract matrix containing smoothed curves for each lineages
Pal_curve_matrix <- Diff.curve_matrix[, 1:nPoints] #Pallial points
CR_curve_matrix <- Diff.curve_matrix[, (nPoints + 1):(2 * nPoints)] #CR points

# Direction of the comparison : postive ABCs <=> Upregulated in CR lineage
ABCs_res <- CR_curve_matrix - Pal_curve_matrix

# Average logFC between the 2 curves
ILR_res <- log2(CR_curve_matrix/ (Pal_curve_matrix + 0.1)) 
  
ABCs_res <- apply(ABCs_res, 1, function(x, nPoints) {
                  avg_delta_x <- (x[1:(nPoints - 1)] + x[2:(nPoints)])/2
                  step <- (100/(nPoints - 1))
                  res <- round(sum(avg_delta_x * step), 3)
                  return(res)},
                  nPoints = nPoints) # Compute the area below the curve
  
ABCs_res <- cbind(ABCs_res, ILR_res[,ncol(ILR_res)])
colnames(ABCs_res)<- c("ABCs", "Endpoint_ILR")

# Import ABC values into the DE test results table
pseudo.maturation.diff.filtered <- cbind(pseudo.maturation.diff.filtered[,1:4],
                                         ABCs_res,
                                         pseudo.maturation.diff.filtered[,5:6])

Intersect with WT CR and CPx

WT.CPx <- read.table("../ChoroidPlexus_trajectory/ChP.Gene.dynamique.csv", sep = ";", header = T)
WT.CPx.postGmnc <- WT.CPx  %>% filter(Waves %in% c(1,2)) %>% pull(Gene)

KO.CR.postGmnc <- pseudo.maturation.diff.filtered %>% filter(ABCs >20) %>% pull(gene_short_name)

gene.list <- list(WT.CR.postGmnc = WT.CR.postGmnc, KO.CR.postGmnc = KO.CR.postGmnc, WT.CPx.postGmnc = WT.CPx.postGmnc)
ggvenn::ggvenn(gene.list)

Session Info

#date
format(Sys.time(), "%d %B, %Y, %H,%M")
## [1] "20 juin, 2022, 18,05"
#Packages used
sessionInfo()
## R version 4.2.0 (2022-04-22)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.4 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] UpSetR_1.4.0        wesanderson_0.3.6   cowplot_1.1.1      
##  [4] ggExtra_0.9         RColorBrewer_1.1-2  dplyr_1.0.7        
##  [7] seriation_1.3.1     gprofiler2_0.2.1    monocle_2.22.0     
## [10] DDRTree_0.1.5       irlba_2.3.3         VGAM_1.1-5         
## [13] ggplot2_3.3.5       Biobase_2.54.0      BiocGenerics_0.40.0
## [16] Matrix_1.4-1        Revelio_0.1.0       princurve_2.1.6    
## [19] SeuratObject_4.0.4  Seurat_4.0.5       
## 
## loaded via a namespace (and not attached):
##   [1] plyr_1.8.6            igraph_1.2.11         lazyeval_0.2.2       
##   [4] densityClust_0.3      listenv_0.8.0         scattermore_0.7      
##   [7] fastICA_1.2-3         digest_0.6.29         foreach_1.5.1        
##  [10] htmltools_0.5.2       viridis_0.6.2         fansi_0.5.0          
##  [13] magrittr_2.0.2        tensor_1.5            cluster_2.1.3        
##  [16] ROCR_1.0-11           limma_3.50.0          globals_0.14.0       
##  [19] matrixStats_0.61.0    docopt_0.7.1          spatstat.sparse_2.0-0
##  [22] colorspace_2.0-2      ggrepel_0.9.1         xfun_0.28            
##  [25] sparsesvd_0.2         crayon_1.4.2          jsonlite_1.7.2       
##  [28] spatstat.data_2.1-0   survival_3.2-13       zoo_1.8-9            
##  [31] iterators_1.0.13      glue_1.5.1            polyclip_1.10-0      
##  [34] registry_0.5-1        gtable_0.3.0          leiden_0.3.9         
##  [37] future.apply_1.8.1    abind_1.4-5           scales_1.1.1         
##  [40] pheatmap_1.0.12       DBI_1.1.1             miniUI_0.1.1.1       
##  [43] Rcpp_1.0.8            viridisLite_0.4.0     xtable_1.8-4         
##  [46] reticulate_1.22       spatstat.core_2.3-1   htmlwidgets_1.5.4    
##  [49] httr_1.4.2            FNN_1.1.3             ellipsis_0.3.2       
##  [52] ica_1.0-2             farver_2.1.0          pkgconfig_2.0.3      
##  [55] sass_0.4.0            uwot_0.1.10           deldir_1.0-6         
##  [58] utf8_1.2.2            labeling_0.4.2        tidyselect_1.1.1     
##  [61] rlang_0.4.12          reshape2_1.4.4        later_1.3.0          
##  [64] munsell_0.5.0         tools_4.2.0           generics_0.1.1       
##  [67] ggridges_0.5.3        evaluate_0.14         stringr_1.4.0        
##  [70] fastmap_1.1.0         yaml_2.2.1            goftest_1.2-3        
##  [73] knitr_1.36            fitdistrplus_1.1-6    purrr_0.3.4          
##  [76] RANN_2.6.1            pbapply_1.5-0         future_1.23.0        
##  [79] nlme_3.1-153          mime_0.12             slam_0.1-49          
##  [82] ggvenn_0.1.9          compiler_4.2.0        plotly_4.10.0        
##  [85] png_0.1-7             spatstat.utils_2.2-0  tibble_3.1.6         
##  [88] bslib_0.3.1           stringi_1.7.6         highr_0.9            
##  [91] lattice_0.20-45       HSMMSingleCell_1.14.0 vctrs_0.3.8          
##  [94] pillar_1.6.4          lifecycle_1.0.1       spatstat.geom_2.3-0  
##  [97] combinat_0.0-8        lmtest_0.9-39         jquerylib_0.1.4      
## [100] RcppAnnoy_0.0.19      data.table_1.14.2     httpuv_1.6.3         
## [103] patchwork_1.1.1       R6_2.5.1              promises_1.2.0.1     
## [106] TSP_1.1-11            KernSmooth_2.23-20    gridExtra_2.3        
## [109] parallelly_1.29.0     codetools_0.2-18      MASS_7.3-57          
## [112] assertthat_0.2.1      withr_2.4.3           qlcMatrix_0.9.7      
## [115] sctransform_0.3.2     mgcv_1.8-40           parallel_4.2.0       
## [118] grid_4.2.0            rpart_4.1.16          tidyr_1.1.4          
## [121] rmarkdown_2.11        Rtsne_0.15            shiny_1.7.1

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

LS0tCnRpdGxlOiAiQ29tcGFyaXNvbiBiZXR3ZWVuIHRyYWplY3RvcmllcyBpbiB0aGUgR21uYyBXVC9LTyIKYXV0aG9yOgogICAtIE1hdHRoaWV1IE1vcmVhdV5bSW5zdGl0dXRlIG9mIFBzeWNoaWF0cnkgYW5kIE5ldXJvc2NpZW5jZSBvZiBQYXJpcywgSU5TRVJNIFUxMjY2LCA3NTAxNCwgUGFyaXMsIEZyYW5jZSwgbWF0dGhpZXUubW9yZWF1QGluc2VybS5mcl0gWyFbXShodHRwczovL29yY2lkLm9yZy9zaXRlcy9kZWZhdWx0L2ZpbGVzL2ltYWdlcy9vcmNpZF8xNngxNi5wbmcpXShodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItMjU5Mi0yMzczKQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIGRmX3ByaW50OiB0aWJibGUKICAgIGhpZ2hsaWdodDogaGFkZG9jawogICAgdGhlbWU6IGNvc21vCiAgICBjc3M6ICIuLi9zdHlsZS5jc3MiCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA1CiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogeWVzCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZmlnLmFsaWduID0gJ2NlbnRlcicsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlLmxhenkgPSBGQUxTRSkKCiMgVG8gdXNlIGJpb21hcnQgCm5ld19jb25maWcgPC0gaHR0cjo6Y29uZmlnKHNzbF92ZXJpZnlwZWVyID0gRkFMU0UpCmh0dHI6OnNldF9jb25maWcobmV3X2NvbmZpZywgb3ZlcnJpZGUgPSBGQUxTRSkKYGBgCgojIExvYWQgbGlicmFyaWVzCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShwcmluY3VydmUpCmxpYnJhcnkoUmV2ZWxpbykKbGlicmFyeShtb25vY2xlKQpsaWJyYXJ5KGdwcm9maWxlcjIpCmxpYnJhcnkoc2VyaWF0aW9uKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShkcGx5cikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ0V4dHJhKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkod2VzYW5kZXJzb24pCmxpYnJhcnkoVXBTZXRSKQoKI1NldCBnZ3Bsb3QgdGhlbWUgYXMgY2xhc3NpYwp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQpgYGAKCiMgTG9hZCBpbnRlZ3JhdGVkIGRhdGFzZXRzCgpgYGB7cn0KV1QuS08uaW50ZWdyYXRlZCA8LSByZWFkUkRTKCJXVF9LTy5pbnRlZ3JhdGVkLlJEUyIpCmBgYAoKCmBgYHtyfQpEaW1QbG90KFdULktPLmludGVncmF0ZWQsCiAgICAgICAgcmVkdWN0aW9uID0gImludGVncmF0ZWRfc3ByaW5nIiwKICAgICAgICBncm91cC5ieSA9ICJMaW5lYWdlIiwKICAgICAgICBwdC5zaXplID0gMSwKICAgICAgICBjb2xzID0gICBjKCIjY2MzOTFiIiwiI2U3ODIzYSIsIiM5Njk2OTYiLCIjMDI2YzlhIikKICAgICAgICApICsgTm9BeGVzKCkKYGBgCgojIFBzZXVkb3RpbWUgaW4gdGhlIFdUCgojIyBFeHRyYWN0IENSIGFuZCBDUCBuZXVyb25zIAoKYGBge3J9Ck5ldXJvbnMuZGF0YSA8LSAgc3Vic2V0KFdULktPLmludGVncmF0ZWQsCiAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldCA9IExpbmVhZ2UgJWluJSBjKCJDYWphbC1SZXR6aXVzX25ldXJvbnMiLCAiUGFsbGlhbF9uZXVyb25zIikgJiBvcmlnLmlkZW50ID09ICJIZW0xIiAmIENlbGwuaWRlbnQuV1QgJWluJSBjKCJDYWphbC1SZXR6aXVzX25ldXJvbnMiLCAiUGFsbGlhbF9uZXVyb25zIikpCgpEaW1QbG90KE5ldXJvbnMuZGF0YSwKICAgICAgICBncm91cC5ieSA9ICJMaW5lYWdlIiwKICAgICAgICByZWR1Y3Rpb24gPSAiaW50ZWdyYXRlZF9zcHJpbmciLAogICAgICAgIHB0LnNpemUgPSAxLAogICAgICAgIGNvbHMgPSAgYygiI2NjMzkxYiIsIiMwMjZjOWEiKQogICAgICAgICkgKyBOb0F4ZXMoKQoKcm0oV1QuS08uaW50ZWdyYXRlZCkKZ2MoKQpgYGAKCiMjIEZpdCBwcmluY2lwYWxlIGN1cnZlIG9uIHRoZSB0d28gbGluZWFnZXMKCiMjIyBDYWphbC1SZXR6aXVzIGNlbGxzCgpgYGB7cn0KVHJhamVjdG9yaWVzLkhlbSA8LSBOZXVyb25zLmRhdGFAbWV0YS5kYXRhICU+JQogICAgICAgICAgICAgICAgICAgIHNlbGVjdCgiQmFyY29kZXMiLCAibkNvdW50X1JOQSIsICJTcHJpbmdfMSIsICJTcHJpbmdfMiIsICJBUF9zaWduYXR1cmUiLCJCUF9zaWduYXR1cmUiLCAiRU5fc2lnbmF0dXJlIiwgIkxOX3NpZ25hdHVyZSIsICJMaW5lYWdlIikgJT4lCiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKExpbmVhZ2UgPT0gIkNhamFsLVJldHppdXNfbmV1cm9ucyIpCmBgYAoKYGBge3J9CmZpdCA8LSBwcmluY2lwYWxfY3VydmUoYXMubWF0cml4KFRyYWplY3Rvcmllcy5IZW1bLGMoIlNwcmluZ18xIiwgIlNwcmluZ18yIildKSwKICAgICAgICAgICAgICAgICAgICAgICBzbW9vdGhlcj0nbG93ZXNzJywKICAgICAgICAgICAgICAgICAgICAgICB0cmFjZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgIGYgPSAuNywKICAgICAgICAgICAgICAgICAgICAgICBzdHJldGNoPTApCgojVGhlIHByaW5jaXBhbCBjdXJ2ZSBzbW9vdGhlZApIZW0ucGMubGluZSA8LSBhcy5kYXRhLmZyYW1lKGZpdCRzW29yZGVyKGZpdCRsYW1iZGEpLF0pIAoKI1BzZXVkb3RpbWUgc2NvcmUKVHJhamVjdG9yaWVzLkhlbSRQc2V1ZG90aW1lU2NvcmUgPC0gZml0JGxhbWJkYS9tYXgoZml0JGxhbWJkYSkKCmBgYAoKYGBge3J9CmlmIChjb3IoVHJhamVjdG9yaWVzLkhlbSRQc2V1ZG90aW1lU2NvcmUsIE5ldXJvbnMuZGF0YUBhc3NheXMkUk5BQGRhdGFbJ0htZ2EyJywgVHJhamVjdG9yaWVzLkhlbSRCYXJjb2Rlc10pID4gMCkgewogIFRyYWplY3Rvcmllcy5IZW0kUHNldWRvdGltZVNjb3JlIDwtIC0oVHJhamVjdG9yaWVzLkhlbSRQc2V1ZG90aW1lU2NvcmUgLSBtYXgoVHJhamVjdG9yaWVzLkhlbSRQc2V1ZG90aW1lU2NvcmUpKQp9CmBgYAoKIyMjIFBhbGxpYWwgbmV1cm9ucwoKYGBge3J9ClRyYWplY3Rvcmllcy5QYWxsaWFsIDwtIE5ldXJvbnMuZGF0YUBtZXRhLmRhdGEgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgiQmFyY29kZXMiLCAibkNvdW50X1JOQSIsICJTcHJpbmdfMSIsICJTcHJpbmdfMiIsICJBUF9zaWduYXR1cmUiLCJCUF9zaWduYXR1cmUiLCAiRU5fc2lnbmF0dXJlIiwgIkxOX3NpZ25hdHVyZSIsICJMaW5lYWdlIikgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihMaW5lYWdlID09ICJQYWxsaWFsX25ldXJvbnMiKQogICAgICAgICAgICAgICAgICAKYGBgCgpgYGB7cn0KZml0IDwtIHByaW5jaXBhbF9jdXJ2ZShhcy5tYXRyaXgoVHJhamVjdG9yaWVzLlBhbGxpYWxbLGMoIlNwcmluZ18xIiwgIlNwcmluZ18yIildKSwKICAgICAgICAgICAgICAgICAgICAgICBzbW9vdGhlcj0nbG93ZXNzJywKICAgICAgICAgICAgICAgICAgICAgICB0cmFjZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgIGYgPSAuNywKICAgICAgICAgICAgICAgICAgICAgICBzdHJldGNoPTApCgojVGhlIHByaW5jaXBhbCBjdXJ2ZSBzbW9vdGhlZApQYWxsaWFsLnBjLmxpbmUgPC0gYXMuZGF0YS5mcmFtZShmaXQkc1tvcmRlcihmaXQkbGFtYmRhKSxdKQoKI1BzZXVkb3RpbWUgc2NvcmUKVHJhamVjdG9yaWVzLlBhbGxpYWwkUHNldWRvdGltZVNjb3JlIDwtIGZpdCRsYW1iZGEvbWF4KGZpdCRsYW1iZGEpCmBgYAoKYGBge3J9CmlmIChjb3IoVHJhamVjdG9yaWVzLlBhbGxpYWwkUHNldWRvdGltZVNjb3JlLCBOZXVyb25zLmRhdGFAYXNzYXlzJFJOQUBkYXRhWydIbWdhMicsIFRyYWplY3Rvcmllcy5QYWxsaWFsJEJhcmNvZGVzXSkgPiAwKSB7CiAgVHJhamVjdG9yaWVzLlBhbGxpYWwkUHNldWRvdGltZVNjb3JlIDwtIC0oVHJhamVjdG9yaWVzLlBhbGxpYWwkUHNldWRvdGltZVNjb3JlIC0gbWF4KFRyYWplY3Rvcmllcy5QYWxsaWFsJFBzZXVkb3RpbWVTY29yZSkpCn0KYGBgCgojIyMgQ29tYmluZSB0aGUgdHdvIHRyYWplY3RvcmllcycgZGF0YQoKYGBge3J9ClRyYWplY3Rvcmllcy5uZXVyb25zIDwtIHJiaW5kKFRyYWplY3Rvcmllcy5QYWxsaWFsLCBUcmFqZWN0b3JpZXMuSGVtKQpgYGAKCmBgYHtyfQpjb2xzIDwtIGJyZXdlci5wYWwobiA9MTEsIG5hbWUgPSAiU3BlY3RyYWwiKQoKZ2dwbG90KFRyYWplY3Rvcmllcy5uZXVyb25zLCBhZXMoU3ByaW5nXzEsIFNwcmluZ18yKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPVBzZXVkb3RpbWVTY29yZSksIHNpemU9Miwgc2hhcGU9MTYpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnM9cmV2KGNvbHMpLCBuYW1lPSdTcGV1ZG90aW1lIHNjb3JlJykgKwogIGdlb21fbGluZShkYXRhPVBhbGxpYWwucGMubGluZSwgY29sb3I9IiMwMjZjOWEiLCBzaXplPTAuNzcpICsKICBnZW9tX2xpbmUoZGF0YT1IZW0ucGMubGluZSwgY29sb3I9IiNjYzM5MWIiLCBzaXplPTAuNzcpCmBgYAoKIyMjIFNoaWZ0IFBzZXVkb3RpbWUgaW4gYm90aCBsaW5lYWdlCgpTaW5jZSB3ZSBvYnNlcnZlIHRoZSBmaXJzdCAyNSUgb2YgYm90aCB0cmFqZWN0b3JpZXMgYXJlIG9jY3VwaWVkIGJ5IGZldywgbGlrZWx5IHByb2dlbml0b3IgY2VsbHMsIHdlIHNoaWZ0IHRoaXMgY2VsbCBhbG9uZyB0aGUgYXhpcwoKYGBge3J9ClBzZXVkb3RpbWUuaW50ZXJ2YWxzIDwtIFRyYWplY3Rvcmllcy5uZXVyb25zJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KExpbmVhZ2UsIFBzZXVkb3RpbWVTY29yZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKFBzZXVkb3RpbWUuYmlucyA9IGN1dChUcmFqZWN0b3JpZXMubmV1cm9ucyRQc2V1ZG90aW1lU2NvcmUsIHNlcSgwLCBtYXgoVHJhamVjdG9yaWVzLm5ldXJvbnMkUHNldWRvdGltZVNjb3JlKSArIDAuMDUsIDAuMDUpLCBkaWcubGFiID0gMiwgcmlnaHQgPSBGQUxTRSkpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KExpbmVhZ2UsIFBzZXVkb3RpbWUuYmlucykgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKG49bigpKQoKZ2dwbG90KFBzZXVkb3RpbWUuaW50ZXJ2YWxzLCBhZXMoeD1Qc2V1ZG90aW1lLmJpbnMsIHk9biwgZmlsbD1MaW5lYWdlKSkgKwogICAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuOTApICsKICAgICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdD0xKSkrCiAgICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkKYGBgCgpgYGB7cn0Kc2NvcmUgPC0gc2FwcGx5KFRyYWplY3Rvcmllcy5uZXVyb25zJFBzZXVkb3RpbWVTY29yZSwKICAgICAgICAgICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIGlmICh4IDw9IDAuMikge3g9IDAuMn0gZWxzZSB7IHg9eCB9KQoKVHJhamVjdG9yaWVzLm5ldXJvbnMkUHNldWRvdGltZVNjb3JlLnNoaWZ0ZWQgPC0gKHNjb3JlIC0gbWluKHNjb3JlKSkgLyAobWF4KHNjb3JlKSAtIG1pbihzY29yZSkpCmBgYAoKYGBge3J9CmdncGxvdChUcmFqZWN0b3JpZXMubmV1cm9ucywgYWVzKHg9IFBzZXVkb3RpbWVTY29yZS5zaGlmdGVkLCB5PSBuQ291bnRfUk5BLzEwMDAwKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkgKwogICAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgYWVzKGNvbG9yPSBMaW5lYWdlKSkgKwogICAgICAgIHlsaW0oMCxOQSkKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCJUcmFqZWN0b3JpZXMubmV1cm9ucyIpXSkKYGBgCgojIyBMb2FkIHByb2dlbml0b3JzIHdpdGggY2VsbCBjeWNsZSB0cmFqZWN0b3J5IGZpdHRlZAoKYGBge3J9ClByb2dlbml0b3JzLmRhdGEgPC0gcmVhZFJEUygiLi4vUHJvZ2VuaXRvcnNEaXZlcnNpdHkvUHJvZ2VuaXRvcnMuUkRTIikKYGBgCgpgYGB7cn0KdGFibGUoUHJvZ2VuaXRvcnMuZGF0YSRDZWxsX2lkZW50KQpgYGAKClRvIGJhbGFuY2UgdGhlIG51bWJlciBvZiBwcm9nZW5pdG9ycyBpbiBib3RoIGRvbWFpbiB3ZSB3aWxsIG9ubHkgd29yayB3aXRoICpIZW0qIGFuZCAqTWVkaWFsX3BhbGxpdW0qIGFubm90YXRlZCBjZWxscy4gU2luY2Ugd2UgYXJlIHVzaW5nIHBhbGxpYWwgY2VsbCB0byBjb250cmFzdCBDUiBzcGVjaWZpYyB0cmFqZWN0b3J5IHdlIHRoaW5rIHRoaXMgYXBwcm94aW1hdGlvbiB3aWxsIG5vdCBzaWduaWZpY2FudGx5IGFmZmVjdCBvdXIgYW5hbHlzaXMuCgpgYGB7cn0KUHJvZ2VuaXRvcnMuZGF0YSA8LSAgc3Vic2V0KFByb2dlbml0b3JzLmRhdGEsIHN1YnNldCA9IENlbGxfaWRlbnQgJWluJSBjKCJIZW0iLCAiTWVkaWFsX3BhbGxpdW0iKSAmIG9yaWcuaWRlbnQgPT0gIkhlbTEiKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg2LCA0KX0KcDEgPC0gRGltUGxvdChQcm9nZW5pdG9ycy5kYXRhLAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgY29scyA9ICBjKCIjZTNjMTQ4IiwgIiNlNDZiNmIiKSkgKyBOb0F4ZXMoKQoKcDIgPC0gRmVhdHVyZVBsb3Qob2JqZWN0ID0gUHJvZ2VuaXRvcnMuZGF0YSwKICAgICAgICAgICAgZmVhdHVyZXMgPSAiUmV2ZWxpby5jYyIsCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgIGNvbHMgPSByZXYoYnJld2VyLnBhbCgxMCwiU3BlY3RyYWwiKSksCiAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgICAgICBvcmRlciA9IFQpICYgTm9BeGVzKCkKCnAzIDwtIERpbVBsb3Qob2JqZWN0ID0gUHJvZ2VuaXRvcnMuZGF0YSwKICAgICAgICBncm91cC5ieSA9ICJSZXZlbGlvLnBoYXNlIiwKICAgICAgICBwdC5zaXplID0gMC41LAogICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgIGNvbHMgPSAgYyh3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzE6M10sImdyZXk0MCIsd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVs1XSkpICYgTm9BeGVzKCkKCnAxICsgcDIgKyBwMyAgKyBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCiMjIENvbWJpbmVkIHByb2dlbml0b3JzIGFuZCBuZXVyb25zIGFsb25nIFBzZXVkb3RpbWUKCmBgYHtyfQojIFN0YXJ0IHdpdGggbmV1cm9ucyBkYXRhClRyYWplY3Rvcmllcy5hbGwgPC0gVHJhamVjdG9yaWVzLm5ldXJvbnMgJT4lIHNlbGVjdChCYXJjb2RlcywgbkNvdW50X1JOQSwgU3ByaW5nXzEsIFNwcmluZ18yLCBMaW5lYWdlKQoKVHJhamVjdG9yaWVzLmFsbCRQc2V1ZG90aW1lIDwtIFRyYWplY3Rvcmllcy5uZXVyb25zJFBzZXVkb3RpbWVTY29yZS5zaGlmdGVkICsgMC41ClRyYWplY3Rvcmllcy5hbGwkUGhhc2UgPC0gTkEKYGBgCgpgYGB7cn0KIyBBZGQgcHJvZ2VuaXRvcnMgZGF0YQpUcmFqZWN0b3JpZXMucHJvZ2VuaXRvcnMgPC0gUHJvZ2VuaXRvcnMuZGF0YUBtZXRhLmRhdGEgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChCYXJjb2RlcywgbkNvdW50X1JOQSwgU3ByaW5nXzEsIFNwcmluZ18yKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShMaW5lYWdlPSBpZmVsc2UoUHJvZ2VuaXRvcnMuZGF0YSRDZWxsX2lkZW50ID09ICJNZWRpYWxfcGFsbGl1bSIsICJQYWxsaWFsX25ldXJvbnMiLCAiQ2FqYWwtUmV0eml1c19uZXVyb25zIikgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHNldWRvdGltZT0gUHJvZ2VuaXRvcnMuZGF0YSRSZXZlbGlvLmNjLzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQaGFzZSA9IFByb2dlbml0b3JzLmRhdGEkUmV2ZWxpby5waGFzZSkKYGBgCgpgYGB7cn0KVHJhamVjdG9yaWVzLldUPC0gcmJpbmQoVHJhamVjdG9yaWVzLmFsbCwgVHJhamVjdG9yaWVzLnByb2dlbml0b3JzKQoKVHJhamVjdG9yaWVzLldUJFBoYXNlIDwtIGZhY3RvcihUcmFqZWN0b3JpZXMuV1QkUGhhc2UsIGxldmVscyA9IGMoIkcxLlMiLCAiUyIsICJHMiIsICJHMi5NIiwgIk0uRzEiKSkKYGBgCgpgYGB7ciBmaWcuZGltPWMoOSwzKX0KcDEgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5XVCwgYWVzKFNwcmluZ18xLCBTcHJpbmdfMikpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1Qc2V1ZG90aW1lKSwgc2l6ZT0wLjUpICsgCiAgICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnM9cmV2KGJyZXdlci5wYWwobiA9MTEsIG5hbWUgPSAiU3BlY3RyYWwiKSksIG5hbWU9J1BzZXVkb3RpbWUgc2NvcmUnKQoKcDIgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5XVCwgYWVzKFNwcmluZ18xLCBTcHJpbmdfMikpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gTGluZWFnZSksIHNpemU9MC41KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiI2NjMzkxYiIsICIjMDI2YzlhIikpCgpwMSArIHAyCmBgYAoKYGBge3IgZmlnLmRpbT1jKDksMyl9CnAxIDwtIGdncGxvdChUcmFqZWN0b3JpZXMuV1QsIGFlcyh4PSBQc2V1ZG90aW1lLCB5PSBuQ291bnRfUk5BLzEwMDAwKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBQaGFzZSksIHNpemU9MC41KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYyh3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzE6M10sImdyZXk0MCIsd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVs1XSkpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgbj0gNTAsIGZpbGw9ImdyZXkiKSArCiAgICAgICAgeWxpbSgwLE5BKQoKcDIgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5XVCwgYWVzKHg9IFBzZXVkb3RpbWUsIHk9IG5Db3VudF9STkEvMTAwMDApKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IExpbmVhZ2UpLCBzaXplPTAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKSArCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDUwLCBmaWxsPSJncmV5IikgKwogICAgICAgIHlsaW0oMCxOQSkKCnAxIC8gcDIKYGBgCmBgYHtyfQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIGMoIlRyYWplY3Rvcmllcy5XVCIpXSkKZ2MoKQpgYGAKCiMgUHNldWRvdGltZSBpbiB0aGUgS08KCmBgYHtyfQpXVC5LTy5pbnRlZ3JhdGVkIDwtIHJlYWRSRFMoIldUX0tPLmludGVncmF0ZWQuUkRTIikKYGBgCgojIyBFeHRyYWN0IENSIGFuZCBwYWxsaWFsIG5ldXJvbnMKCmBgYHtyfQpOZXVyb25zLmRhdGEgPC0gIHN1YnNldChXVC5LTy5pbnRlZ3JhdGVkLAogICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSBMaW5lYWdlICVpbiUgYygiQ2FqYWwtUmV0eml1c19uZXVyb25zIiwgIlBhbGxpYWxfbmV1cm9ucyIpICYgb3JpZy5pZGVudCA9PSAiR21uY19LTyIgJiBDZWxsLmlkZW50LktPICVpbiUgYygiTmV1cm9uX3Byb2IuMiIsICJOZXVyb25fcHJvYi4zIikpCgpEaW1QbG90KE5ldXJvbnMuZGF0YSwKICAgICAgICBncm91cC5ieSA9ICJMaW5lYWdlIiwKICAgICAgICByZWR1Y3Rpb24gPSAiaW50ZWdyYXRlZF9zcHJpbmciLAogICAgICAgIHB0LnNpemUgPSAxLAogICAgICAgIGNvbHMgPSAgYygiI2NjMzkxYiIsIiMwMjZjOWEiKQogICAgICAgICkgKyBOb0F4ZXMoKQpgYGAKCiMjIEZpdCBwcmluY2lwYWxlIGN1cnZlIG9uIHRoZSB0d28gbGluZWFnZXMKCmBgYHtyfQpmaXQgPC0gcHJpbmNpcGFsX2N1cnZlKGFzLm1hdHJpeChOZXVyb25zLmRhdGFAbWV0YS5kYXRhWyxjKCJTcHJpbmdfMSIsICJTcHJpbmdfMiIpXSksCiAgICAgICAgICAgICAgICAgICAgICAgc21vb3RoZXI9J2xvd2VzcycsCiAgICAgICAgICAgICAgICAgICAgICAgdHJhY2U9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICBmID0gMSwKICAgICAgICAgICAgICAgICAgICAgICBzdHJldGNoPTApCmBgYAoKYGBge3J9CiNQc2V1ZG90aW1lIHNjb3JlClBzZXVkb3RpbWVTY29yZSA8LSBmaXQkbGFtYmRhL21heChmaXQkbGFtYmRhKQoKaWYgKGNvcihQc2V1ZG90aW1lU2NvcmUsIE5ldXJvbnMuZGF0YUBhc3NheXMkUk5BQGRhdGFbJ0htZ2EyJywgXSkgPiAwKSB7CiAgTmV1cm9ucy5kYXRhJFBzZXVkb3RpbWVTY29yZSA8LSAtKFBzZXVkb3RpbWVTY29yZSAtIG1heChQc2V1ZG90aW1lU2NvcmUpKQp9Cgpjb2xzIDwtIGJyZXdlci5wYWwobiA9MTEsIG5hbWUgPSAiU3BlY3RyYWwiKQoKZ2dwbG90KE5ldXJvbnMuZGF0YUBtZXRhLmRhdGEsIGFlcyhTcHJpbmdfMSwgU3ByaW5nXzIpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9UHNldWRvdGltZVNjb3JlKSwgc2l6ZT0yLCBzaGFwZT0xNikgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3Vycz1yZXYoY29scyksIG5hbWU9J1BzZXVkb3RpbWUgc2NvcmUnKQpgYGAKCiMjIyBTaGlmdCBwc2V1ZG90aW1lIHNjb3JlCgpgYGB7cn0Kc2NvcmUgPC0gc2FwcGx5KE5ldXJvbnMuZGF0YSRQc2V1ZG90aW1lU2NvcmUsCiAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4KSBpZiAoeCA8PSAwLjI1KSB7eD0gMC4yNX0gZWxzZSB7IHg9eCB9KQoKTmV1cm9ucy5kYXRhJFBzZXVkb3RpbWVTY29yZS5zaGlmdGVkIDwtIChzY29yZSAtIG1pbihzY29yZSkpIC8gKG1heChzY29yZSkgLSBtaW4oc2NvcmUpKQpgYGAKCgpgYGB7cn0KZ2dwbG90KE5ldXJvbnMuZGF0YUBtZXRhLmRhdGEsIGFlcyh4PSBQc2V1ZG90aW1lU2NvcmUuc2hpZnRlZCwgeT0gbkNvdW50X1JOQS8xMDAwMCkpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gTGluZWFnZSksIHNpemU9MC41KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiI2NjMzkxYiIsICIjMDI2YzlhIikpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgbj0gNTAsIGFlcyhjb2xvcj0gTGluZWFnZSkpICsKICAgICAgICB5bGltKDAsTkEpCmBgYAoKIyMgRml0IGNlbGwgY3ljbGUgdHJhamVjdG9yeSBvbiBwcm9nZW5pdG9ycwoKVG8gYmFsYW5jZSB0aGUgbnVtYmVyIG9mIHByb2dlbml0b3JzIGluIGJvdGggZG9tYWluIHdlIHdpbGwgb25seSB3b3JrIHdpdGggKkhlbSogYW5kICpNZWRpYWxfcGFsbGl1bSogYW5ub3RhdGVkIGNlbGxzLiBTaW5jZSB3ZSBhcmUgdXNpbmcgcGFsbGlhbCBjZWxsIHRvIGNvbnRyYXN0IENSIHNwZWNpZmljIHRyYWplY3Rvcnkgd2UgdGhpbmsgdGhpcyBhcHByb3hpbWF0aW9uIHdpbGwgbm90IHNpZ25pZmljYW50bHkgYWZmZWN0IG91ciBhbmFseXNpcy4KCmBgYHtyfQpQcm9nZW5pdG9ycy5kYXRhIDwtICBzdWJzZXQoV1QuS08uaW50ZWdyYXRlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldCA9IExpbmVhZ2UgJWluJSBjKCJDYWphbC1SZXR6aXVzX25ldXJvbnMiLCAiUGFsbGlhbF9uZXVyb25zIikgJiBvcmlnLmlkZW50ID09ICJHbW5jX0tPIiAmIENlbGwuaWRlbnQuS08gJWluJSBjKCJIZW0iLCAiTWVkaWFsX3BhbGxpdW0iKSkKCkRpbVBsb3QoUHJvZ2VuaXRvcnMuZGF0YSwKICAgICAgICByZWR1Y3Rpb24gPSAiaW50ZWdyYXRlZF9zcHJpbmciLAogICAgICAgIGdyb3VwLmJ5ID0gIkNlbGwuaWRlbnQuS08iLAogICAgICAgIHB0LnNpemUgPSAxLAogICAgICAgIGNvbHMgPSAgYygiI2NjMzkxYiIsIiMwMjZjOWEiKQogICAgICAgICkgKyBOb0F4ZXMoKQoKcm0oV1QuS08uaW50ZWdyYXRlZCkKZ2MoKQpgYGAKCiMjIyBQcmVwYXJlIGRhdGEgZm9yIFJldmVsaW8KCmBgYHtyfQpyYXdDb3VudHMgPC0gYXMubWF0cml4KFByb2dlbml0b3JzLmRhdGFbWyJSTkEiXV1AY291bnRzKQoKIyBGaWx0ZXIgZ2VuZXMgZXhwcmVzc2VkIGJ5IGxlc3MgdGhhbiAxMCBjZWxscwpudW0uY2VsbHMgPC0gTWF0cml4Ojpyb3dTdW1zKHJhd0NvdW50cyA+IDApCmdlbmVzLnVzZSA8LSBuYW1lcyh4ID0gbnVtLmNlbGxzW3doaWNoKHggPSBudW0uY2VsbHMgPj0gMTApXSkKcmF3Q291bnRzIDwtIHJhd0NvdW50c1tnZW5lcy51c2UsIF0KYGBgCgojIyMgUnVuIFJldmVsaW8KCmBgYHtyfQpDQ2dlbmVzIDwtIHJlYWQudGFibGUoIi4uL0Nob3JvaWRQbGV4dXNfdHJhamVjdG9yeS9DQ2dlbmVzLmNzdiIsIHNlcCA9ICI7IiwgaGVhZGVyID0gVCkKYGBgCgpXZSBjYW4gbm93IGZvbGxvdyB0aGUgdHV0b3JpYWwgZm9ybSB0aGUgW3BhY2thZ2UgZ2l0aHViIHBhZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9kYW5pZWxzY2h3MTg4L1JldmVsaW8pCgoKYGBge3IgY2FjaGU9VFJVRX0KbXlEYXRhIDwtIGNyZWF0ZVJldmVsaW9PYmplY3QocmF3RGF0YSA9IHJhd0NvdW50cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3ljbGljR2VuZXMgPSBDQ2dlbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb3dlcm5HZW5lQ3V0b2ZmID0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBwZXJuVU1JQ3V0b2ZmID0gSW5mLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjY1BoYXNlQXNzaWduQmFzZWRPbkluZGl2aWR1YWxCYXRjaGVzID0gRikKCnJtKCJyYXdDb3VudHMiKQpnYygpCmBgYAoKVGhlIGdldENlbGxDeWNsZVBoYXNlQXNzaWduSW5mb3JtYXRpb24gZmlsdGVyIOKAnG91dGxpZXLigJ0gY2VsbHMgZm9yIGNlbGwgY3ljbGUgcGhhc2UgYXNzaWduYXRpb24uIFdlIG1vZGlmaWVkIHRoZSBmdW5jdGlvbiB0byBrZWVwIGFsbCBjZWxscyBhcyB3ZSBvYnNlcnZlZCB0aGlzIGRvZXMgbm90IGFmZmVjdCB0aGUgZ2xvYmFsIGNlbGwgY3ljbGUgZml0dGluZyBwcm9jZWR1cmUKCmBgYHtyIGNhY2hlPVRSVUV9CnNvdXJjZSgiLi4vRnVuY3Rpb25zL2Z1bmN0aW9uc19Jbml0aWFsaXphdGlvbkNDUGhhc2VBc3NpZ25GaWx0ZXJpbmcuUiIpCgpteURhdGEgPC0gZ2V0Q2VsbEN5Y2xlUGhhc2VBc3NpZ25fYWxsY2VsbHMobXlEYXRhKQpgYGAKCmBgYHtyIGNhY2hlPVRSVUV9Cm15RGF0YSA8LSBnZXRQQ0FEYXRhKGRhdGFMaXN0ID0gbXlEYXRhKQoKbXlEYXRhIDwtIGdldE9wdGltYWxSb3RhdGlvbihkYXRhTGlzdCA9IG15RGF0YSkKYGBgCgojIyMgR3JhcGhpY2FsIGFzc2VzbWVudCBvZiBjZWxsIGN5Y2xlIGZpdHRpbmcKCmBgYHtyfQpDZWxsQ3ljbGVkYXRhIDwtIGNiaW5kKGFzLmRhdGEuZnJhbWUodChteURhdGFAdHJhbnNmb3JtZWREYXRhJGRjJGRhdGFbMToyLF0pKSwKICAgICAgICAgICAgICAgICAgICAgICBuVU1JPSBteURhdGFAY2VsbEluZm8kblVNSSwKICAgICAgICAgICAgICAgICAgICAgICBSZXZlbGlvLnBoYXNlID0gZmFjdG9yKG15RGF0YUBjZWxsSW5mbyRjY1BoYXNlLCBsZXZlbHMgPSBjKCJHMS5TIiwgIlMiLCAiRzIiLCAiRzIuTSIsICJNLkcxIikpLAogICAgICAgICAgICAgICAgICAgICAgIFJldmVsaW8uY2M9IG15RGF0YUBjZWxsSW5mbyRjY1BlcmNlbnRhZ2VVbmlmb3JtbHlTcGFjZWQsCiAgICAgICAgICAgICAgICAgICAgICAgRG9tYWluPSBQcm9nZW5pdG9ycy5kYXRhJENlbGwuaWRlbnQuS08pCmBgYAoKYGBge3J9CnAxIDwtIGdncGxvdChDZWxsQ3ljbGVkYXRhLCBhZXMoREMxLCBEQzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBSZXZlbGlvLnBoYXNlKSwgc2l6ZT0gMC41KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYyh3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzE6M10sImdyZXk0MCIsd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVs1XSkpCgpwMiA8LSBnZ3Bsb3QoQ2VsbEN5Y2xlZGF0YSwgYWVzKERDMSwgREMyKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gRG9tYWluKSwgc2l6ZSA9IDAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCIjMDI2YzlhIikpCgpwMyA8LSBnZ3Bsb3QoQ2VsbEN5Y2xlZGF0YSwgYWVzKERDMSwgREMyKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPVJldmVsaW8uY2MpLCBzaXplPTAuNSwgc2hhcGU9MTYpICsgCiAgICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnM9cmV2KGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbChuID0xMSwgbmFtZSA9ICJTcGVjdHJhbCIpKSgxMDApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZT0nUmV2ZWxpb19jYycpCgpwNCA8LSBnZ3Bsb3QoQ2VsbEN5Y2xlZGF0YSwgYWVzKHg9IFJldmVsaW8uY2MsIHk9IG5VTUkvMTAwMDApKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IFJldmVsaW8ucGhhc2UpLCBzaXplPTAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMod2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVsxOjNdLCJncmV5NDAiLHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbNV0pKSArCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDUwLCBmaWxsPSJncmV5IikgKwogICAgICAgIHlsaW0oMCxOQSkKCihwMSArIHAyKSAvKHAzICsgcDQpCmBgYAoKYGBge3J9ClByb2dlbml0b3JzLmRhdGEkUmV2ZWxpby5waGFzZSA8LSBDZWxsQ3ljbGVkYXRhJFJldmVsaW8ucGhhc2UKUHJvZ2VuaXRvcnMuZGF0YSRSZXZlbGlvLmNjIDwtIENlbGxDeWNsZWRhdGEkUmV2ZWxpby5jYwoKcDEgPC0gRmVhdHVyZVBsb3Qob2JqZWN0ID0gUHJvZ2VuaXRvcnMuZGF0YSwKICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiUmV2ZWxpby5jYyIsCiAgICAgICAgICAgICAgICAgIHB0LnNpemUgPSAxLAogICAgICAgICAgICAgICAgICBjb2xzID0gcmV2KGJyZXdlci5wYWwoMTAsIlNwZWN0cmFsIikpLAogICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAiaW50ZWdyYXRlZF9zcHJpbmciLAogICAgICAgICAgICAgICAgICBvcmRlciA9IFQpICYgTm9BeGVzKCkKCnAyIDwtIERpbVBsb3Qob2JqZWN0ID0gUHJvZ2VuaXRvcnMuZGF0YSwKICAgICAgICAgICAgICBncm91cC5ieSA9ICJSZXZlbGlvLnBoYXNlIiwKICAgICAgICAgICAgICBwdC5zaXplID0gMSwKICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAiaW50ZWdyYXRlZF9zcHJpbmciLAogICAgICAgICAgICAgIGNvbHMgPSAgYyh3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzE6M10sImdyZXk0MCIsd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVs1XSkpICYgTm9BeGVzKCkKCnAxIC8gcDIKYGBgCgojIyMgVHJhbnNmZXJ0IGxlYXJuIGNlbGwgY3ljbGUgYXhpcwoKYGBge3J9ClByb2dlbml0b3JzIDwtIFByb2dlbml0b3JzLmRhdGFAbWV0YS5kYXRhICU+JSBzZWxlY3QoQmFyY29kZXMsIG5Db3VudF9STkEsIFNwcmluZ18xLCBTcHJpbmdfMiwgTGluZWFnZSkKClByb2dlbml0b3JzJFBzZXVkb3RpbWVTY29yZSA8LSBDZWxsQ3ljbGVkYXRhJFJldmVsaW8uY2MKUHJvZ2VuaXRvcnMkblVNSSA8LSBQcm9nZW5pdG9ycy5kYXRhJG5Db3VudF9STkEKYGBgCgojIyBDb21iaW5lIFByb2dlbml0b3JzIGFuZCBkaWZmZXJlbnRpYXRpbmcgbmV1cm9ucyBkYXRhCgpgYGB7cn0KIyBTdGFydCB3aXRoIG5ldXJvbnMgZGF0YQpUcmFqZWN0b3JpZXMuYWxsIDwtIE5ldXJvbnMuZGF0YUBtZXRhLmRhdGEgJT4lIHNlbGVjdChCYXJjb2RlcywgbkNvdW50X1JOQSwgU3ByaW5nXzEsIFNwcmluZ18yLCBMaW5lYWdlKQoKVHJhamVjdG9yaWVzLmFsbCRQc2V1ZG90aW1lIDwtIE5ldXJvbnMuZGF0YSRQc2V1ZG90aW1lU2NvcmUuc2hpZnRlZCArIDAuNQpUcmFqZWN0b3JpZXMuYWxsJFBoYXNlIDwtIE5BCmBgYAoKYGBge3J9CiMgQWRkIHByb2dlbml0b3JzIGRhdGEKVHJhamVjdG9yaWVzLnByb2dlbml0b3JzIDwtIFByb2dlbml0b3JzICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoQmFyY29kZXMsIG5Db3VudF9STkEsIFNwcmluZ18xLCBTcHJpbmdfMiwgTGluZWFnZSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoUHNldWRvdGltZT0gUHJvZ2VuaXRvcnMuZGF0YSRSZXZlbGlvLmNjLzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQaGFzZSA9IFByb2dlbml0b3JzLmRhdGEkUmV2ZWxpby5waGFzZSkKYGBgCgpgYGB7cn0KVHJhamVjdG9yaWVzLktPIDwtIHJiaW5kKFRyYWplY3Rvcmllcy5hbGwsIFRyYWplY3Rvcmllcy5wcm9nZW5pdG9ycykKClRyYWplY3Rvcmllcy5LTyRQaGFzZSA8LSBmYWN0b3IoVHJhamVjdG9yaWVzLktPJFBoYXNlLCBsZXZlbHMgPSBjKCJHMS5TIiwgIlMiLCAiRzIiLCAiRzIuTSIsICJNLkcxIikpCmBgYAoKYGBge3J9CnAxIDwtIGdncGxvdChUcmFqZWN0b3JpZXMuS08sIGFlcyhTcHJpbmdfMSwgU3ByaW5nXzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9UHNldWRvdGltZSksIHNpemU9MC41KSArIAogICAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzPXJldihicmV3ZXIucGFsKG4gPTExLCBuYW1lID0gIlNwZWN0cmFsIikpLCBuYW1lPSdQc2V1ZG90aW1lIHNjb3JlJykKCnAyIDwtIGdncGxvdChUcmFqZWN0b3JpZXMuS08sIGFlcyhTcHJpbmdfMSwgU3ByaW5nXzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IExpbmVhZ2UpLCBzaXplPTAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKQoKcDEgKyBwMgpgYGAKCmBgYHtyfQpwMSA8LSBnZ3Bsb3QoVHJhamVjdG9yaWVzLktPLCBhZXMoeD0gUHNldWRvdGltZSwgeT0gbkNvdW50X1JOQS8xMDAwMCkpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gUGhhc2UpLCBzaXplPTAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMod2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVsxOjNdLCJncmV5NDAiLHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbNV0pKSArCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDUwLCBmaWxsPSJncmV5IikgKwogICAgICAgIHlsaW0oMCxOQSkKCnAyIDwtIGdncGxvdChUcmFqZWN0b3JpZXMuS08sIGFlcyh4PSBQc2V1ZG90aW1lLCB5PSBuQ291bnRfUk5BLzEwMDAwKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkgKwogICAgICAgIGdlb21fc21vb3RoKGFlcyhjb2xvcj0gTGluZWFnZSksIG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgZmlsbD0iZ3JleSIpICsKICAgICAgICB5bGltKDAsTkEpCgpwMSAvIHAyCmBgYAoKYGBge3J9ClRyYWplY3RvcmllcyA8LSByYmluZChUcmFqZWN0b3JpZXMuS08sIFRyYWplY3Rvcmllcy5XVCkKCnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgYygiVHJhamVjdG9yaWVzIildKQpgYGAKCgojIFN1YnNldCB0aGUgZnVsbCBpbnRlZ3JhdGVkIGRhdGFzZXQKCmBgYHtyfQpXVC5LTy5pbnRlZ3JhdGVkIDwtIHJlYWRSRFMoIldUX0tPLmludGVncmF0ZWQuUkRTIikKCm1ldGEuZGF0YSA8LSBXVC5LTy5pbnRlZ3JhdGVkQG1ldGEuZGF0YVtUcmFqZWN0b3JpZXMkQmFyY29kZXMsXQptZXRhLmRhdGEkUHNldWRvdGltZSA8LSBUcmFqZWN0b3JpZXMkUHNldWRvdGltZQptZXRhLmRhdGEkUGhhc2UgPC0gVHJhamVjdG9yaWVzJFBoYXNlCmBgYAoKYGBge3J9CldULktPLmludGVncmF0ZWQgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IFdULktPLmludGVncmF0ZWRAYXNzYXlzJFJOQUBjb3VudHNbLCBUcmFqZWN0b3JpZXMkQmFyY29kZXNdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhLmRhdGEgPSBtZXRhLmRhdGEpCgpzcHJpbmcgPC0gYXMubWF0cml4KFdULktPLmludGVncmF0ZWRAbWV0YS5kYXRhICU+JSBzZWxlY3QoIkludGVncmF0ZWRfU3ByaW5nXzEiLCAiSW50ZWdyYXRlZF9TcHJpbmdfMiIpKQogIApXVC5LTy5pbnRlZ3JhdGVkW1siaW50ZWdyYXRlZF9zcHJpbmciXV0gPC0gQ3JlYXRlRGltUmVkdWNPYmplY3QoZW1iZWRkaW5ncyA9IHNwcmluZywga2V5ID0gIlNwcmluZ18iLCBhc3NheSA9IERlZmF1bHRBc3NheShXVC5LTy5pbnRlZ3JhdGVkKSkKYGBgCgpgYGB7cn0KcDEgPC0gRmVhdHVyZVBsb3Qob2JqZWN0ID0gV1QuS08uaW50ZWdyYXRlZCwKICAgICAgICAgICAgZmVhdHVyZXMgPSAiUHNldWRvdGltZSIsCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgIGNvbHMgPSByZXYoY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKG4gPTExLCBuYW1lID0gIlNwZWN0cmFsIikpKDEwMCkpLAogICAgICAgICAgICByZWR1Y3Rpb24gPSAiaW50ZWdyYXRlZF9zcHJpbmciLAogICAgICAgICAgICBvcmRlciA9IFQpICYgTm9BeGVzKCkKCnAyIDwtIERpbVBsb3Qob2JqZWN0ID0gV1QuS08uaW50ZWdyYXRlZCwKICAgICAgICBncm91cC5ieSA9ICJMaW5lYWdlIiwKICAgICAgICBwdC5zaXplID0gMC41LAogICAgICAgIHJlZHVjdGlvbiA9ICJpbnRlZ3JhdGVkX3NwcmluZyIsCiAgICAgICAgY29scyA9ICBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkgJiBOb0F4ZXMoKQoKCnAzIDwtIERpbVBsb3Qob2JqZWN0ID0gV1QuS08uaW50ZWdyYXRlZCwKICAgICAgICBncm91cC5ieSA9ICJQaGFzZSIsCiAgICAgICAgcHQuc2l6ZSA9IDAuNSwKICAgICAgICByZWR1Y3Rpb24gPSAiaW50ZWdyYXRlZF9zcHJpbmciLAogICAgICAgIGNvbHMgPSAgYyh3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzE6M10sImdyZXk0MCIsd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVs1XSkpICYgTm9BeGVzKCkKCnAxICsgcDIgKyBwMwoKZ2dzYXZlKCJ+L0J1cmVhdS9GaWc0X01NL1NwcmluZ19XVF9LT19Qc2V1ZG90aW1lLnBkZiIsCiAgICAgICBkZXZpY2UgPSAicGRmIiwKICAgICAgIGRwaSA9ICJyZXRpbmEiKQpgYGAKCiMjIyBOb3JtYWxpemF0aW9uCgpgYGB7cn0KV1QuS08uaW50ZWdyYXRlZCA8LSBOb3JtYWxpemVEYXRhKFdULktPLmludGVncmF0ZWQsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIHNjYWxlLmZhY3RvciA9IDEwMDAwLCBhc3NheSA9ICJSTkEiKQpgYGAKCmBgYHtyfQpXVC5LTy5pbnRlZ3JhdGVkIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKFdULktPLmludGVncmF0ZWQsIHNlbGVjdGlvbi5tZXRob2QgPSAiZGlzcCIsIG5mZWF0dXJlcyA9IDY1MDAsIGFzc2F5ID0gIlJOQSIpCmBgYAoKIyMjIFBsb3Qgc29tZSBnZW5lcyBhbG9uZyBwc2V1ZG90aW1lCgpgYGB7ciBmaWcuZGltPWMoMTUsOSksIHdhcm5pbmc9RkFMU0V9CnNvdXJjZSgiLi4vRnVuY3Rpb25zL2Z1bmN0aW9uc19HZW5lVHJlbmRzLlIiKQoKUGxvdC5HZW5lcy50cmVuZChTZXVyYXQuZGF0YT0gV1QuS08uaW50ZWdyYXRlZCwKICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJHZW5vdHlwZSIsCiAgICAgICAgICAgICAgICAgZ2VuZXM9IGMoIkdhczEiLCJTb3gyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiTmV1cm9nMiIsICJCdGcyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiVGJyMSIsICJNYXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJwNzMiLCAiRm94ZzEiKSkKCmdnc2F2ZSgifi9CdXJlYXUvRmlnNF9NTS9OZXVyby1HZW5lc190cmVuZF9XVC1LTy5wZGYiLAogICAgICAgd2lkdGggPSAxNSwgaGVpZ2h0ID0gOSwKICAgICAgIGRldmljZSA9ICJwZGYiLAogICAgICAgZHBpID0gInJldGluYSIpCmBgYAoKYGBge3IgZmlnLmRpbT1jKDE1LDkpLCB3YXJuaW5nPUZBTFNFfQpQbG90LkdlbmVzLnRyZW5kKFNldXJhdC5kYXRhPSBXVC5LTy5pbnRlZ3JhdGVkLAogICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gIkdlbm90eXBlIiwKICAgICAgICAgICAgICAgICBnZW5lcz0gYygiR21uYyIsICJNY2lkYXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJDY25vIiwgIkNjZGM2NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkZveGoxIiwgIlRycDczIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiTGh4MSIsICJDZGtuMWEiKSkKCmdnc2F2ZSgifi9CdXJlYXUvRmlnNF9NTS9HbW5jLUdlbmVzX3RyZW5kX1dULUtPLnBkZiIsCiAgICAgICB3aWR0aCA9IDE1LCBoZWlnaHQgPSA5LAogICAgICAgZGV2aWNlID0gInBkZiIsCiAgICAgICBkcGkgPSAicmV0aW5hIikKYGBgCgpgYGB7ciB3YXJuaW5nPUZBTFNFLCBmaWcuZGltPWMoNSwyKX0KQ1IgPC0gV1QuS08uaW50ZWdyYXRlZEBtZXRhLmRhdGEgJT4lIGZpbHRlcihMaW5lYWdlID09ICJDYWphbC1SZXR6aXVzX25ldXJvbnMiKSAlPiUgc2VsZWN0KEJhcmNvZGVzLFBzZXVkb3RpbWUsb3JpZy5pZGVudCkKCkNSLmdlbmVzIDwtIGFzLmRhdGEuZnJhbWUodChXVC5LTy5pbnRlZ3JhdGVkQGFzc2F5cyRSTkFAZGF0YVtjKCJDYWNuYTJkMiIsIlJlbG4iLCAiTmhsaDIiLCAiRWJmMyIpLENSJEJhcmNvZGVzXSkpCkNSLmdlbmVzJFBzZXVkb3RpbWUgPC0gQ1IkUHNldWRvdGltZQpDUi5nZW5lcyRHZW5vdHlwZSA8LSBmYWN0b3IoQ1Ikb3JpZy5pZGVudCwgbGV2ZWxzID0gYygiSGVtMSIsICJHbW5jX0tPIikgKSAKCkNSLmdlbmVzICA8LSByZXNoYXBlMjo6bWVsdChDUi5nZW5lcywgaWQgPSBjKCJQc2V1ZG90aW1lIiwgIkdlbm90eXBlIikpCgoKZ2dwbG90KENSLmdlbmVzLCBhZXMoeD0gUHNldWRvdGltZSwgeT0gdmFsdWUpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDUwLCBhZXMoY29sb3I9IHZhcmlhYmxlLCBsaW5ldHlwZT0gR2Vub3R5cGUpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYyh3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzE6M10sd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVs1XSkpICsKICB5bGltKDAsTkEpCgpnZ3NhdmUoIn4vQnVyZWF1L0ZpZzRfTU0vQ1ItR2VuZXNfdHJlbmRfV1QtS08ucGRmIiwKICAgICAgIHdpZHRoID0gNSwgaGVpZ2h0ID0gMiwKICAgICAgIGRldmljZSA9ICJwZGYiLAogICAgICAgZHBpID0gInJldGluYSIpCmBgYAoKIyMgVXNlIG1vbm9jbGUyIHRvIG1vZGVsIGdlbmUgZXhwcmVzc2lvbiBhbG9uZyBjeWNsaW5nIGF4aXMKCmBgYHtyfQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIGMoIldULktPLmludGVncmF0ZWQiKV0pCmdjKCkKYGBgCgojIyMgSW5pdGlhbGl6ZSBhIG1vbm9jbGUgb2JqZWN0CgpgYGB7cn0KIyBUcmFuc2ZlciBtZXRhZGF0YQpBbm5vdC5kYXRhICA8LSBuZXcoJ0Fubm90YXRlZERhdGFGcmFtZScsCiAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YS5mcmFtZShCYXJjb2RlPSBXVC5LTy5pbnRlZ3JhdGVkJEJhcmNvZGVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTGluZWFnZT0gV1QuS08uaW50ZWdyYXRlZCRMaW5lYWdlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHNldWRvdGltZT0gV1QuS08uaW50ZWdyYXRlZCRQc2V1ZG90aW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGhhc2U9IFdULktPLmludGVncmF0ZWQkUGhhc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW5vdHlwZT0gV1QuS08uaW50ZWdyYXRlZCRvcmlnLmlkZW50KSkKCiMgVHJhbnNmZXIgY291bnRzIGRhdGEKdmFyLmdlbmVzIDwtIFdULktPLmludGVncmF0ZWRbWyJSTkEiXV1AdmFyLmZlYXR1cmVzCgpmZWF0dXJlLmRhdGEgPC0gbmV3KCdBbm5vdGF0ZWREYXRhRnJhbWUnLAogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhLmZyYW1lKGdlbmVfc2hvcnRfbmFtZSA9IHJvd25hbWVzKFdULktPLmludGVncmF0ZWRbWyJSTkEiXV1AY291bnRzW3Zhci5nZW5lcyxdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSByb3duYW1lcyhXVC5LTy5pbnRlZ3JhdGVkW1siUk5BIl1dQGNvdW50c1t2YXIuZ2VuZXMsXSkpKQoKIyBDcmVhdGUgdGhlIENlbGxEYXRhU2V0IG9iamVjdCBpbmNsdWRpbmcgdmFyaWFibGUgZ2VuZXMgb25seQpnYm1fY2RzIDwtIG5ld0NlbGxEYXRhU2V0KFdULktPLmludGVncmF0ZWRbWyJSTkEiXV1AY291bnRzW3Zhci5nZW5lcyxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGEgPSBBbm5vdC5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlRGF0YSA9IGZlYXR1cmUuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG93ZXJEZXRlY3Rpb25MaW1pdCA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cHJlc3Npb25GYW1pbHkgPSBuZWdiaW5vbWlhbCgpKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUV9CmdibV9jZHMgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhnYm1fY2RzKQpnYm1fY2RzIDwtIGVzdGltYXRlRGlzcGVyc2lvbnMoZ2JtX2NkcykKZ2JtX2NkcyA8LSBkZXRlY3RHZW5lcyhnYm1fY2RzLCBtaW5fZXhwciA9IDAuMSkKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCJXVC5LTy5pbnRlZ3JhdGVkIiwgImdibV9jZHMiKV0pCmdjKCkKYGBgCiMgREVHIGdlbmVzIGluIHRoZSBLTwoKIyMgRmluZCBERUcgaW4gdGhlIEtPCgpgYGB7ciBjYWNoZT1UUlVFfQpLTy5wRGF0YSA8LSBwRGF0YShnYm1fY2RzKSAlPiUgc3Vic2V0KEdlbm90eXBlID09ICJHbW5jX0tPIikKCnBzZXVkby5tYXR1cmF0aW9uLmRpZmYgPC0gZGlmZmVyZW50aWFsR2VuZVRlc3QoZ2JtX2Nkc1tmRGF0YShnYm1fY2RzKSRudW1fY2VsbHNfZXhwcmVzc2VkID4gODAsIEtPLnBEYXRhJEJhcmNvZGVdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGxNb2RlbEZvcm11bGFTdHIgPSAifnNtLm5zKFBzZXVkb3RpbWUsIGRmID0gMykqTGluZWFnZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWNlZE1vZGVsRm9ybXVsYVN0ciA9ICJ+c20ubnMoUHNldWRvdGltZSwgZGYgPSAzKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSAtIDIpCgojIEZpbHRlciBnZW5lcyBiYXNlZCBvbiBGRFIKcHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZCA8LSBwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmICU+JSBmaWx0ZXIocXZhbCA8IDFlLTQwKQpgYGAKCmBgYHtyIGNhY2hlPVRSVUV9CiMgQ3JlYXRlIGEgbmV3IHBzZXVkby1EViB2ZWN0b3Igb2YgMzAwIHBvaW50cwpuUG9pbnRzIDwtIDMwMAoKbmV3X2RhdGEgPSBsaXN0KCkKZm9yIChMaW5lYWdlIGluIHVuaXF1ZShLTy5wRGF0YSRMaW5lYWdlKSl7CiAgbmV3X2RhdGFbW2xlbmd0aChuZXdfZGF0YSkgKyAxXV0gPSBkYXRhLmZyYW1lKFBzZXVkb3RpbWUgPSBzZXEobWluKEtPLnBEYXRhJFBzZXVkb3RpbWUpLCBtYXgoS08ucERhdGEkUHNldWRvdGltZSksIGxlbmd0aC5vdXQgPSBuUG9pbnRzKSwgTGluZWFnZT1MaW5lYWdlKQp9CgpuZXdfZGF0YSA9IGRvLmNhbGwocmJpbmQsIG5ld19kYXRhKQoKIyBTbW9vdGggZ2VuZSBleHByZXNzaW9uCkRpZmYuY3VydmVfbWF0cml4IDwtIGdlblNtb290aEN1cnZlcyhnYm1fY2RzW2FzLmNoYXJhY3Rlcihwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmLmZpbHRlcmVkJGdlbmVfc2hvcnRfbmFtZSksIEtPLnBEYXRhJEJhcmNvZGVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyZW5kX2Zvcm11bGEgPSAifnNtLm5zKFBzZXVkb3RpbWUsIGRmID0gMykqTGluZWFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpdmVfZXhwciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3X2RhdGEgPSBuZXdfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3Jlcz0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkgLSAyKQpgYGAKCiMjIENvbXB1dGUgdGhlIEFCQyBmb3IgZWFjaCBnZW5lCgpgYGB7cn0KIyBFeHRyYWN0IG1hdHJpeCBjb250YWluaW5nIHNtb290aGVkIGN1cnZlcyBmb3IgZWFjaCBsaW5lYWdlcwpQYWxfY3VydmVfbWF0cml4IDwtIERpZmYuY3VydmVfbWF0cml4WywgMTpuUG9pbnRzXSAjUGFsbGlhbCBwb2ludHMKQ1JfY3VydmVfbWF0cml4IDwtIERpZmYuY3VydmVfbWF0cml4WywgKG5Qb2ludHMgKyAxKTooMiAqIG5Qb2ludHMpXSAjQ1IgcG9pbnRzCgojIERpcmVjdGlvbiBvZiB0aGUgY29tcGFyaXNvbiA6IHBvc3RpdmUgQUJDcyA8PT4gVXByZWd1bGF0ZWQgaW4gQ1IgbGluZWFnZQpBQkNzX3JlcyA8LSBDUl9jdXJ2ZV9tYXRyaXggLSBQYWxfY3VydmVfbWF0cml4CgojIEF2ZXJhZ2UgbG9nRkMgYmV0d2VlbiB0aGUgMiBjdXJ2ZXMKSUxSX3JlcyA8LSBsb2cyKENSX2N1cnZlX21hdHJpeC8gKFBhbF9jdXJ2ZV9tYXRyaXggKyAwLjEpKSAKICAKQUJDc19yZXMgPC0gYXBwbHkoQUJDc19yZXMsIDEsIGZ1bmN0aW9uKHgsIG5Qb2ludHMpIHsKICAgICAgICAgICAgICAgICAgYXZnX2RlbHRhX3ggPC0gKHhbMTooblBvaW50cyAtIDEpXSArIHhbMjooblBvaW50cyldKS8yCiAgICAgICAgICAgICAgICAgIHN0ZXAgPC0gKDEwMC8oblBvaW50cyAtIDEpKQogICAgICAgICAgICAgICAgICByZXMgPC0gcm91bmQoc3VtKGF2Z19kZWx0YV94ICogc3RlcCksIDMpCiAgICAgICAgICAgICAgICAgIHJldHVybihyZXMpfSwKICAgICAgICAgICAgICAgICAgblBvaW50cyA9IG5Qb2ludHMpICMgQ29tcHV0ZSB0aGUgYXJlYSBiZWxvdyB0aGUgY3VydmUKICAKQUJDc19yZXMgPC0gY2JpbmQoQUJDc19yZXMsIElMUl9yZXNbLG5jb2woSUxSX3JlcyldKQpjb2xuYW1lcyhBQkNzX3Jlcyk8LSBjKCJBQkNzIiwgIkVuZHBvaW50X0lMUiIpCgojIEltcG9ydCBBQkMgdmFsdWVzIGludG8gdGhlIERFIHRlc3QgcmVzdWx0cyB0YWJsZQpwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmLmZpbHRlcmVkIDwtIGNiaW5kKHBzZXVkby5tYXR1cmF0aW9uLmRpZmYuZmlsdGVyZWRbLDE6NF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQUJDc19yZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZFssNTo2XSkKYGBgCgojIyBLTyBDYWphbC1SZXR6aXVzIGNlbGxzIHNwZWNpZmljIHRyYWplY3RvcnkgYW5hbHlzaXMKCmBgYHtyfQojIEV4dHJhY3QgQ2FqYWwtUmV0eml1cyBleHByZXNzZWQgZ2VuZXMKQ1IucmVzIDwtIGFzLmRhdGEuZnJhbWUocHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZFtwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmLmZpbHRlcmVkJEFCQ3MgPiAwLF0pCkNSLmdlbmVzIDwtIHJvdy5uYW1lcyhDUi5yZXMpCgpDUl9jdXJ2ZV9tYXRyaXggPC0gQ1JfY3VydmVfbWF0cml4W0NSLmdlbmVzLCBdCmBgYAoKYGBge3J9ClBzZXVkb3RpbWUuZ2VuZXMuY2x1c3RlcnMgPC0gY2x1c3Rlcjo6cGFtKGFzLmRpc3QoKDEgLSBjb3IoTWF0cml4Ojp0KENSX2N1cnZlX21hdHJpeCksbWV0aG9kID0gInBlYXJzb24iKSkpLCBrPSA1KQoKQ1IuR2VuZS5keW5hbWlxdWUgPC0gZGF0YS5mcmFtZShHZW5lPSBuYW1lcyhQc2V1ZG90aW1lLmdlbmVzLmNsdXN0ZXJzJGNsdXN0ZXJpbmcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYXZlcz0gUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW5lLkNsdXN0ZXJzID0gUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxLnZhbCA9IENSLnJlcyRxdmFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBQkNzPSBDUi5yZXMkQUJDcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSBhcnJhbmdlKEdlbmUuQ2x1c3RlcnMpCgpyb3cubmFtZXMoQ1IuR2VuZS5keW5hbWlxdWUpIDwtIENSLkdlbmUuZHluYW1pcXVlJEdlbmUKQ1IuR2VuZS5keW5hbWlxdWUkR2VuZS5DbHVzdGVycyA8LSBwYXN0ZTAoIkNsdXN0LiIsIENSLkdlbmUuZHluYW1pcXVlJEdlbmUuQ2x1c3RlcnMpCgp3cml0ZS50YWJsZShDUi5HZW5lLmR5bmFtaXF1ZSwgIktPX0NSX2R5bmFtaWNfZ2VuZXMuY3N2Iiwgc2VwID0gIjsiLCBxdW90ZSA9IEYsIHJvdy5uYW1lcyA9IEYpCmBgYAoKYGBge3J9CiMgT3JkZXIgdGhlIHJvd3MgdXNpbmcgc2VyaWF0aW9uCmRzdCA8LSBhcy5kaXN0KCgxLWNvcihzY2FsZSh0KENSX2N1cnZlX21hdHJpeCkpLCBtZXRob2QgPSAicGVhcnNvbiIpKSkKcm93LnNlciA8LSBzZXJpYXRpb246OnNlcmlhdGUoZHN0LCBtZXRob2QgPSJSMkUiKSAjIlIyRSIgI1RTUCAjIkdXIiAiR1dfd2FyZCIKZ2VuZS5vcmRlciA8LSByb3duYW1lcyhDUl9jdXJ2ZV9tYXRyaXhbZ2V0X29yZGVyKHJvdy5zZXIpLF0pCgojIFNldCBhbm5vdGF0aW9uIGNvbG9ycwpwYWwgPC0gd2VzX3BhbGV0dGUoIkRhcmplZWxpbmcxIikKYW5uby5jb2xvcnMgPC0gbGlzdChsaW5lYWdlID0gYyhLT19QYWxsaWFsX25ldXJvbnM9IiMwMjZjOWEiLCBLT19DYWphbF9SZXR6aXVzPSIjY2MzOTFiIiksCiAgICAgICAgICAgICAgICAgICAgR2VuZS5DbHVzdGVycyA9IGMoQ2x1c3QuMSA9cGFsWzFdICwgQ2x1c3QuMj1wYWxbMl0sIENsdXN0LjM9cGFsWzNdLCBDbHVzdC40PXBhbFs0XSwgQ2x1c3QuNT1wYWxbNV0pKQpgYGAKCmBgYHtyfQpwaGVhdG1hcDo6cGhlYXRtYXAoRGlmZi5jdXJ2ZV9tYXRyaXhbZ2VuZS5vcmRlcltjKDIzNToxLDI5MjoyMzYpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKDMwMDoxLCNQYWwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAzMDE6NjAwKV0sICNDUgogICAgICAgICAgICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZHBseXI6OnNlbGVjdChHZW5lLkNsdXN0ZXJzKSwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gZGF0YS5mcmFtZShsaW5lYWdlID0gcmVwKGMoIktPX1BhbGxpYWxfbmV1cm9ucyIsIktPX0NhamFsX1JldHppdXMiKSwgZWFjaD0zMDApKSwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5uby5jb2xvcnMsCiAgICAgICAgICAgICAgICAgICBnYXBzX2NvbCA9IGMoMjAwLDQwMCksCiAgICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgZm9udHNpemVfcm93ID0gOCwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIHZpcmlkaXM6OnZpcmlkaXMoOSksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTIuNSwyLjUsIGxlbmd0aC5vdXQgPSA5KSwKICAgICAgICAgICAgICAgICAgIG1haW4gPSAiIikKYGBgCgojIyBJbnRlcnNlY3QgZHluYW1pY2FsIGdlbmVzIGluIENQeCBhbmQgQ1IgaW4gV1QgYW5kIEtPCgpgYGB7cn0KV1QuQ1IgPC0gcmVhZC50YWJsZSgiLi4vQ2FqYWxSZXR6aXVzX3RyYWplY3RvcnkvQ1JfZHluYW1pY19nZW5lcy5jc3YiLCBzZXAgPSAiOyIsIGhlYWRlciA9IFQpCldULkNQeCA8LSByZWFkLnRhYmxlKCIuLi9DaG9yb2lkUGxleHVzX3RyYWplY3RvcnkvQ2hQLkdlbmUuZHluYW1pcXVlLmNzdiIsIHNlcCA9ICI7IiwgaGVhZGVyID0gVCkKCktPLkNSIDwtIHJlYWQudGFibGUoIi4vS09fQ1JfZHluYW1pY19nZW5lcy5jc3YiLCBzZXAgPSAiOyIsIGhlYWRlciA9IFQpCktPLkNQeCA8LSByZWFkLnRhYmxlKCIuL0tPX0NQeF9keW5hbWljX2dlbmVzLmNzdiIsIHNlcCA9ICI7IiwgaGVhZGVyID0gVCkKCmxpc3RJbnB1dCA8LSBsaXN0KFdULkNSPSBXVC5DUiRHZW5lLCBXVC5DUHg9IFdULkNQeCRHZW5lLCBLTy5DUj0gS08uQ1IkR2VuZSwgS08uQ1B4PSBLTy5DUHgkR2VuZSkKYGBgCgpgYGB7cn0KdXBzZXQoZnJvbUxpc3QobGlzdElucHV0KSwKICAgICAgb3JkZXIuYnkgPSAiZnJlcSIsCiAgICAgIHF1ZXJpZXMgPSBsaXN0KGxpc3QocXVlcnkgPSBpbnRlcnNlY3RzLCBwYXJhbXMgPSBsaXN0KCJXVC5DUiIsICJXVC5DUHgiKSwgY29sb3IgPSAib3JhbmdlIiwgYWN0aXZlID0gVCksCiAgICAgICAgICAgICAgICAgICAgIGxpc3QocXVlcnkgPSBpbnRlcnNlY3RzLCBwYXJhbXMgPSBsaXN0KCJXVC5DUHgiLCAiS08uQ1IiLCAiV1QuQ1IiKSwgY29sb3IgPSAicmVkIiwgYWN0aXZlID0gVCksCiAgICAgICAgICAgICAgICAgICAgIGxpc3QocXVlcnkgPSBpbnRlcnNlY3RzLCBwYXJhbXMgPSBsaXN0KCJLTy5DUiIsICJXVC5DUiIpLCBjb2xvciA9ICJibHVlIiwgYWN0aXZlID0gVCkpKQpgYGAKYGBge3J9CnVwc2V0KGZyb21MaXN0KGxpc3RJbnB1dCksCiAgICAgIG9yZGVyLmJ5ID0gImZyZXEiLAogICAgICBpbnRlcnNlY3Rpb25zID0gbGlzdChsaXN0KCJXVC5DUiIsICJXVC5DUHgiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCgiS08uQ1IiLCAiV1QuQ1IiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCgiV1QuQ1B4IiwgIktPLkNSIiwgIldULkNSIikpLAogICAgICBxdWVyaWVzID0gbGlzdChsaXN0KHF1ZXJ5ID0gaW50ZXJzZWN0cywgcGFyYW1zID0gbGlzdCgiV1QuQ1IiLCAiV1QuQ1B4IiksIGNvbG9yID0gIm9yYW5nZSIsIGFjdGl2ZSA9IFQpLAogICAgICAgICAgICAgICAgICAgICBsaXN0KHF1ZXJ5ID0gaW50ZXJzZWN0cywgcGFyYW1zID0gbGlzdCgiV1QuQ1B4IiwgIktPLkNSIiwgIldULkNSIiksIGNvbG9yID0gInJlZCIsIGFjdGl2ZSA9IFQpLAogICAgICAgICAgICAgICAgICAgICBsaXN0KHF1ZXJ5ID0gaW50ZXJzZWN0cywgcGFyYW1zID0gbGlzdCgiS08uQ1IiLCAiV1QuQ1IiKSwgY29sb3IgPSAiYmx1ZSIsIGFjdGl2ZSA9IFQpKQogICAgICApCmBgYAoKYGBge3J9CiMgU2hhcmVkIGdlbmVzIGJlZm9yZSBHbW5jIGluZHVjdGlvbgpXVC5DUi5hbnRlR21uYyA8LSBXVC5DUiAlPiUgZmlsdGVyKFdhdmVzICVpbiUgYygxLDMpKSAlPiUgcHVsbChHZW5lKQpLTy5DUi5hbnRlR21uYyA8LSBLTy5DUiAlPiUgZmlsdGVyKFdhdmVzICVpbiUgYygyLDMsNSkpICU+JSBwdWxsKEdlbmUpCgpnZW5lLmxpc3QgPC0gbGlzdChXVC5DUiA9IFdULkNSLmFudGVHbW5jLCBLTy5DUiA9IEtPLkNSLmFudGVHbW5jKQpnZ3Zlbm46OmdndmVubihnZW5lLmxpc3QpCmBgYAoKCmBgYHtyfQojIFNoYXJlZCBnZW5lcyBhZnRlciBHbW5jIGluZHVjdGlvbgpXVC5DUi5wb3N0R21uYyAgPC0gV1QuQ1IgJT4lIGZpbHRlcihXYXZlcyAlaW4lIGMoMiw1LDQpKSAlPiUgcHVsbChHZW5lKQpLTy5DUi5wb3N0R21uYyA8LSBLTy5DUiAlPiUgZmlsdGVyKFdhdmVzICVpbiUgYygxLDQpKSAlPiUgcHVsbChHZW5lKQoKCmdlbmUubGlzdCA8LSBsaXN0KFdULkNSID0gV1QuQ1IucG9zdEdtbmMsIEtPLkNSID0gS08uQ1IucG9zdEdtbmMpCmdndmVubjo6Z2d2ZW5uKGdlbmUubGlzdCkKCmBgYAoKCiMgUGxvdCBXVCBDUiBkeW5hbWljIGdlbmVzIGFsb25nIFdUIGFuZCBLTyB0cmFqZWN0b3JpZXMKCiMjIEFsbCBDUiBlbnJpY2hlZCBnZW5lcyBpZGVudGlmaWVkIGluIHRoZSBXVCBkYXRhc2V0CgpgYGB7ciBjYWNoZT1UUlVFfQojIExvYWQgV1QgQ1IgdmFyaWFibGUgZ2VuZXMgYWxvbmcgcHNldWRvdGltZQpXVC5DUi5nZW5lcyA8LSByZWFkLnRhYmxlKCIuLi9DYWphbFJldHppdXNfdHJhamVjdG9yeS9DUl9keW5hbWljX2dlbmVzLmNzdiIsIHNlcCA9ICI7IiwgaGVhZGVyID0gVCkKCiMgRXh0cmFjdCBDUiBsaW5lYWdlIGluIGJvdGggZ2Vub3R5cGVzCkNSLnBEYXRhIDwtIHBEYXRhKGdibV9jZHMpICU+JSBzdWJzZXQoTGluZWFnZSA9PSAiQ2FqYWwtUmV0eml1c19uZXVyb25zIikKCiMgQ3JlYXRlIGEgbmV3IHBzZXVkb3RpbWUgdmVjdG9yIG9mIDMwMCBwb2ludHMKblBvaW50cyA8LSAzMDAKCm5ld19kYXRhID0gbGlzdCgpCmZvciAoR2Vub3R5cGUgaW4gdW5pcXVlKENSLnBEYXRhJEdlbm90eXBlKSl7CiAgbmV3X2RhdGFbW2xlbmd0aChuZXdfZGF0YSkgKyAxXV0gPSBkYXRhLmZyYW1lKFBzZXVkb3RpbWUgPSBzZXEobWluKENSLnBEYXRhJFBzZXVkb3RpbWUpLCBtYXgoQ1IucERhdGEkUHNldWRvdGltZSksIGxlbmd0aC5vdXQgPSBuUG9pbnRzKSwgR2Vub3R5cGU9IEdlbm90eXBlKQp9CgpuZXdfZGF0YSA9IGRvLmNhbGwocmJpbmQsIG5ld19kYXRhKQoKIyBTbW9vdGggZ2VuZSBleHByZXNzaW9uCkRpZmYuY3VydmVfbWF0cml4IDwtIGdlblNtb290aEN1cnZlcyhnYm1fY2RzW1dULkNSLmdlbmVzJEdlbmUsIENSLnBEYXRhJEJhcmNvZGVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyZW5kX2Zvcm11bGEgPSAifnNtLm5zKFBzZXVkb3RpbWUsIGRmID0gMykqR2Vub3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aXZlX2V4cHIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19kYXRhID0gbmV3X2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yZXM9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpIC0gMikKYGBgCgpgYGB7cn0KUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyA8LSBjbHVzdGVyOjpwYW0oYXMuZGlzdCgoMSAtIGNvcihNYXRyaXg6OnQoRGlmZi5jdXJ2ZV9tYXRyaXhbLDMwMTo2MDBdKSxtZXRob2QgPSAicGVhcnNvbiIpKSksIGs9IDUpCgpDUi5HZW5lLmR5bmFtaXF1ZSA8LSBkYXRhLmZyYW1lKEdlbmU9IG5hbWVzKFBzZXVkb3RpbWUuZ2VuZXMuY2x1c3RlcnMkY2x1c3RlcmluZyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2F2ZXM9IFBzZXVkb3RpbWUuZ2VuZXMuY2x1c3RlcnMkY2x1c3RlcmluZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW5lLkNsdXN0ZXJzID0gUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdULkNSX2FuZF9LTy5DUiA9IGlmZWxzZShsaXN0SW5wdXQkV1QuQ1IgJWluJSBsaXN0SW5wdXQkS08uQ1IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTaGFyZWQiLCAiTm90LlNoYXJlZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdULkNSX2FuZF9XVC5DUHhfb25seSA9IGlmZWxzZShsaXN0SW5wdXQkV1QuQ1IgJWluJSBsaXN0SW5wdXQkV1QuQ1B4ICYgIWxpc3RJbnB1dCRXVC5DUiAlaW4lIGxpc3RJbnB1dCRLTy5DUiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNoYXJlZCIsICJOb3QuU2hhcmVkIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSBhcnJhbmdlKEdlbmUuQ2x1c3RlcnMpCgpyb3cubmFtZXMoQ1IuR2VuZS5keW5hbWlxdWUpIDwtIENSLkdlbmUuZHluYW1pcXVlJEdlbmUKQ1IuR2VuZS5keW5hbWlxdWUkR2VuZS5DbHVzdGVycyA8LSBwYXN0ZTAoIkNsdXN0LiIsIENSLkdlbmUuZHluYW1pcXVlJEdlbmUuQ2x1c3RlcnMpCmBgYAoKYGBge3IgfQojIE9yZGVyIHRoZSByb3dzIHVzaW5nIHNlcmlhdGlvbgpkc3QgPC0gYXMuZGlzdCgoMS1jb3Ioc2NhbGUodChEaWZmLmN1cnZlX21hdHJpeFssMzAxOjYwMF0pKSwgbWV0aG9kID0gInBlYXJzb24iKSkpCnJvdy5zZXIgPC0gc2VyaWF0aW9uOjpzZXJpYXRlKGRzdCwgbWV0aG9kID0iUjJFIikgIyJSMkUiICNUU1AgIyJHVyIgIkdXX3dhcmQiCmdlbmUub3JkZXIgPC0gcm93bmFtZXMoRGlmZi5jdXJ2ZV9tYXRyaXhbLDMwMTo2MDBdW2dldF9vcmRlcihyb3cuc2VyKSxdKQoKIyBTZXQgYW5ub3RhdGlvbiBjb2xvcnMKcGFsIDwtIHdlc19wYWxldHRlKCJEYXJqZWVsaW5nMSIpCmFubm8uY29sb3JzIDwtIGxpc3QobGluZWFnZSA9IGMoR21uY19LTz0iIzAyNmM5YSIsIEdtbmNfV1Q9IiNjYzM5MWIiKSwKICAgICAgICAgICAgICAgICAgICBHZW5lLkNsdXN0ZXJzID0gYyhDbHVzdC4xID1wYWxbMV0gLCBDbHVzdC4yPXBhbFsyXSwgQ2x1c3QuMz1wYWxbM10sIENsdXN0LjQ9cGFsWzRdLCBDbHVzdC41PXBhbFs1XSksCiAgICAgICAgICAgICAgICAgICAgV1QuQ1JfYW5kX1dULkNQeCA9IGMoU2hhcmVkID0gcGFsWzJdLCBOb3QuU2hhcmVkPXBhbFszXSksCiAgICAgICAgICAgICAgICAgICAgV1QuQ1JfYW5kX1dULkNQeF9vbmx5PSBjKFNoYXJlZCA9IHBhbFsyXSwgTm90LlNoYXJlZD1wYWxbM10pKQoKZ2VuZS5vcmRlciA8LSBnZW5lLm9yZGVyW2MoMzgyOjYyMiwxOjM4MSldCmBgYAoKCmBgYHtyIH0KaGVhdG1hcC5nZW5lIDwtIHBoZWF0bWFwOjpwaGVhdG1hcChEaWZmLmN1cnZlX21hdHJpeFtnZW5lLm9yZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKDMwMDoxLCNQYWwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMzAxOjYwMCldLCAjQ1IKICAgICAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRiwKICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IENSLkdlbmUuZHluYW1pcXVlICU+JSBkcGx5cjo6c2VsZWN0KEdlbmUuQ2x1c3RlcnMsIFdULkNSX2FuZF9LTy5DUiwgV1QuQ1JfYW5kX1dULkNQeF9vbmx5KSwKICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGRhdGEuZnJhbWUobGluZWFnZSA9IHJlcChjKCJHbW5jX0tPIiwiR21uY19XVCIpLCBlYWNoPTMwMCkpLAogICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5uby5jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgZ2Fwc19jb2wgPSBjKDIwMCw0MDApLAogICAgICAgICAgICAgICAgICAgICAgIGdhcHNfcm93ID0gYygyMzMpLAogICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBULAogICAgICAgICAgICAgICAgICAgICAgIGZvbnRzaXplX3JvdyA9IDgsCiAgICAgICAgICAgICAgICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAgdmlyaWRpczo6dmlyaWRpcyg5KSwKICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTIuNSwyLjUsIGxlbmd0aC5vdXQgPSA5KSwKICAgICAgICAgICAgICAgICAgICAgICBtYWluID0gIldUIENSIGVucmljaGVkIGdlbmVzIGFsb25nIEdtbmNLTyBhbmQgR21uY1dUIENSIHRyYWplY3RvcmllcyIpCgpwZGYoIn4vQnVyZWF1L0ZpZzRfTU0vaGVhdG1hcF9GaWc0R2VuZXNfV1QtS08ucGRmIiwgd2lkdGg9MTAsIGhlaWdodD01KQpncmlkOjpncmlkLm5ld3BhZ2UoKQpncmlkOjpncmlkLmRyYXcoaGVhdG1hcC5nZW5lJGd0YWJsZSkKZGV2Lm9mZigpCmBgYAoKYGBge3J9CmFubm8uY29sb3JzIDwtIGxpc3QoQ2VsbC5zdGF0ZSA9IGMoQ3ljbGluZ19SRz0iIzA0NmM5YSIsIERpZmZlcmVudGlhdGluZ19jZWxscz0iI2ViY2IyZSIpLAogICAgICAgICAgICAgICAgICAgIEdlbmUuQ2x1c3RlcnMgPSBjKENsdXN0LjEgPXBhbFsxXSAsIENsdXN0LjI9cGFsWzJdLCBDbHVzdC4zPXBhbFszXSwgQ2x1c3QuND1wYWxbNF0sIENsdXN0LjU9cGFsWzVdKSkKCmNvbC5hbm5vIDwtIGRhdGEuZnJhbWUoQ2VsbC5zdGF0ZSA9IHJlcChjKCJDeWNsaW5nX1JHIiwiRGlmZmVyZW50aWF0aW5nX2NlbGxzIiksIGMoMTAwLDIwMCkpKQpyb3duYW1lcyhjb2wuYW5ubykgPC0gMzAxOjYwMAoKcGhlYXRtYXA6OnBoZWF0bWFwKERpZmYuY3VydmVfbWF0cml4W2dlbmUub3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygzMDE6NjAwKV0sICNDUgogICAgICAgICAgICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZHBseXI6OnNlbGVjdChHZW5lLkNsdXN0ZXJzKSwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gY29sLmFubm8sCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm8uY29sb3JzLAogICAgICAgICAgICAgICAgICAgZ2Fwc19jb2wgPSAxMDAsCiAgICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgZm9udHNpemVfcm93ID0gOCwKICAgICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAgdmlyaWRpczo6dmlyaWRpcyg5KSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgtMi41LDIuNSwgbGVuZ3RoLm91dCA9IDkpLAogICAgICAgICAgICAgICAgICAgbWFpbiA9ICJXVCBDUiBlbnJpY2hlZCBnZW5lcyBhbG9uZyBHbW5jS08gYW5kIEdtbmNXVCBDUiB0cmFqZWN0b3JpZXMiKQpgYGAKCmBgYHtyfQphbm5vLmNvbG9ycyA8LSBsaXN0KENlbGwuc3RhdGUgPSBjKEN5Y2xpbmdfUkc9IiMwNDZjOWEiLCBEaWZmZXJlbnRpYXRpbmdfY2VsbHM9IiNlYmNiMmUiKSwKICAgICAgICAgICAgICAgICAgICBHZW5lLkNsdXN0ZXJzID0gYyhDbHVzdC4xID1wYWxbMV0gLCBDbHVzdC4yPXBhbFsyXSwgQ2x1c3QuMz1wYWxbM10sIENsdXN0LjQ9cGFsWzRdLCBDbHVzdC41PXBhbFs1XSkpCgpwaGVhdG1hcDo6cGhlYXRtYXAoRGlmZi5jdXJ2ZV9tYXRyaXhbZ2VuZS5vcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKDE6MzAwKV0sICNDUgogICAgICAgICAgICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZHBseXI6OnNlbGVjdChHZW5lLkNsdXN0ZXJzKSwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gZGF0YS5mcmFtZShDZWxsLnN0YXRlID0gcmVwKGMoIkN5Y2xpbmdfUkciLCJEaWZmZXJlbnRpYXRpbmdfY2VsbHMiKSwgYygxMDAsMjAwKSkpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vLmNvbG9ycywKICAgICAgICAgICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBnYXBzX2NvbCA9IDEwMCwKICAgICAgICAgICAgICAgICAgIGZvbnRzaXplX3JvdyA9IDgsCiAgICAgICAgICAgICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIHZpcmlkaXM6OnZpcmlkaXMoOSksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTIuNSwyLjUsIGxlbmd0aC5vdXQgPSA5KSwKICAgICAgICAgICAgICAgICAgIG1haW4gPSAiV1QgQ1IgZW5yaWNoZWQgZ2VuZXMgYWxvbmcgR21uY0tPIENSIHRyYWplY3RvcmllcyIpCmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KQ1IgPC0gV1QuS08uaW50ZWdyYXRlZEBtZXRhLmRhdGEgJT4lIGZpbHRlcihMaW5lYWdlID09ICJDYWphbC1SZXR6aXVzX25ldXJvbnMiKSAlPiUgc2VsZWN0KEJhcmNvZGVzLFBzZXVkb3RpbWUsb3JpZy5pZGVudCkKCkNSLmdlbmVzIDwtIGFzLmRhdGEuZnJhbWUodChXVC5LTy5pbnRlZ3JhdGVkQGFzc2F5cyRSTkFAZGF0YVtjKCJHbW5jIiwiVHJwNzMiLCAiTGh4MSIsICJCYXJobDIiKSxDUiRCYXJjb2Rlc10pKQpDUi5nZW5lcyRQc2V1ZG90aW1lIDwtIENSJFBzZXVkb3RpbWUKQ1IuZ2VuZXMkR2Vub3R5cGUgPC0gZmFjdG9yKENSJG9yaWcuaWRlbnQsIGxldmVscyA9IGMoIkhlbTEiLCAiR21uY19LTyIpICkgCgpDUi5nZW5lcyAgPC0gcmVzaGFwZTI6Om1lbHQoQ1IuZ2VuZXMsIGlkID0gYygiUHNldWRvdGltZSIsICJHZW5vdHlwZSIpKQoKCmdncGxvdChDUi5nZW5lcywgYWVzKHg9IFBzZXVkb3RpbWUsIHk9IHZhbHVlKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgYWVzKGNvbG9yPSB2YXJpYWJsZSwgbGluZXR5cGU9IEdlbm90eXBlKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMod2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVsxOjNdLHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbNV0pKSArCiAgeWxpbSgwLE5BKQoKZ2dzYXZlKCJ+L0J1cmVhdS9GaWc0X01NL01lZGlhbC1DUl9HZW5lcy10cmVuZF9XVC1LTy5wZGYiLAogICAgICAgd2lkdGggPSA1LCBoZWlnaHQgPSAyLAogICAgICAgZGV2aWNlID0gInBkZiIsCiAgICAgICBkcGkgPSAicmV0aW5hIikKYGBgCgojIyBURiBvbmx5CgpgYGB7cn0KIyBTZXQgYW5ub3RhdGlvbiBjb2xvcnMKcGFsIDwtIHdlc19wYWxldHRlKCJEYXJqZWVsaW5nMSIpCmFubm8uY29sb3JzIDwtIGxpc3QobGluZWFnZSA9IGMoR21uY19LTz0iIzAyNmM5YSIsIEdtbmNfV1Q9IiNjYzM5MWIiKSwKICAgICAgICAgICAgICAgICAgICBHZW5lLkNsdXN0ZXJzID0gYyhDbHVzdC4xID1wYWxbMV0gLCBDbHVzdC4yPXBhbFsyXSwgQ2x1c3QuMz1wYWxbM10sIENsdXN0LjQ9cGFsWzRdLCBDbHVzdC41PXBhbFs1XSkpCgpURnMgPC0gcmVhZC50YWJsZSgiVEYuY3N2Iiwgc2VwID0gIjsiKVssMV0KCmdlbmUub3JkZXIgPC0gZ2VuZS5vcmRlcltnZW5lLm9yZGVyICVpbiUgVEZzXQoKaGVhdG1hcC5nZW5lIDwtIHBoZWF0bWFwOjpwaGVhdG1hcChEaWZmLmN1cnZlX21hdHJpeFtnZW5lLm9yZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoMzAwOjEsI0tPIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMzAxOjYwMCldLCAjV1QKICAgICAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRiwKICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IENSLkdlbmUuZHluYW1pcXVlICU+JSBkcGx5cjo6c2VsZWN0KEdlbmUuQ2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gZGF0YS5mcmFtZShsaW5lYWdlID0gcmVwKGMoIkdtbmNfS08iLCJHbW5jX1dUIiksIGVhY2g9MzAwKSksCiAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vLmNvbG9ycywKICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd25hbWVzID0gVCwKICAgICAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3cgPSA4LAogICAgICAgICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLAogICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIHZpcmlkaXM6OnZpcmlkaXMoOSksCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKC0yLjUsMi41LCBsZW5ndGgub3V0ID0gOSksCiAgICAgICAgICAgICAgICAgICAgICAgbWFpbiA9ICJXVCBDUiBkeW5hbWljIFRGcyBhbG9uZyBHbW5jV1QgYW5kIEtPIHRyYWplY3RvcmllcyIpCmBgYAojIFdoaWNoIFdUIENSIGVucmljaGVkIGdlbmVzIGFyZSBzdGlsbCBleHByZXNzZWQgaW4gdGhlIEtPIENSCgojIyBGaW5kIERFRyBpbiB0aGUgS08KCmBgYHtyIGNhY2hlPVRSVUV9CktPLnBEYXRhIDwtIHBEYXRhKGdibV9jZHMpICU+JSBzdWJzZXQoR2Vub3R5cGUgPT0gIkdtbmNfS08iICYgUHNldWRvdGltZSA+IDAuNSkKCldULkNSLnBvc3RHbW5jIDwtIHJlYWQudGFibGUoIi4uL0NhamFsUmV0eml1c190cmFqZWN0b3J5L0NSX2R5bmFtaWNfZ2VuZXMuY3N2Iiwgc2VwID0gIjsiLCBoZWFkZXIgPSBUKSAKV1QuQ1IucG9zdEdtbmMgPC0gV1QuQ1IucG9zdEdtbmMgJT4lIGZpbHRlcihXYXZlcyAlaW4lIGMoMiwzLDQsNSkpICU+JSBwdWxsKEdlbmUpCgpwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmIDwtIGRpZmZlcmVudGlhbEdlbmVUZXN0KGdibV9jZHNbV1QuQ1IucG9zdEdtbmMsIEtPLnBEYXRhJEJhcmNvZGVdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGxNb2RlbEZvcm11bGFTdHIgPSAifnNtLm5zKFBzZXVkb3RpbWUsIGRmID0gMykqTGluZWFnZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWNlZE1vZGVsRm9ybXVsYVN0ciA9ICJ+c20ubnMoUHNldWRvdGltZSwgZGYgPSAzKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSAtIDIpCgojIEZpbHRlciBnZW5lcyBiYXNlZCBvbiBGRFIKcHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZCA8LSBwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmICU+JSBmaWx0ZXIocXZhbCA8IDFlLTExKQpgYGAKCmBgYHtyIGNhY2hlPVRSVUV9CiMgQ3JlYXRlIGEgbmV3IHBzZXVkby1EViB2ZWN0b3Igb2YgMzAwIHBvaW50cwpuUG9pbnRzIDwtIDEwMAoKbmV3X2RhdGEgPSBsaXN0KCkKZm9yIChMaW5lYWdlIGluIHVuaXF1ZShLTy5wRGF0YSRMaW5lYWdlKSl7CiAgbmV3X2RhdGFbW2xlbmd0aChuZXdfZGF0YSkgKyAxXV0gPSBkYXRhLmZyYW1lKFBzZXVkb3RpbWUgPSBzZXEobWluKEtPLnBEYXRhJFBzZXVkb3RpbWUpLCBtYXgoS08ucERhdGEkUHNldWRvdGltZSksIGxlbmd0aC5vdXQgPSBuUG9pbnRzKSwgTGluZWFnZT1MaW5lYWdlKQp9CgpuZXdfZGF0YSA9IGRvLmNhbGwocmJpbmQsIG5ld19kYXRhKQoKIyBTbW9vdGggZ2VuZSBleHByZXNzaW9uCkRpZmYuY3VydmVfbWF0cml4IDwtIGdlblNtb290aEN1cnZlcyhnYm1fY2RzW2FzLmNoYXJhY3Rlcihwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmLmZpbHRlcmVkJGdlbmVfc2hvcnRfbmFtZSksIEtPLnBEYXRhJEJhcmNvZGVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyZW5kX2Zvcm11bGEgPSAifnNtLm5zKFBzZXVkb3RpbWUsIGRmID0gMykqTGluZWFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpdmVfZXhwciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3X2RhdGEgPSBuZXdfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3Jlcz0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkgLSAyKQpgYGAKCiMjIENvbXB1dGUgdGhlIEFCQyBmb3IgZWFjaCBnZW5lCgpgYGB7cn0KIyBFeHRyYWN0IG1hdHJpeCBjb250YWluaW5nIHNtb290aGVkIGN1cnZlcyBmb3IgZWFjaCBsaW5lYWdlcwpQYWxfY3VydmVfbWF0cml4IDwtIERpZmYuY3VydmVfbWF0cml4WywgMTpuUG9pbnRzXSAjUGFsbGlhbCBwb2ludHMKQ1JfY3VydmVfbWF0cml4IDwtIERpZmYuY3VydmVfbWF0cml4WywgKG5Qb2ludHMgKyAxKTooMiAqIG5Qb2ludHMpXSAjQ1IgcG9pbnRzCgojIERpcmVjdGlvbiBvZiB0aGUgY29tcGFyaXNvbiA6IHBvc3RpdmUgQUJDcyA8PT4gVXByZWd1bGF0ZWQgaW4gQ1IgbGluZWFnZQpBQkNzX3JlcyA8LSBDUl9jdXJ2ZV9tYXRyaXggLSBQYWxfY3VydmVfbWF0cml4CgojIEF2ZXJhZ2UgbG9nRkMgYmV0d2VlbiB0aGUgMiBjdXJ2ZXMKSUxSX3JlcyA8LSBsb2cyKENSX2N1cnZlX21hdHJpeC8gKFBhbF9jdXJ2ZV9tYXRyaXggKyAwLjEpKSAKICAKQUJDc19yZXMgPC0gYXBwbHkoQUJDc19yZXMsIDEsIGZ1bmN0aW9uKHgsIG5Qb2ludHMpIHsKICAgICAgICAgICAgICAgICAgYXZnX2RlbHRhX3ggPC0gKHhbMTooblBvaW50cyAtIDEpXSArIHhbMjooblBvaW50cyldKS8yCiAgICAgICAgICAgICAgICAgIHN0ZXAgPC0gKDEwMC8oblBvaW50cyAtIDEpKQogICAgICAgICAgICAgICAgICByZXMgPC0gcm91bmQoc3VtKGF2Z19kZWx0YV94ICogc3RlcCksIDMpCiAgICAgICAgICAgICAgICAgIHJldHVybihyZXMpfSwKICAgICAgICAgICAgICAgICAgblBvaW50cyA9IG5Qb2ludHMpICMgQ29tcHV0ZSB0aGUgYXJlYSBiZWxvdyB0aGUgY3VydmUKICAKQUJDc19yZXMgPC0gY2JpbmQoQUJDc19yZXMsIElMUl9yZXNbLG5jb2woSUxSX3JlcyldKQpjb2xuYW1lcyhBQkNzX3Jlcyk8LSBjKCJBQkNzIiwgIkVuZHBvaW50X0lMUiIpCgojIEltcG9ydCBBQkMgdmFsdWVzIGludG8gdGhlIERFIHRlc3QgcmVzdWx0cyB0YWJsZQpwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmLmZpbHRlcmVkIDwtIGNiaW5kKHBzZXVkby5tYXR1cmF0aW9uLmRpZmYuZmlsdGVyZWRbLDE6NF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQUJDc19yZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZFssNTo2XSkKYGBgCgojIyBJbnRlcnNlY3Qgd2l0aCBXVCBDUiBhbmQgQ1B4CgpgYGB7cn0KV1QuQ1B4IDwtIHJlYWQudGFibGUoIi4uL0Nob3JvaWRQbGV4dXNfdHJhamVjdG9yeS9DaFAuR2VuZS5keW5hbWlxdWUuY3N2Iiwgc2VwID0gIjsiLCBoZWFkZXIgPSBUKQpXVC5DUHgucG9zdEdtbmMgPC0gV1QuQ1B4ICAlPiUgZmlsdGVyKFdhdmVzICVpbiUgYygxLDIpKSAlPiUgcHVsbChHZW5lKQoKS08uQ1IucG9zdEdtbmMgPC0gcHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZCAlPiUgZmlsdGVyKEFCQ3MgPjIwKSAlPiUgcHVsbChnZW5lX3Nob3J0X25hbWUpCgpnZW5lLmxpc3QgPC0gbGlzdChXVC5DUi5wb3N0R21uYyA9IFdULkNSLnBvc3RHbW5jLCBLTy5DUi5wb3N0R21uYyA9IEtPLkNSLnBvc3RHbW5jLCBXVC5DUHgucG9zdEdtbmMgPSBXVC5DUHgucG9zdEdtbmMpCmdndmVubjo6Z2d2ZW5uKGdlbmUubGlzdCkKYGBgCgojIFNlc3Npb24gSW5mbwoKYGBge3J9CiNkYXRlCmZvcm1hdChTeXMudGltZSgpLCAiJWQgJUIsICVZLCAlSCwlTSIpCgojUGFja2FnZXMgdXNlZApzZXNzaW9uSW5mbygpCmBgYA==