EB3I n1 2025 scRNAseq
-
INTEGRATION
-





1 PREAMBLE

1.1 Purpose of this session

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

  • Integration of a pair of samples

1.2 The integration data set

  • We will still use data from the Paiva et al. publication

  • While we focused on the KO sample TD3a until now, we will integrate it with its wild-type counterpart TDCT

  • TDCT was processed in a very similar manner (QC, doublers, filtering, LogNormalization, HVGs and scaling) to what you just performed for TD3A

  • Both samples were halved for their number of cells, for practical reasons in the context of this training (computation speed)

  • As the purpose is to integrate both samples into a single analytical entity, the objects we will use as input are Seurat objects up to the data scaling and regression (thus, there are no dimension reduction data in these)



2 Start Rstudio

3 Warm-up

  • We set common parameters we will use throughout this session :
## Seed for the RNG (our sole parameter for this session)
my_seed <- 1337L

## Required for our big objects
options(future.globals.maxSize = 1E+09)
library(Seurat)
library(ggplot2)
library(patchwork)
library(dplyr)


4 Prepare the data structure

We will do the same as for former steps, just changing the session name :

4.1 Main directory

## Setting the project name
project_name <- "ebaii_sc_teachers"  # Do not copy-paste this ! It's MY project !!

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

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

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

4.2 Current session

## Creating the session (Preproc.1) directory
session_dir <- paste0(TD_dir, "/06_Integration")
dir.create(path = session_dir, recursive = TRUE)
## Print the session directory on-screen
print(session_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/06_Integration"

4.3 Input directory

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

## Print the input directory on-screen
print(input_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/06_Integration/DATA"

4.4 Output directory

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

## Print the output directory on-screen
print(output_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/06_Integration/RESULTS"


5 Load input data

  • The data consists into a pair of Seurat objects (one per sample) as RDS, hosted in a Zenodo respository (Id : 14081572)

5.1 Download data

  • We will directly retrieve data from Zenodo to your input_dir :
### Named files (will be used later on !)
TD3A_rds <- "TD3A_Filtered_12508.2018.RDS"
TDCT_rds <- "TDCT_Filtered_12254.1868.RDS"

## Filename(s) to retrieve
toget_files <- c(TD3A_rds,
                 TDCT_rds)

## Folder to store retrieved files
local_folder <- input_dir

## Use local backup ?
backup <- FALSE
if(backup) message("Using local backup !")

## Force download ?
force <- FALSE
if(force) message("Forcing (re)download !")

## Zenodo ID
zen_id <- '14094752'

### Define remote folder
remote_folder <- if (backup) "/shared/projects/2422_ebaii_n1/atelier_scrnaseq/TD/BACKUP/INTEGRATION/" else paste0("https://zenodo.org/records/", zen_id, "/files/")

### Reconstruct the input paths
remote_path <- paste0(remote_folder, "/", toget_files)

### Reconstruct the output paths
local_path <- paste0(local_folder, "/", toget_files)

## Retrieve files (if they don't exist), in loop
for (tg in seq_along(toget_files)) {
  ## If the file does not locally exist
  if (!file.exists(local_path[tg]) | force) {
    ## Retrieve data
    if(backup) {
      file.copy(from = remote_path[tg],
                to = local_path[tg])
    } else {
      download.file(url = remote_path[tg], 
                    destfile = local_path[tg])
    }
    ## Check if downloaded files exist locally
    if(file.exists(local_path[tg])) message("\tOK")
  } else message(paste0(toget_files[tg], " already downloaded !"))
}
TD3A_Filtered_12508.2018.RDS already downloaded !
TDCT_Filtered_12254.1868.RDS already downloaded !

5.2 Load into R

## Loading TD3A
TD3A_so <- readRDS(file = paste0(input_dir, 
                                   "/", 
                                   TD3A_rds))

## Loading TDCT
TDCT_so <- readRDS(file = paste0(input_dir, 
                                   "/",
                                   TDCT_rds))


6 Merge datasets

  • We simply merge the two objects into a single one :
TD3A_TDCT_merge <- merge(
  x = TD3A_so, ## First object to merge
  y = TDCT_so, ## Second object to merge
  add.cell.ids = c("TD3A", "TDCT"), ## Keep track of sample of origin for cells (optional)
)
  • We can take a look at its summary
## See the object summary
TD3A_TDCT_merge
Show output
An object of class Seurat 
12926 features across 3886 samples within 1 assay 
Active assay: RNA (12926 features, 0 variable features)
 4 layers present: counts.TD3A, counts.TDCT, data.TD3A, data.TDCT
## TD3A
dim(TD3A_so)
[1] 12508  2018
## TDCT
dim(TDCT_so)
[1] 12254  1868
## Merged
dim(TD3A_TDCT_merge)
[1] 12926  3886
## Clean unneeded objects
rm(TD3A_so, TDCT_so)

6.1 Process the merged dataset

In order to visualize the data in a 2-D space, one needs to process it the way one learnt :

## All steps
TD3A_TDCT_merge <- NormalizeData(object = TD3A_TDCT_merge, verbose = FALSE)
TD3A_TDCT_merge <- FindVariableFeatures(object = TD3A_TDCT_merge, nfeatures = 2000, verbose = FALSE)
TD3A_TDCT_merge <- ScaleData(object = TD3A_TDCT_merge, verbose = FALSE)
TD3A_TDCT_merge <- RunPCA(object = TD3A_TDCT_merge, npcs = 50, verbose = FALSE)
ElbowPlot(object = TD3A_TDCT_merge, ndims = 50, reduction = "pca")
Show plot

6.2 Visualization of batch effect

DimPlot(object = TD3A_TDCT_merge, 
        reduction = "pca",
        group.by = "orig.ident", 
        cols = sample_cols)
Show plot

TD3A_TDCT_merge <- RunUMAP(object = TD3A_TDCT_merge, dims = 1:20, verbose = FALSE)
DimPlot(object = TD3A_TDCT_merge, 
        reduction = "umap",
        group.by = "orig.ident", 
        cols = sample_cols)
Show plot

CCL ??

7 Integration

7.1 Seurat : CCA

7.1.1 CCA integration

computing_time <- data.frame()
start <- Sys.time()
TD3A_TDCT_merge <- IntegrateLayers(object = TD3A_TDCT_merge, 
                                      orig.reduction = "pca",
                                      method = CCAIntegration, 
                                      new.reduction = "integrated.cca", 
                                      normalization.method = "LogNormalize")
end <- Sys.time()
computing_time["cca","comptime"] <- end-start

7.1.2 Seurat : RPCA

start <- Sys.time()
TD3A_TDCT_merge <- IntegrateLayers(object = TD3A_TDCT_merge, 
                                      orig.reduction = "pca",
                                      method = RPCAIntegration, 
                                      new.reduction = "integrated.rpca", 
                                      normalization.method = "LogNormalize")
end <- Sys.time()
computing_time["rPCA","comptime"] <- end-start

7.1.3 Seurat : Harmony

start <- Sys.time()
TD3A_TDCT_merge <- IntegrateLayers(object = TD3A_TDCT_merge, 
                                      orig.reduction = "pca",
                                      method = HarmonyIntegration, 
                                      new.reduction = "harmony",
                                      normalization.method = "LogNormalize")
end <- Sys.time()
computing_time["Harmony","comptime"] <- end-start
Show output
comptime
cca 6.603909 secs
rPCA 8.666372 secs
Harmony 2.732028 secs

Reductions(object = TD3A_TDCT_merge)

7.2 Computing umap from “corrected” spaces

TD3A_TDCT_merge <- RunUMAP(object = TD3A_TDCT_merge, 
                           reduction = "integrated.cca", 
                           dims = 1:20, 
                           reduction.name = "umapcca")
TD3A_TDCT_merge <- RunUMAP(object = TD3A_TDCT_merge, 
                           reduction = "integrated.rpca",
                           dims = 1:20, 
                           reduction.name = "umaprpca")
TD3A_TDCT_merge <- RunUMAP(object = TD3A_TDCT_merge, 
                           reduction = "harmony", 
                           dims = 1:20, 
                           reduction.name = "umapharm")
Reductions(object = TD3A_TDCT_merge)
Show output
[1] "pca"             "umap"            "integrated.cca"  "integrated.rpca"
[5] "harmony"         "umapcca"         "umaprpca"        "umapharm"       

7.3 Effect of the correction/integration

7.4 Visualization of the different methods of integration: Projecting the sample id onto the different umap spaces obtained

Show plot

7.4.1 Clustering

7.4.1.0.1 Graph based on CCA Intgration
TD3A_TDCT_merge <- FindNeighbors(object = TD3A_TDCT_merge, 
                                 reduction = "integrated.cca", 
                                 dims = 1:20, 
                                 graph.name = "snn_cca")
TD3A_TDCT_merge <- FindClusters(object = TD3A_TDCT_merge,
                                graph.name = "snn_cca",
                                resolution = c(.5,.8,1), 
                                algorithm = 4)
7.4.1.0.2 Graph based on rPCA Intgration
TD3A_TDCT_merge <- FindNeighbors(object = TD3A_TDCT_merge, 
                                 reduction = "integrated.rpca", 
                                 dims = 1:20, 
                                 graph.name = "snn_rpca")
TD3A_TDCT_merge <- FindClusters(object = TD3A_TDCT_merge,
                                graph.name = "snn_rpca",
                                resolution = c(.5,.8,1), 
                                algorithm = 4)
7.4.1.0.3 Graph based on Harmony Intgration
TD3A_TDCT_merge <- FindNeighbors(object = TD3A_TDCT_merge, 
                                 reduction = "harmony", 
                                 dims = 1:20, 
                                 graph.name = "snn_harm")
TD3A_TDCT_merge <- FindClusters(object = TD3A_TDCT_merge,
                                graph.name = "snn_harm",
                                resolution = c(.5,.8,1), 
                                algorithm = 4)
Show plot

Show plot

Show plot

Show plot

Show plot

Show plot

table("snn_harm_res.1" = TD3A_TDCT_merge$snn_harm_res.1,
      "snn_rpca_res.1" = TD3A_TDCT_merge$snn_rpca_res.1)
Show output
              snn_rpca_res.1
snn_harm_res.1   1   2   3   4   5   6   7   8   9  10  11  12  13
            1  448   1  18 124  50   1   0   3   4   0   0   0   0
            2   74   0  32  58  72   0   0 294   6   0   0   0   0
            3   21   0 454   0   0   0   0  13   4   0   0   0   0
            4    0 439   0   2   3   5   0   0   1   0   0   0  12
            5    3  60   0  41 303   6   0   1   1   0   0   0   0
            6   10   0   0   0   0 373   0   0   1   1   0   0   0
            7    5  20   3 224   4   0   0   2   3   0   0   0   0
            8    0   0   0   0   0   0 243   0   0   0   0   0   1
            9    8   5   6   5   6   1   0   2 116   0   0   0   0
            10   0   0   0   0   0   0  82   0   0   0   0   0   0
            11   0   0   0   3   0   0   0   2   0  69   0   0   0
            12   0   0   0   0   0   1   0   0   0   0  64   0   0
            13   0   0   0   0   0   1   0   0   0   1   1  47   0
            14   0   0   0   0   0   0   0   0   0   0   0   0  22
table(TD3A_TDCT_merge$orig.ident, TD3A_TDCT_merge$snn_cca_res.1)
Show output
      
         1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
  TD3A 422 184 226 193 199 239 144  75 113  69  24  59  54   7  10
  TDCT 286 326 229 207 197 114 136 168  31  32  58   8   9  43  24
table(TD3A_TDCT_merge$orig.ident, TD3A_TDCT_merge$snn_harm_res.1)
Show output
      
         1   2   3   4   5   6   7   8   9  10  11  12  13  14
  TD3A 423 218 303 144 142 190 231  77 130  24  67  56   6   7
  TDCT 226 318 189 318 273 195  30 167  19  58   7   9  44  15
TD3A_TDCT_merge <- JoinLayers(object = TD3A_TDCT_merge, assay = "RNA")

If we have extra-time

Idents(object = TD3A_TDCT_merge) <- "snn_harm_res.1"
harm_res1_markers <- FindAllMarkers(
  object = TD3A_TDCT_merge,
  only.pos = TRUE,
  logfc.threshold = 1
)
harm_res1_topmarkers <- harm_res1_markers %>%
  group_by(cluster) %>%
  dplyr::slice_head(n = 20)
DoHeatmap(object = TD3A_TDCT_merge,
          features = harm_res1_topmarkers$gene,
          group.by = "snn_harm_res.1")
Show plot

8 Save the Seurat object

## A name for our Seurat object file (rich naming)
out_name <- paste0(
          output_dir, "/", paste(
            c("Twelve", Seurat::Project(TD3A_TDCT_merge), "S5", 
              "Integrated", paste(
                dim(TD3A_TDCT_merge), collapse = '.'
              )
            ), collapse = "_"),
            ".RDS")

## Check (I like this)
print(out_name)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/06_Integration/RESULTS/Twelve_SeuratProject_S5_Integrated_12926.3886.RDS"
## Write on disk
saveRDS(object = TD3A_TDCT_merge, 
        file = out_name)





9 Rsession

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

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

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

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

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

other attached packages:
[1] future_1.49.0      dplyr_1.1.4        patchwork_1.3.0    ggplot2_3.5.2     
[5] Seurat_5.3.0       SeuratObject_5.1.0 sp_2.2-0          

loaded via a namespace (and not attached):
  [1] deldir_2.0-4           pbapply_1.7-2          gridExtra_2.3         
  [4] rlang_1.1.6            magrittr_2.0.3         RcppAnnoy_0.0.22      
  [7] spatstat.geom_3.4-1    matrixStats_1.5.0      ggridges_0.5.6        
 [10] compiler_4.4.1         png_0.1-8              vctrs_0.6.5           
 [13] reshape2_1.4.4         stringr_1.5.1          pkgconfig_2.0.3       
 [16] fastmap_1.2.0          labeling_0.4.3         rmdformats_1.0.4      
 [19] promises_1.3.2         rmarkdown_2.29         purrr_1.0.4           
 [22] xfun_0.52              cachem_1.1.0           jsonlite_2.0.0        
 [25] goftest_1.2-3          later_1.4.2            spatstat.utils_3.1-4  
 [28] irlba_2.3.5.1          parallel_4.4.1         cluster_2.1.6         
 [31] R6_2.6.1               ica_1.0-3              spatstat.data_3.1-6   
 [34] bslib_0.9.0            stringi_1.8.7          RColorBrewer_1.1-3    
 [37] limma_3.60.6           reticulate_1.42.0      spatstat.univar_3.1-3 
 [40] parallelly_1.45.0      lmtest_0.9-40          jquerylib_0.1.4       
 [43] scattermore_1.2        Rcpp_1.0.14            bookdown_0.39         
 [46] knitr_1.50             tensor_1.5             future.apply_1.11.3   
 [49] zoo_1.8-14             sctransform_0.4.2      httpuv_1.6.15         
 [52] Matrix_1.7-3           splines_4.4.1          igraph_2.1.4          
 [55] tidyselect_1.2.1       abind_1.4-8            rstudioapi_0.17.1     
 [58] dichromat_2.0-0.1      yaml_2.3.10            spatstat.random_3.4-1 
 [61] codetools_0.2-20       miniUI_0.1.2           spatstat.explore_3.4-3
 [64] listenv_0.9.1          lattice_0.22-6         tibble_3.2.1          
 [67] plyr_1.8.9             withr_3.0.2            shiny_1.10.0          
 [70] ROCR_1.0-11            evaluate_1.0.3         Rtsne_0.17            
 [73] fastDummies_1.7.5      survival_3.7-0         polyclip_1.10-7       
 [76] fitdistrplus_1.2-2     pillar_1.10.2          leidenbase_0.1.35     
 [79] KernSmooth_2.23-24     plotly_4.10.4          generics_0.1.4        
 [82] RcppHNSW_0.6.0         scales_1.4.0           globals_0.18.0        
 [85] xtable_1.8-4           RhpcBLASctl_0.23-42    glue_1.8.0            
 [88] lazyeval_0.2.2         tools_4.4.1            data.table_1.17.4     
 [91] RSpectra_0.16-2        RANN_2.6.2             dotCall64_1.2         
 [94] cowplot_1.1.3          grid_4.4.1             tidyr_1.3.1           
 [97] nlme_3.1-165           presto_1.0.0           cli_3.6.5             
[100] spatstat.sparse_3.1-0  spam_2.11-1            viridisLite_0.4.2     
[103] uwot_0.2.3             gtable_0.3.6           sass_0.4.10           
[106] digest_0.6.37          progressr_0.15.1       ggrepel_0.9.6         
[109] htmlwidgets_1.6.4      farver_2.1.2           htmltools_0.5.8.1     
[112] lifecycle_1.0.4        httr_1.4.7             statmod_1.5.0         
[115] mime_0.13              harmony_1.2.3          MASS_7.3-65           
LS0tCnRpdGxlOiAiPENFTlRFUj5FQjNJIG4xIDIwMjUgc2NSTkFzZXE8QlI+LTxCUj48Qj5JTlRFR1JBVElPTjwvQj48QlI+LTxCUj48L0NFTlRFUj4iCmRhdGU6ICIyMDI1LTE2LTIxLjIyIgphdXRob3I6CiAgLSBuYW1lOiAiRUIzSSBuMSBzY1JOQXNlcSBUZWFtIgogIC0gbmFtZTogIkJhc3RpZW4gSk9CIgogICAgZW1haWw6ICJiYXN0aWVuLmpvYkBndXN0YXZlcm91c3N5LmZyIgogIC0gbmFtZTogIlPDqWIgTUVMTEEiCiAgICBlbWFpbDogInNlYmFzdGllbi5tZWxsYUBtYWVzdHJvLnBhc3RldXIuZnIiIApvdXRwdXQ6CiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246CiAgICBmaWdfd2lkdGg6IDgKICAgIGZpZ19oZWlnaHQ6IDYKICAgIGhpZ2hsaWdodDogdGFuZ28gICMjIFRoZW1lIGZvciB0aGUgY29kZSBjaHVua3MKICAgIGVtYmVkX2ZvbnRzOiBUUlVFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUgICMjIEFkZHMgbnVtYmVyIHRvIGhlYWRlcnMgKHNlY3Rpb25zKQogICAgdGhlbWU6IGZsYXRseSAgIyMgQ1NTIHRoZW1lIGZvciB0aGUgSFRNTCBwYWdlCiAgICBjb2xsYXBzZWQ6IHRydWUgICMjIEJ5IGRlZmF1bHQsIHRoZSBUT0MgaXMgZm9sZGVkCiAgICB0b2NfZGVwdGg6IDMKICAgIHNtb290aF9zY3JvbGw6IHRydWUgIyMgU21vb3RoIHNjcm9sbCBvZiB0aGUgSFRNTCBwYWdlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZSAjIyBJbmNsdWRlcyBhbGwgcGxvdHMvaW1hZ2VzIHdpdGhpbiB0aGUgSFRNTAogICAgY29kZV9kb3dubG9hZDogdHJ1ZSAjIyBBZGRzIGEgYnV0dG9uIHRvIGRvd25sb2FkIHRoZSBSbWQKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdGh1bWJuYWlsczogZmFsc2UKICAgIGxpZ2h0Ym94OiB0cnVlCiAgICBmaWdfY2FwdGlvbjogZmFsc2UKICAgIGdhbGxlcnk6IHRydWUKICAgIHVzZV9ib29rZG93bjogdHJ1ZQphbHdheXNfYWxsb3dfaHRtbDogdHJ1ZSAjIyBBbGxvdyBwbGFpbiBIVE1MIGNvZGUgaW4gdGhlIFJtZAplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgo8IS0tIGtuaXQgc2V0dXAgLS0+CgpgYGB7ciBrbml0X3NldHVwLCBlY2hvID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwgICAgICAgICMgUHJpbnQgdGhlIGNvZGUKICBldmFsID0gVFJVRSwgICAgICAgICMgUnVuIGNvbW1hbmQgbGluZXMKICBtZXNzYWdlID0gRkFMU0UsICAgICMgUHJpbnQgbWVzc2FnZXMKICBwcm9tcHQgPSBGQUxTRSwgICAgICMgRG8gbm90IGRpc3BsYXkgcHJvbXB0CiAgY29tbWVudCA9IE5BLCAgICAgICAjIE5vIGNvbW1lbnRzIG9uIHRoaXMgc2VjdGlvbgogIHdhcm5pbmcgPSBGQUxTRSwgICAgIyBEaXNwbGF5IHdhcm5pbmdzCiAgdGlkeSA9IEZBTFNFLAogIGZpZy5hbGlnbj0iY2VudGVyIiwgCiAgIyByZXN1bHRzID0gJ2hpZGUnLAogIHdpZHRoID0gMTAwICAgICAgICMgTnVtYmVyIG9mIGNoYXJhY3RlcnMgcGVyIGxpbmUKKQpgYGAKCjwhLS0gQ1NTIHRvIGNvbG9yIGNodW5rcyBhbmQgb3V0cHV0cyAtLT4KCmBgYHtjc3MsIGVjaG89RkFMU0V9Ci5ub3RydW4gewogIGJhY2tncm91bmQtY29sb3I6IGxpZ2h0Z3JleSAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGJsYWNrICFpbXBvcnRhbnQ7Cn0KLm5vdHJ1bm8gewogIGJhY2tncm91bmQtY29sb3I6IGxpZ2h0Z3JleSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQoucXVlc3Rpb24gewogIGJhY2tncm91bmQtY29sb3I6IGFxdWFtYXJpbmUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgbGltZWdyZWVuICFpbXBvcnRhbnQ7Cn0KLnF1ZXN0aW9ubyB7CiAgYmFja2dyb3VuZC1jb2xvcjogYXF1YW1hcmluZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQouYW5zd2VyIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBuYXZham93aGl0ZSAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGJyb3duICFpbXBvcnRhbnQ7Cn0KLmFuc3dlcm8gewogIGJhY2tncm91bmQtY29sb3I6IG5hdmFqb3doaXRlICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9Ci5iZXlvbmQgewogIGJhY2tncm91bmQtY29sb3I6IHZpb2xldCAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIHB1cnBsZSAhaW1wb3J0YW50Owp9Ci5iZXlvbmRvIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB2aW9sZXQgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KYGBgCgo8IS0tIEhvb2sgdG8gaGFuZGxlIGNvZGUgYmxvY2tzIG91dHB1dCBmb2xkaW5nIC0tPgoKYGBge3Iga25pdF9ob29rLCBlY2hvID0gRkFMU0V9Cmhvb2tzID0ga25pdHI6OmtuaXRfaG9va3MkZ2V0KCkKaG9va19mb2xkYWJsZSA9IGZ1bmN0aW9uKHR5cGUpIHsKICBmb3JjZSh0eXBlKQogIGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgIHJlcyA9IGhvb2tzW1t0eXBlXV0oeCwgb3B0aW9ucykKICAgIAogICAgaWYgKGlzRkFMU0Uob3B0aW9uc1tbcGFzdGUwKCJmb2xkLiIsIHR5cGUpXV0pKSByZXR1cm4ocmVzKQogICAgCiAgICBwYXN0ZTAoCiAgICAgICI8ZGV0YWlscz48c3VtbWFyeT5TaG93ICIsIHR5cGUsICI8L3N1bW1hcnk+XG5cbiIsCiAgICAgIHJlcywKICAgICAgIlxuXG48L2RldGFpbHM+IgogICAgKQogIH0KfQprbml0cjo6a25pdF9ob29rcyRzZXQoCiAgb3V0cHV0ID0gaG9va19mb2xkYWJsZSgib3V0cHV0IiksCiAgcGxvdCA9IGhvb2tfZm9sZGFibGUoInBsb3QiKQopCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxjZW50ZXI+IVtdKGViM2lfYmFubmVyLnBuZyk8L2NlbnRlcj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFBSRUFNQkxFCgojIyBQdXJwb3NlIG9mIHRoaXMgc2Vzc2lvbgoKVGhpcyBmaWxlIGRlc2NyaWJlcyB0aGUgZGlmZmVyZW50IHN0ZXBzIHRvIHBlcmZvcm0gKipzaXh0aCoqIHBhcnQgb2YgZGF0YSAKcHJvY2Vzc2luZyBmb3IgdGhlIHNpbmdsZSBjZWxsIFJOQXNlcSBkYXRhIGFuYWx5c2lzIHRyYWluaW5nIGNvdXJzZSBmb3IgCnRoZSAqKkVCQUlJIG4xIDIwMjQqKiwgY292ZXJpbmcgdGhlc2Ugc3RlcHMgOgoKKiAqKkludGVncmF0aW9uKiogb2YgYSBwYWlyIG9mIHNhbXBsZXMKCiMjIFRoZSBpbnRlZ3JhdGlvbiBkYXRhIHNldAoKLSAgIFdlIHdpbGwgc3RpbGwgdXNlIGRhdGEgZnJvbSB0aGUgW1BhaXZhIGV0CiAgICBhbC5dKGh0dHBzOi8vZmVicy5vbmxpbmVsaWJyYXJ5LndpbGV5LmNvbS9kb2kvZnVsbC8xMC4xMTExL2ZlYnMuMTQ2NTEpCiAgICBwdWJsaWNhdGlvbgogICAgCi0gICBXaGlsZSB3ZSBmb2N1c2VkIG9uIHRoZSAqKktPIHNhbXBsZSBgVEQzYWAqKiB1bnRpbCBub3csIHdlIHdpbGwgaW50ZWdyYXRlIAogICAgaXQgd2l0aCBpdHMgKip3aWxkLXR5cGUqKiBjb3VudGVycGFydCAqKmBURENUYCoqCgotICAgYFREQ1RgIHdhcyBwcm9jZXNzZWQgaW4gYSB2ZXJ5ICoqc2ltaWxhciBtYW5uZXIqKiAoUUMsIGRvdWJsZXJzLCBmaWx0ZXJpbmcsCiAgICBMb2dOb3JtYWxpemF0aW9uLCBIVkdzIGFuZCBzY2FsaW5nKSB0byB3aGF0IHlvdSBqdXN0IHBlcmZvcm1lZCBmb3IgYFREM0FgCiAgICAKLSAgIEJvdGggc2FtcGxlcyB3ZXJlIGhhbHZlZCBmb3IgdGhlaXIgbnVtYmVyIG9mIGNlbGxzLCBmb3IgcHJhY3RpY2FsIHJlYXNvbnMKICAgIGluIHRoZSBjb250ZXh0IG9mIHRoaXMgdHJhaW5pbmcgKGNvbXB1dGF0aW9uIHNwZWVkKQogICAgCi0gICBBcyB0aGUgcHVycG9zZSBpcyB0byBpbnRlZ3JhdGUgYm90aCBzYW1wbGVzIGludG8gYSBzaW5nbGUgYW5hbHl0aWNhbCBlbnRpdHksCiAgICB0aGUgb2JqZWN0cyB3ZSB3aWxsIHVzZSBhcyBpbnB1dCBhcmUgU2V1cmF0IG9iamVjdHMgdXAgdG8gdGhlIGRhdGEgKipzY2FsaW5nCiAgICBhbmQgcmVncmVzc2lvbioqICh0aHVzLCB0aGVyZSBhcmUgbm8gZGltZW5zaW9uIHJlZHVjdGlvbiBkYXRhIGluIHRoZXNlKQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgU3RhcnQgUnN0dWRpbwoKLSAgIFVzaW5nIHRoZSBbT3Blbk9uRGVtYW5kIGNoZWF0CiAgICBzaGVldF0oaHR0cHM6Ly9pZmItZWxpeGlyZnIuZ2l0aHViLmlvL0VCQUlJLzIwMjMvZWJhaWluMS9TaW5nbGVDZWxsLzIwMjRfVERfT3Blbk9uRGVtYW5kLmh0bWwpLAogICAgY29ubmVjdCB0byB0aGUgW09wZW5PbkRlbWFuZAogICAgcG9ydGFsXShodHRwczovL29uZGVtYW5kLmNsdXN0ZXIuZnJhbmNlLWJpb2luZm9ybWF0aXF1ZS5mcikgYW5kCiAgICAqKmNyZWF0ZSBhIFJzdHVkaW8gc2Vzc2lvbioqIHdpdGggdGhlIHJpZ2h0IHJlc291cmNlIHJlcXVpcmVtZW50cy4KCiMgV2FybS11cAoKLSAgIFdlIHNldCAqKmNvbW1vbiBwYXJhbWV0ZXJzKiogd2Ugd2lsbCB1c2UgdGhyb3VnaG91dCB0aGlzIHNlc3Npb24gOgoKYGBge3Igc2V0cGFyYW19CiMjIFNlZWQgZm9yIHRoZSBSTkcgKG91ciBzb2xlIHBhcmFtZXRlciBmb3IgdGhpcyBzZXNzaW9uKQpteV9zZWVkIDwtIDEzMzdMCgojIyBSZXF1aXJlZCBmb3Igb3VyIGJpZyBvYmplY3RzCm9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDFFKzA5KQpgYGAKCmBgYHtyIGxvYWQgbGlicmFyaWVzfQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShkcGx5cikKYGBgCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUHJlcGFyZSB0aGUgZGF0YSBzdHJ1Y3R1cmUKCldlIHdpbGwgZG8gdGhlIHNhbWUgYXMgZm9yIGZvcm1lciBzdGVwcywganVzdCBjaGFuZ2luZyB0aGUgc2Vzc2lvbiBuYW1lCjoKCiMjIE1haW4gZGlyZWN0b3J5CgpgYGB7ciBtYWluZGlyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIyBTZXR0aW5nIHRoZSBwcm9qZWN0IG5hbWUKcHJvamVjdF9uYW1lIDwtICJlYmFpaV9zY190ZWFjaGVycyIgICMgRG8gbm90IGNvcHktcGFzdGUgdGhpcyAhIEl0J3MgTVkgcHJvamVjdCAhIQoKIyMgUHJlcGFyaW5nIHRoZSBwYXRoClREX2RpciA8LSBwYXN0ZTAoIi9zaGFyZWQvcHJvamVjdHMvIiwgcHJvamVjdF9uYW1lLCAiL1NDX1REIikKCiMjIENyZWF0aW5nIHRoZSByb290IGRpcmVjdG9yeQpkaXIuY3JlYXRlKHBhdGggPSBURF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgcm9vdCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KFREX2RpcikKYGBgCgojIyBDdXJyZW50IHNlc3Npb24KCmBgYHtyIHNlc3Npb25kaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIENyZWF0aW5nIHRoZSBzZXNzaW9uIChQcmVwcm9jLjEpIGRpcmVjdG9yeQpzZXNzaW9uX2RpciA8LSBwYXN0ZTAoVERfZGlyLCAiLzA2X0ludGVncmF0aW9uIikKZGlyLmNyZWF0ZShwYXRoID0gc2Vzc2lvbl9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCiMjIFByaW50IHRoZSBzZXNzaW9uIGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoc2Vzc2lvbl9kaXIpCmBgYAoKIyMgSW5wdXQgZGlyZWN0b3J5CgpgYGB7ciBpbmRpciwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyMgQ3JlYXRpbmcgdGhlIElOUFVUIGRhdGEgZGlyZWN0b3J5CmlucHV0X2RpciA8LSBwYXN0ZTAoc2Vzc2lvbl9kaXIsICIvREFUQSIpCmRpci5jcmVhdGUocGF0aCA9IGlucHV0X2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSBpbnB1dCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KGlucHV0X2RpcikKYGBgCgojIyBPdXRwdXQgZGlyZWN0b3J5CgpgYGB7ciBvdXRkaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIENyZWF0aW5nIHRoZSBPVVRQVVQgZGF0YSBkaXJlY3RvcnkKb3V0cHV0X2RpciA8LSBwYXN0ZTAoc2Vzc2lvbl9kaXIsICIvUkVTVUxUUyIpCmRpci5jcmVhdGUocGF0aCA9IG91dHB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgb3V0cHV0IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQob3V0cHV0X2RpcikKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBMb2FkIGlucHV0IGRhdGEKCi0gICBUaGUgZGF0YSBjb25zaXN0cyBpbnRvIGEgKipwYWlyIG9mIFNldXJhdCBvYmplY3RzKiogKG9uZSBwZXIgc2FtcGxlKSAKICAgIGFzIFJEUywgaG9zdGVkIGluIGEgWmVub2RvIHJlc3Bvc2l0b3J5IChJZCA6CiAgICBbMTQwODE1NzJdKGh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmRzLzE0MDgxNTcyICJaZW5vZG8gSW50ZWdyYXRpb24iKXsjMTQwODE1NzJ9KQoKIyMgRG93bmxvYWQgZGF0YQoKLSAgIFdlIHdpbGwgZGlyZWN0bHkgcmV0cmlldmUgZGF0YSBmcm9tIFplbm9kbyB0byB5b3VyIGBpbnB1dF9kaXJgIDoKCmBgYHtyIGRsX2ludCwgbWVzc2FnZSA9IFRSVUUsIGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIGZvbGQub3V0cHV0ID0gRkFMU0UsIGZvbGQucGxvdCA9IEZBTFNFfQojIyMgTmFtZWQgZmlsZXMgKHdpbGwgYmUgdXNlZCBsYXRlciBvbiAhKQpURDNBX3JkcyA8LSAiVEQzQV9GaWx0ZXJlZF8xMjUwOC4yMDE4LlJEUyIKVERDVF9yZHMgPC0gIlREQ1RfRmlsdGVyZWRfMTIyNTQuMTg2OC5SRFMiCgojIyBGaWxlbmFtZShzKSB0byByZXRyaWV2ZQp0b2dldF9maWxlcyA8LSBjKFREM0FfcmRzLAogICAgICAgICAgICAgICAgIFREQ1RfcmRzKQoKIyMgRm9sZGVyIHRvIHN0b3JlIHJldHJpZXZlZCBmaWxlcwpsb2NhbF9mb2xkZXIgPC0gaW5wdXRfZGlyCgojIyBVc2UgbG9jYWwgYmFja3VwID8KYmFja3VwIDwtIEZBTFNFCmlmKGJhY2t1cCkgbWVzc2FnZSgiVXNpbmcgbG9jYWwgYmFja3VwICEiKQoKIyMgRm9yY2UgZG93bmxvYWQgPwpmb3JjZSA8LSBGQUxTRQppZihmb3JjZSkgbWVzc2FnZSgiRm9yY2luZyAocmUpZG93bmxvYWQgISIpCgojIyBaZW5vZG8gSUQKemVuX2lkIDwtICcxNDA5NDc1MicKCiMjIyBEZWZpbmUgcmVtb3RlIGZvbGRlcgpyZW1vdGVfZm9sZGVyIDwtIGlmIChiYWNrdXApICIvc2hhcmVkL3Byb2plY3RzLzI0MjJfZWJhaWlfbjEvYXRlbGllcl9zY3JuYXNlcS9URC9CQUNLVVAvSU5URUdSQVRJT04vIiBlbHNlIHBhc3RlMCgiaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvIiwgemVuX2lkLCAiL2ZpbGVzLyIpCgojIyMgUmVjb25zdHJ1Y3QgdGhlIGlucHV0IHBhdGhzCnJlbW90ZV9wYXRoIDwtIHBhc3RlMChyZW1vdGVfZm9sZGVyLCAiLyIsIHRvZ2V0X2ZpbGVzKQoKIyMjIFJlY29uc3RydWN0IHRoZSBvdXRwdXQgcGF0aHMKbG9jYWxfcGF0aCA8LSBwYXN0ZTAobG9jYWxfZm9sZGVyLCAiLyIsIHRvZ2V0X2ZpbGVzKQoKIyMgUmV0cmlldmUgZmlsZXMgKGlmIHRoZXkgZG9uJ3QgZXhpc3QpLCBpbiBsb29wCmZvciAodGcgaW4gc2VxX2Fsb25nKHRvZ2V0X2ZpbGVzKSkgewogICMjIElmIHRoZSBmaWxlIGRvZXMgbm90IGxvY2FsbHkgZXhpc3QKICBpZiAoIWZpbGUuZXhpc3RzKGxvY2FsX3BhdGhbdGddKSB8IGZvcmNlKSB7CiAgICAjIyBSZXRyaWV2ZSBkYXRhCiAgICBpZihiYWNrdXApIHsKICAgICAgZmlsZS5jb3B5KGZyb20gPSByZW1vdGVfcGF0aFt0Z10sCiAgICAgICAgICAgICAgICB0byA9IGxvY2FsX3BhdGhbdGddKQogICAgfSBlbHNlIHsKICAgICAgZG93bmxvYWQuZmlsZSh1cmwgPSByZW1vdGVfcGF0aFt0Z10sIAogICAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gbG9jYWxfcGF0aFt0Z10pCiAgICB9CiAgICAjIyBDaGVjayBpZiBkb3dubG9hZGVkIGZpbGVzIGV4aXN0IGxvY2FsbHkKICAgIGlmKGZpbGUuZXhpc3RzKGxvY2FsX3BhdGhbdGddKSkgbWVzc2FnZSgiXHRPSyIpCiAgfSBlbHNlIG1lc3NhZ2UocGFzdGUwKHRvZ2V0X2ZpbGVzW3RnXSwgIiBhbHJlYWR5IGRvd25sb2FkZWQgISIpKQp9CmBgYAoKIyMgTG9hZCBpbnRvIFIKCmBgYHtyIHJkc19sb2FkfQojIyBMb2FkaW5nIFREM0EKVEQzQV9zbyA8LSByZWFkUkRTKGZpbGUgPSBwYXN0ZTAoaW5wdXRfZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiLyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREM0FfcmRzKSkKCiMjIExvYWRpbmcgVERDVApURENUX3NvIDwtIHJlYWRSRFMoZmlsZSA9IHBhc3RlMChpbnB1dF9kaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBURENUX3JkcykpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIE1lcmdlIGRhdGFzZXRzCgotICAgV2Ugc2ltcGx5IG1lcmdlIHRoZSB0d28gb2JqZWN0cyBpbnRvIGEgc2luZ2xlIG9uZSA6CgpgYGB7ciBtZXJnZX0KVEQzQV9URENUX21lcmdlIDwtIG1lcmdlKAogIHggPSBURDNBX3NvLCAjIyBGaXJzdCBvYmplY3QgdG8gbWVyZ2UKICB5ID0gVERDVF9zbywgIyMgU2Vjb25kIG9iamVjdCB0byBtZXJnZQogIGFkZC5jZWxsLmlkcyA9IGMoIlREM0EiLCAiVERDVCIpLCAjIyBLZWVwIHRyYWNrIG9mIHNhbXBsZSBvZiBvcmlnaW4gZm9yIGNlbGxzIChvcHRpb25hbCkKKQpgYGAKCi0gICBXZSBjYW4gdGFrZSBhIGxvb2sgYXQgaXRzIHN1bW1hcnkKCmBgYHtyIHNvYmptX3NlZX0KIyMgU2VlIHRoZSBvYmplY3Qgc3VtbWFyeQpURDNBX1REQ1RfbWVyZ2UKYGBgCgoKYGBge3IgbWVyZ2VfZGltcywgd2FybmluZyA9IEZBTFNFLCBmb2xkLm91dHB1dCA9IEZBTFNFLCBmb2xkLnBsb3QgPSBGQUxTRX0KIyMgVEQzQQpkaW0oVEQzQV9zbykKCiMjIFREQ1QKZGltKFREQ1Rfc28pCgojIyBNZXJnZWQKZGltKFREM0FfVERDVF9tZXJnZSkKYGBgCgoKYGBge3J9CiMjIENsZWFuIHVubmVlZGVkIG9iamVjdHMKcm0oVEQzQV9zbywgVERDVF9zbykKYGBgCgo8L2Rpdj4KCiMjIFByb2Nlc3MgdGhlIG1lcmdlZCBkYXRhc2V0CgpJbiBvcmRlciB0byB2aXN1YWxpemUgdGhlIGRhdGEgaW4gYSAyLUQgc3BhY2UsIG9uZSBuZWVkcyB0byBwcm9jZXNzIGl0IHRoZSB3YXkKb25lIGxlYXJudCA6CgpgYGB7ciBtcHJvY30KIyMgQWxsIHN0ZXBzClREM0FfVERDVF9tZXJnZSA8LSBOb3JtYWxpemVEYXRhKG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwgdmVyYm9zZSA9IEZBTFNFKQpURDNBX1REQ1RfbWVyZ2UgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLCBuZmVhdHVyZXMgPSAyMDAwLCB2ZXJib3NlID0gRkFMU0UpClREM0FfVERDVF9tZXJnZSA8LSBTY2FsZURhdGEob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLCB2ZXJib3NlID0gRkFMU0UpClREM0FfVERDVF9tZXJnZSA8LSBSdW5QQ0Eob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLCBucGNzID0gNTAsIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgoKYGBge3IgZWxib3d9CkVsYm93UGxvdChvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIG5kaW1zID0gNTAsIHJlZHVjdGlvbiA9ICJwY2EiKQpgYGAKCiMjIFZpc3VhbGl6YXRpb24gb2YgYmF0Y2ggZWZmZWN0CgpgYGB7ciBzZXR0aW5nU2FtcGxlQ29scywgZWNobz1GQUxTRX0Kc2FtcGxlX2NvbHMgPC0gYygiZ3JleTMwIiwgImluZGlhbnJlZDEiKQpgYGAKCgpgYGB7ciAsIG1lc3NhZ2UgPSBGQUxTRX0KRGltUGxvdChvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgIHJlZHVjdGlvbiA9ICJwY2EiLAogICAgICAgIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLCAKICAgICAgICBjb2xzID0gc2FtcGxlX2NvbHMpCmBgYAoKYGBge3J9ClREM0FfVERDVF9tZXJnZSA8LSBSdW5VTUFQKG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwgZGltcyA9IDE6MjAsIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgpgYGB7cn0KRGltUGxvdChvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwKICAgICAgICBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwgCiAgICAgICAgY29scyA9IHNhbXBsZV9jb2xzKQpgYGAKCkNDTCA/PwoKCgojIEludGVncmF0aW9uCgojIyBTZXVyYXQgOiBbQ0NBXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L3JlZmVyZW5jZS9jY2FpbnRlZ3JhdGlvbikKCiMjIyBDQ0EgaW50ZWdyYXRpb24KCmBgYHtyfQpjb21wdXRpbmdfdGltZSA8LSBkYXRhLmZyYW1lKCkKYGBgCgoKYGBge3IgY2NhIGludGVncmF0aW9uLCBldmFsPVRSVUV9CnN0YXJ0IDwtIFN5cy50aW1lKCkKVEQzQV9URENUX21lcmdlIDwtIEludGVncmF0ZUxheWVycyhvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yaWcucmVkdWN0aW9uID0gInBjYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gQ0NBSW50ZWdyYXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5yZWR1Y3Rpb24gPSAiaW50ZWdyYXRlZC5jY2EiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiKQplbmQgPC0gU3lzLnRpbWUoKQpjb21wdXRpbmdfdGltZVsiY2NhIiwiY29tcHRpbWUiXSA8LSBlbmQtc3RhcnQKYGBgCgojIyMgU2V1cmF0IDogW1JQQ0FdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvaW50ZWdyYXRpb25fcnBjYSkKCmBgYHtyIHJQQ0EgaW50ZWdyYXRpb259CnN0YXJ0IDwtIFN5cy50aW1lKCkKVEQzQV9URENUX21lcmdlIDwtIEludGVncmF0ZUxheWVycyhvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yaWcucmVkdWN0aW9uID0gInBjYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gUlBDQUludGVncmF0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcucmVkdWN0aW9uID0gImludGVncmF0ZWQucnBjYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIpCmVuZCA8LSBTeXMudGltZSgpCmNvbXB1dGluZ190aW1lWyJyUENBIiwiY29tcHRpbWUiXSA8LSBlbmQtc3RhcnQKYGBgCgojIyMgU2V1cmF0IDogW0hhcm1vbnldKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvcmVmZXJlbmNlL2hhcm1vbnlpbnRlZ3JhdGlvbikKCmBgYHtyIGhhcm1vbnkgaW50ZWdyYXRpb259CnN0YXJ0IDwtIFN5cy50aW1lKCkKVEQzQV9URENUX21lcmdlIDwtIEludGVncmF0ZUxheWVycyhvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yaWcucmVkdWN0aW9uID0gInBjYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gSGFybW9ueUludGVncmF0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcucmVkdWN0aW9uID0gImhhcm1vbnkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIpCmVuZCA8LSBTeXMudGltZSgpCmNvbXB1dGluZ190aW1lWyJIYXJtb255IiwiY29tcHRpbWUiXSA8LSBlbmQtc3RhcnQKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0Ka25pdHI6OmthYmxlKAogIGNvbXB1dGluZ190aW1lCiAgKQpgYGAKClJlZHVjdGlvbnMob2JqZWN0ID0gVEQzQV9URENUX21lcmdlKQoKIyMgQ29tcHV0aW5nIHVtYXAgZnJvbSAiY29ycmVjdGVkIiBzcGFjZXMKCmBgYHtyIGNjYSB1bWFwLCBldmFsPVRSVUV9ClREM0FfVERDVF9tZXJnZSA8LSBSdW5VTUFQKG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJpbnRlZ3JhdGVkLmNjYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gMToyMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbi5uYW1lID0gInVtYXBjY2EiKQpgYGAKCmBgYHtyIHJwY2EgdW1hcCwgZXZhbD1UUlVFfQpURDNBX1REQ1RfbWVyZ2UgPC0gUnVuVU1BUChvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAiaW50ZWdyYXRlZC5ycGNhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltcyA9IDE6MjAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24ubmFtZSA9ICJ1bWFwcnBjYSIpCmBgYAoKYGBge3IgaGFybW9ueSB1bWFwLCBldmFsPVRSVUV9ClREM0FfVERDVF9tZXJnZSA8LSBSdW5VTUFQKG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJoYXJtb255IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbXMgPSAxOjIwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uLm5hbWUgPSAidW1hcGhhcm0iKQpgYGAKCmBgYHtyfQpSZWR1Y3Rpb25zKG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSkKYGBgCgojIyBFZmZlY3Qgb2YgdGhlIGNvcnJlY3Rpb24vaW50ZWdyYXRpb24KCiMjIFZpc3VhbGl6YXRpb24gb2YgdGhlIGRpZmZlcmVudCBtZXRob2RzIG9mIGludGVncmF0aW9uOiBQcm9qZWN0aW5nIHRoZSBzYW1wbGUgaWQgb250byB0aGUgZGlmZmVyZW50IHVtYXAgc3BhY2VzIG9idGFpbmVkCgpgYGB7ciwgZWNobz1GQUxTRX0Kbm9fY29yciA8LSBEaW1QbG90KG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgY29scyA9IHNhbXBsZV9jb2xzKSsKICBnZ3RpdGxlKCJObyBDb3JyZWN0aW9uIikrCiAgdGhlbWVfbGluZWRyYXcoKSsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gLjUpKQoKY2NhX2NvcnIgPC0gRGltUGxvdChvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiLCByZWR1Y3Rpb24gPSAidW1hcGNjYSIsIGNvbHMgPSBzYW1wbGVfY29scykrCiAgZ2d0aXRsZSgiQ0NBIEludGVncmF0aW9uIikrCiAgdGhlbWVfbGluZWRyYXcoKSsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gLjUpKQoKcnBjYV9jb3JyIDwtIERpbVBsb3Qob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwgcmVkdWN0aW9uID0gInVtYXBycGNhIiwgY29scyA9IHNhbXBsZV9jb2xzKSsKICBnZ3RpdGxlKCJyUENBIEludGVncmF0aW9uIikrCiAgdGhlbWVfbGluZWRyYXcoKSsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gLjUpKQoKaGFybV9jb3JyIDwtIERpbVBsb3Qob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwgcmVkdWN0aW9uID0gInVtYXBoYXJtIiwgY29scyA9IHNhbXBsZV9jb2xzKSsKICBnZ3RpdGxlKCJIYXJtb255IEludGVncmF0aW9uIikrCiAgdGhlbWVfbGluZWRyYXcoKSsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gLjUpKQpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTksIGVjaG89RkFMU0V9Cm5vX2NvcnIgKyBjY2FfY29yciArIHJwY2FfY29yciArIGhhcm1fY29yciArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCgojIyMgQ2x1c3RlcmluZwoKCgojIyMjIyBHcmFwaCBiYXNlZCBvbiBDQ0EgSW50Z3JhdGlvbgoKYGBge3IsIGNjYSBjbHVzdGVyaW5nLCBldmFsPVRSVUV9ClREM0FfVERDVF9tZXJnZSA8LSBGaW5kTmVpZ2hib3JzKG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJpbnRlZ3JhdGVkLmNjYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gMToyMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyYXBoLm5hbWUgPSAic25uX2NjYSIpClREM0FfVERDVF9tZXJnZSA8LSBGaW5kQ2x1c3RlcnMob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyYXBoLm5hbWUgPSAic25uX2NjYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvbiA9IGMoLjUsLjgsMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsZ29yaXRobSA9IDQpCmBgYAoKIyMjIyMgR3JhcGggYmFzZWQgb24gclBDQSBJbnRncmF0aW9uCgpgYGB7ciBycGNhIGNsdXN0ZXJpbmcsIGV2YWw9VFJVRX0KVEQzQV9URENUX21lcmdlIDwtIEZpbmROZWlnaGJvcnMob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gImludGVncmF0ZWQucnBjYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gMToyMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyYXBoLm5hbWUgPSAic25uX3JwY2EiKQpURDNBX1REQ1RfbWVyZ2UgPC0gRmluZENsdXN0ZXJzKG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmFwaC5uYW1lID0gInNubl9ycGNhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvbHV0aW9uID0gYyguNSwuOCwxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxnb3JpdGhtID0gNCkKYGBgCgojIyMjIyBHcmFwaCBiYXNlZCBvbiBIYXJtb255IEludGdyYXRpb24KCmBgYHtyLCBoYXJtIGNsdXN0ZXJpbmcsIGV2YWw9VFJVRX0KVEQzQV9URENUX21lcmdlIDwtIEZpbmROZWlnaGJvcnMob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gImhhcm1vbnkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltcyA9IDE6MjAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmFwaC5uYW1lID0gInNubl9oYXJtIikKVEQzQV9URENUX21lcmdlIDwtIEZpbmRDbHVzdGVycyhvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JhcGgubmFtZSA9ICJzbm5faGFybSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvbiA9IGMoLjUsLjgsMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsZ29yaXRobSA9IDQpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00LGVjaG89RkFMU0V9CkRpbVBsb3Qob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLCAKICAgICAgICBncm91cC5ieSA9ICJzbm5fY2NhX3Jlcy4wLjUiLCAKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcGNjYSIsIAogICAgICAgIGxhYmVsID0gVFJVRSkrTm9MZWdlbmQoKSB8IERpbVBsb3Qob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLCAKICAgICAgICBncm91cC5ieSA9ICJzbm5fcnBjYV9yZXMuMC41IiwgCiAgICAgICAgcmVkdWN0aW9uID0gInVtYXBycGNhIiwgCiAgICAgICAgbGFiZWwgPSBUUlVFKStOb0xlZ2VuZCgpIHwgRGltUGxvdChvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic25uX2hhcm1fcmVzLjAuNSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXBoYXJtIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFRSVUUpK05vTGVnZW5kKCkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTQsZWNobz1GQUxTRX0KRGltUGxvdChvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgIGdyb3VwLmJ5ID0gInNubl9jY2FfcmVzLjAuOCIsIAogICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwY2NhIiwgCiAgICAgICAgbGFiZWwgPSBUUlVFKStOb0xlZ2VuZCgpIHwgRGltUGxvdChvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgIGdyb3VwLmJ5ID0gInNubl9ycGNhX3Jlcy4wLjgiLCAKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcHJwY2EiLCAKICAgICAgICBsYWJlbCA9IFRSVUUpK05vTGVnZW5kKCkgfCBEaW1QbG90KG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzbm5faGFybV9yZXMuMC44IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAidW1hcGhhcm0iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gVFJVRSkrTm9MZWdlbmQoKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NCxlY2hvPUZBTFNFfQpEaW1QbG90KG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwgCiAgICAgICAgZ3JvdXAuYnkgPSAic25uX2NjYV9yZXMuMSIsIAogICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwY2NhIiwgCiAgICAgICAgbGFiZWwgPSBUUlVFKStOb0xlZ2VuZCgpIHwgRGltUGxvdChvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgIGdyb3VwLmJ5ID0gInNubl9ycGNhX3Jlcy4xIiwgCiAgICAgICAgcmVkdWN0aW9uID0gInVtYXBycGNhIiwgCiAgICAgICAgbGFiZWwgPSBUUlVFKStOb0xlZ2VuZCgpIHwgRGltUGxvdChvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic25uX2hhcm1fcmVzLjEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwaGFybSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBUUlVFKStOb0xlZ2VuZCgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD01LGVjaG89RkFMU0V9CkRpbVBsb3Qob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLCAKICAgICAgICBncm91cC5ieSA9ICJzbm5fY2NhX3Jlcy4xIiwgCiAgICAgICAgcmVkdWN0aW9uID0gInVtYXBjY2EiLCAKICAgICAgICBsYWJlbCA9IFRSVUUsCiAgICAgICAgc3BsaXQuYnkgPSAib3JpZy5pZGVudCIpKwogICB0aGVtZV9saW5lZHJhdygpKwogICBOb0xlZ2VuZCgpKwogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICJpbmRpYW5yZWQiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAic3RlZWxibHVlNCIsIGhqdXN0ID0gLjUpKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NSxlY2hvPUZBTFNFfQpEaW1QbG90KG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwgCiAgICAgICAgZ3JvdXAuYnkgPSAic25uX3JwY2FfcmVzLjEiLCAKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcHJwY2EiLCAKICAgICAgICBsYWJlbCA9IFRSVUUsCiAgICAgICAgc3BsaXQuYnkgPSAib3JpZy5pZGVudCIpKwogICB0aGVtZV9saW5lZHJhdygpKwogICBOb0xlZ2VuZCgpKwogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICJpbmRpYW5yZWQiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAic3RlZWxibHVlNCIsIGhqdXN0ID0gLjUpKQpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTUsZWNobz1GQUxTRX0KRGltUGxvdChvYmplY3QgPSBURDNBX1REQ1RfbWVyZ2UsIAogICAgICAgIGdyb3VwLmJ5ID0gInNubl9oYXJtX3Jlcy4xIiwgCiAgICAgICAgcmVkdWN0aW9uID0gInVtYXBoYXJtIiwgCiAgICAgICAgbGFiZWwgPSBUUlVFLAogICAgICAgIHNwbGl0LmJ5ID0gIm9yaWcuaWRlbnQiKSsKICAgdGhlbWVfbGluZWRyYXcoKSsKICAgTm9MZWdlbmQoKSsKICB0aGVtZShzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiaW5kaWFucmVkIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gInN0ZWVsYmx1ZTQiLCBoanVzdCA9IC41KSkKYGBgCgpgYGB7cn0KdGFibGUoInNubl9oYXJtX3Jlcy4xIiA9IFREM0FfVERDVF9tZXJnZSRzbm5faGFybV9yZXMuMSwKICAgICAgInNubl9ycGNhX3Jlcy4xIiA9IFREM0FfVERDVF9tZXJnZSRzbm5fcnBjYV9yZXMuMSkKYGBgCgpgYGB7cn0KdGFibGUoVEQzQV9URENUX21lcmdlJG9yaWcuaWRlbnQsIFREM0FfVERDVF9tZXJnZSRzbm5fY2NhX3Jlcy4xKQpgYGAKCmBgYHtyfQp0YWJsZShURDNBX1REQ1RfbWVyZ2Ukb3JpZy5pZGVudCwgVEQzQV9URENUX21lcmdlJHNubl9oYXJtX3Jlcy4xKQpgYGAKCgoKYGBge3J9ClREM0FfVERDVF9tZXJnZSA8LSBKb2luTGF5ZXJzKG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwgYXNzYXkgPSAiUk5BIikKYGBgCgpJZiB3ZSBoYXZlIGV4dHJhLXRpbWUKCmBgYHtyfQpJZGVudHMob2JqZWN0ID0gVEQzQV9URENUX21lcmdlKSA8LSAic25uX2hhcm1fcmVzLjEiCmhhcm1fcmVzMV9tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKAogIG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwKICBvbmx5LnBvcyA9IFRSVUUsCiAgbG9nZmMudGhyZXNob2xkID0gMQopCmBgYAoKYGBge3J9Cmhhcm1fcmVzMV90b3BtYXJrZXJzIDwtIGhhcm1fcmVzMV9tYXJrZXJzICU+JQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQogIGRwbHlyOjpzbGljZV9oZWFkKG4gPSAyMCkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTI0fQpEb0hlYXRtYXAob2JqZWN0ID0gVEQzQV9URENUX21lcmdlLAogICAgICAgICAgZmVhdHVyZXMgPSBoYXJtX3JlczFfdG9wbWFya2VycyRnZW5lLAogICAgICAgICAgZ3JvdXAuYnkgPSAic25uX2hhcm1fcmVzLjEiKQpgYGAKCiMgU2F2ZSB0aGUgU2V1cmF0IG9iamVjdAoKYGBge3Igc2F2ZXJkczIsIHdhcm5pbmcgPSBGQUxTRSwgZm9sZC5vdXRwdXQgPSBGQUxTRSwgZm9sZC5wbG90ID0gRkFMU0V9CiMjIEEgbmFtZSBmb3Igb3VyIFNldXJhdCBvYmplY3QgZmlsZSAocmljaCBuYW1pbmcpCm91dF9uYW1lIDwtIHBhc3RlMCgKICAgICAgICAgIG91dHB1dF9kaXIsICIvIiwgcGFzdGUoCiAgICAgICAgICAgIGMoIlR3ZWx2ZSIsIFNldXJhdDo6UHJvamVjdChURDNBX1REQ1RfbWVyZ2UpLCAiUzUiLCAKICAgICAgICAgICAgICAiSW50ZWdyYXRlZCIsIHBhc3RlKAogICAgICAgICAgICAgICAgZGltKFREM0FfVERDVF9tZXJnZSksIGNvbGxhcHNlID0gJy4nCiAgICAgICAgICAgICAgKQogICAgICAgICAgICApLCBjb2xsYXBzZSA9ICJfIiksCiAgICAgICAgICAgICIuUkRTIikKCiMjIENoZWNrIChJIGxpa2UgdGhpcykKcHJpbnQob3V0X25hbWUpCmBgYAoKCmBgYHtyIHNhdmVyZHMyYiwgd2FybmluZyA9IEZBTFNFLCBmb2xkLm91dHB1dCA9IEZBTFNFLCBmb2xkLnBsb3QgPSBGQUxTRX0KIyMgV3JpdGUgb24gZGlzawpzYXZlUkRTKG9iamVjdCA9IFREM0FfVERDVF9tZXJnZSwgCiAgICAgICAgZmlsZSA9IG91dF9uYW1lKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8QlI+PEJSPjxCUj4KCiMgUnNlc3Npb24KCjxkaXYgY2xhc3MgPSAibm90cnVuIj4KCmBgYHtyfQp1dGlsczo6c2Vzc2lvbkluZm8oKQpgYGAKCjwvZGl2Pg==