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 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 JURKAT data can be found at https://support.10xgenomics.com/single-cell-gene-expression/datasets/1.1.0/jurkat.
The original 293T data can be found at https://support.10xgenomics.com/single-cell-gene-expression/datasets/1.1.0/293t.
We filter cells following this paper (https://www.biorxiv.org/content/10.1101/2020.01.29.925974v1).

if(!file.exists("./data/JURKAT_293T/raw_Jurkat.loom")){
  temp <- tempfile()
  download.file("http://cf.10xgenomics.com/samples/cell-exp/1.1.0/jurkat/jurkat_filtered_gene_bc_matrices.tar.gz", temp)
  exdir = "./data/JURKAT_293T/original_data/Jurkat"
  dir.create(exdir, showWarnings = F, recursive = T)
  untar(temp, exdir = exdir)
  unlink(temp)
  original_data <- Read10X(data.dir = paste0(exdir, "/filtered_matrices_mex/hg19/"))
  save_h5("./data/JURKAT_293T/original_Jurkat.loom", as.matrix(t(original_data)))
  seurat_obj <- CreateSeuratObject(counts = as.data.frame(original_data), min.features = 500)
  cell_names_JURKAT = colnames(seurat_obj[["RNA"]]@counts)
  gene_names_JURKAT = rownames(original_data)
  raw_data_JURKAT = as.matrix(original_data[, cell_names_JURKAT])
  save_h5("./data/JURKAT_293T/raw_Jurkat.loom", t(raw_data_JURKAT))
}else{
  raw_data_JURKAT = readh5_loom("./data/JURKAT_293T/raw_Jurkat.loom")
  cell_names_JURKAT = colnames(raw_data_JURKAT)
  gene_names_JURKAT = rownames(raw_data_JURKAT)
}
试开URL’http://cf.10xgenomics.com/samples/cell-exp/1.1.0/jurkat/jurkat_filtered_gene_bc_matrices.tar.gz'
Content type 'application/x-tar' length 32962265 bytes (31.4 MB)
downloaded 31.4 MB

Feature names cannot have underscores ('_'), replacing with dashes ('-')
print(dim(raw_data_JURKAT))
[1] 32738  3258
print("JURKAT...OK!")
[1] "JURKAT...OK!"
if(!file.exists("./data/JURKAT_293T/raw_293T.loom")){
  temp <- tempfile()
  download.file("http://cf.10xgenomics.com/samples/cell-exp/1.1.0/293t/293t_filtered_gene_bc_matrices.tar.gz", temp)
  exdir = "./data/JURKAT_293T/original_data/293T"
  dir.create(exdir, showWarnings = F, recursive = T)
  untar(temp, exdir = exdir)
  unlink(temp)
  original_data <- Read10X(data.dir = paste0(exdir, "/filtered_matrices_mex/hg19/"))
  save_h5("./data/JURKAT_293T/original_293T.loom", as.matrix(t(original_data)))
  seurat_obj <- CreateSeuratObject(counts = as.data.frame(original_data), min.features = 500)
  cell_names_293T = colnames(seurat_obj[["RNA"]]@counts)
  gene_names_293T = rownames(original_data)
  raw_data_293T = as.matrix(original_data[, cell_names_293T])
  save_h5("./data/JURKAT_293T/raw_293T.loom", t(raw_data_293T))
}else{
  raw_data_293T = readh5_loom("./data/JURKAT_293T/raw_293T.loom")
  cell_names_293T = colnames(raw_data_293T)
  gene_names_293T = rownames(raw_data_293T)
}
试开URL’http://cf.10xgenomics.com/samples/cell-exp/1.1.0/293t/293t_filtered_gene_bc_matrices.tar.gz'
Content type 'application/x-tar' length 30756999 bytes (29.3 MB)
downloaded 29.3 MB

Feature names cannot have underscores ('_'), replacing with dashes ('-')
print(dim(raw_data_293T))
[1] 32738  2885
print("293T...OK!")
[1] "293T...OK!"
if(!file.exists("./data/JURKAT_293T/raw.loom")){
  gene_names_JURKAT_293T = unique(c(gene_names_JURKAT, gene_names_293T))
  raw_data_JURKAT_293T = matrix(0, nrow = length(gene_names_JURKAT_293T), ncol = ncol(raw_data_JURKAT) + ncol(raw_data_293T), dimnames = list(gene_names_JURKAT_293T, c(paste0("JURKAT_", cell_names_JURKAT), paste0("293T_", cell_names_293T))))
  raw_data_JURKAT_293T[gene_names_JURKAT, paste0("JURKAT_", cell_names_JURKAT)] = raw_data_JURKAT
  raw_data_JURKAT_293T[gene_names_293T,  paste0("293T_", cell_names_293T)] = raw_data_293T
  save_h5("./data/JURKAT_293T/raw.loom", t(raw_data_JURKAT_293T))
}else{
  raw_data_JURKAT_293T = readh5_loom("./data/JURKAT_293T/raw.loom")
}
print(dim(raw_data_JURKAT_293T))
[1] 32738  6143
print("JURKAT_293T...OK!")
[1] "JURKAT_293T...OK!"

The bulk data can be found at https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE129240.
The FPKMs are extracted from GSE129240_RAW.tar. GSM3703348 Mixture 1: 100% HEK, 0% Jurkat
GSM3703349 Mixture 2: 100% HEK, 0% Jurkat
GSM3703350 Mixture 3: 80% HEK, 20% Jurkat
GSM3703351 Mixture 4: 80% HEK, 20% Jurkat
GSM3703352 Mixture 5: 60% HEK, 40% Jurkat
GSM3703353 Mixture 6: 60% HEK, 40% Jurkat
GSM3703354 Mixture 7: 50% HEK, 50% Jurkat
GSM3703355 Mixture 8: 50% HEK, 50% Jurkat
GSM3703356 Mixture 9: 40% HEK, 60% Jurkat
GSM3703357 Mixture 10: 40% HEK, 60% Jurkat
GSM3703358 Mixture 11: 20% HEK, 80% Jurkat
GSM3703359 Mixture 12: 20% HEK, 80% Jurkat
GSM3703360 Mixture 13: 0% HEK, 100% Jurkat
GSM3703361 Mixture 14: 0% HEK, 100% Jurkat

if(!file.exists("./data/JURKAT_293T/bulk_fpkm.loom") | !file.exists("./data/JURKAT_293T/bulk.loom")){
  s1_293t = read.table("./data/JURKAT_293T/original_data/bulk/GSE129240_RAW/GSM3703348_AS_2_ATAGCGTC.genes.results.txt.gz", header = T, row.names = 1, sep = "\t")[, c("FPKM", "expected_count")]
  s2_293t = read.table("./data/JURKAT_293T/original_data/bulk/GSE129240_RAW/GSM3703349_AS_2_CGTTACCA.genes.results.txt.gz", header = T, row.names = 1, sep = "\t")[, c("FPKM", "expected_count")]
  s1_jurkat = read.table("./data/JURKAT_293T/original_data/bulk/GSE129240_RAW/GSM3703360_AS_1_TTACGCAC.genes.results.txt.gz", header = T, row.names = 1, sep = "\t")[, c("FPKM", "expected_count")]
  s2_jurkat = read.table("./data/JURKAT_293T/original_data/bulk/GSE129240_RAW/GSM3703361_AS_1_ATTCTAGG.genes.results.txt.gz", header = T, row.names = 1, sep = "\t")[, c("FPKM", "expected_count")]
  gene_bulk_fpkm = as.matrix(data.frame(s1_293t = s1_293t[rownames(s1_293t), "FPKM"], s2_293t = s2_293t[rownames(s1_293t), "FPKM"], s1_jurkat = s1_jurkat[rownames(s1_293t), "FPKM"], s2_jurkat = s2_jurkat[rownames(s1_293t), "FPKM"], row.names = rownames(s1_293t)))
  gene_bulk_mat = as.matrix(data.frame(s1_293t = s1_293t[rownames(s1_293t), "expected_count"], s2_293t = s2_293t[rownames(s1_293t), "expected_count"], s1_jurkat = s1_jurkat[rownames(s1_293t), "expected_count"], s2_jurkat = s2_jurkat[rownames(s1_293t), "expected_count"], row.names = rownames(s1_293t)))
  gz_path = "./data/annotation/Homo_sapiens.GRCh38.100.gtf.gz"
  annotation_mat = get_map(gz_path)
  mapping = annotation_mat$gene_name
  names(mapping) = annotation_mat$gene_id
  gene_name = mapping[sapply(rownames(gene_bulk_mat), function(x){
    strsplit(x, ".", fixed = T)[[1]][1]
  })]
  rownames(gene_bulk_fpkm) = gene_name
  rownames(gene_bulk_mat) = gene_name
  gene_bulk_fpkm = gene_bulk_fpkm[!is.na(gene_name), ]
  gene_bulk_mat = gene_bulk_mat[!is.na(gene_name), ]
  gene_name_used = rownames(gene_bulk_mat)
  used_index = sapply(unique(gene_name_used), function(x){
    this_index = which(gene_name_used == x)
    if(length(this_index) > 1){
      return(this_index[which.max(rowSums(gene_bulk_mat[this_index, ]))])
    }else{
      return(this_index)
    }
  })
  gene_bulk_fpkm = gene_bulk_fpkm[used_index, ]
  gene_bulk_mat = gene_bulk_mat[used_index, ]
  save_h5("./data/JURKAT_293T/bulk_fpkm.loom", as.matrix(t(gene_bulk_fpkm)))
  save_h5("./data/JURKAT_293T/bulk.loom", as.matrix(t(gene_bulk_mat)))
}else{
  gene_bulk_mat = readh5_loom("./data/JURKAT_293T/bulk.loom")
  gene_bulk_fpkm = readh5_loom("./data/JURKAT_293T/bulk_fpkm.loom")
}
Treat input as file
|--------------------------------------------------|
|==================================================|
print(dim(gene_bulk_mat))
[1] 58011     4
print(dim(gene_bulk_fpkm))
[1] 58011     4
print("JURKAT_293T_Bulk...OK!")
[1] "JURKAT_293T_Bulk...OK!"
LS0tDQp0aXRsZTogIkRhdGEgcHJlcGFyYXRpb24gZm9yIEpVUktBVF8yOTNUIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCiMjIyBTZXR1cCBrbml0ciBhbmQgbG9hZCB1dGlsaXR5IGZ1bmN0aW9ucw0KYGBge3Igc2V0dXB9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpcj0iRTovRElTQy9yZXByb2R1Y2liaWxpdHkiKQ0KYGBgDQpgYGB7cn0NCnV0aWxpdGllc19wYXRoID0gIi4vc291cmNlL3V0aWxpdGllcy5yIg0Kc291cmNlKHV0aWxpdGllc19wYXRoKQ0KYGBgDQojIyMgR2VuZXJhdGUgZGF0YSBmaWxlDQpUaGUgb3JpZ2luYWwgSlVSS0FUIGRhdGEgY2FuIGJlIGZvdW5kIGF0IGh0dHBzOi8vc3VwcG9ydC4xMHhnZW5vbWljcy5jb20vc2luZ2xlLWNlbGwtZ2VuZS1leHByZXNzaW9uL2RhdGFzZXRzLzEuMS4wL2p1cmthdC4gPC9icj4NClRoZSBvcmlnaW5hbCAyOTNUIGRhdGEgY2FuIGJlIGZvdW5kIGF0IGh0dHBzOi8vc3VwcG9ydC4xMHhnZW5vbWljcy5jb20vc2luZ2xlLWNlbGwtZ2VuZS1leHByZXNzaW9uL2RhdGFzZXRzLzEuMS4wLzI5M3QuIDwvYnI+DQpXZSBmaWx0ZXIgY2VsbHMgZm9sbG93aW5nIHRoaXMgcGFwZXIgKGh0dHBzOi8vd3d3LmJpb3J4aXYub3JnL2NvbnRlbnQvMTAuMTEwMS8yMDIwLjAxLjI5LjkyNTk3NHYxKS4NCmBgYHtyfQ0KaWYoIWZpbGUuZXhpc3RzKCIuL2RhdGEvSlVSS0FUXzI5M1QvcmF3X0p1cmthdC5sb29tIikpew0KICB0ZW1wIDwtIHRlbXBmaWxlKCkNCiAgZG93bmxvYWQuZmlsZSgiaHR0cDovL2NmLjEweGdlbm9taWNzLmNvbS9zYW1wbGVzL2NlbGwtZXhwLzEuMS4wL2p1cmthdC9qdXJrYXRfZmlsdGVyZWRfZ2VuZV9iY19tYXRyaWNlcy50YXIuZ3oiLCB0ZW1wKQ0KICBleGRpciA9ICIuL2RhdGEvSlVSS0FUXzI5M1Qvb3JpZ2luYWxfZGF0YS9KdXJrYXQiDQogIGRpci5jcmVhdGUoZXhkaXIsIHNob3dXYXJuaW5ncyA9IEYsIHJlY3Vyc2l2ZSA9IFQpDQogIHVudGFyKHRlbXAsIGV4ZGlyID0gZXhkaXIpDQogIHVubGluayh0ZW1wKQ0KICBvcmlnaW5hbF9kYXRhIDwtIFJlYWQxMFgoZGF0YS5kaXIgPSBwYXN0ZTAoZXhkaXIsICIvZmlsdGVyZWRfbWF0cmljZXNfbWV4L2hnMTkvIikpDQogIHNhdmVfaDUoIi4vZGF0YS9KVVJLQVRfMjkzVC9vcmlnaW5hbF9KdXJrYXQubG9vbSIsIGFzLm1hdHJpeCh0KG9yaWdpbmFsX2RhdGEpKSkNCiAgc2V1cmF0X29iaiA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gYXMuZGF0YS5mcmFtZShvcmlnaW5hbF9kYXRhKSwgbWluLmZlYXR1cmVzID0gNTAwKQ0KICBjZWxsX25hbWVzX0pVUktBVCA9IGNvbG5hbWVzKHNldXJhdF9vYmpbWyJSTkEiXV1AY291bnRzKQ0KICBnZW5lX25hbWVzX0pVUktBVCA9IHJvd25hbWVzKG9yaWdpbmFsX2RhdGEpDQogIHJhd19kYXRhX0pVUktBVCA9IGFzLm1hdHJpeChvcmlnaW5hbF9kYXRhWywgY2VsbF9uYW1lc19KVVJLQVRdKQ0KICBzYXZlX2g1KCIuL2RhdGEvSlVSS0FUXzI5M1QvcmF3X0p1cmthdC5sb29tIiwgdChyYXdfZGF0YV9KVVJLQVQpKQ0KfWVsc2V7DQogIHJhd19kYXRhX0pVUktBVCA9IHJlYWRoNV9sb29tKCIuL2RhdGEvSlVSS0FUXzI5M1QvcmF3X0p1cmthdC5sb29tIikNCiAgY2VsbF9uYW1lc19KVVJLQVQgPSBjb2xuYW1lcyhyYXdfZGF0YV9KVVJLQVQpDQogIGdlbmVfbmFtZXNfSlVSS0FUID0gcm93bmFtZXMocmF3X2RhdGFfSlVSS0FUKQ0KfQ0KcHJpbnQoZGltKHJhd19kYXRhX0pVUktBVCkpDQpwcmludCgiSlVSS0FULi4uT0shIikNCmBgYA0KYGBge3J9DQppZighZmlsZS5leGlzdHMoIi4vZGF0YS9KVVJLQVRfMjkzVC9yYXdfMjkzVC5sb29tIikpew0KICB0ZW1wIDwtIHRlbXBmaWxlKCkNCiAgZG93bmxvYWQuZmlsZSgiaHR0cDovL2NmLjEweGdlbm9taWNzLmNvbS9zYW1wbGVzL2NlbGwtZXhwLzEuMS4wLzI5M3QvMjkzdF9maWx0ZXJlZF9nZW5lX2JjX21hdHJpY2VzLnRhci5neiIsIHRlbXApDQogIGV4ZGlyID0gIi4vZGF0YS9KVVJLQVRfMjkzVC9vcmlnaW5hbF9kYXRhLzI5M1QiDQogIGRpci5jcmVhdGUoZXhkaXIsIHNob3dXYXJuaW5ncyA9IEYsIHJlY3Vyc2l2ZSA9IFQpDQogIHVudGFyKHRlbXAsIGV4ZGlyID0gZXhkaXIpDQogIHVubGluayh0ZW1wKQ0KICBvcmlnaW5hbF9kYXRhIDwtIFJlYWQxMFgoZGF0YS5kaXIgPSBwYXN0ZTAoZXhkaXIsICIvZmlsdGVyZWRfbWF0cmljZXNfbWV4L2hnMTkvIikpDQogIHNhdmVfaDUoIi4vZGF0YS9KVVJLQVRfMjkzVC9vcmlnaW5hbF8yOTNULmxvb20iLCBhcy5tYXRyaXgodChvcmlnaW5hbF9kYXRhKSkpDQogIHNldXJhdF9vYmogPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGFzLmRhdGEuZnJhbWUob3JpZ2luYWxfZGF0YSksIG1pbi5mZWF0dXJlcyA9IDUwMCkNCiAgY2VsbF9uYW1lc18yOTNUID0gY29sbmFtZXMoc2V1cmF0X29ialtbIlJOQSJdXUBjb3VudHMpDQogIGdlbmVfbmFtZXNfMjkzVCA9IHJvd25hbWVzKG9yaWdpbmFsX2RhdGEpDQogIHJhd19kYXRhXzI5M1QgPSBhcy5tYXRyaXgob3JpZ2luYWxfZGF0YVssIGNlbGxfbmFtZXNfMjkzVF0pDQogIHNhdmVfaDUoIi4vZGF0YS9KVVJLQVRfMjkzVC9yYXdfMjkzVC5sb29tIiwgdChyYXdfZGF0YV8yOTNUKSkNCn1lbHNlew0KICByYXdfZGF0YV8yOTNUID0gcmVhZGg1X2xvb20oIi4vZGF0YS9KVVJLQVRfMjkzVC9yYXdfMjkzVC5sb29tIikNCiAgY2VsbF9uYW1lc18yOTNUID0gY29sbmFtZXMocmF3X2RhdGFfMjkzVCkNCiAgZ2VuZV9uYW1lc18yOTNUID0gcm93bmFtZXMocmF3X2RhdGFfMjkzVCkNCn0NCnByaW50KGRpbShyYXdfZGF0YV8yOTNUKSkNCnByaW50KCIyOTNULi4uT0shIikNCmBgYA0KYGBge3J9DQppZighZmlsZS5leGlzdHMoIi4vZGF0YS9KVVJLQVRfMjkzVC9yYXcubG9vbSIpKXsNCiAgZ2VuZV9uYW1lc19KVVJLQVRfMjkzVCA9IHVuaXF1ZShjKGdlbmVfbmFtZXNfSlVSS0FULCBnZW5lX25hbWVzXzI5M1QpKQ0KICByYXdfZGF0YV9KVVJLQVRfMjkzVCA9IG1hdHJpeCgwLCBucm93ID0gbGVuZ3RoKGdlbmVfbmFtZXNfSlVSS0FUXzI5M1QpLCBuY29sID0gbmNvbChyYXdfZGF0YV9KVVJLQVQpICsgbmNvbChyYXdfZGF0YV8yOTNUKSwgZGltbmFtZXMgPSBsaXN0KGdlbmVfbmFtZXNfSlVSS0FUXzI5M1QsIGMocGFzdGUwKCJKVVJLQVRfIiwgY2VsbF9uYW1lc19KVVJLQVQpLCBwYXN0ZTAoIjI5M1RfIiwgY2VsbF9uYW1lc18yOTNUKSkpKQ0KICByYXdfZGF0YV9KVVJLQVRfMjkzVFtnZW5lX25hbWVzX0pVUktBVCwgcGFzdGUwKCJKVVJLQVRfIiwgY2VsbF9uYW1lc19KVVJLQVQpXSA9IHJhd19kYXRhX0pVUktBVA0KICByYXdfZGF0YV9KVVJLQVRfMjkzVFtnZW5lX25hbWVzXzI5M1QsICBwYXN0ZTAoIjI5M1RfIiwgY2VsbF9uYW1lc18yOTNUKV0gPSByYXdfZGF0YV8yOTNUDQogIHNhdmVfaDUoIi4vZGF0YS9KVVJLQVRfMjkzVC9yYXcubG9vbSIsIHQocmF3X2RhdGFfSlVSS0FUXzI5M1QpKQ0KfWVsc2V7DQogIHJhd19kYXRhX0pVUktBVF8yOTNUID0gcmVhZGg1X2xvb20oIi4vZGF0YS9KVVJLQVRfMjkzVC9yYXcubG9vbSIpDQp9DQpwcmludChkaW0ocmF3X2RhdGFfSlVSS0FUXzI5M1QpKQ0KcHJpbnQoIkpVUktBVF8yOTNULi4uT0shIikNCmBgYA0KVGhlIGJ1bGsgZGF0YSBjYW4gYmUgZm91bmQgYXQgaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vcXVlcnkvYWNjLmNnaT9hY2M9R1NFMTI5MjQwLiA8L2JyPg0KVGhlIEZQS01zIGFyZSBleHRyYWN0ZWQgZnJvbSBHU0UxMjkyNDBfUkFXLnRhci4NCkdTTTM3MDMzNDgJTWl4dHVyZSAxOiAxMDAlIEhFSywgMCUgSnVya2F0IDwvYnI+DQpHU00zNzAzMzQ5CU1peHR1cmUgMjogMTAwJSBIRUssIDAlIEp1cmthdCA8L2JyPg0KR1NNMzcwMzM1MAlNaXh0dXJlIDM6IDgwJSBIRUssIDIwJSBKdXJrYXQgPC9icj4NCkdTTTM3MDMzNTEJTWl4dHVyZSA0OiA4MCUgSEVLLCAyMCUgSnVya2F0IDwvYnI+DQpHU00zNzAzMzUyCU1peHR1cmUgNTogNjAlIEhFSywgNDAlIEp1cmthdCA8L2JyPg0KR1NNMzcwMzM1MwlNaXh0dXJlIDY6IDYwJSBIRUssIDQwJSBKdXJrYXQgPC9icj4NCkdTTTM3MDMzNTQJTWl4dHVyZSA3OiA1MCUgSEVLLCA1MCUgSnVya2F0IDwvYnI+DQpHU00zNzAzMzU1CU1peHR1cmUgODogNTAlIEhFSywgNTAlIEp1cmthdCA8L2JyPg0KR1NNMzcwMzM1NglNaXh0dXJlIDk6IDQwJSBIRUssIDYwJSBKdXJrYXQgPC9icj4NCkdTTTM3MDMzNTcJTWl4dHVyZSAxMDogNDAlIEhFSywgNjAlIEp1cmthdCA8L2JyPg0KR1NNMzcwMzM1OAlNaXh0dXJlIDExOiAyMCUgSEVLLCA4MCUgSnVya2F0IDwvYnI+DQpHU00zNzAzMzU5CU1peHR1cmUgMTI6IDIwJSBIRUssIDgwJSBKdXJrYXQgPC9icj4NCkdTTTM3MDMzNjAJTWl4dHVyZSAxMzogMCUgSEVLLCAxMDAlIEp1cmthdCA8L2JyPg0KR1NNMzcwMzM2MQlNaXh0dXJlIDE0OiAwJSBIRUssIDEwMCUgSnVya2F0IA0KYGBge3J9DQppZighZmlsZS5leGlzdHMoIi4vZGF0YS9KVVJLQVRfMjkzVC9idWxrX2Zwa20ubG9vbSIpIHwgIWZpbGUuZXhpc3RzKCIuL2RhdGEvSlVSS0FUXzI5M1QvYnVsay5sb29tIikpew0KICBzMV8yOTN0ID0gcmVhZC50YWJsZSgiLi9kYXRhL0pVUktBVF8yOTNUL29yaWdpbmFsX2RhdGEvYnVsay9HU0UxMjkyNDBfUkFXL0dTTTM3MDMzNDhfQVNfMl9BVEFHQ0dUQy5nZW5lcy5yZXN1bHRzLnR4dC5neiIsIGhlYWRlciA9IFQsIHJvdy5uYW1lcyA9IDEsIHNlcCA9ICJcdCIpWywgYygiRlBLTSIsICJleHBlY3RlZF9jb3VudCIpXQ0KICBzMl8yOTN0ID0gcmVhZC50YWJsZSgiLi9kYXRhL0pVUktBVF8yOTNUL29yaWdpbmFsX2RhdGEvYnVsay9HU0UxMjkyNDBfUkFXL0dTTTM3MDMzNDlfQVNfMl9DR1RUQUNDQS5nZW5lcy5yZXN1bHRzLnR4dC5neiIsIGhlYWRlciA9IFQsIHJvdy5uYW1lcyA9IDEsIHNlcCA9ICJcdCIpWywgYygiRlBLTSIsICJleHBlY3RlZF9jb3VudCIpXQ0KICBzMV9qdXJrYXQgPSByZWFkLnRhYmxlKCIuL2RhdGEvSlVSS0FUXzI5M1Qvb3JpZ2luYWxfZGF0YS9idWxrL0dTRTEyOTI0MF9SQVcvR1NNMzcwMzM2MF9BU18xX1RUQUNHQ0FDLmdlbmVzLnJlc3VsdHMudHh0Lmd6IiwgaGVhZGVyID0gVCwgcm93Lm5hbWVzID0gMSwgc2VwID0gIlx0IilbLCBjKCJGUEtNIiwgImV4cGVjdGVkX2NvdW50IildDQogIHMyX2p1cmthdCA9IHJlYWQudGFibGUoIi4vZGF0YS9KVVJLQVRfMjkzVC9vcmlnaW5hbF9kYXRhL2J1bGsvR1NFMTI5MjQwX1JBVy9HU00zNzAzMzYxX0FTXzFfQVRUQ1RBR0cuZ2VuZXMucmVzdWx0cy50eHQuZ3oiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxLCBzZXAgPSAiXHQiKVssIGMoIkZQS00iLCAiZXhwZWN0ZWRfY291bnQiKV0NCiAgZ2VuZV9idWxrX2Zwa20gPSBhcy5tYXRyaXgoZGF0YS5mcmFtZShzMV8yOTN0ID0gczFfMjkzdFtyb3duYW1lcyhzMV8yOTN0KSwgIkZQS00iXSwgczJfMjkzdCA9IHMyXzI5M3Rbcm93bmFtZXMoczFfMjkzdCksICJGUEtNIl0sIHMxX2p1cmthdCA9IHMxX2p1cmthdFtyb3duYW1lcyhzMV8yOTN0KSwgIkZQS00iXSwgczJfanVya2F0ID0gczJfanVya2F0W3Jvd25hbWVzKHMxXzI5M3QpLCAiRlBLTSJdLCByb3cubmFtZXMgPSByb3duYW1lcyhzMV8yOTN0KSkpDQogIGdlbmVfYnVsa19tYXQgPSBhcy5tYXRyaXgoZGF0YS5mcmFtZShzMV8yOTN0ID0gczFfMjkzdFtyb3duYW1lcyhzMV8yOTN0KSwgImV4cGVjdGVkX2NvdW50Il0sIHMyXzI5M3QgPSBzMl8yOTN0W3Jvd25hbWVzKHMxXzI5M3QpLCAiZXhwZWN0ZWRfY291bnQiXSwgczFfanVya2F0ID0gczFfanVya2F0W3Jvd25hbWVzKHMxXzI5M3QpLCAiZXhwZWN0ZWRfY291bnQiXSwgczJfanVya2F0ID0gczJfanVya2F0W3Jvd25hbWVzKHMxXzI5M3QpLCAiZXhwZWN0ZWRfY291bnQiXSwgcm93Lm5hbWVzID0gcm93bmFtZXMoczFfMjkzdCkpKQ0KICBnel9wYXRoID0gIi4vZGF0YS9hbm5vdGF0aW9uL0hvbW9fc2FwaWVucy5HUkNoMzguMTAwLmd0Zi5neiINCiAgYW5ub3RhdGlvbl9tYXQgPSBnZXRfbWFwKGd6X3BhdGgpDQogIG1hcHBpbmcgPSBhbm5vdGF0aW9uX21hdCRnZW5lX25hbWUNCiAgbmFtZXMobWFwcGluZykgPSBhbm5vdGF0aW9uX21hdCRnZW5lX2lkDQogIGdlbmVfbmFtZSA9IG1hcHBpbmdbc2FwcGx5KHJvd25hbWVzKGdlbmVfYnVsa19tYXQpLCBmdW5jdGlvbih4KXsNCiAgICBzdHJzcGxpdCh4LCAiLiIsIGZpeGVkID0gVClbWzFdXVsxXQ0KICB9KV0NCiAgcm93bmFtZXMoZ2VuZV9idWxrX2Zwa20pID0gZ2VuZV9uYW1lDQogIHJvd25hbWVzKGdlbmVfYnVsa19tYXQpID0gZ2VuZV9uYW1lDQogIGdlbmVfYnVsa19mcGttID0gZ2VuZV9idWxrX2Zwa21bIWlzLm5hKGdlbmVfbmFtZSksIF0NCiAgZ2VuZV9idWxrX21hdCA9IGdlbmVfYnVsa19tYXRbIWlzLm5hKGdlbmVfbmFtZSksIF0NCiAgZ2VuZV9uYW1lX3VzZWQgPSByb3duYW1lcyhnZW5lX2J1bGtfbWF0KQ0KICB1c2VkX2luZGV4ID0gc2FwcGx5KHVuaXF1ZShnZW5lX25hbWVfdXNlZCksIGZ1bmN0aW9uKHgpew0KICAgIHRoaXNfaW5kZXggPSB3aGljaChnZW5lX25hbWVfdXNlZCA9PSB4KQ0KICAgIGlmKGxlbmd0aCh0aGlzX2luZGV4KSA+IDEpew0KICAgICAgcmV0dXJuKHRoaXNfaW5kZXhbd2hpY2gubWF4KHJvd1N1bXMoZ2VuZV9idWxrX21hdFt0aGlzX2luZGV4LCBdKSldKQ0KICAgIH1lbHNlew0KICAgICAgcmV0dXJuKHRoaXNfaW5kZXgpDQogICAgfQ0KICB9KQ0KICBnZW5lX2J1bGtfZnBrbSA9IGdlbmVfYnVsa19mcGttW3VzZWRfaW5kZXgsIF0NCiAgZ2VuZV9idWxrX21hdCA9IGdlbmVfYnVsa19tYXRbdXNlZF9pbmRleCwgXQ0KICBzYXZlX2g1KCIuL2RhdGEvSlVSS0FUXzI5M1QvYnVsa19mcGttLmxvb20iLCBhcy5tYXRyaXgodChnZW5lX2J1bGtfZnBrbSkpKQ0KICBzYXZlX2g1KCIuL2RhdGEvSlVSS0FUXzI5M1QvYnVsay5sb29tIiwgYXMubWF0cml4KHQoZ2VuZV9idWxrX21hdCkpKQ0KfWVsc2V7DQogIGdlbmVfYnVsa19tYXQgPSByZWFkaDVfbG9vbSgiLi9kYXRhL0pVUktBVF8yOTNUL2J1bGsubG9vbSIpDQogIGdlbmVfYnVsa19mcGttID0gcmVhZGg1X2xvb20oIi4vZGF0YS9KVVJLQVRfMjkzVC9idWxrX2Zwa20ubG9vbSIpDQp9DQpwcmludChkaW0oZ2VuZV9idWxrX21hdCkpDQpwcmludChkaW0oZ2VuZV9idWxrX2Zwa20pKQ0KcHJpbnQoIkpVUktBVF8yOTNUX0J1bGsuLi5PSyEiKQ0KDQpgYGANCg0KDQo=