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")

# install packages if not yet installed.
pkgs <- c("SingleCellExperiment",
          "ExperimentHub",
          "edgeR",
          "biomaRt",
          "DropletUtils", 
          "scRNAseq", 
          "scater", 
          "scuttle", 
          "scran",
          "scry",
          "BiocSingular", 
          "scDblFinder",
          "Seurat",
          "PCAtools",
          "glmpca",
          "genefilter")
notInstalled <- pkgs[!pkgs %in% installed.packages()[,1]]
if(length(notInstalled) > 0){
  BiocManager::install(notInstalled)
}

Steps taken

  1. Import the Macosko dataset as SingleCellExperiment object from the scRNAseq Bioconductor package.

  2. Include ENSEMBL gene identifiers in the rowData

  3. Remove very lowly expressed genes

  4. Remove low quality cells 4.1. Cells with outlying library size 4.2. Cells with outlying transcriptome complexity 4.3. Cells with outlying percentage of mitochondrial reads 4.4. Empty droplets 4.5. Doublets

  5. Normalization 5.1. Compute log-normalized counts 5.2. Compute scaling factor to correct for differences in library size

  6. Feature selection 6.1. Genes with high variance 6.2. Genes with high variance with respect to their mean expression 6.3. Genes with high deviance 6.4. Genes with high variance after variance-stabilizing transformation (VST)

  7. Dimensionality reduction 7.1. Based on two most variable genes from step 6.2. 7.2. PCA 7.3. GLM-PCA 7.4. T-SNE 7.5. UMAP

2 Load end-result after lab session 2

library(SingleCellExperiment)
library(scater)
library(scran)
sce <- readRDS("/Users/jg/Desktop/sce_after_labsession2.rds")  # specify YOUR path!
head(colData(sce))
reducedDimNames(sce)

3 Add cluster information from publication

# install.packages("tidyverse") # if not yet installed
library(tidyverse)
sce$cluster_lowRes <- fct_recode(colData(sce)$cluster, 
           "Horizontal_cells" = "1",
           "Ganglion_cells" = "2",
           "Amacrine" = "3",
           "Amacrine" = "4",
           "Amacrine" = "5",
           "Amacrine" = "6",
           "Amacrine" = "7",
           "Amacrine" = "8",
           "Amacrine" = "9",
           "Amacrine" = "10",
           "Amacrine" = "11",
           "Amacrine" = "12",
           "Amacrine" = "13",
           "Amacrine" = "14",
           "Amacrine" = "15",
           "Amacrine" = "16",
           "Amacrine" = "17",
           "Amacrine" = "18",
           "Amacrine" = "19",
           "Amacrine" = "20",
           "Amacrine" = "21",
           "Amacrine" = "22",
           "Amacrine" = "23",
           "Rods" = "24",
           "Cones" = "25",
           "Bipolar" = "26",
           "Bipolar" = "27",
           "Bipolar" = "28",
           "Bipolar" = "29",
           "Bipolar" = "30",
           "Bipolar" = "31",
           "Bipolar" = "32",
           "Bipolar" = "33",
           "Muller_glia" = "34",
           "Astrocytes" = "35",
           "Fibroblast" = "36",
           "Vascular_endothelium" = "37",
           "Pericytes" = "38",
           "Microglia" = "39")

sce$cluster_highRes <- fct_recode(colData(sce)$cluster, 
           "Horizontal_cells" = "1",
           "Ganglion_cells" = "2",
           "Amacrine_1" = "3",
           "Amacrine_2" = "4",
           "Amacrine_3" = "5",
           "Amacrine_4" = "6",
           "Amacrine_5" = "7",
           "Amacrine_6" = "8",
           "Amacrine_7" = "9",
           "Amacrine_8" = "10",
           "Amacrine_9" = "11",
           "Amacrine_10" = "12",
           "Amacrine_11" = "13",
           "Amacrine_12" = "14",
           "Amacrine_13" = "15",
           "Amacrine_14" = "16",
           "Amacrine_15" = "17",
           "Amacrine_16" = "18",
           "Amacrine_17" = "19",
           "Amacrine_18" = "20",
           "Amacrine_19" = "21",
           "Amacrine_20" = "22",
           "Amacrine_21" = "23",
           "Rods" = "24",
           "Cones" = "25",
           "Bipolar_1" = "26",
           "Bipolar_2" = "27",
           "Bipolar_3" = "28",
           "Bipolar_4" = "29",
           "Bipolar_5" = "30",
           "Bipolar_6" = "31",
           "Bipolar_7" = "32",
           "Bipolar_8" = "33",
           "Muller_glia" = "34",
           "Astrocytes" = "35",
           "Fibroblast" = "36",
           "Vascular_endothelium" = "37",
           "Pericytes" = "38",
           "Microglia" = "39")

4 Clustering

4.1 Graph-based clustering

First, we discuss graph-based clustering methods for scRNA-Seq data. This is very well explained in the OSCA book chapter 5.2.

As written in the OSCA book: “Popularized by its use in Seurat, graph-based clustering is a flexible and scalable technique for clustering large scRNA-seq datasets. We first build a graph where each node is a cell that is connected to its nearest neighbors in the high-dimensional space. Edges are weighted based on the similarity between the cells involved, with higher weight given to cells that are more closely related. We then apply algorithms to identify “communities” of cells that are more connected to cells in the same community than they are to cells of different communities. Each community represents a cluster that we can use for downstream interpretation.

The major advantage of graph-based clustering lies in its scalability. It only requires a k-nearest neighbor search that can be done in log-linear time on average, in contrast to hierarchical clustering methods with runtimes that are quadratic with respect to the number of cells. Graph construction avoids making strong assumptions about the shape of the clusters or the distribution of cells within each cluster, compared to other methods like k-means (that favor spherical clusters) or Gaussian mixture models (that require normality)."

Several graph-based clustering algorithms are implemented in the scran library. The most global wrapper-function in this package is the clusterCells function. Typically, the input to this function is a SingleCellExperiment object with pre-computed principal components; these are used to take advantage of data compression and denoising. If the default settings are adopted, clusterCells will perform two steps under the hood:

  1. Build a shared nearest neighbors (SNN) graph of observations for downstream community detection. The SNN graph is closely related to the more common KNN graph. For each observation, its k-nearest neighbors are identified (k=10 by default), based on distances between their expression profiles (Euclidean distances are used by default) as observed in PCA space. An edge is drawn between all pairs of observations that share at least one neighbor, weighted by the characteristics of the shared nearest neighbors.

  2. The clusterCells function next internally calls the cluster_walktrap function from the igraph library. Based on the SNN graph from step 1, this function tries to find densely connected subgraphs, also called communities in a graph via random walks. The idea is that short random walks tend to stay in the same community.

# Do not run; clusterCells function with default settings, i.e., building an
# SNN graph and finding clusters with the walktrap algorithm.
library(scran)
nn.clusters <- clusterCells(sce, 
                            use.dimred="PCA")
table(nn.clusters)

The disadvantage of using clusterCells is that the default setting of the second step, the cluster_walktrap function, is slow for large datasets. While it is possible to adjust the different arguments of the clusterCells function, it might be more clear and intuitive to simply break up the process in two steps: building the graph and detecting clusters in that graph. For this second step, we may then adopt a faster algorithm.

4.1.1 Build graph (SNN graph)

# Build a shared nearest-neighbor graph from PCA space
graph <- buildSNNGraph(x = ..., # our SCE object
                       use.dimred = ...) #  A string specifying which existing values in reducedDims(x) should be used.
# alternative: buildKNNGraph()

4.1.2 Detect clusters on the graph

Two popular graph-based clustering algorithm are the leiden and louvain algorithms, both referring to the location of its developers. A common implementation of the louvainalgorithm is to optimize the modularity, effectively attempting to maximize the difference between the observed number of edges in a community and the expected number of such edges.

However, additional evaluations found that modularity optimization using the louvain algorithm is confined to a resolution limit, and in addition may result in communities that are not well connected. The leiden algorithm, instead, guarantees well-connected communities.

set.seed(464688)
# Walktrap community finding algorithm on the SNN graph
# DO NOT RUN -> takes 20 minutes
# cluster_walktrap <- factor(igraph::cluster_walktrap(g)$membership) #20min

# The `cluster_fast_greedy` function tries to find dense subgraph, also called 
# communities in graphs via directly optimizing a modularity score
# DO NOT RUN -> takes 4 minutes
# cluster_fastGreedy <- factor(igraph::cluster_fast_greedy(graph)$membership) #4min

# Louvain clustering on the SNN graph
cluster_louvain <- factor(igraph::cluster_louvain(graph)$membership) #8sec
nlevels(cluster_louvain) # 11 clusters

# Leiden clustering on the SNN graph
cluster_leiden <- ... #10sec
nlevels(cluster_leiden) # 1326 different clusters!

# obtain a less overly fine-grained clustering
cluster_leiden2 <- factor(...(graph = ...,
                              resolution_parameter = 0.01)$membership) #10sec
nlevels(cluster_leiden2) #14 different clusters

# add clusterings to colData
colData(sce)$cluster_louvain <- ...
colData(sce)$cluster_leiden2 <- ...

4.1.3 Comparing clustering strategies

A direct comparison of the Louvain and Leiden clustering results using a table of the cluster labels, shows good agreement.

table(..., ...) # compare Louvain and Leiden2 clustering

Interpret the result of the comparison between the two clustering strategies. Is there a strong correspondence between the strategies?

To make a visualization that gives us very similar information, we may use a heatmap:

#install.packages("pheatmap") # if not yet installed
library(pheatmap)
pheatmap::pheatmap(table(..., ...)) # heatmap to visualize the tabular comparison of above 

Again, interpret the result of the comparison between the two clustering strategies.

Alternatively, we may compute a clustering similarity score that captures the agreement between two sets of partitions. The Adjusted Rand Index (ARI) is often used in the literature for this purpose. The ARI is equal to 1 if the two partitions agree perfectly, and it is zero if the two partitions are unrelated. In some cases, the ARI may also be negative if the partitions are much more different than what could be expected by chance.

#install.packages("mclust") # if not yet installed
library(mclust)
mclust::adjustedRandIndex(..., # Louvain 
                          ... # Leiden)

4.1.4 Visualization

# Visualization. Add the cluster labels to the previously generated TSNE
# coordinates
plotTSNE(sce, 
         colour_by="cluster_louvain")

plotTSNE(sce, 
         colour_by="cluster_leiden2")

4.2 K-means clustering

K-means is a clustering algorithm that has been used in many application areas. In R, it can be applied via the kmeans function. Typically, it is applied to a reduced dimensional representation of the expression data (most often PCA). We need to define the number of clusters in advance. Since the results depend on the initialization of the cluster centers, it is typically recommended to run k-means with multiple starting configurations (via the nstart argument). For reproducibility, we also strongly advise to set a seed.

set.seed(123)

# k=10
clust_kmeans_k10 <- kmeans(reducedDim(...), # extract the principal components from the SCE object
                           centers = ..., # choose k = 10
                           nstart = ...)  # choose 5 different starting configurations
table(clust_kmeans_k10$cluster) # inspect the number of cells in each kmeans cluster
colData(sce)$kmeans10 <- ... # add to colData
plotTSNE(object = ..., 
         colour_by = ...) # color the observations according to the k-means clustering

# repeat for k=39

We here arbitrarily performed two k-means clustering analyses, once with k=10 and once with k=39 (the number of clusters communicated by the authors). The choice of the number of clusters k can be guided by known biology, however, it is arbitrary at least to some interval.

4.3 Hierarchical clustering

From OSCA book chapter 5.4:

"Hierarchical clustering is an old technique that arranges samples into a hierarchy based on their relative similarity to each other. Most implementations do so by joining the most similar samples into a new cluster, then joining similar clusters into larger clusters, and so on, until all samples belong to a single cluster. This process yields a dendrogram that defines clusters with progressively increasing granularity. Variants of hierarchical clustering methods primarily differ in how they choose to perform the agglomerations. For example, complete linkage aims to merge clusters with the smallest maximum distance between their elements, while Ward’s method aims to minimize the increase in within-cluster variance.

In the context of scRNA-seq, the main advantage of hierarchical clustering lies in the production of the dendrogram. This is a rich summary that quantitatively captures the relationships between subpopulations at various resolutions. Cutting the dendrogram at high resolution is also guaranteed to yield clusters that are nested within those obtained at a low-resolution cut; this can be helpful for interpretation."

Indeed, low-resolution clusters can typically be interpreted as super-level cell types, like immune cells, neuron cells or endothelial cells. Higher resolution clusters correspond with a higher biological resolution: immune cell -> lymphocyte -> T-cell -> Th1 cell.

However, note that we can also overcluster the data (splitting a homogenous set of cells in multiple clusters), resulting in spurious cell type identification.

The clusterCells function of the scran library also allows for performing hierarchical clustering. This can be implemented as follows:

# takes 4 minutes
library(bluster)
hclust.sce <- clusterCells(x = sce, 
                            use.dimred = "PCA",
                            BLUSPARAM = HclustParam(method="ward.D2"))

Equivalently, we may again split the process in two steps:

  1. Compute the pairwise distances between all cells. These are by default Euclidean distances and, in order to reduce data complexity and increase signal to noise, we may perform this on the top (30) PC’s, just like we did when constructing the SNN graph in graph-based clustering. Calculating a dissimilarity matrix is implemented in the dist function.

  2. Perform a hierarchical clustering on the distances from step 1. In an agglomerative procedure, each cell is first assigned to its own cluster and then the algorithm proceeds iteratively, at each stage joining the two most similar clusters, continuing until there is just a single cluster. Implemented in the hclust function.

Note that the hclust function allows for specifying a “method” argument. The differences between the different methods goes beyond the scope of this session, but a brief description is provided in the function help file. In the context of scRNA-seq, we recommend the use of the "ward.D2" method.

distsce <- dist(...) # extract the principal components from the SCE object (runs 1min)
hcl <- hclust(distsce, 
              method = ...) # runs 3min
plot(hcl, 
     labels = FALSE) # visualize dendrogram

Next, in order to derive a given set of cluster labels, we need to “cut the tree”, i.e., choose at which resolution we want to report the (cell type) clusters. This can be achieved with the cutree function. As an input, cutree takes the dendrogram from the hclust function and a threshold value for cutting the tree. The latter may be either k, the number of clusters we want to report, or h, the height of the dendrogram at which we want to cut the tree.

# cut to get 10 clusters
clust_hcl_k10 <- cutree(tree = ..., # name of clustering tree
                        k = ...) # desired number of groups
table(clust_hcl_k10)
# cut to get 39 clusters
...
sce$clust_hcl_k10 <- ... # add to colData
sce$clust_hcl_k39 <- a... # add to colData

# Visualization. Add the cluster labels to the previously generated TSNE
# coordinates
plotTSNE(object = ..., 
         colour_by = ...) # k=10

plotTSNE(object = ..., 
         colour_by = ...) # k=39

5 Clustering in the original paper

When we compare our cluster labels with those from the original paper, we’ll see that the correspondence is not great. As a demonstration, I make a table and a heatmap comparing the low-resolution cluster labels from the paper with our Louvain, Leiden2 and hierarchical clustering (k=10) labels:

table(sce$cluster_lowRes, ...) # compare low resolution clusters of authors to our Louvain clustering
... # compare low resolution clusters of authors to our Leiden2 clustering
... # compare low resolution clusters of authors to our hierarchical (k=10) clustering
# all three comparisons with heatmaps
pheatmap::pheatmap(...)

pheatmap::pheatmap(...)

pheatmap::pheatmap(...)

Also, when we look at the t-SNE from the original publication, we observe clearly distinct clusters:

knitr::include_graphics("macosko_figure_5B.jpeg")

The main reason for this is that the authors of the original paper used quite a different strategy for performing the feature selection and dimension reduction that we have performed in lab session 2.

To demonstrate the pipeline of the original authors, and to make our results more comparable to theirs, We will here mimic their strategy for feature selection and clustering. However, we will do this approximatively! We will take similar steps conceptually, but will remain within the current Bioconductor framework and the range of functions that we have seen in this lecture series.

The authors performed the following steps:

  1. Filtering: The authors first filtered the 49,300-cell dataset to retain only single-cell libraries where at least 900 genes were detected.

  2. Feature selection: The authors first identified the set of genes that were most variable across the selected susbet of cells, after controlling for the relationship between mean expression and variability. To do this, the authors adopted a manual implementation.

-> We will not use the exact same strategy, but the modelGeneVar-getTopHVGs strategy, which is conceptually similar in addressing the mean-variance relationship during feature selection.

  1. Principal component analysis: The authors performed PCA after scaling the data. They next performed a test to determine how many PCs contributed significantly to explaining the variability in the data. Based on this test, they continued the analysis pipeline with the top 32 PCs.

  2. t-SNE: Next, the authors performed a t-SNE on the top 32 PC’s, setting the perplexity parameter of the t-SNE algorithm to 30.

  3. Projection of remaining cells and clustering: Finally, the authors adopted a manually implemented, rather complex strategy to project the remaining cells (where less than 900 different genes were detected) on the t-SNE embedding obtained in step 4. Next, they cluster the cells using a density clustering (DBSCAN algorithm) that was not discussed in this lecture series. Because this 5th step uses techniques beyond the scope of this course, we will simply continue working with the filtered dataset and perform hierarchical clustering. However, we included some code that allows you to do something similar to what the authors did for your reference (note that running
    this code requires installing the snifter R package, which requires a working Python and Conda installation).

# code for steps 1-4
library(scater)
library(genefilter)
library(scran)

# Step 1: Downsampling
sce_900 <- sce[,sce$detected > 900]

# Step 2: Feature selection
sce_900 <- logNormCounts(sce_900)
dec_900 <- modelGeneVar(sce_900)
hvg_900 <- getTopHVGs(dec_900,
                      n = 374) # same number of top features as original paper

# Step 3: PCA
set.seed(1234)
sce_900 <- runPCA(sce_900, 
                  ncomponents = 32, # same number of PCs as original paper
                  subset_row = hvg_900,
                  scale=TRUE) # scale the data like in original paper

# Step 4: T-SNE
set.seed(484854)
sce_900 <- runTSNE(sce_900, 
               dimred = 'PCA',
               n_dimred = 32,
               perplexity = 30) # same perplexity as original paper
# Step 5 authors (just for your reference): project new cells on t-SNE embedding
#BiocManager::install("snifter")
library(snifter)
tsne1 <- snifter::fitsne(reducedDim(sce_900, type="PCA"))
embedding <- reducedDim(sce[hvg_900,sce$detected>900], type="PCA")

ggplot() +
  aes(tsne1[, 1], tsne1[, 2], colour = as.factor(sce[,sce$detected>900]$cluster)) +
  geom_point(pch = 19) +
  scale_colour_discrete(name = "Cluster") +
  labs(x = "t-SNE 1", y = "t-SNE 2") +
  theme_bw()

new_coords <- project(tsne1, 
                      new = reducedDim(sce[,sce$detected<=900], type="PCA"), 
                      old = reducedDim(sce[,sce$detected>900], type="PCA"))
ggplot() +
    geom_point(
        aes(tsne1[, 1], tsne1[, 2],
            colour = as.factor(sce[,sce$detected>900]$cluster),
            shape = "embedding"
        )
    ) +
    geom_point(
        aes(new_coords[, 1], new_coords[, 2], 
            colour = as.factor(sce[,sce$detected<=900]$cluster),
            shape = "projection"
        )
    ) +
    scale_colour_discrete(name = "Cell type") +
    scale_shape_discrete(name = NULL) +
    labs(x = "t-SNE 1", y = "t-SNE 2") +
    theme_bw()
# Step 5 for us: perform hierarchical clustering
distsce <- dist(...)
hcl <- hclust(tree = ..., 
              method = ...)

clust_hcl_k10 <- ...
clust_hcl_k39 <- ...

sce_900$clust_hcl_k10 <- ...
sce_900$clust_hcl_k39 <- ...

Visualize our labels and compare with original labels at the low resolution

# visualize our labels
plotTSNE(object = ...,
         colour_by = ...,
         text_by = ...)

# compare with original labels
plotTSNE(object = sce_900,
         colour_by = "cluster_lowRes",
         text_by = "cluster_lowRes",
         text_size = 3)

Carefully compare the two resulting figures!

# repeat for high resolution labels (2 new t-SNE plots)
...

Carefully compare the two resulting figures!

Overall, we observe a rather strong correspondence between our clusters and those of the authors. Also note that our t-SNE visualization now resembles the t-SNE map of the original authors much more closely:

knitr::include_graphics("./macosko_figure_5B.jpeg")

6 Cell type annotation

6.1 Supervised: using a limited set of known markers

In the publication, the authors aimed to identify the different clusters in the data by using a set of 12 well-known molecular markers; genes for which the expression profile is typically very specific, i.e., highly expressed only in one specific cell type. They used the following markers:

markers <- c("LHX1", "SLC17A6","PAX6","GAD1","SLC6A9","OPN1MW","VSX2",
             "RLBP1", "GFAP", "PECAM1", "KCNJ8","CX3CR1")

We will here visualize the expression of these markers in t-SNE space. First, we create a “baseline” figure, displaying each cell in 2D space, colored in black.

library(ggplot2)
gg_hlp_data <- data.frame(x = reducedDim(sce_900, type = "TSNE")[,1],
                          y = reducedDim(sce_900, type = "TSNE")[,2],
                          cluster = sce_900$cluster)
gg_base <- ggplot(data = gg_hlp_data[!is.na(sce_900$cluster),],
                  aes(x = x, y = y,)) +
    geom_point(size=0.4) +
    theme_bw() +
    xlab("TSNE 1") +
    ylab("TSNE 2")
gg_base

Next, we obtain the counts of the 12 pre-selected marker genes for all cells.

marker_counts <- assays(sce_900)$counts[markers,]
marker_counts <- as.matrix(t(marker_counts))

Finally, we make one figure for each of the 12 marker genes. The idea is to give each cell that has a non-zero expression of the marker (i.e., for which the marker is expressed) a red coloring.

marker_counts_binary <- marker_counts
marker_counts_binary[which(marker_counts_binary > 0)] <- 1

for (i in seq_along(markers)) {
    gg <- ggplot(data = gg_hlp_data[!is.na(sce_900$cluster),],
                    aes(x = x, y = y)) +
        geom_point(aes(color = as.factor(marker_counts_binary[,i])[!is.na(sce_900$cluster)]), size = 0.4) +
        scale_color_manual(values=c("black","red")) +
        xlab("TSNE 1") +
        ylab("TSNE 2") +
        ggtitle(colnames(marker_counts_binary)[i]) +
        theme_bw() + 
      theme(legend.title = element_blank())
    print(gg)
}
# Visualize our clusters
plotTSNE(sce_900,
         colour_by = "clust_hcl_k39",
         text_by = "clust_hcl_k39")

From figure 5D, we obtain the marker-cell type relationship:

knitr::include_graphics("macosko_figure_5D.jpeg")

Try to go back and forth between

  1. the visualization of marker expression on the t-SNE map,
  2. the visualization of our cluster labels on the t-SNE map, and
  3. the marker-cell type relationship from figure 5D.

This should allow you to obtain a low-resolution annotation for most of the clusters we have manually obtained ourselves! We give away one of the easiest annotations:

  • The LHX1 marker is specific for horizontal cells. As such, our cluster 3 corresponds to horizontal cells.

You can do this for the other markers/clusters (sometimes it will still be ambiguous).

6.2 Supervised: Using marker genes detected from this data

Sometimes it will be very difficult to set up a panel of known marker genes that would allow us to distinguish between all cell types in our dataset. For instance, sometimes we may not know in advance which cell types to expect, or we may not have good information on relevant markers (if the studied system is not well known).

An alternative strategy is to identify the genes that drive separation between clusters. These marker genes allow us to assign biological meaning to each cluster based on their functional annotation. This strategy is referred to as marker gene detection.

The most straightforward approach to marker gene detection involves testing for differential expression (DE) between clusters. If a gene is strongly DE between clusters, it is likely to have driven the separation of cells in the dimensionality reduction. The general strategy is to compare each pair of clusters and compute scores quantifying the differences in the expression distributions between clusters. The scores for all pairwise comparisons involving a particular cluster are then consolidated into a single data frame for that cluster. This approach is implemented in the scoreMarkers function of the scran package.

library(scran)
marker.info <- scoreMarkers(x = ..., # the SCE object
                            groups = ...) # our cluster identifiers
marker.info # one dataframe for each of the 39 clusters
colnames(marker.info[["1"]]) # statistics for cluster 1.
head(marker.info[["1"]])

We observe several summary statistics for each gene in the dataframe for cluster 1. We highlight a few:

  • self.average: the average log-normalized expression of the gene in the target cluster (cluster 1)

  • other.average: the average log-normalized expression of the gene in all the other clusters (clusters 2-39)

  • self.detected: the fraction of cells in which the gene was expressed in the target cluster (cluster 1)

  • other.detected: the fraction of cells in which the gene was expressed in all the other clusters (cluster 2-39)

  • mean.AUC: From the OSCA book chapter 6.3: “In the context of marker detection, the area under the curve (AUC) quantifies our ability to distinguish between two distributions in a pairwise comparison. The AUC represents the probability that a randomly chosen observation from our cluster of interest is greater than a randomly chosen observation from the other cluster. A value of 1 corresponds to upregulation, where all values of our cluster of interest are greater than any value from the other cluster; a value of 0.5 means that there is no net difference in the location of the distributions; and a value of 0 corresponds to downregulation. The AUC is closely related to the U statistic in the Wilcoxon ranked sum test (a.k.a., Mann-Whitney U-test).” As such, this a very interesting column to use for selecting marker genes.

Based on the mean.AUC statistic, we may now inspect the top10 markers to distinguish between cells of cluster 1 and cells of the other clusters:

chosen <- marker.info[["1"]]
ordered <- chosen[order(chosen$mean.AUC, decreasing=TRUE),]
head(ordered[,c(1:4,10)], n=10) # showing basic stats only, for brevity.

We can also visualize the log-normalized expression of the top10 markers in each cell, stratified on cluster label, using the plotExpression function of the scater package:

library(scater)
plotExpression(bject = ..., # the SCE object, 
               features = ..., # top 10 features according to the `scoreMarkers` results
               x = ..., # our cluster labels 
               colour_by = ...) # our cluster labels 

Based on these results, which markers would you choose to unambiguously distinguish cells from cluster 1 from cells of the other clusters?

Does the name of the 9th marker gene ring any bells?

6.3 Semi-supervised using SingleR

A conceptually straightforward annotation approach is to compare our current scRNA-seq dataset with a previously annotated reference dataset. Labels can then be assigned to each cell in the Macosko dataset based on the most similar reference cells, for some definition of “similar”. This is a standard classification challenge that can be tackled by standard machine learning techniques such as random forests and support vector machines. Any published and labeled RNA-seq dataset (bulk or single-cell) can be used as a reference, though its reliability depends greatly on the expertise of the original authors who assigned the labels in the first place and the closer the reference dataset is to the dataset we would like to annotate (e.g., full-length vs UMI-based protocol), the more accurate the annotation will typically be.

In this section, we will perform such label transfer between the annotated reference dataset from Shekhar et al., which also is a scRNA-seq dataset that studied the mouse retina, and the Macosko dataset.

To perform the actual label transfer, will use the SingleR Bioconductor package. For each “test” cell in the Macosko dataset, singleR will:

  1. Compute Spearman correlation between the test cell and each reference cell. To improve signal/noise, only marker genes identified using the reference dataset are used for this.

  2. For each label (cell type), set the score as the (default of) 80% quantile of Spearman correlations.

  3. The prediction is then the label with the highest score.

Before we can use singleR to perform label transfer, we will need to import, explore (brief) and wrangle the reference dataset by Shekhar et al..

6.3.1 Import reference data

The dataset by Shekhar et al. can be conveniently imported using the scRNAseq package.

library(scRNAseq)
(ref.data <- scRNAseq::ShekharRetinaData(ensembl = TRUE))

6.3.2 Explore metadata of reference data

Inspect the colData for the Shekhar dataset. Compare the CLUSTER and SUBCLUSTER variables!

6.3.3 Process reference data

  1. Remove unlabeled cells
sum(is.na(ref.data$CLUSTER))
ref.data <- ref.data[,-which(is.na(ref.data$CLUSTER))]
  1. Remove doublets/contaminant cells

The original authors already performed quality control; we have cells with cluster label “Doublets/Contaminants”. Let’s remove those:

sum(ref.data$CLUSTER == ...)
ref.data <- ...
  1. Make lower resolution cell type levels (for easier interpretation)
ref.data$CLUSTER_lowRes <- fct_recode(ref.data$CLUSTER, 
           "Amacrine_cells" = "AC (Amacrine cell)",
           "Bipolar_cells" = "BC1A",
           "Bipolar_cells" = "BC1B",
           "Bipolar_cells" = "BC2",
           "Bipolar_cells" = "BC3A",
           "Bipolar_cells" = "BC3B",
           "Bipolar_cells" = "BC4",
           "Bipolar_cells" = "BC5A (Cone Bipolar cell 5A)",
           "Bipolar_cells" = "BC5B",
           "Bipolar_cells" = "BC5C",
           "Bipolar_cells" = "BC5D",
           "Bipolar_cells" = "BC6",
           "Bipolar_cells" = "BC7 (Cone Bipolar cell 7)",
           "Bipolar_cells" = "BC8/9 (mixture of BC8 and BC9)",
           "Cones" = "Cone Photoreceptors",
           "Muller_glia" = "MG (Mueller Glia)",
           "Rod_Bipolar_cell" = "RBC (Rod Bipolar cell)",
           "Rod Photoreceptors" = "Rod Photoreceptors")
  1. Remove lowly expressed genes
keep <- rowSums(assays(ref.data)$counts > 0) > 10
table(keep)
ref.data <- ref.data[keep,]
  1. Obtain same gene ID format as in target data

To avoid problems with different version of gene symbols, it is good practice do work with unambiguous gene identifiers like those of ENSEMBL instead.

rownames(sce_900) <- rowData(sce_900)$ensembl_gene_id # use ENSEMBL identifiers instead
sum(rownames(sce_900) %in% rownames(ref.data))
  1. Compute logNormCounts
library(scuttle)
ref.data <- logNormCounts(...)

6.3.4 SingleR at low reference resolution

#BiocManager::install("SingleR")
library(SingleR)

# runs 2min 30sec for me
pred.lowRes <- SingleR(test = ..., # our dataset
                       ref = ..., # the reference dataset 
                       labels = ..., # vector of known labels for all cells in ref
                       de.method = "wilcox") # most suitable method for sparse (droplet) data
table(pred.lowRes$labels)

Using the SingleR classifier that was trained on the reference dataset by Shekhar et al., we have labeled 2731 cells from the Macosko dataset as amacrine cells, 1528 cells as bipolar cells, and so on. Again note that we predicted a label for each cell in the Macosko dataset based its similarity (in gene expression) with labeled cells from the Shekhar dataset.

Most importantly, we want to compare our predicted cell labels with the labels that were obtained by Macokso et al., which we could consider to be ground truth labels if we assume that the authors succeeded in their large effort of annotating their cell clusters for their publication.

tab <- table(..., ...) # the original low-resolution labels versus our predicted low-resolution labels
tab

Inspect the results.

Before we dive deeper into this, we may visualize our result using a heatmap:

pheatmap::pheatmap(tab / rowSums(tab))

Inspect the results.

One other visualization strategy is implemented in the plotScoreHeatmap of the SingleR package. Remember, the SingleR classifier assigns each target cell to a cell type label with a certain probability. The plotScoreHeatmap function then allows you to plot, for each cell (columns) the assignment score (which can be thought of as a probability) of that cell belonging to each of the reference label categories (rows).

gg <- plotScoreHeatmap(pred.lowRes[1:20,])
gg

Inspect the results.

Finally, we can use these assignment score to filter out cells that could not be unambiguously assigned to one cell type, i.e., only report those cells that we were able to reliably assign:

summary(is.na(pred.lowRes$pruned.labels))

range(rowMaxs(pred.lowRes$scores)) # all assignments
range(rowMaxs(pred.lowRes$scores[!is.na(pred.lowRes$pruned.labels),])) # reliable assignments

table(sce_900$cluster_lowRes[!is.na(pred.lowRes$pruned.labels)], 
      pred.lowRes$labels[!is.na(pred.lowRes$pruned.labels)])

We will interpret the concordance between the predicted and the original labels on this subset. We observe a strong correspondence between the predicted labels and the labels from Macokso et al. We can read this table as follows:

  • Almost all amacrine cells of the Macosko dataset are correctly predicted as amacrine cells (2036/(2036+7+1+41+1) ±= 98% correctly assigned).

Try to interpret the results for the other cell types.

We can now also visualize the predicted labels and the original labels on our t-SNE.

sce_900$SingleR_lowRes <- pred.lowRes$labels

# our low resolution labels based on reference
plotTSNE(...,
         colour_by = ...,
         text_by = ...,
         text_size = ...)

# low resolution labels of the authors
plotTSNE(...,
         colour_by = ...,
         text_by = ...,
         text_size = ...)

Interpret the visualizations.

Altogether, label-transfer was quite successful, and we would have been able to very quickly get high-quality results using this very user-friendly and fast approach implemented in the SingleR package.

6.3.5 Addendum: SingleR at high reference resolution

For the sake of completeness, we may also perform label transfer with SingleR using the more fine-grained labels from the reference dataset.

...
LS0tCnRpdGxlOiAnTGFiMzogQ2x1c3RlcmluZywgbWFya2VyIGdlbmUgZGV0ZWN0aW9uIGFuZCBjZWxsIHR5cGUgYW5ub3RhdGlvbicKYXV0aG9yOiAiS29lbiBWYW4gZGVuIEJlcmdlIGFuZCBKZXJvZW4gR2lsaXMiCmRhdGU6ICI2LzEyLzIwMjEiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGRvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMgUHJlYW1ibGU6IGluc3RhbGxhdGlvbiBvZiBCaW9jb25kdWN0b3IgbGlicmFyaWVzCgpgYGB7cixldmFsPUZBTFNFfQojIGluc3RhbGwgQmlvY01hbmFnZXIgcGFja2FnZSBpZiBub3QgaW5zdGFsbGVkIHlldC4KIyBCaW9jTWFuYWdlciBpcyB0aGUgcGFja2FnZSBpbnN0YWxsZXIgZm9yIEJpb2NvbmR1Y3RvciBzb2Z0d2FyZS4KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKCiMgaW5zdGFsbCBwYWNrYWdlcyBpZiBub3QgeWV0IGluc3RhbGxlZC4KcGtncyA8LSBjKCJTaW5nbGVDZWxsRXhwZXJpbWVudCIsCiAgICAgICAgICAiRXhwZXJpbWVudEh1YiIsCiAgICAgICAgICAiZWRnZVIiLAogICAgICAgICAgImJpb21hUnQiLAogICAgICAgICAgIkRyb3BsZXRVdGlscyIsIAogICAgICAgICAgInNjUk5Bc2VxIiwgCiAgICAgICAgICAic2NhdGVyIiwgCiAgICAgICAgICAic2N1dHRsZSIsIAogICAgICAgICAgInNjcmFuIiwKICAgICAgICAgICJzY3J5IiwKICAgICAgICAgICJCaW9jU2luZ3VsYXIiLCAKICAgICAgICAgICJzY0RibEZpbmRlciIsCiAgICAgICAgICAiU2V1cmF0IiwKICAgICAgICAgICJQQ0F0b29scyIsCiAgICAgICAgICAiZ2xtcGNhIiwKICAgICAgICAgICJnZW5lZmlsdGVyIikKbm90SW5zdGFsbGVkIDwtIHBrZ3NbIXBrZ3MgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssMV1dCmlmKGxlbmd0aChub3RJbnN0YWxsZWQpID4gMCl7CiAgQmlvY01hbmFnZXI6Omluc3RhbGwobm90SW5zdGFsbGVkKQp9CmBgYAoKU3RlcHMgdGFrZW4KCjEuIEltcG9ydCB0aGUgTWFjb3NrbyBkYXRhc2V0IGFzIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0IGZyb20gdGhlIApgc2NSTkFzZXFgIEJpb2NvbmR1Y3RvciBwYWNrYWdlLgoKMi4gSW5jbHVkZSBFTlNFTUJMIGdlbmUgaWRlbnRpZmllcnMgaW4gdGhlIGByb3dEYXRhYAoKMy4gUmVtb3ZlIHZlcnkgbG93bHkgZXhwcmVzc2VkIGdlbmVzCgo0LiBSZW1vdmUgbG93IHF1YWxpdHkgY2VsbHMgCiAgNC4xLiBDZWxscyB3aXRoIG91dGx5aW5nIGxpYnJhcnkgc2l6ZSAKICA0LjIuIENlbGxzIHdpdGggb3V0bHlpbmcgdHJhbnNjcmlwdG9tZSBjb21wbGV4aXR5IAogIDQuMy4gQ2VsbHMgd2l0aCBvdXRseWluZyBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgcmVhZHMgCiAgNC40LiBFbXB0eSBkcm9wbGV0cyAKICA0LjUuIERvdWJsZXRzCgo1LiBOb3JtYWxpemF0aW9uIAogIDUuMS4gQ29tcHV0ZSBsb2ctbm9ybWFsaXplZCBjb3VudHMgCiAgNS4yLiBDb21wdXRlIHNjYWxpbmcgZmFjdG9yIHRvIGNvcnJlY3QgZm9yIGRpZmZlcmVuY2VzIGluIGxpYnJhcnkgc2l6ZQoKNi4gRmVhdHVyZSBzZWxlY3Rpb24gCiAgNi4xLiBHZW5lcyB3aXRoIGhpZ2ggdmFyaWFuY2UgCiAgNi4yLiBHZW5lcyB3aXRoIGhpZ2ggdmFyaWFuY2Ugd2l0aCByZXNwZWN0IHRvIHRoZWlyIG1lYW4gZXhwcmVzc2lvbiAKICA2LjMuIEdlbmVzIHdpdGggaGlnaCBkZXZpYW5jZSAKICA2LjQuIEdlbmVzIHdpdGggaGlnaCB2YXJpYW5jZSBhZnRlciB2YXJpYW5jZS1zdGFiaWxpemluZyB0cmFuc2Zvcm1hdGlvbiAoVlNUKQoKNy4gRGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIAogIDcuMS4gQmFzZWQgb24gdHdvIG1vc3QgdmFyaWFibGUgZ2VuZXMgZnJvbSBzdGVwIDYuMi4gCiAgNy4yLiBQQ0EgCiAgNy4zLiBHTE0tUENBIAogIDcuNC4gVC1TTkUgCiAgNy41LiBVTUFQCgojIExvYWQgZW5kLXJlc3VsdCBhZnRlciBsYWIgc2Vzc2lvbiAyCgpgYGB7cixldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQpsaWJyYXJ5KHNjYXRlcikKbGlicmFyeShzY3JhbikKYGBgCgpgYGB7cixldmFsPUZBTFNFfQpzY2UgPC0gcmVhZFJEUygiL1VzZXJzL2pnL0Rlc2t0b3Avc2NlX2FmdGVyX2xhYnNlc3Npb24yLnJkcyIpICAjIHNwZWNpZnkgWU9VUiBwYXRoIQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CmhlYWQoY29sRGF0YShzY2UpKQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CnJlZHVjZWREaW1OYW1lcyhzY2UpCmBgYAoKIyBBZGQgY2x1c3RlciBpbmZvcm1hdGlvbiBmcm9tIHB1YmxpY2F0aW9uCgpgYGB7cixldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpICMgaWYgbm90IHlldCBpbnN0YWxsZWQKbGlicmFyeSh0aWR5dmVyc2UpCnNjZSRjbHVzdGVyX2xvd1JlcyA8LSBmY3RfcmVjb2RlKGNvbERhdGEoc2NlKSRjbHVzdGVyLCAKICAgICAgICAgICAiSG9yaXpvbnRhbF9jZWxscyIgPSAiMSIsCiAgICAgICAgICAgIkdhbmdsaW9uX2NlbGxzIiA9ICIyIiwKICAgICAgICAgICAiQW1hY3JpbmUiID0gIjMiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiNCIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICI1IiwKICAgICAgICAgICAiQW1hY3JpbmUiID0gIjYiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiNyIsCiAgICAgICAgICAgIkFtYWNyaW5lIiA9ICI4IiwKICAgICAgICAgICAiQW1hY3JpbmUiID0gIjkiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMTAiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMTEiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMTIiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMTMiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMTQiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMTUiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMTYiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMTciLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMTgiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMTkiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMjAiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMjEiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMjIiLAogICAgICAgICAgICJBbWFjcmluZSIgPSAiMjMiLAogICAgICAgICAgICJSb2RzIiA9ICIyNCIsCiAgICAgICAgICAgIkNvbmVzIiA9ICIyNSIsCiAgICAgICAgICAgIkJpcG9sYXIiID0gIjI2IiwKICAgICAgICAgICAiQmlwb2xhciIgPSAiMjciLAogICAgICAgICAgICJCaXBvbGFyIiA9ICIyOCIsCiAgICAgICAgICAgIkJpcG9sYXIiID0gIjI5IiwKICAgICAgICAgICAiQmlwb2xhciIgPSAiMzAiLAogICAgICAgICAgICJCaXBvbGFyIiA9ICIzMSIsCiAgICAgICAgICAgIkJpcG9sYXIiID0gIjMyIiwKICAgICAgICAgICAiQmlwb2xhciIgPSAiMzMiLAogICAgICAgICAgICJNdWxsZXJfZ2xpYSIgPSAiMzQiLAogICAgICAgICAgICJBc3Ryb2N5dGVzIiA9ICIzNSIsCiAgICAgICAgICAgIkZpYnJvYmxhc3QiID0gIjM2IiwKICAgICAgICAgICAiVmFzY3VsYXJfZW5kb3RoZWxpdW0iID0gIjM3IiwKICAgICAgICAgICAiUGVyaWN5dGVzIiA9ICIzOCIsCiAgICAgICAgICAgIk1pY3JvZ2xpYSIgPSAiMzkiKQoKc2NlJGNsdXN0ZXJfaGlnaFJlcyA8LSBmY3RfcmVjb2RlKGNvbERhdGEoc2NlKSRjbHVzdGVyLCAKICAgICAgICAgICAiSG9yaXpvbnRhbF9jZWxscyIgPSAiMSIsCiAgICAgICAgICAgIkdhbmdsaW9uX2NlbGxzIiA9ICIyIiwKICAgICAgICAgICAiQW1hY3JpbmVfMSIgPSAiMyIsCiAgICAgICAgICAgIkFtYWNyaW5lXzIiID0gIjQiLAogICAgICAgICAgICJBbWFjcmluZV8zIiA9ICI1IiwKICAgICAgICAgICAiQW1hY3JpbmVfNCIgPSAiNiIsCiAgICAgICAgICAgIkFtYWNyaW5lXzUiID0gIjciLAogICAgICAgICAgICJBbWFjcmluZV82IiA9ICI4IiwKICAgICAgICAgICAiQW1hY3JpbmVfNyIgPSAiOSIsCiAgICAgICAgICAgIkFtYWNyaW5lXzgiID0gIjEwIiwKICAgICAgICAgICAiQW1hY3JpbmVfOSIgPSAiMTEiLAogICAgICAgICAgICJBbWFjcmluZV8xMCIgPSAiMTIiLAogICAgICAgICAgICJBbWFjcmluZV8xMSIgPSAiMTMiLAogICAgICAgICAgICJBbWFjcmluZV8xMiIgPSAiMTQiLAogICAgICAgICAgICJBbWFjcmluZV8xMyIgPSAiMTUiLAogICAgICAgICAgICJBbWFjcmluZV8xNCIgPSAiMTYiLAogICAgICAgICAgICJBbWFjcmluZV8xNSIgPSAiMTciLAogICAgICAgICAgICJBbWFjcmluZV8xNiIgPSAiMTgiLAogICAgICAgICAgICJBbWFjcmluZV8xNyIgPSAiMTkiLAogICAgICAgICAgICJBbWFjcmluZV8xOCIgPSAiMjAiLAogICAgICAgICAgICJBbWFjcmluZV8xOSIgPSAiMjEiLAogICAgICAgICAgICJBbWFjcmluZV8yMCIgPSAiMjIiLAogICAgICAgICAgICJBbWFjcmluZV8yMSIgPSAiMjMiLAogICAgICAgICAgICJSb2RzIiA9ICIyNCIsCiAgICAgICAgICAgIkNvbmVzIiA9ICIyNSIsCiAgICAgICAgICAgIkJpcG9sYXJfMSIgPSAiMjYiLAogICAgICAgICAgICJCaXBvbGFyXzIiID0gIjI3IiwKICAgICAgICAgICAiQmlwb2xhcl8zIiA9ICIyOCIsCiAgICAgICAgICAgIkJpcG9sYXJfNCIgPSAiMjkiLAogICAgICAgICAgICJCaXBvbGFyXzUiID0gIjMwIiwKICAgICAgICAgICAiQmlwb2xhcl82IiA9ICIzMSIsCiAgICAgICAgICAgIkJpcG9sYXJfNyIgPSAiMzIiLAogICAgICAgICAgICJCaXBvbGFyXzgiID0gIjMzIiwKICAgICAgICAgICAiTXVsbGVyX2dsaWEiID0gIjM0IiwKICAgICAgICAgICAiQXN0cm9jeXRlcyIgPSAiMzUiLAogICAgICAgICAgICJGaWJyb2JsYXN0IiA9ICIzNiIsCiAgICAgICAgICAgIlZhc2N1bGFyX2VuZG90aGVsaXVtIiA9ICIzNyIsCiAgICAgICAgICAgIlBlcmljeXRlcyIgPSAiMzgiLAogICAgICAgICAgICJNaWNyb2dsaWEiID0gIjM5IikKYGBgCgojIENsdXN0ZXJpbmcKCiMjIEdyYXBoLWJhc2VkIGNsdXN0ZXJpbmcKCkZpcnN0LCB3ZSBkaXNjdXNzIGdyYXBoLWJhc2VkIGNsdXN0ZXJpbmcgbWV0aG9kcyBmb3Igc2NSTkEtU2VxIGRhdGEuClRoaXMgaXMgdmVyeSB3ZWxsIGV4cGxhaW5lZCBpbiB0aGUgCltPU0NBIGJvb2sgY2hhcHRlciA1LjJdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL2Jvb2tzLzMuMTQvT1NDQS5iYXNpYy9jbHVzdGVyaW5nLmh0bWwjY2x1c3RlcmluZy1ncmFwaCkuCgpBcyB3cml0dGVuIGluIHRoZSBPU0NBIGJvb2s6ICJQb3B1bGFyaXplZCBieSBpdHMgdXNlIGluIFNldXJhdCwgZ3JhcGgtYmFzZWQgCmNsdXN0ZXJpbmcgaXMgYSBmbGV4aWJsZSBhbmQgc2NhbGFibGUgdGVjaG5pcXVlIGZvciBjbHVzdGVyaW5nIGxhcmdlIHNjUk5BLXNlcSAKZGF0YXNldHMuIFdlIGZpcnN0IGJ1aWxkIGEgZ3JhcGggd2hlcmUgZWFjaCBub2RlIGlzIGEgY2VsbCB0aGF0IGlzIGNvbm5lY3RlZCAKdG8gaXRzIG5lYXJlc3QgbmVpZ2hib3JzIGluIHRoZSBoaWdoLWRpbWVuc2lvbmFsIHNwYWNlLiBFZGdlcyBhcmUgd2VpZ2h0ZWQgCmJhc2VkIG9uIHRoZSBzaW1pbGFyaXR5IGJldHdlZW4gdGhlIGNlbGxzIGludm9sdmVkLCB3aXRoIGhpZ2hlciB3ZWlnaHQgZ2l2ZW4gCnRvIGNlbGxzIHRoYXQgYXJlIG1vcmUgY2xvc2VseSByZWxhdGVkLiBXZSB0aGVuIGFwcGx5IGFsZ29yaXRobXMgdG8gaWRlbnRpZnkgCuKAnGNvbW11bml0aWVz4oCdIG9mIGNlbGxzIHRoYXQgYXJlIG1vcmUgY29ubmVjdGVkIHRvIGNlbGxzIGluIHRoZSBzYW1lIGNvbW11bml0eSAKdGhhbiB0aGV5IGFyZSB0byBjZWxscyBvZiBkaWZmZXJlbnQgY29tbXVuaXRpZXMuIEVhY2ggY29tbXVuaXR5IHJlcHJlc2VudHMgYSAKY2x1c3RlciB0aGF0IHdlIGNhbiB1c2UgZm9yIGRvd25zdHJlYW0gaW50ZXJwcmV0YXRpb24uCgpUaGUgbWFqb3IgYWR2YW50YWdlIG9mIGdyYXBoLWJhc2VkIGNsdXN0ZXJpbmcgbGllcyBpbiBpdHMgc2NhbGFiaWxpdHkuIEl0IG9ubHkgCnJlcXVpcmVzIGEgay1uZWFyZXN0IG5laWdoYm9yIHNlYXJjaCB0aGF0IGNhbiBiZSBkb25lIGluIGxvZy1saW5lYXIgdGltZSBvbiAKYXZlcmFnZSwgaW4gY29udHJhc3QgdG8gaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgbWV0aG9kcyB3aXRoIHJ1bnRpbWVzIHRoYXQgYXJlIApxdWFkcmF0aWMgd2l0aCByZXNwZWN0IHRvIHRoZSBudW1iZXIgb2YgY2VsbHMuIEdyYXBoIGNvbnN0cnVjdGlvbiBhdm9pZHMgbWFraW5nIApzdHJvbmcgYXNzdW1wdGlvbnMgYWJvdXQgdGhlIHNoYXBlIG9mIHRoZSBjbHVzdGVycyBvciB0aGUgZGlzdHJpYnV0aW9uIG9mIGNlbGxzIAp3aXRoaW4gZWFjaCBjbHVzdGVyLCBjb21wYXJlZCB0byBvdGhlciBtZXRob2RzIGxpa2Ugay1tZWFucyAodGhhdCBmYXZvciAKc3BoZXJpY2FsIGNsdXN0ZXJzKSBvciBHYXVzc2lhbiBtaXh0dXJlIG1vZGVscyAodGhhdCByZXF1aXJlIG5vcm1hbGl0eSkuIgoKU2V2ZXJhbCBncmFwaC1iYXNlZCBjbHVzdGVyaW5nIGFsZ29yaXRobXMgYXJlIGltcGxlbWVudGVkIGluIHRoZSBgc2NyYW5gIGxpYnJhcnkuIApUaGUgbW9zdCBnbG9iYWwgd3JhcHBlci1mdW5jdGlvbiBpbiB0aGlzIHBhY2thZ2UgaXMgdGhlIGBjbHVzdGVyQ2VsbHNgIGZ1bmN0aW9uLiAKVHlwaWNhbGx5LCB0aGUgaW5wdXQgdG8gdGhpcyBmdW5jdGlvbiBpcyBhIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0IHdpdGggCnByZS1jb21wdXRlZCBwcmluY2lwYWwgY29tcG9uZW50czsgdGhlc2UgYXJlIHVzZWQgdG8gdGFrZSBhZHZhbnRhZ2Ugb2YgZGF0YSAKY29tcHJlc3Npb24gYW5kIGRlbm9pc2luZy4gSWYgdGhlIGRlZmF1bHQgc2V0dGluZ3MgYXJlIGFkb3B0ZWQsIGBjbHVzdGVyQ2VsbHNgCndpbGwgcGVyZm9ybSB0d28gc3RlcHMgdW5kZXIgdGhlIGhvb2Q6CgoxLiBCdWlsZCBhIHNoYXJlZCBuZWFyZXN0IG5laWdoYm9ycyAoU05OKSBncmFwaCBvZiBvYnNlcnZhdGlvbnMgZm9yIGRvd25zdHJlYW0gY29tbXVuaXR5IApkZXRlY3Rpb24uIFRoZSBTTk4gZ3JhcGggaXMgY2xvc2VseSByZWxhdGVkIHRvIHRoZSBtb3JlIGNvbW1vbiBLTk4gZ3JhcGguIEZvciAKZWFjaCBvYnNlcnZhdGlvbiwgaXRzIGstbmVhcmVzdCBuZWlnaGJvcnMgYXJlIGlkZW50aWZpZWQgKGs9MTAgYnkgZGVmYXVsdCksIApiYXNlZCBvbiBkaXN0YW5jZXMgYmV0d2VlbiB0aGVpciBleHByZXNzaW9uIHByb2ZpbGVzIChFdWNsaWRlYW4gZGlzdGFuY2VzIGFyZSAKdXNlZCBieSBkZWZhdWx0KSBhcyBvYnNlcnZlZCBpbiBQQ0Egc3BhY2UuIEFuIGVkZ2UgaXMgZHJhd24gYmV0d2VlbiBhbGwgcGFpcnMgCm9mIG9ic2VydmF0aW9ucyB0aGF0IHNoYXJlIGF0IGxlYXN0IG9uZSBuZWlnaGJvciwgd2VpZ2h0ZWQgYnkgdGhlIGNoYXJhY3RlcmlzdGljcyAKb2YgdGhlIHNoYXJlZCBuZWFyZXN0IG5laWdoYm9ycy4KCjIuIFRoZSBgY2x1c3RlckNlbGxzYCBmdW5jdGlvbiBuZXh0IGludGVybmFsbHkgY2FsbHMgdGhlIGBjbHVzdGVyX3dhbGt0cmFwYCBmdW5jdGlvbiAKZnJvbSB0aGUgYGlncmFwaGAgbGlicmFyeS4gQmFzZWQgb24gdGhlIFNOTiBncmFwaCBmcm9tIHN0ZXAgMSwgdGhpcyBmdW5jdGlvbiB0cmllcwp0byBmaW5kIGRlbnNlbHkgY29ubmVjdGVkIHN1YmdyYXBocywgYWxzbyBjYWxsZWQgY29tbXVuaXRpZXMgaW4gYSBncmFwaCB2aWEgCnJhbmRvbSB3YWxrcy4gVGhlIGlkZWEgaXMgdGhhdCBzaG9ydCByYW5kb20gd2Fsa3MgdGVuZCB0byBzdGF5IGluIHRoZSBzYW1lIApjb21tdW5pdHkuCgpgYGAKIyBEbyBub3QgcnVuOyBjbHVzdGVyQ2VsbHMgZnVuY3Rpb24gd2l0aCBkZWZhdWx0IHNldHRpbmdzLCBpLmUuLCBidWlsZGluZyBhbgojIFNOTiBncmFwaCBhbmQgZmluZGluZyBjbHVzdGVycyB3aXRoIHRoZSB3YWxrdHJhcCBhbGdvcml0aG0uCmxpYnJhcnkoc2NyYW4pCm5uLmNsdXN0ZXJzIDwtIGNsdXN0ZXJDZWxscyhzY2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlLmRpbXJlZD0iUENBIikKdGFibGUobm4uY2x1c3RlcnMpCmBgYAoKVGhlIGRpc2FkdmFudGFnZSBvZiB1c2luZyBgY2x1c3RlckNlbGxzYCBpcyB0aGF0IHRoZSBkZWZhdWx0IHNldHRpbmcgb2YgdGhlIApzZWNvbmQgc3RlcCwgdGhlIGBjbHVzdGVyX3dhbGt0cmFwYCBmdW5jdGlvbiwgaXMgc2xvdyBmb3IgbGFyZ2UgZGF0YXNldHMuIFdoaWxlIAppdCBpcyBwb3NzaWJsZSB0byBhZGp1c3QgdGhlIGRpZmZlcmVudCBhcmd1bWVudHMgb2YgdGhlIGBjbHVzdGVyQ2VsbHNgIGZ1bmN0aW9uLAppdCBtaWdodCBiZSBtb3JlIGNsZWFyIGFuZCBpbnR1aXRpdmUgdG8gc2ltcGx5IGJyZWFrIHVwIHRoZSBwcm9jZXNzIGluIHR3byBzdGVwczoKYnVpbGRpbmcgdGhlIGdyYXBoIGFuZCBkZXRlY3RpbmcgY2x1c3RlcnMgaW4gdGhhdCBncmFwaC4gRm9yIHRoaXMgc2Vjb25kIHN0ZXAsIAp3ZSBtYXkgdGhlbiBhZG9wdCBhIGZhc3RlciBhbGdvcml0aG0uCgojIyMgQnVpbGQgZ3JhcGggKFNOTiBncmFwaCkKCmBgYHtyLGV2YWw9RkFMU0V9CiMgQnVpbGQgYSBzaGFyZWQgbmVhcmVzdC1uZWlnaGJvciBncmFwaCBmcm9tIFBDQSBzcGFjZQpncmFwaCA8LSBidWlsZFNOTkdyYXBoKHggPSAuLi4sICMgb3VyIFNDRSBvYmplY3QKICAgICAgICAgICAgICAgICAgICAgICB1c2UuZGltcmVkID0gLi4uKSAjIAlBIHN0cmluZyBzcGVjaWZ5aW5nIHdoaWNoIGV4aXN0aW5nIHZhbHVlcyBpbiByZWR1Y2VkRGltcyh4KSBzaG91bGQgYmUgdXNlZC4KIyBhbHRlcm5hdGl2ZTogYnVpbGRLTk5HcmFwaCgpCmBgYAoKIyMjIERldGVjdCBjbHVzdGVycyBvbiB0aGUgZ3JhcGgKClR3byBwb3B1bGFyIGdyYXBoLWJhc2VkIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIGFyZSB0aGUgYGxlaWRlbmAgYW5kIGBsb3V2YWluYCAKYWxnb3JpdGhtcywgYm90aCByZWZlcnJpbmcgdG8gdGhlIGxvY2F0aW9uIG9mIGl0cyBkZXZlbG9wZXJzLiBBIGNvbW1vbiAKaW1wbGVtZW50YXRpb24gb2YgdGhlIApbYGxvdXZhaW5gYWxnb3JpdGhtXShodHRwczovL2lvcHNjaWVuY2UuaW9wLm9yZy9hcnRpY2xlLzEwLjEwODgvMTc0Mi01NDY4LzIwMDgvMTAvUDEwMDA4KSAKaXMgdG8gb3B0aW1pemUgdGhlIG1vZHVsYXJpdHksIGVmZmVjdGl2ZWx5IGF0dGVtcHRpbmcgdG8gbWF4aW1pemUgdGhlIGRpZmZlcmVuY2UgCmJldHdlZW4gdGhlIG9ic2VydmVkIG51bWJlciBvZiBlZGdlcyBpbiBhIGNvbW11bml0eSBhbmQgdGhlIGV4cGVjdGVkIG51bWJlciBvZiAKc3VjaCBlZGdlcy4KCkhvd2V2ZXIsIGFkZGl0aW9uYWwgZXZhbHVhdGlvbnMgZm91bmQgdGhhdCBtb2R1bGFyaXR5IG9wdGltaXphdGlvbiB1c2luZyB0aGUgCmBsb3V2YWluYCBhbGdvcml0aG0gaXMgY29uZmluZWQgdG8gYSAKW3Jlc29sdXRpb24gbGltaXRdKGh0dHBzOi8vd3d3LnBuYXMub3JnL2NvbnRlbnQvMTA0LzEvMzYpLCBhbmQgaW4gYWRkaXRpb24gbWF5IApyZXN1bHQgaW4gY29tbXVuaXRpZXMgdGhhdCBhcmUgbm90IHdlbGwgY29ubmVjdGVkLiAKVGhlIFtgbGVpZGVuYCBhbGdvcml0aG1dKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTk4LTAxOS00MTY5NS16KSwgCmluc3RlYWQsIGd1YXJhbnRlZXMgd2VsbC1jb25uZWN0ZWQgY29tbXVuaXRpZXMuCgpgYGB7cixldmFsPUZBTFNFfQpzZXQuc2VlZCg0NjQ2ODgpCiMgV2Fsa3RyYXAgY29tbXVuaXR5IGZpbmRpbmcgYWxnb3JpdGhtIG9uIHRoZSBTTk4gZ3JhcGgKIyBETyBOT1QgUlVOIC0+IHRha2VzIDIwIG1pbnV0ZXMKIyBjbHVzdGVyX3dhbGt0cmFwIDwtIGZhY3RvcihpZ3JhcGg6OmNsdXN0ZXJfd2Fsa3RyYXAoZykkbWVtYmVyc2hpcCkgIzIwbWluCgojIFRoZSBgY2x1c3Rlcl9mYXN0X2dyZWVkeWAgZnVuY3Rpb24gdHJpZXMgdG8gZmluZCBkZW5zZSBzdWJncmFwaCwgYWxzbyBjYWxsZWQgCiMgY29tbXVuaXRpZXMgaW4gZ3JhcGhzIHZpYSBkaXJlY3RseSBvcHRpbWl6aW5nIGEgbW9kdWxhcml0eSBzY29yZQojIERPIE5PVCBSVU4gLT4gdGFrZXMgNCBtaW51dGVzCiMgY2x1c3Rlcl9mYXN0R3JlZWR5IDwtIGZhY3RvcihpZ3JhcGg6OmNsdXN0ZXJfZmFzdF9ncmVlZHkoZ3JhcGgpJG1lbWJlcnNoaXApICM0bWluCgojIExvdXZhaW4gY2x1c3RlcmluZyBvbiB0aGUgU05OIGdyYXBoCmNsdXN0ZXJfbG91dmFpbiA8LSBmYWN0b3IoaWdyYXBoOjpjbHVzdGVyX2xvdXZhaW4oZ3JhcGgpJG1lbWJlcnNoaXApICM4c2VjCm5sZXZlbHMoY2x1c3Rlcl9sb3V2YWluKSAjIDExIGNsdXN0ZXJzCgojIExlaWRlbiBjbHVzdGVyaW5nIG9uIHRoZSBTTk4gZ3JhcGgKY2x1c3Rlcl9sZWlkZW4gPC0gLi4uICMxMHNlYwpubGV2ZWxzKGNsdXN0ZXJfbGVpZGVuKSAjIDEzMjYgZGlmZmVyZW50IGNsdXN0ZXJzIQoKIyBvYnRhaW4gYSBsZXNzIG92ZXJseSBmaW5lLWdyYWluZWQgY2x1c3RlcmluZwpjbHVzdGVyX2xlaWRlbjIgPC0gZmFjdG9yKC4uLihncmFwaCA9IC4uLiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x1dGlvbl9wYXJhbWV0ZXIgPSAwLjAxKSRtZW1iZXJzaGlwKSAjMTBzZWMKbmxldmVscyhjbHVzdGVyX2xlaWRlbjIpICMxNCBkaWZmZXJlbnQgY2x1c3RlcnMKCiMgYWRkIGNsdXN0ZXJpbmdzIHRvIGNvbERhdGEKY29sRGF0YShzY2UpJGNsdXN0ZXJfbG91dmFpbiA8LSAuLi4KY29sRGF0YShzY2UpJGNsdXN0ZXJfbGVpZGVuMiA8LSAuLi4KYGBgCgojIyMgQ29tcGFyaW5nIGNsdXN0ZXJpbmcgc3RyYXRlZ2llcwoKQSBkaXJlY3QgY29tcGFyaXNvbiBvZiB0aGUgTG91dmFpbiBhbmQgTGVpZGVuIGNsdXN0ZXJpbmcgcmVzdWx0cyB1c2luZyBhIHRhYmxlIApvZiB0aGUgY2x1c3RlciBsYWJlbHMsIHNob3dzIGdvb2QgYWdyZWVtZW50LgoKYGBge3IsZXZhbD1GQUxTRX0KdGFibGUoLi4uLCAuLi4pICMgY29tcGFyZSBMb3V2YWluIGFuZCBMZWlkZW4yIGNsdXN0ZXJpbmcKYGBgCiAgICAgICAgICAgICAKSW50ZXJwcmV0IHRoZSByZXN1bHQgb2YgdGhlIGNvbXBhcmlzb24gYmV0d2VlbiB0aGUgdHdvIGNsdXN0ZXJpbmcgc3RyYXRlZ2llcy4KSXMgdGhlcmUgYSBzdHJvbmcgY29ycmVzcG9uZGVuY2UgYmV0d2VlbiB0aGUgc3RyYXRlZ2llcz8KClRvIG1ha2UgYSB2aXN1YWxpemF0aW9uIHRoYXQgZ2l2ZXMgdXMgdmVyeSBzaW1pbGFyIGluZm9ybWF0aW9uLCB3ZSBtYXkgdXNlIGEgCmhlYXRtYXA6CgpgYGB7cixldmFsPUZBTFNFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KI2luc3RhbGwucGFja2FnZXMoInBoZWF0bWFwIikgIyBpZiBub3QgeWV0IGluc3RhbGxlZApsaWJyYXJ5KHBoZWF0bWFwKQpwaGVhdG1hcDo6cGhlYXRtYXAodGFibGUoLi4uLCAuLi4pKSAjIGhlYXRtYXAgdG8gdmlzdWFsaXplIHRoZSB0YWJ1bGFyIGNvbXBhcmlzb24gb2YgYWJvdmUgCmBgYAoKQWdhaW4sIGludGVycHJldCB0aGUgcmVzdWx0IG9mIHRoZSBjb21wYXJpc29uIGJldHdlZW4gdGhlIHR3byBjbHVzdGVyaW5nIApzdHJhdGVnaWVzLgoKQWx0ZXJuYXRpdmVseSwgd2UgbWF5IGNvbXB1dGUgYSBjbHVzdGVyaW5nIHNpbWlsYXJpdHkgc2NvcmUgdGhhdCBjYXB0dXJlcyB0aGUgCmFncmVlbWVudCBiZXR3ZWVuIHR3byBzZXRzIG9mIHBhcnRpdGlvbnMuIFRoZSBBZGp1c3RlZCBSYW5kIEluZGV4IChBUkkpIGlzIG9mdGVuCnVzZWQgaW4gdGhlIGxpdGVyYXR1cmUgZm9yIHRoaXMgcHVycG9zZS4gVGhlIEFSSSBpcyBlcXVhbCB0byAxIGlmIHRoZSB0d28gCnBhcnRpdGlvbnMgYWdyZWUgcGVyZmVjdGx5LCBhbmQgaXQgaXMgemVybyBpZiB0aGUgdHdvIHBhcnRpdGlvbnMgYXJlIHVucmVsYXRlZC4gCkluIHNvbWUgY2FzZXMsIHRoZSBBUkkgbWF5IGFsc28gYmUgbmVnYXRpdmUgaWYgdGhlIHBhcnRpdGlvbnMgYXJlIG11Y2ggbW9yZSAKZGlmZmVyZW50IHRoYW4gd2hhdCBjb3VsZCBiZSBleHBlY3RlZCBieSBjaGFuY2UuCgpgYGB7cixldmFsPUZBTFNFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KI2luc3RhbGwucGFja2FnZXMoIm1jbHVzdCIpICMgaWYgbm90IHlldCBpbnN0YWxsZWQKbGlicmFyeShtY2x1c3QpCm1jbHVzdDo6YWRqdXN0ZWRSYW5kSW5kZXgoLi4uLCAjIExvdXZhaW4gCiAgICAgICAgICAgICAgICAgICAgICAgICAgLi4uICMgTGVpZGVuKQpgYGAKCiMjIyBWaXN1YWxpemF0aW9uCgpgYGB7cixldmFsPUZBTFNFfQojIFZpc3VhbGl6YXRpb24uIEFkZCB0aGUgY2x1c3RlciBsYWJlbHMgdG8gdGhlIHByZXZpb3VzbHkgZ2VuZXJhdGVkIFRTTkUKIyBjb29yZGluYXRlcwpwbG90VFNORShzY2UsIAogICAgICAgICBjb2xvdXJfYnk9ImNsdXN0ZXJfbG91dmFpbiIpCgpwbG90VFNORShzY2UsIAogICAgICAgICBjb2xvdXJfYnk9ImNsdXN0ZXJfbGVpZGVuMiIpCmBgYAoKIyMgSy1tZWFucyBjbHVzdGVyaW5nCgpLLW1lYW5zIGlzIGEgY2x1c3RlcmluZyBhbGdvcml0aG0gdGhhdCBoYXMgYmVlbiB1c2VkIGluIG1hbnkgYXBwbGljYXRpb24gYXJlYXMuIApJbiBSLCBpdCBjYW4gYmUgYXBwbGllZCB2aWEgdGhlIGBrbWVhbnNgIGZ1bmN0aW9uLiBUeXBpY2FsbHksIGl0IGlzIGFwcGxpZWQgdG8gCmEgcmVkdWNlZCBkaW1lbnNpb25hbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgZXhwcmVzc2lvbiBkYXRhIChtb3N0IG9mdGVuIFBDQSkuIApXZSBuZWVkIHRvIGRlZmluZSB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIGluIGFkdmFuY2UuIFNpbmNlIHRoZSByZXN1bHRzIGRlcGVuZCBvbiAKdGhlIGluaXRpYWxpemF0aW9uIG9mIHRoZSBjbHVzdGVyIGNlbnRlcnMsIGl0IGlzIHR5cGljYWxseSByZWNvbW1lbmRlZCB0byBydW4gCmstbWVhbnMgd2l0aCBtdWx0aXBsZSBzdGFydGluZyBjb25maWd1cmF0aW9ucyAodmlhIHRoZSBgbnN0YXJ0YCBhcmd1bWVudCkuCkZvciByZXByb2R1Y2liaWxpdHksIHdlIGFsc28gc3Ryb25nbHkgYWR2aXNlIHRvIHNldCBhIHNlZWQuCgpgYGB7cixldmFsPUZBTFNFfQpzZXQuc2VlZCgxMjMpCgojIGs9MTAKY2x1c3Rfa21lYW5zX2sxMCA8LSBrbWVhbnMocmVkdWNlZERpbSguLi4pLCAjIGV4dHJhY3QgdGhlIHByaW5jaXBhbCBjb21wb25lbnRzIGZyb20gdGhlIFNDRSBvYmplY3QKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VudGVycyA9IC4uLiwgIyBjaG9vc2UgayA9IDEwCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zdGFydCA9IC4uLikgICMgY2hvb3NlIDUgZGlmZmVyZW50IHN0YXJ0aW5nIGNvbmZpZ3VyYXRpb25zCnRhYmxlKGNsdXN0X2ttZWFuc19rMTAkY2x1c3RlcikgIyBpbnNwZWN0IHRoZSBudW1iZXIgb2YgY2VsbHMgaW4gZWFjaCBrbWVhbnMgY2x1c3Rlcgpjb2xEYXRhKHNjZSkka21lYW5zMTAgPC0gLi4uICMgYWRkIHRvIGNvbERhdGEKcGxvdFRTTkUob2JqZWN0ID0gLi4uLCAKICAgICAgICAgY29sb3VyX2J5ID0gLi4uKSAjIGNvbG9yIHRoZSBvYnNlcnZhdGlvbnMgYWNjb3JkaW5nIHRvIHRoZSBrLW1lYW5zIGNsdXN0ZXJpbmcKCiMgcmVwZWF0IGZvciBrPTM5CmBgYAoKV2UgaGVyZSBhcmJpdHJhcmlseSBwZXJmb3JtZWQgdHdvIGstbWVhbnMgY2x1c3RlcmluZyBhbmFseXNlcywgb25jZSB3aXRoIGs9MTAgCmFuZCBvbmNlIHdpdGggaz0zOSAodGhlIG51bWJlciBvZiBjbHVzdGVycyBjb21tdW5pY2F0ZWQgYnkgdGhlIGF1dGhvcnMpLiBUaGUgCmNob2ljZSBvZiB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIGsgY2FuIGJlIGd1aWRlZCBieSBrbm93biBiaW9sb2d5LCBob3dldmVyLCBpdCAKaXMgYXJiaXRyYXJ5IGF0IGxlYXN0IHRvIHNvbWUgaW50ZXJ2YWwuCgojIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZwoKRnJvbSBbT1NDQSBib29rIGNoYXB0ZXIgNS40XShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9ib29rcy8zLjE0L09TQ0EuYmFzaWMvY2x1c3RlcmluZy5odG1sI2hpZXJhcmNoaWNhbC1jbHVzdGVyaW5nKToKCiJIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBpcyBhbiBvbGQgdGVjaG5pcXVlIHRoYXQgYXJyYW5nZXMgc2FtcGxlcyBpbnRvIGEgCmhpZXJhcmNoeSBiYXNlZCBvbiB0aGVpciByZWxhdGl2ZSBzaW1pbGFyaXR5IHRvIGVhY2ggb3RoZXIuIE1vc3QgaW1wbGVtZW50YXRpb25zCmRvIHNvIGJ5IGpvaW5pbmcgdGhlIG1vc3Qgc2ltaWxhciBzYW1wbGVzIGludG8gYSBuZXcgY2x1c3RlciwgdGhlbiBqb2luaW5nIApzaW1pbGFyIGNsdXN0ZXJzIGludG8gbGFyZ2VyIGNsdXN0ZXJzLCBhbmQgc28gb24sIHVudGlsIGFsbCBzYW1wbGVzIGJlbG9uZyB0byBhIApzaW5nbGUgY2x1c3Rlci4gVGhpcyBwcm9jZXNzIHlpZWxkcyBhIGRlbmRyb2dyYW0gdGhhdCBkZWZpbmVzIGNsdXN0ZXJzIHdpdGggCnByb2dyZXNzaXZlbHkgaW5jcmVhc2luZyBncmFudWxhcml0eS4gVmFyaWFudHMgb2YgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgCm1ldGhvZHMgcHJpbWFyaWx5IGRpZmZlciBpbiBob3cgdGhleSBjaG9vc2UgdG8gcGVyZm9ybSB0aGUgYWdnbG9tZXJhdGlvbnMuIApGb3IgZXhhbXBsZSwgY29tcGxldGUgbGlua2FnZSBhaW1zIHRvIG1lcmdlIGNsdXN0ZXJzIHdpdGggdGhlIHNtYWxsZXN0IG1heGltdW0gCmRpc3RhbmNlIGJldHdlZW4gdGhlaXIgZWxlbWVudHMsIHdoaWxlIFdhcmTigJlzIG1ldGhvZCBhaW1zIHRvIG1pbmltaXplIHRoZSAKaW5jcmVhc2UgaW4gd2l0aGluLWNsdXN0ZXIgdmFyaWFuY2UuCgpJbiB0aGUgY29udGV4dCBvZiBzY1JOQS1zZXEsIHRoZSBtYWluIGFkdmFudGFnZSBvZiBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBsaWVzCmluIHRoZSBwcm9kdWN0aW9uIG9mIHRoZSBkZW5kcm9ncmFtLiBUaGlzIGlzIGEgcmljaCBzdW1tYXJ5IHRoYXQgcXVhbnRpdGF0aXZlbHkKY2FwdHVyZXMgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBzdWJwb3B1bGF0aW9ucyBhdCB2YXJpb3VzIHJlc29sdXRpb25zLiAKQ3V0dGluZyB0aGUgZGVuZHJvZ3JhbSBhdCBoaWdoIHJlc29sdXRpb24gaXMgYWxzbyBndWFyYW50ZWVkIHRvIHlpZWxkIGNsdXN0ZXJzIAp0aGF0IGFyZSBuZXN0ZWQgd2l0aGluIHRob3NlIG9idGFpbmVkIGF0IGEgbG93LXJlc29sdXRpb24gY3V0OyB0aGlzIGNhbiBiZQpoZWxwZnVsIGZvciBpbnRlcnByZXRhdGlvbi4iCgpJbmRlZWQsIGxvdy1yZXNvbHV0aW9uIGNsdXN0ZXJzIGNhbiB0eXBpY2FsbHkgYmUgaW50ZXJwcmV0ZWQgYXMgc3VwZXItbGV2ZWwgY2VsbAp0eXBlcywgbGlrZSBpbW11bmUgY2VsbHMsIG5ldXJvbiBjZWxscyBvciBlbmRvdGhlbGlhbCBjZWxscy4gSGlnaGVyIHJlc29sdXRpb24KY2x1c3RlcnMgY29ycmVzcG9uZCB3aXRoIGEgaGlnaGVyIGJpb2xvZ2ljYWwgcmVzb2x1dGlvbjogaW1tdW5lIGNlbGwgLT4gCmx5bXBob2N5dGUgLT4gVC1jZWxsIC0+IFRoMSBjZWxsLgoKSG93ZXZlciwgbm90ZSB0aGF0IHdlIGNhbiBhbHNvIG92ZXJjbHVzdGVyIHRoZSBkYXRhIChzcGxpdHRpbmcgYSBob21vZ2Vub3VzIApzZXQgb2YgY2VsbHMgaW4gbXVsdGlwbGUgY2x1c3RlcnMpLCByZXN1bHRpbmcgaW4gc3B1cmlvdXMgY2VsbCB0eXBlIAppZGVudGlmaWNhdGlvbi4KClRoZSBgY2x1c3RlckNlbGxzYCBmdW5jdGlvbiBvZiB0aGUgYHNjcmFuYCBsaWJyYXJ5IGFsc28gYWxsb3dzIGZvciBwZXJmb3JtaW5nIApoaWVyYXJjaGljYWwgY2x1c3RlcmluZy4gVGhpcyBjYW4gYmUgaW1wbGVtZW50ZWQgYXMgZm9sbG93czoKCmBgYAojIHRha2VzIDQgbWludXRlcwpsaWJyYXJ5KGJsdXN0ZXIpCmhjbHVzdC5zY2UgPC0gY2x1c3RlckNlbGxzKHggPSBzY2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlLmRpbXJlZCA9ICJQQ0EiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgQkxVU1BBUkFNID0gSGNsdXN0UGFyYW0obWV0aG9kPSJ3YXJkLkQyIikpCmBgYAoKRXF1aXZhbGVudGx5LCB3ZSBtYXkgYWdhaW4gc3BsaXQgdGhlIHByb2Nlc3MgaW4gdHdvIHN0ZXBzOgoKMS4gQ29tcHV0ZSB0aGUgcGFpcndpc2UgZGlzdGFuY2VzIGJldHdlZW4gYWxsIGNlbGxzLiBUaGVzZSBhcmUgYnkgZGVmYXVsdCBFdWNsaWRlYW4KZGlzdGFuY2VzIGFuZCwgaW4gb3JkZXIgdG8gcmVkdWNlIGRhdGEgY29tcGxleGl0eSBhbmQgaW5jcmVhc2Ugc2lnbmFsIHRvIG5vaXNlLCAKd2UgbWF5IHBlcmZvcm0gdGhpcyBvbiB0aGUgdG9wICgzMCkgUEPigJlzLCBqdXN0IGxpa2Ugd2UgZGlkIHdoZW4gY29uc3RydWN0aW5nIAp0aGUgU05OIGdyYXBoIGluIGdyYXBoLWJhc2VkIGNsdXN0ZXJpbmcuIENhbGN1bGF0aW5nIGEgZGlzc2ltaWxhcml0eSBtYXRyaXggCmlzIGltcGxlbWVudGVkIGluIHRoZSBgZGlzdGAgZnVuY3Rpb24uCgoyLiBQZXJmb3JtIGEgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgb24gdGhlIGRpc3RhbmNlcyBmcm9tIHN0ZXAgMS4gSW4gYW4gCmFnZ2xvbWVyYXRpdmUgcHJvY2VkdXJlLCBlYWNoIGNlbGwgaXMgZmlyc3QgYXNzaWduZWQgdG8gaXRzIG93biBjbHVzdGVyIGFuZCAKdGhlbiB0aGUgYWxnb3JpdGhtIHByb2NlZWRzIGl0ZXJhdGl2ZWx5LCBhdCBlYWNoIHN0YWdlIGpvaW5pbmcgdGhlIHR3byBtb3N0IApzaW1pbGFyIGNsdXN0ZXJzLCBjb250aW51aW5nIHVudGlsIHRoZXJlIGlzIGp1c3QgYSBzaW5nbGUgY2x1c3Rlci4gSW1wbGVtZW50ZWQgCmluIHRoZSBgaGNsdXN0YCBmdW5jdGlvbi4KCk5vdGUgdGhhdCB0aGUgYGhjbHVzdGAgZnVuY3Rpb24gYWxsb3dzIGZvciBzcGVjaWZ5aW5nIGEgIm1ldGhvZCIgYXJndW1lbnQuIApUaGUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgZGlmZmVyZW50IG1ldGhvZHMgZ29lcyBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgCnNlc3Npb24sIGJ1dCBhIGJyaWVmIGRlc2NyaXB0aW9uIGlzIHByb3ZpZGVkIGluIHRoZSBmdW5jdGlvbiBoZWxwIGZpbGUuIEluIAp0aGUgY29udGV4dCBvZiBzY1JOQS1zZXEsIHdlIHJlY29tbWVuZCB0aGUgdXNlIG9mIHRoZSBgIndhcmQuRDIiYCBtZXRob2QuCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGlzdHNjZSA8LSBkaXN0KC4uLikgIyBleHRyYWN0IHRoZSBwcmluY2lwYWwgY29tcG9uZW50cyBmcm9tIHRoZSBTQ0Ugb2JqZWN0IChydW5zIDFtaW4pCmhjbCA8LSBoY2x1c3QoZGlzdHNjZSwgCiAgICAgICAgICAgICAgbWV0aG9kID0gLi4uKSAjIHJ1bnMgM21pbgpwbG90KGhjbCwgCiAgICAgbGFiZWxzID0gRkFMU0UpICMgdmlzdWFsaXplIGRlbmRyb2dyYW0KYGBgCgpOZXh0LCBpbiBvcmRlciB0byBkZXJpdmUgYSBnaXZlbiBzZXQgb2YgY2x1c3RlciBsYWJlbHMsIHdlIG5lZWQgdG8gCiJjdXQgdGhlIHRyZWUiLCBpLmUuLCBjaG9vc2UgYXQgd2hpY2ggcmVzb2x1dGlvbiB3ZSB3YW50IHRvIApyZXBvcnQgdGhlIChjZWxsIHR5cGUpIGNsdXN0ZXJzLiBUaGlzIGNhbiBiZSBhY2hpZXZlZCB3aXRoIHRoZSBgY3V0cmVlYCAKZnVuY3Rpb24uIEFzIGFuIGlucHV0LCBgY3V0cmVlYCB0YWtlcyB0aGUgZGVuZHJvZ3JhbSBmcm9tIHRoZSBgaGNsdXN0YCBmdW5jdGlvbiAKYW5kIGEgdGhyZXNob2xkIHZhbHVlIGZvciBjdXR0aW5nIHRoZSB0cmVlLiBUaGUgbGF0dGVyIG1heSBiZSBlaXRoZXIgYGtgLCB0aGUgCm51bWJlciBvZiBjbHVzdGVycyB3ZSB3YW50IHRvIHJlcG9ydCwgb3IgYGhgLCB0aGUgaGVpZ2h0IG9mIHRoZSBkZW5kcm9ncmFtIGF0IAp3aGljaCB3ZSB3YW50IHRvIGN1dCB0aGUgdHJlZS4KCmBgYHtyLGV2YWw9RkFMU0V9CiMgY3V0IHRvIGdldCAxMCBjbHVzdGVycwpjbHVzdF9oY2xfazEwIDwtIGN1dHJlZSh0cmVlID0gLi4uLCAjIG5hbWUgb2YgY2x1c3RlcmluZyB0cmVlCiAgICAgICAgICAgICAgICAgICAgICAgIGsgPSAuLi4pICMgZGVzaXJlZCBudW1iZXIgb2YgZ3JvdXBzCnRhYmxlKGNsdXN0X2hjbF9rMTApCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0KIyBjdXQgdG8gZ2V0IDM5IGNsdXN0ZXJzCi4uLgpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CnNjZSRjbHVzdF9oY2xfazEwIDwtIC4uLiAjIGFkZCB0byBjb2xEYXRhCnNjZSRjbHVzdF9oY2xfazM5IDwtIGEuLi4gIyBhZGQgdG8gY29sRGF0YQoKIyBWaXN1YWxpemF0aW9uLiBBZGQgdGhlIGNsdXN0ZXIgbGFiZWxzIHRvIHRoZSBwcmV2aW91c2x5IGdlbmVyYXRlZCBUU05FCiMgY29vcmRpbmF0ZXMKcGxvdFRTTkUob2JqZWN0ID0gLi4uLCAKICAgICAgICAgY29sb3VyX2J5ID0gLi4uKSAjIGs9MTAKCnBsb3RUU05FKG9iamVjdCA9IC4uLiwgCiAgICAgICAgIGNvbG91cl9ieSA9IC4uLikgIyBrPTM5CmBgYAoKIyBDbHVzdGVyaW5nIGluIHRoZSBvcmlnaW5hbCBwYXBlcgoKV2hlbiB3ZSBjb21wYXJlIG91ciBjbHVzdGVyIGxhYmVscyB3aXRoIHRob3NlIGZyb20gdGhlIG9yaWdpbmFsIHBhcGVyLCB3ZSdsbApzZWUgdGhhdCB0aGUgY29ycmVzcG9uZGVuY2UgaXMgbm90IGdyZWF0LiBBcyBhIGRlbW9uc3RyYXRpb24sIEkgbWFrZSBhIHRhYmxlIGFuZCAKYSBoZWF0bWFwIGNvbXBhcmluZyB0aGUgbG93LXJlc29sdXRpb24gY2x1c3RlciBsYWJlbHMgZnJvbSB0aGUgcGFwZXIgd2l0aCBvdXIgCkxvdXZhaW4sIExlaWRlbjIgYW5kIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIChrPTEwKSBsYWJlbHM6CgpgYGB7cixldmFsPUZBTFNFfQp0YWJsZShzY2UkY2x1c3Rlcl9sb3dSZXMsIC4uLikgIyBjb21wYXJlIGxvdyByZXNvbHV0aW9uIGNsdXN0ZXJzIG9mIGF1dGhvcnMgdG8gb3VyIExvdXZhaW4gY2x1c3RlcmluZwpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9Ci4uLiAjIGNvbXBhcmUgbG93IHJlc29sdXRpb24gY2x1c3RlcnMgb2YgYXV0aG9ycyB0byBvdXIgTGVpZGVuMiBjbHVzdGVyaW5nCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0KLi4uICMgY29tcGFyZSBsb3cgcmVzb2x1dGlvbiBjbHVzdGVycyBvZiBhdXRob3JzIHRvIG91ciBoaWVyYXJjaGljYWwgKGs9MTApIGNsdXN0ZXJpbmcKYGBgCgpgYGB7cixldmFsPUZBTFNFfQojIGFsbCB0aHJlZSBjb21wYXJpc29ucyB3aXRoIGhlYXRtYXBzCnBoZWF0bWFwOjpwaGVhdG1hcCguLi4pCgpwaGVhdG1hcDo6cGhlYXRtYXAoLi4uKQoKcGhlYXRtYXA6OnBoZWF0bWFwKC4uLikKYGBgCgpBbHNvLCB3aGVuIHdlIGxvb2sgYXQgdGhlIHQtU05FIGZyb20gdGhlIG9yaWdpbmFsIHB1YmxpY2F0aW9uLCB3ZSBvYnNlcnZlCmNsZWFybHkgZGlzdGluY3QgY2x1c3RlcnM6CgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIm1hY29za29fZmlndXJlXzVCLmpwZWciKQpgYGAKClRoZSBtYWluIHJlYXNvbiBmb3IgdGhpcyBpcyB0aGF0IHRoZSBhdXRob3JzIG9mIHRoZSBvcmlnaW5hbCBwYXBlciB1c2VkIHF1aXRlIGEgCmRpZmZlcmVudCBzdHJhdGVneSBmb3IgcGVyZm9ybWluZyB0aGUgZmVhdHVyZSBzZWxlY3Rpb24gYW5kIGRpbWVuc2lvbiByZWR1Y3Rpb24gCnRoYXQgd2UgaGF2ZSBwZXJmb3JtZWQgaW4gbGFiIHNlc3Npb24gMi4KClRvIGRlbW9uc3RyYXRlIHRoZSBwaXBlbGluZSBvZiB0aGUgb3JpZ2luYWwgYXV0aG9ycywgYW5kIHRvIG1ha2Ugb3VyIHJlc3VsdHMgCm1vcmUgY29tcGFyYWJsZSB0byB0aGVpcnMsIFdlIHdpbGwgaGVyZSBtaW1pYyB0aGVpciBzdHJhdGVneSBmb3IgZmVhdHVyZSAKc2VsZWN0aW9uIGFuZCBjbHVzdGVyaW5nLiAqKkhvd2V2ZXIsIHdlIHdpbGwgZG8gdGhpcyBhcHByb3hpbWF0aXZlbHkhKiogV2Ugd2lsbCAKdGFrZSBzaW1pbGFyIHN0ZXBzIGNvbmNlcHR1YWxseSwgYnV0IHdpbGwgcmVtYWluIHdpdGhpbiB0aGUgY3VycmVudCAKQmlvY29uZHVjdG9yIGZyYW1ld29yayBhbmQgdGhlIHJhbmdlIG9mIGZ1bmN0aW9ucyB0aGF0IHdlIGhhdmUgc2VlbiBpbiB0aGlzIApsZWN0dXJlIHNlcmllcy4KClRoZSBhdXRob3JzIHBlcmZvcm1lZCB0aGUgZm9sbG93aW5nIHN0ZXBzOgoKMS4gKipGaWx0ZXJpbmc6KiogVGhlIGF1dGhvcnMgZmlyc3QgZmlsdGVyZWQgdGhlIDQ5LDMwMC1jZWxsIGRhdGFzZXQgdG8gcmV0YWluIApvbmx5IHNpbmdsZS1jZWxsIGxpYnJhcmllcyB3aGVyZSBhdCBsZWFzdCA5MDAgZ2VuZXMgd2VyZSBkZXRlY3RlZC4KCjIuICoqRmVhdHVyZSBzZWxlY3Rpb246KiogVGhlIGF1dGhvcnMgZmlyc3QgaWRlbnRpZmllZCB0aGUgc2V0IG9mIGdlbmVzIHRoYXQgCndlcmUgbW9zdCB2YXJpYWJsZSBhY3Jvc3MgdGhlIHNlbGVjdGVkIHN1c2JldCBvZiBjZWxscywgYWZ0ZXIgY29udHJvbGxpbmcgZm9yIAp0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gbWVhbiBleHByZXNzaW9uIGFuZCB2YXJpYWJpbGl0eS4gVG8gZG8gdGhpcywgdGhlIAphdXRob3JzIGFkb3B0ZWQgYSBtYW51YWwgaW1wbGVtZW50YXRpb24uCgotPiBXZSB3aWxsIG5vdCB1c2UgdGhlIGV4YWN0IHNhbWUgc3RyYXRlZ3ksIGJ1dCB0aGUgYG1vZGVsR2VuZVZhci1nZXRUb3BIVkdzYCAKc3RyYXRlZ3ksIHdoaWNoIGlzIGNvbmNlcHR1YWxseSBzaW1pbGFyIGluIGFkZHJlc3NpbmcgdGhlIG1lYW4tdmFyaWFuY2UgCnJlbGF0aW9uc2hpcCBkdXJpbmcgZmVhdHVyZSBzZWxlY3Rpb24uCgozLiAqKlByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXM6KiogVGhlIGF1dGhvcnMgcGVyZm9ybWVkIFBDQSBhZnRlciBzY2FsaW5nIAp0aGUgZGF0YS4gVGhleSBuZXh0IHBlcmZvcm1lZCBhIHRlc3QgdG8gZGV0ZXJtaW5lIGhvdyBtYW55IFBDcyBjb250cmlidXRlZCAKc2lnbmlmaWNhbnRseSB0byBleHBsYWluaW5nIHRoZSB2YXJpYWJpbGl0eSBpbiB0aGUgZGF0YS4gQmFzZWQgb24gdGhpcyB0ZXN0LCAKdGhleSBjb250aW51ZWQgdGhlIGFuYWx5c2lzIHBpcGVsaW5lIHdpdGggdGhlIHRvcCAzMiBQQ3MuCgo0LiAqKnQtU05FOioqIE5leHQsIHRoZSBhdXRob3JzIHBlcmZvcm1lZCBhIHQtU05FIG9uIHRoZSB0b3AgMzIgUEPigJlzLCBzZXR0aW5nIAp0aGUgYHBlcnBsZXhpdHlgIHBhcmFtZXRlciBvZiB0aGUgdC1TTkUgYWxnb3JpdGhtIHRvIDMwLgoKNS4gKipQcm9qZWN0aW9uIG9mIHJlbWFpbmluZyBjZWxscyBhbmQgY2x1c3RlcmluZzoqKiBGaW5hbGx5LCB0aGUgYXV0aG9ycyAKYWRvcHRlZCBhIG1hbnVhbGx5IGltcGxlbWVudGVkLCByYXRoZXIgY29tcGxleCBzdHJhdGVneSB0byBwcm9qZWN0IHRoZSAKcmVtYWluaW5nIGNlbGxzICh3aGVyZSBsZXNzIHRoYW4gOTAwIGRpZmZlcmVudCBnZW5lcyB3ZXJlIGRldGVjdGVkKSBvbiB0aGUgCnQtU05FIGVtYmVkZGluZyBvYnRhaW5lZCBpbiBzdGVwIDQuIE5leHQsIHRoZXkgY2x1c3RlciB0aGUgY2VsbHMgdXNpbmcgYSAKZGVuc2l0eSBjbHVzdGVyaW5nIChEQlNDQU4gYWxnb3JpdGhtKSB0aGF0IHdhcyBub3QgZGlzY3Vzc2VkIGluIHRoaXMgbGVjdHVyZSAKc2VyaWVzLiBCZWNhdXNlIHRoaXMgNXRoIHN0ZXAgdXNlcyB0ZWNobmlxdWVzIGJleW9uZCB0aGUgc2NvcGUgb2YgdGhpcyBjb3Vyc2UsIAp3ZSB3aWxsIHNpbXBseSBjb250aW51ZSB3b3JraW5nIHdpdGggdGhlIGZpbHRlcmVkIGRhdGFzZXQgYW5kIHBlcmZvcm0gCmhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLiBIb3dldmVyLCB3ZSBpbmNsdWRlZCBzb21lIGNvZGUgdGhhdCBhbGxvd3MgeW91IHRvIGRvIApzb21ldGhpbmcgc2ltaWxhciB0byB3aGF0IHRoZSBhdXRob3JzIGRpZCBmb3IgeW91ciByZWZlcmVuY2UgKG5vdGUgdGhhdCBydW5uaW5nICAKdGhpcyBjb2RlIHJlcXVpcmVzIGluc3RhbGxpbmcgdGhlIGBzbmlmdGVyYCBSIHBhY2thZ2UsIHdoaWNoIHJlcXVpcmVzIGEgd29ya2luZwpQeXRob24gYW5kIENvbmRhIGluc3RhbGxhdGlvbikuCgpgYGB7cixldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGNvZGUgZm9yIHN0ZXBzIDEtNApsaWJyYXJ5KHNjYXRlcikKbGlicmFyeShnZW5lZmlsdGVyKQpsaWJyYXJ5KHNjcmFuKQoKIyBTdGVwIDE6IERvd25zYW1wbGluZwpzY2VfOTAwIDwtIHNjZVssc2NlJGRldGVjdGVkID4gOTAwXQoKIyBTdGVwIDI6IEZlYXR1cmUgc2VsZWN0aW9uCnNjZV85MDAgPC0gbG9nTm9ybUNvdW50cyhzY2VfOTAwKQpkZWNfOTAwIDwtIG1vZGVsR2VuZVZhcihzY2VfOTAwKQpodmdfOTAwIDwtIGdldFRvcEhWR3MoZGVjXzkwMCwKICAgICAgICAgICAgICAgICAgICAgIG4gPSAzNzQpICMgc2FtZSBudW1iZXIgb2YgdG9wIGZlYXR1cmVzIGFzIG9yaWdpbmFsIHBhcGVyCgojIFN0ZXAgMzogUENBCnNldC5zZWVkKDEyMzQpCnNjZV85MDAgPC0gcnVuUENBKHNjZV85MDAsIAogICAgICAgICAgICAgICAgICBuY29tcG9uZW50cyA9IDMyLCAjIHNhbWUgbnVtYmVyIG9mIFBDcyBhcyBvcmlnaW5hbCBwYXBlcgogICAgICAgICAgICAgICAgICBzdWJzZXRfcm93ID0gaHZnXzkwMCwKICAgICAgICAgICAgICAgICAgc2NhbGU9VFJVRSkgIyBzY2FsZSB0aGUgZGF0YSBsaWtlIGluIG9yaWdpbmFsIHBhcGVyCgojIFN0ZXAgNDogVC1TTkUKc2V0LnNlZWQoNDg0ODU0KQpzY2VfOTAwIDwtIHJ1blRTTkUoc2NlXzkwMCwgCiAgICAgICAgICAgICAgIGRpbXJlZCA9ICdQQ0EnLAogICAgICAgICAgICAgICBuX2RpbXJlZCA9IDMyLAogICAgICAgICAgICAgICBwZXJwbGV4aXR5ID0gMzApICMgc2FtZSBwZXJwbGV4aXR5IGFzIG9yaWdpbmFsIHBhcGVyCmBgYAoKYGBgCiMgU3RlcCA1IGF1dGhvcnMgKGp1c3QgZm9yIHlvdXIgcmVmZXJlbmNlKTogcHJvamVjdCBuZXcgY2VsbHMgb24gdC1TTkUgZW1iZWRkaW5nCiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgic25pZnRlciIpCmxpYnJhcnkoc25pZnRlcikKdHNuZTEgPC0gc25pZnRlcjo6Zml0c25lKHJlZHVjZWREaW0oc2NlXzkwMCwgdHlwZT0iUENBIikpCmVtYmVkZGluZyA8LSByZWR1Y2VkRGltKHNjZVtodmdfOTAwLHNjZSRkZXRlY3RlZD45MDBdLCB0eXBlPSJQQ0EiKQoKZ2dwbG90KCkgKwogIGFlcyh0c25lMVssIDFdLCB0c25lMVssIDJdLCBjb2xvdXIgPSBhcy5mYWN0b3Ioc2NlWyxzY2UkZGV0ZWN0ZWQ+OTAwXSRjbHVzdGVyKSkgKwogIGdlb21fcG9pbnQocGNoID0gMTkpICsKICBzY2FsZV9jb2xvdXJfZGlzY3JldGUobmFtZSA9ICJDbHVzdGVyIikgKwogIGxhYnMoeCA9ICJ0LVNORSAxIiwgeSA9ICJ0LVNORSAyIikgKwogIHRoZW1lX2J3KCkKCm5ld19jb29yZHMgPC0gcHJvamVjdCh0c25lMSwgCiAgICAgICAgICAgICAgICAgICAgICBuZXcgPSByZWR1Y2VkRGltKHNjZVssc2NlJGRldGVjdGVkPD05MDBdLCB0eXBlPSJQQ0EiKSwgCiAgICAgICAgICAgICAgICAgICAgICBvbGQgPSByZWR1Y2VkRGltKHNjZVssc2NlJGRldGVjdGVkPjkwMF0sIHR5cGU9IlBDQSIpKQpnZ3Bsb3QoKSArCiAgICBnZW9tX3BvaW50KAogICAgICAgIGFlcyh0c25lMVssIDFdLCB0c25lMVssIDJdLAogICAgICAgICAgICBjb2xvdXIgPSBhcy5mYWN0b3Ioc2NlWyxzY2UkZGV0ZWN0ZWQ+OTAwXSRjbHVzdGVyKSwKICAgICAgICAgICAgc2hhcGUgPSAiZW1iZWRkaW5nIgogICAgICAgICkKICAgICkgKwogICAgZ2VvbV9wb2ludCgKICAgICAgICBhZXMobmV3X2Nvb3Jkc1ssIDFdLCBuZXdfY29vcmRzWywgMl0sIAogICAgICAgICAgICBjb2xvdXIgPSBhcy5mYWN0b3Ioc2NlWyxzY2UkZGV0ZWN0ZWQ8PTkwMF0kY2x1c3RlciksCiAgICAgICAgICAgIHNoYXBlID0gInByb2plY3Rpb24iCiAgICAgICAgKQogICAgKSArCiAgICBzY2FsZV9jb2xvdXJfZGlzY3JldGUobmFtZSA9ICJDZWxsIHR5cGUiKSArCiAgICBzY2FsZV9zaGFwZV9kaXNjcmV0ZShuYW1lID0gTlVMTCkgKwogICAgbGFicyh4ID0gInQtU05FIDEiLCB5ID0gInQtU05FIDIiKSArCiAgICB0aGVtZV9idygpCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0KIyBTdGVwIDUgZm9yIHVzOiBwZXJmb3JtIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCmRpc3RzY2UgPC0gZGlzdCguLi4pCmhjbCA8LSBoY2x1c3QodHJlZSA9IC4uLiwgCiAgICAgICAgICAgICAgbWV0aG9kID0gLi4uKQoKY2x1c3RfaGNsX2sxMCA8LSAuLi4KY2x1c3RfaGNsX2szOSA8LSAuLi4KCnNjZV85MDAkY2x1c3RfaGNsX2sxMCA8LSAuLi4Kc2NlXzkwMCRjbHVzdF9oY2xfazM5IDwtIC4uLgpgYGAKClZpc3VhbGl6ZSBvdXIgbGFiZWxzIGFuZCBjb21wYXJlIHdpdGggb3JpZ2luYWwgbGFiZWxzIGF0IHRoZSBsb3cgcmVzb2x1dGlvbgoKYGBge3IsZXZhbD1GQUxTRX0KIyB2aXN1YWxpemUgb3VyIGxhYmVscwpwbG90VFNORShvYmplY3QgPSAuLi4sCiAgICAgICAgIGNvbG91cl9ieSA9IC4uLiwKICAgICAgICAgdGV4dF9ieSA9IC4uLikKCiMgY29tcGFyZSB3aXRoIG9yaWdpbmFsIGxhYmVscwpwbG90VFNORShvYmplY3QgPSBzY2VfOTAwLAogICAgICAgICBjb2xvdXJfYnkgPSAiY2x1c3Rlcl9sb3dSZXMiLAogICAgICAgICB0ZXh0X2J5ID0gImNsdXN0ZXJfbG93UmVzIiwKICAgICAgICAgdGV4dF9zaXplID0gMykKYGBgCgpDYXJlZnVsbHkgY29tcGFyZSB0aGUgdHdvIHJlc3VsdGluZyBmaWd1cmVzIQoKYGBge3IsZXZhbD1GQUxTRX0KIyByZXBlYXQgZm9yIGhpZ2ggcmVzb2x1dGlvbiBsYWJlbHMgKDIgbmV3IHQtU05FIHBsb3RzKQouLi4KYGBgCgpDYXJlZnVsbHkgY29tcGFyZSB0aGUgdHdvIHJlc3VsdGluZyBmaWd1cmVzIQoKKipPdmVyYWxsLCB3ZSBvYnNlcnZlIGEgcmF0aGVyIHN0cm9uZyBjb3JyZXNwb25kZW5jZSBiZXR3ZWVuIG91ciBjbHVzdGVycyBhbmQqKgoqKnRob3NlIG9mIHRoZSBhdXRob3JzLioqIEFsc28gbm90ZSB0aGF0IG91ciB0LVNORSB2aXN1YWxpemF0aW9uIG5vdyByZXNlbWJsZXMKdGhlIHQtU05FIG1hcCBvZiB0aGUgb3JpZ2luYWwgYXV0aG9ycyBtdWNoIG1vcmUgY2xvc2VseToKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9tYWNvc2tvX2ZpZ3VyZV81Qi5qcGVnIikKYGBgCgojIENlbGwgdHlwZSBhbm5vdGF0aW9uCgojIyBTdXBlcnZpc2VkOiB1c2luZyBhIGxpbWl0ZWQgc2V0IG9mIGtub3duIG1hcmtlcnMKCkluIHRoZSBwdWJsaWNhdGlvbiwgdGhlIGF1dGhvcnMgYWltZWQgdG8gaWRlbnRpZnkgdGhlIGRpZmZlcmVudCBjbHVzdGVycyBpbiB0aGUKZGF0YSBieSB1c2luZyBhIHNldCBvZiAxMiB3ZWxsLWtub3duIG1vbGVjdWxhciBtYXJrZXJzOyBnZW5lcyBmb3Igd2hpY2ggdGhlCmV4cHJlc3Npb24gcHJvZmlsZSBpcyB0eXBpY2FsbHkgdmVyeSBzcGVjaWZpYywgaS5lLiwgaGlnaGx5IGV4cHJlc3NlZCBvbmx5IGluCm9uZSBzcGVjaWZpYyBjZWxsIHR5cGUuIFRoZXkgdXNlZCB0aGUgZm9sbG93aW5nIG1hcmtlcnM6IAoKYGBge3IsZXZhbD1GQUxTRX0KbWFya2VycyA8LSBjKCJMSFgxIiwgIlNMQzE3QTYiLCJQQVg2IiwiR0FEMSIsIlNMQzZBOSIsIk9QTjFNVyIsIlZTWDIiLAogICAgICAgICAgICAgIlJMQlAxIiwgIkdGQVAiLCAiUEVDQU0xIiwgIktDTko4IiwiQ1gzQ1IxIikKYGBgCgpXZSB3aWxsIGhlcmUgdmlzdWFsaXplIHRoZSBleHByZXNzaW9uIG9mIHRoZXNlIG1hcmtlcnMgaW4gdC1TTkUgc3BhY2UuCkZpcnN0LCB3ZSBjcmVhdGUgYSAiYmFzZWxpbmUiIGZpZ3VyZSwgZGlzcGxheWluZyBlYWNoIGNlbGwgaW4gMkQgc3BhY2UsIGNvbG9yZWQKaW4gYmxhY2suCgpgYGB7cixldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmdnX2hscF9kYXRhIDwtIGRhdGEuZnJhbWUoeCA9IHJlZHVjZWREaW0oc2NlXzkwMCwgdHlwZSA9ICJUU05FIilbLDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByZWR1Y2VkRGltKHNjZV85MDAsIHR5cGUgPSAiVFNORSIpWywyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyID0gc2NlXzkwMCRjbHVzdGVyKQpnZ19iYXNlIDwtIGdncGxvdChkYXRhID0gZ2dfaGxwX2RhdGFbIWlzLm5hKHNjZV85MDAkY2x1c3RlciksXSwKICAgICAgICAgICAgICAgICAgYWVzKHggPSB4LCB5ID0geSwpKSArCiAgICBnZW9tX3BvaW50KHNpemU9MC40KSArCiAgICB0aGVtZV9idygpICsKICAgIHhsYWIoIlRTTkUgMSIpICsKICAgIHlsYWIoIlRTTkUgMiIpCmdnX2Jhc2UKYGBgCgpOZXh0LCB3ZSBvYnRhaW4gdGhlIGNvdW50cyBvZiB0aGUgMTIgcHJlLXNlbGVjdGVkIG1hcmtlciBnZW5lcyBmb3IgYWxsIGNlbGxzLgoKYGBge3IsIGV2YWw9RkFMU0V9Cm1hcmtlcl9jb3VudHMgPC0gYXNzYXlzKHNjZV85MDApJGNvdW50c1ttYXJrZXJzLF0KbWFya2VyX2NvdW50cyA8LSBhcy5tYXRyaXgodChtYXJrZXJfY291bnRzKSkKYGBgCgpGaW5hbGx5LCB3ZSBtYWtlIG9uZSBmaWd1cmUgZm9yIGVhY2ggb2YgdGhlIDEyIG1hcmtlciBnZW5lcy4gVGhlIGlkZWEgaXMgdG8KZ2l2ZSBlYWNoIGNlbGwgdGhhdCBoYXMgYSBub24temVybyBleHByZXNzaW9uIG9mIHRoZSBtYXJrZXIgKGkuZS4sIGZvciB3aGljaAp0aGUgbWFya2VyIGlzIGV4cHJlc3NlZCkgYSByZWQgY29sb3JpbmcuCgpgYGB7cixldmFsPUZBTFNFfQptYXJrZXJfY291bnRzX2JpbmFyeSA8LSBtYXJrZXJfY291bnRzCm1hcmtlcl9jb3VudHNfYmluYXJ5W3doaWNoKG1hcmtlcl9jb3VudHNfYmluYXJ5ID4gMCldIDwtIDEKCmZvciAoaSBpbiBzZXFfYWxvbmcobWFya2VycykpIHsKICAgIGdnIDwtIGdncGxvdChkYXRhID0gZ2dfaGxwX2RhdGFbIWlzLm5hKHNjZV85MDAkY2x1c3RlciksXSwKICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IHgsIHkgPSB5KSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYXMuZmFjdG9yKG1hcmtlcl9jb3VudHNfYmluYXJ5WyxpXSlbIWlzLm5hKHNjZV85MDAkY2x1c3RlcildKSwgc2l6ZSA9IDAuNCkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siLCJyZWQiKSkgKwogICAgICAgIHhsYWIoIlRTTkUgMSIpICsKICAgICAgICB5bGFiKCJUU05FIDIiKSArCiAgICAgICAgZ2d0aXRsZShjb2xuYW1lcyhtYXJrZXJfY291bnRzX2JpbmFyeSlbaV0pICsKICAgICAgICB0aGVtZV9idygpICsgCiAgICAgIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKICAgIHByaW50KGdnKQp9CmBgYAoKYGBge3IsZXZhbD1GQUxTRX0KIyBWaXN1YWxpemUgb3VyIGNsdXN0ZXJzCnBsb3RUU05FKHNjZV85MDAsCiAgICAgICAgIGNvbG91cl9ieSA9ICJjbHVzdF9oY2xfazM5IiwKICAgICAgICAgdGV4dF9ieSA9ICJjbHVzdF9oY2xfazM5IikKYGBgCgpGcm9tIGZpZ3VyZSA1RCwgd2Ugb2J0YWluIHRoZSBtYXJrZXItY2VsbCB0eXBlIHJlbGF0aW9uc2hpcDoKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygibWFjb3Nrb19maWd1cmVfNUQuanBlZyIpCmBgYAoKVHJ5IHRvIGdvIGJhY2sgYW5kIGZvcnRoIGJldHdlZW4gCgoxLiB0aGUgdmlzdWFsaXphdGlvbiBvZiBtYXJrZXIgZXhwcmVzc2lvbiBvbiB0aGUgdC1TTkUgbWFwLAoyLiB0aGUgdmlzdWFsaXphdGlvbiBvZiBvdXIgY2x1c3RlciBsYWJlbHMgb24gdGhlIHQtU05FIG1hcCwgYW5kCjMuIHRoZSBtYXJrZXItY2VsbCB0eXBlIHJlbGF0aW9uc2hpcCBmcm9tIGZpZ3VyZSA1RC4KClRoaXMgc2hvdWxkIGFsbG93IHlvdSB0byBvYnRhaW4gYSBsb3ctcmVzb2x1dGlvbiBhbm5vdGF0aW9uIGZvciBtb3N0IG9mIHRoZQpjbHVzdGVycyB3ZSBoYXZlIG1hbnVhbGx5IG9idGFpbmVkIG91cnNlbHZlcyEgV2UgZ2l2ZSBhd2F5IG9uZSBvZiB0aGUgZWFzaWVzdAphbm5vdGF0aW9uczoKCi0gVGhlICpMSFgxKiBtYXJrZXIgaXMgc3BlY2lmaWMgZm9yIGhvcml6b250YWwgY2VsbHMuIEFzIHN1Y2gsIG91ciAqKmNsdXN0ZXIgMyoqCmNvcnJlc3BvbmRzIHRvICoqaG9yaXpvbnRhbCBjZWxscyoqLgoKWW91IGNhbiBkbyB0aGlzIGZvciB0aGUgb3RoZXIgbWFya2Vycy9jbHVzdGVycyAoc29tZXRpbWVzIGl0IHdpbGwgc3RpbGwgYmUgCmFtYmlndW91cykuCgojIyBTdXBlcnZpc2VkOiBVc2luZyBtYXJrZXIgZ2VuZXMgZGV0ZWN0ZWQgZnJvbSB0aGlzIGRhdGEKClNvbWV0aW1lcyBpdCB3aWxsIGJlIHZlcnkgZGlmZmljdWx0IHRvIHNldCB1cCBhIHBhbmVsIG9mIGtub3duIG1hcmtlciBnZW5lcyAKdGhhdCB3b3VsZCBhbGxvdyB1cyB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIGFsbCBjZWxsIHR5cGVzIGluIG91ciBkYXRhc2V0LgpGb3IgaW5zdGFuY2UsIHNvbWV0aW1lcyB3ZSBtYXkgbm90IGtub3cgaW4gYWR2YW5jZSB3aGljaCBjZWxsIHR5cGVzIHRvIGV4cGVjdCwKb3Igd2UgbWF5IG5vdCBoYXZlIGdvb2QgaW5mb3JtYXRpb24gb24gcmVsZXZhbnQgbWFya2VycyAoaWYgdGhlIHN0dWRpZWQgc3lzdGVtCmlzIG5vdCB3ZWxsIGtub3duKS4KCkFuIGFsdGVybmF0aXZlIHN0cmF0ZWd5IGlzIHRvIGlkZW50aWZ5IHRoZSBnZW5lcyB0aGF0IGRyaXZlIHNlcGFyYXRpb24gYmV0d2VlbiAKY2x1c3RlcnMuIFRoZXNlIG1hcmtlciBnZW5lcyBhbGxvdyB1cyB0byBhc3NpZ24gYmlvbG9naWNhbCBtZWFuaW5nIHRvIGVhY2ggCmNsdXN0ZXIgYmFzZWQgb24gdGhlaXIgZnVuY3Rpb25hbCBhbm5vdGF0aW9uLiBUaGlzIHN0cmF0ZWd5IGlzIHJlZmVycmVkIHRvIGFzIAoqKm1hcmtlciBnZW5lIGRldGVjdGlvbioqLiAKClRoZSBtb3N0IHN0cmFpZ2h0Zm9yd2FyZCBhcHByb2FjaCB0byBtYXJrZXIgZ2VuZSBkZXRlY3Rpb24gaW52b2x2ZXMgdGVzdGluZyAKZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIChERSkgYmV0d2VlbiBjbHVzdGVycy4gSWYgYSBnZW5lIGlzIHN0cm9uZ2x5IERFIApiZXR3ZWVuIGNsdXN0ZXJzLCBpdCBpcyBsaWtlbHkgdG8gaGF2ZSBkcml2ZW4gdGhlIHNlcGFyYXRpb24gb2YgY2VsbHMgaW4gdGhlIApkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24uIFRoZSBnZW5lcmFsIHN0cmF0ZWd5IGlzIHRvIGNvbXBhcmUgZWFjaCBwYWlyIG9mIGNsdXN0ZXJzIAphbmQgY29tcHV0ZSBzY29yZXMgcXVhbnRpZnlpbmcgdGhlIGRpZmZlcmVuY2VzIGluIHRoZSBleHByZXNzaW9uIGRpc3RyaWJ1dGlvbnMgCmJldHdlZW4gY2x1c3RlcnMuIFRoZSBzY29yZXMgZm9yIGFsbCBwYWlyd2lzZSBjb21wYXJpc29ucyBpbnZvbHZpbmcgYSAKcGFydGljdWxhciBjbHVzdGVyIGFyZSB0aGVuIGNvbnNvbGlkYXRlZCBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUgZm9yIHRoYXQgCmNsdXN0ZXIuIFRoaXMgYXBwcm9hY2ggaXMgaW1wbGVtZW50ZWQgaW4gdGhlIGBzY29yZU1hcmtlcnNgIGZ1bmN0aW9uIG9mIHRoZQpgc2NyYW5gIHBhY2thZ2UuCgpgYGB7cixldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHNjcmFuKQptYXJrZXIuaW5mbyA8LSBzY29yZU1hcmtlcnMoeCA9IC4uLiwgIyB0aGUgU0NFIG9iamVjdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBzID0gLi4uKSAjIG91ciBjbHVzdGVyIGlkZW50aWZpZXJzCm1hcmtlci5pbmZvICMgb25lIGRhdGFmcmFtZSBmb3IgZWFjaCBvZiB0aGUgMzkgY2x1c3RlcnMKYGBgCgpgYGB7cixldmFsPUZBTFNFfQpjb2xuYW1lcyhtYXJrZXIuaW5mb1tbIjEiXV0pICMgc3RhdGlzdGljcyBmb3IgY2x1c3RlciAxLgpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CmhlYWQobWFya2VyLmluZm9bWyIxIl1dKQpgYGAKCldlIG9ic2VydmUgc2V2ZXJhbCBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIGVhY2ggZ2VuZSBpbiB0aGUgZGF0YWZyYW1lIGZvcgpjbHVzdGVyIDEuIFdlIGhpZ2hsaWdodCBhIGZldzoKCi0gYHNlbGYuYXZlcmFnZWA6IHRoZSBhdmVyYWdlIGxvZy1ub3JtYWxpemVkIGV4cHJlc3Npb24gb2YgdGhlIGdlbmUgaW4gdGhlCnRhcmdldCBjbHVzdGVyIChjbHVzdGVyIDEpCgotIGBvdGhlci5hdmVyYWdlYDogdGhlIGF2ZXJhZ2UgbG9nLW5vcm1hbGl6ZWQgZXhwcmVzc2lvbiBvZiB0aGUgZ2VuZSBpbiBhbGwgdGhlCm90aGVyIGNsdXN0ZXJzIChjbHVzdGVycyAyLTM5KQoKLSBgc2VsZi5kZXRlY3RlZGA6IHRoZSBmcmFjdGlvbiBvZiBjZWxscyBpbiB3aGljaCB0aGUgZ2VuZSB3YXMgZXhwcmVzc2VkIGluIHRoZQp0YXJnZXQgY2x1c3RlciAoY2x1c3RlciAxKQoKLSBgb3RoZXIuZGV0ZWN0ZWRgOiB0aGUgZnJhY3Rpb24gb2YgY2VsbHMgaW4gd2hpY2ggdGhlIGdlbmUgd2FzIGV4cHJlc3NlZCBpbiBhbGwKdGhlIG90aGVyIGNsdXN0ZXJzIChjbHVzdGVyIDItMzkpCgotIGBtZWFuLkFVQ2A6IEZyb20gdGhlIApbT1NDQSBib29rIGNoYXB0ZXIgNi4zXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9ib29rcy8zLjE0L09TQ0EuYmFzaWMvbWFya2VyLWRldGVjdGlvbi5odG1sI2VmZmVjdC1zaXplcy1mb3ItcGFpcndpc2UtY29tcGFyaXNvbnMpOiAKIkluIHRoZSBjb250ZXh0IG9mIG1hcmtlciBkZXRlY3Rpb24sIHRoZSBhcmVhIHVuZGVyIHRoZSBjdXJ2ZSAoQVVDKSBxdWFudGlmaWVzIApvdXIgYWJpbGl0eSB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIHR3byBkaXN0cmlidXRpb25zIGluIGEgcGFpcndpc2UgY29tcGFyaXNvbi4gClRoZSBBVUMgcmVwcmVzZW50cyB0aGUgcHJvYmFiaWxpdHkgdGhhdCBhIHJhbmRvbWx5IGNob3NlbiBvYnNlcnZhdGlvbiBmcm9tIG91ciAKY2x1c3RlciBvZiBpbnRlcmVzdCBpcyBncmVhdGVyIHRoYW4gYSByYW5kb21seSBjaG9zZW4gb2JzZXJ2YXRpb24gZnJvbSB0aGUgb3RoZXIKY2x1c3Rlci4gQSB2YWx1ZSBvZiAxIGNvcnJlc3BvbmRzIHRvIHVwcmVndWxhdGlvbiwgd2hlcmUgYWxsIHZhbHVlcyBvZiBvdXIgCmNsdXN0ZXIgb2YgaW50ZXJlc3QgYXJlIGdyZWF0ZXIgdGhhbiBhbnkgdmFsdWUgZnJvbSB0aGUgb3RoZXIgY2x1c3RlcjsgYSAKdmFsdWUgb2YgMC41IG1lYW5zIHRoYXQgdGhlcmUgaXMgbm8gbmV0IGRpZmZlcmVuY2UgaW4gdGhlIGxvY2F0aW9uIG9mIHRoZSAKZGlzdHJpYnV0aW9uczsgYW5kIGEgdmFsdWUgb2YgMCBjb3JyZXNwb25kcyB0byBkb3ducmVndWxhdGlvbi4gVGhlIEFVQyBpcyAKY2xvc2VseSByZWxhdGVkIHRvIHRoZSBVIHN0YXRpc3RpYyBpbiB0aGUgV2lsY294b24gcmFua2VkIHN1bSB0ZXN0IChhLmsuYS4sIApNYW5uLVdoaXRuZXkgVS10ZXN0KS4iIEFzIHN1Y2gsIHRoaXMgYSB2ZXJ5IGludGVyZXN0aW5nIGNvbHVtbiB0byB1c2UgZm9yCnNlbGVjdGluZyBtYXJrZXIgZ2VuZXMuCgpCYXNlZCBvbiB0aGUgYG1lYW4uQVVDYCBzdGF0aXN0aWMsIHdlIG1heSBub3cgaW5zcGVjdCB0aGUgdG9wMTAgbWFya2VycyB0bwpkaXN0aW5ndWlzaCBiZXR3ZWVuIGNlbGxzIG9mIGNsdXN0ZXIgMSBhbmQgY2VsbHMgb2YgdGhlIG90aGVyIGNsdXN0ZXJzOgoKYGBge3IsZXZhbD1GQUxTRX0KY2hvc2VuIDwtIG1hcmtlci5pbmZvW1siMSJdXQpvcmRlcmVkIDwtIGNob3NlbltvcmRlcihjaG9zZW4kbWVhbi5BVUMsIGRlY3JlYXNpbmc9VFJVRSksXQpoZWFkKG9yZGVyZWRbLGMoMTo0LDEwKV0sIG49MTApICMgc2hvd2luZyBiYXNpYyBzdGF0cyBvbmx5LCBmb3IgYnJldml0eS4KYGBgCgpXZSBjYW4gYWxzbyB2aXN1YWxpemUgdGhlIGxvZy1ub3JtYWxpemVkIGV4cHJlc3Npb24gb2YgdGhlIHRvcDEwIG1hcmtlcnMgaW4gCmVhY2ggY2VsbCwgc3RyYXRpZmllZCBvbiBjbHVzdGVyIGxhYmVsLCB1c2luZyB0aGUgYHBsb3RFeHByZXNzaW9uYCBmdW5jdGlvbgpvZiB0aGUgYHNjYXRlcmAgcGFja2FnZToKCmBgYHtyLGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoc2NhdGVyKQpwbG90RXhwcmVzc2lvbihiamVjdCA9IC4uLiwgIyB0aGUgU0NFIG9iamVjdCwgCiAgICAgICAgICAgICAgIGZlYXR1cmVzID0gLi4uLCAjIHRvcCAxMCBmZWF0dXJlcyBhY2NvcmRpbmcgdG8gdGhlIGBzY29yZU1hcmtlcnNgIHJlc3VsdHMKICAgICAgICAgICAgICAgeCA9IC4uLiwgIyBvdXIgY2x1c3RlciBsYWJlbHMgCiAgICAgICAgICAgICAgIGNvbG91cl9ieSA9IC4uLikgIyBvdXIgY2x1c3RlciBsYWJlbHMgCmBgYAoKQmFzZWQgb24gdGhlc2UgcmVzdWx0cywgd2hpY2ggbWFya2VycyB3b3VsZCB5b3UgY2hvb3NlIHRvIHVuYW1iaWd1b3VzbHkgCmRpc3Rpbmd1aXNoIGNlbGxzIGZyb20gY2x1c3RlciAxIGZyb20gY2VsbHMgb2YgdGhlIG90aGVyIGNsdXN0ZXJzPwoKRG9lcyB0aGUgbmFtZSBvZiB0aGUgOXRoIG1hcmtlciBnZW5lIHJpbmcgYW55IGJlbGxzPwoKIyMgU2VtaS1zdXBlcnZpc2VkIHVzaW5nIFNpbmdsZVIKCkEgY29uY2VwdHVhbGx5IHN0cmFpZ2h0Zm9yd2FyZCBhbm5vdGF0aW9uIGFwcHJvYWNoIGlzIHRvIGNvbXBhcmUgb3VyIGN1cnJlbnQKc2NSTkEtc2VxIGRhdGFzZXQgd2l0aCBhIHByZXZpb3VzbHkgYW5ub3RhdGVkIHJlZmVyZW5jZSBkYXRhc2V0LiBMYWJlbHMgY2FuIAp0aGVuIGJlIGFzc2lnbmVkIHRvIGVhY2ggY2VsbCBpbiB0aGUgTWFjb3NrbyBkYXRhc2V0IGJhc2VkIG9uIHRoZSAKbW9zdCBzaW1pbGFyIHJlZmVyZW5jZSBjZWxscywgZm9yIHNvbWUgZGVmaW5pdGlvbiBvZiAic2ltaWxhciIuIFRoaXMgaXMgYSAKc3RhbmRhcmQgY2xhc3NpZmljYXRpb24gY2hhbGxlbmdlIHRoYXQgY2FuIGJlIHRhY2tsZWQgYnkgc3RhbmRhcmQgbWFjaGluZSAKbGVhcm5pbmcgdGVjaG5pcXVlcyBzdWNoIGFzIHJhbmRvbSBmb3Jlc3RzIGFuZCBzdXBwb3J0IHZlY3RvciBtYWNoaW5lcy4gQW55IApwdWJsaXNoZWQgYW5kIGxhYmVsZWQgUk5BLXNlcSBkYXRhc2V0IChidWxrIG9yIHNpbmdsZS1jZWxsKSBjYW4gYmUgdXNlZCBhcyBhIApyZWZlcmVuY2UsIHRob3VnaCBpdHMgcmVsaWFiaWxpdHkgZGVwZW5kcyBncmVhdGx5IG9uIHRoZSBleHBlcnRpc2Ugb2YgdGhlIApvcmlnaW5hbCBhdXRob3JzIHdobyBhc3NpZ25lZCB0aGUgbGFiZWxzIGluIHRoZSBmaXJzdCBwbGFjZSBhbmQgdGhlIGNsb3Nlcgp0aGUgcmVmZXJlbmNlIGRhdGFzZXQgaXMgdG8gdGhlIGRhdGFzZXQgd2Ugd291bGQgbGlrZSB0byBhbm5vdGF0ZSAoZS5nLiwgCmZ1bGwtbGVuZ3RoIHZzIFVNSS1iYXNlZCBwcm90b2NvbCksIHRoZSBtb3JlIGFjY3VyYXRlIHRoZSBhbm5vdGF0aW9uIHdpbGwgdHlwaWNhbGx5IGJlLgoKSW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIHBlcmZvcm0gc3VjaCBsYWJlbCB0cmFuc2ZlciBiZXR3ZWVuIHRoZSBhbm5vdGF0ZWQKcmVmZXJlbmNlIGRhdGFzZXQgZnJvbSAKW1NoZWtoYXIgZXQgYWwuXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmNlbGwuMjAxNi4wNy4wNTQpLCB3aGljaCBhbHNvIGlzIAphIHNjUk5BLXNlcSBkYXRhc2V0IHRoYXQgc3R1ZGllZCB0aGUgbW91c2UgcmV0aW5hLCBhbmQgdGhlIE1hY29za28gZGF0YXNldC4gCgpUbyBwZXJmb3JtIHRoZSBhY3R1YWwgbGFiZWwgdHJhbnNmZXIsIHdpbGwgdXNlIHRoZSBgU2luZ2xlUmAgQmlvY29uZHVjdG9yIApwYWNrYWdlLiBGb3IgZWFjaCAidGVzdCIgY2VsbCBpbiB0aGUgTWFjb3NrbyBkYXRhc2V0LCBgc2luZ2xlUmAgd2lsbDoKCjEuIENvbXB1dGUgU3BlYXJtYW4gY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdGVzdCBjZWxsIGFuZCBlYWNoIHJlZmVyZW5jZSBjZWxsLiAKVG8gaW1wcm92ZSBzaWduYWwvbm9pc2UsIG9ubHkgbWFya2VyIGdlbmVzIGlkZW50aWZpZWQgdXNpbmcgdGhlIHJlZmVyZW5jZSAKZGF0YXNldCBhcmUgdXNlZCBmb3IgdGhpcy4KCjIuIEZvciBlYWNoIGxhYmVsIChjZWxsIHR5cGUpLCBzZXQgdGhlIHNjb3JlIGFzIHRoZSAoZGVmYXVsdCBvZikgODAlIHF1YW50aWxlIG9mClNwZWFybWFuIGNvcnJlbGF0aW9ucy4KCjMuIFRoZSBwcmVkaWN0aW9uIGlzIHRoZW4gdGhlIGxhYmVsIHdpdGggdGhlIGhpZ2hlc3Qgc2NvcmUuCgpCZWZvcmUgd2UgY2FuIHVzZSBgc2luZ2xlUmAgdG8gcGVyZm9ybSBsYWJlbCB0cmFuc2Zlciwgd2Ugd2lsbCBuZWVkIHRvIGltcG9ydCwKZXhwbG9yZSAoYnJpZWYpIGFuZCB3cmFuZ2xlIHRoZSByZWZlcmVuY2UgZGF0YXNldCBieSBTaGVraGFyICpldCBhbC4qLgoKIyMjIEltcG9ydCByZWZlcmVuY2UgZGF0YQoKVGhlIGRhdGFzZXQgYnkgU2hla2hhciAqZXQgYWwuKiBjYW4gYmUgY29udmVuaWVudGx5IGltcG9ydGVkIHVzaW5nIHRoZSAKYHNjUk5Bc2VxYCBwYWNrYWdlLgoKYGBge3IsZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShzY1JOQXNlcSkKKHJlZi5kYXRhIDwtIHNjUk5Bc2VxOjpTaGVraGFyUmV0aW5hRGF0YShlbnNlbWJsID0gVFJVRSkpCmBgYAoKIyMjIEV4cGxvcmUgbWV0YWRhdGEgb2YgcmVmZXJlbmNlIGRhdGEKCkluc3BlY3QgdGhlIGNvbERhdGEgZm9yIHRoZSBTaGVraGFyIGRhdGFzZXQuIENvbXBhcmUgdGhlIENMVVNURVIgYW5kIFNVQkNMVVNURVIKdmFyaWFibGVzIQoKIyMjIFByb2Nlc3MgcmVmZXJlbmNlIGRhdGEKCjEuIFJlbW92ZSB1bmxhYmVsZWQgY2VsbHMKCmBgYHtyLGV2YWw9RkFMU0V9CnN1bShpcy5uYShyZWYuZGF0YSRDTFVTVEVSKSkKcmVmLmRhdGEgPC0gcmVmLmRhdGFbLC13aGljaChpcy5uYShyZWYuZGF0YSRDTFVTVEVSKSldCmBgYAoKMi4gUmVtb3ZlIGRvdWJsZXRzL2NvbnRhbWluYW50IGNlbGxzCgpUaGUgb3JpZ2luYWwgYXV0aG9ycyBhbHJlYWR5IHBlcmZvcm1lZCBxdWFsaXR5IGNvbnRyb2w7IHdlIGhhdmUgY2VsbHMgd2l0aCAKY2x1c3RlciBsYWJlbCAiRG91YmxldHMvQ29udGFtaW5hbnRzIi4gTGV04oCZcyByZW1vdmUgdGhvc2U6CgpgYGB7cixldmFsPUZBTFNFfQpzdW0ocmVmLmRhdGEkQ0xVU1RFUiA9PSAuLi4pCnJlZi5kYXRhIDwtIC4uLgpgYGAKCjMuIE1ha2UgbG93ZXIgcmVzb2x1dGlvbiBjZWxsIHR5cGUgbGV2ZWxzIChmb3IgZWFzaWVyIGludGVycHJldGF0aW9uKQoKYGBge3IsZXZhbD1GQUxTRX0KcmVmLmRhdGEkQ0xVU1RFUl9sb3dSZXMgPC0gZmN0X3JlY29kZShyZWYuZGF0YSRDTFVTVEVSLCAKICAgICAgICAgICAiQW1hY3JpbmVfY2VsbHMiID0gIkFDIChBbWFjcmluZSBjZWxsKSIsCiAgICAgICAgICAgIkJpcG9sYXJfY2VsbHMiID0gIkJDMUEiLAogICAgICAgICAgICJCaXBvbGFyX2NlbGxzIiA9ICJCQzFCIiwKICAgICAgICAgICAiQmlwb2xhcl9jZWxscyIgPSAiQkMyIiwKICAgICAgICAgICAiQmlwb2xhcl9jZWxscyIgPSAiQkMzQSIsCiAgICAgICAgICAgIkJpcG9sYXJfY2VsbHMiID0gIkJDM0IiLAogICAgICAgICAgICJCaXBvbGFyX2NlbGxzIiA9ICJCQzQiLAogICAgICAgICAgICJCaXBvbGFyX2NlbGxzIiA9ICJCQzVBIChDb25lIEJpcG9sYXIgY2VsbCA1QSkiLAogICAgICAgICAgICJCaXBvbGFyX2NlbGxzIiA9ICJCQzVCIiwKICAgICAgICAgICAiQmlwb2xhcl9jZWxscyIgPSAiQkM1QyIsCiAgICAgICAgICAgIkJpcG9sYXJfY2VsbHMiID0gIkJDNUQiLAogICAgICAgICAgICJCaXBvbGFyX2NlbGxzIiA9ICJCQzYiLAogICAgICAgICAgICJCaXBvbGFyX2NlbGxzIiA9ICJCQzcgKENvbmUgQmlwb2xhciBjZWxsIDcpIiwKICAgICAgICAgICAiQmlwb2xhcl9jZWxscyIgPSAiQkM4LzkgKG1peHR1cmUgb2YgQkM4IGFuZCBCQzkpIiwKICAgICAgICAgICAiQ29uZXMiID0gIkNvbmUgUGhvdG9yZWNlcHRvcnMiLAogICAgICAgICAgICJNdWxsZXJfZ2xpYSIgPSAiTUcgKE11ZWxsZXIgR2xpYSkiLAogICAgICAgICAgICJSb2RfQmlwb2xhcl9jZWxsIiA9ICJSQkMgKFJvZCBCaXBvbGFyIGNlbGwpIiwKICAgICAgICAgICAiUm9kIFBob3RvcmVjZXB0b3JzIiA9ICJSb2QgUGhvdG9yZWNlcHRvcnMiKQpgYGAKCjQuIFJlbW92ZSBsb3dseSBleHByZXNzZWQgZ2VuZXMKCmBgYHtyLGV2YWw9RkFMU0V9CmtlZXAgPC0gcm93U3Vtcyhhc3NheXMocmVmLmRhdGEpJGNvdW50cyA+IDApID4gMTAKdGFibGUoa2VlcCkKcmVmLmRhdGEgPC0gcmVmLmRhdGFba2VlcCxdCmBgYAoKNS4gT2J0YWluIHNhbWUgZ2VuZSBJRCBmb3JtYXQgYXMgaW4gdGFyZ2V0IGRhdGEKClRvIGF2b2lkIHByb2JsZW1zIHdpdGggZGlmZmVyZW50IHZlcnNpb24gb2YgZ2VuZSBzeW1ib2xzLCBpdCBpcyBnb29kIHByYWN0aWNlCmRvIHdvcmsgd2l0aCB1bmFtYmlndW91cyBnZW5lIGlkZW50aWZpZXJzIGxpa2UgdGhvc2Ugb2YgRU5TRU1CTCBpbnN0ZWFkLgoKYGBge3IsZXZhbD1GQUxTRX0Kcm93bmFtZXMoc2NlXzkwMCkgPC0gcm93RGF0YShzY2VfOTAwKSRlbnNlbWJsX2dlbmVfaWQgIyB1c2UgRU5TRU1CTCBpZGVudGlmaWVycyBpbnN0ZWFkCnN1bShyb3duYW1lcyhzY2VfOTAwKSAlaW4lIHJvd25hbWVzKHJlZi5kYXRhKSkKYGBgCgo2LiBDb21wdXRlIGBsb2dOb3JtQ291bnRzYAoKYGBge3IsZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShzY3V0dGxlKQpyZWYuZGF0YSA8LSBsb2dOb3JtQ291bnRzKC4uLikKYGBgCgojIyMgYFNpbmdsZVJgIGF0IGxvdyByZWZlcmVuY2UgcmVzb2x1dGlvbgoKYGBge3IsZXZhbD1GQUxTRX0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJTaW5nbGVSIikKbGlicmFyeShTaW5nbGVSKQoKIyBydW5zIDJtaW4gMzBzZWMgZm9yIG1lCnByZWQubG93UmVzIDwtIFNpbmdsZVIodGVzdCA9IC4uLiwgIyBvdXIgZGF0YXNldAogICAgICAgICAgICAgICAgICAgICAgIHJlZiA9IC4uLiwgIyB0aGUgcmVmZXJlbmNlIGRhdGFzZXQgCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gLi4uLCAjIHZlY3RvciBvZiBrbm93biBsYWJlbHMgZm9yIGFsbCBjZWxscyBpbiByZWYKICAgICAgICAgICAgICAgICAgICAgICBkZS5tZXRob2QgPSAid2lsY294IikgIyBtb3N0IHN1aXRhYmxlIG1ldGhvZCBmb3Igc3BhcnNlIChkcm9wbGV0KSBkYXRhCnRhYmxlKHByZWQubG93UmVzJGxhYmVscykKYGBgCgpVc2luZyB0aGUgYFNpbmdsZVJgIGNsYXNzaWZpZXIgdGhhdCB3YXMgdHJhaW5lZCBvbiB0aGUgcmVmZXJlbmNlIGRhdGFzZXQgYnkKU2hla2hhciAqZXQgYWwuKiwgd2UgaGF2ZSBsYWJlbGVkIDI3MzEgY2VsbHMgZnJvbSB0aGUgTWFjb3NrbyBkYXRhc2V0IGFzCmFtYWNyaW5lIGNlbGxzLCAxNTI4IGNlbGxzIGFzIGJpcG9sYXIgY2VsbHMsIGFuZCBzbyBvbi4gQWdhaW4gbm90ZSB0aGF0IHdlCnByZWRpY3RlZCBhIGxhYmVsIGZvciBlYWNoIGNlbGwgaW4gdGhlIE1hY29za28gZGF0YXNldCBiYXNlZCBpdHMgc2ltaWxhcml0eQooaW4gZ2VuZSBleHByZXNzaW9uKSB3aXRoIGxhYmVsZWQgY2VsbHMgZnJvbSB0aGUgU2hla2hhciBkYXRhc2V0LgoKKipNb3N0IGltcG9ydGFudGx5LCoqIHdlIHdhbnQgdG8gY29tcGFyZSBvdXIgcHJlZGljdGVkIGNlbGwgbGFiZWxzIHdpdGggdGhlCmxhYmVscyB0aGF0IHdlcmUgb2J0YWluZWQgYnkgTWFjb2tzbyAqZXQgYWwuKiwgd2hpY2ggd2UgKmNvdWxkKiBjb25zaWRlciB0byBiZQpncm91bmQgdHJ1dGggbGFiZWxzIGlmIHdlIGFzc3VtZSB0aGF0IHRoZSBhdXRob3JzIHN1Y2NlZWRlZCBpbiB0aGVpciBsYXJnZSAKZWZmb3J0IG9mIGFubm90YXRpbmcgdGhlaXIgY2VsbCBjbHVzdGVycyBmb3IgdGhlaXIgcHVibGljYXRpb24uCgpgYGB7cixldmFsPUZBTFNFfQp0YWIgPC0gdGFibGUoLi4uLCAuLi4pICMgdGhlIG9yaWdpbmFsIGxvdy1yZXNvbHV0aW9uIGxhYmVscyB2ZXJzdXMgb3VyIHByZWRpY3RlZCBsb3ctcmVzb2x1dGlvbiBsYWJlbHMKdGFiCmBgYAoKSW5zcGVjdCB0aGUgcmVzdWx0cy4KCkJlZm9yZSB3ZSBkaXZlIGRlZXBlciBpbnRvIHRoaXMsIHdlIG1heSB2aXN1YWxpemUgb3VyIHJlc3VsdCB1c2luZyBhIGhlYXRtYXA6CgpgYGB7cixldmFsPUZBTFNFfQpwaGVhdG1hcDo6cGhlYXRtYXAodGFiIC8gcm93U3Vtcyh0YWIpKQpgYGAKCkluc3BlY3QgdGhlIHJlc3VsdHMuCgpPbmUgb3RoZXIgdmlzdWFsaXphdGlvbiBzdHJhdGVneSBpcyBpbXBsZW1lbnRlZCBpbiB0aGUgYHBsb3RTY29yZUhlYXRtYXBgIG9mIHRoZQpgU2luZ2xlUmAgcGFja2FnZS4gUmVtZW1iZXIsIHRoZSBgU2luZ2xlUmAgY2xhc3NpZmllciBhc3NpZ25zIGVhY2ggdGFyZ2V0IGNlbGwKdG8gYSBjZWxsIHR5cGUgbGFiZWwgKip3aXRoIGEgY2VydGFpbiBwcm9iYWJpbGl0eSoqLiBUaGUgYHBsb3RTY29yZUhlYXRtYXBgCmZ1bmN0aW9uIHRoZW4gYWxsb3dzIHlvdSB0byBwbG90LCBmb3IgZWFjaCBjZWxsIChjb2x1bW5zKSB0aGUgYXNzaWdubWVudCBzY29yZSAKKHdoaWNoIGNhbiBiZSB0aG91Z2h0IG9mIGFzIGEgcHJvYmFiaWxpdHkpIG9mIHRoYXQgY2VsbCBiZWxvbmdpbmcgdG8gZWFjaCBvZiAKdGhlIHJlZmVyZW5jZSBsYWJlbCBjYXRlZ29yaWVzIChyb3dzKS4KCmBgYHtyLGV2YWw9RkFMU0V9CmdnIDwtIHBsb3RTY29yZUhlYXRtYXAocHJlZC5sb3dSZXNbMToyMCxdKQpnZwpgYGAKCkluc3BlY3QgdGhlIHJlc3VsdHMuCgpGaW5hbGx5LCB3ZSBjYW4gdXNlIHRoZXNlIGFzc2lnbm1lbnQgc2NvcmUgdG8gZmlsdGVyIG91dCBjZWxscyB0aGF0IGNvdWxkIG5vdCBiZQp1bmFtYmlndW91c2x5IGFzc2lnbmVkIHRvIG9uZSBjZWxsIHR5cGUsIGkuZS4sIG9ubHkgcmVwb3J0IHRob3NlIGNlbGxzIHRoYXQgd2UKd2VyZSBhYmxlIHRvIHJlbGlhYmx5IGFzc2lnbjoKCmBgYHtyLGV2YWw9RkFMU0V9CnN1bW1hcnkoaXMubmEocHJlZC5sb3dSZXMkcHJ1bmVkLmxhYmVscykpCgpyYW5nZShyb3dNYXhzKHByZWQubG93UmVzJHNjb3JlcykpICMgYWxsIGFzc2lnbm1lbnRzCnJhbmdlKHJvd01heHMocHJlZC5sb3dSZXMkc2NvcmVzWyFpcy5uYShwcmVkLmxvd1JlcyRwcnVuZWQubGFiZWxzKSxdKSkgIyByZWxpYWJsZSBhc3NpZ25tZW50cwoKdGFibGUoc2NlXzkwMCRjbHVzdGVyX2xvd1Jlc1shaXMubmEocHJlZC5sb3dSZXMkcHJ1bmVkLmxhYmVscyldLCAKICAgICAgcHJlZC5sb3dSZXMkbGFiZWxzWyFpcy5uYShwcmVkLmxvd1JlcyRwcnVuZWQubGFiZWxzKV0pCmBgYAoKV2Ugd2lsbCBpbnRlcnByZXQgdGhlIGNvbmNvcmRhbmNlIGJldHdlZW4gdGhlIHByZWRpY3RlZCBhbmQgdGhlCm9yaWdpbmFsIGxhYmVscyBvbiB0aGlzIHN1YnNldC4gV2Ugb2JzZXJ2ZSBhIHN0cm9uZyBjb3JyZXNwb25kZW5jZSBiZXR3ZWVuIHRoZSAKcHJlZGljdGVkIGxhYmVscyBhbmQgdGhlIGxhYmVscyBmcm9tIE1hY29rc28gKmV0IGFsLiogV2UgY2FuIHJlYWQgdGhpcyB0YWJsZSAKYXMgZm9sbG93czoKCi0gQWxtb3N0IGFsbCBhbWFjcmluZSBjZWxscyBvZiB0aGUgTWFjb3NrbyBkYXRhc2V0IGFyZSBjb3JyZWN0bHkgcHJlZGljdGVkIGFzCmFtYWNyaW5lIGNlbGxzICgyMDM2LygyMDM2KzcrMSs0MSsxKSDCsT0gOTglIGNvcnJlY3RseSBhc3NpZ25lZCkuCgpUcnkgdG8gaW50ZXJwcmV0IHRoZSByZXN1bHRzIGZvciB0aGUgb3RoZXIgY2VsbCB0eXBlcy4KCldlIGNhbiBub3cgYWxzbyB2aXN1YWxpemUgdGhlIHByZWRpY3RlZCBsYWJlbHMgYW5kIHRoZSBvcmlnaW5hbCBsYWJlbHMgb24gb3VyCnQtU05FLgoKYGBge3IsZXZhbD1GQUxTRX0Kc2NlXzkwMCRTaW5nbGVSX2xvd1JlcyA8LSBwcmVkLmxvd1JlcyRsYWJlbHMKCiMgb3VyIGxvdyByZXNvbHV0aW9uIGxhYmVscyBiYXNlZCBvbiByZWZlcmVuY2UKcGxvdFRTTkUoLi4uLAogICAgICAgICBjb2xvdXJfYnkgPSAuLi4sCiAgICAgICAgIHRleHRfYnkgPSAuLi4sCiAgICAgICAgIHRleHRfc2l6ZSA9IC4uLikKCiMgbG93IHJlc29sdXRpb24gbGFiZWxzIG9mIHRoZSBhdXRob3JzCnBsb3RUU05FKC4uLiwKICAgICAgICAgY29sb3VyX2J5ID0gLi4uLAogICAgICAgICB0ZXh0X2J5ID0gLi4uLAogICAgICAgICB0ZXh0X3NpemUgPSAuLi4pCmBgYAoKSW50ZXJwcmV0IHRoZSB2aXN1YWxpemF0aW9ucy4KCioqQWx0b2dldGhlciwgbGFiZWwtdHJhbnNmZXIgd2FzIHF1aXRlIHN1Y2Nlc3NmdWwsIGFuZCB3ZSB3b3VsZCBoYXZlIGJlZW4gYWJsZSoqCioqdG8gdmVyeSBxdWlja2x5IGdldCBoaWdoLXF1YWxpdHkgcmVzdWx0cyB1c2luZyB0aGlzIHZlcnkgdXNlci1mcmllbmRseSBhbmQqKiAKKipmYXN0IGFwcHJvYWNoIGltcGxlbWVudGVkIGluIHRoZSBgU2luZ2xlUmAgcGFja2FnZS4qKgoKIyMjIEFkZGVuZHVtOiBgU2luZ2xlUmAgYXQgaGlnaCByZWZlcmVuY2UgcmVzb2x1dGlvbgoKRm9yIHRoZSBzYWtlIG9mIGNvbXBsZXRlbmVzcywgd2UgbWF5IGFsc28gcGVyZm9ybSBsYWJlbCB0cmFuc2ZlciB3aXRoIGBTaW5nbGVSYAp1c2luZyB0aGUgbW9yZSBmaW5lLWdyYWluZWQgbGFiZWxzIGZyb20gdGhlIHJlZmVyZW5jZSBkYXRhc2V0LgoKYGBge3IsZXZhbD1GQUxTRX0KLi4uCmBgYAoK