1. load libraries

library(dplyr)
library(Seurat)
library(ComplexHeatmap)
# https://github.com/immunogenomics/presto
library(presto)
library(tictoc)

library(plot1cell)

2. Load Seurat Object


#Load Seurat Object merged from cell lines and a control(PBMC) after filtration
load("../../To_Transfer_between_computers/23-Harmony_Integration/0-robj/5-Harmony_Integrated_All_samples_Merged_CD4Tcells_final_Resolution_Selected_0.8_ADT_Normalized_cleaned_mt.robj")

3. Circlize plot to visualize cell clustering and meta data

All_samples_Merged$Condition <- ifelse(
  All_samples_Merged$cell_line %in% c("L1", "L2", "L3", "L4", "L5", "L6", "L7"),  # your malignant lines
  "Malignant",
  "Control"
)

library(plot1cell)

SS.integrated <- All_samples_Merged

###Check and see the meta data info on your Seurat object
colnames(SS.integrated@meta.data)  
 [1] "orig.ident"                         "nCount_RNA"                         "nFeature_RNA"                      
 [4] "nCount_ADT"                         "nFeature_ADT"                       "nUMI"                              
 [7] "ngene"                              "cell_line"                          "Patient_origin"                    
[10] "Patient_Immunophenotype"            "condition_of_amplification_in_vivo" "culture_medium"                    
[13] "Stromal_cells"                      "Cell_line_Immunophenotype"          "TP53_mutation"                     
[16] "age_at_diagnosis"                   "stage_diagnosis"                    "stage_analysis"                    
[19] "Treatments_analysis"                "TCRVB2"                             "CD3_M"                             
[22] "CD30_M"                             "CCR4_M"                             "CD162_BL"                          
[25] "CD26_BL"                            "CD7_M"                              "CLA_BD"                            
[28] "CD4_BD"                             "CCR7_M"                             "CD45RO_BD"                         
[31] "CD45RA_BD"                          "CellName"                           "percent.mt"                        
[34] "CD26_BD"                            "CD45RA_M"                           "predicted.celltype.l1.score"       
[37] "predicted.celltype.l1"              "predicted.celltype.l2.score"        "predicted.celltype.l2"             
[40] "predicted.celltype.l3.score"        "predicted.celltype.l3"              "mapping.score"                     
[43] "percent.rb"                         "nCount_SCT"                         "nFeature_SCT"                      
[46] "S.Score"                            "G2M.Score"                          "Phase"                             
[49] "old.ident"                          "CC.Difference"                      "SCT_snn_res.0.4"                   
[52] "SCT_snn_res.0.5"                    "SCT_snn_res.0.6"                    "SCT_snn_res.0.7"                   
[55] "SCT_snn_res.0.8"                    "seurat_clusters"                    "harmony_res_0.1"                   
[58] "harmony_res_0.2"                    "harmony_res_0.3"                    "harmony_res_0.4"                   
[61] "harmony_res_0.5"                    "harmony_res_0.6"                    "harmony_res_0.7"                   
[64] "harmony_res_0.8"                    "harmony_res_0.9"                    "harmony_res_1"                     
[67] "harmony_res_1.2"                    "predicted.celltype.l1_backup"       "predicted.celltype.l2_backup"      
[70] "predicted.celltype.l3_backup"       "Condition"                         
###Prepare data for ploting
circ_data <- prepare_circlize_data(SS.integrated, scale = 0.8 )
set.seed(1234)
cluster_colors<-rand_color(length(levels(SS.integrated)))
group_colors<-rand_color(length(names(table(SS.integrated$Condition))))
rep_colors<-rand_color(length(names(table(SS.integrated$cell_line))))

###plot and save figures
png(filename =  'circlize_plot.png', width = 6, height = 6,units = 'in', res = 300)
plot_circlize(circ_data,do.label = T, pt.size = 0.01, col.use = cluster_colors ,bg.color = 'white', kde2d.n = 200, repel = T, label.cex = 0.6)
add_track(circ_data, group = "Condition", colors = group_colors, track_num = 2) ## can change it to one of the columns in the meta data of your seurat object
add_track(circ_data, group = "cell_line",colors = rep_colors, track_num = 3) ## can change it to one of the columns in the meta data of your seurat object
dev.off()
null device 
          1 
plot_circlize(circ_data,do.label = T, pt.size = 0.01, col.use = cluster_colors ,bg.color = 'white', kde2d.n = 200, repel = T, label.cex = 0.6)
add_track(circ_data, group = "Condition", colors = group_colors, track_num = 2) ## can change it to one of the columns in the meta data of your seurat object
add_track(circ_data, group = "cell_line",colors = rep_colors, track_num = 3) ## can change it to one of the columns in the meta data of your seurat object

Dotplot to show gene expression across groups


png(filename = 'dotplot_CLIC1.png', width = 4, height = 6, units = 'in', res = 100)
complex_dotplot_single(seu_obj = SS.integrated, feature = "CLIC1", groups = "Condition")
dev.off()
null device 
          1 
complex_dotplot_single(seu_obj = SS.integrated, feature = "CLIC1", groups = "Condition")


png(filename =  'dotplot_IL4.png', width = 4, height = 6,units = 'in', res = 100)
complex_dotplot_single(seu_obj = SS.integrated, feature = "IL4", groups = "Condition")
dev.off()
png 
  2 

complex_dotplot_single(seu_obj = SS.integrated, feature = "IL4", groups = "Condition")


png(filename =  'dotplot_IL7R.png', width = 4, height = 6,units = 'in', res = 100)
complex_dotplot_single(seu_obj = SS.integrated, feature = "IL7R", groups = "Condition")
dev.off()
png 
  2 

complex_dotplot_single(seu_obj = SS.integrated, feature = "IL7R", groups = "Condition")

Dotplot to show gene expression across groups

SS.integrated@meta.data$Status <- plyr::mapvalues(
  SS.integrated@meta.data$Condition, 
  from = c("Control", "Malignant"),
  to = c("Healthy", "Malignant")
)
SS.integrated@meta.data$Status <- as.character(SS.integrated@meta.data$Status)


png(filename = 'dotplot_single_split.png', width = 4, height = 6, units = 'in', res = 100)
complex_dotplot_single(seu_obj = SS.integrated, feature = "CLIC1", groups = "Condition", splitby = "Status")
NULL
dev.off()
null device 
          1 
complex_dotplot_single(seu_obj = SS.integrated, feature = "CLIC1", groups = "Condition", splitby = "Status")
NULL
png(filename = 'dotplot_more_groups.png', width = 8, height = 6, units = 'in', res = 100)
complex_dotplot_single(
  seu_obj = SS.integrated,
  feature = "CLIC1",
  groups = c("Condition", "orig.ident")  # you can replace "orig.ident" if you have another replicates info
)
NULL
dev.off()
png 
  2 
complex_dotplot_single(
  seu_obj = SS.integrated,
  feature = "CLIC1",
  groups = c("Condition", "orig.ident")  # you can replace "orig.ident" if you have another replicates info
)
NULL
# 1. Create Condition (if you haven't already)
# Assuming you have orig.ident with L1–L7 (malignant) and PBMC1–2 (control)
SS.integrated@meta.data$Condition <- plyr::mapvalues(
  SS.integrated@meta.data$orig.ident,
  from = levels(factor(SS.integrated@meta.data$orig.ident)),  # dynamically get L1–L7 + PBMCs
  to = c(rep("Malignant", 7), rep("Control", 2))
)
SS.integrated@meta.data$Condition <- as.character(SS.integrated@meta.data$Condition)

# 2. Create ReplicateID (fake replicate names just for plotting clarity)
SS.integrated@meta.data$ReplicateID <- plyr::mapvalues(
  SS.integrated@meta.data$orig.ident,
  from = names(table(SS.integrated@meta.data$orig.ident)),
  to = c(paste0("Malignant_", 1:7), paste0("Control_", 1:2))
)
SS.integrated@meta.data$ReplicateID <- as.character(SS.integrated@meta.data$ReplicateID)

# 3. Create GroupType (instead of Phase, because you already have Phase for cell cycle)
SS.integrated@meta.data$GroupType <- ifelse(
  SS.integrated@meta.data$Condition == "Malignant",
  "SS", "Normal"
)

# 4. Plot
png(filename = 'dotplot_more_groups_split.png', width = 9, height = 6, units = 'in', res = 200)
complex_dotplot_single(
  seu_obj = SS.integrated,
  feature = "CLIC1",  # You can replace with any gene of interest
  groups = c("Condition", "orig.ident"),  # Condition first, then sample names
  splitby = c("GroupType", "ReplicateID")  # Splitting based on GroupType and sample replicate IDs
)
Avis : attributes are not identical across measure variables; they will be dropped
dev.off()
png 
  2 

complex_dotplot_single(
  seu_obj = SS.integrated,
  feature = "CLIC1",  # You can replace with any gene of interest
  groups = c("Condition", "orig.ident"),  # Condition first, then sample names
  splitby = c("GroupType", "ReplicateID")  # Splitting based on GroupType and sample replicate IDs
)
Avis : attributes are not identical across measure variables; they will be dropped

Violin plot to show gene expression across groups

# Ensure Group2 is correctly assigned in your metadata
SS.integrated$Group2 <- plyr::mapvalues(SS.integrated$Condition, 
                                         from = c("Control", "Malignant"),  # Original group values
                                         to = c("Ctrl", "Malignant"))       # New mapped values

# Ensure Group2 is a factor with correct levels
SS.integrated$Group2 <- factor(SS.integrated$Group2, levels = c("Ctrl", "Malignant"))

# Check the distribution of Group2 to ensure both groups have cells
print(table(SS.integrated$Group2))  # This should show counts for "Ctrl" and "Malignant"

     Ctrl Malignant 
     8610     40695 
# Check if "Malignant" is present in seurat_clusters or your celltype variable
# If using clusters or other identifiers, modify accordingly
print(table(SS.integrated$seurat_clusters))  # Check the distribution of clusters (or celltype)

   0    1    2    3    4    5    6    7    8    9   10   11   12   13 
6789 5275 4663 4661 4086 3634 3536 3409 3338 3273 3212 1675 1063  691 
# Genes of interest
genes_of_interest <- c("CLIC1", "MYL6B", "TUBA1C", "UBE2C", "NME1", "PLK1", 
                       "BTG1", "IKZF1", "PIK3IP1", "RIPOR2", "TXNIP", "IL7R", 
                       "CDKN1B", "FOXP1", "CD247")

# Check if all genes are present in the dataset
missing_genes <- genes_of_interest[!genes_of_interest %in% rownames(SS.integrated)]
if (length(missing_genes) > 0) {
  print(paste("Missing genes:", paste(missing_genes, collapse = ", ")))
} else {
  print("All genes are present in the dataset.")
}
[1] "All genes are present in the dataset."
# Now create the heatmap, ensuring the correct grouping and celltype
png(filename = 'heatmap_group_sezary.png', width = 4, height = 8, units = 'in', res = 100)

# Make sure to adjust the celltype argument based on valid clusters or celltypes in your dataset
complex_heatmap_unique(
    seu_obj = SS.integrated,              # Seurat object
    celltype = "Malignant",                # Adjust if you want a specific celltype or cluster
    group = "Group2",                      # Grouping variable (Control vs Malignant)
    gene_highlight = genes_of_interest     # Genes you want to highlight in the heatmap
)
The following grouping variables have 1 value and will be ignored: ident
All grouping variables have 1 value only. Computing across all cells.
Avis : No DE genes identifiedAvis : The following tests were not performed: Avis : When testing Malignant versus all:
    Cells in one or both identity groups are not present in the data requestedErreur dans unique_list[[i]] : indice hors limites
LS0tCnRpdGxlOiAicGxvdDFjZWxsIGEgcGFja2FnZSBmb3IgYWR2YW5jZWQgc2luZ2xlIGNlbGwgZGF0YSB2aXN1YWxpemF0aW9uCiIKYXV0aG9yOiAiTmFzaXIgTWFobW9vZCBBYmJhc2kiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19jb2xsYXBzZWQ6IHllcwogIHdvcmRfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCgoKIyAxLiBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1UUlVFfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKIyBodHRwczovL2dpdGh1Yi5jb20vaW1tdW5vZ2Vub21pY3MvcHJlc3RvCmxpYnJhcnkocHJlc3RvKQpsaWJyYXJ5KHRpY3RvYykKCmxpYnJhcnkocGxvdDFjZWxsKQpgYGAKCgojIDIuIExvYWQgU2V1cmF0IE9iamVjdCAKYGBge3J9CgojTG9hZCBTZXVyYXQgT2JqZWN0IG1lcmdlZCBmcm9tIGNlbGwgbGluZXMgYW5kIGEgY29udHJvbChQQk1DKSBhZnRlciBmaWx0cmF0aW9uCmxvYWQoIi4uLy4uL1RvX1RyYW5zZmVyX2JldHdlZW5fY29tcHV0ZXJzLzIzLUhhcm1vbnlfSW50ZWdyYXRpb24vMC1yb2JqLzUtSGFybW9ueV9JbnRlZ3JhdGVkX0FsbF9zYW1wbGVzX01lcmdlZF9DRDRUY2VsbHNfZmluYWxfUmVzb2x1dGlvbl9TZWxlY3RlZF8wLjhfQURUX05vcm1hbGl6ZWRfY2xlYW5lZF9tdC5yb2JqIikKYGBgCgoKIyAzLiBDaXJjbGl6ZSBwbG90IHRvIHZpc3VhbGl6ZSBjZWxsIGNsdXN0ZXJpbmcgYW5kIG1ldGEgZGF0YQpgYGB7cn0KQWxsX3NhbXBsZXNfTWVyZ2VkJENvbmRpdGlvbiA8LSBpZmVsc2UoCiAgQWxsX3NhbXBsZXNfTWVyZ2VkJGNlbGxfbGluZSAlaW4lIGMoIkwxIiwgIkwyIiwgIkwzIiwgIkw0IiwgIkw1IiwgIkw2IiwgIkw3IiksICAjIHlvdXIgbWFsaWduYW50IGxpbmVzCiAgIk1hbGlnbmFudCIsCiAgIkNvbnRyb2wiCikKCmxpYnJhcnkocGxvdDFjZWxsKQoKU1MuaW50ZWdyYXRlZCA8LSBBbGxfc2FtcGxlc19NZXJnZWQKCiMjI0NoZWNrIGFuZCBzZWUgdGhlIG1ldGEgZGF0YSBpbmZvIG9uIHlvdXIgU2V1cmF0IG9iamVjdApjb2xuYW1lcyhTUy5pbnRlZ3JhdGVkQG1ldGEuZGF0YSkgIAoKIyMjUHJlcGFyZSBkYXRhIGZvciBwbG90aW5nCmNpcmNfZGF0YSA8LSBwcmVwYXJlX2NpcmNsaXplX2RhdGEoU1MuaW50ZWdyYXRlZCwgc2NhbGUgPSAwLjggKQpzZXQuc2VlZCgxMjM0KQpjbHVzdGVyX2NvbG9yczwtcmFuZF9jb2xvcihsZW5ndGgobGV2ZWxzKFNTLmludGVncmF0ZWQpKSkKZ3JvdXBfY29sb3JzPC1yYW5kX2NvbG9yKGxlbmd0aChuYW1lcyh0YWJsZShTUy5pbnRlZ3JhdGVkJENvbmRpdGlvbikpKSkKcmVwX2NvbG9yczwtcmFuZF9jb2xvcihsZW5ndGgobmFtZXModGFibGUoU1MuaW50ZWdyYXRlZCRjZWxsX2xpbmUpKSkpCgojIyNwbG90IGFuZCBzYXZlIGZpZ3VyZXMKcG5nKGZpbGVuYW1lID0gICdjaXJjbGl6ZV9wbG90LnBuZycsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNix1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkKcGxvdF9jaXJjbGl6ZShjaXJjX2RhdGEsZG8ubGFiZWwgPSBULCBwdC5zaXplID0gMC4wMSwgY29sLnVzZSA9IGNsdXN0ZXJfY29sb3JzICxiZy5jb2xvciA9ICd3aGl0ZScsIGtkZTJkLm4gPSAyMDAsIHJlcGVsID0gVCwgbGFiZWwuY2V4ID0gMC42KQphZGRfdHJhY2soY2lyY19kYXRhLCBncm91cCA9ICJDb25kaXRpb24iLCBjb2xvcnMgPSBncm91cF9jb2xvcnMsIHRyYWNrX251bSA9IDIpICMjIGNhbiBjaGFuZ2UgaXQgdG8gb25lIG9mIHRoZSBjb2x1bW5zIGluIHRoZSBtZXRhIGRhdGEgb2YgeW91ciBzZXVyYXQgb2JqZWN0CmFkZF90cmFjayhjaXJjX2RhdGEsIGdyb3VwID0gImNlbGxfbGluZSIsY29sb3JzID0gcmVwX2NvbG9ycywgdHJhY2tfbnVtID0gMykgIyMgY2FuIGNoYW5nZSBpdCB0byBvbmUgb2YgdGhlIGNvbHVtbnMgaW4gdGhlIG1ldGEgZGF0YSBvZiB5b3VyIHNldXJhdCBvYmplY3QKZGV2Lm9mZigpCgpwbG90X2NpcmNsaXplKGNpcmNfZGF0YSxkby5sYWJlbCA9IFQsIHB0LnNpemUgPSAwLjAxLCBjb2wudXNlID0gY2x1c3Rlcl9jb2xvcnMgLGJnLmNvbG9yID0gJ3doaXRlJywga2RlMmQubiA9IDIwMCwgcmVwZWwgPSBULCBsYWJlbC5jZXggPSAwLjYpCmFkZF90cmFjayhjaXJjX2RhdGEsIGdyb3VwID0gIkNvbmRpdGlvbiIsIGNvbG9ycyA9IGdyb3VwX2NvbG9ycywgdHJhY2tfbnVtID0gMikgIyMgY2FuIGNoYW5nZSBpdCB0byBvbmUgb2YgdGhlIGNvbHVtbnMgaW4gdGhlIG1ldGEgZGF0YSBvZiB5b3VyIHNldXJhdCBvYmplY3QKYWRkX3RyYWNrKGNpcmNfZGF0YSwgZ3JvdXAgPSAiY2VsbF9saW5lIixjb2xvcnMgPSByZXBfY29sb3JzLCB0cmFja19udW0gPSAzKSAjIyBjYW4gY2hhbmdlIGl0IHRvIG9uZSBvZiB0aGUgY29sdW1ucyBpbiB0aGUgbWV0YSBkYXRhIG9mIHlvdXIgc2V1cmF0IG9iamVjdAoKYGBgCgoKIyMgRG90cGxvdCB0byBzaG93IGdlbmUgZXhwcmVzc2lvbiBhY3Jvc3MgZ3JvdXBzCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KCnBuZyhmaWxlbmFtZSA9ICdkb3RwbG90X0NMSUMxLnBuZycsIHdpZHRoID0gNCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAnaW4nLCByZXMgPSAxMDApCmNvbXBsZXhfZG90cGxvdF9zaW5nbGUoc2V1X29iaiA9IFNTLmludGVncmF0ZWQsIGZlYXR1cmUgPSAiQ0xJQzEiLCBncm91cHMgPSAiQ29uZGl0aW9uIikKZGV2Lm9mZigpCgpjb21wbGV4X2RvdHBsb3Rfc2luZ2xlKHNldV9vYmogPSBTUy5pbnRlZ3JhdGVkLCBmZWF0dXJlID0gIkNMSUMxIiwgZ3JvdXBzID0gIkNvbmRpdGlvbiIpCgoKcG5nKGZpbGVuYW1lID0gICdkb3RwbG90X0lMNC5wbmcnLCB3aWR0aCA9IDQsIGhlaWdodCA9IDYsdW5pdHMgPSAnaW4nLCByZXMgPSAxMDApCmNvbXBsZXhfZG90cGxvdF9zaW5nbGUoc2V1X29iaiA9IFNTLmludGVncmF0ZWQsIGZlYXR1cmUgPSAiSUw0IiwgZ3JvdXBzID0gIkNvbmRpdGlvbiIpCmRldi5vZmYoKQoKY29tcGxleF9kb3RwbG90X3NpbmdsZShzZXVfb2JqID0gU1MuaW50ZWdyYXRlZCwgZmVhdHVyZSA9ICJJTDQiLCBncm91cHMgPSAiQ29uZGl0aW9uIikKCgpwbmcoZmlsZW5hbWUgPSAgJ2RvdHBsb3RfSUw3Ui5wbmcnLCB3aWR0aCA9IDQsIGhlaWdodCA9IDYsdW5pdHMgPSAnaW4nLCByZXMgPSAxMDApCmNvbXBsZXhfZG90cGxvdF9zaW5nbGUoc2V1X29iaiA9IFNTLmludGVncmF0ZWQsIGZlYXR1cmUgPSAiSUw3UiIsIGdyb3VwcyA9ICJDb25kaXRpb24iKQpkZXYub2ZmKCkKCmNvbXBsZXhfZG90cGxvdF9zaW5nbGUoc2V1X29iaiA9IFNTLmludGVncmF0ZWQsIGZlYXR1cmUgPSAiSUw3UiIsIGdyb3VwcyA9ICJDb25kaXRpb24iKQpgYGAKCiMjIERvdHBsb3QgdG8gc2hvdyBnZW5lIGV4cHJlc3Npb24gYWNyb3NzIGdyb3VwcwpgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9ClNTLmludGVncmF0ZWRAbWV0YS5kYXRhJFN0YXR1cyA8LSBwbHlyOjptYXB2YWx1ZXMoCiAgU1MuaW50ZWdyYXRlZEBtZXRhLmRhdGEkQ29uZGl0aW9uLCAKICBmcm9tID0gYygiQ29udHJvbCIsICJNYWxpZ25hbnQiKSwKICB0byA9IGMoIkhlYWx0aHkiLCAiTWFsaWduYW50IikKKQpTUy5pbnRlZ3JhdGVkQG1ldGEuZGF0YSRTdGF0dXMgPC0gYXMuY2hhcmFjdGVyKFNTLmludGVncmF0ZWRAbWV0YS5kYXRhJFN0YXR1cykKCgpwbmcoZmlsZW5hbWUgPSAnZG90cGxvdF9zaW5nbGVfc3BsaXQucG5nJywgd2lkdGggPSA0LCBoZWlnaHQgPSA2LCB1bml0cyA9ICdpbicsIHJlcyA9IDEwMCkKY29tcGxleF9kb3RwbG90X3NpbmdsZShzZXVfb2JqID0gU1MuaW50ZWdyYXRlZCwgZmVhdHVyZSA9ICJDTElDMSIsIGdyb3VwcyA9ICJDb25kaXRpb24iLCBzcGxpdGJ5ID0gIlN0YXR1cyIpCmRldi5vZmYoKQoKCmNvbXBsZXhfZG90cGxvdF9zaW5nbGUoc2V1X29iaiA9IFNTLmludGVncmF0ZWQsIGZlYXR1cmUgPSAiQ0xJQzEiLCBncm91cHMgPSAiQ29uZGl0aW9uIiwgc3BsaXRieSA9ICJTdGF0dXMiKQoKCgpwbmcoZmlsZW5hbWUgPSAnZG90cGxvdF9tb3JlX2dyb3Vwcy5wbmcnLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIHVuaXRzID0gJ2luJywgcmVzID0gMTAwKQpjb21wbGV4X2RvdHBsb3Rfc2luZ2xlKAogIHNldV9vYmogPSBTUy5pbnRlZ3JhdGVkLAogIGZlYXR1cmUgPSAiQ0xJQzEiLAogIGdyb3VwcyA9IGMoIkNvbmRpdGlvbiIsICJvcmlnLmlkZW50IikgICMgeW91IGNhbiByZXBsYWNlICJvcmlnLmlkZW50IiBpZiB5b3UgaGF2ZSBhbm90aGVyIHJlcGxpY2F0ZXMgaW5mbwopCmRldi5vZmYoKQoKY29tcGxleF9kb3RwbG90X3NpbmdsZSgKICBzZXVfb2JqID0gU1MuaW50ZWdyYXRlZCwKICBmZWF0dXJlID0gIkNMSUMxIiwKICBncm91cHMgPSBjKCJDb25kaXRpb24iLCAib3JpZy5pZGVudCIpICAjIHlvdSBjYW4gcmVwbGFjZSAib3JpZy5pZGVudCIgaWYgeW91IGhhdmUgYW5vdGhlciByZXBsaWNhdGVzIGluZm8KKQoKIyAxLiBDcmVhdGUgQ29uZGl0aW9uIChpZiB5b3UgaGF2ZW4ndCBhbHJlYWR5KQojIEFzc3VtaW5nIHlvdSBoYXZlIG9yaWcuaWRlbnQgd2l0aCBMMeKAk0w3IChtYWxpZ25hbnQpIGFuZCBQQk1DMeKAkzIgKGNvbnRyb2wpClNTLmludGVncmF0ZWRAbWV0YS5kYXRhJENvbmRpdGlvbiA8LSBwbHlyOjptYXB2YWx1ZXMoCiAgU1MuaW50ZWdyYXRlZEBtZXRhLmRhdGEkb3JpZy5pZGVudCwKICBmcm9tID0gbGV2ZWxzKGZhY3RvcihTUy5pbnRlZ3JhdGVkQG1ldGEuZGF0YSRvcmlnLmlkZW50KSksICAjIGR5bmFtaWNhbGx5IGdldCBMMeKAk0w3ICsgUEJNQ3MKICB0byA9IGMocmVwKCJNYWxpZ25hbnQiLCA3KSwgcmVwKCJDb250cm9sIiwgMikpCikKU1MuaW50ZWdyYXRlZEBtZXRhLmRhdGEkQ29uZGl0aW9uIDwtIGFzLmNoYXJhY3RlcihTUy5pbnRlZ3JhdGVkQG1ldGEuZGF0YSRDb25kaXRpb24pCgojIDIuIENyZWF0ZSBSZXBsaWNhdGVJRCAoZmFrZSByZXBsaWNhdGUgbmFtZXMganVzdCBmb3IgcGxvdHRpbmcgY2xhcml0eSkKU1MuaW50ZWdyYXRlZEBtZXRhLmRhdGEkUmVwbGljYXRlSUQgPC0gcGx5cjo6bWFwdmFsdWVzKAogIFNTLmludGVncmF0ZWRAbWV0YS5kYXRhJG9yaWcuaWRlbnQsCiAgZnJvbSA9IG5hbWVzKHRhYmxlKFNTLmludGVncmF0ZWRAbWV0YS5kYXRhJG9yaWcuaWRlbnQpKSwKICB0byA9IGMocGFzdGUwKCJNYWxpZ25hbnRfIiwgMTo3KSwgcGFzdGUwKCJDb250cm9sXyIsIDE6MikpCikKU1MuaW50ZWdyYXRlZEBtZXRhLmRhdGEkUmVwbGljYXRlSUQgPC0gYXMuY2hhcmFjdGVyKFNTLmludGVncmF0ZWRAbWV0YS5kYXRhJFJlcGxpY2F0ZUlEKQoKIyAzLiBDcmVhdGUgR3JvdXBUeXBlIChpbnN0ZWFkIG9mIFBoYXNlLCBiZWNhdXNlIHlvdSBhbHJlYWR5IGhhdmUgUGhhc2UgZm9yIGNlbGwgY3ljbGUpClNTLmludGVncmF0ZWRAbWV0YS5kYXRhJEdyb3VwVHlwZSA8LSBpZmVsc2UoCiAgU1MuaW50ZWdyYXRlZEBtZXRhLmRhdGEkQ29uZGl0aW9uID09ICJNYWxpZ25hbnQiLAogICJTUyIsICJOb3JtYWwiCikKCiMgNC4gUGxvdApwbmcoZmlsZW5hbWUgPSAnZG90cGxvdF9tb3JlX2dyb3Vwc19zcGxpdC5wbmcnLCB3aWR0aCA9IDksIGhlaWdodCA9IDYsIHVuaXRzID0gJ2luJywgcmVzID0gMjAwKQpjb21wbGV4X2RvdHBsb3Rfc2luZ2xlKAogIHNldV9vYmogPSBTUy5pbnRlZ3JhdGVkLAogIGZlYXR1cmUgPSAiQ0xJQzEiLCAgIyBZb3UgY2FuIHJlcGxhY2Ugd2l0aCBhbnkgZ2VuZSBvZiBpbnRlcmVzdAogIGdyb3VwcyA9IGMoIkNvbmRpdGlvbiIsICJvcmlnLmlkZW50IiksICAjIENvbmRpdGlvbiBmaXJzdCwgdGhlbiBzYW1wbGUgbmFtZXMKICBzcGxpdGJ5ID0gYygiR3JvdXBUeXBlIiwgIlJlcGxpY2F0ZUlEIikgICMgU3BsaXR0aW5nIGJhc2VkIG9uIEdyb3VwVHlwZSBhbmQgc2FtcGxlIHJlcGxpY2F0ZSBJRHMKKQpkZXYub2ZmKCkKCmNvbXBsZXhfZG90cGxvdF9zaW5nbGUoCiAgc2V1X29iaiA9IFNTLmludGVncmF0ZWQsCiAgZmVhdHVyZSA9ICJDTElDMSIsICAjIFlvdSBjYW4gcmVwbGFjZSB3aXRoIGFueSBnZW5lIG9mIGludGVyZXN0CiAgZ3JvdXBzID0gYygiQ29uZGl0aW9uIiwgIm9yaWcuaWRlbnQiKSwgICMgQ29uZGl0aW9uIGZpcnN0LCB0aGVuIHNhbXBsZSBuYW1lcwogIHNwbGl0YnkgPSBjKCJHcm91cFR5cGUiLCAiUmVwbGljYXRlSUQiKSAgIyBTcGxpdHRpbmcgYmFzZWQgb24gR3JvdXBUeXBlIGFuZCBzYW1wbGUgcmVwbGljYXRlIElEcwopCmBgYAojIyBWaW9saW4gcGxvdCB0byBzaG93IGdlbmUgZXhwcmVzc2lvbiBhY3Jvc3MgZ3JvdXBzCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD02fQojIEVuc3VyZSBHcm91cDIgaXMgY29ycmVjdGx5IGFzc2lnbmVkIGluIHlvdXIgbWV0YWRhdGEKU1MuaW50ZWdyYXRlZCRHcm91cDIgPC0gcGx5cjo6bWFwdmFsdWVzKFNTLmludGVncmF0ZWQkQ29uZGl0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmcm9tID0gYygiQ29udHJvbCIsICJNYWxpZ25hbnQiKSwgICMgT3JpZ2luYWwgZ3JvdXAgdmFsdWVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSBjKCJDdHJsIiwgIk1hbGlnbmFudCIpKSAgICAgICAjIE5ldyBtYXBwZWQgdmFsdWVzCgojIEVuc3VyZSBHcm91cDIgaXMgYSBmYWN0b3Igd2l0aCBjb3JyZWN0IGxldmVscwpTUy5pbnRlZ3JhdGVkJEdyb3VwMiA8LSBmYWN0b3IoU1MuaW50ZWdyYXRlZCRHcm91cDIsIGxldmVscyA9IGMoIkN0cmwiLCAiTWFsaWduYW50IikpCgojIENoZWNrIHRoZSBkaXN0cmlidXRpb24gb2YgR3JvdXAyIHRvIGVuc3VyZSBib3RoIGdyb3VwcyBoYXZlIGNlbGxzCnByaW50KHRhYmxlKFNTLmludGVncmF0ZWQkR3JvdXAyKSkgICMgVGhpcyBzaG91bGQgc2hvdyBjb3VudHMgZm9yICJDdHJsIiBhbmQgIk1hbGlnbmFudCIKCiMgQ2hlY2sgaWYgIk1hbGlnbmFudCIgaXMgcHJlc2VudCBpbiBzZXVyYXRfY2x1c3RlcnMgb3IgeW91ciBjZWxsdHlwZSB2YXJpYWJsZQojIElmIHVzaW5nIGNsdXN0ZXJzIG9yIG90aGVyIGlkZW50aWZpZXJzLCBtb2RpZnkgYWNjb3JkaW5nbHkKcHJpbnQodGFibGUoU1MuaW50ZWdyYXRlZCRzZXVyYXRfY2x1c3RlcnMpKSAgIyBDaGVjayB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsdXN0ZXJzIChvciBjZWxsdHlwZSkKCiMgR2VuZXMgb2YgaW50ZXJlc3QKZ2VuZXNfb2ZfaW50ZXJlc3QgPC0gYygiQ0xJQzEiLCAiTVlMNkIiLCAiVFVCQTFDIiwgIlVCRTJDIiwgIk5NRTEiLCAiUExLMSIsIAogICAgICAgICAgICAgICAgICAgICAgICJCVEcxIiwgIklLWkYxIiwgIlBJSzNJUDEiLCAiUklQT1IyIiwgIlRYTklQIiwgIklMN1IiLCAKICAgICAgICAgICAgICAgICAgICAgICAiQ0RLTjFCIiwgIkZPWFAxIiwgIkNEMjQ3IikKCiMgQ2hlY2sgaWYgYWxsIGdlbmVzIGFyZSBwcmVzZW50IGluIHRoZSBkYXRhc2V0Cm1pc3NpbmdfZ2VuZXMgPC0gZ2VuZXNfb2ZfaW50ZXJlc3RbIWdlbmVzX29mX2ludGVyZXN0ICVpbiUgcm93bmFtZXMoU1MuaW50ZWdyYXRlZCldCmlmIChsZW5ndGgobWlzc2luZ19nZW5lcykgPiAwKSB7CiAgcHJpbnQocGFzdGUoIk1pc3NpbmcgZ2VuZXM6IiwgcGFzdGUobWlzc2luZ19nZW5lcywgY29sbGFwc2UgPSAiLCAiKSkpCn0gZWxzZSB7CiAgcHJpbnQoIkFsbCBnZW5lcyBhcmUgcHJlc2VudCBpbiB0aGUgZGF0YXNldC4iKQp9CgojIE5vdyBjcmVhdGUgdGhlIGhlYXRtYXAsIGVuc3VyaW5nIHRoZSBjb3JyZWN0IGdyb3VwaW5nIGFuZCBjZWxsdHlwZQpwbmcoZmlsZW5hbWUgPSAnaGVhdG1hcF9ncm91cF9zZXphcnkucG5nJywgd2lkdGggPSA0LCBoZWlnaHQgPSA4LCB1bml0cyA9ICdpbicsIHJlcyA9IDEwMCkKCiMgTWFrZSBzdXJlIHRvIGFkanVzdCB0aGUgY2VsbHR5cGUgYXJndW1lbnQgYmFzZWQgb24gdmFsaWQgY2x1c3RlcnMgb3IgY2VsbHR5cGVzIGluIHlvdXIgZGF0YXNldApjb21wbGV4X2hlYXRtYXBfdW5pcXVlKAogICAgc2V1X29iaiA9IFNTLmludGVncmF0ZWQsICAgICAgICAgICAgICAjIFNldXJhdCBvYmplY3QKICAgIGNlbGx0eXBlID0gIk1hbGlnbmFudCIsICAgICAgICAgICAgICAgICMgQWRqdXN0IGlmIHlvdSB3YW50IGEgc3BlY2lmaWMgY2VsbHR5cGUgb3IgY2x1c3RlcgogICAgZ3JvdXAgPSAiR3JvdXAyIiwgICAgICAgICAgICAgICAgICAgICAgIyBHcm91cGluZyB2YXJpYWJsZSAoQ29udHJvbCB2cyBNYWxpZ25hbnQpCiAgICBnZW5lX2hpZ2hsaWdodCA9IGdlbmVzX29mX2ludGVyZXN0ICAgICAjIEdlbmVzIHlvdSB3YW50IHRvIGhpZ2hsaWdodCBpbiB0aGUgaGVhdG1hcAopCgpkZXYub2ZmKCkgICMgU2F2ZSB0aGUgcGxvdCBhcyBhIFBORyBmaWxlCgoKYGBgCgoKCg==