Prepare analysis workflow

Load Libraries

library(tidyverse)
library(furrr)
plan(multiprocess)
options(future.globals.maxSize= +Inf, future.fork.enable=TRUE)

Define Functions

call_emptydrops <-
  function(sample_umi_count,
           lower = 200,
           fdr_thresh = 0.005) {
    sample_emptydrops <-
      DropletUtils::emptyDrops(sample_umi_count,
                               lower = lower) %>%
      as.data.frame() %>%
      rownames_to_column(var = "cell") %>%
      select(cell, FDR)
    
    sample_emptydrops[is.na(sample_emptydrops)] <- 1
    
    sample_emptydrops <-
      sample_emptydrops %>%
      mutate(is_cell = FDR <= fdr_thresh,
             FDR = NULL)
    
    return(sample_emptydrops)
  }

call_defaultdrops <- function(sample_umi_count) {
  sample_defaultdrops <- DropletUtils::defaultDrops(sample_umi_count)
  sample_defaultdrops <- enframe(sample_defaultdrops,
                                 name = "cell",
                                 value = "is_cell")
  
  return(sample_defaultdrops)
}

filter_cells <- function(mat, cell) {
  mat[, cell$is_cell]
}

Call cells

Read data

output_dir <- "./data/novaseq_l1/output"
out <- readRDS( file.path(output_dir, "out_novaseq_l1.rds"))

Combine retained and discarded counts

umi_counts <- map2(out$umi_counts$retained, 
                   out$umi_counts$discarded, `+`)
out$umi_counts$discarded <- NULL

Call cells

called_cells <- 
  future_map(
    umi_counts,
    plyr::failwith(
    NULL,
    call_emptydrops)
    )

Discard barcodes

filtered_umi_counts <- 
  future_map2(umi_counts, 
              called_cells,
              filter_cells)

Call cells of purged data

called_cells_retained <- 
  future_map(
    out$umi_counts$retained,
    plyr::failwith(
    NULL,
    call_emptydrops
  )
)

Discard barcodes

filtered_umi_counts_retained <- 
  future_map2(out$umi_counts$retained,
              called_cells_retained,
              filter_cells)

Save data

saveRDS(filtered_umi_counts, 
        "./data/filtered_umi_counts_novaseq_l1.rds")
saveRDS(filtered_umi_counts_retained,
        "./data/filtered_umi_counts_retained_novaseq_l1.rds")

Run analysis

Restart first!

library(tidyverse)
library(AnnotationHub)
library(scran)
library(scater)
library(uwot) # For umap
library(zeallot)
set.seed(42)
get_transcriptome <- function(ah_id) {
  ah <- AnnotationHub()
  gene_metadata <- ah[[ah_id]]
  gene_metadata <-
    gene_metadata[mcols(gene_metadata)$type == "gene",
                  c("gene_id", "gene_name", "gene_biotype")]
  seqlevelsStyle(gene_metadata) <- "UCSC"
  #gene_metadata <- keepStandardChromosomes(gene_metadata, 
   #                                        pruning.mode="coarse")
  gene_metadata  <-
    gene_metadata %>%
    as.data.frame() %>%
    dplyr::select(gene_id,
                  seqnames,
                  gene_name,
                  gene_biotype,
                  gene_id)
  
  return(gene_metadata)
  
}

add_metadata <- function(sce10x, gene_metadata) {
  rowData(sce10x) <-
    gene_metadata %>%
    left_join(as_tibble(rowData(sce10x)),
              .,
              by = "gene_id") %>%
    dplyr::rename(chr = seqnames)
  
  rownames(sce10x) <- uniquifyFeatureNames(rowData(sce10x)$gene_id,
                                           rowData(sce10x)$gene_name)
  sce10x <- addPerCellQC(sce10x,
                         subsets = list(mito = which(rowData(sce10x)$chr ==
                                                       "chrM")),
                         flatten = TRUE)
  
  sce10x <- addPerFeatureQC(sce10x)
  
  
  return(sce10x)
}
to_sce <- function(data, suff) {
  col_names <- paste(colnames(data), suff, sep = "_")
  row_names <- rownames(data)
  
  data <-
    data %>%
    as.matrix()
  
  colnames(data) <- NULL
  rownames(data) <- NULL
  
  
  data <- SingleCellExperiment(
    assays = list(counts = data),
    rowData = DataFrame(gene_id = row_names),
    colData = DataFrame(barcode = col_names)
  )
  
  return(data)
}

create_sce <- function(data_list, gene_metadata) {
  suffixes <- str_split_fixed(names(data_list), "_", 2)[, 2]
  data_list <-
    map2(data_list,
         suffixes,
         to_sce)
  
  data_list <- map(data_list, add_metadata, gene_metadata)
  
  return(data_list)
}

process_sample <-
  function(sample,
           umi_counts_purged,
           sce10x_unpurged) {
    sce10x_purged <- umi_counts_purged[[sample]]
    sce10x_unpurged <- umi_counts_unpurged[[sample]]
    
    n_exprs_genes_unpurged <-
      nexprs(sce10x_unpurged,
             detection_limit = 0,
             byrow = TRUE)
    sce10x_unpurged <- sce10x_unpurged[n_exprs_genes_unpurged > 0, ]
    
    n_exprs_genes_purged <-
      nexprs(sce10x_purged,
             detection_limit = 0,
             byrow = TRUE)
    sce10x_purged <- sce10x_purged[n_exprs_genes_purged > 0, ]
    
    
    dt_summary <-
      left_join(
        colData(sce10x_unpurged)[, c("barcode", "sum", "detected")] %>% as_tibble(),
        colData(sce10x_purged)[, c("barcode", "sum", "detected")] %>% as_tibble(),
        by = "barcode"
      ) %>%
      mutate(
        phantom_sum = sum.x - sum.y ,
        phantom_detected = detected.x - detected.y ,
        uncalled = !(
          colData(sce10x_unpurged)$barcode %in% colData(sce10x_purged)$barcode
        )
      ) %>%
      dplyr::select(barcode, phantom_sum, phantom_detected, uncalled)
    
    colData(sce10x_unpurged) <-
      left_join(colData(sce10x_unpurged) %>% as_tibble(), dt_summary ,
                by = "barcode") %>%
      as(., "DataFrame")
    
    gene_summary_dt <-
      full_join(
        rowData(sce10x_unpurged)[, c("gene_id", "gene_name", "mean")] %>%
          as_tibble() %>%
          mutate(sum = mean * dim(sce10x_unpurged)[2]) %>%
          dplyr::select(-mean),
        rowData(sce10x_purged)[, c("gene_id", "gene_name", "mean")] %>%
          as_tibble() %>%
          mutate(sum = mean * dim(sce10x_purged)[2]) %>%
          dplyr::select(-mean),
        by = c("gene_id", "gene_name")
      ) %>%
      mutate(phantoms_sum = sum.x - sum.y) %>%
      arrange(-phantoms_sum)
    
    clusters <- quickCluster(sce10x_unpurged,
                             min.mean=0.12)
    sce10x_unpurged <- computeSumFactors(sce10x_unpurged,
                                 clusters=clusters,
                                 min.mean=0.12)
    
    clusters <- quickCluster(sce10x_purged,
                             min.mean=0.12)
    sce10x_purged <- computeSumFactors(sce10x_purged,
                                 clusters=clusters,
                                 min.mean=0.12)
    
    sce10x_unpurged <- scater::logNormCounts(sce10x_unpurged)
    sce10x_purged <- scater::logNormCounts(sce10x_purged)
    
    return(
      list(
        sce10x_unpurged = sce10x_unpurged,
        sce10x_purged = sce10x_purged,
        gene_summary_dt = gene_summary_dt
      )
    )
    
  }

run_umap <- function(sce10x,
                     assay = "counts",
                     prefix = "umap") {
  sce10x_mtx <-
    t(assay(sce10x, assay) %>%
        as.matrix())
  
  
  sce_cells_umap <- umap(
    sce10x_mtx,
    #  n_neighbors=100L,
    n_components = 2,
   #  min_dist=0.005,
    #  metric="manhattan",
    verbose = TRUE,
    n_threads = 7
  )
  
  colnames(sce_cells_umap) <- paste0(prefix, seq(1, 2))
  reducedDim(sce10x, prefix) <- sce_cells_umap
  
  return(sce10x)
}
input_dir <- "~/Documents/index_hopping/data"
target_samples <- c("P7_1", "P7_8" )

Load Transcriptome

gene_metadata <- get_transcriptome("AH50870")  #Mus_musculus.GRCm38.84
snapshotDate(): 2019-10-29
loading from cache
Importing File into R ..
require(“rtracklayer”)

Load UMI Counts before and after purging

umi_counts_unpurged <- readRDS( file.path(input_dir,
                                          "filtered_umi_counts_novaseq_l1.rds"))
umi_counts_unpurged <- umi_counts_unpurged[target_samples]
umi_counts_purged <- readRDS( file.path(input_dir,
                                        "filtered_umi_counts_retained_novaseq_l1.rds"))
umi_counts_purged <- umi_counts_purged[target_samples]

Create SCE objects with metadata

umi_counts_unpurged <- create_sce(umi_counts_unpurged,  gene_metadata)
umi_counts_purged <- create_sce(umi_counts_purged, gene_metadata)

Process sample P7_1

c(sce10x_unpurged, sce10x_purged, gene_summary_dt_1) %<-% 
  process_sample("P7_1", umi_counts_purged, sce10x_unpurged )
encountered negative size factor estimates
gene_summary_dt_1

Run UMAP

sce10x_unpurged <- run_umap(sce10x_unpurged, assay="logcounts", prefix="umap")
16:57:51 UMAP embedding parameters a = 1.896 b = 0.8006
16:57:51 Read 772 rows and found 13316 numeric columns
16:57:51 Using FNN for neighbor search, n_neighbors = 15
16:57:58 Commencing smooth kNN distance calibration using 7 threads
16:57:59 Initializing from normalized Laplacian + noise
16:57:59 Commencing optimization for 500 epochs, with 20730 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:58:01 Optimization finished

PLot imbedding

df_to_plot_1 <- 
  bind_cols(reducedDim(sce10x_unpurged, "umap") %>% 
              as_tibble(), 
            t(assay(sce10x_unpurged, "logcounts")[c("Sftpc", "Lyz2"),]) %>% 
              as_tibble(), 
            colData(sce10x_unpurged)[, c("sum","uncalled",
                                         "phantom_sum",
                                         "phantom_detected")] %>% 
              as_tibble())
ggplot(df_to_plot_1 ) +
  geom_point(aes( umap1,
                  umap2,
                  colour= phantom_sum==0),
             size=0.9,
             alpha=1) +
  theme_classic() 


ggsave("P7_1_umap.pdf")
Saving 10 x 6.18 in image
ggplot(df_to_plot_1 ) +
  geom_point(aes( umap1,
                  umap2,
                  colour= Lyz2!=0),
             size=0.9,
             alpha=1) +
  theme_classic() 


ggsave("P7_1_Lyz2_umap.pdf")
Saving 10 x 6.18 in image

Process sample P7_8

c(sce10x_unpurged, sce10x_purged, gene_summary_dt_2) %<-% 
  process_sample("P7_8", umi_counts_purged, sce10x_unpurged )
gene_summary_dt_2

Run UMAP

sce10x_unpurged <- run_umap(sce10x_unpurged, assay="logcounts", prefix="umap")
16:58:13 UMAP embedding parameters a = 1.896 b = 0.8006
16:58:13 Read 1194 rows and found 17018 numeric columns
16:58:13 Using FNN for neighbor search, n_neighbors = 15
16:58:33 Commencing smooth kNN distance calibration using 7 threads
16:58:34 Initializing from normalized Laplacian + noise
16:58:34 Commencing optimization for 500 epochs, with 31250 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:58:36 Optimization finished

PLot imbedding

df_to_plot_2 <- 
  bind_cols(reducedDim(sce10x_unpurged, "umap") %>% 
              as_tibble(), 
            t(assay(sce10x_unpurged, "logcounts")[c("Fabp1", "Scgb1a1"),]) %>% 
              as_tibble(),
            colData(sce10x_unpurged)[, c("sum",
                                         "uncalled",
                                         "phantom_sum",
                                         "phantom_detected")] %>% 
              as_tibble())
ggplot(df_to_plot_2) +
  geom_point(aes( umap1,
                  umap2,
                  colour= phantom_sum==0),
             size=0.9,
             alpha=1) +
  theme_classic() 

ggsave("P7_8_umap.pdf")
Saving 10 x 6.18 in image
ggplot(df_to_plot_2 ) +
  geom_point(aes( umap1,
                  umap2,
                  colour= Fabp1 != 0),
             size=0.9,
             alpha=1) +
  theme_classic() 

ggsave("P7_8_Fabp1_umap.pdf")
Saving 10 x 6.18 in image
sessionInfo()
R version 3.6.2 (2019-12-12)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.3 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8   
 [6] LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats4    parallel  stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] rtracklayer_1.46.0          zeallot_0.1.0               uwot_0.1.5                  Matrix_1.2-18              
 [5] scater_1.14.6               scran_1.14.5                SingleCellExperiment_1.8.0  SummarizedExperiment_1.16.1
 [9] DelayedArray_0.12.2         BiocParallel_1.20.1         matrixStats_0.55.0          Biobase_2.46.0             
[13] GenomicRanges_1.38.0        GenomeInfoDb_1.22.0         IRanges_2.20.1              S4Vectors_0.24.1           
[17] AnnotationHub_2.18.0        BiocFileCache_1.10.2        dbplyr_1.4.2                BiocGenerics_0.32.0        
[21] forcats_0.4.0               stringr_1.4.0               dplyr_0.8.3                 purrr_0.3.3                
[25] readr_1.3.1                 tidyr_1.0.0                 tibble_2.1.3                ggplot2_3.2.1              
[29] tidyverse_1.3.0            

loaded via a namespace (and not attached):
 [1] ggbeeswarm_0.6.0              colorspace_1.4-1              XVector_0.26.0                BiocNeighbors_1.4.1          
 [5] fs_1.3.1                      rstudioapi_0.10               farver_2.0.1                  bit64_0.9-7                  
 [9] RSpectra_0.16-0               interactiveDisplayBase_1.24.0 AnnotationDbi_1.48.0          fansi_0.4.1                  
[13] lubridate_1.7.4               xml2_1.2.2                    knitr_1.26                    jsonlite_1.6                 
[17] Rsamtools_2.2.1               broom_0.5.3                   shiny_1.4.0                   BiocManager_1.30.10          
[21] compiler_3.6.2                httr_1.4.1                    dqrng_0.2.1                   backports_1.1.5              
[25] assertthat_0.2.1              fastmap_1.0.1                 lazyeval_0.2.2                limma_3.42.0                 
[29] cli_2.0.1                     later_1.0.0                   BiocSingular_1.2.1            htmltools_0.4.0              
[33] tools_3.6.2                   rsvd_1.0.2                    igraph_1.2.4.2                gtable_0.3.0                 
[37] glue_1.3.1                    GenomeInfoDbData_1.2.2        rappdirs_0.3.1                Rcpp_1.0.3                   
[41] cellranger_1.1.0              Biostrings_2.54.0             vctrs_0.2.1                   nlme_3.1-143                 
[45] DelayedMatrixStats_1.8.0      xfun_0.12                     rvest_0.3.5                   mime_0.8                     
[49] lifecycle_0.1.0               irlba_2.3.3                   XML_3.98-1.20                 statmod_1.4.33               
[53] edgeR_3.28.0                  zlibbioc_1.32.0               scales_1.1.0                  BSgenome_1.54.0              
[57] hms_0.5.3                     promises_1.1.0                yaml_2.2.0                    curl_4.3                     
[61] memoise_1.1.0                 gridExtra_2.3                 stringi_1.4.5                 RSQLite_2.2.0                
[65] BiocVersion_3.10.1            rlang_0.4.2                   pkgconfig_2.0.3               bitops_1.0-6                 
[69] lattice_0.20-38               labeling_0.3                  GenomicAlignments_1.22.1      bit_1.1-15                   
[73] tidyselect_0.2.5              magrittr_1.5                  R6_2.4.1                      generics_0.0.2               
[77] DBI_1.1.0                     pillar_1.4.3                  haven_2.2.0                   withr_2.1.2                  
[81] RCurl_1.95-4.12               modelr_0.1.5                  crayon_1.3.4                  viridis_0.5.1                
[85] locfit_1.5-9.1                grid_3.6.2                    readxl_1.3.1                  FNN_1.1.3                    
[89] blob_1.2.0                    reprex_0.3.0                  digest_0.6.23                 xtable_1.8-4                 
[93] httpuv_1.5.2                  RcppParallel_4.4.4            munsell_0.5.0                 beeswarm_0.2.3               
[97] viridisLite_0.3.0             vipor_0.4.5                  
LS0tCnRpdGxlOiAiUGhhbnRvbVB1cmdlUjogRG93bnN0cmVhbSBBbmFseXNpcyBDb21wYXJpc29uIgphdXRob3I6ICJSaWNrIEZhcm91bmkiCnBhY2thZ2U6IFBoYW50b21QdXJnZVIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiAKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQotLS0KCiMgUHJlcGFyZSBhbmFseXNpcyB3b3JrZmxvdwoKIyMgTG9hZCBMaWJyYXJpZXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGZ1cnJyKQpwbGFuKG11bHRpcHJvY2VzcykKb3B0aW9ucyhmdXR1cmUuZ2xvYmFscy5tYXhTaXplPSArSW5mLCBmdXR1cmUuZm9yay5lbmFibGU9VFJVRSkKYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKIyMgRGVmaW5lIEZ1bmN0aW9ucwoKYGBge3J9CmNhbGxfZW1wdHlkcm9wcyA8LQogIGZ1bmN0aW9uKHNhbXBsZV91bWlfY291bnQsCiAgICAgICAgICAgbG93ZXIgPSAyMDAsCiAgICAgICAgICAgZmRyX3RocmVzaCA9IDAuMDA1KSB7CiAgICBzYW1wbGVfZW1wdHlkcm9wcyA8LQogICAgICBEcm9wbGV0VXRpbHM6OmVtcHR5RHJvcHMoc2FtcGxlX3VtaV9jb3VudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvd2VyID0gbG93ZXIpICU+JQogICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiY2VsbCIpICU+JQogICAgICBzZWxlY3QoY2VsbCwgRkRSKQogICAgCiAgICBzYW1wbGVfZW1wdHlkcm9wc1tpcy5uYShzYW1wbGVfZW1wdHlkcm9wcyldIDwtIDEKICAgIAogICAgc2FtcGxlX2VtcHR5ZHJvcHMgPC0KICAgICAgc2FtcGxlX2VtcHR5ZHJvcHMgJT4lCiAgICAgIG11dGF0ZShpc19jZWxsID0gRkRSIDw9IGZkcl90aHJlc2gsCiAgICAgICAgICAgICBGRFIgPSBOVUxMKQogICAgCiAgICByZXR1cm4oc2FtcGxlX2VtcHR5ZHJvcHMpCiAgfQoKY2FsbF9kZWZhdWx0ZHJvcHMgPC0gZnVuY3Rpb24oc2FtcGxlX3VtaV9jb3VudCkgewogIHNhbXBsZV9kZWZhdWx0ZHJvcHMgPC0gRHJvcGxldFV0aWxzOjpkZWZhdWx0RHJvcHMoc2FtcGxlX3VtaV9jb3VudCkKICBzYW1wbGVfZGVmYXVsdGRyb3BzIDwtIGVuZnJhbWUoc2FtcGxlX2RlZmF1bHRkcm9wcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJjZWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAiaXNfY2VsbCIpCiAgCiAgcmV0dXJuKHNhbXBsZV9kZWZhdWx0ZHJvcHMpCn0KCmZpbHRlcl9jZWxscyA8LSBmdW5jdGlvbihtYXQsIGNlbGwpIHsKICBtYXRbLCBjZWxsJGlzX2NlbGxdCn0KYGBgCgoKIyAgQ2FsbCBjZWxscyAKCiMjIFJlYWQgZGF0YQoKYGBge3J9Cm91dHB1dF9kaXIgPC0gIi4vZGF0YS9ub3Zhc2VxX2wxL291dHB1dCIKb3V0IDwtIHJlYWRSRFMoIGZpbGUucGF0aChvdXRwdXRfZGlyLCAib3V0X25vdmFzZXFfbDEucmRzIikpCmBgYAoKIyMgQ29tYmluZSByZXRhaW5lZCBhbmQgZGlzY2FyZGVkIGNvdW50cwoKYGBge3J9CnVtaV9jb3VudHMgPC0gbWFwMihvdXQkdW1pX2NvdW50cyRyZXRhaW5lZCwgCiAgICAgICAgICAgICAgICAgICBvdXQkdW1pX2NvdW50cyRkaXNjYXJkZWQsIGArYCkKb3V0JHVtaV9jb3VudHMkZGlzY2FyZGVkIDwtIE5VTEwKYGBgCgojIyBDYWxsIGNlbGxzCgpgYGB7cn0KY2FsbGVkX2NlbGxzIDwtIAogIGZ1dHVyZV9tYXAoCiAgICB1bWlfY291bnRzLAogICAgcGx5cjo6ZmFpbHdpdGgoCiAgICBOVUxMLAogICAgY2FsbF9lbXB0eWRyb3BzKQogICAgKQpgYGAKCiMjIERpc2NhcmQgYmFyY29kZXMKCmBgYHtyfQpmaWx0ZXJlZF91bWlfY291bnRzIDwtIAogIGZ1dHVyZV9tYXAyKHVtaV9jb3VudHMsIAogICAgICAgICAgICAgIGNhbGxlZF9jZWxscywKICAgICAgICAgICAgICBmaWx0ZXJfY2VsbHMpCmBgYAoKIyMgQ2FsbCBjZWxscyBvZiBwdXJnZWQgZGF0YQoKYGBge3J9CmNhbGxlZF9jZWxsc19yZXRhaW5lZCA8LSAKICBmdXR1cmVfbWFwKAogICAgb3V0JHVtaV9jb3VudHMkcmV0YWluZWQsCiAgICBwbHlyOjpmYWlsd2l0aCgKICAgIE5VTEwsCiAgICBjYWxsX2VtcHR5ZHJvcHMKICApCikKYGBgCgoKIyMgRGlzY2FyZCBiYXJjb2RlcwoKYGBge3J9CmZpbHRlcmVkX3VtaV9jb3VudHNfcmV0YWluZWQgPC0gCiAgZnV0dXJlX21hcDIob3V0JHVtaV9jb3VudHMkcmV0YWluZWQsCiAgICAgICAgICAgICAgY2FsbGVkX2NlbGxzX3JldGFpbmVkLAogICAgICAgICAgICAgIGZpbHRlcl9jZWxscykKYGBgCgojIyBTYXZlIGRhdGEKCmBgYHtyfQpzYXZlUkRTKGZpbHRlcmVkX3VtaV9jb3VudHMsIAogICAgICAgICIuL2RhdGEvZmlsdGVyZWRfdW1pX2NvdW50c19ub3Zhc2VxX2wxLnJkcyIpCnNhdmVSRFMoZmlsdGVyZWRfdW1pX2NvdW50c19yZXRhaW5lZCwKICAgICAgICAiLi9kYXRhL2ZpbHRlcmVkX3VtaV9jb3VudHNfcmV0YWluZWRfbm92YXNlcV9sMS5yZHMiKQpgYGAKCgoKCiMgUnVuIGFuYWx5c2lzCgpSZXN0YXJ0IGZpcnN0IQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoQW5ub3RhdGlvbkh1YikKbGlicmFyeShzY3JhbikKbGlicmFyeShzY2F0ZXIpCmxpYnJhcnkodXdvdCkgIyBGb3IgdW1hcApsaWJyYXJ5KHplYWxsb3QpCnNldC5zZWVkKDQyKQpgYGAKCgpgYGB7cn0KZ2V0X3RyYW5zY3JpcHRvbWUgPC0gZnVuY3Rpb24oYWhfaWQpIHsKICBhaCA8LSBBbm5vdGF0aW9uSHViKCkKICBnZW5lX21ldGFkYXRhIDwtIGFoW1thaF9pZF1dCiAgZ2VuZV9tZXRhZGF0YSA8LQogICAgZ2VuZV9tZXRhZGF0YVttY29scyhnZW5lX21ldGFkYXRhKSR0eXBlID09ICJnZW5lIiwKICAgICAgICAgICAgICAgICAgYygiZ2VuZV9pZCIsICJnZW5lX25hbWUiLCAiZ2VuZV9iaW90eXBlIildCiAgc2VxbGV2ZWxzU3R5bGUoZ2VuZV9tZXRhZGF0YSkgPC0gIlVDU0MiCiAgI2dlbmVfbWV0YWRhdGEgPC0ga2VlcFN0YW5kYXJkQ2hyb21vc29tZXMoZ2VuZV9tZXRhZGF0YSwgCiAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJ1bmluZy5tb2RlPSJjb2Fyc2UiKQogIGdlbmVfbWV0YWRhdGEgIDwtCiAgICBnZW5lX21ldGFkYXRhICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgZHBseXI6OnNlbGVjdChnZW5lX2lkLAogICAgICAgICAgICAgICAgICBzZXFuYW1lcywKICAgICAgICAgICAgICAgICAgZ2VuZV9uYW1lLAogICAgICAgICAgICAgICAgICBnZW5lX2Jpb3R5cGUsCiAgICAgICAgICAgICAgICAgIGdlbmVfaWQpCiAgCiAgcmV0dXJuKGdlbmVfbWV0YWRhdGEpCiAgCn0KCmFkZF9tZXRhZGF0YSA8LSBmdW5jdGlvbihzY2UxMHgsIGdlbmVfbWV0YWRhdGEpIHsKICByb3dEYXRhKHNjZTEweCkgPC0KICAgIGdlbmVfbWV0YWRhdGEgJT4lCiAgICBsZWZ0X2pvaW4oYXNfdGliYmxlKHJvd0RhdGEoc2NlMTB4KSksCiAgICAgICAgICAgICAgLiwKICAgICAgICAgICAgICBieSA9ICJnZW5lX2lkIikgJT4lCiAgICBkcGx5cjo6cmVuYW1lKGNociA9IHNlcW5hbWVzKQogIAogIHJvd25hbWVzKHNjZTEweCkgPC0gdW5pcXVpZnlGZWF0dXJlTmFtZXMocm93RGF0YShzY2UxMHgpJGdlbmVfaWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dEYXRhKHNjZTEweCkkZ2VuZV9uYW1lKQogIHNjZTEweCA8LSBhZGRQZXJDZWxsUUMoc2NlMTB4LAogICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0cyA9IGxpc3QobWl0byA9IHdoaWNoKHJvd0RhdGEoc2NlMTB4KSRjaHIgPT0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjaHJNIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgZmxhdHRlbiA9IFRSVUUpCiAgCiAgc2NlMTB4IDwtIGFkZFBlckZlYXR1cmVRQyhzY2UxMHgpCiAgCiAgCiAgcmV0dXJuKHNjZTEweCkKfQp0b19zY2UgPC0gZnVuY3Rpb24oZGF0YSwgc3VmZikgewogIGNvbF9uYW1lcyA8LSBwYXN0ZShjb2xuYW1lcyhkYXRhKSwgc3VmZiwgc2VwID0gIl8iKQogIHJvd19uYW1lcyA8LSByb3duYW1lcyhkYXRhKQogIAogIGRhdGEgPC0KICAgIGRhdGEgJT4lCiAgICBhcy5tYXRyaXgoKQogIAogIGNvbG5hbWVzKGRhdGEpIDwtIE5VTEwKICByb3duYW1lcyhkYXRhKSA8LSBOVUxMCiAgCiAgCiAgZGF0YSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudCgKICAgIGFzc2F5cyA9IGxpc3QoY291bnRzID0gZGF0YSksCiAgICByb3dEYXRhID0gRGF0YUZyYW1lKGdlbmVfaWQgPSByb3dfbmFtZXMpLAogICAgY29sRGF0YSA9IERhdGFGcmFtZShiYXJjb2RlID0gY29sX25hbWVzKQogICkKICAKICByZXR1cm4oZGF0YSkKfQoKY3JlYXRlX3NjZSA8LSBmdW5jdGlvbihkYXRhX2xpc3QsIGdlbmVfbWV0YWRhdGEpIHsKICBzdWZmaXhlcyA8LSBzdHJfc3BsaXRfZml4ZWQobmFtZXMoZGF0YV9saXN0KSwgIl8iLCAyKVssIDJdCiAgZGF0YV9saXN0IDwtCiAgICBtYXAyKGRhdGFfbGlzdCwKICAgICAgICAgc3VmZml4ZXMsCiAgICAgICAgIHRvX3NjZSkKICAKICBkYXRhX2xpc3QgPC0gbWFwKGRhdGFfbGlzdCwgYWRkX21ldGFkYXRhLCBnZW5lX21ldGFkYXRhKQogIAogIHJldHVybihkYXRhX2xpc3QpCn0KCnByb2Nlc3Nfc2FtcGxlIDwtCiAgZnVuY3Rpb24oc2FtcGxlLAogICAgICAgICAgIHVtaV9jb3VudHNfcHVyZ2VkLAogICAgICAgICAgIHNjZTEweF91bnB1cmdlZCkgewogICAgc2NlMTB4X3B1cmdlZCA8LSB1bWlfY291bnRzX3B1cmdlZFtbc2FtcGxlXV0KICAgIHNjZTEweF91bnB1cmdlZCA8LSB1bWlfY291bnRzX3VucHVyZ2VkW1tzYW1wbGVdXQogICAgCiAgICBuX2V4cHJzX2dlbmVzX3VucHVyZ2VkIDwtCiAgICAgIG5leHBycyhzY2UxMHhfdW5wdXJnZWQsCiAgICAgICAgICAgICBkZXRlY3Rpb25fbGltaXQgPSAwLAogICAgICAgICAgICAgYnlyb3cgPSBUUlVFKQogICAgc2NlMTB4X3VucHVyZ2VkIDwtIHNjZTEweF91bnB1cmdlZFtuX2V4cHJzX2dlbmVzX3VucHVyZ2VkID4gMCwgXQogICAgCiAgICBuX2V4cHJzX2dlbmVzX3B1cmdlZCA8LQogICAgICBuZXhwcnMoc2NlMTB4X3B1cmdlZCwKICAgICAgICAgICAgIGRldGVjdGlvbl9saW1pdCA9IDAsCiAgICAgICAgICAgICBieXJvdyA9IFRSVUUpCiAgICBzY2UxMHhfcHVyZ2VkIDwtIHNjZTEweF9wdXJnZWRbbl9leHByc19nZW5lc19wdXJnZWQgPiAwLCBdCiAgICAKICAgIAogICAgZHRfc3VtbWFyeSA8LQogICAgICBsZWZ0X2pvaW4oCiAgICAgICAgY29sRGF0YShzY2UxMHhfdW5wdXJnZWQpWywgYygiYmFyY29kZSIsICJzdW0iLCAiZGV0ZWN0ZWQiKV0gJT4lIGFzX3RpYmJsZSgpLAogICAgICAgIGNvbERhdGEoc2NlMTB4X3B1cmdlZClbLCBjKCJiYXJjb2RlIiwgInN1bSIsICJkZXRlY3RlZCIpXSAlPiUgYXNfdGliYmxlKCksCiAgICAgICAgYnkgPSAiYmFyY29kZSIKICAgICAgKSAlPiUKICAgICAgbXV0YXRlKAogICAgICAgIHBoYW50b21fc3VtID0gc3VtLnggLSBzdW0ueSAsCiAgICAgICAgcGhhbnRvbV9kZXRlY3RlZCA9IGRldGVjdGVkLnggLSBkZXRlY3RlZC55ICwKICAgICAgICB1bmNhbGxlZCA9ICEoCiAgICAgICAgICBjb2xEYXRhKHNjZTEweF91bnB1cmdlZCkkYmFyY29kZSAlaW4lIGNvbERhdGEoc2NlMTB4X3B1cmdlZCkkYmFyY29kZQogICAgICAgICkKICAgICAgKSAlPiUKICAgICAgZHBseXI6OnNlbGVjdChiYXJjb2RlLCBwaGFudG9tX3N1bSwgcGhhbnRvbV9kZXRlY3RlZCwgdW5jYWxsZWQpCiAgICAKICAgIGNvbERhdGEoc2NlMTB4X3VucHVyZ2VkKSA8LQogICAgICBsZWZ0X2pvaW4oY29sRGF0YShzY2UxMHhfdW5wdXJnZWQpICU+JSBhc190aWJibGUoKSwgZHRfc3VtbWFyeSAsCiAgICAgICAgICAgICAgICBieSA9ICJiYXJjb2RlIikgJT4lCiAgICAgIGFzKC4sICJEYXRhRnJhbWUiKQogICAgCiAgICBnZW5lX3N1bW1hcnlfZHQgPC0KICAgICAgZnVsbF9qb2luKAogICAgICAgIHJvd0RhdGEoc2NlMTB4X3VucHVyZ2VkKVssIGMoImdlbmVfaWQiLCAiZ2VuZV9uYW1lIiwgIm1lYW4iKV0gJT4lCiAgICAgICAgICBhc190aWJibGUoKSAlPiUKICAgICAgICAgIG11dGF0ZShzdW0gPSBtZWFuICogZGltKHNjZTEweF91bnB1cmdlZClbMl0pICU+JQogICAgICAgICAgZHBseXI6OnNlbGVjdCgtbWVhbiksCiAgICAgICAgcm93RGF0YShzY2UxMHhfcHVyZ2VkKVssIGMoImdlbmVfaWQiLCAiZ2VuZV9uYW1lIiwgIm1lYW4iKV0gJT4lCiAgICAgICAgICBhc190aWJibGUoKSAlPiUKICAgICAgICAgIG11dGF0ZShzdW0gPSBtZWFuICogZGltKHNjZTEweF9wdXJnZWQpWzJdKSAlPiUKICAgICAgICAgIGRwbHlyOjpzZWxlY3QoLW1lYW4pLAogICAgICAgIGJ5ID0gYygiZ2VuZV9pZCIsICJnZW5lX25hbWUiKQogICAgICApICU+JQogICAgICBtdXRhdGUocGhhbnRvbXNfc3VtID0gc3VtLnggLSBzdW0ueSkgJT4lCiAgICAgIGFycmFuZ2UoLXBoYW50b21zX3N1bSkKICAgIAogICAgY2x1c3RlcnMgPC0gcXVpY2tDbHVzdGVyKHNjZTEweF91bnB1cmdlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4ubWVhbj0wLjEyKQogICAgc2NlMTB4X3VucHVyZ2VkIDwtIGNvbXB1dGVTdW1GYWN0b3JzKHNjZTEweF91bnB1cmdlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3RlcnM9Y2x1c3RlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5tZWFuPTAuMTIpCiAgICAKICAgIGNsdXN0ZXJzIDwtIHF1aWNrQ2x1c3RlcihzY2UxMHhfcHVyZ2VkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5tZWFuPTAuMTIpCiAgICBzY2UxMHhfcHVyZ2VkIDwtIGNvbXB1dGVTdW1GYWN0b3JzKHNjZTEweF9wdXJnZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJzPWNsdXN0ZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4ubWVhbj0wLjEyKQogICAgCiAgICBzY2UxMHhfdW5wdXJnZWQgPC0gc2NhdGVyOjpsb2dOb3JtQ291bnRzKHNjZTEweF91bnB1cmdlZCkKICAgIHNjZTEweF9wdXJnZWQgPC0gc2NhdGVyOjpsb2dOb3JtQ291bnRzKHNjZTEweF9wdXJnZWQpCiAgICAKICAgIHJldHVybigKICAgICAgbGlzdCgKICAgICAgICBzY2UxMHhfdW5wdXJnZWQgPSBzY2UxMHhfdW5wdXJnZWQsCiAgICAgICAgc2NlMTB4X3B1cmdlZCA9IHNjZTEweF9wdXJnZWQsCiAgICAgICAgZ2VuZV9zdW1tYXJ5X2R0ID0gZ2VuZV9zdW1tYXJ5X2R0CiAgICAgICkKICAgICkKICAgIAogIH0KCnJ1bl91bWFwIDwtIGZ1bmN0aW9uKHNjZTEweCwKICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiY291bnRzIiwKICAgICAgICAgICAgICAgICAgICAgcHJlZml4ID0gInVtYXAiKSB7CiAgc2NlMTB4X210eCA8LQogICAgdChhc3NheShzY2UxMHgsIGFzc2F5KSAlPiUKICAgICAgICBhcy5tYXRyaXgoKSkKICAKICAKICBzY2VfY2VsbHNfdW1hcCA8LSB1bWFwKAogICAgc2NlMTB4X210eCwKICAgICMgIG5fbmVpZ2hib3JzPTEwMEwsCiAgICBuX2NvbXBvbmVudHMgPSAyLAogICAjICBtaW5fZGlzdD0wLjAwNSwKICAgICMgIG1ldHJpYz0ibWFuaGF0dGFuIiwKICAgIHZlcmJvc2UgPSBUUlVFLAogICAgbl90aHJlYWRzID0gNwogICkKICAKICBjb2xuYW1lcyhzY2VfY2VsbHNfdW1hcCkgPC0gcGFzdGUwKHByZWZpeCwgc2VxKDEsIDIpKQogIHJlZHVjZWREaW0oc2NlMTB4LCBwcmVmaXgpIDwtIHNjZV9jZWxsc191bWFwCiAgCiAgcmV0dXJuKHNjZTEweCkKfQpgYGAKCgpgYGB7cn0KaW5wdXRfZGlyIDwtICJ+L0RvY3VtZW50cy9pbmRleF9ob3BwaW5nL2RhdGEiCnRhcmdldF9zYW1wbGVzIDwtIGMoIlA3XzEiLCAiUDdfOCIgKQpgYGAKCiMjIExvYWQgVHJhbnNjcmlwdG9tZQoKYGBge3J9CmdlbmVfbWV0YWRhdGEgPC0gZ2V0X3RyYW5zY3JpcHRvbWUoIkFINTA4NzAiKSAgI011c19tdXNjdWx1cy5HUkNtMzguODQKYGBgCgoKIyMgTG9hZCBVTUkgQ291bnRzIGJlZm9yZSBhbmQgYWZ0ZXIgcHVyZ2luZwoKYGBge3J9CnVtaV9jb3VudHNfdW5wdXJnZWQgPC0gcmVhZFJEUyggZmlsZS5wYXRoKGlucHV0X2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpbHRlcmVkX3VtaV9jb3VudHNfbm92YXNlcV9sMS5yZHMiKSkKdW1pX2NvdW50c191bnB1cmdlZCA8LSB1bWlfY291bnRzX3VucHVyZ2VkW3RhcmdldF9zYW1wbGVzXQpgYGAKCmBgYHtyfQp1bWlfY291bnRzX3B1cmdlZCA8LSByZWFkUkRTKCBmaWxlLnBhdGgoaW5wdXRfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpbHRlcmVkX3VtaV9jb3VudHNfcmV0YWluZWRfbm92YXNlcV9sMS5yZHMiKSkKdW1pX2NvdW50c19wdXJnZWQgPC0gdW1pX2NvdW50c19wdXJnZWRbdGFyZ2V0X3NhbXBsZXNdCmBgYAoKIyMgQ3JlYXRlIFNDRSBvYmplY3RzIHdpdGggbWV0YWRhdGEKCmBgYHtyfQp1bWlfY291bnRzX3VucHVyZ2VkIDwtIGNyZWF0ZV9zY2UodW1pX2NvdW50c191bnB1cmdlZCwgIGdlbmVfbWV0YWRhdGEpCmBgYAoKYGBge3J9CnVtaV9jb3VudHNfcHVyZ2VkIDwtIGNyZWF0ZV9zY2UodW1pX2NvdW50c19wdXJnZWQsIGdlbmVfbWV0YWRhdGEpCmBgYAoKIyMgUHJvY2VzcyBzYW1wbGUgUDdfMQoKYGBge3J9CmMoc2NlMTB4X3VucHVyZ2VkLCBzY2UxMHhfcHVyZ2VkLCBnZW5lX3N1bW1hcnlfZHRfMSkgJTwtJSAKICBwcm9jZXNzX3NhbXBsZSgiUDdfMSIsIHVtaV9jb3VudHNfcHVyZ2VkLCBzY2UxMHhfdW5wdXJnZWQgKQpgYGAKCmBgYHtyfQpnZW5lX3N1bW1hcnlfZHRfMQpgYGAKCiMjIyBSdW4gVU1BUAoKYGBge3J9CnNjZTEweF91bnB1cmdlZCA8LSBydW5fdW1hcChzY2UxMHhfdW5wdXJnZWQsIGFzc2F5PSJsb2djb3VudHMiLCBwcmVmaXg9InVtYXAiKQpgYGAKCiMjIyBQTG90IGltYmVkZGluZwoKYGBge3J9CmRmX3RvX3Bsb3RfMSA8LSAKICBiaW5kX2NvbHMocmVkdWNlZERpbShzY2UxMHhfdW5wdXJnZWQsICJ1bWFwIikgJT4lIAogICAgICAgICAgICAgIGFzX3RpYmJsZSgpLCAKICAgICAgICAgICAgdChhc3NheShzY2UxMHhfdW5wdXJnZWQsICJsb2djb3VudHMiKVtjKCJTZnRwYyIsICJMeXoyIiksXSkgJT4lIAogICAgICAgICAgICAgIGFzX3RpYmJsZSgpLCAKICAgICAgICAgICAgY29sRGF0YShzY2UxMHhfdW5wdXJnZWQpWywgYygic3VtIiwidW5jYWxsZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwaGFudG9tX3N1bSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBoYW50b21fZGV0ZWN0ZWQiKV0gJT4lIAogICAgICAgICAgICAgIGFzX3RpYmJsZSgpKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9MTB9CmdncGxvdChkZl90b19wbG90XzEgKSArCiAgZ2VvbV9wb2ludChhZXMoIHVtYXAxLAogICAgICAgICAgICAgICAgICB1bWFwMiwKICAgICAgICAgICAgICAgICAgY29sb3VyPSBwaGFudG9tX3N1bT09MCksCiAgICAgICAgICAgICBzaXplPTAuOSwKICAgICAgICAgICAgIGFscGhhPTEpICsKICB0aGVtZV9jbGFzc2ljKCkgCgpnZ3NhdmUoIlA3XzFfdW1hcC5wZGYiKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMH0KZ2dwbG90KGRmX3RvX3Bsb3RfMSApICsKICBnZW9tX3BvaW50KGFlcyggdW1hcDEsCiAgICAgICAgICAgICAgICAgIHVtYXAyLAogICAgICAgICAgICAgICAgICBjb2xvdXI9IEx5ejIhPTApLAogICAgICAgICAgICAgc2l6ZT0wLjksCiAgICAgICAgICAgICBhbHBoYT0xKSArCiAgdGhlbWVfY2xhc3NpYygpIAoKZ2dzYXZlKCJQN18xX0x5ejJfdW1hcC5wZGYiKQpgYGAKCiMjIFByb2Nlc3Mgc2FtcGxlIFA3XzgKCmBgYHtyfQpjKHNjZTEweF91bnB1cmdlZCwgc2NlMTB4X3B1cmdlZCwgZ2VuZV9zdW1tYXJ5X2R0XzIpICU8LSUgCiAgcHJvY2Vzc19zYW1wbGUoIlA3XzgiLCB1bWlfY291bnRzX3B1cmdlZCwgc2NlMTB4X3VucHVyZ2VkICkKYGBgCgpgYGB7cn0KZ2VuZV9zdW1tYXJ5X2R0XzIKYGBgCgojIyMgUnVuIFVNQVAKCmBgYHtyfQpzY2UxMHhfdW5wdXJnZWQgPC0gcnVuX3VtYXAoc2NlMTB4X3VucHVyZ2VkLCBhc3NheT0ibG9nY291bnRzIiwgcHJlZml4PSJ1bWFwIikKYGBgCgojIyMgUExvdCBpbWJlZGRpbmcKCgpgYGB7cn0KZGZfdG9fcGxvdF8yIDwtIAogIGJpbmRfY29scyhyZWR1Y2VkRGltKHNjZTEweF91bnB1cmdlZCwgInVtYXAiKSAlPiUgCiAgICAgICAgICAgICAgYXNfdGliYmxlKCksIAogICAgICAgICAgICB0KGFzc2F5KHNjZTEweF91bnB1cmdlZCwgImxvZ2NvdW50cyIpW2MoIkZhYnAxIiwgIlNjZ2IxYTEiKSxdKSAlPiUgCiAgICAgICAgICAgICAgYXNfdGliYmxlKCksCiAgICAgICAgICAgIGNvbERhdGEoc2NlMTB4X3VucHVyZ2VkKVssIGMoInN1bSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInVuY2FsbGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGhhbnRvbV9zdW0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwaGFudG9tX2RldGVjdGVkIildICU+JSAKICAgICAgICAgICAgICBhc190aWJibGUoKSkKYGBgCgoKCmBgYHtyIGZpZy53aWR0aD0xMH0KZ2dwbG90KGRmX3RvX3Bsb3RfMikgKwogIGdlb21fcG9pbnQoYWVzKCB1bWFwMSwKICAgICAgICAgICAgICAgICAgdW1hcDIsCiAgICAgICAgICAgICAgICAgIGNvbG91cj0gcGhhbnRvbV9zdW09PTApLAogICAgICAgICAgICAgc2l6ZT0wLjksCiAgICAgICAgICAgICBhbHBoYT0xKSArCiAgdGhlbWVfY2xhc3NpYygpIApnZ3NhdmUoIlA3XzhfdW1hcC5wZGYiKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9MTB9CmdncGxvdChkZl90b19wbG90XzIgKSArCiAgZ2VvbV9wb2ludChhZXMoIHVtYXAxLAogICAgICAgICAgICAgICAgICB1bWFwMiwKICAgICAgICAgICAgICAgICAgY29sb3VyPSBGYWJwMSAhPSAwKSwKICAgICAgICAgICAgIHNpemU9MC45LAogICAgICAgICAgICAgYWxwaGE9MSkgKwogIHRoZW1lX2NsYXNzaWMoKSAKZ2dzYXZlKCJQN184X0ZhYnAxX3VtYXAucGRmIikKYGBgCgoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=