1. load libraries


# Core single-cell
suppressPackageStartupMessages({
library(Seurat)
library(SingleR)
library(SummarizedExperiment)
library(Matrix)
library(S4Vectors)
library(dplyr)
library(ggplot2)
})


# Optional helpers (used only if present)
suppressWarnings({
if (!requireNamespace("BiocParallel", quietly = TRUE)) {
message("BiocParallel not found; proceeding in serial mode.")
}
})
options(stringsAsFactors = FALSE)
set.seed(1337)

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) + 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)

Plot

ggplot(df_combined, aes(x=UMAP_1, y=UMAP_2, color=dataset)) +
  geom_point(alpha=0.5, size=0.5) +
  theme_minimal() +
  ggtitle("Melange_M0 projected onto Atlas UMAP")

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

Summary of Steps and Observations

Load Query and Atlas

  • Query: Melange_M0 Seurat object with valid RNA counts.
  • Atlas: Coulton macrophage atlas (atlas) with UMAP embeddings and cluster labels, but no gene expression data (RNA assay counts are all zero).

Assess Data Quality

  • Query has usable expression for all cells → suitable for expression-based annotation.
  • Atlas lacks gene expression → cannot perform expression-based label transfer (tools like SingleR, Seurat label transfer, or ProjecTILs rely on comparing expression profiles).

Visualization

  • Both atlas and query UMAPs can be visualized.
  • Query cells can be projected onto atlas UMAP using nearest-neighbor approaches in coordinate space, but cluster assignment will be approximate.

Attempting Annotation Without Expression

  • Since the atlas has no expression data, tools like SingleR or normal ProjecTILs cannot assign labels.
  • Alternative: coordinate-based nearest-neighbor projection on UMAP or PCA embeddings, which approximates the closest atlas cluster for each query cell.

Why Expression Data is Needed

  • Expression-based annotation tools compare the gene expression profile of query cells to the reference.
  • Without expression:
    • Cannot calculate similarity.
    • Cannot identify marker genes.
    • Cannot confidently assign labels.
  • UMAP or PCA embeddings alone allow visual projection, but not true transcriptomic label transfer.

Methods That Can Work Without Expression

  • UMAP/PCA nearest-neighbor projection:
    Assign each query cell to the nearest atlas cluster using embeddings. Provides approximate annotation, not transcriptomic confirmation.

Key Takeaway

For true expression-based annotation, the reference atlas must have non-zero counts.
Without it, you can only perform embedding-based nearest-neighbor mapping, useful for visualization and rough cluster assignment, but not for transcriptomic validation.

LS0tCnRpdGxlOiAiQXRsYXMgTWFwcGluZyBvZiBNZWxhbmdlX00wIHRvIENvdWx0b24gTWFjcm9waGFnZSBBdGxhcyIKYXV0aG9yOiAiTmFzaXIgTWFobW9vZCBBYmJhc2kiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19jb2xsYXBzZWQ6IHllcwogIHdvcmRfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCgojIDEuIGxvYWQgbGlicmFyaWVzCmBgYHtyfQoKIyBDb3JlIHNpbmdsZS1jZWxsCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KFNpbmdsZVIpCmxpYnJhcnkoU3VtbWFyaXplZEV4cGVyaW1lbnQpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KFM0VmVjdG9ycykKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQp9KQoKCiMgT3B0aW9uYWwgaGVscGVycyAodXNlZCBvbmx5IGlmIHByZXNlbnQpCnN1cHByZXNzV2FybmluZ3MoewppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NQYXJhbGxlbCIsIHF1aWV0bHkgPSBUUlVFKSkgewptZXNzYWdlKCJCaW9jUGFyYWxsZWwgbm90IGZvdW5kOyBwcm9jZWVkaW5nIGluIHNlcmlhbCBtb2RlLiIpCn0KfSkKb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCnNldC5zZWVkKDEzMzcpCgpgYGAKCgojIDIuIExvYWQgTWVsYW5nZQpgYGB7cn0KCiMgIyBMb2FkIE1lbGFuZ2VfTTAgU2V1cmF0IG9iamVjdAojIGxvYWQoIi9ydW4vdXNlci8xMDAwL2d2ZnMvc21iLXNoYXJlOnNlcnZlcj0xMC4xNDQuMTQyLjEzMSxzaGFyZT1jb21tdW4vTHVkaXZpbmUvYW5uw6llX3JlY2hlcmNoZV9MdWRpdmluZS9KVUlOX09DVE9CUkUvbWFuaXBfMy9NZWxhbmdlX00wLlJvYmoiKSAgCiMgTWVsYW5nZV9NMCAgIyBDaGVjayBvYmplY3QKCmBgYAoKIyAzLiBMb2FkIHRoZSBDb3VsdG9uIHJlZmVyZW5jZSBhdGxhcwpgYGB7cn0KCiMgYXRsYXMgPC0gcmVhZFJEUygiL3J1bi91c2VyLzEwMDAvZ3Zmcy9zbWItc2hhcmU6c2VydmVyPTEwLjE0NC4xNDIuMTMxLHNoYXJlPWNvbW11bi9MdWRpdmluZS9hbGV4Y291bHRvbi1tYWNyb3BoYWdlLWF0bGFzLTU0NDkwNDgvQ291bHRvbi5yZHMiKQojIAojIGF0bGFzCiMgCiMgIyBUaGlzIG1lYW5zIHlvdSBkbyBoYXZlIGV4cHJlc3Npb24gZGF0YSBpbiB0aGUgYXRsYXMhCgpgYGAKCiMjICBFeHByZXNzaW9uIERhdGEgUXVhbGl0eSBpbiBRdWVyeSBhbmQgQXRsYXMKYGBge3J9CgojIEZvciBtZWxhbmdlCk1lbGFuZ2VfTTAKc3RyKE1lbGFuZ2VfTTApCmhlYWQoY29sbmFtZXMoTWVsYW5nZV9NMCkpCmhlYWQocm93bmFtZXMoTWVsYW5nZV9NMCkpCgojIEZvciBhdGxhcwphdGxhcwpzdHIoYXRsYXMpCmhlYWQoY29sbmFtZXMoYXRsYXMpKQpoZWFkKHJvd25hbWVzKGF0bGFzKSkKaGVhZChhdGxhc0BtZXRhLmRhdGEpCgojQWxsIGNlbGxzIGhhdmUgemVybyBpbiBuQ291bnRfUk5BIGFuZCBuRmVhdHVyZV9STkEuCiNUaGlzIG1lYW5zIHRoZSBjb3VudHMgbGF5ZXIgaW4gdGhlIFJOQSBhc3NheSBmb3IgZXZlcnkgY2VsbCBzdW1zIHRvIHplcm/igJRubyBnZW5lIGV4cHJlc3Npb24gZGF0YSBpcyBwcmVzZW50IGZvciBhbnkgY2VsbC4KCgpjYXQoIioqTm90ZToqKiB0aGUgcmVmZXJlbmNlIGRvZXMgbm90IGhhdmUgZ2VuZSBleHByZXNzaW9uIGluZm9ybWF0aW9uLlxuXG4iLAogICAgIioqSW1wb3J0YW50OioqIE1vc3QgYW5ub3RhdGlvbiB0b29scyAoUHJvamVjVGlscywgU2luZ2xlUiwgU2V1cmF0IGxhYmVsIHRyYW5zZmVyKSByZXF1aXJlIGV4cHJlc3Npb24gZGF0YSBpbiB0aGUgYXRsYXMgdG8gY29tcGFyZSBuZXcgY2VsbHMgZm9yIGxhYmVsIGFzc2lnbm1lbnQuXG5cbiIsCiAgICAiKipSZW1pbmRlcjoqKiBUaGUgcHJlc2VuY2Ugb2YgY2VsbCBJRHMgYW5kIGFubm90YXRpb24gYWxvbmUgaXMgbm90IGVub3VnaCBmb3IgbGFiZWwgdHJhbnNmZXIuXG5cbiIsCiAgICAiKipSZXF1aXJlbWVudDoqKiBZb3UgbmVlZCBhdCBsZWFzdCBhIG5vbi16ZXJvIGNvdW50cyBtYXRyaXggaW4gdGhlIFJOQSBhc3NheSBvZiB0aGUgYXRsYXMuXG4iKQoKCgpgYGAKCiMjICBBc3Nlc3NtZW50IG9mIFJlZmVyZW5jZSBhbmQgUXVlcnkKYGBge3J9CiMgU3VtIGNvdW50cyBwZXIgY2VsbCwgdXNpbmcgbGF5ZXIgJ2NvdW50cycKc3VtX3plcm9fbWVsYW5nZSA8LSBzdW0oTWF0cml4Ojpjb2xTdW1zKEdldEFzc2F5RGF0YShNZWxhbmdlX00wLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpKSA9PSAwKQoKY2F0KCJaZXJvLWNvdW50IGNlbGxzIGluIE1lbGFuZ2VfTTAgUk5BIGFzc2F5OiIsIHN1bV96ZXJvX21lbGFuZ2UsICJcbiIpCgoKc3VtX3plcm9fYXRsYXMgPC0gc3VtKE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEoYXRsYXMsIGFzc2F5ID0gIlJOQSIsIHNsb3QgPSAiY291bnRzIikpID09IDApCgpjYXQoIlplcm8tY291bnQgY2VsbHMgaW4gYXRsYXMgUk5BIGFzc2F5OiIsIHN1bV96ZXJvX2F0bGFzLCAiXG4iKQoKYGBgCgoqKlN0ZXAgMSDigJMgQXNzZXNzbWVudCBvZiBSZWZlcmVuY2UgYW5kIFF1ZXJ5OioqICAKCk91ciBxdWVyeSBvYmplY3QgaXMgZ29vZCwgYnV0IHRoZSBhdGxhcyBpcyBlZmZlY3RpdmVseSB1bnVzYWJsZSBhcyBhIHJlZmVyZW5jZSBmb3IgZXhwcmVzc2lvbi1iYXNlZCBsYWJlbCB0cmFuc2ZlciBiZWNhdXNlIGl0IGhhcyBubyBnZW5lIGV4cHJlc3Npb24gZGF0YSBwZXIgY2VsbC4KCioqUmVzdWx0czoqKiAgCgoqKlF1ZXJ5IChNZWxhbmdlX00wKSBSTkEgYXNzYXk6KiogMCBjZWxscyBoYXZlIHplcm8gdG90YWwgY291bnRzLiBUaGlzIG1lYW5zIGFsbCBjZWxscyBpbiB5b3VyIHF1ZXJ5IG9iamVjdCBoYXZlIHZhbGlkIGV4cHJlc3Npb24gZGF0YSwgd2hpY2ggaXMgZ29vZCBmb3IgYW5hbHlzaXMgYW5kIGxhYmVsIHRyYW5zZmVyLiAgCgoqKkF0bGFzIFJOQSBhc3NheToqKiBBbGwgMzYzLDMxNSBjZWxscyBzaG93IHplcm8gY291bnRzLiBUaGlzIGNvbmZpcm1zIHRoZSBlYXJsaWVyIG9ic2VydmF0aW9uIHRoYXQgdGhlIGF0bGFzIGxhY2tzIGFueSB1c2FibGUgZXhwcmVzc2lvbiBkYXRhIGluIHRoZSBSTkEgYXNzYXkuCgoKKipOb3RlOioqIHRoZSByZWZlcmVuY2UgZG9lcyBub3QgaGF2ZSBnZW5lIGV4cHJlc3Npb24gaW5mb3JtYXRpb24uICAKCioqSW1wb3J0YW50OioqIE1vc3QgYW5ub3RhdGlvbiB0b29scyAoUHJvamVjVGlscywgU2luZ2xlUiwgU2V1cmF0IGxhYmVsIHRyYW5zZmVyKSByZXF1aXJlIGV4cHJlc3Npb24gZGF0YSBpbiB0aGUgYXRsYXMgdG8gY29tcGFyZSBuZXcgY2VsbHMgZm9yIGxhYmVsIGFzc2lnbm1lbnQuICAKCioqUmVtaW5kZXI6KiogVGhlIHByZXNlbmNlIG9mIGNlbGwgSURzIGFuZCBhbm5vdGF0aW9uIGFsb25lIGlzIG5vdCBlbm91Z2ggZm9yIGxhYmVsIHRyYW5zZmVyLiAgCgoqKlJlcXVpcmVtZW50OioqIFlvdSBuZWVkIGF0IGxlYXN0IGEgbm9uLXplcm8gY291bnRzIG1hdHJpeCBpbiB0aGUgUk5BIGFzc2F5IG9mIHRoZSBhdGxhcy4KCgojIyBBbHRlcm5hdGl2ZSBBc3NheXMgaW4gQXRsYXMgZm9yIFVzYWJsZSBEYXRhCmBgYHtyfQoKIyBVc2luZyBHZXRBc3NheURhdGEgd2l0aCAnbGF5ZXInIGFyZ3VtZW50IGluIFNldXJhdCB2NQpzdW1femVyb19TQ1QgPC0gc3VtKE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEoYXRsYXMsIGFzc2F5ID0gIlNDVCIsIGxheWVyID0gImNvdW50cyIpKSA9PSAwKQpjYXQoIlplcm8tY291bnQgY2VsbHMgaW4gYXRsYXMgU0NUIGFzc2F5OiIsIHN1bV96ZXJvX1NDVCwgIlxuIikKCnN1bV96ZXJvX2ludGVncmF0ZWQgPC0gc3VtKE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEoYXRsYXMsIGFzc2F5ID0gImludGVncmF0ZWQiLCBsYXllciA9ICJjb3VudHMiKSkgPT0gMCkKY2F0KCJaZXJvLWNvdW50IGNlbGxzIGluIGF0bGFzIGludGVncmF0ZWQgYXNzYXk6Iiwgc3VtX3plcm9faW50ZWdyYXRlZCwgIlxuIikKCgoKYGBgCgoqKlN0ZXAgMiDigJMgRGF0YSBRdWFsaXR5IENoZWNrOioqICAKCkJlZm9yZSBwZXJmb3JtaW5nIGxhYmVsIHRyYW5zZmVyLCB3ZSBjb25maXJtZWQgdGhhdCB0aGUgcXVlcnkgb2JqZWN0IChNZWxhbmdlX00wKSBjb250YWlucyB2YWxpZCBleHByZXNzaW9uIGRhdGEgZm9yIGFsbCBjZWxscy4gIAoKVGhlIHJlZmVyZW5jZSBhdGxhcywgaG93ZXZlciwgbGFja3MgZ2VuZSBleHByZXNzaW9uIGRhdGEgaW4gaXRzIFJOQSBhc3NheSwgbWFraW5nIGl0IHVuc3VpdGFibGUgZm9yIGV4cHJlc3Npb24tYmFzZWQgbGFiZWwgdHJhbnNmZXIuICAKCioqSW1wbGljYXRpb246KiogIApFeHByZXNzaW9uLWJhc2VkIGFubm90YXRpb24gdG9vbHMgKGUuZy4sIFNpbmdsZVIsIFNldXJhdCBsYWJlbCB0cmFuc2ZlciwgUHJvamVjVElMcykgcmVxdWlyZSBub24temVybyBjb3VudHMgaW4gdGhlIHJlZmVyZW5jZSBhdGxhcyB0byBhc3NpZ24gbGFiZWxzIG1lYW5pbmdmdWxseS4gU2luY2UgdGhlIGF0bGFzIGhhcyB6ZXJvIGNvdW50cywgaXQgY2Fubm90IHNlcnZlIGFzIGEgcHJvcGVyIHJlZmVyZW5jZS4KCgoKIyMgRGltcGxvdCBWaXN1YWxpemF0aW9uCmBgYHtyfQpsaWJyYXJ5KFNldXJhdCkKCiMgRm9yIE1lbGFuZ2VfTTAgb2JqZWN0CkRpbVBsb3QoTWVsYW5nZV9NMCwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUpICsgZ2d0aXRsZSgiVU1BUCBvZiBNZWxhbmdlX00wIikKCkRlZmF1bHRBc3NheShhdGxhcykgPC0gImludGVncmF0ZWQiCgoKCkRpbVBsb3QoYXRsYXMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAic2hvcnQubGFiZWwiLGxhYmVsID0gRiwgY2VsbHMgPSB3aGljaCghaXMubmEoYXRsYXMkc2hvcnQubGFiZWwpKSkKCgpgYGAKCgojIyAgUHJlcGFyZSB0aGUgVU1BUCBjb29yZGluYXRlcwpgYGB7cn0KIyBTaW5jZSB0aGUgYXRsYXMgaGFzIG5vIGV4cHJlc3Npb24gZGF0YSwgd2Ugd2lsbCBwcm9qZWN0IHlvdXIgcXVlcnkgb250byB0aGUgYXRsYXMgVU1BUCB1c2luZyB0aGUgZXhpc3RpbmcgY29vcmRpbmF0ZXMsIHdpdGhvdXQgcmVxdWlyaW5nIFJOQSBjb3VudHMuCiMgQXRsYXMgVU1BUCBjb29yZGluYXRlcwphdGxhc191bWFwIDwtIEVtYmVkZGluZ3MoYXRsYXMsICJ1bWFwIikKaGVhZChhdGxhc191bWFwKQoKIyBNZWxhbmdlX00wIFVNQVAgY29vcmRpbmF0ZXMKbWVsYW5nZV91bWFwIDwtIEVtYmVkZGluZ3MoTWVsYW5nZV9NMCwgInVtYXAiKQpoZWFkKG1lbGFuZ2VfdW1hcCkKCmBgYAoKIyMgQ3JlYXRlIGNvbWJpbmVkIGRhdGEgZnJhbWUgZm9yIHBsb3R0aW5nCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCgojIEF0bGFzCmRmX2F0bGFzIDwtIGRhdGEuZnJhbWUoYXRsYXNfdW1hcCkKZGZfYXRsYXMkZGF0YXNldCA8LSAiQXRsYXMiCmRmX2F0bGFzJGNsdXN0ZXIgPC0gYXRsYXMkc2hvcnQubGFiZWwgICMgb3Igc2V1cmF0X2NsdXN0ZXJzIGlmIHlvdSBwcmVmZXIKCiMgTWVsYW5nZV9NMApkZl9tZWxhbmdlIDwtIGRhdGEuZnJhbWUobWVsYW5nZV91bWFwKQpkZl9tZWxhbmdlJGRhdGFzZXQgPC0gIk1lbGFuZ2VfTTAiCgoKIyBBdGxhcwpkZl9hdGxhcyA8LSBkYXRhLmZyYW1lKAogIFVNQVBfMSA9IGF0bGFzX3VtYXBbLDFdLAogIFVNQVBfMiA9IGF0bGFzX3VtYXBbLDJdLAogIGRhdGFzZXQgPSAiQXRsYXMiLAogIGNsdXN0ZXIgPSBhdGxhcyRzaG9ydC5sYWJlbCAgICMgb3B0aW9uYWw7IGNhbiByZW1vdmUgaWYgbm90IG5lZWRlZAopCiMKIyBNZWxhbmdlX00wCmRmX21lbGFuZ2UgPC0gZGF0YS5mcmFtZSgKICBVTUFQXzEgPSBtZWxhbmdlX3VtYXBbLDFdLAogIFVNQVBfMiA9IG1lbGFuZ2VfdW1hcFssMl0sCiAgZGF0YXNldCA9ICJNZWxhbmdlX00wIiwKICBjbHVzdGVyID0gTkEgICAjIEFkZCBOQSBzbyBjb2x1bW4gc3RydWN0dXJlIG1hdGNoZXMgYXRsYXMKKQoKCiMgQ29tYmluZQpkZl9jb21iaW5lZCA8LSByYmluZChkZl9hdGxhcywgZGZfbWVsYW5nZSkKCgpgYGAKCiMgUGxvdApgYGB7cn0KZ2dwbG90KGRmX2NvbWJpbmVkLCBhZXMoeD1VTUFQXzEsIHk9VU1BUF8yLCBjb2xvcj1kYXRhc2V0KSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC41LCBzaXplPTAuNSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgZ2d0aXRsZSgiTWVsYW5nZV9NMCBwcm9qZWN0ZWQgb250byBBdGxhcyBVTUFQIikKCmBgYAoKIyBPdmVybGF5IHF1ZXJ5IG9uIGF0bGFzIFVNQVAKYGBge3J9CiNTaW5jZSB5b3VyIGF0bGFzIGhhcyBubyBleHByZXNzaW9uIGRhdGEsIHlvdSBjYW5ub3QgZG8gbGFiZWwgdHJhbnNmZXIgeWV0LCBidXQgeW91IGNhbiBzdGlsbCBpbnNwZWN0IGhvdyB0aGUgcXVlcnkgY2VsbHMgYWxpZ24gaW4gVU1BUCBzcGFjZS4KCmxpYnJhcnkoZ2dwbG90MikKCmdncGxvdChkZl9jb21iaW5lZCwgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMikpICsKICBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQoZGZfY29tYmluZWQsIGRhdGFzZXQgPT0gIkF0bGFzIiksCiAgICAgICAgICAgICBjb2xvciA9ICJsaWdodGdyYXkiLCBhbHBoYSA9IDAuMywgc2l6ZSA9IDAuNSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChkZl9jb21iaW5lZCwgZGF0YXNldCA9PSAiTWVsYW5nZV9NMCIpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgYWxwaGEgPSAwLjUsIHNpemUgPSAwLjcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGdndGl0bGUoIlByb2plY3Rpb24gb2YgTWVsYW5nZV9NMCBvbnRvIEF0bGFzIFVNQVAiKQoKCmBgYAoKIyMgaGlnaGxpZ2h0IGF0bGFzIGNsdXN0ZXJzCmBgYHtyfQpnZ3Bsb3QoZGZfY29tYmluZWQsIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRmX2NvbWJpbmVkLCBkYXRhc2V0ID09ICJBdGxhcyIpLAogICAgICAgICAgICAgYWVzKGNvbG9yID0gY2x1c3RlciksIGFscGhhID0gMC4zLCBzaXplID0gMC41KSArCiAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRmX2NvbWJpbmVkLCBkYXRhc2V0ID09ICJNZWxhbmdlX00wIiksCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBhbHBoYSA9IDAuNSwgc2l6ZSA9IDAuNykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgZ2d0aXRsZSgiUHJvamVjdGlvbiBvZiBNZWxhbmdlX00wIG9udG8gQXRsYXMgVU1BUCB3aXRoIGF0bGFzIGNsdXN0ZXJzIikKCgpgYGAKCgojIyBQcmVwYXJlIHlvdXIgcXVlcnkgYW5kIHJlZmVyZW5jZQpgYGB7cn0KIyBNYWtlIHN1cmUgeW91ciBhdGxhcyBpcyBub3JtYWxpemVkIGFuZCBoYXMgYSBVTUFQCkRlZmF1bHRBc3NheShhdGxhcykgPC0gImludGVncmF0ZWQiCgpgYGAKCiMjIEFwcHJveGltYXRlIGFubm90YXRpb24gdXNpbmcgVU1BUCBjb29yZGluYXRlcwpgYGB7cn0KbGlicmFyeShGTk4pCgojIDEuIEV4dHJhY3QgVU1BUCBlbWJlZGRpbmdzIGZvciBhdGxhcyBhbmQgcXVlcnkKYXRsYXNfdW1hcCA8LSBFbWJlZGRpbmdzKGF0bGFzLCAidW1hcCIpCnF1ZXJ5X3VtYXAgPC0gRW1iZWRkaW5ncyhNZWxhbmdlX00wLCAidW1hcCIpCgojIDIuIEdldCB0aGUgY2x1c3RlciBsYWJlbHMgZnJvbSBhdGxhcyAoY2hvb3NlIGNvcnJlY3QgY29sdW1uKQphdGxhc19jbHVzdGVycyA8LSBhdGxhcyRzaG9ydC5sYWJlbCAgIAoKIyAzLiBSdW4gbmVhcmVzdC1uZWlnaGJvciBzZWFyY2ggKGZvciBlYWNoIHF1ZXJ5IGNlbGwsIGZpbmQgY2xvc2VzdCBhdGxhcyBjZWxsIGluIFVNQVAgc3BhY2UpCm5uIDwtIGdldC5rbm54KGRhdGEgPSBhdGxhc191bWFwLCBxdWVyeSA9IHF1ZXJ5X3VtYXAsIGsgPSAxKQoKIyA0LiBBc3NpZ24gcXVlcnkgY2VsbHMgdGhlIGNsdXN0ZXIgbGFiZWwgb2YgdGhlaXIgbmVhcmVzdCBhdGxhcyBuZWlnaGJvcgpwcmVkaWN0ZWRfY2x1c3RlciA8LSBhdGxhc19jbHVzdGVyc1tubiRubi5pbmRleFssMV1dCm5hbWVzKHByZWRpY3RlZF9jbHVzdGVyKSA8LSByb3duYW1lcyhxdWVyeV91bWFwKQoKIyA1LiBBZGQgdG8gcXVlcnkgU2V1cmF0IG9iamVjdApNZWxhbmdlX00wIDwtIEFkZE1ldGFEYXRhKE1lbGFuZ2VfTTAsIG1ldGFkYXRhID0gcHJlZGljdGVkX2NsdXN0ZXIsIGNvbC5uYW1lID0gInByZWRpY3RlZF9jbHVzdGVyIikKCiMgNi4gVmlzdWFsaXplCkRpbVBsb3QoTWVsYW5nZV9NMCwgZ3JvdXAuYnkgPSAicHJlZGljdGVkX2NsdXN0ZXIiLCBsYWJlbCA9IEYsIHJlcGVsID0gVFJVRSkKCnRhYmxlKE1lbGFuZ2VfTTAkQ2x1c3RlcnMsIE1lbGFuZ2VfTTAkcHJlZGljdGVkX2NsdXN0ZXIpCgpgYGAKCiMjIFByZXBhcmUgeW91ciBxdWVyeSBhbmQgcmVmZXJlbmNlCmBgYHtyfQpEaW1QbG90KE1lbGFuZ2VfTTAsIGdyb3VwLmJ5ID0gInByZWRpY3RlZF9jbHVzdGVyIiwgbGFiZWwgPSBGKSArCiAgZ2d0aXRsZSgiTWVsYW5nZV9NMCBwcm9qZWN0ZWQgb250byBhdGxhcyBjbHVzdGVycyAobmVhcmVzdCBuZWlnaGJvcikiKQoKYGBgCgojIyBQcmVwYXJlIHlvdXIgcXVlcnkgYW5kIHJlZmVyZW5jZQpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKRGltUGxvdChhdGxhcywgZ3JvdXAuYnkgPSAic2hvcnQubGFiZWwiKSArCiAgZ2VvbV9wb2ludCgKICAgIGRhdGEgPSBhcy5kYXRhLmZyYW1lKEVtYmVkZGluZ3MoTWVsYW5nZV9NMCwgInVtYXAiKSkgJT4lCiAgICAgICAgICAgICBtdXRhdGUocHJlZGljdGVkX2NsdXN0ZXIgPSBNZWxhbmdlX00wJHByZWRpY3RlZF9jbHVzdGVyKSwKICAgIGFlcyh4ID0gdW1hcF8xLCB5ID0gdW1hcF8yLCBjb2xvciA9IHByZWRpY3RlZF9jbHVzdGVyKSwKICAgIGFscGhhID0gMC41LCBzaXplID0gMC41CiAgKSArCiAgZ2d0aXRsZSgiTWVsYW5nZV9NMCBjZWxscyBwcm9qZWN0ZWQgb250byBBdGxhcyBVTUFQIikKCgpgYGAKIyAqKlN1bW1hcnkgb2YgU3RlcHMgYW5kIE9ic2VydmF0aW9ucyoqCgojIyAqKkxvYWQgUXVlcnkgYW5kIEF0bGFzKioKLSAqKlF1ZXJ5OioqIGBNZWxhbmdlX00wYCBTZXVyYXQgb2JqZWN0IHdpdGggdmFsaWQgUk5BIGNvdW50cy4KLSAqKkF0bGFzOioqIENvdWx0b24gbWFjcm9waGFnZSBhdGxhcyAoYGF0bGFzYCkgd2l0aCBVTUFQIGVtYmVkZGluZ3MgYW5kIGNsdXN0ZXIgbGFiZWxzLCBidXQgKipubyBnZW5lIGV4cHJlc3Npb24gZGF0YSoqIChSTkEgYXNzYXkgY291bnRzIGFyZSBhbGwgemVybykuCgojIyAqKkFzc2VzcyBEYXRhIFF1YWxpdHkqKgotIFF1ZXJ5IGhhcyB1c2FibGUgZXhwcmVzc2lvbiBmb3IgYWxsIGNlbGxzIOKGkiBzdWl0YWJsZSBmb3IgKipleHByZXNzaW9uLWJhc2VkIGFubm90YXRpb24qKi4gIAotIEF0bGFzIGxhY2tzIGdlbmUgZXhwcmVzc2lvbiDihpIgY2Fubm90IHBlcmZvcm0gKipleHByZXNzaW9uLWJhc2VkIGxhYmVsIHRyYW5zZmVyKiogKHRvb2xzIGxpa2UgU2luZ2xlUiwgU2V1cmF0IGxhYmVsIHRyYW5zZmVyLCBvciBQcm9qZWNUSUxzIHJlbHkgb24gY29tcGFyaW5nIGV4cHJlc3Npb24gcHJvZmlsZXMpLgoKIyMgKipWaXN1YWxpemF0aW9uKioKLSBCb3RoIGF0bGFzIGFuZCBxdWVyeSAqKlVNQVBzIGNhbiBiZSB2aXN1YWxpemVkKiouICAKLSBRdWVyeSBjZWxscyBjYW4gYmUgcHJvamVjdGVkIG9udG8gYXRsYXMgVU1BUCB1c2luZyAqKm5lYXJlc3QtbmVpZ2hib3IgYXBwcm9hY2hlcyBpbiBjb29yZGluYXRlIHNwYWNlKiosIGJ1dCAqKmNsdXN0ZXIgYXNzaWdubWVudCB3aWxsIGJlIGFwcHJveGltYXRlKiouCgojIyAqKkF0dGVtcHRpbmcgQW5ub3RhdGlvbiBXaXRob3V0IEV4cHJlc3Npb24qKgotIFNpbmNlIHRoZSBhdGxhcyBoYXMgbm8gZXhwcmVzc2lvbiBkYXRhLCB0b29scyBsaWtlICoqU2luZ2xlUioqIG9yIG5vcm1hbCAqKlByb2plY1RJTHMqKiBjYW5ub3QgYXNzaWduIGxhYmVscy4gIAotICoqQWx0ZXJuYXRpdmU6KiogY29vcmRpbmF0ZS1iYXNlZCBuZWFyZXN0LW5laWdoYm9yIHByb2plY3Rpb24gb24gKipVTUFQIG9yIFBDQSBlbWJlZGRpbmdzKiosIHdoaWNoIGFwcHJveGltYXRlcyB0aGUgY2xvc2VzdCBhdGxhcyBjbHVzdGVyIGZvciBlYWNoIHF1ZXJ5IGNlbGwuCgojIyAqKldoeSBFeHByZXNzaW9uIERhdGEgaXMgTmVlZGVkKioKLSBFeHByZXNzaW9uLWJhc2VkIGFubm90YXRpb24gdG9vbHMgY29tcGFyZSB0aGUgKipnZW5lIGV4cHJlc3Npb24gcHJvZmlsZSoqIG9mIHF1ZXJ5IGNlbGxzIHRvIHRoZSByZWZlcmVuY2UuICAKLSBXaXRob3V0IGV4cHJlc3Npb246CiAgLSBDYW5ub3QgY2FsY3VsYXRlIHNpbWlsYXJpdHkuCiAgLSBDYW5ub3QgaWRlbnRpZnkgbWFya2VyIGdlbmVzLgogIC0gQ2Fubm90IGNvbmZpZGVudGx5IGFzc2lnbiBsYWJlbHMuICAKLSBVTUFQIG9yIFBDQSBlbWJlZGRpbmdzIGFsb25lIGFsbG93IHZpc3VhbCBwcm9qZWN0aW9uLCAqKmJ1dCBub3QgdHJ1ZSB0cmFuc2NyaXB0b21pYyBsYWJlbCB0cmFuc2ZlcioqLgoKIyMgKipNZXRob2RzIFRoYXQgQ2FuIFdvcmsgV2l0aG91dCBFeHByZXNzaW9uKioKLSAqKlVNQVAvUENBIG5lYXJlc3QtbmVpZ2hib3IgcHJvamVjdGlvbjoqKiAgCiAgQXNzaWduIGVhY2ggcXVlcnkgY2VsbCB0byB0aGUgbmVhcmVzdCBhdGxhcyBjbHVzdGVyIHVzaW5nIGVtYmVkZGluZ3MuIFByb3ZpZGVzIGFwcHJveGltYXRlIGFubm90YXRpb24sIG5vdCB0cmFuc2NyaXB0b21pYyBjb25maXJtYXRpb24uICAKCgojIyAqKktleSBUYWtlYXdheSoqCkZvciAqKnRydWUgZXhwcmVzc2lvbi1iYXNlZCBhbm5vdGF0aW9uKiosIHRoZSByZWZlcmVuY2UgYXRsYXMgKiptdXN0IGhhdmUgbm9uLXplcm8gY291bnRzKiouICAKV2l0aG91dCBpdCwgeW91IGNhbiBvbmx5IHBlcmZvcm0gKiplbWJlZGRpbmctYmFzZWQgbmVhcmVzdC1uZWlnaGJvciBtYXBwaW5nKiosIHVzZWZ1bCBmb3IgdmlzdWFsaXphdGlvbiBhbmQgcm91Z2ggY2x1c3RlciBhc3NpZ25tZW50LCAqKmJ1dCBub3QgZm9yIHRyYW5zY3JpcHRvbWljIHZhbGlkYXRpb24qKi4KCgoKCgoKCg==