Setup knitr and load utility functions

knitr::opts_chunk$set(echo = TRUE)
knitr::opts_knit$set(root.dir="E:/DISC/reproducibility")
utilities_path = "./source/utilities.r"
source(utilities_path)
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
reldist: Relative Distribution Methods
Version 1.6-6 created on 2016-10-07.
copyright (c) 2003, Mark S. Handcock, University of California-Los Angeles
 For citation information, type citation("reldist").
 Type help(package="reldist") to get started.

Generate data file

The original BONE_MARROW data was downloaded from HCA website (https://data.humancellatlas.org/explore/projects/cc95ff89-2e68-4a08-a234-480eca21ce79).
The authors of this paper (https://www.biorxiv.org/content/10.1101/2020.01.29.925974v1) aligned the original sequence data (in .fastq format) to hg19 reference genome using CellRanger instead of using the count matrix provided by HCA.
Their cell-filtered count matrix can be found at https://doc-04-6g-docs.googleusercontent.com/docs/securesc/rm132bl2k8nvnlftqa8a8d5p239lbngf/6o5dsruhjpmecgnkd0nn4b1ak3ss8ufd/1588554075000/07888005335114604629/01857410241295225190/1euh8YB8ThSLHJNQMTCuuKp_nRiME1KzN?e=download&authuser=0&nonce=7apqnnaq9bch8&user=01857410241295225190&hash=a60rd66gq56e0af1vc5ua60146t3gq7m or https://github.com/iyhaoo/DISC/blob/master/reproducibility/data/HCA_tissue/original_data/MantonBM6_count.rds.

if(!file.exists("./data/BONE_MARROW/raw.loom")){
  original_data <- readRDS("./data/BONE_MARROW/original_data/MantonBM6_count.rds")
  save_h5("./data/BONE_MARROW/original.loom", as.matrix(t(original_data)))
  seurat_obj <- CreateSeuratObject(counts = as.data.frame(original_data), min.features = 500)
  cell_names = colnames(seurat_obj[["RNA"]]@counts)
  gene_names = rownames(original_data)
  raw_data = as.matrix(original_data[, cell_names])
  save_h5("./data/BONE_MARROW/raw.loom", t(raw_data))
}else{
  raw_data = readh5_loom("./data/BONE_MARROW/raw.loom")
  cell_names = colnames(raw_data)
  gene_names = rownames(raw_data)
}
[1] "./data/BONE_MARROW/raw.loom"
[1] 32738  6939
print(dim(raw_data))
[1] 32738  6939
print("BONE_MARROW...OK!")
[1] "BONE_MARROW...OK!"
if(!file.exists("./data/BONE_MARROW/bulk.loom")){
  gene_bulk_all = as.matrix(read.table("./data/BONE_MARROW/original_data/GSE74246_RNAseq_All_Counts.txt.gz", header = T, row.names = 1))
  gene_bulk_mat = gene_bulk_all[, grep("^X", colnames(gene_bulk_all))]
  save_h5("./data/BONE_MARROW/bulk.loom", as.matrix(t(gene_bulk_mat)))
}else{
  gene_bulk_mat = readh5_loom("./data/BONE_MARROW/bulk.loom")
}
[1] "./data/BONE_MARROW/bulk.loom"
[1] 25498    49
print(dim(gene_bulk_mat))
[1] 25498    49
print("BONE_MARROW_bulk...OK!")
[1] "BONE_MARROW_bulk...OK!"
#  We used the raw data after gene selection for cell identification.
gene_bc_mat = readh5_loom("./data/BONE_MARROW/raw.loom")
[1] "./data/BONE_MARROW/raw.loom"
[1] 32738  6939
gene_bc_filt = gene_bc_mat[gene_selection(gene_bc_mat, 10), ]
dim(gene_bc_filt) #  13813, 6939
[1] 13813  6939
used_genes = rownames(gene_bc_filt)
output_dir = "./results/BONE_MARROW"
dir.create(output_dir, showWarnings = F, recursive = T)

Identify cells using bulk data.

Following this script (https://github.com/Winnie09/imputationBenchmark/blob/master/data/code/process/07_hca_assign_celltype.R), we use the bulk-sequence data (https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE74246) of 13 normal hematopoietic cell types and 3 acute myeloid leukemia cell types for cell identification, the file is downloaded from https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE74246&format=file&file=GSE74246%5FRNAseq%5FAll%5FCounts%2Etxt%2Egz.

if(!file.exists("./data/BONE_MARROW/cell_type.rds")){
  library(scran)
  scran_normalization = function(gene_bc_mat){
    #  The source of this function is https://github.com/Winnie09/imputationBenchmark/blob/master/data/code/process/06_make_hca_MantonBM6.R
    dimnames_gene_bc_mat = dimnames(gene_bc_mat)
    dimnames(gene_bc_mat) = list()
    sce = SingleCellExperiment(list(counts = gene_bc_mat))
    no_cores = max(c(detectCores() - 1, 1))
    if(ncol(gene_bc_mat) < 21){
      sce = computeSumFactors(sce, BPPARAM = MulticoreParam(workers = no_cores), sizes = c(5, 10, 15, 20))
    } else {
      sce = computeSumFactors(sce, BPPARAM = MulticoreParam(workers = no_cores))  
    }
    sf = sizeFactors(sce)
    dimnames(gene_bc_mat) = dimnames_gene_bc_mat
    return(log2(sweep(gene_bc_mat, 2, sf, "/") + 1))
  }
  scalematrix = function(data){
    cm = rowMeans(data)
    csd = apply(data, 1, sd)
    (data - cm) / csd
  }
  corfunc = function(m1, m2){
    scalematrix(t(m1)) %*% t(scalematrix(t(m2))) / (nrow(m1) - 1)
  }
  #  Use annotation information
  gz_path = "./data/hg19/Homo_sapiens.GRCh37.87.gtf.gz"
  annotation_mat = get_map(gz_path)
  tgngl = tapply(annotation_mat[, "gene_length"] / 1000, annotation_mat[, "gene_name"], max)
  gngl = as.vector(tgngl)
  names(gngl) = names(tgngl)
  gene_bulk_filt = gene_bulk_mat[row.names(gene_bulk_mat) %in% names(gngl),]
  gene_bulk_norm = sweep(gene_bulk_filt / gngl[rownames(gene_bulk_filt)], 2, colSums(gene_bulk_filt) / 1e6, "/")
  bulk_data = log2(gene_bulk_norm[rowSums(gene_bulk_norm) > 0,] + 1)
  bulk_cell_type = sapply(colnames(bulk_data), function(x){
    strsplit(x,"\\.")[[1]][2]
  }, USE.NAMES = F)
  gene_bc_filt = gene_bc_mat[gene_selection(gene_bc_mat, 10), ]
  dim(gene_bc_filt) #  13813, 6939
  sc_data = scran_normalization(gene_bc_filt)
  rownames(sc_data) = sub(".*:", "", rownames(gene_bc_filt))
  used_genes = intersect(rownames(bulk_data), rownames(sc_data))
  bulk_filt = bulk_data[used_genes, ]
  sc_filt = sc_data[used_genes, ]
  #  The expression level for each cell type in bulk sequencing
  bulk_mean = sapply(unique(bulk_cell_type),function(x) {
    rowMeans(bulk_filt[ , bulk_cell_type == x])
  })
  #  Find 100 top postive differentially expressed genes for each celltype pair.
  DEG_list = list()
  top_number = 100
  unique_celltype_pairs = combn(ncol(bulk_mean), 2)
  for(ii in seq(ncol(unique_celltype_pairs))){
    celltype_1 = colnames(bulk_mean)[unique_celltype_pairs[1, ii]]
    celltype_2 = colnames(bulk_mean)[unique_celltype_pairs[2, ii]]
    sort_result = sort(bulk_mean[ , celltype_1] - bulk_mean[ , celltype_2], decreasing = FALSE)
    DEG_list[[paste(celltype_2, celltype_1, sep = "-")]] = names(sort_result)[seq(top_number)]
    DEG_list[[paste(celltype_1, celltype_2, sep = "-")]] = names(sort_result)[seq(from = length(sort_result), to = length(sort_result) - (top_number - 1))]
  }
  #  Calculate the mean expression of these top-gene combinations across cell types (bulk) or cells (single-cell).
  expression_mean_function = function(gene_bc_norm, DEG_list){
    return(t(sapply(DEG_list, function(x){
      colMeans(gene_bc_norm[x, ])
    })))
  }
  bulk_DEG_expression_mean = expression_mean_function(bulk_mean, DEG_list)
  sc_DEG_expression_mean = expression_mean_function(sc_filt, DEG_list)
  #  Calculate the expression variation of these top-gene combinations across cell types (bulk) or cells (single-cell).
  expression_variation_function = function(x){
    return((x - rowMeans(x)) / apply(x, 1, sd))
  }
  bulk_DEG_expression_variation = expression_variation_function(bulk_DEG_expression_mean)
  sc_DEG_expression_variation = expression_variation_function(sc_DEG_expression_mean)
  #  Each top-gene combination correspond a cell type.
  bulk_DEG_combination_rank = apply(bulk_DEG_expression_variation, 2, rank)
  sc_DEG_combination_rank = apply(sc_DEG_expression_variation, 2, rank)
  #  Cell type identification.
  maxcorcut = 0.6
  difcorcut = 0
  cormat = corfunc(sc_DEG_combination_rank, bulk_DEG_combination_rank)
  maxcor = apply(cormat, 1, max)
  max2cor = apply(cormat, 1, function(x){
    sort(x, decreasing = T)[2]
  })
  cell_type = colnames(cormat)[apply(cormat, 1, which.max)]
  cell_type[maxcor < maxcorcut] = NA
  cell_type[maxcor - max2cor < difcorcut] = NA
  names(cell_type) = colnames(sc_data)
  saveRDS(cell_type, "./data/BONE_MARROW/cell_type.rds")
}else{
  cell_type = readRDS("./data/BONE_MARROW/cell_type.rds")
}
搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡SingleCellExperiment
搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡SummarizedExperiment
搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡GenomicRanges
搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡stats4
搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡BiocGenerics

搼㸴搼㸸挼㸸攼㹢戼㸳̼愼㹤戼㸰昼㹣愼㸳戼㹡愼㸱愼㹥BiocGenerics愼㸱愼㹦

The following objects are masked from 愼㸱愼㹥package:parallel愼㸱愼㹦:

    clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport, clusterMap, parApply, parCapply, parLapply,
    parLapplyLB, parRapply, parSapply, parSapplyLB

The following object is masked from 愼㸱愼㹥package:Matrix愼㸱愼㹦:

    which

The following objects are masked from 愼㸱愼㹥package:stats愼㸱愼㹦:

    IQR, mad, sd, var, xtabs

The following objects are masked from 愼㸱愼㹥package:base愼㸱愼㹦:

    anyDuplicated, append, as.data.frame, basename, cbind, colnames, dirname, do.call, duplicated, eval, evalq, Filter,
    Find, get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply, match, mget, order, paste, pmax, pmax.int,
    pmin, pmin.int, Position, rank, rbind, Reduce, rownames, sapply, setdiff, sort, table, tapply, union, unique,
    unsplit, which, which.max, which.min

搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡S4Vectors

搼㸴搼㸸挼㸸攼㹢戼㸳̼愼㹤戼㸰昼㹣愼㸳戼㹡愼㸱愼㹥S4Vectors愼㸱愼㹦

The following object is masked from 愼㸱愼㹥package:future愼㸱愼㹦:

    values

The following object is masked from 愼㸱愼㹥package:Matrix愼㸱愼㹦:

    expand

The following object is masked from 愼㸱愼㹥package:base愼㸱愼㹦:

    expand.grid

搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡IRanges

搼㸴搼㸸挼㸸攼㹢戼㸳̼愼㹤戼㸰昼㹣愼㸳戼㹡愼㸱愼㹥IRanges愼㸱愼㹦

The following object is masked from 愼㸱愼㹥package:grDevices愼㸱愼㹦:

    windows

搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡GenomeInfoDb
搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡Biobase
Welcome to Bioconductor

    Vignettes contain introductory material; view with 'browseVignettes()'. To cite Bioconductor, see
    'citation("Biobase")', and for packages 'citation("pkgname")'.

搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡DelayedArray
搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡matrixStats

搼㸴搼㸸挼㸸攼㹢戼㸳̼愼㹤戼㸰昼㹣愼㸳戼㹡愼㸱愼㹥matrixStats愼㸱愼㹦

The following objects are masked from 愼㸱愼㹥package:Biobase愼㸱愼㹦:

    anyMissing, rowMedians

The following object is masked from 愼㸱愼㹥package:reldist愼㸱愼㹦:

    iqr

搼㸴搼㸸挼㸸攼㹢搼㸰攼㸸Ҫ戼㸵ij̼愼㹤戼㸰昼㹣愼㸳戼㹡BiocParallel

搼㸴搼㸸挼㸸攼㹢戼㸳̼愼㹤戼㸰昼㹣愼㸳戼㹡愼㸱愼㹥DelayedArray愼㸱愼㹦

The following objects are masked from 愼㸱愼㹥package:matrixStats愼㸱愼㹦:

    colMaxs, colMins, colRanges, rowMaxs, rowMins, rowRanges

The following objects are masked from 愼㸱愼㹥package:base愼㸱愼㹦:

    aperm, apply, rowsum


搼㸴搼㸸挼㸸攼㹢戼㸳̼愼㹤戼㸰昼㹣愼㸳戼㹡愼㸱愼㹥SummarizedExperiment愼㸱愼㹦

The following object is masked from 愼㸱愼㹥package:Seurat愼㸱愼㹦:

    Assays

Treat input as file
|--------------------------------------------------|
|==================================================|
MulticoreParam() not supported on Windows, use SnowParam()
print("Cell Type ... OK!")
[1] "Cell Type ... OK!"
LS0tDQp0aXRsZTogIkRhdGEgcHJlcGFyYXRpb24gZm9yIEJPTkVfTUFSUk9XIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCiMjIyBTZXR1cCBrbml0ciBhbmQgbG9hZCB1dGlsaXR5IGZ1bmN0aW9ucw0KYGBge3Igc2V0dXB9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpcj0iRTovRElTQy9yZXByb2R1Y2liaWxpdHkiKQ0KYGBgDQpgYGB7cn0NCnV0aWxpdGllc19wYXRoID0gIi4vc291cmNlL3V0aWxpdGllcy5yIg0Kc291cmNlKHV0aWxpdGllc19wYXRoKQ0KYGBgDQojIyMgR2VuZXJhdGUgZGF0YSBmaWxlDQpUaGUgb3JpZ2luYWwgQk9ORV9NQVJST1cgZGF0YSB3YXMgZG93bmxvYWRlZCBmcm9tIEhDQSB3ZWJzaXRlIChodHRwczovL2RhdGEuaHVtYW5jZWxsYXRsYXMub3JnL2V4cGxvcmUvcHJvamVjdHMvY2M5NWZmODktMmU2OC00YTA4LWEyMzQtNDgwZWNhMjFjZTc5KS48L2JyPg0KVGhlIGF1dGhvcnMgb2YgdGhpcyBwYXBlciAoaHR0cHM6Ly93d3cuYmlvcnhpdi5vcmcvY29udGVudC8xMC4xMTAxLzIwMjAuMDEuMjkuOTI1OTc0djEpIGFsaWduZWQgdGhlIG9yaWdpbmFsIHNlcXVlbmNlIGRhdGEgKGluIC5mYXN0cSBmb3JtYXQpIHRvIGhnMTkgcmVmZXJlbmNlIGdlbm9tZSB1c2luZyBDZWxsUmFuZ2VyIGluc3RlYWQgb2YgdXNpbmcgdGhlIGNvdW50IG1hdHJpeCBwcm92aWRlZCBieSBIQ0EuPC9icj4NClRoZWlyIGNlbGwtZmlsdGVyZWQgY291bnQgbWF0cml4IGNhbiBiZSBmb3VuZCBhdCBodHRwczovL2RvYy0wNC02Zy1kb2NzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9kb2NzL3NlY3VyZXNjL3JtMTMyYmwyazhudm5sZnRxYThhOGQ1cDIzOWxibmdmLzZvNWRzcnVoanBtZWNnbmtkMG5uNGIxYWszc3M4dWZkLzE1ODg1NTQwNzUwMDAvMDc4ODgwMDUzMzUxMTQ2MDQ2MjkvMDE4NTc0MTAyNDEyOTUyMjUxOTAvMWV1aDhZQjhUaFNMSEpOUU1UQ3V1S3BfblJpTUUxS3pOP2U9ZG93bmxvYWQmYXV0aHVzZXI9MCZub25jZT03YXBxbm5hcTliY2g4JnVzZXI9MDE4NTc0MTAyNDEyOTUyMjUxOTAmaGFzaD1hNjByZDY2Z3E1NmUwYWYxdmM1dWE2MDE0NnQzZ3E3bSBvciBodHRwczovL2dpdGh1Yi5jb20vaXloYW9vL0RJU0MvYmxvYi9tYXN0ZXIvcmVwcm9kdWNpYmlsaXR5L2RhdGEvSENBX3Rpc3N1ZS9vcmlnaW5hbF9kYXRhL01hbnRvbkJNNl9jb3VudC5yZHMuPC9icj4NCmBgYHtyfQ0KaWYoIWZpbGUuZXhpc3RzKCIuL2RhdGEvQk9ORV9NQVJST1cvcmF3Lmxvb20iKSl7DQogIG9yaWdpbmFsX2RhdGEgPC0gcmVhZFJEUygiLi9kYXRhL0JPTkVfTUFSUk9XL29yaWdpbmFsX2RhdGEvTWFudG9uQk02X2NvdW50LnJkcyIpDQogIHNhdmVfaDUoIi4vZGF0YS9CT05FX01BUlJPVy9vcmlnaW5hbC5sb29tIiwgYXMubWF0cml4KHQob3JpZ2luYWxfZGF0YSkpKQ0KICBzZXVyYXRfb2JqIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBhcy5kYXRhLmZyYW1lKG9yaWdpbmFsX2RhdGEpLCBtaW4uZmVhdHVyZXMgPSA1MDApDQogIGNlbGxfbmFtZXMgPSBjb2xuYW1lcyhzZXVyYXRfb2JqW1siUk5BIl1dQGNvdW50cykNCiAgZ2VuZV9uYW1lcyA9IHJvd25hbWVzKG9yaWdpbmFsX2RhdGEpDQogIHJhd19kYXRhID0gYXMubWF0cml4KG9yaWdpbmFsX2RhdGFbLCBjZWxsX25hbWVzXSkNCiAgc2F2ZV9oNSgiLi9kYXRhL0JPTkVfTUFSUk9XL3Jhdy5sb29tIiwgdChyYXdfZGF0YSkpDQp9ZWxzZXsNCiAgcmF3X2RhdGEgPSByZWFkaDVfbG9vbSgiLi9kYXRhL0JPTkVfTUFSUk9XL3Jhdy5sb29tIikNCiAgY2VsbF9uYW1lcyA9IGNvbG5hbWVzKHJhd19kYXRhKQ0KICBnZW5lX25hbWVzID0gcm93bmFtZXMocmF3X2RhdGEpDQp9DQpwcmludChkaW0ocmF3X2RhdGEpKQ0KcHJpbnQoIkJPTkVfTUFSUk9XLi4uT0shIikNCmlmKCFmaWxlLmV4aXN0cygiLi9kYXRhL0JPTkVfTUFSUk9XL2J1bGsubG9vbSIpKXsNCiAgZ2VuZV9idWxrX2FsbCA9IGFzLm1hdHJpeChyZWFkLnRhYmxlKCIuL2RhdGEvQk9ORV9NQVJST1cvb3JpZ2luYWxfZGF0YS9HU0U3NDI0Nl9STkFzZXFfQWxsX0NvdW50cy50eHQuZ3oiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxKSkNCiAgZ2VuZV9idWxrX21hdCA9IGdlbmVfYnVsa19hbGxbLCBncmVwKCJeWCIsIGNvbG5hbWVzKGdlbmVfYnVsa19hbGwpKV0NCiAgc2F2ZV9oNSgiLi9kYXRhL0JPTkVfTUFSUk9XL2J1bGsubG9vbSIsIGFzLm1hdHJpeCh0KGdlbmVfYnVsa19tYXQpKSkNCn1lbHNlew0KICBnZW5lX2J1bGtfbWF0ID0gcmVhZGg1X2xvb20oIi4vZGF0YS9CT05FX01BUlJPVy9idWxrLmxvb20iKQ0KfQ0KcHJpbnQoZGltKGdlbmVfYnVsa19tYXQpKQ0KcHJpbnQoIkJPTkVfTUFSUk9XX2J1bGsuLi5PSyEiKQ0KYGBgDQpgYGB7cn0NCiMgIFdlIHVzZWQgdGhlIHJhdyBkYXRhIGFmdGVyIGdlbmUgc2VsZWN0aW9uIGZvciBjZWxsIGlkZW50aWZpY2F0aW9uLg0KZ2VuZV9iY19tYXQgPSByZWFkaDVfbG9vbSgiLi9kYXRhL0JPTkVfTUFSUk9XL3Jhdy5sb29tIikNCmdlbmVfYmNfZmlsdCA9IGdlbmVfYmNfbWF0W2dlbmVfc2VsZWN0aW9uKGdlbmVfYmNfbWF0LCAxMCksIF0NCmRpbShnZW5lX2JjX2ZpbHQpICMgIDEzODEzLCA2OTM5DQp1c2VkX2dlbmVzID0gcm93bmFtZXMoZ2VuZV9iY19maWx0KQ0Kb3V0cHV0X2RpciA9ICIuL3Jlc3VsdHMvQk9ORV9NQVJST1ciDQpkaXIuY3JlYXRlKG91dHB1dF9kaXIsIHNob3dXYXJuaW5ncyA9IEYsIHJlY3Vyc2l2ZSA9IFQpDQpgYGANCiMjIyBJZGVudGlmeSBjZWxscyB1c2luZyBidWxrIGRhdGEuDQpGb2xsb3dpbmcgdGhpcyBzY3JpcHQgKGh0dHBzOi8vZ2l0aHViLmNvbS9XaW5uaWUwOS9pbXB1dGF0aW9uQmVuY2htYXJrL2Jsb2IvbWFzdGVyL2RhdGEvY29kZS9wcm9jZXNzLzA3X2hjYV9hc3NpZ25fY2VsbHR5cGUuUiksIHdlIHVzZSB0aGUgYnVsay1zZXF1ZW5jZSBkYXRhIChodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0U3NDI0Nikgb2YgMTMgbm9ybWFsIGhlbWF0b3BvaWV0aWMgY2VsbCB0eXBlcyBhbmQgMyBhY3V0ZSBteWVsb2lkIGxldWtlbWlhIGNlbGwgdHlwZXMgZm9yIGNlbGwgaWRlbnRpZmljYXRpb24sIHRoZSBmaWxlIGlzIGRvd25sb2FkZWQgZnJvbSBodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9kb3dubG9hZC8/YWNjPUdTRTc0MjQ2JmZvcm1hdD1maWxlJmZpbGU9R1NFNzQyNDYlNUZSTkFzZXElNUZBbGwlNUZDb3VudHMlMkV0eHQlMkVnei4NCmBgYHtyfQ0KaWYoIWZpbGUuZXhpc3RzKCIuL2RhdGEvQk9ORV9NQVJST1cvY2VsbF90eXBlLnJkcyIpKXsNCiAgbGlicmFyeShzY3JhbikNCiAgc2NyYW5fbm9ybWFsaXphdGlvbiA9IGZ1bmN0aW9uKGdlbmVfYmNfbWF0KXsNCiAgICAjICBUaGUgc291cmNlIG9mIHRoaXMgZnVuY3Rpb24gaXMgaHR0cHM6Ly9naXRodWIuY29tL1dpbm5pZTA5L2ltcHV0YXRpb25CZW5jaG1hcmsvYmxvYi9tYXN0ZXIvZGF0YS9jb2RlL3Byb2Nlc3MvMDZfbWFrZV9oY2FfTWFudG9uQk02LlINCiAgICBkaW1uYW1lc19nZW5lX2JjX21hdCA9IGRpbW5hbWVzKGdlbmVfYmNfbWF0KQ0KICAgIGRpbW5hbWVzKGdlbmVfYmNfbWF0KSA9IGxpc3QoKQ0KICAgIHNjZSA9IFNpbmdsZUNlbGxFeHBlcmltZW50KGxpc3QoY291bnRzID0gZ2VuZV9iY19tYXQpKQ0KICAgIG5vX2NvcmVzID0gbWF4KGMoZGV0ZWN0Q29yZXMoKSAtIDEsIDEpKQ0KICAgIGlmKG5jb2woZ2VuZV9iY19tYXQpIDwgMjEpew0KICAgICAgc2NlID0gY29tcHV0ZVN1bUZhY3RvcnMoc2NlLCBCUFBBUkFNID0gTXVsdGljb3JlUGFyYW0od29ya2VycyA9IG5vX2NvcmVzKSwgc2l6ZXMgPSBjKDUsIDEwLCAxNSwgMjApKQ0KICAgIH0gZWxzZSB7DQogICAgICBzY2UgPSBjb21wdXRlU3VtRmFjdG9ycyhzY2UsIEJQUEFSQU0gPSBNdWx0aWNvcmVQYXJhbSh3b3JrZXJzID0gbm9fY29yZXMpKSAgDQogICAgfQ0KICAgIHNmID0gc2l6ZUZhY3RvcnMoc2NlKQ0KICAgIGRpbW5hbWVzKGdlbmVfYmNfbWF0KSA9IGRpbW5hbWVzX2dlbmVfYmNfbWF0DQogICAgcmV0dXJuKGxvZzIoc3dlZXAoZ2VuZV9iY19tYXQsIDIsIHNmLCAiLyIpICsgMSkpDQogIH0NCiAgc2NhbGVtYXRyaXggPSBmdW5jdGlvbihkYXRhKXsNCiAgICBjbSA9IHJvd01lYW5zKGRhdGEpDQogICAgY3NkID0gYXBwbHkoZGF0YSwgMSwgc2QpDQogICAgKGRhdGEgLSBjbSkgLyBjc2QNCiAgfQ0KICBjb3JmdW5jID0gZnVuY3Rpb24obTEsIG0yKXsNCiAgICBzY2FsZW1hdHJpeCh0KG0xKSkgJSolIHQoc2NhbGVtYXRyaXgodChtMikpKSAvIChucm93KG0xKSAtIDEpDQogIH0NCiAgIyAgVXNlIGFubm90YXRpb24gaW5mb3JtYXRpb24NCiAgZ3pfcGF0aCA9ICIuL2RhdGEvYW5ub3RhdGlvbi9Ib21vX3NhcGllbnMuR1JDaDM3Ljg3Lmd0Zi5neiINCiAgYW5ub3RhdGlvbl9tYXQgPSBnZXRfbWFwKGd6X3BhdGgpDQogIHRnbmdsID0gdGFwcGx5KGFubm90YXRpb25fbWF0WywgImdlbmVfbGVuZ3RoIl0gLyAxMDAwLCBhbm5vdGF0aW9uX21hdFssICJnZW5lX25hbWUiXSwgbWF4KQ0KICBnbmdsID0gYXMudmVjdG9yKHRnbmdsKQ0KICBuYW1lcyhnbmdsKSA9IG5hbWVzKHRnbmdsKQ0KICBnZW5lX2J1bGtfZmlsdCA9IGdlbmVfYnVsa19tYXRbcm93Lm5hbWVzKGdlbmVfYnVsa19tYXQpICVpbiUgbmFtZXMoZ25nbCksXQ0KICBnZW5lX2J1bGtfbm9ybSA9IHN3ZWVwKGdlbmVfYnVsa19maWx0IC8gZ25nbFtyb3duYW1lcyhnZW5lX2J1bGtfZmlsdCldLCAyLCBjb2xTdW1zKGdlbmVfYnVsa19maWx0KSAvIDFlNiwgIi8iKQ0KICBidWxrX2RhdGEgPSBsb2cyKGdlbmVfYnVsa19ub3JtW3Jvd1N1bXMoZ2VuZV9idWxrX25vcm0pID4gMCxdICsgMSkNCiAgYnVsa19jZWxsX3R5cGUgPSBzYXBwbHkoY29sbmFtZXMoYnVsa19kYXRhKSwgZnVuY3Rpb24oeCl7DQogICAgc3Ryc3BsaXQoeCwiXFwuIilbWzFdXVsyXQ0KICB9LCBVU0UuTkFNRVMgPSBGKQ0KICBnZW5lX2JjX2ZpbHQgPSBnZW5lX2JjX21hdFtnZW5lX3NlbGVjdGlvbihnZW5lX2JjX21hdCwgMTApLCBdDQogIGRpbShnZW5lX2JjX2ZpbHQpICMgIDEzODEzLCA2OTM5DQogIHNjX2RhdGEgPSBzY3Jhbl9ub3JtYWxpemF0aW9uKGdlbmVfYmNfZmlsdCkNCiAgcm93bmFtZXMoc2NfZGF0YSkgPSBzdWIoIi4qOiIsICIiLCByb3duYW1lcyhnZW5lX2JjX2ZpbHQpKQ0KICB1c2VkX2dlbmVzID0gaW50ZXJzZWN0KHJvd25hbWVzKGJ1bGtfZGF0YSksIHJvd25hbWVzKHNjX2RhdGEpKQ0KICBidWxrX2ZpbHQgPSBidWxrX2RhdGFbdXNlZF9nZW5lcywgXQ0KICBzY19maWx0ID0gc2NfZGF0YVt1c2VkX2dlbmVzLCBdDQogICMgIFRoZSBleHByZXNzaW9uIGxldmVsIGZvciBlYWNoIGNlbGwgdHlwZSBpbiBidWxrIHNlcXVlbmNpbmcNCiAgYnVsa19tZWFuID0gc2FwcGx5KHVuaXF1ZShidWxrX2NlbGxfdHlwZSksZnVuY3Rpb24oeCkgew0KICAgIHJvd01lYW5zKGJ1bGtfZmlsdFsgLCBidWxrX2NlbGxfdHlwZSA9PSB4XSkNCiAgfSkNCiAgIyAgRmluZCAxMDAgdG9wIHBvc3RpdmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGZvciBlYWNoIGNlbGx0eXBlIHBhaXIuDQogIERFR19saXN0ID0gbGlzdCgpDQogIHRvcF9udW1iZXIgPSAxMDANCiAgdW5pcXVlX2NlbGx0eXBlX3BhaXJzID0gY29tYm4obmNvbChidWxrX21lYW4pLCAyKQ0KICBmb3IoaWkgaW4gc2VxKG5jb2wodW5pcXVlX2NlbGx0eXBlX3BhaXJzKSkpew0KICAgIGNlbGx0eXBlXzEgPSBjb2xuYW1lcyhidWxrX21lYW4pW3VuaXF1ZV9jZWxsdHlwZV9wYWlyc1sxLCBpaV1dDQogICAgY2VsbHR5cGVfMiA9IGNvbG5hbWVzKGJ1bGtfbWVhbilbdW5pcXVlX2NlbGx0eXBlX3BhaXJzWzIsIGlpXV0NCiAgICBzb3J0X3Jlc3VsdCA9IHNvcnQoYnVsa19tZWFuWyAsIGNlbGx0eXBlXzFdIC0gYnVsa19tZWFuWyAsIGNlbGx0eXBlXzJdLCBkZWNyZWFzaW5nID0gRkFMU0UpDQogICAgREVHX2xpc3RbW3Bhc3RlKGNlbGx0eXBlXzIsIGNlbGx0eXBlXzEsIHNlcCA9ICItIildXSA9IG5hbWVzKHNvcnRfcmVzdWx0KVtzZXEodG9wX251bWJlcildDQogICAgREVHX2xpc3RbW3Bhc3RlKGNlbGx0eXBlXzEsIGNlbGx0eXBlXzIsIHNlcCA9ICItIildXSA9IG5hbWVzKHNvcnRfcmVzdWx0KVtzZXEoZnJvbSA9IGxlbmd0aChzb3J0X3Jlc3VsdCksIHRvID0gbGVuZ3RoKHNvcnRfcmVzdWx0KSAtICh0b3BfbnVtYmVyIC0gMSkpXQ0KICB9DQogICMgIENhbGN1bGF0ZSB0aGUgbWVhbiBleHByZXNzaW9uIG9mIHRoZXNlIHRvcC1nZW5lIGNvbWJpbmF0aW9ucyBhY3Jvc3MgY2VsbCB0eXBlcyAoYnVsaykgb3IgY2VsbHMgKHNpbmdsZS1jZWxsKS4NCiAgZXhwcmVzc2lvbl9tZWFuX2Z1bmN0aW9uID0gZnVuY3Rpb24oZ2VuZV9iY19ub3JtLCBERUdfbGlzdCl7DQogICAgcmV0dXJuKHQoc2FwcGx5KERFR19saXN0LCBmdW5jdGlvbih4KXsNCiAgICAgIGNvbE1lYW5zKGdlbmVfYmNfbm9ybVt4LCBdKQ0KICAgIH0pKSkNCiAgfQ0KICBidWxrX0RFR19leHByZXNzaW9uX21lYW4gPSBleHByZXNzaW9uX21lYW5fZnVuY3Rpb24oYnVsa19tZWFuLCBERUdfbGlzdCkNCiAgc2NfREVHX2V4cHJlc3Npb25fbWVhbiA9IGV4cHJlc3Npb25fbWVhbl9mdW5jdGlvbihzY19maWx0LCBERUdfbGlzdCkNCiAgIyAgQ2FsY3VsYXRlIHRoZSBleHByZXNzaW9uIHZhcmlhdGlvbiBvZiB0aGVzZSB0b3AtZ2VuZSBjb21iaW5hdGlvbnMgYWNyb3NzIGNlbGwgdHlwZXMgKGJ1bGspIG9yIGNlbGxzIChzaW5nbGUtY2VsbCkuDQogIGV4cHJlc3Npb25fdmFyaWF0aW9uX2Z1bmN0aW9uID0gZnVuY3Rpb24oeCl7DQogICAgcmV0dXJuKCh4IC0gcm93TWVhbnMoeCkpIC8gYXBwbHkoeCwgMSwgc2QpKQ0KICB9DQogIGJ1bGtfREVHX2V4cHJlc3Npb25fdmFyaWF0aW9uID0gZXhwcmVzc2lvbl92YXJpYXRpb25fZnVuY3Rpb24oYnVsa19ERUdfZXhwcmVzc2lvbl9tZWFuKQ0KICBzY19ERUdfZXhwcmVzc2lvbl92YXJpYXRpb24gPSBleHByZXNzaW9uX3ZhcmlhdGlvbl9mdW5jdGlvbihzY19ERUdfZXhwcmVzc2lvbl9tZWFuKQ0KICAjICBFYWNoIHRvcC1nZW5lIGNvbWJpbmF0aW9uIGNvcnJlc3BvbmQgYSBjZWxsIHR5cGUuDQogIGJ1bGtfREVHX2NvbWJpbmF0aW9uX3JhbmsgPSBhcHBseShidWxrX0RFR19leHByZXNzaW9uX3ZhcmlhdGlvbiwgMiwgcmFuaykNCiAgc2NfREVHX2NvbWJpbmF0aW9uX3JhbmsgPSBhcHBseShzY19ERUdfZXhwcmVzc2lvbl92YXJpYXRpb24sIDIsIHJhbmspDQogICMgIENlbGwgdHlwZSBpZGVudGlmaWNhdGlvbi4NCiAgbWF4Y29yY3V0ID0gMC42DQogIGRpZmNvcmN1dCA9IDANCiAgY29ybWF0ID0gY29yZnVuYyhzY19ERUdfY29tYmluYXRpb25fcmFuaywgYnVsa19ERUdfY29tYmluYXRpb25fcmFuaykNCiAgbWF4Y29yID0gYXBwbHkoY29ybWF0LCAxLCBtYXgpDQogIG1heDJjb3IgPSBhcHBseShjb3JtYXQsIDEsIGZ1bmN0aW9uKHgpew0KICAgIHNvcnQoeCwgZGVjcmVhc2luZyA9IFQpWzJdDQogIH0pDQogIGNlbGxfdHlwZSA9IGNvbG5hbWVzKGNvcm1hdClbYXBwbHkoY29ybWF0LCAxLCB3aGljaC5tYXgpXQ0KICBjZWxsX3R5cGVbbWF4Y29yIDwgbWF4Y29yY3V0XSA9IE5BDQogIGNlbGxfdHlwZVttYXhjb3IgLSBtYXgyY29yIDwgZGlmY29yY3V0XSA9IE5BDQogIG5hbWVzKGNlbGxfdHlwZSkgPSBjb2xuYW1lcyhzY19kYXRhKQ0KICBzYXZlUkRTKGNlbGxfdHlwZSwgIi4vZGF0YS9CT05FX01BUlJPVy9jZWxsX3R5cGUucmRzIikNCn1lbHNlew0KICBjZWxsX3R5cGUgPSByZWFkUkRTKCIuL2RhdGEvQk9ORV9NQVJST1cvY2VsbF90eXBlLnJkcyIpDQp9DQpwcmludCgiQ2VsbCBUeXBlIC4uLiBPSyEiKQ0KYGBgDQo=