projDir <- "/mnt/scratcha/bioinformatics/baller01/20200511_FernandesM_ME_crukBiSs2020"
outDirBit <- "AnaWiSce/Attempt1"
nbPcToComp <- 50
suppressMessages(library(ggplot2))
suppressMessages(library(scater))
suppressMessages(library(scran))
suppressMessages(library(dplyr))
fontsize <- theme(axis.text=element_text(size=12), axis.title=element_text(size=16))

1 Cell cycle assignment

Source: Cell cycle assignment of the OSCA book.

1.1 Load data

We will load the R file keeping the SCE object with the normalised counts for 500 cells per sample.

setName <- "caron"
setSuf <- "_5hCellPerSpl"

# Read object in:
tmpFn <- sprintf("%s/%s/Robjects/%s_sce_nz_postDeconv%s_clustered.Rds", projDir, outDirBit, setName, setSuf)
print(tmpFn)
[1] "/mnt/scratcha/bioinformatics/baller01/20200511_FernandesM_ME_crukBiSs2020/AnaWiSce/Attempt1/Robjects/caron_sce_nz_postDeconv_5hCellPerSpl_clustered.Rds"
if(!file.exists(tmpFn))
{
    knitr::knit_exit()
}
sce <- readRDS(tmpFn)
sce
class: SingleCellExperiment 
dim: 18372 5500 
metadata(0):
assays(2): counts logcounts
rownames(18372): ENSG00000238009 ENSG00000237491 ... ENSG00000275063
  ENSG00000271254
rowData names(11): ensembl_gene_id external_gene_name ... detected
  gene_sparsity
colnames: NULL
colData names(22): Sample Barcode ... cluster kmeans10
reducedDimNames(3): PCA TSNE UMAP
altExpNames(0):
sce$clusterStg <- factor(paste0("c", sce$cluster),
             levels = paste0("c", levels( sce$cluster)) )

1.2 Motivation

On occasion, it can be desirable to determine cell cycle activity from scRNA-seq data. In and of itself, the distribution of cells across phases of the cell cycle is not usually informative, but we can use this to determine if there are differences in proliferation between subpopulations or across treatment conditions. Many of the key events in the cell cycle (e.g., passage through checkpoints) are post-translational and thus not directly visible in transcriptomic data; nonetheless, there are enough changes in expression that can be exploited to determine cell cycle phase. We demonstrate using the 416B dataset, which is known to contain actively cycling cells after oncogene induction.

1.3 Using the cyclins

The cyclins control progression through the cell cycle and have well-characterized patterns of expression across cell cycle phases. Cyclin D is expressed throughout but peaks at G1; cyclin E is expressed highest in the G1/S transition; cyclin A is expressed across S and G2; and cyclin B is expressed highest in late G2 and mitosis. Inspection of the relative expression of cyclins across the population can often be sufficient to determine the relative cell cycle activity in each cluster (Figure 16.1). For example, cluster 1 is likely to be in G1 while the other clusters are scattered across the later phases.

#cyclin.genes <- grep("^CCN[ABDE][0-9]$", rowData(sce)$SYMBOL)
cyclin.genes <- grep("^CCN[ABDE]+", rowData(sce)$Symbol)
cyclin.genes <- rownames(sce)[cyclin.genes]
rowData(sce)[cyclin.genes,]
DataFrame with 11 rows and 11 columns
                ensembl_gene_id external_gene_name chromosome_name
                    <character>        <character>     <character>
ENSG00000145386 ENSG00000145386              CCNA2               4
ENSG00000134057 ENSG00000134057              CCNB1               5
ENSG00000112576 ENSG00000112576              CCND3               6
ENSG00000175305 ENSG00000175305              CCNE2               8
ENSG00000110092 ENSG00000110092              CCND1              11
ENSG00000118971 ENSG00000118971              CCND2              12
ENSG00000133101 ENSG00000133101              CCNA1              13
ENSG00000100814 ENSG00000100814           CCNB1IP1              14
ENSG00000166946 ENSG00000166946            CCNDBP1              15
ENSG00000157456 ENSG00000157456              CCNB2              15
ENSG00000105173 ENSG00000105173              CCNE1              19
                start_position end_position strandNum      Symbol
                     <integer>    <integer> <integer> <character>
ENSG00000145386      121816444    121823933        -1       CCNA2
ENSG00000134057       69167135     69178245         1       CCNB1
ENSG00000112576       41934934     42050357        -1       CCND3
ENSG00000175305       94879770     94896678        -1       CCNE2
ENSG00000110092       69641156     69654474         1       CCND1
ENSG00000118971        4273762      4305353         1       CCND2
ENSG00000133101       36431520     36442870         1       CCNA1
ENSG00000100814       20311368     20333312        -1    CCNB1IP1
ENSG00000166946       43185118     43197177         1     CCNDBP1
ENSG00000157456       59105126     59125045         1       CCNB2
ENSG00000105173       29811991     29824312         1       CCNE1
                           Type       mean  detected gene_sparsity
                    <character>  <numeric> <numeric>     <numeric>
ENSG00000145386 Gene Expression 0.18827907  6.582173      0.916136
ENSG00000134057 Gene Expression 0.24689175  6.112821      0.926208
ENSG00000112576 Gene Expression 0.89323057 42.111037      0.404038
ENSG00000175305 Gene Expression 0.03021450  2.333095      0.975673
ENSG00000110092 Gene Expression 0.00974467  0.790423      0.987453
ENSG00000118971 Gene Expression 0.16729488 11.804110      0.885239
ENSG00000133101 Gene Expression 0.00209762  0.173194      0.993431
ENSG00000100814 Gene Expression 0.14573204 11.180853      0.844533
ENSG00000166946 Gene Expression 0.37205349 23.893930      0.698132
ENSG00000157456 Gene Expression 0.27627042  7.518866      0.907662
ENSG00000105173 Gene Expression 0.08911249  4.335072      0.976746
# only use the 10 most variable cyclins:
tmpVar <- DelayedMatrixStats::rowVars( DelayedArray(assay(sce[cyclin.genes,], "logcounts")))
names(tmpVar) <- cyclin.genes
cyclin.genes.sub <- names(tmpVar[order(tmpVar, decreasing=T)])[1:10]
rowData(sce)[cyclin.genes.sub,c("ensembl_gene_id", "Symbol")]
DataFrame with 10 rows and 2 columns
                ensembl_gene_id      Symbol
                    <character> <character>
ENSG00000112576 ENSG00000112576       CCND3
ENSG00000166946 ENSG00000166946     CCNDBP1
ENSG00000100814 ENSG00000100814    CCNB1IP1
ENSG00000157456 ENSG00000157456       CCNB2
ENSG00000118971 ENSG00000118971       CCND2
ENSG00000134057 ENSG00000134057       CCNB1
ENSG00000145386 ENSG00000145386       CCNA2
ENSG00000105173 ENSG00000105173       CCNE1
ENSG00000175305 ENSG00000175305       CCNE2
ENSG00000110092 ENSG00000110092       CCND1
library(scater)
plotHeatmap(sce, order_columns_by="clusterStg", 
    cluster_rows=TRUE, features=sort(cyclin.genes.sub))

For example, we can use standard DE methods (Chapter 11) to look for upregulation of each cyclin, which would imply that a subpopulation contains more cells in the corresponding cell cycle phase. The same logic applies to comparisons between treatment conditions, as described in Chapter 14.

library(scran)
markers <- findMarkers(sce, groups=sce$clusterStg, subset.row=cyclin.genes, 
    test.type="wilcox", direction="up")

# We can infer that cluster 4 has more cells in G2/M than the other clusters,
# based on higher expression of the cyclin B's.
markers[[4]] %>% data.frame() %>%
    tibble::rownames_to_column("ensembl_gene_id") %>%
    left_join(data.frame(rowData(sce))[, c("ensembl_gene_id", "Symbol")], by="ensembl_gene_id") %>%
    select(Symbol, Top, p.value, FDR, summary.AUC)

Direct examination of cyclin expression is easily to understand, interpret and experimentally validate. However, it is best suited for statements about relative cell cycle activity; for example, we would find it difficult to assign cell cycle phase in Figure 16.1 without the presence of clusters spanning all phases to provide benchmarks for “high” and “low” expression of each cyclin. We also assume that cyclin expression is not affected by biological processes other than the cell cycle, which may be a strong assumption in some cases, e.g., malignant cells. This strategy is strongly dependent on having good sequencing coverage of the cyclins, which is less of an issue for the whole-of-transcriptome methods described below that use information from more genes.

1.4 Using the cyclone() classifier

The prediction method described by Scialdone et al. (2015) is another approach for classifying cells into cell cycle phases. Using a reference dataset, we first compute the sign of the difference in expression between each pair of genes. Pairs with changes in the sign across cell cycle phases are chosen as markers. Cells in a test dataset can then be classified into the appropriate phase, based on whether the observed sign for each marker pair is consistent with one phase or another. This approach is implemented in the cyclone() function from the scran package, which also contains pre-trained set of marker pairs for mouse and human data.

set.seed(100)
library(scran)
hs.pairs <- readRDS(system.file("exdata", "human_cycle_markers.rds", 
    package="scran"))

# Using Ensembl IDs to match up with the annotation in 'mm.pairs'.
assignments <- cyclone(sce, hs.pairs, gene.names=rowData(sce)$ensembl_gene_id) # SLOW

Write assignments to file.

tmpFn <- sprintf("%s/%s/Robjects/%s_sce_nz_postDeconv%s_cyclone.Rds", projDir, outDirBit, setName, setSuf)
print(tmpFn)
saveRDS(assignments, file=tmpFn)
tmpFn <- sprintf("%s/%s/Robjects/%s_sce_nz_postDeconv%s_cyclone.Rds", projDir, outDirBit, setName, setSuf)
print(tmpFn)
[1] "/mnt/scratcha/bioinformatics/baller01/20200511_FernandesM_ME_crukBiSs2020/AnaWiSce/Attempt1/Robjects/caron_sce_nz_postDeconv_5hCellPerSpl_cyclone.Rds"
assignments <- readRDS(file=tmpFn)

Copy cell cycle assignments to SCE object:

colData(sce)$phases <- assignments$phases

For each cell, a higher score for a phase corresponds to a higher probability that the cell is in that phase. We focus on the G1 and G2/M scores as these are the most informative for classification.

The plot below show the cell cycle phase scores obtained by applying the pair-based classifier on the dataset. Each point represents a cell, plotted according to its scores for G1 and G2/M phases.

plot(assignments$score$G1, assignments$score$G2M,
    xlab="G1 score", ylab="G2/M score", pch=16)

Cells are classified as being in G1 phase if the G1 score is above 0.5 and greater than the G2/M score; in G2/M phase if the G2/M score is above 0.5 and greater than the G1 score; and in S phase if neither score is above 0.5.

colLabels(sce) <- colData(sce)$source_name
table(assignments$phases, colLabels(sce))
     
      ABMMC ETV6-RUNX1  HHD PBMMC PRE-T
  G1      0       1597  650  1085   641
  G2M     0        118   78   232   132
  S       0        285  272   183   227
# prop per phase
round(prop.table(table(assignments$phases, colLabels(sce)),1),2)
     
      ABMMC ETV6-RUNX1  HHD PBMMC PRE-T
  G1   0.00       0.40 0.16  0.27  0.16
  G2M  0.00       0.21 0.14  0.41  0.24
  S    0.00       0.29 0.28  0.19  0.23
# prop per type
round(prop.table(table(assignments$phases, colLabels(sce)),2),2)
     
      ABMMC ETV6-RUNX1  HHD PBMMC PRE-T
  G1              0.80 0.65  0.72  0.64
  G2M             0.06 0.08  0.15  0.13
  S               0.14 0.27  0.12  0.23
tsne1 <- plotTSNE(sce, colour_by="phases") + fontsize
tsne1 + facet_wrap(. ~ sce$source_name)

tsne1 + facet_wrap(. ~ sce$phases)

umap1 <- plotUMAP(sce, colour_by="phases") + fontsize
umap1 + facet_wrap(. ~ sce$source_name)

umap1 + facet_wrap(. ~ sce$phases)

1.5 Regressing out cell cycle phase

For some time, it was popular to regress out the cell cycle phase prior to downstream analyses. The aim was to remove uninteresting variation due to cell cycle, thus improving resolution of other biological processes of interest. We could implement this by performing cell cycle phase assignment as described above, treating each phase as a separate batch and applying any of the batch correction strategies. The most common approach is to use a linear model to simply regress out the phase effect, e.g., via regressBatches().

library(batchelor)
sce.nocycle <- regressBatches(sce, batch=sce$phases)

# PCA
#plotPCA(sce, colour_by = "Sample.Name")
sce.nocycle <- runPCA(
  sce.nocycle,
  exprs_values = "corrected"
)
p <- plotPCA(
    sce.nocycle,
    colour_by = "batch"
)
p

# TSNE
sce.nocycle <- runTSNE(sce.nocycle, exprs_values = "corrected")
p <- plotTSNE(
    sce.nocycle,
    colour_by = "batch"
)
p

That said, we do not consider adjusting for cell cycle to be a necessary step in routine scRNA-seq analyses. In most applications, the cell cycle is a minor factor of variation, secondary to differences between cell types. Any attempt at removal would also need to assume that the cell cycle effect is orthogonal to other biological processes. For example, regression would potentially remove interesting signal if cell cycle activity varied across clusters or conditions, with a prime example being the increased proliferation of activated T cells (Richard et al. 2018). We suggest only performing cell cycle adjustment on an as-needed basis in populations with clear cell cycle effects.

Challenge Remove the cell cycle genes listed in the ‘cell cycle’ GO term, perform PCA and plot t-SNE.

library(org.Hs.eg.db)
Loading required package: AnnotationDbi

Attaching package: 'AnnotationDbi'
The following object is masked from 'package:dplyr':

    select
cc.genes <- select(org.Hs.eg.db, keys="GO:0007049", keytype="GOALL", column="ENSEMBL")
'select()' returned 1:many mapping between keys and columns
length(cc.genes)
[1] 4
sce.uncycle <- sce[!rowData(sce)$ensembl_gene_id %in% cc.genes$ENSEMBL,]

# PCA
sce.uncycle <- runPCA(
  sce.uncycle,
  exprs_values = "logcounts"
)
p <- plotPCA(
    sce.uncycle,
    colour_by = "phases",
    size_by = "sum",
    shape_by = "source_name"
) 
p

# TSNE
sce.uncycle <- runTSNE(sce.uncycle, exprs_values = "logcounts")
p <- plotTSNE(
    sce.uncycle,
    colour_by = "phases",
    size_by = "sum",
    shape_by = "source_name"
)
p

# UMAP
sce.uncycle <- runUMAP(sce.uncycle, exprs_values = "logcounts")
p <- plotUMAP(
    sce.uncycle,
    colour_by = "phases",
    size_by = "sum",
    shape_by = "source_name"
)
p

Write SCE object to file.

tmpFn <- sprintf("%s/%s/Robjects/%s_sce_nz_postDeconv%s_cellCycle.Rds", projDir, outDirBit, setName, setSuf)
print(tmpFn)
saveRDS(sce, file=tmpFn)
LS0tCnRpdGxlOiAiQ1JVSyBDSSBTdW1tZXIgU2Nob29sIDIwMjAgLSBpbnRyb2R1Y3Rpb24gdG8gc2luZ2xlLWNlbGwgUk5BLXNlcSBhbmFseXNpcyIKc3VidGl0bGU6ICdDZWxsIGN5Y2xlIHBoYXNlcycKCmF1dGhvcjogIlN0ZXBoYW5lIEJhbGxlcmVhdSwgWmV5bmVwIEthbGVuZGVyIEF0YWssIEthdGFyenluYSBLYW5pYSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgaHRtbF9ib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCnBhcmFtczoKICBvdXREaXJCaXQ6ICJBbmFXaVNjZS9BdHRlbXB0MSIKLS0tCgpgYGB7cn0KcHJvakRpciA8LSAiL21udC9zY3JhdGNoYS9iaW9pbmZvcm1hdGljcy9iYWxsZXIwMS8yMDIwMDUxMV9GZXJuYW5kZXNNX01FX2NydWtCaVNzMjAyMCIKb3V0RGlyQml0IDwtICJBbmFXaVNjZS9BdHRlbXB0MSIKbmJQY1RvQ29tcCA8LSA1MApgYGAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCBlY2hvPUZBTFNFfQojIEZpcnN0LCBzZXQgc29tZSB2YXJpYWJsZXM6CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCnNldC5zZWVkKDEyMykgIyBmb3IgcmVwcm9kdWNpYmlsaXR5CmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsID0gVFJVRSkgCmBgYAoKYGBge3J9CnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShnZ3Bsb3QyKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHNjYXRlcikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShzY3JhbikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShkcGx5cikpCmZvbnRzaXplIDwtIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYpKQpgYGAKCiMgQ2VsbCBjeWNsZSBhc3NpZ25tZW50CgpTb3VyY2U6IFtDZWxsIGN5Y2xlIGFzc2lnbm1lbnRdKGh0dHBzOi8vb3NjYS5iaW9jb25kdWN0b3Iub3JnL2NlbGwtY3ljbGUtYXNzaWdubWVudC5odG1sKSBvZiB0aGUgT1NDQSBib29rLgoKIyMgTG9hZCBkYXRhCgpXZSB3aWxsIGxvYWQgdGhlIFIgZmlsZSBrZWVwaW5nIHRoZSBTQ0Ugb2JqZWN0IHdpdGggdGhlIG5vcm1hbGlzZWQgY291bnRzIGZvciA1MDAgY2VsbHMgcGVyIHNhbXBsZS4KCmBgYHtyfQpzZXROYW1lIDwtICJjYXJvbiIKc2V0U3VmIDwtICJfNWhDZWxsUGVyU3BsIgoKIyBSZWFkIG9iamVjdCBpbjoKdG1wRm4gPC0gc3ByaW50ZigiJXMvJXMvUm9iamVjdHMvJXNfc2NlX256X3Bvc3REZWNvbnYlc19jbHVzdGVyZWQuUmRzIiwgcHJvakRpciwgb3V0RGlyQml0LCBzZXROYW1lLCBzZXRTdWYpCnByaW50KHRtcEZuKQppZighZmlsZS5leGlzdHModG1wRm4pKQp7Cglrbml0cjo6a25pdF9leGl0KCkKfQpzY2UgPC0gcmVhZFJEUyh0bXBGbikKc2NlCgpzY2UkY2x1c3RlclN0ZyA8LSBmYWN0b3IocGFzdGUwKCJjIiwgc2NlJGNsdXN0ZXIpLAoJCQkgbGV2ZWxzID0gcGFzdGUwKCJjIiwgbGV2ZWxzKCBzY2UkY2x1c3RlcikpICkKYGBgCgojIyBNb3RpdmF0aW9uCgpPbiBvY2Nhc2lvbiwgaXQgY2FuIGJlIGRlc2lyYWJsZSB0byBkZXRlcm1pbmUgY2VsbCBjeWNsZSBhY3Rpdml0eSBmcm9tIHNjUk5BLXNlcSBkYXRhLiBJbiBhbmQgb2YgaXRzZWxmLCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNlbGxzIGFjcm9zcyBwaGFzZXMgb2YgdGhlIGNlbGwgY3ljbGUgaXMgbm90IHVzdWFsbHkgaW5mb3JtYXRpdmUsIGJ1dCB3ZSBjYW4gdXNlIHRoaXMgdG8gZGV0ZXJtaW5lIGlmIHRoZXJlIGFyZSBkaWZmZXJlbmNlcyBpbiBwcm9saWZlcmF0aW9uIGJldHdlZW4gc3VicG9wdWxhdGlvbnMgb3IgYWNyb3NzIHRyZWF0bWVudCBjb25kaXRpb25zLiBNYW55IG9mIHRoZSBrZXkgZXZlbnRzIGluIHRoZSBjZWxsIGN5Y2xlIChlLmcuLCBwYXNzYWdlIHRocm91Z2ggY2hlY2twb2ludHMpIGFyZSBwb3N0LXRyYW5zbGF0aW9uYWwgYW5kIHRodXMgbm90IGRpcmVjdGx5IHZpc2libGUgaW4gdHJhbnNjcmlwdG9taWMgZGF0YTsgbm9uZXRoZWxlc3MsIHRoZXJlIGFyZSBlbm91Z2ggY2hhbmdlcyBpbiBleHByZXNzaW9uIHRoYXQgY2FuIGJlIGV4cGxvaXRlZCB0byBkZXRlcm1pbmUgY2VsbCBjeWNsZSBwaGFzZS4gV2UgZGVtb25zdHJhdGUgdXNpbmcgdGhlIDQxNkIgZGF0YXNldCwgd2hpY2ggaXMga25vd24gdG8gY29udGFpbiBhY3RpdmVseSBjeWNsaW5nIGNlbGxzIGFmdGVyIG9uY29nZW5lIGluZHVjdGlvbi4KCgojIyBVc2luZyB0aGUgY3ljbGlucwoKVGhlIGN5Y2xpbnMgY29udHJvbCBwcm9ncmVzc2lvbiB0aHJvdWdoIHRoZSBjZWxsIGN5Y2xlIGFuZCBoYXZlIHdlbGwtY2hhcmFjdGVyaXplZCBwYXR0ZXJucyBvZiBleHByZXNzaW9uIGFjcm9zcyBjZWxsIGN5Y2xlIHBoYXNlcy4gQ3ljbGluIEQgaXMgZXhwcmVzc2VkIHRocm91Z2hvdXQgYnV0IHBlYWtzIGF0IEcxOyBjeWNsaW4gRSBpcyBleHByZXNzZWQgaGlnaGVzdCBpbiB0aGUgRzEvUyB0cmFuc2l0aW9uOyBjeWNsaW4gQSBpcyBleHByZXNzZWQgYWNyb3NzIFMgYW5kIEcyOyBhbmQgY3ljbGluIEIgaXMgZXhwcmVzc2VkIGhpZ2hlc3QgaW4gbGF0ZSBHMiBhbmQgbWl0b3Npcy4gSW5zcGVjdGlvbiBvZiB0aGUgcmVsYXRpdmUgZXhwcmVzc2lvbiBvZiBjeWNsaW5zIGFjcm9zcyB0aGUgcG9wdWxhdGlvbiBjYW4gb2Z0ZW4gYmUgc3VmZmljaWVudCB0byBkZXRlcm1pbmUgdGhlIHJlbGF0aXZlIGNlbGwgY3ljbGUgYWN0aXZpdHkgaW4gZWFjaCBjbHVzdGVyIChGaWd1cmUgMTYuMSkuIEZvciBleGFtcGxlLCBjbHVzdGVyIDEgaXMgbGlrZWx5IHRvIGJlIGluIEcxIHdoaWxlIHRoZSBvdGhlciBjbHVzdGVycyBhcmUgc2NhdHRlcmVkIGFjcm9zcyB0aGUgbGF0ZXIgcGhhc2VzLgoKYGBge3J9CiNjeWNsaW4uZ2VuZXMgPC0gZ3JlcCgiXkNDTltBQkRFXVswLTldJCIsIHJvd0RhdGEoc2NlKSRTWU1CT0wpCmN5Y2xpbi5nZW5lcyA8LSBncmVwKCJeQ0NOW0FCREVdKyIsIHJvd0RhdGEoc2NlKSRTeW1ib2wpCmN5Y2xpbi5nZW5lcyA8LSByb3duYW1lcyhzY2UpW2N5Y2xpbi5nZW5lc10Kcm93RGF0YShzY2UpW2N5Y2xpbi5nZW5lcyxdCgojIG9ubHkgdXNlIHRoZSAxMCBtb3N0IHZhcmlhYmxlIGN5Y2xpbnM6CnRtcFZhciA8LSBEZWxheWVkTWF0cml4U3RhdHM6OnJvd1ZhcnMoIERlbGF5ZWRBcnJheShhc3NheShzY2VbY3ljbGluLmdlbmVzLF0sICJsb2djb3VudHMiKSkpCm5hbWVzKHRtcFZhcikgPC0gY3ljbGluLmdlbmVzCmN5Y2xpbi5nZW5lcy5zdWIgPC0gbmFtZXModG1wVmFyW29yZGVyKHRtcFZhciwgZGVjcmVhc2luZz1UKV0pWzE6MTBdCnJvd0RhdGEoc2NlKVtjeWNsaW4uZ2VuZXMuc3ViLGMoImVuc2VtYmxfZ2VuZV9pZCIsICJTeW1ib2wiKV0KYGBgCgpgYGB7cn0KbGlicmFyeShzY2F0ZXIpCnBsb3RIZWF0bWFwKHNjZSwgb3JkZXJfY29sdW1uc19ieT0iY2x1c3RlclN0ZyIsIAogICAgY2x1c3Rlcl9yb3dzPVRSVUUsIGZlYXR1cmVzPXNvcnQoY3ljbGluLmdlbmVzLnN1YikpCmBgYAoKRm9yIGV4YW1wbGUsIHdlIGNhbiB1c2Ugc3RhbmRhcmQgREUgbWV0aG9kcyAoQ2hhcHRlciAxMSkgdG8gbG9vayBmb3IgdXByZWd1bGF0aW9uIG9mIGVhY2ggY3ljbGluLCB3aGljaCB3b3VsZCBpbXBseSB0aGF0IGEgc3VicG9wdWxhdGlvbiBjb250YWlucyBtb3JlIGNlbGxzIGluIHRoZSBjb3JyZXNwb25kaW5nIGNlbGwgY3ljbGUgcGhhc2UuIFRoZSBzYW1lIGxvZ2ljIGFwcGxpZXMgdG8gY29tcGFyaXNvbnMgYmV0d2VlbiB0cmVhdG1lbnQgY29uZGl0aW9ucywgYXMgZGVzY3JpYmVkIGluIENoYXB0ZXIgMTQuCgpgYGB7cn0KbGlicmFyeShzY3JhbikKbWFya2VycyA8LSBmaW5kTWFya2VycyhzY2UsIGdyb3Vwcz1zY2UkY2x1c3RlclN0Zywgc3Vic2V0LnJvdz1jeWNsaW4uZ2VuZXMsIAogICAgdGVzdC50eXBlPSJ3aWxjb3giLCBkaXJlY3Rpb249InVwIikKCiMgV2UgY2FuIGluZmVyIHRoYXQgY2x1c3RlciA0IGhhcyBtb3JlIGNlbGxzIGluIEcyL00gdGhhbiB0aGUgb3RoZXIgY2x1c3RlcnMsCiMgYmFzZWQgb24gaGlnaGVyIGV4cHJlc3Npb24gb2YgdGhlIGN5Y2xpbiBCJ3MuCm1hcmtlcnNbWzRdXSAlPiUgZGF0YS5mcmFtZSgpICU+JQoJdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImVuc2VtYmxfZ2VuZV9pZCIpICU+JQoJbGVmdF9qb2luKGRhdGEuZnJhbWUocm93RGF0YShzY2UpKVssIGMoImVuc2VtYmxfZ2VuZV9pZCIsICJTeW1ib2wiKV0sIGJ5PSJlbnNlbWJsX2dlbmVfaWQiKSAlPiUKCXNlbGVjdChTeW1ib2wsIFRvcCwgcC52YWx1ZSwgRkRSLCBzdW1tYXJ5LkFVQykKYGBgCgpEaXJlY3QgZXhhbWluYXRpb24gb2YgY3ljbGluIGV4cHJlc3Npb24gaXMgZWFzaWx5IHRvIHVuZGVyc3RhbmQsIGludGVycHJldCBhbmQgZXhwZXJpbWVudGFsbHkgdmFsaWRhdGUuIEhvd2V2ZXIsIGl0IGlzIGJlc3Qgc3VpdGVkIGZvciBzdGF0ZW1lbnRzIGFib3V0IHJlbGF0aXZlIGNlbGwgY3ljbGUgYWN0aXZpdHk7IGZvciBleGFtcGxlLCB3ZSB3b3VsZCBmaW5kIGl0IGRpZmZpY3VsdCB0byBhc3NpZ24gY2VsbCBjeWNsZSBwaGFzZSBpbiBGaWd1cmUgMTYuMSB3aXRob3V0IHRoZSBwcmVzZW5jZSBvZiBjbHVzdGVycyBzcGFubmluZyBhbGwgcGhhc2VzIHRvIHByb3ZpZGUgYmVuY2htYXJrcyBmb3Ig4oCcaGlnaOKAnSBhbmQg4oCcbG934oCdIGV4cHJlc3Npb24gb2YgZWFjaCBjeWNsaW4uIFdlIGFsc28gYXNzdW1lIHRoYXQgY3ljbGluIGV4cHJlc3Npb24gaXMgbm90IGFmZmVjdGVkIGJ5IGJpb2xvZ2ljYWwgcHJvY2Vzc2VzIG90aGVyIHRoYW4gdGhlIGNlbGwgY3ljbGUsIHdoaWNoIG1heSBiZSBhIHN0cm9uZyBhc3N1bXB0aW9uIGluIHNvbWUgY2FzZXMsIGUuZy4sIG1hbGlnbmFudCBjZWxscy4gVGhpcyBzdHJhdGVneSBpcyBzdHJvbmdseSBkZXBlbmRlbnQgb24gaGF2aW5nIGdvb2Qgc2VxdWVuY2luZyBjb3ZlcmFnZSBvZiB0aGUgY3ljbGlucywgd2hpY2ggaXMgbGVzcyBvZiBhbiBpc3N1ZSBmb3IgdGhlIHdob2xlLW9mLXRyYW5zY3JpcHRvbWUgbWV0aG9kcyBkZXNjcmliZWQgYmVsb3cgdGhhdCB1c2UgaW5mb3JtYXRpb24gZnJvbSBtb3JlIGdlbmVzLgoKIyMgVXNpbmcgdGhlIGN5Y2xvbmUoKSBjbGFzc2lmaWVyCgpUaGUgcHJlZGljdGlvbiBtZXRob2QgZGVzY3JpYmVkIGJ5IFNjaWFsZG9uZSBldCBhbC4gKDIwMTUpIGlzIGFub3RoZXIgYXBwcm9hY2ggZm9yIGNsYXNzaWZ5aW5nIGNlbGxzIGludG8gY2VsbCBjeWNsZSBwaGFzZXMuIFVzaW5nIGEgcmVmZXJlbmNlIGRhdGFzZXQsIHdlIGZpcnN0IGNvbXB1dGUgdGhlIHNpZ24gb2YgdGhlIGRpZmZlcmVuY2UgaW4gZXhwcmVzc2lvbiBiZXR3ZWVuIGVhY2ggcGFpciBvZiBnZW5lcy4gUGFpcnMgd2l0aCBjaGFuZ2VzIGluIHRoZSBzaWduIGFjcm9zcyBjZWxsIGN5Y2xlIHBoYXNlcyBhcmUgY2hvc2VuIGFzIG1hcmtlcnMuIENlbGxzIGluIGEgdGVzdCBkYXRhc2V0IGNhbiB0aGVuIGJlIGNsYXNzaWZpZWQgaW50byB0aGUgYXBwcm9wcmlhdGUgcGhhc2UsIGJhc2VkIG9uIHdoZXRoZXIgdGhlIG9ic2VydmVkIHNpZ24gZm9yIGVhY2ggbWFya2VyIHBhaXIgaXMgY29uc2lzdGVudCB3aXRoIG9uZSBwaGFzZSBvciBhbm90aGVyLiBUaGlzIGFwcHJvYWNoIGlzIGltcGxlbWVudGVkIGluIHRoZSBjeWNsb25lKCkgZnVuY3Rpb24gZnJvbSB0aGUgc2NyYW4gcGFja2FnZSwgd2hpY2ggYWxzbyBjb250YWlucyBwcmUtdHJhaW5lZCBzZXQgb2YgbWFya2VyIHBhaXJzIGZvciBtb3VzZSBhbmQgaHVtYW4gZGF0YS4KCmBgYHtyLCBldmFsPUZBTFNFfQpzZXQuc2VlZCgxMDApCmxpYnJhcnkoc2NyYW4pCmhzLnBhaXJzIDwtIHJlYWRSRFMoc3lzdGVtLmZpbGUoImV4ZGF0YSIsICJodW1hbl9jeWNsZV9tYXJrZXJzLnJkcyIsIAogICAgcGFja2FnZT0ic2NyYW4iKSkKCiMgVXNpbmcgRW5zZW1ibCBJRHMgdG8gbWF0Y2ggdXAgd2l0aCB0aGUgYW5ub3RhdGlvbiBpbiAnbW0ucGFpcnMnLgphc3NpZ25tZW50cyA8LSBjeWNsb25lKHNjZSwgaHMucGFpcnMsIGdlbmUubmFtZXM9cm93RGF0YShzY2UpJGVuc2VtYmxfZ2VuZV9pZCkgI8KgU0xPVwpgYGAKCldyaXRlIGFzc2lnbm1lbnRzIHRvIGZpbGUuCgpgYGB7ciwgZXZhbD1GQUxTRX0KdG1wRm4gPC0gc3ByaW50ZigiJXMvJXMvUm9iamVjdHMvJXNfc2NlX256X3Bvc3REZWNvbnYlc19jeWNsb25lLlJkcyIsIHByb2pEaXIsIG91dERpckJpdCwgc2V0TmFtZSwgc2V0U3VmKQpwcmludCh0bXBGbikKc2F2ZVJEUyhhc3NpZ25tZW50cywgZmlsZT10bXBGbikKYGBgCgpgYGB7ciwgZXZhbD1UUlVFfQp0bXBGbiA8LSBzcHJpbnRmKCIlcy8lcy9Sb2JqZWN0cy8lc19zY2VfbnpfcG9zdERlY29udiVzX2N5Y2xvbmUuUmRzIiwgcHJvakRpciwgb3V0RGlyQml0LCBzZXROYW1lLCBzZXRTdWYpCnByaW50KHRtcEZuKQphc3NpZ25tZW50cyA8LSByZWFkUkRTKGZpbGU9dG1wRm4pCmBgYAoKQ29weSBjZWxsIGN5Y2xlIGFzc2lnbm1lbnRzIHRvIFNDRSBvYmplY3Q6CgpgYGB7cn0KY29sRGF0YShzY2UpJHBoYXNlcyA8LSBhc3NpZ25tZW50cyRwaGFzZXMKYGBgCgpGb3IgZWFjaCBjZWxsLCBhIGhpZ2hlciBzY29yZSBmb3IgYSBwaGFzZSBjb3JyZXNwb25kcyB0byBhIGhpZ2hlciBwcm9iYWJpbGl0eSB0aGF0IHRoZSBjZWxsIGlzIGluIHRoYXQgcGhhc2UuIFdlIGZvY3VzIG9uIHRoZSBHMSBhbmQgRzIvTSBzY29yZXMgYXMgdGhlc2UgYXJlIHRoZSBtb3N0IGluZm9ybWF0aXZlIGZvciBjbGFzc2lmaWNhdGlvbi4KClRoZSBwbG90IGJlbG93IHNob3cgdGhlIGNlbGwgY3ljbGUgcGhhc2Ugc2NvcmVzIG9idGFpbmVkIGJ5IGFwcGx5aW5nIHRoZSBwYWlyLWJhc2VkIGNsYXNzaWZpZXIgb24gdGhlIGRhdGFzZXQuIEVhY2ggcG9pbnQgcmVwcmVzZW50cyBhIGNlbGwsIHBsb3R0ZWQgYWNjb3JkaW5nIHRvIGl0cyBzY29yZXMgZm9yIEcxIGFuZCBHMi9NIHBoYXNlcy4gCgpgYGB7cn0KcGxvdChhc3NpZ25tZW50cyRzY29yZSRHMSwgYXNzaWdubWVudHMkc2NvcmUkRzJNLAogICAgeGxhYj0iRzEgc2NvcmUiLCB5bGFiPSJHMi9NIHNjb3JlIiwgcGNoPTE2KQpgYGAKCkNlbGxzIGFyZSBjbGFzc2lmaWVkIGFzIGJlaW5nIGluIEcxIHBoYXNlIGlmIHRoZSBHMSBzY29yZSBpcyBhYm92ZSAwLjUgYW5kIGdyZWF0ZXIgdGhhbiB0aGUgRzIvTSBzY29yZTsgaW4gRzIvTSBwaGFzZSBpZiB0aGUgRzIvTSBzY29yZSBpcyBhYm92ZSAwLjUgYW5kIGdyZWF0ZXIgdGhhbiB0aGUgRzEgc2NvcmU7IGFuZCBpbiBTIHBoYXNlIGlmIG5laXRoZXIgc2NvcmUgaXMgYWJvdmUgMC41LiAKCmBgYHtyfQpjb2xMYWJlbHMoc2NlKSA8LSBjb2xEYXRhKHNjZSkkc291cmNlX25hbWUKdGFibGUoYXNzaWdubWVudHMkcGhhc2VzLCBjb2xMYWJlbHMoc2NlKSkKIyBwcm9wIHBlciBwaGFzZQpyb3VuZChwcm9wLnRhYmxlKHRhYmxlKGFzc2lnbm1lbnRzJHBoYXNlcywgY29sTGFiZWxzKHNjZSkpLDEpLDIpCiMgcHJvcCBwZXIgdHlwZQpyb3VuZChwcm9wLnRhYmxlKHRhYmxlKGFzc2lnbm1lbnRzJHBoYXNlcywgY29sTGFiZWxzKHNjZSkpLDIpLDIpCmBgYAoKYGBge3J9CnRzbmUxIDwtIHBsb3RUU05FKHNjZSwgY29sb3VyX2J5PSJwaGFzZXMiKSArIGZvbnRzaXplCnRzbmUxICsgZmFjZXRfd3JhcCguIH4gc2NlJHNvdXJjZV9uYW1lKQp0c25lMSArIGZhY2V0X3dyYXAoLiB+IHNjZSRwaGFzZXMpCmBgYAoKYGBge3J9CnVtYXAxIDwtIHBsb3RVTUFQKHNjZSwgY29sb3VyX2J5PSJwaGFzZXMiKSArIGZvbnRzaXplCnVtYXAxICsgZmFjZXRfd3JhcCguIH4gc2NlJHNvdXJjZV9uYW1lKQp1bWFwMSArIGZhY2V0X3dyYXAoLiB+IHNjZSRwaGFzZXMpCmBgYAoKIyMgUmVncmVzc2luZyBvdXQgY2VsbCBjeWNsZSBwaGFzZQoKRm9yIHNvbWUgdGltZSwgaXQgd2FzIHBvcHVsYXIgdG8gcmVncmVzcyBvdXQgdGhlIGNlbGwgY3ljbGUgcGhhc2UgcHJpb3IgdG8gZG93bnN0cmVhbSBhbmFseXNlcy4gVGhlIGFpbSB3YXMgdG8gcmVtb3ZlIHVuaW50ZXJlc3RpbmcgdmFyaWF0aW9uIGR1ZSB0byBjZWxsIGN5Y2xlLCB0aHVzIGltcHJvdmluZyByZXNvbHV0aW9uIG9mIG90aGVyIGJpb2xvZ2ljYWwgcHJvY2Vzc2VzIG9mIGludGVyZXN0LiBXZSBjb3VsZCBpbXBsZW1lbnQgdGhpcyBieSBwZXJmb3JtaW5nIGNlbGwgY3ljbGUgcGhhc2UgYXNzaWdubWVudCBhcyBkZXNjcmliZWQgYWJvdmUsIHRyZWF0aW5nIGVhY2ggcGhhc2UgYXMgYSBzZXBhcmF0ZSBiYXRjaCBhbmQgYXBwbHlpbmcgYW55IG9mIHRoZSBiYXRjaCBjb3JyZWN0aW9uIHN0cmF0ZWdpZXMuIFRoZSBtb3N0IGNvbW1vbiBhcHByb2FjaCBpcyB0byB1c2UgYSBsaW5lYXIgbW9kZWwgdG8gc2ltcGx5IHJlZ3Jlc3Mgb3V0IHRoZSBwaGFzZSBlZmZlY3QsIGUuZy4sIHZpYSByZWdyZXNzQmF0Y2hlcygpLgoKYGBge3J9CmxpYnJhcnkoYmF0Y2hlbG9yKQpzY2Uubm9jeWNsZSA8LSByZWdyZXNzQmF0Y2hlcyhzY2UsIGJhdGNoPXNjZSRwaGFzZXMpCgojwqBQQ0EKI3Bsb3RQQ0Eoc2NlLCBjb2xvdXJfYnkgPSAiU2FtcGxlLk5hbWUiKQpzY2Uubm9jeWNsZSA8LSBydW5QQ0EoCiAgc2NlLm5vY3ljbGUsCiAgZXhwcnNfdmFsdWVzID0gImNvcnJlY3RlZCIKKQpwIDwtIHBsb3RQQ0EoCiAgICBzY2Uubm9jeWNsZSwKICAgIGNvbG91cl9ieSA9ICJiYXRjaCIKKQpwCgojwqBUU05FCnNjZS5ub2N5Y2xlIDwtIHJ1blRTTkUoc2NlLm5vY3ljbGUsIGV4cHJzX3ZhbHVlcyA9ICJjb3JyZWN0ZWQiKQpwIDwtIHBsb3RUU05FKAogICAgc2NlLm5vY3ljbGUsCiAgICBjb2xvdXJfYnkgPSAiYmF0Y2giCikKcApgYGAKClRoYXQgc2FpZCwgd2UgZG8gbm90IGNvbnNpZGVyIGFkanVzdGluZyBmb3IgY2VsbCBjeWNsZSB0byBiZSBhIG5lY2Vzc2FyeSBzdGVwIGluIHJvdXRpbmUgc2NSTkEtc2VxIGFuYWx5c2VzLiBJbiBtb3N0IGFwcGxpY2F0aW9ucywgdGhlIGNlbGwgY3ljbGUgaXMgYSBtaW5vciBmYWN0b3Igb2YgdmFyaWF0aW9uLCBzZWNvbmRhcnkgdG8gZGlmZmVyZW5jZXMgYmV0d2VlbiBjZWxsIHR5cGVzLiBBbnkgYXR0ZW1wdCBhdCByZW1vdmFsIHdvdWxkIGFsc28gbmVlZCB0byBhc3N1bWUgdGhhdCB0aGUgY2VsbCBjeWNsZSBlZmZlY3QgaXMgb3J0aG9nb25hbCB0byBvdGhlciBiaW9sb2dpY2FsIHByb2Nlc3Nlcy4gRm9yIGV4YW1wbGUsIHJlZ3Jlc3Npb24gd291bGQgcG90ZW50aWFsbHkgcmVtb3ZlIGludGVyZXN0aW5nIHNpZ25hbCBpZiBjZWxsIGN5Y2xlIGFjdGl2aXR5IHZhcmllZCBhY3Jvc3MgY2x1c3RlcnMgb3IgY29uZGl0aW9ucywgd2l0aCBhIHByaW1lIGV4YW1wbGUgYmVpbmcgdGhlIGluY3JlYXNlZCBwcm9saWZlcmF0aW9uIG9mIGFjdGl2YXRlZCBUIGNlbGxzIChSaWNoYXJkIGV0IGFsLiAyMDE4KS4gV2Ugc3VnZ2VzdCBvbmx5IHBlcmZvcm1pbmcgY2VsbCBjeWNsZSBhZGp1c3RtZW50IG9uIGFuIGFzLW5lZWRlZCBiYXNpcyBpbiBwb3B1bGF0aW9ucyB3aXRoIGNsZWFyIGNlbGwgY3ljbGUgZWZmZWN0cy4KCl9fQ2hhbGxlbmdlX18gUmVtb3ZlIHRoZSBjZWxsIGN5Y2xlIGdlbmVzIGxpc3RlZCBpbiB0aGUgJ2NlbGwgY3ljbGUnIEdPIHRlcm0sIHBlcmZvcm0gUENBIGFuZCBwbG90IHQtU05FLgoKYGBge3J9CmxpYnJhcnkob3JnLkhzLmVnLmRiKQpjYy5nZW5lcyA8LSBzZWxlY3Qob3JnLkhzLmVnLmRiLCBrZXlzPSJHTzowMDA3MDQ5Iiwga2V5dHlwZT0iR09BTEwiLCBjb2x1bW49IkVOU0VNQkwiKQpsZW5ndGgoY2MuZ2VuZXMpCnNjZS51bmN5Y2xlIDwtIHNjZVshcm93RGF0YShzY2UpJGVuc2VtYmxfZ2VuZV9pZCAlaW4lIGNjLmdlbmVzJEVOU0VNQkwsXQoKI8KgUENBCnNjZS51bmN5Y2xlIDwtIHJ1blBDQSgKICBzY2UudW5jeWNsZSwKICBleHByc192YWx1ZXMgPSAibG9nY291bnRzIgopCnAgPC0gcGxvdFBDQSgKICAgIHNjZS51bmN5Y2xlLAogICAgY29sb3VyX2J5ID0gInBoYXNlcyIsCiAgICBzaXplX2J5ID0gInN1bSIsCiAgICBzaGFwZV9ieSA9ICJzb3VyY2VfbmFtZSIKKSAKcAoKI8KgVFNORQpzY2UudW5jeWNsZSA8LSBydW5UU05FKHNjZS51bmN5Y2xlLCBleHByc192YWx1ZXMgPSAibG9nY291bnRzIikKcCA8LSBwbG90VFNORSgKICAgIHNjZS51bmN5Y2xlLAogICAgY29sb3VyX2J5ID0gInBoYXNlcyIsCiAgICBzaXplX2J5ID0gInN1bSIsCiAgICBzaGFwZV9ieSA9ICJzb3VyY2VfbmFtZSIKKQpwCgojwqBVTUFQCnNjZS51bmN5Y2xlIDwtIHJ1blVNQVAoc2NlLnVuY3ljbGUsIGV4cHJzX3ZhbHVlcyA9ICJsb2djb3VudHMiKQpwIDwtIHBsb3RVTUFQKAogICAgc2NlLnVuY3ljbGUsCiAgICBjb2xvdXJfYnkgPSAicGhhc2VzIiwKICAgIHNpemVfYnkgPSAic3VtIiwKICAgIHNoYXBlX2J5ID0gInNvdXJjZV9uYW1lIgopCnAKYGBgCgpXcml0ZSBTQ0Ugb2JqZWN0IHRvIGZpbGUuCgpgYGB7ciwgZXZhbD1GQUxTRX0KdG1wRm4gPC0gc3ByaW50ZigiJXMvJXMvUm9iamVjdHMvJXNfc2NlX256X3Bvc3REZWNvbnYlc19jZWxsQ3ljbGUuUmRzIiwgcHJvakRpciwgb3V0RGlyQml0LCBzZXROYW1lLCBzZXRTdWYpCnByaW50KHRtcEZuKQpzYXZlUkRTKHNjZSwgZmlsZT10bXBGbikKYGBgCg==