1 PREAMBLE

1.1 Purpose of this session

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

  • Filtering of bad cells and extreme low-expression features

  • Estimation of the cell cycle phase

  • Identification and removal of cell doublets



2 Start Rstudio



3 Warm-up

  • We set common parameters we will use throughout this session :
## Set your project name
project_name <- "ebaii_sc_teachers"  # Do not copy-paste this ! It's MY project ! Put yours !!

## Seed for the RNG
my_seed <- 1337L

## Minimum cells with counts to keep a feature
min_cells <- 5


4 Prepare the data structure

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

4.1 Main directory

## 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.3) directory
session_dir <- paste0(TD_dir, "/03_Preproc.3")
dir.create(path = session_dir, recursive = TRUE)

## Print the session directory on-screen
print(session_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/03_Preproc.3"

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/03_Preproc.3/DATA"

4.4 Genelists directory

res_dir <- paste0(TD_dir, "/Resources")
glist_dir <- paste0(res_dir, "/Genelists")

## Print the resources directory on-screen
print(glist_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/Resources/Genelists"

4.5 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/03_Preproc.3/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 <- "02_TD3A_S5_Metrics.Bio_31053.4587.RDS"

## The latest Seurat object saved as RDS (full path)
sobj_path <- paste0(TD_dir, 
                    "/02_Preproc.2/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)



Now, we finally have all the metrics and bias sources we could use, so we can actually get to the filtering step.

6 Filtering

6.1 Features

  • As already explained, the sparsity of the count matrix is high, very high.

  • To the point that there probably are some features that have so low expression level that they were only counted in very few cells. As such, they have absolutely no chance to characterize any cell type, nor harbor some variation between different cell types.

  • As such, we should discard these features


What are the actual dimensions of our expression data ?

## Dimensions of our Seurat object
dim(sobj)
Show output
[1] 31053  4587


We want to remove the features that are expressed in less than 5 cells.

First, we quantify, for each feature, the number of cells with 0 count.

## Compute the amount of 0s per feature
nFeat_zero <- sparseMatrixStats::rowCounts(
  x = SeuratObject::GetAssayData(
    object = sobj, 
    assay = "RNA", 
    layer = "counts"), 
  value = 0)

## Inversion : computing the number of cells with at least one count, per feature
nFeat_nonzero <- ncol(sobj) - nFeat_zero

## Identify those with at least 5 cells with expression
nFeat_keep <- nFeat_nonzero >= min_cells

## Quantify the selected ones
table(nFeat_keep)
Show output
nFeat_keep
FALSE  TRUE 
18545 12508 


And now we can discard features expressed in less than 5 cells :

filtc5_pre <- SC.helper::QnD_viz(sobj = sobj, 
                                return_plot = TRUE)
Show plot


## Restrict the Seurat object
sobj <- sobj[nFeat_keep,]


What are the Seurat object dimensions, now ?

dim(sobj)
Show output
[1] 12508  4587


We can visualize the cell space after this features filtering

filtc5_post <- SC.helper::QnD_viz(sobj = sobj, 
                                  return_plot = TRUE)
Show plot


Merging plots for ease of visualization :

## Using the patchwork package to merge plots (and ggplot2 to add titles)
patchwork::wrap_plots(
  list(
    filtc5_pre & ggplot2::ggtitle(label = "BEFORE"),
    filtc5_post & ggplot2::ggtitle(label = "AFTER")), 
  nrow = 1)
Show plot


  • Question :

    Do you see any difference when comparing before vs after features filtering ?


    ## . Not much changed, maybe cell groups seem a bit more condensed
    ##
    ## . This is expected, as we removed features with almost no expression



6.2 Cells

We are now able to apply all the filtering strategies we established for each QC metric.

  • Identify “good” cells
## Identify the cells to keep
bc_keep <-  sobj$nFeature_RNA_in_range & 
            sobj$nCount_RNA_in_range & 
            sobj$percent_mt_in_range & 
            sobj$percent_rb_in_range & 
            sobj$percent_st_in_range

## Contengency
table(bc_keep)
Show output
bc_keep
FALSE  TRUE 
  309  4278 


  • Visualize metrics filtering effect (as an upset-plot)

    ## Create a list of all cells filtered ou for each criterion
    up_list <-list(
      "nFeature" = colnames(sobj)[!sobj$nFeature_RNA_in_range],
      "nCount" = colnames(sobj)[!sobj$nCount_RNA_in_range],
      "%MITO" = colnames(sobj)[!sobj$percent_mt_in_range],
      "%RIBO" = colnames(sobj)[!sobj$percent_rb_in_range],
      "%STRESS" = colnames(sobj)[!sobj$percent_st_in_range])
    
    ## Create an upset-plot
    UpSetR::upset(data = UpSetR::fromList(up_list), 
                  nintersects = NA, 
                  sets = rev(names(up_list)),
                  keep.order = TRUE,
                  order.by = "freq")

    Show plot


  • Apply the filter

    ## Seurat object BEFORE cell filtering
    dim(sobj)

    Show output

    [1] 12508  4587
    ## Apply cell filtering
    sobj <- subset(x = sobj, cells = colnames(sobj)[bc_keep])
    
    ## Seurat object AFTER cell filtering
    dim(sobj)

    Show output

    [1] 12508  4278


We can visualize the cell space since this cell filtering

SC.helper::QnD_viz(sobj = sobj)
Show plot


  • Question

    Do you see any difference when comparing before vs after features filtering ?


    ## . Not much changed as well (we did not discard many cells)
    ##
    ## . The biggest cluster structure seems more defined




7 Save the Seurat object

We will save our Seurat object that now contains filtered cells and features :

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

## Check
print(out_name)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/03_Preproc.3/RESULTS/03_TD3A_S5_Metrics.Filtered_12508.4278.RDS"
## Write on disk
saveRDS(object = sobj, 
        file = out_name)




8 Cell cycle scores

  • We are currently analyzing independent profiles from isolated cells, from a sample dissociation

  • As such, cells were most probably not synchronized, thus the effect of their cell cycle state on genes expression may be strong, to the point that it can bias the data (ie, mask some lower amplitude biological variation).

  • In order to assess (and maybe, remove) this bias, we have to quantify it.

  • We will perform this estimation thanks to heuristics based on knowledge : Seurat includes a method that evaluates the cell cycle phase of cells through scores for the S and G2M phases, each based on phase-specific gene signatures.

  • For this step, we will use additional gene lists from knowledge (cell cycle phase), hosted in a Zenodo respository (Id : 14037355)

8.1 Download gene lists

  • We will directly retrieve data from Zenodo to your input_dir :

    ## Zenodo ID
    zen_id <- '14101506'
    
    ### Named files (will be used later on !)
    cc_file <- "mus_musculus_Seurat_cc.genes_20191031.rds"
    
    ## Filename(s) to retrieve
    toget_files <- c(cc_file)
    
    ## Folder to store retrieved files
    local_folder <- glist_dir
    
    ## Use local backup ?
    backup <- FALSE
    if(backup) message("Using local backup !")
    
    ## Force download ?
    force <- FALSE
    if(force) message("Forcing (re)download !")
    
    ### Define remote folder
    remote_folder <- if (backup) {
      "/shared/projects/2422_ebaii_n1/atelier_scrnaseq/TD/RESOURCES/GENELISTS/"
    }  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 !"))
    }


8.2 Load gene lists

## The cell cycle gene lists file
cc_file <- paste0(glist_dir, 
                  "/", 
                  cc_file)

## Load the cell cycle reference genes lists
cc_genes <- readRDS(file = cc_file)

## Have a look on its content
str(cc_genes)
Show output
List of 2
 $ s.genes  : chr [1:43] "Mcl5" "Pcna" "Tyms" "Fen1" ...
 $ g2m.genes: chr [1:54] "Hmgb2" "Cdk1" "Nusap1" "Ube2c" ...


  • Question

    As explained, we will use gene lists extracted from community knowledge. 
    Our data contain values for genes also, so we will cross them. 
    Is there something we should check ?


    ## . We may check if the genes in our gene lists are effectively 
    ##   present in our Seurat object !
    ##
    ## . This is expected, as we removed features with almost no expression


Beyond :

  1. Write a code that performs this check

  2. Add a code to adjust the content of the gene lists accordingly (remove genes from the gene lists that are not present in our dataset)

  3. (NOTE : this is actually not needed as already checked and corrected by the cell cyle estimation method we will use)


## Check if our data genes cover the gene lists
lapply(cc_genes, function(x) x %in% rownames(sobj))

## Remove genes not in sobj
cc_genes <- lapply(cc_genes, function(gl) { gl[gl %in% rownames(sobj)] })

## Check the modification
str(cc_genes)

## Check that all genes are available in sobj
all(unique(unlist(cc_genes)) %in% rownames(sobj))



8.3 Estimation

  • Let’s perform this estimation. But how ?

    ## Reading the function help page
    ?Seurat::CellCycleScoring


  • We will actually use a helper function to ease up the process :

    ?SC.helper::CC_Seurat




  • Run the cell-cycle estimation

    ## Perform the estimation
    ## The RNG seed is needed here !
    sobj <- SC.helper::CC_Seurat(
      sobj = sobj, 
      assay = "RNA",
      seurat_cc_genes = cc_genes, 
      SmG2M = TRUE, 
      nbin = 20, 
      my_seed = my_seed)


  • Description of the object to see the data added

    SC.helper::SeuratObject_descriptor(
      sobj = sobj,
      describe = "coldata")

    Show output

    OBJECT VERSION :    5.0.2 
    PROJECT :   [TD3A] 
    
    [BARCODES METADATA]
    orig.ident    Freq
    -----------  -----
    TD3A          4278
    NA               0
    
     nCount_RNA 
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
       1000    2054    2446    3737    3213   48866 
    
     nFeature_RNA 
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
        750    1325    1498    1697    1752    5968 
    
     log10_nCount_RNA 
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      3.000   3.313   3.389   3.458   3.507   4.689 
    nCount_RNA_in_range    Freq
    --------------------  -----
    TRUE                   4278
    NA                        0
    nFeature_RNA_in_range    Freq
    ----------------------  -----
    TRUE                     4278
    NA                          0
    
     percent_mt 
        Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
    0.001801 0.019618 0.024355 0.025143 0.029851 0.050000 
    
     percent_rb 
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    0.02687 0.08067 0.09697 0.11161 0.12280 0.42010 
    
     percent_st 
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    0.01546 0.02991 0.03365 0.03422 0.03773 0.05980 
    percent_mt_in_range    Freq
    --------------------  -----
    TRUE                   4278
    NA                        0
    percent_rb_in_range    Freq
    --------------------  -----
    TRUE                   4278
    NA                        0
    percent_st_in_range    Freq
    --------------------  -----
    TRUE                   4278
    NA                        0
    
     CC_Seurat_S.Score 
          Min.    1st Qu.     Median       Mean    3rd Qu.       Max. 
    -0.2338439 -0.1060737 -0.0584921 -0.0184198 -0.0005651  1.2329054 
    
     CC_Seurat_G2M.Score 
        Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
    -0.22728 -0.11143 -0.07202 -0.03641 -0.02488  1.59231 
    CC_Seurat_Phase    Freq
    ----------------  -----
    G1                 2878
    G2M                 471
    S                   929
    NA                    0
    
     CC_Seurat_SmG2M.Score 
        Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
    -1.56874 -0.04569  0.01532  0.01799  0.08286  0.96435 


8.4 Visualization

As usual, we can visualize the results as violins :

Seurat::VlnPlot(object = sobj,
                features = c("CC_Seurat_S.Score",
                             "CC_Seurat_G2M.Score",
                             "CC_Seurat_SmG2M.Score"))
Show plot


But it’s not that easy to interpret…

Let’s plot it in the cell space

8.4.1 “S minus G2M” (SmG2M) score :

## Using the 'features' parameter to plot the SmG2M score (continuous data)
## Here, we will keep the modified Seurat object to speed up further plots
VIZ <- SC.helper::QnD_viz(sobj = sobj, 
                          features = "CC_Seurat_SmG2M.Score", 
                          return_object = TRUE)
Show plot


8.4.2 Estimated cell phase :

## Using the 'group_by' parameter to plot the estimated phases (categorical data)
## Here, we recycle keep the VIZ Seurat object that contains everything to perform the plot without computing it again
SC.helper::QnD_viz(sobj = VIZ, 
                   slot = NULL, 
                   dimred = "umap", 
                   group_by = "CC_Seurat_Phase")
Show plot

## VIZ is not needed anymore
rm(VIZ)


Questions

Does the structure of the cells in this representation seem to have a link with these cell cycle phases/scores ?


## It's an absolute yes !


Do you think this is the result of an artifact, or
biology-related ?


##
##    ¯\_(ツ)_/¯
##




9 Save the Seurat object

We will save our Seurat object that now contains the cell cycle states/scores :

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

## Check
print(out_name)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/03_Preproc.3/RESULTS/04_TD3A_S5_CC_12508.4278.RDS"
## Write on disk
saveRDS(object = sobj, 
        file = out_name)





10 Cell doublets

10.1

We will use two different methods to detect and remove cell doublets :

  • scds (in its “hybrid” mode){target=“_blank”} : more efficient at detecting homotypic doublets

  • scDblFinder : more efficient at detecting heterotypic doublets

None of the methods accepts a SeuratObject as input, but a SingleCellExperiment object.

Hopefully :

  • Seurat has a function to perform the conversion
  • The output results can be integrated into our Seurat object with ease

10.2 scds

## Fix seed
set.seed(my_seed)

## Run scds
sobj$doublet_scds.hybrid <- unname(
  scds::cxds_bcds_hybrid(
    Seurat::as.SingleCellExperiment(
      sobj, assay = "RNA"))$hybrid_score > 1)

## Contingencies
table(sobj$doublet_scds.hybrid)
Show output

FALSE  TRUE 
 4072   206 

10.3 scDblFinder

## Fix seed
set.seed(my_seed)

## Run scDblFinder (which needs another object type)
sobj$doublet_scDblFinder <- scDblFinder::scDblFinder(
  sce = Seurat::as.SingleCellExperiment(
    x = sobj, 
    assay = "RNA"),
  returnType = "table")$class == "doublet"

## Contingencies
table(sobj$doublet_scDblFinder)
Show output

FALSE  TRUE 
 4100   178 




10.4 Merge results

We merge results of the two methods

## Logical union of both methods
sobj$doublet_union <- sobj$doublet_scds.hybrid | sobj$doublet_scDblFinder

## Quantify doublets
table(sobj$doublet_union)
Show output

FALSE  TRUE 
 4035   243 


We can assess tool-specific and common doublets

### Singlets by default
sobj$doublet_viz <- "singlet"

### Union
sobj$doublet_viz[sobj$doublet_union] <- "both"

### scds-specific
sobj$doublet_viz[sobj$doublet_scds.hybrid & !sobj$doublet_scDblFinder] <- "scds"

### scDblFinder-specific
sobj$doublet_viz[sobj$doublet_scDblFinder & !sobj$doublet_scds.hybrid] <- "scDblFinder"

## Convert to factor
sobj$doublet_viz <- as.factor(sobj$doublet_viz)

## Contingencies
table(sobj$doublet_viz)
Show output

       both scDblFinder        scds     singlet 
        141          37          65        4035 


Beyond : Create an upset-plot for the doublet status according to the two methods used

## Build a list of cells tagged by one tool, the other, or both
dbl_list <-list(
  "scds" = colnames(sobj)[sobj$doublet_scds.hybrid & !sobj$doublet_scDblFinder],
  "scDblFinder" = colnames(sobj)[!sobj$doublet_scds.hybrid & sobj$doublet_scDblFinder],
  "both" = colnames(sobj)[sobj$doublet_scds.hybrid & sobj$doublet_scDblFinder])

## Draw the upset plot
UpSetR::upset(data = UpSetR::fromList(dbl_list), 
              nintersects = NA, 
              sets = rev(names(dbl_list)),
              keep.order = TRUE,
              order.by = "freq")
Show plot


Now we can remove barcodes identified as cell doublets, and visualize the cell space before and after.

10.5 Doublets filtering

10.5.1 BEFORE

### Doublets viz (before removal)
umap_dbl_unfilt <- SC.helper::QnD_viz(
  sobj = sobj, 
  group_by = "doublet_viz",
  return_plot = TRUE)
Show plot

10.5.2 Remove doublets

## Dimensions before removal
dim(sobj)
[1] 12508  4278
## Perform the removal
sobj <- sobj[,!sobj$doublet_union]

## Dimensions after
dim(sobj)
[1] 12508  4035

10.5.3 AFTER

### Doublets viz (after removal)
umap_dbl_filt <- SC.helper::QnD_viz(
  sobj = sobj, 
  group_by = "doublet_viz",
  return_plot = TRUE)
Show plot




Merge plots for ease of use :

## Using the patchwork package to merge plots (and ggplot2 to add titles)
patchwork::wrap_plots(
  list(
    umap_dbl_unfilt & ggplot2::ggtitle(label = "Cell doublets (unfiltered)"),
    umap_dbl_filt & ggplot2::ggtitle(label = "Cell doublets (filtered)")), 
  nrow = 1)
Show plot


Question

What do you observe when comparing before and after the doublets filtering ?




11 Save the Seurat object

We will save our Seurat object that is now filtered for doublets :

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

## Check
print(out_name)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/03_Preproc.3/RESULTS/05_TD3A_S5_Doublets.Filtered_12508.4035.RDS"
## Write on disk
saveRDS(object = sobj, 
        file = out_name)







12 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     

other attached packages:
[1] SeuratObject_5.0.2 sp_2.1-4          

loaded via a namespace (and not attached):
  [1] bitops_1.0-9                matrixStats_1.4.1          
  [3] spatstat.sparse_3.1-0       SC.helper_0.0.6            
  [5] httr_1.4.7                  RColorBrewer_1.1-3         
  [7] doParallel_1.0.17           alabaster.base_1.4.1       
  [9] tools_4.4.1                 sctransform_0.4.1          
 [11] backports_1.5.0             utf8_1.2.4                 
 [13] R6_2.5.1                    HDF5Array_1.32.0           
 [15] lazyeval_0.2.2              uwot_0.2.2                 
 [17] rhdf5filters_1.16.0         GetoptLong_1.0.5           
 [19] withr_3.0.1                 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] Rsamtools_2.20.0            foreign_0.8-86             
 [35] scater_1.32.1               parallelly_1.38.0          
 [37] limma_3.60.6                rstudioapi_0.17.0          
 [39] RSQLite_2.3.7               BiocIO_1.14.0              
 [41] generics_0.1.3              shape_1.4.6.1              
 [43] ica_1.0-3                   spatstat.random_3.3-2      
 [45] dplyr_1.1.4                 Matrix_1.7-1               
 [47] ggbeeswarm_0.7.2            fansi_1.0.6                
 [49] S4Vectors_0.42.1            abind_1.4-8                
 [51] lifecycle_1.0.4             SoupX_1.6.2                
 [53] yaml_2.3.10                 edgeR_4.2.2                
 [55] SummarizedExperiment_1.34.0 rhdf5_2.48.0               
 [57] SparseArray_1.4.8           BiocFileCache_2.12.0       
 [59] Rtsne_0.17                  grid_4.4.1                 
 [61] blob_1.2.4                  promises_1.3.0             
 [63] dqrng_0.4.1                 ExperimentHub_2.12.0       
 [65] crayon_1.5.3                miniUI_0.1.1.1             
 [67] lattice_0.22-6              beachmat_2.20.0            
 [69] cowplot_1.1.3               KEGGREST_1.44.0            
 [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] xgboost_1.7.8.1             future.apply_1.11.2        
 [79] codetools_0.2-20            leiden_0.4.3.1             
 [81] glue_1.8.0                  spatstat.univar_3.0-1      
 [83] data.table_1.16.2           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                 scds_1.20.0                
[103] alabaster.ranges_1.4.1      filelock_1.0.3             
[105] RcppAnnoy_0.0.22            UpSetR_1.4.0               
[107] GenomeInfoDb_1.40.1         bslib_0.8.0                
[109] irlba_2.3.5.1               vipor_0.4.7                
[111] KernSmooth_2.23-24          rpart_4.1.23               
[113] colorspace_2.1-1            BiocGenerics_0.50.0        
[115] DBI_1.2.3                   Hmisc_5.1-3                
[117] celldex_1.14.0              nnet_7.3-19                
[119] ggrastr_1.0.2               tidyselect_1.2.1           
[121] bit_4.5.0                   compiler_4.4.1             
[123] curl_5.2.3                  httr2_1.0.1                
[125] htmlTable_2.4.2             BiocNeighbors_1.22.0       
[127] DelayedArray_0.30.1         plotly_4.10.4              
[129] rtracklayer_1.64.0          checkmate_2.3.1            
[131] scales_1.3.0                lmtest_0.9-40              
[133] rappdirs_0.3.3              stringr_1.5.1              
[135] digest_0.6.37               goftest_1.2-3              
[137] spatstat.utils_3.1-0        alabaster.matrix_1.4.1     
[139] rmarkdown_2.28              XVector_0.44.0             
[141] htmltools_0.5.8.1           pkgconfig_2.0.3            
[143] base64enc_0.1-3             SingleR_2.6.0              
[145] sparseMatrixStats_1.16.0    MatrixGenerics_1.16.0      
[147] highr_0.11                  dbplyr_2.5.0               
[149] fastmap_1.2.0               rlang_1.1.4                
[151] GlobalOptions_0.1.2         htmlwidgets_1.6.4          
[153] UCSC.utils_1.0.0            shiny_1.9.1                
[155] DelayedMatrixStats_1.26.0   farver_2.1.2               
[157] jquerylib_0.1.4             zoo_1.8-12                 
[159] jsonlite_1.8.9              BiocParallel_1.38.0        
[161] RCurl_1.98-1.14             BiocSingular_1.20.0        
[163] magrittr_2.0.3              Formula_1.2-5              
[165] scuttle_1.14.0              GenomeInfoDbData_1.2.12    
[167] dotCall64_1.2               patchwork_1.3.0            
[169] Rhdf5lib_1.26.0             munsell_0.5.1              
[171] Rcpp_1.0.13                 viridis_0.6.5              
[173] reticulate_1.39.0           pROC_1.18.5                
[175] alabaster.schemas_1.4.0     stringi_1.8.4              
[177] zlibbioc_1.50.0             MASS_7.3-61                
[179] AnnotationHub_3.12.0        plyr_1.8.9                 
[181] parallel_4.4.1              listenv_0.9.1              
[183] ggrepel_0.9.6               scDblFinder_1.18.0         
[185] deldir_2.0-4                Biostrings_2.72.1          
[187] splines_4.4.1               tensor_1.5                 
[189] circlize_0.4.16             locfit_1.5-9.9             
[191] igraph_2.1.1                spatstat.geom_3.3-3        
[193] RcppHNSW_0.6.0              reshape2_1.4.4             
[195] stats4_4.4.1                ScaledMatrix_1.12.0        
[197] XML_3.99-0.17               BiocVersion_3.19.1         
[199] evaluate_1.0.1              scran_1.32.0               
[201] BiocManager_1.30.25         foreach_1.5.2              
[203] httpuv_1.6.15               RANN_2.6.2                 
[205] tidyr_1.3.1                 purrr_1.0.2                
[207] polyclip_1.10-7             future_1.34.0              
[209] clue_0.3-65                 scattermore_1.2            
[211] ggplot2_3.5.1               rsvd_1.0.5                 
[213] xtable_1.8-4                restfulr_0.0.15            
[215] RSpectra_0.16-2             later_1.3.2                
[217] viridisLite_0.4.2           tibble_3.2.1               
[219] GenomicAlignments_1.40.0    beeswarm_0.4.0             
[221] memoise_2.0.1               AnnotationDbi_1.66.0       
[223] IRanges_2.38.1              cluster_2.1.6              
[225] globals_0.16.3             
LS0tCnRpdGxlOiAiPENFTlRFUj5FQkFJSSBuMSAyMDI0IDogU0lOR0xFIENFTEwgQU5BTFlTSVMgVFJBSU5JTkc8QlI+IDxCPlBSRVBST0NFU1NJTkcgKElJSSk8L0I+PEJSPkZpbHRlcmluZywgY2VsbCBjeWxlLCBjZWxsIGRvdWJsZXRzPC9DRU5URVI+IgpkYXRlOiAiMjAyNC0xMS0xNy4yMiIKYXV0aG9yOiAiRUJBSUkgbjEgc2NSTkFzZXEgVGVhbSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IAogICAgYmFja2dyb3VuZDogYmxhY2sKICAgIGZpZ19oZWlnaHQ6IDYKICAgIGZpZ193aWR0aDogOAogICAgaGlnaGxpZ2h0OiB0YW5nbyAgIyMgVGhlbWUgZm9yIHRoZSBjb2RlIGNodW5rcwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlICAjIyBBZGRzIG51bWJlciB0byBoZWFkZXJzIChzZWN0aW9ucykKICAgIHRoZW1lOiBmbGF0bHkgICMjIENTUyB0aGVtZSBmb3IgdGhlIEhUTUwgcGFnZQogICAgdG9jOiB0cnVlICAjIyBBZGRzIGEgdGFibGUgb2YgY29udGVudAogICAgdG9jX2Zsb2F0OiAgIyMgVE9DIG9wdGlvbnMKICAgICAgY29sbGFwc2VkOiB0cnVlICAjIyBCeSBkZWZhdWx0LCB0aGUgVE9DIGlzIGZvbGRlZAogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlICMjIFNtb290aCBzY3JvbGwgb2YgdGhlIEhUTUwgcGFnZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUgIyMgSW5jbHVkZXMgYWxsIHBsb3RzL2ltYWdlcyB3aXRoaW4gdGhlIEhUTUwKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUgIyMgQWRkcyBhIGJ1dHRvbiB0byBkb3dubG9hZCB0aGUgUm1kCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIHRodW1ibmFpbHM6IGZhbHNlCiAgICBsaWdodGJveDogdHJ1ZQogICAgZmlnX2NhcHRpb246IGZhbHNlCiAgICBnYWxsZXJ5OiB0cnVlCiAgICB1c2VfYm9va2Rvd246IHRydWUKYWx3YXlzX2FsbG93X2h0bWw6IHRydWUgIyMgQWxsb3cgcGxhaW4gSFRNTCBjb2RlIGluIHRoZSBSbWQKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKPCEtLSBBZGQgdGhlIFJvc2NvZmYgYmFubmVyIC0tPgoKYGBge2NzcywgZWNobyA9IEZBTFNFfQpib2R5IHsKICBiYWNrZ3JvdW5kLWltYWdlOiB1cmwoJ2ViYWlpX2Jhbm5lci5wbmcnKTsKICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0OwogIGJhY2tncm91bmQtc2l6ZTogMTAwJTsKfQoKZGl2IHsKICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1NSwgMjU1LCAyNTUsIDAuMzUpICAgLyogMzUlIG9wYXF1ZSB3aGl0ZSAqLzsKICBwYWRkaW5nOiAwLjI1ZW07Cn0KYGBgCgo8IS0tIEFsbG93cyB0byBoaWRlIHRoZSBUT0MgYnkgZGVmYXVsdCwgZGlzcGxheSBpdCB3aXRoIGEgYnV0dG9uLCBtb3ZlIGl0IHRvIHRoZSByaWdodCBvciBsZWZ0IG9mIHRoZSBwYWdlIC0tPgoKYHIgSG1pc2M6OmhpZGluZ1RPQyhidXR0b25MYWJlbCA9ICdTaG93IFRPQycsIGhpZGRlbiA9IFRSVUUsIHRvY1NpZGUgPSAnbGVmdCcsIGJ1dHRvblNpZGU9J2xlZnQnLCBwb3NDb2xsYXBzZSA9ICdtYXJnaW4nLCBsZXZlbHMgPSAzKWAKCmBgYHtyIGtuaXRfc2V0dXAsIGVjaG8gPSBGQUxTRX0KIyBvcHRpb25zKHdpZHRoID0gNjApOwprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsICAgICAgICAjIFByaW50IHRoZSBjb2RlCiAgZXZhbCA9IFRSVUUsICAgICAgICAjIFJ1biBjb21tYW5kIGxpbmVzCiAgbWVzc2FnZSA9IEZBTFNFLCAgICAjIFByaW50IG1lc3NhZ2VzCiAgcHJvbXB0ID0gRkFMU0UsICAgICAjIERvIG5vdCBkaXNwbGF5IHByb21wdAogIGNvbW1lbnQgPSBOQSwgICAgICAgIyBObyBjb21tZW50cyBvbiB0aGlzIHNlY3Rpb24KICB3YXJuaW5nID0gRkFMU0UsICAgICMgRGlzcGxheSB3YXJuaW5ncwogIHRpZHkgPSBGQUxTRSwKICBmaWcuYWxpZ24gPSAiY2VudGVyIiwKICBmaWcuYWxpZ249ImNlbnRlciIsIAogICMgcmVzdWx0cyA9ICdoaWRlJwogIHdpZHRoID0gMTAwICAgICAgICMgTnVtYmVyIG9mIGNoYXJhY3RlcnMgcGVyIGxpbmUKKQpgYGAKCmBgYHtyIGtuaXRfaG9vaywgZWNobyA9IEZBTFNFfQpob29rcyA9IGtuaXRyOjprbml0X2hvb2tzJGdldCgpCmhvb2tfZm9sZGFibGUgPSBmdW5jdGlvbih0eXBlKSB7CiAgZm9yY2UodHlwZSkKICBmdW5jdGlvbih4LCBvcHRpb25zKSB7CiAgICByZXMgPSBob29rc1tbdHlwZV1dKHgsIG9wdGlvbnMpCiAgICAKICAgIGlmIChpc0ZBTFNFKG9wdGlvbnNbW3Bhc3RlMCgiZm9sZC4iLCB0eXBlKV1dKSkgcmV0dXJuKHJlcykKICAgIAogICAgcGFzdGUwKAogICAgICAiPGRldGFpbHM+PHN1bW1hcnk+U2hvdyAiLCB0eXBlLCAiPC9zdW1tYXJ5PlxuXG4iLAogICAgICByZXMsCiAgICAgICJcblxuPC9kZXRhaWxzPiIKICAgICkKICB9Cn0Ka25pdHI6OmtuaXRfaG9va3Mkc2V0KAogIG91dHB1dCA9IGhvb2tfZm9sZGFibGUoIm91dHB1dCIpLAogIHBsb3QgPSBob29rX2ZvbGRhYmxlKCJwbG90IikKKQpgYGAKCjwhLS0gQ1NTIHRvIGNvbG9yIGNodW5rcyBhbmQgb3V0cHV0cyAtLT4KCmBgYHtjc3MsIGVjaG89RkFMU0V9Ci5ub3RydW4gewogIGJhY2tncm91bmQtY29sb3I6IGxpZ2h0Z3JleSAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGJsYWNrICFpbXBvcnRhbnQ7Cn0KLm5vdHJ1bm8gewogIGJhY2tncm91bmQtY29sb3I6IGxpZ2h0Z3JleSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQoucXVlc3Rpb24gewogIGJhY2tncm91bmQtY29sb3I6IGFxdWFtYXJpbmUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgbGltZWdyZWVuICFpbXBvcnRhbnQ7Cn0KLnF1ZXN0aW9ubyB7CiAgYmFja2dyb3VuZC1jb2xvcjogYXF1YW1hcmluZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQouYW5zd2VyIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBuYXZham93aGl0ZSAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGJyb3duICFpbXBvcnRhbnQ7Cn0KLmFuc3dlcm8gewogIGJhY2tncm91bmQtY29sb3I6IG5hdmFqb3doaXRlICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9Ci5iZXlvbmQgewogIGJhY2tncm91bmQtY29sb3I6IHZpb2xldCAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIHB1cnBsZSAhaW1wb3J0YW50Owp9Ci5iZXlvbmRvIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB2aW9sZXQgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQUkVBTUJMRQoKIyMgUHVycG9zZSBvZiB0aGlzIHNlc3Npb24KClRoaXMgZmlsZSBkZXNjcmliZXMgdGhlIGRpZmZlcmVudCBzdGVwcyB0byBwZXJmb3JtIHRoZSAqKnRoaXJkIHBhcnQqKiBvZgp0aGUgc2luZ2xlIGNlbGwgUk5Bc2VxIGRhdGEgYW5hbHlzaXMgdHJhaW5pbmcgY291cnNlIGZvciB0aGUgKipFQkFJSSBuMQoyMDI0KiosIGNvdmVyaW5nIHRoZXNlIHN0ZXBzIDoKCi0gICAqKkZpbHRlcmluZyoqIG9mIGJhZCBjZWxscyBhbmQgZXh0cmVtZSBsb3ctZXhwcmVzc2lvbiBmZWF0dXJlcwoKLSAgIEVzdGltYXRpb24gb2YgdGhlICoqY2VsbCBjeWNsZSoqIHBoYXNlCgotICAgSWRlbnRpZmljYXRpb24gYW5kIHJlbW92YWwgb2YgKipjZWxsIGRvdWJsZXRzKioKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFN0YXJ0IFJzdHVkaW8KCi0gICBVc2luZyB0aGUgW09wZW5PbkRlbWFuZCBjaGVhdAogICAgc2hlZXRdKGh0dHBzOi8vaWZiLWVsaXhpcmZyLmdpdGh1Yi5pby9FQkFJSS8yMDIzL2ViYWlpbjEvU2luZ2xlQ2VsbC8yMDI0X1REX09wZW5PbkRlbWFuZC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9LAogICAgY29ubmVjdCB0byB0aGUgW09wZW5PbkRlbWFuZAogICAgcG9ydGFsXShodHRwczovL29uZGVtYW5kLmNsdXN0ZXIuZnJhbmNlLWJpb2luZm9ybWF0aXF1ZS5mcil7dGFyZ2V0PSJfYmxhbmsifSBhbmQKICAgICoqY3JlYXRlIGEgUnN0dWRpbyBzZXNzaW9uKiogd2l0aCB0aGUgcmlnaHQgcmVzb3VyY2UgcmVxdWlyZW1lbnRzLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgV2FybS11cAoKLSAgIFdlIHNldCAqKmNvbW1vbiBwYXJhbWV0ZXJzKiogd2Ugd2lsbCB1c2UgdGhyb3VnaG91dCB0aGlzIHNlc3Npb24gOgoKYGBge3Igc2V0cGFyYW19CiMjIFNldCB5b3VyIHByb2plY3QgbmFtZQpwcm9qZWN0X25hbWUgPC0gImViYWlpX3NjX3RlYWNoZXJzIiAgIyBEbyBub3QgY29weS1wYXN0ZSB0aGlzICEgSXQncyBNWSBwcm9qZWN0ICEgUHV0IHlvdXJzICEhCgojIyBTZWVkIGZvciB0aGUgUk5HCm15X3NlZWQgPC0gMTMzN0wKCiMjIE1pbmltdW0gY2VsbHMgd2l0aCBjb3VudHMgdG8ga2VlcCBhIGZlYXR1cmUKbWluX2NlbGxzIDwtIDUKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQcmVwYXJlIHRoZSBkYXRhIHN0cnVjdHVyZQoKV2Ugd2lsbCBkbyB0aGUgc2FtZSBhcyBmb3IgZm9ybWVyIHN0ZXBzLCBqdXN0IGNoYW5naW5nIHRoZSBzZXNzaW9uIG5hbWUKOgoKIyMgTWFpbiBkaXJlY3RvcnkKCmBgYHtyIG1haW5kaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIFByZXBhcmluZyB0aGUgcGF0aApURF9kaXIgPC0gcGFzdGUwKCIvc2hhcmVkL3Byb2plY3RzLyIsIHByb2plY3RfbmFtZSwgIi9TQ19URCIpCgojIyBDcmVhdGluZyB0aGUgcm9vdCBkaXJlY3RvcnkKZGlyLmNyZWF0ZShwYXRoID0gVERfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIHJvb3QgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChURF9kaXIpCmBgYAoKIyMgQ3VycmVudCBzZXNzaW9uCgpgYGB7ciBzZXNzaW9uZGlyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIyBDcmVhdGluZyB0aGUgc2Vzc2lvbiAoUHJlcHJvYy4zKSBkaXJlY3RvcnkKc2Vzc2lvbl9kaXIgPC0gcGFzdGUwKFREX2RpciwgIi8wM19QcmVwcm9jLjMiKQpkaXIuY3JlYXRlKHBhdGggPSBzZXNzaW9uX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSBzZXNzaW9uIGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoc2Vzc2lvbl9kaXIpCmBgYAoKIyMgSW5wdXQgZGlyZWN0b3J5CgpgYGB7ciBpbmRpciwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyMgQ3JlYXRpbmcgdGhlIElOUFVUIGRhdGEgZGlyZWN0b3J5CmlucHV0X2RpciA8LSBwYXN0ZTAoc2Vzc2lvbl9kaXIsICIvREFUQSIpCmRpci5jcmVhdGUocGF0aCA9IGlucHV0X2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSBpbnB1dCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KGlucHV0X2RpcikKYGBgCgojIyBHZW5lbGlzdHMgZGlyZWN0b3J5CgpgYGB7ciByZXNkaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CnJlc19kaXIgPC0gcGFzdGUwKFREX2RpciwgIi9SZXNvdXJjZXMiKQpnbGlzdF9kaXIgPC0gcGFzdGUwKHJlc19kaXIsICIvR2VuZWxpc3RzIikKCiMjIFByaW50IHRoZSByZXNvdXJjZXMgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChnbGlzdF9kaXIpCmBgYAoKIyMgT3V0cHV0IGRpcmVjdG9yeQoKYGBge3Igb3V0ZGlyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIyBDcmVhdGluZyB0aGUgT1VUUFVUIGRhdGEgZGlyZWN0b3J5Cm91dHB1dF9kaXIgPC0gcGFzdGUwKHNlc3Npb25fZGlyLCAiL1JFU1VMVFMiKQpkaXIuY3JlYXRlKHBhdGggPSBvdXRwdXRfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIG91dHB1dCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KG91dHB1dF9kaXIpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUmVsb2FkIHRoZSBTZXVyYXQgT2JqZWN0CgotICAgV2UgY2FuIHJlbG9hZCB0aGUgb2JqZWN0IHdlIHNhdmVkIGF0IHRoZSBmb3JtZXIgc3RlcAoKYGBge3IgZGF0YWxvYWR9CiMjIFRoZSBsYXRlc3QgU2V1cmF0IG9iamVjdCBzYXZlZCBhcyBSRFMgKG5hbWUpCnNvYmpfZmlsZSA8LSAiMDJfVEQzQV9TNV9NZXRyaWNzLkJpb18zMTA1My40NTg3LlJEUyIKCiMjIFRoZSBsYXRlc3QgU2V1cmF0IG9iamVjdCBzYXZlZCBhcyBSRFMgKGZ1bGwgcGF0aCkKc29ial9wYXRoIDwtIHBhc3RlMChURF9kaXIsIAogICAgICAgICAgICAgICAgICAgICIvMDJfUHJlcHJvYy4yL1JFU1VMVFMvIiwKICAgICAgICAgICAgICAgICAgICBzb2JqX2ZpbGUpCgpmb3JjZSA8LSBGQUxTRSAgIyMgVG8gZm9yY2UgYSByZS1kb3dubG9hZCBvZiBhIFplbm9kby1ob3N0ZWQgYmFja3VwCmxvY2FsIDwtIEZBTFNFICAjIyBUbyBmb3JjZSBhIGxvYWRpbmcgZnJvbSBhIGxvY2FsIGJhY2t1cAoKIyMgSW4gY2FzZSBvZiBlcnJvci9sb3N0IGRhdGEgOiBmb3JjZSBhIHJlbG9hZCBmcm9tIGEgWmVub2RvIGJhY2t1cCByZXBvc2l0b3J5CmlmKGZvcmNlKSB7CiAgemVuX2lkIDwtICIxNDAzNTI5MyIKICB6ZW5fYmFja3VwX2ZpbGUgPC0gcGFzdGUwKCJodHRwczovL3plbm9kby5vcmcvcmVjb3Jkcy8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgemVuX2lkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIi9maWxlcy8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc29ial9maWxlKQogIGRvd25sb2FkLmZpbGUodXJsID0gemVuX2JhY2t1cF9maWxlLAogICAgICAgICAgICAgICAgZGVzdGZpbGUgPSBzb2JqX3BhdGgpCn0KCiMjIEluIGNhc2Ugb2YgZXJyb3IvbG9zdCBkYXRhIDogZm9yY2UgYSByZWxvYWQgZnJvbSBhIGxvY2FsIGJhY2t1cCByZXBvc2l0b3J5CmlmKGxvY2FsKSB7CiAgc29ial9wYXRoIDwtIHBhc3RlMCgKICAgICIvc2hhcmVkL3Byb2plY3RzLzI0MjJfZWJhaWlfbjEvYXRlbGllcl9zY3JuYXNlcS9URC9CQUNLVVAvUkRTLyIsCiAgICBzb2JqX2ZpbGUpCn0KCiMjIExvYWQgdGhlIG9iamVjdApzb2JqIDwtIHJlYWRSRFMoZmlsZSA9IHNvYmpfcGF0aCkKYGBgCgo8YnI+PGJyPgoKTm93LCB3ZSBmaW5hbGx5IGhhdmUgYWxsIHRoZSBtZXRyaWNzIGFuZCBiaWFzIHNvdXJjZXMgd2UgY291bGQKdXNlLCBzbyB3ZSBjYW4gYWN0dWFsbHkgZ2V0IHRvIHRoZSBmaWx0ZXJpbmcgc3RlcC4KCiMgRmlsdGVyaW5nCgojIyBGZWF0dXJlcwoKLSAgIEFzIGFscmVhZHkgZXhwbGFpbmVkLCB0aGUgKipzcGFyc2l0eSoqIG9mIHRoZSBjb3VudCBtYXRyaXggaXMgaGlnaCwgX3ZlcnlfCmhpZ2guIAoKLSAgIFRvIHRoZSBwb2ludCB0aGF0IHRoZXJlIHByb2JhYmx5IGFyZSBzb21lIGZlYXR1cmVzIHRoYXQgaGF2ZSAqKnNvCmxvdyBleHByZXNzaW9uKiogbGV2ZWwgdGhhdCB0aGV5IHdlcmUgb25seSBjb3VudGVkIGluICoqdmVyeSBmZXcgY2VsbHMqKi4gQXMKc3VjaCwgdGhleSBoYXZlIGFic29sdXRlbHkgKipubyBjaGFuY2UqKiB0byBjaGFyYWN0ZXJpemUgYW55IGNlbGwgdHlwZSwgbm9yCmhhcmJvciBzb21lIHZhcmlhdGlvbiBiZXR3ZWVuIGRpZmZlcmVudCBjZWxsIHR5cGVzLgoKLSAgIEFzIHN1Y2gsIHdlIHNob3VsZCAqKmRpc2NhcmQqKiB0aGVzZSBmZWF0dXJlcwoKPGJyPgoKV2hhdCBhcmUgdGhlIGFjdHVhbCBkaW1lbnNpb25zIG9mIG91ciBleHByZXNzaW9uIGRhdGEgPwoKYGBge3Igc2V1ZGltMX0KIyMgRGltZW5zaW9ucyBvZiBvdXIgU2V1cmF0IG9iamVjdApkaW0oc29iaikKYGBgCgo8YnI+CgpXZSB3YW50IHRvIHJlbW92ZSB0aGUgZmVhdHVyZXMgdGhhdCBhcmUgZXhwcmVzc2VkIGluIGxlc3MgdGhhbiAqKmByIG1pbl9jZWxsc2AqKgpjZWxscy4KCkZpcnN0LCB3ZSBxdWFudGlmeSwgZm9yIGVhY2ggZmVhdHVyZSwgdGhlIG51bWJlciBvZiBjZWxscyB3aXRoIDAgY291bnQuCgpgYGB7ciBuZmVhdGRpZmZ9CiMjIENvbXB1dGUgdGhlIGFtb3VudCBvZiAwcyBwZXIgZmVhdHVyZQpuRmVhdF96ZXJvIDwtIHNwYXJzZU1hdHJpeFN0YXRzOjpyb3dDb3VudHMoCiAgeCA9IFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKAogICAgb2JqZWN0ID0gc29iaiwgCiAgICBhc3NheSA9ICJSTkEiLCAKICAgIGxheWVyID0gImNvdW50cyIpLCAKICB2YWx1ZSA9IDApCgojIyBJbnZlcnNpb24gOiBjb21wdXRpbmcgdGhlIG51bWJlciBvZiBjZWxscyB3aXRoIGF0IGxlYXN0IG9uZSBjb3VudCwgcGVyIGZlYXR1cmUKbkZlYXRfbm9uemVybyA8LSBuY29sKHNvYmopIC0gbkZlYXRfemVybwoKIyMgSWRlbnRpZnkgdGhvc2Ugd2l0aCBhdCBsZWFzdCA1IGNlbGxzIHdpdGggZXhwcmVzc2lvbgpuRmVhdF9rZWVwIDwtIG5GZWF0X25vbnplcm8gPj0gbWluX2NlbGxzCgojIyBRdWFudGlmeSB0aGUgc2VsZWN0ZWQgb25lcwp0YWJsZShuRmVhdF9rZWVwKQpgYGAKCjxicj4KCkFuZCBub3cgd2UgY2FuICoqZGlzY2FyZCoqIGZlYXR1cmVzIGV4cHJlc3NlZCBpbiBsZXNzIHRoYW4gYHIgbWluX2NlbGxzYCBjZWxscyA6CgpgYGB7ciBwcmVmZWF0ZmlsdHZpeiwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQpmaWx0YzVfcHJlIDwtIFNDLmhlbHBlcjo6UW5EX3Zpeihzb2JqID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuX3Bsb3QgPSBUUlVFKQpgYGAKCjxicj4KCmBgYHtyIGZlYXRzZWx9CgojIyBSZXN0cmljdCB0aGUgU2V1cmF0IG9iamVjdApzb2JqIDwtIHNvYmpbbkZlYXRfa2VlcCxdCgpgYGAKCjxicj4KCldoYXQgYXJlIHRoZSBTZXVyYXQgb2JqZWN0IGRpbWVuc2lvbnMsIG5vdyA/CgpgYGB7ciBzZXVkaW0yLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CmRpbShzb2JqKQpgYGAKCjxicj4KCldlIGNhbiAqKnZpc3VhbGl6ZSB0aGUgY2VsbCBzcGFjZSoqIGFmdGVyIHRoaXMgZmVhdHVyZXMgZmlsdGVyaW5nCgpgYGB7ciBwb3N0ZmVhdGZpbHR2aXp9CmZpbHRjNV9wb3N0IDwtIFNDLmhlbHBlcjo6UW5EX3Zpeihzb2JqID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm5fcGxvdCA9IFRSVUUpCmBgYAoKPGJyPgoKTWVyZ2luZyBwbG90cyBmb3IgZWFzZSBvZiB2aXN1YWxpemF0aW9uIDoKCmBgYHtyIGM1X3B3LCBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDYsIGNsYXNzLnNvdXJjZT1jKCJmb2xkLWhpZGUiLCAibm90cnVuIiksIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CiMjIFVzaW5nIHRoZSBwYXRjaHdvcmsgcGFja2FnZSB0byBtZXJnZSBwbG90cyAoYW5kIGdncGxvdDIgdG8gYWRkIHRpdGxlcykKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKAogIGxpc3QoCiAgICBmaWx0YzVfcHJlICYgZ2dwbG90Mjo6Z2d0aXRsZShsYWJlbCA9ICJCRUZPUkUiKSwKICAgIGZpbHRjNV9wb3N0ICYgZ2dwbG90Mjo6Z2d0aXRsZShsYWJlbCA9ICJBRlRFUiIpKSwgCiAgbnJvdyA9IDEpCmBgYAoKPGJyPgoKLSAgICoqUXVlc3Rpb24qKiA6IAogICAgCiAgICBgYGB7ciBxX21pbmNlbGwsIGV2YWwgPSBGQUxTRSwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KICAgIERvIHlvdSBzZWUgYW55IGRpZmZlcmVuY2Ugd2hlbiBjb21wYXJpbmcgYmVmb3JlIHZzIGFmdGVyIGZlYXR1cmVzIGZpbHRlcmluZyA/CiAgICBgYGAKICAgIAogICAgPGJyPgogICAgCiAgICBgYGB7ciBhX21pbmNlbGwsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQogICAgIyMgLiBOb3QgbXVjaCBjaGFuZ2VkLCBtYXliZSBjZWxsIGdyb3VwcyBzZWVtIGEgYml0IG1vcmUgY29uZGVuc2VkCiAgICAjIwogICAgIyMgLiBUaGlzIGlzIGV4cGVjdGVkLCBhcyB3ZSByZW1vdmVkIGZlYXR1cmVzIHdpdGggYWxtb3N0IG5vIGV4cHJlc3Npb24KICAgIGBgYAogICAgCjxicj48YnI+CgojIyBDZWxscwoKV2UgYXJlIG5vdyBhYmxlIHRvICoqYXBwbHkgYWxsIHRoZSBmaWx0ZXJpbmcgc3RyYXRlZ2llcyoqIHdlIGVzdGFibGlzaGVkIGZvcgplYWNoIFFDIG1ldHJpYy4KCi0gICBJZGVudGlmeSAiZ29vZCIgY2VsbHMKCmBgYHtyIGJja2VlcH0KIyMgSWRlbnRpZnkgdGhlIGNlbGxzIHRvIGtlZXAKYmNfa2VlcCA8LSAgc29iaiRuRmVhdHVyZV9STkFfaW5fcmFuZ2UgJiAKICAgICAgICAgICAgc29iaiRuQ291bnRfUk5BX2luX3JhbmdlICYgCiAgICAgICAgICAgIHNvYmokcGVyY2VudF9tdF9pbl9yYW5nZSAmIAogICAgICAgICAgICBzb2JqJHBlcmNlbnRfcmJfaW5fcmFuZ2UgJiAKICAgICAgICAgICAgc29iaiRwZXJjZW50X3N0X2luX3JhbmdlCgojIyBDb250ZW5nZW5jeQp0YWJsZShiY19rZWVwKQpgYGAKCjxicj4KCi0gICBWaXN1YWxpemUgbWV0cmljcyBmaWx0ZXJpbmcgZWZmZWN0IChhcyBhbiB1cHNldC1wbG90KQoKICAgIGBgYHtyIGJjdXBzZXQsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KICAgICMjIENyZWF0ZSBhIGxpc3Qgb2YgYWxsIGNlbGxzIGZpbHRlcmVkIG91IGZvciBlYWNoIGNyaXRlcmlvbgogICAgdXBfbGlzdCA8LWxpc3QoCiAgICAgICJuRmVhdHVyZSIgPSBjb2xuYW1lcyhzb2JqKVshc29iaiRuRmVhdHVyZV9STkFfaW5fcmFuZ2VdLAogICAgICAibkNvdW50IiA9IGNvbG5hbWVzKHNvYmopWyFzb2JqJG5Db3VudF9STkFfaW5fcmFuZ2VdLAogICAgICAiJU1JVE8iID0gY29sbmFtZXMoc29iailbIXNvYmokcGVyY2VudF9tdF9pbl9yYW5nZV0sCiAgICAgICIlUklCTyIgPSBjb2xuYW1lcyhzb2JqKVshc29iaiRwZXJjZW50X3JiX2luX3JhbmdlXSwKICAgICAgIiVTVFJFU1MiID0gY29sbmFtZXMoc29iailbIXNvYmokcGVyY2VudF9zdF9pbl9yYW5nZV0pCiAgICAKICAgICMjIENyZWF0ZSBhbiB1cHNldC1wbG90CiAgICBVcFNldFI6OnVwc2V0KGRhdGEgPSBVcFNldFI6OmZyb21MaXN0KHVwX2xpc3QpLCAKICAgICAgICAgICAgICAgICAgbmludGVyc2VjdHMgPSBOQSwgCiAgICAgICAgICAgICAgICAgIHNldHMgPSByZXYobmFtZXModXBfbGlzdCkpLAogICAgICAgICAgICAgICAgICBrZWVwLm9yZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgb3JkZXIuYnkgPSAiZnJlcSIpCiAgICBgYGAKCjxicj4KCi0gICBBcHBseSB0aGUgZmlsdGVyCgogICAgYGBge3IgY2VsbGZpbHRfMX0KICAgICMjIFNldXJhdCBvYmplY3QgQkVGT1JFIGNlbGwgZmlsdGVyaW5nCiAgICBkaW0oc29iaikKICAgIGBgYAoKICAgIGBgYHtyIGNlbGxmaWx0XzJ9CiAgICAjIyBBcHBseSBjZWxsIGZpbHRlcmluZwogICAgc29iaiA8LSBzdWJzZXQoeCA9IHNvYmosIGNlbGxzID0gY29sbmFtZXMoc29iailbYmNfa2VlcF0pCiAgICAKICAgICMjIFNldXJhdCBvYmplY3QgQUZURVIgY2VsbCBmaWx0ZXJpbmcKICAgIGRpbShzb2JqKQogICAgYGBgCgo8YnI+CgpXZSBjYW4gKip2aXN1YWxpemUgdGhlIGNlbGwgc3BhY2UqKiBzaW5jZSB0aGlzIGNlbGwgZmlsdGVyaW5nCgpgYGB7ciBwb3N0Y2VsbGZpbHR2aXp9ClNDLmhlbHBlcjo6UW5EX3Zpeihzb2JqID0gc29iaikKYGBgCgo8YnI+CgotICAgKipRdWVzdGlvbioqCiAgICAKICAgIGBgYHtyIHFfdml6ZmlsdCwgZXZhbCA9IEZBTFNFLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQogICAgRG8geW91IHNlZSBhbnkgZGlmZmVyZW5jZSB3aGVuIGNvbXBhcmluZyBiZWZvcmUgdnMgYWZ0ZXIgZmVhdHVyZXMgZmlsdGVyaW5nID8KICAgIGBgYAogICAgCiAgICA8YnI+CiAgICAKICAgIGBgYHtyIHFfcG9zdGNlbGxmaWx0LCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGV2YWwgPSBGQUxTRX0KICAgICMjIC4gTm90IG11Y2ggY2hhbmdlZCBhcyB3ZWxsICh3ZSBkaWQgbm90IGRpc2NhcmQgbWFueSBjZWxscykKICAgICMjCiAgICAjIyAuIFRoZSBiaWdnZXN0IGNsdXN0ZXIgc3RydWN0dXJlIHNlZW1zIG1vcmUgZGVmaW5lZAogICAgYGBgCiAgICAKICAgIDxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFNhdmUgdGhlIFNldXJhdCBvYmplY3QKCldlIHdpbGwgKipzYXZlIG91ciBTZXVyYXQgb2JqZWN0KiogdGhhdCBub3cgY29udGFpbnMgKipmaWx0ZXJlZCBjZWxscyBhbmQKZmVhdHVyZXMqKiA6CgpgYGB7ciBzYXZlcmRzMiwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyMgU2F2ZSBvdXIgU2V1cmF0IG9iamVjdCAocmljaCBuYW1pbmcpCm91dF9uYW1lIDwtIHBhc3RlMCgKICAgICAgICAgIG91dHB1dF9kaXIsICIvIiwgcGFzdGUoCiAgICAgICAgICAgIGMoIjAzIiwgU2V1cmF0OjpQcm9qZWN0KHNvYmopLCAiUzUiLCAKICAgICAgICAgICAgICAiTWV0cmljcy5GaWx0ZXJlZCIsIHBhc3RlKAogICAgICAgICAgICAgICAgZGltKHNvYmopLCAKICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gJy4nCiAgICAgICAgICAgICAgKQogICAgICAgICAgICApLCBjb2xsYXBzZSA9ICJfIiksCiAgICAgICAgICAgICIuUkRTIikKCiMjIENoZWNrCnByaW50KG91dF9uYW1lKQoKIyMgV3JpdGUgb24gZGlzawpzYXZlUkRTKG9iamVjdCA9IHNvYmosIAogICAgICAgIGZpbGUgPSBvdXRfbmFtZSkKYGBgCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIENlbGwgY3ljbGUgc2NvcmVzCgotICAgV2UgYXJlIGN1cnJlbnRseSBhbmFseXppbmcgKippbmRlcGVuZGVudCoqIHByb2ZpbGVzIGZyb20gICoqaXNvbGF0ZWQqKiBjZWxscywgZnJvbSBhIHNhbXBsZSBkaXNzb2NpYXRpb24KCi0gICBBcyBzdWNoLCBjZWxscyB3ZXJlIG1vc3QgcHJvYmFibHkgKipub3Qgc3luY2hyb25pemVkKiosIHRodXMgdGhlIGVmZmVjdCBvZiB0aGVpciAqKmNlbGwgY3ljbGUgc3RhdGUqKiBvbiBnZW5lcyBleHByZXNzaW9uIG1heSBiZSBzdHJvbmcsIHRvIHRoZSBwb2ludCB0aGF0IGl0IGNhbiAqKmJpYXMgdGhlIGRhdGEqKiAoaWUsIG1hc2sgc29tZSBsb3dlciBhbXBsaXR1ZGUgYmlvbG9naWNhbCB2YXJpYXRpb24pLgoKLSAgIEluIG9yZGVyIHRvICoqYXNzZXNzKiogKGFuZCBtYXliZSwgX3JlbW92ZV8pIHRoaXMgYmlhcywgd2UgaGF2ZSB0byAqKnF1YW50aWZ5KiogaXQuCgotICAgV2Ugd2lsbCBwZXJmb3JtIHRoaXMgZXN0aW1hdGlvbiB0aGFua3MgdG8gaGV1cmlzdGljcyBiYXNlZCBvbiAqKmtub3dsZWRnZSoqIDogU2V1cmF0IGluY2x1ZGVzIGEgbWV0aG9kIHRoYXQgZXZhbHVhdGVzIHRoZSBjZWxsIGN5Y2xlIHBoYXNlIG9mIGNlbGxzIHRocm91Z2ggKipzY29yZXMgZm9yIHRoZSBTIGFuZCBHMk0gcGhhc2VzKiosIGVhY2ggYmFzZWQgb24gcGhhc2Utc3BlY2lmaWMgKipnZW5lIHNpZ25hdHVyZXMqKi4KCi0gICBGb3IgdGhpcyBzdGVwLCB3ZSB3aWxsIHVzZSBhZGRpdGlvbmFsIGdlbmUgbGlzdHMgZnJvbSBrbm93bGVkZ2UKICAgIChjZWxsIGN5Y2xlIHBoYXNlKSwgaG9zdGVkIGluIGEgWmVub2RvIHJlc3Bvc2l0b3J5IChJZCA6CiAgICBbMTQwMzczNTVdKGh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmRzLzE0MDM3MzU1ICJaZW5vZG8gZ2VuZSBsaXN0cyIpe3RhcmdldD0iX2JsYW5rIn0pCgojIyBEb3dubG9hZCBnZW5lIGxpc3RzCgotICAgV2Ugd2lsbCBkaXJlY3RseSByZXRyaWV2ZSBkYXRhIGZyb20gWmVub2RvIHRvIHlvdXIgYGlucHV0X2RpcmAgOgoKICAgIGBgYHtyIGRsemVubWF0Mn0KICAgICMjIFplbm9kbyBJRAogICAgemVuX2lkIDwtICcxNDEwMTUwNicKICAgIAogICAgIyMjIE5hbWVkIGZpbGVzICh3aWxsIGJlIHVzZWQgbGF0ZXIgb24gISkKICAgIGNjX2ZpbGUgPC0gIm11c19tdXNjdWx1c19TZXVyYXRfY2MuZ2VuZXNfMjAxOTEwMzEucmRzIgogICAgCiAgICAjIyBGaWxlbmFtZShzKSB0byByZXRyaWV2ZQogICAgdG9nZXRfZmlsZXMgPC0gYyhjY19maWxlKQogICAgCiAgICAjIyBGb2xkZXIgdG8gc3RvcmUgcmV0cmlldmVkIGZpbGVzCiAgICBsb2NhbF9mb2xkZXIgPC0gZ2xpc3RfZGlyCiAgICAKICAgICMjIFVzZSBsb2NhbCBiYWNrdXAgPwogICAgYmFja3VwIDwtIEZBTFNFCiAgICBpZihiYWNrdXApIG1lc3NhZ2UoIlVzaW5nIGxvY2FsIGJhY2t1cCAhIikKICAgIAogICAgIyMgRm9yY2UgZG93bmxvYWQgPwogICAgZm9yY2UgPC0gRkFMU0UKICAgIGlmKGZvcmNlKSBtZXNzYWdlKCJGb3JjaW5nIChyZSlkb3dubG9hZCAhIikKICAgIAogICAgIyMjIERlZmluZSByZW1vdGUgZm9sZGVyCiAgICByZW1vdGVfZm9sZGVyIDwtIGlmIChiYWNrdXApIHsKICAgICAgIi9zaGFyZWQvcHJvamVjdHMvMjQyMl9lYmFpaV9uMS9hdGVsaWVyX3Njcm5hc2VxL1REL1JFU09VUkNFUy9HRU5FTElTVFMvIgogICAgfSAgZWxzZSB7CiAgICAgIHBhc3RlMCgiaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvIiwgemVuX2lkLCAiL2ZpbGVzLyIpCiAgICB9CiAgICAKICAgICMjIyBSZWNvbnN0cnVjdCB0aGUgaW5wdXQgcGF0aHMKICAgIHJlbW90ZV9wYXRoIDwtIHBhc3RlMChyZW1vdGVfZm9sZGVyLCAiLyIsIHRvZ2V0X2ZpbGVzKQogICAgCiAgICAjIyMgUmVjb25zdHJ1Y3QgdGhlIG91dHB1dCBwYXRocwogICAgbG9jYWxfcGF0aCA8LSBwYXN0ZTAobG9jYWxfZm9sZGVyLCAiLyIsIHRvZ2V0X2ZpbGVzKQogICAgCiAgICAjIyBSZXRyaWV2ZSBmaWxlcyAoaWYgdGhleSBkb24ndCBleGlzdCksIGluIGxvb3AKICAgIGZvciAodGcgaW4gc2VxX2Fsb25nKHRvZ2V0X2ZpbGVzKSkgewogICAgICAjIyBJZiB0aGUgZmlsZSBkb2VzIG5vdCBsb2NhbGx5IGV4aXN0CiAgICAgIGlmICghZmlsZS5leGlzdHMobG9jYWxfcGF0aFt0Z10pIHwgZm9yY2UpIHsKICAgICAgICAjIyBSZXRyaWV2ZSBkYXRhCiAgICAgICAgaWYoYmFja3VwKSB7CiAgICAgICAgICBmaWxlLmNvcHkoZnJvbSA9IHJlbW90ZV9wYXRoW3RnXSwKICAgICAgICAgICAgICAgICAgICB0byA9IGxvY2FsX3BhdGhbdGddKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBkb3dubG9hZC5maWxlKHVybCA9IHJlbW90ZV9wYXRoW3RnXSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gbG9jYWxfcGF0aFt0Z10pCiAgICAgICAgfQogICAgICAgICMjIENoZWNrIGlmIGRvd25sb2FkZWQgZmlsZXMgZXhpc3QgbG9jYWxseQogICAgICAgIGlmKGZpbGUuZXhpc3RzKGxvY2FsX3BhdGhbdGddKSkgbWVzc2FnZSgiXHRPSyIpCiAgICAgIH0gZWxzZSBtZXNzYWdlKHBhc3RlMCh0b2dldF9maWxlc1t0Z10sICIgYWxyZWFkeSBkb3dubG9hZGVkICEiKSkKICAgIH0KICAgIGBgYAoKPGJyPgoKIyMgTG9hZCBnZW5lIGxpc3RzCgpgYGB7ciBjY19sb2FkfQojIyBUaGUgY2VsbCBjeWNsZSBnZW5lIGxpc3RzIGZpbGUKY2NfZmlsZSA8LSBwYXN0ZTAoZ2xpc3RfZGlyLCAKICAgICAgICAgICAgICAgICAgIi8iLCAKICAgICAgICAgICAgICAgICAgY2NfZmlsZSkKCiMjIExvYWQgdGhlIGNlbGwgY3ljbGUgcmVmZXJlbmNlIGdlbmVzIGxpc3RzCmNjX2dlbmVzIDwtIHJlYWRSRFMoZmlsZSA9IGNjX2ZpbGUpCgojIyBIYXZlIGEgbG9vayBvbiBpdHMgY29udGVudApzdHIoY2NfZ2VuZXMpCmBgYAoKPGJyPgoKLSAgICoqUXVlc3Rpb24qKgogICAgCiAgICBgYGB7ciBxX2NjZ2VuZXMsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CiAgICBBcyBleHBsYWluZWQsIHdlIHdpbGwgdXNlIGdlbmUgbGlzdHMgZXh0cmFjdGVkIGZyb20gY29tbXVuaXR5IGtub3dsZWRnZS4gCiAgICBPdXIgZGF0YSBjb250YWluIHZhbHVlcyBmb3IgZ2VuZXMgYWxzbywgc28gd2Ugd2lsbCBjcm9zcyB0aGVtLiAKICAgIElzIHRoZXJlIHNvbWV0aGluZyB3ZSBzaG91bGQgY2hlY2sgPwogICAgYGBgCiAgICAKICAgIDxicj4KICAgIAogICAgYGBge3IgcV9jY19jaGVjaywgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiAgICAjIyAuIFdlIG1heSBjaGVjayBpZiB0aGUgZ2VuZXMgaW4gb3VyIGdlbmUgbGlzdHMgYXJlIGVmZmVjdGl2ZWx5IAogICAgIyMgICBwcmVzZW50IGluIG91ciBTZXVyYXQgb2JqZWN0ICEKICAgICMjCiAgICAjIyAuIFRoaXMgaXMgZXhwZWN0ZWQsIGFzIHdlIHJlbW92ZWQgZmVhdHVyZXMgd2l0aCBhbG1vc3Qgbm8gZXhwcmVzc2lvbgogICAgYGBgCiAgICAKICAgIDxicj4KCioqQmV5b25kKiogOgoKMS4gIFdyaXRlIGEgY29kZSB0aGF0IHBlcmZvcm1zIHRoaXMgY2hlY2sKCjIuICBBZGQgYSBjb2RlIHRvIGFkanVzdCB0aGUgY29udGVudCBvZiB0aGUgZ2VuZSBsaXN0cyBhY2NvcmRpbmdseSAocmVtb3ZlIGdlbmVzIGZyb20gdGhlIGdlbmUgbGlzdHMgdGhhdCBhcmUgbm90IHByZXNlbnQgaW4gb3VyIGRhdGFzZXQpCgozLiAgKihOT1RFIDogdGhpcyBpcyBhY3R1YWxseSBub3QgbmVlZGVkIGFzIGFscmVhZHkgY2hlY2tlZCBhbmQKICAgIGNvcnJlY3RlZCBieSB0aGUgY2VsbCBjeWxlIGVzdGltYXRpb24gbWV0aG9kIHdlIHdpbGwgdXNlKSoKCjxicj4KCmBgYHtyIGJfY2NfYW5zd2VyLCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYmV5b25kIiksIGNsYXNzLm91dHB1dD0iYmV5b25kbyIsIGV2YWwgPSBGQUxTRX0KIyMgQ2hlY2sgaWYgb3VyIGRhdGEgZ2VuZXMgY292ZXIgdGhlIGdlbmUgbGlzdHMKbGFwcGx5KGNjX2dlbmVzLCBmdW5jdGlvbih4KSB4ICVpbiUgcm93bmFtZXMoc29iaikpCgojIyBSZW1vdmUgZ2VuZXMgbm90IGluIHNvYmoKY2NfZ2VuZXMgPC0gbGFwcGx5KGNjX2dlbmVzLCBmdW5jdGlvbihnbCkgeyBnbFtnbCAlaW4lIHJvd25hbWVzKHNvYmopXSB9KQoKIyMgQ2hlY2sgdGhlIG1vZGlmaWNhdGlvbgpzdHIoY2NfZ2VuZXMpCgojIyBDaGVjayB0aGF0IGFsbCBnZW5lcyBhcmUgYXZhaWxhYmxlIGluIHNvYmoKYWxsKHVuaXF1ZSh1bmxpc3QoY2NfZ2VuZXMpKSAlaW4lIHJvd25hbWVzKHNvYmopKQpgYGAKCjxicj48YnI+CgojIyBFc3RpbWF0aW9uCgotICAgTGV0J3MgcGVyZm9ybSB0aGlzIGVzdGltYXRpb24uIEJ1dCBob3cgPwoKICAgIGBgYHtyIGhfQ2VsbEN5Y2xlU2NvcmluZywgY2xhc3Muc291cmNlID0gIm5vdHJ1biIsIGV2YWwgPSBGQUxTRX0KICAgICMjIFJlYWRpbmcgdGhlIGZ1bmN0aW9uIGhlbHAgcGFnZQogICAgP1NldXJhdDo6Q2VsbEN5Y2xlU2NvcmluZwogICAgYGBgCiAgICAKICAgIDxicj4KCi0gICBXZSB3aWxsIGFjdHVhbGx5IHVzZSBhIGhlbHBlciBmdW5jdGlvbiB0byBlYXNlIHVwIHRoZSBwcm9jZXNzIDoKCiAgICBgYGB7ciBDQ19TZXVyYXQsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRkFMU0V9CiAgICA/U0MuaGVscGVyOjpDQ19TZXVyYXQKICAgIGBgYAoKPGJyPjxicj48YnI+CgotICAgUnVuIHRoZSBjZWxsLWN5Y2xlIGVzdGltYXRpb24KCiAgICBgYGB7ciBjY19ydW59CiAgICAjIyBQZXJmb3JtIHRoZSBlc3RpbWF0aW9uCiAgICAjIyBUaGUgUk5HIHNlZWQgaXMgbmVlZGVkIGhlcmUgIQogICAgc29iaiA8LSBTQy5oZWxwZXI6OkNDX1NldXJhdCgKICAgICAgc29iaiA9IHNvYmosIAogICAgICBhc3NheSA9ICJSTkEiLAogICAgICBzZXVyYXRfY2NfZ2VuZXMgPSBjY19nZW5lcywgCiAgICAgIFNtRzJNID0gVFJVRSwgCiAgICAgIG5iaW4gPSAyMCwgCiAgICAgIG15X3NlZWQgPSBteV9zZWVkKQogICAgYGBgCgo8YnI+CgotICAgRGVzY3JpcHRpb24gb2YgdGhlIG9iamVjdCB0byBzZWUgdGhlIGRhdGEgYWRkZWQKCiAgICBgYGB7ciBkZXNjX2NjLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CiAgICBTQy5oZWxwZXI6OlNldXJhdE9iamVjdF9kZXNjcmlwdG9yKAogICAgICBzb2JqID0gc29iaiwKICAgICAgZGVzY3JpYmUgPSAiY29sZGF0YSIpCiAgICBgYGAKCjxicj4KCiMjIFZpc3VhbGl6YXRpb24gCgpBcyB1c3VhbCwgd2UgY2FuIHZpc3VhbGl6ZSB0aGUgcmVzdWx0cyBhcyB2aW9saW5zIDoKCmBgYHtyIHZsbmNjLCBmaWcud2lkdGggPSAxMiwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQpTZXVyYXQ6OlZsblBsb3Qob2JqZWN0ID0gc29iaiwKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gYygiQ0NfU2V1cmF0X1MuU2NvcmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDQ19TZXVyYXRfRzJNLlNjb3JlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0NfU2V1cmF0X1NtRzJNLlNjb3JlIikpCmBgYAoKPGJyPgoKQnV0IGl0J3Mgbm90IHRoYXQgZWFzeSB0byBpbnRlcnByZXQuLi4gCgpMZXQncyBwbG90IGl0IGluIHRoZSBjZWxsIHNwYWNlCgojIyB7LnVubnVtYmVyZWQgLnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCiMjIyAiUyBtaW51cyBHMk0iIChgU21HMk1gKSBzY29yZSA6CgpgYGB7ciB2aXpjY3N9CiMjIFVzaW5nIHRoZSAnZmVhdHVyZXMnIHBhcmFtZXRlciB0byBwbG90IHRoZSBTbUcyTSBzY29yZSAoY29udGludW91cyBkYXRhKQojIyBIZXJlLCB3ZSB3aWxsIGtlZXAgdGhlIG1vZGlmaWVkIFNldXJhdCBvYmplY3QgdG8gc3BlZWQgdXAgZnVydGhlciBwbG90cwpWSVogPC0gU0MuaGVscGVyOjpRbkRfdml6KHNvYmogPSBzb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJDQ19TZXVyYXRfU21HMk0uU2NvcmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm5fb2JqZWN0ID0gVFJVRSkKYGBgCgo8YnI+CgojIyMgRXN0aW1hdGVkIGNlbGwgcGhhc2UgOgoKYGBge3Igdml6Y2NwfQojIyBVc2luZyB0aGUgJ2dyb3VwX2J5JyBwYXJhbWV0ZXIgdG8gcGxvdCB0aGUgZXN0aW1hdGVkIHBoYXNlcyAoY2F0ZWdvcmljYWwgZGF0YSkKIyMgSGVyZSwgd2UgcmVjeWNsZSBrZWVwIHRoZSBWSVogU2V1cmF0IG9iamVjdCB0aGF0IGNvbnRhaW5zIGV2ZXJ5dGhpbmcgdG8gcGVyZm9ybSB0aGUgcGxvdCB3aXRob3V0IGNvbXB1dGluZyBpdCBhZ2FpbgpTQy5oZWxwZXI6OlFuRF92aXooc29iaiA9IFZJWiwgCiAgICAgICAgICAgICAgICAgICBzbG90ID0gTlVMTCwgCiAgICAgICAgICAgICAgICAgICBkaW1yZWQgPSAidW1hcCIsIAogICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkgPSAiQ0NfU2V1cmF0X1BoYXNlIikKCiMjIFZJWiBpcyBub3QgbmVlZGVkIGFueW1vcmUKcm0oVklaKQpgYGAKCjxicj4KCiMjIHsudW5udW1iZXJlZH0KCioqUXVlc3Rpb25zKioKCmBgYHtyIHFfY2VsbDEsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CkRvZXMgdGhlIHN0cnVjdHVyZSBvZiB0aGUgY2VsbHMgaW4gdGhpcyByZXByZXNlbnRhdGlvbiBzZWVtIHRvIGhhdmUgYSBsaW5rIHdpdGggdGhlc2UgY2VsbCBjeWNsZSBwaGFzZXMvc2NvcmVzID8KYGBgCgo8YnI+CgpgYGB7ciBhX2NlbGwxLCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGV2YWwgPSBGQUxTRX0KIyMgSXQncyBhbiBhYnNvbHV0ZSB5ZXMgIQpgYGAKCjxicj4KCmBgYHtyIHFfY2VsbDIsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CkRvIHlvdSB0aGluayB0aGlzIGlzIHRoZSByZXN1bHQgb2YgYW4gYXJ0aWZhY3QsIG9yCmJpb2xvZ3ktcmVsYXRlZCA/CmBgYAoKPGJyPgoKYGBge3IgYV9jZWxsMiwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiMjCiMjICAgIMKvXF8o44OEKV8vwq8KIyMKYGBgCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTYXZlIHRoZSBTZXVyYXQgb2JqZWN0CgpXZSB3aWxsIHNhdmUgb3VyIFNldXJhdCBvYmplY3QgdGhhdCBub3cgY29udGFpbnMgdGhlIGNlbGwgY3ljbGUKc3RhdGVzL3Njb3JlcyA6CgpgYGB7ciBzYXZlcmRzMSwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyMgU2F2ZSBvdXIgU2V1cmF0IG9iamVjdCAocmljaCBuYW1pbmcpCm91dF9uYW1lIDwtIHBhc3RlMCgKICAgICAgICAgIG91dHB1dF9kaXIsICIvIiwgcGFzdGUoCiAgICAgICAgICAgIGMoIjA0IiwgU2V1cmF0OjpQcm9qZWN0KHNvYmopLCAiUzUiLCAKICAgICAgICAgICAgICAiQ0MiLCBwYXN0ZSgKICAgICAgICAgICAgICAgIGRpbShzb2JqKSwgCiAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICcuJwogICAgICAgICAgICAgICkKICAgICAgICAgICAgKSwgY29sbGFwc2UgPSAiXyIpLAogICAgICAgICAgICAiLlJEUyIpCgojIyBDaGVjawpwcmludChvdXRfbmFtZSkKCiMjIFdyaXRlIG9uIGRpc2sKc2F2ZVJEUyhvYmplY3QgPSBzb2JqLCAKICAgICAgICBmaWxlID0gb3V0X25hbWUpCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMgQ2VsbCBkb3VibGV0cwoKIyMgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgpXZSB3aWxsIHVzZSAqKnR3byBkaWZmZXJlbnQgbWV0aG9kcyoqIHRvIGRldGVjdCBhbmQgcmVtb3ZlIGNlbGwgZG91YmxldHMgOgoKLSAgIFtgc2Nkc2BdKGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvc2Nkcy5odG1sKSAoaW4gaXRzICJoeWJyaWQiIG1vZGUpe3RhcmdldD0iX2JsYW5rIn0gOiBtb3JlIGVmZmljaWVudCBhdCBkZXRlY3RpbmcgX2hvbW90eXBpY18gZG91YmxldHMKCi0gICBbc2NEYmxGaW5kZXJdKGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvc2NEYmxGaW5kZXIuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSA6IG1vcmUgZWZmaWNpZW50IGF0IGRldGVjdGluZyBfaGV0ZXJvdHlwaWNfIGRvdWJsZXRzCgpOb25lIG9mIHRoZSBtZXRob2RzIGFjY2VwdHMgYSBgU2V1cmF0T2JqZWN0YCBhcyBpbnB1dCwgYnV0IGEgW2BTaW5nbGVDZWxsRXhwZXJpbWVudGBdKGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvU2luZ2xlQ2VsbEV4cGVyaW1lbnQuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSBvYmplY3QuCgpIb3BlZnVsbHkgOgoKLSAgIFNldXJhdCBoYXMgYSBmdW5jdGlvbiB0byBwZXJmb3JtIHRoZSBjb252ZXJzaW9uCi0gICBUaGUgb3V0cHV0IHJlc3VsdHMgY2FuIGJlIGludGVncmF0ZWQgaW50byBvdXIgU2V1cmF0IG9iamVjdCB3aXRoIGVhc2UKCiMjIGBzY2RzYAoKYGBge3Igc2Nkc30KIyMgRml4IHNlZWQKc2V0LnNlZWQobXlfc2VlZCkKCiMjIFJ1biBzY2RzCnNvYmokZG91YmxldF9zY2RzLmh5YnJpZCA8LSB1bm5hbWUoCiAgc2Nkczo6Y3hkc19iY2RzX2h5YnJpZCgKICAgIFNldXJhdDo6YXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQoCiAgICAgIHNvYmosIGFzc2F5ID0gIlJOQSIpKSRoeWJyaWRfc2NvcmUgPiAxKQoKIyMgQ29udGluZ2VuY2llcwp0YWJsZShzb2JqJGRvdWJsZXRfc2Nkcy5oeWJyaWQpCmBgYAoKCiMjIHNjRGJsRmluZGVyCgpgYGB7ciBzY2RibH0KIyMgRml4IHNlZWQKc2V0LnNlZWQobXlfc2VlZCkKCiMjIFJ1biBzY0RibEZpbmRlciAod2hpY2ggbmVlZHMgYW5vdGhlciBvYmplY3QgdHlwZSkKc29iaiRkb3VibGV0X3NjRGJsRmluZGVyIDwtIHNjRGJsRmluZGVyOjpzY0RibEZpbmRlcigKICBzY2UgPSBTZXVyYXQ6OmFzLlNpbmdsZUNlbGxFeHBlcmltZW50KAogICAgeCA9IHNvYmosIAogICAgYXNzYXkgPSAiUk5BIiksCiAgcmV0dXJuVHlwZSA9ICJ0YWJsZSIpJGNsYXNzID09ICJkb3VibGV0IgoKIyMgQ29udGluZ2VuY2llcwp0YWJsZShzb2JqJGRvdWJsZXRfc2NEYmxGaW5kZXIpCmBgYAoKIyAgey51bm51bWJlcmVkfQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMjIE1lcmdlIHJlc3VsdHMKCldlIG1lcmdlIHJlc3VsdHMgb2YgdGhlIHR3byBtZXRob2RzCgpgYGB7ciBkYmxtZXJnZX0KIyMgTG9naWNhbCB1bmlvbiBvZiBib3RoIG1ldGhvZHMKc29iaiRkb3VibGV0X3VuaW9uIDwtIHNvYmokZG91YmxldF9zY2RzLmh5YnJpZCB8IHNvYmokZG91YmxldF9zY0RibEZpbmRlcgoKIyMgUXVhbnRpZnkgZG91YmxldHMKdGFibGUoc29iaiRkb3VibGV0X3VuaW9uKQpgYGAKCjxicj4KCldlIGNhbiBhc3Nlc3MgKip0b29sLXNwZWNpZmljKiogYW5kICoqY29tbW9uKiogZG91YmxldHMKCmBgYHtyIGRibF90eXBlcywgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQojIyMgU2luZ2xldHMgYnkgZGVmYXVsdApzb2JqJGRvdWJsZXRfdml6IDwtICJzaW5nbGV0IgoKIyMjIFVuaW9uCnNvYmokZG91YmxldF92aXpbc29iaiRkb3VibGV0X3VuaW9uXSA8LSAiYm90aCIKCiMjIyBzY2RzLXNwZWNpZmljCnNvYmokZG91YmxldF92aXpbc29iaiRkb3VibGV0X3NjZHMuaHlicmlkICYgIXNvYmokZG91YmxldF9zY0RibEZpbmRlcl0gPC0gInNjZHMiCgojIyMgc2NEYmxGaW5kZXItc3BlY2lmaWMKc29iaiRkb3VibGV0X3Zpeltzb2JqJGRvdWJsZXRfc2NEYmxGaW5kZXIgJiAhc29iaiRkb3VibGV0X3NjZHMuaHlicmlkXSA8LSAic2NEYmxGaW5kZXIiCgojIyBDb252ZXJ0IHRvIGZhY3Rvcgpzb2JqJGRvdWJsZXRfdml6IDwtIGFzLmZhY3Rvcihzb2JqJGRvdWJsZXRfdml6KQoKIyMgQ29udGluZ2VuY2llcwp0YWJsZShzb2JqJGRvdWJsZXRfdml6KQpgYGAKCjxicj4KCioqQmV5b25kKiogOiBDcmVhdGUgYW4gdXBzZXQtcGxvdCBmb3IgdGhlIGRvdWJsZXQgc3RhdHVzIGFjY29yZGluZyB0byB0aGUgdHdvIG1ldGhvZHMgdXNlZAoKYGBge3IgYl91cHNldCwgY2xhc3Muc291cmNlPWMoImZvbGQtaGlkZSIsICJiZXlvbmQiKSwgY2xhc3Mub3V0cHV0PSJiZXlvbmRvIn0KIyMgQnVpbGQgYSBsaXN0IG9mIGNlbGxzIHRhZ2dlZCBieSBvbmUgdG9vbCwgdGhlIG90aGVyLCBvciBib3RoCmRibF9saXN0IDwtbGlzdCgKICAic2NkcyIgPSBjb2xuYW1lcyhzb2JqKVtzb2JqJGRvdWJsZXRfc2Nkcy5oeWJyaWQgJiAhc29iaiRkb3VibGV0X3NjRGJsRmluZGVyXSwKICAic2NEYmxGaW5kZXIiID0gY29sbmFtZXMoc29iailbIXNvYmokZG91YmxldF9zY2RzLmh5YnJpZCAmIHNvYmokZG91YmxldF9zY0RibEZpbmRlcl0sCiAgImJvdGgiID0gY29sbmFtZXMoc29iailbc29iaiRkb3VibGV0X3NjZHMuaHlicmlkICYgc29iaiRkb3VibGV0X3NjRGJsRmluZGVyXSkKCiMjIERyYXcgdGhlIHVwc2V0IHBsb3QKVXBTZXRSOjp1cHNldChkYXRhID0gVXBTZXRSOjpmcm9tTGlzdChkYmxfbGlzdCksIAogICAgICAgICAgICAgIG5pbnRlcnNlY3RzID0gTkEsIAogICAgICAgICAgICAgIHNldHMgPSByZXYobmFtZXMoZGJsX2xpc3QpKSwKICAgICAgICAgICAgICBrZWVwLm9yZGVyID0gVFJVRSwKICAgICAgICAgICAgICBvcmRlci5ieSA9ICJmcmVxIikKYGBgCgo8YnI+CgpOb3cgd2UgY2FuIHJlbW92ZSBiYXJjb2RlcyBpZGVudGlmaWVkIGFzIGNlbGwgZG91YmxldHMsIGFuZCB2aXN1YWxpemUgdGhlIGNlbGwgc3BhY2UgYmVmb3JlIGFuZCBhZnRlci4KCiMjIERvdWJsZXRzIGZpbHRlcmluZyB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCiMjIyBCRUZPUkUKCmBgYHtyIGRibHZpejEsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KIyMjIERvdWJsZXRzIHZpeiAoYmVmb3JlIHJlbW92YWwpCnVtYXBfZGJsX3VuZmlsdCA8LSBTQy5oZWxwZXI6OlFuRF92aXooCiAgc29iaiA9IHNvYmosIAogIGdyb3VwX2J5ID0gImRvdWJsZXRfdml6IiwKICByZXR1cm5fcGxvdCA9IFRSVUUpCmBgYAoKIyMjIFJlbW92ZSBkb3VibGV0cwoKYGBge3IgZGJscm0sIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIERpbWVuc2lvbnMgYmVmb3JlIHJlbW92YWwKZGltKHNvYmopCgojIyBQZXJmb3JtIHRoZSByZW1vdmFsCnNvYmogPC0gc29ialssIXNvYmokZG91YmxldF91bmlvbl0KCiMjIERpbWVuc2lvbnMgYWZ0ZXIKZGltKHNvYmopCmBgYAoKIyMjIEFGVEVSCgpgYGB7ciBkYmx2aXoyfQojIyMgRG91YmxldHMgdml6IChhZnRlciByZW1vdmFsKQp1bWFwX2RibF9maWx0IDwtIFNDLmhlbHBlcjo6UW5EX3ZpeigKICBzb2JqID0gc29iaiwgCiAgZ3JvdXBfYnkgPSAiZG91YmxldF92aXoiLAogIHJldHVybl9wbG90ID0gVFJVRSkKYGBgCgojIyAgey51bm51bWJlcmVkfQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCk1lcmdlIHBsb3RzIGZvciBlYXNlIG9mIHVzZSA6CgpgYGB7ciBkYmxfdW1hcHMsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNiwgY2xhc3Muc291cmNlPWMoImZvbGQtaGlkZSIsICJub3RydW4iKSwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KIyMgVXNpbmcgdGhlIHBhdGNod29yayBwYWNrYWdlIHRvIG1lcmdlIHBsb3RzIChhbmQgZ2dwbG90MiB0byBhZGQgdGl0bGVzKQpwYXRjaHdvcms6OndyYXBfcGxvdHMoCiAgbGlzdCgKICAgIHVtYXBfZGJsX3VuZmlsdCAmIGdncGxvdDI6OmdndGl0bGUobGFiZWwgPSAiQ2VsbCBkb3VibGV0cyAodW5maWx0ZXJlZCkiKSwKICAgIHVtYXBfZGJsX2ZpbHQgJiBnZ3Bsb3QyOjpnZ3RpdGxlKGxhYmVsID0gIkNlbGwgZG91YmxldHMgKGZpbHRlcmVkKSIpKSwgCiAgbnJvdyA9IDEpCmBgYAoKPGJyPgoKKipRdWVzdGlvbioqCgpgYGB7ciBxX2RibGNvbXAsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsPUZBTFNFfQpXaGF0IGRvIHlvdSBvYnNlcnZlIHdoZW4gY29tcGFyaW5nIGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIGRvdWJsZXRzIGZpbHRlcmluZyA/CmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIFNhdmUgdGhlIFNldXJhdCBvYmplY3QKCldlIHdpbGwgc2F2ZSBvdXIgU2V1cmF0IG9iamVjdCB0aGF0IGlzIG5vdyBmaWx0ZXJlZCBmb3IgZG91YmxldHMgOgoKYGBge3Igc2F2ZXJkczN9CiMjIFNhdmUgb3VyIFNldXJhdCBvYmplY3QgKHJpY2ggbmFtaW5nKQpvdXRfbmFtZSA8LSBwYXN0ZTAoCiAgICAgICAgICBvdXRwdXRfZGlyLCAiLyIsIHBhc3RlKAogICAgICAgICAgICBjKCIwNSIsIFNldXJhdDo6UHJvamVjdChzb2JqKSwgIlM1IiwgCiAgICAgICAgICAgICAgIkRvdWJsZXRzLkZpbHRlcmVkIiwgcGFzdGUoCiAgICAgICAgICAgICAgICBkaW0oc29iaiksIAogICAgICAgICAgICAgICAgY29sbGFwc2UgPSAnLicKICAgICAgICAgICAgICApCiAgICAgICAgICAgICksIGNvbGxhcHNlID0gIl8iKSwKICAgICAgICAgICAgIi5SRFMiKQoKIyMgQ2hlY2sKcHJpbnQob3V0X25hbWUpCgojIyBXcml0ZSBvbiBkaXNrCnNhdmVSRFMob2JqZWN0ID0gc29iaiwgCiAgICAgICAgZmlsZSA9IG91dF9uYW1lKQpgYGAKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+PGJyPjxicj4KCiMgUnNlc3Npb24KCmBgYHtyIHJzZXNzaW9uLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CnV0aWxzOjpzZXNzaW9uSW5mbygpCmBgYAo=