1. load libraries

2. Load Data into Seurat


# Patient IDs and H5 files (replace with your actual file paths)

# Define paths to your H5 files
h5_files <- list(
  "Patient1" = "data/GSM5687767_SZ22raw_feature_bc_matrix.h5",
  "Patient2" = "data/GSM5687769_SZ16raw_feature_bc_matrix.h5",
  "Patient3" = "data/GSM5687772_SZ30raw_feature_bc_matrix.h5"
)

# Read and create individual Seurat objects
seurat_list <- list()

3. Load each patient sample with metadata


seurat_list <- list()

for (patient in names(h5_files)) {
  raw_data <- Read10X_h5(h5_files[[patient]])

  # If raw_data is a list (multi-modal), extract "Gene Expression"
  if (is.list(raw_data)) {
    if (!"Gene Expression" %in% names(raw_data)) {
      stop(paste("Gene Expression modality not found in patient", patient))
    }
    raw_counts <- raw_data[["Gene Expression"]]
  } else {
    # Single modality
    raw_counts <- raw_data
  }

  seu <- CreateSeuratObject(
    counts = raw_counts,
    min.cells = 3,
    min.features = 200,
    project = patient
  )

  seu$patient_id <- patient
  seurat_list[[patient]] <- seu
}
Genome matrix has multiple modalities, returning a list of matrices for this genome
Genome matrix has multiple modalities, returning a list of matrices for this genome

4. Merge All Patients into One Seurat Object

# Step 8: JackStraw (optional but useful for PC significance)
combined_seu <- JackStraw(combined_seu, num.replicate = 100)
Warning: The `slot` argument of `GetAssayData()` is deprecated as of SeuratObject 5.0.0.
Please use the `layer` argument instead.

  |                                                  | 0 % ~calculating  
  |+                                                 | 1 % ~11m 24s      
  |+                                                 | 2 % ~11m 48s      
  |++                                                | 3 % ~11m 26s      
  |++                                                | 4 % ~11m 16s      
  |+++                                               | 5 % ~11m 06s      
  |+++                                               | 6 % ~11m 08s      
  |++++                                              | 7 % ~10m 58s      
  |++++                                              | 8 % ~10m 49s      
  |+++++                                             | 9 % ~10m 40s      

5. QC


# Calculate percent.mt for each cell
combined_seu[["percent.mt"]] <- PercentageFeatureSet(combined_seu, pattern = "^MT-")

# Filter cells with too few or too many genes
combined_seu <- subset(combined_seu, 
                       subset = nFeature_RNA > 200 & 
                                nFeature_RNA < 8000 & 
                                percent.mt < 10)


VlnPlot(combined_seu, features = c("nFeature_RNA", 
                                    "nCount_RNA", 
                                    "percent.mt"), 
                                      ncol = 3)


VlnPlot(combined_seu, features = c("nFeature_RNA", 
                                         "nCount_RNA", 
                                         "percent.mt",
                                         "percent.rb"), 
                            ncol = 4, pt.size = 0.1) & 
              theme(plot.title = element_text(size=10))
Warning: The following requested variables were not found: percent.rb

FeatureScatter(combined_seu, 
               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_seu, 
               feature1 = "nCount_RNA", 
               feature2 = "percent.mt")+
  geom_smooth(method = 'lm')


FeatureScatter(combined_seu, 
               feature1 = "nCount_RNA", 
               feature2 = "nFeature_RNA")+
  geom_smooth(method = 'lm')

6. PCA Visualization




# TEST-1
# given that the output of RunPCA is "pca"
# replace "so" by the name of your seurat object

pct <- combined_seu[["pca"]]@stdev / sum(combined_seu[["pca"]]@stdev) * 100
cumu <- cumsum(pct) # Calculate cumulative percents for each PC
# Determine the difference between variation of PC and subsequent PC
co2 <- sort(which((pct[-length(pct)] - pct[-1]) > 0.1), decreasing = T)[1] + 1
# last point where change of % of variation is more than 0.1%. -> co2
co2
[1] 15
# TEST-2
# get significant PCs
stdv <- combined_seu[["pca"]]@stdev
sum.stdv <- sum(combined_seu[["pca"]]@stdev)
percent.stdv <- (stdv / sum.stdv) * 100
cumulative <- cumsum(percent.stdv)
co1 <- which(cumulative > 90 & percent.stdv < 5)[1]
co2 <- sort(which((percent.stdv[1:length(percent.stdv) - 1] - 
                       percent.stdv[2:length(percent.stdv)]) > 0.1), 
              decreasing = T)[1] + 1
min.pc <- min(co1, co2)
min.pc
[1] 15
# Create a dataframe with values
plot_df <- data.frame(pct = percent.stdv, 
           cumu = cumulative, 
           rank = 1:length(percent.stdv))

# Elbow plot to visualize 
  ggplot(plot_df, aes(cumulative, percent.stdv, label = rank, color = rank > min.pc)) + 
  geom_text() + 
  geom_vline(xintercept = 90, color = "grey") + 
  geom_hline(yintercept = min(percent.stdv[percent.stdv > 5]), color = "grey") +
  theme_bw()

NA
NA
NA

8. Clustering (resolution = 1.2)



combined_seu <- FindNeighbors(combined_seu, dims = 1:15)
Computing nearest neighbor graph
Computing SNN
combined_seu <- FindClusters(combined_seu, resolution = 0.3)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 29558
Number of edges: 1047845

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9523
Number of communities: 21
Elapsed time: 3 seconds
# Run UMAP
combined_seu <- RunUMAP(combined_seu, dims = 1:15)
15:04:53 UMAP embedding parameters a = 0.9922 b = 1.112
15:04:53 Read 29558 rows and found 15 numeric columns
15:04:53 Using Annoy for neighbor search, n_neighbors = 30
15:04:53 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
15:04:55 Writing NN index file to temp file /tmp/Rtmp4VyJCk/file908877941321
15:04:55 Searching Annoy index using 1 thread, search_k = 3000
15:05:02 Annoy recall = 100%
15:05:03 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 30
15:05:04 Initializing from normalized Laplacian + noise (using RSpectra)
15:05:06 Commencing optimization for 200 epochs, with 1265872 positive edges
15:05:06 Using rng type: pcg
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
15:05:15 Optimization finished
# UMAP plot colored by clusters
DimPlot(combined_seu, reduction = "umap", label = TRUE, pt.size = 0.5) +
  ggtitle("UMAP of CD4+ T Cells (Resolution = 1.2)")

9. Visualize UMAP with Clusters


DimPlot(combined_seu, reduction = "umap", group.by = "patient_id", label = TRUE, pt.size = 0.6) +
  ggtitle("UMAP of Sézary Syndrome CD4+ T Cells")


DimPlot(combined_seu, reduction = "umap", label = TRUE,label.box = T,repel = T, pt.size = 0.6) +
  ggtitle("UMAP of Sézary Syndrome CD4+ T Cells")

NA
NA
NA

10. FeaturePlots for Top50 UP

top_50_up <- read.csv("top_50_upregulated.csv")        # or read.delim("top_50_up.tsv")
top_50_down <- read.csv("top_50_downregulated.csv")



FeaturePlot(combined_seu, 
             features = top_50_up$gene[1:10], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)



FeaturePlot(combined_seu, 
             features = top_50_up$gene[11:20], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)


FeaturePlot(combined_seu, 
             features = top_50_up$gene[21:30], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)

FeaturePlot(combined_seu, 
             features = top_50_up$gene[31:40], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)

FeaturePlot(combined_seu, 
             features = top_50_up$gene[41:50], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)

11. FeaturePlots for Top50 DOWN


FeaturePlot(combined_seu, 
             features = top_50_down$gene[1:10], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)



FeaturePlot(combined_seu, 
             features = top_50_down$gene[11:20], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)


FeaturePlot(combined_seu, 
             features = top_50_down$gene[21:30], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)

FeaturePlot(combined_seu, 
             features = top_50_down$gene[31:40], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)

FeaturePlot(combined_seu, 
             features = top_50_down$gene[41:50], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)

Visualization



DimPlot(combined_seu, group.by = "seurat_clusters", label = T, label.box = T, repel = T, reduction = "umap")

NA
NA

Visualization of Potential biomarkers-Upregulated



# Vector of genes to plot
up_genes <- c("CLIC1", "COX5A","GTSF1", "MAD2L1","MYBL2","MYL6B","NME1","PLK1", "PYCR1", "SLC25A5", "SRI", "TUBA1C", "UBE2T", "YWHAH")

# DotPlot with custom firebrick-red gradient
DotPlot(combined_seu, features = up_genes) +
  RotatedAxis() +
  scale_color_gradient2(low = "lightyellow", mid = "red", high = "firebrick", midpoint = 1) +
  ggtitle("Expression of Upregulated Genes in Sézary Syndrome") +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1, size = 12),
    axis.text.y = element_text(size = 12),
    plot.title = element_text(hjust = 0.5, face = "bold", size = 14)
  )
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

Visualization of Potential biomarkers-Downregulated



# Downregulated genes
down_genes <- c("TXNIP", "RASA3", "RIPOR2", 
                "ZFP36", "ZFP36L1", "ZFP36L2",
                "PRMT2", "MAX", "PIK3IP1", 
                "BTG1", "CDKN1B")

# DotPlot with firebrick color for high expression
DotPlot(combined_seu, features = down_genes) +
  RotatedAxis() +
  scale_color_gradient2(low = "lightyellow", mid = "red", high = "firebrick", midpoint = 1) +
  ggtitle("Expression of Downregulated Genes in Sézary Syndrome") +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1, size = 12),
    axis.text.y = element_text(size = 12),
    plot.title = element_text(hjust = 0.5, face = "bold", size = 14)
  )
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

Save the Seurat object as an RDS



saveRDS(combined_seu, file = "data/ss_Gaydosik.rds")
LS0tCnRpdGxlOiAiUG90ZW50aWFsIGJpb21hcmtlcnMgVmFsaWRhdGlvbiBvbiBQdWJsaWMgUGF0aWVudCBkYXRhX0dheWRvc2lrXzNfTWFsaWduYW50X1BhdGllbnRfU2FtcGxlIgphdXRob3I6IE5hc2lyIE1haG1vb2QgQWJiYXNpCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogICNybWRmb3JtYXRzOjpyZWFkdGhlZG93bgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQotLS0KCiMgMS4gbG9hZCBsaWJyYXJpZXMKYGBge3IgLCBpbmNsdWRlPUZBTFNFfQoKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQpsaWJyYXJ5KFNldXJhdERhdGEpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KEF6aW11dGgpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocm1hcmtkb3duKQpsaWJyYXJ5KHRpbnl0ZXgpCgoKbGlicmFyeShkcGx5cikKbGlicmFyeShkaXR0b1NlcSkKbGlicmFyeShnZ3JlcGVsKQojbGlicmFyeShnZ3RyZWUpCmxpYnJhcnkocGFyYWxsZWwpCmxpYnJhcnkocGxvdGx5KSAgIyAzRCBwbG90CmxpYnJhcnkoU2V1cmF0KSAgIyBJZGVudHMoKQpsaWJyYXJ5KFNldXJhdERpc2spICAjIFNhdmVINVNldXJhdCgpCmxpYnJhcnkodGliYmxlKSAgIyByb3dubmFtZXNfdG9fY29sdW1uCmxpYnJhcnkoaGFybW9ueSkgIyBSdW5IYXJtb255KCkKI29wdGlvbnMobWMuY29yZXMgPSBkZXRlY3RDb3JlcygpIC0gMSkKCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KGRicGx5cikKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodGlueXRleCkKI0F6aW11dGggQW5ub3RhdGlvbiBsaWJyYXJpZXMKbGlicmFyeShBemltdXRoKQojUHJvamVjVGlscyBBbm5vdGF0aW9uIGxpYnJhcmllcwpsaWJyYXJ5KFNUQUNBUykKbGlicmFyeShQcm9qZWNUSUxzKQojc2luZ2xlUiBBbm5vdGF0aW9uIGxpYnJhcmllcwoKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKCmBgYAoKCiMgMi4gTG9hZCBEYXRhIGludG8gU2V1cmF0CmBgYHtyIH0KCiMgUGF0aWVudCBJRHMgYW5kIEg1IGZpbGVzIChyZXBsYWNlIHdpdGggeW91ciBhY3R1YWwgZmlsZSBwYXRocykKCiMgRGVmaW5lIHBhdGhzIHRvIHlvdXIgSDUgZmlsZXMKaDVfZmlsZXMgPC0gbGlzdCgKICAiUGF0aWVudDEiID0gImRhdGEvR1NNNTY4Nzc2N19TWjIycmF3X2ZlYXR1cmVfYmNfbWF0cml4Lmg1IiwKICAiUGF0aWVudDIiID0gImRhdGEvR1NNNTY4Nzc2OV9TWjE2cmF3X2ZlYXR1cmVfYmNfbWF0cml4Lmg1IiwKICAiUGF0aWVudDMiID0gImRhdGEvR1NNNTY4Nzc3Ml9TWjMwcmF3X2ZlYXR1cmVfYmNfbWF0cml4Lmg1IgopCgojIFJlYWQgYW5kIGNyZWF0ZSBpbmRpdmlkdWFsIFNldXJhdCBvYmplY3RzCnNldXJhdF9saXN0IDwtIGxpc3QoKQoKYGBgCgojIDMuIExvYWQgZWFjaCBwYXRpZW50IHNhbXBsZSB3aXRoIG1ldGFkYXRhCmBgYHtyICwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgpzZXVyYXRfbGlzdCA8LSBsaXN0KCkKCmZvciAocGF0aWVudCBpbiBuYW1lcyhoNV9maWxlcykpIHsKICByYXdfZGF0YSA8LSBSZWFkMTBYX2g1KGg1X2ZpbGVzW1twYXRpZW50XV0pCgogICMgSWYgcmF3X2RhdGEgaXMgYSBsaXN0IChtdWx0aS1tb2RhbCksIGV4dHJhY3QgIkdlbmUgRXhwcmVzc2lvbiIKICBpZiAoaXMubGlzdChyYXdfZGF0YSkpIHsKICAgIGlmICghIkdlbmUgRXhwcmVzc2lvbiIgJWluJSBuYW1lcyhyYXdfZGF0YSkpIHsKICAgICAgc3RvcChwYXN0ZSgiR2VuZSBFeHByZXNzaW9uIG1vZGFsaXR5IG5vdCBmb3VuZCBpbiBwYXRpZW50IiwgcGF0aWVudCkpCiAgICB9CiAgICByYXdfY291bnRzIDwtIHJhd19kYXRhW1siR2VuZSBFeHByZXNzaW9uIl1dCiAgfSBlbHNlIHsKICAgICMgU2luZ2xlIG1vZGFsaXR5CiAgICByYXdfY291bnRzIDwtIHJhd19kYXRhCiAgfQoKICBzZXUgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KAogICAgY291bnRzID0gcmF3X2NvdW50cywKICAgIG1pbi5jZWxscyA9IDMsCiAgICBtaW4uZmVhdHVyZXMgPSAyMDAsCiAgICBwcm9qZWN0ID0gcGF0aWVudAogICkKCiAgc2V1JHBhdGllbnRfaWQgPC0gcGF0aWVudAogIHNldXJhdF9saXN0W1twYXRpZW50XV0gPC0gc2V1Cn0KCgoKYGBgCgojIDQuIE1lcmdlIEFsbCBQYXRpZW50cyBpbnRvIE9uZSBTZXVyYXQgT2JqZWN0CmBgYHtyIH0KCiMgU3RlcCAyOiBNZXJnZSBpbnRvIGNvbWJpbmVkIFNldXJhdCBvYmplY3QKY29tYmluZWRfc2V1IDwtIG1lcmdlKAogIHNldXJhdF9saXN0W1sxXV0sCiAgeSA9IHNldXJhdF9saXN0Wy0xXSwKICBhZGQuY2VsbC5pZHMgPSBuYW1lcyhzZXVyYXRfbGlzdCksCiAgcHJvamVjdCA9ICJTU19NZXJnZWQiCikKCiMgU3RlcCAzOiBOb3JtYWxpemF0aW9uIChMb2dOb3JtYWxpemUpCmNvbWJpbmVkX3NldSA8LSBOb3JtYWxpemVEYXRhKGNvbWJpbmVkX3NldSwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMTAwMDApCgojIFN0ZXAgNDogVmFyaWFibGUgZmVhdHVyZXMgc2VsZWN0aW9uCmNvbWJpbmVkX3NldSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhjb21iaW5lZF9zZXUsIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IiwgbmZlYXR1cmVzID0gMjAwMCkKCiMgU3RlcCA1OiBTY2FsaW5nCmNvbWJpbmVkX3NldSA8LSBTY2FsZURhdGEoY29tYmluZWRfc2V1KQoKIyBTdGVwIDY6IFBDQQpjb21iaW5lZF9zZXUgPC0gUnVuUENBKGNvbWJpbmVkX3NldSwgZmVhdHVyZXMgPSBWYXJpYWJsZUZlYXR1cmVzKGNvbWJpbmVkX3NldSkpCgojIFN0ZXAgNzogRWxib3cgUGxvdCB0byBkZWNpZGUgbnVtYmVyIG9mIFBDcwpFbGJvd1Bsb3QoY29tYmluZWRfc2V1KQoKCiMgIyBTdGVwIDg6IEphY2tTdHJhdyAob3B0aW9uYWwgYnV0IHVzZWZ1bCBmb3IgUEMgc2lnbmlmaWNhbmNlKQojIGNvbWJpbmVkX3NldSA8LSBKYWNrU3RyYXcoY29tYmluZWRfc2V1LCBudW0ucmVwbGljYXRlID0gMTAwKQojIGNvbWJpbmVkX3NldSA8LSBTY29yZUphY2tTdHJhdyhjb21iaW5lZF9zZXUsIGRpbXMgPSAxOjIwKQojIEphY2tTdHJhd1Bsb3QoY29tYmluZWRfc2V1LCBkaW1zID0gMToyMCkKCgpgYGAKCgoKIyA1LiBRQwpgYGB7ciAsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKIyBDYWxjdWxhdGUgcGVyY2VudC5tdCBmb3IgZWFjaCBjZWxsCmNvbWJpbmVkX3NldVtbInBlcmNlbnQubXQiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoY29tYmluZWRfc2V1LCBwYXR0ZXJuID0gIl5NVC0iKQoKIyBGaWx0ZXIgY2VsbHMgd2l0aCB0b28gZmV3IG9yIHRvbyBtYW55IGdlbmVzCmNvbWJpbmVkX3NldSA8LSBzdWJzZXQoY29tYmluZWRfc2V1LCAKICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSBuRmVhdHVyZV9STkEgPiAyMDAgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuRmVhdHVyZV9STkEgPCA4MDAwICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVyY2VudC5tdCA8IDEwKQoKClZsblBsb3QoY29tYmluZWRfc2V1LCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibkNvdW50X1JOQSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGVyY2VudC5tdCIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMykKClZsblBsb3QoY29tYmluZWRfc2V1LCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuQ291bnRfUk5BIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBlcmNlbnQubXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwZXJjZW50LnJiIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDQsIHB0LnNpemUgPSAwLjEpICYgCiAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkKCkZlYXR1cmVTY2F0dGVyKGNvbWJpbmVkX3NldSwgCiAgICAgICAgICAgICAgIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCAKICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAibkZlYXR1cmVfUk5BIikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScpCgpgYGAKCiMjRmVhdHVyZVNjYXR0ZXIgaXMgdHlwaWNhbGx5IHVzZWQgdG8gdmlzdWFsaXplIGZlYXR1cmUtZmVhdHVyZSByZWxhdGlvbnNoaXBzCiMjZm9yIGFueXRoaW5nIGNhbGN1bGF0ZWQgYnkgdGhlIG9iamVjdCwgCiMjaS5lLiBjb2x1bW5zIGluIG9iamVjdCBtZXRhZGF0YSwgUEMgc2NvcmVzIGV0Yy4KCmBgYHtyICwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgpGZWF0dXJlU2NhdHRlcihjb21iaW5lZF9zZXUsIAogICAgICAgICAgICAgICBmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgCiAgICAgICAgICAgICAgIGZlYXR1cmUyID0gInBlcmNlbnQubXQiKSsKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKQoKRmVhdHVyZVNjYXR0ZXIoY29tYmluZWRfc2V1LCAKICAgICAgICAgICAgICAgZmVhdHVyZTEgPSAibkNvdW50X1JOQSIsIAogICAgICAgICAgICAgICBmZWF0dXJlMiA9ICJuRmVhdHVyZV9STkEiKSsKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKQoKYGBgCgojIDYuIFBDQSBWaXN1YWxpemF0aW9uCmBgYHtyIFBDQS1URVNUMiwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgoKCiMgVEVTVC0xCiMgZ2l2ZW4gdGhhdCB0aGUgb3V0cHV0IG9mIFJ1blBDQSBpcyAicGNhIgojIHJlcGxhY2UgInNvIiBieSB0aGUgbmFtZSBvZiB5b3VyIHNldXJhdCBvYmplY3QKCnBjdCA8LSBjb21iaW5lZF9zZXVbWyJwY2EiXV1Ac3RkZXYgLyBzdW0oY29tYmluZWRfc2V1W1sicGNhIl1dQHN0ZGV2KSAqIDEwMApjdW11IDwtIGN1bXN1bShwY3QpICMgQ2FsY3VsYXRlIGN1bXVsYXRpdmUgcGVyY2VudHMgZm9yIGVhY2ggUEMKIyBEZXRlcm1pbmUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB2YXJpYXRpb24gb2YgUEMgYW5kIHN1YnNlcXVlbnQgUEMKY28yIDwtIHNvcnQod2hpY2goKHBjdFstbGVuZ3RoKHBjdCldIC0gcGN0Wy0xXSkgPiAwLjEpLCBkZWNyZWFzaW5nID0gVClbMV0gKyAxCiMgbGFzdCBwb2ludCB3aGVyZSBjaGFuZ2Ugb2YgJSBvZiB2YXJpYXRpb24gaXMgbW9yZSB0aGFuIDAuMSUuIC0+IGNvMgpjbzIKCiMgVEVTVC0yCiMgZ2V0IHNpZ25pZmljYW50IFBDcwpzdGR2IDwtIGNvbWJpbmVkX3NldVtbInBjYSJdXUBzdGRldgpzdW0uc3RkdiA8LSBzdW0oY29tYmluZWRfc2V1W1sicGNhIl1dQHN0ZGV2KQpwZXJjZW50LnN0ZHYgPC0gKHN0ZHYgLyBzdW0uc3RkdikgKiAxMDAKY3VtdWxhdGl2ZSA8LSBjdW1zdW0ocGVyY2VudC5zdGR2KQpjbzEgPC0gd2hpY2goY3VtdWxhdGl2ZSA+IDkwICYgcGVyY2VudC5zdGR2IDwgNSlbMV0KY28yIDwtIHNvcnQod2hpY2goKHBlcmNlbnQuc3RkdlsxOmxlbmd0aChwZXJjZW50LnN0ZHYpIC0gMV0gLSAKICAgICAgICAgICAgICAgICAgICAgICBwZXJjZW50LnN0ZHZbMjpsZW5ndGgocGVyY2VudC5zdGR2KV0pID4gMC4xKSwgCiAgICAgICAgICAgICAgZGVjcmVhc2luZyA9IFQpWzFdICsgMQptaW4ucGMgPC0gbWluKGNvMSwgY28yKQptaW4ucGMKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lIHdpdGggdmFsdWVzCnBsb3RfZGYgPC0gZGF0YS5mcmFtZShwY3QgPSBwZXJjZW50LnN0ZHYsIAogICAgICAgICAgIGN1bXUgPSBjdW11bGF0aXZlLCAKICAgICAgICAgICByYW5rID0gMTpsZW5ndGgocGVyY2VudC5zdGR2KSkKCiMgRWxib3cgcGxvdCB0byB2aXN1YWxpemUgCiAgZ2dwbG90KHBsb3RfZGYsIGFlcyhjdW11bGF0aXZlLCBwZXJjZW50LnN0ZHYsIGxhYmVsID0gcmFuaywgY29sb3IgPSByYW5rID4gbWluLnBjKSkgKyAKICBnZW9tX3RleHQoKSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDkwLCBjb2xvciA9ICJncmV5IikgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtaW4ocGVyY2VudC5zdGR2W3BlcmNlbnQuc3RkdiA+IDVdKSwgY29sb3IgPSAiZ3JleSIpICsKICB0aGVtZV9idygpCgogIAoKYGBgCgojIDguIENsdXN0ZXJpbmcgKHJlc29sdXRpb24gPSAxLjIpCmBgYHtyICwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgoKY29tYmluZWRfc2V1IDwtIEZpbmROZWlnaGJvcnMoY29tYmluZWRfc2V1LCBkaW1zID0gMToxNSkKY29tYmluZWRfc2V1IDwtIEZpbmRDbHVzdGVycyhjb21iaW5lZF9zZXUsIHJlc29sdXRpb24gPSAwLjMpCgojIFJ1biBVTUFQCmNvbWJpbmVkX3NldSA8LSBSdW5VTUFQKGNvbWJpbmVkX3NldSwgZGltcyA9IDE6MTUpCgojIFVNQVAgcGxvdCBjb2xvcmVkIGJ5IGNsdXN0ZXJzCkRpbVBsb3QoY29tYmluZWRfc2V1LCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgcHQuc2l6ZSA9IDAuNSkgKwogIGdndGl0bGUoIlVNQVAgb2YgQ0Q0KyBUIENlbGxzIChSZXNvbHV0aW9uID0gMS4yKSIpCgpgYGAKIyA5LiBWaXN1YWxpemUgVU1BUCB3aXRoIENsdXN0ZXJzCmBgYHtyICwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgpEaW1QbG90KGNvbWJpbmVkX3NldSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJwYXRpZW50X2lkIiwgbGFiZWwgPSBUUlVFLCBwdC5zaXplID0gMC42KSArCiAgZ2d0aXRsZSgiVU1BUCBvZiBTw6l6YXJ5IFN5bmRyb21lIENENCsgVCBDZWxscyIpCgpEaW1QbG90KGNvbWJpbmVkX3NldSwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsbGFiZWwuYm94ID0gVCxyZXBlbCA9IFQsIHB0LnNpemUgPSAwLjYpICsKICBnZ3RpdGxlKCJVTUFQIG9mIFPDqXphcnkgU3luZHJvbWUgQ0Q0KyBUIENlbGxzIikKCgoKYGBgCgojIDEwLiAgRmVhdHVyZVBsb3RzIGZvciBUb3A1MCBVUApgYGB7ciAsIGZpZy5oZWlnaHQ9MTYsIGZpZy53aWR0aD0yMH0KdG9wXzUwX3VwIDwtIHJlYWQuY3N2KCJ0b3BfNTBfdXByZWd1bGF0ZWQuY3N2IikgICAgICAgICMgb3IgcmVhZC5kZWxpbSgidG9wXzUwX3VwLnRzdiIpCnRvcF81MF9kb3duIDwtIHJlYWQuY3N2KCJ0b3BfNTBfZG93bnJlZ3VsYXRlZC5jc3YiKQoKCgpGZWF0dXJlUGxvdChjb21iaW5lZF9zZXUsIAogICAgICAgICAgICAgZmVhdHVyZXMgPSB0b3BfNTBfdXAkZ2VuZVsxOjEwXSwgCiAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Ymx1ZSIsICJyZWQiKSwgICMgQ3VzdG9tIGNvbG9yIGdyYWRpZW50IGZyb20gbGlnaHQgYmx1ZSB0byByZWQKICAgICAgICAgICAgIGxhYmVsID0gVFJVRSkKCgpGZWF0dXJlUGxvdChjb21iaW5lZF9zZXUsIAogICAgICAgICAgICAgZmVhdHVyZXMgPSB0b3BfNTBfdXAkZ2VuZVsxMToyMF0sIAogICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLCAKICAgICAgICAgICAgIGNvbHMgPSBjKCJsaWdodGJsdWUiLCAicmVkIiksICAjIEN1c3RvbSBjb2xvciBncmFkaWVudCBmcm9tIGxpZ2h0IGJsdWUgdG8gcmVkCiAgICAgICAgICAgICBsYWJlbCA9IFRSVUUpCgpGZWF0dXJlUGxvdChjb21iaW5lZF9zZXUsIAogICAgICAgICAgICAgZmVhdHVyZXMgPSB0b3BfNTBfdXAkZ2VuZVsyMTozMF0sIAogICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLCAKICAgICAgICAgICAgIGNvbHMgPSBjKCJsaWdodGJsdWUiLCAicmVkIiksICAjIEN1c3RvbSBjb2xvciBncmFkaWVudCBmcm9tIGxpZ2h0IGJsdWUgdG8gcmVkCiAgICAgICAgICAgICBsYWJlbCA9IFRSVUUpCkZlYXR1cmVQbG90KGNvbWJpbmVkX3NldSwgCiAgICAgICAgICAgICBmZWF0dXJlcyA9IHRvcF81MF91cCRnZW5lWzMxOjQwXSwgCiAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Ymx1ZSIsICJyZWQiKSwgICMgQ3VzdG9tIGNvbG9yIGdyYWRpZW50IGZyb20gbGlnaHQgYmx1ZSB0byByZWQKICAgICAgICAgICAgIGxhYmVsID0gVFJVRSkKRmVhdHVyZVBsb3QoY29tYmluZWRfc2V1LCAKICAgICAgICAgICAgIGZlYXR1cmVzID0gdG9wXzUwX3VwJGdlbmVbNDE6NTBdLCAKICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgCiAgICAgICAgICAgICBjb2xzID0gYygibGlnaHRibHVlIiwgInJlZCIpLCAgIyBDdXN0b20gY29sb3IgZ3JhZGllbnQgZnJvbSBsaWdodCBibHVlIHRvIHJlZAogICAgICAgICAgICAgbGFiZWwgPSBUUlVFKQoKYGBgCgoKCiMgMTEuICBGZWF0dXJlUGxvdHMgZm9yIFRvcDUwIERPV04KYGBge3IgLCBmaWcuaGVpZ2h0PTE2LCBmaWcud2lkdGg9MjB9CgpGZWF0dXJlUGxvdChjb21iaW5lZF9zZXUsIAogICAgICAgICAgICAgZmVhdHVyZXMgPSB0b3BfNTBfZG93biRnZW5lWzE6MTBdLCAKICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgCiAgICAgICAgICAgICBjb2xzID0gYygibGlnaHRibHVlIiwgInJlZCIpLCAgIyBDdXN0b20gY29sb3IgZ3JhZGllbnQgZnJvbSBsaWdodCBibHVlIHRvIHJlZAogICAgICAgICAgICAgbGFiZWwgPSBUUlVFKQoKCkZlYXR1cmVQbG90KGNvbWJpbmVkX3NldSwgCiAgICAgICAgICAgICBmZWF0dXJlcyA9IHRvcF81MF9kb3duJGdlbmVbMTE6MjBdLCAKICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgCiAgICAgICAgICAgICBjb2xzID0gYygibGlnaHRibHVlIiwgInJlZCIpLCAgIyBDdXN0b20gY29sb3IgZ3JhZGllbnQgZnJvbSBsaWdodCBibHVlIHRvIHJlZAogICAgICAgICAgICAgbGFiZWwgPSBUUlVFKQoKRmVhdHVyZVBsb3QoY29tYmluZWRfc2V1LCAKICAgICAgICAgICAgIGZlYXR1cmVzID0gdG9wXzUwX2Rvd24kZ2VuZVsyMTozMF0sIAogICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLCAKICAgICAgICAgICAgIGNvbHMgPSBjKCJsaWdodGJsdWUiLCAicmVkIiksICAjIEN1c3RvbSBjb2xvciBncmFkaWVudCBmcm9tIGxpZ2h0IGJsdWUgdG8gcmVkCiAgICAgICAgICAgICBsYWJlbCA9IFRSVUUpCkZlYXR1cmVQbG90KGNvbWJpbmVkX3NldSwgCiAgICAgICAgICAgICBmZWF0dXJlcyA9IHRvcF81MF9kb3duJGdlbmVbMzE6NDBdLCAKICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgCiAgICAgICAgICAgICBjb2xzID0gYygibGlnaHRibHVlIiwgInJlZCIpLCAgIyBDdXN0b20gY29sb3IgZ3JhZGllbnQgZnJvbSBsaWdodCBibHVlIHRvIHJlZAogICAgICAgICAgICAgbGFiZWwgPSBUUlVFKQpGZWF0dXJlUGxvdChjb21iaW5lZF9zZXUsIAogICAgICAgICAgICAgZmVhdHVyZXMgPSB0b3BfNTBfZG93biRnZW5lWzQxOjUwXSwgCiAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Ymx1ZSIsICJyZWQiKSwgICMgQ3VzdG9tIGNvbG9yIGdyYWRpZW50IGZyb20gbGlnaHQgYmx1ZSB0byByZWQKICAgICAgICAgICAgIGxhYmVsID0gVFJVRSkKYGBgCgojIyBWaXN1YWxpemF0aW9uCmBgYHtyICwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgoKRGltUGxvdChjb21iaW5lZF9zZXUsIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCwgcmVwZWwgPSBULCByZWR1Y3Rpb24gPSAidW1hcCIpCgoKYGBgCgojIyBWaXN1YWxpemF0aW9uIG9mIFBvdGVudGlhbCBiaW9tYXJrZXJzLVVwcmVndWxhdGVkCmBgYHtyICwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgoKIyBWZWN0b3Igb2YgZ2VuZXMgdG8gcGxvdAp1cF9nZW5lcyA8LSBjKCJDTElDMSIsICJDT1g1QSIsIkdUU0YxIiwgIk1BRDJMMSIsIk1ZQkwyIiwiTVlMNkIiLCJOTUUxIiwiUExLMSIsICJQWUNSMSIsICJTTEMyNUE1IiwgIlNSSSIsICJUVUJBMUMiLCAiVUJFMlQiLCAiWVdIQUgiKQoKIyBEb3RQbG90IHdpdGggY3VzdG9tIGZpcmVicmljay1yZWQgZ3JhZGllbnQKRG90UGxvdChjb21iaW5lZF9zZXUsIGZlYXR1cmVzID0gdXBfZ2VuZXMpICsKICBSb3RhdGVkQXhpcygpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIobG93ID0gImxpZ2h0eWVsbG93IiwgbWlkID0gInJlZCIsIGhpZ2ggPSAiZmlyZWJyaWNrIiwgbWlkcG9pbnQgPSAxKSArCiAgZ2d0aXRsZSgiRXhwcmVzc2lvbiBvZiBVcHJlZ3VsYXRlZCBHZW5lcyBpbiBTw6l6YXJ5IFN5bmRyb21lIikgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCkKICApCgoKCmBgYAoKCgojIyBWaXN1YWxpemF0aW9uIG9mIFBvdGVudGlhbCBiaW9tYXJrZXJzLURvd25yZWd1bGF0ZWQKYGBge3IgQzIsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKCiMgRG93bnJlZ3VsYXRlZCBnZW5lcwpkb3duX2dlbmVzIDwtIGMoIlRYTklQIiwgIlJBU0EzIiwgIlJJUE9SMiIsIAogICAgICAgICAgICAgICAgIlpGUDM2IiwgIlpGUDM2TDEiLCAiWkZQMzZMMiIsCiAgICAgICAgICAgICAgICAiUFJNVDIiLCAiTUFYIiwgIlBJSzNJUDEiLCAKICAgICAgICAgICAgICAgICJCVEcxIiwgIkNES04xQiIpCgojIERvdFBsb3Qgd2l0aCBmaXJlYnJpY2sgY29sb3IgZm9yIGhpZ2ggZXhwcmVzc2lvbgpEb3RQbG90KGNvbWJpbmVkX3NldSwgZmVhdHVyZXMgPSBkb3duX2dlbmVzKSArCiAgUm90YXRlZEF4aXMoKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKGxvdyA9ICJsaWdodHllbGxvdyIsIG1pZCA9ICJyZWQiLCBoaWdoID0gImZpcmVicmljayIsIG1pZHBvaW50ID0gMSkgKwogIGdndGl0bGUoIkV4cHJlc3Npb24gb2YgRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBTw6l6YXJ5IFN5bmRyb21lIikgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCkKICApCgoKCmBgYAoKIyBTYXZlIHRoZSBTZXVyYXQgb2JqZWN0IGFzIGFuIFJEUwpgYGB7ciBzYXZlUkRTfQoKCnNhdmVSRFMoY29tYmluZWRfc2V1LCBmaWxlID0gImRhdGEvc3NfR2F5ZG9zaWsucmRzIikKCgpgYGAKCgoK