This file describes the different steps to perform first part of data processing for the single cell RNAseq data analysis training course for the EBAII n1 2023, covering these steps : * Normalization of the raw counts * Scaling

1 Load the latest Seurat object

Start a Rstudio session


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

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

Load the latest Seurat object saved as RDS

But, how ?

?base::readRDS
sobj <- readRDS(file = './TD3A_01_Filtered.RDS')

2 Normalization

Until now, we manipulated a raw count matrix.

During QC / preprocessing, we observed that cells expression profiles had a wide variety in multiple metrics, be it their number of expressed genes, their amount of captured RNA molecules, their rate of expression involved in mitochondrion genes, etc…

Consequently, we have to harmonize the data to try to identify biological differences drowned into artefactual differences.

This is what is performed during normalization.

Here, we will use the two main normalization methods available in Seurat :

  • LogNormalization
  • SCT (variance-stabilized transformation, v2)

Let’s describe this modified Seurat object, especially the ‘data’ slot


We can visualize the modification performed through normalization using boxplots.

To understand why the LogNorm is not just a log-scale transformation of counts, we will display the raw counts in log scale, for comparison



RNA:counts (raw)

Boxplot :

boxplot(
  log10(as.matrix(
      Seurat::GetAssayData(
        object = sobj, 
        assay = 'RNA', 
        slot = 'counts'))[,1:10] +1),
  main = 'Counts', 
  xlab = 'Cells',
  ylab = 'Raw counts (log-scale)')



RNA:data (LogNorm)

Normalization :

How ?

?Seurat::NormalizeData
sobj <- Seurat::NormalizeData(
  object = sobj,
  method = 'LogNormalize',
  Sverbose = FALSE)

Description :

## Using our good ol' sobj
EBAII.n1.SC.helper::seurat4_descriptor(
  sobj = sobj, 
  describe = 'assay')
OBJECT VERSION :    5.0.0 
PROJECT :   [TD3A] 

[ASSAYS]
   ASSAY 1 :    [RNA] [ACTIVE] 
      SLOT 1 :  [counts]    Dims:[12508 x 4038]  Range:[0.00-1588.00] 
         Counts :   14263194 
         Sparsity : 86.87712% 
      SLOT 2 :  [data]  Dims:[12508 x 4038]  Range:[0.00-7.71] 
         Sparsity : 86.87712% 
      SLOT 3 :  [scale.data]    Dims:[0 x 0] 

Boxplot :

boxplot(
  as.matrix(
    Seurat::GetAssayData(
      object = sobj, 
      assay = 'RNA', 
      slot = 'data'))[,1:10],
  main = 'Data (LogNorm)', 
  xlab = 'Cells',
  ylab = 'LN values')

Visualization :

EBAII.n1.SC.helper::QnD_viz(
  sobj = sobj,
  assay = 'RNA',
  slot = 'data')

SCT:data (SCTv2)

Normalization :

How ?

?Seurat::SCTransform
sobjSCT <- Seurat::SCTransform(
  object = sobj, 
  vst.flavor = 'v2', 
  verbose = FALSE)

Description :

## Warning : here we're dealing with sobjSCT
EBAII.n1.SC.helper::seurat4_descriptor(
  sobj = sobjSCT, 
  describe = 'assay')
OBJECT VERSION :    5.0.0 
PROJECT :   [TD3A] 

[ASSAYS]
   ASSAY 1 :    [RNA] 
      SLOT 1 :  [counts]    Dims:[12508 x 4038]  Range:[0.00-1588.00] 
         Counts :   14263194 
         Sparsity : 86.87712% 
      SLOT 2 :  [data]  Dims:[12508 x 4038]  Range:[0.00-7.71] 
         Sparsity : 86.87712% 
      SLOT 3 :  [scale.data]    Dims:[0 x 0] 
   ASSAY 2 :    [SCT] [ACTIVE] 
      SLOT 1 :  [counts]    Dims:[12309 x 4038]  Range:[0.00-510.00] 
         Counts :   9723658 
         Sparsity : 88.36753% 
      SLOT 2 :  [data]  Dims:[12309 x 4038]  Range:[0.00-6.24] 
         Sparsity : 88.36753% 
      SLOT 3 :  [scale.data]    Dims:[3000 x 4038]  Range:[-3.98-11.68] 

Boxplot :

boxplot(as.matrix(
  Seurat::GetAssayData(
    object = sobjSCT, assay = 'SCT', 
    slot = 'data'))[,1:10], main = 'Data (SCTv2)', 
    xlab = 'Cells', ylab = 'SCTv2 values')

Visualization :

EBAII.n1.SC.helper::QnD_viz(
  sobj = sobjSCT,
  assay = 'SCT',
  slot = 'data')

Question : Does the normalization method have a high impact on the cell space ?

For further steps, we will keep using the LN version.

rm(sobjSCT)

3 Scaling

that is a standard pre-processing step prior to dimensional reduction techniques like PCA. The ScaleData() function:

Shifts the expression of each gene, so that the mean expression across cells is 0
Scales the expression of each gene, so that the variance across cells is 1
    This step gives equal weight in downstream analyses, so that highly-expressed genes do not dominate
The results of this are stored in pbmc[["RNA"]]$scale.data
By default, only variable features are scaled.
You can specify the features argument to scale additional features

Normalization considered the homogenization of expression values at the cell level.

Despite this, the next important step that is dimensional reduction is heavily sensitive to variations of the range of expression that remain in the normalized expression data, and that would impair their expected behavior.

Thus, we need to transform data in a harder way just for this dimensional reduction step, which is performed through scaling :

  • shifts the expression of each feature, so that the mean expression across cells is 0 (centralization)
  • scales the expression of each feature, so that the variance across cells is 1 (scaling)

This transformation gives equal weight to features in downstream analyses, so that highly-expressed genes do not dominate.

To be efficient in the single cell context, data scaling relies on a selection of features of importance.

By default, the metric used to select these is their contribution to the global variance of the expression matrix (then called “HVG”s, for “highly variable genes”).

Thus, the main parameter of importance at this step is the number of features used in this selection.

To assess this parameter importance, we will split the audience in 4 subgroups :

  • The 500’s
  • The 1000’s
  • The 2500’s
  • The 5000’s

But first… how ?

?Seurat::FindVariableFeatures
?Seurat::ScaleData



500

Feature selection :

sobj <- Seurat::FindVariableFeatures(
  object = sobj, 
  assay = 'RNA', 
  nfeatures = 500,
  verbose = FALSE)
## Show the last selected features
str(rev(sobj@assays$RNA@var.features))
 chr [1:500] "Gm2682" "Fdps" "Hist1h2bh" "Trav4-2" "Rpl10a" "Cxcl9" "Paics" ...

Scaling :

sobj <- Seurat::ScaleData(
  object = sobj, 
  assay = 'RNA', 
  verbose = FALSE)

Description :

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

[ASSAYS]
   ASSAY 1 :    [RNA] [ACTIVE] 
      SLOT 1 :  [counts]    Dims:[12508 x 4038]  Range:[0.00-1588.00] 
         Counts :   14263194 
         Sparsity : 86.87712% 
      SLOT 2 :  [data]  Dims:[12508 x 4038]  Range:[0.00-7.71] 
         Sparsity : 86.87712% 
      SLOT 3 :  [scale.data]    Dims:[500 x 4038]  Range:[-2.55-10.00] 

Visualization :

EBAII.n1.SC.helper::QnD_viz(
  sobj = sobj,
  assay = 'RNA',
  slot = 'scale.data')

1000

Feature selection :

sobj <- Seurat::FindVariableFeatures(
  object = sobj, 
  assay = 'RNA', 
  nfeatures = 1000,
  verbose = FALSE)
## Show the last selected features
str(rev(sobj@assays$RNA@var.features))
 chr [1:1000] "Fmc1" "Spin2c" "Traj39" "Mrps16" "Hist1h4a" "Cd72" "Ywhae" ...

Scaling :

sobj <- Seurat::ScaleData(
  object = sobj, 
  assay = 'RNA', 
  verbose = FALSE)

Description :

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

[ASSAYS]
   ASSAY 1 :    [RNA] [ACTIVE] 
      SLOT 1 :  [counts]    Dims:[12508 x 4038]  Range:[0.00-1588.00] 
         Counts :   14263194 
         Sparsity : 86.87712% 
      SLOT 2 :  [data]  Dims:[12508 x 4038]  Range:[0.00-7.71] 
         Sparsity : 86.87712% 
      SLOT 3 :  [scale.data]    Dims:[1000 x 4038]  Range:[-5.59-10.00] 

Visualization :

EBAII.n1.SC.helper::QnD_viz(
  sobj = sobj,
  assay = 'RNA',
  slot = 'scale.data')



2500

Feature selection :

sobj <- Seurat::FindVariableFeatures(
  object = sobj, 
  assay = 'RNA', 
  nfeatures = 2500,
  verbose = FALSE)
## Show the last selected features
str(rev(sobj@assays$RNA@var.features))
 chr [1:2500] "Rpgrip1" "St3gal6" "Zranb2" "Eif3f" "Dus1l" "Drg1" "Spats2l" ...

Scaling :

sobj <- Seurat::ScaleData(
  object = sobj, 
  assay = 'RNA', 
  verbose = FALSE)

Description :

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

[ASSAYS]
   ASSAY 1 :    [RNA] [ACTIVE] 
      SLOT 1 :  [counts]    Dims:[12508 x 4038]  Range:[0.00-1588.00] 
         Counts :   14263194 
         Sparsity : 86.87712% 
      SLOT 2 :  [data]  Dims:[12508 x 4038]  Range:[0.00-7.71] 
         Sparsity : 86.87712% 
      SLOT 3 :  [scale.data]    Dims:[2500 x 4038]  Range:[-5.76-10.00] 

Visualization :

EBAII.n1.SC.helper::QnD_viz(
  sobj = sobj,
  assay = 'RNA',
  slot = 'scale.data')

5000

Feature selection :

sobj <- Seurat::FindVariableFeatures(
  object = sobj, 
  assay = 'RNA', 
  nfeatures = 5000,
  verbose = FALSE)
## Show the last selected features
str(rev(sobj@assays$RNA@var.features))
 chr [1:5000] "Zfp365" "Zdhhc13" "Ndufs5" "Hibch" "Nelfcd" "Mrpl38" "Crem" ...

Scaling :

sobj <- Seurat::ScaleData(
  object = sobj, 
  assay = 'RNA', 
  verbose = FALSE)

Description :

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

[ASSAYS]
   ASSAY 1 :    [RNA] [ACTIVE] 
      SLOT 1 :  [counts]    Dims:[12508 x 4038]  Range:[0.00-1588.00] 
         Counts :   14263194 
         Sparsity : 86.87712% 
      SLOT 2 :  [data]  Dims:[12508 x 4038]  Range:[0.00-7.71] 
         Sparsity : 86.87712% 
      SLOT 3 :  [scale.data]    Dims:[5000 x 4038]  Range:[-5.87-10.00] 

Visualization :

EBAII.n1.SC.helper::QnD_viz(
  sobj = sobj,
  assay = 'RNA',
  slot = 'scale.data')



Actually, we will use the default value for LogNormalized data : 2000 HVGs

## Feature selection
sobj <- Seurat::FindVariableFeatures(
  object = sobj, 
  assay = 'RNA', 
  nfeatures = 5000,
  verbose = FALSE)
## Scaling
sobj <- Seurat::ScaleData(
  object = sobj, 
  assay = 'RNA', 
  verbose = FALSE)

We can look at the scaled distribution :

boxplot(
  as.matrix(
    Seurat::GetAssayData(
      object = sobj, 
      assay = 'RNA', 
      slot = 'scale.data'))[,1:10],
  main = 'Scaled Data (LogNorm)', 
  xlab = 'Cells',
  ylab = 'Scaled values')

4 Save

We can now save the results of our hard work !

saveRDS(object = sobj, 
        file = './TD3A_02_Scaled.2K.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] S4Vectors_0.40.1    Biobase_2.62.0      BiocGenerics_0.48.0

loaded via a namespace (and not attached):
  [1] matrixStats_1.0.0             spatstat.sparse_3.0-3        
  [3] bitops_1.0-7                  webshot_0.5.5                
  [5] doParallel_1.0.17             httr_1.4.7                   
  [7] RColorBrewer_1.1-3            Rgraphviz_2.46.0             
  [9] tools_4.3.1                   sctransform_0.4.1            
 [11] backports_1.4.1               DT_0.30                      
 [13] utf8_1.2.4                    R6_2.5.1                     
 [15] lazyeval_0.2.2                uwot_0.1.16                  
 [17] GetoptLong_1.0.5              withr_2.5.2                  
 [19] sp_2.1-1                      prettyunits_1.2.0            
 [21] gridExtra_2.3                 progressr_0.14.0             
 [23] cli_3.6.1                     spatstat.explore_3.2-5       
 [25] TSP_1.2-4                     labeling_0.4.3               
 [27] sass_0.4.7                    topGO_2.54.0                 
 [29] Seurat_4.4.0                  spatstat.data_3.0-3          
 [31] genefilter_1.84.0             ggridges_0.5.4               
 [33] pbapply_1.7-2                 foreign_0.8-85               
 [35] AnnotationForge_1.44.0        parallelly_1.36.0            
 [37] limma_3.58.1                  rstudioapi_0.15.0            
 [39] RSQLite_2.3.2                 shape_1.4.6                  
 [41] GOstats_2.68.0                generics_0.1.3               
 [43] ica_1.0-3                     spatstat.random_3.2-1        
 [45] crosstalk_1.2.0               dendextend_1.17.1            
 [47] dplyr_1.1.3                   GO.db_3.18.0                 
 [49] Matrix_1.6-1.1                fansi_1.0.5                  
 [51] abind_1.4-5                   lifecycle_1.0.3              
 [53] SoupX_1.6.2                   yaml_2.3.7                   
 [55] edgeR_4.0.1                   SummarizedExperiment_1.30.2  
 [57] glmGamPoi_1.14.0              SparseArray_1.2.0            
 [59] BiocFileCache_2.10.1          Rtsne_0.16                   
 [61] grid_4.3.1                    blob_1.2.4                   
 [63] promises_1.2.1                dqrng_0.3.1                  
 [65] ExperimentHub_2.10.0          crayon_1.5.2                 
 [67] shinydashboard_0.7.2          miniUI_0.1.1.1               
 [69] lattice_0.22-5                beachmat_2.18.0              
 [71] cowplot_1.1.1                 annotate_1.80.0              
 [73] KEGGREST_1.42.0               ComplexHeatmap_2.18.0        
 [75] pillar_1.9.0                  knitr_1.45                   
 [77] metapod_1.10.0                GenomicRanges_1.54.1         
 [79] rjson_0.2.21                  future.apply_1.11.0          
 [81] codetools_0.2-19              leiden_0.4.3                 
 [83] glue_1.6.2                    data.table_1.14.8            
 [85] vctrs_0.6.4                   png_0.1-8                    
 [87] spam_2.10-0                   gtable_0.3.4                 
 [89] assertthat_0.2.1              cachem_1.0.8                 
 [91] xfun_0.41                     pcaExplorer_2.28.0           
 [93] S4Arrays_1.2.0                mime_0.12                    
 [95] survival_3.5-7                pheatmap_1.0.12              
 [97] seriation_1.5.1               SingleCellExperiment_1.22.0  
 [99] iterators_1.0.14              statmod_1.5.0                
[101] bluster_1.12.0                interactiveDisplayBase_1.40.0
[103] ellipsis_0.3.2                fitdistrplus_1.1-11          
[105] ROCR_1.0-11                   Category_2.68.0              
[107] nlme_3.1-163                  bit64_4.0.5                  
[109] threejs_0.3.3                 progress_1.2.2               
[111] filelock_1.0.2                RcppAnnoy_0.0.21             
[113] GenomeInfoDb_1.38.0           bslib_0.5.1                  
[115] irlba_2.3.5.1                 KernSmooth_2.23-22           
[117] rpart_4.1.21                  colorspace_2.1-0             
[119] DBI_1.1.3                     Hmisc_5.1-1                  
[121] celldex_1.12.0                nnet_7.3-19                  
[123] DESeq2_1.42.0                 tidyselect_1.2.0             
[125] bit_4.0.5                     compiler_4.3.1               
[127] curl_5.1.0                    graph_1.80.0                 
[129] htmlTable_2.4.2               BiocNeighbors_1.20.0         
[131] SparseM_1.81                  xml2_1.3.5                   
[133] DelayedArray_0.28.0           plotly_4.10.3                
[135] checkmate_2.2.0               scales_1.2.1                 
[137] lmtest_0.9-40                 RBGL_1.78.0                  
[139] NMF_0.26                      rappdirs_0.3.3               
[141] stringr_1.5.0                 digest_0.6.33                
[143] goftest_1.2-3                 shinyBS_0.61.1               
[145] spatstat.utils_3.0-4          rmarkdown_2.25               
[147] ca_0.71.1                     XVector_0.42.0               
[149] htmltools_0.5.6.1             pkgconfig_2.0.3              
[151] base64enc_0.1-3               SingleR_2.4.0                
[153] sparseMatrixStats_1.14.0      MatrixGenerics_1.14.0        
[155] highr_0.10                    dbplyr_2.4.0                 
[157] fastmap_1.1.1                 GlobalOptions_0.1.2          
[159] rlang_1.1.1                   htmlwidgets_1.6.2            
[161] shiny_1.7.5.1                 DelayedMatrixStats_1.24.0    
[163] farver_2.1.1                  jquerylib_0.1.4              
[165] zoo_1.8-12                    jsonlite_1.8.7               
[167] BiocParallel_1.36.0           BiocSingular_1.18.0          
[169] RCurl_1.98-1.12               magrittr_2.0.3               
[171] Formula_1.2-5                 scuttle_1.12.0               
[173] GenomeInfoDbData_1.2.11       dotCall64_1.1-0              
[175] patchwork_1.1.3               munsell_0.5.0                
[177] Rcpp_1.0.11                   viridis_0.6.4                
[179] reticulate_1.34.0             stringi_1.7.12               
[181] zlibbioc_1.48.0               MASS_7.3-60                  
[183] AnnotationHub_3.10.0          plyr_1.8.9                   
[185] parallel_4.3.1                listenv_0.9.0                
[187] ggrepel_0.9.4                 deldir_1.0-9                 
[189] Biostrings_2.70.1             splines_4.3.1                
[191] tensor_1.5                    circlize_0.4.15              
[193] hms_1.1.3                     locfit_1.5-9.8               
[195] igraph_1.5.1                  spatstat.geom_3.2-7          
[197] rngtools_1.5.2                reshape2_1.4.4               
[199] biomaRt_2.58.0                stats4_4.3.1                 
[201] ScaledMatrix_1.10.0           BiocVersion_3.18.0           
[203] XML_3.99-0.14                 evaluate_0.23                
[205] SeuratObject_5.0.0            BiocManager_1.30.22          
[207] scran_1.30.0                  foreach_1.5.2                
[209] httpuv_1.6.12                 RANN_2.6.1                   
[211] tidyr_1.3.0                   purrr_1.0.2                  
[213] polyclip_1.10-6               clue_0.3-65                  
[215] heatmaply_1.5.0               future_1.33.0                
[217] scattermore_1.2               ggplot2_3.4.4                
[219] gridBase_0.4-7                rsvd_1.0.5                   
[221] xtable_1.8-4                  later_1.3.1                  
[223] viridisLite_0.4.2             tibble_3.2.1                 
[225] EBAII.n1.SC.helper_0.0.2      registry_0.5-1               
[227] memoise_2.0.1                 AnnotationDbi_1.64.0         
[229] IRanges_2.36.0                cluster_2.1.4                
[231] globals_0.16.2                GSEABase_1.64.0              
[233] shinyAce_0.4.2               
LS0tCnRpdGxlOiAiPENFTlRFUj5FQkFJSSBuMSAyMDIzIDogU0lOR0xFIENFTEwgQU5BTFlTSVMgVFJBSU5JTkc8QlI+IDxCPlBST0NFU1NJTkcgKEkpPC9CPjxCUj5EYXRhIG5vcm1hbGl6YXRpb24gYW5kIHNjYWxpbmc8L0NFTlRFUj4iCmRhdGU6ICIyMDIzLTExLTA1LjEwIgphdXRob3I6CiAgLSBuYW1lOiAiQmFzdGllbiBKT0IiCiAgICBlbWFpbDogImJhc3RpZW4uam9iQGd1c3RhdmVyb3Vzc3kuZnIiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIGNzczogY29sdW1ucy5jc3MKICAgIGJhY2tncm91bmQ6IGJsYWNrCiAgICBmaWdfaGVpZ2h0OiA2CiAgICBmaWdfd2lkdGg6IDgKICAgIGhpZ2hsaWdodDogdGFuZ28gICMjIFRoZW1lIGZvciB0aGUgY29kZSBjaHVua3MKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgIyMgQWRkcyBudW1iZXIgdG8gaGVhZGVycyAoc2VjdGlvbnMpCiAgICB0aGVtZTogZmxhdGx5ICAjIyBDU1MgdGhlbWUgZm9yIHRoZSBIVE1MIHBhZ2UKICAgIHRvYzogdHJ1ZSAgIyMgQWRkcyBhIHRhYmxlIG9mIGNvbnRlbnQKICAgIHRvY19mbG9hdDogICMjIFRPQyBvcHRpb25zCiAgICAgIGNvbGxhcHNlZDogdHJ1ZSAgIyMgQnkgZGVmYXVsdCwgdGhlIFRPQyBpcyBmb2xkZWQKICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZSAjIyBTbW9vdGggc2Nyb2xsIG9mIHRoZSBIVE1MIHBhZ2UKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlICMjIEluY2x1ZGVzIGFsbCBwbG90cy9pbWFnZXMgd2l0aGluIHRoZSBIVE1MCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICMjIEFkZHMgYSBidXR0b24gdG8gZG93bmxvYWQgdGhlIFJtZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aHVtYm5haWxzOiBmYWxzZQogICAgbGlnaHRib3g6IHRydWUKICAgIGZpZ19jYXB0aW9uOiBmYWxzZQogICAgZ2FsbGVyeTogdHJ1ZQogICAgdXNlX2Jvb2tkb3duOiB0cnVlCmFsd2F5c19hbGxvd19odG1sOiB0cnVlICMjIEFsbG93IHBsYWluIEhUTUwgY29kZSBpbiB0aGUgUm1kCi0tLQoKPCEtLSBBbGxvd3MgdG8gaGlkZSB0aGUgVE9DIGJ5IGRlZmF1bHQsIGRpc3BsYXkgaXQgd2l0aCBhIGJ1dHRvbiwgbW92ZSBpdCB0byB0aGUgcmlnaHQgb3IgbGVmdCBvZiB0aGUgcGFnZSAtLT4KYHIgSG1pc2M6OmhpZGluZ1RPQyhidXR0b25MYWJlbCA9ICdTaG93IFRPQycsIGhpZGRlbiA9IFRSVUUsIHRvY1NpZGUgPSAnbGVmdCcsIGJ1dHRvblNpZGU9J2xlZnQnLCBwb3NDb2xsYXBzZSA9ICdtYXJnaW4nLCBsZXZlbHMgPSAzKWAKCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBvcHRpb25zKHdpZHRoID0gNjApOwprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsICAgICAgICAjIFByaW50IHRoZSBjb2RlCiAgZXZhbCA9IFRSVUUsICAgICAgICMgRG8gbm90IHJ1biBjb21tYW5kIGxpbmVzCiAgbWVzc2FnZSA9IEZBTFNFLCAgICAjIFByaW50IG1lc3NhZ2VzCiAgcHJvbXB0ID0gRkFMU0UsICAgICAjIERvIG5vdCBkaXNwbGF5IHByb21wdAogIGNvbW1lbnQgPSBOQSwgICAgICAgIyBObyBjb21tZW50cyBvbiB0aGlzIHNlY3Rpb24KICB3YXJuaW5nID0gRkFMU0UsICAgICMgRGlzcGxheSB3YXJuaW5ncwogIHRpZHkgPSBGQUxTRSwKICAjIHJlc3VsdHMgPSAnaGlkZScKICB3aWR0aCA9IDEwMCAgICAgICAjIE51bWJlciBvZiBjaGFyYWN0ZXJzIHBlciBsaW5lCikKYGBgCgoKVGhpcyBmaWxlIGRlc2NyaWJlcyB0aGUgZGlmZmVyZW50IHN0ZXBzIHRvIHBlcmZvcm0gZmlyc3QgcGFydCBvZiBkYXRhIHByb2Nlc3NpbmcgZm9yIHRoZSBzaW5nbGUgY2VsbCBSTkFzZXEgZGF0YSBhbmFseXNpcyB0cmFpbmluZyBjb3Vyc2UgZm9yIHRoZSBFQkFJSSBuMSAyMDIzLCBjb3ZlcmluZyB0aGVzZSBzdGVwcyA6CiogTm9ybWFsaXphdGlvbiBvZiB0aGUgcmF3IGNvdW50cwoqIFNjYWxpbmcKCiMgTG9hZCB0aGUgbGF0ZXN0IFNldXJhdCBvYmplY3QKClN0YXJ0IGEgKipSc3R1ZGlvKiogc2Vzc2lvbgo8QlI+PEJSPjxjZW50ZXI+IVtdKFJzdHVkaW8ucG5nKTwvY2VudGVyPjxCUj4KClNldCB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5IGluICoqeW91cioqIFREIG91dHB1dCBkaXJlY3RvcnkgXygqKmdvbGYqKiBpcyBtaW5lISlfCgpgYGB7ciBzZXR3ZCwgZXZhbCA9IEZBTFNFfQpzZXR3ZCgnL3NoYXJlZC9wcm9qZWN0cy9nb2xmL1REL1JFU1VMVFMnKQpgYGAKCkxvYWQgdGhlIGxhdGVzdCBTZXVyYXQgb2JqZWN0IHNhdmVkIGFzIFJEUwoKX0J1dCwgaG93ID9fCgpgYGB7ciBoX3JlYWRSRFMsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQo/YmFzZTo6cmVhZFJEUwpgYGAKCmBgYHtyIHJlYWQsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpzb2JqIDwtIHJlYWRSRFMoZmlsZSA9ICcuL1REM0FfMDFfRmlsdGVyZWQuUkRTJykKYGBgCgojIE5vcm1hbGl6YXRpb24KClVudGlsIG5vdywgd2UgbWFuaXB1bGF0ZWQgYSByYXcgY291bnQgbWF0cml4LgoKRHVyaW5nIFFDIC8gcHJlcHJvY2Vzc2luZywgd2Ugb2JzZXJ2ZWQgdGhhdCBjZWxscyBleHByZXNzaW9uIHByb2ZpbGVzIGhhZCBhIHdpZGUgdmFyaWV0eSBpbiBtdWx0aXBsZSBtZXRyaWNzLCBiZSBpdCB0aGVpciBudW1iZXIgb2YgZXhwcmVzc2VkIGdlbmVzLCB0aGVpciBhbW91bnQgb2YgY2FwdHVyZWQgUk5BIG1vbGVjdWxlcywgdGhlaXIgcmF0ZSBvZiBleHByZXNzaW9uIGludm9sdmVkIGluIG1pdG9jaG9uZHJpb24gZ2VuZXMsIGV0Yy4uLgoKQ29uc2VxdWVudGx5LCB3ZSBoYXZlIHRvIGhhcm1vbml6ZSB0aGUgZGF0YSB0byB0cnkgdG8gaWRlbnRpZnkgYmlvbG9naWNhbCBkaWZmZXJlbmNlcyBkcm93bmVkIGludG8gYXJ0ZWZhY3R1YWwgZGlmZmVyZW5jZXMuCgpUaGlzIGlzIHdoYXQgaXMgcGVyZm9ybWVkIGR1cmluZyBub3JtYWxpemF0aW9uLgoKSGVyZSwgd2Ugd2lsbCB1c2UgdGhlIHR3byBtYWluIG5vcm1hbGl6YXRpb24gbWV0aG9kcyBhdmFpbGFibGUgaW4gU2V1cmF0IDoKCiAqICoqTG9nTm9ybWFsaXphdGlvbioqCiAqICoqU0NUKiogKHZhcmlhbmNlLXN0YWJpbGl6ZWQgdHJhbnNmb3JtYXRpb24sIHYyKQoKTGV0J3MgZGVzY3JpYmUgdGhpcyBtb2RpZmllZCBTZXVyYXQgb2JqZWN0LCBlc3BlY2lhbGx5IHRoZSAnZGF0YScgc2xvdAoKCjxCUj4KV2UgY2FuIHZpc3VhbGl6ZSB0aGUgbW9kaWZpY2F0aW9uIHBlcmZvcm1lZCB0aHJvdWdoIG5vcm1hbGl6YXRpb24gdXNpbmcgYm94cGxvdHMuCgpUbyB1bmRlcnN0YW5kIHdoeSB0aGUgTG9nTm9ybSBpcyBub3QganVzdCBhIGxvZy1zY2FsZSB0cmFuc2Zvcm1hdGlvbiBvZiBjb3VudHMsIHdlIHdpbGwgZGlzcGxheSB0aGUgcmF3IGNvdW50cyBpbiBsb2cgc2NhbGUsIGZvciBjb21wYXJpc29uCgo8QlI+PEJSPgoKOjo6IHt9Cjo6OiB7LmNvbHVtbiB3aWR0aD0iMzMlIn0KCjxjZW50ZXI+PGZvbnQgc2l6ZT0iNCI+KipSTkE6Y291bnRzIChyYXcpKio8L2ZvbnQ+PC9jZW50ZXI+CgpCb3hwbG90IDoKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBib3hwX2NvdW50cywgZmlnLmFsaWduPSJjZW50ZXIifQpib3hwbG90KAogIGxvZzEwKGFzLm1hdHJpeCgKICAgICAgU2V1cmF0OjpHZXRBc3NheURhdGEoCiAgICAgICAgb2JqZWN0ID0gc29iaiwgCiAgICAgICAgYXNzYXkgPSAnUk5BJywgCiAgICAgICAgc2xvdCA9ICdjb3VudHMnKSlbLDE6MTBdICsxKSwKICBtYWluID0gJ0NvdW50cycsIAogIHhsYWIgPSAnQ2VsbHMnLAogIHlsYWIgPSAnUmF3IGNvdW50cyAobG9nLXNjYWxlKScpCmBgYAoKPEJSPjxCUj4KCjo6Ogo6Ojogey5jb2x1bW4gd2lkdGg9IjMzJSJ9Cgo8IS0tIDo6OiBjb2x1bW5zIC0tPgo8IS0tIDo6OiBjb2x1bW4gLS0+Cgo8Y2VudGVyPjxmb250IHNpemU9IjQiPioqUk5BOmRhdGEgKExvZ05vcm0pKio8L2ZvbnQ+PC9jZW50ZXI+CgpOb3JtYWxpemF0aW9uIDoKCl9Ib3cgP18KCmBgYHtyIGhfTm9ybWFsaXplRGF0YSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9Cj9TZXVyYXQ6Ok5vcm1hbGl6ZURhdGEKYGBgCgpgYGB7ciBMTiwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnNvYmogPC0gU2V1cmF0OjpOb3JtYWxpemVEYXRhKAogIG9iamVjdCA9IHNvYmosCiAgbWV0aG9kID0gJ0xvZ05vcm1hbGl6ZScsCiAgU3ZlcmJvc2UgPSBGQUxTRSkKYGBgCgpEZXNjcmlwdGlvbiA6IAoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIExOZGVzY30KIyMgVXNpbmcgb3VyIGdvb2Qgb2wnIHNvYmoKRUJBSUkubjEuU0MuaGVscGVyOjpzZXVyYXQ0X2Rlc2NyaXB0b3IoCiAgc29iaiA9IHNvYmosIAogIGRlc2NyaWJlID0gJ2Fzc2F5JykKYGBgCgpCb3hwbG90IDogCgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgYm94cF9MTn0KYm94cGxvdCgKICBhcy5tYXRyaXgoCiAgICBTZXVyYXQ6OkdldEFzc2F5RGF0YSgKICAgICAgb2JqZWN0ID0gc29iaiwgCiAgICAgIGFzc2F5ID0gJ1JOQScsIAogICAgICBzbG90ID0gJ2RhdGEnKSlbLDE6MTBdLAogIG1haW4gPSAnRGF0YSAoTG9nTm9ybSknLCAKICB4bGFiID0gJ0NlbGxzJywKICB5bGFiID0gJ0xOIHZhbHVlcycpCmBgYAoKVmlzdWFsaXphdGlvbiA6Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgcW5kX0xOfQpFQkFJSS5uMS5TQy5oZWxwZXI6OlFuRF92aXooCiAgc29iaiA9IHNvYmosCiAgYXNzYXkgPSAnUk5BJywKICBzbG90ID0gJ2RhdGEnKQpgYGAKCjo6Ogo6Ojogey5jb2x1bW4gd2lkdGg9IjMzJSJ9Cgo8IS0tIDo6OiAtLT4KPCEtLSA6OjogY29sdW1uIC0tPgoKPGNlbnRlcj48Zm9udCBzaXplPSI0Ij4qKlNDVDpkYXRhIChTQ1R2MikqKjwvZm9udD48L2NlbnRlcj4KCk5vcm1hbGl6YXRpb24gOgoKX0hvdyA/XwoKYGBge3IgaF9TQ1RyYW5zZm9ybSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9Cj9TZXVyYXQ6OlNDVHJhbnNmb3JtCmBgYAoKYGBge3Igc2N0LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0Kc29ialNDVCA8LSBTZXVyYXQ6OlNDVHJhbnNmb3JtKAogIG9iamVjdCA9IHNvYmosIAogIHZzdC5mbGF2b3IgPSAndjInLCAKICB2ZXJib3NlID0gRkFMU0UpCmBgYAoKRGVzY3JpcHRpb24gOgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIFNDVGRlc2N9CiMjIFdhcm5pbmcgOiBoZXJlIHdlJ3JlIGRlYWxpbmcgd2l0aCBzb2JqU0NUCkVCQUlJLm4xLlNDLmhlbHBlcjo6c2V1cmF0NF9kZXNjcmlwdG9yKAogIHNvYmogPSBzb2JqU0NULCAKICBkZXNjcmliZSA9ICdhc3NheScpCmBgYAoKQm94cGxvdCA6Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgYm94cF9zY3R9CmJveHBsb3QoYXMubWF0cml4KAogIFNldXJhdDo6R2V0QXNzYXlEYXRhKAogICAgb2JqZWN0ID0gc29ialNDVCwgYXNzYXkgPSAnU0NUJywgCiAgICBzbG90ID0gJ2RhdGEnKSlbLDE6MTBdLCBtYWluID0gJ0RhdGEgKFNDVHYyKScsIAogICAgeGxhYiA9ICdDZWxscycsIHlsYWIgPSAnU0NUdjIgdmFsdWVzJykKYGBgCgpWaXN1YWxpemF0aW9uIDogCgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgcW5kX3NjdH0KRUJBSUkubjEuU0MuaGVscGVyOjpRbkRfdml6KAogIHNvYmogPSBzb2JqU0NULAogIGFzc2F5ID0gJ1NDVCcsCiAgc2xvdCA9ICdkYXRhJykKYGBgCgo6OjoKOjo6CgoqKipRdWVzdGlvbioqKiA6IERvZXMgdGhlIG5vcm1hbGl6YXRpb24gbWV0aG9kIGhhdmUgYSBoaWdoIGltcGFjdCBvbiB0aGUgY2VsbCBzcGFjZSA/CgpGb3IgZnVydGhlciBzdGVwcywgd2Ugd2lsbCBrZWVwIHVzaW5nIHRoZSBMTiB2ZXJzaW9uLgoKYGBge3J9CnJtKHNvYmpTQ1QpCmBgYAoKIyBTY2FsaW5nCgp0aGF0IGlzIGEgc3RhbmRhcmQgcHJlLXByb2Nlc3Npbmcgc3RlcCBwcmlvciB0byBkaW1lbnNpb25hbCByZWR1Y3Rpb24gdGVjaG5pcXVlcyBsaWtlIFBDQS4gVGhlIFNjYWxlRGF0YSgpIGZ1bmN0aW9uOgoKICAgIFNoaWZ0cyB0aGUgZXhwcmVzc2lvbiBvZiBlYWNoIGdlbmUsIHNvIHRoYXQgdGhlIG1lYW4gZXhwcmVzc2lvbiBhY3Jvc3MgY2VsbHMgaXMgMAogICAgU2NhbGVzIHRoZSBleHByZXNzaW9uIG9mIGVhY2ggZ2VuZSwgc28gdGhhdCB0aGUgdmFyaWFuY2UgYWNyb3NzIGNlbGxzIGlzIDEKICAgICAgICBUaGlzIHN0ZXAgZ2l2ZXMgZXF1YWwgd2VpZ2h0IGluIGRvd25zdHJlYW0gYW5hbHlzZXMsIHNvIHRoYXQgaGlnaGx5LWV4cHJlc3NlZCBnZW5lcyBkbyBub3QgZG9taW5hdGUKICAgIFRoZSByZXN1bHRzIG9mIHRoaXMgYXJlIHN0b3JlZCBpbiBwYm1jW1siUk5BIl1dJHNjYWxlLmRhdGEKICAgIEJ5IGRlZmF1bHQsIG9ubHkgdmFyaWFibGUgZmVhdHVyZXMgYXJlIHNjYWxlZC4KICAgIFlvdSBjYW4gc3BlY2lmeSB0aGUgZmVhdHVyZXMgYXJndW1lbnQgdG8gc2NhbGUgYWRkaXRpb25hbCBmZWF0dXJlcwoKCk5vcm1hbGl6YXRpb24gY29uc2lkZXJlZCB0aGUgaG9tb2dlbml6YXRpb24gb2YgZXhwcmVzc2lvbiB2YWx1ZXMgKiphdCB0aGUgY2VsbCBsZXZlbCoqLgoKRGVzcGl0ZSB0aGlzLCB0aGUgbmV4dCBpbXBvcnRhbnQgc3RlcCB0aGF0IGlzIGRpbWVuc2lvbmFsIHJlZHVjdGlvbiBpcyBoZWF2aWx5IHNlbnNpdGl2ZSB0byB2YXJpYXRpb25zIG9mIHRoZSByYW5nZSBvZiBleHByZXNzaW9uIHRoYXQgcmVtYWluIGluIHRoZSBub3JtYWxpemVkIGV4cHJlc3Npb24gZGF0YSwgYW5kIHRoYXQgd291bGQgaW1wYWlyIHRoZWlyIGV4cGVjdGVkIGJlaGF2aW9yLgoKVGh1cywgd2UgbmVlZCB0byB0cmFuc2Zvcm0gZGF0YSBpbiBhIGhhcmRlciB3YXkganVzdCBmb3IgdGhpcyBkaW1lbnNpb25hbCByZWR1Y3Rpb24gc3RlcCwgd2hpY2ggaXMgcGVyZm9ybWVkIHRocm91Z2ggc2NhbGluZyA6CgoqICoqc2hpZnRzKiogdGhlIGV4cHJlc3Npb24gb2YgZWFjaCBmZWF0dXJlLCBzbyB0aGF0IHRoZSBtZWFuIGV4cHJlc3Npb24gYWNyb3NzIGNlbGxzIGlzIDAgXyhjZW50cmFsaXphdGlvbilfCiogKipzY2FsZXMqKiB0aGUgZXhwcmVzc2lvbiBvZiBlYWNoIGZlYXR1cmUsIHNvIHRoYXQgdGhlIHZhcmlhbmNlIGFjcm9zcyBjZWxscyBpcyAxIF8oc2NhbGluZylfCgpUaGlzIHRyYW5zZm9ybWF0aW9uIGdpdmVzIGVxdWFsIHdlaWdodCB0byBmZWF0dXJlcyBpbiBkb3duc3RyZWFtIGFuYWx5c2VzLCBzbyB0aGF0IGhpZ2hseS1leHByZXNzZWQgZ2VuZXMgZG8gbm90IGRvbWluYXRlLgoKVG8gYmUgZWZmaWNpZW50IGluIHRoZSBzaW5nbGUgY2VsbCBjb250ZXh0LCBkYXRhIHNjYWxpbmcgcmVsaWVzIG9uIGEgc2VsZWN0aW9uIG9mIGZlYXR1cmVzIG9mIGltcG9ydGFuY2UuCgpCeSBkZWZhdWx0LCB0aGUgbWV0cmljIHVzZWQgdG8gc2VsZWN0IHRoZXNlIGlzICoqdGhlaXIgY29udHJpYnV0aW9uIHRvIHRoZSBnbG9iYWwgdmFyaWFuY2UqKiBvZiB0aGUgZXhwcmVzc2lvbiBtYXRyaXggKHRoZW4gY2FsbGVkICJIVkcicywgZm9yICJoaWdobHkgdmFyaWFibGUgZ2VuZXMiKS4KClRodXMsIHRoZSBtYWluIHBhcmFtZXRlciBvZiBpbXBvcnRhbmNlIGF0IHRoaXMgc3RlcCBpcyB0aGUgKipudW1iZXIgb2YgZmVhdHVyZXMqKiB1c2VkIGluIHRoaXMgc2VsZWN0aW9uLgoKVG8gYXNzZXNzIHRoaXMgcGFyYW1ldGVyIGltcG9ydGFuY2UsIHdlIHdpbGwgKipzcGxpdCB0aGUgYXVkaWVuY2UqKiBpbiA0IHN1Ymdyb3VwcyA6CgoqIFRoZSA1MDAncwoqIFRoZSAxMDAwJ3MKKiBUaGUgMjUwMCdzCiogVGhlIDUwMDAncwoKQnV0IGZpcnN0Li4uIF9ob3cgP18KCmBgYHtyIGhfc2NhbGluZywgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9Cj9TZXVyYXQ6OkZpbmRWYXJpYWJsZUZlYXR1cmVzCj9TZXVyYXQ6OlNjYWxlRGF0YQpgYGAKCgo8QlI+PEJSPgoKOjo6IGNvbHVtbnMKOjo6IGNvbHVtbgoKPGNlbnRlcj48Zm9udCBzaXplPSI0Ij4qKjUwMCoqPC9mb250PjwvY2VudGVyPgoKRmVhdHVyZSBzZWxlY3Rpb24gOgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIHNlbDUwMH0Kc29iaiA8LSBTZXVyYXQ6OkZpbmRWYXJpYWJsZUZlYXR1cmVzKAogIG9iamVjdCA9IHNvYmosIAogIGFzc2F5ID0gJ1JOQScsIAogIG5mZWF0dXJlcyA9IDUwMCwKICB2ZXJib3NlID0gRkFMU0UpCiMjIFNob3cgdGhlIGxhc3Qgc2VsZWN0ZWQgZmVhdHVyZXMKc3RyKHJldihzb2JqQGFzc2F5cyRSTkFAdmFyLmZlYXR1cmVzKSkKYGBgCgpTY2FsaW5nIDoKCmBgYHtyIHNjYWxlNTAwfQpzb2JqIDwtIFNldXJhdDo6U2NhbGVEYXRhKAogIG9iamVjdCA9IHNvYmosIAogIGFzc2F5ID0gJ1JOQScsIAogIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgpEZXNjcmlwdGlvbiA6Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgZGVzYzUwMH0KRUJBSUkubjEuU0MuaGVscGVyOjpzZXVyYXQ0X2Rlc2NyaXB0b3IoCiAgc29iaiA9IHNvYmosIAogIGRlc2NyaWJlID0gJ2Fzc2F5JykKYGBgCgpWaXN1YWxpemF0aW9uIDoKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBxbmQ1MDB9CkVCQUlJLm4xLlNDLmhlbHBlcjo6UW5EX3ZpeigKICBzb2JqID0gc29iaiwKICBhc3NheSA9ICdSTkEnLAogIHNsb3QgPSAnc2NhbGUuZGF0YScpCmBgYAoKOjo6Cjo6OiBjb2x1bW4KCjxjZW50ZXI+PGZvbnQgc2l6ZT0iNCI+KioxMDAwKio8L2ZvbnQ+PC9jZW50ZXI+CgpGZWF0dXJlIHNlbGVjdGlvbiA6Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3Igc2VsMTAwMH0Kc29iaiA8LSBTZXVyYXQ6OkZpbmRWYXJpYWJsZUZlYXR1cmVzKAogIG9iamVjdCA9IHNvYmosIAogIGFzc2F5ID0gJ1JOQScsIAogIG5mZWF0dXJlcyA9IDEwMDAsCiAgdmVyYm9zZSA9IEZBTFNFKQojIyBTaG93IHRoZSBsYXN0IHNlbGVjdGVkIGZlYXR1cmVzCnN0cihyZXYoc29iakBhc3NheXMkUk5BQHZhci5mZWF0dXJlcykpCmBgYAoKU2NhbGluZyA6CgpgYGB7ciBzY2FsZTEwMDB9CnNvYmogPC0gU2V1cmF0OjpTY2FsZURhdGEoCiAgb2JqZWN0ID0gc29iaiwgCiAgYXNzYXkgPSAnUk5BJywgCiAgdmVyYm9zZSA9IEZBTFNFKQpgYGAKCkRlc2NyaXB0aW9uIDoKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBkZXNjMTAwMH0KRUJBSUkubjEuU0MuaGVscGVyOjpzZXVyYXQ0X2Rlc2NyaXB0b3IoCiAgc29iaiA9IHNvYmosIAogIGRlc2NyaWJlID0gJ2Fzc2F5JykKYGBgCgpWaXN1YWxpemF0aW9uIDoKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBxbmQxMDAwfQpFQkFJSS5uMS5TQy5oZWxwZXI6OlFuRF92aXooCiAgc29iaiA9IHNvYmosCiAgYXNzYXkgPSAnUk5BJywKICBzbG90ID0gJ3NjYWxlLmRhdGEnKQpgYGAKCjo6Ogo6OjoKCjxCUj48QlI+Cgo6OjogY29sdW1ucwo6OjogY29sdW1uCgo8Y2VudGVyPjxmb250IHNpemU9IjQiPioqMjUwMCoqPC9mb250PjwvY2VudGVyPgoKRmVhdHVyZSBzZWxlY3Rpb24gOgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIHNlbDI1MDB9CnNvYmogPC0gU2V1cmF0OjpGaW5kVmFyaWFibGVGZWF0dXJlcygKICBvYmplY3QgPSBzb2JqLCAKICBhc3NheSA9ICdSTkEnLCAKICBuZmVhdHVyZXMgPSAyNTAwLAogIHZlcmJvc2UgPSBGQUxTRSkKIyMgU2hvdyB0aGUgbGFzdCBzZWxlY3RlZCBmZWF0dXJlcwpzdHIocmV2KHNvYmpAYXNzYXlzJFJOQUB2YXIuZmVhdHVyZXMpKQpgYGAKClNjYWxpbmcgOgoKYGBge3Igc2NhbGUyNTAwfQpzb2JqIDwtIFNldXJhdDo6U2NhbGVEYXRhKAogIG9iamVjdCA9IHNvYmosIAogIGFzc2F5ID0gJ1JOQScsIAogIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgpEZXNjcmlwdGlvbiA6Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgZGVzYzI1MDB9CkVCQUlJLm4xLlNDLmhlbHBlcjo6c2V1cmF0NF9kZXNjcmlwdG9yKAogIHNvYmogPSBzb2JqLCAKICBkZXNjcmliZSA9ICdhc3NheScpCmBgYAoKVmlzdWFsaXphdGlvbiA6Cgo8aW5wdXQgdHlwZT1idXR0b24gY2xhc3M9aGlkZXNob3c+PC9pbnB1dD4KYGBge3IgcW5kMjUwMH0KRUJBSUkubjEuU0MuaGVscGVyOjpRbkRfdml6KAogIHNvYmogPSBzb2JqLAogIGFzc2F5ID0gJ1JOQScsCiAgc2xvdCA9ICdzY2FsZS5kYXRhJykKYGBgCgo6OjoKOjo6IGNvbHVtbgoKPGNlbnRlcj48Zm9udCBzaXplPSI0Ij4qKjUwMDAqKjwvZm9udD48L2NlbnRlcj4KCkZlYXR1cmUgc2VsZWN0aW9uIDoKCjxpbnB1dCB0eXBlPWJ1dHRvbiBjbGFzcz1oaWRlc2hvdz48L2lucHV0PgpgYGB7ciBzZWw1MDAwfQpzb2JqIDwtIFNldXJhdDo6RmluZFZhcmlhYmxlRmVhdHVyZXMoCiAgb2JqZWN0ID0gc29iaiwgCiAgYXNzYXkgPSAnUk5BJywgCiAgbmZlYXR1cmVzID0gNTAwMCwKICB2ZXJib3NlID0gRkFMU0UpCiMjIFNob3cgdGhlIGxhc3Qgc2VsZWN0ZWQgZmVhdHVyZXMKc3RyKHJldihzb2JqQGFzc2F5cyRSTkFAdmFyLmZlYXR1cmVzKSkKYGBgCgpTY2FsaW5nIDoKCmBgYHtyIHNjYWxlNTAwMH0Kc29iaiA8LSBTZXVyYXQ6OlNjYWxlRGF0YSgKICBvYmplY3QgPSBzb2JqLCAKICBhc3NheSA9ICdSTkEnLCAKICB2ZXJib3NlID0gRkFMU0UpCmBgYAoKRGVzY3JpcHRpb24gOgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIGRlc2M1MDAwfQpFQkFJSS5uMS5TQy5oZWxwZXI6OnNldXJhdDRfZGVzY3JpcHRvcigKICBzb2JqID0gc29iaiwgCiAgZGVzY3JpYmUgPSAnYXNzYXknKQpgYGAKClZpc3VhbGl6YXRpb24gOgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyIHFuZDUwMDB9CkVCQUlJLm4xLlNDLmhlbHBlcjo6UW5EX3ZpeigKICBzb2JqID0gc29iaiwKICBhc3NheSA9ICdSTkEnLAogIHNsb3QgPSAnc2NhbGUuZGF0YScpCmBgYAoKOjo6Cjo6OgoKPEJSPjxCUj4KCkFjdHVhbGx5LCB3ZSB3aWxsIHVzZSB0aGUgZGVmYXVsdCB2YWx1ZSBmb3IgTG9nTm9ybWFsaXplZCBkYXRhIDogMjAwMCBIVkdzCgpgYGB7ciBzZWwyMDAwfQojIyBGZWF0dXJlIHNlbGVjdGlvbgpzb2JqIDwtIFNldXJhdDo6RmluZFZhcmlhYmxlRmVhdHVyZXMoCiAgb2JqZWN0ID0gc29iaiwgCiAgYXNzYXkgPSAnUk5BJywgCiAgbmZlYXR1cmVzID0gNTAwMCwKICB2ZXJib3NlID0gRkFMU0UpCiMjIFNjYWxpbmcKc29iaiA8LSBTZXVyYXQ6OlNjYWxlRGF0YSgKICBvYmplY3QgPSBzb2JqLCAKICBhc3NheSA9ICdSTkEnLCAKICB2ZXJib3NlID0gRkFMU0UpCmBgYAoKV2UgY2FuIGxvb2sgYXQgdGhlIHNjYWxlZCBkaXN0cmlidXRpb24gOgoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciJ9CmJveHBsb3QoCiAgYXMubWF0cml4KAogICAgU2V1cmF0OjpHZXRBc3NheURhdGEoCiAgICAgIG9iamVjdCA9IHNvYmosIAogICAgICBhc3NheSA9ICdSTkEnLCAKICAgICAgc2xvdCA9ICdzY2FsZS5kYXRhJykpWywxOjEwXSwKICBtYWluID0gJ1NjYWxlZCBEYXRhIChMb2dOb3JtKScsIAogIHhsYWIgPSAnQ2VsbHMnLAogIHlsYWIgPSAnU2NhbGVkIHZhbHVlcycpCmBgYAoKIyBTYXZlCgpXZSBjYW4gbm93IHNhdmUgdGhlIHJlc3VsdHMgb2Ygb3VyIGhhcmQgd29yayAhCgpgYGB7ciBzYXZlfQpzYXZlUkRTKG9iamVjdCA9IHNvYmosIAogICAgICAgIGZpbGUgPSAnLi9URDNBXzAyX1NjYWxlZC4ySy5SRFMnLCAKICAgICAgICBjb21wcmVzcyA9ICdiemlwMicpCmBgYAoKCjxCUj48QlI+PEJSPgoKX1JzZXNzaW9uXwoKPGlucHV0IHR5cGU9YnV0dG9uIGNsYXNzPWhpZGVzaG93PjwvaW5wdXQ+CmBgYHtyfQp1dGlsczo6c2Vzc2lvbkluZm8oKQpgYGAKCgo8IS0tIFNjcmlwdCB0byAgSGlkZS9TaG93IG91dHB1dHMgLS0+CjxzY3JpcHQ+CiQoICJpbnB1dC5oaWRlc2hvdyIgKS5lYWNoKCBmdW5jdGlvbiAoIGluZGV4LCBidXR0b24gKSB7CiAgYnV0dG9uLnZhbHVlID0gJ0hpZGUgT3V0cHV0JzsKICAkKCBidXR0b24gKS5jbGljayggZnVuY3Rpb24gKCkgewogICAgdmFyIHRhcmdldCA9IHRoaXMubmV4dFNpYmxpbmcgPyB0aGlzIDogdGhpcy5wYXJlbnROb2RlOwogICAgdGFyZ2V0ID0gdGFyZ2V0Lm5leHRTaWJsaW5nLm5leHRTaWJsaW5nLm5leHRTaWJsaW5nLm5leHRTaWJsaW5nOwogICAgaWYgKCB0YXJnZXQuc3R5bGUuZGlzcGxheSA9PSAnYmxvY2snIHx8IHRhcmdldC5zdHlsZS5kaXNwbGF5ID09ICcnICkgewogICAgICB0YXJnZXQuc3R5bGUuZGlzcGxheSA9ICdub25lJzsKICAgICAgdGhpcy52YWx1ZSA9ICdTaG93IE91dHB1dCc7CiAgICB9IGVsc2UgewogICAgICB0YXJnZXQuc3R5bGUuZGlzcGxheSA9ICdibG9jayc7CiAgICAgIHRoaXMudmFsdWUgPSAnSGlkZSBPdXRwdXQnOwogICAgfQogIH0gKTsKfSApOwokKCAiaW5wdXQuaGlkZXNob3ciICkuY2xpY2soKQo8L3NjcmlwdD4KCgo8IS0tIFNob3cvaGlkZSBIVE1MIC0tPgo8U0NSSVBUPgpmdW5jdGlvbiBTaG93QW5kSGlkZSgpIHsKICAgIHZhciB4ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ1NlY3Rpb25OYW1lJyk7CiAgICBpZiAoeC5zdHlsZS5kaXNwbGF5ID09ICdub25lJykgewogICAgICAgIHguc3R5bGUuZGlzcGxheSA9ICdibG9jayc7CiAgICB9IGVsc2UgewogICAgICAgIHguc3R5bGUuZGlzcGxheSA9ICdub25lJzsKICAgIH0KfQo8L1NDUklQVD4K