Modern high-throughput datasets. Here we’ll analyze a 10x Chromium dataset using pagoda2.

library(pagoda2)

10x count matrix is normally split into three files. Let’s write a utility function to load it:

# set names euqal to the values
sn <- function(x) { names(x) <- x; return(x); }
# load 10x matrices from a named list of result folders
t.load.10x.data <- function(matrixPaths,n.cores=1) {
  require(parallel)
  require(Matrix)
  mclapply(sn(names(matrixPaths)),function(nam) {
    matrixPath <- matrixPaths[nam];
    # read all count files (*_unique.counts) under a given path
    #cat("loading data from ",matrixPath, " ");
    x <- as(readMM(paste(matrixPath,'matrix.mtx',sep='/')),'dgCMatrix'); # convert to the required sparse matrix representation
    cat(".")
    gs <- read.delim(paste(matrixPath,'genes.tsv',sep='/'),header=F)
    rownames(x) <- gs[,2]
    cat(".")
    gs <- read.delim(paste(matrixPath,'barcodes.tsv',sep='/'),header=F)
    colnames(x) <- gs[,1]
    cat(".")
    colnames(x) <- paste(nam,colnames(x),sep='_');
    x
  },mc.cores=n.cores)
}
n.cores <- 30;

Load (10x PBMC data)[https://support.10xgenomics.com/single-cell-gene-expression/datasets/pbmc8k]:

data.path <- "~/workshop_materials/transcriptomics/10x_pbmc8k"
data.path <- "/d0-mendel/home/pkharchenko/p2/walkthrough/pbmc8k/raw_gene_bc_matrices/GRCh38"
cd <- t.load.10x.data(list(PBMC8K=data.path))
Loading required package: parallel
Loading required package: Matrix
...
str(cd)
List of 1
 $ PBMC8K:Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
  .. ..@ i       : int [1:17171166] 1614 21625 110 153 231 484 548 550 633 673 ...
  .. ..@ p       : int [1:737281] 0 2 2 2 116 120 120 125 126 126 ...
  .. ..@ Dim     : int [1:2] 33694 737280
  .. ..@ Dimnames:List of 2
  .. .. ..$ : chr [1:33694] "RP11-34P13.3" "FAM138A" "OR4F5" "RP11-34P13.7" ...
  .. .. ..$ : chr [1:737280] "PBMC8K_AAACCTGAGAAACCAT-1" "PBMC8K_AAACCTGAGAAACCGC-1" "PBMC8K_AAACCTGAGAAACCTA-1" "PBMC8K_AAACCTGAGAAACGAG-1" ...
  .. ..@ x       : num [1:17171166] 1 1 1 1 1 2 1 1 1 1 ...
  .. ..@ factors : list()

Look at the summary counts

cd <- cd[[1]]
par(mfrow=c(1,2), mar = c(3.5,3.5,2.0,0.5), mgp = c(2,0.65,0), cex = 1.0)
hist(log10(colSums(cd)+1),main='reads per cell',col='wheat')
hist(log10(rowSums(cd)+1),main='reads per gene',col='wheat')

Define a quick cell filtering function based on cell depth vs. number of genes relationship:

# filter cells based on the gene/molecule dependency
t.filter.for.valid.cells <- function(countMatrix,min.cell.size=500, max.cell.size=5e4,p.level=min(1e-3,1/ncol(countMatrix)),alpha=0.1,do.par=T) {
  if(do.par) { par(mfrow=c(1,2), mar = c(3.5,3.5,2.0,0.5), mgp = c(2,0.65,0), cex = 1.0);}
  hist(log10(colSums(countMatrix)),col='wheat',xlab='log10[ molecules ]',main='') 
  # some of the cells are very large .. those can skew the analysis of more subtle populations (too much bias) .. letting them in here though
  
  abline(v=log10(c(min.cell.size,max.cell.size)),lty=2,col=2)
  # look at the number of genes vs. molecule size depenency
  df <- data.frame(molecules=colSums(countMatrix),genes=colSums(countMatrix>0)); 
  df <- df[df$molecules>=min.cell.size,];
  df <- log10(df);
  df <- df[order(df$molecules,decreasing=F),]
  plot(df,col=adjustcolor(1,alpha=alpha),cex=0.5,ylab='log10[ gene counts]',xlab='log10[ molecule counts]')
  abline(v=log10(c(min.cell.size,max.cell.size)),lty=2,col=2)
  #abline(lm(genes ~ molecules, data=df),col=4)
  require(MASS)  
  m <- rlm(genes~molecules,data=df)
  suppressWarnings(pb <- data.frame(predict(m,interval='prediction',level = 1-p.level,type="response")))
  polygon(c(df$molecules,rev(df$molecules)),c(pb$lwr,rev(pb$upr)),col=adjustcolor(2,alpha=0.1),border = NA)
  outliers <- rownames(df)[df$genes > pb$upr | df$genes < pb$lwr];
  points(df[outliers,],col=2,cex=0.6)
  # set of filtered cells to move forward with  
  valid.cells <- colSums(countMatrix)>min.cell.size & colSums(countMatrix)<max.cell.size & !(colnames(countMatrix) %in% outliers)
  countMatrix[,valid.cells,drop=F]
}

Run the filtering procedure:

counts <- t.filter.for.valid.cells(cd,min.cell.size=500)

str(counts)
Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
  ..@ i       : int [1:11965872] 33 45 59 153 335 375 408 440 484 495 ...
  ..@ p       : int [1:8787] 0 871 1677 2993 3891 5417 6912 8165 9598 11230 ...
  ..@ Dim     : int [1:2] 33694 8786
  ..@ Dimnames:List of 2
  .. ..$ : chr [1:33694] "RP11-34P13.3" "FAM138A" "OR4F5" "RP11-34P13.7" ...
  .. ..$ : chr [1:8786] "PBMC8K_AAACCTGAGCATCATC-1" "PBMC8K_AAACCTGAGCTAACTC-1" "PBMC8K_AAACCTGAGCTAGTGG-1" "PBMC8K_AAACCTGCACATTAGC-1" ...
  ..@ x       : num [1:11965872] 1 1 1 5 1 1 1 1 18 1 ...
  ..@ factors : list()

We can also looka at the number of molecules per gene, and omit low-expressed genes to save computational time:

hist(log10(rowSums(counts)+1),main='Molecules per gene',xlab='molecules (log10)',col='wheat')
abline(v=1,lty=2,col=2)

Now we have a clean/lean count matrix and are ready to start analysis. First we’ll create pagoda2 object that will maintain all of the results. It will also provide handles for running all operations on the data.

r <- Pagoda2$new(counts,log.scale=FALSE)
Error in setCountMatrix(x, min.cells.per.gene = min.cells.per.gene, trim = trim,  : 
  duplicate gene names are not allowed - please reduce

Yes, we need to make gene names unique:

rownames(counts) <- make.unique(rownames(counts))
r <- Pagoda2$new(counts,log.scale=FALSE)
8786 cells, 13004 genes; normalizing ... using plain model winsorizing ... done.

Next, we’ll adjust the variance, to normalize the extent to which genes with (very) different expression magnitudes will contribute to the downstream anlaysis:

r$adjustVariance(plot=T,gam.k=10)
calculating variance fit ... using gam 
Loading required package: mgcv
Loading required package: nlme
This is mgcv 1.8-18. For overview type 'help("mgcv-package")'.
743 overdispersed genes ... 743 persisting ... done.

There are many alternative ways of proceeding with the downstream analysis. Below we’ll use the simplest, default scenario, where we first reduce the dataset dimensions by running PCA, and then move into k-nearest neighbor graph space for clustering and visualization calculations. First, the PCA reduction:

r$calculatePcaReduction(nPcs=100,n.odgenes=3e3,maxit = 200)
Loading required package: irlba

Clustering, visualization and many other procedures can take advantage of a cell kNN graph. Let’s calculate it.

Loading required package: igraph

Attaching package: ‘igraph’

The following objects are masked from ‘package:stats’:

    decompose, spectrum

The following object is masked from ‘package:base’:

    union

2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: dummy distance type: INT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: dummy distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: dummy distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: bit_hamming distance type: INT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: leven distance type: INT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: normleven distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: kldivfast distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: kldivfast distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: kldivfastrq distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: kldivfastrq distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: kldivgenfast distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: kldivgenfast distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: kldivgenslow distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: kldivgenslow distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: kldivgenfastrq distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: kldivgenfastrq distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: itakurasaitofast distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: itakurasaitofast distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsdivslow distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsdivslow distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsdivfast distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsdivfast distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsdivfastapprox distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsdivfastapprox distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsmetrslow distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsmetrslow distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsmetrfast distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsmetrfast distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsmetrfastapprox distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: jsmetrfastapprox distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: word_embed distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: word_embed distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: lp distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: lp distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: linf distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: linf distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: l1 distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: l1 distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: l2 distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: l2 distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: cosinesimil distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: cosinesimil distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: angulardist distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: angulardist distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: lp_sparse distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: lp_sparse distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: linf_sparse distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: linf_sparse distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: l1_sparse distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: l1_sparse distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: l2_sparse distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: l2_sparse distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: cosinesimil_sparse distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: cosinesimil_sparse distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: angulardist_sparse distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: angulardist_sparse distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: negdotprod_sparse distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: querynorm_negdotprod_sparse distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: cosinesimil_sparse_fast distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: angulardist_sparse_fast distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: negdotprod_sparse_fast distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: querynorm_negdotprod_sparse_fast distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: savch distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: sqfd_heuristic_func distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: sqfd_heuristic_func distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: sqfd_minus_func distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: sqfd_minus_func distance type: DOUBLE
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: sqfd_gaussian_func distance type: FLOAT
2017-09-14 12:48:34 spacefactory.h:44 (Register) [INFO] Registering at the factory, space: sqfd_gaussian_func distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: dummy distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: dummy distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: dummy distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: bbtree distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: bbtree distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: ghtree distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: ghtree distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: ghtree distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: list_clusters distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: list_clusters distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: list_clusters distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: lsh_cauchy distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: lsh_gaussian distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: lsh_threshold distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: mplsh distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: lsh_multiprobe distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: mvptree distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: mvptree distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: mvptree distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_bin_vptree distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_bin_vptree distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_bin_vptree distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_incsort_bin distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_incsort_bin distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_incsort_bin distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_bin_incsort distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_bin_incsort distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_bin_incsort distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_lsh_bin distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_lsh_bin distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_lsh_bin distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_inv_indx distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_inv_indx distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_inv_indx distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: mi-file distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: mi-file distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: mi-file distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_prefix distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_prefix distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: perm_prefix distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: pp-index distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: pp-index distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: pp-index distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: pivot_neighb_invindx distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: pivot_neighb_invindx distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: pivot_neighb_invindx distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: napp distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: napp distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: napp distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: omedrank distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: omedrank distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: omedrank distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: proj_vptree distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: proj_vptree distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: proj_vptree distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: proj_incsort distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: proj_incsort distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: proj_incsort distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: brute_force distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: brute_force distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: brute_force distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: seq_search distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: seq_search distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: seq_search distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: sw-graph distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: sw-graph distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: sw-graph distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: small_world_rand distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: small_world_rand distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: small_world_rand distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: hnsw distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: hnsw distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: hnsw distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: sw-graph-split distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: sw-graph-split distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: sw-graph-split distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: nndes distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: nndes distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: nndes distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: satree distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: satree distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: satree distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: vptree distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: vptree distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: vptree distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: mult_index distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: mult_index distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: mult_index distance type: INT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: nonmetr_list_clust distance type: FLOAT
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: nonmetr_list_clust distance type: DOUBLE
2017-09-14 12:48:34 methodfactory.h:49 (Register) [INFO] Registering at the factory, method: nonmetr_list_clust distance type: INT
reading points ... done (8786 points)
building the index ... 
2017-09-14 12:48:34 hnsw.cc:160 (CreateIndex) [INFO] M                   = 20
2017-09-14 12:48:34 hnsw.cc:161 (CreateIndex) [INFO] indexThreadQty      = 30
2017-09-14 12:48:34 hnsw.cc:162 (CreateIndex) [INFO] efConstruction      = 100
2017-09-14 12:48:34 hnsw.cc:163 (CreateIndex) [INFO] maxM                     = 20
2017-09-14 12:48:34 hnsw.cc:164 (CreateIndex) [INFO] maxM0                    = 40
2017-09-14 12:48:34 hnsw.cc:166 (CreateIndex) [INFO] mult                = 0.333808
2017-09-14 12:48:34 hnsw.cc:167 (CreateIndex) [INFO] skip_optimized_index= 0
2017-09-14 12:48:34 hnsw.cc:168 (CreateIndex) [INFO] delaunay_type       = 2
2017-09-14 12:48:34 hnsw.cc:434 (SetQueryTimeParams) [INFO] Set HNSW query-time parameters:
2017-09-14 12:48:34 hnsw.cc:435 (SetQueryTimeParams) [INFO] ef(Search)         =20
2017-09-14 12:48:34 hnsw.cc:436 (SetQueryTimeParams) [INFO] algoType           =2

0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************2017-09-14 12:48:34 hnsw.cc:320 (CreateIndex) [INFO] 
The vectorspace is Cosine Similarity
2017-09-14 12:48:34 hnsw.cc:322 (CreateIndex) [INFO] Vector length=100
2017-09-14 12:48:34 hnsw.cc:325 (CreateIndex) [INFO] Thus using an optimised function for base 4
2017-09-14 12:48:34 hnsw.cc:347 (CreateIndex) [INFO] searchMethod             = 4
2017-09-14 12:48:34 hnsw.cc:358 (CreateIndex) [INFO] Making optimized index
2017-09-14 12:48:34 hnsw.cc:402 (CreateIndex) [INFO] Finished making optimized index
2017-09-14 12:48:34 hnsw.cc:403 (CreateIndex) [INFO] Maximum level = 2
2017-09-14 12:48:34 hnsw.cc:404 (CreateIndex) [INFO] Total memory allocated for optimized index+data: 4 Mb
2017-09-14 12:48:34 hnsw.cc:434 (SetQueryTimeParams) [INFO] Set HNSW query-time parameters:
2017-09-14 12:48:34 hnsw.cc:435 (SetQueryTimeParams) [INFO] ef(Search)         =100
2017-09-14 12:48:34 hnsw.cc:436 (SetQueryTimeParams) [INFO] algoType           =2

done (0.109416s)
running queries with k=40 ... 
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
***************************************************

done (0.575964s)
creating report data frame ... done

Determine clusters using “multilevel.community” algorithm.

r$getKnnClusters(method=multilevel.community,type='PCA')

Determine a largeVis embedding:

M <- 30; r$getEmbedding(type = 'PCA',embeddingType = 'largeVis', M = M,  perplexity = 30,  gamma = 1 / M,  alpha = 1)
Loading required package: largeVis
largeVis was compiled with 32-bit types. This will limit the size of the datasets it can process. Consider recompiling with -DARMA_64BIT_WORD
Estimating embeddings.
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|

Now we can visualize the embedding using the determined clusters:

r$plotEmbedding(type='PCA',show.legend=F,mark.clusters=T,min.group.size=50,shuffle.colors=F,mark.cluster.cex=1,alpha=0.1,main='clusters (lV)')

r$getEmbedding(type='PCA',embeddingType='tSNE',perplexity=50,verbose=F,n.cores=n.cores)
Loading required package: Rtsne
calculating distance ... pearson ... done
running tSNE using 30 cores:
r$plotEmbedding(type='PCA',embeddingType='tSNE',show.legend=F,mark.clusters=T,min.group.size=1,shuffle.colors=F,mark.cluster.cex=1,alpha=0.1,main='clusters (tSNE)')

Or load precalculated tSNE

tSNE <- readRDS("tSNE.rds")
r$embeddings$PCA$tSNE <- tSNE;

We can use the same plotEmbedding() function to show all kinds of other values. For instance, let’s look at depth, or an expresson pattern of a gene:

str(r$depth)
 Named num [1:8786] 2376 1659 4517 2779 4656 ...
 - attr(*, "names")= chr [1:8786] "PBMC8K_AAACCTGAGCATCATC-1" "PBMC8K_AAACCTGAGCTAACTC-1" "PBMC8K_AAACCTGAGCTAGTGG-1" "PBMC8K_AAACCTGCACATTAGC-1" ...
par(mfrow=c(1,2))
r$plotEmbedding(type='PCA',embeddingType='tSNE',show.legend=F,mark.clusters=T,min.group.size=1,shuffle.colors=F,mark.cluster.cex=1,alpha=0.1,main='clusters (tSNE)')
r$plotEmbedding(type='PCA',embeddingType='tSNE',colors=r$depth,shuffle.colors=F,mark.cluster.cex=1,alpha=0.1,main='depth')
treating colors as a gradient with zlim: 1440 8903.75 

Or expression of a given gene:

par(mfrow=c(1,2))
r$plotEmbedding(type='PCA',embeddingType='tSNE',show.legend=F,mark.clusters=T,min.group.size=1,shuffle.colors=F,mark.cluster.cex=1,alpha=0.1,main='clusters (tSNE)')
gene <-"LYZ"
r$plotEmbedding(type='PCA',embeddingType='tSNE',colors=r$counts[,gene],shuffle.colors=F,mark.cluster.cex=1,alpha=0.1,main=gene)
treating colors as a gradient with zlim: 0 16.79779 

We can generate multiple potential clusterings, with different names. Here we’ll use multilevel clustering:

r$getKnnClusters(method=infomap.community,type='PCA',name='infomap')
str(r$clusters)
List of 1
 $ PCA:List of 2
  ..$ community: Factor w/ 12 levels "1","2","3","4",..: 9 10 6 2 10 10 12 2 10 2 ...
  .. ..- attr(*, "names")= chr [1:8786] "PBMC8K_AAACCTGAGCATCATC-1" "PBMC8K_AAACCTGAGCTAACTC-1" "PBMC8K_AAACCTGAGCTAGTGG-1" "PBMC8K_AAACCTGCACATTAGC-1" ...
  ..$ infomap  : Factor w/ 25 levels "1","2","3","4",..: 4 6 5 2 6 3 15 2 24 2 ...
  .. ..- attr(*, "names")= chr [1:8786] "PBMC8K_AAACCTGAGCATCATC-1" "PBMC8K_AAACCTGAGCTAACTC-1" "PBMC8K_AAACCTGAGCTAGTGG-1" "PBMC8K_AAACCTGCACATTAGC-1" ...

Compare with infomap:

par(mfrow=c(1,2))
r$plotEmbedding(type='PCA',embeddingType='tSNE',groups=r$clusters$PCA$community,show.legend=F,mark.clusters=T,min.group.size=1,shuffle.colors=F,mark.cluster.cex=1,alpha=0.1,main='multilevel clusters (tSNE)')
using provided groups as a factor
r$plotEmbedding(type='PCA',embeddingType='tSNE',clusterType='infomap',show.legend=F,mark.clusters=T,min.group.size=1,shuffle.colors=F,mark.cluster.cex=1,alpha=0.1,main='infomap clusters (tSNE)')

You can try other cluster methods (e.g. walktrap.community), or change k on the gene kNN graph.

Run differential expression on the infomap clusters:

r$getDifferentialGenes(type='PCA',verbose=T,clusterType='community')
running differential expression with  12  clusters ... adjusting p-values ... done.

Visualize top genes:

names(r$diffgenes)
[1] "PCA"
de <- r$diffgenes$PCA[[1]][['4']];
r$plotGeneHeatmap(genes=rownames(de)[1:15],groups=r$clusters$PCA[[1]])

Spot-check a gene:

gene <-"IGHM"
r$plotEmbedding(type='PCA',embeddingType='tSNE',colors=r$counts[,gene],shuffle.colors=F,mark.cluster.cex=1,alpha=0.1,main=gene)
treating colors as a gradient with zlim: 0 1.940716 

Pathway overdispersion analysis (a la PAGODA1)

First, build GO->gene environment:

suppressMessages(library(org.Hs.eg.db))
# translate gene names to ids
ids <- unlist(lapply(mget(colnames(r$counts),org.Hs.egALIAS2EG,ifnotfound=NA),function(x) x[1]))
# reverse map
rids <- names(ids); names(rids) <- ids;
# list all the ids per GO category
go.env <- list2env(eapply(org.Hs.egGO2ALLEGS,function(x) as.character(na.omit(rids[x]))))

Now run overdispersion anlaysis

r$testPathwayOverdispersion(go.env,verbose=T,correlation.distance.threshold=0.95,recalculate.pca=F,top.aspects=15)

We’ll use hierarchical differential expression results instead:

r$getHierarchicalDiffExpressionAspects(type='PCA',clusterName='community',z.threshold=3)
using community  clustering for PCA space

We’ll make an app with that, ordering the “differential expression aspects” explicitly (otherwise if row clustering is omitted they’ll be clustered by similarity)

app <- p2.make.pagoda1.app(r,inner.clustering=TRUE,embeddingType='tSNE',clusterType='community',min.group.size=50,row.clustering=list(order=rev(1:nrow(r$misc$pathwayOD$xv))))

Show app:

show.app(app,'pbmc',browse=T)
LS0tCnRpdGxlOiAiSGV0ZXJvZ2VuZWl0eSBhbmFseXNpcyBJSUkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCk1vZGVybiBoaWdoLXRocm91Z2hwdXQgZGF0YXNldHMuIEhlcmUgd2UnbGwgYW5hbHl6ZSBhIDEweCBDaHJvbWl1bSBkYXRhc2V0IHVzaW5nIHBhZ29kYTIuCgpgYGB7cn0KbGlicmFyeShwYWdvZGEyKQpgYGAKCgoxMHggY291bnQgbWF0cml4IGlzIG5vcm1hbGx5IHNwbGl0IGludG8gdGhyZWUgZmlsZXMuIExldCdzIHdyaXRlIGEgdXRpbGl0eSBmdW5jdGlvbiB0byBsb2FkIGl0OgpgYGB7cn0KIyBzZXQgbmFtZXMgZXVxYWwgdG8gdGhlIHZhbHVlcwpzbiA8LSBmdW5jdGlvbih4KSB7IG5hbWVzKHgpIDwtIHg7IHJldHVybih4KTsgfQoKIyBsb2FkIDEweCBtYXRyaWNlcyBmcm9tIGEgbmFtZWQgbGlzdCBvZiByZXN1bHQgZm9sZGVycwp0LmxvYWQuMTB4LmRhdGEgPC0gZnVuY3Rpb24obWF0cml4UGF0aHMsbi5jb3Jlcz0xKSB7CiAgcmVxdWlyZShwYXJhbGxlbCkKICByZXF1aXJlKE1hdHJpeCkKICBtY2xhcHBseShzbihuYW1lcyhtYXRyaXhQYXRocykpLGZ1bmN0aW9uKG5hbSkgewogICAgbWF0cml4UGF0aCA8LSBtYXRyaXhQYXRoc1tuYW1dOwogICAgIyByZWFkIGFsbCBjb3VudCBmaWxlcyAoKl91bmlxdWUuY291bnRzKSB1bmRlciBhIGdpdmVuIHBhdGgKICAgICNjYXQoImxvYWRpbmcgZGF0YSBmcm9tICIsbWF0cml4UGF0aCwgIiAiKTsKICAgIHggPC0gYXMocmVhZE1NKHBhc3RlKG1hdHJpeFBhdGgsJ21hdHJpeC5tdHgnLHNlcD0nLycpKSwnZGdDTWF0cml4Jyk7ICMgY29udmVydCB0byB0aGUgcmVxdWlyZWQgc3BhcnNlIG1hdHJpeCByZXByZXNlbnRhdGlvbgogICAgY2F0KCIuIikKICAgIGdzIDwtIHJlYWQuZGVsaW0ocGFzdGUobWF0cml4UGF0aCwnZ2VuZXMudHN2JyxzZXA9Jy8nKSxoZWFkZXI9RikKICAgIHJvd25hbWVzKHgpIDwtIGdzWywyXQogICAgY2F0KCIuIikKICAgIGdzIDwtIHJlYWQuZGVsaW0ocGFzdGUobWF0cml4UGF0aCwnYmFyY29kZXMudHN2JyxzZXA9Jy8nKSxoZWFkZXI9RikKICAgIGNvbG5hbWVzKHgpIDwtIGdzWywxXQogICAgY2F0KCIuIikKICAgIGNvbG5hbWVzKHgpIDwtIHBhc3RlKG5hbSxjb2xuYW1lcyh4KSxzZXA9J18nKTsKICAgIHgKICB9LG1jLmNvcmVzPW4uY29yZXMpCn0Kbi5jb3JlcyA8LSAzMDsKYGBgCgoKTG9hZCAoMTB4IFBCTUMgZGF0YSlbaHR0cHM6Ly9zdXBwb3J0LjEweGdlbm9taWNzLmNvbS9zaW5nbGUtY2VsbC1nZW5lLWV4cHJlc3Npb24vZGF0YXNldHMvcGJtYzhrXToKYGBge3J9CmRhdGEucGF0aCA8LSAifi93b3Jrc2hvcF9tYXRlcmlhbHMvdHJhbnNjcmlwdG9taWNzLzEweF9wYm1jOGsiCiNkYXRhLnBhdGggPC0gIi9kMC1tZW5kZWwvaG9tZS9wa2hhcmNoZW5rby9wMi93YWxrdGhyb3VnaC9wYm1jOGsvcmF3X2dlbmVfYmNfbWF0cmljZXMvR1JDaDM4IgpjZCA8LSB0LmxvYWQuMTB4LmRhdGEobGlzdChQQk1DOEs9ZGF0YS5wYXRoKSkKYGBgCmBgYHtyfQpzdHIoY2QpCgpgYGAKCgpMb29rIGF0IHRoZSBzdW1tYXJ5IGNvdW50cwpgYGB7cn0KY2QgPC0gY2RbWzFdXQpwYXIobWZyb3c9YygxLDIpLCBtYXIgPSBjKDMuNSwzLjUsMi4wLDAuNSksIG1ncCA9IGMoMiwwLjY1LDApLCBjZXggPSAxLjApCmhpc3QobG9nMTAoY29sU3VtcyhjZCkrMSksbWFpbj0ncmVhZHMgcGVyIGNlbGwnLGNvbD0nd2hlYXQnKQpoaXN0KGxvZzEwKHJvd1N1bXMoY2QpKzEpLG1haW49J3JlYWRzIHBlciBnZW5lJyxjb2w9J3doZWF0JykKYGBgCgoKCkRlZmluZSBhIHF1aWNrIGNlbGwgZmlsdGVyaW5nIGZ1bmN0aW9uIGJhc2VkIG9uIGNlbGwgZGVwdGggdnMuIG51bWJlciBvZiBnZW5lcyByZWxhdGlvbnNoaXA6CmBgYHtyfQojIGZpbHRlciBjZWxscyBiYXNlZCBvbiB0aGUgZ2VuZS9tb2xlY3VsZSBkZXBlbmRlbmN5CnQuZmlsdGVyLmZvci52YWxpZC5jZWxscyA8LSBmdW5jdGlvbihjb3VudE1hdHJpeCxtaW4uY2VsbC5zaXplPTUwMCwgbWF4LmNlbGwuc2l6ZT01ZTQscC5sZXZlbD1taW4oMWUtMywxL25jb2woY291bnRNYXRyaXgpKSxhbHBoYT0wLjEsZG8ucGFyPVQpIHsKICBpZihkby5wYXIpIHsgcGFyKG1mcm93PWMoMSwyKSwgbWFyID0gYygzLjUsMy41LDIuMCwwLjUpLCBtZ3AgPSBjKDIsMC42NSwwKSwgY2V4ID0gMS4wKTt9CiAgaGlzdChsb2cxMChjb2xTdW1zKGNvdW50TWF0cml4KSksY29sPSd3aGVhdCcseGxhYj0nbG9nMTBbIG1vbGVjdWxlcyBdJyxtYWluPScnKSAKICAjIHNvbWUgb2YgdGhlIGNlbGxzIGFyZSB2ZXJ5IGxhcmdlIC4uIHRob3NlIGNhbiBza2V3IHRoZSBhbmFseXNpcyBvZiBtb3JlIHN1YnRsZSBwb3B1bGF0aW9ucyAodG9vIG11Y2ggYmlhcykgLi4gbGV0dGluZyB0aGVtIGluIGhlcmUgdGhvdWdoCiAgCiAgYWJsaW5lKHY9bG9nMTAoYyhtaW4uY2VsbC5zaXplLG1heC5jZWxsLnNpemUpKSxsdHk9Mixjb2w9MikKICAjIGxvb2sgYXQgdGhlIG51bWJlciBvZiBnZW5lcyB2cy4gbW9sZWN1bGUgc2l6ZSBkZXBlbmVuY3kKICBkZiA8LSBkYXRhLmZyYW1lKG1vbGVjdWxlcz1jb2xTdW1zKGNvdW50TWF0cml4KSxnZW5lcz1jb2xTdW1zKGNvdW50TWF0cml4PjApKTsgCiAgZGYgPC0gZGZbZGYkbW9sZWN1bGVzPj1taW4uY2VsbC5zaXplLF07CiAgZGYgPC0gbG9nMTAoZGYpOwogIGRmIDwtIGRmW29yZGVyKGRmJG1vbGVjdWxlcyxkZWNyZWFzaW5nPUYpLF0KICBwbG90KGRmLGNvbD1hZGp1c3Rjb2xvcigxLGFscGhhPWFscGhhKSxjZXg9MC41LHlsYWI9J2xvZzEwWyBnZW5lIGNvdW50c10nLHhsYWI9J2xvZzEwWyBtb2xlY3VsZSBjb3VudHNdJykKICBhYmxpbmUodj1sb2cxMChjKG1pbi5jZWxsLnNpemUsbWF4LmNlbGwuc2l6ZSkpLGx0eT0yLGNvbD0yKQogICNhYmxpbmUobG0oZ2VuZXMgfiBtb2xlY3VsZXMsIGRhdGE9ZGYpLGNvbD00KQogIHJlcXVpcmUoTUFTUykgIAogIG0gPC0gcmxtKGdlbmVzfm1vbGVjdWxlcyxkYXRhPWRmKQogIHN1cHByZXNzV2FybmluZ3MocGIgPC0gZGF0YS5mcmFtZShwcmVkaWN0KG0saW50ZXJ2YWw9J3ByZWRpY3Rpb24nLGxldmVsID0gMS1wLmxldmVsLHR5cGU9InJlc3BvbnNlIikpKQogIHBvbHlnb24oYyhkZiRtb2xlY3VsZXMscmV2KGRmJG1vbGVjdWxlcykpLGMocGIkbHdyLHJldihwYiR1cHIpKSxjb2w9YWRqdXN0Y29sb3IoMixhbHBoYT0wLjEpLGJvcmRlciA9IE5BKQogIG91dGxpZXJzIDwtIHJvd25hbWVzKGRmKVtkZiRnZW5lcyA+IHBiJHVwciB8IGRmJGdlbmVzIDwgcGIkbHdyXTsKICBwb2ludHMoZGZbb3V0bGllcnMsXSxjb2w9MixjZXg9MC42KQogICMgc2V0IG9mIGZpbHRlcmVkIGNlbGxzIHRvIG1vdmUgZm9yd2FyZCB3aXRoICAKICB2YWxpZC5jZWxscyA8LSBjb2xTdW1zKGNvdW50TWF0cml4KT5taW4uY2VsbC5zaXplICYgY29sU3Vtcyhjb3VudE1hdHJpeCk8bWF4LmNlbGwuc2l6ZSAmICEoY29sbmFtZXMoY291bnRNYXRyaXgpICVpbiUgb3V0bGllcnMpCiAgY291bnRNYXRyaXhbLHZhbGlkLmNlbGxzLGRyb3A9Rl0KfQoKYGBgCgoKUnVuIHRoZSBmaWx0ZXJpbmcgcHJvY2VkdXJlOgpgYGB7cn0KY291bnRzIDwtIHQuZmlsdGVyLmZvci52YWxpZC5jZWxscyhjZCxtaW4uY2VsbC5zaXplPTUwMCkKYGBgCgoKYGBge3J9CnN0cihjb3VudHMpCmBgYAoKV2UgY2FuIGFsc28gbG9va2EgYXQgdGhlIG51bWJlciBvZiBtb2xlY3VsZXMgcGVyIGdlbmUsIGFuZCBvbWl0IGxvdy1leHByZXNzZWQgZ2VuZXMgdG8gc2F2ZSBjb21wdXRhdGlvbmFsIHRpbWU6CgpgYGB7cn0KaGlzdChsb2cxMChyb3dTdW1zKGNvdW50cykrMSksbWFpbj0nTW9sZWN1bGVzIHBlciBnZW5lJyx4bGFiPSdtb2xlY3VsZXMgKGxvZzEwKScsY29sPSd3aGVhdCcpCmFibGluZSh2PTEsbHR5PTIsY29sPTIpCmBgYAoKTm93IHdlIGhhdmUgYSBjbGVhbi9sZWFuIGNvdW50IG1hdHJpeCBhbmQgYXJlIHJlYWR5IHRvIHN0YXJ0IGFuYWx5c2lzLiBGaXJzdCB3ZeKAmWxsIGNyZWF0ZSBwYWdvZGEyIG9iamVjdCB0aGF0IHdpbGwgbWFpbnRhaW4gYWxsIG9mIHRoZSByZXN1bHRzLiBJdCB3aWxsIGFsc28gcHJvdmlkZSBoYW5kbGVzIGZvciBydW5uaW5nIGFsbCBvcGVyYXRpb25zIG9uIHRoZSBkYXRhLgoKYGBge3IgZXJyb3I9VFJVRX0KciA8LSBQYWdvZGEyJG5ldyhjb3VudHMsbG9nLnNjYWxlPUZBTFNFKQpgYGAKCgpZZXMsIHdlIG5lZWQgdG8gbWFrZSBnZW5lIG5hbWVzIHVuaXF1ZToKYGBge3J9CnJvd25hbWVzKGNvdW50cykgPC0gbWFrZS51bmlxdWUocm93bmFtZXMoY291bnRzKSkKciA8LSBQYWdvZGEyJG5ldyhjb3VudHMsbG9nLnNjYWxlPUZBTFNFKQpgYGAKCk5leHQsIHdl4oCZbGwgYWRqdXN0IHRoZSB2YXJpYW5jZSwgdG8gbm9ybWFsaXplIHRoZSBleHRlbnQgdG8gd2hpY2ggZ2VuZXMgd2l0aCAodmVyeSkgZGlmZmVyZW50IGV4cHJlc3Npb24gbWFnbml0dWRlcyB3aWxsIGNvbnRyaWJ1dGUgdG8gdGhlIGRvd25zdHJlYW0gYW5sYXlzaXM6CgpgYGB7cn0KciRhZGp1c3RWYXJpYW5jZShwbG90PVQsZ2FtLms9MTApCmBgYAoKVGhlcmUgYXJlIG1hbnkgYWx0ZXJuYXRpdmUgd2F5cyBvZiBwcm9jZWVkaW5nIHdpdGggdGhlIGRvd25zdHJlYW0gYW5hbHlzaXMuIEJlbG93IHdl4oCZbGwgdXNlIHRoZSBzaW1wbGVzdCwgZGVmYXVsdCBzY2VuYXJpbywgd2hlcmUgd2UgZmlyc3QgcmVkdWNlIHRoZSBkYXRhc2V0IGRpbWVuc2lvbnMgYnkgcnVubmluZyBQQ0EsIGFuZCB0aGVuIG1vdmUgaW50byBrLW5lYXJlc3QgbmVpZ2hib3IgZ3JhcGggc3BhY2UgZm9yIGNsdXN0ZXJpbmcgYW5kIHZpc3VhbGl6YXRpb24gY2FsY3VsYXRpb25zLiBGaXJzdCwgdGhlIFBDQSByZWR1Y3Rpb246CmBgYHtyfQpyJGNhbGN1bGF0ZVBjYVJlZHVjdGlvbihuUGNzPTEwMCxuLm9kZ2VuZXM9M2UzLG1heGl0ID0gMjAwKQpgYGAKCgpDbHVzdGVyaW5nLCB2aXN1YWxpemF0aW9uIGFuZCBtYW55IG90aGVyIHByb2NlZHVyZXMgY2FuIHRha2UgYWR2YW50YWdlIG9mIGEgY2VsbCBrTk4gZ3JhcGguIExldCdzIGNhbGN1bGF0ZSBpdC4KYGBge3IgZWNobz1GQUxTRX0KciRtYWtlS25uR3JhcGgoaz00MCx0eXBlPSdQQ0EnLGNlbnRlcj1ULGRpc3RhbmNlPSdjb3NpbmUnKQpgYGAKCgpEZXRlcm1pbmUgY2x1c3RlcnMgdXNpbmcgIm11bHRpbGV2ZWwuY29tbXVuaXR5IiBhbGdvcml0aG0uCmBgYHtyfQpyJGdldEtubkNsdXN0ZXJzKG1ldGhvZD1tdWx0aWxldmVsLmNvbW11bml0eSx0eXBlPSdQQ0EnKQpgYGAKCgpEZXRlcm1pbmUgYSBsYXJnZVZpcyBlbWJlZGRpbmc6CmBgYHtyfQpNIDwtIDMwOyByJGdldEVtYmVkZGluZyh0eXBlID0gJ1BDQScsZW1iZWRkaW5nVHlwZSA9ICdsYXJnZVZpcycsIE0gPSBNLCAgcGVycGxleGl0eSA9IDMwLCAgZ2FtbWEgPSAxIC8gTSwgIGFscGhhID0gMSkKYGBgCk5vdyB3ZSBjYW4gdmlzdWFsaXplIHRoZSBlbWJlZGRpbmcgdXNpbmcgdGhlIGRldGVybWluZWQgY2x1c3RlcnM6CgpgYGB7cn0KciRwbG90RW1iZWRkaW5nKHR5cGU9J1BDQScsc2hvdy5sZWdlbmQ9RixtYXJrLmNsdXN0ZXJzPVQsbWluLmdyb3VwLnNpemU9NTAsc2h1ZmZsZS5jb2xvcnM9RixtYXJrLmNsdXN0ZXIuY2V4PTEsYWxwaGE9MC4xLG1haW49J2NsdXN0ZXJzIChsViknKQpgYGAKCmBgYHtyfQpyJGdldEVtYmVkZGluZyh0eXBlPSdQQ0EnLGVtYmVkZGluZ1R5cGU9J3RTTkUnLHBlcnBsZXhpdHk9NTAsdmVyYm9zZT1GLG4uY29yZXM9bi5jb3JlcykKYGBgCmBgYHtyfQpyJHBsb3RFbWJlZGRpbmcodHlwZT0nUENBJyxlbWJlZGRpbmdUeXBlPSd0U05FJyxzaG93LmxlZ2VuZD1GLG1hcmsuY2x1c3RlcnM9VCxtaW4uZ3JvdXAuc2l6ZT0xLHNodWZmbGUuY29sb3JzPUYsbWFyay5jbHVzdGVyLmNleD0xLGFscGhhPTAuMSxtYWluPSdjbHVzdGVycyAodFNORSknKQpgYGAKCk9yIGxvYWQgcHJlY2FsY3VsYXRlZCB0U05FCmBgYHtyIGV2YWw9Rn0KdFNORSA8LSByZWFkUkRTKCJ0U05FLnJkcyIpCnIkZW1iZWRkaW5ncyRQQ0EkdFNORSA8LSB0U05FOwpgYGAKCgoKV2UgY2FuIHVzZSB0aGUgc2FtZSBwbG90RW1iZWRkaW5nKCkgZnVuY3Rpb24gdG8gc2hvdyBhbGwga2luZHMgb2Ygb3RoZXIgdmFsdWVzLiBGb3IgaW5zdGFuY2UsIGxldOKAmXMgbG9vayBhdCBkZXB0aCwgb3IgYW4gZXhwcmVzc29uIHBhdHRlcm4gb2YgYSBnZW5lOgoKYGBge3J9CnN0cihyJGRlcHRoKQpgYGAKYGBge3J9CnBhcihtZnJvdz1jKDEsMikpCnIkcGxvdEVtYmVkZGluZyh0eXBlPSdQQ0EnLGVtYmVkZGluZ1R5cGU9J3RTTkUnLHNob3cubGVnZW5kPUYsbWFyay5jbHVzdGVycz1ULG1pbi5ncm91cC5zaXplPTEsc2h1ZmZsZS5jb2xvcnM9RixtYXJrLmNsdXN0ZXIuY2V4PTEsYWxwaGE9MC4xLG1haW49J2NsdXN0ZXJzICh0U05FKScpCnIkcGxvdEVtYmVkZGluZyh0eXBlPSdQQ0EnLGVtYmVkZGluZ1R5cGU9J3RTTkUnLGNvbG9ycz1yJGRlcHRoLHNodWZmbGUuY29sb3JzPUYsbWFyay5jbHVzdGVyLmNleD0xLGFscGhhPTAuMSxtYWluPSdkZXB0aCcpCmBgYApPciBleHByZXNzaW9uIG9mIGEgZ2l2ZW4gZ2VuZToKYGBge3J9CnBhcihtZnJvdz1jKDEsMikpCnIkcGxvdEVtYmVkZGluZyh0eXBlPSdQQ0EnLGVtYmVkZGluZ1R5cGU9J3RTTkUnLHNob3cubGVnZW5kPUYsbWFyay5jbHVzdGVycz1ULG1pbi5ncm91cC5zaXplPTEsc2h1ZmZsZS5jb2xvcnM9RixtYXJrLmNsdXN0ZXIuY2V4PTEsYWxwaGE9MC4xLG1haW49J2NsdXN0ZXJzICh0U05FKScpCgpnZW5lIDwtIkxZWiIKciRwbG90RW1iZWRkaW5nKHR5cGU9J1BDQScsZW1iZWRkaW5nVHlwZT0ndFNORScsY29sb3JzPXIkY291bnRzWyxnZW5lXSxzaHVmZmxlLmNvbG9ycz1GLG1hcmsuY2x1c3Rlci5jZXg9MSxhbHBoYT0wLjEsbWFpbj1nZW5lKQpgYGAKCldlIGNhbiBnZW5lcmF0ZSBtdWx0aXBsZSBwb3RlbnRpYWwgY2x1c3RlcmluZ3MsIHdpdGggZGlmZmVyZW50IG5hbWVzLiBIZXJlIHdl4oCZbGwgdXNlIG11bHRpbGV2ZWwgY2x1c3RlcmluZzoKYGBge3J9CnIkZ2V0S25uQ2x1c3RlcnMobWV0aG9kPWluZm9tYXAuY29tbXVuaXR5LHR5cGU9J1BDQScsbmFtZT0naW5mb21hcCcpCnN0cihyJGNsdXN0ZXJzKQpgYGAKCkNvbXBhcmUgd2l0aCBpbmZvbWFwOgpgYGB7cn0KcGFyKG1mcm93PWMoMSwyKSkKciRwbG90RW1iZWRkaW5nKHR5cGU9J1BDQScsZW1iZWRkaW5nVHlwZT0ndFNORScsZ3JvdXBzPXIkY2x1c3RlcnMkUENBJGNvbW11bml0eSxzaG93LmxlZ2VuZD1GLG1hcmsuY2x1c3RlcnM9VCxtaW4uZ3JvdXAuc2l6ZT0xLHNodWZmbGUuY29sb3JzPUYsbWFyay5jbHVzdGVyLmNleD0xLGFscGhhPTAuMSxtYWluPSdtdWx0aWxldmVsIGNsdXN0ZXJzICh0U05FKScpCnIkcGxvdEVtYmVkZGluZyh0eXBlPSdQQ0EnLGVtYmVkZGluZ1R5cGU9J3RTTkUnLGNsdXN0ZXJUeXBlPSdpbmZvbWFwJyxzaG93LmxlZ2VuZD1GLG1hcmsuY2x1c3RlcnM9VCxtaW4uZ3JvdXAuc2l6ZT0xLHNodWZmbGUuY29sb3JzPUYsbWFyay5jbHVzdGVyLmNleD0xLGFscGhhPTAuMSxtYWluPSdpbmZvbWFwIGNsdXN0ZXJzICh0U05FKScpCgpgYGAKCllvdSBjYW4gdHJ5IG90aGVyIGNsdXN0ZXIgbWV0aG9kcyAoZS5nLiB3YWxrdHJhcC5jb21tdW5pdHkpLCBvciBjaGFuZ2UgayBvbiB0aGUgZ2VuZSBrTk4gZ3JhcGguCgoKUnVuIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9uIHRoZSBpbmZvbWFwIGNsdXN0ZXJzOgpgYGB7cn0KciRnZXREaWZmZXJlbnRpYWxHZW5lcyh0eXBlPSdQQ0EnLHZlcmJvc2U9VCxjbHVzdGVyVHlwZT0nY29tbXVuaXR5JykKYGBgClZpc3VhbGl6ZSB0b3AgZ2VuZXM6CmBgYHtyfQpuYW1lcyhyJGRpZmZnZW5lcykKYGBgCgpgYGB7cn0KZGUgPC0gciRkaWZmZ2VuZXMkUENBW1sxXV1bWyc0J11dOwpyJHBsb3RHZW5lSGVhdG1hcChnZW5lcz1yb3duYW1lcyhkZSlbMToxNV0sZ3JvdXBzPXIkY2x1c3RlcnMkUENBW1sxXV0pCmBgYAoKU3BvdC1jaGVjayBhIGdlbmU6CmBgYHtyfQpnZW5lIDwtIklHSE0iCnIkcGxvdEVtYmVkZGluZyh0eXBlPSdQQ0EnLGVtYmVkZGluZ1R5cGU9J3RTTkUnLGNvbG9ycz1yJGNvdW50c1ssZ2VuZV0sc2h1ZmZsZS5jb2xvcnM9RixtYXJrLmNsdXN0ZXIuY2V4PTEsYWxwaGE9MC4xLG1haW49Z2VuZSkKYGBgCgpQYXRod2F5IG92ZXJkaXNwZXJzaW9uIGFuYWx5c2lzIChhIGxhIFBBR09EQTEpCgpGaXJzdCwgYnVpbGQgR08tPmdlbmUgZW52aXJvbm1lbnQ6CmBgYHtyIGV2YWw9Rn0Kc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KG9yZy5Icy5lZy5kYikpCiMgdHJhbnNsYXRlIGdlbmUgbmFtZXMgdG8gaWRzCmlkcyA8LSB1bmxpc3QobGFwcGx5KG1nZXQoY29sbmFtZXMociRjb3VudHMpLG9yZy5Icy5lZ0FMSUFTMkVHLGlmbm90Zm91bmQ9TkEpLGZ1bmN0aW9uKHgpIHhbMV0pKQojIHJldmVyc2UgbWFwCnJpZHMgPC0gbmFtZXMoaWRzKTsgbmFtZXMocmlkcykgPC0gaWRzOwojIGxpc3QgYWxsIHRoZSBpZHMgcGVyIEdPIGNhdGVnb3J5CmdvLmVudiA8LSBsaXN0MmVudihlYXBwbHkob3JnLkhzLmVnR08yQUxMRUdTLGZ1bmN0aW9uKHgpIGFzLmNoYXJhY3RlcihuYS5vbWl0KHJpZHNbeF0pKSkpCmBgYAoKTm93IHJ1biBvdmVyZGlzcGVyc2lvbiBhbmxheXNpcwpgYGB7ciBldmFsPUZ9CnIkdGVzdFBhdGh3YXlPdmVyZGlzcGVyc2lvbihnby5lbnYsdmVyYm9zZT1ULGNvcnJlbGF0aW9uLmRpc3RhbmNlLnRocmVzaG9sZD0wLjk1LHJlY2FsY3VsYXRlLnBjYT1GLHRvcC5hc3BlY3RzPTE1KQpgYGAKCgpXZSdsbCB1c2UgaGllcmFyY2hpY2FsIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHMgaW5zdGVhZDoKYGBge3J9CnIkZ2V0SGllcmFyY2hpY2FsRGlmZkV4cHJlc3Npb25Bc3BlY3RzKHR5cGU9J1BDQScsY2x1c3Rlck5hbWU9J2NvbW11bml0eScsei50aHJlc2hvbGQ9MykKYGBgCgpXZSdsbCBtYWtlIGFuIGFwcCB3aXRoIHRoYXQsIG9yZGVyaW5nIHRoZSAiZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYXNwZWN0cyIgZXhwbGljaXRseSAob3RoZXJ3aXNlIGlmIHJvdyBjbHVzdGVyaW5nIGlzIG9taXR0ZWQgdGhleSdsbCBiZSBjbHVzdGVyZWQgYnkgc2ltaWxhcml0eSkKYGBge3J9CmFwcCA8LSBwMi5tYWtlLnBhZ29kYTEuYXBwKHIsaW5uZXIuY2x1c3RlcmluZz1UUlVFLGVtYmVkZGluZ1R5cGU9J3RTTkUnLGNsdXN0ZXJUeXBlPSdjb21tdW5pdHknLG1pbi5ncm91cC5zaXplPTUwLHJvdy5jbHVzdGVyaW5nPWxpc3Qob3JkZXI9cmV2KDE6bnJvdyhyJG1pc2MkcGF0aHdheU9EJHh2KSkpKQpgYGAKClNob3cgYXBwOgpgYGB7ciBldmFsPUZ9CnNob3cuYXBwKGFwcCwncGJtYycsYnJvd3NlPVQpCmBgYAoK