EB3I n1 2025 scRNAseq
-
PROCESSING (II)
-
Dimension reduction & visualization





1 PREAMBLE

1.1 Purpose of this session

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

  • Dimension reduction of the expression data

  • Visualization of cells expression in a 2-D space

  • Unsupervised clustering of cells

  • Description of the defined clusters



2 Start Rstudio

3 Warm-up

  • We set common parameters we will use throughout this session :
# setparam


## Set your project name
# WARNING : Do not just copy-paste this ! It's MY project name ! Put YOURS !!
project_name <- "ebaii_sc_teachers"


## Control if the project_name exists on the cluster
cat('PATH CHECK : ', dir.exists(paste0('/shared/projects/', project_name)))
Show output
PATH CHECK :  TRUE
## Seed for the RNG
my_seed <- 1337L

## Known marker genes for TD3A
td3a_markers <- c("Apoe", "Birc5", "Plac8", "Itm2a", "Ptcra", "Trbv29", "Isg15", "Cldn10")


4 Prepare the data structure

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

4.1 Main directory

#maindir

## 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

# sessiondir

## Creating the session (Preproc.2) directory
session_dir <- paste0(TD_dir, "/05_Proc.2")
dir.create(path = session_dir, recursive = TRUE)

## Print the session directory on-screen
print(session_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/05_Proc.2"

4.3 Input directory

#indir

## 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/05_Proc.2/DATA"

4.4 Output directory

#outdir

## 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/05_Proc.2/RESULTS"


5 Reload the Seurat Object

  • We can reload the object we saved at the former step
##  dataload


## This is the path to the current EB3I backup
sessionid <- '2538_eb3i_n1_2025'


## The latest Seurat object saved as RDS (name)
sobj_file <- "08_TD3A_S5_Scaled.2k_Reg.PCrb_12508.4035.RDS"

## The latest Seurat object saved as RDS (full path)
sobj_path <- paste0(TD_dir, 
                    "/04_Proc.1/RESULTS/",
                    sobj_file)

force <- FALSE  ## To force a re-download of a Zenodo-hosted backup
local <- FALSE  ## To force a loading from a local backup

## In case of error/lost data : force a reload from a Zenodo backup repository
if(force) {
  zen_id <- "14035293"
  zen_backup_file <- paste0("https://zenodo.org/records/",
                            zen_id,
                            "/files/",
                            sobj_file)
  ## Recreate the expected path if it does not exist
  dir.create(path = dirname(sobj_path), recursive = TRUE)
  ## Download the file
  download.file(url = zen_backup_file,
                destfile = sobj_path)
}

## In case of error/lost data : force a reload from a local backup repository
if(local) {
  sobj_path <- paste0(
    "/shared/projects/", sessionid, "/atelier_scrnaseq/TD/BACKUP/RDS/",
    sobj_file)
}

## Load the object
sobj <- readRDS(file = sobj_path)


6 Dimension reduction

This step originates from the observation that we do not want nor need to characterize each of our thousends of cells, but groups of them (clusters ? cell types ? other ?). Thus, we do no need all data, and even may benefit from such a reduction :

  • Reduce the data complexity

    • For interpretation

    • For computations

  • Increase the quality of information contained in the data

    • Enriching “good” biological signals

    • Discarding noise / cell-specific signals

There is a multitude of methods for dimension reduction

6.1 Principal Component Analysis (PCA)

Here, we will use the grand-mother of all : the PCA (Principal Component Analysis)

# h_runPCA

?Seurat::RunPCA()

Questions : ⭍⭍ Lightning quizz ⭍⭍ :

# q_pca1

How many principal components (PC) will be generated by default ?


# a_pca1

## . The answer is 50 (npcs parameter)
##
## . We will use this default value.
##
## . Warning : in some (rare) contexts, this
##   may not be enough !


# q_pca2

Which data type (ie, which Seurat object layer) will be used 
to generate the components ?


# a_pca2

## . Data from the scale.data layer will be used
##
## . This is unfortunately not really explicit
##   from the Seurat::RunPCA help page ! (see the "features"
##   part)
##
## . Web search : "which slot is used by RunPCA"
##"
## . When run on a Seurat object without @scale.data layer :
##   "Error in GetAssayData(object, assay.type = assay.type, slot = "scale.data") : 
##   Object@scale.data has not been set. Run ScaleData() and then retry."


Perform PCA on our data

# pca

## Note : a seed is used here !
sobj <- Seurat::RunPCA(
  object = sobj, 
  assay = 'RNA', 
  seed.use = my_seed, 
  verbose = FALSE)


Description :

# PCAdesc

## Please, focus on the "reductions" slot
View(sobj)


Visualization of the very first two components, with cells coloring according to the estimated cell cycle phase :

# PCAplot

## Scatter plot along dimensions
Seurat::DimPlot(
  object = sobj, 
  ## First two components
  dims = c(1,2), 
  ## Color dots per cell phase groups
  group.by = 'CC_Seurat_Phase', 
  ## Data to use
  reduction = 'pca')
Show plot


6.2 Questions

# q_pca3

Give us your interpretation / feelings from this plot !


# q_pca4

Should we limit ourselves to using 2 dimensions to interpret our data ?




  • Maybe we shoud reduce information a tad more, just for the sake of …

    • … understanding our data …

    • …with our poor human brains

    • … born and raised in a 3D euclidean world !

##
##     __              .___/\                .__  __  .__                __               .__    .___ ._.
##    / /   ______     |   )/_____   __  _  _|__|/  |_|  |__     _______/  |_ __ ________ |__| __| _/ | |
##   / /   /_____/     |   |/     \  \ \/ \/ /  \   __\  |  \   /  ___/\   __\  |  \____ \|  |/ __ |  | |
##   \ \   /_____/     |   |  Y Y  \  \     /|  ||  | |   Y  \  \___  \ |  | |  |  /  |_> >  / /_/ |   \|
##    \_\              |___|__|_|  /   \/\_/ |__||__| |___|  / /____  / |__| |____/|   __/|__\____ |   __
##                               \/                        \/       \/             |__|           \/   \/
##


7 Visualization

This final processing step need to finally observe our data requires a novel dimension reduction method with a very high challenge to overcome : reduce a space of dozens of dimensions to just a few !

We will use the UMAP method.

7.1 Uniform Manifold Approximation and Projection (UMAP)

How ?

# humap

?Seurat::RunUMAP()

7.1.1 Select dimensions

We generated 50 PCA components from our ~12 K features

  • These 50 dimensions may not all contain valuable information

  • We should try do select the most useful ones and discard the remaining noise

  • But how many should we keep ?

  • Question :

    # q_ndim1
    
    Do you have an idea about this number ?


    # r_ndim1
    
    ## . Impossible to guess with our current knowledge.
    ##
    ## . But we can get some help from the PCA data itself
    ##
    ## . If you said a value above the 50 components we
    ##   generated for our PCA, you should wear the
    ##   cone of shame !


There are several methods to help us choose.

Several, but none perfect.

We will use a very simple, graphical method : the observation of the amount of global variance explained by each component, through the elbow-plot.



# h_elbow

?Seurat::ElbowPlot()


Apply on our data :

# elbow

## Perform the "elbow plot"
Seurat::ElbowPlot(
  object = sobj, 
  reduction = 'pca',
  ndims = 50)
Show plot


Question :

# q_ndim2

Any more precise idea, now ?


# r_ndim2

## . The contribution to the variance (sd²) seems
##   greatly reduced after 30 PCs.
##
## . Maybe something between ~15 and ~30 should do
##   the trick ?


7.1.2 Assess dimensions

To demonstrate the effect of the number of PC dimensions used as input to the UMAP generation, we will perform a comparison using 6 different amounts of retained PCs :


TeAmWoRk TiMe !


We will dispatch the assessment of each amount of dimensions to groups of trainees.

# dim_sel

## PCA max dimensions to evaluate
pca_dims <- c(3, 7, 17, 25, 30, 40)

## Define a function to compute the UMAP
pca_dim_eval <- function(object = NULL, dim.max = 2, my_seed = 1337L) {
  
  message('\nRunning UMAP with ', dim.max, ' dimensions ...\n')
  
  ## RunUMAP
  object <- Seurat::RunUMAP(
    object = object, assay = "RNA", 
    reduction = "pca", dims = 1:dim.max, 
    seed.use = my_seed)
  
  ## Plot
  dpN <- Seurat::DimPlot(
    object = object, 
    reduction = 'umap',
    combine = TRUE) + ggplot2::ggtitle(
      label = paste0("Dim : ", dim.max)) + Seurat::DarkTheme()
  
  ## Clean
  rm(object)
  
  ## Return the plot object
  return(dpN)
}

## Run the function on multiple dimensions, get a list of ggplots
pca_eval_res <- lapply(X = pca_dims,
                       FUN = function(p) {
                         pca_dim_eval(object = sobj, 
                                      dim.max = p,
                                      my_seed = my_seed)
                       })

## Plot the list alltogether
patchwork::wrap_plots(pca_eval_res, nrow = 2)
Show plot


Question

# q_ndim3

Any more precise idea, now, FOR REAL ?


# r_ndim3

## . A limited amount of PCs is not able to capture
##   enough information (variation) to build a sufficiently 
##   defined topology.
##
## . The differences in the global topology is
##   is quite limited between the higher PCs versions.
##
## . This may imply that the additional
##   components above 20~25 do not add more 
##   information (neither more noise, in this case).


We can now perform the final UMAP with the PC dimensions of your choice.


7.1.3 Create the UMAP

For the next steps of the training, we will use 20 PCA dimensions.

# umap20

## Fixing n_dim
n_dim <- 20

## Using 20 PCs
## A seed is needed here !
sobj <- Seurat::RunUMAP(
    object = sobj, assay = "RNA", 
    reduction = "pca", 
    dims = 1:n_dim, 
    seed.use = my_seed)

## DimPlot
umap2d <- Seurat::DimPlot(
  object = sobj, 
  reduction = 'umap') + Seurat::DarkTheme()
print(umap2d)
Show plot


7.2 Bonus : 3D UMAP (DEMO)

While by default Seurat::RunUMAP will produce 2-dimension reductions, the method can generate further components.

Despite our limited brain, this is sometimes interesting and useful to attempt a reduction to 3 dimensions instead of 2. This can be very effective when looking for trajectories.

We can generate a UMAP with 3 components, from the 20 PCs :

# umap3D20

## UMAP from 20 PCs, 3 components requested
sobj_U3D <- Seurat::RunUMAP(
  object = sobj, assay = 'RNA', 
  graph.name = 'RNA_snn', 
  reduction = 'pca', 
  reduction.name = 'umap3d',
  dims = 1:n_dim, 
  seed.use = my_seed,
  n.components = 3)

We can plot the two first UMAP components from this 3D space :

# plot_umap3D20

## DimPlot of the first 2 UMAP components
umap3d <- Seurat::DimPlot(
  object = sobj_U3D, 
  dims = c(1,2),
  reduction = 'umap3d') + Seurat::DarkTheme()
print(umap3d)
Show plot


Question :

# q_umap3D

Isn't there something striking ?


# a_umap3D

## . The plot is not the same as when
##   using 25 PCs when requesting 3 UMAP
##   components instead of 2 !
##
## . The 2 components of a 2D UMAP are not
##   the same as the two first components
##   of a dim>2 UMAP.


# umapXd

patchwork::wrap_plots(
  list(
    umap2d & ggplot2::ggtitle(label = "UMAP (2D)"),
    umap3d & ggplot2::ggtitle(label = "UMAP (3D)")), 
  nrow = 1)
Show plot


Let’s perform a 3D representation of our UMAP (PRE-RENDERED)

# umap3D20rgl

## 3D plot (not run as failing in interactive mode)
plotly::plot_ly(
  data = as.data.frame(Seurat::Reductions(
    object = sobj_U3D, 
    slot = "umap3d")@cell.embeddings),
  x = ~umap3d_1, 
  y = ~umap3d_2, 
  z = ~umap3d_3, 
  type = 'scatter3d', 
  marker = list(size = 2, width=2))

## Clean
rm(sobj_U3D)






8 Save the Seurat object

We will save our Seurat object that now contains PCA and UMAP reductions :

# saverds1

## Save our Seurat object (rich naming)
out_name <- paste0(
          output_dir, "/", paste(
            c("09", Seurat::Project(sobj), "S5", 
              "DimRed.PCA", paste(
                dim(sobj), 
                collapse = '.'
              )
            ), collapse = "_"),
            ".RDS")

## Check
print(out_name)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/05_Proc.2/RESULTS/09_TD3A_S5_DimRed.PCA_12508.4035.RDS"
## Write on disk
saveRDS(object = sobj, 
        file = out_name)


9 Clustering

We can now attempt to determine how cells are organized in an unsupervised manner in this space

We will use the graph-based clustering method Louvain

Clustering will be performed on the PCA dimension reduction, NOT on the UMAP one !

# q_clust_on_PCA

Any idea why ?
# a_clust_on_PCA

## . UMAP is a strong APPROXIMATION of the multidimensional space
##   which single purpose is to HELP us understand more easily the
##   hidden structure of our dataset.
##
## . The PCA component (or any reduction method used directly from our
##   normalized/scaled data, like MDS or ICA, ...) contains the "real" 
##   information, just distributed differently.
##
## . If one uses the "second reduced space" used for visualization (UMAP,
##   tSNE, DM, ...) as the basis for clustering, one may obtain pretty plots
##   with well-defined clusters. The latter being based on wrong data, thus
##   leading to wrong interpretation :(


9.1 Find neighbors

Before running the Louvain method, a first pass method is used to generate a “K-Nearest Neighbour” graph (see more details here).

# fnn20

## Compute a SNN using the first 20 PCs
sobj <- Seurat::FindNeighbors(
  object = sobj, 
  dims = 1:20, 
  reduction = "pca")

9.2 Louvain clustering

  • We will test multiple different resolutions

  • The Seurat function to perform clustering can be called with multiple resolutions at once (less to code, lucky us !).


TeAmWoRk TiMe !


We wil dispatch the different clustering resolution values to groups of trainees.

# clustL20

## Louvain resolutions to test
resol <- c(.3, .6, .9, 1.2, 1.5, 1.8)

## Clustering
sobj <- Seurat::FindClusters(
  object = sobj, 
  resolution = resol,
  verbose = FALSE)


Question

# q_clustdesc

Could you tell us what changed in our object ?


# a_clustdesc

## Please, focus on the [meta.data] slot
View(sobj)

## . New entries in the barcodes metadata :
##   . The results of our clusterings
##   . 'seurat_clusters' which corresponds to the 
##     last version we ran
##
## . This 'seurat_clusters' annotation will be the
##   one used by default by Seurat for any further
##   analysis


9.3 Assess resolutions

9.3.1 On UMAPs

9.3.1.1 Clusters

Plotting UMAPs harboring the clustering results for our tested resolutions

# clust_dimplot

## Metadata name of clustering results (defined by default by Seurat)
resol_names <- paste0("RNA_snn_res.", resol)

## DimPlot
Seurat::DimPlot(
  object = sobj, 
  reduction = "umap", 
  group.by = resol_names,
  label = TRUE, 
  repel = TRUE)
Show plot


# q_compres

. Could you briefly compare the different versions ?

. Would you consider that some results are under/over-clustered ?

. Are all clusters well-defined ?



  • Something that might help (a bit) : splitting the clustering results :

    # clust_dimplot_split
    
    ## Looping on resolutions
    for (my_grp in resol_names) {
      ## DimPlot
      p <- Seurat::DimPlot(
        object = sobj, 
        reduction = "umap", 
        group.by = my_grp,
        split.by = my_grp,
        label = TRUE, 
        repel = TRUE)
      print(p)
    }

    Show plot

    Show plot

    Show plot

    Show plot

    Show plot

    Show plot


  • Something other : assessing clusters stability across resolutions, thanks to clustree (DEMO) :

    # clustree_demo
    
    ## Full loading of clustree is needed here...
    library(clustree)
    
    ## Running clustree
    clustree::clustree(x = sobj, prefix = 'RNA_snn_res.')

    Show plot

    ## Unloading the clustree package
    detach('package:clustree', unload = TRUE)

9.3.1.2 Cell-type markers

We can plot the cell markers we already know

# u_markers

## Multiple FeaturePlots with markers
Seurat::FeaturePlot(object = sobj, 
                    features = td3a_markers) + 
  patchwork::plot_layout(nrow = 3, 
                         ncol = 3)
Show plot


Just for “fun” : what would have been seen on a PCA ? (DEMO)

# u_markers_pca

## Multiple FeaturePlots with markers
Seurat::FeaturePlot(object = sobj, 
                    features = td3a_markers, 
                    reduction = "pca") + 
  patchwork::plot_layout(nrow = 3, 
                         ncol = 3)
Show plot


9.3.2 Cluster-specific markers

A practical way to characterize our clustering results is to get back to a level of knowledge you are confident in : marker genes.

Seurat has a handy function to :

  • Identify differential expressed genes specific to each and every provided category of cells (here, clustering results)

  • Draw a clusterized, annotated heatmap of these genes

# h_fam

?Seurat::FindAllMarkers
# dhm_prep

## Looping on clustering results
fam_all <- lapply(resol_names, function(r) {
  
  ## Find markers for all clusters
  ## A seed is needed here !
  Seurat::Idents(object = sobj) <- sobj[[r]][[1]]
  fam <- Seurat::FindAllMarkers(
    object = sobj, 
    logfc.threshold = .5, 
    only.pos = TRUE, 
    min.pct = .5, 
    verbose = FALSE,
    random.seed = my_seed)
  
  ## Get top gene per cluster
  famtop <- fam$gene[!duplicated(fam$cluster)]
  vln_pl <- Seurat::VlnPlot(object = sobj, features = famtop)
  print(vln_pl)
  
  ## Select top10 genes when available
  fam_rdx <- dplyr::group_by(.data = fam, cluster)
  fam_rdx <- dplyr::filter(.data = fam_rdx, avg_log2FC > 1)
  fam_rdx <- dplyr::slice_head(.data = fam_rdx, n = 10)
  dh <- Seurat::DoHeatmap(
    object = sobj, features = fam_rdx$gene, size = 3,
    combine = TRUE) + ggplot2::ggtitle(label = r)
  return(dh)
})
Show plot

Show plot

Show plot

Show plot

Show plot

Show plot

# dhm_plot

## Plot all heatmaps at once
patchwork::wrap_plots(fam_all) + patchwork::plot_layout(nrow = 2)
Show plot


Questions : Comparing the heatmaps :

# q_hm1

Which resolution would you choose, and why ?


# q_hm2

Is there a single one and only answer to the former question ?


9.3.3 Clusters contingencies and proportions

One can observe how many cells are in each cluster, and what proportion of all cells these represent (DEMO)

# clustpop

## Looping on resolutions
for (x in resol_names) {
  ## Contingencies
  print(table(sobj[[x]]))
  ## Proportions
  print(format(table(sobj[[x]]) / ncol(sobj), digits = 2))
  cat('\n\n')
}
Show output
RNA_snn_res.0.3
   0    1    2    3    4    5    6    7 
1989  873  418  204  171  159  118  103 
RNA_snn_res.0.3
      0       1       2       3       4       5       6       7 
"0.493" "0.216" "0.104" "0.051" "0.042" "0.039" "0.029" "0.026" 


RNA_snn_res.0.6
  0   1   2   3   4   5   6   7   8   9 
907 779 674 481 421 204 189 159 118 103 
RNA_snn_res.0.6
      0       1       2       3       4       5       6       7       8       9 
"0.225" "0.193" "0.167" "0.119" "0.104" "0.051" "0.047" "0.039" "0.029" "0.026" 


RNA_snn_res.0.9
  0   1   2   3   4   5   6   7   8   9  10 
889 761 683 482 381 216 203 159 119 104  38 
RNA_snn_res.0.9
       0        1        2        3        4        5        6        7 
"0.2203" "0.1886" "0.1693" "0.1195" "0.0944" "0.0535" "0.0503" "0.0394" 
       8        9       10 
"0.0295" "0.0258" "0.0094" 


RNA_snn_res.1.2
  0   1   2   3   4   5   6   7   8   9  10  11  12 
587 508 483 459 399 395 331 253 203 157 117 105  38 
RNA_snn_res.1.2
       0        1        2        3        4        5        6        7 
"0.1455" "0.1259" "0.1197" "0.1138" "0.0989" "0.0979" "0.0820" "0.0627" 
       8        9       10       11       12 
"0.0503" "0.0389" "0.0290" "0.0260" "0.0094" 


RNA_snn_res.1.5
  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14 
455 455 414 372 371 337 301 257 241 210 201 159 119 104  39 
RNA_snn_res.1.5
       0        1        2        3        4        5        6        7 
"0.1128" "0.1128" "0.1026" "0.0922" "0.0919" "0.0835" "0.0746" "0.0637" 
       8        9       10       11       12       13       14 
"0.0597" "0.0520" "0.0498" "0.0394" "0.0295" "0.0258" "0.0097" 


RNA_snn_res.1.8
  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18 
457 347 329 322 281 258 240 232 218 195 193 171 171 170 155 119 105  38  34 
RNA_snn_res.1.8
       0        1        2        3        4        5        6        7 
"0.1133" "0.0860" "0.0815" "0.0798" "0.0696" "0.0639" "0.0595" "0.0575" 
       8        9       10       11       12       13       14       15 
"0.0540" "0.0483" "0.0478" "0.0424" "0.0424" "0.0421" "0.0384" "0.0295" 
      16       17       18 
"0.0260" "0.0094" "0.0084" 


9.4 Selection

For the downstream analyses, we will use the resolution 0.8.

We will fix the clustering results for this resolution as the default one Seurat will use for any further analyses / plots, using the Seurat::Idents() function.

# sel_res

## Fixing l_res
l_res <- .8

## Performing Louvain clustering at the selected resolution
sobj <- Seurat::FindClusters(
  object = sobj, 
  resolution = l_res,
  verbose = FALSE)

## Check on default "Idents"
identical(
  x = SeuratObject::FetchData(
    object = sobj, 
    vars = paste0("RNA_snn_res.", l_res))[[1]],
  y = unname(Seurat::Idents(object = sobj))
)
Show output
[1] TRUE
## DimPlot without specifying the resolution
Seurat::DimPlot(
  object = sobj, 
  reduction = "umap", 
  label = TRUE, 
  repel = TRUE)
Show plot





10 Save the Seurat object

We will save our Seurat object that now contains our clustering results :

# saverds2

## Save our Seurat object (rich naming)
out_name <- paste0(
          output_dir, "/", paste(
            c("10", Seurat::Project(sobj), "S5", 
              paste0(
                "Clustered.",
                l_res), 
              paste(
                dim(sobj), 
                collapse = '.'
              )
            ), collapse = "_"),
            ".RDS")

## Check
print(out_name)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/05_Proc.2/RESULTS/10_TD3A_S5_Clustered.0.8_12508.4035.RDS"
## Write on disk
saveRDS(object = sobj, 
        file = out_name)







11 Rsession

# 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] ggraph_2.2.1  ggplot2_3.5.2 future_1.49.0

loaded via a namespace (and not attached):
  [1] RColorBrewer_1.1-3     rstudioapi_0.17.1      jsonlite_2.0.0        
  [4] magrittr_2.0.3         ggbeeswarm_0.7.2       spatstat.utils_3.1-4  
  [7] farver_2.1.2           rmarkdown_2.29         vctrs_0.6.5           
 [10] ROCR_1.0-11            memoise_2.0.1          spatstat.explore_3.4-3
 [13] htmltools_0.5.8.1      sass_0.4.10            sctransform_0.4.2     
 [16] parallelly_1.45.0      KernSmooth_2.23-24     bslib_0.9.0           
 [19] htmlwidgets_1.6.4      ica_1.0-3              plyr_1.8.9            
 [22] plotly_4.10.4          zoo_1.8-14             cachem_1.1.0          
 [25] igraph_2.1.4           mime_0.13              lifecycle_1.0.4       
 [28] pkgconfig_2.0.3        Matrix_1.7-3           R6_2.6.1              
 [31] fastmap_1.2.0          fitdistrplus_1.2-2     shiny_1.10.0          
 [34] digest_0.6.37          patchwork_1.3.0        Seurat_5.3.0          
 [37] tensor_1.5             RSpectra_0.16-2        irlba_2.3.5.1         
 [40] labeling_0.4.3         progressr_0.15.1       spatstat.sparse_3.1-0 
 [43] httr_1.4.7             polyclip_1.10-7        abind_1.4-8           
 [46] compiler_4.4.1         withr_3.0.2            backports_1.5.0       
 [49] viridis_0.6.5          fastDummies_1.7.5      ggforce_0.4.2         
 [52] MASS_7.3-65            tools_4.4.1            vipor_0.4.7           
 [55] lmtest_0.9-40          beeswarm_0.4.0         httpuv_1.6.15         
 [58] future.apply_1.11.3    goftest_1.2-3          glue_1.8.0            
 [61] nlme_3.1-165           promises_1.3.2         grid_4.4.1            
 [64] checkmate_2.3.2        Rtsne_0.17             cluster_2.1.6         
 [67] reshape2_1.4.4         generics_0.1.4         gtable_0.3.6          
 [70] spatstat.data_3.1-6    rmdformats_1.0.4       tidyr_1.3.1           
 [73] data.table_1.17.4      tidygraph_1.3.1        sp_2.2-0              
 [76] spatstat.geom_3.4-1    RcppAnnoy_0.0.22       ggrepel_0.9.6         
 [79] RANN_2.6.2             pillar_1.10.2          stringr_1.5.1         
 [82] limma_3.60.6           spam_2.11-1            RcppHNSW_0.6.0        
 [85] later_1.4.2            splines_4.4.1          dplyr_1.1.4           
 [88] tweenr_2.0.3           lattice_0.22-6         survival_3.7-0        
 [91] deldir_2.0-4           tidyselect_1.2.1       miniUI_0.1.2          
 [94] pbapply_1.7-2          knitr_1.50             gridExtra_2.3         
 [97] bookdown_0.39          scattermore_1.2        xfun_0.52             
[100] graphlayouts_1.1.1     statmod_1.5.0          matrixStats_1.5.0     
[103] stringi_1.8.7          lazyeval_0.2.2         yaml_2.3.10           
[106] evaluate_1.0.3         codetools_0.2-20       tibble_3.2.1          
[109] cli_3.6.5              uwot_0.2.3             xtable_1.8-4          
[112] reticulate_1.42.0      jquerylib_0.1.4        dichromat_2.0-0.1     
[115] Rcpp_1.0.14            globals_0.18.0         spatstat.random_3.4-1 
[118] png_0.1-8              ggrastr_1.0.2          spatstat.univar_3.1-3 
[121] parallel_4.4.1         presto_1.0.0           dotCall64_1.2         
[124] listenv_0.9.1          viridisLite_0.4.2      scales_1.4.0          
[127] ggridges_0.5.6         SeuratObject_5.1.0     purrr_1.0.4           
[130] rlang_1.1.6            cowplot_1.1.3         
LS0tCnRpdGxlOiAiPENFTlRFUj5FQjNJIG4xIDIwMjUgc2NSTkFzZXE8QlI+LTxCUj48Qj5QUk9DRVNTSU5HIChJSSk8L0I+PEJSPi08QlI+RGltZW5zaW9uIHJlZHVjdGlvbiAmIHZpc3VhbGl6YXRpb248L0NFTlRFUj4iCmRhdGU6ICIyMDI1LTE2LTIxLjIyIgphdXRob3I6CiAgLSBuYW1lOiAiRUIzSSBuMSBzY1JOQXNlcSBUZWFtIgogIC0gbmFtZTogIkJhc3RpZW4gSk9CIgogICAgZW1haWw6ICJiYXN0aWVuLmpvYkBndXN0YXZlcm91c3N5LmZyIgogIC0gbmFtZTogIkxpbGlhIFlPVU5TSSIKICAgIGVtYWlsOiAibGlsaWEueW91bnNpQGluc2VybS5mciIgCm91dHB1dDoKICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoKICAgIGZpZ193aWR0aDogOAogICAgZmlnX2hlaWdodDogNgogICAgaGlnaGxpZ2h0OiB0YW5nbyAgIyMgVGhlbWUgZm9yIHRoZSBjb2RlIGNodW5rcwogICAgZW1iZWRfZm9udHM6IFRSVUUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgIyMgQWRkcyBudW1iZXIgdG8gaGVhZGVycyAoc2VjdGlvbnMpCiAgICB0aGVtZTogZmxhdGx5ICAjIyBDU1MgdGhlbWUgZm9yIHRoZSBIVE1MIHBhZ2UKICAgIGNvbGxhcHNlZDogdHJ1ZSAgIyMgQnkgZGVmYXVsdCwgdGhlIFRPQyBpcyBmb2xkZWQKICAgIHRvY19kZXB0aDogMwogICAgc21vb3RoX3Njcm9sbDogdHJ1ZSAjIyBTbW9vdGggc2Nyb2xsIG9mIHRoZSBIVE1MIHBhZ2UKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlICMjIEluY2x1ZGVzIGFsbCBwbG90cy9pbWFnZXMgd2l0aGluIHRoZSBIVE1MCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICMjIEFkZHMgYSBidXR0b24gdG8gZG93bmxvYWQgdGhlIFJtZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aHVtYm5haWxzOiBmYWxzZQogICAgbGlnaHRib3g6IHRydWUKICAgIGZpZ19jYXB0aW9uOiBmYWxzZQogICAgZ2FsbGVyeTogdHJ1ZQogICAgdXNlX2Jvb2tkb3duOiB0cnVlCmFsd2F5c19hbGxvd19odG1sOiB0cnVlICMjIEFsbG93IHBsYWluIEhUTUwgY29kZSBpbiB0aGUgUm1kCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCjwhLS0ga25pdCBzZXR1cCAtLT4KCmBgYHtyIGtuaXRfc2V0dXAsIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLCAgICAgICAgIyBQcmludCB0aGUgY29kZQogIGV2YWwgPSBUUlVFLCAgICAgICAgIyBSdW4gY29tbWFuZCBsaW5lcwogIG1lc3NhZ2UgPSBGQUxTRSwgICAgIyBQcmludCBtZXNzYWdlcwogIHByb21wdCA9IEZBTFNFLCAgICAgIyBEbyBub3QgZGlzcGxheSBwcm9tcHQKICBjb21tZW50ID0gTkEsICAgICAgICMgTm8gY29tbWVudHMgb24gdGhpcyBzZWN0aW9uCiAgd2FybmluZyA9IEZBTFNFLCAgICAjIERpc3BsYXkgd2FybmluZ3MKICB0aWR5ID0gRkFMU0UsCiAgZmlnLmFsaWduPSJjZW50ZXIiLCAKICAjIHJlc3VsdHMgPSAnaGlkZScsCiAgd2lkdGggPSAxMDAgICAgICAgIyBOdW1iZXIgb2YgY2hhcmFjdGVycyBwZXIgbGluZQopCmBgYAoKPCEtLSBDU1MgdG8gY29sb3IgY2h1bmtzIGFuZCBvdXRwdXRzIC0tPgoKYGBge2NzcywgZWNobz1GQUxTRX0KLm5vdHJ1biB7CiAgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmV5ICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgYmxhY2sgIWltcG9ydGFudDsKfQoubm90cnVubyB7CiAgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmV5ICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9Ci5xdWVzdGlvbiB7CiAgYmFja2dyb3VuZC1jb2xvcjogYXF1YW1hcmluZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBsaW1lZ3JlZW4gIWltcG9ydGFudDsKfQoucXVlc3Rpb25vIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBhcXVhbWFyaW5lICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9Ci5hbnN3ZXIgewogIGJhY2tncm91bmQtY29sb3I6IG5hdmFqb3doaXRlICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgYnJvd24gIWltcG9ydGFudDsKfQouYW5zd2VybyB7CiAgYmFja2dyb3VuZC1jb2xvcjogbmF2YWpvd2hpdGUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLmJleW9uZCB7CiAgYmFja2dyb3VuZC1jb2xvcjogdmlvbGV0ICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgcHVycGxlICFpbXBvcnRhbnQ7Cn0KLmJleW9uZG8gewogIGJhY2tncm91bmQtY29sb3I6IHZpb2xldCAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQpgYGAKCjwhLS0gSG9vayB0byBoYW5kbGUgY29kZSBibG9ja3Mgb3V0cHV0IGZvbGRpbmcgLS0+CgpgYGB7ciBrbml0X2hvb2ssIGVjaG8gPSBGQUxTRX0KaG9va3MgPSBrbml0cjo6a25pdF9ob29rcyRnZXQoKQpob29rX2ZvbGRhYmxlID0gZnVuY3Rpb24odHlwZSkgewogIGZvcmNlKHR5cGUpCiAgZnVuY3Rpb24oeCwgb3B0aW9ucykgewogICAgcmVzID0gaG9va3NbW3R5cGVdXSh4LCBvcHRpb25zKQogICAgCiAgICBpZiAoaXNGQUxTRShvcHRpb25zW1twYXN0ZTAoImZvbGQuIiwgdHlwZSldXSkpIHJldHVybihyZXMpCiAgICAKICAgIHBhc3RlMCgKICAgICAgIjxkZXRhaWxzPjxzdW1tYXJ5PlNob3cgIiwgdHlwZSwgIjwvc3VtbWFyeT5cblxuIiwKICAgICAgcmVzLAogICAgICAiXG5cbjwvZGV0YWlscz4iCiAgICApCiAgfQp9CmtuaXRyOjprbml0X2hvb2tzJHNldCgKICBvdXRwdXQgPSBob29rX2ZvbGRhYmxlKCJvdXRwdXQiKSwKICBwbG90ID0gaG9va19mb2xkYWJsZSgicGxvdCIpCikKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGNlbnRlcj4hW10oZWIzaV9iYW5uZXIucG5nKTwvY2VudGVyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUFJFQU1CTEUKCiMjIFB1cnBvc2Ugb2YgdGhpcyBzZXNzaW9uCgpUaGlzIGZpbGUgZGVzY3JpYmVzIHRoZSBkaWZmZXJlbnQgc3RlcHMgdG8gcGVyZm9ybSAqKmZpZnRoKiogcGFydCBvZiB0aGUgZGF0YSBwcm9jZXNzaW5nIGZvciB0aGUgc2luZ2xlIGNlbGwgUk5Bc2VxIGRhdGEgYW5hbHlzaXMgdHJhaW5pbmcgY291cnNlIGZvciB0aGUgKipFQjNJIG4xIDIwMjUqKiwgY292ZXJpbmcgdGhlc2Ugc3RlcHMgOgoKLSAgICoqRGltZW5zaW9uIHJlZHVjdGlvbioqIG9mIHRoZSBleHByZXNzaW9uIGRhdGEKCi0gICAqKlZpc3VhbGl6YXRpb24qKiBvZiBjZWxscyBleHByZXNzaW9uIGluIGEgMi1EIHNwYWNlCgotICAgVW5zdXBlcnZpc2VkICoqY2x1c3RlcmluZyoqIG9mIGNlbGxzCgotICAgKipEZXNjcmlwdGlvbioqIG9mIHRoZSBkZWZpbmVkIGNsdXN0ZXJzCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTdGFydCBSc3R1ZGlvCgotICAgVXNpbmcgdGhlIFtPcGVuT25EZW1hbmQvUnN0dWRpbyBjaGVhdAogICAgc2hlZXRdKGh0dHBzOi8vbW9vZGxlLmZyYW5jZS1iaW9pbmZvcm1hdGlxdWUuZnIvcGx1Z2luZmlsZS5waHAvMTQ3NS9tb2RfZm9sZGVyL2NvbnRlbnQvMC9Pb0RfUl9Sc3R1ZGlvLmh0bWwpLAogICAgY29ubmVjdCB0byB0aGUgW09wZW5PbkRlbWFuZAogICAgcG9ydGFsXShodHRwczovL29uZGVtYW5kLmNsdXN0ZXIuZnJhbmNlLWJpb2luZm9ybWF0aXF1ZS5mcikgYW5kCiAgICAqKmNyZWF0ZSBhIFJzdHVkaW8gc2Vzc2lvbioqIHdpdGggdGhlIHJpZ2h0IHJlc291cmNlIHJlcXVpcmVtZW50cy4KCiMgV2FybS11cAoKLSAgIFdlIHNldCAqKmNvbW1vbiBwYXJhbWV0ZXJzKiogd2Ugd2lsbCB1c2UgdGhyb3VnaG91dCB0aGlzIHNlc3Npb24gOgoKYGBge3Igc2V0cGFyYW19CiMgc2V0cGFyYW0KCgojIyBTZXQgeW91ciBwcm9qZWN0IG5hbWUKIyBXQVJOSU5HIDogRG8gbm90IGp1c3QgY29weS1wYXN0ZSB0aGlzICEgSXQncyBNWSBwcm9qZWN0IG5hbWUgISBQdXQgWU9VUlMgISEKcHJvamVjdF9uYW1lIDwtICJlYmFpaV9zY190ZWFjaGVycyIKCgojIyBDb250cm9sIGlmIHRoZSBwcm9qZWN0X25hbWUgZXhpc3RzIG9uIHRoZSBjbHVzdGVyCmNhdCgnUEFUSCBDSEVDSyA6ICcsIGRpci5leGlzdHMocGFzdGUwKCcvc2hhcmVkL3Byb2plY3RzLycsIHByb2plY3RfbmFtZSkpKQoKIyMgU2VlZCBmb3IgdGhlIFJORwpteV9zZWVkIDwtIDEzMzdMCgojIyBLbm93biBtYXJrZXIgZ2VuZXMgZm9yIFREM0EKdGQzYV9tYXJrZXJzIDwtIGMoIkFwb2UiLCAiQmlyYzUiLCAiUGxhYzgiLCAiSXRtMmEiLCAiUHRjcmEiLCAiVHJidjI5IiwgIklzZzE1IiwgIkNsZG4xMCIpCgpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFByZXBhcmUgdGhlIGRhdGEgc3RydWN0dXJlCgpXZSB3aWxsIGRvIHRoZSBzYW1lIGFzIGZvciBmb3JtZXIgc3RlcHMsIGp1c3QgY2hhbmdpbmcgdGhlIHNlc3Npb24gbmFtZQo6CgojIyBNYWluIGRpcmVjdG9yeQoKYGBge3IgbWFpbmRpciwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KI21haW5kaXIKCiMjIFByZXBhcmluZyB0aGUgcGF0aApURF9kaXIgPC0gcGFzdGUwKCIvc2hhcmVkL3Byb2plY3RzLyIsIHByb2plY3RfbmFtZSwgIi9TQ19URCIpCgojIyBDcmVhdGluZyB0aGUgcm9vdCBkaXJlY3RvcnkKIyBkaXIuY3JlYXRlKHBhdGggPSBURF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgcm9vdCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KFREX2RpcikKCmBgYAoKIyMgQ3VycmVudCBzZXNzaW9uCgpgYGB7ciBzZXNzaW9uZGlyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIHNlc3Npb25kaXIKCiMjIENyZWF0aW5nIHRoZSBzZXNzaW9uIChQcmVwcm9jLjIpIGRpcmVjdG9yeQpzZXNzaW9uX2RpciA8LSBwYXN0ZTAoVERfZGlyLCAiLzA1X1Byb2MuMiIpCmRpci5jcmVhdGUocGF0aCA9IHNlc3Npb25fZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIHNlc3Npb24gZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChzZXNzaW9uX2RpcikKCmBgYAoKIyMgSW5wdXQgZGlyZWN0b3J5CgpgYGB7ciBpbmRpciwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KI2luZGlyCgojIyBDcmVhdGluZyB0aGUgSU5QVVQgZGF0YSBkaXJlY3RvcnkKaW5wdXRfZGlyIDwtIHBhc3RlMChzZXNzaW9uX2RpciwgIi9EQVRBIikKZGlyLmNyZWF0ZShwYXRoID0gaW5wdXRfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIGlucHV0IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoaW5wdXRfZGlyKQoKYGBgCgojIyBPdXRwdXQgZGlyZWN0b3J5CgpgYGB7ciBvdXRkaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiNvdXRkaXIKCiMjIENyZWF0aW5nIHRoZSBPVVRQVVQgZGF0YSBkaXJlY3RvcnkKb3V0cHV0X2RpciA8LSBwYXN0ZTAoc2Vzc2lvbl9kaXIsICIvUkVTVUxUUyIpCmRpci5jcmVhdGUocGF0aCA9IG91dHB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgb3V0cHV0IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQob3V0cHV0X2RpcikKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUmVsb2FkIHRoZSBTZXVyYXQgT2JqZWN0CgotICAgV2UgY2FuIHJlbG9hZCB0aGUgb2JqZWN0IHdlIHNhdmVkIGF0IHRoZSBmb3JtZXIgc3RlcAoKYGBge3IgZGF0YWxvYWR9CiMjICBkYXRhbG9hZAoKCiMjIFRoaXMgaXMgdGhlIHBhdGggdG8gdGhlIGN1cnJlbnQgRUIzSSBiYWNrdXAKc2Vzc2lvbmlkIDwtICcyNTM4X2ViM2lfbjFfMjAyNScKCgojIyBUaGUgbGF0ZXN0IFNldXJhdCBvYmplY3Qgc2F2ZWQgYXMgUkRTIChuYW1lKQpzb2JqX2ZpbGUgPC0gIjA4X1REM0FfUzVfU2NhbGVkLjJrX1JlZy5QQ3JiXzEyNTA4LjQwMzUuUkRTIgoKIyMgVGhlIGxhdGVzdCBTZXVyYXQgb2JqZWN0IHNhdmVkIGFzIFJEUyAoZnVsbCBwYXRoKQpzb2JqX3BhdGggPC0gcGFzdGUwKFREX2RpciwgCiAgICAgICAgICAgICAgICAgICAgIi8wNF9Qcm9jLjEvUkVTVUxUUy8iLAogICAgICAgICAgICAgICAgICAgIHNvYmpfZmlsZSkKCmZvcmNlIDwtIEZBTFNFICAjIyBUbyBmb3JjZSBhIHJlLWRvd25sb2FkIG9mIGEgWmVub2RvLWhvc3RlZCBiYWNrdXAKbG9jYWwgPC0gRkFMU0UgICMjIFRvIGZvcmNlIGEgbG9hZGluZyBmcm9tIGEgbG9jYWwgYmFja3VwCgojIyBJbiBjYXNlIG9mIGVycm9yL2xvc3QgZGF0YSA6IGZvcmNlIGEgcmVsb2FkIGZyb20gYSBaZW5vZG8gYmFja3VwIHJlcG9zaXRvcnkKaWYoZm9yY2UpIHsKICB6ZW5faWQgPC0gIjE0MDM1MjkzIgogIHplbl9iYWNrdXBfZmlsZSA8LSBwYXN0ZTAoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmRzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ZW5faWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiL2ZpbGVzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqX2ZpbGUpCiAgIyMgUmVjcmVhdGUgdGhlIGV4cGVjdGVkIHBhdGggaWYgaXQgZG9lcyBub3QgZXhpc3QKICBkaXIuY3JlYXRlKHBhdGggPSBkaXJuYW1lKHNvYmpfcGF0aCksIHJlY3Vyc2l2ZSA9IFRSVUUpCiAgIyMgRG93bmxvYWQgdGhlIGZpbGUKICBkb3dubG9hZC5maWxlKHVybCA9IHplbl9iYWNrdXBfZmlsZSwKICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gc29ial9wYXRoKQp9CgojIyBJbiBjYXNlIG9mIGVycm9yL2xvc3QgZGF0YSA6IGZvcmNlIGEgcmVsb2FkIGZyb20gYSBsb2NhbCBiYWNrdXAgcmVwb3NpdG9yeQppZihsb2NhbCkgewogIHNvYmpfcGF0aCA8LSBwYXN0ZTAoCiAgICAiL3NoYXJlZC9wcm9qZWN0cy8iLCBzZXNzaW9uaWQsICIvYXRlbGllcl9zY3JuYXNlcS9URC9CQUNLVVAvUkRTLyIsCiAgICBzb2JqX2ZpbGUpCn0KCiMjIExvYWQgdGhlIG9iamVjdApzb2JqIDwtIHJlYWRSRFMoZmlsZSA9IHNvYmpfcGF0aCkKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgRGltZW5zaW9uIHJlZHVjdGlvbgoKVGhpcyBzdGVwIG9yaWdpbmF0ZXMgZnJvbSB0aGUgb2JzZXJ2YXRpb24gdGhhdCB3ZSBkbyBub3Qgd2FudCBub3IgbmVlZCB0byBjaGFyYWN0ZXJpemUgKiplYWNoKiogb2Ygb3VyICoqdGhvdXNlbmRzIG9mIGNlbGxzKiosIGJ1dCAqKmdyb3VwcyoqIG9mIHRoZW0gKGNsdXN0ZXJzID8gY2VsbCB0eXBlcyA/IG90aGVyID8pLiBUaHVzLCB3ZSBkbyBubyBuZWVkIGFsbCBkYXRhLCBhbmQgZXZlbiBtYXkgYmVuZWZpdCBmcm9tIHN1Y2ggYSByZWR1Y3Rpb24gOgoKLSAgIFJlZHVjZSB0aGUgZGF0YSBjb21wbGV4aXR5CgogICAgLSAgIEZvciAqKmludGVycHJldGF0aW9uKioKCiAgICAtICAgRm9yICoqY29tcHV0YXRpb25zKioKICAgIAotICAgSW5jcmVhc2UgdGhlIHF1YWxpdHkgb2YgaW5mb3JtYXRpb24gY29udGFpbmVkIGluIHRoZSBkYXRhCgogICAgLSAgICoqRW5yaWNoaW5nKiogImdvb2QiIGJpb2xvZ2ljYWwgKipzaWduYWxzKioKICAgIAogICAgLSAgICoqRGlzY2FyZGluZyBub2lzZSoqIC8gY2VsbC1zcGVjaWZpYyBzaWduYWxzCgpUaGVyZSBpcyBhICoqbXVsdGl0dWRlIG9mIG1ldGhvZHMqKiBmb3IgZGltZW5zaW9uIHJlZHVjdGlvbgo8Y2VudGVyPiFbXShkaW1yZWRfdHJlZS5wbmcpPC9jZW50ZXI+CgoKIyMgUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyAoUENBKQoKSGVyZSwgd2Ugd2lsbCB1c2UgdGhlIGdyYW5kLW1vdGhlciBvZiBhbGwgOiB0aGUgUENBIChQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzKQoKYGBge3IgaF9SdW5QQ0EsIGV2YWwgPSBGQUxTRX0KIyBoX3J1blBDQQoKP1NldXJhdDo6UnVuUENBKCkKCmBgYAoKKipRdWVzdGlvbnMgOiDirY3irY0gTGlnaHRuaW5nIHF1aXp6IOKtjeKtjSAqKiA6IAoKYGBge3IgcV9wY2ExLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQojIHFfcGNhMQoKSG93IG1hbnkgcHJpbmNpcGFsIGNvbXBvbmVudHMgKFBDKSB3aWxsIGJlIGdlbmVyYXRlZCBieSBkZWZhdWx0ID8KCmBgYAoKPGJyPgoKYGBge3IgYV9wY2ExLCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGV2YWwgPSBGQUxTRX0KIyBhX3BjYTEKCiMjIC4gVGhlIGFuc3dlciBpcyA1MCAobnBjcyBwYXJhbWV0ZXIpCiMjCiMjIC4gV2Ugd2lsbCB1c2UgdGhpcyBkZWZhdWx0IHZhbHVlLgojIwojIyAuIFdhcm5pbmcgOiBpbiBzb21lIChyYXJlKSBjb250ZXh0cywgdGhpcwojIyAgIG1heSBub3QgYmUgZW5vdWdoICEKCmBgYAoKPGJyPgoKYGBge3IgcV9wY2EyLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQojIHFfcGNhMgoKV2hpY2ggZGF0YSB0eXBlIChpZSwgd2hpY2ggU2V1cmF0IG9iamVjdCBsYXllcikgd2lsbCBiZSB1c2VkIAp0byBnZW5lcmF0ZSB0aGUgY29tcG9uZW50cyA/CgpgYGAKCjxicj4KCmBgYHtyIGFfcGNhMiwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiMgYV9wY2EyCgojIyAuIERhdGEgZnJvbSB0aGUgc2NhbGUuZGF0YSBsYXllciB3aWxsIGJlIHVzZWQKIyMKIyMgLiBUaGlzIGlzIHVuZm9ydHVuYXRlbHkgbm90IHJlYWxseSBleHBsaWNpdAojIyAgIGZyb20gdGhlIFNldXJhdDo6UnVuUENBIGhlbHAgcGFnZSAhIChzZWUgdGhlICJmZWF0dXJlcyIKIyMgICBwYXJ0KQojIwojIyAuIFdlYiBzZWFyY2ggOiAid2hpY2ggc2xvdCBpcyB1c2VkIGJ5IFJ1blBDQSIKIyMiCiMjIC4gV2hlbiBydW4gb24gYSBTZXVyYXQgb2JqZWN0IHdpdGhvdXQgQHNjYWxlLmRhdGEgbGF5ZXIgOgojIyAgICJFcnJvciBpbiBHZXRBc3NheURhdGEob2JqZWN0LCBhc3NheS50eXBlID0gYXNzYXkudHlwZSwgc2xvdCA9ICJzY2FsZS5kYXRhIikgOiAKIyMgICBPYmplY3RAc2NhbGUuZGF0YSBoYXMgbm90IGJlZW4gc2V0LiBSdW4gU2NhbGVEYXRhKCkgYW5kIHRoZW4gcmV0cnkuIgoKYGBgCgo8YnI+CgpQZXJmb3JtIFBDQSBvbiBvdXIgZGF0YQoKYGBge3IgcGNhfQojIHBjYQoKIyMgTm90ZSA6IGEgc2VlZCBpcyB1c2VkIGhlcmUgIQpzb2JqIDwtIFNldXJhdDo6UnVuUENBKAogIG9iamVjdCA9IHNvYmosIAogIGFzc2F5ID0gJ1JOQScsIAogIHNlZWQudXNlID0gbXlfc2VlZCwgCiAgdmVyYm9zZSA9IEZBTFNFKQoKYGBgCgo8YnI+CgpEZXNjcmlwdGlvbiA6CgpgYGB7ciBQQ0FkZXNjLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyIsIGV2YWwgPSBGQUxTRX0KIyBQQ0FkZXNjCgojIyBQbGVhc2UsIGZvY3VzIG9uIHRoZSAicmVkdWN0aW9ucyIgc2xvdApWaWV3KHNvYmopCgpgYGAKCjxicj4KClZpc3VhbGl6YXRpb24gb2YgdGhlIHZlcnkgZmlyc3QgKip0d28gY29tcG9uZW50cyoqLCB3aXRoIGNlbGxzIGNvbG9yaW5nIGFjY29yZGluZyB0byB0aGUgZXN0aW1hdGVkICoqY2VsbCBjeWNsZSBwaGFzZSoqIDoKCmBgYHtyIFBDQXBsb3R9CiMgUENBcGxvdAoKIyMgU2NhdHRlciBwbG90IGFsb25nIGRpbWVuc2lvbnMKU2V1cmF0OjpEaW1QbG90KAogIG9iamVjdCA9IHNvYmosIAogICMjIEZpcnN0IHR3byBjb21wb25lbnRzCiAgZGltcyA9IGMoMSwyKSwgCiAgIyMgQ29sb3IgZG90cyBwZXIgY2VsbCBwaGFzZSBncm91cHMKICBncm91cC5ieSA9ICdDQ19TZXVyYXRfUGhhc2UnLCAKICAjIyBEYXRhIHRvIHVzZQogIHJlZHVjdGlvbiA9ICdwY2EnKQoKYGBgCgo8YnI+CgojIyBRdWVzdGlvbnMKCmBgYHtyIHFfcGNhMywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX3BjYTMKCkdpdmUgdXMgeW91ciBpbnRlcnByZXRhdGlvbiAvIGZlZWxpbmdzIGZyb20gdGhpcyBwbG90ICEKCmBgYAoKPGJyPgoKYGBge3IgcV9wY2E0LCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQojIHFfcGNhNAoKU2hvdWxkIHdlIGxpbWl0IG91cnNlbHZlcyB0byB1c2luZyAyIGRpbWVuc2lvbnMgdG8gaW50ZXJwcmV0IG91ciBkYXRhID8KCmBgYAoKPGJyPjxicj48YnI+CgotICAgTWF5YmUgd2Ugc2hvdWQgKipyZWR1Y2UqKiBpbmZvcm1hdGlvbiBhIHRhZCAqKm1vcmUqKiwganVzdCBmb3IgdGhlIHNha2Ugb2YgLi4uIAoKICAgIC0gICAuLi4gdW5kZXJzdGFuZGluZyBvdXIgZGF0YSAuLi4KCiAgICAtICAgLi4ud2l0aCBvdXIgKipwb29yIGh1bWFuIGJyYWlucyoqIC4uLgoKICAgIC0gICAuLi4gYm9ybiBhbmQgcmFpc2VkIGluIGEgM0QgKipldWNsaWRlYW4gd29ybGQqKiAhCgo8IS0tIEFTQ0lJIEFSVCBHRU5FUkFUT1IgSSdNIFdJVEggU1RPT1BJRCAtLT4KCmBgYHtyIHN0b29waWQsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJub3RydW4iKX0KIyMKIyMgICAgIF9fICAgICAgICAgICAgICAuX19fL1wgICAgICAgICAgICAgICAgLl9fICBfXyAgLl9fICAgICAgICAgICAgICAgIF9fICAgICAgICAgICAgICAgLl9fICAgIC5fX18gLl8uCiMjICAgIC8gLyAgIF9fX19fXyAgICAgfCAgICkvX19fX18gICBfXyAgXyAgX3xfX3wvICB8X3wgIHxfXyAgICAgX19fX19fXy8gIHxfIF9fIF9fX19fX19fIHxfX3wgX198IF8vIHwgfAojIyAgIC8gLyAgIC9fX19fXy8gICAgIHwgICB8LyAgICAgXCAgXCBcLyBcLyAvICBcICAgX19cICB8ICBcICAgLyAgX19fL1wgICBfX1wgIHwgIFxfX19fIFx8ICB8LyBfXyB8ICB8IHwKIyMgICBcIFwgICAvX19fX18vICAgICB8ICAgfCAgWSBZICBcICBcICAgICAvfCAgfHwgIHwgfCAgIFkgIFwgIFxfX18gIFwgfCAgfCB8ICB8ICAvICB8Xz4gPiAgLyAvXy8gfCAgIFx8CiMjICAgIFxfXCAgICAgICAgICAgICAgfF9fX3xfX3xffCAgLyAgIFwvXF8vIHxfX3x8X198IHxfX198ICAvIC9fX19fICAvIHxfX3wgfF9fX18vfCAgIF9fL3xfX1xfX19fIHwgICBfXwojIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcLyAgICAgICAgICAgICAgICAgICAgICAgIFwvICAgICAgIFwvICAgICAgICAgICAgIHxfX3wgICAgICAgICAgIFwvICAgXC8KIyMKYGBgCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgVmlzdWFsaXphdGlvbgoKVGhpcyBmaW5hbCBwcm9jZXNzaW5nIHN0ZXAgbmVlZCB0byBmaW5hbGx5ICoqb2JzZXJ2ZSoqIG91ciBkYXRhIHJlcXVpcmVzIGEgbm92ZWwgZGltZW5zaW9uIHJlZHVjdGlvbiBtZXRob2Qgd2l0aCBhIHZlcnkgaGlnaCBjaGFsbGVuZ2UgdG8gb3ZlcmNvbWUgOiByZWR1Y2UgYSBzcGFjZSBvZiBkb3plbnMgb2YgZGltZW5zaW9ucyB0byAqKmp1c3QgYSBmZXcqKiAhCgpXZSB3aWxsIHVzZSB0aGUgWyoqVU1BUCoqXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9VTUFQKXt0YXJnZXQ9Il9ibGFuayJ9IG1ldGhvZC4KCiMjIFVuaWZvcm0gTWFuaWZvbGQgQXBwcm94aW1hdGlvbiBhbmQgUHJvamVjdGlvbiAoVU1BUCkKCkhvdyA/CgpgYGB7ciBodW1hcCwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8iLCBldmFsID0gRkFMU0V9CiMgaHVtYXAKCj9TZXVyYXQ6OlJ1blVNQVAoKQoKYGBgCgojIyMgU2VsZWN0IGRpbWVuc2lvbnMKCldlIGdlbmVyYXRlZCAqKjUwIFBDQSBjb21wb25lbnRzKiogZnJvbSBvdXIgfjEyIEsgZmVhdHVyZXMKCi0gICBUaGVzZSA1MCBkaW1lbnNpb25zIG1heSAqKm5vdCBhbGwqKiBjb250YWluICoqdmFsdWFibGUqKiBpbmZvcm1hdGlvbgoKLSAgIFdlIHNob3VsZCB0cnkgZG8gKipzZWxlY3QgdGhlIG1vc3QgdXNlZnVsKiogb25lcyBhbmQgKipkaXNjYXJkKiogdGhlIHJlbWFpbmluZyAqKm5vaXNlKioKCi0gICBCdXQgKipob3cgbWFueSoqIHNob3VsZCB3ZSBrZWVwID8KCi0gICAqKlF1ZXN0aW9uKiogOiAKCiAgICBgYGB7ciBxX25kaW0xLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQogICAgIyBxX25kaW0xCiAgICAKICAgIERvIHlvdSBoYXZlIGFuIGlkZWEgYWJvdXQgdGhpcyBudW1iZXIgPwogICAgCiAgICBgYGAKICAgIAogICAgPGJyPgoKICAgIGBgYHtyIHJfbmRpbTEsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQogICAgIyByX25kaW0xCiAgICAKICAgICMjIC4gSW1wb3NzaWJsZSB0byBndWVzcyB3aXRoIG91ciBjdXJyZW50IGtub3dsZWRnZS4KICAgICMjCiAgICAjIyAuIEJ1dCB3ZSBjYW4gZ2V0IHNvbWUgaGVscCBmcm9tIHRoZSBQQ0EgZGF0YSBpdHNlbGYKICAgICMjCiAgICAjIyAuIElmIHlvdSBzYWlkIGEgdmFsdWUgYWJvdmUgdGhlIDUwIGNvbXBvbmVudHMgd2UKICAgICMjICAgZ2VuZXJhdGVkIGZvciBvdXIgUENBLCB5b3Ugc2hvdWxkIHdlYXIgdGhlCiAgICAjIyAgIGNvbmUgb2Ygc2hhbWUgIQogICAgCiAgICBgYGAKICAgIAogICAgPGJyPgoKVGhlcmUgYXJlICoqc2V2ZXJhbCoqIG1ldGhvZHMgdG8gaGVscCB1cyBjaG9vc2UuCgpTZXZlcmFsLCBidXQgKipub25lIHBlcmZlY3QqKi4KCldlIHdpbGwgdXNlIGEgdmVyeSAqKnNpbXBsZSwgZ3JhcGhpY2FsKiogbWV0aG9kIDogdGhlIG9ic2VydmF0aW9uIG9mIHRoZSBhbW91bnQgb2YgZ2xvYmFsIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBlYWNoIGNvbXBvbmVudCwgdGhyb3VnaCB0aGUgYGVsYm93LXBsb3RgLgoKPGJyPgo8Y2VudGVyPiFbXShlbGJvd19rbmVlX0VMQk9XLnBuZyk8L2NlbnRlcj4KPGJyPgoKYGBge3IgaF9lbGJvdywgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8iLCBldmFsID0gRkFMU0V9CiMgaF9lbGJvdwoKP1NldXJhdDo6RWxib3dQbG90KCkKCmBgYAoKPGJyPgoKQXBwbHkgb24gb3VyIGRhdGEgOgoKYGBge3IgZWxib3d9CiMgZWxib3cKCiMjIFBlcmZvcm0gdGhlICJlbGJvdyBwbG90IgpTZXVyYXQ6OkVsYm93UGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICByZWR1Y3Rpb24gPSAncGNhJywKICBuZGltcyA9IDUwKQoKYGBgCgo8YnI+CgoqKlF1ZXN0aW9uKiogOiAKCmBgYHtyIHFfbmRpbTIsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CiMgcV9uZGltMgoKQW55IG1vcmUgcHJlY2lzZSBpZGVhLCBub3cgPwoKYGBgCgo8YnI+CgpgYGB7ciByX25kaW0yLCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGV2YWwgPSBGQUxTRX0KIyByX25kaW0yCgojIyAuIFRoZSBjb250cmlidXRpb24gdG8gdGhlIHZhcmlhbmNlIChzZMKyKSBzZWVtcwojIyAgIGdyZWF0bHkgcmVkdWNlZCBhZnRlciAzMCBQQ3MuCiMjCiMjIC4gTWF5YmUgc29tZXRoaW5nIGJldHdlZW4gfjE1IGFuZCB+MzAgc2hvdWxkIGRvCiMjICAgdGhlIHRyaWNrID8KYGBgCgo8YnI+CgojIyMgQXNzZXNzIGRpbWVuc2lvbnMKClRvIGRlbW9uc3RyYXRlIHRoZSAqKmVmZmVjdCoqIG9mIHRoZSBudW1iZXIgb2YgUEMgZGltZW5zaW9ucyB1c2VkIGFzIGlucHV0IHRvIHRoZSBVTUFQIGdlbmVyYXRpb24sIHdlIHdpbGwgcGVyZm9ybSBhIGNvbXBhcmlzb24gdXNpbmcgYDZgIGRpZmZlcmVudCBhbW91bnRzIG9mIHJldGFpbmVkIFBDcyA6Cgo8YnI+CjxDRU5URVI+KipUZUFtV29SayBUaU1lICEqKgo8YnI+CiFbXShhMm1pYmxldWZyYWlzZTUwLmpwZykKPC9DRU5URVI+Cjxicj4KCldlIHdpbGwgZGlzcGF0Y2ggdGhlIGFzc2Vzc21lbnQgb2YgZWFjaCBhbW91bnQgb2YgZGltZW5zaW9ucyB0byBncm91cHMgCm9mIHRyYWluZWVzLgoKYGBge3IgZGltX3NlbCwgZmlnLndpZHRoID0gMjQsIGZpZy5oZWlnaHQgPSAxMn0KIyBkaW1fc2VsCgojIyBQQ0EgbWF4IGRpbWVuc2lvbnMgdG8gZXZhbHVhdGUKcGNhX2RpbXMgPC0gYygzLCA3LCAxNywgMjUsIDMwLCA0MCkKCiMjIERlZmluZSBhIGZ1bmN0aW9uIHRvIGNvbXB1dGUgdGhlIFVNQVAKcGNhX2RpbV9ldmFsIDwtIGZ1bmN0aW9uKG9iamVjdCA9IE5VTEwsIGRpbS5tYXggPSAyLCBteV9zZWVkID0gMTMzN0wpIHsKICAKICBtZXNzYWdlKCdcblJ1bm5pbmcgVU1BUCB3aXRoICcsIGRpbS5tYXgsICcgZGltZW5zaW9ucyAuLi5cbicpCiAgCiAgIyMgUnVuVU1BUAogIG9iamVjdCA8LSBTZXVyYXQ6OlJ1blVNQVAoCiAgICBvYmplY3QgPSBvYmplY3QsIGFzc2F5ID0gIlJOQSIsIAogICAgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOmRpbS5tYXgsIAogICAgc2VlZC51c2UgPSBteV9zZWVkKQogIAogICMjIFBsb3QKICBkcE4gPC0gU2V1cmF0OjpEaW1QbG90KAogICAgb2JqZWN0ID0gb2JqZWN0LCAKICAgIHJlZHVjdGlvbiA9ICd1bWFwJywKICAgIGNvbWJpbmUgPSBUUlVFKSArIGdncGxvdDI6OmdndGl0bGUoCiAgICAgIGxhYmVsID0gcGFzdGUwKCJEaW0gOiAiLCBkaW0ubWF4KSkgKyBTZXVyYXQ6OkRhcmtUaGVtZSgpCiAgCiAgIyMgQ2xlYW4KICBybShvYmplY3QpCiAgCiAgIyMgUmV0dXJuIHRoZSBwbG90IG9iamVjdAogIHJldHVybihkcE4pCn0KCiMjIFJ1biB0aGUgZnVuY3Rpb24gb24gbXVsdGlwbGUgZGltZW5zaW9ucywgZ2V0IGEgbGlzdCBvZiBnZ3Bsb3RzCnBjYV9ldmFsX3JlcyA8LSBsYXBwbHkoWCA9IHBjYV9kaW1zLAogICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGZ1bmN0aW9uKHApIHsKICAgICAgICAgICAgICAgICAgICAgICAgIHBjYV9kaW1fZXZhbChvYmplY3QgPSBzb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW0ubWF4ID0gcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBteV9zZWVkID0gbXlfc2VlZCkKICAgICAgICAgICAgICAgICAgICAgICB9KQoKIyMgUGxvdCB0aGUgbGlzdCBhbGx0b2dldGhlcgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGNhX2V2YWxfcmVzLCBucm93ID0gMikKCmBgYAoKPGJyPgoKKipRdWVzdGlvbioqCgpgYGB7ciBxX25kaW0zLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQojIHFfbmRpbTMKCkFueSBtb3JlIHByZWNpc2UgaWRlYSwgbm93LCBGT1IgUkVBTCA/CgpgYGAKCjxicj4KCmBgYHtyIHJfbmRpbTMsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIHJfbmRpbTMKCiMjIC4gQSBsaW1pdGVkIGFtb3VudCBvZiBQQ3MgaXMgbm90IGFibGUgdG8gY2FwdHVyZQojIyAgIGVub3VnaCBpbmZvcm1hdGlvbiAodmFyaWF0aW9uKSB0byBidWlsZCBhIHN1ZmZpY2llbnRseSAKIyMgICBkZWZpbmVkIHRvcG9sb2d5LgojIwojIyAuIFRoZSBkaWZmZXJlbmNlcyBpbiB0aGUgZ2xvYmFsIHRvcG9sb2d5IGlzCiMjICAgaXMgcXVpdGUgbGltaXRlZCBiZXR3ZWVuIHRoZSBoaWdoZXIgUENzIHZlcnNpb25zLgojIwojIyAuIFRoaXMgbWF5IGltcGx5IHRoYXQgdGhlIGFkZGl0aW9uYWwKIyMgICBjb21wb25lbnRzIGFib3ZlIDIwfjI1IGRvIG5vdCBhZGQgbW9yZSAKIyMgICBpbmZvcm1hdGlvbiAobmVpdGhlciBtb3JlIG5vaXNlLCBpbiB0aGlzIGNhc2UpLgoKYGBgCgo8YnI+CgpXZSBjYW4gbm93IHBlcmZvcm0gdGhlIGZpbmFsIFVNQVAgd2l0aCB0aGUgUEMgZGltZW5zaW9ucyBvZiB5b3VyIGNob2ljZS4KCjxicj4KCiMjIyBDcmVhdGUgdGhlIFVNQVAKCkZvciB0aGUgbmV4dCBzdGVwcyBvZiB0aGUgdHJhaW5pbmcsIHdlIHdpbGwgdXNlICoqYDIwYCoqIFBDQSBkaW1lbnNpb25zLgoKYGBge3IgdW1hcDIwfQojIHVtYXAyMAoKIyMgRml4aW5nIG5fZGltCm5fZGltIDwtIDIwCgojIyBVc2luZyAyMCBQQ3MKIyMgQSBzZWVkIGlzIG5lZWRlZCBoZXJlICEKc29iaiA8LSBTZXVyYXQ6OlJ1blVNQVAoCiAgICBvYmplY3QgPSBzb2JqLCBhc3NheSA9ICJSTkEiLCAKICAgIHJlZHVjdGlvbiA9ICJwY2EiLCAKICAgIGRpbXMgPSAxOm5fZGltLCAKICAgIHNlZWQudXNlID0gbXlfc2VlZCkKCiMjIERpbVBsb3QKdW1hcDJkIDwtIFNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICByZWR1Y3Rpb24gPSAndW1hcCcpICsgU2V1cmF0OjpEYXJrVGhlbWUoKQpwcmludCh1bWFwMmQpCgpgYGAKCjxicj4KCiMjIEJvbnVzIDogM0QgVU1BUCAoREVNTykKCldoaWxlIGJ5IGRlZmF1bHQgU2V1cmF0OjpSdW5VTUFQIHdpbGwgcHJvZHVjZSAyLWRpbWVuc2lvbiByZWR1Y3Rpb25zLCB0aGUgbWV0aG9kIGNhbiBnZW5lcmF0ZSBmdXJ0aGVyIGNvbXBvbmVudHMuCgpEZXNwaXRlIG91ciBsaW1pdGVkIGJyYWluLCB0aGlzIGlzIHNvbWV0aW1lcyBpbnRlcmVzdGluZyBhbmQgdXNlZnVsIHRvIGF0dGVtcHQgYSByZWR1Y3Rpb24gdG8gMyBkaW1lbnNpb25zIGluc3RlYWQgb2YgMi4gVGhpcyBjYW4gYmUgdmVyeSBlZmZlY3RpdmUgd2hlbiBsb29raW5nIGZvciB0cmFqZWN0b3JpZXMuCgpXZSBjYW4gZ2VuZXJhdGUgYSBVTUFQIHdpdGggMyBjb21wb25lbnRzLCBmcm9tIHRoZSAyMCBQQ3MgOgoKYGBge3IgdW1hcDNEMjAsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KIyB1bWFwM0QyMAoKIyMgVU1BUCBmcm9tIDIwIFBDcywgMyBjb21wb25lbnRzIHJlcXVlc3RlZApzb2JqX1UzRCA8LSBTZXVyYXQ6OlJ1blVNQVAoCiAgb2JqZWN0ID0gc29iaiwgYXNzYXkgPSAnUk5BJywgCiAgZ3JhcGgubmFtZSA9ICdSTkFfc25uJywgCiAgcmVkdWN0aW9uID0gJ3BjYScsIAogIHJlZHVjdGlvbi5uYW1lID0gJ3VtYXAzZCcsCiAgZGltcyA9IDE6bl9kaW0sIAogIHNlZWQudXNlID0gbXlfc2VlZCwKICBuLmNvbXBvbmVudHMgPSAzKQoKYGBgCgpXZSBjYW4gcGxvdCB0aGUgdHdvIGZpcnN0IFVNQVAgY29tcG9uZW50cyBmcm9tIHRoaXMgM0Qgc3BhY2UgOgoKYGBge3IsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIiwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDh9CiMgcGxvdF91bWFwM0QyMAoKIyMgRGltUGxvdCBvZiB0aGUgZmlyc3QgMiBVTUFQIGNvbXBvbmVudHMKdW1hcDNkIDwtIFNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqX1UzRCwgCiAgZGltcyA9IGMoMSwyKSwKICByZWR1Y3Rpb24gPSAndW1hcDNkJykgKyBTZXVyYXQ6OkRhcmtUaGVtZSgpCnByaW50KHVtYXAzZCkKCmBgYAoKPGJyPgoKKipRdWVzdGlvbioqIDogCgpgYGB7ciBxX3VtYXAzRCwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX3VtYXAzRAoKSXNuJ3QgdGhlcmUgc29tZXRoaW5nIHN0cmlraW5nID8KCmBgYAoKPGJyPgoKYGBge3IgYV91bWFwM0QsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIGFfdW1hcDNECgojIyAuIFRoZSBwbG90IGlzIG5vdCB0aGUgc2FtZSBhcyB3aGVuCiMjICAgdXNpbmcgMjUgUENzIHdoZW4gcmVxdWVzdGluZyAzIFVNQVAKIyMgICBjb21wb25lbnRzIGluc3RlYWQgb2YgMiAhCiMjCiMjIC4gVGhlIDIgY29tcG9uZW50cyBvZiBhIDJEIFVNQVAgYXJlIG5vdAojIyAgIHRoZSBzYW1lIGFzIHRoZSB0d28gZmlyc3QgY29tcG9uZW50cwojIyAgIG9mIGEgZGltPjIgVU1BUC4KCmBgYAoKPGJyPgoKYGBge3IgdW1hcFhkLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyIsIGZpZy53aWR0aCA9IDE2LCBmaWcuaGVpZ2h0ID0gOCB9CiMgdW1hcFhkCgpwYXRjaHdvcms6OndyYXBfcGxvdHMoCiAgbGlzdCgKICAgIHVtYXAyZCAmIGdncGxvdDI6OmdndGl0bGUobGFiZWwgPSAiVU1BUCAoMkQpIiksCiAgICB1bWFwM2QgJiBnZ3Bsb3QyOjpnZ3RpdGxlKGxhYmVsID0gIlVNQVAgKDNEKSIpKSwgCiAgbnJvdyA9IDEpCgpgYGAKCjxicj4KCkxldCdzIHBlcmZvcm0gYSAzRCByZXByZXNlbnRhdGlvbiBvZiBvdXIgVU1BUCAoUFJFLVJFTkRFUkVEKQoKYGBge3IgdW1hcDNEMjByZ2wsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIiwgZXZhbCA9IEZBTFNFfQojIHVtYXAzRDIwcmdsCgojIyAzRCBwbG90IChub3QgcnVuIGFzIGZhaWxpbmcgaW4gaW50ZXJhY3RpdmUgbW9kZSkKcGxvdGx5OjpwbG90X2x5KAogIGRhdGEgPSBhcy5kYXRhLmZyYW1lKFNldXJhdDo6UmVkdWN0aW9ucygKICAgIG9iamVjdCA9IHNvYmpfVTNELCAKICAgIHNsb3QgPSAidW1hcDNkIilAY2VsbC5lbWJlZGRpbmdzKSwKICB4ID0gfnVtYXAzZF8xLCAKICB5ID0gfnVtYXAzZF8yLCAKICB6ID0gfnVtYXAzZF8zLCAKICB0eXBlID0gJ3NjYXR0ZXIzZCcsIAogIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDIsIHdpZHRoPTIpKQoKIyMgQ2xlYW4Kcm0oc29ial9VM0QpCgpgYGAKCjxicj4KCjxjZW50ZXI+IVtdKHRkM2FfdW1hcDNkLnBuZyk8L2NlbnRlcj4KCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgojIFNhdmUgdGhlIFNldXJhdCBvYmplY3QKCldlIHdpbGwgc2F2ZSBvdXIgU2V1cmF0IG9iamVjdCB0aGF0IG5vdyBjb250YWlucyBQQ0EgYW5kIFVNQVAgcmVkdWN0aW9ucyAgOgoKYGBge3Igc2F2ZXJkczEsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMgc2F2ZXJkczEKCiMjIFNhdmUgb3VyIFNldXJhdCBvYmplY3QgKHJpY2ggbmFtaW5nKQpvdXRfbmFtZSA8LSBwYXN0ZTAoCiAgICAgICAgICBvdXRwdXRfZGlyLCAiLyIsIHBhc3RlKAogICAgICAgICAgICBjKCIwOSIsIFNldXJhdDo6UHJvamVjdChzb2JqKSwgIlM1IiwgCiAgICAgICAgICAgICAgIkRpbVJlZC5QQ0EiLCBwYXN0ZSgKICAgICAgICAgICAgICAgIGRpbShzb2JqKSwgCiAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICcuJwogICAgICAgICAgICAgICkKICAgICAgICAgICAgKSwgY29sbGFwc2UgPSAiXyIpLAogICAgICAgICAgICAiLlJEUyIpCgojIyBDaGVjawpwcmludChvdXRfbmFtZSkKCiMjIFdyaXRlIG9uIGRpc2sKc2F2ZVJEUyhvYmplY3QgPSBzb2JqLCAKICAgICAgICBmaWxlID0gb3V0X25hbWUpCgpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIENsdXN0ZXJpbmcKCldlIGNhbiBub3cgYXR0ZW1wdCB0byBkZXRlcm1pbmUgaG93IGNlbGxzICoqYXJlIG9yZ2FuaXplZCoqIGluIGFuICoqdW5zdXBlcnZpc2VkKiogbWFubmVyIGluIHRoaXMgc3BhY2UKCldlIHdpbGwgdXNlIHRoZSBncmFwaC1iYXNlZCBjbHVzdGVyaW5nIG1ldGhvZCBbTG91dmFpbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTG91dmFpbl9tZXRob2Qpe3RhcmdldD0iX2JsYW5rIn0KCkNsdXN0ZXJpbmcgd2lsbCBiZSBwZXJmb3JtZWQgb24gdGhlICoqYFBDQWAqKiBkaW1lbnNpb24gcmVkdWN0aW9uLCAKKipOT1QqKiBvbiB0aGUgYFVNQVBgIG9uZSAhCgpgYGB7ciBxX2NsdXN0X29uX1BDQSwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX2NsdXN0X29uX1BDQQoKQW55IGlkZWEgd2h5ID8KCmBgYAoKYGBge3IgYV9jbHVzdF9vbl9QQ0EsIGNsYXNzLnNvdXJjZT1jKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGNsYXNzLm91dHB1dD0iYW5zd2VybyJ9CiMgYV9jbHVzdF9vbl9QQ0EKCiMjIC4gVU1BUCBpcyBhIHN0cm9uZyBBUFBST1hJTUFUSU9OIG9mIHRoZSBtdWx0aWRpbWVuc2lvbmFsIHNwYWNlCiMjICAgd2hpY2ggc2luZ2xlIHB1cnBvc2UgaXMgdG8gSEVMUCB1cyB1bmRlcnN0YW5kIG1vcmUgZWFzaWx5IHRoZQojIyAgIGhpZGRlbiBzdHJ1Y3R1cmUgb2Ygb3VyIGRhdGFzZXQuCiMjCiMjIC4gVGhlIFBDQSBjb21wb25lbnQgKG9yIGFueSByZWR1Y3Rpb24gbWV0aG9kIHVzZWQgZGlyZWN0bHkgZnJvbSBvdXIKIyMgICBub3JtYWxpemVkL3NjYWxlZCBkYXRhLCBsaWtlIE1EUyBvciBJQ0EsIC4uLikgY29udGFpbnMgdGhlICJyZWFsIiAKIyMgICBpbmZvcm1hdGlvbiwganVzdCBkaXN0cmlidXRlZCBkaWZmZXJlbnRseS4KIyMKIyMgLiBJZiBvbmUgdXNlcyB0aGUgInNlY29uZCByZWR1Y2VkIHNwYWNlIiB1c2VkIGZvciB2aXN1YWxpemF0aW9uIChVTUFQLAojIyAgIHRTTkUsIERNLCAuLi4pIGFzIHRoZSBiYXNpcyBmb3IgY2x1c3RlcmluZywgb25lIG1heSBvYnRhaW4gcHJldHR5IHBsb3RzCiMjICAgd2l0aCB3ZWxsLWRlZmluZWQgY2x1c3RlcnMuIFRoZSBsYXR0ZXIgYmVpbmcgYmFzZWQgb24gd3JvbmcgZGF0YSwgdGh1cwojIyAgIGxlYWRpbmcgdG8gd3JvbmcgaW50ZXJwcmV0YXRpb24gOigKCmBgYAoKPGJyPgoKIyMgRmluZCBuZWlnaGJvcnMKCkJlZm9yZSBydW5uaW5nIHRoZSBMb3V2YWluIG1ldGhvZCwgYSBmaXJzdCBwYXNzIG1ldGhvZCBpcyB1c2VkIHRvIGdlbmVyYXRlIGEgIkstTmVhcmVzdCBOZWlnaGJvdXIiIGdyYXBoIChzZWUgbW9yZSBkZXRhaWxzIFtoZXJlXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL3BibWMza190dXRvcmlhbC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9KS4KCmBgYHtyIGZubjIwfQojIGZubjIwCgojIyBDb21wdXRlIGEgU05OIHVzaW5nIHRoZSBmaXJzdCAyMCBQQ3MKc29iaiA8LSBTZXVyYXQ6OkZpbmROZWlnaGJvcnMoCiAgb2JqZWN0ID0gc29iaiwgCiAgZGltcyA9IDE6MjAsIAogIHJlZHVjdGlvbiA9ICJwY2EiKQoKYGBgCgojIyBMb3V2YWluIGNsdXN0ZXJpbmcKCi0gICBXZSB3aWxsIHRlc3QgbXVsdGlwbGUgZGlmZmVyZW50IHJlc29sdXRpb25zCgotICAgVGhlIFNldXJhdCBmdW5jdGlvbiB0byBwZXJmb3JtIGNsdXN0ZXJpbmcgY2FuIGJlIGNhbGxlZCB3aXRoIAogICAgbXVsdGlwbGUgcmVzb2x1dGlvbnMgYXQgb25jZSAobGVzcyB0byBjb2RlLCBsdWNreSB1cyAhKS4KICAgIAo8YnI+CjxDRU5URVI+KipUZUFtV29SayBUaU1lICEqKgo8YnI+CiFbXShhMm1pYmxldWZyYWlzZTUwLmpwZykKPC9DRU5URVI+Cjxicj4KCldlIHdpbCBkaXNwYXRjaCB0aGUgZGlmZmVyZW50IGNsdXN0ZXJpbmcgcmVzb2x1dGlvbiB2YWx1ZXMgdG8gZ3JvdXBzIG9mIHRyYWluZWVzLgoKYGBge3IgY2x1c3RMMjB9CiMgY2x1c3RMMjAKCiMjIExvdXZhaW4gcmVzb2x1dGlvbnMgdG8gdGVzdApyZXNvbCA8LSBjKC4zLCAuNiwgLjksIDEuMiwgMS41LCAxLjgpCgojIyBDbHVzdGVyaW5nCnNvYmogPC0gU2V1cmF0OjpGaW5kQ2x1c3RlcnMoCiAgb2JqZWN0ID0gc29iaiwgCiAgcmVzb2x1dGlvbiA9IHJlc29sLAogIHZlcmJvc2UgPSBGQUxTRSkKCmBgYAoKPGJyPgoKKipRdWVzdGlvbioqCgpgYGB7ciBxX2NsdXN0ZGVzYywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX2NsdXN0ZGVzYwoKQ291bGQgeW91IHRlbGwgdXMgd2hhdCBjaGFuZ2VkIGluIG91ciBvYmplY3QgPwoKYGBgCgo8YnI+CgpgYGB7ciBhX2NsdXN0ZGVzYywgY2xhc3Muc291cmNlPWMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgY2xhc3Mub3V0cHV0PSJhbnN3ZXJvIiwgZXZhbCA9IEZBTFNFfQojIGFfY2x1c3RkZXNjCgojIyBQbGVhc2UsIGZvY3VzIG9uIHRoZSBbbWV0YS5kYXRhXSBzbG90ClZpZXcoc29iaikKCiMjIC4gTmV3IGVudHJpZXMgaW4gdGhlIGJhcmNvZGVzIG1ldGFkYXRhIDoKIyMgICAuIFRoZSByZXN1bHRzIG9mIG91ciBjbHVzdGVyaW5ncwojIyAgIC4gJ3NldXJhdF9jbHVzdGVycycgd2hpY2ggY29ycmVzcG9uZHMgdG8gdGhlIAojIyAgICAgbGFzdCB2ZXJzaW9uIHdlIHJhbgojIwojIyAuIFRoaXMgJ3NldXJhdF9jbHVzdGVycycgYW5ub3RhdGlvbiB3aWxsIGJlIHRoZQojIyAgIG9uZSB1c2VkIGJ5IGRlZmF1bHQgYnkgU2V1cmF0IGZvciBhbnkgZnVydGhlcgojIyAgIGFuYWx5c2lzCgpgYGAKCjxicj4KCiMjIEFzc2VzcyByZXNvbHV0aW9ucwoKIyMjIE9uIFVNQVBzCgojIyMjIENsdXN0ZXJzCgpQbG90dGluZyBVTUFQcyBoYXJib3JpbmcgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyBmb3Igb3VyIHRlc3RlZCByZXNvbHV0aW9ucwoKYGBge3IgY2x1c3RfZGltcGxvdCwgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTl9CiMgY2x1c3RfZGltcGxvdAoKIyMgTWV0YWRhdGEgbmFtZSBvZiBjbHVzdGVyaW5nIHJlc3VsdHMgKGRlZmluZWQgYnkgZGVmYXVsdCBieSBTZXVyYXQpCnJlc29sX25hbWVzIDwtIHBhc3RlMCgiUk5BX3Nubl9yZXMuIiwgcmVzb2wpCgojIyBEaW1QbG90ClNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICByZWR1Y3Rpb24gPSAidW1hcCIsIAogIGdyb3VwLmJ5ID0gcmVzb2xfbmFtZXMsCiAgbGFiZWwgPSBUUlVFLCAKICByZXBlbCA9IFRSVUUpCgpgYGAKCjxicj4KCmBgYHtyIHFfY29tcHJlcywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX2NvbXByZXMKCi4gQ291bGQgeW91IGJyaWVmbHkgY29tcGFyZSB0aGUgZGlmZmVyZW50IHZlcnNpb25zID8KCi4gV291bGQgeW91IGNvbnNpZGVyIHRoYXQgc29tZSByZXN1bHRzIGFyZSB1bmRlci9vdmVyLWNsdXN0ZXJlZCA/CgouIEFyZSBhbGwgY2x1c3RlcnMgd2VsbC1kZWZpbmVkID8KCmBgYAoKPGJyPgo8YnI+CgotICAgU29tZXRoaW5nIHRoYXQgbWlnaHQgaGVscCAoYSBiaXQpIDogYHNwbGl0dGluZ2AgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyA6CiAgICAKICAgIGBgYHtyIGNsdXN0X2RpbXBsb3Rfc3BsaXQsIGZpZy53aWR0aD0xOCwgZmlnLmhlaWdodD00fQogICAgIyBjbHVzdF9kaW1wbG90X3NwbGl0CiAgICAKICAgICMjIExvb3Bpbmcgb24gcmVzb2x1dGlvbnMKICAgIGZvciAobXlfZ3JwIGluIHJlc29sX25hbWVzKSB7CiAgICAgICMjIERpbVBsb3QKICAgICAgcCA8LSBTZXVyYXQ6OkRpbVBsb3QoCiAgICAgICAgb2JqZWN0ID0gc29iaiwgCiAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLCAKICAgICAgICBncm91cC5ieSA9IG15X2dycCwKICAgICAgICBzcGxpdC5ieSA9IG15X2dycCwKICAgICAgICBsYWJlbCA9IFRSVUUsIAogICAgICAgIHJlcGVsID0gVFJVRSkKICAgICAgcHJpbnQocCkKICAgIH0KICAgIGBgYAogICAgCjxicj4KCi0gICBTb21ldGhpbmcgb3RoZXIgOiBhc3Nlc3NpbmcgY2x1c3RlcnMgKipzdGFiaWxpdHkqKiBhY3Jvc3MgcmVzb2x1dGlvbnMsCiAgICB0aGFua3MgdG8gYGNsdXN0cmVlYCAoREVNTykgOgogICAgCiAgICBgYGB7ciBjbHVzdHJlZV9kZW1vLCBjbGFzcy5zb3VyY2UgPSAibm90cnVuIiwgY2xhc3Mub3V0cHV0ID0gIm5vdHJ1bm8iLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gOH0KICAgICMgY2x1c3RyZWVfZGVtbwogICAgCiAgICAjIyBGdWxsIGxvYWRpbmcgb2YgY2x1c3RyZWUgaXMgbmVlZGVkIGhlcmUuLi4KICAgIGxpYnJhcnkoY2x1c3RyZWUpCiAgICAKICAgICMjIFJ1bm5pbmcgY2x1c3RyZWUKICAgIGNsdXN0cmVlOjpjbHVzdHJlZSh4ID0gc29iaiwgcHJlZml4ID0gJ1JOQV9zbm5fcmVzLicpCiAgICAKICAgICMjIFVubG9hZGluZyB0aGUgY2x1c3RyZWUgcGFja2FnZQogICAgZGV0YWNoKCdwYWNrYWdlOmNsdXN0cmVlJywgdW5sb2FkID0gVFJVRSkKICAgIAogICAgYGBgCgojIyMjIENlbGwtdHlwZSBtYXJrZXJzCgpXZSBjYW4gcGxvdCB0aGUgY2VsbCBtYXJrZXJzIHdlIGFscmVhZHkga25vdwoKYGBge3IgdV9tYXJrZXJzLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTJ9CiMgdV9tYXJrZXJzCgojIyBNdWx0aXBsZSBGZWF0dXJlUGxvdHMgd2l0aCBtYXJrZXJzClNldXJhdDo6RmVhdHVyZVBsb3Qob2JqZWN0ID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSB0ZDNhX21hcmtlcnMpICsgCiAgcGF0Y2h3b3JrOjpwbG90X2xheW91dChucm93ID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMykKCmBgYAoKPGJyPgoKSnVzdCBmb3IgImZ1biIgOiB3aGF0IHdvdWxkIGhhdmUgYmVlbiBzZWVuIG9uIGEgUENBID8gKERFTU8pCgpgYGB7ciB1X21hcmtlcnNfcGNhLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTIsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KIyB1X21hcmtlcnNfcGNhCgojIyBNdWx0aXBsZSBGZWF0dXJlUGxvdHMgd2l0aCBtYXJrZXJzClNldXJhdDo6RmVhdHVyZVBsb3Qob2JqZWN0ID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSB0ZDNhX21hcmtlcnMsIAogICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJwY2EiKSArIAogIHBhdGNod29yazo6cGxvdF9sYXlvdXQobnJvdyA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDMpCgpgYGAKCjxicj4KCiMjIyBDbHVzdGVyLXNwZWNpZmljIG1hcmtlcnMKCkEgcHJhY3RpY2FsIHdheSB0byBjaGFyYWN0ZXJpemUgb3VyIGNsdXN0ZXJpbmcgcmVzdWx0cyBpcyB0byBnZXQgYmFjayB0byBhIGxldmVsIG9mIGtub3dsZWRnZSB5b3UgYXJlIGNvbmZpZGVudCBpbiA6IG1hcmtlciBnZW5lcy4KClNldXJhdCBoYXMgYSBoYW5keSBmdW5jdGlvbiB0byA6CgotICAgSWRlbnRpZnkgZGlmZmVyZW50aWFsIGV4cHJlc3NlZCBnZW5lcyBzcGVjaWZpYyB0byBlYWNoIGFuZCBldmVyeSBwcm92aWRlZCBjYXRlZ29yeSBvZiBjZWxscyAoaGVyZSwgY2x1c3RlcmluZyByZXN1bHRzKQoKLSAgIERyYXcgYSBjbHVzdGVyaXplZCwgYW5ub3RhdGVkIGhlYXRtYXAgb2YgdGhlc2UgZ2VuZXMKCmBgYHtyIGhfZmFtLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyIsIGV2YWwgPSBGQUxTRX0KIyBoX2ZhbQoKP1NldXJhdDo6RmluZEFsbE1hcmtlcnMKCmBgYAoKYGBge3IgZGhtX3ByZXAsIGZpZy53aWR0aD0yNCwgZmlnLmhlaWdodD04fQojIGRobV9wcmVwCgojIyBMb29waW5nIG9uIGNsdXN0ZXJpbmcgcmVzdWx0cwpmYW1fYWxsIDwtIGxhcHBseShyZXNvbF9uYW1lcywgZnVuY3Rpb24ocikgewogIAogICMjIEZpbmQgbWFya2VycyBmb3IgYWxsIGNsdXN0ZXJzCiAgIyMgQSBzZWVkIGlzIG5lZWRlZCBoZXJlICEKICBTZXVyYXQ6OklkZW50cyhvYmplY3QgPSBzb2JqKSA8LSBzb2JqW1tyXV1bWzFdXQogIGZhbSA8LSBTZXVyYXQ6OkZpbmRBbGxNYXJrZXJzKAogICAgb2JqZWN0ID0gc29iaiwgCiAgICBsb2dmYy50aHJlc2hvbGQgPSAuNSwgCiAgICBvbmx5LnBvcyA9IFRSVUUsIAogICAgbWluLnBjdCA9IC41LCAKICAgIHZlcmJvc2UgPSBGQUxTRSwKICAgIHJhbmRvbS5zZWVkID0gbXlfc2VlZCkKICAKICAjIyBHZXQgdG9wIGdlbmUgcGVyIGNsdXN0ZXIKICBmYW10b3AgPC0gZmFtJGdlbmVbIWR1cGxpY2F0ZWQoZmFtJGNsdXN0ZXIpXQogIHZsbl9wbCA8LSBTZXVyYXQ6OlZsblBsb3Qob2JqZWN0ID0gc29iaiwgZmVhdHVyZXMgPSBmYW10b3ApCiAgcHJpbnQodmxuX3BsKQogIAogICMjIFNlbGVjdCB0b3AxMCBnZW5lcyB3aGVuIGF2YWlsYWJsZQogIGZhbV9yZHggPC0gZHBseXI6Omdyb3VwX2J5KC5kYXRhID0gZmFtLCBjbHVzdGVyKQogIGZhbV9yZHggPC0gZHBseXI6OmZpbHRlciguZGF0YSA9IGZhbV9yZHgsIGF2Z19sb2cyRkMgPiAxKQogIGZhbV9yZHggPC0gZHBseXI6OnNsaWNlX2hlYWQoLmRhdGEgPSBmYW1fcmR4LCBuID0gMTApCiAgZGggPC0gU2V1cmF0OjpEb0hlYXRtYXAoCiAgICBvYmplY3QgPSBzb2JqLCBmZWF0dXJlcyA9IGZhbV9yZHgkZ2VuZSwgc2l6ZSA9IDMsCiAgICBjb21iaW5lID0gVFJVRSkgKyBnZ3Bsb3QyOjpnZ3RpdGxlKGxhYmVsID0gcikKICByZXR1cm4oZGgpCn0pCgoKYGBgCgpgYGB7ciBkaG1fcGxvdCwgZmlnLndpZHRoPTI0LCBmaWcuaGVpZ2h0PTEyfQojIGRobV9wbG90CgojIyBQbG90IGFsbCBoZWF0bWFwcyBhdCBvbmNlCnBhdGNod29yazo6d3JhcF9wbG90cyhmYW1fYWxsKSArIHBhdGNod29yazo6cGxvdF9sYXlvdXQobnJvdyA9IDIpCgpgYGAKCjxicj4KCioqUXVlc3Rpb25zKiogOiBDb21wYXJpbmcgdGhlIGhlYXRtYXBzIDoKCmBgYHtyIHFfaG0xLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQojIHFfaG0xCgpXaGljaCByZXNvbHV0aW9uIHdvdWxkIHlvdSBjaG9vc2UsIGFuZCB3aHkgPwoKYGBgCgo8YnI+CgpgYGB7ciBxX2htMiwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX2htMgoKSXMgdGhlcmUgYSBzaW5nbGUgb25lIGFuZCBvbmx5IGFuc3dlciB0byB0aGUgZm9ybWVyIHF1ZXN0aW9uID8KCmBgYAoKPGJyPgoKIyMjIENsdXN0ZXJzIGNvbnRpbmdlbmNpZXMgYW5kIHByb3BvcnRpb25zCgpPbmUgY2FuIG9ic2VydmUgaG93IG1hbnkgY2VsbHMgYXJlIGluIGVhY2ggY2x1c3RlciwgYW5kIHdoYXQgcHJvcG9ydGlvbiBvZiBhbGwgY2VsbHMgdGhlc2UgcmVwcmVzZW50IChERU1PKQoKYGBge3IgY2x1c3RfcG9wLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CiMgY2x1c3Rwb3AKCiMjIExvb3Bpbmcgb24gcmVzb2x1dGlvbnMKZm9yICh4IGluIHJlc29sX25hbWVzKSB7CiAgIyMgQ29udGluZ2VuY2llcwogIHByaW50KHRhYmxlKHNvYmpbW3hdXSkpCiAgIyMgUHJvcG9ydGlvbnMKICBwcmludChmb3JtYXQodGFibGUoc29ialtbeF1dKSAvIG5jb2woc29iaiksIGRpZ2l0cyA9IDIpKQogIGNhdCgnXG5cbicpCn0KCmBgYAoKPGJyPgoKCiMjIFNlbGVjdGlvbgoKRm9yIHRoZSBkb3duc3RyZWFtIGFuYWx5c2VzLCB3ZSB3aWxsIHVzZSB0aGUgcmVzb2x1dGlvbiAqKmAwLjhgKiouCgpXZSB3aWxsIGZpeCB0aGUgY2x1c3RlcmluZyByZXN1bHRzIGZvciB0aGlzIHJlc29sdXRpb24gYXMgdGhlIGRlZmF1bHQgb25lClNldXJhdCB3aWxsIHVzZSBmb3IgYW55IGZ1cnRoZXIgYW5hbHlzZXMgLyBwbG90cywgdXNpbmcgdGhlIGBTZXVyYXQ6OklkZW50cygpYApmdW5jdGlvbi4KCmBgYHtyIHNlbF9yZXMsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9CiMgc2VsX3JlcwoKIyMgRml4aW5nIGxfcmVzCmxfcmVzIDwtIC44CgojIyBQZXJmb3JtaW5nIExvdXZhaW4gY2x1c3RlcmluZyBhdCB0aGUgc2VsZWN0ZWQgcmVzb2x1dGlvbgpzb2JqIDwtIFNldXJhdDo6RmluZENsdXN0ZXJzKAogIG9iamVjdCA9IHNvYmosIAogIHJlc29sdXRpb24gPSBsX3JlcywKICB2ZXJib3NlID0gRkFMU0UpCgojIyBDaGVjayBvbiBkZWZhdWx0ICJJZGVudHMiCmlkZW50aWNhbCgKICB4ID0gU2V1cmF0T2JqZWN0OjpGZXRjaERhdGEoCiAgICBvYmplY3QgPSBzb2JqLCAKICAgIHZhcnMgPSBwYXN0ZTAoIlJOQV9zbm5fcmVzLiIsIGxfcmVzKSlbWzFdXSwKICB5ID0gdW5uYW1lKFNldXJhdDo6SWRlbnRzKG9iamVjdCA9IHNvYmopKQopCgojIyBEaW1QbG90IHdpdGhvdXQgc3BlY2lmeWluZyB0aGUgcmVzb2x1dGlvbgpTZXVyYXQ6OkRpbVBsb3QoCiAgb2JqZWN0ID0gc29iaiwgCiAgcmVkdWN0aW9uID0gInVtYXAiLCAKICBsYWJlbCA9IFRSVUUsIAogIHJlcGVsID0gVFJVRSkKCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMgU2F2ZSB0aGUgU2V1cmF0IG9iamVjdAoKV2Ugd2lsbCBzYXZlIG91ciBTZXVyYXQgb2JqZWN0IHRoYXQgbm93IGNvbnRhaW5zIG91ciBjbHVzdGVyaW5nIHJlc3VsdHMgIDoKCmBgYHtyIHNhdmVyZHMyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIHNhdmVyZHMyCgojIyBTYXZlIG91ciBTZXVyYXQgb2JqZWN0IChyaWNoIG5hbWluZykKb3V0X25hbWUgPC0gcGFzdGUwKAogICAgICAgICAgb3V0cHV0X2RpciwgIi8iLCBwYXN0ZSgKICAgICAgICAgICAgYygiMTAiLCBTZXVyYXQ6OlByb2plY3Qoc29iaiksICJTNSIsIAogICAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgICJDbHVzdGVyZWQuIiwKICAgICAgICAgICAgICAgIGxfcmVzKSwgCiAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICBkaW0oc29iaiksIAogICAgICAgICAgICAgICAgY29sbGFwc2UgPSAnLicKICAgICAgICAgICAgICApCiAgICAgICAgICAgICksIGNvbGxhcHNlID0gIl8iKSwKICAgICAgICAgICAgIi5SRFMiKQoKIyMgQ2hlY2sKcHJpbnQob3V0X25hbWUpCgojIyBXcml0ZSBvbiBkaXNrCnNhdmVSRFMob2JqZWN0ID0gc29iaiwgCiAgICAgICAgZmlsZSA9IG91dF9uYW1lKQpgYGAKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+PGJyPjxicj4KCiMgUnNlc3Npb24KCmBgYHtyIHJzZXNzaW9uLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CiMgcnNlc3Npb24KCnV0aWxzOjpzZXNzaW9uSW5mbygpCgpgYGAK