1. load libraries

2. Load Data into Seurat

# Check metadata distribution
table(all_seurat$orig.ident)

Control   SS_P1   SS_P2   SS_P3   SS_P4   SS_P5   SS_P6 
   4437    3443   34179    3084     849    1582    3521 

3. QC


VlnPlot(ss_Borcherding, features = c("nFeature_RNA", 
                                    "nCount_RNA", 
                                    "percent.mt"), 
                                      ncol = 3)
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.Warning: The `slot` argument of `FetchData()` is deprecated as of SeuratObject 5.0.0.
Please use the `layer` argument instead.Warning: `PackageCheck()` was deprecated in SeuratObject 5.0.0.
Please use `rlang::check_installed()` instead.

VlnPlot(ss_Borcherding, features = c("nFeature_RNA", 
                                         "nCount_RNA", 
                                         "percent.mt",
                                         "percent.rb"), 
                            ncol = 4, pt.size = 0.1) & 
              theme(plot.title = element_text(size=10))
Warning: Default search for "data" layer in "RNA" assay yielded no results; utilizing "counts" layer instead.Warning: The following requested variables were not found: percent.rb

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


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

4. Log-normalization (scale factor = 10,000) and Integration



library(Seurat)

# 1. Split the Seurat object by patient_id or sample
ss_list <- SplitObject(ss_Borcherding, split.by = "orig.ident")

# 2. Normalize and find variable features for each dataset independently
ss_list <- lapply(ss_list, function(x) {
  x <- NormalizeData(x, normalization.method = "LogNormalize", scale.factor = 10000)
  x <- FindVariableFeatures(x, selection.method = "vst", nfeatures = 2000)
  return(x)
})
Normalizing layer: counts.SS_P1.SeuratProject.SeuratProject.SeuratProject.SeuratProject.SeuratProject
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Finding variable features for layer counts.SS_P1.SeuratProject.SeuratProject.SeuratProject.SeuratProject.SeuratProject
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Normalizing layer: counts.SS_P2.SeuratProject.SeuratProject.SeuratProject.SeuratProject.SeuratProject
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Finding variable features for layer counts.SS_P2.SeuratProject.SeuratProject.SeuratProject.SeuratProject.SeuratProject
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Normalizing layer: counts.SS_P3.SeuratProject.SeuratProject.SeuratProject.SeuratProject
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Finding variable features for layer counts.SS_P3.SeuratProject.SeuratProject.SeuratProject.SeuratProject
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Normalizing layer: counts.SS_P4.SeuratProject.SeuratProject.SeuratProject
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Finding variable features for layer counts.SS_P4.SeuratProject.SeuratProject.SeuratProject
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Normalizing layer: counts.SS_P5.SeuratProject.SeuratProject
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Finding variable features for layer counts.SS_P5.SeuratProject.SeuratProject
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Normalizing layer: counts.SS_P6.SeuratProject
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Finding variable features for layer counts.SS_P6.SeuratProject
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Normalizing layer: counts.Control
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Finding variable features for layer counts.Control
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
# 3. Select integration features and find anchors
features <- SelectIntegrationFeatures(object.list = ss_list, nfeatures = 2000)
anchors <- FindIntegrationAnchors(object.list = ss_list, anchor.features = features, reduction = "cca")
Scaling features for provided objects

  |                                                  | 0 % ~calculating  
  |++++++++                                          | 14% ~01s          
  |+++++++++++++++                                   | 29% ~07s          
  |++++++++++++++++++++++                            | 43% ~04s          
  |+++++++++++++++++++++++++++++                     | 57% ~02s          
  |++++++++++++++++++++++++++++++++++++              | 71% ~01s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~01s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=03s  
Finding all pairwise anchors

  |                                                  | 0 % ~calculating  
Warning: The `slot` argument of `GetAssayData()` is deprecated as of SeuratObject 5.0.0.
Please use the `layer` argument instead.Running CCA
Merging objects
Warning: The `slot` argument of `SetAssayData()` is deprecated as of SeuratObject 5.0.0.
Please use the `layer` argument instead.Finding neighborhoods
Finding anchors
    Found 16592 anchors
Filtering anchors
    Retained 656 anchors

  |+++                                               | 5 % ~01h 41m 44s  
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 8842 anchors
Filtering anchors
    Retained 1649 anchors

  |+++++                                             | 10% ~54m 25s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 11820 anchors
Filtering anchors
    Retained 3215 anchors

  |++++++++                                          | 14% ~01h 10m 17s  
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 3216 anchors
Filtering anchors
    Retained 1265 anchors

  |++++++++++                                        | 19% ~50m 49s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 3356 anchors
Filtering anchors
    Retained 2187 anchors

  |++++++++++++                                      | 24% ~45m 13s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 3053 anchors
Filtering anchors
    Retained 2786 anchors

  |+++++++++++++++                                   | 29% ~35m 51s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 5828 anchors
Filtering anchors
    Retained 1676 anchors

  |+++++++++++++++++                                 | 33% ~29m 25s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 6544 anchors
Filtering anchors
    Retained 3213 anchors

  |++++++++++++++++++++                              | 38% ~29m 26s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 5816 anchors
Filtering anchors
    Retained 3569 anchors

  |++++++++++++++++++++++                            | 43% ~24m 34s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 2911 anchors
Filtering anchors
    Retained 2169 anchors

  |++++++++++++++++++++++++                          | 48% ~20m 24s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 9206 anchors
Filtering anchors
    Retained 1877 anchors

  |+++++++++++++++++++++++++++                       | 52% ~17m 26s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 11842 anchors
Filtering anchors
    Retained 4050 anchors

  |+++++++++++++++++++++++++++++                     | 57% ~19m 28s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 8120 anchors
Filtering anchors
    Retained 3963 anchors

  |+++++++++++++++++++++++++++++++                   | 62% ~16m 24s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 3260 anchors
Filtering anchors
    Retained 1962 anchors

  |++++++++++++++++++++++++++++++++++                | 67% ~13m 26s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 5750 anchors
Filtering anchors
    Retained 3022 anchors

  |++++++++++++++++++++++++++++++++++++              | 71% ~10m 54s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 12799 anchors
Filtering anchors
    Retained 1902 anchors

  |+++++++++++++++++++++++++++++++++++++++           | 76% ~08m 48s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 20781 anchors
Filtering anchors
    Retained 3017 anchors

  |+++++++++++++++++++++++++++++++++++++++++         | 81% ~08m 34s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 9303 anchors
Filtering anchors
    Retained 1984 anchors

  |+++++++++++++++++++++++++++++++++++++++++++       | 86% ~06m 11s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 3377 anchors
Filtering anchors
    Retained 808 anchors

  |++++++++++++++++++++++++++++++++++++++++++++++    | 90% ~03m 56s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 6250 anchors
Filtering anchors
    Retained 1422 anchors

  |++++++++++++++++++++++++++++++++++++++++++++++++  | 95% ~01m 53s      
Running CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 10154 anchors
Filtering anchors
    Retained 2154 anchors

  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=38m 33s
# 4. Integrate data
ss_integrated <- IntegrateData(anchorset = anchors)
Merging dataset 4 into 3
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 NULLMerging dataset 5 into 3 4
Extracting anchors for merged samples
Finding integration vectors
Warning: Different cells in new layer data than already exists for scale.dataFinding 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 NULLMerging dataset 6 into 3 4 5
Extracting anchors for merged samples
Finding integration vectors
Warning: Different cells in new layer data than already exists for scale.dataFinding 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 NULLMerging dataset 3 4 5 6 into 2
Extracting anchors for merged samples
Finding integration vectors
Warning: Different cells in new layer data than already exists for scale.dataFinding 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 NULLMerging dataset 7 into 2 3 4 5 6
Extracting anchors for merged samples
Finding integration vectors
Warning: Different cells in new layer data than already exists for scale.dataFinding 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 NULLMerging dataset 1 into 2 3 4 5 6 7
Extracting anchors for merged samples
Finding integration vectors
Warning: Different cells in new layer data than already exists for scale.dataFinding 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 NULL
# 5. Proceed with downstream analysis on integrated data
DefaultAssay(ss_integrated) <- "integrated"

# Scale data
ss_integrated <- ScaleData(ss_integrated, verbose = FALSE)

5. PCA + UMAP


ss_Borcherding <- ss_integrated

# PCA
ss_Borcherding <- RunPCA(ss_Borcherding,  features = VariableFeatures(ss_Borcherding))
PC_ 1 
Positive:  TRBV7-2, IL32, IFITM1, CD52, B2M, CD3D, CD3E, LTB, TRAV41, PTPRCAP 
       RPS18, CRIP1, IL7R, IL2RG, CCR7, LAT, TCF7, LDHB, DDX5, RPL3 
       PPP2R5C, AIRE, SLC9A3R1, HLA-B, AQP3, EMP3, RPS6, TAGLN2, GSTK1, CD27 
Negative:  TYROBP, LYZ, FCN1, CST3, S100A9, AIF1, S100A8, FCER1G, LST1, SPI1 
       SERPINA1, VCAN, HLA-DRA, S100A12, CYBB, CTSS, LGALS2, MNDA, GSTP1, CFD 
       CSTA, CD14, FTL, MS4A6A, FOS, HLA-DRB1, NCF1, CD68, PSAP, FGR 
PC_ 2 
Positive:  TMSB10, S100A6, S100A10, CD52, CRIP1, S100A4, IL32, S100A11, GAPDH, RPS18 
       TRBV7-2, VIM, RPS19, HLA-B, TMSB4X, ACTG1, IFITM1, CFL1, RPS6, HLA-C 
       PPIA, YBX1, MT-CO1, LGALS1, CORO1A, NME2, CD3D, FYB, GSTK1, IFITM2 
Negative:  SNCA, MS4A1, HBB, MPP1, CTD-3214H19.6, C2orf88, CA2, CD79A, STX1A, FAM212B 
       NRGN, CTNNAL1, RGS18, ATF3, SLC25A37, SH3BGRL2, PDGFA, FAM46C, SUSD1, ESAM 
       ACRBP, EPB42, HIST1H3H, MMRN1, LINC00926, IGLV2-14, CENPN, BCL2L1, NFE2, MAP3K8 
PC_ 3 
Positive:  TRBV7-2, S100A10, JUNB, S100A6, LTB, DDX5, S100A8, S100A12, S100A9, IL7R 
       VCAN, TRAV41, CD52, LYZ, TSC22D3, AIRE, TCF7, EPHB6, FOS, CD14 
       MNDA, TMSB10, RNASET2, CCR7, CRIP1, TRBV12-4, PPP2R5C, FCN1, LGALS2, MAL 
Negative:  NKG7, CCL5, GZMA, GZMH, GZMB, CTSW, CCL4, CD8A, PRF1, CD8B 
       CLIC3, CD7, KLRD1, GZMM, CMC1, HOPX, GZMK, CST7, GNLY, MATK 
       KLRB1, PFN1, PLEK, C12orf75, TBX21, SPON2, HCST, TRBV7-6, KLRG1, APOBEC3G 
PC_ 4 
Positive:  RPLP1, RPS19, SCN1B, MS4A1, RPS12, CD79A, RPS18, KLHL35, TMSB10, RPL13 
       RPS6, IGLV2-14, MALAT1, HLA-DQA1, RPLP0, NKG7, LINC00926, AQP10, GZMA, MT-CO1 
       RPL3, MT-CO2, POU2AF1, HLA-DRA, HLA-DRB1, HLA-DPB1, GZMB, GZMH, HLA-DOB, CD7 
Negative:  CTD-3214H19.6, NRGN, STX1A, FAM212B, ATF3, RGS18, HIST1H3H, CENPN, C2orf88, PDGFA 
       SH3BGRL2, CORO1C, CTNNAL1, HIST1H2AC, ESAM, SUSD1, HIST1H2BJ, MMD, TSC22D1, CLU 
       ACRBP, PLA2G12A, CDK2AP1, RP11-367G6.3, MMRN1, AP003068.23, MAP3K7CL, SLC40A1, CA2, MPP1 
PC_ 5 
Positive:  MS4A1, CD79A, HLA-DQA1, LINC00926, HLA-DRA, IGLV2-14, HLA-DPB1, POU2AF1, HLA-DPA1, IGHM 
       HLA-DOB, HLA-DRB1, CD74, HLA-DRB5, HLA-DMA, CD79B, MEF2C, TCF4, MYBL2, HVCN1 
       SCIMP, CD40, HLA-DQB1, KIAA0226L, FAM26F, NT5E, FAM129C, MZB1, EAF2, RP11-126O1.6 
Negative:  S100A12, NKG7, VCAN, S100A8, GZMA, GZMH, S100A9, GZMB, CCL4, CCL5 
       CTSW, LYZ, CD14, PRF1, KLRD1, CLIC3, CD7, CTSD, CD8A, MGST1 
       S100A6, CSF3R, MNDA, CD8B, S100A4, FOS, TYROBP, CST7, GZMM, HOPX 
# Optional: Visualize elbow plot
ElbowPlot(ss_Borcherding, ndims = 50)


# # Run JackStraw analysis properly
# ss_Borcherding <- JackStraw(ss_Borcherding, num.replicate = 100)
# ss_Borcherding <- ScoreJackStraw(ss_Borcherding, dims = 1:20)
# 
# # Visualize JackStraw scores
# JackStrawPlot(ss_Borcherding, dims = 1:20)

6. PCA Visualization




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

pct <- ss_Borcherding[["pca"]]@stdev / sum(ss_Borcherding[["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] 13
# TEST-2
# get significant PCs
stdv <- ss_Borcherding[["pca"]]@stdev
sum.stdv <- sum(ss_Borcherding[["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] 13
# 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

7. Clustering (resolution = 1.2)

# Then find neighbors & clusters
ss_Borcherding <- FindNeighbors(ss_Borcherding, dims = 1:40)
Computing nearest neighbor graph
Computing SNN
ss_Borcherding <- FindClusters(ss_Borcherding, resolution = 0.5)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 51095
Number of edges: 1483726

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8983
Number of communities: 33
Elapsed time: 8 seconds
12 singletons identified. 21 final clusters.
# run UMAP
ss_Borcherding <- RunUMAP(ss_Borcherding, dims = 1:40)
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 session17:00:51 UMAP embedding parameters a = 0.9922 b = 1.112
17:00:51 Read 51095 rows and found 40 numeric columns
17:00:51 Using Annoy for neighbor search, n_neighbors = 30
17:00:51 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
17:00:56 Writing NN index file to temp file /tmp/RtmpgtON3t/file158c73aa3390f
17:00:56 Searching Annoy index using 1 thread, search_k = 3000
17:01:13 Annoy recall = 100%
17:01:14 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 30
17:01:16 Initializing from normalized Laplacian + noise (using RSpectra)
17:01:17 Commencing optimization for 200 epochs, with 2375354 positive edges
17:01:17 Using rng type: pcg
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
17:01:36 Optimization finished
ss_Borcherding <-RunTSNE(ss_Borcherding, dims.use = 1:40)

8. Visualize UMAP with Clusters


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



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



DimPlot(ss_Borcherding, reduction = "tsne", label = TRUE, pt.size = 0.6) +
  ggtitle("TSNE of Sézary Syndrome CD4+ T Cells")

9. FeaturePlots for Top50 UP

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

Idents(ss_Borcherding) <- "seurat_clusters"


FeaturePlot(ss_Borcherding, 
             features = top_50_up$gene[1:10], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)
Warning: Could not find CDT1 in the default search locations, found in 'RNA' assay insteadWarning: Removing 849 cells missing data for features requestedWarning: Could not find CRNDE in the default search locations, found in 'RNA' assay insteadWarning: Removing 5515 cells missing data for features requestedWarning: Could not find CDCA8 in the default search locations, found in 'RNA' assay insteadWarning: Removing 2431 cells missing data for features requestedWarning: The following requested variables were not found: PCLAF

FeaturePlot(ss_Borcherding, 
             features = top_50_up$gene[11:20], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)
Warning: Could not find PKMYT1 in the default search locations, found in 'RNA' assay insteadWarning: Removing 5874 cells missing data for features requestedWarning: Could not find AHCY in the default search locations, found in 'RNA' assay insteadWarning: Could not find TTF2 in the default search locations, found in 'RNA' assay insteadWarning: Could not find COX5A in the default search locations, found in 'RNA' assay instead

FeaturePlot(ss_Borcherding, 
             features = top_50_up$gene[21:30], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)
Warning: Could not find FH in the default search locations, found in 'RNA' assay insteadWarning: Could not find UCK2 in the default search locations, found in 'RNA' assay insteadWarning: Could not find CCNB1 in the default search locations, found in 'RNA' assay insteadWarning: Removing 1582 cells missing data for features requestedWarning: Could not find PYCR1 in the default search locations, found in 'RNA' assay insteadWarning: Removing 1582 cells missing data for features requestedWarning: Could not find TRIP13 in the default search locations, found in 'RNA' assay insteadWarning: Removing 849 cells missing data for features requested

FeaturePlot(ss_Borcherding, 
             features = top_50_up$gene[31:40], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)
Warning: Could not find RNASEH2A in the default search locations, found in 'RNA' assay insteadWarning: Could not find NME1 in the default search locations, found in 'RNA' assay insteadWarning: Could not find NABP2 in the default search locations, found in 'RNA' assay insteadWarning: Could not find CDC20 in the default search locations, found in 'RNA' assay insteadWarning: Removing 5874 cells missing data for features requested

FeaturePlot(ss_Borcherding, 
             features = top_50_up$gene[41:50], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)
Warning: Could not find UBE2C in the default search locations, found in 'RNA' assay insteadWarning: Removing 849 cells missing data for features requestedWarning: Could not find EBP in the default search locations, found in 'RNA' assay insteadWarning: Could not find SLC29A1 in the default search locations, found in 'RNA' assay insteadWarning: Could not find SRI in the default search locations, found in 'RNA' assay insteadWarning: Could not find PLK1 in the default search locations, found in 'RNA' assay insteadWarning: Removing 849 cells missing data for features requested

10. FeaturePlots for Top50 DOWN


FeaturePlot(ss_Borcherding, 
             features = top_50_down$gene[1:10], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)
Warning: Could not find SARAF in the default search locations, found in 'RNA' assay insteadWarning: Could not find BTG1 in the default search locations, found in 'RNA' assay insteadWarning: Could not find SRSF5 in the default search locations, found in 'RNA' assay insteadWarning: Could not find IKZF1 in the default search locations, found in 'RNA' assay insteadWarning: Could not find PRMT2 in the default search locations, found in 'RNA' assay insteadWarning: Could not find RBL2 in the default search locations, found in 'RNA' assay insteadWarning: Could not find PACS1 in the default search locations, found in 'RNA' assay insteadWarning: The following requested variables were not found: PCED1B-AS1, SNHG5

FeaturePlot(ss_Borcherding, 
             features = top_50_down$gene[11:20], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)
Warning: Could not find ZBTB20 in the default search locations, found in 'RNA' assay insteadWarning: Could not find RAPGEF6 in the default search locations, found in 'RNA' assay insteadWarning: Could not find N4BP2L2 in the default search locations, found in 'RNA' assay insteadWarning: Could not find SF1 in the default search locations, found in 'RNA' assay insteadWarning: Could not find RPS27 in the default search locations, found in 'RNA' assay insteadWarning: Could not find RSRP1 in the default search locations, found in 'RNA' assay insteadWarning: Could not find DAZAP2 in the default search locations, found in 'RNA' assay insteadWarning: The following requested variables were not found: RIPOR2

FeaturePlot(ss_Borcherding, 
             features = top_50_down$gene[21:30], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)
Warning: Could not find HLA-E in the default search locations, found in 'RNA' assay insteadWarning: Could not find SON in the default search locations, found in 'RNA' assay insteadWarning: Could not find DDX6 in the default search locations, found in 'RNA' assay insteadWarning: Could not find RASA3 in the default search locations, found in 'RNA' assay insteadWarning: Could not find VAMP2 in the default search locations, found in 'RNA' assay insteadWarning: Could not find PNRC1 in the default search locations, found in 'RNA' assay insteadWarning: Could not find CDKN1B in the default search locations, found in 'RNA' assay instead

FeaturePlot(ss_Borcherding, 
             features = top_50_down$gene[31:40], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)
Warning: Could not find LEPROTL1 in the default search locations, found in 'RNA' assay insteadWarning: Could not find EPC1 in the default search locations, found in 'RNA' assay insteadWarning: Could not find FOXP1 in the default search locations, found in 'RNA' assay insteadWarning: Could not find CIRBP in the default search locations, found in 'RNA' assay insteadWarning: Could not find SUN2 in the default search locations, found in 'RNA' assay insteadWarning: The following requested variables were not found: LINC01578

FeaturePlot(ss_Borcherding, 
             features = top_50_down$gene[41:50], 
             reduction = "umap", 
             cols = c("lightblue", "red"),  # Custom color gradient from light blue to red
             label = TRUE)
Warning: Could not find HIF1A in the default search locations, found in 'RNA' assay insteadWarning: Could not find PNISR in the default search locations, found in 'RNA' assay insteadWarning: Could not find R3HDM4 in the default search locations, found in 'RNA' assay insteadWarning: Could not find ANKRD44 in the default search locations, found in 'RNA' assay insteadWarning: Could not find DDX24 in the default search locations, found in 'RNA' assay insteadWarning: The following requested variables were not found: AL138963.4

Visualization



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

NA
NA

Visualization of Potential biomarkers-Upregulated


DefaultAssay(ss_Borcherding) <- "RNA"
Idents(ss_Borcherding) <- "Disease_state"

# 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(ss_Borcherding, 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)
  )
Warning: Scaling data with a low number of groups may produce misleading resultsScale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

Idents(ss_Borcherding) <- "orig.ident"

# 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(ss_Borcherding, 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.

Idents(ss_Borcherding) <- "seurat_clusters"

# 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(ss_Borcherding, 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


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

# DotPlot with firebrick color for high expression
DotPlot(ss_Borcherding, 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)
  )
Warning: The following requested variables were not found: RIPOR2Warning: Scaling data with a low number of groups may produce misleading resultsScale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

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

# DotPlot with firebrick color for high expression
DotPlot(ss_Borcherding, 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)
  )
Warning: The following requested variables were not found: RIPOR2Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

Idents(ss_Borcherding) <- "seurat_clusters"

# DotPlot with firebrick color for high expression
DotPlot(ss_Borcherding, 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)
  )
Warning: The following requested variables were not found: RIPOR2Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

Save the Seurat object as an RDS



saveRDS(ss_Borcherding, file = "data/ss_Borcherding_Malignant_6_Normal_1_Integrated_object.rds")

11. Compare disease status using RNA assay

# Load required libraries
library(Seurat)
library(dplyr)
library(tibble)

combined_seu <- ss_Borcherding 

# Join the layers of the RNA assay
combined_seu <- JoinLayers(combined_seu, assay = "RNA")

# Ensure your identity class is set to disease status
Idents(combined_seu) <- "Disease_state"  # e.g., levels: "SS", "Control"

# Run differential expression between SS vs Control
markers_disease <- FindMarkers(
  object = combined_seu,
  ident.1 = "Malignant",
  ident.2 = "Healthy",
  assay = "RNA",
  logfc.threshold = 0,
  min.pct = 0,
  test.use = "wilcox"  # or "MAST" if RNA assay supports it
)

# Save results to CSV
write.csv(markers_disease, file = "DE_SS_vs_Healthy.csv", row.names = TRUE)

# Get log-normalized expression matrix (RNA assay)
expression_data_RNA <- GetAssayData(combined_seu, assay = "RNA", slot = "data")

# Get cell names for each group
ss_cells <- WhichCells(combined_seu, idents = "Malignant")
healthy_cells <- WhichCells(combined_seu, idents = "Healthy")

# Function to add mean expression per group
calculate_mean_expression <- function(markers, group1_cells, group2_cells, expression_data) {
  group1_mean <- rowMeans(expression_data[, group1_cells, drop = FALSE], na.rm = TRUE)
  group2_mean <- rowMeans(expression_data[, group2_cells, drop = FALSE], na.rm = TRUE)
  markers <- markers %>%
    rownames_to_column("gene") %>%
    mutate(
      mean_expr_SS = group1_mean[gene],
      mean_expr_Healthy = group2_mean[gene],
      log2FC_manual = log2(mean_expr_SS + 1) - log2(mean_expr_Healthy + 1)
    )
  return(markers)
}

# Apply the function and save final result
markers_disease_with_mean <- calculate_mean_expression(markers_disease, ss_cells, healthy_cells, expression_data_RNA)
write.csv(markers_disease_with_mean, "Borcherding_integrated_object_DE/DE_SS_vs_Healthy_with_MeanExpr_Borcherding2023.csv", row.names = FALSE)
LS0tCnRpdGxlOiAiUG90ZW50aWFsIGJpb21hcmtlcnMgVmFsaWRhdGlvbiAoQm9yY2hlcmRpbmdfNl9NYWxpZ25hbnRfYW5kIE5vbm1hbGlnbmFudF9QYXRpZW50X1NhbXBsZSkiCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgI3JtZGZvcm1hdHM6OnJlYWR0aGVkb3duCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfY29sbGFwc2VkOiB0cnVlCi0tLQoKIyAxLiBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KFNldXJhdE9iamVjdCkKbGlicmFyeShTZXVyYXREYXRhKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShBemltdXRoKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeSh0aW55dGV4KQoKCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZGl0dG9TZXEpCmxpYnJhcnkoZ2dyZXBlbCkKI2xpYnJhcnkoZ2d0cmVlKQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KHBsb3RseSkgICMgM0QgcGxvdApsaWJyYXJ5KFNldXJhdCkgICMgSWRlbnRzKCkKbGlicmFyeShTZXVyYXREaXNrKSAgIyBTYXZlSDVTZXVyYXQoKQpsaWJyYXJ5KHRpYmJsZSkgICMgcm93bm5hbWVzX3RvX2NvbHVtbgpsaWJyYXJ5KGhhcm1vbnkpICMgUnVuSGFybW9ueSgpCiNvcHRpb25zKG1jLmNvcmVzID0gZGV0ZWN0Q29yZXMoKSAtIDEpCgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShkYnBseXIpCmxpYnJhcnkocm1hcmtkb3duKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHRpbnl0ZXgpCiNBemltdXRoIEFubm90YXRpb24gbGlicmFyaWVzCmxpYnJhcnkoQXppbXV0aCkKI1Byb2plY1RpbHMgQW5ub3RhdGlvbiBsaWJyYXJpZXMKbGlicmFyeShTVEFDQVMpCmxpYnJhcnkoUHJvamVjVElMcykKI3NpbmdsZVIgQW5ub3RhdGlvbiBsaWJyYXJpZXMKCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCgpgYGAKCgojIDIuIExvYWQgRGF0YSBpbnRvIFNldXJhdApgYGB7ciBsb2FkX3NldXJhdH0KCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGRwbHlyKQoKIyBTZXQgZGlyZWN0b3J5IHBhdGhzCnNzX2RpciA8LSAiZGF0YS9NYWxpZ25hbnRfU1NfUGF0aWVudHMvIgpjb250cm9sX2RpciA8LSAiZGF0YS9Ob25fbWFsaWduYW50X3BhdGllbnRfQm9yY2hlcmRpbmdfMjAxOS8iCgojIExpc3QgYW5kIHNvcnQgcGF0aWVudCBkaXJlY3RvcmllcwpwYXRpZW50X2RpcnMgPC0gc29ydChsaXN0LmRpcnMoc3NfZGlyLCBmdWxsLm5hbWVzID0gVFJVRSwgcmVjdXJzaXZlID0gRkFMU0UpKQoKIyBFeHBsaWNpdCBuYW1lcyBmb3IgcGF0aWVudHMKcGF0aWVudF9uYW1lcyA8LSBwYXN0ZTAoIlNTX1AiLCAxOjYpCgojIEluaXRpYWxpemUgbGlzdCBmb3IgU2V1cmF0IG9iamVjdHMKc2V1cmF0X29iamVjdHMgPC0gbGlzdCgpCgojIExvYWQgcGF0aWVudCBkYXRhIGFuZCBjcmVhdGUgU2V1cmF0IG9iamVjdHMKZm9yIChpIGluIHNlcV9hbG9uZyhwYXRpZW50X2RpcnMpKSB7CiAgcGF0aWVudF9kaXIgPC0gcGF0aWVudF9kaXJzW2ldCiAgcGF0aWVudF9uYW1lIDwtIHBhdGllbnRfbmFtZXNbaV0KICAKICAjIExvYWQgMTBYIGRhdGEKICBzc19kYXRhIDwtIFJlYWQxMFgoZGF0YS5kaXIgPSBwYXRpZW50X2RpcikKICAKICAjIENyZWF0ZSBTZXVyYXQgb2JqZWN0CiAgc3MgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IHNzX2RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qZWN0ID0gcGF0aWVudF9uYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmNlbGxzID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5mZWF0dXJlcyA9IDIwMCkKICAKICAjIEFkZCBzYW1wbGUgbWV0YWRhdGEKICBzcyRzYW1wbGUgPC0gcGFzdGUoIk1hbGlnbmFudCIsIHBhdGllbnRfbmFtZSkKICAKICAjIFJlbmFtZSBjZWxsIGJhcmNvZGVzCiAgc3MgPC0gUmVuYW1lQ2VsbHMoc3MsIGFkZC5jZWxsLmlkID0gcGF0aWVudF9uYW1lKQogIAogICMgU3RvcmUgaW4gbGlzdAogIHNldXJhdF9vYmplY3RzW1twYXRpZW50X25hbWVdXSA8LSBzcwp9CgojIExvYWQgYW5kIHByZXBhcmUgY29udHJvbCBzYW1wbGUKY29udHJvbF9kYXRhIDwtIFJlYWQxMFgoZGF0YS5kaXIgPSBjb250cm9sX2RpcikKY29udHJvbCA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gY29udHJvbF9kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvamVjdCA9ICJDb250cm9sIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5jZWxscyA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZmVhdHVyZXMgPSAyMDApCmNvbnRyb2wkc2FtcGxlIDwtICJDb250cm9sIgpjb250cm9sIDwtIFJlbmFtZUNlbGxzKGNvbnRyb2wsIGFkZC5jZWxsLmlkID0gIkNUUkwiKQoKIyBNZXJnZSBhbGwgU2V1cmF0IG9iamVjdHMKYWxsX3NldXJhdCA8LSBSZWR1Y2UoZnVuY3Rpb24oeCwgeSkgbWVyZ2UoeCwgeSksIGMoc2V1cmF0X29iamVjdHMsIGxpc3QoY29udHJvbCkpKQoKIyBBZGQgU2FtcGxlX0lEIGZyb20gY2VsbCBiYXJjb2RlcwphbGxfc2V1cmF0JFNhbXBsZV9JRCA8LSBzYXBwbHkoc3Ryc3BsaXQoY29sbmFtZXMoYWxsX3NldXJhdCksICJfIiksIGBbYCwgMSkKCiMgQWRkIERpc2Vhc2Vfc3RhdGUKYWxsX3NldXJhdCREaXNlYXNlX3N0YXRlIDwtIGlmZWxzZShhbGxfc2V1cmF0JFNhbXBsZV9JRCA9PSAiQ1RSTCIsICJIZWFsdGh5IiwgIk1hbGlnbmFudCIpCgojIE1hcCBTYW1wbGVfSURzIHRvIFBhdGllbnRfSURzOiByZW1vdmUgIlNTXyIgcHJlZml4IHRvIGdldCBQMS1QNjsgIkNUUkwiIHRvIENvbnRyb2wKYWxsX3NldXJhdCRQYXRpZW50X0lEIDwtIGlmZWxzZShhbGxfc2V1cmF0JFNhbXBsZV9JRCA9PSAiQ1RSTCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvbnRyb2wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdzdWIoIl5TU19QIiwgIlAiLCBhbGxfc2V1cmF0JFNhbXBsZV9JRCkpCgojIEJhc2ljIFFDIGZpbHRlcmluZwphbGxfc2V1cmF0W1sicGVyY2VudC5tdCJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChhbGxfc2V1cmF0LCBwYXR0ZXJuID0gIl5NVC0iKQphbGxfc2V1cmF0IDwtIHN1YnNldChhbGxfc2V1cmF0LCBzdWJzZXQgPSBuRmVhdHVyZV9STkEgPiAyMDAgJiBuRmVhdHVyZV9STkEgPCAzNTAwICYgcGVyY2VudC5tdCA8IDkpCgoKc3NfQm9yY2hlcmRpbmcgPC0gYWxsX3NldXJhdAoKYWxsX3NldXJhdCRTYW1wbGVfSUQgPC0gTlVMTAphbGxfc2V1cmF0JFBhdGllbnRfSUQgPC0gTlVMTAoKIyBDaGVjayBtZXRhZGF0YSBkaXN0cmlidXRpb24KdGFibGUoYWxsX3NldXJhdCRvcmlnLmlkZW50KQoKYGBgCgojIDMuIFFDCmBgYHtyIFFDLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KClZsblBsb3Qoc3NfQm9yY2hlcmRpbmcsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuQ291bnRfUk5BIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwZXJjZW50Lm10IiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSAzKQoKVmxuUGxvdChzc19Cb3JjaGVyZGluZywgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibkNvdW50X1JOQSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwZXJjZW50Lm10IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGVyY2VudC5yYiIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSA0LCBwdC5zaXplID0gMC4xKSAmIAogICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpCgpGZWF0dXJlU2NhdHRlcihzc19Cb3JjaGVyZGluZywgCiAgICAgICAgICAgICAgIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCAKICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAibkZlYXR1cmVfUk5BIikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScpCgpgYGAKCgojI0ZlYXR1cmVTY2F0dGVyIGlzIHR5cGljYWxseSB1c2VkIHRvIHZpc3VhbGl6ZSBmZWF0dXJlLWZlYXR1cmUgcmVsYXRpb25zaGlwcwojI2ZvciBhbnl0aGluZyBjYWxjdWxhdGVkIGJ5IHRoZSBvYmplY3QsIAojI2kuZS4gY29sdW1ucyBpbiBvYmplY3QgbWV0YWRhdGEsIFBDIHNjb3JlcyBldGMuCgpgYGB7ciBGQywgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgpGZWF0dXJlU2NhdHRlcihzc19Cb3JjaGVyZGluZywgCiAgICAgICAgICAgICAgIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCAKICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAicGVyY2VudC5tdCIpKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScpCgpGZWF0dXJlU2NhdHRlcihzc19Cb3JjaGVyZGluZywgCiAgICAgICAgICAgICAgIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCAKICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAibkZlYXR1cmVfUk5BIikrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJykKCmBgYAojIDQuIExvZy1ub3JtYWxpemF0aW9uIChzY2FsZSBmYWN0b3IgPSAxMCwwMDApIGFuZCBJbnRlZ3JhdGlvbgpgYGB7ciBQQ0EsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKCmxpYnJhcnkoU2V1cmF0KQoKIyAxLiBTcGxpdCB0aGUgU2V1cmF0IG9iamVjdCBieSBwYXRpZW50X2lkIG9yIHNhbXBsZQpzc19saXN0IDwtIFNwbGl0T2JqZWN0KHNzX0JvcmNoZXJkaW5nLCBzcGxpdC5ieSA9ICJvcmlnLmlkZW50IikKCiMgMi4gTm9ybWFsaXplIGFuZCBmaW5kIHZhcmlhYmxlIGZlYXR1cmVzIGZvciBlYWNoIGRhdGFzZXQgaW5kZXBlbmRlbnRseQpzc19saXN0IDwtIGxhcHBseShzc19saXN0LCBmdW5jdGlvbih4KSB7CiAgeCA8LSBOb3JtYWxpemVEYXRhKHgsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIHNjYWxlLmZhY3RvciA9IDEwMDAwKQogIHggPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoeCwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAyMDAwKQogIHJldHVybih4KQp9KQoKIyAzLiBTZWxlY3QgaW50ZWdyYXRpb24gZmVhdHVyZXMgYW5kIGZpbmQgYW5jaG9ycwpmZWF0dXJlcyA8LSBTZWxlY3RJbnRlZ3JhdGlvbkZlYXR1cmVzKG9iamVjdC5saXN0ID0gc3NfbGlzdCwgbmZlYXR1cmVzID0gMjAwMCkKYW5jaG9ycyA8LSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKG9iamVjdC5saXN0ID0gc3NfbGlzdCwgYW5jaG9yLmZlYXR1cmVzID0gZmVhdHVyZXMsIHJlZHVjdGlvbiA9ICJjY2EiKQoKIyA0LiBJbnRlZ3JhdGUgZGF0YQpzc19pbnRlZ3JhdGVkIDwtIEludGVncmF0ZURhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycykKCiMgNS4gUHJvY2VlZCB3aXRoIGRvd25zdHJlYW0gYW5hbHlzaXMgb24gaW50ZWdyYXRlZCBkYXRhCkRlZmF1bHRBc3NheShzc19pbnRlZ3JhdGVkKSA8LSAiaW50ZWdyYXRlZCIKCiMgU2NhbGUgZGF0YQpzc19pbnRlZ3JhdGVkIDwtIFNjYWxlRGF0YShzc19pbnRlZ3JhdGVkLCB2ZXJib3NlID0gRkFMU0UpCgpgYGAKCgojIDUuIFBDQSArIFVNQVAKYGBge3IgLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCnNzX0JvcmNoZXJkaW5nIDwtIHNzX2ludGVncmF0ZWQKCiMgUENBCnNzX0JvcmNoZXJkaW5nIDwtIFJ1blBDQShzc19Cb3JjaGVyZGluZywgIGZlYXR1cmVzID0gVmFyaWFibGVGZWF0dXJlcyhzc19Cb3JjaGVyZGluZykpCgoKCgojIE9wdGlvbmFsOiBWaXN1YWxpemUgZWxib3cgcGxvdApFbGJvd1Bsb3Qoc3NfQm9yY2hlcmRpbmcsIG5kaW1zID0gNTApCgojICMgUnVuIEphY2tTdHJhdyBhbmFseXNpcyBwcm9wZXJseQojIHNzX0JvcmNoZXJkaW5nIDwtIEphY2tTdHJhdyhzc19Cb3JjaGVyZGluZywgbnVtLnJlcGxpY2F0ZSA9IDEwMCkKIyBzc19Cb3JjaGVyZGluZyA8LSBTY29yZUphY2tTdHJhdyhzc19Cb3JjaGVyZGluZywgZGltcyA9IDE6MjApCiMgCiMgIyBWaXN1YWxpemUgSmFja1N0cmF3IHNjb3JlcwojIEphY2tTdHJhd1Bsb3Qoc3NfQm9yY2hlcmRpbmcsIGRpbXMgPSAxOjIwKQoKYGBgCgojIDYuIFBDQSBWaXN1YWxpemF0aW9uCmBgYHtyIFBDQS1URVNUMiwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgoKCiMgVEVTVC0xCiMgZ2l2ZW4gdGhhdCB0aGUgb3V0cHV0IG9mIFJ1blBDQSBpcyAicGNhIgojIHJlcGxhY2UgInNvIiBieSB0aGUgbmFtZSBvZiB5b3VyIHNldXJhdCBvYmplY3QKCnBjdCA8LSBzc19Cb3JjaGVyZGluZ1tbInBjYSJdXUBzdGRldiAvIHN1bShzc19Cb3JjaGVyZGluZ1tbInBjYSJdXUBzdGRldikgKiAxMDAKY3VtdSA8LSBjdW1zdW0ocGN0KSAjIENhbGN1bGF0ZSBjdW11bGF0aXZlIHBlcmNlbnRzIGZvciBlYWNoIFBDCiMgRGV0ZXJtaW5lIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdmFyaWF0aW9uIG9mIFBDIGFuZCBzdWJzZXF1ZW50IFBDCmNvMiA8LSBzb3J0KHdoaWNoKChwY3RbLWxlbmd0aChwY3QpXSAtIHBjdFstMV0pID4gMC4xKSwgZGVjcmVhc2luZyA9IFQpWzFdICsgMQojIGxhc3QgcG9pbnQgd2hlcmUgY2hhbmdlIG9mICUgb2YgdmFyaWF0aW9uIGlzIG1vcmUgdGhhbiAwLjElLiAtPiBjbzIKY28yCgojIFRFU1QtMgojIGdldCBzaWduaWZpY2FudCBQQ3MKc3RkdiA8LSBzc19Cb3JjaGVyZGluZ1tbInBjYSJdXUBzdGRldgpzdW0uc3RkdiA8LSBzdW0oc3NfQm9yY2hlcmRpbmdbWyJwY2EiXV1Ac3RkZXYpCnBlcmNlbnQuc3RkdiA8LSAoc3RkdiAvIHN1bS5zdGR2KSAqIDEwMApjdW11bGF0aXZlIDwtIGN1bXN1bShwZXJjZW50LnN0ZHYpCmNvMSA8LSB3aGljaChjdW11bGF0aXZlID4gOTAgJiBwZXJjZW50LnN0ZHYgPCA1KVsxXQpjbzIgPC0gc29ydCh3aGljaCgocGVyY2VudC5zdGR2WzE6bGVuZ3RoKHBlcmNlbnQuc3RkdikgLSAxXSAtIAogICAgICAgICAgICAgICAgICAgICAgIHBlcmNlbnQuc3RkdlsyOmxlbmd0aChwZXJjZW50LnN0ZHYpXSkgPiAwLjEpLCAKICAgICAgICAgICAgICBkZWNyZWFzaW5nID0gVClbMV0gKyAxCm1pbi5wYyA8LSBtaW4oY28xLCBjbzIpCm1pbi5wYwoKIyBDcmVhdGUgYSBkYXRhZnJhbWUgd2l0aCB2YWx1ZXMKcGxvdF9kZiA8LSBkYXRhLmZyYW1lKHBjdCA9IHBlcmNlbnQuc3RkdiwgCiAgICAgICAgICAgY3VtdSA9IGN1bXVsYXRpdmUsIAogICAgICAgICAgIHJhbmsgPSAxOmxlbmd0aChwZXJjZW50LnN0ZHYpKQoKIyBFbGJvdyBwbG90IHRvIHZpc3VhbGl6ZSAKICBnZ3Bsb3QocGxvdF9kZiwgYWVzKGN1bXVsYXRpdmUsIHBlcmNlbnQuc3RkdiwgbGFiZWwgPSByYW5rLCBjb2xvciA9IHJhbmsgPiBtaW4ucGMpKSArIAogIGdlb21fdGV4dCgpICsgCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gOTAsIGNvbG9yID0gImdyZXkiKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1pbihwZXJjZW50LnN0ZHZbcGVyY2VudC5zdGR2ID4gNV0pLCBjb2xvciA9ICJncmV5IikgKwogIHRoZW1lX2J3KCkKCiAgCgpgYGAKCiMgNy4gQ2x1c3RlcmluZyAocmVzb2x1dGlvbiA9IDEuMikKYGBge3IgLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KIyBUaGVuIGZpbmQgbmVpZ2hib3JzICYgY2x1c3RlcnMKc3NfQm9yY2hlcmRpbmcgPC0gRmluZE5laWdoYm9ycyhzc19Cb3JjaGVyZGluZywgZGltcyA9IDE6NDApCnNzX0JvcmNoZXJkaW5nIDwtIEZpbmRDbHVzdGVycyhzc19Cb3JjaGVyZGluZywgcmVzb2x1dGlvbiA9IDAuNSkKCiMgcnVuIFVNQVAKc3NfQm9yY2hlcmRpbmcgPC0gUnVuVU1BUChzc19Cb3JjaGVyZGluZywgZGltcyA9IDE6NDApCgpzc19Cb3JjaGVyZGluZyA8LVJ1blRTTkUoc3NfQm9yY2hlcmRpbmcsIGRpbXMudXNlID0gMTo0MCkKCmBgYAoKCiMgOC4gVmlzdWFsaXplIFVNQVAgd2l0aCBDbHVzdGVycwpgYGB7ciAsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKRGltUGxvdChzc19Cb3JjaGVyZGluZywgcmVkdWN0aW9uID0gInVtYXAiLGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLCBsYWJlbCA9IFRSVUUscmVwZWwgPSBULCBwdC5zaXplID0gMC42KSArCiAgZ2d0aXRsZSgiVU1BUCBvZiBTw6l6YXJ5IFN5bmRyb21lIENENCsgVCBDZWxscyIpCgoKRGltUGxvdChzc19Cb3JjaGVyZGluZywgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUscmVwZWwgPSBULCBwdC5zaXplID0gMC42KSArCiAgZ2d0aXRsZSgiVU1BUCBvZiBTw6l6YXJ5IFN5bmRyb21lIENENCsgVCBDZWxscyIpCgoKRGltUGxvdChzc19Cb3JjaGVyZGluZywgcmVkdWN0aW9uID0gInRzbmUiLCBsYWJlbCA9IFRSVUUsIHB0LnNpemUgPSAwLjYpICsKICBnZ3RpdGxlKCJUU05FIG9mIFPDqXphcnkgU3luZHJvbWUgQ0Q0KyBUIENlbGxzIikKCmBgYAoKIyA5LiAgRmVhdHVyZVBsb3RzIGZvciBUb3A1MCBVUApgYGB7ciAsIGZpZy5oZWlnaHQ9MTYsIGZpZy53aWR0aD0yMH0KdG9wXzUwX3VwIDwtIHJlYWQuY3N2KCIuLi9EYXRhX1NTX0JvcmNoZXJkaW5nXzIwMTkvdG9wXzUwX3VwcmVndWxhdGVkLmNzdiIpICAgICAgICAjIG9yIHJlYWQuZGVsaW0oInRvcF81MF91cC50c3YiKQp0b3BfNTBfZG93biA8LSByZWFkLmNzdigiLi4vRGF0YV9TU19Cb3JjaGVyZGluZ18yMDE5L3RvcF81MF9kb3ducmVndWxhdGVkLmNzdiIpCgpJZGVudHMoc3NfQm9yY2hlcmRpbmcpIDwtICJzZXVyYXRfY2x1c3RlcnMiCgoKRmVhdHVyZVBsb3Qoc3NfQm9yY2hlcmRpbmcsIAogICAgICAgICAgICAgZmVhdHVyZXMgPSB0b3BfNTBfdXAkZ2VuZVsxOjEwXSwgCiAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Ymx1ZSIsICJyZWQiKSwgICMgQ3VzdG9tIGNvbG9yIGdyYWRpZW50IGZyb20gbGlnaHQgYmx1ZSB0byByZWQKICAgICAgICAgICAgIGxhYmVsID0gVFJVRSkKCgpGZWF0dXJlUGxvdChzc19Cb3JjaGVyZGluZywgCiAgICAgICAgICAgICBmZWF0dXJlcyA9IHRvcF81MF91cCRnZW5lWzExOjIwXSwgCiAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Ymx1ZSIsICJyZWQiKSwgICMgQ3VzdG9tIGNvbG9yIGdyYWRpZW50IGZyb20gbGlnaHQgYmx1ZSB0byByZWQKICAgICAgICAgICAgIGxhYmVsID0gVFJVRSkKCkZlYXR1cmVQbG90KHNzX0JvcmNoZXJkaW5nLCAKICAgICAgICAgICAgIGZlYXR1cmVzID0gdG9wXzUwX3VwJGdlbmVbMjE6MzBdLCAKICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgCiAgICAgICAgICAgICBjb2xzID0gYygibGlnaHRibHVlIiwgInJlZCIpLCAgIyBDdXN0b20gY29sb3IgZ3JhZGllbnQgZnJvbSBsaWdodCBibHVlIHRvIHJlZAogICAgICAgICAgICAgbGFiZWwgPSBUUlVFKQpGZWF0dXJlUGxvdChzc19Cb3JjaGVyZGluZywgCiAgICAgICAgICAgICBmZWF0dXJlcyA9IHRvcF81MF91cCRnZW5lWzMxOjQwXSwgCiAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Ymx1ZSIsICJyZWQiKSwgICMgQ3VzdG9tIGNvbG9yIGdyYWRpZW50IGZyb20gbGlnaHQgYmx1ZSB0byByZWQKICAgICAgICAgICAgIGxhYmVsID0gVFJVRSkKRmVhdHVyZVBsb3Qoc3NfQm9yY2hlcmRpbmcsIAogICAgICAgICAgICAgZmVhdHVyZXMgPSB0b3BfNTBfdXAkZ2VuZVs0MTo1MF0sIAogICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLCAKICAgICAgICAgICAgIGNvbHMgPSBjKCJsaWdodGJsdWUiLCAicmVkIiksICAjIEN1c3RvbSBjb2xvciBncmFkaWVudCBmcm9tIGxpZ2h0IGJsdWUgdG8gcmVkCiAgICAgICAgICAgICBsYWJlbCA9IFRSVUUpCgpgYGAKCgoKIyAxMC4gIEZlYXR1cmVQbG90cyBmb3IgVG9wNTAgRE9XTgpgYGB7ciAsIGZpZy5oZWlnaHQ9MTYsIGZpZy53aWR0aD0yMH0KCkZlYXR1cmVQbG90KHNzX0JvcmNoZXJkaW5nLCAKICAgICAgICAgICAgIGZlYXR1cmVzID0gdG9wXzUwX2Rvd24kZ2VuZVsxOjEwXSwgCiAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Ymx1ZSIsICJyZWQiKSwgICMgQ3VzdG9tIGNvbG9yIGdyYWRpZW50IGZyb20gbGlnaHQgYmx1ZSB0byByZWQKICAgICAgICAgICAgIGxhYmVsID0gVFJVRSkKCgpGZWF0dXJlUGxvdChzc19Cb3JjaGVyZGluZywgCiAgICAgICAgICAgICBmZWF0dXJlcyA9IHRvcF81MF9kb3duJGdlbmVbMTE6MjBdLCAKICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgCiAgICAgICAgICAgICBjb2xzID0gYygibGlnaHRibHVlIiwgInJlZCIpLCAgIyBDdXN0b20gY29sb3IgZ3JhZGllbnQgZnJvbSBsaWdodCBibHVlIHRvIHJlZAogICAgICAgICAgICAgbGFiZWwgPSBUUlVFKQoKRmVhdHVyZVBsb3Qoc3NfQm9yY2hlcmRpbmcsIAogICAgICAgICAgICAgZmVhdHVyZXMgPSB0b3BfNTBfZG93biRnZW5lWzIxOjMwXSwgCiAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Ymx1ZSIsICJyZWQiKSwgICMgQ3VzdG9tIGNvbG9yIGdyYWRpZW50IGZyb20gbGlnaHQgYmx1ZSB0byByZWQKICAgICAgICAgICAgIGxhYmVsID0gVFJVRSkKRmVhdHVyZVBsb3Qoc3NfQm9yY2hlcmRpbmcsIAogICAgICAgICAgICAgZmVhdHVyZXMgPSB0b3BfNTBfZG93biRnZW5lWzMxOjQwXSwgCiAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Ymx1ZSIsICJyZWQiKSwgICMgQ3VzdG9tIGNvbG9yIGdyYWRpZW50IGZyb20gbGlnaHQgYmx1ZSB0byByZWQKICAgICAgICAgICAgIGxhYmVsID0gVFJVRSkKRmVhdHVyZVBsb3Qoc3NfQm9yY2hlcmRpbmcsIAogICAgICAgICAgICAgZmVhdHVyZXMgPSB0b3BfNTBfZG93biRnZW5lWzQxOjUwXSwgCiAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsIAogICAgICAgICAgICAgY29scyA9IGMoImxpZ2h0Ymx1ZSIsICJyZWQiKSwgICMgQ3VzdG9tIGNvbG9yIGdyYWRpZW50IGZyb20gbGlnaHQgYmx1ZSB0byByZWQKICAgICAgICAgICAgIGxhYmVsID0gVFJVRSkKYGBgCgojIyBWaXN1YWxpemF0aW9uCmBgYHtyICwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgoKRGltUGxvdChzc19Cb3JjaGVyZGluZywgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwgbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBULCByZXBlbCA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikKCgpgYGAKCiMjIFZpc3VhbGl6YXRpb24gb2YgUG90ZW50aWFsIGJpb21hcmtlcnMtVXByZWd1bGF0ZWQKYGBge3IgLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCkRlZmF1bHRBc3NheShzc19Cb3JjaGVyZGluZykgPC0gIlJOQSIKSWRlbnRzKHNzX0JvcmNoZXJkaW5nKSA8LSAiRGlzZWFzZV9zdGF0ZSIKCiMgVmVjdG9yIG9mIGdlbmVzIHRvIHBsb3QKdXBfZ2VuZXMgPC0gYygiQ0xJQzEiLCAiQ09YNUEiLCJHVFNGMSIsICJNQUQyTDEiLCJNWUJMMiIsIk1ZTDZCIiwiTk1FMSIsIlBMSzEiLCAiUFlDUjEiLCAiU0xDMjVBNSIsICJTUkkiLCAiVFVCQTFDIiwgIlVCRTJUIiwgIllXSEFIIikKCiMgRG90UGxvdCB3aXRoIGN1c3RvbSBmaXJlYnJpY2stcmVkIGdyYWRpZW50CkRvdFBsb3Qoc3NfQm9yY2hlcmRpbmcsIGZlYXR1cmVzID0gdXBfZ2VuZXMpICsKICBSb3RhdGVkQXhpcygpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIobG93ID0gImxpZ2h0eWVsbG93IiwgbWlkID0gInJlZCIsIGhpZ2ggPSAiZmlyZWJyaWNrIiwgbWlkcG9pbnQgPSAxKSArCiAgZ2d0aXRsZSgiRXhwcmVzc2lvbiBvZiBVcHJlZ3VsYXRlZCBHZW5lcyBpbiBTw6l6YXJ5IFN5bmRyb21lIikgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCkKICApCgpJZGVudHMoc3NfQm9yY2hlcmRpbmcpIDwtICJvcmlnLmlkZW50IgoKIyBWZWN0b3Igb2YgZ2VuZXMgdG8gcGxvdAp1cF9nZW5lcyA8LSBjKCJDTElDMSIsICJDT1g1QSIsIkdUU0YxIiwgIk1BRDJMMSIsIk1ZQkwyIiwiTVlMNkIiLCJOTUUxIiwiUExLMSIsICJQWUNSMSIsICJTTEMyNUE1IiwgIlNSSSIsICJUVUJBMUMiLCAiVUJFMlQiLCAiWVdIQUgiKQoKIyBEb3RQbG90IHdpdGggY3VzdG9tIGZpcmVicmljay1yZWQgZ3JhZGllbnQKRG90UGxvdChzc19Cb3JjaGVyZGluZywgZmVhdHVyZXMgPSB1cF9nZW5lcykgKwogIFJvdGF0ZWRBeGlzKCkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50Mihsb3cgPSAibGlnaHR5ZWxsb3ciLCBtaWQgPSAicmVkIiwgaGlnaCA9ICJmaXJlYnJpY2siLCBtaWRwb2ludCA9IDEpICsKICBnZ3RpdGxlKCJFeHByZXNzaW9uIG9mIFVwcmVndWxhdGVkIEdlbmVzIGluIFPDqXphcnkgU3luZHJvbWUiKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSAxMiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KQogICkKCklkZW50cyhzc19Cb3JjaGVyZGluZykgPC0gInNldXJhdF9jbHVzdGVycyIKCiMgVmVjdG9yIG9mIGdlbmVzIHRvIHBsb3QKdXBfZ2VuZXMgPC0gYygiQ0xJQzEiLCAiQ09YNUEiLCJHVFNGMSIsICJNQUQyTDEiLCJNWUJMMiIsIk1ZTDZCIiwiTk1FMSIsIlBMSzEiLCAiUFlDUjEiLCAiU0xDMjVBNSIsICJTUkkiLCAiVFVCQTFDIiwgIlVCRTJUIiwgIllXSEFIIikKCiMgRG90UGxvdCB3aXRoIGN1c3RvbSBmaXJlYnJpY2stcmVkIGdyYWRpZW50CkRvdFBsb3Qoc3NfQm9yY2hlcmRpbmcsIGZlYXR1cmVzID0gdXBfZ2VuZXMpICsKICBSb3RhdGVkQXhpcygpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIobG93ID0gImxpZ2h0eWVsbG93IiwgbWlkID0gInJlZCIsIGhpZ2ggPSAiZmlyZWJyaWNrIiwgbWlkcG9pbnQgPSAxKSArCiAgZ2d0aXRsZSgiRXhwcmVzc2lvbiBvZiBVcHJlZ3VsYXRlZCBHZW5lcyBpbiBTw6l6YXJ5IFN5bmRyb21lIikgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCkKICApCgoKYGBgCgoKCiMjIFZpc3VhbGl6YXRpb24gb2YgUG90ZW50aWFsIGJpb21hcmtlcnMtRG93bnJlZ3VsYXRlZApgYGB7ciBDMiwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgpJZGVudHMoc3NfQm9yY2hlcmRpbmcpIDwtICJEaXNlYXNlX3N0YXRlIgojIERvd25yZWd1bGF0ZWQgZ2VuZXMKZG93bl9nZW5lcyA8LSBjKCJUWE5JUCIsICJSQVNBMyIsICJSSVBPUjIiLCAKICAgICAgICAgICAgICAgICJaRlAzNiIsICJaRlAzNkwxIiwgIlpGUDM2TDIiLAogICAgICAgICAgICAgICAgIlBSTVQyIiwgIk1BWCIsICJQSUszSVAxIiwgCiAgICAgICAgICAgICAgICAiQlRHMSIsICJDREtOMUIiKQoKIyBEb3RQbG90IHdpdGggZmlyZWJyaWNrIGNvbG9yIGZvciBoaWdoIGV4cHJlc3Npb24KRG90UGxvdChzc19Cb3JjaGVyZGluZywgZmVhdHVyZXMgPSBkb3duX2dlbmVzKSArCiAgUm90YXRlZEF4aXMoKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKGxvdyA9ICJsaWdodHllbGxvdyIsIG1pZCA9ICJyZWQiLCBoaWdoID0gImZpcmVicmljayIsIG1pZHBvaW50ID0gMSkgKwogIGdndGl0bGUoIkV4cHJlc3Npb24gb2YgRG93bnJlZ3VsYXRlZCBHZW5lcyBpbiBTw6l6YXJ5IFN5bmRyb21lIikgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCkKICApCgoKSWRlbnRzKHNzX0JvcmNoZXJkaW5nKSA8LSAib3JpZy5pZGVudCIKIyBEb3ducmVndWxhdGVkIGdlbmVzCmRvd25fZ2VuZXMgPC0gYygiVFhOSVAiLCAiUkFTQTMiLCAiUklQT1IyIiwgCiAgICAgICAgICAgICAgICAiWkZQMzYiLCAiWkZQMzZMMSIsICJaRlAzNkwyIiwKICAgICAgICAgICAgICAgICJQUk1UMiIsICJNQVgiLCAiUElLM0lQMSIsIAogICAgICAgICAgICAgICAgIkJURzEiLCAiQ0RLTjFCIikKCiMgRG90UGxvdCB3aXRoIGZpcmVicmljayBjb2xvciBmb3IgaGlnaCBleHByZXNzaW9uCkRvdFBsb3Qoc3NfQm9yY2hlcmRpbmcsIGZlYXR1cmVzID0gZG93bl9nZW5lcykgKwogIFJvdGF0ZWRBeGlzKCkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50Mihsb3cgPSAibGlnaHR5ZWxsb3ciLCBtaWQgPSAicmVkIiwgaGlnaCA9ICJmaXJlYnJpY2siLCBtaWRwb2ludCA9IDEpICsKICBnZ3RpdGxlKCJFeHByZXNzaW9uIG9mIERvd25yZWd1bGF0ZWQgR2VuZXMgaW4gU8OpemFyeSBTeW5kcm9tZSIpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDEyKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBzaXplID0gMTQpCiAgKQpJZGVudHMoc3NfQm9yY2hlcmRpbmcpIDwtICJzZXVyYXRfY2x1c3RlcnMiCgojIERvdFBsb3Qgd2l0aCBmaXJlYnJpY2sgY29sb3IgZm9yIGhpZ2ggZXhwcmVzc2lvbgpEb3RQbG90KHNzX0JvcmNoZXJkaW5nLCBmZWF0dXJlcyA9IGRvd25fZ2VuZXMpICsKICBSb3RhdGVkQXhpcygpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIobG93ID0gImxpZ2h0eWVsbG93IiwgbWlkID0gInJlZCIsIGhpZ2ggPSAiZmlyZWJyaWNrIiwgbWlkcG9pbnQgPSAxKSArCiAgZ2d0aXRsZSgiRXhwcmVzc2lvbiBvZiBEb3ducmVndWxhdGVkIEdlbmVzIGluIFPDqXphcnkgU3luZHJvbWUiKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSAxMiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KQogICkKYGBgCgojIFNhdmUgdGhlIFNldXJhdCBvYmplY3QgYXMgYW4gUkRTCmBgYHtyIHNhdmVSRFN9CgoKc2F2ZVJEUyhzc19Cb3JjaGVyZGluZywgZmlsZSA9ICJkYXRhL3NzX0JvcmNoZXJkaW5nX01hbGlnbmFudF82X05vcm1hbF8xX0ludGVncmF0ZWRfb2JqZWN0LnJkcyIpCgoKYGBgCgojIDExLiBDb21wYXJlIGRpc2Vhc2Ugc3RhdHVzIHVzaW5nIFJOQSBhc3NheQpgYGB7ciBhemltdXRoX0Fubm90YXRpb24sIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTZ9CiMgTG9hZCByZXF1aXJlZCBsaWJyYXJpZXMKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGliYmxlKQoKY29tYmluZWRfc2V1IDwtIHNzX0JvcmNoZXJkaW5nIAoKIyBKb2luIHRoZSBsYXllcnMgb2YgdGhlIFJOQSBhc3NheQpjb21iaW5lZF9zZXUgPC0gSm9pbkxheWVycyhjb21iaW5lZF9zZXUsIGFzc2F5ID0gIlJOQSIpCgojIEVuc3VyZSB5b3VyIGlkZW50aXR5IGNsYXNzIGlzIHNldCB0byBkaXNlYXNlIHN0YXR1cwpJZGVudHMoY29tYmluZWRfc2V1KSA8LSAiRGlzZWFzZV9zdGF0ZSIgICMgZS5nLiwgbGV2ZWxzOiAiU1MiLCAiQ29udHJvbCIKCiMgUnVuIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJldHdlZW4gU1MgdnMgQ29udHJvbAptYXJrZXJzX2Rpc2Vhc2UgPC0gRmluZE1hcmtlcnMoCiAgb2JqZWN0ID0gY29tYmluZWRfc2V1LAogIGlkZW50LjEgPSAiTWFsaWduYW50IiwKICBpZGVudC4yID0gIkhlYWx0aHkiLAogIGFzc2F5ID0gIlJOQSIsCiAgbG9nZmMudGhyZXNob2xkID0gMCwKICBtaW4ucGN0ID0gMCwKICB0ZXN0LnVzZSA9ICJ3aWxjb3giICAjIG9yICJNQVNUIiBpZiBSTkEgYXNzYXkgc3VwcG9ydHMgaXQKKQoKIyBTYXZlIHJlc3VsdHMgdG8gQ1NWCndyaXRlLmNzdihtYXJrZXJzX2Rpc2Vhc2UsIGZpbGUgPSAiREVfU1NfdnNfSGVhbHRoeS5jc3YiLCByb3cubmFtZXMgPSBUUlVFKQoKIyBHZXQgbG9nLW5vcm1hbGl6ZWQgZXhwcmVzc2lvbiBtYXRyaXggKFJOQSBhc3NheSkKZXhwcmVzc2lvbl9kYXRhX1JOQSA8LSBHZXRBc3NheURhdGEoY29tYmluZWRfc2V1LCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImRhdGEiKQoKIyBHZXQgY2VsbCBuYW1lcyBmb3IgZWFjaCBncm91cApzc19jZWxscyA8LSBXaGljaENlbGxzKGNvbWJpbmVkX3NldSwgaWRlbnRzID0gIk1hbGlnbmFudCIpCmhlYWx0aHlfY2VsbHMgPC0gV2hpY2hDZWxscyhjb21iaW5lZF9zZXUsIGlkZW50cyA9ICJIZWFsdGh5IikKCiMgRnVuY3Rpb24gdG8gYWRkIG1lYW4gZXhwcmVzc2lvbiBwZXIgZ3JvdXAKY2FsY3VsYXRlX21lYW5fZXhwcmVzc2lvbiA8LSBmdW5jdGlvbihtYXJrZXJzLCBncm91cDFfY2VsbHMsIGdyb3VwMl9jZWxscywgZXhwcmVzc2lvbl9kYXRhKSB7CiAgZ3JvdXAxX21lYW4gPC0gcm93TWVhbnMoZXhwcmVzc2lvbl9kYXRhWywgZ3JvdXAxX2NlbGxzLCBkcm9wID0gRkFMU0VdLCBuYS5ybSA9IFRSVUUpCiAgZ3JvdXAyX21lYW4gPC0gcm93TWVhbnMoZXhwcmVzc2lvbl9kYXRhWywgZ3JvdXAyX2NlbGxzLCBkcm9wID0gRkFMU0VdLCBuYS5ybSA9IFRSVUUpCiAgbWFya2VycyA8LSBtYXJrZXJzICU+JQogICAgcm93bmFtZXNfdG9fY29sdW1uKCJnZW5lIikgJT4lCiAgICBtdXRhdGUoCiAgICAgIG1lYW5fZXhwcl9TUyA9IGdyb3VwMV9tZWFuW2dlbmVdLAogICAgICBtZWFuX2V4cHJfSGVhbHRoeSA9IGdyb3VwMl9tZWFuW2dlbmVdLAogICAgICBsb2cyRkNfbWFudWFsID0gbG9nMihtZWFuX2V4cHJfU1MgKyAxKSAtIGxvZzIobWVhbl9leHByX0hlYWx0aHkgKyAxKQogICAgKQogIHJldHVybihtYXJrZXJzKQp9CgojIEFwcGx5IHRoZSBmdW5jdGlvbiBhbmQgc2F2ZSBmaW5hbCByZXN1bHQKbWFya2Vyc19kaXNlYXNlX3dpdGhfbWVhbiA8LSBjYWxjdWxhdGVfbWVhbl9leHByZXNzaW9uKG1hcmtlcnNfZGlzZWFzZSwgc3NfY2VsbHMsIGhlYWx0aHlfY2VsbHMsIGV4cHJlc3Npb25fZGF0YV9STkEpCndyaXRlLmNzdihtYXJrZXJzX2Rpc2Vhc2Vfd2l0aF9tZWFuLCAiQm9yY2hlcmRpbmdfaW50ZWdyYXRlZF9vYmplY3RfREUvREVfU1NfdnNfSGVhbHRoeV93aXRoX01lYW5FeHByX0JvcmNoZXJkaW5nMjAyMy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCmBgYAoKCg==