This file describes the different steps to perform the second part of the single cell RNAseq data analysis training course for the EBAII n1 2023, covering these steps :

  • Computing and evaluating QC metrics
    • Based on technical characteristics of the count matrix
    • Based on biological characteristics
  • Estimation of the cells cycle phase
  • Filtering of features and cells
  • Identification and removal of cell doublets

1 PREREQUISITES

1.1 The training data set

  • Data from the Paiva et al. publication.
  • The study concerns thymus autonomy :
    • The thymus is an “organ of passage”, critical in its function to the adaptative 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 differenciation 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 leukaemia !
  • Organism is : mus musculus
  • Individuals are : mice in development, grafted
  • The design corresponds to two conditions (Test / control)
    • Control : thymus from wild type newborn mouse 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 mouse 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’)
  • Input data consists in a count matrix, as a gzip’d 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.

1.2 Copy data and resources

  • Using the Jupyter cheat sheet, connect to JupyterHub and create a session using the resource requirements for an Rstudio session.
  • In the [Launcher] panel, click on [Terminal] in the [Other] section :


  • You should be in your home directory by default. Check it by typing :
pwd
  • Go to your project DATA directory
cd /shared/projects/<your_project>/TD/DATA
pwd
  • Copy the required input file :
cp -r /shared/projects/2325_ebaii/SingleCell/TD_DATA/DATA_START/TD3A .
  • Get back to your project directory :
cd ..
# or : cd /shared/projects/<your_project>/TD
  • Your directory should look like this (mine is called “golf”)


  • You can check by yourself :
tree -sh $PWD
  • We need to create a new directory in which we will copy the other resources needed for this session. Here we will try a variation : copy without moving !
## Here, we diretly copy the whole RESOURCES directory from the origin (2325_ebaii project) to its destination (your project)
cp -r /shared/projects/2325_ebaii/SingleCell/TD_DATA/RESOURCES /shared/projects/<your_project>/TD/
  • Your directory should look like this


  • You can check by yourself :
tree -sh $PWD
  • You can now create your output directory
mkdir ./RESULTS
  • You can now close the terminal.

2 Load raw data

Start a Rstudio session


Set your working directory in your TD output directory (golf is mine!)

setwd('/shared/projects/golf/TD/RESULTS')

Setting parameters

## Data directory
input_matrix <- '../DATA/TD3A/GSM4861194_gex_2_raw_gene_expression.tsv.gz'

## Sample name
samplename <- 'TD3A'

## Seed for the RNG
my_seed <- 1337

We can load the matrix

## R can read compressed text file without hassle
scmat <- as.matrix(
  read.table(
    file = input_matrix, 
    header = TRUE, 
    sep = '\t'))
## Displaying its size in-memory (this is a basic matrix)
format(object.size(scmat), units = "auto")
[1] "545.7 Mb"

A quick look at its content

## Displaying its structure
str(scmat)
 int [1:31053, 1:4587] 0 0 0 0 0 0 0 0 0 0 ...
 - attr(*, "dimnames")=List of 2
  ..$ : chr [1:31053] "Xkr4" "Gm1992" "Gm37381" "Rp1" ...
  ..$ : chr [1:4587] "AAACCTGAGACGCTTT.1" "AAACCTGAGGCATTGG.1" "AAACCTGGTCAACATC.1" "AAACCTGTCGAGGTAG.1" ...


Wait a second …
We have a raw count matrix that’s already been filtered for empty droplets. What may we do with it before creating a Seurat object from it ?

## Run SoupX !
soup_frac <- EBAII.n1.SC.helper::SoupX_auto(scmat_filt = scmat)

Soup-contributing features (Top 20) :


|        |       est| counts|
|:-------|---------:|------:|
|Actb    | 0.0080580| 132061|
|Gm42418 | 0.0069444| 113810|
|Eef1a1  | 0.0055896|  91607|
|Malat1  | 0.0051948|  85137|
|Ppia    | 0.0049767|  81562|
|Tmsb10  | 0.0046515|  76232|
|mt-Co1  | 0.0044872|  73540|
|Ptma    | 0.0044203|  72443|
|mt-Atp8 | 0.0040750|  66784|
|Rplp0   | 0.0038984|  63890|
|Uba52   | 0.0038012|  62297|
|Gm10076 | 0.0037717|  61814|
|Rps24   | 0.0037488|  61438|
|mt-Co2  | 0.0036859|  60407|
|Pfn1    | 0.0035459|  58113|
|Fau     | 0.0034193|  56038|
|Ubb     | 0.0033782|  55364|
|Rps16   | 0.0032381|  53068|
|Rpl13   | 0.0031730|  52002|
|Rps3a1  | 0.0031379|  51426|

cat('Soup fraction : ', soup_frac)
Soup fraction :  0.01

We can create our Seurat object

## Create Seurat object
sobj <- Seurat::CreateSeuratObject(
  counts = scmat, 
  project = samplename, 
  assay = 'RNA')
## Remove the matrix to free some RAM
rm(scmat)
## Displaying sobj size in-memory (matrices are converted to dgCMatrix)
format(object.size(sobj), units = "auto")
[1] "179.4 Mb"

Let’s describe it

## Describe the object
EBAII.n1.SC.helper::seurat4_descriptor(sobj = sobj)
OBJECT VERSION :    5.0.0 
PROJECT :   [TD3A] 

[ASSAYS]
   ASSAY 1 :    [RNA] [ACTIVE] 
      SLOT 1 :  [counts]    Dims:[31053 x 4587]  Range:[0.00-3103.00] 
         Counts :   16388810 
         Sparsity : 94.72482% 
      SLOT 2 :  [data]  Dims:[31053 x 4587]  Range:[0.00-3103.00] 
         Sparsity : 94.72482% 
      SLOT 3 :  [scale.data]    Dims:[0 x 0] 

[DIMREDS]

[BARCODES METADATA]
orig.ident    Freq
-----------  -----
TD3A          4587
NA               0

 nCount_RNA 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    502    1986    2397    3573    3134   48875 

 nFeature_RNA 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
     22    1291    1476    1638    1733    5975 

3 Count metrics

Here we will focus on the barcode/cell-level count metrics :

  • nCount_RNA : Total number of counts in each cell/barcode
  • nFeature_RNA : Number of features/gens with at least 1 count in each cell/barcode (ie : number of expressed genesin cell)

Both were automatically computed by Seurat when creating the object (but you already observed it, isn’t it ?)

For graphical purpose, we will convert count metrics (nCount_RNA) to log10

## Add l10counts ====
sobj$log10_nCount_RNA <- log10(sobj$nCount_RNA+1)

Question : Why this “+1” ?

## . Because most counts are 0, and log10(0) = -Inf

3.1 Visualization and thresholds

We can visualize these metrics’ distribution as violins …

Seurat::VlnPlot(
  object = sobj, 
  features = c('nFeature_RNA', 
               'nCount_RNA', 
               'log10_nCount_RNA'))

Question : Please describe these distributions (modes, tailing, outliers, …)

… but for my own part, I find them hard to read. I prefer histograms.



nCount_RNA

hist(x = sobj$nCount_RNA, breaks = 1000)

Still hard to read… let’s focus on the left part

hist(x = sobj$nCount_RNA, 
     breaks = 1000, 
     xlim = c(0,10000))

Question : Which thresholds would you pick to select cells of interest, based on nCount_RNA ?

## . For the left part of the distribution (very low 
##   counts), I would take 1000 as a lower bound
## . For the right part ... this is highly debatable 
##   due to the high spread of this right tailing. 
##   Removing it infers the risk of discarding somme 
##   cell subtypes with different characteristics than 
##   the mode of the distribution. Keeping them may 
##   just pollute the dataset... I'm feeling 
##   optimistic, I would take all values (so the upper 
##   threshold would be the distribution max), but will 
##   keep in mind that I may have to adapt my anaylsis 
##   to this variation in global expression levels.

Let’s set this nCount_RNA selection range and display it

## Setting nCount conservation range
ncount_range <- c(1000, max(sobj$nCount_RNA))
## Replot histogram with cutoffs
hist(x = sobj$nCount_RNA, 
     breaks = 1000, 
     xlim = c(0,10000))
abline(v = ncount_range, lty = 3, col = "red")

nFeature_RNA

hist(x = sobj$nFeature_RNA, breaks = 1000)

Question : Which thresholds would you pick to select cells of interest, based on nFeature_RNA ?

## . For the left part of the distribution (very low 
##   counts), I would take 750 as a lower bound.
## . For the right part ... for the same reason, 
##   I would take all values (so the distribution 
##   max)

Let’s set this nFeature_RNA selection range and display it

## Setting nFeature conservation range
nfeature_range <- c(750, max(sobj$nFeature_RNA))
## Replot histogram with cutoffs
hist(x = sobj$nFeature_RNA, breaks = 1000)
abline(v = nfeature_range, lty = 3, col = "red")



However,
we won’t apply these filtering criteria right now, because we have other metrics to consider.

4 Technical and biological metrics

Here we will build 3 new metrics that correspond to the fraction of expression (in counts) of three categories of gene signatures among the global expression. These are

  • Mitochondrial genes : A good proxy to display cells in bad condition (high level of expression for those genes indicates that the cell outer membrane lysis went to far and also began for internal organites).
  • Genes coding for ribosomal proteins (and not ribosomal genes as often used as a bad shortcut) : High expression of these witnesses of higher metabolic rates for concerned cells, thus it may be useful to regroup cells of a same cell type but with different metabolic states (linked to cell division, by example). However, different cell types may naturally have default different metabolic rates by default …
  • Mechanical stress genes : In droplet-based protocols, during the separation, cells flow through a microfluidic circuit, during which they pass through hard bottlenecks compared to their diameter. For very fragile cells, this may have an impact in their gene expression. Actually, fragile cells are most often completely lost/destroyed in the process, so the interest of this metric is often very limited … but it may be in some cases !

4.1 Compute metrics

We will need a function that eases the computational step for you :

## Reading the function help page
?EBAII.n1.SC.helper::count_rate_metric



%MITO

## Load the genelist
mito_symbols <- readRDS(file = '../RESOURCES/GENELISTS/mus_musculus_mito_symbols_20191015.rds')
## Compute the metric
sobj$percent_mt <- EBAII.n1.SC.helper::count_rate_metric(
  sobj = sobj,
  features = mito_symbols,
  assay = 'RNA')
## Summarize the computed values
summary(object = sobj$percent_mt)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.01985 0.02489 0.02997 0.03125 0.97250 
%RIBO

## Load the genelist
ribo_symbols <- readRDS(file = '../RESOURCES/GENELISTS/mus_musculus_cribo_symbols_20191015.rds')
## Compute the metric
sobj$percent_rb <- EBAII.n1.SC.helper::count_rate_metric(
  sobj = sobj,
  features = ribo_symbols,
  assay = 'RNA')
## Summarize the computed values
summary(object = sobj$percent_rb)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.07884 0.09577 0.10880 0.12167 0.42010 
%STRESS

## Load the genelist
stress_symbols <- readRDS(file = '../RESOURCES/GENELISTS/mus_musculus_stress_symbols_20200224.rds')
## Compute the metric
sobj$percent_st <- EBAII.n1.SC.helper::count_rate_metric(
  sobj = sobj,
  features = stress_symbols,
  assay = 'RNA')
## Summarize the computed values
summary(object = sobj$percent_st)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.02946 0.03338 0.03368 0.03749 0.10198 

4.2 Visualization and thresholds

We can then visualize these metrics as violins (as well as older ones)

Seurat::VlnPlot(object = sobj, features = c(
  'nFeature_RNA', 'nCount_RNA', 
  'log10_nCount_RNA', 'percent_mt', 
  'percent_rb', 'percent_st'), 
  ncol = 3)

Did you notice I preferred histograms ? :)



%MITO

hist(x = sobj$percent_mt, breaks = 1000)

Hard to read, let’s slash it to focus on lower values

## Restrict to first 10%
hist(x = sobj$percent_mt, 
     breaks = 1000, 
     xlim = c(0,.1))

Let’s observe the %mito distribution in the data space

## Performing the "quick viz" and saving 
## it (to speed up next plots)
VIZ <- EBAII.n1.SC.helper::QnD_viz(
  sobj = sobj, 
  features = 'percent_mt',
  return_object = TRUE)

Let’s set this %mito selection range to [0, 5%] and display it

## Setting %mito conservation range
pc_mito_range <- c(0, .05)
# pc_mito_range <- c(0, .04)
## Replot histogram with cutoffs
hist(x = sobj$percent_mt, 
     breaks = 1000, 
     xlim = c(0,.1))
abline(v = pc_mito_range, 
       lty = 3, 
       col = "red")

%RIBO

hist(x = sobj$percent_rb, breaks = 1000)

Let’s observe the %ribo distribution in the data space

## Performing the "quick viz" faster with 
## the "VIZ" Seurat object
EBAII.n1.SC.helper::QnD_viz(
  sobj = VIZ, 
  slot = NULL, dimred = 'umap',
  features = 'percent_rb')

We don’t have any clue to consider thise %ribo variations as biological or technical artefacts… So we will keep all values : [0, 1]

## Setting %ribo conservation range
pc_ribo_range <- c(0, 1)
%STRESS

hist(x = sobj$percent_st, breaks = 1000)

Let’s observe the %stress distribution in the data space

## Performing the "quick viz" faster with 
## the "VIZ" Seurat object
EBAII.n1.SC.helper::QnD_viz(
  sobj = VIZ, 
  slot = NULL, dimred = 'umap',
  features = 'percent_st')

Let’s set this %stress selection range to [0, 6%] and display it

## Setting %stress conservation range
pc_stress_range <- c(0, .06)
## Replot histogram with cutoffs
hist(x = sobj$percent_st, 
     breaks = 1000, 
     xlim = c(0,.1))
abline(v = pc_stress_range, lty = 3, 
       col = "red")

We can delete the VIZ object

rm(VIZ)



But we’re not done yet !

Single cell data from droplet-based technologies can be biased in so many ways…

Evaluating their quality, increasing the knowledge of biases affecting the values is mandatory to perform an analysis of quality !

5 Cell cycle scores

As we are analysis cells independently, they may each be in a different phase of their cell cycle. Interestingly, the effect of this cell cycle step on the cell genes expression is strong, and can bias the data. In order to assess (and maybe, remove) this bias, we have to quantify it.

We will perform this estimation thanks to heuristics based on knowledge : Seurat includes a method that evaluates the cell cycle phase of cells through scores for the S and G2M phases, each based on phase-specific gene signatures.

Some of these require the evaluation of genes which expression is expected to be null : that’s the reason why we did not filter out for unexpressed genes till now.

5.1 Estimation

Let’s perform this estimation. But how ?

## Reading the function help page
?Seurat::CellCycleScoring

We will actually use a helper function to ease up the process :

?EBAII.n1.SC.helper::CC_Seurat

Run it !

## Load the cell cycle reference genes lists
seu.cc <- readRDS(file = '../RESOURCES/GENELISTS/mus_musculus_Seurat_cc.genes_20191031.rds')
## Perform the estimation
sobj <- EBAII.n1.SC.helper::CC_Seurat(
  sobj = sobj, assay = 'RNA', 
  seurat_cc_genes = seu.cc, SmG2M = TRUE, 
  nbin = 20, my_seed = my_seed)

Description of the object to see the data added

EBAII.n1.SC.helper::seurat4_descriptor(
  sobj = sobj,
  describe = 'coldata'
)
OBJECT VERSION :    5.0.0 
PROJECT :   [TD3A] 

[BARCODES METADATA]
orig.ident    Freq
-----------  -----
TD3A          4587
NA               0

 nCount_RNA 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    502    1986    2397    3573    3134   48875 

 nFeature_RNA 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
     22    1291    1476    1638    1733    5975 

 log10_nCount_RNA 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.702   3.298   3.380   3.429   3.496   4.689 

 percent_mt 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.01985 0.02489 0.02997 0.03125 0.97250 

 percent_rb 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.07884 0.09577 0.10880 0.12167 0.42010 

 percent_st 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.02946 0.03338 0.03368 0.03749 0.10198 

 CC_Seurat_S.Score 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-2.6505 -0.3444 -0.2557 -0.2278 -0.1815  4.3548 

 CC_Seurat_G2M.Score 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-2.04357 -0.21453 -0.15637 -0.02719 -0.10224 12.76613 
CC_Seurat_Phase    Freq
----------------  -----
G1                 4259
G2M                 209
S                   119
NA                    0

 CC_Seurat_SmG2M.Score 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-13.3524  -0.1735  -0.1036  -0.2006  -0.0439   3.2976 

5.2 Visualization

As usual, we can visualize the results as violins :

Seurat::VlnPlot(object = sobj,
                features = c('CC_Seurat_S.Score',
                             'CC_Seurat_G2M.Score',
                             'CC_Seurat_SmG2M.Score'))

But it’s not that easy to interpret… Let’s plot it in the cell space

  • For the S minus G2M score :

## Using the 'features' parameter to plot the SmG2M score (continuous data)
## Here, we will keep the modified Seurat object to speed up further plots
VIZ <- EBAII.n1.SC.helper::QnD_viz(
  sobj = sobj, 
  features = 'CC_Seurat_SmG2M.Score', 
  return_object = TRUE)

  • For the estimated cell phase :

## Using the 'group_by' parameter to plot the estimated phases (categorical data)
## Here, we recycle keep the VIZ Seurat object that contains everything to perform the plot without computing it again
EBAII.n1.SC.helper::QnD_viz(sobj = VIZ, slot = NULL, dimred = 'umap', 
                            group_by = 'CC_Seurat_Phase')

## VIZ is not needed anymore
rm(VIZ)

Question1 : Does the structure of the cells in this representation seem to have a link with these cell cycle phases/scores ?

## It's an absolute yes !

Question2 : Do you think this is the result of an artifact, or biology-related ?



Now, we finally have all the metrics and bias sources we could use, so we can actually get to the filtering step.

6 Filtering

6.1 Features

As already explained, the sparcity of the count matrix is high, very high. To the point that there probably are some features that have so low expression level that they were only counted in a very few cells. As such, they have absolutely no chance to characterize any cell type, nor harbor some variation between different cell types.

As such, we can discard these features

What are the actual dimensions of our count matrix ?

dim(sobj)
[1] 31053  4587

We want to remove the features that are expressed in less than 5 cells.

## Computing the 0s per feature
nFeat_zero <- sparseMatrixStats::rowCounts(
  x = Seurat::GetAssayData(
    object = sobj, 
    assay = 'RNA', 
    slot = 'counts'), 
  value = 0)
nFeat_nonzero <- ncol(sobj) - nFeat_zero
## Summary
summary(nFeat_nonzero)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      0       0       0     242     166    4575 

## Identifying those with at least than 5 cells with expression
nFeat_keep <- nFeat_nonzero >= 5
## Restrict the Seurat object
sobj <- sobj[nFeat_keep,]
## Quantify the selected ones
table(nFeat_keep)
nFeat_keep
FALSE  TRUE 
18545 12508 

What are the Seurat object dimensions, now ?

dim(sobj)
[1] 12508  4587

We can visualize the cell space after this features filtering

EBAII.n1.SC.helper::QnD_viz(
  sobj = sobj, 
  group_by = 'CC_Seurat_Phase')

Question : Do you see any difference when comparing before vs after features filtering ?

## . Not much changed, maybe cell groups seem a bit more condensed
## . This is expected, as we removed features with almost no expression



6.2 Cells

We are now able to apply all the filtering strategies we established for each QC metric.

As a reminder, here are the metrics thresholds we set

## Feature_RNA
print(nfeature_range)
[1]  750 5975
## nCount_RNA
print(ncount_range)
[1]  1000 48875
## %mito
print(pc_mito_range)
[1] 0.00 0.05
## %ribo
print(pc_ribo_range)
[1] 0 1
## %stress
print(pc_stress_range)
[1] 0.00 0.06

Selecting “good” cells

## Applying QC metrics thresholds
bc_keep <- sobj$nFeature_RNA > nfeature_range[1] & 
  sobj$nFeature_RNA < nfeature_range[2] & 
  sobj$nCount_RNA > ncount_range[1] & 
  sobj$nCount_RNA < ncount_range[2] & 
  sobj$percent_mt > pc_mito_range[1] & 
  sobj$percent_mt < pc_mito_range[2] & 
  sobj$percent_mt > pc_ribo_range[1] & 
  sobj$percent_mt < pc_ribo_range[2] & 
  sobj$percent_st > pc_stress_range[1] & 
  sobj$percent_st < pc_stress_range[2]
table(bc_keep)
bc_keep
FALSE  TRUE 
  311  4276 

Applying the filter

sobj <- sobj[, bc_keep]
dim(sobj)
[1] 12508  4276

We can visualize the cell space since this cells filtering

EBAII.n1.SC.helper::QnD_viz(
  sobj = sobj, 
  group_by = 'CC_Seurat_Phase')

Question : Do you see any difference when comparing before vs after features filtering ?

## . Not much changed as well (we did not discard many cells)
## . The biggest cluster structure seems more defined ?

7 Cell doublets

We will use two different methods to detect and remove cell doublets :

  • scds (in its “hybrid” mode)
  • scDblFinder

7.1 scds

## Fix seed
set.seed(my_seed)
## Run scds
sobj$doublet_scds.hybrid <- unname(
  scds::cxds_bcds_hybrid(
    Seurat::as.SingleCellExperiment(
      sobj, assay = 'RNA'))$hybrid_score > 1)

7.2 scDblFinder

## Fix seed
set.seed(my_seed)
## Run scDblFinder
sobj$doublet_scDblFinder <- unname(
  Seurat::as.Seurat(
    scDblFinder::scDblFinder(
      Seurat::as.SingleCellExperiment(
        x = sobj, 
        assay = 'RNA')))$scDblFinder.class == 'doublet')

7.3 Merge results

We merge results to identify tool-specific and common doublets

sobj$doublet_union <- sobj$doublet_scds.hybrid | sobj$doublet_scDblFinder
sobj$doublet_viz <- 'singlet'
sobj$doublet_viz[sobj$doublet_union] <- 'both'
sobj$doublet_viz[sobj$doublet_scds.hybrid & !sobj$doublet_scDblFinder] <- 'scds'
sobj$doublet_viz[sobj$doublet_scDblFinder & !sobj$doublet_scds.hybrid] <- 'scDblFinder'
sobj$doublet_viz <- as.factor(sobj$doublet_viz)
table(sobj$doublet_viz)

       both scDblFinder        scds     singlet 
        142          28          68        4038 

7.4 Removal and visualization

Now we can remove and visualize the cell space with/without doublets

BEFORE

### Doublets viz (before removal)
EBAII.n1.SC.helper::QnD_viz(
  sobj = sobj, 
  group_by = 'doublet_viz')

Let’s remove doublets :

sobj <- sobj[,!sobj$doublet_union]
dim(sobj)
[1] 12508  4038
AFTER removal

### Doublets viz (after removal)
EBAII.n1.SC.helper::QnD_viz(
  sobj = sobj, 
  group_by = 'doublet_viz')

8 Save

We can now save the results of our hard work !

But, how ?

?base::saveRDS
saveRDS(object = sobj, 
        file = './TD3A_01_Filtered.RDS', 
        compress = 'bzip2')




Rsession

utils::sessionInfo()
R version 4.3.1 (2023-06-16)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.6 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0 
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0

locale:
 [1] LC_CTYPE=fr_FR.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=fr_FR.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=fr_FR.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] GenomicRanges_1.54.1        Matrix_1.6-1.1             
[3] SummarizedExperiment_1.30.2 SingleCellExperiment_1.22.0
[5] IRanges_2.36.0              S4Vectors_0.40.1           
[7] Biobase_2.62.0              BiocGenerics_0.48.0        

loaded via a namespace (and not attached):
  [1] GSEABase_1.64.0               progress_1.2.2               
  [3] nnet_7.3-19                   goftest_1.2-3                
  [5] DT_0.30                       Biostrings_2.70.1            
  [7] vctrs_0.6.4                   spatstat.random_3.2-1        
  [9] digest_0.6.33                 png_0.1-8                    
 [11] shape_1.4.6                   shinyBS_0.61.1               
 [13] registry_0.5-1                ggrepel_0.9.4                
 [15] deldir_1.0-9                  parallelly_1.36.0            
 [17] MASS_7.3-60                   reshape2_1.4.4               
 [19] httpuv_1.6.12                 foreach_1.5.2                
 [21] withr_2.5.2                   ggrastr_1.0.2                
 [23] xfun_0.41                     ellipsis_0.3.2               
 [25] survival_3.5-7                memoise_2.0.1                
 [27] ggbeeswarm_0.7.2              zoo_1.8-12                   
 [29] GlobalOptions_0.1.2           pbapply_1.7-2                
 [31] Formula_1.2-5                 prettyunits_1.2.0            
 [33] KEGGREST_1.42.0               promises_1.2.1               
 [35] httr_1.4.7                    restfulr_0.0.15              
 [37] globals_0.16.2                fitdistrplus_1.1-11          
 [39] rstudioapi_0.15.0             shinyAce_0.4.2               
 [41] miniUI_0.1.1.1                generics_0.1.3               
 [43] base64enc_0.1-3               curl_5.1.0                   
 [45] zlibbioc_1.48.0               ScaledMatrix_1.10.0          
 [47] polyclip_1.10-6               ca_0.71.1                    
 [49] GenomeInfoDbData_1.2.11       ExperimentHub_2.10.0         
 [51] SparseArray_1.2.0             RBGL_1.78.0                  
 [53] threejs_0.3.3                 interactiveDisplayBase_1.40.0
 [55] xtable_1.8-4                  stringr_1.5.0                
 [57] doParallel_1.0.17             evaluate_0.23                
 [59] S4Arrays_1.2.0                BiocFileCache_2.10.1         
 [61] hms_1.1.3                     irlba_2.3.5.1                
 [63] colorspace_2.1-0              filelock_1.0.2               
 [65] ROCR_1.0-11                   reticulate_1.34.0            
 [67] pcaExplorer_2.28.0            spatstat.data_3.0-3          
 [69] magrittr_2.0.3                lmtest_0.9-40                
 [71] Rgraphviz_2.46.0              later_1.3.1                  
 [73] viridis_0.6.4                 lattice_0.22-5               
 [75] spatstat.geom_3.2-7           NMF_0.26                     
 [77] future.apply_1.11.0           genefilter_1.84.0            
 [79] SparseM_1.81                  scattermore_1.2              
 [81] XML_3.99-0.14                 scuttle_1.12.0               
 [83] cowplot_1.1.1                 matrixStats_1.0.0            
 [85] RcppAnnoy_0.0.21              Hmisc_5.1-1                  
 [87] pillar_1.9.0                  nlme_3.1-163                 
 [89] iterators_1.0.14              gridBase_0.4-7               
 [91] compiler_4.3.1                beachmat_2.18.0              
 [93] stringi_1.7.12                Category_2.68.0              
 [95] TSP_1.2-4                     EBAII.n1.SC.helper_0.0.2     
 [97] tensor_1.5                    dendextend_1.17.1            
 [99] GenomicAlignments_1.36.0      plyr_1.8.9                   
[101] scater_1.28.0                 BiocIO_1.10.0                
[103] crayon_1.5.2                  abind_1.4-5                  
[105] SoupX_1.6.2                   locfit_1.5-9.8               
[107] sp_2.1-1                      bit_4.0.5                    
[109] dplyr_1.1.3                   codetools_0.2-19             
[111] BiocSingular_1.18.0           crosstalk_1.2.0              
[113] bslib_0.5.1                   GetoptLong_1.0.5             
[115] plotly_4.10.3                 mime_0.12                    
[117] splines_4.3.1                 circlize_0.4.15              
[119] Rcpp_1.0.11                   dbplyr_2.4.0                 
[121] sparseMatrixStats_1.14.0      knitr_1.45                   
[123] blob_1.2.4                    utf8_1.2.4                   
[125] clue_0.3-65                   BiocVersion_3.18.0           
[127] listenv_0.9.0                 checkmate_2.2.0              
[129] DelayedMatrixStats_1.24.0     tibble_3.2.1                 
[131] statmod_1.5.0                 pkgconfig_2.0.3              
[133] pheatmap_1.0.12               tools_4.3.1                  
[135] cachem_1.0.8                  RSQLite_2.3.2                
[137] viridisLite_0.4.2             DBI_1.1.3                    
[139] celldex_1.12.0                scDblFinder_1.14.0           
[141] fastmap_1.1.1                 rmarkdown_2.25               
[143] scales_1.2.1                  grid_4.3.1                   
[145] ica_1.0-3                     Seurat_4.4.0                 
[147] shinydashboard_0.7.2          Rsamtools_2.16.0             
[149] AnnotationHub_3.10.0          sass_0.4.7                   
[151] patchwork_1.1.3               BiocManager_1.30.22          
[153] dotCall64_1.1-0               graph_1.80.0                 
[155] RANN_2.6.1                    SingleR_2.4.0                
[157] rpart_4.1.21                  farver_2.1.1                 
[159] yaml_2.3.7                    AnnotationForge_1.44.0       
[161] MatrixGenerics_1.14.0         foreign_0.8-85               
[163] rtracklayer_1.60.1            cli_3.6.1                    
[165] purrr_1.0.2                   stats4_4.3.1                 
[167] webshot_0.5.5                 leiden_0.4.3                 
[169] lifecycle_1.0.3               uwot_0.1.16                  
[171] bluster_1.12.0                backports_1.4.1              
[173] BiocParallel_1.36.0           annotate_1.80.0              
[175] gtable_0.3.4                  rjson_0.2.21                 
[177] ggridges_0.5.4                progressr_0.14.0             
[179] pROC_1.18.4                   parallel_4.3.1               
[181] limma_3.58.1                  jsonlite_1.8.7               
[183] edgeR_4.0.1                   seriation_1.5.1              
[185] bitops_1.0-7                  ggplot2_3.4.4                
[187] xgboost_1.7.5.1               bit64_4.0.5                  
[189] assertthat_0.2.1              Rtsne_0.16                   
[191] spatstat.utils_3.0-4          BiocNeighbors_1.20.0         
[193] SeuratObject_5.0.0            heatmaply_1.5.0              
[195] highr_0.10                    jquerylib_0.1.4              
[197] metapod_1.10.0                dqrng_0.3.1                  
[199] scds_1.16.0                   lazyeval_0.2.2               
[201] shiny_1.7.5.1                 htmltools_0.5.6.1            
[203] GO.db_3.18.0                  sctransform_0.4.1            
[205] rappdirs_0.3.3                glue_1.6.2                   
[207] spam_2.10-0                   XVector_0.42.0               
[209] RCurl_1.98-1.12               scran_1.30.0                 
[211] gridExtra_2.3                 igraph_1.5.1                 
[213] R6_2.5.1                      tidyr_1.3.0                  
[215] DESeq2_1.42.0                 labeling_0.4.3               
[217] cluster_2.1.4                 rngtools_1.5.2               
[219] GenomeInfoDb_1.38.0           DelayedArray_0.28.0          
[221] tidyselect_1.2.0              vipor_0.4.5                  
[223] htmlTable_2.4.2               GOstats_2.68.0               
[225] xml2_1.3.5                    AnnotationDbi_1.64.0         
[227] future_1.33.0                 rsvd_1.0.5                   
[229] munsell_0.5.0                 KernSmooth_2.23-22           
[231] topGO_2.54.0                  data.table_1.14.8            
[233] htmlwidgets_1.6.2             ComplexHeatmap_2.18.0        
[235] RColorBrewer_1.1-3            biomaRt_2.58.0               
[237] rlang_1.1.1                   spatstat.sparse_3.0-3        
[239] spatstat.explore_3.2-5        fansi_1.0.5                  
[241] beeswarm_0.4.0               
LS0tCnRpdGxlOiAiPENFTlRFUj5FQkFJSSBuMSAyMDIzIDogU0lOR0xFIENFTEwgQU5BTFlTSVMgVFJBSU5JTkc8QlI+IDxCPlBSRVBST0NFU1NJTkcgKElJKTwvQj48QlI+VGVjaG5pY2FsIGFuZCBiaW9sb2dpY2FsIG1ldHJpY3MsIGNlbGwgY3lsZSAsIGRvdWJsZXRzIGFuZCBmaWx0ZXJpbmc8L0NFTlRFUj4iCmRhdGU6ICIyMDIzLTExLTA1LjEwIgphdXRob3I6CiAgLSBuYW1lOiAiQmFzdGllbiBKT0IiCiAgICBlbWFpbDogImJhc3RpZW4uam9iQGd1c3RhdmVyb3Vzc3kuZnIiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIGNzczogY29sdW1ucy5jc3MKICAgIGJhY2tncm91bmQ6IGJsYWNrCiAgICBmaWdfaGVpZ2h0OiA2CiAgICBmaWdfd2lkdGg6IDgKICAgIGhpZ2hsaWdodDogdGFuZ28gICMjIFRoZW1lIGZvciB0aGUgY29kZSBjaHVua3MKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgIyMgQWRkcyBudW1iZXIgdG8gaGVhZGVycyAoc2VjdGlvbnMpCiAgICB0aGVtZTogZmxhdGx5ICAjIyBDU1MgdGhlbWUgZm9yIHRoZSBIVE1MIHBhZ2UKICAgIHRvYzogdHJ1ZSAgIyMgQWRkcyBhIHRhYmxlIG9mIGNvbnRlbnQKICAgIHRvY19mbG9hdDogICMjIFRPQyBvcHRpb25zCiAgICAgIGNvbGxhcHNlZDogdHJ1ZSAgIyMgQnkgZGVmYXVsdCwgdGhlIFRPQyBpcyBmb2xkZWQKICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZSAjIyBTbW9vdGggc2Nyb2xsIG9mIHRoZSBIVE1MIHBhZ2UKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlICMjIEluY2x1ZGVzIGFsbCBwbG90cy9pbWFnZXMgd2l0aGluIHRoZSBIVE1MCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICMjIEFkZHMgYSBidXR0b24gdG8gZG93bmxvYWQgdGhlIFJtZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aHVtYm5haWxzOiBmYWxzZQogICAgbGlnaHRib3g6IHRydWUKICAgIGZpZ19jYXB0aW9uOiBmYWxzZQogICAgZ2FsbGVyeTogdHJ1ZQogICAgdXNlX2Jvb2tkb3duOiB0cnVlCmFsd2F5c19hbGxvd19odG1sOiB0cnVlICMjIEFsbG93IHBsYWluIEhUTUwgY29kZSBpbiB0aGUgUm1kCi0tLQoKPCEtLSBBbGxvd3MgdG8gaGlkZSB0aGUgVE9DIGJ5IGRlZmF1bHQsIGRpc3BsYXkgaXQgd2l0aCBhIGJ1dHRvbiwgbW92ZSBpdCB0byB0aGUgcmlnaHQgb3IgbGVmdCBvZiB0aGUgcGFnZSAtLT4KYHIgSG1pc2M6OmhpZGluZ1RPQyhidXR0b25MYWJlbCA9ICdTaG93IFRPQycsIGhpZGRlbiA9IFRSVUUsIHRvY1NpZGUgPSAnbGVmdCcsIGJ1dHRvblNpZGU9J2xlZnQnLCBwb3NDb2xsYXBzZSA9ICdtYXJnaW4nLCBsZXZlbHMgPSAzKWAKCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBvcHRpb25zKHdpZHRoID0gNjApOwprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsICAgICAgICAjIFByaW50IHRoZSBjb2RlCiAgZXZhbCA9IFRSVUUsICAgICAgICMgRG8gbm90IHJ1biBjb21tYW5kIGxpbmVzCiAgbWVzc2FnZSA9IEZBTFNFLCAgICAjIFByaW50IG1lc3NhZ2VzCiAgcHJvbXB0ID0gRkFMU0UsICAgICAjIERvIG5vdCBkaXNwbGF5IHByb21wdAogIGNvbW1lbnQgPSBOQSwgICAgICAgIyBObyBjb21tZW50cyBvbiB0aGlzIHNlY3Rpb24KICB3YXJuaW5nID0gRkFMU0UsICAgICMgRGlzcGxheSB3YXJuaW5ncwogIHRpZHkgPSBGQUxTRSwKICAjIHJlc3VsdHMgPSAnaGlkZScKICB3aWR0aCA9IDEwMCAgICAgICAjIE51bWJlciBvZiBjaGFyYWN0ZXJzIHBlciBsaW5lCikKYGBgCgoKVGhpcyBmaWxlIGRlc2NyaWJlcyB0aGUgZGlmZmVyZW50IHN0ZXBzIHRvIHBlcmZvcm0gdGhlIHNlY29uZCBwYXJ0IG9mIHRoZSBzaW5nbGUgY2VsbCBSTkFzZXEgZGF0YSBhbmFseXNpcyB0cmFpbmluZyBjb3Vyc2UgZm9yIHRoZSBFQkFJSSBuMSAyMDIzLCBjb3ZlcmluZyB0aGVzZSBzdGVwcyA6CgoqIENvbXB1dGluZyBhbmQgZXZhbHVhdGluZyBRQyBtZXRyaWNzCiAgKiBCYXNlZCBvbiB0ZWNobmljYWwgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBjb3VudCBtYXRyaXgKICAqIEJhc2VkIG9uIGJpb2xvZ2ljYWwgY2hhcmFjdGVyaXN0aWNzCiogRXN0aW1hdGlvbiBvZiB0aGUgY2VsbHMgY3ljbGUgcGhhc2UKKiBGaWx0ZXJpbmcgb2YgZmVhdHVyZXMgYW5kIGNlbGxzCiogSWRlbnRpZmljYXRpb24gYW5kIHJlbW92YWwgb2YgY2VsbCBkb3VibGV0cwoKCiMgUFJFUkVRVUlTSVRFUwoKIyMgVGhlIHRyYWluaW5nIGRhdGEgc2V0CgoqIERhdGEgZnJvbSB0aGUgW1BhaXZhIGV0IGFsLl0oaHR0cHM6Ly9mZWJzLm9ubGluZWxpYnJhcnkud2lsZXkuY29tL2RvaS9mdWxsLzEwLjExMTEvZmVicy4xNDY1MSkgcHVibGljYXRpb24uCiogVGhlIHN0dWR5IGNvbmNlcm5zICoqdGh5bXVzIGF1dG9ub215KiogOgogICogVGhlIHRoeW11cyBpcyBhbiAib3JnYW4gb2YgcGFzc2FnZSIsIGNyaXRpY2FsIGluIGl0cyBmdW5jdGlvbiB0byB0aGUgYWRhcHRhdGl2ZSBpbW11bmUgc3lzdGVtIGZvciB0aGUgbWF0dXJhdGlvbiBvZiBUIGNlbGwgbHltcGhvY3l0ZXMuCiAgKiBUaGlzIG1hdHVyYXRpb24gaW52b2x2ZXMgdHdvIG1haW4gc3RlcHMsIHBlcmZvcm1lZCB0aGFua3MgdG8gbWFjcm9waGFnZXMgOgogICAgKiBQb3NpdGl2ZSBzZWxlY3Rpb24gOiBrZWVwaW5nIGNlbGxzIHRoYXQgc3VjY2Vzc2Z1bGx5IGRldmVsb3AgcmVhY3QgYXBwcm9wcmlhdGVseSB3aXRoIE1IQyBpbW11bmUgcmVjZXB0b3JzIG9mIHRoZSBib2R5CiAgICAqIE5lZ2F0aXZlIHNlbGVjdGlvbiA6IGtlZXBpbmcgY2VsbHMgdGhhdCBkbyBub3QgcmVhY3QgYWdhaW5zdCBuYXR1cmFsIHByb3RlaW5zIG9mIHRoZSBib2R5LgogICogVGh5bXVzIF9hdXRvbm9teV8gaXMgYSBuYXR1cmFsIG1lY2hhbmlzbSB0aGF0IGFsbG93cyB0byBjcmVhdGUgVCBjZWxscyBpbiB0aGUgdGh5bXVzIGJ5IGRpZmZlcmVuY2lhdGlvbiBhbmQgY2VsbCBjb21wZXRpdGlvbiwgZXZlbiB3aGVuIG5vcm1hbCBwcm9nZW5pdG9ycyBmcm9tIHRoZSBib25lIG1hcnJvdyBhcmUgbGFja2luZywgaW4gY3JpdGljYWwgY29uZGl0aW9ucy4KICAqIFRoaXMgbWVjaGFuaXNtIGlzIGtub3duIGluIGl0cyBlZmZlY3RzLCBidXQgdGhlIGNlbGxzIGludm9sdmVkIGluIGFyZSBub3QuCiAgKiBUaGlzIHN0dWR5IGlzIG9mIGltcG9ydGFuY2UgaW4gdGhlIGhlYWx0aCBmaWVsZCwgYXMgdGhpcyBtZWNoYW5pc20gcmVsaWVzIG9uIGEgdGVtcG9yYXJ5IGxvc3Mgb2YgY29udHJvbCBvZiB0aGUgY2VsbCBub3JtYWwgZnVuY3Rpb25zLgogICogVGhlIGNvbnNlcXVlbmNlIGlzIHRoYXQgaWYgdGh5bXVzIGlzIGluIGF1dG9ub215IGZvciB0b28gbG9uZyAoZmV3IHdlZWtzKSwgdGhpcyBpcyBhIHByZWx1ZGUgZm9yIGxldWthZW1pYSAhCiAgPGNlbnRlcj4hW10odGh5bXVzX2F1dG9ub215LnBuZyk8L2NlbnRlcj4KKiBPcmdhbmlzbSBpcyA6ICoqbXVzIG11c2N1bHVzKioKKiBJbmRpdmlkdWFscyBhcmUgOiBtaWNlICoqaW4gZGV2ZWxvcG1lbnQsIGdyYWZ0ZWQqKgoqIFRoZSBkZXNpZ24gY29ycmVzcG9uZHMgdG8gKip0d28gY29uZGl0aW9ucyoqIChUZXN0IC8gY29udHJvbCkKICAqIENvbnRyb2wgOiB0aHltdXMgZnJvbSAqKndpbGQgdHlwZSoqIG5ld2Jvcm4gbW91c2UgdHJhbnNwbGFudGVkIGludG8gKip3aWxkIHR5cGUqKiBqdXZlbmlsZSBtb3VzZS4gSW4gdGhpcyBjb250cm9sIGNhc2UsICoqZG9ub3IqKiBULWNlbGxzIHByb2dlbml0b3JzIChETjMpIHdlcmUgcmVwbGFjZWQgYnkgKipob3N0KiogY2VsbHMgKiozIHdlZWtzKiogYWZ0ZXIgdHJhbnNwbGFudGF0aW9uLgogICogVGVzdCA6IHRoeW11cyBmcm9tICoqd2lsZCB0eXBlKiogbmV3Ym9ybiBtb3VzZSB0cmFuc3BsYW50ZWQgaW4gKipLTyBSYWctLy0qKiB0eXBlIGp1dmVuaWxlIG1vdXNlICh0aGUgS08gcGFydGlhbGx5IGltcGFpcnMgdGhlaXIgYWJpbGl0eSB0byBwcm9kdWNlIFQtY2VsbCBwcm9nZW5pdG9ycyBpbiBub3JtYWwgYW1vdW50cykuIEluIHRoaXMgdGVzdCBjYXNlLCAqKmRvbm9yKiogVC1jZWxscyBwcm9nZW5pdG9ycyAoRE4zKSB3ZXJlIHJlcGxhY2VkIGJ5ICoqaG9zdCoqIGNlbGxzICoqOSB3ZWVrcyoqIGFmdGVyIHRyYW5zcGxhbnRhdGlvbiwgc2hvd2luZyB0aGF0IHRoZSBkb25vciBETjMgY2VsbHMgb3V0bGl2ZWQgdGhlaXIgbm9ybWFsIGxpZmVzcGFuIGJ5IH42IHdlZWtzLgogIDxjZW50ZXI+IVtdKHBhaXZhX3d0X2tvLnBuZyk8L2NlbnRlcj4KKiBZb3Ugd2lsbCBtYWlubHkgd29yayBvbiB0aGUgKipLTyBzYW1wbGUgKCdURDNBJykqKgoqIElucHV0IGRhdGEgY29uc2lzdHMgaW4gYSAqKmNvdW50IG1hdHJpeCoqLCBhcyBhIGd6aXAnZCB0YWJ1bGFyIHRleHQgZmlsZSwgdGhhdCBjb250YWlucyBldmVyeXRoaW5nIG5lZWRlZCB0byBjcmVhdGUgYSBiYXNpYyBTZXVyYXQgb2JqZWN0IDoKICAqIFRoZSBleHByZXNzaW9ucyAqKmNvdW50cyoqCiAgKiBUaGUgKipmZWF0dXJlIG5hbWVzKiogKGhlcmUsIGdlbmUgc3ltYm9scykKICAqIFRoZSAqKmJhcmNvZGUgbmFtZXMqKgoqIFRoaXMgbWF0cml4IGhhcyBhbHJlYWR5IGJlZW4gKipmaWx0ZXJlZCBmb3IgZW1wdHkgZHJvcGxldHMqKi4KCgojIyBDb3B5IGRhdGEgYW5kIHJlc291cmNlcwoKKiBVc2luZyB0aGUgW0p1cHl0ZXIgY2hlYXQgc2hlZXRdKGh0dHBzOi8vaWZiLWVsaXhpcmZyLmdpdGh1Yi5pby9FQkFJSS8yMDIzL2ViYWlpbjEvU2luZ2xlQ2VsbC8yMDIzX1REX0p1cHl0ZXJIdWIuaHRtbCksIGNvbm5lY3QgdG8gW0p1cHl0ZXJIdWJdKGh0dHBzOi8vanVweXRlcmh1Yi5jbHVzdGVyLmZyYW5jZS1iaW9pbmZvcm1hdGlxdWUuZnIpIGFuZCBjcmVhdGUgYSBzZXNzaW9uIHVzaW5nIHRoZSByZXNvdXJjZSByZXF1aXJlbWVudHMgZm9yIGFuIFJzdHVkaW8gc2Vzc2lvbi4KKiBJbiB0aGUgW0xhdW5jaGVyXSBwYW5lbCwgY2xpY2sgb24gKipbVGVybWluYWxdKiogaW4gdGhlIFtPdGhlcl0gc2VjdGlvbiA6CjxCUj48QlI+PGNlbnRlcj4hW10oanVwX290aGVyX3Rlcm1pbmFsLnBuZyk8L2NlbnRlcj48QlI+CiogWW91IHNob3VsZCBiZSBpbiB5b3VyIGhvbWUgZGlyZWN0b3J5IGJ5IGRlZmF1bHQuIENoZWNrIGl0IGJ5IHR5cGluZyA6CmBgYHtiYXNoIHB3ZDEsIGV2YWwgPSBGQUxTRSwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyJ9CnB3ZApgYGAKKiBHbyB0byAqKip5b3VyIHByb2plY3QgREFUQSoqKiBkaXJlY3RvcnkKYGBge2Jhc2ggY2RfcHJvaiwgZXZhbCA9IEZBTFNFLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KY2QgL3NoYXJlZC9wcm9qZWN0cy88eW91cl9wcm9qZWN0Pi9URC9EQVRBCnB3ZApgYGAKKiBDb3B5IHRoZSByZXF1aXJlZCBpbnB1dCBmaWxlIDoKYGBge2Jhc2ggY3BfaW5kYXRhLCBldmFsID0gRkFMU0UsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3cifQpjcCAtciAvc2hhcmVkL3Byb2plY3RzLzIzMjVfZWJhaWkvU2luZ2xlQ2VsbC9URF9EQVRBL0RBVEFfU1RBUlQvVEQzQSAuCmBgYAoqIEdldCBiYWNrIHRvIHlvdXIgcHJvamVjdCBkaXJlY3RvcnkgOgpgYGB7YmFzaCBiYWNrcHJvaiwgZXZhbCA9IEZBTFNFLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KY2QgLi4KIyBvciA6IGNkIC9zaGFyZWQvcHJvamVjdHMvPHlvdXJfcHJvamVjdD4vVEQKYGBgCiogWW91ciBkaXJlY3Rvcnkgc2hvdWxkIGxvb2sgbGlrZSB0aGlzIF8obWluZSBpcyBjYWxsZWQgIioqKmdvbGYiKioqKV8KPEJSPjxCUj48Y2VudGVyPiFbXShiYXNoX3RyZWUxLnBuZyk8L2NlbnRlcj48QlI+CiogWW91IGNhbiBjaGVjayBieSB5b3Vyc2VsZiA6CmBgYHtiYXNoIHRyZWUxLCBldmFsID0gRkFMU0UsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3cifQp0cmVlIC1zaCAkUFdECmBgYAoqIFdlIG5lZWQgdG8gY3JlYXRlIGEgbmV3IGRpcmVjdG9yeSBpbiB3aGljaCB3ZSB3aWxsIGNvcHkgdGhlIG90aGVyIHJlc291cmNlcyBuZWVkZWQgZm9yIHRoaXMgc2Vzc2lvbi4gSGVyZSB3ZSB3aWxsIHRyeSBhIHZhcmlhdGlvbiA6IGNvcHkgd2l0aG91dCBtb3ZpbmcgIQpgYGB7YmFzaCBjcmVhdGVfZGF0YWRpciwgZXZhbCA9IEZBTFNFLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KIyMgSGVyZSwgd2UgZGlyZXRseSBjb3B5IHRoZSB3aG9sZSBSRVNPVVJDRVMgZGlyZWN0b3J5IGZyb20gdGhlIG9yaWdpbiAoMjMyNV9lYmFpaSBwcm9qZWN0KSB0byBpdHMgZGVzdGluYXRpb24gKHlvdXIgcHJvamVjdCkKY3AgLXIgL3NoYXJlZC9wcm9qZWN0cy8yMzI1X2ViYWlpL1NpbmdsZUNlbGwvVERfREFUQS9SRVNPVVJDRVMgL3NoYXJlZC9wcm9qZWN0cy88eW91cl9wcm9qZWN0Pi9URC8KYGBgCiogWW91ciBkaXJlY3Rvcnkgc2hvdWxkIGxvb2sgbGlrZSB0aGlzCjxCUj48QlI+PGNlbnRlcj4hW10oYmFzaF90cmVlMi5wbmcpPC9jZW50ZXI+PEJSPgoqIFlvdSBjYW4gY2hlY2sgYnkgeW91cnNlbGYgOgpgYGB7YmFzaCB0cmVlMiwgZXZhbCA9IEZBTFNFLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KdHJlZSAtc2ggJFBXRApgYGAKKiBZb3UgY2FuIG5vdyBjcmVhdGUgeW91ciBvdXRwdXQgZGlyZWN0b3J5CmBgYHtiYXNoIG9kaXIsIGV2YWwgPSBGQUxTRSwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyJ9Cm1rZGlyIC4vUkVTVUxUUwpgYGAKCiogWW91IGNhbiBub3cgY2xvc2UgdGhlIHRlcm1pbmFsLgoKIyBMb2FkIHJhdyBkYXRhCgpTdGFydCBhICoqUnN0dWRpbyoqIHNlc3Npb24KPEJSPjxCUj48Y2VudGVyPiFbXShSc3R1ZGlvLnBuZyk8L2NlbnRlcj48QlI+CgpTZXQgeW91ciB3b3JraW5nIGRpcmVjdG9yeSBpbiAqKnlvdXIqKiBURCBvdXRwdXQgZGlyZWN0b3J5IF8oKipnb2xmKiogaXMgbWluZSEpXwoKYGBge3Igc2V0d2QsIGV2YWwgPSBGQUxTRX0Kc2V0d2QoJy9zaGFyZWQvcHJvamVjdHMvZ29sZi9URC9SRVNVTFRTJykKYGBgCgpTZXR0aW5nIHBhcmFtZXRlcnMKYGBge3Igc2V0cGFyYW0sIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3cifQojIyBEYXRhIGRpcmVjdG9yeQppbnB1dF9tYXRyaXggPC0gJy4uL0RBVEEvVEQzQS9HU000ODYxMTk0X2dleF8yX3Jhd19nZW5lX2V4cHJlc3Npb24udHN2Lmd6JwoKIyMgU2FtcGxlIG5hbWUKc2FtcGxlbmFtZSA8LSAnVEQzQScKCiMjIFNlZWQgZm9yIHRoZSBSTkcKbXlfc2VlZCA8LSAxMzM3CmBgYAoKV2UgY2FuIGxvYWQgdGhlIG1hdHJpeAoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIGRhdGFsb2FkfQojIyBSIGNhbiByZWFkIGNvbXByZXNzZWQgdGV4dCBmaWxlIHdpdGhvdXQgaGFzc2xlCnNjbWF0IDwtIGFzLm1hdHJpeCgKICByZWFkLnRhYmxlKAogICAgZmlsZSA9IGlucHV0X21hdHJpeCwgCiAgICBoZWFkZXIgPSBUUlVFLCAKICAgIHNlcCA9ICdcdCcpKQojIyBEaXNwbGF5aW5nIGl0cyBzaXplIGluLW1lbW9yeSAodGhpcyBpcyBhIGJhc2ljIG1hdHJpeCkKZm9ybWF0KG9iamVjdC5zaXplKHNjbWF0KSwgdW5pdHMgPSAiYXV0byIpCmBgYAoKQSBxdWljayBsb29rIGF0IGl0cyBjb250ZW50Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgZGF0YXN0cn0KIyMgRGlzcGxheWluZyBpdHMgc3RydWN0dXJlCnN0cihzY21hdCkKYGBgCgo8QlI+CldhaXQgYSBzZWNvbmQgLi4uCjxCUj4KV2UgaGF2ZSBhIHJhdyBjb3VudCBtYXRyaXggdGhhdCdzIGFscmVhZHkgYmVlbiBmaWx0ZXJlZCBmb3IgZW1wdHkgZHJvcGxldHMuIFdoYXQgbWF5IHdlIGRvIHdpdGggaXQgYmVmb3JlIGNyZWF0aW5nIGEgU2V1cmF0IG9iamVjdCBmcm9tIGl0ID8KCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBydW5zb3VweCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CiMjIFJ1biBTb3VwWCAhCnNvdXBfZnJhYyA8LSBFQkFJSS5uMS5TQy5oZWxwZXI6OlNvdXBYX2F1dG8oc2NtYXRfZmlsdCA9IHNjbWF0KQpgYGAKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBzb3VweGZyYWMsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjYXQoJ1NvdXAgZnJhY3Rpb24gOiAnLCBzb3VwX2ZyYWMpCmBgYAoKV2UgY2FuIGNyZWF0ZSBvdXIgU2V1cmF0IG9iamVjdAoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIGNyZWF0ZXNldX0KIyMgQ3JlYXRlIFNldXJhdCBvYmplY3QKc29iaiA8LSBTZXVyYXQ6OkNyZWF0ZVNldXJhdE9iamVjdCgKICBjb3VudHMgPSBzY21hdCwgCiAgcHJvamVjdCA9IHNhbXBsZW5hbWUsIAogIGFzc2F5ID0gJ1JOQScpCiMjIFJlbW92ZSB0aGUgbWF0cml4IHRvIGZyZWUgc29tZSBSQU0Kcm0oc2NtYXQpCiMjIERpc3BsYXlpbmcgc29iaiBzaXplIGluLW1lbW9yeSAobWF0cmljZXMgYXJlIGNvbnZlcnRlZCB0byBkZ0NNYXRyaXgpCmZvcm1hdChvYmplY3Quc2l6ZShzb2JqKSwgdW5pdHMgPSAiYXV0byIpCmBgYAoKTGV0J3MgZGVzY3JpYmUgaXQKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBzb2JqZGVzY30KIyMgRGVzY3JpYmUgdGhlIG9iamVjdApFQkFJSS5uMS5TQy5oZWxwZXI6OnNldXJhdDRfZGVzY3JpcHRvcihzb2JqID0gc29iaikKYGBgCgojIENvdW50IG1ldHJpY3MKCkhlcmUgd2Ugd2lsbCBmb2N1cyBvbiB0aGUgYmFyY29kZS9jZWxsLWxldmVsIGNvdW50IG1ldHJpY3MgOgoKKiAqKm5Db3VudF9STkEqKiA6IFRvdGFsIG51bWJlciBvZiBjb3VudHMgaW4gZWFjaCBjZWxsL2JhcmNvZGUKKiAqKm5GZWF0dXJlX1JOQSoqIDogTnVtYmVyIG9mIGZlYXR1cmVzL2dlbnMgd2l0aCBhdCBsZWFzdCAxIGNvdW50IGluIGVhY2ggY2VsbC9iYXJjb2RlIChpZSA6IG51bWJlciBvZiBleHByZXNzZWQgZ2VuZXNpbiBjZWxsKQoKQm90aCB3ZXJlIGF1dG9tYXRpY2FsbHkgY29tcHV0ZWQgYnkgU2V1cmF0IHdoZW4gY3JlYXRpbmcgdGhlIG9iamVjdCAoYnV0IHlvdSBhbHJlYWR5IG9ic2VydmVkIGl0LCBpc24ndCBpdCA/KQoKRm9yIGdyYXBoaWNhbCBwdXJwb3NlLCB3ZSB3aWxsIGNvbnZlcnQgY291bnQgbWV0cmljcyAobkNvdW50X1JOQSkgdG8gbG9nMTAKCmBgYHtyIGwxMGNvdW50fQojIyBBZGQgbDEwY291bnRzID09PT0Kc29iaiRsb2cxMF9uQ291bnRfUk5BIDwtIGxvZzEwKHNvYmokbkNvdW50X1JOQSsxKQpgYGAKCioqKlF1ZXN0aW9uKioqIDogV2h5IHRoaXMgKioiKzEiKiogPwoKYGBge3IgcjEsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQojIyAuIEJlY2F1c2UgbW9zdCBjb3VudHMgYXJlIDAsIGFuZCBsb2cxMCgwKSA9IC1JbmYKYGBgCgojIyBWaXN1YWxpemF0aW9uIGFuZCB0aHJlc2hvbGRzCldlIGNhbiB2aXN1YWxpemUgdGhlc2UgbWV0cmljcycgZGlzdHJpYnV0aW9uIGFzIHZpb2xpbnMgLi4uCgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgdmxuMSwgZmlnLmFsaWduID0gJ2NlbnRlcid9ClNldXJhdDo6VmxuUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICBmZWF0dXJlcyA9IGMoJ25GZWF0dXJlX1JOQScsIAogICAgICAgICAgICAgICAnbkNvdW50X1JOQScsIAogICAgICAgICAgICAgICAnbG9nMTBfbkNvdW50X1JOQScpKQpgYGAKCioqKlF1ZXN0aW9uKioqIDogUGxlYXNlIGRlc2NyaWJlIHRoZXNlIGRpc3RyaWJ1dGlvbnMgKG1vZGVzLCB0YWlsaW5nLCBvdXRsaWVycywgLi4uKQoKLi4uIGJ1dCBmb3IgbXkgb3duIHBhcnQsIEkgZmluZCB0aGVtIGhhcmQgdG8gcmVhZC4gSSBwcmVmZXIgaGlzdG9ncmFtcy4KCjxicj48YnI+Cgo6OjogY29sdW1ucwo6OjogY29sdW1uCgo8Y2VudGVyPjxmb250IHNpemU9IjQiPioqbkNvdW50X1JOQSoqPC9mb250PjwvY2VudGVyPgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIGhpc3Rjb3VudDEsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA4LCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KaGlzdCh4ID0gc29iaiRuQ291bnRfUk5BLCBicmVha3MgPSAxMDAwKQpgYGAKCgpTdGlsbCBoYXJkIHRvIHJlYWQuLi4gbGV0J3MgZm9jdXMgb24gdGhlIGxlZnQgcGFydAoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIGhpc3Rjb3VudDIsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA4LCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KaGlzdCh4ID0gc29iaiRuQ291bnRfUk5BLCAKICAgICBicmVha3MgPSAxMDAwLCAKICAgICB4bGltID0gYygwLDEwMDAwKSkKYGBgCgoqKipRdWVzdGlvbioqKiA6IFdoaWNoIHRocmVzaG9sZHMgd291bGQgeW91IHBpY2sgdG8gc2VsZWN0IGNlbGxzIG9mIGludGVyZXN0LCBiYXNlZCBvbiBuQ291bnRfUk5BID8KCmBgYHtyIHFfbmNvdW50X3RocmVzaCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CiMjIC4gRm9yIHRoZSBsZWZ0IHBhcnQgb2YgdGhlIGRpc3RyaWJ1dGlvbiAodmVyeSBsb3cgCiMjICAgY291bnRzKSwgSSB3b3VsZCB0YWtlIDEwMDAgYXMgYSBsb3dlciBib3VuZAojIyAuIEZvciB0aGUgcmlnaHQgcGFydCAuLi4gdGhpcyBpcyBoaWdobHkgZGViYXRhYmxlIAojIyAgIGR1ZSB0byB0aGUgaGlnaCBzcHJlYWQgb2YgdGhpcyByaWdodCB0YWlsaW5nLiAKIyMgICBSZW1vdmluZyBpdCBpbmZlcnMgdGhlIHJpc2sgb2YgZGlzY2FyZGluZyBzb21tZSAKIyMgICBjZWxsIHN1YnR5cGVzIHdpdGggZGlmZmVyZW50IGNoYXJhY3RlcmlzdGljcyB0aGFuIAojIyAgIHRoZSBtb2RlIG9mIHRoZSBkaXN0cmlidXRpb24uIEtlZXBpbmcgdGhlbSBtYXkgCiMjICAganVzdCBwb2xsdXRlIHRoZSBkYXRhc2V0Li4uIEknbSBmZWVsaW5nIAojIyAgIG9wdGltaXN0aWMsIEkgd291bGQgdGFrZSBhbGwgdmFsdWVzIChzbyB0aGUgdXBwZXIgCiMjICAgdGhyZXNob2xkIHdvdWxkIGJlIHRoZSBkaXN0cmlidXRpb24gbWF4KSwgYnV0IHdpbGwgCiMjICAga2VlcCBpbiBtaW5kIHRoYXQgSSBtYXkgaGF2ZSB0byBhZGFwdCBteSBhbmF5bHNpcyAKIyMgICB0byB0aGlzIHZhcmlhdGlvbiBpbiBnbG9iYWwgZXhwcmVzc2lvbiBsZXZlbHMuCmBgYAoKTGV0J3Mgc2V0IHRoaXMgbkNvdW50X1JOQSBzZWxlY3Rpb24gcmFuZ2UgYW5kIGRpc3BsYXkgaXQKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBuY291bnRyYW5nZSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDgsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQojIyBTZXR0aW5nIG5Db3VudCBjb25zZXJ2YXRpb24gcmFuZ2UKbmNvdW50X3JhbmdlIDwtIGMoMTAwMCwgbWF4KHNvYmokbkNvdW50X1JOQSkpCiMjIFJlcGxvdCBoaXN0b2dyYW0gd2l0aCBjdXRvZmZzCmhpc3QoeCA9IHNvYmokbkNvdW50X1JOQSwgCiAgICAgYnJlYWtzID0gMTAwMCwgCiAgICAgeGxpbSA9IGMoMCwxMDAwMCkpCmFibGluZSh2ID0gbmNvdW50X3JhbmdlLCBsdHkgPSAzLCBjb2wgPSAicmVkIikKYGBgCgo6OjoKOjo6IGNvbHVtbgoKPGNlbnRlcj48Zm9udCBzaXplPSI0Ij4qKm5GZWF0dXJlX1JOQSoqPC9mb250PjwvY2VudGVyPgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIGhpc3RmZWF0MSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDgsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQpoaXN0KHggPSBzb2JqJG5GZWF0dXJlX1JOQSwgYnJlYWtzID0gMTAwMCkKYGBgCgoqKipRdWVzdGlvbioqKiA6IFdoaWNoIHRocmVzaG9sZHMgd291bGQgeW91IHBpY2sgdG8gc2VsZWN0IGNlbGxzIG9mIGludGVyZXN0LCBiYXNlZCBvbiBuRmVhdHVyZV9STkEgPwoKYGBge3IgcV9uZmVhdF90aHJlc2gsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQojIyAuIEZvciB0aGUgbGVmdCBwYXJ0IG9mIHRoZSBkaXN0cmlidXRpb24gKHZlcnkgbG93IAojIyAgIGNvdW50cyksIEkgd291bGQgdGFrZSA3NTAgYXMgYSBsb3dlciBib3VuZC4KIyMgLiBGb3IgdGhlIHJpZ2h0IHBhcnQgLi4uIGZvciB0aGUgc2FtZSByZWFzb24sIAojIyAgIEkgd291bGQgdGFrZSBhbGwgdmFsdWVzIChzbyB0aGUgZGlzdHJpYnV0aW9uIAojIyAgIG1heCkKYGBgCgpMZXQncyBzZXQgdGhpcyBuRmVhdHVyZV9STkEgc2VsZWN0aW9uIHJhbmdlIGFuZCBkaXNwbGF5IGl0Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgbmZlYXRyYW5nZSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDgsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQojIyBTZXR0aW5nIG5GZWF0dXJlIGNvbnNlcnZhdGlvbiByYW5nZQpuZmVhdHVyZV9yYW5nZSA8LSBjKDc1MCwgbWF4KHNvYmokbkZlYXR1cmVfUk5BKSkKIyMgUmVwbG90IGhpc3RvZ3JhbSB3aXRoIGN1dG9mZnMKaGlzdCh4ID0gc29iaiRuRmVhdHVyZV9STkEsIGJyZWFrcyA9IDEwMDApCmFibGluZSh2ID0gbmZlYXR1cmVfcmFuZ2UsIGx0eSA9IDMsIGNvbCA9ICJyZWQiKQpgYGAKCjo6Ogo6OjoKCjxicj48YnI+CjxjZW50ZXI+CiFbXShkbWJsZHJfaG93ZXZlci5qcGVnKQoKPGZvbnQgc2l6ZT0iNiI+KipIb3dldmVyKiosPEJSPndlIHdvbid0IF9hcHBseV8gdGhlc2UgZmlsdGVyaW5nIGNyaXRlcmlhIHJpZ2h0IG5vdywgYmVjYXVzZSB3ZSBoYXZlIG90aGVyIG1ldHJpY3MgdG8gY29uc2lkZXIuPC9mb250Pgo8L2NlbnRlcj4KCiMgVGVjaG5pY2FsIGFuZCBiaW9sb2dpY2FsIG1ldHJpY3MKCkhlcmUgd2Ugd2lsbCBidWlsZCAzIG5ldyBtZXRyaWNzIHRoYXQgY29ycmVzcG9uZCB0byB0aGUgZnJhY3Rpb24gb2YgZXhwcmVzc2lvbiAoaW4gY291bnRzKSBvZiB0aHJlZSBjYXRlZ29yaWVzIG9mIGdlbmUgc2lnbmF0dXJlcyBhbW9uZyB0aGUgZ2xvYmFsIGV4cHJlc3Npb24uIFRoZXNlIGFyZQoKKiAqKk1pdG9jaG9uZHJpYWwqKiBnZW5lcyA6IEEgZ29vZCBwcm94eSB0byBkaXNwbGF5IGNlbGxzIGluIGJhZCBjb25kaXRpb24gKGhpZ2ggbGV2ZWwgb2YgZXhwcmVzc2lvbiBmb3IgdGhvc2UgZ2VuZXMgaW5kaWNhdGVzIHRoYXQgdGhlIGNlbGwgb3V0ZXIgbWVtYnJhbmUgbHlzaXMgd2VudCB0byBmYXIgYW5kIGFsc28gYmVnYW4gZm9yIGludGVybmFsIG9yZ2FuaXRlcykuCiogR2VuZXMgY29kaW5nIGZvciAqKnJpYm9zb21hbCBwcm90ZWlucyoqIChhbmQgbm90IF9yaWJvc29tYWwgZ2VuZXNfIGFzIG9mdGVuIHVzZWQgYXMgYSBiYWQgc2hvcnRjdXQpIDogSGlnaCBleHByZXNzaW9uIG9mIHRoZXNlIHdpdG5lc3NlcyBvZiBoaWdoZXIgbWV0YWJvbGljIHJhdGVzIGZvciBjb25jZXJuZWQgY2VsbHMsIHRodXMgaXQgbWF5IGJlIHVzZWZ1bCB0byByZWdyb3VwIGNlbGxzIG9mIGEgc2FtZSBjZWxsIHR5cGUgYnV0IHdpdGggZGlmZmVyZW50IG1ldGFib2xpYyBzdGF0ZXMgKGxpbmtlZCB0byBjZWxsIGRpdmlzaW9uLCBieSBleGFtcGxlKS4gKipIb3dldmVyKiosIGRpZmZlcmVudCBjZWxsIHR5cGVzIG1heSBuYXR1cmFsbHkgaGF2ZSBkZWZhdWx0IGRpZmZlcmVudCBtZXRhYm9saWMgcmF0ZXMgYnkgZGVmYXVsdCAuLi4KKiAqKk1lY2hhbmljYWwgc3RyZXNzKiogZ2VuZXMgOiBJbiBkcm9wbGV0LWJhc2VkIHByb3RvY29scywgZHVyaW5nIHRoZSBzZXBhcmF0aW9uLCBjZWxscyBmbG93IHRocm91Z2ggYSBtaWNyb2ZsdWlkaWMgY2lyY3VpdCwgZHVyaW5nIHdoaWNoIHRoZXkgcGFzcyB0aHJvdWdoIGhhcmQgYm90dGxlbmVja3MgY29tcGFyZWQgdG8gdGhlaXIgZGlhbWV0ZXIuIEZvciB2ZXJ5IGZyYWdpbGUgY2VsbHMsIHRoaXMgbWF5IGhhdmUgYW4gaW1wYWN0IGluIHRoZWlyIGdlbmUgZXhwcmVzc2lvbi4gX0FjdHVhbGx5LCBmcmFnaWxlIGNlbGxzIGFyZSBtb3N0IG9mdGVuIGNvbXBsZXRlbHkgbG9zdC9kZXN0cm95ZWQgaW4gdGhlIHByb2Nlc3MsIHNvIHRoZSBpbnRlcmVzdCBvZiB0aGlzIG1ldHJpYyBpcyBvZnRlbiB2ZXJ5IGxpbWl0ZWQgLi4uIGJ1dCBpdCBtYXkgYmUgaW4gc29tZSBjYXNlcyAhXwoKIyMgQ29tcHV0ZSBtZXRyaWNzCgpXZSB3aWxsIG5lZWQgYSBmdW5jdGlvbiB0aGF0IGVhc2VzIHRoZSBjb21wdXRhdGlvbmFsIHN0ZXAgZm9yIHlvdSA6CgpgYGB7ciBoX2NvdW50X3JhdGVfbWV0cmljLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KIyMgUmVhZGluZyB0aGUgZnVuY3Rpb24gaGVscCBwYWdlCj9FQkFJSS5uMS5TQy5oZWxwZXI6OmNvdW50X3JhdGVfbWV0cmljCmBgYAoKPEJSPjxCUj4KCjo6OiB7fQo6Ojogey5jb2x1bW4gd2lkdGg9IjMzJSJ9Cgo8Y2VudGVyPjxmb250IHNpemU9IjQiPioqJU1JVE8qKjwvZm9udD48L2NlbnRlcj4KCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBjb21wX21pdG99CiMjIExvYWQgdGhlIGdlbmVsaXN0Cm1pdG9fc3ltYm9scyA8LSByZWFkUkRTKGZpbGUgPSAnLi4vUkVTT1VSQ0VTL0dFTkVMSVNUUy9tdXNfbXVzY3VsdXNfbWl0b19zeW1ib2xzXzIwMTkxMDE1LnJkcycpCiMjIENvbXB1dGUgdGhlIG1ldHJpYwpzb2JqJHBlcmNlbnRfbXQgPC0gRUJBSUkubjEuU0MuaGVscGVyOjpjb3VudF9yYXRlX21ldHJpYygKICBzb2JqID0gc29iaiwKICBmZWF0dXJlcyA9IG1pdG9fc3ltYm9scywKICBhc3NheSA9ICdSTkEnKQojIyBTdW1tYXJpemUgdGhlIGNvbXB1dGVkIHZhbHVlcwpzdW1tYXJ5KG9iamVjdCA9IHNvYmokcGVyY2VudF9tdCkKYGBgCgo6OjoKOjo6IHsuY29sdW1uIHdpZHRoPSIzMyUifQoKPGNlbnRlcj48Zm9udCBzaXplPSI0Ij4qKiVSSUJPKio8L2ZvbnQ+PC9jZW50ZXI+Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgY29tcF9yaWJvfQojIyBMb2FkIHRoZSBnZW5lbGlzdApyaWJvX3N5bWJvbHMgPC0gcmVhZFJEUyhmaWxlID0gJy4uL1JFU09VUkNFUy9HRU5FTElTVFMvbXVzX211c2N1bHVzX2NyaWJvX3N5bWJvbHNfMjAxOTEwMTUucmRzJykKIyMgQ29tcHV0ZSB0aGUgbWV0cmljCnNvYmokcGVyY2VudF9yYiA8LSBFQkFJSS5uMS5TQy5oZWxwZXI6OmNvdW50X3JhdGVfbWV0cmljKAogIHNvYmogPSBzb2JqLAogIGZlYXR1cmVzID0gcmlib19zeW1ib2xzLAogIGFzc2F5ID0gJ1JOQScpCiMjIFN1bW1hcml6ZSB0aGUgY29tcHV0ZWQgdmFsdWVzCnN1bW1hcnkob2JqZWN0ID0gc29iaiRwZXJjZW50X3JiKQoKYGBgCgo6OjoKOjo6IHsuY29sdW1uIHdpZHRoPSIzMyUifQoKPGNlbnRlcj48Zm9udCBzaXplPSI0Ij4qKiVTVFJFU1MqKjwvZm9udD48L2NlbnRlcj4KCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBjb21wX3N0cmVzc30KIyMgTG9hZCB0aGUgZ2VuZWxpc3QKc3RyZXNzX3N5bWJvbHMgPC0gcmVhZFJEUyhmaWxlID0gJy4uL1JFU09VUkNFUy9HRU5FTElTVFMvbXVzX211c2N1bHVzX3N0cmVzc19zeW1ib2xzXzIwMjAwMjI0LnJkcycpCiMjIENvbXB1dGUgdGhlIG1ldHJpYwpzb2JqJHBlcmNlbnRfc3QgPC0gRUJBSUkubjEuU0MuaGVscGVyOjpjb3VudF9yYXRlX21ldHJpYygKICBzb2JqID0gc29iaiwKICBmZWF0dXJlcyA9IHN0cmVzc19zeW1ib2xzLAogIGFzc2F5ID0gJ1JOQScpCiMjIFN1bW1hcml6ZSB0aGUgY29tcHV0ZWQgdmFsdWVzCnN1bW1hcnkob2JqZWN0ID0gc29iaiRwZXJjZW50X3N0KQpgYGAKCjo6Ogo6OjoKCiMjIFZpc3VhbGl6YXRpb24gYW5kIHRocmVzaG9sZHMKCldlIGNhbiB0aGVuIHZpc3VhbGl6ZSB0aGVzZSBtZXRyaWNzIGFzIHZpb2xpbnMgKGFzIHdlbGwgYXMgb2xkZXIgb25lcykKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciB2bG5fbXJzLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KU2V1cmF0OjpWbG5QbG90KG9iamVjdCA9IHNvYmosIGZlYXR1cmVzID0gYygKICAnbkZlYXR1cmVfUk5BJywgJ25Db3VudF9STkEnLCAKICAnbG9nMTBfbkNvdW50X1JOQScsICdwZXJjZW50X210JywgCiAgJ3BlcmNlbnRfcmInLCAncGVyY2VudF9zdCcpLCAKICBuY29sID0gMykKYGBgCgpEaWQgeW91IG5vdGljZSBJIHByZWZlcnJlZCBoaXN0b2dyYW1zID8gOikKCjxCUj48QlI+Cgo6Ojoge30KOjo6IHsuY29sdW1uIHdpZHRoPSIzMyUifQoKPGNlbnRlcj48Zm9udCBzaXplPSI0Ij4qKiVNSVRPKio8L2ZvbnQ+PC9jZW50ZXI+Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgbWl0b19oMSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDgsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQpoaXN0KHggPSBzb2JqJHBlcmNlbnRfbXQsIGJyZWFrcyA9IDEwMDApCmBgYAoKSGFyZCB0byByZWFkLCBsZXQncyBzbGFzaCBpdCB0byBmb2N1cyBvbiBsb3dlciB2YWx1ZXMKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBtaXRvX2gyLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOCwgZmlnLmFsaWduID0gJ2NlbnRlcid9CiMjIFJlc3RyaWN0IHRvIGZpcnN0IDEwJQpoaXN0KHggPSBzb2JqJHBlcmNlbnRfbXQsIAogICAgIGJyZWFrcyA9IDEwMDAsIAogICAgIHhsaW0gPSBjKDAsLjEpKQpgYGAKCkxldCdzIG9ic2VydmUgdGhlICVtaXRvIGRpc3RyaWJ1dGlvbiBpbiB0aGUgZGF0YSBzcGFjZQoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIG1pdG9fdml6LCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMgUGVyZm9ybWluZyB0aGUgInF1aWNrIHZpeiIgYW5kIHNhdmluZyAKIyMgaXQgKHRvIHNwZWVkIHVwIG5leHQgcGxvdHMpClZJWiA8LSBFQkFJSS5uMS5TQy5oZWxwZXI6OlFuRF92aXooCiAgc29iaiA9IHNvYmosIAogIGZlYXR1cmVzID0gJ3BlcmNlbnRfbXQnLAogIHJldHVybl9vYmplY3QgPSBUUlVFKQpgYGAKCkxldCdzIHNldCB0aGlzICVtaXRvIHNlbGVjdGlvbiByYW5nZSB0byBbMCwgNSVdIGFuZCBkaXNwbGF5IGl0Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgcGNtaXRvcmFuZ2UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA4LCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMgU2V0dGluZyAlbWl0byBjb25zZXJ2YXRpb24gcmFuZ2UKcGNfbWl0b19yYW5nZSA8LSBjKDAsIC4wNSkKIyBwY19taXRvX3JhbmdlIDwtIGMoMCwgLjA0KQojIyBSZXBsb3QgaGlzdG9ncmFtIHdpdGggY3V0b2ZmcwpoaXN0KHggPSBzb2JqJHBlcmNlbnRfbXQsIAogICAgIGJyZWFrcyA9IDEwMDAsIAogICAgIHhsaW0gPSBjKDAsLjEpKQphYmxpbmUodiA9IHBjX21pdG9fcmFuZ2UsIAogICAgICAgbHR5ID0gMywgCiAgICAgICBjb2wgPSAicmVkIikKYGBgCgo6OjoKOjo6IHsuY29sdW1uIHdpZHRoPSIzMyUifQoKPGNlbnRlcj48Zm9udCBzaXplPSI0Ij4qKiVSSUJPKio8L2ZvbnQ+PC9jZW50ZXI+Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3Igcmlib19oMSwgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDgsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQpoaXN0KHggPSBzb2JqJHBlcmNlbnRfcmIsIGJyZWFrcyA9IDEwMDApCmBgYAoKTGV0J3Mgb2JzZXJ2ZSB0aGUgJXJpYm8gZGlzdHJpYnV0aW9uIGluIHRoZSBkYXRhIHNwYWNlCgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3Igcmlib192aXosIGZpZy5hbGlnbiA9ICdjZW50ZXInfQojIyBQZXJmb3JtaW5nIHRoZSAicXVpY2sgdml6IiBmYXN0ZXIgd2l0aCAKIyMgdGhlICJWSVoiIFNldXJhdCBvYmplY3QKRUJBSUkubjEuU0MuaGVscGVyOjpRbkRfdml6KAogIHNvYmogPSBWSVosIAogIHNsb3QgPSBOVUxMLCBkaW1yZWQgPSAndW1hcCcsCiAgZmVhdHVyZXMgPSAncGVyY2VudF9yYicpCmBgYAoKV2UgZG9uJ3QgaGF2ZSBhbnkgY2x1ZSB0byBjb25zaWRlciB0aGlzZSAlcmlibyB2YXJpYXRpb25zIGFzIGJpb2xvZ2ljYWwgb3IgdGVjaG5pY2FsIGFydGVmYWN0cy4uLiBTbyB3ZSB3aWxsIGtlZXAgYWxsIHZhbHVlcyA6IFswLCAxXQoKYGBge3IgcGNyaWJvcmFuZ2UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA4LCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMgU2V0dGluZyAlcmlibyBjb25zZXJ2YXRpb24gcmFuZ2UKcGNfcmlib19yYW5nZSA8LSBjKDAsIDEpCmBgYAoKOjo6Cjo6OiB7LmNvbHVtbiB3aWR0aD0iMzMlIn0KCjxjZW50ZXI+PGZvbnQgc2l6ZT0iNCI+KiolU1RSRVNTKio8L2ZvbnQ+PC9jZW50ZXI+Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3Igc3RyZXNzX2gxLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOCwgZmlnLmFsaWduID0gJ2NlbnRlcid9Cmhpc3QoeCA9IHNvYmokcGVyY2VudF9zdCwgYnJlYWtzID0gMTAwMCkKYGBgCgpMZXQncyBvYnNlcnZlIHRoZSAlc3RyZXNzIGRpc3RyaWJ1dGlvbiBpbiB0aGUgZGF0YSBzcGFjZQoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIHN0cmVzc192aXosIGZpZy5hbGlnbiA9ICdjZW50ZXInfQojIyBQZXJmb3JtaW5nIHRoZSAicXVpY2sgdml6IiBmYXN0ZXIgd2l0aCAKIyMgdGhlICJWSVoiIFNldXJhdCBvYmplY3QKRUJBSUkubjEuU0MuaGVscGVyOjpRbkRfdml6KAogIHNvYmogPSBWSVosIAogIHNsb3QgPSBOVUxMLCBkaW1yZWQgPSAndW1hcCcsCiAgZmVhdHVyZXMgPSAncGVyY2VudF9zdCcpCmBgYAoKTGV0J3Mgc2V0IHRoaXMgJXN0cmVzcyBzZWxlY3Rpb24gcmFuZ2UgdG8gWzAsIDYlXSBhbmQgZGlzcGxheSBpdAoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIHBjc3RyZXNzcmFuZ2UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA4LCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMgU2V0dGluZyAlc3RyZXNzIGNvbnNlcnZhdGlvbiByYW5nZQpwY19zdHJlc3NfcmFuZ2UgPC0gYygwLCAuMDYpCiMjIFJlcGxvdCBoaXN0b2dyYW0gd2l0aCBjdXRvZmZzCmhpc3QoeCA9IHNvYmokcGVyY2VudF9zdCwgCiAgICAgYnJlYWtzID0gMTAwMCwgCiAgICAgeGxpbSA9IGMoMCwuMSkpCmFibGluZSh2ID0gcGNfc3RyZXNzX3JhbmdlLCBsdHkgPSAzLCAKICAgICAgIGNvbCA9ICJyZWQiKQpgYGAKCjo6Ogo6OjoKCldlIGNhbiBkZWxldGUgdGhlIFZJWiBvYmplY3QKCmBgYHtyIGRlbHZpen0Kcm0oVklaKQpgYGAKCjxicj48YnI+CgoqKipCdXQqKiogd2UncmUgbm90IGRvbmUgeWV0ICEKClNpbmdsZSBjZWxsIGRhdGEgZnJvbSBkcm9wbGV0LWJhc2VkIHRlY2hub2xvZ2llcyBjYW4gYmUgYmlhc2VkIGluIHNvIG1hbnkgd2F5cy4uLiAKCkV2YWx1YXRpbmcgdGhlaXIgcXVhbGl0eSwgaW5jcmVhc2luZyB0aGUga25vd2xlZGdlIG9mIGJpYXNlcyBhZmZlY3RpbmcgdGhlIHZhbHVlcyBpcyBtYW5kYXRvcnkgdG8gcGVyZm9ybSBhbiBhbmFseXNpcyBvZiBxdWFsaXR5ICEKCiMgQ2VsbCBjeWNsZSBzY29yZXMKCkFzIHdlIGFyZSBhbmFseXNpcyBjZWxscyBpbmRlcGVuZGVudGx5LCB0aGV5IG1heSBlYWNoIGJlIGluIGEgZGlmZmVyZW50IHBoYXNlIG9mIHRoZWlyIGNlbGwgY3ljbGUuIEludGVyZXN0aW5nbHksIHRoZSBlZmZlY3Qgb2YgdGhpcyBjZWxsIGN5Y2xlIHN0ZXAgb24gdGhlIGNlbGwgZ2VuZXMgZXhwcmVzc2lvbiBpcyBzdHJvbmcsIGFuZCBjYW4gYmlhcyB0aGUgZGF0YS4gSW4gb3JkZXIgdG8gYXNzZXNzIChhbmQgbWF5YmUsIHJlbW92ZSkgdGhpcyBiaWFzLCB3ZSBoYXZlIHRvIHF1YW50aWZ5IGl0LgoKV2Ugd2lsbCBwZXJmb3JtIHRoaXMgZXN0aW1hdGlvbiB0aGFua3MgdG8gaGV1cmlzdGljcyBiYXNlZCBvbiBrbm93bGVkZ2UgOiBTZXVyYXQgaW5jbHVkZXMgYSBtZXRob2QgdGhhdCBldmFsdWF0ZXMgdGhlIGNlbGwgY3ljbGUgcGhhc2Ugb2YgY2VsbHMgdGhyb3VnaCBzY29yZXMgZm9yIHRoZSBTIGFuZCBHMk0gcGhhc2VzLCBlYWNoIGJhc2VkIG9uIHBoYXNlLXNwZWNpZmljIGdlbmUgc2lnbmF0dXJlcy4KClNvbWUgb2YgdGhlc2UgcmVxdWlyZSB0aGUgZXZhbHVhdGlvbiBvZiBnZW5lcyB3aGljaCBleHByZXNzaW9uIGlzIGV4cGVjdGVkIHRvIGJlIG51bGwgOiB0aGF0J3MgdGhlIHJlYXNvbiB3aHkgKip3ZSBkaWQgbm90IGZpbHRlciBvdXQgZm9yIHVuZXhwcmVzc2VkIGdlbmVzIHRpbGwgbm93KiouCgoKIyMgRXN0aW1hdGlvbgoKTGV0J3MgcGVyZm9ybSB0aGlzIGVzdGltYXRpb24uIEJ1dCBob3cgPwoKYGBge3IgaF9DZWxsQ3ljbGVTY29yaW5nLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KIyMgUmVhZGluZyB0aGUgZnVuY3Rpb24gaGVscCBwYWdlCj9TZXVyYXQ6OkNlbGxDeWNsZVNjb3JpbmcKYGBgCgpXZSB3aWxsIGFjdHVhbGx5IHVzZSBhIGhlbHBlciBmdW5jdGlvbiB0byBlYXNlIHVwIHRoZSBwcm9jZXNzIDoKCmBgYHtyIENDX1NldXJhdCwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyJ9Cj9FQkFJSS5uMS5TQy5oZWxwZXI6OkNDX1NldXJhdApgYGAKClJ1biBpdCAhCgpgYGB7ciBjZWxsY3ljbGV9CiMjIExvYWQgdGhlIGNlbGwgY3ljbGUgcmVmZXJlbmNlIGdlbmVzIGxpc3RzCnNldS5jYyA8LSByZWFkUkRTKGZpbGUgPSAnLi4vUkVTT1VSQ0VTL0dFTkVMSVNUUy9tdXNfbXVzY3VsdXNfU2V1cmF0X2NjLmdlbmVzXzIwMTkxMDMxLnJkcycpCiMjIFBlcmZvcm0gdGhlIGVzdGltYXRpb24Kc29iaiA8LSBFQkFJSS5uMS5TQy5oZWxwZXI6OkNDX1NldXJhdCgKICBzb2JqID0gc29iaiwgYXNzYXkgPSAnUk5BJywgCiAgc2V1cmF0X2NjX2dlbmVzID0gc2V1LmNjLCBTbUcyTSA9IFRSVUUsIAogIG5iaW4gPSAyMCwgbXlfc2VlZCA9IG15X3NlZWQpCmBgYAoKRGVzY3JpcHRpb24gb2YgdGhlIG9iamVjdCB0byBzZWUgdGhlIGRhdGEgYWRkZWQKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBkZXNjX2NjfQpFQkFJSS5uMS5TQy5oZWxwZXI6OnNldXJhdDRfZGVzY3JpcHRvcigKICBzb2JqID0gc29iaiwKICBkZXNjcmliZSA9ICdjb2xkYXRhJwopCmBgYAoKCiMjIFZpc3VhbGl6YXRpb24KCkFzIHVzdWFsLCB3ZSBjYW4gdmlzdWFsaXplIHRoZSByZXN1bHRzIGFzIHZpb2xpbnMgOgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIHZsbmNjLCBmaWcud2lkdGggPSAxMn0KU2V1cmF0OjpWbG5QbG90KG9iamVjdCA9IHNvYmosCiAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGMoJ0NDX1NldXJhdF9TLlNjb3JlJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnQ0NfU2V1cmF0X0cyTS5TY29yZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0NDX1NldXJhdF9TbUcyTS5TY29yZScpKQpgYGAKCkJ1dCBpdCdzIG5vdCB0aGF0IGVhc3kgdG8gaW50ZXJwcmV0Li4uIExldCdzIHBsb3QgaXQgaW4gdGhlIGNlbGwgc3BhY2UKCiogRm9yIHRoZSBTIG1pbnVzIEcyTSBzY29yZSA6Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3Igdml6Y2NzfQojIyBVc2luZyB0aGUgJ2ZlYXR1cmVzJyBwYXJhbWV0ZXIgdG8gcGxvdCB0aGUgU21HMk0gc2NvcmUgKGNvbnRpbnVvdXMgZGF0YSkKIyMgSGVyZSwgd2Ugd2lsbCBrZWVwIHRoZSBtb2RpZmllZCBTZXVyYXQgb2JqZWN0IHRvIHNwZWVkIHVwIGZ1cnRoZXIgcGxvdHMKVklaIDwtIEVCQUlJLm4xLlNDLmhlbHBlcjo6UW5EX3ZpeigKICBzb2JqID0gc29iaiwgCiAgZmVhdHVyZXMgPSAnQ0NfU2V1cmF0X1NtRzJNLlNjb3JlJywgCiAgcmV0dXJuX29iamVjdCA9IFRSVUUpCmBgYAoKKiBGb3IgdGhlIGVzdGltYXRlZCBjZWxsIHBoYXNlIDoKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciB2aXpjY3B9CiMjIFVzaW5nIHRoZSAnZ3JvdXBfYnknIHBhcmFtZXRlciB0byBwbG90IHRoZSBlc3RpbWF0ZWQgcGhhc2VzIChjYXRlZ29yaWNhbCBkYXRhKQojIyBIZXJlLCB3ZSByZWN5Y2xlIGtlZXAgdGhlIFZJWiBTZXVyYXQgb2JqZWN0IHRoYXQgY29udGFpbnMgZXZlcnl0aGluZyB0byBwZXJmb3JtIHRoZSBwbG90IHdpdGhvdXQgY29tcHV0aW5nIGl0IGFnYWluCkVCQUlJLm4xLlNDLmhlbHBlcjo6UW5EX3Zpeihzb2JqID0gVklaLCBzbG90ID0gTlVMTCwgZGltcmVkID0gJ3VtYXAnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5ID0gJ0NDX1NldXJhdF9QaGFzZScpCiMjIFZJWiBpcyBub3QgbmVlZGVkIGFueW1vcmUKcm0oVklaKQpgYGAKCioqKlF1ZXN0aW9uMSoqKiA6IERvZXMgdGhlIHN0cnVjdHVyZSBvZiB0aGUgY2VsbHMgaW4gdGhpcyByZXByZXNlbnRhdGlvbiBzZWVtIHRvIGhhdmUgYSBsaW5rIHdpdGggdGhlc2UgY2VsbCBjeWNsZSBwaGFzZXMvc2NvcmVzID8KCmBgYHtyIHFjZWxsMSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CiMjIEl0J3MgYW4gYWJzb2x1dGUgeWVzICEKYGBgCgoqKipRdWVzdGlvbjIqKiogOiBEbyB5b3UgdGhpbmsgdGhpcyBpcyB0aGUgcmVzdWx0IG9mIGFuIGFydGlmYWN0LCBvciBiaW9sb2d5LXJlbGF0ZWQgPwoKPGNlbnRlcj48YnV0dG9uIG9uY2xpY2s9IlNob3dBbmRIaWRlKCkiPkFuc3dlcjwvYnV0dG9uPjwvY2VudGVyPgo8ZGl2IGlkPSJTZWN0aW9uTmFtZSIgc3R5bGU9ImRpc3BsYXk6bm9uZSI+PGNlbnRlcj4hW10oY2x1ZS5qcGVnKTwvY2VudGVyPjwvZGl2PgoKPEJSPjxCUj4KTm93LCB3ZSBmaW5hbGx5IGhhdmUgYWxsIHRoZSBtZXRyaWNzIGFuZCBiaWFzIHNvdXJjZXMgd2UgY291bGQgdXNlLCBzbyB3ZSBjYW4gYWN0dWFsbHkgZ2V0IHRvIHRoZSBmaWx0ZXJpbmcgc3RlcC4KCiMgRmlsdGVyaW5nCgojIyBGZWF0dXJlcwoKQXMgYWxyZWFkeSBleHBsYWluZWQsIHRoZSBzcGFyY2l0eSBvZiB0aGUgY291bnQgbWF0cml4IGlzIGhpZ2gsIHZlcnkgaGlnaC4gVG8gdGhlIHBvaW50IHRoYXQgdGhlcmUgcHJvYmFibHkgYXJlIHNvbWUgZmVhdHVyZXMgdGhhdCBoYXZlIHNvIGxvdyBleHByZXNzaW9uIGxldmVsIHRoYXQgdGhleSB3ZXJlIG9ubHkgY291bnRlZCBpbiBhIHZlcnkgZmV3IGNlbGxzLiBBcyBzdWNoLCB0aGV5IGhhdmUgYWJzb2x1dGVseSBubyBjaGFuY2UgdG8gY2hhcmFjdGVyaXplIGFueSBjZWxsIHR5cGUsIG5vciBoYXJib3Igc29tZSB2YXJpYXRpb24gYmV0d2VlbiBkaWZmZXJlbnQgY2VsbCB0eXBlcy4KCkFzIHN1Y2gsIHdlIGNhbiBkaXNjYXJkIHRoZXNlIGZlYXR1cmVzCgpXaGF0IGFyZSB0aGUgYWN0dWFsIGRpbWVuc2lvbnMgb2Ygb3VyIGNvdW50IG1hdHJpeCA/Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3Igc2V1ZGltMX0KZGltKHNvYmopCmBgYAoKV2Ugd2FudCB0byByZW1vdmUgdGhlIGZlYXR1cmVzIHRoYXQgYXJlIGV4cHJlc3NlZCBpbiBsZXNzIHRoYW4gKio1KiogY2VsbHMuCgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgbmZlYXRkaWZmfQojIyBDb21wdXRpbmcgdGhlIDBzIHBlciBmZWF0dXJlCm5GZWF0X3plcm8gPC0gc3BhcnNlTWF0cml4U3RhdHM6OnJvd0NvdW50cygKICB4ID0gU2V1cmF0OjpHZXRBc3NheURhdGEoCiAgICBvYmplY3QgPSBzb2JqLCAKICAgIGFzc2F5ID0gJ1JOQScsIAogICAgc2xvdCA9ICdjb3VudHMnKSwgCiAgdmFsdWUgPSAwKQpuRmVhdF9ub256ZXJvIDwtIG5jb2woc29iaikgLSBuRmVhdF96ZXJvCiMjIFN1bW1hcnkKc3VtbWFyeShuRmVhdF9ub256ZXJvKQpgYGAKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBzZGltfQojIyBJZGVudGlmeWluZyB0aG9zZSB3aXRoIGF0IGxlYXN0IHRoYW4gNSBjZWxscyB3aXRoIGV4cHJlc3Npb24KbkZlYXRfa2VlcCA8LSBuRmVhdF9ub256ZXJvID49IDUKYGBgCgpgYGB7ciBmZWF0c2VsfQojIyBSZXN0cmljdCB0aGUgU2V1cmF0IG9iamVjdApzb2JqIDwtIHNvYmpbbkZlYXRfa2VlcCxdCiMjIFF1YW50aWZ5IHRoZSBzZWxlY3RlZCBvbmVzCnRhYmxlKG5GZWF0X2tlZXApCmBgYAoKV2hhdCBhcmUgdGhlIFNldXJhdCBvYmplY3QgZGltZW5zaW9ucywgbm93ID8KCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBzZXVkaW0yfQpkaW0oc29iaikKYGBgCgpXZSBjYW4gdmlzdWFsaXplIHRoZSBjZWxsIHNwYWNlIGFmdGVyIHRoaXMgZmVhdHVyZXMgZmlsdGVyaW5nCgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgcG9zdGZlYXRmaWx0dml6fQpFQkFJSS5uMS5TQy5oZWxwZXI6OlFuRF92aXooCiAgc29iaiA9IHNvYmosIAogIGdyb3VwX2J5ID0gJ0NDX1NldXJhdF9QaGFzZScpCmBgYAoKKipRdWVzdGlvbioqIDogRG8geW91IHNlZSBhbnkgZGlmZmVyZW5jZSB3aGVuIGNvbXBhcmluZyBiZWZvcmUgdnMgYWZ0ZXIgZmVhdHVyZXMgZmlsdGVyaW5nID8KCmBgYHtyIHFfcG9zdGZlYXRmaWx0LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KIyMgLiBOb3QgbXVjaCBjaGFuZ2VkLCBtYXliZSBjZWxsIGdyb3VwcyBzZWVtIGEgYml0IG1vcmUgY29uZGVuc2VkCiMjIC4gVGhpcyBpcyBleHBlY3RlZCwgYXMgd2UgcmVtb3ZlZCBmZWF0dXJlcyB3aXRoIGFsbW9zdCBubyBleHByZXNzaW9uCmBgYAoKPEJSPjxCUj4KCiMjIENlbGxzCgpXZSBhcmUgbm93IGFibGUgdG8gYXBwbHkgYWxsIHRoZSBmaWx0ZXJpbmcgc3RyYXRlZ2llcyB3ZSBlc3RhYmxpc2hlZCBmb3IgZWFjaCBRQyBtZXRyaWMuCgpBcyBhIHJlbWluZGVyLCBoZXJlIGFyZSB0aGUgbWV0cmljcyB0aHJlc2hvbGRzIHdlIHNldAoKYGBge3IgbWV0cmljc30KIyMgRmVhdHVyZV9STkEKcHJpbnQobmZlYXR1cmVfcmFuZ2UpCiMjIG5Db3VudF9STkEKcHJpbnQobmNvdW50X3JhbmdlKQojIyAlbWl0bwpwcmludChwY19taXRvX3JhbmdlKQojIyAlcmlibwpwcmludChwY19yaWJvX3JhbmdlKQojIyAlc3RyZXNzCnByaW50KHBjX3N0cmVzc19yYW5nZSkKYGBgCgpTZWxlY3RpbmcgImdvb2QiIGNlbGxzCgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3J9CiMjIEFwcGx5aW5nIFFDIG1ldHJpY3MgdGhyZXNob2xkcwpiY19rZWVwIDwtIHNvYmokbkZlYXR1cmVfUk5BID4gbmZlYXR1cmVfcmFuZ2VbMV0gJiAKICBzb2JqJG5GZWF0dXJlX1JOQSA8IG5mZWF0dXJlX3JhbmdlWzJdICYgCiAgc29iaiRuQ291bnRfUk5BID4gbmNvdW50X3JhbmdlWzFdICYgCiAgc29iaiRuQ291bnRfUk5BIDwgbmNvdW50X3JhbmdlWzJdICYgCiAgc29iaiRwZXJjZW50X210ID4gcGNfbWl0b19yYW5nZVsxXSAmIAogIHNvYmokcGVyY2VudF9tdCA8IHBjX21pdG9fcmFuZ2VbMl0gJiAKICBzb2JqJHBlcmNlbnRfbXQgPiBwY19yaWJvX3JhbmdlWzFdICYgCiAgc29iaiRwZXJjZW50X210IDwgcGNfcmlib19yYW5nZVsyXSAmIAogIHNvYmokcGVyY2VudF9zdCA+IHBjX3N0cmVzc19yYW5nZVsxXSAmIAogIHNvYmokcGVyY2VudF9zdCA8IHBjX3N0cmVzc19yYW5nZVsyXQp0YWJsZShiY19rZWVwKQpgYGAKCkFwcGx5aW5nIHRoZSBmaWx0ZXIKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBjZWxsZmlsdH0Kc29iaiA8LSBzb2JqWywgYmNfa2VlcF0KZGltKHNvYmopCmBgYAoKV2UgY2FuIHZpc3VhbGl6ZSB0aGUgY2VsbCBzcGFjZSBzaW5jZSB0aGlzIGNlbGxzIGZpbHRlcmluZwoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIHBvc3RjZWxsZmlsdHZpen0KRUJBSUkubjEuU0MuaGVscGVyOjpRbkRfdml6KAogIHNvYmogPSBzb2JqLCAKICBncm91cF9ieSA9ICdDQ19TZXVyYXRfUGhhc2UnKQpgYGAKCioqUXVlc3Rpb24qKiA6IERvIHlvdSBzZWUgYW55IGRpZmZlcmVuY2Ugd2hlbiBjb21wYXJpbmcgYmVmb3JlIHZzIGFmdGVyIGZlYXR1cmVzIGZpbHRlcmluZyA/CgpgYGB7ciBxX3Bvc3RjZWxsZmlsdCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CiMjIC4gTm90IG11Y2ggY2hhbmdlZCBhcyB3ZWxsICh3ZSBkaWQgbm90IGRpc2NhcmQgbWFueSBjZWxscykKIyMgLiBUaGUgYmlnZ2VzdCBjbHVzdGVyIHN0cnVjdHVyZSBzZWVtcyBtb3JlIGRlZmluZWQgPwpgYGAKCgojIENlbGwgZG91YmxldHMKCldlIHdpbGwgdXNlIHR3byBkaWZmZXJlbnQgbWV0aG9kcyB0byBkZXRlY3QgYW5kIHJlbW92ZSBjZWxsIGRvdWJsZXRzIDoKCiogc2NkcyAoaW4gaXRzICJoeWJyaWQiIG1vZGUpCiogc2NEYmxGaW5kZXIKCiMjIHNjZHMKCmBgYHtyIHNjZHN9CiMjIEZpeCBzZWVkCnNldC5zZWVkKG15X3NlZWQpCiMjIFJ1biBzY2RzCnNvYmokZG91YmxldF9zY2RzLmh5YnJpZCA8LSB1bm5hbWUoCiAgc2Nkczo6Y3hkc19iY2RzX2h5YnJpZCgKICAgIFNldXJhdDo6YXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQoCiAgICAgIHNvYmosIGFzc2F5ID0gJ1JOQScpKSRoeWJyaWRfc2NvcmUgPiAxKQpgYGAKCiMjIHNjRGJsRmluZGVyCgpgYGB7ciBzY2RibH0KIyMgRml4IHNlZWQKc2V0LnNlZWQobXlfc2VlZCkKIyMgUnVuIHNjRGJsRmluZGVyCnNvYmokZG91YmxldF9zY0RibEZpbmRlciA8LSB1bm5hbWUoCiAgU2V1cmF0Ojphcy5TZXVyYXQoCiAgICBzY0RibEZpbmRlcjo6c2NEYmxGaW5kZXIoCiAgICAgIFNldXJhdDo6YXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQoCiAgICAgICAgeCA9IHNvYmosIAogICAgICAgIGFzc2F5ID0gJ1JOQScpKSkkc2NEYmxGaW5kZXIuY2xhc3MgPT0gJ2RvdWJsZXQnKQpgYGAKCiMjIE1lcmdlIHJlc3VsdHMKCldlIG1lcmdlIHJlc3VsdHMgdG8gaWRlbnRpZnkgdG9vbC1zcGVjaWZpYyBhbmQgY29tbW9uIGRvdWJsZXRzCgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgZGJsbWVyZ2V9CnNvYmokZG91YmxldF91bmlvbiA8LSBzb2JqJGRvdWJsZXRfc2Nkcy5oeWJyaWQgfCBzb2JqJGRvdWJsZXRfc2NEYmxGaW5kZXIKc29iaiRkb3VibGV0X3ZpeiA8LSAnc2luZ2xldCcKc29iaiRkb3VibGV0X3Zpeltzb2JqJGRvdWJsZXRfdW5pb25dIDwtICdib3RoJwpzb2JqJGRvdWJsZXRfdml6W3NvYmokZG91YmxldF9zY2RzLmh5YnJpZCAmICFzb2JqJGRvdWJsZXRfc2NEYmxGaW5kZXJdIDwtICdzY2RzJwpzb2JqJGRvdWJsZXRfdml6W3NvYmokZG91YmxldF9zY0RibEZpbmRlciAmICFzb2JqJGRvdWJsZXRfc2Nkcy5oeWJyaWRdIDwtICdzY0RibEZpbmRlcicKc29iaiRkb3VibGV0X3ZpeiA8LSBhcy5mYWN0b3Ioc29iaiRkb3VibGV0X3ZpeikKdGFibGUoc29iaiRkb3VibGV0X3ZpeikKYGBgCgojIyBSZW1vdmFsIGFuZCB2aXN1YWxpemF0aW9uCgpOb3cgd2UgY2FuIHJlbW92ZSBhbmQgdmlzdWFsaXplIHRoZSBjZWxsIHNwYWNlIHdpdGgvd2l0aG91dCBkb3VibGV0cwoKOjo6IGNvbHVtbnMKOjo6IGNvbHVtbgoKPGNlbnRlcj48Zm9udCBzaXplPSI0Ij4qKkJFRk9SRSoqPC9mb250PjwvY2VudGVyPgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIGRibHZpejF9CiMjIyBEb3VibGV0cyB2aXogKGJlZm9yZSByZW1vdmFsKQpFQkFJSS5uMS5TQy5oZWxwZXI6OlFuRF92aXooCiAgc29iaiA9IHNvYmosIAogIGdyb3VwX2J5ID0gJ2RvdWJsZXRfdml6JykKYGBgCgpMZXQncyByZW1vdmUgZG91YmxldHMgOgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIGRibHJtfQpzb2JqIDwtIHNvYmpbLCFzb2JqJGRvdWJsZXRfdW5pb25dCmRpbShzb2JqKQpgYGAKCjo6Ogo6OjogY29sdW1uCgo8Y2VudGVyPjxmb250IHNpemU9IjQiPioqQUZURVIgcmVtb3ZhbCoqPC9mb250PjwvY2VudGVyPgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIGRibHZpejJ9CiMjIyBEb3VibGV0cyB2aXogKGFmdGVyIHJlbW92YWwpCkVCQUlJLm4xLlNDLmhlbHBlcjo6UW5EX3ZpeigKICBzb2JqID0gc29iaiwgCiAgZ3JvdXBfYnkgPSAnZG91YmxldF92aXonKQpgYGAKCjo6Ogo6OjoKCiMgU2F2ZQoKV2UgY2FuIG5vdyBzYXZlIHRoZSByZXN1bHRzIG9mIG91ciBoYXJkIHdvcmsgIQoKX0J1dCwgaG93ID9fCgpgYGB7ciBoX3NhdmVSRFMsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQo/YmFzZTo6c2F2ZVJEUwpgYGAKCmBgYHtyIHNhdmUsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpzYXZlUkRTKG9iamVjdCA9IHNvYmosIAogICAgICAgIGZpbGUgPSAnLi9URDNBXzAxX0ZpbHRlcmVkLlJEUycsIAogICAgICAgIGNvbXByZXNzID0gJ2J6aXAyJykKYGBgCgoKPEJSPjxCUj48QlI+CgpfUnNlc3Npb25fCgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3J9CnV0aWxzOjpzZXNzaW9uSW5mbygpCmBgYAoKCjwhLS0gU2NyaXB0IHRvICBIaWRlL1Nob3cgb3V0cHV0cyAtLT4KPHNjcmlwdD4KJCggImlucHV0LmhpZGVzaG93IiApLmVhY2goIGZ1bmN0aW9uICggaW5kZXgsIGJ1dHRvbiApIHsKICBidXR0b24udmFsdWUgPSAnSGlkZSBPdXRwdXQnOwogICQoIGJ1dHRvbiApLmNsaWNrKCBmdW5jdGlvbiAoKSB7CiAgICB2YXIgdGFyZ2V0ID0gdGhpcy5uZXh0U2libGluZyA/IHRoaXMgOiB0aGlzLnBhcmVudE5vZGU7CiAgICB0YXJnZXQgPSB0YXJnZXQubmV4dFNpYmxpbmcubmV4dFNpYmxpbmcubmV4dFNpYmxpbmcubmV4dFNpYmxpbmc7CiAgICBpZiAoIHRhcmdldC5zdHlsZS5kaXNwbGF5ID09ICdibG9jaycgfHwgdGFyZ2V0LnN0eWxlLmRpc3BsYXkgPT0gJycgKSB7CiAgICAgIHRhcmdldC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnOwogICAgICB0aGlzLnZhbHVlID0gJ1Nob3cgT3V0cHV0JzsKICAgIH0gZWxzZSB7CiAgICAgIHRhcmdldC5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJzsKICAgICAgdGhpcy52YWx1ZSA9ICdIaWRlIE91dHB1dCc7CiAgICB9CiAgfSApOwp9ICk7CiQoICJpbnB1dC5oaWRlc2hvdyIgKS5jbGljaygpCjwvc2NyaXB0PgoKCjwhLS0gU2hvdy9oaWRlIEhUTUwgLS0+CjxTQ1JJUFQ+CmZ1bmN0aW9uIFNob3dBbmRIaWRlKCkgewogICAgdmFyIHggPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnU2VjdGlvbk5hbWUnKTsKICAgIGlmICh4LnN0eWxlLmRpc3BsYXkgPT0gJ25vbmUnKSB7CiAgICAgICAgeC5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJzsKICAgIH0gZWxzZSB7CiAgICAgICAgeC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnOwogICAgfQp9CjwvU0NSSVBUPgo=