1 Load libraries

2 Load Annotated Healthy Object

reference_integrated <- readRDS("../CD4_reference_RPCA_SCT_integrated.rds")


cat("Healthy Reference Integrated CD4 cells:", ncol(reference_integrated), "\n")
Healthy Reference Integrated CD4 cells: 12034 
print(table(reference_integrated$predicted.celltype.l2, reference_integrated$dataset))
                   
                    CD4T_10x_S1 CD4T_10x_S2 CD4T_lab
  CD4 CTL                     1           0       11
  CD4 Naive                1367         381      359
  CD4 Proliferating           9           4        0
  CD4 TCM                  2010        2856     4654
  CD4 TEM                    29          51       73
  Treg                       87         137        5
print(table(reference_integrated$predicted.celltype.l3, reference_integrated$dataset))
                   
                    CD4T_10x_S1 CD4T_10x_S2 CD4T_lab
  CD4 CTL                     1           0       11
  CD4 Naive                1370         382      363
  CD4 Proliferating           9           4        0
  CD4 TCM_1                1518        1859     4181
  CD4 TCM_2                 113         148      248
  CD4 TCM_3                 370         849      226
  CD4 TEM_1                  12          26        8
  CD4 TEM_2                  19          11       13
  CD4 TEM_3                   2           7       49
  Treg Memory                58         134        3
  Treg Naive                 31           9        0
DefaultAssay(reference_integrated) <- "RNA"
reference_integrated <- JoinLayers(reference_integrated)

reference_integrated
An object of class Seurat 
57742 features across 12034 samples within 7 assays 
Active assay: RNA (36601 features, 2902 variable features)
 3 layers present: scale.data, data, counts
 6 other assays present: ADT, prediction.score.celltype.l1, prediction.score.celltype.l2, prediction.score.celltype.l3, SCT, integrated
 2 dimensional reductions calculated: pca, umap

3 Doublet detection and removal

DefaultAssay(reference_integrated) <- "RNA"

# Convert to SingleCellExperiment
sce <- as.SingleCellExperiment(reference_integrated, assay = "RNA")

# Run scDblFinder with cluster-aware mode (recommended for droplet data)[3][4]
sce <- scDblFinder(sce)

table(sce$scDblFinder.class)

singlet doublet 
  11539     495 

3.1 Doublet detection and removal

library(dplyr)
# Explore results and add to seurat object
meta_scdblfinder <- sce@colData@listData %>% as.data.frame() %>% 
  dplyr::select(starts_with('scDblFinder')) # 'scDblFinder.class')
head(meta_scdblfinder)

3.2 Bring calls back into Seurat and inspect cluster 9

# Bring doublet calls back to Seurat
reference_integrated$scDblFinder.class <- sce$scDblFinder.class[
  match(colnames(reference_integrated), colnames(sce))
]
reference_integrated$scDblFinder.score <- sce$scDblFinder.score[
  match(colnames(reference_integrated), colnames(sce))
]

# Inspect enrichment per cluster
table(reference_integrated$seurat_clusters,
      reference_integrated$scDblFinder.class)
   
    singlet doublet
  0    5325     154
  1    2907     175
  2    1314      37
  3     530      44
  4     419      29
  5     314      17
  6     307       9
  7     275      10
  8      93       2
  9      55      18
DimPlot(reference_integrated, group.by = "scDblFinder.class") +
  ggtitle("scDblFinder doublet calls on integrated UMAP")

# Doublet stats
# Check how doublets singlets differ in QC measures per sample.
VlnPlot(reference_integrated, group.by = 'dataset', split.by = "scDblFinder.class",
        features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), 
        ncol = 4, pt.size = 0) + theme(legend.position = 'right')

doublets_summary <- reference_integrated@meta.data %>% 
  group_by(dataset, scDblFinder.class) %>% 
  summarise(total_count = n(),.groups = 'drop') %>% as.data.frame() %>% ungroup() %>%
  group_by(dataset) %>%
  mutate(countT = sum(total_count)) %>%
  group_by(scDblFinder.class, .add = TRUE) %>%
  mutate(percent = paste0(round(100 * total_count/countT, 2),'%')) %>%
  dplyr::select(-countT)
doublets_summary
write.table(doublets_summary, file = "doublet_folder/scDblFinder_doublets_summary.txt", quote = FALSE, row.names = FALSE, sep = '\t')

4 Keep singlets only and recluster

# Keep singlets only and recluster
reference_singlets <- subset(reference_integrated,
                             subset = scDblFinder.class == "singlet")

reference_singlets <- FindClusters(reference_singlets, 
                                   graph.name = "integrated_snn",
                                   resolution = 0.5)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 11539
Number of edges: 398563

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8335
Number of communities: 13
Elapsed time: 1 seconds
reference_singlets <- RunUMAP(reference_singlets, dims = 1:15)
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
DimPlot(reference_singlets, group.by = "seurat_clusters") +
  ggtitle("Reclustered singlets using integrated_snn")

5 Validation plots after doublet removal

DefaultAssay(reference_singlets) <- "RNA"

p1 <- DimPlot(reference_singlets, group.by = "predicted.celltype.l2",
              label = TRUE, repel = TRUE, label.size = 3) + NoLegend()
p2 <- DimPlot(reference_singlets, group.by = "dataset")
p3 <- DimPlot(reference_singlets, group.by = "seurat_clusters", label = TRUE)
(p1 | p2) / p3

5.1 Validation plots

DimPlot(reference_singlets, group.by = "predicted.celltype.l1",
        label = TRUE, repel = TRUE, label.size = 3) + NoLegend()

DimPlot(reference_singlets, group.by = "predicted.celltype.l2",
        label = TRUE, repel = TRUE, label.size = 3) + NoLegend()

DimPlot(reference_singlets, group.by = "predicted.celltype.l3",
        label = TRUE, repel = TRUE, label.size = 3) + NoLegend()


print(table(reference_singlets$predicted.celltype.l1, reference_singlets$seurat_clusters))
       
           0    1    2    3    4    5    6    7    8    9   10   11   12
  CD4 T 2748 2178 1712 1497  860  528  524  426  316  307  278  104   55
  CD8 T    0    0    0    0    0    5    0    0    0    0    1    0    0
print(table(reference_singlets$predicted.celltype.l2, reference_singlets$seurat_clusters))
                   
                       0    1    2    3    4    5    6    7    8    9   10   11   12
  CD4 CTL              0    0    0    0    0   11    0    0    0    0    0    0    1
  CD4 Naive            0 1010  400  599    0    0    0   14    6    1   12   22    0
  CD4 Proliferating    1    0    0    0    0   11    0    0    0    0    0    0    0
  CD4 TCM           2731 1159 1305  881  858  377  523  411  308  128  264   82   52
  CD4 TEM              6    1    0    0    2  134    0    0    1    0    0    0    0
  Treg                10    8    7   17    0    0    1    1    1  178    3    0    2
print(table(reference_singlets$predicted.celltype.l3, reference_singlets$seurat_clusters))
                   
                       0    1    2    3    4    5    6    7    8    9   10   11   12
  CD4 CTL              0    0    0    0    0   11    0    0    0    0    0    0    1
  CD4 Naive            1 1013  402  599    0    0    0   15    6    1   12   23    0
  CD4 Proliferating    1    0    0    0    0   11    0    0    0    0    0    0    0
  CD4 TCM_1         2283 1151 1303  878  200   77  198  392  300   53  245   73   39
  CD4 TCM_2          258    1    1    0   13   50   52   16    0   70   10    5    6
  CD4 TCM_3          186    3    1    4  645  259  270    2    8    3    6    3    7
  CD4 TEM_1            1    0    0    0    0   41    0    0    0    0    0    0    0
  CD4 TEM_2            6    1    0    0    0   32    1    0    0    0    0    0    0
  CD4 TEM_3            0    0    0    0    2   52    1    0    1    0    0    0    0
  Treg Memory          9    5    1    1    0    0    2    1    1  166    6    0    2
  Treg Naive           3    4    4   15    0    0    0    0    0   14    0    0    0

6 Save Reference (before MapQuery)

# Save
saveRDS(reference_singlets, "../CD4_reference_RPCA_SCT_integrated_doublets_removed.rds")
cat("✅ COMPLETE:", ncol(reference_singlets), "cells integrated\n")
✅ COMPLETE: 11539 cells integrated

7 RNA marker module scores (differentiation states)


DefaultAssay(reference_singlets) <- "RNA"

marker_list <- list(
  Tnaive = c("CCR7","SELL","LEF1","TCF7","IL7R","CD27","PTPRC"),
  Tcm    = c("CCR7","SELL","CD27","IL7R","BCL2","TCF7"),
  Tem    = c("CCR6","CXCR3","GZMK","PRF1","IFNG"),
  Temra  = c("GZMB","PRF1","KLRG1","CX3CR1"),
  Treg   = c("FOXP3","IL2RA","CTLA4","IKZF2"),
  Tex    = c("PDCD1","CTLA4","LAG3","TIGIT","TOX","ENTPD1"),
  CD4CTL = c("GZMB","PRF1","NKG7","KLRG1","CX3CR1")
)

marker_list <- lapply(marker_list, function(x)
  intersect(x, rownames(reference_singlets))
)
marker_list <- marker_list[vapply(marker_list, length, 1L) > 0]

for (state in names(marker_list)) {
  reference_singlets <- AddModuleScore(
    reference_singlets,
    features = list(marker_list[[state]]),
    name     = state
  )
}

score_features <- paste0(names(marker_list), "1")

FeaturePlot(reference_singlets,
            features   = score_features,
            ncol       = 4,
            cols       = c("lightblue","red"),
            max.cutoff = "q95") +
  plot_annotation(title = "Differentiation State Module Scores")

7.1 FeaturePlots Markers based

tnaive_plot
tcm_plot

tem_plot

temra_plot

tex_plot

cd4ctl_plot

8 ADT feature plots and gating (CD4T_10x_S1 ONLY)

plot_markers_grid_ADT(tnaive_adt, "Tnaive")
plot_markers_grid_ADT(tcm_adt,    "Tcm")

plot_markers_grid_ADT(tem_adt,    "Tem")

plot_markers_grid_ADT(temra_adt,  "Temra/CD4CTL")

plot_markers_grid_ADT(treg_adt,   "Treg")

plot_markers_grid_ADT(tex_adt,    "Tex")

8.1 Visualize ADT-defined states on UMAP

DefaultAssay(reference_singlets) <- "ADT"

s1 <- subset(reference_singlets, subset = dataset == "CD4T_10x_S1")

adt_s1 <- GetAssayData(s1, assay = "ADT", layer = "data")
rownames(adt_s1) <- gsub("-TotalC","",rownames(adt_s1))

adt_z_s1 <- t(scale(t(adt_s1)))

is_hi <- function(g, zmat, z = 0.5)
  if (g %in% rownames(zmat)) zmat[g, ] > z else rep(FALSE, ncol(zmat))

CD45RA_hi <- is_hi("CD45RA", adt_z_s1)
CD45RO_hi <- is_hi("CD45RO", adt_z_s1)
CCR7_hi   <- is_hi("CD197",  adt_z_s1)
CD127_hi  <- is_hi("CD127",  adt_z_s1)
CD25_hi   <- is_hi("CD25",   adt_z_s1)
PD1_hi    <- is_hi("PD-1",   adt_z_s1)
TIGIT_hi  <- is_hi("TIGIT",  adt_z_s1)
HLADR_hi  <- is_hi("HLA-DR", adt_z_s1)

CCR7_lo  <- !CCR7_hi
CD127_lo <- !CD127_hi

state_s1 <- rep("Other", ncol(adt_s1))

treg_idx   <- CD25_hi & CD127_lo
state_s1[treg_idx] <- "Treg_ADT"

tnaive_idx <- CD45RA_hi & CCR7_hi & CD127_hi & !CD45RO_hi & !PD1_hi & !TIGIT_hi
state_s1[tnaive_idx & state_s1 == "Other"] <- "Tnaive_ADT"

tcm_idx    <- CD45RO_hi & CCR7_hi & !CD45RA_hi
state_s1[tcm_idx & state_s1 == "Other"] <- "Tcm_ADT"

tem_idx    <- CD45RO_hi & CCR7_lo & !CD45RA_hi
state_s1[tem_idx & state_s1 == "Other"] <- "Tem_ADT"

temra_idx  <- CD45RA_hi & CCR7_lo & (PD1_hi | TIGIT_hi | HLADR_hi)
state_s1[temra_idx & state_s1 == "Other"] <- "Temra_CTL_ADT"

table(state_s1)
state_s1
        Other       Tcm_ADT       Tem_ADT Temra_CTL_ADT    Tnaive_ADT      Treg_ADT 
         1722            94           672           233            98           579 
# Make sure ADT_state is character BEFORE filling
reference_singlets$ADT_state <- NA_character_

# Store on Seurat object
s1$ADT_state <- state_s1
reference_singlets$ADT_state[colnames(s1)] <- s1$ADT_state

cat(
  "ADT-based phenotyping was restricted to the CD4T_10x_S1 dataset, ",
  "which was generated with a uniform antibody panel and normalization strategy. ",
  "These ADT-derived states were used to validate and calibrate RNA-based ",
  "differentiation signatures, which were subsequently applied across all samples.\n"
)
ADT-based phenotyping was restricted to the CD4T_10x_S1 dataset,  which was generated with a uniform antibody panel and normalization strategy.  These ADT-derived states were used to validate and calibrate RNA-based  differentiation signatures, which were subsequently applied across all samples.

9 Assign per-cell RNA differentiation state

DefaultAssay(reference_singlets) <- "RNA"

state_names <- names(marker_list)
score_cols  <- paste0(state_names, "1")

score_mat <- as.data.frame(reference_singlets@meta.data[, score_cols])

state_max_idx   <- apply(score_mat, 1, which.max)
state_max_label <- state_names[state_max_idx]

top_vals    <- apply(score_mat, 1, max)
second_vals <- apply(score_mat, 1, function(x) sort(x, decreasing = TRUE))[3]
delta <- top_vals - second_vals
state_max_label[delta < 0.05] <- "Mixed"

reference_singlets$diff_state <- factor(
  state_max_label,
  levels = c("Tnaive","Tcm","Tem","Temra","Treg","Tex","CD4CTL","Mixed")
)

DimPlot(reference_singlets, group.by = "diff_state",
        label = TRUE, label.box = TRUE, repel = TRUE) +
  ggtitle("Inferred CD4 T-cell differentiation states (RNA)")

10 Visualize ADT-defined states on UMAP

DefaultAssay(reference_singlets) <- "integrated"

DimPlot(reference_singlets,
        group.by = "ADT_state",
        reduction = "umap",
        label = TRUE, label.box = TRUE, repel = TRUE) +
  ggtitle("CD4T_10x_S1 cells annotated by ADT gating")

11 Definitive macrophage contamination markers for cluster 12:

FeaturePlot(reference_singlets, 
            features = c("ASGR2", "SIRPB2", "TREM1", "CD3E", "CD4"),
            ncol = 3, split.by = "seurat_clusters")

12 Seurat DoHeatmap: cleaner and shows patterns better

# Compare cluster 12 vs all other clusters
Idents(reference_singlets) <- "seurat_clusters"

cluster12_markers <- FindMarkers(reference_singlets,
                                 ident.1 = "12",
                                 ident.2 = NULL,  # vs all others
                                 logfc.threshold = 0.25,
                                 assay = "RNA")

head(cluster12_markers, n = 20)

# Top positive markers only
cluster12_pos <- cluster12_markers %>% 
  filter(avg_log2FC > 0.5, p_val_adj < 0.05) %>% 
  arrange(desc(avg_log2FC))
head(cluster12_pos, 15)

13 Seurat DoHeatmap: cleaner and shows patterns better



# Publication violin plot
VlnPlot(reference_singlets,
        features = c("ASGR2", "AQP9", "VSIG4"),
        group.by = "seurat_clusters",
        stack = TRUE,flip = T,
        pt.size = 0.1) +
  ggtitle("Cluster 12 = Hepatic Memory CD4 (ASGR2+/AQP9+)")

14 Run this after reclustering and before RNA heatmap:

```

LS0tCnRpdGxlOiAiQ0Q0IFQtY2VsbCBSZWZlcmVuY2UgKyBBbm5vdGF0aW9uICsgRG91YmxldCIKc3VidGl0bGU6ICJEb3VibGV0IHwgQURUIHwgQW5ub3RhdGlvbiIKYXV0aG9yOiBOYXNpciBNYWhtb29kIEFiYmFzaQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICB0aGVtZTogam91cm5hbAotLS0KCgojIExvYWQgbGlicmFyaWVzCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4KQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShtb25vY2xlMykKbGlicmFyeShTZXVyYXRXcmFwcGVycykKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCmxpYnJhcnkoc2NEYmxGaW5kZXIpCmxpYnJhcnkoZHBseXIpCm9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDhlOSkKc2V0LnNlZWQoMTIzKQoKYGBgCgoKIyBMb2FkIEFubm90YXRlZCBIZWFsdGh5IE9iamVjdApgYGB7ciB9CnJlZmVyZW5jZV9pbnRlZ3JhdGVkIDwtIHJlYWRSRFMoIi4uL0NENF9yZWZlcmVuY2VfUlBDQV9TQ1RfaW50ZWdyYXRlZC5yZHMiKQoKCmNhdCgiSGVhbHRoeSBSZWZlcmVuY2UgSW50ZWdyYXRlZCBDRDQgY2VsbHM6IiwgbmNvbChyZWZlcmVuY2VfaW50ZWdyYXRlZCksICJcbiIpCnByaW50KHRhYmxlKHJlZmVyZW5jZV9pbnRlZ3JhdGVkJHByZWRpY3RlZC5jZWxsdHlwZS5sMiwgcmVmZXJlbmNlX2ludGVncmF0ZWQkZGF0YXNldCkpCnByaW50KHRhYmxlKHJlZmVyZW5jZV9pbnRlZ3JhdGVkJHByZWRpY3RlZC5jZWxsdHlwZS5sMywgcmVmZXJlbmNlX2ludGVncmF0ZWQkZGF0YXNldCkpCgpEZWZhdWx0QXNzYXkocmVmZXJlbmNlX2ludGVncmF0ZWQpIDwtICJSTkEiCnJlZmVyZW5jZV9pbnRlZ3JhdGVkIDwtIEpvaW5MYXllcnMocmVmZXJlbmNlX2ludGVncmF0ZWQpCgpyZWZlcmVuY2VfaW50ZWdyYXRlZAoKYGBgCgojIERvdWJsZXQgZGV0ZWN0aW9uIGFuZCByZW1vdmFsCmBgYHtyfQpEZWZhdWx0QXNzYXkocmVmZXJlbmNlX2ludGVncmF0ZWQpIDwtICJSTkEiCgojIENvbnZlcnQgdG8gU2luZ2xlQ2VsbEV4cGVyaW1lbnQKc2NlIDwtIGFzLlNpbmdsZUNlbGxFeHBlcmltZW50KHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBhc3NheSA9ICJSTkEiKQoKIyBSdW4gc2NEYmxGaW5kZXIgd2l0aCBjbHVzdGVyLWF3YXJlIG1vZGUgKHJlY29tbWVuZGVkIGZvciBkcm9wbGV0IGRhdGEpWzNdWzRdCnNjZSA8LSBzY0RibEZpbmRlcihzY2UpCgp0YWJsZShzY2Ukc2NEYmxGaW5kZXIuY2xhc3MpCmBgYAoKCiMjIERvdWJsZXQgZGV0ZWN0aW9uIGFuZCByZW1vdmFsCmBgYHtyIH0KbGlicmFyeShkcGx5cikKIyBFeHBsb3JlIHJlc3VsdHMgYW5kIGFkZCB0byBzZXVyYXQgb2JqZWN0Cm1ldGFfc2NkYmxmaW5kZXIgPC0gc2NlQGNvbERhdGFAbGlzdERhdGEgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgZHBseXI6OnNlbGVjdChzdGFydHNfd2l0aCgnc2NEYmxGaW5kZXInKSkgIyAnc2NEYmxGaW5kZXIuY2xhc3MnKQpoZWFkKG1ldGFfc2NkYmxmaW5kZXIpCmBgYAoKCiMjIEJyaW5nIGNhbGxzIGJhY2sgaW50byBTZXVyYXQgYW5kIGluc3BlY3QgY2x1c3RlciA5CmBgYHtyIH0KIyBCcmluZyBkb3VibGV0IGNhbGxzIGJhY2sgdG8gU2V1cmF0CnJlZmVyZW5jZV9pbnRlZ3JhdGVkJHNjRGJsRmluZGVyLmNsYXNzIDwtIHNjZSRzY0RibEZpbmRlci5jbGFzc1sKICBtYXRjaChjb2xuYW1lcyhyZWZlcmVuY2VfaW50ZWdyYXRlZCksIGNvbG5hbWVzKHNjZSkpCl0KcmVmZXJlbmNlX2ludGVncmF0ZWQkc2NEYmxGaW5kZXIuc2NvcmUgPC0gc2NlJHNjRGJsRmluZGVyLnNjb3JlWwogIG1hdGNoKGNvbG5hbWVzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkKSwgY29sbmFtZXMoc2NlKSkKXQoKIyBJbnNwZWN0IGVucmljaG1lbnQgcGVyIGNsdXN0ZXIKdGFibGUocmVmZXJlbmNlX2ludGVncmF0ZWQkc2V1cmF0X2NsdXN0ZXJzLAogICAgICByZWZlcmVuY2VfaW50ZWdyYXRlZCRzY0RibEZpbmRlci5jbGFzcykKCkRpbVBsb3QocmVmZXJlbmNlX2ludGVncmF0ZWQsIGdyb3VwLmJ5ID0gInNjRGJsRmluZGVyLmNsYXNzIikgKwogIGdndGl0bGUoInNjRGJsRmluZGVyIGRvdWJsZXQgY2FsbHMgb24gaW50ZWdyYXRlZCBVTUFQIikKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQojIERvdWJsZXQgc3RhdHMKIyBDaGVjayBob3cgZG91YmxldHMgc2luZ2xldHMgZGlmZmVyIGluIFFDIG1lYXN1cmVzIHBlciBzYW1wbGUuClZsblBsb3QocmVmZXJlbmNlX2ludGVncmF0ZWQsIGdyb3VwLmJ5ID0gJ2RhdGFzZXQnLCBzcGxpdC5ieSA9ICJzY0RibEZpbmRlci5jbGFzcyIsCiAgICAgICAgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIsICJwZXJjZW50Lm10IiksIAogICAgICAgIG5jb2wgPSA0LCBwdC5zaXplID0gMCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAncmlnaHQnKQoKYGBgCgpgYGB7ciB9CmRvdWJsZXRzX3N1bW1hcnkgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWRAbWV0YS5kYXRhICU+JSAKICBncm91cF9ieShkYXRhc2V0LCBzY0RibEZpbmRlci5jbGFzcykgJT4lIAogIHN1bW1hcmlzZSh0b3RhbF9jb3VudCA9IG4oKSwuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoZGF0YXNldCkgJT4lCiAgbXV0YXRlKGNvdW50VCA9IHN1bSh0b3RhbF9jb3VudCkpICU+JQogIGdyb3VwX2J5KHNjRGJsRmluZGVyLmNsYXNzLCAuYWRkID0gVFJVRSkgJT4lCiAgbXV0YXRlKHBlcmNlbnQgPSBwYXN0ZTAocm91bmQoMTAwICogdG90YWxfY291bnQvY291bnRULCAyKSwnJScpKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1jb3VudFQpCmRvdWJsZXRzX3N1bW1hcnkKd3JpdGUudGFibGUoZG91YmxldHNfc3VtbWFyeSwgZmlsZSA9ICJkb3VibGV0X2ZvbGRlci9zY0RibEZpbmRlcl9kb3VibGV0c19zdW1tYXJ5LnR4dCIsIHF1b3RlID0gRkFMU0UsIHJvdy5uYW1lcyA9IEZBTFNFLCBzZXAgPSAnXHQnKQpgYGAKCiMgS2VlcCBzaW5nbGV0cyBvbmx5IGFuZCByZWNsdXN0ZXIKYGBge3IgfQojIEtlZXAgc2luZ2xldHMgb25seSBhbmQgcmVjbHVzdGVyCnJlZmVyZW5jZV9zaW5nbGV0cyA8LSBzdWJzZXQocmVmZXJlbmNlX2ludGVncmF0ZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gc2NEYmxGaW5kZXIuY2xhc3MgPT0gInNpbmdsZXQiKQoKcmVmZXJlbmNlX3NpbmdsZXRzIDwtIEZpbmRDbHVzdGVycyhyZWZlcmVuY2Vfc2luZ2xldHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyYXBoLm5hbWUgPSAiaW50ZWdyYXRlZF9zbm4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb24gPSAwLjUpCgpyZWZlcmVuY2Vfc2luZ2xldHMgPC0gUnVuVU1BUChyZWZlcmVuY2Vfc2luZ2xldHMsIGRpbXMgPSAxOjE1KQoKRGltUGxvdChyZWZlcmVuY2Vfc2luZ2xldHMsIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIpICsKICBnZ3RpdGxlKCJSZWNsdXN0ZXJlZCBzaW5nbGV0cyB1c2luZyBpbnRlZ3JhdGVkX3NubiIpCmBgYAoKCiMgVmFsaWRhdGlvbiBwbG90cyBhZnRlciBkb3VibGV0IHJlbW92YWwKYGBge3IgdmFsaWRhdGUsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD0xMH0KRGVmYXVsdEFzc2F5KHJlZmVyZW5jZV9zaW5nbGV0cykgPC0gIlJOQSIKCnAxIDwtIERpbVBsb3QocmVmZXJlbmNlX3NpbmdsZXRzLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiLAogICAgICAgICAgICAgIGxhYmVsID0gVFJVRSwgcmVwZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gMykgKyBOb0xlZ2VuZCgpCnAyIDwtIERpbVBsb3QocmVmZXJlbmNlX3NpbmdsZXRzLCBncm91cC5ieSA9ICJkYXRhc2V0IikKcDMgPC0gRGltUGxvdChyZWZlcmVuY2Vfc2luZ2xldHMsIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsIGxhYmVsID0gVFJVRSkKKHAxIHwgcDIpIC8gcDMKYGBgCgojIyBWYWxpZGF0aW9uIHBsb3RzCmBgYHtyICwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTh9CkRpbVBsb3QocmVmZXJlbmNlX3NpbmdsZXRzLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDEiLAogICAgICAgIGxhYmVsID0gVFJVRSwgcmVwZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gMykgKyBOb0xlZ2VuZCgpCkRpbVBsb3QocmVmZXJlbmNlX3NpbmdsZXRzLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiLAogICAgICAgIGxhYmVsID0gVFJVRSwgcmVwZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gMykgKyBOb0xlZ2VuZCgpCkRpbVBsb3QocmVmZXJlbmNlX3NpbmdsZXRzLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDMiLAogICAgICAgIGxhYmVsID0gVFJVRSwgcmVwZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gMykgKyBOb0xlZ2VuZCgpCgpwcmludCh0YWJsZShyZWZlcmVuY2Vfc2luZ2xldHMkcHJlZGljdGVkLmNlbGx0eXBlLmwxLCByZWZlcmVuY2Vfc2luZ2xldHMkc2V1cmF0X2NsdXN0ZXJzKSkKcHJpbnQodGFibGUocmVmZXJlbmNlX3NpbmdsZXRzJHByZWRpY3RlZC5jZWxsdHlwZS5sMiwgcmVmZXJlbmNlX3NpbmdsZXRzJHNldXJhdF9jbHVzdGVycykpCnByaW50KHRhYmxlKHJlZmVyZW5jZV9zaW5nbGV0cyRwcmVkaWN0ZWQuY2VsbHR5cGUubDMsIHJlZmVyZW5jZV9zaW5nbGV0cyRzZXVyYXRfY2x1c3RlcnMpKQpgYGAKCgoKCgoKCgoKCiMgU2F2ZSBSZWZlcmVuY2UgKGJlZm9yZSBNYXBRdWVyeSkKYGBge3Igc2F2ZS1yZWZ9CiMgU2F2ZQpzYXZlUkRTKHJlZmVyZW5jZV9zaW5nbGV0cywgIi4uL0NENF9yZWZlcmVuY2VfUlBDQV9TQ1RfaW50ZWdyYXRlZF9kb3VibGV0c19yZW1vdmVkLnJkcyIpCmNhdCgi4pyFIENPTVBMRVRFOiIsIG5jb2wocmVmZXJlbmNlX3NpbmdsZXRzKSwgImNlbGxzIGludGVncmF0ZWRcbiIpCgoKYGBgCgoKCiMgUk5BIG1hcmtlciBtb2R1bGUgc2NvcmVzIChkaWZmZXJlbnRpYXRpb24gc3RhdGVzKQpgYGB7ciBtYXJrZXJzc2NvcmUsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD04fQoKRGVmYXVsdEFzc2F5KHJlZmVyZW5jZV9zaW5nbGV0cykgPC0gIlJOQSIKCm1hcmtlcl9saXN0IDwtIGxpc3QoCiAgVG5haXZlID0gYygiQ0NSNyIsIlNFTEwiLCJMRUYxIiwiVENGNyIsIklMN1IiLCJDRDI3IiwiUFRQUkMiKSwKICBUY20gICAgPSBjKCJDQ1I3IiwiU0VMTCIsIkNEMjciLCJJTDdSIiwiQkNMMiIsIlRDRjciKSwKICBUZW0gICAgPSBjKCJDQ1I2IiwiQ1hDUjMiLCJHWk1LIiwiUFJGMSIsIklGTkciKSwKICBUZW1yYSAgPSBjKCJHWk1CIiwiUFJGMSIsIktMUkcxIiwiQ1gzQ1IxIiksCiAgVHJlZyAgID0gYygiRk9YUDMiLCJJTDJSQSIsIkNUTEE0IiwiSUtaRjIiKSwKICBUZXggICAgPSBjKCJQRENEMSIsIkNUTEE0IiwiTEFHMyIsIlRJR0lUIiwiVE9YIiwiRU5UUEQxIiksCiAgQ0Q0Q1RMID0gYygiR1pNQiIsIlBSRjEiLCJOS0c3IiwiS0xSRzEiLCJDWDNDUjEiKQopCgptYXJrZXJfbGlzdCA8LSBsYXBwbHkobWFya2VyX2xpc3QsIGZ1bmN0aW9uKHgpCiAgaW50ZXJzZWN0KHgsIHJvd25hbWVzKHJlZmVyZW5jZV9zaW5nbGV0cykpCikKbWFya2VyX2xpc3QgPC0gbWFya2VyX2xpc3RbdmFwcGx5KG1hcmtlcl9saXN0LCBsZW5ndGgsIDFMKSA+IDBdCgpmb3IgKHN0YXRlIGluIG5hbWVzKG1hcmtlcl9saXN0KSkgewogIHJlZmVyZW5jZV9zaW5nbGV0cyA8LSBBZGRNb2R1bGVTY29yZSgKICAgIHJlZmVyZW5jZV9zaW5nbGV0cywKICAgIGZlYXR1cmVzID0gbGlzdChtYXJrZXJfbGlzdFtbc3RhdGVdXSksCiAgICBuYW1lICAgICA9IHN0YXRlCiAgKQp9CgpzY29yZV9mZWF0dXJlcyA8LSBwYXN0ZTAobmFtZXMobWFya2VyX2xpc3QpLCAiMSIpCgpGZWF0dXJlUGxvdChyZWZlcmVuY2Vfc2luZ2xldHMsCiAgICAgICAgICAgIGZlYXR1cmVzICAgPSBzY29yZV9mZWF0dXJlcywKICAgICAgICAgICAgbmNvbCAgICAgICA9IDQsCiAgICAgICAgICAgIGNvbHMgICAgICAgPSBjKCJsaWdodGJsdWUiLCJyZWQiKSwKICAgICAgICAgICAgbWF4LmN1dG9mZiA9ICJxOTUiKSArCiAgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gIkRpZmZlcmVudGlhdGlvbiBTdGF0ZSBNb2R1bGUgU2NvcmVzIikKCmBgYAoKCiMjIEZlYXR1cmVQbG90cyBNYXJrZXJzIGJhc2VkCmBgYHtyLGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEyfQojIC0tLS0gTG9hZCBsaWJyYXJpZXMgLS0tLQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBhdGNod29yaykKCiMgLS0tLSBEZWZpbmUgbWFya2VycyBmb3IgZGlmZmVyZW50aWF0aW9uIHN0YXRlcyAtLS0tCnRuYWl2ZV9tYXJrZXJzIDwtIGMoIkNDUjciLCJTRUxMIiwiTEVGMSIsIlRDRjciLCJJTDdSIiwiQ0QyNyIsIlBUUFJDIikKdGNtX21hcmtlcnMgICAgPC0gYygiQ0NSNyIsIlNFTEwiLCJDRDQ1Uk8iLCJJTDdSIiwiQ0QyNyIpCnRlbV9tYXJrZXJzICAgIDwtIGMoIkNDUjYiLCJDWENSMyIsIkdaTUsiLCJQUkYxIiwiSUZORyIpCnRlbXJhX21hcmtlcnMgIDwtIGMoIkdaTUIiLCJQUkYxIiwiS0xSRzEiLCJDWDNDUjEiLCJDRDQ1UkEiKQp0ZXhfbWFya2VycyAgICA8LSBjKCJQRENEMSIsIkNUTEE0IiwiTEFHMyIsIlRJR0lUIiwiVE9YIiwiRU5UUEQxIikKY2Q0Y3RsX21hcmtlcnMgPC0gYygiR1pNQiIsIlBSRjEiLCJOS0c3IiwiS0xSRzEiLCJDWDNDUjEiKQoKa2VlcCA8LSBmdW5jdGlvbih2KSB2W3YgJWluJSByb3duYW1lcyhyZWZlcmVuY2Vfc2luZ2xldHMpXQp0bmFpdmVfbWFya2VycyA8LSBrZWVwKHRuYWl2ZV9tYXJrZXJzKQp0Y21fbWFya2VycyAgICA8LSBrZWVwKHRjbV9tYXJrZXJzKQp0ZW1fbWFya2VycyAgICA8LSBrZWVwKHRlbV9tYXJrZXJzKQp0ZW1yYV9tYXJrZXJzICA8LSBrZWVwKHRlbXJhX21hcmtlcnMpCnRleF9tYXJrZXJzICAgIDwtIGtlZXAodGV4X21hcmtlcnMpCmNkNGN0bF9tYXJrZXJzIDwtIGtlZXAoY2Q0Y3RsX21hcmtlcnMpCgpwbG90X21hcmtlcnNfZ3JpZCA8LSBmdW5jdGlvbihtYXJrZXJfdmVjdG9yLCBzdGF0ZV9uYW1lKSB7CiAgcGxvdHMgPC0gbGFwcGx5KG1hcmtlcl92ZWN0b3IsIGZ1bmN0aW9uKGdlbmUpewogICAgRmVhdHVyZVBsb3QocmVmZXJlbmNlX3NpbmdsZXRzLCBmZWF0dXJlcyA9IGdlbmUsIHJlZHVjdGlvbiA9ICJ1bWFwIiwKICAgICAgICAgICAgICAgIGNvbHMgPSBjKCJsaWdodGJsdWUiLCJyZWQiKSwgbGFiZWwgPSBUUlVFKSArCiAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQogIH0pCiAgd3JhcF9wbG90cyhwbG90cykgKyBwbG90X2Fubm90YXRpb24odGl0bGUgPSBwYXN0ZShzdGF0ZV9uYW1lLCAiTWFya2VyIEV4cHJlc3Npb24iKSkKfQoKdG5haXZlX3Bsb3QgPC0gcGxvdF9tYXJrZXJzX2dyaWQodG5haXZlX21hcmtlcnMsICJUbmFpdmUiKQp0Y21fcGxvdCAgICA8LSBwbG90X21hcmtlcnNfZ3JpZCh0Y21fbWFya2VycywgIlRjbSIpCnRlbV9wbG90ICAgIDwtIHBsb3RfbWFya2Vyc19ncmlkKHRlbV9tYXJrZXJzLCAiVGVtIikKdGVtcmFfcGxvdCAgPC0gcGxvdF9tYXJrZXJzX2dyaWQodGVtcmFfbWFya2VycywgIlRlbXJhIikKdGV4X3Bsb3QgICAgPC0gcGxvdF9tYXJrZXJzX2dyaWQodGV4X21hcmtlcnMsICJUZXgiKQpjZDRjdGxfcGxvdCA8LSBwbG90X21hcmtlcnNfZ3JpZChjZDRjdGxfbWFya2VycywgIkNENCBDVEwiKQoKdG5haXZlX3Bsb3QKdGNtX3Bsb3QKdGVtX3Bsb3QKdGVtcmFfcGxvdAp0ZXhfcGxvdApjZDRjdGxfcGxvdAoKYGBgCgoKCgojIEFEVCBmZWF0dXJlIHBsb3RzIGFuZCBnYXRpbmcgKENENFRfMTB4X1MxIE9OTFkpCmBgYHtyLGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE4fQpEZWZhdWx0QXNzYXkocmVmZXJlbmNlX3NpbmdsZXRzKSA8LSAiQURUIgoKdG5haXZlX2FkdCA8LSBjKCJDRDQ1UkEtVG90YWxDIiwiQ0QxOTctVG90YWxDIiwiQ0QxMjctVG90YWxDIikKdGNtX2FkdCAgICA8LSBjKCJDRDQ1Uk8tVG90YWxDIiwiQ0QxOTctVG90YWxDIiwiQ0QxMjctVG90YWxDIikKdGVtX2FkdCAgICA8LSBjKCJDRDQ1Uk8tVG90YWxDIikKdGVtcmFfYWR0ICA8LSBjKCJDRDQ1UkEtVG90YWxDIiwiUEQtMS1Ub3RhbEMiLCJUSUdJVC1Ub3RhbEMiLCJITEEtRFItVG90YWxDIikKdHJlZ19hZHQgICA8LSBjKCJDRDI1LVRvdGFsQyIsIkNEMTI3LVRvdGFsQyIsIkNENDVSQS1Ub3RhbEMiKQp0ZXhfYWR0ICAgIDwtIGMoIlBELTEtVG90YWxDIiwiVElHSVQtVG90YWxDIiwiSExBLURSLVRvdGFsQyIpCgpwbG90X21hcmtlcnNfZ3JpZF9BRFQgPC0gZnVuY3Rpb24obWFya2VyX3ZlY3Rvciwgc3RhdGVfbmFtZSkgewogIHBsb3RzIDwtIGxhcHBseShtYXJrZXJfdmVjdG9yLCBmdW5jdGlvbihwcm90KXsKICAgIEZlYXR1cmVQbG90KHJlZmVyZW5jZV9zaW5nbGV0cywgZmVhdHVyZXMgPSBwcm90LCByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgICAgICAgICBjb2xzID0gYygibGlnaHRibHVlIiwicmVkIikpICsKICAgICAgZ2d0aXRsZShwcm90KQogIH0pCiAgd3JhcF9wbG90cyhwbG90cykgKyBwbG90X2Fubm90YXRpb24odGl0bGUgPSBwYXN0ZShzdGF0ZV9uYW1lLCAiQURUIGV4cHJlc3Npb24iKSkKfQoKcGxvdF9tYXJrZXJzX2dyaWRfQURUKHRuYWl2ZV9hZHQsICJUbmFpdmUiKQpwbG90X21hcmtlcnNfZ3JpZF9BRFQodGNtX2FkdCwgICAgIlRjbSIpCnBsb3RfbWFya2Vyc19ncmlkX0FEVCh0ZW1fYWR0LCAgICAiVGVtIikKcGxvdF9tYXJrZXJzX2dyaWRfQURUKHRlbXJhX2FkdCwgICJUZW1yYS9DRDRDVEwiKQpwbG90X21hcmtlcnNfZ3JpZF9BRFQodHJlZ19hZHQsICAgIlRyZWciKQpwbG90X21hcmtlcnNfZ3JpZF9BRFQodGV4X2FkdCwgICAgIlRleCIpCgpgYGAKIyMgVmlzdWFsaXplIEFEVC1kZWZpbmVkIHN0YXRlcyBvbiBVTUFQCmBgYHtyLGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE0fQpEZWZhdWx0QXNzYXkocmVmZXJlbmNlX3NpbmdsZXRzKSA8LSAiQURUIgoKczEgPC0gc3Vic2V0KHJlZmVyZW5jZV9zaW5nbGV0cywgc3Vic2V0ID0gZGF0YXNldCA9PSAiQ0Q0VF8xMHhfUzEiKQoKYWR0X3MxIDwtIEdldEFzc2F5RGF0YShzMSwgYXNzYXkgPSAiQURUIiwgbGF5ZXIgPSAiZGF0YSIpCnJvd25hbWVzKGFkdF9zMSkgPC0gZ3N1YigiLVRvdGFsQyIsIiIscm93bmFtZXMoYWR0X3MxKSkKCmFkdF96X3MxIDwtIHQoc2NhbGUodChhZHRfczEpKSkKCmlzX2hpIDwtIGZ1bmN0aW9uKGcsIHptYXQsIHogPSAwLjUpCiAgaWYgKGcgJWluJSByb3duYW1lcyh6bWF0KSkgem1hdFtnLCBdID4geiBlbHNlIHJlcChGQUxTRSwgbmNvbCh6bWF0KSkKCkNENDVSQV9oaSA8LSBpc19oaSgiQ0Q0NVJBIiwgYWR0X3pfczEpCkNENDVST19oaSA8LSBpc19oaSgiQ0Q0NVJPIiwgYWR0X3pfczEpCkNDUjdfaGkgICA8LSBpc19oaSgiQ0QxOTciLCAgYWR0X3pfczEpCkNEMTI3X2hpICA8LSBpc19oaSgiQ0QxMjciLCAgYWR0X3pfczEpCkNEMjVfaGkgICA8LSBpc19oaSgiQ0QyNSIsICAgYWR0X3pfczEpClBEMV9oaSAgICA8LSBpc19oaSgiUEQtMSIsICAgYWR0X3pfczEpClRJR0lUX2hpICA8LSBpc19oaSgiVElHSVQiLCAgYWR0X3pfczEpCkhMQURSX2hpICA8LSBpc19oaSgiSExBLURSIiwgYWR0X3pfczEpCgpDQ1I3X2xvICA8LSAhQ0NSN19oaQpDRDEyN19sbyA8LSAhQ0QxMjdfaGkKCnN0YXRlX3MxIDwtIHJlcCgiT3RoZXIiLCBuY29sKGFkdF9zMSkpCgp0cmVnX2lkeCAgIDwtIENEMjVfaGkgJiBDRDEyN19sbwpzdGF0ZV9zMVt0cmVnX2lkeF0gPC0gIlRyZWdfQURUIgoKdG5haXZlX2lkeCA8LSBDRDQ1UkFfaGkgJiBDQ1I3X2hpICYgQ0QxMjdfaGkgJiAhQ0Q0NVJPX2hpICYgIVBEMV9oaSAmICFUSUdJVF9oaQpzdGF0ZV9zMVt0bmFpdmVfaWR4ICYgc3RhdGVfczEgPT0gIk90aGVyIl0gPC0gIlRuYWl2ZV9BRFQiCgp0Y21faWR4ICAgIDwtIENENDVST19oaSAmIENDUjdfaGkgJiAhQ0Q0NVJBX2hpCnN0YXRlX3MxW3RjbV9pZHggJiBzdGF0ZV9zMSA9PSAiT3RoZXIiXSA8LSAiVGNtX0FEVCIKCnRlbV9pZHggICAgPC0gQ0Q0NVJPX2hpICYgQ0NSN19sbyAmICFDRDQ1UkFfaGkKc3RhdGVfczFbdGVtX2lkeCAmIHN0YXRlX3MxID09ICJPdGhlciJdIDwtICJUZW1fQURUIgoKdGVtcmFfaWR4ICA8LSBDRDQ1UkFfaGkgJiBDQ1I3X2xvICYgKFBEMV9oaSB8IFRJR0lUX2hpIHwgSExBRFJfaGkpCnN0YXRlX3MxW3RlbXJhX2lkeCAmIHN0YXRlX3MxID09ICJPdGhlciJdIDwtICJUZW1yYV9DVExfQURUIgoKdGFibGUoc3RhdGVfczEpCgojIE1ha2Ugc3VyZSBBRFRfc3RhdGUgaXMgY2hhcmFjdGVyIEJFRk9SRSBmaWxsaW5nCnJlZmVyZW5jZV9zaW5nbGV0cyRBRFRfc3RhdGUgPC0gTkFfY2hhcmFjdGVyXwoKIyBTdG9yZSBvbiBTZXVyYXQgb2JqZWN0CnMxJEFEVF9zdGF0ZSA8LSBzdGF0ZV9zMQpyZWZlcmVuY2Vfc2luZ2xldHMkQURUX3N0YXRlW2NvbG5hbWVzKHMxKV0gPC0gczEkQURUX3N0YXRlCgpjYXQoCiAgIkFEVC1iYXNlZCBwaGVub3R5cGluZyB3YXMgcmVzdHJpY3RlZCB0byB0aGUgQ0Q0VF8xMHhfUzEgZGF0YXNldCwgIiwKICAid2hpY2ggd2FzIGdlbmVyYXRlZCB3aXRoIGEgdW5pZm9ybSBhbnRpYm9keSBwYW5lbCBhbmQgbm9ybWFsaXphdGlvbiBzdHJhdGVneS4gIiwKICAiVGhlc2UgQURULWRlcml2ZWQgc3RhdGVzIHdlcmUgdXNlZCB0byB2YWxpZGF0ZSBhbmQgY2FsaWJyYXRlIFJOQS1iYXNlZCAiLAogICJkaWZmZXJlbnRpYXRpb24gc2lnbmF0dXJlcywgd2hpY2ggd2VyZSBzdWJzZXF1ZW50bHkgYXBwbGllZCBhY3Jvc3MgYWxsIHNhbXBsZXMuXG4iCikKCmBgYAojIEFzc2lnbiBwZXItY2VsbCBSTkEgZGlmZmVyZW50aWF0aW9uIHN0YXRlCmBgYHtyLGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE0fQpEZWZhdWx0QXNzYXkocmVmZXJlbmNlX3NpbmdsZXRzKSA8LSAiUk5BIgoKc3RhdGVfbmFtZXMgPC0gbmFtZXMobWFya2VyX2xpc3QpCnNjb3JlX2NvbHMgIDwtIHBhc3RlMChzdGF0ZV9uYW1lcywgIjEiKQoKc2NvcmVfbWF0IDwtIGFzLmRhdGEuZnJhbWUocmVmZXJlbmNlX3NpbmdsZXRzQG1ldGEuZGF0YVssIHNjb3JlX2NvbHNdKQoKc3RhdGVfbWF4X2lkeCAgIDwtIGFwcGx5KHNjb3JlX21hdCwgMSwgd2hpY2gubWF4KQpzdGF0ZV9tYXhfbGFiZWwgPC0gc3RhdGVfbmFtZXNbc3RhdGVfbWF4X2lkeF0KCnRvcF92YWxzICAgIDwtIGFwcGx5KHNjb3JlX21hdCwgMSwgbWF4KQpzZWNvbmRfdmFscyA8LSBhcHBseShzY29yZV9tYXQsIDEsIGZ1bmN0aW9uKHgpIHNvcnQoeCwgZGVjcmVhc2luZyA9IFRSVUUpKVszXQpkZWx0YSA8LSB0b3BfdmFscyAtIHNlY29uZF92YWxzCnN0YXRlX21heF9sYWJlbFtkZWx0YSA8IDAuMDVdIDwtICJNaXhlZCIKCnJlZmVyZW5jZV9zaW5nbGV0cyRkaWZmX3N0YXRlIDwtIGZhY3RvcigKICBzdGF0ZV9tYXhfbGFiZWwsCiAgbGV2ZWxzID0gYygiVG5haXZlIiwiVGNtIiwiVGVtIiwiVGVtcmEiLCJUcmVnIiwiVGV4IiwiQ0Q0Q1RMIiwiTWl4ZWQiKQopCgpEaW1QbG90KHJlZmVyZW5jZV9zaW5nbGV0cywgZ3JvdXAuYnkgPSAiZGlmZl9zdGF0ZSIsCiAgICAgICAgbGFiZWwgPSBUUlVFLCBsYWJlbC5ib3ggPSBUUlVFLCByZXBlbCA9IFRSVUUpICsKICBnZ3RpdGxlKCJJbmZlcnJlZCBDRDQgVC1jZWxsIGRpZmZlcmVudGlhdGlvbiBzdGF0ZXMgKFJOQSkiKQoKYGBgCgojIFZpc3VhbGl6ZSBBRFQtZGVmaW5lZCBzdGF0ZXMgb24gVU1BUApgYGB7cixmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xNH0KRGVmYXVsdEFzc2F5KHJlZmVyZW5jZV9zaW5nbGV0cykgPC0gImludGVncmF0ZWQiCgpEaW1QbG90KHJlZmVyZW5jZV9zaW5nbGV0cywKICAgICAgICBncm91cC5ieSA9ICJBRFRfc3RhdGUiLAogICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwKICAgICAgICBsYWJlbCA9IFRSVUUsIGxhYmVsLmJveCA9IFRSVUUsIHJlcGVsID0gVFJVRSkgKwogIGdndGl0bGUoIkNENFRfMTB4X1MxIGNlbGxzIGFubm90YXRlZCBieSBBRFQgZ2F0aW5nIikKYGBgCgoKCiMgRGVmaW5pdGl2ZSBtYWNyb3BoYWdlIGNvbnRhbWluYXRpb24gbWFya2VycyBmb3IgY2x1c3RlciAxMjoKYGBge3IsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0yNCB9CkZlYXR1cmVQbG90KHJlZmVyZW5jZV9zaW5nbGV0cywgCiAgICAgICAgICAgIGZlYXR1cmVzID0gYygiQVNHUjIiLCAiU0lSUEIyIiwgIlRSRU0xIiwgIkNEM0UiLCAiQ0Q0IiksCiAgICAgICAgICAgIG5jb2wgPSAzLCBzcGxpdC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiKQoKYGBgCgoKCiMgU2V1cmF0IERvSGVhdG1hcDogY2xlYW5lciBhbmQgc2hvd3MgcGF0dGVybnMgYmV0dGVyCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xNCAgfQojIENvbXBhcmUgY2x1c3RlciAxMiB2cyBhbGwgb3RoZXIgY2x1c3RlcnMKSWRlbnRzKHJlZmVyZW5jZV9zaW5nbGV0cykgPC0gInNldXJhdF9jbHVzdGVycyIKCmNsdXN0ZXIxMl9tYXJrZXJzIDwtIEZpbmRNYXJrZXJzKHJlZmVyZW5jZV9zaW5nbGV0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWRlbnQuMSA9ICIxMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkZW50LjIgPSBOVUxMLCAgIyB2cyBhbGwgb3RoZXJzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIpCgpoZWFkKGNsdXN0ZXIxMl9tYXJrZXJzLCBuID0gMjApCgojIFRvcCBwb3NpdGl2ZSBtYXJrZXJzIG9ubHkKY2x1c3RlcjEyX3BvcyA8LSBjbHVzdGVyMTJfbWFya2VycyAlPiUgCiAgZmlsdGVyKGF2Z19sb2cyRkMgPiAwLjUsIHBfdmFsX2FkaiA8IDAuMDUpICU+JSAKICBhcnJhbmdlKGRlc2MoYXZnX2xvZzJGQykpCmhlYWQoY2x1c3RlcjEyX3BvcywgMTUpCgojIFB1YmxpY2F0aW9uIHZpb2xpbiBwbG90ClZsblBsb3QocmVmZXJlbmNlX3NpbmdsZXRzLAogICAgICAgIGZlYXR1cmVzID0gYygiQVNHUjIiLCAiQVFQOSIsICJWU0lHNCIpLAogICAgICAgIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsCiAgICAgICAgc3RhY2sgPSBUUlVFLAogICAgICAgIHB0LnNpemUgPSAwKSArCiAgZ2d0aXRsZSgiQ2x1c3RlciAxMiA9IEhlcGF0aWMgTWVtb3J5IENENCAoQVNHUjIrL0FRUDkrKSIpCgpgYGAKIyBTZXVyYXQgRG9IZWF0bWFwOiBjbGVhbmVyIGFuZCBzaG93cyBwYXR0ZXJucyBiZXR0ZXIKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE0ICB9CgoKIyBQdWJsaWNhdGlvbiB2aW9saW4gcGxvdApWbG5QbG90KHJlZmVyZW5jZV9zaW5nbGV0cywKICAgICAgICBmZWF0dXJlcyA9IGMoIkFTR1IyIiwgIkFRUDkiLCAiVlNJRzQiKSwKICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgICAgIHN0YWNrID0gVFJVRSxmbGlwID0gVCwKICAgICAgICBwdC5zaXplID0gMC4xKSArCiAgZ2d0aXRsZSgiQ2x1c3RlciAxMiA9IEhlcGF0aWMgTWVtb3J5IENENCAoQVNHUjIrL0FRUDkrKSIpCgpgYGAKIyBSdW4gdGhpcyBhZnRlciByZWNsdXN0ZXJpbmcgYW5kIGJlZm9yZSBSTkEgaGVhdG1hcDoKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE0ICB9CkRlZmF1bHRBc3NheShyZWZlcmVuY2Vfc2luZ2xldHMpIDwtICJSTkEiCgpJZGVudHMocmVmZXJlbmNlX3NpbmdsZXRzKSA8LSAic2V1cmF0X2NsdXN0ZXJzIgoKY2x1c3Rlcl9tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKHJlZmVyZW5jZV9zaW5nbGV0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmx5LnBvcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dmYy50aHJlc2hvbGQgPSAwLjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLnBjdCA9IDAuMSkKCiMgVG9wIDUgbWFya2VycyBwZXIgY2x1c3Rlcgp0b3A1X21hcmtlcnMgPC0gY2x1c3Rlcl9tYXJrZXJzICU+JSAKICBncm91cF9ieShjbHVzdGVyKSAlPiUgCiAgc2xpY2VfbWF4KGF2Z19sb2cyRkMsIG4gPSA1KSAlPiUgCiAgdW5ncm91cCgpCgojIFNhdmUgZm9yIGxhdGVyCndyaXRlLnRhYmxlKGNsdXN0ZXJfbWFya2VycywgCiAgICAgICAgICAgICJjbHVzdGVyX21hcmtlcnNfZG91YmxldHNfcmVtb3ZlZC50eHQiLAogICAgICAgICAgICBxdW90ZSA9IEZBTFNFLCBzZXAgPSAiXHQiLCByb3cubmFtZXMgPSBGQUxTRSkKCnRvcDVfbWFya2VycwoKIyBTYXZlIGZvciBsYXRlcgp3cml0ZS50YWJsZSh0b3A1X21hcmtlcnMsIAogICAgICAgICAgICAiY2x1c3Rlcl90b3A1X21hcmtlcnNfZG91YmxldHNfcmVtb3ZlZC50eHQiLAogICAgICAgICAgICBxdW90ZSA9IEZBTFNFLCBzZXAgPSAiXHQiLCByb3cubmFtZXMgPSBGQUxTRSkKCmBgYAoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCmBgYAoKCgoK