1 PREAMBLE

1.1 Purpose of this session

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

  • Integration of a pair of samples

1.2 The integration data set

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

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

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

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

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



2 Start Rstudio

3 Warm-up

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

## Required for our big objects
options(future.globals.maxSize = 1E+09)


4 Prepare the data structure

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

4.1 Main directory

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

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

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

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

4.2 Current session

## Creating the session (Preproc.1) directory
session_dir <- paste0(TD_dir, "/06_Integration")
dir.create(path = session_dir, recursive = TRUE)

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

4.3 Input directory

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

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

4.4 Output directory

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

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


5 Load input data

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

5.1 Download data

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

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

## Folder to store retrieved files
local_folder <- input_dir

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

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

## Zenodo ID
zen_id <- '14094752'

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

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

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

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

5.2 Load into R

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

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


6 Merge datasets

6.1 Describe objects

## Describe TD3A
EBAII.n1.SC.helper::SeuratObject_descriptor(sobj = sobj.TD3A, describe = "assay")
OBJECT VERSION :    5.0.2 
PROJECT :   [TD3A] 

[ASSAYS]
   ASSAY 1 :    [RNA] [ACTIVE] 
      SLOT/LAYER 1 :    [counts]    Dims:[12508 x 2018]  Range:[0.00-729.00] 
         Counts :   7133935 
         Sparsity : 86.88770% 
      SLOT/LAYER 2 :    [data]  Dims:[12508 x 2018]  Range:[0.00-7.71] 
         Sparsity : 86.88770% 
      SLOT/LAYER 3 :    [scale.data]    Dims:[0 x 0] 
## Describe TDCT
EBAII.n1.SC.helper::SeuratObject_descriptor(sobj = sobj.TDCT, describe = "assay")
OBJECT VERSION :    5.0.2 
PROJECT :   [TDCT] 

[ASSAYS]
   ASSAY 1 :    [RNA] [ACTIVE] 
      SLOT/LAYER 1 :    [counts]    Dims:[12254 x 1868]  Range:[0.00-1010.00] 
         Counts :   8001618 
         Sparsity : 87.58623% 
      SLOT/LAYER 2 :    [data]  Dims:[12254 x 1868]  Range:[0.00-7.20] 
         Sparsity : 87.58623% 
      SLOT/LAYER 3 :    [scale.data]    Dims:[0 x 0] 

6.2 Merge

  • We simply merge the two objects into a single one :
sobj.M <- merge(
  ## First object to merge
  x = sobj.TD3A, 
  ## Second object to merge
  y = sobj.TDCT, 
  ## Keep track of sample of origin for cells
  add.cell.ids = c("TD3A", "TDCT"), 
  ## Set a new project.name
  project = "TD3A.TDCT", 
  ## Also merge the "data" and "scale.data" layers (instead of "counts" only)
  merge.data = FALSE
)
  • We can take a look at its summary
## See the object summary
sobj.M
Show output
An object of class Seurat 
12926 features across 3886 samples within 1 assay 
Active assay: RNA (12926 features, 0 variable features)
 4 layers present: counts.TD3A, counts.TDCT, data.TD3A, data.TDCT

Question : Something unexpected ?

## . Seurat v5 does merge samples keeping their different layers split !
##
## . It was not the case for SeuratObject v4
  • What are its current dimensions ?
## TD3A
dim(sobj.TD3A)
[1] 12508  2018
## TDCT
dim(sobj.TDCT)
[1] 12254  1868
## Merged
dim(sobj.M)
[1] 12926  3886

Question : What can you conclude from these dimensions about the way the two samples were merged ?

## Object description (joined)
EBAII.n1.SC.helper::SeuratObject_descriptor(
  sobj = SeuratObject::JoinLayers(object = sobj.M))
OBJECT VERSION :    5.0.2 
PROJECT :   [TD3A.TDCT] 

[ASSAYS]
   ASSAY 1 :    [RNA] [ACTIVE] 
      SLOT/LAYER 1 :    [counts]    Dims:[12926 x 3886]  Range:[0.00-1010.00] 
         Counts :   15135553 
         Sparsity : 87.75391% 
      SLOT/LAYER 2 :    [data]  Dims:[12926 x 3886]  Range:[0.00-7.71] 
         Sparsity : 87.75391% 
      SLOT/LAYER 3 :    [scale.data]    Dims:[0 x 0] 

[DIMREDS]

[BARCODES METADATA]
orig.ident    Freq
-----------  -----
TD3A          2018
TDCT          1868
NA               0

 nCount_RNA 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1011    1848    2260    3895    2894   48866 

 nFeature_RNA 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    750    1155    1359    1583    1609    5968 

 log10_nCount_RNA 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  3.005   3.267   3.354   3.432   3.462   4.689 
nCount_RNA_in_range    Freq
--------------------  -----
TRUE                   3886
NA                        0
nFeature_RNA_in_range    Freq
----------------------  -----
TRUE                     3886
NA                          0

 percent_mt 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
0.002375 0.023919 0.041116 1.383173 2.749409 5.000000 

 percent_rb 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
 0.02687  0.09509  0.24232  7.74491 14.31667 35.88704 

 percent_st 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.01558 0.03337 0.04673 1.71057 3.40138 5.95447 
percent_mt_in_range    Freq
--------------------  -----
TRUE                   3886
NA                        0
percent_rb_in_range    Freq
--------------------  -----
TRUE                   3886
NA                        0
percent_st_in_range    Freq
--------------------  -----
TRUE                   3886
NA                        0

 CC_Seurat_S.Score 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.25897 -0.12940 -0.07629 -0.02261 -0.01139  1.12063 

 CC_Seurat_G2M.Score 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.32327 -0.15325 -0.10140 -0.05262 -0.04054  1.55433 
CC_Seurat_Phase    Freq
----------------  -----
G1                 2838
G2M                 337
S                   711
NA                    0

 CC_Seurat_SmG2M.Score 
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-1.65714 -0.03646  0.02949  0.03001  0.09579  0.96435 
doublet_scds.hybrid    Freq
--------------------  -----
FALSE                  3886
NA                        0
doublet_scDblFinder    Freq
--------------------  -----
FALSE                  3886
NA                        0
doublet_union    Freq
--------------  -----
FALSE            3886
NA                  0
doublet_viz    Freq
------------  -----
singlet        3886
NA                0
## Clean unneeded objects
rm(sobj.TD3A, sobj.TDCT)

6.3 Process the merged dataset

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

## All steps
sobj.M <- Seurat::NormalizeData(object = sobj.M, verbose = FALSE)
sobj.M <- Seurat::FindVariableFeatures(object = sobj.M, nfeatures = 2000, verbose = FALSE)
sobj.M <- Seurat::ScaleData(object = sobj.M, verbose = FALSE)
sobj.M <- Seurat::RunPCA(object = sobj.M, npcs = 30, seed.use = my_seed, verbose = FALSE)
Seurat::ElbowPlot(object = sobj.M, ndims = 30, reduction = "pca")
Show plot

6.4 Visualization of batch effect

EBAII.n1.SC.helper::QnD_viz(sobj = sobj.M, 
                            assay = "RNA",
                            slot = NULL,
                            dimred = "pca",
                            ncomp = 20,
                            group_by = "orig.ident",   ## This is were samples are distinguished
)
Show plot

Question : Describe what you are seeing ?



7 Save the Seurat object

## A name for our Seurat object file (rich naming)
out_name <- paste0(
          output_dir, "/", paste(
            c("11", sobj.M@project.name, "S5", 
              "Merged", paste(
                dim(sobj.M), collapse = '.'
              )
            ), collapse = "_"),
            ".RDS")

## Check (I like this)
print(out_name)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/06_Integration/RESULTS/11_TD3A.TDCT_S5_Merged_12926.3886.RDS"
## Write on disk
saveRDS(object = sobj.M, 
        file = out_name)


8 Integration

8.1 Effect of covariates

paiva_markers <- c("Cdc6", "C1qa", "Birc5", "Il7r", "Plac8", "Pck2", "Anxa2", "Rag1", "Gtf")

EBAII.n1.SC.helper::assess_covar(
  mat = Seurat::GetAssayData(object = sobj.M, 
                             assay = "RNA", 
                             slot = "scale.data"), 
  covar.df = sobj.M[[]],
  markers = sort(paiva_markers),
  ndim = 10L, 
  center = TRUE, 
  scale = TRUE)

8.2 Write a function

To ease up the process of generating integrations using multiple methods available through Seurat, we will define a small function :

## object       A SeuratObject v5 after Seurat::IntegrateLayers
## assay        The name of the `object` assay to use (default is "RNA")
## markers      A vector of feature names to use as markers in the covariates weight estimation
## int_reduction    The name of a dimension reduction generated by integration (See https://satijalab.org/seurat/reference/#integration)
## n_dim        The number of PCA dimensions to use for the integration
## resolution   The resolution of the Louvain clustering
## my_seed      The seed given to the RNG

integration_describe <- function(object = NULL, assay = "RNA", markers = NULL, int_reduction = "CCAIntegration", n_dim = 20, resolution = .8, my_seed = 1337) {
  
  ## Elbow plot
  object@reductions[[int_method]]@stdev <- matrixStats::colSds(Seurat::Embeddings(object = Seurat::Reductions(object = object, slot = int_method)))
  ep <- Seurat::ElbowPlot(
    object = object, 
    ndims = min(30, 
                ncol(Seurat::Reductions(object = object, 
                                        slot = int_reduction))))
  print(ep)
  
  ## Assess covariates
  ### Prepare the covariates data.frame
  covar_df <- if(is.null(markers)) object[[]] else cbind(object[[]], t(Seurat::GetAssayData(object = subset(x = SeuratObject::JoinLayers(object = object), features = paiva_markers), assay = "RNA", slot = "scale.data")))
  ### Run the helper function
  EBAII.n1.SC.helper::assess_covar(
    mat = t(Seurat::Embeddings(Seurat::Reductions(object = object, 
                                                  slot = int_method)
                               )
            ), 
    covar.df = covar_df,
    ndim = 10L, 
    center = TRUE, 
    scale = TRUE)
  
  ## Create UMAP
  object <- Seurat::RunUMAP(
    object = object,
    assay = assay,
    reduction = int_method,
    dims = 1:n_dim,
    seed.use = my_seed,
    verbose = FALSE
  )
  
  ## Clustering
  clust_name <- paste0(int_method, "_clusters")
  
  ### Compute neighbours
  object <- Seurat::FindNeighbors(object = object, 
                                  reduction = int_method, 
                                  dims = 1:n_dim,
                                  verbose = FALSE)
  ### Perform Louvain clustering
  object <- Seurat::FindClusters(object = object, 
                                 resolution = resolution, 
                                 cluster.name = clust_name, 
                                 random.seed = my_seed,
                                 verbose = FALSE)
  
  ## Plot UMAP : idents
  dp_ident <- Seurat::DimPlot(object = object, 
                              dims = c(1,2), 
                              seed = my_seed, 
                              group.by = "orig.ident") & 
    Seurat::DarkTheme() & 
    ggplot2::ggtitle(label = int_method)
  print(dp_ident)
  
  ## Plot UMAP : clusters
  dp_clusters <- Seurat::DimPlot(object = object, 
                                 dims = c(1,2), 
                                 seed = my_seed, 
                                 group.by = clust_name, 
                                 label = TRUE, 
                                 repel = TRUE) & 
    ggplot2::ggtitle(label = int_method)
  print(dp_clusters)
  
  ## Use markers for heatmap
  Seurat::Idents(object = object) <- object[[clust_name]][[1]]
  fam <- Seurat::FindAllMarkers(
    object = SeuratObject::JoinLayers(object = object), 
    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 = object, 
                          features = fam_rdx$gene, 
                          combine = TRUE) & 
    ggplot2::ggtitle(label = int_method)
  print(dh)
  
  ## Proportions
  prop_tab <- as.data.frame.matrix(table(object[[clust_name]][[1]], object$orig.ident))
  prop_tab[[paste0(colnames(prop_tab)[1], "_prop")]] <- prop_tab[,1] / sum(prop_tab[,1])
  prop_tab[[paste0(colnames(prop_tab)[2], "_prop")]] <- prop_tab[,2] / sum(prop_tab[,2])
  
  ### Nice table
  prop_dt <- DT::formatPercentage(table = DT::datatable(prop_tab, filter = "none", options = list(pageLength = 100)), columns = c(3,4), digits = 2)
  print(prop_dt)
  
  ### Mosaic plot
  vcd::mosaic(x = as.matrix(prop_tab[,c(1,2)]), 
              shade = TRUE, 
              main = int_method)
  
  ## Return
  return(object)
}

8.3 Different methods

8.3.1 Seurat : CCA

## Define integration method
int_method <- "CCAIntegration"

## Setting dimensions to keep
i_dim <- 15

## Perform integration
sobj.M <- Seurat::IntegrateLayers(
  object = sobj.M, 
  method = int_method,
  orig.reduction = "pca", 
  new.reduction = int_method,
  verbose = FALSE,
  dims = 1:i_dim
)
  
## Describe the results
sobj.M <- integration_describe(object = sobj.M, 
                             assay = "RNA", 
                             int_reduction = int_method, 
                             n_dim = i_dim, 
                             resolution = .8,
                             markers = sort(paiva_markers),
                             my_seed = my_seed)

8.3.2 Seurat : RPCA

## Define integration method
int_method <- "RPCAIntegration"

## Setting dimensions to keep
i_dim <- 15

## Perform integration
sobj.M <- Seurat::IntegrateLayers(
  object = sobj.M, 
  method = int_method,
  orig.reduction = "pca", 
  new.reduction = int_method,
  verbose = FALSE,
  dims = 1:i_dim
)
  
## Describe the results
sobj.M <- integration_describe(object = sobj.M, 
                             assay = "RNA", 
                             int_reduction = int_method, 
                             n_dim = i_dim, 
                             resolution = .8,
                             markers = sort(paiva_markers),
                             my_seed = my_seed)

8.3.3 Seurat : Harmony

## Define integration method
int_method <- "HarmonyIntegration"

## Setting dimensions to keep
i_dim <- 15

## Perform integration
sobj.M <- Seurat::IntegrateLayers(
  object = sobj.M, 
  method = int_method,
  orig.reduction = "pca", 
  new.reduction = int_method,
  verbose = FALSE
)
  
## Describe the results
sobj.M <- integration_describe(object = sobj.M, 
                             assay = "RNA", 
                             int_reduction = int_method, 
                             n_dim = i_dim, 
                             resolution = .8,
                             markers = sort(paiva_markers),
                             my_seed = my_seed)

8.3.4 Harmony (standalone)

## Setting dimensions to keep
i_dim <- 15

## Define a name for the integrated reduction
int_method <- "HarmonyStandalone"

## Perform integration (Requires a join !)
sobj.H <- harmony::RunHarmony(
  object = SeuratObject::JoinLayers(object = sobj.M), 
  reduction.use = 'pca', 
  dims.use = 1:i_dim, 
  group.by.vars = 'orig.ident', 
  reduction.save = int_method,
  max_iter = 100, ## or sigma .2
  verbose = FALSE)

## Give harmony reduction from sobj.H to sobj.M
sobj.M@reductions[[int_method]] <- Seurat::Reductions(object = sobj.H, slot = int_method)
rm(sobj.H)

## Describe the results
sobj.M <- integration_describe(object = sobj.M, 
                             assay = "RNA", 
                             int_reduction = int_method, 
                             n_dim = i_dim, 
                             resolution = .8,
                             markers = sort(paiva_markers),
                             my_seed = my_seed)



9 Save the Seurat object

## A name for our Seurat object file (rich naming)
out_name <- paste0(
          output_dir, "/", paste(
            c("12", sobj.M@project.name, "S5", 
              "Integrated", paste(
                dim(sobj.M), collapse = '.'
              )
            ), collapse = "_"),
            ".RDS")

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





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