logo



1 A bit of theory

1.1 Why ?

Because …

  • … cells in a defined environment (body, organoid, …) are :
    1. possibly of different kinds (cell types)
    2. able to temporaly evolve from a state to another (ex : stem to differentiated)
    3. not (all) synchronous …
  • … and a single cell experiment is an observation of numerous cells of various types in a common context :
    1. Various types : states ?
    2. Common context : lineage ?
    3. Numerous : internal variation in each state ?
logo

1.2 How ?

1.2.1 Main steps

The basic principle can be divided into 3 main steps :

  • Inferring a schematic representation of the transitional behavior of some cells : deciphering a topology from the data
    • Which data ?
      • Cell by feature matrix ? (spoiler : NO)
      • A reduced space ?
        • Which method ?
        • Number of dimensions ?
    • Which topology ?
      • Linear ?
      • Circular ?
      • Poly-cyclic (ie, nested cycles)?
      • Bifurcations ?
      • Multi-furcations ?
      • Simple / complex tree ?
      • A combination of some of all above ?
      • Single one / multiple disconnected ?
      • Oriented or not (rooting) ?
logo


  • Positioning each cell in the defined topology : defining cells pseudotimes
    • Directly / Proxy (clusters) ?
    • Direction : single / multiple roots ? Which one(s) ?

1.2.2 Inferring a topology

  • Most methods rely on identifying a minimal spanning tree (MST), with specific variations (to allow cycles, by example) and tweaks (branch pruning).

  • The key idea in MST is to define the simplest path that connects all cells (vertices) using ponderated edges (their distance in the reduced space)

logo


1.2.3 Defining cells pseudotime

Once a topology has been created, pseudotime of a cell consists in the rank of the cell projection to its closest topology backbone point

logo


1.2.4 Historically

  • First methods (circa 2014)
    • Strong priors :
      • Reduced space (PCA)
      • Clustering results (~ cell states) or inferred cell types
      • Expected topology type
        • Single one
        • Simple : linear (direct or cyclic), bifurcation tree
  • Evolution :
    • Lower priors (just space +/- clusters +/- root)
      • No mandatory input topology type
    • Better-fited types of reduced space (tSNE, uMAP, LLE, Diffusion maps, Poincarre maps, GPLVM, …)

1.2.5 Methods : Welcome to the dynverse !

Inferring any kind of topology is a complex task :

“It is perhaps surprising that of the 59 methods in existence today [at the time of the publication], almost all methods have a unique combination of [these prior + reduced space + topology inferring method] characteristics” (dynverse publication, 2018)

  • Multiple (dozens) of methods are available, either through R or python packages, or independent binaries.

  • Wouter Saelens and Robrecht Cannoodt did a tremendous work setting up the dynverse, a framework that :

    • re-implements/wraps 55 existing methods (dynmethods)
    • harmonizes them in a common way of setting inputs and parameters, getting results (dyno, dynparam)
    • is compatible with any novel method for new developers (dynwrap)
    • contains metrics to compare methods
    • allows a comparison of numerous methods (dyneval)
    • is available as reference map to help select the best method based on user context (dynguidelines)
dynguidelines::guidelines_shiny()
logo


2 Practice

2.1 Prerequisites

2.1.1 This Rmarkdown

Copy this Rmd file (and its dependences) from the common ‘Courses’ dir to your working directory :

## Working directory
user.id <- Sys.getenv('USER')
root.dir <- '/shared/projects/sincellte_2022'
script.dir <- paste(c(root.dir, 'Courses', 'Secondary_analyses',
                      'scripts'), collapse = '/')
work.dir <- paste(c(root.dir, user.id, 'Courses', 'Secondary_analysis',
  'trajectory_analysis'), collapse = '/')
dir.create(path = paste(c(work.dir, 'trajectory_rmd_resources'), collapse = '/'), 
           recursive = TRUE, showWarnings = FALSE)
## Copying the Rmd
file.copy(from = paste0(script.dir, '/Trajectory_TD_deliverable.Rmd'),
          to = work.dir, overwrite = TRUE)
## Copying the dependencies
file.copy(from = list.files(
  path = paste0(script.dir, '/trajectory_rmd_resources/'), full.names = TRUE), 
  to = paste0(work.dir, '/trajectory_rmd_resources/'), overwrite = TRUE)
## Moving to your working directory
setwd(work.dir)
## If you want to visualize your files in Rstudio, you can click the little greyish/white arrow on the right of the path written at the top of your Rstudio Console.

2.1.2 The dataset

  • This practice is inspired by this session from this Galaxy training
  • The dataset you will use :
    • Comes from [Bacon et al, Frontiers in Immunology, 2018].
    • Is available as a scanpy h5ad object (tool-specific format based upon the HDF5 standard)
    • Is hosted at Zenodo #4726927
    • Consists in a drop-seq preparation of thymic cells in differenciation from 7 neonatal mice, selected by mass flow cytometry using 22 immunologic markers corresponding to multiple T-cell types and subtypes (see embedded Excel file)
    • For this practical session, will focus on these subtypes :
      • Double-negative (DN) : Early immature cells
      • Double-positive Module 1 (DP-M1) : Intermediate state 1
      • Double-positive Module 2 (DP-M2) : Intermediate state 2
      • Double-positive Module 3 (DP-M3) : Intermediate state 3
      • Double-positive Module 4 (DP-M4) : Intermediate state 4
      • Double-positive Late (DP-L) : Immature late state
      • Mature T-cells (T-mat)

Download markers_celltypes.xlsx

2.1.3 The environment

The R packages needed for this tutorial are :

Additional packages needed to build this HTML report :

  • CRAN :
    • gifski To generate GIF animations of markers expression
    • knitr To set the Rmarkdown hook to compute CPU time of chunks
    • Hmisc To set the button-hidden TOC

2.2 Preprocessing steps

This is just a description of the preparation I performed on the data, building the the object you will work on. This is not part of the practical session.

Unfold

2.2.1 Setting our variables

## Remote input file
remote.h5ad.file <- 'https://zenodo.org/record/4726927/files/Final_cell_annotated_object.h5ad?download=1'
## Working directory
user.id <- Sys.getenv('USER')
root.dir <- '/shared/projects/sincellte_2022'
data.dir <- paste(c(root.dir, 'Courses', 'Secondary_analyses', 'input', 'trajectory_analysis'), collapse = '/')
work.dir <- paste(c(root.dir, user.id, 'Courses', 'Secondary_analysis', 'trajectory_analysis'), collapse = '/')
## Seurat assay name
assay <- 'RNA'
## Contaminating cell types
contam.types <- c('Macrophages', 'RBC')
## RNG seed (for irlba PCA)
my.seed <- 1337L
## HVGs to keep
nfeatures <- 3000
## 1st reduction method
reduction <- 'pca'
## Number of PCs to produce
pca.dims <- 100
## Number of PCs to retain for 2nd reduction
maxdim <- 30
## Use scaled data instead of unscaled
use.scaled <- TRUE
## Cell type to use as root to orient the trajectory
root.group <- 'DN'

2.2.2 Loading source data

  • First, the dataset was retrieved from Zenodo
dir.create(path = work.dir, recursive = TRUE, showWarnings = FALSE)
local.root.file <- paste0(work.dir, '/Original_scanpy_object.h5')
local.h5ad.file <- paste0(local.root.file, 'ad')
download.file(url = h5ad.file, destfile = local.h5ad.file,
              method = 'wget', quiet = TRUE)
SeuratDisk::Convert(source = local.h5ad.file, dest = 'h5seurat',
                    assay = assay, verbose = FALSE, overwrite = TRUE)
  • The converted object was loaded into R as a SeuratObject using the SeuratDisk::LoadH5Seurat() function. All optional slots (neighbors, graphs, reductions, images), won’t be loaded as we want to reprocess them.
local.h5s.file <- paste0(local.root.file, 'seurat')
sobj <- SeuratDisk::LoadH5Seurat(file = local.h5s.file, assays = assay,
                                 reductions = FALSE, graphs = FALSE,
                                 neighbors = FALSE, images = FALSE)

2.2.3 Prepping the object to our context

  • The downloaded object contains cell-type annotation from the original analysis. From the publication, we learnt that two cell types are not related to T cells, thus considered as contaminants : macrophages and red blood cells (RBC). As their presence in the analysis space can modify the shape of the cells position, thus impact trajectory analysis, we decided to remove them :
sobj <- sobj[, !(sobj$cell_type %in% contam.types)]
  • We then follow the Seurat4 canonical pipeline, applying log-normalization to the count data …
sobj <- Seurat::NormalizeData(object = sobj, assay = assay)
  • … then scaling. As this object is a merge of 7 experiments (labelled as ‘batch’ in the object metadata), we want to regress this effect.
sobj <- Seurat::ScaleData(object = sobj, assay = assay,
                          vars.to.regress = c('batch'))
  • … then identify HVGs. As we focus on subtypes of a single, main cell type (T-cells) in a differentiation context, we anticipate that we may have limited variation of gene expression (at least compared to, by example, multiple cell types of a differentiated organ). Thus, we increase the default number of features to output from 2,000 to 3,000.
sobj <- Seurat::FindVariableFeatures(object = sobj, assay = assay,
                                     verbose = FALSE, nfeatures = nfeatures)
  • … then reducing dimensions (through PCA). We request 100 PCs as output.
sobj <- Seurat::RunPCA(object = sobj, assay = assay, npcs = pca.dims,
                       reduction.name = reduction, seed.use = my.seed,
                       verbose = FALSE)
  • … then building additional reductions for exploration / visualization (t-SNE, uMAP). After manual exploration, we decided to retain 30 PCs as input.
## TSNE
reduction2t <- paste0(reduction, '.', maxdim, '_tsne')
sobj <- Seurat::RunTSNE(object = sobj, dims = 1:maxdim, assay = assay,
                        reduction = reduction, seed.use = my.seed,
                        verbose = FALSE, reduction.name = reduction2t)
## uMAP
reduction2u <- paste0(reduction, '.', maxdim, '_umap')
sobj <- Seurat::RunUMAP(object = sobj, dims = 1:maxdim, assay = assay,
                        reduction = reduction, seed.use = my.seed,
                        verbose = FALSE, reduction.name = reduction2u)
Seurat::DimPlot(object = sobj, reduction = reduction2u, group.by = 'cell_type',
                seed = my.seed)
  • Lastly, we save the processed object (as a RDS file).
saveRDS(object = sobj, file = paste0(work.dir,'/Thymus_neonat_mm_Seurat_ENSEMBL.RDS'),
        compress = 'bzip2')

2.3 Trajectory analysis using TInGa

We will use a tool named TInGa to perform this analysis, due to its convenience to our educational purpose :

  • It’s in full R
  • It’s very fast
  • Its parameters are limited (as compared to other renowned tools like Monocle3)
  • It is compatible with most topologies (see here)
  • It’s integrated in the versatile dynverse (as a plugin)

2.3.1 Setting parameters

## Working directory
user.id <- Sys.getenv('USER')
root.dir <- '/shared/projects/sincellte_2022'
data.dir <- paste(c(root.dir, 'Courses', 'Secondary_analyses', 'input',
                    'trajectory_analysis'), collapse = '/')
work.dir <- paste(c(root.dir, user.id, 'Courses', 'Secondary_analysis',
                    'trajectory_analysis'), collapse = '/')
if(!dir.exists(work.dir)) dir.create(path = work.dir, recursive = TRUE)
## Local input file
## The file created if you performed the preprocessing by yourself
# local.Seurat.file <- paste0(work.dir, '/Thymus_neonat_mm_Seurat_ENSEMBL.RDS')
## The preprocessed file I prepared for you
local.Seurat.file <- paste0(data.dir, '/Thymus_neonat_mm_Seurat_ENSEMBL.RDS')
## Seurat assay name
assay <- 'RNA'
## RNG seed (for irlba PCA)
my.seed <- 1337L
## 1st reduction method
reduction <- 'pca'
## 2nd reduction method
reduction2 <- 'pca.30_umap'
## Use scaled data instead of unscaled
use.scaled <- TRUE
## Cell type to use as root to orient the trajectory
root.group <- 'DN'
## Marker genes to plot
trans.markers <- c('Il2ra', 'Npm1', 'Nusap1', 'Ube2c',
                   'Pclaf','Cd8b1', 'Itm2a', 'Cd52', 'H2-D1')

2.3.2 Loading source data

We will simply load the pre-processed Seurat object, contained in a RDS archive, as input :

sobj <- readRDS(file = local.Seurat.file)

Time for this code chunk to run : 1.305 s

2.3.3 Performing trajectory analysis

2.3.3.1 The reduced space

First, to impregnate us with the cells topology on which TInGa will work, we can display our 2D dimension reduction (uMAP or t-SNE) :

## With batches
db <- Seurat::DimPlot(object = sobj, reduction = reduction2, group.by = 'batch',
                      pt.size = 2) + Seurat::DarkTheme()
## With cell types
dct <- Seurat::DimPlot(object = sobj, reduction = reduction2, group.by = 'cell_type',
                       pt.size = 2) + Seurat::DarkTheme()
## With sex
ds <- Seurat::DimPlot(object = sobj, reduction = reduction2, group.by = 'sex',
                      pt.size = 2) + Seurat::DarkTheme()
print(patchwork::wrap_plots(list(db, dct, ds)))

2.3.3.2 Extracting data

Using TInGa first requires to build a specific object from the count and normalized matrices, that we will extract from our Seurat object :

## Matrices are required to be formatted as [cells] x [features], thus a transposition is required
## Normalized matrix
my.exp <- t(as.matrix(if(use.scaled) sobj@assays[[assay]]@scale.data
                      else sobj@assays[[assay]]@data)) 
### Ordering to keep synch in the [use.scaled] context
my.exp <- my.exp[, order(colnames(my.exp))]
## Count matrix
my.counts <- t(as.matrix(if(use.scaled) sobj@assays[[assay]]@counts[rownames(sobj@assays[[assay]]@counts)%in% colnames(my.exp),]
                         else sobj@assays[[assay]]@counts))
### Ordering to keep synch in the [use.scaled] context
my.counts <- my.counts[, order(colnames(my.counts))]

2.3.3.3 Building a dynwrap object

We can now build our object (a dynwrap object, which actually consists in a list with mandatory entries) :

my.data <- dynwrap::wrap_expression(
  id = sobj@project.name,
  expression = my.exp,
  counts = my.counts
)
is(my.data)
## [1] "dynwrap::with_expression"

Time for this code chunk to run : 2.55 s

2.3.3.4 Import prior

We can import our reduced space (t-SNE or uMAP) as a prior, which will be used by TInGa to build the trajectory :

my.data <- dynwrap::add_prior_information(
  dataset = my.data,
  dimred = sobj@reductions[[reduction2]]@cell.embeddings
)

2.3.3.5 Running TInGa

Now, we can run TInGa !

## Performing TInGa analysis
set.seed(my.seed)
my.traj <- dynwrap::infer_trajectory(dataset = my.data, method = TInGa::gng_param2(),
                                     seed = my.seed, give_priors = 'dimred',
                                     parameters = list(max_nodes = 8, lambda = 200))
## Adding pseudotime
my.traj <- dynwrap::add_pseudotime(trajectory = my.traj)
## Warning in calculate_pseudotime(trajectory): Trajectory is not rooted. Add a
## root to the trajectory using dynwrap::add_root(). This will result in an error
## in future releases.
## root cell or milestone not provided, trying first outgoing milestone_id
## Using '1' as root
## Plotting with pseudotime
dynplot::plot_dimred(trajectory = my.traj, label_milestones = TRUE,
                     color_cells = 'pseudotime') + Seurat::DarkTheme()
## Registered S3 method overwritten by 'cli':
##   method     from         
##   print.boxx spatstat.geom

Time for this code chunk to run : 18.4 s

2.3.3.6 Adding cell types

We can import our cell types (as a metadata for later plots) in the trajectory object :

## Plotting cell types as grouping
my.traj <- dynwrap::add_grouping(dataset = my.traj,
                                 grouping = as.character(sobj$cell_type))
## Plotting with cell types
dynplot::plot_dimred(trajectory = my.traj, label_milestones = TRUE,
                     color_cells = "grouping") + Seurat::DarkTheme()

2.3.3.7 Adding a root

  • As we know our root cell type (DN), we can give a direction to the trajectory. However, TInGa requires a unique root cell, not a root cluster… Thus, we will first identify a putative root cell by computing the centroid of the DN cells in the reduced space :
if (!is.null(root.group)) {
  ## Identifying cells of the root group
  cells_in_root.group <- which(my.traj$grouping == root.group)
  ## Computing the root group medoid in the reduced space
  root_group.medoid <- apply(my.traj$dimred[cells_in_root.group,], 2, median)
  ## Computing the root group cells distance to root group medoid
  cells.dist_to_medoid <- vapply(cells_in_root.group, function(x) { norm(as.matrix(my.traj$dimred[x,] - root_group.medoid), 'F')}, .1)
  ## Identifying the root cell
  root.cell.id <- my.traj$cell_info$cell_id[cells_in_root.group[which.min(cells.dist_to_medoid)]]
  message(paste0(root.group, ' root cell : ', root.cell.id))
  ## Plotting cell types
  dct <- Seurat::DimPlot(object = sobj, reduction = reduction2, 
                         group.by = 'cell_type', pt.size = 2) + Seurat::DarkTheme()
  ## Plotting the root cell
  sobj$root_cell <- FALSE
  sobj$root_cell[colnames(sobj) == root.cell.id] <- TRUE
  drc <- Seurat::DimPlot(object = sobj, reduction = reduction2, 
                         group.by = 'root_cell', pt.size = c(rep(2, ncol(sobj)-1), 5),
                         order = TRUE) + Seurat::DarkTheme()
  print(patchwork::wrap_plots(list(dct, drc)))
}
## DN root cell : AGGGCGTAGTTA-0

Time for this code chunk to run : 2.057 s

  • Rooted plots
## Adding root cell
my.traj <- dynwrap::add_root(trajectory = my.traj, root_cell_id = root.cell.id)
## Plotting with cell types
dct <- dynplot::plot_dimred(trajectory = my.traj, label_milestones = TRUE, 
                            color_cells = "grouping") + Seurat::DarkTheme()
## Plotting with pseudotime
dpt <- dynplot::plot_dimred(trajectory = my.traj, label_milestones = TRUE, 
                            color_cells = "pseudotime") + Seurat::DarkTheme()
print(patchwork::wrap_plots(list(dct, dpt)))

2.3.3.8 Plotting marker genes

  • First, we’ll replace EnsemblIDs with MGI Symbols (easier to handle)
## Injecting symbols
symbvec <- as.character(sobj@assays$RNA@meta.features$Symbol)
names(symbvec) <- sobj@assays$RNA@meta.features$ID
colnames(my.data$counts) <- colnames(my.data$expression) <- unname(symbvec[colnames(my.data$expression)])
  • Plotting markers :
    • From milestone 1 to 3 :
## Milestones 1 to 3 ...
tmplots <- sapply(trans.markers[1:4], function(x) {
  dynplot::plot_dimred(trajectory = my.traj, label_milestones = TRUE, 
                       color_cells = "feature", feature_oi = x, 
                       expression_source = my.data$expression) + Seurat::DarkTheme()
  }, simplify = FALSE)
print(patchwork::wrap_plots(tmplots))

Time for this code chunk to run : 39.59 s

  • From milestone 1 to 2 :
## Milestones 1 to 2 ...
tmplots <- sapply(trans.markers[c(1:2,5:9)], function(x) {
  dynplot::plot_dimred(trajectory = my.traj, label_milestones = TRUE, 
                       color_cells = "feature", feature_oi = x, 
                       expression_source = my.data$expression) + Seurat::DarkTheme()
  }, simplify = FALSE)
print(patchwork::wrap_plots(tmplots))

Time for this code chunk to run : 1.126 s

2.3.3.9 Animated !

  • From milestone 1 to 3 :
## Milestones 1 to 3 ...
for (x in trans.markers[1:4]) print(
  dynplot::plot_dimred(trajectory = my.traj, label_milestones = TRUE, 
                       color_cells = "feature", feature_oi = x, 
                       expression_source = my.data$expression) + Seurat::DarkTheme())
anim2a


  • From milestone 1 to 2 :
## Milestones 1 to 2 ...
for (x in trans.markers[c(1:2,5:9)]) print(
  dynplot::plot_dimred(trajectory = my.traj, label_milestones = TRUE, 
                       color_cells = "feature", feature_oi = x, 
                       expression_source = my.data$expression) + Seurat::DarkTheme())
anim2b


3 Trajectory analysis using Slingshot

  • Slingshot is a way more commonly used trajectory inference tool than TInGa (that I chose for educational purpose, among other things).

  • Like TInGa, it’s a full-R, easy to use tool that is compatible with most trajectory topologies.

  • However, it does have a key difference : compared to TInGa, it additionally requires a cell clustering results. This may be either an advantage (if you are confident in the capacity of you clustering results to reflect the biology, espacially the cells lineage) or a con (if you are not).

  • Running Slingshot

## Run slingshot with root cluster
sling.res <- slingshot::slingshot(data = sobj@reductions$pca.30_umap@cell.embeddings,
                                  clusterLabels = sobj$cell_type, start.clus = 'DN')
## Plot results (lineage)
plot(sobj@reductions$pca.30_umap@cell.embeddings, 
     col = RColorBrewer::brewer.pal(9,"Set1")[sobj$cell_type], 
     asp = 1, pch = 16)
lines(slingshot::as.SlingshotDataSet(sling.res), type = 'lineages')

## Plot results (fitted curve)
plot(sobj@reductions$pca.30_umap@cell.embeddings, 
     col = RColorBrewer::brewer.pal(9,"Set1")[sobj$cell_type], 
     asp = 1, pch = 16)
lines(slingshot::as.SlingshotDataSet(sling.res))

Time for this code chunk to run : 14.77 s

Slingshot identified 3 lineages, much in concordance with TInGa results !

  • Getting cells pseudotimes (per lineage)
## Getting pseudotimes
my.pseudo <- slingshot::slingPseudotime(x = sling.res)
pal <- viridis::viridis(100, end = 0.95)

oripar <- par(no.readonly = TRUE)
par(mfrow = c(1,3))
for (lin.x in 1:ncol(my.pseudo)) {
  colors <- pal[cut(my.pseudo[,lin.x], breaks = 100)]
  plot(sobj@reductions$pca.30_umap@cell.embeddings, col = colors, 
       pch = 16, cex = 0.5, main = colnames(my.pseudo)[lin.x])
  lines(slingshot::as.SlingshotDataSet(sling.res))
}

par(oripar)

Time for this code chunk to run : 0.426 s

NOTE : Slingshot is also available through the dynverse !

4 Limits

  • No one method to rule them all (some are or more efficient for some specific types of topology, terrible with others)
  • Dimension reduction : in the name of interpretation/visualization (and CPU time), but is it relevant ?
    • Cooley et al. deposited a paper in 2019 on biorXiv (slight warning : thus, non-peer-reviewed yet), updated very recently, stating that trajectory inference based on reduced spaces fail in every case !
    • The only working case according to them : using PCA with … almost all dimensions ?!
      • Not a reduced space anymore ?
    • Why : all tested reduction methods sacrifice too much information to be able to represent the true topology right without breakage, just so that we can visualize it with our poor human brains
    • So, how to deal with it ?
      • As a Maths teacher told me, “Mathematics are an art of reasoning right on wrong images”. However, in our case we would call it observer bias!
      • A solution could be to generate pseudotime values in a high-dimension space (like the recommended "almost-full’ PCA), without visualization.
        • Drawback : how to evaluate, how to interpret such results ? The question will remain open for today.
logo


6 R session

## R version 4.1.2 (2021-11-01)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.3 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       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] dynutils_1.0.9 dynwrap_1.2.2  purrr_0.3.4    tidyr_1.1.4    dplyr_1.0.7   
## 
## loaded via a namespace (and not attached):
##   [1] utf8_1.2.2                  reticulate_1.22            
##   [3] tidyselect_1.1.1            htmlwidgets_1.5.4          
##   [5] grid_4.1.2                  ranger_0.13.1              
##   [7] Rtsne_0.15                  munsell_0.5.0              
##   [9] codetools_0.2-18            ica_1.0-2                  
##  [11] future_1.23.0               miniUI_0.1.1.1             
##  [13] gng_0.1.0                   colorspace_2.0-2           
##  [15] Biobase_2.54.0              highr_0.9                  
##  [17] knitr_1.37                  rstudioapi_0.13            
##  [19] SingleCellExperiment_1.16.0 stats4_4.1.2               
##  [21] Seurat_4.0.6                ROCR_1.0-11                
##  [23] TInGa_0.0.0.9000            tensor_1.5                 
##  [25] listenv_0.8.0               MatrixGenerics_1.6.0       
##  [27] labeling_0.4.2              dynplot_1.1.2              
##  [29] GenomeInfoDbData_1.2.7      polyclip_1.10-0            
##  [31] farver_2.1.0                rprojroot_2.0.2            
##  [33] parallelly_1.30.0           vctrs_0.3.8                
##  [35] generics_0.1.1              xfun_0.29                  
##  [37] GenomeInfoDb_1.30.0         R6_2.5.1                   
##  [39] GA_3.2.2                    graphlayouts_0.8.0         
##  [41] DelayedArray_0.20.0         bitops_1.0-7               
##  [43] spatstat.utils_2.3-0        assertthat_0.2.1           
##  [45] promises_1.2.0.1            scales_1.1.1               
##  [47] ggraph_2.0.5                nnet_7.3-16                
##  [49] lmds_0.1.0                  gtable_0.3.0               
##  [51] dynfeature_1.0.0            babelwhale_1.0.3           
##  [53] globals_0.14.0              processx_3.5.2             
##  [55] goftest_1.2-3               tidygraph_1.2.0            
##  [57] rlang_0.4.12                randomForestSRC_3.0.0      
##  [59] splines_4.1.2               lazyeval_0.2.2             
##  [61] princurve_2.1.6             spatstat.geom_2.3-1        
##  [63] checkmate_2.0.0             yaml_2.2.1                 
##  [65] reshape2_1.4.4              abind_1.4-5                
##  [67] backports_1.4.1             httpuv_1.6.5               
##  [69] Hmisc_4.6-0                 DiagrammeR_1.0.6.1         
##  [71] tools_4.1.2                 ggplot2_3.3.5              
##  [73] ellipsis_0.3.2              spatstat.core_2.3-2        
##  [75] jquerylib_0.1.4             RColorBrewer_1.1-2         
##  [77] BiocGenerics_0.40.0         ggridges_0.5.3             
##  [79] Rcpp_1.0.7                  plyr_1.8.6                 
##  [81] sparseMatrixStats_1.6.0     zlibbioc_1.40.0            
##  [83] base64enc_0.1-3             visNetwork_2.1.0           
##  [85] RCurl_1.98-1.5              TrajectoryUtils_1.0.0      
##  [87] ps_1.6.0                    rpart_4.1-15               
##  [89] deldir_1.0-6                pbapply_1.5-0              
##  [91] viridis_0.6.2               cowplot_1.1.1              
##  [93] S4Vectors_0.32.3            dynparam_1.0.2             
##  [95] zoo_1.8-9                   SummarizedExperiment_1.24.0
##  [97] SeuratObject_4.0.4          ggrepel_0.9.1              
##  [99] cluster_2.1.2               magrittr_2.0.1             
## [101] data.table_1.14.2           scattermore_0.7            
## [103] carrier_0.1.0               lmtest_0.9-39              
## [105] RANN_2.6.1                  fitdistrplus_1.1-6         
## [107] matrixStats_0.61.0          hms_1.1.1                  
## [109] patchwork_1.1.1             mime_0.12                  
## [111] evaluate_0.14               xtable_1.8-4               
## [113] jpeg_0.1-9                  IRanges_2.28.0             
## [115] gridExtra_2.3               testthat_3.1.1             
## [117] compiler_4.1.2              tibble_3.1.6               
## [119] KernSmooth_2.23-20          crayon_1.4.2               
## [121] htmltools_0.5.2             proxyC_0.2.4               
## [123] mgcv_1.8-38                 later_1.3.0                
## [125] tzdb_0.2.0                  Formula_1.2-4              
## [127] RcppParallel_5.1.5          DBI_1.1.2                  
## [129] tweenr_1.0.2                slingshot_2.0.0            
## [131] MASS_7.3-54                 data.tree_1.0.0            
## [133] Matrix_1.4-0                readr_2.1.1                
## [135] cli_3.1.0                   parallel_4.1.2             
## [137] igraph_1.2.11               GenomicRanges_1.46.1       
## [139] pkgconfig_2.0.3             dyneval_0.9.9              
## [141] foreign_0.8-81              plotly_4.10.0              
## [143] spatstat.sparse_2.1-0       foreach_1.5.1              
## [145] bslib_0.3.1                 XVector_0.34.0             
## [147] stringr_1.4.0               digest_0.6.29              
## [149] dyndimred_1.0.4             sctransform_0.3.2          
## [151] RcppAnnoy_0.0.19            spatstat.data_2.1-2        
## [153] rmarkdown_2.11              leiden_0.3.9               
## [155] htmlTable_2.4.0             uwot_0.1.11                
## [157] DelayedMatrixStats_1.16.0   shiny_1.7.1                
## [159] lifecycle_1.0.1             nlme_3.1-153               
## [161] jsonlite_1.7.2              desc_1.4.0                 
## [163] viridisLite_0.4.0           fansi_1.0.0                
## [165] pillar_1.6.4                lattice_0.20-45            
## [167] fastmap_1.1.0               httr_1.4.2                 
## [169] survival_3.2-13             glue_1.6.0                 
## [171] remotes_2.4.2               png_0.1-7                  
## [173] iterators_1.0.13            ggforce_0.3.3              
## [175] stringi_1.7.6               sass_0.4.0                 
## [177] latticeExtra_0.6-29         irlba_2.3.5                
## [179] future.apply_1.8.1
LS0tCnRpdGxlOiAiU2luQ2VsbFRFIDIwMjI8QlI+U2Vjb25kYXJ5IEFuYWx5c2VzIDogVHJhamVjdG9yeSBJbmZlcmVuY2UiCmF1dGhvcjogIkJhc3RpZW4gSk9CPEJSPmJhc3RpZW4uam9iQGd1c3RhdmVyb3Vzc3kuZnIiCmRhdGU6ICIyMDIyLTAxLTExIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICBiYWNrZ3JvdW5kOiBibGFjawogICAgZmlnX2hlaWdodDogMTAKICAgIGZpZ193aWR0aDogMTUKICAgIGhpZ2hsaWdodDogZXNwcmVzc28gICMjIFRoZW1lIGZvciB0aGUgY29kZSBjaHVua3MKICAgIG51bWJlcl9zZWN0aW9uczogeWVzICAjIyBBZGRzIG51bWJlciB0byBoZWFkZXJzIChzZWN0aW9ucykKICAgIHRoZW1lOiBmbGF0bHkgICMjIENTUyB0aGVtZSBmb3IgdGhlIEhUTUwgcGFnZQogICAgdG9jOiB5ZXMgICMjIEFkZHMgYSB0YWJsZSBvZiBjb250ZW50CiAgICB0b2NfZmxvYXQ6ICAjIyBUT0Mgb3B0aW9ucwogICAgICBjb2xsYXBzZWQ6IHllcyAgIyMgQnkgZGVmYXVsdCwgdGhlIFRPQyBpcyBmb2xkZWQKICAgICAgc21vb3RoX3Njcm9sbDogeWVzICMjIFNtb290aCBzY3JvbGwgb2YgdGhlIEhUTUwgcGFnZQogICAgc2VsZl9jb250YWluZWQ6IHllcyAjIyBJbmNsdWRlcyBhbGwgcGxvdHMvaW1hZ2VzIHdpdGhpbiB0aGUgSFRNTAogICAgY29kZV9kb3dubG9hZDogeWVzICMjIEFkZHMgYSBidXR0b24gdG8gZG93bmxvYWQgdGhlIFJtZAphbHdheXNfYWxsb3dfaHRtbDogeWVzICMjIEFsbG93IHBsYWluIEhUTUwgY29kZSBpbiB0aGUgUm1kCmhlYWRlci1pbmNsdWRlczoKICAgIyAtIFx1c2VwYWNrYWdle2dpZnNraX0KLS0tCgo8IS0tIEF1dG9tYXRpY2FsbHkgY29tcHV0ZXMgYW5kIHByaW50cyBpbiB0aGUgb3V0cHV0IHRoZSBydW5uaW5nIHRpbWUgZm9yIGFueSBjb2RlIGNodW5rIC0tPgpgYGB7ciB0aW1laG9vaywgZWNobyA9IEZBTFNFfQprbml0cjo6a25pdF9ob29rcyRzZXQodGltZV9pdCA9IGxvY2FsKHsKICBub3cgPC0gTlVMTAogIGZ1bmN0aW9uKGJlZm9yZSwgb3B0aW9ucykgewogICAgaWYgKGJlZm9yZSkgewogICAgICAjIHJlY29yZCB0aGUgY3VycmVudCB0aW1lIGJlZm9yZSBlYWNoIGNodW5rCiAgICAgIG5vdyA8PC0gU3lzLnRpbWUoKQogICAgfSBlbHNlIHsKICAgICAgIyBjYWxjdWxhdGUgdGhlIHRpbWUgZGlmZmVyZW5jZSBhZnRlciBhIGNodW5rCiAgICAgIHJlcyA8LSBkaWZmdGltZShTeXMudGltZSgpLCBub3cpCiAgICAgICMgcmV0dXJuIGEgY2hhcmFjdGVyIHN0cmluZyB0byBzaG93IHRoZSB0aW1lCiAgICAgIHBhc3RlMCgnVGltZSBmb3IgdGhpcyBjb2RlIGNodW5rIHRvIHJ1biA6ICcsIHJvdW5kKHggPSByZXMsIGRpZ2l0cyA9IDMpLCAnIHMnKQogICAgfQogIH0KfSkpCiMga25pdHI6Om9wdHNfY2h1bmskc2V0KHRpbWVfaXQgPSBGQUxTRSkKYGBgCgo8IS0tIEFsbG93cyB0byBoaWRlIHRoZSBUT0MgYnkgZGVmYXVsdCwgZGlzcGxheSBpdCB3aXRoIGEgYnV0dG9uLCBtb3ZlIGl0IHRvIHRoZSByaWdodCBvciBsZWZ0IG9mIHRoZSBwYWdlIC0tPgpgciBIbWlzYzo6aGlkaW5nVE9DKGJ1dHRvbkxhYmVsID0gJ1Nob3cgVE9DJywgaGlkZGVuID0gVFJVRSwgdG9jU2lkZSA9ICdsZWZ0JywgYnV0dG9uU2lkZT0nbGVmdCcsIHBvc0NvbGxhcHNlID0gJ21hcmdpbicsIGxldmVscyA9IDMpYAoKYGBge3IgdXNlcmlkLCBlY2hvID0gRkFMU0V9CnVzZXIuaWQgPC0gU3lzLmdldGVudignVVNFUicpCmBgYAoKPGNlbnRlcj4hW2xvZ29dKC9zaGFyZWQvcHJvamVjdHMvc2luY2VsbHRlXzIwMjIvYHIgdXNlci5pZGAvQ291cnNlcy9TZWNvbmRhcnlfYW5hbHlzaXMvdHJhamVjdG9yeV9hbmFseXNpcy90cmFqZWN0b3J5X3JtZF9yZXNvdXJjZXMvYmFuaWVyZV90aXRyZS5wbmcpPC9jZW50ZXI+Cjxicj48YnI+CgojIEEgYml0IG9mIHRoZW9yeQoKIyMgV2h5ID8KCioqKkJlY2F1c2UgLi4uKioqCgoqIC4uLiBjZWxscyBpbiBhIGRlZmluZWQgZW52aXJvbm1lbnQgKGJvZHksIG9yZ2Fub2lkLCAuLi4pIGFyZSA6CiAgMS4gcG9zc2libHkgb2YgZGlmZmVyZW50IGtpbmRzIChjZWxsIHR5cGVzKQogIDIuIGFibGUgdG8gdGVtcG9yYWx5IGV2b2x2ZSBmcm9tIGEgc3RhdGUgdG8gYW5vdGhlciAoZXggOiBzdGVtIHRvIGRpZmZlcmVudGlhdGVkKQogIDMuIG5vdCAoYWxsKSBzeW5jaHJvbm91cyAuLi4KICAKKiAuLi4gYW5kIGEgc2luZ2xlIGNlbGwgZXhwZXJpbWVudCBpcyBhbiBvYnNlcnZhdGlvbiBvZiAqbnVtZXJvdXMqIGNlbGxzIG9mICp2YXJpb3VzIHR5cGVzKiBpbiBhICpjb21tb24gY29udGV4dCogOgogIDEuIFZhcmlvdXMgdHlwZXMgOiBzdGF0ZXMgPwogIDIuIENvbW1vbiBjb250ZXh0IDogbGluZWFnZSA/CiAgMy4gTnVtZXJvdXMgOiBpbnRlcm5hbCB2YXJpYXRpb24gaW4gZWFjaCBzdGF0ZSA/Cgo8Y2VudGVyPiFbbG9nb10oL3NoYXJlZC9wcm9qZWN0cy9zaW5jZWxsdGVfMjAyMi9gciB1c2VyLmlkYC9Db3Vyc2VzL1NlY29uZGFyeV9hbmFseXNpcy90cmFqZWN0b3J5X2FuYWx5c2lzL3RyYWplY3Rvcnlfcm1kX3Jlc291cmNlcy9pdHNhbWF0Y2guanBlZyk8L2NlbnRlcj4KCiMjIEhvdyA/CgojIyMgTWFpbiBzdGVwcwoKVGhlIGJhc2ljIHByaW5jaXBsZSBjYW4gYmUgZGl2aWRlZCBpbnRvIDMgbWFpbiBzdGVwcyA6CgoqIEluZmVycmluZyBhIHNjaGVtYXRpYyByZXByZXNlbnRhdGlvbiBvZiB0aGUgdHJhbnNpdGlvbmFsIGJlaGF2aW9yIG9mIHNvbWUgY2VsbHMgOiBkZWNpcGhlcmluZyBhIHRvcG9sb2d5IGZyb20gdGhlIGRhdGEKICAqIFdoaWNoIGRhdGEgPwogICAgKiBDZWxsIGJ5IGZlYXR1cmUgbWF0cml4ID8gKHNwb2lsZXIgOiAqKipOTyoqKikKICAgICogQSByZWR1Y2VkIHNwYWNlID8KICAgICAgKiBXaGljaCBtZXRob2QgPwogICAgICAqIE51bWJlciBvZiBkaW1lbnNpb25zID8KICAqIFdoaWNoIHRvcG9sb2d5ID8KICAgICogTGluZWFyID8KICAgICogQ2lyY3VsYXIgPwogICAgKiBQb2x5LWN5Y2xpYyAoaWUsIG5lc3RlZCBjeWNsZXMpPwogICAgKiBCaWZ1cmNhdGlvbnMgPwogICAgKiBNdWx0aS1mdXJjYXRpb25zID8KICAgICogU2ltcGxlIC8gY29tcGxleCB0cmVlID8KICAgICogQSBjb21iaW5hdGlvbiBvZiBzb21lIG9mIGFsbCBhYm92ZSA/CiAgICAqIFNpbmdsZSBvbmUgLyBtdWx0aXBsZSBkaXNjb25uZWN0ZWQgPwogICAgKiBPcmllbnRlZCBvciBub3QgKHJvb3RpbmcpID8KCjxjZW50ZXI+IVtsb2dvXSgvc2hhcmVkL3Byb2plY3RzL3NpbmNlbGx0ZV8yMDIyL2ByIHVzZXIuaWRgL0NvdXJzZXMvU2Vjb25kYXJ5X2FuYWx5c2lzL3RyYWplY3RvcnlfYW5hbHlzaXMvdHJhamVjdG9yeV9ybWRfcmVzb3VyY2VzL3RvcG9sb2dpZXMucG5nKTwvY2VudGVyPgo8YnI+CgoqIFBvc2l0aW9uaW5nIGVhY2ggY2VsbCBpbiB0aGUgZGVmaW5lZCB0b3BvbG9neSA6IGRlZmluaW5nIGNlbGxzIHBzZXVkb3RpbWVzCiAgKiBEaXJlY3RseSAvIFByb3h5IChjbHVzdGVycykgPwogICogRGlyZWN0aW9uIDogc2luZ2xlIC8gbXVsdGlwbGUgcm9vdHMgPyBXaGljaCBvbmUocykgPwoKIyMjIEluZmVycmluZyBhIHRvcG9sb2d5CgoqIE1vc3QgbWV0aG9kcyByZWx5IG9uIGlkZW50aWZ5aW5nIGEgbWluaW1hbCBzcGFubmluZyB0cmVlIChbTVNUXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9NaW5pbXVtX3NwYW5uaW5nX3RyZWUpKSwgd2l0aCBzcGVjaWZpYyB2YXJpYXRpb25zICh0byBhbGxvdyBjeWNsZXMsIGJ5IGV4YW1wbGUpIGFuZCB0d2Vha3MgKGJyYW5jaCBwcnVuaW5nKS4KCiogVGhlIGtleSBpZGVhIGluIE1TVCBpcyB0byBkZWZpbmUgdGhlIHNpbXBsZXN0IHBhdGggdGhhdCBjb25uZWN0cyBhbGwgY2VsbHMgKHZlcnRpY2VzKSB1c2luZyBwb25kZXJhdGVkIGVkZ2VzICh0aGVpciBkaXN0YW5jZSBpbiB0aGUgcmVkdWNlZCBzcGFjZSkKCjxjZW50ZXI+IVtsb2dvXSgvc2hhcmVkL3Byb2plY3RzL3NpbmNlbGx0ZV8yMDIyL2ByIHVzZXIuaWRgL0NvdXJzZXMvU2Vjb25kYXJ5X2FuYWx5c2lzL3RyYWplY3RvcnlfYW5hbHlzaXMvdHJhamVjdG9yeV9ybWRfcmVzb3VyY2VzL21zdC5qcGcpPC9jZW50ZXI+Cjxicj4KCiMjIyBEZWZpbmluZyBjZWxscyBwc2V1ZG90aW1lCgpPbmNlIGEgdG9wb2xvZ3kgaGFzIGJlZW4gY3JlYXRlZCwgcHNldWRvdGltZSBvZiBhIGNlbGwgY29uc2lzdHMgaW4gdGhlIHJhbmsgb2YgdGhlIGNlbGwgcHJvamVjdGlvbiB0byBpdHMgY2xvc2VzdCB0b3BvbG9neSBiYWNrYm9uZSBwb2ludAoKPGNlbnRlcj4hW2xvZ29dKC9zaGFyZWQvcHJvamVjdHMvc2luY2VsbHRlXzIwMjIvYHIgdXNlci5pZGAvQ291cnNlcy9TZWNvbmRhcnlfYW5hbHlzaXMvdHJhamVjdG9yeV9hbmFseXNpcy90cmFqZWN0b3J5X3JtZF9yZXNvdXJjZXMvcHNldWRvdGltZS5wbmcpPC9jZW50ZXI+Cjxicj4KCiMjIyBIaXN0b3JpY2FsbHkKCiogRmlyc3QgbWV0aG9kcyAoY2lyY2EgMjAxNCkKICAqIFN0cm9uZyBwcmlvcnMgOgogICAgKiBSZWR1Y2VkIHNwYWNlIChQQ0EpCiAgICAqIENsdXN0ZXJpbmcgcmVzdWx0cyAofiBjZWxsIHN0YXRlcykgb3IgaW5mZXJyZWQgY2VsbCB0eXBlcwogICAgKiBFeHBlY3RlZCB0b3BvbG9neSB0eXBlCiAgICAgICogU2luZ2xlIG9uZQogICAgICAqIFNpbXBsZSA6IGxpbmVhciAoZGlyZWN0IG9yIGN5Y2xpYyksIGJpZnVyY2F0aW9uIHRyZWUKKiBFdm9sdXRpb24gOgogICogTG93ZXIgcHJpb3JzIChqdXN0IHNwYWNlICsvLSBjbHVzdGVycyArLy0gcm9vdCkKICAgICogTm8gbWFuZGF0b3J5IGlucHV0IHRvcG9sb2d5IHR5cGUKICAqIEJldHRlci1maXRlZCB0eXBlcyBvZiByZWR1Y2VkIHNwYWNlICh0U05FLCB1TUFQLCBMTEUsIFtEaWZmdXNpb24gbWFwc10oaHR0cHM6Ly93d3cuc2NpZW5jZWRpcmVjdC5jb20vc2NpZW5jZS9hcnRpY2xlL3BpaS9TMTA2MzUyMDMwNjAwMDU0NiksIFtQb2luY2FycmUgbWFwc10oaHR0cHM6Ly9wdWJtZWQubmNiaS5ubG0ubmloLmdvdi8zMjUyODA3NS8pLCBbR1BMVk1dKGh0dHBzOi8vcHlyby5haS9leGFtcGxlcy9ncGx2bS5odG1sKSwgLi4uKQoKIyMjIE1ldGhvZHMgOiBXZWxjb21lIHRvIHRoZSBkeW52ZXJzZSAhCgpJbmZlcnJpbmcgYW55IGtpbmQgb2YgdG9wb2xvZ3kgaXMgYSBjb21wbGV4IHRhc2sgOgoKPiJJdCBpcyBwZXJoYXBzIHN1cnByaXNpbmcgdGhhdCBvZiB0aGUgNTkgbWV0aG9kcyBpbiBleGlzdGVuY2UgdG9kYXkgW2F0IHRoZSB0aW1lIG9mIHRoZSBwdWJsaWNhdGlvbl0sIGFsbW9zdCBhbGwgbWV0aG9kcyBoYXZlIGEgdW5pcXVlIGNvbWJpbmF0aW9uIG9mIFt0aGVzZSBwcmlvciArIHJlZHVjZWQgc3BhY2UgKyB0b3BvbG9neSBpbmZlcnJpbmcgbWV0aG9kXSBjaGFyYWN0ZXJpc3RpY3MiIChbZHludmVyc2UgcHVibGljYXRpb25dKGh0dHBzOi8vcHVibWVkLm5jYmkubmxtLm5paC5nb3YvMzA5MzY1NTkvKSwgMjAxOCkKCiogTXVsdGlwbGUgKGRvemVucykgb2YgbWV0aG9kcyBhcmUgYXZhaWxhYmxlLCBlaXRoZXIgdGhyb3VnaCBSIG9yIHB5dGhvbiBwYWNrYWdlcywgb3IgaW5kZXBlbmRlbnQgYmluYXJpZXMuCgoqIFdvdXRlciBTYWVsZW5zIGFuZCBSb2JyZWNodCBDYW5ub29kdCBkaWQgYSB0cmVtZW5kb3VzIHdvcmsgc2V0dGluZyB1cCB0aGUgKioqZHludmVyc2UqKiosIGEgZnJhbWV3b3JrIHRoYXQgOgogICogcmUtaW1wbGVtZW50cy93cmFwcyA1NSBleGlzdGluZyBtZXRob2RzICooZHlubWV0aG9kcykqCiAgKiBoYXJtb25pemVzIHRoZW0gaW4gYSBjb21tb24gd2F5IG9mIHNldHRpbmcgaW5wdXRzIGFuZCBwYXJhbWV0ZXJzLCBnZXR0aW5nIHJlc3VsdHMgKihkeW5vLCBkeW5wYXJhbSkqCiAgKiBpcyBjb21wYXRpYmxlIHdpdGggYW55IG5vdmVsIG1ldGhvZCBmb3IgbmV3IGRldmVsb3BlcnMgKihkeW53cmFwKSoKICAqIGNvbnRhaW5zIG1ldHJpY3MgdG8gY29tcGFyZSBtZXRob2RzCiAgKiBhbGxvd3MgYSBjb21wYXJpc29uIG9mIG51bWVyb3VzIG1ldGhvZHMgKihkeW5ldmFsKSoKICAqIGlzIGF2YWlsYWJsZSBhcyByZWZlcmVuY2UgbWFwIHRvIGhlbHAgc2VsZWN0IHRoZSBiZXN0IG1ldGhvZCBiYXNlZCBvbiB1c2VyIGNvbnRleHQgKihkeW5ndWlkZWxpbmVzKSoKCmBgYHtyIGR5bmd1aWQsIGV2YWwgPSBGQUxTRSwgYXR0ci5zb3VyY2U9Jy5udW1iZXJMaW5lcyd9CmR5bmd1aWRlbGluZXM6Omd1aWRlbGluZXNfc2hpbnkoKQpgYGAKCjxjZW50ZXI+IVtsb2dvXSgvc2hhcmVkL3Byb2plY3RzL3NpbmNlbGx0ZV8yMDIyL2ByIHVzZXIuaWRgL0NvdXJzZXMvU2Vjb25kYXJ5X2FuYWx5c2lzL3RyYWplY3RvcnlfYW5hbHlzaXMvdHJhamVjdG9yeV9ybWRfcmVzb3VyY2VzL2R5bmd1aWRlbGluZXMucG5nKTwvY2VudGVyPgo8YnI+CgojIFByYWN0aWNlCgojIyBQcmVyZXF1aXNpdGVzCgojIyMgVGhpcyBSbWFya2Rvd24KCkNvcHkgdGhpcyBSbWQgZmlsZSAoYW5kIGl0cyBkZXBlbmRlbmNlcykgZnJvbSB0aGUgY29tbW9uICdDb3Vyc2VzJyBkaXIgdG8geW91ciB3b3JraW5nIGRpcmVjdG9yeSA6CgpgYGB7ciBybWRjcCwgZWNobyA9IFRSVUUsIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCByZXN1bHRzID0gJ2hpZGUnfQojIyBXb3JraW5nIGRpcmVjdG9yeQp1c2VyLmlkIDwtIFN5cy5nZXRlbnYoJ1VTRVInKQpyb290LmRpciA8LSAnL3NoYXJlZC9wcm9qZWN0cy9zaW5jZWxsdGVfMjAyMicKc2NyaXB0LmRpciA8LSBwYXN0ZShjKHJvb3QuZGlyLCAnQ291cnNlcycsICdTZWNvbmRhcnlfYW5hbHlzZXMnLAogICAgICAgICAgICAgICAgICAgICAgJ3NjcmlwdHMnKSwgY29sbGFwc2UgPSAnLycpCndvcmsuZGlyIDwtIHBhc3RlKGMocm9vdC5kaXIsIHVzZXIuaWQsICdDb3Vyc2VzJywgJ1NlY29uZGFyeV9hbmFseXNpcycsCiAgJ3RyYWplY3RvcnlfYW5hbHlzaXMnKSwgY29sbGFwc2UgPSAnLycpCmRpci5jcmVhdGUocGF0aCA9IHBhc3RlKGMod29yay5kaXIsICd0cmFqZWN0b3J5X3JtZF9yZXNvdXJjZXMnKSwgY29sbGFwc2UgPSAnLycpLCAKICAgICAgICAgICByZWN1cnNpdmUgPSBUUlVFLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKIyMgQ29weWluZyB0aGUgUm1kCmZpbGUuY29weShmcm9tID0gcGFzdGUwKHNjcmlwdC5kaXIsICcvVHJhamVjdG9yeV9URF9kZWxpdmVyYWJsZS5SbWQnKSwKICAgICAgICAgIHRvID0gd29yay5kaXIsIG92ZXJ3cml0ZSA9IFRSVUUpCiMjIENvcHlpbmcgdGhlIGRlcGVuZGVuY2llcwpmaWxlLmNvcHkoZnJvbSA9IGxpc3QuZmlsZXMoCiAgcGF0aCA9IHBhc3RlMChzY3JpcHQuZGlyLCAnL3RyYWplY3Rvcnlfcm1kX3Jlc291cmNlcy8nKSwgZnVsbC5uYW1lcyA9IFRSVUUpLCAKICB0byA9IHBhc3RlMCh3b3JrLmRpciwgJy90cmFqZWN0b3J5X3JtZF9yZXNvdXJjZXMvJyksIG92ZXJ3cml0ZSA9IFRSVUUpCiMjIE1vdmluZyB0byB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5CnNldHdkKHdvcmsuZGlyKQojIyBJZiB5b3Ugd2FudCB0byB2aXN1YWxpemUgeW91ciBmaWxlcyBpbiBSc3R1ZGlvLCB5b3UgY2FuIGNsaWNrIHRoZSBsaXR0bGUgZ3JleWlzaC93aGl0ZSBhcnJvdyBvbiB0aGUgcmlnaHQgb2YgdGhlIHBhdGggd3JpdHRlbiBhdCB0aGUgdG9wIG9mIHlvdXIgUnN0dWRpbyBDb25zb2xlLgpgYGAKCiMjIyBUaGUgZGF0YXNldAoKKiBUaGlzIHByYWN0aWNlIGlzIGluc3BpcmVkIGJ5IHRoaXMgW3Nlc3Npb25dKGh0dHBzOi8vdHJhaW5pbmcuZ2FsYXh5cHJvamVjdC5vcmcvdHJhaW5pbmctbWF0ZXJpYWwvdG9waWNzL3RyYW5zY3JpcHRvbWljcy90dXRvcmlhbHMvc2NybmEtSlVQWVRFUi10cmFqZWN0b3JpZXMvdHV0b3JpYWwuaHRtbCkgZnJvbSB0aGlzIFtHYWxheHkgdHJhaW5pbmddKGh0dHBzOi8vdHJhaW5pbmcuZ2FsYXh5cHJvamVjdC5vcmcvdHJhaW5pbmctbWF0ZXJpYWwvdG9waWNzL3RyYW5zY3JpcHRvbWljcy8pCiogVGhlIGRhdGFzZXQgeW91IHdpbGwgdXNlIDoKICAqIENvbWVzIGZyb20gW0JhY29uIGV0IGFsLCBGcm9udGllcnMgaW4gSW1tdW5vbG9neSwgMjAxOF0uCiAgKiBJcyBhdmFpbGFibGUgYXMgYSBbc2NhbnB5XShodHRwczovL3NjYW5weS5yZWFkdGhlZG9jcy5pby9lbi9zdGFibGUvKSBoNWFkIG9iamVjdCAodG9vbC1zcGVjaWZpYyBmb3JtYXQgYmFzZWQgdXBvbiB0aGUgW0hERjVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0hpZXJhcmNoaWNhbF9EYXRhX0Zvcm1hdCNIREY1KSBzdGFuZGFyZCkKICAqIElzIGhvc3RlZCBhdCBaZW5vZG8gWyM0NzI2OTI3XShodHRwczovL3plbm9kby5vcmcvcmVjb3JkLzQ3MjY5MjcpCiAgKiBDb25zaXN0cyBpbiBhIFtkcm9wLXNlcV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNDQ4MTEzOS8pIHByZXBhcmF0aW9uIG9mIHRoeW1pYyBjZWxscyBpbiBkaWZmZXJlbmNpYXRpb24gZnJvbSA3IG5lb25hdGFsIG1pY2UsIHNlbGVjdGVkIGJ5IG1hc3MgZmxvdyBjeXRvbWV0cnkgdXNpbmcgMjIgaW1tdW5vbG9naWMgbWFya2VycyBjb3JyZXNwb25kaW5nIHRvIG11bHRpcGxlIFQtY2VsbCB0eXBlcyBhbmQgc3VidHlwZXMgKHNlZSBlbWJlZGRlZCBFeGNlbCBmaWxlKQogICogRm9yIHRoaXMgcHJhY3RpY2FsIHNlc3Npb24sIHdpbGwgZm9jdXMgb24gdGhlc2Ugc3VidHlwZXMgOgogICAgKiBEb3VibGUtbmVnYXRpdmUgKiooRE4pKiogOiBFYXJseSBpbW1hdHVyZSBjZWxscwogICAgKiBEb3VibGUtcG9zaXRpdmUgTW9kdWxlIDEgKiooRFAtTTEpKiogOiBJbnRlcm1lZGlhdGUgc3RhdGUgMQogICAgKiBEb3VibGUtcG9zaXRpdmUgTW9kdWxlIDIgKiooRFAtTTIpKiogOiBJbnRlcm1lZGlhdGUgc3RhdGUgMgogICAgKiBEb3VibGUtcG9zaXRpdmUgTW9kdWxlIDMgKiooRFAtTTMpKiogOiBJbnRlcm1lZGlhdGUgc3RhdGUgMwogICAgKiBEb3VibGUtcG9zaXRpdmUgTW9kdWxlIDQgKiooRFAtTTQpKiogOiBJbnRlcm1lZGlhdGUgc3RhdGUgNAogICAgKiBEb3VibGUtcG9zaXRpdmUgTGF0ZSAqKihEUC1MKSoqIDogSW1tYXR1cmUgbGF0ZSBzdGF0ZQogICAgKiBNYXR1cmUgVC1jZWxscyAqKihULW1hdCkqKgogICAgCmBgYHtyIG1hcmtlcnMsIGVjaG89RkFMU0V9CiMjIENlbGwgbWFya2Vycwp4ZnVuOjplbWJlZF9maWxlKHBhc3RlMCgnL3NoYXJlZC9wcm9qZWN0cy9zaW5jZWxsdGVfMjAyMi8nLCB1c2VyLmlkLCAnL0NvdXJzZXMvU2Vjb25kYXJ5X2FuYWx5c2lzL3RyYWplY3RvcnlfYW5hbHlzaXMvdHJhamVjdG9yeV9ybWRfcmVzb3VyY2VzL21hcmtlcnNfY2VsbHR5cGVzLnhsc3gnKSkKYGBgCgojIyMgVGhlIGVudmlyb25tZW50CgpUaGUgUiBwYWNrYWdlcyBuZWVkZWQgZm9yIHRoaXMgdHV0b3JpYWwgYXJlIDoKCiogQ1JBTiA6CiAgKiBbU2V1cmF0XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvU2V1cmF0L2luZGV4Lmh0bWwpCiAgKiBbZHlud3JhcF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2R5bndyYXAvaW5kZXguaHRtbCkKICAgICogW2RwbHlyXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZHBseXIvaW5kZXguaHRtbCkKICAqIFtkeW5wbG90XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZHlucGxvdC9pbmRleC5odG1sKQogICogW2R5bmd1aWRlbGluZXNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9keW5ndWlkZWxpbmVzL2luZGV4Lmh0bWwpCiAgKiBbcGF0Y2h3b3JrXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcGF0Y2h3b3JrL2luZGV4Lmh0bWwpCiAgKiBbUkNvbG9yQnJld2VyXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvUkNvbG9yQnJld2VyL2luZGV4Lmh0bWwpCiAgKiBbdmlyaWRpc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3ZpcmlkaXMvaW5kZXguaHRtbCkKKiBCaW9Db25kdWN0b3IgOgogICogW3NsaW5nc2hvdF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL3NsaW5nc2hvdC5odG1sKQoqIEdpdGh1YiA6CiAgKiBbbW9qYXZlYXp1cmUvc2V1cmF0LWRpc2tdKGh0dHBzOi8vZ2l0aHViLmNvbS9tb2phdmVhenVyZS9zZXVyYXQtZGlzaykKICAqIFtIZWxlbmEtdG9kZC9USW5HYS9wYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vSGVsZW5hLXRvZGQvVEluR2EpCiAgICAqIFtyY2Fubm9vZC9nbmddKGh0dHBzOi8vZ2l0aHViLmNvbS9yY2Fubm9vZC9nbmcpCgpBZGRpdGlvbmFsIHBhY2thZ2VzIG5lZWRlZCB0byBidWlsZCB0aGlzIEhUTUwgcmVwb3J0IDoKCiogQ1JBTiA6CiAgKiBbZ2lmc2tpXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2lmc2tpL2luZGV4Lmh0bWwpICAgICpUbyBnZW5lcmF0ZSBHSUYgYW5pbWF0aW9ucyBvZiBtYXJrZXJzIGV4cHJlc3Npb24qCiAgKiBba25pdHJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9rbml0ci9pbmRleC5odG1sKSAgICAgKlRvIHNldCB0aGUgUm1hcmtkb3duIGhvb2sgdG8gY29tcHV0ZSBDUFUgdGltZSBvZiBjaHVua3MqCiAgKiBbSG1pc2NdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9IbWlzYy9pbmRleC5odG1sKSAgICAgKlRvIHNldCB0aGUgYnV0dG9uLWhpZGRlbiBUT0MqCgoKIyMgUHJlcHJvY2Vzc2luZyBzdGVwcwoKKioqVGhpcyBpcyBqdXN0IGEgZGVzY3JpcHRpb24gb2YgdGhlIHByZXBhcmF0aW9uIEkgcGVyZm9ybWVkIG9uIHRoZSBkYXRhLCBidWlsZGluZyB0aGUgdGhlIG9iamVjdCB5b3Ugd2lsbCB3b3JrIG9uLiBUaGlzIGlzIG5vdCBwYXJ0IG9mIHRoZSBwcmFjdGljYWwgc2Vzc2lvbi4qKioKCjxkZXRhaWxzPjxzdW1tYXJ5PipVbmZvbGQqPC9zdW1tYXJ5PgoKIyMjIFNldHRpbmcgb3VyIHZhcmlhYmxlcwoKYGBge3Igc2V0dGluZ3MsIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBldmFsID0gRkFMU0V9CiMjIFJlbW90ZSBpbnB1dCBmaWxlCnJlbW90ZS5oNWFkLmZpbGUgPC0gJ2h0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvNDcyNjkyNy9maWxlcy9GaW5hbF9jZWxsX2Fubm90YXRlZF9vYmplY3QuaDVhZD9kb3dubG9hZD0xJwojIyBXb3JraW5nIGRpcmVjdG9yeQp1c2VyLmlkIDwtIFN5cy5nZXRlbnYoJ1VTRVInKQpyb290LmRpciA8LSAnL3NoYXJlZC9wcm9qZWN0cy9zaW5jZWxsdGVfMjAyMicKZGF0YS5kaXIgPC0gcGFzdGUoYyhyb290LmRpciwgJ0NvdXJzZXMnLCAnU2Vjb25kYXJ5X2FuYWx5c2VzJywgJ2lucHV0JywgJ3RyYWplY3RvcnlfYW5hbHlzaXMnKSwgY29sbGFwc2UgPSAnLycpCndvcmsuZGlyIDwtIHBhc3RlKGMocm9vdC5kaXIsIHVzZXIuaWQsICdDb3Vyc2VzJywgJ1NlY29uZGFyeV9hbmFseXNpcycsICd0cmFqZWN0b3J5X2FuYWx5c2lzJyksIGNvbGxhcHNlID0gJy8nKQojIyBTZXVyYXQgYXNzYXkgbmFtZQphc3NheSA8LSAnUk5BJwojIyBDb250YW1pbmF0aW5nIGNlbGwgdHlwZXMKY29udGFtLnR5cGVzIDwtIGMoJ01hY3JvcGhhZ2VzJywgJ1JCQycpCiMjIFJORyBzZWVkIChmb3IgaXJsYmEgUENBKQpteS5zZWVkIDwtIDEzMzdMCiMjIEhWR3MgdG8ga2VlcApuZmVhdHVyZXMgPC0gMzAwMAojIyAxc3QgcmVkdWN0aW9uIG1ldGhvZApyZWR1Y3Rpb24gPC0gJ3BjYScKIyMgTnVtYmVyIG9mIFBDcyB0byBwcm9kdWNlCnBjYS5kaW1zIDwtIDEwMAojIyBOdW1iZXIgb2YgUENzIHRvIHJldGFpbiBmb3IgMm5kIHJlZHVjdGlvbgptYXhkaW0gPC0gMzAKIyMgVXNlIHNjYWxlZCBkYXRhIGluc3RlYWQgb2YgdW5zY2FsZWQKdXNlLnNjYWxlZCA8LSBUUlVFCiMjIENlbGwgdHlwZSB0byB1c2UgYXMgcm9vdCB0byBvcmllbnQgdGhlIHRyYWplY3RvcnkKcm9vdC5ncm91cCA8LSAnRE4nCmBgYAoKIyMjIExvYWRpbmcgc291cmNlIGRhdGEKCiogRmlyc3QsIHRoZSBkYXRhc2V0IHdhcyByZXRyaWV2ZWQgZnJvbSBaZW5vZG8KCmBgYHtyIGRhdGFkbCwgYXR0ci5zb3VyY2U9Jy5udW1iZXJMaW5lcycsIGV2YWwgPSBGQUxTRX0KZGlyLmNyZWF0ZShwYXRoID0gd29yay5kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQpsb2NhbC5yb290LmZpbGUgPC0gcGFzdGUwKHdvcmsuZGlyLCAnL09yaWdpbmFsX3NjYW5weV9vYmplY3QuaDUnKQpsb2NhbC5oNWFkLmZpbGUgPC0gcGFzdGUwKGxvY2FsLnJvb3QuZmlsZSwgJ2FkJykKZG93bmxvYWQuZmlsZSh1cmwgPSBoNWFkLmZpbGUsIGRlc3RmaWxlID0gbG9jYWwuaDVhZC5maWxlLAogICAgICAgICAgICAgIG1ldGhvZCA9ICd3Z2V0JywgcXVpZXQgPSBUUlVFKQpgYGAKCiogVGhpcyBvYmplY3Qgd2FzIHRoZW4gY29udmVydGVkIHRvIGEgU2V1cmF0LWNvbXBhdGlibGUgSERGNSBvYmplY3QgdXNpbmcgdGhlIFtTZXVyYXREaXNrOjpDb252ZXJ0KCldKGh0dHBzOi8vcmRyci5pby9naXRodWIvbW9qYXZlYXp1cmUvc2V1cmF0LWRpc2svbWFuL0NvbnZlcnQuaHRtbCkgZnVuY3Rpb24gZnJvbSB0aGUgIFtTZXVyYXREaXNrXShodHRwczovL2dpdGh1Yi5jb20vbW9qYXZlYXp1cmUvc2V1cmF0LWRpc2spIHBhY2thZ2UuCgpgYGB7ciBoNWNvbnYsIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBldmFsID0gRkFMU0V9ClNldXJhdERpc2s6OkNvbnZlcnQoc291cmNlID0gbG9jYWwuaDVhZC5maWxlLCBkZXN0ID0gJ2g1c2V1cmF0JywKICAgICAgICAgICAgICAgICAgICBhc3NheSA9IGFzc2F5LCB2ZXJib3NlID0gRkFMU0UsIG92ZXJ3cml0ZSA9IFRSVUUpCmBgYAoKKiBUaGUgY29udmVydGVkIG9iamVjdCB3YXMgbG9hZGVkIGludG8gUiBhcyBhIFNldXJhdE9iamVjdCB1c2luZyB0aGUgW1NldXJhdERpc2s6OkxvYWRINVNldXJhdCgpXShodHRwczovL3JkcnIuaW8vZ2l0aHViL21vamF2ZWF6dXJlL3NldXJhdC1kaXNrL21hbi9Mb2FkSDVTZXVyYXQuaHRtbCkgZnVuY3Rpb24uIEFsbCBvcHRpb25hbCBzbG90cyAobmVpZ2hib3JzLCBncmFwaHMsIHJlZHVjdGlvbnMsIGltYWdlcyksIHdvbid0IGJlIGxvYWRlZCBhcyB3ZSB3YW50IHRvIHJlcHJvY2VzcyB0aGVtLgoKYGBge3IgaDVTNGxvYWQsIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBldmFsID0gRkFMU0V9CmxvY2FsLmg1cy5maWxlIDwtIHBhc3RlMChsb2NhbC5yb290LmZpbGUsICdzZXVyYXQnKQpzb2JqIDwtIFNldXJhdERpc2s6OkxvYWRINVNldXJhdChmaWxlID0gbG9jYWwuaDVzLmZpbGUsIGFzc2F5cyA9IGFzc2F5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb25zID0gRkFMU0UsIGdyYXBocyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZWlnaGJvcnMgPSBGQUxTRSwgaW1hZ2VzID0gRkFMU0UpCmBgYAoKIyMjIFByZXBwaW5nIHRoZSBvYmplY3QgdG8gb3VyIGNvbnRleHQKCiogVGhlIGRvd25sb2FkZWQgb2JqZWN0IGNvbnRhaW5zIGNlbGwtdHlwZSBhbm5vdGF0aW9uIGZyb20gdGhlIG9yaWdpbmFsIGFuYWx5c2lzLiBGcm9tIHRoZSBbcHVibGljYXRpb25dKGh0dHBzOi8vd3d3LmZyb250aWVyc2luLm9yZy9hcnRpY2xlcy8xMC4zMzg5L2ZpbW11LjIwMTguMDI1MjMvZnVsbCksIHdlIGxlYXJudCB0aGF0IHR3byBjZWxsIHR5cGVzIGFyZSBub3QgcmVsYXRlZCB0byBUIGNlbGxzLCB0aHVzIGNvbnNpZGVyZWQgYXMgY29udGFtaW5hbnRzIDogKiptYWNyb3BoYWdlcyoqIGFuZCAqKnJlZCBibG9vZCBjZWxscyAoUkJDKSoqLiBBcyB0aGVpciBwcmVzZW5jZSBpbiB0aGUgYW5hbHlzaXMgc3BhY2UgY2FuIG1vZGlmeSB0aGUgc2hhcGUgb2YgdGhlIGNlbGxzIHBvc2l0aW9uLCB0aHVzIGltcGFjdCB0cmFqZWN0b3J5IGFuYWx5c2lzLCB3ZSBkZWNpZGVkIHRvIHJlbW92ZSB0aGVtIDoKCmBgYHtyIG5vblRvdXQsIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBldmFsID0gRkFMU0V9CnNvYmogPC0gc29ialssICEoc29iaiRjZWxsX3R5cGUgJWluJSBjb250YW0udHlwZXMpXQpgYGAKCiogV2UgdGhlbiBmb2xsb3cgdGhlICoqU2V1cmF0NCoqIGNhbm9uaWNhbCBwaXBlbGluZSwgYXBwbHlpbmcgKipsb2ctbm9ybWFsaXphdGlvbioqIHRvIHRoZSBjb3VudCBkYXRhIC4uLgoKYGBge3IgUzRsb2dub3JtLCBhdHRyLnNvdXJjZT0nLm51bWJlckxpbmVzJywgZXZhbCA9IEZBTFNFfQpzb2JqIDwtIFNldXJhdDo6Tm9ybWFsaXplRGF0YShvYmplY3QgPSBzb2JqLCBhc3NheSA9IGFzc2F5KQpgYGAKCiogLi4uIHRoZW4gKipzY2FsaW5nKiouIEFzIHRoaXMgb2JqZWN0IGlzIGEgbWVyZ2Ugb2YgNyBleHBlcmltZW50cyAobGFiZWxsZWQgYXMgJ2JhdGNoJyBpbiB0aGUgb2JqZWN0IG1ldGFkYXRhKSwgd2Ugd2FudCB0byByZWdyZXNzIHRoaXMgZWZmZWN0LgoKYGBge3IgUzRzY2FsaW5nLCBhdHRyLnNvdXJjZT0nLm51bWJlckxpbmVzJywgZXZhbCA9IEZBTFNFfQpzb2JqIDwtIFNldXJhdDo6U2NhbGVEYXRhKG9iamVjdCA9IHNvYmosIGFzc2F5ID0gYXNzYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmFycy50by5yZWdyZXNzID0gYygnYmF0Y2gnKSkKYGBgCgoqIC4uLiB0aGVuIGlkZW50aWZ5ICoqSFZHKipzLiBBcyB3ZSBmb2N1cyBvbiBzdWJ0eXBlcyBvZiBhIHNpbmdsZSwgbWFpbiBjZWxsIHR5cGUgKFQtY2VsbHMpIGluIGEgZGlmZmVyZW50aWF0aW9uIGNvbnRleHQsIHdlIGFudGljaXBhdGUgdGhhdCB3ZSBtYXkgaGF2ZSBsaW1pdGVkIHZhcmlhdGlvbiBvZiBnZW5lIGV4cHJlc3Npb24gKGF0IGxlYXN0IGNvbXBhcmVkIHRvLCBieSBleGFtcGxlLCBtdWx0aXBsZSBjZWxsIHR5cGVzIG9mIGEgZGlmZmVyZW50aWF0ZWQgb3JnYW4pLiBUaHVzLCB3ZSBpbmNyZWFzZSB0aGUgZGVmYXVsdCBudW1iZXIgb2YgZmVhdHVyZXMgdG8gb3V0cHV0IGZyb20gMiwwMDAgdG8gKjMsMDAwKi4KCmBgYHtyIGh2Z3MzaywgYXR0ci5zb3VyY2U9Jy5udW1iZXJMaW5lcycsIGV2YWwgPSBGQUxTRX0Kc29iaiA8LSBTZXVyYXQ6OkZpbmRWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IHNvYmosIGFzc2F5ID0gYXNzYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UsIG5mZWF0dXJlcyA9IG5mZWF0dXJlcykKYGBgCgoqIC4uLiB0aGVuIHJlZHVjaW5nIGRpbWVuc2lvbnMgKHRocm91Z2ggKipQQ0EqKikuIFdlIHJlcXVlc3QgMTAwIFBDcyBhcyBvdXRwdXQuCgpgYGB7ciByZWRfcGNhLCBhdHRyLnNvdXJjZT0nLm51bWJlckxpbmVzJywgZXZhbCA9IEZBTFNFfQpzb2JqIDwtIFNldXJhdDo6UnVuUENBKG9iamVjdCA9IHNvYmosIGFzc2F5ID0gYXNzYXksIG5wY3MgPSBwY2EuZGltcywKICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24ubmFtZSA9IHJlZHVjdGlvbiwgc2VlZC51c2UgPSBteS5zZWVkLAogICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgoqIC4uLiB0aGVuIGJ1aWxkaW5nIGFkZGl0aW9uYWwgcmVkdWN0aW9ucyBmb3IgZXhwbG9yYXRpb24gLyB2aXN1YWxpemF0aW9uICgqKnQtU05FKiosICoqdU1BUCoqKS4gQWZ0ZXIgbWFudWFsIGV4cGxvcmF0aW9uLCB3ZSBkZWNpZGVkIHRvIHJldGFpbiAzMCBQQ3MgYXMgaW5wdXQuCgpgYGB7ciByZWRfdHNuZV91bWFwLCBhdHRyLnNvdXJjZT0nLm51bWJlckxpbmVzJywgZXZhbCA9IEZBTFNFfQojIyBUU05FCnJlZHVjdGlvbjJ0IDwtIHBhc3RlMChyZWR1Y3Rpb24sICcuJywgbWF4ZGltLCAnX3RzbmUnKQpzb2JqIDwtIFNldXJhdDo6UnVuVFNORShvYmplY3QgPSBzb2JqLCBkaW1zID0gMTptYXhkaW0sIGFzc2F5ID0gYXNzYXksCiAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IHJlZHVjdGlvbiwgc2VlZC51c2UgPSBteS5zZWVkLAogICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UsIHJlZHVjdGlvbi5uYW1lID0gcmVkdWN0aW9uMnQpCiMjIHVNQVAKcmVkdWN0aW9uMnUgPC0gcGFzdGUwKHJlZHVjdGlvbiwgJy4nLCBtYXhkaW0sICdfdW1hcCcpCnNvYmogPC0gU2V1cmF0OjpSdW5VTUFQKG9iamVjdCA9IHNvYmosIGRpbXMgPSAxOm1heGRpbSwgYXNzYXkgPSBhc3NheSwKICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gcmVkdWN0aW9uLCBzZWVkLnVzZSA9IG15LnNlZWQsCiAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSwgcmVkdWN0aW9uLm5hbWUgPSByZWR1Y3Rpb24ydSkKU2V1cmF0OjpEaW1QbG90KG9iamVjdCA9IHNvYmosIHJlZHVjdGlvbiA9IHJlZHVjdGlvbjJ1LCBncm91cC5ieSA9ICdjZWxsX3R5cGUnLAogICAgICAgICAgICAgICAgc2VlZCA9IG15LnNlZWQpCmBgYAoKKiBMYXN0bHksIHdlIHNhdmUgdGhlIHByb2Nlc3NlZCBvYmplY3QgKGFzIGEgUkRTIGZpbGUpLgoKYGBge3Igc2F2ZXBvYmosIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBldmFsID0gRkFMU0V9CnNhdmVSRFMob2JqZWN0ID0gc29iaiwgZmlsZSA9IHBhc3RlMCh3b3JrLmRpciwnL1RoeW11c19uZW9uYXRfbW1fU2V1cmF0X0VOU0VNQkwuUkRTJyksCiAgICAgICAgY29tcHJlc3MgPSAnYnppcDInKQpgYGAKCjwvZGV0YWlscz4KCiMjIFRyYWplY3RvcnkgYW5hbHlzaXMgdXNpbmcgVEluR2EKCldlIHdpbGwgdXNlIGEgdG9vbCBuYW1lZCBbVEluR2FdKGh0dHBzOi8vYWNhZGVtaWMub3VwLmNvbS9iaW9pbmZvcm1hdGljcy9hcnRpY2xlLzM2L1N1cHBsZW1lbnRfMS9pNjYvNTg3MDUxNykgdG8gcGVyZm9ybSB0aGlzIGFuYWx5c2lzLCBkdWUgdG8gaXRzIGNvbnZlbmllbmNlIHRvIG91ciBlZHVjYXRpb25hbCBwdXJwb3NlIDoKCiAgKiBJdCdzIGluIGZ1bGwgUgogICogSXQncyB2ZXJ5IGZhc3QKICAqIEl0cyBwYXJhbWV0ZXJzIGFyZSBsaW1pdGVkIChhcyBjb21wYXJlZCB0byBvdGhlciByZW5vd25lZCB0b29scyBsaWtlICoqTW9ub2NsZTMqKikKICAqIEl0IGlzIGNvbXBhdGlibGUgd2l0aCBtb3N0IHRvcG9sb2dpZXMgKHNlZSBbaGVyZV0oaHR0cHM6Ly9hY2FkZW1pYy5vdXAuY29tL3ZpZXctbGFyZ2UvZmlndXJlLzIwNTQ4MDQ1My9idGFhNDYzZjIudGlmKSkKICAqIEl0J3MgaW50ZWdyYXRlZCBpbiB0aGUgdmVyc2F0aWxlICoqZHludmVyc2UqKiAoYXMgYSBwbHVnaW4pCgojIyMgU2V0dGluZyBwYXJhbWV0ZXJzCgpgYGB7ciBUc2V0dGluZ3MsIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBldmFsID0gVFJVRX0KIyMgV29ya2luZyBkaXJlY3RvcnkKdXNlci5pZCA8LSBTeXMuZ2V0ZW52KCdVU0VSJykKcm9vdC5kaXIgPC0gJy9zaGFyZWQvcHJvamVjdHMvc2luY2VsbHRlXzIwMjInCmRhdGEuZGlyIDwtIHBhc3RlKGMocm9vdC5kaXIsICdDb3Vyc2VzJywgJ1NlY29uZGFyeV9hbmFseXNlcycsICdpbnB1dCcsCiAgICAgICAgICAgICAgICAgICAgJ3RyYWplY3RvcnlfYW5hbHlzaXMnKSwgY29sbGFwc2UgPSAnLycpCndvcmsuZGlyIDwtIHBhc3RlKGMocm9vdC5kaXIsIHVzZXIuaWQsICdDb3Vyc2VzJywgJ1NlY29uZGFyeV9hbmFseXNpcycsCiAgICAgICAgICAgICAgICAgICAgJ3RyYWplY3RvcnlfYW5hbHlzaXMnKSwgY29sbGFwc2UgPSAnLycpCmlmKCFkaXIuZXhpc3RzKHdvcmsuZGlyKSkgZGlyLmNyZWF0ZShwYXRoID0gd29yay5kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCiMjIExvY2FsIGlucHV0IGZpbGUKIyMgVGhlIGZpbGUgY3JlYXRlZCBpZiB5b3UgcGVyZm9ybWVkIHRoZSBwcmVwcm9jZXNzaW5nIGJ5IHlvdXJzZWxmCiMgbG9jYWwuU2V1cmF0LmZpbGUgPC0gcGFzdGUwKHdvcmsuZGlyLCAnL1RoeW11c19uZW9uYXRfbW1fU2V1cmF0X0VOU0VNQkwuUkRTJykKIyMgVGhlIHByZXByb2Nlc3NlZCBmaWxlIEkgcHJlcGFyZWQgZm9yIHlvdQpsb2NhbC5TZXVyYXQuZmlsZSA8LSBwYXN0ZTAoZGF0YS5kaXIsICcvVGh5bXVzX25lb25hdF9tbV9TZXVyYXRfRU5TRU1CTC5SRFMnKQojIyBTZXVyYXQgYXNzYXkgbmFtZQphc3NheSA8LSAnUk5BJwojIyBSTkcgc2VlZCAoZm9yIGlybGJhIFBDQSkKbXkuc2VlZCA8LSAxMzM3TAojIyAxc3QgcmVkdWN0aW9uIG1ldGhvZApyZWR1Y3Rpb24gPC0gJ3BjYScKIyMgMm5kIHJlZHVjdGlvbiBtZXRob2QKcmVkdWN0aW9uMiA8LSAncGNhLjMwX3VtYXAnCiMjIFVzZSBzY2FsZWQgZGF0YSBpbnN0ZWFkIG9mIHVuc2NhbGVkCnVzZS5zY2FsZWQgPC0gVFJVRQojIyBDZWxsIHR5cGUgdG8gdXNlIGFzIHJvb3QgdG8gb3JpZW50IHRoZSB0cmFqZWN0b3J5CnJvb3QuZ3JvdXAgPC0gJ0ROJwojIyBNYXJrZXIgZ2VuZXMgdG8gcGxvdAp0cmFucy5tYXJrZXJzIDwtIGMoJ0lsMnJhJywgJ05wbTEnLCAnTnVzYXAxJywgJ1ViZTJjJywKICAgICAgICAgICAgICAgICAgICdQY2xhZicsJ0NkOGIxJywgJ0l0bTJhJywgJ0NkNTInLCAnSDItRDEnKQpgYGAKCiMjIyBMb2FkaW5nIHNvdXJjZSBkYXRhCgpXZSB3aWxsIHNpbXBseSBsb2FkIHRoZSBwcmUtcHJvY2Vzc2VkIFNldXJhdCBvYmplY3QsIGNvbnRhaW5lZCBpbiBhIFJEUyBhcmNoaXZlLCBhcyBpbnB1dCA6CgpgYGB7ciBUbG9hZCwgYXR0ci5zb3VyY2U9Jy5udW1iZXJMaW5lcycsIGV2YWwgPSBUUlVFLCB0aW1lX2l0ID0gVFJVRX0Kc29iaiA8LSByZWFkUkRTKGZpbGUgPSBsb2NhbC5TZXVyYXQuZmlsZSkKYGBgCgojIyMgUGVyZm9ybWluZyB0cmFqZWN0b3J5IGFuYWx5c2lzCgojIyMjIFRoZSByZWR1Y2VkIHNwYWNlCgpGaXJzdCwgdG8gaW1wcmVnbmF0ZSB1cyB3aXRoIHRoZSBjZWxscyB0b3BvbG9neSBvbiB3aGljaCBUSW5HYSB3aWxsIHdvcmssIHdlIGNhbiBkaXNwbGF5IG91ciAyRCBkaW1lbnNpb24gcmVkdWN0aW9uICh1TUFQIG9yIHQtU05FKSA6CgpgYGB7ciBTZGltMnBsb3QsIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBldmFsID0gVFJVRSwgZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA1fQojIyBXaXRoIGJhdGNoZXMKZGIgPC0gU2V1cmF0OjpEaW1QbG90KG9iamVjdCA9IHNvYmosIHJlZHVjdGlvbiA9IHJlZHVjdGlvbjIsIGdyb3VwLmJ5ID0gJ2JhdGNoJywKICAgICAgICAgICAgICAgICAgICAgIHB0LnNpemUgPSAyKSArIFNldXJhdDo6RGFya1RoZW1lKCkKIyMgV2l0aCBjZWxsIHR5cGVzCmRjdCA8LSBTZXVyYXQ6OkRpbVBsb3Qob2JqZWN0ID0gc29iaiwgcmVkdWN0aW9uID0gcmVkdWN0aW9uMiwgZ3JvdXAuYnkgPSAnY2VsbF90eXBlJywKICAgICAgICAgICAgICAgICAgICAgICBwdC5zaXplID0gMikgKyBTZXVyYXQ6OkRhcmtUaGVtZSgpCiMjIFdpdGggc2V4CmRzIDwtIFNldXJhdDo6RGltUGxvdChvYmplY3QgPSBzb2JqLCByZWR1Y3Rpb24gPSByZWR1Y3Rpb24yLCBncm91cC5ieSA9ICdzZXgnLAogICAgICAgICAgICAgICAgICAgICAgcHQuc2l6ZSA9IDIpICsgU2V1cmF0OjpEYXJrVGhlbWUoKQpwcmludChwYXRjaHdvcms6OndyYXBfcGxvdHMobGlzdChkYiwgZGN0LCBkcykpKQpgYGAKCmBgYHtyIHJtMSwgZWNobyA9IEZBTFNFfQpybShkYiwgZGN0LCBkcykKYGBgCgojIyMjIEV4dHJhY3RpbmcgZGF0YQoKVXNpbmcgVEluR2EgZmlyc3QgcmVxdWlyZXMgdG8gYnVpbGQgYSBzcGVjaWZpYyBvYmplY3QgZnJvbSB0aGUgY291bnQgYW5kIG5vcm1hbGl6ZWQgbWF0cmljZXMsIHRoYXQgd2Ugd2lsbCBleHRyYWN0IGZyb20gb3VyIFNldXJhdCBvYmplY3QgOgoKYGBge3IgbWF0ZXh0cmFjdCwgYXR0ci5zb3VyY2U9Jy5udW1iZXJMaW5lcycsIGV2YWwgPSBUUlVFfQojIyBNYXRyaWNlcyBhcmUgcmVxdWlyZWQgdG8gYmUgZm9ybWF0dGVkIGFzIFtjZWxsc10geCBbZmVhdHVyZXNdLCB0aHVzIGEgdHJhbnNwb3NpdGlvbiBpcyByZXF1aXJlZAojIyBOb3JtYWxpemVkIG1hdHJpeApteS5leHAgPC0gdChhcy5tYXRyaXgoaWYodXNlLnNjYWxlZCkgc29iakBhc3NheXNbW2Fzc2F5XV1Ac2NhbGUuZGF0YQogICAgICAgICAgICAgICAgICAgICAgZWxzZSBzb2JqQGFzc2F5c1tbYXNzYXldXUBkYXRhKSkgCiMjIyBPcmRlcmluZyB0byBrZWVwIHN5bmNoIGluIHRoZSBbdXNlLnNjYWxlZF0gY29udGV4dApteS5leHAgPC0gbXkuZXhwWywgb3JkZXIoY29sbmFtZXMobXkuZXhwKSldCiMjIENvdW50IG1hdHJpeApteS5jb3VudHMgPC0gdChhcy5tYXRyaXgoaWYodXNlLnNjYWxlZCkgc29iakBhc3NheXNbW2Fzc2F5XV1AY291bnRzW3Jvd25hbWVzKHNvYmpAYXNzYXlzW1thc3NheV1dQGNvdW50cyklaW4lIGNvbG5hbWVzKG15LmV4cCksXQogICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSBzb2JqQGFzc2F5c1tbYXNzYXldXUBjb3VudHMpKQojIyMgT3JkZXJpbmcgdG8ga2VlcCBzeW5jaCBpbiB0aGUgW3VzZS5zY2FsZWRdIGNvbnRleHQKbXkuY291bnRzIDwtIG15LmNvdW50c1ssIG9yZGVyKGNvbG5hbWVzKG15LmNvdW50cykpXQpgYGAKCiMjIyMgQnVpbGRpbmcgYSBkeW53cmFwIG9iamVjdAoKV2UgY2FuIG5vdyBidWlsZCBvdXIgb2JqZWN0IChhICoqZHlud3JhcCoqIG9iamVjdCwgd2hpY2ggYWN0dWFsbHkgY29uc2lzdHMgaW4gYSBsaXN0IHdpdGggbWFuZGF0b3J5IGVudHJpZXMpIDoKCmBgYHtyIFRvYmosIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBldmFsID0gVFJVRSwgdGltZV9pdCA9IFRSVUV9Cm15LmRhdGEgPC0gZHlud3JhcDo6d3JhcF9leHByZXNzaW9uKAogIGlkID0gc29iakBwcm9qZWN0Lm5hbWUsCiAgZXhwcmVzc2lvbiA9IG15LmV4cCwKICBjb3VudHMgPSBteS5jb3VudHMKKQppcyhteS5kYXRhKQpgYGAKCmBgYHtyIHJtMiwgZWNobyA9IEZBTFNFfQpybShteS5leHAsIG15LmNvdW50cykKYGBgCgojIyMjIEltcG9ydCBwcmlvcgoKV2UgY2FuIGltcG9ydCBvdXIgKipyZWR1Y2VkIHNwYWNlKiogKHQtU05FIG9yIHVNQVApIGFzIGEgcHJpb3IsIHdoaWNoIHdpbGwgYmUgdXNlZCBieSBUSW5HYSB0byBidWlsZCB0aGUgdHJhamVjdG9yeSA6CgpgYGB7ciBUcmVkaW1wb3J0LCBhdHRyLnNvdXJjZT0nLm51bWJlckxpbmVzJywgZXZhbCA9IFRSVUV9Cm15LmRhdGEgPC0gZHlud3JhcDo6YWRkX3ByaW9yX2luZm9ybWF0aW9uKAogIGRhdGFzZXQgPSBteS5kYXRhLAogIGRpbXJlZCA9IHNvYmpAcmVkdWN0aW9uc1tbcmVkdWN0aW9uMl1dQGNlbGwuZW1iZWRkaW5ncwopCmBgYAoKIyMjIyBSdW5uaW5nIFRJbkdhCgpOb3csIHdlIGNhbiBydW4gVEluR2EgIQoKYGBge3IgVG9uZSwgYXR0ci5zb3VyY2U9Jy5udW1iZXJMaW5lcycsIGV2YWwgPSBUUlVFLCBmaWcuYWxpZ24gPSAnY2VudGVyJywgdGltZV9pdCA9IFRSVUV9CiMjIFBlcmZvcm1pbmcgVEluR2EgYW5hbHlzaXMKc2V0LnNlZWQobXkuc2VlZCkKbXkudHJhaiA8LSBkeW53cmFwOjppbmZlcl90cmFqZWN0b3J5KGRhdGFzZXQgPSBteS5kYXRhLCBtZXRob2QgPSBUSW5HYTo6Z25nX3BhcmFtMigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IG15LnNlZWQsIGdpdmVfcHJpb3JzID0gJ2RpbXJlZCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbWV0ZXJzID0gbGlzdChtYXhfbm9kZXMgPSA4LCBsYW1iZGEgPSAyMDApKQojIyBBZGRpbmcgcHNldWRvdGltZQpteS50cmFqIDwtIGR5bndyYXA6OmFkZF9wc2V1ZG90aW1lKHRyYWplY3RvcnkgPSBteS50cmFqKQojIyBQbG90dGluZyB3aXRoIHBzZXVkb3RpbWUKZHlucGxvdDo6cGxvdF9kaW1yZWQodHJhamVjdG9yeSA9IG15LnRyYWosIGxhYmVsX21pbGVzdG9uZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICBjb2xvcl9jZWxscyA9ICdwc2V1ZG90aW1lJykgKyBTZXVyYXQ6OkRhcmtUaGVtZSgpCmBgYAoKIyMjIyBBZGRpbmcgY2VsbCB0eXBlcwoKV2UgY2FuIGltcG9ydCBvdXIgY2VsbCB0eXBlcyAoYXMgYSBtZXRhZGF0YSBmb3IgbGF0ZXIgcGxvdHMpIGluIHRoZSB0cmFqZWN0b3J5IG9iamVjdCA6CgpgYGB7ciBUdHlwZWltcG9ydCwgYXR0ci5zb3VyY2U9Jy5udW1iZXJMaW5lcycsIGV2YWwgPSBUUlVFLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMgUGxvdHRpbmcgY2VsbCB0eXBlcyBhcyBncm91cGluZwpteS50cmFqIDwtIGR5bndyYXA6OmFkZF9ncm91cGluZyhkYXRhc2V0ID0gbXkudHJhaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBpbmcgPSBhcy5jaGFyYWN0ZXIoc29iaiRjZWxsX3R5cGUpKQojIyBQbG90dGluZyB3aXRoIGNlbGwgdHlwZXMKZHlucGxvdDo6cGxvdF9kaW1yZWQodHJhamVjdG9yeSA9IG15LnRyYWosIGxhYmVsX21pbGVzdG9uZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICBjb2xvcl9jZWxscyA9ICJncm91cGluZyIpICsgU2V1cmF0OjpEYXJrVGhlbWUoKQpgYGAKCiMjIyMgQWRkaW5nIGEgcm9vdAoKKiBBcyB3ZSBrbm93IG91ciByb290IGNlbGwgdHlwZSAoRE4pLCB3ZSBjYW4gZ2l2ZSBhIGRpcmVjdGlvbiB0byB0aGUgdHJhamVjdG9yeS4gSG93ZXZlciwgVEluR2EgcmVxdWlyZXMgYSAqKnVuaXF1ZSByb290IGNlbGwqKiwgbm90IGEgcm9vdCAqKmNsdXN0ZXIqKi4uLiBUaHVzLCB3ZSB3aWxsIGZpcnN0IGlkZW50aWZ5IGEgcHV0YXRpdmUgcm9vdCBjZWxsIGJ5IGNvbXB1dGluZyB0aGUgY2VudHJvaWQgb2YgdGhlIEROIGNlbGxzIGluIHRoZSByZWR1Y2VkIHNwYWNlIDoKCmBgYHtyIFRjZW50cm9pZHMsIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBldmFsID0gVFJVRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA1LCBmaWcuYWxpZ24gPSAnY2VudGVyJywgdGltZV9pdCA9IFRSVUV9CmlmICghaXMubnVsbChyb290Lmdyb3VwKSkgewogICMjIElkZW50aWZ5aW5nIGNlbGxzIG9mIHRoZSByb290IGdyb3VwCiAgY2VsbHNfaW5fcm9vdC5ncm91cCA8LSB3aGljaChteS50cmFqJGdyb3VwaW5nID09IHJvb3QuZ3JvdXApCiAgIyMgQ29tcHV0aW5nIHRoZSByb290IGdyb3VwIG1lZG9pZCBpbiB0aGUgcmVkdWNlZCBzcGFjZQogIHJvb3RfZ3JvdXAubWVkb2lkIDwtIGFwcGx5KG15LnRyYWokZGltcmVkW2NlbGxzX2luX3Jvb3QuZ3JvdXAsXSwgMiwgbWVkaWFuKQogICMjIENvbXB1dGluZyB0aGUgcm9vdCBncm91cCBjZWxscyBkaXN0YW5jZSB0byByb290IGdyb3VwIG1lZG9pZAogIGNlbGxzLmRpc3RfdG9fbWVkb2lkIDwtIHZhcHBseShjZWxsc19pbl9yb290Lmdyb3VwLCBmdW5jdGlvbih4KSB7IG5vcm0oYXMubWF0cml4KG15LnRyYWokZGltcmVkW3gsXSAtIHJvb3RfZ3JvdXAubWVkb2lkKSwgJ0YnKX0sIC4xKQogICMjIElkZW50aWZ5aW5nIHRoZSByb290IGNlbGwKICByb290LmNlbGwuaWQgPC0gbXkudHJhaiRjZWxsX2luZm8kY2VsbF9pZFtjZWxsc19pbl9yb290Lmdyb3VwW3doaWNoLm1pbihjZWxscy5kaXN0X3RvX21lZG9pZCldXQogIG1lc3NhZ2UocGFzdGUwKHJvb3QuZ3JvdXAsICcgcm9vdCBjZWxsIDogJywgcm9vdC5jZWxsLmlkKSkKICAjIyBQbG90dGluZyBjZWxsIHR5cGVzCiAgZGN0IDwtIFNldXJhdDo6RGltUGxvdChvYmplY3QgPSBzb2JqLCByZWR1Y3Rpb24gPSByZWR1Y3Rpb24yLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gJ2NlbGxfdHlwZScsIHB0LnNpemUgPSAyKSArIFNldXJhdDo6RGFya1RoZW1lKCkKICAjIyBQbG90dGluZyB0aGUgcm9vdCBjZWxsCiAgc29iaiRyb290X2NlbGwgPC0gRkFMU0UKICBzb2JqJHJvb3RfY2VsbFtjb2xuYW1lcyhzb2JqKSA9PSByb290LmNlbGwuaWRdIDwtIFRSVUUKICBkcmMgPC0gU2V1cmF0OjpEaW1QbG90KG9iamVjdCA9IHNvYmosIHJlZHVjdGlvbiA9IHJlZHVjdGlvbjIsIAogICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAncm9vdF9jZWxsJywgcHQuc2l6ZSA9IGMocmVwKDIsIG5jb2woc29iaiktMSksIDUpLAogICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSBUUlVFKSArIFNldXJhdDo6RGFya1RoZW1lKCkKICBwcmludChwYXRjaHdvcms6OndyYXBfcGxvdHMobGlzdChkY3QsIGRyYykpKQp9CmBgYAoKYGBge3Igcm0zLCBlY2hvID0gRkFMU0V9CnJtKGRjdCwgZHJjKQpgYGAKCgoqIFJvb3RlZCBwbG90cwoKYGBge3IgVHJvb3RlZCwgYXR0ci5zb3VyY2U9Jy5udW1iZXJMaW5lcycsIGV2YWwgPSBUUlVFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDUsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQojIyBBZGRpbmcgcm9vdCBjZWxsCm15LnRyYWogPC0gZHlud3JhcDo6YWRkX3Jvb3QodHJhamVjdG9yeSA9IG15LnRyYWosIHJvb3RfY2VsbF9pZCA9IHJvb3QuY2VsbC5pZCkKIyMgUGxvdHRpbmcgd2l0aCBjZWxsIHR5cGVzCmRjdCA8LSBkeW5wbG90OjpwbG90X2RpbXJlZCh0cmFqZWN0b3J5ID0gbXkudHJhaiwgbGFiZWxfbWlsZXN0b25lcyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JfY2VsbHMgPSAiZ3JvdXBpbmciKSArIFNldXJhdDo6RGFya1RoZW1lKCkKIyMgUGxvdHRpbmcgd2l0aCBwc2V1ZG90aW1lCmRwdCA8LSBkeW5wbG90OjpwbG90X2RpbXJlZCh0cmFqZWN0b3J5ID0gbXkudHJhaiwgbGFiZWxfbWlsZXN0b25lcyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JfY2VsbHMgPSAicHNldWRvdGltZSIpICsgU2V1cmF0OjpEYXJrVGhlbWUoKQpwcmludChwYXRjaHdvcms6OndyYXBfcGxvdHMobGlzdChkY3QsIGRwdCkpKQpgYGAKCmBgYHtyIHJtNCwgZWNobyA9IEZBTFNFfQpybShkY3QsIGRwdCkKYGBgCgojIyMjIFBsb3R0aW5nIG1hcmtlciBnZW5lcwoKKiBGaXJzdCwgd2UnbGwgcmVwbGFjZSBFbnNlbWJsSURzIHdpdGggTUdJIFN5bWJvbHMgKGVhc2llciB0byBoYW5kbGUpCiAgCmBgYHtyIGFkZHN5bWIsIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBldmFsID0gVFJVRX0KIyMgSW5qZWN0aW5nIHN5bWJvbHMKc3ltYnZlYyA8LSBhcy5jaGFyYWN0ZXIoc29iakBhc3NheXMkUk5BQG1ldGEuZmVhdHVyZXMkU3ltYm9sKQpuYW1lcyhzeW1idmVjKSA8LSBzb2JqQGFzc2F5cyRSTkFAbWV0YS5mZWF0dXJlcyRJRApjb2xuYW1lcyhteS5kYXRhJGNvdW50cykgPC0gY29sbmFtZXMobXkuZGF0YSRleHByZXNzaW9uKSA8LSB1bm5hbWUoc3ltYnZlY1tjb2xuYW1lcyhteS5kYXRhJGV4cHJlc3Npb24pXSkKYGBgCiAgCiogUGxvdHRpbmcgbWFya2VycyA6CiAgKiBGcm9tIG1pbGVzdG9uZSAxIHRvIDMgOgoKYGBge3IgVE1tMTMsIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBldmFsID0gVFJVRSwgZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQ9IDE1LCBmaWcuYWxpZ24gPSAnY2VudGVyJywgdGltZV9pdCA9IFRSVUV9CiMjIE1pbGVzdG9uZXMgMSB0byAzIC4uLgp0bXBsb3RzIDwtIHNhcHBseSh0cmFucy5tYXJrZXJzWzE6NF0sIGZ1bmN0aW9uKHgpIHsKICBkeW5wbG90OjpwbG90X2RpbXJlZCh0cmFqZWN0b3J5ID0gbXkudHJhaiwgbGFiZWxfbWlsZXN0b25lcyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX2NlbGxzID0gImZlYXR1cmUiLCBmZWF0dXJlX29pID0geCwgCiAgICAgICAgICAgICAgICAgICAgICAgZXhwcmVzc2lvbl9zb3VyY2UgPSBteS5kYXRhJGV4cHJlc3Npb24pICsgU2V1cmF0OjpEYXJrVGhlbWUoKQogIH0sIHNpbXBsaWZ5ID0gRkFMU0UpCnByaW50KHBhdGNod29yazo6d3JhcF9wbG90cyh0bXBsb3RzKSkKYGBgCgpgYGB7ciBybTUsIGVjaG8gPSBGQUxTRX0Kcm0odG1wbG90cykKYGBgCgogICogRnJvbSBtaWxlc3RvbmUgMSB0byAyIDoKCmBgYHtyIFRNbTEyLCBhdHRyLnNvdXJjZT0nLm51bWJlckxpbmVzJywgZXZhbCA9IFRSVUUsIGZpZy53aWR0aCA9IDE1LCBmaWcuaGVpZ2h0PSAxNSwgZmlnLmFsaWduID0gJ2NlbnRlcicsIHRpbWVfaXQgPSBUUlVFfQojIyBNaWxlc3RvbmVzIDEgdG8gMiAuLi4KdG1wbG90cyA8LSBzYXBwbHkodHJhbnMubWFya2Vyc1tjKDE6Miw1OjkpXSwgZnVuY3Rpb24oeCkgewogIGR5bnBsb3Q6OnBsb3RfZGltcmVkKHRyYWplY3RvcnkgPSBteS50cmFqLCBsYWJlbF9taWxlc3RvbmVzID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3JfY2VsbHMgPSAiZmVhdHVyZSIsIGZlYXR1cmVfb2kgPSB4LCAKICAgICAgICAgICAgICAgICAgICAgICBleHByZXNzaW9uX3NvdXJjZSA9IG15LmRhdGEkZXhwcmVzc2lvbikgKyBTZXVyYXQ6OkRhcmtUaGVtZSgpCiAgfSwgc2ltcGxpZnkgPSBGQUxTRSkKcHJpbnQocGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHRtcGxvdHMpKQpgYGAKCmBgYHtyIHJtNiwgZWNobyA9IEZBTFNFfQpybSh0bXBsb3RzKQpgYGAKCiMjIyMgQW5pbWF0ZWQgIQoKKiBGcm9tIG1pbGVzdG9uZSAxIHRvIDMgOgoKYGBge3IgVGFuaW0yYSwgYXR0ci5zb3VyY2U9Jy5udW1iZXJMaW5lcycsIGFuaW1hdGlvbi5ob29rPSJnaWZza2kiLCBpbnRlcnZhbCA9IC4zMywgZXZhbCA9IEZBTFNFLCBlY2hvID0gVFJVRX0KIyMgTWlsZXN0b25lcyAxIHRvIDMgLi4uCmZvciAoeCBpbiB0cmFucy5tYXJrZXJzWzE6NF0pIHByaW50KAogIGR5bnBsb3Q6OnBsb3RfZGltcmVkKHRyYWplY3RvcnkgPSBteS50cmFqLCBsYWJlbF9taWxlc3RvbmVzID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3JfY2VsbHMgPSAiZmVhdHVyZSIsIGZlYXR1cmVfb2kgPSB4LCAKICAgICAgICAgICAgICAgICAgICAgICBleHByZXNzaW9uX3NvdXJjZSA9IG15LmRhdGEkZXhwcmVzc2lvbikgKyBTZXVyYXQ6OkRhcmtUaGVtZSgpKQpgYGAKCjxjZW50ZXI+IVthbmltMmFdKC9zaGFyZWQvcHJvamVjdHMvc2luY2VsbHRlXzIwMjIvYHIgdXNlci5pZGAvQ291cnNlcy9TZWNvbmRhcnlfYW5hbHlzaXMvdHJhamVjdG9yeV9hbmFseXNpcy90cmFqZWN0b3J5X3JtZF9yZXNvdXJjZXMvVGFuaW0yYS5naWYpPC9jZW50ZXI+PGJyPgoKKiBGcm9tIG1pbGVzdG9uZSAxIHRvIDIgOgoKYGBge3IgVGFuaW0yYiwgYXR0ci5zb3VyY2U9Jy5udW1iZXJMaW5lcycsIGFuaW1hdGlvbi5ob29rPSJnaWZza2kiLCBpbnRlcnZhbCA9IC4zMywgZXZhbCA9IEZBTFNFLCBlY2hvID0gVFJVRX0KIyMgTWlsZXN0b25lcyAxIHRvIDIgLi4uCmZvciAoeCBpbiB0cmFucy5tYXJrZXJzW2MoMToyLDU6OSldKSBwcmludCgKICBkeW5wbG90OjpwbG90X2RpbXJlZCh0cmFqZWN0b3J5ID0gbXkudHJhaiwgbGFiZWxfbWlsZXN0b25lcyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX2NlbGxzID0gImZlYXR1cmUiLCBmZWF0dXJlX29pID0geCwgCiAgICAgICAgICAgICAgICAgICAgICAgZXhwcmVzc2lvbl9zb3VyY2UgPSBteS5kYXRhJGV4cHJlc3Npb24pICsgU2V1cmF0OjpEYXJrVGhlbWUoKSkKYGBgCgo8Y2VudGVyPiFbYW5pbTJiXSgvc2hhcmVkL3Byb2plY3RzL3NpbmNlbGx0ZV8yMDIyL2ByIHVzZXIuaWRgL0NvdXJzZXMvU2Vjb25kYXJ5X2FuYWx5c2lzL3RyYWplY3RvcnlfYW5hbHlzaXMvdHJhamVjdG9yeV9ybWRfcmVzb3VyY2VzL1RhbmltMmIuZ2lmKTwvY2VudGVyPjxicj4KCiMgVHJhamVjdG9yeSBhbmFseXNpcyB1c2luZyBTbGluZ3Nob3QKCiogW1NsaW5nc2hvdF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL3NsaW5nc2hvdC5odG1sKSBpcyBhIHdheSBtb3JlIGNvbW1vbmx5IHVzZWQgdHJhamVjdG9yeSBpbmZlcmVuY2UgdG9vbCB0aGFuIFRJbkdhICoodGhhdCBJIGNob3NlIGZvciBlZHVjYXRpb25hbCBwdXJwb3NlLCBhbW9uZyBvdGhlciB0aGluZ3MpKi4KKiBMaWtlIFRJbkdhLCBpdCdzIGEgZnVsbC1SLCBlYXN5IHRvIHVzZSB0b29sIHRoYXQgaXMgY29tcGF0aWJsZSB3aXRoIG1vc3QgdHJhamVjdG9yeSB0b3BvbG9naWVzLgoqIEhvd2V2ZXIsIGl0IGRvZXMgaGF2ZSBhIGtleSBkaWZmZXJlbmNlIDogY29tcGFyZWQgdG8gVEluR2EsIGl0IGFkZGl0aW9uYWxseSByZXF1aXJlcyBhIGNlbGwgY2x1c3RlcmluZyByZXN1bHRzLiBUaGlzIG1heSBiZSBlaXRoZXIgYW4gYWR2YW50YWdlIChpZiB5b3UgYXJlIGNvbmZpZGVudCBpbiB0aGUgY2FwYWNpdHkgb2YgeW91IGNsdXN0ZXJpbmcgcmVzdWx0cyB0byByZWZsZWN0IHRoZSBiaW9sb2d5LCBlc3BhY2lhbGx5IHRoZSBjZWxscyBsaW5lYWdlKSBvciBhIGNvbiAoaWYgeW91IGFyZSBub3QpLgoKKiBSdW5uaW5nIFNsaW5nc2hvdAoKYGBge3Igc2xpbmdydW4sIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCB0aW1lX2l0ID0gVFJVRX0KIyMgUnVuIHNsaW5nc2hvdCB3aXRoIHJvb3QgY2x1c3RlcgpzbGluZy5yZXMgPC0gc2xpbmdzaG90OjpzbGluZ3Nob3QoZGF0YSA9IHNvYmpAcmVkdWN0aW9ucyRwY2EuMzBfdW1hcEBjZWxsLmVtYmVkZGluZ3MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyTGFiZWxzID0gc29iaiRjZWxsX3R5cGUsIHN0YXJ0LmNsdXMgPSAnRE4nKQojIyBQbG90IHJlc3VsdHMgKGxpbmVhZ2UpCnBsb3Qoc29iakByZWR1Y3Rpb25zJHBjYS4zMF91bWFwQGNlbGwuZW1iZWRkaW5ncywgCiAgICAgY29sID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDksIlNldDEiKVtzb2JqJGNlbGxfdHlwZV0sIAogICAgIGFzcCA9IDEsIHBjaCA9IDE2KQpsaW5lcyhzbGluZ3Nob3Q6OmFzLlNsaW5nc2hvdERhdGFTZXQoc2xpbmcucmVzKSwgdHlwZSA9ICdsaW5lYWdlcycpCiMjIFBsb3QgcmVzdWx0cyAoZml0dGVkIGN1cnZlKQpwbG90KHNvYmpAcmVkdWN0aW9ucyRwY2EuMzBfdW1hcEBjZWxsLmVtYmVkZGluZ3MsIAogICAgIGNvbCA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg5LCJTZXQxIilbc29iaiRjZWxsX3R5cGVdLCAKICAgICBhc3AgPSAxLCBwY2ggPSAxNikKbGluZXMoc2xpbmdzaG90Ojphcy5TbGluZ3Nob3REYXRhU2V0KHNsaW5nLnJlcykpCmBgYAoKU2xpbmdzaG90IGlkZW50aWZpZWQgMyBsaW5lYWdlcywgbXVjaCBpbiBjb25jb3JkYW5jZSB3aXRoIFRJbkdhIHJlc3VsdHMgIQoKKiBHZXR0aW5nIGNlbGxzIHBzZXVkb3RpbWVzIChwZXIgbGluZWFnZSkKCmBgYHtyIHNsaW5ncHQsIGF0dHIuc291cmNlPScubnVtYmVyTGluZXMnLCBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDUsIHRpbWVfaXQgPSBUUlVFfQojIyBHZXR0aW5nIHBzZXVkb3RpbWVzCm15LnBzZXVkbyA8LSBzbGluZ3Nob3Q6OnNsaW5nUHNldWRvdGltZSh4ID0gc2xpbmcucmVzKQpwYWwgPC0gdmlyaWRpczo6dmlyaWRpcygxMDAsIGVuZCA9IDAuOTUpCgpvcmlwYXIgPC0gcGFyKG5vLnJlYWRvbmx5ID0gVFJVRSkKcGFyKG1mcm93ID0gYygxLDMpKQpmb3IgKGxpbi54IGluIDE6bmNvbChteS5wc2V1ZG8pKSB7CiAgY29sb3JzIDwtIHBhbFtjdXQobXkucHNldWRvWyxsaW4ueF0sIGJyZWFrcyA9IDEwMCldCiAgcGxvdChzb2JqQHJlZHVjdGlvbnMkcGNhLjMwX3VtYXBAY2VsbC5lbWJlZGRpbmdzLCBjb2wgPSBjb2xvcnMsIAogICAgICAgcGNoID0gMTYsIGNleCA9IDAuNSwgbWFpbiA9IGNvbG5hbWVzKG15LnBzZXVkbylbbGluLnhdKQogIGxpbmVzKHNsaW5nc2hvdDo6YXMuU2xpbmdzaG90RGF0YVNldChzbGluZy5yZXMpKQp9CnBhcihvcmlwYXIpCmBgYAoKKipOT1RFKiogOiBTbGluZ3Nob3QgaXMgYWxzbyBhdmFpbGFibGUgdGhyb3VnaCB0aGUgW2R5bnZlcnNlXShodHRwczovL2R5bnZlcnNlLm9yZy9yZWZlcmVuY2UvZHlubWV0aG9kcy9tZXRob2QvdGlfc2xpbmdzaG90LykgIQoKIyBMaW1pdHMKCiAgKiBObyBvbmUgbWV0aG9kIHRvIHJ1bGUgdGhlbSBhbGwgKHNvbWUgYXJlIG9yIG1vcmUgZWZmaWNpZW50IGZvciBzb21lIHNwZWNpZmljIHR5cGVzIG9mIHRvcG9sb2d5LCB0ZXJyaWJsZSB3aXRoIG90aGVycykKICAqIERpbWVuc2lvbiByZWR1Y3Rpb24gOiBpbiB0aGUgbmFtZSBvZiBpbnRlcnByZXRhdGlvbi92aXN1YWxpemF0aW9uICooYW5kIENQVSB0aW1lKSosIGJ1dCAqaXMgaXQgcmVsZXZhbnQqID8KICAgICogW0Nvb2xleSBldCBhbC5dKGh0dHBzOi8vd3d3LmJpb3J4aXYub3JnL2NvbnRlbnQvMTAuMTEwMS82ODk4NTF2NikgZGVwb3NpdGVkIGEgcGFwZXIgaW4gMjAxOSBvbiBiaW9yWGl2IChzbGlnaHQgd2FybmluZyA6IHRodXMsIG5vbi1wZWVyLXJldmlld2VkIHlldCksIHVwZGF0ZWQgdmVyeSByZWNlbnRseSwgc3RhdGluZyB0aGF0IHRyYWplY3RvcnkgaW5mZXJlbmNlIGJhc2VkIG9uIHJlZHVjZWQgc3BhY2VzICoqZmFpbCBpbiBldmVyeSBjYXNlKiogIQogICAgKiBUaGUgb25seSB3b3JraW5nIGNhc2UgYWNjb3JkaW5nIHRvIHRoZW0gOiB1c2luZyBQQ0Egd2l0aCAuLi4gKiphbG1vc3QgYWxsIGRpbWVuc2lvbnMqKiA/IQogICAgICAqICpOb3QgYSByZWR1Y2VkIHNwYWNlIGFueW1vcmUgPyoKICAgICogV2h5IDogYWxsIHRlc3RlZCByZWR1Y3Rpb24gbWV0aG9kcyBzYWNyaWZpY2UgdG9vIG11Y2ggaW5mb3JtYXRpb24gdG8gYmUgYWJsZSB0byByZXByZXNlbnQgdGhlIHRydWUgdG9wb2xvZ3kgcmlnaHQgd2l0aG91dCBicmVha2FnZSwganVzdCBzbyB0aGF0IHdlIGNhbiB2aXN1YWxpemUgaXQgd2l0aCBvdXIgW3Bvb3IgaHVtYW4gYnJhaW5zXShodHRwczovL3lvdXR1LmJlL010T2JybmFvdnJJKSAuLi4KICAgICogU28sIGhvdyB0byBkZWFsIHdpdGggaXQgPwogICAgICAqIEFzIGEgTWF0aHMgdGVhY2hlciB0b2xkIG1lLCAqKioiTWF0aGVtYXRpY3MgYXJlIGFuIGFydCBvZiByZWFzb25pbmcgcmlnaHQgb24gd3JvbmcgaW1hZ2VzIioqKi4gSG93ZXZlciwgaW4gb3VyIGNhc2Ugd2Ugd291bGQgY2FsbCBpdCBvYnNlcnZlciBiaWFzIQogICAgICAqIEEgc29sdXRpb24gY291bGQgYmUgdG8gZ2VuZXJhdGUgcHNldWRvdGltZSB2YWx1ZXMgaW4gYSBoaWdoLWRpbWVuc2lvbiBzcGFjZSAobGlrZSB0aGUgcmVjb21tZW5kZWQgImFsbW9zdC1mdWxsJyBQQ0EpLCAqd2l0aG91dCB2aXN1YWxpemF0aW9uKi4KICAgICAgICAqIERyYXdiYWNrIDogaG93IHRvIGV2YWx1YXRlLCBob3cgdG8gaW50ZXJwcmV0IHN1Y2ggcmVzdWx0cyA/IFRoZSBxdWVzdGlvbiB3aWxsIHJlbWFpbiBvcGVuIGZvciB0b2RheS4KCgo8Y2VudGVyPiFbbG9nb10oL3NoYXJlZC9wcm9qZWN0cy9zaW5jZWxsdGVfMjAyMi9gciB1c2VyLmlkYC9Db3Vyc2VzL1NlY29uZGFyeV9hbmFseXNpcy90cmFqZWN0b3J5X2FuYWx5c2lzL3RyYWplY3Rvcnlfcm1kX3Jlc291cmNlcy9hYnN0cmFjdGlvbi5wbmcpPC9jZW50ZXI+Cjxicj4KCiMgTGlua3MKCiogUHVibGljYXRpb25zIDoKICAqIGR5bnZlcnNlIFtcQG5hdHVyZSBiaW90ZWNoXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU4Ny0wMTktMDA3MS05KSBbXEBiaW9yeGl2XShodHRwczovL3d3dy5iaW9yeGl2Lm9yZy9jb250ZW50L2Jpb3J4aXYvZWFybHkvMjAxOC8wMy8wNS8yNzY5MDcuZnVsbC5wZGYpCiAgKiBbVEluR2FdKGh0dHBzOi8vYWNhZGVtaWMub3VwLmNvbS9iaW9pbmZvcm1hdGljcy9hcnRpY2xlLzM2L1N1cHBsZW1lbnRfMS9pNjYvNTg3MDUxNykKICAqIFtTbGluZ3Nob3RdKGh0dHBzOi8vYm1jZ2Vub21pY3MuYmlvbWVkY2VudHJhbC5jb20vdHJhY2svcGRmLzEwLjExODYvczEyODY0LTAxOC00NzcyLTAucGRmKQogICogQW4gaW50ZXJlc3RpbmcgW2NvbW1lbnQvcmV2aWV3XShodHRwczovL3ByZWxpZ2h0cy5iaW9sb2dpc3RzLmNvbS9oaWdobGlnaHRzL2Etbm92ZWwtbWV0cmljLXJldmVhbHMtcHJldmlvdXNseS11bnJlY29nbml6ZWQtZGlzdG9ydGlvbi1pbi1kaW1lbnNpb25hbGl0eS1yZWR1Y3Rpb24tb2Ytc2NybmEtc2VxLWRhdGEvKSBvZiB0aGUgcmVjZW50IENvb2xleSBwYXBlciBhYm91dCB0aGUgZmFpbHVyZSBvZiBkaW1lbnNpb24gcmVkdWN0aW9uIGluIFRJLgoqIEEgaGl0Y2hoaWtlcidzIGd1aWRlIHRvIHRoZSBbZHludmVyc2VdKGh0dHBzOi8vZHludmVyc2Uub3JnL3VzZXJzLykgKihET04nVCBQQU5JQyEpKgoqIEEgcXVpY2sgW1NsaW5nc2hvdCB0dXRvcmlhbF0oaHR0cHM6Ly9idXN0b29scy5naXRodWIuaW8vQlVTX25vdGVib29rc19SL3NsaW5nc2hvdC5odG1sI3NsaW5nc2hvdCkgYnkgdGhlIHRlYW0gYmVoaW5kIEthbGxpc3RvL0JVU3Rvb2xzCiogUmVjZW50IHRvb2xzIHRvIGtlZXAgYW4gZXllIG9uIDoKICAqIFtWSUFdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNDY3LTAyMS0yNTc3My0zKSAocHl0aG9uKQogICogW0xJU0EyXShodHRwczovL3d3dy5mcm9udGllcnNpbi5vcmcvYXJ0aWNsZXMvMTAuMzM4OS9mZ2VuZS4yMDIxLjY4MTIwNi9mdWxsKSAoUikKCiMgUiBzZXNzaW9uCgpgYGB7ciBzZXNzaW9uaW5mbywgZWNobyA9IEZBTFNFfQpzZXNzaW9uSW5mbygpCmBgYAo=