Load libraries

library(Seurat)
library(dplyr)
library(RColorBrewer)
library(ggplot2)
library(ggExtra)
library(cowplot)
library(reticulate)
library(wesanderson)
use_python("/usr/bin/python3")

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

This dataset was generated from the sequencing of two 10X V3 libraries run in parallel from the same tissue dissociation prep

Process the first library

Load the raw filtered matrix output from Cellranger

Countdata <- Read10X(data.dir = "../../RawData/Hem_1_filtered_feature_bc_matrix/")

Raw.data <- CreateSeuratObject(raw.data = Countdata,
                              min.cells = 3,
                              min.genes = 800,
                              project = "Hem1") ; rm(Countdata)

Raw.data@meta.data$Barcodes <- rownames(Raw.data@meta.data)

dim(Raw.data@data)
## [1] 18098 10180

Compute mito and ribo gene content per cell

mito.genes <- grep(pattern = "^mt-", x = rownames(x = Raw.data@data), value = TRUE)
percent.mito <- Matrix::colSums(Raw.data@raw.data[mito.genes, ])/Matrix::colSums(Raw.data@raw.data)
Raw.data <- AddMetaData(object = Raw.data, metadata = percent.mito, col.name = "percent.mito")

ribo.genes <- grep(pattern = "(^Rpl|^Rps|^Mrp)", x = rownames(x = Raw.data@data), value = TRUE)
percent.ribo <- Matrix::colSums(Raw.data@raw.data[ribo.genes, ])/Matrix::colSums(Raw.data@raw.data)
Raw.data <- AddMetaData(object = Raw.data, metadata = percent.ribo, col.name = "percent.ribo")

rm(mito.genes, percent.mito,ribo.genes,percent.ribo)
VlnPlot(object = Raw.data, features.plot = c("nGene","nUMI", "percent.mito", "percent.ribo"), nCol = 2 )

Inspect cell based on relation between nUMI and nGene detected

# Relation between nUMI and nGene detected
Cell.QC.Stat <- Raw.data@meta.data
Cell.QC.Stat$Barcodes <- rownames(Cell.QC.Stat)

p1 <- ggplot(Cell.QC.Stat, aes(x=nUMI, y=nGene)) + geom_point() + geom_smooth(method="lm")
p1 <- ggMarginal(p1, type = "histogram", fill="lightgrey")

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) + geom_point() + geom_smooth(method="lm")
p2 <- ggMarginal(p2, type = "histogram", fill="lightgrey")

plot_grid(plotlist = list(p1,p2), ncol=2, align='h', rel_widths = c(1, 1)) ; rm(p1,p2)

Cells with deviating nGene/nUMI ratio display an Erythrocyte signature

genes.list <- list(c("Hbb-bt", "Hbq1a", "Isg20", "Fech", "Snca", "Rec114"))
enrich.name <- "Erythrocyte.signature"
Raw.data <- AddModuleScore(Raw.data,
                           genes.list = genes.list,
                           genes.pool = NULL,
                           n.bin = 5,
                           seed.use = 1,
                           ctrl.size = length(genes.list),
                           use.k = FALSE,
                           enrich.name = enrich.name,
                           random.seed = 1)

Cell.QC.Stat$Erythrocyte.signature <- Raw.data@meta.data$Erythrocyte.signature1
gradient <- colorRampPalette(brewer.pal(n =11, name = "Spectral"))(100)

p1 <- ggplot(Cell.QC.Stat, aes(x= log10(nUMI), y= log10(nGene))) +
      geom_point(aes(color= Erythrocyte.signature))  + 
      scale_color_gradientn(colours=rev(gradient), name='Erythrocyte score') +
      geom_smooth(method="lm")


p1 <- ggMarginal(p1, type = "histogram", fill="lightgrey")
p1

Low quality cell filtering

Filtering cells based on percentage of mitochondrial transcripts

We applied a high and low median absolute deviation (mad) thresholds to exclude outlier cells

max.mito.thr <- median(Cell.QC.Stat$percent.mito) + 3*mad(Cell.QC.Stat$percent.mito)
min.mito.thr <- median(Cell.QC.Stat$percent.mito) - 3*mad(Cell.QC.Stat$percent.mito)
p1 <- ggplot(Cell.QC.Stat, aes(x=nGene, y=percent.mito)) +
  geom_point() +
  geom_hline(aes(yintercept = max.mito.thr), colour = "red", linetype = 2) +
  geom_hline(aes(yintercept = min.mito.thr), colour = "red", linetype = 2) +
  annotate(geom = "text", label = paste0(as.numeric(table(Cell.QC.Stat$percent.mito > max.mito.thr | Cell.QC.Stat$percent.mito < min.mito.thr)[2])," cells removed\n",
                                         as.numeric(table(Cell.QC.Stat$percent.mito > max.mito.thr | Cell.QC.Stat$percent.mito < min.mito.thr)[1])," cells remain"),
           x = 6000, y = 0.4)

ggMarginal(p1, type = "histogram", fill="lightgrey", bins=100) 

# Filter cells based on these thresholds
Cell.QC.Stat <- Cell.QC.Stat %>% filter(percent.mito < max.mito.thr) %>% filter(percent.mito > min.mito.thr)

Filtering cells based on number of genes and transcripts detected

Remove cells with to few gene detected or with to many UMI counts

We filter cells which are likely to be doublet based on their higher content of transcript detected as well as cell with to few genes/UMI sequenced

# Set low and hight thresholds on the number of detected genes
min.Genes.thr <- median(log10(Cell.QC.Stat$nGene)) - 3*mad(log10(Cell.QC.Stat$nGene))
max.Genes.thr <- median(log10(Cell.QC.Stat$nGene)) + 3*mad(log10(Cell.QC.Stat$nGene))

# Set hight threshold on the number of transcripts
max.nUMI.thr <- median(log10(Cell.QC.Stat$nUMI)) + 3*mad(log10(Cell.QC.Stat$nUMI))
# Gene/UMI scatter plot before filtering
p1 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point() +
  geom_smooth(method="lm") +
  geom_hline(aes(yintercept = min.Genes.thr), colour = "green", linetype = 2) +
  geom_hline(aes(yintercept = max.Genes.thr), colour = "green", linetype = 2) +
  geom_vline(aes(xintercept = max.nUMI.thr), colour = "red", linetype = 2)

ggMarginal(p1, type = "histogram", fill="lightgrey")

# Filter cells base on both metrics
Cell.QC.Stat <- Cell.QC.Stat %>% filter(log10(nGene) > min.Genes.thr) %>% filter(log10(nUMI) < max.nUMI.thr)

Filter cells below the main population nUMI/nGene relationship

lm.model <- lm(data = Cell.QC.Stat, formula = log10(nGene) ~ log10(nUMI))

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point() +
  geom_smooth(method="lm") +
  geom_hline(aes(yintercept = min.Genes.thr), colour = "green", linetype = 2) +
  geom_hline(aes(yintercept = max.Genes.thr), colour = "green", linetype = 2) +
  geom_vline(aes(xintercept = max.nUMI.thr), colour = "red", linetype = 2) +
  geom_abline(intercept = lm.model$coefficients[1] - 0.09 , slope = lm.model$coefficients[2], color="orange") +
  annotate(geom = "text", label = paste0(dim(Cell.QC.Stat)[1], " QC passed cells"), x = 4, y = 3.8)

ggMarginal(p2, type = "histogram", fill="lightgrey")

# Cells to exclude lie below an intercept offset of -0.09
Cell.QC.Stat$valideCells <- log10(Cell.QC.Stat$nGene) > (log10(Cell.QC.Stat$nUMI) * lm.model$coefficients[2] + (lm.model$coefficients[1] - 0.09))
p3 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point(aes(colour = valideCells)) +
  geom_smooth(method="lm") +
  geom_abline(intercept = lm.model$coefficients[1] - 0.09 , slope = lm.model$coefficients[2], color="orange") + 
  theme(legend.position="none") +
  annotate(geom = "text", label = paste0(as.numeric(table(Cell.QC.Stat$valideCells)[2]), " QC passed cells\n",
                                         as.numeric(table(Cell.QC.Stat$valideCells)[1]), " QC filtered"), x = 4, y = 3.8)

ggMarginal(p3, type = "histogram", fill="lightgrey")

# Remove invalid cells
Cell.QC.Stat <- Cell.QC.Stat %>% filter(valideCells)
Keep only the valid cells in the Seurat object
Raw.data <- SubsetData(Raw.data, cells.use = Cell.QC.Stat$Barcodes , subset.raw = T,  do.clean = F)
# Plot final QC metrics
VlnPlot(object = Raw.data, features.plot = c("nGene","nUMI", "percent.mito", "percent.ribo"), nCol = 2 )

p1 <- ggplot(Raw.data@meta.data, aes(x=log10(nUMI), y=log10(nGene))) + geom_point() + geom_smooth(method="lm")
ggMarginal(p1, type = "histogram", fill="lightgrey")

rm(list = ls()[!ls() %in% "Raw.data"])

Use Scrublet to detect obvious doublets

Run Scrublet with default parameter

Export raw count matrix as input to Scrublet

#Export filtered matrix
dir.create("../../Scrublet_inputs")

exprData <- Matrix(as.matrix(Raw.data@raw.data), sparse = TRUE)
writeMM(exprData, "../../Scrublet_inputs/matrix1.mtx")
## NULL
import scrublet as scr
import scipy.io
import numpy as np
import os

#Load raw counts matrix and gene list
input_dir = '../../Scrublet_inputs'
counts_matrix = scipy.io.mmread(input_dir + '/matrix1.mtx').T.tocsc()

#Initialize Scrublet
scrub = scr.Scrublet(counts_matrix,
                     expected_doublet_rate=0.1,
                     sim_doublet_ratio=2,
                     n_neighbors = 8)

#Run the default pipeline
doublet_scores, predicted_doublets = scrub.scrub_doublets(min_counts=1, 
                                                          min_cells=3, 
                                                          min_gene_variability_pctl=85, 
                                                          n_prin_comps=25)
## Preprocessing...
## Simulating doublets...
## Embedding transcriptomes using PCA...
## Calculating doublet scores...
## Automatically set threshold at doublet score = 0.20
## Detected doublet rate = 7.4%
## Estimated detectable doublet fraction = 68.6%
## Overall doublet rate:
##  Expected   = 10.0%
##  Estimated  = 10.8%
## Elapsed time: 28.5 seconds
# Import scrublet's doublet score
Raw.data@meta.data$Doubletscore <- py$doublet_scores

# Plot doublet score
ggplot(Raw.data@meta.data, aes(x = Doubletscore, stat(ndensity))) +
  geom_histogram(bins = 200, colour ="lightgrey")+
  geom_vline(xintercept = 0.20, colour = "red", linetype = 2)

# Manually set threshold at doublet score to 0.2
Raw.data@meta.data$Predicted_doublets <- ifelse(py$doublet_scores > 0.2, "Doublet","Singlet" )
table(Raw.data@meta.data$Predicted_doublets)
## 
## Doublet Singlet 
##     658    8184

Filter doublets

#Remove Scrublet inferred doublets
Valid.Cells <- rownames(Raw.data@meta.data[Raw.data@meta.data$Predicted_doublets == "Singlet",])

QC.data.1 <- SubsetData(Raw.data,  cells.use = Valid.Cells, subset.raw = T, do.clean = F)
rm(list = ls()[!ls() %in% "QC.data.1"])

Process the second library

Load the raw filtered matrix output from Cellranger

Countdata <- Read10X(data.dir = "../../RawData/Hem_2_filtered_feature_bc_matrix/")

Raw.data <- CreateSeuratObject(raw.data = Countdata,
                              min.cells = 3,
                              min.genes = 800,
                              project = "Hem2") ; rm(Countdata)

Raw.data@meta.data$Barcodes <- rownames(Raw.data@meta.data)

dim(Raw.data@data)
## [1] 18048  8817

Compute mito and ribo gene content per cell

mito.genes <- grep(pattern = "^mt-", x = rownames(x = Raw.data@data), value = TRUE)
percent.mito <- Matrix::colSums(Raw.data@raw.data[mito.genes, ])/Matrix::colSums(Raw.data@raw.data)
Raw.data <- AddMetaData(object = Raw.data, metadata = percent.mito, col.name = "percent.mito")

ribo.genes <- grep(pattern = "(^Rpl|^Rps|^Mrp)", x = rownames(x = Raw.data@data), value = TRUE)
percent.ribo <- Matrix::colSums(Raw.data@raw.data[ribo.genes, ])/Matrix::colSums(Raw.data@raw.data)
Raw.data <- AddMetaData(object = Raw.data, metadata = percent.ribo, col.name = "percent.ribo")

rm(mito.genes, percent.mito,ribo.genes,percent.ribo)
VlnPlot(object = Raw.data, features.plot = c("nGene","nUMI", "percent.mito", "percent.ribo"), nCol = 2 )

Inspect cell based on relation between nUMI and nGene detected

# Relation between nUMI and nGene detected
Cell.QC.Stat <- Raw.data@meta.data
Cell.QC.Stat$Barcodes <- rownames(Cell.QC.Stat)

p1 <- ggplot(Cell.QC.Stat, aes(x=nUMI, y=nGene)) + geom_point() + geom_smooth(method="lm")
p1 <- ggMarginal(p1, type = "histogram", fill="lightgrey")

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) + geom_point() + geom_smooth(method="lm")
p2 <- ggMarginal(p2, type = "histogram", fill="lightgrey")

plot_grid(plotlist = list(p1,p2), ncol=2, align='h', rel_widths = c(1, 1)) ; rm(p1,p2)

Cells with deviating nGene/nUMI ratio display an Erythrocyte signature

genes.list <- list(c("Hbb-bt", "Hbq1a", "Isg20", "Fech", "Snca", "Rec114"))
enrich.name <- "Erythrocyte.signature"
Raw.data <- AddModuleScore(Raw.data,
                           genes.list = genes.list,
                           genes.pool = NULL,
                           n.bin = 5,
                           seed.use = 1,
                           ctrl.size = length(genes.list),
                           use.k = FALSE,
                           enrich.name = enrich.name,
                           random.seed = 1)

Cell.QC.Stat$Erythrocyte.signature <- Raw.data@meta.data$Erythrocyte.signature1
gradient <- colorRampPalette(brewer.pal(n =11, name = "Spectral"))(100)

p1 <- ggplot(Cell.QC.Stat, aes(x= log10(nUMI), y= log10(nGene))) +
      geom_point(aes(color= Erythrocyte.signature))  + 
      scale_color_gradientn(colours=rev(gradient), name='Erythrocyte score') +
      geom_smooth(method="lm")


p1 <- ggMarginal(p1, type = "histogram", fill="lightgrey")
p1

Low quality cell filtering

Filtering cells based on percentage of mitochondrial transcripts

We applied a high and low median absolute deviation (mad) thresholds to exclude outlier cells

max.mito.thr <- median(Cell.QC.Stat$percent.mito) + 3*mad(Cell.QC.Stat$percent.mito)
min.mito.thr <- median(Cell.QC.Stat$percent.mito) - 3*mad(Cell.QC.Stat$percent.mito)
p1 <- ggplot(Cell.QC.Stat, aes(x=nGene, y=percent.mito)) +
  geom_point() +
  geom_hline(aes(yintercept = max.mito.thr), colour = "red", linetype = 2) +
  geom_hline(aes(yintercept = min.mito.thr), colour = "red", linetype = 2) +
  annotate(geom = "text", label = paste0(as.numeric(table(Cell.QC.Stat$percent.mito > max.mito.thr | Cell.QC.Stat$percent.mito < min.mito.thr)[2])," cells removed\n",
                                         as.numeric(table(Cell.QC.Stat$percent.mito > max.mito.thr | Cell.QC.Stat$percent.mito < min.mito.thr)[1])," cells remain"),
           x = 6000, y = 0.4)

ggMarginal(p1, type = "histogram", fill="lightgrey", bins=100) 

# Filter cells based on these thresholds
Cell.QC.Stat <- Cell.QC.Stat %>% filter(percent.mito < max.mito.thr) %>% filter(percent.mito > min.mito.thr)

Filtering cells based on number of genes and transcripts detected

Remove cells with to few gene detected or with to many UMI counts

We filter cells which are likely to be doublet based on their higher content of transcript detected as well as cell with to few genes/UMI sequenced

# Set low and hight thresholds on the number of detected genes
min.Genes.thr <- median(log10(Cell.QC.Stat$nGene)) - 3*mad(log10(Cell.QC.Stat$nGene))
max.Genes.thr <- median(log10(Cell.QC.Stat$nGene)) + 3*mad(log10(Cell.QC.Stat$nGene))

# Set hight threshold on the number of transcripts
max.nUMI.thr <- median(log10(Cell.QC.Stat$nUMI)) + 3*mad(log10(Cell.QC.Stat$nUMI))
# Gene/UMI scatter plot before filtering
p1 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point() +
  geom_smooth(method="lm") +
  geom_hline(aes(yintercept = min.Genes.thr), colour = "green", linetype = 2) +
  geom_hline(aes(yintercept = max.Genes.thr), colour = "green", linetype = 2) +
  geom_vline(aes(xintercept = max.nUMI.thr), colour = "red", linetype = 2)

ggMarginal(p1, type = "histogram", fill="lightgrey")

# Filter cells base on both metrics
Cell.QC.Stat <- Cell.QC.Stat %>% filter(log10(nGene) > min.Genes.thr) %>% filter(log10(nUMI) < max.nUMI.thr)

Filter cells below the main population nUMI/nGene relationship

lm.model <- lm(data = Cell.QC.Stat, formula = log10(nGene) ~ log10(nUMI))

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point() +
  geom_smooth(method="lm") +
  geom_hline(aes(yintercept = min.Genes.thr), colour = "green", linetype = 2) +
  geom_hline(aes(yintercept = max.Genes.thr), colour = "green", linetype = 2) +
  geom_vline(aes(xintercept = max.nUMI.thr), colour = "red", linetype = 2) +
  geom_abline(intercept = lm.model$coefficients[1] - 0.09 , slope = lm.model$coefficients[2], color="orange") +
  annotate(geom = "text", label = paste0(dim(Cell.QC.Stat)[1], " QC passed cells"), x = 4, y = 3.8)

ggMarginal(p2, type = "histogram", fill="lightgrey")

# Cells to exclude lie below an intercept offset of -0.09
Cell.QC.Stat$valideCells <- log10(Cell.QC.Stat$nGene) > (log10(Cell.QC.Stat$nUMI) * lm.model$coefficients[2] + (lm.model$coefficients[1] - 0.09))
p3 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point(aes(colour = valideCells)) +
  geom_smooth(method="lm") +
  geom_abline(intercept = lm.model$coefficients[1] - 0.09 , slope = lm.model$coefficients[2], color="orange") + 
  theme(legend.position="none") +
  annotate(geom = "text", label = paste0(as.numeric(table(Cell.QC.Stat$valideCells)[2]), " QC passed cells\n",
                                         as.numeric(table(Cell.QC.Stat$valideCells)[1]), " QC filtered"), x = 4, y = 3.8)

ggMarginal(p3, type = "histogram", fill="lightgrey")

# Remove invalid cells
Cell.QC.Stat <- Cell.QC.Stat %>% filter(valideCells)
Keep only the valid cells in the Seurat object
Raw.data <- SubsetData(Raw.data, cells.use = Cell.QC.Stat$Barcodes , subset.raw = T,  do.clean = F)
# Plot final QC metrics
VlnPlot(object = Raw.data, features.plot = c("nGene","nUMI", "percent.mito", "percent.ribo"), nCol = 2 )

p1 <- ggplot(Raw.data@meta.data, aes(x=log10(nUMI), y=log10(nGene))) + geom_point() + geom_smooth(method="lm")
ggMarginal(p1, type = "histogram", fill="lightgrey")

rm(list = ls()[!ls() %in% c("Raw.data", "QC.data.1")])

Use Scrublet to detect obvious doublets

Run Scrublet with default parameter

Export raw count matrix as input to Scrublet

#Export filtered matrix
exprData <- Matrix(as.matrix(Raw.data@raw.data), sparse = TRUE)
writeMM(exprData, "../../Scrublet_inputs/matrix2.mtx")
## NULL
import scrublet as scr
import scipy.io
import numpy as np
import os

#Load raw counts matrix and gene list
input_dir = '../../Scrublet_inputs'
counts_matrix = scipy.io.mmread(input_dir + '/matrix2.mtx').T.tocsc()

#Initialize Scrublet
scrub = scr.Scrublet(counts_matrix,
                     expected_doublet_rate=0.1,
                     sim_doublet_ratio=2,
                     n_neighbors = 8)

#Run the default pipeline
doublet_scores, predicted_doublets = scrub.scrub_doublets(min_counts=1, 
                                                          min_cells=3, 
                                                          min_gene_variability_pctl=85, 
                                                          n_prin_comps=25)
## Preprocessing...
## Simulating doublets...
## Embedding transcriptomes using PCA...
## Calculating doublet scores...
## Automatically set threshold at doublet score = 0.24
## Detected doublet rate = 5.1%
## Estimated detectable doublet fraction = 59.0%
## Overall doublet rate:
##  Expected   = 10.0%
##  Estimated  = 8.7%
## Elapsed time: 9.8 seconds
## 
## /home/matthieu/.local/lib/python3.6/site-packages/scrublet/helper_functions.py:239: RuntimeWarning: invalid value encountered in log
##   gLog = lambda input: np.log(input[1] * np.exp(-input[0]) + input[2])
# Import scrublet's doublet score
Raw.data@meta.data$Doubletscore <- py$doublet_scores

# Plot doublet score
ggplot(Raw.data@meta.data, aes(x = Doubletscore, stat(ndensity))) +
  geom_histogram(bins = 200, colour ="lightgrey")+
  geom_vline(xintercept = 0.24, colour = "red", linetype = 2)

# Manually set threshold at doublet score to 0.2
Raw.data@meta.data$Predicted_doublets <- ifelse(py$doublet_scores > 0.24, "Doublet","Singlet" )
table(Raw.data@meta.data$Predicted_doublets)
## 
## Doublet Singlet 
##     385    7149

Filter doublets

#Remove Scrublet inferred doublets
Valid.Cells <- rownames(Raw.data@meta.data[Raw.data@meta.data$Predicted_doublets == "Singlet",])

QC.data.2 <- SubsetData(Raw.data,  cells.use = Valid.Cells, subset.raw = T, do.clean = F)
rm(list = ls()[!ls() %in% c("QC.data.1", "QC.data.2")])

Merge the two libraries

Hem.data <- MergeSeurat(QC.data.1, QC.data.2,
                        do.normalize = F,
                        add.cell.id1 = "Hem1",
                        add.cell.id2 = "Hem2")

Hem.data@meta.data$Barcodes <- rownames(Hem.data@meta.data)
Cell.QC.Stat <- Hem.data@meta.data
Cell.QC.Stat$Barcodes <- rownames(Cell.QC.Stat)

p1 <- ggplot(Cell.QC.Stat, aes(x=nUMI, y=nGene)) + geom_point() + geom_smooth(method="lm")
p1 <- ggMarginal(p1, type = "histogram", fill="lightgrey")

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) + geom_point() + geom_smooth(method="lm")
p2 <- ggMarginal(p2, type = "histogram", fill="lightgrey")

plot_grid(plotlist = list(p1,p2), ncol=2, align='h', rel_widths = c(1, 1)) ; rm(p1,p2)

rm(list = ls()[!ls() %in% "Hem.data"])

Filter gene expression matrix

# Filter genes expressed by less than 3 cells
num.cells <- Matrix::rowSums(Hem.data@data > 0)
genes.use <- names(x = num.cells[which(x = num.cells >= 3)])
Hem.data@raw.data <- Hem.data@raw.data[genes.use, ]
Hem.data@data <- Hem.data@data[genes.use, ]
# log-normalize the gene expression matrix
Hem.data<- NormalizeData(object = Hem.data,
                          normalization.method = "LogNormalize", 
                          scale.factor = round(median(Hem.data@meta.data$nUMI)),
                          display.progress = F)

Generate SRING dimentionality reduction

dir.create("../../SpringCoordinates")
# Export raw expression matrix and gene list to regenerate a spring plot
exprData <- Matrix(as.matrix(Hem.data@raw.data), sparse = TRUE)
writeMM(exprData, "../../SpringCoordinates/ExprData.mtx")
## NULL
Genelist <- row.names(Hem.data@raw.data)
write.table(Genelist, "../../SpringCoordinates/Genelist.csv", sep="\t", col.names = F, row.names = F)

Spring coordinates were generated using the online version of SPRING with the following parameters :

Number of cells: 15333
Number of genes that passed filter: 874
Min expressing cells (gene filtering): 3
Min number of UMIs (gene filtering): 3
Gene variability %ile (gene filtering): 95
Number of principal components: 20
Number of nearest neighbors: 8
Number of force layout iterations: 500

Import the new coordinates

# Import Spring coordinates
Coordinates <-read.table("../SpringCoordinates/hem_spring.csv", sep=",", header = T)
rownames(Coordinates) <- colnames(Hem.data@data)

Hem.data <- SetDimReduction(Hem.data,
                            reduction.type = "spring",
                            slot = "cell.embeddings",
                            new.data = as.matrix(Coordinates))

Hem.data@dr$spring@key <- "spring"
colnames(Hem.data@dr$spring@cell.embeddings) <- paste0(GetDimReduction(object= Hem.data, reduction.type = "spring",slot = "key"), c(1,2))

Assign cell state scores

Cell-Cycle Scores

s.genes <- c("Mcm5", "Pcna", "Tym5", "Fen1", "Mcm2", "Mcm4", "Rrm1", "Ung", "Gins2", "Mcm6", "Cdca7", "Dtl", "Prim1", "Uhrf1", "Mlf1ip", "Hells", "Rfc2", "Rap2", "Nasp", "Rad51ap1", "Gmnn", "Wdr76", "Slbp", "Ccne2", "Ubr7", "Pold3", "Msh2", "Atad2", "Rad51", "Rrm2", "Cdc45", "Cdc6", "Exo1", "Tipin", "Dscc1", "Blm", " Casp8ap2", "Usp1", "Clspn", "Pola1", "Chaf1b", "Brip1", "E2f8")
g2m.genes <- c("Hmgb2", "Ddk1","Nusap1", "Ube2c", "Birc5", "Tpx2", "Top2a", "Ndc80", "Cks2", "Nuf2", "Cks1b", "Mki67", "Tmpo", " Cenpk", "Tacc3", "Fam64a", "Smc4", "Ccnb2", "Ckap2l", "Ckap2", "Aurkb", "Bub1", "Kif11", "Anp32e", "Tubb4b", "Gtse1", "kif20b", "Hjurp", "Cdca3", "Hn1", "Cdc20", "Ttk", "Cdc25c", "kif2c", "Rangap1", "Ncapd2", "Dlgap5", "Cdca2", "Cdca8", "Ect2", "Kif23", "Hmmr", "Aurka", "Psrc1", "Anln", "Lbr", "Ckap5", "Cenpe", "Ctcf", "Nek2", "G2e3", "Gas2l3", "Cbx5", "Cenpa")

Hem.data <- CellCycleScoring(object = Hem.data,
                             s.genes = s.genes,
                             g2m.genes = g2m.genes,
                             set.ident = TRUE)

Hem.data@meta.data$CC.Difference <- Hem.data@meta.data$S.Score - Hem.data@meta.data$G2M.Score
DimPlot(Hem.data,
        reduction.use = "spring",
        group.by = "Phase",
        cols.use = wes_palette("GrandBudapest1", 3, type = "discrete")[3:1],
        dim.1 = 1, 
        dim.2 = 2,
        do.label=T,
        label.size = 4,
        no.legend = F )

We assigned broad transcriptional cell state score based on known and manually curated marker genes

Apical progenitors

APgenes <- c("Rgcc", "Sparc", "Hes5","Hes1", "Slc1a3",
             "Ddah1", "Ldha", "Hmga2","Sfrp1", "Id4",
             "Creb5", "Ptn", "Lpar1", "Rcn1","Zfp36l1",
             "Sox9", "Sox2", "Nr2e1", "Ttyh1", "Trip6")
genes.list <- list(APgenes)
enrich.name <- "AP_signature"
Hem.data <- AddModuleScore(Hem.data,
                                  genes.list = genes.list,
                                  genes.pool = NULL,
                                  n.bin = 5,
                                  seed.use = 1,
                                  ctrl.size = length(genes.list),
                                  use.k = FALSE,
                                  enrich.name = enrich.name,
                                  random.seed = 1)
plot <- FeaturePlot(object = Hem.data,
                    features.plot = APgenes,
                    cols.use = c("grey90", brewer.pal(9,"YlGnBu")),
                    reduction.use = "spring",
                    no.legend = T,
                    overlay = F,
                    dark.theme = F,
                    do.return =T,
                    no.axes = T)
for (i in 1:length(plot)){
  plot[[i]]$data <- plot[[i]]$data[order(plot[[i]]$data$gene),]
}
cowplot::plot_grid(plotlist = plot[1:20], ncol = 5)
Apical progenitors gene expression

Apical progenitors gene expression

Basal progenitors

BPgenes <- c("Eomes", "Igsf8", "Insm1", "Elavl2", "Elavl4",
             "Hes6","Gadd45g", "Neurog2", "Btg2", "Neurog1")
genes.list <- list(BPgenes)
enrich.name <- "BP_signature"
Hem.data <- AddModuleScore(Hem.data,
                                  genes.list = genes.list,
                                  genes.pool = NULL,
                                  n.bin = 5,
                                  seed.use = 1,
                                  ctrl.size = length(genes.list),
                                  use.k = FALSE,
                                  enrich.name = enrich.name,
                                  random.seed = 1)
plot <- FeaturePlot(object = Hem.data,
                    features.plot = BPgenes,
                    cols.use = c("grey90", brewer.pal(9,"YlGnBu")),
                    reduction.use = "spring",
                    no.legend = T,
                    overlay = F,
                    dark.theme = F,
                    do.return =T,
                    no.axes = T)
for (i in 1:length(plot)){
  plot[[i]]$data <- plot[[i]]$data[order(plot[[i]]$data$gene),]
}
cowplot::plot_grid(plotlist = plot[1:10], ncol = 5)
Basal progenitors gene expression

Basal progenitors gene expression

Early pallial neurons

ENgenes <- c("Mfap4", "Nhlh2", "Nhlh1", "Ppp1r14a", "Nav1",
             "Neurod1", "Sorl1", "Svip", "Cxcl12", "Tenm4",
             "Dll3", "Rgmb", "Cntn2", "Vat1")
genes.list <- list(ENgenes)
enrich.name <- "EN_signature"
Hem.data <- AddModuleScore(Hem.data,
                                  genes.list = genes.list,
                                  genes.pool = NULL,
                                  n.bin = 5,
                                  seed.use = 1,
                                  ctrl.size = length(genes.list),
                                  use.k = FALSE,
                                  enrich.name = enrich.name,
                                  random.seed = 1)
plot <- FeaturePlot(object = Hem.data,
                    features.plot = ENgenes,
                    cols.use = c("grey90", brewer.pal(9,"YlGnBu")),
                    reduction.use = "spring",
                    no.legend = T,
                    overlay = F,
                    dark.theme = F,
                    do.return =T,
                    no.axes = T)
for (i in 1:length(plot)){
  plot[[i]]$data <- plot[[i]]$data[order(plot[[i]]$data$gene),]
}
cowplot::plot_grid(plotlist = plot[1:14], ncol = 5)
Early pallial neurons gene expression

Early pallial neurons gene expression

Late pallial neurons

LNgenes <- c("Snhg11", "Pcsk1n", "Mapt", "Ina", "Stmn4",
             "Gap43", "Tubb2a", "Ly6h","Ptprd", "Mef2c")
genes.list <- list(LNgenes)
enrich.name <- "LN_signature"
Hem.data <- AddModuleScore(Hem.data,
                                  genes.list = genes.list,
                                  genes.pool = NULL,
                                  n.bin = 5,
                                  seed.use = 1,
                                  ctrl.size = length(genes.list),
                                  use.k = FALSE,
                                  enrich.name = enrich.name,
                                  random.seed = 1)
plot <- FeaturePlot(object = Hem.data,
                    features.plot = LNgenes,
                    cols.use = c("grey90", brewer.pal(9,"YlGnBu")),
                    reduction.use = "spring",
                    no.legend = T,
                    overlay = F,
                    dark.theme = F,
                    do.return =T,
                    no.axes = T)
for (i in 1:length(plot)){
  plot[[i]]$data <- plot[[i]]$data[order(plot[[i]]$data$gene),]
}
cowplot::plot_grid(plotlist = plot[1:10], ncol = 5)
Late pallial neurons gene expression

Late pallial neurons gene expression

Meninges cells

Mgenes <- c("Lum", "Lgals1", "Foxc1")
genes.list <- list(Mgenes)
enrich.name <- "Meninges_signature"
Hem.data <- AddModuleScore(Hem.data,
                                  genes.list = genes.list,
                                  genes.pool = NULL,
                                  n.bin = 5,
                                  seed.use = 1,
                                  ctrl.size = length(genes.list),
                                  use.k = FALSE,
                                  enrich.name = enrich.name,
                                  random.seed = 1)
plot <- FeaturePlot(object = Hem.data,
                    features.plot = Mgenes,
                    cols.use = c("grey90", brewer.pal(9,"YlGnBu")),
                    reduction.use = "spring",
                    no.legend = T,
                    overlay = F,
                    dark.theme = F,
                    do.return =T,
                    no.axes = T)
for (i in 1:length(plot)){
  plot[[i]]$data <- plot[[i]]$data[order(plot[[i]]$data$gene),]
}
cowplot::plot_grid(plotlist = plot[1:3], ncol = 3)
Meninges gene expression

Meninges gene expression

Immune cells

Immunegenes <- c("Fcer1g", "C1qb", "Tyrobp")
genes.list <- list(Immunegenes)
enrich.name <- "Immune_signature"
Hem.data <- AddModuleScore(Hem.data,
                           genes.list = genes.list,
                           genes.pool = NULL,
                           n.bin = 5,
                           seed.use = 1,
                           ctrl.size = length(genes.list),
                           use.k = FALSE,
                           enrich.name = enrich.name,
                           random.seed = 1)
plot <- FeaturePlot(object = Hem.data,
                    features.plot = Immunegenes,
                    cols.use = c("grey90", brewer.pal(9,"YlGnBu")),
                    reduction.use = "spring",
                    no.legend = T,
                    overlay = F,
                    dark.theme = F,
                    do.return =T,
                    no.axes = T)
for (i in 1:length(plot)){
  plot[[i]]$data <- plot[[i]]$data[order(plot[[i]]$data$gene),]
}
cowplot::plot_grid(plotlist = plot[1:3], ncol = 3)
Immune gene expression

Immune gene expression

FeaturePlot(object = Hem.data,
            features.plot = c("AP_signature1", "BP_signature1",
                              "EN_signature1", "LN_signature1",
                              "Meninges_signature1","Immune_signature1"),
            cols.use = rev(brewer.pal(10,"Spectral")),
            reduction.use = "spring",
            no.legend = T,
            overlay = F,
            dark.theme = F,
            no.axes = T)

Save Seurat object

saveRDS(Hem.data, "../QC.filtered.cells.RDS")

Session Info

#date
format(Sys.time(), "%d %B, %Y, %H,%M")
## [1] "15 septembre, 2021, 09,47"
#Packages used
sessionInfo()
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 18.04.5 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/atlas/libblas.so.3.10.3
## LAPACK: /usr/lib/x86_64-linux-gnu/atlas/liblapack.so.3.10.3
## 
## 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] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] wesanderson_0.3.6  reticulate_1.18    ggExtra_0.9        RColorBrewer_1.1-2
## [5] dplyr_1.0.6        Seurat_2.3.4       Matrix_1.2-17      cowplot_1.1.1     
## [9] ggplot2_3.3.3     
## 
## loaded via a namespace (and not attached):
##   [1] Rtsne_0.15          colorspace_2.0-1    ellipsis_0.3.2     
##   [4] class_7.3-17        modeltools_0.2-22   ggridges_0.5.1     
##   [7] mclust_5.4.5        htmlTable_1.13.2    base64enc_0.1-3    
##  [10] rstudioapi_0.11     proxy_0.4-23        farver_2.1.0       
##  [13] npsurv_0.4-0        flexmix_2.3-15      bit64_4.0.2        
##  [16] fansi_0.5.0         codetools_0.2-16    splines_3.6.3      
##  [19] R.methodsS3_1.7.1   lsei_1.2-0          robustbase_0.93-5  
##  [22] knitr_1.26          jsonlite_1.7.2      Formula_1.2-3      
##  [25] ica_1.0-2           cluster_2.1.0       kernlab_0.9-29     
##  [28] png_0.1-7           R.oo_1.23.0         shiny_1.4.0        
##  [31] compiler_3.6.3      httr_1.4.2          backports_1.1.5    
##  [34] fastmap_1.0.1       later_1.2.0         lars_1.2           
##  [37] acepack_1.4.1       htmltools_0.5.1.1   tools_3.6.3        
##  [40] igraph_1.2.5        gtable_0.3.0        glue_1.4.2         
##  [43] reshape2_1.4.3      RANN_2.6.1          rappdirs_0.3.1     
##  [46] Rcpp_1.0.6          vctrs_0.3.8         gdata_2.18.0       
##  [49] ape_5.3             nlme_3.1-141        iterators_1.0.12   
##  [52] fpc_2.2-3           lmtest_0.9-37       gbRd_0.4-11        
##  [55] xfun_0.18           stringr_1.4.0       mime_0.10          
##  [58] miniUI_0.1.1.1      lifecycle_1.0.0     irlba_2.3.3        
##  [61] gtools_3.8.1        DEoptimR_1.0-8      zoo_1.8-6          
##  [64] MASS_7.3-53         scales_1.1.1        promises_1.2.0.1   
##  [67] doSNOW_1.0.18       parallel_3.6.3      yaml_2.2.1         
##  [70] pbapply_1.4-2       gridExtra_2.3       segmented_1.0-0    
##  [73] rpart_4.1-15        latticeExtra_0.6-28 stringi_1.4.6      
##  [76] highr_0.8           foreach_1.4.7       checkmate_1.9.4    
##  [79] caTools_1.17.1.2    bibtex_0.4.2        Rdpack_0.11-0      
##  [82] SDMTools_1.1-221.1  rlang_0.4.11        pkgconfig_2.0.3    
##  [85] dtw_1.21-3          prabclus_2.3-1      bitops_1.0-6       
##  [88] evaluate_0.14       lattice_0.20-41     ROCR_1.0-7         
##  [91] purrr_0.3.4         labeling_0.4.2      htmlwidgets_1.5.3  
##  [94] bit_4.0.4           tidyselect_1.1.1    plyr_1.8.4         
##  [97] magrittr_2.0.1      R6_2.5.0            snow_0.4-3         
## [100] gplots_3.0.1.1      generics_0.1.0      Hmisc_4.3-0        
## [103] DBI_1.0.0           mgcv_1.8-33         pillar_1.6.1       
## [106] foreign_0.8-72      withr_2.4.2         mixtools_1.1.0     
## [109] fitdistrplus_1.0-14 survival_2.44-1.1   nnet_7.3-14        
## [112] tsne_0.1-3          tibble_3.1.2        crayon_1.4.1       
## [115] hdf5r_1.3.2.9000    KernSmooth_2.23-15  utf8_1.2.1         
## [118] rmarkdown_2.5       grid_3.6.3          data.table_1.14.0  
## [121] metap_1.1           digest_0.6.27       diptest_0.75-7     
## [124] xtable_1.8-4        httpuv_1.5.2        tidyr_1.1.3        
## [127] R.utils_2.9.0       stats4_3.6.3        munsell_0.5.0

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

LS0tCnRpdGxlOiAiQ2VsbCBxdWFsaXR5IGNvbnRyb2wiCmF1dGhvcjoKICAgLSBNYXR0aGlldSBNb3JlYXVeW0luc3RpdHV0ZSBvZiBQc3ljaGlhdHJ5IGFuZCBOZXVyb3NjaWVuY2Ugb2YgUGFyaXMsIElOU0VSTSBVMTI2NiwgNzUwMTQsIFBhcmlzLCBGcmFuY2UsIG1hdHRoaWV1Lm1vcmVhdUBpbnNlcm0uZnJdIFshW10oaHR0cHM6Ly9vcmNpZC5vcmcvc2l0ZXMvZGVmYXVsdC9maWxlcy9pbWFnZXMvb3JjaWRfMTZ4MTYucG5nKV0oaHR0cHM6Ly9vcmNpZC5vcmcvMDAwMC0wMDAyLTI1OTItMjM3MykKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6IAogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBkZl9wcmludDogdGliYmxlCiAgICBoaWdobGlnaHQ6IGhhZGRvY2sKICAgIHRoZW1lOiBjb3NtbwogICAgY3NzOiAiLi4vc3R5bGUuY3NzIgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHllcwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGZpZy5hbGlnbiA9ICdjZW50ZXInLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFKQpzZXQuc2VlZCgxMjM0KQpgYGAKCiMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdnRXh0cmEpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShyZXRpY3VsYXRlKQpsaWJyYXJ5KHdlc2FuZGVyc29uKQp1c2VfcHl0aG9uKCIvdXNyL2Jpbi9weXRob24zIikKCiNTZXQgZ2dwbG90IHRoZW1lIGFzIGNsYXNzaWMKdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkKYGBgCgpUaGlzIGRhdGFzZXQgd2FzIGdlbmVyYXRlZCBmcm9tIHRoZSBzZXF1ZW5jaW5nIG9mIHR3byAxMFggVjMgbGlicmFyaWVzIHJ1biBpbiBwYXJhbGxlbCBmcm9tIHRoZSBzYW1lIHRpc3N1ZSBkaXNzb2NpYXRpb24gcHJlcAoKIyBQcm9jZXNzIHRoZSBmaXJzdCBsaWJyYXJ5CgojIyBMb2FkIHRoZSByYXcgZmlsdGVyZWQgbWF0cml4IG91dHB1dCBmcm9tIENlbGxyYW5nZXIKCmBgYHtyfQpDb3VudGRhdGEgPC0gUmVhZDEwWChkYXRhLmRpciA9ICIuLi8uLi9SYXdEYXRhL0hlbV8xX2ZpbHRlcmVkX2ZlYXR1cmVfYmNfbWF0cml4LyIpCgpSYXcuZGF0YSA8LSBDcmVhdGVTZXVyYXRPYmplY3QocmF3LmRhdGEgPSBDb3VudGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jZWxscyA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5nZW5lcyA9IDgwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvamVjdCA9ICJIZW0xIikgOyBybShDb3VudGRhdGEpCgpSYXcuZGF0YUBtZXRhLmRhdGEkQmFyY29kZXMgPC0gcm93bmFtZXMoUmF3LmRhdGFAbWV0YS5kYXRhKQoKZGltKFJhdy5kYXRhQGRhdGEpCmBgYAoKIyMgQ29tcHV0ZSBtaXRvIGFuZCByaWJvIGdlbmUgY29udGVudCBwZXIgY2VsbAoKYGBge3J9Cm1pdG8uZ2VuZXMgPC0gZ3JlcChwYXR0ZXJuID0gIl5tdC0iLCB4ID0gcm93bmFtZXMoeCA9IFJhdy5kYXRhQGRhdGEpLCB2YWx1ZSA9IFRSVUUpCnBlcmNlbnQubWl0byA8LSBNYXRyaXg6OmNvbFN1bXMoUmF3LmRhdGFAcmF3LmRhdGFbbWl0by5nZW5lcywgXSkvTWF0cml4Ojpjb2xTdW1zKFJhdy5kYXRhQHJhdy5kYXRhKQpSYXcuZGF0YSA8LSBBZGRNZXRhRGF0YShvYmplY3QgPSBSYXcuZGF0YSwgbWV0YWRhdGEgPSBwZXJjZW50Lm1pdG8sIGNvbC5uYW1lID0gInBlcmNlbnQubWl0byIpCgpyaWJvLmdlbmVzIDwtIGdyZXAocGF0dGVybiA9ICIoXlJwbHxeUnBzfF5NcnApIiwgeCA9IHJvd25hbWVzKHggPSBSYXcuZGF0YUBkYXRhKSwgdmFsdWUgPSBUUlVFKQpwZXJjZW50LnJpYm8gPC0gTWF0cml4Ojpjb2xTdW1zKFJhdy5kYXRhQHJhdy5kYXRhW3JpYm8uZ2VuZXMsIF0pL01hdHJpeDo6Y29sU3VtcyhSYXcuZGF0YUByYXcuZGF0YSkKUmF3LmRhdGEgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gUmF3LmRhdGEsIG1ldGFkYXRhID0gcGVyY2VudC5yaWJvLCBjb2wubmFtZSA9ICJwZXJjZW50LnJpYm8iKQoKcm0obWl0by5nZW5lcywgcGVyY2VudC5taXRvLHJpYm8uZ2VuZXMscGVyY2VudC5yaWJvKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg1LCA0KX0KVmxuUGxvdChvYmplY3QgPSBSYXcuZGF0YSwgZmVhdHVyZXMucGxvdCA9IGMoIm5HZW5lIiwiblVNSSIsICJwZXJjZW50Lm1pdG8iLCAicGVyY2VudC5yaWJvIiksIG5Db2wgPSAyICkKYGBgCgojIyBJbnNwZWN0IGNlbGwgYmFzZWQgb24gcmVsYXRpb24gYmV0d2VlbiBuVU1JIGFuZCBuR2VuZSBkZXRlY3RlZAoKYGBge3IgZmlnLmRpbT1jKDYsIDMuNSl9CiMgUmVsYXRpb24gYmV0d2VlbiBuVU1JIGFuZCBuR2VuZSBkZXRlY3RlZApDZWxsLlFDLlN0YXQgPC0gUmF3LmRhdGFAbWV0YS5kYXRhCkNlbGwuUUMuU3RhdCRCYXJjb2RlcyA8LSByb3duYW1lcyhDZWxsLlFDLlN0YXQpCgpwMSA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMoeD1uVU1JLCB5PW5HZW5lKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikKcDEgPC0gZ2dNYXJnaW5hbChwMSwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiKQoKcDIgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAoblVNSSksIHk9bG9nMTAobkdlbmUpKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikKcDIgPC0gZ2dNYXJnaW5hbChwMiwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiKQoKcGxvdF9ncmlkKHBsb3RsaXN0ID0gbGlzdChwMSxwMiksIG5jb2w9MiwgYWxpZ249J2gnLCByZWxfd2lkdGhzID0gYygxLCAxKSkgOyBybShwMSxwMikKYGBgCgpDZWxscyB3aXRoIGRldmlhdGluZyBuR2VuZS9uVU1JIHJhdGlvIGRpc3BsYXkgYW4gW0VyeXRocm9jeXRlIHNpZ25hdHVyZV0oaHR0cDovL21vdXNlYnJhaW4ub3JnL2RldmVsb3BtZW50L2NlbGx0eXBlcy5odG1sKQoKYGBge3J9CmdlbmVzLmxpc3QgPC0gbGlzdChjKCJIYmItYnQiLCAiSGJxMWEiLCAiSXNnMjAiLCAiRmVjaCIsICJTbmNhIiwgIlJlYzExNCIpKQplbnJpY2gubmFtZSA8LSAiRXJ5dGhyb2N5dGUuc2lnbmF0dXJlIgpSYXcuZGF0YSA8LSBBZGRNb2R1bGVTY29yZShSYXcuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXMubGlzdCA9IGdlbmVzLmxpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzLnBvb2wgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICBuLmJpbiA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY3RybC5zaXplID0gbGVuZ3RoKGdlbmVzLmxpc3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB1c2UuayA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBlbnJpY2gubmFtZSA9IGVucmljaC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDEpCgpDZWxsLlFDLlN0YXQkRXJ5dGhyb2N5dGUuc2lnbmF0dXJlIDwtIFJhdy5kYXRhQG1ldGEuZGF0YSRFcnl0aHJvY3l0ZS5zaWduYXR1cmUxCmBgYAoKYGBge3IgZmlnLmRpbT1jKDQsIDQpfQpncmFkaWVudCA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwobiA9MTEsIG5hbWUgPSAiU3BlY3RyYWwiKSkoMTAwKQoKcDEgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9IGxvZzEwKG5VTUkpLCB5PSBsb2cxMChuR2VuZSkpKSArCiAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBFcnl0aHJvY3l0ZS5zaWduYXR1cmUpKSAgKyAKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnM9cmV2KGdyYWRpZW50KSwgbmFtZT0nRXJ5dGhyb2N5dGUgc2NvcmUnKSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKQoKCnAxIDwtIGdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKcDEKYGBgCgojIyBMb3cgcXVhbGl0eSBjZWxsIGZpbHRlcmluZwoKIyMjIEZpbHRlcmluZyBjZWxscyBiYXNlZCBvbiBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdHMKCldlIGFwcGxpZWQgYSBoaWdoIGFuZCBsb3cgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiAobWFkKSB0aHJlc2hvbGRzIHRvIGV4Y2x1ZGUgb3V0bGllciBjZWxscwoKYGBge3J9Cm1heC5taXRvLnRociA8LSBtZWRpYW4oQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0bykgKyAzKm1hZChDZWxsLlFDLlN0YXQkcGVyY2VudC5taXRvKQptaW4ubWl0by50aHIgPC0gbWVkaWFuKENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8pIC0gMyptYWQoQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0bykKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CnAxIDwtIGdncGxvdChDZWxsLlFDLlN0YXQsIGFlcyh4PW5HZW5lLCB5PXBlcmNlbnQubWl0bykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXgubWl0by50aHIpLCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1pbi5taXRvLnRociksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpICsKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCBsYWJlbCA9IHBhc3RlMChhcy5udW1lcmljKHRhYmxlKENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8gPiBtYXgubWl0by50aHIgfCBDZWxsLlFDLlN0YXQkcGVyY2VudC5taXRvIDwgbWluLm1pdG8udGhyKVsyXSksIiBjZWxscyByZW1vdmVkXG4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLm51bWVyaWModGFibGUoQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0byA+IG1heC5taXRvLnRociB8IENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8gPCBtaW4ubWl0by50aHIpWzFdKSwiIGNlbGxzIHJlbWFpbiIpLAogICAgICAgICAgIHggPSA2MDAwLCB5ID0gMC40KQoKZ2dNYXJnaW5hbChwMSwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiLCBiaW5zPTEwMCkgCmBgYAoKYGBge3J9CiMgRmlsdGVyIGNlbGxzIGJhc2VkIG9uIHRoZXNlIHRocmVzaG9sZHMKQ2VsbC5RQy5TdGF0IDwtIENlbGwuUUMuU3RhdCAlPiUgZmlsdGVyKHBlcmNlbnQubWl0byA8IG1heC5taXRvLnRocikgJT4lIGZpbHRlcihwZXJjZW50Lm1pdG8gPiBtaW4ubWl0by50aHIpCmBgYAoKIyMjIEZpbHRlcmluZyBjZWxscyBiYXNlZCBvbiBudW1iZXIgb2YgZ2VuZXMgYW5kIHRyYW5zY3JpcHRzIGRldGVjdGVkCgojIyMjIFJlbW92ZSBjZWxscyB3aXRoIHRvIGZldyBnZW5lIGRldGVjdGVkIG9yIHdpdGggdG8gbWFueSBVTUkgY291bnRzCgpXZSBmaWx0ZXIgY2VsbHMgd2hpY2ggYXJlIGxpa2VseSB0byBiZSBkb3VibGV0IGJhc2VkIG9uIHRoZWlyIGhpZ2hlciBjb250ZW50IG9mIHRyYW5zY3JpcHQgZGV0ZWN0ZWQgYXMgd2VsbCBhcyBjZWxsIHdpdGggdG8gZmV3IGdlbmVzL1VNSSBzZXF1ZW5jZWQKCmBgYHtyfQojIFNldCBsb3cgYW5kIGhpZ2h0IHRocmVzaG9sZHMgb24gdGhlIG51bWJlciBvZiBkZXRlY3RlZCBnZW5lcwptaW4uR2VuZXMudGhyIDwtIG1lZGlhbihsb2cxMChDZWxsLlFDLlN0YXQkbkdlbmUpKSAtIDMqbWFkKGxvZzEwKENlbGwuUUMuU3RhdCRuR2VuZSkpCm1heC5HZW5lcy50aHIgPC0gbWVkaWFuKGxvZzEwKENlbGwuUUMuU3RhdCRuR2VuZSkpICsgMyptYWQobG9nMTAoQ2VsbC5RQy5TdGF0JG5HZW5lKSkKCiMgU2V0IGhpZ2h0IHRocmVzaG9sZCBvbiB0aGUgbnVtYmVyIG9mIHRyYW5zY3JpcHRzCm1heC5uVU1JLnRociA8LSBtZWRpYW4obG9nMTAoQ2VsbC5RQy5TdGF0JG5VTUkpKSArIDMqbWFkKGxvZzEwKENlbGwuUUMuU3RhdCRuVU1JKSkKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CiMgR2VuZS9VTUkgc2NhdHRlciBwbG90IGJlZm9yZSBmaWx0ZXJpbmcKcDEgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAoblVNSSksIHk9bG9nMTAobkdlbmUpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWluLkdlbmVzLnRociksIGNvbG91ciA9ICJncmVlbiIsIGxpbmV0eXBlID0gMikgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXguR2VuZXMudGhyKSwgY29sb3VyID0gImdyZWVuIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1heC5uVU1JLnRociksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpCgpnZ01hcmdpbmFsKHAxLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGw9ImxpZ2h0Z3JleSIpCmBgYAoKYGBge3J9CiMgRmlsdGVyIGNlbGxzIGJhc2Ugb24gYm90aCBtZXRyaWNzCkNlbGwuUUMuU3RhdCA8LSBDZWxsLlFDLlN0YXQgJT4lIGZpbHRlcihsb2cxMChuR2VuZSkgPiBtaW4uR2VuZXMudGhyKSAlPiUgZmlsdGVyKGxvZzEwKG5VTUkpIDwgbWF4Lm5VTUkudGhyKQpgYGAKCiMjIyMgRmlsdGVyIGNlbGxzIGJlbG93IHRoZSBtYWluIHBvcHVsYXRpb24gblVNSS9uR2VuZSByZWxhdGlvbnNoaXAKCmBgYHtyIGZpZy5kaW09Yyg0LCA0KX0KbG0ubW9kZWwgPC0gbG0oZGF0YSA9IENlbGwuUUMuU3RhdCwgZm9ybXVsYSA9IGxvZzEwKG5HZW5lKSB+IGxvZzEwKG5VTUkpKQoKcDIgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAoblVNSSksIHk9bG9nMTAobkdlbmUpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWluLkdlbmVzLnRociksIGNvbG91ciA9ICJncmVlbiIsIGxpbmV0eXBlID0gMikgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXguR2VuZXMudGhyKSwgY29sb3VyID0gImdyZWVuIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1heC5uVU1JLnRociksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSBsbS5tb2RlbCRjb2VmZmljaWVudHNbMV0gLSAwLjA5ICwgc2xvcGUgPSBsbS5tb2RlbCRjb2VmZmljaWVudHNbMl0sIGNvbG9yPSJvcmFuZ2UiKSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgbGFiZWwgPSBwYXN0ZTAoZGltKENlbGwuUUMuU3RhdClbMV0sICIgUUMgcGFzc2VkIGNlbGxzIiksIHggPSA0LCB5ID0gMy44KQoKZ2dNYXJnaW5hbChwMiwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiKQpgYGAKCmBgYHtyfQojIENlbGxzIHRvIGV4Y2x1ZGUgbGllIGJlbG93IGFuIGludGVyY2VwdCBvZmZzZXQgb2YgLTAuMDkKQ2VsbC5RQy5TdGF0JHZhbGlkZUNlbGxzIDwtIGxvZzEwKENlbGwuUUMuU3RhdCRuR2VuZSkgPiAobG9nMTAoQ2VsbC5RQy5TdGF0JG5VTUkpICogbG0ubW9kZWwkY29lZmZpY2llbnRzWzJdICsgKGxtLm1vZGVsJGNvZWZmaWNpZW50c1sxXSAtIDAuMDkpKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg0LCA0KX0KcDMgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAoblVNSSksIHk9bG9nMTAobkdlbmUpKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IHZhbGlkZUNlbGxzKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gbG0ubW9kZWwkY29lZmZpY2llbnRzWzFdIC0gMC4wOSAsIHNsb3BlID0gbG0ubW9kZWwkY29lZmZpY2llbnRzWzJdLCBjb2xvcj0ib3JhbmdlIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgbGFiZWwgPSBwYXN0ZTAoYXMubnVtZXJpYyh0YWJsZShDZWxsLlFDLlN0YXQkdmFsaWRlQ2VsbHMpWzJdKSwgIiBRQyBwYXNzZWQgY2VsbHNcbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMubnVtZXJpYyh0YWJsZShDZWxsLlFDLlN0YXQkdmFsaWRlQ2VsbHMpWzFdKSwgIiBRQyBmaWx0ZXJlZCIpLCB4ID0gNCwgeSA9IDMuOCkKCmdnTWFyZ2luYWwocDMsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKYGBgCgpgYGB7cn0KIyBSZW1vdmUgaW52YWxpZCBjZWxscwpDZWxsLlFDLlN0YXQgPC0gQ2VsbC5RQy5TdGF0ICU+JSBmaWx0ZXIodmFsaWRlQ2VsbHMpCmBgYAoKIyMjIyMgS2VlcCBvbmx5IHRoZSB2YWxpZCBjZWxscyBpbiB0aGUgU2V1cmF0IG9iamVjdAoKYGBge3J9ClJhdy5kYXRhIDwtIFN1YnNldERhdGEoUmF3LmRhdGEsIGNlbGxzLnVzZSA9IENlbGwuUUMuU3RhdCRCYXJjb2RlcyAsIHN1YnNldC5yYXcgPSBULCAgZG8uY2xlYW4gPSBGKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg0LCA0KX0KIyBQbG90IGZpbmFsIFFDIG1ldHJpY3MKVmxuUGxvdChvYmplY3QgPSBSYXcuZGF0YSwgZmVhdHVyZXMucGxvdCA9IGMoIm5HZW5lIiwiblVNSSIsICJwZXJjZW50Lm1pdG8iLCAicGVyY2VudC5yaWJvIiksIG5Db2wgPSAyICkKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CnAxIDwtIGdncGxvdChSYXcuZGF0YUBtZXRhLmRhdGEsIGFlcyh4PWxvZzEwKG5VTUkpLCB5PWxvZzEwKG5HZW5lKSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCmdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSAiUmF3LmRhdGEiXSkKYGBgCgojIyBVc2UgU2NydWJsZXQgdG8gZGV0ZWN0IG9idmlvdXMgZG91YmxldHMKCiMjIyBSdW4gU2NydWJsZXQgd2l0aCBkZWZhdWx0IHBhcmFtZXRlcgoKRXhwb3J0IHJhdyBjb3VudCBtYXRyaXggYXMgaW5wdXQgdG8gU2NydWJsZXQKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNFeHBvcnQgZmlsdGVyZWQgbWF0cml4CmRpci5jcmVhdGUoIi4uLy4uL1NjcnVibGV0X2lucHV0cyIpCgpleHByRGF0YSA8LSBNYXRyaXgoYXMubWF0cml4KFJhdy5kYXRhQHJhdy5kYXRhKSwgc3BhcnNlID0gVFJVRSkKd3JpdGVNTShleHByRGF0YSwgIi4uLy4uL1NjcnVibGV0X2lucHV0cy9tYXRyaXgxLm10eCIpCmBgYAoKYGBge3B5dGhvbiB9CmltcG9ydCBzY3J1YmxldCBhcyBzY3IKaW1wb3J0IHNjaXB5LmlvCmltcG9ydCBudW1weSBhcyBucAppbXBvcnQgb3MKCiNMb2FkIHJhdyBjb3VudHMgbWF0cml4IGFuZCBnZW5lIGxpc3QKaW5wdXRfZGlyID0gJy4uLy4uL1NjcnVibGV0X2lucHV0cycKY291bnRzX21hdHJpeCA9IHNjaXB5LmlvLm1tcmVhZChpbnB1dF9kaXIgKyAnL21hdHJpeDEubXR4JykuVC50b2NzYygpCgojSW5pdGlhbGl6ZSBTY3J1YmxldApzY3J1YiA9IHNjci5TY3J1YmxldChjb3VudHNfbWF0cml4LAogICAgICAgICAgICAgICAgICAgICBleHBlY3RlZF9kb3VibGV0X3JhdGU9MC4xLAogICAgICAgICAgICAgICAgICAgICBzaW1fZG91YmxldF9yYXRpbz0yLAogICAgICAgICAgICAgICAgICAgICBuX25laWdoYm9ycyA9IDgpCgojUnVuIHRoZSBkZWZhdWx0IHBpcGVsaW5lCmRvdWJsZXRfc2NvcmVzLCBwcmVkaWN0ZWRfZG91YmxldHMgPSBzY3J1Yi5zY3J1Yl9kb3VibGV0cyhtaW5fY291bnRzPTEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluX2NlbGxzPTMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluX2dlbmVfdmFyaWFiaWxpdHlfcGN0bD04NSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3ByaW5fY29tcHM9MjUpCgoKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgMyl9CiMgSW1wb3J0IHNjcnVibGV0J3MgZG91YmxldCBzY29yZQpSYXcuZGF0YUBtZXRhLmRhdGEkRG91YmxldHNjb3JlIDwtIHB5JGRvdWJsZXRfc2NvcmVzCgojIFBsb3QgZG91YmxldCBzY29yZQpnZ3Bsb3QoUmF3LmRhdGFAbWV0YS5kYXRhLCBhZXMoeCA9IERvdWJsZXRzY29yZSwgc3RhdChuZGVuc2l0eSkpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDIwMCwgY29sb3VyID0ibGlnaHRncmV5IikrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMC4yMCwgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gMikKCmBgYAoKYGBge3J9CiMgTWFudWFsbHkgc2V0IHRocmVzaG9sZCBhdCBkb3VibGV0IHNjb3JlIHRvIDAuMgpSYXcuZGF0YUBtZXRhLmRhdGEkUHJlZGljdGVkX2RvdWJsZXRzIDwtIGlmZWxzZShweSRkb3VibGV0X3Njb3JlcyA+IDAuMiwgIkRvdWJsZXQiLCJTaW5nbGV0IiApCnRhYmxlKFJhdy5kYXRhQG1ldGEuZGF0YSRQcmVkaWN0ZWRfZG91YmxldHMpCmBgYAoKIyMjIEZpbHRlciBkb3VibGV0cwoKYGBge3J9CiNSZW1vdmUgU2NydWJsZXQgaW5mZXJyZWQgZG91YmxldHMKVmFsaWQuQ2VsbHMgPC0gcm93bmFtZXMoUmF3LmRhdGFAbWV0YS5kYXRhW1Jhdy5kYXRhQG1ldGEuZGF0YSRQcmVkaWN0ZWRfZG91YmxldHMgPT0gIlNpbmdsZXQiLF0pCgpRQy5kYXRhLjEgPC0gU3Vic2V0RGF0YShSYXcuZGF0YSwgIGNlbGxzLnVzZSA9IFZhbGlkLkNlbGxzLCBzdWJzZXQucmF3ID0gVCwgZG8uY2xlYW4gPSBGKQpgYGAKCmBgYHtyfQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lICJRQy5kYXRhLjEiXSkKYGBgCgojIFByb2Nlc3MgdGhlIHNlY29uZCBsaWJyYXJ5CgojIyBMb2FkIHRoZSByYXcgZmlsdGVyZWQgbWF0cml4IG91dHB1dCBmcm9tIENlbGxyYW5nZXIKCmBgYHtyfQpDb3VudGRhdGEgPC0gUmVhZDEwWChkYXRhLmRpciA9ICIuLi8uLi9SYXdEYXRhL0hlbV8yX2ZpbHRlcmVkX2ZlYXR1cmVfYmNfbWF0cml4LyIpCgpSYXcuZGF0YSA8LSBDcmVhdGVTZXVyYXRPYmplY3QocmF3LmRhdGEgPSBDb3VudGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jZWxscyA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5nZW5lcyA9IDgwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvamVjdCA9ICJIZW0yIikgOyBybShDb3VudGRhdGEpCgpSYXcuZGF0YUBtZXRhLmRhdGEkQmFyY29kZXMgPC0gcm93bmFtZXMoUmF3LmRhdGFAbWV0YS5kYXRhKQoKZGltKFJhdy5kYXRhQGRhdGEpCmBgYAoKIyMgQ29tcHV0ZSBtaXRvIGFuZCByaWJvIGdlbmUgY29udGVudCBwZXIgY2VsbAoKYGBge3J9Cm1pdG8uZ2VuZXMgPC0gZ3JlcChwYXR0ZXJuID0gIl5tdC0iLCB4ID0gcm93bmFtZXMoeCA9IFJhdy5kYXRhQGRhdGEpLCB2YWx1ZSA9IFRSVUUpCnBlcmNlbnQubWl0byA8LSBNYXRyaXg6OmNvbFN1bXMoUmF3LmRhdGFAcmF3LmRhdGFbbWl0by5nZW5lcywgXSkvTWF0cml4Ojpjb2xTdW1zKFJhdy5kYXRhQHJhdy5kYXRhKQpSYXcuZGF0YSA8LSBBZGRNZXRhRGF0YShvYmplY3QgPSBSYXcuZGF0YSwgbWV0YWRhdGEgPSBwZXJjZW50Lm1pdG8sIGNvbC5uYW1lID0gInBlcmNlbnQubWl0byIpCgpyaWJvLmdlbmVzIDwtIGdyZXAocGF0dGVybiA9ICIoXlJwbHxeUnBzfF5NcnApIiwgeCA9IHJvd25hbWVzKHggPSBSYXcuZGF0YUBkYXRhKSwgdmFsdWUgPSBUUlVFKQpwZXJjZW50LnJpYm8gPC0gTWF0cml4Ojpjb2xTdW1zKFJhdy5kYXRhQHJhdy5kYXRhW3JpYm8uZ2VuZXMsIF0pL01hdHJpeDo6Y29sU3VtcyhSYXcuZGF0YUByYXcuZGF0YSkKUmF3LmRhdGEgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gUmF3LmRhdGEsIG1ldGFkYXRhID0gcGVyY2VudC5yaWJvLCBjb2wubmFtZSA9ICJwZXJjZW50LnJpYm8iKQoKcm0obWl0by5nZW5lcywgcGVyY2VudC5taXRvLHJpYm8uZ2VuZXMscGVyY2VudC5yaWJvKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg1LCA0KX0KVmxuUGxvdChvYmplY3QgPSBSYXcuZGF0YSwgZmVhdHVyZXMucGxvdCA9IGMoIm5HZW5lIiwiblVNSSIsICJwZXJjZW50Lm1pdG8iLCAicGVyY2VudC5yaWJvIiksIG5Db2wgPSAyICkKYGBgCgojIyBJbnNwZWN0IGNlbGwgYmFzZWQgb24gcmVsYXRpb24gYmV0d2VlbiBuVU1JIGFuZCBuR2VuZSBkZXRlY3RlZAoKYGBge3IgZmlnLmRpbT1jKDYsIDMuNSl9CiMgUmVsYXRpb24gYmV0d2VlbiBuVU1JIGFuZCBuR2VuZSBkZXRlY3RlZApDZWxsLlFDLlN0YXQgPC0gUmF3LmRhdGFAbWV0YS5kYXRhCkNlbGwuUUMuU3RhdCRCYXJjb2RlcyA8LSByb3duYW1lcyhDZWxsLlFDLlN0YXQpCgpwMSA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMoeD1uVU1JLCB5PW5HZW5lKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikKcDEgPC0gZ2dNYXJnaW5hbChwMSwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiKQoKcDIgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAoblVNSSksIHk9bG9nMTAobkdlbmUpKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikKcDIgPC0gZ2dNYXJnaW5hbChwMiwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiKQoKcGxvdF9ncmlkKHBsb3RsaXN0ID0gbGlzdChwMSxwMiksIG5jb2w9MiwgYWxpZ249J2gnLCByZWxfd2lkdGhzID0gYygxLCAxKSkgOyBybShwMSxwMikKYGBgCgpDZWxscyB3aXRoIGRldmlhdGluZyBuR2VuZS9uVU1JIHJhdGlvIGRpc3BsYXkgYW4gW0VyeXRocm9jeXRlIHNpZ25hdHVyZV0oaHR0cDovL21vdXNlYnJhaW4ub3JnL2RldmVsb3BtZW50L2NlbGx0eXBlcy5odG1sKQoKYGBge3J9CmdlbmVzLmxpc3QgPC0gbGlzdChjKCJIYmItYnQiLCAiSGJxMWEiLCAiSXNnMjAiLCAiRmVjaCIsICJTbmNhIiwgIlJlYzExNCIpKQplbnJpY2gubmFtZSA8LSAiRXJ5dGhyb2N5dGUuc2lnbmF0dXJlIgpSYXcuZGF0YSA8LSBBZGRNb2R1bGVTY29yZShSYXcuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXMubGlzdCA9IGdlbmVzLmxpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzLnBvb2wgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICBuLmJpbiA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY3RybC5zaXplID0gbGVuZ3RoKGdlbmVzLmxpc3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB1c2UuayA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBlbnJpY2gubmFtZSA9IGVucmljaC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDEpCgpDZWxsLlFDLlN0YXQkRXJ5dGhyb2N5dGUuc2lnbmF0dXJlIDwtIFJhdy5kYXRhQG1ldGEuZGF0YSRFcnl0aHJvY3l0ZS5zaWduYXR1cmUxCmBgYAoKYGBge3IgZmlnLmRpbT1jKDQsIDQpfQpncmFkaWVudCA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwobiA9MTEsIG5hbWUgPSAiU3BlY3RyYWwiKSkoMTAwKQoKcDEgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9IGxvZzEwKG5VTUkpLCB5PSBsb2cxMChuR2VuZSkpKSArCiAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBFcnl0aHJvY3l0ZS5zaWduYXR1cmUpKSAgKyAKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnM9cmV2KGdyYWRpZW50KSwgbmFtZT0nRXJ5dGhyb2N5dGUgc2NvcmUnKSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKQoKCnAxIDwtIGdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKcDEKYGBgCgojIyBMb3cgcXVhbGl0eSBjZWxsIGZpbHRlcmluZwoKIyMjIEZpbHRlcmluZyBjZWxscyBiYXNlZCBvbiBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdHMKCldlIGFwcGxpZWQgYSBoaWdoIGFuZCBsb3cgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiAobWFkKSB0aHJlc2hvbGRzIHRvIGV4Y2x1ZGUgb3V0bGllciBjZWxscwoKYGBge3J9Cm1heC5taXRvLnRociA8LSBtZWRpYW4oQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0bykgKyAzKm1hZChDZWxsLlFDLlN0YXQkcGVyY2VudC5taXRvKQptaW4ubWl0by50aHIgPC0gbWVkaWFuKENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8pIC0gMyptYWQoQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0bykKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CnAxIDwtIGdncGxvdChDZWxsLlFDLlN0YXQsIGFlcyh4PW5HZW5lLCB5PXBlcmNlbnQubWl0bykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXgubWl0by50aHIpLCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1pbi5taXRvLnRociksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpICsKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCBsYWJlbCA9IHBhc3RlMChhcy5udW1lcmljKHRhYmxlKENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8gPiBtYXgubWl0by50aHIgfCBDZWxsLlFDLlN0YXQkcGVyY2VudC5taXRvIDwgbWluLm1pdG8udGhyKVsyXSksIiBjZWxscyByZW1vdmVkXG4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLm51bWVyaWModGFibGUoQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0byA+IG1heC5taXRvLnRociB8IENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8gPCBtaW4ubWl0by50aHIpWzFdKSwiIGNlbGxzIHJlbWFpbiIpLAogICAgICAgICAgIHggPSA2MDAwLCB5ID0gMC40KQoKZ2dNYXJnaW5hbChwMSwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiLCBiaW5zPTEwMCkgCmBgYAoKYGBge3J9CiMgRmlsdGVyIGNlbGxzIGJhc2VkIG9uIHRoZXNlIHRocmVzaG9sZHMKQ2VsbC5RQy5TdGF0IDwtIENlbGwuUUMuU3RhdCAlPiUgZmlsdGVyKHBlcmNlbnQubWl0byA8IG1heC5taXRvLnRocikgJT4lIGZpbHRlcihwZXJjZW50Lm1pdG8gPiBtaW4ubWl0by50aHIpCmBgYAoKIyMjIEZpbHRlcmluZyBjZWxscyBiYXNlZCBvbiBudW1iZXIgb2YgZ2VuZXMgYW5kIHRyYW5zY3JpcHRzIGRldGVjdGVkCgojIyMjIFJlbW92ZSBjZWxscyB3aXRoIHRvIGZldyBnZW5lIGRldGVjdGVkIG9yIHdpdGggdG8gbWFueSBVTUkgY291bnRzCgpXZSBmaWx0ZXIgY2VsbHMgd2hpY2ggYXJlIGxpa2VseSB0byBiZSBkb3VibGV0IGJhc2VkIG9uIHRoZWlyIGhpZ2hlciBjb250ZW50IG9mIHRyYW5zY3JpcHQgZGV0ZWN0ZWQgYXMgd2VsbCBhcyBjZWxsIHdpdGggdG8gZmV3IGdlbmVzL1VNSSBzZXF1ZW5jZWQKCmBgYHtyfQojIFNldCBsb3cgYW5kIGhpZ2h0IHRocmVzaG9sZHMgb24gdGhlIG51bWJlciBvZiBkZXRlY3RlZCBnZW5lcwptaW4uR2VuZXMudGhyIDwtIG1lZGlhbihsb2cxMChDZWxsLlFDLlN0YXQkbkdlbmUpKSAtIDMqbWFkKGxvZzEwKENlbGwuUUMuU3RhdCRuR2VuZSkpCm1heC5HZW5lcy50aHIgPC0gbWVkaWFuKGxvZzEwKENlbGwuUUMuU3RhdCRuR2VuZSkpICsgMyptYWQobG9nMTAoQ2VsbC5RQy5TdGF0JG5HZW5lKSkKCiMgU2V0IGhpZ2h0IHRocmVzaG9sZCBvbiB0aGUgbnVtYmVyIG9mIHRyYW5zY3JpcHRzCm1heC5uVU1JLnRociA8LSBtZWRpYW4obG9nMTAoQ2VsbC5RQy5TdGF0JG5VTUkpKSArIDMqbWFkKGxvZzEwKENlbGwuUUMuU3RhdCRuVU1JKSkKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CiMgR2VuZS9VTUkgc2NhdHRlciBwbG90IGJlZm9yZSBmaWx0ZXJpbmcKcDEgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAoblVNSSksIHk9bG9nMTAobkdlbmUpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWluLkdlbmVzLnRociksIGNvbG91ciA9ICJncmVlbiIsIGxpbmV0eXBlID0gMikgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXguR2VuZXMudGhyKSwgY29sb3VyID0gImdyZWVuIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1heC5uVU1JLnRociksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpCgpnZ01hcmdpbmFsKHAxLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGw9ImxpZ2h0Z3JleSIpCmBgYAoKYGBge3J9CiMgRmlsdGVyIGNlbGxzIGJhc2Ugb24gYm90aCBtZXRyaWNzCkNlbGwuUUMuU3RhdCA8LSBDZWxsLlFDLlN0YXQgJT4lIGZpbHRlcihsb2cxMChuR2VuZSkgPiBtaW4uR2VuZXMudGhyKSAlPiUgZmlsdGVyKGxvZzEwKG5VTUkpIDwgbWF4Lm5VTUkudGhyKQpgYGAKCiMjIyMgRmlsdGVyIGNlbGxzIGJlbG93IHRoZSBtYWluIHBvcHVsYXRpb24gblVNSS9uR2VuZSByZWxhdGlvbnNoaXAKCmBgYHtyIGZpZy5kaW09Yyg0LCA0KX0KbG0ubW9kZWwgPC0gbG0oZGF0YSA9IENlbGwuUUMuU3RhdCwgZm9ybXVsYSA9IGxvZzEwKG5HZW5lKSB+IGxvZzEwKG5VTUkpKQoKcDIgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAoblVNSSksIHk9bG9nMTAobkdlbmUpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWluLkdlbmVzLnRociksIGNvbG91ciA9ICJncmVlbiIsIGxpbmV0eXBlID0gMikgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXguR2VuZXMudGhyKSwgY29sb3VyID0gImdyZWVuIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1heC5uVU1JLnRociksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSBsbS5tb2RlbCRjb2VmZmljaWVudHNbMV0gLSAwLjA5ICwgc2xvcGUgPSBsbS5tb2RlbCRjb2VmZmljaWVudHNbMl0sIGNvbG9yPSJvcmFuZ2UiKSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgbGFiZWwgPSBwYXN0ZTAoZGltKENlbGwuUUMuU3RhdClbMV0sICIgUUMgcGFzc2VkIGNlbGxzIiksIHggPSA0LCB5ID0gMy44KQoKZ2dNYXJnaW5hbChwMiwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiKQpgYGAKCmBgYHtyfQojIENlbGxzIHRvIGV4Y2x1ZGUgbGllIGJlbG93IGFuIGludGVyY2VwdCBvZmZzZXQgb2YgLTAuMDkKQ2VsbC5RQy5TdGF0JHZhbGlkZUNlbGxzIDwtIGxvZzEwKENlbGwuUUMuU3RhdCRuR2VuZSkgPiAobG9nMTAoQ2VsbC5RQy5TdGF0JG5VTUkpICogbG0ubW9kZWwkY29lZmZpY2llbnRzWzJdICsgKGxtLm1vZGVsJGNvZWZmaWNpZW50c1sxXSAtIDAuMDkpKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg0LCA0KX0KcDMgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAoblVNSSksIHk9bG9nMTAobkdlbmUpKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IHZhbGlkZUNlbGxzKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gbG0ubW9kZWwkY29lZmZpY2llbnRzWzFdIC0gMC4wOSAsIHNsb3BlID0gbG0ubW9kZWwkY29lZmZpY2llbnRzWzJdLCBjb2xvcj0ib3JhbmdlIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgbGFiZWwgPSBwYXN0ZTAoYXMubnVtZXJpYyh0YWJsZShDZWxsLlFDLlN0YXQkdmFsaWRlQ2VsbHMpWzJdKSwgIiBRQyBwYXNzZWQgY2VsbHNcbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMubnVtZXJpYyh0YWJsZShDZWxsLlFDLlN0YXQkdmFsaWRlQ2VsbHMpWzFdKSwgIiBRQyBmaWx0ZXJlZCIpLCB4ID0gNCwgeSA9IDMuOCkKCmdnTWFyZ2luYWwocDMsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKYGBgCgpgYGB7cn0KIyBSZW1vdmUgaW52YWxpZCBjZWxscwpDZWxsLlFDLlN0YXQgPC0gQ2VsbC5RQy5TdGF0ICU+JSBmaWx0ZXIodmFsaWRlQ2VsbHMpCmBgYAoKIyMjIyMgS2VlcCBvbmx5IHRoZSB2YWxpZCBjZWxscyBpbiB0aGUgU2V1cmF0IG9iamVjdAoKYGBge3J9ClJhdy5kYXRhIDwtIFN1YnNldERhdGEoUmF3LmRhdGEsIGNlbGxzLnVzZSA9IENlbGwuUUMuU3RhdCRCYXJjb2RlcyAsIHN1YnNldC5yYXcgPSBULCAgZG8uY2xlYW4gPSBGKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg0LCA0KX0KIyBQbG90IGZpbmFsIFFDIG1ldHJpY3MKVmxuUGxvdChvYmplY3QgPSBSYXcuZGF0YSwgZmVhdHVyZXMucGxvdCA9IGMoIm5HZW5lIiwiblVNSSIsICJwZXJjZW50Lm1pdG8iLCAicGVyY2VudC5yaWJvIiksIG5Db2wgPSAyICkKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CnAxIDwtIGdncGxvdChSYXcuZGF0YUBtZXRhLmRhdGEsIGFlcyh4PWxvZzEwKG5VTUkpLCB5PWxvZzEwKG5HZW5lKSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCmdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCJSYXcuZGF0YSIsICJRQy5kYXRhLjEiKV0pCmBgYAoKIyMgVXNlIFNjcnVibGV0IHRvIGRldGVjdCBvYnZpb3VzIGRvdWJsZXRzCgojIyMgUnVuIFNjcnVibGV0IHdpdGggZGVmYXVsdCBwYXJhbWV0ZXIKCkV4cG9ydCByYXcgY291bnQgbWF0cml4IGFzIGlucHV0IHRvIFNjcnVibGV0CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojRXhwb3J0IGZpbHRlcmVkIG1hdHJpeApleHByRGF0YSA8LSBNYXRyaXgoYXMubWF0cml4KFJhdy5kYXRhQHJhdy5kYXRhKSwgc3BhcnNlID0gVFJVRSkKd3JpdGVNTShleHByRGF0YSwgIi4uLy4uL1NjcnVibGV0X2lucHV0cy9tYXRyaXgyLm10eCIpCmBgYAoKYGBge3B5dGhvbiB9CmltcG9ydCBzY3J1YmxldCBhcyBzY3IKaW1wb3J0IHNjaXB5LmlvCmltcG9ydCBudW1weSBhcyBucAppbXBvcnQgb3MKCiNMb2FkIHJhdyBjb3VudHMgbWF0cml4IGFuZCBnZW5lIGxpc3QKaW5wdXRfZGlyID0gJy4uLy4uL1NjcnVibGV0X2lucHV0cycKY291bnRzX21hdHJpeCA9IHNjaXB5LmlvLm1tcmVhZChpbnB1dF9kaXIgKyAnL21hdHJpeDIubXR4JykuVC50b2NzYygpCgojSW5pdGlhbGl6ZSBTY3J1YmxldApzY3J1YiA9IHNjci5TY3J1YmxldChjb3VudHNfbWF0cml4LAogICAgICAgICAgICAgICAgICAgICBleHBlY3RlZF9kb3VibGV0X3JhdGU9MC4xLAogICAgICAgICAgICAgICAgICAgICBzaW1fZG91YmxldF9yYXRpbz0yLAogICAgICAgICAgICAgICAgICAgICBuX25laWdoYm9ycyA9IDgpCgojUnVuIHRoZSBkZWZhdWx0IHBpcGVsaW5lCmRvdWJsZXRfc2NvcmVzLCBwcmVkaWN0ZWRfZG91YmxldHMgPSBzY3J1Yi5zY3J1Yl9kb3VibGV0cyhtaW5fY291bnRzPTEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluX2NlbGxzPTMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluX2dlbmVfdmFyaWFiaWxpdHlfcGN0bD04NSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3ByaW5fY29tcHM9MjUpCgoKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgMyl9CiMgSW1wb3J0IHNjcnVibGV0J3MgZG91YmxldCBzY29yZQpSYXcuZGF0YUBtZXRhLmRhdGEkRG91YmxldHNjb3JlIDwtIHB5JGRvdWJsZXRfc2NvcmVzCgojIFBsb3QgZG91YmxldCBzY29yZQpnZ3Bsb3QoUmF3LmRhdGFAbWV0YS5kYXRhLCBhZXMoeCA9IERvdWJsZXRzY29yZSwgc3RhdChuZGVuc2l0eSkpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDIwMCwgY29sb3VyID0ibGlnaHRncmV5IikrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMC4yNCwgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gMikKCmBgYAoKYGBge3J9CiMgTWFudWFsbHkgc2V0IHRocmVzaG9sZCBhdCBkb3VibGV0IHNjb3JlIHRvIDAuMgpSYXcuZGF0YUBtZXRhLmRhdGEkUHJlZGljdGVkX2RvdWJsZXRzIDwtIGlmZWxzZShweSRkb3VibGV0X3Njb3JlcyA+IDAuMjQsICJEb3VibGV0IiwiU2luZ2xldCIgKQp0YWJsZShSYXcuZGF0YUBtZXRhLmRhdGEkUHJlZGljdGVkX2RvdWJsZXRzKQpgYGAKCiMjIyBGaWx0ZXIgZG91YmxldHMKCmBgYHtyfQojUmVtb3ZlIFNjcnVibGV0IGluZmVycmVkIGRvdWJsZXRzClZhbGlkLkNlbGxzIDwtIHJvd25hbWVzKFJhdy5kYXRhQG1ldGEuZGF0YVtSYXcuZGF0YUBtZXRhLmRhdGEkUHJlZGljdGVkX2RvdWJsZXRzID09ICJTaW5nbGV0IixdKQoKUUMuZGF0YS4yIDwtIFN1YnNldERhdGEoUmF3LmRhdGEsICBjZWxscy51c2UgPSBWYWxpZC5DZWxscywgc3Vic2V0LnJhdyA9IFQsIGRvLmNsZWFuID0gRikKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCJRQy5kYXRhLjEiLCAiUUMuZGF0YS4yIildKQpgYGAKCiMgTWVyZ2UgdGhlIHR3byBsaWJyYXJpZXMKCmBgYHtyfQpIZW0uZGF0YSA8LSBNZXJnZVNldXJhdChRQy5kYXRhLjEsIFFDLmRhdGEuMiwKICAgICAgICAgICAgICAgICAgICAgICAgZG8ubm9ybWFsaXplID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgYWRkLmNlbGwuaWQxID0gIkhlbTEiLAogICAgICAgICAgICAgICAgICAgICAgICBhZGQuY2VsbC5pZDIgPSAiSGVtMiIpCgpIZW0uZGF0YUBtZXRhLmRhdGEkQmFyY29kZXMgPC0gcm93bmFtZXMoSGVtLmRhdGFAbWV0YS5kYXRhKQpgYGAKCgpgYGB7ciBmaWcuZGltPWMoNiwgMy41KX0KQ2VsbC5RQy5TdGF0IDwtIEhlbS5kYXRhQG1ldGEuZGF0YQpDZWxsLlFDLlN0YXQkQmFyY29kZXMgPC0gcm93bmFtZXMoQ2VsbC5RQy5TdGF0KQoKcDEgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9blVNSSwgeT1uR2VuZSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCnAxIDwtIGdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKCnAyIDwtIGdncGxvdChDZWxsLlFDLlN0YXQsIGFlcyh4PWxvZzEwKG5VTUkpLCB5PWxvZzEwKG5HZW5lKSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCnAyIDwtIGdnTWFyZ2luYWwocDIsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKCnBsb3RfZ3JpZChwbG90bGlzdCA9IGxpc3QocDEscDIpLCBuY29sPTIsIGFsaWduPSdoJywgcmVsX3dpZHRocyA9IGMoMSwgMSkpIDsgcm0ocDEscDIpCmBgYAoKYGBge3J9CnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgIkhlbS5kYXRhIl0pCmBgYAoKIyMgRmlsdGVyIGdlbmUgZXhwcmVzc2lvbiBtYXRyaXgKCmBgYHtyfQojIEZpbHRlciBnZW5lcyBleHByZXNzZWQgYnkgbGVzcyB0aGFuIDMgY2VsbHMKbnVtLmNlbGxzIDwtIE1hdHJpeDo6cm93U3VtcyhIZW0uZGF0YUBkYXRhID4gMCkKZ2VuZXMudXNlIDwtIG5hbWVzKHggPSBudW0uY2VsbHNbd2hpY2goeCA9IG51bS5jZWxscyA+PSAzKV0pCkhlbS5kYXRhQHJhdy5kYXRhIDwtIEhlbS5kYXRhQHJhdy5kYXRhW2dlbmVzLnVzZSwgXQpIZW0uZGF0YUBkYXRhIDwtIEhlbS5kYXRhQGRhdGFbZ2VuZXMudXNlLCBdCmBgYAoKYGBge3J9CiMgbG9nLW5vcm1hbGl6ZSB0aGUgZ2VuZSBleHByZXNzaW9uIG1hdHJpeApIZW0uZGF0YTwtIE5vcm1hbGl6ZURhdGEob2JqZWN0ID0gSGVtLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUuZmFjdG9yID0gcm91bmQobWVkaWFuKEhlbS5kYXRhQG1ldGEuZGF0YSRuVU1JKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGxheS5wcm9ncmVzcyA9IEYpCmBgYAoKIyMgR2VuZXJhdGUgU1JJTkcgZGltZW50aW9uYWxpdHkgcmVkdWN0aW9uCgpgYGB7cn0KZGlyLmNyZWF0ZSgiLi4vLi4vU3ByaW5nQ29vcmRpbmF0ZXMiKQpgYGAKCmBgYHtyfQojIEV4cG9ydCByYXcgZXhwcmVzc2lvbiBtYXRyaXggYW5kIGdlbmUgbGlzdCB0byByZWdlbmVyYXRlIGEgc3ByaW5nIHBsb3QKZXhwckRhdGEgPC0gTWF0cml4KGFzLm1hdHJpeChIZW0uZGF0YUByYXcuZGF0YSksIHNwYXJzZSA9IFRSVUUpCndyaXRlTU0oZXhwckRhdGEsICIuLi8uLi9TcHJpbmdDb29yZGluYXRlcy9FeHByRGF0YS5tdHgiKQpgYGAKCmBgYHtyfQpHZW5lbGlzdCA8LSByb3cubmFtZXMoSGVtLmRhdGFAcmF3LmRhdGEpCndyaXRlLnRhYmxlKEdlbmVsaXN0LCAiLi4vLi4vU3ByaW5nQ29vcmRpbmF0ZXMvR2VuZWxpc3QuY3N2Iiwgc2VwPSJcdCIsIGNvbC5uYW1lcyA9IEYsIHJvdy5uYW1lcyA9IEYpCmBgYAoKU3ByaW5nIGNvb3JkaW5hdGVzIHdlcmUgZ2VuZXJhdGVkIHVzaW5nIHRoZSBvbmxpbmUgdmVyc2lvbiBvZiBbU1BSSU5HXShodHRwczovL2tsZWludG9vbHMuaG1zLmhhcnZhcmQuZWR1L3Rvb2xzL3NwcmluZy5odG1sKSB3aXRoIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyA6CgpgYGAKTnVtYmVyIG9mIGNlbGxzOiAxNTMzMwpOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzZWQgZmlsdGVyOiA4NzQKTWluIGV4cHJlc3NpbmcgY2VsbHMgKGdlbmUgZmlsdGVyaW5nKTogMwpNaW4gbnVtYmVyIG9mIFVNSXMgKGdlbmUgZmlsdGVyaW5nKTogMwpHZW5lIHZhcmlhYmlsaXR5ICVpbGUgKGdlbmUgZmlsdGVyaW5nKTogOTUKTnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzOiAyMApOdW1iZXIgb2YgbmVhcmVzdCBuZWlnaGJvcnM6IDgKTnVtYmVyIG9mIGZvcmNlIGxheW91dCBpdGVyYXRpb25zOiA1MDAKYGBgCgpJbXBvcnQgdGhlIG5ldyBjb29yZGluYXRlcwoKYGBge3J9CiMgSW1wb3J0IFNwcmluZyBjb29yZGluYXRlcwpDb29yZGluYXRlcyA8LXJlYWQudGFibGUoIi4uL1NwcmluZ0Nvb3JkaW5hdGVzL2hlbV9zcHJpbmcuY3N2Iiwgc2VwPSIsIiwgaGVhZGVyID0gVCkKcm93bmFtZXMoQ29vcmRpbmF0ZXMpIDwtIGNvbG5hbWVzKEhlbS5kYXRhQGRhdGEpCgpIZW0uZGF0YSA8LSBTZXREaW1SZWR1Y3Rpb24oSGVtLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24udHlwZSA9ICJzcHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2xvdCA9ICJjZWxsLmVtYmVkZGluZ3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmRhdGEgPSBhcy5tYXRyaXgoQ29vcmRpbmF0ZXMpKQoKSGVtLmRhdGFAZHIkc3ByaW5nQGtleSA8LSAic3ByaW5nIgpjb2xuYW1lcyhIZW0uZGF0YUBkciRzcHJpbmdAY2VsbC5lbWJlZGRpbmdzKSA8LSBwYXN0ZTAoR2V0RGltUmVkdWN0aW9uKG9iamVjdD0gSGVtLmRhdGEsIHJlZHVjdGlvbi50eXBlID0gInNwcmluZyIsc2xvdCA9ICJrZXkiKSwgYygxLDIpKQpgYGAKCiMgQXNzaWduIGNlbGwgc3RhdGUgc2NvcmVzCgojIyBDZWxsLUN5Y2xlIFNjb3JlcwoKYGBge3J9CnMuZ2VuZXMgPC0gYygiTWNtNSIsICJQY25hIiwgIlR5bTUiLCAiRmVuMSIsICJNY20yIiwgIk1jbTQiLCAiUnJtMSIsICJVbmciLCAiR2luczIiLCAiTWNtNiIsICJDZGNhNyIsICJEdGwiLCAiUHJpbTEiLCAiVWhyZjEiLCAiTWxmMWlwIiwgIkhlbGxzIiwgIlJmYzIiLCAiUmFwMiIsICJOYXNwIiwgIlJhZDUxYXAxIiwgIkdtbm4iLCAiV2RyNzYiLCAiU2xicCIsICJDY25lMiIsICJVYnI3IiwgIlBvbGQzIiwgIk1zaDIiLCAiQXRhZDIiLCAiUmFkNTEiLCAiUnJtMiIsICJDZGM0NSIsICJDZGM2IiwgIkV4bzEiLCAiVGlwaW4iLCAiRHNjYzEiLCAiQmxtIiwgIiBDYXNwOGFwMiIsICJVc3AxIiwgIkNsc3BuIiwgIlBvbGExIiwgIkNoYWYxYiIsICJCcmlwMSIsICJFMmY4IikKZzJtLmdlbmVzIDwtIGMoIkhtZ2IyIiwgIkRkazEiLCJOdXNhcDEiLCAiVWJlMmMiLCAiQmlyYzUiLCAiVHB4MiIsICJUb3AyYSIsICJOZGM4MCIsICJDa3MyIiwgIk51ZjIiLCAiQ2tzMWIiLCAiTWtpNjciLCAiVG1wbyIsICIgQ2VucGsiLCAiVGFjYzMiLCAiRmFtNjRhIiwgIlNtYzQiLCAiQ2NuYjIiLCAiQ2thcDJsIiwgIkNrYXAyIiwgIkF1cmtiIiwgIkJ1YjEiLCAiS2lmMTEiLCAiQW5wMzJlIiwgIlR1YmI0YiIsICJHdHNlMSIsICJraWYyMGIiLCAiSGp1cnAiLCAiQ2RjYTMiLCAiSG4xIiwgIkNkYzIwIiwgIlR0ayIsICJDZGMyNWMiLCAia2lmMmMiLCAiUmFuZ2FwMSIsICJOY2FwZDIiLCAiRGxnYXA1IiwgIkNkY2EyIiwgIkNkY2E4IiwgIkVjdDIiLCAiS2lmMjMiLCAiSG1tciIsICJBdXJrYSIsICJQc3JjMSIsICJBbmxuIiwgIkxiciIsICJDa2FwNSIsICJDZW5wZSIsICJDdGNmIiwgIk5lazIiLCAiRzJlMyIsICJHYXMybDMiLCAiQ2J4NSIsICJDZW5wYSIpCgpIZW0uZGF0YSA8LSBDZWxsQ3ljbGVTY29yaW5nKG9iamVjdCA9IEhlbS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHMuZ2VuZXMgPSBzLmdlbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGcybS5nZW5lcyA9IGcybS5nZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXQuaWRlbnQgPSBUUlVFKQoKSGVtLmRhdGFAbWV0YS5kYXRhJENDLkRpZmZlcmVuY2UgPC0gSGVtLmRhdGFAbWV0YS5kYXRhJFMuU2NvcmUgLSBIZW0uZGF0YUBtZXRhLmRhdGEkRzJNLlNjb3JlCmBgYAoKYGBge3IgZmlnLmRpbT1jKDgsIDYpfQpEaW1QbG90KEhlbS5kYXRhLAogICAgICAgIHJlZHVjdGlvbi51c2UgPSAic3ByaW5nIiwKICAgICAgICBncm91cC5ieSA9ICJQaGFzZSIsCiAgICAgICAgY29scy51c2UgPSB3ZXNfcGFsZXR0ZSgiR3JhbmRCdWRhcGVzdDEiLCAzLCB0eXBlID0gImRpc2NyZXRlIilbMzoxXSwKICAgICAgICBkaW0uMSA9IDEsIAogICAgICAgIGRpbS4yID0gMiwKICAgICAgICBkby5sYWJlbD1ULAogICAgICAgIGxhYmVsLnNpemUgPSA0LAogICAgICAgIG5vLmxlZ2VuZCA9IEYgKQpgYGAKCldlIGFzc2lnbmVkIGJyb2FkIHRyYW5zY3JpcHRpb25hbCBjZWxsIHN0YXRlIHNjb3JlIGJhc2VkIG9uIGtub3duIGFuZCBtYW51YWxseSBjdXJhdGVkIG1hcmtlciBnZW5lcwoKIyMgQXBpY2FsIHByb2dlbml0b3JzCgpgYGB7cn0KQVBnZW5lcyA8LSBjKCJSZ2NjIiwgIlNwYXJjIiwgIkhlczUiLCJIZXMxIiwgIlNsYzFhMyIsCiAgICAgICAgICAgICAiRGRhaDEiLCAiTGRoYSIsICJIbWdhMiIsIlNmcnAxIiwgIklkNCIsCiAgICAgICAgICAgICAiQ3JlYjUiLCAiUHRuIiwgIkxwYXIxIiwgIlJjbjEiLCJaZnAzNmwxIiwKICAgICAgICAgICAgICJTb3g5IiwgIlNveDIiLCAiTnIyZTEiLCAiVHR5aDEiLCAiVHJpcDYiKQpnZW5lcy5saXN0IDwtIGxpc3QoQVBnZW5lcykKZW5yaWNoLm5hbWUgPC0gIkFQX3NpZ25hdHVyZSIKSGVtLmRhdGEgPC0gQWRkTW9kdWxlU2NvcmUoSGVtLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lcy5saXN0ID0gZ2VuZXMubGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzLnBvb2wgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbi5iaW4gPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3RybC5zaXplID0gbGVuZ3RoKGdlbmVzLmxpc3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlLmsgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVucmljaC5uYW1lID0gZW5yaWNoLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDEpCmBgYAoKYGBge3IgZmlnLnNob3c9J2hpZGUnIH0KcGxvdCA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBIZW0uZGF0YSwKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcy5wbG90ID0gQVBnZW5lcywKICAgICAgICAgICAgICAgICAgICBjb2xzLnVzZSA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbi51c2UgPSAic3ByaW5nIiwKICAgICAgICAgICAgICAgICAgICBuby5sZWdlbmQgPSBULAogICAgICAgICAgICAgICAgICAgIG92ZXJsYXkgPSBGLAogICAgICAgICAgICAgICAgICAgIGRhcmsudGhlbWUgPSBGLAogICAgICAgICAgICAgICAgICAgIGRvLnJldHVybiA9VCwKICAgICAgICAgICAgICAgICAgICBuby5heGVzID0gVCkKCmZvciAoaSBpbiAxOmxlbmd0aChwbG90KSl7CiAgcGxvdFtbaV1dJGRhdGEgPC0gcGxvdFtbaV1dJGRhdGFbb3JkZXIocGxvdFtbaV1dJGRhdGEkZ2VuZSksXQp9CmBgYAoKYGBge3IgZmlnLmRpbT1jKDcsIDkuMyksIGZpZy5jYXA9ICJBcGljYWwgcHJvZ2VuaXRvcnMgZ2VuZSBleHByZXNzaW9uIn0KY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdFsxOjIwXSwgbmNvbCA9IDUpCmBgYAoKIyMgQmFzYWwgcHJvZ2VuaXRvcnMKCmBgYHtyfQpCUGdlbmVzIDwtIGMoIkVvbWVzIiwgIklnc2Y4IiwgIkluc20xIiwgIkVsYXZsMiIsICJFbGF2bDQiLAogICAgICAgICAgICAgIkhlczYiLCJHYWRkNDVnIiwgIk5ldXJvZzIiLCAiQnRnMiIsICJOZXVyb2cxIikKZ2VuZXMubGlzdCA8LSBsaXN0KEJQZ2VuZXMpCmVucmljaC5uYW1lIDwtICJCUF9zaWduYXR1cmUiCkhlbS5kYXRhIDwtIEFkZE1vZHVsZVNjb3JlKEhlbS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXMubGlzdCA9IGdlbmVzLmxpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lcy5wb29sID0gTlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4uYmluID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN0cmwuc2l6ZSA9IGxlbmd0aChnZW5lcy5saXN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZS5rID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbnJpY2gubmFtZSA9IGVucmljaC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tLnNlZWQgPSAxKQpgYGAKCmBgYHtyIGZpZy5zaG93PSdoaWRlJyB9CnBsb3QgPC0gRmVhdHVyZVBsb3Qob2JqZWN0ID0gSGVtLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMucGxvdCA9IEJQZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgY29scy51c2UgPSBjKCJncmV5OTAiLCBicmV3ZXIucGFsKDksIllsR25CdSIpKSwKICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24udXNlID0gInNwcmluZyIsCiAgICAgICAgICAgICAgICAgICAgbm8ubGVnZW5kID0gVCwKICAgICAgICAgICAgICAgICAgICBvdmVybGF5ID0gRiwKICAgICAgICAgICAgICAgICAgICBkYXJrLnRoZW1lID0gRiwKICAgICAgICAgICAgICAgICAgICBkby5yZXR1cm4gPVQsCiAgICAgICAgICAgICAgICAgICAgbm8uYXhlcyA9IFQpCgpmb3IgKGkgaW4gMTpsZW5ndGgocGxvdCkpewogIHBsb3RbW2ldXSRkYXRhIDwtIHBsb3RbW2ldXSRkYXRhW29yZGVyKHBsb3RbW2ldXSRkYXRhJGdlbmUpLF0KfQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg3LCA3KSwgZmlnLmNhcD0gIkJhc2FsIHByb2dlbml0b3JzIGdlbmUgZXhwcmVzc2lvbiJ9CmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHBsb3RbMToxMF0sIG5jb2wgPSA1KQpgYGAKCiMjIEVhcmx5IHBhbGxpYWwgbmV1cm9ucwoKYGBge3J9CkVOZ2VuZXMgPC0gYygiTWZhcDQiLCAiTmhsaDIiLCAiTmhsaDEiLCAiUHBwMXIxNGEiLCAiTmF2MSIsCiAgICAgICAgICAgICAiTmV1cm9kMSIsICJTb3JsMSIsICJTdmlwIiwgIkN4Y2wxMiIsICJUZW5tNCIsCiAgICAgICAgICAgICAiRGxsMyIsICJSZ21iIiwgIkNudG4yIiwgIlZhdDEiKQpnZW5lcy5saXN0IDwtIGxpc3QoRU5nZW5lcykKZW5yaWNoLm5hbWUgPC0gIkVOX3NpZ25hdHVyZSIKSGVtLmRhdGEgPC0gQWRkTW9kdWxlU2NvcmUoSGVtLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lcy5saXN0ID0gZ2VuZXMubGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzLnBvb2wgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbi5iaW4gPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3RybC5zaXplID0gbGVuZ3RoKGdlbmVzLmxpc3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlLmsgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVucmljaC5uYW1lID0gZW5yaWNoLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDEpCmBgYAoKYGBge3IgZmlnLnNob3c9J2hpZGUnIH0KcGxvdCA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBIZW0uZGF0YSwKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcy5wbG90ID0gRU5nZW5lcywKICAgICAgICAgICAgICAgICAgICBjb2xzLnVzZSA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbi51c2UgPSAic3ByaW5nIiwKICAgICAgICAgICAgICAgICAgICBuby5sZWdlbmQgPSBULAogICAgICAgICAgICAgICAgICAgIG92ZXJsYXkgPSBGLAogICAgICAgICAgICAgICAgICAgIGRhcmsudGhlbWUgPSBGLAogICAgICAgICAgICAgICAgICAgIGRvLnJldHVybiA9VCwKICAgICAgICAgICAgICAgICAgICBuby5heGVzID0gVCkKCmZvciAoaSBpbiAxOmxlbmd0aChwbG90KSl7CiAgcGxvdFtbaV1dJGRhdGEgPC0gcGxvdFtbaV1dJGRhdGFbb3JkZXIocGxvdFtbaV1dJGRhdGEkZ2VuZSksXQp9CmBgYAoKYGBge3IgZmlnLmRpbT1jKDcsIDguMyksIGZpZy5jYXA9ICJFYXJseSBwYWxsaWFsIG5ldXJvbnMgZ2VuZSBleHByZXNzaW9uIn0KY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdFsxOjE0XSwgbmNvbCA9IDUpCmBgYAoKIyMgTGF0ZSBwYWxsaWFsIG5ldXJvbnMKCmBgYHtyfQpMTmdlbmVzIDwtIGMoIlNuaGcxMSIsICJQY3NrMW4iLCAiTWFwdCIsICJJbmEiLCAiU3RtbjQiLAogICAgICAgICAgICAgIkdhcDQzIiwgIlR1YmIyYSIsICJMeTZoIiwiUHRwcmQiLCAiTWVmMmMiKQpnZW5lcy5saXN0IDwtIGxpc3QoTE5nZW5lcykKZW5yaWNoLm5hbWUgPC0gIkxOX3NpZ25hdHVyZSIKSGVtLmRhdGEgPC0gQWRkTW9kdWxlU2NvcmUoSGVtLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lcy5saXN0ID0gZ2VuZXMubGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzLnBvb2wgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbi5iaW4gPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3RybC5zaXplID0gbGVuZ3RoKGdlbmVzLmxpc3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlLmsgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVucmljaC5uYW1lID0gZW5yaWNoLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDEpCmBgYAoKYGBge3IgZmlnLnNob3c9J2hpZGUnIH0KcGxvdCA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBIZW0uZGF0YSwKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcy5wbG90ID0gTE5nZW5lcywKICAgICAgICAgICAgICAgICAgICBjb2xzLnVzZSA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbi51c2UgPSAic3ByaW5nIiwKICAgICAgICAgICAgICAgICAgICBuby5sZWdlbmQgPSBULAogICAgICAgICAgICAgICAgICAgIG92ZXJsYXkgPSBGLAogICAgICAgICAgICAgICAgICAgIGRhcmsudGhlbWUgPSBGLAogICAgICAgICAgICAgICAgICAgIGRvLnJldHVybiA9VCwKICAgICAgICAgICAgICAgICAgICBuby5heGVzID0gVCkKCmZvciAoaSBpbiAxOmxlbmd0aChwbG90KSl7CiAgcGxvdFtbaV1dJGRhdGEgPC0gcGxvdFtbaV1dJGRhdGFbb3JkZXIocGxvdFtbaV1dJGRhdGEkZ2VuZSksXQp9CmBgYAoKYGBge3IgZmlnLmRpbT1jKDcsIDcpLCBmaWcuY2FwPSAiTGF0ZSBwYWxsaWFsIG5ldXJvbnMgZ2VuZSBleHByZXNzaW9uIn0KY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdFsxOjEwXSwgbmNvbCA9IDUpCmBgYAoKIyMgTWVuaW5nZXMgY2VsbHMKCmBgYHtyfQpNZ2VuZXMgPC0gYygiTHVtIiwgIkxnYWxzMSIsICJGb3hjMSIpCmdlbmVzLmxpc3QgPC0gbGlzdChNZ2VuZXMpCmVucmljaC5uYW1lIDwtICJNZW5pbmdlc19zaWduYXR1cmUiCkhlbS5kYXRhIDwtIEFkZE1vZHVsZVNjb3JlKEhlbS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXMubGlzdCA9IGdlbmVzLmxpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lcy5wb29sID0gTlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4uYmluID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN0cmwuc2l6ZSA9IGxlbmd0aChnZW5lcy5saXN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZS5rID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbnJpY2gubmFtZSA9IGVucmljaC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tLnNlZWQgPSAxKQpgYGAKCmBgYHtyIGZpZy5zaG93PSdoaWRlJyB9CnBsb3QgPC0gRmVhdHVyZVBsb3Qob2JqZWN0ID0gSGVtLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMucGxvdCA9IE1nZW5lcywKICAgICAgICAgICAgICAgICAgICBjb2xzLnVzZSA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbi51c2UgPSAic3ByaW5nIiwKICAgICAgICAgICAgICAgICAgICBuby5sZWdlbmQgPSBULAogICAgICAgICAgICAgICAgICAgIG92ZXJsYXkgPSBGLAogICAgICAgICAgICAgICAgICAgIGRhcmsudGhlbWUgPSBGLAogICAgICAgICAgICAgICAgICAgIGRvLnJldHVybiA9VCwKICAgICAgICAgICAgICAgICAgICBuby5heGVzID0gVCkKCmZvciAoaSBpbiAxOmxlbmd0aChwbG90KSl7CiAgcGxvdFtbaV1dJGRhdGEgPC0gcGxvdFtbaV1dJGRhdGFbb3JkZXIocGxvdFtbaV1dJGRhdGEkZ2VuZSksXQp9CmBgYAoKYGBge3IgZmlnLmRpbT1jKDcsIDcpLCBmaWcuY2FwPSAiTWVuaW5nZXMgZ2VuZSBleHByZXNzaW9uIn0KY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdFsxOjNdLCBuY29sID0gMykKYGBgCiMjIEltbXVuZSBjZWxscwoKYGBge3J9CkltbXVuZWdlbmVzIDwtIGMoIkZjZXIxZyIsICJDMXFiIiwgIlR5cm9icCIpCmdlbmVzLmxpc3QgPC0gbGlzdChJbW11bmVnZW5lcykKZW5yaWNoLm5hbWUgPC0gIkltbXVuZV9zaWduYXR1cmUiCkhlbS5kYXRhIDwtIEFkZE1vZHVsZVNjb3JlKEhlbS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lcy5saXN0ID0gZ2VuZXMubGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXMucG9vbCA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG4uYmluID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjdHJsLnNpemUgPSBsZW5ndGgoZ2VuZXMubGlzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZS5rID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGVucmljaC5uYW1lID0gZW5yaWNoLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbS5zZWVkID0gMSkKYGBgCgpgYGB7ciBmaWcuc2hvdz0naGlkZScgfQpwbG90IDwtIEZlYXR1cmVQbG90KG9iamVjdCA9IEhlbS5kYXRhLAogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzLnBsb3QgPSBJbW11bmVnZW5lcywKICAgICAgICAgICAgICAgICAgICBjb2xzLnVzZSA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbi51c2UgPSAic3ByaW5nIiwKICAgICAgICAgICAgICAgICAgICBuby5sZWdlbmQgPSBULAogICAgICAgICAgICAgICAgICAgIG92ZXJsYXkgPSBGLAogICAgICAgICAgICAgICAgICAgIGRhcmsudGhlbWUgPSBGLAogICAgICAgICAgICAgICAgICAgIGRvLnJldHVybiA9VCwKICAgICAgICAgICAgICAgICAgICBuby5heGVzID0gVCkKCmZvciAoaSBpbiAxOmxlbmd0aChwbG90KSl7CiAgcGxvdFtbaV1dJGRhdGEgPC0gcGxvdFtbaV1dJGRhdGFbb3JkZXIocGxvdFtbaV1dJGRhdGEkZ2VuZSksXQp9CmBgYAoKYGBge3IgZmlnLmRpbT1jKDcsIDcpLCBmaWcuY2FwPSAiSW1tdW5lIGdlbmUgZXhwcmVzc2lvbiJ9CmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHBsb3RbMTozXSwgbmNvbCA9IDMpCmBgYAoKCgpgYGB7ciBmaWcuZGltPWMoNiwgOSl9CkZlYXR1cmVQbG90KG9iamVjdCA9IEhlbS5kYXRhLAogICAgICAgICAgICBmZWF0dXJlcy5wbG90ID0gYygiQVBfc2lnbmF0dXJlMSIsICJCUF9zaWduYXR1cmUxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVOX3NpZ25hdHVyZTEiLCAiTE5fc2lnbmF0dXJlMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZW5pbmdlc19zaWduYXR1cmUxIiwiSW1tdW5lX3NpZ25hdHVyZTEiKSwKICAgICAgICAgICAgY29scy51c2UgPSByZXYoYnJld2VyLnBhbCgxMCwiU3BlY3RyYWwiKSksCiAgICAgICAgICAgIHJlZHVjdGlvbi51c2UgPSAic3ByaW5nIiwKICAgICAgICAgICAgbm8ubGVnZW5kID0gVCwKICAgICAgICAgICAgb3ZlcmxheSA9IEYsCiAgICAgICAgICAgIGRhcmsudGhlbWUgPSBGLAogICAgICAgICAgICBuby5heGVzID0gVCkKYGBgCgojIFNhdmUgU2V1cmF0IG9iamVjdAoKYGBge3J9CnNhdmVSRFMoSGVtLmRhdGEsICIuLi9RQy5maWx0ZXJlZC5jZWxscy5SRFMiKQpgYGAKCiMgU2Vzc2lvbiBJbmZvCgpgYGB7cn0KI2RhdGUKZm9ybWF0KFN5cy50aW1lKCksICIlZCAlQiwgJVksICVILCVNIikKCiNQYWNrYWdlcyB1c2VkCnNlc3Npb25JbmZvKCkKYGBgCg==