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

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/igJRubyBnZW5lIGV4cHJlc3Npb24gZGF0YSBpcyBwcmVzZW50IGZvciBhbnkgY2VsbC4KCgpjYXQoIioqTm90ZToqKiB0aGUgcmVmZXJlbmNlIGRvZXMgbm90IGhhdmUgZ2VuZSBleHByZXNzaW9uIGluZm9ybWF0aW9uLlxuXG4iLAogICAgIioqSW1wb3J0YW50OioqIE1vc3QgYW5ub3RhdGlvbiB0b29scyAoUHJvamVjVGlscywgU2luZ2xlUiwgU2V1cmF0IGxhYmVsIHRyYW5zZmVyKSByZXF1aXJlIGV4cHJlc3Npb24gZGF0YSBpbiB0aGUgYXRsYXMgdG8gY29tcGFyZSBuZXcgY2VsbHMgZm9yIGxhYmVsIGFzc2lnbm1lbnQuXG5cbiIsCiAgICAiKipSZW1pbmRlcjoqKiBUaGUgcHJlc2VuY2Ugb2YgY2VsbCBJRHMgYW5kIGFubm90YXRpb24gYWxvbmUgaXMgbm90IGVub3VnaCBmb3IgbGFiZWwgdHJhbnNmZXIuXG5cbiIsCiAgICAiKipSZXF1aXJlbWVudDoqKiBZb3UgbmVlZCBhdCBsZWFzdCBhIG5vbi16ZXJvIGNvdW50cyBtYXRyaXggaW4gdGhlIFJOQSBhc3NheSBvZiB0aGUgYXRsYXMuXG4iKQoKCgpgYGAKCiMjICBBc3Nlc3NtZW50IG9mIFJlZmVyZW5jZSBhbmQgUXVlcnkKYGBge3J9CiMgU3VtIGNvdW50cyBwZXIgY2VsbCwgdXNpbmcgbGF5ZXIgJ2NvdW50cycKc3VtX3plcm9fbWVsYW5nZSA8LSBzdW0oTWF0cml4Ojpjb2xTdW1zKEdldEFzc2F5RGF0YShNZWxhbmdlX00wLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpKSA9PSAwKQoKY2F0KCJaZXJvLWNvdW50IGNlbGxzIGluIE1lbGFuZ2VfTTAgUk5BIGFzc2F5OiIsIHN1bV96ZXJvX21lbGFuZ2UsICJcbiIpCgoKc3VtX3plcm9fYXRsYXMgPC0gc3VtKE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEoYXRsYXMsIGFzc2F5ID0gIlJOQSIsIHNsb3QgPSAiY291bnRzIikpID09IDApCgpjYXQoIlplcm8tY291bnQgY2VsbHMgaW4gYXRsYXMgUk5BIGFzc2F5OiIsIHN1bV96ZXJvX2F0bGFzLCAiXG4iKQoKYGBgCgoqKlN0ZXAgMSDigJMgQXNzZXNzbWVudCBvZiBSZWZlcmVuY2UgYW5kIFF1ZXJ5OioqICAKCk91ciBxdWVyeSBvYmplY3QgaXMgZ29vZCwgYnV0IHRoZSBhdGxhcyBpcyBlZmZlY3RpdmVseSB1bnVzYWJsZSBhcyBhIHJlZmVyZW5jZSBmb3IgZXhwcmVzc2lvbi1iYXNlZCBsYWJlbCB0cmFuc2ZlciBiZWNhdXNlIGl0IGhhcyBubyBnZW5lIGV4cHJlc3Npb24gZGF0YSBwZXIgY2VsbC4KCioqUmVzdWx0czoqKiAgCgoqKlF1ZXJ5IChNZWxhbmdlX00wKSBSTkEgYXNzYXk6KiogMCBjZWxscyBoYXZlIHplcm8gdG90YWwgY291bnRzLiBUaGlzIG1lYW5zIGFsbCBjZWxscyBpbiB5b3VyIHF1ZXJ5IG9iamVjdCBoYXZlIHZhbGlkIGV4cHJlc3Npb24gZGF0YSwgd2hpY2ggaXMgZ29vZCBmb3IgYW5hbHlzaXMgYW5kIGxhYmVsIHRyYW5zZmVyLiAgCgoqKkF0bGFzIFJOQSBhc3NheToqKiBBbGwgMzYzLDMxNSBjZWxscyBzaG93IHplcm8gY291bnRzLiBUaGlzIGNvbmZpcm1zIHRoZSBlYXJsaWVyIG9ic2VydmF0aW9uIHRoYXQgdGhlIGF0bGFzIGxhY2tzIGFueSB1c2FibGUgZXhwcmVzc2lvbiBkYXRhIGluIHRoZSBSTkEgYXNzYXkuCgoKKipOb3RlOioqIHRoZSByZWZlcmVuY2UgZG9lcyBub3QgaGF2ZSBnZW5lIGV4cHJlc3Npb24gaW5mb3JtYXRpb24uICAKCioqSW1wb3J0YW50OioqIE1vc3QgYW5ub3RhdGlvbiB0b29scyAoUHJvamVjVGlscywgU2luZ2xlUiwgU2V1cmF0IGxhYmVsIHRyYW5zZmVyKSByZXF1aXJlIGV4cHJlc3Npb24gZGF0YSBpbiB0aGUgYXRsYXMgdG8gY29tcGFyZSBuZXcgY2VsbHMgZm9yIGxhYmVsIGFzc2lnbm1lbnQuICAKCioqUmVtaW5kZXI6KiogVGhlIHByZXNlbmNlIG9mIGNlbGwgSURzIGFuZCBhbm5vdGF0aW9uIGFsb25lIGlzIG5vdCBlbm91Z2ggZm9yIGxhYmVsIHRyYW5zZmVyLiAgCgoqKlJlcXVpcmVtZW50OioqIFlvdSBuZWVkIGF0IGxlYXN0IGEgbm9uLXplcm8gY291bnRzIG1hdHJpeCBpbiB0aGUgUk5BIGFzc2F5IG9mIHRoZSBhdGxhcy4KCgojIyBBbHRlcm5hdGl2ZSBBc3NheXMgaW4gQXRsYXMgZm9yIFVzYWJsZSBEYXRhCmBgYHtyfQoKIyBVc2luZyBHZXRBc3NheURhdGEgd2l0aCAnbGF5ZXInIGFyZ3VtZW50IGluIFNldXJhdCB2NQpzdW1femVyb19TQ1QgPC0gc3VtKE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEoYXRsYXMsIGFzc2F5ID0gIlNDVCIsIGxheWVyID0gImNvdW50cyIpKSA9PSAwKQpjYXQoIlplcm8tY291bnQgY2VsbHMgaW4gYXRsYXMgU0NUIGFzc2F5OiIsIHN1bV96ZXJvX1NDVCwgIlxuIikKCnN1bV96ZXJvX2ludGVncmF0ZWQgPC0gc3VtKE1hdHJpeDo6Y29sU3VtcyhHZXRBc3NheURhdGEoYXRsYXMsIGFzc2F5ID0gImludGVncmF0ZWQiLCBsYXllciA9ICJjb3VudHMiKSkgPT0gMCkKY2F0KCJaZXJvLWNvdW50IGNlbGxzIGluIGF0bGFzIGludGVncmF0ZWQgYXNzYXk6Iiwgc3VtX3plcm9faW50ZWdyYXRlZCwgIlxuIikKCgoKYGBgCgoqKlN0ZXAgMiDigJMgRGF0YSBRdWFsaXR5IENoZWNrOioqICAKCkJlZm9yZSBwZXJmb3JtaW5nIGxhYmVsIHRyYW5zZmVyLCB3ZSBjb25maXJtZWQgdGhhdCB0aGUgcXVlcnkgb2JqZWN0IChNZWxhbmdlX00wKSBjb250YWlucyB2YWxpZCBleHByZXNzaW9uIGRhdGEgZm9yIGFsbCBjZWxscy4gIAoKVGhlIHJlZmVyZW5jZSBhdGxhcywgaG93ZXZlciwgbGFja3MgZ2VuZSBleHByZXNzaW9uIGRhdGEgaW4gaXRzIFJOQSBhc3NheSwgbWFraW5nIGl0IHVuc3VpdGFibGUgZm9yIGV4cHJlc3Npb24tYmFzZWQgbGFiZWwgdHJhbnNmZXIuICAKCioqSW1wbGljYXRpb246KiogIApFeHByZXNzaW9uLWJhc2VkIGFubm90YXRpb24gdG9vbHMgKGUuZy4sIFNpbmdsZVIsIFNldXJhdCBsYWJlbCB0cmFuc2ZlciwgUHJvamVjVElMcykgcmVxdWlyZSBub24temVybyBjb3VudHMgaW4gdGhlIHJlZmVyZW5jZSBhdGxhcyB0byBhc3NpZ24gbGFiZWxzIG1lYW5pbmdmdWxseS4gU2luY2UgdGhlIGF0bGFzIGhhcyB6ZXJvIGNvdW50cywgaXQgY2Fubm90IHNlcnZlIGFzIGEgcHJvcGVyIHJlZmVyZW5jZS4KCgoKIyMgRGltcGxvdCBWaXN1YWxpemF0aW9uCmBgYHtyfQpsaWJyYXJ5KFNldXJhdCkKCiMgRm9yIE1lbGFuZ2VfTTAgb2JqZWN0CkRpbVBsb3QoTWVsYW5nZV9NMCwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGdyb3VwLmJ5ID0gIkNsdXN0ZXJzIiwgcmVwZWwgPSBUKSArIGdndGl0bGUoIlVNQVAgb2YgTWVsYW5nZV9NMCIpCgpEZWZhdWx0QXNzYXkoYXRsYXMpIDwtICJpbnRlZ3JhdGVkIgoKCgpEaW1QbG90KGF0bGFzLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInNob3J0LmxhYmVsIixsYWJlbCA9IEYsIGNlbGxzID0gd2hpY2goIWlzLm5hKGF0bGFzJHNob3J0LmxhYmVsKSkpCgoKYGBgCgoKIyMgIFByZXBhcmUgdGhlIFVNQVAgY29vcmRpbmF0ZXMKYGBge3J9CiMgU2luY2UgdGhlIGF0bGFzIGhhcyBubyBleHByZXNzaW9uIGRhdGEsIHdlIHdpbGwgcHJvamVjdCB5b3VyIHF1ZXJ5IG9udG8gdGhlIGF0bGFzIFVNQVAgdXNpbmcgdGhlIGV4aXN0aW5nIGNvb3JkaW5hdGVzLCB3aXRob3V0IHJlcXVpcmluZyBSTkEgY291bnRzLgojIEF0bGFzIFVNQVAgY29vcmRpbmF0ZXMKYXRsYXNfdW1hcCA8LSBFbWJlZGRpbmdzKGF0bGFzLCAidW1hcCIpCmhlYWQoYXRsYXNfdW1hcCkKCiMgTWVsYW5nZV9NMCBVTUFQIGNvb3JkaW5hdGVzCm1lbGFuZ2VfdW1hcCA8LSBFbWJlZGRpbmdzKE1lbGFuZ2VfTTAsICJ1bWFwIikKaGVhZChtZWxhbmdlX3VtYXApCgpgYGAKCiMjIENyZWF0ZSBjb21iaW5lZCBkYXRhIGZyYW1lIGZvciBwbG90dGluZwpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKIyBBdGxhcwpkZl9hdGxhcyA8LSBkYXRhLmZyYW1lKGF0bGFzX3VtYXApCmRmX2F0bGFzJGRhdGFzZXQgPC0gIkF0bGFzIgpkZl9hdGxhcyRjbHVzdGVyIDwtIGF0bGFzJHNob3J0LmxhYmVsICAjIG9yIHNldXJhdF9jbHVzdGVycyBpZiB5b3UgcHJlZmVyCgojIE1lbGFuZ2VfTTAKZGZfbWVsYW5nZSA8LSBkYXRhLmZyYW1lKG1lbGFuZ2VfdW1hcCkKZGZfbWVsYW5nZSRkYXRhc2V0IDwtICJNZWxhbmdlX00wIgoKCiMgQXRsYXMKZGZfYXRsYXMgPC0gZGF0YS5mcmFtZSgKICBVTUFQXzEgPSBhdGxhc191bWFwWywxXSwKICBVTUFQXzIgPSBhdGxhc191bWFwWywyXSwKICBkYXRhc2V0ID0gIkF0bGFzIiwKICBjbHVzdGVyID0gYXRsYXMkc2hvcnQubGFiZWwgICAjIG9wdGlvbmFsOyBjYW4gcmVtb3ZlIGlmIG5vdCBuZWVkZWQKKQojCiMgTWVsYW5nZV9NMApkZl9tZWxhbmdlIDwtIGRhdGEuZnJhbWUoCiAgVU1BUF8xID0gbWVsYW5nZV91bWFwWywxXSwKICBVTUFQXzIgPSBtZWxhbmdlX3VtYXBbLDJdLAogIGRhdGFzZXQgPSAiTWVsYW5nZV9NMCIsCiAgY2x1c3RlciA9IE5BICAgIyBBZGQgTkEgc28gY29sdW1uIHN0cnVjdHVyZSBtYXRjaGVzIGF0bGFzCikKCgojIENvbWJpbmUKZGZfY29tYmluZWQgPC0gcmJpbmQoZGZfYXRsYXMsIGRmX21lbGFuZ2UpCgoKYGBgCgojIFBsb3QKYGBge3J9CmdncGxvdChkZl9jb21iaW5lZCwgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMiwgY29sb3I9ZGF0YXNldCkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNSwgc2l6ZT0wLjUpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGdndGl0bGUoIk1lbGFuZ2VfTTAgcHJvamVjdGVkIG9udG8gQXRsYXMgVU1BUCIpCgpgYGAKCiMgT3ZlcmxheSBxdWVyeSBvbiBhdGxhcyBVTUFQCmBgYHtyfQojU2luY2UgeW91ciBhdGxhcyBoYXMgbm8gZXhwcmVzc2lvbiBkYXRhLCB5b3UgY2Fubm90IGRvIGxhYmVsIHRyYW5zZmVyIHlldCwgYnV0IHlvdSBjYW4gc3RpbGwgaW5zcGVjdCBob3cgdGhlIHF1ZXJ5IGNlbGxzIGFsaWduIGluIFVNQVAgc3BhY2UuCgpsaWJyYXJ5KGdncGxvdDIpCgpnZ3Bsb3QoZGZfY29tYmluZWQsIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRmX2NvbWJpbmVkLCBkYXRhc2V0ID09ICJBdGxhcyIpLAogICAgICAgICAgICAgY29sb3IgPSAibGlnaHRncmF5IiwgYWxwaGEgPSAwLjMsIHNpemUgPSAwLjUpICsKICBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQoZGZfY29tYmluZWQsIGRhdGFzZXQgPT0gIk1lbGFuZ2VfTTAiKSwKICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGFscGhhID0gMC41LCBzaXplID0gMC43KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBnZ3RpdGxlKCJQcm9qZWN0aW9uIG9mIE1lbGFuZ2VfTTAgb250byBBdGxhcyBVTUFQIikKCgpgYGAKCiMjIGhpZ2hsaWdodCBhdGxhcyBjbHVzdGVycwpgYGB7cn0KZ2dwbG90KGRmX2NvbWJpbmVkLCBhZXMoeD1VTUFQXzEsIHk9VU1BUF8yKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChkZl9jb21iaW5lZCwgZGF0YXNldCA9PSAiQXRsYXMiKSwKICAgICAgICAgICAgIGFlcyhjb2xvciA9IGNsdXN0ZXIpLCBhbHBoYSA9IDAuMywgc2l6ZSA9IDAuNSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChkZl9jb21iaW5lZCwgZGF0YXNldCA9PSAiTWVsYW5nZV9NMCIpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgYWxwaGEgPSAwLjUsIHNpemUgPSAwLjcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGdndGl0bGUoIlByb2plY3Rpb24gb2YgTWVsYW5nZV9NMCBvbnRvIEF0bGFzIFVNQVAgd2l0aCBhdGxhcyBjbHVzdGVycyIpCgoKYGBgCgoKIyMgUHJlcGFyZSB5b3VyIHF1ZXJ5IGFuZCByZWZlcmVuY2UKYGBge3J9CiMgTWFrZSBzdXJlIHlvdXIgYXRsYXMgaXMgbm9ybWFsaXplZCBhbmQgaGFzIGEgVU1BUApEZWZhdWx0QXNzYXkoYXRsYXMpIDwtICJpbnRlZ3JhdGVkIgoKYGBgCgojIyBBcHByb3hpbWF0ZSBhbm5vdGF0aW9uIHVzaW5nIFVNQVAgY29vcmRpbmF0ZXMKYGBge3J9CmxpYnJhcnkoRk5OKQoKIyAxLiBFeHRyYWN0IFVNQVAgZW1iZWRkaW5ncyBmb3IgYXRsYXMgYW5kIHF1ZXJ5CmF0bGFzX3VtYXAgPC0gRW1iZWRkaW5ncyhhdGxhcywgInVtYXAiKQpxdWVyeV91bWFwIDwtIEVtYmVkZGluZ3MoTWVsYW5nZV9NMCwgInVtYXAiKQoKIyAyLiBHZXQgdGhlIGNsdXN0ZXIgbGFiZWxzIGZyb20gYXRsYXMgKGNob29zZSBjb3JyZWN0IGNvbHVtbikKYXRsYXNfY2x1c3RlcnMgPC0gYXRsYXMkc2hvcnQubGFiZWwgICAKCiMgMy4gUnVuIG5lYXJlc3QtbmVpZ2hib3Igc2VhcmNoIChmb3IgZWFjaCBxdWVyeSBjZWxsLCBmaW5kIGNsb3Nlc3QgYXRsYXMgY2VsbCBpbiBVTUFQIHNwYWNlKQpubiA8LSBnZXQua25ueChkYXRhID0gYXRsYXNfdW1hcCwgcXVlcnkgPSBxdWVyeV91bWFwLCBrID0gMSkKCiMgNC4gQXNzaWduIHF1ZXJ5IGNlbGxzIHRoZSBjbHVzdGVyIGxhYmVsIG9mIHRoZWlyIG5lYXJlc3QgYXRsYXMgbmVpZ2hib3IKcHJlZGljdGVkX2NsdXN0ZXIgPC0gYXRsYXNfY2x1c3RlcnNbbm4kbm4uaW5kZXhbLDFdXQpuYW1lcyhwcmVkaWN0ZWRfY2x1c3RlcikgPC0gcm93bmFtZXMocXVlcnlfdW1hcCkKCiMgNS4gQWRkIHRvIHF1ZXJ5IFNldXJhdCBvYmplY3QKTWVsYW5nZV9NMCA8LSBBZGRNZXRhRGF0YShNZWxhbmdlX00wLCBtZXRhZGF0YSA9IHByZWRpY3RlZF9jbHVzdGVyLCBjb2wubmFtZSA9ICJwcmVkaWN0ZWRfY2x1c3RlciIpCgojIDYuIFZpc3VhbGl6ZQpEaW1QbG90KE1lbGFuZ2VfTTAsIGdyb3VwLmJ5ID0gInByZWRpY3RlZF9jbHVzdGVyIiwgbGFiZWwgPSBGLCByZXBlbCA9IFRSVUUpCgp0YWJsZShNZWxhbmdlX00wJENsdXN0ZXJzLCBNZWxhbmdlX00wJHByZWRpY3RlZF9jbHVzdGVyKQoKYGBgCgojIyBQcmVwYXJlIHlvdXIgcXVlcnkgYW5kIHJlZmVyZW5jZQpgYGB7cn0KRGltUGxvdChNZWxhbmdlX00wLCBncm91cC5ieSA9ICJwcmVkaWN0ZWRfY2x1c3RlciIsIGxhYmVsID0gRikgKwogIGdndGl0bGUoIk1lbGFuZ2VfTTAgcHJvamVjdGVkIG9udG8gYXRsYXMgY2x1c3RlcnMgKG5lYXJlc3QgbmVpZ2hib3IpIikKCmBgYAoKIyMgUHJlcGFyZSB5b3VyIHF1ZXJ5IGFuZCByZWZlcmVuY2UKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKCkRpbVBsb3QoYXRsYXMsIGdyb3VwLmJ5ID0gInNob3J0LmxhYmVsIikgKwogIGdlb21fcG9pbnQoCiAgICBkYXRhID0gYXMuZGF0YS5mcmFtZShFbWJlZGRpbmdzKE1lbGFuZ2VfTTAsICJ1bWFwIikpICU+JQogICAgICAgICAgICAgbXV0YXRlKHByZWRpY3RlZF9jbHVzdGVyID0gTWVsYW5nZV9NMCRwcmVkaWN0ZWRfY2x1c3RlciksCiAgICBhZXMoeCA9IHVtYXBfMSwgeSA9IHVtYXBfMiwgY29sb3IgPSBwcmVkaWN0ZWRfY2x1c3RlciksCiAgICBhbHBoYSA9IDAuNSwgc2l6ZSA9IDAuNQogICkgKwogIGdndGl0bGUoIk1lbGFuZ2VfTTAgY2VsbHMgcHJvamVjdGVkIG9udG8gQXRsYXMgVU1BUCIpCgoKYGBgCiMgKipTdW1tYXJ5IG9mIFN0ZXBzIGFuZCBPYnNlcnZhdGlvbnMqKgoKIyMgKipMb2FkIFF1ZXJ5IGFuZCBBdGxhcyoqCi0gKipRdWVyeToqKiBgTWVsYW5nZV9NMGAgU2V1cmF0IG9iamVjdCB3aXRoIHZhbGlkIFJOQSBjb3VudHMuCi0gKipBdGxhczoqKiBDb3VsdG9uIG1hY3JvcGhhZ2UgYXRsYXMgKGBhdGxhc2ApIHdpdGggVU1BUCBlbWJlZGRpbmdzIGFuZCBjbHVzdGVyIGxhYmVscywgYnV0ICoqbm8gZ2VuZSBleHByZXNzaW9uIGRhdGEqKiAoUk5BIGFzc2F5IGNvdW50cyBhcmUgYWxsIHplcm8pLgoKIyMgKipBc3Nlc3MgRGF0YSBRdWFsaXR5KioKLSBRdWVyeSBoYXMgdXNhYmxlIGV4cHJlc3Npb24gZm9yIGFsbCBjZWxscyDihpIgc3VpdGFibGUgZm9yICoqZXhwcmVzc2lvbi1iYXNlZCBhbm5vdGF0aW9uKiouICAKLSBBdGxhcyBsYWNrcyBnZW5lIGV4cHJlc3Npb24g4oaSIGNhbm5vdCBwZXJmb3JtICoqZXhwcmVzc2lvbi1iYXNlZCBsYWJlbCB0cmFuc2ZlcioqICh0b29scyBsaWtlIFNpbmdsZVIsIFNldXJhdCBsYWJlbCB0cmFuc2Zlciwgb3IgUHJvamVjVElMcyByZWx5IG9uIGNvbXBhcmluZyBleHByZXNzaW9uIHByb2ZpbGVzKS4KCiMjICoqVmlzdWFsaXphdGlvbioqCi0gQm90aCBhdGxhcyBhbmQgcXVlcnkgKipVTUFQcyBjYW4gYmUgdmlzdWFsaXplZCoqLiAgCi0gUXVlcnkgY2VsbHMgY2FuIGJlIHByb2plY3RlZCBvbnRvIGF0bGFzIFVNQVAgdXNpbmcgKipuZWFyZXN0LW5laWdoYm9yIGFwcHJvYWNoZXMgaW4gY29vcmRpbmF0ZSBzcGFjZSoqLCBidXQgKipjbHVzdGVyIGFzc2lnbm1lbnQgd2lsbCBiZSBhcHByb3hpbWF0ZSoqLgoKIyMgKipBdHRlbXB0aW5nIEFubm90YXRpb24gV2l0aG91dCBFeHByZXNzaW9uKioKLSBTaW5jZSB0aGUgYXRsYXMgaGFzIG5vIGV4cHJlc3Npb24gZGF0YSwgdG9vbHMgbGlrZSAqKlNpbmdsZVIqKiBvciBub3JtYWwgKipQcm9qZWNUSUxzKiogY2Fubm90IGFzc2lnbiBsYWJlbHMuICAKLSAqKkFsdGVybmF0aXZlOioqIGNvb3JkaW5hdGUtYmFzZWQgbmVhcmVzdC1uZWlnaGJvciBwcm9qZWN0aW9uIG9uICoqVU1BUCBvciBQQ0EgZW1iZWRkaW5ncyoqLCB3aGljaCBhcHByb3hpbWF0ZXMgdGhlIGNsb3Nlc3QgYXRsYXMgY2x1c3RlciBmb3IgZWFjaCBxdWVyeSBjZWxsLgoKIyMgKipXaHkgRXhwcmVzc2lvbiBEYXRhIGlzIE5lZWRlZCoqCi0gRXhwcmVzc2lvbi1iYXNlZCBhbm5vdGF0aW9uIHRvb2xzIGNvbXBhcmUgdGhlICoqZ2VuZSBleHByZXNzaW9uIHByb2ZpbGUqKiBvZiBxdWVyeSBjZWxscyB0byB0aGUgcmVmZXJlbmNlLiAgCi0gV2l0aG91dCBleHByZXNzaW9uOgogIC0gQ2Fubm90IGNhbGN1bGF0ZSBzaW1pbGFyaXR5LgogIC0gQ2Fubm90IGlkZW50aWZ5IG1hcmtlciBnZW5lcy4KICAtIENhbm5vdCBjb25maWRlbnRseSBhc3NpZ24gbGFiZWxzLiAgCi0gVU1BUCBvciBQQ0EgZW1iZWRkaW5ncyBhbG9uZSBhbGxvdyB2aXN1YWwgcHJvamVjdGlvbiwgKipidXQgbm90IHRydWUgdHJhbnNjcmlwdG9taWMgbGFiZWwgdHJhbnNmZXIqKi4KCiMjICoqTWV0aG9kcyBUaGF0IENhbiBXb3JrIFdpdGhvdXQgRXhwcmVzc2lvbioqCi0gKipVTUFQL1BDQSBuZWFyZXN0LW5laWdoYm9yIHByb2plY3Rpb246KiogIAogIEFzc2lnbiBlYWNoIHF1ZXJ5IGNlbGwgdG8gdGhlIG5lYXJlc3QgYXRsYXMgY2x1c3RlciB1c2luZyBlbWJlZGRpbmdzLiBQcm92aWRlcyBhcHByb3hpbWF0ZSBhbm5vdGF0aW9uLCBub3QgdHJhbnNjcmlwdG9taWMgY29uZmlybWF0aW9uLiAgCgoKIyMgKipLZXkgVGFrZWF3YXkqKgpGb3IgKip0cnVlIGV4cHJlc3Npb24tYmFzZWQgYW5ub3RhdGlvbioqLCB0aGUgcmVmZXJlbmNlIGF0bGFzICoqbXVzdCBoYXZlIG5vbi16ZXJvIGNvdW50cyoqLiAgCldpdGhvdXQgaXQsIHlvdSBjYW4gb25seSBwZXJmb3JtICoqZW1iZWRkaW5nLWJhc2VkIG5lYXJlc3QtbmVpZ2hib3IgbWFwcGluZyoqLCB1c2VmdWwgZm9yIHZpc3VhbGl6YXRpb24gYW5kIHJvdWdoIGNsdXN0ZXIgYXNzaWdubWVudCwgKipidXQgbm90IGZvciB0cmFuc2NyaXB0b21pYyB2YWxpZGF0aW9uKiouCgoKCgoKCgo=