2. Load Melange
# Load Melange_M0 Seurat object
load("/run/user/1000/gvfs/smb-share:server=10.144.142.131,share=commun/Ludivine/année_recherche_Ludivine/JUIN_OCTOBRE/manip_3/Melange_M0.Robj")
Melange_M0 # Check object
An object of class Seurat
59684 features across 58773 samples within 3 assays
Active assay: RNA (36601 features, 0 variable features)
2 layers present: data, counts
2 other assays present: ADT, SCT
2 dimensional reductions calculated: pca, umap
3. Load the Coulton reference atlas
atlas
An object of class Seurat
111707 features across 363315 samples within 3 assays
Active assay: RNA (64082 features, 0 variable features)
2 layers present: counts, data
2 other assays present: SCT, integrated
1 dimensional reduction calculated: umap
Expression Data Quality in Query and Atlas
# For melange
Melange_M0
str(Melange_M0)
head(colnames(Melange_M0))
head(rownames(Melange_M0))
# For atlas
atlas
str(atlas)
head(colnames(atlas))
head(rownames(atlas))
head(atlas@meta.data)
#All cells have zero in nCount_RNA and nFeature_RNA.
#This means the counts layer in the RNA assay for every cell sums to zero—no gene expression data is present for any cell.
cat("**Note:** the reference does not have gene expression information.\n\n",
"**Important:** Most annotation tools (ProjecTils, SingleR, Seurat label transfer) require expression data in the atlas to compare new cells for label assignment.\n\n",
"**Reminder:** The presence of cell IDs and annotation alone is not enough for label transfer.\n\n",
"**Requirement:** You need at least a non-zero counts matrix in the RNA assay of the atlas.\n")
Assessment of Reference and Query
# Sum counts per cell, using layer 'counts'
sum_zero_melange <- sum(Matrix::colSums(GetAssayData(Melange_M0, assay = "RNA", slot = "counts")) == 0)
cat("Zero-count cells in Melange_M0 RNA assay:", sum_zero_melange, "\n")
Zero-count cells in Melange_M0 RNA assay: 0
sum_zero_atlas <- sum(Matrix::colSums(GetAssayData(atlas, assay = "RNA", slot = "counts")) == 0)
cat("Zero-count cells in atlas RNA assay:", sum_zero_atlas, "\n")
Zero-count cells in atlas RNA assay: 363315
Step 1 – Assessment of Reference and Query:
Our query object is good, but the atlas is effectively unusable as a
reference for expression-based label transfer because it has no gene
expression data per cell.
Results:
Query (Melange_M0) RNA assay: 0 cells have zero
total counts. This means all cells in your query object have valid
expression data, which is good for analysis and label transfer.
Atlas RNA assay: All 363,315 cells show zero counts.
This confirms the earlier observation that the atlas lacks any usable
expression data in the RNA assay.
Note: the reference does not have gene expression
information.
Important: Most annotation tools (ProjecTils,
SingleR, Seurat label transfer) require expression data in the atlas to
compare new cells for label assignment.
Reminder: The presence of cell IDs and annotation
alone is not enough for label transfer.
Requirement: You need at least a non-zero counts
matrix in the RNA assay of the atlas.
Alternative Assays in Atlas for Usable Data
# Using GetAssayData with 'layer' argument in Seurat v5
sum_zero_SCT <- sum(Matrix::colSums(GetAssayData(atlas, assay = "SCT", layer = "counts")) == 0)
cat("Zero-count cells in atlas SCT assay:", sum_zero_SCT, "\n")
Zero-count cells in atlas SCT assay: 363315
sum_zero_integrated <- sum(Matrix::colSums(GetAssayData(atlas, assay = "integrated", layer = "counts")) == 0)
cat("Zero-count cells in atlas integrated assay:", sum_zero_integrated, "\n")
Zero-count cells in atlas integrated assay: 0
Step 2 – Data Quality Check:
Before performing label transfer, we confirmed that the query object
(Melange_M0) contains valid expression data for all cells.
The reference atlas, however, lacks gene expression data in its RNA
assay, making it unsuitable for expression-based label transfer.
Implication:
Expression-based annotation tools (e.g., SingleR, Seurat label transfer,
ProjecTILs) require non-zero counts in the reference atlas to assign
labels meaningfully. Since the atlas has zero counts, it cannot serve as
a proper reference.
Dimplot Visualization
library(Seurat)
# For Melange_M0 object
DimPlot(Melange_M0, reduction = "umap", label = TRUE, group.by = "Clusters", repel = T) + ggtitle("UMAP of Melange_M0")

DefaultAssay(atlas) <- "integrated"
DimPlot(atlas, reduction = "umap", group.by = "short.label",label = F, cells = which(!is.na(atlas$short.label)))

NA
NA
Prepare the UMAP coordinates
# Since the atlas has no expression data, we will project your query onto the atlas UMAP using the existing coordinates, without requiring RNA counts.
# Atlas UMAP coordinates
atlas_umap <- Embeddings(atlas, "umap")
head(atlas_umap)
UMAP_1 UMAP_2
c99 1.722056 1.956212
c177 -3.854096 -4.504594
c215 3.202773 1.778220
c461 1.706688 1.998811
c538 -3.784627 -4.484729
c576 3.187453 1.769555
# Melange_M0 UMAP coordinates
melange_umap <- Embeddings(Melange_M0, "umap")
head(melange_umap)
umap_1 umap_2
ARSI-M0-T24_AAACCTGAGAGGTTGC-1 6.909624 -0.7008778
ARSI-M0-T24_AAACCTGAGAGTACCG-1 -3.761140 -8.0694714
ARSI-M0-T24_AAACCTGAGCACCGCT-1 4.789340 2.2480630
ARSI-M0-T24_AAACCTGAGCCGATTT-1 -4.228836 -8.1086551
ARSI-M0-T24_AAACCTGAGCGAGAAA-1 9.942248 0.4261332
ARSI-M0-T24_AAACCTGCACGTAAGG-1 -4.625619 -6.1553312
Create combined data frame for plotting
library(ggplot2)
# Atlas
df_atlas <- data.frame(atlas_umap)
df_atlas$dataset <- "Atlas"
df_atlas$cluster <- atlas$short.label # or seurat_clusters if you prefer
# Melange_M0
df_melange <- data.frame(melange_umap)
df_melange$dataset <- "Melange_M0"
# Atlas
df_atlas <- data.frame(
UMAP_1 = atlas_umap[,1],
UMAP_2 = atlas_umap[,2],
dataset = "Atlas",
cluster = atlas$short.label # optional; can remove if not needed
)
#
# Melange_M0
df_melange <- data.frame(
UMAP_1 = melange_umap[,1],
UMAP_2 = melange_umap[,2],
dataset = "Melange_M0",
cluster = NA # Add NA so column structure matches atlas
)
# Combine
df_combined <- rbind(df_atlas, df_melange)
Overlay query on atlas UMAP
#Since your atlas has no expression data, you cannot do label transfer yet, but you can still inspect how the query cells align in UMAP space.
library(ggplot2)
ggplot(df_combined, aes(x=UMAP_1, y=UMAP_2)) +
geom_point(data = subset(df_combined, dataset == "Atlas"),
color = "lightgray", alpha = 0.3, size = 0.5) +
geom_point(data = subset(df_combined, dataset == "Melange_M0"),
color = "red", alpha = 0.5, size = 0.7) +
theme_minimal() +
ggtitle("Projection of Melange_M0 onto Atlas UMAP")

NA
NA
highlight atlas clusters
ggplot(df_combined, aes(x=UMAP_1, y=UMAP_2)) +
geom_point(data = subset(df_combined, dataset == "Atlas"),
aes(color = cluster), alpha = 0.3, size = 0.5) +
geom_point(data = subset(df_combined, dataset == "Melange_M0"),
color = "red", alpha = 0.5, size = 0.7) +
theme_minimal() +
ggtitle("Projection of Melange_M0 onto Atlas UMAP with atlas clusters")

NA
NA
Prepare your query and reference
# Make sure your atlas is normalized and has a UMAP
DefaultAssay(atlas) <- "integrated"
Approximate annotation using UMAP coordinates

# 6. Visualize
DimPlot(Melange_M0, group.by = "predicted_cluster", label = F, repel = TRUE)
table(Melange_M0$Clusters, Melange_M0$predicted_cluster)
0_AlvMac 1_MetM2Mac 2_C3Mac 3_ICIMac1 4_ICIMac2 5_StressMac 6_SPP1AREGMac
ARSI_J1 1636 173 686 40 2880 132 58
ARSI_J4 1447 243 600 29 2846 150 57
ARSI_M0_J1 14 4461 8 0 3 0 8
ARSI_M0_J4 7301 8 11 17 56 24 1409
M0 2 820 529 33 81 667 359
TAM_J1 0 140 1409 4676 20 5653 42
TAM_J4 0 0 0 34 6 0 0
7_IFNMac 8_IFNGMac 9_AngioMac 10_InflamMac 11_MetalloMac 12_MBMMac
ARSI_J1 21 1015 204 75 793 1076
ARSI_J4 31 948 252 94 859 1068
ARSI_M0_J1 5 5 14 2 9 4
ARSI_M0_J4 18 21 24 13 32 17
M0 763 32 225 316 29 34
TAM_J1 203 0 0 11 43 4
TAM_J4 0 3 0 0 0 0
13_CalciumMac 14_ProliMac 15_LYZMac 16_ECMHomeoMac 17_IFNMac3 18_ECMMac
ARSI_J1 919 44 742 41 34 132
ARSI_J4 1103 47 814 62 46 104
ARSI_M0_J1 1 1 3 1 0 1
ARSI_M0_J4 14 4 19 19 1 2733
M0 20 2 5 108 61 14
TAM_J1 2 2 0 104 31 5
TAM_J4 0 233 0 1587 0 0
19_ClassMono 20_TDoub 21_HemeMac 22_IFNMac4 23_NA
ARSI_J1 378 750 24 84 0
ARSI_J4 489 741 17 102 0
ARSI_M0_J1 4 1 11 1 0
ARSI_M0_J4 14 14 0 4 0
M0 11 16 11 1 0
TAM_J1 0 0 10 0 0
TAM_J4 0 0 0 0 0
Prepare your query and reference
DimPlot(Melange_M0, group.by = "predicted_cluster", label = F) +
ggtitle("Melange_M0 projected onto atlas clusters (nearest neighbor)")

Prepare your query and reference
library(ggplot2)
DimPlot(atlas, group.by = "short.label") +
geom_point(
data = as.data.frame(Embeddings(Melange_M0, "umap")) %>%
mutate(predicted_cluster = Melange_M0$predicted_cluster),
aes(x = umap_1, y = umap_2, color = predicted_cluster),
alpha = 0.5, size = 0.5
) +
ggtitle("Melange_M0 cells projected onto Atlas UMAP")

NA
NA
LS0tCnRpdGxlOiAiQXRsYXMgTWFwcGluZyBvZiBNZWxhbmdlX00wIHRvIENvdWx0b24gTWFjcm9waGFnZSBBdGxhcyIKYXV0aG9yOiAiTmFzaXIgTWFobW9vZCBBYmJhc2kiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19jb2xsYXBzZWQ6IHllcwogIHdvcmRfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCgojIDEuIGxvYWQgbGlicmFyaWVzCmBgYHtyfQoKIyBDb3JlIHNpbmdsZS1jZWxsCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KFNpbmdsZVIpCmxpYnJhcnkoU3VtbWFyaXplZEV4cGVyaW1lbnQpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KFM0VmVjdG9ycykKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQp9KQoKCiMgT3B0aW9uYWwgaGVscGVycyAodXNlZCBvbmx5IGlmIHByZXNlbnQpCnN1cHByZXNzV2FybmluZ3MoewppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NQYXJhbGxlbCIsIHF1aWV0bHkgPSBUUlVFKSkgewptZXNzYWdlKCJCaW9jUGFyYWxsZWwgbm90IGZvdW5kOyBwcm9jZWVkaW5nIGluIHNlcmlhbCBtb2RlLiIpCn0KfSkKb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCnNldC5zZWVkKDEzMzcpCgpgYGAKCgojIDIuIExvYWQgTWVsYW5nZQpgYGB7cn0KCiMgIyBMb2FkIE1lbGFuZ2VfTTAgU2V1cmF0IG9iamVjdAojIGxvYWQoIi9ydW4vdXNlci8xMDAwL2d2ZnMvc21iLXNoYXJlOnNlcnZlcj0xMC4xNDQuMTQyLjEzMSxzaGFyZT1jb21tdW4vTHVkaXZpbmUvYW5uw6llX3JlY2hlcmNoZV9MdWRpdmluZS9KVUlOX09DVE9CUkUvbWFuaXBfMy9NZWxhbmdlX00wLlJvYmoiKSAgCiMgTWVsYW5nZV9NMCAgIyBDaGVjayBvYmplY3QKCmBgYAoKIyAzLiBMb2FkIHRoZSBDb3VsdG9uIHJlZmVyZW5jZSBhdGxhcwpgYGB7cn0KCiMgYXRsYXMgPC0gcmVhZFJEUygiL3J1bi91c2VyLzEwMDAvZ3Zmcy9zbWItc2hhcmU6c2VydmVyPTEwLjE0NC4xNDIuMTMxLHNoYXJlPWNvbW11bi9MdWRpdmluZS9hbGV4Y291bHRvbi1tYWNyb3BoYWdlLWF0bGFzLTU0NDkwNDgvQ291bHRvbi5yZHMiKQojIAojIGF0bGFzCiMgCiMgIyBUaGlzIG1lYW5zIHlvdSBkbyBoYXZlIGV4cHJlc3Npb24gZGF0YSBpbiB0aGUgYXRsYXMhCgpgYGAKCiMjICBFeHByZXNzaW9uIERhdGEgUXVhbGl0eSBpbiBRdWVyeSBhbmQgQXRsYXMKYGBge3J9CgojIEZvciBtZWxhbmdlCk1lbGFuZ2VfTTAKc3RyKE1lbGFuZ2VfTTApCmhlYWQoY29sbmFtZXMoTWVsYW5nZV9NMCkpCmhlYWQocm93bmFtZXMoTWVsYW5nZV9NMCkpCgojIEZvciBhdGxhcwphdGxhcwpzdHIoYXRsYXMpCmhlYWQoY29sbmFtZXMoYXRsYXMpKQpoZWFkKHJvd25hbWVzKGF0bGFzKSkKaGVhZChhdGxhc0BtZXRhLmRhdGEpCgojQWxsIGNlbGxzIGhhdmUgemVybyBpbiBuQ291bnRfUk5BIGFuZCBuRmVhdHVyZV9STkEuCiNUaGlzIG1lYW5zIHRoZSBjb3VudHMgbGF5ZXIgaW4gdGhlIFJOQSBhc3NheSBmb3IgZXZlcnkgY2VsbCBzdW1zIHRvIHplcm/igJRubyBnZW5lIGV4cHJlc3Npb24gZGF0YSBpcyBwcmVzZW50IGZvciBhbnkgY2VsbC4KCgpjYXQoIioqTm90ZToqKiB0aGUgcmVmZXJlbmNlIGRvZXMgbm90IGhhdmUgZ2VuZSBleHByZXNzaW9uIGluZm9ybWF0aW9uLlxuXG4iLAogICAgIioqSW1wb3J0YW50OioqIE1vc3QgYW5ub3RhdGlvbiB0b29scyAoUHJvamVjVGlscywgU2luZ2xlUiwgU2V1cmF0IGxhYmVsIHRyYW5zZmVyKSByZXF1aXJlIGV4cHJlc3Npb24gZGF0YSBpbiB0aGUgYXRsYXMgdG8gY29tcGFyZSBuZXcgY2VsbHMgZm9yIGxhYmVsIGFzc2lnbm1lbnQuXG5cbiIsCiAgICAiKipSZW1pbmRlcjoqKiBUaGUgcHJlc2VuY2Ugb2YgY2VsbCBJRHMgYW5kIGFubm90YXRpb24gYWxvbmUgaXMgbm90IGVub3VnaCBmb3IgbGFiZWwgdHJhbnNmZXIuXG5cbiIsCiAgICAiKipSZXF1aXJlbWVudDoqKiBZb3UgbmVlZCBhdCBsZWFzdCBhIG5vbi16ZXJvIGNvdW50cyBtYXRyaXggaW4gdGhlIFJOQSBhc3NheSBvZiB0aGUgYXRsYXMuXG4iKQoKCgpgYGAKCiMjICBBc3Nlc3NtZW50IG9mIFJlZmVyZW5jZSBhbmQgUXVlcnkKYGBge3J9CiMgU3VtIGNvdW50cyBwZXIgY2VsbCwgdXNpbmcgbGF5ZXIgJ2NvdW50cycKc3VtX3plcm9fbWVsYW5nZSA8LSBzdW0oTWF0cml4Ojpjb2xTdW1zKEdldEFzc2F5RGF0YShNZWxhbmdlX00wLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpKSA9PSAwKQoKY2F0KCJaZXJvLWNvdW50IGNlbGxzIGluIE1lbGFuZ2VfTTAgUk5BIGFzc2F5OiIsIHN1bV96ZXJvX21lbGFuZ2UsICJcbiIpCgoKc3VtX3plcm9fYXRsYXMgPC0gc3VtKE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEoYXRsYXMsIGFzc2F5ID0gIlJOQSIsIHNsb3QgPSAiY291bnRzIikpID09IDApCgpjYXQoIlplcm8tY291bnQgY2VsbHMgaW4gYXRsYXMgUk5BIGFzc2F5OiIsIHN1bV96ZXJvX2F0bGFzLCAiXG4iKQoKYGBgCgoqKlN0ZXAgMSDigJMgQXNzZXNzbWVudCBvZiBSZWZlcmVuY2UgYW5kIFF1ZXJ5OioqICAKCk91ciBxdWVyeSBvYmplY3QgaXMgZ29vZCwgYnV0IHRoZSBhdGxhcyBpcyBlZmZlY3RpdmVseSB1bnVzYWJsZSBhcyBhIHJlZmVyZW5jZSBmb3IgZXhwcmVzc2lvbi1iYXNlZCBsYWJlbCB0cmFuc2ZlciBiZWNhdXNlIGl0IGhhcyBubyBnZW5lIGV4cHJlc3Npb24gZGF0YSBwZXIgY2VsbC4KCioqUmVzdWx0czoqKiAgCgoqKlF1ZXJ5IChNZWxhbmdlX00wKSBSTkEgYXNzYXk6KiogMCBjZWxscyBoYXZlIHplcm8gdG90YWwgY291bnRzLiBUaGlzIG1lYW5zIGFsbCBjZWxscyBpbiB5b3VyIHF1ZXJ5IG9iamVjdCBoYXZlIHZhbGlkIGV4cHJlc3Npb24gZGF0YSwgd2hpY2ggaXMgZ29vZCBmb3IgYW5hbHlzaXMgYW5kIGxhYmVsIHRyYW5zZmVyLiAgCgoqKkF0bGFzIFJOQSBhc3NheToqKiBBbGwgMzYzLDMxNSBjZWxscyBzaG93IHplcm8gY291bnRzLiBUaGlzIGNvbmZpcm1zIHRoZSBlYXJsaWVyIG9ic2VydmF0aW9uIHRoYXQgdGhlIGF0bGFzIGxhY2tzIGFueSB1c2FibGUgZXhwcmVzc2lvbiBkYXRhIGluIHRoZSBSTkEgYXNzYXkuCgoKKipOb3RlOioqIHRoZSByZWZlcmVuY2UgZG9lcyBub3QgaGF2ZSBnZW5lIGV4cHJlc3Npb24gaW5mb3JtYXRpb24uICAKCioqSW1wb3J0YW50OioqIE1vc3QgYW5ub3RhdGlvbiB0b29scyAoUHJvamVjVGlscywgU2luZ2xlUiwgU2V1cmF0IGxhYmVsIHRyYW5zZmVyKSByZXF1aXJlIGV4cHJlc3Npb24gZGF0YSBpbiB0aGUgYXRsYXMgdG8gY29tcGFyZSBuZXcgY2VsbHMgZm9yIGxhYmVsIGFzc2lnbm1lbnQuICAKCioqUmVtaW5kZXI6KiogVGhlIHByZXNlbmNlIG9mIGNlbGwgSURzIGFuZCBhbm5vdGF0aW9uIGFsb25lIGlzIG5vdCBlbm91Z2ggZm9yIGxhYmVsIHRyYW5zZmVyLiAgCgoqKlJlcXVpcmVtZW50OioqIFlvdSBuZWVkIGF0IGxlYXN0IGEgbm9uLXplcm8gY291bnRzIG1hdHJpeCBpbiB0aGUgUk5BIGFzc2F5IG9mIHRoZSBhdGxhcy4KCgojIyBBbHRlcm5hdGl2ZSBBc3NheXMgaW4gQXRsYXMgZm9yIFVzYWJsZSBEYXRhCmBgYHtyfQoKIyBVc2luZyBHZXRBc3NheURhdGEgd2l0aCAnbGF5ZXInIGFyZ3VtZW50IGluIFNldXJhdCB2NQpzdW1femVyb19TQ1QgPC0gc3VtKE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEoYXRsYXMsIGFzc2F5ID0gIlNDVCIsIGxheWVyID0gImNvdW50cyIpKSA9PSAwKQpjYXQoIlplcm8tY291bnQgY2VsbHMgaW4gYXRsYXMgU0NUIGFzc2F5OiIsIHN1bV96ZXJvX1NDVCwgIlxuIikKCnN1bV96ZXJvX2ludGVncmF0ZWQgPC0gc3VtKE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEoYXRsYXMsIGFzc2F5ID0gImludGVncmF0ZWQiLCBsYXllciA9ICJjb3VudHMiKSkgPT0gMCkKY2F0KCJaZXJvLWNvdW50IGNlbGxzIGluIGF0bGFzIGludGVncmF0ZWQgYXNzYXk6Iiwgc3VtX3plcm9faW50ZWdyYXRlZCwgIlxuIikKCgoKYGBgCgoqKlN0ZXAgMiDigJMgRGF0YSBRdWFsaXR5IENoZWNrOioqICAKCkJlZm9yZSBwZXJmb3JtaW5nIGxhYmVsIHRyYW5zZmVyLCB3ZSBjb25maXJtZWQgdGhhdCB0aGUgcXVlcnkgb2JqZWN0IChNZWxhbmdlX00wKSBjb250YWlucyB2YWxpZCBleHByZXNzaW9uIGRhdGEgZm9yIGFsbCBjZWxscy4gIAoKVGhlIHJlZmVyZW5jZSBhdGxhcywgaG93ZXZlciwgbGFja3MgZ2VuZSBleHByZXNzaW9uIGRhdGEgaW4gaXRzIFJOQSBhc3NheSwgbWFraW5nIGl0IHVuc3VpdGFibGUgZm9yIGV4cHJlc3Npb24tYmFzZWQgbGFiZWwgdHJhbnNmZXIuICAKCioqSW1wbGljYXRpb246KiogIApFeHByZXNzaW9uLWJhc2VkIGFubm90YXRpb24gdG9vbHMgKGUuZy4sIFNpbmdsZVIsIFNldXJhdCBsYWJlbCB0cmFuc2ZlciwgUHJvamVjVElMcykgcmVxdWlyZSBub24temVybyBjb3VudHMgaW4gdGhlIHJlZmVyZW5jZSBhdGxhcyB0byBhc3NpZ24gbGFiZWxzIG1lYW5pbmdmdWxseS4gU2luY2UgdGhlIGF0bGFzIGhhcyB6ZXJvIGNvdW50cywgaXQgY2Fubm90IHNlcnZlIGFzIGEgcHJvcGVyIHJlZmVyZW5jZS4KCgoKIyMgRGltcGxvdCBWaXN1YWxpemF0aW9uCmBgYHtyfQpsaWJyYXJ5KFNldXJhdCkKCiMgRm9yIE1lbGFuZ2VfTTAgb2JqZWN0CkRpbVBsb3QoTWVsYW5nZV9NMCwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGdyb3VwLmJ5ID0gIkNsdXN0ZXJzIiwgcmVwZWwgPSBUKSArIGdndGl0bGUoIlVNQVAgb2YgTWVsYW5nZV9NMCIpCgpEZWZhdWx0QXNzYXkoYXRsYXMpIDwtICJpbnRlZ3JhdGVkIgoKCgpEaW1QbG90KGF0bGFzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInNob3J0LmxhYmVsIixsYWJlbCA9IEYsIGNlbGxzID0gd2hpY2goIWlzLm5hKGF0bGFzJHNob3J0LmxhYmVsKSkpCgoKYGBgCgoKIyMgIFByZXBhcmUgdGhlIFVNQVAgY29vcmRpbmF0ZXMKYGBge3J9CiMgU2luY2UgdGhlIGF0bGFzIGhhcyBubyBleHByZXNzaW9uIGRhdGEsIHdlIHdpbGwgcHJvamVjdCB5b3VyIHF1ZXJ5IG9udG8gdGhlIGF0bGFzIFVNQVAgdXNpbmcgdGhlIGV4aXN0aW5nIGNvb3JkaW5hdGVzLCB3aXRob3V0IHJlcXVpcmluZyBSTkEgY291bnRzLgojIEF0bGFzIFVNQVAgY29vcmRpbmF0ZXMKYXRsYXNfdW1hcCA8LSBFbWJlZGRpbmdzKGF0bGFzLCAidW1hcCIpCmhlYWQoYXRsYXNfdW1hcCkKCiMgTWVsYW5nZV9NMCBVTUFQIGNvb3JkaW5hdGVzCm1lbGFuZ2VfdW1hcCA8LSBFbWJlZGRpbmdzKE1lbGFuZ2VfTTAsICJ1bWFwIikKaGVhZChtZWxhbmdlX3VtYXApCgpgYGAKCiMjIENyZWF0ZSBjb21iaW5lZCBkYXRhIGZyYW1lIGZvciBwbG90dGluZwpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKIyBBdGxhcwpkZl9hdGxhcyA8LSBkYXRhLmZyYW1lKGF0bGFzX3VtYXApCmRmX2F0bGFzJGRhdGFzZXQgPC0gIkF0bGFzIgpkZl9hdGxhcyRjbHVzdGVyIDwtIGF0bGFzJHNob3J0LmxhYmVsICAjIG9yIHNldXJhdF9jbHVzdGVycyBpZiB5b3UgcHJlZmVyCgojIE1lbGFuZ2VfTTAKZGZfbWVsYW5nZSA8LSBkYXRhLmZyYW1lKG1lbGFuZ2VfdW1hcCkKZGZfbWVsYW5nZSRkYXRhc2V0IDwtICJNZWxhbmdlX00wIgoKCiMgQXRsYXMKZGZfYXRsYXMgPC0gZGF0YS5mcmFtZSgKICBVTUFQXzEgPSBhdGxhc191bWFwWywxXSwKICBVTUFQXzIgPSBhdGxhc191bWFwWywyXSwKICBkYXRhc2V0ID0gIkF0bGFzIiwKICBjbHVzdGVyID0gYXRsYXMkc2hvcnQubGFiZWwgICAjIG9wdGlvbmFsOyBjYW4gcmVtb3ZlIGlmIG5vdCBuZWVkZWQKKQojCiMgTWVsYW5nZV9NMApkZl9tZWxhbmdlIDwtIGRhdGEuZnJhbWUoCiAgVU1BUF8xID0gbWVsYW5nZV91bWFwWywxXSwKICBVTUFQXzIgPSBtZWxhbmdlX3VtYXBbLDJdLAogIGRhdGFzZXQgPSAiTWVsYW5nZV9NMCIsCiAgY2x1c3RlciA9IE5BICAgIyBBZGQgTkEgc28gY29sdW1uIHN0cnVjdHVyZSBtYXRjaGVzIGF0bGFzCikKCgojIENvbWJpbmUKZGZfY29tYmluZWQgPC0gcmJpbmQoZGZfYXRsYXMsIGRmX21lbGFuZ2UpCgoKYGBgCgojIFBsb3QKYGBge3J9CmdncGxvdChkZl9jb21iaW5lZCwgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMiwgY29sb3I9ZGF0YXNldCkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNSwgc2l6ZT0wLjUpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGdndGl0bGUoIk1lbGFuZ2VfTTAgcHJvamVjdGVkIG9udG8gQXRsYXMgVU1BUCIpCgpgYGAKCiMgT3ZlcmxheSBxdWVyeSBvbiBhdGxhcyBVTUFQCmBgYHtyfQojU2luY2UgeW91ciBhdGxhcyBoYXMgbm8gZXhwcmVzc2lvbiBkYXRhLCB5b3UgY2Fubm90IGRvIGxhYmVsIHRyYW5zZmVyIHlldCwgYnV0IHlvdSBjYW4gc3RpbGwgaW5zcGVjdCBob3cgdGhlIHF1ZXJ5IGNlbGxzIGFsaWduIGluIFVNQVAgc3BhY2UuCgpsaWJyYXJ5KGdncGxvdDIpCgpnZ3Bsb3QoZGZfY29tYmluZWQsIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRmX2NvbWJpbmVkLCBkYXRhc2V0ID09ICJBdGxhcyIpLAogICAgICAgICAgICAgY29sb3IgPSAibGlnaHRncmF5IiwgYWxwaGEgPSAwLjMsIHNpemUgPSAwLjUpICsKICBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQoZGZfY29tYmluZWQsIGRhdGFzZXQgPT0gIk1lbGFuZ2VfTTAiKSwKICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGFscGhhID0gMC41LCBzaXplID0gMC43KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBnZ3RpdGxlKCJQcm9qZWN0aW9uIG9mIE1lbGFuZ2VfTTAgb250byBBdGxhcyBVTUFQIikKCgpgYGAKCiMjIGhpZ2hsaWdodCBhdGxhcyBjbHVzdGVycwpgYGB7cn0KZ2dwbG90KGRmX2NvbWJpbmVkLCBhZXMoeD1VTUFQXzEsIHk9VU1BUF8yKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChkZl9jb21iaW5lZCwgZGF0YXNldCA9PSAiQXRsYXMiKSwKICAgICAgICAgICAgIGFlcyhjb2xvciA9IGNsdXN0ZXIpLCBhbHBoYSA9IDAuMywgc2l6ZSA9IDAuNSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChkZl9jb21iaW5lZCwgZGF0YXNldCA9PSAiTWVsYW5nZV9NMCIpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgYWxwaGEgPSAwLjUsIHNpemUgPSAwLjcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGdndGl0bGUoIlByb2plY3Rpb24gb2YgTWVsYW5nZV9NMCBvbnRvIEF0bGFzIFVNQVAgd2l0aCBhdGxhcyBjbHVzdGVycyIpCgoKYGBgCgoKIyMgUHJlcGFyZSB5b3VyIHF1ZXJ5IGFuZCByZWZlcmVuY2UKYGBge3J9CiMgTWFrZSBzdXJlIHlvdXIgYXRsYXMgaXMgbm9ybWFsaXplZCBhbmQgaGFzIGEgVU1BUApEZWZhdWx0QXNzYXkoYXRsYXMpIDwtICJpbnRlZ3JhdGVkIgoKYGBgCgojIyBBcHByb3hpbWF0ZSBhbm5vdGF0aW9uIHVzaW5nIFVNQVAgY29vcmRpbmF0ZXMKYGBge3J9CmxpYnJhcnkoRk5OKQoKIyAxLiBFeHRyYWN0IFVNQVAgZW1iZWRkaW5ncyBmb3IgYXRsYXMgYW5kIHF1ZXJ5CmF0bGFzX3VtYXAgPC0gRW1iZWRkaW5ncyhhdGxhcywgInVtYXAiKQpxdWVyeV91bWFwIDwtIEVtYmVkZGluZ3MoTWVsYW5nZV9NMCwgInVtYXAiKQoKIyAyLiBHZXQgdGhlIGNsdXN0ZXIgbGFiZWxzIGZyb20gYXRsYXMgKGNob29zZSBjb3JyZWN0IGNvbHVtbikKYXRsYXNfY2x1c3RlcnMgPC0gYXRsYXMkc2hvcnQubGFiZWwgICAKCiMgMy4gUnVuIG5lYXJlc3QtbmVpZ2hib3Igc2VhcmNoIChmb3IgZWFjaCBxdWVyeSBjZWxsLCBmaW5kIGNsb3Nlc3QgYXRsYXMgY2VsbCBpbiBVTUFQIHNwYWNlKQpubiA8LSBnZXQua25ueChkYXRhID0gYXRsYXNfdW1hcCwgcXVlcnkgPSBxdWVyeV91bWFwLCBrID0gMSkKCiMgNC4gQXNzaWduIHF1ZXJ5IGNlbGxzIHRoZSBjbHVzdGVyIGxhYmVsIG9mIHRoZWlyIG5lYXJlc3QgYXRsYXMgbmVpZ2hib3IKcHJlZGljdGVkX2NsdXN0ZXIgPC0gYXRsYXNfY2x1c3RlcnNbbm4kbm4uaW5kZXhbLDFdXQpuYW1lcyhwcmVkaWN0ZWRfY2x1c3RlcikgPC0gcm93bmFtZXMocXVlcnlfdW1hcCkKCiMgNS4gQWRkIHRvIHF1ZXJ5IFNldXJhdCBvYmplY3QKTWVsYW5nZV9NMCA8LSBBZGRNZXRhRGF0YShNZWxhbmdlX00wLCBtZXRhZGF0YSA9IHByZWRpY3RlZF9jbHVzdGVyLCBjb2wubmFtZSA9ICJwcmVkaWN0ZWRfY2x1c3RlciIpCgojIDYuIFZpc3VhbGl6ZQpEaW1QbG90KE1lbGFuZ2VfTTAsIGdyb3VwLmJ5ID0gInByZWRpY3RlZF9jbHVzdGVyIiwgbGFiZWwgPSBGLCByZXBlbCA9IFRSVUUpCgp0YWJsZShNZWxhbmdlX00wJENsdXN0ZXJzLCBNZWxhbmdlX00wJHByZWRpY3RlZF9jbHVzdGVyKQoKYGBgCgojIyBQcmVwYXJlIHlvdXIgcXVlcnkgYW5kIHJlZmVyZW5jZQpgYGB7cn0KRGltUGxvdChNZWxhbmdlX00wLCBncm91cC5ieSA9ICJwcmVkaWN0ZWRfY2x1c3RlciIsIGxhYmVsID0gRikgKwogIGdndGl0bGUoIk1lbGFuZ2VfTTAgcHJvamVjdGVkIG9udG8gYXRsYXMgY2x1c3RlcnMgKG5lYXJlc3QgbmVpZ2hib3IpIikKCmBgYAoKIyMgUHJlcGFyZSB5b3VyIHF1ZXJ5IGFuZCByZWZlcmVuY2UKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKCkRpbVBsb3QoYXRsYXMsIGdyb3VwLmJ5ID0gInNob3J0LmxhYmVsIikgKwogIGdlb21fcG9pbnQoCiAgICBkYXRhID0gYXMuZGF0YS5mcmFtZShFbWJlZGRpbmdzKE1lbGFuZ2VfTTAsICJ1bWFwIikpICU+JQogICAgICAgICAgICAgbXV0YXRlKHByZWRpY3RlZF9jbHVzdGVyID0gTWVsYW5nZV9NMCRwcmVkaWN0ZWRfY2x1c3RlciksCiAgICBhZXMoeCA9IHVtYXBfMSwgeSA9IHVtYXBfMiwgY29sb3IgPSBwcmVkaWN0ZWRfY2x1c3RlciksCiAgICBhbHBoYSA9IDAuNSwgc2l6ZSA9IDAuNQogICkgKwogIGdndGl0bGUoIk1lbGFuZ2VfTTAgY2VsbHMgcHJvamVjdGVkIG9udG8gQXRsYXMgVU1BUCIpCgoKYGBgCiMgKipTdW1tYXJ5IG9mIFN0ZXBzIGFuZCBPYnNlcnZhdGlvbnMqKgoKIyMgKipMb2FkIFF1ZXJ5IGFuZCBBdGxhcyoqCi0gKipRdWVyeToqKiBgTWVsYW5nZV9NMGAgU2V1cmF0IG9iamVjdCB3aXRoIHZhbGlkIFJOQSBjb3VudHMuCi0gKipBdGxhczoqKiBDb3VsdG9uIG1hY3JvcGhhZ2UgYXRsYXMgKGBhdGxhc2ApIHdpdGggVU1BUCBlbWJlZGRpbmdzIGFuZCBjbHVzdGVyIGxhYmVscywgYnV0ICoqbm8gZ2VuZSBleHByZXNzaW9uIGRhdGEqKiAoUk5BIGFzc2F5IGNvdW50cyBhcmUgYWxsIHplcm8pLgoKIyMgKipBc3Nlc3MgRGF0YSBRdWFsaXR5KioKLSBRdWVyeSBoYXMgdXNhYmxlIGV4cHJlc3Npb24gZm9yIGFsbCBjZWxscyDihpIgc3VpdGFibGUgZm9yICoqZXhwcmVzc2lvbi1iYXNlZCBhbm5vdGF0aW9uKiouICAKLSBBdGxhcyBsYWNrcyBnZW5lIGV4cHJlc3Npb24g4oaSIGNhbm5vdCBwZXJmb3JtICoqZXhwcmVzc2lvbi1iYXNlZCBsYWJlbCB0cmFuc2ZlcioqICh0b29scyBsaWtlIFNpbmdsZVIsIFNldXJhdCBsYWJlbCB0cmFuc2Zlciwgb3IgUHJvamVjVElMcyByZWx5IG9uIGNvbXBhcmluZyBleHByZXNzaW9uIHByb2ZpbGVzKS4KCiMjICoqVmlzdWFsaXphdGlvbioqCi0gQm90aCBhdGxhcyBhbmQgcXVlcnkgKipVTUFQcyBjYW4gYmUgdmlzdWFsaXplZCoqLiAgCi0gUXVlcnkgY2VsbHMgY2FuIGJlIHByb2plY3RlZCBvbnRvIGF0bGFzIFVNQVAgdXNpbmcgKipuZWFyZXN0LW5laWdoYm9yIGFwcHJvYWNoZXMgaW4gY29vcmRpbmF0ZSBzcGFjZSoqLCBidXQgKipjbHVzdGVyIGFzc2lnbm1lbnQgd2lsbCBiZSBhcHByb3hpbWF0ZSoqLgoKIyMgKipBdHRlbXB0aW5nIEFubm90YXRpb24gV2l0aG91dCBFeHByZXNzaW9uKioKLSBTaW5jZSB0aGUgYXRsYXMgaGFzIG5vIGV4cHJlc3Npb24gZGF0YSwgdG9vbHMgbGlrZSAqKlNpbmdsZVIqKiBvciBub3JtYWwgKipQcm9qZWNUSUxzKiogY2Fubm90IGFzc2lnbiBsYWJlbHMuICAKLSAqKkFsdGVybmF0aXZlOioqIGNvb3JkaW5hdGUtYmFzZWQgbmVhcmVzdC1uZWlnaGJvciBwcm9qZWN0aW9uIG9uICoqVU1BUCBvciBQQ0EgZW1iZWRkaW5ncyoqLCB3aGljaCBhcHByb3hpbWF0ZXMgdGhlIGNsb3Nlc3QgYXRsYXMgY2x1c3RlciBmb3IgZWFjaCBxdWVyeSBjZWxsLgoKIyMgKipXaHkgRXhwcmVzc2lvbiBEYXRhIGlzIE5lZWRlZCoqCi0gRXhwcmVzc2lvbi1iYXNlZCBhbm5vdGF0aW9uIHRvb2xzIGNvbXBhcmUgdGhlICoqZ2VuZSBleHByZXNzaW9uIHByb2ZpbGUqKiBvZiBxdWVyeSBjZWxscyB0byB0aGUgcmVmZXJlbmNlLiAgCi0gV2l0aG91dCBleHByZXNzaW9uOgogIC0gQ2Fubm90IGNhbGN1bGF0ZSBzaW1pbGFyaXR5LgogIC0gQ2Fubm90IGlkZW50aWZ5IG1hcmtlciBnZW5lcy4KICAtIENhbm5vdCBjb25maWRlbnRseSBhc3NpZ24gbGFiZWxzLiAgCi0gVU1BUCBvciBQQ0EgZW1iZWRkaW5ncyBhbG9uZSBhbGxvdyB2aXN1YWwgcHJvamVjdGlvbiwgKipidXQgbm90IHRydWUgdHJhbnNjcmlwdG9taWMgbGFiZWwgdHJhbnNmZXIqKi4KCiMjICoqTWV0aG9kcyBUaGF0IENhbiBXb3JrIFdpdGhvdXQgRXhwcmVzc2lvbioqCi0gKipVTUFQL1BDQSBuZWFyZXN0LW5laWdoYm9yIHByb2plY3Rpb246KiogIAogIEFzc2lnbiBlYWNoIHF1ZXJ5IGNlbGwgdG8gdGhlIG5lYXJlc3QgYXRsYXMgY2x1c3RlciB1c2luZyBlbWJlZGRpbmdzLiBQcm92aWRlcyBhcHByb3hpbWF0ZSBhbm5vdGF0aW9uLCBub3QgdHJhbnNjcmlwdG9taWMgY29uZmlybWF0aW9uLiAgCgoKIyMgKipLZXkgVGFrZWF3YXkqKgpGb3IgKip0cnVlIGV4cHJlc3Npb24tYmFzZWQgYW5ub3RhdGlvbioqLCB0aGUgcmVmZXJlbmNlIGF0bGFzICoqbXVzdCBoYXZlIG5vbi16ZXJvIGNvdW50cyoqLiAgCldpdGhvdXQgaXQsIHlvdSBjYW4gb25seSBwZXJmb3JtICoqZW1iZWRkaW5nLWJhc2VkIG5lYXJlc3QtbmVpZ2hib3IgbWFwcGluZyoqLCB1c2VmdWwgZm9yIHZpc3VhbGl6YXRpb24gYW5kIHJvdWdoIGNsdXN0ZXIgYXNzaWdubWVudCwgKipidXQgbm90IGZvciB0cmFuc2NyaXB0b21pYyB2YWxpZGF0aW9uKiouCgoKCgoKCgo=