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
PREREQUISITES
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 .
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
:
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 :
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 :
You can now create your output directory
You can now close the terminal.
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
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.
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 !
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
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
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
!
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 .
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
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 ?
Answer
Now, we finally have all the metrics and bias sources we
could use, so we can actually get to the filtering step.
Filtering
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 ?
[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 ?
[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
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 ?
Cell doublets
We will use two different methods to detect and remove cell doublets
:
scds (in its “hybrid” mode)
scDblFinder
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 )
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' )
Removal and
visualization
Now we can remove and visualize the cell space with/without
doublets
Save
We can now save the results of our hard work !
But, how ?
saveRDS (object = sobj,
file = './TD3A_01_Filtered.RDS' ,
compress = 'bzip2' )
Rsession
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=