1 Preamble: installation of Bioconductor libraries

# install BiocManager package if not installed yet.
# BiocManager is the package installer for Bioconductor software.
if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
## Installing BiocManager [1.30.16] ...
##  OK [linked cache]
# install packages if not yet installed.
pkgs <- c("SingleCellExperiment",
          "ExperimentHub",
          "edgeR",
          "biomaRt",
          "DropletUtils", 
          "scRNAseq", 
          "scater", 
          "scuttle", 
          "scran",
          "scry",
          "BiocSingular", 
          "scDblFinder",
          "glmpca",
          "PCAtools",
          "Seurat")
notInstalled <- pkgs[!pkgs %in% installed.packages()[,1]]
if(length(notInstalled) > 0){
  BiocManager::install(notInstalled)
}
## 'getOption("repos")' replaces Bioconductor standard repositories, see
## '?repositories' for details
## 
## replacement repositories:
##     CRAN: https://cran.rstudio.com
## Bioconductor version 3.14 (BiocManager 1.30.16), R 4.1.2 (2021-11-01)
## Installing package(s) 'BiocVersion', 'SingleCellExperiment', 'ExperimentHub',
##   'edgeR', 'biomaRt', 'DropletUtils', 'scRNAseq', 'scater', 'scuttle', 'scran',
##   'scry', 'BiocSingular', 'scDblFinder', 'glmpca', 'PCAtools', 'Seurat'
## also installing the dependencies 'bit', 'sys', 'formatR', 'rjson', 'fs', 'GenomeInfoDbData', 'zlibbioc', 'bit64', 'blob', 'memoise', 'plogr', 'DT', 'generics', 'tidyselect', 'assertthat', 'askpass', 'lambda.r', 'futile.options', 'GenomicAlignments', 'restfulr', 'bitops', 'Rhtslib', 'cpp11', 'rprojroot', 'gtools', 'caTools', 'sass', 'jquerylib', 'MatrixGenerics', 'Biobase', 'GenomeInfoDb', 'XVector', 'RSQLite', 'interactiveDisplayBase', 'dplyr', 'dbplyr', 'DBI', 'filelock', 'KEGGREST', 'hms', 'prettyunits', 'openssl', 'futile.logger', 'snow', 'sparseMatrixStats', 'rhdf5filters', 'R.oo', 'R.methodsS3', 'sitmo', 'AnnotationFilter', 'rtracklayer', 'Rsamtools', 'ProtGenerics', 'Biostrings', 'RCurl', 'BiocIO', 'RcppHNSW', 'beeswarm', 'vipor', 'data.table', 'plyr', 'globals', 'listenv', 'parallelly', 'zoo', 'htmlwidgets', 'tidyr', 'lazyeval', 'crosstalk', 'purrr', 'promises', 'here', 'gplots', 'RcppArmadillo', 'httpuv', 'xtable', 'fontawesome', 'sourcetools', 'later', 'commonmark', 'bslib', 'cachem', 'spatstat.data', 'spatstat.utils', 'spatstat.sparse', 'abind', 'tensor', 'goftest', 'deldir', 'polyclip', 'FNN', 'RSpectra', 'SummarizedExperiment', 'S4Vectors', 'BiocGenerics', 'GenomicRanges', 'DelayedArray', 'AnnotationHub', 'BiocFileCache', 'curl', 'rappdirs', 'limma', 'locfit', 'Rcpp', 'XML', 'AnnotationDbi', 'progress', 'httr', 'xml2', 'IRanges', 'BiocParallel', 'DelayedMatrixStats', 'HDF5Array', 'rhdf5', 'R.utils', 'dqrng', 'beachmat', 'Rhdf5lib', 'BH', 'ensembldb', 'GenomicFeatures', 'gridExtra', 'BiocNeighbors', 'ggbeeswarm', 'viridis', 'Rtsne', 'ggrepel', 'igraph', 'statmod', 'bluster', 'metapod', 'ScaledMatrix', 'irlba', 'rsvd', 'xgboost', 'cowplot', 'reshape2', 'fitdistrplus', 'future', 'future.apply', 'ggridges', 'ica', 'leiden', 'lmtest', 'matrixStats', 'miniUI', 'patchwork', 'pbapply', 'plotly', 'png', 'RANN', 'RcppAnnoy', 'reticulate', 'ROCR', 'scattermore', 'sctransform', 'SeuratObject', 'shiny', 'spatstat.core', 'spatstat.geom', 'uwot', 'RcppEigen', 'RcppProgress'
## 
##   There are binary versions available but the source versions are later:
##          binary   source needs_compilation
## httpuv    1.6.3    1.6.4              TRUE
## BH     1.75.0-0 1.78.0-0             FALSE
## igraph    1.2.9   1.2.10              TRUE
## 
## 
## The downloaded binary packages are in
##  /var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T//RtmpDprmjP/downloaded_packages
## installing the source packages 'GenomeInfoDbData', 'httpuv', 'BH', 'igraph', 'scRNAseq'
## Old packages: 'crayon', 'digest', 'glue', 'knitr', 'lattice', 'lifecycle',
##   'mgcv', 'mime', 'nlme', 'pillar', 'rlang', 'rmarkdown', 'stringi', 'tibble',
##   'tinytex', 'withr', 'xfun', 'Matrix'

2 The Macosko dataset

In this workshop session, we will analyze the single-cell RNA-seq dataset from the publication by Macosko et al., Cell 161, 1202–1214 from 2015 (link). This is the manuscript in which the droplet scRNA-seq technology Drop-seq was introduced. Six years after the original publication, drop-seq is still one of the most commonly adopted scRNA-seq protocols, as evidenced by the large number of citations for Macosko et al. (4.303 citations at November 3, 2021).

The basic idea behind the Drop-seq protocol can be taken from the graphical abstract of the publication.

knitr::include_graphics("macosko_graphicalAbstract.jpeg")

The success of Drop-seq can be explained by the following advantageous features:

  • The use of unique molecular identifiers (UMIs). By working with UMIs, one count corresponds to one observed mRNA molecule present in the cell. Thanks to the use of UMI barcodes, PCR artifacts are reduced.

  • Scalability: microfluidics technology allows for performing the library prep reactions inside nanodroplets, in which single cells may be contained. Library prep occurs across all droplets simultaneously.

  • Cost: the experiment costs around 6.5 cents (USD) per cell.

  • Speed: The very large dataset that we will be working with today was generated in an experiment that took only 4 days.

In this particular experiment, Macosko et al. sequenced 49,300 cells from the mouse retina, identifying 39 transcriptionally distinct cell populations. The experiment was performed in 7 batches.

3 Data availability

3.1 SRA

The Sequence Read Archive (SRA) is the largest publicly available repository of high-throughput sequencing data. The data are stored by the National Center for Biotechnology Information (NCBI) services and multiple cloud storage providers. From this website “raw” sequencing data can be retrieved. In practice, these are usually .sra files, which can be downloaded and converted into FASTQ files using functions from the sratoolkit software.

For our dataset, the FASTQ data can be retrieved from this link. The data are stored as one file per sequencing batch, with each file approximately 20Gb. As such, it will be unfeasible to download and process these FASTQ files in this practical session.

Instead, for demonstrative purposes, we have taken a subsample of the FASTQ file for the first sequencing batch for you to work with. On this subsample, we may perform all the tasks that we would have performed on the full dataset. The steps that are required for downloading and quantifying drop-seq data can be found in a Shell script on our companion GitHub repository.

3.2 GEO

The dataset from Macosko et al. was also uploaded by the authors on the Gene Expression Omnibus (GEO) platform under accession number GSE63472, from which raw and readily-processed data files may be retrieved, including:

  1. GSE63472_RAW.tar, a 90.6Gb object that contains the “raw data” for the experiment. In the scRNA-seq context, FASTQ files are often considered the raw data format.

  2. GSE63472_P14Retina_merged_digital_expression.txt.gz, a 50.7Mb matrix that stores the gene expression values for each cell. These values are integer counts, that did not undergo any type of preprocessing or normalization.

  3. GSE63472_mm10_reference_metadata.tar.gz, a 862.9Mb compressed folder containing information on the reference genome to which the scRNA-seq reads were aligned (see theory slides).

  4. GSE63472_P14Retina_logDGE.txt.gz, a 316.8Mb compressed text file, not clear what it contains (results from a differential gene expression analysis, but with log-transformation, so log-fold changes maybe?).

As such, by downloading GSE63472_P14Retina_merged_digital_expression.txt.gz, we avoid re-quantifying the data, i.e., the translation from reads from the FASTQ files into a gene-level expression values for each cell.

One issue that often arises from data downloaded from GEO, is that there is no strict requirements for which data should be included in the upload by the authors. As such, from my personal experience, it can often be the case that important information like metadata are missing, or the content of the submitted files is unclear. Even if all the required data are available, as is the case here, we would still need to piece all the information together from different files and file formats before we can use them.

3.3 ExperimentHub

The Bioconductor ExperimentHub web resource, which can be accessed using the ExperimentHub R package, provides a central location where curated data from experiments, publications or training courses can be accessed. While it contains far less datasets than the SRA or GEO (4965 records to date), these datasets all follow the tidy data format of Bioconductor. Note that the ExperimentHub contains several types of data, like bulk and single-cell transcriptomics data, microarrays and more.

The Macosko dataset is available from ExperimentHub and can be accessed as follows:

library(ExperimentHub)
edb <- ExperimentHub()

edb[grep("Macosko", edb$title)] # find accession number (can be inefficient)

edb_counts <- edb[["EH2690"]]
edb_counts[1:5,1:5]

edb_coldata <- edb[["EH2691"]]
edb_coldata[1:5,]

rm(edb, edb_counts, edb_coldata)

3.4 scRNASeq

In addition to ExperimentHub, Bioconductor provides the package scRNAseq. This package provides an even more user-friendly client to access (only) scRNA-seq datasets from the ExperimentHub web resource. Data retrieved using the scRNAseq package are stored as user-friendly SingleCellExperiment objects, with the expression data, gene-level information, cell-level information and experiment metadata all in place in one data object. The scRNA-seq package currently holds 61 datasets, including the data from Macosko et al.:

library(scRNAseq)
scRNAseq::MacoskoRetinaData()

4 Import data

The scRNAseq package provides convenient access to several datasets. See the package Bioconductor page for more information.

# Code below might ask you to create an ExperimentHub directory. 
# Type 'yes' and hit Enter, to allow this.
suppressPackageStartupMessages(library(scRNAseq))
sce <- MacoskoRetinaData()

5 A SingleCellExperiment object

sce

5.1 Accessing data from a SingleCellExperiment object

Please see Figure 4.1 in OSCA for an overview of a SingleCellExperiment object.

# Data: assays
assays(sce)
assays(sce)$counts[1:5, 1:5]

# Feature metadata: rowData
rowData(sce) # empty for now

# Cell metadata: colData
colData(sce)

# Reduced dimensions: reducedDims
reducedDims(sce) # empty for now

5.2 Creating a new SingleCellExperiment object

sceNew <- SingleCellExperiment(assays = list(counts = assays(sce)$counts))
sceNew

rm(sceNew)

5.3 Storing (meta)data in a SingleCellExperiment object

fakeGeneNames <- paste0("gene", 1:nrow(sce))
rowData(sce)$fakeName <- fakeGeneNames
head(rowData(sce))
# Remove again by setting to NULL
rowData(sce)$fakeName <- NULL

assays(sce)$logCounts <- log1p(assays(sce)$counts)
assays(sce)
assays(sce)$logCounts[1:5, 1:5]
assays(sce)$logCounts <- NULL

6 Obtaining and including rowData

The rowData slot of a SingleCellExperiment object allows for storing information on the features, i.e. the genes, in a dataset. In our object, the rowData slot is empty.

rowData(sce)

As such, the only information we have on the genes are their names, which can be retrieved as the rownames of the expression matrix.

head(rownames(sce))

These are the gene names (symbols). Note that it may be useful to include additional information in the rowData slot. For instance, we may want to store:

  • Unambiguous gene identifiers (e.g. from ENSEMBL)
  • On which chromosome the gene is located
  • Gene length (genomic start position and end position)
  • Others…
library("biomaRt")

ensembl75 <- useEnsembl(biomart = 'genes', 
                        dataset = 'mmusculus_gene_ensembl',
                        version = 75)

head(listAttributes(ensembl75)) # potential info to extract

geneInfo <- getBM(attributes = c("ensembl_gene_id", # ENSEMBL unambiguous identifier
                                 "mgi_symbol", # Gene symbol (to link with SCE rownames),
                                 "chromosome_name", # On which chromosome
                                 "start_position", # Start position
                                 "end_position"),# End position
                  mart = ensembl75)

head(geneInfo)
geneInfo$mgi_symbol_upper <- toupper(geneInfo$mgi_symbol) 
# match between gene info and rownames

sum(rownames(sce) %in% geneInfo$mgi_symbol_upper)
sum(!rownames(sce) %in% geneInfo$mgi_symbol_upper) # lost in conversion :(

rowData(sce) <- geneInfo[match(rownames(sce),geneInfo$mgi_symbol_upper),]
head(rowData(sce))

7 Filtering non-informative genes

library(edgeR)

# A very simple strategy: remove all genes that are expressed in less than 10
# out of 49300 cells -> note that this a very lenient filtering criterium
keep <- rowSums(assays(sce)$counts > 0) > 10
table(keep)

sce <- sce[keep,]

Note that dedicated functions for filtering out lowly expressed genes exist. One such function is the filterByExpr function of the edgeR R package. In brief, the strategy keeps genes that have at least “min.count” reads in a worthwhile number samples.

More precisely, the filtering keeps genes that have count-per-million (CPM) above k in n samples.

k is determined by the min.count argument to the function, and by the sample library sizes.

n is determined by the design matrix. n can be seen as the smallest group sample size. A group of samples/cells can be defined as cells that are more similar to one another, e.g., from the same sequencing batch, the same patient…. Here we could also use the cluster assignment (cell type) for each cell as the grouping variable; note however, that this usually is not available until a later stage in the analysis pipeline (i.e., after dimension reduction and clustering, topics we will cover next session.)

If all the group sizes are larger than the large.n argument of the filterByExpr function, which defaults to 10, then n will be taken as min.prop* the the number of samples/cells in the smallest group.

Note that all the group sizes will often be larger than the large.n in case of single-cell data.

# Slow (more than 1min) -> do not run
# keep2 <- filterByExpr(sce,
#                       group = sce$cluster,
#                       min.count = 1,
#                       min.total.count = 15,
#                       min.prop = 0.2)
# table(keep2)

8 Quality control

In quality control (QC), we check the quality of our dataset. In particular, we investigate undesirable oddities, such as low-quality cells, empty droplets or doublets.

8.1 Identifying and removing low-quality cells

There are several distinguishing features of low-quality cells that can be used in order to identify them. As described in the OSCA book book):

  1. The library size is defined as the total sum of counts across all relevant features for each cell. Here, we will consider the relevant features to be the endogenous genes. Cells with small library sizes are of low quality as the RNA has been lost at some point during library preparation, either due to cell lysis or inefficient cDNA capture and amplification.

  2. The number of expressed features in each cell is defined as the number of endogenous genes with non-zero counts for that cell. Any cell with very few expressed genes is likely to be of poor quality as the diverse transcript population has not been successfully captured.

  3. We sometimes have spike-in (ERCC) transcripts available. The proportion of reads mapped to spike-in transcripts is calculated relative to the total count across all features (including spike-ins) for each cell. As the same amount of spike-in RNA should have been added to each cell, any enrichment in spike-in counts is symptomatic of loss of endogenous RNA.

  4. In the absence of spike-in transcripts, the proportion of reads mapped to genes in the mitochondrial genome can be used. High proportions are indicative of poor-quality cells (Islam et al. 2014; Ilicic et al. 2016), presumably because of loss of cytoplasmic RNA from perforated cells. The reasoning is that, in the presence of modest damage, the holes in the cell membrane permit efflux of individual transcript molecules but are too small to allow mitochondria to escape, leading to a relative enrichment of mitochondrial transcripts. For single-nuclei RNA-seq experiments, high proportions are also useful as they can mark cells where the cytoplasm has not been successfully stripped.

8.2 Calculate QC variables

This function calculates useful QC metrics for identification and removal of potentially problematic cells. Default per-cell metrics are the sum of counts (i.e., the library size) and the number of detected features. The percentage of counts in the top features also provides a measure of library complexity.

If subsets is specified, these statistics are also computed for each subset of features. This is useful for investigating gene sets of interest, e.g., mitochondrial genes.

library(scater)

# check ERCC spike-in transcripts
sum(grepl("^ERCC-", rownames(sce))) # no spike-in transcripts available

# check mitochondrial genes
sum(rowData(sce)$chromosome_name=="MT",na.rm = TRUE) # 28 mitochondrial genes
sum(grepl("^MT-", rownames(sce))) # alternatively
is.mito <- grepl("^MT-", rownames(sce))

## calculate QC metrics
df <- perCellQCMetrics(sce, subsets=list(Mito=is.mito))
head(df)

# add QC variables to sce object
colData(sce) <- cbind(colData(sce), df)

# the QC variables have now been added to the colData of our SCE object.
head(colData(sce))

8.3 Exploratory data analysis

In the figure below, 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 we should remove these for the downstream analysis.

# Number of genes vs library size
plotColData(sce, x = "sum", y="detected", colour_by="cluster") 

# Mitochondrial genes
plotColData(sce, x = "detected", y="subsets_Mito_percent")

8.4 QC using adaptive thresholds

Below, we 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.

Here we will remove cells for QC based on adaptive thresholds related to the three points from above. Adaptive trhesholds are used as opposed to fixed thresholds.

With fixed thresholds, we use fixed cut-off values for each cell to pass QC, e.g., we might consider cells to be low quality if they have library sizes below 100,000 reads; express fewer than 5,000 genes; have spike-in proportions above 10%; or have mitochondrial proportions above 10%.

With adaptive thresholds, we assume that most of the dataset consists of high-quality cells. We then identify cells that are outliers for the various QC metrics, based on the median absolute deviation (MAD) from the median value of each metric across all cells. By default, we consider a value to be anoutlier if it is more than 3 MADs from the median in the “problematic” direction. This is loosely motivated by the fact that such a filter will retain 99% of non-outlier values that follow a normal distribution. We demonstrate adopting adaptive thresholds on the Macosko dataset:

lowLib <- isOutlier(df$sum, type="lower", log=TRUE)
lowFeatures <- isOutlier(df$detected, type="lower", log=TRUE)
highMito <- isOutlier(df$subsets_Mito_percent, type="higher")

table(lowLib)
table(lowFeatures)
table(highMito)

discardCells <- (lowLib | lowFeatures | highMito)
table(discardCells)
colData(sce)$discardCells <- discardCells

# visualize cells to be removed
plotColData(sce, x = "sum", y="detected", colour_by="discardCells")
plotColData(sce, x = "detected", y="subsets_Mito_percent", colour_by = "discardCells")

We removed a total of \(3423\) cells, most of which because of an outlyingly high percentage of reads from mitochondrial genes.

8.5 Identifying and removing empty droplets

Note that the removal of cells with low sequencing depth using the adaptive threshold procedure above is a way of removing empty droplets. Other approaches are possible, e.g., removing cells by statistical testing using emtpyDrops. This does require us to specify a lower bound on the total number of UMIs, below which all cells are considered to correspond to empty droplets. This lower bound may not be trivial to derive, but the barcodeRanks function can be useful to identify an elbow/knee point.

In brief, the steps taken by the emtpyDrops function can be summarized as follows:

  1. Define threshold T of total UMI counts (e.g. with the help of the barcodeRanks function), below which cells may be considered to be from empty droplets. Call this set of cells E.

  2. Define \(A_g\) as the total gene expression across all cells in E.

  3. Define \(pi_g\) as the relative contribution of gene g to the ambient profile.

  4. Calculate p-value for each cell to have a transcriptional profile similar to the ambient solution. Intuitively, a p-value below the requested alpha level would correspond to a cell for which the observed count profile strongly deviates from the count profile observed in the cells with a library size below threshold T, i.e., a non-empty droplet.

library(DropletUtils)
bcrank <- barcodeRanks(counts(sce))

# Only showing unique points for plotting speed. Duplicated ranks are a 
# consequence of ties in the ranks, i.e., when cells have an equal library size.
sum(duplicated(bcrank$rank))
uniq <- !duplicated(bcrank$rank)

plot(bcrank$rank[uniq], bcrank$total[uniq], log="xy",
    xlab="Rank", ylab="Total UMI count", cex.lab=1.2)
abline(h=metadata(bcrank)$inflection, col="darkgreen", lty=2)
abline(h=metadata(bcrank)$knee, col="dodgerblue", lty=2)
abline(h=350, col="orange", lty=2) # picked visually myself
legend("topright", 
       legend=c("Inflection", "Knee", "Empirical knee point"), 
       col=c("darkgreen", "dodgerblue", "orange"), 
       lty=2, 
       cex=1.2)

set.seed(100)
limit <- 350   
all.out <- emptyDrops(counts(sce), lower=limit, test.ambient=TRUE)
# p-values for cells with total UMI count under the lower bound.
hist(all.out$PValue[all.out$Total <= limit & all.out$Total > 0],
    xlab="P-value", main="", col="grey80", breaks=20)

# but note that it would remove a very high number of cells
length(which(all.out$FDR <= 0.01)) # retained

# so we stick to the more lenient adaptive filtering strategy
# remove cells identified using adaptive thresholds
sce <- sce[, !colData(sce)$discardCells]

8.6 Identifying and removing doublets

We will use scDblFinder to detect doublet cells.

As discussed in the theory session of last week, the steps taken by scDblFinder can be summarized as follows:

  1. Perform principal components analysis (PCA) on log-normalized expression counts. This allows for projecting each cell in the dataset into a 2D space (for more details on PCA, see next weeks session).

  2. Randomly select two cells, sum their counts and normalize, and project into PCA space from step 1. In other words, artificially generate doublets and see where they are located in the 2D space.

  3. Repeat step 2 many times (generate many artificial doublets).

  4. Generate neighbor network in the 2D space. The network is then used to estimate a number of characteristics for each cell, in particular the proportion f artificial doublets among the nearest neighbors.

  5. Use this information, along with other predictors, to train a classifier (gradient boosted tree) that allows for distinguishing doublets from singlets.

Note: only classifies for identifying doublets for which the two cells are from different cell type clusters.

## perform doublet detection
library(scDblFinder)
set.seed(211103)
sampleID <- unlist(lapply(strsplit(colData(sce)$cell.id, split="_"), "[[", 1))
table(sampleID)
sce <- scDblFinder(sce, 
                   returnType="table",
                   samples = factor(sampleID))
table(sce$scDblFinder.class)


## visualize these scores
## explore doublet score wrt original cluster labels
boxplot(log1p(sce$scDblFinder.score) ~ factor(colData(sce)$cluster, exclude=NULL))

tab <- table(sce$scDblFinder.class, sce$cluster, 
      exclude=NULL)
tab
t(t(tab) / colSums(tab))

barplot(t(t(tab) / colSums(tab))[2,],
        xlab = "Cluster", ylab = "Fraction of doublets")

range(sce$scDblFinder.score[sce$scDblFinder.class  == "doublet" & sampleID == "r1"])
range(sce$scDblFinder.score[sce$scDblFinder.class  == "singlet" & sampleID == "r1"])
# remove doublets
sce <- sce[,!sce$scDblFinder.class == "doublet"]

9 Normalization

Normalization aims to remove technical effects such as sequencing depth so that comparisons between cells are not confounded by them. The most commonly used normalization methods methods use scaling, where a scaling factor (also called size factor, normalization factor) is estimated for each cell. These scaling factors (e.g., the effective library size) can be included as an offset to downstream modeling procedures. This effectively allows for performing inference on the relative abundance of a gene in a cell, after accounting for library size and RNA composition differences between cells, which is much more relevant than comparing raw counts

Note that we always prefer the use of offsets over transformation of the data. In brief, such transformation would distort the mean-variance relationship in the data. For more details, we refer to the document that was discussed in theory session of last week (link)

For normalization, the size factors \(s_i\) computed here are simply scaled library sizes: \[ N_i = \sum_g Y_{gi} \] \[ s_i = N_i / \bar{N}_i \]

sce <- logNormCounts(sce)

# note we also returned log counts: see the additional logcounts assay.
sce

# you can extract size factors using
sf <- librarySizeFactors(sce)
mean(sf) # equal to 1 due to scaling.
plot(x= log(colSums(assays(sce)$counts)), 
     y=sf)

From the OSCA book: Alternatively, we may use more sophisticated approaches for variance stabilizing transformations in genomics data, e.g., DESeq2 or sctransform. These aim to remove the mean-variance trend more effectively than the simpler transformations mentioned above, though it could be argued whether this is actually desirable. For low-coverage scRNA-seq data, there will always be a mean-variance trend under any transformation, for the simple reason that the variance must be zero when the mean count is zero. These methods also face the challenge of removing the mean-variance trend while preserving the interesting component of variation, i.e., the log-fold changes between subpopulations; this may or may not be done adequately, depending on the aggressiveness of the algorithm.

In practice, the log-transformation is a good default choice due to its simplicity and interpretability, and is what we will be using for all downstream analyses.


— end lab session 1 —


We have created a DropBox folder containing convenience files, e.g., a preprocessed SingleCellExperiment that is the end result of lab session 1. You may want to downlaod them and import them in the code chunk below.

10 Normalization (continued)

Normalization is necessary to correct for several sources of technical variation:

  • Differences in sequencing depth between samples. Some samples get sequenced deeper in the sense that they consist of more (mapped) reads and therefore can be considered to contain a higher amount of information, which we should be taking into account. In addition, if a sample is sequenced deeper, it is natural that the counts for each gene will be higher, jeopardizing a direct comparison of the expression counts.
  • Differences in RNA population composition between samples. As an extreme example, suppose that two samples have been sequenced to the exact same depth. One sample is contaminated and has a very high concentration of the contaminant cDNA being sequenced, but otherwise the two samples are identical. Since the contaminant will be taking up a significant proportion of the reads being sequenced, the counts will not be directly comparable between the samples. Hence, we may also want to correct for differences in the composition of the RNA population of the samples (see edgeR manual chapter 2.8).
  • Other technical variation such as sample-specific GC-content or transcript length effects may also be accounted for.
table(sce$cluster)

Below, we will visualize the normalization occurring between two cells of the same cell type (which could be considered technical repeats):

select <- sce$cluster == "1"
select[is.na(select)] <- FALSE
cs <- colSums(assays(sce)$counts[,select])
cs[order(cs, decreasing = TRUE)][c(1,10)]

Let’s take a look at how comparable two cells (replicates) of cluster 1 are. We will compare the cell with the highest library size with the cell that has the 10th highest library size using MD-plots (mean-difference plots, as introduced by Dudoit et al. (2002)), also sometimes referred to as MA-plots.

targetCells <- names(cs[order(cs, decreasing = TRUE)][c(1,10)])

M <- rowMeans(assays(sce)$counts[,targetCells])
D <- assays(sce)$counts[,targetCells[2]] / assays(sce)$counts[,targetCells[1]]
plot(x = log(M), y = log2(D),
     pch = 16, cex=1/3,
     main = paste0("Cell ", targetCells[2], " vs cell ", targetCells[1]),
     xlab = "Log mean", ylab = "Log2 fold-change",
     bty = 'l')
abline(h = 0, col="orange", lwd=2)

We see clear bias in the comparison of the 1st and 10th most deeply sequenced cell from cell cluster 1. We see that the log fold-changes are biased downwards. This means that, on average, a gene is higher expressed in cell 1 versus cell 10. Looking at the library sizes, we can indeed see that the library size for cell 1 is 20869 counts, while it is only 14719 counts for cell 10! This is a clear library size effect that we should take into account.

# normalize the count data using the previously computed "size factors"
assay(sce, "normed") <- normalizeCounts(sce, 
                                        log=FALSE,
                                        size.factors=sf, 
                                        pseudo.count=0)

M <- rowMeans(assays(sce)$normed[,targetCells])
D <- assays(sce)$normed[,targetCells[2]] / assays(sce)$normed[,targetCells[1]]
plot(x = log(M), y = log2(D),
     pch = 16, cex=1/3,
     main = paste0("Cell ", targetCells[2], " vs cell ", targetCells[1]),
     xlab = "Log mean", ylab = "Log2 fold-change",
     bty = 'l')
abline(h = 0, col="orange", lwd=2)

Upon normalizing the data using size factors, we have removed bias as a consequence of differences in sequencing depth.

Note that we computed the normalized count matrix only for demonstrating the effect of such normalization. In a typical workflow, the size factors are used as offsets in the downstream models rather than to perform data transformation. In brief, such transformation would distort the mean-variance relationship in the data. For more details, we refer to the document that was touched upon in the first theory session (link)

11 Feature selection

As dimensions increase, shortest and farthest distances between points become nearly inseparable. In high-dimensional space, it is therefore extremely difficult to separate signal from noise.

In order to recover structure (e.g. setting up a dimension-reduced space to help us find cell-type clusters in the data), we want to move to an informative, lower-dimensional space. We will select genes which we hope are informative for recovering the biological structure. But what defines an informative gene?

11.1 Selecting genes with high variance

The simplest approach to quantifying per-gene variation is to compute the variance of the log-normalized expression values (i.e., “log-counts”) for each gene across all cells.

# calculate variance of log-normalized counts for each gene
geneVars <- genefilter::rowVars(...)
# select top 1000 highly variable genes
highVarGenes <- names(geneVars)[order(geneVars, decreasing=TRUE)[1:1e3]]
head(highVarGenes)

11.2 Selecting genes with high variation with respect to mean

While calculation of the per-gene variance is simple, feature selection requires modelling of the mean-variance relationship. Indeed, not accounting for the mean-variance structure while selecting highly variable genes will oftentimes boil down to selecting the most highly expressed genes. The log-transformation is a helpful variance stabilizing transformation, however, it is not perfect, meaning that the variance of a gene is not completely independent of its mean. Therefore, feature selection may still be driven by average expression rather than underlying biological heterogeneity.

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.

To account for the mean-variance effect, we use the modelGeneVar function of the scran package to fit a trend to the variance with respect to abundance across all genes (on log-normalized expression values of the sce object).

library(scran)
# `modelGeneVar` function description: Model the variance of the log-expression 
# profiles for each gene, decomposing it into technical and biological 
# components based on a fitted mean-variance trend.
dec <- modelGeneVar(...) # input a sce object with precomputed logcounts assay
head(dec)

The fitted value for each gene is used as a proxy for the technical component of variation for each gene, under the assumption that most genes exhibit a low baseline level of variation that is not biologically interesting. The biological component of variation for each gene is defined as the the residual from the trend. Ranking genes by the biological component enables identification of interesting genes for downstream analyses in a manner that accounts for the mean-variance relationship.

fitRetina <- metadata(...)
plot(x = ..., # the fitted means on the x-axis 
     y = ..., # the fitted variances on the y-axis
     xlab="Mean of log-expression",
    ylab="Variance of log-expression")
curve(fitRetina$trend(x), col="dodgerblue", add=TRUE, lwd=2) # adds a trend line

We are interested in those genes for which the variance in expression is higher than what we would expect for that gene based on its mean expression.

# get 10% most variable genes
# Based on the output statistics of modelGeneVar, select the 10% most variable genes
hvg <- getTopHVGs(stats = ..., 
                  prop = ...) # percentage of top variable genes. Alternatively,
# n = ... could be used to select the top number of variable genes
head(hvg)

# plot these 
plot(x = ..., # the fitted means on the x-axis 
     y = ..., # the fitted variances on the y-axis
     col = c("orange", "darkseagreen3")[(names(fitRetina$mean) %in% hvg)+1],
     xlab="Mean of log-expression",
    ylab="Variance of log-expression")
curve(fitRetina$trend(x), col="dodgerblue", add=TRUE, lwd=2)
legend("topleft", 
       legend = c("Selected", "Not selected"), 
       col = c("darkseagreen3", "orange"),
       pch = 16,
       bty='n')

As a comparison, we could color the genes on this figure according to the selection that was made purely by looking at the raw variance of each gene.

...

As expected, we here simply select genes with a high variance, without recognizing that a high variance is typically driven by a high mean.

11.3 High deviance genes

Here, we will select genes with a high residual deviance. The idea is that we assume a null model of constant expression fraction (i.e., RNA concentration) across all cells. We subsequently calculate a goodness-of-fit statistic for each gene, assessing whether the model is a good approximation to the gene expression of the corresponding gene.

If the model fits poorly, i.e., the gene has a high deviance, the expression fraction varies significantly across the cells in our datasets.Genes with a high deviance will thus most poorly fit a null model where the relative abundance is equal for all cells, which therefore are informative.

#BiocManager::install("scry")
library(scry)
# `devianceFeatureSelection` function description: Computes a deviance statistic 
# for each row feature (such as a gene) for count data based on a multinomial 
# null model [...]. 
sce <- devianceFeatureSelection(object = ..., 
                                assay = ...) # computed on raw counts

plot(sort(rowData(sce)$binomial_deviance, decreasing = TRUE), 
     type="l", 
     xlab="ranked genes", 
     ylab="binomial deviance", 
     main="Feature Selection with Deviance")
abline(v=2000, lty=2, col="red")

Our plot looks similar to one displayed in the vignette of the scry package. Based on that plot, the authors suggest retaining 2.000 genes (the top 2000 based on the deviance residuals) for downstream dimensionality reduction and clustering.

11.4 Seurat VST

Another very common feature selection strategy is the variance-stabilizing transformation from the Seurat R package, which amounts to calculating Pearson residuals from a regularized negative binomial regression model, with sequencing depth as a covariate.

Intermezzo: interoperability between SingleCellExperiment and Seurat objects

In this lecture series, we always make use of the SingleCellExperiment object and the packages available from Bioconductor. Another very popular toolbox for performing scRNA-seq data analysis is Seurat. However, functions from Seurat cannot be used directly to manipulate SingleCellExperiment objects, and vice versa. Fortunately, efforts have been made to increase the interoperability between the two toolboxes.

library(Seurat)
seurat_obj <- as.Seurat(...) # sce object
seurat_obj # notice the "0 variable features"

On this object, we may use functions from the Seurat toolbox. For instance, we may search for highly variable features using Seurat’s VST implementation:

# Prior to detecting variable features using VST, Seurat normalizes the data.
# The `NormalizeData` function takes as input the Seurat object, and uses 
# log normalization and a scale factor of 10000 as default (see help file).
seurat_obj <- Seurat::NormalizeData(object = ..., 
                                    normalization.method = ..., 
                                    scale.factor = ...)

# Next, perform feature selection based on VST
seurat_obj <-  FindVariableFeatures(object = ...,
                                    selection.method = ...)
seurat_obj  # notice the "2000 variable features" (default)
head(VariableFeatures(seurat_obj)) # here they are

12 Dimensionality reduction

Note that, below, we color the cells using the known, true cell type label as defined in the metadata, to empirically evaluate the dimensionality reduction. In reality, we don’t know this yet at this stage.

12.1 The most basic DR

Just by looking at the top two genes based on our feature selection criterion, we can already see some separation according to the cell type!

colData(sce)$cluster <- as.factor(colData(sce)$cluster)
cl <- colData(sce)$cluster
par(bty='l')
plot(x = ..., # extract the counts for the most variable gene
     y = ..., # extract the counts for the second most variable gene
     col = as.numeric(cl),
     pch = 16, cex = 1/3,
     xlab = "Most informative gene",
     ylab = "Second most informative gene",
     main = "Cells colored acc to cell type")

We are able to recover quite some structure. However, many cell populations remain obscure, and the plot is overcrowded.

12.2 Linear dimensionality reduction: PCA

A DR method is linear when the reduced dimensions are a linear function of the original variables. For example, in PCA, each principal component is a linear combination of genes, therefore the DR is a linear function of the original variables.

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.

There are several R functions that allow you to perform PCA. Here, we make use of the runPCA function of the scater package, which has been specifically developed for performing PCA on SingleCellExperiment objects. We calculate the top 30 principal components.

12.2.1 PCA with feature selection

# Perform a PCA for our data. Compute the first 30 principal components, only
# using the selected features with high variance with respect to the mean.
set.seed(1234)
sce <- runPCA(...) # you need three arguments

PCA has been performed. The PCA information has been automatically stored in the reducedDim slot of the SingleCellExperiment object.

reducedDimNames(sce)
head(reducedDim(sce,
                type="PCA"))

The plotPCA function of the scater package now allows us to visualize the cells in PCA space, based on the PCA information stored in our object:

plotPCA(sce, 
        colour_by = "cluster")

While the large number of clusters in this dataset makes it hard to distinguish between all the different colors, we can already see that PCA retrieves some, but not all, of the structure in the data that was discovered by the original authors.

How many of the top PCs should we retain for downstream analyses? The choice of the number of PCs is a decision that is analogous to the choice of the number of HVGs to use. Using more PCs will retain more biological signal at the cost of including more noise that might mask said signal. On the other hand, using fewer PCs will introduce competition between different factors of variation, where weaker (but still interesting) factors may be pushed down into lower PCs and inadvertently discarded from downstream analyses.

Most analysts will simply aim to use a “reasonable” but arbitrary value, typically ranging from 10 to 50. This is often satisfactory as the later PCs explain so little variance that their inclusion or omission has no major effect.

percent.var <- attr(reducedDim(sce), "percentVar")
plot(percent.var, log="y", xlab="PC", ylab="Variance explained (%)")
plot(cumsum(percent.var), xlab="PC", ylab="Cumulative variance explained (%)")

Here, retaining ±15PCs seems reasonable. If you really prefer a more data-driven way for determining this, there are procedures available that aim to computationally identify the elbow/knee point in the variance explained per PC plot.

library(PCAtools)
chosen.elbow <- findElbowPoint(percent.var)
chosen.elbow

12.2.2 PCA without feature selection

Note: more features -> computationally more intensive!

set.seed(1234)
sceNoFS <- runPCA(x = ..., 
                  ncomponents = ..., 
                  subset_row = 1:nrow(sce))
plotPCA(...)
rm(sceNoFS) # remove the object, we don't need it anymore

While we use more information to make this PCA plot (17.887 genes) as compared to the feature selected PCA plot (642 genes), we seem to retrieve less structure in the data. This is the power of feature selection, an increase in the signal-to-noise ratio!

12.2.3 Effect of feature selection on PCA

First, we compare the different feature selection criteria, using the top 1000 highly variable genes for each method.

# Get top 1000 highly variable features using the highly variable genes
# (w.r.t. mean), high deviance genes and the VST strategy of Seurat

hvg1000 <- ... # extract top 1000 highly variable genes
hdg1000 <- ... # extract top 1000 genes with high deviance
vst1000 <- ... # extract top 1000 genes based on VST analysis

# HVG strategy
plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg1000), 
    colour_by = "cluster") +
  ggtitle("Highly variable genes")

# HDG strategy
plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hdg1000), 
    colour_by = "cluster") +
  ggtitle("High-deviance genes")

# VST strategy
plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = vst1000), 
    colour_by = "cluster",
    ) +
  ggtitle("Seurat VST")

Next, we assess the sensitivity on the number of top features for the highly variable genes method;

hvg_all <- getTopHVGs(dec)

hvg2000 <- ... # extract top 2000 highly variable genes
hvg1000 <- ... # extract top 1000 highly variable genes
hvg100 <- ... # extract top 100 highly variable genes
hvg10 <- ... # extract top 10 highly variable genes
hvg5 <- ... # extract top 5 highly variable genes

plotPCA(
    runPCA(sce,
           ncomponents = 2), 
    colour_by = "cluster") +
  ggtitle("All genes")

plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg2000), 
    colour_by = "cluster") +
  ggtitle("Top 2000 genes")

plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg1000), 
    colour_by = "cluster") +
  ggtitle("Top 1000 genes")

plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg100), 
    colour_by = "cluster") +
  ggtitle("Top 100 genes")

plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg10), 
    colour_by = "cluster") +
  ggtitle("Top 10 genes")

plotPCA(
    runPCA(sce,
           ncomponents = 2, 
           subset_row = hvg5), 
    colour_by = "cluster") +
  ggtitle("Top 5 genes")

12.2.4 Remarks on PCA

Visualizations of reduced dimensions from linear dimensionality reduction methods are often “overcrowded”, and it is hard to see structure (e.g., the PCA plot we just saw). Non-linear dimensionality 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.

12.3 A generalization of PCA for exponential family distributions.

PCA is implicitly based on Euclidean distances, corresponding to maximizing a Gaussian likelihood, which is inappropriate for count data such as scRNA-seq. Townes et al. (2019) develop GLM-PCA, a generalization of PCA for exponential family likelihoods. They posit, using negative control data, that the data generative mechanism of UMI count data can be considered to be multinomial.

The GLM-PCA strategy is implemented in the glmpca function of the glmpca package.

Note that this function is quite computationally intensive. For regular PCA on log-transformed normalized counts, the underlying computations can be strongly simplified. Here, we work with the raw counts, which we assume to be Poisson distributed, requiring an iterative optimization scheme.

# runs 2min on my laptop
library(glmpca)
set.seed(211103)
poipca <- glmpca(Y = ..., # counts for the higly variable genes
                 L = ..., # two latent dimensions (components)
                 fam = ..., # assume Poisson for droplet data
                 minibatch = "stochastic") # used in the package vignette by 
# the authors, but beyond the scope of this session.
reducedDim(sce, "PoiPCA") <- poipca$factors
plotReducedDim(...) # make sure to specify the right value for the `dimred` argument

Alternatively, we could adopt the GLM-PCA strategy using only the genes with high deviance.

# Based on the diagnostic plot from the feature selection, we would select the 
# top 2000 genes with highest deviance. However, to reduce the running time of
# the function, I here select the top 500 (which still is quite slow - 5min).
hdg500 <- names(sort(rowData(sce)$binomial_deviance, decreasing = TRUE))[1:500]

...

The authors of the glmpca package note that GLM-PCA can be slow for large datasets. Therefore, they have implemented a fast approximation of the algorithm, which first fits a null model of constant expression for each gene across all cells, and subsequently fits standard PCA to either the Pearson or deviance residuals from the null model.

However, at least for me the nullResiduals function was extremely slow even slower than the glmpca code above. Therefore, I suggest not running this code, but I leave it in for reference.

sce <- nullResiduals(sce, assay="counts", type="deviance")
sce <- nullResiduals(sce, assay="counts", type="pearson")

pca<-function(Y, L=2, center=TRUE, scale=TRUE){
    #assumes features=rows, observations=cols
    res<-prcomp(as.matrix(t(Y)), center=center, scale.=scale, rank.=L)
    factors<-as.data.frame(res$x)
    colnames(factors)<-paste0("dim", 1:L)
    factors
}
pca_d <- pca(assay(sce[hdg,], "binomial_deviance_residuals"))
pca_d$resid_type <- "deviance_residuals"
pca_p <- pca(assay(sce[hdg,], "binomial_pearson_residuals"))
pca_p$resid_type <- "pearson_residuals"
cm <- as.data.frame(colData(sce[hdg,]))
pd <- rbind(cbind(cm, pca_d), cbind(cm, pca_p))
ggplot(pd, aes(x=dim1, y=dim2, colour=phenoid)) + geom_point() +
  facet_wrap(~resid_type, scales="free", nrow=2) +
  ggtitle("PCA applied to null residuals of high deviance genes")

12.4 Non-linear dimensionality reduction: t-SNE

t-SNE focuses on 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.

Running t-SNE on a SingleCellExperiment object can be achieved with the runTSNE function of the scater package. By default, this function will first perform PCA, and use the top 50 PCs as an input to the actual t-SNE algorithm. Since we already performed PCA, we may set dimred = "PCA" as argument to the function. As such we will be performing a T-SNE on the 30 PCs we computed before. If we would like to run t-SNE only using the top 10 PCs, we could set n_dimred = 10.

In addition, we may wish to set external_neighbors=TRUE, which increases the speed of the algorithm for large datasets by applying a heuristic.

Note: for me this was the slowest function of the analysis (so far). If you feel like your PC/laptop had a lot of trouble with the previous step(s), you may consider not running this code, or running it on a subset of the data (e.g., randomly subsampling cells) for demonstrational purposes. Alternatively, you may consider reducing the input space for the t-SNE algorithm, e.g. by setting n_dimred = 5.

# (For me it takes 3min30 with n_dimred = 5)
sce <- runTSNE(x = ..., 
               dimred = ...,
               n_dimred = ...,
               external_neighbors = ...)
plotTSNE(sce,
         colour_by = "cluster")

12.5 Non-linear dimensionality reduction: UMAP

It is often suggested that UMAP is better than t-SNE in preserving global differences. Therefore, UMAP is also often used in analyses such as trajectory inference, where this is important.

Running UMAP on a SingleCellExperiment object can be achieved with the runUMAP function of the scater package.

# Using top 10% highly variable genes and top 30 PCs
sce <- runUMAP(...)
plotUMAP(...)

# Using top 10% highly variable genes and top 10 PCs
plotUMAP(runUMAP(...),
  colour_by = "cluster")
LS0tCnRpdGxlOiAnTm9ybWFsaXphdGlvbiwgZmVhdHVyZSBzZWxlY3Rpb24gYW5kIGRpbWVuc2lvbiByZWR1Y3Rpb24gZm9yIHRoZSBNYWNvc2tvIGRhdGFzZXQnCmF1dGhvcjogIktvZW4gVmFuIGRlbiBCZXJnZSBhbmQgSmVyb2VuIEdpbGlzIgpkYXRlOiAiMzAvMTEvMjAyMSIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgojIFByZWFtYmxlOiBpbnN0YWxsYXRpb24gb2YgQmlvY29uZHVjdG9yIGxpYnJhcmllcwoKYGBge3J9CiMgaW5zdGFsbCBCaW9jTWFuYWdlciBwYWNrYWdlIGlmIG5vdCBpbnN0YWxsZWQgeWV0LgojIEJpb2NNYW5hZ2VyIGlzIHRoZSBwYWNrYWdlIGluc3RhbGxlciBmb3IgQmlvY29uZHVjdG9yIHNvZnR3YXJlLgppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQoKIyBpbnN0YWxsIHBhY2thZ2VzIGlmIG5vdCB5ZXQgaW5zdGFsbGVkLgpwa2dzIDwtIGMoIlNpbmdsZUNlbGxFeHBlcmltZW50IiwKICAgICAgICAgICJFeHBlcmltZW50SHViIiwKICAgICAgICAgICJlZGdlUiIsCiAgICAgICAgICAiYmlvbWFSdCIsCiAgICAgICAgICAiRHJvcGxldFV0aWxzIiwgCiAgICAgICAgICAic2NSTkFzZXEiLCAKICAgICAgICAgICJzY2F0ZXIiLCAKICAgICAgICAgICJzY3V0dGxlIiwgCiAgICAgICAgICAic2NyYW4iLAogICAgICAgICAgInNjcnkiLAogICAgICAgICAgIkJpb2NTaW5ndWxhciIsIAogICAgICAgICAgInNjRGJsRmluZGVyIiwKICAgICAgICAgICJnbG1wY2EiLAogICAgICAgICAgIlBDQXRvb2xzIiwKICAgICAgICAgICJTZXVyYXQiKQpub3RJbnN0YWxsZWQgPC0gcGtnc1shcGtncyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywxXV0KaWYobGVuZ3RoKG5vdEluc3RhbGxlZCkgPiAwKXsKICBCaW9jTWFuYWdlcjo6aW5zdGFsbChub3RJbnN0YWxsZWQpCn0KCmBgYAoKIyBUaGUgTWFjb3NrbyBkYXRhc2V0CgpJbiB0aGlzIHdvcmtzaG9wIHNlc3Npb24sIHdlIHdpbGwgYW5hbHl6ZSB0aGUgc2luZ2xlLWNlbGwgUk5BLXNlcSBkYXRhc2V0CmZyb20gdGhlIHB1YmxpY2F0aW9uIGJ5IE1hY29za28gKmV0IGFsLiosIENlbGwgMTYxLCAxMjAy4oCTMTIxNCBmcm9tIDIwMTUKWyhsaW5rKV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5jZWxsLjIwMTUuMDUuMDAyKS4gVGhpcyBpcyB0aGUgbWFudXNjcmlwdCBpbgp3aGljaCB0aGUgZHJvcGxldCBzY1JOQS1zZXEgdGVjaG5vbG9neSAqKkRyb3Atc2VxKiogd2FzIGludHJvZHVjZWQuClNpeCB5ZWFycyBhZnRlciB0aGUgb3JpZ2luYWwgcHVibGljYXRpb24sIGRyb3Atc2VxIGlzIHN0aWxsIG9uZSBvZiB0aGUgbW9zdCAKY29tbW9ubHkgYWRvcHRlZCBzY1JOQS1zZXEgcHJvdG9jb2xzLCBhcyBldmlkZW5jZWQgYnkgdGhlCmxhcmdlIG51bWJlciBvZiBjaXRhdGlvbnMgZm9yIE1hY29za28gKmV0IGFsLiogCig0LjMwMyBjaXRhdGlvbnMgYXQgTm92ZW1iZXIgMywgMjAyMSkuCgpUaGUgYmFzaWMgaWRlYSBiZWhpbmQgdGhlIERyb3Atc2VxIHByb3RvY29sIGNhbiBiZSB0YWtlbiBmcm9tIHRoZSBncmFwaGljYWwKYWJzdHJhY3Qgb2YgdGhlIHB1YmxpY2F0aW9uLgoKYGBgCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJtYWNvc2tvX2dyYXBoaWNhbEFic3RyYWN0LmpwZWciKQpgYGAKClRoZSBzdWNjZXNzIG9mIERyb3Atc2VxIGNhbiBiZSBleHBsYWluZWQgYnkgdGhlIGZvbGxvd2luZyBhZHZhbnRhZ2VvdXMgCmZlYXR1cmVzOgoKLSBUaGUgdXNlIG9mIHVuaXF1ZSBtb2xlY3VsYXIgaWRlbnRpZmllcnMgKFVNSXMpLiBCeSB3b3JraW5nIHdpdGggVU1Jcywgb25lIGNvdW50CmNvcnJlc3BvbmRzIHRvIG9uZSBvYnNlcnZlZCBtUk5BIG1vbGVjdWxlIHByZXNlbnQgaW4gdGhlIGNlbGwuIFRoYW5rcyB0byB0aGUgdXNlCm9mIFVNSSBiYXJjb2RlcywgUENSIGFydGlmYWN0cyBhcmUgcmVkdWNlZC4KCi0gU2NhbGFiaWxpdHk6IG1pY3JvZmx1aWRpY3MgdGVjaG5vbG9neSBhbGxvd3MgZm9yIHBlcmZvcm1pbmcgdGhlIGxpYnJhcnkgcHJlcApyZWFjdGlvbnMgaW5zaWRlIG5hbm9kcm9wbGV0cywgaW4gd2hpY2ggc2luZ2xlIGNlbGxzIG1heSBiZSBjb250YWluZWQuIExpYnJhcnkgcHJlcCBvY2N1cnMgYWNyb3NzIGFsbCBkcm9wbGV0cyBzaW11bHRhbmVvdXNseS4KCi0gQ29zdDogdGhlIGV4cGVyaW1lbnQgY29zdHMgYXJvdW5kIDYuNSBjZW50cyAoVVNEKSBwZXIgY2VsbC4KCi0gU3BlZWQ6IFRoZSB2ZXJ5IGxhcmdlIGRhdGFzZXQgdGhhdCB3ZSB3aWxsIGJlIHdvcmtpbmcgd2l0aCB0b2RheSB3YXMgCmdlbmVyYXRlZCBpbiBhbiBleHBlcmltZW50IHRoYXQgdG9vayBvbmx5IDQgZGF5cy4KCkluIHRoaXMgcGFydGljdWxhciBleHBlcmltZW50LCBNYWNvc2tvICpldCBhbC4qIHNlcXVlbmNlZCA0OSwzMDAgY2VsbHMgZnJvbSB0aGUKbW91c2UgcmV0aW5hLCBpZGVudGlmeWluZyAzOSB0cmFuc2NyaXB0aW9uYWxseSBkaXN0aW5jdCBjZWxsIHBvcHVsYXRpb25zLiBUaGUKZXhwZXJpbWVudCB3YXMgcGVyZm9ybWVkIGluIDcgYmF0Y2hlcy4KCiMgRGF0YSBhdmFpbGFiaWxpdHkKCiMjIFNSQQoKVGhlIFtTZXF1ZW5jZSBSZWFkIEFyY2hpdmUgKFNSQSldKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3Yvc3JhKSBpcyB0aGUgCmxhcmdlc3QgcHVibGljbHkgYXZhaWxhYmxlIHJlcG9zaXRvcnkgb2YgaGlnaC10aHJvdWdocHV0IHNlcXVlbmNpbmcgZGF0YS4KVGhlIGRhdGEgYXJlIHN0b3JlZCBieSB0aGUgTmF0aW9uYWwgQ2VudGVyIGZvciBCaW90ZWNobm9sb2d5IEluZm9ybWF0aW9uIChOQ0JJKSAKc2VydmljZXMgYW5kIG11bHRpcGxlIGNsb3VkIHN0b3JhZ2UgcHJvdmlkZXJzLiBGcm9tIHRoaXMgd2Vic2l0ZSAicmF3IiAKc2VxdWVuY2luZyBkYXRhIGNhbiBiZSByZXRyaWV2ZWQuIEluIHByYWN0aWNlLCB0aGVzZSBhcmUgdXN1YWxseSBgLnNyYWAgZmlsZXMsCndoaWNoIGNhbiBiZSBkb3dubG9hZGVkIGFuZCBjb252ZXJ0ZWQgaW50byBGQVNUUSBmaWxlcyB1c2luZyBmdW5jdGlvbnMKZnJvbSB0aGUgYHNyYXRvb2xraXRgIHNvZnR3YXJlLiAKCkZvciBvdXIgZGF0YXNldCwgdGhlIEZBU1RRIGRhdGEgY2FuIGJlIHJldHJpZXZlZCBmcm9tIHRoaXMgCltsaW5rXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L1RyYWNlcy9zdHVkeS8/YWNjPVBSSk5BMjY3ODU3Jm89YWNjX3MlM0FhKS4KVGhlIGRhdGEgYXJlIHN0b3JlZCBhcyBvbmUgZmlsZSBwZXIgc2VxdWVuY2luZyBiYXRjaCwgd2l0aCBlYWNoIGZpbGUgCmFwcHJveGltYXRlbHkgMjBHYi4gQXMgc3VjaCwgaXQgd2lsbCBiZSB1bmZlYXNpYmxlIHRvIGRvd25sb2FkIGFuZCBwcm9jZXNzIHRoZXNlCkZBU1RRIGZpbGVzIGluIHRoaXMgcHJhY3RpY2FsIHNlc3Npb24uCgpJbnN0ZWFkLCBmb3IgZGVtb25zdHJhdGl2ZSBwdXJwb3Nlcywgd2UgaGF2ZSB0YWtlbiBhIHN1YnNhbXBsZSBvZiB0aGUgRkFTVFEKZmlsZSBmb3IgdGhlIGZpcnN0IHNlcXVlbmNpbmcgYmF0Y2ggZm9yIHlvdSB0byB3b3JrIHdpdGguIE9uIHRoaXMgc3Vic2FtcGxlLCAKd2UgbWF5IHBlcmZvcm0gYWxsIHRoZSB0YXNrcyB0aGF0IHdlIHdvdWxkIGhhdmUgcGVyZm9ybWVkIG9uIHRoZSBmdWxsIGRhdGFzZXQuClRoZSBzdGVwcyB0aGF0IGFyZSByZXF1aXJlZCBmb3IgZG93bmxvYWRpbmcgYW5kIHF1YW50aWZ5aW5nIGRyb3Atc2VxIGRhdGEKY2FuIGJlIGZvdW5kIGluIFthIFNoZWxsIHNjcmlwdCBvbiBvdXIgY29tcGFuaW9uIEdpdEh1YiByZXBvc2l0b3J5XShodHRwczovL2dpdGh1Yi5jb20vc3RhdE9taWNzL3NpbmdsZUNlbGxDb3Vyc2UvYmxvYi9tYXN0ZXIvbGFiMV9wcmVwcm9jZXNzaW5nL3ByZXByb2Nlc3NEcm9wc2VxLnNoKS4KCiMjIEdFTwoKVGhlIGRhdGFzZXQgZnJvbSBNYWNvc2tvICpldCBhbC4qIHdhcyBhbHNvIHVwbG9hZGVkIGJ5IHRoZSBhdXRob3JzIG9uIHRoZSAKR2VuZSBFeHByZXNzaW9uIE9tbmlidXMgKEdFTykgcGxhdGZvcm0gdW5kZXIgYWNjZXNzaW9uIG51bWJlciAKW0dTRTYzNDcyXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0U2MzQ3MiksIApmcm9tIHdoaWNoIHJhdyBhbmQgcmVhZGlseS1wcm9jZXNzZWQgZGF0YSBmaWxlcyBtYXkgYmUgcmV0cmlldmVkLCBpbmNsdWRpbmc6CgoxLiAqR1NFNjM0NzJfUkFXLnRhciosIGEgOTAuNkdiIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZSAicmF3IGRhdGEiIGZvciB0aGUKZXhwZXJpbWVudC4gSW4gdGhlIHNjUk5BLXNlcSBjb250ZXh0LCBGQVNUUSBmaWxlcyBhcmUgb2Z0ZW4gY29uc2lkZXJlZCB0aGUgcmF3CmRhdGEgZm9ybWF0LgoKMi4gKkdTRTYzNDcyX1AxNFJldGluYV9tZXJnZWRfZGlnaXRhbF9leHByZXNzaW9uLnR4dC5neiosIGEgNTAuN01iIG1hdHJpeCB0aGF0CnN0b3JlcyB0aGUgZ2VuZSBleHByZXNzaW9uIHZhbHVlcyBmb3IgZWFjaCBjZWxsLiBUaGVzZSB2YWx1ZXMgYXJlIGludGVnZXIgCmNvdW50cywgdGhhdCBkaWQgbm90IHVuZGVyZ28gYW55IHR5cGUgb2YgcHJlcHJvY2Vzc2luZyBvciBub3JtYWxpemF0aW9uLgoKMy4gKkdTRTYzNDcyX21tMTBfcmVmZXJlbmNlX21ldGFkYXRhLnRhci5neiosIGEgODYyLjlNYiBjb21wcmVzc2VkIGZvbGRlcgpjb250YWluaW5nIGluZm9ybWF0aW9uIG9uIHRoZSByZWZlcmVuY2UgZ2Vub21lIHRvIHdoaWNoIHRoZSBzY1JOQS1zZXEgcmVhZHMKd2VyZSBhbGlnbmVkIChzZWUgdGhlb3J5IHNsaWRlcykuCgo0LiAqR1NFNjM0NzJfUDE0UmV0aW5hX2xvZ0RHRS50eHQuZ3oqLCBhIDMxNi44TWIgY29tcHJlc3NlZCB0ZXh0IGZpbGUsIG5vdCBjbGVhcgp3aGF0IGl0IGNvbnRhaW5zIChyZXN1bHRzIGZyb20gYSBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGFuYWx5c2lzLCBidXQKd2l0aCBsb2ctdHJhbnNmb3JtYXRpb24sIHNvIGxvZy1mb2xkIGNoYW5nZXMgbWF5YmU/KS4KCkFzIHN1Y2gsIGJ5IGRvd25sb2FkaW5nICpHU0U2MzQ3Ml9QMTRSZXRpbmFfbWVyZ2VkX2RpZ2l0YWxfZXhwcmVzc2lvbi50eHQuZ3oqLAp3ZSBhdm9pZCByZS1xdWFudGlmeWluZyB0aGUgZGF0YSwgaS5lLiwgdGhlIHRyYW5zbGF0aW9uIGZyb20gcmVhZHMgZnJvbSB0aGUKRkFTVFEgZmlsZXMgaW50byBhIGdlbmUtbGV2ZWwgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGVhY2ggY2VsbC4gCgpPbmUgaXNzdWUgdGhhdCBvZnRlbiBhcmlzZXMgZnJvbSBkYXRhIGRvd25sb2FkZWQgZnJvbSBHRU8sIGlzIHRoYXQgdGhlcmUgaXMgbm8Kc3RyaWN0IHJlcXVpcmVtZW50cyBmb3Igd2hpY2ggZGF0YSBzaG91bGQgYmUgaW5jbHVkZWQgaW4gdGhlIHVwbG9hZCBieSB0aGUgCmF1dGhvcnMuIEFzIHN1Y2gsIGZyb20gbXkgcGVyc29uYWwgZXhwZXJpZW5jZSwgaXQgY2FuIG9mdGVuIGJlIHRoZSBjYXNlIHRoYXQKaW1wb3J0YW50IGluZm9ybWF0aW9uIGxpa2UgbWV0YWRhdGEgYXJlIG1pc3NpbmcsIG9yIHRoZSBjb250ZW50IG9mIHRoZSBzdWJtaXR0ZWQKZmlsZXMgaXMgdW5jbGVhci4gRXZlbiBpZiBhbGwgdGhlIHJlcXVpcmVkIGRhdGEgYXJlIGF2YWlsYWJsZSwgYXMgaXMgdGhlIGNhc2UgCmhlcmUsIHdlIHdvdWxkIHN0aWxsIG5lZWQgdG8gcGllY2UgYWxsIHRoZSBpbmZvcm1hdGlvbiB0b2dldGhlciBmcm9tIGRpZmZlcmVudCAKZmlsZXMgYW5kIGZpbGUgZm9ybWF0cyBiZWZvcmUgd2UgY2FuIHVzZSB0aGVtLgoKIyMgRXhwZXJpbWVudEh1YgoKVGhlIEJpb2NvbmR1Y3RvciAqRXhwZXJpbWVudEh1Yiogd2ViIHJlc291cmNlLCB3aGljaCBjYW4gYmUgYWNjZXNzZWQgdXNpbmcgdGhlIApbRXhwZXJpbWVudEh1Yl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0V4cGVyaW1lbnRIdWIuaHRtbCkgClIgcGFja2FnZSwgcHJvdmlkZXMgYSBjZW50cmFsIGxvY2F0aW9uIHdoZXJlIGN1cmF0ZWQgZGF0YSBmcm9tIGV4cGVyaW1lbnRzLCAKcHVibGljYXRpb25zIG9yIHRyYWluaW5nIGNvdXJzZXMgY2FuIGJlIGFjY2Vzc2VkLiBXaGlsZSBpdCBjb250YWlucyBmYXIgbGVzcwpkYXRhc2V0cyB0aGFuIHRoZSBTUkEgb3IgR0VPICg0OTY1IHJlY29yZHMgdG8gZGF0ZSksIHRoZXNlIGRhdGFzZXRzIGFsbCBmb2xsb3cgCnRoZSB0aWR5IGRhdGEgZm9ybWF0IG9mIEJpb2NvbmR1Y3Rvci4gTm90ZSB0aGF0IHRoZSBFeHBlcmltZW50SHViIGNvbnRhaW5zIApzZXZlcmFsIHR5cGVzIG9mIGRhdGEsIGxpa2UgYnVsayBhbmQgc2luZ2xlLWNlbGwgdHJhbnNjcmlwdG9taWNzIGRhdGEsIAptaWNyb2FycmF5cyBhbmQgbW9yZS4KClRoZSBNYWNvc2tvIGRhdGFzZXQgaXMgYXZhaWxhYmxlIGZyb20gRXhwZXJpbWVudEh1YiBhbmQgY2FuIGJlIGFjY2Vzc2VkIGFzCmZvbGxvd3M6CgpgYGB7ciwgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShFeHBlcmltZW50SHViKQplZGIgPC0gRXhwZXJpbWVudEh1YigpCgplZGJbZ3JlcCgiTWFjb3NrbyIsIGVkYiR0aXRsZSldICMgZmluZCBhY2Nlc3Npb24gbnVtYmVyIChjYW4gYmUgaW5lZmZpY2llbnQpCgplZGJfY291bnRzIDwtIGVkYltbIkVIMjY5MCJdXQplZGJfY291bnRzWzE6NSwxOjVdCgplZGJfY29sZGF0YSA8LSBlZGJbWyJFSDI2OTEiXV0KZWRiX2NvbGRhdGFbMTo1LF0KCnJtKGVkYiwgZWRiX2NvdW50cywgZWRiX2NvbGRhdGEpCmBgYAoKIyMgc2NSTkFTZXEKCkluIGFkZGl0aW9uIHRvIEV4cGVyaW1lbnRIdWIsIEJpb2NvbmR1Y3RvciBwcm92aWRlcyB0aGUgcGFja2FnZQpbc2NSTkFzZXFdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2RhdGEvZXhwZXJpbWVudC9odG1sL3NjUk5Bc2VxLmh0bWwpLgpUaGlzIHBhY2thZ2UgcHJvdmlkZXMgYW4gZXZlbiBtb3JlIHVzZXItZnJpZW5kbHkgY2xpZW50IHRvIGFjY2VzcyAob25seSkgCnNjUk5BLXNlcSBkYXRhc2V0cyBmcm9tIHRoZSBFeHBlcmltZW50SHViIHdlYiByZXNvdXJjZS4gRGF0YSByZXRyaWV2ZWQgdXNpbmcgdGhlCnNjUk5Bc2VxIHBhY2thZ2UgYXJlIHN0b3JlZCBhcyB1c2VyLWZyaWVuZGx5IGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0cywgCndpdGggdGhlIGV4cHJlc3Npb24gZGF0YSwgZ2VuZS1sZXZlbCBpbmZvcm1hdGlvbiwgY2VsbC1sZXZlbCBpbmZvcm1hdGlvbiBhbmQgCmV4cGVyaW1lbnQgbWV0YWRhdGEgYWxsIGluIHBsYWNlIGluIG9uZSBkYXRhIG9iamVjdC4gVGhlIHNjUk5BLXNlcSBwYWNrYWdlCmN1cnJlbnRseSBob2xkcyA2MSBkYXRhc2V0cywgaW5jbHVkaW5nIHRoZSBkYXRhIGZyb20gKk1hY29za28gZXQgYWwuKjoKCmBgYHtyLCBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHNjUk5Bc2VxKQpzY1JOQXNlcTo6TWFjb3Nrb1JldGluYURhdGEoKQpgYGAKCiMgSW1wb3J0IGRhdGEKClRoZSBgc2NSTkFzZXFgIHBhY2thZ2UgcHJvdmlkZXMgY29udmVuaWVudCBhY2Nlc3MgdG8gc2V2ZXJhbCBkYXRhc2V0cy4gU2VlIHRoZSBbcGFja2FnZSBCaW9jb25kdWN0b3IgcGFnZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9kYXRhL2V4cGVyaW1lbnQvaHRtbC9zY1JOQXNlcS5odG1sKSAKZm9yIG1vcmUgaW5mb3JtYXRpb24uCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBDb2RlIGJlbG93IG1pZ2h0IGFzayB5b3UgdG8gY3JlYXRlIGFuIEV4cGVyaW1lbnRIdWIgZGlyZWN0b3J5LiAKIyBUeXBlICd5ZXMnIGFuZCBoaXQgRW50ZXIsIHRvIGFsbG93IHRoaXMuCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHNjUk5Bc2VxKSkKc2NlIDwtIE1hY29za29SZXRpbmFEYXRhKCkKYGBgCgojIEEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QKCmBgYHtyLCBldmFsPUZBTFNFfQpzY2UKYGBgCgojIyBBY2Nlc3NpbmcgZGF0YSBmcm9tIGEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QKClBsZWFzZSBzZWUgW0ZpZ3VyZSA0LjEgaW4gT1NDQV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvcmVsZWFzZS9PU0NBL2RhdGEtaW5mcmFzdHJ1Y3R1cmUuaHRtbCkgCmZvciBhbiBvdmVydmlldyBvZiBhIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0LgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgRGF0YTogYXNzYXlzCmFzc2F5cyhzY2UpCmFzc2F5cyhzY2UpJGNvdW50c1sxOjUsIDE6NV0KCiMgRmVhdHVyZSBtZXRhZGF0YTogcm93RGF0YQpyb3dEYXRhKHNjZSkgIyBlbXB0eSBmb3Igbm93CgojIENlbGwgbWV0YWRhdGE6IGNvbERhdGEKY29sRGF0YShzY2UpCgojIFJlZHVjZWQgZGltZW5zaW9uczogcmVkdWNlZERpbXMKcmVkdWNlZERpbXMoc2NlKSAjIGVtcHR5IGZvciBub3cKYGBgCgojIyBDcmVhdGluZyBhIG5ldyBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIG9iamVjdAoKYGBge3IsIGV2YWw9RkFMU0V9CnNjZU5ldyA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChhc3NheXMgPSBsaXN0KGNvdW50cyA9IGFzc2F5cyhzY2UpJGNvdW50cykpCnNjZU5ldwoKcm0oc2NlTmV3KQpgYGAKCiMjIFN0b3JpbmcgKG1ldGEpZGF0YSBpbiBhIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0CgpgYGB7ciwgZXZhbD1GQUxTRX0KZmFrZUdlbmVOYW1lcyA8LSBwYXN0ZTAoImdlbmUiLCAxOm5yb3coc2NlKSkKcm93RGF0YShzY2UpJGZha2VOYW1lIDwtIGZha2VHZW5lTmFtZXMKaGVhZChyb3dEYXRhKHNjZSkpCiMgUmVtb3ZlIGFnYWluIGJ5IHNldHRpbmcgdG8gTlVMTApyb3dEYXRhKHNjZSkkZmFrZU5hbWUgPC0gTlVMTAoKYXNzYXlzKHNjZSkkbG9nQ291bnRzIDwtIGxvZzFwKGFzc2F5cyhzY2UpJGNvdW50cykKYXNzYXlzKHNjZSkKYXNzYXlzKHNjZSkkbG9nQ291bnRzWzE6NSwgMTo1XQphc3NheXMoc2NlKSRsb2dDb3VudHMgPC0gTlVMTApgYGAKCiMgT2J0YWluaW5nIGFuZCBpbmNsdWRpbmcgcm93RGF0YQoKVGhlIGByb3dEYXRhYCBzbG90IG9mIGEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QgYWxsb3dzIGZvciBzdG9yaW5nIAppbmZvcm1hdGlvbiBvbiB0aGUgZmVhdHVyZXMsIGkuZS4gdGhlIGdlbmVzLCBpbiBhIGRhdGFzZXQuIEluIG91ciBvYmplY3QsCnRoZSBgcm93RGF0YWAgc2xvdCBpcyBlbXB0eS4KCmBgYHtyLCBldmFsPUZBTFNFfQpyb3dEYXRhKHNjZSkKYGBgCgpBcyBzdWNoLCB0aGUgb25seSBpbmZvcm1hdGlvbiB3ZSBoYXZlIG9uIHRoZSBnZW5lcyBhcmUgdGhlaXIgbmFtZXMsIHdoaWNoIGNhbgpiZSByZXRyaWV2ZWQgYXMgdGhlIGByb3duYW1lc2Agb2YgdGhlIGV4cHJlc3Npb24gbWF0cml4LgoKYGBge3IsIGV2YWw9RkFMU0V9CmhlYWQocm93bmFtZXMoc2NlKSkKYGBgCgpUaGVzZSBhcmUgdGhlIGdlbmUgbmFtZXMgKHN5bWJvbHMpLiBOb3RlIHRoYXQgaXQgbWF5IGJlIHVzZWZ1bCB0byBpbmNsdWRlIAphZGRpdGlvbmFsIGluZm9ybWF0aW9uIGluIHRoZSBgcm93RGF0YWAgc2xvdC4gRm9yIGluc3RhbmNlLCB3ZSBtYXkgd2FudCB0byAKc3RvcmU6CgotIFVuYW1iaWd1b3VzIGdlbmUgaWRlbnRpZmllcnMgKGUuZy4gZnJvbSBFTlNFTUJMKQotIE9uIHdoaWNoIGNocm9tb3NvbWUgdGhlIGdlbmUgaXMgbG9jYXRlZAotIEdlbmUgbGVuZ3RoIChnZW5vbWljIHN0YXJ0IHBvc2l0aW9uIGFuZCBlbmQgcG9zaXRpb24pCi0gT3RoZXJzLi4uCgpgYGB7ciwgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSgiYmlvbWFSdCIpCgplbnNlbWJsNzUgPC0gdXNlRW5zZW1ibChiaW9tYXJ0ID0gJ2dlbmVzJywgCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFzZXQgPSAnbW11c2N1bHVzX2dlbmVfZW5zZW1ibCcsCiAgICAgICAgICAgICAgICAgICAgICAgIHZlcnNpb24gPSA3NSkKCmhlYWQobGlzdEF0dHJpYnV0ZXMoZW5zZW1ibDc1KSkgIyBwb3RlbnRpYWwgaW5mbyB0byBleHRyYWN0CgpnZW5lSW5mbyA8LSBnZXRCTShhdHRyaWJ1dGVzID0gYygiZW5zZW1ibF9nZW5lX2lkIiwgIyBFTlNFTUJMIHVuYW1iaWd1b3VzIGlkZW50aWZpZXIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm1naV9zeW1ib2wiLCAjIEdlbmUgc3ltYm9sICh0byBsaW5rIHdpdGggU0NFIHJvd25hbWVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNocm9tb3NvbWVfbmFtZSIsICMgT24gd2hpY2ggY2hyb21vc29tZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RhcnRfcG9zaXRpb24iLCAjIFN0YXJ0IHBvc2l0aW9uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJlbmRfcG9zaXRpb24iKSwjIEVuZCBwb3NpdGlvbgogICAgICAgICAgICAgICAgICBtYXJ0ID0gZW5zZW1ibDc1KQoKaGVhZChnZW5lSW5mbykKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZ2VuZUluZm8kbWdpX3N5bWJvbF91cHBlciA8LSB0b3VwcGVyKGdlbmVJbmZvJG1naV9zeW1ib2wpIAojIG1hdGNoIGJldHdlZW4gZ2VuZSBpbmZvIGFuZCByb3duYW1lcwoKc3VtKHJvd25hbWVzKHNjZSkgJWluJSBnZW5lSW5mbyRtZ2lfc3ltYm9sX3VwcGVyKQpzdW0oIXJvd25hbWVzKHNjZSkgJWluJSBnZW5lSW5mbyRtZ2lfc3ltYm9sX3VwcGVyKSAjIGxvc3QgaW4gY29udmVyc2lvbiA6KAoKcm93RGF0YShzY2UpIDwtIGdlbmVJbmZvW21hdGNoKHJvd25hbWVzKHNjZSksZ2VuZUluZm8kbWdpX3N5bWJvbF91cHBlciksXQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpoZWFkKHJvd0RhdGEoc2NlKSkKYGBgCgojIEZpbHRlcmluZyBub24taW5mb3JtYXRpdmUgZ2VuZXMKCmBgYHtyLCBldmFsPUZBTFNFfQpsaWJyYXJ5KGVkZ2VSKQoKIyBBIHZlcnkgc2ltcGxlIHN0cmF0ZWd5OiByZW1vdmUgYWxsIGdlbmVzIHRoYXQgYXJlIGV4cHJlc3NlZCBpbiBsZXNzIHRoYW4gMTAKIyBvdXQgb2YgNDkzMDAgY2VsbHMgLT4gbm90ZSB0aGF0IHRoaXMgYSB2ZXJ5IGxlbmllbnQgZmlsdGVyaW5nIGNyaXRlcml1bQprZWVwIDwtIHJvd1N1bXMoYXNzYXlzKHNjZSkkY291bnRzID4gMCkgPiAxMAp0YWJsZShrZWVwKQoKc2NlIDwtIHNjZVtrZWVwLF0KYGBgCgpOb3RlIHRoYXQgZGVkaWNhdGVkIGZ1bmN0aW9ucyBmb3IgZmlsdGVyaW5nIG91dCBsb3dseSBleHByZXNzZWQgZ2VuZXMgZXhpc3QuCk9uZSBzdWNoIGZ1bmN0aW9uIGlzIHRoZSBgZmlsdGVyQnlFeHByYCBmdW5jdGlvbiBvZiB0aGUgZWRnZVIgUiBwYWNrYWdlLgpJbiBicmllZiwgdGhlIHN0cmF0ZWd5IGtlZXBzIGdlbmVzIHRoYXQgaGF2ZSBhdCBsZWFzdCAibWluLmNvdW50IiByZWFkcyBpbiBhIAp3b3J0aHdoaWxlIG51bWJlciBzYW1wbGVzLiAKCk1vcmUgcHJlY2lzZWx5LCB0aGUgZmlsdGVyaW5nIGtlZXBzIGdlbmVzIHRoYXQgaGF2ZSBjb3VudC1wZXItbWlsbGlvbiAoQ1BNKSAKYWJvdmUgayBpbiBuIHNhbXBsZXMuIAoKayBpcyBkZXRlcm1pbmVkIGJ5IHRoZSBtaW4uY291bnQgYXJndW1lbnQgdG8gdGhlIGZ1bmN0aW9uLCBhbmQgYnkgdGhlIHNhbXBsZSAKbGlicmFyeSBzaXplcy4KCm4gaXMgZGV0ZXJtaW5lZCBieSB0aGUgZGVzaWduIG1hdHJpeC4gbiBjYW4gYmUgc2VlbiBhcyB0aGUgc21hbGxlc3QgZ3JvdXAgc2FtcGxlCnNpemUuIEEgYGdyb3VwYCBvZiBzYW1wbGVzL2NlbGxzIGNhbiBiZSBkZWZpbmVkIGFzIGNlbGxzIHRoYXQgYXJlIG1vcmUgc2ltaWxhcgp0byBvbmUgYW5vdGhlciwgZS5nLiwgZnJvbSB0aGUgc2FtZSBzZXF1ZW5jaW5nIGJhdGNoLCB0aGUgc2FtZSBwYXRpZW50Li4uLgpIZXJlIHdlIGNvdWxkIGFsc28gdXNlIHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgKGNlbGwgdHlwZSkgZm9yIGVhY2ggY2VsbCBhcyB0aGUKZ3JvdXBpbmcgdmFyaWFibGU7ICpub3RlIGhvd2V2ZXIsIHRoYXQgdGhpcyB1c3VhbGx5IGlzIG5vdCBhdmFpbGFibGUgdW50aWwgYSoKKmxhdGVyIHN0YWdlIGluIHRoZSBhbmFseXNpcyBwaXBlbGluZSogKGkuZS4sIGFmdGVyIGRpbWVuc2lvbiByZWR1Y3Rpb24gYW5kIApjbHVzdGVyaW5nLCB0b3BpY3Mgd2Ugd2lsbCBjb3ZlciBuZXh0IHNlc3Npb24uKQoKSWYgYWxsIHRoZSBncm91cCBzaXplcyBhcmUgbGFyZ2VyIHRoYW4gdGhlIGBsYXJnZS5uYCBhcmd1bWVudCBvZiB0aGUgCmZpbHRlckJ5RXhwciBmdW5jdGlvbiwgd2hpY2ggZGVmYXVsdHMgdG8gMTAsIHRoZW4gbiB3aWxsIGJlIHRha2VuIGFzCmBtaW4ucHJvcGAqIHRoZSB0aGUgbnVtYmVyIG9mIHNhbXBsZXMvY2VsbHMgaW4gdGhlIHNtYWxsZXN0IGdyb3VwLgoKTm90ZSB0aGF0IGFsbCB0aGUgZ3JvdXAgc2l6ZXMgd2lsbCBvZnRlbiBiZSBsYXJnZXIgdGhhbiB0aGUgYGxhcmdlLm5gIGluIGNhc2UKb2Ygc2luZ2xlLWNlbGwgZGF0YS4KCmBgYHtyLCBldmFsPUZBTFNFfQojIFNsb3cgKG1vcmUgdGhhbiAxbWluKSAtPiBkbyBub3QgcnVuCiMga2VlcDIgPC0gZmlsdGVyQnlFeHByKHNjZSwKIyAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBzY2UkY2x1c3RlciwKIyAgICAgICAgICAgICAgICAgICAgICAgbWluLmNvdW50ID0gMSwKIyAgICAgICAgICAgICAgICAgICAgICAgbWluLnRvdGFsLmNvdW50ID0gMTUsCiMgICAgICAgICAgICAgICAgICAgICAgIG1pbi5wcm9wID0gMC4yKQojIHRhYmxlKGtlZXAyKQpgYGAKCgojIFF1YWxpdHkgY29udHJvbAoKSW4gcXVhbGl0eSBjb250cm9sIChRQyksIHdlIGNoZWNrIHRoZSBxdWFsaXR5IG9mIG91ciBkYXRhc2V0LiBJbiBwYXJ0aWN1bGFyLCAKd2UgaW52ZXN0aWdhdGUgdW5kZXNpcmFibGUgb2RkaXRpZXMsIHN1Y2ggYXMgbG93LXF1YWxpdHkgY2VsbHMsIGVtcHR5IGRyb3BsZXRzCm9yIGRvdWJsZXRzLgoKIyMgSWRlbnRpZnlpbmcgYW5kIHJlbW92aW5nIGxvdy1xdWFsaXR5IGNlbGxzCgpUaGVyZSBhcmUgc2V2ZXJhbCBkaXN0aW5ndWlzaGluZyBmZWF0dXJlcyBvZiBsb3ctcXVhbGl0eSBjZWxscyB0aGF0IGNhbiBiZSB1c2VkCmluIG9yZGVyIHRvIGlkZW50aWZ5IHRoZW0uIFtBcyBkZXNjcmliZWQgaW4gdGhlIE9TQ0EgYm9va10oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xNC9PU0NBLmJhc2ljL3F1YWxpdHktY29udHJvbC5odG1sKSAKYm9vayk6CgoxLiBUaGUgbGlicmFyeSBzaXplIGlzIGRlZmluZWQgYXMgdGhlIHRvdGFsIHN1bSBvZiBjb3VudHMgYWNyb3NzIGFsbCAKcmVsZXZhbnQgZmVhdHVyZXMgZm9yIGVhY2ggY2VsbC4gSGVyZSwgd2Ugd2lsbCBjb25zaWRlciB0aGUgcmVsZXZhbnQgZmVhdHVyZXMgCnRvIGJlIHRoZSBlbmRvZ2Vub3VzIGdlbmVzLiBDZWxscyB3aXRoIHNtYWxsIGxpYnJhcnkgc2l6ZXMgYXJlIG9mIGxvdyBxdWFsaXR5IAphcyB0aGUgUk5BIGhhcyBiZWVuIGxvc3QgYXQgc29tZSBwb2ludCBkdXJpbmcgbGlicmFyeSBwcmVwYXJhdGlvbiwgZWl0aGVyIGR1ZSAKdG8gY2VsbCBseXNpcyBvciBpbmVmZmljaWVudCBjRE5BIGNhcHR1cmUgYW5kIGFtcGxpZmljYXRpb24uCgoyLiBUaGUgbnVtYmVyIG9mIGV4cHJlc3NlZCBmZWF0dXJlcyBpbiBlYWNoIGNlbGwgaXMgZGVmaW5lZCBhcyB0aGUgbnVtYmVyIG9mIAplbmRvZ2Vub3VzIGdlbmVzIHdpdGggbm9uLXplcm8gY291bnRzIGZvciB0aGF0IGNlbGwuIEFueSBjZWxsIHdpdGggdmVyeSAKZmV3IGV4cHJlc3NlZCBnZW5lcyBpcyBsaWtlbHkgdG8gYmUgb2YgcG9vciBxdWFsaXR5IGFzIHRoZSBkaXZlcnNlIHRyYW5zY3JpcHQgCnBvcHVsYXRpb24gaGFzIG5vdCBiZWVuIHN1Y2Nlc3NmdWxseSBjYXB0dXJlZC4KCjMuIFdlIHNvbWV0aW1lcyBoYXZlIHNwaWtlLWluIChFUkNDKSB0cmFuc2NyaXB0cyBhdmFpbGFibGUuClRoZSBwcm9wb3J0aW9uIG9mIHJlYWRzIG1hcHBlZCB0byBzcGlrZS1pbiB0cmFuc2NyaXB0cyBpcyBjYWxjdWxhdGVkIHJlbGF0aXZlIAp0byB0aGUgdG90YWwgY291bnQgYWNyb3NzIGFsbCBmZWF0dXJlcyAoaW5jbHVkaW5nIHNwaWtlLWlucykgZm9yIGVhY2ggY2VsbC4gCkFzIHRoZSBzYW1lIGFtb3VudCBvZiBzcGlrZS1pbiBSTkEgc2hvdWxkIGhhdmUgYmVlbiBhZGRlZCB0byBlYWNoIGNlbGwsIAphbnkgZW5yaWNobWVudCBpbiBzcGlrZS1pbiBjb3VudHMgaXMgc3ltcHRvbWF0aWMgb2YgbG9zcyBvZiBlbmRvZ2Vub3VzIFJOQS4KCjQuIEluIHRoZSBhYnNlbmNlIG9mIHNwaWtlLWluIHRyYW5zY3JpcHRzLCB0aGUgcHJvcG9ydGlvbiBvZiByZWFkcyBtYXBwZWQgCnRvIGdlbmVzIGluIHRoZSBtaXRvY2hvbmRyaWFsIGdlbm9tZSBjYW4gYmUgdXNlZC4gSGlnaCBwcm9wb3J0aW9ucyBhcmUgCmluZGljYXRpdmUgb2YgcG9vci1xdWFsaXR5IGNlbGxzIChJc2xhbSBldCBhbC4gMjAxNDsgSWxpY2ljIGV0IGFsLiAyMDE2KSwgCnByZXN1bWFibHkgYmVjYXVzZSBvZiBsb3NzIG9mIGN5dG9wbGFzbWljIFJOQSBmcm9tIHBlcmZvcmF0ZWQgY2VsbHMuIApUaGUgcmVhc29uaW5nIGlzIHRoYXQsIGluIHRoZSBwcmVzZW5jZSBvZiBtb2Rlc3QgZGFtYWdlLCB0aGUgaG9sZXMgaW4gdGhlIApjZWxsIG1lbWJyYW5lIHBlcm1pdCBlZmZsdXggb2YgaW5kaXZpZHVhbCB0cmFuc2NyaXB0IG1vbGVjdWxlcyBidXQgYXJlIHRvbyAKc21hbGwgdG8gYWxsb3cgbWl0b2Nob25kcmlhIHRvIGVzY2FwZSwgbGVhZGluZyB0byBhIHJlbGF0aXZlIGVucmljaG1lbnQgb2YgCm1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdHMuIEZvciBzaW5nbGUtbnVjbGVpIFJOQS1zZXEgZXhwZXJpbWVudHMsIGhpZ2ggCnByb3BvcnRpb25zIGFyZSBhbHNvIHVzZWZ1bCBhcyB0aGV5IGNhbiBtYXJrIGNlbGxzIHdoZXJlIHRoZSBjeXRvcGxhc20gaGFzIApub3QgYmVlbiBzdWNjZXNzZnVsbHkgc3RyaXBwZWQuCgojIyBDYWxjdWxhdGUgUUMgdmFyaWFibGVzCgpUaGlzIGZ1bmN0aW9uIGNhbGN1bGF0ZXMgdXNlZnVsIFFDIG1ldHJpY3MgZm9yIGlkZW50aWZpY2F0aW9uIGFuZCByZW1vdmFsIG9mIApwb3RlbnRpYWxseSBwcm9ibGVtYXRpYyBjZWxscy4gRGVmYXVsdCBwZXItY2VsbCBtZXRyaWNzIGFyZSB0aGUgc3VtIG9mIGNvdW50cyAKKGkuZS4sIHRoZSBsaWJyYXJ5IHNpemUpIGFuZCB0aGUgbnVtYmVyIG9mIGRldGVjdGVkIGZlYXR1cmVzLiBUaGUgcGVyY2VudGFnZSBvZiAKY291bnRzIGluIHRoZSB0b3AgZmVhdHVyZXMgYWxzbyBwcm92aWRlcyBhIG1lYXN1cmUgb2YgbGlicmFyeSBjb21wbGV4aXR5LgoKSWYgc3Vic2V0cyBpcyBzcGVjaWZpZWQsIHRoZXNlIHN0YXRpc3RpY3MgYXJlIGFsc28gY29tcHV0ZWQgZm9yIGVhY2ggc3Vic2V0IG9mIApmZWF0dXJlcy4gVGhpcyBpcyB1c2VmdWwgZm9yIGludmVzdGlnYXRpbmcgZ2VuZSBzZXRzIG9mIGludGVyZXN0LCBlLmcuLCAKbWl0b2Nob25kcmlhbCBnZW5lcy4KCmBgYHtyLCBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHNjYXRlcikKCiMgY2hlY2sgRVJDQyBzcGlrZS1pbiB0cmFuc2NyaXB0cwpzdW0oZ3JlcGwoIl5FUkNDLSIsIHJvd25hbWVzKHNjZSkpKSAjIG5vIHNwaWtlLWluIHRyYW5zY3JpcHRzIGF2YWlsYWJsZQoKIyBjaGVjayBtaXRvY2hvbmRyaWFsIGdlbmVzCnN1bShyb3dEYXRhKHNjZSkkY2hyb21vc29tZV9uYW1lPT0iTVQiLG5hLnJtID0gVFJVRSkgIyAyOCBtaXRvY2hvbmRyaWFsIGdlbmVzCnN1bShncmVwbCgiXk1ULSIsIHJvd25hbWVzKHNjZSkpKSAjIGFsdGVybmF0aXZlbHkKaXMubWl0byA8LSBncmVwbCgiXk1ULSIsIHJvd25hbWVzKHNjZSkpCgojIyBjYWxjdWxhdGUgUUMgbWV0cmljcwpkZiA8LSBwZXJDZWxsUUNNZXRyaWNzKHNjZSwgc3Vic2V0cz1saXN0KE1pdG89aXMubWl0bykpCmhlYWQoZGYpCgojIGFkZCBRQyB2YXJpYWJsZXMgdG8gc2NlIG9iamVjdApjb2xEYXRhKHNjZSkgPC0gY2JpbmQoY29sRGF0YShzY2UpLCBkZikKCiMgdGhlIFFDIHZhcmlhYmxlcyBoYXZlIG5vdyBiZWVuIGFkZGVkIHRvIHRoZSBjb2xEYXRhIG9mIG91ciBTQ0Ugb2JqZWN0LgpoZWFkKGNvbERhdGEoc2NlKSkKYGBgCgojIyBFeHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzCgpJbiB0aGUgZmlndXJlIGJlbG93LCB3ZSBzZWUgdGhhdCBzZXZlcmFsIGNlbGxzIGhhdmUgYSB2ZXJ5IGxvdyBudW1iZXIgb2YgCmV4cHJlc3NlZCBnZW5lcywgYW5kIHdoZXJlIG1vc3Qgb2YgdGhlIG1vbGVjdWxlcyBhcmUgZGVyaXZlZCBmcm9tIAptaXRvY2hvbmRyaWFsIGdlbmVzLiBUaGlzIGluZGljYXRlcyBsaWtlbHkgZGFtYWdlZCBjZWxscywgcHJlc3VtYWJseSBiZWNhdXNlIApvZiBsb3NzIG9mIGN5dG9wbGFzbWljIFJOQSBmcm9tIHBlcmZvcmF0ZWQgY2VsbHMsIHNvIHdlIHNob3VsZCByZW1vdmUgdGhlc2UgZm9yIAp0aGUgZG93bnN0cmVhbSBhbmFseXNpcy4KCmBgYHtyLCBldmFsPUZBTFNFfQojIE51bWJlciBvZiBnZW5lcyB2cyBsaWJyYXJ5IHNpemUKcGxvdENvbERhdGEoc2NlLCB4ID0gInN1bSIsIHk9ImRldGVjdGVkIiwgY29sb3VyX2J5PSJjbHVzdGVyIikgCgojIE1pdG9jaG9uZHJpYWwgZ2VuZXMKcGxvdENvbERhdGEoc2NlLCB4ID0gImRldGVjdGVkIiwgeT0ic3Vic2V0c19NaXRvX3BlcmNlbnQiKQpgYGAKCiMjIFFDIHVzaW5nIGFkYXB0aXZlIHRocmVzaG9sZHMKCkJlbG93LCB3ZSByZW1vdmUgY2VsbHMgdGhhdCBhcmUgb3V0bHlpbmcgd2l0aCByZXNwZWN0IHRvCgogMS4gQSBsb3cgc2VxdWVuY2luZyBkZXB0aCAobnVtYmVyIG9mIFVNSXMpOwogMi4gQSBsb3cgbnVtYmVyIG9mIGdlbmVzIGRldGVjdGVkOwogMy4gQSBoaWdoIHBlcmNlbnRhZ2Ugb2YgcmVhZHMgZnJvbSBtaXRvY2hvbmRyaWFsIGdlbmVzLgogCiBIZXJlIHdlIHdpbGwgcmVtb3ZlIGNlbGxzIGZvciBRQyBiYXNlZCBvbiAqKmFkYXB0aXZlIHRocmVzaG9sZHMqKiByZWxhdGVkIHRvCiB0aGUgdGhyZWUgcG9pbnRzIGZyb20gYWJvdmUuIEFkYXB0aXZlIHRyaGVzaG9sZHMgYXJlIHVzZWQgYXMgb3Bwb3NlZCB0bwogKipmaXhlZCB0aHJlc2hvbGRzKiouIAogCldpdGggZml4ZWQgdGhyZXNob2xkcywgd2UgdXNlIGZpeGVkIGN1dC1vZmYgdmFsdWVzIGZvciBlYWNoIGNlbGwgdG8gcGFzcyBRQywgCmUuZy4sIHdlIG1pZ2h0IGNvbnNpZGVyIGNlbGxzIHRvIGJlIGxvdyBxdWFsaXR5IGlmIHRoZXkgaGF2ZSBsaWJyYXJ5IHNpemVzIApiZWxvdyAxMDAsMDAwIHJlYWRzOyBleHByZXNzIGZld2VyIHRoYW4gNSwwMDAgZ2VuZXM7IGhhdmUgc3Bpa2UtaW4gcHJvcG9ydGlvbnMKYWJvdmUgMTAlOyBvciBoYXZlIG1pdG9jaG9uZHJpYWwgcHJvcG9ydGlvbnMgYWJvdmUgMTAlLgoKV2l0aCBhZGFwdGl2ZSB0aHJlc2hvbGRzLCB3ZSBhc3N1bWUgdGhhdCBtb3N0IG9mIHRoZSBkYXRhc2V0IGNvbnNpc3RzIG9mIApoaWdoLXF1YWxpdHkgY2VsbHMuIFdlIHRoZW4gaWRlbnRpZnkgY2VsbHMgdGhhdCBhcmUgb3V0bGllcnMgZm9yIHRoZSB2YXJpb3VzIApRQyBtZXRyaWNzLCBiYXNlZCBvbiB0aGUgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiAoTUFEKSBmcm9tIHRoZSBtZWRpYW4gdmFsdWUKb2YgZWFjaCBtZXRyaWMgYWNyb3NzIGFsbCBjZWxscy4gQnkgZGVmYXVsdCwgd2UgY29uc2lkZXIgYSB2YWx1ZSB0byBiZSBhbm91dGxpZXIgCmlmIGl0IGlzIG1vcmUgdGhhbiAzIE1BRHMgZnJvbSB0aGUgbWVkaWFuIGluIHRoZSDigJxwcm9ibGVtYXRpY+KAnSBkaXJlY3Rpb24uIFRoaXMgCmlzIGxvb3NlbHkgbW90aXZhdGVkIGJ5IHRoZSBmYWN0IHRoYXQgc3VjaCBhIGZpbHRlciB3aWxsIHJldGFpbiA5OSUgb2YgCm5vbi1vdXRsaWVyIHZhbHVlcyB0aGF0IGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFdlIGRlbW9uc3RyYXRlIGFkb3B0aW5nCmFkYXB0aXZlIHRocmVzaG9sZHMgb24gdGhlIE1hY29za28gZGF0YXNldDoKCmBgYHtyLCBldmFsPUZBTFNFfQpsb3dMaWIgPC0gaXNPdXRsaWVyKGRmJHN1bSwgdHlwZT0ibG93ZXIiLCBsb2c9VFJVRSkKbG93RmVhdHVyZXMgPC0gaXNPdXRsaWVyKGRmJGRldGVjdGVkLCB0eXBlPSJsb3dlciIsIGxvZz1UUlVFKQpoaWdoTWl0byA8LSBpc091dGxpZXIoZGYkc3Vic2V0c19NaXRvX3BlcmNlbnQsIHR5cGU9ImhpZ2hlciIpCgp0YWJsZShsb3dMaWIpCnRhYmxlKGxvd0ZlYXR1cmVzKQp0YWJsZShoaWdoTWl0bykKCmRpc2NhcmRDZWxscyA8LSAobG93TGliIHwgbG93RmVhdHVyZXMgfCBoaWdoTWl0bykKdGFibGUoZGlzY2FyZENlbGxzKQpjb2xEYXRhKHNjZSkkZGlzY2FyZENlbGxzIDwtIGRpc2NhcmRDZWxscwoKIyB2aXN1YWxpemUgY2VsbHMgdG8gYmUgcmVtb3ZlZApwbG90Q29sRGF0YShzY2UsIHggPSAic3VtIiwgeT0iZGV0ZWN0ZWQiLCBjb2xvdXJfYnk9ImRpc2NhcmRDZWxscyIpCnBsb3RDb2xEYXRhKHNjZSwgeCA9ICJkZXRlY3RlZCIsIHk9InN1YnNldHNfTWl0b19wZXJjZW50IiwgY29sb3VyX2J5ID0gImRpc2NhcmRDZWxscyIpCmBgYAoKV2UgcmVtb3ZlZCBhIHRvdGFsIG9mICQzNDIzJCBjZWxscywgbW9zdCBvZiB3aGljaCBiZWNhdXNlIG9mIGFuIG91dGx5aW5nbHkgaGlnaCAKcGVyY2VudGFnZSBvZiByZWFkcyBmcm9tIG1pdG9jaG9uZHJpYWwgZ2VuZXMuCgojIyBJZGVudGlmeWluZyBhbmQgcmVtb3ZpbmcgZW1wdHkgZHJvcGxldHMKCk5vdGUgdGhhdCB0aGUgcmVtb3ZhbCBvZiBjZWxscyB3aXRoIGxvdyBzZXF1ZW5jaW5nIGRlcHRoIHVzaW5nIHRoZSBhZGFwdGl2ZSAKdGhyZXNob2xkIHByb2NlZHVyZSBhYm92ZSBpcyBhIHdheSBvZiByZW1vdmluZyBlbXB0eSBkcm9wbGV0cy4gCk90aGVyIGFwcHJvYWNoZXMgYXJlIHBvc3NpYmxlLCBlLmcuLCByZW1vdmluZyBjZWxscyBieSBzdGF0aXN0aWNhbCB0ZXN0aW5nIAp1c2luZyBgZW10cHlEcm9wc2AuIFRoaXMgZG9lcyByZXF1aXJlIHVzIHRvIHNwZWNpZnkgYSBsb3dlciBib3VuZCBvbiB0aGUgdG90YWwgCm51bWJlciBvZiBVTUlzLCBiZWxvdyB3aGljaCBhbGwgY2VsbHMgYXJlIGNvbnNpZGVyZWQgdG8gY29ycmVzcG9uZCB0byBlbXB0eSAKZHJvcGxldHMuIFRoaXMgbG93ZXIgYm91bmQgbWF5IG5vdCBiZSB0cml2aWFsIHRvIGRlcml2ZSwgYnV0IHRoZSBgYmFyY29kZVJhbmtzYApmdW5jdGlvbiBjYW4gYmUgdXNlZnVsIHRvIGlkZW50aWZ5IGFuIGVsYm93L2tuZWUgcG9pbnQuCgpJbiBicmllZiwgdGhlIHN0ZXBzIHRha2VuIGJ5IHRoZSBgZW10cHlEcm9wc2AgZnVuY3Rpb24gY2FuIGJlIHN1bW1hcml6ZWQgYXMgCmZvbGxvd3M6CgoxLglEZWZpbmUgdGhyZXNob2xkIFQgb2YgdG90YWwgVU1JIGNvdW50cyAoZS5nLiB3aXRoIHRoZSBoZWxwIG9mIHRoZSAKYGJhcmNvZGVSYW5rc2AgZnVuY3Rpb24pLCBiZWxvdyB3aGljaCBjZWxscyBtYXkgYmUgY29uc2lkZXJlZCB0byBiZSBmcm9tIGVtcHR5IApkcm9wbGV0cy4gQ2FsbCB0aGlzIHNldCBvZiBjZWxscyBFLgoKMi4JRGVmaW5lICRBX2ckIGFzIHRoZSB0b3RhbCBnZW5lIGV4cHJlc3Npb24gYWNyb3NzIGFsbCBjZWxscyBpbiBFLgoKMy4JRGVmaW5lICRwaV9nJCBhcyB0aGUgcmVsYXRpdmUgY29udHJpYnV0aW9uIG9mIGdlbmUgZyB0byB0aGUgYW1iaWVudCBwcm9maWxlLgoKNC4JQ2FsY3VsYXRlIHAtdmFsdWUgZm9yIGVhY2ggY2VsbCB0byBoYXZlIGEgdHJhbnNjcmlwdGlvbmFsIHByb2ZpbGUgc2ltaWxhciB0bwp0aGUgYW1iaWVudCBzb2x1dGlvbi4gSW50dWl0aXZlbHksIGEgcC12YWx1ZSBiZWxvdyB0aGUgcmVxdWVzdGVkIGFscGhhIGxldmVsIAp3b3VsZCBjb3JyZXNwb25kIHRvIGEgY2VsbCBmb3Igd2hpY2ggdGhlIG9ic2VydmVkIGNvdW50IHByb2ZpbGUgc3Ryb25nbHkgCmRldmlhdGVzIGZyb20gdGhlIGNvdW50IHByb2ZpbGUgb2JzZXJ2ZWQgaW4gdGhlIGNlbGxzIHdpdGggYSBsaWJyYXJ5IHNpemUgCmJlbG93IHRocmVzaG9sZCBULCBpLmUuLCBhIG5vbi1lbXB0eSBkcm9wbGV0LgoKYGBge3IsIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoRHJvcGxldFV0aWxzKQpiY3JhbmsgPC0gYmFyY29kZVJhbmtzKGNvdW50cyhzY2UpKQoKIyBPbmx5IHNob3dpbmcgdW5pcXVlIHBvaW50cyBmb3IgcGxvdHRpbmcgc3BlZWQuIER1cGxpY2F0ZWQgcmFua3MgYXJlIGEgCiMgY29uc2VxdWVuY2Ugb2YgdGllcyBpbiB0aGUgcmFua3MsIGkuZS4sIHdoZW4gY2VsbHMgaGF2ZSBhbiBlcXVhbCBsaWJyYXJ5IHNpemUuCnN1bShkdXBsaWNhdGVkKGJjcmFuayRyYW5rKSkKdW5pcSA8LSAhZHVwbGljYXRlZChiY3JhbmskcmFuaykKCnBsb3QoYmNyYW5rJHJhbmtbdW5pcV0sIGJjcmFuayR0b3RhbFt1bmlxXSwgbG9nPSJ4eSIsCiAgICB4bGFiPSJSYW5rIiwgeWxhYj0iVG90YWwgVU1JIGNvdW50IiwgY2V4LmxhYj0xLjIpCmFibGluZShoPW1ldGFkYXRhKGJjcmFuaykkaW5mbGVjdGlvbiwgY29sPSJkYXJrZ3JlZW4iLCBsdHk9MikKYWJsaW5lKGg9bWV0YWRhdGEoYmNyYW5rKSRrbmVlLCBjb2w9ImRvZGdlcmJsdWUiLCBsdHk9MikKYWJsaW5lKGg9MzUwLCBjb2w9Im9yYW5nZSIsIGx0eT0yKSAjIHBpY2tlZCB2aXN1YWxseSBteXNlbGYKbGVnZW5kKCJ0b3ByaWdodCIsIAogICAgICAgbGVnZW5kPWMoIkluZmxlY3Rpb24iLCAiS25lZSIsICJFbXBpcmljYWwga25lZSBwb2ludCIpLCAKICAgICAgIGNvbD1jKCJkYXJrZ3JlZW4iLCAiZG9kZ2VyYmx1ZSIsICJvcmFuZ2UiKSwgCiAgICAgICBsdHk9MiwgCiAgICAgICBjZXg9MS4yKQoKc2V0LnNlZWQoMTAwKQpsaW1pdCA8LSAzNTAgICAKYWxsLm91dCA8LSBlbXB0eURyb3BzKGNvdW50cyhzY2UpLCBsb3dlcj1saW1pdCwgdGVzdC5hbWJpZW50PVRSVUUpCiMgcC12YWx1ZXMgZm9yIGNlbGxzIHdpdGggdG90YWwgVU1JIGNvdW50IHVuZGVyIHRoZSBsb3dlciBib3VuZC4KaGlzdChhbGwub3V0JFBWYWx1ZVthbGwub3V0JFRvdGFsIDw9IGxpbWl0ICYgYWxsLm91dCRUb3RhbCA+IDBdLAogICAgeGxhYj0iUC12YWx1ZSIsIG1haW49IiIsIGNvbD0iZ3JleTgwIiwgYnJlYWtzPTIwKQoKIyBidXQgbm90ZSB0aGF0IGl0IHdvdWxkIHJlbW92ZSBhIHZlcnkgaGlnaCBudW1iZXIgb2YgY2VsbHMKbGVuZ3RoKHdoaWNoKGFsbC5vdXQkRkRSIDw9IDAuMDEpKSAjIHJldGFpbmVkCgojIHNvIHdlIHN0aWNrIHRvIHRoZSBtb3JlIGxlbmllbnQgYWRhcHRpdmUgZmlsdGVyaW5nIHN0cmF0ZWd5CiMgcmVtb3ZlIGNlbGxzIGlkZW50aWZpZWQgdXNpbmcgYWRhcHRpdmUgdGhyZXNob2xkcwpzY2UgPC0gc2NlWywgIWNvbERhdGEoc2NlKSRkaXNjYXJkQ2VsbHNdCmBgYAoKIyMgSWRlbnRpZnlpbmcgYW5kIHJlbW92aW5nIGRvdWJsZXRzCgpXZSB3aWxsIHVzZSAKW3NjRGJsRmluZGVyXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvMy4xNC9iaW9jL2h0bWwvc2NEYmxGaW5kZXIuaHRtbCkgCnRvIGRldGVjdCBkb3VibGV0IGNlbGxzLgoKQXMgZGlzY3Vzc2VkIGluIHRoZSB0aGVvcnkgc2Vzc2lvbiBvZiBsYXN0IHdlZWssIHRoZSBzdGVwcyB0YWtlbiBieSAKYHNjRGJsRmluZGVyYCBjYW4gYmUgc3VtbWFyaXplZCBhcyBmb2xsb3dzOgoKMS4gUGVyZm9ybSBwcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpcyAoUENBKSBvbiBsb2ctbm9ybWFsaXplZCBleHByZXNzaW9uIApjb3VudHMuIFRoaXMgYWxsb3dzIGZvciBwcm9qZWN0aW5nIGVhY2ggY2VsbCBpbiB0aGUgZGF0YXNldCBpbnRvIGEgMkQgc3BhY2UKKGZvciBtb3JlIGRldGFpbHMgb24gUENBLCBzZWUgbmV4dCB3ZWVrcyBzZXNzaW9uKS4KCjIuIFJhbmRvbWx5IHNlbGVjdCB0d28gY2VsbHMsIHN1bSB0aGVpciBjb3VudHMgYW5kIG5vcm1hbGl6ZSwgYW5kIHByb2plY3QgCmludG8gUENBIHNwYWNlIGZyb20gc3RlcCAxLiBJbiBvdGhlciB3b3JkcywgYXJ0aWZpY2lhbGx5IGdlbmVyYXRlIGRvdWJsZXRzIGFuZApzZWUgd2hlcmUgdGhleSBhcmUgbG9jYXRlZCBpbiB0aGUgMkQgc3BhY2UuCgozLiBSZXBlYXQgc3RlcCAyIG1hbnkgdGltZXMgKGdlbmVyYXRlIG1hbnkgYXJ0aWZpY2lhbCBkb3VibGV0cykuCgo0LiBHZW5lcmF0ZSBuZWlnaGJvciBuZXR3b3JrIGluIHRoZSAyRCBzcGFjZS4gVGhlIG5ldHdvcmsgaXMgdGhlbiB1c2VkIHRvIAplc3RpbWF0ZSBhIG51bWJlciBvZiBjaGFyYWN0ZXJpc3RpY3MgZm9yIGVhY2ggY2VsbCwgaW4gcGFydGljdWxhciB0aGUgcHJvcG9ydGlvbiAKZiBhcnRpZmljaWFsIGRvdWJsZXRzIGFtb25nIHRoZSBuZWFyZXN0IG5laWdoYm9ycy4KCjUuIFVzZSB0aGlzIGluZm9ybWF0aW9uLCBhbG9uZyB3aXRoIG90aGVyIHByZWRpY3RvcnMsIHRvIHRyYWluIGEgY2xhc3NpZmllcgooZ3JhZGllbnQgYm9vc3RlZCB0cmVlKSB0aGF0IGFsbG93cyBmb3IgZGlzdGluZ3Vpc2hpbmcgZG91YmxldHMgZnJvbSBzaW5nbGV0cy4KCk5vdGU6IG9ubHkgY2xhc3NpZmllcyBmb3IgaWRlbnRpZnlpbmcgZG91YmxldHMgZm9yIHdoaWNoIHRoZSB0d28gY2VsbHMKYXJlIGZyb20gZGlmZmVyZW50IGNlbGwgdHlwZSBjbHVzdGVycy4KCgpgYGB7ciwgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyMgcGVyZm9ybSBkb3VibGV0IGRldGVjdGlvbgpsaWJyYXJ5KHNjRGJsRmluZGVyKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpzZXQuc2VlZCgyMTExMDMpCnNhbXBsZUlEIDwtIHVubGlzdChsYXBwbHkoc3Ryc3BsaXQoY29sRGF0YShzY2UpJGNlbGwuaWQsIHNwbGl0PSJfIiksICJbWyIsIDEpKQp0YWJsZShzYW1wbGVJRCkKc2NlIDwtIHNjRGJsRmluZGVyKHNjZSwgCiAgICAgICAgICAgICAgICAgICByZXR1cm5UeXBlPSJ0YWJsZSIsCiAgICAgICAgICAgICAgICAgICBzYW1wbGVzID0gZmFjdG9yKHNhbXBsZUlEKSkKdGFibGUoc2NlJHNjRGJsRmluZGVyLmNsYXNzKQoKCiMjIHZpc3VhbGl6ZSB0aGVzZSBzY29yZXMKIyMgZXhwbG9yZSBkb3VibGV0IHNjb3JlIHdydCBvcmlnaW5hbCBjbHVzdGVyIGxhYmVscwpib3hwbG90KGxvZzFwKHNjZSRzY0RibEZpbmRlci5zY29yZSkgfiBmYWN0b3IoY29sRGF0YShzY2UpJGNsdXN0ZXIsIGV4Y2x1ZGU9TlVMTCkpCgp0YWIgPC0gdGFibGUoc2NlJHNjRGJsRmluZGVyLmNsYXNzLCBzY2UkY2x1c3RlciwgCiAgICAgIGV4Y2x1ZGU9TlVMTCkKdGFiCnQodCh0YWIpIC8gY29sU3Vtcyh0YWIpKQoKYmFycGxvdCh0KHQodGFiKSAvIGNvbFN1bXModGFiKSlbMixdLAogICAgICAgIHhsYWIgPSAiQ2x1c3RlciIsIHlsYWIgPSAiRnJhY3Rpb24gb2YgZG91YmxldHMiKQoKcmFuZ2Uoc2NlJHNjRGJsRmluZGVyLnNjb3JlW3NjZSRzY0RibEZpbmRlci5jbGFzcyAgPT0gImRvdWJsZXQiICYgc2FtcGxlSUQgPT0gInIxIl0pCnJhbmdlKHNjZSRzY0RibEZpbmRlci5zY29yZVtzY2Ukc2NEYmxGaW5kZXIuY2xhc3MgID09ICJzaW5nbGV0IiAmIHNhbXBsZUlEID09ICJyMSJdKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQojIHJlbW92ZSBkb3VibGV0cwpzY2UgPC0gc2NlWywhc2NlJHNjRGJsRmluZGVyLmNsYXNzID09ICJkb3VibGV0Il0KYGBgCgojIE5vcm1hbGl6YXRpb24KCk5vcm1hbGl6YXRpb24gYWltcyB0byByZW1vdmUgdGVjaG5pY2FsIGVmZmVjdHMgc3VjaCBhcyBzZXF1ZW5jaW5nIGRlcHRoIHNvIHRoYXQgCmNvbXBhcmlzb25zIGJldHdlZW4gY2VsbHMgYXJlIG5vdCBjb25mb3VuZGVkIGJ5IHRoZW0uIFRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgCm5vcm1hbGl6YXRpb24gbWV0aG9kcyBtZXRob2RzIHVzZSBzY2FsaW5nLCB3aGVyZSBhIHNjYWxpbmcgZmFjdG9yIChhbHNvIGNhbGxlZCAKc2l6ZSBmYWN0b3IsIG5vcm1hbGl6YXRpb24gZmFjdG9yKSBpcyBlc3RpbWF0ZWQgZm9yIGVhY2ggY2VsbC4gVGhlc2Ugc2NhbGluZyAKZmFjdG9ycyAoZS5nLiwgdGhlIGVmZmVjdGl2ZSBsaWJyYXJ5IHNpemUpIGNhbiBiZSBpbmNsdWRlZCBhcyBhbiBvZmZzZXQKdG8gZG93bnN0cmVhbSBtb2RlbGluZyBwcm9jZWR1cmVzLiBUaGlzIGVmZmVjdGl2ZWx5IGFsbG93cyBmb3IgcGVyZm9ybWluZwppbmZlcmVuY2Ugb24gdGhlIHJlbGF0aXZlIGFidW5kYW5jZSBvZiBhIGdlbmUgaW4gYSBjZWxsLCBhZnRlciBhY2NvdW50aW5nIGZvciAKbGlicmFyeSBzaXplIGFuZCBSTkEgY29tcG9zaXRpb24gZGlmZmVyZW5jZXMgYmV0d2VlbiBjZWxscywgd2hpY2ggaXMgbXVjaCBtb3JlCnJlbGV2YW50IHRoYW4gY29tcGFyaW5nIHJhdyBjb3VudHMKCk5vdGUgdGhhdCB3ZSBhbHdheXMgcHJlZmVyIHRoZSB1c2Ugb2Ygb2Zmc2V0cyBvdmVyIHRyYW5zZm9ybWF0aW9uIG9mIHRoZSBkYXRhLgpJbiBicmllZiwgc3VjaCB0cmFuc2Zvcm1hdGlvbiB3b3VsZCBkaXN0b3J0IHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcAppbiB0aGUgZGF0YS4gRm9yIG1vcmUgZGV0YWlscywgd2UgcmVmZXIgdG8gdGhlIGRvY3VtZW50IHRoYXQgd2FzIGRpc2N1c3NlZAppbiB0aGVvcnkgc2Vzc2lvbiBvZiBsYXN0IHdlZWsKKFtsaW5rXShodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8vU0dBMjEvc2VxdWVuY2luZ19zY2FsaW5nTm9ybWFsaXphdGlvbi5odG1sKSkKCkZvciBub3JtYWxpemF0aW9uLCB0aGUgc2l6ZSBmYWN0b3JzICRzX2kkIGNvbXB1dGVkIGhlcmUgYXJlIHNpbXBseSBzY2FsZWQgCmxpYnJhcnkgc2l6ZXM6ClxbIE5faSA9IFxzdW1fZyBZX3tnaX0gXF0KXFsgc19pID0gTl9pIC8gXGJhcntOfV9pIFxdCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc2NlIDwtIGxvZ05vcm1Db3VudHMoc2NlKQoKIyBub3RlIHdlIGFsc28gcmV0dXJuZWQgbG9nIGNvdW50czogc2VlIHRoZSBhZGRpdGlvbmFsIGxvZ2NvdW50cyBhc3NheS4Kc2NlCgojIHlvdSBjYW4gZXh0cmFjdCBzaXplIGZhY3RvcnMgdXNpbmcKc2YgPC0gbGlicmFyeVNpemVGYWN0b3JzKHNjZSkKbWVhbihzZikgIyBlcXVhbCB0byAxIGR1ZSB0byBzY2FsaW5nLgpwbG90KHg9IGxvZyhjb2xTdW1zKGFzc2F5cyhzY2UpJGNvdW50cykpLCAKICAgICB5PXNmKQpgYGAKCkZyb20gdGhlIE9TQ0EgYm9vazoKQWx0ZXJuYXRpdmVseSwgd2UgbWF5IHVzZSBtb3JlIHNvcGhpc3RpY2F0ZWQgYXBwcm9hY2hlcyBmb3IgdmFyaWFuY2Ugc3RhYmlsaXppbmcgCnRyYW5zZm9ybWF0aW9ucyBpbiBnZW5vbWljcyBkYXRhLCBlLmcuLCBgREVTZXEyYCBvciBgc2N0cmFuc2Zvcm1gLiBUaGVzZSBhaW0gCnRvIHJlbW92ZSB0aGUgbWVhbi12YXJpYW5jZSB0cmVuZCBtb3JlIGVmZmVjdGl2ZWx5IHRoYW4gdGhlIHNpbXBsZXIgCnRyYW5zZm9ybWF0aW9ucyBtZW50aW9uZWQgYWJvdmUsIHRob3VnaCBpdCBjb3VsZCBiZSBhcmd1ZWQgd2hldGhlciB0aGlzIGlzIAphY3R1YWxseSBkZXNpcmFibGUuIEZvciBsb3ctY292ZXJhZ2Ugc2NSTkEtc2VxIGRhdGEsIHRoZXJlIHdpbGwgYWx3YXlzIGJlIGEgCm1lYW4tdmFyaWFuY2UgdHJlbmQgdW5kZXIgYW55IHRyYW5zZm9ybWF0aW9uLCBmb3IgdGhlIHNpbXBsZSByZWFzb24gdGhhdCB0aGUgCnZhcmlhbmNlIG11c3QgYmUgemVybyB3aGVuIHRoZSBtZWFuIGNvdW50IGlzIHplcm8uIFRoZXNlIG1ldGhvZHMgYWxzbyBmYWNlIHRoZSAKY2hhbGxlbmdlIG9mIHJlbW92aW5nIHRoZSBtZWFuLXZhcmlhbmNlIHRyZW5kIHdoaWxlIHByZXNlcnZpbmcgdGhlIGludGVyZXN0aW5nIApjb21wb25lbnQgb2YgdmFyaWF0aW9uLCBpLmUuLCB0aGUgbG9nLWZvbGQgY2hhbmdlcyBiZXR3ZWVuIHN1YnBvcHVsYXRpb25zOyB0aGlzIAptYXkgb3IgbWF5IG5vdCBiZSBkb25lIGFkZXF1YXRlbHksIGRlcGVuZGluZyBvbiB0aGUgYWdncmVzc2l2ZW5lc3Mgb2YgCnRoZSBhbGdvcml0aG0uCgpJbiBwcmFjdGljZSwgdGhlIGxvZy10cmFuc2Zvcm1hdGlvbiBpcyBhIGdvb2QgZGVmYXVsdCBjaG9pY2UgZHVlIHRvIGl0cyAKc2ltcGxpY2l0eSBhbmQgaW50ZXJwcmV0YWJpbGl0eSwgYW5kIGlzIHdoYXQgd2Ugd2lsbCBiZSB1c2luZyBmb3IgYWxsIApkb3duc3RyZWFtIGFuYWx5c2VzLgoKLS0tCgotLS0gZW5kIGxhYiBzZXNzaW9uIDEgLS0tCgotLS0KCldlIGhhdmUgY3JlYXRlZCBhIApbRHJvcEJveCBmb2xkZXJdKGh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3NoLzN5YzkwNjVyaTc2YjJlNy9BQUFyMHo5WkdwM0FQWi1yZUg2STBER1FhP2RsPTApIApjb250YWluaW5nIGNvbnZlbmllbmNlIGZpbGVzLCBlLmcuLCBhIHByZXByb2Nlc3NlZCBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIHRoYXQgCmlzIHRoZSBlbmQgcmVzdWx0IG9mIGxhYiBzZXNzaW9uIDEuIFlvdSBtYXkgd2FudCB0byBkb3dubGFvZCB0aGVtIGFuZCBpbXBvcnQgdGhlbSBpbiB0aGUgY29kZSBjaHVuayBiZWxvdy4KCmBgYHtyLCBldmFsPUZBTFNFfQpgYGAKCiMgTm9ybWFsaXphdGlvbiAoY29udGludWVkKQoKTm9ybWFsaXphdGlvbiBpcyBuZWNlc3NhcnkgdG8gY29ycmVjdCBmb3Igc2V2ZXJhbCBzb3VyY2VzIG9mIHRlY2huaWNhbCAKdmFyaWF0aW9uOgoKIC0gKipEaWZmZXJlbmNlcyBpbiBzZXF1ZW5jaW5nIGRlcHRoKiogYmV0d2VlbiBzYW1wbGVzLiBTb21lIHNhbXBsZXMgZ2V0IAogc2VxdWVuY2VkIGRlZXBlciBpbiB0aGUgc2Vuc2UgdGhhdCB0aGV5IGNvbnNpc3Qgb2YgbW9yZSAobWFwcGVkKSByZWFkcyBhbmQgCiB0aGVyZWZvcmUgY2FuIGJlIGNvbnNpZGVyZWQgdG8gY29udGFpbiBhIGhpZ2hlciBhbW91bnQgb2YgaW5mb3JtYXRpb24sIHdoaWNoIAogd2Ugc2hvdWxkIGJlIHRha2luZyBpbnRvIGFjY291bnQuIEluIGFkZGl0aW9uLCBpZiBhIHNhbXBsZSBpcyBzZXF1ZW5jZWQgZGVlcGVyLCAKIGl0IGlzIG5hdHVyYWwgdGhhdCB0aGUgY291bnRzIGZvciBlYWNoIGdlbmUgd2lsbCBiZSBoaWdoZXIsIGplb3BhcmRpemluZyBhIAogZGlyZWN0IGNvbXBhcmlzb24gb2YgdGhlIGV4cHJlc3Npb24gY291bnRzLgogLSAqKkRpZmZlcmVuY2VzIGluIFJOQSBwb3B1bGF0aW9uIGNvbXBvc2l0aW9uKiogYmV0d2VlbiBzYW1wbGVzLiBBcyBhbiBleHRyZW1lIAogZXhhbXBsZSwgc3VwcG9zZSB0aGF0IHR3byBzYW1wbGVzIGhhdmUgYmVlbiBzZXF1ZW5jZWQgdG8gdGhlIGV4YWN0IHNhbWUgZGVwdGguCiBPbmUgc2FtcGxlIGlzIGNvbnRhbWluYXRlZCBhbmQgaGFzIGEgdmVyeSBoaWdoIGNvbmNlbnRyYXRpb24gb2YgdGhlIAogY29udGFtaW5hbnQgY0ROQSBiZWluZyBzZXF1ZW5jZWQsIGJ1dCBvdGhlcndpc2UgdGhlIHR3byBzYW1wbGVzIGFyZSBpZGVudGljYWwuIAogU2luY2UgdGhlIGNvbnRhbWluYW50IHdpbGwgYmUgdGFraW5nIHVwIGEgc2lnbmlmaWNhbnQgcHJvcG9ydGlvbiBvZiB0aGUgcmVhZHMgCiBiZWluZyBzZXF1ZW5jZWQsIHRoZSBjb3VudHMgd2lsbCBub3QgYmUgZGlyZWN0bHkgY29tcGFyYWJsZSBiZXR3ZWVuIHRoZSAKIHNhbXBsZXMuIEhlbmNlLCB3ZSBtYXkgYWxzbyB3YW50IHRvIGNvcnJlY3QgZm9yIGRpZmZlcmVuY2VzIGluIHRoZSBjb21wb3NpdGlvbiAKIG9mIHRoZSBSTkEgcG9wdWxhdGlvbiBvZiB0aGUgc2FtcGxlcwogKHNlZSBbZWRnZVIgbWFudWFsIGNoYXB0ZXIgMi44XShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvZWRnZVIvaW5zdC9kb2MvZWRnZVJVc2Vyc0d1aWRlLnBkZikpLgogLSAqKk90aGVyIHRlY2huaWNhbCB2YXJpYXRpb24qKiBzdWNoIGFzIHNhbXBsZS1zcGVjaWZpYyBHQy1jb250ZW50IG9yIAogdHJhbnNjcmlwdCBsZW5ndGggZWZmZWN0cyBtYXkgYWxzbyBiZSBhY2NvdW50ZWQgZm9yLgoKYGBge3IsIGV2YWw9RkFMU0V9CnRhYmxlKHNjZSRjbHVzdGVyKQpgYGAKCkJlbG93LCB3ZSB3aWxsIHZpc3VhbGl6ZSB0aGUgbm9ybWFsaXphdGlvbiBvY2N1cnJpbmcgYmV0d2VlbiB0d28gY2VsbHMgb2YgdGhlIApzYW1lIGNlbGwgdHlwZSAod2hpY2ggY291bGQgYmUgY29uc2lkZXJlZCB0ZWNobmljYWwgcmVwZWF0cyk6IAoKYGBge3IsIGV2YWw9RkFMU0V9CnNlbGVjdCA8LSBzY2UkY2x1c3RlciA9PSAiMSIKc2VsZWN0W2lzLm5hKHNlbGVjdCldIDwtIEZBTFNFCmNzIDwtIGNvbFN1bXMoYXNzYXlzKHNjZSkkY291bnRzWyxzZWxlY3RdKQpjc1tvcmRlcihjcywgZGVjcmVhc2luZyA9IFRSVUUpXVtjKDEsMTApXQpgYGAKCkxldOKAmXMgdGFrZSBhIGxvb2sgYXQgaG93IGNvbXBhcmFibGUgdHdvIGNlbGxzIChyZXBsaWNhdGVzKSBvZiBjbHVzdGVyIDEgYXJlLiAKV2Ugd2lsbCBjb21wYXJlIHRoZSBjZWxsIHdpdGggdGhlIGhpZ2hlc3QgbGlicmFyeSBzaXplIHdpdGggdGhlIGNlbGwgdGhhdCBoYXMgCnRoZSAxMHRoIGhpZ2hlc3QgbGlicmFyeSBzaXplIHVzaW5nIE1ELXBsb3RzIChtZWFuLWRpZmZlcmVuY2UgcGxvdHMsIGFzIAppbnRyb2R1Y2VkIGJ5IApbRHVkb2l0IGV0IGFsLiAoMjAwMildKGh0dHBzOi8vd3d3LmpzdG9yLm9yZy9zdGFibGUvMjQzMDcwMzg/c2VxPTEjbWV0YWRhdGFfaW5mb190YWJfY29udGVudHMpKSwgCmFsc28gc29tZXRpbWVzIHJlZmVycmVkIHRvIGFzIE1BLXBsb3RzLgoKYGBge3IsIGV2YWw9RkFMU0V9CnRhcmdldENlbGxzIDwtIG5hbWVzKGNzW29yZGVyKGNzLCBkZWNyZWFzaW5nID0gVFJVRSldW2MoMSwxMCldKQoKTSA8LSByb3dNZWFucyhhc3NheXMoc2NlKSRjb3VudHNbLHRhcmdldENlbGxzXSkKRCA8LSBhc3NheXMoc2NlKSRjb3VudHNbLHRhcmdldENlbGxzWzJdXSAvIGFzc2F5cyhzY2UpJGNvdW50c1ssdGFyZ2V0Q2VsbHNbMV1dCnBsb3QoeCA9IGxvZyhNKSwgeSA9IGxvZzIoRCksCiAgICAgcGNoID0gMTYsIGNleD0xLzMsCiAgICAgbWFpbiA9IHBhc3RlMCgiQ2VsbCAiLCB0YXJnZXRDZWxsc1syXSwgIiB2cyBjZWxsICIsIHRhcmdldENlbGxzWzFdKSwKICAgICB4bGFiID0gIkxvZyBtZWFuIiwgeWxhYiA9ICJMb2cyIGZvbGQtY2hhbmdlIiwKICAgICBidHkgPSAnbCcpCmFibGluZShoID0gMCwgY29sPSJvcmFuZ2UiLCBsd2Q9MikKCmBgYAoKV2Ugc2VlIGNsZWFyIGJpYXMgaW4gdGhlIGNvbXBhcmlzb24gb2YgdGhlIDFzdCBhbmQgMTB0aCBtb3N0IGRlZXBseSBzZXF1ZW5jZWQgCmNlbGwgZnJvbSBjZWxsIGNsdXN0ZXIgMS4gV2Ugc2VlIHRoYXQgdGhlIGxvZyBmb2xkLWNoYW5nZXMgYXJlIGJpYXNlZCBkb3dud2FyZHMuIApUaGlzIG1lYW5zIHRoYXQsIG9uIGF2ZXJhZ2UsIGEgZ2VuZSBpcyBoaWdoZXIgZXhwcmVzc2VkIGluIGNlbGwgMSB2ZXJzdXMgY2VsbCAxMC4KTG9va2luZyBhdCB0aGUgbGlicmFyeSBzaXplcywgd2UgY2FuIGluZGVlZCBzZWUgdGhhdCB0aGUgbGlicmFyeSBzaXplIGZvciAKY2VsbCAxIGlzIDIwODY5IGNvdW50cywgd2hpbGUgaXQgaXMgb25seSAxNDcxOSBjb3VudHMgZm9yIGNlbGwgMTAhIFRoaXMgaXMgYQpjbGVhciBsaWJyYXJ5IHNpemUgZWZmZWN0IHRoYXQgd2Ugc2hvdWxkIHRha2UgaW50byBhY2NvdW50LgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgbm9ybWFsaXplIHRoZSBjb3VudCBkYXRhIHVzaW5nIHRoZSBwcmV2aW91c2x5IGNvbXB1dGVkICJzaXplIGZhY3RvcnMiCmFzc2F5KHNjZSwgIm5vcm1lZCIpIDwtIG5vcm1hbGl6ZUNvdW50cyhzY2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZS5mYWN0b3JzPXNmLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBzZXVkby5jb3VudD0wKQoKTSA8LSByb3dNZWFucyhhc3NheXMoc2NlKSRub3JtZWRbLHRhcmdldENlbGxzXSkKRCA8LSBhc3NheXMoc2NlKSRub3JtZWRbLHRhcmdldENlbGxzWzJdXSAvIGFzc2F5cyhzY2UpJG5vcm1lZFssdGFyZ2V0Q2VsbHNbMV1dCnBsb3QoeCA9IGxvZyhNKSwgeSA9IGxvZzIoRCksCiAgICAgcGNoID0gMTYsIGNleD0xLzMsCiAgICAgbWFpbiA9IHBhc3RlMCgiQ2VsbCAiLCB0YXJnZXRDZWxsc1syXSwgIiB2cyBjZWxsICIsIHRhcmdldENlbGxzWzFdKSwKICAgICB4bGFiID0gIkxvZyBtZWFuIiwgeWxhYiA9ICJMb2cyIGZvbGQtY2hhbmdlIiwKICAgICBidHkgPSAnbCcpCmFibGluZShoID0gMCwgY29sPSJvcmFuZ2UiLCBsd2Q9MikKYGBgCgpVcG9uIG5vcm1hbGl6aW5nIHRoZSBkYXRhIHVzaW5nIHNpemUgZmFjdG9ycywgd2UgaGF2ZSByZW1vdmVkIGJpYXMgYXMgYQpjb25zZXF1ZW5jZSBvZiBkaWZmZXJlbmNlcyBpbiBzZXF1ZW5jaW5nIGRlcHRoLgoKTm90ZSB0aGF0IHdlIGNvbXB1dGVkIHRoZSBub3JtYWxpemVkIGNvdW50IG1hdHJpeCBvbmx5IGZvciBkZW1vbnN0cmF0aW5nIHRoZQplZmZlY3Qgb2Ygc3VjaCBub3JtYWxpemF0aW9uLiBJbiBhIHR5cGljYWwgd29ya2Zsb3csIHRoZSBzaXplIGZhY3RvcnMgYXJlIHVzZWQKYXMgb2Zmc2V0cyBpbiB0aGUgZG93bnN0cmVhbSBtb2RlbHMgcmF0aGVyIHRoYW4gdG8gcGVyZm9ybSBkYXRhIHRyYW5zZm9ybWF0aW9uLgpJbiBicmllZiwgc3VjaCB0cmFuc2Zvcm1hdGlvbiB3b3VsZCBkaXN0b3J0IHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcAppbiB0aGUgZGF0YS4gRm9yIG1vcmUgZGV0YWlscywgd2UgcmVmZXIgdG8gdGhlIGRvY3VtZW50IHRoYXQgd2FzIHRvdWNoZWQgdXBvbgppbiB0aGUgZmlyc3QgdGhlb3J5IHNlc3Npb24KKFtsaW5rXShodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8vU0dBMjEvc2VxdWVuY2luZ19zY2FsaW5nTm9ybWFsaXphdGlvbi5odG1sKSkKCiMgRmVhdHVyZSBzZWxlY3Rpb24KCkFzIGRpbWVuc2lvbnMgaW5jcmVhc2UsIHNob3J0ZXN0IGFuZCBmYXJ0aGVzdCBkaXN0YW5jZXMgYmV0d2VlbiBwb2ludHMgYmVjb21lCm5lYXJseSBpbnNlcGFyYWJsZS4gSW4gaGlnaC1kaW1lbnNpb25hbCBzcGFjZSwgaXQgaXMgdGhlcmVmb3JlIGV4dHJlbWVseSAKZGlmZmljdWx0IHRvIHNlcGFyYXRlIHNpZ25hbCBmcm9tIG5vaXNlLgoKSW4gb3JkZXIgdG8gcmVjb3ZlciBzdHJ1Y3R1cmUgKGUuZy4gc2V0dGluZyB1cCBhIGRpbWVuc2lvbi1yZWR1Y2VkCnNwYWNlIHRvIGhlbHAgdXMgZmluZCBjZWxsLXR5cGUgY2x1c3RlcnMgaW4gdGhlIGRhdGEpLCB3ZSB3YW50IHRvIG1vdmUgdG8gYW4KaW5mb3JtYXRpdmUsIGxvd2VyLWRpbWVuc2lvbmFsIHNwYWNlLiBXZSB3aWxsIHNlbGVjdCBnZW5lcyB3aGljaCB3ZSBob3BlIGFyZQppbmZvcm1hdGl2ZSBmb3IgcmVjb3ZlcmluZyB0aGUgYmlvbG9naWNhbCBzdHJ1Y3R1cmUuIEJ1dCB3aGF0IGRlZmluZXMgYW4gCmluZm9ybWF0aXZlIGdlbmU/CgojIyBTZWxlY3RpbmcgZ2VuZXMgd2l0aCBoaWdoIHZhcmlhbmNlCgpUaGUgc2ltcGxlc3QgYXBwcm9hY2ggdG8gcXVhbnRpZnlpbmcgcGVyLWdlbmUgdmFyaWF0aW9uIGlzIHRvIGNvbXB1dGUgdGhlIAp2YXJpYW5jZSBvZiB0aGUgbG9nLW5vcm1hbGl6ZWQgZXhwcmVzc2lvbiB2YWx1ZXMgKGkuZS4sICJsb2ctY291bnRzIikgZm9yIAplYWNoIGdlbmUgYWNyb3NzIGFsbCBjZWxscy4gCgpgYGB7cixldmFsPUZBTFNFfQojIGNhbGN1bGF0ZSB2YXJpYW5jZSBvZiBsb2ctbm9ybWFsaXplZCBjb3VudHMgZm9yIGVhY2ggZ2VuZQpnZW5lVmFycyA8LSBnZW5lZmlsdGVyOjpyb3dWYXJzKC4uLikKIyBzZWxlY3QgdG9wIDEwMDAgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCmhpZ2hWYXJHZW5lcyA8LSBuYW1lcyhnZW5lVmFycylbb3JkZXIoZ2VuZVZhcnMsIGRlY3JlYXNpbmc9VFJVRSlbMToxZTNdXQpoZWFkKGhpZ2hWYXJHZW5lcykKYGBgCgojIyBTZWxlY3RpbmcgZ2VuZXMgd2l0aCBoaWdoIHZhcmlhdGlvbiB3aXRoIHJlc3BlY3QgdG8gbWVhbgoKV2hpbGUgY2FsY3VsYXRpb24gb2YgdGhlIHBlci1nZW5lIHZhcmlhbmNlIGlzIHNpbXBsZSwgZmVhdHVyZSBzZWxlY3Rpb24gcmVxdWlyZXMKbW9kZWxsaW5nIG9mIHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcC4gSW5kZWVkLCBub3QgYWNjb3VudGluZyBmb3IgdGhlIAptZWFuLXZhcmlhbmNlIHN0cnVjdHVyZSB3aGlsZSBzZWxlY3RpbmcgaGlnaGx5IHZhcmlhYmxlIGdlbmVzIHdpbGwgb2Z0ZW50aW1lcyAKYm9pbCBkb3duIHRvIHNlbGVjdGluZyB0aGUgbW9zdCBoaWdobHkgZXhwcmVzc2VkIGdlbmVzLiBUaGUgbG9nLXRyYW5zZm9ybWF0aW9uIAppcyBhIGhlbHBmdWwgdmFyaWFuY2Ugc3RhYmlsaXppbmcgdHJhbnNmb3JtYXRpb24sIGhvd2V2ZXIsIGl0IGlzIG5vdCBwZXJmZWN0LCAKbWVhbmluZyB0aGF0IHRoZSB2YXJpYW5jZSBvZiBhIGdlbmUgaXMgbm90IGNvbXBsZXRlbHkgaW5kZXBlbmRlbnQgb2YgaXRzIG1lYW4uClRoZXJlZm9yZSwgZmVhdHVyZSBzZWxlY3Rpb24gbWF5IHN0aWxsIGJlIGRyaXZlbiBieSBhdmVyYWdlIGV4cHJlc3Npb24gcmF0aGVyIAp0aGFuIHVuZGVybHlpbmcgYmlvbG9naWNhbCBoZXRlcm9nZW5laXR5LgoKQSAocmlnaHRseSBzbykgcG9wdWxhciBhcHByb2FjaCBpcyB0byBzZWxlY3QgZ2VuZXMgdGhhdCBoYXZlIGEgaGlnaCB2YXJpYW5jZQp3aXRoIHJlc3BlY3QgdG8gdGhlaXIgbWVhbi4gT2Z0ZW4sIGZpcnN0IGFuIGVtcGlyaWNhbCBtZWFuLXZhcmlhbmNlIHRyZW5kIGlzCmZpdHRlZCwgdXBvbiB3aGljaCBnZW5lcyB3aXRoIHRoZSBoaWdoZXN0IHBvc2l0aXZlIHJlc2lkdWFscyBhcmUgc2VsZWN0ZWQuIApCZWluZyBpbnR1aXRpdmUsIHJlYXNvbmFibGUgYW5kIGZhaXJseSBzdHJhaWdodC1mb3J3YXJkLCB0aGlzIG1ldGhvZCBpcyB3aWRlbHkKdXNlZC4KClRvIGFjY291bnQgZm9yIHRoZSBtZWFuLXZhcmlhbmNlIGVmZmVjdCwgd2UgdXNlIHRoZSBgbW9kZWxHZW5lVmFyYCBmdW5jdGlvbiBvZgp0aGUgYHNjcmFuYCBwYWNrYWdlIHRvIGZpdCBhIHRyZW5kIHRvIHRoZSB2YXJpYW5jZSB3aXRoIHJlc3BlY3QgdG8gYWJ1bmRhbmNlCmFjcm9zcyBhbGwgZ2VuZXMgKG9uIGxvZy1ub3JtYWxpemVkIGV4cHJlc3Npb24gdmFsdWVzIG9mIHRoZSBzY2Ugb2JqZWN0KS4KCmBgYHtyLGV2YWw9RkFMU0V9CmxpYnJhcnkoc2NyYW4pCiMgYG1vZGVsR2VuZVZhcmAgZnVuY3Rpb24gZGVzY3JpcHRpb246IE1vZGVsIHRoZSB2YXJpYW5jZSBvZiB0aGUgbG9nLWV4cHJlc3Npb24gCiMgcHJvZmlsZXMgZm9yIGVhY2ggZ2VuZSwgZGVjb21wb3NpbmcgaXQgaW50byB0ZWNobmljYWwgYW5kIGJpb2xvZ2ljYWwgCiMgY29tcG9uZW50cyBiYXNlZCBvbiBhIGZpdHRlZCBtZWFuLXZhcmlhbmNlIHRyZW5kLgpkZWMgPC0gbW9kZWxHZW5lVmFyKC4uLikgIyBpbnB1dCBhIHNjZSBvYmplY3Qgd2l0aCBwcmVjb21wdXRlZCBsb2djb3VudHMgYXNzYXkKaGVhZChkZWMpCmBgYAoKVGhlIGZpdHRlZCB2YWx1ZSBmb3IgZWFjaCBnZW5lIGlzIHVzZWQgYXMgYSBwcm94eSBmb3IgdGhlIHRlY2huaWNhbCBjb21wb25lbnQgb2YKdmFyaWF0aW9uIGZvciBlYWNoIGdlbmUsIHVuZGVyIHRoZSBhc3N1bXB0aW9uIHRoYXQgbW9zdCBnZW5lcyBleGhpYml0IGEgbG93CmJhc2VsaW5lIGxldmVsIG9mIHZhcmlhdGlvbiB0aGF0IGlzIG5vdCBiaW9sb2dpY2FsbHkgaW50ZXJlc3RpbmcuIFRoZSBiaW9sb2dpY2FsCmNvbXBvbmVudCBvZiB2YXJpYXRpb24gZm9yIGVhY2ggZ2VuZSBpcyBkZWZpbmVkIGFzIHRoZSB0aGUgcmVzaWR1YWwgZnJvbSB0aGUKdHJlbmQuIFJhbmtpbmcgZ2VuZXMgYnkgdGhlIGJpb2xvZ2ljYWwgY29tcG9uZW50IGVuYWJsZXMgaWRlbnRpZmljYXRpb24gb2YKaW50ZXJlc3RpbmcgZ2VuZXMgZm9yIGRvd25zdHJlYW0gYW5hbHlzZXMgaW4gYSBtYW5uZXIgdGhhdCBhY2NvdW50cyBmb3IgdGhlCm1lYW4tdmFyaWFuY2UgcmVsYXRpb25zaGlwLgoKYGBge3IsZXZhbD1GQUxTRX0KZml0UmV0aW5hIDwtIG1ldGFkYXRhKC4uLikKcGxvdCh4ID0gLi4uLCAjIHRoZSBmaXR0ZWQgbWVhbnMgb24gdGhlIHgtYXhpcyAKICAgICB5ID0gLi4uLCAjIHRoZSBmaXR0ZWQgdmFyaWFuY2VzIG9uIHRoZSB5LWF4aXMKICAgICB4bGFiPSJNZWFuIG9mIGxvZy1leHByZXNzaW9uIiwKICAgIHlsYWI9IlZhcmlhbmNlIG9mIGxvZy1leHByZXNzaW9uIikKY3VydmUoZml0UmV0aW5hJHRyZW5kKHgpLCBjb2w9ImRvZGdlcmJsdWUiLCBhZGQ9VFJVRSwgbHdkPTIpICMgYWRkcyBhIHRyZW5kIGxpbmUKYGBgCgpXZSBhcmUgaW50ZXJlc3RlZCBpbiB0aG9zZSBnZW5lcyBmb3Igd2hpY2ggdGhlIHZhcmlhbmNlIGluIGV4cHJlc3Npb24gaXMgaGlnaGVyCnRoYW4gd2hhdCB3ZSB3b3VsZCBleHBlY3QgZm9yIHRoYXQgZ2VuZSBiYXNlZCBvbiBpdHMgbWVhbiBleHByZXNzaW9uLgoKYGBge3IsZXZhbD1GQUxTRX0KIyBnZXQgMTAlIG1vc3QgdmFyaWFibGUgZ2VuZXMKIyBCYXNlZCBvbiB0aGUgb3V0cHV0IHN0YXRpc3RpY3Mgb2YgbW9kZWxHZW5lVmFyLCBzZWxlY3QgdGhlIDEwJSBtb3N0IHZhcmlhYmxlIGdlbmVzCmh2ZyA8LSBnZXRUb3BIVkdzKHN0YXRzID0gLi4uLCAKICAgICAgICAgICAgICAgICAgcHJvcCA9IC4uLikgIyBwZXJjZW50YWdlIG9mIHRvcCB2YXJpYWJsZSBnZW5lcy4gQWx0ZXJuYXRpdmVseSwKIyBuID0gLi4uIGNvdWxkIGJlIHVzZWQgdG8gc2VsZWN0IHRoZSB0b3AgbnVtYmVyIG9mIHZhcmlhYmxlIGdlbmVzCmhlYWQoaHZnKQoKIyBwbG90IHRoZXNlIApwbG90KHggPSAuLi4sICMgdGhlIGZpdHRlZCBtZWFucyBvbiB0aGUgeC1heGlzIAogICAgIHkgPSAuLi4sICMgdGhlIGZpdHRlZCB2YXJpYW5jZXMgb24gdGhlIHktYXhpcwogICAgIGNvbCA9IGMoIm9yYW5nZSIsICJkYXJrc2VhZ3JlZW4zIilbKG5hbWVzKGZpdFJldGluYSRtZWFuKSAlaW4lIGh2ZykrMV0sCiAgICAgeGxhYj0iTWVhbiBvZiBsb2ctZXhwcmVzc2lvbiIsCiAgICB5bGFiPSJWYXJpYW5jZSBvZiBsb2ctZXhwcmVzc2lvbiIpCmN1cnZlKGZpdFJldGluYSR0cmVuZCh4KSwgY29sPSJkb2RnZXJibHVlIiwgYWRkPVRSVUUsIGx3ZD0yKQpsZWdlbmQoInRvcGxlZnQiLCAKICAgICAgIGxlZ2VuZCA9IGMoIlNlbGVjdGVkIiwgIk5vdCBzZWxlY3RlZCIpLCAKICAgICAgIGNvbCA9IGMoImRhcmtzZWFncmVlbjMiLCAib3JhbmdlIiksCiAgICAgICBwY2ggPSAxNiwKICAgICAgIGJ0eT0nbicpCmBgYAoKQXMgYSBjb21wYXJpc29uLCB3ZSBjb3VsZCBjb2xvciB0aGUgZ2VuZXMgb24gdGhpcyBmaWd1cmUgYWNjb3JkaW5nIHRvIHRoZSAKc2VsZWN0aW9uIHRoYXQgd2FzIG1hZGUgcHVyZWx5IGJ5IGxvb2tpbmcgYXQgdGhlIHJhdyB2YXJpYW5jZSBvZiBlYWNoIGdlbmUuCgpgYGB7cixldmFsPUZBTFNFfQouLi4KYGBgCgpBcyBleHBlY3RlZCwgd2UgaGVyZSBzaW1wbHkgc2VsZWN0IGdlbmVzIHdpdGggYSBoaWdoIHZhcmlhbmNlLCB3aXRob3V0IApyZWNvZ25pemluZyB0aGF0IGEgaGlnaCB2YXJpYW5jZSBpcyB0eXBpY2FsbHkgZHJpdmVuIGJ5IGEgaGlnaCBtZWFuLgoKIyMgSGlnaCBkZXZpYW5jZSBnZW5lcwoKSGVyZSwgd2Ugd2lsbCBzZWxlY3QgZ2VuZXMgd2l0aCBhIGhpZ2ggcmVzaWR1YWwgZGV2aWFuY2UuIFRoZSBpZGVhIGlzIHRoYXQgd2UgCmFzc3VtZSBhIG51bGwgbW9kZWwgb2YgY29uc3RhbnQgZXhwcmVzc2lvbiBmcmFjdGlvbiAoaS5lLiwgUk5BIGNvbmNlbnRyYXRpb24pIAphY3Jvc3MgYWxsIGNlbGxzLiBXZSBzdWJzZXF1ZW50bHkgY2FsY3VsYXRlIGEgZ29vZG5lc3Mtb2YtZml0IHN0YXRpc3RpYyBmb3IgCmVhY2ggZ2VuZSwgYXNzZXNzaW5nIHdoZXRoZXIgdGhlIG1vZGVsIGlzIGEgZ29vZCBhcHByb3hpbWF0aW9uIHRvIHRoZSBnZW5lIApleHByZXNzaW9uIG9mIHRoZSBjb3JyZXNwb25kaW5nIGdlbmUuCgpJZiB0aGUgbW9kZWwgZml0cyBwb29ybHksIGkuZS4sIHRoZSBnZW5lIGhhcyBhIGhpZ2ggZGV2aWFuY2UsIHRoZSBleHByZXNzaW9uIApmcmFjdGlvbiB2YXJpZXMgc2lnbmlmaWNhbnRseSBhY3Jvc3MgdGhlIGNlbGxzIGluIG91ciBkYXRhc2V0cy5HZW5lcyB3aXRoIGEgCmhpZ2ggZGV2aWFuY2Ugd2lsbCB0aHVzIG1vc3QgcG9vcmx5IGZpdCBhIG51bGwgbW9kZWwgd2hlcmUgdGhlIHJlbGF0aXZlCmFidW5kYW5jZSBpcyBlcXVhbCBmb3IgYWxsIGNlbGxzLCB3aGljaCB0aGVyZWZvcmUgYXJlIGluZm9ybWF0aXZlLgoKYGBge3IsIGV2YWw9RkFMU0V9CiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgic2NyeSIpCmxpYnJhcnkoc2NyeSkKIyBgZGV2aWFuY2VGZWF0dXJlU2VsZWN0aW9uYCBmdW5jdGlvbiBkZXNjcmlwdGlvbjogQ29tcHV0ZXMgYSBkZXZpYW5jZSBzdGF0aXN0aWMgCiMgZm9yIGVhY2ggcm93IGZlYXR1cmUgKHN1Y2ggYXMgYSBnZW5lKSBmb3IgY291bnQgZGF0YSBiYXNlZCBvbiBhIG11bHRpbm9taWFsIAojIG51bGwgbW9kZWwgWy4uLl0uIApzY2UgPC0gZGV2aWFuY2VGZWF0dXJlU2VsZWN0aW9uKG9iamVjdCA9IC4uLiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAuLi4pICMgY29tcHV0ZWQgb24gcmF3IGNvdW50cwoKcGxvdChzb3J0KHJvd0RhdGEoc2NlKSRiaW5vbWlhbF9kZXZpYW5jZSwgZGVjcmVhc2luZyA9IFRSVUUpLCAKICAgICB0eXBlPSJsIiwgCiAgICAgeGxhYj0icmFua2VkIGdlbmVzIiwgCiAgICAgeWxhYj0iYmlub21pYWwgZGV2aWFuY2UiLCAKICAgICBtYWluPSJGZWF0dXJlIFNlbGVjdGlvbiB3aXRoIERldmlhbmNlIikKYWJsaW5lKHY9MjAwMCwgbHR5PTIsIGNvbD0icmVkIikKYGBgCgpPdXIgcGxvdCBsb29rcyBzaW1pbGFyIHRvIG9uZSBkaXNwbGF5ZWQgaW4gdGhlIApbdmlnbmV0dGUgb2YgdGhlIHNjcnkgcGFja2FnZV0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL3NjcnkvaW5zdC9kb2Mvc2NyeS5odG1sKS4gCkJhc2VkIG9uIHRoYXQgcGxvdCwgdGhlIGF1dGhvcnMgc3VnZ2VzdCByZXRhaW5pbmcgMi4wMDAgZ2VuZXMgKHRoZSB0b3AgMjAwMCAKYmFzZWQgb24gdGhlIGRldmlhbmNlIHJlc2lkdWFscykgZm9yIGRvd25zdHJlYW0gZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIGFuZCAKY2x1c3RlcmluZy4KCiMjIFNldXJhdCBWU1QKCkFub3RoZXIgdmVyeSBjb21tb24gZmVhdHVyZSBzZWxlY3Rpb24gc3RyYXRlZ3kgaXMgdGhlIHZhcmlhbmNlLXN0YWJpbGl6aW5nCnRyYW5zZm9ybWF0aW9uIGZyb20gdGhlIGBTZXVyYXRgIFIgcGFja2FnZSwgd2hpY2ggYW1vdW50cyB0byBjYWxjdWxhdGluZyBQZWFyc29uCnJlc2lkdWFscyBmcm9tIGEgcmVndWxhcml6ZWQgbmVnYXRpdmUgYmlub21pYWwgcmVncmVzc2lvbiBtb2RlbCwgd2l0aCBzZXF1ZW5jaW5nIApkZXB0aCBhcyBhIGNvdmFyaWF0ZS4KCioqSW50ZXJtZXp6bzogaW50ZXJvcGVyYWJpbGl0eSBiZXR3ZWVuIFNpbmdsZUNlbGxFeHBlcmltZW50IGFuZCBTZXVyYXQgb2JqZWN0cyoqCgpJbiB0aGlzIGxlY3R1cmUgc2VyaWVzLCB3ZSBhbHdheXMgbWFrZSB1c2Ugb2YgdGhlIFNpbmdsZUNlbGxFeHBlcmltZW50IG9iamVjdAphbmQgdGhlIHBhY2thZ2VzIGF2YWlsYWJsZSBmcm9tIEJpb2NvbmR1Y3Rvci4gQW5vdGhlciB2ZXJ5IHBvcHVsYXIgdG9vbGJveApmb3IgcGVyZm9ybWluZyBzY1JOQS1zZXEgZGF0YSBhbmFseXNpcyBpcyBgU2V1cmF0YC4gSG93ZXZlciwgZnVuY3Rpb25zIGZyb20KYFNldXJhdGAgY2Fubm90IGJlIHVzZWQgZGlyZWN0bHkgdG8gbWFuaXB1bGF0ZSBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIG9iamVjdHMsCmFuZCB2aWNlIHZlcnNhLiBGb3J0dW5hdGVseSwgZWZmb3J0cyBoYXZlIGJlZW4gbWFkZSB0byBpbmNyZWFzZSB0aGUKaW50ZXJvcGVyYWJpbGl0eSBiZXR3ZWVuIHRoZSB0d28gdG9vbGJveGVzLgoKYGBge3IsIGV2YWw9RkFMU0V9CmxpYnJhcnkoU2V1cmF0KQpzZXVyYXRfb2JqIDwtIGFzLlNldXJhdCguLi4pICMgc2NlIG9iamVjdApzZXVyYXRfb2JqICMgbm90aWNlIHRoZSAiMCB2YXJpYWJsZSBmZWF0dXJlcyIKYGBgCgpPbiB0aGlzIG9iamVjdCwgd2UgbWF5IHVzZSBmdW5jdGlvbnMgZnJvbSB0aGUgYFNldXJhdGAgdG9vbGJveC4KRm9yIGluc3RhbmNlLCB3ZSBtYXkgc2VhcmNoIGZvciBoaWdobHkgdmFyaWFibGUgZmVhdHVyZXMgdXNpbmcgYFNldXJhdGAncyBWU1QKaW1wbGVtZW50YXRpb246CgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBQcmlvciB0byBkZXRlY3RpbmcgdmFyaWFibGUgZmVhdHVyZXMgdXNpbmcgVlNULCBTZXVyYXQgbm9ybWFsaXplcyB0aGUgZGF0YS4KIyBUaGUgYE5vcm1hbGl6ZURhdGFgIGZ1bmN0aW9uIHRha2VzIGFzIGlucHV0IHRoZSBTZXVyYXQgb2JqZWN0LCBhbmQgdXNlcyAKIyBsb2cgbm9ybWFsaXphdGlvbiBhbmQgYSBzY2FsZSBmYWN0b3Igb2YgMTAwMDAgYXMgZGVmYXVsdCAoc2VlIGhlbHAgZmlsZSkuCnNldXJhdF9vYmogPC0gU2V1cmF0OjpOb3JtYWxpemVEYXRhKG9iamVjdCA9IC4uLiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gLi4uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUuZmFjdG9yID0gLi4uKQoKIyBOZXh0LCBwZXJmb3JtIGZlYXR1cmUgc2VsZWN0aW9uIGJhc2VkIG9uIFZTVApzZXVyYXRfb2JqIDwtICBGaW5kVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSAuLi4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdGlvbi5tZXRob2QgPSAuLi4pCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0Kc2V1cmF0X29iaiAgIyBub3RpY2UgdGhlICIyMDAwIHZhcmlhYmxlIGZlYXR1cmVzIiAoZGVmYXVsdCkKaGVhZChWYXJpYWJsZUZlYXR1cmVzKHNldXJhdF9vYmopKSAjIGhlcmUgdGhleSBhcmUKYGBgCgojIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbgoKTm90ZSB0aGF0LCBiZWxvdywgd2UgY29sb3IgdGhlIGNlbGxzIHVzaW5nIHRoZSBrbm93biwgdHJ1ZSBjZWxsIHR5cGUgbGFiZWwgYXMgCmRlZmluZWQgaW4gdGhlIG1ldGFkYXRhLCB0byBlbXBpcmljYWxseSBldmFsdWF0ZSB0aGUgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uLiAKSW4gcmVhbGl0eSwgd2UgZG9uJ3Qga25vdyB0aGlzIHlldCBhdCB0aGlzIHN0YWdlLgoKIyMgVGhlIG1vc3QgYmFzaWMgRFIKCkp1c3QgYnkgbG9va2luZyBhdCB0aGUgdG9wIHR3byBnZW5lcyBiYXNlZCBvbiBvdXIgZmVhdHVyZSBzZWxlY3Rpb24gY3JpdGVyaW9uLCAKd2UgY2FuIGFscmVhZHkgc2VlIHNvbWUgc2VwYXJhdGlvbiBhY2NvcmRpbmcgdG8gdGhlIGNlbGwgdHlwZSEKCmBgYHtyLGV2YWw9RkFMU0V9CmNvbERhdGEoc2NlKSRjbHVzdGVyIDwtIGFzLmZhY3Rvcihjb2xEYXRhKHNjZSkkY2x1c3RlcikKY2wgPC0gY29sRGF0YShzY2UpJGNsdXN0ZXIKcGFyKGJ0eT0nbCcpCnBsb3QoeCA9IC4uLiwgIyBleHRyYWN0IHRoZSBjb3VudHMgZm9yIHRoZSBtb3N0IHZhcmlhYmxlIGdlbmUKICAgICB5ID0gLi4uLCAjIGV4dHJhY3QgdGhlIGNvdW50cyBmb3IgdGhlIHNlY29uZCBtb3N0IHZhcmlhYmxlIGdlbmUKICAgICBjb2wgPSBhcy5udW1lcmljKGNsKSwKICAgICBwY2ggPSAxNiwgY2V4ID0gMS8zLAogICAgIHhsYWIgPSAiTW9zdCBpbmZvcm1hdGl2ZSBnZW5lIiwKICAgICB5bGFiID0gIlNlY29uZCBtb3N0IGluZm9ybWF0aXZlIGdlbmUiLAogICAgIG1haW4gPSAiQ2VsbHMgY29sb3JlZCBhY2MgdG8gY2VsbCB0eXBlIikKYGBgCgpXZSBhcmUgYWJsZSB0byByZWNvdmVyIHF1aXRlIHNvbWUgc3RydWN0dXJlLiBIb3dldmVyLCBtYW55IGNlbGwgcG9wdWxhdGlvbnMgCnJlbWFpbiBvYnNjdXJlLCBhbmQgdGhlIHBsb3QgaXMgb3ZlcmNyb3dkZWQuCgojIyBMaW5lYXIgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uOiBQQ0EKCkEgRFIgbWV0aG9kIGlzIGxpbmVhciB3aGVuIHRoZSByZWR1Y2VkIGRpbWVuc2lvbnMgYXJlIGEgbGluZWFyIGZ1bmN0aW9uIG9mIHRoZQpvcmlnaW5hbCB2YXJpYWJsZXMuIEZvciBleGFtcGxlLCBpbiBQQ0EsIGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudCBpcyBhIGxpbmVhcgpjb21iaW5hdGlvbiBvZiBnZW5lcywgdGhlcmVmb3JlIHRoZSBEUiBpcyBhIGxpbmVhciBmdW5jdGlvbiBvZiB0aGUgb3JpZ2luYWwKdmFyaWFibGVzLgoKVHlwaWNhbGx5LCBQQ0EgaXMgcGVyZm9ybWVkIG9uIGxvZy10cmFuc2Zvcm1lZCBub3JtYWxpemVkIGNvdW50cy4gClRoZSBsb2ctdHJhbnNmb3JtYXRpb24gaGVscHMgIHNvbWV3aGF0LCBidXQgbm90IGNvbXBsZXRlbHksIHRvIGFjY291bnQgZm9yIHRoZQptZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcC4gUENBIHdvcmtzIHdlbGwgZm9yIGJ1bGsgUk5BLXNlcSBkYXRhLiBIb3dldmVyLCB0aGUKc3RydWN0dXJlIG9mIHNjUk5BLXNlcSBkYXRhIGlzIG9mdGVuIHRvbyBjb21wbGV4IHRvIGJlIHZpc3VhbGl6ZWQgYnkgYSBzbWFsbCAKbnVtYmVyIG9mIFBDcy4KClRoZXJlIGFyZSBzZXZlcmFsIFIgZnVuY3Rpb25zIHRoYXQgYWxsb3cgeW91IHRvIHBlcmZvcm0gUENBLiBIZXJlLCB3ZSBtYWtlIHVzZQpvZiB0aGUgYHJ1blBDQWAgZnVuY3Rpb24gb2YgdGhlIGBzY2F0ZXJgIHBhY2thZ2UsIHdoaWNoIGhhcyBiZWVuIHNwZWNpZmljYWxseQpkZXZlbG9wZWQgZm9yIHBlcmZvcm1pbmcgUENBIG9uIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0cy4KV2UgY2FsY3VsYXRlIHRoZSB0b3AgMzAgcHJpbmNpcGFsIGNvbXBvbmVudHMuCgojIyMgUENBIHdpdGggZmVhdHVyZSBzZWxlY3Rpb24KCmBgYHtyLGV2YWw9RkFMU0V9CiMgUGVyZm9ybSBhIFBDQSBmb3Igb3VyIGRhdGEuIENvbXB1dGUgdGhlIGZpcnN0IDMwIHByaW5jaXBhbCBjb21wb25lbnRzLCBvbmx5CiMgdXNpbmcgdGhlIHNlbGVjdGVkIGZlYXR1cmVzIHdpdGggaGlnaCB2YXJpYW5jZSB3aXRoIHJlc3BlY3QgdG8gdGhlIG1lYW4uCnNldC5zZWVkKDEyMzQpCnNjZSA8LSBydW5QQ0EoLi4uKSAjIHlvdSBuZWVkIHRocmVlIGFyZ3VtZW50cwpgYGAKClBDQSBoYXMgYmVlbiBwZXJmb3JtZWQuIFRoZSBQQ0EgaW5mb3JtYXRpb24gaGFzIGJlZW4gYXV0b21hdGljYWxseSBzdG9yZWQgaW4gdGhlCipyZWR1Y2VkRGltKiBzbG90IG9mIHRoZSBTaW5nbGVDZWxsRXhwZXJpbWVudCBvYmplY3QuCgpgYGB7cixldmFsPUZBTFNFfQpyZWR1Y2VkRGltTmFtZXMoc2NlKQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CmhlYWQocmVkdWNlZERpbShzY2UsCiAgICAgICAgICAgICAgICB0eXBlPSJQQ0EiKSkKYGBgCgpUaGUgYHBsb3RQQ0FgIGZ1bmN0aW9uIG9mIHRoZSBgc2NhdGVyYCBwYWNrYWdlIG5vdyBhbGxvd3MgdXMgdG8gdmlzdWFsaXplCnRoZSBjZWxscyBpbiBQQ0Egc3BhY2UsIGJhc2VkIG9uIHRoZSBQQ0EgaW5mb3JtYXRpb24gc3RvcmVkIGluIG91ciBvYmplY3Q6CgpgYGB7cixldmFsPUZBTFNFfQpwbG90UENBKHNjZSwgCiAgICAgICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKQpgYGAKCldoaWxlIHRoZSBsYXJnZSBudW1iZXIgb2YgY2x1c3RlcnMgaW4gdGhpcyBkYXRhc2V0IG1ha2VzIGl0IGhhcmQgdG8gCmRpc3Rpbmd1aXNoIGJldHdlZW4gYWxsIHRoZSBkaWZmZXJlbnQgY29sb3JzLCB3ZSBjYW4gYWxyZWFkeSBzZWUgdGhhdCBQQ0EKcmV0cmlldmVzIHNvbWUsIGJ1dCBub3QgYWxsLCBvZiB0aGUgc3RydWN0dXJlIGluIHRoZSBkYXRhIHRoYXQgd2FzIGRpc2NvdmVyZWQKYnkgdGhlIG9yaWdpbmFsIGF1dGhvcnMuCgpIb3cgbWFueSBvZiB0aGUgdG9wIFBDcyBzaG91bGQgd2UgcmV0YWluIGZvciBkb3duc3RyZWFtIGFuYWx5c2VzPyBUaGUgY2hvaWNlIG9mCnRoZSBudW1iZXIgb2YgUENzIGlzIGEgZGVjaXNpb24gdGhhdCBpcyBhbmFsb2dvdXMgdG8gdGhlIGNob2ljZSBvZiB0aGUgbnVtYmVyIG9mCkhWR3MgdG8gdXNlLiBVc2luZyBtb3JlIFBDcyB3aWxsIHJldGFpbiBtb3JlIGJpb2xvZ2ljYWwgc2lnbmFsIGF0IHRoZSBjb3N0IG9mCmluY2x1ZGluZyBtb3JlIG5vaXNlIHRoYXQgbWlnaHQgbWFzayBzYWlkIHNpZ25hbC4gT24gdGhlIG90aGVyIGhhbmQsIHVzaW5nIGZld2VyClBDcyB3aWxsIGludHJvZHVjZSBjb21wZXRpdGlvbiBiZXR3ZWVuIGRpZmZlcmVudCBmYWN0b3JzIG9mIHZhcmlhdGlvbiwgd2hlcmUKd2Vha2VyIChidXQgc3RpbGwgaW50ZXJlc3RpbmcpIGZhY3RvcnMgbWF5IGJlIHB1c2hlZCBkb3duIGludG8gbG93ZXIgUENzIGFuZAppbmFkdmVydGVudGx5IGRpc2NhcmRlZCBmcm9tIGRvd25zdHJlYW0gYW5hbHlzZXMuCgpNb3N0IGFuYWx5c3RzIHdpbGwgc2ltcGx5IGFpbSB0byB1c2UgYSDigJxyZWFzb25hYmxl4oCdIGJ1dCBhcmJpdHJhcnkgdmFsdWUsCnR5cGljYWxseSByYW5naW5nIGZyb20gMTAgdG8gNTAuIFRoaXMgaXMgb2Z0ZW4gc2F0aXNmYWN0b3J5IGFzIHRoZSBsYXRlciBQQ3MKZXhwbGFpbiBzbyBsaXR0bGUgdmFyaWFuY2UgdGhhdCB0aGVpciBpbmNsdXNpb24gb3Igb21pc3Npb24gaGFzIG5vIG1ham9yIGVmZmVjdC4KCmBgYHtyLCBldmFsID0gRkFMU0V9CnBlcmNlbnQudmFyIDwtIGF0dHIocmVkdWNlZERpbShzY2UpLCAicGVyY2VudFZhciIpCnBsb3QocGVyY2VudC52YXIsIGxvZz0ieSIsIHhsYWI9IlBDIiwgeWxhYj0iVmFyaWFuY2UgZXhwbGFpbmVkICglKSIpCnBsb3QoY3Vtc3VtKHBlcmNlbnQudmFyKSwgeGxhYj0iUEMiLCB5bGFiPSJDdW11bGF0aXZlIHZhcmlhbmNlIGV4cGxhaW5lZCAoJSkiKQpgYGAKCkhlcmUsIHJldGFpbmluZyDCsTE1UENzIHNlZW1zIHJlYXNvbmFibGUuIElmIHlvdSByZWFsbHkgcHJlZmVyIGEgbW9yZSBkYXRhLWRyaXZlbgp3YXkgZm9yIGRldGVybWluaW5nIHRoaXMsIHRoZXJlIGFyZSBwcm9jZWR1cmVzIGF2YWlsYWJsZSB0aGF0IGFpbSB0byAKY29tcHV0YXRpb25hbGx5IGlkZW50aWZ5IHRoZSBlbGJvdy9rbmVlIHBvaW50IGluIHRoZSB2YXJpYW5jZSBleHBsYWluZWQgcGVyIApQQyBwbG90LgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShQQ0F0b29scykKY2hvc2VuLmVsYm93IDwtIGZpbmRFbGJvd1BvaW50KHBlcmNlbnQudmFyKQpjaG9zZW4uZWxib3cKYGBgCgojIyMgUENBIHdpdGhvdXQgZmVhdHVyZSBzZWxlY3Rpb24KCk5vdGU6IG1vcmUgZmVhdHVyZXMgLT4gY29tcHV0YXRpb25hbGx5IG1vcmUgaW50ZW5zaXZlIQoKYGBge3IsIGV2YWwgPSBGQUxTRX0Kc2V0LnNlZWQoMTIzNCkKc2NlTm9GUyA8LSBydW5QQ0EoeCA9IC4uLiwgCiAgICAgICAgICAgICAgICAgIG5jb21wb25lbnRzID0gLi4uLCAKICAgICAgICAgICAgICAgICAgc3Vic2V0X3JvdyA9IDE6bnJvdyhzY2UpKQpwbG90UENBKC4uLikKcm0oc2NlTm9GUykgIyByZW1vdmUgdGhlIG9iamVjdCwgd2UgZG9uJ3QgbmVlZCBpdCBhbnltb3JlCmBgYAoKV2hpbGUgd2UgdXNlIG1vcmUgaW5mb3JtYXRpb24gdG8gbWFrZSB0aGlzIFBDQSBwbG90ICgxNy44ODcgZ2VuZXMpIGFzIGNvbXBhcmVkCnRvIHRoZSBmZWF0dXJlIHNlbGVjdGVkIFBDQSBwbG90ICg2NDIgZ2VuZXMpLCB3ZSBzZWVtIHRvIHJldHJpZXZlIGxlc3Mgc3RydWN0dXJlCmluIHRoZSBkYXRhLiBUaGlzIGlzIHRoZSBwb3dlciBvZiBmZWF0dXJlIHNlbGVjdGlvbiwgYW4gaW5jcmVhc2UgaW4gdGhlCnNpZ25hbC10by1ub2lzZSByYXRpbyEKCiMjIyBFZmZlY3Qgb2YgZmVhdHVyZSBzZWxlY3Rpb24gb24gUENBCgpGaXJzdCwgd2UgY29tcGFyZSB0aGUgZGlmZmVyZW50IGZlYXR1cmUgc2VsZWN0aW9uIGNyaXRlcmlhLCB1c2luZyB0aGUgdG9wIDEwMDAKaGlnaGx5IHZhcmlhYmxlIGdlbmVzIGZvciBlYWNoIG1ldGhvZC4KCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgR2V0IHRvcCAxMDAwIGhpZ2hseSB2YXJpYWJsZSBmZWF0dXJlcyB1c2luZyB0aGUgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCiMgKHcuci50LiBtZWFuKSwgaGlnaCBkZXZpYW5jZSBnZW5lcyBhbmQgdGhlIFZTVCBzdHJhdGVneSBvZiBTZXVyYXQKCmh2ZzEwMDAgPC0gLi4uICMgZXh0cmFjdCB0b3AgMTAwMCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKaGRnMTAwMCA8LSAuLi4gIyBleHRyYWN0IHRvcCAxMDAwIGdlbmVzIHdpdGggaGlnaCBkZXZpYW5jZQp2c3QxMDAwIDwtIC4uLiAjIGV4dHJhY3QgdG9wIDEwMDAgZ2VuZXMgYmFzZWQgb24gVlNUIGFuYWx5c2lzCgojIEhWRyBzdHJhdGVneQpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsIAogICAgICAgICAgIHN1YnNldF9yb3cgPSBodmcxMDAwKSwgCiAgICBjb2xvdXJfYnkgPSAiY2x1c3RlciIpICsKICBnZ3RpdGxlKCJIaWdobHkgdmFyaWFibGUgZ2VuZXMiKQoKIyBIREcgc3RyYXRlZ3kKcGxvdFBDQSgKICAgIHJ1blBDQShzY2UsCiAgICAgICAgICAgbmNvbXBvbmVudHMgPSAyLCAKICAgICAgICAgICBzdWJzZXRfcm93ID0gaGRnMTAwMCksIAogICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKSArCiAgZ2d0aXRsZSgiSGlnaC1kZXZpYW5jZSBnZW5lcyIpCgojIFZTVCBzdHJhdGVneQpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsIAogICAgICAgICAgIHN1YnNldF9yb3cgPSB2c3QxMDAwKSwgCiAgICBjb2xvdXJfYnkgPSAiY2x1c3RlciIsCiAgICApICsKICBnZ3RpdGxlKCJTZXVyYXQgVlNUIikKYGBgCgpOZXh0LCB3ZSBhc3Nlc3MgdGhlIHNlbnNpdGl2aXR5IG9uIHRoZSBudW1iZXIgb2YgdG9wIGZlYXR1cmVzIGZvciB0aGUgaGlnaGx5CnZhcmlhYmxlIGdlbmVzIG1ldGhvZDsKCmBgYHtyLCBldmFsID0gRkFMU0V9Cmh2Z19hbGwgPC0gZ2V0VG9wSFZHcyhkZWMpCgpodmcyMDAwIDwtIC4uLiAjIGV4dHJhY3QgdG9wIDIwMDAgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCmh2ZzEwMDAgPC0gLi4uICMgZXh0cmFjdCB0b3AgMTAwMCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKaHZnMTAwIDwtIC4uLiAjIGV4dHJhY3QgdG9wIDEwMCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKaHZnMTAgPC0gLi4uICMgZXh0cmFjdCB0b3AgMTAgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCmh2ZzUgPC0gLi4uICMgZXh0cmFjdCB0b3AgNSBoaWdobHkgdmFyaWFibGUgZ2VuZXMKCnBsb3RQQ0EoCiAgICBydW5QQ0Eoc2NlLAogICAgICAgICAgIG5jb21wb25lbnRzID0gMiksIAogICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKSArCiAgZ2d0aXRsZSgiQWxsIGdlbmVzIikKCnBsb3RQQ0EoCiAgICBydW5QQ0Eoc2NlLAogICAgICAgICAgIG5jb21wb25lbnRzID0gMiwgCiAgICAgICAgICAgc3Vic2V0X3JvdyA9IGh2ZzIwMDApLCAKICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikgKwogIGdndGl0bGUoIlRvcCAyMDAwIGdlbmVzIikKCnBsb3RQQ0EoCiAgICBydW5QQ0Eoc2NlLAogICAgICAgICAgIG5jb21wb25lbnRzID0gMiwgCiAgICAgICAgICAgc3Vic2V0X3JvdyA9IGh2ZzEwMDApLCAKICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikgKwogIGdndGl0bGUoIlRvcCAxMDAwIGdlbmVzIikKCnBsb3RQQ0EoCiAgICBydW5QQ0Eoc2NlLAogICAgICAgICAgIG5jb21wb25lbnRzID0gMiwgCiAgICAgICAgICAgc3Vic2V0X3JvdyA9IGh2ZzEwMCksIAogICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKSArCiAgZ2d0aXRsZSgiVG9wIDEwMCBnZW5lcyIpCgpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsIAogICAgICAgICAgIHN1YnNldF9yb3cgPSBodmcxMCksIAogICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKSArCiAgZ2d0aXRsZSgiVG9wIDEwIGdlbmVzIikKCnBsb3RQQ0EoCiAgICBydW5QQ0Eoc2NlLAogICAgICAgICAgIG5jb21wb25lbnRzID0gMiwgCiAgICAgICAgICAgc3Vic2V0X3JvdyA9IGh2ZzUpLCAKICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikgKwogIGdndGl0bGUoIlRvcCA1IGdlbmVzIikKYGBgCgojIyMgUmVtYXJrcyBvbiBQQ0EKClZpc3VhbGl6YXRpb25zIG9mIHJlZHVjZWQgZGltZW5zaW9ucyBmcm9tIGxpbmVhciBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24KbWV0aG9kcyBhcmUgb2Z0ZW4gIm92ZXJjcm93ZGVkIiwgYW5kIGl0IGlzIGhhcmQgdG8gc2VlIHN0cnVjdHVyZSAoZS5nLiwgdGhlIFBDQQpwbG90IHdlIGp1c3Qgc2F3KS4gTm9uLWxpbmVhciBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gbWV0aG9kcyBjYW4gb3ZlcmNvbWUgdGhpcwpwcm9ibGVtLiBBcyB0aGUgbmFtZSBzdWdnZXN0cywgdGhlIHJlZHVjZWQgZGltZW5zaW9ucyBhcmUgYSBub24tbGluZWFyIGZ1bmN0aW9uCm9mIHRoZSBvYnNlcnZlZCBkYXRhLiBXZSB3aWxsIG5vdCBnbyBpbnRvIGRldGFpbCBhcyB0byBob3cgdGhlc2Ugd29yayB1bmRlciB0aGUKaG9vZCwgYnV0IHByb3ZpZGUgYSBmZXcgZ3VpZGVsaW5lcyBmb3IgdGhlIG1vc3QgcG9wdWxhciBtZXRob2RzLiBPZnRlbiwgdGhlIHRvcAoofjEwLTUwKSBQQ3MgYXJlIHByb3ZpZGVkIGFzIGlucHV0LgoKIyMgQSBnZW5lcmFsaXphdGlvbiBvZiBQQ0EgZm9yIGV4cG9uZW50aWFsIGZhbWlseSBkaXN0cmlidXRpb25zLgoKUENBIGlzIGltcGxpY2l0bHkgYmFzZWQgb24gRXVjbGlkZWFuIGRpc3RhbmNlcywgY29ycmVzcG9uZGluZyB0byBtYXhpbWl6aW5nIGEKR2F1c3NpYW4gbGlrZWxpaG9vZCwgd2hpY2ggaXMgaW5hcHByb3ByaWF0ZSBmb3IgY291bnQgZGF0YSBzdWNoIGFzIHNjUk5BLXNlcS4KW1Rvd25lcyBldCBhbC5dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTktMTg2MS02KSAoMjAxOSkgZGV2ZWxvcApHTE0tUENBLCBhIGdlbmVyYWxpemF0aW9uIG9mIFBDQSBmb3IgZXhwb25lbnRpYWwgZmFtaWx5IGxpa2VsaWhvb2RzLiBUaGV5IHBvc2l0LAp1c2luZyBuZWdhdGl2ZSBjb250cm9sIGRhdGEsIHRoYXQgdGhlIGRhdGEgZ2VuZXJhdGl2ZSBtZWNoYW5pc20gb2YgVU1JIGNvdW50CmRhdGEgY2FuIGJlIGNvbnNpZGVyZWQgdG8gYmUgbXVsdGlub21pYWwuCgpUaGUgR0xNLVBDQSBzdHJhdGVneSBpcyBpbXBsZW1lbnRlZCBpbiB0aGUgYGdsbXBjYWAgZnVuY3Rpb24gb2YgdGhlIGBnbG1wY2FgCnBhY2thZ2UuCgpOb3RlIHRoYXQgdGhpcyBmdW5jdGlvbiBpcyBxdWl0ZSBjb21wdXRhdGlvbmFsbHkgaW50ZW5zaXZlLiBGb3IgcmVndWxhciBQQ0EgCm9uIGxvZy10cmFuc2Zvcm1lZCBub3JtYWxpemVkIGNvdW50cywgdGhlIHVuZGVybHlpbmcgY29tcHV0YXRpb25zIGNhbiBiZSAKc3Ryb25nbHkgc2ltcGxpZmllZC4gSGVyZSwgd2Ugd29yayB3aXRoIHRoZSByYXcgY291bnRzLCB3aGljaCB3ZSBhc3N1bWUgdG8gCmJlICpQb2lzc29uKiBkaXN0cmlidXRlZCwgcmVxdWlyaW5nIGFuIGl0ZXJhdGl2ZSBvcHRpbWl6YXRpb24gc2NoZW1lLgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgcnVucyAybWluIG9uIG15IGxhcHRvcApsaWJyYXJ5KGdsbXBjYSkKc2V0LnNlZWQoMjExMTAzKQpwb2lwY2EgPC0gZ2xtcGNhKFkgPSAuLi4sICMgY291bnRzIGZvciB0aGUgaGlnbHkgdmFyaWFibGUgZ2VuZXMKICAgICAgICAgICAgICAgICBMID0gLi4uLCAjIHR3byBsYXRlbnQgZGltZW5zaW9ucyAoY29tcG9uZW50cykKICAgICAgICAgICAgICAgICBmYW0gPSAuLi4sICMgYXNzdW1lIFBvaXNzb24gZm9yIGRyb3BsZXQgZGF0YQogICAgICAgICAgICAgICAgIG1pbmliYXRjaCA9ICJzdG9jaGFzdGljIikgIyB1c2VkIGluIHRoZSBwYWNrYWdlIHZpZ25ldHRlIGJ5IAojIHRoZSBhdXRob3JzLCBidXQgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIHNlc3Npb24uCnJlZHVjZWREaW0oc2NlLCAiUG9pUENBIikgPC0gcG9pcGNhJGZhY3RvcnMKcGxvdFJlZHVjZWREaW0oLi4uKSAjIG1ha2Ugc3VyZSB0byBzcGVjaWZ5IHRoZSByaWdodCB2YWx1ZSBmb3IgdGhlIGBkaW1yZWRgIGFyZ3VtZW50CmBgYAoKQWx0ZXJuYXRpdmVseSwgd2UgY291bGQgYWRvcHQgdGhlIEdMTS1QQ0Egc3RyYXRlZ3kgdXNpbmcgb25seSB0aGUgZ2VuZXMgd2l0aCAKaGlnaCBkZXZpYW5jZS4KCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgQmFzZWQgb24gdGhlIGRpYWdub3N0aWMgcGxvdCBmcm9tIHRoZSBmZWF0dXJlIHNlbGVjdGlvbiwgd2Ugd291bGQgc2VsZWN0IHRoZSAKIyB0b3AgMjAwMCBnZW5lcyB3aXRoIGhpZ2hlc3QgZGV2aWFuY2UuIEhvd2V2ZXIsIHRvIHJlZHVjZSB0aGUgcnVubmluZyB0aW1lIG9mCiMgdGhlIGZ1bmN0aW9uLCBJIGhlcmUgc2VsZWN0IHRoZSB0b3AgNTAwICh3aGljaCBzdGlsbCBpcyBxdWl0ZSBzbG93IC0gNW1pbikuCmhkZzUwMCA8LSBuYW1lcyhzb3J0KHJvd0RhdGEoc2NlKSRiaW5vbWlhbF9kZXZpYW5jZSwgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjUwMF0KCi4uLgpgYGAKClRoZSBhdXRob3JzIG9mIHRoZSBgZ2xtcGNhYCBwYWNrYWdlIG5vdGUgdGhhdCBHTE0tUENBIGNhbiBiZSBzbG93IGZvciBsYXJnZSAKZGF0YXNldHMuIFRoZXJlZm9yZSwgdGhleSBoYXZlIGltcGxlbWVudGVkIGEgZmFzdCBhcHByb3hpbWF0aW9uIG9mIHRoZSAKYWxnb3JpdGhtLCB3aGljaCBmaXJzdCBmaXRzIGEgbnVsbCBtb2RlbCBvZiBjb25zdGFudCBleHByZXNzaW9uIGZvciBlYWNoIGdlbmUgCmFjcm9zcyBhbGwgY2VsbHMsIGFuZCBzdWJzZXF1ZW50bHkgZml0cyBzdGFuZGFyZCBQQ0EgdG8gZWl0aGVyIHRoZSBQZWFyc29uIG9yIApkZXZpYW5jZSByZXNpZHVhbHMgZnJvbSB0aGUgbnVsbCBtb2RlbC4KCkhvd2V2ZXIsIGF0IGxlYXN0IGZvciBtZSB0aGUgYG51bGxSZXNpZHVhbHNgIGZ1bmN0aW9uIHdhcyAqKmV4dHJlbWVseSBzbG93KioKZXZlbiBzbG93ZXIgdGhhbiB0aGUgYGdsbXBjYWAgY29kZSBhYm92ZS4gVGhlcmVmb3JlLCAqKkkgc3VnZ2VzdCBub3QgcnVubmluZyoqCnRoaXMgY29kZSwgYnV0IEkgbGVhdmUgaXQgaW4gZm9yIHJlZmVyZW5jZS4KCmBgYApzY2UgPC0gbnVsbFJlc2lkdWFscyhzY2UsIGFzc2F5PSJjb3VudHMiLCB0eXBlPSJkZXZpYW5jZSIpCnNjZSA8LSBudWxsUmVzaWR1YWxzKHNjZSwgYXNzYXk9ImNvdW50cyIsIHR5cGU9InBlYXJzb24iKQoKcGNhPC1mdW5jdGlvbihZLCBMPTIsIGNlbnRlcj1UUlVFLCBzY2FsZT1UUlVFKXsKICAgICNhc3N1bWVzIGZlYXR1cmVzPXJvd3MsIG9ic2VydmF0aW9ucz1jb2xzCiAgICByZXM8LXByY29tcChhcy5tYXRyaXgodChZKSksIGNlbnRlcj1jZW50ZXIsIHNjYWxlLj1zY2FsZSwgcmFuay49TCkKICAgIGZhY3RvcnM8LWFzLmRhdGEuZnJhbWUocmVzJHgpCiAgICBjb2xuYW1lcyhmYWN0b3JzKTwtcGFzdGUwKCJkaW0iLCAxOkwpCiAgICBmYWN0b3JzCn0KcGNhX2QgPC0gcGNhKGFzc2F5KHNjZVtoZGcsXSwgImJpbm9taWFsX2RldmlhbmNlX3Jlc2lkdWFscyIpKQpwY2FfZCRyZXNpZF90eXBlIDwtICJkZXZpYW5jZV9yZXNpZHVhbHMiCnBjYV9wIDwtIHBjYShhc3NheShzY2VbaGRnLF0sICJiaW5vbWlhbF9wZWFyc29uX3Jlc2lkdWFscyIpKQpwY2FfcCRyZXNpZF90eXBlIDwtICJwZWFyc29uX3Jlc2lkdWFscyIKY20gPC0gYXMuZGF0YS5mcmFtZShjb2xEYXRhKHNjZVtoZGcsXSkpCnBkIDwtIHJiaW5kKGNiaW5kKGNtLCBwY2FfZCksIGNiaW5kKGNtLCBwY2FfcCkpCmdncGxvdChwZCwgYWVzKHg9ZGltMSwgeT1kaW0yLCBjb2xvdXI9cGhlbm9pZCkpICsgZ2VvbV9wb2ludCgpICsKICBmYWNldF93cmFwKH5yZXNpZF90eXBlLCBzY2FsZXM9ImZyZWUiLCBucm93PTIpICsKICBnZ3RpdGxlKCJQQ0EgYXBwbGllZCB0byBudWxsIHJlc2lkdWFscyBvZiBoaWdoIGRldmlhbmNlIGdlbmVzIikKYGBgCgojIyBOb24tbGluZWFyIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbjogdC1TTkUKCnQtU05FIGZvY3VzZXMgb24gcHJlc2VydmluZyBsb2NhbCByYXRoZXIgdGhhbiBnbG9iYWwgZGlzdGFuY2VzLiBUaGVyZWZvcmUsCmRpc3RhbmNlcyBvbiBhIHQtU05FIHJlZHVjZWQgZGltZW5zaW9uIHBsb3QgY2FuIG9ubHkgYmUgaW50ZXJwcmV0ZWQgbG9jYWxseSwKaS5lLiwgY2VsbHMgdGhhdCBhcmUgY2xvc2UgdG9nZXRoZXIgaW4gcmVkdWNlZCBkaW1lbnNpb24gd2lsbCBoYXZlIGEgc2ltaWxhcgp0cmFuc2NyaXB0b21lLCBidXQgY2VsbHMgdGhhdCBhcmUgZmFyIGF3YXkgbWF5IG5vdCBuZWNlc3NhcmlseSBoYXZlIGEgdmVyeSBkaXN0aW5jdCB0cmFuc2NyaXB0b21lLgoKUnVubmluZyB0LVNORSBvbiBhIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0IGNhbiBiZSBhY2hpZXZlZCB3aXRoIHRoZQpgcnVuVFNORWAgZnVuY3Rpb24gb2YgdGhlIGBzY2F0ZXJgIHBhY2thZ2UuIEJ5IGRlZmF1bHQsIHRoaXMgZnVuY3Rpb24gd2lsbCBmaXJzdApwZXJmb3JtIFBDQSwgYW5kIHVzZSB0aGUgdG9wIDUwIFBDcyBhcyBhbiBpbnB1dCB0byB0aGUgYWN0dWFsIHQtU05FCmFsZ29yaXRobS4gU2luY2Ugd2UgYWxyZWFkeSBwZXJmb3JtZWQgUENBLCB3ZSBtYXkgc2V0IGBkaW1yZWQgPSAiUENBImAgYXMKYXJndW1lbnQgdG8gdGhlIGZ1bmN0aW9uLiBBcyBzdWNoIHdlIHdpbGwgYmUgcGVyZm9ybWluZyBhIFQtU05FIG9uIHRoZSAzMCBQQ3MKd2UgY29tcHV0ZWQgYmVmb3JlLiBJZiB3ZSB3b3VsZCBsaWtlIHRvIHJ1biB0LVNORSBvbmx5IHVzaW5nIHRoZSB0b3AgMTAKUENzLCB3ZSBjb3VsZCBzZXQgYG5fZGltcmVkID0gMTBgLgoKSW4gYWRkaXRpb24sIHdlIG1heSB3aXNoIHRvIHNldCBgZXh0ZXJuYWxfbmVpZ2hib3JzPVRSVUVgLCB3aGljaCBpbmNyZWFzZXMgdGhlCnNwZWVkIG9mIHRoZSBhbGdvcml0aG0gZm9yIGxhcmdlIGRhdGFzZXRzIGJ5IGFwcGx5aW5nIGEgaGV1cmlzdGljLgoKKipOb3RlOioqIGZvciBtZSB0aGlzIHdhcyB0aGUgc2xvd2VzdCBmdW5jdGlvbiBvZiB0aGUgYW5hbHlzaXMgKHNvIGZhcikuIElmIHlvdQpmZWVsIGxpa2UgeW91ciBQQy9sYXB0b3AgaGFkIGEgbG90IG9mIHRyb3VibGUgd2l0aCB0aGUgcHJldmlvdXMgc3RlcChzKSwgeW91Cm1heSBjb25zaWRlciBub3QgcnVubmluZyB0aGlzIGNvZGUsIG9yIHJ1bm5pbmcgaXQgb24gYSBzdWJzZXQgb2YgdGhlIGRhdGEKKGUuZy4sIHJhbmRvbWx5IHN1YnNhbXBsaW5nIGNlbGxzKSBmb3IgZGVtb25zdHJhdGlvbmFsIHB1cnBvc2VzLiAKQWx0ZXJuYXRpdmVseSwgeW91IG1heSBjb25zaWRlciByZWR1Y2luZyB0aGUgaW5wdXQgc3BhY2UgZm9yIHRoZSB0LVNORSAKYWxnb3JpdGhtLCBlLmcuIGJ5IHNldHRpbmcgYG5fZGltcmVkID0gNWAuCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQojIChGb3IgbWUgaXQgdGFrZXMgM21pbjMwIHdpdGggbl9kaW1yZWQgPSA1KQpzY2UgPC0gcnVuVFNORSh4ID0gLi4uLCAKICAgICAgICAgICAgICAgZGltcmVkID0gLi4uLAogICAgICAgICAgICAgICBuX2RpbXJlZCA9IC4uLiwKICAgICAgICAgICAgICAgZXh0ZXJuYWxfbmVpZ2hib3JzID0gLi4uKQpwbG90VFNORShzY2UsCiAgICAgICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikKYGBgCgojIyBOb24tbGluZWFyIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbjogVU1BUAoKSXQgaXMgb2Z0ZW4gc3VnZ2VzdGVkIHRoYXQgVU1BUCBpcyBiZXR0ZXIgdGhhbiB0LVNORSBpbiBwcmVzZXJ2aW5nIGdsb2JhbCAKZGlmZmVyZW5jZXMuIFRoZXJlZm9yZSwgVU1BUCBpcyBhbHNvIG9mdGVuIHVzZWQgaW4gYW5hbHlzZXMgc3VjaCBhcyB0cmFqZWN0b3J5IAppbmZlcmVuY2UsIHdoZXJlIHRoaXMgaXMgaW1wb3J0YW50LgoKUnVubmluZyBVTUFQIG9uIGEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QgY2FuIGJlIGFjaGlldmVkIHdpdGggdGhlCmBydW5VTUFQYCBmdW5jdGlvbiBvZiB0aGUgYHNjYXRlcmAgcGFja2FnZS4KCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgVXNpbmcgdG9wIDEwJSBoaWdobHkgdmFyaWFibGUgZ2VuZXMgYW5kIHRvcCAzMCBQQ3MKc2NlIDwtIHJ1blVNQVAoLi4uKQpwbG90VU1BUCguLi4pCgojIFVzaW5nIHRvcCAxMCUgaGlnaGx5IHZhcmlhYmxlIGdlbmVzIGFuZCB0b3AgMTAgUENzCnBsb3RVTUFQKHJ1blVNQVAoLi4uKSwKICBjb2xvdXJfYnkgPSAiY2x1c3RlciIpCmBgYAoK