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)

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

Load the full dataset

WT <- readRDS("../QC.filtered.clustered.cells.RDS")
KO <- readRDS("./GmncKO.cells.RDS") %>% subset(idents = c(6:9), invert = T)
p1 <- DimPlot(object = WT,
              group.by = "Cell_ident",
              reduction = "spring",
              cols = c("#ebcb2e", #"CR"
                       "#e7823a", #"ChP"
                       "#4cabdc", # Chp_prog
                       "#68b041", #"Dorso-Medial_pallium" 
                       "#e46b6b", #"Hem" 
                       "#e3c148", #"Medial_pallium"
                       "#046c9a", # Pallial
                       "#4990c9"#"Thalamic_eminence"
              ),
              pt.size = 0.5)  & NoAxes()


p2 <- DimPlot(object = KO,
              group.by = "Cell.ident",
              reduction = "spring",
              cols = c( "#4cabdc", # Chp_prog
                        "#68b041", #"Dorso-Medial_pallium" 
                        "#e46b6b", #"Hem" 
                        "#e3c148", #"Medial_pallium"
                        "#a9961b",
                        "#ebcb2e",
                        "#046c9a", # Pallial
                        "#4990c9"#"Thalamic_eminence"
              ),
              pt.size = 0.5)  & NoAxes()

p1 + p2

Expression of MCC and stress response in WT and KO

MCC.genes <- list(c("Trp73", "Gmnc", "Foxj1", "Myb", "Ccno", "Ccdc67", "Deup1","Mcidas",
                    "E2f4", "E2f5", "Ahr", "Trrap", "Cdc20b", "Ccdc78", "Rfx2",
                    "Rfx3", "Foxn4", "Fank1", "Jazf1", "Ccna1", "Nek10", "Plk4",
                    "Cep63", "Cep152", "Sass6", "Pcnt", "Pcm1", "Cetn2", "Tfdp1"))

KO <- AddModuleScore(KO,
                     features = MCC.genes,
                     name = "MCC_score")

WT <- AddModuleScore(WT,
                     features = MCC.genes,
                     name = "MCC_score")
WT.CR.goterm <- read.table("../CajalRetzius_trajectory/CR_GO_res-by-waves.csv", sep = ";", header = T)

DNA_damage_GOterm <- WT.CR.goterm %>% filter(term_id %in% c("GO:0008630", "GO:0030330", "GO:0031571",
                                                            "GO:0006974", "GO:0006977","GO:0033554",
                                                            "GO:0044773", "GO:0042771", "GO:0042770",
                                                            "GO:2001021", "GO:1902229")
                                             )

DNA_damage_genes <- DNA_damage_GOterm %>%
                    filter(query %in% c("Clust.2", "Clust.3", "Clust.4")) %>%
                    filter(term_id == "GO:0033554") %>%
                    pull(intersection) %>% strsplit("\\,") %>% unlist() %>% unique()

KO <- AddModuleScore(KO,
                     features = list(DNA_damage_genes),
                     name = "cellular_response_to_stress_score")

WT <- AddModuleScore(WT,
                     features = list(DNA_damage_genes),
                     name = "cellular_response_to_stress_score")
gradient <- rev(brewer.pal(8,"RdYlBu"))
lim <-  c(-0.5,0.8)

p1 <- ggplot(KO@meta.data, aes(Spring_1, Spring_2)) +
  geom_point(aes(color=MCC_score1), size=1, shape=16) + 
  scale_color_gradientn(colours=gradient,
                        limits = lim,
                        name='Multiciliation score')


p2 <- ggplot(WT@meta.data, aes(Spring_1, Spring_2)) +
  geom_point(aes(color=MCC_score1), size=1, shape=16) + 
  scale_color_gradientn(colours=gradient,
                        limits = lim,
                        name='Multiciliation score')

p3 <- ggplot(KO@meta.data, aes(Spring_1, Spring_2)) +
  geom_point(aes(color=cellular_response_to_stress_score1), size=1, shape=16) + 
  scale_color_gradientn(colours=gradient,
                        limits = lim,
                        name='Stress response score')

p4 <- ggplot(WT@meta.data, aes(Spring_1, Spring_2)) +
  geom_point(aes(color=cellular_response_to_stress_score1), size=1, shape=16) + 
  scale_color_gradientn(colours= gradient,
                        limits = lim,
                        name='Stress response score')


MCC.scores.plot <- p2 + p1 
Stress.score.plot <- p4 + p3

for (i in 1:2){
  MCC.scores.plot[[i]]$data <- MCC.scores.plot[[i]]$data[order(MCC.scores.plot[[i]]$data$MCC_score1),]
}

for (i in 1:2){
  Stress.score.plot[[i]]$data <- Stress.score.plot[[i]]$data[order(Stress.score.plot[[i]]$data$cellular_response_to_stress_score1),]
}

MCC.scores.plot / Stress.score.plot

# Compute differentiation states scores

AP

APgenes <- c("Rgcc", "Sparc", "Hes5","Hes1", "Slc1a3",
             "Ddah1", "Ldha", "Hmga2","Sfrp1", "Id4",
             "Creb5", "Ptn", "Lpar1", "Rcn1","Zfp36l1",
             "Sox9", "Sox2", "Nr2e1", "Ttyh1", "Trip6")

KO <- AddModuleScore(KO,
                     features = list(APgenes),
                     name = "AP_signature")

BP

BPgenes <- c("Eomes", "Igsf8", "Insm1", "Elavl2", "Elavl4",
             "Hes6","Gadd45g", "Neurog2", "Btg2", "Neurog1")

KO <- AddModuleScore(KO,
                     features = list(BPgenes),
                     name = "BP_signature")

EN

ENgenes <- c("Mfap4", "Nhlh2", "Nhlh1", "Ppp1r14a", "Nav1",
             "Neurod1", "Sorl1", "Svip", "Cxcl12", "Tenm4",
             "Dll3", "Rgmb", "Cntn2", "Vat1")

KO <- AddModuleScore(KO,
                     features = list(ENgenes),
                     name = "EN_signature")

LN

LNgenes <- c("Snhg11", "Pcsk1n", "Mapt", "Ina", "Stmn4",
             "Gap43", "Tubb2a", "Ly6h","Ptprd", "Mef2c")

KO <- AddModuleScore(KO,
                     features = list(LNgenes),
                     name = "LN_signature")
FeaturePlot(object = KO,
            features = c("AP_signature1", "BP_signature1",
                              "EN_signature1", "LN_signature1"),
            pt.size = 0.75,
            cols = rev(brewer.pal(10,"Spectral")),
            reduction = "spring",
            order = T) & NoAxes() & NoLegend()

Fit pseudotime on CR and CP differentiating neurons

Group cells in Pallial or CR lineage

KO$Lineage <- sapply(KO$Cell.ident,
                              FUN = function(x) {
                                if (x %in% c("Neuron_prob.2", "Hem")) {
                                  x = "Cajal-Retzius_neurons"
                                } else if (x %in% c("Neuron_prob.3", "Medial_pallium")) {
                                  x = "Pallial_neurons"
                                } else {
                                  x = "other"
                                  }
                              })
DimPlot(KO,
        reduction = "spring",
        group.by = "Lineage",
        pt.size = 0.5,
        cols =  c("#cc391b","#969696","#026c9a")
        ) + NoAxes()

Fit principale curve on the two lineages

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

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

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$SCT@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')

Plot pan-neuronal genes along this axis

Neurons.data <- NormalizeData(Neurons.data, normalization.method = "LogNormalize", scale.factor = 10000, assay = "RNA")
Trajectories.neurons <- Neurons.data@meta.data %>% select(Barcodes, Spring_1, Spring_2,
                                                          AP_signature1, BP_signature1, EN_signature1, LN_signature1,
                                                          Lineage, PseudotimeScore)

# Neurog2
p1 <- FeaturePlot(object = Neurons.data,
            features = c("Neurog2"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()

Trajectories.neurons$Neurog2 <- Neurons.data@assays$RNA@data["Neurog2", Trajectories.neurons$Barcodes]

p2 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore, y= Neurog2)) +
        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)

# Tbr1 
p3 <- FeaturePlot(object = Neurons.data ,
            features = c("Tbr1"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()
Trajectories.neurons$Tbr1 <- Neurons.data@assays$RNA@data["Tbr1", Trajectories.neurons$Barcodes]

p4 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore, y= Tbr1)) +
        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)

# Mapt 
p5 <- FeaturePlot(object = Neurons.data ,
            features = c("Mapt"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()

Trajectories.neurons$Mapt <- Neurons.data@assays$RNA@data["Mapt", Trajectories.neurons$Barcodes]

p6 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore, y= Mapt)) +
        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)

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

## Shift pseudotime score

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

Trajectories.neurons$PseudotimeScore.shifted <- (score - min(score)) / (max(score) - min(score))
# Neurog2
p1 <- FeaturePlot(object = Neurons.data ,
            features = c("Neurog2"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()

p2 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore.shifted, y= Neurog2)) +
        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)

# Tbr1 
p3 <- FeaturePlot(object = Neurons.data ,
            features = c("Tbr1"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()

p4 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore.shifted, y= Tbr1)) +
        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)

# Mapt 
p5 <- FeaturePlot(object = Neurons.data ,
            features = c("Mapt"),
            pt.size = 0.5,
            cols = c("grey90", brewer.pal(9,"YlGnBu")),
            reduction = "spring",
            order = T) & NoAxes()

p6 <- ggplot(Trajectories.neurons, aes(x= PseudotimeScore.shifted, y= Mapt)) +
        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)

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

Trajectories.neurons$nUMI <- Neurons.data$nCount_RNA

ggplot(Trajectories.neurons, aes(x= PseudotimeScore.shifted, y= nUMI/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(KO,  subset = Lineage %in% c("Cajal-Retzius_neurons", "Pallial_neurons") & Cell.ident %in% c("Hem", "Medial_pallium"))

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

table(Progenitors.data$Cell.ident)
## 
##            Hem Medial_pallium 
##           1114           2983
rm(list = ls()[!ls() %in% c("Trajectories.neurons", "Progenitors.data")])
gc()
##             used  (Mb) gc trigger   (Mb)   max used   (Mb)
## Ncells   3468720 185.3    6151937  328.6    6151937  328.6
## Vcells 102142773 779.3  821756700 6269.6 1026242019 7829.7

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-05-09 16:04:23: reading data: 5.99secs
rm("rawCounts")
gc()
##             used   (Mb) gc trigger   (Mb)   max used   (Mb)
## Ncells   3474932  185.6    6151937  328.6    6151937  328.6
## Vcells 165303030 1261.2  657405360 5015.7 1026242019 7829.7

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-05-09 16:04:29: assigning cell cycle phases: 34.65secs
myData <- getPCAData(dataList = myData)
## 2022-05-09 16:05:04: calculating PCA: 27.97secs
myData <- getOptimalRotation(dataList = myData)
## 2022-05-09 16:05:32: calculating optimal rotation: 17.79secs

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)
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 = "spring",
                  order = T) & NoAxes()

p2 <- DimPlot(object = Progenitors.data,
              group.by = "Revelio.phase",
              pt.size = 1,
              reduction = "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, Spring_1, Spring_2,
                                                     AP_signature1, BP_signature1, EN_signature1, LN_signature1,
                                                     Lineage)

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

Combine Progenitors and differentiating neurons data

# Start with neurons data
Trajectories.all <- Trajectories.neurons %>% select(Barcodes, nUMI, Spring_1, Spring_2, AP_signature1, BP_signature1, EN_signature1, LN_signature1, Lineage)

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

Trajectories.all$Phase <- factor(Trajectories.all$Phase, levels = c("G1.S", "S", "G2", "G2.M", "M.G1"))
p1 <- ggplot(Trajectories.all, 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.all, 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.all, aes(x= Pseudotime, y= nUMI/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.all, aes(x= Pseudotime, y= nUMI/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

p1 <- ggplot(Trajectories.all, aes(x= Pseudotime, y= AP_signature1)) +
  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")


p2 <- ggplot(Trajectories.all, aes(x= Pseudotime, y= BP_signature1)) +
  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")

p3 <- ggplot(Trajectories.all, aes(x= Pseudotime, y= EN_signature1)) +
  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")

p4 <- ggplot(Trajectories.all, aes(x= Pseudotime, y= LN_signature1)) +
  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")


p1 / p2 / p3 / p4

Subset the full Seurat object

KO <- readRDS("./GmncKO.cells.RDS") %>% subset(idents = c(6:9), invert = T)
Neuro.trajectories <- CreateSeuratObject(counts = KO@assays$RNA@data[, Trajectories.all$Barcodes],
                                         meta.data = Trajectories.all)

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

p2 <- DimPlot(object = Neuro.trajectories,
        group.by = "Lineage",
        pt.size = 0.5,
        reduction = "spring",
        cols =  c("#cc391b", "#026c9a")) & NoAxes()


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

p1 + p2 + p3

rm(list = ls()[!ls() %in% c("Neuro.trajectories")])
gc()
##            used  (Mb) gc trigger   (Mb)   max used   (Mb)
## Ncells  3517520 187.9    6151937  328.6    6151937  328.6
## Vcells 35253774 269.0  757471775 5779.1 1026242019 7829.7

Normalization

Neuro.trajectories<- NormalizeData(Neuro.trajectories, normalization.method = "LogNormalize", scale.factor = 10000, assay = "RNA")
Neuro.trajectories <- FindVariableFeatures(Neuro.trajectories, selection.method = "disp", nfeatures = 3000, assay = "RNA")

Plot some genes along pseudotime

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

Plot.Genes.trend(Seurat.data= Neuro.trajectories,
                 group.by = "Lineage",
                 genes= c("Gas1","Sox2",
                          "Neurog2", "Btg2",
                          "Tbr1", "Mapt",
                          "Trp73", "Foxg1"))

Plot.Genes.trend(Seurat.data= Neuro.trajectories,
                 group.by = "Lineage",
                 genes= c("Gmnc", "Mcidas",
                          "Foxj1", "Trp73",
                          "Lhx1", "Cdkn1a"))

Plot.Genes.trend(Seurat.data= Neuro.trajectories,
                 group.by = "Lineage",
                 genes= c("Mki67", "Top2a",
                          "H2afx", "Cdkn1c"))

Use monocle2 to model gene expression along cycling axis

Initialize a monocle object

# Transfer metadata
meta.data <- data.frame(Barcode= Neuro.trajectories$Barcodes,
                        Lineage= Neuro.trajectories$Lineage,
                        Pseudotime= Neuro.trajectories$Pseudotime,
                        Phase= Neuro.trajectories$Phase)

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

# Transfer counts data
var.genes <- Neuro.trajectories[["RNA"]]@var.features
count.data = data.frame(gene_short_name = rownames(Neuro.trajectories[["RNA"]]@data[var.genes,]),
                        row.names = rownames(Neuro.trajectories[["RNA"]]@data[var.genes,]))

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

# Create the CellDataSet object including variable genes only
gbm_cds <- newCellDataSet(Neuro.trajectories[["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("Neuro.trajectories", "gbm_cds", "Gene.Trend", "Plot.Genes.trend")])
gc()
##            used  (Mb) gc trigger   (Mb)   max used   (Mb)
## Ncells  3587157 191.6    6151937  328.6    6151937  328.6
## Vcells 71157383 542.9  605977420 4623.3 1026242019 7829.7

Find Pan-neuronal genes

# Split pallial and subpallial cells for gene expression fitting
#Pallial cells
Pallialcells <- Neuro.trajectories@meta.data %>%
                filter(Lineage == "Pallial_neurons") %>%
                pull(Barcodes)

# Cajal-Retzius cells
CRcells <- Neuro.trajectories@meta.data %>%
                   filter(Lineage == "Cajal-Retzius_neurons") %>%
                   pull(Barcodes)
# We filter-out genes detected in less than 200 or 200 cells along Pallial or CR lineages
num.cells <- Matrix::rowSums(Neuro.trajectories@assays$RNA@counts[,Pallialcells] > 0)
Pallial.expressed <- names(x = num.cells[which(x = num.cells >= 200)])

num.cells <- Matrix::rowSums(Neuro.trajectories@assays$RNA@counts[,CRcells] > 0)
CR.expressed <- names(x = num.cells[which(x = num.cells >= 200)])

Input.genes <- rownames(gbm_cds)[rownames(gbm_cds) %in% intersect(Pallial.expressed, CR.expressed)]
Pallial.genes <- differentialGeneTest(gbm_cds[Input.genes, Pallialcells], 
                                                 fullModelFormulaStr = "~sm.ns(Pseudotime, df = 3)", 
                                                 reducedModelFormulaStr = "~1", 
                                                 cores = parallel::detectCores() - 2)

#Filter based on FDR
Pallial.genes.filtered <- Pallial.genes  %>% filter(qval < 1e-3)
CRcells.genes <- differentialGeneTest(gbm_cds[Input.genes, CRcells], 
                                                 fullModelFormulaStr = "~sm.ns(Pseudotime, df = 3)", 
                                                 reducedModelFormulaStr = "~1", 
                                                 cores = parallel::detectCores() - 2)

#Filter based on FDR
CRcells.genes.filtered <- CRcells.genes  %>% filter(qval < 1e-3)
Common.genes <- intersect(Pallial.genes.filtered$gene_short_name, CRcells.genes.filtered$gene_short_name)
# Smooth genes expression along the two trajectories
nPoints <- 300

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

new_data = do.call(rbind, new_data)

# Smooth gene expression
curve_matrix <- genSmoothCurves(gbm_cds[as.character(Common.genes),],
                                trend_formula = "~sm.ns(Pseudotime, df = 3)*Lineage",
                                relative_expr = TRUE,
                                new_data = new_data,
                                cores= parallel::detectCores() - 2)
# Extract genes with person's cor > 0.6 between the 2 trajectories

Pallial.smoothed <- scale(t(curve_matrix[,c(1:300)]))  #Pallial points
CR.smoothed <- scale(t(curve_matrix[,c(301:600)])) #CR points

mat <- cor(Pallial.smoothed, CR.smoothed, method = "pearson")

Gene.Cor <- diag(mat)
hist(Gene.Cor, breaks = 100)
abline(v=0.8,col=c("blue"))

PanNeuro.genes <- names(Gene.Cor[Gene.Cor > 0.8])
# Order rows using seriation
dst <- as.dist((1-cor(scale(t(curve_matrix[PanNeuro.genes,c(600:301)])), method = "pearson")))
row.ser <- seriate(dst, method ="MDS_angle") #MDS_angle
gene.order <- PanNeuro.genes[get_order(row.ser)]

anno.colors <- list(lineage = c(Pallial="#026c9a",CR="#cc391b"))


pheatmap::pheatmap(curve_matrix[rev(gene.order),
                                c(1:300, 301:600)], #CR
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_col = data.frame(lineage = rep(c("Pallial","CR"), each=300)),
                   annotation_colors = anno.colors,
                   show_colnames = F,
                   show_rownames = T,
                   fontsize_row = 2,
                   color =  viridis::viridis(10),
                   breaks = seq(-2.5,2.5, length.out = 10),
                   main = "")

rm(list = ls()[!ls() %in% c("Neuro.trajectories", "gbm_cds", "Gene.Trend", "Plot.Genes.trend")])
gc()
##            used  (Mb) gc trigger   (Mb)   max used   (Mb)
## Ncells  3621897 193.5    6151937  328.6    6151937  328.6
## Vcells 71246487 543.6  484781936 3698.6 1026242019 7829.7

Test each gene trend over pseudotime score

Find genes DE along pseudomaturation axis

pseudo.maturation.diff <- differentialGeneTest(gbm_cds[fData(gbm_cds)$num_cells_expressed > 80,], 
                                                 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)

Direction of the DEG by calculating the area between curves (ABC)

Smooth commun genes along the two trajectories

# Create a new pseudo-DV vector of 300 points
nPoints <- 300

new_data = list()
for (Lineage in unique(pData(gbm_cds)$Lineage)){
  new_data[[length(new_data) + 1]] = data.frame(Pseudotime = seq(min(pData(gbm_cds)$Pseudotime), max(pData(gbm_cds)$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),],
                                      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])

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, ]

Gene expression profiles along the trajectory

## Cluster gene by expression profiles
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)
# 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(Pallial_neurons="#026c9a", 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(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("Pallial_neurons","Cajal_Retzius"), each=300)),
                   annotation_colors = anno.colors,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

We manually correct the reordering so genes are aligned from top left to bottom rigth

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),
                   annotation_col = data.frame(lineage = rep(c("Pallial_neurons","Cajal_Retzius"), each=300)),
                   annotation_colors = anno.colors,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

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(CR_curve_matrix[gene.order,],
                   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,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

diff.state <- Neuro.trajectories@meta.data %>%
              filter(Lineage ==  "Cajal-Retzius_neurons") %>%
              select("AP_signature1", "BP_signature1", "EN_signature1", "LN_signature1", "Pseudotime")

AP.loess <- loess(AP_signature1 ~ Pseudotime, diff.state)
AP.smooth <- predict(AP.loess,
                     seq(0.01,1.5, length.out= 300))

BP.loess <- loess(BP_signature1 ~ Pseudotime, diff.state)
BP.smooth <- predict(BP.loess,
                     seq(0.01,1.5, length.out= 300))

EN.loess <- loess(EN_signature1 ~ Pseudotime, diff.state)
EN.smooth <- predict(EN.loess,
                     seq(0.01,1.5, length.out= 300))

LN.loess <- loess(LN_signature1 ~ Pseudotime, diff.state)
LN.smooth <- predict(LN.loess,
                     seq(0.01,1.5, length.out= 300))

Smoothed.states <- cbind(AP.smooth, BP.smooth, EN.smooth, LN.smooth)
heatmap.states <- pheatmap::pheatmap(as.data.frame(t(Smoothed.states)),
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   gaps_col = 100,
                   gaps_row = c(1,2,3),
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  rev(colorRampPalette(brewer.pal(n= 8, name = "RdBu"))(100)),
                   breaks = seq(-1,1, length.out = 100),
                   main = "")
heatmap.gene <- pheatmap::pheatmap(CR_curve_matrix[gene.order,],
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   gaps_col = 100,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")
cowplot::plot_grid(heatmap.states$gtable, heatmap.gene$gtable,
                   ncol = 1,
                   align = "v",
                   rel_heights = c(1,3),
                   greedy = T)

Gene cluster trend

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

Plot.clust.trends(Neuro.trajectories,
                   Lineage = "Cajal-Retzius_neurons",
                   Which.cluster = 1:5,
                   clust.list = Pseudotime.genes.clusters$clustering,
                   Smooth.method = "gam")

GO term enrichment in gene clusters using gprofiler2

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

write.table(apply(CR.gostres$result,2,as.character),
            "KO_CR_GO_res-by-waves.csv", sep = ";", quote = F, row.names = F)
DNA_damage_GOterm <- CR.gostres$result[CR.gostres$result$term_id %in% c("GO:0008630", "GO:0030330", "GO:0031571", "GO:0006974", "GO:0006977","GO:0033554",
                                                                                 "GO:0044773", "GO:0042771", "GO:0042770", "GO:2001021", "GO:1902229"),]

DNA_damage_GOterm[,c(9,1,2,3,5,6,7,11)]
## # A tibble: 0 × 8
## # … with 8 variables: term_id <chr>, query <chr>, significant <lgl>,
## #   p_value <dbl>, query_size <int>, intersection_size <int>, precision <dbl>,
## #   term_name <chr>

Go term on all CR genes

CR.gostres <- gost(query = as.character(CR.Gene.dynamique$Gene),
                organism = "mmusculus", ordered_query = F, 
                multi_query = F, significant = T, exclude_iea = T, 
                measure_underrepresentation = F, evcodes = T, 
                user_threshold = 0.05, correction_method = "fdr", 
                domain_scope = "annotated", custom_bg = NULL, 
                numeric_ns = "", sources = c("GO:MF", "GO:BP"), as_short_link = F)

write.table(apply(CR.gostres$result,2,as.character),
            "KOCR_GO_res.csv", sep = ";", quote = F, row.names = F)
DNA_damage_GOterm <- CR.gostres$result[CR.gostres$result$term_id %in% c("GO:0008630", "GO:0030330", "GO:0031571", "GO:0006974", "GO:0006977",
                                                                                 "GO:0044773", "GO:0042771", "GO:0042770", "GO:2001021", "GO:1902229"),]

DNA_damage_GOterm[,c(1,2,3,5,6,7,11)]
## # A tibble: 0 × 7
## # … with 7 variables: query <chr>, significant <lgl>, p_value <dbl>,
## #   query_size <int>, intersection_size <int>, precision <dbl>, term_name <chr>

Intersection with ChP dynamicaly expressed genes

ChP_dynamic_genes <- read.table("../ChoroidPlexus_trajectory/ChP.Gene.dynamique.csv", sep = ";", header = T, row.names = 1)
CR_ChP_common_genes <- CR.Gene.dynamique %>% filter(Gene %in% ChP_dynamic_genes$Gene)
gene.order2 <- gene.order[gene.order %in% CR_ChP_common_genes$Gene]

pheatmap::pheatmap(CR_curve_matrix[gene.order2,],
                   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,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

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

write.table(apply(CR_ChP_common.gostres$result,2,as.character),
            "KO_CR_ChP_common_GO_res-by-waves.csv", sep = ";", quote = F, row.names = F)
DNA_damage_GOterm <- CR_ChP_common.gostres$result[CR_ChP_common.gostres$result$term_id %in% c("GO:0008630", "GO:0030330", "GO:0031571", "GO:0006974", "GO:0006977",
                                                                                       "GO:0044773", "GO:0042771", "GO:0042770", "GO:2001021", "GO:1902229"),]

DNA_damage_GOterm[,c(1,2,3,5,6,7,11)]
## # A tibble: 0 × 7
## # … with 7 variables: query <chr>, significant <lgl>, p_value <dbl>,
## #   query_size <int>, intersection_size <int>, precision <dbl>, term_name <chr>
CR_ChP_common.gostres <- gost(query = as.character(CR_ChP_common_genes$Gene),
                organism = "mmusculus", ordered_query = F, 
                multi_query = F, significant = T, exclude_iea = T, 
                measure_underrepresentation = F, evcodes = T, 
                user_threshold = 0.05, correction_method = "fdr", 
                domain_scope = "annotated", custom_bg = NULL, 
                numeric_ns = "", sources = c("GO:MF", "GO:BP"), as_short_link = F)

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

Pallial neurons trajectory analysis

# Extract Pallial neurons trajectory genes
Pal.res <- as.data.frame(pseudo.maturation.diff.filtered[pseudo.maturation.diff.filtered$ABCs < 0,])
Pal.genes <- row.names(Pal.res)

Pal_curve_matrix <- Pal_curve_matrix[Pal.genes, ]

Gene expression profiles along the trajectory

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

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

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

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

We manually correct the reordering so genes are aligned from top right to bottom left

pheatmap::pheatmap(Diff.curve_matrix[gene.order,
                                c(300:1,#Pal
                                  301:600)], #CR
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = Pal.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_col = data.frame(lineage = rep(c("Pallial_neurons","Cajal_Retzius"), each=300)),
                   annotation_colors = anno.colors,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

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("Differentiating_cells","Cycling_RG"), c(200,100)))
rownames(col.anno) <- 300:1

pheatmap::pheatmap(Pal_curve_matrix[gene.order,300:1],
                   scale = "row",
                   cluster_rows = F,
                   cluster_cols = F,
                   annotation_row = Pal.Gene.dynamique %>% dplyr::select(Gene.Clusters),
                   annotation_col = col.anno,
                   annotation_colors = anno.colors,
                   gaps_col = 200,
                   show_colnames = F,
                   show_rownames = F,
                   fontsize_row = 8,
                   color =  viridis::viridis(9),
                   breaks = seq(-2.5,2.5, length.out = 9),
                   main = "")

Gene cluster trend

Plot.clust.trends(Neuro.trajectories,
                   Lineage = "Pallial_neurons",
                   Which.cluster = 1:5,
                   clust.list = Pseudotime.genes.clusters$clustering,
                   Smooth.method = "gam")

Pal.gostres <- gost(query = as.character(Pal.Gene.dynamique$Gene),
                organism = "mmusculus", ordered_query = F, 
                multi_query = F, significant = T, exclude_iea = T, 
                measure_underrepresentation = F, evcodes = T, 
                user_threshold = 0.05, correction_method = "fdr", 
                domain_scope = "annotated", custom_bg = NULL, 
                numeric_ns = "", sources = c("GO:MF", "GO:BP"), as_short_link = F)

write.table(apply(Pal.gostres$result, 2, as.character),
            "KO_Pal.gostres.csv", sep = ";", quote = F, row.names = F)
DNA_damage_GOterm <- Pal.gostres$result[Pal.gostres$result$term_id %in% c("GO:0008630", "GO:0030330", "GO:0031571", "GO:0006974", "GO:0006977",
                                                                                 "GO:0044773", "GO:0042771", "GO:0042770", "GO:2001021", "GO:1902229"),]

DNA_damage_GOterm[,c(1,2,3,5,6,7,11)]
## # A tibble: 7 × 7
##   query   significant p_value query_size intersection_size precision term_name  
##   <chr>   <lgl>         <dbl>      <int>             <int>     <dbl> <chr>      
## 1 query_1 TRUE        0.00461        160                 3    0.0188 mitotic G1…
## 2 query_1 TRUE        0.00649        160                 4    0.025  DNA damage…
## 3 query_1 TRUE        0.0132         160                 3    0.0188 regulation…
## 4 query_1 TRUE        0.0207         160                 3    0.0188 intrinsic …
## 5 query_1 TRUE        0.0211         160                 2    0.0125 DNA damage…
## 6 query_1 TRUE        0.0287         160                 5    0.0312 signal tra…
## 7 query_1 TRUE        0.0358         160                 4    0.025  intrinsic …

Session Info

#date
format(Sys.time(), "%d %B, %Y, %H,%M")
## [1] "09 mai, 2022, 16,07"
#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] wesanderson_0.3.6   cowplot_1.1.1       ggExtra_0.9        
##  [4] RColorBrewer_1.1-2  dplyr_1.0.7         seriation_1.3.1    
##  [7] gprofiler2_0.2.1    monocle_2.22.0      DDRTree_0.1.5      
## [10] irlba_2.3.3         VGAM_1.1-5          ggplot2_3.3.5      
## [13] Biobase_2.54.0      BiocGenerics_0.40.0 Matrix_1.4-1       
## [16] Revelio_0.1.0       princurve_2.1.6     SeuratObject_4.0.4 
## [19] 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] RCurl_1.98-1.5        sparsesvd_0.2         crayon_1.4.2         
##  [28] jsonlite_1.7.2        spatstat.data_2.1-0   survival_3.2-13      
##  [31] zoo_1.8-9             iterators_1.0.13      glue_1.5.1           
##  [34] polyclip_1.10-0       registry_0.5-1        gtable_0.3.0         
##  [37] leiden_0.3.9          future.apply_1.8.1    abind_1.4-5          
##  [40] scales_1.1.1          pheatmap_1.0.12       DBI_1.1.1            
##  [43] miniUI_0.1.1.1        Rcpp_1.0.8            viridisLite_0.4.0    
##  [46] xtable_1.8-4          reticulate_1.22       spatstat.core_2.3-1  
##  [49] htmlwidgets_1.5.4     httr_1.4.2            FNN_1.1.3            
##  [52] ellipsis_0.3.2        ica_1.0-2             farver_2.1.0         
##  [55] pkgconfig_2.0.3       sass_0.4.0            uwot_0.1.10          
##  [58] deldir_1.0-6          utf8_1.2.2            labeling_0.4.2       
##  [61] tidyselect_1.1.1      rlang_0.4.12          reshape2_1.4.4       
##  [64] later_1.3.0           munsell_0.5.0         tools_4.2.0          
##  [67] cli_3.1.0             generics_0.1.1        ggridges_0.5.3       
##  [70] evaluate_0.14         stringr_1.4.0         fastmap_1.1.0        
##  [73] yaml_2.2.1            goftest_1.2-3         knitr_1.36           
##  [76] fitdistrplus_1.1-6    purrr_0.3.4           RANN_2.6.1           
##  [79] pbapply_1.5-0         future_1.23.0         nlme_3.1-153         
##  [82] mime_0.12             slam_0.1-49           rstudioapi_0.13      
##  [85] compiler_4.2.0        plotly_4.10.0         png_0.1-7            
##  [88] spatstat.utils_2.2-0  tibble_3.1.6          bslib_0.3.1          
##  [91] stringi_1.7.6         highr_0.9             lattice_0.20-45      
##  [94] HSMMSingleCell_1.14.0 vctrs_0.3.8           pillar_1.6.4         
##  [97] lifecycle_1.0.1       spatstat.geom_2.3-0   combinat_0.0-8       
## [100] lmtest_0.9-39         jquerylib_0.1.4       RcppAnnoy_0.0.19     
## [103] bitops_1.0-7          data.table_1.14.2     httpuv_1.6.3         
## [106] patchwork_1.1.1       R6_2.5.1              promises_1.2.0.1     
## [109] TSP_1.1-11            KernSmooth_2.23-20    gridExtra_2.3        
## [112] parallelly_1.29.0     codetools_0.2-18      MASS_7.3-56          
## [115] assertthat_0.2.1      withr_2.4.3           qlcMatrix_0.9.7      
## [118] sctransform_0.3.2     mgcv_1.8-40           parallel_4.2.0       
## [121] grid_4.2.0            rpart_4.1.16          tidyr_1.1.4          
## [124] rmarkdown_2.11        Rtsne_0.15            shiny_1.7.1

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

LS0tCnRpdGxlOiAiQ2FqYWwtUmV0eml1cyBjZWxscyBUcmFqZWN0b3J5IgphdXRob3I6CiAgIC0gTWF0dGhpZXUgTW9yZWF1XltJbnN0aXR1dGUgb2YgUHN5Y2hpYXRyeSBhbmQgTmV1cm9zY2llbmNlIG9mIFBhcmlzLCBJTlNFUk0gVTEyNjYsIDc1MDE0LCBQYXJpcywgRnJhbmNlLCBtYXR0aGlldS5tb3JlYXVAaW5zZXJtLmZyXSBbIVtdKGh0dHBzOi8vb3JjaWQub3JnL3NpdGVzL2RlZmF1bHQvZmlsZXMvaW1hZ2VzL29yY2lkXzE2eDE2LnBuZyldKGh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMi0yNTkyLTIzNzMpCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OiAKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgZGZfcHJpbnQ6IHRpYmJsZQogICAgaGlnaGxpZ2h0OiBoYWRkb2NrCiAgICB0aGVtZTogY29zbW8KICAgIGNzczogIi4uL3N0eWxlLmNzcyIKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBmaWcuYWxpZ24gPSAnY2VudGVyJywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGUubGF6eSA9IEZBTFNFKQoKIyBUbyB1c2UgYmlvbWFydCAKbmV3X2NvbmZpZyA8LSBodHRyOjpjb25maWcoc3NsX3ZlcmlmeXBlZXIgPSBGQUxTRSkKaHR0cjo6c2V0X2NvbmZpZyhuZXdfY29uZmlnLCBvdmVycmlkZSA9IEZBTFNFKQpgYGAKCiMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KHByaW5jdXJ2ZSkKbGlicmFyeShSZXZlbGlvKQpsaWJyYXJ5KG1vbm9jbGUpCmxpYnJhcnkoZ3Byb2ZpbGVyMikKbGlicmFyeShzZXJpYXRpb24pCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdnRXh0cmEpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeSh3ZXNhbmRlcnNvbikKCiNTZXQgZ2dwbG90IHRoZW1lIGFzIGNsYXNzaWMKdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkKYGBgCgojIExvYWQgdGhlIGZ1bGwgZGF0YXNldAoKYGBge3J9CldUIDwtIHJlYWRSRFMoIi4uL1FDLmZpbHRlcmVkLmNsdXN0ZXJlZC5jZWxscy5SRFMiKQpLTyA8LSByZWFkUkRTKCIuL0dtbmNLTy5jZWxscy5SRFMiKSAlPiUgc3Vic2V0KGlkZW50cyA9IGMoNjo5KSwgaW52ZXJ0ID0gVCkKYGBgCgpgYGB7cn0KcDEgPC0gRGltUGxvdChvYmplY3QgPSBXVCwKICAgICAgICAgICAgICBncm91cC5ieSA9ICJDZWxsX2lkZW50IiwKICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICAgICAgICBjb2xzID0gYygiI2ViY2IyZSIsICMiQ1IiCiAgICAgICAgICAgICAgICAgICAgICAgIiNlNzgyM2EiLCAjIkNoUCIKICAgICAgICAgICAgICAgICAgICAgICAiIzRjYWJkYyIsICMgQ2hwX3Byb2cKICAgICAgICAgICAgICAgICAgICAgICAiIzY4YjA0MSIsICMiRG9yc28tTWVkaWFsX3BhbGxpdW0iIAogICAgICAgICAgICAgICAgICAgICAgICIjZTQ2YjZiIiwgIyJIZW0iIAogICAgICAgICAgICAgICAgICAgICAgICIjZTNjMTQ4IiwgIyJNZWRpYWxfcGFsbGl1bSIKICAgICAgICAgICAgICAgICAgICAgICAiIzA0NmM5YSIsICMgUGFsbGlhbAogICAgICAgICAgICAgICAgICAgICAgICIjNDk5MGM5IiMiVGhhbGFtaWNfZW1pbmVuY2UiCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICBwdC5zaXplID0gMC41KSAgJiBOb0F4ZXMoKQoKCnAyIDwtIERpbVBsb3Qob2JqZWN0ID0gS08sCiAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiQ2VsbC5pZGVudCIsCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgICAgY29scyA9IGMoICIjNGNhYmRjIiwgIyBDaHBfcHJvZwogICAgICAgICAgICAgICAgICAgICAgICAiIzY4YjA0MSIsICMiRG9yc28tTWVkaWFsX3BhbGxpdW0iIAogICAgICAgICAgICAgICAgICAgICAgICAiI2U0NmI2YiIsICMiSGVtIiAKICAgICAgICAgICAgICAgICAgICAgICAgIiNlM2MxNDgiLCAjIk1lZGlhbF9wYWxsaXVtIgogICAgICAgICAgICAgICAgICAgICAgICAiI2E5OTYxYiIsCiAgICAgICAgICAgICAgICAgICAgICAgICIjZWJjYjJlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIiMwNDZjOWEiLCAjIFBhbGxpYWwKICAgICAgICAgICAgICAgICAgICAgICAgIiM0OTkwYzkiIyJUaGFsYW1pY19lbWluZW5jZSIKICAgICAgICAgICAgICApLAogICAgICAgICAgICAgIHB0LnNpemUgPSAwLjUpICAmIE5vQXhlcygpCgpwMSArIHAyCmBgYAoKIyBFeHByZXNzaW9uIG9mIE1DQyBhbmQgc3RyZXNzIHJlc3BvbnNlIGluIFdUIGFuZCBLTwoKYGBge3J9Ck1DQy5nZW5lcyA8LSBsaXN0KGMoIlRycDczIiwgIkdtbmMiLCAiRm94ajEiLCAiTXliIiwgIkNjbm8iLCAiQ2NkYzY3IiwgIkRldXAxIiwiTWNpZGFzIiwKICAgICAgICAgICAgICAgICAgICAiRTJmNCIsICJFMmY1IiwgIkFociIsICJUcnJhcCIsICJDZGMyMGIiLCAiQ2NkYzc4IiwgIlJmeDIiLAogICAgICAgICAgICAgICAgICAgICJSZngzIiwgIkZveG40IiwgIkZhbmsxIiwgIkphemYxIiwgIkNjbmExIiwgIk5lazEwIiwgIlBsazQiLAogICAgICAgICAgICAgICAgICAgICJDZXA2MyIsICJDZXAxNTIiLCAiU2FzczYiLCAiUGNudCIsICJQY20xIiwgIkNldG4yIiwgIlRmZHAxIikpCgpLTyA8LSBBZGRNb2R1bGVTY29yZShLTywKICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBNQ0MuZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiTUNDX3Njb3JlIikKCldUIDwtIEFkZE1vZHVsZVNjb3JlKFdULAogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IE1DQy5nZW5lcywKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJNQ0Nfc2NvcmUiKQpgYGAKCmBgYHtyfQpXVC5DUi5nb3Rlcm0gPC0gcmVhZC50YWJsZSgiLi4vQ2FqYWxSZXR6aXVzX3RyYWplY3RvcnkvQ1JfR09fcmVzLWJ5LXdhdmVzLmNzdiIsIHNlcCA9ICI7IiwgaGVhZGVyID0gVCkKCkROQV9kYW1hZ2VfR090ZXJtIDwtIFdULkNSLmdvdGVybSAlPiUgZmlsdGVyKHRlcm1faWQgJWluJSBjKCJHTzowMDA4NjMwIiwgIkdPOjAwMzAzMzAiLCAiR086MDAzMTU3MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHTzowMDA2OTc0IiwgIkdPOjAwMDY5NzciLCJHTzowMDMzNTU0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdPOjAwNDQ3NzMiLCAiR086MDA0Mjc3MSIsICJHTzowMDQyNzcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdPOjIwMDEwMjEiLCAiR086MTkwMjIyOSIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCkROQV9kYW1hZ2VfZ2VuZXMgPC0gRE5BX2RhbWFnZV9HT3Rlcm0gJT4lCiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHF1ZXJ5ICVpbiUgYygiQ2x1c3QuMiIsICJDbHVzdC4zIiwgIkNsdXN0LjQiKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHRlcm1faWQgPT0gIkdPOjAwMzM1NTQiKSAlPiUKICAgICAgICAgICAgICAgICAgICBwdWxsKGludGVyc2VjdGlvbikgJT4lIHN0cnNwbGl0KCJcXCwiKSAlPiUgdW5saXN0KCkgJT4lIHVuaXF1ZSgpCgpLTyA8LSBBZGRNb2R1bGVTY29yZShLTywKICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBsaXN0KEROQV9kYW1hZ2VfZ2VuZXMpLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gImNlbGx1bGFyX3Jlc3BvbnNlX3RvX3N0cmVzc19zY29yZSIpCgpXVCA8LSBBZGRNb2R1bGVTY29yZShXVCwKICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBsaXN0KEROQV9kYW1hZ2VfZ2VuZXMpLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gImNlbGx1bGFyX3Jlc3BvbnNlX3RvX3N0cmVzc19zY29yZSIpCmBgYAoKCmBgYHtyfQpncmFkaWVudCA8LSByZXYoYnJld2VyLnBhbCg4LCJSZFlsQnUiKSkKbGltIDwtICBjKC0wLjUsMC44KQoKcDEgPC0gZ2dwbG90KEtPQG1ldGEuZGF0YSwgYWVzKFNwcmluZ18xLCBTcHJpbmdfMikpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj1NQ0Nfc2NvcmUxKSwgc2l6ZT0xLCBzaGFwZT0xNikgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3Vycz1ncmFkaWVudCwKICAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gbGltLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSdNdWx0aWNpbGlhdGlvbiBzY29yZScpCgoKcDIgPC0gZ2dwbG90KFdUQG1ldGEuZGF0YSwgYWVzKFNwcmluZ18xLCBTcHJpbmdfMikpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj1NQ0Nfc2NvcmUxKSwgc2l6ZT0xLCBzaGFwZT0xNikgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3Vycz1ncmFkaWVudCwKICAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gbGltLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSdNdWx0aWNpbGlhdGlvbiBzY29yZScpCgpwMyA8LSBnZ3Bsb3QoS09AbWV0YS5kYXRhLCBhZXMoU3ByaW5nXzEsIFNwcmluZ18yKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPWNlbGx1bGFyX3Jlc3BvbnNlX3RvX3N0cmVzc19zY29yZTEpLCBzaXplPTEsIHNoYXBlPTE2KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzPWdyYWRpZW50LAogICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBsaW0sCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWU9J1N0cmVzcyByZXNwb25zZSBzY29yZScpCgpwNCA8LSBnZ3Bsb3QoV1RAbWV0YS5kYXRhLCBhZXMoU3ByaW5nXzEsIFNwcmluZ18yKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPWNlbGx1bGFyX3Jlc3BvbnNlX3RvX3N0cmVzc19zY29yZTEpLCBzaXplPTEsIHNoYXBlPTE2KSArIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzPSBncmFkaWVudCwKICAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gbGltLAogICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSdTdHJlc3MgcmVzcG9uc2Ugc2NvcmUnKQoKCk1DQy5zY29yZXMucGxvdCA8LSBwMiArIHAxIApTdHJlc3Muc2NvcmUucGxvdCA8LSBwNCArIHAzCgpmb3IgKGkgaW4gMToyKXsKICBNQ0Muc2NvcmVzLnBsb3RbW2ldXSRkYXRhIDwtIE1DQy5zY29yZXMucGxvdFtbaV1dJGRhdGFbb3JkZXIoTUNDLnNjb3Jlcy5wbG90W1tpXV0kZGF0YSRNQ0Nfc2NvcmUxKSxdCn0KCmZvciAoaSBpbiAxOjIpewogIFN0cmVzcy5zY29yZS5wbG90W1tpXV0kZGF0YSA8LSBTdHJlc3Muc2NvcmUucGxvdFtbaV1dJGRhdGFbb3JkZXIoU3RyZXNzLnNjb3JlLnBsb3RbW2ldXSRkYXRhJGNlbGx1bGFyX3Jlc3BvbnNlX3RvX3N0cmVzc19zY29yZTEpLF0KfQoKTUNDLnNjb3Jlcy5wbG90IC8gU3RyZXNzLnNjb3JlLnBsb3QKYGBgCiMgQ29tcHV0ZSBkaWZmZXJlbnRpYXRpb24gc3RhdGVzIHNjb3JlcwoKIyMgQVAKCmBgYHtyfQpBUGdlbmVzIDwtIGMoIlJnY2MiLCAiU3BhcmMiLCAiSGVzNSIsIkhlczEiLCAiU2xjMWEzIiwKICAgICAgICAgICAgICJEZGFoMSIsICJMZGhhIiwgIkhtZ2EyIiwiU2ZycDEiLCAiSWQ0IiwKICAgICAgICAgICAgICJDcmViNSIsICJQdG4iLCAiTHBhcjEiLCAiUmNuMSIsIlpmcDM2bDEiLAogICAgICAgICAgICAgIlNveDkiLCAiU294MiIsICJOcjJlMSIsICJUdHloMSIsICJUcmlwNiIpCgpLTyA8LSBBZGRNb2R1bGVTY29yZShLTywKICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBsaXN0KEFQZ2VuZXMpLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkFQX3NpZ25hdHVyZSIpCmBgYAoKIyMgQlAKCmBgYHtyfQpCUGdlbmVzIDwtIGMoIkVvbWVzIiwgIklnc2Y4IiwgIkluc20xIiwgIkVsYXZsMiIsICJFbGF2bDQiLAogICAgICAgICAgICAgIkhlczYiLCJHYWRkNDVnIiwgIk5ldXJvZzIiLCAiQnRnMiIsICJOZXVyb2cxIikKCktPIDwtIEFkZE1vZHVsZVNjb3JlKEtPLAogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGxpc3QoQlBnZW5lcyksCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQlBfc2lnbmF0dXJlIikKYGBgCgojIyBFTgoKYGBge3J9CkVOZ2VuZXMgPC0gYygiTWZhcDQiLCAiTmhsaDIiLCAiTmhsaDEiLCAiUHBwMXIxNGEiLCAiTmF2MSIsCiAgICAgICAgICAgICAiTmV1cm9kMSIsICJTb3JsMSIsICJTdmlwIiwgIkN4Y2wxMiIsICJUZW5tNCIsCiAgICAgICAgICAgICAiRGxsMyIsICJSZ21iIiwgIkNudG4yIiwgIlZhdDEiKQoKS08gPC0gQWRkTW9kdWxlU2NvcmUoS08sCiAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gbGlzdChFTmdlbmVzKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJFTl9zaWduYXR1cmUiKQpgYGAKCiMjIExOCgpgYGB7cn0KTE5nZW5lcyA8LSBjKCJTbmhnMTEiLCAiUGNzazFuIiwgIk1hcHQiLCAiSW5hIiwgIlN0bW40IiwKICAgICAgICAgICAgICJHYXA0MyIsICJUdWJiMmEiLCAiTHk2aCIsIlB0cHJkIiwgIk1lZjJjIikKCktPIDwtIEFkZE1vZHVsZVNjb3JlKEtPLAogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGxpc3QoTE5nZW5lcyksCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiTE5fc2lnbmF0dXJlIikKYGBgCgpgYGB7cn0KRmVhdHVyZVBsb3Qob2JqZWN0ID0gS08sCiAgICAgICAgICAgIGZlYXR1cmVzID0gYygiQVBfc2lnbmF0dXJlMSIsICJCUF9zaWduYXR1cmUxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVOX3NpZ25hdHVyZTEiLCAiTE5fc2lnbmF0dXJlMSIpLAogICAgICAgICAgICBwdC5zaXplID0gMC43NSwKICAgICAgICAgICAgY29scyA9IHJldihicmV3ZXIucGFsKDEwLCJTcGVjdHJhbCIpKSwKICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgIG9yZGVyID0gVCkgJiBOb0F4ZXMoKSAmIE5vTGVnZW5kKCkKYGBgCgojIEZpdCBwc2V1ZG90aW1lIG9uIENSIGFuZCBDUCBkaWZmZXJlbnRpYXRpbmcgbmV1cm9ucwoKIyMgR3JvdXAgY2VsbHMgaW4gUGFsbGlhbCBvciBDUiBsaW5lYWdlCgpgYGB7cn0KS08kTGluZWFnZSA8LSBzYXBwbHkoS08kQ2VsbC5pZGVudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh4ICVpbiUgYygiTmV1cm9uX3Byb2IuMiIsICJIZW0iKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJDYWphbC1SZXR6aXVzX25ldXJvbnMiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmICh4ICVpbiUgYygiTmV1cm9uX3Byb2IuMyIsICJNZWRpYWxfcGFsbGl1bSIpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gIlBhbGxpYWxfbmV1cm9ucyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJvdGhlciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSkKYGBgCgpgYGB7cn0KRGltUGxvdChLTywKICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICBncm91cC5ieSA9ICJMaW5lYWdlIiwKICAgICAgICBwdC5zaXplID0gMC41LAogICAgICAgIGNvbHMgPSAgYygiI2NjMzkxYiIsIiM5Njk2OTYiLCIjMDI2YzlhIikKICAgICAgICApICsgTm9BeGVzKCkKYGBgCgoKIyMgRml0IHByaW5jaXBhbGUgY3VydmUgb24gdGhlIHR3byBsaW5lYWdlcwoKYGBge3J9Ck5ldXJvbnMuZGF0YSA8LSAgc3Vic2V0KEtPLCAgc3Vic2V0ID0gTGluZWFnZSAlaW4lIGMoIkNhamFsLVJldHppdXNfbmV1cm9ucyIsICJQYWxsaWFsX25ldXJvbnMiKSAmIENlbGwuaWRlbnQgJWluJSBjKCJOZXVyb25fcHJvYi4yIiwgIk5ldXJvbl9wcm9iLjMiKSkKCkRpbVBsb3QoTmV1cm9ucy5kYXRhICwKICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICBncm91cC5ieSA9ICJMaW5lYWdlIiwKICAgICAgICBwdC5zaXplID0gMSwKICAgICAgICBjb2xzID0gIGMoIiNjYzM5MWIiLCIjMDI2YzlhIikKICAgICAgICApICsgTm9BeGVzKCkKYGBgCgpgYGB7cn0KZml0IDwtIHByaW5jaXBhbF9jdXJ2ZShhcy5tYXRyaXgoTmV1cm9ucy5kYXRhQG1ldGEuZGF0YVssYygiU3ByaW5nXzEiLCAiU3ByaW5nXzIiKV0pLAogICAgICAgICAgICAgICAgICAgICAgIHNtb290aGVyPSdsb3dlc3MnLAogICAgICAgICAgICAgICAgICAgICAgIHRyYWNlPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgZiA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgc3RyZXRjaD0wKQpgYGAKCmBgYHtyfQojUHNldWRvdGltZSBzY29yZQpQc2V1ZG90aW1lU2NvcmUgPC0gZml0JGxhbWJkYS9tYXgoZml0JGxhbWJkYSkKCmlmIChjb3IoUHNldWRvdGltZVNjb3JlLCBOZXVyb25zLmRhdGFAYXNzYXlzJFNDVEBkYXRhWydIbWdhMicsIF0pID4gMCkgewogIE5ldXJvbnMuZGF0YSRQc2V1ZG90aW1lU2NvcmUgPC0gLShQc2V1ZG90aW1lU2NvcmUgLSBtYXgoUHNldWRvdGltZVNjb3JlKSkKfQoKY29scyA8LSBicmV3ZXIucGFsKG4gPTExLCBuYW1lID0gIlNwZWN0cmFsIikKCmdncGxvdChOZXVyb25zLmRhdGFAbWV0YS5kYXRhLCBhZXMoU3ByaW5nXzEsIFNwcmluZ18yKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPVBzZXVkb3RpbWVTY29yZSksIHNpemU9Miwgc2hhcGU9MTYpICsgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnM9cmV2KGNvbHMpLCBuYW1lPSdQc2V1ZG90aW1lIHNjb3JlJykKYGBgCgojIyBQbG90IHBhbi1uZXVyb25hbCBnZW5lcyBhbG9uZyB0aGlzIGF4aXMKCmBgYHtyfQpOZXVyb25zLmRhdGEgPC0gTm9ybWFsaXplRGF0YShOZXVyb25zLmRhdGEsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIHNjYWxlLmZhY3RvciA9IDEwMDAwLCBhc3NheSA9ICJSTkEiKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg5LDEwKX0KVHJhamVjdG9yaWVzLm5ldXJvbnMgPC0gTmV1cm9ucy5kYXRhQG1ldGEuZGF0YSAlPiUgc2VsZWN0KEJhcmNvZGVzLCBTcHJpbmdfMSwgU3ByaW5nXzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBUF9zaWduYXR1cmUxLCBCUF9zaWduYXR1cmUxLCBFTl9zaWduYXR1cmUxLCBMTl9zaWduYXR1cmUxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTGluZWFnZSwgUHNldWRvdGltZVNjb3JlKQoKIyBOZXVyb2cyCnAxIDwtIEZlYXR1cmVQbG90KG9iamVjdCA9IE5ldXJvbnMuZGF0YSwKICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJOZXVyb2cyIiksCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCBicmV3ZXIucGFsKDksIllsR25CdSIpKSwKICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgIG9yZGVyID0gVCkgJiBOb0F4ZXMoKQoKVHJhamVjdG9yaWVzLm5ldXJvbnMkTmV1cm9nMiA8LSBOZXVyb25zLmRhdGFAYXNzYXlzJFJOQUBkYXRhWyJOZXVyb2cyIiwgVHJhamVjdG9yaWVzLm5ldXJvbnMkQmFyY29kZXNdCgpwMiA8LSBnZ3Bsb3QoVHJhamVjdG9yaWVzLm5ldXJvbnMsIGFlcyh4PSBQc2V1ZG90aW1lU2NvcmUsIHk9IE5ldXJvZzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IExpbmVhZ2UpLCBzaXplPTAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKSArCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDUwLCBhZXMoY29sb3I9IExpbmVhZ2UpKSArCiAgICAgICAgeWxpbSgwLE5BKQoKIyBUYnIxIApwMyA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBOZXVyb25zLmRhdGEgLAogICAgICAgICAgICBmZWF0dXJlcyA9IGMoIlRicjEiKSwKICAgICAgICAgICAgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgY29scyA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICAgICAgb3JkZXIgPSBUKSAmIE5vQXhlcygpClRyYWplY3Rvcmllcy5uZXVyb25zJFRicjEgPC0gTmV1cm9ucy5kYXRhQGFzc2F5cyRSTkFAZGF0YVsiVGJyMSIsIFRyYWplY3Rvcmllcy5uZXVyb25zJEJhcmNvZGVzXQoKcDQgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5uZXVyb25zLCBhZXMoeD0gUHNldWRvdGltZVNjb3JlLCB5PSBUYnIxKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkgKwogICAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgYWVzKGNvbG9yPSBMaW5lYWdlKSkgKwogICAgICAgIHlsaW0oMCxOQSkKCiMgTWFwdCAKcDUgPC0gRmVhdHVyZVBsb3Qob2JqZWN0ID0gTmV1cm9ucy5kYXRhICwKICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJNYXB0IiksCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCBicmV3ZXIucGFsKDksIllsR25CdSIpKSwKICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgIG9yZGVyID0gVCkgJiBOb0F4ZXMoKQoKVHJhamVjdG9yaWVzLm5ldXJvbnMkTWFwdCA8LSBOZXVyb25zLmRhdGFAYXNzYXlzJFJOQUBkYXRhWyJNYXB0IiwgVHJhamVjdG9yaWVzLm5ldXJvbnMkQmFyY29kZXNdCgpwNiA8LSBnZ3Bsb3QoVHJhamVjdG9yaWVzLm5ldXJvbnMsIGFlcyh4PSBQc2V1ZG90aW1lU2NvcmUsIHk9IE1hcHQpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IExpbmVhZ2UpLCBzaXplPTAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKSArCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDUwLCBhZXMoY29sb3I9IExpbmVhZ2UpKSArCiAgICAgICAgeWxpbSgwLE5BKQoKcDEgKyBwMiArIHAzICsgcDQgKyBwNSArIHA2ICsgcGF0Y2h3b3JrOjpwbG90X2xheW91dChuY29sID0gMikKYGBgCiMjIFNoaWZ0IHBzZXVkb3RpbWUgc2NvcmUKCmBgYHtyfQpzY29yZSA8LSBzYXBwbHkoVHJhamVjdG9yaWVzLm5ldXJvbnMkUHNldWRvdGltZVNjb3JlLAogICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgaWYgKHggPD0gMC4xNSkge3g9IDAuMTV9IGVsc2UgeyB4PXggfSkKClRyYWplY3Rvcmllcy5uZXVyb25zJFBzZXVkb3RpbWVTY29yZS5zaGlmdGVkIDwtIChzY29yZSAtIG1pbihzY29yZSkpIC8gKG1heChzY29yZSkgLSBtaW4oc2NvcmUpKQpgYGAKCmBgYHtyfQojIE5ldXJvZzIKcDEgPC0gRmVhdHVyZVBsb3Qob2JqZWN0ID0gTmV1cm9ucy5kYXRhICwKICAgICAgICAgICAgZmVhdHVyZXMgPSBjKCJOZXVyb2cyIiksCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgIGNvbHMgPSBjKCJncmV5OTAiLCBicmV3ZXIucGFsKDksIllsR25CdSIpKSwKICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgIG9yZGVyID0gVCkgJiBOb0F4ZXMoKQoKcDIgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5uZXVyb25zLCBhZXMoeD0gUHNldWRvdGltZVNjb3JlLnNoaWZ0ZWQsIHk9IE5ldXJvZzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IExpbmVhZ2UpLCBzaXplPTAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKSArCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDUwLCBhZXMoY29sb3I9IExpbmVhZ2UpKSArCiAgICAgICAgeWxpbSgwLE5BKQoKIyBUYnIxIApwMyA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBOZXVyb25zLmRhdGEgLAogICAgICAgICAgICBmZWF0dXJlcyA9IGMoIlRicjEiKSwKICAgICAgICAgICAgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgY29scyA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICAgICAgb3JkZXIgPSBUKSAmIE5vQXhlcygpCgpwNCA8LSBnZ3Bsb3QoVHJhamVjdG9yaWVzLm5ldXJvbnMsIGFlcyh4PSBQc2V1ZG90aW1lU2NvcmUuc2hpZnRlZCwgeT0gVGJyMSkpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gTGluZWFnZSksIHNpemU9MC41KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiI2NjMzkxYiIsICIjMDI2YzlhIikpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgbj0gNTAsIGFlcyhjb2xvcj0gTGluZWFnZSkpICsKICAgICAgICB5bGltKDAsTkEpCgojIE1hcHQgCnA1IDwtIEZlYXR1cmVQbG90KG9iamVjdCA9IE5ldXJvbnMuZGF0YSAsCiAgICAgICAgICAgIGZlYXR1cmVzID0gYygiTWFwdCIpLAogICAgICAgICAgICBwdC5zaXplID0gMC41LAogICAgICAgICAgICBjb2xzID0gYygiZ3JleTkwIiwgYnJld2VyLnBhbCg5LCJZbEduQnUiKSksCiAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgICAgICBvcmRlciA9IFQpICYgTm9BeGVzKCkKCnA2IDwtIGdncGxvdChUcmFqZWN0b3JpZXMubmV1cm9ucywgYWVzKHg9IFBzZXVkb3RpbWVTY29yZS5zaGlmdGVkLCB5PSBNYXB0KSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkgKwogICAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgYWVzKGNvbG9yPSBMaW5lYWdlKSkgKwogICAgICAgIHlsaW0oMCxOQSkKCnAxICsgcDIgKyBwMyArIHA0ICsgcDUgKyBwNiArIHBhdGNod29yazo6cGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKCmBgYHtyfQpUcmFqZWN0b3JpZXMubmV1cm9ucyRuVU1JIDwtIE5ldXJvbnMuZGF0YSRuQ291bnRfUk5BCgpnZ3Bsb3QoVHJhamVjdG9yaWVzLm5ldXJvbnMsIGFlcyh4PSBQc2V1ZG90aW1lU2NvcmUuc2hpZnRlZCwgeT0gblVNSS8xMDAwMCkpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gTGluZWFnZSksIHNpemU9MC41KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiI2NjMzkxYiIsICIjMDI2YzlhIikpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgbj0gNTAsIGFlcyhjb2xvcj0gTGluZWFnZSkpICsKICAgICAgICB5bGltKDAsTkEpCmBgYAoKIyBGaXQgY2VsbCBjeWNsZSB0cmFqZWN0b3J5IG9uIHByb2dlbml0b3JzCgpUbyBiYWxhbmNlIHRoZSBudW1iZXIgb2YgcHJvZ2VuaXRvcnMgaW4gYm90aCBkb21haW4gd2Ugd2lsbCBvbmx5IHdvcmsgd2l0aCAqSGVtKiBhbmQgKk1lZGlhbF9wYWxsaXVtKiBhbm5vdGF0ZWQgY2VsbHMuIFNpbmNlIHdlIGFyZSB1c2luZyBwYWxsaWFsIGNlbGwgdG8gY29udHJhc3QgQ1Igc3BlY2lmaWMgdHJhamVjdG9yeSB3ZSB0aGluayB0aGlzIGFwcHJveGltYXRpb24gd2lsbCBub3Qgc2lnbmlmaWNhbnRseSBhZmZlY3Qgb3VyIGFuYWx5c2lzLgoKYGBge3J9ClByb2dlbml0b3JzLmRhdGEgPC0gIHN1YnNldChLTywgIHN1YnNldCA9IExpbmVhZ2UgJWluJSBjKCJDYWphbC1SZXR6aXVzX25ldXJvbnMiLCAiUGFsbGlhbF9uZXVyb25zIikgJiBDZWxsLmlkZW50ICVpbiUgYygiSGVtIiwgIk1lZGlhbF9wYWxsaXVtIikpCgpEaW1QbG90KFByb2dlbml0b3JzLmRhdGEsCiAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgZ3JvdXAuYnkgPSAiQ2VsbC5pZGVudCIsCiAgICAgICAgcHQuc2l6ZSA9IDEsCiAgICAgICAgY29scyA9ICBjKCIjY2MzOTFiIiwiIzAyNmM5YSIpCiAgICAgICAgKSArIE5vQXhlcygpCmBgYApgYGB7cn0KdGFibGUoUHJvZ2VuaXRvcnMuZGF0YSRDZWxsLmlkZW50KQpgYGAKCmBgYHtyfQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIGMoIlRyYWplY3Rvcmllcy5uZXVyb25zIiwgIlByb2dlbml0b3JzLmRhdGEiKV0pCmdjKCkKYGBgCgojIyBQcmVwYXJlIGRhdGEgZm9yIFJldmVsaW8KCmBgYHtyfQpyYXdDb3VudHMgPC0gYXMubWF0cml4KFByb2dlbml0b3JzLmRhdGFbWyJSTkEiXV1AY291bnRzKQoKIyBGaWx0ZXIgZ2VuZXMgZXhwcmVzc2VkIGJ5IGxlc3MgdGhhbiAxMCBjZWxscwpudW0uY2VsbHMgPC0gTWF0cml4Ojpyb3dTdW1zKHJhd0NvdW50cyA+IDApCmdlbmVzLnVzZSA8LSBuYW1lcyh4ID0gbnVtLmNlbGxzW3doaWNoKHggPSBudW0uY2VsbHMgPj0gMTApXSkKcmF3Q291bnRzIDwtIHJhd0NvdW50c1tnZW5lcy51c2UsIF0KYGBgCgojIyBSdW4gUmV2ZWxpbwoKYGBge3J9CkNDZ2VuZXMgPC0gcmVhZC50YWJsZSgiLi4vQ2hvcm9pZFBsZXh1c190cmFqZWN0b3J5L0NDZ2VuZXMuY3N2Iiwgc2VwID0gIjsiLCBoZWFkZXIgPSBUKQpgYGAKCldlIGNhbiBub3cgZm9sbG93IHRoZSB0dXRvcmlhbCBmb3JtIHRoZSBbcGFja2FnZSBnaXRodWIgcGFnZV0oaHR0cHM6Ly9naXRodWIuY29tL2RhbmllbHNjaHcxODgvUmV2ZWxpbykKCgpgYGB7cn0KbXlEYXRhIDwtIGNyZWF0ZVJldmVsaW9PYmplY3QocmF3RGF0YSA9IHJhd0NvdW50cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3ljbGljR2VuZXMgPSBDQ2dlbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb3dlcm5HZW5lQ3V0b2ZmID0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBwZXJuVU1JQ3V0b2ZmID0gSW5mLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjY1BoYXNlQXNzaWduQmFzZWRPbkluZGl2aWR1YWxCYXRjaGVzID0gRikKCnJtKCJyYXdDb3VudHMiKQpnYygpCmBgYApUaGUgZ2V0Q2VsbEN5Y2xlUGhhc2VBc3NpZ25JbmZvcm1hdGlvbiBmaWx0ZXIg4oCcb3V0bGllcuKAnSBjZWxscyBmb3IgY2VsbCBjeWNsZSBwaGFzZSBhc3NpZ25hdGlvbi4gV2UgbW9kaWZpZWQgdGhlIGZ1bmN0aW9uIHRvIGtlZXAgYWxsIGNlbGxzIGFzIHdlIG9ic2VydmVkIHRoaXMgZG9lcyBub3QgYWZmZWN0IHRoZSBnbG9iYWwgY2VsbCBjeWNsZSBmaXR0aW5nIHByb2NlZHVyZQoKYGBge3J9CnNvdXJjZSgiLi4vRnVuY3Rpb25zL2Z1bmN0aW9uc19Jbml0aWFsaXphdGlvbkNDUGhhc2VBc3NpZ25GaWx0ZXJpbmcuUiIpCgpteURhdGEgPC0gZ2V0Q2VsbEN5Y2xlUGhhc2VBc3NpZ25fYWxsY2VsbHMobXlEYXRhKQpgYGAKCmBgYHtyfQpteURhdGEgPC0gZ2V0UENBRGF0YShkYXRhTGlzdCA9IG15RGF0YSkKCm15RGF0YSA8LSBnZXRPcHRpbWFsUm90YXRpb24oZGF0YUxpc3QgPSBteURhdGEpCmBgYAojIyBHcmFwaGljYWwgYXNzZXNtZW50IG9mIGNlbGwgY3ljbGUgZml0dGluZwoKYGBge3J9CkNlbGxDeWNsZWRhdGEgPC0gY2JpbmQoYXMuZGF0YS5mcmFtZSh0KG15RGF0YUB0cmFuc2Zvcm1lZERhdGEkZGMkZGF0YVsxOjIsXSkpLAogICAgICAgICAgICAgICAgICAgICAgIG5VTUk9IG15RGF0YUBjZWxsSW5mbyRuVU1JLAogICAgICAgICAgICAgICAgICAgICAgIFJldmVsaW8ucGhhc2UgPSBmYWN0b3IobXlEYXRhQGNlbGxJbmZvJGNjUGhhc2UsIGxldmVscyA9IGMoIkcxLlMiLCAiUyIsICJHMiIsICJHMi5NIiwgIk0uRzEiKSksCiAgICAgICAgICAgICAgICAgICAgICAgUmV2ZWxpby5jYz0gbXlEYXRhQGNlbGxJbmZvJGNjUGVyY2VudGFnZVVuaWZvcm1seVNwYWNlZCwKICAgICAgICAgICAgICAgICAgICAgICBEb21haW49IFByb2dlbml0b3JzLmRhdGEkQ2VsbC5pZGVudCkKYGBgCgpgYGB7cn0KcDEgPC0gZ2dwbG90KENlbGxDeWNsZWRhdGEsIGFlcyhEQzEsIERDMikpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFJldmVsaW8ucGhhc2UpLCBzaXplPSAwLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbMTozXSwiZ3JleTQwIix3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzVdKSkKCnAyIDwtIGdncGxvdChDZWxsQ3ljbGVkYXRhLCBhZXMoREMxLCBEQzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBEb21haW4pLCBzaXplID0gMC41KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiI2NjMzkxYiIsIiMwMjZjOWEiKSkKCnAzIDwtIGdncGxvdChDZWxsQ3ljbGVkYXRhLCBhZXMoREMxLCBEQzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9UmV2ZWxpby5jYyksIHNpemU9MC41LCBzaGFwZT0xNikgKyAKICAgICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3Vycz1yZXYoY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKG4gPTExLCBuYW1lID0gIlNwZWN0cmFsIikpKDEwMCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSdSZXZlbGlvX2NjJykKCnA0IDwtIGdncGxvdChDZWxsQ3ljbGVkYXRhLCBhZXMoeD0gUmV2ZWxpby5jYywgeT0gblVNSS8xMDAwMCkpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gUmV2ZWxpby5waGFzZSksIHNpemU9MC41KSArCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYyh3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzE6M10sImdyZXk0MCIsd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVs1XSkpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgbj0gNTAsIGZpbGw9ImdyZXkiKSArCiAgICAgICAgeWxpbSgwLE5BKQoKKHAxICsgcDIpIC8ocDMgKyBwNCkKYGBgCmBgYHtyfQpQcm9nZW5pdG9ycy5kYXRhJFJldmVsaW8ucGhhc2UgPC0gQ2VsbEN5Y2xlZGF0YSRSZXZlbGlvLnBoYXNlClByb2dlbml0b3JzLmRhdGEkUmV2ZWxpby5jYyA8LSBDZWxsQ3ljbGVkYXRhJFJldmVsaW8uY2MKCnAxIDwtIEZlYXR1cmVQbG90KG9iamVjdCA9IFByb2dlbml0b3JzLmRhdGEsCiAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIlJldmVsaW8uY2MiLAogICAgICAgICAgICAgICAgICBwdC5zaXplID0gMSwKICAgICAgICAgICAgICAgICAgY29scyA9IHJldihicmV3ZXIucGFsKDEwLCJTcGVjdHJhbCIpKSwKICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgICAgICAgIG9yZGVyID0gVCkgJiBOb0F4ZXMoKQoKcDIgPC0gRGltUGxvdChvYmplY3QgPSBQcm9nZW5pdG9ycy5kYXRhLAogICAgICAgICAgICAgIGdyb3VwLmJ5ID0gIlJldmVsaW8ucGhhc2UiLAogICAgICAgICAgICAgIHB0LnNpemUgPSAxLAogICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJzcHJpbmciLAogICAgICAgICAgICAgIGNvbHMgPSAgYyh3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzE6M10sImdyZXk0MCIsd2VzX3BhbGV0dGUoIkZhbnRhc3RpY0ZveDEiKVs1XSkpICYgTm9BeGVzKCkKCnAxIC8gcDIKYGBgCgojIyBUcmFuc2ZlcnQgbGVhcm4gY2VsbCBjeWNsZSBheGlzCgpgYGB7cn0KUHJvZ2VuaXRvcnMgPC0gUHJvZ2VuaXRvcnMuZGF0YUBtZXRhLmRhdGEgJT4lIHNlbGVjdChCYXJjb2RlcywgU3ByaW5nXzEsIFNwcmluZ18yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFQX3NpZ25hdHVyZTEsIEJQX3NpZ25hdHVyZTEsIEVOX3NpZ25hdHVyZTEsIExOX3NpZ25hdHVyZTEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTGluZWFnZSkKClByb2dlbml0b3JzJFBzZXVkb3RpbWVTY29yZSA8LSBDZWxsQ3ljbGVkYXRhJFJldmVsaW8uY2MKUHJvZ2VuaXRvcnMkblVNSSA8LSBQcm9nZW5pdG9ycy5kYXRhJG5Db3VudF9STkEKYGBgCgojIENvbWJpbmUgUHJvZ2VuaXRvcnMgYW5kIGRpZmZlcmVudGlhdGluZyBuZXVyb25zIGRhdGEKCmBgYHtyfQojIFN0YXJ0IHdpdGggbmV1cm9ucyBkYXRhClRyYWplY3Rvcmllcy5hbGwgPC0gVHJhamVjdG9yaWVzLm5ldXJvbnMgJT4lIHNlbGVjdChCYXJjb2RlcywgblVNSSwgU3ByaW5nXzEsIFNwcmluZ18yLCBBUF9zaWduYXR1cmUxLCBCUF9zaWduYXR1cmUxLCBFTl9zaWduYXR1cmUxLCBMTl9zaWduYXR1cmUxLCBMaW5lYWdlKQoKVHJhamVjdG9yaWVzLmFsbCRQc2V1ZG90aW1lIDwtIFRyYWplY3Rvcmllcy5uZXVyb25zJFBzZXVkb3RpbWVTY29yZS5zaGlmdGVkICsgMC41ClRyYWplY3Rvcmllcy5hbGwkUGhhc2UgPC0gTkEKYGBgCgpgYGB7cn0KIyBBZGQgcHJvZ2VuaXRvcnMgZGF0YQpUcmFqZWN0b3JpZXMucHJvZ2VuaXRvcnMgPC0gUHJvZ2VuaXRvcnMgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChCYXJjb2RlcywgblVNSSwgU3ByaW5nXzEsIFNwcmluZ18yLCBBUF9zaWduYXR1cmUxLCBCUF9zaWduYXR1cmUxLCBFTl9zaWduYXR1cmUxLCBMTl9zaWduYXR1cmUxLCBMaW5lYWdlKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShQc2V1ZG90aW1lPSBQcm9nZW5pdG9ycy5kYXRhJFJldmVsaW8uY2MvMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBoYXNlID0gUHJvZ2VuaXRvcnMuZGF0YSRSZXZlbGlvLnBoYXNlKQpgYGAKCmBgYHtyfQpUcmFqZWN0b3JpZXMuYWxsIDwtIHJiaW5kKFRyYWplY3Rvcmllcy5hbGwsIFRyYWplY3Rvcmllcy5wcm9nZW5pdG9ycykKClRyYWplY3Rvcmllcy5hbGwkUGhhc2UgPC0gZmFjdG9yKFRyYWplY3Rvcmllcy5hbGwkUGhhc2UsIGxldmVscyA9IGMoIkcxLlMiLCAiUyIsICJHMiIsICJHMi5NIiwgIk0uRzEiKSkKYGBgCgpgYGB7cn0KcDEgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5hbGwsIGFlcyhTcHJpbmdfMSwgU3ByaW5nXzIpKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9UHNldWRvdGltZSksIHNpemU9MC41KSArIAogICAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzPXJldihicmV3ZXIucGFsKG4gPTExLCBuYW1lID0gIlNwZWN0cmFsIikpLCBuYW1lPSdQc2V1ZG90aW1lIHNjb3JlJykKCnAyIDwtIGdncGxvdChUcmFqZWN0b3JpZXMuYWxsLCBhZXMoU3ByaW5nXzEsIFNwcmluZ18yKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkKCnAxICsgcDIKYGBgCmBgYHtyfQpwMSA8LSBnZ3Bsb3QoVHJhamVjdG9yaWVzLmFsbCwgYWVzKHg9IFBzZXVkb3RpbWUsIHk9IG5VTUkvMTAwMDApKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IFBoYXNlKSwgc2l6ZT0wLjUpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbMTozXSwiZ3JleTQwIix3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzVdKSkgKwogICAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgZmlsbD0iZ3JleSIpICsKICAgICAgICB5bGltKDAsTkEpCgpwMiA8LSBnZ3Bsb3QoVHJhamVjdG9yaWVzLmFsbCwgYWVzKHg9IFBzZXVkb3RpbWUsIHk9IG5VTUkvMTAwMDApKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IExpbmVhZ2UpLCBzaXplPTAuNSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKSArCiAgICAgICAgZ2VvbV9zbW9vdGgoYWVzKGNvbG9yPSBMaW5lYWdlKSwgbWV0aG9kPSJsb2VzcyIsIG49IDUwLCBmaWxsPSJncmV5IikgKwogICAgICAgIHlsaW0oMCxOQSkKCnAxIC8gcDIKYGBgCgoKYGBge3J9CnAxIDwtIGdncGxvdChUcmFqZWN0b3JpZXMuYWxsLCBhZXMoeD0gUHNldWRvdGltZSwgeT0gQVBfc2lnbmF0dXJlMSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gTGluZWFnZSksIHNpemU9MC41KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiI2NjMzkxYiIsICIjMDI2YzlhIikpICsKICBnZW9tX3Ntb290aChhZXMoY29sb3I9IExpbmVhZ2UpLCBtZXRob2Q9ImxvZXNzIiwgbj0gNTAsIGZpbGw9ImdyZXkiKQoKCnAyIDwtIGdncGxvdChUcmFqZWN0b3JpZXMuYWxsLCBhZXMoeD0gUHNldWRvdGltZSwgeT0gQlBfc2lnbmF0dXJlMSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvcj0gTGluZWFnZSksIHNpemU9MC41KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiI2NjMzkxYiIsICIjMDI2YzlhIikpICsKICBnZW9tX3Ntb290aChhZXMoY29sb3I9IExpbmVhZ2UpLCBtZXRob2Q9ImxvZXNzIiwgbj0gNTAsIGZpbGw9ImdyZXkiKQoKcDMgPC0gZ2dwbG90KFRyYWplY3Rvcmllcy5hbGwsIGFlcyh4PSBQc2V1ZG90aW1lLCB5PSBFTl9zaWduYXR1cmUxKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBMaW5lYWdlKSwgc2l6ZT0wLjUpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjY2MzOTFiIiwgIiMwMjZjOWEiKSkgKwogIGdlb21fc21vb3RoKGFlcyhjb2xvcj0gTGluZWFnZSksIG1ldGhvZD0ibG9lc3MiLCBuPSA1MCwgZmlsbD0iZ3JleSIpCgpwNCA8LSBnZ3Bsb3QoVHJhamVjdG9yaWVzLmFsbCwgYWVzKHg9IFBzZXVkb3RpbWUsIHk9IExOX3NpZ25hdHVyZTEpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9IExpbmVhZ2UpLCBzaXplPTAuNSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKSArCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbG9yPSBMaW5lYWdlKSwgbWV0aG9kPSJsb2VzcyIsIG49IDUwLCBmaWxsPSJncmV5IikKCgpwMSAvIHAyIC8gcDMgLyBwNApgYGAKCiMjIFN1YnNldCB0aGUgZnVsbCBTZXVyYXQgb2JqZWN0CgpgYGB7cn0KS08gPC0gcmVhZFJEUygiLi9HbW5jS08uY2VsbHMuUkRTIikgJT4lIHN1YnNldChpZGVudHMgPSBjKDY6OSksIGludmVydCA9IFQpCmBgYAoKYGBge3J9Ck5ldXJvLnRyYWplY3RvcmllcyA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gS09AYXNzYXlzJFJOQUBkYXRhWywgVHJhamVjdG9yaWVzLmFsbCRCYXJjb2Rlc10sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YS5kYXRhID0gVHJhamVjdG9yaWVzLmFsbCkKCnNwcmluZyA8LSBhcy5tYXRyaXgoTmV1cm8udHJhamVjdG9yaWVzQG1ldGEuZGF0YSAlPiUgc2VsZWN0KCJTcHJpbmdfMSIsICJTcHJpbmdfMiIpKQogIApOZXVyby50cmFqZWN0b3JpZXNbWyJzcHJpbmciXV0gPC0gQ3JlYXRlRGltUmVkdWNPYmplY3QoZW1iZWRkaW5ncyA9IHNwcmluZywga2V5ID0gIlNwcmluZ18iLCBhc3NheSA9IERlZmF1bHRBc3NheShOZXVyby50cmFqZWN0b3JpZXMpKQpgYGAKCmBgYHtyfQpwMSA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBOZXVyby50cmFqZWN0b3JpZXMsCiAgICAgICAgICAgIGZlYXR1cmVzID0gIlBzZXVkb3RpbWUiLAogICAgICAgICAgICBwdC5zaXplID0gMC41LAogICAgICAgICAgICBjb2xzID0gcmV2KGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbChuID0xMSwgbmFtZSA9ICJTcGVjdHJhbCIpKSgxMDApKSwKICAgICAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgICAgIG9yZGVyID0gVCkgJiBOb0F4ZXMoKQoKcDIgPC0gRGltUGxvdChvYmplY3QgPSBOZXVyby50cmFqZWN0b3JpZXMsCiAgICAgICAgZ3JvdXAuYnkgPSAiTGluZWFnZSIsCiAgICAgICAgcHQuc2l6ZSA9IDAuNSwKICAgICAgICByZWR1Y3Rpb24gPSAic3ByaW5nIiwKICAgICAgICBjb2xzID0gIGMoIiNjYzM5MWIiLCAiIzAyNmM5YSIpKSAmIE5vQXhlcygpCgoKcDMgPC0gRGltUGxvdChvYmplY3QgPSBOZXVyby50cmFqZWN0b3JpZXMsCiAgICAgICAgZ3JvdXAuYnkgPSAiUGhhc2UiLAogICAgICAgIHB0LnNpemUgPSAwLjUsCiAgICAgICAgcmVkdWN0aW9uID0gInNwcmluZyIsCiAgICAgICAgY29scyA9ICBjKHdlc19wYWxldHRlKCJGYW50YXN0aWNGb3gxIilbMTozXSwiZ3JleTQwIix3ZXNfcGFsZXR0ZSgiRmFudGFzdGljRm94MSIpWzVdKSkgJiBOb0F4ZXMoKQoKcDEgKyBwMiArIHAzCmBgYApgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCJOZXVyby50cmFqZWN0b3JpZXMiKV0pCmdjKCkKYGBgCgojIyBOb3JtYWxpemF0aW9uCgpgYGB7cn0KTmV1cm8udHJhamVjdG9yaWVzPC0gTm9ybWFsaXplRGF0YShOZXVyby50cmFqZWN0b3JpZXMsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIHNjYWxlLmZhY3RvciA9IDEwMDAwLCBhc3NheSA9ICJSTkEiKQpgYGAKCmBgYHtyfQpOZXVyby50cmFqZWN0b3JpZXMgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoTmV1cm8udHJhamVjdG9yaWVzLCBzZWxlY3Rpb24ubWV0aG9kID0gImRpc3AiLCBuZmVhdHVyZXMgPSAzMDAwLCBhc3NheSA9ICJSTkEiKQpgYGAKCiMjIFBsb3Qgc29tZSBnZW5lcyBhbG9uZyBwc2V1ZG90aW1lCgpgYGB7ciBmaWcuZGltPWMoOSw4KX0Kc291cmNlKCIuLi9GdW5jdGlvbnMvZnVuY3Rpb25zX0dlbmVUcmVuZHMuUiIpCgpQbG90LkdlbmVzLnRyZW5kKFNldXJhdC5kYXRhPSBOZXVyby50cmFqZWN0b3JpZXMsCiAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiTGluZWFnZSIsCiAgICAgICAgICAgICAgICAgZ2VuZXM9IGMoIkdhczEiLCJTb3gyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiTmV1cm9nMiIsICJCdGcyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiVGJyMSIsICJNYXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJwNzMiLCAiRm94ZzEiKSkKYGBgCgpgYGB7ciBmaWcuZGltPWMoOSw2KX0KUGxvdC5HZW5lcy50cmVuZChTZXVyYXQuZGF0YT0gTmV1cm8udHJhamVjdG9yaWVzLAogICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gIkxpbmVhZ2UiLAogICAgICAgICAgICAgICAgIGdlbmVzPSBjKCJHbW5jIiwgIk1jaWRhcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkZveGoxIiwgIlRycDczIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiTGh4MSIsICJDZGtuMWEiKSkKYGBgCgpgYGB7ciBmaWcuZGltPWMoOSw1KX0KUGxvdC5HZW5lcy50cmVuZChTZXVyYXQuZGF0YT0gTmV1cm8udHJhamVjdG9yaWVzLAogICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gIkxpbmVhZ2UiLAogICAgICAgICAgICAgICAgIGdlbmVzPSBjKCJNa2k2NyIsICJUb3AyYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkgyYWZ4IiwgIkNka24xYyIpKQpgYGAKCiMgVXNlIG1vbm9jbGUyIHRvIG1vZGVsIGdlbmUgZXhwcmVzc2lvbiBhbG9uZyBjeWNsaW5nIGF4aXMKCiMjIEluaXRpYWxpemUgYSBtb25vY2xlIG9iamVjdAoKYGBge3J9CiMgVHJhbnNmZXIgbWV0YWRhdGEKbWV0YS5kYXRhIDwtIGRhdGEuZnJhbWUoQmFyY29kZT0gTmV1cm8udHJhamVjdG9yaWVzJEJhcmNvZGVzLAogICAgICAgICAgICAgICAgICAgICAgICBMaW5lYWdlPSBOZXVyby50cmFqZWN0b3JpZXMkTGluZWFnZSwKICAgICAgICAgICAgICAgICAgICAgICAgUHNldWRvdGltZT0gTmV1cm8udHJhamVjdG9yaWVzJFBzZXVkb3RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgIFBoYXNlPSBOZXVyby50cmFqZWN0b3JpZXMkUGhhc2UpCgpBbm5vdC5kYXRhICA8LSBuZXcoJ0Fubm90YXRlZERhdGFGcmFtZScsIGRhdGEgPSBtZXRhLmRhdGEpCgojIFRyYW5zZmVyIGNvdW50cyBkYXRhCnZhci5nZW5lcyA8LSBOZXVyby50cmFqZWN0b3JpZXNbWyJSTkEiXV1AdmFyLmZlYXR1cmVzCmNvdW50LmRhdGEgPSBkYXRhLmZyYW1lKGdlbmVfc2hvcnRfbmFtZSA9IHJvd25hbWVzKE5ldXJvLnRyYWplY3Rvcmllc1tbIlJOQSJdXUBkYXRhW3Zhci5nZW5lcyxdKSwKICAgICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gcm93bmFtZXMoTmV1cm8udHJhamVjdG9yaWVzW1siUk5BIl1dQGRhdGFbdmFyLmdlbmVzLF0pKQoKZmVhdHVyZS5kYXRhIDwtIG5ldygnQW5ub3RhdGVkRGF0YUZyYW1lJywgZGF0YSA9IGNvdW50LmRhdGEpCgojIENyZWF0ZSB0aGUgQ2VsbERhdGFTZXQgb2JqZWN0IGluY2x1ZGluZyB2YXJpYWJsZSBnZW5lcyBvbmx5CmdibV9jZHMgPC0gbmV3Q2VsbERhdGFTZXQoTmV1cm8udHJhamVjdG9yaWVzW1siUk5BIl1dQGNvdW50c1t2YXIuZ2VuZXMsXSwKICAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGEgPSBBbm5vdC5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVEYXRhID0gZmVhdHVyZS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxvd2VyRGV0ZWN0aW9uTGltaXQgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgIGV4cHJlc3Npb25GYW1pbHkgPSBuZWdiaW5vbWlhbCgpKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdibV9jZHMgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhnYm1fY2RzKQpnYm1fY2RzIDwtIGVzdGltYXRlRGlzcGVyc2lvbnMoZ2JtX2NkcykKZ2JtX2NkcyA8LSBkZXRlY3RHZW5lcyhnYm1fY2RzLCBtaW5fZXhwciA9IDAuMSkKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCJOZXVyby50cmFqZWN0b3JpZXMiLCAiZ2JtX2NkcyIsICJHZW5lLlRyZW5kIiwgIlBsb3QuR2VuZXMudHJlbmQiKV0pCmdjKCkKYGBgCiMjIEZpbmQgUGFuLW5ldXJvbmFsIGdlbmVzCgpgYGB7cn0KIyBTcGxpdCBwYWxsaWFsIGFuZCBzdWJwYWxsaWFsIGNlbGxzIGZvciBnZW5lIGV4cHJlc3Npb24gZml0dGluZwojUGFsbGlhbCBjZWxscwpQYWxsaWFsY2VsbHMgPC0gTmV1cm8udHJhamVjdG9yaWVzQG1ldGEuZGF0YSAlPiUKICAgICAgICAgICAgICAgIGZpbHRlcihMaW5lYWdlID09ICJQYWxsaWFsX25ldXJvbnMiKSAlPiUKICAgICAgICAgICAgICAgIHB1bGwoQmFyY29kZXMpCgojIENhamFsLVJldHppdXMgY2VsbHMKQ1JjZWxscyA8LSBOZXVyby50cmFqZWN0b3JpZXNAbWV0YS5kYXRhICU+JQogICAgICAgICAgICAgICAgICAgZmlsdGVyKExpbmVhZ2UgPT0gIkNhamFsLVJldHppdXNfbmV1cm9ucyIpICU+JQogICAgICAgICAgICAgICAgICAgcHVsbChCYXJjb2RlcykKYGBgCgpgYGB7cn0KIyBXZSBmaWx0ZXItb3V0IGdlbmVzIGRldGVjdGVkIGluIGxlc3MgdGhhbiAyMDAgb3IgMjAwIGNlbGxzIGFsb25nIFBhbGxpYWwgb3IgQ1IgbGluZWFnZXMKbnVtLmNlbGxzIDwtIE1hdHJpeDo6cm93U3VtcyhOZXVyby50cmFqZWN0b3JpZXNAYXNzYXlzJFJOQUBjb3VudHNbLFBhbGxpYWxjZWxsc10gPiAwKQpQYWxsaWFsLmV4cHJlc3NlZCA8LSBuYW1lcyh4ID0gbnVtLmNlbGxzW3doaWNoKHggPSBudW0uY2VsbHMgPj0gMjAwKV0pCgpudW0uY2VsbHMgPC0gTWF0cml4Ojpyb3dTdW1zKE5ldXJvLnRyYWplY3Rvcmllc0Bhc3NheXMkUk5BQGNvdW50c1ssQ1JjZWxsc10gPiAwKQpDUi5leHByZXNzZWQgPC0gbmFtZXMoeCA9IG51bS5jZWxsc1t3aGljaCh4ID0gbnVtLmNlbGxzID49IDIwMCldKQoKSW5wdXQuZ2VuZXMgPC0gcm93bmFtZXMoZ2JtX2Nkcylbcm93bmFtZXMoZ2JtX2NkcykgJWluJSBpbnRlcnNlY3QoUGFsbGlhbC5leHByZXNzZWQsIENSLmV4cHJlc3NlZCldCmBgYAoKCmBgYHtyICBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFfQpQYWxsaWFsLmdlbmVzIDwtIGRpZmZlcmVudGlhbEdlbmVUZXN0KGdibV9jZHNbSW5wdXQuZ2VuZXMsIFBhbGxpYWxjZWxsc10sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbE1vZGVsRm9ybXVsYVN0ciA9ICJ+c20ubnMoUHNldWRvdGltZSwgZGYgPSAzKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWNlZE1vZGVsRm9ybXVsYVN0ciA9ICJ+MSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSAtIDIpCgojRmlsdGVyIGJhc2VkIG9uIEZEUgpQYWxsaWFsLmdlbmVzLmZpbHRlcmVkIDwtIFBhbGxpYWwuZ2VuZXMgICU+JSBmaWx0ZXIocXZhbCA8IDFlLTMpCmBgYAoKYGBge3IgIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUV9CkNSY2VsbHMuZ2VuZXMgPC0gZGlmZmVyZW50aWFsR2VuZVRlc3QoZ2JtX2Nkc1tJbnB1dC5nZW5lcywgQ1JjZWxsc10sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbE1vZGVsRm9ybXVsYVN0ciA9ICJ+c20ubnMoUHNldWRvdGltZSwgZGYgPSAzKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWNlZE1vZGVsRm9ybXVsYVN0ciA9ICJ+MSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSAtIDIpCgojRmlsdGVyIGJhc2VkIG9uIEZEUgpDUmNlbGxzLmdlbmVzLmZpbHRlcmVkIDwtIENSY2VsbHMuZ2VuZXMgICU+JSBmaWx0ZXIocXZhbCA8IDFlLTMpCmBgYAoKYGBge3J9CkNvbW1vbi5nZW5lcyA8LSBpbnRlcnNlY3QoUGFsbGlhbC5nZW5lcy5maWx0ZXJlZCRnZW5lX3Nob3J0X25hbWUsIENSY2VsbHMuZ2VuZXMuZmlsdGVyZWQkZ2VuZV9zaG9ydF9uYW1lKQpgYGAKCmBgYHtyLCBjYWNoZT1UUlVFfQojIFNtb290aCBnZW5lcyBleHByZXNzaW9uIGFsb25nIHRoZSB0d28gdHJhamVjdG9yaWVzCm5Qb2ludHMgPC0gMzAwCgpuZXdfZGF0YSA9IGxpc3QoKQpmb3IgKExpbmVhZ2UgaW4gdW5pcXVlKHBEYXRhKGdibV9jZHMpJExpbmVhZ2UpKXsKICBuZXdfZGF0YVtbbGVuZ3RoKG5ld19kYXRhKSArIDFdXSA9IGRhdGEuZnJhbWUoUHNldWRvdGltZSA9IHNlcShtaW4ocERhdGEoZ2JtX2NkcykkUHNldWRvdGltZSksIG1heChwRGF0YShnYm1fY2RzKSRQc2V1ZG90aW1lKSwgbGVuZ3RoLm91dCA9IG5Qb2ludHMpLCBMaW5lYWdlPUxpbmVhZ2UpCn0KCm5ld19kYXRhID0gZG8uY2FsbChyYmluZCwgbmV3X2RhdGEpCgojIFNtb290aCBnZW5lIGV4cHJlc3Npb24KY3VydmVfbWF0cml4IDwtIGdlblNtb290aEN1cnZlcyhnYm1fY2RzW2FzLmNoYXJhY3RlcihDb21tb24uZ2VuZXMpLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJlbmRfZm9ybXVsYSA9ICJ+c20ubnMoUHNldWRvdGltZSwgZGYgPSAzKSpMaW5lYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGl2ZV9leHByID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfZGF0YSA9IG5ld19kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcmVzPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSAtIDIpCmBgYAoKYGBge3J9CiMgRXh0cmFjdCBnZW5lcyB3aXRoIHBlcnNvbidzIGNvciA+IDAuNiBiZXR3ZWVuIHRoZSAyIHRyYWplY3RvcmllcwoKUGFsbGlhbC5zbW9vdGhlZCA8LSBzY2FsZSh0KGN1cnZlX21hdHJpeFssYygxOjMwMCldKSkgICNQYWxsaWFsIHBvaW50cwpDUi5zbW9vdGhlZCA8LSBzY2FsZSh0KGN1cnZlX21hdHJpeFssYygzMDE6NjAwKV0pKSAjQ1IgcG9pbnRzCgptYXQgPC0gY29yKFBhbGxpYWwuc21vb3RoZWQsIENSLnNtb290aGVkLCBtZXRob2QgPSAicGVhcnNvbiIpCgpHZW5lLkNvciA8LSBkaWFnKG1hdCkKaGlzdChHZW5lLkNvciwgYnJlYWtzID0gMTAwKQphYmxpbmUodj0wLjgsY29sPWMoImJsdWUiKSkKYGBgCmBgYHtyfQpQYW5OZXVyby5nZW5lcyA8LSBuYW1lcyhHZW5lLkNvcltHZW5lLkNvciA+IDAuOF0pCmBgYAoKYGBge3J9CiMgT3JkZXIgcm93cyB1c2luZyBzZXJpYXRpb24KZHN0IDwtIGFzLmRpc3QoKDEtY29yKHNjYWxlKHQoY3VydmVfbWF0cml4W1Bhbk5ldXJvLmdlbmVzLGMoNjAwOjMwMSldKSksIG1ldGhvZCA9ICJwZWFyc29uIikpKQpyb3cuc2VyIDwtIHNlcmlhdGUoZHN0LCBtZXRob2QgPSJNRFNfYW5nbGUiKSAjTURTX2FuZ2xlCmdlbmUub3JkZXIgPC0gUGFuTmV1cm8uZ2VuZXNbZ2V0X29yZGVyKHJvdy5zZXIpXQoKYW5uby5jb2xvcnMgPC0gbGlzdChsaW5lYWdlID0gYyhQYWxsaWFsPSIjMDI2YzlhIixDUj0iI2NjMzkxYiIpKQoKCnBoZWF0bWFwOjpwaGVhdG1hcChjdXJ2ZV9tYXRyaXhbcmV2KGdlbmUub3JkZXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoMTozMDAsIDMwMTo2MDApXSwgI0NSCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IEYsCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGRhdGEuZnJhbWUobGluZWFnZSA9IHJlcChjKCJQYWxsaWFsIiwiQ1IiKSwgZWFjaD0zMDApKSwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5uby5jb2xvcnMsCiAgICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBULAogICAgICAgICAgICAgICAgICAgZm9udHNpemVfcm93ID0gMiwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIHZpcmlkaXM6OnZpcmlkaXMoMTApLAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKC0yLjUsMi41LCBsZW5ndGgub3V0ID0gMTApLAogICAgICAgICAgICAgICAgICAgbWFpbiA9ICIiKQpgYGAKCmBgYHtyfQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIGMoIk5ldXJvLnRyYWplY3RvcmllcyIsICJnYm1fY2RzIiwgIkdlbmUuVHJlbmQiLCAiUGxvdC5HZW5lcy50cmVuZCIpXSkKZ2MoKQpgYGAKCiMjIFRlc3QgZWFjaCBnZW5lIHRyZW5kIG92ZXIgcHNldWRvdGltZSBzY29yZQoKIyMjIEZpbmQgZ2VuZXMgREUgYWxvbmcgcHNldWRvbWF0dXJhdGlvbiBheGlzCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFfQpwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmIDwtIGRpZmZlcmVudGlhbEdlbmVUZXN0KGdibV9jZHNbZkRhdGEoZ2JtX2NkcykkbnVtX2NlbGxzX2V4cHJlc3NlZCA+IDgwLF0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbE1vZGVsRm9ybXVsYVN0ciA9ICJ+c20ubnMoUHNldWRvdGltZSwgZGYgPSAzKSpMaW5lYWdlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y2VkTW9kZWxGb3JtdWxhU3RyID0gIn5zbS5ucyhQc2V1ZG90aW1lLCBkZiA9IDMpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpIC0gMikKCmBgYAoKYGBge3J9CiMgRmlsdGVyIGdlbmVzIGJhc2VkIG9uIEZEUgpwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmLmZpbHRlcmVkIDwtIHBzZXVkby5tYXR1cmF0aW9uLmRpZmYgJT4lIGZpbHRlcihxdmFsIDwgMWUtNDApCmBgYAoKIyMgRGlyZWN0aW9uIG9mIHRoZSBERUcgYnkgY2FsY3VsYXRpbmcgdGhlIGFyZWEgYmV0d2VlbiBjdXJ2ZXMgKEFCQykKCiMjIyBTbW9vdGggY29tbXVuIGdlbmVzIGFsb25nIHRoZSB0d28gdHJhamVjdG9yaWVzCgpgYGB7ciBTbW9vdGggZ2VuZSBleHByZXNzaW9uLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFfQojIENyZWF0ZSBhIG5ldyBwc2V1ZG8tRFYgdmVjdG9yIG9mIDMwMCBwb2ludHMKblBvaW50cyA8LSAzMDAKCm5ld19kYXRhID0gbGlzdCgpCmZvciAoTGluZWFnZSBpbiB1bmlxdWUocERhdGEoZ2JtX2NkcykkTGluZWFnZSkpewogIG5ld19kYXRhW1tsZW5ndGgobmV3X2RhdGEpICsgMV1dID0gZGF0YS5mcmFtZShQc2V1ZG90aW1lID0gc2VxKG1pbihwRGF0YShnYm1fY2RzKSRQc2V1ZG90aW1lKSwgbWF4KHBEYXRhKGdibV9jZHMpJFBzZXVkb3RpbWUpLCBsZW5ndGgub3V0ID0gblBvaW50cyksIExpbmVhZ2U9TGluZWFnZSkKfQoKbmV3X2RhdGEgPSBkby5jYWxsKHJiaW5kLCBuZXdfZGF0YSkKCiMgU21vb3RoIGdlbmUgZXhwcmVzc2lvbgpEaWZmLmN1cnZlX21hdHJpeCA8LSBnZW5TbW9vdGhDdXJ2ZXMoZ2JtX2Nkc1thcy5jaGFyYWN0ZXIocHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZCRnZW5lX3Nob3J0X25hbWUpLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJlbmRfZm9ybXVsYSA9ICJ+c20ubnMoUHNldWRvdGltZSwgZGYgPSAzKSpMaW5lYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWxhdGl2ZV9leHByID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfZGF0YSA9IG5ld19kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcmVzPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSAtIDIpCmBgYAoKIyMjIENvbXB1dGUgdGhlIEFCQyBmb3IgZWFjaCBnZW5lCgpgYGB7ciBDb21wdXRlIHRoZSBBQkN9CiMgRXh0cmFjdCBtYXRyaXggY29udGFpbmluZyBzbW9vdGhlZCBjdXJ2ZXMgZm9yIGVhY2ggbGluZWFnZXMKUGFsX2N1cnZlX21hdHJpeCA8LSBEaWZmLmN1cnZlX21hdHJpeFssIDE6blBvaW50c10gI1BhbGxpYWwgcG9pbnRzCkNSX2N1cnZlX21hdHJpeCA8LSBEaWZmLmN1cnZlX21hdHJpeFssIChuUG9pbnRzICsgMSk6KDIgKiBuUG9pbnRzKV0gI0NSIHBvaW50cwoKIyBEaXJlY3Rpb24gb2YgdGhlIGNvbXBhcmlzb24gOiBwb3N0aXZlIEFCQ3MgPD0+IFVwcmVndWxhdGVkIGluIENSIGxpbmVhZ2UKQUJDc19yZXMgPC0gQ1JfY3VydmVfbWF0cml4IC0gUGFsX2N1cnZlX21hdHJpeAoKIyBBdmVyYWdlIGxvZ0ZDIGJldHdlZW4gdGhlIDIgY3VydmVzCklMUl9yZXMgPC0gbG9nMihDUl9jdXJ2ZV9tYXRyaXgvIChQYWxfY3VydmVfbWF0cml4ICsgMC4xKSkgCiAgCkFCQ3NfcmVzIDwtIGFwcGx5KEFCQ3NfcmVzLCAxLCBmdW5jdGlvbih4LCBuUG9pbnRzKSB7CiAgICAgICAgICAgICAgICAgIGF2Z19kZWx0YV94IDwtICh4WzE6KG5Qb2ludHMgLSAxKV0gKyB4WzI6KG5Qb2ludHMpXSkvMgogICAgICAgICAgICAgICAgICBzdGVwIDwtICgxMDAvKG5Qb2ludHMgLSAxKSkKICAgICAgICAgICAgICAgICAgcmVzIDwtIHJvdW5kKHN1bShhdmdfZGVsdGFfeCAqIHN0ZXApLCAzKQogICAgICAgICAgICAgICAgICByZXR1cm4ocmVzKX0sCiAgICAgICAgICAgICAgICAgIG5Qb2ludHMgPSBuUG9pbnRzKSAjIENvbXB1dGUgdGhlIGFyZWEgYmVsb3cgdGhlIGN1cnZlCiAgCkFCQ3NfcmVzIDwtIGNiaW5kKEFCQ3NfcmVzLCBJTFJfcmVzWyxuY29sKElMUl9yZXMpXSkKY29sbmFtZXMoQUJDc19yZXMpPC0gYygiQUJDcyIsICJFbmRwb2ludF9JTFIiKQoKIyBJbXBvcnQgQUJDIHZhbHVlcyBpbnRvIHRoZSBERSB0ZXN0IHJlc3VsdHMgdGFibGUKcHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZCA8LSBjYmluZChwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmLmZpbHRlcmVkWywxOjRdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFCQ3NfcmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBzZXVkby5tYXR1cmF0aW9uLmRpZmYuZmlsdGVyZWRbLDU6Nl0pCmBgYAoKIyMgQ2FqYWwtUmV0eml1cyBjZWxscyBzcGVjaWZpYyB0cmFqZWN0b3J5IGFuYWx5c2lzCgpgYGB7cn0KIyBFeHRyYWN0IENhamFsLVJldHppdXMgZXhwcmVzc2VkIGdlbmVzCkNSLnJlcyA8LSBhcy5kYXRhLmZyYW1lKHBzZXVkby5tYXR1cmF0aW9uLmRpZmYuZmlsdGVyZWRbcHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZCRBQkNzID4gMCxdKQpDUi5nZW5lcyA8LSByb3cubmFtZXMoQ1IucmVzKQoKQ1JfY3VydmVfbWF0cml4IDwtIENSX2N1cnZlX21hdHJpeFtDUi5nZW5lcywgXQpgYGAKCiMjIyBHZW5lIGV4cHJlc3Npb24gcHJvZmlsZXMgYWxvbmcgdGhlIHRyYWplY3RvcnkKCmBgYHtyfQojIyBDbHVzdGVyIGdlbmUgYnkgZXhwcmVzc2lvbiBwcm9maWxlcwpQc2V1ZG90aW1lLmdlbmVzLmNsdXN0ZXJzIDwtIGNsdXN0ZXI6OnBhbShhcy5kaXN0KCgxIC0gY29yKE1hdHJpeDo6dChDUl9jdXJ2ZV9tYXRyaXgpLG1ldGhvZCA9ICJwZWFyc29uIikpKSwgaz0gNSkKCkNSLkdlbmUuZHluYW1pcXVlIDwtIGRhdGEuZnJhbWUoR2VuZT0gbmFtZXMoUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2F2ZXM9IFBzZXVkb3RpbWUuZ2VuZXMuY2x1c3RlcnMkY2x1c3RlcmluZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2VuZS5DbHVzdGVycyA9IFBzZXVkb3RpbWUuZ2VuZXMuY2x1c3RlcnMkY2x1c3RlcmluZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcS52YWwgPSBDUi5yZXMkcXZhbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQUJDcz0gQ1IucmVzJEFCQ3MKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgYXJyYW5nZShHZW5lLkNsdXN0ZXJzKQoKcm93Lm5hbWVzKENSLkdlbmUuZHluYW1pcXVlKSA8LSBDUi5HZW5lLmR5bmFtaXF1ZSRHZW5lCkNSLkdlbmUuZHluYW1pcXVlJEdlbmUuQ2x1c3RlcnMgPC0gcGFzdGUwKCJDbHVzdC4iLCBDUi5HZW5lLmR5bmFtaXF1ZSRHZW5lLkNsdXN0ZXJzKQpgYGAKCmBgYHtyIENSIGdlbmUgaGVhdG1hcCwgZmlnLmRpbT1jKDksIDUpfQojIE9yZGVyIHRoZSByb3dzIHVzaW5nIHNlcmlhdGlvbgpkc3QgPC0gYXMuZGlzdCgoMS1jb3Ioc2NhbGUodChDUl9jdXJ2ZV9tYXRyaXgpKSwgbWV0aG9kID0gInBlYXJzb24iKSkpCnJvdy5zZXIgPC0gc2VyaWF0aW9uOjpzZXJpYXRlKGRzdCwgbWV0aG9kID0iUjJFIikgIyJSMkUiICNUU1AgIyJHVyIgIkdXX3dhcmQiCmdlbmUub3JkZXIgPC0gcm93bmFtZXMoQ1JfY3VydmVfbWF0cml4W2dldF9vcmRlcihyb3cuc2VyKSxdKQoKIyBTZXQgYW5ub3RhdGlvbiBjb2xvcnMKcGFsIDwtIHdlc19wYWxldHRlKCJEYXJqZWVsaW5nMSIpCmFubm8uY29sb3JzIDwtIGxpc3QobGluZWFnZSA9IGMoUGFsbGlhbF9uZXVyb25zPSIjMDI2YzlhIiwgQ2FqYWxfUmV0eml1cz0iI2NjMzkxYiIpLAogICAgICAgICAgICAgICAgICAgIEdlbmUuQ2x1c3RlcnMgPSBjKENsdXN0LjEgPXBhbFsxXSAsIENsdXN0LjI9cGFsWzJdLCBDbHVzdC4zPXBhbFszXSwgQ2x1c3QuND1wYWxbNF0sIENsdXN0LjU9cGFsWzVdKSkKCgpwaGVhdG1hcDo6cGhlYXRtYXAoRGlmZi5jdXJ2ZV9tYXRyaXhbZ2VuZS5vcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKDMwMDoxLCNQYWwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAzMDE6NjAwKV0sICNDUgogICAgICAgICAgICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZHBseXI6OnNlbGVjdChHZW5lLkNsdXN0ZXJzKSwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gZGF0YS5mcmFtZShsaW5lYWdlID0gcmVwKGMoIlBhbGxpYWxfbmV1cm9ucyIsIkNhamFsX1JldHppdXMiKSwgZWFjaD0zMDApKSwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5uby5jb2xvcnMsCiAgICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgZm9udHNpemVfcm93ID0gOCwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIHZpcmlkaXM6OnZpcmlkaXMoOSksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTIuNSwyLjUsIGxlbmd0aC5vdXQgPSA5KSwKICAgICAgICAgICAgICAgICAgIG1haW4gPSAiIikKYGBgCgpXZSBtYW51YWxseSBjb3JyZWN0IHRoZSByZW9yZGVyaW5nIHNvIGdlbmVzIGFyZSBhbGlnbmVkIGZyb20gdG9wIGxlZnQgdG8gYm90dG9tIHJpZ3RoCgpgYGB7ciBmaWcuZGltPWMoOSwgNSl9CnBoZWF0bWFwOjpwaGVhdG1hcChEaWZmLmN1cnZlX21hdHJpeFtnZW5lLm9yZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoMzAwOjEsI1BhbCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDMwMTo2MDApXSwgI0NSCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IEYsCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IENSLkdlbmUuZHluYW1pcXVlICU+JSBkcGx5cjo6c2VsZWN0KEdlbmUuQ2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBkYXRhLmZyYW1lKGxpbmVhZ2UgPSByZXAoYygiUGFsbGlhbF9uZXVyb25zIiwiQ2FqYWxfUmV0eml1cyIpLCBlYWNoPTMwMCkpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vLmNvbG9ycywKICAgICAgICAgICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3cgPSA4LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAgdmlyaWRpczo6dmlyaWRpcyg5KSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgtMi41LDIuNSwgbGVuZ3RoLm91dCA9IDkpLAogICAgICAgICAgICAgICAgICAgbWFpbiA9ICIiKQpgYGAKCgpgYGB7ciBmaWcuZGltPWMoOSwgNSl9CmFubm8uY29sb3JzIDwtIGxpc3QoQ2VsbC5zdGF0ZSA9IGMoQ3ljbGluZ19SRz0iIzA0NmM5YSIsIERpZmZlcmVudGlhdGluZ19jZWxscz0iI2ViY2IyZSIpLAogICAgICAgICAgICAgICAgICAgIEdlbmUuQ2x1c3RlcnMgPSBjKENsdXN0LjEgPXBhbFsxXSAsIENsdXN0LjI9cGFsWzJdLCBDbHVzdC4zPXBhbFszXSwgQ2x1c3QuND1wYWxbNF0sIENsdXN0LjU9cGFsWzVdKSkKCmNvbC5hbm5vIDwtIGRhdGEuZnJhbWUoQ2VsbC5zdGF0ZSA9IHJlcChjKCJDeWNsaW5nX1JHIiwiRGlmZmVyZW50aWF0aW5nX2NlbGxzIiksIGMoMTAwLDIwMCkpKQpyb3duYW1lcyhjb2wuYW5ubykgPC0gMzAxOjYwMAoKcGhlYXRtYXA6OnBoZWF0bWFwKENSX2N1cnZlX21hdHJpeFtnZW5lLm9yZGVyLF0sCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IEYsCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IENSLkdlbmUuZHluYW1pcXVlICU+JSBkcGx5cjo6c2VsZWN0KEdlbmUuQ2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBjb2wuYW5ubywKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5uby5jb2xvcnMsCiAgICAgICAgICAgICAgICAgICBnYXBzX2NvbCA9IDEwMCwKICAgICAgICAgICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3cgPSA4LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAgdmlyaWRpczo6dmlyaWRpcyg5KSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgtMi41LDIuNSwgbGVuZ3RoLm91dCA9IDkpLAogICAgICAgICAgICAgICAgICAgbWFpbiA9ICIiKQoKYGBgCgpgYGB7cn0KZGlmZi5zdGF0ZSA8LSBOZXVyby50cmFqZWN0b3JpZXNAbWV0YS5kYXRhICU+JQogICAgICAgICAgICAgIGZpbHRlcihMaW5lYWdlID09ICAiQ2FqYWwtUmV0eml1c19uZXVyb25zIikgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KCJBUF9zaWduYXR1cmUxIiwgIkJQX3NpZ25hdHVyZTEiLCAiRU5fc2lnbmF0dXJlMSIsICJMTl9zaWduYXR1cmUxIiwgIlBzZXVkb3RpbWUiKQoKQVAubG9lc3MgPC0gbG9lc3MoQVBfc2lnbmF0dXJlMSB+IFBzZXVkb3RpbWUsIGRpZmYuc3RhdGUpCkFQLnNtb290aCA8LSBwcmVkaWN0KEFQLmxvZXNzLAogICAgICAgICAgICAgICAgICAgICBzZXEoMC4wMSwxLjUsIGxlbmd0aC5vdXQ9IDMwMCkpCgpCUC5sb2VzcyA8LSBsb2VzcyhCUF9zaWduYXR1cmUxIH4gUHNldWRvdGltZSwgZGlmZi5zdGF0ZSkKQlAuc21vb3RoIDwtIHByZWRpY3QoQlAubG9lc3MsCiAgICAgICAgICAgICAgICAgICAgIHNlcSgwLjAxLDEuNSwgbGVuZ3RoLm91dD0gMzAwKSkKCkVOLmxvZXNzIDwtIGxvZXNzKEVOX3NpZ25hdHVyZTEgfiBQc2V1ZG90aW1lLCBkaWZmLnN0YXRlKQpFTi5zbW9vdGggPC0gcHJlZGljdChFTi5sb2VzcywKICAgICAgICAgICAgICAgICAgICAgc2VxKDAuMDEsMS41LCBsZW5ndGgub3V0PSAzMDApKQoKTE4ubG9lc3MgPC0gbG9lc3MoTE5fc2lnbmF0dXJlMSB+IFBzZXVkb3RpbWUsIGRpZmYuc3RhdGUpCkxOLnNtb290aCA8LSBwcmVkaWN0KExOLmxvZXNzLAogICAgICAgICAgICAgICAgICAgICBzZXEoMC4wMSwxLjUsIGxlbmd0aC5vdXQ9IDMwMCkpCgpTbW9vdGhlZC5zdGF0ZXMgPC0gY2JpbmQoQVAuc21vb3RoLCBCUC5zbW9vdGgsIEVOLnNtb290aCwgTE4uc21vb3RoKQpgYGAKCmBgYHtyLCBmaWcuc2hvdz0iaGlkZSJ9CmhlYXRtYXAuc3RhdGVzIDwtIHBoZWF0bWFwOjpwaGVhdG1hcChhcy5kYXRhLmZyYW1lKHQoU21vb3RoZWQuc3RhdGVzKSksCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IEYsCiAgICAgICAgICAgICAgICAgICBnYXBzX2NvbCA9IDEwMCwKICAgICAgICAgICAgICAgICAgIGdhcHNfcm93ID0gYygxLDIsMyksCiAgICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgZm9udHNpemVfcm93ID0gOCwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIHJldihjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwobj0gOCwgbmFtZSA9ICJSZEJ1IikpKDEwMCkpLAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKC0xLDEsIGxlbmd0aC5vdXQgPSAxMDApLAogICAgICAgICAgICAgICAgICAgbWFpbiA9ICIiKQoKaGVhdG1hcC5nZW5lIDwtIHBoZWF0bWFwOjpwaGVhdG1hcChDUl9jdXJ2ZV9tYXRyaXhbZ2VuZS5vcmRlcixdLAogICAgICAgICAgICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGLAogICAgICAgICAgICAgICAgICAgZ2Fwc19jb2wgPSAxMDAsCiAgICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgZm9udHNpemVfcm93ID0gOCwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIHZpcmlkaXM6OnZpcmlkaXMoOSksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTIuNSwyLjUsIGxlbmd0aC5vdXQgPSA5KSwKICAgICAgICAgICAgICAgICAgIG1haW4gPSAiIikKYGBgCgoKYGBge3J9CmNvd3Bsb3Q6OnBsb3RfZ3JpZChoZWF0bWFwLnN0YXRlcyRndGFibGUsIGhlYXRtYXAuZ2VuZSRndGFibGUsCiAgICAgICAgICAgICAgICAgICBuY29sID0gMSwKICAgICAgICAgICAgICAgICAgIGFsaWduID0gInYiLAogICAgICAgICAgICAgICAgICAgcmVsX2hlaWdodHMgPSBjKDEsMyksCiAgICAgICAgICAgICAgICAgICBncmVlZHkgPSBUKQpgYGAKCiMjIyBHZW5lIGNsdXN0ZXIgdHJlbmQKCmBgYHtyIGZpZy5kaW09Yyg5LDYpLCBjYWNoZT1UUlVFfQpzb3VyY2UoIi4uL0Z1bmN0aW9ucy9mdW5jdGlvbnNfR2VuZUNsdXN0ZXJUcmVuZC5SIikKClBsb3QuY2x1c3QudHJlbmRzKE5ldXJvLnRyYWplY3RvcmllcywKICAgICAgICAgICAgICAgICAgIExpbmVhZ2UgPSAiQ2FqYWwtUmV0eml1c19uZXVyb25zIiwKICAgICAgICAgICAgICAgICAgIFdoaWNoLmNsdXN0ZXIgPSAxOjUsCiAgICAgICAgICAgICAgICAgICBjbHVzdC5saXN0ID0gUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nLAogICAgICAgICAgICAgICAgICAgU21vb3RoLm1ldGhvZCA9ICJnYW0iKQpgYGAKCiMjIyBHTyB0ZXJtIGVucmljaG1lbnQgaW4gZ2VuZSBjbHVzdGVycyB1c2luZyBncHJvZmlsZXIyCgpgYGB7cn0KQ1IuZ29zdHJlcyA8LSBnb3N0KHF1ZXJ5ID0gbGlzdCgiQ2x1c3QuMSIgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZmlsdGVyKEdlbmUuQ2x1c3RlcnMgPT0gIkNsdXN0LjEiKSAlPiUgcHVsbChHZW5lKSAlPiUgYXMuY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNsdXN0LjIiID0gQ1IuR2VuZS5keW5hbWlxdWUgJT4lIGZpbHRlcihHZW5lLkNsdXN0ZXJzID09ICJDbHVzdC4yIikgJT4lIHB1bGwoR2VuZSkgJT4lIGFzLmNoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDbHVzdC4zIiA9IENSLkdlbmUuZHluYW1pcXVlICU+JSBmaWx0ZXIoR2VuZS5DbHVzdGVycyA9PSAiQ2x1c3QuMyIpICU+JSBwdWxsKEdlbmUpICU+JSBhcy5jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2x1c3QuNCIgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZmlsdGVyKEdlbmUuQ2x1c3RlcnMgPT0gIkNsdXN0LjQiKSAlPiUgcHVsbChHZW5lKSAlPiUgYXMuY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNsdXN0LjUiID0gQ1IuR2VuZS5keW5hbWlxdWUgJT4lIGZpbHRlcihHZW5lLkNsdXN0ZXJzID09ICJDbHVzdC41IikgJT4lIHB1bGwoR2VuZSkgJT4lIGFzLmNoYXJhY3RlcigpKSwKICAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gIm1tdXNjdWx1cyIsIG9yZGVyZWRfcXVlcnkgPSBGLCAKICAgICAgICAgICAgICAgIG11bHRpX3F1ZXJ5ID0gRiwgc2lnbmlmaWNhbnQgPSBULCBleGNsdWRlX2llYSA9IFQsIAogICAgICAgICAgICAgICAgbWVhc3VyZV91bmRlcnJlcHJlc2VudGF0aW9uID0gRiwgZXZjb2RlcyA9IFQsIAogICAgICAgICAgICAgICAgdXNlcl90aHJlc2hvbGQgPSAwLjA1LCBjb3JyZWN0aW9uX21ldGhvZCA9ICJmZHIiLCAKICAgICAgICAgICAgICAgIGRvbWFpbl9zY29wZSA9ICJhbm5vdGF0ZWQiLCBjdXN0b21fYmcgPSBOVUxMLCAKICAgICAgICAgICAgICAgIG51bWVyaWNfbnMgPSAiIiwgc291cmNlcyA9IGMoIkdPOk1GIiwgIkdPOkJQIiksIGFzX3Nob3J0X2xpbmsgPSBGKQoKd3JpdGUudGFibGUoYXBwbHkoQ1IuZ29zdHJlcyRyZXN1bHQsMixhcy5jaGFyYWN0ZXIpLAogICAgICAgICAgICAiS09fQ1JfR09fcmVzLWJ5LXdhdmVzLmNzdiIsIHNlcCA9ICI7IiwgcXVvdGUgPSBGLCByb3cubmFtZXMgPSBGKQpgYGAKCmBgYHtyfQpETkFfZGFtYWdlX0dPdGVybSA8LSBDUi5nb3N0cmVzJHJlc3VsdFtDUi5nb3N0cmVzJHJlc3VsdCR0ZXJtX2lkICVpbiUgYygiR086MDAwODYzMCIsICJHTzowMDMwMzMwIiwgIkdPOjAwMzE1NzEiLCAiR086MDAwNjk3NCIsICJHTzowMDA2OTc3IiwiR086MDAzMzU1NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHTzowMDQ0NzczIiwgIkdPOjAwNDI3NzEiLCAiR086MDA0Mjc3MCIsICJHTzoyMDAxMDIxIiwgIkdPOjE5MDIyMjkiKSxdCgpETkFfZGFtYWdlX0dPdGVybVssYyg5LDEsMiwzLDUsNiw3LDExKV0KYGBgCgojIyMgR28gdGVybSBvbiBhbGwgQ1IgZ2VuZXMKCmBgYHtyfQpDUi5nb3N0cmVzIDwtIGdvc3QocXVlcnkgPSBhcy5jaGFyYWN0ZXIoQ1IuR2VuZS5keW5hbWlxdWUkR2VuZSksCiAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJtbXVzY3VsdXMiLCBvcmRlcmVkX3F1ZXJ5ID0gRiwgCiAgICAgICAgICAgICAgICBtdWx0aV9xdWVyeSA9IEYsIHNpZ25pZmljYW50ID0gVCwgZXhjbHVkZV9pZWEgPSBULCAKICAgICAgICAgICAgICAgIG1lYXN1cmVfdW5kZXJyZXByZXNlbnRhdGlvbiA9IEYsIGV2Y29kZXMgPSBULCAKICAgICAgICAgICAgICAgIHVzZXJfdGhyZXNob2xkID0gMC4wNSwgY29ycmVjdGlvbl9tZXRob2QgPSAiZmRyIiwgCiAgICAgICAgICAgICAgICBkb21haW5fc2NvcGUgPSAiYW5ub3RhdGVkIiwgY3VzdG9tX2JnID0gTlVMTCwgCiAgICAgICAgICAgICAgICBudW1lcmljX25zID0gIiIsIHNvdXJjZXMgPSBjKCJHTzpNRiIsICJHTzpCUCIpLCBhc19zaG9ydF9saW5rID0gRikKCndyaXRlLnRhYmxlKGFwcGx5KENSLmdvc3RyZXMkcmVzdWx0LDIsYXMuY2hhcmFjdGVyKSwKICAgICAgICAgICAgIktPQ1JfR09fcmVzLmNzdiIsIHNlcCA9ICI7IiwgcXVvdGUgPSBGLCByb3cubmFtZXMgPSBGKQpgYGAKCgpgYGB7cn0KRE5BX2RhbWFnZV9HT3Rlcm0gPC0gQ1IuZ29zdHJlcyRyZXN1bHRbQ1IuZ29zdHJlcyRyZXN1bHQkdGVybV9pZCAlaW4lIGMoIkdPOjAwMDg2MzAiLCAiR086MDAzMDMzMCIsICJHTzowMDMxNTcxIiwgIkdPOjAwMDY5NzQiLCAiR086MDAwNjk3NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHTzowMDQ0NzczIiwgIkdPOjAwNDI3NzEiLCAiR086MDA0Mjc3MCIsICJHTzoyMDAxMDIxIiwgIkdPOjE5MDIyMjkiKSxdCgpETkFfZGFtYWdlX0dPdGVybVssYygxLDIsMyw1LDYsNywxMSldCmBgYAoKIyMjIEludGVyc2VjdGlvbiB3aXRoIENoUCBkeW5hbWljYWx5IGV4cHJlc3NlZCBnZW5lcwoKYGBge3J9CkNoUF9keW5hbWljX2dlbmVzIDwtIHJlYWQudGFibGUoIi4uL0Nob3JvaWRQbGV4dXNfdHJhamVjdG9yeS9DaFAuR2VuZS5keW5hbWlxdWUuY3N2Iiwgc2VwID0gIjsiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxKQpgYGAKCgpgYGB7cn0KQ1JfQ2hQX2NvbW1vbl9nZW5lcyA8LSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZmlsdGVyKEdlbmUgJWluJSBDaFBfZHluYW1pY19nZW5lcyRHZW5lKQpgYGAKCmBgYHtyfQpnZW5lLm9yZGVyMiA8LSBnZW5lLm9yZGVyW2dlbmUub3JkZXIgJWluJSBDUl9DaFBfY29tbW9uX2dlbmVzJEdlbmVdCgpwaGVhdG1hcDo6cGhlYXRtYXAoQ1JfY3VydmVfbWF0cml4W2dlbmUub3JkZXIyLF0sCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IEYsCiAgICAgICAgICAgICAgICAgICAjYW5ub3RhdGlvbl9yb3cgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZHBseXI6OnNlbGVjdChHZW5lLkNsdXN0ZXJzKSwKICAgICAgICAgICAgICAgICAgICNhbm5vdGF0aW9uX2NvbCA9IGNvbC5hbm5vLAogICAgICAgICAgICAgICAgICAgI2Fubm90YXRpb25fY29sb3JzID0gYW5uby5jb2xvcnMsCiAgICAgICAgICAgICAgICAgICBnYXBzX2NvbCA9IDEwMCwKICAgICAgICAgICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3cgPSA4LAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAgdmlyaWRpczo6dmlyaWRpcyg5KSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgtMi41LDIuNSwgbGVuZ3RoLm91dCA9IDkpLAogICAgICAgICAgICAgICAgICAgbWFpbiA9ICIiKQpgYGAKYGBge3J9CkNSX0NoUF9jb21tb24uZ29zdHJlcyA8LSBnb3N0KHF1ZXJ5ID0gbGlzdCgiQ2x1c3QuMSIgPSBDUl9DaFBfY29tbW9uX2dlbmVzICU+JSBmaWx0ZXIoR2VuZS5DbHVzdGVycyA9PSAiQ2x1c3QuMSIpICU+JSBwdWxsKEdlbmUpICU+JSBhcy5jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2x1c3QuMiIgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZmlsdGVyKEdlbmUuQ2x1c3RlcnMgPT0gIkNsdXN0LjIiKSAlPiUgcHVsbChHZW5lKSAlPiUgYXMuY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNsdXN0LjMiID0gQ1IuR2VuZS5keW5hbWlxdWUgJT4lIGZpbHRlcihHZW5lLkNsdXN0ZXJzID09ICJDbHVzdC4zIikgJT4lIHB1bGwoR2VuZSkgJT4lIGFzLmNoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDbHVzdC40IiA9IENSLkdlbmUuZHluYW1pcXVlICU+JSBmaWx0ZXIoR2VuZS5DbHVzdGVycyA9PSAiQ2x1c3QuNCIpICU+JSBwdWxsKEdlbmUpICU+JSBhcy5jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2x1c3QuNSIgPSBDUi5HZW5lLmR5bmFtaXF1ZSAlPiUgZmlsdGVyKEdlbmUuQ2x1c3RlcnMgPT0gIkNsdXN0LjUiKSAlPiUgcHVsbChHZW5lKSAlPiUgYXMuY2hhcmFjdGVyKCkpLAogICAgICAgICAgICAgICAgb3JnYW5pc20gPSAibW11c2N1bHVzIiwgb3JkZXJlZF9xdWVyeSA9IEYsIAogICAgICAgICAgICAgICAgbXVsdGlfcXVlcnkgPSBGLCBzaWduaWZpY2FudCA9IFQsIGV4Y2x1ZGVfaWVhID0gVCwgCiAgICAgICAgICAgICAgICBtZWFzdXJlX3VuZGVycmVwcmVzZW50YXRpb24gPSBGLCBldmNvZGVzID0gVCwgCiAgICAgICAgICAgICAgICB1c2VyX3RocmVzaG9sZCA9IDAuMDUsIGNvcnJlY3Rpb25fbWV0aG9kID0gImZkciIsIAogICAgICAgICAgICAgICAgZG9tYWluX3Njb3BlID0gImFubm90YXRlZCIsIGN1c3RvbV9iZyA9IE5VTEwsIAogICAgICAgICAgICAgICAgbnVtZXJpY19ucyA9ICIiLCBzb3VyY2VzID0gYygiR086TUYiLCAiR086QlAiKSwgYXNfc2hvcnRfbGluayA9IEYpCgp3cml0ZS50YWJsZShhcHBseShDUl9DaFBfY29tbW9uLmdvc3RyZXMkcmVzdWx0LDIsYXMuY2hhcmFjdGVyKSwKICAgICAgICAgICAgIktPX0NSX0NoUF9jb21tb25fR09fcmVzLWJ5LXdhdmVzLmNzdiIsIHNlcCA9ICI7IiwgcXVvdGUgPSBGLCByb3cubmFtZXMgPSBGKQpgYGAKCmBgYHtyfQpETkFfZGFtYWdlX0dPdGVybSA8LSBDUl9DaFBfY29tbW9uLmdvc3RyZXMkcmVzdWx0W0NSX0NoUF9jb21tb24uZ29zdHJlcyRyZXN1bHQkdGVybV9pZCAlaW4lIGMoIkdPOjAwMDg2MzAiLCAiR086MDAzMDMzMCIsICJHTzowMDMxNTcxIiwgIkdPOjAwMDY5NzQiLCAiR086MDAwNjk3NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHTzowMDQ0NzczIiwgIkdPOjAwNDI3NzEiLCAiR086MDA0Mjc3MCIsICJHTzoyMDAxMDIxIiwgIkdPOjE5MDIyMjkiKSxdCgpETkFfZGFtYWdlX0dPdGVybVssYygxLDIsMyw1LDYsNywxMSldCmBgYAoKYGBge3J9CkNSX0NoUF9jb21tb24uZ29zdHJlcyA8LSBnb3N0KHF1ZXJ5ID0gYXMuY2hhcmFjdGVyKENSX0NoUF9jb21tb25fZ2VuZXMkR2VuZSksCiAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJtbXVzY3VsdXMiLCBvcmRlcmVkX3F1ZXJ5ID0gRiwgCiAgICAgICAgICAgICAgICBtdWx0aV9xdWVyeSA9IEYsIHNpZ25pZmljYW50ID0gVCwgZXhjbHVkZV9pZWEgPSBULCAKICAgICAgICAgICAgICAgIG1lYXN1cmVfdW5kZXJyZXByZXNlbnRhdGlvbiA9IEYsIGV2Y29kZXMgPSBULCAKICAgICAgICAgICAgICAgIHVzZXJfdGhyZXNob2xkID0gMC4wNSwgY29ycmVjdGlvbl9tZXRob2QgPSAiZmRyIiwgCiAgICAgICAgICAgICAgICBkb21haW5fc2NvcGUgPSAiYW5ub3RhdGVkIiwgY3VzdG9tX2JnID0gTlVMTCwgCiAgICAgICAgICAgICAgICBudW1lcmljX25zID0gIiIsIHNvdXJjZXMgPSBjKCJHTzpNRiIsICJHTzpCUCIpLCBhc19zaG9ydF9saW5rID0gRikKCndyaXRlLnRhYmxlKGFwcGx5KENSX0NoUF9jb21tb24uZ29zdHJlcyRyZXN1bHQsMixhcy5jaGFyYWN0ZXIpLAogICAgICAgICAgICAiS09fQ1JfQ2hQX2NvbW1vbl9HT19yZXNfYWxsLmNzdiIsIHNlcCA9ICI7IiwgcXVvdGUgPSBGLCByb3cubmFtZXMgPSBGKQpgYGAKCgojIyBQYWxsaWFsIG5ldXJvbnMgdHJhamVjdG9yeSBhbmFseXNpcwoKYGBge3J9CiMgRXh0cmFjdCBQYWxsaWFsIG5ldXJvbnMgdHJhamVjdG9yeSBnZW5lcwpQYWwucmVzIDwtIGFzLmRhdGEuZnJhbWUocHNldWRvLm1hdHVyYXRpb24uZGlmZi5maWx0ZXJlZFtwc2V1ZG8ubWF0dXJhdGlvbi5kaWZmLmZpbHRlcmVkJEFCQ3MgPCAwLF0pClBhbC5nZW5lcyA8LSByb3cubmFtZXMoUGFsLnJlcykKClBhbF9jdXJ2ZV9tYXRyaXggPC0gUGFsX2N1cnZlX21hdHJpeFtQYWwuZ2VuZXMsIF0KYGBgCgojIyMgR2VuZSBleHByZXNzaW9uIHByb2ZpbGVzIGFsb25nIHRoZSB0cmFqZWN0b3J5CgpgYGB7cn0KIyMgQ2x1c3RlciBnZW5lIGJ5IGV4cHJlc3Npb24gcHJvZmlsZXMKUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyA8LSBjbHVzdGVyOjpwYW0oYXMuZGlzdCgoMSAtIGNvcihNYXRyaXg6OnQoUGFsX2N1cnZlX21hdHJpeCksbWV0aG9kID0gInBlYXJzb24iKSkpLCBrPSA1KQoKUGFsLkdlbmUuZHluYW1pcXVlIDwtIGRhdGEuZnJhbWUoR2VuZT0gbmFtZXMoUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYXZlcz0gUHNldWRvdGltZS5nZW5lcy5jbHVzdGVycyRjbHVzdGVyaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdlbmUuQ2x1c3RlcnMgPSBQc2V1ZG90aW1lLmdlbmVzLmNsdXN0ZXJzJGNsdXN0ZXJpbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcS52YWwgPSBQYWwucmVzJHB2YWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQUJDcz0gUGFsLnJlcyRBQkNzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgYXJyYW5nZShHZW5lLkNsdXN0ZXJzKQoKcm93Lm5hbWVzKFBhbC5HZW5lLmR5bmFtaXF1ZSkgPC0gUGFsLkdlbmUuZHluYW1pcXVlJEdlbmUKUGFsLkdlbmUuZHluYW1pcXVlJEdlbmUuQ2x1c3RlcnMgPC0gcGFzdGUwKCJDbHVzdC4iLCBQYWwuR2VuZS5keW5hbWlxdWUkR2VuZS5DbHVzdGVycykKYGBgCgpgYGB7ciBmaWcuZGltPWMoOSwgNSl9CiMgT3JkZXIgdGhlIHJvd3MgdXNpbmcgc2VyaWF0aW9uCmRzdCA8LSBhcy5kaXN0KCgxLWNvcihzY2FsZSh0KFBhbF9jdXJ2ZV9tYXRyaXgpKSwgbWV0aG9kID0gInBlYXJzb24iKSkpCnJvdy5zZXIgPC0gc2VyaWF0aW9uOjpzZXJpYXRlKGRzdCwgbWV0aG9kID0iUjJFIikgIyJSMkUiICNUU1AgIyJHVyIgIkdXX3dhcmQiCmdlbmUub3JkZXIgPC0gcm93bmFtZXMoUGFsX2N1cnZlX21hdHJpeFtnZXRfb3JkZXIocm93LnNlciksXSkKCiMgU2V0IGFubm90YXRpb24gY29sb3JzCnBhbCA8LSB3ZXNfcGFsZXR0ZSgiRGFyamVlbGluZzEiKQphbm5vLmNvbG9ycyA8LSBsaXN0KGxpbmVhZ2UgPSBjKFBhbGxpYWxfbmV1cm9ucz0iIzAyNmM5YSIsIENhamFsX1JldHppdXM9IiNjYzM5MWIiKSwKICAgICAgICAgICAgICAgICAgICBHZW5lLkNsdXN0ZXJzID0gYyhDbHVzdC4xID1wYWxbMV0gLCBDbHVzdC4yPXBhbFsyXSwgQ2x1c3QuMz1wYWxbM10sIENsdXN0LjQ9cGFsWzRdLCBDbHVzdC41PXBhbFs1XSkpCgoKcGhlYXRtYXA6OnBoZWF0bWFwKERpZmYuY3VydmVfbWF0cml4W2dlbmUub3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygzMDA6MSwjUGFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAzMDE6NjAwKV0sICNDUgogICAgICAgICAgICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBQYWwuR2VuZS5keW5hbWlxdWUgJT4lIGRwbHlyOjpzZWxlY3QoR2VuZS5DbHVzdGVycyksCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGRhdGEuZnJhbWUobGluZWFnZSA9IHJlcChjKCJQYWxsaWFsX25ldXJvbnMiLCJDYWphbF9SZXR6aXVzIiksIGVhY2g9MzAwKSksCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm8uY29sb3JzLAogICAgICAgICAgICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBzaG93X3Jvd25hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIGZvbnRzaXplX3JvdyA9IDgsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICB2aXJpZGlzOjp2aXJpZGlzKDkpLAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKC0yLjUsMi41LCBsZW5ndGgub3V0ID0gOSksCiAgICAgICAgICAgICAgICAgICBtYWluID0gIiIpCmBgYAoKV2UgbWFudWFsbHkgY29ycmVjdCB0aGUgcmVvcmRlcmluZyBzbyBnZW5lcyBhcmUgYWxpZ25lZCBmcm9tIHRvcCByaWdodCB0byBib3R0b20gbGVmdAoKYGBge3IgZmlnLmRpbT1jKDksIDUpfQpwaGVhdG1hcDo6cGhlYXRtYXAoRGlmZi5jdXJ2ZV9tYXRyaXhbZ2VuZS5vcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKDMwMDoxLCNQYWwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDMwMTo2MDApXSwgI0NSCiAgICAgICAgICAgICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IEYsCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IFBhbC5HZW5lLmR5bmFtaXF1ZSAlPiUgZHBseXI6OnNlbGVjdChHZW5lLkNsdXN0ZXJzKSwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gZGF0YS5mcmFtZShsaW5lYWdlID0gcmVwKGMoIlBhbGxpYWxfbmV1cm9ucyIsIkNhamFsX1JldHppdXMiKSwgZWFjaD0zMDApKSwKICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5uby5jb2xvcnMsCiAgICAgICAgICAgICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgZm9udHNpemVfcm93ID0gOCwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIHZpcmlkaXM6OnZpcmlkaXMoOSksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTIuNSwyLjUsIGxlbmd0aC5vdXQgPSA5KSwKICAgICAgICAgICAgICAgICAgIG1haW4gPSAiIikKYGBgCgpgYGB7ciBmaWcuZGltPWMoOSwgNSl9CmFubm8uY29sb3JzIDwtIGxpc3QoQ2VsbC5zdGF0ZSA9IGMoQ3ljbGluZ19SRz0iIzA0NmM5YSIsIERpZmZlcmVudGlhdGluZ19jZWxscz0iI2ViY2IyZSIpLAogICAgICAgICAgICAgICAgICAgIEdlbmUuQ2x1c3RlcnMgPSBjKENsdXN0LjEgPXBhbFsxXSAsIENsdXN0LjI9cGFsWzJdLCBDbHVzdC4zPXBhbFszXSwgQ2x1c3QuND1wYWxbNF0sIENsdXN0LjU9cGFsWzVdKSkKCmNvbC5hbm5vIDwtIGRhdGEuZnJhbWUoQ2VsbC5zdGF0ZSA9IHJlcChjKCJEaWZmZXJlbnRpYXRpbmdfY2VsbHMiLCJDeWNsaW5nX1JHIiksIGMoMjAwLDEwMCkpKQpyb3duYW1lcyhjb2wuYW5ubykgPC0gMzAwOjEKCnBoZWF0bWFwOjpwaGVhdG1hcChQYWxfY3VydmVfbWF0cml4W2dlbmUub3JkZXIsMzAwOjFdLAogICAgICAgICAgICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBQYWwuR2VuZS5keW5hbWlxdWUgJT4lIGRwbHlyOjpzZWxlY3QoR2VuZS5DbHVzdGVycyksCiAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGNvbC5hbm5vLAogICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vLmNvbG9ycywKICAgICAgICAgICAgICAgICAgIGdhcHNfY29sID0gMjAwLAogICAgICAgICAgICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICBzaG93X3Jvd25hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgIGZvbnRzaXplX3JvdyA9IDgsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICB2aXJpZGlzOjp2aXJpZGlzKDkpLAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKC0yLjUsMi41LCBsZW5ndGgub3V0ID0gOSksCiAgICAgICAgICAgICAgICAgICBtYWluID0gIiIpCgpgYGAKCiMjIyBHZW5lIGNsdXN0ZXIgdHJlbmQKCmBgYHtyIGZpZy5kaW09Yyg5LDYpLCBjYWNoZT1UUlVFfQpQbG90LmNsdXN0LnRyZW5kcyhOZXVyby50cmFqZWN0b3JpZXMsCiAgICAgICAgICAgICAgICAgICBMaW5lYWdlID0gIlBhbGxpYWxfbmV1cm9ucyIsCiAgICAgICAgICAgICAgICAgICBXaGljaC5jbHVzdGVyID0gMTo1LAogICAgICAgICAgICAgICAgICAgY2x1c3QubGlzdCA9IFBzZXVkb3RpbWUuZ2VuZXMuY2x1c3RlcnMkY2x1c3RlcmluZywKICAgICAgICAgICAgICAgICAgIFNtb290aC5tZXRob2QgPSAiZ2FtIikKYGBgCgpgYGB7cn0KUGFsLmdvc3RyZXMgPC0gZ29zdChxdWVyeSA9IGFzLmNoYXJhY3RlcihQYWwuR2VuZS5keW5hbWlxdWUkR2VuZSksCiAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJtbXVzY3VsdXMiLCBvcmRlcmVkX3F1ZXJ5ID0gRiwgCiAgICAgICAgICAgICAgICBtdWx0aV9xdWVyeSA9IEYsIHNpZ25pZmljYW50ID0gVCwgZXhjbHVkZV9pZWEgPSBULCAKICAgICAgICAgICAgICAgIG1lYXN1cmVfdW5kZXJyZXByZXNlbnRhdGlvbiA9IEYsIGV2Y29kZXMgPSBULCAKICAgICAgICAgICAgICAgIHVzZXJfdGhyZXNob2xkID0gMC4wNSwgY29ycmVjdGlvbl9tZXRob2QgPSAiZmRyIiwgCiAgICAgICAgICAgICAgICBkb21haW5fc2NvcGUgPSAiYW5ub3RhdGVkIiwgY3VzdG9tX2JnID0gTlVMTCwgCiAgICAgICAgICAgICAgICBudW1lcmljX25zID0gIiIsIHNvdXJjZXMgPSBjKCJHTzpNRiIsICJHTzpCUCIpLCBhc19zaG9ydF9saW5rID0gRikKCndyaXRlLnRhYmxlKGFwcGx5KFBhbC5nb3N0cmVzJHJlc3VsdCwgMiwgYXMuY2hhcmFjdGVyKSwKICAgICAgICAgICAgIktPX1BhbC5nb3N0cmVzLmNzdiIsIHNlcCA9ICI7IiwgcXVvdGUgPSBGLCByb3cubmFtZXMgPSBGKQpgYGAKCgpgYGB7cn0KRE5BX2RhbWFnZV9HT3Rlcm0gPC0gUGFsLmdvc3RyZXMkcmVzdWx0W1BhbC5nb3N0cmVzJHJlc3VsdCR0ZXJtX2lkICVpbiUgYygiR086MDAwODYzMCIsICJHTzowMDMwMzMwIiwgIkdPOjAwMzE1NzEiLCAiR086MDAwNjk3NCIsICJHTzowMDA2OTc3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdPOjAwNDQ3NzMiLCAiR086MDA0Mjc3MSIsICJHTzowMDQyNzcwIiwgIkdPOjIwMDEwMjEiLCAiR086MTkwMjIyOSIpLF0KCkROQV9kYW1hZ2VfR090ZXJtWyxjKDEsMiwzLDUsNiw3LDExKV0KYGBgCgoKIyBTZXNzaW9uIEluZm8KCmBgYHtyfQojZGF0ZQpmb3JtYXQoU3lzLnRpbWUoKSwgIiVkICVCLCAlWSwgJUgsJU0iKQoKI1BhY2thZ2VzIHVzZWQKc2Vzc2lvbkluZm8oKQpgYGA=