1. load libraries
Load your objects
combined_merged <- readRDS("Poglio_Gaydosik_combined_merged_QCfiltered.rds")
2. QC
# Remove the percent.mito column
combined_merged$percent.mito <- NULL
# Set identity classes to an existing column in meta data
Idents(object = combined_merged) <- "batch"
# Add percent ribosomal and mitochondrial content
combined_merged[["percent.rb"]] <- PercentageFeatureSet(combined_merged, pattern = "^RP[SL]")
combined_merged$percent.rb <- replace(as.numeric(combined_merged$percent.rb), is.na(combined_merged$percent.rb), 0)
combined_merged[["percent.mt"]] <- PercentageFeatureSet(combined_merged, pattern = "^MT-")
combined_merged$percent.mt <- replace(as.numeric(combined_merged$percent.mt), is.na(combined_merged$percent.mt), 0)
# ----------------------------
# Filter high `nCount_RNA` cells
# ----------------------------
# Define an upper threshold (e.g., top 0.5% or absolute cutoff)
ncount_thresh <- quantile(combined_merged$nCount_RNA, 0.995) # top 0.5% outliers
message("Removing cells with nCount_RNA > ", round(ncount_thresh))
# Subset the object to remove both types of outliers
combined_merged <- subset(combined_merged,
subset = nCount_RNA < ncount_thresh & percent.mt < 10)
# ----------------------------
# QC Plots (after filtering)
# ----------------------------
VlnPlot(combined_merged, features = c("nFeature_RNA",
"nCount_RNA",
"percent.mt",
"percent.rb"),
ncol = 4, pt.size = 0.1) & theme(plot.title = element_text(size=10))
FeatureScatter(combined_merged, feature1 = "percent.mt", feature2 = "percent.rb")
FeatureScatter(combined_merged, feature1 = "nCount_RNA", feature2 = "nFeature_RNA") +
geom_smooth(method = 'lm')
##FeatureScatter is typically used to visualize feature-feature
relationships ##for anything calculated by the object, ##i.e. columns in
object metadata, PC scores etc.
FeatureScatter(combined_merged,
feature1 = "nCount_RNA",
feature2 = "percent.mt")+
geom_smooth(method = 'lm')
FeatureScatter(combined_merged,
feature1 = "nCount_RNA",
feature2 = "nFeature_RNA")+
geom_smooth(method = 'lm')
JoinLayers
Assign Cell-Cycle Scores
3. Normalize data
# Apply SCTransform
combined_merged <- SCTransform(combined_merged,
vars.to.regress = c("percent.rb","percent.mt","CC.Difference", "nCount_RNA"),
assay = "RNA",
do.scale=TRUE,
do.center=TRUE,
verbose = TRUE)
6. Clustering
# Set the seed for clustering steps
set.seed(123)
combined_merged <- FindNeighbors(combined_merged,
dims = 1:min.pc,
verbose = FALSE)
# understanding resolution
combined_merged <- FindClusters(combined_merged,
resolution = c(0.3, 0.4, 0.5, 0.6, 0.7,0.8, 0.9, 1, 1.2,1.5))
UMAP Visualization
# Set the seed for clustering steps
set.seed(123)
# non-linear dimensionality reduction --------------
combined_merged <- RunUMAP(combined_merged,
dims = 1:min.pc,
verbose = FALSE)
# Define resolution values to plot
resolutions <- c(0.3, 0.4, 0.5, 0.6, 0.7,0.8, 0.9, 1, 1.2,1.5)
# Loop through and generate DimPlots
for (res in resolutions) {
res_col <- paste0("SCT_snn_res.", res)
p <- DimPlot(combined_merged,
group.by = res_col,
reduction = "umap",
label.size = 3,
repel = TRUE,
label = TRUE,
label.box = TRUE) +
ggtitle(paste("Clustering resolution:", res))
print(p)
}
# Set identity classes to an existing column in meta data
Idents(object = combined_merged) <- "SCT_snn_res.0.4"
cluster_table <- table(Idents(combined_merged))
barplot(cluster_table, main = "Number of Cells in Each Cluster",
xlab = "Cluster",
ylab = "Number of Cells",
col = rainbow(length(cluster_table)))
print(cluster_table)
Save the Seurat object as an Robj file
saveRDS(combined_merged, file = "Poglio_Gaydosik_combined_merged_SCT_Normalized.rds")
7. clusTree
library(clustree)
clustree(combined_merged, prefix = "SCT_snn_res.")
8. Azimuth Annotation
InstallData("pbmcref")
# The RunAzimuth function can take a Seurat object as input
combined_merged <- RunAzimuth(combined_merged, reference = "pbmcref")
9. Azimuth Visualization
DimPlot(combined_merged, group.by = "predicted.celltype.l1",
reduction = "umap",
label.size = 3,
repel = T,
label = T, label.box = T)
DimPlot(combined_merged, group.by = "predicted.celltype.l1",
reduction = "umap",
label.size = 3,
repel = T,
label = F)
DimPlot(combined_merged, group.by = "predicted.celltype.l2",
reduction = "umap",
label.size = 3,
repel = T,
label = T, label.box = T)
DimPlot(combined_merged, group.by = "predicted.celltype.l2",
reduction = "umap",
label.size = 3,
repel = T,
label = F)
DimPlot(combined_merged, group.by = "predicted.celltype.l2",
reduction = "umap",
label.size = 3,
repel = T,
label = F)
table(combined_merged$predicted.celltype.l2, combined_merged$SCT_snn_res.0.4)
10. Harmony Integration
# Load required libraries
library(Seurat)
library(harmony)
library(ggplot2)
# Run Harmony, adjusting for batch effect using "cell_line" or another grouping variable
combined_merged <- RunHarmony(
combined_merged,
group.by.vars = c("batch"), # Replace with the metadata column specifying batch or cell line
assay.use="SCT")
# Check results in harmony embeddings
harmony_embeddings <- Embeddings(combined_merged, reduction = "harmony")
head(harmony_embeddings)
# Set the seed for clustering steps
set.seed(123)
# Run UMAP on Harmony embeddings
combined_merged <- RunUMAP(combined_merged, reduction = "harmony", dims = 1:16)
# Set the seed for clustering steps
set.seed(123)
# Optionally, find neighbors and clusters (if you plan to do clustering analysis)
combined_merged <- FindNeighbors(combined_merged, reduction = "harmony", dims = 1:16)
combined_merged <- FindClusters(combined_merged, reduction = "harmony", resolution = 0.5) # Adjust resolution as needed
# Visualize UMAP
DimPlot(combined_merged, reduction = "umap", group.by = "batch", label = TRUE, pt.size = 0.5) +
ggtitle("UMAP of Harmony-Integrated Data")
# Visualize UMAP with batch/cell line information
DimPlot(combined_merged, reduction = "umap", group.by = "batch", label = TRUE, pt.size = 0.5) +
ggtitle("UMAP - Colored by Cell Line (After Harmony Integration)")
# Visualize UMAP with clusters
DimPlot(combined_merged, reduction = "umap", group.by = "seurat_clusters", label = TRUE, pt.size = 0.5) +
ggtitle("UMAP - Clustered Data (After Harmony Integration)")
# # Visualize specific cell types or other metadata
# DimPlot(combined_merged, reduction = "umap", group.by = "predicted.celltype.l2", label = TRUE, pt.size = 0.5) +
# ggtitle("UMAP - Cell Types After Harmony Integration")
Save the Seurat object as an Robj file
saveRDS(combined_merged, file = "Poglio_Gaydosik_combined_merged_Harmonized.rds")
LS0tCnRpdGxlOiAiTWVyZ2luZyBQb2dsaW8gYW5kIEdheWRvc2lrIERhdGEiCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgI3JtZGZvcm1hdHM6OnJlYWR0aGVkb3duCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfY29sbGFwc2VkOiB0cnVlCgotLS0KCiMgMS4gbG9hZCBsaWJyYXJpZXMKYGBge3IgLCBpbmNsdWRlPUZBTFNFfQoKIyBBdCB0aGUgdG9wIG9mIHlvdXIgc2NyaXB0LCBjb21iaW5lIHBhY2thZ2VzIGxpa2UgdGhpczoKbGlicmFyeSh0aWR5dmVyc2UpICAgICAgIyBpbmNsdWRlcyBkcGx5ciwgZ2dwbG90MiwgdGliYmxlLCByZWFkciwgZXRjLgpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShoYXJtb255KQpsaWJyYXJ5KGRpdHRvU2VxKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShTZXVyYXREaXNrKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodGlueXRleCkKbGlicmFyeShBemltdXRoKQpsaWJyYXJ5KFNUQUNBUykKbGlicmFyeShQcm9qZWNUSUxzKQpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQoKYGBgCgoKIyMgTG9hZCB5b3VyIG9iamVjdHMKYGBge3IgLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD02fQoKY29tYmluZWRfbWVyZ2VkIDwtIHJlYWRSRFMoIlBvZ2xpb19HYXlkb3Npa19jb21iaW5lZF9tZXJnZWRfUUNmaWx0ZXJlZC5yZHMiKQpgYGAKCiMgMi4gUUMKYGBge3IgUUMsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE0fQoKIyBSZW1vdmUgdGhlIHBlcmNlbnQubWl0byBjb2x1bW4KY29tYmluZWRfbWVyZ2VkJHBlcmNlbnQubWl0byA8LSBOVUxMCgoKIyBTZXQgaWRlbnRpdHkgY2xhc3NlcyB0byBhbiBleGlzdGluZyBjb2x1bW4gaW4gbWV0YSBkYXRhCklkZW50cyhvYmplY3QgPSBjb21iaW5lZF9tZXJnZWQpIDwtICJiYXRjaCIKCiMgQWRkIHBlcmNlbnQgcmlib3NvbWFsIGFuZCBtaXRvY2hvbmRyaWFsIGNvbnRlbnQKY29tYmluZWRfbWVyZ2VkW1sicGVyY2VudC5yYiJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChjb21iaW5lZF9tZXJnZWQsIHBhdHRlcm4gPSAiXlJQW1NMXSIpCmNvbWJpbmVkX21lcmdlZCRwZXJjZW50LnJiIDwtIHJlcGxhY2UoYXMubnVtZXJpYyhjb21iaW5lZF9tZXJnZWQkcGVyY2VudC5yYiksIGlzLm5hKGNvbWJpbmVkX21lcmdlZCRwZXJjZW50LnJiKSwgMCkKCmNvbWJpbmVkX21lcmdlZFtbInBlcmNlbnQubXQiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoY29tYmluZWRfbWVyZ2VkLCBwYXR0ZXJuID0gIl5NVC0iKQpjb21iaW5lZF9tZXJnZWQkcGVyY2VudC5tdCA8LSByZXBsYWNlKGFzLm51bWVyaWMoY29tYmluZWRfbWVyZ2VkJHBlcmNlbnQubXQpLCBpcy5uYShjb21iaW5lZF9tZXJnZWQkcGVyY2VudC5tdCksIDApCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBGaWx0ZXIgaGlnaCBgbkNvdW50X1JOQWAgY2VsbHMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIERlZmluZSBhbiB1cHBlciB0aHJlc2hvbGQgKGUuZy4sIHRvcCAwLjUlIG9yIGFic29sdXRlIGN1dG9mZikKbmNvdW50X3RocmVzaCA8LSBxdWFudGlsZShjb21iaW5lZF9tZXJnZWQkbkNvdW50X1JOQSwgMC45OTUpICAjIHRvcCAwLjUlIG91dGxpZXJzCm1lc3NhZ2UoIlJlbW92aW5nIGNlbGxzIHdpdGggbkNvdW50X1JOQSA+ICIsIHJvdW5kKG5jb3VudF90aHJlc2gpKQoKIyBTdWJzZXQgdGhlIG9iamVjdCB0byByZW1vdmUgYm90aCB0eXBlcyBvZiBvdXRsaWVycwpjb21iaW5lZF9tZXJnZWQgPC0gc3Vic2V0KGNvbWJpbmVkX21lcmdlZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gbkNvdW50X1JOQSA8IG5jb3VudF90aHJlc2ggJiBwZXJjZW50Lm10IDwgMTApCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBRQyBQbG90cyAoYWZ0ZXIgZmlsdGVyaW5nKQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KClZsblBsb3QoY29tYmluZWRfbWVyZ2VkLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuQ291bnRfUk5BIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBlcmNlbnQubXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwZXJjZW50LnJiIiksIAogICAgICAgIG5jb2wgPSA0LCBwdC5zaXplID0gMC4xKSAmIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpCgpGZWF0dXJlU2NhdHRlcihjb21iaW5lZF9tZXJnZWQsIGZlYXR1cmUxID0gInBlcmNlbnQubXQiLCBmZWF0dXJlMiA9ICJwZXJjZW50LnJiIikKCkZlYXR1cmVTY2F0dGVyKGNvbWJpbmVkX21lcmdlZCwgZmVhdHVyZTEgPSAibkNvdW50X1JOQSIsIGZlYXR1cmUyID0gIm5GZWF0dXJlX1JOQSIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKQoKYGBgCgojI0ZlYXR1cmVTY2F0dGVyIGlzIHR5cGljYWxseSB1c2VkIHRvIHZpc3VhbGl6ZSBmZWF0dXJlLWZlYXR1cmUgcmVsYXRpb25zaGlwcwojI2ZvciBhbnl0aGluZyBjYWxjdWxhdGVkIGJ5IHRoZSBvYmplY3QsIAojI2kuZS4gY29sdW1ucyBpbiBvYmplY3QgbWV0YWRhdGEsIFBDIHNjb3JlcyBldGMuCgpgYGB7ciAsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKRmVhdHVyZVNjYXR0ZXIoY29tYmluZWRfbWVyZ2VkLCAKICAgICAgICAgICAgICAgZmVhdHVyZTEgPSAibkNvdW50X1JOQSIsIAogICAgICAgICAgICAgICBmZWF0dXJlMiA9ICJwZXJjZW50Lm10IikrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJykKCkZlYXR1cmVTY2F0dGVyKGNvbWJpbmVkX21lcmdlZCwgCiAgICAgICAgICAgICAgIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCAKICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAibkZlYXR1cmVfUk5BIikrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJykKCmBgYAoKIyMgIEpvaW5MYXllcnMKYGBge3IgLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCgpEZWZhdWx0QXNzYXkoY29tYmluZWRfbWVyZ2VkKSA8LSAiUk5BIgoKIyBNZXJnZSBhbGwgY291bnRzLiogbGF5ZXJzIGludG8gYSBzaW5nbGUgY291bnRzIGxheWVyCmNvbWJpbmVkX21lcmdlZCA8LSBKb2luTGF5ZXJzKGNvbWJpbmVkX21lcmdlZCwgYXNzYXkgPSAiUk5BIikKCiMgQ29uZmlybSBsYXllcnMgbm93CkxheWVycyhjb21iaW5lZF9tZXJnZWRbWyJSTkEiXV0pCmBgYAoKIyMgIEFzc2lnbiBDZWxsLUN5Y2xlIFNjb3JlcwpgYGB7ciAsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKb3B0aW9ucyhmdXR1cmUuZ2xvYmFscy5tYXhTaXplID0gODAwMCAqIDEwMjReMikgICMgU2V0IHRvIDgwMDAgTWlCIChhYm91dCA4IEdCKQoKCmNvbWJpbmVkX21lcmdlZCA8LSBTQ1RyYW5zZm9ybShjb21iaW5lZF9tZXJnZWQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvLnNjYWxlID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvLmNlbnRlciA9IEZBTFNFKSAgIyBSZWR1Y2UgdG8gMTAwMCB2YXJpYWJsZSBmZWF0dXJlcwoKCiMgQSBsaXN0IG9mIGNlbGwgY3ljbGUgbWFya2VycywgZnJvbSBUaXJvc2ggZXQgYWwsIDIwMTUsIGlzIGxvYWRlZCB3aXRoIFNldXJhdC4gIFdlIGNhbgojIHNlZ3JlZ2F0ZSB0aGlzIGxpc3QgaW50byBtYXJrZXJzIG9mIEcyL00gcGhhc2UgYW5kIG1hcmtlcnMgb2YgUyBwaGFzZQpzLmdlbmVzIDwtIGNjLmdlbmVzJHMuZ2VuZXMKZzJtLmdlbmVzIDwtIGNjLmdlbmVzJGcybS5nZW5lcwoKCmNvbWJpbmVkX21lcmdlZCA8LSBDZWxsQ3ljbGVTY29yaW5nKGNvbWJpbmVkX21lcmdlZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHMuZmVhdHVyZXMgPSBzLmdlbmVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZzJtLmZlYXR1cmVzID0gZzJtLmdlbmVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V0LmlkZW50ID0gVFJVRSkKCkRlZmF1bHRBc3NheShjb21iaW5lZF9tZXJnZWQpIDwtICJSTkEiCmNvbWJpbmVkX21lcmdlZCRDQy5EaWZmZXJlbmNlIDwtIGNvbWJpbmVkX21lcmdlZCRTLlNjb3JlIC0gY29tYmluZWRfbWVyZ2VkJEcyTS5TY29yZQoKYGBgCgoKIyAzLiBOb3JtYWxpemUgZGF0YQpgYGB7ciBTQ1ROb3JtYWxpemUsIGluY2x1ZGU9VFJVRX0KIyBBcHBseSBTQ1RyYW5zZm9ybQpjb21iaW5lZF9tZXJnZWQgPC0gU0NUcmFuc2Zvcm0oY29tYmluZWRfbWVyZ2VkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcyA9IGMoInBlcmNlbnQucmIiLCJwZXJjZW50Lm10IiwiQ0MuRGlmZmVyZW5jZSIsICJuQ291bnRfUk5BIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvLnNjYWxlPVRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG8uY2VudGVyPVRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUpCmBgYAoKCiMgNC4gUGVyZm9ybSBQQ0EKYGBge3IgUENBLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KClZhcmlhYmxlc19nZW5lcyA8LSBjb21iaW5lZF9tZXJnZWRAYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMKCiMgRXhjbHVkZSBnZW5lcyBzdGFydGluZyB3aXRoICJITEEtIiBBTkQgIlhpc3QiIEFORCAiVFJCViwgVFJBViIKVmFyaWFibGVzX2dlbmVzX2FmdGVyX2V4Y2x1c2lvbiA8LSBWYXJpYWJsZXNfZ2VuZXNbIWdyZXBsKCJeSExBLXxeWElTVHxeVFJCVnxeVFJBViIsIFZhcmlhYmxlc19nZW5lcyldCgojIFNldCB0aGUgc2VlZCBmb3IgY2x1c3RlcmluZyBzdGVwcwpzZXQuc2VlZCgxMjMpCgojIFRoZXNlIGFyZSBub3cgc3RhbmRhcmQgc3RlcHMgaW4gdGhlIFNldXJhdCB3b3JrZmxvdyBmb3IgdmlzdWFsaXphdGlvbiBhbmQgY2x1c3RlcmluZwpjb21iaW5lZF9tZXJnZWQgPC0gUnVuUENBKGNvbWJpbmVkX21lcmdlZCwKICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBWYXJpYWJsZXNfZ2VuZXNfYWZ0ZXJfZXhjbHVzaW9uLAogICAgICAgICAgICAgICAgICAgICAgICBkby5wcmludCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICBwY3MucHJpbnQgPSAxOjUsIAogICAgICAgICAgICAgICAgICAgICAgICBnZW5lcy5wcmludCA9IDE1LAogICAgICAgICAgICAgICAgICAgICAgICBucGNzID0gNTApCgojIGRldGVybWluZSBkaW1lbnNpb25hbGl0eSBvZiB0aGUgZGF0YQpFbGJvd1Bsb3QoY29tYmluZWRfbWVyZ2VkLCBuZGltcyA9IDUwKQpgYGAKCiMgNS4gUGVyZm9ybSBQQ0EgVEVTVApgYGB7ciBQQ0EtVEVTVCwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikgIAoKIyBBc3N1bWluZyB5b3UgaGF2ZSAxMCBkaWZmZXJlbnQgY2VsbCBsaW5lcywgZ2VuZXJhdGluZyBhIGNvbG9yIHBhbGV0dGUgd2l0aCAxMCBjb2xvcnMKY2VsbF9saW5lX2NvbG9ycyA8LSBicmV3ZXIucGFsKDEwLCAiU2V0MyIpCgojIEFzc3VtaW5nIGNvbWJpbmVkX21lcmdlZCRjZWxsX2xpbmUgaXMgYSBmYWN0b3Igb3IgY2hhcmFjdGVyIHZlY3RvciBjb250YWluaW5nIGNlbGwgbGluZSBuYW1lcwpkYXRhIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoY29tYmluZWRfbWVyZ2VkJGNlbGxfbGluZSkpCmNvbG5hbWVzKGRhdGEpIDwtIGMoImNlbGxfbGluZSIsICJuVU1JIikgICMgQ2hhbmdlIGNvbHVtbiBuYW1lIHRvIG5VTUkKCm5jZWxscyA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBjZWxsX2xpbmUsIHkgPSBuVU1JLCBmaWxsID0gY2VsbF9saW5lKSkgKyAKICBnZW9tX2NvbCgpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuVU1JKSwgCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCAKICAgICAgICAgICAgdmp1c3QgPSAtMC4yNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNlbGxfbGluZV9jb2xvcnMpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKyAgIyBBZGp1c3QgdGhlIHRpdGxlIHBvc2l0aW9uCiAgZ2d0aXRsZSgiRmlsdGVyZWQgY2VsbHMgcGVyIHNhbXBsZSIpICsKICB4bGFiKCJDZWxsIGxpbmVzIikgKyAgIyBBZGp1c3QgeC1heGlzIGxhYmVsCiAgeWxhYigiRnJlcXVlbmN5IikgICAgIyBBZGp1c3QgeS1heGlzIGxhYmVsCgpwcmludChuY2VsbHMpCgoKCiMgVEVTVC0xCiMgZ2l2ZW4gdGhhdCB0aGUgb3V0cHV0IG9mIFJ1blBDQSBpcyAicGNhIgojIHJlcGxhY2UgInNvIiBieSB0aGUgbmFtZSBvZiB5b3VyIHNldXJhdCBvYmplY3QKCnBjdCA8LSBjb21iaW5lZF9tZXJnZWRbWyJwY2EiXV1Ac3RkZXYgLyBzdW0oY29tYmluZWRfbWVyZ2VkW1sicGNhIl1dQHN0ZGV2KSAqIDEwMApjdW11IDwtIGN1bXN1bShwY3QpICMgQ2FsY3VsYXRlIGN1bXVsYXRpdmUgcGVyY2VudHMgZm9yIGVhY2ggUEMKIyBEZXRlcm1pbmUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB2YXJpYXRpb24gb2YgUEMgYW5kIHN1YnNlcXVlbnQgUEMKY28yIDwtIHNvcnQod2hpY2goKHBjdFstbGVuZ3RoKHBjdCldIC0gcGN0Wy0xXSkgPiAwLjEpLCBkZWNyZWFzaW5nID0gVClbMV0gKyAxCiMgbGFzdCBwb2ludCB3aGVyZSBjaGFuZ2Ugb2YgJSBvZiB2YXJpYXRpb24gaXMgbW9yZSB0aGFuIDAuMSUuIC0+IGNvMgpjbzIKCiMgVEVTVC0yCiMgZ2V0IHNpZ25pZmljYW50IFBDcwpzdGR2IDwtIGNvbWJpbmVkX21lcmdlZFtbInBjYSJdXUBzdGRldgpzdW0uc3RkdiA8LSBzdW0oY29tYmluZWRfbWVyZ2VkW1sicGNhIl1dQHN0ZGV2KQpwZXJjZW50LnN0ZHYgPC0gKHN0ZHYgLyBzdW0uc3RkdikgKiAxMDAKY3VtdWxhdGl2ZSA8LSBjdW1zdW0ocGVyY2VudC5zdGR2KQpjbzEgPC0gd2hpY2goY3VtdWxhdGl2ZSA+IDkwICYgcGVyY2VudC5zdGR2IDwgNSlbMV0KY28yIDwtIHNvcnQod2hpY2goKHBlcmNlbnQuc3RkdlsxOmxlbmd0aChwZXJjZW50LnN0ZHYpIC0gMV0gLSAKICAgICAgICAgICAgICAgICAgICAgICBwZXJjZW50LnN0ZHZbMjpsZW5ndGgocGVyY2VudC5zdGR2KV0pID4gMC4xKSwgCiAgICAgICAgICAgICAgZGVjcmVhc2luZyA9IFQpWzFdICsgMQptaW4ucGMgPC0gbWluKGNvMSwgY28yKQptaW4ucGMKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lIHdpdGggdmFsdWVzCnBsb3RfZGYgPC0gZGF0YS5mcmFtZShwY3QgPSBwZXJjZW50LnN0ZHYsIAogICAgICAgICAgIGN1bXUgPSBjdW11bGF0aXZlLCAKICAgICAgICAgICByYW5rID0gMTpsZW5ndGgocGVyY2VudC5zdGR2KSkKCiMgRWxib3cgcGxvdCB0byB2aXN1YWxpemUgCiAgZ2dwbG90KHBsb3RfZGYsIGFlcyhjdW11bGF0aXZlLCBwZXJjZW50LnN0ZHYsIGxhYmVsID0gcmFuaywgY29sb3IgPSByYW5rID4gbWluLnBjKSkgKyAKICBnZW9tX3RleHQoKSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDkwLCBjb2xvciA9ICJncmV5IikgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtaW4ocGVyY2VudC5zdGR2W3BlcmNlbnQuc3RkdiA+IDVdKSwgY29sb3IgPSAiZ3JleSIpICsKICB0aGVtZV9idygpCgogIAoKYGBgCgojIDYuIENsdXN0ZXJpbmcKYGBge3IgQzEsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKIyBTZXQgdGhlIHNlZWQgZm9yIGNsdXN0ZXJpbmcgc3RlcHMKc2V0LnNlZWQoMTIzKQoKY29tYmluZWRfbWVyZ2VkIDwtIEZpbmROZWlnaGJvcnMoY29tYmluZWRfbWVyZ2VkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gMTptaW4ucGMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKCiMgdW5kZXJzdGFuZGluZyByZXNvbHV0aW9uCmNvbWJpbmVkX21lcmdlZCA8LSBGaW5kQ2x1c3RlcnMoY29tYmluZWRfbWVyZ2VkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvbiA9IGMoMC4zLCAwLjQsIDAuNSwgMC42LCAwLjcsMC44LCAwLjksIDEsIDEuMiwxLjUpKQoKCmBgYAoKIyMgVU1BUCBWaXN1YWxpemF0aW9uCmBgYHtyIEMyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KIyBTZXQgdGhlIHNlZWQgZm9yIGNsdXN0ZXJpbmcgc3RlcHMKc2V0LnNlZWQoMTIzKQoKIyBub24tbGluZWFyIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiAtLS0tLS0tLS0tLS0tLQpjb21iaW5lZF9tZXJnZWQgPC0gUnVuVU1BUChjb21iaW5lZF9tZXJnZWQsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGRpbXMgPSAxOm1pbi5wYywKICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKCiMgRGVmaW5lIHJlc29sdXRpb24gdmFsdWVzIHRvIHBsb3QKcmVzb2x1dGlvbnMgPC0gYygwLjMsIDAuNCwgMC41LCAwLjYsIDAuNywwLjgsIDAuOSwgMSwgMS4yLDEuNSkKCiMgTG9vcCB0aHJvdWdoIGFuZCBnZW5lcmF0ZSBEaW1QbG90cwpmb3IgKHJlcyBpbiByZXNvbHV0aW9ucykgewogIHJlc19jb2wgPC0gcGFzdGUwKCJTQ1Rfc25uX3Jlcy4iLCByZXMpCiAgCiAgcCA8LSBEaW1QbG90KGNvbWJpbmVkX21lcmdlZCwKICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSByZXNfY29sLAogICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgICAgICAgIGxhYmVsLnNpemUgPSAzLAogICAgICAgICAgICAgICByZXBlbCA9IFRSVUUsCiAgICAgICAgICAgICAgIGxhYmVsID0gVFJVRSwKICAgICAgICAgICAgICAgbGFiZWwuYm94ID0gVFJVRSkgKwogICAgICAgZ2d0aXRsZShwYXN0ZSgiQ2x1c3RlcmluZyByZXNvbHV0aW9uOiIsIHJlcykpCiAgCiAgcHJpbnQocCkKfQoKCiMgU2V0IGlkZW50aXR5IGNsYXNzZXMgdG8gYW4gZXhpc3RpbmcgY29sdW1uIGluIG1ldGEgZGF0YQpJZGVudHMob2JqZWN0ID0gY29tYmluZWRfbWVyZ2VkKSA8LSAiU0NUX3Nubl9yZXMuMC40IgoKY2x1c3Rlcl90YWJsZSA8LSB0YWJsZShJZGVudHMoY29tYmluZWRfbWVyZ2VkKSkKCgpiYXJwbG90KGNsdXN0ZXJfdGFibGUsIG1haW4gPSAiTnVtYmVyIG9mIENlbGxzIGluIEVhY2ggQ2x1c3RlciIsIAogICAgICAgICAgICAgICAgICAgICAgeGxhYiA9ICJDbHVzdGVyIiwgCiAgICAgICAgICAgICAgICAgICAgICB5bGFiID0gIk51bWJlciBvZiBDZWxscyIsIAogICAgICAgICAgICAgICAgICAgICAgY29sID0gcmFpbmJvdyhsZW5ndGgoY2x1c3Rlcl90YWJsZSkpKQoKcHJpbnQoY2x1c3Rlcl90YWJsZSkKCmBgYAoKCiMjIFNhdmUgdGhlIFNldXJhdCBvYmplY3QgYXMgYW4gUm9iaiBmaWxlCmBgYHtyLCBlY2hvPVRSVUV9CgpzYXZlUkRTKGNvbWJpbmVkX21lcmdlZCwgZmlsZSA9ICJQb2dsaW9fR2F5ZG9zaWtfY29tYmluZWRfbWVyZ2VkX1NDVF9Ob3JtYWxpemVkLnJkcyIpCgpgYGAKCiMgNy4gY2x1c1RyZWUKYGBge3IgY2x1c1RyZWUsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xMH0KbGlicmFyeShjbHVzdHJlZSkKY2x1c3RyZWUoY29tYmluZWRfbWVyZ2VkLCBwcmVmaXggPSAiU0NUX3Nubl9yZXMuIikKYGBgCgojIDguIEF6aW11dGggQW5ub3RhdGlvbgpgYGB7ciBhemltdXRoX0Fubm90YXRpb24yLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KSW5zdGFsbERhdGEoInBibWNyZWYiKQoKIyBUaGUgUnVuQXppbXV0aCBmdW5jdGlvbiBjYW4gdGFrZSBhIFNldXJhdCBvYmplY3QgYXMgaW5wdXQKY29tYmluZWRfbWVyZ2VkIDwtIFJ1bkF6aW11dGgoY29tYmluZWRfbWVyZ2VkLCByZWZlcmVuY2UgPSAicGJtY3JlZiIpCgpgYGAKCiMgOS4gQXppbXV0aCBWaXN1YWxpemF0aW9uCmBgYHtyIGF6aW11dGhfVmlzdWFsaXphdGlvbiwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CkRpbVBsb3QoY29tYmluZWRfbWVyZ2VkLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDEiLCAKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgbGFiZWwuc2l6ZSA9IDMsCiAgICAgICAgcmVwZWwgPSBULAogICAgICAgIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkKCkRpbVBsb3QoY29tYmluZWRfbWVyZ2VkLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDEiLCAKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgbGFiZWwuc2l6ZSA9IDMsCiAgICAgICAgcmVwZWwgPSBULAogICAgICAgIGxhYmVsID0gRikKCkRpbVBsb3QoY29tYmluZWRfbWVyZ2VkLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiLCAKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgbGFiZWwuc2l6ZSA9IDMsCiAgICAgICAgcmVwZWwgPSBULAogICAgICAgIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkKCkRpbVBsb3QoY29tYmluZWRfbWVyZ2VkLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiLCAKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgbGFiZWwuc2l6ZSA9IDMsCiAgICAgICAgcmVwZWwgPSBULAogICAgICAgIGxhYmVsID0gRikKCgpEaW1QbG90KGNvbWJpbmVkX21lcmdlZCwgZ3JvdXAuYnkgPSAicHJlZGljdGVkLmNlbGx0eXBlLmwyIiwgCiAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgIGxhYmVsLnNpemUgPSAzLAogICAgICAgIHJlcGVsID0gVCwKICAgICAgICBsYWJlbCA9IEYpCgoKCnRhYmxlKGNvbWJpbmVkX21lcmdlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIsIGNvbWJpbmVkX21lcmdlZCRTQ1Rfc25uX3Jlcy4wLjQpCgpgYGAKCgojIDEwLiBIYXJtb255IEludGVncmF0aW9uCmBgYHtyIGhhcm1vbnksIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKCiMgTG9hZCByZXF1aXJlZCBsaWJyYXJpZXMKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoaGFybW9ueSkKbGlicmFyeShnZ3Bsb3QyKQoKIyBSdW4gSGFybW9ueSwgYWRqdXN0aW5nIGZvciBiYXRjaCBlZmZlY3QgdXNpbmcgImNlbGxfbGluZSIgb3IgYW5vdGhlciBncm91cGluZyB2YXJpYWJsZQpjb21iaW5lZF9tZXJnZWQgPC0gUnVuSGFybW9ueSgKICBjb21iaW5lZF9tZXJnZWQsCiAgZ3JvdXAuYnkudmFycyA9IGMoImJhdGNoIiksICAjIFJlcGxhY2Ugd2l0aCB0aGUgbWV0YWRhdGEgY29sdW1uIHNwZWNpZnlpbmcgYmF0Y2ggb3IgY2VsbCBsaW5lCiAgYXNzYXkudXNlPSJTQ1QiKQoKIyBDaGVjayByZXN1bHRzIGluIGhhcm1vbnkgZW1iZWRkaW5ncwpoYXJtb255X2VtYmVkZGluZ3MgPC0gRW1iZWRkaW5ncyhjb21iaW5lZF9tZXJnZWQsIHJlZHVjdGlvbiA9ICJoYXJtb255IikKaGVhZChoYXJtb255X2VtYmVkZGluZ3MpCgojIFNldCB0aGUgc2VlZCBmb3IgY2x1c3RlcmluZyBzdGVwcwpzZXQuc2VlZCgxMjMpCgojIFJ1biBVTUFQIG9uIEhhcm1vbnkgZW1iZWRkaW5ncwpjb21iaW5lZF9tZXJnZWQgPC0gUnVuVU1BUChjb21iaW5lZF9tZXJnZWQsIHJlZHVjdGlvbiA9ICJoYXJtb255IiwgZGltcyA9IDE6MTYpCgojIFNldCB0aGUgc2VlZCBmb3IgY2x1c3RlcmluZyBzdGVwcwpzZXQuc2VlZCgxMjMpCgojIE9wdGlvbmFsbHksIGZpbmQgbmVpZ2hib3JzIGFuZCBjbHVzdGVycyAoaWYgeW91IHBsYW4gdG8gZG8gY2x1c3RlcmluZyBhbmFseXNpcykKY29tYmluZWRfbWVyZ2VkIDwtIEZpbmROZWlnaGJvcnMoY29tYmluZWRfbWVyZ2VkLCByZWR1Y3Rpb24gPSAiaGFybW9ueSIsIGRpbXMgPSAxOjE2KQpjb21iaW5lZF9tZXJnZWQgPC0gRmluZENsdXN0ZXJzKGNvbWJpbmVkX21lcmdlZCwgcmVkdWN0aW9uID0gImhhcm1vbnkiLCByZXNvbHV0aW9uID0gMC41KSAgIyBBZGp1c3QgcmVzb2x1dGlvbiBhcyBuZWVkZWQKCiMgVmlzdWFsaXplIFVNQVAKRGltUGxvdChjb21iaW5lZF9tZXJnZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiYmF0Y2giLCBsYWJlbCA9IFRSVUUsIHB0LnNpemUgPSAwLjUpICsKICAgIGdndGl0bGUoIlVNQVAgb2YgSGFybW9ueS1JbnRlZ3JhdGVkIERhdGEiKQoKCiMgVmlzdWFsaXplIFVNQVAgd2l0aCBiYXRjaC9jZWxsIGxpbmUgaW5mb3JtYXRpb24KRGltUGxvdChjb21iaW5lZF9tZXJnZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiYmF0Y2giLCBsYWJlbCA9IFRSVUUsIHB0LnNpemUgPSAwLjUpICsKICAgIGdndGl0bGUoIlVNQVAgLSBDb2xvcmVkIGJ5IENlbGwgTGluZSAoQWZ0ZXIgSGFybW9ueSBJbnRlZ3JhdGlvbikiKQoKCiMgVmlzdWFsaXplIFVNQVAgd2l0aCBjbHVzdGVycwpEaW1QbG90KGNvbWJpbmVkX21lcmdlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBsYWJlbCA9IFRSVUUsIHB0LnNpemUgPSAwLjUpICsKICAgIGdndGl0bGUoIlVNQVAgLSBDbHVzdGVyZWQgRGF0YSAoQWZ0ZXIgSGFybW9ueSBJbnRlZ3JhdGlvbikiKQoKIyAjIFZpc3VhbGl6ZSBzcGVjaWZpYyBjZWxsIHR5cGVzIG9yIG90aGVyIG1ldGFkYXRhCiMgRGltUGxvdChjb21iaW5lZF9tZXJnZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAicHJlZGljdGVkLmNlbGx0eXBlLmwyIiwgbGFiZWwgPSBUUlVFLCBwdC5zaXplID0gMC41KSArCiMgICAgIGdndGl0bGUoIlVNQVAgLSBDZWxsIFR5cGVzIEFmdGVyIEhhcm1vbnkgSW50ZWdyYXRpb24iKQoKCgpgYGAKCgojIyBTYXZlIHRoZSBTZXVyYXQgb2JqZWN0IGFzIGFuIFJvYmogZmlsZQpgYGB7ciwgZWNobz1UUlVFfQoKc2F2ZVJEUyhjb21iaW5lZF9tZXJnZWQsIGZpbGUgPSAiUG9nbGlvX0dheWRvc2lrX2NvbWJpbmVkX21lcmdlZF9IYXJtb25pemVkLnJkcyIpCgpgYGAKCgoKCg==