Load Telley et al 2019 data and order cells along pseudo-maturation axis
To define temporally versus spatially regulated genes, we used dorso-pallial apical progenitors scRNAseq data obtained by facs sorting 1H after flashtag injection from :
Telley L, Agirman G et al. Temporal patterning of apical progenitors and their daughter neurons in the developing neocortex. Science 2019 May 10;364(6440).
The R script used for the preprocessing of the data can be find here
##
## E12.1H E13.1H E14.1H E15.1H
## 178 202 124 221
Run PCA
# Select all DEG between sampling days as input to PCA
Allmarkers <- FindAllMarkers(object = Telley.data,
min.pct = 0.3,
logfc.threshold = 0.6,
print.bar = F,
only.pos = T)
DE.genes <- unique(Allmarkers$gene)
# PCA with Seurat
Telley.data <- RunPCA(object = Telley.data,
pcs.compute = 10,
pc.genes = DE.genes,
do.print = F,
pcs.print = 1,
genes.print = 1,
weight.by.var=T)
PrintPCAParams(Telley.data)
## Parameters used in latest PCA calculation run on: 2020-11-30 11:03:06
## =============================================================================
## PCs computed Genes used in calculation PCs Scaled by Variance Explained
## 10 815 TRUE
## -----------------------------------------------------------------------------
## rev.pca
## FALSE
## -----------------------------------------------------------------------------
## Full gene list can be accessed using
## GetCalcParam(object = object, calculation = "RunPCA", parameter = "pc.genes")
Fit the principal curve over the first 6 PCs
## Starting curve---distance^2: 20282698
## Iteration 1---distance^2: 26243.03
## Iteration 2---distance^2: 25451.13
## Iteration 3---distance^2: 24542.64
## Iteration 4---distance^2: 23847.34
## Iteration 5---distance^2: 23528.5
## Iteration 6---distance^2: 23464.76
## Iteration 7---distance^2: 23435.29
## Iteration 8---distance^2: 23404.87
## Iteration 9---distance^2: 23372.66
## Iteration 10---distance^2: 23359.4
# Plot the principal curve
p1 <- ggplot(data, aes(PC1, PC2)) +
geom_point(aes(color=Cluster), size=3, shape=16) +
geom_line(data=pc.line, color='red', size=0.77) +
scale_color_manual(values=c("#bdd8ef", "#6db8e2", "#357ebc", "#1c4896"))
# Plot pseudo-maturation score
cols <- colorRampPalette(brewer.pal(n =11, name = "Spectral"))(100)
p2 <- ggplot(data, aes(PC1, PC2)) +
geom_point(aes(color=PseudoMaturation.score), size=3, shape=16) +
scale_color_gradientn(colours=rev(cols), name='Speudotime score') +
geom_line(data=pc.line, color='red', size=0.77)
p1 + p2
Load E12 dataset
Filter, normalize and scale the expression matrix
# Remove non epressed genes
num.cells <- Matrix::rowSums(E12.AP.data@data > 0)
genes.use <- names(x = num.cells[which(x = num.cells >= 20)])
E12.AP.data@raw.data <- E12.AP.data@raw.data[genes.use, ]
E12.AP.data@data <- E12.AP.data@data[genes.use, ]
# Normalize the counts matrix
E12.AP.data <- NormalizeData(object = E12.AP.data,
normalization.method = "LogNormalize",
scale.factor = round(median(E12.AP.data@meta.data$nUMI)), display.progress = F)
# Scale expression matrix
E12.AP.data <- ScaleData(object = E12.AP.data,
vars.to.regress = c("CC.Difference", "percent.ribo" ,"nUMI"),
display.progress = F)
# Find most variable genes
E12.AP.data <- FindVariableGenes(object = E12.AP.data,
mean.function = ExpMean,
dispersion.function = LogVMR,
x.low.cutoff = 0.0125,
x.high.cutoff = 3,
y.cutoff = 1,
do.plot = F,
display.progress = F)
length(E12.AP.data@var.genes)
## [1] 1129
Predict E12 AP pseudo-maturation score with a regression model trainned on Telley 2019 dataset
Train a random forest regression model on Telley 2019 data
We reoptimize the regression model by selecting the 100 most importante features of the first model
# Select the top 100 importante features
FeatureImportance <- sort(Telley.model$finalModel$variable.importance, decreasing = T)
SelectedFeatures <- names(FeatureImportance[1:100])
# Re-train the model with the restricted features
Telley.model <- train(PseudoMaturation.score~.,
data = model.data[,colnames(model.data) %in% c(SelectedFeatures,"PseudoMaturation.score")],
method = "ranger",
trControl=trctrl,
importance="permutation",
tuneGrid = data.frame(mtry=50, min.node.size = 5, splitrule="variance"))
Predict maturation score of E12 pallial apical progenitors
# Plot E12 predicted maturation score by cluster
domaine <- factor(E12.plot.df$Domaine, level = c("Ventral.Pallium","lateral.Pallium.1","lateral.Pallium.2","Dorsal.Pallium"))
ggplot(E12.plot.df, aes(x= domaine, y= PseudoMaturation.score, fill=Domaine)) +
geom_boxplot(notch=TRUE) +
geom_jitter(color="black", size=0.8, alpha=0.9) +
scale_fill_manual(values = tolower(c("#68B041", "#E3C148", "#B7D174", "#E46B6B"))) +
xlab("")
Decomposition of spatial vs. temoral aspect of progenitor cells identity
Smooth expression over pseudo-DV and pseudo maturation
Find all variable genes along the pseudo-DV axis in E12 AP
# Transfert metadata
meta.data <- data.frame(barcode = rownames(E12.AP.data@meta.data),
Cluster = E12.AP.data@meta.data$old.ident,
DorsoVentral.Score = E12.AP.data@meta.data$DorsoVentral.Score,
CellcyclePhase = E12.AP.data@meta.data$Phase,
row.names = rownames(E12.AP.data@meta.data))
Annot.data <- new('AnnotatedDataFrame', data = meta.data)
# Transfert count data
count.data = data.frame(gene_short_name = rownames(E12.AP.data@raw.data),
row.names = rownames(E12.AP.data@raw.data))
feature.data <- new('AnnotatedDataFrame', data = count.data)
# Create the CellDataSet object
E12.gbm_cds <- newCellDataSet(as.matrix(E12.AP.data@raw.data),
phenoData = Annot.data,
featureData = feature.data,
lowerDetectionLimit = 1,
expressionFamily = negbinomial())
Find all variable genes along the pseudo-maturation axis in Telley 2019 APs
# Transfert metadata
meta.data <- data.frame(barcode = rownames(Telley.data@meta.data),
Cluster = Telley.data@meta.data$orig.ident,
PseudoMaturation.score = Telley.data@meta.data$PseudoMaturation.score,
CellcyclePhase = Telley.data@meta.data$Phase,
row.names = rownames(Telley.data@meta.data))
Annot.data <- new('AnnotatedDataFrame', data = meta.data)
# Transfert count data
count.data = data.frame(gene_short_name = rownames(Telley.data@raw.data),
row.names = rownames(Telley.data@raw.data))
feature.data <- new('AnnotatedDataFrame', data = count.data)
# Create the CellDataSet object
Telley.gbm_cds <- newCellDataSet(as.matrix(Telley.data@raw.data),
phenoData = Annot.data,
featureData = feature.data,
lowerDetectionLimit = 1,
expressionFamily = negbinomial())
# Exclude cell cycle associated, mt and ribo ribo
CCgenes <- as.character(read.table("./Progenitors/CellCycleGenes.csv", sep = "\t", header = F)[,1])
GenesToRemove <- c(grep(pattern = "(^Rpl|^Rps|^Mrp)", x = row.names(Telley.data@data), value = TRUE),
grep(pattern = "^mt-", x = row.names(Telley.data@data), value = TRUE),
"Xist", CCgenes)
Input.genes <- row.names(Telley.data@data)[!row.names(Telley.data@data) %in% GenesToRemove]
# We select all genes which are differentialy expressed between E12 and E15 AP
E12vsE15.DEgenes <- FindMarkers(object = Telley.data,
ident.1 = "E12.1H",
ident.2 = "E15.1H",
min.pct = 0.25,
logfc.threshold = 0.4,
print.bar = F,
only.pos = F,
genes.use = Input.genes)
E12vsE15.DEgenes$Gene <- rownames(E12vsE15.DEgenes)
E12vsE15.DEgenes.filtered <- E12vsE15.DEgenes %>% filter(p_val_adj < 1e-3) %>% pull(Gene)
Identify temporal genes as highly anti-correlating between the two datasets
Intersect the two smoothed expression matrices
Cluster temporal genes according to their smoothed expression inverse correlation between the two datasets
# Build temporal genes dendrogram
cor.mat <- cor(x= t(E12.Smooth.Common[,200:1]), y= t(Telley.Smooth.Common), method = "pearson")
dst <- dist(cor.mat, method = "euclidean")
hc <- hclust(dst, method = "complete")
# Find 3 cluster based on hierarchical clustering
clusters <- cutree(hc, k=3)
Temporal.gene.clusters <- data.frame(Gene = names(clusters),
Gene.Clusters = as.numeric(clusters)) %>% arrange(Gene.Clusters)
row.names(Temporal.gene.clusters) <- Temporal.gene.clusters$Gene
Temporal.gene.clusters$Gene.Clusters <- paste0("Clust.",Temporal.gene.clusters$Gene.Clusters)
write.table(Temporal.gene.clusters, "./Progenitors/Temporal.gene.clusters.csv", sep = ";", quote = F)
pheatmap(cor.mat,
cluster_rows= hc,
scale = "none",
annotation_row = Temporal.gene.clusters %>% dplyr::select(Gene.Clusters),
annotation_colors = list(Gene.Clusters = c(Clust.1 ="#4784a2" , Clust.2="#ebcb2e",Clust.3="#cc3a1b")),
show_colnames = T,
show_rownames = T,
border_color = NA,
color = rev(brewer.pal(8,"RdBu")),
main = "Genes inverse pearson correlation matrix")
Plot correlation between smoothed expression profiles
Domaine.E12.AP <- E12.AP.data@meta.data %>% select(DorsoVentral.Score, Domaine)
Domaine.boundaries <- aggregate(DorsoVentral.Score ~ Domaine, Domaine.E12.AP, max) %>% arrange(DorsoVentral.Score) %>% pull(DorsoVentral.Score)
Domaine.Ident <- sapply(E12.new_data$DorsoVentral.Score,
function(x){ if(x<Domaine.boundaries[1]){ x = "Ventral.Pallium"
} else if(x> Domaine.boundaries[1] & x< Domaine.boundaries[2]){ x ="lateral.Pallium.1"
} else if(x> Domaine.boundaries[3]){ x = "Dorsal.Pallium"
} else x="lateral.Pallium.2"})
Domaines.Clust <- data.frame(Domaine.Ident)
pheatmap(t(Smoothed.point.cor),
cluster_rows = F,
cluster_cols = F,
annotation_col = Domaines.Clust,
gaps_col = cumsum(as.numeric(table(Domaines.Clust$Domaine.Ident))[c(4,2,3,1)]),
annotation_colors = list(Domaine.Ident = c(Ventral.Pallium = "#e46b6b",lateral.Pallium.1 = "#e3c148", lateral.Pallium.2 = "#b7d174",Dorsal.Pallium = "#68b041")),
show_colnames = F,
show_rownames = F,
color = colorRampPalette(rev(brewer.pal(n = 10, name = "RdBu")))(100),
main = "Pearson's Correlation matrix")
Extract E12 spatial domain genes which do not show E12/E15 difference in Telley data
# Build temporal genes dendrogram
cor.mat <- cor(t(E12.smooth.specific), method = "spearman")
dst <- as.dist(1 - cor.mat)
hc <- hclust(dst, method = "ward.D") #"ward.D"
# Seriate the dendrogram
dend <- as.dendrogram(hc)
dend <- dendextend::seriate_dendrogram(dend, dst, method="GW")
# Find 2 cluster based on hierarchical clustering
clusters <- cutree(hc, k=4)
rownames(DV.Axis.genes.FDR.filtered) <- DV.Axis.genes.FDR.filtered$gene_short_name
DV.Axis.genes.FDR.filtered <- DV.Axis.genes.FDR.filtered[names(clusters),]
Spatial.gene.clusters <- data.frame(Gene = names(clusters),
pval= DV.Axis.genes.FDR.filtered$pval,
qval= DV.Axis.genes.FDR.filtered$qval,
num_cells_expressed= DV.Axis.genes.FDR.filtered$num_cells_expressed,
Gene.Clusters = as.numeric(clusters)) %>% arrange(Gene.Clusters)
row.names(Spatial.gene.clusters) <- Spatial.gene.clusters$Gene
Spatial.gene.clusters$Gene.Clusters <- paste0("Clust.",Spatial.gene.clusters$Gene.Clusters)
write.table(Spatial.gene.clusters, "./Progenitors/Spatial.gene.clusters.csv", sep = ";", quote = F)
anno.col <- list(Domaine.Ident = c(Ventral.Pallium = "#e46b6b",lateral.Pallium.1 = "#e3c148", lateral.Pallium.2 = "#b7d174",Dorsal.Pallium = "#68b041"),
Gene.Clusters = c(Clust.1 ="#4784a2" , Clust.2="#ebcb2e", Clust.3="#cc3a1b",Clust.4="#4cabdc",Clust.5="#3ca73f"))
pheatmap(E12.smooth.specific,
cluster_rows= as.hclust(dend),
scale = "row",
cluster_cols = F,
annotation_col = Domaines.Clust,
gaps_col = cumsum(as.numeric(table(Domaines.Clust$Domaine.Ident))[c(4,2,3,1)]),
annotation_row = Spatial.gene.clusters %>% dplyr::select(Gene.Clusters),
annotation_colors = anno.col,
show_colnames = F,
show_rownames = F,
color = rev(brewer.pal(11,"RdBu")),
breaks = seq(-2.5,2.5, length.out = 11),
main = "Spatial smoothed expression along pseudoDV axis")
Plot the representative trends
dataE12 <- data.frame(Cluster = paste0("Cluster",as.character(E12.AP.data@ident)),
DorsoVentral.Score = E12.AP.data@meta.data$DorsoVentral.Score,
Zbtb20 = E12.AP.data@data["Zbtb20",],
Lrrn1 = E12.AP.data@data["Lrrn1",],
Lmo4 = E12.AP.data@data["Lmo4",],
Mpped2 =E12.AP.data@data["Mpped2",])
dataTelley <- data.frame(Cluster = paste0("Cluster",as.character(Telley.data@ident)),
PseudoMaturation.score = Telley.data@meta.data$PseudoMaturation.score,
Zbtb20 = Telley.data@data["Zbtb20",],
Lrrn1 = Telley.data@data["Lrrn1",],
Lmo4 = Telley.data@data["Lmo4",],
Mpped2 = Telley.data@data["Mpped2",])
ggplot(data=dataE12, aes(x=DorsoVentral.Score, y=Zbtb20, color=Cluster)) +
geom_point() +
scale_color_manual(values= tolower(c("#68B041", "#E3C148", "#B7D174", "#E46B6B"))) +
geom_smooth(method="loess", n= 30, color="red", fill="grey") +
ggtitle("Zbtb20") +
ylim(0,NA) +
theme(legend.position="none")
ggplot(data=dataE12, aes(x=DorsoVentral.Score, y=Lrrn1, color=Cluster)) +
geom_point() +
scale_color_manual(values= tolower(c("#68B041", "#E3C148", "#B7D174", "#E46B6B"))) +
geom_smooth(method="loess", n= 30, color="red", fill="grey") +
ggtitle("Lrrn1") +
ylim(0,NA) +
theme(legend.position="none")
ggplot(data=dataE12, aes(x=DorsoVentral.Score, y=Lmo4, color=Cluster)) +
geom_point() +
scale_color_manual(values= tolower(c("#68B041", "#E3C148", "#B7D174", "#E46B6B"))) +
geom_smooth(method="loess", n= 30, color="red", fill="grey") +
ggtitle("Lmo4") +
ylim(0,NA) +
theme(legend.position="none")
ggplot(data=dataE12, aes(x=DorsoVentral.Score, y=Mpped2, color=Cluster)) +
geom_point() +
scale_color_manual(values= tolower(c("#68B041", "#E3C148", "#B7D174", "#E46B6B"))) +
geom_smooth(method="loess", n= 30, color="red", fill="grey") +
ggtitle("Mpped2") +
ylim(0,NA) +
theme(legend.position="none")
ggplot(data=dataTelley, aes(x=PseudoMaturation.score, y=Zbtb20, color=Cluster)) +
geom_point() +
scale_color_manual(values= c("#bdd8ef", "#6db8e2", "#357ebc", "#1c4896")) +
geom_smooth(method="loess", n= 30, color="red", fill="grey",span = 0.5) +
ggtitle("Zbtb20") +
ylim(0,NA) +
theme(legend.position="none")
ggplot(data=dataTelley, aes(x=PseudoMaturation.score, y=Lrrn1, color=Cluster)) +
geom_point() +
scale_color_manual(values= c("#bdd8ef", "#6db8e2", "#357ebc", "#1c4896")) +
geom_smooth(method="loess", n= 30, color="red", fill="grey") +
ggtitle("Lrrn1") +
ylim(0,NA) +
theme(legend.position="none")
ggplot(data=dataTelley, aes(x=PseudoMaturation.score, y=Lmo4, color=Cluster)) +
geom_point() +
scale_color_manual(values= c("#bdd8ef", "#6db8e2", "#357ebc", "#1c4896")) +
geom_smooth(method="loess", n= 30, color="red", fill="grey") +
ggtitle("Lmo4") +
ylim(0,NA) +
theme(legend.position="none")
ggplot(data=dataTelley, aes(x=PseudoMaturation.score, y=Mpped2, color=Cluster)) +
geom_point() +
scale_color_manual(values= c("#bdd8ef", "#6db8e2", "#357ebc", "#1c4896")) +
geom_smooth(method="loess", n= 30, color="red", fill="grey") +
ggtitle("Mpped2") +
ylim(0,NA) +
theme(legend.position="none")
Session Info
## [1] "30 novembre, 2020, 11,09"
## 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] splines stats4 parallel stats graphics grDevices utils
## [8] datasets methods base
##
## other attached packages:
## [1] RColorBrewer_1.1-2 patchwork_0.0.1 ggExtra_0.9
## [4] gridExtra_2.3 pheatmap_1.0.12 dplyr_0.8.3
## [7] monocle_2.14.0 DDRTree_0.1.5 irlba_2.3.3
## [10] VGAM_1.1-2 Biobase_2.46.0 BiocGenerics_0.32.0
## [13] caret_6.0-84 lattice_0.20-41 princurve_2.1.4
## [16] Seurat_2.3.4 Matrix_1.2-17 cowplot_1.0.0
## [19] ggplot2_3.2.1
##
## loaded via a namespace (and not attached):
## [1] snow_0.4-3 backports_1.1.5 Hmisc_4.3-0
## [4] plyr_1.8.4 igraph_1.2.5 lazyeval_0.2.2
## [7] densityClust_0.3 fastICA_1.2-2 digest_0.6.25
## [10] foreach_1.4.7 htmltools_0.5.0 viridis_0.5.1
## [13] lars_1.2 gdata_2.18.0 magrittr_1.5
## [16] checkmate_1.9.4 cluster_2.1.0 gclus_1.3.2
## [19] mixtools_1.1.0 ROCR_1.0-7 limma_3.42.0
## [22] recipes_0.1.7 gower_0.2.1 matrixStats_0.55.0
## [25] docopt_0.6.1 R.utils_2.9.0 colorspace_1.4-1
## [28] ggrepel_0.8.1 xfun_0.18 sparsesvd_0.2
## [31] crayon_1.3.4 jsonlite_1.7.0 zeallot_0.1.0
## [34] survival_2.44-1.1 zoo_1.8-6 iterators_1.0.12
## [37] ape_5.3 glue_1.4.1 registry_0.5-1
## [40] gtable_0.3.0 ipred_0.9-9 kernlab_0.9-29
## [43] prabclus_2.3-1 DEoptimR_1.0-8 scales_1.1.0
## [46] bibtex_0.4.2 miniUI_0.1.1.1 Rcpp_1.0.5
## [49] metap_1.1 dtw_1.21-3 xtable_1.8-4
## [52] viridisLite_0.3.0 htmlTable_1.13.2 reticulate_1.13
## [55] foreign_0.8-72 bit_4.0.4 proxy_0.4-23
## [58] mclust_5.4.5 SDMTools_1.1-221.1 Formula_1.2-3
## [61] tsne_0.1-3 lava_1.6.6 prodlim_2019.11.13
## [64] htmlwidgets_1.5.1 httr_1.4.1 FNN_1.1.3
## [67] gplots_3.0.1.1 fpc_2.2-3 acepack_1.4.1
## [70] modeltools_0.2-22 ica_1.0-2 farver_2.0.1
## [73] pkgconfig_2.0.3 R.methodsS3_1.7.1 flexmix_2.3-15
## [76] nnet_7.3-14 labeling_0.3 later_1.0.0
## [79] tidyselect_0.2.5 rlang_0.4.7 reshape2_1.4.3
## [82] munsell_0.5.0 tools_3.6.3 generics_0.0.2
## [85] ranger_0.11.2 ggridges_0.5.1 fastmap_1.0.1
## [88] evaluate_0.14 stringr_1.4.0 yaml_2.2.1
## [91] npsurv_0.4-0 ModelMetrics_1.2.2 knitr_1.26
## [94] bit64_4.0.2 fitdistrplus_1.0-14 robustbase_0.93-5
## [97] caTools_1.17.1.2 purrr_0.3.3 RANN_2.6.1
## [100] dendextend_1.12.0 pbapply_1.4-2 nlme_3.1-141
## [103] mime_0.7 slam_0.1-46 R.oo_1.23.0
## [106] hdf5r_1.3.2.9000 compiler_3.6.3 rstudioapi_0.11
## [109] png_0.1-7 e1071_1.7-2 lsei_1.2-0
## [112] tibble_2.1.3 stringi_1.4.6 highr_0.8
## [115] HSMMSingleCell_1.6.0 vctrs_0.2.0 pillar_1.4.2
## [118] lifecycle_0.1.0 combinat_0.0-8 Rdpack_0.11-0
## [121] lmtest_0.9-37 data.table_1.12.6 bitops_1.0-6
## [124] seriation_1.2-9 gbRd_0.4-11 httpuv_1.5.2
## [127] R6_2.4.1 latticeExtra_0.6-28 TSP_1.1-10
## [130] promises_1.1.0 KernSmooth_2.23-15 codetools_0.2-16
## [133] MASS_7.3-53 gtools_3.8.1 assertthat_0.2.1
## [136] withr_2.1.2 qlcMatrix_0.9.7 diptest_0.75-7
## [139] doSNOW_1.0.18 grid_3.6.3 rpart_4.1-15
## [142] timeDate_3043.102 tidyr_1.0.0 class_7.3-17
## [145] rmarkdown_2.5 segmented_1.0-0 Rtsne_0.15
## [148] shiny_1.4.0 lubridate_1.7.4 base64enc_0.1-3
LS0tCnRpdGxlOiAiVGVtcG9yYWwgYW5kIHNwYXRpYWwgYXNwZWN0cyBvZiBwcm9nZW5pdG9ycyBpZGVudGl0eSIKYXV0aG9yOgogICAtIE1hdHRoaWV1IE1vcmVhdV5bSW5zdGl0dXRlIG9mIFBzeWNoaWF0cnkgYW5kIE5ldXJvc2NpZW5jZSBvZiBQYXJpcywgSU5TRVJNIFUxMjY2LCA3NTAxNCwgUGFyaXMsIEZyYW5jZSwgbWF0dGhpZXUubW9yZWF1QGluc2VybS5mcl0gWyFbXShodHRwczovL29yY2lkLm9yZy9zaXRlcy9kZWZhdWx0L2ZpbGVzL2ltYWdlcy9vcmNpZF8xNngxNi5wbmcpXShodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItMjU5Mi0yMzczKQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIGRmX3ByaW50OiB0aWJibGUKICAgIGhpZ2hsaWdodDogaGFkZG9jawogICAgaW5jbHVkZXM6CiAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgIHRoZW1lOiBjb3NtbwogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHllcwotLS0KCmBgYHtjc3MsIGVjaG89RkFMU0V9CmgxIHsKICBmb250LXNpemU6IDM0cHg7CiAgbWFyZ2luLXRvcDogMnJlbTsKICBtYXJnaW4tYm90dG9tOiAxcmVtOwogIGNvbG9yOiAjZTY0ZDAwOwogIHRleHQtZGVjb3JhdGlvbjogbm9uZTsKfQpoMS50aXRsZSB7CiAgZm9udC1zaXplOiA0MHB4OwogIG1hcmdpbi10b3A6IDJyZW07CiAgbWFyZ2luLWJvdHRvbTogMXJlbTsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgdGV4dC1kZWNvcmF0aW9uOiBub25lOwogIGNvbG9yOiAjMDAwMDAwOwp9CmgyIHsKICBmb250LXNpemU6IDMwcHg7CiAgbWFyZ2luLXRvcDogMnJlbTsKICBtYXJnaW4tYm90dG9tOiAxcmVtOwogIGNvbG9yOiAjMDAwMDAwOwp9CmgzIHsKICBmb250LXNpemU6IDI0cHg7CiAgbWFyZ2luLXRvcDogMnJlbTsKICBtYXJnaW4tYm90dG9tOiAxcmVtOwogIGNvbG9yOiAjMDAwMDAwOwp9Cmg0IHsKICBmb250LXNpemU6IDIwcHg7CiAgbWFyZ2luLXRvcDogMnJlbTsKICBtYXJnaW4tYm90dG9tOiAxcmVtOwogIGNvbG9yOiAjMDAwMDAwOwp9Cmg1IHsKICBmb250LXNpemU6IDE4cHg7CiAgbWFyZ2luLXRvcDogMnJlbTsKICBtYXJnaW4tYm90dG9tOiAxcmVtOwogIGNvbG9yOiAjMDAwMDAwOwp9Cgouc2Nyb2xsLTEwMCB7CiAgbWF4LWhlaWdodDogMjAwcHg7CiAgb3ZlcmZsb3cteTogYXV0bzsKICBiYWNrZ3JvdW5kLWNvbG9yOiBpbmhlcml0Owp9CgpwIHsKICBmb250LXNpemU6IDE2cHg7Cn0KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBmaWcuYWxpZ24gPSAnY2VudGVyJywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSkKYGBgCgojIExvYWQgbGlicmFyaWVzCgpgYGB7ciB9CiMgTG9hZCBsaWJyYXJ5CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KHByaW5jdXJ2ZSkKbGlicmFyeShjYXJldCkKbGlicmFyeShtb25vY2xlKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGdnRXh0cmEpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoUkNvbG9yQnJld2VyKQoKI1NldCBnZ3Bsb3QgdGhlbWUgYXMgY2xhc3NpYwp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQpgYGAKCiMgTG9hZCBUZWxsZXkgZXQgYWwgMjAxOSBkYXRhIGFuZCBvcmRlciBjZWxscyBhbG9uZyBwc2V1ZG8tbWF0dXJhdGlvbiBheGlzCgpUbyBkZWZpbmUgdGVtcG9yYWxseSB2ZXJzdXMgc3BhdGlhbGx5IHJlZ3VsYXRlZCBnZW5lcywgd2UgdXNlZCAqKmRvcnNvLXBhbGxpYWwgYXBpY2FsIHByb2dlbml0b3JzKiogc2NSTkFzZXEgZGF0YSBvYnRhaW5lZCBieSBmYWNzIHNvcnRpbmcgMUggYWZ0ZXIgZmxhc2h0YWcgaW5qZWN0aW9uIGZyb20gOgoKPiBUZWxsZXkgTCwgQWdpcm1hbiBHICpldCBhbC4qICoqVGVtcG9yYWwgcGF0dGVybmluZyBvZiBhcGljYWwgcHJvZ2VuaXRvcnMgYW5kIHRoZWlyIGRhdWdodGVyIG5ldXJvbnMgaW4gdGhlIGRldmVsb3BpbmcgbmVvY29ydGV4LioqIFtTY2llbmNlIDIwMTkgTWF5IDEwOzM2NCg2NDQwKS5dKGh0dHBzOi8vc2NpZW5jZS5zY2llbmNlbWFnLm9yZy9jb250ZW50LzM2NC82NDQwL2VhYXYyNTIyLmxvbmcpCgoKPiBUaGUgUiBzY3JpcHQgdXNlZCBmb3IgdGhlIHByZXByb2Nlc3Npbmcgb2YgdGhlIGRhdGEgY2FuIGJlIGZpbmQgW2hlcmVdKGh0dHBzOi8vbWF0dGhpZXV4bW9yZWF1LmdpdGh1Yi5pby9FYXJseVBhbGxpYWxOZXVyb2dlbmVzaXMvaHRtbC1SZXBvcnRzL1RlbGxleTIwMTlQcmVwcm9jZXNzaW5nLmh0bWwpCgpgYGB7cn0KIyBMb2FkIHRoZSBmdWxsIGFubm90YXRlZCBkYXRhc2V0ClRlbGxleS5kYXRhIDwtIHJlYWRSRFMoIi4vVGVsbGV5MjAxOWRhdGEvVGVsbGV5MjAxOS5SRFMiKQpUZWxsZXkuZGF0YSA8LSBTZXRBbGxJZGVudChUZWxsZXkuZGF0YSwgaWQgPSAib3JpZy5pZGVudCIpCgp0YWJsZShUZWxsZXkuZGF0YUBpZGVudCkKYGBgCgojIyBSdW4gUENBCgpgYGB7cn0KIyBTZWxlY3QgYWxsIERFRyBiZXR3ZWVuIHNhbXBsaW5nIGRheXMgYXMgaW5wdXQgdG8gUENBCkFsbG1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMob2JqZWN0ID0gVGVsbGV5LmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLnBjdCA9IDAuMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dmYy50aHJlc2hvbGQgPSAwLjYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbnQuYmFyID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmx5LnBvcyA9IFQpCgpERS5nZW5lcyA8LSB1bmlxdWUoQWxsbWFya2VycyRnZW5lKQoKIyBQQ0Egd2l0aCBTZXVyYXQKVGVsbGV5LmRhdGEgPC0gUnVuUENBKG9iamVjdCA9IFRlbGxleS5kYXRhLAogICAgICAgICAgICAgICAgICBwY3MuY29tcHV0ZSA9IDEwLAogICAgICAgICAgICAgICAgICBwYy5nZW5lcyA9IERFLmdlbmVzLAogICAgICAgICAgICAgICAgICBkby5wcmludCA9IEYsCiAgICAgICAgICAgICAgICAgIHBjcy5wcmludCA9IDEsCiAgICAgICAgICAgICAgICAgIGdlbmVzLnByaW50ID0gMSwKICAgICAgICAgICAgICAgICAgd2VpZ2h0LmJ5LnZhcj1UKQoKUHJpbnRQQ0FQYXJhbXMoVGVsbGV5LmRhdGEpCmBgYAoKYGBge3IgZmlnLmRpbT1jKDUuMywgNCl9CiMgU2VsZWN0IHBjcyBieSAlIHZhcmlhbmNlIGV4cGxhaW5lZAplaWdzIDwtIChUZWxsZXkuZGF0YUBkciRwY2FAc2RldileMgpQZXJjZW50VkFyIDwtIGRhdGEuZnJhbWUoUGVyY2VudFZBciA9IGVpZ3MqMTAwIC8gc3VtKGVpZ3MpLAogICAgICAgICAgICAgICAgICAgICAgICAgUENzID0gMTpsZW5ndGgoZWlncykpCgpnZ3Bsb3QoUGVyY2VudFZBcixhZXMoeD0gYXMuZmFjdG9yKFBDcykseT1QZXJjZW50VkFyKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA1LGxpbmV0eXBlID0gMiwgY29sb3VyPSJyZWQiKQpgYGAKCmBgYHtyfQojIEtlZXAgb25seSBQQ3MgZXhwbGFpbmluZyA+IDUlIG9mIHRoZSB2YXJpYW5jZSBvdmVyIHRoZSAxMCBmaXJzdApQQ3MgPC0gUGVyY2VudFZBciAlPiUgZmlsdGVyKFBlcmNlbnRWQXIgPiA1KSAlPiUgcHVsbChQQ3MpCmBgYAoKCiMjIEZpdCB0aGUgcHJpbmNpcGFsIGN1cnZlIG92ZXIgdGhlIGZpcnN0IDYgUENzCgpgYGB7cn0KZGF0YSA8LSBhcy5kYXRhLmZyYW1lKFRlbGxleS5kYXRhQGRyJHBjYUBjZWxsLmVtYmVkZGluZ3NbLFBDc10pCgpmaXQgPC0gcHJpbmNpcGFsX2N1cnZlKGFzLm1hdHJpeChkYXRhWyxQQ3NdKSwKICAgICAgICAgICAgICAgICAgICAgICBzbW9vdGhlcj0nbG93ZXNzJywKICAgICAgICAgICAgICAgICAgICAgICB0cmFjZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgIGYgPSAuNSwKICAgICAgICAgICAgICAgICAgICAgICBzdHJldGNoPTApCmBgYAoKYGBge3J9ClBzZXVkb01hdHVyYXRpb24uc2NvcmUgPC0gZml0JGxhbWJkYS9tYXgoZml0JGxhbWJkYSkKcGMubGluZSA8LSBhcy5kYXRhLmZyYW1lKGZpdCRzW29yZGVyKGZpdCRsYW1iZGEpLF0pCgpkYXRhJENsdXN0ZXIgPC0gVGVsbGV5LmRhdGFAbWV0YS5kYXRhJG9yaWcuaWRlbnQKZGF0YSRQc2V1ZG9NYXR1cmF0aW9uLnNjb3JlIDwtIFBzZXVkb01hdHVyYXRpb24uc2NvcmUKClRlbGxleS5kYXRhQG1ldGEuZGF0YSRQc2V1ZG9NYXR1cmF0aW9uLnNjb3JlIDwtIFBzZXVkb01hdHVyYXRpb24uc2NvcmUKYGBgCgpgYGB7ciBmaWcuZGltPWMoMTIsIDQpfQojIFBsb3QgdGhlIHByaW5jaXBhbCBjdXJ2ZQpwMSA8LSBnZ3Bsb3QoZGF0YSwgYWVzKFBDMSwgUEMyKSkgKyAKICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9Q2x1c3RlciksIHNpemU9Mywgc2hhcGU9MTYpICsKICAgICAgZ2VvbV9saW5lKGRhdGE9cGMubGluZSwgY29sb3I9J3JlZCcsIHNpemU9MC43NykgKwogICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiNiZGQ4ZWYiLCAiIzZkYjhlMiIsICIjMzU3ZWJjIiwgIiMxYzQ4OTYiKSkKCiMgUGxvdCBwc2V1ZG8tbWF0dXJhdGlvbiBzY29yZQpjb2xzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbChuID0xMSwgbmFtZSA9ICJTcGVjdHJhbCIpKSgxMDApCgpwMiA8LSBnZ3Bsb3QoZGF0YSwgYWVzKFBDMSwgUEMyKSkgKwogICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPVBzZXVkb01hdHVyYXRpb24uc2NvcmUpLCBzaXplPTMsIHNoYXBlPTE2KSArIAogICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzPXJldihjb2xzKSwgbmFtZT0nU3BldWRvdGltZSBzY29yZScpICsKICAgICBnZW9tX2xpbmUoZGF0YT1wYy5saW5lLCBjb2xvcj0ncmVkJywgc2l6ZT0wLjc3KQoKcDEgKyBwMgpgYGAKCmBgYHtyIGZpZy5kaW09Yyg1LjMsIDQpfQojIFBsb3QgZGVuc2l0eSBvbiBzcGV1ZG90aW1lCmdncGxvdChkYXRhPWRhdGEsIGFlcyh4PVBzZXVkb01hdHVyYXRpb24uc2NvcmUsZmlsbD1DbHVzdGVyKSkgKwogICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI2JkZDhlZiIsICIjNmRiOGUyIiwgIiMzNTdlYmMiLCAiIzFjNDg5NiIpKSArCiAgIGdlb21fZGVuc2l0eShhbHBoYT0uNCkgCmBgYAoKYGBge3J9CnJtKGxpc3Q9bHMoKVshbHMoKSAlaW4lICJUZWxsZXkuZGF0YSJdKQpgYGAKCiMgTG9hZCBFMTIgZGF0YXNldAoKYGBge3J9CkUxMi5BUC5kYXRhIDwtIHJlYWRSRFMoIi4vQVAuZGF0YS5SRFMiKQpgYGAKCiMjIEV4dHJhY3QgcGFsbGlhbCBwcm9nZW5pdG9ycwoKYGBge3J9CiMgRmlsdGVyIHN1YnBhbGxpYWwgcHJvZ2VuaXRvcnMKQVAuaWRlbnQgPC0gdW5pcXVlKEUxMi5BUC5kYXRhQGlkZW50KQpFMTIuQVAuZGF0YSA8LSAgU3Vic2V0RGF0YShFMTIuQVAuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICBpZGVudC51c2UgPSBncmVwKCJTdWIiLCBBUC5pZGVudCwgdmFsdWUgPSBULCBpbnZlcnQgPSBUKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQucmF3ID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICBkby5jbGVhbiA9IEYpCgojIFJlc2V0IHRoZSBwc2V1ZG8tRFYgc2NvcmUgdG8gWzAsMV0Kc2NvcmUgPC0gRTEyLkFQLmRhdGFAbWV0YS5kYXRhJERvcnNvVmVudHJhbC5TY29yZQpFMTIuQVAuZGF0YUBtZXRhLmRhdGEkRG9yc29WZW50cmFsLlNjb3JlIDwtIChzY29yZSAtIG1pbihzY29yZSkpIC8gKG1heChzY29yZSkgLSBtaW4oc2NvcmUpKQpgYGAKCgpgYGB7ciBmaWcuZGltPWMoMTIsIDQpfQpwMSA8LSBEaW1QbG90KEUxMi5BUC5kYXRhLAogICAgICAgICAgICAgICBncm91cC5ieSA9ICJEb21haW5lIiwKICAgICAgICAgICAgICAgcmVkdWN0aW9uLnVzZSA9ICJzcHJpbmciLAogICAgICAgICAgICAgICBkaW0uMSA9IDEsCiAgICAgICAgICAgICAgIGRpbS4yID0gMiwKICAgICAgICAgICAgICAgZG8ubGFiZWw9RiwKICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IDQsCiAgICAgICAgICAgICAgIG5vLmxlZ2VuZCA9IEYsCiAgICAgICAgICAgICAgIGRvLnJldHVybiA9IFQsCiAgICAgICAgICAgICAgIGNvbHMudXNlID0gdG9sb3dlcihjKCIjNjhCMDQxIiwgIiNFM0MxNDgiLCAiI0I3RDE3NCIsICIjRTQ2QjZCIikpKQoKCmRhdGEgPC0gZGF0YS5mcmFtZShzcHJpbmcxID0gRTEyLkFQLmRhdGFAZHIkc3ByaW5nQGNlbGwuZW1iZWRkaW5nc1ssMV0sCiAgICAgICAgICAgICAgICAgICBzcHJpbmcyID0gRTEyLkFQLmRhdGFAZHIkc3ByaW5nQGNlbGwuZW1iZWRkaW5nc1ssMl0sCiAgICAgICAgICAgICAgICAgICBEb3Jzb1ZlbnRyYWwuU2NvcmUgPSBFMTIuQVAuZGF0YUBtZXRhLmRhdGEkRG9yc29WZW50cmFsLlNjb3JlKQoKY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwobiA9MTEsIG5hbWUgPSAiU3BlY3RyYWwiKSkoMTAwKQpwMiA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHNwcmluZzEsIHNwcmluZzIpKSArIAogICAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1Eb3Jzb1ZlbnRyYWwuU2NvcmUpLCBzaXplPTIsIHNoYXBlPTE2KSAgKyAKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnM9cmV2KGNvbHMpLCBuYW1lPSdTcGV1ZG90aW1lIHNjb3JlJykKCnAxICsgcDIKYGBgCgojIyBGaWx0ZXIsIG5vcm1hbGl6ZSBhbmQgc2NhbGUgdGhlIGV4cHJlc3Npb24gbWF0cml4CgpgYGB7cn0KIyBSZW1vdmUgbm9uIGVwcmVzc2VkIGdlbmVzCm51bS5jZWxscyA8LSBNYXRyaXg6OnJvd1N1bXMoRTEyLkFQLmRhdGFAZGF0YSA+IDApCmdlbmVzLnVzZSA8LSBuYW1lcyh4ID0gbnVtLmNlbGxzW3doaWNoKHggPSBudW0uY2VsbHMgPj0gMjApXSkKRTEyLkFQLmRhdGFAcmF3LmRhdGEgPC0gRTEyLkFQLmRhdGFAcmF3LmRhdGFbZ2VuZXMudXNlLCBdCkUxMi5BUC5kYXRhQGRhdGEgPC0gRTEyLkFQLmRhdGFAZGF0YVtnZW5lcy51c2UsIF0KCiMgTm9ybWFsaXplIHRoZSBjb3VudHMgbWF0cml4CkUxMi5BUC5kYXRhIDwtIE5vcm1hbGl6ZURhdGEob2JqZWN0ID0gRTEyLkFQLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLmZhY3RvciA9IHJvdW5kKG1lZGlhbihFMTIuQVAuZGF0YUBtZXRhLmRhdGEkblVNSSkpLCBkaXNwbGF5LnByb2dyZXNzID0gRikKCiMgU2NhbGUgZXhwcmVzc2lvbiBtYXRyaXgKRTEyLkFQLmRhdGEgPC0gU2NhbGVEYXRhKG9iamVjdCA9IEUxMi5BUC5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgdmFycy50by5yZWdyZXNzID0gYygiQ0MuRGlmZmVyZW5jZSIsICJwZXJjZW50LnJpYm8iICwiblVNSSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGxheS5wcm9ncmVzcyA9IEYpCgoKIyBGaW5kIG1vc3QgdmFyaWFibGUgZ2VuZXMKRTEyLkFQLmRhdGEgPC0gRmluZFZhcmlhYmxlR2VuZXMob2JqZWN0ID0gRTEyLkFQLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4uZnVuY3Rpb24gPSBFeHBNZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXNwZXJzaW9uLmZ1bmN0aW9uID0gTG9nVk1SLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4Lmxvdy5jdXRvZmYgPSAwLjAxMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHguaGlnaC5jdXRvZmYgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5LmN1dG9mZiA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvLnBsb3QgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXNwbGF5LnByb2dyZXNzID0gRikKbGVuZ3RoKEUxMi5BUC5kYXRhQHZhci5nZW5lcykKCmBgYAoKYGBge3J9CnJtKGxpc3Q9bHMoKVshbHMoKSAlaW4lIGMoIkUxMi5BUC5kYXRhIiwiVGVsbGV5LmRhdGEiKV0pCmBgYAoKIyBQcmVkaWN0IEUxMiBBUCBwc2V1ZG8tbWF0dXJhdGlvbiBzY29yZSB3aXRoIGEgcmVncmVzc2lvbiBtb2RlbCB0cmFpbm5lZCBvbiBUZWxsZXkgMjAxOSBkYXRhc2V0CgojIyBUcmFpbiBhIHJhbmRvbSBmb3Jlc3QgcmVncmVzc2lvbiBtb2RlbCBvbiBUZWxsZXkgMjAxOSBkYXRhCgpgYGB7cn0KIyBXZSB0cmFpbmVkIHRoZSBtb2RlbCB1c2luZyBvbmx5IGdlbmVzIGZvdW5kIHRvIGJlIGhpZ2hseSB2YXJpYWJsZSBpbiBvdXIgRTEyIGRhdGFzZXQKbW9kZWwuZGF0YSA8LSBkYXRhLmZyYW1lKFBzZXVkb01hdHVyYXRpb24uc2NvcmUgPSBUZWxsZXkuZGF0YUBtZXRhLmRhdGEkUHNldWRvTWF0dXJhdGlvbi5zY29yZSkKCmdlbmVzIDwtIHQoVGVsbGV5LmRhdGFAc2NhbGUuZGF0YVtyb3duYW1lcyhUZWxsZXkuZGF0YUBzY2FsZS5kYXRhKSAlaW4lIEUxMi5BUC5kYXRhQHZhci5nZW5lcyxdKQpnZW5lcyA8LSBhcy5kYXRhLmZyYW1lKGFzLm1hdHJpeChnZW5lcykpCm1vZGVsLmRhdGEgPC0gY2JpbmQobW9kZWwuZGF0YSxnZW5lcykKCiMgV2UgZGlkIG5vdCBwZXJmb3JtIGNyb3NzLXZhbGlkYXRpbiBidXQgcmVsaWVkIG9uIHRoZSBPdXQgT2YgQmFnIGVycm9yCnRyY3RybCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gIm5vbmUiKQpgYGAKCgpgYGB7cn0Kc2V0LnNlZWQoMTIzNCkKCiMgVHJhaW4gdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwKVGVsbGV5Lm1vZGVsIDwtIHRyYWluKFBzZXVkb01hdHVyYXRpb24uc2NvcmV+LiwgCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbW9kZWwuZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicmFuZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbD10cmN0cmwsCiAgICAgICAgICAgICAgICAgICAgICBpbXBvcnRhbmNlPSJwZXJtdXRhdGlvbiIsIAogICAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBkYXRhLmZyYW1lKG10cnk9NTAsIG1pbi5ub2RlLnNpemUgPSA1LCBzcGxpdHJ1bGU9InZhcmlhbmNlIikpCmBgYAoKV2UgcmVvcHRpbWl6ZSB0aGUgcmVncmVzc2lvbiBtb2RlbCBieSBzZWxlY3RpbmcgdGhlIDEwMCBtb3N0IGltcG9ydGFudGUgZmVhdHVyZXMgb2YgdGhlIGZpcnN0IG1vZGVsCgpgYGB7cn0KIyBTZWxlY3QgdGhlIHRvcCAxMDAgaW1wb3J0YW50ZSBmZWF0dXJlcwpGZWF0dXJlSW1wb3J0YW5jZSA8LSBzb3J0KFRlbGxleS5tb2RlbCRmaW5hbE1vZGVsJHZhcmlhYmxlLmltcG9ydGFuY2UsIGRlY3JlYXNpbmcgPSBUKQpTZWxlY3RlZEZlYXR1cmVzIDwtIG5hbWVzKEZlYXR1cmVJbXBvcnRhbmNlWzE6MTAwXSkKCiMgUmUtdHJhaW4gdGhlIG1vZGVsIHdpdGggdGhlIHJlc3RyaWN0ZWQgZmVhdHVyZXMKVGVsbGV5Lm1vZGVsIDwtIHRyYWluKFBzZXVkb01hdHVyYXRpb24uc2NvcmV+LiwgCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbW9kZWwuZGF0YVssY29sbmFtZXMobW9kZWwuZGF0YSkgJWluJSBjKFNlbGVjdGVkRmVhdHVyZXMsIlBzZXVkb01hdHVyYXRpb24uc2NvcmUiKV0sIAogICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJhbmdlciIsCiAgICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2w9dHJjdHJsLAogICAgICAgICAgICAgICAgICAgICAgaW1wb3J0YW5jZT0icGVybXV0YXRpb24iLCAKICAgICAgICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gZGF0YS5mcmFtZShtdHJ5PTUwLCBtaW4ubm9kZS5zaXplID0gNSwgc3BsaXRydWxlPSJ2YXJpYW5jZSIpKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg1LjMsIDQpfQojIFBsb3QgT2JzZXJ2ZWQgdnMgT09CIHByZWRpY3RlZCB2YWx1ZXMgZnJvbSB0aGUgbW9kZWwKcGxvdChtb2RlbC5kYXRhJFBzZXVkb01hdHVyYXRpb24uc2NvcmUsIFRlbGxleS5tb2RlbCRmaW5hbE1vZGVsJHByZWRpY3Rpb25zLAogICAgIHBjaD0xOSwgeGxhYj0ib2JzZXJ2ZWQgUHNldWRvLW1hdHVyYXRpb24iLAogICAgIHlsYWI9Ik9PQiBwcmVkaWN0ZWQgUHNldWRvLW1hdHVyYXRpb24iKQptdGV4dChwYXN0ZSgiUi1zcXVhcmVkIiwgZm9ybWF0KFRlbGxleS5tb2RlbCRmaW5hbE1vZGVsJHIuc3F1YXJlZCxkaWdpdHM9MikpKQpgYGAKCiMjIFByZWRpY3QgbWF0dXJhdGlvbiBzY29yZSBvZiBFMTIgcGFsbGlhbCBhcGljYWwgcHJvZ2VuaXRvcnMKCmBgYHtyfQojIFByZWRpY3QgbWF0dXJhdGlvbiBzY29yZSBvZiBFMTIgQVBzCkUxMi5kZiA8LSBhcy5kYXRhLmZyYW1lKHQoRTEyLkFQLmRhdGFAc2NhbGUuZGF0YVtyb3duYW1lcyhFMTIuQVAuZGF0YUBzY2FsZS5kYXRhKSAlaW4lIFNlbGVjdGVkRmVhdHVyZXMsXSkpCgpFMTIucHJlZC5tYXR1cmF0aW9uIDwtIHByZWRpY3QoVGVsbGV5Lm1vZGVsLCBFMTIuZGYpCgpFMTIuQVAuZGF0YUBtZXRhLmRhdGEkUHNldWRvTWF0dXJhdGlvbi5zY29yZSA8LSBFMTIucHJlZC5tYXR1cmF0aW9uCgpFMTIucGxvdC5kZiA8LSBFMTIuQVAuZGF0YUBtZXRhLmRhdGEgJT4lIGRwbHlyOjpzZWxlY3QoQmFyY29kZXMsIERvbWFpbmUsIERvcnNvVmVudHJhbC5TY29yZSxQc2V1ZG9NYXR1cmF0aW9uLnNjb3JlKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg1LjMsIDQpLCBmaWcuY2FwPSAiTWFudXNjcmlwdCBGaWcuIDZCIn0KIyBQbG90IEUxMiBwcmVkaWN0ZWQgbWF0dXJhdGlvbiBzY29yZSBieSBjbHVzdGVyCmRvbWFpbmUgPC0gZmFjdG9yKEUxMi5wbG90LmRmJERvbWFpbmUsIGxldmVsID0gYygiVmVudHJhbC5QYWxsaXVtIiwibGF0ZXJhbC5QYWxsaXVtLjEiLCJsYXRlcmFsLlBhbGxpdW0uMiIsIkRvcnNhbC5QYWxsaXVtIikpCgpnZ3Bsb3QoRTEyLnBsb3QuZGYsIGFlcyh4PSBkb21haW5lLCB5PSBQc2V1ZG9NYXR1cmF0aW9uLnNjb3JlLCBmaWxsPURvbWFpbmUpKSArCiAgZ2VvbV9ib3hwbG90KG5vdGNoPVRSVUUpICsKICBnZW9tX2ppdHRlcihjb2xvcj0iYmxhY2siLCBzaXplPTAuOCwgYWxwaGE9MC45KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdG9sb3dlcihjKCIjNjhCMDQxIiwgIiNFM0MxNDgiLCAiI0I3RDE3NCIsICIjRTQ2QjZCIikpKSArCiAgeGxhYigiIikKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCJUZWxsZXkuZGF0YSIsICJFMTIuQVAuZGF0YSIpXSkKYGBgCgojIERlY29tcG9zaXRpb24gb2Ygc3BhdGlhbCB2cy4gdGVtb3JhbCBhc3BlY3Qgb2YgcHJvZ2VuaXRvciBjZWxscyBpZGVudGl0eQoKIyMgU21vb3RoIGV4cHJlc3Npb24gb3ZlciBwc2V1ZG8tRFYgYW5kIHBzZXVkbyBtYXR1cmF0aW9uCgojIyMgRmluZCBhbGwgdmFyaWFibGUgZ2VuZXMgYWxvbmcgdGhlIHBzZXVkby1EViBheGlzIGluIEUxMiBBUAoKYGBge3J9CiMgVHJhbnNmZXJ0IG1ldGFkYXRhIAptZXRhLmRhdGEgPC0gZGF0YS5mcmFtZShiYXJjb2RlID0gcm93bmFtZXMoRTEyLkFQLmRhdGFAbWV0YS5kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgQ2x1c3RlciA9IEUxMi5BUC5kYXRhQG1ldGEuZGF0YSRvbGQuaWRlbnQsCiAgICAgICAgICAgICAgICAgICAgICAgIERvcnNvVmVudHJhbC5TY29yZSA9ICBFMTIuQVAuZGF0YUBtZXRhLmRhdGEkRG9yc29WZW50cmFsLlNjb3JlLAogICAgICAgICAgICAgICAgICAgICAgICBDZWxsY3ljbGVQaGFzZSA9IEUxMi5BUC5kYXRhQG1ldGEuZGF0YSRQaGFzZSwKICAgICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gcm93bmFtZXMoRTEyLkFQLmRhdGFAbWV0YS5kYXRhKSkKICAgICAgICAgICAgICAgICAgIApBbm5vdC5kYXRhICA8LSBuZXcoJ0Fubm90YXRlZERhdGFGcmFtZScsIGRhdGEgPSBtZXRhLmRhdGEpCgojIFRyYW5zZmVydCBjb3VudCBkYXRhCmNvdW50LmRhdGEgPSBkYXRhLmZyYW1lKGdlbmVfc2hvcnRfbmFtZSA9IHJvd25hbWVzKEUxMi5BUC5kYXRhQHJhdy5kYXRhKSwKICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gcm93bmFtZXMoRTEyLkFQLmRhdGFAcmF3LmRhdGEpKQoKZmVhdHVyZS5kYXRhIDwtIG5ldygnQW5ub3RhdGVkRGF0YUZyYW1lJywgZGF0YSA9IGNvdW50LmRhdGEpCgojIENyZWF0ZSB0aGUgQ2VsbERhdGFTZXQgb2JqZWN0CkUxMi5nYm1fY2RzIDwtIG5ld0NlbGxEYXRhU2V0KGFzLm1hdHJpeChFMTIuQVAuZGF0YUByYXcuZGF0YSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm9EYXRhID0gQW5ub3QuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlRGF0YSA9IGZlYXR1cmUuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsb3dlckRldGVjdGlvbkxpbWl0ID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICBleHByZXNzaW9uRmFtaWx5ID0gbmVnYmlub21pYWwoKSkKYGBgCgoKYGBge3J9CkUxMi5nYm1fY2RzIDwtIGVzdGltYXRlU2l6ZUZhY3RvcnMoRTEyLmdibV9jZHMpCkUxMi5nYm1fY2RzIDwtIGVzdGltYXRlRGlzcGVyc2lvbnMoRTEyLmdibV9jZHMpCkUxMi5nYm1fY2RzIDwtIGRldGVjdEdlbmVzKEUxMi5nYm1fY2RzLCBtaW5fZXhwciA9IDAuMSkKYGBgCgpgYGB7cn0KIyBFeGNsdWRlIGNlbGwgY3ljbGUgYXNzb2NpYXRlZCBnZW5lcwpDQ2dlbmVzIDwtIGFzLmNoYXJhY3RlcihyZWFkLnRhYmxlKCIuL1Byb2dlbml0b3JzL0NlbGxDeWNsZUdlbmVzLmNzdiIsIHNlcCA9ICJcdCIsIGhlYWRlciA9IEYpWywxXSkKSW5wdXQuZ2VuZXMgPC0gRTEyLkFQLmRhdGFAdmFyLmdlbmVzWyFFMTIuQVAuZGF0YUB2YXIuZ2VuZXMgJWluJSBDQ2dlbmVzXQpgYGAKCmBgYHtyfQojIFBlcmZvcm0gdGhlIHRlc3QgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFzIGEgZnVuY3Rpb24gb2YgcHNldWRvLURWIHNjb3JlCkRWLkF4aXMuZ2VuZXMgPC0gZGlmZmVyZW50aWFsR2VuZVRlc3QoRTEyLmdibV9jZHNbSW5wdXQuZ2VuZXMsXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbE1vZGVsRm9ybXVsYVN0ciA9ICJ+c20ubnMoRG9yc29WZW50cmFsLlNjb3JlLCBkZiA9IDMpKkNlbGxjeWNsZVBoYXNlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWNlZE1vZGVsRm9ybXVsYVN0ciA9ICJ+Q2VsbGN5Y2xlUGhhc2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JlcyA9IGRldGVjdENvcmVzKCkgLTIpCgojIEZpbHRlciBnZW5lcyB3aXRoIGEgRkRSIDwgMC4wMDEKRFYuQXhpcy5nZW5lcy5GRFIuZmlsdGVyZWQgPC0gRFYuQXhpcy5nZW5lcyAlPiUgZmlsdGVyKHF2YWwgPCAxZS0zKQpgYGAKCmBgYHtyfQojIENyZWF0ZSBhIG5ldyBwc2V1ZG8tRFYgdmVjdG9yIG9mIDUwMCBwb2ludHMKblBvaW50cyA8LSAyMDAKRTEyLm5ld19kYXRhIDwtIGRhdGEuZnJhbWUoRG9yc29WZW50cmFsLlNjb3JlID0gc2VxKG1pbihwRGF0YShFMTIuZ2JtX2NkcykkRG9yc29WZW50cmFsLlNjb3JlKSwgbWF4KHBEYXRhKEUxMi5nYm1fY2RzKSREb3Jzb1ZlbnRyYWwuU2NvcmUpLCBsZW5ndGgub3V0ID0gblBvaW50cykpCgojIFNtb290aCBnZW5lIGV4cHJlc3Npb24KRTEyLnNtb290aC5jdXJ2ZS5tYXRyaXggPC0gZ2VuU21vb3RoQ3VydmVzKEUxMi5nYm1fY2RzW2FzLmNoYXJhY3RlcihEVi5BeGlzLmdlbmVzLkZEUi5maWx0ZXJlZCRnZW5lX3Nob3J0X25hbWUpLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmVuZF9mb3JtdWxhID0gIn5zbS5ucyhEb3Jzb1ZlbnRyYWwuU2NvcmUsIGRmID0gMykiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpdmVfZXhwciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfZGF0YSA9IEUxMi5uZXdfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcmVzPSBkZXRlY3RDb3JlcygpIC0gMikKYGBgCgpgYGB7cn0Kcm0obGlzdD1scygpWyFscygpICVpbiUgYygiVGVsbGV5LmRhdGEiLCAiRTEyLkFQLmRhdGEiLCAiRTEyLnNtb290aC5jdXJ2ZS5tYXRyaXgiLCAiRTEyLm5ld19kYXRhIiwgIkRWLkF4aXMuZ2VuZXMuRkRSLmZpbHRlcmVkIildKQpgYGAKCiMjIyBGaW5kIGFsbCB2YXJpYWJsZSBnZW5lcyBhbG9uZyB0aGUgcHNldWRvLW1hdHVyYXRpb24gYXhpcyBpbiBUZWxsZXkgMjAxOSBBUHMKCmBgYHtyfQojIFRyYW5zZmVydCBtZXRhZGF0YSAKbWV0YS5kYXRhIDwtIGRhdGEuZnJhbWUoYmFyY29kZSA9IHJvd25hbWVzKFRlbGxleS5kYXRhQG1ldGEuZGF0YSksCiAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXIgPSBUZWxsZXkuZGF0YUBtZXRhLmRhdGEkb3JpZy5pZGVudCwKICAgICAgICAgICAgICAgICAgICAgICAgUHNldWRvTWF0dXJhdGlvbi5zY29yZSA9ICBUZWxsZXkuZGF0YUBtZXRhLmRhdGEkUHNldWRvTWF0dXJhdGlvbi5zY29yZSwKICAgICAgICAgICAgICAgICAgICAgICAgQ2VsbGN5Y2xlUGhhc2UgPSBUZWxsZXkuZGF0YUBtZXRhLmRhdGEkUGhhc2UsCiAgICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IHJvd25hbWVzKFRlbGxleS5kYXRhQG1ldGEuZGF0YSkpCiAgICAgICAgICAgICAgICAgICAKQW5ub3QuZGF0YSAgPC0gbmV3KCdBbm5vdGF0ZWREYXRhRnJhbWUnLCBkYXRhID0gbWV0YS5kYXRhKQoKIyBUcmFuc2ZlcnQgY291bnQgZGF0YQpjb3VudC5kYXRhID0gZGF0YS5mcmFtZShnZW5lX3Nob3J0X25hbWUgPSByb3duYW1lcyhUZWxsZXkuZGF0YUByYXcuZGF0YSksCiAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IHJvd25hbWVzKFRlbGxleS5kYXRhQHJhdy5kYXRhKSkKCmZlYXR1cmUuZGF0YSA8LSBuZXcoJ0Fubm90YXRlZERhdGFGcmFtZScsIGRhdGEgPSBjb3VudC5kYXRhKQoKIyBDcmVhdGUgdGhlIENlbGxEYXRhU2V0IG9iamVjdApUZWxsZXkuZ2JtX2NkcyA8LSBuZXdDZWxsRGF0YVNldChhcy5tYXRyaXgoVGVsbGV5LmRhdGFAcmF3LmRhdGEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGEgPSBBbm5vdC5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlRGF0YSA9IGZlYXR1cmUuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG93ZXJEZXRlY3Rpb25MaW1pdCA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cHJlc3Npb25GYW1pbHkgPSBuZWdiaW5vbWlhbCgpKQpgYGAKCgpgYGB7cn0KVGVsbGV5LmdibV9jZHMgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhUZWxsZXkuZ2JtX2NkcykKVGVsbGV5LmdibV9jZHMgPC0gZXN0aW1hdGVEaXNwZXJzaW9ucyhUZWxsZXkuZ2JtX2NkcykKVGVsbGV5LmdibV9jZHMgPC0gZGV0ZWN0R2VuZXMoVGVsbGV5LmdibV9jZHMsIG1pbl9leHByID0gMC4xKQpgYGAKCmBgYHtyfQojIEV4Y2x1ZGUgY2VsbCBjeWNsZSBhc3NvY2lhdGVkLCBtdCBhbmQgcmlibyByaWJvCkNDZ2VuZXMgPC0gYXMuY2hhcmFjdGVyKHJlYWQudGFibGUoIi4vUHJvZ2VuaXRvcnMvQ2VsbEN5Y2xlR2VuZXMuY3N2Iiwgc2VwID0gIlx0IiwgaGVhZGVyID0gRilbLDFdKQpHZW5lc1RvUmVtb3ZlIDwtIGMoZ3JlcChwYXR0ZXJuID0gIiheUnBsfF5ScHN8Xk1ycCkiLCB4ID0gcm93Lm5hbWVzKFRlbGxleS5kYXRhQGRhdGEpLCB2YWx1ZSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgZ3JlcChwYXR0ZXJuID0gIl5tdC0iLCB4ID0gcm93Lm5hbWVzKFRlbGxleS5kYXRhQGRhdGEpLCB2YWx1ZSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgIlhpc3QiLCBDQ2dlbmVzKQoKSW5wdXQuZ2VuZXMgPC0gcm93Lm5hbWVzKFRlbGxleS5kYXRhQGRhdGEpWyFyb3cubmFtZXMoVGVsbGV5LmRhdGFAZGF0YSkgJWluJSBHZW5lc1RvUmVtb3ZlXQoKIyBXZSBzZWxlY3QgYWxsIGdlbmVzIHdoaWNoIGFyZSBkaWZmZXJlbnRpYWx5IGV4cHJlc3NlZCBiZXR3ZWVuIEUxMiBhbmQgRTE1IEFQIApFMTJ2c0UxNS5ERWdlbmVzIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IFRlbGxleS5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkZW50LjEgPSAiRTEyLjFIIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZGVudC4yID0gIkUxNS4xSCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLnBjdCA9IDAuMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nZmMudGhyZXNob2xkID0gMC40LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW50LmJhciA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzLnVzZSA9IElucHV0LmdlbmVzKQoKRTEydnNFMTUuREVnZW5lcyRHZW5lIDwtIHJvd25hbWVzKEUxMnZzRTE1LkRFZ2VuZXMpCgpFMTJ2c0UxNS5ERWdlbmVzLmZpbHRlcmVkIDwtIEUxMnZzRTE1LkRFZ2VuZXMgJT4lIGZpbHRlcihwX3ZhbF9hZGogPCAxZS0zKSAlPiUgcHVsbChHZW5lKQpgYGAKCgpgYGB7cn0KIyBDcmVhdGUgYSBuZXcgcHNldWRvLURWIHZlY3RvciBvZiA1MDAgcG9pbnRzCm5Qb2ludHMgPC0gMjAwClRlbGxleS5uZXdfZGF0YSA8LSBkYXRhLmZyYW1lKFBzZXVkb01hdHVyYXRpb24uc2NvcmUgPSBzZXEobWluKHBEYXRhKFRlbGxleS5nYm1fY2RzKSRQc2V1ZG9NYXR1cmF0aW9uLnNjb3JlKSwgbWF4KHBEYXRhKFRlbGxleS5nYm1fY2RzKSRQc2V1ZG9NYXR1cmF0aW9uLnNjb3JlKSwgbGVuZ3RoLm91dCA9IG5Qb2ludHMpKQoKIyBTbW9vdGggZ2VuZSBleHByZXNzaW9uIGFsb25nIHBzZXVkby1tYXR1cmF0aW9uIGF4aXMKVGVsbGV5LnNtb290aC5jdXJ2ZS5tYXRyaXggPC0gZ2VuU21vb3RoQ3VydmVzKFRlbGxleS5nYm1fY2RzW0UxMnZzRTE1LkRFZ2VuZXMuZmlsdGVyZWQsXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyZW5kX2Zvcm11bGEgPSAifnNtLm5zKFBzZXVkb01hdHVyYXRpb24uc2NvcmUsIGRmID0gMykiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpdmVfZXhwciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfZGF0YSA9IFRlbGxleS5uZXdfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcmVzPSBkZXRlY3RDb3JlcygpIC0gMikKYGBgCgojIyBJZGVudGlmeSB0ZW1wb3JhbCBnZW5lcyBhcyBoaWdobHkgYW50aS1jb3JyZWxhdGluZyBiZXR3ZWVuIHRoZSB0d28gZGF0YXNldHMKCiMjIyBJbnRlcnNlY3QgdGhlIHR3byBzbW9vdGhlZCBleHByZXNzaW9uIG1hdHJpY2VzCgpgYGB7cn0KQ29tbW9uR2VuZXMgPC0gaW50ZXJzZWN0KHJvd25hbWVzKEUxMi5zbW9vdGguY3VydmUubWF0cml4KSwgcm93bmFtZXMoVGVsbGV5LnNtb290aC5jdXJ2ZS5tYXRyaXgpKQoKRTEyLlNtb290aC5Db21tb24gPC0gdChzY2FsZSh0KEUxMi5zbW9vdGguY3VydmUubWF0cml4W0NvbW1vbkdlbmVzLF0pKSkKVGVsbGV5LlNtb290aC5Db21tb24gPC0gdChzY2FsZSh0KFRlbGxleS5zbW9vdGguY3VydmUubWF0cml4W0NvbW1vbkdlbmVzLF0pKSkKYGBgCgojIyMgQ2x1c3RlciB0ZW1wb3JhbCBnZW5lcyBhY2NvcmRpbmcgdG8gdGhlaXIgc21vb3RoZWQgZXhwcmVzc2lvbiBpbnZlcnNlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHR3byBkYXRhc2V0cwoKYGBge3J9CiMgQnVpbGQgdGVtcG9yYWwgZ2VuZXMgZGVuZHJvZ3JhbQpjb3IubWF0IDwtIGNvcih4PSB0KEUxMi5TbW9vdGguQ29tbW9uWywyMDA6MV0pLCB5PSB0KFRlbGxleS5TbW9vdGguQ29tbW9uKSwgbWV0aG9kID0gInBlYXJzb24iKQpkc3QgPC0gZGlzdChjb3IubWF0LCBtZXRob2QgPSAiZXVjbGlkZWFuIikKaGMgPC0gIGhjbHVzdChkc3QsIG1ldGhvZCA9ICJjb21wbGV0ZSIpCgojIEZpbmQgMyBjbHVzdGVyIGJhc2VkIG9uIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCmNsdXN0ZXJzIDwtIGN1dHJlZShoYywgaz0zKQoKVGVtcG9yYWwuZ2VuZS5jbHVzdGVycyA8LSBkYXRhLmZyYW1lKEdlbmUgPSBuYW1lcyhjbHVzdGVycyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2VuZS5DbHVzdGVycyA9IGFzLm51bWVyaWMoY2x1c3RlcnMpKSAlPiUgYXJyYW5nZShHZW5lLkNsdXN0ZXJzKQoKcm93Lm5hbWVzKFRlbXBvcmFsLmdlbmUuY2x1c3RlcnMpIDwtIFRlbXBvcmFsLmdlbmUuY2x1c3RlcnMkR2VuZQpUZW1wb3JhbC5nZW5lLmNsdXN0ZXJzJEdlbmUuQ2x1c3RlcnMgPC0gcGFzdGUwKCJDbHVzdC4iLFRlbXBvcmFsLmdlbmUuY2x1c3RlcnMkR2VuZS5DbHVzdGVycykKCndyaXRlLnRhYmxlKFRlbXBvcmFsLmdlbmUuY2x1c3RlcnMsICIuL1Byb2dlbml0b3JzL1RlbXBvcmFsLmdlbmUuY2x1c3RlcnMuY3N2Iiwgc2VwID0gIjsiLCBxdW90ZSA9IEYpCmBgYAoKCmBgYHtyIGZpZy5kaW09Yyg2LjQsIDQuNCksIGZpZy5jYXA9ICJNYW51c2NyaXB0IEZpZy4gNkUifQpwaGVhdG1hcChjb3IubWF0LAogICAgICAgICBjbHVzdGVyX3Jvd3M9IGhjLAogICAgICAgICBzY2FsZSA9ICJub25lIiwKICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBUZW1wb3JhbC5nZW5lLmNsdXN0ZXJzICU+JSBkcGx5cjo6c2VsZWN0KEdlbmUuQ2x1c3RlcnMpLAogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoR2VuZS5DbHVzdGVycyA9IGMoQ2x1c3QuMSA9IiM0Nzg0YTIiICwgQ2x1c3QuMj0iI2ViY2IyZSIsQ2x1c3QuMz0iI2NjM2ExYiIpKSwKICAgICAgICAgc2hvd19jb2xuYW1lcyA9IFQsCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBULAogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwKICAgICAgICAgY29sb3IgPSByZXYoYnJld2VyLnBhbCg4LCJSZEJ1IikpLAogICAgICAgICBtYWluID0gIkdlbmVzIGludmVyc2UgcGVhcnNvbiBjb3JyZWxhdGlvbiBtYXRyaXgiKQpgYGAKCiMjIyBQbG90IGNvcnJlbGF0aW9uIGJldHdlZW4gc21vb3RoZWQgZXhwcmVzc2lvbiBwcm9maWxlcyAKCmBgYHtyfQpTbW9vdGhlZC5wb2ludC5jb3IgPC0gY29yKHg9IEUxMi5TbW9vdGguQ29tbW9uLCB5PSBUZWxsZXkuU21vb3RoLkNvbW1vblssMjAwOjFdLCBtZXRob2QgPSAicGVhcnNvbiIpCmBgYAoKYGBge3IgZmlnLmRpbT1jKDYuNCwgNC40KSwgZmlnLmNhcD0gIk1hbnVzY3JpcHQgRmlnLiA2RCJ9CkRvbWFpbmUuRTEyLkFQIDwtIEUxMi5BUC5kYXRhQG1ldGEuZGF0YSAlPiUgc2VsZWN0KERvcnNvVmVudHJhbC5TY29yZSwgRG9tYWluZSkKRG9tYWluZS5ib3VuZGFyaWVzIDwtIGFnZ3JlZ2F0ZShEb3Jzb1ZlbnRyYWwuU2NvcmUgfiBEb21haW5lLCBEb21haW5lLkUxMi5BUCwgbWF4KSAlPiUgYXJyYW5nZShEb3Jzb1ZlbnRyYWwuU2NvcmUpICU+JSBwdWxsKERvcnNvVmVudHJhbC5TY29yZSkKCkRvbWFpbmUuSWRlbnQgPC0gc2FwcGx5KEUxMi5uZXdfZGF0YSREb3Jzb1ZlbnRyYWwuU2NvcmUsCiAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpeyBpZih4PERvbWFpbmUuYm91bmRhcmllc1sxXSl7IHggPSAiVmVudHJhbC5QYWxsaXVtIgogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYoeD4gRG9tYWluZS5ib3VuZGFyaWVzWzFdICYgeDwgRG9tYWluZS5ib3VuZGFyaWVzWzJdKXsgeCA9ImxhdGVyYWwuUGFsbGl1bS4xIgogICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYoeD4gRG9tYWluZS5ib3VuZGFyaWVzWzNdKXsgeCA9ICJEb3JzYWwuUGFsbGl1bSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHg9ImxhdGVyYWwuUGFsbGl1bS4yIn0pIAoKRG9tYWluZXMuQ2x1c3QgPC0gZGF0YS5mcmFtZShEb21haW5lLklkZW50KQoKcGhlYXRtYXAodChTbW9vdGhlZC5wb2ludC5jb3IpLAogICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGLAogICAgICAgICBjbHVzdGVyX2NvbHMgPSBGLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IERvbWFpbmVzLkNsdXN0LAogICAgICAgICBnYXBzX2NvbCA9IGN1bXN1bShhcy5udW1lcmljKHRhYmxlKERvbWFpbmVzLkNsdXN0JERvbWFpbmUuSWRlbnQpKVtjKDQsMiwzLDEpXSksCiAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gbGlzdChEb21haW5lLklkZW50ID0gYyhWZW50cmFsLlBhbGxpdW0gPSAiI2U0NmI2YiIsbGF0ZXJhbC5QYWxsaXVtLjEgPSAiI2UzYzE0OCIsIGxhdGVyYWwuUGFsbGl1bS4yID0gIiNiN2QxNzQiLERvcnNhbC5QYWxsaXVtID0gIiM2OGIwNDEiKSksCiAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICBzaG93X3Jvd25hbWVzID0gRiwKICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKHJldihicmV3ZXIucGFsKG4gPSAxMCwgbmFtZSA9ICJSZEJ1IikpKSgxMDApLAogICAgICAgICBtYWluID0gIlBlYXJzb24ncyBDb3JyZWxhdGlvbiBtYXRyaXgiKQpgYGAKCiMjIEV4dHJhY3QgRTEyIHNwYXRpYWwgZG9tYWluIGdlbmVzIHdoaWNoIGRvIG5vdCBzaG93IEUxMi9FMTUgZGlmZmVyZW5jZSBpbiBUZWxsZXkgZGF0YQoKYGBge3J9CiMgU2VsZWN0IGdlbmVzIHZhcmlhYmxlIGluIEUxMiBkYXRhc2V0IHdoaWNoIGRvIG5vdCBzaG93IHNpZ25pZmljYXRpdmUgZGlmZmVyZW5jZSBiZXR3ZWVuIEUxMiBhbmQgRTE1IEFQIGluIFRlbGxleSBldCBhbCBkYXRhc2V0CkUxMi5Eb21haW4uc3BlY2lmaWMuZ2VuZXMgPC0gcm93bmFtZXMoRTEyLnNtb290aC5jdXJ2ZS5tYXRyaXgpWyFyb3duYW1lcyhFMTIuc21vb3RoLmN1cnZlLm1hdHJpeCkgJWluJSBDb21tb25HZW5lc10KCkUxMi5zbW9vdGguc3BlY2lmaWMgPC0gRTEyLnNtb290aC5jdXJ2ZS5tYXRyaXhbRTEyLkRvbWFpbi5zcGVjaWZpYy5nZW5lcyxdCmBgYAoKYGBge3IgfQojIEJ1aWxkIHRlbXBvcmFsIGdlbmVzIGRlbmRyb2dyYW0KY29yLm1hdCA8LSBjb3IodChFMTIuc21vb3RoLnNwZWNpZmljKSwgbWV0aG9kID0gInNwZWFybWFuIikKZHN0IDwtIGFzLmRpc3QoMSAtIGNvci5tYXQpCmhjIDwtICBoY2x1c3QoZHN0LCBtZXRob2QgPSAid2FyZC5EIikgIyJ3YXJkLkQiCgojIFNlcmlhdGUgdGhlIGRlbmRyb2dyYW0KZGVuZCA8LSBhcy5kZW5kcm9ncmFtKGhjKQpkZW5kIDwtIGRlbmRleHRlbmQ6OnNlcmlhdGVfZGVuZHJvZ3JhbShkZW5kLCBkc3QsIG1ldGhvZD0iR1ciKQoKIyBGaW5kIDIgY2x1c3RlciBiYXNlZCBvbiBoaWVyYXJjaGljYWwgY2x1c3RlcmluZwpjbHVzdGVycyA8LSBjdXRyZWUoaGMsIGs9NCkKCnJvd25hbWVzKERWLkF4aXMuZ2VuZXMuRkRSLmZpbHRlcmVkKSAgPC0gRFYuQXhpcy5nZW5lcy5GRFIuZmlsdGVyZWQkZ2VuZV9zaG9ydF9uYW1lCkRWLkF4aXMuZ2VuZXMuRkRSLmZpbHRlcmVkIDwtIERWLkF4aXMuZ2VuZXMuRkRSLmZpbHRlcmVkW25hbWVzKGNsdXN0ZXJzKSxdCgpTcGF0aWFsLmdlbmUuY2x1c3RlcnMgPC0gZGF0YS5mcmFtZShHZW5lID0gbmFtZXMoY2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsPSBEVi5BeGlzLmdlbmVzLkZEUi5maWx0ZXJlZCRwdmFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdmFsPSBEVi5BeGlzLmdlbmVzLkZEUi5maWx0ZXJlZCRxdmFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1fY2VsbHNfZXhwcmVzc2VkPSBEVi5BeGlzLmdlbmVzLkZEUi5maWx0ZXJlZCRudW1fY2VsbHNfZXhwcmVzc2VkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW5lLkNsdXN0ZXJzID0gYXMubnVtZXJpYyhjbHVzdGVycykpICU+JSBhcnJhbmdlKEdlbmUuQ2x1c3RlcnMpCgpyb3cubmFtZXMoU3BhdGlhbC5nZW5lLmNsdXN0ZXJzKSA8LSBTcGF0aWFsLmdlbmUuY2x1c3RlcnMkR2VuZQpTcGF0aWFsLmdlbmUuY2x1c3RlcnMkR2VuZS5DbHVzdGVycyA8LSBwYXN0ZTAoIkNsdXN0LiIsU3BhdGlhbC5nZW5lLmNsdXN0ZXJzJEdlbmUuQ2x1c3RlcnMpCgp3cml0ZS50YWJsZShTcGF0aWFsLmdlbmUuY2x1c3RlcnMsICIuL1Byb2dlbml0b3JzL1NwYXRpYWwuZ2VuZS5jbHVzdGVycy5jc3YiLCBzZXAgPSAiOyIsIHF1b3RlID0gRikKYGBgCgoKYGBge3IgZmlnLmRpbT1jKDYuNCwgNC40KSwgZmlnLmNhcD0gIk1hbnVzY3JpcHQgRmlnLiA2RyJ9CmFubm8uY29sIDwtIGxpc3QoRG9tYWluZS5JZGVudCA9IGMoVmVudHJhbC5QYWxsaXVtID0gIiNlNDZiNmIiLGxhdGVyYWwuUGFsbGl1bS4xID0gIiNlM2MxNDgiLCBsYXRlcmFsLlBhbGxpdW0uMiA9ICIjYjdkMTc0IixEb3JzYWwuUGFsbGl1bSA9ICIjNjhiMDQxIiksCiAgICAgICAgICAgICAgICAgR2VuZS5DbHVzdGVycyA9IGMoQ2x1c3QuMSA9IiM0Nzg0YTIiICwgQ2x1c3QuMj0iI2ViY2IyZSIsIENsdXN0LjM9IiNjYzNhMWIiLENsdXN0LjQ9IiM0Y2FiZGMiLENsdXN0LjU9IiMzY2E3M2YiKSkKCnBoZWF0bWFwKEUxMi5zbW9vdGguc3BlY2lmaWMsCiAgICAgICAgIGNsdXN0ZXJfcm93cz0gYXMuaGNsdXN0KGRlbmQpLAogICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICBjbHVzdGVyX2NvbHMgPSBGLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IERvbWFpbmVzLkNsdXN0LAogICAgICAgICBnYXBzX2NvbCA9IGN1bXN1bShhcy5udW1lcmljKHRhYmxlKERvbWFpbmVzLkNsdXN0JERvbWFpbmUuSWRlbnQpKVtjKDQsMiwzLDEpXSksCiAgICAgICAgIGFubm90YXRpb25fcm93ID0gU3BhdGlhbC5nZW5lLmNsdXN0ZXJzICU+JSBkcGx5cjo6c2VsZWN0KEdlbmUuQ2x1c3RlcnMpLAogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm8uY29sLAogICAgICAgICBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgIGNvbG9yID0gcmV2KGJyZXdlci5wYWwoMTEsIlJkQnUiKSksCiAgICAgICAgIGJyZWFrcyA9IHNlcSgtMi41LDIuNSwgbGVuZ3RoLm91dCA9IDExKSwKICAgICAgICAgbWFpbiA9ICJTcGF0aWFsIHNtb290aGVkIGV4cHJlc3Npb24gYWxvbmcgcHNldWRvRFYgYXhpcyIpCmBgYAoKIyBQbG90IHRoZSByZXByZXNlbnRhdGl2ZSB0cmVuZHMKCmBgYHtyfQpkYXRhRTEyIDwtIGRhdGEuZnJhbWUoQ2x1c3RlciA9IHBhc3RlMCgiQ2x1c3RlciIsYXMuY2hhcmFjdGVyKEUxMi5BUC5kYXRhQGlkZW50KSksCiAgICAgICAgICAgICAgICAgICBEb3Jzb1ZlbnRyYWwuU2NvcmUgPSAgRTEyLkFQLmRhdGFAbWV0YS5kYXRhJERvcnNvVmVudHJhbC5TY29yZSwKICAgICAgICAgICAgICAgICAgIFpidGIyMCA9IEUxMi5BUC5kYXRhQGRhdGFbIlpidGIyMCIsXSwKICAgICAgICAgICAgICAgICAgIExycm4xID0gRTEyLkFQLmRhdGFAZGF0YVsiTHJybjEiLF0sCiAgICAgICAgICAgICAgICAgICBMbW80ID0gRTEyLkFQLmRhdGFAZGF0YVsiTG1vNCIsXSwKICAgICAgICAgICAgICAgICAgIE1wcGVkMiA9RTEyLkFQLmRhdGFAZGF0YVsiTXBwZWQyIixdKQoKZGF0YVRlbGxleSA8LSBkYXRhLmZyYW1lKENsdXN0ZXIgPSBwYXN0ZTAoIkNsdXN0ZXIiLGFzLmNoYXJhY3RlcihUZWxsZXkuZGF0YUBpZGVudCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgUHNldWRvTWF0dXJhdGlvbi5zY29yZSA9ICBUZWxsZXkuZGF0YUBtZXRhLmRhdGEkUHNldWRvTWF0dXJhdGlvbi5zY29yZSwKICAgICAgICAgICAgICAgICAgICAgICAgIFpidGIyMCA9IFRlbGxleS5kYXRhQGRhdGFbIlpidGIyMCIsXSwKICAgICAgICAgICAgICAgICAgICAgICAgIExycm4xID0gVGVsbGV5LmRhdGFAZGF0YVsiTHJybjEiLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICBMbW80ID0gVGVsbGV5LmRhdGFAZGF0YVsiTG1vNCIsXSwKICAgICAgICAgICAgICAgICAgICAgICAgIE1wcGVkMiA9IFRlbGxleS5kYXRhQGRhdGFbIk1wcGVkMiIsXSkKYGBgCgoKYGBge3IgZmlnLmRpbT1jKDYsIDQuNSl9CmdncGxvdChkYXRhPWRhdGFFMTIsIGFlcyh4PURvcnNvVmVudHJhbC5TY29yZSwgeT1aYnRiMjAsIGNvbG9yPUNsdXN0ZXIpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gdG9sb3dlcihjKCIjNjhCMDQxIiwgIiNFM0MxNDgiLCAiI0I3RDE3NCIsICIjRTQ2QjZCIikpKSArIAogICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDMwLCBjb2xvcj0icmVkIiwgZmlsbD0iZ3JleSIpICsgCiAgICBnZ3RpdGxlKCJaYnRiMjAiKSArCiAgICB5bGltKDAsTkEpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCmBgYApgYGB7ciBmaWcuZGltPWMoNiwgNC41KX0KZ2dwbG90KGRhdGE9ZGF0YUUxMiwgYWVzKHg9RG9yc29WZW50cmFsLlNjb3JlLCB5PUxycm4xLCBjb2xvcj1DbHVzdGVyKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IHRvbG93ZXIoYygiIzY4QjA0MSIsICIjRTNDMTQ4IiwgIiNCN0QxNzQiLCAiI0U0NkI2QiIpKSkgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSAzMCwgY29sb3I9InJlZCIsIGZpbGw9ImdyZXkiKSArIAogICAgZ2d0aXRsZSgiTHJybjEiKSArCiAgICB5bGltKDAsTkEpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCmBgYAoKYGBge3IgZmlnLmRpbT1jKDYsIDQuNSl9CmdncGxvdChkYXRhPWRhdGFFMTIsIGFlcyh4PURvcnNvVmVudHJhbC5TY29yZSwgeT1MbW80LCBjb2xvcj1DbHVzdGVyKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IHRvbG93ZXIoYygiIzY4QjA0MSIsICIjRTNDMTQ4IiwgIiNCN0QxNzQiLCAiI0U0NkI2QiIpKSkgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSAzMCwgY29sb3I9InJlZCIsIGZpbGw9ImdyZXkiKSArIAogICAgZ2d0aXRsZSgiTG1vNCIpICsKICAgIHlsaW0oMCxOQSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKYGBgCmBgYHtyIGZpZy5kaW09Yyg2LCA0LjUpfQpnZ3Bsb3QoZGF0YT1kYXRhRTEyLCBhZXMoeD1Eb3Jzb1ZlbnRyYWwuU2NvcmUsIHk9TXBwZWQyLCBjb2xvcj1DbHVzdGVyKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IHRvbG93ZXIoYygiIzY4QjA0MSIsICIjRTNDMTQ4IiwgIiNCN0QxNzQiLCAiI0U0NkI2QiIpKSkgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSAzMCwgY29sb3I9InJlZCIsIGZpbGw9ImdyZXkiKSArIAogICAgZ2d0aXRsZSgiTXBwZWQyIikgKwogICAgeWxpbSgwLE5BKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpgYGAKYGBge3IgZmlnLmRpbT1jKDYsIDQuNSl9CmdncGxvdChkYXRhPWRhdGFUZWxsZXksIGFlcyh4PVBzZXVkb01hdHVyYXRpb24uc2NvcmUsIHk9WmJ0YjIwLCBjb2xvcj1DbHVzdGVyKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNiZGQ4ZWYiLCAiIzZkYjhlMiIsICIjMzU3ZWJjIiwgIiMxYzQ4OTYiKSkgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSAzMCwgY29sb3I9InJlZCIsIGZpbGw9ImdyZXkiLHNwYW4gPSAwLjUpICsgCiAgICBnZ3RpdGxlKCJaYnRiMjAiKSArCiAgICB5bGltKDAsTkEpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCmBgYApgYGB7ciBmaWcuZGltPWMoNiwgNC41KX0KZ2dwbG90KGRhdGE9ZGF0YVRlbGxleSwgYWVzKHg9UHNldWRvTWF0dXJhdGlvbi5zY29yZSwgeT1McnJuMSwgY29sb3I9Q2x1c3RlcikpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPSBjKCIjYmRkOGVmIiwgIiM2ZGI4ZTIiLCAiIzM1N2ViYyIsICIjMWM0ODk2IikpICsgCiAgICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgbj0gMzAsIGNvbG9yPSJyZWQiLCBmaWxsPSJncmV5IikgKyAKICAgIGdndGl0bGUoIkxycm4xIikgKwogICAgeWxpbSgwLE5BKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpgYGAKCmBgYHtyIGZpZy5kaW09Yyg2LCA0LjUpfQpnZ3Bsb3QoZGF0YT1kYXRhVGVsbGV5LCBhZXMoeD1Qc2V1ZG9NYXR1cmF0aW9uLnNjb3JlLCB5PUxtbzQsIGNvbG9yPUNsdXN0ZXIpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz0gYygiI2JkZDhlZiIsICIjNmRiOGUyIiwgIiMzNTdlYmMiLCAiIzFjNDg5NiIpKSArIAogICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIG49IDMwLCBjb2xvcj0icmVkIiwgZmlsbD0iZ3JleSIpICsgCiAgICBnZ3RpdGxlKCJMbW80IikgKwogICAgeWxpbSgwLE5BKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpgYGAKYGBge3IgZmlnLmRpbT1jKDYsIDQuNSl9CmdncGxvdChkYXRhPWRhdGFUZWxsZXksIGFlcyh4PVBzZXVkb01hdHVyYXRpb24uc2NvcmUsIHk9TXBwZWQyLCBjb2xvcj1DbHVzdGVyKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9IGMoIiNiZGQ4ZWYiLCAiIzZkYjhlMiIsICIjMzU3ZWJjIiwgIiMxYzQ4OTYiKSkgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBuPSAzMCwgY29sb3I9InJlZCIsIGZpbGw9ImdyZXkiKSArIAogICAgZ2d0aXRsZSgiTXBwZWQyIikgKwogICAgeWxpbSgwLE5BKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpgYGAKCiMgU2Vzc2lvbiBJbmZvCmBgYHtyfQojZGF0ZQpmb3JtYXQoU3lzLnRpbWUoKSwgIiVkICVCLCAlWSwgJUgsJU0iKQoKI1BhY2thZ2VzIHVzZWQKc2Vzc2lvbkluZm8oKQpgYGA=