Introduction
This R Markdown script outlines a comprehensive, corrected workflow
for the analysis of Antibody-Derived Tag (ADT) data from a CITE-seq
experiment, with a specific focus on T cell
immunophenotyping in your L1-L7 cell lines. The primary goal is
to use the 28 surface protein markers to accurately define T cell
subsets.
Prerequisites: The analysis assumes a Seurat object
named All_samples_Merged is loaded, containing both RNA
(SCT assay) and ADT (ADT assay) data.
# Replace with the actual path to your Seurat object
All_samples_Merged <- readRDS("../../../PHD_3rd_YEAR_Analysis/0-Seurat_RDS_OBJECT_FINAL/All_samples_Merged_with_Renamed_Clusters_final-26-10-2025.rds")
# Verify the object structure
print(All_samples_Merged)
# Set default assay to SCT for initial RNA-based context
DefaultAssay(All_samples_Merged) <- "SCT"
DimPlot(All_samples_Merged, group.by = "orig.ident", label = T)
QC & Normalization
of ADT (Corrected)
CRITICAL CORRECTION: Subset Object
for ADT-Positive Cells This step is necessary because
the ‘CD4T_10x’ sample does not have ADT data. We will create a subsetted
object for ADT-only analysis. The full object will be used for RNA/ADT
integration.
# Identify cells with ADT data (i.e., cells where the total ADT count is > 0)
# This assumes that cells without ADT data have 0 total ADT counts after re-initialization.
adt_cells <- WhichCells(All_samples_Merged, expression = nCount_ADT > 0)
# Create a subsetted object for ADT-only analysis
All_samples_Merged_ADT <- subset(All_samples_Merged, cells = adt_cells)
# Verify the subsetting
print(All_samples_Merged_ADT)
DimPlot(All_samples_Merged_ADT, group.by = "orig.ident", label = T)
Examine Raw Counts
Distribution and Protein-to-RNA Correlation
This step is crucial for initial quality assessment and identifying
potential issues like non-specific binding or technical artifacts.
Idents(All_samples_Merged) <- "orig.ident"
# **CORRECTION:** Re-initialize the ADT assay from raw counts to ensure a clean start for normalization.
# This is necessary if the ADT assay was previously incorrectly normalized.
# We assume the raw counts are still in the 'counts' slot of the 'ADT' assay.
raw_counts <- GetAssayData(All_samples_Merged, assay = "ADT", layer = "counts")
All_samples_Merged[["ADT"]] <- CreateAssayObject(counts = raw_counts)
# Set the default assay to ADT for protein-specific QC
DefaultAssay(All_samples_Merged) <- "ADT"
# Calculate the total number of ADT counts per cell
All_samples_Merged$nFeature_ADT <- colSums(All_samples_Merged@assays$ADT@counts > 0)
All_samples_Merged$nCount_ADT <- colSums(All_samples_Merged@assays$ADT@counts)
# Visualize ADT count distribution
p1 <- VlnPlot(All_samples_Merged, features = c("nFeature_ADT", "nCount_ADT"), ncol = 2, pt.size = 0.1)
print(p1)

# Identify potential outlier antibodies (e.g., highly expressed across all cells)
# Plot raw counts for all 28 proteins
adt_features <- rownames(All_samples_Merged[["ADT"]])
p2 <- VlnPlot(All_samples_Merged, features = adt_features[1:min(28, length(adt_features))], ncol = 4, pt.size = 0)
print(p2)

Apply Centered
Log-Ratio (CLR) Normalization (CRITICAL
CORRECTION)
CLR normalization is the standard method in Seurat for ADT data.
The critical correction here is the use of
margin = 2.
# **CORRECTION:** Use margin = 2 for CLR normalization.
# margin = 2 normalizes across features (proteins) for each cell (standard for compositional data).
All_samples_Merged <- NormalizeData(All_samples_Merged, assay = "ADT", normalization.method = "CLR", margin = 2)
Quality control after
normalization
# **CORRECTION:** Plot the *normalized* data (default layer for VlnPlot after NormalizeData)
# to assess the effect of CLR normalization.
adt_features <- rownames(All_samples_Merged[["ADT"]])
p3 <- VlnPlot(All_samples_Merged, features = adt_features[1:min(28, length(adt_features))], ncol = 4, pt.size = 0)
print(p3)

T Cell
Immunophenotyping and Visualization
Visualize multiple
modalities side-by-side (CORRECTION)
The previous code used the adt_ prefix which is
unnecessary and can be confusing. The standard way is to set the default
assay and use the feature name directly.
# Set default assay to ADT for protein plots
DefaultAssay(All_samples_Merged_ADT) <- "ADT"
p4 <- FeaturePlot(All_samples_Merged_ADT, "CD4", cols = c("lightgrey", "darkgreen"), reduction = "umap") + ggtitle("CD4 Protein (ADT UMAP)")
# Set default assay to SCT for RNA plots
DefaultAssay(All_samples_Merged) <- "SCT"
p5 <- FeaturePlot(All_samples_Merged, "CD4", reduction = "umap") + ggtitle("CD4 RNA (RNA UMAP)")
# place plots side-by-side
p4 | p5

# Example for PD1/PDCD1
DefaultAssay(All_samples_Merged_ADT) <- "ADT"
p6 <- FeaturePlot(All_samples_Merged_ADT, "PD1", cols = c("lightgrey", "darkgreen"), reduction = "umap") + ggtitle("PD1 Protein")
DefaultAssay(All_samples_Merged) <- "SCT"
p7 <- FeaturePlot(All_samples_Merged, "PDCD1", reduction = "umap") + ggtitle("PDCD1 RNA (SCT)")
p6 | p7

Biaxial ADT Plots for
Phenotypic Gating (T Cell Focus)
This is the core of T cell immunophenotyping. We use
FeatureScatter on the normalized ADT data to define
subsets, similar to flow cytometry.
# Set ADT as the active assay
DefaultAssay(All_samples_Merged_ADT)
[1] "ADT"
Idents(All_samples_Merged_ADT) <- "orig.ident"
DefaultAssay(All_samples_Merged_ADT) <- "ADT"
# Define gating marker pairs
gating_pairs <- list(
c("CD4", "CD19"),
c("CD45RA", "CD45RO"),
c("CD4", "CD25"),
c("PD1", "CD274"),
c("CCR7", "CD62L")
)
# Generate FeatureScatter plots
gating_plots <- list()
for (i in seq_along(gating_pairs)) {
f1 <- gating_pairs[[i]][1]
f2 <- gating_pairs[[i]][2]
# Ensure both markers exist in ADT
if (f1 %in% rownames(All_samples_Merged_ADT[["ADT"]]) &&
f2 %in% rownames(All_samples_Merged_ADT[["ADT"]])) {
gating_plots[[i]] <- FeatureScatter(
All_samples_Merged_ADT,
feature1 = f1,
feature2 = f2,
group.by = "orig.ident"
) + ggtitle(paste("T Cell Gating:", f1, "vs", f2))
}
}
library(patchwork)
wrap_plots(gating_plots)

Heatmap of Protein
Expression by Cell Line (L1-L7 Focus)
This visualization directly addresses the goal of comparing
immunophenotypes across the L1-L7 cell lines.
DefaultAssay(All_samples_Merged_ADT) <- "ADT"
# -----------------------
# 1. Extract metadata
# -----------------------
cell_line_info <- All_samples_Merged_ADT$orig.ident # L1–L7
cell_lines <- sort(unique(cell_line_info))
# -----------------------
# 2. Extract ADT data
# -----------------------
adt_matrix <- GetAssayData(All_samples_Merged_ADT, slot = "data")
adt_features <- rownames(adt_matrix) # actual protein names
# -----------------------
# 3. Compute mean protein expression per cell line
# -----------------------
mean_expression_cell_line <- sapply(cell_lines, function(cl) {
cells <- names(cell_line_info)[cell_line_info == cl]
rowMeans(adt_matrix[, cells, drop = FALSE])
})
# reorder rows to ADT feature order
mean_expression_cell_line <- mean_expression_cell_line[adt_features, ]
# -----------------------
# 4. Heatmap with labels
# -----------------------
pheatmap(
mean_expression_cell_line,
main = "Protein Expression Heatmap by Cell Line",
scale = "row",
cluster_rows = TRUE,
cluster_cols = TRUE,
show_rownames = TRUE,
show_colnames = TRUE,
fontsize_col = 9,
angle_col = 45,
labels_col = cell_lines # <- YOUR COLUMN LABELS
)

# -----------------------
# 5. NJ tree (labels = orig.ident)
# -----------------------
dist_matrix <- dist(t(mean_expression_cell_line))
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by ‘BiocGenerics’
tree <- nj(dist_matrix)
plot(
tree,
main = "Cell Line Similarity Tree (ADT Expression)",
tip.label = cell_lines # <- LABEL THE TREE
)

Marker Discovery and
Visualization by ADT Cluster
Generate
Protein-level Differential Markers
# Find markers for ADT clusters
adt_markers <- FindAllMarkers(All_samples_Merged_ADT, assay = "ADT", only.pos = TRUE, min.pct = 0.25, logfc.threshold = 0.25, group.by = "seurat_clusters")
# Display top 5 markers per cluster
top5_adt_markers <- adt_markers %>%
group_by(cluster) %>%
slice_max(n = 5, order_by = avg_log2FC)
print(top5_adt_markers)
Heatmap of Protein
Expression by ADT Cluster
DefaultAssay(All_samples_Merged_ADT) <- "ADT"
adt_feats <- unique(top5_adt_markers$gene) # remove duplicates
p12 <- DotPlot(
All_samples_Merged_ADT,
features = adt_feats,
assay = "ADT",
group.by = "seurat_clusters"
) +
ggtitle("Protein Expression by ADT Cluster")
p12

p12_2 <- DotPlot(
All_samples_Merged_ADT,
features = adt_feats,
assay = "ADT",
group.by = "orig.ident"
) +
ggtitle("Protein Expression by ADT Cluster")
p12_2

Ridgeplots: Protein
Expression across RNA Clusters
DefaultAssay(All_samples_Merged) <- "ADT"
# Ensure you only use existing features
adt_feats <- adt_features[adt_features %in% rownames(All_samples_Merged[["ADT"]])]
for (f in adt_feats) {
p <- RidgePlot(
All_samples_Merged,
features = f,
assay = "ADT",
group.by = "orig.ident"
) + ggtitle(f)
print(p)
}




























Fwatureplots: Protein
Expression across RNA Clusters
DefaultAssay(All_samples_Merged) <- "ADT"
adt_feats <- adt_features[adt_features %in% rownames(All_samples_Merged[["ADT"]])]
for (f in adt_feats) {
p <- FeaturePlot(
All_samples_Merged,
features = f,
reduction = "umap", # UMAP calculated from SCT RNA
cols = c("lightgrey", "darkred") # optional
) + ggtitle(f)
print(p)
}




























NA
NA
LS0tCnRpdGxlOiAiQ29ycmVjdGVkIEFEVCAoQ0lURS1zZXEpIEFuYWx5c2lzIFdvcmtmbG93IGZvciBUIENlbGwgSW1tdW5vcGhlbm90eXBpbmciCmF1dGhvcjogIk5BU0lSIE1BSE1PT0QgQUJCQVNJIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclQiAlZCwgJVknKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICB0aGVtZTogam91cm5hbAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDgpCiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBoZWF0bWFwKSAjIEZvciBoZWF0bWFwIHZpc3VhbGl6YXRpb24KbGlicmFyeShhcGUpICMgRm9yIHRyZWUgdmlzdWFsaXphdGlvbgpsaWJyYXJ5KGRpdHRvU2VxKSAjIEZvciBkaXR0b0hlYXRtYXAKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KVGhpcyBSIE1hcmtkb3duIHNjcmlwdCBvdXRsaW5lcyBhIGNvbXByZWhlbnNpdmUsIGNvcnJlY3RlZCB3b3JrZmxvdyBmb3IgdGhlIGFuYWx5c2lzIG9mIEFudGlib2R5LURlcml2ZWQgVGFnIChBRFQpIGRhdGEgZnJvbSBhIENJVEUtc2VxIGV4cGVyaW1lbnQsIHdpdGggYSBzcGVjaWZpYyBmb2N1cyBvbiAqKlQgY2VsbCBpbW11bm9waGVub3R5cGluZyoqIGluIHlvdXIgTDEtTDcgY2VsbCBsaW5lcy4gVGhlIHByaW1hcnkgZ29hbCBpcyB0byB1c2UgdGhlIDI4IHN1cmZhY2UgcHJvdGVpbiBtYXJrZXJzIHRvIGFjY3VyYXRlbHkgZGVmaW5lIFQgY2VsbCBzdWJzZXRzLgoKKipQcmVyZXF1aXNpdGVzOioqIFRoZSBhbmFseXNpcyBhc3N1bWVzIGEgU2V1cmF0IG9iamVjdCBuYW1lZCBgQWxsX3NhbXBsZXNfTWVyZ2VkYCBpcyBsb2FkZWQsIGNvbnRhaW5pbmcgYm90aCBSTkEgKFNDVCBhc3NheSkgYW5kIEFEVCAoQURUIGFzc2F5KSBkYXRhLgoKYGBge3IgbG9hZF9kYXRhLCBldmFsPUZBTFNFfQojIFJlcGxhY2Ugd2l0aCB0aGUgYWN0dWFsIHBhdGggdG8geW91ciBTZXVyYXQgb2JqZWN0CkFsbF9zYW1wbGVzX01lcmdlZCA8LSByZWFkUkRTKCIuLi8uLi8uLi9QSERfM3JkX1lFQVJfQW5hbHlzaXMvMC1TZXVyYXRfUkRTX09CSkVDVF9GSU5BTC9BbGxfc2FtcGxlc19NZXJnZWRfd2l0aF9SZW5hbWVkX0NsdXN0ZXJzX2ZpbmFsLTI2LTEwLTIwMjUucmRzIikgCgojIFZlcmlmeSB0aGUgb2JqZWN0IHN0cnVjdHVyZQpwcmludChBbGxfc2FtcGxlc19NZXJnZWQpCiMgU2V0IGRlZmF1bHQgYXNzYXkgdG8gU0NUIGZvciBpbml0aWFsIFJOQS1iYXNlZCBjb250ZXh0CkRlZmF1bHRBc3NheShBbGxfc2FtcGxlc19NZXJnZWQpIDwtICJTQ1QiCgpEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIsIGxhYmVsID0gVCkKYGBgCgojICBRQyAmIE5vcm1hbGl6YXRpb24gb2YgQURUIChDb3JyZWN0ZWQpCgogKio8c3BhbiBzdHlsZT0iY29sb3I6cmVkOyI+Q1JJVElDQUwgQ09SUkVDVElPTjogU3Vic2V0IE9iamVjdCBmb3IgQURULVBvc2l0aXZlIENlbGxzPC9zcGFuPioqCiBUaGlzIHN0ZXAgaXMgbmVjZXNzYXJ5IGJlY2F1c2UgdGhlICdDRDRUXzEweCcgc2FtcGxlIGRvZXMgbm90IGhhdmUgQURUIGRhdGEuCiBXZSB3aWxsIGNyZWF0ZSBhIHN1YnNldHRlZCBvYmplY3QgZm9yIEFEVC1vbmx5IGFuYWx5c2lzLgogVGhlIGZ1bGwgb2JqZWN0IHdpbGwgYmUgdXNlZCBmb3IgUk5BL0FEVCBpbnRlZ3JhdGlvbi4KCmBgYHtyIHN1YnNldF9hZHRfY2VsbHMsIGV2YWw9RkFMU0V9CiMgSWRlbnRpZnkgY2VsbHMgd2l0aCBBRFQgZGF0YSAoaS5lLiwgY2VsbHMgd2hlcmUgdGhlIHRvdGFsIEFEVCBjb3VudCBpcyA+IDApCiMgVGhpcyBhc3N1bWVzIHRoYXQgY2VsbHMgd2l0aG91dCBBRFQgZGF0YSBoYXZlIDAgdG90YWwgQURUIGNvdW50cyBhZnRlciByZS1pbml0aWFsaXphdGlvbi4KYWR0X2NlbGxzIDwtIFdoaWNoQ2VsbHMoQWxsX3NhbXBsZXNfTWVyZ2VkLCBleHByZXNzaW9uID0gbkNvdW50X0FEVCA+IDApCgojIENyZWF0ZSBhIHN1YnNldHRlZCBvYmplY3QgZm9yIEFEVC1vbmx5IGFuYWx5c2lzCkFsbF9zYW1wbGVzX01lcmdlZF9BRFQgPC0gc3Vic2V0KEFsbF9zYW1wbGVzX01lcmdlZCwgY2VsbHMgPSBhZHRfY2VsbHMpCgojIFZlcmlmeSB0aGUgc3Vic2V0dGluZwpwcmludChBbGxfc2FtcGxlc19NZXJnZWRfQURUKQpEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZF9BRFQsIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLCBsYWJlbCA9IFQpCmBgYAoKIyMgRXhhbWluZSBSYXcgQ291bnRzIERpc3RyaWJ1dGlvbiBhbmQgUHJvdGVpbi10by1STkEgQ29ycmVsYXRpb24KClRoaXMgc3RlcCBpcyBjcnVjaWFsIGZvciBpbml0aWFsIHF1YWxpdHkgYXNzZXNzbWVudCBhbmQgaWRlbnRpZnlpbmcgcG90ZW50aWFsIGlzc3VlcyBsaWtlIG5vbi1zcGVjaWZpYyBiaW5kaW5nIG9yIHRlY2huaWNhbCBhcnRpZmFjdHMuCgpgYGB7ciBhZHRfcWMxLCBmaWcuaGVpZ2h0PSA0LCBmaWcud2lkdGg9IDEyfQoKU2NpZW50aWZpYyByZXZpZXcgYnkgVGhlIEltcHVsc2lvbiBORVdNT09OIFJlc2VhcmNoIE5ldHdvcmsgKE5ldyBNb2RlbHMgaW4gT25jb2xvZ3kpCkZyaWRheSwgT2N0b2JlciAxNywgMjAyNQpQbGFjZTogYW1waGl0aGVhdGVyIG9mIHRoZSBCb3JkZWF1eCBCaW9sb2d5IEhlYWx0aCAoQkJTKSBidWlsZGluZywgQ2FycmVpcmUgQ2FtcHVzCgpJdHMgbm90IFRoZXNpcyBkcmFmdCBpdHMgTWFudXNjcmlwdCBvbiBEaXNlY3RpbmcgSGV0ZXJvZ2VuZWl0eSBhbmQgaW1tdW5lIGV2YXNpb24gTWVjaGFuaXNtcyBpbiBTZXphcnkgc3luZHJvbWUgTW9kZWxzSWRlbnRzKEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gIm9yaWcuaWRlbnQiCgojICoqQ09SUkVDVElPTjoqKiBSZS1pbml0aWFsaXplIHRoZSBBRFQgYXNzYXkgZnJvbSByYXcgY291bnRzIHRvIGVuc3VyZSBhIGNsZWFuIHN0YXJ0IGZvciBub3JtYWxpemF0aW9uLgojIFRoaXMgaXMgbmVjZXNzYXJ5IGlmIHRoZSBBRFQgYXNzYXkgd2FzIHByZXZpb3VzbHkgaW5jb3JyZWN0bHkgbm9ybWFsaXplZC4KIyBXZSBhc3N1bWUgdGhlIHJhdyBjb3VudHMgYXJlIHN0aWxsIGluIHRoZSAnY291bnRzJyBzbG90IG9mIHRoZSAnQURUJyBhc3NheS4KcmF3X2NvdW50cyA8LSBHZXRBc3NheURhdGEoQWxsX3NhbXBsZXNfTWVyZ2VkLCBhc3NheSA9ICJBRFQiLCBsYXllciA9ICJjb3VudHMiKQpBbGxfc2FtcGxlc19NZXJnZWRbWyJBRFQiXV0gPC0gQ3JlYXRlQXNzYXlPYmplY3QoY291bnRzID0gcmF3X2NvdW50cykKCiMgU2V0IHRoZSBkZWZhdWx0IGFzc2F5IHRvIEFEVCBmb3IgcHJvdGVpbi1zcGVjaWZpYyBRQwpEZWZhdWx0QXNzYXkoQWxsX3NhbXBsZXNfTWVyZ2VkKSA8LSAiQURUIgoKIyBDYWxjdWxhdGUgdGhlIHRvdGFsIG51bWJlciBvZiBBRFQgY291bnRzIHBlciBjZWxsCkFsbF9zYW1wbGVzX01lcmdlZCRuRmVhdHVyZV9BRFQgPC0gY29sU3VtcyhBbGxfc2FtcGxlc19NZXJnZWRAYXNzYXlzJEFEVEBjb3VudHMgPiAwKQpBbGxfc2FtcGxlc19NZXJnZWQkbkNvdW50X0FEVCA8LSBjb2xTdW1zKEFsbF9zYW1wbGVzX01lcmdlZEBhc3NheXMkQURUQGNvdW50cykKCiMgVmlzdWFsaXplIEFEVCBjb3VudCBkaXN0cmlidXRpb24KcDEgPC0gVmxuUGxvdChBbGxfc2FtcGxlc19NZXJnZWQsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfQURUIiwgIm5Db3VudF9BRFQiKSwgbmNvbCA9IDIsIHB0LnNpemUgPSAwLjEpCnByaW50KHAxKQpgYGAKCmBgYHtyIGFkdF9xYzIsIGZpZy5oZWlnaHQ9IDI0LCBmaWcud2lkdGg9IDMwfQojIElkZW50aWZ5IHBvdGVudGlhbCBvdXRsaWVyIGFudGlib2RpZXMgKGUuZy4sIGhpZ2hseSBleHByZXNzZWQgYWNyb3NzIGFsbCBjZWxscykKIyBQbG90IHJhdyBjb3VudHMgZm9yIGFsbCAyOCBwcm90ZWlucwphZHRfZmVhdHVyZXMgPC0gcm93bmFtZXMoQWxsX3NhbXBsZXNfTWVyZ2VkW1siQURUIl1dKQpwMiA8LSBWbG5QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgZmVhdHVyZXMgPSBhZHRfZmVhdHVyZXNbMTptaW4oMjgsIGxlbmd0aChhZHRfZmVhdHVyZXMpKV0sIG5jb2wgPSA0LCBwdC5zaXplID0gMCkKcHJpbnQocDIpCmBgYAoKIyMgQXBwbHkgQ2VudGVyZWQgTG9nLVJhdGlvIChDTFIpIE5vcm1hbGl6YXRpb24gKCoqQ1JJVElDQUwgQ09SUkVDVElPTioqKQoKQ0xSIG5vcm1hbGl6YXRpb24gaXMgdGhlIHN0YW5kYXJkIG1ldGhvZCBpbiBTZXVyYXQgZm9yIEFEVCBkYXRhLiAqKlRoZSBjcml0aWNhbCBjb3JyZWN0aW9uIGhlcmUgaXMgdGhlIHVzZSBvZiBgbWFyZ2luID0gMmAqKi4KCmBgYHtyIGFkdF9ub3JtYWxpemF0aW9uLCBldmFsPUZBTFNFfQojICoqQ09SUkVDVElPTjoqKiBVc2UgbWFyZ2luID0gMiBmb3IgQ0xSIG5vcm1hbGl6YXRpb24uIAojIG1hcmdpbiA9IDIgbm9ybWFsaXplcyBhY3Jvc3MgZmVhdHVyZXMgKHByb3RlaW5zKSBmb3IgZWFjaCBjZWxsIChzdGFuZGFyZCBmb3IgY29tcG9zaXRpb25hbCBkYXRhKS4KQWxsX3NhbXBsZXNfTWVyZ2VkIDwtIE5vcm1hbGl6ZURhdGEoQWxsX3NhbXBsZXNfTWVyZ2VkLCBhc3NheSA9ICJBRFQiLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJDTFIiLCBtYXJnaW4gPSAyKQpgYGAKCiMjIFF1YWxpdHkgY29udHJvbCBhZnRlciBub3JtYWxpemF0aW9uCgpgYGB7ciBhZHRfcWMzLCBmaWcuaGVpZ2h0PSAyNCwgZmlnLndpZHRoPSAzMH0KIyAqKkNPUlJFQ1RJT046KiogUGxvdCB0aGUgKm5vcm1hbGl6ZWQqIGRhdGEgKGRlZmF1bHQgbGF5ZXIgZm9yIFZsblBsb3QgYWZ0ZXIgTm9ybWFsaXplRGF0YSkKIyB0byBhc3Nlc3MgdGhlIGVmZmVjdCBvZiBDTFIgbm9ybWFsaXphdGlvbi4KYWR0X2ZlYXR1cmVzIDwtIHJvd25hbWVzKEFsbF9zYW1wbGVzX01lcmdlZFtbIkFEVCJdXSkKcDMgPC0gVmxuUGxvdChBbGxfc2FtcGxlc19NZXJnZWQsIGZlYXR1cmVzID0gYWR0X2ZlYXR1cmVzWzE6bWluKDI4LCBsZW5ndGgoYWR0X2ZlYXR1cmVzKSldLCBuY29sID0gNCwgcHQuc2l6ZSA9IDApCnByaW50KHAzKQpgYGAKCgoKCgoKIyBUIENlbGwgSW1tdW5vcGhlbm90eXBpbmcgYW5kIFZpc3VhbGl6YXRpb24KCiMjIFZpc3VhbGl6ZSBtdWx0aXBsZSBtb2RhbGl0aWVzIHNpZGUtYnktc2lkZSAoKipDT1JSRUNUSU9OKiopCgpUaGUgcHJldmlvdXMgY29kZSB1c2VkIHRoZSBgYWR0X2AgcHJlZml4IHdoaWNoIGlzIHVubmVjZXNzYXJ5IGFuZCBjYW4gYmUgY29uZnVzaW5nLiBUaGUgc3RhbmRhcmQgd2F5IGlzIHRvIHNldCB0aGUgZGVmYXVsdCBhc3NheSBhbmQgdXNlIHRoZSBmZWF0dXJlIG5hbWUgZGlyZWN0bHkuCgpgYGB7ciBybmFfYWR0X3NpZGVfYnlfc2lkZSwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTB9CiMgU2V0IGRlZmF1bHQgYXNzYXkgdG8gQURUIGZvciBwcm90ZWluIHBsb3RzCkRlZmF1bHRBc3NheShBbGxfc2FtcGxlc19NZXJnZWRfQURUKSA8LSAiQURUIgpwNCA8LSBGZWF0dXJlUGxvdChBbGxfc2FtcGxlc19NZXJnZWRfQURULCAiQ0Q0IiwgY29scyA9IGMoImxpZ2h0Z3JleSIsICJkYXJrZ3JlZW4iKSwgcmVkdWN0aW9uID0gInVtYXAiKSArIGdndGl0bGUoIkNENCBQcm90ZWluIChBRFQgVU1BUCkiKQoKIyBTZXQgZGVmYXVsdCBhc3NheSB0byBTQ1QgZm9yIFJOQSBwbG90cwpEZWZhdWx0QXNzYXkoQWxsX3NhbXBsZXNfTWVyZ2VkKSA8LSAiU0NUIgpwNSA8LSBGZWF0dXJlUGxvdChBbGxfc2FtcGxlc19NZXJnZWQsICJDRDQiLCByZWR1Y3Rpb24gPSAidW1hcCIpICsgZ2d0aXRsZSgiQ0Q0IFJOQSAoUk5BIFVNQVApIikKCiMgcGxhY2UgcGxvdHMgc2lkZS1ieS1zaWRlCnA0IHwgcDUKCiMgRXhhbXBsZSBmb3IgUEQxL1BEQ0QxCkRlZmF1bHRBc3NheShBbGxfc2FtcGxlc19NZXJnZWRfQURUKSA8LSAiQURUIgpwNiA8LSBGZWF0dXJlUGxvdChBbGxfc2FtcGxlc19NZXJnZWRfQURULCAiUEQxIiwgY29scyA9IGMoImxpZ2h0Z3JleSIsICJkYXJrZ3JlZW4iKSwgcmVkdWN0aW9uID0gInVtYXAiKSArIGdndGl0bGUoIlBEMSBQcm90ZWluIikKRGVmYXVsdEFzc2F5KEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gIlNDVCIKcDcgPC0gRmVhdHVyZVBsb3QoQWxsX3NhbXBsZXNfTWVyZ2VkLCAiUERDRDEiLCByZWR1Y3Rpb24gPSAidW1hcCIpICsgZ2d0aXRsZSgiUERDRDEgUk5BIChTQ1QpIikKcDYgfCBwNwpgYGAKCiMjICBCaWF4aWFsIEFEVCBQbG90cyBmb3IgUGhlbm90eXBpYyBHYXRpbmcgKFQgQ2VsbCBGb2N1cykKClRoaXMgaXMgdGhlIGNvcmUgb2YgVCBjZWxsIGltbXVub3BoZW5vdHlwaW5nLiBXZSB1c2UgYEZlYXR1cmVTY2F0dGVyYCBvbiB0aGUgbm9ybWFsaXplZCBBRFQgZGF0YSB0byBkZWZpbmUgc3Vic2V0cywgc2ltaWxhciB0byBmbG93IGN5dG9tZXRyeS4KCmBgYHtyIGFkdF9nYXRpbmdfdGNlbGwsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0yMH0KIyBTZXQgQURUIGFzIHRoZSBhY3RpdmUgYXNzYXkgCkRlZmF1bHRBc3NheShBbGxfc2FtcGxlc19NZXJnZWRfQURUKQoKSWRlbnRzKEFsbF9zYW1wbGVzX01lcmdlZF9BRFQpIDwtICJvcmlnLmlkZW50IgpEZWZhdWx0QXNzYXkoQWxsX3NhbXBsZXNfTWVyZ2VkX0FEVCkgPC0gIkFEVCIKCiMgRGVmaW5lIGdhdGluZyBtYXJrZXIgcGFpcnMKZ2F0aW5nX3BhaXJzIDwtIGxpc3QoCiAgYygiQ0Q0IiwgIkNEMTkiKSwKICBjKCJDRDQ1UkEiLCAiQ0Q0NVJPIiksCiAgYygiQ0Q0IiwgIkNEMjUiKSwKICBjKCJQRDEiLCAiQ0QyNzQiKSwKICBjKCJDQ1I3IiwgIkNENjJMIikKKQoKIyBHZW5lcmF0ZSBGZWF0dXJlU2NhdHRlciBwbG90cwpnYXRpbmdfcGxvdHMgPC0gbGlzdCgpCgpmb3IgKGkgaW4gc2VxX2Fsb25nKGdhdGluZ19wYWlycykpIHsKICBmMSA8LSBnYXRpbmdfcGFpcnNbW2ldXVsxXQogIGYyIDwtIGdhdGluZ19wYWlyc1tbaV1dWzJdCgogICMgRW5zdXJlIGJvdGggbWFya2VycyBleGlzdCBpbiBBRFQKICBpZiAoZjEgJWluJSByb3duYW1lcyhBbGxfc2FtcGxlc19NZXJnZWRfQURUW1siQURUIl1dKSAmJgogICAgICBmMiAlaW4lIHJvd25hbWVzKEFsbF9zYW1wbGVzX01lcmdlZF9BRFRbWyJBRFQiXV0pKSB7CgogICAgZ2F0aW5nX3Bsb3RzW1tpXV0gPC0gRmVhdHVyZVNjYXR0ZXIoCiAgICAgIEFsbF9zYW1wbGVzX01lcmdlZF9BRFQsCiAgICAgIGZlYXR1cmUxID0gZjEsCiAgICAgIGZlYXR1cmUyID0gZjIsCiAgICAgIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiCiAgICApICsgZ2d0aXRsZShwYXN0ZSgiVCBDZWxsIEdhdGluZzoiLCBmMSwgInZzIiwgZjIpKQogIH0KfQoKbGlicmFyeShwYXRjaHdvcmspCndyYXBfcGxvdHMoZ2F0aW5nX3Bsb3RzKQpgYGAKCiMjIEhlYXRtYXAgb2YgUHJvdGVpbiBFeHByZXNzaW9uIGJ5IENlbGwgTGluZSAoTDEtTDcgRm9jdXMpCgpUaGlzIHZpc3VhbGl6YXRpb24gZGlyZWN0bHkgYWRkcmVzc2VzIHRoZSBnb2FsIG9mIGNvbXBhcmluZyBpbW11bm9waGVub3R5cGVzIGFjcm9zcyB0aGUgTDEtTDcgY2VsbCBsaW5lcy4KCmBgYHtyIGFkdF9oZWF0bWFwX2NlbGxsaW5lLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMn0KRGVmYXVsdEFzc2F5KEFsbF9zYW1wbGVzX01lcmdlZF9BRFQpIDwtICJBRFQiCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgMS4gRXh0cmFjdCBtZXRhZGF0YQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmNlbGxfbGluZV9pbmZvIDwtIEFsbF9zYW1wbGVzX01lcmdlZF9BRFQkb3JpZy5pZGVudCAgICAjIEwx4oCTTDcKY2VsbF9saW5lcyA8LSBzb3J0KHVuaXF1ZShjZWxsX2xpbmVfaW5mbykpCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgMi4gRXh0cmFjdCBBRFQgZGF0YQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmFkdF9tYXRyaXggPC0gR2V0QXNzYXlEYXRhKEFsbF9zYW1wbGVzX01lcmdlZF9BRFQsIHNsb3QgPSAiZGF0YSIpICAKYWR0X2ZlYXR1cmVzIDwtIHJvd25hbWVzKGFkdF9tYXRyaXgpICAgICAjIGFjdHVhbCBwcm90ZWluIG5hbWVzCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgMy4gQ29tcHV0ZSBtZWFuIHByb3RlaW4gZXhwcmVzc2lvbiBwZXIgY2VsbCBsaW5lCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KbWVhbl9leHByZXNzaW9uX2NlbGxfbGluZSA8LSBzYXBwbHkoY2VsbF9saW5lcywgZnVuY3Rpb24oY2wpIHsKICBjZWxscyA8LSBuYW1lcyhjZWxsX2xpbmVfaW5mbylbY2VsbF9saW5lX2luZm8gPT0gY2xdCiAgcm93TWVhbnMoYWR0X21hdHJpeFssIGNlbGxzLCBkcm9wID0gRkFMU0VdKQp9KQoKIyByZW9yZGVyIHJvd3MgdG8gQURUIGZlYXR1cmUgb3JkZXIKbWVhbl9leHByZXNzaW9uX2NlbGxfbGluZSA8LSBtZWFuX2V4cHJlc3Npb25fY2VsbF9saW5lW2FkdF9mZWF0dXJlcywgXQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDQuIEhlYXRtYXAgd2l0aCBsYWJlbHMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpwaGVhdG1hcCgKICBtZWFuX2V4cHJlc3Npb25fY2VsbF9saW5lLAogIG1haW4gPSAiUHJvdGVpbiBFeHByZXNzaW9uIEhlYXRtYXAgYnkgQ2VsbCBMaW5lIiwKICBzY2FsZSA9ICJyb3ciLAogIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgY2x1c3Rlcl9jb2xzID0gVFJVRSwKICBzaG93X3Jvd25hbWVzID0gVFJVRSwKICBzaG93X2NvbG5hbWVzID0gVFJVRSwKICBmb250c2l6ZV9jb2wgPSA5LAogIGFuZ2xlX2NvbCA9IDQ1LAogIGxhYmVsc19jb2wgPSBjZWxsX2xpbmVzICAgICAgICAgIyA8LSBZT1VSIENPTFVNTiBMQUJFTFMKKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDUuIE5KIHRyZWUgKGxhYmVscyA9IG9yaWcuaWRlbnQpCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KZGlzdF9tYXRyaXggPC0gZGlzdCh0KG1lYW5fZXhwcmVzc2lvbl9jZWxsX2xpbmUpKQp0cmVlIDwtIG5qKGRpc3RfbWF0cml4KQoKcGxvdCgKICB0cmVlLAogIG1haW4gPSAiQ2VsbCBMaW5lIFNpbWlsYXJpdHkgVHJlZSAoQURUIEV4cHJlc3Npb24pIiwKICB0aXAubGFiZWwgPSBjZWxsX2xpbmVzICAgICAgICAgICAjIDwtIExBQkVMIFRIRSBUUkVFCikKCmBgYAoKIyBNYXJrZXIgRGlzY292ZXJ5IGFuZCBWaXN1YWxpemF0aW9uIGJ5IEFEVCBDbHVzdGVyCgojIyBHZW5lcmF0ZSBQcm90ZWluLWxldmVsIERpZmZlcmVudGlhbCBNYXJrZXJzCgpgYGB7ciBhZHRfbWFya2VycywgZXZhbD1GQUxTRX0KIyBGaW5kIG1hcmtlcnMgZm9yIEFEVCBjbHVzdGVycwphZHRfbWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhBbGxfc2FtcGxlc19NZXJnZWRfQURULCBhc3NheSA9ICJBRFQiLCBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjI1LCBsb2dmYy50aHJlc2hvbGQgPSAwLjI1LCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiKQoKIyBEaXNwbGF5IHRvcCA1IG1hcmtlcnMgcGVyIGNsdXN0ZXIKdG9wNV9hZHRfbWFya2VycyA8LSBhZHRfbWFya2VycyAlPiUKICAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQogICAgc2xpY2VfbWF4KG4gPSA1LCBvcmRlcl9ieSA9IGF2Z19sb2cyRkMpCgpwcmludCh0b3A1X2FkdF9tYXJrZXJzKQpgYGAKCiMjIEhlYXRtYXAgb2YgUHJvdGVpbiBFeHByZXNzaW9uIGJ5IEFEVCBDbHVzdGVyCgpgYGB7ciBhZHRfaGVhdG1hcF9jbHVzdGVyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0yMH0KRGVmYXVsdEFzc2F5KEFsbF9zYW1wbGVzX01lcmdlZF9BRFQpIDwtICJBRFQiCgphZHRfZmVhdHMgPC0gdW5pcXVlKHRvcDVfYWR0X21hcmtlcnMkZ2VuZSkgICAjIHJlbW92ZSBkdXBsaWNhdGVzCgpwMTIgPC0gRG90UGxvdCgKICBBbGxfc2FtcGxlc19NZXJnZWRfQURULAogIGZlYXR1cmVzID0gYWR0X2ZlYXRzLAogIGFzc2F5ID0gIkFEVCIsCiAgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIgopICsKICBnZ3RpdGxlKCJQcm90ZWluIEV4cHJlc3Npb24gYnkgQURUIENsdXN0ZXIiKQoKcDEyCgoKcDEyXzIgPC0gRG90UGxvdCgKICBBbGxfc2FtcGxlc19NZXJnZWRfQURULAogIGZlYXR1cmVzID0gYWR0X2ZlYXRzLAogIGFzc2F5ID0gIkFEVCIsCiAgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIKKSArCiAgZ2d0aXRsZSgiUHJvdGVpbiBFeHByZXNzaW9uIGJ5IEFEVCBDbHVzdGVyIikKCnAxMl8yCmBgYAoKCgojIyBSaWRnZXBsb3RzOiBQcm90ZWluIEV4cHJlc3Npb24gYWNyb3NzIFJOQSBDbHVzdGVycwoKYGBge3Igcm5hX2FkdF9yaWRnZSwgIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTIwfQpEZWZhdWx0QXNzYXkoQWxsX3NhbXBsZXNfTWVyZ2VkKSA8LSAiQURUIgoKIyBFbnN1cmUgeW91IG9ubHkgdXNlIGV4aXN0aW5nIGZlYXR1cmVzCmFkdF9mZWF0cyA8LSBhZHRfZmVhdHVyZXNbYWR0X2ZlYXR1cmVzICVpbiUgcm93bmFtZXMoQWxsX3NhbXBsZXNfTWVyZ2VkW1siQURUIl1dKV0KCmZvciAoZiBpbiBhZHRfZmVhdHMpIHsKICAgIHAgPC0gUmlkZ2VQbG90KAogICAgICAgIEFsbF9zYW1wbGVzX01lcmdlZCwKICAgICAgICBmZWF0dXJlcyA9IGYsCiAgICAgICAgYXNzYXkgPSAiQURUIiwKICAgICAgICBncm91cC5ieSA9ICJvcmlnLmlkZW50IgogICAgKSArIGdndGl0bGUoZikKICAgIHByaW50KHApCn0KCmBgYAoKCiMjIEZ3YXR1cmVwbG90czogUHJvdGVpbiBFeHByZXNzaW9uIGFjcm9zcyBSTkEgQ2x1c3RlcnMKYGBge3Igcm5hX2FkdF9mZWF0dXJlLCAgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KRGVmYXVsdEFzc2F5KEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gIkFEVCIKCmFkdF9mZWF0cyA8LSBhZHRfZmVhdHVyZXNbYWR0X2ZlYXR1cmVzICVpbiUgcm93bmFtZXMoQWxsX3NhbXBsZXNfTWVyZ2VkW1siQURUIl1dKV0KCmZvciAoZiBpbiBhZHRfZmVhdHMpIHsKICAgIHAgPC0gRmVhdHVyZVBsb3QoCiAgICAgICAgQWxsX3NhbXBsZXNfTWVyZ2VkLAogICAgICAgIGZlYXR1cmVzID0gZiwKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsICAjIFVNQVAgY2FsY3VsYXRlZCBmcm9tIFNDVCBSTkEKICAgICAgICBjb2xzID0gYygibGlnaHRncmV5IiwgImRhcmtyZWQiKSAgIyBvcHRpb25hbAogICAgKSArIGdndGl0bGUoZikKICAgIHByaW50KHApCn0KCgpgYGAKCgoKCgoK