1 PREAMBLE

1.1 Purpose of this session

This file describes the different steps to perform fifth part of data processing for the single cell RNAseq data analysis training course for the EBAII n1 2024, 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 :
## Seed for the RNG
my_seed <- 1337L

## Dimensions to keep from dimension reduction
n_dim <- 20

## Resolution for Louvain clustering
l_res <- .8


4 Prepare the data structure

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

4.1 Main directory

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

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

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

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

4.2 Current session

## Creating the session (Preproc.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

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

## 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
## 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)
  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/2422_ebaii_n1/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)

?Seurat::RunPCA

Questions : ⭍⭍ Lightning quizz ⭍⭍ :

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


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


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


## . Data from the scale.data layer will be used
##
## . This is unfortunately not explicit
##   from the Seurat::RunPCA help page !


Perform PCA on our data

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


Description :

SC.helper::SeuratObject_descriptor(sobj = sobj, describe = 'dimred')
Show output
OBJECT VERSION :    5.0.2 
PROJECT :   [TD3A] 

[DIMREDS]
   DIMRED 1 : [pca]  Dims:[4035 x 50] 


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

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

Give us your interpretation / feelings from this plot !


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 ?

?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 :

    Do you have an idea about this number ?


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

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

?Seurat::ElbowPlot


Apply on our data :

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


Question :

Any more precise idea, now ?


## . 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 4 different amounts of retained PCs : 3, 7, 23 and 49.

## PCA max dimensions to evaluate
pca_dims <- c(3, 7, 23, 49)

## Define a function to compute the UMAP
pca_dim_eval <- function(object = NULL, dim.max = 2, my_seed = 1337L) {
  object <- Seurat::RunUMAP(
    object = object, assay = "RNA", 
    graph.name = "RNA_snn", 
    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))
  
  ## 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) {
                         message("Dim : ", p)
                         pca_dim_eval(object = sobj, 
                                      dim.max = p,
                                      my_seed = my_seed)
                       })

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


Question

Any more precise idea, now, FOR REAL ?


## . Very few PCs are not able to retrieve
##   a sufficiently defined structure.
##
## . The differences between 25 and 49 are
##   limited in the global structure.
##   This may imply that the additional
##   components above 25 do not add more 
##   information (neither more noise, here).


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.

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


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 20` PCs :

## UMAP from 25 PCs, 3 components requested
sobj <- 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)

## DimPlot of the first 2 UMAP components
Seurat::DimPlot(
  object = sobj, 
  dims = c(1,2),
  reduction = 'umap3d')
Show plot


Question :

Isn't there something striking ?


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


Let’s perform a 3D representation of our UMAP

## Structure data to plot in a data.frame
df3d <- as.data.frame(
  Seurat::Reductions(object = sobj, 
                     slot = "umap3d")@cell.embeddings
  )

## 3D plot
plotly::plot_ly(
  data = df3d, 
  x = ~umap3d_1, 
  y = ~umap3d_2, 
  z = ~umap3d_3, 
  type = 'scatter3d', 
  marker = list(size = 2, width=2))






8 Save the Seurat object

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

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

Any idea why ?


9.1 Find neighbours

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

## 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 3 different resolutions

  • The Seurat function to perform clustering can be called with multiple resolutions at once.

## Louvain resolutions to test
resol <- c(.3, 0.8, 1.5)

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


Question

Could you tell us what changed in our object ?


## One can just call it :
sobj
Show output
An object of class Seurat 
12508 features across 4035 samples within 1 assay 
Active assay: RNA (12508 features, 2000 variable features)
 3 layers present: counts, data, scale.data
 3 dimensional reductions calculated: pca, umap, umap3d
### Hmmm, nothing new under the sun ...

## One can describe it :
SC.helper::SeuratObject_descriptor(
  sobj = sobj,
  describe = "coldata")
Show output
OBJECT VERSION :    5.0.2 
PROJECT :   [TD3A] 

[GRAPHS]
  RNA_nn 
  RNA_snn 

[BARCODES METADATA]
orig.ident    Freq
-----------  -----
TD3A          4035
NA               0

 nCount_RNA 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1000    2033    2397    3497    2998   48866 

 nFeature_RNA 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    750    1316    1476    1638    1690    5968 

 log10_nCount_RNA 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  3.000   3.309   3.380   3.438   3.477   4.689 
nCount_RNA_in_range    Freq
--------------------  -----
TRUE                   4035
NA                        0
nFeature_RNA_in_range    Freq
----------------------  -----
TRUE                     4035
NA                          0

 percent_mt 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
0.001801 0.019608 0.024401 0.025177 0.029933 0.050000 

 percent_rb 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.02687 0.08010 0.09640 0.11062 0.12154 0.42010 

 percent_st 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.01546 0.02986 0.03364 0.03423 0.03778 0.05980 
percent_mt_in_range    Freq
--------------------  -----
TRUE                   4035
NA                        0
percent_rb_in_range    Freq
--------------------  -----
TRUE                   4035
NA                        0
percent_st_in_range    Freq
--------------------  -----
TRUE                   4035
NA                        0

 CC_Seurat_S.Score 
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
-0.233844 -0.105613 -0.058412 -0.022117 -0.001082  1.232905 

 CC_Seurat_G2M.Score 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.22728 -0.11205 -0.07244 -0.04314 -0.02550  1.35266 
CC_Seurat_Phase    Freq
----------------  -----
G1                 2718
G2M                 435
S                   882
NA                    0

 CC_Seurat_SmG2M.Score 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-1.16787 -0.04463  0.01571  0.02102  0.08349  0.96435 
doublet_scds.hybrid    Freq
--------------------  -----
FALSE                  4035
NA                        0
doublet_scDblFinder    Freq
--------------------  -----
FALSE                  4035
NA                        0
doublet_union    Freq
--------------  -----
FALSE            4035
NA                  0
doublet_viz    Freq
------------  -----
both              0
scDblFinder       0
scds              0
singlet        4035
NA                0
RNA_snn_res.0.3    Freq
----------------  -----
0                  1989
1                   873
2                   418
3                   204
4                   171
5                   159
6                   118
7                   103
NA                    0
RNA_snn_res.0.8    Freq
----------------  -----
0                   931
1                   724
2                   680
3                   480
4                   417
5                   218
6                   203
7                   159
8                   119
9                   104
NA                    0
RNA_snn_res.1.5    Freq
----------------  -----
0                   455
1                   455
2                   414
3                   372
4                   371
5                   337
6                   301
7                   257
8                   241
9                   210
10                  201
11                  159
12                  119
13                  104
14                   39
NA                    0
seurat_clusters    Freq
----------------  -----
0                   455
1                   455
2                   414
3                   372
4                   371
5                   337
6                   301
7                   257
8                   241
9                   210
10                  201
11                  159
12                  119
13                  104
14                   39
NA                    0


9.3 Visualization & selection

9.3.1 On UMAPs

Plotting UMAPs harboring the clustering results for our 3 tested resolutions

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

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


9.3.2 Clusters contingencies and proportions

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

for (x in resol_names) {
  ## Contingencies
  print(table(sobj[[x]]))
  ## Proportions
  print(format(table(sobj[[x]]) / ncol(sobj), digits = 2))
  cat('\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.8
  0   1   2   3   4   5   6   7   8   9 
931 724 680 480 417 218 203 159 119 104 
RNA_snn_res.0.8
      0       1       2       3       4       5       6       7       8       9 
"0.231" "0.179" "0.169" "0.119" "0.103" "0.054" "0.050" "0.039" "0.029" "0.026" 

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" 


9.3.3 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

?Seurat::FindAllMarkers
## Looping on clustering results
fma_all <- lapply(resol_names, function(r) {
  
  ## Find markers for all clusters
  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)
  
  ## 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, combine = TRUE) + ggplot2::ggtitle(label = r)
  return(dh)
})

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


Questions : Comparing the heatmaps :

Which resolution would you choose, and why ?


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


9.4 Selection

For the downstream analyses, we will use the resolution 0.8

Seurat::Idents(object = sobj) <- sobj[[paste0("RNA_snn_res.", l_res)]][[1]]





10 Save the Seurat object

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

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

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

Matrix products: default
BLAS/LAPACK: /shared/ifbstor1/software/miniconda/envs/r-4.4.1/lib/libopenblasp-r0.3.27.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     

loaded via a namespace (and not attached):
  [1] matrixStats_1.4.1           spatstat.sparse_3.1-0      
  [3] SC.helper_0.0.6             httr_1.4.7                 
  [5] RColorBrewer_1.1-3          doParallel_1.0.17          
  [7] alabaster.base_1.4.1        tools_4.4.1                
  [9] sctransform_0.4.1           backports_1.5.0            
 [11] utf8_1.2.4                  R6_2.5.1                   
 [13] HDF5Array_1.32.0            lazyeval_0.2.2             
 [15] uwot_0.2.2                  rhdf5filters_1.16.0        
 [17] GetoptLong_1.0.5            withr_3.0.1                
 [19] sp_2.1-4                    gridExtra_2.3              
 [21] progressr_0.14.0            cli_3.6.3                  
 [23] Biobase_2.64.0              spatstat.explore_3.3-2     
 [25] fastDummies_1.7.4           labeling_0.4.3             
 [27] alabaster.se_1.4.1          sass_0.4.9                 
 [29] Seurat_5.1.0                spatstat.data_3.1-2        
 [31] ggridges_0.5.6              pbapply_1.7-2              
 [33] foreign_0.8-86              parallelly_1.38.0          
 [35] limma_3.60.6                rstudioapi_0.17.0          
 [37] RSQLite_2.3.7               generics_0.1.3             
 [39] shape_1.4.6.1               ica_1.0-3                  
 [41] spatstat.random_3.3-2       dplyr_1.1.4                
 [43] Matrix_1.7-1                fansi_1.0.6                
 [45] S4Vectors_0.42.1            abind_1.4-8                
 [47] lifecycle_1.0.4             SoupX_1.6.2                
 [49] yaml_2.3.10                 edgeR_4.2.2                
 [51] SummarizedExperiment_1.34.0 rhdf5_2.48.0               
 [53] SparseArray_1.4.8           BiocFileCache_2.12.0       
 [55] Rtsne_0.17                  grid_4.4.1                 
 [57] blob_1.2.4                  promises_1.3.0             
 [59] dqrng_0.4.1                 ExperimentHub_2.12.0       
 [61] crayon_1.5.3                miniUI_0.1.1.1             
 [63] lattice_0.22-6              beachmat_2.20.0            
 [65] cowplot_1.1.3               KEGGREST_1.44.0            
 [67] pillar_1.9.0                knitr_1.48                 
 [69] ComplexHeatmap_2.20.0       metapod_1.12.0             
 [71] GenomicRanges_1.56.2        rjson_0.2.21               
 [73] future.apply_1.11.2         codetools_0.2-20           
 [75] leiden_0.4.3.1              glue_1.8.0                 
 [77] spatstat.univar_3.0-1       data.table_1.16.2          
 [79] gypsum_1.0.1                vctrs_0.6.5                
 [81] png_0.1-8                   spam_2.11-0                
 [83] gtable_0.3.5                cachem_1.1.0               
 [85] xfun_0.48                   S4Arrays_1.4.1             
 [87] mime_0.12                   survival_3.7-0             
 [89] SingleCellExperiment_1.26.0 iterators_1.0.14           
 [91] statmod_1.5.0               bluster_1.14.0             
 [93] fitdistrplus_1.2-1          ROCR_1.0-11                
 [95] nlme_3.1-165                bit64_4.5.2                
 [97] alabaster.ranges_1.4.1      filelock_1.0.3             
 [99] RcppAnnoy_0.0.22            GenomeInfoDb_1.40.1        
[101] bslib_0.8.0                 irlba_2.3.5.1              
[103] KernSmooth_2.23-24          rpart_4.1.23               
[105] colorspace_2.1-1            BiocGenerics_0.50.0        
[107] DBI_1.2.3                   Hmisc_5.1-3                
[109] celldex_1.14.0              nnet_7.3-19                
[111] tidyselect_1.2.1            bit_4.5.0                  
[113] compiler_4.4.1              curl_5.2.3                 
[115] httr2_1.0.1                 htmlTable_2.4.2            
[117] BiocNeighbors_1.22.0        DelayedArray_0.30.1        
[119] plotly_4.10.4               checkmate_2.3.1            
[121] scales_1.3.0                lmtest_0.9-40              
[123] rappdirs_0.3.3              stringr_1.5.1              
[125] digest_0.6.37               goftest_1.2-3              
[127] spatstat.utils_3.1-0        alabaster.matrix_1.4.1     
[129] rmarkdown_2.28              XVector_0.44.0             
[131] htmltools_0.5.8.1           pkgconfig_2.0.3            
[133] base64enc_0.1-3             SingleR_2.6.0              
[135] sparseMatrixStats_1.16.0    MatrixGenerics_1.16.0      
[137] highr_0.11                  dbplyr_2.5.0               
[139] fastmap_1.2.0               rlang_1.1.4                
[141] GlobalOptions_0.1.2         htmlwidgets_1.6.4          
[143] UCSC.utils_1.0.0            shiny_1.9.1                
[145] DelayedMatrixStats_1.26.0   farver_2.1.2               
[147] jquerylib_0.1.4             zoo_1.8-12                 
[149] jsonlite_1.8.9              BiocParallel_1.38.0        
[151] BiocSingular_1.20.0         magrittr_2.0.3             
[153] Formula_1.2-5               scuttle_1.14.0             
[155] GenomeInfoDbData_1.2.12     dotCall64_1.2              
[157] patchwork_1.3.0             Rhdf5lib_1.26.0            
[159] munsell_0.5.1               Rcpp_1.0.13                
[161] reticulate_1.39.0           alabaster.schemas_1.4.0    
[163] stringi_1.8.4               zlibbioc_1.50.0            
[165] MASS_7.3-61                 AnnotationHub_3.12.0       
[167] plyr_1.8.9                  parallel_4.4.1             
[169] listenv_0.9.1               ggrepel_0.9.6              
[171] deldir_2.0-4                Biostrings_2.72.1          
[173] splines_4.4.1               tensor_1.5                 
[175] circlize_0.4.16             locfit_1.5-9.9             
[177] igraph_2.1.1                spatstat.geom_3.3-3        
[179] RcppHNSW_0.6.0              reshape2_1.4.4             
[181] stats4_4.4.1                ScaledMatrix_1.12.0        
[183] BiocVersion_3.19.1          evaluate_1.0.1             
[185] SeuratObject_5.0.2          scran_1.32.0               
[187] BiocManager_1.30.25         foreach_1.5.2              
[189] httpuv_1.6.15               RANN_2.6.2                 
[191] tidyr_1.3.1                 purrr_1.0.2                
[193] polyclip_1.10-7             future_1.34.0              
[195] clue_0.3-65                 scattermore_1.2            
[197] ggplot2_3.5.1               rsvd_1.0.5                 
[199] xtable_1.8-4                RSpectra_0.16-2            
[201] later_1.3.2                 viridisLite_0.4.2          
[203] tibble_3.2.1                memoise_2.0.1              
[205] AnnotationDbi_1.66.0        IRanges_2.38.1             
[207] cluster_2.1.6               globals_0.16.3             
LS0tCnRpdGxlOiAiPENFTlRFUj5FQkFJSSBuMSAyMDI0IDogU0lOR0xFIENFTEwgQU5BTFlTSVMgVFJBSU5JTkc8QlI+IDxCPlBST0NFU1NJTkcgKElJKTwvQj48QlI+RGltZW5zaW9uIHJlZHVjdGlvbiAmIHZpc3VhbGl6YXRpb248L0NFTlRFUj4iCmRhdGU6ICIyMDI0LTExLTE3LjIyIgphdXRob3I6ICJFQkFJSSBuMSBzY1JOQXNlcSBUZWFtIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICBiYWNrZ3JvdW5kOiBibGFjawogICAgZmlnX2hlaWdodDogNgogICAgZmlnX3dpZHRoOiA4CiAgICBoaWdobGlnaHQ6IHRhbmdvICAjIyBUaGVtZSBmb3IgdGhlIGNvZGUgY2h1bmtzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUgICMjIEFkZHMgbnVtYmVyIHRvIGhlYWRlcnMgKHNlY3Rpb25zKQogICAgdGhlbWU6IGZsYXRseSAgIyMgQ1NTIHRoZW1lIGZvciB0aGUgSFRNTCBwYWdlCiAgICB0b2M6IHRydWUgICMjIEFkZHMgYSB0YWJsZSBvZiBjb250ZW50CiAgICB0b2NfZmxvYXQ6ICAjIyBUT0Mgb3B0aW9ucwogICAgICBjb2xsYXBzZWQ6IHRydWUgICMjIEJ5IGRlZmF1bHQsIHRoZSBUT0MgaXMgZm9sZGVkCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUgIyMgU21vb3RoIHNjcm9sbCBvZiB0aGUgSFRNTCBwYWdlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZSAjIyBJbmNsdWRlcyBhbGwgcGxvdHMvaW1hZ2VzIHdpdGhpbiB0aGUgSFRNTAogICAgY29kZV9kb3dubG9hZDogdHJ1ZSAjIyBBZGRzIGEgYnV0dG9uIHRvIGRvd25sb2FkIHRoZSBSbWQKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdGh1bWJuYWlsczogZmFsc2UKICAgIGxpZ2h0Ym94OiB0cnVlCiAgICBmaWdfY2FwdGlvbjogZmFsc2UKICAgIGdhbGxlcnk6IHRydWUKICAgIHVzZV9ib29rZG93bjogdHJ1ZQphbHdheXNfYWxsb3dfaHRtbDogdHJ1ZSAjIyBBbGxvdyBwbGFpbiBIVE1MIGNvZGUgaW4gdGhlIFJtZAplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgoKPCEtLSBBZGQgdGhlIFJvc2NvZmYgYmFubmVyIC0tPgoKYGBge2NzcywgZWNobyA9IEZBTFNFfQpib2R5IHsKICBiYWNrZ3JvdW5kLWltYWdlOiB1cmwoJ2ViYWlpX2Jhbm5lci5wbmcnKTsKICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0OwogIGJhY2tncm91bmQtc2l6ZTogMTAwJTsKfQoKZGl2IHsKICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1NSwgMjU1LCAyNTUsIDAuMzUpICAgLyogMzUlIG9wYXF1ZSB3aGl0ZSAqLzsKICBwYWRkaW5nOiAwLjI1ZW07Cn0KYGBgCgo8IS0tIEFsbG93cyB0byBoaWRlIHRoZSBUT0MgYnkgZGVmYXVsdCwgZGlzcGxheSBpdCB3aXRoIGEgYnV0dG9uLCBtb3ZlIGl0IHRvIHRoZSByaWdodCBvciBsZWZ0IG9mIHRoZSBwYWdlIC0tPgpgciBIbWlzYzo6aGlkaW5nVE9DKGJ1dHRvbkxhYmVsID0gJ1Nob3cgVE9DJywgaGlkZGVuID0gVFJVRSwgdG9jU2lkZSA9ICdsZWZ0JywgYnV0dG9uU2lkZT0nbGVmdCcsIHBvc0NvbGxhcHNlID0gJ21hcmdpbicsIGxldmVscyA9IDMpYAoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIG9wdGlvbnMod2lkdGggPSA2MCk7CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwgICAgICAgICMgUHJpbnQgdGhlIGNvZGUKICBldmFsID0gVFJVRSwgICAgICAgIyBEbyBub3QgcnVuIGNvbW1hbmQgbGluZXMKICBtZXNzYWdlID0gRkFMU0UsICAgICMgUHJpbnQgbWVzc2FnZXMKICBwcm9tcHQgPSBGQUxTRSwgICAgICMgRG8gbm90IGRpc3BsYXkgcHJvbXB0CiAgY29tbWVudCA9IE5BLCAgICAgICAjIE5vIGNvbW1lbnRzIG9uIHRoaXMgc2VjdGlvbgogIHdhcm5pbmcgPSBGQUxTRSwgICAgIyBEaXNwbGF5IHdhcm5pbmdzCiAgdGlkeSA9IEZBTFNFLAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogICMgcmVzdWx0cyA9ICdoaWRlJwogIHdpZHRoID0gMTAwICAgICAgICMgTnVtYmVyIG9mIGNoYXJhY3RlcnMgcGVyIGxpbmUKKQpgYGAKCgpgYGB7ciBrbml0X2hvb2ssIGVjaG8gPSBGQUxTRX0KaG9va3MgPSBrbml0cjo6a25pdF9ob29rcyRnZXQoKQpob29rX2ZvbGRhYmxlID0gZnVuY3Rpb24odHlwZSkgewogIGZvcmNlKHR5cGUpCiAgZnVuY3Rpb24oeCwgb3B0aW9ucykgewogICAgcmVzID0gaG9va3NbW3R5cGVdXSh4LCBvcHRpb25zKQogICAgCiAgICBpZiAoaXNGQUxTRShvcHRpb25zW1twYXN0ZTAoImZvbGQuIiwgdHlwZSldXSkpIHJldHVybihyZXMpCiAgICAKICAgIHBhc3RlMCgKICAgICAgIjxkZXRhaWxzPjxzdW1tYXJ5PlNob3cgIiwgdHlwZSwgIjwvc3VtbWFyeT5cblxuIiwKICAgICAgcmVzLAogICAgICAiXG5cbjwvZGV0YWlscz4iCiAgICApCiAgfQp9CmtuaXRyOjprbml0X2hvb2tzJHNldCgKICBvdXRwdXQgPSBob29rX2ZvbGRhYmxlKCJvdXRwdXQiKSwKICBwbG90ID0gaG9va19mb2xkYWJsZSgicGxvdCIpCikKYGBgCgo8IS0tIENTUyB0byBjb2xvciBjaHVua3MgYW5kIG91dHB1dHMgLS0+CgpgYGB7Y3NzLCBlY2hvPUZBTFNFfQoubm90cnVuIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBsaWdodGdyZXkgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBibGFjayAhaW1wb3J0YW50Owp9Ci5ub3RydW5vIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBsaWdodGdyZXkgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLnF1ZXN0aW9uIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBhcXVhbWFyaW5lICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGxpbWVncmVlbiAhaW1wb3J0YW50Owp9Ci5xdWVzdGlvbm8gewogIGJhY2tncm91bmQtY29sb3I6IGFxdWFtYXJpbmUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLmFuc3dlciB7CiAgYmFja2dyb3VuZC1jb2xvcjogbmF2YWpvd2hpdGUgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBicm93biAhaW1wb3J0YW50Owp9Ci5hbnN3ZXJvIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBuYXZham93aGl0ZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQouYmV5b25kIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB2aW9sZXQgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBwdXJwbGUgIWltcG9ydGFudDsKfQouYmV5b25kbyB7CiAgYmFja2dyb3VuZC1jb2xvcjogdmlvbGV0ICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9CmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUFJFQU1CTEUKCiMjIFB1cnBvc2Ugb2YgdGhpcyBzZXNzaW9uCgpUaGlzIGZpbGUgZGVzY3JpYmVzIHRoZSBkaWZmZXJlbnQgc3RlcHMgdG8gcGVyZm9ybSAqKmZpZnRoKiogcGFydCBvZiBkYXRhIHByb2Nlc3NpbmcgZm9yIHRoZSBzaW5nbGUgY2VsbCBSTkFzZXEgZGF0YSBhbmFseXNpcyB0cmFpbmluZyBjb3Vyc2UgZm9yIHRoZSAqKkVCQUlJIG4xCjIwMjQqKiwgY292ZXJpbmcgdGhlc2Ugc3RlcHMgOgoKLSAgICoqRGltZW5zaW9uIHJlZHVjdGlvbioqIG9mIHRoZSBleHByZXNzaW9uIGRhdGEKCi0gICAqKlZpc3VhbGl6YXRpb24qKiBvZiBjZWxscyBleHByZXNzaW9uIGluIGEgMi1EIHNwYWNlCgotICAgVW5zdXBlcnZpc2VkICoqY2x1c3RlcmluZyoqIG9mIGNlbGxzCgotICAgKipEZXNjcmlwdGlvbioqIG9mIHRoZSBkZWZpbmVkIGNsdXN0ZXJzCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTdGFydCBSc3R1ZGlvCgotICAgVXNpbmcgdGhlIFtPcGVuT25EZW1hbmQgY2hlYXQKICAgIHNoZWV0XShodHRwczovL2lmYi1lbGl4aXJmci5naXRodWIuaW8vRUJBSUkvMjAyMy9lYmFpaW4xL1NpbmdsZUNlbGwvMjAyNF9URF9PcGVuT25EZW1hbmQuaHRtbCksCiAgICBjb25uZWN0IHRvIHRoZSBbT3Blbk9uRGVtYW5kCiAgICBwb3J0YWxdKGh0dHBzOi8vb25kZW1hbmQuY2x1c3Rlci5mcmFuY2UtYmlvaW5mb3JtYXRpcXVlLmZyKSBhbmQKICAgICoqY3JlYXRlIGEgUnN0dWRpbyBzZXNzaW9uKiogd2l0aCB0aGUgcmlnaHQgcmVzb3VyY2UgcmVxdWlyZW1lbnRzLgoKIyBXYXJtLXVwCgotICAgV2Ugc2V0ICoqY29tbW9uIHBhcmFtZXRlcnMqKiB3ZSB3aWxsIHVzZSB0aHJvdWdob3V0IHRoaXMgc2Vzc2lvbiA6CgpgYGB7ciBzZXRwYXJhbX0KIyMgU2VlZCBmb3IgdGhlIFJORwpteV9zZWVkIDwtIDEzMzdMCgojIyBEaW1lbnNpb25zIHRvIGtlZXAgZnJvbSBkaW1lbnNpb24gcmVkdWN0aW9uCm5fZGltIDwtIDIwCgojIyBSZXNvbHV0aW9uIGZvciBMb3V2YWluIGNsdXN0ZXJpbmcKbF9yZXMgPC0gLjgKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQcmVwYXJlIHRoZSBkYXRhIHN0cnVjdHVyZQoKV2Ugd2lsbCBkbyB0aGUgc2FtZSBhcyBmb3IgZm9ybWVyIHN0ZXBzLCBqdXN0IGNoYW5naW5nIHRoZSBzZXNzaW9uIG5hbWUKOgoKIyMgTWFpbiBkaXJlY3RvcnkKCmBgYHtyIG1haW5kaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIFNldHRpbmcgdGhlIHByb2plY3QgbmFtZQpwcm9qZWN0X25hbWUgPC0gImViYWlpX3NjX3RlYWNoZXJzIiAgIyBEbyBub3QgY29weS1wYXN0ZSB0aGlzICEgSXQncyBNWSBwcm9qZWN0ICEhCgojIyBQcmVwYXJpbmcgdGhlIHBhdGgKVERfZGlyIDwtIHBhc3RlMCgiL3NoYXJlZC9wcm9qZWN0cy8iLCBwcm9qZWN0X25hbWUsICIvU0NfVEQiKQoKIyMgQ3JlYXRpbmcgdGhlIHJvb3QgZGlyZWN0b3J5CmRpci5jcmVhdGUocGF0aCA9IFREX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSByb290IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoVERfZGlyKQpgYGAKCiMjIEN1cnJlbnQgc2Vzc2lvbgoKYGBge3Igc2Vzc2lvbmRpciwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyMgQ3JlYXRpbmcgdGhlIHNlc3Npb24gKFByZXByb2MuMikgZGlyZWN0b3J5CnNlc3Npb25fZGlyIDwtIHBhc3RlMChURF9kaXIsICIvMDVfUHJvYy4yIikKZGlyLmNyZWF0ZShwYXRoID0gc2Vzc2lvbl9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgc2Vzc2lvbiBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KHNlc3Npb25fZGlyKQpgYGAKCiMjIElucHV0IGRpcmVjdG9yeQoKYGBge3IgaW5kaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIENyZWF0aW5nIHRoZSBJTlBVVCBkYXRhIGRpcmVjdG9yeQppbnB1dF9kaXIgPC0gcGFzdGUwKHNlc3Npb25fZGlyLCAiL0RBVEEiKQpkaXIuY3JlYXRlKHBhdGggPSBpbnB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgaW5wdXQgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChpbnB1dF9kaXIpCmBgYAoKIyMgT3V0cHV0IGRpcmVjdG9yeQoKYGBge3Igb3V0ZGlyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIyBDcmVhdGluZyB0aGUgT1VUUFVUIGRhdGEgZGlyZWN0b3J5Cm91dHB1dF9kaXIgPC0gcGFzdGUwKHNlc3Npb25fZGlyLCAiL1JFU1VMVFMiKQpkaXIuY3JlYXRlKHBhdGggPSBvdXRwdXRfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIG91dHB1dCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KG91dHB1dF9kaXIpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUmVsb2FkIHRoZSBTZXVyYXQgT2JqZWN0CgotICAgV2UgY2FuIHJlbG9hZCB0aGUgb2JqZWN0IHdlIHNhdmVkIGF0IHRoZSBmb3JtZXIgc3RlcAoKYGBge3IgZGF0YWxvYWR9CiMjIFRoZSBsYXRlc3QgU2V1cmF0IG9iamVjdCBzYXZlZCBhcyBSRFMgKG5hbWUpCnNvYmpfZmlsZSA8LSAiMDhfVEQzQV9TNV9TY2FsZWQuMmtfUmVnLlBDcmJfMTI1MDguNDAzNS5SRFMiCgojIyBUaGUgbGF0ZXN0IFNldXJhdCBvYmplY3Qgc2F2ZWQgYXMgUkRTIChmdWxsIHBhdGgpCnNvYmpfcGF0aCA8LSBwYXN0ZTAoVERfZGlyLCAKICAgICAgICAgICAgICAgICAgICAiLzA0X1Byb2MuMS9SRVNVTFRTLyIsCiAgICAgICAgICAgICAgICAgICAgc29ial9maWxlKQoKZm9yY2UgPC0gRkFMU0UgICMjIFRvIGZvcmNlIGEgcmUtZG93bmxvYWQgb2YgYSBaZW5vZG8taG9zdGVkIGJhY2t1cApsb2NhbCA8LSBGQUxTRSAgIyMgVG8gZm9yY2UgYSBsb2FkaW5nIGZyb20gYSBsb2NhbCBiYWNrdXAKCiMjIEluIGNhc2Ugb2YgZXJyb3IvbG9zdCBkYXRhIDogZm9yY2UgYSByZWxvYWQgZnJvbSBhIFplbm9kbyBiYWNrdXAgcmVwb3NpdG9yeQppZihmb3JjZSkgewogIHplbl9pZCA8LSAiMTQwMzUyOTMiCiAgemVuX2JhY2t1cF9maWxlIDwtIHBhc3RlMCgiaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHplbl9pZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICIvZmlsZXMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvYmpfZmlsZSkKICBkb3dubG9hZC5maWxlKHVybCA9IHplbl9iYWNrdXBfZmlsZSwKICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gc29ial9wYXRoKQp9CgojIyBJbiBjYXNlIG9mIGVycm9yL2xvc3QgZGF0YSA6IGZvcmNlIGEgcmVsb2FkIGZyb20gYSBsb2NhbCBiYWNrdXAgcmVwb3NpdG9yeQppZihsb2NhbCkgewogIHNvYmpfcGF0aCA8LSBwYXN0ZTAoCiAgICAiL3NoYXJlZC9wcm9qZWN0cy8yNDIyX2ViYWlpX24xL2F0ZWxpZXJfc2NybmFzZXEvVEQvQkFDS1VQL1JEUy8iLAogICAgc29ial9maWxlKQp9CgojIyBMb2FkIHRoZSBvYmplY3QKc29iaiA8LSByZWFkUkRTKGZpbGUgPSBzb2JqX3BhdGgpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgRGltZW5zaW9uIHJlZHVjdGlvbgoKVGhpcyBzdGVwIG9yaWdpbmF0ZXMgZnJvbSB0aGUgb2JzZXJ2YXRpb24gdGhhdCB3ZSBkbyBub3Qgd2FudCBub3IgbmVlZCB0byBjaGFyYWN0ZXJpemUgKiplYWNoKiogb2Ygb3VyICoqdGhvdXNlbmRzIG9mIGNlbGxzKiosIGJ1dCAqKmdyb3VwcyoqIG9mIHRoZW0gKGNsdXN0ZXJzID8gY2VsbCB0eXBlcyA/IG90aGVyID8pLiBUaHVzLCB3ZSBkbyBubyBuZWVkIGFsbCBkYXRhLCBhbmQgZXZlbiBtYXkgYmVuZWZpdCBmcm9tIHN1Y2ggYSByZWR1Y3Rpb24gOgoKKiBSZWR1Y2UgdGhlIGRhdGEgY29tcGxleGl0eQogICogRm9yIGludGVycHJldGF0aW9uCiAgKiBGb3IgY29tcHV0YXRpb25zCiogSW5jcmVhc2UgdGhlIHF1YWxpdHkgb2YgaW5mb3JtYXRpb24gY29udGFpbmVkIGluIHRoZSBkYXRhCiAgKiAqKkVucmljaGluZyoqICJnb29kIGJpb2xvZ2ljYWwgKipzaWduYWxzKioiCiAgKiAqKkRpc2NhcmRpbmcgbm9pc2UqKiAvIGNlbGwtc3BlY2lmaWMgc2lnbmFscwoKVGhlcmUgaXMgYSAqKm11bHRpdHVkZSBvZiBtZXRob2RzKiogZm9yIGRpbWVuc2lvbiByZWR1Y3Rpb24KPGNlbnRlcj4hW10oZGltcmVkX3RyZWUucG5nKTwvY2VudGVyPgoKCiMjIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkKCkhlcmUsIHdlIHdpbGwgdXNlIHRoZSBncmFuZC1tb3RoZXIgb2YgYWxsIDogdGhlIFBDQSAoUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcykKCmBgYHtyIGhfUnVuUENBLCBldmFsID0gRkFMU0V9Cj9TZXVyYXQ6OlJ1blBDQQpgYGAKCioqUXVlc3Rpb25zIDog4q2N4q2NIExpZ2h0bmluZyBxdWl6eiDirY3irY0gKiogOiAKCmBgYHtyIHFfcGNhMSwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KSG93IG1hbnkgcHJpbmNpcGFsIGNvbXBvbmVudHMgKFBDKSB3aWxsIGJlIGdlbmVyYXRlZCBieSBkZWZhdWx0ID8KYGBgCgo8YnI+CgpgYGB7ciBhX3BjYTEsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIyAuIFRoZSBhbnN3ZXIgaXMgNTAgKG5wY3MgcGFyYW1ldGVyKQojIwojIyAuIFdlIHdpbGwgdXNlIHRoaXMgZGVmYXVsdCB2YWx1ZS4KIyMKIyMgLiBXYXJuaW5nIDogaW4gc29tZSAocmFyZSkgY29udGV4dHMsIHRoaXMKIyMgICBtYXkgbm90IGJlIGVub3VnaCAhCmBgYAoKPGJyPgoKYGBge3IgcV9wY2EyLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQpXaGljaCBkYXRhIHR5cGUgKGllLCB3aGljaCBTZXVyYXQgb2JqZWN0IGxheWVyKSB3aWxsIGJlIHVzZWQgCnRvIGdlbmVyYXRlIHRoZSBjb21wb25lbnRzID8KYGBgCgo8YnI+CgpgYGB7ciBhX3BjYTIsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIyAuIERhdGEgZnJvbSB0aGUgc2NhbGUuZGF0YSBsYXllciB3aWxsIGJlIHVzZWQKIyMKIyMgLiBUaGlzIGlzIHVuZm9ydHVuYXRlbHkgbm90IGV4cGxpY2l0CiMjICAgZnJvbSB0aGUgU2V1cmF0OjpSdW5QQ0EgaGVscCBwYWdlICEKYGBgCgo8YnI+CgpQZXJmb3JtIFBDQSBvbiBvdXIgZGF0YQoKYGBge3IgUENBfQojIyBOb3RlIDogYSBzZWVkIGlzIHVzZWQgaGVyZSAhCnNvYmogPC0gU2V1cmF0OjpSdW5QQ0EoCiAgb2JqZWN0ID0gc29iaiwgCiAgYXNzYXkgPSAnUk5BJywgCiAgc2VlZC51c2UgPSBteV9zZWVkLCAKICB2ZXJib3NlID0gRkFMU0UpCmBgYAoKPGJyPgoKRGVzY3JpcHRpb24gOgoKYGBge3IgUENBZGVzYywgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQpTQy5oZWxwZXI6OlNldXJhdE9iamVjdF9kZXNjcmlwdG9yKHNvYmogPSBzb2JqLCBkZXNjcmliZSA9ICdkaW1yZWQnKQpgYGAKCjxicj4KClZpc3VhbGl6YXRpb24gb2YgdGhlIHZlcnkgZmlyc3QgKip0d28gY29tcG9uZW50cyoqLCB3aXRoIGNlbGxzIGNvbG9yaW5nIGFjY29yZGluZyB0byB0aGUgZXN0aW1hdGVkICoqY2VsbCBjeWNsZSBwaGFzZSoqIDoKCmBgYHtyIFBDQXBsb3R9CiMjIFNjYXR0ZXIgcGxvdCBhbG9uZyBkaW1lbnNpb25zClNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICAjIyBGaXJzdCB0d28gY29tcG9uZW50cwogIGRpbXMgPSBjKDEsMiksIAogICMjIENvbG9yIGRvdHMgcGVyIGNlbGwgcGhhc2UgZ3JvdXBzCiAgZ3JvdXAuYnkgPSAnQ0NfU2V1cmF0X1BoYXNlJywgCiAgIyMgRGF0YSB0byB1c2UKICByZWR1Y3Rpb24gPSAncGNhJykKYGBgCgo8YnI+CgojIyBRdWVzdGlvbnMKCmBgYHtyIHFfcGNhMywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KR2l2ZSB1cyB5b3VyIGludGVycHJldGF0aW9uIC8gZmVlbGluZ3MgZnJvbSB0aGlzIHBsb3QgIQpgYGAKCjxicj4KCmBgYHtyIHFfcGNhNCwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KU2hvdWxkIHdlIGxpbWl0IG91cnNlbHZlcyB0byB1c2luZyAyIGRpbWVuc2lvbnMgdG8gaW50ZXJwcmV0IG91ciBkYXRhID8KYGBgCgo8YnI+PGJyPjxicj4KCi0gICBNYXliZSB3ZSBzaG91ZCAqKnJlZHVjZSoqIGluZm9ybWF0aW9uIGEgdGFkICoqbW9yZSoqLCBqdXN0IGZvciB0aGUgc2FrZSBvZiAuLi4gCgogICAgLSAgIC4uLiB1bmRlcnN0YW5kaW5nIG91ciBkYXRhIC4uLgoKICAgIC0gICAuLi53aXRoIG91ciAqKnBvb3IgaHVtYW4gYnJhaW5zKiogLi4uCgogICAgLSAgIC4uLiBib3JuIGFuZCByYWlzZWQgaW4gYSAzRCAqKmV1Y2xpZGVhbiB3b3JsZCoqICEKCjwhLS0gQVNDSUkgQVJUIEdFTkVSQVRPUiBJJ00gV0lUSCBTVE9PUElEIC0tPgoKYGBge3Igc3Rvb3BpZCwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgIm5vdHJ1biIpfQojIwojIyAgICAgX18gICAgICAgICAgICAgIC5fX18vXCAgICAgICAgICAgICAgICAuX18gIF9fICAuX18gICAgICAgICAgICAgICAgX18gICAgICAgICAgICAgICAuX18gICAgLl9fXyAuXy4KIyMgICAgLyAvICAgX19fX19fICAgICB8ICAgKS9fX19fXyAgIF9fICBfICBffF9ffC8gIHxffCAgfF9fICAgICBfX19fX19fLyAgfF8gX18gX19fX19fX18gfF9ffCBfX3wgXy8gfCB8CiMjICAgLyAvICAgL19fX19fLyAgICAgfCAgIHwvICAgICBcICBcIFwvIFwvIC8gIFwgICBfX1wgIHwgIFwgICAvICBfX18vXCAgIF9fXCAgfCAgXF9fX18gXHwgIHwvIF9fIHwgIHwgfAojIyAgIFwgXCAgIC9fX19fXy8gICAgIHwgICB8ICBZIFkgIFwgIFwgICAgIC98ICB8fCAgfCB8ICAgWSAgXCAgXF9fXyAgXCB8ICB8IHwgIHwgIC8gIHxfPiA+ICAvIC9fLyB8ICAgXHwKIyMgICAgXF9cICAgICAgICAgICAgICB8X19ffF9ffF98ICAvICAgXC9cXy8gfF9ffHxfX3wgfF9fX3wgIC8gL19fX18gIC8gfF9ffCB8X19fXy98ICAgX18vfF9fXF9fX18gfCAgIF9fCiMjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwvICAgICAgICAgICAgICAgICAgICAgICAgXC8gICAgICAgXC8gICAgICAgICAgICAgfF9ffCAgICAgICAgICAgXC8gICBcLwojIwpgYGAKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBWaXN1YWxpemF0aW9uCgpUaGlzIGZpbmFsIHByb2Nlc3Npbmcgc3RlcCBuZWVkIHRvIGZpbmFsbHkgKipvYnNlcnZlKiogb3VyIGRhdGEgcmVxdWlyZXMgYSBub3ZlbCBkaW1lbnNpb24gcmVkdWN0aW9uIG1ldGhvZCB3aXRoIGEgdmVyeSBoaWdoIGNoYWxsZW5nZSB0byBvdmVyY29tZSA6IHJlZHVjZSBhIHNwYWNlIG9mIGRvemVucyBvZiBkaW1lbnNpb25zIHRvICoqanVzdCBhIGZldyoqICEKCldlIHdpbGwgdXNlIHRoZSBbKipVTUFQKipdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1VNQVApe3RhcmdldD0iX2JsYW5rIn0gbWV0aG9kLgoKIyMgVW5pZm9ybSBNYW5pZm9sZCBBcHByb3hpbWF0aW9uIGFuZCBQcm9qZWN0aW9uIChVTUFQKQoKSG93ID8KCmBgYHtyIGh1bWFwLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyIsIGV2YWwgPSBGQUxTRX0KP1NldXJhdDo6UnVuVU1BUApgYGAKCiMjIyBTZWxlY3QgZGltZW5zaW9ucwoKV2UgZ2VuZXJhdGVkICoqNTAgUENBIGNvbXBvbmVudHMqKiBmcm9tIG91ciB+MTIgSyBmZWF0dXJlcwoKLSAgIFRoZXNlIDUwIGRpbWVuc2lvbnMgbWF5ICoqbm90IGFsbCoqIGNvbnRhaW4gKip2YWx1YWJsZSoqIGluZm9ybWF0aW9uCgotICAgV2Ugc2hvdWxkIHRyeSBkbyAqKnNlbGVjdCB0aGUgbW9zdCB1c2VmdWwqKiBvbmVzIGFuZCAqKmRpc2NhcmQqKiB0aGUgcmVtYWluaW5nICoqbm9pc2UqKgoKLSAgIEJ1dCAqKmhvdyBtYW55Kiogc2hvdWxkIHdlIGtlZXAgPwoKLSAgICoqUXVlc3Rpb24qKiA6IAoKICAgIGBgYHtyIHFfbmRpbTEsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CiAgICBEbyB5b3UgaGF2ZSBhbiBpZGVhIGFib3V0IHRoaXMgbnVtYmVyID8KICAgIGBgYAogICAgCiAgICA8YnI+CgogICAgYGBge3Igcl9uZGltMSwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiAgICAjIyAuIEltcG9zc2libGUgdG8gZ3Vlc3Mgd2l0aCBvdXIgY3VycmVudCBrbm93bGVkZ2UuCiAgICAjIwogICAgIyMgLiBCdXQgd2UgY2FuIGdldCBzb21lIGhlbHAgZnJvbSB0aGUgUENBIGRhdGEgaXRzZWxmCiAgICAjIwogICAgIyMgLiBJZiB5b3Ugc2FpZCBhIHZhbHVlIGFib3ZlIHRoZSA1MCBjb21wb25lbnRzIHdlCiAgICAjIyAgIGdlbmVyYXRlZCBmb3Igb3VyIFBDQSwgeW91IHNob3VsZCB3ZWFyIHRoZQogICAgIyMgICBjb25lIG9mIHNoYW1lICEKICAgIGBgYAogICAgCiAgICA8YnI+CgpUaGVyZSBhcmUgKipzZXZlcmFsKiogbWV0aG9kcyB0byBoZWxwIHVzIGNob29zZS4KCldlIHdpbGwgdXNlIGEgdmVyeSAqKnNpbXBsZSwgZ3JhcGhpY2FsKiogbWV0aG9kIDogdGhlIG9ic2VydmF0aW9uIG9mIHRoZSBhbW91bnQgb2YgZ2xvYmFsIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBlYWNoIGNvbXBvbmVudC4KCmBgYHtyIGhfZWxib3csIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIiwgZXZhbCA9IEZBTFNFfQo/U2V1cmF0OjpFbGJvd1Bsb3QKYGBgCgo8YnI+CgpBcHBseSBvbiBvdXIgZGF0YSA6CgpgYGB7ciBlbGJvd30KIyMgUGVyZm9ybSB0aGUgImVsYm93IHBsb3QiClNldXJhdDo6RWxib3dQbG90KAogIG9iamVjdCA9IHNvYmosIAogIG5kaW1zID0gNTApCmBgYAoKPGJyPgoKKipRdWVzdGlvbioqIDogCgpgYGB7ciBxX25kaW0yLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQpBbnkgbW9yZSBwcmVjaXNlIGlkZWEsIG5vdyA/CmBgYAoKPGJyPgoKYGBge3Igcl9uZGltMiwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiMjIC4gVGhlIGNvbnRyaWJ1dGlvbiB0byB0aGUgdmFyaWFuY2UgKHNkwrIpIHNlZW1zCiMjICAgZ3JlYXRseSByZWR1Y2VkIGFmdGVyIDMwIFBDcy4KIyMKIyMgLiBNYXliZSBzb21ldGhpbmcgYmV0d2VlbiB+MTUgYW5kIH4zMCBzaG91bGQgZG8KIyMgICB0aGUgdHJpY2sgPwpgYGAKCjxicj4KCiMjIyBBc3Nlc3MgZGltZW5zaW9ucwoKVG8gZGVtb25zdHJhdGUgdGhlICoqZWZmZWN0Kiogb2YgdGhlIG51bWJlciBvZiBQQyBkaW1lbnNpb25zIHVzZWQgYXMgaW5wdXQgdG8gdGhlIFVNQVAgZ2VuZXJhdGlvbiwgd2Ugd2lsbCBwZXJmb3JtIGEgY29tcGFyaXNvbiB1c2luZyA0IGRpZmZlcmVudCBhbW91bnRzIG9mIHJldGFpbmVkIFBDcyA6ICoqMywgNywgMjMgYW5kIDQ5KiouCgpgYGB7ciBkaW1fc2VsLCBmaWcud2lkdGggPSAyNCwgZmlnLmhlaWdodCA9IDZ9CiMjIFBDQSBtYXggZGltZW5zaW9ucyB0byBldmFsdWF0ZQpwY2FfZGltcyA8LSBjKDMsIDcsIDIzLCA0OSkKCiMjIERlZmluZSBhIGZ1bmN0aW9uIHRvIGNvbXB1dGUgdGhlIFVNQVAKcGNhX2RpbV9ldmFsIDwtIGZ1bmN0aW9uKG9iamVjdCA9IE5VTEwsIGRpbS5tYXggPSAyLCBteV9zZWVkID0gMTMzN0wpIHsKICBvYmplY3QgPC0gU2V1cmF0OjpSdW5VTUFQKAogICAgb2JqZWN0ID0gb2JqZWN0LCBhc3NheSA9ICJSTkEiLCAKICAgIGdyYXBoLm5hbWUgPSAiUk5BX3NubiIsIAogICAgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOmRpbS5tYXgsIAogICAgc2VlZC51c2UgPSBteV9zZWVkKQogIAogICMjIFBsb3QKICBkcE4gPC0gU2V1cmF0OjpEaW1QbG90KAogICAgb2JqZWN0ID0gb2JqZWN0LCAKICAgIHJlZHVjdGlvbiA9ICd1bWFwJywKICAgIGNvbWJpbmUgPSBUUlVFKSArIGdncGxvdDI6OmdndGl0bGUobGFiZWwgPSBwYXN0ZTAoIkRpbSA6ICIsIGRpbS5tYXgpKQogIAogICMjIENsZWFuCiAgcm0ob2JqZWN0KQogIAogICMjIFJldHVybiB0aGUgcGxvdCBvYmplY3QKICByZXR1cm4oZHBOKQp9CgojIyBSdW4gdGhlIGZ1bmN0aW9uIG9uIG11bHRpcGxlIGRpbWVuc2lvbnMsIGdldCBhIGxpc3Qgb2YgZ2dwbG90cwpwY2FfZXZhbF9yZXMgPC0gbGFwcGx5KFggPSBwY2FfZGltcywKICAgICAgICAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbihwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlKCJEaW0gOiAiLCBwKQogICAgICAgICAgICAgICAgICAgICAgICAgcGNhX2RpbV9ldmFsKG9iamVjdCA9IHNvYmosIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbS5tYXggPSBwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG15X3NlZWQgPSBteV9zZWVkKQogICAgICAgICAgICAgICAgICAgICAgIH0pCgojIyBQbG90IHRoZSBsaXN0IGFsbHRvZ2V0aGVyCnBhdGNod29yazo6d3JhcF9wbG90cyhwY2FfZXZhbF9yZXMsIG5yb3cgPSAxKQpgYGAKCjxicj4KCioqUXVlc3Rpb24qKgoKYGBge3IgcV9uZGltMywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KQW55IG1vcmUgcHJlY2lzZSBpZGVhLCBub3csIEZPUiBSRUFMID8KYGBgCgo8YnI+CgpgYGB7ciByX25kaW0zLCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGV2YWwgPSBGQUxTRX0KIyMgLiBWZXJ5IGZldyBQQ3MgYXJlIG5vdCBhYmxlIHRvIHJldHJpZXZlCiMjICAgYSBzdWZmaWNpZW50bHkgZGVmaW5lZCBzdHJ1Y3R1cmUuCiMjCiMjIC4gVGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gMjUgYW5kIDQ5IGFyZQojIyAgIGxpbWl0ZWQgaW4gdGhlIGdsb2JhbCBzdHJ1Y3R1cmUuCiMjICAgVGhpcyBtYXkgaW1wbHkgdGhhdCB0aGUgYWRkaXRpb25hbAojIyAgIGNvbXBvbmVudHMgYWJvdmUgMjUgZG8gbm90IGFkZCBtb3JlIAojIyAgIGluZm9ybWF0aW9uIChuZWl0aGVyIG1vcmUgbm9pc2UsIGhlcmUpLgpgYGAKCjxicj4KCldlIGNhbiBub3cgcGVyZm9ybSB0aGUgZmluYWwgVU1BUCB3aXRoIHRoZSBQQyBkaW1lbnNpb25zIG9mIHlvdXIgY2hvaWNlLgoKIyMjIENyZWF0ZSB0aGUgVU1BUAoKRm9yIHRoZSBuZXh0IHN0ZXBzIG9mIHRoZSB0cmFpbmluZywgd2Ugd2lsbCB1c2UgKipgciBuX2RpbWBgKiogUENBIGRpbWVuc2lvbnMuCgpgYGB7ciB1bWFwMjB9CiMjIFVzaW5nIDIwIFBDcwojIyBBIHNlZWQgaXMgbmVlZGVkIGhlcmUgIQpzb2JqIDwtIFNldXJhdDo6UnVuVU1BUCgKICAgIG9iamVjdCA9IHNvYmosIGFzc2F5ID0gIlJOQSIsIAogICAgZ3JhcGgubmFtZSA9ICJSTkFfc25uIiwgCiAgICByZWR1Y3Rpb24gPSAicGNhIiwgCiAgICBkaW1zID0gMTpuX2RpbSwgCiAgICBzZWVkLnVzZSA9IG15X3NlZWQpCmBgYAoKPGJyPgoKIyMgQm9udXMgOiAzRCBVTUFQIChERU1PKQoKV2hpbGUgYnkgZGVmYXVsdCBTZXVyYXQ6OlJ1blVNQVAgd2lsbCBwcm9kdWNlIDItZGltZW5zaW9uIHJlZHVjdGlvbnMsIHRoZSBtZXRob2QgY2FuIGdlbmVyYXRlIGZ1cnRoZXIgY29tcG9uZW50cy4KCkRlc3BpdGUgb3VyIGxpbWl0ZWQgYnJhaW4sIHRoaXMgaXMgc29tZXRpbWVzIGludGVyZXN0aW5nIGFuZCB1c2VmdWwgdG8gYXR0ZW1wdCBhIHJlZHVjdGlvbiB0byAzIGRpbWVuc2lvbnMgaW5zdGVhZCBvZiAyLiBUaGlzIGNhbiBiZSB2ZXJ5IGVmZmVjdGl2ZSB3aGVuIGxvb2tpbmcgZm9yIHRyYWplY3Rvcmllcy4KCldlIGNhbiBnZW5lcmF0ZSBhIFVNQVAgd2l0aCAzIGNvbXBvbmVudHMgZnJvbSBgciBuX2RpbWBgIFBDcyA6CgpgYGB7ciB1bWFwMkQyNSwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQojIyBVTUFQIGZyb20gMjUgUENzLCAzIGNvbXBvbmVudHMgcmVxdWVzdGVkCnNvYmogPC0gU2V1cmF0OjpSdW5VTUFQKAogIG9iamVjdCA9IHNvYmosIGFzc2F5ID0gJ1JOQScsIAogIGdyYXBoLm5hbWUgPSAnUk5BX3NubicsIAogIHJlZHVjdGlvbiA9ICdwY2EnLCAKICByZWR1Y3Rpb24ubmFtZSA9ICd1bWFwM2QnLAogIGRpbXMgPSAxOm5fZGltLCAKICBzZWVkLnVzZSA9IG15X3NlZWQsCiAgbi5jb21wb25lbnRzID0gMykKCiMjIERpbVBsb3Qgb2YgdGhlIGZpcnN0IDIgVU1BUCBjb21wb25lbnRzClNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICBkaW1zID0gYygxLDIpLAogIHJlZHVjdGlvbiA9ICd1bWFwM2QnKQpgYGAKCjxicj4KCioqUXVlc3Rpb24qKiA6IAoKYGBge3IgcV91bWFwM2QsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9Cklzbid0IHRoZXJlIHNvbWV0aGluZyBzdHJpa2luZyA/CmBgYAoKPGJyPgoKYGBge3IgYV91bWFwM2QsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIyAuIFRoZSBwbG90IGlzIG5vdCB0aGUgc2FtZSBhcyB3aGVuCiMjICAgdXNpbmcgMjUgUENzIGFuZCByZXF1ZXN0aW5nIDIgVU1BUAojIyAgIGNvbXBvbmVudHMgaW5zdGVhZCBvZiAzIGhlcmUgIQojIyAuIFRoZSAyIGNvbXBvbmVudHMgb2YgYSAyRCBVTUFQIGFyZSBub3QKIyMgICB0aGUgc2FtZSBhcyB0aGUgdHdvIGZpcnN0IGNvbXBvbmVudHMKIyMgICBvZiBhIGRpbT4yIFVNQVAuCmBgYAoKPGJyPgoKTGV0J3MgcGVyZm9ybSBhIDNEIHJlcHJlc2VudGF0aW9uIG9mIG91ciBVTUFQCgpgYGB7ciB1bWFwM0QyNSwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8iLCBldmFsID0gRkFMU0V9CiMjIFN0cnVjdHVyZSBkYXRhIHRvIHBsb3QgaW4gYSBkYXRhLmZyYW1lCmRmM2QgPC0gYXMuZGF0YS5mcmFtZSgKICBTZXVyYXQ6OlJlZHVjdGlvbnMob2JqZWN0ID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgIHNsb3QgPSAidW1hcDNkIilAY2VsbC5lbWJlZGRpbmdzCiAgKQoKIyMgM0QgcGxvdApwbG90bHk6OnBsb3RfbHkoCiAgZGF0YSA9IGRmM2QsIAogIHggPSB+dW1hcDNkXzEsIAogIHkgPSB+dW1hcDNkXzIsIAogIHogPSB+dW1hcDNkXzMsIAogIHR5cGUgPSAnc2NhdHRlcjNkJywgCiAgbWFya2VyID0gbGlzdChzaXplID0gMiwgd2lkdGg9MikpCmBgYAoKPGJyPgoKPGNlbnRlcj4hW10odGQzYV91bWFwM2QucG5nKTwvY2VudGVyPgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMgU2F2ZSB0aGUgU2V1cmF0IG9iamVjdAoKV2Ugd2lsbCBzYXZlIG91ciBTZXVyYXQgb2JqZWN0IHRoYXQgbm93IGNvbnRhaW5zIFBDQSBhbmQgVU1BUCByZWR1Y3Rpb25zICA6CgpgYGB7ciBzYXZlcmRzMSwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyMgU2F2ZSBvdXIgU2V1cmF0IG9iamVjdCAocmljaCBuYW1pbmcpCm91dF9uYW1lIDwtIHBhc3RlMCgKICAgICAgICAgIG91dHB1dF9kaXIsICIvIiwgcGFzdGUoCiAgICAgICAgICAgIGMoIjA5IiwgU2V1cmF0OjpQcm9qZWN0KHNvYmopLCAiUzUiLCAKICAgICAgICAgICAgICAiRGltUmVkLlBDQSIsIHBhc3RlKAogICAgICAgICAgICAgICAgZGltKHNvYmopLCAKICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gJy4nCiAgICAgICAgICAgICAgKQogICAgICAgICAgICApLCBjb2xsYXBzZSA9ICJfIiksCiAgICAgICAgICAgICIuUkRTIikKCiMjIENoZWNrCnByaW50KG91dF9uYW1lKQoKIyMgV3JpdGUgb24gZGlzawpzYXZlUkRTKG9iamVjdCA9IHNvYmosIAogICAgICAgIGZpbGUgPSBvdXRfbmFtZSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBDbHVzdGVyaW5nCgpXZSBjYW4gbm93IGF0dGVtcHQgdG8gZGV0ZXJtaW5lIGhvdyBjZWxscyAqKmFyZSBvcmdhbml6ZWQqKiBpbiBhbiAqKnVuc3VwZXJ2aXNlZCoqIG1hbm5lciBpbiB0aGlzIHNwYWNlCgpXZSB3aWxsIHVzZSB0aGUgZ3JhcGgtYmFzZWQgY2x1c3RlcmluZyBtZXRob2QgW0xvdXZhaW5dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xvdXZhaW5fbWV0aG9kKXt0YXJnZXQ9Il9ibGFuayJ9CgpDbHVzdGVyaW5nIHdpbGwgYmUgcGVyZm9ybWVkIG9uIHRoZSAqKlBDQSoqIGRpbWVuc2lvbiByZWR1Y3Rpb24sICoqbm90Kiogb24gdGhlIFVNQVAgb25lCgpgYGB7ciBxX2NsdXN0X29uX1BDQSwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KQW55IGlkZWEgd2h5ID8KYGBgCgo8YnI+CgojIyBGaW5kIG5laWdoYm91cnMKCkJlZm9yZSBydW5uaW5nIHRoZSBMb3V2YWluIG1ldGhvZCwgYSBmaXJzdCBwYXNzIG1ldGhvZCBpcyB1c2VkIHRvIGdlbmVyYXRlIGEgIkstTmVhcmVzdCBOZWlnaGJvdXIiIGdyYXBoIChzZWUgbW9yZSBkZXRhaWxzIFtoZXJlXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL3BibWMza190dXRvcmlhbC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9KS4KCmBgYHtyIGZubjIwfQojIyBDb21wdXRlIGEgU05OIHVzaW5nIHRoZSBmaXJzdCAyMCBQQ3MKc29iaiA8LSBTZXVyYXQ6OkZpbmROZWlnaGJvcnMoCiAgb2JqZWN0ID0gc29iaiwgCiAgZGltcyA9IDE6MjAsIAogIHJlZHVjdGlvbiA9ICJwY2EiKQpgYGAKCiMjIExvdXZhaW4gY2x1c3RlcmluZwoKLSAgIFdlIHdpbGwgdGVzdCAzIGRpZmZlcmVudCByZXNvbHV0aW9ucwoKLSAgIFRoZSBTZXVyYXQgZnVuY3Rpb24gdG8gcGVyZm9ybSBjbHVzdGVyaW5nIGNhbiBiZSBjYWxsZWQgd2l0aCBtdWx0aXBsZSByZXNvbHV0aW9ucyBhdCBvbmNlLgoKYGBge3IgY2x1c3RMMjB9CiMjIExvdXZhaW4gcmVzb2x1dGlvbnMgdG8gdGVzdApyZXNvbCA8LSBjKC4zLCAwLjgsIDEuNSkKCiMjIENsdXN0ZXJpbmcKc29iaiA8LSBTZXVyYXQ6OkZpbmRDbHVzdGVycygKICBvYmplY3QgPSBzb2JqLCAKICByZXNvbHV0aW9uID0gcmVzb2wsCiAgdmVyYm9zZSA9IEZBTFNFKQpgYGAKCjxicj4KCioqUXVlc3Rpb24qKgoKYGBge3IgcV9jbHVzdGRlc2MsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CkNvdWxkIHlvdSB0ZWxsIHVzIHdoYXQgY2hhbmdlZCBpbiBvdXIgb2JqZWN0ID8KYGBgCgo8YnI+CgpgYGB7ciBhX2NsdXN0ZGVzYywgY2xhc3Muc291cmNlPWMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgY2xhc3Mub3V0cHV0PSJhbnN3ZXJvIn0KIyMgT25lIGNhbiBqdXN0IGNhbGwgaXQgOgpzb2JqCiMjIyBIbW1tLCBub3RoaW5nIG5ldyB1bmRlciB0aGUgc3VuIC4uLgoKIyMgT25lIGNhbiBkZXNjcmliZSBpdCA6ClNDLmhlbHBlcjo6U2V1cmF0T2JqZWN0X2Rlc2NyaXB0b3IoCiAgc29iaiA9IHNvYmosCiAgZGVzY3JpYmUgPSAiY29sZGF0YSIpCmBgYAoKPGJyPgoKIyMgVmlzdWFsaXphdGlvbiAmIHNlbGVjdGlvbgoKIyMjIE9uIFVNQVBzCgpQbG90dGluZyBVTUFQcyBoYXJib3JpbmcgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyBmb3Igb3VyIDMgdGVzdGVkIHJlc29sdXRpb25zCgpgYGB7ciBjbHVzdF9kaW1wbG90LCBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9Nn0KIyMgTWV0YWRhdGEgbmFtZSBvZiBjbHVzdGVyaW5nIHJlc3VsdHMKcmVzb2xfbmFtZXMgPC0gcGFzdGUwKCJSTkFfc25uX3Jlcy4iLCByZXNvbCkKClNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICByZWR1Y3Rpb24gPSAidW1hcCIsIAogIGdyb3VwLmJ5ID0gcmVzb2xfbmFtZXMsCiAgbGFiZWwgPSBUUlVFLCAKICByZXBlbCA9IFRSVUUpCmBgYAoKPGJyPgoKIyMjIENsdXN0ZXJzIGNvbnRpbmdlbmNpZXMgYW5kIHByb3BvcnRpb25zCgpPbmUgY2FuIG9ic2VydmUgaG93IG1hbnkgY2VsbHMgYXJlIGluIGVhY2ggY2x1c3RlciwgYW5kIHdoYXQgcHJvcG9ydGlvbiBvZiBhbGwgY2VsbHMgdGhlc2UgcmVwcmVzZW50CgpgYGB7ciBjbHVzdF9wb3AuOH0KZm9yICh4IGluIHJlc29sX25hbWVzKSB7CiAgIyMgQ29udGluZ2VuY2llcwogIHByaW50KHRhYmxlKHNvYmpbW3hdXSkpCiAgIyMgUHJvcG9ydGlvbnMKICBwcmludChmb3JtYXQodGFibGUoc29ialtbeF1dKSAvIG5jb2woc29iaiksIGRpZ2l0cyA9IDIpKQogIGNhdCgnXG4nKQp9CmBgYAoKPGJyPgoKIyMjIENsdXN0ZXItc3BlY2lmaWMgbWFya2VycwoKQSBwcmFjdGljYWwgd2F5IHRvIGNoYXJhY3Rlcml6ZSBvdXIgY2x1c3RlcmluZyByZXN1bHRzIGlzIHRvIGdldCBiYWNrIHRvIGEgbGV2ZWwgb2Yga25vd2xlZGdlIHlvdSBhcmUgY29uZmlkZW50IGluIDogbWFya2VyIGdlbmVzLgoKU2V1cmF0IGhhcyBhIGhhbmR5IGZ1bmN0aW9uIHRvIDoKCi0gICBJZGVudGlmeSBkaWZmZXJlbnRpYWwgZXhwcmVzc2VkIGdlbmVzIHNwZWNpZmljIHRvIGVhY2ggYW5kIGV2ZXJ5IHByb3ZpZGVkIGNhdGVnb3J5IG9mIGNlbGxzIChoZXJlLCBjbHVzdGVyaW5nIHJlc3VsdHMpCgotICAgRHJhdyBhIGNsdXN0ZXJpemVkLCBhbm5vdGF0ZWQgaGVhdG1hcCBvZiB0aGVzZSBnZW5lcwoKYGBge3IgaF9mYW0sIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIiwgZXZhbCA9IEZBTFNFfQo/U2V1cmF0OjpGaW5kQWxsTWFya2VycwpgYGAKCmBgYHtyIGRobSwgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTZ9CiMjIExvb3Bpbmcgb24gY2x1c3RlcmluZyByZXN1bHRzCmZtYV9hbGwgPC0gbGFwcGx5KHJlc29sX25hbWVzLCBmdW5jdGlvbihyKSB7CiAgCiAgIyMgRmluZCBtYXJrZXJzIGZvciBhbGwgY2x1c3RlcnMKICBTZXVyYXQ6OklkZW50cyhvYmplY3QgPSBzb2JqKSA8LSBzb2JqW1tyXV1bWzFdXQogIGZhbSA8LSBTZXVyYXQ6OkZpbmRBbGxNYXJrZXJzKAogICAgb2JqZWN0ID0gc29iaiwgCiAgICBsb2dmYy50aHJlc2hvbGQgPSAuNSwKICAgIG9ubHkucG9zID0gVFJVRSwgCiAgICBtaW4ucGN0ID0gLjUsIAogICAgdmVyYm9zZSA9IEZBTFNFLAogICAgcmFuZG9tLnNlZWQgPSBteV9zZWVkKQogIAogICMjIFNlbGVjdCB0b3AxMCBnZW5lcyB3aGVuIGF2YWlsYWJsZQogIGZhbV9yZHggPC0gZHBseXI6Omdyb3VwX2J5KC5kYXRhID0gZmFtLCBjbHVzdGVyKQogIGZhbV9yZHggPC0gZHBseXI6OmZpbHRlciguZGF0YSA9IGZhbV9yZHgsIGF2Z19sb2cyRkMgPiAxKQogIGZhbV9yZHggPC0gZHBseXI6OnNsaWNlX2hlYWQoLmRhdGEgPSBmYW1fcmR4LCBuID0gMTApCiAgZGggPC0gU2V1cmF0OjpEb0hlYXRtYXAob2JqZWN0ID0gc29iaiwgZmVhdHVyZXMgPSBmYW1fcmR4JGdlbmUsIGNvbWJpbmUgPSBUUlVFKSArIGdncGxvdDI6OmdndGl0bGUobGFiZWwgPSByKQogIHJldHVybihkaCkKfSkKCiMjIFBsb3QgYWxsIGhlYXRtYXBzIGF0IG9uY2UKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKGZtYV9hbGwpICsgcGF0Y2h3b3JrOjpwbG90X2xheW91dChucm93ID0gMSkKCmBgYAoKPGJyPgoKKipRdWVzdGlvbnMqKiA6IENvbXBhcmluZyB0aGUgaGVhdG1hcHMgOiAKCmBgYHtyIHFfaG0xLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQpXaGljaCByZXNvbHV0aW9uIHdvdWxkIHlvdSBjaG9vc2UsIGFuZCB3aHkgPwpgYGAKCjxicj4KCmBgYHtyIHFfaG0yLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQpJcyB0aGVyZSBhIHNpbmdsZSBvbmUgYW5kIG9ubHkgYW5zd2VyIHRvIHRoZSBmb3JtZXIgcXVlc3Rpb24gPwpgYGAKCjxicj4KCiMjIFNlbGVjdGlvbgoKRm9yIHRoZSBkb3duc3RyZWFtIGFuYWx5c2VzLCB3ZSB3aWxsIHVzZSB0aGUgcmVzb2x1dGlvbiAqKmByIGxfcmVzYCoqCgpgYGB7ciBzZWxfcmVzfQpTZXVyYXQ6OklkZW50cyhvYmplY3QgPSBzb2JqKSA8LSBzb2JqW1twYXN0ZTAoIlJOQV9zbm5fcmVzLiIsIGxfcmVzKV1dW1sxXV0KYGBgCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKIyBTYXZlIHRoZSBTZXVyYXQgb2JqZWN0CgpXZSB3aWxsIHNhdmUgb3VyIFNldXJhdCBvYmplY3QgdGhhdCBub3cgY29udGFpbnMgb3VyIGNsdXN0ZXJpbmcgcmVzdWx0cyAgOgoKYGBge3Igc2F2ZXJkczIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIFNhdmUgb3VyIFNldXJhdCBvYmplY3QgKHJpY2ggbmFtaW5nKQpvdXRfbmFtZSA8LSBwYXN0ZTAoCiAgICAgICAgICBvdXRwdXRfZGlyLCAiLyIsIHBhc3RlKAogICAgICAgICAgICBjKCIxMCIsIFNldXJhdDo6UHJvamVjdChzb2JqKSwgIlM1IiwgCiAgICAgICAgICAgICAgcGFzdGUwKAogICAgICAgICAgICAgICAgIkNsdXN0ZXJlZC4iLAogICAgICAgICAgICAgICAgbF9yZXMpLCAKICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgIGRpbShzb2JqKSwgCiAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICcuJwogICAgICAgICAgICAgICkKICAgICAgICAgICAgKSwgY29sbGFwc2UgPSAiXyIpLAogICAgICAgICAgICAiLlJEUyIpCgojIyBDaGVjawpwcmludChvdXRfbmFtZSkKCiMjIFdyaXRlIG9uIGRpc2sKc2F2ZVJEUyhvYmplY3QgPSBzb2JqLCAKICAgICAgICBmaWxlID0gb3V0X25hbWUpCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj48YnI+PGJyPgoKIyBSc2Vzc2lvbgoKYGBge3IgcnNlc3Npb24sIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KdXRpbHM6OnNlc3Npb25JbmZvKCkKYGBgCg==