load libraries————————————

Read Seurat object with load as you save it with save() function


All_samples_Merged <- readRDS("../STCAT_Annotation/All_samples_Merged_with_STCAT.rds")

1. Subset Normal CD4⁺ T Cells for Reference

# Load required library
library(Seurat)

# Subset normal CD4+ T cells from merged object
reference_cd4 <- subset(
  All_samples_Merged,
  subset = cell_line %in% c("CD4Tcells_lab", "CD4Tcells_10x")
)

# Ensure the SCT assay is available and set it as default
if ("SCT" %in% names(reference_cd4@assays)) {
  DefaultAssay(reference_cd4) <- "SCT"
} else {
  stop("SCT assay not found in the object. Please run SCTransform first.")
}

# Confirm object class and assays
print(class(reference_cd4))            # Should return "Seurat"
[1] "Seurat"
attr(,"package")
[1] "SeuratObject"
print(names(reference_cd4@assays))     # List available assays
[1] "RNA"                          "ADT"                          "prediction.score.celltype.l1"
[4] "prediction.score.celltype.l2" "prediction.score.celltype.l3" "SCT"                         

2. Normalize & Integrate Reference (Accommodate Multiple Donors)


ref_list <- SplitObject(reference_cd4, split.by = "cell_line")

# Run SCTransform on each subset
ref_list <- lapply(ref_list, SCTransform, verbose = FALSE)
Warning: The `slot` argument of `SetAssayData()` is deprecated as of SeuratObject 5.0.0.
Please use the `layer` argument instead.Warning: The `slot` argument of `GetAssayData()` is deprecated as of SeuratObject 5.0.0.
Please use the `layer` argument instead.Warning: Different cells and/or features from existing assay SCTWarning: Different cells and/or features from existing assay SCT
# Run PCA on each SCT-normalized subset (required for RPCA)
ref_list <- lapply(ref_list, function(x) {
  x <- RunPCA(x, assay = "SCT", verbose = FALSE)
  return(x)
})

# Select integration features
ref_features <- SelectIntegrationFeatures(object.list = ref_list)

# Prepare SCT integration
ref_list <- PrepSCTIntegration(object.list = ref_list, anchor.features = ref_features)

  |                                                  | 0 % ~calculating  
  |+++++++++++++++++++++++++                         | 50% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=01s  
# Find integration anchors using RPCA reduction
ref_anchors <- FindIntegrationAnchors(
  object.list = ref_list,
  anchor.features = ref_features,
  normalization.method = "SCT",
  reduction = "rpca"
)
Computing within dataset neighborhoods

  |                                                  | 0 % ~calculating  
  |+++++++++++++++++++++++++                         | 50% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=02s  
Finding all pairwise anchors

  |                                                  | 0 % ~calculating  
Projecting new data onto SVD
Projecting new data onto SVD
Finding neighborhoods
Finding anchors
    Found 630 anchors

  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=04s  
# Integrate data
reference_integrated <- IntegrateData(anchorset = ref_anchors, normalization.method = "SCT")
[1] 1
Warning: Different cells and/or features from existing assay SCTWarning: Layer counts isn't present in the assay object; returning NULL
[1] 2
Warning: Different cells and/or features from existing assay SCTWarning: Layer counts isn't present in the assay object; returning NULLMerging dataset 2 into 1
Extracting anchors for merged samples
Finding integration vectors
Finding integration vector weights
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Integrating data
Warning: Layer counts isn't present in the assay object; returning NULLWarning: Assay integrated changing from Assay to SCTAssayWarning: Layer counts isn't present in the assay object; returning NULLWarning: Different cells and/or features from existing assay SCT
# Set default assay to integrated
DefaultAssay(reference_integrated) <- "integrated"

3. Clustering & Dimensionality Reduction


reference_integrated <- RunPCA(reference_integrated, verbose = FALSE)
reference_integrated <- RunUMAP(reference_integrated, dims = 1:18)
Warning: The default method for RunUMAP has changed from calling Python UMAP via reticulate to the R-native UWOT using the cosine metric
To use Python UMAP via reticulate, set umap.method to 'umap-learn' and metric to 'correlation'
This message will be shown once per session12:29:11 UMAP embedding parameters a = 0.9922 b = 1.112
12:29:11 Read 8610 rows and found 18 numeric columns
12:29:11 Using Annoy for neighbor search, n_neighbors = 30
12:29:11 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
12:29:12 Writing NN index file to temp file /tmp/RtmpgsQ1lC/file1a753457555a35
12:29:12 Searching Annoy index using 1 thread, search_k = 3000
12:29:14 Annoy recall = 100%
12:29:14 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 30
12:29:15 Initializing from normalized Laplacian + noise (using RSpectra)
12:29:15 Commencing optimization for 500 epochs, with 361792 positive edges
12:29:15 Using rng type: pcg
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
12:29:22 Optimization finished
reference_integrated <- FindNeighbors(reference_integrated, dims = 1:18)
Computing nearest neighbor graph
Computing SNN
reference_integrated <- FindClusters(reference_integrated, resolution = 0.3)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 8610
Number of edges: 308059

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9201
Number of communities: 11
Elapsed time: 0 seconds
ElbowPlot(reference_integrated, ndims = 50)


# Visualize UMAP colored by original donor (cell_line)
DimPlot(reference_integrated, group.by = "cell_line", reduction = "umap") +
  ggtitle("UMAP of Integrated CD4⁺ T Cells")


# Visualize UMAP colored by original donor (cell_line)
DimPlot(reference_integrated, group.by = "integrated_snn_res.0.3", reduction = "umap") +
  ggtitle("UMAP of Integrated CD4⁺ T Cells")


# Visualize UMAP colored by original donor (cell_line)
DimPlot(reference_integrated, group.by = "Prediction", reduction = "umap") +
  ggtitle("UMAP of Integrated CD4⁺ T Cells")


# Visualize UMAP colored by original donor (cell_line)
DimPlot(reference_integrated, group.by = "predicted.celltype.l2", reduction = "umap") +
  ggtitle("UMAP of Integrated CD4⁺ T Cells")

Save the mapped query object (Sézary cell lines projected onto reference trajectory):



saveRDS(reference_integrated, file = "sezary_cell_lines_mapped_to_cd4_reference_integrated_before_Monocle3.rds")

4. Trajectory and Pseudotime with Monocle3

library(monocle3)
library(SeuratWrappers)
library(Matrix)

Attaching package: ‘Matrix’

The following object is masked from ‘package:S4Vectors’:

    expand
cds <- as.cell_data_set(reference_integrated)
Warning: `PackageCheck()` was deprecated in SeuratObject 5.0.0.
Please use `rlang::check_installed()` instead.Warning: Monocle 3 trajectories require cluster partitions, which Seurat does not calculate. Please run 'cluster_cells' on your cell_data_set object
cds <- cluster_cells(cds, reduction_method = "UMAP")
cds <- learn_graph(cds, use_partition = TRUE)

  |                                                                                                                     
  |                                                                                                               |   0%
  |                                                                                                                     
  |===============================================================================================================| 100%

  |                                                                                                                     
  |                                                                                                               |   0%
  |                                                                                                                     
  |===============================================================================================================| 100%

  |                                                                                                                     
  |                                                                                                               |   0%
  |                                                                                                                     
  |===============================================================================================================| 100%
naive_markers <- c("CCR7", "SELL", "LEF1")
naive_markers <- naive_markers[naive_markers %in% rownames(cds)]

# Extract log-normalized expression or fallback to counts log-transformed
if("logcounts" %in% assayNames(cds)) {
  expr_mat <- assay(cds, "logcounts")
} else {
  expr_mat <- log1p(assay(cds, "counts"))
}

naive_score <- Matrix::colMeans(expr_mat[naive_markers, , drop = FALSE])
threshold <- quantile(naive_score, 0.95)
root_cells <- names(naive_score[naive_score > threshold])

cds <- order_cells(cds, root_cells = root_cells)
reference_integrated$pseudotime <- pseudotime(cds)

plot_cells(cds, color_cells_by = "pseudotime", show_trajectory_graph = TRUE)
Cells aren't colored in a way that allows them to be grouped.

# Visualize UMAP colored by original donor (cell_line)
DimPlot(reference_integrated, group.by = "Prediction", reduction = "umap") +
  ggtitle("UMAP of Integrated CD4⁺ T Cells")


# Visualize UMAP colored by original donor (cell_line)
DimPlot(reference_integrated, group.by = "predicted.celltype.l2", reduction = "umap") +
  ggtitle("UMAP of Integrated CD4⁺ T Cells")

Save the mapped query object (Sézary cell lines projected onto reference trajectory):



saveRDS(reference_integrated, file = "sezary_cell_lines_mapped_to_cd4_reference_integrated_before_Query_Projection.rds")

5. Prepare Sézary Syndrome Cell Lines as Query

# Load required packages
library(Seurat)
library(glmGamPoi)   # Recommended for memory-efficient SCTransform

Attaching package: ‘glmGamPoi’

The following object is masked from ‘package:ggplot2’:

    vars

The following object is masked from ‘package:dplyr’:

    vars
library(future)

# Optional: Parallel setup to handle memory better
plan("multisession", workers = 4)
options(future.globals.maxSize = 50 * 1024^3)  # 50 GB memory ceiling

# Step 1: Subset Sézary syndrome cell lines
query_subset <- subset(All_samples_Merged, subset = cell_line %in% paste0("L", 1:7))
gc()
             used    (Mb) gc trigger    (Mb)   max used    (Mb)
Ncells    9283855   495.9   14812904   791.1   14812904   791.1
Vcells 2971711686 22672.4 4735597000 36129.8 4085964052 31173.5
# Step 2: Get raw counts matrix from RNA assay
query_raw <- GetAssayData(query_subset, assay = "RNA", slot = "counts")

# Step 3: Filter out genes expressed in <3 cells (saves memory)
keep_genes <- rowSums(query_raw > 0) >= 3
query_raw_filtered <- query_raw[keep_genes, ]

# Step 4: Create a new Seurat object with metadata preserved
query_cells <- CreateSeuratObject(counts = query_raw_filtered, meta.data = query_subset@meta.data)

# Step 5: Run SCTransform with glmGamPoi backend for better performance
query_cells <- SCTransform(
  query_cells,
  method = "glmGamPoi",               # Faster and uses less RAM
  variable.features.n = 3000,         # Optional: focus on top 3000 variable genes
  verbose = FALSE
)

6. Inject Cell Lines into Reference with MapQuery


# Find anchors between reference and query
anchors_query <- FindTransferAnchors(
  reference = reference_integrated,
  query = query_cells,
  reference.reduction = "pca",
  normalization.method = "SCT",
  dims = 1:18
)

# Map query cells to reference
query_mapped <- MapQuery(
  anchorset = anchors_query,
  query = query_cells,
  reference = reference_integrated,
  refdata = list(
    pseudotime = "pseudotime",        # Transfer pseudotime
    seurat_clusters = "seurat_clusters", # Transfer clusters
    trajectory_position = "monocle3_embedding" # If storing MST coords
  ),
  reference.reduction = "pca", 
  reduction.model = "umap"
)

7. Visualization (Plot Reference and Injected Cells by Pseudotime and Cell Line)


# Plot the reference trajectory UMAP, colored by pseudotime
DimPlot(reference_integrated, group.by = "pseudotime", reduction = "umap") +
  ggtitle("Reference CD4⁺ T Cell Trajectory (Pseudotime)")

# Overlay injected cell lines
DimPlot(query_mapped, reduction = "ref.umap", group.by = "cell_line", label = TRUE) +
  ggtitle("Injected Sézary Cell Lines on Reference Trajectory")

Visualization & Analysis


# Combined UMAP with pseudotime
p1 <- DimPlot(reference_integrated, 
              group.by = "pseudotime", 
              reduction = "umap") + 
  scale_color_viridis_c(option = "magma")

p2 <- DimPlot(query_mapped, 
              reduction = "ref.umap", 
              group.by = "cell_line", 
              cols = "darkred") + 
  ggtitle("Sézary Cell Lines")

p1 + p2

# Pseudotime distribution in cell lines
VlnPlot(query_mapped, 
        features = "pseudotime", 
        group.by = "cell_line", 
        pt.size = 0.1) +
  geom_boxplot(width = 0.2)

# Project cell lines on reference trajectory
FeaturePlot(query_mapped, 
           features = "pseudotime", 
           reduction = "ref.umap") +
  geom_point(data = query_mapped[[]], 
             aes(x = refUMAP_1, y = refUMAP_2, color = pseudotime),
             size = 1.5)

Analyze Pseudotime Distributions Across Cell Lines



FeaturePlot(query_mapped, features = "pseudotime", reduction = "ref.umap") +
  ggtitle("Pseudotime Values of Injected Cell Lines")

VlnPlot(query_mapped, features = "pseudotime", group.by = "cell_line") +
  ggtitle("Pseudotime Distribution by Cell Line")

Save the mapped query object (Sézary cell lines projected onto reference trajectory):



saveRDS(query_mapped, file = "sezary_cell_lines_mapped_to_cd4_reference.rds")
Ci0tLQp0aXRsZTogIlRyYWplY3RvcnkgQW5hbHlzaXMgdXB0byBJbnRlZ3JhdGlvbiBvZiBDb250cm9sIgphdXRob3I6IE5hc2lyIE1haG1vb2QgQWJiYXNpCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogICNybWRmb3JtYXRzOjpyZWFkdGhlZG93bgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQotLS0KCgoKIyMgbG9hZCBsaWJyYXJpZXMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CgpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShtb25vY2xlMykKbGlicmFyeShTZXVyYXRXcmFwcGVycykKbGlicmFyeShoYXJtb255KQoKCiMgRXh0cmEgbGlicmFyaWVzCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShTQ3B1YnIpCgpgYGAKCgojIyBSZWFkIFNldXJhdCBvYmplY3Qgd2l0aCBsb2FkIGFzIHlvdSBzYXZlIGl0IHdpdGggc2F2ZSgpIGZ1bmN0aW9uCmBgYHtyIGxvYWRTZXVyYXR9CgpBbGxfc2FtcGxlc19NZXJnZWQgPC0gcmVhZFJEUygiLi4vU1RDQVRfQW5ub3RhdGlvbi9BbGxfc2FtcGxlc19NZXJnZWRfd2l0aF9TVENBVC5yZHMiKQoKCgoKYGBgCgojIDEuIFN1YnNldCBOb3JtYWwgQ0Q04oG6IFQgQ2VsbHMgZm9yIFJlZmVyZW5jZQpgYGB7cn0KIyBMb2FkIHJlcXVpcmVkIGxpYnJhcnkKbGlicmFyeShTZXVyYXQpCgojIFN1YnNldCBub3JtYWwgQ0Q0KyBUIGNlbGxzIGZyb20gbWVyZ2VkIG9iamVjdApyZWZlcmVuY2VfY2Q0IDwtIHN1YnNldCgKICBBbGxfc2FtcGxlc19NZXJnZWQsCiAgc3Vic2V0ID0gY2VsbF9saW5lICVpbiUgYygiQ0Q0VGNlbGxzX2xhYiIsICJDRDRUY2VsbHNfMTB4IikKKQoKIyBFbnN1cmUgdGhlIFNDVCBhc3NheSBpcyBhdmFpbGFibGUgYW5kIHNldCBpdCBhcyBkZWZhdWx0CmlmICgiU0NUIiAlaW4lIG5hbWVzKHJlZmVyZW5jZV9jZDRAYXNzYXlzKSkgewogIERlZmF1bHRBc3NheShyZWZlcmVuY2VfY2Q0KSA8LSAiU0NUIgp9IGVsc2UgewogIHN0b3AoIlNDVCBhc3NheSBub3QgZm91bmQgaW4gdGhlIG9iamVjdC4gUGxlYXNlIHJ1biBTQ1RyYW5zZm9ybSBmaXJzdC4iKQp9CgojIENvbmZpcm0gb2JqZWN0IGNsYXNzIGFuZCBhc3NheXMKcHJpbnQoY2xhc3MocmVmZXJlbmNlX2NkNCkpICAgICAgICAgICAgIyBTaG91bGQgcmV0dXJuICJTZXVyYXQiCnByaW50KG5hbWVzKHJlZmVyZW5jZV9jZDRAYXNzYXlzKSkgICAgICMgTGlzdCBhdmFpbGFibGUgYXNzYXlzCgoKYGBgCgojIDIuIE5vcm1hbGl6ZSAmIEludGVncmF0ZSBSZWZlcmVuY2UgKEFjY29tbW9kYXRlIE11bHRpcGxlIERvbm9ycykKYGBge3J9CgpyZWZfbGlzdCA8LSBTcGxpdE9iamVjdChyZWZlcmVuY2VfY2Q0LCBzcGxpdC5ieSA9ICJjZWxsX2xpbmUiKQoKIyBSdW4gU0NUcmFuc2Zvcm0gb24gZWFjaCBzdWJzZXQKcmVmX2xpc3QgPC0gbGFwcGx5KHJlZl9saXN0LCBTQ1RyYW5zZm9ybSwgdmVyYm9zZSA9IEZBTFNFKQoKIyBSdW4gUENBIG9uIGVhY2ggU0NULW5vcm1hbGl6ZWQgc3Vic2V0IChyZXF1aXJlZCBmb3IgUlBDQSkKcmVmX2xpc3QgPC0gbGFwcGx5KHJlZl9saXN0LCBmdW5jdGlvbih4KSB7CiAgeCA8LSBSdW5QQ0EoeCwgYXNzYXkgPSAiU0NUIiwgdmVyYm9zZSA9IEZBTFNFKQogIHJldHVybih4KQp9KQoKIyBTZWxlY3QgaW50ZWdyYXRpb24gZmVhdHVyZXMKcmVmX2ZlYXR1cmVzIDwtIFNlbGVjdEludGVncmF0aW9uRmVhdHVyZXMob2JqZWN0Lmxpc3QgPSByZWZfbGlzdCkKCiMgUHJlcGFyZSBTQ1QgaW50ZWdyYXRpb24KcmVmX2xpc3QgPC0gUHJlcFNDVEludGVncmF0aW9uKG9iamVjdC5saXN0ID0gcmVmX2xpc3QsIGFuY2hvci5mZWF0dXJlcyA9IHJlZl9mZWF0dXJlcykKCiMgRmluZCBpbnRlZ3JhdGlvbiBhbmNob3JzIHVzaW5nIFJQQ0EgcmVkdWN0aW9uCnJlZl9hbmNob3JzIDwtIEZpbmRJbnRlZ3JhdGlvbkFuY2hvcnMoCiAgb2JqZWN0Lmxpc3QgPSByZWZfbGlzdCwKICBhbmNob3IuZmVhdHVyZXMgPSByZWZfZmVhdHVyZXMsCiAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiU0NUIiwKICByZWR1Y3Rpb24gPSAicnBjYSIKKQoKIyBJbnRlZ3JhdGUgZGF0YQpyZWZlcmVuY2VfaW50ZWdyYXRlZCA8LSBJbnRlZ3JhdGVEYXRhKGFuY2hvcnNldCA9IHJlZl9hbmNob3JzLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJTQ1QiKQoKIyBTZXQgZGVmYXVsdCBhc3NheSB0byBpbnRlZ3JhdGVkCkRlZmF1bHRBc3NheShyZWZlcmVuY2VfaW50ZWdyYXRlZCkgPC0gImludGVncmF0ZWQiCgpgYGAKCgojIDMuIENsdXN0ZXJpbmcgJiBEaW1lbnNpb25hbGl0eSBSZWR1Y3Rpb24KYGBge3J9CgpyZWZlcmVuY2VfaW50ZWdyYXRlZCA8LSBSdW5QQ0EocmVmZXJlbmNlX2ludGVncmF0ZWQsIHZlcmJvc2UgPSBGQUxTRSkKcmVmZXJlbmNlX2ludGVncmF0ZWQgPC0gUnVuVU1BUChyZWZlcmVuY2VfaW50ZWdyYXRlZCwgZGltcyA9IDE6MTgpCnJlZmVyZW5jZV9pbnRlZ3JhdGVkIDwtIEZpbmROZWlnaGJvcnMocmVmZXJlbmNlX2ludGVncmF0ZWQsIGRpbXMgPSAxOjE4KQpyZWZlcmVuY2VfaW50ZWdyYXRlZCA8LSBGaW5kQ2x1c3RlcnMocmVmZXJlbmNlX2ludGVncmF0ZWQsIHJlc29sdXRpb24gPSAwLjMpCgpFbGJvd1Bsb3QocmVmZXJlbmNlX2ludGVncmF0ZWQsIG5kaW1zID0gNTApCgojIFZpc3VhbGl6ZSBVTUFQIGNvbG9yZWQgYnkgb3JpZ2luYWwgZG9ub3IgKGNlbGxfbGluZSkKRGltUGxvdChyZWZlcmVuY2VfaW50ZWdyYXRlZCwgZ3JvdXAuYnkgPSAiY2VsbF9saW5lIiwgcmVkdWN0aW9uID0gInVtYXAiKSArCiAgZ2d0aXRsZSgiVU1BUCBvZiBJbnRlZ3JhdGVkIENENOKBuiBUIENlbGxzIikKCiMgVmlzdWFsaXplIFVNQVAgY29sb3JlZCBieSBvcmlnaW5hbCBkb25vciAoY2VsbF9saW5lKQpEaW1QbG90KHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBncm91cC5ieSA9ICJpbnRlZ3JhdGVkX3Nubl9yZXMuMC4zIiwgcmVkdWN0aW9uID0gInVtYXAiKSArCiAgZ2d0aXRsZSgiVU1BUCBvZiBJbnRlZ3JhdGVkIENENOKBuiBUIENlbGxzIikKCiMgVmlzdWFsaXplIFVNQVAgY29sb3JlZCBieSBvcmlnaW5hbCBkb25vciAoY2VsbF9saW5lKQpEaW1QbG90KHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBncm91cC5ieSA9ICJQcmVkaWN0aW9uIiwgcmVkdWN0aW9uID0gInVtYXAiKSArCiAgZ2d0aXRsZSgiVU1BUCBvZiBJbnRlZ3JhdGVkIENENOKBuiBUIENlbGxzIikKCiMgVmlzdWFsaXplIFVNQVAgY29sb3JlZCBieSBvcmlnaW5hbCBkb25vciAoY2VsbF9saW5lKQpEaW1QbG90KHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiLCByZWR1Y3Rpb24gPSAidW1hcCIpICsKICBnZ3RpdGxlKCJVTUFQIG9mIEludGVncmF0ZWQgQ0Q04oG6IFQgQ2VsbHMiKQoKYGBgCgojIyBTYXZlIHRoZSBtYXBwZWQgcXVlcnkgb2JqZWN0IChTw6l6YXJ5IGNlbGwgbGluZXMgcHJvamVjdGVkIG9udG8gcmVmZXJlbmNlIHRyYWplY3RvcnkpOgpgYGB7cn0KCgpzYXZlUkRTKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBmaWxlID0gInNlemFyeV9jZWxsX2xpbmVzX21hcHBlZF90b19jZDRfcmVmZXJlbmNlX2ludGVncmF0ZWRfYmVmb3JlX01vbm9jbGUzLnJkcyIpCgoKYGBgCgojIDQuIFRyYWplY3RvcnkgYW5kIFBzZXVkb3RpbWUgd2l0aCBNb25vY2xlMwpgYGB7cn0KbGlicmFyeShtb25vY2xlMykKbGlicmFyeShTZXVyYXRXcmFwcGVycykKbGlicmFyeShNYXRyaXgpCgpjZHMgPC0gYXMuY2VsbF9kYXRhX3NldChyZWZlcmVuY2VfaW50ZWdyYXRlZCkKY2RzIDwtIGNsdXN0ZXJfY2VsbHMoY2RzLCByZWR1Y3Rpb25fbWV0aG9kID0gIlVNQVAiKQpjZHMgPC0gbGVhcm5fZ3JhcGgoY2RzLCB1c2VfcGFydGl0aW9uID0gVFJVRSkKCm5haXZlX21hcmtlcnMgPC0gYygiQ0NSNyIsICJTRUxMIiwgIkxFRjEiKQpuYWl2ZV9tYXJrZXJzIDwtIG5haXZlX21hcmtlcnNbbmFpdmVfbWFya2VycyAlaW4lIHJvd25hbWVzKGNkcyldCgojIEV4dHJhY3QgbG9nLW5vcm1hbGl6ZWQgZXhwcmVzc2lvbiBvciBmYWxsYmFjayB0byBjb3VudHMgbG9nLXRyYW5zZm9ybWVkCmlmKCJsb2djb3VudHMiICVpbiUgYXNzYXlOYW1lcyhjZHMpKSB7CiAgZXhwcl9tYXQgPC0gYXNzYXkoY2RzLCAibG9nY291bnRzIikKfSBlbHNlIHsKICBleHByX21hdCA8LSBsb2cxcChhc3NheShjZHMsICJjb3VudHMiKSkKfQoKbmFpdmVfc2NvcmUgPC0gTWF0cml4Ojpjb2xNZWFucyhleHByX21hdFtuYWl2ZV9tYXJrZXJzLCAsIGRyb3AgPSBGQUxTRV0pCnRocmVzaG9sZCA8LSBxdWFudGlsZShuYWl2ZV9zY29yZSwgMC45NSkKcm9vdF9jZWxscyA8LSBuYW1lcyhuYWl2ZV9zY29yZVtuYWl2ZV9zY29yZSA+IHRocmVzaG9sZF0pCgpjZHMgPC0gb3JkZXJfY2VsbHMoY2RzLCByb290X2NlbGxzID0gcm9vdF9jZWxscykKcmVmZXJlbmNlX2ludGVncmF0ZWQkcHNldWRvdGltZSA8LSBwc2V1ZG90aW1lKGNkcykKCnBsb3RfY2VsbHMoY2RzLCBjb2xvcl9jZWxsc19ieSA9ICJwc2V1ZG90aW1lIiwgc2hvd190cmFqZWN0b3J5X2dyYXBoID0gVFJVRSkKCiMgVmlzdWFsaXplIFVNQVAgY29sb3JlZCBieSBvcmlnaW5hbCBkb25vciAoY2VsbF9saW5lKQpEaW1QbG90KHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBncm91cC5ieSA9ICJQcmVkaWN0aW9uIiwgcmVkdWN0aW9uID0gInVtYXAiKSArCiAgZ2d0aXRsZSgiVU1BUCBvZiBJbnRlZ3JhdGVkIENENOKBuiBUIENlbGxzIikKCiMgVmlzdWFsaXplIFVNQVAgY29sb3JlZCBieSBvcmlnaW5hbCBkb25vciAoY2VsbF9saW5lKQpEaW1QbG90KHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiLCByZWR1Y3Rpb24gPSAidW1hcCIpICsKICBnZ3RpdGxlKCJVTUFQIG9mIEludGVncmF0ZWQgQ0Q04oG6IFQgQ2VsbHMiKQoKYGBgCgojIyBTYXZlIHRoZSBtYXBwZWQgcXVlcnkgb2JqZWN0IChTw6l6YXJ5IGNlbGwgbGluZXMgcHJvamVjdGVkIG9udG8gcmVmZXJlbmNlIHRyYWplY3RvcnkpOgpgYGB7cn0KCgpzYXZlUkRTKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBmaWxlID0gInNlemFyeV9jZWxsX2xpbmVzX21hcHBlZF90b19jZDRfcmVmZXJlbmNlX2ludGVncmF0ZWRfYmVmb3JlX1F1ZXJ5X1Byb2plY3Rpb24ucmRzIikKCgpgYGAKCgoKIyA1LiBQcmVwYXJlIFPDqXphcnkgU3luZHJvbWUgQ2VsbCBMaW5lcyBhcyBRdWVyeQpgYGB7cn0KIyBMb2FkIHJlcXVpcmVkIHBhY2thZ2VzCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdsbUdhbVBvaSkgICAjIFJlY29tbWVuZGVkIGZvciBtZW1vcnktZWZmaWNpZW50IFNDVHJhbnNmb3JtCmxpYnJhcnkoZnV0dXJlKQoKIyBPcHRpb25hbDogUGFyYWxsZWwgc2V0dXAgdG8gaGFuZGxlIG1lbW9yeSBiZXR0ZXIKcGxhbigibXVsdGlzZXNzaW9uIiwgd29ya2VycyA9IDQpCm9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDUwICogMTAyNF4zKSAgIyA1MCBHQiBtZW1vcnkgY2VpbGluZwoKIyBTdGVwIDE6IFN1YnNldCBTw6l6YXJ5IHN5bmRyb21lIGNlbGwgbGluZXMKcXVlcnlfc3Vic2V0IDwtIHN1YnNldChBbGxfc2FtcGxlc19NZXJnZWQsIHN1YnNldCA9IGNlbGxfbGluZSAlaW4lIHBhc3RlMCgiTCIsIDE6NykpCgojIFN0ZXAgMjogR2V0IHJhdyBjb3VudHMgbWF0cml4IGZyb20gUk5BIGFzc2F5CnF1ZXJ5X3JhdyA8LSBHZXRBc3NheURhdGEocXVlcnlfc3Vic2V0LCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpCgojIFN0ZXAgMzogRmlsdGVyIG91dCBnZW5lcyBleHByZXNzZWQgaW4gPDMgY2VsbHMgKHNhdmVzIG1lbW9yeSkKa2VlcF9nZW5lcyA8LSByb3dTdW1zKHF1ZXJ5X3JhdyA+IDApID49IDMKcXVlcnlfcmF3X2ZpbHRlcmVkIDwtIHF1ZXJ5X3Jhd1trZWVwX2dlbmVzLCBdCgojIFN0ZXAgNDogQ3JlYXRlIGEgbmV3IFNldXJhdCBvYmplY3Qgd2l0aCBtZXRhZGF0YSBwcmVzZXJ2ZWQKcXVlcnlfY2VsbHMgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IHF1ZXJ5X3Jhd19maWx0ZXJlZCwgbWV0YS5kYXRhID0gcXVlcnlfc3Vic2V0QG1ldGEuZGF0YSkKCiMgU3RlcCA1OiBSdW4gU0NUcmFuc2Zvcm0gd2l0aCBnbG1HYW1Qb2kgYmFja2VuZCBmb3IgYmV0dGVyIHBlcmZvcm1hbmNlCnF1ZXJ5X3NldXJhdCA8LSBTQ1RyYW5zZm9ybSgKICBxdWVyeV9zZXVyYXQsCiAgbWV0aG9kID0gImdsbUdhbVBvaSIsCiAgY29uc2VydmUubWVtb3J5ID0gVFJVRSwgICAjIENyaXRpY2FsIGZvciBsYXJnZSBkYXRhc2V0cwogIHZhcmlhYmxlLmZlYXR1cmVzLm4gPSAwLCAgIyBEb24ndCByZXNlbGVjdCBmZWF0dXJlcyAodXNlIHJlZl9mZWF0dXJlcykKICB2ZXJib3NlID0gRkFMU0UKKQoKCmBgYAoKIyA2LiBJbmplY3QgQ2VsbCBMaW5lcyBpbnRvIFJlZmVyZW5jZSB3aXRoIE1hcFF1ZXJ5CmBgYHtyfQoKIyBGaW5kIGFuY2hvcnMgYmV0d2VlbiByZWZlcmVuY2UgYW5kIHF1ZXJ5CmFuY2hvcnNfcXVlcnkgPC0gRmluZFRyYW5zZmVyQW5jaG9ycygKICByZWZlcmVuY2UgPSByZWZlcmVuY2VfaW50ZWdyYXRlZCwKICBxdWVyeSA9IHF1ZXJ5X2NlbGxzLAogIHJlZmVyZW5jZS5yZWR1Y3Rpb24gPSAicGNhIiwKICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJTQ1QiLAogIGRpbXMgPSAxOjE4CikKCiMgTWFwIHF1ZXJ5IGNlbGxzIHRvIHJlZmVyZW5jZQpxdWVyeV9tYXBwZWQgPC0gTWFwUXVlcnkoCiAgYW5jaG9yc2V0ID0gYW5jaG9yc19xdWVyeSwKICBxdWVyeSA9IHF1ZXJ5X2NlbGxzLAogIHJlZmVyZW5jZSA9IHJlZmVyZW5jZV9pbnRlZ3JhdGVkLAogIHJlZmRhdGEgPSBsaXN0KAogICAgcHNldWRvdGltZSA9ICJwc2V1ZG90aW1lIiwgICAgICAgICMgVHJhbnNmZXIgcHNldWRvdGltZQogICAgc2V1cmF0X2NsdXN0ZXJzID0gInNldXJhdF9jbHVzdGVycyIsICMgVHJhbnNmZXIgY2x1c3RlcnMKICAgIHRyYWplY3RvcnlfcG9zaXRpb24gPSAibW9ub2NsZTNfZW1iZWRkaW5nIiAjIElmIHN0b3JpbmcgTVNUIGNvb3JkcwogICksCiAgcmVmZXJlbmNlLnJlZHVjdGlvbiA9ICJwY2EiLCAKICByZWR1Y3Rpb24ubW9kZWwgPSAidW1hcCIKKQoKCmBgYAoKIyA3LiBWaXN1YWxpemF0aW9uIChQbG90IFJlZmVyZW5jZSBhbmQgSW5qZWN0ZWQgQ2VsbHMgYnkgUHNldWRvdGltZSBhbmQgQ2VsbCBMaW5lKQpgYGB7cn0KCiMgUGxvdCB0aGUgcmVmZXJlbmNlIHRyYWplY3RvcnkgVU1BUCwgY29sb3JlZCBieSBwc2V1ZG90aW1lCkRpbVBsb3QocmVmZXJlbmNlX2ludGVncmF0ZWQsIGdyb3VwLmJ5ID0gInBzZXVkb3RpbWUiLCByZWR1Y3Rpb24gPSAidW1hcCIpICsKICBnZ3RpdGxlKCJSZWZlcmVuY2UgQ0Q04oG6IFQgQ2VsbCBUcmFqZWN0b3J5IChQc2V1ZG90aW1lKSIpCgojIE92ZXJsYXkgaW5qZWN0ZWQgY2VsbCBsaW5lcwpEaW1QbG90KHF1ZXJ5X21hcHBlZCwgcmVkdWN0aW9uID0gInJlZi51bWFwIiwgZ3JvdXAuYnkgPSAiY2VsbF9saW5lIiwgbGFiZWwgPSBUUlVFKSArCiAgZ2d0aXRsZSgiSW5qZWN0ZWQgU8OpemFyeSBDZWxsIExpbmVzIG9uIFJlZmVyZW5jZSBUcmFqZWN0b3J5IikKCgpgYGAKCgojIyBWaXN1YWxpemF0aW9uICYgQW5hbHlzaXMKYGBge3J9CgojIENvbWJpbmVkIFVNQVAgd2l0aCBwc2V1ZG90aW1lCnAxIDwtIERpbVBsb3QocmVmZXJlbmNlX2ludGVncmF0ZWQsIAogICAgICAgICAgICAgIGdyb3VwLmJ5ID0gInBzZXVkb3RpbWUiLCAKICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIpICsgCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJtYWdtYSIpCgpwMiA8LSBEaW1QbG90KHF1ZXJ5X21hcHBlZCwgCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInJlZi51bWFwIiwgCiAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2VsbF9saW5lIiwgCiAgICAgICAgICAgICAgY29scyA9ICJkYXJrcmVkIikgKyAKICBnZ3RpdGxlKCJTw6l6YXJ5IENlbGwgTGluZXMiKQoKcDEgKyBwMgoKIyBQc2V1ZG90aW1lIGRpc3RyaWJ1dGlvbiBpbiBjZWxsIGxpbmVzClZsblBsb3QocXVlcnlfbWFwcGVkLCAKICAgICAgICBmZWF0dXJlcyA9ICJwc2V1ZG90aW1lIiwgCiAgICAgICAgZ3JvdXAuYnkgPSAiY2VsbF9saW5lIiwgCiAgICAgICAgcHQuc2l6ZSA9IDAuMSkgKwogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMikKCiMgUHJvamVjdCBjZWxsIGxpbmVzIG9uIHJlZmVyZW5jZSB0cmFqZWN0b3J5CkZlYXR1cmVQbG90KHF1ZXJ5X21hcHBlZCwgCiAgICAgICAgICAgZmVhdHVyZXMgPSAicHNldWRvdGltZSIsIAogICAgICAgICAgIHJlZHVjdGlvbiA9ICJyZWYudW1hcCIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBxdWVyeV9tYXBwZWRbW11dLCAKICAgICAgICAgICAgIGFlcyh4ID0gcmVmVU1BUF8xLCB5ID0gcmVmVU1BUF8yLCBjb2xvciA9IHBzZXVkb3RpbWUpLAogICAgICAgICAgICAgc2l6ZSA9IDEuNSkKCmBgYAojIyBBbmFseXplIFBzZXVkb3RpbWUgRGlzdHJpYnV0aW9ucyBBY3Jvc3MgQ2VsbCBMaW5lcwpgYGB7cn0KCgpGZWF0dXJlUGxvdChxdWVyeV9tYXBwZWQsIGZlYXR1cmVzID0gInBzZXVkb3RpbWUiLCByZWR1Y3Rpb24gPSAicmVmLnVtYXAiKSArCiAgZ2d0aXRsZSgiUHNldWRvdGltZSBWYWx1ZXMgb2YgSW5qZWN0ZWQgQ2VsbCBMaW5lcyIpCgpWbG5QbG90KHF1ZXJ5X21hcHBlZCwgZmVhdHVyZXMgPSAicHNldWRvdGltZSIsIGdyb3VwLmJ5ID0gImNlbGxfbGluZSIpICsKICBnZ3RpdGxlKCJQc2V1ZG90aW1lIERpc3RyaWJ1dGlvbiBieSBDZWxsIExpbmUiKQpgYGAKCiMjIFNhdmUgdGhlIG1hcHBlZCBxdWVyeSBvYmplY3QgKFPDqXphcnkgY2VsbCBsaW5lcyBwcm9qZWN0ZWQgb250byByZWZlcmVuY2UgdHJhamVjdG9yeSk6CmBgYHtyfQoKCnNhdmVSRFMocXVlcnlfbWFwcGVkLCBmaWxlID0gInNlemFyeV9jZWxsX2xpbmVzX21hcHBlZF90b19jZDRfcmVmZXJlbmNlLnJkcyIpCgpgYGAK