1 Introduction

1.1 Evolution of gene expression measurements

Image credit: @BoXia7

Image credit: @BoXia7



1.2 Exciting applications

Hwang et al. 2018, doi: 10.1038/s12276-018-0071-8

Hwang et al. 2018, doi: 10.1038/s12276-018-0071-8



1.3 Bulk vs single cell

1.3.1 Protocol

Owens, 2012, doi: 10.1038/491027a

Owens, 2012, doi: 10.1038/491027a


  • Bulk RNA-seq datasets are typically characterized by a high sequencing depth, i.e., typically millions of reads are sequenced for each individual sample. This is possible since there are often only a limited number of samples being sequenced.

  • This is different for single-cell RNA-sequencing (scRNA-seq), where many individual cells (thousands to millions) are sequenced. Due to this high cell number, only a limited number of sequencing reads are typically available for each individual cell.

This difference in wetlab protocol will have a significant impact on the statistical analysis of the data. The data analysis is affected by the data-generating protocol, and one must carefully take this into consideration.



1.3.2 Variability in data

Image credit: Davide Risso

Image credit: Davide Risso



1.4 scRNA-seq Protocols

Griffiths et al., 2018, doi.org/10.15252/msb.20178046

Griffiths et al., 2018, doi.org/10.15252/msb.20178046



Image credit: Davide Risso

Image credit: Davide Risso



  • Full-length protocols (typically plate-based). Here, a count has a similar interpretation to bulk RNA-seq: the number of sequenced reads that mapped back to that genomic locus. The count is a proxy for the concentration of mRNA molecules in the cell.

  • UMI-based protocols (typically droplet-based). Here, one count corresponds to one observed mRNA molecule present in the cell. Thanks to the use of UMI barcodes, PCR artifacts are reduced. However, no full length information on transcript because with fragmentation based protocols only one fragment would have the UMI!

Both protocols suffer from a low ‘capture frequency’, i.e., the number of molecules present in the cell that have actually been captured and may therefore be observed.



1.5 Multiplexing

Kang et al. 2018, doi: 10.1038/nbt.4042

Kang et al. 2018, doi: 10.1038/nbt.4042



Experimental Design!!!

eh <- ExperimentHub()
## snapshotDate(): 2022-04-26
sce <- eh[["EH2259"]]
## 'getOption("repos")' replaces Bioconductor standard repositories, see
## '?repositories' for details
## 
## replacement repositories:
##     CRAN: https://cran.rstudio.com
## Bioconductor version 3.15 (BiocManager 1.30.18), R 4.2.3 (2023-03-15)
## Installing package(s) 'muscData'
## Installation paths not writeable, unable to update packages
##   path: /opt/R/4.2.3/lib/R/library
##   packages:
##     class, KernSmooth, lattice, rpart, spatial
## Old packages: 'askpass', 'BH', 'BiocManager', 'BiocParallel', 'Biostrings',
##   'bit', 'blob', 'bookdown', 'boot', 'broom', 'bslib', 'cachem', 'callr',
##   'car', 'cli', 'clue', 'cluster', 'codetools', 'colorspace', 'commonmark',
##   'cpp11', 'crayon', 'curl', 'data.table', 'DBI', 'dbplyr',
##   'DelayedMatrixStats', 'deldir', 'desc', 'digest', 'dplyr', 'dqrng', 'DT',
##   'dtplyr', 'ensembldb', 'evaluate', 'fansi', 'farver', 'fastmap',
##   'fontawesome', 'forcats', 'foreign', 'formatR', 'fs', 'gargle', 'generics',
##   'GenomeInfoDb', 'GenomicAlignments', 'GenomicFeatures', 'ggbeeswarm',
##   'ggplot2', 'ggpubr', 'ggrepel', 'ggsci', 'ggsignif', 'googledrive',
##   'googlesheets4', 'gtable', 'haven', 'highr', 'hms', 'htmltools',
##   'htmlwidgets', 'httpuv', 'httr', 'igraph', 'interp', 'IRanges', 'isoband',
##   'jpeg', 'jsonlite', 'KEGGREST', 'knitr', 'labeling', 'later', 'lifecycle',
##   'limma', 'lme4', 'locfit', 'lubridate', 'MASS', 'Matrix', 'MatrixGenerics',
##   'MatrixModels', 'matrixStats', 'mgcv', 'minqa', 'modelr', 'multcomp',
##   'mvtnorm', 'ncdf4', 'nlme', 'nnet', 'openssl', 'pbkrtest', 'pillar',
##   'pkgload', 'plotly', 'plyr', 'png', 'prettyunits', 'printr', 'processx',
##   'promises', 'ps', 'purrr', 'quantreg', 'R.methodsS3', 'R.oo', 'R.utils',
##   'Rcpp', 'RcppArmadillo', 'RcppEigen', 'RcppHNSW', 'RCurl', 'readr', 'readxl',
##   'rematch', 'renv', 'reprex', 'restfulr', 'rintrojs', 'rlang', 'rmarkdown',
##   'rprojroot', 'RSQLite', 'rstatix', 'rstudioapi', 'rtracklayer', 'rvest',
##   'sass', 'ScaledMatrix', 'scales', 'scuttle', 'shiny', 'SingleCellExperiment',
##   'sourcetools', 'sp', 'statmod', 'stringi', 'stringr', 'survival', 'sys',
##   'testthat', 'TH.data', 'tibble', 'tidyr', 'tidyselect', 'tidyverse',
##   'tinytex', 'tzdb', 'utf8', 'uuid', 'vctrs', 'viridis', 'viridisLite',
##   'vroom', 'waldo', 'withr', 'xfun', 'xgboost', 'XML', 'xml2', 'yaml', 'zoo'
## snapshotDate(): 2022-04-26
## see ?muscData and browseVignettes('muscData') for documentation
## downloading 1 resources
## retrieving 1 resource
## loading from cache
sce
## class: SingleCellExperiment 
## dim: 35635 29065 
## metadata(0):
## assays(1): counts
## rownames(35635): MIR1302-10 FAM138A ... MT-ND6 MT-CYB
## rowData names(2): ENSEMBL SYMBOL
## colnames(29065): AAACATACAATGCC-1 AAACATACATTTCC-1 ... TTTGCATGGTTTGG-1
##   TTTGCATGTCTTAC-1
## colData names(5): ind stim cluster cell multiplets
## reducedDimNames(1): TSNE
## mainExpName: NULL
## altExpNames(0):
plotReducedDim(sce, dimred="TSNE", colour_by="cell")

plotReducedDim(sce, dimred="TSNE", colour_by="stim")


  • Note, that we see huge effect of treatment. If we see such large effects we always have to be on our guard!

  • Cells of 8 patients were not stimulated or stimulated.

  • Cells of different patients could be hashed.

  • So all control cells were sequenced in a first run and all stimulated cells were on a second sequencing run.

  • So the large effect might be an effect of batch!



2 Typical Workflow

See OSCA book

Image credit: OSCA book

Image credit: OSCA book



2.1 Preprocessing

Additional challenges arise as compared to bulk RNA-seq.

  • Cellular barcode identification: Errors occurring during PCR amplification and sequencing can corrupt the cellular barcode sequence.

    • Alevin-fry only quantifies cells reliably assigned as non-empty droplets by looking for the “knee” inflection point in the CDF of the total number of UMIs for each droplet.

    • Once this list of barcodes is generated, reads with cellular barcodes not in this set are “corrected” against it by checking if they are within one edit of the set.

  • Cell demultiplexing:

    • Cell Hashing: Cellular barcode identification from HTO counts.
    • Biological variation: Model-based clustering of cells to individuals.
  • Processing thousands-millions of cells

  • UMI identification and collapsing:

    • Determine the set of genes corresponding to all reads of a particular UMI.
    • Often, a UMI count is assigned to the gene with highest frequency amongst all reads having that UMI. Also here, UMI barcodes may become corrupted.



2.2 Single cell experiment class

Image credit: OSCA book

Image credit: OSCA book



Why use a SingleCellExperiment object?

  • Convenient framework to store all essential (meta)data in one object.
  • All different (meta)data structures are linked: avoids confusion on whether, e.g., the cluster labels and the count matrix are identically sorted.
  • Filtering of genes or cells immediately also filters the corresponding metadata.
  • Most Bioconductor software accepts a SingleCellExperiment object as input, and outputs a SingleCellExperiment object $ rightarrow$ one common infrastructure across entire analysis workflow.
  • Please see the OSCA chapter on SingleCellExperiment class for more information and how to use it (please try and get familiar!).



2.2.1 Example

We illustrate these steps using a dataset on Peripheral Blood Mononuclear Cell (PBMC) data from a healthy donor provided by 10X Genomics. We use the DropletTestFiles Bioconductor package to download the raw (i.e., unfiltered) count matrix that contains the UMI counts of all genes in all droplets.

raw.path <- getTestFile("tenx-2.1.0-pbmc4k/1.0.0/raw.tar.gz")
## snapshotDate(): 2022-04-26
## see ?DropletTestFiles and browseVignettes('DropletTestFiles') for documentation
## downloading 1 resources
## retrieving 1 resource
## loading from cache
out.path <- file.path(tempdir(), "pbmc4k")
untar(raw.path, exdir=out.path)

fname <- file.path(out.path, "raw_gene_bc_matrices/GRCh38")
sce <- read10xCounts(fname, col.names=TRUE)
sce
## class: SingleCellExperiment 
## dim: 33694 737280 
## metadata(1): Samples
## assays(1): counts
## rownames(33694): ENSG00000243485 ENSG00000237613 ... ENSG00000277475
##   ENSG00000268674
## rowData names(2): ID Symbol
## colnames(737280): AAACCTGAGAAACCAT-1 AAACCTGAGAAACCGC-1 ...
##   TTTGTCATCTTTAGTC-1 TTTGTCATCTTTCCTC-1
## colData names(2): Sample Barcode
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):

The read10xCounts function starts from the output of the Cell Ranger software and imports the data into R as an object of class SingleCellExperiment.

We can notice that the dimension of the matrix is very big; in fact this matrix includes the UMI that have been detected in all the droplets that have been sequenced, including the empty droplets that may contain only ambient RNA.

This is a very sparse matrix, with a large fraction of zeros; read10xCounts is aware of this and stores the counts as a sparse matrix, which has a very small memory footprint.



2.2.2 Explore SingleCellExperiment object

sce %>% 
  counts %>% 
  head(n=c(6,6))
## 6 x 6 sparse Matrix of class "dgCMatrix"
##                 AAACCTGAGAAACCAT-1 AAACCTGAGAAACCGC-1 AAACCTGAGAAACCTA-1
## ENSG00000243485                  .                  .                  .
## ENSG00000237613                  .                  .                  .
## ENSG00000186092                  .                  .                  .
## ENSG00000238009                  .                  .                  .
## ENSG00000239945                  .                  .                  .
## ENSG00000239906                  .                  .                  .
##                 AAACCTGAGAAACGAG-1 AAACCTGAGAAACGCC-1 AAACCTGAGAAAGTGG-1
## ENSG00000243485                  .                  .                  .
## ENSG00000237613                  .                  .                  .
## ENSG00000186092                  .                  .                  .
## ENSG00000238009                  .                  .                  .
## ENSG00000239945                  .                  .                  .
## ENSG00000239906                  .                  .                  .
sce %>% 
  counts %>% 
  class
## [1] "dgCMatrix"
## attr(,"package")
## [1] "Matrix"
sce %>% 
  assays
## List of length 1
## names(1): counts
sce %>% 
  rowData %>% 
  head
## DataFrame with 6 rows and 2 columns
##                              ID        Symbol
##                     <character>   <character>
## ENSG00000243485 ENSG00000243485  RP11-34P13.3
## ENSG00000237613 ENSG00000237613       FAM138A
## ENSG00000186092 ENSG00000186092         OR4F5
## ENSG00000238009 ENSG00000238009  RP11-34P13.7
## ENSG00000239945 ENSG00000239945  RP11-34P13.8
## ENSG00000239906 ENSG00000239906 RP11-34P13.14
sce %>% 
  colData %>% 
  head
## DataFrame with 6 rows and 2 columns
##                                    Sample            Barcode
##                               <character>        <character>
## AAACCTGAGAAACCAT-1 /tmp/RtmpqbhPBi/pbmc.. AAACCTGAGAAACCAT-1
## AAACCTGAGAAACCGC-1 /tmp/RtmpqbhPBi/pbmc.. AAACCTGAGAAACCGC-1
## AAACCTGAGAAACCTA-1 /tmp/RtmpqbhPBi/pbmc.. AAACCTGAGAAACCTA-1
## AAACCTGAGAAACGAG-1 /tmp/RtmpqbhPBi/pbmc.. AAACCTGAGAAACGAG-1
## AAACCTGAGAAACGCC-1 /tmp/RtmpqbhPBi/pbmc.. AAACCTGAGAAACGCC-1
## AAACCTGAGAAAGTGG-1 /tmp/RtmpqbhPBi/pbmc.. AAACCTGAGAAAGTGG-1



2.2.3 Use sensible gene names

Before starting the analysis, it may be a good idea to store the names of the genes in a more human-friendly ID system. We can also include information on the chromosome location of the genes; this will be useful for e.g. identifying mitochondrial genes.

library(scuttle)
rownames(sce) <- uniquifyFeatureNames(
    rowData(sce)$ID, rowData(sce)$Symbol)

rowData(sce)$location <- mapIds(EnsDb.Hsapiens.v86,
                                     keys=rowData(sce)$ID, 
                                     column="SEQNAME", keytype="GENEID")
## Warning: Unable to map 144 of 33694 requested IDs.
rowData(sce)$location[is.na(rowData(sce)$location)] <- "NA"
rowData(sce)
## DataFrame with 33694 rows and 3 columns
##                           ID       Symbol    location
##                  <character>  <character> <character>
## RP11-34P13.3 ENSG00000243485 RP11-34P13.3           1
## FAM138A      ENSG00000237613      FAM138A           1
## OR4F5        ENSG00000186092        OR4F5           1
## RP11-34P13.7 ENSG00000238009 RP11-34P13.7           1
## RP11-34P13.8 ENSG00000239945 RP11-34P13.8           1
## ...                      ...          ...         ...
## AC233755.2   ENSG00000277856   AC233755.2  KI270726.1
## AC233755.1   ENSG00000275063   AC233755.1  KI270726.1
## AC240274.1   ENSG00000271254   AC240274.1  KI270711.1
## AC213203.1   ENSG00000277475   AC213203.1  KI270713.1
## FAM231B      ENSG00000268674      FAM231B  KI270713.1



2.3 Filter non-informative genes

keep <- rowSums(assays(sce)$counts > 0) > 10
table(keep)
FALSE TRUE
19706 13988
sce <- sce[keep,]



2.4 Detection and removal of empty droplets

The first step is the identification of droplets that do not contain any live cell.

The reason why these droplets contain some RNA is that there may be some ambient RNA due to some cell leaking or they may contain dead or dying cells.



2.4.1 Example

The barcodeRanks function can be used to rank the barcodes by number of UMIs and to estimate the knee and inflection point of the distribution.

bcrank <- barcodeRanks(counts(sce))

# Only showing unique points for plotting speed.
uniq <- !duplicated(bcrank$rank)
plot(bcrank$rank[uniq], bcrank$total[uniq], log="xy",
    xlab="Rank", ylab="Total UMI count", cex.lab=1.2)
## Warning in xy.coords(x, y, xlabel, ylabel, log): 1 y value <= 0 omitted from
## logarithmic plot
abline(h=metadata(bcrank)$inflection, col="darkgreen", lty=2)
abline(h=metadata(bcrank)$knee, col="dodgerblue", lty=2)

legend("bottomleft", legend=c("Inflection", "Knee"), 
        col=c("darkgreen", "dodgerblue"), lty=2, cex=1.2)


  • There is a sharp distinction between droplets with very high counts, very likely to contain a live cell, and droplets with very low counts, very likely to be empty.

  • However, it is not straightforward to classify the droplets in the middle of the distribution.

  • We can apply a statistical test of hypothesis to decide, for each droplet, if its RNA profile is significantly different from the profile of ambient RNA, estimated from the very low counts (Aaron TL Lun et al. 2019).

  • We use a very low threshold on the False Discovery Rate to have very few false positive cells.

set.seed(100)
e.out <- emptyDrops(counts(sce))
summary(e.out$FDR <= 0.001)
Mode FALSE TRUE NA’s
logical 988 4300 731992



The large majority of droplets are not tested, since by default all droplets with fewer than 100 UMIs are considered empty.

table(colSums(counts(sce))>100, e.out$FDR<=0.001, useNA = "ifany")
/ FALSE TRUE NA
FALSE 0 0 731992
TRUE 988 4300 0



We can now proceed by removing the empty droplets and keep only the ones identified to be cells.

sce <- sce[,which(e.out$FDR <= 0.001)]
sce
## class: SingleCellExperiment 
## dim: 13988 4300 
## metadata(1): Samples
## assays(1): counts
## rownames(13988): FO538757.2 AP006222.2 ... AC004556.1 AC240274.1
## rowData names(3): ID Symbol location
## colnames(4300): AAACCTGAGAAGGCCT-1 AAACCTGAGACAGACC-1 ...
##   TTTGTCAGTTAAGACA-1 TTTGTCATCCCAAGAT-1
## colData names(2): Sample Barcode
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):



2.5 Quality control

Some cells have poor quality (e.g., damaged or stressed cells).

  • Expression of mitochondrial genes, total number of genes expressed, read distribution across genes and library size are good indicators for a cell’s quality.



2.5.1 Concerns

One concern is that we would inadvertently remove, e.g., an entire cell type from the downstream analysis. QC variables may not be independent of biological state. There is a trade-off between only working with high-quality cells and risk of removing biological signal.


2.5.2 Criteria

  • A high-quality cell should have

    • A high number of genes expressed
    • A fair library size
    • No major expression of mitochondrial genes, relative to other genes
  • Based on this information, we could set a fixed threshold, e.g., “keep cells with 4000 genes expressed, a library size of at least 10000 and fewer than 5% of the total output coming from mitochondrial genes”.

    • Requires substantial experience with the biological system and scRNA-seq data, e.g., tumors typically express a relatively higher amount of mitochondrial genes.
  • A better approach is to use an adaptive threshold.

    • Again assumes most cells are high-quality cells \(\rightarrow\) identification of outliers allows identification of low-quality cells.
    • For example, for removing low library size cells in the OSCA book they use

\[I(N_i < median(\mathbf{N})-3 \times MAD(\mathbf{N}))\] with \(N_i\) the library size of cell i and \(\mathbf{N}=[N_1,\ldots,N_m]\) with \(m\) the number of cells.



2.5.3 Example

The perCellQCMetrics function can be used to compute a set of metrics useful to evaluate the quality of the samples. The isOutlier function uses a data driven threshold to define cells of lower quality compared to the rest of the dataset.

stats <- perCellQCMetrics(sce,
            subsets=list(Mito=which(rowData(sce)$location=="MT")))

High-quality cells should have many features expressed, and a low contribution of mitochondrial genes. Here, we see that several cells have a very low number of expressed genes, and where most of the molecules are derived from mitochondrial genes. This indicates likely damaged cells, presumably because of loss of cytoplasmic RNA from perforated cells, so they have to be removed for the downstream analysis.

colData(sce) <- cbind(colData(sce), stats)
plotColData(sce, x = "sum", y="detected")

plotColData(sce, x = "detected", y="subsets_Mito_percent")


We normally remove cells that are outlying with respect to

  1. A low sequencing depth (number of UMIs);
  2. A low number of genes detected;
  3. A high percentage of reads from mitochondrial genes.
lowFeatures <- isOutlier(stats$detected, type="lower", log=TRUE)
lowLib <- isOutlier(stats$sum, type="lower", log=TRUE)
highMito <- isOutlier(stats$subsets_Mito_percent, type="higher")
table(lowFeatures)
FALSE TRUE
4006 294
table(lowLib)
FALSE TRUE
3973 327
table(highMito)
FALSE TRUE
3990 310



discardCells <- (lowLib|lowFeatures|highMito)
table(discardCells)
FALSE TRUE
3864 436
colData(sce)$discardCells <- discardCells



scater::plotColData(sce, y="subsets_Mito_percent",
        colour_by="discardCells")

scater::plotColData(sce, y="detected",
        colour_by="discardCells")

scater::plotColData(sce, y="sum",
        colour_by="discardCells")

plotColData(sce, x = "detected", y="subsets_Mito_percent", colour_by = "discardCells")



2.6 Detection and removal of doublets

Not all droplets contain a single cell; some contain >1 cell.

Doublet cell clusters may be mistaken for intermediate/transitory states.



2.6.1 Methods

knitr::include_graphics("./images_sequencing/scDblFinder.png")
Germain et al. 2021, doi: 10.12688/f1000research.73600.2

Germain et al. 2021, doi: 10.12688/f1000research.73600.2



  1. PCA on log-normalized expression counts.
  2. Randomly select two cells, sum their counts and normalize, and project into PCA space.
  3. Repeat step 2 many times.
  4. For each cell, check its neighborhood to simulated doublets as compared to original cells.
  5. Use this information, along with other predictors, to train a classifier for doublets.



2.6.2 Concerns

A potential concern is that we would inadvertently remove biological single cells, that may have e.g. a low transcriptome complexity. There is a trade-off between only working with high-quality cells and risk of removing biological signal.


2.6.3 Example

dbl.dens <- computeDoubletDensity(sce)
sce$DoubletScore <- dbl.dens
summary(dbl.dens)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0 0.1548 0.3182 0.692634 0.7052 20.124
dbl.calls <- doubletThresholding(data.frame(score=dbl.dens),
    method="griffiths", returnType="call")
summary(dbl.calls)
## singlet doublet 
##    3651     649



sce <- sce[, dbl.calls == "singlet"]
sce
## class: SingleCellExperiment 
## dim: 13988 3651 
## metadata(1): Samples
## assays(1): counts
## rownames(13988): FO538757.2 AP006222.2 ... AC004556.1 AC240274.1
## rowData names(3): ID Symbol location
## colnames(3651): AAACCTGAGAAGGCCT-1 AAACCTGAGACAGACC-1 ...
##   TTTGTCAGTTAAGACA-1 TTTGTCATCCCAAGAT-1
## colData names(10): Sample Barcode ... discardCells DoubletScore
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):



Note that, similarly to what we have said for the removal of low-quality cells, one should be careful in removing putative doublets. Indeed, it may be difficult to distinguish between rare transient cell populations and doublet cells.



2.7 Normalisation

Normalization aims to remove technical effects such as sequencing depth so that comparisons between cells are not confounded by them.

As in bulk RNA-seq, the most commonly used methods are scaling normalizations, where a scaling factor (also called size factor, normalization factor) is estimated for each cell.


2.7.1 Concerns

  • Some cell types may naturally have fewer mRNA molecules and therefore a lower library size. Removal of sequencing depth effects across cells therefore also risks removing some biological signal.

  • The assumption of a cell-specific scaling factor is that any technical effect it supposedly accounts for, affects all genes equally through a scaling of the mean count. This is not necessarily realistic.

  • Scaling the data and log-transforming the data is not a good option. Use offsets!

knitr::include_graphics("./images_sequencing/glmPCAFig2.png")
Townes et al. 2019, DOI: 10.1186/s13059-019-1861-6

Townes et al. 2019, DOI: 10.1186/s13059-019-1861-6



2.7.2 Offsets

Let \(y_{gi}\) be the expression for gene \(g\) in cell \(i\). Assume, as in bulk RNA-seq that

\(E[y_{gi}] = \mu_{gi} = \pi_{gi} s_iN_i\)

In the edgeR model, \(s_iN_i\) is referred to as the ‘effective library size’, which is used as offset in the GLM, thereby effectively focussing the statistical inference on \(\pi_{gi}\).

Lun et al. 2016, DOI: 10.1186/s13059-016-0947-7

Lun et al. 2016, DOI: 10.1186/s13059-016-0947-7


  • TMM and DESeq2 normalisation are largely driven by a few genes without zero counts!

Popular, bespoke single-cell normalization methods attempt to alleviate these issues.

Lun et al. 2016, DOI: 10.1186/s13059-016-0947-7

Lun et al. 2016, DOI: 10.1186/s13059-016-0947-7



2.7.3 Example

Here, we use the scran method to normalize the data for differences in sequencing depth. This approach is based on the deconvolution of size factors estimated from pools of cells (Lun et all. 2016).

Since we have a heterogeneous cell population, we perform a quick clustering to make sure that we pool together cells that are not too different from each other.

library(scran)
cl <- quickCluster(sce)
table(cl)
1 2 3 4 5 6 7 8 9 10 11
204 203 364 262 547 999 190 233 399 124 126
sce <- computeSumFactors(sce, clusters = cl)
## Warning in (function (x, sizes, min.mean = NULL, positive = FALSE, scaling =
## NULL) : encountered non-positive size factor estimates
summary(sizeFactors(sce))
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0027868 0.7399026 0.9211244 1 1.148053 5.538119


We can check that the estimated library sizes are not too far from the library size factors, estimated from the total number of counts.

plot(librarySizeFactors(sce), sizeFactors(sce), xlab="Library size factor",
    ylab="Deconvolution size factor", log='xy', pch=16,
    col=as.integer(factor(cl)))
abline(a=0, b=1, col="red")



2.8 Feature selection

  • The curse of dimension: In high-dimensional data, all data points appear to be dissimilar (i.e., they are far apart), as the data become sparse in the high-dimensional space as dimensions increase (e.g., Euclidean distance).

  • What does this mean for scRNA-seq? Trying to recover the global structure of a dataset using all genes is not a good idea. Often, we perform a variable selection step, selecting genes which we believe to be highly informative for a dataset’s structure. One may view this as a first simple step of dimension reduction.

We will thus select genes which we hope are informative for recovering the biological structure. Prior to visualisation.


2.8.1 Concerns

Feature selection is crucial for dimension reduction. However, it remains subjective and there are no clear guidelines to follow. The amount of features to select depends on the complexity of your dataset, and the informativeness of each individual feature.

Many issues remain.



2.8.2 What defines an informative gene?

  • Genes that are not expressed in any cell are obviously uninformative.

  • Neither are genes that are highly expressed at a similar level in all cells. So what should we be looking for?

  • Genes with high variance. But note our previous observations on the mean-variance relationship.

  • Simply selecting features on high variance is common, but possibly not optimal.

  • Genes with high variance relative to their mean. A (rightly so) popular approach is to select genes that have a high variance with respect to their mean. Often, first an empirical mean-variance trend is fitted, upon which genes with the highest positive residuals are selected. Being intuitive, reasonable and fairly straight-forward, this method is widely used.

  • Genes with high deviance. See Townes et al. (2019) for feature selection based on Binomial deviance. Genes with a high deviance will most poorly fit a null model where the relative abundance is equal for all cells, which therefore are informative.



2.8.3 Example: feature selection based on high variance wrt mean

You need an assay log counts for this.

sce <- logNormCounts(sce)
dec <- modelGeneVar(sce)
varfit <- metadata(dec)
plot(varfit$mean, varfit$var, 
     xlab="Mean of log-expression",
    ylab="Variance of log-expression")
curve(varfit$trend(x), col="dodgerblue", add=TRUE, lwd=2)



Select highly variable genes with variance larger than expected based on the mean variance trend. We then have to select the top number of most variable genes. We can for instance take the top 10% of the most variable genes

hvg <- getTopHVGs(dec, prop=0.1)
head(hvg)
## [1] "LYZ"     "S100A9"  "S100A8"  "HLA-DRA" "CD74"    "TYROBP"
plot(varfit$mean, varfit$var, 
     col = c("orange", "darkseagreen3")[(names(varfit$mean) %in% hvg)+1],
     xlab="Mean of log-expression",
    ylab="Variance of log-expression")
curve(varfit$trend(x), col="dodgerblue", add=TRUE, lwd=2)
legend("topleft", 
       legend = c("Selected", "Not selected"), 
       col = c("darkseagreen3", "orange"),
       pch = 16,
       bty='n')



2.8.4 Genes with high deviance

See Townes et al. (2019) for feature selection based on Binomial deviance. Genes with a high deviance will most poorly fit a null model where the relative abundance is equal for all cells, which therefore are informative.

Note that the deviance for a GLM is defined as:

\[ D = 2 [l(\mathbf{y},\mathbf{y}) - l(\mathbf{y},\hat{\mathbf{\mu}})] \]

which is follows a \(\chi^2\) distribution with n degrees of freedom. Indeed, it is a LR test between the best model with a perfect model fit and the current model.

If the difference is significant, there is lack of fit.

Townes et al. 2019, DOI: 10.1186/s13059-019-1861-6

Townes et al. 2019, DOI: 10.1186/s13059-019-1861-6



2.8.5 Variance-stabilizing transformation (VST) using Pearson residuals

Lause et al. 2021 introduced the use of Pearson residuals to stabilize the variance (doi: 10.1186/s13059-021-02451-7).

  • When a model is fitted to each gene we can construct pearson residuals that correct for the mean variance relation.

  • Here we model \(y_{ig} \sim NB(\mu_{ig}=\pi_gs_iN_i,\phi_g)\)

  • Pearson residuals are then defined as

\[ e_i = \frac{y_{ig} - \mu_{g}}{\sqrt{\mu_{g} + \phi\mu_{g}^2}} \]

Indeed, the residuals are corrected for the mean variance relationship.

We can then assess if the residuals exhibit over excess variance with respect to the trend. If this is the case, the gene is informative. There is more variance than we expect under the intercept model.

\[ \hat\sigma^2 = \sum\limits_{i=1}^n \frac{e_i^2}{n-p} \] Note, that this one of the variance estimator of the quasi-negative binomial model (See Lund et al., 2012, DOI 10.1515/1544-6115.1826).

Genes with high variance are informative.

Note, that there is also a close connection to the method of Townes. Another type of residuals for GLMs are deviance residuals, i.e. 

\[ e_i^d = \sqrt{2 [l(y_{ig},y_{ig})-l(y_{ig},\hat\mu_{ig})]} \]

Another variance estimator is

\[ \hat\sigma^2 = \sum\limits_{i=1}^n \frac{(e_i^d)^2}{n-p} = \frac{D}{n-p} \] Note, that this is the other excess variance estimator for the quasi-negative binomial model. See Lund et al., 2012 (DOI 10.1515/1544-6115.1826).



2.9 Dimension reduction

We would like to be able to project the data to a low-dimensional space, while retaining as much information as possible. This will be useful downstream for, e.g., visualization, identification of batch effects, clustering, trajectory inference.

Many dimension reduction methods exist and this is a very active area of research in scRNA-seq. We will discuss the most common methods such as PCA, GLM-PCA, t-SNE and UMAP.


2.9.1 Concerns

Like feature selection, this remains a subjective step in the analysis. Several choices have to be made: input data, e.g., log-transformed data or not?; number of components to retain; parameter tuning; etc..



2.9.2 Principal Component Analysis

  • A DR method is linear when the reduced dimensions are a linear function of the original variables.

  • In PCA, each principal component is a linear combination of genes. See here for a geometric interpretation of PCA and for a PCA details

  • Typically, PCA is performed on log-transformed normalized counts. The log-transformation helps somewhat, but not completely, to account for the mean-variance relationship.

  • PCA works well for bulk RNA-seq data. However, the structure of scRNA-seq data is often too complex to be visualized by a small number of PCs.



2.9.2.1 Example

We do PCA on the highly variable genes.

sce <- runPCA(sce, subset_row = hvg)
plotPCA(sce)

Note that the subset_row argument ensures that we use the previously selected HVGs to compute the PCs. Also note that by default runPCA will store the PCs in the PCA slot, hence overwriting the previous PCs. In the tutorials we will see that we already use PCA during the QC for visualisation purposes. These results are based on a quick normalization and prior to removing noisy genes and cells. Hence, it is ok to overwrite them. In other cases, it may be reasonable to keep more than one set of PCs: this can be achieved by specifying a different slot name via the name argument.

Finally, note that by default scater will compute the top 50 PCs. This is a reasonable choice, but it may be a good idea to explore the variance explained by each component to decide the number of components to retain. An alternative is to use the denoisePCA function from scran that aims at selecting the number of PCs that explain biological variability.



2.9.3 GLM-PCA

A generalization of PCA to exponential family distributions is provide by Townes et al. 2019. PCA is based on squared/Euclidean distances (e.g.Townes et al. 2019), which has a close link to Gaussian likelihood, which is inappropriate for count data due to the mean-variance relationship. Therefore, PCA results risks to be dominated by highly expressed genes, which have large variance.

Townes et al. 2019, DOI: 10.1186/s13059-019-1861-6

Townes et al. 2019, DOI: 10.1186/s13059-019-1861-6


They argue that UMI data are multinomially distributed, which can be approximated by a Poisson distribution when conditioning on the library size and propose a generalisation of PCA.

Townes et al. 2019, DOI: 10.1186/s13059-019-1861-6

Townes et al. 2019, DOI: 10.1186/s13059-019-1861-6



Townes et al. 2019, DOI: 10.1186/s13059-019-1861-6

Townes et al. 2019, DOI: 10.1186/s13059-019-1861-6



  • Note, that full length plate based data scRNA-seq data are over disperse with respect to the Poisson distribution and therefore NB-PCA would be better.



2.9.4 GLM-PCA Example

GLM-PCA is implemented in the scry Bioconductor package; it uses a Poisson model by default, but this can be changed to use negative binomial, multinomial or binomial.

Unlike runPCA, the GLM-PCA function does not allow for a selection of genes. Hence, we will first create a filtered SingleCellExperiment object that contains only the HVGs.

Similarly to PCA, GLM-PCA needs the number of components (factors) that need to be calculated. Here, we compute 10 latent factors.

We use the minibatch argument to use only a random subset of observations to compute the gradient in the optimization algorithm. This speeds up computations and avoids memory problems in big datasets.


suppressPackageStartupMessages({
  library(scry)
})
set.seed(100000)
filtered <- sce[hvg,]
filtered <- GLMPCA(filtered, L=10, minibatch = "stochastic")
filtered
## class: SingleCellExperiment 
## dim: 874 3651 
## metadata(2): Samples glmpca
## assays(2): counts logcounts
## rownames(874): LYZ S100A9 ... RBAK-RBAKDN WRB
## rowData names(3): ID Symbol location
## colnames(3651): AAACCTGAGAAGGCCT-1 AAACCTGAGACAGACC-1 ...
##   TTTGTCAGTTAAGACA-1 TTTGTCATCCCAAGAT-1
## colData names(11): Sample Barcode ... DoubletScore sizeFactor
## reducedDimNames(2): PCA GLMPCA
## mainExpName: NULL
## altExpNames(0):


To visualize the first two components of GLM-PCA, we can use the plotReducedDim function from scater.

plotReducedDim(filtered, "GLMPCA")



2.9.5 NewWave

An alternative method is implemented in the NewWave package, which implements a negative binomial factor analysis model and uses a penalized likelihood to estimate the latent factors (Agostinis et al. 2022 DOI: 10.1093/bioinformatics/btac149).


2.9.5.1 NewWave Example

NewWave can also use minibatches, and if multiple CPUs are available parallel computing, to speed up computations. The n_gene_par, n_cell_par, and n_gene_disp arguments allow the user to choose how many observations to use to estimate the gene- and cell-specific parameters. The children argument allows one to use multiple cores; here we use four, but you should change it depending on how many cores your computer has (see parallel::detectCores()).

suppressPackageStartupMessages({
  library(NewWave)
})
set.seed(100000)
filtered <- newWave(filtered, K=10, children=4,
                    n_gene_disp = 100, n_gene_par = 100, 
                    n_cell_par = 100)
filtered
## class: SingleCellExperiment 
## dim: 874 3651 
## metadata(2): Samples glmpca
## assays(2): counts logcounts
## rownames(874): LYZ S100A9 ... RBAK-RBAKDN WRB
## rowData names(3): ID Symbol location
## colnames(3651): AAACCTGAGAAGGCCT-1 AAACCTGAGACAGACC-1 ...
##   TTTGTCAGTTAAGACA-1 TTTGTCATCCCAAGAT-1
## colData names(11): Sample Barcode ... DoubletScore sizeFactor
## reducedDimNames(3): PCA GLMPCA newWave
## mainExpName: NULL
## altExpNames(0):
plotReducedDim(filtered, "newWave")



2.9.6 TSNE and UMAP

  • Non-linear dimension reduction methods

  • Visualizations of reduced dimensions from linear dimension reduction methods are often ‘overcrowded’, and it is hard to see structure.

  • Non-linear dimension reduction methods can overcome this problem. As the name suggests, the reduced dimensions are a non-linear function of the observed data. We will not go into detail as to how these work under the hood, but provide a few guidelines for the most popular methods. Often, the top (10-50) PCs are provided as input.

  • t-SNE: Preserving local rather than global distances. Therefore, distances on a t-SNE reduced dimension plot can only be interpreted locally, i.e., cells that are close together in reduced dimension will have a similar transcriptome, but cells that are far away may not necessarily have a very distinct transcriptome.

  • UMAP: Claimed to be better than t-SNE on preserving global differences. Therefore, UMAP is also often used in analyses such as trajectory inference, where this is important.


2.9.6.1 Example

sce <- runTSNE(sce, dimred="PCA")
plotTSNE(sce)


  if(!"uwot" %in% installed.packages()[,1]) BiocManager::install("uwot")
## 'getOption("repos")' replaces Bioconductor standard repositories, see
## '?repositories' for details
## 
## replacement repositories:
##     CRAN: https://cran.rstudio.com
## Bioconductor version 3.15 (BiocManager 1.30.18), R 4.2.3 (2023-03-15)
## Installing package(s) 'uwot'
## also installing the dependencies 'FNN', 'RcppAnnoy', 'RcppProgress'
## Installation paths not writeable, unable to update packages
##   path: /opt/R/4.2.3/lib/R/library
##   packages:
##     class, KernSmooth, lattice, rpart, spatial
## Old packages: 'askpass', 'BH', 'BiocManager', 'BiocParallel', 'Biostrings',
##   'bit', 'blob', 'bookdown', 'boot', 'broom', 'bslib', 'cachem', 'callr',
##   'car', 'cli', 'clue', 'cluster', 'codetools', 'colorspace', 'commonmark',
##   'cpp11', 'crayon', 'curl', 'data.table', 'DBI', 'dbplyr',
##   'DelayedMatrixStats', 'deldir', 'desc', 'digest', 'dplyr', 'dqrng', 'DT',
##   'dtplyr', 'ensembldb', 'evaluate', 'fansi', 'farver', 'fastmap',
##   'fontawesome', 'forcats', 'foreign', 'formatR', 'fs', 'gargle', 'generics',
##   'GenomeInfoDb', 'GenomicAlignments', 'GenomicFeatures', 'ggbeeswarm',
##   'ggplot2', 'ggpubr', 'ggrepel', 'ggsci', 'ggsignif', 'googledrive',
##   'googlesheets4', 'gtable', 'haven', 'highr', 'hms', 'htmltools',
##   'htmlwidgets', 'httpuv', 'httr', 'igraph', 'interp', 'IRanges', 'isoband',
##   'jpeg', 'jsonlite', 'KEGGREST', 'knitr', 'labeling', 'later', 'lifecycle',
##   'limma', 'lme4', 'locfit', 'lubridate', 'MASS', 'Matrix', 'MatrixGenerics',
##   'MatrixModels', 'matrixStats', 'mgcv', 'minqa', 'modelr', 'multcomp',
##   'mvtnorm', 'ncdf4', 'nlme', 'nnet', 'openssl', 'pbkrtest', 'pillar',
##   'pkgload', 'plotly', 'plyr', 'png', 'prettyunits', 'printr', 'processx',
##   'promises', 'ps', 'purrr', 'quantreg', 'R.methodsS3', 'R.oo', 'R.utils',
##   'Rcpp', 'RcppArmadillo', 'RcppEigen', 'RcppHNSW', 'RCurl', 'readr', 'readxl',
##   'rematch', 'renv', 'reprex', 'restfulr', 'rintrojs', 'rlang', 'rmarkdown',
##   'rprojroot', 'RSQLite', 'rstatix', 'rstudioapi', 'rtracklayer', 'rvest',
##   'sass', 'ScaledMatrix', 'scales', 'scuttle', 'shiny', 'SingleCellExperiment',
##   'sourcetools', 'sp', 'statmod', 'stringi', 'stringr', 'survival', 'sys',
##   'testthat', 'TH.data', 'tibble', 'tidyr', 'tidyselect', 'tidyverse',
##   'tinytex', 'tzdb', 'utf8', 'uuid', 'vctrs', 'viridis', 'viridisLite',
##   'vroom', 'waldo', 'withr', 'xfun', 'xgboost', 'XML', 'xml2', 'yaml', 'zoo'
sce <- runUMAP(sce, dimred = 'PCA', external_neighbors=TRUE)
plotUMAP(sce)



2.10 Clustering: unsupervised cell type identification

  • The identification of homogeneous, biologically relevant entities that define a common cell identity.

  • Traditional clustering methods do not perform great, often graph-based clustering or ensemble methods are used.


2.10.1 Concerns

  • There are many resolutions at which this can be done, and there is no such thing as an optimal cell type resolution; cells can be described by a hierarchy of different cell types and states. More clusters may result in higher resolution cell types, but can hamper replicability of the results across datasets.

2.11 (Semi-)supervised cell type identification

  • We can use prior knowledge to reliably identify cell clusters and annotate them.

  • We can annotate a new scRNA-seq dataset by training a classifier that predicts cell type label based on existing data.

    1. Gather reference data that are relevant for your current dataset you would like to annotate.
    2. Train a classifier (lasso/ridge, SVM, neural net, kNN, …) to classify cells into cell types, given gene expression data.
    3. Use pre-trained classifier to estimate, for each cell, P(cell i is cell type Z | gene expression of cell i).


2.11.1 Concerns

The reference data used for training the classifier must be representative for the dataset we would like to annotate. Problems may occur if e.g., different technologies are used, or particular (rare) cell types are not present in the reference data.



3 Differential expression (DE), discovery of marker genes

  • DE is often a first step towards interpreting differences between cell groups, and provides a basis for further biological validation.

  • DE analysis in scRNA-seq is typically similar to bulk RNA-seq, e.g. edgeR analysis. In fact, due to the high number of cells, often simpler methods may be used, e.g. Wilcoxon tests.


3.1 Concerns

  • There is an ongoing debate in the community about the appropriate noise model for scRNA-seq data. For example, are the data zero inflated?

  • Post-selection inference problem, the data are used twice

    1. Cluster
    2. Find biomarkers between clusters
  • Pseudo-replication in multi-subject-multi-cell experiments!


3.2 Zero-inflation

knitr::include_graphics("./images_sequencing/zeroInflation.png")




  • Motivation for zero-inflation is that we may consider two types of zeros in scRNA-seq data:

    • Biological zeros: We observe a zero count because the gene is not expressed in that cell.
    • Technical zeros: Even though the gene was expressed in that cell, we do not observe any molecules due to technical reasons (e.g., low capture efficiency / sequencing depth).
  • A zero-inflated count distribution is a mixture distribution that consists of a point mass at zero and a count component. For example, a zero-inflated negative binomial (ZINB) distribution.

\[ \begin{array}{lcl} Y_{ig} & \sim & ZINB(\mu_{ig}, \phi_g, \pi_{ig})\\\\ f(Y_{ig}) &=& \pi_{ig} \delta_0 + (1-\pi_{ig}) f_{NB}(\mu_{ig},\pi_{ig}) \end{array} \]

Our take:

  • Data from full-length protocols consist of high counts and many zeros, and are more likely to be zero inflated.

  • Instead, data from UMI droplet-based protocols consist of low counts with manyzeros. This seems to reasonably fit under a negative binomial distribution, and zero inflation may not be necessary.


3.3 Pseudo-replication in multi-cell-multi-patient scRNA-seq data

  • In multi-patient datasets, we have a hierarchical correlation structure.

  • Cells are nested within patient \(\rightarrow\) Expect correlation between cells from the same patient.

Image credit Milan Malfait

Image credit Milan Malfait


  • When assessing differences in means within cell types between different treatments, we can bulk the cells of the same cell types to perform pseudobulks per subject.

  • These pseudobulk samples can than be assessed using conventional bulk RNA-seq tools.

Crowell et al. 2020 (DOI: 10.1038/s41467-020-19894-4)

Crowell et al. 2020 (DOI: 10.1038/s41467-020-19894-4)


Juntilla et al. 2022 (DOI 10.1093/bib/bbac286)

Juntilla et al. 2022 (DOI 10.1093/bib/bbac286)


Squair et al. 2021 (DOI 10.1038/s41467-021-25960-2)

Squair et al. 2021 (DOI 10.1038/s41467-021-25960-2)




3.4 Differential Distributions

  • A few methods exist that compare distributions between treatments.

  • But, they do not allow to interpret the difference in the distributions.

Acknowledgements

Thanks to Davide Risso for sharing his materials from the CSAMA 2022 workshop, which I used as the starting point for the R code in this document.

LS0tCnRpdGxlOiAnU2VxdWVuY2luZzogU2luZ2xlIENlbGwgUk5BLXNlcScKYXV0aG9yOiAiTGlldmVuIENsZW1lbnQgYW5kIEtvZW4gVmFuIGRlbiBCZXJnZSIKZGF0ZTogIkxhc3QgZWRpdGVkIG9uIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBsYXRleF9lbmdpbmU6IHhlbGF0ZXgKYWx3YXlzX2FsbG93X2h0bWw6IHRydWUKbGlua2NvbG9yOiBibHVlCnVybGNvbG9yOiBibHVlIApjaXRlY29sb3I6IGJsdWUKbGluay1jaXRhdGlvbnM6IHllcwoKLS0tCgpgYGB7ciBmdW5jdGlvbnMsIGluY2x1ZGU9RkFMU0V9CiMgQSBmdW5jdGlvbiBmb3IgY2FwdGlvbmluZyBhbmQgcmVmZXJlbmNpbmcgaW1hZ2VzCmZpZyA8LSBsb2NhbCh7CiAgICBpIDwtIDAKICAgIHJlZiA8LSBsaXN0KCkKICAgIGxpc3QoCiAgICAgICAgY2FwPWZ1bmN0aW9uKHJlZk5hbWUsIHRleHQpIHsKICAgICAgICAgICAgaSA8PC0gaSArIDEKICAgICAgICAgICAgcmVmW1tyZWZOYW1lXV0gPDwtIGkKICAgICAgICAgICAgcGFzdGUoIkZpZ3VyZSAiLCBpLCAiOiAiLCB0ZXh0LCBzZXA9IiIpCiAgICAgICAgfSwKICAgICAgICByZWY9ZnVuY3Rpb24ocmVmTmFtZSkgewogICAgICAgICAgICByZWZbW3JlZk5hbWVdXQogICAgICAgIH0pCn0pCmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGV2YWw9VFJVRX0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KGtuaXRyKQogIGxpYnJhcnkocm1hcmtkb3duKQogIGxpYnJhcnkoZ2dwbG90MikKICBsaWJyYXJ5KHByaW50cikKICBsaWJyYXJ5KGdyaWRFeHRyYSkKICBsaWJyYXJ5KHRpZHl2ZXJzZSkKICBsaWJyYXJ5KHBsb3RseSkKICBsaWJyYXJ5KGpwZWcpCiAgbGlicmFyeShncmlkKQogIGxpYnJhcnkoZ2dwdWJyKQogIGlmKCEic2NEYmxGaW5kZXIiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLDFdKSBCaW9jTWFuYWdlcjo6aW5zdGFsbCgic2NEYmxGaW5kZXIiKQogIGxpYnJhcnkoc2NEYmxGaW5kZXIpCn0pCmBgYAoKIyBJbnRyb2R1Y3Rpb24gCgojIyBFdm9sdXRpb24gb2YgZ2VuZSBleHByZXNzaW9uIG1lYXN1cmVtZW50cwoKYGBge3IgbGFyLCBmaWcuY2FwPSJJbWFnZSBjcmVkaXQ6IEBCb1hpYTciLCBvdXQud2lkdGg9IjEwMCUiLCBlY2hvPUZBTFNFfQpvcmdhbiA8LSByZWFkSlBFRygiLi9pbWFnZXNfc2VxdWVuY2luZy9zY1JOQS1zZXFfZnVsbE9yZ2FuLmpwZWciKQpidWxrIDwtIHJlYWRKUEVHKCIuL2ltYWdlc19zZXF1ZW5jaW5nL3NjUk5BLWJ1bGtSTkFzZXEuanBlZyIpCnNjIDwtIHJlYWRKUEVHKCIuL2ltYWdlc19zZXF1ZW5jaW5nL3NjUk5BLXNjUk5BLmpwZWciKQpzcGF0IDwtIHJlYWRKUEVHKCIuL2ltYWdlc19zZXF1ZW5jaW5nL3NjUk5BLXNwYXRpYWxSTkFzZXEuanBlZyIpCgpnIDwtIGdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKAogIHJhc3Rlckdyb2Iob3JnYW4sIGludGVycG9sYXRlPVRSVUUpLAogIHJhc3Rlckdyb2IoYnVsaywgaW50ZXJwb2xhdGU9VFJVRSksCiAgcmFzdGVyR3JvYihzYywgaW50ZXJwb2xhdGU9VFJVRSksCiAgcmFzdGVyR3JvYihzcGF0LCBpbnRlcnBvbGF0ZT1UUlVFKSwKICB0b3AgPSAib3JnYW4iLAogIGJvdHRvbSA9ICJCdWxrIFJOQS1zZXEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NSTkEtc2VxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BhdGlhbCBSTkEtc2VxIiwKICBuY29sPTExLAogIG5yb3c9MiwKICBsYXlvdXRfbWF0cml4ID0gcmJpbmQoCiAgICBjKE5BLCBOQSwgTkEsIE5BLCAxLCAxLCAxLCBOQSwgTkEsIE5BLCBOQSksCiAgICBjKDIsIDIsIDIsIE5BLCAzLCAzLCAzLCBOQSwgNCwgNCwgNCkpKSAKYGBgCgo8YnIvPjxici8+CgojIyBFeGNpdGluZyBhcHBsaWNhdGlvbnMgCgpgYGB7ciBhcHBsaWNhdGlvbnMsIG91dC53aWR0aD0iMTAwJSIsIGZpZy5jYXA9Ikh3YW5nIGV0IGFsLiAyMDE4LCBkb2k6IDEwLjEwMzgvczEyMjc2LTAxOC0wMDcxLTgiLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjEwMCUifQppbmNsdWRlX2dyYXBoaWNzKCIuL2ltYWdlc19zZXF1ZW5jaW5nL0FwcGxpY2F0aW9ucy1vZi1zY1JOQS5wbmciKQpgYGAKPGJyLz48YnIvPgoKIyMgQnVsayB2cyBzaW5nbGUgY2VsbAoKIyMjIFByb3RvY29sCgpgYGB7ciBidWxrVnNTYzEsIGZpZy5jYXA9Ik93ZW5zLCAyMDEyLCBkb2k6IDEwLjEwMzgvNDkxMDI3YSIsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iMTAwJSJ9CmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvb3dlbnNTaW5nbGVDZWxsLnBuZyIpCmBgYAo8YnIvPgoKLSBCdWxrIFJOQS1zZXEgZGF0YXNldHMgYXJlIHR5cGljYWxseSBjaGFyYWN0ZXJpemVkIGJ5IGEgaGlnaCBzZXF1ZW5jaW5nIGRlcHRoLCBpLmUuLCB0eXBpY2FsbHkgbWlsbGlvbnMgb2YgcmVhZHMgYXJlIHNlcXVlbmNlZCBmb3IgZWFjaCBpbmRpdmlkdWFsIHNhbXBsZS4gVGhpcyBpcyBwb3NzaWJsZSBzaW5jZSB0aGVyZSBhcmUgb2Z0ZW4gb25seSBhIGxpbWl0ZWQgbnVtYmVyIG9mIHNhbXBsZXMgYmVpbmcgc2VxdWVuY2VkLgoKLSBUaGlzIGlzIGRpZmZlcmVudCBmb3Igc2luZ2xlLWNlbGwgUk5BLXNlcXVlbmNpbmcgKHNjUk5BLXNlcSksIHdoZXJlIG1hbnkgaW5kaXZpZHVhbCBjZWxscyAodGhvdXNhbmRzIHRvIG1pbGxpb25zKSBhcmUgc2VxdWVuY2VkLiBEdWUgdG8gdGhpcyBoaWdoIGNlbGwgbnVtYmVyLCBvbmx5IGEgbGltaXRlZCBudW1iZXIgb2Ygc2VxdWVuY2luZyByZWFkcyBhcmUgdHlwaWNhbGx5IGF2YWlsYWJsZSBmb3IgZWFjaCBpbmRpdmlkdWFsIGNlbGwuCgpUaGlzIGRpZmZlcmVuY2UgaW4gd2V0bGFiIHByb3RvY29sIHdpbGwgaGF2ZSBhIHNpZ25pZmljYW50IGltcGFjdCBvbiB0aGUgc3RhdGlzdGljYWwgYW5hbHlzaXMgb2YgdGhlIGRhdGEuIFRoZSBkYXRhIGFuYWx5c2lzIGlzIGFmZmVjdGVkIGJ5IHRoZSBkYXRhLWdlbmVyYXRpbmcgcHJvdG9jb2wsIGFuZCBvbmUgbXVzdCBjYXJlZnVsbHkgdGFrZSB0aGlzIGludG8gY29uc2lkZXJhdGlvbi4KCjxici8+PGJyLz4KCiMjIyBWYXJpYWJpbGl0eSBpbiBkYXRhCgpgYGB7ciBidWxrVnNTYzIsIGZpZy5jYXA9IkltYWdlIGNyZWRpdDogRGF2aWRlIFJpc3NvIiwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSIxMDAlIn0KaW5jbHVkZV9ncmFwaGljcygiLi9pbWFnZXNfc2VxdWVuY2luZy9pbmNyZWFzZVZhcmlhYmlsaXR5Umlzc28ucG5nIikKYGBgCjxici8+PGJyLz4KCiMjIHNjUk5BLXNlcSBQcm90b2NvbHMKCmBgYHtyIHByb3RvY29scywgZmlnLmNhcD0iR3JpZmZpdGhzIGV0IGFsLiwgMjAxOCwgZG9pLm9yZy8xMC4xNTI1Mi9tc2IuMjAxNzgwNDYiLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjEwMCUifQppbmNsdWRlX2dyYXBoaWNzKCIuL2ltYWdlc19zZXF1ZW5jaW5nL3NjUk5BLXNlcVRlY2hub2xvZ2llcy5wbmciKQpgYGAKCjxici8+PGJyLz4KCmBgYHtyIHVtaTIsIGZpZy5jYXA9IkltYWdlIGNyZWRpdDogRGF2aWRlIFJpc3NvIiwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSIxMDAlIn0KaW5jbHVkZV9ncmFwaGljcygiLi9pbWFnZXNfc2VxdWVuY2luZy91bWlWc0Z1bGxMZW5naHRSaXNzby5wbmciKQpgYGAKCjxici8+PGJyLz4KCgotIEZ1bGwtbGVuZ3RoIHByb3RvY29scyAodHlwaWNhbGx5IHBsYXRlLWJhc2VkKS4gSGVyZSwgYSBjb3VudCBoYXMgYSBzaW1pbGFyIGludGVycHJldGF0aW9uIHRvIGJ1bGsgUk5BLXNlcTogdGhlIG51bWJlciBvZiBzZXF1ZW5jZWQgcmVhZHMgdGhhdCBtYXBwZWQgYmFjayB0byB0aGF0IGdlbm9taWMgbG9jdXMuIFRoZSBjb3VudCBpcyBhIHByb3h5IGZvciB0aGUgY29uY2VudHJhdGlvbiBvZiBtUk5BIG1vbGVjdWxlcyBpbiB0aGUgY2VsbC4KCi0gVU1JLWJhc2VkIHByb3RvY29scyAodHlwaWNhbGx5IGRyb3BsZXQtYmFzZWQpLiBIZXJlLCBvbmUgY291bnQgY29ycmVzcG9uZHMgdG8gb25lIG9ic2VydmVkIG1STkEgbW9sZWN1bGUgcHJlc2VudCBpbiB0aGUgY2VsbC4gVGhhbmtzIHRvIHRoZSB1c2Ugb2YgVU1JIGJhcmNvZGVzLCBQQ1IgYXJ0aWZhY3RzIGFyZSByZWR1Y2VkLiBIb3dldmVyLCBubyBmdWxsIGxlbmd0aCBpbmZvcm1hdGlvbiBvbiB0cmFuc2NyaXB0IGJlY2F1c2Ugd2l0aCBmcmFnbWVudGF0aW9uIGJhc2VkIHByb3RvY29scyBvbmx5IG9uZSBmcmFnbWVudCB3b3VsZCBoYXZlIHRoZSBVTUkhCgpCb3RoIHByb3RvY29scyBzdWZmZXIgZnJvbSBhIGxvdyDigJhjYXB0dXJlIGZyZXF1ZW5jeeKAmSwgaS5lLiwgdGhlIG51bWJlciBvZiBtb2xlY3VsZXMgcHJlc2VudCBpbiB0aGUgY2VsbCB0aGF0IGhhdmUgYWN0dWFsbHkgYmVlbiBjYXB0dXJlZCBhbmQgbWF5IHRoZXJlZm9yZSBiZSBvYnNlcnZlZC4KCjxici8+PGJyLz4KCiMjIE11bHRpcGxleGluZyAKCmBgYHtyIGthbmdNdWx0aXBsZXgsIGZpZy5jYXA9IkthbmcgZXQgYWwuIDIwMTgsIGRvaTogMTAuMTAzOC9uYnQuNDA0MiIsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iMTAwJSJ9CmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcva2FuZ011bHRpcGxleGluZy5wbmciKQpgYGAKCjxici8+PGJyLz4KCkV4cGVyaW1lbnRhbCBEZXNpZ24hISEKCmBgYHtyIGVjaG89RkFMU0V9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CmxpYnJhcnkoRXhwZXJpbWVudEh1YikKbGlicmFyeShzY2F0ZXIpCn0pCmBgYAoKYGBge3J9CmVoIDwtIEV4cGVyaW1lbnRIdWIoKQpzY2UgPC0gZWhbWyJFSDIyNTkiXV0Kc2NlCmBgYAoKYGBge3J9CnBsb3RSZWR1Y2VkRGltKHNjZSwgZGltcmVkPSJUU05FIiwgY29sb3VyX2J5PSJjZWxsIikKcGxvdFJlZHVjZWREaW0oc2NlLCBkaW1yZWQ9IlRTTkUiLCBjb2xvdXJfYnk9InN0aW0iKQpgYGAKCjxici8+CgotIE5vdGUsIHRoYXQgd2Ugc2VlIGh1Z2UgZWZmZWN0IG9mIHRyZWF0bWVudC4gSWYgd2Ugc2VlIHN1Y2ggbGFyZ2UgZWZmZWN0cyB3ZSBhbHdheXMgaGF2ZSB0byBiZSBvbiBvdXIgZ3VhcmQhCgotIENlbGxzIG9mIDggcGF0aWVudHMgd2VyZSBub3Qgc3RpbXVsYXRlZCAgb3Igc3RpbXVsYXRlZC4gCgotIENlbGxzIG9mIGRpZmZlcmVudCBwYXRpZW50cyBjb3VsZCBiZSBoYXNoZWQuIAoKLSBTbyBhbGwgY29udHJvbCBjZWxscyB3ZXJlIHNlcXVlbmNlZCBpbiBhIGZpcnN0IHJ1biBhbmQgYWxsIHN0aW11bGF0ZWQgY2VsbHMgd2VyZSBvbiBhIHNlY29uZCBzZXF1ZW5jaW5nIHJ1bi4gCgotIFNvIHRoZSBsYXJnZSBlZmZlY3QgbWlnaHQgYmUgYW4gZWZmZWN0IG9mIGJhdGNoIQoKPGJyLz48YnIvPgoKCiMgVHlwaWNhbCBXb3JrZmxvdwoKU2VlIFtPU0NBIGJvb2tdKGh0dHBzOi8vZ2l0aHViLmNvbS9PU0NBLXNvdXJjZS9PU0NBKQoKYGBge3Igb3NjYXdvcmtmbG93LCBmaWcuY2FwPSJJbWFnZSBjcmVkaXQ6IE9TQ0EgYm9vayIsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iMTAwJSJ9CmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvc2NSTkEtZGF0YS13b3JrZmxvdy5wbmciKQpgYGAKCjxici8+PGJyLz4KCiMjIFByZXByb2Nlc3NpbmcgCgpBZGRpdGlvbmFsIGNoYWxsZW5nZXMgYXJpc2UgYXMgY29tcGFyZWQgdG8gYnVsayBSTkEtc2VxLgoKLSBDZWxsdWxhciBiYXJjb2RlIGlkZW50aWZpY2F0aW9uOiBFcnJvcnMgb2NjdXJyaW5nIGR1cmluZyBQQ1IgYW1wbGlmaWNhdGlvbiBhbmQgc2VxdWVuY2luZyBjYW4gY29ycnVwdCB0aGUgY2VsbHVsYXIgYmFyY29kZSBzZXF1ZW5jZS4KCiAgLSBBbGV2aW4tZnJ5IG9ubHkgcXVhbnRpZmllcyBjZWxscyByZWxpYWJseSBhc3NpZ25lZCBhcyBub24tZW1wdHkgZHJvcGxldHMgYnkgbG9va2luZyBmb3IgdGhlIOKAnGtuZWXigJ0gaW5mbGVjdGlvbiBwb2ludCBpbiB0aGUgQ0RGIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgVU1JcyBmb3IgZWFjaCBkcm9wbGV0LgoKICAtIE9uY2UgdGhpcyBsaXN0IG9mIGJhcmNvZGVzIGlzIGdlbmVyYXRlZCwgcmVhZHMgd2l0aCBjZWxsdWxhciBiYXJjb2RlcyBub3QgaW4gdGhpcyBzZXQgYXJlIOKAnGNvcnJlY3RlZOKAnSBhZ2FpbnN0IGl0IGJ5IGNoZWNraW5nIGlmIHRoZXkgYXJlIHdpdGhpbiBvbmUgZWRpdCBvZiB0aGUgc2V0LgoKLSBDZWxsIGRlbXVsdGlwbGV4aW5nOiAKCiAgLSBDZWxsIEhhc2hpbmc6IENlbGx1bGFyIGJhcmNvZGUgaWRlbnRpZmljYXRpb24gZnJvbSBIVE8gY291bnRzLgogIC0gQmlvbG9naWNhbCB2YXJpYXRpb246IE1vZGVsLWJhc2VkIGNsdXN0ZXJpbmcgb2YgY2VsbHMgdG8gaW5kaXZpZHVhbHMuCgotIFByb2Nlc3NpbmcgdGhvdXNhbmRzLW1pbGxpb25zIG9mIGNlbGxzCgotIFVNSSBpZGVudGlmaWNhdGlvbiBhbmQgY29sbGFwc2luZzoKCiAgLSBEZXRlcm1pbmUgdGhlIHNldCBvZiBnZW5lcyBjb3JyZXNwb25kaW5nIHRvIGFsbCByZWFkcyBvZiBhIHBhcnRpY3VsYXIgVU1JLgogIC0gT2Z0ZW4sIGEgVU1JIGNvdW50IGlzIGFzc2lnbmVkIHRvIHRoZSBnZW5lIHdpdGggaGlnaGVzdCBmcmVxdWVuY3kgYW1vbmdzdCBhbGwgcmVhZHMgaGF2aW5nIHRoYXQgVU1JLiBBbHNvIGhlcmUsIFVNSSBiYXJjb2RlcyBtYXkgYmVjb21lIGNvcnJ1cHRlZC4KCjxici8+PGJyLz4KCiMjIFNpbmdsZSBjZWxsIGV4cGVyaW1lbnQgY2xhc3MgCgpgYGB7ciBzaW5nbGVDZWxsRXhwZXJpbWVudCwgZmlnLmNhcD0iSW1hZ2UgY3JlZGl0OiBPU0NBIGJvb2siLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjEwMCUifQppbmNsdWRlX2dyYXBoaWNzKCIuL2ltYWdlc19zZXF1ZW5jaW5nL1NpbmdsZUNlbGxFeHBlcmltZW50LnBuZyIpCmBgYAoKPGJyLz48YnIvPgoKV2h5IHVzZSBhIFNpbmdsZUNlbGxFeHBlcmltZW50IG9iamVjdD8KCi0gQ29udmVuaWVudCBmcmFtZXdvcmsgdG8gc3RvcmUgYWxsIGVzc2VudGlhbCAobWV0YSlkYXRhIGluIG9uZSBvYmplY3QuCi0gQWxsIGRpZmZlcmVudCAobWV0YSlkYXRhIHN0cnVjdHVyZXMgYXJlIGxpbmtlZDogYXZvaWRzIGNvbmZ1c2lvbiBvbiB3aGV0aGVyLCBlLmcuLCB0aGUgY2x1c3RlciBsYWJlbHMgYW5kIHRoZSBjb3VudCBtYXRyaXggYXJlIGlkZW50aWNhbGx5IHNvcnRlZC4KLSBGaWx0ZXJpbmcgb2YgZ2VuZXMgb3IgY2VsbHMgaW1tZWRpYXRlbHkgYWxzbyBmaWx0ZXJzIHRoZSBjb3JyZXNwb25kaW5nIG1ldGFkYXRhLgotIE1vc3QgQmlvY29uZHVjdG9yIHNvZnR3YXJlIGFjY2VwdHMgYSBTaW5nbGVDZWxsRXhwZXJpbWVudCBvYmplY3QgYXMgaW5wdXQsIGFuZCBvdXRwdXRzIGEgU2luZ2xlQ2VsbEV4cGVyaW1lbnQgb2JqZWN0ICQKcmlnaHRhcnJvdyQgb25lIGNvbW1vbiBpbmZyYXN0cnVjdHVyZSBhY3Jvc3MgZW50aXJlIGFuYWx5c2lzIHdvcmtmbG93LgotIFBsZWFzZSBzZWUgdGhlIFtPU0NBIGNoYXB0ZXIgb24gU2luZ2xlQ2VsbEV4cGVyaW1lbnQgY2xhc3NdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL2Jvb2tzLzMuMTQvT1NDQS5pbnRyby90aGUtc2luZ2xlY2VsbGV4cGVyaW1lbnQtY2xhc3MuaHRtbCkgZm9yIG1vcmUgaW5mb3JtYXRpb24gYW5kIGhvdyB0byB1c2UgaXQgKHBsZWFzZSB0cnkgYW5kIGdldCBmYW1pbGlhciEpLgoKPGJyLz48YnIvPgoKIyMjIEV4YW1wbGUgCgpXZSBpbGx1c3RyYXRlIHRoZXNlIHN0ZXBzIHVzaW5nIGEgZGF0YXNldCBvbiBQZXJpcGhlcmFsIEJsb29kIE1vbm9udWNsZWFyIENlbGwgKFBCTUMpIGRhdGEgZnJvbSBhIGhlYWx0aHkgZG9ub3IgcHJvdmlkZWQgYnkgMTBYIEdlbm9taWNzLiBXZSB1c2UgdGhlIERyb3BsZXRUZXN0RmlsZXMgQmlvY29uZHVjdG9yIHBhY2thZ2UgdG8gZG93bmxvYWQgdGhlIHJhdyAoaS5lLiwgdW5maWx0ZXJlZCkgY291bnQgbWF0cml4IHRoYXQgY29udGFpbnMgdGhlIFVNSSBjb3VudHMgb2YgYWxsIGdlbmVzIGluIGFsbCBkcm9wbGV0cy4KCgpgYGB7ciBlY2hvPUZBTFNFfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGlmKCEiRHJvcGxldFRlc3RGaWxlcyIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssMV0pIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJEcm9wbGV0VGVzdEZpbGVzIikKICBsaWJyYXJ5KERyb3BsZXRUZXN0RmlsZXMpCiAgCiAgaWYoISJEcm9wbGV0VXRpbHMiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLDFdKSBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiRHJvcGxldFV0aWxzIikKICBsaWJyYXJ5KERyb3BsZXRVdGlscykKICAKICBpZighIkVuc0RiLkhzYXBpZW5zLnY4NiIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssMV0pIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJFbnNEYi5Ic2FwaWVucy52ODYiKQogIGxpYnJhcnkoRW5zRGIuSHNhcGllbnMudjg2KQoKfSkKYGBgCgpgYGB7cn0KcmF3LnBhdGggPC0gZ2V0VGVzdEZpbGUoInRlbngtMi4xLjAtcGJtYzRrLzEuMC4wL3Jhdy50YXIuZ3oiKQpvdXQucGF0aCA8LSBmaWxlLnBhdGgodGVtcGRpcigpLCAicGJtYzRrIikKdW50YXIocmF3LnBhdGgsIGV4ZGlyPW91dC5wYXRoKQoKZm5hbWUgPC0gZmlsZS5wYXRoKG91dC5wYXRoLCAicmF3X2dlbmVfYmNfbWF0cmljZXMvR1JDaDM4IikKc2NlIDwtIHJlYWQxMHhDb3VudHMoZm5hbWUsIGNvbC5uYW1lcz1UUlVFKQpzY2UKYGBgCgpUaGUgcmVhZDEweENvdW50cyBmdW5jdGlvbiBzdGFydHMgZnJvbSB0aGUgb3V0cHV0IG9mIHRoZSBDZWxsIFJhbmdlciBzb2Z0d2FyZSBhbmQgaW1wb3J0cyB0aGUgZGF0YSBpbnRvIFIgYXMgYW4gb2JqZWN0IG9mIGNsYXNzIFNpbmdsZUNlbGxFeHBlcmltZW50LgoKV2UgY2FuIG5vdGljZSB0aGF0IHRoZSBkaW1lbnNpb24gb2YgdGhlIG1hdHJpeCBpcyB2ZXJ5IGJpZzsgaW4gZmFjdCB0aGlzIG1hdHJpeCBpbmNsdWRlcyB0aGUgVU1JIHRoYXQgaGF2ZSBiZWVuIGRldGVjdGVkIGluIGFsbCB0aGUgZHJvcGxldHMgdGhhdCBoYXZlIGJlZW4gc2VxdWVuY2VkLCBpbmNsdWRpbmcgdGhlIGVtcHR5IGRyb3BsZXRzIHRoYXQgbWF5IGNvbnRhaW4gb25seSBhbWJpZW50IFJOQS4KClRoaXMgaXMgYSB2ZXJ5IHNwYXJzZSBtYXRyaXgsIHdpdGggYSBsYXJnZSBmcmFjdGlvbiBvZiB6ZXJvczsgcmVhZDEweENvdW50cyBpcyBhd2FyZSBvZiB0aGlzIGFuZCBzdG9yZXMgdGhlIGNvdW50cyBhcyBhIHNwYXJzZSBtYXRyaXgsIHdoaWNoIGhhcyBhIHZlcnkgc21hbGwgbWVtb3J5IGZvb3RwcmludC4KCjxici8+PGJyLz4KCiMjIyBFeHBsb3JlIFNpbmdsZUNlbGxFeHBlcmltZW50IG9iamVjdAoKYGBge3J9CnNjZSAlPiUgCiAgY291bnRzICU+JSAKICBoZWFkKG49Yyg2LDYpKQoKc2NlICU+JSAKICBjb3VudHMgJT4lIAogIGNsYXNzCgpzY2UgJT4lIAogIGFzc2F5cwoKc2NlICU+JSAKICByb3dEYXRhICU+JSAKICBoZWFkCgpzY2UgJT4lIAogIGNvbERhdGEgJT4lIAogIGhlYWQKYGBgCgo8YnIvPjxici8+CgojIyMgVXNlIHNlbnNpYmxlIGdlbmUgbmFtZXMKCkJlZm9yZSBzdGFydGluZyB0aGUgYW5hbHlzaXMsIGl0IG1heSBiZSBhIGdvb2QgaWRlYSB0byBzdG9yZSB0aGUgbmFtZXMgb2YgdGhlIGdlbmVzIGluIGEgbW9yZSBodW1hbi1mcmllbmRseSBJRCBzeXN0ZW0uIFdlIGNhbiBhbHNvIGluY2x1ZGUgaW5mb3JtYXRpb24gb24gdGhlIGNocm9tb3NvbWUgbG9jYXRpb24gb2YgdGhlIGdlbmVzOyB0aGlzIHdpbGwgYmUgdXNlZnVsIGZvciBlLmcuIGlkZW50aWZ5aW5nIG1pdG9jaG9uZHJpYWwgZ2VuZXMuCgoKYGBge3J9CmxpYnJhcnkoc2N1dHRsZSkKcm93bmFtZXMoc2NlKSA8LSB1bmlxdWlmeUZlYXR1cmVOYW1lcygKICAgIHJvd0RhdGEoc2NlKSRJRCwgcm93RGF0YShzY2UpJFN5bWJvbCkKCnJvd0RhdGEoc2NlKSRsb2NhdGlvbiA8LSBtYXBJZHMoRW5zRGIuSHNhcGllbnMudjg2LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5cz1yb3dEYXRhKHNjZSkkSUQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uPSJTRVFOQU1FIiwga2V5dHlwZT0iR0VORUlEIikKcm93RGF0YShzY2UpJGxvY2F0aW9uW2lzLm5hKHJvd0RhdGEoc2NlKSRsb2NhdGlvbildIDwtICJOQSIKcm93RGF0YShzY2UpCmBgYAoKPGJyLz48YnIvPgoKIyMgRmlsdGVyIG5vbi1pbmZvcm1hdGl2ZSBnZW5lcwoKYGBge3J9CmtlZXAgPC0gcm93U3Vtcyhhc3NheXMoc2NlKSRjb3VudHMgPiAwKSA+IDEwCnRhYmxlKGtlZXApCnNjZSA8LSBzY2Vba2VlcCxdCmBgYAoKPGJyLz48YnIvPgoKIyMgRGV0ZWN0aW9uIGFuZCByZW1vdmFsIG9mIGVtcHR5IGRyb3BsZXRzIAoKVGhlIGZpcnN0IHN0ZXAgaXMgdGhlIGlkZW50aWZpY2F0aW9uIG9mIGRyb3BsZXRzIHRoYXQgZG8gbm90IGNvbnRhaW4gYW55IGxpdmUgY2VsbC4KClRoZSByZWFzb24gd2h5IHRoZXNlIGRyb3BsZXRzIGNvbnRhaW4gc29tZSBSTkEgaXMgdGhhdCB0aGVyZSBtYXkgYmUgc29tZSBhbWJpZW50IFJOQSBkdWUgdG8gc29tZSBjZWxsIGxlYWtpbmcgb3IgdGhleSBtYXkgY29udGFpbiBkZWFkIG9yIGR5aW5nIGNlbGxzLgoKPGJyLz48YnIvPgoKIyMjIEV4YW1wbGUgCgpUaGUgYmFyY29kZVJhbmtzIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIHJhbmsgdGhlIGJhcmNvZGVzIGJ5IG51bWJlciBvZiBVTUlzIGFuZCB0byBlc3RpbWF0ZSB0aGUga25lZSBhbmQgaW5mbGVjdGlvbiBwb2ludCBvZiB0aGUgZGlzdHJpYnV0aW9uLgoKCmBgYHtyfQpiY3JhbmsgPC0gYmFyY29kZVJhbmtzKGNvdW50cyhzY2UpKQoKIyBPbmx5IHNob3dpbmcgdW5pcXVlIHBvaW50cyBmb3IgcGxvdHRpbmcgc3BlZWQuCnVuaXEgPC0gIWR1cGxpY2F0ZWQoYmNyYW5rJHJhbmspCnBsb3QoYmNyYW5rJHJhbmtbdW5pcV0sIGJjcmFuayR0b3RhbFt1bmlxXSwgbG9nPSJ4eSIsCiAgICB4bGFiPSJSYW5rIiwgeWxhYj0iVG90YWwgVU1JIGNvdW50IiwgY2V4LmxhYj0xLjIpCgphYmxpbmUoaD1tZXRhZGF0YShiY3JhbmspJGluZmxlY3Rpb24sIGNvbD0iZGFya2dyZWVuIiwgbHR5PTIpCmFibGluZShoPW1ldGFkYXRhKGJjcmFuaykka25lZSwgY29sPSJkb2RnZXJibHVlIiwgbHR5PTIpCgpsZWdlbmQoImJvdHRvbWxlZnQiLCBsZWdlbmQ9YygiSW5mbGVjdGlvbiIsICJLbmVlIiksIAogICAgICAgIGNvbD1jKCJkYXJrZ3JlZW4iLCAiZG9kZ2VyYmx1ZSIpLCBsdHk9MiwgY2V4PTEuMikKYGBgCgo8YnIvPgoKLSBUaGVyZSBpcyBhIHNoYXJwIGRpc3RpbmN0aW9uIGJldHdlZW4gZHJvcGxldHMgd2l0aCB2ZXJ5IGhpZ2ggY291bnRzLCB2ZXJ5IGxpa2VseSB0byBjb250YWluIGEgbGl2ZSBjZWxsLCBhbmQgZHJvcGxldHMgd2l0aCB2ZXJ5IGxvdyBjb3VudHMsIHZlcnkgbGlrZWx5IHRvIGJlIGVtcHR5LgoKLSBIb3dldmVyLCBpdCBpcyBub3Qgc3RyYWlnaHRmb3J3YXJkIHRvIGNsYXNzaWZ5IHRoZSBkcm9wbGV0cyBpbiB0aGUgbWlkZGxlIG9mIHRoZSBkaXN0cmlidXRpb24uCgotIFdlIGNhbiBhcHBseSBhIHN0YXRpc3RpY2FsIHRlc3Qgb2YgaHlwb3RoZXNpcyB0byBkZWNpZGUsIGZvciBlYWNoIGRyb3BsZXQsIGlmIGl0cyBSTkEgcHJvZmlsZSBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIHRoZSBwcm9maWxlIG9mIGFtYmllbnQgUk5BLCBlc3RpbWF0ZWQgZnJvbSB0aGUgdmVyeSBsb3cgY291bnRzIChBYXJvbiBUTCBMdW4gZXQgYWwuIDIwMTkpLgoKLSBXZSB1c2UgYSB2ZXJ5IGxvdyB0aHJlc2hvbGQgb24gdGhlIEZhbHNlIERpc2NvdmVyeSBSYXRlIHRvIGhhdmUgdmVyeSBmZXcgZmFsc2UgcG9zaXRpdmUgY2VsbHMuCgpgYGB7cn0Kc2V0LnNlZWQoMTAwKQplLm91dCA8LSBlbXB0eURyb3BzKGNvdW50cyhzY2UpKQpzdW1tYXJ5KGUub3V0JEZEUiA8PSAwLjAwMSkKYGBgCgo8YnIvPjxici8+CgpUaGUgbGFyZ2UgbWFqb3JpdHkgb2YgZHJvcGxldHMgYXJlIG5vdCB0ZXN0ZWQsIHNpbmNlIGJ5IGRlZmF1bHQgYWxsIGRyb3BsZXRzIHdpdGggZmV3ZXIgdGhhbiAxMDAgVU1JcyBhcmUgY29uc2lkZXJlZCBlbXB0eS4KCmBgYHtyfQp0YWJsZShjb2xTdW1zKGNvdW50cyhzY2UpKT4xMDAsIGUub3V0JEZEUjw9MC4wMDEsIHVzZU5BID0gImlmYW55IikKYGBgCgo8YnIvPjxici8+CgpXZSBjYW4gbm93IHByb2NlZWQgYnkgcmVtb3ZpbmcgdGhlIGVtcHR5IGRyb3BsZXRzIGFuZCBrZWVwIG9ubHkgdGhlIG9uZXMgaWRlbnRpZmllZCB0byBiZSBjZWxscy4KCmBgYHtyfQpzY2UgPC0gc2NlWyx3aGljaChlLm91dCRGRFIgPD0gMC4wMDEpXQpzY2UKYGBgCgo8YnIvPjxici8+CgojIyBRdWFsaXR5IGNvbnRyb2wKClNvbWUgY2VsbHMgaGF2ZSBwb29yIHF1YWxpdHkgKGUuZy4sIGRhbWFnZWQgb3Igc3RyZXNzZWQgY2VsbHMpLgoKLSBFeHByZXNzaW9uIG9mIG1pdG9jaG9uZHJpYWwgZ2VuZXMsIHRvdGFsIG51bWJlciBvZiBnZW5lcyBleHByZXNzZWQsIHJlYWQgZGlzdHJpYnV0aW9uIGFjcm9zcyBnZW5lcyBhbmQgbGlicmFyeSBzaXplIGFyZSBnb29kIGluZGljYXRvcnMgZm9yIGEgY2VsbOKAmXMgcXVhbGl0eS4KCjxici8+PGJyLz4KCiMjIyBDb25jZXJucyAKCk9uZSBjb25jZXJuIGlzIHRoYXQgd2Ugd291bGQgaW5hZHZlcnRlbnRseSByZW1vdmUsIGUuZy4sIGFuIGVudGlyZSBjZWxsIHR5cGUgZnJvbSB0aGUgZG93bnN0cmVhbSBhbmFseXNpcy4gUUMgdmFyaWFibGVzIG1heSBub3QgYmUgaW5kZXBlbmRlbnQgb2YgYmlvbG9naWNhbCBzdGF0ZS4gVGhlcmUgaXMgYSB0cmFkZS1vZmYgYmV0d2VlbiBvbmx5IHdvcmtpbmcgd2l0aCBoaWdoLXF1YWxpdHkgY2VsbHMgYW5kIHJpc2sgb2YgcmVtb3ZpbmcgYmlvbG9naWNhbCBzaWduYWwuCgo8YnIvPgoKIyMjIENyaXRlcmlhCgotIEEgaGlnaC1xdWFsaXR5IGNlbGwgc2hvdWxkIGhhdmUKCiAgLSBBIGhpZ2ggbnVtYmVyIG9mIGdlbmVzIGV4cHJlc3NlZAogIC0gQSBmYWlyIGxpYnJhcnkgc2l6ZQogIC0gTm8gbWFqb3IgZXhwcmVzc2lvbiBvZiBtaXRvY2hvbmRyaWFsIGdlbmVzLCByZWxhdGl2ZSB0byBvdGhlciBnZW5lcwoKLSBCYXNlZCBvbiB0aGlzIGluZm9ybWF0aW9uLCB3ZSBjb3VsZCBzZXQgYSBmaXhlZCB0aHJlc2hvbGQsIGUuZy4sICJrZWVwIGNlbGxzIHdpdGggNDAwMCBnZW5lcyBleHByZXNzZWQsIGEgbGlicmFyeSBzaXplIG9mIGF0IGxlYXN0IDEwMDAwIGFuZCBmZXdlciB0aGFuIDUlIG9mIHRoZSB0b3RhbCBvdXRwdXQgY29taW5nIGZyb20gbWl0b2Nob25kcmlhbCBnZW5lcyIuCgogIC0gUmVxdWlyZXMgc3Vic3RhbnRpYWwgZXhwZXJpZW5jZSB3aXRoIHRoZSBiaW9sb2dpY2FsIHN5c3RlbSBhbmQgc2NSTkEtc2VxIGRhdGEsIGUuZy4sIHR1bW9ycyB0eXBpY2FsbHkgZXhwcmVzcyBhIHJlbGF0aXZlbHkgaGlnaGVyIGFtb3VudCBvZiBtaXRvY2hvbmRyaWFsIGdlbmVzLgoKLSBBIGJldHRlciBhcHByb2FjaCBpcyB0byB1c2UgYW4gYWRhcHRpdmUgdGhyZXNob2xkLgoKICAtIEFnYWluIGFzc3VtZXMgbW9zdCBjZWxscyBhcmUgaGlnaC1xdWFsaXR5IGNlbGxzICRccmlnaHRhcnJvdyQgaWRlbnRpZmljYXRpb24gb2Ygb3V0bGllcnMgYWxsb3dzIGlkZW50aWZpY2F0aW9uIG9mIGxvdy1xdWFsaXR5IGNlbGxzLgogIC0gRm9yIGV4YW1wbGUsIGZvciByZW1vdmluZyBsb3cgbGlicmFyeSBzaXplIGNlbGxzIGluIHRoZSBPU0NBIGJvb2sgdGhleSB1c2UKCiQkSShOX2kgPCBtZWRpYW4oXG1hdGhiZntOfSktMyBcdGltZXMgTUFEKFxtYXRoYmZ7Tn0pKSQkCndpdGggJE5faSQgdGhlIGxpYnJhcnkgc2l6ZSBvZiBjZWxsIGkgYW5kICRcbWF0aGJme059PVtOXzEsXGxkb3RzLE5fbV0kIHdpdGggJG0kIHRoZSBudW1iZXIgb2YgY2VsbHMuCgo8YnIvPjxici8+CgoKIyMjIEV4YW1wbGUgCgpUaGUgYHBlckNlbGxRQ01ldHJpY3NgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIGNvbXB1dGUgYSBzZXQgb2YgbWV0cmljcyB1c2VmdWwgdG8gZXZhbHVhdGUgdGhlIHF1YWxpdHkgb2YgdGhlIHNhbXBsZXMuIFRoZSBgaXNPdXRsaWVyYCBmdW5jdGlvbiB1c2VzIGEgZGF0YSBkcml2ZW4gdGhyZXNob2xkIHRvIGRlZmluZSBjZWxscyBvZiBsb3dlciBxdWFsaXR5IGNvbXBhcmVkIHRvIHRoZSByZXN0IG9mIHRoZSBkYXRhc2V0LgoKYGBge3J9CnN0YXRzIDwtIHBlckNlbGxRQ01ldHJpY3Moc2NlLAogICAgICAgICAgICBzdWJzZXRzPWxpc3QoTWl0bz13aGljaChyb3dEYXRhKHNjZSkkbG9jYXRpb249PSJNVCIpKSkKYGBgCgpIaWdoLXF1YWxpdHkgY2VsbHMgc2hvdWxkIGhhdmUgbWFueSBmZWF0dXJlcyBleHByZXNzZWQsIGFuZCBhIGxvdyBjb250cmlidXRpb24gb2YgbWl0b2Nob25kcmlhbCBnZW5lcy4gSGVyZSwgd2Ugc2VlIHRoYXQgc2V2ZXJhbCBjZWxscyBoYXZlIGEgdmVyeSBsb3cgbnVtYmVyIG9mIGV4cHJlc3NlZCBnZW5lcywgYW5kIHdoZXJlIG1vc3Qgb2YgdGhlIG1vbGVjdWxlcyBhcmUgZGVyaXZlZCBmcm9tIG1pdG9jaG9uZHJpYWwgZ2VuZXMuIFRoaXMgaW5kaWNhdGVzIGxpa2VseSBkYW1hZ2VkIGNlbGxzLCBwcmVzdW1hYmx5IGJlY2F1c2Ugb2YgbG9zcyBvZiBjeXRvcGxhc21pYyBSTkEgZnJvbSBwZXJmb3JhdGVkIGNlbGxzLCBzbyB0aGV5IGhhdmUgdG8gYmUgcmVtb3ZlZCBmb3IgdGhlIGRvd25zdHJlYW0gYW5hbHlzaXMuCgpgYGB7cn0KY29sRGF0YShzY2UpIDwtIGNiaW5kKGNvbERhdGEoc2NlKSwgc3RhdHMpCnBsb3RDb2xEYXRhKHNjZSwgeCA9ICJzdW0iLCB5PSJkZXRlY3RlZCIpCnBsb3RDb2xEYXRhKHNjZSwgeCA9ICJkZXRlY3RlZCIsIHk9InN1YnNldHNfTWl0b19wZXJjZW50IikKYGBgCgo8YnIvPgoKV2Ugbm9ybWFsbHkgcmVtb3ZlIGNlbGxzIHRoYXQgYXJlIG91dGx5aW5nIHdpdGggcmVzcGVjdCB0bwoKMS4gQSBsb3cgc2VxdWVuY2luZyBkZXB0aCAobnVtYmVyIG9mIFVNSXMpOwoyLiBBIGxvdyBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQ7CjMuIEEgaGlnaCBwZXJjZW50YWdlIG9mIHJlYWRzIGZyb20gbWl0b2Nob25kcmlhbCBnZW5lcy4KCmBgYHtyfQpsb3dGZWF0dXJlcyA8LSBpc091dGxpZXIoc3RhdHMkZGV0ZWN0ZWQsIHR5cGU9Imxvd2VyIiwgbG9nPVRSVUUpCmxvd0xpYiA8LSBpc091dGxpZXIoc3RhdHMkc3VtLCB0eXBlPSJsb3dlciIsIGxvZz1UUlVFKQpoaWdoTWl0byA8LSBpc091dGxpZXIoc3RhdHMkc3Vic2V0c19NaXRvX3BlcmNlbnQsIHR5cGU9ImhpZ2hlciIpCnRhYmxlKGxvd0ZlYXR1cmVzKQp0YWJsZShsb3dMaWIpCnRhYmxlKGhpZ2hNaXRvKQpgYGAKCjxici8+PGJyLz4KCmBgYHtyfQpkaXNjYXJkQ2VsbHMgPC0gKGxvd0xpYnxsb3dGZWF0dXJlc3xoaWdoTWl0bykKdGFibGUoZGlzY2FyZENlbGxzKQpjb2xEYXRhKHNjZSkkZGlzY2FyZENlbGxzIDwtIGRpc2NhcmRDZWxscwpgYGAKCjxici8+PGJyLz4KCmBgYHtyfQpzY2F0ZXI6OnBsb3RDb2xEYXRhKHNjZSwgeT0ic3Vic2V0c19NaXRvX3BlcmNlbnQiLAogICAgICAgIGNvbG91cl9ieT0iZGlzY2FyZENlbGxzIikKc2NhdGVyOjpwbG90Q29sRGF0YShzY2UsIHk9ImRldGVjdGVkIiwKICAgICAgICBjb2xvdXJfYnk9ImRpc2NhcmRDZWxscyIpCnNjYXRlcjo6cGxvdENvbERhdGEoc2NlLCB5PSJzdW0iLAogICAgICAgIGNvbG91cl9ieT0iZGlzY2FyZENlbGxzIikKcGxvdENvbERhdGEoc2NlLCB4ID0gImRldGVjdGVkIiwgeT0ic3Vic2V0c19NaXRvX3BlcmNlbnQiLCBjb2xvdXJfYnkgPSAiZGlzY2FyZENlbGxzIikKYGBgCgo8YnIvPjxici8+CgojIyBEZXRlY3Rpb24gYW5kIHJlbW92YWwgb2YgZG91YmxldHMKCk5vdCBhbGwgZHJvcGxldHMgY29udGFpbiBhIHNpbmdsZSBjZWxsOyBzb21lIGNvbnRhaW4gPjEgY2VsbC4KCkRvdWJsZXQgY2VsbCBjbHVzdGVycyBtYXkgYmUgbWlzdGFrZW4gZm9yIGludGVybWVkaWF0ZS90cmFuc2l0b3J5IHN0YXRlcy4KCjxici8+PGJyLz4KCiMjIyBNZXRob2RzCgoKYGBge3Igc2NEYmlGaW5kZXIsIGZpZy5jYXA9Ikdlcm1haW4gZXQgYWwuIDIwMjEsIGRvaTogMTAuMTI2ODgvZjEwMDByZXNlYXJjaC43MzYwMC4yIiwgb3V0LndpZHRoPSIxMDAlIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvc2NEYmxGaW5kZXIucG5nIikKYGBgCgo8YnIvPjxici8+CgoxLiBQQ0Egb24gbG9nLW5vcm1hbGl6ZWQgZXhwcmVzc2lvbiBjb3VudHMuCjIuIFJhbmRvbWx5IHNlbGVjdCB0d28gY2VsbHMsIHN1bSB0aGVpciBjb3VudHMgYW5kIG5vcm1hbGl6ZSwgYW5kIHByb2plY3QgaW50byBQQ0Egc3BhY2UuCjMuIFJlcGVhdCBzdGVwIDIgbWFueSB0aW1lcy4KNC4gRm9yIGVhY2ggY2VsbCwgY2hlY2sgaXRzIG5laWdoYm9yaG9vZCB0byBzaW11bGF0ZWQgZG91YmxldHMgYXMgY29tcGFyZWQgdG8gb3JpZ2luYWwgY2VsbHMuCjUuIFVzZSB0aGlzIGluZm9ybWF0aW9uLCBhbG9uZyB3aXRoIG90aGVyIHByZWRpY3RvcnMsIHRvIHRyYWluIGEgY2xhc3NpZmllciBmb3IgZG91YmxldHMuCgo8YnIvPjxici8+CgojIyMgQ29uY2VybnMKCkEgcG90ZW50aWFsIGNvbmNlcm4gaXMgdGhhdCB3ZSB3b3VsZCBpbmFkdmVydGVudGx5IHJlbW92ZSBiaW9sb2dpY2FsIHNpbmdsZSBjZWxscywgdGhhdCBtYXkgaGF2ZSBlLmcuIGEgbG93IHRyYW5zY3JpcHRvbWUgY29tcGxleGl0eS4gVGhlcmUgaXMgYSB0cmFkZS1vZmYgYmV0d2VlbiBvbmx5IHdvcmtpbmcgd2l0aCBoaWdoLXF1YWxpdHkgY2VsbHMgYW5kIHJpc2sgb2YgcmVtb3ZpbmcgYmlvbG9naWNhbCBzaWduYWwuCgo8YnIvPiAKCiMjIyBFeGFtcGxlCgpgYGB7cn0KZGJsLmRlbnMgPC0gY29tcHV0ZURvdWJsZXREZW5zaXR5KHNjZSkKc2NlJERvdWJsZXRTY29yZSA8LSBkYmwuZGVucwpzdW1tYXJ5KGRibC5kZW5zKQoKZGJsLmNhbGxzIDwtIGRvdWJsZXRUaHJlc2hvbGRpbmcoZGF0YS5mcmFtZShzY29yZT1kYmwuZGVucyksCiAgICBtZXRob2Q9ImdyaWZmaXRocyIsIHJldHVyblR5cGU9ImNhbGwiKQpzdW1tYXJ5KGRibC5jYWxscykKYGBgCgo8YnIvPjxici8+CgpgYGB7cn0Kc2NlIDwtIHNjZVssIGRibC5jYWxscyA9PSAic2luZ2xldCJdCnNjZQpgYGAKCjxici8+PGJyLz4KCk5vdGUgdGhhdCwgc2ltaWxhcmx5IHRvIHdoYXQgd2UgaGF2ZSBzYWlkIGZvciB0aGUgcmVtb3ZhbCBvZiBsb3ctcXVhbGl0eSBjZWxscywgb25lIHNob3VsZCBiZSBjYXJlZnVsIGluIHJlbW92aW5nIHB1dGF0aXZlIGRvdWJsZXRzLiBJbmRlZWQsIGl0IG1heSBiZSBkaWZmaWN1bHQgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiByYXJlIHRyYW5zaWVudCBjZWxsIHBvcHVsYXRpb25zIGFuZCBkb3VibGV0IGNlbGxzLgoKPGJyLz48YnIvPgoKIyMgTm9ybWFsaXNhdGlvbgoKTm9ybWFsaXphdGlvbiBhaW1zIHRvIHJlbW92ZSB0ZWNobmljYWwgZWZmZWN0cyBzdWNoIGFzIHNlcXVlbmNpbmcgZGVwdGggc28gdGhhdCAKCWNvbXBhcmlzb25zIGJldHdlZW4gY2VsbHMgYXJlIG5vdCBjb25mb3VuZGVkIGJ5IHRoZW0uCgpBcyBpbiBidWxrIFJOQS1zZXEsIHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgbWV0aG9kcyBhcmUgc2NhbGluZyBub3JtYWxpemF0aW9ucywgd2hlcmUgYSBzY2FsaW5nIGZhY3RvciAoYWxzbyBjYWxsZWQgc2l6ZSBmYWN0b3IsIG5vcm1hbGl6YXRpb24gZmFjdG9yKSBpcyBlc3RpbWF0ZWQgZm9yIGVhY2ggY2VsbC4KCjxici8+CgojIyMgQ29uY2VybnMKCi0gU29tZSBjZWxsIHR5cGVzIG1heSBuYXR1cmFsbHkgaGF2ZSBmZXdlciBtUk5BIG1vbGVjdWxlcyBhbmQgdGhlcmVmb3JlIGEgbG93ZXIgbGlicmFyeSBzaXplLiBSZW1vdmFsIG9mIHNlcXVlbmNpbmcgZGVwdGggZWZmZWN0cyBhY3Jvc3MgY2VsbHMgdGhlcmVmb3JlIGFsc28gcmlza3MgcmVtb3Zpbmcgc29tZSBiaW9sb2dpY2FsIHNpZ25hbC4KCQotIFRoZSBhc3N1bXB0aW9uIG9mIGEgY2VsbC1zcGVjaWZpYyBzY2FsaW5nIGZhY3RvciBpcyB0aGF0IGFueSB0ZWNobmljYWwgZWZmZWN0IGl0IHN1cHBvc2VkbHkgYWNjb3VudHMgZm9yLCBhZmZlY3RzIGFsbCBnZW5lcyBlcXVhbGx5IHRocm91Z2ggYSBzY2FsaW5nIG9mIHRoZSBtZWFuIGNvdW50LiBUaGlzIGlzIG5vdCBuZWNlc3NhcmlseSByZWFsaXN0aWMuCgotIFNjYWxpbmcgdGhlIGRhdGEgYW5kIGxvZy10cmFuc2Zvcm1pbmcgdGhlIGRhdGEgaXMgbm90IGEgZ29vZCBvcHRpb24uICBVc2Ugb2Zmc2V0cyEgCgpgYGB7ciBnbG1QQ0FGaWcyLCBmaWcuY2FwPSJUb3duZXMgZXQgYWwuIDIwMTksIERPSTogMTAuMTE4Ni9zMTMwNTktMDE5LTE4NjEtNiIsIG91dC53aWR0aD0iMTAwJSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL2ltYWdlc19zZXF1ZW5jaW5nL2dsbVBDQUZpZzIucG5nIikKYGBgCgo8YnIvPjxici8+CgojIyMgT2Zmc2V0cwoKTGV0ICR5X3tnaX0kIGJlIHRoZSBleHByZXNzaW9uIGZvciBnZW5lICRnJCBpbiBjZWxsICRpJC4gCkFzc3VtZSwgYXMgaW4gYnVsayBSTkEtc2VxIHRoYXQgCgokRVt5X3tnaX1dID0gXG11X3tnaX0gPSAgXHBpX3tnaX0gc19pTl9pJAoKSW4gdGhlIGVkZ2VSIG1vZGVsLCAkc19pTl9pJCBpcyByZWZlcnJlZCB0byBhcyB0aGUg4oCYZWZmZWN0aXZlIGxpYnJhcnkgc2l6ZeKAmSwgd2hpY2ggaXMgdXNlZCBhcyAKCW9mZnNldCBpbiB0aGUgR0xNLCB0aGVyZWJ5IGVmZmVjdGl2ZWx5IGZvY3Vzc2luZyB0aGUgc3RhdGlzdGljYWwgaW5mZXJlbmNlIG9uICRccGlfe2dpfSQuCgpgYGB7ciBsdW5Ob3JtYWxpc2F0aW9uLCBmaWcuY2FwPSJMdW4gZXQgYWwuIDIwMTYsIERPSTogMTAuMTE4Ni9zMTMwNTktMDE2LTA5NDctNyIsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iMTAwJSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJpbWFnZXNfc2VxdWVuY2luZy9sdW5Ob3JtYWxpc2F0aW9uLnBuZyIpCmBgYAoKPGJyLz4KCi0gVE1NIGFuZCBERVNlcTIgbm9ybWFsaXNhdGlvbiBhcmUgbGFyZ2VseSBkcml2ZW4gYnkgYSBmZXcgZ2VuZXMgd2l0aG91dCB6ZXJvIGNvdW50cyEgCgpQb3B1bGFyLCBiZXNwb2tlIHNpbmdsZS1jZWxsIG5vcm1hbGl6YXRpb24gbWV0aG9kcyBhdHRlbXB0IHRvIGFsbGV2aWF0ZSB0aGVzZSBpc3N1ZXMuCgpgYGB7ciBsdW5Ob3JtYWxpc2F0aW9uMiwgZmlnLmNhcD0iTHVuIGV0IGFsLiAyMDE2LCBET0k6IDEwLjExODYvczEzMDU5LTAxNi0wOTQ3LTciLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjEwMCUifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzX3NlcXVlbmNpbmcvbHVuTm9ybWFsaXNhdGlvbjIucG5nIikKYGBgCgo8YnIvPjxici8+CgojIyMgRXhhbXBsZSAKCkhlcmUsIHdlIHVzZSB0aGUgc2NyYW4gbWV0aG9kIHRvIG5vcm1hbGl6ZSB0aGUgZGF0YSBmb3IgZGlmZmVyZW5jZXMgaW4gc2VxdWVuY2luZyBkZXB0aC4gVGhpcyBhcHByb2FjaCBpcyBiYXNlZCBvbiB0aGUgZGVjb252b2x1dGlvbiBvZiBzaXplIGZhY3RvcnMgZXN0aW1hdGVkIGZyb20gcG9vbHMgb2YgY2VsbHMgKEx1biBldCBhbGwuIDIwMTYpLgoKU2luY2Ugd2UgaGF2ZSBhIGhldGVyb2dlbmVvdXMgY2VsbCBwb3B1bGF0aW9uLCB3ZSBwZXJmb3JtIGEgcXVpY2sgY2x1c3RlcmluZyB0byBtYWtlIHN1cmUgdGhhdCB3ZSBwb29sIHRvZ2V0aGVyIGNlbGxzIHRoYXQgYXJlIG5vdCB0b28gZGlmZmVyZW50IGZyb20gZWFjaCBvdGhlci4KCmBgYHtyfQpsaWJyYXJ5KHNjcmFuKQpjbCA8LSBxdWlja0NsdXN0ZXIoc2NlKQp0YWJsZShjbCkKc2NlIDwtIGNvbXB1dGVTdW1GYWN0b3JzKHNjZSwgY2x1c3RlcnMgPSBjbCkKc3VtbWFyeShzaXplRmFjdG9ycyhzY2UpKQpgYGAKCjxici8+CgpXZSBjYW4gY2hlY2sgdGhhdCB0aGUgZXN0aW1hdGVkIGxpYnJhcnkgc2l6ZXMgYXJlIG5vdCB0b28gZmFyIGZyb20gdGhlIGxpYnJhcnkgc2l6ZSBmYWN0b3JzLCBlc3RpbWF0ZWQgZnJvbSB0aGUgdG90YWwgbnVtYmVyIG9mIGNvdW50cy4KCmBgYHtyfQpwbG90KGxpYnJhcnlTaXplRmFjdG9ycyhzY2UpLCBzaXplRmFjdG9ycyhzY2UpLCB4bGFiPSJMaWJyYXJ5IHNpemUgZmFjdG9yIiwKICAgIHlsYWI9IkRlY29udm9sdXRpb24gc2l6ZSBmYWN0b3IiLCBsb2c9J3h5JywgcGNoPTE2LAogICAgY29sPWFzLmludGVnZXIoZmFjdG9yKGNsKSkpCmFibGluZShhPTAsIGI9MSwgY29sPSJyZWQiKQpgYGAKCjxici8+PGJyLz4KCiMjIEZlYXR1cmUgc2VsZWN0aW9uIAoKCi0gVGhlIGN1cnNlIG9mIGRpbWVuc2lvbjogSW4gaGlnaC1kaW1lbnNpb25hbCBkYXRhLCBhbGwgZGF0YSBwb2ludHMgYXBwZWFyIHRvIGJlIGRpc3NpbWlsYXIgKGkuZS4sIHRoZXkgYXJlIGZhciBhcGFydCksIGFzIHRoZSBkYXRhIGJlY29tZSBzcGFyc2UgaW4gdGhlIGhpZ2gtZGltZW5zaW9uYWwgc3BhY2UgYXMgZGltZW5zaW9ucyBpbmNyZWFzZSAoZS5nLiwgRXVjbGlkZWFuIGRpc3RhbmNlKS4gCgotIFdoYXQgZG9lcyB0aGlzIG1lYW4gZm9yIHNjUk5BLXNlcT8gVHJ5aW5nIHRvIHJlY292ZXIgdGhlIGdsb2JhbCBzdHJ1Y3R1cmUgb2YgYSBkYXRhc2V0IHVzaW5nIGFsbCBnZW5lcyBpcyBub3QgYSBnb29kIGlkZWEuIE9mdGVuLCB3ZSBwZXJmb3JtIGEgdmFyaWFibGUgc2VsZWN0aW9uIHN0ZXAsIHNlbGVjdGluZyBnZW5lcyB3aGljaCB3ZSBiZWxpZXZlIHRvIGJlIGhpZ2hseSBpbmZvcm1hdGl2ZSBmb3IgYSBkYXRhc2V04oCZcyBzdHJ1Y3R1cmUuIE9uZSBtYXkgdmlldyB0aGlzIGFzIGEgZmlyc3Qgc2ltcGxlIHN0ZXAgb2YgZGltZW5zaW9uIHJlZHVjdGlvbi4KCldlIHdpbGwgdGh1cyBzZWxlY3QgZ2VuZXMgd2hpY2ggd2UgaG9wZSBhcmUgaW5mb3JtYXRpdmUgZm9yIHJlY292ZXJpbmcgdGhlIGJpb2xvZ2ljYWwgc3RydWN0dXJlLiBQcmlvciB0byB2aXN1YWxpc2F0aW9uLgoKPGJyLz4KCiMjIyBDb25jZXJucwoKRmVhdHVyZSBzZWxlY3Rpb24gaXMgY3J1Y2lhbCBmb3IgZGltZW5zaW9uIHJlZHVjdGlvbi4gSG93ZXZlciwgaXQgcmVtYWlucyBzdWJqZWN0aXZlIGFuZCB0aGVyZSBhcmUgbm8gY2xlYXIgZ3VpZGVsaW5lcyB0byBmb2xsb3cuIFRoZSBhbW91bnQgb2YgZmVhdHVyZXMgdG8gc2VsZWN0IGRlcGVuZHMgb24gdGhlIGNvbXBsZXhpdHkgb2YgeW91ciBkYXRhc2V0LCBhbmQgdGhlIGluZm9ybWF0aXZlbmVzcyBvZiBlYWNoIGluZGl2aWR1YWwgZmVhdHVyZS4KCk1hbnkgaXNzdWVzIHJlbWFpbi4gCgo8YnIvPjxici8+CgojIyMgV2hhdCBkZWZpbmVzIGFuIGluZm9ybWF0aXZlIGdlbmU/CgotIEdlbmVzIHRoYXQgYXJlIG5vdCBleHByZXNzZWQgaW4gYW55IGNlbGwgYXJlIG9idmlvdXNseSB1bmluZm9ybWF0aXZlLiAKCi0gTmVpdGhlciBhcmUgZ2VuZXMgdGhhdCAKCWFyZSBoaWdobHkgZXhwcmVzc2VkIGF0IGEgc2ltaWxhciBsZXZlbCBpbiBhbGwgY2VsbHMuIFNvIHdoYXQgc2hvdWxkIHdlIGJlIGxvb2tpbmcgZm9yPwoKLSBHZW5lcyB3aXRoIGhpZ2ggdmFyaWFuY2UuIEJ1dCBub3RlIG91ciBwcmV2aW91cyBvYnNlcnZhdGlvbnMgb24gdGhlIG1lYW4tdmFyaWFuY2UgcmVsYXRpb25zaGlwLiAKCi0gU2ltcGx5IHNlbGVjdGluZyBmZWF0dXJlcyBvbiBoaWdoIHZhcmlhbmNlIGlzIGNvbW1vbiwgYnV0IHBvc3NpYmx5IG5vdCBvcHRpbWFsLgoKLSBHZW5lcyB3aXRoIGhpZ2ggdmFyaWFuY2UgcmVsYXRpdmUgdG8gdGhlaXIgbWVhbi4gQSAocmlnaHRseSBzbykgcG9wdWxhciBhcHByb2FjaCBpcyB0byBzZWxlY3QgZ2VuZXMgdGhhdCBoYXZlIGEgaGlnaCB2YXJpYW5jZSB3aXRoIHJlc3BlY3QgdG8gdGhlaXIgbWVhbi4gT2Z0ZW4sIGZpcnN0IGFuIGVtcGlyaWNhbCBtZWFuLXZhcmlhbmNlIHRyZW5kIGlzIGZpdHRlZCwgdXBvbiB3aGljaCBnZW5lcyB3aXRoIHRoZSBoaWdoZXN0IHBvc2l0aXZlIHJlc2lkdWFscyBhcmUgc2VsZWN0ZWQuIEJlaW5nIGludHVpdGl2ZSwgcmVhc29uYWJsZSBhbmQgZmFpcmx5IHN0cmFpZ2h0LWZvcndhcmQsIHRoaXMgbWV0aG9kIGlzIHdpZGVseSB1c2VkLgoKLSBHZW5lcyB3aXRoIGhpZ2ggZGV2aWFuY2UuIFNlZSBUb3duZXMgZXQgYWwuICgyMDE5KSBmb3IgZmVhdHVyZSBzZWxlY3Rpb24gYmFzZWQgb24gQmlub21pYWwgZGV2aWFuY2UuIEdlbmVzIHdpdGggYSBoaWdoIGRldmlhbmNlIHdpbGwgbW9zdCBwb29ybHkgZml0IGEgbnVsbCBtb2RlbCB3aGVyZSB0aGUgcmVsYXRpdmUgYWJ1bmRhbmNlIGlzIGVxdWFsIGZvciBhbGwgY2VsbHMsIHdoaWNoIHRoZXJlZm9yZSBhcmUgaW5mb3JtYXRpdmUuCgo8YnIvPjxici8+CgojIyMgRXhhbXBsZTogZmVhdHVyZSBzZWxlY3Rpb24gYmFzZWQgb24gaGlnaCB2YXJpYW5jZSB3cnQgbWVhbgoKWW91IG5lZWQgYW4gYXNzYXkgbG9nIGNvdW50cyBmb3IgdGhpcy4gCgpgYGB7cn0Kc2NlIDwtIGxvZ05vcm1Db3VudHMoc2NlKQpkZWMgPC0gbW9kZWxHZW5lVmFyKHNjZSkKdmFyZml0IDwtIG1ldGFkYXRhKGRlYykKcGxvdCh2YXJmaXQkbWVhbiwgdmFyZml0JHZhciwgCiAgICAgeGxhYj0iTWVhbiBvZiBsb2ctZXhwcmVzc2lvbiIsCiAgICB5bGFiPSJWYXJpYW5jZSBvZiBsb2ctZXhwcmVzc2lvbiIpCmN1cnZlKHZhcmZpdCR0cmVuZCh4KSwgY29sPSJkb2RnZXJibHVlIiwgYWRkPVRSVUUsIGx3ZD0yKQpgYGAKCjxici8+PGJyLz4KClNlbGVjdCBoaWdobHkgdmFyaWFibGUgZ2VuZXMgd2l0aCB2YXJpYW5jZSBsYXJnZXIgdGhhbiBleHBlY3RlZCBiYXNlZCBvbiB0aGUgbWVhbiB2YXJpYW5jZSB0cmVuZC4gV2UgdGhlbiBoYXZlIHRvIHNlbGVjdCB0aGUgdG9wIG51bWJlciBvZiBtb3N0IHZhcmlhYmxlIGdlbmVzLiBXZSBjYW4gZm9yIGluc3RhbmNlIHRha2UgdGhlIHRvcCAxMCUgb2YgdGhlIG1vc3QgdmFyaWFibGUgZ2VuZXMKCmBgYHtyfQpodmcgPC0gZ2V0VG9wSFZHcyhkZWMsIHByb3A9MC4xKQpoZWFkKGh2ZykKCnBsb3QodmFyZml0JG1lYW4sIHZhcmZpdCR2YXIsIAogICAgIGNvbCA9IGMoIm9yYW5nZSIsICJkYXJrc2VhZ3JlZW4zIilbKG5hbWVzKHZhcmZpdCRtZWFuKSAlaW4lIGh2ZykrMV0sCiAgICAgeGxhYj0iTWVhbiBvZiBsb2ctZXhwcmVzc2lvbiIsCiAgICB5bGFiPSJWYXJpYW5jZSBvZiBsb2ctZXhwcmVzc2lvbiIpCmN1cnZlKHZhcmZpdCR0cmVuZCh4KSwgY29sPSJkb2RnZXJibHVlIiwgYWRkPVRSVUUsIGx3ZD0yKQpsZWdlbmQoInRvcGxlZnQiLCAKICAgICAgIGxlZ2VuZCA9IGMoIlNlbGVjdGVkIiwgIk5vdCBzZWxlY3RlZCIpLCAKICAgICAgIGNvbCA9IGMoImRhcmtzZWFncmVlbjMiLCAib3JhbmdlIiksCiAgICAgICBwY2ggPSAxNiwKICAgICAgIGJ0eT0nbicpCmBgYAoKPGJyLz48YnIvPgoKCiMjIyBHZW5lcyB3aXRoIGhpZ2ggZGV2aWFuY2UKClNlZSBUb3duZXMgZXQgYWwuICgyMDE5KSBmb3IgZmVhdHVyZSBzZWxlY3Rpb24gYmFzZWQgb24gCglCaW5vbWlhbCBkZXZpYW5jZS4gR2VuZXMgd2l0aCBhIGhpZ2ggZGV2aWFuY2Ugd2lsbCBtb3N0IHBvb3JseSBmaXQgYSBudWxsIG1vZGVsIHdoZXJlIHRoZSByZWxhdGl2ZSBhYnVuZGFuY2UgaXMgZXF1YWwgZm9yIGFsbCBjZWxscywgd2hpY2ggdGhlcmVmb3JlIGFyZSBpbmZvcm1hdGl2ZS4KCQpOb3RlIHRoYXQgdGhlIGRldmlhbmNlIGZvciBhIEdMTSAgaXMgZGVmaW5lZCBhczogCgokJApEID0gMiBbbChcbWF0aGJme3l9LFxtYXRoYmZ7eX0pIC0gbChcbWF0aGJme3l9LFxoYXR7XG1hdGhiZntcbXV9fSldCiQkCgp3aGljaCBpcyBmb2xsb3dzIGEgJFxjaGleMiQgZGlzdHJpYnV0aW9uIHdpdGggbiBkZWdyZWVzIG9mIGZyZWVkb20uIEluZGVlZCwgaXQgaXMgYSBMUiB0ZXN0IGJldHdlZW4gdGhlIGJlc3QgbW9kZWwgd2l0aCBhIHBlcmZlY3QgbW9kZWwgZml0IGFuZCB0aGUgY3VycmVudCBtb2RlbC4gCgpJZiB0aGUgZGlmZmVyZW5jZSBpcyBzaWduaWZpY2FudCwgdGhlcmUgaXMgbGFjayBvZiBmaXQuCgpgYGB7ciBnbG1QQ0FEZXZpYW5jZSwgZmlnLmNhcD0iVG93bmVzIGV0IGFsLiAyMDE5LCBET0k6IDEwLjExODYvczEzMDU5LTAxOS0xODYxLTYiLCBvdXQud2lkdGg9IjUwJSIsZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvZ2xtUENBRGV2aWFuY2UucG5nIikKYGBgCgo8YnIvPjxici8+CgojIyMgVmFyaWFuY2Utc3RhYmlsaXppbmcgdHJhbnNmb3JtYXRpb24gKFZTVCkgdXNpbmcgUGVhcnNvbiByZXNpZHVhbHMKCkxhdXNlIGV0IGFsLiAyMDIxIGludHJvZHVjZWQgdGhlIHVzZSBvZiBQZWFyc29uIHJlc2lkdWFscyB0byBzdGFiaWxpemUgdGhlIHZhcmlhbmNlIChkb2k6IDEwLjExODYvczEzMDU5LTAyMS0wMjQ1MS03KS4gCgotIFdoZW4gYSBtb2RlbCBpcyBmaXR0ZWQgdG8gZWFjaCBnZW5lIHdlIGNhbiBjb25zdHJ1Y3QgcGVhcnNvbiByZXNpZHVhbHMgdGhhdCBjb3JyZWN0IGZvciB0aGUgbWVhbiB2YXJpYW5jZSByZWxhdGlvbi4gCgotIEhlcmUgd2UgbW9kZWwgJHlfe2lnfSBcc2ltIE5CKFxtdV97aWd9PVxwaV9nc19pTl9pLFxwaGlfZykkIAotIFBlYXJzb24gcmVzaWR1YWxzIGFyZSB0aGVuIGRlZmluZWQgYXMgCgokJAplX2kgPSBcZnJhY3t5X3tpZ30gLSBcbXVfe2d9fXtcc3FydHtcbXVfe2d9ICsgXHBoaVxtdV97Z31eMn19IAokJAoKSW5kZWVkLCB0aGUgcmVzaWR1YWxzIGFyZSBjb3JyZWN0ZWQgZm9yIHRoZSBtZWFuIHZhcmlhbmNlIHJlbGF0aW9uc2hpcC4KCldlIGNhbiB0aGVuIGFzc2VzcyBpZiB0aGUgcmVzaWR1YWxzIGV4aGliaXQgb3ZlciBleGNlc3MgIHZhcmlhbmNlIHdpdGggcmVzcGVjdCB0byB0aGUgdHJlbmQuIElmIHRoaXMgaXMgdGhlIGNhc2UsIHRoZSBnZW5lIGlzIGluZm9ybWF0aXZlLiBUaGVyZSBpcyBtb3JlIHZhcmlhbmNlIHRoYW4gd2UgZXhwZWN0IHVuZGVyIHRoZSBpbnRlcmNlcHQgbW9kZWwuCgokJApcaGF0XHNpZ21hXjIgPSBcc3VtXGxpbWl0c197aT0xfV5uIFxmcmFje2VfaV4yfXtuLXB9CiQkCk5vdGUsIHRoYXQgdGhpcyBvbmUgb2YgdGhlIHZhcmlhbmNlIGVzdGltYXRvciBvZiB0aGUgcXVhc2ktbmVnYXRpdmUgYmlub21pYWwgbW9kZWwgKFNlZSBMdW5kIGV0IGFsLiwgMjAxMiwgRE9JIDEwLjE1MTUvMTU0NC02MTE1LjE4MjYpLiAKCkdlbmVzIHdpdGggaGlnaCB2YXJpYW5jZSBhcmUgaW5mb3JtYXRpdmUuIAoKTm90ZSwgdGhhdCB0aGVyZSBpcyBhbHNvIGEgY2xvc2UgY29ubmVjdGlvbiB0byB0aGUgbWV0aG9kIG9mIFRvd25lcy4gQW5vdGhlciB0eXBlIG9mIHJlc2lkdWFscyBmb3IgR0xNcyBhcmUgZGV2aWFuY2UgcmVzaWR1YWxzLCBpLmUuIAoKJCQKZV9pXmQgPSBcc3FydHsyIFtsKHlfe2lnfSx5X3tpZ30pLWwoeV97aWd9LFxoYXRcbXVfe2lnfSldfQokJAoKQW5vdGhlciB2YXJpYW5jZSBlc3RpbWF0b3IgaXMgCgokJApcaGF0XHNpZ21hXjIgPSBcc3VtXGxpbWl0c197aT0xfV5uIFxmcmFjeyhlX2leZCleMn17bi1wfSA9IFxmcmFje0R9e24tcH0KJCQKTm90ZSwgdGhhdCB0aGlzIGlzIHRoZSBvdGhlciBleGNlc3MgdmFyaWFuY2UgZXN0aW1hdG9yIGZvciB0aGUgcXVhc2ktbmVnYXRpdmUgYmlub21pYWwgbW9kZWwuIFNlZSBMdW5kIGV0IGFsLiwgMjAxMiAoRE9JIDEwLjE1MTUvMTU0NC02MTE1LjE4MjYpLiAKCjxici8+PGJyLz4KCiMjIERpbWVuc2lvbiByZWR1Y3Rpb24gCgpXZSB3b3VsZCBsaWtlIHRvIGJlIGFibGUgdG8gcHJvamVjdCB0aGUgZGF0YSB0byBhIGxvdy1kaW1lbnNpb25hbCBzcGFjZSwgd2hpbGUgCnJldGFpbmluZyBhcyBtdWNoIGluZm9ybWF0aW9uIGFzIHBvc3NpYmxlLiBUaGlzIHdpbGwgYmUgdXNlZnVsIGRvd25zdHJlYW0gZm9yLCBlLmcuLCAKIHZpc3VhbGl6YXRpb24sIGlkZW50aWZpY2F0aW9uIG9mIGJhdGNoIGVmZmVjdHMsIGNsdXN0ZXJpbmcsIHRyYWplY3RvcnkgaW5mZXJlbmNlLgogCk1hbnkgZGltZW5zaW9uIHJlZHVjdGlvbiBtZXRob2RzIGV4aXN0IGFuZCB0aGlzIGlzIGEgdmVyeSBhY3RpdmUgYXJlYSBvZiByZXNlYXJjaCBpbiBzY1JOQS1zZXEuIFdlIHdpbGwgZGlzY3VzcyB0aGUgbW9zdCBjb21tb24gbWV0aG9kcyBzdWNoIGFzIFBDQSwgR0xNLVBDQSwgdC1TTkUgYW5kIFVNQVAuCgo8YnIvPgoKIyMjIENvbmNlcm5zCgpMaWtlIGZlYXR1cmUgc2VsZWN0aW9uLCB0aGlzIHJlbWFpbnMgYSBzdWJqZWN0aXZlIHN0ZXAgaW4gdGhlIGFuYWx5c2lzLiBTZXZlcmFsIGNob2ljZXMgaGF2ZSB0byBiZSBtYWRlOiBpbnB1dCBkYXRhLCBlLmcuLCBsb2ctdHJhbnNmb3JtZWQgZGF0YSBvciBub3Q/OyBudW1iZXIgb2YgY29tcG9uZW50cyB0byByZXRhaW47CiBwYXJhbWV0ZXIgdHVuaW5nOyBldGMuLgoKPGJyLz48YnIvPgoKIyMjIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgCgotIEEgRFIgbWV0aG9kIGlzIGxpbmVhciB3aGVuIHRoZSByZWR1Y2VkIGRpbWVuc2lvbnMgYXJlIGEgbGluZWFyIGZ1bmN0aW9uIG9mIHRoZSBvcmlnaW5hbCB2YXJpYWJsZXMuIAoKLSBJbiBQQ0EsIGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudCBpcyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiBnZW5lcy4gClNlZSBoZXJlIGZvciBhIFtnZW9tZXRyaWMgaW50ZXJwcmV0YXRpb24gb2YgUENBXShodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8vSEREQS9zdmQuaHRtbCMzX0dlb21ldHJpY19pbnRlcnByZXRhdGlvbikgYW5kIGZvciBhIFtQQ0EgZGV0YWlsc10oaHR0cHM6Ly9zdGF0b21pY3MuZ2l0aHViLmlvL0hEREEvc3ZkLmh0bWwjOF9TVkRfYW5kX3ByaW5jaXBhbF9jb21wb25lbnRfYW5hbHlzaXNfKFBDQSkpCgotIFR5cGljYWxseSwgUENBIGlzIHBlcmZvcm1lZCBvbiBsb2ctdHJhbnNmb3JtZWQgbm9ybWFsaXplZCBjb3VudHMuIApUaGUgbG9nLXRyYW5zZm9ybWF0aW9uIGhlbHBzIHNvbWV3aGF0LCBidXQgbm90IGNvbXBsZXRlbHksIHRvIGFjY291bnQgZm9yIHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcC4KCi0gUENBIHdvcmtzIHdlbGwgZm9yIGJ1bGsgUk5BLXNlcSBkYXRhLiBIb3dldmVyLCB0aGUgc3RydWN0dXJlIG9mIHNjUk5BLXNlcSBkYXRhIGlzIG9mdGVuIAoJdG9vIGNvbXBsZXggdG8gYmUgdmlzdWFsaXplZCBieSBhIHNtYWxsIG51bWJlciBvZiBQQ3MuCgo8YnIvPjxici8+CgojIyMjIEV4YW1wbGUgCgpXZSBkbyBQQ0Egb24gdGhlIGhpZ2hseSB2YXJpYWJsZSBnZW5lcy4gCgpgYGB7cn0Kc2NlIDwtIHJ1blBDQShzY2UsIHN1YnNldF9yb3cgPSBodmcpCnBsb3RQQ0Eoc2NlKQpgYGAKCk5vdGUgdGhhdCB0aGUgc3Vic2V0X3JvdyBhcmd1bWVudCBlbnN1cmVzIHRoYXQgd2UgdXNlIHRoZSBwcmV2aW91c2x5IHNlbGVjdGVkIEhWR3MgdG8gY29tcHV0ZSB0aGUgUENzLiBBbHNvIG5vdGUgdGhhdCBieSBkZWZhdWx0IHJ1blBDQSB3aWxsIHN0b3JlIHRoZSBQQ3MgaW4gdGhlIFBDQSBzbG90LCBoZW5jZSBvdmVyd3JpdGluZyB0aGUgcHJldmlvdXMgUENzLiBJbiB0aGUgdHV0b3JpYWxzIHdlIHdpbGwgc2VlIHRoYXQgd2UgYWxyZWFkeSB1c2UgUENBIGR1cmluZyB0aGUgUUMgZm9yIHZpc3VhbGlzYXRpb24gcHVycG9zZXMuIFRoZXNlIHJlc3VsdHMgYXJlIGJhc2VkIG9uIGEgcXVpY2sgbm9ybWFsaXphdGlvbiBhbmQgcHJpb3IgdG8gcmVtb3Zpbmcgbm9pc3kgZ2VuZXMgYW5kIGNlbGxzLiBIZW5jZSwgaXQgaXMgb2sgdG8gb3ZlcndyaXRlIHRoZW0uIEluIG90aGVyIGNhc2VzLCBpdCBtYXkgYmUgcmVhc29uYWJsZSB0byBrZWVwIG1vcmUgdGhhbiBvbmUgc2V0IG9mIFBDczogdGhpcyBjYW4gYmUgYWNoaWV2ZWQgYnkgc3BlY2lmeWluZyBhIGRpZmZlcmVudCBzbG90IG5hbWUgdmlhIHRoZSBuYW1lIGFyZ3VtZW50LgoKRmluYWxseSwgbm90ZSB0aGF0IGJ5IGRlZmF1bHQgc2NhdGVyIHdpbGwgY29tcHV0ZSB0aGUgdG9wIDUwIFBDcy4gVGhpcyBpcyBhIHJlYXNvbmFibGUgY2hvaWNlLCBidXQgaXQgbWF5IGJlIGEgZ29vZCBpZGVhIHRvIGV4cGxvcmUgdGhlIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBlYWNoIGNvbXBvbmVudCB0byBkZWNpZGUgdGhlIG51bWJlciBvZiBjb21wb25lbnRzIHRvIHJldGFpbi4gQW4gYWx0ZXJuYXRpdmUgaXMgdG8gdXNlIHRoZSBkZW5vaXNlUENBIGZ1bmN0aW9uIGZyb20gc2NyYW4gdGhhdCBhaW1zIGF0IHNlbGVjdGluZyB0aGUgbnVtYmVyIG9mIFBDcyB0aGF0IGV4cGxhaW4gYmlvbG9naWNhbCB2YXJpYWJpbGl0eS4KCjxici8+PGJyLz4KCiMjIyBHTE0tUENBCgpBIGdlbmVyYWxpemF0aW9uIG9mIFBDQSB0byBleHBvbmVudGlhbCBmYW1pbHkgZGlzdHJpYnV0aW9ucyBpcyBwcm92aWRlIGJ5IFRvd25lcyBldCBhbC4gMjAxOS4KUENBIGlzIGJhc2VkIG9uIHNxdWFyZWQvRXVjbGlkZWFuIGRpc3RhbmNlcyAoZS5nLlRvd25lcyBldCBhbC4gMjAxOSksIHdoaWNoIGhhcyBhIGNsb3NlIGxpbmsgdG8gR2F1c3NpYW4gbGlrZWxpaG9vZCwgd2hpY2ggaXMgaW5hcHByb3ByaWF0ZSBmb3IgY291bnQgZGF0YSBkdWUgdG8gdGhlIG1lYW4tdmFyaWFuY2UgcmVsYXRpb25zaGlwLiAKVGhlcmVmb3JlLCBQQ0EgcmVzdWx0cyByaXNrcyB0byBiZSBkb21pbmF0ZWQgYnkgaGlnaGx5IGV4cHJlc3NlZCBnZW5lcywgd2hpY2ggaGF2ZSBsYXJnZSB2YXJpYW5jZS4gCgpgYGB7ciBnbG1QQ0FGaWcyUmVwLCBmaWcuY2FwPSJUb3duZXMgZXQgYWwuIDIwMTksIERPSTogMTAuMTE4Ni9zMTMwNTktMDE5LTE4NjEtNiIsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iMTAwJSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL2ltYWdlc19zZXF1ZW5jaW5nL2dsbVBDQUZpZzIucG5nIikKYGBgCgo8YnIvPgoKVGhleSBhcmd1ZSB0aGF0IFVNSSBkYXRhIGFyZSBtdWx0aW5vbWlhbGx5IGRpc3RyaWJ1dGVkLCB3aGljaCBjYW4gYmUgYXBwcm94aW1hdGVkIGJ5IGEgUG9pc3NvbiBkaXN0cmlidXRpb24gd2hlbiBjb25kaXRpb25pbmcgb24gdGhlIGxpYnJhcnkgc2l6ZSBhbmQgcHJvcG9zZSBhIGdlbmVyYWxpc2F0aW9uIG9mIFBDQS4gCgpgYGB7ciBnbG1QQ0FEZXJpdiwgZmlnLmNhcD0iVG93bmVzIGV0IGFsLiAyMDE5LCBET0k6IDEwLjExODYvczEzMDU5LTAxOS0xODYxLTYiLCBvdXQud2lkdGggPSAiNTAlIixlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9pbWFnZXNfc2VxdWVuY2luZy9nbG1QQ0FEZXJpdmF0aW9uLnBuZyIpCmBgYAoKPGJyLz48YnIvPgoKYGBge3IgZ2xtUENBT25EZXZSZXNpZHMsIGZpZy5jYXA9IlRvd25lcyBldCBhbC4gMjAxOSwgRE9JOiAxMC4xMTg2L3MxMzA1OS0wMTktMTg2MS02IiwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI4MCUifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9pbWFnZXNfc2VxdWVuY2luZy9nbG1QQ0FfT25EZXZpYW5jZVJlc2lkdWFscy5wbmciKQpgYGAKCjxici8+PGJyLz4KCi0gTm90ZSwgdGhhdCBmdWxsIGxlbmd0aCBwbGF0ZSBiYXNlZCBkYXRhIHNjUk5BLXNlcSBkYXRhIGFyZSBvdmVyIGRpc3BlcnNlIHdpdGggcmVzcGVjdCB0byB0aGUgUG9pc3NvbiBkaXN0cmlidXRpb24gYW5kIHRoZXJlZm9yZSBOQi1QQ0Egd291bGQgYmUgYmV0dGVyLgoKPGJyLz48YnIvPgoKIyMjIEdMTS1QQ0EgRXhhbXBsZSAKCkdMTS1QQ0EgaXMgaW1wbGVtZW50ZWQgaW4gdGhlIGBzY3J5YCBCaW9jb25kdWN0b3IgcGFja2FnZTsgaXQgdXNlcyBhIFBvaXNzb24gbW9kZWwgYnkgZGVmYXVsdCwgYnV0IHRoaXMgY2FuIGJlIGNoYW5nZWQgdG8gdXNlIG5lZ2F0aXZlIGJpbm9taWFsLCBtdWx0aW5vbWlhbCBvciBiaW5vbWlhbC4KClVubGlrZSBydW5QQ0EsIHRoZSBHTE0tUENBIGZ1bmN0aW9uIGRvZXMgbm90IGFsbG93IGZvciBhIHNlbGVjdGlvbiBvZiBnZW5lcy4gSGVuY2UsIHdlIHdpbGwgZmlyc3QgY3JlYXRlIGEgZmlsdGVyZWQgU2luZ2xlQ2VsbEV4cGVyaW1lbnQgb2JqZWN0IHRoYXQgY29udGFpbnMgb25seSB0aGUgSFZHcy4KClNpbWlsYXJseSB0byBQQ0EsIEdMTS1QQ0EgbmVlZHMgdGhlIG51bWJlciBvZiBjb21wb25lbnRzIChmYWN0b3JzKSB0aGF0IG5lZWQgdG8gYmUgY2FsY3VsYXRlZC4gSGVyZSwgd2UgY29tcHV0ZSAxMCBsYXRlbnQgZmFjdG9ycy4KCldlIHVzZSB0aGUgbWluaWJhdGNoIGFyZ3VtZW50IHRvIHVzZSBvbmx5IGEgcmFuZG9tIHN1YnNldCBvZiBvYnNlcnZhdGlvbnMgdG8gY29tcHV0ZSB0aGUgZ3JhZGllbnQgaW4gdGhlIG9wdGltaXphdGlvbiBhbGdvcml0aG0uIFRoaXMgc3BlZWRzIHVwIGNvbXB1dGF0aW9ucyBhbmQgYXZvaWRzIG1lbW9yeSBwcm9ibGVtcyBpbiBiaWcgZGF0YXNldHMuCgpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGlmKCEiTmV3V2F2ZSIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssMV0pIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJOZXdXYXZlIikKICBpZighInNjcnkiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLDFdKSBCaW9jTWFuYWdlcjo6aW5zdGFsbCgic2NyeSIpCn0pCmBgYAoKPGJyLz4KCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkoc2NyeSkKfSkKc2V0LnNlZWQoMTAwMDAwKQpmaWx0ZXJlZCA8LSBzY2VbaHZnLF0KZmlsdGVyZWQgPC0gR0xNUENBKGZpbHRlcmVkLCBMPTEwLCBtaW5pYmF0Y2ggPSAic3RvY2hhc3RpYyIpCmZpbHRlcmVkCmBgYAoKPGJyLz4KClRvIHZpc3VhbGl6ZSB0aGUgZmlyc3QgdHdvIGNvbXBvbmVudHMgb2YgR0xNLVBDQSwgd2UgY2FuIHVzZSB0aGUgcGxvdFJlZHVjZWREaW0gZnVuY3Rpb24gZnJvbSBgc2NhdGVyYC4KCmBgYHtyfQpwbG90UmVkdWNlZERpbShmaWx0ZXJlZCwgIkdMTVBDQSIpCmBgYAoKPGJyLz48YnIvPgoKIyMjIE5ld1dhdmUKCkFuIGFsdGVybmF0aXZlIG1ldGhvZCBpcyBpbXBsZW1lbnRlZCBpbiB0aGUgTmV3V2F2ZSBwYWNrYWdlLCB3aGljaCBpbXBsZW1lbnRzIGEgbmVnYXRpdmUgYmlub21pYWwgZmFjdG9yIGFuYWx5c2lzIG1vZGVsIGFuZCB1c2VzIGEgcGVuYWxpemVkIGxpa2VsaWhvb2QgdG8gZXN0aW1hdGUgdGhlIGxhdGVudCBmYWN0b3JzIChBZ29zdGluaXMgZXQgYWwuIDIwMjIgRE9JOiAxMC4xMDkzL2Jpb2luZm9ybWF0aWNzL2J0YWMxNDkpLiAKCjxici8+CgojIyMjIE5ld1dhdmUgRXhhbXBsZQoKTmV3V2F2ZSBjYW4gYWxzbyB1c2UgbWluaWJhdGNoZXMsIGFuZCBpZiBtdWx0aXBsZSBDUFVzIGFyZSBhdmFpbGFibGUgcGFyYWxsZWwgY29tcHV0aW5nLCB0byBzcGVlZCB1cCBjb21wdXRhdGlvbnMuIFRoZSBuX2dlbmVfcGFyLCBuX2NlbGxfcGFyLCBhbmQgbl9nZW5lX2Rpc3AgYXJndW1lbnRzIGFsbG93IHRoZSB1c2VyIHRvIGNob29zZSBob3cgbWFueSBvYnNlcnZhdGlvbnMgdG8gdXNlIHRvIGVzdGltYXRlIHRoZSBnZW5lLSBhbmQgY2VsbC1zcGVjaWZpYyBwYXJhbWV0ZXJzLiBUaGUgY2hpbGRyZW4gYXJndW1lbnQgYWxsb3dzIG9uZSB0byB1c2UgbXVsdGlwbGUgY29yZXM7IGhlcmUgd2UgdXNlIGZvdXIsIGJ1dCB5b3Ugc2hvdWxkIGNoYW5nZSBpdCBkZXBlbmRpbmcgb24gaG93IG1hbnkgY29yZXMgeW91ciBjb21wdXRlciBoYXMgKHNlZSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSkuCgoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShOZXdXYXZlKQp9KQpzZXQuc2VlZCgxMDAwMDApCmZpbHRlcmVkIDwtIG5ld1dhdmUoZmlsdGVyZWQsIEs9MTAsIGNoaWxkcmVuPTQsCiAgICAgICAgICAgICAgICAgICAgbl9nZW5lX2Rpc3AgPSAxMDAsIG5fZ2VuZV9wYXIgPSAxMDAsIAogICAgICAgICAgICAgICAgICAgIG5fY2VsbF9wYXIgPSAxMDApCmZpbHRlcmVkCnBsb3RSZWR1Y2VkRGltKGZpbHRlcmVkLCAibmV3V2F2ZSIpCmBgYAoKPGJyLz48YnIvPgoKIyMjIFRTTkUgYW5kIFVNQVAKCi0gTm9uLWxpbmVhciBkaW1lbnNpb24gcmVkdWN0aW9uIG1ldGhvZHMKCi0gVmlzdWFsaXphdGlvbnMgb2YgcmVkdWNlZCBkaW1lbnNpb25zIGZyb20gbGluZWFyIGRpbWVuc2lvbiByZWR1Y3Rpb24gbWV0aG9kcyBhcmUgb2Z0ZW4gCgnigJhvdmVyY3Jvd2RlZOKAmSwgYW5kIGl0IGlzIGhhcmQgdG8gc2VlIHN0cnVjdHVyZS4KCi0gTm9uLWxpbmVhciBkaW1lbnNpb24gcmVkdWN0aW9uIG1ldGhvZHMgY2FuIG92ZXJjb21lIHRoaXMgcHJvYmxlbS4gQXMgdGhlIG5hbWUgc3VnZ2VzdHMsIHRoZSByZWR1Y2VkIGRpbWVuc2lvbnMgYXJlIGEgbm9uLWxpbmVhciBmdW5jdGlvbiBvZiB0aGUgb2JzZXJ2ZWQgZGF0YS4gV2Ugd2lsbCBub3QgZ28gaW50byBkZXRhaWwgYXMgdG8gaG93IHRoZXNlIHdvcmsgdW5kZXIgdGhlIGhvb2QsIGJ1dCBwcm92aWRlIGEgZmV3IGd1aWRlbGluZXMgZm9yIHRoZSBtb3N0IHBvcHVsYXIgbWV0aG9kcy4gT2Z0ZW4sIHRoZSB0b3AgKDEwLTUwKSBQQ3MgYXJlIHByb3ZpZGVkIGFzIGlucHV0LgoJCi0gdC1TTkU6IFByZXNlcnZpbmcgbG9jYWwgcmF0aGVyIHRoYW4gZ2xvYmFsIGRpc3RhbmNlcy4gVGhlcmVmb3JlLCBkaXN0YW5jZXMgb24gYSB0LVNORSByZWR1Y2VkIGRpbWVuc2lvbiBwbG90IGNhbiBvbmx5IGJlIGludGVycHJldGVkIGxvY2FsbHksIGkuZS4sIGNlbGxzIHRoYXQgYXJlIGNsb3NlIHRvZ2V0aGVyIGluIHJlZHVjZWQgZGltZW5zaW9uIHdpbGwgaGF2ZSBhIHNpbWlsYXIgdHJhbnNjcmlwdG9tZSwgYnV0IGNlbGxzIHRoYXQgYXJlIGZhciBhd2F5IG1heSBub3QgbmVjZXNzYXJpbHkgaGF2ZSBhIHZlcnkgZGlzdGluY3QgdHJhbnNjcmlwdG9tZS4KCi0gVU1BUDogQ2xhaW1lZCB0byBiZSBiZXR0ZXIgdGhhbiB0LVNORSBvbiBwcmVzZXJ2aW5nIGdsb2JhbCBkaWZmZXJlbmNlcy4gVGhlcmVmb3JlLCBVTUFQIGlzIGFsc28gb2Z0ZW4gdXNlZCBpbiBhbmFseXNlcyBzdWNoIGFzIHRyYWplY3RvcnkgaW5mZXJlbmNlLCB3aGVyZSB0aGlzIGlzIGltcG9ydGFudC4KCjxici8+CgojIyMjIEV4YW1wbGUgCgpgYGB7cn0Kc2NlIDwtIHJ1blRTTkUoc2NlLCBkaW1yZWQ9IlBDQSIpCnBsb3RUU05FKHNjZSkKYGBgCgo8YnIvPgoKYGBge3J9CiAgaWYoISJ1d290IiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywxXSkgQmlvY01hbmFnZXI6Omluc3RhbGwoInV3b3QiKQpzY2UgPC0gcnVuVU1BUChzY2UsIGRpbXJlZCA9ICdQQ0EnLCBleHRlcm5hbF9uZWlnaGJvcnM9VFJVRSkKcGxvdFVNQVAoc2NlKQpgYGAKCjxici8+PGJyLz4KCiMjIENsdXN0ZXJpbmc6IHVuc3VwZXJ2aXNlZCBjZWxsIHR5cGUgaWRlbnRpZmljYXRpb24KCi0gVGhlIGlkZW50aWZpY2F0aW9uIG9mIGhvbW9nZW5lb3VzLCBiaW9sb2dpY2FsbHkgcmVsZXZhbnQgZW50aXRpZXMgdGhhdCBkZWZpbmUgYSBjb21tb24gY2VsbCBpZGVudGl0eS4KCi0gVHJhZGl0aW9uYWwgY2x1c3RlcmluZyBtZXRob2RzIGRvIG5vdCBwZXJmb3JtIGdyZWF0LCBvZnRlbiBncmFwaC1iYXNlZCBjbHVzdGVyaW5nIG9yIAplbnNlbWJsZSBtZXRob2RzIGFyZSB1c2VkLgoKPGJyLz4KCiMjIyBDb25jZXJucwoKLSBUaGVyZSBhcmUgbWFueSByZXNvbHV0aW9ucyBhdCB3aGljaCB0aGlzIGNhbiBiZSBkb25lLCBhbmQgdGhlcmUgaXMgbm8gc3VjaCB0aGluZyBhcyAKCWFuIG9wdGltYWwgY2VsbCB0eXBlIHJlc29sdXRpb247IGNlbGxzIGNhbiBiZSBkZXNjcmliZWQgYnkgYSBoaWVyYXJjaHkgb2YgZGlmZmVyZW50IGNlbGwgdHlwZXMgYW5kIHN0YXRlcy4gTW9yZSBjbHVzdGVycyBtYXkgcmVzdWx0IGluIGhpZ2hlciByZXNvbHV0aW9uIGNlbGwgdHlwZXMsIGJ1dCAKCWNhbiBoYW1wZXIgcmVwbGljYWJpbGl0eSBvZiB0aGUgcmVzdWx0cyBhY3Jvc3MgZGF0YXNldHMuIAo8YnIvPjxici8+CgojIyAoU2VtaS0pc3VwZXJ2aXNlZCBjZWxsIHR5cGUgaWRlbnRpZmljYXRpb24gCgotIFdlIGNhbiB1c2UgcHJpb3Iga25vd2xlZGdlIHRvIHJlbGlhYmx5IGlkZW50aWZ5IGNlbGwgY2x1c3RlcnMgYW5kIGFubm90YXRlIHRoZW0uCgotIFdlIGNhbiBhbm5vdGF0ZSBhIG5ldyBzY1JOQS1zZXEgZGF0YXNldCBieSB0cmFpbmluZyBhIGNsYXNzaWZpZXIgdGhhdCBwcmVkaWN0cyBjZWxsIHR5cGUgbGFiZWwgYmFzZWQgb24gZXhpc3RpbmcgZGF0YS4KCiAgMS4gR2F0aGVyIHJlZmVyZW5jZSBkYXRhIHRoYXQgYXJlIHJlbGV2YW50IGZvciB5b3VyIGN1cnJlbnQgZGF0YXNldCB5b3Ugd291bGQgbGlrZSB0byBhbm5vdGF0ZS4KICAyLiBUcmFpbiBhIGNsYXNzaWZpZXIgKGxhc3NvL3JpZGdlLCBTVk0sIG5ldXJhbCBuZXQsIGtOTiwg4oCmKSB0byBjbGFzc2lmeSBjZWxscyBpbnRvIGNlbGwgdHlwZXMsIGdpdmVuIGdlbmUgZXhwcmVzc2lvbiBkYXRhLgogIDMuIFVzZSBwcmUtdHJhaW5lZCBjbGFzc2lmaWVyIHRvIGVzdGltYXRlLCBmb3IgZWFjaCBjZWxsLCAKCVAoY2VsbCBpIGlzIGNlbGwgdHlwZSBaIHwgZ2VuZSBleHByZXNzaW9uIG9mIGNlbGwgaSkuCgo8YnIvPgoKIyMjIENvbmNlcm5zCgpUaGUgcmVmZXJlbmNlIGRhdGEgdXNlZCBmb3IgdHJhaW5pbmcgdGhlIGNsYXNzaWZpZXIgbXVzdCBiZSByZXByZXNlbnRhdGl2ZSBmb3IgdGhlIGRhdGFzZXQgd2Ugd291bGQgbGlrZSB0byBhbm5vdGF0ZS4gUHJvYmxlbXMgbWF5IG9jY3VyIGlmIGUuZy4sIGRpZmZlcmVudCB0ZWNobm9sb2dpZXMgYXJlIHVzZWQsIG9yIHBhcnRpY3VsYXIgKHJhcmUpIGNlbGwgdHlwZXMgYXJlIG5vdCBwcmVzZW50IGluIHRoZSByZWZlcmVuY2UgZGF0YS4KCjxici8+PGJyLz4KCiMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gKERFKSwgZGlzY292ZXJ5IG9mIG1hcmtlciBnZW5lcwoKLSBERSBpcyBvZnRlbiBhIGZpcnN0IHN0ZXAgdG93YXJkcyBpbnRlcnByZXRpbmcgZGlmZmVyZW5jZXMgYmV0d2VlbiBjZWxsIGdyb3VwcywgYW5kIHByb3ZpZGVzIAoJYSBiYXNpcyBmb3IgZnVydGhlciBiaW9sb2dpY2FsIHZhbGlkYXRpb24uCgotIERFIGFuYWx5c2lzIGluIHNjUk5BLXNlcSBpcyB0eXBpY2FsbHkgc2ltaWxhciB0byBidWxrIFJOQS1zZXEsIGUuZy4gZWRnZVIgYW5hbHlzaXMuIEluIGZhY3QsIGR1ZSB0byB0aGUgaGlnaCBudW1iZXIgb2YgY2VsbHMsIG9mdGVuIHNpbXBsZXIgbWV0aG9kcyBtYXkgYmUgdXNlZCwgZS5nLiBXaWxjb3hvbiB0ZXN0cy4KCjxici8+CgojIyBDb25jZXJucwoKLSBUaGVyZSBpcyBhbiBvbmdvaW5nIGRlYmF0ZSBpbiB0aGUgY29tbXVuaXR5IGFib3V0IHRoZSBhcHByb3ByaWF0ZSBub2lzZSBtb2RlbCBmb3Igc2NSTkEtc2VxIGRhdGEuIEZvciBleGFtcGxlLCBhcmUgdGhlIGRhdGEgemVybyBpbmZsYXRlZD8KCi0gUG9zdC1zZWxlY3Rpb24gaW5mZXJlbmNlIHByb2JsZW0sIHRoZSBkYXRhIGFyZSB1c2VkIHR3aWNlCiAgMS4gQ2x1c3RlcgogIDIuIEZpbmQgYmlvbWFya2VycyBiZXR3ZWVuIGNsdXN0ZXJzCiAgCi0gUHNldWRvLXJlcGxpY2F0aW9uIGluIG11bHRpLXN1YmplY3QtbXVsdGktY2VsbCBleHBlcmltZW50cyEgCgo8YnIvPgoKIyMgWmVyby1pbmZsYXRpb24gCgpgYGB7ciB6ZXJvSW5mbGF0aW9uMSwgb3V0LndpZHRoPSIxMDAlIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvemVyb0luZmxhdGlvbi5wbmciKQpgYGAKCjxici8+PGJyLz4KCmBgYHtyIG91dC53aWR0aD0iNTAlIiwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShNQVNTKQojIyMjIyBGVUxMLUxFTkdUSCBQUk9UT0NPTFMgIyMjIyMjCnNldC5zZWVkKDkpCiMjIHNpbXVsYXRlIE5CCng9cm5iaW5vbShuPTQwMCwgbXU9NDMwLCBzaXplPTQpCiMjIGVzdGltYXRlIE5CIHBhcmFtCm0gPC0gZ2xtLm5iKHh+MSkKaGlzdCh4LCBmcmVxPUZBTFNFLCBicmVha3M9NDAsIG1haW49Ik5CIERhdGEiKQpncmlkIDwtIHJvdW5kKHNlcSgwLG1heCh4KSxsZW5ndGg9NTAwKSkKbGluZXMoeD1ncmlkLCB5PWRuYmlub20oZ3JpZCwgbXU9ZXhwKGNvZWYobSkpLCBzaXplPW0kdGhldGEpLCBjb2w9InJlZCIsIGx3ZD0zKQoKIyMgYWRkIFpJIGFuZCByZS1lc3RpbWF0ZQp4WzE6MTVdPTAKbU5CIDwtIGdsbS5uYih4fjEpCmhpc3QoeCwgZnJlcT1GQUxTRSwgYnJlYWtzPTQwLCBtYWluPSJaSU5CIERhdGEiKQpsaW5lcyh4PWdyaWQsIHk9ZG5iaW5vbShncmlkLCBtdT1leHAoY29lZihtTkIpKSwgc2l6ZT1tTkIkdGhldGEpLCBjb2w9Im9yYW5nZSIsIGx3ZD0zKQoKIyMgWklOQiBlc3RpbWF0aW9uCncgPC0gYyhyZXAoMCwgMTUpLCByZXAoMSwgNDAwLTE1KSkKbVpJTkIgPC0gZ2xtLm5iKHh+MSwgd2VpZ2h0cz13KQpsaW5lcyh4PWdyaWQsIHk9ZG5iaW5vbShncmlkLCBtdT1leHAoY29lZihtWklOQikpLCBzaXplPW1aSU5CJHRoZXRhKSwgY29sPSJkYXJrc2VhZ3JlZW4zIiwgbHdkPTMpCmxpbmVzKHg9cmVwKDAsIDEwKSwgeT1zZXEoMCwgMC4wMDA1LCBsZW5ndGg9MTApLCBjb2w9ImRhcmtzZWFncmVlbjMiLCBsd2Q9MykKbGVnZW5kKCJ0b3ByaWdodCIsIGMoIk5CIiwgIlpJTkIiKSwgY29sPWMoIm9yYW5nZSIsICJkYXJrc2VhZ3JlZW4zIiksIGx0eT0xLCBsd2Q9MywgYnR5PSduJykKYGBgCgo8YnIvPgoKLSBNb3RpdmF0aW9uIGZvciB6ZXJvLWluZmxhdGlvbiBpcyB0aGF0IHdlIG1heSBjb25zaWRlciB0d28gdHlwZXMgb2YgemVyb3MgaW4gc2NSTkEtc2VxIGRhdGE6CgkKCS0gIEJpb2xvZ2ljYWwgemVyb3M6IFdlIG9ic2VydmUgYSB6ZXJvIGNvdW50IGJlY2F1c2UgdGhlIGdlbmUgaXMgbm90IGV4cHJlc3NlZCBpbiB0aGF0IGNlbGwuCgktIFRlY2huaWNhbCB6ZXJvczogRXZlbiB0aG91Z2ggdGhlIGdlbmUgd2FzIGV4cHJlc3NlZCBpbiB0aGF0IGNlbGwsIHdlIGRvIG5vdCBvYnNlcnZlIGFueSAKCQltb2xlY3VsZXMgZHVlIHRvIHRlY2huaWNhbCByZWFzb25zIChlLmcuLCBsb3cgY2FwdHVyZSBlZmZpY2llbmN5IC8gc2VxdWVuY2luZyBkZXB0aCkuCgotIEEgemVyby1pbmZsYXRlZCBjb3VudCBkaXN0cmlidXRpb24gaXMgYSBtaXh0dXJlIGRpc3RyaWJ1dGlvbiB0aGF0IGNvbnNpc3RzIG9mIGEgcG9pbnQgbWFzcyBhdCB6ZXJvIGFuZCBhIGNvdW50IGNvbXBvbmVudC4gRm9yIGV4YW1wbGUsIGEgemVyby1pbmZsYXRlZCBuZWdhdGl2ZSBiaW5vbWlhbCAoWklOQikgZGlzdHJpYnV0aW9uLgoKJCQgClxiZWdpbnthcnJheX17bGNsfQpZX3tpZ30gJiBcc2ltICYgWklOQihcbXVfe2lnfSwgXHBoaV9nLCBccGlfe2lnfSlcXFxcCmYoWV97aWd9KSAmPSYgXHBpX3tpZ30gXGRlbHRhXzAgKyAoMS1ccGlfe2lnfSkgZl97TkJ9KFxtdV97aWd9LFxwaV97aWd9KQpcZW5ke2FycmF5fQokJAoKT3VyIHRha2U6IAoKLSBEYXRhIGZyb20gZnVsbC1sZW5ndGggcHJvdG9jb2xzIGNvbnNpc3Qgb2YgaGlnaCBjb3VudHMgYW5kIG1hbnkgemVyb3MsIGFuZCBhcmUgbW9yZSBsaWtlbHkgdG8gYmUgemVybyBpbmZsYXRlZC4gCgotIEluc3RlYWQsIGRhdGEgZnJvbSBVTUkgZHJvcGxldC1iYXNlZCBwcm90b2NvbHMgY29uc2lzdCBvZiBsb3cgY291bnRzIHdpdGggbWFueXplcm9zLiBUaGlzIHNlZW1zIHRvIHJlYXNvbmFibHkgZml0IHVuZGVyIGEgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uLCBhbmQgemVybyBpbmZsYXRpb24gbWF5IG5vdCBiZSBuZWNlc3NhcnkuCgo8YnIvPgoKIyMgUHNldWRvLXJlcGxpY2F0aW9uIGluIG11bHRpLWNlbGwtbXVsdGktcGF0aWVudCBzY1JOQS1zZXEgZGF0YQoKLSBJbiBtdWx0aS1wYXRpZW50IGRhdGFzZXRzLCB3ZSBoYXZlIGEgaGllcmFyY2hpY2FsIGNvcnJlbGF0aW9uIHN0cnVjdHVyZS4KCi0gQ2VsbHMgYXJlIG5lc3RlZCB3aXRoaW4gcGF0aWVudCAkXHJpZ2h0YXJyb3ckIEV4cGVjdCBjb3JyZWxhdGlvbiBiZXR3ZWVuIGNlbGxzIGZyb20gdGhlIHNhbWUgcGF0aWVudC4KCmBgYHtyIHBzZXVkb3JlcGxpY2F0aW9uLCBmaWcuY2FwPSJJbWFnZSBjcmVkaXQgTWlsYW4gTWFsZmFpdCIsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSIxMDAlIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvbXVsdGktY2VsbC1tdWx0aS1wYXRpZW50LnBuZyIpCmBgYAoKPGJyLz4KCi0gV2hlbiBhc3Nlc3NpbmcgZGlmZmVyZW5jZXMgaW4gbWVhbnMgd2l0aGluIGNlbGwgdHlwZXMgYmV0d2VlbiBkaWZmZXJlbnQgdHJlYXRtZW50cywgd2UgY2FuIGJ1bGsgdGhlIGNlbGxzIG9mIHRoZSBzYW1lIGNlbGwgdHlwZXMgdG8gcGVyZm9ybSBwc2V1ZG9idWxrcyBwZXIgc3ViamVjdC4gCgotIFRoZXNlIHBzZXVkb2J1bGsgc2FtcGxlcyBjYW4gdGhhbiBiZSBhc3Nlc3NlZCB1c2luZyBjb252ZW50aW9uYWwgYnVsayBSTkEtc2VxIHRvb2xzLiAgCgoKCmBgYHtyIHBzZXVkb3JlcGxpY2F0aW9uMiwgZmlnLmNhcD0iQ3Jvd2VsbCBldCBhbC4gMjAyMCAoRE9JOiAxMC4xMDM4L3M0MTQ2Ny0wMjAtMTk4OTQtNCkiLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjgwJSIsIGZpZy5hbGlnbj0nY2VudGVyJ30Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvbXVzY2F0LnBuZyIpCmBgYAoKPGJyLz4KCmBgYHtyIHBzZXVkb1JlcGxpY2F0aW9uRlAsIGZpZy5jYXA9Ikp1bnRpbGxhIGV0IGFsLiAyMDIyIChET0kgMTAuMTA5My9iaWIvYmJhYzI4NikiLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjEwMCUifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9pbWFnZXNfc2VxdWVuY2luZy9wc2V1ZG9SZXBsaWNhdGlvbkZhbHNlUG9zaXRpdmVzLnBuZyIpCmBgYAoKPGJyLz4KCmBgYHtyIHBzZXVkb1JlcGxpY2F0aW9uQVVDLCBmaWcuY2FwPSJTcXVhaXIgZXQgYWwuIDIwMjEgKERPSSAxMC4xMDM4L3M0MTQ2Ny0wMjEtMjU5NjAtMikiLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjEwMCUifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9pbWFnZXNfc2VxdWVuY2luZy9wc2V1ZG9yZXBsaWNhdGlvbkNvbXBhcmlzb24ucG5nIikKYGBgCgo8YnIvPgoKLSBJbXBsZW1lbnRhdGlvbiBzZWUgW09TQ0EgQm9va10oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xNi9PU0NBLm11bHRpc2FtcGxlL211bHRpLXNhbXBsZS1jb21wYXJpc29ucy5odG1sI2NyZWF0aW5nLXBzZXVkby1idWxrLXNhbXBsZXMpCgo8YnIvPjxici8+CgojIyBEaWZmZXJlbnRpYWwgRGlzdHJpYnV0aW9ucwoKLSBBIGZldyBtZXRob2RzIGV4aXN0IHRoYXQgY29tcGFyZSBkaXN0cmlidXRpb25zIGJldHdlZW4gdHJlYXRtZW50cy4gCgotIEJ1dCwgdGhleSBkbyBub3QgYWxsb3cgdG8gaW50ZXJwcmV0IHRoZSBkaWZmZXJlbmNlIGluIHRoZSBkaXN0cmlidXRpb25zLgoKCiMgQWNrbm93bGVkZ2VtZW50cyB7LX0KClRoYW5rcyB0byBEYXZpZGUgUmlzc28gZm9yIHNoYXJpbmcgaGlzIG1hdGVyaWFscyBmcm9tIHRoZSBDU0FNQSAyMDIyIHdvcmtzaG9wLCB3aGljaCBJIHVzZWQgYXMgdGhlIHN0YXJ0aW5nIHBvaW50IGZvciB0aGUgUiBjb2RlIGluIHRoaXMgZG9jdW1lbnQu