#Workspace setup
#Import data ##Load filtered objects
U1 <- Read10X_h5("Filtered h5 objects/UTI-1_filtered_feature_bc_matrix.h5")
U2 <- Read10X_h5("Filtered h5 objects/UTI-2_filtered_feature_bc_matrix.h5")
U3 <- Read10X_h5("Filtered h5 objects/UTI-3_filtered_feature_bc_matrix.h5")
C1 <- Read10X_h5("Filtered h5 objects/Control-1_filtered_feature_bc_matrix.h5")
C2 <- Read10X_h5("Filtered h5 objects/Control-2_filtered_feature_bc_matrix.h5")
C3 <- Read10X_h5("Filtered h5 objects/Control-3_filtered_feature_bc_matrix.h5")
##Create seurat objects
#Turn into seurat objects
U1 <- CreateSeuratObject(counts = U1)
U2 <- CreateSeuratObject(counts = U2)
U3 <- CreateSeuratObject(counts = U3)
C1 <- CreateSeuratObject(counts = C1)
C2 <- CreateSeuratObject(counts = C2)
C3 <- CreateSeuratObject(counts = C3)
##Merge, labeling cell.id in metadata
#merge into single seurat object, layers still separated
merged_all <- merge(U1, y = c(U2, U3, C1, C2, C3),
add.cell.ids = c("U1_UTI", "U2_UTI", "U3_UTI", "C1_Control", "C2_Control", "C3_Control"),
project = "RecUTI")
#Adjust metadata
merged_all$sample <- rownames(merged_all@meta.data)
merged_all@meta.data <- separate(merged_all@meta.data, col = 'sample', into = c('sample', 'condition', 'barcode'),
sep = '_')
#label percent mitochondrial DNA
merged_all$percent.mt <- PercentageFeatureSet(merged_all, pattern = "^mt-")
#add number of genes per UMI for each cell to metadata
#log10genesPerUMI expected is ~0.8-0.9 for scRNA data, >1.0 is suspected doublet
merged_all$log10GenesPerUMI <- log10(merged_all$nFeature_RNA) / log10(merged_all$nCount_RNA)
merged_all$GenesPerUMI <- (merged_all$nFeature_RNA / merged_all$nCount_RNA)
#QC ##log10genesperUMI
#Visualize log10genesPerUMI PRIOR to subsetting
VlnPlot(merged_all, features = "log10GenesPerUMI") +
geom_hline(yintercept = 0.95, linetype = "dashed", color = "red") +
geom_hline(yintercept = 0.8, linetype = "dashed", color = "red")

#plot log10UMI vs nCount_RNA
merged_all@meta.data %>%
ggplot(aes(color=condition, x=nCount_RNA, fill= sample)) +
geom_density(alpha = 0.2) +
scale_x_log10() +
theme_classic() +
ylab("log10 cell density") +
geom_vline(xintercept = 500)

##qc feature plots (before subsetting)
#plot nCount vs nFeature
qcfeat1 <- FeatureScatter(merged_all, feature1 = 'nCount_RNA', feature2 = 'nFeature_RNA', group.by = 'sample', pt.size = 0.05)
qcfeat2 <- FeatureScatter(merged_all, feature1 = "nCount_RNA", feature2 = "percent.mt", group.by = 'sample', pt.size = 0.05)
qcfeat3 <- FeatureScatter(merged_all, feature1 = "nFeature_RNA", feature2 = "percent.mt", group.by = 'sample', pt.size = 0.05)
qcfeat1 + qcfeat2 + qcfeat3
##qc vln plots (pre-subsetting)
qc_vln <- VlnPlot(merged_all, features = c('nCount_RNA', 'nFeature_RNA', 'percent.mt'), alpha = 0.1, group.by = 'sample')
qc_vln[[2]] <- qc_vln[[2]] + geom_hline(yintercept = 500) + geom_hline(yintercept = 5000)
qc_vln[[3]] <- qc_vln[[3]] + geom_hline(yintercept = 10)
qc_vln
##Subset data - percent.mt<10, nFeatures 500-5000,
log10genesperUMI>0.8
merged_all <- subset(merged_all, subset = percent.mt<10 &
nFeature_RNA>500 &
nFeature_RNA<5000 &
log10GenesPerUMI >=0.8)
##repeat QC vln plots
qc_vln <- VlnPlot(merged_all, features = c('nCount_RNA', 'nFeature_RNA', 'percent.mt'), alpha = 0.1, group.by = 'sample')
qc_vln[[2]] <- qc_vln[[2]] + geom_hline(yintercept = 500) + geom_hline(yintercept = 5000)
qc_vln[[3]] <- qc_vln[[3]] + geom_hline(yintercept = 10)
qc_vln


##cell density by nFeature_RNA (post-subsetting)
#plot log10UMI vs nFeature_RNA
merged_all@meta.data %>%
ggplot(aes(color=condition, x=nFeature_RNA, fill= sample)) +
geom_density(alpha = 0.2) +
scale_x_log10() +
theme_classic() +
ylab("log10 cell density") +
geom_vline(xintercept = 500)

##cell density by percent.mt
#plot log10UMI vs percent.mt
merged_all@meta.data %>%
ggplot(aes(color=condition, x=percent.mt, fill= sample)) +
geom_density(alpha = 0.2) +
theme_classic() +
ylab("log10 cell density")

##Filter out low quality cells (features expressed in <10
cells)
#subset out features expressed in <10 cells (Suzstak used 10)
counts <- LayerData(merged_all, assay = "RNA", layer = "counts")
keep_genes <- rowSums(counts>0) >=10
merged_all <- subset(merged_all, features = names(keep_genes[keep_genes]))
merged_all #features 33696 -> 17500 genes with sufficient expression data
An object of class Seurat
17535 features across 27248 samples within 1 assay
Active assay: RNA (17535 features, 0 variable features)
6 layers present: counts.1, counts.2, counts.3, counts.4, counts.5, counts.6
#Processing ##Join layers and preprocessing workflow
#Join layers
#Perform standard PREprocessing workflow steps to figure out if we have significant batch effects
merged_all <- JoinLayers(merged_all)
merged_all <- NormalizeData(merged_all)
Performing log-normalization
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
merged_all <- FindVariableFeatures(merged_all, nfeatures = 3000)
Calculating gene variances
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
merged_all <- ScaleData(merged_all)
|
| | 0%
|
|======================================= | 33%
|
|============================================================================== | 67%
|
|=====================================================================================================================| 100%
top_var_genes_3000 <- head(VariableFeatures(merged_all), 20)
print(top_var_genes_3000)
[1] "Jchain" "Igkc" "Igha" "Hba-a1" "Hbb-bs" "Slc12a3" "Slc26a4" "Abca13" "Atp6v1g3" "Calb1" "Ngp"
[12] "Mzb1" "Lcn2" "Ccl4" "Robo2" "Camp" "Nrxn3" "Retnlg" "S100g" "Cxcl10"
##Visualize most variable features
#Visualize most variable features
VariableFeaturePlot(merged_all)

LabelPoints(points = top_var_genes_3000, plot = last_plot(), repel = TRUE)

##decide number of PCAs to use for analysis: npcs=20
#decide number of PCAs to use for analysis
merged_all <- RunPCA(merged_all, npcs = 50)
ElbowPlot_50PCAs <- ElbowPlot(merged_all, ndims = 50) +
scale_y_continuous(n.breaks = 20) +
geom_hline(yintercept = 2) +
geom_vline(xintercept = 20)
ElbowPlot_50PCAs

##Standard processing and clustering, 0.6 resolution
#Standard processing and clustering
merged_all <- FindNeighbors(merged_all, dims = 1:20, verbose = F)
merged_all <- FindClusters(merged_all, resolution = 0.6)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 27248
Number of edges: 954660
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9489
Number of communities: 27
Elapsed time: 3 seconds
merged_all <- RunUMAP(merged_all, dims = 1:20)
Using method 'umap'
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
##Calculate number of cells in each cluster
print(cells_per_cluster)
print(cells_per_cluster_cond)
#UMAP visualization pre-doublet removal
Idents(merged_all) <- "seurat_clusters"
DimPlot(merged_all, reduction = "umap", label = T, repel = T) + ggtitle("Seurat Clusters")

#Remove doublets
##Remove doublets
#install and load scDblFinder package
#BiocManager::install("scDblFinder")
library(scDblFinder)
library(SingleCellExperiment)
##convert to single cell experiment and run scDblFinder
#convert to single cell experiment and run scDblFinder
sce <- as.SingleCellExperiment(merged_all)
sce <- scDblFinder::scDblFinder(sce, samples = 'sample')
|
| | 0%
|
|==================== | 17%
|
|======================================= | 33%
|
|========================================================== | 50%
|
|============================================================================== | 67%
|
|================================================================================================== | 83%
|
|=====================================================================================================================| 100%
##add doublet results back into metadata and visualize on UMAP
#add results back into seurat metadata
merged_all$scDblFinder.class <- colData(sce)$scDblFinder.class
#visualize
sing_vs_doub_before_subset <- DimPlot(merged_all,split.by = 'condition', reduction = "umap", group.by = "scDblFinder.class", pt.size = 0.5) + ggtitle("scDblFinder: Singlets vs Doublets")
sing_vs_doub_before_subset

##subset to only singlets
##rename merged_all2 and findneighbors and clusters again?
merged_all2 <- merged_all
merged_all2 <- FindNeighbors(merged_all2, dims = 1:20, verbose = F)
merged_all2 <- FindClusters(merged_all2, resolution = 0.6)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 26229
Number of edges: 923064
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9492
Number of communities: 25
Elapsed time: 2 seconds
merged_all2 <- RunUMAP(merged_all2, dims = 1:20)
Using method 'umap'
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
A <- DimPlot(merged_all, label = T, repel = T) + NoLegend() + ggtitle("Old Clusters After \nDoublet Removal")
B <- DimPlot(merged_all2, label = T, repel = T) + NoLegend() + ggtitle("Reclustering After \nDoublet Removal")
A+B

##calculate cells in each cluster after doublet removal and
reclustering
cells_per_cluster2 <- merged_all2@meta.data %>%
group_by(seurat_clusters) %>%
summarise(cell_count = n()) %>%
arrange(seurat_clusters)
cells_per_cluster_cond2 <- merged_all2@meta.data %>%
group_by(seurat_clusters, condition) %>%
summarise(cell_count = n()) %>%
arrange(seurat_clusters, condition)
View(cells_per_cluster2)
View(cells_per_cluster_cond2)
print(cells_per_cluster2)
print(cells_per_cluster_cond2)
#Visualization ##By condition
#Group by condition
DimPlot(merged_all2, reduction = "umap", group.by = "condition", pt.size = 0.1, alpha = 0.3)

##by sample, split by condition

##grouped by cluster, split by condition

##Bar graph number of cells per cluster from each individual
sample
#visualize percent of each cluster from individual samples
meta_data <- merged_all2@meta.data
# Count cells per cluster per sample
cluster_counts <- meta_data %>%
group_by(seurat_clusters, sample) %>%
summarise(count = n(), .groups = 'drop')
# Calculate total cells per sample
total_per_cluster <- cluster_counts %>%
group_by(seurat_clusters) %>%
summarise(total = sum(count), .groups = 'drop')
# Merge and calculate percentage
cluster_percentages <- cluster_counts %>%
left_join(total_per_cluster, by = "seurat_clusters") %>%
mutate(percentage = (count / total) * 100)
# Plot
ggplot(cluster_percentages, aes(x = seurat_clusters, y = percentage, fill = sample)) +
geom_bar(stat = "identity", position = "stack") +
labs(title = "Cluster Composition By Sample",
x = "Cluster",
y = "Percentage of Cluster",
fill = "Sample") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

NA
NA
##percent of each cluster from each condition
#visualize percent of each cluster from each condition
# Count cells per cluster per condition
cluster_counts_cond <- meta_data %>%
group_by(seurat_clusters, condition) %>%
summarise(count = n(), .groups = 'drop')
# Calculate total cells per sample
total_cond_per_cluster <- cluster_counts_cond %>%
group_by(seurat_clusters) %>%
summarise(total = sum(count), .groups = 'drop')
# Merge and calculate percentage
cluster_cond_percentages <- cluster_counts_cond %>%
left_join(total_cond_per_cluster, by = "seurat_clusters") %>%
mutate(percentage = (count / total) * 100)
# Plot
ggplot(cluster_cond_percentages, aes(x = seurat_clusters, y = percentage, fill = condition)) +
geom_bar(stat = "identity", position = "stack") +
labs(title = "Cluster Composition by Condition",
x = "Cluster",
y = "Percentage of Cluster",
fill = "Sample") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

##visualize percent.mt per cluster
#add metadata column for cluster_condition
merged_all2$cluster_condition <- paste0(merged_all2$seurat_clusters,"_",merged_all2$condition)
Idents(merged_all2) <- merged_all2$cluster_condition
#check that metadata column is added
colnames(merged_all2@meta.data)
[1] "orig.ident" "nCount_RNA" "nFeature_RNA" "sample" "condition" "barcode"
[7] "percent.mt" "log10GenesPerUMI" "GenesPerUMI" "RNA_snn_res.0.6" "seurat_clusters" "scDblFinder.class"
[13] "cluster_condition"
# Create summary data frame
percent_mt_summary <- merged_all2@meta.data %>%
group_by(seurat_clusters) %>%
summarise(
mean_percent_mt = mean(percent.mt, na.rm = TRUE),
median_percent_mt = median(percent.mt, na.rm = TRUE),
n_cells = n()
)
print(percent_mt_summary)
ggplot(percent_mt_summary, aes(x = factor(seurat_clusters), y = mean_percent_mt)) +
geom_bar(stat = "identity", fill = "#2c7fb8") +
labs(
title = "Mean Mitochondrial Percentage by Cluster",
x = "Cluster",
y = "Mean % Mitochondrial Genes"
) +
theme_minimal()

clusters_table <- table(merged_all2$seurat_clusters, merged_all2$sample)
clusters_table_cond <- table(merged_all2$seurat_clusters, merged_all2$condition)
clusters_table
C1 C2 C3 U1 U2 U3
0 209 414 535 325 612 820
1 124 379 872 234 585 670
2 139 332 596 231 775 780
3 168 283 371 656 304 405
4 123 174 507 516 256 454
5 111 339 448 126 433 543
6 67 232 335 148 332 385
7 50 103 441 135 285 266
8 134 127 123 640 50 144
9 51 82 68 855 65 91
10 99 280 141 106 194 194
11 20 39 46 414 33 100
12 4 4 30 65 197 335
13 15 41 190 12 113 143
14 29 88 83 48 133 114
15 40 36 113 58 88 140
16 11 40 102 51 121 116
17 4 8 74 17 98 178
18 20 31 51 43 77 90
19 43 23 60 54 40 89
20 5 45 59 23 66 56
21 3 34 46 12 46 85
22 28 8 64 60 52 12
23 1 9 13 4 51 47
24 7 7 36 18 28 20
clusters_table_cond
Control UTI
0 1158 1757
1 1375 1489
2 1067 1786
3 822 1365
4 804 1226
5 898 1102
6 634 865
7 594 686
8 384 834
9 201 1011
10 520 494
11 105 547
12 38 597
13 246 268
14 200 295
15 189 286
16 153 288
17 86 293
18 102 210
19 126 183
20 109 145
21 83 143
22 100 124
23 23 102
24 50 66
#Create cluster tree
# Clustree visualization
install.packages("clustree")
Error in install.packages : Updating loaded packages
library(clustree)
merged_tree <- merged_all2
#Add multiple resolutions
merged_tree <- FindClusters(merged_tree, resolution = c(0.1, 0.2, 0.4, 0.6, 0.8))
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 26229
Number of edges: 923064
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9871
Number of communities: 14
Elapsed time: 3 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 26229
Number of edges: 923064
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9777
Number of communities: 17
Elapsed time: 2 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 26229
Number of edges: 923064
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9621
Number of communities: 24
Elapsed time: 2 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 26229
Number of edges: 923064
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9492
Number of communities: 25
Elapsed time: 2 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 26229
Number of edges: 923064
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9369
Number of communities: 28
Elapsed time: 3 seconds
install.packages("clustree")
Warning in install.packages :
package ‘clustree’ is in use and will not be installed
cluster.tree <- clustree(
merged_tree, "RNA_snn_res.") + ggtitle("Cluster tree")
ggsave("resolution cluster tree.svg", plot = cluster.tree, width = 12)
cluster.tree

##silhouette plot

#Evaluate conserved marker genes, use to consolidate smaller clusters
into larger ones
Idents(merged_all2) <- 'seurat_clusters'
merged_all2$consolidated_clusters <- merged_all2$seurat_clusters
library(presto)
library(SeuratWrappers)
# Initialize the list
conserved_markers <- list()
# Loop through the clusters in the specified order
for (i in 0:24) {
aux <- SeuratWrappers::RunPresto(
merged_all2,
ident.1 = i,
only.pos = TRUE,
logfc.threshold = 0.5,
min.pct = 0.4
)
if (!is.null(aux) && nrow(aux) > 0) {
aux$gene <- rownames(aux)
aux$cluster <- i
conserved_markers[[paste0("cluster_", i)]] <- aux
}
}
#filter
for(i in names(conserved_markers)){
aux <- conserved_markers[[i]]
aux$cluster <- i
aux <- aux[aux$p_val_adj < 0.05,]
aux <- aux[order(aux$avg_log2FC, decreasing=T),]
conserved_markers[[i]] <- aux
}
##export conserved markers as excel file
#Consolidate clusters
#copy seurat_clusters to new metadate column
merged_all2$consolidated_clusters <- merged_all2$seurat_clusters
#set identity as new column, 'consolidated_clusters'
Idents(merged_all2) <- 'consolidated_clusters'
# Make new column a character vector instead of factor
merged_all2@meta.data$consolidated_clusters <-
as.character(merged_all2@meta.data$consolidated_clusters)
#define 'current clusters' for mapping later
current_clusters <- as.character(merged_all2$seurat_clusters)
#Consolidations (unnamed clusters go unchanged)
new_cluster_map <- c(
"24" = "12",
"23" = "1",
"21" = "14"
)
##5,10,14,18,21 are all myeloid (CD68+)
#14/21 = DC
##from cluster tree: 5 and 10 combine, 14, 18, and 21 combine
#24 -> 12 (gamma delta T?) -> 7 (also expressing Trgv in top genes)
#Apply mapping back to metadata
merged_all2$consolidated_clusters <- ifelse(
current_clusters %in% names(new_cluster_map),
new_cluster_map[current_clusters],
current_clusters
)
# Convert back to factor (optional, but usually cleaner for Idents)
merged_all2@meta.data$consolidated_clusters <-
factor(merged_all2@meta.data$consolidated_clusters, levels = c(0:20,22))
# Set active identities correctly
Idents(merged_all2) <- 'consolidated_clusters'
#Naming clusters (preliminary, manual). In new object
‘merged_annot’
merged_annot <- merged_all2
merged_annot$annot <- merged_annot$consolidated_clusters
#set identity as new column, 'consolidated_clusters'
Idents(merged_annot) <- 'annot'
# Make new column a character vector instead of factor
merged_annot@meta.data$annot <-
as.character(merged_annot@meta.data$annot)
#define 'current clusters' for mapping later
current_clusters_annot <- as.character(merged_annot$annot)
#Consolidations (unnamed clusters go unchanged)
new_cluster_map_annot <- c(
"0" = "Cytotoxic NK-T",
"1" = "B cell",
"2" = "Naive T_TCM",
"3" = "Neutrophil",
"4" = "PT group 1",
"5" = "Activated macrophage",
"6" = "Immature T_group 2",
"7" = "Th1",
"8" = "PT group 2",
"9" = "PT group 3",
"10" = "Myeloid",
"11" = "PT group 4",
"12" = "T cell_gamma_delta",
"13" = "TAL_DCT",
"14" = "DC",
"15" = "DCT_CNT",
"16" = "TAL_DCT",
"17" = "TAL",
"18" = "Mono_macrophage",
"19" = "Tissue-resident NK-T",
"20" = "Endothelial",
"22" = "Intercalated cell")
#Apply mapping back to metadata
merged_annot$annot <- ifelse(
current_clusters_annot %in% names(new_cluster_map_annot),
new_cluster_map_annot[current_clusters_annot],
current_clusters_annot
)
# Convert back to factor (optional, but usually cleaner for Idents)
merged_annot@meta.data$annot <-
factor(merged_annot@meta.data$annot)
# Set active identities correctly
Idents(merged_annot) <- 'annot'
#Visualize umap with annotations

##split by condition

Idents(merged_annot) <- 'annot'
annotations <- list()
annotations <- unique(Idents(merged_annot))
# Initialize the list
conserved_markers_annot <- list()
# Loop through the clusters in the specified order
for (i in unique(annotations)) {
aux <- SeuratWrappers::RunPresto(
merged_annot,
ident.1 = i,
only.pos = TRUE,
logfc.threshold = 0.5,
min.pct = 0.4
)
if (!is.null(aux) && nrow(aux) > 0) {
aux$gene <- rownames(aux)
aux$cluster <- i
conserved_markers_annot[[i]] <- aux
}
}
#filter
for(i in names(conserved_markers_annot)){
aux <- conserved_markers_annot[[i]]
aux$cluster <- i
aux <- aux[aux$p_val_adj < 0.05,]
aux <- aux[order(aux$avg_log2FC, decreasing=T),]
conserved_markers_annot[[i]] <- aux
}
merged_all2$consolidated_clusters <- merged_all2$seurat_clusters
Idents(merged_all2) <- 'consolidated_clusters'
##24 into 12, T cell population with Trg
merged_all2@meta.data$consolidated_clusters <-
ifelse(
merged_all2@meta.data$consolidated_clusters == "24",
12,
merged_all2@meta.data$consolidated_clusters
)
#23 into 1, B cells - lots of immunoglobulins in 23 and jchain, 1 is cd19-high b cell group
merged_all2@meta.data$consolidated_clusters <-
ifelse(
merged_all2@meta.data$consolidated_clusters == "23",
1,
merged_all2@meta.data$consolidated_clusters
)
unique(merged_all2$consolidated_clusters)
++++ #Evaluate conserved and differentially expressed genes
Idents(merged_all2) <- 'seurat_clusters'
# Define the custom order
cluster_order_0.1 <- c(0, 1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9)
# Initialize the list
conserved_markers_res0.1 <- list()
# Loop through the clusters in the specified order
for (i in cluster_order_0.1) {
aux <- SeuratWrappers::RunPresto(
merged_all_cons,
ident.1 = i,
only.pos = TRUE,
logfc.threshold = 0.5,
min.pct = 0.4
)
if (!is.null(aux) && nrow(aux) > 0) {
aux$gene <- rownames(aux)
aux$cluster <- i
conserved_markers_res0.1[[paste0("cluster_", i)]] <- aux
}
}
#filter
for(i in names(conserved_markers_res0.1)){
aux <- conserved_markers_res0.1[[i]]
aux$cluster <- i
aux <- aux[aux$p_val_adj < 0.05,]
aux <- aux[order(aux$avg_log2FC, decreasing=T),]
conserved_markers_res0.1[[i]] <- aux
}
# take top 15 genes from each cluster to do annotations
cluster_annot_df_0.1 <- conserved_markers_res0.1 %>%
map(~ head(.x, 10)) %>% # Take first 10 rows of each data frame
bind_rows() # Combine into one data frame
rio::export(conserved_markers_res0.1, "all pos conserved genes per cluster.xlsx")
rio::export(cluster_annot_df_0.1, "top 15 conserved genes per cluster.xlsx")
#Put in cluster annotations
l1_annotations_res0.1 <- c(
"PT", # cluster 0
"T cell", # cluster 1
"Myeloid", # cluster 2
"B Cell", # cluster 3
"NK-T", # cluster 4
"T cell", # cluster 5
"PMN", # cluster 6
"Fibro_Stromal", # cluster 7
"Podocyte", # cluster 8
"Macrophage", # cluster 9
"T cell", # cluster 10
"TAL", # cluster 11
"pDC", # cluster 12
"DCT", # cluster 13
"IC-B", # cluster 14
"ILC2/Myeloid" # cluster 15
)
Idents(merged_all_cons) <- 'RNA_snn_res.0.1'
unique(Idents(merged_all_cons))
cluster.tree
Idents(merged_all_cons) <- 'seurat_clusters'
DefaultAssay(merged_all_cons)
merged_0.05 <- FindClusters(merged_0.05, resolution = 0.01)
cluster.tree.8dim <- clustree(merged_0.05, prefix = "RNA_snn_res.")
cluster.tree.8dim
# Make sure cluster IDs are numeric and 0-based
clusters <- as.numeric(as.character(merged_all_cons$RNA_snn_res.0.1))
# Map cluster IDs to manual annotations
# Create a named vector for easy lookup
names(l1_annotations_res0.1) <- as.character(0:15)
# Add annotation metadata by matching cluster IDs
merged_all_cons@meta.data$l1_annotations_res0.1 <- l1_annotations_res0.1[as.character(clusters)]
merged_all_cons@meta.data
Idents(merged_all_cons) <- 'annotation'
DimPlot(merged_all_cons, label = T, repel = T)
Idents(merged_all_cons) <- 'RNA_snn_res.0.1'
DimPlot(merged_all_cons, reduction = "tsne")+ ggtitle("unbiased clustering")
Idents(obj_annot) <- 'grouping'
DimPlot(obj_annot, reduction = "tsne", split.by = 'condition')
DimPlot(merged_all_cons, label = T, repel = T, split.by = 'condition') + NoLegend()
clusters_0.1_table <- table(merged_all_cons$RNA_snn_res.0.1, merged_all_cons$sample)
clusters_0.1_table_cond <- table(merged_all_cons$RNA_snn_res.0.1, merged_all_cons$condition)
clusters_0.1_table
clusters_0.1_table_cond
clusters_0.01_table <- table(merged_0.05$RNA_snn_res.0.01, merged_0.05$sample)
clusters_0.01_table_cond <- table(merged_0.05$RNA_snn_res.0.01, merged_0.05$condition)
clusters_0.01_table
clusters_0.01_table_cond
clusters_0.01_df <- as.data.frame(clusters_0.01_table)
clusters_0.01_df
clusters_0.01_df$annot <- c("lymphocyte", "myeloid", "PT", "B cell", "NK", "PMN", "TAL_Distal tubule", "PT_EMT_Fibro", "DCT")
merged_all_cons@meta.data$l1_annotations_res0.1 <-
ifelse(
merged_all_cons@meta.data$l1_annotations_res0.1 == "Fibroblast/Stroma",
"Fibro_Stromal",
merged_all_cons@meta.data$l1_annotations_res0.1
)
Idents(merged_all_cons) <- 'RNA_snn_res.0.8'
DE_0.8_10v19 <- SeuratWrappers::RunPresto(merged_all_cons, ident.1 = 10, ident.2 = 19)
gene <- rownames(DE_0.8_10v19)
DE_0.8_10v19$gene <- gene
DE_0.8_10v19 <- DE_0.8_10v19[DE_0.8_10v19$p_val_adj < 0.05,]
DE_0.8_10v19 <- order(DE_0.8_10v19$avg_log2FC, decreasing = T)
#compare DE gene expression between UTI/controls in each cluster
#defined as deg_cons (list)
colnames(merged_all_cons@meta.data)
merged_all_cons@meta.data$l1_condition <- paste0(merged_all_cons@meta.data$l1_annotations_res0.1,"_",merged_all_cons$condition)
Idents(merged_all_cons) <- 'l1_condition'
unique(Idents(merged_all_cons))
DEg_res_0.1_UTI <- list()
for(i in unique(merged_all_cons$l1_annotations_res0.1)){
aux <- SeuratWrappers::RunPresto(merged_all_cons,ident.1 = paste0(i,"_UTI"), ident.2 = paste0(i,"_Control"))
aux$gene <- rownames(aux)
DEg_res_0.1_UTI[[paste0(i,"_UTI")]] <- aux
}
#Filter for easier analysis
for(i in names(DEg_res_0.1_UTI)){
aux <- DEg_res_0.1_UTI[[i]]
aux <- aux[aux$p_val_adj < 0.05,]
aux <- aux[order(aux$avg_log2FC, decreasing=T),]
DEg_res_0.1_UTI[[i]] <- aux
}
names(DEg_res_0.1_UTI)[names(DEg_res_0.1_UTI) %in% "ILC2/Myeloid_UTI"] <- "ILC2_UTI"
#export as excel file
rio::export(DEg_res_0.1_UTI, "res 0.1_DE genes.xlsx")
#save RDS and create loupe
saveRDS(merged_all_cons, "merged_obj.RDS")
save.image("res 0.5_PCA 25_percentmt10.RData")
setwd("C:/Users/evrajadh/OneDrive - Indiana University/Research/Recurrent UTI GFR/ScRNA/Rec_UTI")
create_loupe_from_seurat(merged_all_cons, output_dir = "cloupes/", output_name = "manual annot_res_0.5")
#Split object by condition and analyze conditions separately
merged_split <- SplitObject(merged_all_cons, split.by = "condition")
#eval conserved markers by condition
merged_split$UTI
merged_UTI <- merged_split$UTI
#Add multiple resolutions
merged_UTI <- RunPCA(merged_UTI, npcs=30)
merged_UTI <- FindNeighbors(merged_UTI, dims = 1:20, verbose = F)
merged_UTI <- FindClusters(merged_UTI, resolution = c(0.1, 0.2, 0.4, 0.6, 0.8))
merged_UTI <- RunUMAP(merged_UTI, dims = 1:20)
DimPlot(merged_UTI)
cluster.tree_UTI <- clustree(
merged_UTI, "RNA_snn_res.") + ggtitle("Cluster tree")
cluster.tree_UTI
#haven't removed doublets, only preliminary
Idents
Idents(merged_UTI) <- 'l1_annotations_res0.1'
DEg_UTI_LOW_res <- list()
for(i in unique(merged_0.05$RNA_snn_res.0.01)){
aux <- SeuratWrappers::RunPresto(merged_0.05,ident.1 = paste0(i,"_UTI"), ident.2 = paste0(i,"_Control"))
aux$gene <- rownames(aux)
DEg_UTI_LOW_res[[paste0(i,"_UTI")]] <- aux
}
#Filter for easier analysis
for(i in names(DEg_UTI_LOW_res)){
aux <- DEg_UTI_LOW_res[[i]]
aux <- aux[aux$p_val_adj < 0.05,]
aux <- aux[order(aux$avg_log2FC, decreasing=T),]
DEg_UTI_LOW_res[[i]] <- aux
}
rio::export(DEg_UTI_LOW_res, "LOW resolution DEgenes_UTI.xlsx")
Idents(merged_0.05) <- 'RNA_snn_res.0.01'
DimPlot(merged_0.05, reduction = "tsne")
DimPlot(merged_0.05, reduction = "pca", label = T, repel = T)
Idents(merged_0.05) <- 'l1_annotations_res0.1'
DimPlot(merged_0.05, reduction = "tsne", label = T)
DimPlot(merged_0.05, reduction = "pca", label = T) + NoLegend()
obj_annot <- merged_all_cons
obj_annot$grouping <- NA
obj_annot$grouping[obj_annot$RNA_snn_res.0.1 %in% c(1,5,10,15,4,3)] <- "Lymphocyte"
obj_annot$grouping[obj_annot$RNA_snn_res.0.1 %in% c(2,12,9,6)] <- "Myeloid"
obj_annot$grouping[obj_annot$RNA_snn_res.0.1 %in% c(0)] <- "PT"
obj_annot$grouping[obj_annot$RNA_snn_res.0.1 %in% c(7)] <- "PT_EMT"
obj_annot$grouping[obj_annot$RNA_snn_res.0.1 %in% c(14,11,8,13)] <- "TAL_distal tubule"
Idents(obj_annot) <- 'grouping'
unique(Idents(obj_annot))
conserved_genes_lowres_grouping <- list()
Idents(obj_annot) <- 'grouping'
# Loop through the clusters in the specified order
conserved_genes_lowres_grouping <- SeuratWrappers::RunPrestoAll(obj_annot,
only.pos = TRUE,
logfc.threshold = 0.5,
min.pct = 0.4)
conserved_genes_lowres_grouping$gene <- rownames(conserved_genes_lowres_grouping)
rio::export(conserved_genes_lowres_grouping, "Cluster_GROUPING_conserved genes.xlsx")
obj_annot$group_cond <- paste0(obj_annot$grouping,'_',obj_annot$condition)
Idents(obj_annot) <- 'group_cond'
DEg_UTI_LOW_res_annot <- list()
for(i in unique(obj_annot$grouping)){
aux <- SeuratWrappers::RunPresto(obj_annot,ident.1 = paste0(i,"_UTI"), ident.2 = paste0(i,"_Control"))
aux$gene <- rownames(aux)
DEg_UTI_LOW_res_annot[[paste0(i,"_UTI")]] <- aux
}
#Filter for easier analysis
for(i in names(DEg_UTI_LOW_res_annot)){
aux <- DEg_UTI_LOW_res_annot[[i]]
aux <- aux[aux$p_val_adj < 0.05,]
aux <- aux[order(aux$avg_log2FC, decreasing=T),]
DEg_UTI_LOW_res_annot[[i]] <- aux
}
rio::export(DEg_UTI_LOW_res_annot, "DEgenes cluster_GROUPING_UTIvControl.xlsx")
rio::export(DEg_UTI_LOW_res, "LOW resolution DEgenes_UTI.xlsx")
Monocling
Idents(merged_all_cons) <- 'RNA_snn_res.0.1'
pt_cells <- subset(merged_all_cons, idents = c('0', '7'))
library(monocle3)
cds <- SeuratWrappers::as.cell_data_set(pt_cells)
cds <- cluster_cells(cds)
cds <- learn_graph(cds)
plot_cells(cds, color_cells_by = "cluster", label_cell_groups = T)
Idents(obj_annot) <- 'l1_annotations_res0.1'
unique(Idents(obj_annot))
obj_annot$l1_annotations_res0.1[obj_annot$l1_annotations_res0.1 %in% "Fibro_Stromal"] <- "PT_EMT"
obj_annot$l1_annotations_res0.1[obj_annot$l1_annotations_res0.1 %in% "ILC2"] <- "ILC"
obj_annot$l1_annotations_res0.1[obj_annot$l1_annotations_res0.1 %in% "Podocyte"] <- "TAL_DCT"
obj_annot$l1_annotations_res0.1[obj_annot$l1_annotations_res0.1 %in% "pDC"] <- "DC"
cds <- SeuratWrappers::as.cell_data_set(obj_annot)
cds <- cluster_cells(cds)
cds <- learn_graph(cds)
# Plot to choose root node visually
plot_cells(cds, color_cells_by = "cluster", label_cell_groups = T, label_roots = T)
get_earliest_principal_node <- function(cds) {
pr_graph_test <- principal_graph(cds)[[1]]
names(which.min(igraph::degree(pr_graph_test)))
}
# Once you identify the root cell/cluster:
cds <- order_cells(cds, root_pr_nodes = get_earliest_principal_node(cds))
plot_cells(cds, color_cells_by = "pseudotime", label_groups_by_cluster = FALSE)
DimPlot(obj_annot)
BiocManager::install("slingshot")
library(slingshot)
Idents(obj_annot) <- 'l1_annotations_res0.1'
DimPlot(obj_annot, reduction = 'tsne') + DimPlot(obj_annot, reduction = 'pca')
sce <- as.SingleCellExperiment(obj_annot)
sce <- slingshot(sce, clusterLabels = obj_annot$l1_annotations_res0.1, reducedDim = 'UMAP')
#Pseudotime analysis with slingshot
sce <- as.SingleCellExperiment(obj_annot)
sce <- slingshot::slingshot(sce, clusterLabels = sce$l1_annotations_res0.1, reducedDim = 'UMAP')
#plot pseudotime
library(scater)
plot(reducedDim(sce, "UMAP"), col = sce$slingPseudotime_1 , pch = 16)
plot(reducedDim(sce, "TSNE"), col = sce$slingPseudotime_1 , pch = 16)
plot(reducedDim(sce, "UMAP"), col = sce$slingPseudotime_1 , pch = 16)
clusters <- sce$l1_annotations_res0.1
centers <- t(sapply(unique(clusters),function(cl){
colMeans(reducedDim(sce, "UMAP")[clusters == cl,])
}))
text(centers, labels = unique(clusters),col = "black", cex = 0.8)
sce <- as.SingleCellExperiment(pt_cells)
sce <- slingshot::slingshot(sce, clusterLabels = sce$l1_annotations_res0.1, reducedDim = 'UMAP')
plot(reducedDim(sce, "UMAP"), col = sce$slingPseudotime_1 , pch = 10)
clusters <- sce$l1_annotations_res0.1
centers <- t(sapply(unique(clusters),function(cl){
colMeans(reducedDim(sce, "UMAP")[clusters == cl,])
}))
text(centers, labels = unique(clusters),col = "black", cex = 0.8)
#For just PT cells and PT_EMT
# Extract data from SingleCellExperiment
umap_df <- as.data.frame(reducedDim(sce, "UMAP"))
colnames(umap_df) <- c("UMAP_1", "UMAP_2") # Rename to readable names
umap_df$cluster <- sce$l1_annotations_res0.1
umap_df$pseudotime <- sce$slingPseudotime_1
# Calculate cluster centroids for labeling
label_df <- umap_df %>%
group_by(cluster) %>%
summarize(UMAP_1 = mean(UMAP_1), UMAP_2 = mean(UMAP_2))
# Plot with ggplot2
ggplot(umap_df, aes(x = UMAP_1, y = UMAP_2, color = pseudotime)) +
geom_point(size = 0.5) +
scale_color_viridis_c() +
geom_text(data = label_df, aes(x = UMAP_1, y = UMAP_2, label = cluster),
color = "black", size = 4) +
theme_minimal() +
labs(x = "UMAP 1", y = "UMAP 2", title = "Slingshot Pseudotime [PT Cells] with Cluster Labels")
sce <- as.SingleCellExperiment(obj_annot)
sce <- slingshot::slingshot(sce, clusterLabels = sce$RNA_snn_res.0.4, reducedDim = 'UMAP')
#For just PT cells and PT_EMT
# Extract data from SingleCellExperiment
umap_df <- as.data.frame(reducedDim(sce, "UMAP"))
colnames(umap_df) <- c("UMAP_1", "UMAP_2") # Rename to readable names
umap_df$cluster <- sce$l1_annotations_res0.1
umap_df$pseudotime <- sce$slingPseudotime_1
# Calculate cluster centroids for labeling
label_df <- umap_df %>%
group_by(cluster) %>%
summarize(UMAP_1 = mean(UMAP_1), UMAP_2 = mean(UMAP_2))
# Plot with ggplot2
ggplot(umap_df, aes(x = UMAP_1, y = UMAP_2, color = pseudotime)) +
geom_point(size = 0.5) +
scale_color_viridis_c() +
geom_text(data = label_df, aes(x = UMAP_1, y = UMAP_2, label = cluster),
color = "black", size = 4) +
theme_minimal() +
labs(x = "UMAP 1", y = "UMAP 2", title = "Slingshot Pseudotime [PT Cells] with Cluster Labels")
Idents(obj_annot) <- 'grouping'
unique(Idents(obj_annot))
#already subsetted PT and PT_EMT to pt_cells
myeloid_cells <- subset(obj_annot, idents = "Myeloid")
lympho_cells <- subset(obj_annot, idents = "Lymphocyte")
TAL_DCT_cells <- subset(obj_annot, idents = "TAL_distal tubule")
#myeloid slingshot TSNE
sce_myeloid <- as.SingleCellExperiment(myeloid_cells)
sce_myeloid <- slingshot::slingshot(sce_myeloid, clusterLabels = sce_myeloid$RNA_snn_res.0.4, reducedDim = 'TSNE')
#For just PT cells and PT_EMT
# Extract data from SingleCellExperiment
umap_myeloid_df <- as.data.frame(reducedDim(sce_myeloid, "TSNE"))
colnames(umap_myeloid_df) <- c("TSNE_1", "TSNE_2") # Rename to readable names
umap_myeloid_df$cluster <- sce_myeloid$l1_annotations_res0.1
umap_myeloid_df$pseudotime <- sce_myeloid$slingPseudotime_1
# Calculate cluster centroids for labeling
label_myeloid_df <- umap_myeloid_df %>%
group_by(cluster) %>%
summarize(TSNE_1 = mean(TSNE_1), TSNE_2 = mean(TSNE_2))
# Plot with ggplot2
ggplot(umap_myeloid_df, aes(x = TSNE_1, y = TSNE_2, color = pseudotime)) +
geom_point(size = 0.5) +
scale_color_viridis_c() +
geom_text(data = label_myeloid_df, aes(x = TSNE_1, y = TSNE_2, label = cluster),
color = "black", size = 4) +
theme_minimal() +
labs(x = "TSNE1", y = "TSNE 2", title = "Slingshot Pseudotime [Myeloid Cells] with Cluster Labels")
#myeloid slingshot UMAP
sce_myeloid <- as.SingleCellExperiment(myeloid_cells)
sce_myeloid <- slingshot::slingshot(sce_myeloid, clusterLabels = sce_myeloid$RNA_snn_res.0.4, reducedDim = 'UMAP')
#For just PT cells and PT_EMT
# Extract data from SingleCellExperiment
umap_myeloid_df <- as.data.frame(reducedDim(sce_myeloid, "UMAP"))
colnames(umap_myeloid_df) <- c("UMAP_1", "UMAP_2") # Rename to readable names
umap_myeloid_df$cluster <- sce_myeloid$l1_annotations_res0.1
umap_myeloid_df$pseudotime <- sce_myeloid$slingPseudotime_1
# Calculate cluster centroids for labeling
label_myeloid_df <- umap_myeloid_df %>%
group_by(cluster) %>%
summarize(UMAP_1 = mean(UMAP_1), UMAP_2 = mean(UMAP_2))
# Plot with ggplot2
ggplot(umap_myeloid_df, aes(x = UMAP_1, y = UMAP_2, color = pseudotime)) +
geom_point(size = 0.5) +
scale_color_viridis_c() +
geom_text(data = label_myeloid_df, aes(x = UMAP_1, y = UMAP_2, label = cluster),
color = "black", size = 4) +
theme_minimal() +
labs(x = "UMAP 1", y = "UMAP 2", title = "Slingshot Pseudotime [PT Cells] with Cluster Labels")
#lympho slingshot
sce_lymph <- as.SingleCellExperiment(lympho_cells)
sce_lymph <- slingshot::slingshot(sce_lymph, clusterLabels = sce_lymph$RNA_snn_res.0.4, reducedDim = 'TSNE')
#For just PT cells and PT_EMT
# Extract data from SingleCellExperiment
umap_lymph_df <- as.data.frame(reducedDim(sce_lymph, "TSNE"))
colnames(umap_lymph_df) <- c("TSNE_1", "TSNE_2") # Rename to readable names
umap_lymph_df$cluster <- sce_lymph$l1_annotations_res0.1
umap_lymph_df$pseudotime <- sce_lymph$slingPseudotime_1
# Calculate cluster centroids for labeling
label_lymph_df <- umap_lymph_df %>%
group_by(cluster) %>%
summarize(TSNE_1 = mean(TSNE_1), TSNE_2 = mean(TSNE_2))
# Plot with ggplot2
ggplot(umap_lymph_df, aes(x = TSNE_1, y = TSNE_2, color = pseudotime)) +
geom_point(size = 0.5) +
scale_color_viridis_c() +
geom_text(data = label_lymph_df, aes(x = TSNE_1, y = TSNE_2, label = cluster),
color = "black", size = 4) +
theme_minimal() +
labs(x = "TSNE1", y = "TSNE 2", title = "Slingshot Pseudotime [PT Cells] with Cluster Labels")
#TAL_DCT cell slingshot
sce_tub <- as.SingleCellExperiment(TAL_DCT_cells)
sce_tub <- slingshot::slingshot(sce_tub, clusterLabels = sce_tub$RNA_snn_res.0.4, reducedDim = 'TSNE')
# Extract data from SingleCellExperiment
tub_df <- as.data.frame(reducedDim(sce_tub, "TSNE"))
colnames(tub_df) <- c("TSNE_1", "TSNE_2") # Rename to readable names
tub_df$cluster <- sce_tub$l1_annotations_res0.1
tub_df$pseudotime <- sce_tub$slingPseudotime_1
# Calculate cluster centroids for labeling
label_tub_df <- tub_df %>%
group_by(cluster) %>%
summarize(TSNE_1 = mean(TSNE_1), TSNE_2 = mean(TSNE_2))
# Plot with ggplot2
ggplot(tub_df, aes(x = TSNE_1, y = TSNE_2, color = pseudotime)) +
geom_point(size = 0.5) +
scale_color_viridis_c() +
geom_text(data = label_tub_df, aes(x = TSNE_1, y = TSNE_2, label = cluster),
color = "black", size = 4) +
theme_minimal() +
labs(x = "TSNE1", y = "TSNE 2", title = "Slingshot Pseudotime [PT Cells] with Cluster Labels")
Idents(obj_annot) <- 'RNA_snn_res.0.1'
DimPlot(obj_annot, reduction = "tsne", label = T,repel=T, label.size = 5) + ggtitle("0.1 resolution clustering")
obj_annot <- readRDS("C:/Users/evrajadh/OneDrive - Indiana University/Research/Recurrent UTI GFR/ScRNA/Rec_UTI/res 0.5_PCA 25_7-29/annotation object.RDS")
Idents(obj_annot) <- 'l1_annotations_res0.1'
DimPlot(obj_annot, reduction = "tsne", label = T,repel=T, label.size = 5)
Idents(obj_annot) <- 'grouping'
DimPlot(obj_annot, reduction = "tsne", label = T, label.size = 4, pt.size = 0.1) + NoLegend() + ggtitle('Clusters')
Idents(obj_annot) <- 'grouping'
DoHeatmap(obj_annot, features = c("Nrep", "Map3k7cl", "Angptl7", "Hdc", "Slc6a18", "Tmigd1", "Slc22a12", "Slc22a6"), cells = "PT")
VlnPlot(obj_annot, features = c("Nrep", "Map3k7cl", "Angptl7", "Hdc", "Slc6a18", "Tmigd1", "Slc22a12", "Slc22a6"), raster = F, split.by = 'condition')
FeaturePlot(obj_annot, features = "Nrep", reduction = "tsne", split.by = 'condition', pt.size = 0.8)
FeaturePlot(obj_annot, features = "Map3k7cl", reduction = "tsne", split.by = 'condition', pt.size = 0.8)
VlnPlot(obj_annot, features = "Nrep", raster = F, split.by = 'condition')
VlnPlot(obj_annot, features = "Map3k7cl", raster = F, split.by = 'condition')
VlnPlot(obj_annot, features = "B2m", raster = F, split.by = 'condition')
feat_scCKDatlas <- c("Cd79a", "Cd3d", "Prf1", "Cpa3", "Folr2", "Sema3g", "Sost", "Prox1", "Vcam1", "Plvap", "Rergl", "Cox4l2", "Dcn", "Postn", "Slc22a6", "Prune2", "Nos1", "Umod", "Calb1", "Slc4a1", "Upk3b", "Wt1", "Gfr3a")
DoHeatmap(obj_annot, features = c("Cryab", "Sema5a", "Pdgfrb", "Dcdc2a", "Cp", "Nkg7", "Xcr1", "S100a9", "Lrp2", "Slc12a1", "Slc12a3"))
FeaturePlot(obj_annot, features = c("Cryab", "Sema5a", "Pdgfrb", "Dcdc2a", "Cp", "Nkg7", "Xcr1", "S100a9", "Lrp2", "Slc12a1", "Slc12a3"), reduction = "tsne", split.by = 'condition', pt.size = 0.8)
VlnPlot(obj_annot, features = c("Cryab", "Sema5a", "Pdgfrb", "Dcdc2a", "Cp", "Nkg7", "Xcr1", "S100a9", "Lrp2", "Slc12a1", "Slc12a3"), raster = F, split.by = 'condition')
feat_PTinjury <- c("Slc5a12", "Slc34a1", "Slc22a30", "Col27a1", "Plin2", "Ypel2", "Bcat1", "Slc7a12", "Hsp90aa1", "Top2a", "Kcnip4", "Vcam1", "Havcr1")
#Healthy: Slc5a12, 45a1, 22a30, 7a13
#type 1 injured S1/2: Col27a1, Plin2
#Type 1 injured S3: Plin2, Havcr1
#type2 injured S1/2: Ypel2
#Type2 injured S3: ypel2, Bcat1, Slc7a12
#acute injury: Hsp90aa1
#Repairing: Top2a
#Failed repair: Ypel2, Kcnip4,Vcam1, Havcr1
FeaturePlot(obj_annot, features = feat_PTinjury, reduction = "tsne", split.by = 'condition', pt.size = 0.8)
VlnPlot(obj_annot, features = feat_PTinjury, raster = F, split.by = 'condition')
#see which PT clusters are where on tsne (clusters 5,9, and 11 on res0.5)
Idents(obj_annot) <- 'RNA_snn_res.0.5'
DimPlot(obj_annot, reduction = "tsne", label = T)
sce <- as.SingleCellExperiment(pt_cells)
sce <- slingshot(sce, clusterLabels = sce$l1_con, reducedDim = "TSNE")
curves <- slingCurves(sce)
df <- as.data.frame(reducedDim(sce, "TSNE"))
colnames(df) <- c("TSNE_1", "TSNE_2")
df$pseudotime <- sce$slingPseudotime_1
df$cluster <- sce$l1_annotations_res0.1
p <- ggplot(df, aes(x = TSNE_1, y = TSNE_2, color = pseudotime)) +
geom_point(size = 0.5) +
scale_color_viridis_c() +
theme_minimal()
for (i in seq_along(curves)) {
curve_df <- as.data.frame(curves[[i]]$s)
colnames(curve_df) <- c("TSNE_1", "TSNE_2")
# Add arrows along the curve
p <- p + geom_path(data = curve_df, aes(x = TSNE_1, y = TSNE_2),
arrow = arrow(type = "open", length = unit(0.15, "inches")),
color = "black", size = 0.8)
}
p
#PT cells also with condition separation
sce <- as.SingleCellExperiment(pt_cells)
sce <- slingshot(sce, clusterLabels = sce$l1_condition, reducedDim = "TSNE")
curves <- slingCurves(sce)
df <- as.data.frame(reducedDim(sce, "TSNE"))
colnames(df) <- c("TSNE_1", "TSNE_2")
df$pseudotime <- sce$slingPseudotime_1
df$cluster <- sce$l1_condition
p <- ggplot(df, aes(x = TSNE_1, y = TSNE_2, color = pseudotime)) +
geom_point(size = 0.5) +
scale_color_viridis_c() +
theme_minimal()
for (i in seq_along(curves)) {
curve_df <- as.data.frame(curves[[i]]$s)
colnames(curve_df) <- c("TSNE_1", "TSNE_2")
# Add arrows along the curve
p <- p + geom_path(data = curve_df, aes(x = TSNE_1, y = TSNE_2),
arrow = arrow(type = "open", length = unit(0.15, "inches")),
color = "black", size = 0.8)
}
p
saveRDS(obj_annot, "annotation object.RDS")
unique(merged_all_cons@meta.data$l1_annotations_res0.1)
Idents(merged_all_cons) <- 'l1_annotations_res0.1'
DimPlot(merged_all_cons, reduction = "umap")
markers_test <- FindMarkers(merged_all_cons, grouping.var = "l1_annotations_res0.1", ident.1 = "Podocyte")
markers_test
DimPlot(merged_all_cons, label = T, repel = T)
+++++++++++
myeloid_df <- as.data.frame(reducedDim(sce_myeloid, "TSNE"))
colnames(myeloid_df) <- c("TSNE_1", "TSNE_2")
myeloid_df$lineage <- is.na(sce_myeloid$slingPseudotime_1)
ggplot(myeloid_df, aes(x = TSNE_1, y = TSNE_2, color = lineage)) +
geom_point(size = 0.5) +
scale_color_manual(values = c("FALSE" = "blue", "TRUE" = "gray")) +
theme_minimal() +
labs(title = "Cells Assigned to Slingshot Lineage 1",
color = "Assigned?")
sce_all <- as.SingleCellExperiment(obj_annot)
sce_all <- slingshot::slingshot(sce_all, clusterLabels = sce_all$RNA_snn_res.0.4, reducedDim = 'TSNE')
#For just PT cells and PT_EMT
# Extract data from SingleCellExperiment
tsne_all_df <- as.data.frame(reducedDim(sce_all, "TSNE"))
colnames(tsne_all_df) <- c("TSNE_1", "TSNE_2") # Rename to readable names
tsne_all_df$cluster <- sce_all$l1_annotations_res0.1
tsne_all_df$pseudotime <- sce_all$slingPseudotime_1
# Calculate cluster centroids for labeling
label_all_tsne_df <- tsne_all_df %>%
group_by(cluster) %>%
summarize(TSNE_1 = mean(TSNE_1), TSNE_2 = mean(TSNE_2))
# Plot with ggplot2
ggplot(tsne_all_df, aes(x = TSNE_1, y = TSNE_2, color = pseudotime)) +
geom_point(size = 0.5) +
scale_color_viridis_c() +
geom_text(data = label_all_tsne_df, aes(x = TSNE_1, y = TSNE_2, label = cluster),
color = "black", size = 4) +
theme_minimal() +
labs(x = "TSNE1", y = "TSNE 2", title = "Slingshot Pseudotime [All Cells] with Cluster Labels")
tsne_all_df <- as.data.frame(reducedDim(sce_all, "TSNE"))
colnames(tsne_all_df) <- c("TSNE_1", "TSNE_2")
tsne_all_df$lineage <- is.na(sce_all$slingPseudotime_1)
ggplot(tsne_all_df, aes(x = TSNE_1, y = TSNE_2, color = lineage)) +
geom_point(size = 0.5) +
scale_color_manual(values = c("FALSE" = "blue", "TRUE" = "gray")) +
theme_minimal() +
labs(title = "Cells Assigned to Slingshot Lineage 1",
color = "Assigned?")
sce_myeloid$lineage <- is.na(sce_myeloid$slingPseudotime_1)
ggplot(..., color = lineage) # TRUE = unassigned = gray
df <- as.data.frame(reducedDim(sce_myeloid, "TSNE"))
colnames(df) <- c("TSNE_1", "TSNE_2")
df$lineage <- is.na(sce_myeloid$slingPseudotime_1)
plot(reducedDim(sce), col = sce$cluster, type = 'lineages')
sce$li
plot_cells(cds, color_cells_by = "cluster")
plot_cells(cds, genes = c("Cdh2"), show_trajectory_graph = TRUE)
+++++++++++
ggsave("resolution cluster tree.svg", plot = cluster.tree, width = 12)
cluster.tree
UTI_0.5_markers_clust_annot <- SeuratWrappers::RunPrestoAll(merged_0.5_splitlist$UTI)
UTI_0.5_markers_list <- split(UTI_0.5_markers_clust_annot, UTI_0.5_markers_clust_annot[['cluster']])
for(i in names(UTI_0.5_markers_list)){
aux <- UTI_0.5_markers_list[[i]]
aux <- aux[aux$p_val_adj < 0.05,]
aux <- aux[order(aux$avg_log2FC, decreasing=T),]
UTI_all_markers_list[[i]] <- aux
}
#DE genes by condition at res 0.01
merged_0.05$l0_condition <- paste0(merged_0.05$RNA_snn_res.0.01, '_', merged_0.05$condition)
Idents(merged_0.05) <- 'l0_condition'
DE_0.05_resol_condition <- SeuratWrappers::RunPresto(merged_0.05)
Add a new chunk by clicking the Insert Chunk button on the
toolbar or by pressing Ctrl+Alt+I.
When you save the notebook, an HTML file containing the code and
output will be saved alongside it (click the Preview button or
press Ctrl+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the
editor. Consequently, unlike Knit, Preview does not
run any R code chunks. Instead, the output of the chunk when it was last
run in the editor is displayed.
LS0tDQp0aXRsZTogIkF1Z3VzdCAyMDI1IGFuYWx5c2lzIGZvciBUYWthc2hpIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQojV29ya3NwYWNlIHNldHVwDQpgYGB7cn0NCiNsb2FkIG5lY2Vzc2FyeSBwYWNrYWdlcw0KbGlicmFyeShjZWxscmFuZ2VyKQ0KbGlicmFyeShoZGY1cikNCmxpYnJhcnkoU2V1cmF0KQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocGF0Y2h3b3JrKQ0KbGlicmFyeShnbG1HYW1Qb2kpDQpsaWJyYXJ5KHNjdHJhbnNmb3JtKQ0KbGlicmFyeShsb3VwZVIpDQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQojdXBkYXRlLnBhY2thZ2VzKGxpc3QgPSBjKGNlbGxyYW5nZXIsIGhkZjVyLCBTZXVyYXQsIGRwbHlyLCBwYXRjaHdvcmssIGdsbUdhbVBvaSwgc2N0cmFuc2Zvcm0sIGxvdXBlUiwgdGlkeXZlcnNlKSkNCg0Kc2V0d2QoIkM6L1VzZXJzL2V2cmFqYWRoL09uZURyaXZlIC0gSW5kaWFuYSBVbml2ZXJzaXR5L1Jlc2VhcmNoL1JlY3VycmVudCBVVEkgR0ZSL1NjUk5BL1JlY19VVEkiKQ0Kcm0obGlzdCA9IGxzKCkpDQpgYGANCg0KI0ltcG9ydCBkYXRhDQojI0xvYWQgZmlsdGVyZWQgb2JqZWN0cw0KYGBge3J9DQpVMSA8LSBSZWFkMTBYX2g1KCJGaWx0ZXJlZCBoNSBvYmplY3RzL1VUSS0xX2ZpbHRlcmVkX2ZlYXR1cmVfYmNfbWF0cml4Lmg1IikNClUyIDwtIFJlYWQxMFhfaDUoIkZpbHRlcmVkIGg1IG9iamVjdHMvVVRJLTJfZmlsdGVyZWRfZmVhdHVyZV9iY19tYXRyaXguaDUiKQ0KVTMgPC0gUmVhZDEwWF9oNSgiRmlsdGVyZWQgaDUgb2JqZWN0cy9VVEktM19maWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeC5oNSIpDQpDMSA8LSBSZWFkMTBYX2g1KCJGaWx0ZXJlZCBoNSBvYmplY3RzL0NvbnRyb2wtMV9maWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeC5oNSIpDQpDMiA8LSBSZWFkMTBYX2g1KCJGaWx0ZXJlZCBoNSBvYmplY3RzL0NvbnRyb2wtMl9maWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeC5oNSIpDQpDMyA8LSBSZWFkMTBYX2g1KCJGaWx0ZXJlZCBoNSBvYmplY3RzL0NvbnRyb2wtM19maWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeC5oNSIpDQpgYGANCg0KIyNDcmVhdGUgc2V1cmF0IG9iamVjdHMgDQpgYGB7cn0NCiNUdXJuIGludG8gc2V1cmF0IG9iamVjdHMNClUxIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBVMSkNClUyIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBVMikNClUzIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBVMykNCkMxIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBDMSkNCkMyIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBDMikNCkMzIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBDMykNCmBgYA0KDQojI01lcmdlLCBsYWJlbGluZyBjZWxsLmlkIGluIG1ldGFkYXRhDQpgYGB7cn0NCiNtZXJnZSBpbnRvIHNpbmdsZSBzZXVyYXQgb2JqZWN0LCBsYXllcnMgc3RpbGwgc2VwYXJhdGVkDQptZXJnZWRfYWxsIDwtIG1lcmdlKFUxLCB5ID0gYyhVMiwgVTMsIEMxLCBDMiwgQzMpLA0KICAgICAgICAgICAgICAgIGFkZC5jZWxsLmlkcyA9IGMoIlUxX1VUSSIsICJVMl9VVEkiLCAiVTNfVVRJIiwgIkMxX0NvbnRyb2wiLCAiQzJfQ29udHJvbCIsICJDM19Db250cm9sIiksDQogICAgICAgICAgICAgICAgcHJvamVjdCA9ICJSZWNVVEkiKQ0KYGBgDQoNCmBgYHtyfQ0KI0FkanVzdCBtZXRhZGF0YQ0KbWVyZ2VkX2FsbCRzYW1wbGUgPC0gcm93bmFtZXMobWVyZ2VkX2FsbEBtZXRhLmRhdGEpDQptZXJnZWRfYWxsQG1ldGEuZGF0YSA8LSBzZXBhcmF0ZShtZXJnZWRfYWxsQG1ldGEuZGF0YSwgY29sID0gJ3NhbXBsZScsIGludG8gPSBjKCdzYW1wbGUnLCAnY29uZGl0aW9uJywgJ2JhcmNvZGUnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gJ18nKQ0KDQojbGFiZWwgcGVyY2VudCBtaXRvY2hvbmRyaWFsIEROQQ0KbWVyZ2VkX2FsbCRwZXJjZW50Lm10IDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KG1lcmdlZF9hbGwsIHBhdHRlcm4gPSAiXm10LSIpDQoNCiNhZGQgbnVtYmVyIG9mIGdlbmVzIHBlciBVTUkgZm9yIGVhY2ggY2VsbCB0byBtZXRhZGF0YQ0KI2xvZzEwZ2VuZXNQZXJVTUkgZXhwZWN0ZWQgaXMgfjAuOC0wLjkgZm9yIHNjUk5BIGRhdGEsID4xLjAgaXMgc3VzcGVjdGVkIGRvdWJsZXQNCm1lcmdlZF9hbGwkbG9nMTBHZW5lc1BlclVNSSA8LSBsb2cxMChtZXJnZWRfYWxsJG5GZWF0dXJlX1JOQSkgLyBsb2cxMChtZXJnZWRfYWxsJG5Db3VudF9STkEpDQptZXJnZWRfYWxsJEdlbmVzUGVyVU1JIDwtIChtZXJnZWRfYWxsJG5GZWF0dXJlX1JOQSAvIG1lcmdlZF9hbGwkbkNvdW50X1JOQSkNCmBgYA0KDQojUUMNCiMjbG9nMTBnZW5lc3BlclVNSQ0KYGBge3J9DQojVmlzdWFsaXplIGxvZzEwZ2VuZXNQZXJVTUkgUFJJT1IgdG8gc3Vic2V0dGluZw0KVmxuUGxvdChtZXJnZWRfYWxsLCBmZWF0dXJlcyA9ICJsb2cxMEdlbmVzUGVyVU1JIikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjk1LCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArIA0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjgsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpDQpgYGANCg0KYGBge3J9DQojcGxvdCBsb2cxMFVNSSB2cyBuQ291bnRfUk5BDQptZXJnZWRfYWxsQG1ldGEuZGF0YSAlPiUgDQogIAlnZ3Bsb3QoYWVzKGNvbG9yPWNvbmRpdGlvbiwgeD1uQ291bnRfUk5BLCBmaWxsPSBzYW1wbGUpKSArIA0KICAJZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4yKSArIA0KICAJc2NhbGVfeF9sb2cxMCgpICsgDQogIAl0aGVtZV9jbGFzc2ljKCkgKw0KICAJeWxhYigibG9nMTAgY2VsbCBkZW5zaXR5IikgKw0KICAJZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNTAwKQ0KYGBgDQojI3FjIGZlYXR1cmUgcGxvdHMgKGJlZm9yZSBzdWJzZXR0aW5nKQ0KYGBge3IsIGZpZy53aWR0aD0xMH0NCiNwbG90IG5Db3VudCB2cyBuRmVhdHVyZQ0KcWNmZWF0MSA8LSBGZWF0dXJlU2NhdHRlcihtZXJnZWRfYWxsLCBmZWF0dXJlMSA9ICduQ291bnRfUk5BJywgZmVhdHVyZTIgPSAnbkZlYXR1cmVfUk5BJywgZ3JvdXAuYnkgPSAnc2FtcGxlJywgcHQuc2l6ZSA9IDAuMDUpIA0KcWNmZWF0MiA8LSBGZWF0dXJlU2NhdHRlcihtZXJnZWRfYWxsLCBmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgZmVhdHVyZTIgPSAicGVyY2VudC5tdCIsIGdyb3VwLmJ5ID0gJ3NhbXBsZScsIHB0LnNpemUgPSAwLjA1KQ0KcWNmZWF0MyA8LSBGZWF0dXJlU2NhdHRlcihtZXJnZWRfYWxsLCBmZWF0dXJlMSA9ICJuRmVhdHVyZV9STkEiLCBmZWF0dXJlMiA9ICJwZXJjZW50Lm10IiwgZ3JvdXAuYnkgPSAnc2FtcGxlJywgcHQuc2l6ZSA9IDAuMDUpDQoNCnFjZmVhdDEgKyBxY2ZlYXQyICsgcWNmZWF0Mw0KYGBgDQojI3FjIHZsbiBwbG90cyAocHJlLXN1YnNldHRpbmcpDQpgYGB7ciwgZmlnLndpZHRoPTEyfQ0KcWNfdmxuIDwtIFZsblBsb3QobWVyZ2VkX2FsbCwgZmVhdHVyZXMgPSBjKCduQ291bnRfUk5BJywgJ25GZWF0dXJlX1JOQScsICdwZXJjZW50Lm10JyksIGFscGhhID0gMC4xLCBncm91cC5ieSA9ICdzYW1wbGUnKQ0KcWNfdmxuW1syXV0gPC0gcWNfdmxuW1syXV0gKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA1MDApICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNTAwMCkNCnFjX3ZsbltbM11dIDwtIHFjX3ZsbltbM11dICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMTApIA0KcWNfdmxuDQpgYGANCiMjU3Vic2V0IGRhdGEgLSBwZXJjZW50Lm10PDEwLCBuRmVhdHVyZXMgNTAwLTUwMDAsIGxvZzEwZ2VuZXNwZXJVTUk+MC44DQpgYGB7cn0NCm1lcmdlZF9hbGwgPC0gc3Vic2V0KG1lcmdlZF9hbGwsIHN1YnNldCA9IHBlcmNlbnQubXQ8MTAgJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuRmVhdHVyZV9STkE+NTAwICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbkZlYXR1cmVfUk5BPDUwMDAgJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cxMEdlbmVzUGVyVU1JID49MC44KQ0KDQpgYGANCg0KIyNyZXBlYXQgUUMgdmxuIHBsb3RzDQpgYGB7cn0NCnFjX3ZsbiA8LSBWbG5QbG90KG1lcmdlZF9hbGwsIGZlYXR1cmVzID0gYygnbkNvdW50X1JOQScsICduRmVhdHVyZV9STkEnLCAncGVyY2VudC5tdCcpLCBhbHBoYSA9IDAuMSwgZ3JvdXAuYnkgPSAnc2FtcGxlJykNCnFjX3ZsbltbMl1dIDwtIHFjX3ZsbltbMl1dICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNTAwKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUwMDApDQpxY192bG5bWzNdXSA8LSBxY192bG5bWzNdXSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEwKSANCnFjX3Zsbg0KYGBgDQpgYGB7cn0NClZsblBsb3QobWVyZ2VkX2FsbCwgZmVhdHVyZXMgPSAibG9nMTBHZW5lc1BlclVNSSIpIA0KYGBgDQojI2NlbGwgZGVuc2l0eSBieSBuRmVhdHVyZV9STkEgKHBvc3Qtc3Vic2V0dGluZykNCmBgYHtyfQ0KI3Bsb3QgbG9nMTBVTUkgdnMgbkZlYXR1cmVfUk5BDQptZXJnZWRfYWxsQG1ldGEuZGF0YSAlPiUgDQogIAlnZ3Bsb3QoYWVzKGNvbG9yPWNvbmRpdGlvbiwgeD1uRmVhdHVyZV9STkEsIGZpbGw9IHNhbXBsZSkpICsgDQogIAlnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjIpICsgDQogIAlzY2FsZV94X2xvZzEwKCkgKyANCiAgCXRoZW1lX2NsYXNzaWMoKSArDQogIAl5bGFiKCJsb2cxMCBjZWxsIGRlbnNpdHkiKSArDQogIAlnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA1MDApDQpgYGANCiMjY2VsbCBkZW5zaXR5IGJ5IHBlcmNlbnQubXQNCmBgYHtyfQ0KI3Bsb3QgbG9nMTBVTUkgdnMgcGVyY2VudC5tdA0KbWVyZ2VkX2FsbEBtZXRhLmRhdGEgJT4lIA0KICAJZ2dwbG90KGFlcyhjb2xvcj1jb25kaXRpb24sIHg9cGVyY2VudC5tdCwgZmlsbD0gc2FtcGxlKSkgKyANCiAgCWdlb21fZGVuc2l0eShhbHBoYSA9IDAuMikgKyANCiAgCXRoZW1lX2NsYXNzaWMoKSArDQogIAl5bGFiKCJsb2cxMCBjZWxsIGRlbnNpdHkiKSANCmBgYA0KIyNGaWx0ZXIgb3V0IGxvdyBxdWFsaXR5IGNlbGxzIChmZWF0dXJlcyBleHByZXNzZWQgaW4gPDEwIGNlbGxzKQ0KYGBge3J9DQojc3Vic2V0IG91dCBmZWF0dXJlcyBleHByZXNzZWQgaW4gPDEwIGNlbGxzIChTdXpzdGFrIHVzZWQgMTApDQpjb3VudHMgPC0gTGF5ZXJEYXRhKG1lcmdlZF9hbGwsIGFzc2F5ID0gIlJOQSIsIGxheWVyID0gImNvdW50cyIpDQprZWVwX2dlbmVzIDwtIHJvd1N1bXMoY291bnRzPjApID49MTANCm1lcmdlZF9hbGwgPC0gc3Vic2V0KG1lcmdlZF9hbGwsIGZlYXR1cmVzID0gbmFtZXMoa2VlcF9nZW5lc1trZWVwX2dlbmVzXSkpDQoNCm1lcmdlZF9hbGwgI2ZlYXR1cmVzIDMzNjk2IC0+IDE3NTM1IGdlbmVzIHdpdGggc3VmZmljaWVudCBleHByZXNzaW9uIGRhdGENCmBgYA0KDQojUHJvY2Vzc2luZw0KIyNKb2luIGxheWVycyBhbmQgcHJlcHJvY2Vzc2luZyB3b3JrZmxvdw0KYGBge3J9DQojSm9pbiBsYXllcnMNCiNQZXJmb3JtIHN0YW5kYXJkIFBSRXByb2Nlc3Npbmcgd29ya2Zsb3cgc3RlcHMgdG8gZmlndXJlIG91dCBpZiB3ZSBoYXZlIHNpZ25pZmljYW50IGJhdGNoIGVmZmVjdHMNCg0KbWVyZ2VkX2FsbCA8LSBKb2luTGF5ZXJzKG1lcmdlZF9hbGwpDQptZXJnZWRfYWxsIDwtIE5vcm1hbGl6ZURhdGEobWVyZ2VkX2FsbCkNCg0KbWVyZ2VkX2FsbCA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhtZXJnZWRfYWxsLCBuZmVhdHVyZXMgPSAzMDAwKQ0KbWVyZ2VkX2FsbCA8LSBTY2FsZURhdGEobWVyZ2VkX2FsbCkNCg0KdG9wX3Zhcl9nZW5lc18zMDAwIDwtIGhlYWQoVmFyaWFibGVGZWF0dXJlcyhtZXJnZWRfYWxsKSwgMjApDQpwcmludCh0b3BfdmFyX2dlbmVzXzMwMDApDQpgYGANCg0KIyNWaXN1YWxpemUgbW9zdCB2YXJpYWJsZSBmZWF0dXJlcw0KYGBge3J9DQpWYXJpYWJsZUZlYXR1cmVQbG90KG1lcmdlZF9hbGwpDQpMYWJlbFBvaW50cyhwb2ludHMgPSB0b3BfdmFyX2dlbmVzXzMwMDAsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgcmVwZWwgPSBUUlVFKQ0KDQpgYGANCg0KIyNkZWNpZGUgbnVtYmVyIG9mIFBDQXMgdG8gdXNlIGZvciBhbmFseXNpczogbnBjcz0yMA0KYGBge3J9DQptZXJnZWRfYWxsIDwtIFJ1blBDQShtZXJnZWRfYWxsLCBucGNzID0gNTApDQpFbGJvd1Bsb3RfNTBQQ0FzIDwtIEVsYm93UGxvdChtZXJnZWRfYWxsLCBuZGltcyA9IDUwKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobi5icmVha3MgPSAyMCkgKyANCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAyMCkNCkVsYm93UGxvdF81MFBDQXMgDQpgYGANCg0KIyNTdGFuZGFyZCBwcm9jZXNzaW5nIGFuZCBjbHVzdGVyaW5nLCAwLjYgcmVzb2x1dGlvbg0KYGBge3J9DQojU3RhbmRhcmQgcHJvY2Vzc2luZyBhbmQgY2x1c3RlcmluZw0KbWVyZ2VkX2FsbCA8LSBGaW5kTmVpZ2hib3JzKG1lcmdlZF9hbGwsIGRpbXMgPSAxOjIwLCB2ZXJib3NlID0gRikNCm1lcmdlZF9hbGwgPC0gRmluZENsdXN0ZXJzKG1lcmdlZF9hbGwsIHJlc29sdXRpb24gPSAwLjYpDQptZXJnZWRfYWxsIDwtIFJ1blVNQVAobWVyZ2VkX2FsbCwgZGltcyA9IDE6MjApDQpgYGANCg0KIyNDYWxjdWxhdGUgbnVtYmVyIG9mIGNlbGxzIGluIGVhY2ggY2x1c3Rlcg0KYGBge3J9DQoNCiBjZWxsc19wZXJfY2x1c3RlciA8LSBtZXJnZWRfYWxsQG1ldGEuZGF0YSAlPiUNCiAgZ3JvdXBfYnkoc2V1cmF0X2NsdXN0ZXJzKSAlPiUNCiAgc3VtbWFyaXNlKGNlbGxfY291bnQgPSBuKCkpICU+JQ0KICBhcnJhbmdlKHNldXJhdF9jbHVzdGVycykNCg0KIGNlbGxzX3Blcl9jbHVzdGVyX2NvbmQgPC0gbWVyZ2VkX2FsbEBtZXRhLmRhdGEgJT4lDQogIGdyb3VwX2J5KHNldXJhdF9jbHVzdGVycywgY29uZGl0aW9uKSAlPiUNCiAgc3VtbWFyaXNlKGNlbGxfY291bnQgPSBuKCkpICU+JQ0KICBhcnJhbmdlKHNldXJhdF9jbHVzdGVycywgY29uZGl0aW9uKQ0KDQpWaWV3KGNlbGxzX3Blcl9jbHVzdGVyKQ0KVmlldyhjZWxsc19wZXJfY2x1c3Rlcl9jb25kKQ0KDQpwcmludChjZWxsc19wZXJfY2x1c3RlcikNCnByaW50KGNlbGxzX3Blcl9jbHVzdGVyX2NvbmQpDQpgYGANCg0KI1VNQVAgdmlzdWFsaXphdGlvbiBwcmUtZG91YmxldCByZW1vdmFsDQpgYGB7cn0NCklkZW50cyhtZXJnZWRfYWxsKSA8LSAic2V1cmF0X2NsdXN0ZXJzIg0KRGltUGxvdChtZXJnZWRfYWxsLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVCwgcmVwZWwgPSBUKSArIGdndGl0bGUoIlNldXJhdCBDbHVzdGVycyIpDQpgYGANCg0KI1JlbW92ZSBkb3VibGV0cw0KYGBge3J9DQojaW5zdGFsbCBhbmQgbG9hZCBzY0RibEZpbmRlciBwYWNrYWdlDQoNCiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgic2NEYmxGaW5kZXIiKQ0KbGlicmFyeShzY0RibEZpbmRlcikNCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpDQpgYGANCiMjY29udmVydCB0byBzaW5nbGUgY2VsbCBleHBlcmltZW50IGFuZCBydW4gc2NEYmxGaW5kZXINCmBgYHtyfQ0KI2NvbnZlcnQgdG8gc2luZ2xlIGNlbGwgZXhwZXJpbWVudCBhbmQgcnVuIHNjRGJsRmluZGVyDQpzY2UgPC0gYXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQobWVyZ2VkX2FsbCkNCnNjZSA8LSBzY0RibEZpbmRlcjo6c2NEYmxGaW5kZXIoc2NlLCBzYW1wbGVzID0gJ3NhbXBsZScpDQoNCmBgYA0KIyNhZGQgZG91YmxldCByZXN1bHRzIGJhY2sgaW50byBtZXRhZGF0YSBhbmQgdmlzdWFsaXplIG9uIFVNQVANCmBgYHtyfQ0KI2FkZCByZXN1bHRzIGJhY2sgaW50byBzZXVyYXQgbWV0YWRhdGENCm1lcmdlZF9hbGwkc2NEYmxGaW5kZXIuY2xhc3MgPC0gY29sRGF0YShzY2UpJHNjRGJsRmluZGVyLmNsYXNzDQoNCiN2aXN1YWxpemUNCnNpbmdfdnNfZG91Yl9iZWZvcmVfc3Vic2V0IDwtIERpbVBsb3QobWVyZ2VkX2FsbCxzcGxpdC5ieSA9ICdjb25kaXRpb24nLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInNjRGJsRmluZGVyLmNsYXNzIiwgcHQuc2l6ZSA9IDAuNSkgKyBnZ3RpdGxlKCJzY0RibEZpbmRlcjogU2luZ2xldHMgdnMgRG91YmxldHMiKQ0Kc2luZ192c19kb3ViX2JlZm9yZV9zdWJzZXQNCmBgYA0KDQojI3N1YnNldCB0byBvbmx5IHNpbmdsZXRzDQpgYGB7cn0NCm1lcmdlZF9hbGwgPC0gc3Vic2V0KG1lcmdlZF9hbGwsIHN1YnNldCA9IHNjRGJsRmluZGVyLmNsYXNzID09ICJzaW5nbGV0IikNCmBgYA0KDQojI3JlbmFtZSBtZXJnZWRfYWxsMiBhbmQgZmluZG5laWdoYm9ycyBhbmQgY2x1c3RlcnMgYWdhaW4/DQpgYGB7cn0NCm1lcmdlZF9hbGwyIDwtIG1lcmdlZF9hbGwNCg0KbWVyZ2VkX2FsbDIgPC0gRmluZE5laWdoYm9ycyhtZXJnZWRfYWxsMiwgZGltcyA9IDE6MjAsIHZlcmJvc2UgPSBGKQ0KbWVyZ2VkX2FsbDIgPC0gRmluZENsdXN0ZXJzKG1lcmdlZF9hbGwyLCByZXNvbHV0aW9uID0gMC42KQ0KDQptZXJnZWRfYWxsMiA8LSBSdW5VTUFQKG1lcmdlZF9hbGwyLCBkaW1zID0gMToyMCkNCmBgYA0KYGBge3J9DQpBIDwtIERpbVBsb3QobWVyZ2VkX2FsbCwgbGFiZWwgPSBULCByZXBlbCA9IFQpICsgTm9MZWdlbmQoKSArIGdndGl0bGUoIk9sZCBDbHVzdGVycyBBZnRlciBcbkRvdWJsZXQgUmVtb3ZhbCIpDQpCIDwtIERpbVBsb3QobWVyZ2VkX2FsbDIsIGxhYmVsID0gVCwgcmVwZWwgPSBUKSArIE5vTGVnZW5kKCkgKyBnZ3RpdGxlKCJSZWNsdXN0ZXJpbmcgQWZ0ZXIgXG5Eb3VibGV0IFJlbW92YWwiKQ0KQStCDQpgYGANCiMjY2FsY3VsYXRlIGNlbGxzIGluIGVhY2ggY2x1c3RlciBhZnRlciBkb3VibGV0IHJlbW92YWwgYW5kIHJlY2x1c3RlcmluZw0KYGBge3J9DQoNCmNlbGxzX3Blcl9jbHVzdGVyMiA8LSBtZXJnZWRfYWxsMkBtZXRhLmRhdGEgJT4lDQogIGdyb3VwX2J5KHNldXJhdF9jbHVzdGVycykgJT4lDQogIHN1bW1hcmlzZShjZWxsX2NvdW50ID0gbigpKSAlPiUNCiAgYXJyYW5nZShzZXVyYXRfY2x1c3RlcnMpDQoNCmNlbGxzX3Blcl9jbHVzdGVyX2NvbmQyIDwtIG1lcmdlZF9hbGwyQG1ldGEuZGF0YSAlPiUNCiAgZ3JvdXBfYnkoc2V1cmF0X2NsdXN0ZXJzLCBjb25kaXRpb24pICU+JQ0KICBzdW1tYXJpc2UoY2VsbF9jb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2Uoc2V1cmF0X2NsdXN0ZXJzLCBjb25kaXRpb24pDQoNClZpZXcoY2VsbHNfcGVyX2NsdXN0ZXIyKQ0KVmlldyhjZWxsc19wZXJfY2x1c3Rlcl9jb25kMikNCg0KcHJpbnQoY2VsbHNfcGVyX2NsdXN0ZXIyKQ0KcHJpbnQoY2VsbHNfcGVyX2NsdXN0ZXJfY29uZDIpDQpgYGANCg0KI1Zpc3VhbGl6YXRpb24NCiMjQnkgY29uZGl0aW9uDQpgYGB7cn0NCiNHcm91cCBieSBjb25kaXRpb24NCkRpbVBsb3QobWVyZ2VkX2FsbDIsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiY29uZGl0aW9uIiwgcHQuc2l6ZSA9IDAuMSwgYWxwaGEgPSAwLjMpIA0KYGBgDQojI2J5IHNhbXBsZSwgc3BsaXQgYnkgY29uZGl0aW9uDQpgYGB7ciwgZmlnLndpZHRoPTl9DQpEaW1QbG90KG1lcmdlZF9hbGwyLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ3NhbXBsZScsIHNwbGl0LmJ5ID0gImNvbmRpdGlvbiIsIGFscGhhID0gMC4zKQ0KYGBgDQojI2dyb3VwZWQgYnkgY2x1c3Rlciwgc3BsaXQgYnkgY29uZGl0aW9uDQpgYGB7ciwgZmlnLndpZHRoPTEwfQ0KRGltUGxvdChtZXJnZWRfYWxsMiwgcmVkdWN0aW9uID0gInVtYXAiLCBzcGxpdC5ieSA9ICJjb25kaXRpb24iLCBsYWJlbCA9IFQsIHJlcGVsID0gRiwgbGFiZWwuc2l6ZSA9IDUpICsgTm9MZWdlbmQoKQ0KDQpgYGANCiMjQmFyIGdyYXBoIG51bWJlciBvZiBjZWxscyBwZXIgY2x1c3RlciBmcm9tIGVhY2ggaW5kaXZpZHVhbCBzYW1wbGUNCmBgYHtyfQ0KI3Zpc3VhbGl6ZSBwZXJjZW50IG9mIGVhY2ggY2x1c3RlciBmcm9tIGluZGl2aWR1YWwgc2FtcGxlcw0KbWV0YV9kYXRhIDwtIG1lcmdlZF9hbGwyQG1ldGEuZGF0YQ0KDQojIENvdW50IGNlbGxzIHBlciBjbHVzdGVyIHBlciBzYW1wbGUNCmNsdXN0ZXJfY291bnRzIDwtIG1ldGFfZGF0YSAlPiUNCiAgZ3JvdXBfYnkoc2V1cmF0X2NsdXN0ZXJzLCBzYW1wbGUpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCksIC5ncm91cHMgPSAnZHJvcCcpDQoNCiMgQ2FsY3VsYXRlIHRvdGFsIGNlbGxzIHBlciBzYW1wbGUNCnRvdGFsX3Blcl9jbHVzdGVyIDwtIGNsdXN0ZXJfY291bnRzICU+JQ0KICBncm91cF9ieShzZXVyYXRfY2x1c3RlcnMpICU+JQ0KICBzdW1tYXJpc2UodG90YWwgPSBzdW0oY291bnQpLCAuZ3JvdXBzID0gJ2Ryb3AnKQ0KDQojIE1lcmdlIGFuZCBjYWxjdWxhdGUgcGVyY2VudGFnZQ0KY2x1c3Rlcl9wZXJjZW50YWdlcyA8LSBjbHVzdGVyX2NvdW50cyAlPiUNCiAgbGVmdF9qb2luKHRvdGFsX3Blcl9jbHVzdGVyLCBieSA9ICJzZXVyYXRfY2x1c3RlcnMiKSAlPiUNCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSAoY291bnQgLyB0b3RhbCkgKiAxMDApDQoNCg0KIyBQbG90DQpnZ3Bsb3QoY2x1c3Rlcl9wZXJjZW50YWdlcywgYWVzKHggPSBzZXVyYXRfY2x1c3RlcnMsIHkgPSBwZXJjZW50YWdlLCBmaWxsID0gc2FtcGxlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAic3RhY2siKSArDQogIGxhYnModGl0bGUgPSAiQ2x1c3RlciBDb21wb3NpdGlvbiBCeSBTYW1wbGUiLA0KICAgICAgIHggPSAiQ2x1c3RlciIsDQogICAgICAgeSA9ICJQZXJjZW50YWdlIG9mIENsdXN0ZXIiLA0KICAgICAgIGZpbGwgPSAiU2FtcGxlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KDQoNCmBgYA0KIyNwZXJjZW50IG9mIGVhY2ggY2x1c3RlciBmcm9tIGVhY2ggY29uZGl0aW9uDQpgYGB7cn0NCiN2aXN1YWxpemUgcGVyY2VudCBvZiBlYWNoIGNsdXN0ZXIgZnJvbSBlYWNoIGNvbmRpdGlvbg0KDQojIENvdW50IGNlbGxzIHBlciBjbHVzdGVyIHBlciBjb25kaXRpb24NCmNsdXN0ZXJfY291bnRzX2NvbmQgPC0gbWV0YV9kYXRhICU+JQ0KICBncm91cF9ieShzZXVyYXRfY2x1c3RlcnMsIGNvbmRpdGlvbikgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSwgLmdyb3VwcyA9ICdkcm9wJykNCg0KIyBDYWxjdWxhdGUgdG90YWwgY2VsbHMgcGVyIHNhbXBsZQ0KdG90YWxfY29uZF9wZXJfY2x1c3RlciA8LSBjbHVzdGVyX2NvdW50c19jb25kICU+JQ0KICBncm91cF9ieShzZXVyYXRfY2x1c3RlcnMpICU+JQ0KICBzdW1tYXJpc2UodG90YWwgPSBzdW0oY291bnQpLCAuZ3JvdXBzID0gJ2Ryb3AnKQ0KDQojIE1lcmdlIGFuZCBjYWxjdWxhdGUgcGVyY2VudGFnZQ0KY2x1c3Rlcl9jb25kX3BlcmNlbnRhZ2VzIDwtIGNsdXN0ZXJfY291bnRzX2NvbmQgJT4lDQogIGxlZnRfam9pbih0b3RhbF9jb25kX3Blcl9jbHVzdGVyLCBieSA9ICJzZXVyYXRfY2x1c3RlcnMiKSAlPiUNCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSAoY291bnQgLyB0b3RhbCkgKiAxMDApDQoNCg0KIyBQbG90DQpnZ3Bsb3QoY2x1c3Rlcl9jb25kX3BlcmNlbnRhZ2VzLCBhZXMoeCA9IHNldXJhdF9jbHVzdGVycywgeSA9IHBlcmNlbnRhZ2UsIGZpbGwgPSBjb25kaXRpb24pKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgbGFicyh0aXRsZSA9ICJDbHVzdGVyIENvbXBvc2l0aW9uIGJ5IENvbmRpdGlvbiIsDQogICAgICAgeCA9ICJDbHVzdGVyIiwNCiAgICAgICB5ID0gIlBlcmNlbnRhZ2Ugb2YgQ2x1c3RlciIsDQogICAgICAgZmlsbCA9ICJTYW1wbGUiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCiMjdmlzdWFsaXplIHBlcmNlbnQubXQgcGVyIGNsdXN0ZXINCmBgYHtyfQ0KI2FkZCBtZXRhZGF0YSBjb2x1bW4gZm9yIGNsdXN0ZXJfY29uZGl0aW9uDQptZXJnZWRfYWxsMiRjbHVzdGVyX2NvbmRpdGlvbiA8LSBwYXN0ZTAobWVyZ2VkX2FsbDIkc2V1cmF0X2NsdXN0ZXJzLCJfIixtZXJnZWRfYWxsMiRjb25kaXRpb24pDQpJZGVudHMobWVyZ2VkX2FsbDIpIDwtIG1lcmdlZF9hbGwyJGNsdXN0ZXJfY29uZGl0aW9uDQoNCiNjaGVjayB0aGF0IG1ldGFkYXRhIGNvbHVtbiBpcyBhZGRlZA0KY29sbmFtZXMobWVyZ2VkX2FsbDJAbWV0YS5kYXRhKQ0KDQojIENyZWF0ZSBzdW1tYXJ5IGRhdGEgZnJhbWUNCnBlcmNlbnRfbXRfc3VtbWFyeSA8LSBtZXJnZWRfYWxsMkBtZXRhLmRhdGEgJT4lDQogIGdyb3VwX2J5KHNldXJhdF9jbHVzdGVycykgJT4lDQogICAgc3VtbWFyaXNlKA0KICAgIG1lYW5fcGVyY2VudF9tdCA9IG1lYW4ocGVyY2VudC5tdCwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWRpYW5fcGVyY2VudF9tdCA9IG1lZGlhbihwZXJjZW50Lm10LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICBuX2NlbGxzID0gbigpDQogICkNCg0KcHJpbnQocGVyY2VudF9tdF9zdW1tYXJ5KQ0KDQpnZ3Bsb3QocGVyY2VudF9tdF9zdW1tYXJ5LCBhZXMoeCA9IGZhY3RvcihzZXVyYXRfY2x1c3RlcnMpLCB5ID0gbWVhbl9wZXJjZW50X210KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICIjMmM3ZmI4IikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIk1lYW4gTWl0b2Nob25kcmlhbCBQZXJjZW50YWdlIGJ5IENsdXN0ZXIiLA0KICAgIHggPSAiQ2x1c3RlciIsDQogICAgeSA9ICJNZWFuICUgTWl0b2Nob25kcmlhbCBHZW5lcyINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCmBgYHtyfQ0KY2x1c3RlcnNfdGFibGUgPC0gdGFibGUobWVyZ2VkX2FsbDIkc2V1cmF0X2NsdXN0ZXJzLCBtZXJnZWRfYWxsMiRzYW1wbGUpDQpjbHVzdGVyc190YWJsZV9jb25kIDwtIHRhYmxlKG1lcmdlZF9hbGwyJHNldXJhdF9jbHVzdGVycywgbWVyZ2VkX2FsbDIkY29uZGl0aW9uKQ0KY2x1c3RlcnNfdGFibGUNCmNsdXN0ZXJzX3RhYmxlX2NvbmQNCg0KYGBgDQojQ3JlYXRlIGNsdXN0ZXIgdHJlZSANCmBgYHtyfQ0KIyBDbHVzdHJlZSB2aXN1YWxpemF0aW9uDQppbnN0YWxsLnBhY2thZ2VzKCJjbHVzdHJlZSIpDQpsaWJyYXJ5KGNsdXN0cmVlKQ0KDQptZXJnZWRfdHJlZSA8LSBtZXJnZWRfYWxsMg0KDQojQWRkIG11bHRpcGxlIHJlc29sdXRpb25zDQptZXJnZWRfdHJlZSA8LSBGaW5kQ2x1c3RlcnMobWVyZ2VkX3RyZWUsIHJlc29sdXRpb24gPSBjKDAuMSwgMC4yLCAwLjQsIDAuNiwgMC44KSkNCg0KDQpjbHVzdGVyLnRyZWUgPC0gY2x1c3RyZWUoDQogIG1lcmdlZF90cmVlLCAiUk5BX3Nubl9yZXMuIikgKyBnZ3RpdGxlKCJDbHVzdGVyIHRyZWUiKQ0KDQpnZ3NhdmUoInJlc29sdXRpb24gY2x1c3RlciB0cmVlLnN2ZyIsIHBsb3QgPSBjbHVzdGVyLnRyZWUsIHdpZHRoID0gMTIpDQoNCmNsdXN0ZXIudHJlZSANCmBgYA0KIyNzaWxob3VldHRlIHBsb3QNCmBgYHtyLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0PTEyfQ0KbGlicmFyeShjbHVzdGVyKQ0KbGlicmFyeShwdXJycikNCg0KIyBDb252ZXJ0IGNsdXN0ZXJzIHRvIG51bWVyaWMNCmNsdXN0ZXJzIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKG1lcmdlZF9hbGwyJHNldXJhdF9jbHVzdGVycykpDQoNCiMgQ29tcHV0ZSBkaXN0YW5jZSBvbiBQQ0EgZW1iZWRkaW5ncw0KZCA8LSBkaXN0KG1lcmdlZF9hbGwyQHJlZHVjdGlvbnMkcGNhQGNlbGwuZW1iZWRkaW5ncykNCg0KIyBSdW4gc2lsaG91ZXR0ZQ0Kc2lsIDwtIGNsdXN0ZXI6OnNpbGhvdWV0dGUoY2x1c3RlcnMsIGQpDQoNCiMgNC4gQ29udmVydCB0byBkYXRhIGZyYW1lDQpzaWxfZGYgPC0gYXMuZGF0YS5mcmFtZShzaWwpDQpzaWxfZGYkY2VsbCA8LSByb3duYW1lcyhzaWxfZGYpIA0KY29sbmFtZXMoc2lsX2RmKSA8LSBjKCJjbHVzdGVyIiwgIm5laWdoYm9yIiwgInNpbF93aWR0aCIsICJjZWxsIikNCnNpbF9kZg0KDQojIE1ha2UgY2x1c3RlciBhIGZhY3RvciBmb3IgcGxvdHRpbmcNCnNpbF9kZiRjbHVzdGVyIDwtIGFzLmZhY3RvcihzaWxfZGYkY2x1c3RlcikNCg0KIyA1LiBnZ3Bsb3Qgc2lsaG91ZXR0ZSBwbG90DQpnZ3Bsb3Qoc2lsX2RmLCBhZXMoeCA9IHJlb3JkZXIoY2VsbCwgc2lsX3dpZHRoKSwgeSA9IHNpbF93aWR0aCwgZmlsbCA9IGNsdXN0ZXIpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEsIHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+IGNsdXN0ZXIsIHNjYWxlcyA9ICJmcmVlX3kiLCBuY29sID0gMSkgKw0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHggPSAiQ2VsbHMgKG9yZGVyZWQgYnkgc2lsaG91ZXR0ZSB3aWR0aCkiLA0KICAgICAgIHkgPSAiU2lsaG91ZXR0ZSB3aWR0aCIsDQogICAgICAgdGl0bGUgPSAiU2lsaG91ZXR0ZSBwbG90IG9mIFNldXJhdCBjbHVzdGVycyIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQoNCiNFdmFsdWF0ZSBjb25zZXJ2ZWQgbWFya2VyIGdlbmVzLCB1c2UgdG8gY29uc29saWRhdGUgc21hbGxlciBjbHVzdGVycyBpbnRvIGxhcmdlciBvbmVzDQpgYGB7cn0NCklkZW50cyhtZXJnZWRfYWxsMikgPC0gJ3NldXJhdF9jbHVzdGVycycNCm1lcmdlZF9hbGwyJGNvbnNvbGlkYXRlZF9jbHVzdGVycyA8LSBtZXJnZWRfYWxsMiRzZXVyYXRfY2x1c3RlcnMNCg0KbGlicmFyeShwcmVzdG8pDQpsaWJyYXJ5KFNldXJhdFdyYXBwZXJzKQ0KDQojIEluaXRpYWxpemUgdGhlIGxpc3QNCmNvbnNlcnZlZF9tYXJrZXJzIDwtIGxpc3QoKQ0KDQojIExvb3AgdGhyb3VnaCB0aGUgY2x1c3RlcnMgaW4gdGhlIHNwZWNpZmllZCBvcmRlcg0KZm9yIChpIGluIDA6MjQpIHsNCiAgYXV4IDwtIFNldXJhdFdyYXBwZXJzOjpSdW5QcmVzdG8oDQogICAgbWVyZ2VkX2FsbDIsDQogICAgaWRlbnQuMSA9IGksDQogICAgb25seS5wb3MgPSBUUlVFLA0KICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAuNSwNCiAgICBtaW4ucGN0ID0gMC40DQogICkNCiAgDQogIGlmICghaXMubnVsbChhdXgpICYmIG5yb3coYXV4KSA+IDApIHsNCiAgICBhdXgkZ2VuZSA8LSByb3duYW1lcyhhdXgpDQogICAgYXV4JGNsdXN0ZXIgPC0gaQ0KICAgIGNvbnNlcnZlZF9tYXJrZXJzW1twYXN0ZTAoImNsdXN0ZXJfIiwgaSldXSA8LSBhdXgNCiAgfQ0KfQ0KDQojZmlsdGVyDQpmb3IoaSBpbiBuYW1lcyhjb25zZXJ2ZWRfbWFya2Vycykpew0KICBhdXggPC0gY29uc2VydmVkX21hcmtlcnNbW2ldXQ0KICBhdXgkY2x1c3RlciA8LSBpDQogIGF1eCA8LSBhdXhbYXV4JHBfdmFsX2FkaiA8IDAuMDUsXQ0KICBhdXggPC0gYXV4W29yZGVyKGF1eCRhdmdfbG9nMkZDLCBkZWNyZWFzaW5nPVQpLF0NCiAgY29uc2VydmVkX21hcmtlcnNbW2ldXSA8LSBhdXgNCn0NCg0KYGBgDQojI2V4cG9ydCBjb25zZXJ2ZWQgbWFya2VycyBhcyBleGNlbCBmaWxlDQpgYGB7cn0NCmxpYnJhcnkocmlvKQ0KcmlvOjpleHBvcnQoY29uc2VydmVkX21hcmtlcnMsICJjb25zZXJ2ZWQgbWFya2VycyBhbGwgY2x1c3RlcnMueGxzeCIpDQpgYGANCg0KI0NvbnNvbGlkYXRlIGNsdXN0ZXJzDQpgYGB7cn0NCg0KI2NvcHkgc2V1cmF0X2NsdXN0ZXJzIHRvIG5ldyBtZXRhZGF0ZSBjb2x1bW4NCm1lcmdlZF9hbGwyJGNvbnNvbGlkYXRlZF9jbHVzdGVycyA8LSBtZXJnZWRfYWxsMiRzZXVyYXRfY2x1c3RlcnMNCg0KI3NldCBpZGVudGl0eSBhcyBuZXcgY29sdW1uLCAnY29uc29saWRhdGVkX2NsdXN0ZXJzJw0KSWRlbnRzKG1lcmdlZF9hbGwyKSA8LSAnY29uc29saWRhdGVkX2NsdXN0ZXJzJw0KDQojIE1ha2UgbmV3IGNvbHVtbiBhIGNoYXJhY3RlciB2ZWN0b3IgaW5zdGVhZCBvZiBmYWN0b3INCm1lcmdlZF9hbGwyQG1ldGEuZGF0YSRjb25zb2xpZGF0ZWRfY2x1c3RlcnMgPC0gDQogIGFzLmNoYXJhY3RlcihtZXJnZWRfYWxsMkBtZXRhLmRhdGEkY29uc29saWRhdGVkX2NsdXN0ZXJzKQ0KDQojZGVmaW5lICdjdXJyZW50IGNsdXN0ZXJzJyBmb3IgbWFwcGluZyBsYXRlcg0KY3VycmVudF9jbHVzdGVycyA8LSBhcy5jaGFyYWN0ZXIobWVyZ2VkX2FsbDIkc2V1cmF0X2NsdXN0ZXJzKQ0KDQoNCiNDb25zb2xpZGF0aW9ucyAodW5uYW1lZCBjbHVzdGVycyBnbyB1bmNoYW5nZWQpDQpuZXdfY2x1c3Rlcl9tYXAgPC0gYygNCiAgIjI0IiAgPSAiMTIiLA0KICAiMjMiICA9ICIxIiwNCiAgIjIxIiA9ICIxNCINCikNCg0KIyM1LDEwLDE0LDE4LDIxIGFyZSBhbGwgbXllbG9pZCAoQ0Q2OCspDQogICAjMTQvMjEgPSBEQw0KIyNmcm9tIGNsdXN0ZXIgdHJlZTogNSBhbmQgMTAgY29tYmluZSwgMTQsIDE4LCBhbmQgMjEgY29tYmluZQ0KIzI0IC0+IDEyIChnYW1tYSBkZWx0YSBUPykgLT4gNyAoYWxzbyBleHByZXNzaW5nIFRyZ3YgaW4gdG9wIGdlbmVzKQ0KDQojQXBwbHkgbWFwcGluZyBiYWNrIHRvIG1ldGFkYXRhDQptZXJnZWRfYWxsMiRjb25zb2xpZGF0ZWRfY2x1c3RlcnMgPC0gaWZlbHNlKA0KICBjdXJyZW50X2NsdXN0ZXJzICVpbiUgbmFtZXMobmV3X2NsdXN0ZXJfbWFwKSwNCiAgbmV3X2NsdXN0ZXJfbWFwW2N1cnJlbnRfY2x1c3RlcnNdLA0KICBjdXJyZW50X2NsdXN0ZXJzDQopDQoNCiMgQ29udmVydCBiYWNrIHRvIGZhY3RvciAob3B0aW9uYWwsIGJ1dCB1c3VhbGx5IGNsZWFuZXIgZm9yIElkZW50cykNCm1lcmdlZF9hbGwyQG1ldGEuZGF0YSRjb25zb2xpZGF0ZWRfY2x1c3RlcnMgPC0gDQogIGZhY3RvcihtZXJnZWRfYWxsMkBtZXRhLmRhdGEkY29uc29saWRhdGVkX2NsdXN0ZXJzLCBsZXZlbHMgPSBjKDA6MjAsMjIpKQ0KDQojIFNldCBhY3RpdmUgaWRlbnRpdGllcyBjb3JyZWN0bHkNCklkZW50cyhtZXJnZWRfYWxsMikgPC0gJ2NvbnNvbGlkYXRlZF9jbHVzdGVycycNCmBgYA0KDQojTmFtaW5nIGNsdXN0ZXJzIChwcmVsaW1pbmFyeSwgbWFudWFsKS4gSW4gbmV3IG9iamVjdCAnbWVyZ2VkX2Fubm90Jw0KYGBge3J9DQptZXJnZWRfYW5ub3QgPC0gbWVyZ2VkX2FsbDINCm1lcmdlZF9hbm5vdCRhbm5vdCA8LSBtZXJnZWRfYW5ub3QkY29uc29saWRhdGVkX2NsdXN0ZXJzDQoNCg0KI3NldCBpZGVudGl0eSBhcyBuZXcgY29sdW1uLCAnY29uc29saWRhdGVkX2NsdXN0ZXJzJw0KSWRlbnRzKG1lcmdlZF9hbm5vdCkgPC0gJ2Fubm90Jw0KDQojIE1ha2UgbmV3IGNvbHVtbiBhIGNoYXJhY3RlciB2ZWN0b3IgaW5zdGVhZCBvZiBmYWN0b3INCm1lcmdlZF9hbm5vdEBtZXRhLmRhdGEkYW5ub3QgPC0gDQogIGFzLmNoYXJhY3RlcihtZXJnZWRfYW5ub3RAbWV0YS5kYXRhJGFubm90KQ0KDQojZGVmaW5lICdjdXJyZW50IGNsdXN0ZXJzJyBmb3IgbWFwcGluZyBsYXRlcg0KY3VycmVudF9jbHVzdGVyc19hbm5vdCA8LSBhcy5jaGFyYWN0ZXIobWVyZ2VkX2Fubm90JGFubm90KQ0KDQoNCiNDb25zb2xpZGF0aW9ucyAodW5uYW1lZCBjbHVzdGVycyBnbyB1bmNoYW5nZWQpDQpuZXdfY2x1c3Rlcl9tYXBfYW5ub3QgPC0gYygNCiAgIjAiID0gIkN5dG90b3hpYyBOSy1UIiwNCiAgIjEiID0gIkIgY2VsbCIsDQogICIyIiA9ICJOYWl2ZSBUX1RDTSIsDQogICIzIiA9ICJOZXV0cm9waGlsIiwgDQogICI0IiA9ICJQVCBncm91cCAxIiwNCiAgIjUiID0gIkFjdGl2YXRlZCBtYWNyb3BoYWdlIiwNCiAgIjYiID0gIkltbWF0dXJlIFRfZ3JvdXAgMiIsDQogICI3IiA9ICJUaDEiLA0KICAiOCIgPSAiUFQgZ3JvdXAgMiIsDQogICI5IiA9ICJQVCBncm91cCAzIiwNCiAgIjEwIiA9ICJNeWVsb2lkIiwNCiAgIjExIiA9ICJQVCBncm91cCA0IiwNCiAgIjEyIiA9ICJUIGNlbGxfZ2FtbWFfZGVsdGEiLA0KICAiMTMiID0gIlRBTF9EQ1QiLA0KICAiMTQiID0gIkRDIiwNCiAgIjE1IiA9ICJEQ1RfQ05UIiwNCiAgIjE2IiA9ICJUQUxfRENUIiwNCiAgIjE3IiA9ICJUQUwiLA0KICAiMTgiID0gIk1vbm9fbWFjcm9waGFnZSIsDQogICIxOSIgPSAiVGlzc3VlLXJlc2lkZW50IE5LLVQiLA0KICAiMjAiID0gIkVuZG90aGVsaWFsIiwNCiAgIjIyIiA9ICJJbnRlcmNhbGF0ZWQgY2VsbCIpDQoNCg0KDQojQXBwbHkgbWFwcGluZyBiYWNrIHRvIG1ldGFkYXRhDQptZXJnZWRfYW5ub3QkYW5ub3QgPC0gaWZlbHNlKA0KICBjdXJyZW50X2NsdXN0ZXJzX2Fubm90ICVpbiUgbmFtZXMobmV3X2NsdXN0ZXJfbWFwX2Fubm90KSwNCiAgbmV3X2NsdXN0ZXJfbWFwX2Fubm90W2N1cnJlbnRfY2x1c3RlcnNfYW5ub3RdLA0KICBjdXJyZW50X2NsdXN0ZXJzX2Fubm90DQopDQoNCiMgQ29udmVydCBiYWNrIHRvIGZhY3RvciAob3B0aW9uYWwsIGJ1dCB1c3VhbGx5IGNsZWFuZXIgZm9yIElkZW50cykNCm1lcmdlZF9hbm5vdEBtZXRhLmRhdGEkYW5ub3QgPC0gDQogIGZhY3RvcihtZXJnZWRfYW5ub3RAbWV0YS5kYXRhJGFubm90KQ0KDQojIFNldCBhY3RpdmUgaWRlbnRpdGllcyBjb3JyZWN0bHkNCklkZW50cyhtZXJnZWRfYW5ub3QpIDwtICdhbm5vdCcNCg0KDQpgYGANCg0KI1Zpc3VhbGl6ZSB1bWFwIHdpdGggYW5ub3RhdGlvbnMNCmBgYHtyLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD03fQ0KRGltUGxvdChtZXJnZWRfYW5ub3QsIGxhYmVsPVQpICsgTm9MZWdlbmQoKQ0KYGBgDQojI3NwbGl0IGJ5IGNvbmRpdGlvbg0KYGBge3IsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEzfQ0KRGltUGxvdChtZXJnZWRfYW5ub3QsIGxhYmVsPVQsIHNwbGl0LmJ5ID0gJ2NvbmRpdGlvbicpICsgTm9MZWdlbmQoKQ0KYGBgDQoNCmBgYHtyfQ0Kc2F2ZVJEUyhtZXJnZWRfYW5ub3QsICJFdmFuX3NldXJhdF9vYmouUkRTIikNCmBgYA0KDQpgYGB7cn0NCklkZW50cyhtZXJnZWRfYW5ub3QpIDwtICdhbm5vdCcNCg0KYW5ub3RhdGlvbnMgPC0gbGlzdCgpDQphbm5vdGF0aW9ucyA8LSB1bmlxdWUoSWRlbnRzKG1lcmdlZF9hbm5vdCkpDQoNCiMgSW5pdGlhbGl6ZSB0aGUgbGlzdA0KY29uc2VydmVkX21hcmtlcnNfYW5ub3QgPC0gbGlzdCgpDQoNCiMgTG9vcCB0aHJvdWdoIHRoZSBjbHVzdGVycyBpbiB0aGUgc3BlY2lmaWVkIG9yZGVyDQpmb3IgKGkgaW4gdW5pcXVlKGFubm90YXRpb25zKSkgew0KICBhdXggPC0gU2V1cmF0V3JhcHBlcnM6OlJ1blByZXN0bygNCiAgICBtZXJnZWRfYW5ub3QsDQogICAgaWRlbnQuMSA9IGksDQogICAgb25seS5wb3MgPSBUUlVFLA0KICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAuNSwNCiAgICBtaW4ucGN0ID0gMC40DQogICkNCiAgDQogIGlmICghaXMubnVsbChhdXgpICYmIG5yb3coYXV4KSA+IDApIHsNCiAgICBhdXgkZ2VuZSA8LSByb3duYW1lcyhhdXgpDQogICAgYXV4JGNsdXN0ZXIgPC0gaQ0KICAgIGNvbnNlcnZlZF9tYXJrZXJzX2Fubm90W1tpXV0gPC0gYXV4DQogIH0NCn0NCg0KI2ZpbHRlcg0KZm9yKGkgaW4gbmFtZXMoY29uc2VydmVkX21hcmtlcnNfYW5ub3QpKXsNCiAgYXV4IDwtIGNvbnNlcnZlZF9tYXJrZXJzX2Fubm90W1tpXV0NCiAgYXV4JGNsdXN0ZXIgPC0gaQ0KICBhdXggPC0gYXV4W2F1eCRwX3ZhbF9hZGogPCAwLjA1LF0NCiAgYXV4IDwtIGF1eFtvcmRlcihhdXgkYXZnX2xvZzJGQywgZGVjcmVhc2luZz1UKSxdDQogIGNvbnNlcnZlZF9tYXJrZXJzX2Fubm90W1tpXV0gPC0gYXV4DQp9DQoNCmBgYA0KDQoNCmBgYHtyfQ0KbWVyZ2VkX2FsbDIkY29uc29saWRhdGVkX2NsdXN0ZXJzIDwtIG1lcmdlZF9hbGwyJHNldXJhdF9jbHVzdGVycw0KSWRlbnRzKG1lcmdlZF9hbGwyKSA8LSAnY29uc29saWRhdGVkX2NsdXN0ZXJzJw0KDQojIzI0IGludG8gMTIsIFQgY2VsbCBwb3B1bGF0aW9uIHdpdGggVHJnIA0KbWVyZ2VkX2FsbDJAbWV0YS5kYXRhJGNvbnNvbGlkYXRlZF9jbHVzdGVycyA8LSANCiAgaWZlbHNlKA0KICAgIG1lcmdlZF9hbGwyQG1ldGEuZGF0YSRjb25zb2xpZGF0ZWRfY2x1c3RlcnMgPT0gIjI0IiwNCiAgICAxMiwNCiAgICBtZXJnZWRfYWxsMkBtZXRhLmRhdGEkY29uc29saWRhdGVkX2NsdXN0ZXJzDQogICkNCg0KIzIzIGludG8gMSwgQiBjZWxscyAtIGxvdHMgb2YgaW1tdW5vZ2xvYnVsaW5zIGluIDIzIGFuZCBqY2hhaW4sIDEgaXMgY2QxOS1oaWdoIGIgY2VsbCBncm91cA0KbWVyZ2VkX2FsbDJAbWV0YS5kYXRhJGNvbnNvbGlkYXRlZF9jbHVzdGVycyA8LSANCiAgaWZlbHNlKA0KICAgIG1lcmdlZF9hbGwyQG1ldGEuZGF0YSRjb25zb2xpZGF0ZWRfY2x1c3RlcnMgPT0gIjIzIiwNCiAgICAxLA0KICAgIG1lcmdlZF9hbGwyQG1ldGEuZGF0YSRjb25zb2xpZGF0ZWRfY2x1c3RlcnMNCiAgKQ0KDQp1bmlxdWUobWVyZ2VkX2FsbDIkY29uc29saWRhdGVkX2NsdXN0ZXJzKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCisrKysNCiNFdmFsdWF0ZSBjb25zZXJ2ZWQgYW5kIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcw0KYGBge3J9DQpJZGVudHMobWVyZ2VkX2FsbDIpIDwtICdzZXVyYXRfY2x1c3RlcnMnDQoNCiMgRGVmaW5lIHRoZSBjdXN0b20gb3JkZXINCmNsdXN0ZXJfb3JkZXJfMC4xIDwtIGMoMCwgMSwgMTAsIDExLCAxMiwgMTMsIDE0LCAxNSwgMiwgMywgNCwgNSwgNiwgNywgOCwgOSkNCg0KIyBJbml0aWFsaXplIHRoZSBsaXN0DQpjb25zZXJ2ZWRfbWFya2Vyc19yZXMwLjEgPC0gbGlzdCgpDQoNCiMgTG9vcCB0aHJvdWdoIHRoZSBjbHVzdGVycyBpbiB0aGUgc3BlY2lmaWVkIG9yZGVyDQpmb3IgKGkgaW4gY2x1c3Rlcl9vcmRlcl8wLjEpIHsNCiAgYXV4IDwtIFNldXJhdFdyYXBwZXJzOjpSdW5QcmVzdG8oDQogICAgbWVyZ2VkX2FsbF9jb25zLA0KICAgIGlkZW50LjEgPSBpLA0KICAgIG9ubHkucG9zID0gVFJVRSwNCiAgICBsb2dmYy50aHJlc2hvbGQgPSAwLjUsDQogICAgbWluLnBjdCA9IDAuNA0KICApDQogIA0KICBpZiAoIWlzLm51bGwoYXV4KSAmJiBucm93KGF1eCkgPiAwKSB7DQogICAgYXV4JGdlbmUgPC0gcm93bmFtZXMoYXV4KQ0KICAgIGF1eCRjbHVzdGVyIDwtIGkNCiAgICBjb25zZXJ2ZWRfbWFya2Vyc19yZXMwLjFbW3Bhc3RlMCgiY2x1c3Rlcl8iLCBpKV1dIDwtIGF1eA0KICB9DQp9DQpgYGANCg0KDQpgYGB7cn0NCiNmaWx0ZXINCmZvcihpIGluIG5hbWVzKGNvbnNlcnZlZF9tYXJrZXJzX3JlczAuMSkpew0KICBhdXggPC0gY29uc2VydmVkX21hcmtlcnNfcmVzMC4xW1tpXV0NCiAgYXV4JGNsdXN0ZXIgPC0gaQ0KICBhdXggPC0gYXV4W2F1eCRwX3ZhbF9hZGogPCAwLjA1LF0NCiAgYXV4IDwtIGF1eFtvcmRlcihhdXgkYXZnX2xvZzJGQywgZGVjcmVhc2luZz1UKSxdDQogIGNvbnNlcnZlZF9tYXJrZXJzX3JlczAuMVtbaV1dIDwtIGF1eA0KfQ0KYGBgDQoNCmBgYHtyfQ0KIyB0YWtlIHRvcCAxNSBnZW5lcyBmcm9tIGVhY2ggY2x1c3RlciB0byBkbyBhbm5vdGF0aW9ucw0KY2x1c3Rlcl9hbm5vdF9kZl8wLjEgPC0gY29uc2VydmVkX21hcmtlcnNfcmVzMC4xICU+JQ0KICBtYXAofiBoZWFkKC54LCAxMCkpICU+JSAgIyBUYWtlIGZpcnN0IDEwIHJvd3Mgb2YgZWFjaCBkYXRhIGZyYW1lDQogIGJpbmRfcm93cygpICAgICAgICAgICAgICAjIENvbWJpbmUgaW50byBvbmUgZGF0YSBmcmFtZQ0KYGBgDQoNCg0KYGBge3J9DQpyaW86OmV4cG9ydChjb25zZXJ2ZWRfbWFya2Vyc19yZXMwLjEsICJhbGwgcG9zIGNvbnNlcnZlZCBnZW5lcyBwZXIgY2x1c3Rlci54bHN4IikNCnJpbzo6ZXhwb3J0KGNsdXN0ZXJfYW5ub3RfZGZfMC4xLCAidG9wIDE1IGNvbnNlcnZlZCBnZW5lcyBwZXIgY2x1c3Rlci54bHN4IikNCmBgYA0KDQojUHV0IGluIGNsdXN0ZXIgYW5ub3RhdGlvbnMNCmBgYHtyfQ0KbDFfYW5ub3RhdGlvbnNfcmVzMC4xIDwtIGMoDQogICJQVCIsICAgICAgICAgICAjIGNsdXN0ZXIgMA0KICAiVCBjZWxsIiwgICAgICAgICAgICAgICAgICAgICMgY2x1c3RlciAxDQogICJNeWVsb2lkIiwgICAgICAgICMgY2x1c3RlciAyDQogICJCIENlbGwiLCAgICAgICAgICAgICAgICAgICAgICAgICAjIGNsdXN0ZXIgMw0KICAiTkstVCIsICAgICAgIyBjbHVzdGVyIDQNCiAgIlQgY2VsbCIsICMgY2x1c3RlciA1DQogICJQTU4iLCAgICAgICAgICAgICAgICAgICAgIyBjbHVzdGVyIDYNCiAgIkZpYnJvX1N0cm9tYWwiLCAgICAgIyBjbHVzdGVyIDcNCiAgIlBvZG9jeXRlIiwgICAgICAgICAgICAgICAgICAgICAgIyBjbHVzdGVyIDgNCiAgIk1hY3JvcGhhZ2UiLCAgICAgICAgICAgICAgIyBjbHVzdGVyIDkNCiAgIlQgY2VsbCIsICAgICAgICAgICMgY2x1c3RlciAxMA0KICAiVEFMIiwgICAgICAgICAgICMgY2x1c3RlciAxMQ0KICAicERDIiwgICAgICAgICAgICMgY2x1c3RlciAxMg0KICAiRENUIiwgICAgICAgIyBjbHVzdGVyIDEzDQogICJJQy1CIiwgICAgICAjIGNsdXN0ZXIgMTQNCiAgIklMQzIvTXllbG9pZCIgICAgIyBjbHVzdGVyIDE1DQopDQoNCklkZW50cyhtZXJnZWRfYWxsX2NvbnMpIDwtICdSTkFfc25uX3Jlcy4wLjEnDQoNCnVuaXF1ZShJZGVudHMobWVyZ2VkX2FsbF9jb25zKSkNCmBgYA0KYGBge3IsIGZpZy53aWR0aD0xMH0NCmNsdXN0ZXIudHJlZQ0KYGBgDQpgYGB7ciwgZmlnLmhlaWdodD04fQ0KSWRlbnRzKG1lcmdlZF9hbGxfY29ucykgPC0gJ3NldXJhdF9jbHVzdGVycycNCkRlZmF1bHRBc3NheShtZXJnZWRfYWxsX2NvbnMpDQoNCm1lcmdlZF8wLjA1IDwtIEZpbmRDbHVzdGVycyhtZXJnZWRfMC4wNSwgcmVzb2x1dGlvbiA9IDAuMDEpDQogIA0KY2x1c3Rlci50cmVlLjhkaW0gPC0gY2x1c3RyZWUobWVyZ2VkXzAuMDUsIHByZWZpeCA9ICJSTkFfc25uX3Jlcy4iKQ0KY2x1c3Rlci50cmVlLjhkaW0NCmBgYA0KDQoNCmBgYHtyfQ0KIyBNYWtlIHN1cmUgY2x1c3RlciBJRHMgYXJlIG51bWVyaWMgYW5kIDAtYmFzZWQNCmNsdXN0ZXJzIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKG1lcmdlZF9hbGxfY29ucyRSTkFfc25uX3Jlcy4wLjEpKQ0KDQojIE1hcCBjbHVzdGVyIElEcyB0byBtYW51YWwgYW5ub3RhdGlvbnMNCiMgQ3JlYXRlIGEgbmFtZWQgdmVjdG9yIGZvciBlYXN5IGxvb2t1cA0KbmFtZXMobDFfYW5ub3RhdGlvbnNfcmVzMC4xKSA8LSBhcy5jaGFyYWN0ZXIoMDoxNSkNCg0KIyBBZGQgYW5ub3RhdGlvbiBtZXRhZGF0YSBieSBtYXRjaGluZyBjbHVzdGVyIElEcw0KbWVyZ2VkX2FsbF9jb25zQG1ldGEuZGF0YSRsMV9hbm5vdGF0aW9uc19yZXMwLjEgPC0gbDFfYW5ub3RhdGlvbnNfcmVzMC4xW2FzLmNoYXJhY3RlcihjbHVzdGVycyldDQoNCmBgYA0KDQpgYGB7cn0NCm1lcmdlZF9hbGxfY29uc0BtZXRhLmRhdGENCmBgYA0KDQpgYGB7cn0NCklkZW50cyhtZXJnZWRfYWxsX2NvbnMpIDwtICdhbm5vdGF0aW9uJw0KRGltUGxvdChtZXJnZWRfYWxsX2NvbnMsIGxhYmVsID0gVCwgcmVwZWwgPSBUKQ0KYGBgDQpgYGB7cn0NCklkZW50cyhtZXJnZWRfYWxsX2NvbnMpIDwtICdSTkFfc25uX3Jlcy4wLjEnDQpEaW1QbG90KG1lcmdlZF9hbGxfY29ucywgcmVkdWN0aW9uID0gInRzbmUiKSsgZ2d0aXRsZSgidW5iaWFzZWQgY2x1c3RlcmluZyIpDQpgYGANCmBgYHtyfQ0KSWRlbnRzKG9ial9hbm5vdCkgPC0gJ2dyb3VwaW5nJw0KRGltUGxvdChvYmpfYW5ub3QsIHJlZHVjdGlvbiA9ICJ0c25lIiwgc3BsaXQuYnkgPSAnY29uZGl0aW9uJykNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTl9DQpEaW1QbG90KG1lcmdlZF9hbGxfY29ucywgbGFiZWwgPSBULCByZXBlbCA9IFQsIHNwbGl0LmJ5ID0gJ2NvbmRpdGlvbicpICsgTm9MZWdlbmQoKQ0KYGBgDQoNCmBgYHtyfQ0KY2x1c3RlcnNfMC4xX3RhYmxlIDwtIHRhYmxlKG1lcmdlZF9hbGxfY29ucyRSTkFfc25uX3Jlcy4wLjEsIG1lcmdlZF9hbGxfY29ucyRzYW1wbGUpDQpjbHVzdGVyc18wLjFfdGFibGVfY29uZCA8LSB0YWJsZShtZXJnZWRfYWxsX2NvbnMkUk5BX3Nubl9yZXMuMC4xLCBtZXJnZWRfYWxsX2NvbnMkY29uZGl0aW9uKQ0KY2x1c3RlcnNfMC4xX3RhYmxlDQpjbHVzdGVyc18wLjFfdGFibGVfY29uZA0KYGBgDQpgYGB7cn0NCmNsdXN0ZXJzXzAuMDFfdGFibGUgPC0gdGFibGUobWVyZ2VkXzAuMDUkUk5BX3Nubl9yZXMuMC4wMSwgbWVyZ2VkXzAuMDUkc2FtcGxlKQ0KY2x1c3RlcnNfMC4wMV90YWJsZV9jb25kIDwtIHRhYmxlKG1lcmdlZF8wLjA1JFJOQV9zbm5fcmVzLjAuMDEsIG1lcmdlZF8wLjA1JGNvbmRpdGlvbikNCmNsdXN0ZXJzXzAuMDFfdGFibGUNCmNsdXN0ZXJzXzAuMDFfdGFibGVfY29uZA0KDQpjbHVzdGVyc18wLjAxX2RmIDwtIGFzLmRhdGEuZnJhbWUoY2x1c3RlcnNfMC4wMV90YWJsZSkNCmNsdXN0ZXJzXzAuMDFfZGYNCg0KY2x1c3RlcnNfMC4wMV9kZiRhbm5vdCA8LSBjKCJseW1waG9jeXRlIiwgIm15ZWxvaWQiLCAiUFQiLCAiQiBjZWxsIiwgIk5LIiwgIlBNTiIsICJUQUxfRGlzdGFsIHR1YnVsZSIsICJQVF9FTVRfRmlicm8iLCAiRENUIikNCg0KYGBgDQoNCg0KYGBge3J9DQptZXJnZWRfYWxsX2NvbnNAbWV0YS5kYXRhJGwxX2Fubm90YXRpb25zX3JlczAuMSA8LSANCiAgaWZlbHNlKA0KICAgIG1lcmdlZF9hbGxfY29uc0BtZXRhLmRhdGEkbDFfYW5ub3RhdGlvbnNfcmVzMC4xID09ICJGaWJyb2JsYXN0L1N0cm9tYSIsDQogICAgIkZpYnJvX1N0cm9tYWwiLA0KICAgIG1lcmdlZF9hbGxfY29uc0BtZXRhLmRhdGEkbDFfYW5ub3RhdGlvbnNfcmVzMC4xDQogICkNCmBgYA0KDQpgYGB7cn0NCklkZW50cyhtZXJnZWRfYWxsX2NvbnMpIDwtICdSTkFfc25uX3Jlcy4wLjgnDQoNCkRFXzAuOF8xMHYxOSA8LSBTZXVyYXRXcmFwcGVyczo6UnVuUHJlc3RvKG1lcmdlZF9hbGxfY29ucywgaWRlbnQuMSA9IDEwLCBpZGVudC4yID0gMTkpDQpnZW5lIDwtIHJvd25hbWVzKERFXzAuOF8xMHYxOSkNCkRFXzAuOF8xMHYxOSRnZW5lIDwtIGdlbmUNCg0KREVfMC44XzEwdjE5IDwtIERFXzAuOF8xMHYxOVtERV8wLjhfMTB2MTkkcF92YWxfYWRqIDwgMC4wNSxdDQpERV8wLjhfMTB2MTkgPC0gb3JkZXIoREVfMC44XzEwdjE5JGF2Z19sb2cyRkMsIGRlY3JlYXNpbmcgPSBUKSAgDQoNCg0KDQoNCmBgYA0KDQoNCmBgYHtyfQ0KI2NvbXBhcmUgREUgZ2VuZSBleHByZXNzaW9uIGJldHdlZW4gVVRJL2NvbnRyb2xzIGluIGVhY2ggY2x1c3Rlcg0KI2RlZmluZWQgYXMgZGVnX2NvbnMgKGxpc3QpDQpjb2xuYW1lcyhtZXJnZWRfYWxsX2NvbnNAbWV0YS5kYXRhKQ0KDQptZXJnZWRfYWxsX2NvbnNAbWV0YS5kYXRhJGwxX2NvbmRpdGlvbiA8LSBwYXN0ZTAobWVyZ2VkX2FsbF9jb25zQG1ldGEuZGF0YSRsMV9hbm5vdGF0aW9uc19yZXMwLjEsIl8iLG1lcmdlZF9hbGxfY29ucyRjb25kaXRpb24pDQoNCklkZW50cyhtZXJnZWRfYWxsX2NvbnMpIDwtICdsMV9jb25kaXRpb24nDQp1bmlxdWUoSWRlbnRzKG1lcmdlZF9hbGxfY29ucykpDQoNCkRFZ19yZXNfMC4xX1VUSSA8LSAgbGlzdCgpDQoNCmZvcihpIGluIHVuaXF1ZShtZXJnZWRfYWxsX2NvbnMkbDFfYW5ub3RhdGlvbnNfcmVzMC4xKSl7DQogIGF1eCA8LSBTZXVyYXRXcmFwcGVyczo6UnVuUHJlc3RvKG1lcmdlZF9hbGxfY29ucyxpZGVudC4xID0gcGFzdGUwKGksIl9VVEkiKSwgaWRlbnQuMiA9IHBhc3RlMChpLCJfQ29udHJvbCIpKSAgDQogIGF1eCRnZW5lIDwtIHJvd25hbWVzKGF1eCkNCiAgREVnX3Jlc18wLjFfVVRJW1twYXN0ZTAoaSwiX1VUSSIpXV0gPC0gYXV4DQp9DQoNCiNGaWx0ZXIgZm9yIGVhc2llciBhbmFseXNpcw0KZm9yKGkgaW4gbmFtZXMoREVnX3Jlc18wLjFfVVRJKSl7DQogIGF1eCA8LSBERWdfcmVzXzAuMV9VVElbW2ldXQ0KICBhdXggPC0gYXV4W2F1eCRwX3ZhbF9hZGogPCAwLjA1LF0NCiAgYXV4IDwtIGF1eFtvcmRlcihhdXgkYXZnX2xvZzJGQywgZGVjcmVhc2luZz1UKSxdDQogIERFZ19yZXNfMC4xX1VUSVtbaV1dIDwtIGF1eA0KfQ0KYGBgDQoNCmBgYHtyfQ0KDQpuYW1lcyhERWdfcmVzXzAuMV9VVEkpW25hbWVzKERFZ19yZXNfMC4xX1VUSSkgJWluJSAiSUxDMi9NeWVsb2lkX1VUSSJdIDwtICJJTEMyX1VUSSINCg0KI2V4cG9ydCBhcyBleGNlbCBmaWxlDQpyaW86OmV4cG9ydChERWdfcmVzXzAuMV9VVEksICJyZXMgMC4xX0RFIGdlbmVzLnhsc3giKQ0KDQpgYGANCg0KI3NhdmUgUkRTIGFuZCBjcmVhdGUgbG91cGUNCmBgYHtyfQ0KDQpzYXZlUkRTKG1lcmdlZF9hbGxfY29ucywgIm1lcmdlZF9vYmouUkRTIikNCg0KYGBgDQoNCmBgYHtyfQ0Kc2F2ZS5pbWFnZSgicmVzIDAuNV9QQ0EgMjVfcGVyY2VudG10MTAuUkRhdGEiKQ0KDQpgYGANCg0KDQpgYGB7cn0NCnNldHdkKCJDOi9Vc2Vycy9ldnJhamFkaC9PbmVEcml2ZSAtIEluZGlhbmEgVW5pdmVyc2l0eS9SZXNlYXJjaC9SZWN1cnJlbnQgVVRJIEdGUi9TY1JOQS9SZWNfVVRJIikNCmNyZWF0ZV9sb3VwZV9mcm9tX3NldXJhdChtZXJnZWRfYWxsX2NvbnMsIG91dHB1dF9kaXIgPSAiY2xvdXBlcy8iLCBvdXRwdXRfbmFtZSA9ICJtYW51YWwgYW5ub3RfcmVzXzAuNSIpDQpgYGANCg0KDQoNCmBgYHtyfQ0KI1NwbGl0IG9iamVjdCBieSBjb25kaXRpb24gYW5kIGFuYWx5emUgY29uZGl0aW9ucyBzZXBhcmF0ZWx5DQptZXJnZWRfc3BsaXQgPC0gU3BsaXRPYmplY3QobWVyZ2VkX2FsbF9jb25zLCBzcGxpdC5ieSA9ICJjb25kaXRpb24iKQ0KDQojZXZhbCBjb25zZXJ2ZWQgbWFya2VycyBieSBjb25kaXRpb24NCg0KbWVyZ2VkX3NwbGl0JFVUSQ0KDQptZXJnZWRfVVRJIDwtIG1lcmdlZF9zcGxpdCRVVEkNCg0KI0FkZCBtdWx0aXBsZSByZXNvbHV0aW9ucw0KbWVyZ2VkX1VUSSA8LSBSdW5QQ0EobWVyZ2VkX1VUSSwgbnBjcz0zMCkNCm1lcmdlZF9VVEkgPC0gRmluZE5laWdoYm9ycyhtZXJnZWRfVVRJLCBkaW1zID0gMToyMCwgdmVyYm9zZSA9IEYpDQptZXJnZWRfVVRJIDwtIEZpbmRDbHVzdGVycyhtZXJnZWRfVVRJLCByZXNvbHV0aW9uID0gYygwLjEsIDAuMiwgMC40LCAwLjYsIDAuOCkpDQoNCm1lcmdlZF9VVEkgPC0gUnVuVU1BUChtZXJnZWRfVVRJLCBkaW1zID0gMToyMCkNCg0KRGltUGxvdChtZXJnZWRfVVRJKQ0KDQpjbHVzdGVyLnRyZWVfVVRJIDwtIGNsdXN0cmVlKA0KICBtZXJnZWRfVVRJLCAiUk5BX3Nubl9yZXMuIikgKyBnZ3RpdGxlKCJDbHVzdGVyIHRyZWUiKQ0KDQpjbHVzdGVyLnRyZWVfVVRJDQojaGF2ZW4ndCByZW1vdmVkIGRvdWJsZXRzLCBvbmx5IHByZWxpbWluYXJ5DQoNCg0KDQpgYGANCmBgYHtyfQ0KSWRlbnRzDQpgYGANCg0KYGBge3J9DQpJZGVudHMobWVyZ2VkX1VUSSkgPC0gJ2wxX2Fubm90YXRpb25zX3JlczAuMScNCg0KREVnX1VUSV9MT1dfcmVzIDwtIGxpc3QoKQ0KZm9yKGkgaW4gdW5pcXVlKG1lcmdlZF8wLjA1JFJOQV9zbm5fcmVzLjAuMDEpKXsNCiAgYXV4IDwtIFNldXJhdFdyYXBwZXJzOjpSdW5QcmVzdG8obWVyZ2VkXzAuMDUsaWRlbnQuMSA9IHBhc3RlMChpLCJfVVRJIiksIGlkZW50LjIgPSBwYXN0ZTAoaSwiX0NvbnRyb2wiKSkgIA0KICBhdXgkZ2VuZSA8LSByb3duYW1lcyhhdXgpDQogIERFZ19VVElfTE9XX3Jlc1tbcGFzdGUwKGksIl9VVEkiKV1dIDwtIGF1eA0KfQ0KDQojRmlsdGVyIGZvciBlYXNpZXIgYW5hbHlzaXMNCmZvcihpIGluIG5hbWVzKERFZ19VVElfTE9XX3Jlcykpew0KICBhdXggPC0gREVnX1VUSV9MT1dfcmVzW1tpXV0NCiAgYXV4IDwtIGF1eFthdXgkcF92YWxfYWRqIDwgMC4wNSxdDQogIGF1eCA8LSBhdXhbb3JkZXIoYXV4JGF2Z19sb2cyRkMsIGRlY3JlYXNpbmc9VCksXQ0KICBERWdfVVRJX0xPV19yZXNbW2ldXSA8LSBhdXgNCn0NCg0KcmlvOjpleHBvcnQoREVnX1VUSV9MT1dfcmVzLCAiTE9XIHJlc29sdXRpb24gREVnZW5lc19VVEkueGxzeCIpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KSWRlbnRzKG1lcmdlZF8wLjA1KSA8LSAnUk5BX3Nubl9yZXMuMC4wMScNCkRpbVBsb3QobWVyZ2VkXzAuMDUsIHJlZHVjdGlvbiA9ICJ0c25lIikNCmBgYA0KYGBge3J9DQpEaW1QbG90KG1lcmdlZF8wLjA1LCByZWR1Y3Rpb24gPSAicGNhIiwgbGFiZWwgPSBULCByZXBlbCA9IFQpDQpgYGANCmBgYHtyfQ0KSWRlbnRzKG1lcmdlZF8wLjA1KSA8LSAnbDFfYW5ub3RhdGlvbnNfcmVzMC4xJw0KRGltUGxvdChtZXJnZWRfMC4wNSwgcmVkdWN0aW9uID0gInRzbmUiLCBsYWJlbCA9IFQpDQpgYGANCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD02fQ0KRGltUGxvdChtZXJnZWRfMC4wNSwgcmVkdWN0aW9uID0gInBjYSIsIGxhYmVsID0gVCkgKyBOb0xlZ2VuZCgpDQpgYGANCg0KYGBge3J9DQpvYmpfYW5ub3QgPC0gbWVyZ2VkX2FsbF9jb25zDQpgYGANCg0KDQpgYGB7cn0NCm9ial9hbm5vdCRncm91cGluZyA8LSBOQQ0KDQpvYmpfYW5ub3QkZ3JvdXBpbmdbb2JqX2Fubm90JFJOQV9zbm5fcmVzLjAuMSAlaW4lIGMoMSw1LDEwLDE1LDQsMyldIDwtICJMeW1waG9jeXRlIg0Kb2JqX2Fubm90JGdyb3VwaW5nW29ial9hbm5vdCRSTkFfc25uX3Jlcy4wLjEgJWluJSBjKDIsMTIsOSw2KV0gPC0gIk15ZWxvaWQiDQpvYmpfYW5ub3QkZ3JvdXBpbmdbb2JqX2Fubm90JFJOQV9zbm5fcmVzLjAuMSAlaW4lIGMoMCldIDwtICJQVCINCm9ial9hbm5vdCRncm91cGluZ1tvYmpfYW5ub3QkUk5BX3Nubl9yZXMuMC4xICVpbiUgYyg3KV0gPC0gIlBUX0VNVCINCm9ial9hbm5vdCRncm91cGluZ1tvYmpfYW5ub3QkUk5BX3Nubl9yZXMuMC4xICVpbiUgYygxNCwxMSw4LDEzKV0gPC0gIlRBTF9kaXN0YWwgdHVidWxlIg0KYGBgDQoNCmBgYHtyfQ0KSWRlbnRzKG9ial9hbm5vdCkgPC0gJ2dyb3VwaW5nJw0KdW5pcXVlKElkZW50cyhvYmpfYW5ub3QpKQ0KYGBgDQoNCmBgYHtyfQ0KY29uc2VydmVkX2dlbmVzX2xvd3Jlc19ncm91cGluZyA8LSBsaXN0KCkNCklkZW50cyhvYmpfYW5ub3QpIDwtICdncm91cGluZycNCg0KIyBMb29wIHRocm91Z2ggdGhlIGNsdXN0ZXJzIGluIHRoZSBzcGVjaWZpZWQgb3JkZXINCmNvbnNlcnZlZF9nZW5lc19sb3dyZXNfZ3JvdXBpbmcgPC0gU2V1cmF0V3JhcHBlcnM6OlJ1blByZXN0b0FsbChvYmpfYW5ub3QsDQogICAgb25seS5wb3MgPSBUUlVFLA0KICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAuNSwNCiAgICBtaW4ucGN0ID0gMC40KQ0KY29uc2VydmVkX2dlbmVzX2xvd3Jlc19ncm91cGluZyRnZW5lIDwtIHJvd25hbWVzKGNvbnNlcnZlZF9nZW5lc19sb3dyZXNfZ3JvdXBpbmcpDQoNCnJpbzo6ZXhwb3J0KGNvbnNlcnZlZF9nZW5lc19sb3dyZXNfZ3JvdXBpbmcsICJDbHVzdGVyX0dST1VQSU5HX2NvbnNlcnZlZCBnZW5lcy54bHN4IikNCiAgICANCmBgYA0KYGBge3J9DQpvYmpfYW5ub3QkZ3JvdXBfY29uZCA8LSBwYXN0ZTAob2JqX2Fubm90JGdyb3VwaW5nLCdfJyxvYmpfYW5ub3QkY29uZGl0aW9uKQ0KSWRlbnRzKG9ial9hbm5vdCkgPC0gJ2dyb3VwX2NvbmQnDQoNCkRFZ19VVElfTE9XX3Jlc19hbm5vdCA8LSBsaXN0KCkNCmZvcihpIGluIHVuaXF1ZShvYmpfYW5ub3QkZ3JvdXBpbmcpKXsNCiAgYXV4IDwtIFNldXJhdFdyYXBwZXJzOjpSdW5QcmVzdG8ob2JqX2Fubm90LGlkZW50LjEgPSBwYXN0ZTAoaSwiX1VUSSIpLCBpZGVudC4yID0gcGFzdGUwKGksIl9Db250cm9sIikpICANCiAgYXV4JGdlbmUgPC0gcm93bmFtZXMoYXV4KQ0KICBERWdfVVRJX0xPV19yZXNfYW5ub3RbW3Bhc3RlMChpLCJfVVRJIildXSA8LSBhdXgNCn0NCg0KI0ZpbHRlciBmb3IgZWFzaWVyIGFuYWx5c2lzDQpmb3IoaSBpbiBuYW1lcyhERWdfVVRJX0xPV19yZXNfYW5ub3QpKXsNCiAgYXV4IDwtIERFZ19VVElfTE9XX3Jlc19hbm5vdFtbaV1dDQogIGF1eCA8LSBhdXhbYXV4JHBfdmFsX2FkaiA8IDAuMDUsXQ0KICBhdXggPC0gYXV4W29yZGVyKGF1eCRhdmdfbG9nMkZDLCBkZWNyZWFzaW5nPVQpLF0NCiAgREVnX1VUSV9MT1dfcmVzX2Fubm90W1tpXV0gPC0gYXV4DQp9DQoNCnJpbzo6ZXhwb3J0KERFZ19VVElfTE9XX3Jlc19hbm5vdCwgIkRFZ2VuZXMgY2x1c3Rlcl9HUk9VUElOR19VVEl2Q29udHJvbC54bHN4IikNCmBgYA0KDQoNCmBgYHtyfQ0KcmlvOjpleHBvcnQoREVnX1VUSV9MT1dfcmVzLCAiTE9XIHJlc29sdXRpb24gREVnZW5lc19VVEkueGxzeCIpDQoNCg0KYGBgDQoNCg0KDQoNCg0KTW9ub2NsaW5nDQoNCmBgYHtyfQ0KSWRlbnRzKG1lcmdlZF9hbGxfY29ucykgPC0gJ1JOQV9zbm5fcmVzLjAuMScNCnB0X2NlbGxzIDwtIHN1YnNldChtZXJnZWRfYWxsX2NvbnMsIGlkZW50cyA9IGMoJzAnLCAnNycpKQ0KbGlicmFyeShtb25vY2xlMykNCmBgYA0KYGBge3J9DQoNCmNkcyA8LSBTZXVyYXRXcmFwcGVyczo6YXMuY2VsbF9kYXRhX3NldChwdF9jZWxscykNCmNkcyA8LSBjbHVzdGVyX2NlbGxzKGNkcykNCmNkcyA8LSBsZWFybl9ncmFwaChjZHMpDQpgYGANCmBgYHtyfQ0KcGxvdF9jZWxscyhjZHMsIGNvbG9yX2NlbGxzX2J5ID0gImNsdXN0ZXIiLCBsYWJlbF9jZWxsX2dyb3VwcyA9IFQpDQpgYGANCg0KYGBge3J9DQpJZGVudHMob2JqX2Fubm90KSA8LSAnbDFfYW5ub3RhdGlvbnNfcmVzMC4xJw0KdW5pcXVlKElkZW50cyhvYmpfYW5ub3QpKQ0KDQpvYmpfYW5ub3QkbDFfYW5ub3RhdGlvbnNfcmVzMC4xW29ial9hbm5vdCRsMV9hbm5vdGF0aW9uc19yZXMwLjEgJWluJSAiRmlicm9fU3Ryb21hbCJdIDwtICJQVF9FTVQiDQpvYmpfYW5ub3QkbDFfYW5ub3RhdGlvbnNfcmVzMC4xW29ial9hbm5vdCRsMV9hbm5vdGF0aW9uc19yZXMwLjEgJWluJSAiSUxDMiJdIDwtICJJTEMiDQpvYmpfYW5ub3QkbDFfYW5ub3RhdGlvbnNfcmVzMC4xW29ial9hbm5vdCRsMV9hbm5vdGF0aW9uc19yZXMwLjEgJWluJSAiUG9kb2N5dGUiXSA8LSAiVEFMX0RDVCINCm9ial9hbm5vdCRsMV9hbm5vdGF0aW9uc19yZXMwLjFbb2JqX2Fubm90JGwxX2Fubm90YXRpb25zX3JlczAuMSAlaW4lICJwREMiXSA8LSAiREMiDQoNCmBgYA0KYGBge3J9DQpjZHMgPC0gU2V1cmF0V3JhcHBlcnM6OmFzLmNlbGxfZGF0YV9zZXQob2JqX2Fubm90KQ0KY2RzIDwtIGNsdXN0ZXJfY2VsbHMoY2RzKQ0KY2RzIDwtIGxlYXJuX2dyYXBoKGNkcykNCmBgYA0KDQoNCmBgYHtyfQ0KIyBQbG90IHRvIGNob29zZSByb290IG5vZGUgdmlzdWFsbHkNCnBsb3RfY2VsbHMoY2RzLCBjb2xvcl9jZWxsc19ieSA9ICJjbHVzdGVyIiwgbGFiZWxfY2VsbF9ncm91cHMgPSBULCBsYWJlbF9yb290cyA9IFQpIA0KDQpgYGANCmBgYHtyfQ0KDQpnZXRfZWFybGllc3RfcHJpbmNpcGFsX25vZGUgPC0gZnVuY3Rpb24oY2RzKSB7DQogIHByX2dyYXBoX3Rlc3QgPC0gcHJpbmNpcGFsX2dyYXBoKGNkcylbWzFdXQ0KICBuYW1lcyh3aGljaC5taW4oaWdyYXBoOjpkZWdyZWUocHJfZ3JhcGhfdGVzdCkpKQ0KfQ0KDQojIE9uY2UgeW91IGlkZW50aWZ5IHRoZSByb290IGNlbGwvY2x1c3RlcjoNCmNkcyA8LSBvcmRlcl9jZWxscyhjZHMsIHJvb3RfcHJfbm9kZXMgPSBnZXRfZWFybGllc3RfcHJpbmNpcGFsX25vZGUoY2RzKSkNCmBgYA0KDQpgYGB7cn0NCnBsb3RfY2VsbHMoY2RzLCBjb2xvcl9jZWxsc19ieSA9ICJwc2V1ZG90aW1lIiwgbGFiZWxfZ3JvdXBzX2J5X2NsdXN0ZXIgPSBGQUxTRSkNCg0KYGBgDQpgYGB7cn0NCkRpbVBsb3Qob2JqX2Fubm90KQ0KYGBgDQoNCmBgYHtyfQ0KQmlvY01hbmFnZXI6Omluc3RhbGwoInNsaW5nc2hvdCIpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHNsaW5nc2hvdCkNCg0KSWRlbnRzKG9ial9hbm5vdCkgPC0gJ2wxX2Fubm90YXRpb25zX3JlczAuMScNCkRpbVBsb3Qob2JqX2Fubm90LCByZWR1Y3Rpb24gPSAndHNuZScpICsgRGltUGxvdChvYmpfYW5ub3QsIHJlZHVjdGlvbiA9ICdwY2EnKQ0KDQpzY2UgPC0gYXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQob2JqX2Fubm90KQ0Kc2NlIDwtIHNsaW5nc2hvdChzY2UsIGNsdXN0ZXJMYWJlbHMgPSBvYmpfYW5ub3QkbDFfYW5ub3RhdGlvbnNfcmVzMC4xLCByZWR1Y2VkRGltID0gJ1VNQVAnKQ0KYGBgDQojUHNldWRvdGltZSBhbmFseXNpcyB3aXRoIHNsaW5nc2hvdA0KYGBge3J9DQpzY2UgPC0gYXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQob2JqX2Fubm90KQ0Kc2NlIDwtIHNsaW5nc2hvdDo6c2xpbmdzaG90KHNjZSwgY2x1c3RlckxhYmVscyA9IHNjZSRsMV9hbm5vdGF0aW9uc19yZXMwLjEsIHJlZHVjZWREaW0gPSAnVU1BUCcpDQoNCmBgYA0KDQpgYGB7cn0NCiNwbG90IHBzZXVkb3RpbWUNCmxpYnJhcnkoc2NhdGVyKQ0KDQpwbG90KHJlZHVjZWREaW0oc2NlLCAiVU1BUCIpLCBjb2wgPSBzY2Ukc2xpbmdQc2V1ZG90aW1lXzEgLCBwY2ggPSAxNikNCmBgYA0KDQpgYGB7cn0NCnBsb3QocmVkdWNlZERpbShzY2UsICJUU05FIiksIGNvbCA9IHNjZSRzbGluZ1BzZXVkb3RpbWVfMSAsIHBjaCA9IDE2KQ0KYGBgDQoNCmBgYHtyfQ0KDQpwbG90KHJlZHVjZWREaW0oc2NlLCAiVU1BUCIpLCBjb2wgPSBzY2Ukc2xpbmdQc2V1ZG90aW1lXzEgLCBwY2ggPSAxNikNCg0KY2x1c3RlcnMgPC0gc2NlJGwxX2Fubm90YXRpb25zX3JlczAuMQ0KY2VudGVycyA8LSB0KHNhcHBseSh1bmlxdWUoY2x1c3RlcnMpLGZ1bmN0aW9uKGNsKXsNCiAgY29sTWVhbnMocmVkdWNlZERpbShzY2UsICJVTUFQIilbY2x1c3RlcnMgPT0gY2wsXSkNCn0pKQ0KdGV4dChjZW50ZXJzLCBsYWJlbHMgPSB1bmlxdWUoY2x1c3RlcnMpLGNvbCA9ICJibGFjayIsIGNleCA9IDAuOCkNCmBgYA0KDQpgYGB7cn0NCnNjZSA8LSBhcy5TaW5nbGVDZWxsRXhwZXJpbWVudChwdF9jZWxscykNCnNjZSA8LSBzbGluZ3Nob3Q6OnNsaW5nc2hvdChzY2UsIGNsdXN0ZXJMYWJlbHMgPSBzY2UkbDFfYW5ub3RhdGlvbnNfcmVzMC4xLCByZWR1Y2VkRGltID0gJ1VNQVAnKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChyZWR1Y2VkRGltKHNjZSwgIlVNQVAiKSwgY29sID0gc2NlJHNsaW5nUHNldWRvdGltZV8xICwgcGNoID0gMTApDQoNCmNsdXN0ZXJzIDwtIHNjZSRsMV9hbm5vdGF0aW9uc19yZXMwLjENCmNlbnRlcnMgPC0gdChzYXBwbHkodW5pcXVlKGNsdXN0ZXJzKSxmdW5jdGlvbihjbCl7DQogIGNvbE1lYW5zKHJlZHVjZWREaW0oc2NlLCAiVU1BUCIpW2NsdXN0ZXJzID09IGNsLF0pDQp9KSkNCnRleHQoY2VudGVycywgbGFiZWxzID0gdW5pcXVlKGNsdXN0ZXJzKSxjb2wgPSAiYmxhY2siLCBjZXggPSAwLjgpDQpgYGANCg0KYGBge3J9DQojRm9yIGp1c3QgUFQgY2VsbHMgYW5kIFBUX0VNVA0KDQojIEV4dHJhY3QgZGF0YSBmcm9tIFNpbmdsZUNlbGxFeHBlcmltZW50DQp1bWFwX2RmIDwtIGFzLmRhdGEuZnJhbWUocmVkdWNlZERpbShzY2UsICJVTUFQIikpDQpjb2xuYW1lcyh1bWFwX2RmKSA8LSBjKCJVTUFQXzEiLCAiVU1BUF8yIikgICMgUmVuYW1lIHRvIHJlYWRhYmxlIG5hbWVzDQp1bWFwX2RmJGNsdXN0ZXIgPC0gc2NlJGwxX2Fubm90YXRpb25zX3JlczAuMQ0KdW1hcF9kZiRwc2V1ZG90aW1lIDwtIHNjZSRzbGluZ1BzZXVkb3RpbWVfMQ0KDQoNCiMgQ2FsY3VsYXRlIGNsdXN0ZXIgY2VudHJvaWRzIGZvciBsYWJlbGluZw0KbGFiZWxfZGYgPC0gdW1hcF9kZiAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIHN1bW1hcml6ZShVTUFQXzEgPSBtZWFuKFVNQVBfMSksIFVNQVBfMiA9IG1lYW4oVU1BUF8yKSkNCg0KDQojIFBsb3Qgd2l0aCBnZ3Bsb3QyDQpnZ3Bsb3QodW1hcF9kZiwgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gcHNldWRvdGltZSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMC41KSArDQogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBsYWJlbF9kZiwgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGxhYmVsID0gY2x1c3RlciksIA0KICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHggPSAiVU1BUCAxIiwgeSA9ICJVTUFQIDIiLCB0aXRsZSA9ICJTbGluZ3Nob3QgUHNldWRvdGltZSBbUFQgQ2VsbHNdIHdpdGggQ2x1c3RlciBMYWJlbHMiKQ0KDQoNCmBgYA0KYGBge3J9DQpzY2UgPC0gYXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQob2JqX2Fubm90KQ0Kc2NlIDwtIHNsaW5nc2hvdDo6c2xpbmdzaG90KHNjZSwgY2x1c3RlckxhYmVscyA9IHNjZSRSTkFfc25uX3Jlcy4wLjQsIHJlZHVjZWREaW0gPSAnVU1BUCcpDQpgYGANCg0KYGBge3J9DQojRm9yIGp1c3QgUFQgY2VsbHMgYW5kIFBUX0VNVA0KDQojIEV4dHJhY3QgZGF0YSBmcm9tIFNpbmdsZUNlbGxFeHBlcmltZW50DQp1bWFwX2RmIDwtIGFzLmRhdGEuZnJhbWUocmVkdWNlZERpbShzY2UsICJVTUFQIikpDQpjb2xuYW1lcyh1bWFwX2RmKSA8LSBjKCJVTUFQXzEiLCAiVU1BUF8yIikgICMgUmVuYW1lIHRvIHJlYWRhYmxlIG5hbWVzDQp1bWFwX2RmJGNsdXN0ZXIgPC0gc2NlJGwxX2Fubm90YXRpb25zX3JlczAuMQ0KdW1hcF9kZiRwc2V1ZG90aW1lIDwtIHNjZSRzbGluZ1BzZXVkb3RpbWVfMQ0KDQoNCiMgQ2FsY3VsYXRlIGNsdXN0ZXIgY2VudHJvaWRzIGZvciBsYWJlbGluZw0KbGFiZWxfZGYgPC0gdW1hcF9kZiAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIHN1bW1hcml6ZShVTUFQXzEgPSBtZWFuKFVNQVBfMSksIFVNQVBfMiA9IG1lYW4oVU1BUF8yKSkNCg0KDQojIFBsb3Qgd2l0aCBnZ3Bsb3QyDQpnZ3Bsb3QodW1hcF9kZiwgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gcHNldWRvdGltZSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMC41KSArDQogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBsYWJlbF9kZiwgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGxhYmVsID0gY2x1c3RlciksIA0KICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHggPSAiVU1BUCAxIiwgeSA9ICJVTUFQIDIiLCB0aXRsZSA9ICJTbGluZ3Nob3QgUHNldWRvdGltZSBbUFQgQ2VsbHNdIHdpdGggQ2x1c3RlciBMYWJlbHMiKQ0KDQpgYGANCg0KYGBge3J9DQpJZGVudHMob2JqX2Fubm90KSA8LSAnZ3JvdXBpbmcnDQp1bmlxdWUoSWRlbnRzKG9ial9hbm5vdCkpDQoNCiNhbHJlYWR5IHN1YnNldHRlZCBQVCBhbmQgUFRfRU1UIHRvIHB0X2NlbGxzDQpteWVsb2lkX2NlbGxzIDwtIHN1YnNldChvYmpfYW5ub3QsIGlkZW50cyA9ICJNeWVsb2lkIikNCmx5bXBob19jZWxscyA8LSBzdWJzZXQob2JqX2Fubm90LCBpZGVudHMgPSAiTHltcGhvY3l0ZSIpDQpUQUxfRENUX2NlbGxzIDwtIHN1YnNldChvYmpfYW5ub3QsIGlkZW50cyA9ICJUQUxfZGlzdGFsIHR1YnVsZSIpDQoNCmBgYA0KDQojbXllbG9pZCBzbGluZ3Nob3QgVFNORQ0KDQpgYGB7cn0NCnNjZV9teWVsb2lkIDwtIGFzLlNpbmdsZUNlbGxFeHBlcmltZW50KG15ZWxvaWRfY2VsbHMpDQpzY2VfbXllbG9pZCA8LSBzbGluZ3Nob3Q6OnNsaW5nc2hvdChzY2VfbXllbG9pZCwgY2x1c3RlckxhYmVscyA9IHNjZV9teWVsb2lkJFJOQV9zbm5fcmVzLjAuNCwgcmVkdWNlZERpbSA9ICdUU05FJykNCmBgYA0KDQpgYGB7cn0NCiNGb3IganVzdCBQVCBjZWxscyBhbmQgUFRfRU1UDQoNCiMgRXh0cmFjdCBkYXRhIGZyb20gU2luZ2xlQ2VsbEV4cGVyaW1lbnQNCnVtYXBfbXllbG9pZF9kZiA8LSBhcy5kYXRhLmZyYW1lKHJlZHVjZWREaW0oc2NlX215ZWxvaWQsICJUU05FIikpDQpjb2xuYW1lcyh1bWFwX215ZWxvaWRfZGYpIDwtIGMoIlRTTkVfMSIsICJUU05FXzIiKSAgIyBSZW5hbWUgdG8gcmVhZGFibGUgbmFtZXMNCnVtYXBfbXllbG9pZF9kZiRjbHVzdGVyIDwtIHNjZV9teWVsb2lkJGwxX2Fubm90YXRpb25zX3JlczAuMQ0KdW1hcF9teWVsb2lkX2RmJHBzZXVkb3RpbWUgPC0gc2NlX215ZWxvaWQkc2xpbmdQc2V1ZG90aW1lXzENCg0KDQojIENhbGN1bGF0ZSBjbHVzdGVyIGNlbnRyb2lkcyBmb3IgbGFiZWxpbmcNCmxhYmVsX215ZWxvaWRfZGYgPC0gdW1hcF9teWVsb2lkX2RmICU+JQ0KICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgc3VtbWFyaXplKFRTTkVfMSA9IG1lYW4oVFNORV8xKSwgVFNORV8yID0gbWVhbihUU05FXzIpKQ0KDQoNCiMgUGxvdCB3aXRoIGdncGxvdDINCmdncGxvdCh1bWFwX215ZWxvaWRfZGYsIGFlcyh4ID0gVFNORV8xLCB5ID0gVFNORV8yLCBjb2xvciA9IHBzZXVkb3RpbWUpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArDQogIGdlb21fdGV4dChkYXRhID0gbGFiZWxfbXllbG9pZF9kZiwgYWVzKHggPSBUU05FXzEsIHkgPSBUU05FXzIsIGxhYmVsID0gY2x1c3RlciksIA0KICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHggPSAiVFNORTEiLCB5ID0gIlRTTkUgMiIsIHRpdGxlID0gIlNsaW5nc2hvdCBQc2V1ZG90aW1lIFtNeWVsb2lkIENlbGxzXSB3aXRoIENsdXN0ZXIgTGFiZWxzIikNCg0KYGBgDQoNCiNteWVsb2lkIHNsaW5nc2hvdCBVTUFQDQpgYGB7cn0NCnNjZV9teWVsb2lkIDwtIGFzLlNpbmdsZUNlbGxFeHBlcmltZW50KG15ZWxvaWRfY2VsbHMpDQpzY2VfbXllbG9pZCA8LSBzbGluZ3Nob3Q6OnNsaW5nc2hvdChzY2VfbXllbG9pZCwgY2x1c3RlckxhYmVscyA9IHNjZV9teWVsb2lkJFJOQV9zbm5fcmVzLjAuNCwgcmVkdWNlZERpbSA9ICdVTUFQJykNCmBgYA0KDQpgYGB7cn0NCiNGb3IganVzdCBQVCBjZWxscyBhbmQgUFRfRU1UDQoNCiMgRXh0cmFjdCBkYXRhIGZyb20gU2luZ2xlQ2VsbEV4cGVyaW1lbnQNCnVtYXBfbXllbG9pZF9kZiA8LSBhcy5kYXRhLmZyYW1lKHJlZHVjZWREaW0oc2NlX215ZWxvaWQsICJVTUFQIikpDQpjb2xuYW1lcyh1bWFwX215ZWxvaWRfZGYpIDwtIGMoIlVNQVBfMSIsICJVTUFQXzIiKSAgIyBSZW5hbWUgdG8gcmVhZGFibGUgbmFtZXMNCnVtYXBfbXllbG9pZF9kZiRjbHVzdGVyIDwtIHNjZV9teWVsb2lkJGwxX2Fubm90YXRpb25zX3JlczAuMQ0KdW1hcF9teWVsb2lkX2RmJHBzZXVkb3RpbWUgPC0gc2NlX215ZWxvaWQkc2xpbmdQc2V1ZG90aW1lXzENCg0KDQojIENhbGN1bGF0ZSBjbHVzdGVyIGNlbnRyb2lkcyBmb3IgbGFiZWxpbmcNCmxhYmVsX215ZWxvaWRfZGYgPC0gdW1hcF9teWVsb2lkX2RmICU+JQ0KICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgc3VtbWFyaXplKFVNQVBfMSA9IG1lYW4oVU1BUF8xKSwgVU1BUF8yID0gbWVhbihVTUFQXzIpKQ0KDQoNCiMgUGxvdCB3aXRoIGdncGxvdDINCmdncGxvdCh1bWFwX215ZWxvaWRfZGYsIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IHBzZXVkb3RpbWUpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArDQogIGdlb21fdGV4dChkYXRhID0gbGFiZWxfbXllbG9pZF9kZiwgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGxhYmVsID0gY2x1c3RlciksIA0KICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHggPSAiVU1BUCAxIiwgeSA9ICJVTUFQIDIiLCB0aXRsZSA9ICJTbGluZ3Nob3QgUHNldWRvdGltZSBbUFQgQ2VsbHNdIHdpdGggQ2x1c3RlciBMYWJlbHMiKQ0KDQpgYGANCg0KI2x5bXBobyBzbGluZ3Nob3QNCmBgYHtyfQ0Kc2NlX2x5bXBoIDwtIGFzLlNpbmdsZUNlbGxFeHBlcmltZW50KGx5bXBob19jZWxscykNCnNjZV9seW1waCA8LSBzbGluZ3Nob3Q6OnNsaW5nc2hvdChzY2VfbHltcGgsIGNsdXN0ZXJMYWJlbHMgPSBzY2VfbHltcGgkUk5BX3Nubl9yZXMuMC40LCByZWR1Y2VkRGltID0gJ1RTTkUnKQ0KYGBgDQoNCmBgYHtyfQ0KI0ZvciBqdXN0IFBUIGNlbGxzIGFuZCBQVF9FTVQNCg0KIyBFeHRyYWN0IGRhdGEgZnJvbSBTaW5nbGVDZWxsRXhwZXJpbWVudA0KdW1hcF9seW1waF9kZiA8LSBhcy5kYXRhLmZyYW1lKHJlZHVjZWREaW0oc2NlX2x5bXBoLCAiVFNORSIpKQ0KY29sbmFtZXModW1hcF9seW1waF9kZikgPC0gYygiVFNORV8xIiwgIlRTTkVfMiIpICAjIFJlbmFtZSB0byByZWFkYWJsZSBuYW1lcw0KdW1hcF9seW1waF9kZiRjbHVzdGVyIDwtIHNjZV9seW1waCRsMV9hbm5vdGF0aW9uc19yZXMwLjENCnVtYXBfbHltcGhfZGYkcHNldWRvdGltZSA8LSBzY2VfbHltcGgkc2xpbmdQc2V1ZG90aW1lXzENCg0KDQojIENhbGN1bGF0ZSBjbHVzdGVyIGNlbnRyb2lkcyBmb3IgbGFiZWxpbmcNCmxhYmVsX2x5bXBoX2RmIDwtIHVtYXBfbHltcGhfZGYgJT4lDQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICBzdW1tYXJpemUoVFNORV8xID0gbWVhbihUU05FXzEpLCBUU05FXzIgPSBtZWFuKFRTTkVfMikpDQoNCg0KIyBQbG90IHdpdGggZ2dwbG90Mg0KZ2dwbG90KHVtYXBfbHltcGhfZGYsIGFlcyh4ID0gVFNORV8xLCB5ID0gVFNORV8yLCBjb2xvciA9IHBzZXVkb3RpbWUpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArDQogIGdlb21fdGV4dChkYXRhID0gbGFiZWxfbHltcGhfZGYsIGFlcyh4ID0gVFNORV8xLCB5ID0gVFNORV8yLCBsYWJlbCA9IGNsdXN0ZXIpLCANCiAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDQpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh4ID0gIlRTTkUxIiwgeSA9ICJUU05FIDIiLCB0aXRsZSA9ICJTbGluZ3Nob3QgUHNldWRvdGltZSBbUFQgQ2VsbHNdIHdpdGggQ2x1c3RlciBMYWJlbHMiKQ0KDQpgYGANCiNUQUxfRENUIGNlbGwgc2xpbmdzaG90DQpgYGB7cn0NCnNjZV90dWIgPC0gYXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQoVEFMX0RDVF9jZWxscykNCnNjZV90dWIgPC0gc2xpbmdzaG90OjpzbGluZ3Nob3Qoc2NlX3R1YiwgY2x1c3RlckxhYmVscyA9IHNjZV90dWIkUk5BX3Nubl9yZXMuMC40LCByZWR1Y2VkRGltID0gJ1RTTkUnKQ0KYGBgDQoNCmBgYHtyfQ0KDQojIEV4dHJhY3QgZGF0YSBmcm9tIFNpbmdsZUNlbGxFeHBlcmltZW50DQp0dWJfZGYgPC0gYXMuZGF0YS5mcmFtZShyZWR1Y2VkRGltKHNjZV90dWIsICJUU05FIikpDQpjb2xuYW1lcyh0dWJfZGYpIDwtIGMoIlRTTkVfMSIsICJUU05FXzIiKSAgIyBSZW5hbWUgdG8gcmVhZGFibGUgbmFtZXMNCnR1Yl9kZiRjbHVzdGVyIDwtIHNjZV90dWIkbDFfYW5ub3RhdGlvbnNfcmVzMC4xDQp0dWJfZGYkcHNldWRvdGltZSA8LSBzY2VfdHViJHNsaW5nUHNldWRvdGltZV8xDQoNCg0KIyBDYWxjdWxhdGUgY2x1c3RlciBjZW50cm9pZHMgZm9yIGxhYmVsaW5nDQpsYWJlbF90dWJfZGYgPC0gdHViX2RmICU+JQ0KICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgc3VtbWFyaXplKFRTTkVfMSA9IG1lYW4oVFNORV8xKSwgVFNORV8yID0gbWVhbihUU05FXzIpKQ0KDQoNCiMgUGxvdCB3aXRoIGdncGxvdDINCmdncGxvdCh0dWJfZGYsIGFlcyh4ID0gVFNORV8xLCB5ID0gVFNORV8yLCBjb2xvciA9IHBzZXVkb3RpbWUpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArDQogIGdlb21fdGV4dChkYXRhID0gbGFiZWxfdHViX2RmLCBhZXMoeCA9IFRTTkVfMSwgeSA9IFRTTkVfMiwgbGFiZWwgPSBjbHVzdGVyKSwgDQogICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0KSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnMoeCA9ICJUU05FMSIsIHkgPSAiVFNORSAyIiwgdGl0bGUgPSAiU2xpbmdzaG90IFBzZXVkb3RpbWUgW1BUIENlbGxzXSB3aXRoIENsdXN0ZXIgTGFiZWxzIikNCg0KYGBgDQoNCmBgYHtyfQ0KSWRlbnRzKG9ial9hbm5vdCkgPC0gJ1JOQV9zbm5fcmVzLjAuMScNCkRpbVBsb3Qob2JqX2Fubm90LCByZWR1Y3Rpb24gPSAidHNuZSIsIGxhYmVsID0gVCxyZXBlbD1ULCAgbGFiZWwuc2l6ZSA9IDUpICsgZ2d0aXRsZSgiMC4xIHJlc29sdXRpb24gY2x1c3RlcmluZyIpDQpgYGANCg0KDQpgYGB7cn0NCm9ial9hbm5vdCA8LSByZWFkUkRTKCJDOi9Vc2Vycy9ldnJhamFkaC9PbmVEcml2ZSAtIEluZGlhbmEgVW5pdmVyc2l0eS9SZXNlYXJjaC9SZWN1cnJlbnQgVVRJIEdGUi9TY1JOQS9SZWNfVVRJL3JlcyAwLjVfUENBIDI1XzctMjkvYW5ub3RhdGlvbiBvYmplY3QuUkRTIikNCg0KSWRlbnRzKG9ial9hbm5vdCkgPC0gJ2wxX2Fubm90YXRpb25zX3JlczAuMScNCkRpbVBsb3Qob2JqX2Fubm90LCByZWR1Y3Rpb24gPSAidHNuZSIsIGxhYmVsID0gVCxyZXBlbD1ULCAgbGFiZWwuc2l6ZSA9IDUpDQpgYGANCmBgYHtyLCBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0zfQ0KSWRlbnRzKG9ial9hbm5vdCkgPC0gJ2dyb3VwaW5nJw0KRGltUGxvdChvYmpfYW5ub3QsIHJlZHVjdGlvbiA9ICJ0c25lIiwgbGFiZWwgPSBULCBsYWJlbC5zaXplID0gNCwgcHQuc2l6ZSA9IDAuMSkgKyBOb0xlZ2VuZCgpICsgZ2d0aXRsZSgnQ2x1c3RlcnMnKQ0KYGBgDQpgYGB7cn0NCklkZW50cyhvYmpfYW5ub3QpIDwtICdncm91cGluZycNCkRvSGVhdG1hcChvYmpfYW5ub3QsIGZlYXR1cmVzID0gYygiTnJlcCIsICJNYXAzazdjbCIsICJBbmdwdGw3IiwgIkhkYyIsICJTbGM2YTE4IiwgIlRtaWdkMSIsICJTbGMyMmExMiIsICJTbGMyMmE2IiksIGNlbGxzID0gIlBUIikNCmBgYA0KYGBge3IsIGZpZy5oZWlnaHQ9MTJ9DQpWbG5QbG90KG9ial9hbm5vdCwgZmVhdHVyZXMgPSBjKCJOcmVwIiwgIk1hcDNrN2NsIiwgIkFuZ3B0bDciLCAiSGRjIiwgIlNsYzZhMTgiLCAiVG1pZ2QxIiwgIlNsYzIyYTEyIiwgIlNsYzIyYTYiKSwgcmFzdGVyID0gRiwgc3BsaXQuYnkgPSAnY29uZGl0aW9uJykNCmBgYA0KYGBge3IsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTN9DQpGZWF0dXJlUGxvdChvYmpfYW5ub3QsIGZlYXR1cmVzID0gIk5yZXAiLCByZWR1Y3Rpb24gPSAidHNuZSIsIHNwbGl0LmJ5ID0gJ2NvbmRpdGlvbicsIHB0LnNpemUgPSAwLjgpDQpGZWF0dXJlUGxvdChvYmpfYW5ub3QsIGZlYXR1cmVzID0gIk1hcDNrN2NsIiwgcmVkdWN0aW9uID0gInRzbmUiLCBzcGxpdC5ieSA9ICdjb25kaXRpb24nLCBwdC5zaXplID0gMC44KQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00fQ0KVmxuUGxvdChvYmpfYW5ub3QsIGZlYXR1cmVzID0gIk5yZXAiLCByYXN0ZXIgPSBGLCBzcGxpdC5ieSA9ICdjb25kaXRpb24nKQ0KVmxuUGxvdChvYmpfYW5ub3QsIGZlYXR1cmVzID0gIk1hcDNrN2NsIiwgcmFzdGVyID0gRiwgc3BsaXQuYnkgPSAnY29uZGl0aW9uJykNClZsblBsb3Qob2JqX2Fubm90LCBmZWF0dXJlcyA9ICJCMm0iLCByYXN0ZXIgPSBGLCBzcGxpdC5ieSA9ICdjb25kaXRpb24nKQ0KYGBgDQpgYGB7cn0NCmZlYXRfc2NDS0RhdGxhcyA8LSBjKCJDZDc5YSIsICJDZDNkIiwgIlByZjEiLCAiQ3BhMyIsICJGb2xyMiIsICJTZW1hM2ciLCAiU29zdCIsICJQcm94MSIsICJWY2FtMSIsICJQbHZhcCIsICJSZXJnbCIsICJDb3g0bDIiLCAiRGNuIiwgIlBvc3RuIiwgIlNsYzIyYTYiLCAiUHJ1bmUyIiwgIk5vczEiLCAiVW1vZCIsICJDYWxiMSIsICJTbGM0YTEiLCAiVXBrM2IiLCAiV3QxIiwgIkdmcjNhIikNCg0KRG9IZWF0bWFwKG9ial9hbm5vdCwgZmVhdHVyZXMgPSBjKCJDcnlhYiIsICJTZW1hNWEiLCAiUGRnZnJiIiwgIkRjZGMyYSIsICJDcCIsICJOa2c3IiwgIlhjcjEiLCAiUzEwMGE5IiwgIkxycDIiLCAiU2xjMTJhMSIsICJTbGMxMmEzIikpDQpgYGANCmBgYHtyLCBmaWcuaGVpZ2h0PTIyfQ0KRmVhdHVyZVBsb3Qob2JqX2Fubm90LCBmZWF0dXJlcyA9IGMoIkNyeWFiIiwgIlNlbWE1YSIsICJQZGdmcmIiLCAiRGNkYzJhIiwgIkNwIiwgIk5rZzciLCAiWGNyMSIsICJTMTAwYTkiLCAiTHJwMiIsICJTbGMxMmExIiwgIlNsYzEyYTMiKSwgcmVkdWN0aW9uID0gInRzbmUiLCBzcGxpdC5ieSA9ICdjb25kaXRpb24nLCBwdC5zaXplID0gMC44KQ0KVmxuUGxvdChvYmpfYW5ub3QsIGZlYXR1cmVzID0gYygiQ3J5YWIiLCAiU2VtYTVhIiwgIlBkZ2ZyYiIsICJEY2RjMmEiLCAiQ3AiLCAiTmtnNyIsICJYY3IxIiwgIlMxMDBhOSIsICJMcnAyIiwgIlNsYzEyYTEiLCAiU2xjMTJhMyIpLCByYXN0ZXIgPSBGLCBzcGxpdC5ieSA9ICdjb25kaXRpb24nKQ0KYGBgDQpgYGB7ciwgZmlnLmhlaWdodD0yNX0NCg0KZmVhdF9QVGluanVyeSA8LSBjKCJTbGM1YTEyIiwgIlNsYzM0YTEiLCAiU2xjMjJhMzAiLCAiQ29sMjdhMSIsICJQbGluMiIsICJZcGVsMiIsICJCY2F0MSIsICJTbGM3YTEyIiwgIkhzcDkwYWExIiwgIlRvcDJhIiwgIktjbmlwNCIsICJWY2FtMSIsICJIYXZjcjEiKQ0KI0hlYWx0aHk6IFNsYzVhMTIsIDQ1YTEsIDIyYTMwLCA3YTEzDQojdHlwZSAxIGluanVyZWQgUzEvMjogQ29sMjdhMSwgUGxpbjINCiNUeXBlIDEgaW5qdXJlZCBTMzogUGxpbjIsIEhhdmNyMQ0KI3R5cGUyIGluanVyZWQgUzEvMjogWXBlbDINCiNUeXBlMiBpbmp1cmVkIFMzOiB5cGVsMiwgQmNhdDEsIFNsYzdhMTINCiNhY3V0ZSBpbmp1cnk6IEhzcDkwYWExDQojUmVwYWlyaW5nOiBUb3AyYQ0KI0ZhaWxlZCByZXBhaXI6IFlwZWwyLCBLY25pcDQsVmNhbTEsIEhhdmNyMQ0KDQpGZWF0dXJlUGxvdChvYmpfYW5ub3QsIGZlYXR1cmVzID0gZmVhdF9QVGluanVyeSwgcmVkdWN0aW9uID0gInRzbmUiLCBzcGxpdC5ieSA9ICdjb25kaXRpb24nLCBwdC5zaXplID0gMC44KQ0KVmxuUGxvdChvYmpfYW5ub3QsIGZlYXR1cmVzID0gZmVhdF9QVGluanVyeSwgcmFzdGVyID0gRiwgc3BsaXQuYnkgPSAnY29uZGl0aW9uJykNCg0KYGBgDQoNCg0KYGBge3J9DQojc2VlIHdoaWNoIFBUIGNsdXN0ZXJzIGFyZSB3aGVyZSBvbiB0c25lIChjbHVzdGVycyA1LDksIGFuZCAxMSBvbiByZXMwLjUpDQpJZGVudHMob2JqX2Fubm90KSA8LSAnUk5BX3Nubl9yZXMuMC41Jw0KRGltUGxvdChvYmpfYW5ub3QsIHJlZHVjdGlvbiA9ICJ0c25lIiwgbGFiZWwgPSBUKQ0KYGBgDQoNCg0KYGBge3J9DQpzY2UgPC0gYXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQocHRfY2VsbHMpDQpzY2UgPC0gc2xpbmdzaG90KHNjZSwgY2x1c3RlckxhYmVscyA9IHNjZSRsMV9jb24sIHJlZHVjZWREaW0gPSAiVFNORSIpDQpjdXJ2ZXMgPC0gc2xpbmdDdXJ2ZXMoc2NlKQ0KYGBgDQoNCmBgYHtyfQ0KDQogIA0KICBkZiA8LSBhcy5kYXRhLmZyYW1lKHJlZHVjZWREaW0oc2NlLCAiVFNORSIpKQ0KY29sbmFtZXMoZGYpIDwtIGMoIlRTTkVfMSIsICJUU05FXzIiKQ0KZGYkcHNldWRvdGltZSA8LSBzY2Ukc2xpbmdQc2V1ZG90aW1lXzENCmRmJGNsdXN0ZXIgPC0gc2NlJGwxX2Fubm90YXRpb25zX3JlczAuMSANCg0KICBwIDwtIGdncGxvdChkZiwgYWVzKHggPSBUU05FXzEsIHkgPSBUU05FXzIsIGNvbG9yID0gcHNldWRvdGltZSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMC41KSArDQogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsNCiAgdGhlbWVfbWluaW1hbCgpDQogIA0KICBmb3IgKGkgaW4gc2VxX2Fsb25nKGN1cnZlcykpIHsNCiAgY3VydmVfZGYgPC0gYXMuZGF0YS5mcmFtZShjdXJ2ZXNbW2ldXSRzKQ0KICBjb2xuYW1lcyhjdXJ2ZV9kZikgPC0gYygiVFNORV8xIiwgIlRTTkVfMiIpDQogIA0KICAjIEFkZCBhcnJvd3MgYWxvbmcgdGhlIGN1cnZlDQogIHAgPC0gcCArIGdlb21fcGF0aChkYXRhID0gY3VydmVfZGYsIGFlcyh4ID0gVFNORV8xLCB5ID0gVFNORV8yKSwNCiAgICAgICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3codHlwZSA9ICJvcGVuIiwgbGVuZ3RoID0gdW5pdCgwLjE1LCAiaW5jaGVzIikpLA0KICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC44KQ0KICB9DQogIA0KICBwDQpgYGANCiNQVCBjZWxscyBhbHNvIHdpdGggY29uZGl0aW9uIHNlcGFyYXRpb24NCmBgYHtyfQ0Kc2NlIDwtIGFzLlNpbmdsZUNlbGxFeHBlcmltZW50KHB0X2NlbGxzKQ0Kc2NlIDwtIHNsaW5nc2hvdChzY2UsIGNsdXN0ZXJMYWJlbHMgPSBzY2UkbDFfY29uZGl0aW9uLCByZWR1Y2VkRGltID0gIlRTTkUiKQ0KY3VydmVzIDwtIHNsaW5nQ3VydmVzKHNjZSkNCmBgYA0KDQpgYGB7cn0NCg0KICANCiAgZGYgPC0gYXMuZGF0YS5mcmFtZShyZWR1Y2VkRGltKHNjZSwgIlRTTkUiKSkNCmNvbG5hbWVzKGRmKSA8LSBjKCJUU05FXzEiLCAiVFNORV8yIikNCmRmJHBzZXVkb3RpbWUgPC0gc2NlJHNsaW5nUHNldWRvdGltZV8xDQpkZiRjbHVzdGVyIDwtIHNjZSRsMV9jb25kaXRpb24NCg0KICBwIDwtIGdncGxvdChkZiwgYWVzKHggPSBUU05FXzEsIHkgPSBUU05FXzIsIGNvbG9yID0gcHNldWRvdGltZSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMC41KSArDQogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsNCiAgdGhlbWVfbWluaW1hbCgpDQogIA0KICBmb3IgKGkgaW4gc2VxX2Fsb25nKGN1cnZlcykpIHsNCiAgY3VydmVfZGYgPC0gYXMuZGF0YS5mcmFtZShjdXJ2ZXNbW2ldXSRzKQ0KICBjb2xuYW1lcyhjdXJ2ZV9kZikgPC0gYygiVFNORV8xIiwgIlRTTkVfMiIpDQogIA0KICAjIEFkZCBhcnJvd3MgYWxvbmcgdGhlIGN1cnZlDQogIHAgPC0gcCArIGdlb21fcGF0aChkYXRhID0gY3VydmVfZGYsIGFlcyh4ID0gVFNORV8xLCB5ID0gVFNORV8yKSwNCiAgICAgICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3codHlwZSA9ICJvcGVuIiwgbGVuZ3RoID0gdW5pdCgwLjE1LCAiaW5jaGVzIikpLA0KICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC44KQ0KICB9DQogIA0KICBwDQpgYGANCg0KDQoNCmBgYHtyfQ0Kc2F2ZVJEUyhvYmpfYW5ub3QsICJhbm5vdGF0aW9uIG9iamVjdC5SRFMiKQ0KYGBgDQoNCg0KYGBge3J9DQp1bmlxdWUobWVyZ2VkX2FsbF9jb25zQG1ldGEuZGF0YSRsMV9hbm5vdGF0aW9uc19yZXMwLjEpDQpgYGANCg0KYGBge3J9DQpJZGVudHMobWVyZ2VkX2FsbF9jb25zKSA8LSAnbDFfYW5ub3RhdGlvbnNfcmVzMC4xJw0KYGBgDQoNCmBgYHtyfQ0KRGltUGxvdChtZXJnZWRfYWxsX2NvbnMsIHJlZHVjdGlvbiA9ICJ1bWFwIikNCmBgYA0KYGBge3J9DQptYXJrZXJzX3Rlc3QgPC0gRmluZE1hcmtlcnMobWVyZ2VkX2FsbF9jb25zLCBncm91cGluZy52YXIgPSAibDFfYW5ub3RhdGlvbnNfcmVzMC4xIiwgaWRlbnQuMSA9ICJQb2RvY3l0ZSIpDQpgYGANCg0KYGBge3J9DQptYXJrZXJzX3Rlc3QNCmBgYA0KDQpgYGB7cn0NCkRpbVBsb3QobWVyZ2VkX2FsbF9jb25zLCBsYWJlbCA9IFQsIHJlcGVsID0gVCkNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQorKysrKysrKysrKw0KYGBge3J9DQpteWVsb2lkX2RmIDwtIGFzLmRhdGEuZnJhbWUocmVkdWNlZERpbShzY2VfbXllbG9pZCwgIlRTTkUiKSkNCmNvbG5hbWVzKG15ZWxvaWRfZGYpIDwtIGMoIlRTTkVfMSIsICJUU05FXzIiKQ0KDQpteWVsb2lkX2RmJGxpbmVhZ2UgPC0gaXMubmEoc2NlX215ZWxvaWQkc2xpbmdQc2V1ZG90aW1lXzEpDQoNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChteWVsb2lkX2RmLCBhZXMoeCA9IFRTTkVfMSwgeSA9IFRTTkVfMiwgY29sb3IgPSBsaW5lYWdlKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAwLjUpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkZBTFNFIiA9ICJibHVlIiwgIlRSVUUiID0gImdyYXkiKSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIkNlbGxzIEFzc2lnbmVkIHRvIFNsaW5nc2hvdCBMaW5lYWdlIDEiLA0KICAgICAgIGNvbG9yID0gIkFzc2lnbmVkPyIpDQoNCmBgYA0KDQpgYGB7cn0NCnNjZV9hbGwgPC0gYXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQob2JqX2Fubm90KQ0Kc2NlX2FsbCA8LSBzbGluZ3Nob3Q6OnNsaW5nc2hvdChzY2VfYWxsLCBjbHVzdGVyTGFiZWxzID0gc2NlX2FsbCRSTkFfc25uX3Jlcy4wLjQsIHJlZHVjZWREaW0gPSAnVFNORScpDQpgYGANCg0KYGBge3J9DQojRm9yIGp1c3QgUFQgY2VsbHMgYW5kIFBUX0VNVA0KDQojIEV4dHJhY3QgZGF0YSBmcm9tIFNpbmdsZUNlbGxFeHBlcmltZW50DQp0c25lX2FsbF9kZiA8LSBhcy5kYXRhLmZyYW1lKHJlZHVjZWREaW0oc2NlX2FsbCwgIlRTTkUiKSkNCmNvbG5hbWVzKHRzbmVfYWxsX2RmKSA8LSBjKCJUU05FXzEiLCAiVFNORV8yIikgICMgUmVuYW1lIHRvIHJlYWRhYmxlIG5hbWVzDQp0c25lX2FsbF9kZiRjbHVzdGVyIDwtIHNjZV9hbGwkbDFfYW5ub3RhdGlvbnNfcmVzMC4xDQp0c25lX2FsbF9kZiRwc2V1ZG90aW1lIDwtIHNjZV9hbGwkc2xpbmdQc2V1ZG90aW1lXzENCg0KDQojIENhbGN1bGF0ZSBjbHVzdGVyIGNlbnRyb2lkcyBmb3IgbGFiZWxpbmcNCmxhYmVsX2FsbF90c25lX2RmIDwtIHRzbmVfYWxsX2RmICU+JQ0KICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgc3VtbWFyaXplKFRTTkVfMSA9IG1lYW4oVFNORV8xKSwgVFNORV8yID0gbWVhbihUU05FXzIpKQ0KDQoNCiMgUGxvdCB3aXRoIGdncGxvdDINCmdncGxvdCh0c25lX2FsbF9kZiwgYWVzKHggPSBUU05FXzEsIHkgPSBUU05FXzIsIGNvbG9yID0gcHNldWRvdGltZSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMC41KSArDQogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBsYWJlbF9hbGxfdHNuZV9kZiwgYWVzKHggPSBUU05FXzEsIHkgPSBUU05FXzIsIGxhYmVsID0gY2x1c3RlciksIA0KICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHggPSAiVFNORTEiLCB5ID0gIlRTTkUgMiIsIHRpdGxlID0gIlNsaW5nc2hvdCBQc2V1ZG90aW1lIFtBbGwgQ2VsbHNdIHdpdGggQ2x1c3RlciBMYWJlbHMiKQ0KDQpgYGANCmBgYHtyfQ0KdHNuZV9hbGxfZGYgPC0gYXMuZGF0YS5mcmFtZShyZWR1Y2VkRGltKHNjZV9hbGwsICJUU05FIikpDQpjb2xuYW1lcyh0c25lX2FsbF9kZikgPC0gYygiVFNORV8xIiwgIlRTTkVfMiIpDQoNCnRzbmVfYWxsX2RmJGxpbmVhZ2UgPC0gaXMubmEoc2NlX2FsbCRzbGluZ1BzZXVkb3RpbWVfMSkNCg0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KHRzbmVfYWxsX2RmLCBhZXMoeCA9IFRTTkVfMSwgeSA9IFRTTkVfMiwgY29sb3IgPSBsaW5lYWdlKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAwLjUpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkZBTFNFIiA9ICJibHVlIiwgIlRSVUUiID0gImdyYXkiKSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIkNlbGxzIEFzc2lnbmVkIHRvIFNsaW5nc2hvdCBMaW5lYWdlIDEiLA0KICAgICAgIGNvbG9yID0gIkFzc2lnbmVkPyIpDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQpgYGB7cn0NCnNjZV9teWVsb2lkJGxpbmVhZ2UgPC0gaXMubmEoc2NlX215ZWxvaWQkc2xpbmdQc2V1ZG90aW1lXzEpDQpnZ3Bsb3QoLi4uLCBjb2xvciA9IGxpbmVhZ2UpICAjIFRSVUUgPSB1bmFzc2lnbmVkID0gZ3JheQ0KDQpkZiA8LSBhcy5kYXRhLmZyYW1lKHJlZHVjZWREaW0oc2NlX215ZWxvaWQsICJUU05FIikpDQpjb2xuYW1lcyhkZikgPC0gYygiVFNORV8xIiwgIlRTTkVfMiIpDQoNCmRmJGxpbmVhZ2UgPC0gaXMubmEoc2NlX215ZWxvaWQkc2xpbmdQc2V1ZG90aW1lXzEpDQoNCg0KYGBgDQoNCg0KDQoNCg0KDQoNCmBgYHtyfQ0KcGxvdChyZWR1Y2VkRGltKHNjZSksIGNvbCA9IHNjZSRjbHVzdGVyLCB0eXBlID0gJ2xpbmVhZ2VzJykNCg0Kc2NlJGxpDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQpgYGB7cn0NCnBsb3RfY2VsbHMoY2RzLCBjb2xvcl9jZWxsc19ieSA9ICJjbHVzdGVyIikNCnBsb3RfY2VsbHMoY2RzLCBnZW5lcyA9IGMoIkNkaDIiKSwgc2hvd190cmFqZWN0b3J5X2dyYXBoID0gVFJVRSkNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQpgYGB7cn0NCisrKysrKysrKysrDQoNCmdnc2F2ZSgicmVzb2x1dGlvbiBjbHVzdGVyIHRyZWUuc3ZnIiwgcGxvdCA9IGNsdXN0ZXIudHJlZSwgd2lkdGggPSAxMikNCg0KY2x1c3Rlci50cmVlIA0KDQoNClVUSV8wLjVfbWFya2Vyc19jbHVzdF9hbm5vdCA8LSBTZXVyYXRXcmFwcGVyczo6UnVuUHJlc3RvQWxsKG1lcmdlZF8wLjVfc3BsaXRsaXN0JFVUSSkNCg0KVVRJXzAuNV9tYXJrZXJzX2xpc3QgPC0gc3BsaXQoVVRJXzAuNV9tYXJrZXJzX2NsdXN0X2Fubm90LCBVVElfMC41X21hcmtlcnNfY2x1c3RfYW5ub3RbWydjbHVzdGVyJ11dKQ0KDQpmb3IoaSBpbiBuYW1lcyhVVElfMC41X21hcmtlcnNfbGlzdCkpew0KICBhdXggPC0gVVRJXzAuNV9tYXJrZXJzX2xpc3RbW2ldXQ0KICBhdXggPC0gYXV4W2F1eCRwX3ZhbF9hZGogPCAwLjA1LF0NCiAgYXV4IDwtIGF1eFtvcmRlcihhdXgkYXZnX2xvZzJGQywgZGVjcmVhc2luZz1UKSxdDQogIFVUSV9hbGxfbWFya2Vyc19saXN0W1tpXV0gPC0gYXV4DQp9DQpgYGANCg0KI0RFIGdlbmVzIGJ5IGNvbmRpdGlvbiBhdCByZXMgMC4wMQ0KYGBge3J9DQptZXJnZWRfMC4wNSRsMF9jb25kaXRpb24gPC0gcGFzdGUwKG1lcmdlZF8wLjA1JFJOQV9zbm5fcmVzLjAuMDEsICdfJywgbWVyZ2VkXzAuMDUkY29uZGl0aW9uKQ0KSWRlbnRzKG1lcmdlZF8wLjA1KSA8LSAnbDBfY29uZGl0aW9uJw0KDQpERV8wLjA1X3Jlc29sX2NvbmRpdGlvbiA8LSBTZXVyYXRXcmFwcGVyczo6UnVuUHJlc3RvKG1lcmdlZF8wLjA1KQ0KYGBgDQoNCg0KDQoNCkFkZCBhIG5ldyBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKi4NCg0KV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuDQoNClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4NCg==