Objectives

This notebook will demonstrate how to:

  • Explore data from antibody-derived tags (CITE-seq)
  • Apply simple rule-based classification to identify cell types
  • Identify cell types by similarity to reference datasets with SingleR
  • Apply SingleR classification to groups of cells

In this notebook, we will attempt to annotate cell types to each of the cells in a dataset, using some of the automated tools that are available within the Bioconductor universe.

Much of the material in this notebook is directly inspired by, and draws heavily on, material presented in the book Orchestrating Single Cell Analysis with Bioconductor.

The data we will use for this notebook is derived from a 10x Genomics dataset of human peripheral blood mononuclear cells (PBMCs). These data include both single cell RNA-seq counts and quantification of antibody-derived tags (ADTs) performed by sequencing short DNA barcodes attached to specific antibodies. This type of ADT sequencing with single cells is commonly known as CITE-seq, after the protocol developed by Stoeckius et al. (2017).
The antibodies used here are the The TotalSeq™-B Human TBNK Cocktail, a set of antibodies designed to react with immune cell surface markers.

Single-cell roadmap: Cell type
Single-cell roadmap: Cell type

The data here have already been filtered, normalized, and had dimension reductions calculated for the single-cell RNA-seq data. The ADT data has also been separately filtered and normalized. For details about how to perform these tasks with data that has been processed with Cell Ranger, you may want to look at the “Integrating with protein abundance” chapter of OSCA.

The processed gene expression and ADT data were saved into a combined SingleCellExperiment (SCE) object, and we will start with that object for our exploration here.

Set up

To start, we will load some of the libraries we will need later, and set a random number seed for reproducibility.

# Load libraries
library(ggplot2) # plotting functions
library(SingleCellExperiment) # Bioconductor single-cell data class
Loading required package: SummarizedExperiment
Loading required package: MatrixGenerics
Loading required package: matrixStats

Attaching package: 'MatrixGenerics'
The following objects are masked from 'package:matrixStats':

    colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse,
    colCounts, colCummaxs, colCummins, colCumprods, colCumsums,
    colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs,
    colMads, colMaxs, colMeans2, colMedians, colMins, colOrderStats,
    colProds, colQuantiles, colRanges, colRanks, colSdDiffs, colSds,
    colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads,
    colWeightedMeans, colWeightedMedians, colWeightedSds,
    colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
    rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods,
    rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps,
    rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins,
    rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks,
    rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars,
    rowWeightedMads, rowWeightedMeans, rowWeightedMedians,
    rowWeightedSds, rowWeightedVars
Loading required package: GenomicRanges
Loading required package: stats4
Loading required package: BiocGenerics

Attaching package: 'BiocGenerics'
The following objects are masked from 'package:stats':

    IQR, mad, sd, var, xtabs
The following objects are masked from 'package:base':

    anyDuplicated, aperm, append, as.data.frame, basename, cbind,
    colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
    get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply,
    match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
    Position, rank, rbind, Reduce, rownames, sapply, setdiff, table,
    tapply, union, unique, unsplit, which.max, which.min
Loading required package: S4Vectors

Attaching package: 'S4Vectors'
The following object is masked from 'package:utils':

    findMatches
The following objects are masked from 'package:base':

    expand.grid, I, unname
Loading required package: IRanges
Loading required package: GenomeInfoDb
Loading required package: Biobase
Welcome to Bioconductor

    Vignettes contain introductory material; view with
    'browseVignettes()'. To cite Bioconductor, see
    'citation("Biobase")', and for packages 'citation("pkgname")'.

Attaching package: 'Biobase'
The following object is masked from 'package:MatrixGenerics':

    rowMedians
The following objects are masked from 'package:matrixStats':

    anyMissing, rowMedians
Warning: replacing previous import 'S4Arrays::makeNindexFromArrayViewport' by
'DelayedArray::makeNindexFromArrayViewport' when loading 'SummarizedExperiment'
# Setting the seed for reproducibility
set.seed(12345)

Directories and files

As mentioned, our input file here is a single normalized and processed SCE object, stored as an rds file. That should be all we need to read in!

Our output will be a table of per-cell information, which will include the cell type assignments we have made throughout this notebook. We aren’t planning any significant modifications of the underlying data, so we won’t bother re-saving the whole SCE object as a new .rds file this time.

# directory for the input data
data_dir <- file.path("data", 
                      "PBMC-TotalSeqB", 
                      "normalized")

# the input file itself
sce_file <- file.path(data_dir, 
                      "PBMC_TotalSeqB_normalized_sce.rds")

# A directory to store outputs
analysis_dir <- file.path("analysis", 
                          "PBMC-TotalSeqB")

# Create directory if it doesn't exist
fs::dir_create(analysis_dir)

# output table path
cellinfo_file <- file.path(analysis_dir, 
                           "PBMC_TotalSeqB_cellinfo.tsv")

Exploring a CITE-seq SingleCellExperiment

Now that the preliminary setup is out of the way, we can get started. First we will read in the SingleCellExperiment from the input file we defined earlier.

# read in the SCE file
sce <- readr::read_rds(sce_file)
# print a summary of the SCE
sce
class: SingleCellExperiment 
dim: 36601 7924 
metadata(1): Samples
assays(2): counts logcounts
rownames(36601): ENSG00000243485 ENSG00000237613 ... ENSG00000278817
  ENSG00000277196
rowData names(3): ID Symbol Type
colnames: NULL
colData names(13): Sample Barcode ... prob_compromised sizeFactor
reducedDimNames(2): PCA UMAP
mainExpName: Gene Expression
altExpNames(1): ADT

This should look similar to the SCE objects that we have seen before, containing counts and logcounts assays where each cell is a column and each row is a gene. We also have some of the rowData, colData and reduced dimension matrices that we have seen before.

But where are the data from the ADTs? We wouldn’t necessarily want those stored in the main data matrices, as the characteristics of ADT barcode data is going to be quite different from gene expression data.

To keep the ADT data separate from the RNA gene expression data, we have split this data off into an alternative experiment (altExp) slot. You can see the name of this altExp on the line altExpNames above. We could have more than one type of alternative experiment (such as spike-in or ATAC-seq), but in this case, just the one.

To access the contents of the altExp slot, we can use the altExp() function. Let’s look at what we have in that slot:

# print a summary of the 'ADT' altExp
altExp(sce, "ADT")
class: SingleCellExperiment 
dim: 10 7924 
metadata(1): Samples
assays(2): counts logcounts
rownames(10): CD3 CD4 ... CD45 IgG1
rowData names(3): ID Symbol Type
colnames: NULL
colData names(1): sizeFactor
reducedDimNames(0):
mainExpName: NULL
altExpNames(0):

It is another SingleCellExperiment! Inception! Let’s look at that embedded SCE more closely.

The first thing to note is that this altExp has the same number of columns as did the main SCE object. Those corresponded to the individual cells before, and still do!

There are only 10 rows, however, and these correspond to the ADTs that were assayed by this particular experiment. Just as we did with the full SCE, we can use rowData() to view the table containing metadata associated with each of these rows. We’ll add the altExp() function to point it to the embedded object we are interested in. Since there is only one altExp, we don’t need the second (name) argument ("ADT") that we used above; the default behavior of altExp() is to just give us the first altExp, and that is the one (and only) that we need.

# What proteins were assayed?
rowData(altExp(sce))
DataFrame with 10 rows and 3 columns
               ID                 Symbol             Type
      <character>            <character>      <character>
CD3           CD3                    CD3 Antibody Capture
CD4           CD4                    CD4 Antibody Capture
CD8           CD8                    CD8 Antibody Capture
CD11c       CD11c                  CD11c Antibody Capture
CD14         CD14                   CD14 Antibody Capture
CD16         CD16                   CD16 Antibody Capture
CD19         CD19                   CD19 Antibody Capture
CD56         CD56                   CD56 Antibody Capture
CD45         CD45                   CD45 Antibody Capture
IgG1         IgG1 IgG1_control_TotalSeqC Antibody Capture

You can see here the names and symbols of the tags used, along with the designation that all have an “Antibody Capture” type (as opposed to “Gene Expression” for the RNA data). One you might note looks different is the IgG1 control, which is actually a mouse antibody used as a negative control.

Clustering redux

While dimension reduction was performed on this data, we have not yet performed any clustering.

Let’s assign some clusters to our cells, using graph-based clustering and default parameters, taking as input the PCA matrix that was previously calculated. Note that this PCA matrix and the UMAP built from it were derived from the gene expression data, so the clustering is going to reflect the gene expression data only. While we have the ADT data, it is not being used for this stage of the analysis.

# perform clustering
nn_clusters <- bluster::clusterRows(
  # PCA input
  reducedDim(sce, "PCA"), 
  # graph clustering & parameters
  bluster::NNGraphParam()
)

# add clusters to colData
sce$nn_cluster <- nn_clusters

Now we can plot the clusters we have identified with scater::plotUMAP(). This is a shortcut for scater::plotReducedDim(dimred = "UMAP", ...), which can save us a lot of typing as we do this repeatedly!

# plot clusters
scater::plotUMAP(sce, color_by = "nn_cluster") + 
  # rename the legend
  guides(color = guide_legend(title = "Cluster"))

But what are these clusters, really? Do they correspond to particular cell types that we are interested in?

Does it bother you that we just used the default nearest-neighbor graph clustering parameters? Do you know what those were?

Investigating cell types

Using ADT data

The first way we will identify cell types of individual cells is to use the ADT normalized counts. These antibody markers were (hopefully) chosen for their relevance to the sequenced cell population.

The first marker we will look at is CD3, which is a protein complex that is found on the surface of T cells. We can again use the plotUMAP() function to color cells by CD3 ADT levels.

Note that this function can plot data from the colData table (as we used it above when plotting clusters), in the main gene expression matrix (as we used it in the previous notebook), AND in altExp tables and matrices! So to color by the ADT levels (as normalized in the logcounts matrix) we only need to provide the tag name that we want to plot in the color_by argument.

# plot CD3 expression
scater::plotUMAP(sce, color_by = "CD3")

It appears that we have a number of potential T cells down in the lower left!

Let’s look at a couple of other markers to try to break those up more specifically.

Two other markers of relevance to the T cells are CD4 and CD8. The CD4 complex is present in helper T cells (hence their other common name, CD4+ T cells). By contrast, the CD8 complex is found on killer T cells (CD8+ cells).

Let’s plot the ADT results for those two markers as well below:

# plot CD4 marker
scater::plotUMAP(sce, 
                 color_by = "CD4")

# plot CD8 marker
scater::plotUMAP(sce, 
                 color_by = "CD8")

Rule-based classification

Plotting the levels of the ADTs provides a nice visual representation, but what we really want to do is to turn these values into specific cell-type assignments for each cell. Such classification could be considered as analogous to a cell-sorter assay, where we would set up some rules to look at a few markers for each cell and use those to assign a cell type. The simplest type of rule might be one where we use a threshold to call a marker as present or absent, and then use the presence of a marker to indicate a specific cell type.

To do this, we will need to make some decisions, such as the thresholds we should use to determine whether a cell is or is not expressing a particular marker. In general, markers that are useful for this cell-typing approach will have a bimodal distribution of expression levels which can be used to separate the population into two groups of cells. One group of cells will have only a background level signal for each marker (due to non-specific binding or other factors), while the other group, those that express the protein, will have a much higher level of binding and higher counts.

To assess whether the ADTs we have chosen have a useful distribution of expression values, and to identify thresholds we might use, we would like to plot each ADT tag. To do this, we will pull out the expression values for these markers from the SCE object and do some data wrangling.

We are interested in the normalized counts for the ADT tags, which are stored in the logcounts assay of the altExp. If you recall, this matrix is stored with the columns as cells and rows as markers, but we really want it with each row a cell and each column a marker. So we will first transpose the data, then convert it to a data frame for our next steps. Because the SCE object stores the assay data matrices in a specialized format, we have to do one extra step convert it first to a “regular” R matrix or R won’t know how to convert it to a data frame.

# convert logcounts data to a data frame
adt_df <- logcounts(altExp(sce)) |>
  t() |> # transpose
  as.matrix() |> # convert to matrix
  as.data.frame() # convert to data frame

# view the data frame
head(adt_df)

If we just wanted to plot one of these tags, we could do so right away, but with a bit more data wrangling, we can convert these results into a “tidier” format, that will allow us to take full advantage of tidyverse tools! In particular, it will let us plot them all at once with ggplot2 faceting.

Right now the data is in a “wide” format, such that each column is a different tag. But the data in all of the columns is the same type, and measures something similar: the normalized count of an ADT. One could even argue that each row contains 10 different observations, where the “tidy” data ideal, as espoused by Wickham (2014), requires a single observation per row, a “long” format. This long format will have one column that tells us which ADT was measured and a second column with the measurement value itself.

We can perform this conversion using the tidyr::pivot_longer() function, which allows us to convert our data frame with one column per tag into a data frame with separate columns for the tag id (ADT) and the expression value (logcount). Following conversion, we will filter to just the ADTs that we care about.

adt_df_long <- adt_df |>
  # pivot to long format
  tidyr::pivot_longer(
    everything(), # use all columns
    names_to = "ADT", # convert row names to a column called "ADT"
    values_to = "logcount" # name the value column "logcount"
  ) |>
  # filter to tags we are interested in
  dplyr::filter(ADT %in% c("CD3", "CD4", "CD8"))

# look at the resulting df
head(adt_df_long)

Now we can make a density plot with ggplot2 for all three ADTs we are interested in at once.

# plot logcounts by ADT
ggplot(adt_df_long, aes(x = logcount, fill = ADT)) + 
  geom_density() + # density plot
  facet_grid(rows = vars(ADT)) + # facet by ADT
  theme_bw() + # nicer theme
  theme(legend.position = "none") # no legend needed

These look pretty good! Each of these markers has a bimodal distribution: A lower peak consisting of cells that do not express the protein but which still have a background level of antibody binding, and an upper peak of cells that do express the protein of interest. The background level does vary by antibody marker, so we will need a different threshold value for each one.

We can now use the values from these plots to construct a set of rules to classify the T cells. We will do this using the “wide” data frame from earlier.

The thresholds we are using here were identified just “by eye”, so this is not a particularly principled method of cell type assignment, but it can be fairly effective. Here we are assigning only three cell types; cells that do not fit any of these criteria will be set as NA.

# add cell type column by thresholding
adt_df <- adt_df |>
  dplyr::mutate(
    celltype = dplyr::case_when(
      CD3 > 6.7 & CD4 > 8 ~ "CD4+ T-cell",
      CD3 > 6.7 & CD8 > 6 ~ "CD8+ T-cell",
      CD3 > 6.7 ~ "T-cell"
    )
  )

adt_df

Now we will want to add the cell types we have assigned back to our original SCE object. We can do that by defining a new column name, threshold_celltype that will be added to the colData object. Creating and assigning values to this column can be done with the $ shortcut, and then we can plot our results with the plotUMAP() function as before.

sce$threshold_celltype <- adt_df$celltype
scater::plotUMAP(sce, 
                 color_by = "threshold_celltype") + 
  guides(color = guide_legend(title = "Cell type"))

How did we do?

Note that while we applied this technique to assign cell types using the ADT data, we could use the same type of procedure using gene expression data alone, or a combination of gene expression data and tag data.

However, what we did here was very ad-hoc and quite manual! We didn’t calculate any statistics, and we had to look at every tag we were interested in to pick thresholds. A different dataset might have different background levels, which would require different thresholds.

While this technique might be good for some simple experiments, and can be useful for manual curation, it might not translate well to more complex datasets with multiple samples. We also looked at each marker separately, which might not be the most efficient or robust method of analysis.

For a more principled approach that allows identification of cell types by looking at the expression of sets of genes that are known to characterize each cell type, you might look at the AUCell package. For more on that method, the OSCA section Assigning cell labels from gene sets is a very good reference.

Cell type annotation with SingleR

An alternative approach to using known marker genes for classification is to instead classify cells by comparing them to a reference expression dataset. To do this, we will find a well-curated gene expression dataset that contains samples with known cell types. We can then train a model based on this dataset and look at each of the cells in our new dataset to determine which (if any) of the known cell types has the most similar expression pattern. The details of how such a model may be constructed and trained will vary by the specific method, but this overall approach is widely applied.

For this section, we will focus on the SingleR package and its methods, which are described in detail in The SingleR Book.

Reference datasets

Selecting a reference dataset is one of the more critical steps for this enterprise. At the most basic level, if the reference dataset does not include the types of cells that we expect to see in our sample, it won’t be useful. So we will want a reference dataset that has as many as possible of the cell types that we expect to find in our dataset, at a level of granularity that aligns with our goals.

For SingleR that reference data can be from bulk RNA sequencing or from other single-cell experiments. SingleR is also fairly robust to the method used for gene expression quantification, which means that we can use either RNA-seq datasets or microarrays, if those are more readily available.

One convenient source of cell reference data is the celldex package, which is what we will use here. This package includes functions to download a variety of well-annotated reference datasets in a common format.
For more information on the datasets available, you will want to refer to the celldex summary vignette.

We will start by using a reference dataset of sorted immune cells from GSE107011 (Monaco et al. 2019). This particular reference was chosen because it is well-suited to PBMC datasets, with a good level of granularity.

The celldex functions also have a convenient option to convert gene symbols to Ensembl ids, which we will use here so that our reference data uses the same gene identifiers as the single-cell data.

# Bioconductor "Hub" packages provide the option to cache
#   downloads, but the interactive prompt can be annoying
#   when working with notebooks.
# These options disable the prompt by giving permission 
#   to create the cache automatically
ExperimentHub::setExperimentHubOption("ASK", FALSE)
AnnotationHub::setAnnotationHubOption("ASK", FALSE)

# Get Monaco 2019 data from celldex with Ensembl ids.
monaco_ref <- celldex::MonacoImmuneData(ensembl = TRUE)
Warning: replacing previous import 'S4Arrays::makeNindexFromArrayViewport' by
'DelayedArray::makeNindexFromArrayViewport' when loading 'HDF5Array'
downloading 1 resources
retrieving 1 resource
loading from cache
require("ensembldb")

What is this monaco_ref object?

monaco_ref
class: SummarizedExperiment 
dim: 46077 114 
metadata(0):
assays(1): logcounts
rownames(46077): ENSG00000121410 ENSG00000268895 ... ENSG00000159840
  ENSG00000074755
rowData names(0):
colnames(114): DZQV_CD8_naive DZQV_CD8_CM ... G4YW_Neutrophils
  G4YW_Basophils
colData names(3): label.main label.fine label.ont

A SummarizedExperiment is very similar to a SingleCellExperiment, except rather than having one column per cell, each column is a sample. Otherwise, the components are very similar: each row is still a gene, for example, and additional data about the samples are stored in the colData. In fact, the SingleCellExperiment object is derived from a SummarizedExperiment, with some extra slots that are more relevant to single-cell data.

What information do we have for the samples?

colData(monaco_ref)
DataFrame with 114 rows and 3 columns
                      label.main             label.fine   label.ont
                     <character>            <character> <character>
DZQV_CD8_naive      CD8+ T cells      Naive CD8 T cells  CL:0000900
DZQV_CD8_CM         CD8+ T cells Central memory CD8 T..  CL:0000907
DZQV_CD8_EM         CD8+ T cells Effector memory CD8 ..  CL:0000913
DZQV_CD8_TE         CD8+ T cells Terminal effector CD..  CL:0001062
DZQV_MAIT                T cells             MAIT cells  CL:0000940
...                          ...                    ...         ...
G4YW_NK                 NK cells   Natural killer cells  CL:0000623
G4YW_pDC         Dendritic cells Plasmacytoid dendrit..  CL:0000784
G4YW_mDC         Dendritic cells Myeloid dendritic ce..  CL:0000782
G4YW_Neutrophils     Neutrophils Low-density neutroph..  CL:0000096
G4YW_Basophils         Basophils  Low-density basophils  CL:0000043

There are three main columns for the sample data:

  • label.main is a more general cell type assignment.

  • label.fine is a fine-level cell type with more specific labels. The exact level of granularity of these main and fine designations (and indeed the label names themselves) will vary among datasets, so it is important to look at the reference to see whether it is suitable for your application.

  • label.ont is a standardized Cell Ontology identifier. Using the cell ontology can allow for more complex representations of the relationships among different cell types, but investigating that is beyond the scope of this workshop.

Another component we would like to explore is how many of each of these cell types we have in the reference dataset. A bit of quick dplyr wrangling can give us the answer.

colData(monaco_ref) |> 
  as.data.frame() |>
  dplyr::count(label.main, label.fine)

This is pretty good! Most cell types have 4 replicates, which is more replicates than we often find.

What does SingleR do?

As mentioned earlier, SingleR builds a model from a set of training data, and then uses that model to classify cells (or groups of cells) in new datasets.

SingleR works by first identifying a set of marker genes that can be used to differentiate among the cell types in the reference dataset. It does this by performing pairwise comparisons among all of the cell types, and retaining the top set of genes differentiating each pair. The idea is that this set of genes will be the most informative for differentiating cell types.

Then, for each cell, SingleR calculates the Spearman correlation between expression of that cell and each cell type (using the only the genes chosen earlier). Notably, this is a non-parametric correlation, so the scaling and normalization that we apply (or don’t) should not matter! Note that if you used a single-cell technology that produces full-length transcripts (i.e., SMART-seq), you will probably want to convert your counts to Transcripts per Million (TPM), to allow more consistent ranking among transcripts of different lengths.

The reference cell type with the highest correlation is then chosen as the cell type assignment for that cell. If there are multiple cell types with high scores, an optional fine-tuning step repeats the process using only the most relevant genes for those cell types.

Running SingleR

For our first run, we will do the marker gene selection (training) and classification in a single step, using the convenience function SingleR::SingleR(). For this we need only supply three main arguments: Our SCE object, a reference matrix (here in SummarizedExperiment format), and the labels for each of the samples in the reference that we want to use. We also need to be sure that our sample and the reference data use the same gene IDs, which is why we requested the Ensembl IDs when getting the reference dataset.

Because this function is doing many repetitive calculations (lots of correlations!), we can speed it up by including the BPPARAM argument. This is a common argument in Bioconductor packages where BP stands for the BiocParallel package, which provides multiprocessing capabilities to many Bioconductor functions. In this case, we will use the argument BiocParallel::MulticoreParam(4) to specify we want to use local multicore processing with 4 “workers”.

# calculate SingleR results in one step
singler_result <- SingleR::SingleR(
  sce, # our query SCE
  ref = monaco_ref, # reference dataset
  labels = monaco_ref$label.main, # reference labels to use
  BPPARAM = BiocParallel::MulticoreParam(4) # multiprocessing
)

SingleR provides a few nice visualizations for evaluating the statistics it calculated and the assignments it makes. One is a heatmap of the scores for each cell, arranged by the cell type that was assigned to each. This is created with the SingleR::plotScoreHeatmap() function.

SingleR::plotScoreHeatmap(singler_result)

We can also pull out individual components of the results object for plotting in the context of our input SCE object. Here we will save the pruned labels (where low-quality assignments have been given an NA label), storing them back in our SCE object (specifically to a new column of the colData table).

sce$celltype_main <- singler_result$pruned.labels

Now we can plot the cell type assignments onto our UMAP to see how they compare to the patterns we saw there before.

scater::plotUMAP(sce, color_by = "celltype_main") 

Annoyingly, the NA and T cells labels are quite close in color, and the scater and SingleR packages don’t agree on color choices. Luckily, since plotUMAP() returns a ggplot object, we can modify the color palette using ggplot2 functions. Still annoyingly, however, when we change the palette, the legend title defaults to the uninformative name "colour_by", so we’ll also specify a matching legend title with our new color palette.

scater::plotUMAP(sce, color_by = "celltype_main") +
  scale_color_brewer(name = "Cell type", # legend title
                     palette = "Dark2",      # color palette
                     na.value = "gray80")    # use light gray for NA values
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

We seem to have a pretty good set of cell type assignments, with most falling into groupings consistent with what we see in the UMAP plot.

We can thank the fact that this is a PBMC sample and that we have a good reference dataset for these cell types for the cleanliness of this plot. Quite often with other kinds of samples (especially cancer cells!) things will be much less clean!

We can also look to see how the cell type assignments are distributed using the base R function table(). Since we like to keep track of the cells that ended up as NA in the pruned labels, we will include the useNA = "ifany" argument.

table(singler_result$pruned.labels, useNA = "ifany")

        B cells    CD4+ T cells    CD8+ T cells Dendritic cells       Monocytes 
            692            1291             904             232            3622 
       NK cells     Progenitors         T cells            <NA> 
            345              47             646             145 

Exploring finer labels

In the previous cell typing, we used the label.main column, but we also had label.fine, so let’s use that to explore the dataset in a bit more detail.

We will also take this time to dive a bit deeper into the steps that SingleR performed. As mentioned, the first step is training the model, during which we identify the genes that will be used for the correlation analysis later. While this step is not particularly slow, if we were classifying multiple samples, we would not want to have to repeat it for every sample.

To do the training, we will use the trainSingleR() function. For this we will start with our reference and the labels we want to train the model with.

We can then specify the method used to select the genes that will be used for classification. The default method is "de", which performs a differential expression analysis for each pair of labels, but we could also use "sd" to select the genes which are most variable across labels, or "all" to use all genes. If we want to get really fancy, we could even provide a specific list of genes to use.

We should note here that the reference dataset for SingleR does not need to be from a compendium like celldex! If you have any well-classified dataset that you want to use as a reference, you can, as long as you can create a gene by sample expression matrix and a vector of cell types for each sample. You will want to ensure that the cell types you expect to see in your sample are present in the reference dataset, and data should be normalized, but otherwise the method can be quite flexible. You can even use a previously-annotated SingleCellExperiment as a reference for a new dataset. For more details about custom references, see the OSCA chapter on cell type annotation

We do want to be sure that the genes selected for the model will be among those present in our SCE object, so we will use the restrict argument with a vector of the genes in our SCE. This step would happen automatically with the SingleR::SingleR() function, but we need to add it manually for this use case.

# build fine model
singler_finemodel <- SingleR::trainSingleR(
  monaco_ref, # reference dataset
  labels = monaco_ref$label.fine, # labels for training dataset
  # use DE to select genes (default)
  genes = "de", 
  # only use genes in the sce object
  restrict = rownames(sce),
  # parallel processing
  BPPARAM = BiocParallel::MulticoreParam(4)
)

Now we can perform the classification step, using our SCE object and the SingleR model that we just created.

# classify with fine model
singler_result_fine <- SingleR::classifySingleR(
  sce, # our SCE object
  singler_finemodel, # the trained model object
  # perform fine tuning (default)
  fine.tune = TRUE,
  # parallel processing
  BPPARAM = BiocParallel::MulticoreParam(4)
)

What labels were assigned, and how many of each?

table(singler_result_fine$pruned.labels, useNA = "ifany")

   Central memory CD8 T cells           Classical monocytes 
                          121                          2926 
  Effector memory CD8 T cells             Exhausted B cells 
                           31                            48 
    Follicular helper T cells        Intermediate monocytes 
                          135                           424 
        Low-density basophils                    MAIT cells 
                            5                           112 
      Myeloid dendritic cells                 Naive B cells 
                          162                           311 
            Naive CD4 T cells             Naive CD8 T cells 
                          600                           752 
         Natural killer cells       Non classical monocytes 
                          320                           189 
  Non-switched memory B cells            Non-Vd2 gd T cells 
                          250                           163 
                 Plasmablasts  Plasmacytoid dendritic cells 
                            3                            93 
             Progenitor cells       Switched memory B cells 
                           36                            81 
           T regulatory cells Terminal effector CD4 T cells 
                          163                            39 
Terminal effector CD8 T cells                     Th1 cells 
                          115                            97 
               Th1/Th17 cells                    Th17 cells 
                          135                           133 
                    Th2 cells                Vd2 gd T cells 
                          144                           146 
                         <NA> 
                          190 
# add fine labels to SCE
sce$celltype_fine <- singler_result_fine$pruned.labels
# plot UMAP with fine labels
scater::plotUMAP(sce, color_by = "celltype_fine")
Warning: Removed 190 rows containing missing values or values outside the scale range
(`geom_point()`).

That’s a pretty messy plot. Mostly that is because there are lots of cell types here, and not enough colors to represent them all. The NA cells also got taken off completely, which is not ideal.

One thing we can do is to use some functions from the tidyverse package forcats, which can be very handy for dealing with categorical variables like these cell types.

We will use two of these functions in the chunk below: First we will use fct_collapse to take some of the finer labels that we might not be as interested in and collapse them into logical groupings (in this case, the main label that they were part of). After that, we will use fct_relevel to put the remaining factor levels in the order we would like them to appear for plotting.

collapsed_labels <- singler_result_fine$pruned.labels |>
  forcats::fct_collapse(
    "Monocytes" = c(
        "Classical monocytes", 
        "Intermediate monocytes",   
        "Non classical monocytes"),
    "Dendritic cells" = c(
        "Myeloid dendritic cells",
        "Plasmacytoid dendritic cells"),
    "T cells" = c(
        "MAIT cells",
        "Non-Vd2 gd T cells",
        "Vd2 gd T cells"),
    "Helper T cells" = c(
        "Th1 cells",
        "Th1/Th17 cells", 
        "Th17 cells", 
        "Th2 cells",
        "Follicular helper T cells"),
    "B cells" = c(
        "Naive B cells",
        "Switched memory B cells",
        "Non-switched memory B cells",
        "Exhausted B cells",
        "Plasmablasts"      
    )
  ) |>
  # order for plotting
  forcats::fct_relevel(
    "Helper T cells",
    "T regulatory cells",
    "Naive CD4 T cells",
    "Terminal effector CD4 T cells",
    "Naive CD8 T cells",
    "Central memory CD8 T cells",
    "Effector memory CD8 T cells",
    "Terminal effector CD8 T cells",
    "T cells",
    "Natural killer cells",
    "B cells",
    "Monocytes",
    "Dendritic cells",
    "Progenitor cells",
    "Low-density basophils"
  )

Now that we have that set up, we can plot using our collapsed and ordered cell type labels.

sce$celltype_collapsed <- collapsed_labels
scater::plotUMAP(sce, 
                 color_by = "celltype_collapsed")

Heatmap of cell types & clusters

Let’s look at how the cell type assignments we obtained using SingleR compare to the clusters that we found using the unsupervised clustering at the start of this notebook.

To do this, we will again use the table() function, but now with two vectors as input, to build a contingency table of the cell types and clusters that each cell was classified with.

# create a table of clusters & cell type counts
type_cluster_tab <- table(sce$celltype_fine, sce$nn_cluster, useNA = "ifany")

# look at the top corner of the results
type_cluster_tab[1:5, 1:5]
                             
                                 1    2    3    4    5
  Central memory CD8 T cells    81    0    0    0    0
  Classical monocytes            0    0 2195  698    0
  Effector memory CD8 T cells   26    0    0    0    0
  Exhausted B cells              0    0    0    0    0
  Follicular helper T cells     93    0    0    0    0

As you can see, this produced a table with rows for each cell type and columns for each cluster number. The values are the count of cells for each cluster/cell type combination. However, these raw counts are not quite what we’ll want for visualization. Since the total number of cells differs across clusters, we’d like to convert these counts into the proportions of each cell type in each cluster.

We’ll do this by going through the table column by column and dividing each value by the sum for that cluster. This will give us normalized values where the values in each column now sum to 1. To do that, we will use the apply function, which allows us to operate on a matrix row by row or column by column, applying a function to each “slice”. Since the function we want to apply is very short, we will use R’s new (as of version 4.1) anonymous function shorthand: \(x) ... can be used to define a function that that takes as input values x (where the ... is where you would put the expression to calculate). Here we will apply the expression x/sum(x), which will divide each element of a vector x by the sum of its values.

# normalize by the number of cells in each cluster (columns)
type_cluster_tab <- apply(
  type_cluster_tab, 
  2, # apply function to columns
  \(x) x/sum(x) # function to apply
)
# print the normalized values
type_cluster_tab[1:5, 1:5]
                             
                                       1 2         3        4 5
  Central memory CD8 T cells  0.08617021 0 0.0000000 0.000000 0
  Classical monocytes         0.00000000 0 0.9825425 0.656015 0
  Effector memory CD8 T cells 0.02765957 0 0.0000000 0.000000 0
  Exhausted B cells           0.00000000 0 0.0000000 0.000000 0
  Follicular helper T cells   0.09893617 0 0.0000000 0.000000 0

Now we can plot these results as a heatmap, using the pheatmap package. There is a lot of customization we could do here, but pheatmap (pretty heatmap) has good defaults, so we won’t spend too much time on it for now.

# plot with pheatmap
pheatmap::pheatmap(type_cluster_tab)

We can see that most of our clusters are indeed defined by a single cell type, though there are some clusters (e.g., 1 & 9) that have a number of (related) cell types within them. There are also some places where single cell types are spread across a few different clusters (Classical monocytes, for example).

Classifying by clusters

While most of the time we will want to classify single cells, sometimes the sparseness of the data may mean that individual cells do not provide reliable estimates of cell types.

An alternative approach is to classify the clusters as a whole, assuming that the clusters we have identified represent a single cell state. If that is the case, then we should be able to combine the data for all cells across each cluster, then apply our cell typing method to this group of cells. This is similar to an approach we will return to later in the context of differential expression.

The first step here is to create a new matrix where we sum the counts across cells that are from the same type according to our clustering. Because SingleR is a non-parametric approach, we can perform this step with the raw counts matrix. There are a few different ways to do this, but we will use the function DelayedArray::colsum(), which can work directly on the sparse matrices that are often found in SCE objects. We will provide it with the matrix we need, and then a vector of the cluster assignments for each column of the matrix. The function will then sum expression values for each gene across all of the columns that have that value.

# sum count matrix by cluster
cluster_mat <- DelayedArray::colsum(counts(sce), sce$nn_cluster)
# print new dimensions
dim(cluster_mat)
[1] 36601    20

You can see that the resulting matrix still has the same number of rows we have seen before, but now only has as many columns as the number of clusters that the cells were assigned to.

Now we can apply the same SingleR model to these results, using the new matrix as input along with the previously trained model. As there are only 20 clusters to classify, this will be very quick, and we don’t need to parallelize it!

# run SingleR classification with previously trained model
singler_cluster <- SingleR::classifySingleR(
  cluster_mat, # cluster expression matrix
  singler_finemodel # pre-trained model
)

# view results
head(singler_cluster)
DataFrame with 6 rows and 4 columns
                          scores                 labels delta.next
                        <matrix>            <character>  <numeric>
1 0.612108:0.431222:0.615571:...         Th1/Th17 cells 0.01424091
2 0.310607:0.468907:0.306530:...          Naive B cells 0.55307985
3 0.275063:0.805908:0.308321:...    Classical monocytes 0.35835004
4 0.276687:0.776140:0.317954:...    Classical monocytes 0.08904561
5 0.289710:0.708883:0.317147:... Myeloid dendritic ce.. 0.07394915
6 0.462245:0.395730:0.451555:...          Naive B cells 0.00785926
           pruned.labels
             <character>
1         Th1/Th17 cells
2          Naive B cells
3    Classical monocytes
4    Classical monocytes
5 Myeloid dendritic ce..
6          Naive B cells

The result is a fairly small table of results, but we are most interested in the labels, which we would like to associate with each cell in our SCE object for visualization. Since the cluster labels are the row names of that table, we can perform a cute little trick to assign labels back to each cell based on the name of the cluster that it was assigned to. (In this case the cluster names are all numbers, but that might not always be the case.) We’ll select values repeatedly from the singler_cluster table, using the cluster assignment to pick a row, and then always picking the pruned.labels column.

sce$celltype_cluster <- singler_cluster[sce$nn_cluster, "pruned.labels"]

Now we can plot these cluster-based cell type assignments using the now familiar plotUMAP() function.

scater::plotUMAP(sce, color_by = "celltype_cluster")

This sure looks nice and clean, but what have we really done here? We are assuming that each cluster has only a single cell type, which is a pretty bold assumption, as we really aren’t sure that the clusters we created were correct. You may recall that clustering algorithms are quite sensitive to parameter choice, so a different parameter choice could quite likely give a different result.

MetaCell approaches

As a middle ground between the potentially messy single-cell cell type assignment and the almost-certainly overconfident cluster-based assignment above, we can take approach inspired by Baran et al. (2019) using something they called metacells. The idea is that we can perform fine-scaled clustering to identify groups of very similar cells, then sum the counts within those clusters as “metacells” to use for further analysis. The original paper includes a number of optimizations to make sure that the metacell clusters have desirable properties for downstream analysis. We won’t go into that depth here, but we can apply similar ideas.

To begin, we will perform some fine-scale clustering, using a simpler clustering algorithm: K-means clustering. We will use the same bluster package, clustering based on the PCA results we have from earlier, but this algorithm allows us to specify the number of clusters we want to end up with. We have about 8000 cells, so let’s cluster those into groups of approximately 80 cells, which works out to 100 clusters. While this is almost certainly more clusters than are “real” in this dataset, our goal here is not to find differences among clusters, just to get homogeneous groups of cells.

# perform k-means clustering
kclusters <- bluster::clusterRows(
  reducedDim(sce, "PCA"), 
  bluster::KmeansParam(
    centers = 100, # the number of clusters 
    iter.max = 100 # more iterations to be sure of convergence
  )
)

Now we can apply exactly the same approach we did when we had the 20 clusters we had identified with the earlier graph-based clustering.

# create a "metacell" matrix by summing fine-scale clusters
metacell_mat <- DelayedArray::colsum(counts(sce), kclusters)

# apply SingleR model to metacell matrix
metacell_singler <- SingleR::classifySingleR(
  metacell_mat, 
  singler_finemodel
)

# apply metacell cell type assignments to individual cells
sce$celltype_metacell <- metacell_singler[kclusters, "pruned.labels"]

Now we can plot the results as we have done before.

scater::plotUMAP(sce, color_by = "celltype_metacell")
Warning: Removed 208 rows containing missing values or values outside the scale range
(`geom_point()`).

What do you think of this plot? Is this more or less useful than the original cell-based clustering?

Save results

To save disk space (and time), we won’t write out the whole SCE object, as we haven’t changed any of the core data there. Instead we will just write out the cell information table (colData) as a TSV file.

colData(sce) |>
  as.data.frame() |>
  readr::write_tsv(file = cellinfo_file)
LS0tCnRpdGxlOiAiQW5ub3RhdGluZyBjZWxsIHR5cGVzIGZyb20gc2NSTkEtc2VxIGRhdGEiCmF1dGhvcjogRGF0YSBMYWIgZm9yIEFMU0YKZGF0ZTogMjAyMwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIEV4cGxvcmUgZGF0YSBmcm9tIGFudGlib2R5LWRlcml2ZWQgdGFncyAoQ0lURS1zZXEpCi0gQXBwbHkgc2ltcGxlIHJ1bGUtYmFzZWQgY2xhc3NpZmljYXRpb24gdG8gaWRlbnRpZnkgY2VsbCB0eXBlcwotIElkZW50aWZ5IGNlbGwgdHlwZXMgYnkgc2ltaWxhcml0eSB0byByZWZlcmVuY2UgZGF0YXNldHMgd2l0aCBgU2luZ2xlUmAKLSBBcHBseSBgU2luZ2xlUmAgY2xhc3NpZmljYXRpb24gdG8gZ3JvdXBzIG9mIGNlbGxzCgotLS0KCkluIHRoaXMgbm90ZWJvb2ssIHdlIHdpbGwgYXR0ZW1wdCB0byBhbm5vdGF0ZSBjZWxsIHR5cGVzIHRvIGVhY2ggb2YgdGhlIGNlbGxzIGluIGEgZGF0YXNldCwgdXNpbmcgc29tZSBvZiB0aGUgYXV0b21hdGVkIHRvb2xzIHRoYXQgYXJlIGF2YWlsYWJsZSB3aXRoaW4gdGhlIEJpb2NvbmR1Y3RvciB1bml2ZXJzZS4KCk11Y2ggb2YgdGhlIG1hdGVyaWFsIGluIHRoaXMgbm90ZWJvb2sgaXMgZGlyZWN0bHkgaW5zcGlyZWQgYnksIGFuZCBkcmF3cyBoZWF2aWx5IG9uLCBtYXRlcmlhbCBwcmVzZW50ZWQgaW4gdGhlIGJvb2sgW19PcmNoZXN0cmF0aW5nIFNpbmdsZSBDZWxsIEFuYWx5c2lzIHdpdGggQmlvY29uZHVjdG9yX10oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xNi9PU0NBLykuIAoKVGhlIGRhdGEgd2Ugd2lsbCB1c2UgZm9yIHRoaXMgbm90ZWJvb2sgaXMgZGVyaXZlZCBmcm9tIGEgWzEweCBHZW5vbWljcyBkYXRhc2V0IG9mIGh1bWFuIHBlcmlwaGVyYWwgYmxvb2QgbW9ub251Y2xlYXIgY2VsbHMgKFBCTUNzKV0oaHR0cHM6Ly9zb2Z0d2FyZS4xMHhnZW5vbWljcy5jb20vc2luZ2xlLWNlbGwtZ2VuZS1leHByZXNzaW9uL2RhdGFzZXRzLzYuMC4wLzEwa19QQk1Dc19Ub3RhbFNlcV9CXzNwKS4KVGhlc2UgZGF0YSBpbmNsdWRlIGJvdGggc2luZ2xlIGNlbGwgUk5BLXNlcSBjb3VudHMgYW5kIHF1YW50aWZpY2F0aW9uIG9mIGFudGlib2R5LWRlcml2ZWQgdGFncyAoQURUcykgcGVyZm9ybWVkIGJ5IHNlcXVlbmNpbmcgc2hvcnQgRE5BIGJhcmNvZGVzIGF0dGFjaGVkIHRvIHNwZWNpZmljIGFudGlib2RpZXMuIApUaGlzIHR5cGUgb2YgQURUIHNlcXVlbmNpbmcgd2l0aCBzaW5nbGUgY2VsbHMgaXMgY29tbW9ubHkga25vd24gYXMgQ0lURS1zZXEsIGFmdGVyIHRoZSBwcm90b2NvbCBkZXZlbG9wZWQgYnkgW1N0b2Vja2l1cyBfZXQgYWwuXyAoMjAxNyldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDM4L25tZXRoLjQzODApLiAgClRoZSBhbnRpYm9kaWVzIHVzZWQgaGVyZSBhcmUgdGhlIFtUaGUgVG90YWxTZXHihKItQiBIdW1hbiBUQk5LIENvY2t0YWlsXShodHRwczovL3d3dy5iaW9sZWdlbmQuY29tL2VuLXVzL3Byb2R1Y3RzL3RvdGFsc2VxLWItaHVtYW4tdGJuay1jb2NrdGFpbC0xOTA0MyksIGEgc2V0IG9mIGFudGlib2RpZXMgZGVzaWduZWQgdG8gcmVhY3Qgd2l0aCBpbW11bmUgY2VsbCBzdXJmYWNlIG1hcmtlcnMuCgohW1NpbmdsZS1jZWxsIHJvYWRtYXA6IENlbGwgdHlwZV0oZGlhZ3JhbXMvcm9hZG1hcF9zaW5nbGVfY2VsbHR5cGUucG5nKQoKVGhlIGRhdGEgaGVyZSBoYXZlIGFscmVhZHkgYmVlbiBmaWx0ZXJlZCwgbm9ybWFsaXplZCwgYW5kIGhhZCBkaW1lbnNpb24gcmVkdWN0aW9ucyBjYWxjdWxhdGVkIGZvciB0aGUgc2luZ2xlLWNlbGwgUk5BLXNlcSBkYXRhLgpUaGUgQURUIGRhdGEgaGFzIGFsc28gYmVlbiBzZXBhcmF0ZWx5IGZpbHRlcmVkIGFuZCBub3JtYWxpemVkLgpGb3IgZGV0YWlscyBhYm91dCBob3cgdG8gcGVyZm9ybSB0aGVzZSB0YXNrcyB3aXRoIGRhdGEgdGhhdCBoYXMgYmVlbiBwcm9jZXNzZWQgd2l0aCBDZWxsIFJhbmdlciwgeW91IG1heSB3YW50IHRvIGxvb2sgYXQgdGhlIFsiSW50ZWdyYXRpbmcgd2l0aCBwcm90ZWluIGFidW5kYW5jZSIgY2hhcHRlcl0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xNi9PU0NBLmFkdmFuY2VkL2ludGVncmF0aW5nLXdpdGgtcHJvdGVpbi1hYnVuZGFuY2UuaHRtbCNzZXR0aW5nLXVwLXRoZS1kYXRhKSBvZiBPU0NBLgoKVGhlIHByb2Nlc3NlZCBnZW5lIGV4cHJlc3Npb24gYW5kIEFEVCBkYXRhIHdlcmUgc2F2ZWQgaW50byBhIGNvbWJpbmVkIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgKFNDRSkgb2JqZWN0LCBhbmQgd2Ugd2lsbCBzdGFydCB3aXRoIHRoYXQgb2JqZWN0IGZvciBvdXIgZXhwbG9yYXRpb24gaGVyZS4KCiMjIFNldCB1cAoKVG8gc3RhcnQsIHdlIHdpbGwgbG9hZCBzb21lIG9mIHRoZSBsaWJyYXJpZXMgd2Ugd2lsbCBuZWVkIGxhdGVyLCBhbmQgc2V0IGEgcmFuZG9tIG51bWJlciBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkuCgpgYGB7ciBzZXR1cH0KIyBMb2FkIGxpYnJhcmllcwpsaWJyYXJ5KGdncGxvdDIpICMgcGxvdHRpbmcgZnVuY3Rpb25zCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpICMgQmlvY29uZHVjdG9yIHNpbmdsZS1jZWxsIGRhdGEgY2xhc3MKCgojIFNldHRpbmcgdGhlIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxMjM0NSkKYGBgCgoKIyMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKQXMgbWVudGlvbmVkLCBvdXIgaW5wdXQgZmlsZSBoZXJlIGlzIGEgc2luZ2xlIG5vcm1hbGl6ZWQgYW5kIHByb2Nlc3NlZCBTQ0Ugb2JqZWN0LCBzdG9yZWQgYXMgYW4gYHJkc2AgZmlsZS4gClRoYXQgc2hvdWxkIGJlIGFsbCB3ZSBuZWVkIHRvIHJlYWQgaW4hCgpPdXIgb3V0cHV0IHdpbGwgYmUgYSB0YWJsZSBvZiBwZXItY2VsbCBpbmZvcm1hdGlvbiwgd2hpY2ggd2lsbCBpbmNsdWRlIHRoZSBjZWxsIHR5cGUgYXNzaWdubWVudHMgd2UgaGF2ZSBtYWRlIHRocm91Z2hvdXQgdGhpcyBub3RlYm9vay4KV2UgYXJlbid0IHBsYW5uaW5nIGFueSBzaWduaWZpY2FudCBtb2RpZmljYXRpb25zIG9mIHRoZSB1bmRlcmx5aW5nIGRhdGEsIHNvIHdlIHdvbid0IGJvdGhlciByZS1zYXZpbmcgdGhlIHdob2xlIFNDRSBvYmplY3QgYXMgYSBuZXcgYC5yZHNgIGZpbGUgdGhpcyB0aW1lLgoKYGBge3IgZmlsZXBhdGhzLCBsaXZlPVRSVUV9CiMgZGlyZWN0b3J5IGZvciB0aGUgaW5wdXQgZGF0YQpkYXRhX2RpciA8LSBmaWxlLnBhdGgoImRhdGEiLCAKICAgICAgICAgICAgICAgICAgICAgICJQQk1DLVRvdGFsU2VxQiIsIAogICAgICAgICAgICAgICAgICAgICAgIm5vcm1hbGl6ZWQiKQoKIyB0aGUgaW5wdXQgZmlsZSBpdHNlbGYKc2NlX2ZpbGUgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICJQQk1DX1RvdGFsU2VxQl9ub3JtYWxpemVkX3NjZS5yZHMiKQoKIyBBIGRpcmVjdG9yeSB0byBzdG9yZSBvdXRwdXRzCmFuYWx5c2lzX2RpciA8LSBmaWxlLnBhdGgoImFuYWx5c2lzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIlBCTUMtVG90YWxTZXFCIikKCiMgQ3JlYXRlIGRpcmVjdG9yeSBpZiBpdCBkb2Vzbid0IGV4aXN0CmZzOjpkaXJfY3JlYXRlKGFuYWx5c2lzX2RpcikKCiMgb3V0cHV0IHRhYmxlIHBhdGgKY2VsbGluZm9fZmlsZSA8LSBmaWxlLnBhdGgoYW5hbHlzaXNfZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBCTUNfVG90YWxTZXFCX2NlbGxpbmZvLnRzdiIpCmBgYAoKCiMjIEV4cGxvcmluZyBhIENJVEUtc2VxIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAKCk5vdyB0aGF0IHRoZSBwcmVsaW1pbmFyeSBzZXR1cCBpcyBvdXQgb2YgdGhlIHdheSwgd2UgY2FuIGdldCBzdGFydGVkLiAKRmlyc3Qgd2Ugd2lsbCByZWFkIGluIHRoZSBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIGZyb20gdGhlIGlucHV0IGZpbGUgd2UgZGVmaW5lZCBlYXJsaWVyLgoKYGBge3IgcmVhZCBTQ0UsIGxpdmU9VFJVRX0KIyByZWFkIGluIHRoZSBTQ0UgZmlsZQpzY2UgPC0gcmVhZHI6OnJlYWRfcmRzKHNjZV9maWxlKQojIHByaW50IGEgc3VtbWFyeSBvZiB0aGUgU0NFCnNjZQpgYGAKClRoaXMgc2hvdWxkIGxvb2sgc2ltaWxhciB0byB0aGUgU0NFIG9iamVjdHMgdGhhdCB3ZSBoYXZlIHNlZW4gYmVmb3JlLCBjb250YWluaW5nIGBjb3VudHNgIGFuZCBgbG9nY291bnRzYCBhc3NheXMgd2hlcmUgZWFjaCBjZWxsIGlzIGEgY29sdW1uIGFuZCBlYWNoIHJvdyBpcyBhIGdlbmUuCldlIGFsc28gaGF2ZSBzb21lIG9mIHRoZSBgcm93RGF0YWAsIGBjb2xEYXRhYCBhbmQgcmVkdWNlZCBkaW1lbnNpb24gbWF0cmljZXMgdGhhdCB3ZSBoYXZlIHNlZW4gYmVmb3JlLgoKQnV0IHdoZXJlIGFyZSB0aGUgZGF0YSBmcm9tIHRoZSBBRFRzPwpXZSB3b3VsZG4ndCBuZWNlc3NhcmlseSB3YW50IHRob3NlIHN0b3JlZCBpbiB0aGUgbWFpbiBkYXRhIG1hdHJpY2VzLCBhcyB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIEFEVCBiYXJjb2RlIGRhdGEgaXMgZ29pbmcgdG8gYmUgcXVpdGUgZGlmZmVyZW50IGZyb20gZ2VuZSBleHByZXNzaW9uIGRhdGEuCgpUbyBrZWVwIHRoZSBBRFQgZGF0YSBzZXBhcmF0ZSBmcm9tIHRoZSBSTkEgZ2VuZSBleHByZXNzaW9uIGRhdGEsIHdlIGhhdmUgc3BsaXQgdGhpcyBkYXRhIG9mZiBpbnRvIGFuIF9hbHRlcm5hdGl2ZSBleHBlcmltZW50XyAoYGFsdEV4cGApIHNsb3QuCllvdSBjYW4gc2VlIHRoZSBuYW1lIG9mIHRoaXMgYGFsdEV4cGAgb24gdGhlIGxpbmUgYGFsdEV4cE5hbWVzYCBhYm92ZS4gCldlIF9jb3VsZF8gaGF2ZSBtb3JlIHRoYW4gb25lIHR5cGUgb2YgYWx0ZXJuYXRpdmUgZXhwZXJpbWVudCAoc3VjaCBhcyBzcGlrZS1pbiBvciBBVEFDLXNlcSksIGJ1dCBpbiB0aGlzIGNhc2UsIGp1c3QgdGhlIG9uZS4KClRvIGFjY2VzcyB0aGUgY29udGVudHMgb2YgdGhlIGBhbHRFeHBgIHNsb3QsIHdlIGNhbiB1c2UgdGhlIGBhbHRFeHAoKWAgZnVuY3Rpb24uCkxldCdzIGxvb2sgYXQgd2hhdCB3ZSBoYXZlIGluIHRoYXQgc2xvdDoKCmBgYHtyIHZpZXcgYWx0RXhwLCBsaXZlPVRSVUV9CiMgcHJpbnQgYSBzdW1tYXJ5IG9mIHRoZSAnQURUJyBhbHRFeHAKYWx0RXhwKHNjZSwgIkFEVCIpCmBgYAoKSXQgaXMgYW5vdGhlciBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgISAKSW5jZXB0aW9uIQpMZXQncyBsb29rIGF0IHRoYXQgZW1iZWRkZWQgU0NFIG1vcmUgY2xvc2VseS4KClRoZSBmaXJzdCB0aGluZyB0byBub3RlIGlzIHRoYXQgdGhpcyBgYWx0RXhwYCBoYXMgdGhlIHNhbWUgbnVtYmVyIG9mIGNvbHVtbnMgYXMgZGlkIHRoZSBtYWluIFNDRSBvYmplY3QuIApUaG9zZSBjb3JyZXNwb25kZWQgdG8gdGhlIGluZGl2aWR1YWwgY2VsbHMgYmVmb3JlLCBhbmQgc3RpbGwgZG8hCgpUaGVyZSBhcmUgb25seSAxMCByb3dzLCBob3dldmVyLCBhbmQgdGhlc2UgY29ycmVzcG9uZCB0byB0aGUgQURUcyB0aGF0IHdlcmUgYXNzYXllZCBieSB0aGlzIHBhcnRpY3VsYXIgZXhwZXJpbWVudC4gCkp1c3QgYXMgd2UgZGlkIHdpdGggdGhlIGZ1bGwgU0NFLCB3ZSBjYW4gdXNlIGByb3dEYXRhKClgIHRvIHZpZXcgdGhlIHRhYmxlIGNvbnRhaW5pbmcgbWV0YWRhdGEgYXNzb2NpYXRlZCB3aXRoIGVhY2ggb2YgdGhlc2Ugcm93cy4KV2UnbGwgYWRkIHRoZSBgYWx0RXhwKClgIGZ1bmN0aW9uIHRvIHBvaW50IGl0IHRvIHRoZSBlbWJlZGRlZCBvYmplY3Qgd2UgYXJlIGludGVyZXN0ZWQgaW4uIApTaW5jZSB0aGVyZSBpcyBvbmx5IG9uZSBgYWx0RXhwYCwgd2UgZG9uJ3QgbmVlZCB0aGUgc2Vjb25kIChuYW1lKSBhcmd1bWVudCAoYCJBRFQiYCkgdGhhdCB3ZSB1c2VkIGFib3ZlOyB0aGUgZGVmYXVsdCBiZWhhdmlvciBvZiBgYWx0RXhwKClgIGlzIHRvIGp1c3QgZ2l2ZSB1cyB0aGUgZmlyc3QgYGFsdEV4cGAsIGFuZCB0aGF0IGlzIHRoZSBvbmUgKGFuZCBvbmx5KSB0aGF0IHdlIG5lZWQuCgpgYGB7ciBhZHQgcm93cywgbGl2ZT1UUlVFfQojIFdoYXQgcHJvdGVpbnMgd2VyZSBhc3NheWVkPwpyb3dEYXRhKGFsdEV4cChzY2UpKQpgYGAKCllvdSBjYW4gc2VlIGhlcmUgdGhlIG5hbWVzIGFuZCBzeW1ib2xzIG9mIHRoZSB0YWdzIHVzZWQsIGFsb25nIHdpdGggdGhlIGRlc2lnbmF0aW9uIHRoYXQgYWxsIGhhdmUgYW4gIkFudGlib2R5IENhcHR1cmUiIHR5cGUgKGFzIG9wcG9zZWQgdG8gIkdlbmUgRXhwcmVzc2lvbiIgZm9yIHRoZSBSTkEgZGF0YSkuCk9uZSB5b3UgbWlnaHQgbm90ZSBsb29rcyBkaWZmZXJlbnQgaXMgdGhlIGBJZ0cxYCBjb250cm9sLCB3aGljaCBpcyBhY3R1YWxseSBhIG1vdXNlIGFudGlib2R5IHVzZWQgYXMgYSBuZWdhdGl2ZSBjb250cm9sLiAKCgojIyMgQ2x1c3RlcmluZyByZWR1eAoKV2hpbGUgZGltZW5zaW9uIHJlZHVjdGlvbiB3YXMgcGVyZm9ybWVkIG9uIHRoaXMgZGF0YSwgd2UgaGF2ZSBub3QgeWV0IHBlcmZvcm1lZCBhbnkgY2x1c3RlcmluZy4KCkxldCdzIGFzc2lnbiBzb21lIGNsdXN0ZXJzIHRvIG91ciBjZWxscywgdXNpbmcgZ3JhcGgtYmFzZWQgY2x1c3RlcmluZyBhbmQgZGVmYXVsdCBwYXJhbWV0ZXJzLCB0YWtpbmcgYXMgaW5wdXQgdGhlIFBDQSBtYXRyaXggdGhhdCB3YXMgcHJldmlvdXNseSBjYWxjdWxhdGVkLgpOb3RlIHRoYXQgdGhpcyBQQ0EgbWF0cml4IGFuZCB0aGUgVU1BUCBidWlsdCBmcm9tIGl0IHdlcmUgZGVyaXZlZCBmcm9tIHRoZSBnZW5lIGV4cHJlc3Npb24gZGF0YSwgc28gdGhlIGNsdXN0ZXJpbmcgaXMgZ29pbmcgdG8gcmVmbGVjdCB0aGUgZ2VuZSBleHByZXNzaW9uIGRhdGEgb25seS4KV2hpbGUgd2UgaGF2ZSB0aGUgQURUIGRhdGEsIGl0IGlzIF9ub3RfIGJlaW5nIHVzZWQgZm9yIHRoaXMgc3RhZ2Ugb2YgdGhlIGFuYWx5c2lzLgoKYGBge3IgY2x1c3RlciBjZWxscywgbGl2ZT1UUlVFfQojIHBlcmZvcm0gY2x1c3RlcmluZwpubl9jbHVzdGVycyA8LSBibHVzdGVyOjpjbHVzdGVyUm93cygKICAjIFBDQSBpbnB1dAogIHJlZHVjZWREaW0oc2NlLCAiUENBIiksIAogICMgZ3JhcGggY2x1c3RlcmluZyAmIHBhcmFtZXRlcnMKICBibHVzdGVyOjpOTkdyYXBoUGFyYW0oKQopCgojIGFkZCBjbHVzdGVycyB0byBjb2xEYXRhCnNjZSRubl9jbHVzdGVyIDwtIG5uX2NsdXN0ZXJzCmBgYAoKTm93IHdlIGNhbiBwbG90IHRoZSBjbHVzdGVycyB3ZSBoYXZlIGlkZW50aWZpZWQgd2l0aCBgc2NhdGVyOjpwbG90VU1BUCgpYC4gClRoaXMgaXMgYSBzaG9ydGN1dCBmb3IgYHNjYXRlcjo6cGxvdFJlZHVjZWREaW0oZGltcmVkID0gIlVNQVAiLCAuLi4pYCwgd2hpY2ggY2FuIHNhdmUgdXMgYSBsb3Qgb2YgdHlwaW5nIGFzIHdlIGRvIHRoaXMgcmVwZWF0ZWRseSEKCmBgYHtyIHBsb3QgY2x1c3RlcnN9CiMgcGxvdCBjbHVzdGVycwpzY2F0ZXI6OnBsb3RVTUFQKHNjZSwgY29sb3JfYnkgPSAibm5fY2x1c3RlciIpICsgCiAgIyByZW5hbWUgdGhlIGxlZ2VuZAogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJDbHVzdGVyIikpCmBgYApCdXQgd2hhdCBhcmUgdGhlc2UgY2x1c3RlcnMsIHJlYWxseT8gCkRvIHRoZXkgY29ycmVzcG9uZCB0byBwYXJ0aWN1bGFyIGNlbGwgdHlwZXMgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbj8KCkRvZXMgaXQgYm90aGVyIHlvdSB0aGF0IHdlIGp1c3QgdXNlZCB0aGUgZGVmYXVsdCBuZWFyZXN0LW5laWdoYm9yIGdyYXBoIGNsdXN0ZXJpbmcgcGFyYW1ldGVycz8KRG8geW91IGtub3cgd2hhdCB0aG9zZSB3ZXJlPwoKIyMgSW52ZXN0aWdhdGluZyBjZWxsIHR5cGVzCgojIyMgVXNpbmcgQURUIGRhdGEKClRoZSBmaXJzdCB3YXkgd2Ugd2lsbCBpZGVudGlmeSBjZWxsIHR5cGVzIG9mIGluZGl2aWR1YWwgY2VsbHMgaXMgdG8gdXNlIHRoZSBBRFQgbm9ybWFsaXplZCBjb3VudHMuClRoZXNlIGFudGlib2R5IG1hcmtlcnMgd2VyZSAoaG9wZWZ1bGx5KSBjaG9zZW4gZm9yIHRoZWlyIHJlbGV2YW5jZSB0byB0aGUgc2VxdWVuY2VkIGNlbGwgcG9wdWxhdGlvbi4KClRoZSBmaXJzdCBtYXJrZXIgd2Ugd2lsbCBsb29rIGF0IGlzIGBDRDNgLCB3aGljaCBpcyBhIHByb3RlaW4gY29tcGxleCB0aGF0IGlzIGZvdW5kIG9uIHRoZSBzdXJmYWNlIG9mIFQgY2VsbHMuCldlIGNhbiBhZ2FpbiB1c2UgdGhlIGBwbG90VU1BUCgpYCBmdW5jdGlvbiB0byBjb2xvciBjZWxscyBieSBgQ0QzYCBBRFQgbGV2ZWxzLiAKCk5vdGUgdGhhdCB0aGlzIGZ1bmN0aW9uIGNhbiBwbG90IGRhdGEgZnJvbSB0aGUgYGNvbERhdGFgIHRhYmxlIChhcyB3ZSB1c2VkIGl0IGFib3ZlIHdoZW4gcGxvdHRpbmcgY2x1c3RlcnMpLCBpbiB0aGUgbWFpbiBnZW5lIGV4cHJlc3Npb24gbWF0cml4IChhcyB3ZSB1c2VkIGl0IGluIHRoZSBwcmV2aW91cyBub3RlYm9vayksICpBTkQqIGluIGBhbHRFeHBgIHRhYmxlcyBhbmQgbWF0cmljZXMhClNvIHRvIGNvbG9yIGJ5IHRoZSBBRFQgbGV2ZWxzIChhcyBub3JtYWxpemVkIGluIHRoZSBgbG9nY291bnRzYCBtYXRyaXgpIHdlIG9ubHkgbmVlZCB0byBwcm92aWRlIHRoZSB0YWcgbmFtZSB0aGF0IHdlIHdhbnQgdG8gcGxvdCBpbiB0aGUgYGNvbG9yX2J5YCBhcmd1bWVudC4KCmBgYHtyIHBsb3QgQ0QzLCBsaXZlPVRSVUV9CiMgcGxvdCBDRDMgZXhwcmVzc2lvbgpzY2F0ZXI6OnBsb3RVTUFQKHNjZSwgY29sb3JfYnkgPSAiQ0QzIikKYGBgCgpJdCBhcHBlYXJzIHRoYXQgd2UgaGF2ZSBhIG51bWJlciBvZiBwb3RlbnRpYWwgVCBjZWxscyBkb3duIGluIHRoZSBsb3dlciBsZWZ0IQoKTGV0J3MgbG9vayBhdCBhIGNvdXBsZSBvZiBvdGhlciBtYXJrZXJzIHRvIHRyeSB0byBicmVhayB0aG9zZSB1cCBtb3JlIHNwZWNpZmljYWxseS4KClR3byBvdGhlciBtYXJrZXJzIG9mIHJlbGV2YW5jZSB0byB0aGUgVCBjZWxscyBhcmUgYENENGAgYW5kIGBDRDhgLgpUaGUgYENENGAgY29tcGxleCBpcyBwcmVzZW50IGluIGhlbHBlciBUIGNlbGxzIChoZW5jZSB0aGVpciBvdGhlciBjb21tb24gbmFtZSwgQ0Q0KyBUIGNlbGxzKS4KQnkgY29udHJhc3QsIHRoZSBgQ0Q4YCBjb21wbGV4IGlzIGZvdW5kIG9uIGtpbGxlciBUIGNlbGxzIChDRDgrIGNlbGxzKS4KCkxldCdzIHBsb3QgdGhlIEFEVCByZXN1bHRzIGZvciB0aG9zZSB0d28gbWFya2VycyBhcyB3ZWxsIGJlbG93OgoKYGBge3IgcGxvdCBDRDQsIGxpdmU9VFJVRX0KIyBwbG90IENENCBtYXJrZXIKc2NhdGVyOjpwbG90VU1BUChzY2UsIAogICAgICAgICAgICAgICAgIGNvbG9yX2J5ID0gIkNENCIpCmBgYAoKYGBge3IgcGxvdCBDRDgsIGxpdmU9VFJVRX0KIyBwbG90IENEOCBtYXJrZXIKc2NhdGVyOjpwbG90VU1BUChzY2UsIAogICAgICAgICAgICAgICAgIGNvbG9yX2J5ID0gIkNEOCIpCmBgYAoKCiMjIyBSdWxlLWJhc2VkIGNsYXNzaWZpY2F0aW9uCgpQbG90dGluZyB0aGUgbGV2ZWxzIG9mIHRoZSBBRFRzIHByb3ZpZGVzIGEgbmljZSB2aXN1YWwgcmVwcmVzZW50YXRpb24sIGJ1dCB3aGF0IHdlIHJlYWxseSB3YW50IHRvIGRvIGlzIHRvIHR1cm4gdGhlc2UgdmFsdWVzIGludG8gc3BlY2lmaWMgY2VsbC10eXBlIGFzc2lnbm1lbnRzIGZvciBlYWNoIGNlbGwuClN1Y2ggY2xhc3NpZmljYXRpb24gY291bGQgYmUgY29uc2lkZXJlZCBhcyBhbmFsb2dvdXMgdG8gYSBjZWxsLXNvcnRlciBhc3NheSwgd2hlcmUgd2Ugd291bGQgc2V0IHVwIHNvbWUgcnVsZXMgdG8gbG9vayBhdCBhIGZldyBtYXJrZXJzIGZvciBlYWNoIGNlbGwgYW5kIHVzZSB0aG9zZSB0byBhc3NpZ24gYSBjZWxsIHR5cGUuClRoZSBzaW1wbGVzdCB0eXBlIG9mIHJ1bGUgbWlnaHQgYmUgb25lIHdoZXJlIHdlIHVzZSBhIHRocmVzaG9sZCB0byBjYWxsIGEgbWFya2VyIGFzIHByZXNlbnQgb3IgYWJzZW50LCBhbmQgdGhlbiB1c2UgdGhlIHByZXNlbmNlIG9mIGEgbWFya2VyIHRvIGluZGljYXRlIGEgc3BlY2lmaWMgY2VsbCB0eXBlLgoKVG8gZG8gdGhpcywgd2Ugd2lsbCBuZWVkIHRvIG1ha2Ugc29tZSBkZWNpc2lvbnMsIHN1Y2ggYXMgdGhlIHRocmVzaG9sZHMgd2Ugc2hvdWxkIHVzZSB0byBkZXRlcm1pbmUgd2hldGhlciBhIGNlbGwgaXMgb3IgaXMgbm90IGV4cHJlc3NpbmcgYSBwYXJ0aWN1bGFyIG1hcmtlci4gCkluIGdlbmVyYWwsIG1hcmtlcnMgdGhhdCBhcmUgdXNlZnVsIGZvciB0aGlzIGNlbGwtdHlwaW5nIGFwcHJvYWNoIHdpbGwgaGF2ZSBhIGJpbW9kYWwgZGlzdHJpYnV0aW9uIG9mIGV4cHJlc3Npb24gbGV2ZWxzIHdoaWNoIGNhbiBiZSB1c2VkIHRvIHNlcGFyYXRlIHRoZSBwb3B1bGF0aW9uIGludG8gdHdvIGdyb3VwcyBvZiBjZWxscy4KT25lIGdyb3VwIG9mIGNlbGxzIHdpbGwgaGF2ZSBvbmx5IGEgYmFja2dyb3VuZCBsZXZlbCBzaWduYWwgZm9yIGVhY2ggbWFya2VyIChkdWUgdG8gbm9uLXNwZWNpZmljIGJpbmRpbmcgb3Igb3RoZXIgZmFjdG9ycyksIHdoaWxlIHRoZSBvdGhlciBncm91cCwgdGhvc2UgdGhhdCBleHByZXNzIHRoZSBwcm90ZWluLCB3aWxsIGhhdmUgYSBtdWNoIGhpZ2hlciBsZXZlbCBvZiBiaW5kaW5nIGFuZCBoaWdoZXIgY291bnRzLgoKVG8gYXNzZXNzIHdoZXRoZXIgdGhlIEFEVHMgd2UgaGF2ZSBjaG9zZW4gaGF2ZSBhIHVzZWZ1bCBkaXN0cmlidXRpb24gb2YgZXhwcmVzc2lvbiB2YWx1ZXMsIGFuZCB0byBpZGVudGlmeSB0aHJlc2hvbGRzIHdlIG1pZ2h0IHVzZSwgd2Ugd291bGQgbGlrZSB0byBwbG90IGVhY2ggQURUIHRhZy4KVG8gZG8gdGhpcywgd2Ugd2lsbCBwdWxsIG91dCB0aGUgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIHRoZXNlIG1hcmtlcnMgZnJvbSB0aGUgU0NFIG9iamVjdCBhbmQgZG8gc29tZSBkYXRhIHdyYW5nbGluZy4gCgpXZSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgbm9ybWFsaXplZCBjb3VudHMgZm9yIHRoZSBBRFQgdGFncywgd2hpY2ggYXJlIHN0b3JlZCBpbiB0aGUgYGxvZ2NvdW50c2AgYXNzYXkgb2YgdGhlIGBhbHRFeHBgLgpJZiB5b3UgcmVjYWxsLCB0aGlzIG1hdHJpeCBpcyBzdG9yZWQgd2l0aCB0aGUgY29sdW1ucyBhcyBjZWxscyBhbmQgcm93cyBhcyBtYXJrZXJzLCBidXQgd2UgcmVhbGx5IHdhbnQgaXQgd2l0aCBlYWNoIHJvdyBhIGNlbGwgYW5kIGVhY2ggY29sdW1uIGEgbWFya2VyLiAKU28gd2Ugd2lsbCBmaXJzdCB0cmFuc3Bvc2UgdGhlIGRhdGEsIHRoZW4gY29udmVydCBpdCB0byBhIGRhdGEgZnJhbWUgZm9yIG91ciBuZXh0IHN0ZXBzLgpCZWNhdXNlIHRoZSBTQ0Ugb2JqZWN0IHN0b3JlcyB0aGUgYXNzYXkgZGF0YSBtYXRyaWNlcyBpbiBhIHNwZWNpYWxpemVkIGZvcm1hdCwgd2UgaGF2ZSB0byBkbyBvbmUgZXh0cmEgc3RlcCBjb252ZXJ0IGl0IGZpcnN0IHRvIGEgInJlZ3VsYXIiIFIgbWF0cml4IG9yIFIgd29uJ3Qga25vdyBob3cgdG8gY29udmVydCBpdCB0byBhIGRhdGEgZnJhbWUuCgpgYGB7ciBleHRyYWN0IEFEVH0KIyBjb252ZXJ0IGxvZ2NvdW50cyBkYXRhIHRvIGEgZGF0YSBmcmFtZQphZHRfZGYgPC0gbG9nY291bnRzKGFsdEV4cChzY2UpKSB8PgogIHQoKSB8PiAjIHRyYW5zcG9zZQogIGFzLm1hdHJpeCgpIHw+ICMgY29udmVydCB0byBtYXRyaXgKICBhcy5kYXRhLmZyYW1lKCkgIyBjb252ZXJ0IHRvIGRhdGEgZnJhbWUKCiMgdmlldyB0aGUgZGF0YSBmcmFtZQpoZWFkKGFkdF9kZikKYGBgCgpJZiB3ZSBqdXN0IHdhbnRlZCB0byBwbG90IG9uZSBvZiB0aGVzZSB0YWdzLCB3ZSBjb3VsZCBkbyBzbyByaWdodCBhd2F5LCBidXQgd2l0aCBhIGJpdCBtb3JlIGRhdGEgd3JhbmdsaW5nLCB3ZSBjYW4gY29udmVydCB0aGVzZSByZXN1bHRzIGludG8gYSAidGlkaWVyIiBmb3JtYXQsIHRoYXQgd2lsbCBhbGxvdyB1cyB0byB0YWtlIGZ1bGwgYWR2YW50YWdlIG9mIGB0aWR5dmVyc2VgIHRvb2xzIQpJbiBwYXJ0aWN1bGFyLCBpdCB3aWxsIGxldCB1cyBwbG90IHRoZW0gYWxsIGF0IG9uY2Ugd2l0aCBgZ2dwbG90MmAgZmFjZXRpbmcuCgpSaWdodCBub3cgdGhlIGRhdGEgaXMgaW4gYSAid2lkZSIgZm9ybWF0LCBzdWNoIHRoYXQgZWFjaCBjb2x1bW4gaXMgYSBkaWZmZXJlbnQgdGFnLiAKQnV0IHRoZSBkYXRhIGluIGFsbCBvZiB0aGUgY29sdW1ucyBpcyB0aGUgc2FtZSB0eXBlLCBhbmQgbWVhc3VyZXMgc29tZXRoaW5nIHNpbWlsYXI6IHRoZSBub3JtYWxpemVkIGNvdW50IG9mIGFuIEFEVC4KT25lIGNvdWxkIGV2ZW4gYXJndWUgdGhhdCBlYWNoIHJvdyBjb250YWlucyAxMCBkaWZmZXJlbnQgb2JzZXJ2YXRpb25zLCB3aGVyZSB0aGUgInRpZHkiIGRhdGEgaWRlYWwsIGFzIGVzcG91c2VkIGJ5IFtXaWNraGFtICgyMDE0KV0oaHR0cHM6Ly9kb2kub3JnLzEwLjE4NjM3L2pzcy52MDU5LmkxMCksIHJlcXVpcmVzIGEgc2luZ2xlIG9ic2VydmF0aW9uIHBlciByb3csIGEgImxvbmciIGZvcm1hdC4KVGhpcyBsb25nIGZvcm1hdCB3aWxsIGhhdmUgb25lIGNvbHVtbiB0aGF0IHRlbGxzIHVzIHdoaWNoIEFEVCB3YXMgbWVhc3VyZWQgYW5kIGEgc2Vjb25kIGNvbHVtbiB3aXRoIHRoZSBtZWFzdXJlbWVudCB2YWx1ZSBpdHNlbGYuCgpXZSBjYW4gcGVyZm9ybSB0aGlzIGNvbnZlcnNpb24gdXNpbmcgdGhlIFtgdGlkeXI6OnBpdm90X2xvbmdlcigpYF0oaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnL2FydGljbGVzL3Bpdm90Lmh0bWwpIGZ1bmN0aW9uLAp3aGljaCBhbGxvd3MgdXMgdG8gY29udmVydCBvdXIgZGF0YSBmcmFtZSB3aXRoIG9uZSBjb2x1bW4gcGVyIHRhZyBpbnRvIGEgZGF0YSBmcmFtZSB3aXRoIHNlcGFyYXRlIGNvbHVtbnMgZm9yIHRoZSB0YWcgaWQgKGBBRFRgKSBhbmQgdGhlIGV4cHJlc3Npb24gdmFsdWUgKGBsb2djb3VudGApLgpGb2xsb3dpbmcgY29udmVyc2lvbiwgd2Ugd2lsbCBmaWx0ZXIgdG8ganVzdCB0aGUgQURUcyB0aGF0IHdlIGNhcmUgYWJvdXQuCgpgYGB7ciBwaXZvdCBsb25nZXJ9CmFkdF9kZl9sb25nIDwtIGFkdF9kZiB8PgogICMgcGl2b3QgdG8gbG9uZyBmb3JtYXQKICB0aWR5cjo6cGl2b3RfbG9uZ2VyKAogICAgZXZlcnl0aGluZygpLCAjIHVzZSBhbGwgY29sdW1ucwogICAgbmFtZXNfdG8gPSAiQURUIiwgIyBjb252ZXJ0IHJvdyBuYW1lcyB0byBhIGNvbHVtbiBjYWxsZWQgIkFEVCIKICAgIHZhbHVlc190byA9ICJsb2djb3VudCIgIyBuYW1lIHRoZSB2YWx1ZSBjb2x1bW4gImxvZ2NvdW50IgogICkgfD4KICAjIGZpbHRlciB0byB0YWdzIHdlIGFyZSBpbnRlcmVzdGVkIGluCiAgZHBseXI6OmZpbHRlcihBRFQgJWluJSBjKCJDRDMiLCAiQ0Q0IiwgIkNEOCIpKQoKIyBsb29rIGF0IHRoZSByZXN1bHRpbmcgZGYKaGVhZChhZHRfZGZfbG9uZykKYGBgCgpOb3cgd2UgY2FuIG1ha2UgYSBkZW5zaXR5IHBsb3Qgd2l0aCBgZ2dwbG90MmAgZm9yIGFsbCB0aHJlZSBBRFRzIHdlIGFyZSBpbnRlcmVzdGVkIGluIGF0IG9uY2UuCgpgYGB7ciBwbG90IEFEVHMsIGxpdmU9VFJVRX0KIyBwbG90IGxvZ2NvdW50cyBieSBBRFQKZ2dwbG90KGFkdF9kZl9sb25nLCBhZXMoeCA9IGxvZ2NvdW50LCBmaWxsID0gQURUKSkgKyAKICBnZW9tX2RlbnNpdHkoKSArICMgZGVuc2l0eSBwbG90CiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhBRFQpKSArICMgZmFjZXQgYnkgQURUCiAgdGhlbWVfYncoKSArICMgbmljZXIgdGhlbWUKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICMgbm8gbGVnZW5kIG5lZWRlZApgYGAKClRoZXNlIGxvb2sgcHJldHR5IGdvb2QhCkVhY2ggb2YgdGhlc2UgbWFya2VycyBoYXMgYSBiaW1vZGFsIGRpc3RyaWJ1dGlvbjogQSBsb3dlciBwZWFrIGNvbnNpc3Rpbmcgb2YgY2VsbHMgdGhhdCBkbyBub3QgZXhwcmVzcyB0aGUgcHJvdGVpbiBidXQgd2hpY2ggc3RpbGwgaGF2ZSBhIGJhY2tncm91bmQgbGV2ZWwgb2YgYW50aWJvZHkgYmluZGluZywgYW5kIGFuIHVwcGVyIHBlYWsgb2YgY2VsbHMgdGhhdCBkbyBleHByZXNzIHRoZSBwcm90ZWluIG9mIGludGVyZXN0LgpUaGUgYmFja2dyb3VuZCBsZXZlbCBkb2VzIHZhcnkgYnkgYW50aWJvZHkgbWFya2VyLCBzbyB3ZSB3aWxsIG5lZWQgYSBkaWZmZXJlbnQgdGhyZXNob2xkIHZhbHVlIGZvciBlYWNoIG9uZS4KCldlIGNhbiBub3cgdXNlIHRoZSB2YWx1ZXMgZnJvbSB0aGVzZSBwbG90cyB0byBjb25zdHJ1Y3QgYSBzZXQgb2YgcnVsZXMgdG8gY2xhc3NpZnkgdGhlIFQgY2VsbHMuIApXZSB3aWxsIGRvIHRoaXMgdXNpbmcgdGhlICJ3aWRlIiBkYXRhIGZyYW1lIGZyb20gZWFybGllci4gCgpUaGUgdGhyZXNob2xkcyB3ZSBhcmUgdXNpbmcgaGVyZSB3ZXJlIGlkZW50aWZpZWQganVzdCAiYnkgZXllIiwgc28gdGhpcyBpcyBub3QgYSBwYXJ0aWN1bGFybHkgcHJpbmNpcGxlZCBtZXRob2Qgb2YgY2VsbCB0eXBlIGFzc2lnbm1lbnQsIGJ1dCBpdCBjYW4gYmUgZmFpcmx5IGVmZmVjdGl2ZS4KSGVyZSB3ZSBhcmUgYXNzaWduaW5nIG9ubHkgdGhyZWUgY2VsbCB0eXBlczsgY2VsbHMgdGhhdCBkbyBub3QgZml0IGFueSBvZiB0aGVzZSBjcml0ZXJpYSB3aWxsIGJlIHNldCBhcyBgTkFgLgoKYGBge3IgdGhyZXNob2xkIGNlbGx0eXBlc30KIyBhZGQgY2VsbCB0eXBlIGNvbHVtbiBieSB0aHJlc2hvbGRpbmcKYWR0X2RmIDwtIGFkdF9kZiB8PgogIGRwbHlyOjptdXRhdGUoCiAgICBjZWxsdHlwZSA9IGRwbHlyOjpjYXNlX3doZW4oCiAgICAgIENEMyA+IDYuNyAmIENENCA+IDggfiAiQ0Q0KyBULWNlbGwiLAogICAgICBDRDMgPiA2LjcgJiBDRDggPiA2IH4gIkNEOCsgVC1jZWxsIiwKICAgICAgQ0QzID4gNi43IH4gIlQtY2VsbCIKICAgICkKICApCgphZHRfZGYKYGBgCgpOb3cgd2Ugd2lsbCB3YW50IHRvIGFkZCB0aGUgY2VsbCB0eXBlcyB3ZSBoYXZlIGFzc2lnbmVkIGJhY2sgdG8gb3VyIG9yaWdpbmFsIFNDRSBvYmplY3QuCldlIGNhbiBkbyB0aGF0IGJ5IGRlZmluaW5nIGEgbmV3IGNvbHVtbiBuYW1lLCBgdGhyZXNob2xkX2NlbGx0eXBlYCB0aGF0IHdpbGwgYmUgYWRkZWQgdG8gdGhlIGBjb2xEYXRhYCBvYmplY3QuCkNyZWF0aW5nIGFuZCBhc3NpZ25pbmcgdmFsdWVzIHRvIHRoaXMgY29sdW1uIGNhbiBiZSBkb25lIHdpdGggdGhlIGAkYCBzaG9ydGN1dCwgYW5kIHRoZW4gd2UgY2FuIHBsb3Qgb3VyIHJlc3VsdHMgd2l0aCB0aGUgYHBsb3RVTUFQKClgIGZ1bmN0aW9uIGFzIGJlZm9yZS4KCmBgYHtyIHBsb3QgdGhyZXNob2xkc30Kc2NlJHRocmVzaG9sZF9jZWxsdHlwZSA8LSBhZHRfZGYkY2VsbHR5cGUKc2NhdGVyOjpwbG90VU1BUChzY2UsIAogICAgICAgICAgICAgICAgIGNvbG9yX2J5ID0gInRocmVzaG9sZF9jZWxsdHlwZSIpICsgCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIkNlbGwgdHlwZSIpKQpgYGAKCkhvdyBkaWQgd2UgZG8/IAoKTm90ZSB0aGF0IHdoaWxlIHdlIGFwcGxpZWQgdGhpcyB0ZWNobmlxdWUgdG8gYXNzaWduIGNlbGwgdHlwZXMgdXNpbmcgdGhlIEFEVCBkYXRhLCB3ZSBjb3VsZCB1c2UgdGhlIHNhbWUgdHlwZSBvZiBwcm9jZWR1cmUgdXNpbmcgZ2VuZSBleHByZXNzaW9uIGRhdGEgYWxvbmUsIG9yIGEgY29tYmluYXRpb24gb2YgZ2VuZSBleHByZXNzaW9uIGRhdGEgYW5kIHRhZyBkYXRhLgoKSG93ZXZlciwgd2hhdCB3ZSBkaWQgaGVyZSB3YXMgdmVyeSBhZC1ob2MgYW5kIHF1aXRlIG1hbnVhbCEKV2UgZGlkbid0IGNhbGN1bGF0ZSBhbnkgc3RhdGlzdGljcywgYW5kIHdlIGhhZCB0byBsb29rIGF0IGV2ZXJ5IHRhZyB3ZSB3ZXJlIGludGVyZXN0ZWQgaW4gdG8gcGljayB0aHJlc2hvbGRzLgpBIGRpZmZlcmVudCBkYXRhc2V0IG1pZ2h0IGhhdmUgZGlmZmVyZW50IGJhY2tncm91bmQgbGV2ZWxzLCB3aGljaCB3b3VsZCByZXF1aXJlIGRpZmZlcmVudCB0aHJlc2hvbGRzLiAKCldoaWxlIHRoaXMgdGVjaG5pcXVlIG1pZ2h0IGJlIGdvb2QgZm9yIHNvbWUgc2ltcGxlIGV4cGVyaW1lbnRzLCBhbmQgY2FuIGJlIHVzZWZ1bCBmb3IgbWFudWFsIGN1cmF0aW9uLCBpdCBtaWdodCBub3QgdHJhbnNsYXRlIHdlbGwgdG8gbW9yZSBjb21wbGV4IGRhdGFzZXRzIHdpdGggbXVsdGlwbGUgc2FtcGxlcy4KV2UgYWxzbyBsb29rZWQgYXQgZWFjaCBtYXJrZXIgc2VwYXJhdGVseSwgd2hpY2ggbWlnaHQgbm90IGJlIHRoZSBtb3N0IGVmZmljaWVudCBvciByb2J1c3QgbWV0aG9kIG9mIGFuYWx5c2lzLgoKRm9yIGEgbW9yZSBwcmluY2lwbGVkIGFwcHJvYWNoIHRoYXQgYWxsb3dzIGlkZW50aWZpY2F0aW9uIG9mIGNlbGwgdHlwZXMgYnkgbG9va2luZyBhdCB0aGUgZXhwcmVzc2lvbiBvZiBzZXRzIG9mIGdlbmVzIHRoYXQgYXJlIGtub3duIHRvIGNoYXJhY3Rlcml6ZSBlYWNoIGNlbGwgdHlwZSwgeW91IG1pZ2h0IGxvb2sgYXQgdGhlIFtgQVVDZWxsYCBwYWNrYWdlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvMy4xNi9iaW9jL2h0bWwvQVVDZWxsLmh0bWwpLgpGb3IgbW9yZSBvbiB0aGF0IG1ldGhvZCwgdGhlIE9TQ0Egc2VjdGlvbiBbQXNzaWduaW5nIGNlbGwgbGFiZWxzIGZyb20gZ2VuZSBzZXRzXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9ib29rcy8zLjE2L09TQ0EuYmFzaWMvY2VsbC10eXBlLWFubm90YXRpb24uaHRtbCNhc3NpZ25pbmctY2VsbC1sYWJlbHMtZnJvbS1nZW5lLXNldHMpIGlzIGEgdmVyeSBnb29kIHJlZmVyZW5jZS4KCgojIyBDZWxsIHR5cGUgYW5ub3RhdGlvbiB3aXRoIGBTaW5nbGVSYAoKQW4gYWx0ZXJuYXRpdmUgYXBwcm9hY2ggdG8gdXNpbmcga25vd24gbWFya2VyIGdlbmVzIGZvciBjbGFzc2lmaWNhdGlvbiBpcyB0byBpbnN0ZWFkIGNsYXNzaWZ5IGNlbGxzIGJ5IGNvbXBhcmluZyB0aGVtIHRvIGEgcmVmZXJlbmNlIGV4cHJlc3Npb24gZGF0YXNldC4KVG8gZG8gdGhpcywgd2Ugd2lsbCBmaW5kIGEgd2VsbC1jdXJhdGVkIGdlbmUgZXhwcmVzc2lvbiBkYXRhc2V0IHRoYXQgY29udGFpbnMgc2FtcGxlcyB3aXRoIGtub3duIGNlbGwgdHlwZXMuCldlIGNhbiB0aGVuIHRyYWluIGEgbW9kZWwgYmFzZWQgb24gdGhpcyBkYXRhc2V0IGFuZCBsb29rIGF0IGVhY2ggb2YgdGhlIGNlbGxzIGluIG91ciBuZXcgZGF0YXNldCB0byBkZXRlcm1pbmUgd2hpY2ggKGlmIGFueSkgb2YgdGhlIGtub3duIGNlbGwgdHlwZXMgaGFzIHRoZSBtb3N0IHNpbWlsYXIgZXhwcmVzc2lvbiBwYXR0ZXJuLgpUaGUgZGV0YWlscyBvZiBob3cgc3VjaCBhIG1vZGVsIG1heSBiZSBjb25zdHJ1Y3RlZCBhbmQgdHJhaW5lZCB3aWxsIHZhcnkgYnkgdGhlIHNwZWNpZmljIG1ldGhvZCwgYnV0IHRoaXMgb3ZlcmFsbCBhcHByb2FjaCBpcyB3aWRlbHkgYXBwbGllZC4gCgpGb3IgdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIGZvY3VzIG9uIHRoZSBgU2luZ2xlUmAgcGFja2FnZSBhbmQgaXRzIG1ldGhvZHMsIHdoaWNoIGFyZSBkZXNjcmliZWQgaW4gZGV0YWlsIGluIFtfVGhlIFNpbmdsZVIgQm9va19dKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9ib29rcy8zLjE2L1NpbmdsZVJCb29rLykuCgojIyMgUmVmZXJlbmNlIGRhdGFzZXRzCgpTZWxlY3RpbmcgYSByZWZlcmVuY2UgZGF0YXNldCBpcyBvbmUgb2YgdGhlIG1vcmUgY3JpdGljYWwgc3RlcHMgZm9yIHRoaXMgZW50ZXJwcmlzZS4KQXQgdGhlIG1vc3QgYmFzaWMgbGV2ZWwsIGlmIHRoZSByZWZlcmVuY2UgZGF0YXNldCBkb2VzIG5vdCBpbmNsdWRlIHRoZSB0eXBlcyBvZiBjZWxscyB0aGF0IHdlIGV4cGVjdCB0byBzZWUgaW4gb3VyIHNhbXBsZSwgaXQgd29uJ3QgYmUgdXNlZnVsLgpTbyB3ZSB3aWxsIHdhbnQgYSByZWZlcmVuY2UgZGF0YXNldCB0aGF0IGhhcyBhcyBtYW55IGFzIHBvc3NpYmxlIG9mIHRoZSBjZWxsIHR5cGVzIHRoYXQgd2UgZXhwZWN0IHRvIGZpbmQgaW4gb3VyIGRhdGFzZXQsIGF0IGEgbGV2ZWwgb2YgZ3JhbnVsYXJpdHkgdGhhdCBhbGlnbnMgd2l0aCBvdXIgZ29hbHMuCgpGb3IgYFNpbmdsZVJgIHRoYXQgcmVmZXJlbmNlIGRhdGEgY2FuIGJlIGZyb20gYnVsayBSTkEgc2VxdWVuY2luZyBvciBmcm9tIG90aGVyIHNpbmdsZS1jZWxsIGV4cGVyaW1lbnRzLgpgU2luZ2xlUmAgaXMgYWxzbyBmYWlybHkgcm9idXN0IHRvIHRoZSBtZXRob2QgdXNlZCBmb3IgZ2VuZSBleHByZXNzaW9uIHF1YW50aWZpY2F0aW9uLCB3aGljaCBtZWFucyB0aGF0IHdlIGNhbiB1c2UgZWl0aGVyIFJOQS1zZXEgZGF0YXNldHMgb3IgbWljcm9hcnJheXMsIGlmIHRob3NlIGFyZSBtb3JlIHJlYWRpbHkgYXZhaWxhYmxlLiAKCk9uZSBjb252ZW5pZW50IHNvdXJjZSBvZiBjZWxsIHJlZmVyZW5jZSBkYXRhIGlzIHRoZSBgY2VsbGRleGAgcGFja2FnZSwgd2hpY2ggaXMgd2hhdCB3ZSB3aWxsIHVzZSBoZXJlLgpUaGlzIHBhY2thZ2UgaW5jbHVkZXMgZnVuY3Rpb25zIHRvIGRvd25sb2FkIGEgdmFyaWV0eSBvZiB3ZWxsLWFubm90YXRlZCByZWZlcmVuY2UgZGF0YXNldHMgaW4gYSBjb21tb24gZm9ybWF0LiAgCkZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHRoZSBkYXRhc2V0cyBhdmFpbGFibGUsIHlvdSB3aWxsIHdhbnQgdG8gcmVmZXIgdG8gW3RoZSBgY2VsbGRleGAgc3VtbWFyeSB2aWduZXR0ZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzLzMuMTYvZGF0YS9leHBlcmltZW50L3ZpZ25ldHRlcy9jZWxsZGV4L2luc3QvZG9jL3VzZXJndWlkZS5odG1sKS4KCldlIHdpbGwgc3RhcnQgYnkgdXNpbmcgYSByZWZlcmVuY2UgZGF0YXNldCBvZiBzb3J0ZWQgaW1tdW5lIGNlbGxzIGZyb20gW0dTRTEwNzAxMSAoTW9uYWNvIF9ldCBhbC5fIDIwMTkpXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0UxMDcwMTEpLgpUaGlzIHBhcnRpY3VsYXIgcmVmZXJlbmNlIHdhcyBjaG9zZW4gYmVjYXVzZSBpdCBpcyB3ZWxsLXN1aXRlZCB0byBQQk1DIGRhdGFzZXRzLCB3aXRoIGEgZ29vZCBsZXZlbCBvZiBncmFudWxhcml0eS4KClRoZSBgY2VsbGRleGAgZnVuY3Rpb25zIGFsc28gaGF2ZSBhIGNvbnZlbmllbnQgb3B0aW9uIHRvIGNvbnZlcnQgZ2VuZSBzeW1ib2xzIHRvIEVuc2VtYmwgaWRzLCB3aGljaCB3ZSB3aWxsIHVzZSBoZXJlIHNvIHRoYXQgb3VyIHJlZmVyZW5jZSBkYXRhIHVzZXMgdGhlIHNhbWUgZ2VuZSBpZGVudGlmaWVycyBhcyB0aGUgc2luZ2xlLWNlbGwgZGF0YS4KCmBgYHtyIGdldCBtb25hY299CiMgQmlvY29uZHVjdG9yICJIdWIiIHBhY2thZ2VzIHByb3ZpZGUgdGhlIG9wdGlvbiB0byBjYWNoZQojICAgZG93bmxvYWRzLCBidXQgdGhlIGludGVyYWN0aXZlIHByb21wdCBjYW4gYmUgYW5ub3lpbmcKIyAgIHdoZW4gd29ya2luZyB3aXRoIG5vdGVib29rcy4KIyBUaGVzZSBvcHRpb25zIGRpc2FibGUgdGhlIHByb21wdCBieSBnaXZpbmcgcGVybWlzc2lvbiAKIyAgIHRvIGNyZWF0ZSB0aGUgY2FjaGUgYXV0b21hdGljYWxseQpFeHBlcmltZW50SHViOjpzZXRFeHBlcmltZW50SHViT3B0aW9uKCJBU0siLCBGQUxTRSkKQW5ub3RhdGlvbkh1Yjo6c2V0QW5ub3RhdGlvbkh1Yk9wdGlvbigiQVNLIiwgRkFMU0UpCgojIEdldCBNb25hY28gMjAxOSBkYXRhIGZyb20gY2VsbGRleCB3aXRoIEVuc2VtYmwgaWRzLgptb25hY29fcmVmIDwtIGNlbGxkZXg6Ok1vbmFjb0ltbXVuZURhdGEoZW5zZW1ibCA9IFRSVUUpCmBgYAoKV2hhdCBpcyB0aGlzIGBtb25hY29fcmVmYCBvYmplY3Q/CgpgYGB7ciBleHBsb3JlIHJlZiwgbGl2ZSA9IFRSVUV9Cm1vbmFjb19yZWYKYGBgCgpBIGBTdW1tYXJpemVkRXhwZXJpbWVudGAgaXMgdmVyeSBzaW1pbGFyIHRvIGEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCwgZXhjZXB0IHJhdGhlciB0aGFuIGhhdmluZyBvbmUgY29sdW1uIHBlciBjZWxsLCBlYWNoIGNvbHVtbiBpcyBhICpzYW1wbGUqLgpPdGhlcndpc2UsIHRoZSBjb21wb25lbnRzIGFyZSB2ZXJ5IHNpbWlsYXI6IGVhY2ggcm93IGlzIHN0aWxsIGEgZ2VuZSwgZm9yIGV4YW1wbGUsIGFuZCBhZGRpdGlvbmFsIGRhdGEgYWJvdXQgdGhlIHNhbXBsZXMgYXJlIHN0b3JlZCBpbiB0aGUgYGNvbERhdGFgLgpJbiBmYWN0LCB0aGUgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QgaXMgZGVyaXZlZCBmcm9tIGEgYFN1bW1hcml6ZWRFeHBlcmltZW50YCwgd2l0aCBzb21lIGV4dHJhIHNsb3RzIHRoYXQgYXJlIG1vcmUgcmVsZXZhbnQgdG8gc2luZ2xlLWNlbGwgZGF0YS4KCldoYXQgaW5mb3JtYXRpb24gZG8gd2UgaGF2ZSBmb3IgdGhlIHNhbXBsZXM/CgpgYGB7ciBleHBsb3JlIHJlZmVyZW5jZSBzYW1wbGUgZGF0YSwgbGl2ZT1UUlVFfQpjb2xEYXRhKG1vbmFjb19yZWYpCmBgYAoKVGhlcmUgYXJlIHRocmVlIG1haW4gY29sdW1ucyBmb3IgdGhlIHNhbXBsZSBkYXRhOgoKLSBgbGFiZWwubWFpbmAgaXMgYSBtb3JlIGdlbmVyYWwgY2VsbCB0eXBlIGFzc2lnbm1lbnQuICAKCi0gYGxhYmVsLmZpbmVgIGlzIGEgZmluZS1sZXZlbCBjZWxsIHR5cGUgd2l0aCBtb3JlIHNwZWNpZmljIGxhYmVscy4KVGhlIGV4YWN0IGxldmVsIG9mIGdyYW51bGFyaXR5IG9mIHRoZXNlIGBtYWluYCBhbmQgYGZpbmVgIGRlc2lnbmF0aW9ucyAoYW5kIGluZGVlZCB0aGUgbGFiZWwgbmFtZXMgdGhlbXNlbHZlcykgd2lsbCB2YXJ5IGFtb25nIGRhdGFzZXRzLCBzbyBpdCBpcyBpbXBvcnRhbnQgdG8gbG9vayBhdCB0aGUgcmVmZXJlbmNlIHRvIHNlZSB3aGV0aGVyIGl0IGlzIHN1aXRhYmxlIGZvciB5b3VyIGFwcGxpY2F0aW9uLgoKLSBgbGFiZWwub250YCBpcyBhIHN0YW5kYXJkaXplZCBbQ2VsbCBPbnRvbG9neV0oaHR0cHM6Ly93d3cuZWJpLmFjLnVrL29scy9vbnRvbG9naWVzL2NsKSBpZGVudGlmaWVyLiAKVXNpbmcgdGhlIGNlbGwgb250b2xvZ3kgY2FuIGFsbG93IGZvciBtb3JlIGNvbXBsZXggcmVwcmVzZW50YXRpb25zIG9mIHRoZSByZWxhdGlvbnNoaXBzIGFtb25nIGRpZmZlcmVudCBjZWxsIHR5cGVzLCBidXQgaW52ZXN0aWdhdGluZyB0aGF0IGlzIGJleW9uZCB0aGUgc2NvcGUgb2YgdGhpcyB3b3Jrc2hvcC4gCgpBbm90aGVyIGNvbXBvbmVudCB3ZSB3b3VsZCBsaWtlIHRvIGV4cGxvcmUgaXMgaG93IG1hbnkgb2YgZWFjaCBvZiB0aGVzZSBjZWxsIHR5cGVzIHdlIGhhdmUgaW4gdGhlIHJlZmVyZW5jZSBkYXRhc2V0LiAKQSBiaXQgb2YgcXVpY2sgYGRwbHlyYCB3cmFuZ2xpbmcgY2FuIGdpdmUgdXMgdGhlIGFuc3dlci4KCmBgYHtyIGNvdW50IGNlbGwgdHlwZXN9CmNvbERhdGEobW9uYWNvX3JlZikgfD4gCiAgYXMuZGF0YS5mcmFtZSgpIHw+CiAgZHBseXI6OmNvdW50KGxhYmVsLm1haW4sIGxhYmVsLmZpbmUpCmBgYAoKVGhpcyBpcyBwcmV0dHkgZ29vZCEgCk1vc3QgY2VsbCB0eXBlcyBoYXZlIDQgcmVwbGljYXRlcywgd2hpY2ggaXMgbW9yZSByZXBsaWNhdGVzIHRoYW4gd2Ugb2Z0ZW4gZmluZC4KCiMjIyBXaGF0IGRvZXMgYFNpbmdsZVJgIGRvPwoKQXMgbWVudGlvbmVkIGVhcmxpZXIsIGBTaW5nbGVSYCBidWlsZHMgYSBtb2RlbCBmcm9tIGEgc2V0IG9mIHRyYWluaW5nIGRhdGEsIGFuZCB0aGVuIHVzZXMgdGhhdCBtb2RlbCB0byBjbGFzc2lmeSBjZWxscyAob3IgZ3JvdXBzIG9mIGNlbGxzKSBpbiBuZXcgZGF0YXNldHMuCgpgU2luZ2xlUmAgd29ya3MgYnkgZmlyc3QgaWRlbnRpZnlpbmcgYSBzZXQgb2YgbWFya2VyIGdlbmVzIHRoYXQgY2FuIGJlIHVzZWQgdG8gZGlmZmVyZW50aWF0ZSBhbW9uZyB0aGUgY2VsbCB0eXBlcyBpbiB0aGUgcmVmZXJlbmNlIGRhdGFzZXQuIApJdCBkb2VzIHRoaXMgYnkgcGVyZm9ybWluZyBwYWlyd2lzZSBjb21wYXJpc29ucyBhbW9uZyBhbGwgb2YgdGhlIGNlbGwgdHlwZXMsIGFuZCByZXRhaW5pbmcgdGhlIHRvcCBzZXQgb2YgZ2VuZXMgZGlmZmVyZW50aWF0aW5nIGVhY2ggcGFpci4KVGhlIGlkZWEgaXMgdGhhdCB0aGlzIHNldCBvZiBnZW5lcyB3aWxsIGJlIHRoZSBtb3N0IGluZm9ybWF0aXZlIGZvciBkaWZmZXJlbnRpYXRpbmcgY2VsbCB0eXBlcy4KClRoZW4sIGZvciBlYWNoIGNlbGwsIGBTaW5nbGVSYCBjYWxjdWxhdGVzIHRoZSBTcGVhcm1hbiBjb3JyZWxhdGlvbiBiZXR3ZWVuIGV4cHJlc3Npb24gb2YgdGhhdCBjZWxsIGFuZCBlYWNoIGNlbGwgdHlwZSAodXNpbmcgdGhlIG9ubHkgdGhlIGdlbmVzIGNob3NlbiBlYXJsaWVyKS4KTm90YWJseSwgdGhpcyBpcyBhIG5vbi1wYXJhbWV0cmljIGNvcnJlbGF0aW9uLCBzbyB0aGUgc2NhbGluZyBhbmQgbm9ybWFsaXphdGlvbiB0aGF0IHdlIGFwcGx5IChvciBkb24ndCkgc2hvdWxkIG5vdCBtYXR0ZXIhCk5vdGUgdGhhdCBpZiB5b3UgdXNlZCBhIHNpbmdsZS1jZWxsIHRlY2hub2xvZ3kgdGhhdCBwcm9kdWNlcyBmdWxsLWxlbmd0aCB0cmFuc2NyaXB0cyAoaS5lLiwgU01BUlQtc2VxKSwgeW91IHdpbGwgcHJvYmFibHkgd2FudCB0byBjb252ZXJ0IHlvdXIgY291bnRzIHRvIFRyYW5zY3JpcHRzIHBlciBNaWxsaW9uIChUUE0pLCB0byBhbGxvdyBtb3JlIGNvbnNpc3RlbnQgcmFua2luZyBhbW9uZyB0cmFuc2NyaXB0cyBvZiBkaWZmZXJlbnQgbGVuZ3Rocy4gCgpUaGUgcmVmZXJlbmNlIGNlbGwgdHlwZSB3aXRoIHRoZSBoaWdoZXN0IGNvcnJlbGF0aW9uIGlzIHRoZW4gY2hvc2VuIGFzIHRoZSBjZWxsIHR5cGUgYXNzaWdubWVudCBmb3IgdGhhdCBjZWxsLgpJZiB0aGVyZSBhcmUgbXVsdGlwbGUgY2VsbCB0eXBlcyB3aXRoIGhpZ2ggc2NvcmVzLCBhbiBvcHRpb25hbCBmaW5lLXR1bmluZyBzdGVwIHJlcGVhdHMgdGhlIHByb2Nlc3MgdXNpbmcgb25seSB0aGUgbW9zdCByZWxldmFudCBnZW5lcyBmb3IgdGhvc2UgY2VsbCB0eXBlcy4KCgojIyMgUnVubmluZyBgU2luZ2xlUmAKCkZvciBvdXIgZmlyc3QgcnVuLCB3ZSB3aWxsIGRvIHRoZSBtYXJrZXIgZ2VuZSBzZWxlY3Rpb24gKHRyYWluaW5nKSBhbmQgY2xhc3NpZmljYXRpb24gaW4gYSBzaW5nbGUgc3RlcCwgdXNpbmcgdGhlIGNvbnZlbmllbmNlIGZ1bmN0aW9uIGBTaW5nbGVSOjpTaW5nbGVSKClgLgpGb3IgdGhpcyB3ZSBuZWVkIG9ubHkgc3VwcGx5IHRocmVlIG1haW4gYXJndW1lbnRzOiBPdXIgU0NFIG9iamVjdCwgYSByZWZlcmVuY2UgbWF0cml4IChoZXJlIGluIGBTdW1tYXJpemVkRXhwZXJpbWVudGAgZm9ybWF0KSwgYW5kIHRoZSBsYWJlbHMgZm9yIGVhY2ggb2YgdGhlIHNhbXBsZXMgaW4gdGhlIHJlZmVyZW5jZSB0aGF0IHdlIHdhbnQgdG8gdXNlLgpXZSBhbHNvIG5lZWQgdG8gYmUgc3VyZSB0aGF0IG91ciBzYW1wbGUgYW5kIHRoZSByZWZlcmVuY2UgZGF0YSB1c2UgdGhlIHNhbWUgZ2VuZSBJRHMsIHdoaWNoIGlzIHdoeSB3ZSByZXF1ZXN0ZWQgdGhlIEVuc2VtYmwgSURzIHdoZW4gZ2V0dGluZyB0aGUgcmVmZXJlbmNlIGRhdGFzZXQuCgpCZWNhdXNlIHRoaXMgZnVuY3Rpb24gaXMgZG9pbmcgbWFueSByZXBldGl0aXZlIGNhbGN1bGF0aW9ucyAobG90cyBvZiBjb3JyZWxhdGlvbnMhKSwgd2UgY2FuIHNwZWVkIGl0IHVwIGJ5IGluY2x1ZGluZyB0aGUgYEJQUEFSQU1gIGFyZ3VtZW50LgpUaGlzIGlzIGEgY29tbW9uIGFyZ3VtZW50IGluIGBCaW9jb25kdWN0b3JgIHBhY2thZ2VzIHdoZXJlIGBCUGAgc3RhbmRzIGZvciB0aGUgYEJpb2NQYXJhbGxlbGAgcGFja2FnZSwgd2hpY2ggcHJvdmlkZXMgbXVsdGlwcm9jZXNzaW5nIGNhcGFiaWxpdGllcyB0byBtYW55IEJpb2NvbmR1Y3RvciBmdW5jdGlvbnMuIApJbiB0aGlzIGNhc2UsIHdlIHdpbGwgdXNlIHRoZSBhcmd1bWVudCBgQmlvY1BhcmFsbGVsOjpNdWx0aWNvcmVQYXJhbSg0KWAgdG8gc3BlY2lmeSB3ZSB3YW50IHRvIHVzZSBsb2NhbCBtdWx0aWNvcmUgcHJvY2Vzc2luZyB3aXRoIDQgIndvcmtlcnMiLgoKYGBge3Igc2ltcGxlIFNpbmdsZVIsIGxpdmU9VFJVRX0KIyBjYWxjdWxhdGUgU2luZ2xlUiByZXN1bHRzIGluIG9uZSBzdGVwCnNpbmdsZXJfcmVzdWx0IDwtIFNpbmdsZVI6OlNpbmdsZVIoCiAgc2NlLCAjIG91ciBxdWVyeSBTQ0UKICByZWYgPSBtb25hY29fcmVmLCAjIHJlZmVyZW5jZSBkYXRhc2V0CiAgbGFiZWxzID0gbW9uYWNvX3JlZiRsYWJlbC5tYWluLCAjIHJlZmVyZW5jZSBsYWJlbHMgdG8gdXNlCiAgQlBQQVJBTSA9IEJpb2NQYXJhbGxlbDo6TXVsdGljb3JlUGFyYW0oNCkgIyBtdWx0aXByb2Nlc3NpbmcKKQpgYGAKCmBTaW5nbGVSYCBwcm92aWRlcyBhIGZldyBuaWNlIHZpc3VhbGl6YXRpb25zIGZvciBldmFsdWF0aW5nIHRoZSBzdGF0aXN0aWNzIGl0IGNhbGN1bGF0ZWQgYW5kIHRoZSBhc3NpZ25tZW50cyBpdCBtYWtlcy4KT25lIGlzIGEgaGVhdG1hcCBvZiB0aGUgc2NvcmVzIGZvciBlYWNoIGNlbGwsIGFycmFuZ2VkIGJ5IHRoZSBjZWxsIHR5cGUgdGhhdCB3YXMgYXNzaWduZWQgdG8gZWFjaC4KVGhpcyBpcyBjcmVhdGVkIHdpdGggdGhlIGBTaW5nbGVSOjpwbG90U2NvcmVIZWF0bWFwKClgIGZ1bmN0aW9uLgoKYGBge3IgcGxvdCBTaW5nbGVSIGhlYXRtYXAsIGxpdmUgPSBUUlVFfQpTaW5nbGVSOjpwbG90U2NvcmVIZWF0bWFwKHNpbmdsZXJfcmVzdWx0KQpgYGAKV2UgY2FuIGFsc28gcHVsbCBvdXQgaW5kaXZpZHVhbCBjb21wb25lbnRzIG9mIHRoZSByZXN1bHRzIG9iamVjdCBmb3IgcGxvdHRpbmcgaW4gdGhlIGNvbnRleHQgb2Ygb3VyIGlucHV0IFNDRSBvYmplY3QuCkhlcmUgd2Ugd2lsbCBzYXZlIHRoZSBwcnVuZWQgbGFiZWxzICh3aGVyZSBsb3ctcXVhbGl0eSBhc3NpZ25tZW50cyBoYXZlIGJlZW4gZ2l2ZW4gYW4gYE5BYCBsYWJlbCksIHN0b3JpbmcgdGhlbSBiYWNrIGluIG91ciBTQ0Ugb2JqZWN0IChzcGVjaWZpY2FsbHkgdG8gYSBuZXcgY29sdW1uIG9mIHRoZSBgY29sRGF0YWAgdGFibGUpLgoKYGBge3Igc2F2ZSBjZWxsdHlwZXMsIGxpdmU9VFJVRX0Kc2NlJGNlbGx0eXBlX21haW4gPC0gc2luZ2xlcl9yZXN1bHQkcHJ1bmVkLmxhYmVscwpgYGAKCk5vdyB3ZSBjYW4gcGxvdCB0aGUgY2VsbCB0eXBlIGFzc2lnbm1lbnRzIG9udG8gb3VyIFVNQVAgdG8gc2VlIGhvdyB0aGV5IGNvbXBhcmUgdG8gdGhlIHBhdHRlcm5zIHdlIHNhdyB0aGVyZSBiZWZvcmUuCgpgYGB7ciBwbG90IGNlbGx0eXBlIHVtYXAsIGxpdmU9VFJVRX0Kc2NhdGVyOjpwbG90VU1BUChzY2UsIGNvbG9yX2J5ID0gImNlbGx0eXBlX21haW4iKSAKYGBgCkFubm95aW5nbHksIHRoZSBgTkFgIGFuZCBgVCBjZWxsc2AgbGFiZWxzIGFyZSBxdWl0ZSBjbG9zZSBpbiBjb2xvciwgYW5kIHRoZSBgc2NhdGVyYCBhbmQgYFNpbmdsZVJgIHBhY2thZ2VzIGRvbid0IGFncmVlIG9uIGNvbG9yIGNob2ljZXMuCkx1Y2tpbHksIHNpbmNlIGBwbG90VU1BUCgpYCByZXR1cm5zIGEgYGdncGxvdGAgb2JqZWN0LCB3ZSBjYW4gbW9kaWZ5IHRoZSBjb2xvciBwYWxldHRlIHVzaW5nIGBnZ3Bsb3QyYCBmdW5jdGlvbnMuClN0aWxsIGFubm95aW5nbHksIGhvd2V2ZXIsIHdoZW4gd2UgY2hhbmdlIHRoZSBwYWxldHRlLCB0aGUgbGVnZW5kIHRpdGxlIGRlZmF1bHRzIHRvIHRoZSB1bmluZm9ybWF0aXZlIG5hbWUgYCJjb2xvdXJfYnkiYCwgc28gd2UnbGwgYWxzbyBzcGVjaWZ5IGEgbWF0Y2hpbmcgbGVnZW5kIHRpdGxlIHdpdGggb3VyIG5ldyBjb2xvciBwYWxldHRlLgoKYGBge3IgcGxvdCBjZWxsdHlwZSB1bWFwIHBhbGV0dGV9CnNjYXRlcjo6cGxvdFVNQVAoc2NlLCBjb2xvcl9ieSA9ICJjZWxsdHlwZV9tYWluIikgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihuYW1lID0gIkNlbGwgdHlwZSIsICMgbGVnZW5kIHRpdGxlCiAgICAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAiRGFyazIiLCAgICAgICMgY29sb3IgcGFsZXR0ZQogICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9ICJncmF5ODAiKSAgICAjIHVzZSBsaWdodCBncmF5IGZvciBOQSB2YWx1ZXMKYGBgCgpXZSBzZWVtIHRvIGhhdmUgYSBwcmV0dHkgZ29vZCBzZXQgb2YgY2VsbCB0eXBlIGFzc2lnbm1lbnRzLCB3aXRoIG1vc3QgZmFsbGluZyBpbnRvIGdyb3VwaW5ncyBjb25zaXN0ZW50IHdpdGggd2hhdCB3ZSBzZWUgaW4gdGhlIFVNQVAgcGxvdC4KCldlIGNhbiB0aGFuayB0aGUgZmFjdCB0aGF0IHRoaXMgaXMgYSBQQk1DIHNhbXBsZSBhbmQgdGhhdCB3ZSBoYXZlIGEgZ29vZCByZWZlcmVuY2UgZGF0YXNldCBmb3IgdGhlc2UgY2VsbCB0eXBlcyBmb3IgdGhlIGNsZWFubGluZXNzIG9mIHRoaXMgcGxvdC4KUXVpdGUgb2Z0ZW4gd2l0aCBvdGhlciBraW5kcyBvZiBzYW1wbGVzIChlc3BlY2lhbGx5IGNhbmNlciBjZWxscyEpIHRoaW5ncyB3aWxsIGJlIG11Y2ggbGVzcyBjbGVhbiEKCldlIGNhbiBhbHNvIGxvb2sgdG8gc2VlIGhvdyB0aGUgY2VsbCB0eXBlIGFzc2lnbm1lbnRzIGFyZSBkaXN0cmlidXRlZCB1c2luZyB0aGUgYmFzZSBSIGZ1bmN0aW9uIGB0YWJsZSgpYC4KU2luY2Ugd2UgbGlrZSB0byBrZWVwIHRyYWNrIG9mIHRoZSBjZWxscyB0aGF0IGVuZGVkIHVwIGFzIGBOQWAgaW4gdGhlIHBydW5lZCBsYWJlbHMsIHdlIHdpbGwgaW5jbHVkZSB0aGUgYHVzZU5BID0gImlmYW55ImAgYXJndW1lbnQuCgpgYGB7ciBjZWxsIHR5cGUgdGFibGV9CnRhYmxlKHNpbmdsZXJfcmVzdWx0JHBydW5lZC5sYWJlbHMsIHVzZU5BID0gImlmYW55IikKYGBgCgojIyMgRXhwbG9yaW5nIGZpbmVyIGxhYmVscwoKSW4gdGhlIHByZXZpb3VzIGNlbGwgdHlwaW5nLCB3ZSB1c2VkIHRoZSBgbGFiZWwubWFpbmAgY29sdW1uLCBidXQgd2UgYWxzbyBoYWQgYGxhYmVsLmZpbmVgLCBzbyBsZXQncyB1c2UgdGhhdCB0byBleHBsb3JlIHRoZSBkYXRhc2V0IGluIGEgYml0IG1vcmUgZGV0YWlsLiAKCldlIHdpbGwgYWxzbyB0YWtlIHRoaXMgdGltZSB0byBkaXZlIGEgYml0IGRlZXBlciBpbnRvIHRoZSBzdGVwcyB0aGF0IGBTaW5nbGVSYCBwZXJmb3JtZWQuIApBcyBtZW50aW9uZWQsIHRoZSBmaXJzdCBzdGVwIGlzIHRyYWluaW5nIHRoZSBtb2RlbCwgZHVyaW5nIHdoaWNoIHdlIGlkZW50aWZ5IHRoZSBnZW5lcyB0aGF0IHdpbGwgYmUgdXNlZCBmb3IgdGhlIGNvcnJlbGF0aW9uIGFuYWx5c2lzIGxhdGVyLgpXaGlsZSB0aGlzIHN0ZXAgaXMgbm90IHBhcnRpY3VsYXJseSBzbG93LCBpZiB3ZSB3ZXJlIGNsYXNzaWZ5aW5nIG11bHRpcGxlIHNhbXBsZXMsIHdlIHdvdWxkIG5vdCB3YW50IHRvIGhhdmUgdG8gcmVwZWF0IGl0IGZvciBldmVyeSBzYW1wbGUuCgpUbyBkbyB0aGUgdHJhaW5pbmcsIHdlIHdpbGwgdXNlIHRoZSBgdHJhaW5TaW5nbGVSKClgIGZ1bmN0aW9uLiAKRm9yIHRoaXMgd2Ugd2lsbCBzdGFydCB3aXRoIG91ciByZWZlcmVuY2UgYW5kIHRoZSBsYWJlbHMgd2Ugd2FudCB0byB0cmFpbiB0aGUgbW9kZWwgd2l0aC4KCldlIGNhbiB0aGVuIHNwZWNpZnkgdGhlIG1ldGhvZCB1c2VkIHRvIHNlbGVjdCB0aGUgZ2VuZXMgdGhhdCB3aWxsIGJlIHVzZWQgZm9yIGNsYXNzaWZpY2F0aW9uLgpUaGUgZGVmYXVsdCBtZXRob2QgaXMgYCJkZSJgLCB3aGljaCBwZXJmb3JtcyBhIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIGZvciBlYWNoIHBhaXIgb2YgbGFiZWxzLCBidXQgd2UgY291bGQgYWxzbyB1c2UgYCJzZCJgIHRvIHNlbGVjdCB0aGUgZ2VuZXMgd2hpY2ggYXJlIG1vc3QgdmFyaWFibGUgYWNyb3NzIGxhYmVscywgb3IgYCJhbGwiYCB0byB1c2UgYWxsIGdlbmVzLgpJZiB3ZSB3YW50IHRvIGdldCByZWFsbHkgZmFuY3ksIHdlIGNvdWxkIGV2ZW4gcHJvdmlkZSBhIHNwZWNpZmljIGxpc3Qgb2YgZ2VuZXMgdG8gdXNlLgoKV2Ugc2hvdWxkIG5vdGUgaGVyZSB0aGF0IHRoZSByZWZlcmVuY2UgZGF0YXNldCBmb3IgYFNpbmdsZVJgIGRvZXMgbm90IG5lZWQgdG8gYmUgZnJvbSBhIGNvbXBlbmRpdW0gbGlrZSBgY2VsbGRleGAhCklmIHlvdSBoYXZlIGFueSB3ZWxsLWNsYXNzaWZpZWQgZGF0YXNldCB0aGF0IHlvdSB3YW50IHRvIHVzZSBhcyBhIHJlZmVyZW5jZSwgeW91IGNhbiwgYXMgbG9uZyBhcyB5b3UgY2FuIGNyZWF0ZSBhIGdlbmUgYnkgc2FtcGxlIGV4cHJlc3Npb24gbWF0cml4IGFuZCBhIHZlY3RvciBvZiBjZWxsIHR5cGVzIGZvciBlYWNoIHNhbXBsZS4gCllvdSB3aWxsIHdhbnQgdG8gZW5zdXJlIHRoYXQgdGhlIGNlbGwgdHlwZXMgeW91IGV4cGVjdCB0byBzZWUgaW4geW91ciBzYW1wbGUgYXJlIHByZXNlbnQgaW4gdGhlIHJlZmVyZW5jZSBkYXRhc2V0LCBhbmQgZGF0YSBzaG91bGQgYmUgbm9ybWFsaXplZCwgYnV0IG90aGVyd2lzZSB0aGUgbWV0aG9kIGNhbiBiZSBxdWl0ZSBmbGV4aWJsZS4gCllvdSBjYW4gZXZlbiB1c2UgYSBwcmV2aW91c2x5LWFubm90YXRlZCBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIGFzIGEgcmVmZXJlbmNlIGZvciBhIG5ldyBkYXRhc2V0LgpGb3IgbW9yZSBkZXRhaWxzIGFib3V0IGN1c3RvbSByZWZlcmVuY2VzLCBzZWUgdGhlIFtPU0NBIGNoYXB0ZXIgb24gY2VsbCB0eXBlIGFubm90YXRpb25dKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL2Jvb2tzLzMuMTYvT1NDQS5iYXNpYy9jZWxsLXR5cGUtYW5ub3RhdGlvbi5odG1sI3VzaW5nLWN1c3RvbS1yZWZlcmVuY2VzKQoKV2UgZG8gd2FudCB0byBiZSBzdXJlIHRoYXQgdGhlIGdlbmVzIHNlbGVjdGVkIGZvciB0aGUgbW9kZWwgd2lsbCBiZSBhbW9uZyB0aG9zZSBwcmVzZW50IGluIG91ciBTQ0Ugb2JqZWN0LCBzbyB3ZSB3aWxsIHVzZSB0aGUgYHJlc3RyaWN0YCBhcmd1bWVudCB3aXRoIGEgdmVjdG9yIG9mIHRoZSBnZW5lcyBpbiBvdXIgU0NFLgpUaGlzIHN0ZXAgd291bGQgaGFwcGVuIGF1dG9tYXRpY2FsbHkgd2l0aCB0aGUgYFNpbmdsZVI6OlNpbmdsZVIoKWAgZnVuY3Rpb24sIGJ1dCB3ZSBuZWVkIHRvIGFkZCBpdCBtYW51YWxseSBmb3IgdGhpcyB1c2UgY2FzZS4KCgpgYGB7ciB0cmFpbiBmaW5lbW9kZWwsIGxpdmU9VFJVRX0KIyBidWlsZCBmaW5lIG1vZGVsCnNpbmdsZXJfZmluZW1vZGVsIDwtIFNpbmdsZVI6OnRyYWluU2luZ2xlUigKICBtb25hY29fcmVmLCAjIHJlZmVyZW5jZSBkYXRhc2V0CiAgbGFiZWxzID0gbW9uYWNvX3JlZiRsYWJlbC5maW5lLCAjIGxhYmVscyBmb3IgdHJhaW5pbmcgZGF0YXNldAogICMgdXNlIERFIHRvIHNlbGVjdCBnZW5lcyAoZGVmYXVsdCkKICBnZW5lcyA9ICJkZSIsIAogICMgb25seSB1c2UgZ2VuZXMgaW4gdGhlIHNjZSBvYmplY3QKICByZXN0cmljdCA9IHJvd25hbWVzKHNjZSksCiAgIyBwYXJhbGxlbCBwcm9jZXNzaW5nCiAgQlBQQVJBTSA9IEJpb2NQYXJhbGxlbDo6TXVsdGljb3JlUGFyYW0oNCkKKQpgYGAKCk5vdyB3ZSBjYW4gcGVyZm9ybSB0aGUgY2xhc3NpZmljYXRpb24gc3RlcCwgdXNpbmcgb3VyIFNDRSBvYmplY3QgYW5kIHRoZSBgU2luZ2xlUmAgbW9kZWwgdGhhdCB3ZSBqdXN0IGNyZWF0ZWQuCgpgYGB7ciBjbGFzc2lmeSBmaW5lLCBsaXZlPVRSVUV9CiMgY2xhc3NpZnkgd2l0aCBmaW5lIG1vZGVsCnNpbmdsZXJfcmVzdWx0X2ZpbmUgPC0gU2luZ2xlUjo6Y2xhc3NpZnlTaW5nbGVSKAogIHNjZSwgIyBvdXIgU0NFIG9iamVjdAogIHNpbmdsZXJfZmluZW1vZGVsLCAjIHRoZSB0cmFpbmVkIG1vZGVsIG9iamVjdAogICMgcGVyZm9ybSBmaW5lIHR1bmluZyAoZGVmYXVsdCkKICBmaW5lLnR1bmUgPSBUUlVFLAogICMgcGFyYWxsZWwgcHJvY2Vzc2luZwogIEJQUEFSQU0gPSBCaW9jUGFyYWxsZWw6Ok11bHRpY29yZVBhcmFtKDQpCikKYGBgCgoKV2hhdCBsYWJlbHMgd2VyZSBhc3NpZ25lZCwgYW5kIGhvdyBtYW55IG9mIGVhY2g/CgpgYGB7ciB0YWJsZSBmaW5lIGxhYmVsc30KdGFibGUoc2luZ2xlcl9yZXN1bHRfZmluZSRwcnVuZWQubGFiZWxzLCB1c2VOQSA9ICJpZmFueSIpCmBgYAoKYGBge3IgcGxvdCB1bWFwIGZpbmUsIGxpdmU9VFJVRX0KIyBhZGQgZmluZSBsYWJlbHMgdG8gU0NFCnNjZSRjZWxsdHlwZV9maW5lIDwtIHNpbmdsZXJfcmVzdWx0X2ZpbmUkcHJ1bmVkLmxhYmVscwojIHBsb3QgVU1BUCB3aXRoIGZpbmUgbGFiZWxzCnNjYXRlcjo6cGxvdFVNQVAoc2NlLCBjb2xvcl9ieSA9ICJjZWxsdHlwZV9maW5lIikKYGBgCgpUaGF0J3MgYSBwcmV0dHkgbWVzc3kgcGxvdC4KTW9zdGx5IHRoYXQgaXMgYmVjYXVzZSB0aGVyZSBhcmUgX2xvdHNfIG9mIGNlbGwgdHlwZXMgaGVyZSwgYW5kIG5vdCBlbm91Z2ggY29sb3JzIHRvIHJlcHJlc2VudCB0aGVtIGFsbC4KVGhlIGBOQWAgY2VsbHMgYWxzbyBnb3QgdGFrZW4gb2ZmIGNvbXBsZXRlbHksIHdoaWNoIGlzIG5vdCBpZGVhbC4KCk9uZSB0aGluZyB3ZSBjYW4gZG8gaXMgdG8gdXNlIHNvbWUgZnVuY3Rpb25zIGZyb20gdGhlIGB0aWR5dmVyc2VgIHBhY2thZ2UgW2Bmb3JjYXRzYF0oaHR0cHM6Ly9mb3JjYXRzLnRpZHl2ZXJzZS5vcmcpLCB3aGljaCBjYW4gYmUgdmVyeSBoYW5keSBmb3IgZGVhbGluZyB3aXRoIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBsaWtlIHRoZXNlIGNlbGwgdHlwZXMuCgpXZSB3aWxsIHVzZSB0d28gb2YgdGhlc2UgZnVuY3Rpb25zIGluIHRoZSBjaHVuayBiZWxvdzogCkZpcnN0IHdlIHdpbGwgdXNlIGBmY3RfY29sbGFwc2VgIHRvIHRha2Ugc29tZSBvZiB0aGUgZmluZXIgbGFiZWxzIHRoYXQgd2UgbWlnaHQgbm90IGJlIGFzIGludGVyZXN0ZWQgaW4gYW5kIGNvbGxhcHNlIHRoZW0gaW50byBsb2dpY2FsIGdyb3VwaW5ncyAoaW4gdGhpcyBjYXNlLCB0aGUgYG1haW5gIGxhYmVsIHRoYXQgdGhleSB3ZXJlIHBhcnQgb2YpLgpBZnRlciB0aGF0LCB3ZSB3aWxsIHVzZSBgZmN0X3JlbGV2ZWxgIHRvIHB1dCB0aGUgcmVtYWluaW5nIGZhY3RvciBsZXZlbHMgaW4gdGhlIG9yZGVyIHdlIHdvdWxkIGxpa2UgdGhlbSB0byBhcHBlYXIgZm9yIHBsb3R0aW5nLgoKYGBge3IgY29sbGFwc2UgbGFiZWxzfQpjb2xsYXBzZWRfbGFiZWxzIDwtIHNpbmdsZXJfcmVzdWx0X2ZpbmUkcHJ1bmVkLmxhYmVscyB8PgogIGZvcmNhdHM6OmZjdF9jb2xsYXBzZSgKICAgICJNb25vY3l0ZXMiID0gYygKICAgICAgICAiQ2xhc3NpY2FsIG1vbm9jeXRlcyIsIAogICAgICAgICJJbnRlcm1lZGlhdGUgbW9ub2N5dGVzIiwgCQogICAgICAgICJOb24gY2xhc3NpY2FsIG1vbm9jeXRlcyIpLAogICAgIkRlbmRyaXRpYyBjZWxscyIgPSBjKAogICAgICAgICJNeWVsb2lkIGRlbmRyaXRpYyBjZWxscyIsCiAgICAgICAgIlBsYXNtYWN5dG9pZCBkZW5kcml0aWMgY2VsbHMiKSwKICAgICJUIGNlbGxzIiA9IGMoCiAgICAgICAgIk1BSVQgY2VsbHMiLAogICAgICAgICJOb24tVmQyIGdkIFQgY2VsbHMiLAogICAgICAgICJWZDIgZ2QgVCBjZWxscyIpLAogICAgIkhlbHBlciBUIGNlbGxzIiA9IGMoCiAgICAgICAgIlRoMSBjZWxscyIsCiAgICAgICAgIlRoMS9UaDE3IGNlbGxzIiwgCiAgICAgICAgIlRoMTcgY2VsbHMiLCAKICAgICAgICAiVGgyIGNlbGxzIiwKICAgICAgICAiRm9sbGljdWxhciBoZWxwZXIgVCBjZWxscyIpLAogICAgIkIgY2VsbHMiID0gYygKICAgICAgICAiTmFpdmUgQiBjZWxscyIsCiAgICAgICAgIlN3aXRjaGVkIG1lbW9yeSBCIGNlbGxzIiwKICAgICAgICAiTm9uLXN3aXRjaGVkIG1lbW9yeSBCIGNlbGxzIiwKICAgICAgICAiRXhoYXVzdGVkIEIgY2VsbHMiLAogICAgICAgICJQbGFzbWFibGFzdHMiCQkKICAgICkKICApIHw+CiAgIyBvcmRlciBmb3IgcGxvdHRpbmcKICBmb3JjYXRzOjpmY3RfcmVsZXZlbCgKICAgICJIZWxwZXIgVCBjZWxscyIsCiAgICAiVCByZWd1bGF0b3J5IGNlbGxzIiwKICAgICJOYWl2ZSBDRDQgVCBjZWxscyIsCiAgICAiVGVybWluYWwgZWZmZWN0b3IgQ0Q0IFQgY2VsbHMiLAogICAgIk5haXZlIENEOCBUIGNlbGxzIiwKICAgICJDZW50cmFsIG1lbW9yeSBDRDggVCBjZWxscyIsCiAgICAiRWZmZWN0b3IgbWVtb3J5IENEOCBUIGNlbGxzIiwKICAgICJUZXJtaW5hbCBlZmZlY3RvciBDRDggVCBjZWxscyIsCiAgICAiVCBjZWxscyIsCiAgICAiTmF0dXJhbCBraWxsZXIgY2VsbHMiLAogICAgIkIgY2VsbHMiLAogICAgIk1vbm9jeXRlcyIsCiAgICAiRGVuZHJpdGljIGNlbGxzIiwKICAgICJQcm9nZW5pdG9yIGNlbGxzIiwKICAgICJMb3ctZGVuc2l0eSBiYXNvcGhpbHMiCiAgKQpgYGAKCk5vdyB0aGF0IHdlIGhhdmUgdGhhdCBzZXQgdXAsIHdlIGNhbiBwbG90IHVzaW5nIG91ciBjb2xsYXBzZWQgYW5kIG9yZGVyZWQgY2VsbCB0eXBlIGxhYmVscy4KCmBgYHtyIHBsb3QgY29sbGFwc2VkLCBsaXZlPVRSVUV9CnNjZSRjZWxsdHlwZV9jb2xsYXBzZWQgPC0gY29sbGFwc2VkX2xhYmVscwpzY2F0ZXI6OnBsb3RVTUFQKHNjZSwgCiAgICAgICAgICAgICAgICAgY29sb3JfYnkgPSAiY2VsbHR5cGVfY29sbGFwc2VkIikKYGBgCgoKIyMjIEhlYXRtYXAgb2YgY2VsbCB0eXBlcyAmIGNsdXN0ZXJzCgpMZXQncyBsb29rIGF0IGhvdyB0aGUgY2VsbCB0eXBlIGFzc2lnbm1lbnRzIHdlIG9idGFpbmVkIHVzaW5nIGBTaW5nbGVSYCBjb21wYXJlIHRvIHRoZSBjbHVzdGVycyB0aGF0IHdlIGZvdW5kIHVzaW5nIHRoZSB1bnN1cGVydmlzZWQgY2x1c3RlcmluZyBhdCB0aGUgc3RhcnQgb2YgdGhpcyBub3RlYm9vay4KClRvIGRvIHRoaXMsIHdlIHdpbGwgYWdhaW4gdXNlIHRoZSBgdGFibGUoKWAgZnVuY3Rpb24sIGJ1dCBub3cgd2l0aCB0d28gdmVjdG9ycyBhcyBpbnB1dCwgdG8gYnVpbGQgYSBjb250aW5nZW5jeSB0YWJsZSBvZiB0aGUgY2VsbCB0eXBlcyBhbmQgY2x1c3RlcnMgdGhhdCBlYWNoIGNlbGwgd2FzIGNsYXNzaWZpZWQgd2l0aC4gCgpgYGB7ciB0eXBlIGNsdXN0ZXIgdGFibGV9CiMgY3JlYXRlIGEgdGFibGUgb2YgY2x1c3RlcnMgJiBjZWxsIHR5cGUgY291bnRzCnR5cGVfY2x1c3Rlcl90YWIgPC0gdGFibGUoc2NlJGNlbGx0eXBlX2ZpbmUsIHNjZSRubl9jbHVzdGVyLCB1c2VOQSA9ICJpZmFueSIpCgojIGxvb2sgYXQgdGhlIHRvcCBjb3JuZXIgb2YgdGhlIHJlc3VsdHMKdHlwZV9jbHVzdGVyX3RhYlsxOjUsIDE6NV0KYGBgCgpBcyB5b3UgY2FuIHNlZSwgdGhpcyBwcm9kdWNlZCBhIHRhYmxlIHdpdGggcm93cyBmb3IgZWFjaCBjZWxsIHR5cGUgYW5kIGNvbHVtbnMgZm9yIGVhY2ggY2x1c3RlciBudW1iZXIuClRoZSB2YWx1ZXMgYXJlIHRoZSBjb3VudCBvZiBjZWxscyBmb3IgZWFjaCBjbHVzdGVyL2NlbGwgdHlwZSBjb21iaW5hdGlvbi4KSG93ZXZlciwgdGhlc2UgcmF3IGNvdW50cyBhcmUgbm90IHF1aXRlIHdoYXQgd2UnbGwgd2FudCBmb3IgdmlzdWFsaXphdGlvbi4gClNpbmNlIHRoZSB0b3RhbCBudW1iZXIgb2YgY2VsbHMgZGlmZmVycyBhY3Jvc3MgY2x1c3RlcnMsIHdlJ2QgbGlrZSB0byBjb252ZXJ0IHRoZXNlIGNvdW50cyBpbnRvIHRoZSBfcHJvcG9ydGlvbnNfIG9mIGVhY2ggY2VsbCB0eXBlIGluIGVhY2ggY2x1c3Rlci4KCldlJ2xsIGRvIHRoaXMgYnkgZ29pbmcgdGhyb3VnaCB0aGUgdGFibGUgY29sdW1uIGJ5IGNvbHVtbiBhbmQgZGl2aWRpbmcgZWFjaCB2YWx1ZSBieSB0aGUgc3VtIGZvciB0aGF0IGNsdXN0ZXIuClRoaXMgd2lsbCBnaXZlIHVzIG5vcm1hbGl6ZWQgdmFsdWVzIHdoZXJlIHRoZSB2YWx1ZXMgaW4gZWFjaCBjb2x1bW4gbm93IHN1bSB0byAxLgpUbyBkbyB0aGF0LCB3ZSB3aWxsIHVzZSB0aGUgYGFwcGx5YCBmdW5jdGlvbiwgd2hpY2ggYWxsb3dzIHVzIHRvIG9wZXJhdGUgb24gYSBtYXRyaXggcm93IGJ5IHJvdyBvciBjb2x1bW4gYnkgY29sdW1uLCBhcHBseWluZyBhIGZ1bmN0aW9uIHRvIGVhY2ggInNsaWNlIi4KU2luY2UgdGhlIGZ1bmN0aW9uIHdlIHdhbnQgdG8gYXBwbHkgaXMgdmVyeSBzaG9ydCwgd2Ugd2lsbCB1c2UgUidzIG5ldyAoYXMgb2YgIHZlcnNpb24gNC4xKSBhbm9ueW1vdXMgZnVuY3Rpb24gc2hvcnRoYW5kOiAKYFwoeCkgLi4uYCBjYW4gYmUgdXNlZCB0byBkZWZpbmUgYSBmdW5jdGlvbiB0aGF0IHRoYXQgdGFrZXMgYXMgaW5wdXQgdmFsdWVzIGB4YCAod2hlcmUgdGhlIGAuLi5gIGlzIHdoZXJlIHlvdSB3b3VsZCBwdXQgdGhlIGV4cHJlc3Npb24gdG8gY2FsY3VsYXRlKS4KSGVyZSB3ZSB3aWxsIGFwcGx5IHRoZSBleHByZXNzaW9uIGB4L3N1bSh4KWAsIHdoaWNoIHdpbGwgZGl2aWRlIGVhY2ggZWxlbWVudCBvZiBhIHZlY3RvciBgeGAgYnkgdGhlIHN1bSBvZiBpdHMgdmFsdWVzLgoKYGBge3Igbm9ybWFsaXplIGJ5IGNvbHVtbn0KIyBub3JtYWxpemUgYnkgdGhlIG51bWJlciBvZiBjZWxscyBpbiBlYWNoIGNsdXN0ZXIgKGNvbHVtbnMpCnR5cGVfY2x1c3Rlcl90YWIgPC0gYXBwbHkoCiAgdHlwZV9jbHVzdGVyX3RhYiwgCiAgMiwgIyBhcHBseSBmdW5jdGlvbiB0byBjb2x1bW5zCiAgXCh4KSB4L3N1bSh4KSAjIGZ1bmN0aW9uIHRvIGFwcGx5CikKIyBwcmludCB0aGUgbm9ybWFsaXplZCB2YWx1ZXMKdHlwZV9jbHVzdGVyX3RhYlsxOjUsIDE6NV0KYGBgCgpOb3cgd2UgY2FuIHBsb3QgdGhlc2UgcmVzdWx0cyBhcyBhIGhlYXRtYXAsIHVzaW5nIHRoZSBgcGhlYXRtYXBgIHBhY2thZ2UuIApUaGVyZSBpcyBhIGxvdCBvZiBjdXN0b21pemF0aW9uIHdlIGNvdWxkIGRvIGhlcmUsIGJ1dCBgcGhlYXRtYXBgIChwcmV0dHkgaGVhdG1hcCkgaGFzIGdvb2QgZGVmYXVsdHMsIHNvIHdlIHdvbid0IHNwZW5kIHRvbyBtdWNoIHRpbWUgb24gaXQgZm9yIG5vdy4KCmBgYHtyIGNsdXN0ZXIgaGVhdG1hcCwgbGl2ZT1UUlVFfQojIHBsb3Qgd2l0aCBwaGVhdG1hcApwaGVhdG1hcDo6cGhlYXRtYXAodHlwZV9jbHVzdGVyX3RhYikKYGBgCgpXZSBjYW4gc2VlIHRoYXQgbW9zdCBvZiBvdXIgY2x1c3RlcnMgYXJlIGluZGVlZCBkZWZpbmVkIGJ5IGEgc2luZ2xlIGNlbGwgdHlwZSwgdGhvdWdoIHRoZXJlIGFyZSBzb21lIGNsdXN0ZXJzIChlLmcuLCAxICYgOSkgdGhhdCBoYXZlIGEgbnVtYmVyIG9mIChyZWxhdGVkKSBjZWxsIHR5cGVzIHdpdGhpbiB0aGVtLgpUaGVyZSBhcmUgYWxzbyBzb21lIHBsYWNlcyB3aGVyZSBzaW5nbGUgY2VsbCB0eXBlcyBhcmUgc3ByZWFkIGFjcm9zcyBhIGZldyBkaWZmZXJlbnQgY2x1c3RlcnMgKENsYXNzaWNhbCBtb25vY3l0ZXMsIGZvciBleGFtcGxlKS4KCiMjIyBDbGFzc2lmeWluZyBieSBjbHVzdGVycwoKV2hpbGUgbW9zdCBvZiB0aGUgdGltZSB3ZSB3aWxsIHdhbnQgdG8gY2xhc3NpZnkgc2luZ2xlIGNlbGxzLCBzb21ldGltZXMgdGhlIHNwYXJzZW5lc3Mgb2YgdGhlIGRhdGEgbWF5IG1lYW4gdGhhdCBpbmRpdmlkdWFsIGNlbGxzIGRvIG5vdCBwcm92aWRlIHJlbGlhYmxlIGVzdGltYXRlcyBvZiBjZWxsIHR5cGVzLiAKCkFuIGFsdGVybmF0aXZlIGFwcHJvYWNoIGlzIHRvIGNsYXNzaWZ5IHRoZSBjbHVzdGVycyBhcyBhIHdob2xlLCBhc3N1bWluZyB0aGF0IHRoZSBjbHVzdGVycyB3ZSBoYXZlIGlkZW50aWZpZWQgcmVwcmVzZW50IGEgc2luZ2xlIGNlbGwgc3RhdGUuCklmIHRoYXQgaXMgdGhlIGNhc2UsIHRoZW4gd2Ugc2hvdWxkIGJlIGFibGUgdG8gY29tYmluZSB0aGUgZGF0YSBmb3IgYWxsIGNlbGxzIGFjcm9zcyBlYWNoIGNsdXN0ZXIsIHRoZW4gYXBwbHkgb3VyIGNlbGwgdHlwaW5nIG1ldGhvZCB0byB0aGlzIGdyb3VwIG9mIGNlbGxzLiAKVGhpcyBpcyBzaW1pbGFyIHRvIGFuIGFwcHJvYWNoIHdlIHdpbGwgcmV0dXJuIHRvIGxhdGVyIGluIHRoZSBjb250ZXh0IG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLiAKClRoZSBmaXJzdCBzdGVwIGhlcmUgaXMgdG8gY3JlYXRlIGEgbmV3IG1hdHJpeCB3aGVyZSB3ZSBzdW0gdGhlIGNvdW50cyBhY3Jvc3MgY2VsbHMgdGhhdCBhcmUgZnJvbSB0aGUgc2FtZSB0eXBlIGFjY29yZGluZyB0byBvdXIgY2x1c3RlcmluZy4KQmVjYXVzZSBgU2luZ2xlUmAgaXMgYSBub24tcGFyYW1ldHJpYyBhcHByb2FjaCwgd2UgY2FuIHBlcmZvcm0gdGhpcyBzdGVwIHdpdGggdGhlIHJhdyBjb3VudHMgbWF0cml4LgpUaGVyZSBhcmUgYSBmZXcgZGlmZmVyZW50IHdheXMgdG8gZG8gdGhpcywgYnV0IHdlIHdpbGwgdXNlIHRoZSBmdW5jdGlvbiBgRGVsYXllZEFycmF5Ojpjb2xzdW0oKWAsIHdoaWNoIGNhbiB3b3JrIGRpcmVjdGx5IG9uIHRoZSBzcGFyc2UgbWF0cmljZXMgdGhhdCBhcmUgb2Z0ZW4gZm91bmQgaW4gU0NFIG9iamVjdHMuCldlIHdpbGwgcHJvdmlkZSBpdCB3aXRoIHRoZSBtYXRyaXggd2UgbmVlZCwgYW5kIHRoZW4gYSB2ZWN0b3Igb2YgdGhlIGNsdXN0ZXIgYXNzaWdubWVudHMgZm9yIGVhY2ggY29sdW1uIG9mIHRoZSBtYXRyaXguClRoZSBmdW5jdGlvbiB3aWxsIHRoZW4gc3VtIGV4cHJlc3Npb24gdmFsdWVzIGZvciBlYWNoIGdlbmUgYWNyb3NzIGFsbCBvZiB0aGUgY29sdW1ucyB0aGF0IGhhdmUgdGhhdCB2YWx1ZS4KCmBgYHtyIHN1bSBjbHVzdGVyc30KIyBzdW0gY291bnQgbWF0cml4IGJ5IGNsdXN0ZXIKY2x1c3Rlcl9tYXQgPC0gRGVsYXllZEFycmF5Ojpjb2xzdW0oY291bnRzKHNjZSksIHNjZSRubl9jbHVzdGVyKQojIHByaW50IG5ldyBkaW1lbnNpb25zCmRpbShjbHVzdGVyX21hdCkKYGBgCgpZb3UgY2FuIHNlZSB0aGF0IHRoZSByZXN1bHRpbmcgbWF0cml4IHN0aWxsIGhhcyB0aGUgc2FtZSBudW1iZXIgb2Ygcm93cyB3ZSBoYXZlIHNlZW4gYmVmb3JlLCBidXQgbm93IG9ubHkgaGFzIGFzIG1hbnkgY29sdW1ucyBhcyB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIHRoYXQgdGhlIGNlbGxzIHdlcmUgYXNzaWduZWQgdG8uCgpOb3cgd2UgY2FuIGFwcGx5IHRoZSBzYW1lIGBTaW5nbGVSYCBtb2RlbCB0byB0aGVzZSByZXN1bHRzLCB1c2luZyB0aGUgbmV3IG1hdHJpeCBhcyBpbnB1dCBhbG9uZyB3aXRoIHRoZSBwcmV2aW91c2x5IHRyYWluZWQgbW9kZWwuCkFzIHRoZXJlIGFyZSBvbmx5IDIwIGNsdXN0ZXJzIHRvIGNsYXNzaWZ5LCB0aGlzIHdpbGwgYmUgdmVyeSBxdWljaywgYW5kIHdlIGRvbid0IG5lZWQgdG8gcGFyYWxsZWxpemUgaXQhCgpgYGB7ciBzaW5nbGVyIGNsdXN0ZXIsIGxpdmU9VFJVRX0KIyBydW4gU2luZ2xlUiBjbGFzc2lmaWNhdGlvbiB3aXRoIHByZXZpb3VzbHkgdHJhaW5lZCBtb2RlbApzaW5nbGVyX2NsdXN0ZXIgPC0gU2luZ2xlUjo6Y2xhc3NpZnlTaW5nbGVSKAogIGNsdXN0ZXJfbWF0LCAjIGNsdXN0ZXIgZXhwcmVzc2lvbiBtYXRyaXgKICBzaW5nbGVyX2ZpbmVtb2RlbCAjIHByZS10cmFpbmVkIG1vZGVsCikKCiMgdmlldyByZXN1bHRzCmhlYWQoc2luZ2xlcl9jbHVzdGVyKQpgYGAKClRoZSByZXN1bHQgaXMgYSBmYWlybHkgc21hbGwgdGFibGUgb2YgcmVzdWx0cywgYnV0IHdlIGFyZSBtb3N0IGludGVyZXN0ZWQgaW4gdGhlIGxhYmVscywgd2hpY2ggd2Ugd291bGQgbGlrZSB0byBhc3NvY2lhdGUgd2l0aCBlYWNoIGNlbGwgaW4gb3VyIFNDRSBvYmplY3QgZm9yIHZpc3VhbGl6YXRpb24uClNpbmNlIHRoZSBjbHVzdGVyIGxhYmVscyBhcmUgdGhlIHJvdyBuYW1lcyBvZiB0aGF0IHRhYmxlLCB3ZSBjYW4gcGVyZm9ybSBhIGN1dGUgbGl0dGxlIHRyaWNrIHRvIGFzc2lnbiBsYWJlbHMgYmFjayB0byBlYWNoIGNlbGwgYmFzZWQgb24gdGhlIG5hbWUgb2YgdGhlIGNsdXN0ZXIgdGhhdCBpdCB3YXMgYXNzaWduZWQgdG8uIAooSW4gdGhpcyBjYXNlIHRoZSBjbHVzdGVyIG5hbWVzIGFyZSBhbGwgbnVtYmVycywgYnV0IHRoYXQgbWlnaHQgbm90IGFsd2F5cyBiZSB0aGUgY2FzZS4pCldlJ2xsIHNlbGVjdCB2YWx1ZXMgcmVwZWF0ZWRseSBmcm9tIHRoZSBgc2luZ2xlcl9jbHVzdGVyYCB0YWJsZSwgdXNpbmcgdGhlIGNsdXN0ZXIgYXNzaWdubWVudCB0byBwaWNrIGEgcm93LCBhbmQgdGhlbiBhbHdheXMgcGlja2luZyB0aGUgYHBydW5lZC5sYWJlbHNgIGNvbHVtbi4KCmBgYHtyIGFzc2lnbiBjZWxsIGxhYmVsc30Kc2NlJGNlbGx0eXBlX2NsdXN0ZXIgPC0gc2luZ2xlcl9jbHVzdGVyW3NjZSRubl9jbHVzdGVyLCAicHJ1bmVkLmxhYmVscyJdCmBgYAoKTm93IHdlIGNhbiBwbG90IHRoZXNlIGNsdXN0ZXItYmFzZWQgY2VsbCB0eXBlIGFzc2lnbm1lbnRzIHVzaW5nIHRoZSBub3cgZmFtaWxpYXIgYHBsb3RVTUFQKClgIGZ1bmN0aW9uLgoKYGBge3IgcGxvdCBjbHVzdGVyIGNlbGx0eXBlcywgbGl2ZT1UUlVFfQpzY2F0ZXI6OnBsb3RVTUFQKHNjZSwgY29sb3JfYnkgPSAiY2VsbHR5cGVfY2x1c3RlciIpCmBgYAoKVGhpcyBzdXJlIGxvb2tzIG5pY2UgYW5kIGNsZWFuLCBidXQgd2hhdCBoYXZlIHdlIHJlYWxseSBkb25lIGhlcmU/IApXZSBhcmUgX2Fzc3VtaW5nXyB0aGF0IGVhY2ggY2x1c3RlciBoYXMgb25seSBhIHNpbmdsZSBjZWxsIHR5cGUsIHdoaWNoIGlzIGEgcHJldHR5IGJvbGQgYXNzdW1wdGlvbiwgYXMgd2UgcmVhbGx5IGFyZW4ndCBzdXJlIHRoYXQgdGhlIGNsdXN0ZXJzIHdlIGNyZWF0ZWQgd2VyZSBjb3JyZWN0LgpZb3UgbWF5IHJlY2FsbCB0aGF0IGNsdXN0ZXJpbmcgYWxnb3JpdGhtcyBhcmUgcXVpdGUgc2Vuc2l0aXZlIHRvIHBhcmFtZXRlciBjaG9pY2UsIHNvIGEgZGlmZmVyZW50IHBhcmFtZXRlciBjaG9pY2UgY291bGQgcXVpdGUgbGlrZWx5IGdpdmUgYSBkaWZmZXJlbnQgcmVzdWx0LgoKIyMjIE1ldGFDZWxsIGFwcHJvYWNoZXMKCkFzIGEgbWlkZGxlIGdyb3VuZCBiZXR3ZWVuIHRoZSBwb3RlbnRpYWxseSBtZXNzeSBzaW5nbGUtY2VsbCBjZWxsIHR5cGUgYXNzaWdubWVudCBhbmQgdGhlIGFsbW9zdC1jZXJ0YWlubHkgb3ZlcmNvbmZpZGVudCBjbHVzdGVyLWJhc2VkIGFzc2lnbm1lbnQgYWJvdmUsIHdlIGNhbiB0YWtlIGFwcHJvYWNoIGluc3BpcmVkIGJ5IFtCYXJhbiBfZXQgYWwuXyAoMjAxOSldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTktMTgxMi0yKSB1c2luZyBzb21ldGhpbmcgdGhleSBjYWxsZWQgX21ldGFjZWxsc18uIApUaGUgaWRlYSBpcyB0aGF0IHdlIGNhbiBwZXJmb3JtIGZpbmUtc2NhbGVkIGNsdXN0ZXJpbmcgdG8gaWRlbnRpZnkgZ3JvdXBzIG9mIHZlcnkgc2ltaWxhciBjZWxscywgdGhlbiBzdW0gdGhlIGNvdW50cyB3aXRoaW4gdGhvc2UgY2x1c3RlcnMgYXMgIm1ldGFjZWxscyIgdG8gdXNlIGZvciBmdXJ0aGVyIGFuYWx5c2lzLiAKVGhlIG9yaWdpbmFsIHBhcGVyIGluY2x1ZGVzIGEgbnVtYmVyIG9mIG9wdGltaXphdGlvbnMgdG8gbWFrZSBzdXJlIHRoYXQgdGhlIG1ldGFjZWxsIGNsdXN0ZXJzIGhhdmUgZGVzaXJhYmxlIHByb3BlcnRpZXMgZm9yIGRvd25zdHJlYW0gYW5hbHlzaXMuCldlIHdvbid0IGdvIGludG8gdGhhdCBkZXB0aCBoZXJlLCBidXQgd2UgY2FuIGFwcGx5IHNpbWlsYXIgaWRlYXMuCgpUbyBiZWdpbiwgd2Ugd2lsbCBwZXJmb3JtIHNvbWUgZmluZS1zY2FsZSBjbHVzdGVyaW5nLCB1c2luZyBhIHNpbXBsZXIgY2x1c3RlcmluZyBhbGdvcml0aG06IEstbWVhbnMgY2x1c3RlcmluZy4KV2Ugd2lsbCB1c2UgdGhlIHNhbWUgYGJsdXN0ZXJgIHBhY2thZ2UsIGNsdXN0ZXJpbmcgYmFzZWQgb24gdGhlIFBDQSByZXN1bHRzIHdlIGhhdmUgZnJvbSBlYXJsaWVyLCBidXQgdGhpcyBhbGdvcml0aG0gYWxsb3dzIHVzIHRvIHNwZWNpZnkgdGhlIG51bWJlciBvZiBjbHVzdGVycyB3ZSB3YW50IHRvIGVuZCB1cCB3aXRoLgpXZSBoYXZlIGFib3V0IDgwMDAgY2VsbHMsIHNvIGxldCdzIGNsdXN0ZXIgdGhvc2UgaW50byBncm91cHMgb2YgYXBwcm94aW1hdGVseSA4MCBjZWxscywgd2hpY2ggd29ya3Mgb3V0IHRvIDEwMCBjbHVzdGVycy4KV2hpbGUgdGhpcyBpcyBhbG1vc3QgY2VydGFpbmx5IG1vcmUgY2x1c3RlcnMgdGhhbiBhcmUgInJlYWwiIGluIHRoaXMgZGF0YXNldCwgb3VyIGdvYWwgaGVyZSBpcyBub3QgdG8gZmluZCBkaWZmZXJlbmNlcyBhbW9uZyBjbHVzdGVycywganVzdCB0byBnZXQgaG9tb2dlbmVvdXMgZ3JvdXBzIG9mIGNlbGxzLgoKYGBge3Iga21lYW5zIGNsdXN0ZXJ9CiMgcGVyZm9ybSBrLW1lYW5zIGNsdXN0ZXJpbmcKa2NsdXN0ZXJzIDwtIGJsdXN0ZXI6OmNsdXN0ZXJSb3dzKAogIHJlZHVjZWREaW0oc2NlLCAiUENBIiksIAogIGJsdXN0ZXI6OkttZWFuc1BhcmFtKAogICAgY2VudGVycyA9IDEwMCwgIyB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIAogICAgaXRlci5tYXggPSAxMDAgIyBtb3JlIGl0ZXJhdGlvbnMgdG8gYmUgc3VyZSBvZiBjb252ZXJnZW5jZQogICkKKQpgYGAKCk5vdyB3ZSBjYW4gYXBwbHkgZXhhY3RseSB0aGUgc2FtZSBhcHByb2FjaCB3ZSBkaWQgd2hlbiB3ZSBoYWQgdGhlIDIwIGNsdXN0ZXJzIHdlIGhhZCBpZGVudGlmaWVkIHdpdGggdGhlIGVhcmxpZXIgZ3JhcGgtYmFzZWQgY2x1c3RlcmluZy4KCmBgYHtyIG1ldGFjZWxsIHNpbmdsZXJ9CiMgY3JlYXRlIGEgIm1ldGFjZWxsIiBtYXRyaXggYnkgc3VtbWluZyBmaW5lLXNjYWxlIGNsdXN0ZXJzCm1ldGFjZWxsX21hdCA8LSBEZWxheWVkQXJyYXk6OmNvbHN1bShjb3VudHMoc2NlKSwga2NsdXN0ZXJzKQoKIyBhcHBseSBTaW5nbGVSIG1vZGVsIHRvIG1ldGFjZWxsIG1hdHJpeAptZXRhY2VsbF9zaW5nbGVyIDwtIFNpbmdsZVI6OmNsYXNzaWZ5U2luZ2xlUigKICBtZXRhY2VsbF9tYXQsIAogIHNpbmdsZXJfZmluZW1vZGVsCikKCiMgYXBwbHkgbWV0YWNlbGwgY2VsbCB0eXBlIGFzc2lnbm1lbnRzIHRvIGluZGl2aWR1YWwgY2VsbHMKc2NlJGNlbGx0eXBlX21ldGFjZWxsIDwtIG1ldGFjZWxsX3NpbmdsZXJba2NsdXN0ZXJzLCAicHJ1bmVkLmxhYmVscyJdCmBgYAoKTm93IHdlIGNhbiBwbG90IHRoZSByZXN1bHRzIGFzIHdlIGhhdmUgZG9uZSBiZWZvcmUuCgpgYGB7ciBtZXRhY2VsbCB1bWFwfQpzY2F0ZXI6OnBsb3RVTUFQKHNjZSwgY29sb3JfYnkgPSAiY2VsbHR5cGVfbWV0YWNlbGwiKQpgYGAKCldoYXQgZG8geW91IHRoaW5rIG9mIHRoaXMgcGxvdD8gCklzIHRoaXMgbW9yZSBvciBsZXNzIHVzZWZ1bCB0aGFuIHRoZSBvcmlnaW5hbCBjZWxsLWJhc2VkIGNsdXN0ZXJpbmc/CgoKIyMgU2F2ZSByZXN1bHRzCgpUbyBzYXZlIGRpc2sgc3BhY2UgKGFuZCB0aW1lKSwgd2Ugd29uJ3Qgd3JpdGUgb3V0IHRoZSB3aG9sZSBTQ0Ugb2JqZWN0LCBhcyB3ZSBoYXZlbid0IGNoYW5nZWQgYW55IG9mIHRoZSBjb3JlIGRhdGEgdGhlcmUuIApJbnN0ZWFkIHdlIHdpbGwganVzdCB3cml0ZSBvdXQgdGhlIGNlbGwgaW5mb3JtYXRpb24gdGFibGUgKGBjb2xEYXRhYCkgYXMgYSBUU1YgZmlsZS4KCmBgYHtyIHNhdmUgY2VsbCBpbmZvLCBsaXZlPVRSVUV9CmNvbERhdGEoc2NlKSB8PgogIGFzLmRhdGEuZnJhbWUoKSB8PgogIHJlYWRyOjp3cml0ZV90c3YoZmlsZSA9IGNlbGxpbmZvX2ZpbGUpCmBgYAoKCiMjIFByaW50IHNlc3Npb24gaW5mbwoKYGBge3Igc2Vzc2lvbiBpbmZvfQpzZXNzaW9uSW5mbygpCmBgYAoK