EB3I n1 2025 scRNAseq
-
PRE-PROCESSING (II)
-
Quality control : Counts & metrics





1 PREAMBLE

1.1 Purpose of this session

This file describes the different steps to perform the quality control of a single cell dataset, based on (per cell) :

  • the number of UMI (~transcripts) detected
  • the number of expressed genes detected
  • the proportion of UMI corresponding to mitochondrial genes
  • the proportion of UMI corresponding to riboprotein-coding genes
  • the proportion of transcripts related to stress signature

Input data: A filtered, flat count matrix retrieved from GEO.

Output data: A Seurat object annotated for the “bad” cells to filter out for downstream analysis.



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

# ## Empty droplets max p-value
# max_p <- 1E-03


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 (Preproc.2) directory
session_dir <- paste0(TD_dir, "/02_Preproc.2")
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/02_Preproc.2"

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/02_Preproc.2/DATA"

4.4 Genelists directory

This is a directory where we will store additional information from knowledge bases about genes used to estimate the cell cycle phase of cells.

# resdir

res_dir <- paste0(TD_dir, "/Resources")
glist_dir <- paste0(res_dir, "/Genelists")

## Create the directory
dir.create(path = glist_dir, recursive = TRUE)

## Print the resources directory on-screen
print(glist_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/Resources/Genelists"

4.5 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/02_Preproc.2/RESULTS"


5 Prerequisites

We have to retrieve the input data file

# mat_dl

local <- FALSE

## The raw count matrix we will start from
scmat_source <- "GSM4861194_gex_2_raw_gene_expression.tsv.gz"

## Download the file from Zenodo
if (!local) {
  
  ### ZenID
  zen_id <- "14033941"
  ### Zen Path
  zen_backup_file <- paste0("https://zenodo.org/records/",
                            zen_id,
                            "/files/",
                            scmat_source)
  
  ## The path to the locally saved input file
  scmat_file <- paste0(input_dir,
                       '/',
                       scmat_source)
  ## Download the file
  download.file(url = zen_backup_file,
                destfile = scmat_file)
} else {
  ebaii_session <- '2538_eb3i_n1_2025'
  scmat_file <- paste0(
      '/shared/projects/',
      ebaii_session,
      '/atelier_scrnaseq/TD/BACKUP/TSV/',
      scmat_source)
}


6 Load the genelists resources

We retrieve the genelists

# gl_dl

local <- FALSE

## The genelist files
mito_source <- "mus_musculus_mito_symbols_20191015.rds"
ribo_source <- "mus_musculus_cribo_symbols_20191015.rds"
stress_source <- "mus_musculus_stress_symbols_20200224.rds"
gl_sources <- c(mito_source, ribo_source, stress_source)

## The (future) local files
mito_file <- paste0(input_dir, '/', mito_source)
ribo_file <- paste0(input_dir, '/', ribo_source)
stress_file <- paste0(input_dir, '/', stress_source)
gl_files <- c(mito_file, ribo_file, stress_file)

## Download the file from Zenodo
if (!local) {
  
  ### ZenID
  zen_id <- "14037355"
  ### Looping on files
  for (glf in seq_along(gl_sources)) {
    ### Zen Path
    zen_backup_file <- paste0("https://zenodo.org/records/",
                              zen_id,
                              "/files/",
                              gl_sources[glf])
    
    ## Download the file
    download.file(url = zen_backup_file,
                  destfile = gl_files[glf])
  }
  rm(gl_files)
} else {  ## Local mode
  ebaii_session <- '2538_eb3i_n1_2025'
  localbackup_dir <- paste0('/shared/projects/', ebaii_session, '/atelier_scrnaseq/TD/RESOURCES/GENELISTS/')
  mito_file <- paste0(localbackup_dir, '/mus_musculus_mito_symbols_20191015.rds')
  ribo_source <- paste0(input_dir, '/mus_musculus_cribo_symbols_20191015.rds')
  stress_source <- paste0(input_dir, '/mus_musculus_stress_symbols_20200224.rds')
}


7 Biological context

For this series of training sessions, we will work on data from this Paiva et al. publication.

  • The study concerns thymus autonomy:

    • The thymus is an “organ of passage”, critical in its function to the adaptive immune system for the maturation of T cell lymphocytes.
    • This maturation involves two main steps, performed thanks to macrophages:
      • Positive selection : keeping cells that successfully develop react appropriately with MHC immune receptors of the body
      • Negative selection : keeping cells that do not react against natural proteins of the body.
    • Thymus autonomy is a natural mechanism that allows to create T cells in the thymus by differentiation and cell competition, even when normal progenitors from the bone marrow are lacking, in critical conditions.
    • This mechanism is known in its effects, but the cells involved in are not.
    • This study is of importance in the health field, as this mechanism relies on a temporary loss of control of the cell normal functions.
    • The consequence is that if thymus is in autonomy for too long (few weeks), this is a prelude for leukemia !



  • Organism is : mus musculus
  • Individuals are : newborn mice, grafted
  • The design corresponds to two conditions (Test / Control)
    • Control : thymus from wild type newborn mice transplanted into wild type juvenile mouse. In this control case, donor T-cells progenitors (DN3) were replaced by host cells 3 weeks after transplantation.
    • Test : thymus from wild type newborn mice transplanted in KO Rag-/- type juvenile mouse (the KO partially impairs their ability to produce T-cell progenitors in normal amounts). In this test case, donor T-cells progenitors (DN3) were replaced by host cells 9 weeks after transplantation, showing that the donor DN3 cells outlived their normal lifespan by ~6 weeks.

You will mainly work on the KO sample : TD3A.

The input data consists in a count matrix, as a gzipped tabular text file, that contains everything needed to create a basic Seurat object :

  • The expressions counts
  • The feature names (here, gene symbols)
  • The barcode names

This matrix has already been filtered for empty droplets.

8 Environment

We load the packages of interest:

# packages
library(ggplot2)
library(Seurat)

9 Data

We load the count matrix:

# mat_load

## Loading the matrix directly as a sparseMatrix
scmat <- scuttle::readSparseCounts(
    file = scmat_file, 
    sep = "\t")

## Displaying its size in-memory (this is a basic matrix)
format(utils::object.size(scmat), units = "auto")
Show output
[1] "88.3 Mb"
# mat_desc

## A basic description of the matrix
dim(scmat)
Show output
[1] 31053  4587
scmat[c(1:5), c(1:5)]
Show output
5 x 5 sparse Matrix of class "dgCMatrix"
        AAACCTGAGACGCTTT.1 AAACCTGAGGCATTGG.1 AAACCTGGTCAACATC.1
Xkr4                     .                  .                  .
Gm1992                   .                  .                  .
Gm37381                  .                  .                  .
Rp1                      .                  .                  .
Sox17                    .                  .                  .
        AAACCTGTCGAGGTAG.1 AAACCTGTCGATCCCT.1
Xkr4                     .                  .
Gm1992                   .                  .
Gm37381                  .                  .
Rp1                      .                  .
Sox17                    .                  .

We want to build a Seurat object from the count matrix.

But how do we do ??




# mat2seurat

?Seurat::CreateSeuratObject()




# make_sobj

sobj = Seurat::CreateSeuratObject(counts = scmat,
                                  assay = "RNA",
                                  project = "TD3A")
sobj
Show output
An object of class Seurat 
31053 features across 4587 samples within 1 assay 
Active assay: RNA (31053 features, 0 variable features)
 1 layer present: counts

We do not need the count matrix anymore :

# rm_mat

rm(scmat)

We set the filtering thresholds based on quality control-related metrics.

Adjust them as necessary based on the figures.

# set_thresholds

cut_nCount_RNA = 1000
cut_log10_nCount_RNA = log10(cut_nCount_RNA)
cut_nFeature_RNA = 750
cut_percent_mt = 5
cut_percent_rb = 20
cut_percent_st = 6

We define a nice palette to visualize the QC metrics:

# color_palette

color_palette = c("lightgray", "#FDBB84", "#EF6548", "#7F0000", "black")

For the QC metrics related to the proportion of UMI belongs to a specific gene sets, we need to load the gene sets.

# load_lists

## MITO
mito_symbols = readRDS(mito_file)
mito_symbols
Show output
 [1] "mt-Atp6" "mt-Atp8" "mt-Co1"  "mt-Co2"  "mt-Co3"  "mt-Cytb" "mt-Nd1" 
 [8] "mt-Nd2"  "mt-Nd3"  "mt-Nd4"  "mt-Nd4l" "mt-Nd5"  "mt-Nd6"  "mt-Rnr1"
[15] "mt-Rnr2" "mt-Ta"   "mt-Tc"   "mt-Td"   "mt-Te"   "mt-Tf"   "mt-Tg"  
[22] "mt-Th"   "mt-Ti"   "mt-Tk"   "mt-Tl1"  "mt-Tl2"  "mt-Tm"   "mt-Tn"  
[29] "mt-Tp"   "mt-Tq"   "mt-Tr"   "mt-Ts1"  "mt-Ts2"  "mt-Tt"   "mt-Tv"  
[36] "mt-Tw"   "mt-Ty"  
## RIBO
ribo_symbols = readRDS(ribo_file)
ribo_symbols
Show output
 [1] "Rpsa"   "Rps2"   "Rps3"   "Rps3a"  "Rps4x"  "Rps5"   "Rps6"   "Rps7"  
 [9] "Rps8"   "Rps9"   "Rps10"  "Rps11"  "Rps12"  "Rps13"  "Rps14"  "Rps15" 
[17] "Rps15a" "Rps16"  "Rps17"  "Rps18"  "Rps19"  "Rps20"  "Rps21"  "Rps23" 
[25] "Rps24"  "Rps25"  "Rps26"  "Rps27"  "Rps27a" "Rps28"  "Rps29"  "Rps30" 
[33] "Rpl3"   "Rpl4"   "Rpl5"   "Rpl6"   "Rpl7"   "Rpl7a"  "Rpl8"   "Rpl9"  
[41] "Rpl10"  "Rpl10a" "Rpl11"  "Rpl12"  "Rpl13"  "Rpl13a" "Rpl14"  "Rpl15" 
[49] "Rpl17"  "Rpl18"  "Rpl18a" "Rpl19"  "Rpl21"  "Rpl22"  "Rpl23"  "Rpl23a"
[57] "Rpl24"  "Rpl26"  "Rpl27"  "Rpl27a" "Rpl28"  "Rpl29"  "Rpl30"  "Rpl31" 
[65] "Rpl32"  "Rpl34"  "Rpl35"  "Rpl35a" "Rpl36"  "Rpl44"  "Rpl37"  "Rpl37a"
[73] "Rpl38"  "Rpl39"  "Rpl40"  "Rpl41"  "Rplp0"  "Rplp1"  "Rplp2" 
## MECHANICAL STRESS
stress_symbols = readRDS(stress_file)
stress_symbols
Show output
 [1] "Atf3"     "Cmss1"    "Diaph1"   "Egr1"     "Fos"      "Gls"     
 [7] "Gm48099"  "lsp90aa1" "lsp90ab1" "lfrd1"    "Jak1"     "Junb"    
[13] "Kpna1"    "Nup210l"  "Peak1"    "Stat3"    "Actb"     "Camk1d"  
[19] "Chka"     "Clic4"    "Fodl2"    "Hspa8"    "Jun"      "Jund"    
[25] "Klf6"     "Litaf"    "Pecam1"   "Ptma"     "Sik3"     "Spag9"   
[31] "Arih1"    "Azin1"    "Brd2"     "Chd4"     "Ctnnb1"   "Dennd4a" 
[37] "Elf1"     "G3bp1"    "Ints6"    "Kdm6b"    "Lsmem1"   "Man1a"   
[43] "Med13"    "Nfkbia"   "Nfkbiz"   "Nop58"    "Piezo1"   "Ppp1r15a"
[49] "Prkcg"    "Sqstm1"   "Taf4b"    "Tmsb4x"   "Ubc"      "Zfp36"   
[55] "Abtb2"    "Adamts1"  "Adamts9"  "Ahnak"    "Ankrd28"  "Atp2a2"  
[61] "Baz1a"    "Btg2"     "Ccdc138"  "Cd44"     "Cdkn1a"   "Elf2"    
[67] "Ep400"    "Epas1"    "Erf"      "Ern1"     "Fabp4"    "Fosb"    
[73] "Gm10073"  "Gsk3a"    "H3f3a"    "H3f3b"    "Hivep2"   "Klf4"    
[79] "Lmna"     "lapkapk2" "Mapre1"   "Msn"      "Mylip"    "Nabp1"   
[85] "Ncl"      "Nsd3"     "Nufip2"   "Plekhg2"  "Ppp1cb"   "Rnf19b"  
[91] "Rps20"    "Rtn4"     "Runx1"    "Sertad2"  "Sptan1"   "Top1"    
[97] "Vcl"      "Zbtb11"  

We keep only the gene symbols available in our data :

# subset_symbols

mito_symbols = intersect(mito_symbols, rownames(sobj))
mito_symbols
Show output
 [1] "mt-Atp6" "mt-Atp8" "mt-Co1"  "mt-Co2"  "mt-Co3"  "mt-Cytb" "mt-Nd1" 
 [8] "mt-Nd2"  "mt-Nd3"  "mt-Nd4"  "mt-Nd4l" "mt-Nd5"  "mt-Nd6" 
ribo_symbols = intersect(ribo_symbols, rownames(sobj))
ribo_symbols
Show output
 [1] "Rpsa"   "Rps2"   "Rps3"   "Rps4x"  "Rps5"   "Rps6"   "Rps7"   "Rps8"  
 [9] "Rps9"   "Rps10"  "Rps11"  "Rps12"  "Rps13"  "Rps14"  "Rps15"  "Rps15a"
[17] "Rps16"  "Rps17"  "Rps18"  "Rps19"  "Rps20"  "Rps21"  "Rps23"  "Rps24" 
[25] "Rps25"  "Rps26"  "Rps27"  "Rps27a" "Rps28"  "Rps29"  "Rpl3"   "Rpl4"  
[33] "Rpl5"   "Rpl6"   "Rpl7"   "Rpl7a"  "Rpl8"   "Rpl9"   "Rpl10"  "Rpl10a"
[41] "Rpl11"  "Rpl12"  "Rpl13"  "Rpl13a" "Rpl14"  "Rpl15"  "Rpl17"  "Rpl18" 
[49] "Rpl18a" "Rpl19"  "Rpl21"  "Rpl22"  "Rpl23"  "Rpl23a" "Rpl24"  "Rpl26" 
[57] "Rpl27"  "Rpl27a" "Rpl28"  "Rpl29"  "Rpl30"  "Rpl31"  "Rpl32"  "Rpl34" 
[65] "Rpl35"  "Rpl35a" "Rpl36"  "Rpl37"  "Rpl37a" "Rpl38"  "Rpl39"  "Rpl41" 
[73] "Rplp0"  "Rplp1"  "Rplp2" 
stress_symbols = intersect(stress_symbols, rownames(sobj))
stress_symbols
Show output
 [1] "Atf3"     "Cmss1"    "Diaph1"   "Egr1"     "Fos"      "Gls"     
 [7] "Gm48099"  "Jak1"     "Junb"     "Kpna1"    "Nup210l"  "Peak1"   
[13] "Stat3"    "Actb"     "Camk1d"   "Chka"     "Clic4"    "Hspa8"   
[19] "Jun"      "Jund"     "Klf6"     "Litaf"    "Pecam1"   "Ptma"    
[25] "Sik3"     "Spag9"    "Arih1"    "Azin1"    "Brd2"     "Chd4"    
[31] "Ctnnb1"   "Dennd4a"  "Elf1"     "G3bp1"    "Ints6"    "Kdm6b"   
[37] "Lsmem1"   "Man1a"    "Med13"    "Nfkbia"   "Nfkbiz"   "Nop58"   
[43] "Piezo1"   "Ppp1r15a" "Prkcg"    "Sqstm1"   "Taf4b"    "Tmsb4x"  
[49] "Ubc"      "Zfp36"    "Abtb2"    "Adamts1"  "Adamts9"  "Ahnak"   
[55] "Ankrd28"  "Atp2a2"   "Baz1a"    "Btg2"     "Ccdc138"  "Cd44"    
[61] "Cdkn1a"   "Elf2"     "Ep400"    "Epas1"    "Erf"      "Ern1"    
[67] "Fabp4"    "Fosb"     "Gm10073"  "Gsk3a"    "H3f3a"    "H3f3b"   
[73] "Hivep2"   "Klf4"     "Lmna"     "Mapre1"   "Msn"      "Mylip"   
[79] "Nabp1"    "Ncl"      "Nsd3"     "Nufip2"   "Plekhg2"  "Ppp1cb"  
[85] "Rnf19b"   "Rps20"    "Rtn4"     "Runx1"    "Sertad2"  "Sptan1"  
[91] "Top1"     "Vcl"      "Zbtb11"  

Note: A more robust analysis may use the gene identifiers (like EntrezID) rather than gene symbols.

10 Quality controls

10.1 Compute metrics

What is already available in the Seurat object ?

# see_metadata

head(sobj@meta.data)
Show output
                   orig.ident nCount_RNA nFeature_RNA
AAACCTGAGACGCTTT.1       TD3A       2813         1594
AAACCTGAGGCATTGG.1       TD3A       2072         1365
AAACCTGGTCAACATC.1       TD3A       2025         1341
AAACCTGTCGAGGTAG.1       TD3A       1877         1241
AAACCTGTCGATCCCT.1       TD3A       2216         1441
AAACGGGCACTTACGA.1       TD3A       2445         1428

How do the two first QC metrics vary ?

# summary_metadata

summary(sobj@meta.data)
Show output
 orig.ident    nCount_RNA     nFeature_RNA 
 TD3A:4587   Min.   :  502   Min.   :  22  
             1st Qu.: 1986   1st Qu.:1291  
             Median : 2397   Median :1476  
             Mean   : 3573   Mean   :1638  
             3rd Qu.: 3134   3rd Qu.:1733  
             Max.   :48875   Max.   :5975  

In the column nCount_RNA, the maximum is far from the third quartile. For visualization purpose, we transform this column to log10 scale.

# log10_ncount_RNA

sobj$log10_nCount_RNA = log10(sobj$nCount_RNA)

summary(sobj@meta.data)
Show output
 orig.ident    nCount_RNA     nFeature_RNA  log10_nCount_RNA
 TD3A:4587   Min.   :  502   Min.   :  22   Min.   :2.701   
             1st Qu.: 1986   1st Qu.:1291   1st Qu.:3.298   
             Median : 2397   Median :1476   Median :3.380   
             Mean   : 3573   Mean   :1638   Mean   :3.429   
             3rd Qu.: 3134   3rd Qu.:1733   3rd Qu.:3.496   
             Max.   :48875   Max.   :5975   Max.   :4.689   

We compute the percentage of UMI related to each of the three list of genes.

First, we compute the proportion of transcripts related to mitochondrial genes, per cell.

# percent_mt

sobj = Seurat::PercentageFeatureSet(
  object = sobj,
  assay = "RNA",
  features = mito_symbols,
  col.name = "percent_mt")

summary(sobj$percent_mt)
Show output
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   1.985   2.489   2.997   3.125  97.250 

Then, we compute the proportion of transcripts related to riboprotein-coding genes, per cell:

# percent_rb

sobj = Seurat::PercentageFeatureSet(
  object = sobj,
  assay = "RNA",
  features = ribo_symbols,
  col.name = "percent_rb")

summary(sobj$percent_rb)
Show output
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   7.884   9.577  10.880  12.167  42.010 

Finally, we compute the proportion of transcripts related to mechanical stress signature, per cell :

# percent_st

sobj = Seurat::PercentageFeatureSet(
  sobj,
  assay = "RNA",
  features = stress_symbols,
  col.name = "percent_st")

summary(sobj$percent_st)
Show output
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   2.946   3.338   3.368   3.749  10.198 

We now have all the QC-related metrics:

# final_summary

summary(sobj@meta.data)
Show output
 orig.ident    nCount_RNA     nFeature_RNA  log10_nCount_RNA   percent_mt    
 TD3A:4587   Min.   :  502   Min.   :  22   Min.   :2.701    Min.   : 0.000  
             1st Qu.: 1986   1st Qu.:1291   1st Qu.:3.298    1st Qu.: 1.985  
             Median : 2397   Median :1476   Median :3.380    Median : 2.489  
             Mean   : 3573   Mean   :1638   Mean   :3.429    Mean   : 2.997  
             3rd Qu.: 3134   3rd Qu.:1733   3rd Qu.:3.496    3rd Qu.: 3.125  
             Max.   :48875   Max.   :5975   Max.   :4.689    Max.   :97.250  
   percent_rb       percent_st    
 Min.   : 0.000   Min.   : 0.000  
 1st Qu.: 7.884   1st Qu.: 2.946  
 Median : 9.577   Median : 3.338  
 Mean   :10.880   Mean   : 3.368  
 3rd Qu.:12.167   3rd Qu.: 3.749  
 Max.   :42.010   Max.   :10.198  

10.2 Failing cells

We identify the cells that do not pass the quality controls. This will be used for the visualization and later for cells filtering.

If the filtering thresholds are modified, do not forget to run again this chunk.

# fail_cells
fail_percent_mt <- rownames(sobj@meta.data)[sobj$percent_mt > cut_percent_mt]

fail_percent_rb <- rownames(sobj@meta.data)[sobj$percent_rb > cut_percent_rb]

fail_percent_st <- rownames(sobj@meta.data)[sobj$percent_st > cut_percent_st]

fail_nFeature_RNA <- rownames(sobj@meta.data)[sobj$nFeature_RNA < cut_nFeature_RNA]

fail_nCount_RNA <- rownames(sobj@meta.data)[sobj$nCount_RNA < cut_nCount_RNA]

10.3 Visualization

This is difficult to handle the distribution of these metrics across cells. We opt for various visualization ways:

  • histogram, showing the distribution of the metric
  • violin plot, showing the distribution of the metric, useful if several datasets are considered
  • UMAP (or alternatively, tSNE), showing the distribution of the metric over a 2D projection of cells

You may choose one of these visualization ways.

To generate the UMAP 2D projection, we need to run multiple Seurat commands in a row. Understanding these commands is not the purpose of the current course, but will be detailed in the next few ones. So just run :

# quick_processing

sobj_tmp = Seurat::NormalizeData(sobj, verbose = FALSE)
sobj_tmp = Seurat::ScaleData(sobj_tmp, verbose = FALSE)
sobj_tmp = Seurat::FindVariableFeatures(sobj_tmp, verbose = FALSE)
sobj_tmp = Seurat::RunPCA(sobj_tmp, npcs = 21, verbose = FALSE)
sobj_tmp = Seurat::RunUMAP(sobj_tmp, dims = c(1:20), verbose = FALSE)

We can now visualize the cells on a 2D projection:

# visualize_cells

Seurat::DimPlot(object = sobj_tmp,
                reduction = "umap") + Seurat::DarkTheme()
Show plot

10.3.1 Define a R function

We want to visualize our different metrics, but using the same kind of plots. Instead of copying-pasting and editing the same code multiple times (this is time-consuming and error-prone), we design a function using the template below:

# my_function_name

my_function_name = function(param1, param2) {
  # do something with the parameter values
  output = "something"
  
  return(output)
}

Here is the function we will use :

# qc_print_function

print_1_qc_metric = function(object = sobj,
                             qc = "log10_nCount_RNA",
                             cut_qc = cut_log10_nCount_RNA,
                             failing_cells = fail_nCount_RNA) {
  # Description of the parameters:
  # - sobj : the Seurat object, with default value to the one
  # - qc : CHARACTER : the QC metric, must be a column in sobj@meta.data
  # - cut_qc : NUMERIC : the filtering threshold for the QC metric
  # - failing_cells : CHARACTER VECTOR : the cells that fail the QC
  
  # Histogram
  p_hist = ggplot(object@meta.data, aes(x = .data[[qc]])) +
    geom_histogram(aes(y = after_stat(density)),
                            colour = "black", fill = "#F8766D", bins = 100) +
    geom_density(alpha = 0, col = "blue", lwd = 0.75) +
    geom_vline(xintercept = cut_qc, col = "red") +
    labs(title = paste0("Threshold for ", qc, " is: ", cut_qc)) +
    theme_classic() +
    theme(plot.title = element_text(hjust = 0.5))
  
  # Violin plot
  p_violin = Seurat::VlnPlot(object, features = qc) +
    geom_hline(yintercept = cut_qc, col = "red") +
    theme(axis.title.x = element_blank(),
                   legend.position = "none")
  
  # Feature plot
  p_umap = Seurat::FeaturePlot(object,
                               reduction = "umap",
                               features = qc) +
    scale_color_gradientn(colors = color_palette) +
    theme(aspect.ratio = 1)
  
  # Dim plot
  object$failorpass = as.factor(
    ifelse(test = colnames(object) %in% failing_cells,
           yes = "fail", 
           no = "pass")
    )

  p_fail = Seurat::DimPlot(object,
                           group.by = "failorpass",
                           order = "fail") +
    scale_color_manual(values = c("#F8766D", "gray80"),
                                breaks = levels(object$failorpass)) +
    labs(title = qc,
                  subtitle = paste0(length(failing_cells), " cells fail (",
                                    round(100*length(failing_cells)/ncol(sobj), 2), " %)")) +
    theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5),
                   plot.subtitle = element_text(hjust = 0.5))
  
  
  ## If we applied the filter
  objectf = subset(object, cells = colnames(object)[object$failorpass == 'pass'])
  objectf = Seurat::ScaleData(objectf, verbose = FALSE)
  objectf = Seurat::FindVariableFeatures(objectf, verbose = FALSE)
  objectf = Seurat::RunPCA(objectf, npcs = 21, verbose = FALSE)
  objectf = Seurat::RunUMAP(objectf, dims = c(1:20), verbose = FALSE)

  p_umapf = Seurat::FeaturePlot(object = objectf,
                               reduction = "umap",
                               features = qc) +
    scale_color_gradientn(colors = color_palette) +
    theme(aspect.ratio = 1)
  
  # Patchwork
  p = patchwork::wrap_plots(p_umap, p_fail, p_hist, p_violin, p_umapf) +
    patchwork::plot_layout(nrow = 1, widths = c(1, 1, 2, 1, 1))
  
  return(p)
}

10.3.2 Number of UMI

10.3.2.1 Check the function

This function should directly work for log10_nCount_RNA, as it is set for this metric by default (qc = "log10_nCount_RNA") :

# check1_log10_nCount_RNA

print_1_qc_metric(object = sobj_tmp)
Show plot

Of course, it will work the same by expliciting all of the parameters value:

# check2_log10_nCount_RNA

print_1_qc_metric(object = sobj_tmp,
                  qc = "log10_nCount_RNA",
                  cut_qc = cut_log10_nCount_RNA,
                  failing_cells = fail_nCount_RNA)
Show plot

So we can copy-paste only this chunk for the next QC metrics !

10.3.3 Number of expressed genes

# see_nFeature_RNA

print_1_qc_metric(object = sobj_tmp,
                  qc = "nFeature_RNA",
                  cut_qc = cut_nFeature_RNA,
                  failing_cells = fail_nFeature_RNA)
Show plot

10.3.4 Mitochondrial genes expression

# see_percent_mt

print_1_qc_metric(object = sobj_tmp,
                  qc = "percent_mt",
                  cut_qc = cut_percent_mt,
                  failing_cells = fail_percent_mt)
Show plot

10.3.5 Riboprotein-coding genes expression

# see_percent_rb

print_1_qc_metric(object = sobj_tmp,
                  qc = "percent_rb",
                  cut_qc = cut_percent_rb,
                  failing_cells = fail_percent_rb)
Show plot

10.3.6 Mechanical stress genes expression

# see_percent_st

print_1_qc_metric(object = sobj_tmp,
                  qc = "percent_st",
                  cut_qc = cut_percent_st,
                  failing_cells = fail_percent_st)
Show plot

As we have finished with the visualization, we can discard our temporary object :

# rm_sobj_tmp

rm(sobj_tmp)

10.3.7 All metrics effect

We can visualize metrics filtering effect as an upset-plot

# bcupset

## Create a list of all cells filtered OUT for each criterion
up_list <-list(
  "nFeature" = fail_nFeature_RNA,
  "nCount" = fail_nCount_RNA,
  "%MITO" = fail_percent_mt,
  "%RIBO" = fail_percent_rb,
  "%STRESS" = fail_percent_st)

## Create an upset-plot
UpSetR::upset(data = UpSetR::fromList(up_list), 
              nintersects = NA, 
              sets = rev(names(up_list)),
              keep.order = TRUE,
              order.by = "freq")
Show plot

11 Filtering ?

We could filter out cells based on these 5 QC metrics now. It is also possible to wait, perform various annotations such as cell type annotation or cell cycle phase scoring, to better characterize the low quality cells.

To filter a Seurat object, we use the Seurat::subset() function:

# filter_sobj

sobj_filtered = subset(sobj, invert = TRUE,
                       cells = unique(c(fail_nCount_RNA, 
                                        fail_nFeature_RNA,
                                        fail_percent_mt, 
                                        # fail_percent_rb, actually we decided not to filter on this parameter
                                        fail_percent_st
                                        )))

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

We are not going to save this filtered Seurat object, but we want to store in our sobj Seurat object the cells that failed the QC as a new cell metadata :

# add_fail_qc

sobj$fail_qc = ifelse(test = colnames(sobj) %in% colnames(sobj_filtered),
                      yes = "pass",
                      no = "fail")

table(sobj$fail_qc)
Show output

fail pass 
 309 4278 

12 Save

We save the unfiltered Seurat object:

# saverds1

## Save our Seurat object (rich naming)
out_name <- paste0(
          output_dir, "/", paste(
            c("02", Seurat::Project(sobj), "S5", 
              "Metrics", paste(
                dim(sobj), 
                collapse = '.'
              )
            ), collapse = "_"),
            ".RDS")

## Check
print(out_name)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/02_Preproc.2/RESULTS/02_TD3A_S5_Metrics_31053.4587.RDS"
## Write on disk
saveRDS(object = sobj, 
        file = out_name)

This Seurat object would then be loaded as the input for further analyses.

13 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] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] Seurat_5.3.0       SeuratObject_5.1.0 sp_2.2-0           ggplot2_3.5.2     

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      htmltools_0.5.8.1          
 [15] S4Arrays_1.4.1              SparseArray_1.4.8          
 [17] sass_0.4.10                 sctransform_0.4.2          
 [19] parallelly_1.45.0           KernSmooth_2.23-24         
 [21] bslib_0.9.0                 htmlwidgets_1.6.4          
 [23] ica_1.0-3                   plyr_1.8.9                 
 [25] plotly_4.10.4               zoo_1.8-14                 
 [27] cachem_1.1.0                igraph_2.1.4               
 [29] mime_0.13                   lifecycle_1.0.4            
 [31] pkgconfig_2.0.3             Matrix_1.7-3               
 [33] R6_2.6.1                    fastmap_1.2.0              
 [35] GenomeInfoDbData_1.2.12     MatrixGenerics_1.16.0      
 [37] fitdistrplus_1.2-2          future_1.49.0              
 [39] shiny_1.10.0                digest_0.6.37              
 [41] patchwork_1.3.0             S4Vectors_0.42.1           
 [43] tensor_1.5                  RSpectra_0.16-2            
 [45] irlba_2.3.5.1               GenomicRanges_1.56.2       
 [47] beachmat_2.20.0             labeling_0.4.3             
 [49] progressr_0.15.1            spatstat.sparse_3.1-0      
 [51] httr_1.4.7                  polyclip_1.10-7            
 [53] abind_1.4-8                 compiler_4.4.1             
 [55] withr_3.0.2                 BiocParallel_1.38.0        
 [57] UpSetR_1.4.0                fastDummies_1.7.5          
 [59] MASS_7.3-65                 DelayedArray_0.30.1        
 [61] tools_4.4.1                 vipor_0.4.7                
 [63] lmtest_0.9-40               beeswarm_0.4.0             
 [65] httpuv_1.6.15               future.apply_1.11.3        
 [67] goftest_1.2-3               glue_1.8.0                 
 [69] nlme_3.1-165                promises_1.3.2             
 [71] grid_4.4.1                  Rtsne_0.17                 
 [73] cluster_2.1.6               reshape2_1.4.4             
 [75] generics_0.1.4              gtable_0.3.6               
 [77] spatstat.data_3.1-6         rmdformats_1.0.4           
 [79] tidyr_1.3.1                 data.table_1.17.4          
 [81] XVector_0.44.0              BiocGenerics_0.50.0        
 [83] spatstat.geom_3.4-1         RcppAnnoy_0.0.22           
 [85] ggrepel_0.9.6               RANN_2.6.2                 
 [87] pillar_1.10.2               stringr_1.5.1              
 [89] spam_2.11-1                 RcppHNSW_0.6.0             
 [91] later_1.4.2                 splines_4.4.1              
 [93] dplyr_1.1.4                 lattice_0.22-6             
 [95] survival_3.7-0              deldir_2.0-4               
 [97] tidyselect_1.2.1            SingleCellExperiment_1.26.0
 [99] miniUI_0.1.2                scuttle_1.14.0             
[101] pbapply_1.7-2               knitr_1.50                 
[103] gridExtra_2.3               bookdown_0.39              
[105] IRanges_2.38.1              SummarizedExperiment_1.34.0
[107] scattermore_1.2             stats4_4.4.1               
[109] xfun_0.52                   Biobase_2.64.0             
[111] matrixStats_1.5.0           UCSC.utils_1.0.0           
[113] stringi_1.8.7               lazyeval_0.2.2             
[115] yaml_2.3.10                 evaluate_1.0.3             
[117] codetools_0.2-20            tibble_3.2.1               
[119] cli_3.6.5                   uwot_0.2.3                 
[121] xtable_1.8-4                reticulate_1.42.0          
[123] jquerylib_0.1.4             GenomeInfoDb_1.40.1        
[125] dichromat_2.0-0.1           Rcpp_1.0.14                
[127] globals_0.18.0              spatstat.random_3.4-1      
[129] png_0.1-8                   ggrastr_1.0.2              
[131] spatstat.univar_3.1-3       parallel_4.4.1             
[133] dotCall64_1.2               sparseMatrixStats_1.16.0   
[135] listenv_0.9.1               viridisLite_0.4.2          
[137] scales_1.4.0                ggridges_0.5.6             
[139] purrr_1.0.4                 crayon_1.5.3               
[141] rlang_1.1.6                 cowplot_1.1.3              
LS0tCnRpdGxlOiAiPENFTlRFUj5FQjNJIG4xIDIwMjUgc2NSTkFzZXE8QlI+LTxCUj4gPEI+UFJFLVBST0NFU1NJTkcgKElJKTwvQj48QlI+LTxCUj5RdWFsaXR5IGNvbnRyb2wgOiBDb3VudHMgJiBtZXRyaWNzPC9DRU5URVI+IgpkYXRlOiAiMjAyNS0xNi0yMS4yMiIKYXV0aG9yOgogIC0gbmFtZTogIkVCM0kgbjEgc2NSTkFzZXEgVGVhbSIKICAtIG5hbWU6ICJMaWxpYSBZT1VOU0kiCiAgICBlbWFpbDogImxpbGlhLnlvdW5zaUBpbnNlcm0uZnIiCiAgLSBuYW1lOiAiU2ViYXN0aWVuIE1FTExBIgogICAgZW1haWw6ICJzZWJhc3RpZW4ubWVsbGFAcGFzdGV1ci5mciIKb3V0cHV0OgogIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOgogICAgZmlnX3dpZHRoOiA4CiAgICBmaWdfaGVpZ2h0OiA2CiAgICBoaWdobGlnaHQ6IHRhbmdvICAjIyBUaGVtZSBmb3IgdGhlIGNvZGUgY2h1bmtzCiAgICBlbWJlZF9mb250czogVFJVRQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlICAjIyBBZGRzIG51bWJlciB0byBoZWFkZXJzIChzZWN0aW9ucykKICAgIHRoZW1lOiBmbGF0bHkgICMjIENTUyB0aGVtZSBmb3IgdGhlIEhUTUwgcGFnZQogICAgY29sbGFwc2VkOiB0cnVlICAjIyBCeSBkZWZhdWx0LCB0aGUgVE9DIGlzIGZvbGRlZAogICAgdG9jX2RlcHRoOiAzCiAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlICMjIFNtb290aCBzY3JvbGwgb2YgdGhlIEhUTUwgcGFnZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUgIyMgSW5jbHVkZXMgYWxsIHBsb3RzL2ltYWdlcyB3aXRoaW4gdGhlIEhUTUwKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUgIyMgQWRkcyBhIGJ1dHRvbiB0byBkb3dubG9hZCB0aGUgUm1kCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIHRodW1ibmFpbHM6IGZhbHNlCiAgICBsaWdodGJveDogdHJ1ZQogICAgZmlnX2NhcHRpb246IGZhbHNlCiAgICBnYWxsZXJ5OiB0cnVlCiAgICB1c2VfYm9va2Rvd246IHRydWUKYWx3YXlzX2FsbG93X2h0bWw6IHRydWUgIyMgQWxsb3cgcGxhaW4gSFRNTCBjb2RlIGluIHRoZSBSbWQKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKPCEtLSBrbml0IHNldHVwIC0tPgoKYGBge3Iga25pdF9zZXR1cCwgZWNobyA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsICAgICAgICAjIFByaW50IHRoZSBjb2RlCiAgZXZhbCA9IFRSVUUsICAgICAgICAjIFJ1biBjb21tYW5kIGxpbmVzCiAgbWVzc2FnZSA9IEZBTFNFLCAgICAjIFByaW50IG1lc3NhZ2VzCiAgcHJvbXB0ID0gRkFMU0UsICAgICAjIERvIG5vdCBkaXNwbGF5IHByb21wdAogIGNvbW1lbnQgPSBOQSwgICAgICAgIyBObyBjb21tZW50cyBvbiB0aGlzIHNlY3Rpb24KICB3YXJuaW5nID0gRkFMU0UsICAgICMgRGlzcGxheSB3YXJuaW5ncwogIHRpZHkgPSBGQUxTRSwKICBmaWcuYWxpZ249ImNlbnRlciIsIAogICMgcmVzdWx0cyA9ICdoaWRlJywKICB3aWR0aCA9IDEwMCAgICAgICAjIE51bWJlciBvZiBjaGFyYWN0ZXJzIHBlciBsaW5lCikKYGBgCgoKPCEtLSBDU1MgdG8gY29sb3IgY2h1bmtzIGFuZCBvdXRwdXRzIC0tPgoKCmBgYHtjc3MsIGVjaG89RkFMU0V9Ci5ub3RydW4gewogIGJhY2tncm91bmQtY29sb3I6IGxpZ2h0Z3JleSAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGJsYWNrICFpbXBvcnRhbnQ7Cn0KLm5vdHJ1bm8gewogIGJhY2tncm91bmQtY29sb3I6IGxpZ2h0Z3JleSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQoucXVlc3Rpb24gewogIGJhY2tncm91bmQtY29sb3I6IGFxdWFtYXJpbmUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgbGltZWdyZWVuICFpbXBvcnRhbnQ7Cn0KLnF1ZXN0aW9ubyB7CiAgYmFja2dyb3VuZC1jb2xvcjogYXF1YW1hcmluZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQouYW5zd2VyIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBuYXZham93aGl0ZSAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGJyb3duICFpbXBvcnRhbnQ7Cn0KLmFuc3dlcm8gewogIGJhY2tncm91bmQtY29sb3I6IG5hdmFqb3doaXRlICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9Ci5iZXlvbmQgewogIGJhY2tncm91bmQtY29sb3I6IHZpb2xldCAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIHB1cnBsZSAhaW1wb3J0YW50Owp9Ci5iZXlvbmRvIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB2aW9sZXQgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KYGBgCgoKPCEtLSBIb29rIHRvIGhhbmRsZSBjb2RlIGJsb2NrcyBvdXRwdXQgZm9sZGluZyAtLT4KCmBgYHtyIGtuaXRfaG9vaywgZWNobyA9IEZBTFNFfQpob29rcyA9IGtuaXRyOjprbml0X2hvb2tzJGdldCgpCmhvb2tfZm9sZGFibGUgPSBmdW5jdGlvbih0eXBlKSB7CiAgZm9yY2UodHlwZSkKICBmdW5jdGlvbih4LCBvcHRpb25zKSB7CiAgICByZXMgPSBob29rc1tbdHlwZV1dKHgsIG9wdGlvbnMpCiAgICAKICAgIGlmIChpc0ZBTFNFKG9wdGlvbnNbW3Bhc3RlMCgiZm9sZC4iLCB0eXBlKV1dKSkgcmV0dXJuKHJlcykKICAgIAogICAgcGFzdGUwKAogICAgICAiPGRldGFpbHM+PHN1bW1hcnk+U2hvdyAiLCB0eXBlLCAiPC9zdW1tYXJ5PlxuXG4iLAogICAgICByZXMsCiAgICAgICJcblxuPC9kZXRhaWxzPiIKICAgICkKICB9Cn0Ka25pdHI6OmtuaXRfaG9va3Mkc2V0KAogIG91dHB1dCA9IGhvb2tfZm9sZGFibGUoIm91dHB1dCIpLAogIHBsb3QgPSBob29rX2ZvbGRhYmxlKCJwbG90IikKKQpgYGAKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGNlbnRlcj4hW10oZWIzaV9iYW5uZXIucG5nKTwvY2VudGVyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUFJFQU1CTEUKCiMjIFB1cnBvc2Ugb2YgdGhpcyBzZXNzaW9uCgpUaGlzIGZpbGUgZGVzY3JpYmVzIHRoZSBkaWZmZXJlbnQgc3RlcHMgdG8gcGVyZm9ybSB0aGUgcXVhbGl0eSBjb250cm9sIG9mCmEgc2luZ2xlIGNlbGwgZGF0YXNldCwgYmFzZWQgb24gKHBlciBjZWxsKSA6CgoqICAgdGhlIG51bWJlciBvZiAqKlVNSSoqICh+dHJhbnNjcmlwdHMpIGRldGVjdGVkCiogICB0aGUgbnVtYmVyIG9mICoqZXhwcmVzc2VkIGdlbmVzKiogZGV0ZWN0ZWQKKiAgIHRoZSBwcm9wb3J0aW9uIG9mIFVNSSBjb3JyZXNwb25kaW5nIHRvICoqbWl0b2Nob25kcmlhbCoqIGdlbmVzCiogICB0aGUgcHJvcG9ydGlvbiBvZiBVTUkgY29ycmVzcG9uZGluZyB0byAqKnJpYm9wcm90ZWluLWNvZGluZyoqIGdlbmVzCiogdGhlIHByb3BvcnRpb24gb2YgdHJhbnNjcmlwdHMgcmVsYXRlZCB0byBzdHJlc3Mgc2lnbmF0dXJlCgoqKklucHV0IGRhdGEqKjogQSBmaWx0ZXJlZCwgZmxhdCBjb3VudCBtYXRyaXggcmV0cmlldmVkIGZyb20gR0VPLgoKKipPdXRwdXQgZGF0YSoqOiBBIGBTZXVyYXRgIG9iamVjdCBhbm5vdGF0ZWQgZm9yIHRoZSAiYmFkIiBjZWxscyB0byBmaWx0ZXIgb3V0IGZvciBkb3duc3RyZWFtIGFuYWx5c2lzLgoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFN0YXJ0IFJzdHVkaW8KCi0gICBVc2luZyB0aGUgW09wZW5PbkRlbWFuZC9Sc3R1ZGlvIGNoZWF0CiAgICBzaGVldF0oaHR0cHM6Ly9tb29kbGUuZnJhbmNlLWJpb2luZm9ybWF0aXF1ZS5mci9wbHVnaW5maWxlLnBocC8xNDc1L21vZF9mb2xkZXIvY29udGVudC8wL09vRF9SX1JzdHVkaW8uaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSwKICAgIGNvbm5lY3QgdG8gdGhlIFtPcGVuT25EZW1hbmQKICAgIHBvcnRhbF0oaHR0cHM6Ly9vbmRlbWFuZC5jbHVzdGVyLmZyYW5jZS1iaW9pbmZvcm1hdGlxdWUuZnIpe3RhcmdldD0iX2JsYW5rIn0gYW5kCiAgICAqKmNyZWF0ZSBhIFJzdHVkaW8gc2Vzc2lvbioqIHdpdGggdGhlIHJpZ2h0IHJlc291cmNlIHJlcXVpcmVtZW50cywgdGhhbmtzIHRvIHRoZSBjaGVhdCBzaGVldC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFdhcm0tdXAKCi0gICBXZSBub3cgc2V0ICoqY29tbW9uIHBhcmFtZXRlcnMqKiBhcyBuZXcgdmFyaWFibGVzLCBvbmNlIGFuZCBmb3IgYWxsIGZvciB0aGlzCnNlc3Npb24gOgoKYGBge3Igc2V0cGFyYW19CiMgc2V0cGFyYW0KCgojIyBTZXQgeW91ciBwcm9qZWN0IG5hbWUKIyBXQVJOSU5HIDogRG8gbm90IGp1c3QgY29weS1wYXN0ZSB0aGlzICEgSXQncyBNWSBwcm9qZWN0IG5hbWUgISBQdXQgWU9VUlMgISEKcHJvamVjdF9uYW1lIDwtICJlYmFpaV9zY190ZWFjaGVycyIKCgojIyBDb250cm9sIGlmIHRoZSBwcm9qZWN0X25hbWUgZXhpc3RzIG9uIHRoZSBjbHVzdGVyCmNhdCgnUEFUSCBDSEVDSyA6ICcsIGRpci5leGlzdHMocGFzdGUwKCcvc2hhcmVkL3Byb2plY3RzLycsIHByb2plY3RfbmFtZSkpKQoKIyMgU2VlZCBmb3IgdGhlIFJORwpteV9zZWVkIDwtIDEzMzdMCgojICMjIEVtcHR5IGRyb3BsZXRzIG1heCBwLXZhbHVlCiMgbWF4X3AgPC0gMUUtMDMKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUHJlcGFyZSB0aGUgZGF0YSBzdHJ1Y3R1cmUgCgojIyBNYWluIGRpcmVjdG9yeQoKYGBge3IgbWFpbmRpcn0KIyBtYWluZGlyCgojIyBQcmVwYXJpbmcgdGhlIHBhdGgKVERfZGlyIDwtIHBhc3RlMCgiL3NoYXJlZC9wcm9qZWN0cy8iLCBwcm9qZWN0X25hbWUsICIvU0NfVEQiKQoKIyMgQ3JlYXRpbmcgdGhlIHJvb3QgZGlyZWN0b3J5CiMgZGlyLmNyZWF0ZShwYXRoID0gVERfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIHJvb3QgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChURF9kaXIpCgpgYGAKCiMjIEN1cnJlbnQgc2Vzc2lvbgoKYGBge3Igc2Vzc2lvbmRpcn0KIyBzZXNzaW9uZGlyCgojIyBDcmVhdGluZyB0aGUgc2Vzc2lvbiAoUHJlcHJvYy4yKSBkaXJlY3RvcnkKc2Vzc2lvbl9kaXIgPC0gcGFzdGUwKFREX2RpciwgIi8wMl9QcmVwcm9jLjIiKQpkaXIuY3JlYXRlKHBhdGggPSBzZXNzaW9uX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSBzZXNzaW9uIGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoc2Vzc2lvbl9kaXIpCgpgYGAKCiMjIElucHV0IGRpcmVjdG9yeQoKYGBge3IgaW5kaXJ9CiMgaW5kaXIKCiMjIENyZWF0aW5nIHRoZSBJTlBVVCBkYXRhIGRpcmVjdG9yeQppbnB1dF9kaXIgPC0gcGFzdGUwKHNlc3Npb25fZGlyLCAiL0RBVEEiKQpkaXIuY3JlYXRlKHBhdGggPSBpbnB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgaW5wdXQgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChpbnB1dF9kaXIpCgpgYGAKCiMjIEdlbmVsaXN0cyBkaXJlY3RvcnkKClRoaXMgaXMgYSBkaXJlY3Rvcnkgd2hlcmUgd2Ugd2lsbCBzdG9yZSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGZyb20gCmtub3dsZWRnZSBiYXNlcyBhYm91dCBnZW5lcyB1c2VkIHRvIGVzdGltYXRlIHRoZSBjZWxsIGN5Y2xlIHBoYXNlIG9mIGNlbGxzLgoKYGBge3IgcmVzZGlyfQojIHJlc2RpcgoKcmVzX2RpciA8LSBwYXN0ZTAoVERfZGlyLCAiL1Jlc291cmNlcyIpCmdsaXN0X2RpciA8LSBwYXN0ZTAocmVzX2RpciwgIi9HZW5lbGlzdHMiKQoKIyMgQ3JlYXRlIHRoZSBkaXJlY3RvcnkKZGlyLmNyZWF0ZShwYXRoID0gZ2xpc3RfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIHJlc291cmNlcyBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KGdsaXN0X2RpcikKCmBgYAoKIyMgT3V0cHV0IGRpcmVjdG9yeQoKYGBge3Igb3V0ZGlyfQojIG91dGRpcgoKIyMgQ3JlYXRpbmcgdGhlIE9VVFBVVCBkYXRhIGRpcmVjdG9yeQpvdXRwdXRfZGlyIDwtIHBhc3RlMChzZXNzaW9uX2RpciwgIi9SRVNVTFRTIikKZGlyLmNyZWF0ZShwYXRoID0gb3V0cHV0X2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSBvdXRwdXQgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChvdXRwdXRfZGlyKQoKYGBgCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUHJlcmVxdWlzaXRlcwoKV2UgaGF2ZSB0byByZXRyaWV2ZSB0aGUgaW5wdXQgZGF0YSBmaWxlCgpgYGB7ciBtYXRfZGx9CiMgbWF0X2RsCgpsb2NhbCA8LSBGQUxTRQoKIyMgVGhlIHJhdyBjb3VudCBtYXRyaXggd2Ugd2lsbCBzdGFydCBmcm9tCnNjbWF0X3NvdXJjZSA8LSAiR1NNNDg2MTE5NF9nZXhfMl9yYXdfZ2VuZV9leHByZXNzaW9uLnRzdi5neiIKCiMjIERvd25sb2FkIHRoZSBmaWxlIGZyb20gWmVub2RvCmlmICghbG9jYWwpIHsKICAKICAjIyMgWmVuSUQKICB6ZW5faWQgPC0gIjE0MDMzOTQxIgogICMjIyBaZW4gUGF0aAogIHplbl9iYWNrdXBfZmlsZSA8LSBwYXN0ZTAoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmRzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ZW5faWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiL2ZpbGVzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY21hdF9zb3VyY2UpCiAgCiAgIyMgVGhlIHBhdGggdG8gdGhlIGxvY2FsbHkgc2F2ZWQgaW5wdXQgZmlsZQogIHNjbWF0X2ZpbGUgPC0gcGFzdGUwKGlucHV0X2RpciwKICAgICAgICAgICAgICAgICAgICAgICAnLycsCiAgICAgICAgICAgICAgICAgICAgICAgc2NtYXRfc291cmNlKQogICMjIERvd25sb2FkIHRoZSBmaWxlCiAgZG93bmxvYWQuZmlsZSh1cmwgPSB6ZW5fYmFja3VwX2ZpbGUsCiAgICAgICAgICAgICAgICBkZXN0ZmlsZSA9IHNjbWF0X2ZpbGUpCn0gZWxzZSB7CiAgZWJhaWlfc2Vzc2lvbiA8LSAnMjUzOF9lYjNpX24xXzIwMjUnCiAgc2NtYXRfZmlsZSA8LSBwYXN0ZTAoCiAgICAgICcvc2hhcmVkL3Byb2plY3RzLycsCiAgICAgIGViYWlpX3Nlc3Npb24sCiAgICAgICcvYXRlbGllcl9zY3JuYXNlcS9URC9CQUNLVVAvVFNWLycsCiAgICAgIHNjbWF0X3NvdXJjZSkKfQpgYGAKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBMb2FkIHRoZSBnZW5lbGlzdHMgcmVzb3VyY2VzIAoKV2UgcmV0cmlldmUgdGhlIGdlbmVsaXN0cwoKYGBge3IgZ2xfZGx9CiMgZ2xfZGwKCmxvY2FsIDwtIEZBTFNFCgojIyBUaGUgZ2VuZWxpc3QgZmlsZXMKbWl0b19zb3VyY2UgPC0gIm11c19tdXNjdWx1c19taXRvX3N5bWJvbHNfMjAxOTEwMTUucmRzIgpyaWJvX3NvdXJjZSA8LSAibXVzX211c2N1bHVzX2NyaWJvX3N5bWJvbHNfMjAxOTEwMTUucmRzIgpzdHJlc3Nfc291cmNlIDwtICJtdXNfbXVzY3VsdXNfc3RyZXNzX3N5bWJvbHNfMjAyMDAyMjQucmRzIgpnbF9zb3VyY2VzIDwtIGMobWl0b19zb3VyY2UsIHJpYm9fc291cmNlLCBzdHJlc3Nfc291cmNlKQoKIyMgVGhlIChmdXR1cmUpIGxvY2FsIGZpbGVzCm1pdG9fZmlsZSA8LSBwYXN0ZTAoaW5wdXRfZGlyLCAnLycsIG1pdG9fc291cmNlKQpyaWJvX2ZpbGUgPC0gcGFzdGUwKGlucHV0X2RpciwgJy8nLCByaWJvX3NvdXJjZSkKc3RyZXNzX2ZpbGUgPC0gcGFzdGUwKGlucHV0X2RpciwgJy8nLCBzdHJlc3Nfc291cmNlKQpnbF9maWxlcyA8LSBjKG1pdG9fZmlsZSwgcmlib19maWxlLCBzdHJlc3NfZmlsZSkKCiMjIERvd25sb2FkIHRoZSBmaWxlIGZyb20gWmVub2RvCmlmICghbG9jYWwpIHsKICAKICAjIyMgWmVuSUQKICB6ZW5faWQgPC0gIjE0MDM3MzU1IgogICMjIyBMb29waW5nIG9uIGZpbGVzCiAgZm9yIChnbGYgaW4gc2VxX2Fsb25nKGdsX3NvdXJjZXMpKSB7CiAgICAjIyMgWmVuIFBhdGgKICAgIHplbl9iYWNrdXBfZmlsZSA8LSBwYXN0ZTAoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmRzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHplbl9pZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi9maWxlcy8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnbF9zb3VyY2VzW2dsZl0pCiAgICAKICAgICMjIERvd25sb2FkIHRoZSBmaWxlCiAgICBkb3dubG9hZC5maWxlKHVybCA9IHplbl9iYWNrdXBfZmlsZSwKICAgICAgICAgICAgICAgICAgZGVzdGZpbGUgPSBnbF9maWxlc1tnbGZdKQogIH0KICBybShnbF9maWxlcykKfSBlbHNlIHsgICMjIExvY2FsIG1vZGUKICBlYmFpaV9zZXNzaW9uIDwtICcyNTM4X2ViM2lfbjFfMjAyNScKICBsb2NhbGJhY2t1cF9kaXIgPC0gcGFzdGUwKCcvc2hhcmVkL3Byb2plY3RzLycsIGViYWlpX3Nlc3Npb24sICcvYXRlbGllcl9zY3JuYXNlcS9URC9SRVNPVVJDRVMvR0VORUxJU1RTLycpCiAgbWl0b19maWxlIDwtIHBhc3RlMChsb2NhbGJhY2t1cF9kaXIsICcvbXVzX211c2N1bHVzX21pdG9fc3ltYm9sc18yMDE5MTAxNS5yZHMnKQogIHJpYm9fc291cmNlIDwtIHBhc3RlMChpbnB1dF9kaXIsICcvbXVzX211c2N1bHVzX2NyaWJvX3N5bWJvbHNfMjAxOTEwMTUucmRzJykKICBzdHJlc3Nfc291cmNlIDwtIHBhc3RlMChpbnB1dF9kaXIsICcvbXVzX211c2N1bHVzX3N0cmVzc19zeW1ib2xzXzIwMjAwMjI0LnJkcycpCn0KYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBCaW9sb2dpY2FsIGNvbnRleHQKCkZvciB0aGlzIHNlcmllcyBvZiB0cmFpbmluZyBzZXNzaW9ucywgd2Ugd2lsbCB3b3JrIG9uIGRhdGEgZnJvbSB0aGlzIFtQYWl2YSBldCBhbC5dKGh0dHBzOi8vd3d3LnNjaWVuY2VkaXJlY3QuY29tL3NjaWVuY2UvYXJ0aWNsZS9waWkvUzIyMTExMjQ3MjEwMDI4MTMpIHB1YmxpY2F0aW9uLgoKKiBUaGUgc3R1ZHkgY29uY2VybnMgKip0aHltdXMgYXV0b25vbXkqKjoKCiAgKiBUaGUgdGh5bXVzIGlzIGFuICJvcmdhbiBvZiBwYXNzYWdlIiwgY3JpdGljYWwgaW4gaXRzIGZ1bmN0aW9uIHRvIHRoZSBhZGFwdGl2ZSBpbW11bmUgc3lzdGVtIGZvciB0aGUgbWF0dXJhdGlvbiBvZiBUIGNlbGwgbHltcGhvY3l0ZXMuCiAgKiBUaGlzIG1hdHVyYXRpb24gaW52b2x2ZXMgdHdvIG1haW4gc3RlcHMsIHBlcmZvcm1lZCB0aGFua3MgdG8gbWFjcm9waGFnZXM6CiAgICAqIFBvc2l0aXZlIHNlbGVjdGlvbiA6IGtlZXBpbmcgY2VsbHMgdGhhdCBzdWNjZXNzZnVsbHkgZGV2ZWxvcCByZWFjdCBhcHByb3ByaWF0ZWx5IHdpdGggTUhDIGltbXVuZSByZWNlcHRvcnMgb2YgdGhlIGJvZHkKICAgICogTmVnYXRpdmUgc2VsZWN0aW9uIDoga2VlcGluZyBjZWxscyB0aGF0IGRvIG5vdCByZWFjdCBhZ2FpbnN0IG5hdHVyYWwgcHJvdGVpbnMgb2YgdGhlIGJvZHkuCiAgKiBUaHltdXMgX2F1dG9ub215XyBpcyBhIG5hdHVyYWwgbWVjaGFuaXNtIHRoYXQgYWxsb3dzIHRvIGNyZWF0ZSBUIGNlbGxzIGluIHRoZSB0aHltdXMgYnkgZGlmZmVyZW50aWF0aW9uIGFuZCBjZWxsIGNvbXBldGl0aW9uLCBldmVuIHdoZW4gbm9ybWFsIHByb2dlbml0b3JzIGZyb20gdGhlIGJvbmUgbWFycm93IGFyZSBsYWNraW5nLCBpbiBjcml0aWNhbCBjb25kaXRpb25zLgogICogVGhpcyBtZWNoYW5pc20gaXMga25vd24gaW4gaXRzIGVmZmVjdHMsIGJ1dCB0aGUgY2VsbHMgaW52b2x2ZWQgaW4gYXJlIG5vdC4KICAqIFRoaXMgc3R1ZHkgaXMgb2YgaW1wb3J0YW5jZSBpbiB0aGUgaGVhbHRoIGZpZWxkLCBhcyB0aGlzIG1lY2hhbmlzbSByZWxpZXMgb24gYSB0ZW1wb3JhcnkgbG9zcyBvZiBjb250cm9sIG9mIHRoZSBjZWxsIG5vcm1hbCBmdW5jdGlvbnMuCiAgKiBUaGUgY29uc2VxdWVuY2UgaXMgdGhhdCBpZiB0aHltdXMgaXMgaW4gYXV0b25vbXkgZm9yIHRvbyBsb25nIChmZXcgd2Vla3MpLCB0aGlzIGlzIGEgcHJlbHVkZSBmb3IgbGV1a2VtaWEgIQoKPGJyPgoKPGNlbnRlcj4hW10odGh5bXVzX2F1dG9ub215LnBuZyk8L2NlbnRlcj4KCjxicj4KCiogT3JnYW5pc20gaXMgOiAqKm11cyBtdXNjdWx1cyoqCiogSW5kaXZpZHVhbHMgYXJlIDogbmV3Ym9ybiAqKm1pY2UsIGdyYWZ0ZWQqKgoqIFRoZSBkZXNpZ24gY29ycmVzcG9uZHMgdG8gKip0d28gY29uZGl0aW9ucyoqIChUZXN0IC8gQ29udHJvbCkKICAqICoqQ29udHJvbCoqIDogdGh5bXVzIGZyb20gKip3aWxkIHR5cGUqKiBuZXdib3JuIG1pY2UgdHJhbnNwbGFudGVkIGludG8gKip3aWxkIHR5cGUqKiBqdXZlbmlsZSBtb3VzZS4gSW4gdGhpcyBjb250cm9sIGNhc2UsICoqZG9ub3IqKiBULWNlbGxzIHByb2dlbml0b3JzIChETjMpIHdlcmUgcmVwbGFjZWQgYnkgKipob3N0KiogY2VsbHMgKiozIHdlZWtzKiogYWZ0ZXIgdHJhbnNwbGFudGF0aW9uLgogICogKipUZXN0KiogOiB0aHltdXMgZnJvbSAqKndpbGQgdHlwZSoqIG5ld2Jvcm4gbWljZSB0cmFuc3BsYW50ZWQgaW4gKipLTyBSYWctLy0qKiB0eXBlIGp1dmVuaWxlIG1vdXNlICh0aGUgS08gcGFydGlhbGx5IGltcGFpcnMgdGhlaXIgYWJpbGl0eSB0byBwcm9kdWNlIFQtY2VsbCBwcm9nZW5pdG9ycyBpbiBub3JtYWwgYW1vdW50cykuIEluIHRoaXMgdGVzdCBjYXNlLCAqKmRvbm9yKiogVC1jZWxscyBwcm9nZW5pdG9ycyAoRE4zKSB3ZXJlIHJlcGxhY2VkIGJ5ICoqaG9zdCoqIGNlbGxzICoqOSB3ZWVrcyoqIGFmdGVyIHRyYW5zcGxhbnRhdGlvbiwgc2hvd2luZyB0aGF0IHRoZSBkb25vciBETjMgY2VsbHMgb3V0bGl2ZWQgdGhlaXIgbm9ybWFsIGxpZmVzcGFuIGJ5IH42IHdlZWtzLgogIAogIDxjZW50ZXI+IVtdKHBhaXZhX3d0X2tvLnBuZyk8L2NlbnRlcj4KICAKWW91IHdpbGwgbWFpbmx5IHdvcmsgb24gdGhlICoqS08gc2FtcGxlIDogYFREM0FgKiouIAoKVGhlIGlucHV0IGRhdGEgY29uc2lzdHMgaW4gYSAqKmNvdW50IG1hdHJpeCoqLCBhcyBhIGd6aXBwZWQgdGFidWxhciB0ZXh0IGZpbGUsIHRoYXQgY29udGFpbnMgZXZlcnl0aGluZyBuZWVkZWQgdG8gY3JlYXRlIGEgYmFzaWMgU2V1cmF0IG9iamVjdCA6CgoqIFRoZSBleHByZXNzaW9ucyAqKmNvdW50cyoqCiogVGhlICoqZmVhdHVyZSBuYW1lcyoqIChoZXJlLCBnZW5lIHN5bWJvbHMpCiogVGhlICoqYmFyY29kZSBuYW1lcyoqCgpUaGlzIG1hdHJpeCBoYXMgYWxyZWFkeSBiZWVuICoqZmlsdGVyZWQgZm9yIGVtcHR5IGRyb3BsZXRzKiouCgojIEVudmlyb25tZW50CgpXZSBsb2FkIHRoZSBwYWNrYWdlcyBvZiBpbnRlcmVzdDoKCmBgYHtyIHBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIHBhY2thZ2VzCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShTZXVyYXQpCgpgYGAKCiMgRGF0YQoKV2UgbG9hZCB0aGUgY291bnQgbWF0cml4OgoKYGBge3IgbWF0X2xvYWR9CiMgbWF0X2xvYWQKCiMjIExvYWRpbmcgdGhlIG1hdHJpeCBkaXJlY3RseSBhcyBhIHNwYXJzZU1hdHJpeApzY21hdCA8LSBzY3V0dGxlOjpyZWFkU3BhcnNlQ291bnRzKAogICAgZmlsZSA9IHNjbWF0X2ZpbGUsIAogICAgc2VwID0gIlx0IikKCiMjIERpc3BsYXlpbmcgaXRzIHNpemUgaW4tbWVtb3J5ICh0aGlzIGlzIGEgYmFzaWMgbWF0cml4KQpmb3JtYXQodXRpbHM6Om9iamVjdC5zaXplKHNjbWF0KSwgdW5pdHMgPSAiYXV0byIpCgpgYGAKCmBgYHtyIG1hdF9kZXNjfQojIG1hdF9kZXNjCgojIyBBIGJhc2ljIGRlc2NyaXB0aW9uIG9mIHRoZSBtYXRyaXgKZGltKHNjbWF0KQpzY21hdFtjKDE6NSksIGMoMTo1KV0KCmBgYApXZSB3YW50IHRvIGJ1aWxkIGEgU2V1cmF0IG9iamVjdCBmcm9tIHRoZSBjb3VudCBtYXRyaXguCgpCdXQgaG93IGRvIHdlIGRvID8/Cgo8YnI+Cjxicj4KPGJyPgoKYGBge3IgbWF0MnNldXJhdCwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiMgbWF0MnNldXJhdAoKP1NldXJhdDo6Q3JlYXRlU2V1cmF0T2JqZWN0KCkKCmBgYAoKPGJyPgo8YnI+Cjxicj4KCmBgYHtyIG1ha2Vfc29ian0KIyBtYWtlX3NvYmoKCnNvYmogPSBTZXVyYXQ6OkNyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBzY21hdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qZWN0ID0gIlREM0EiKQpzb2JqCgpgYGAKCldlIGRvIG5vdCBuZWVkIHRoZSBjb3VudCBtYXRyaXggYW55bW9yZSA6CgpgYGB7ciBybV9tYXR9CiMgcm1fbWF0CgpybShzY21hdCkKCmBgYAoKV2Ugc2V0IHRoZSBmaWx0ZXJpbmcgdGhyZXNob2xkcyBiYXNlZCBvbiBxdWFsaXR5IGNvbnRyb2wtcmVsYXRlZCBtZXRyaWNzLgoKQWRqdXN0IHRoZW0gYXMgbmVjZXNzYXJ5IGJhc2VkIG9uIHRoZSBmaWd1cmVzLgoKYGBge3Igc2V0X3RocmVzaG9sZHN9CiMgc2V0X3RocmVzaG9sZHMKCmN1dF9uQ291bnRfUk5BID0gMTAwMApjdXRfbG9nMTBfbkNvdW50X1JOQSA9IGxvZzEwKGN1dF9uQ291bnRfUk5BKQpjdXRfbkZlYXR1cmVfUk5BID0gNzUwCmN1dF9wZXJjZW50X210ID0gNQpjdXRfcGVyY2VudF9yYiA9IDIwCmN1dF9wZXJjZW50X3N0ID0gNgoKYGBgCgpXZSBkZWZpbmUgYSBuaWNlIHBhbGV0dGUgdG8gdmlzdWFsaXplIHRoZSBRQyBtZXRyaWNzOgoKYGBge3IgY29sb3JfcGFsZXR0ZX0KIyBjb2xvcl9wYWxldHRlCgpjb2xvcl9wYWxldHRlID0gYygibGlnaHRncmF5IiwgIiNGREJCODQiLCAiI0VGNjU0OCIsICIjN0YwMDAwIiwgImJsYWNrIikKCmBgYAoKCkZvciB0aGUgUUMgbWV0cmljcyByZWxhdGVkIHRvIHRoZSBwcm9wb3J0aW9uIG9mIFVNSSBiZWxvbmdzIHRvIGEgc3BlY2lmaWMgZ2VuZSBzZXRzLCB3ZSBuZWVkIHRvIGxvYWQgdGhlIGdlbmUgc2V0cy4KCmBgYHtyIGxvYWRfbGlzdHN9CiMgbG9hZF9saXN0cwoKIyMgTUlUTwptaXRvX3N5bWJvbHMgPSByZWFkUkRTKG1pdG9fZmlsZSkKbWl0b19zeW1ib2xzCgojIyBSSUJPCnJpYm9fc3ltYm9scyA9IHJlYWRSRFMocmlib19maWxlKQpyaWJvX3N5bWJvbHMKCiMjIE1FQ0hBTklDQUwgU1RSRVNTCnN0cmVzc19zeW1ib2xzID0gcmVhZFJEUyhzdHJlc3NfZmlsZSkKc3RyZXNzX3N5bWJvbHMKYGBgCgpXZSBrZWVwIG9ubHkgdGhlIGdlbmUgc3ltYm9scyBhdmFpbGFibGUgaW4gb3VyIGRhdGEgOgoKYGBge3Igc3Vic2V0X3N5bWJvbHN9CiMgc3Vic2V0X3N5bWJvbHMKCm1pdG9fc3ltYm9scyA9IGludGVyc2VjdChtaXRvX3N5bWJvbHMsIHJvd25hbWVzKHNvYmopKQptaXRvX3N5bWJvbHMKCnJpYm9fc3ltYm9scyA9IGludGVyc2VjdChyaWJvX3N5bWJvbHMsIHJvd25hbWVzKHNvYmopKQpyaWJvX3N5bWJvbHMKCnN0cmVzc19zeW1ib2xzID0gaW50ZXJzZWN0KHN0cmVzc19zeW1ib2xzLCByb3duYW1lcyhzb2JqKSkKc3RyZXNzX3N5bWJvbHMKYGBgCgoqKk5vdGUqKjogQSBtb3JlIHJvYnVzdCBhbmFseXNpcyBtYXkgdXNlIHRoZSBnZW5lIF9pZGVudGlmaWVyc18gKGxpa2UgRW50cmV6SUQpIHJhdGhlciB0aGFuIGdlbmUgX3N5bWJvbHNfLgoKIyBRdWFsaXR5IGNvbnRyb2xzCgojIyBDb21wdXRlIG1ldHJpY3MKCldoYXQgaXMgYWxyZWFkeSBhdmFpbGFibGUgaW4gdGhlIFNldXJhdCBvYmplY3QgPwoKYGBge3Igc2VlX21ldGFkYXRhfQojIHNlZV9tZXRhZGF0YQoKaGVhZChzb2JqQG1ldGEuZGF0YSkKCmBgYAoKSG93IGRvIHRoZSB0d28gZmlyc3QgUUMgbWV0cmljcyB2YXJ5ID8KCmBgYHtyIHN1bW1hcnlfbWV0YWRhdGF9CiMgc3VtbWFyeV9tZXRhZGF0YQoKc3VtbWFyeShzb2JqQG1ldGEuZGF0YSkKCmBgYAoKSW4gdGhlIGNvbHVtbiBgbkNvdW50X1JOQWAsIHRoZSBtYXhpbXVtIGlzIGZhciBmcm9tIHRoZSB0aGlyZCBxdWFydGlsZS4gRm9yIHZpc3VhbGl6YXRpb24gcHVycG9zZSwgd2UgdHJhbnNmb3JtIHRoaXMgY29sdW1uIHRvIGxvZzEwIHNjYWxlLgoKYGBge3IgbG9nMTBfbmNvdW50X1JOQX0KIyBsb2cxMF9uY291bnRfUk5BCgpzb2JqJGxvZzEwX25Db3VudF9STkEgPSBsb2cxMChzb2JqJG5Db3VudF9STkEpCgpzdW1tYXJ5KHNvYmpAbWV0YS5kYXRhKQoKYGBgCgpXZSBjb21wdXRlIHRoZSBwZXJjZW50YWdlIG9mIFVNSSByZWxhdGVkIHRvIGVhY2ggb2YgdGhlIHRocmVlIGxpc3Qgb2YgZ2VuZXMuCgpGaXJzdCwgd2UgY29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0cyByZWxhdGVkIHRvIG1pdG9jaG9uZHJpYWwgZ2VuZXMsIHBlciBjZWxsLgoKYGBge3IgcGVyY2VudF9tdH0KIyBwZXJjZW50X210Cgpzb2JqID0gU2V1cmF0OjpQZXJjZW50YWdlRmVhdHVyZVNldCgKICBvYmplY3QgPSBzb2JqLAogIGFzc2F5ID0gIlJOQSIsCiAgZmVhdHVyZXMgPSBtaXRvX3N5bWJvbHMsCiAgY29sLm5hbWUgPSAicGVyY2VudF9tdCIpCgpzdW1tYXJ5KHNvYmokcGVyY2VudF9tdCkKCmBgYAoKVGhlbiwgd2UgY29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0cyByZWxhdGVkIHRvIHJpYm9wcm90ZWluLWNvZGluZyBnZW5lcywgcGVyIGNlbGw6CgpgYGB7ciBwZXJjZW50X3JifQojIHBlcmNlbnRfcmIKCnNvYmogPSBTZXVyYXQ6OlBlcmNlbnRhZ2VGZWF0dXJlU2V0KAogIG9iamVjdCA9IHNvYmosCiAgYXNzYXkgPSAiUk5BIiwKICBmZWF0dXJlcyA9IHJpYm9fc3ltYm9scywKICBjb2wubmFtZSA9ICJwZXJjZW50X3JiIikKCnN1bW1hcnkoc29iaiRwZXJjZW50X3JiKQoKYGBgCgoKRmluYWxseSwgd2UgY29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0cyByZWxhdGVkIHRvIG1lY2hhbmljYWwgc3RyZXNzIHNpZ25hdHVyZSwgcGVyIGNlbGwgOgoKYGBge3IgcGVyY2VudF9zdH0KIyBwZXJjZW50X3N0Cgpzb2JqID0gU2V1cmF0OjpQZXJjZW50YWdlRmVhdHVyZVNldCgKICBzb2JqLAogIGFzc2F5ID0gIlJOQSIsCiAgZmVhdHVyZXMgPSBzdHJlc3Nfc3ltYm9scywKICBjb2wubmFtZSA9ICJwZXJjZW50X3N0IikKCnN1bW1hcnkoc29iaiRwZXJjZW50X3N0KQoKYGBgCgoKV2Ugbm93IGhhdmUgYWxsIHRoZSBRQy1yZWxhdGVkIG1ldHJpY3M6CgpgYGB7ciBmaW5hbF9zdW1tYXJ5fQojIGZpbmFsX3N1bW1hcnkKCnN1bW1hcnkoc29iakBtZXRhLmRhdGEpCgpgYGAKCiMjIEZhaWxpbmcgY2VsbHMKCldlIGlkZW50aWZ5IHRoZSBjZWxscyB0aGF0IGRvIG5vdCBwYXNzIHRoZSBxdWFsaXR5IGNvbnRyb2xzLiBUaGlzIHdpbGwgYmUgdXNlZCBmb3IgdGhlIHZpc3VhbGl6YXRpb24gYW5kIGxhdGVyIGZvciBjZWxscyBmaWx0ZXJpbmcuCgpJZiB0aGUgZmlsdGVyaW5nIHRocmVzaG9sZHMgYXJlIG1vZGlmaWVkLCBkbyBub3QgZm9yZ2V0IHRvIHJ1biBhZ2FpbiB0aGlzIGNodW5rLgoKYGBge3IgZmFpbF9jZWxsc30KIyBmYWlsX2NlbGxzCmZhaWxfcGVyY2VudF9tdCA8LSByb3duYW1lcyhzb2JqQG1ldGEuZGF0YSlbc29iaiRwZXJjZW50X210ID4gY3V0X3BlcmNlbnRfbXRdCgpmYWlsX3BlcmNlbnRfcmIgPC0gcm93bmFtZXMoc29iakBtZXRhLmRhdGEpW3NvYmokcGVyY2VudF9yYiA+IGN1dF9wZXJjZW50X3JiXQoKZmFpbF9wZXJjZW50X3N0IDwtIHJvd25hbWVzKHNvYmpAbWV0YS5kYXRhKVtzb2JqJHBlcmNlbnRfc3QgPiBjdXRfcGVyY2VudF9zdF0KCmZhaWxfbkZlYXR1cmVfUk5BIDwtIHJvd25hbWVzKHNvYmpAbWV0YS5kYXRhKVtzb2JqJG5GZWF0dXJlX1JOQSA8IGN1dF9uRmVhdHVyZV9STkFdCgpmYWlsX25Db3VudF9STkEgPC0gcm93bmFtZXMoc29iakBtZXRhLmRhdGEpW3NvYmokbkNvdW50X1JOQSA8IGN1dF9uQ291bnRfUk5BXQoKYGBgCgojIyBWaXN1YWxpemF0aW9uCgpUaGlzIGlzIGRpZmZpY3VsdCB0byBoYW5kbGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGVzZSBtZXRyaWNzIGFjcm9zcyBjZWxscy4gV2Ugb3B0IGZvciB2YXJpb3VzIHZpc3VhbGl6YXRpb24gd2F5czoKCiogKipoaXN0b2dyYW0qKiwgc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBtZXRyaWMKKiAqKnZpb2xpbiBwbG90KiosIHNob3dpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbWV0cmljLCB1c2VmdWwgaWYgc2V2ZXJhbCBkYXRhc2V0cyBhcmUgY29uc2lkZXJlZAoqICoqVU1BUCoqIChvciBhbHRlcm5hdGl2ZWx5LCB0U05FKSwgc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBtZXRyaWMgb3ZlciBhIDJEIHByb2plY3Rpb24gb2YgY2VsbHMKCllvdSBtYXkgY2hvb3NlIG9uZSBvZiB0aGVzZSB2aXN1YWxpemF0aW9uIHdheXMuCgpUbyBnZW5lcmF0ZSB0aGUgVU1BUCAyRCBwcm9qZWN0aW9uLCB3ZSBuZWVkIHRvIHJ1biBtdWx0aXBsZSBgU2V1cmF0YCBjb21tYW5kcyBpbiBhIHJvdy4gVW5kZXJzdGFuZGluZyB0aGVzZSBjb21tYW5kcyBpcyBub3QgdGhlIHB1cnBvc2Ugb2YgdGhlIGN1cnJlbnQgY291cnNlLCBidXQgd2lsbCBiZSBkZXRhaWxlZCBpbiB0aGUgbmV4dCBmZXcgb25lcy4gU28ganVzdCBydW4gOgoKYGBge3IgcXVpY2tfcHJvY2Vzc2luZywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBxdWlja19wcm9jZXNzaW5nCgpzb2JqX3RtcCA9IFNldXJhdDo6Tm9ybWFsaXplRGF0YShzb2JqLCB2ZXJib3NlID0gRkFMU0UpCnNvYmpfdG1wID0gU2V1cmF0OjpTY2FsZURhdGEoc29ial90bXAsIHZlcmJvc2UgPSBGQUxTRSkKc29ial90bXAgPSBTZXVyYXQ6OkZpbmRWYXJpYWJsZUZlYXR1cmVzKHNvYmpfdG1wLCB2ZXJib3NlID0gRkFMU0UpCnNvYmpfdG1wID0gU2V1cmF0OjpSdW5QQ0Eoc29ial90bXAsIG5wY3MgPSAyMSwgdmVyYm9zZSA9IEZBTFNFKQpzb2JqX3RtcCA9IFNldXJhdDo6UnVuVU1BUChzb2JqX3RtcCwgZGltcyA9IGMoMToyMCksIHZlcmJvc2UgPSBGQUxTRSkKCmBgYAoKV2UgY2FuIG5vdyB2aXN1YWxpemUgdGhlIGNlbGxzIG9uIGEgMkQgcHJvamVjdGlvbjoKCmBgYHtyIHZpc3VhbGl6ZV9jZWxscywgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDh9CiMgdmlzdWFsaXplX2NlbGxzCgpTZXVyYXQ6OkRpbVBsb3Qob2JqZWN0ID0gc29ial90bXAsCiAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIpICsgU2V1cmF0OjpEYXJrVGhlbWUoKQoKYGBgCgojIyMgRGVmaW5lIGEgUiBmdW5jdGlvbgoKV2Ugd2FudCB0byB2aXN1YWxpemUgb3VyIGRpZmZlcmVudCBtZXRyaWNzLCBidXQgdXNpbmcgdGhlIHNhbWUga2luZCBvZiBwbG90cy4gSW5zdGVhZCBvZiBjb3B5aW5nLXBhc3RpbmcgYW5kIGVkaXRpbmcgdGhlIHNhbWUgY29kZSBtdWx0aXBsZSB0aW1lcyAodGhpcyBpcyB0aW1lLWNvbnN1bWluZyBhbmQgZXJyb3ItcHJvbmUpLCB3ZSBkZXNpZ24gYSAqKmZ1bmN0aW9uKiogdXNpbmcgdGhlIHRlbXBsYXRlIGJlbG93OgoKYGBge3IgbXlfZnVuY3Rpb25fbmFtZSwgZXZhbCA9IEZBTFNFfQojIG15X2Z1bmN0aW9uX25hbWUKCm15X2Z1bmN0aW9uX25hbWUgPSBmdW5jdGlvbihwYXJhbTEsIHBhcmFtMikgewogICMgZG8gc29tZXRoaW5nIHdpdGggdGhlIHBhcmFtZXRlciB2YWx1ZXMKICBvdXRwdXQgPSAic29tZXRoaW5nIgogIAogIHJldHVybihvdXRwdXQpCn0KCmBgYAoKSGVyZSBpcyB0aGUgZnVuY3Rpb24gd2Ugd2lsbCB1c2UgOgoKYGBge3IgcWNfcHJpbnRfZnVuY3Rpb259CiMgcWNfcHJpbnRfZnVuY3Rpb24KCnByaW50XzFfcWNfbWV0cmljID0gZnVuY3Rpb24ob2JqZWN0ID0gc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxYyA9ICJsb2cxMF9uQ291bnRfUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXRfcWMgPSBjdXRfbG9nMTBfbkNvdW50X1JOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWlsaW5nX2NlbGxzID0gZmFpbF9uQ291bnRfUk5BKSB7CiAgIyBEZXNjcmlwdGlvbiBvZiB0aGUgcGFyYW1ldGVyczoKICAjIC0gc29iaiA6IHRoZSBTZXVyYXQgb2JqZWN0LCB3aXRoIGRlZmF1bHQgdmFsdWUgdG8gdGhlIG9uZQogICMgLSBxYyA6IENIQVJBQ1RFUiA6IHRoZSBRQyBtZXRyaWMsIG11c3QgYmUgYSBjb2x1bW4gaW4gc29iakBtZXRhLmRhdGEKICAjIC0gY3V0X3FjIDogTlVNRVJJQyA6IHRoZSBmaWx0ZXJpbmcgdGhyZXNob2xkIGZvciB0aGUgUUMgbWV0cmljCiAgIyAtIGZhaWxpbmdfY2VsbHMgOiBDSEFSQUNURVIgVkVDVE9SIDogdGhlIGNlbGxzIHRoYXQgZmFpbCB0aGUgUUMKICAKICAjIEhpc3RvZ3JhbQogIHBfaGlzdCA9IGdncGxvdChvYmplY3RAbWV0YS5kYXRhLCBhZXMoeCA9IC5kYXRhW1txY11dKSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBhZnRlcl9zdGF0KGRlbnNpdHkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsIGZpbGwgPSAiI0Y4NzY2RCIsIGJpbnMgPSAxMDApICsKICAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAsIGNvbCA9ICJibHVlIiwgbHdkID0gMC43NSkgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gY3V0X3FjLCBjb2wgPSAicmVkIikgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlMCgiVGhyZXNob2xkIGZvciAiLCBxYywgIiBpczogIiwgY3V0X3FjKSkgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQogIAogICMgVmlvbGluIHBsb3QKICBwX3Zpb2xpbiA9IFNldXJhdDo6VmxuUGxvdChvYmplY3QsIGZlYXR1cmVzID0gcWMpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGN1dF9xYywgY29sID0gInJlZCIpICsKICAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAKICAjIEZlYXR1cmUgcGxvdAogIHBfdW1hcCA9IFNldXJhdDo6RmVhdHVyZVBsb3Qob2JqZWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBxYykgKwogICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGNvbG9yX3BhbGV0dGUpICsKICAgIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCiAgCiAgIyBEaW0gcGxvdAogIG9iamVjdCRmYWlsb3JwYXNzID0gYXMuZmFjdG9yKAogICAgaWZlbHNlKHRlc3QgPSBjb2xuYW1lcyhvYmplY3QpICVpbiUgZmFpbGluZ19jZWxscywKICAgICAgICAgICB5ZXMgPSAiZmFpbCIsIAogICAgICAgICAgIG5vID0gInBhc3MiKQogICAgKQoKICBwX2ZhaWwgPSBTZXVyYXQ6OkRpbVBsb3Qob2JqZWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJmYWlsb3JwYXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSAiZmFpbCIpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjRjg3NjZEIiwgImdyYXk4MCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGxldmVscyhvYmplY3QkZmFpbG9ycGFzcykpICsKICAgIGxhYnModGl0bGUgPSBxYywKICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAobGVuZ3RoKGZhaWxpbmdfY2VsbHMpLCAiIGNlbGxzIGZhaWwgKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKDEwMCpsZW5ndGgoZmFpbGluZ19jZWxscykvbmNvbChzb2JqKSwgMiksICIgJSkiKSkgKwogICAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCiAgCiAgCiAgIyMgSWYgd2UgYXBwbGllZCB0aGUgZmlsdGVyCiAgb2JqZWN0ZiA9IHN1YnNldChvYmplY3QsIGNlbGxzID0gY29sbmFtZXMob2JqZWN0KVtvYmplY3QkZmFpbG9ycGFzcyA9PSAncGFzcyddKQogIG9iamVjdGYgPSBTZXVyYXQ6OlNjYWxlRGF0YShvYmplY3RmLCB2ZXJib3NlID0gRkFMU0UpCiAgb2JqZWN0ZiA9IFNldXJhdDo6RmluZFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ZiwgdmVyYm9zZSA9IEZBTFNFKQogIG9iamVjdGYgPSBTZXVyYXQ6OlJ1blBDQShvYmplY3RmLCBucGNzID0gMjEsIHZlcmJvc2UgPSBGQUxTRSkKICBvYmplY3RmID0gU2V1cmF0OjpSdW5VTUFQKG9iamVjdGYsIGRpbXMgPSBjKDE6MjApLCB2ZXJib3NlID0gRkFMU0UpCgogIHBfdW1hcGYgPSBTZXVyYXQ6OkZlYXR1cmVQbG90KG9iamVjdCA9IG9iamVjdGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IHFjKSArCiAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gY29sb3JfcGFsZXR0ZSkgKwogICAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkKICAKICAjIFBhdGNod29yawogIHAgPSBwYXRjaHdvcms6OndyYXBfcGxvdHMocF91bWFwLCBwX2ZhaWwsIHBfaGlzdCwgcF92aW9saW4sIHBfdW1hcGYpICsKICAgIHBhdGNod29yazo6cGxvdF9sYXlvdXQobnJvdyA9IDEsIHdpZHRocyA9IGMoMSwgMSwgMiwgMSwgMSkpCiAgCiAgcmV0dXJuKHApCn0KCmBgYAoKIyMjIE51bWJlciBvZiBVTUkKCiMjIyMgQ2hlY2sgdGhlIGZ1bmN0aW9uCgpUaGlzIGZ1bmN0aW9uIHNob3VsZCBkaXJlY3RseSB3b3JrIGZvciBgbG9nMTBfbkNvdW50X1JOQWAsIGFzIGl0IGlzIHNldCBmb3IgdGhpcyBtZXRyaWMgYnkgZGVmYXVsdCAoYHFjID0gImxvZzEwX25Db3VudF9STkEiYCkgOgoKYGBge3IgY2hlY2sxX2xvZzEwX25Db3VudF9STkEsIGZpZy53aWR0aCA9IDE4LCBmaWcuaGVpZ2h0ID0gNH0KIyBjaGVjazFfbG9nMTBfbkNvdW50X1JOQQoKcHJpbnRfMV9xY19tZXRyaWMob2JqZWN0ID0gc29ial90bXApCgpgYGAKCk9mIGNvdXJzZSwgaXQgd2lsbCB3b3JrIHRoZSBzYW1lIGJ5IGV4cGxpY2l0aW5nIGFsbCBvZiB0aGUgcGFyYW1ldGVycyB2YWx1ZToKCmBgYHtyIGNoZWNrMl9sb2cxMF9uQ291bnRfUk5BLCBmaWcud2lkdGggPSAxOCwgZmlnLmhlaWdodCA9IDR9CiMgY2hlY2syX2xvZzEwX25Db3VudF9STkEKCnByaW50XzFfcWNfbWV0cmljKG9iamVjdCA9IHNvYmpfdG1wLAogICAgICAgICAgICAgICAgICBxYyA9ICJsb2cxMF9uQ291bnRfUk5BIiwKICAgICAgICAgICAgICAgICAgY3V0X3FjID0gY3V0X2xvZzEwX25Db3VudF9STkEsCiAgICAgICAgICAgICAgICAgIGZhaWxpbmdfY2VsbHMgPSBmYWlsX25Db3VudF9STkEpCgpgYGAKClNvIHdlIGNhbiBjb3B5LXBhc3RlIG9ubHkgdGhpcyBjaHVuayBmb3IgdGhlIG5leHQgUUMgbWV0cmljcyAhCgojIyMgTnVtYmVyIG9mIGV4cHJlc3NlZCBnZW5lcwoKYGBge3Igc2VlX25GZWF0dXJlX1JOQSwgZmlnLndpZHRoID0gMTgsIGZpZy5oZWlnaHQgPSA0fQojIHNlZV9uRmVhdHVyZV9STkEKCnByaW50XzFfcWNfbWV0cmljKG9iamVjdCA9IHNvYmpfdG1wLAogICAgICAgICAgICAgICAgICBxYyA9ICJuRmVhdHVyZV9STkEiLAogICAgICAgICAgICAgICAgICBjdXRfcWMgPSBjdXRfbkZlYXR1cmVfUk5BLAogICAgICAgICAgICAgICAgICBmYWlsaW5nX2NlbGxzID0gZmFpbF9uRmVhdHVyZV9STkEpCgpgYGAKCiMjIyBNaXRvY2hvbmRyaWFsIGdlbmVzIGV4cHJlc3Npb24KCmBgYHtyIHNlZV9wZXJjZW50X210LCBmaWcud2lkdGggPSAxOCwgZmlnLmhlaWdodCA9IDR9CiMgc2VlX3BlcmNlbnRfbXQKCnByaW50XzFfcWNfbWV0cmljKG9iamVjdCA9IHNvYmpfdG1wLAogICAgICAgICAgICAgICAgICBxYyA9ICJwZXJjZW50X210IiwKICAgICAgICAgICAgICAgICAgY3V0X3FjID0gY3V0X3BlcmNlbnRfbXQsCiAgICAgICAgICAgICAgICAgIGZhaWxpbmdfY2VsbHMgPSBmYWlsX3BlcmNlbnRfbXQpCgpgYGAKCiMjIyBSaWJvcHJvdGVpbi1jb2RpbmcgZ2VuZXMgZXhwcmVzc2lvbgoKYGBge3Igc2VlX3BlcmNlbnRfcmIsIGZpZy53aWR0aCA9IDE4LCBmaWcuaGVpZ2h0ID0gNH0KIyBzZWVfcGVyY2VudF9yYgoKcHJpbnRfMV9xY19tZXRyaWMob2JqZWN0ID0gc29ial90bXAsCiAgICAgICAgICAgICAgICAgIHFjID0gInBlcmNlbnRfcmIiLAogICAgICAgICAgICAgICAgICBjdXRfcWMgPSBjdXRfcGVyY2VudF9yYiwKICAgICAgICAgICAgICAgICAgZmFpbGluZ19jZWxscyA9IGZhaWxfcGVyY2VudF9yYikKCmBgYAoKCgojIyMgTWVjaGFuaWNhbCBzdHJlc3MgZ2VuZXMgZXhwcmVzc2lvbgoKYGBge3Igc2VlX3BlcmNlbnRfc3QsIGZpZy53aWR0aCA9IDE4LCBmaWcuaGVpZ2h0ID0gNH0KIyBzZWVfcGVyY2VudF9zdAoKcHJpbnRfMV9xY19tZXRyaWMob2JqZWN0ID0gc29ial90bXAsCiAgICAgICAgICAgICAgICAgIHFjID0gInBlcmNlbnRfc3QiLAogICAgICAgICAgICAgICAgICBjdXRfcWMgPSBjdXRfcGVyY2VudF9zdCwKICAgICAgICAgICAgICAgICAgZmFpbGluZ19jZWxscyA9IGZhaWxfcGVyY2VudF9zdCkKCmBgYAoKQXMgd2UgaGF2ZSBmaW5pc2hlZCB3aXRoIHRoZSB2aXN1YWxpemF0aW9uLCB3ZSBjYW4gZGlzY2FyZCBvdXIgdGVtcG9yYXJ5IG9iamVjdCA6CgpgYGB7ciBybV9zb2JqX3RtcH0KIyBybV9zb2JqX3RtcAoKcm0oc29ial90bXApCgpgYGAKCiMjIyBBbGwgbWV0cmljcyBlZmZlY3QKCgpXZSBjYW4gdmlzdWFsaXplIG1ldHJpY3MgZmlsdGVyaW5nIGVmZmVjdCBhcyBhbiAqKnVwc2V0LXBsb3QqKgoKYGBge3IgYmN1cHNldCwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQojIGJjdXBzZXQKCiMjIENyZWF0ZSBhIGxpc3Qgb2YgYWxsIGNlbGxzIGZpbHRlcmVkIE9VVCBmb3IgZWFjaCBjcml0ZXJpb24KdXBfbGlzdCA8LWxpc3QoCiAgIm5GZWF0dXJlIiA9IGZhaWxfbkZlYXR1cmVfUk5BLAogICJuQ291bnQiID0gZmFpbF9uQ291bnRfUk5BLAogICIlTUlUTyIgPSBmYWlsX3BlcmNlbnRfbXQsCiAgIiVSSUJPIiA9IGZhaWxfcGVyY2VudF9yYiwKICAiJVNUUkVTUyIgPSBmYWlsX3BlcmNlbnRfc3QpCgojIyBDcmVhdGUgYW4gdXBzZXQtcGxvdApVcFNldFI6OnVwc2V0KGRhdGEgPSBVcFNldFI6OmZyb21MaXN0KHVwX2xpc3QpLCAKICAgICAgICAgICAgICBuaW50ZXJzZWN0cyA9IE5BLCAKICAgICAgICAgICAgICBzZXRzID0gcmV2KG5hbWVzKHVwX2xpc3QpKSwKICAgICAgICAgICAgICBrZWVwLm9yZGVyID0gVFJVRSwKICAgICAgICAgICAgICBvcmRlci5ieSA9ICJmcmVxIikKCmBgYAoKIyBGaWx0ZXJpbmcgPwoKV2UgY291bGQgZmlsdGVyIG91dCBjZWxscyBiYXNlZCBvbiB0aGVzZSA1IFFDIG1ldHJpY3Mgbm93LiBJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIHdhaXQsIHBlcmZvcm0gdmFyaW91cyBhbm5vdGF0aW9ucyBzdWNoIGFzIGNlbGwgdHlwZSBhbm5vdGF0aW9uIG9yIGNlbGwgY3ljbGUgcGhhc2Ugc2NvcmluZywgdG8gYmV0dGVyIGNoYXJhY3Rlcml6ZSB0aGUgbG93IHF1YWxpdHkgY2VsbHMuCgpUbyBmaWx0ZXIgYSBTZXVyYXQgb2JqZWN0LCB3ZSB1c2UgdGhlIGBTZXVyYXQ6OnN1YnNldCgpYCBmdW5jdGlvbjoKCmBgYHtyIGZpbHRlcl9zb2JqfQojIGZpbHRlcl9zb2JqCgpzb2JqX2ZpbHRlcmVkID0gc3Vic2V0KHNvYmosIGludmVydCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgY2VsbHMgPSB1bmlxdWUoYyhmYWlsX25Db3VudF9STkEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFpbF9uRmVhdHVyZV9STkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWlsX3BlcmNlbnRfbXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmYWlsX3BlcmNlbnRfcmIsIGFjdHVhbGx5IHdlIGRlY2lkZWQgbm90IHRvIGZpbHRlciBvbiB0aGlzIHBhcmFtZXRlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFpbF9wZXJjZW50X3N0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSkKCnNvYmpfZmlsdGVyZWQKCmBgYAoKV2UgYXJlICoqbm90IGdvaW5nIHRvIHNhdmUqKiB0aGlzIGZpbHRlcmVkIFNldXJhdCBvYmplY3QsIGJ1dCB3ZSB3YW50IHRvIHN0b3JlIGluIG91ciBgc29iamAgU2V1cmF0IG9iamVjdCB0aGUgY2VsbHMgdGhhdCBmYWlsZWQgdGhlIFFDIGFzIGEgbmV3IGNlbGwgbWV0YWRhdGEgOgoKYGBge3IgYWRkX2ZhaWxfcWN9CiMgYWRkX2ZhaWxfcWMKCnNvYmokZmFpbF9xYyA9IGlmZWxzZSh0ZXN0ID0gY29sbmFtZXMoc29iaikgJWluJSBjb2xuYW1lcyhzb2JqX2ZpbHRlcmVkKSwKICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJwYXNzIiwKICAgICAgICAgICAgICAgICAgICAgIG5vID0gImZhaWwiKQoKdGFibGUoc29iaiRmYWlsX3FjKQoKYGBgCgoKIyBTYXZlCgpXZSBzYXZlIHRoZSAqKnVuZmlsdGVyZWQqKiBTZXVyYXQgb2JqZWN0OgoKYGBge3Igc2F2ZXJkczEsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMgc2F2ZXJkczEKCiMjIFNhdmUgb3VyIFNldXJhdCBvYmplY3QgKHJpY2ggbmFtaW5nKQpvdXRfbmFtZSA8LSBwYXN0ZTAoCiAgICAgICAgICBvdXRwdXRfZGlyLCAiLyIsIHBhc3RlKAogICAgICAgICAgICBjKCIwMiIsIFNldXJhdDo6UHJvamVjdChzb2JqKSwgIlM1IiwgCiAgICAgICAgICAgICAgIk1ldHJpY3MiLCBwYXN0ZSgKICAgICAgICAgICAgICAgIGRpbShzb2JqKSwgCiAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICcuJwogICAgICAgICAgICAgICkKICAgICAgICAgICAgKSwgY29sbGFwc2UgPSAiXyIpLAogICAgICAgICAgICAiLlJEUyIpCgojIyBDaGVjawpwcmludChvdXRfbmFtZSkKCiMjIFdyaXRlIG9uIGRpc2sKc2F2ZVJEUyhvYmplY3QgPSBzb2JqLCAKICAgICAgICBmaWxlID0gb3V0X25hbWUpCgpgYGAKClRoaXMgU2V1cmF0IG9iamVjdCB3b3VsZCB0aGVuIGJlIGxvYWRlZCBhcyB0aGUgaW5wdXQgZm9yIGZ1cnRoZXIgYW5hbHlzZXMuCgoKIyBSIHNlc3Npb24KClRoaXMgaXMgYSBnb29kIHByYWN0aWNlIHRvIHNob3cgdGhlIHZlcnNpb24gb2YgdGhlIHBhY2thZ2VzIHVzZWQgaW4gdGhpcyBub3RlYm9vay4KCmBgYHtyIHJzZXNzaW9uLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KIyByc2Vzc2lvbgoKc2Vzc2lvbkluZm8oKQoKYGBgCgo=