load libraries

1. Load Seurat Object



All_samples_Merged <- readRDS("../0-Seurat_RDS_OBJECT_FINAL/All_samples_Merged_with_STCAT_and_renamed_FINAL.rds")

2. Prepare Data

# Seurat object
sdata <- All_samples_Merged  

DefaultAssay(sdata) <- "RNA"

# Extract counts
data <- GetAssayData(sdata, assay = "RNA", slot = "counts")

# Metadata
cell_metadata <- sdata@meta.data

# Gene annotation
gene_annotation <- data.frame(gene_short_name = rownames(data))
rownames(gene_annotation) <- rownames(data)

# Create Monocle3 CDS
cds <- new_cell_data_set(data,
                         cell_metadata = cell_metadata,
                         gene_metadata = gene_annotation)

3. Preprocessing and Dimension Reduction


cds <- preprocess_cds(cds, num_dim = 20)
cds <- reduce_dimension(cds, preprocess_method = "PCA")

3.1 Use Seurat’s UMAP for Consistency


# Extract UMAP embeddings
cds.embed <- cds@int_colData$reducedDims$UMAP
int.embed <- Embeddings(sdata, reduction = "umap")
int.embed <- int.embed[rownames(cds.embed), ]

# Overwrite UMAP in CDS
cds@int_colData$reducedDims$UMAP <- int.embed


library(monocle3)

# Create a folder for the checkpoint
dir.create("cds_checkpoint_1", showWarnings = FALSE)

# Save everything
save_monocle_objects(cds=cds, directory_path='cds_checkpoint_1', comment='This is my example cds. Stored 2025-08-22.')

3.2 Transfer useful Seurat metadata to cds


# Common metadata fields you likely have:
md_to_copy <- c("seurat_clusters","Patient_origin","cell_line","orig.ident")

for (fld in md_to_copy) {
  if (fld %in% colnames(sdata@meta.data)) {
    cds[[fld]] <- sdata@meta.data[match(colnames(cds), colnames(sdata)), fld]
  }
}

3.3 Clustering, Graph Learning, and Pseudotime Ordering


# Cluster cells
cds <- cluster_cells(cds, reduction_method = "UMAP")

# Learn trajectory graph
cds <- learn_graph(cds)

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

  |                                                                                                    
  |                                                                                              |   0%
  |                                                                                                    
  |==============================================================================================| 100%
# Identify naive T cell markers available in CDS
naive_markers <- c("CCR7", "SELL", "LEF1", "TCF7")
naive_markers <- naive_markers[naive_markers %in% rownames(cds)]

# Extract expression matrix using logcounts assay if present, else log-transform counts
if("logcounts" %in% assayNames(cds)) {
expr_mat <- assay(cds, "logcounts")
} else {
expr_mat <- log1p(assay(cds, "counts"))
}

#Calculate mean naive marker expression (naive score) per cell
naive_score <- Matrix::colMeans(expr_mat[naive_markers, , drop = FALSE])

#Select root cells as those with naive score above 95th percentile threshold
threshold <- quantile(naive_score, 0.95)
root_cells <- names(naive_score[naive_score > threshold])


# Get closest principal graph nodes for root cells
closest_vertex <- cds@principal_graph_aux[["UMAP"]]$pr_graph_cell_proj_closest_vertex

# Map from cells → node names
root_nodes <- igraph::V(principal_graph(cds)[["UMAP"]])$name[
  as.numeric(closest_vertex[root_cells])
]

# Order cells using valid root_pr_nodes
cds <- order_cells(cds, root_cells = root_cells)

save CDS


library(monocle3)

# Create a folder for the checkpoint
dir.create("cds_checkpoint_2", showWarnings = FALSE)

# Save everything
save_monocle_objects(cds=cds, directory_path='cds_checkpoint_2', comment='This is my example cds. Stored 2025-08-22.')

4. Visualization of Trajectory


p1 <- plot_cells(cds,
                color_cells_by = "pseudotime",
                label_cell_groups = FALSE,
                label_leaves = FALSE,
                label_branch_points = FALSE,
                graph_label_size = 1.5)

p1


p2 <- plot_cells(cds,
                color_cells_by = "Patient_origin",
                label_cell_groups = FALSE,
                label_leaves = FALSE,
                label_branch_points = FALSE,
                graph_label_size = 1.5)

p2


p3 <- plot_cells(cds,
                color_cells_by = "orig.ident",
                label_cell_groups = FALSE,
                label_leaves = FALSE,
                label_branch_points = FALSE,
                graph_label_size = 1.5)

p3

Visualize Gene Expression Along Pseudotime

library(monocle3)
library(ggplot2)
library(dplyr)        # optional but commonly used for data manipulation

plot_genes_in_pseudotime(cds["CCR7", ])

plot_genes_in_pseudotime(cds["SELL", ])

plot_genes_in_pseudotime(cds["LEF1", ])

plot_genes_in_pseudotime(cds["TCF7", ])


#Output tells you: how canonical naïve markers decline/shift across pseudotime.

Explore Branch-Specific Differential Expression

## Identify genes that vary along branches
deg_by_branch <- graph_test(cds, neighbor_graph = "principal_graph", cores = 4)
deg_by_branch <- deg_by_branch %>% arrange(q_value)

save CDS


library(monocle3)

# Create a folder for the checkpoint
dir.create("cds_checkpoint_3", showWarnings = FALSE)

# Save everything
save_monocle_objects(cds=cds, directory_path='cds_checkpoint_3', comment='This is my example cds. Stored 2025-08-22.')

Explore Branch-Specific Differential Expression


## Select top DEGs per branch (q_value < 0.05)
top_degs <- deg_by_branch %>%
  filter(q_value < 0.05) %>%
  arrange(q_value)

# Optional: take top 50 for plotting
top50_genes <- head(top_degs$gene_short_name, 50)

## Plot pseudotime expression of top DEGs
plot_cells(cds[top50_genes, ], 
           genes=top50_genes, 
           show_trajectory_graph = TRUE, 
           label_cell_groups = FALSE)

#Output: Highlights branch-specific transcriptional programs and cell fate biases.

Explore Branch-Specific Differential Expression (genes one by one)

## Identify genes that vary along branches
deg_by_branch <- graph_test(cds, neighbor_graph = "principal_graph", cores = 4)

## Select top DEGs per branch (q_value < 0.05)
top_degs <- deg_by_branch %>%
  filter(q_value < 0.05) %>%
  arrange(q_value)

# Optional: take top 50 for plotting
top50_genes <- head(top_degs$gene_short_name, 50)

library(ggplot2)

for (gene in top50_genes) {
  p <- plot_cells(cds[gene, ], 
                  genes = gene,
                  show_trajectory_graph = TRUE,
                  label_cell_groups = FALSE) +
       ggtitle(gene)
  print(p)
}

Summarize Lineages and Cell Fate Branches


cds <- cluster_cells(cds)
p_branches <- plot_cells(cds, color_cells_by = "cluster")
print(p_branches)

Visualize Gene Modules Along Pseudotime


## Identify co-expressed gene modules
gene_modules <- find_gene_modules(cds, resolution = 1e-2)

## Explore first module along trajectory
plot_cells(cds, 
           genes = gene_modules[[1]], 
           show_trajectory_graph = FALSE, 
           label_cell_groups = FALSE) +
  ggtitle("Gene Module 1 Expression Along Trajectory")

## Optional: loop over modules to visualize multiple
lapply(seq_along(gene_modules)[1:5], function(i) {
  plot_cells(cds, genes = gene_modules[[i]], show_trajectory_graph = FALSE)
})

Multi-Gene Pseudotime Plot with Facets


## Multi-Gene Pseudotime Plots by Functional Marker Groups

library(ggplot2)

# Define marker groups
marker_groups <- list(
  "CD4 Tex (Exhausted)" = c("TOX", "LAG3", "CTLA4", "TIGIT"),
  "CD4 Treg" = c("FOXP3", "IL2RA", "CTLA4", "IKZF2", "TIGIT"),
  "CD4 Tcm (Central Memory)" = c("CCR7", "SELL", "IL7R", "TCF7"),
  "CD4 Tn (Naive)" = c("CCR7", "SELL", "IL7R", "TCF7", "LEF1"),
  "CD4 Tem (Effector Memory)" = c("GZMB", "PRF1", "IFNG", "KLRG1"),
  "CD4 Trm (Tissue Resident)" = c("CD69", "CXCR6"),
  "CD4 Tc (Cytotoxic)" = c("PRF1", "GZMB", "NKG7", "GNLY"),
  "CD4 Tisg (IFN Signature)" = c("ISG15", "IFI6", "IFIT3", "MX1"),
  "CD4 Proliferation" = c("MKI67", "TOP2A"),
  "CD4 Th17" = c("STAT3", "AHR", "CCR6", "BATF"),
  "CD4 Temra (Effector Memory RA+)" = c("PTPRC", "GZMB"),
  "CD4 Tfh (Follicular Helper)" = c("BCL6", "ICOS"),
  "CD4 Tstr (Stress)" = c("HSPA1A", "ATF3"),
  "CD4 Activated" = c("IL2RA", "CD69", "HLA-DRA")
)

# Loop through each marker group and plot
for (group_name in names(marker_groups)) {
  
  genes_to_plot <- marker_groups[[group_name]]
  # Keep only genes present in CDS
  genes_to_plot <- genes_to_plot[genes_to_plot %in% rownames(cds)]
  
  if (length(genes_to_plot) > 0) {
    p <- plot_cells(cds[genes_to_plot, ], 
                    genes = genes_to_plot, 
                    show_trajectory_graph = FALSE) +
         facet_wrap(~ gene_short_name, scales = "free_y") +
         ggtitle(paste0(group_name, " Marker Dynamics Along Pseudotime"))
    
    print(p)
  }
}

# ✅ Output:
#   One faceted pseudotime plot per functional T-cell program.
#   Each figure shows expression dynamics of the group’s markers across pseudotime.

Multi-Gene Pseudotime Plot with Facets


library(ggplot2)

# Example gene list
genes_to_plot 

# Keep only genes present in CDS
genes_to_plot <- genes_to_plot[genes_to_plot %in% rownames(cds)]

plot_cells(cds[genes_to_plot, ], 
           genes = genes_to_plot, 
           show_trajectory_graph = FALSE) +
  facet_wrap(~ gene_short_name, scales = "free_y") +
  ggtitle("Naive T Cell Marker Dynamics Along Pseudotime")

#Output: Clear visualization of each gene’s dynamics along pseudotime.

Optional: Overlay CITE-seq Protein Expression


# Example: if your Seurat object has CITE-seq (ADT) data
adt_markers <- c("adt_CD45RA", "adt_CD62L")  # adjust to your panel
for(marker in adt_markers){
  plot_cells(cds, 
             color_cells_by = marker, 
             show_trajectory_graph = TRUE) +
    ggtitle(paste("ADT Expression:", marker))
}

Pathway Enrichment of Top DEGs or Gene Modules


library(clusterProfiler)
library(org.Hs.eg.db)

# Convert gene symbols to Entrez IDs
entrez_ids <- bitr(top50_genes, fromType="SYMBOL", toType="ENTREZID", OrgDb="org.Hs.eg.db")

# KEGG enrichment
kegg_res <- enrichKEGG(entrez_ids$ENTREZID, organism="hsa")

# Visualize top pathways
dotplot(kegg_res, showCategory=10) + ggtitle("KEGG Pathways for Top DEGs")

# Output: Biological interpretation of branches (e.g., proliferation, T-cell activation, immune evasion).

Branch Probability / Fate Bias

library(igraph)

# Identify branch points
branch_nodes <- branch_nodes(cds)

# Map each cell to branch node
cell_branches <- sapply(branch_nodes, function(node){
  igraph::shortest_paths(principal_graph(cds)[["UMAP"]], 
                         from = node, to = colnames(cds))$vpath
})

# Visualize branch probabilities along trajectory
plot_cells(cds, color_cells_by = "pseudotime", label_branch_points = TRUE)
LS0tCnRpdGxlOiAiVHJhamVjdG9yeSBhbmFseXNpcyAoTW9ub2NsZTMpIgphdXRob3I6ICJOYXNpciBNYWhtb29kIEFiYmFzaSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2NvbGxhcHNlZDogeWVzCiAgd29yZF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9Cm9wdGlvbnMoc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpzZXQuc2VlZCgxMjMpCmBgYAoKCiMjIGxvYWQgbGlicmFyaWVzCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShtb25vY2xlMykKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoU0NwdWJyKQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShvcmcuSHMuZWcuZGIpCmxpYnJhcnkoaWdyYXBoKQoKYGBgCgoKIyMgMS4gTG9hZCBTZXVyYXQgT2JqZWN0IApgYGB7cn0KCgpBbGxfc2FtcGxlc19NZXJnZWQgPC0gcmVhZFJEUygiLi4vMC1TZXVyYXRfUkRTX09CSkVDVF9GSU5BTC9BbGxfc2FtcGxlc19NZXJnZWRfd2l0aF9TVENBVF9hbmRfcmVuYW1lZF9GSU5BTC5yZHMiKQoKCmBgYAoKCiMjIDIuIFByZXBhcmUgRGF0YQpgYGB7cn0KIyBTZXVyYXQgb2JqZWN0CnNkYXRhIDwtIEFsbF9zYW1wbGVzX01lcmdlZCAgCgpEZWZhdWx0QXNzYXkoc2RhdGEpIDwtICJSTkEiCgojIEV4dHJhY3QgY291bnRzCmRhdGEgPC0gR2V0QXNzYXlEYXRhKHNkYXRhLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpCgojIE1ldGFkYXRhCmNlbGxfbWV0YWRhdGEgPC0gc2RhdGFAbWV0YS5kYXRhCgojIEdlbmUgYW5ub3RhdGlvbgpnZW5lX2Fubm90YXRpb24gPC0gZGF0YS5mcmFtZShnZW5lX3Nob3J0X25hbWUgPSByb3duYW1lcyhkYXRhKSkKcm93bmFtZXMoZ2VuZV9hbm5vdGF0aW9uKSA8LSByb3duYW1lcyhkYXRhKQoKIyBDcmVhdGUgTW9ub2NsZTMgQ0RTCmNkcyA8LSBuZXdfY2VsbF9kYXRhX3NldChkYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgY2VsbF9tZXRhZGF0YSA9IGNlbGxfbWV0YWRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX21ldGFkYXRhID0gZ2VuZV9hbm5vdGF0aW9uKQoKCmBgYAoKCiMjIDMuIFByZXByb2Nlc3NpbmcgYW5kIERpbWVuc2lvbiBSZWR1Y3Rpb24KYGBge3J9CgpjZHMgPC0gcHJlcHJvY2Vzc19jZHMoY2RzLCBudW1fZGltID0gMjApCmNkcyA8LSByZWR1Y2VfZGltZW5zaW9uKGNkcywgcHJlcHJvY2Vzc19tZXRob2QgPSAiUENBIikKCmBgYAoKIyMjIDMuMSBVc2UgU2V1cmF04oCZcyBVTUFQIGZvciBDb25zaXN0ZW5jeQpgYGB7cn0KCiMgRXh0cmFjdCBVTUFQIGVtYmVkZGluZ3MKY2RzLmVtYmVkIDwtIGNkc0BpbnRfY29sRGF0YSRyZWR1Y2VkRGltcyRVTUFQCmludC5lbWJlZCA8LSBFbWJlZGRpbmdzKHNkYXRhLCByZWR1Y3Rpb24gPSAidW1hcCIpCmludC5lbWJlZCA8LSBpbnQuZW1iZWRbcm93bmFtZXMoY2RzLmVtYmVkKSwgXQoKIyBPdmVyd3JpdGUgVU1BUCBpbiBDRFMKY2RzQGludF9jb2xEYXRhJHJlZHVjZWREaW1zJFVNQVAgPC0gaW50LmVtYmVkCgoKbGlicmFyeShtb25vY2xlMykKCiMgQ3JlYXRlIGEgZm9sZGVyIGZvciB0aGUgY2hlY2twb2ludApkaXIuY3JlYXRlKCJjZHNfY2hlY2twb2ludF8xIiwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCgojIFNhdmUgZXZlcnl0aGluZwpzYXZlX21vbm9jbGVfb2JqZWN0cyhjZHM9Y2RzLCBkaXJlY3RvcnlfcGF0aD0nY2RzX2NoZWNrcG9pbnRfMScsIGNvbW1lbnQ9J1RoaXMgaXMgbXkgZXhhbXBsZSBjZHMuIFN0b3JlZCAyMDI1LTA4LTIyLicpCgpgYGAKCiMjIyAzLjIgVHJhbnNmZXIgdXNlZnVsIFNldXJhdCBtZXRhZGF0YSB0byBjZHMKYGBge3J9CgojIENvbW1vbiBtZXRhZGF0YSBmaWVsZHMgeW91IGxpa2VseSBoYXZlOgptZF90b19jb3B5IDwtIGMoInNldXJhdF9jbHVzdGVycyIsIlBhdGllbnRfb3JpZ2luIiwiY2VsbF9saW5lIiwib3JpZy5pZGVudCIpCgpmb3IgKGZsZCBpbiBtZF90b19jb3B5KSB7CiAgaWYgKGZsZCAlaW4lIGNvbG5hbWVzKHNkYXRhQG1ldGEuZGF0YSkpIHsKICAgIGNkc1tbZmxkXV0gPC0gc2RhdGFAbWV0YS5kYXRhW21hdGNoKGNvbG5hbWVzKGNkcyksIGNvbG5hbWVzKHNkYXRhKSksIGZsZF0KICB9Cn0KCmBgYAoKCiMjIyAzLjMgQ2x1c3RlcmluZywgR3JhcGggTGVhcm5pbmcsIGFuZCBQc2V1ZG90aW1lIE9yZGVyaW5nCmBgYHtyfQoKIyBDbHVzdGVyIGNlbGxzCmNkcyA8LSBjbHVzdGVyX2NlbGxzKGNkcywgcmVkdWN0aW9uX21ldGhvZCA9ICJVTUFQIikKCiMgTGVhcm4gdHJhamVjdG9yeSBncmFwaApjZHMgPC0gbGVhcm5fZ3JhcGgoY2RzKQoKCgoKIyBJZGVudGlmeSBuYWl2ZSBUIGNlbGwgbWFya2VycyBhdmFpbGFibGUgaW4gQ0RTCm5haXZlX21hcmtlcnMgPC0gYygiQ0NSNyIsICJTRUxMIiwgIkxFRjEiLCAiVENGNyIpCm5haXZlX21hcmtlcnMgPC0gbmFpdmVfbWFya2Vyc1tuYWl2ZV9tYXJrZXJzICVpbiUgcm93bmFtZXMoY2RzKV0KCiMgRXh0cmFjdCBleHByZXNzaW9uIG1hdHJpeCB1c2luZyBsb2djb3VudHMgYXNzYXkgaWYgcHJlc2VudCwgZWxzZSBsb2ctdHJhbnNmb3JtIGNvdW50cwppZigibG9nY291bnRzIiAlaW4lIGFzc2F5TmFtZXMoY2RzKSkgewpleHByX21hdCA8LSBhc3NheShjZHMsICJsb2djb3VudHMiKQp9IGVsc2UgewpleHByX21hdCA8LSBsb2cxcChhc3NheShjZHMsICJjb3VudHMiKSkKfQoKI0NhbGN1bGF0ZSBtZWFuIG5haXZlIG1hcmtlciBleHByZXNzaW9uIChuYWl2ZSBzY29yZSkgcGVyIGNlbGwKbmFpdmVfc2NvcmUgPC0gTWF0cml4Ojpjb2xNZWFucyhleHByX21hdFtuYWl2ZV9tYXJrZXJzLCAsIGRyb3AgPSBGQUxTRV0pCgojU2VsZWN0IHJvb3QgY2VsbHMgYXMgdGhvc2Ugd2l0aCBuYWl2ZSBzY29yZSBhYm92ZSA5NXRoIHBlcmNlbnRpbGUgdGhyZXNob2xkCnRocmVzaG9sZCA8LSBxdWFudGlsZShuYWl2ZV9zY29yZSwgMC45NSkKcm9vdF9jZWxscyA8LSBuYW1lcyhuYWl2ZV9zY29yZVtuYWl2ZV9zY29yZSA+IHRocmVzaG9sZF0pCgoKIyBHZXQgY2xvc2VzdCBwcmluY2lwYWwgZ3JhcGggbm9kZXMgZm9yIHJvb3QgY2VsbHMKY2xvc2VzdF92ZXJ0ZXggPC0gY2RzQHByaW5jaXBhbF9ncmFwaF9hdXhbWyJVTUFQIl1dJHByX2dyYXBoX2NlbGxfcHJval9jbG9zZXN0X3ZlcnRleAoKIyBNYXAgZnJvbSBjZWxscyDihpIgbm9kZSBuYW1lcwpyb290X25vZGVzIDwtIGlncmFwaDo6VihwcmluY2lwYWxfZ3JhcGgoY2RzKVtbIlVNQVAiXV0pJG5hbWVbCiAgYXMubnVtZXJpYyhjbG9zZXN0X3ZlcnRleFtyb290X2NlbGxzXSkKXQoKIyBPcmRlciBjZWxscyB1c2luZyB2YWxpZCByb290X3ByX25vZGVzCmNkcyA8LSBvcmRlcl9jZWxscyhjZHMsIHJvb3RfY2VsbHMgPSByb290X2NlbGxzKQoKCmBgYAoKIyMjICBzYXZlIENEUwpgYGB7cn0KCmxpYnJhcnkobW9ub2NsZTMpCgojIENyZWF0ZSBhIGZvbGRlciBmb3IgdGhlIGNoZWNrcG9pbnQKZGlyLmNyZWF0ZSgiY2RzX2NoZWNrcG9pbnRfMiIsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQoKIyBTYXZlIGV2ZXJ5dGhpbmcKc2F2ZV9tb25vY2xlX29iamVjdHMoY2RzPWNkcywgZGlyZWN0b3J5X3BhdGg9J2Nkc19jaGVja3BvaW50XzInLCBjb21tZW50PSdUaGlzIGlzIG15IGV4YW1wbGUgY2RzLiBTdG9yZWQgMjAyNS0wOC0yMi4nKQoKYGBgCgoKIyMgNC4gVmlzdWFsaXphdGlvbiBvZiBUcmFqZWN0b3J5CmBgYHtyfQoKcDEgPC0gcGxvdF9jZWxscyhjZHMsCiAgICAgICAgICAgICAgICBjb2xvcl9jZWxsc19ieSA9ICJwc2V1ZG90aW1lIiwKICAgICAgICAgICAgICAgIGxhYmVsX2NlbGxfZ3JvdXBzID0gRkFMU0UsCiAgICAgICAgICAgICAgICBsYWJlbF9sZWF2ZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGxhYmVsX2JyYW5jaF9wb2ludHMgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGdyYXBoX2xhYmVsX3NpemUgPSAxLjUpCgpwMQoKcDIgPC0gcGxvdF9jZWxscyhjZHMsCiAgICAgICAgICAgICAgICBjb2xvcl9jZWxsc19ieSA9ICJQYXRpZW50X29yaWdpbiIsCiAgICAgICAgICAgICAgICBsYWJlbF9jZWxsX2dyb3VwcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgbGFiZWxfbGVhdmVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICBsYWJlbF9icmFuY2hfcG9pbnRzID0gRkFMU0UsCiAgICAgICAgICAgICAgICBncmFwaF9sYWJlbF9zaXplID0gMS41KQoKcDIKCnAzIDwtIHBsb3RfY2VsbHMoY2RzLAogICAgICAgICAgICAgICAgY29sb3JfY2VsbHNfYnkgPSAib3JpZy5pZGVudCIsCiAgICAgICAgICAgICAgICBsYWJlbF9jZWxsX2dyb3VwcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgbGFiZWxfbGVhdmVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICBsYWJlbF9icmFuY2hfcG9pbnRzID0gRkFMU0UsCiAgICAgICAgICAgICAgICBncmFwaF9sYWJlbF9zaXplID0gMS41KQoKcDMKYGBgCgojIyBWaXN1YWxpemUgR2VuZSBFeHByZXNzaW9uIEFsb25nIFBzZXVkb3RpbWUKYGBge3J9CmxpYnJhcnkobW9ub2NsZTMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikgICAgICAgICMgb3B0aW9uYWwgYnV0IGNvbW1vbmx5IHVzZWQgZm9yIGRhdGEgbWFuaXB1bGF0aW9uCgpwbG90X2dlbmVzX2luX3BzZXVkb3RpbWUoY2RzWyJDQ1I3IiwgXSkKcGxvdF9nZW5lc19pbl9wc2V1ZG90aW1lKGNkc1siU0VMTCIsIF0pCnBsb3RfZ2VuZXNfaW5fcHNldWRvdGltZShjZHNbIkxFRjEiLCBdKQpwbG90X2dlbmVzX2luX3BzZXVkb3RpbWUoY2RzWyJUQ0Y3IiwgXSkKCiNPdXRwdXQgdGVsbHMgeW91OiBob3cgY2Fub25pY2FsIG5hw692ZSBtYXJrZXJzIGRlY2xpbmUvc2hpZnQgYWNyb3NzIHBzZXVkb3RpbWUuCmBgYAoKCiMjIEV4cGxvcmUgQnJhbmNoLVNwZWNpZmljIERpZmZlcmVudGlhbCBFeHByZXNzaW9uCmBgYHtyLCBmaWcuaGVpZ2h0PTE2LCBmaWcud2lkdGg9MjB9CiMjIElkZW50aWZ5IGdlbmVzIHRoYXQgdmFyeSBhbG9uZyBicmFuY2hlcwpkZWdfYnlfYnJhbmNoIDwtIGdyYXBoX3Rlc3QoY2RzLCBuZWlnaGJvcl9ncmFwaCA9ICJwcmluY2lwYWxfZ3JhcGgiLCBjb3JlcyA9IDQpCmRlZ19ieV9icmFuY2ggPC0gZGVnX2J5X2JyYW5jaCAlPiUgYXJyYW5nZShxX3ZhbHVlKQpgYGAKCiMjIyAgc2F2ZSBDRFMKYGBge3IsIHNhdmVSRFN9CgpsaWJyYXJ5KG1vbm9jbGUzKQoKIyBDcmVhdGUgYSBmb2xkZXIgZm9yIHRoZSBjaGVja3BvaW50CmRpci5jcmVhdGUoImNkc19jaGVja3BvaW50XzMiLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKCiMgU2F2ZSBldmVyeXRoaW5nCnNhdmVfbW9ub2NsZV9vYmplY3RzKGNkcz1jZHMsIGRpcmVjdG9yeV9wYXRoPSdjZHNfY2hlY2twb2ludF8zJywgY29tbWVudD0nVGhpcyBpcyBteSBleGFtcGxlIGNkcy4gU3RvcmVkIDIwMjUtMDgtMjIuJykKCmBgYAoKIyMgRXhwbG9yZSBCcmFuY2gtU3BlY2lmaWMgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24KYGBge3IsIGZpZy5oZWlnaHQ9MTYsIGZpZy53aWR0aD0yMH0KCiMjIFNlbGVjdCB0b3AgREVHcyBwZXIgYnJhbmNoIChxX3ZhbHVlIDwgMC4wNSkKdG9wX2RlZ3MgPC0gZGVnX2J5X2JyYW5jaCAlPiUKICBmaWx0ZXIocV92YWx1ZSA8IDAuMDUpICU+JQogIGFycmFuZ2UocV92YWx1ZSkKCiMgT3B0aW9uYWw6IHRha2UgdG9wIDUwIGZvciBwbG90dGluZwp0b3A1MF9nZW5lcyA8LSBoZWFkKHRvcF9kZWdzJGdlbmVfc2hvcnRfbmFtZSwgNTApCgojIyBQbG90IHBzZXVkb3RpbWUgZXhwcmVzc2lvbiBvZiB0b3AgREVHcwpwbG90X2NlbGxzKGNkc1t0b3A1MF9nZW5lcywgXSwgCiAgICAgICAgICAgZ2VuZXM9dG9wNTBfZ2VuZXMsIAogICAgICAgICAgIHNob3dfdHJhamVjdG9yeV9ncmFwaCA9IFRSVUUsIAogICAgICAgICAgIGxhYmVsX2NlbGxfZ3JvdXBzID0gRkFMU0UpCgojT3V0cHV0OiBIaWdobGlnaHRzIGJyYW5jaC1zcGVjaWZpYyB0cmFuc2NyaXB0aW9uYWwgcHJvZ3JhbXMgYW5kIGNlbGwgZmF0ZSBiaWFzZXMuCgpgYGAKCiMjIEV4cGxvcmUgQnJhbmNoLVNwZWNpZmljIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIChnZW5lcyBvbmUgYnkgb25lKQpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CiMjIElkZW50aWZ5IGdlbmVzIHRoYXQgdmFyeSBhbG9uZyBicmFuY2hlcwpkZWdfYnlfYnJhbmNoIDwtIGdyYXBoX3Rlc3QoY2RzLCBuZWlnaGJvcl9ncmFwaCA9ICJwcmluY2lwYWxfZ3JhcGgiLCBjb3JlcyA9IDQpCgojIyBTZWxlY3QgdG9wIERFR3MgcGVyIGJyYW5jaCAocV92YWx1ZSA8IDAuMDUpCnRvcF9kZWdzIDwtIGRlZ19ieV9icmFuY2ggJT4lCiAgZmlsdGVyKHFfdmFsdWUgPCAwLjA1KSAlPiUKICBhcnJhbmdlKHFfdmFsdWUpCgojIE9wdGlvbmFsOiB0YWtlIHRvcCA1MCBmb3IgcGxvdHRpbmcKdG9wNTBfZ2VuZXMgPC0gaGVhZCh0b3BfZGVncyRnZW5lX3Nob3J0X25hbWUsIDUwKQoKbGlicmFyeShnZ3Bsb3QyKQoKZm9yIChnZW5lIGluIHRvcDUwX2dlbmVzKSB7CiAgcCA8LSBwbG90X2NlbGxzKGNkc1tnZW5lLCBdLCAKICAgICAgICAgICAgICAgICAgZ2VuZXMgPSBnZW5lLAogICAgICAgICAgICAgICAgICBzaG93X3RyYWplY3RvcnlfZ3JhcGggPSBUUlVFLAogICAgICAgICAgICAgICAgICBsYWJlbF9jZWxsX2dyb3VwcyA9IEZBTFNFKSArCiAgICAgICBnZ3RpdGxlKGdlbmUpCiAgcHJpbnQocCkKfQoKCgpgYGAKCiMjIFN1bW1hcml6ZSBMaW5lYWdlcyBhbmQgQ2VsbCBGYXRlIEJyYW5jaGVzCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCmNkcyA8LSBjbHVzdGVyX2NlbGxzKGNkcykKcF9icmFuY2hlcyA8LSBwbG90X2NlbGxzKGNkcywgY29sb3JfY2VsbHNfYnkgPSAiY2x1c3RlciIpCnByaW50KHBfYnJhbmNoZXMpCgpgYGAKCiMjIFZpc3VhbGl6ZSBHZW5lIE1vZHVsZXMgQWxvbmcgUHNldWRvdGltZQpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgojIyBJZGVudGlmeSBjby1leHByZXNzZWQgZ2VuZSBtb2R1bGVzCmdlbmVfbW9kdWxlcyA8LSBmaW5kX2dlbmVfbW9kdWxlcyhjZHMsIHJlc29sdXRpb24gPSAxZS0yKQoKIyMgRXhwbG9yZSBmaXJzdCBtb2R1bGUgYWxvbmcgdHJhamVjdG9yeQpwbG90X2NlbGxzKGNkcywgCiAgICAgICAgICAgZ2VuZXMgPSBnZW5lX21vZHVsZXNbWzFdXSwgCiAgICAgICAgICAgc2hvd190cmFqZWN0b3J5X2dyYXBoID0gRkFMU0UsIAogICAgICAgICAgIGxhYmVsX2NlbGxfZ3JvdXBzID0gRkFMU0UpICsKICBnZ3RpdGxlKCJHZW5lIE1vZHVsZSAxIEV4cHJlc3Npb24gQWxvbmcgVHJhamVjdG9yeSIpCgojIyBPcHRpb25hbDogbG9vcCBvdmVyIG1vZHVsZXMgdG8gdmlzdWFsaXplIG11bHRpcGxlCmxhcHBseShzZXFfYWxvbmcoZ2VuZV9tb2R1bGVzKVsxOjVdLCBmdW5jdGlvbihpKSB7CiAgcGxvdF9jZWxscyhjZHMsIGdlbmVzID0gZ2VuZV9tb2R1bGVzW1tpXV0sIHNob3dfdHJhamVjdG9yeV9ncmFwaCA9IEZBTFNFKQp9KQoKCgpgYGAKCgojIyBNdWx0aS1HZW5lIFBzZXVkb3RpbWUgUGxvdCB3aXRoIEZhY2V0cwpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgojIyBNdWx0aS1HZW5lIFBzZXVkb3RpbWUgUGxvdHMgYnkgRnVuY3Rpb25hbCBNYXJrZXIgR3JvdXBzCgpsaWJyYXJ5KGdncGxvdDIpCgojIERlZmluZSBtYXJrZXIgZ3JvdXBzCm1hcmtlcl9ncm91cHMgPC0gbGlzdCgKICAiQ0Q0IFRleCAoRXhoYXVzdGVkKSIgPSBjKCJUT1giLCAiTEFHMyIsICJDVExBNCIsICJUSUdJVCIpLAogICJDRDQgVHJlZyIgPSBjKCJGT1hQMyIsICJJTDJSQSIsICJDVExBNCIsICJJS1pGMiIsICJUSUdJVCIpLAogICJDRDQgVGNtIChDZW50cmFsIE1lbW9yeSkiID0gYygiQ0NSNyIsICJTRUxMIiwgIklMN1IiLCAiVENGNyIpLAogICJDRDQgVG4gKE5haXZlKSIgPSBjKCJDQ1I3IiwgIlNFTEwiLCAiSUw3UiIsICJUQ0Y3IiwgIkxFRjEiKSwKICAiQ0Q0IFRlbSAoRWZmZWN0b3IgTWVtb3J5KSIgPSBjKCJHWk1CIiwgIlBSRjEiLCAiSUZORyIsICJLTFJHMSIpLAogICJDRDQgVHJtIChUaXNzdWUgUmVzaWRlbnQpIiA9IGMoIkNENjkiLCAiQ1hDUjYiKSwKICAiQ0Q0IFRjIChDeXRvdG94aWMpIiA9IGMoIlBSRjEiLCAiR1pNQiIsICJOS0c3IiwgIkdOTFkiKSwKICAiQ0Q0IFRpc2cgKElGTiBTaWduYXR1cmUpIiA9IGMoIklTRzE1IiwgIklGSTYiLCAiSUZJVDMiLCAiTVgxIiksCiAgIkNENCBQcm9saWZlcmF0aW9uIiA9IGMoIk1LSTY3IiwgIlRPUDJBIiksCiAgIkNENCBUaDE3IiA9IGMoIlNUQVQzIiwgIkFIUiIsICJDQ1I2IiwgIkJBVEYiKSwKICAiQ0Q0IFRlbXJhIChFZmZlY3RvciBNZW1vcnkgUkErKSIgPSBjKCJQVFBSQyIsICJHWk1CIiksCiAgIkNENCBUZmggKEZvbGxpY3VsYXIgSGVscGVyKSIgPSBjKCJCQ0w2IiwgIklDT1MiKSwKICAiQ0Q0IFRzdHIgKFN0cmVzcykiID0gYygiSFNQQTFBIiwgIkFURjMiKSwKICAiQ0Q0IEFjdGl2YXRlZCIgPSBjKCJJTDJSQSIsICJDRDY5IiwgIkhMQS1EUkEiKQopCgojIExvb3AgdGhyb3VnaCBlYWNoIG1hcmtlciBncm91cCBhbmQgcGxvdApmb3IgKGdyb3VwX25hbWUgaW4gbmFtZXMobWFya2VyX2dyb3VwcykpIHsKICAKICBnZW5lc190b19wbG90IDwtIG1hcmtlcl9ncm91cHNbW2dyb3VwX25hbWVdXQogICMgS2VlcCBvbmx5IGdlbmVzIHByZXNlbnQgaW4gQ0RTCiAgZ2VuZXNfdG9fcGxvdCA8LSBnZW5lc190b19wbG90W2dlbmVzX3RvX3Bsb3QgJWluJSByb3duYW1lcyhjZHMpXQogIAogIGlmIChsZW5ndGgoZ2VuZXNfdG9fcGxvdCkgPiAwKSB7CiAgICBwIDwtIHBsb3RfY2VsbHMoY2RzW2dlbmVzX3RvX3Bsb3QsIF0sIAogICAgICAgICAgICAgICAgICAgIGdlbmVzID0gZ2VuZXNfdG9fcGxvdCwgCiAgICAgICAgICAgICAgICAgICAgc2hvd190cmFqZWN0b3J5X2dyYXBoID0gRkFMU0UpICsKICAgICAgICAgZmFjZXRfd3JhcCh+IGdlbmVfc2hvcnRfbmFtZSwgc2NhbGVzID0gImZyZWVfeSIpICsKICAgICAgICAgZ2d0aXRsZShwYXN0ZTAoZ3JvdXBfbmFtZSwgIiBNYXJrZXIgRHluYW1pY3MgQWxvbmcgUHNldWRvdGltZSIpKQogICAgCiAgICBwcmludChwKQogIH0KfQoKIyDinIUgT3V0cHV0OgojICAgT25lIGZhY2V0ZWQgcHNldWRvdGltZSBwbG90IHBlciBmdW5jdGlvbmFsIFQtY2VsbCBwcm9ncmFtLgojICAgRWFjaCBmaWd1cmUgc2hvd3MgZXhwcmVzc2lvbiBkeW5hbWljcyBvZiB0aGUgZ3JvdXDigJlzIG1hcmtlcnMgYWNyb3NzIHBzZXVkb3RpbWUuCgoKYGBgCgojIyBNdWx0aS1HZW5lIFBzZXVkb3RpbWUgUGxvdCB3aXRoIEZhY2V0cwpgYGB7cn0KCmxpYnJhcnkoZ2dwbG90MikKCiMgRXhhbXBsZSBnZW5lIGxpc3QKZ2VuZXNfdG9fcGxvdCAKCiMgS2VlcCBvbmx5IGdlbmVzIHByZXNlbnQgaW4gQ0RTCmdlbmVzX3RvX3Bsb3QgPC0gZ2VuZXNfdG9fcGxvdFtnZW5lc190b19wbG90ICVpbiUgcm93bmFtZXMoY2RzKV0KCnBsb3RfY2VsbHMoY2RzW2dlbmVzX3RvX3Bsb3QsIF0sIAogICAgICAgICAgIGdlbmVzID0gZ2VuZXNfdG9fcGxvdCwgCiAgICAgICAgICAgc2hvd190cmFqZWN0b3J5X2dyYXBoID0gRkFMU0UpICsKICBmYWNldF93cmFwKH4gZ2VuZV9zaG9ydF9uYW1lLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGdndGl0bGUoIk5haXZlIFQgQ2VsbCBNYXJrZXIgRHluYW1pY3MgQWxvbmcgUHNldWRvdGltZSIpCgojT3V0cHV0OiBDbGVhciB2aXN1YWxpemF0aW9uIG9mIGVhY2ggZ2VuZeKAmXMgZHluYW1pY3MgYWxvbmcgcHNldWRvdGltZS4KCmBgYAoKIyMgT3B0aW9uYWw6IE92ZXJsYXkgQ0lURS1zZXEgUHJvdGVpbiBFeHByZXNzaW9uCmBgYHtyfQoKIyBFeGFtcGxlOiBpZiB5b3VyIFNldXJhdCBvYmplY3QgaGFzIENJVEUtc2VxIChBRFQpIGRhdGEKYWR0X21hcmtlcnMgPC0gYygiYWR0X0NENDVSQSIsICJhZHRfQ0Q2MkwiKSAgIyBhZGp1c3QgdG8geW91ciBwYW5lbApmb3IobWFya2VyIGluIGFkdF9tYXJrZXJzKXsKICBwbG90X2NlbGxzKGNkcywgCiAgICAgICAgICAgICBjb2xvcl9jZWxsc19ieSA9IG1hcmtlciwgCiAgICAgICAgICAgICBzaG93X3RyYWplY3RvcnlfZ3JhcGggPSBUUlVFKSArCiAgICBnZ3RpdGxlKHBhc3RlKCJBRFQgRXhwcmVzc2lvbjoiLCBtYXJrZXIpKQp9CgoKYGBgCgoKIyMgUGF0aHdheSBFbnJpY2htZW50IG9mIFRvcCBERUdzIG9yIEdlbmUgTW9kdWxlcwpgYGB7cn0KCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKCiMgQ29udmVydCBnZW5lIHN5bWJvbHMgdG8gRW50cmV6IElEcwplbnRyZXpfaWRzIDwtIGJpdHIodG9wNTBfZ2VuZXMsIGZyb21UeXBlPSJTWU1CT0wiLCB0b1R5cGU9IkVOVFJFWklEIiwgT3JnRGI9Im9yZy5Icy5lZy5kYiIpCgojIEtFR0cgZW5yaWNobWVudAprZWdnX3JlcyA8LSBlbnJpY2hLRUdHKGVudHJlel9pZHMkRU5UUkVaSUQsIG9yZ2FuaXNtPSJoc2EiKQoKIyBWaXN1YWxpemUgdG9wIHBhdGh3YXlzCmRvdHBsb3Qoa2VnZ19yZXMsIHNob3dDYXRlZ29yeT0xMCkgKyBnZ3RpdGxlKCJLRUdHIFBhdGh3YXlzIGZvciBUb3AgREVHcyIpCgojIE91dHB1dDogQmlvbG9naWNhbCBpbnRlcnByZXRhdGlvbiBvZiBicmFuY2hlcyAoZS5nLiwgcHJvbGlmZXJhdGlvbiwgVC1jZWxsIGFjdGl2YXRpb24sIGltbXVuZSBldmFzaW9uKS4KYGBgCgojIyBCcmFuY2ggUHJvYmFiaWxpdHkgLyBGYXRlIEJpYXMKYGBge3J9CmxpYnJhcnkoaWdyYXBoKQoKIyBJZGVudGlmeSBicmFuY2ggcG9pbnRzCmJyYW5jaF9ub2RlcyA8LSBicmFuY2hfbm9kZXMoY2RzKQoKIyBNYXAgZWFjaCBjZWxsIHRvIGJyYW5jaCBub2RlCmNlbGxfYnJhbmNoZXMgPC0gc2FwcGx5KGJyYW5jaF9ub2RlcywgZnVuY3Rpb24obm9kZSl7CiAgaWdyYXBoOjpzaG9ydGVzdF9wYXRocyhwcmluY2lwYWxfZ3JhcGgoY2RzKVtbIlVNQVAiXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgZnJvbSA9IG5vZGUsIHRvID0gY29sbmFtZXMoY2RzKSkkdnBhdGgKfSkKCiMgVmlzdWFsaXplIGJyYW5jaCBwcm9iYWJpbGl0aWVzIGFsb25nIHRyYWplY3RvcnkKcGxvdF9jZWxscyhjZHMsIGNvbG9yX2NlbGxzX2J5ID0gInBzZXVkb3RpbWUiLCBsYWJlbF9icmFuY2hfcG9pbnRzID0gVFJVRSkKCgpgYGAKCgo=