EB3I n1 2025 scRNAseq
-
PROCESSING (I)
-
Normalization & scaling





1 PREAMBLE

1.1 Purpose of this session

This practical session is designed to help you understand the importance of normalization, scaling, and regression in single-cell RNA-seq analysis. You will apply these steps using different methods and visualize the effects on the data.

Input data: Seurat object containing the filtered count matrix from the previous class. It’s called 05_TD3A_S5_Doublets.Filtered_12508.4035.RDS.

Output data: Seurat object after the normalization, scaling and regression. It’s called sobj_TD3A_normalized.rds.

1.2 Learning Objectives

  • Discuss why normalizing counts is necessary to compare cells
  • Be able to understand different normalization approaches
  • Be able to decide when to regress out a given variable, by evaluating the effects from any unwanted sources of variation

1.3 Motivation

Up to this point, we have filtered out empty droplets, ambient RNA contamination, low-quality cells, and doublets.

The data is now a count matrix with cells as rows and genes as columns. These counts reflect the capture, reverse transcription, and sequencing steps of the scRNA-seq experiment, each introducing variability. This means the observed differences in gene expression may be influenced by sampling noise rather than true biological differences, resulting in uneven variance across the dataset.

Normalization addresses this by adjusting raw counts to minimize the effects of variable sampling, making the data more suitable for analysis, which often assumes uniform variance. Various normalization techniques exist, each tailored to support different downstream analyses.

A recent study by Ahlmann-Eltze and Huber (2023) benchmarked 22 normalization methods. It’s essential to select the normalization method based on the specific analysis goals. However, for this practical we will just explore the most well-known way to doing single-cell normalization.

1.4 Setup

We first load the packages that are needed :

library(Seurat)
library(ggplot2)
library(patchwork)
library(scater)
# library(dplyr)
library(reshape2)


2 Start Rstudio



3 Warm-up

  • We now set common parameters as new variables, once and for all for this session :
# setparam


## Set your project name
# WARNING : Do not just copy-paste this ! It's MY project name ! Put YOURS !!
project_name <- "ebaii_sc_teachers"


## Control if the project_name exists on the cluster
cat('PATH CHECK : ', dir.exists(paste0('/shared/projects/', project_name)))
Show output
PATH CHECK :  TRUE
## Seed for the RNG
my_seed <- 1337L


4 Prepare the data structure

4.1 Main directory

# maindir

## Preparing the path
TD_dir <- paste0("/shared/projects/", project_name, "/SC_TD")

## Creating the root directory
# dir.create(path = TD_dir, recursive = TRUE)

## Print the root directory on-screen
print(TD_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD"

4.2 Current session

# sessiondir

## Creating the session (Proc.1) directory
session_dir <- paste0(TD_dir, "/04_Proc.1")
dir.create(path = session_dir, recursive = TRUE)

## Print the session directory on-screen
print(session_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/04_Proc.1"

4.3 Input directory

# indir

## Creating the INPUT data directory
input_dir <- paste0(session_dir, "/DATA")
dir.create(path = input_dir, recursive = TRUE)

## Print the input directory on-screen
print(input_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/04_Proc.1/DATA"

4.4 Output directory

# outdir

## Creating the OUTPUT data directory
output_dir <- paste0(session_dir, "/RESULTS")
dir.create(path = output_dir, recursive = TRUE)

## Print the output directory on-screen
print(output_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/04_Proc.1/RESULTS"

5 Reload the Seurat Object

  • We can reload the object we saved at the former step
# dataload


## This is the path to the current EB3I backup
sessionid <- '2538_eb3i_n1_2025'


## The latest Seurat object saved as RDS (name)
sobj_file <- "05_TD3A_S5_Doublets.Filtered_12508.4035.RDS"

## The latest Seurat object saved as RDS (full path)
sobj_path <- paste0(TD_dir, 
                    "/03_Preproc.3/RESULTS/",
                    sobj_file)

force <- FALSE  ## To force a re-download of a Zenodo-hosted backup
local <- FALSE  ## To force a loading from a local backup

## In case of error/lost data : force a reload from a Zenodo backup repository
if(force) {
  zen_id <- "14035293"
  zen_backup_file <- paste0("https://zenodo.org/records/",
                            zen_id,
                            "/files/",
                            sobj_file)
  ## Recreate the expected path if it does not exist
  dir.create(path = dirname(sobj_path), recursive = TRUE)
  ## Download the file
  download.file(url = zen_backup_file,
                destfile = sobj_path)
}

## In case of error/lost data : force a reload from a local backup repository
if(local) {
  sobj_path <- paste0(
    "/shared/projects/", sessionid, "/atelier_scrnaseq/TD/BACKUP/RDS/",
    sobj_file)
}

## Load the object
sobj <- readRDS(file = sobj_path)


5.1 Exploring the object

sobj
Show output
An object of class Seurat 
12508 features across 4035 samples within 1 assay 
Active assay: RNA (12508 features, 0 variable features)
 1 layer present: counts

What’s in the metadata ?

head(sobj@meta.data)
Show output
                   orig.ident nCount_RNA nFeature_RNA log10_nCount_RNA
AAACCTGAGACGCTTT.1       TD3A       2813         1594         3.449170
AAACCTGAGGCATTGG.1       TD3A       2072         1365         3.316390
AAACCTGGTCAACATC.1       TD3A       2025         1341         3.306425
AAACCTGTCGAGGTAG.1       TD3A       1877         1241         3.273464
AAACCTGTCGATCCCT.1       TD3A       2216         1441         3.345570
AAACGGGCACTTACGA.1       TD3A       2445         1428         3.388279
                   percent_mt percent_rb percent_st fail_qc CC_Seurat_S.Score
AAACCTGAGACGCTTT.1   1.670814  12.193388   2.950587    pass       -0.05094074
AAACCTGAGGCATTGG.1   3.426641   5.260618   2.702703    pass       -0.05359801
AAACCTGGTCAACATC.1   1.975309   8.592593   2.419753    pass       -0.06244186
AAACCTGTCGAGGTAG.1   2.930208   8.044752   3.036761    pass       -0.10828525
AAACCTGTCGATCCCT.1   3.203971   7.626354   2.933213    pass       -0.04873018
AAACGGGCACTTACGA.1   3.312883   8.957055   3.353783    pass       -0.15014780
                   CC_Seurat_G2M.Score CC_Seurat_Phase CC_Seurat_SmG2M.Score
AAACCTGAGACGCTTT.1         -0.02430618              G1           -0.02663455
AAACCTGAGGCATTGG.1         -0.11838610              G1            0.06478809
AAACCTGGTCAACATC.1         -0.11211957              G1            0.04967771
AAACCTGTCGAGGTAG.1          0.04384661             G2M           -0.15213186
AAACCTGTCGATCCCT.1         -0.09242939              G1            0.04369921
AAACGGGCACTTACGA.1         -0.07961611              G1           -0.07053168
                   doublet_scds.hybrid doublet_scDblFinder doublet_union
AAACCTGAGACGCTTT.1               FALSE               FALSE         FALSE
AAACCTGAGGCATTGG.1               FALSE               FALSE         FALSE
AAACCTGGTCAACATC.1               FALSE               FALSE         FALSE
AAACCTGTCGAGGTAG.1               FALSE               FALSE         FALSE
AAACCTGTCGATCCCT.1               FALSE               FALSE         FALSE
AAACGGGCACTTACGA.1               FALSE               FALSE         FALSE
                   doublet_viz
AAACCTGAGACGCTTT.1     singlet
AAACCTGAGGCATTGG.1     singlet
AAACCTGGTCAACATC.1     singlet
AAACCTGTCGAGGTAG.1     singlet
AAACCTGTCGATCCCT.1     singlet
AAACGGGCACTTACGA.1     singlet

What are the assays ?

Seurat::Assays(object = sobj)
Show output
[1] "RNA"

What’s a layer ? How to know how many there are ?

Layers(object = sobj, assay = "RNA")
Show output
[1] "counts"

Display first 10 row and first 10 columns of raw matrice

sobj@assays$RNA@layers$counts[1:10, 1:10]
Show output
10 x 10 sparse Matrix of class "dgCMatrix"
                         
 [1,] . 2 . . . . . 1 . .
 [2,] . . . 1 . 1 1 1 . 1
 [3,] . . . . . . . . . .
 [4,] . . 1 . 1 1 2 . 1 1
 [5,] . . . 1 . . 1 1 . 1
 [6,] . 2 . . . . 1 . . .
 [7,] . . . . . . . . . .
 [8,] . . . . . . . . . .
 [9,] . . . . . . . . . .
[10,] . . . . . . . 2 . 1

If there is another matrix, display its first 10 row and first 10 columns

sobj@assays$RNA@layers$data[1:10, 1:10]
Show output
NULL

5.1.1 Assessing of the level of data sparcity

raw_count <- GetAssayData(object = sobj, assay = "RNA", layer = "counts")

Can you spot the difference between sobj@assays$RNA@layers$data[1:10, 1:10] and raw_count[1:10, 1:10]

raw_count[1:10, 1:10]
Show output
10 x 10 sparse Matrix of class "dgCMatrix"
Show output
                                 
Mrpl15        . 2 . . . . . 1 . .
Lypla1        . . . 1 . 1 1 1 . 1
Gm37988       . . . . . . . . . .
Tcea1         . . 1 . 1 1 2 . 1 1
Atp6v1h       . . . 1 . . 1 1 . 1
Rb1cc1        . 2 . . . . 1 . . .
4732440D04Rik . . . . . . . . . .
Pcmtd1        . . . . . . . . . .
Gm26901       . . . . . . . . . .
Rrs1          . . . . . . . 2 . 1
prop.table(
  table(as.matrix(raw_count)>0)
  )*100
Show output

   FALSE     TRUE 
86.90676 13.09324 
mean(as.matrix(raw_count)>0)*100
Show output
[1] 13.09324

Extracting genes attributes

gene_attr <- data.frame("mean" = rowMeans(raw_count), 
                        "detection_rate" = rowMeans(raw_count > 0), 
                        "var" = apply(raw_count, 1, var))
gene_attr$log_mean <- log10(gene_attr$mean)
gene_attr$log_var <- log10(gene_attr$var)

Extracting cell attributes

cell_attr <- data.frame(n_umi = colSums(raw_count), 
                        n_gene = colSums(raw_count >0))

Mean-Variance relationship

ggplot(gene_attr, aes(x = mean, y = var))+
  geom_point()
Show plot

Can we do better ??

ggplot(gene_attr, aes(x = log_mean, y = log_var)) + 
  geom_point(alpha = 0.3, shape = 16) + 
  geom_abline(intercept = 0, slope = 1, color = "red")+
  geom_vline(xintercept = log10(mean(gene_attr$mean)), linetype = "dashed")
Show plot

Other types of relevant information to look at

summary(sobj$nCount_RNA)
Show output
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1000    2034    2399    3498    3000   48875 
summary(sobj$nFeature_RNA)
Show output
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    752    1316    1476    1639    1692    5975 
summary(sobj$percent_mt*100)
Show output
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  18.01  196.08  244.01  251.77  299.33  500.00 

Relationship between nUMI & number of detected genes

FeatureScatter(
  object = sobj, 
  feature1 = "nCount_RNA", 
  feature2 = "nFeature_RNA",
  cols = "grey10"
)
Show plot

ggplot(sobj@meta.data,
       aes(x = nCount_RNA, y = nFeature_RNA))+
  geom_point()
Show plot

FeatureScatter(
  object = sobj, 
  feature1 = "nCount_RNA", 
  feature2 = "percent_mt",
  cols = "grey10"
)
Show plot

5.2 Normalize data: Shifted logarithm method

sobj <- NormalizeData(object = sobj,
                      normalization.method = "LogNormalize", 
                      scale.factor = 1e4)

Visualize the effect of the normalization

logNorm_count <- GetAssayData(object = sobj, assay = "RNA", layer = "data")
SumCount_df <- data.frame(
  "raw" = colSums(raw_count),
  "logNorm" = colSums(logNorm_count)
)
(ggplot(SumCount_df, aes(raw))+
   geom_density()+
   ggtitle("Raw (i.e not normalized) data")+
   theme(plot.title = element_text(face = "bold", hjust = .5, color = "steelblue")))+ (ggplot(SumCount_df, aes(logNorm))+
                                                                                         geom_density()+
                                                                                         ggtitle("Lognorm data")+
                                                                                         theme(plot.title = element_text(face = "bold", hjust = .5, color = "steelblue"))
   )
Show plot

Effect of the normalization on Expression of genes

raw_exp <- VlnPlot(
  object = sobj, 
  features = c("Srm", "Apoe", "Top2a"),
  assay = "RNA", 
  layer = "counts"
)
norm_exp <- VlnPlot(
  object = sobj, 
  features = c("Srm", "Apoe", "Top2a"),
  assay = "RNA", 
  layer = "data"
)

wrap_plots(raw_exp,norm_exp) + plot_layout(nrow = 2)
Show plot

logNorm_exp_3genes <- FetchData(object = sobj, vars = c("Srm", "Apoe", "Top2a"), layer = "data")
logNorm_exp_3genes_l <- reshape2::melt(logNorm_exp_3genes)
raw_exp_3genes <- FetchData(object = sobj, vars = c("Srm", "Apoe", "Top2a"), layer = "counts")
raw_exp_3genes_l <- reshape2::melt(raw_exp_3genes)
(ggplot(data = raw_exp_3genes_l, aes(x = variable, y = value))+
  geom_jitter(height = 0, width = .2, size = .1)+
  geom_boxplot(
    data = raw_exp_3genes_l[raw_exp_3genes_l$value>0,],
    outliers = F, 
    size = .5,
    width = .1)+
   ggtitle("Raw Expression")+
   theme(plot.title = element_text(size = 16, color = "grey50", face = "bold", hjust = .5))) + (ggplot(data = logNorm_exp_3genes_l, aes(x = variable, y = value))+
  geom_jitter(height = 0, width = .2, size = .1)+
  geom_boxplot(
    data = logNorm_exp_3genes_l[logNorm_exp_3genes_l$value>0,],
    outliers = F, 
    size = .5,
    width = .1)+
   ggtitle("logNorm Expression")+
   theme(plot.title = element_text(size = 16, color = "grey50", face = "bold", hjust = .5)))
Show plot

5.3 Normalize data II: What’s the name already ???

sobj <- SCTransform(object = sobj)

How this normalization affect the expression of the 3 genes ??

sct_exp <- VlnPlot(
  object = sobj, 
  features = c("Srm", "Apoe", "Top2a"),
  assay = "SCT", 
  layer = "data"
)
wrap_plots(raw_exp,norm_exp,sct_exp) + plot_layout(nrow = 3)
Show plot

SCT_exp_3genes <- FetchData(object = sobj, vars = c("Srm", "Apoe", "Top2a"), assay = "SCT", layer = "data")
SCT_exp_3genes_l <- reshape2::melt(SCT_exp_3genes)
(ggplot(data = raw_exp_3genes_l, aes(x = variable, y = value))+
  geom_jitter(height = 0, width = .2, size = .1)+
  geom_boxplot(
    data = raw_exp_3genes_l[raw_exp_3genes_l$value>0,],
    outliers = F, 
    size = .5,
    width = .1)+
   ggtitle("Raw Expression")+
   theme(plot.title = element_text(size = 16, color = "grey50", face = "bold", hjust = .5))) + (ggplot(data = logNorm_exp_3genes_l, aes(x = variable, y = value))+
  geom_jitter(height = 0, width = .2, size = .1)+
  geom_boxplot(
    data = logNorm_exp_3genes_l[logNorm_exp_3genes_l$value>0,],
    outliers = F, 
    size = .5,
    width = .1)+
   ggtitle("logNorm Expression")+
   theme(plot.title = element_text(size = 16, color = "grey50", face = "bold", hjust = .5))) + (ggplot(data = SCT_exp_3genes_l, aes(x = variable, y = value))+
  geom_jitter(height = 0, width = .2, size = .1)+
  geom_boxplot(
    data = SCT_exp_3genes_l[SCT_exp_3genes_l$value>0,],
    outliers = F, 
    size = .5,
    width = .1)+
   ggtitle("SCT Expression")+
   theme(plot.title = element_text(size = 16, color = "grey50", face = "bold", hjust = .5)))
Show plot

Compare the Layers available in the different “Assays” available

Layers(sobj, assay = "SCT")
Show output
[1] "counts"     "data"       "scale.data"
Layers(sobj, assay = "RNA")
Show output
[1] "counts" "data"  

What’s missing ?

sobj <- ScaleData(object = sobj, assay = "RNA")
Layers(sobj, assay = "RNA")
Show output
[1] "counts"     "data"       "scale.data"
Reductions(object = sobj)
Show output
NULL
sobj <- FindVariableFeatures(object = sobj, assay = "RNA")

Compare the variance-stabilization for the 2 methods

rna_hvg <- VariableFeaturePlot(
  object = sobj,
  assay = "RNA"
)
sct_hvg <- VariableFeaturePlot(
  object = sobj,
  assay = "SCT"
)
wrap_plots(rna_hvg,sct_hvg)+plot_layout(ncol = 2)
Show plot

rna_hvg+ylim(c(0,10))
Show plot

sct_hvg+ylim(c(0,10))
Show plot

Comparing the 2 normalization methods at the PCA level

sobj <- RunPCA(object = sobj, assay = "RNA", reduction.name = "pcaRNA", reduction.key = "PCrna_")
sobj <- RunPCA(object = sobj, assay = "SCT", reduction.name = "pcaSCT", reduction.key = "PCsct_")
print(sobj[["pcaRNA"]], dims = 1:5, nfeatures = 30)
Show output
PCrna_ 1 
Positive:  Cd52, Gm42418, Ltb, Gm26740, Trbv31, Trbv19, Slfn5, Trbv1, 5830405F06Rik, Slc5a9 
       Trbv4, Trbv16, Adam12, Trbv13-2, Ckb, S100a11, Trav1, Pcsk1, Trbv20, Trbv17 
       Trbv3, Trbv2, Bcl2l11, Trav6-7-dv9, Trbc1, Gm525, Trav13d-3, Trav13n-1, Trbv29, Trbv26 
Negative:  Pclaf, Birc5, Spc24, Ccna2, Rrm2, Pbk, Top2a, Mki67, Cdk1, Rad51 
       Fbxo5, Tk1, Aurkb, Esco2, Clspn, Cdca3, Rad51ap1, Kif15, E2f8, Tpx2 
       Ccnb2, Cdca8, Nusap1, Cenpm, Kif11, Hist1h2ag, Shcbp1, Ncapg, Spc25, Neil3 
PCrna_ 2 
Positive:  Nusap1, Spc25, Ube2c, Aurkb, Kif11, Ccna2, Xrcc6, Mxd3, Esco2, Pbk 
       Neil3, Plk1, Hist1h2ap, Cenpf, Knl1, Mki67, Tpx2, Ckap2l, Cdca3, Cdk1 
       Ccnf, Hmmr, Cenpe, Top2a, Birc5, Prc1, Hist1h2af, Shcbp1, Hist1h2ab, Hist1h2ao 
Negative:  Nkg7, Ms4a4b, Ccnd2, Ctsw, Gimap4, Gimap3, Il2rb, Itgae, Plac8, H2-K1 
       S1pr1, Gm2682, Cd7, Cst7, H2-Q7, Klf2, Ifi27l2a, Klk8, Tmsb10, Cxcr6 
       Cnn2, Cd53, Il7r, Slfn1, Samhd1, Lfng, Evl, Shisa5, Ccr7, Bcl2 
PCrna_ 3 
Positive:  Pdcd1, Srm, Shmt1, Nr4a1, Itm2a, Bcat1, Cdc6, Hivep3, Apex1, Mettl1 
       Ung, Ikzf2, Rrp15, Nab2, Kcnq5, Ptma, Nolc1, Hsp90ab1, Cd5, Dctpp1 
       Tigit, Pus7, Psat1, Mthfd1, Rrs1, Nme1, Gapdh, Chek1, Gnl3, Trib1 
Negative:  Nkg7, H2-Q7, Itgae, S1pr1, Gm2682, Ctsw, Ctss, AW112010, Samhd1, Ms4a4b 
       Klf2, Cd7, Lfng, Cxcr6, Ifit3, Ly6a, Ccnd2, Slfn1, Cenpf, Gimap3 
       Rflnb, H2-K1, Ube2c, Nusap1, Il2rb, Pif1, Ifi213, Ccr7, Gm8369, Hmmr 
PCrna_ 4 
Positive:  Xrcc6, Cdc6, Ly6d, Ptcra, Rps14, Rpl18a, Apoe, C1qb, Hells, Anxa2 
       Chek1, C1qa, C1qc, Fcer1g, Gins2, Gzma, Pdlim4, Rpl7, Tyrobp, Ccne2 
       Cd74, Tcrg-V4, Phlda1, Rpl11, Rpl13, Notch1, Dhfr, Gpr25, Lyz2, Rps3a1 
Negative:  Nab2, Itm2a, Cd5, Tmsb10, Tox, Cd69, Pdcd1, Cd2, Egr1, Egr2 
       Bcl2, Nr4a1, Ephx1, Ccr4, Cd53, Cytip, B630019A10Rik, Hivep3, Ikzf2, Izumo1r 
       Clec2d, Tigit, Cnn2, Gm9844, Lcp1, Gpr183, Tes, Gm43352, Rgs10, Orai1 
PCrna_ 5 
Positive:  C1qc, Apoe, C1qb, C1qa, Fcer1g, Cd74, Tyrobp, Lyz2, H2-Aa, H2-Ab1 
       H2-Eb1, Ctss, Vcam1, Cd63, Grn, Lgmn, Aif1, Lgals3, Igf1, Csf1r 
       Unc93b1, Pltp, Ccl8, Ctsh, H2-DMb1, Fcgr3, Lyn, Ly86, Cfp, Ear2 
Negative:  Nkg7, Itgae, Ctsw, Gm2682, S1pr1, Klf2, Ms4a4b, Lfng, Plac8, Cxcr6 
       Rflnb, Il2rb, Ccnd2, Cd7, Adgrg5, Gm8369, Klk8, Rpl18, Rpl7, Gimap4 
       Ccr7, Rpl18a, Ms4a4c, Actn2, Itgb7, Adam19, Slfn1, Ly6c2, Jaml, Smad7 
print(sobj[["pcaSCT"]], dims = 1:5, nfeatures = 30)
Show output
PCsct_ 1 
Positive:  Malat1, Cd8b1, Rag1, Arl5c, Cd8a, Cd4, Ccr9, Rmnd5a, Ets2, Arpp21 
       Aqp11, Ldhb, Slc16a5, Btg2, Gm15340, Rsrp1, Gtf2h4, Btg1, Thy1, Rag2 
       Ssbp2, Themis, Dntt, Lipa, Satb1, Ypel3, Mier1, B230118H07Rik, Bnip3l, Txnip 
Negative:  Tuba1b, Hmgb2, Pclaf, Rps2, Hmgn2, Rpsa, Rrm2, Stmn1, Rpl3, H2afz 
       Ptma, Eef1a1, Hist1h2ap, Top2a, Ppia, Birc5, Hist1h2ao, Nme2, Crip1, Tmsb10 
       Rps20, Npm1, Hist1h2ae, Rps18, Gm10076, Rpl39, Rpl32, Rpl13, Rps5, Rps8 
PCsct_ 2 
Positive:  Tmsb10, Ifi27l2a, Ms4a4b, H2-K1, Shisa5, Cnn2, Itm2a, Nkg7, Cd5, Ctsw 
       Tmsb4x, Ms4a6b, Cd2, Cd52, Ccnd2, Gimap4, Vim, Cd53, Klf2, Lcp1 
       Gimap3, Cytip, Bcl2, Eef1a1, Rpsa, AW112010, Isg15, Rps20, Ccr7, H2-D1 
Negative:  Hist1h2ap, Hmgb2, Pclaf, Rrm2, Hist1h2ao, Hist1h2ae, H2afz, Top2a, Stmn1, Tuba1b 
       Hmgn2, Birc5, Ly6d, Hist1h1b, Ccna2, Mki67, Ptma, Nusap1, Dut, Ube2c 
       Tyms, Fbxo5, H2afx, Hmgb1, Spc24, Cdca8, Cdk1, Hist1h2an, Cdca3, Pbk 
PCsct_ 3 
Positive:  Cd5, Tmsb10, Itm2a, Cd2, Egr1, Tox, Id3, Cytip, Nab2, Rgs10 
       Cd69, Cd28, Ikzf2, Nr4a1, Actb, Pfn1, Ccr4, Actg1, Egr2, Hivep3 
       Lef1, Pdcd1, Gm43352, Stk17b, Ptma, Cd6, Arap2, Tubb5, Rap1a, Dusp2 
Negative:  H2-K1, Ms4a4b, Nkg7, Rps24, AW112010, Ly6d, Ctsw, Rps15a, Rpl32, Klf2 
       Rpl39, Rpl13, Rps20, Rpl18a, Rps5, Rps18, Ifi27l2a, Plac8, Ccnd2, Rplp0 
       Rps7, B2m, Uba52, Gm525, Rpl23, Rps26, Cd8b1, Rpl37, Fau, Rps8 
PCsct_ 4 
Positive:  Ifi27l2a, Ms4a4b, Nusap1, Isg15, Ube2c, Tmsb10, H2-K1, Hist1h2ae, Top2a, Hist1h2ao 
       Ccna2, H2afx, Cenpf, Birc5, Cd52, Mki67, Rtp4, Kif11, Cdca3, Tgtp2 
       Cdca8, Hist1h1b, Shisa5, Ctsw, Tpx2, Irf7, Cdk1, Cenpe, Ifit1, Hist1h2ab 
Negative:  Ly6d, Npm1, Rps2, Srm, Fabp5, Mif, C1qbp, Eif5a, Mcm6, Ikzf2 
       Mcm3, Gm525, Ung, Myc, Hsp90ab1, Hspd1, Gzma, Hells, Nme2, Phlda1 
       Trgv2, Pdcd1, Pa2g4, Nolc1, Ptcra, Mcm5, Nr4a1, Dctpp1, Cdk4, Dut 
PCsct_ 5 
Positive:  Ly6d, Trbv13-2, mt-Co1, Ccnd3, Anxa2, Trbv13-3, Ifngr1, Phlda1, Lgals1, Trbv12-2 
       Actb, Cd5, Uba52, Trbv29, Trbv13-1, Rgs10, Ppp1r14b, Rpl28, Rpl18a, Gm42418 
       Fau, Rps24, Actg1, Cenpf, Ube2c, Endou, Dusp10, Rps27, Cnn3, Prep 
Negative:  Trbv31, Hspa8, Hsp90ab1, Hsp90aa1, Tuba1b, Cd52, Mcm6, Npm1, Arl5c, Mif 
       Mcm3, Srm, Nme2, Trbv3, Hells, Trbv1, Hspd1, Rag1, Mcm5, Rps2 
       Ung, Pcna, Smoc1, Hspe1, Gtf2h4, Dut, Gapdh, Mcm2, Pa2g4, Slc6a19 
DimPlot(object = sobj, reduction = "pcaRNA")+DimPlot(object = sobj, reduction = "pcaSCT")
Show plot

FeaturePlot(object = sobj, reduction = "pcaRNA", features = "nCount_RNA")
Show plot

FeaturePlot(object = sobj, reduction = "pcaSCT", features = "nCount_RNA")
Show plot

FeatureScatter(object = sobj, 
               feature1 = "PCrna_1", 
               feature2 = "nCount_RNA") + FeatureScatter(object = sobj, 
                                                         feature1 = "PCsct_1", 
                                                         feature2 = "nCount_RNA")
Show plot

FeatureScatter(object = sobj, 
               feature1 = "PCrna_1", 
               feature2 = "CC_Seurat_S.Score") + FeatureScatter(object = sobj, 
                                                                feature1 = "PCsct_1",
                                                                feature2 = "CC_Seurat_S.Score")
Show plot

DimPlot(object = sobj, 
        reduction = "pcaRNA",
        group.by = "CC_Seurat_Phase")+DimPlot(object = sobj, 
                                              reduction = "pcaSCT",
                                              group.by = "CC_Seurat_Phase")
Show plot

5.4 Regressing out unwanted source of variation

sobj_ccg <- ScaleData(object = sobj, 
                      vars.to.regress = c("CC_Seurat_S.Score", "CC_Seurat_G2M.Score"), 
                      assay = "RNA")
sobj_ccg <- RunPCA(object = sobj_ccg, assay = "RNA", reduction.name = "pcaRNAccg", reduction.key = "PCrnaccg_")
DimPlot(object = sobj, 
        reduction = "pcaRNA",
        group.by = "CC_Seurat_Phase")+DimPlot(object = sobj, 
                                              reduction = "pcaSCT",
                                              group.by = "CC_Seurat_Phase")+DimPlot(object = sobj_ccg, 
                                              reduction = "pcaRNAccg",
                                              group.by = "CC_Seurat_Phase")
Show plot

print(sobj_ccg[["pcaRNAccg"]], dims = 1:5, nfeatures = 30)
Show output
PCrnaccg_ 1 
Positive:  Ccnd2, Plac8, Nkg7, Ms4a4b, Ctsw, Gimap4, Rpsa, Nme2, Il2rb, Gimap3 
       H2-K1, Rps2, Itgae, Gm10076, Srm, Klf2, Rplp0, S1pr1, Cnn2, Rps15a 
       Rpl39, Rps8, Rps26, Ifi27l2a, Rps4x, Rpl3, Rpl13, Rps5, Cst7, Gm2682 
Negative:  Xrcc6, Cdca7, Ptcra, Gm525, H2afv, Arsi, Gm42418, Hist1h2ap, Ly6d, Slc5a9 
       Il2ra, Marcks, Trbv19, Trbv31, Phlda1, Ctcf, 5830405F06Rik, Cd24a, Trbc1, Lmnb1 
       Notch1, Trbv1, Nrp1, Trbv13-2, Pcsk1, Hist1h1c, Arhgef10l, Them6, Adam12, Trav1 
PCrnaccg_ 2 
Positive:  Nkg7, Itgae, Gm2682, Ctsw, S1pr1, H2-Q7, Ms4a4b, AW112010, Cxcr6, Samhd1 
       Cd7, Klf2, Lfng, Ccnd2, Slfn1, Rflnb, Il2rb, Ly6a, Ifit3, Ctss 
       Ccr7, Gimap3, H2-K1, Ly6c2, Gm8369, Plac8, Klk8, Cst7, Ifi213, Adgrg5 
Negative:  Pdcd1, Itm2a, Hivep3, Nr4a1, Ptma, Ikzf2, Cd5, Nab2, Bcat1, Tigit 
       Psat1, Tox, Srm, Kcnq5, Trib1, Apex1, Egr2, Gpr183, Ccr4, Cd24a 
       Fabp5, Gm43352, Gcsh, Dctpp1, Shmt1, Pclaf, Egr1, Eif4a1, Nfkbid, Gapdh 
PCrnaccg_ 3 
Positive:  Ccna2, Pbk, Nusap1, Esco2, Aurkb, Top2a, Pclaf, Mxd3, Cdk1, Spc25 
       Neil3, Kif11, Mki67, Hist1h2af, Rrm2, Birc5, Spc24, Ube2c, Hist1h3b, Fbxo5 
       E2f8, Plk1, Ccnf, Cdca3, Hist1h2ab, Hist1h2ag, Shcbp1, Ckap2l, Ncapg, Hist1h2ae 
Negative:  Tmsb10, Tubb4b, Nab2, Itm2a, Bcl2, Cd5, Cd53, Cd69, Egr1, Pdcd1 
       Nr4a1, Tox, Cd2, Ccr4, Lcp1, Ephx1, Cytip, Gm9844, Egr2, B630019A10Rik 
       Izumo1r, Hsp90ab1, Shisa5, Gapdh, Tes, Cd52, Orai1, Cnn2, Clec2d, Nfkbid 
PCrnaccg_ 4 
Positive:  Ccnb2, Anxa2, Ly6d, Xrcc6, Gzma, Rpl18a, Tcrg-V4, Pdlim4, Rps14, Gpr25 
       Cdc6, Apoe, Rpl7, Rpl13, Rpl11, C1qa, Fabp5, Trgv2, Rpl32, C1qb 
       Rpl28, C1qc, Rps3a1, Necab1, Fcer1g, Phlda1, Tcrg-C2, Tyrobp, Rpl34, Cd74 
Negative:  Esco2, Nusap1, Tmsb10, Mxd3, Aurkb, Spc25, Nab2, Neil3, Cd5, Kif11 
       Bcl2, Hist1h2af, Hist1h3b, Top2a, Itm2a, Ccnf, Tox, Pbk, Cd69, Ccna2 
       Egr1, Egr2, Cd53, Ephx1, Cd2, Cdk1, Ccr4, Cd52, B630019A10Rik, Foxm1 
PCrnaccg_ 5 
Positive:  Apoe, C1qc, C1qb, C1qa, Fcer1g, Cd74, Tyrobp, Lyz2, H2-Aa, H2-Ab1 
       Ctss, H2-Eb1, Vcam1, Cd63, Lgmn, Aif1, Grn, Lgals3, Igf1, Csf1r 
       Unc93b1, Pltp, Ccl8, Ctsh, H2-DMb1, Fcgr3, Cfp, Ly86, Lyn, Ear2 
Negative:  Rpl7, Rpl18a, Anxa2, Rplp0, Rpl18, Rps15a, Rpl28, Rpl11, Rps7, Phlda1 
       Rps14, Rps3a1, Xrcc6, Rpl17, Rpl13, Ly6d, Rpl23, Rpl32, Rps16, Rps26 
       Rpl37a, Rpl9-ps6, Eif3h, Rpl13a, Rps5, Rps13, Ptcra, Rpl8, Rps3, Rpl35a 

6 R session

This is a good practice to show the version of the packages used in this notebook.

# rsession

sessionInfo()
Show output
R version 4.4.1 (2024-06-14)
Platform: x86_64-conda-linux-gnu
Running under: Ubuntu 22.04.5 LTS

Matrix products: default
BLAS/LAPACK: /shared/ifbstor1/software/miniconda/envs/r-4.4.1/lib/libopenblasp-r0.3.29.so;  LAPACK version 3.12.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

time zone: Europe/Paris
tzcode source: system (glibc)

attached base packages:
[1] stats4    stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
 [1] reshape2_1.4.4              scater_1.32.1              
 [3] scuttle_1.14.0              SingleCellExperiment_1.26.0
 [5] SummarizedExperiment_1.34.0 Biobase_2.64.0             
 [7] GenomicRanges_1.56.2        GenomeInfoDb_1.40.1        
 [9] IRanges_2.38.1              S4Vectors_0.42.1           
[11] BiocGenerics_0.50.0         MatrixGenerics_1.16.0      
[13] matrixStats_1.5.0           patchwork_1.3.0            
[15] ggplot2_3.5.2               Seurat_5.3.0               
[17] SeuratObject_5.1.0          sp_2.2-0                   

loaded via a namespace (and not attached):
  [1] RColorBrewer_1.1-3        rstudioapi_0.17.1        
  [3] jsonlite_2.0.0            magrittr_2.0.3           
  [5] ggbeeswarm_0.7.2          spatstat.utils_3.1-4     
  [7] farver_2.1.2              rmarkdown_2.29           
  [9] zlibbioc_1.50.0           vctrs_0.6.5              
 [11] ROCR_1.0-11               DelayedMatrixStats_1.26.0
 [13] spatstat.explore_3.4-3    S4Arrays_1.4.1           
 [15] htmltools_0.5.8.1         BiocNeighbors_1.22.0     
 [17] SparseArray_1.4.8         sass_0.4.10              
 [19] sctransform_0.4.2         parallelly_1.45.0        
 [21] KernSmooth_2.23-24        bslib_0.9.0              
 [23] htmlwidgets_1.6.4         ica_1.0-3                
 [25] plyr_1.8.9                plotly_4.10.4            
 [27] zoo_1.8-14                cachem_1.1.0             
 [29] igraph_2.1.4              mime_0.13                
 [31] lifecycle_1.0.4           pkgconfig_2.0.3          
 [33] rsvd_1.0.5                Matrix_1.7-3             
 [35] R6_2.6.1                  fastmap_1.2.0            
 [37] GenomeInfoDbData_1.2.12   fitdistrplus_1.2-2       
 [39] future_1.49.0             shiny_1.10.0             
 [41] digest_0.6.37             tensor_1.5               
 [43] RSpectra_0.16-2           irlba_2.3.5.1            
 [45] beachmat_2.20.0           labeling_0.4.3           
 [47] progressr_0.15.1          spatstat.sparse_3.1-0    
 [49] httr_1.4.7                polyclip_1.10-7          
 [51] abind_1.4-8               compiler_4.4.1           
 [53] withr_3.0.2               BiocParallel_1.38.0      
 [55] viridis_0.6.5             fastDummies_1.7.5        
 [57] MASS_7.3-65               DelayedArray_0.30.1      
 [59] tools_4.4.1               vipor_0.4.7              
 [61] lmtest_0.9-40             beeswarm_0.4.0           
 [63] httpuv_1.6.15             future.apply_1.11.3      
 [65] goftest_1.2-3             glmGamPoi_1.16.0         
 [67] glue_1.8.0                nlme_3.1-165             
 [69] promises_1.3.2            grid_4.4.1               
 [71] Rtsne_0.17                cluster_2.1.6            
 [73] generics_0.1.4            gtable_0.3.6             
 [75] spatstat.data_3.1-6       rmdformats_1.0.4         
 [77] tidyr_1.3.1               data.table_1.17.4        
 [79] ScaledMatrix_1.12.0       BiocSingular_1.20.0      
 [81] XVector_0.44.0            spatstat.geom_3.4-1      
 [83] RcppAnnoy_0.0.22          ggrepel_0.9.6            
 [85] RANN_2.6.2                pillar_1.10.2            
 [87] stringr_1.5.1             spam_2.11-1              
 [89] RcppHNSW_0.6.0            later_1.4.2              
 [91] splines_4.4.1             dplyr_1.1.4              
 [93] lattice_0.22-6            survival_3.7-0           
 [95] deldir_2.0-4              tidyselect_1.2.1         
 [97] miniUI_0.1.2              pbapply_1.7-2            
 [99] knitr_1.50                gridExtra_2.3            
[101] bookdown_0.39             scattermore_1.2          
[103] xfun_0.52                 stringi_1.8.7            
[105] UCSC.utils_1.0.0          lazyeval_0.2.2           
[107] yaml_2.3.10               evaluate_1.0.3           
[109] codetools_0.2-20          tibble_3.2.1             
[111] cli_3.6.5                 uwot_0.2.3               
[113] xtable_1.8-4              reticulate_1.42.0        
[115] jquerylib_0.1.4           dichromat_2.0-0.1        
[117] Rcpp_1.0.14               globals_0.18.0           
[119] spatstat.random_3.4-1     png_0.1-8                
[121] ggrastr_1.0.2             spatstat.univar_3.1-3    
[123] parallel_4.4.1            dotCall64_1.2            
[125] sparseMatrixStats_1.16.0  listenv_0.9.1            
[127] viridisLite_0.4.2         scales_1.4.0             
[129] ggridges_0.5.6            crayon_1.5.3             
[131] purrr_1.0.4               rlang_1.1.6              
[133] cowplot_1.1.3            
LS0tCnRpdGxlOiAiPENFTlRFUj5FQjNJIG4xIDIwMjUgc2NSTkFzZXE8QlI+LTxCUj4gPEI+UFJPQ0VTU0lORyAoSSk8L0I+PEJSPi08QlI+Tm9ybWFsaXphdGlvbiAmIHNjYWxpbmc8L0NFTlRFUj4iCmRhdGU6ICIyMDI1LTE2LTIxLjIyIgphdXRob3I6CiAgLSBuYW1lOiAiRUIzSSBuMSBzY1JOQXNlcSBUZWFtIgogIC0gbmFtZTogIkxpbGlhIFlPVU5TSSIKICAgIGVtYWlsOiAibGlsaWEueW91bnNpQGluc2VybS5mciIKICAtIG5hbWU6ICJTZWJhc3RpZW4gTUVMTEEiCiAgICBlbWFpbDogInNlYmFzdGllbi5tZWxsYUBwYXN0ZXVyLmZyIgpvdXRwdXQ6CiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246CiAgICBmaWdfd2lkdGg6IDgKICAgIGZpZ19oZWlnaHQ6IDYKICAgIGhpZ2hsaWdodDogdGFuZ28gICMjIFRoZW1lIGZvciB0aGUgY29kZSBjaHVua3MKICAgIGVtYmVkX2ZvbnRzOiBUUlVFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUgICMjIEFkZHMgbnVtYmVyIHRvIGhlYWRlcnMgKHNlY3Rpb25zKQogICAgdGhlbWU6IGZsYXRseSAgIyMgQ1NTIHRoZW1lIGZvciB0aGUgSFRNTCBwYWdlCiAgICBjb2xsYXBzZWQ6IHRydWUgICMjIEJ5IGRlZmF1bHQsIHRoZSBUT0MgaXMgZm9sZGVkCiAgICB0b2NfZGVwdGg6IDMKICAgIHNtb290aF9zY3JvbGw6IHRydWUgIyMgU21vb3RoIHNjcm9sbCBvZiB0aGUgSFRNTCBwYWdlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZSAjIyBJbmNsdWRlcyBhbGwgcGxvdHMvaW1hZ2VzIHdpdGhpbiB0aGUgSFRNTAogICAgY29kZV9kb3dubG9hZDogdHJ1ZSAjIyBBZGRzIGEgYnV0dG9uIHRvIGRvd25sb2FkIHRoZSBSbWQKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdGh1bWJuYWlsczogZmFsc2UKICAgIGxpZ2h0Ym94OiB0cnVlCiAgICBmaWdfY2FwdGlvbjogZmFsc2UKICAgIGdhbGxlcnk6IHRydWUKICAgIHVzZV9ib29rZG93bjogdHJ1ZQphbHdheXNfYWxsb3dfaHRtbDogdHJ1ZSAjIyBBbGxvdyBwbGFpbiBIVE1MIGNvZGUgaW4gdGhlIFJtZAplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgo8IS0tIGtuaXQgc2V0dXAgLS0+CgpgYGB7ciBrbml0X3NldHVwLCBlY2hvID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwgICAgICAgICMgUHJpbnQgdGhlIGNvZGUKICBldmFsID0gVFJVRSwgICAgICAgICMgUnVuIGNvbW1hbmQgbGluZXMKICBtZXNzYWdlID0gRkFMU0UsICAgICMgUHJpbnQgbWVzc2FnZXMKICBwcm9tcHQgPSBGQUxTRSwgICAgICMgRG8gbm90IGRpc3BsYXkgcHJvbXB0CiAgY29tbWVudCA9IE5BLCAgICAgICAjIE5vIGNvbW1lbnRzIG9uIHRoaXMgc2VjdGlvbgogIHdhcm5pbmcgPSBGQUxTRSwgICAgIyBEaXNwbGF5IHdhcm5pbmdzCiAgdGlkeSA9IEZBTFNFLAogIGZpZy5hbGlnbj0iY2VudGVyIiwgCiAgIyByZXN1bHRzID0gJ2hpZGUnLAogIHdpZHRoID0gMTAwICAgICAgICMgTnVtYmVyIG9mIGNoYXJhY3RlcnMgcGVyIGxpbmUKKQpgYGAKCgo8IS0tIENTUyB0byBjb2xvciBjaHVua3MgYW5kIG91dHB1dHMgLS0+CgoKYGBge2NzcywgZWNobz1GQUxTRX0KLm5vdHJ1biB7CiAgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmV5ICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgYmxhY2sgIWltcG9ydGFudDsKfQoubm90cnVubyB7CiAgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmV5ICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9Ci5xdWVzdGlvbiB7CiAgYmFja2dyb3VuZC1jb2xvcjogYXF1YW1hcmluZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBsaW1lZ3JlZW4gIWltcG9ydGFudDsKfQoucXVlc3Rpb25vIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBhcXVhbWFyaW5lICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9Ci5hbnN3ZXIgewogIGJhY2tncm91bmQtY29sb3I6IG5hdmFqb3doaXRlICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgYnJvd24gIWltcG9ydGFudDsKfQouYW5zd2VybyB7CiAgYmFja2dyb3VuZC1jb2xvcjogbmF2YWpvd2hpdGUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLmJleW9uZCB7CiAgYmFja2dyb3VuZC1jb2xvcjogdmlvbGV0ICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgcHVycGxlICFpbXBvcnRhbnQ7Cn0KLmJleW9uZG8gewogIGJhY2tncm91bmQtY29sb3I6IHZpb2xldCAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQpgYGAKCgo8IS0tIEhvb2sgdG8gaGFuZGxlIGNvZGUgYmxvY2tzIG91dHB1dCBmb2xkaW5nIC0tPgoKYGBge3Iga25pdF9ob29rLCBlY2hvID0gRkFMU0V9Cmhvb2tzID0ga25pdHI6OmtuaXRfaG9va3MkZ2V0KCkKaG9va19mb2xkYWJsZSA9IGZ1bmN0aW9uKHR5cGUpIHsKICBmb3JjZSh0eXBlKQogIGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgIHJlcyA9IGhvb2tzW1t0eXBlXV0oeCwgb3B0aW9ucykKICAgIAogICAgaWYgKGlzRkFMU0Uob3B0aW9uc1tbcGFzdGUwKCJmb2xkLiIsIHR5cGUpXV0pKSByZXR1cm4ocmVzKQogICAgCiAgICBwYXN0ZTAoCiAgICAgICI8ZGV0YWlscz48c3VtbWFyeT5TaG93ICIsIHR5cGUsICI8L3N1bW1hcnk+XG5cbiIsCiAgICAgIHJlcywKICAgICAgIlxuXG48L2RldGFpbHM+IgogICAgKQogIH0KfQprbml0cjo6a25pdF9ob29rcyRzZXQoCiAgb3V0cHV0ID0gaG9va19mb2xkYWJsZSgib3V0cHV0IiksCiAgcGxvdCA9IGhvb2tfZm9sZGFibGUoInBsb3QiKQopCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8Y2VudGVyPiFbXShlYjNpX2Jhbm5lci5wbmcpPC9jZW50ZXI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQUkVBTUJMRQoKIyMgUHVycG9zZSBvZiB0aGlzIHNlc3Npb24KClRoaXMgcHJhY3RpY2FsIHNlc3Npb24gaXMgZGVzaWduZWQgdG8gaGVscCB5b3UgdW5kZXJzdGFuZCB0aGUgaW1wb3J0YW5jZSBvZiBub3JtYWxpemF0aW9uLCBzY2FsaW5nLCBhbmQgcmVncmVzc2lvbiBpbiBzaW5nbGUtY2VsbCBSTkEtc2VxIGFuYWx5c2lzLiBZb3Ugd2lsbCBhcHBseSB0aGVzZSBzdGVwcyB1c2luZyBkaWZmZXJlbnQgbWV0aG9kcyBhbmQgdmlzdWFsaXplIHRoZSBlZmZlY3RzIG9uIHRoZSBkYXRhLgoKKipJbnB1dCBkYXRhKio6IFNldXJhdCBvYmplY3QgY29udGFpbmluZyB0aGUgZmlsdGVyZWQgY291bnQgbWF0cml4IGZyb20gdGhlIHByZXZpb3VzIGNsYXNzLiBJdCdzIGNhbGxlZCBgMDVfVEQzQV9TNV9Eb3VibGV0cy5GaWx0ZXJlZF8xMjUwOC40MDM1LlJEU2AuCgoqKk91dHB1dCBkYXRhKio6IFNldXJhdCBvYmplY3QgYWZ0ZXIgdGhlIG5vcm1hbGl6YXRpb24sIHNjYWxpbmcgYW5kIHJlZ3Jlc3Npb24uIEl0J3MgY2FsbGVkIGBzb2JqX1REM0Ffbm9ybWFsaXplZC5yZHNgLgoKIyMgTGVhcm5pbmcgT2JqZWN0aXZlcwoKLSAgIERpc2N1c3Mgd2h5IG5vcm1hbGl6aW5nIGNvdW50cyBpcyBuZWNlc3NhcnkgdG8gY29tcGFyZSBjZWxscwotICAgQmUgYWJsZSB0byB1bmRlcnN0YW5kIGRpZmZlcmVudCBub3JtYWxpemF0aW9uIGFwcHJvYWNoZXMKLSAgIEJlIGFibGUgdG8gZGVjaWRlIHdoZW4gdG8gcmVncmVzcyBvdXQgYSBnaXZlbiB2YXJpYWJsZSwgYnkgZXZhbHVhdGluZyB0aGUgZWZmZWN0cyBmcm9tIGFueSB1bndhbnRlZCBzb3VyY2VzIG9mIHZhcmlhdGlvbgoKIyMgTW90aXZhdGlvbgoKVXAgdG8gdGhpcyBwb2ludCwgd2UgaGF2ZSBmaWx0ZXJlZCBvdXQgZW1wdHkgZHJvcGxldHMsIGFtYmllbnQgUk5BIGNvbnRhbWluYXRpb24sIGxvdy1xdWFsaXR5IGNlbGxzLCBhbmQgZG91YmxldHMuIAoKVGhlIGRhdGEgaXMgbm93IGEgY291bnQgbWF0cml4IHdpdGggY2VsbHMgYXMgcm93cyBhbmQgZ2VuZXMgYXMgY29sdW1ucy4gVGhlc2UgY291bnRzIHJlZmxlY3QgdGhlIGNhcHR1cmUsIHJldmVyc2UgdHJhbnNjcmlwdGlvbiwgYW5kIHNlcXVlbmNpbmcgc3RlcHMgb2YgdGhlIHNjUk5BLXNlcSBleHBlcmltZW50LCBlYWNoIGludHJvZHVjaW5nIHZhcmlhYmlsaXR5LiBUaGlzIG1lYW5zIHRoZSBvYnNlcnZlZCBkaWZmZXJlbmNlcyBpbiBnZW5lIGV4cHJlc3Npb24gbWF5IGJlIGluZmx1ZW5jZWQgYnkgc2FtcGxpbmcgbm9pc2UgcmF0aGVyIHRoYW4gdHJ1ZSBiaW9sb2dpY2FsIGRpZmZlcmVuY2VzLCByZXN1bHRpbmcgaW4gdW5ldmVuIHZhcmlhbmNlIGFjcm9zcyB0aGUgZGF0YXNldC4KCk5vcm1hbGl6YXRpb24gYWRkcmVzc2VzIHRoaXMgYnkgYWRqdXN0aW5nIHJhdyBjb3VudHMgdG8gbWluaW1pemUgdGhlIGVmZmVjdHMgb2YgdmFyaWFibGUgc2FtcGxpbmcsIG1ha2luZyB0aGUgZGF0YSBtb3JlIHN1aXRhYmxlIGZvciBhbmFseXNpcywgd2hpY2ggb2Z0ZW4gYXNzdW1lcyB1bmlmb3JtIHZhcmlhbmNlLiBWYXJpb3VzIG5vcm1hbGl6YXRpb24gdGVjaG5pcXVlcyBleGlzdCwgZWFjaCB0YWlsb3JlZCB0byBzdXBwb3J0IGRpZmZlcmVudCBkb3duc3RyZWFtIGFuYWx5c2VzLgoKQSByZWNlbnQgc3R1ZHkgYnkgQWhsbWFubi1FbHR6ZSBhbmQgSHViZXIgKDIwMjMpIGJlbmNobWFya2VkIDIyIG5vcm1hbGl6YXRpb24gbWV0aG9kcy4gSXQncyBlc3NlbnRpYWwgdG8gc2VsZWN0IHRoZSBub3JtYWxpemF0aW9uIG1ldGhvZCBiYXNlZCBvbiB0aGUgc3BlY2lmaWMgYW5hbHlzaXMgZ29hbHMuIEhvd2V2ZXIsIGZvciB0aGlzIHByYWN0aWNhbCB3ZSB3aWxsIGp1c3QgZXhwbG9yZSB0aGUgbW9zdCB3ZWxsLWtub3duIHdheSB0byBkb2luZyBzaW5nbGUtY2VsbCBub3JtYWxpemF0aW9uLgoKIyMgU2V0dXAKCldlIGZpcnN0IGxvYWQgdGhlIHBhY2thZ2VzIHRoYXQgYXJlIG5lZWRlZCA6CgpgYGB7ciBsb2FkLWxpYnJhcmllcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoc2NhdGVyKQojIGxpYnJhcnkoZHBseXIpCmxpYnJhcnkocmVzaGFwZTIpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgU3RhcnQgUnN0dWRpbwoKLSAgIFVzaW5nIHRoZSBbT3Blbk9uRGVtYW5kIGNoZWF0CiAgICBzaGVldF0oaHR0cHM6Ly9tb29kbGUuZnJhbmNlLWJpb2luZm9ybWF0aXF1ZS5mci9wbHVnaW5maWxlLnBocC8xNDc1L21vZF9mb2xkZXIvY29udGVudC8wL09vRF9SX1JzdHVkaW8uaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSwKICAgIGNvbm5lY3QgdG8gdGhlIFtPcGVuT25EZW1hbmQKICAgIHBvcnRhbF0oaHR0cHM6Ly9vbmRlbWFuZC5jbHVzdGVyLmZyYW5jZS1iaW9pbmZvcm1hdGlxdWUuZnIpe3RhcmdldD0iX2JsYW5rIn0gICAgICBhbmQgKipjcmVhdGUgYSBSc3R1ZGlvIHNlc3Npb24qKiB3aXRoIHRoZSByaWdodCByZXNvdXJjZSByZXF1aXJlbWVudHMsIHRoYW5rcyAKICAgIHRvIHRoZSBjaGVhdCBzaGVldC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFdhcm0tdXAKCi0gICBXZSBub3cgc2V0ICoqY29tbW9uIHBhcmFtZXRlcnMqKiBhcyBuZXcgdmFyaWFibGVzLCBvbmNlIGFuZCBmb3IgYWxsIGZvciB0aGlzCnNlc3Npb24gOgoKYGBge3Igc2V0cGFyYW19CiMgc2V0cGFyYW0KCgojIyBTZXQgeW91ciBwcm9qZWN0IG5hbWUKIyBXQVJOSU5HIDogRG8gbm90IGp1c3QgY29weS1wYXN0ZSB0aGlzICEgSXQncyBNWSBwcm9qZWN0IG5hbWUgISBQdXQgWU9VUlMgISEKcHJvamVjdF9uYW1lIDwtICJlYmFpaV9zY190ZWFjaGVycyIKCgojIyBDb250cm9sIGlmIHRoZSBwcm9qZWN0X25hbWUgZXhpc3RzIG9uIHRoZSBjbHVzdGVyCmNhdCgnUEFUSCBDSEVDSyA6ICcsIGRpci5leGlzdHMocGFzdGUwKCcvc2hhcmVkL3Byb2plY3RzLycsIHByb2plY3RfbmFtZSkpKQoKIyMgU2VlZCBmb3IgdGhlIFJORwpteV9zZWVkIDwtIDEzMzdMCgpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFByZXBhcmUgdGhlIGRhdGEgc3RydWN0dXJlIAoKIyMgTWFpbiBkaXJlY3RvcnkKCmBgYHtyIG1haW5kaXJ9CiMgbWFpbmRpcgoKIyMgUHJlcGFyaW5nIHRoZSBwYXRoClREX2RpciA8LSBwYXN0ZTAoIi9zaGFyZWQvcHJvamVjdHMvIiwgcHJvamVjdF9uYW1lLCAiL1NDX1REIikKCiMjIENyZWF0aW5nIHRoZSByb290IGRpcmVjdG9yeQojIGRpci5jcmVhdGUocGF0aCA9IFREX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSByb290IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoVERfZGlyKQoKYGBgCgojIyBDdXJyZW50IHNlc3Npb24KCmBgYHtyIHNlc3Npb25kaXJ9CiMgc2Vzc2lvbmRpcgoKIyMgQ3JlYXRpbmcgdGhlIHNlc3Npb24gKFByb2MuMSkgZGlyZWN0b3J5CnNlc3Npb25fZGlyIDwtIHBhc3RlMChURF9kaXIsICIvMDRfUHJvYy4xIikKZGlyLmNyZWF0ZShwYXRoID0gc2Vzc2lvbl9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgc2Vzc2lvbiBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KHNlc3Npb25fZGlyKQoKYGBgCgojIyBJbnB1dCBkaXJlY3RvcnkKCmBgYHtyIGluZGlyfQojIGluZGlyCgojIyBDcmVhdGluZyB0aGUgSU5QVVQgZGF0YSBkaXJlY3RvcnkKaW5wdXRfZGlyIDwtIHBhc3RlMChzZXNzaW9uX2RpciwgIi9EQVRBIikKZGlyLmNyZWF0ZShwYXRoID0gaW5wdXRfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIGlucHV0IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoaW5wdXRfZGlyKQoKYGBgCgojIyBPdXRwdXQgZGlyZWN0b3J5CgpgYGB7ciBvdXRkaXJ9CiMgb3V0ZGlyCgojIyBDcmVhdGluZyB0aGUgT1VUUFVUIGRhdGEgZGlyZWN0b3J5Cm91dHB1dF9kaXIgPC0gcGFzdGUwKHNlc3Npb25fZGlyLCAiL1JFU1VMVFMiKQpkaXIuY3JlYXRlKHBhdGggPSBvdXRwdXRfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIG91dHB1dCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KG91dHB1dF9kaXIpCgpgYGAKCiMgUmVsb2FkIHRoZSBTZXVyYXQgT2JqZWN0CgotICAgV2UgY2FuIHJlbG9hZCB0aGUgb2JqZWN0IHdlIHNhdmVkIGF0IHRoZSBmb3JtZXIgc3RlcAoKYGBge3IgZGF0YWxvYWR9CiMgZGF0YWxvYWQKCgojIyBUaGlzIGlzIHRoZSBwYXRoIHRvIHRoZSBjdXJyZW50IEVCM0kgYmFja3VwCnNlc3Npb25pZCA8LSAnMjUzOF9lYjNpX24xXzIwMjUnCgoKIyMgVGhlIGxhdGVzdCBTZXVyYXQgb2JqZWN0IHNhdmVkIGFzIFJEUyAobmFtZSkKc29ial9maWxlIDwtICIwNV9URDNBX1M1X0RvdWJsZXRzLkZpbHRlcmVkXzEyNTA4LjQwMzUuUkRTIgoKIyMgVGhlIGxhdGVzdCBTZXVyYXQgb2JqZWN0IHNhdmVkIGFzIFJEUyAoZnVsbCBwYXRoKQpzb2JqX3BhdGggPC0gcGFzdGUwKFREX2RpciwgCiAgICAgICAgICAgICAgICAgICAgIi8wM19QcmVwcm9jLjMvUkVTVUxUUy8iLAogICAgICAgICAgICAgICAgICAgIHNvYmpfZmlsZSkKCmZvcmNlIDwtIEZBTFNFICAjIyBUbyBmb3JjZSBhIHJlLWRvd25sb2FkIG9mIGEgWmVub2RvLWhvc3RlZCBiYWNrdXAKbG9jYWwgPC0gRkFMU0UgICMjIFRvIGZvcmNlIGEgbG9hZGluZyBmcm9tIGEgbG9jYWwgYmFja3VwCgojIyBJbiBjYXNlIG9mIGVycm9yL2xvc3QgZGF0YSA6IGZvcmNlIGEgcmVsb2FkIGZyb20gYSBaZW5vZG8gYmFja3VwIHJlcG9zaXRvcnkKaWYoZm9yY2UpIHsKICB6ZW5faWQgPC0gIjE0MDM1MjkzIgogIHplbl9iYWNrdXBfZmlsZSA8LSBwYXN0ZTAoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmRzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ZW5faWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiL2ZpbGVzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqX2ZpbGUpCiAgIyMgUmVjcmVhdGUgdGhlIGV4cGVjdGVkIHBhdGggaWYgaXQgZG9lcyBub3QgZXhpc3QKICBkaXIuY3JlYXRlKHBhdGggPSBkaXJuYW1lKHNvYmpfcGF0aCksIHJlY3Vyc2l2ZSA9IFRSVUUpCiAgIyMgRG93bmxvYWQgdGhlIGZpbGUKICBkb3dubG9hZC5maWxlKHVybCA9IHplbl9iYWNrdXBfZmlsZSwKICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gc29ial9wYXRoKQp9CgojIyBJbiBjYXNlIG9mIGVycm9yL2xvc3QgZGF0YSA6IGZvcmNlIGEgcmVsb2FkIGZyb20gYSBsb2NhbCBiYWNrdXAgcmVwb3NpdG9yeQppZihsb2NhbCkgewogIHNvYmpfcGF0aCA8LSBwYXN0ZTAoCiAgICAiL3NoYXJlZC9wcm9qZWN0cy8iLCBzZXNzaW9uaWQsICIvYXRlbGllcl9zY3JuYXNlcS9URC9CQUNLVVAvUkRTLyIsCiAgICBzb2JqX2ZpbGUpCn0KCiMjIExvYWQgdGhlIG9iamVjdApzb2JqIDwtIHJlYWRSRFMoZmlsZSA9IHNvYmpfcGF0aCkKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIyBFeHBsb3JpbmcgdGhlIG9iamVjdAoKYGBge3IgcHJpbnRvdXRfc29ian0Kc29iagpgYGAKCldoYXQncyBpbiB0aGUgbWV0YWRhdGEgPwoKYGBge3IgZXhwbG9yZV9tZXRhZGF0YX0KaGVhZChzb2JqQG1ldGEuZGF0YSkKYGBgCgpXaGF0IGFyZSB0aGUgYXNzYXlzID8KCmBgYHtyIGV4cGxvcmVfYXNzYXlzfQpTZXVyYXQ6OkFzc2F5cyhvYmplY3QgPSBzb2JqKQpgYGAKCldoYXQncyBhIGxheWVyID8gSG93IHRvIGtub3cgaG93IG1hbnkgdGhlcmUgYXJlID8KCmBgYHtyIGV4cGxvcmVfcm5hX2xheWVyc30KTGF5ZXJzKG9iamVjdCA9IHNvYmosIGFzc2F5ID0gIlJOQSIpCmBgYAoKRGlzcGxheSBmaXJzdCAxMCByb3cgYW5kIGZpcnN0IDEwIGNvbHVtbnMgb2YgcmF3IG1hdHJpY2UKCmBgYHtyIGV4cGxvcmVfY291bnRfbWF0fQpzb2JqQGFzc2F5cyRSTkFAbGF5ZXJzJGNvdW50c1sxOjEwLCAxOjEwXQpgYGAKCklmIHRoZXJlIGlzIGFub3RoZXIgbWF0cml4LCBkaXNwbGF5IGl0cyBmaXJzdCAxMCByb3cgYW5kIGZpcnN0IDEwIGNvbHVtbnMKCmBgYHtyLCBleHBsb3JlX2RhdGFfbWF0fQpzb2JqQGFzc2F5cyRSTkFAbGF5ZXJzJGRhdGFbMToxMCwgMToxMF0KYGBgCgojIyMgQXNzZXNzaW5nIG9mIHRoZSBsZXZlbCBvZiBkYXRhIHNwYXJjaXR5CgpgYGB7ciBleHRyYWN0IHJhd19jb3VudHN9CnJhd19jb3VudCA8LSBHZXRBc3NheURhdGEob2JqZWN0ID0gc29iaiwgYXNzYXkgPSAiUk5BIiwgbGF5ZXIgPSAiY291bnRzIikKYGBgCgpDYW4geW91IHNwb3QgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBgc29iakBhc3NheXMkUk5BQGxheWVycyRkYXRhWzE6MTAsIDE6MTBdYCBhbmQgYHJhd19jb3VudFsxOjEwLCAxOjEwXWAKCmBgYHtyIGV4cGxvcmUgcmF3X2NvdW50c30KcmF3X2NvdW50WzE6MTAsIDE6MTBdCmBgYAoKYGBge3IgcHJvcHRhYmxlX3NwYXJjZX0KcHJvcC50YWJsZSgKICB0YWJsZShhcy5tYXRyaXgocmF3X2NvdW50KT4wKQogICkqMTAwCmBgYAoKYGBge3IgbWVhbl9zcGFyc2V9Cm1lYW4oYXMubWF0cml4KHJhd19jb3VudCk+MCkqMTAwCmBgYAoKRXh0cmFjdGluZyBnZW5lcyBhdHRyaWJ1dGVzCgpgYGB7ciBnZW5lX2F0dHJfZGZ9CmdlbmVfYXR0ciA8LSBkYXRhLmZyYW1lKCJtZWFuIiA9IHJvd01lYW5zKHJhd19jb3VudCksIAogICAgICAgICAgICAgICAgICAgICAgICAiZGV0ZWN0aW9uX3JhdGUiID0gcm93TWVhbnMocmF3X2NvdW50ID4gMCksIAogICAgICAgICAgICAgICAgICAgICAgICAidmFyIiA9IGFwcGx5KHJhd19jb3VudCwgMSwgdmFyKSkKZ2VuZV9hdHRyJGxvZ19tZWFuIDwtIGxvZzEwKGdlbmVfYXR0ciRtZWFuKQpnZW5lX2F0dHIkbG9nX3ZhciA8LSBsb2cxMChnZW5lX2F0dHIkdmFyKQpgYGAKCkV4dHJhY3RpbmcgY2VsbCBhdHRyaWJ1dGVzCgpgYGB7ciBjZWxsX2F0dHJfZGZ9CmNlbGxfYXR0ciA8LSBkYXRhLmZyYW1lKG5fdW1pID0gY29sU3VtcyhyYXdfY291bnQpLCAKICAgICAgICAgICAgICAgICAgICAgICAgbl9nZW5lID0gY29sU3VtcyhyYXdfY291bnQgPjApKQpgYGAKCk1lYW4tVmFyaWFuY2UgcmVsYXRpb25zaGlwCgpgYGB7ciB2YXJCWW1lYW5QbG90fQpnZ3Bsb3QoZ2VuZV9hdHRyLCBhZXMoeCA9IG1lYW4sIHkgPSB2YXIpKSsKICBnZW9tX3BvaW50KCkKYGBgCgpDYW4gd2UgZG8gYmV0dGVyID8/CgpgYGB7cn0KZ2dwbG90KGdlbmVfYXR0ciwgYWVzKHggPSBsb2dfbWVhbiwgeSA9IGxvZ192YXIpKSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMsIHNoYXBlID0gMTYpICsgCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJyZWQiKSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBsb2cxMChtZWFuKGdlbmVfYXR0ciRtZWFuKSksIGxpbmV0eXBlID0gImRhc2hlZCIpCmBgYAoKT3RoZXIgdHlwZXMgb2YgcmVsZXZhbnQgaW5mb3JtYXRpb24gdG8gbG9vayBhdAoKYGBge3J9CnN1bW1hcnkoc29iaiRuQ291bnRfUk5BKQpzdW1tYXJ5KHNvYmokbkZlYXR1cmVfUk5BKQpzdW1tYXJ5KHNvYmokcGVyY2VudF9tdCoxMDApCmBgYAoKUmVsYXRpb25zaGlwIGJldHdlZW4gblVNSSAmIG51bWJlciBvZiBkZXRlY3RlZCBnZW5lcwoKYGBge3IgbkdlbmVzX25VTUlzZXVyYXR9CkZlYXR1cmVTY2F0dGVyKAogIG9iamVjdCA9IHNvYmosIAogIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCAKICBmZWF0dXJlMiA9ICJuRmVhdHVyZV9STkEiLAogIGNvbHMgPSAiZ3JleTEwIgopCmBgYAoKYGBge3IgbkdlbmVzX25VTUlnZ3Bsb3R9CmdncGxvdChzb2JqQG1ldGEuZGF0YSwKICAgICAgIGFlcyh4ID0gbkNvdW50X1JOQSwgeSA9IG5GZWF0dXJlX1JOQSkpKwogIGdlb21fcG9pbnQoKQpgYGAKCmBgYHtyIG1pdG9fblVNSX0KRmVhdHVyZVNjYXR0ZXIoCiAgb2JqZWN0ID0gc29iaiwgCiAgZmVhdHVyZTEgPSAibkNvdW50X1JOQSIsIAogIGZlYXR1cmUyID0gInBlcmNlbnRfbXQiLAogIGNvbHMgPSAiZ3JleTEwIgopCmBgYAoKIyMgTm9ybWFsaXplIGRhdGE6IFNoaWZ0ZWQgbG9nYXJpdGhtIG1ldGhvZAoKYGBge3IgbG9nbm9ybX0Kc29iaiA8LSBOb3JtYWxpemVEYXRhKG9iamVjdCA9IHNvYmosCiAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCAKICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLmZhY3RvciA9IDFlNCkKYGBgCgpWaXN1YWxpemUgdGhlIGVmZmVjdCBvZiB0aGUgbm9ybWFsaXphdGlvbgoKYGBge3IgZXh0cmFjdCBsb2dub3JtbWF0fQpsb2dOb3JtX2NvdW50IDwtIEdldEFzc2F5RGF0YShvYmplY3QgPSBzb2JqLCBhc3NheSA9ICJSTkEiLCBsYXllciA9ICJkYXRhIikKYGBgCgpgYGB7cn0KU3VtQ291bnRfZGYgPC0gZGF0YS5mcmFtZSgKICAicmF3IiA9IGNvbFN1bXMocmF3X2NvdW50KSwKICAibG9nTm9ybSIgPSBjb2xTdW1zKGxvZ05vcm1fY291bnQpCikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9CihnZ3Bsb3QoU3VtQ291bnRfZGYsIGFlcyhyYXcpKSsKICAgZ2VvbV9kZW5zaXR5KCkrCiAgIGdndGl0bGUoIlJhdyAoaS5lIG5vdCBub3JtYWxpemVkKSBkYXRhIikrCiAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAuNSwgY29sb3IgPSAic3RlZWxibHVlIikpKSsgKGdncGxvdChTdW1Db3VudF9kZiwgYWVzKGxvZ05vcm0pKSsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW9tX2RlbnNpdHkoKSsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3RpdGxlKCJMb2dub3JtIGRhdGEiKSsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gLjUsIGNvbG9yID0gInN0ZWVsYmx1ZSIpKQogICApCmBgYAoKRWZmZWN0IG9mIHRoZSBub3JtYWxpemF0aW9uIG9uIEV4cHJlc3Npb24gb2YgZ2VuZXMKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0KcmF3X2V4cCA8LSBWbG5QbG90KAogIG9iamVjdCA9IHNvYmosIAogIGZlYXR1cmVzID0gYygiU3JtIiwgIkFwb2UiLCAiVG9wMmEiKSwKICBhc3NheSA9ICJSTkEiLCAKICBsYXllciA9ICJjb3VudHMiCikKbm9ybV9leHAgPC0gVmxuUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICBmZWF0dXJlcyA9IGMoIlNybSIsICJBcG9lIiwgIlRvcDJhIiksCiAgYXNzYXkgPSAiUk5BIiwgCiAgbGF5ZXIgPSAiZGF0YSIKKQoKd3JhcF9wbG90cyhyYXdfZXhwLG5vcm1fZXhwKSArIHBsb3RfbGF5b3V0KG5yb3cgPSAyKQpgYGAKCgpgYGB7cn0KbG9nTm9ybV9leHBfM2dlbmVzIDwtIEZldGNoRGF0YShvYmplY3QgPSBzb2JqLCB2YXJzID0gYygiU3JtIiwgIkFwb2UiLCAiVG9wMmEiKSwgbGF5ZXIgPSAiZGF0YSIpCmxvZ05vcm1fZXhwXzNnZW5lc19sIDwtIHJlc2hhcGUyOjptZWx0KGxvZ05vcm1fZXhwXzNnZW5lcykKYGBgCgpgYGB7cn0KcmF3X2V4cF8zZ2VuZXMgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IHNvYmosIHZhcnMgPSBjKCJTcm0iLCAiQXBvZSIsICJUb3AyYSIpLCBsYXllciA9ICJjb3VudHMiKQpyYXdfZXhwXzNnZW5lc19sIDwtIHJlc2hhcGUyOjptZWx0KHJhd19leHBfM2dlbmVzKQpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9CihnZ3Bsb3QoZGF0YSA9IHJhd19leHBfM2dlbmVzX2wsIGFlcyh4ID0gdmFyaWFibGUsIHkgPSB2YWx1ZSkpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gLjIsIHNpemUgPSAuMSkrCiAgZ2VvbV9ib3hwbG90KAogICAgZGF0YSA9IHJhd19leHBfM2dlbmVzX2xbcmF3X2V4cF8zZ2VuZXNfbCR2YWx1ZT4wLF0sCiAgICBvdXRsaWVycyA9IEYsIAogICAgc2l6ZSA9IC41LAogICAgd2lkdGggPSAuMSkrCiAgIGdndGl0bGUoIlJhdyBFeHByZXNzaW9uIikrCiAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBjb2xvciA9ICJncmV5NTAiLCBmYWNlID0gImJvbGQiLCBoanVzdCA9IC41KSkpICsgKGdncGxvdChkYXRhID0gbG9nTm9ybV9leHBfM2dlbmVzX2wsIGFlcyh4ID0gdmFyaWFibGUsIHkgPSB2YWx1ZSkpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gLjIsIHNpemUgPSAuMSkrCiAgZ2VvbV9ib3hwbG90KAogICAgZGF0YSA9IGxvZ05vcm1fZXhwXzNnZW5lc19sW2xvZ05vcm1fZXhwXzNnZW5lc19sJHZhbHVlPjAsXSwKICAgIG91dGxpZXJzID0gRiwgCiAgICBzaXplID0gLjUsCiAgICB3aWR0aCA9IC4xKSsKICAgZ2d0aXRsZSgibG9nTm9ybSBFeHByZXNzaW9uIikrCiAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBjb2xvciA9ICJncmV5NTAiLCBmYWNlID0gImJvbGQiLCBoanVzdCA9IC41KSkpCmBgYAoKIyMgTm9ybWFsaXplIGRhdGEgSUk6IFdoYXQncyB0aGUgbmFtZSBhbHJlYWR5ID8/PwoKYGBge3Igc2N0cmFuc2Zvcm19CnNvYmogPC0gU0NUcmFuc2Zvcm0ob2JqZWN0ID0gc29iaikKYGBgCgpIb3cgdGhpcyBub3JtYWxpemF0aW9uIGFmZmVjdCB0aGUgZXhwcmVzc2lvbiBvZiB0aGUgMyBnZW5lcyA/PwoKYGBge3J9CnNjdF9leHAgPC0gVmxuUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICBmZWF0dXJlcyA9IGMoIlNybSIsICJBcG9lIiwgIlRvcDJhIiksCiAgYXNzYXkgPSAiU0NUIiwgCiAgbGF5ZXIgPSAiZGF0YSIKKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTJ9CndyYXBfcGxvdHMocmF3X2V4cCxub3JtX2V4cCxzY3RfZXhwKSArIHBsb3RfbGF5b3V0KG5yb3cgPSAzKQpgYGAKCmBgYHtyfQpTQ1RfZXhwXzNnZW5lcyA8LSBGZXRjaERhdGEob2JqZWN0ID0gc29iaiwgdmFycyA9IGMoIlNybSIsICJBcG9lIiwgIlRvcDJhIiksIGFzc2F5ID0gIlNDVCIsIGxheWVyID0gImRhdGEiKQpTQ1RfZXhwXzNnZW5lc19sIDwtIHJlc2hhcGUyOjptZWx0KFNDVF9leHBfM2dlbmVzKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0KKGdncGxvdChkYXRhID0gcmF3X2V4cF8zZ2VuZXNfbCwgYWVzKHggPSB2YXJpYWJsZSwgeSA9IHZhbHVlKSkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAuMiwgc2l6ZSA9IC4xKSsKICBnZW9tX2JveHBsb3QoCiAgICBkYXRhID0gcmF3X2V4cF8zZ2VuZXNfbFtyYXdfZXhwXzNnZW5lc19sJHZhbHVlPjAsXSwKICAgIG91dGxpZXJzID0gRiwgCiAgICBzaXplID0gLjUsCiAgICB3aWR0aCA9IC4xKSsKICAgZ2d0aXRsZSgiUmF3IEV4cHJlc3Npb24iKSsKICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gImdyZXk1MCIsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gLjUpKSkgKyAoZ2dwbG90KGRhdGEgPSBsb2dOb3JtX2V4cF8zZ2VuZXNfbCwgYWVzKHggPSB2YXJpYWJsZSwgeSA9IHZhbHVlKSkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAuMiwgc2l6ZSA9IC4xKSsKICBnZW9tX2JveHBsb3QoCiAgICBkYXRhID0gbG9nTm9ybV9leHBfM2dlbmVzX2xbbG9nTm9ybV9leHBfM2dlbmVzX2wkdmFsdWU+MCxdLAogICAgb3V0bGllcnMgPSBGLCAKICAgIHNpemUgPSAuNSwKICAgIHdpZHRoID0gLjEpKwogICBnZ3RpdGxlKCJsb2dOb3JtIEV4cHJlc3Npb24iKSsKICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gImdyZXk1MCIsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gLjUpKSkgKyAoZ2dwbG90KGRhdGEgPSBTQ1RfZXhwXzNnZW5lc19sLCBhZXMoeCA9IHZhcmlhYmxlLCB5ID0gdmFsdWUpKSsKICBnZW9tX2ppdHRlcihoZWlnaHQgPSAwLCB3aWR0aCA9IC4yLCBzaXplID0gLjEpKwogIGdlb21fYm94cGxvdCgKICAgIGRhdGEgPSBTQ1RfZXhwXzNnZW5lc19sW1NDVF9leHBfM2dlbmVzX2wkdmFsdWU+MCxdLAogICAgb3V0bGllcnMgPSBGLCAKICAgIHNpemUgPSAuNSwKICAgIHdpZHRoID0gLjEpKwogICBnZ3RpdGxlKCJTQ1QgRXhwcmVzc2lvbiIpKwogICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgY29sb3IgPSAiZ3JleTUwIiwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAuNSkpKQpgYGAKCkNvbXBhcmUgdGhlIExheWVycyBhdmFpbGFibGUgaW4gdGhlIGRpZmZlcmVudCAiQXNzYXlzIiBhdmFpbGFibGUKCmBgYHtyfQpMYXllcnMoc29iaiwgYXNzYXkgPSAiU0NUIikKYGBgCgpgYGB7cn0KTGF5ZXJzKHNvYmosIGFzc2F5ID0gIlJOQSIpCmBgYAoKV2hhdCdzIG1pc3NpbmcgPwoKYGBge3J9CnNvYmogPC0gU2NhbGVEYXRhKG9iamVjdCA9IHNvYmosIGFzc2F5ID0gIlJOQSIpCmBgYAoKYGBge3J9CkxheWVycyhzb2JqLCBhc3NheSA9ICJSTkEiKQpgYGAKCmBgYHtyfQpSZWR1Y3Rpb25zKG9iamVjdCA9IHNvYmopCmBgYAoKYGBge3IgZmluZHJuYUhWR30Kc29iaiA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBzb2JqLCBhc3NheSA9ICJSTkEiKQpgYGAKCkNvbXBhcmUgdGhlIHZhcmlhbmNlLXN0YWJpbGl6YXRpb24gZm9yIHRoZSAyIG1ldGhvZHMKCmBgYHtyfQpybmFfaHZnIDwtIFZhcmlhYmxlRmVhdHVyZVBsb3QoCiAgb2JqZWN0ID0gc29iaiwKICBhc3NheSA9ICJSTkEiCikKc2N0X2h2ZyA8LSBWYXJpYWJsZUZlYXR1cmVQbG90KAogIG9iamVjdCA9IHNvYmosCiAgYXNzYXkgPSAiU0NUIgopCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQp3cmFwX3Bsb3RzKHJuYV9odmcsc2N0X2h2ZykrcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKYGBge3J9CnJuYV9odmcreWxpbShjKDAsMTApKQpgYGAKCmBgYHtyfQpzY3RfaHZnK3lsaW0oYygwLDEwKSkKYGBgCgpDb21wYXJpbmcgdGhlIDIgbm9ybWFsaXphdGlvbiBtZXRob2RzIGF0IHRoZSBQQ0EgbGV2ZWwKCmBgYHtyIGNvbXB1dGUgUENBfQpzb2JqIDwtIFJ1blBDQShvYmplY3QgPSBzb2JqLCBhc3NheSA9ICJSTkEiLCByZWR1Y3Rpb24ubmFtZSA9ICJwY2FSTkEiLCByZWR1Y3Rpb24ua2V5ID0gIlBDcm5hXyIpCnNvYmogPC0gUnVuUENBKG9iamVjdCA9IHNvYmosIGFzc2F5ID0gIlNDVCIsIHJlZHVjdGlvbi5uYW1lID0gInBjYVNDVCIsIHJlZHVjdGlvbi5rZXkgPSAiUENzY3RfIikKYGBgCgpgYGB7ciBybmFnZW5lTG9hZF9QQ30KcHJpbnQoc29ialtbInBjYVJOQSJdXSwgZGltcyA9IDE6NSwgbmZlYXR1cmVzID0gMzApCmBgYAoKYGBge3Igc2N0Z2VuZUxvYWRfUEN9CnByaW50KHNvYmpbWyJwY2FTQ1QiXV0sIGRpbXMgPSAxOjUsIG5mZWF0dXJlcyA9IDMwKQpgYGAKCgpgYGB7ciBkaXNwbGF5IGRpbXBsb3RzLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0KRGltUGxvdChvYmplY3QgPSBzb2JqLCByZWR1Y3Rpb24gPSAicGNhUk5BIikrRGltUGxvdChvYmplY3QgPSBzb2JqLCByZWR1Y3Rpb24gPSAicGNhU0NUIikKYGBgCgoKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0KRmVhdHVyZVBsb3Qob2JqZWN0ID0gc29iaiwgcmVkdWN0aW9uID0gInBjYVJOQSIsIGZlYXR1cmVzID0gIm5Db3VudF9STkEiKQpGZWF0dXJlUGxvdChvYmplY3QgPSBzb2JqLCByZWR1Y3Rpb24gPSAicGNhU0NUIiwgZmVhdHVyZXMgPSAibkNvdW50X1JOQSIpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NX0KRmVhdHVyZVNjYXR0ZXIob2JqZWN0ID0gc29iaiwgCiAgICAgICAgICAgICAgIGZlYXR1cmUxID0gIlBDcm5hXzEiLCAKICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAibkNvdW50X1JOQSIpICsgRmVhdHVyZVNjYXR0ZXIob2JqZWN0ID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmUxID0gIlBDc2N0XzEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAibkNvdW50X1JOQSIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD01fQpGZWF0dXJlU2NhdHRlcihvYmplY3QgPSBzb2JqLCAKICAgICAgICAgICAgICAgZmVhdHVyZTEgPSAiUENybmFfMSIsIAogICAgICAgICAgICAgICBmZWF0dXJlMiA9ICJDQ19TZXVyYXRfUy5TY29yZSIpICsgRmVhdHVyZVNjYXR0ZXIob2JqZWN0ID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlMSA9ICJQQ3NjdF8xIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmUyID0gIkNDX1NldXJhdF9TLlNjb3JlIikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9CkRpbVBsb3Qob2JqZWN0ID0gc29iaiwgCiAgICAgICAgcmVkdWN0aW9uID0gInBjYVJOQSIsCiAgICAgICAgZ3JvdXAuYnkgPSAiQ0NfU2V1cmF0X1BoYXNlIikrRGltUGxvdChvYmplY3QgPSBzb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJwY2FTQ1QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiQ0NfU2V1cmF0X1BoYXNlIikKYGBgCgojIyBSZWdyZXNzaW5nIG91dCB1bndhbnRlZCBzb3VyY2Ugb2YgdmFyaWF0aW9uCgpgYGB7ciByZWdPdXRjY2d9CnNvYmpfY2NnIDwtIFNjYWxlRGF0YShvYmplY3QgPSBzb2JqLCAKICAgICAgICAgICAgICAgICAgICAgIHZhcnMudG8ucmVncmVzcyA9IGMoIkNDX1NldXJhdF9TLlNjb3JlIiwgIkNDX1NldXJhdF9HMk0uU2NvcmUiKSwgCiAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiKQpzb2JqX2NjZyA8LSBSdW5QQ0Eob2JqZWN0ID0gc29ial9jY2csIGFzc2F5ID0gIlJOQSIsIHJlZHVjdGlvbi5uYW1lID0gInBjYVJOQWNjZyIsIHJlZHVjdGlvbi5rZXkgPSAiUENybmFjY2dfIikKYGBgCgoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQpEaW1QbG90KG9iamVjdCA9IHNvYmosIAogICAgICAgIHJlZHVjdGlvbiA9ICJwY2FSTkEiLAogICAgICAgIGdyb3VwLmJ5ID0gIkNDX1NldXJhdF9QaGFzZSIpK0RpbVBsb3Qob2JqZWN0ID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAicGNhU0NUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gIkNDX1NldXJhdF9QaGFzZSIpK0RpbVBsb3Qob2JqZWN0ID0gc29ial9jY2csIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInBjYVJOQWNjZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJDQ19TZXVyYXRfUGhhc2UiKQpgYGAKCmBgYHtyfQpwcmludChzb2JqX2NjZ1tbInBjYVJOQWNjZyJdXSwgZGltcyA9IDE6NSwgbmZlYXR1cmVzID0gMzApCmBgYAoKIyBSIHNlc3Npb24KClRoaXMgaXMgYSBnb29kIHByYWN0aWNlIHRvIHNob3cgdGhlIHZlcnNpb24gb2YgdGhlIHBhY2thZ2VzIHVzZWQgaW4gdGhpcyBub3RlYm9vay4KCmBgYHtyIHJzZXNzaW9uLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KIyByc2Vzc2lvbgoKc2Vzc2lvbkluZm8oKQoKYGBgCg==