Objectives

This notebook will demonstrate how to:

  • Use pseudo-bulking to prepare scRNA-seq libraries for differential expression
  • Perform differential expression with the DESeq2 package
  • Use ggplot2 and EnhancedVolcano to visualize gene expression changes across cell types and samples

Just like bulk RNA-seq, it is likely that one of the goals when performing scRNA-seq will be to compare the gene expression of multiple samples to each other. Unlike bulk RNA-seq analysis, scRNA-seq analysis allows us to identify and annotate cell types or subpopulations of cells present in each of our samples. This means that we can account for differences in cell type composition across samples and specifically focus on cell types or populations of interest when performing differential expression (DE) analysis. In this notebook, we will work with multiple samples to identify differentially expressed genes across cell types of interest using the DESeq2 package.

Single-cell roadmap: Differential expression
Single-cell roadmap: Differential expression

We will continue working with samples from the SCPCP000005 project, an investigation of pediatric solid tumors led by the Dyer and Chen labs at St. Jude Children’s Research Hospital. This particular dataset contains 10 different samples that have been integrated using fastMNN, following the same procedure we outlined in 02-dataset_integration.Rmd. These 10 samples represent two different types of rhabdomyosarcoma (RMS): embryonal rhabdomyosarcoma (ERMS) and alveolar rhabdomyosarcoma (ARMS). These two subtypes are distinguished by the presence of the PAX3/PAX7-FOXO1 fusion gene, which is present only in ARMS patients. Additionally, cells found in ARMS tumors tend to have an increased mutational burden with cells in a more differentiated state compared to ERMS tumor cells (Shern et al. 2014; Stewart et al. 2018). RMS tumors, regardless of subtype, are made up of cells typically associated with development of skeletal muscle: mesoderm, myoblasts, and myocytes (Sebire and Malone 2003). Patel et al. (2022) tested the hypothesis that cell types have distinct gene expression patterns in ARMS vs. ERMS samples. Here we will look at a subset of the samples they sequenced and identify differentially expressed genes in tumor cells between ARMS and ERMS samples.

Set up

# set seed for reproducibility
set.seed(2022)

# load libraries
library(ggplot2) # plotting functions
library(SingleCellExperiment) 
Warning: replacing previous import 'S4Arrays::makeNindexFromArrayViewport' by
'DelayedArray::makeNindexFromArrayViewport' when loading 'SummarizedExperiment'
# package used for differential expression analysis
library(DESeq2)

Directories and files

We will start by reading in a SingleCellExperiment (SCE) object that contains both the uncorrected (merged but not integrated) and corrected (integrated) gene expression data for all 10 samples.

Prior to integration, all 10 samples went through the same filtering, normalization, and dimensionality reduction. These 10 samples were then merged into one SingleCellExperiment object following the same steps outlined in 03-dataset_integration.Rmd. The merged object was then integrated with fastMNN to obtain a corrected gene expression assay and corrected reduced dimensionality results. The final SCE object was stored in data/rms/integrated/rms_all_sce.rds.

We also have provided a metadata file, data/rms/annotations/rms_sample_metadata.tsv, that contains information from each sample, such as diagnosis, sex, age, etc. Each row in this file corresponds to a sample found in the integrated SCE object.

To begin, let’s set up our directories and files:

# set up file paths 
# data directory for RMS data
data_dir <- file.path("data", "rms")

# integrated file containing samples to use for DE analysis
integrated_sce_file <- file.path(data_dir, 
                                 "integrated", 
                                 "rms_all_sce.rds")

# sample metadata to set up DE analysis
sample_metadata_file <- file.path(data_dir, 
                                  "annotations", 
                                  "rms_sample_metadata.tsv")

# directory to store output
deseq_dir <- file.path("analysis", "rms", "deseq")
fs::dir_create(deseq_dir)

# results file to output from DE analysis
deseq_output_file <- file.path(deseq_dir, 
                               "rms_myoblast_deseq_results.tsv")

# output integrated sce object
output_sce_file <- file.path(data_dir, 
                             "integrated", 
                             "rms_subset_sce.rds")

We can go ahead and read in the SCE object and the metadata file.

# read in the SCE object that has already been integrated
integrated_sce <- readr::read_rds(integrated_sce_file)

# read in sample metadata file 
sample_metadata <- readr::read_tsv(sample_metadata_file)
Rows: 10 Columns: 10
── Column specification ────────────────────────────────────────────────────────
Delimiter: "\t"
chr (10): project_id, submitter, library_id, sample_id, diagnosis, technolog...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Dataset exploration

Before we dive into differential expression, let’s explore our integrated SCE object and the dataset a little more.

We’ll start by looking at what’s inside the object. Here we should have both the original (uncorrected) data and the integrated (corrected) data for both the gene expression and the reduced dimensionality results. How are those stored in our object?

# print out entire object
integrated_sce
class: SingleCellExperiment 
dim: 60319 39332 
metadata(141): salmon_version reference_index ... miQC_model
  combined_hvg
assays(3): counts logcounts fastmnn_corrected
rownames(60319): ENSG00000000003 ENSG00000000005 ... ENSG00000288724
  ENSG00000288725
rowData names(21): gene_symbol SCPCL000478-mean ... SCPCL000498-mean
  SCPCL000498-detected
colnames(39332): SCPCL000478-CCTTTGGCACTACCCT
  SCPCL000478-GCAACCGGTCTTAGTG ... SCPCL000498-CGTCAAATCGAGAATA
  SCPCL000498-GGAACCCCACGACAAG
colData names(13): sum detected ... celltype_broad sample
Loading required package: BiocSingular
reducedDimNames(4): PCA UMAP fastmnn_PCA fastmnn_UMAP
mainExpName: NULL
altExpNames(0):
# look at the assay names in our object
assayNames(integrated_sce)
[1] "counts"            "logcounts"         "fastmnn_corrected"
counts
logcounts
fastmnn_corrected

When we look at the assay names we should see that there are 3 matrices, counts, logcounts, and fastmnn_corrected. The counts and logcounts assays correspond to the uncorrected gene expression data that has been merged but NOT integrated. The fastmnn_corrected data contains the corrected gene expression data obtained from integration. For this exercise we will not be using the fastmnn_corrected data (more on why not once we get to setting up the differential expression), but we need to be aware that it is present and be able to distinguish it from our uncorrected data.

# look at the names of the dimension reductions
reducedDimNames(integrated_sce)
[1] "PCA"          "UMAP"         "fastmnn_PCA"  "fastmnn_UMAP"
PCA
UMAP
fastmnn_PCA
fastmnn_UMAP

In the reducedDim slots you should see PCA and UMAP, which were both calculated from the combined data before integration. You should also see fastmnn_PCA and fastmnn_UMAP reduced dimensions, which correspond to the integrated results.

Cell type annotations

Just like in the integration notebook, this dataset also contains the cell type annotations found in the celltype_fine and celltype_broad columns of the colData. These cell types were originally assigned in Patel et al. (2022). We will use these cell type assignments to set up the DE analysis below, but they are not required for DE analysis itself. It’s important to note that DE analysis can be applied to any subpopulation of interest that is shared across samples besides just cell types.

Because we are going to be doing DE analysis between ARMS and ERMS samples, let’s start by labeling cells in the integrated dataset based on their RMS subtype. To do this we will need to be sure that the subtype is present in the colData of the integrated SCE object. If it’s not there, we need to add it in.

# look at the head of the coldata
head(colData(integrated_sce)) |>
  as.data.frame()

Uh oh, it looks like the RMS subtype is not found in the SCE object. Fortunately we also have the sample metadata table that we read in earlier, which contains information about each of the samples present in the dataset.

# print out sample metadata
head(sample_metadata)

Looking at this sample table, we see a column named subdiagnosis which accounts for the RMS subtype, ARMS or ERMS. We also see other columns that contain information about each specific sample.

We can incorporate the information in this sample metadata table into the colData of the integrated SCE object. This will allow us to match each of the samples in the SCE object with the RMS subtype and also allow us to use any of the columns in the sample metadata for plotting.

# add sample metadata to colData from the integrated SCE object
coldata_df <- colData(integrated_sce) |>
  # convert from DataFrame to data.frame
  as.data.frame() |>
  # merge with sample metadata 
  dplyr::left_join(sample_metadata, by = c("sample" = "library_id")) |>
  # create new columns
  # cell_id is a combination of barcode and sample
  dplyr::mutate(cell_id = glue::glue("{sample}-{barcode}"),
                # simplify subdiagnosis
                diagnosis_group = forcats::fct_recode(
                  subdiagnosis,
                  "ARMS" = "Alveolar rhabdomyosarcoma",
                  "ERMS" = "Embryonal rhabdomyosarcoma"
                ))

# add modified data frame back to SCE as DataFrame
colData(integrated_sce) <- DataFrame(coldata_df, 
                                     row.names = coldata_df$cell_id)

Now when we look at the colData of the SCE object we should see new columns, including the diagnosis_group column which indicates if each cell comes from an ERMS or ARMS sample.

# take a look at the new modified colData
head(colData(integrated_sce)) |>
  as.data.frame()

Plotting with annotations

We can now use that column to label any UMAP plots (or other plot types) that we make. In the chunk below we will start by taking a look at our integration results and color our cells by RMS subtype.

Reminder: You should always use the batch-corrected dimensionality reduction results for visualizing datasets containing multiple libraries or samples.

# UMAP of all samples, separating by diagnosis group
scater::plotReducedDim(integrated_sce,
                       dimred = "fastmnn_UMAP",
                       color_by = "diagnosis_group",
                       point_size= 0.5,
                       point_alpha = 0.2) 

Interestingly, it looks like samples from the ARMS and ERMS subtypes tend to group with samples of the same subtype rather than all together.

In the integration notebook we also looked at the distribution of cell types after integration. In that notebook, we discussed that cells of the same cell type are expected to integrate with other cells of the same type. Is that the case with this dataset?

A word of caution when evaluating the cell type results for this dataset: The cell types for this dataset were assigned in a two stage process as described in Patel et al. (2022). The first stage assigned cells as tumor or non-tumor. The next stage further classified tumor cells into one of three types of tumor cells: myoblast, myocyte, or mesoderm. Some samples could not be further classified, so all of their tumor cells are denoted Tumor. The samples which could be further classified have a mix of Tumor_Mesoderm, Tumor_Myoblast, and Tumor_Myocyte.

# UMAP of all samples labeled by cell type
scater::plotReducedDim(integrated_sce,
                       dimred = "fastmnn_UMAP",
                       # color each point by cell type
                       color_by = "celltype_broad",
                       point_size= 0.5, 
                       point_alpha = 0.4)

Unlike with the previous datasets we have seen where all cells of the same cell type always grouped together, this dataset shows some slightly different patterns and not all cells of the same cell type cluster together. One reason is that tumor data can be heterogeneous and every tumor is unique. Depending on the tumor type we may not expect every sample to integrate perfectly and more heterogeneous tumor types will be more difficult to integrate together. In this particular case we are looking at two subtypes of RMS that have distinct mutation burdens and differentiation states, so it’s likely that those differences contribute to how well they integrate.

To explore whether cells are grouping together both by cell type and by RMS subtype, we can create a plot that incorporates both pieces of metadata. We will take advantage of the facet_grid() function from ggplot2 to look at two variables in the colData at once - the cell type and the subdiagnosis. In the below plot we will color our cells by cell type while also using facet_grid() so that cells from different subdiagnoses will be in their own plot panel.

# UMAP of all samples
# separating by diagnosis group and labeling cell type
scater::plotReducedDim(integrated_sce,
                       dimred = "fastmnn_UMAP",
                       # color each point by cell type
                       color_by = "celltype_broad",
                       point_size= 0.5, 
                       point_alpha = 0.4,
                       # tell scater to use diagnosis_group for plotting
                       other_fields = "diagnosis_group") +
  # include each diagnosis group as its own column
  facet_grid(cols = vars(diagnosis_group))

As expected, we see that cell types are separated, most likely due to different RMS subtypes.

We can also use a stacked barplot to look at the distribution of cell types across each sample, which will require a bit of wrangling first.

# filter coldata to only include tumor cells
tumor_cells_df <- coldata_df |>
  # find rows where the cell type name contains the string "Tumor"
  dplyr::filter(stringr::str_detect(celltype_broad, "Tumor"))

# create a stacked barplot
ggplot(tumor_cells_df, aes(x = sample, fill = celltype_broad)) + 
    geom_bar(position = "fill", color = "black", size = 0.2) +
    labs(
      x = "Sample",
      y = "Proportion of cells", 
      fill = "Cell type"
    ) +
  scale_fill_brewer(palette = "Dark2") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5))+
  # facet by diagnosis group 
  facet_grid(cols = vars(diagnosis_group), 
             # only show non-NA values on x-axis
             scales = "free_x",
             space = "free_x")
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
generated.

Similar to the UMAP, this plot shows that ARMS and ERMS share a lot of the same cell types.

We also see that only 6 of these libraries have tumor cells that have been further classified into mesoderm, myoblast, and myocyte. 3 libraries contain cells that are only classified as tumor or non-tumor, and tumor cells are not further classified, and the remaining library is not even present in our plot because it was not assigned any cell types (all are NA). We will continue our analysis only using the 6 libraries with fully classified cell types, removing the other 4 before we proceed with differential expression.

Filtering samples

The reason we want to pare down our list of samples to consider is that we want to ensure that the cell types (or subpopulations) that we are interested in are present in all samples included in our DE analysis. We want to remove any samples that do not contain our cell population(s) of interest as they have no counts to contribute to the DE analysis.

# define samples to keep
library_ids <- c(
  "SCPCL000479",
  "SCPCL000480",
  "SCPCL000481",
  "SCPCL000484",
  "SCPCL000488",
  "SCPCL000491"
)

# subset sce to only contain samples of interest 
samples_to_keep <- integrated_sce$sample %in% library_ids
rms_sce <- integrated_sce[, samples_to_keep]

# print out our new SCE 
rms_sce
class: SingleCellExperiment 
dim: 60319 26033 
metadata(141): salmon_version reference_index ... miQC_model
  combined_hvg
assays(3): counts logcounts fastmnn_corrected
rownames(60319): ENSG00000000003 ENSG00000000005 ... ENSG00000288724
  ENSG00000288725
rowData names(21): gene_symbol SCPCL000478-mean ... SCPCL000498-mean
  SCPCL000498-detected
colnames(26033): SCPCL000479-GGGACCTCAAGCGGAT
  SCPCL000479-CACAGATAGTGAGTGC ... SCPCL000491-TCGCACTAGGAACGTC
  SCPCL000491-TTGCATTTCAACGCTA
colData names(24): sum detected ... cell_id diagnosis_group
reducedDimNames(4): PCA UMAP fastmnn_PCA fastmnn_UMAP
mainExpName: NULL
altExpNames(0):

Before we move on, we’ll remove the original integrated object from our environment to save some memory.

rm(integrated_sce)

We will also save our new object in case we want to use it for other analysis later on.

# write RDS file with compression
readr::write_rds(rms_sce, file = output_sce_file, compress = "gz")

We now have an updated SCE object that contains 6 samples that were obtained from a mix of ARMS and ERMS patients. We can then ask the question, do specific tumor cell types contain sets of differentially expressed genes between ARMS and ERMS samples?

We should make sure that we have enough biological replicates from each group to set up our experiment. It is imperative to consider good experimental design and ensure that we have enough biological replicates (at least 3 for each group) when performing differential gene expression analysis.

If we look back at our stacked barplot we see that we picked 3 ARMS and 3 ERMS samples. We can also see that the majority of cells are tumor cells, in particular the largest population of cells appears to be the Tumor_Myoblast. For this example we will focus on identifying DE genes in these Tumor_Myoblast cells, but the principles applied below can be applied to any cell types or subpopulations of interest.

Differential expression analysis

Now we are ready to start preparing for our DE analysis, where we will compare the gene expression of tumor myoblast cells between ARMS and ERMS samples.

Throughout the notebook we have been working with an integrated dataset that contains corrected gene expression data (fastmnn_corrected assay) and a corrected UMAP. As a reminder, the uncorrected gene expression data, found in the counts and logcounts assays, correspond to data that has been merged (the first step we walked through prior to integration) into the same SCE but not yet integrated. We do not want to use corrected gene expression values for differential expression; DESeq2 expects the original raw counts as input so we will be using data found in the counts assay of the SingleCellExperiment object.

It is advised to only use the corrected values for any analyses being performed at the cell level, e.g., dimensionality reduction. In contrast, it is not advised to use corrected values for any analyses that are gene-based, such as differential expression or marker gene detection, because within-batch and between-batch gene expression differences are no longer preserved. The reason for this is two-fold – many of the DE models will expect uncorrected counts because they will account for between-sample variation within the model, and we want to ensure we are preserving variation that is present so as not to artificially inflate differences between populations. See the OSCA chapter on Using the corrected values for more insight.

Pseudo-bulking

Before we can compare the gene expression profiles of myoblasts in ARMS vs. ERMS samples, we will need to “pseudo-bulk” the gene counts. Pseudo-bulking creates a new counts matrix that contains the sum of the counts from all cells with a given label (e.g., cell type) for each sample (Tung et al. 2017). If we were to keep each cell’s counts separate, they would be treated as replicates, leading to inflated statistics. By pseudo-bulking first, we will now have one count for each gene for each sample and we can take advantage of well-established methods for differential expression with bulk RNA-seq.

Pseudo-bulking is implemented prior to differential expression analysis on single-cell data because it:

  • Produces larger and less sparse counts, which allows us to use standard normalization and differential expression methods used by bulk RNA-seq.
  • Collapses gene expression counts by sample, so that samples, rather than cells, represent replicates.
  • Masks variance within a sample to emphasize variance across samples. This can be both good and bad! Masking intra-sample variation means you might not identify genes where average expression doesn’t change between samples but the degree of cell-to-cell variation does.

Before we apply pseudo-bulking to our dataset, let’s look at a simple example of how pseudo-bulking works. We’ll start by creating a fake matrix of counts.

# create an example counts matrix
counts_mtx <- matrix(
  1:12, 
  ncol = 4,
  dimnames = list(c("geneA", "geneB", "geneC"),
                  c("A-cell1", "A-cell2", "B-cell1", "B-cell2"))
)
counts_mtx
      A-cell1 A-cell2 B-cell1 B-cell2
geneA       1       4       7      10
geneB       2       5       8      11
geneC       3       6       9      12

Next we will create a pseudo-bulked version of this matrix with only 2 columns: 1 for group A and 1 for group B. To do this we will use the DelayedArray::colsum() function, which allows us to sum the counts for each row across groups of columns.

# define the group that each column belongs to
groups <- c("A", "A", "B", "B")

# sum counts across cells (columns) by group label
pb_counts <- DelayedArray::colsum(counts_mtx, 
                                  groups)
pb_counts  
      A  B
geneA 5 17
geneB 7 19
geneC 9 21

Looking at this output, you should see that the original 4 columns have been condensed to only 2 columns: 1 column to represent all cells from group A, and 1 column to represent all cells from group B.

Now the actual pseudo-bulking for our dataset!

We will use the scuttle::aggregateAcrossCells() function to pseudo-bulk our dataset. This function takes as input an SCE object and the grouping assignments for each cell. The output will be an SCE object that contains only the pseudo-bulked counts for all genes across all specified groups, rather than across all cells. We can then subset this SCE to just include our cell type of interest (tumor myoblasts) for input to the DE analysis.

We can pseudo-bulk using any grouping that we are interested in. For right now, we are interested in looking at gene expression across cell types, so we want to group the pseudo-bulked counts matrix by both cell type and original sample.

# first subset the coldata 
# to only have the columns we care about for pseudo-bulking 
pb_groups <- colData(rms_sce)[, c("celltype_broad", "sample")]

# create a new SCE object that contains 
# the pseudo-bulked counts across the provided groups 
pb_sce <- scuttle::aggregateAcrossCells(rms_sce, 
                                        id = pb_groups)

# column names aren't automatically added to the pseudo-bulked sce, 
# so let's add them in 
colnames(pb_sce) <- glue::glue(
  "{pb_sce$celltype_broad}_{pb_sce$sample}"
)

pb_sce
class: SingleCellExperiment 
dim: 60319 37 
metadata(141): salmon_version reference_index ... miQC_model
  combined_hvg
assays(1): counts
rownames(60319): ENSG00000000003 ENSG00000000005 ... ENSG00000288724
  ENSG00000288725
rowData names(21): gene_symbol SCPCL000478-mean ... SCPCL000498-mean
  SCPCL000498-detected
colnames(37): Fibroblast_SCPCL000488 Lymphocyte_SCPCL000484 ...
  Vascular Endothelium_SCPCL000488 Vascular Endothelium_SCPCL000491
colData names(27): sum detected ... sample ncells
reducedDimNames(4): PCA UMAP fastmnn_PCA fastmnn_UMAP
mainExpName: NULL
altExpNames(0):

How does the new pseudo-bulked SingleCellExperiment look different? How many columns does it have?

Let’s take a look at what the colData looks like in the pseudo-bulked SCE object.

# note the new column with number of cells per group 
head(colData(pb_sce)) |>
  as.data.frame()

You should see that columns such as sum, detected, subsets_mito_sum, and other columns that typically contain per cell QC statistics now contain NA rather than numeric values. This is because these values were initially calculated on a per cell level (we did this using scuttle::addPerCellQCMetrics()), but we no longer have a single column per cell. Instead, each column now represents a group of cells, in this case comprised of cells of a given cell type and sample combination. Therefore, the values that we calculated on a per-cell level are no longer applicable to this pseudo-bulked SCE object.

You should also see a new column that wasn’t present previously, the ncells column. This column was added during pseudo-bulking and indicates the total number of cells that were summed together to form each column of the SCE object.

Before we proceed we will want to filter out any columns that have a low number of cells. A low number of cells will usually result in small counts that can cause issues with the statistical approximations made during differential expression analysis. This is equivalent to filtering out any libraries in bulk RNA-seq analysis that have low library sizes.

We can set a threshold for the number of cells required to continue with our analysis and remove any groups that do not meet the minimum threshold. Here we will use 10, but the threshold you use for your dataset can vary depending on the composition of cell types.

# remove any groups with fewer than 10 cells
filter_pb_sce <- pb_sce[, pb_sce$ncells >= 10]

We can then take a look and see how many cell type-sample columns we removed, if any.

# print out dimensions of unfiltered pseudobulk sce
dim(pb_sce)
[1] 60319    37
# dimensions of filtered pseudobulk sce 
dim(filter_pb_sce)
[1] 60319    36

It looks like we only got rid of one group. We can do a quick check to see which group was removed by finding which column is no longer present in the filtered object.

# find removed columns
removed_cols <- !(colnames(pb_sce) %in% colnames(filter_pb_sce))

# print out missing columns
colnames(pb_sce)[removed_cols]
[1] "Lymphocyte_SCPCL000484"
Lymphocyte_SCPCL000484

The last step we want to do to prepare our dataset for DE is to subset the pseudo-bulked SCE object to contain only the cell type that we are interested in comparing across the two RMS subtypes. As mentioned previously, we are specifically interested in the Tumor_Myoblast cell type.

# logical vector indicating if cells are tumor myoblast or not
myoblast_cells <- filter_pb_sce$celltype_broad == "Tumor_Myoblast"

# create a new sce with only the tumor myoblasts
tumor_myoblast_sce <- filter_pb_sce[, myoblast_cells]

After filtering for our cell type of interest we should have a dataset with 6 columns, 1 for each group of Tumor_Myoblast cells in each of our 6 samples.

Perform differential expression with DESeq2

Now we will use the DESeq2 package to perform differential expression (DE) analysis on our pseudo-bulked SCE object. From this point, we can proceed in the same way we would if we had a bulk RNA-seq dataset with 6 samples. We will start with the unnormalized raw counts in the counts assay of the pseudo-bulked SCE and do the following with DESeq2:

  • Create a DESeqDataSet object
  • Normalize and log transform the counts data
  • Estimate dispersions and shrink estimates
  • Fit a negative binomial model and perform hypothesis testing using Wald statistics

You can also refer to our materials from our previous workshops covering bulk RNA-seq for more information on using DESeq.

Create the DESeqDataSet object

To create the DESeqDataSet object we will need the unnormalized counts matrix, the metadata associated with the samples, and a design formula. The first two items are already stored in our SCE object, so we can create a DESeqDataSet object directly from that object using the DESeqDataSet() function. The design formula is used to indicate which columns of the metadata need to be considered in the DE comparison. For our experiment we are comparing gene expression between different RMS subtypes. The subtype information is stored in the diagnosis_group column of the colData in the pseudo-bulked SCE.

# set up the deseq object, group by diagnosis
deseq_object <- DESeq2::DESeqDataSet(tumor_myoblast_sce,
                                     design = ~ diagnosis_group)
converting counts to integer mode

The pseudo-bulked SCE object contains only one assay: the counts assay. This is because DESeq2 expects raw counts. When we run DESeq2 on our dataset, raw counts will first be normalized using size factors to account for differences in total sample counts. Therefore we don’t have to do any normalization on our own – we’ll let DESeq2 do all the work for us.

However, before we dive into DE analysis, we can do some initial exploration and visualization of our data to see if our samples separate by our known factor of interest, RMS subtype. In particular, we can use principal component analysis (PCA) of our pseudo-bulked dataset to visualize any variation between samples. If there is variation between RMS subtypes, we expect their respective samples to separate in PC space, likely indicating presence of differentially expressed genes. We can evaluate this by plotting PC1 and PC2.

In order to create our PCA plot, we will first need to normalize our data to account for any technical variations across samples. As a reminder, this is NOT required for running DESeq2 analysis; we are just using it to visualize our data prior to DE analysis.

# estimate size factors first
deseq_object <- DESeq2::estimateSizeFactors(deseq_object)

# normalize and log transform to use for visualization
normalized_object <- DESeq2::rlog(deseq_object, 
                                  blind = TRUE)
normalized_object
class: DESeqTransform 
dim: 60319 6 
metadata(142): salmon_version reference_index ... combined_hvg version
assays(1): ''
rownames(60319): ENSG00000000003 ENSG00000000005 ... ENSG00000288724
  ENSG00000288725
rowData names(28): gene_symbol SCPCL000478-mean ... dispFit
  rlogIntercept
colnames(6): Tumor_Myoblast_SCPCL000479 Tumor_Myoblast_SCPCL000480 ...
  Tumor_Myoblast_SCPCL000488 Tumor_Myoblast_SCPCL000491
colData names(27): sum detected ... sample ncells

We now have a normalized and transformed object that can be directly input to the DESeq2::plotPCA() function, which will both calculate and plot the PC results.

DESeq2::plotPCA(normalized_object, intgroup = "diagnosis_group")
using ntop=500 top features by variance

As expected we see that samples group together based on RMS subtype and are separated along the PC1 axis, the PC contributing the highest amount of variation.

Run DESeq

We’ll now use the convenience function DESeq() to perform our differential expression analysis. This function calculates normalization factors, estimates gene-wise dispersions, fits a negative binomial model and performs hypothesis testing using Wald statistics.

# run DESeq
deseq_object <- DESeq2::DESeq(deseq_object)
using pre-existing size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing

We can evaluate how well the model fit our data by looking at the dispersion estimates. We expect to see the dispersion estimates decrease as means are increasing and follow the line of best fit.

plotDispEsts(deseq_object)

Now we can extract the results from the object, specifying the p-value threshold that we would like to use.

# extract the results as a DataFrame
deseq_results <- DESeq2::results(deseq_object, alpha = 0.05)

But we aren’t done yet!

The estimates of log2 fold change calculated by DESeq() are not corrected for expression level. This means that when counts are small, we are likely to end up with some large fold change values that overestimate the true extent of the change between conditions.

We can correct this by applying a “shrinkage” procedure, which will adjust large values with small counts downward, while preserving values with larger counts, which are likely to be more accurate.

To do this, we will use the lfcShrink() function, but first we need to know the name and/or position of the “coefficient” that was calculated by DESeq(), which we can do with the resultsNames() function.

# identify position of coefficient
DESeq2::resultsNames(deseq_object)
[1] "Intercept"                    "diagnosis_group_ERMS_vs_ARMS"
Intercept
diagnosis_group_ERMS_vs_ARMS
# appyly logFC shrinkage using the default model
shrink_results <- DESeq2::lfcShrink(
  deseq_object, 
  res = deseq_results, 
  coef = 2,
  type = "apeglm"
)
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
head(shrink_results)
log2 fold change (MAP): diagnosis group ERMS vs ARMS 
Wald test p-value: diagnosis group ERMS vs ARMS 
DataFrame with 6 rows and 5 columns
                 baseMean log2FoldChange     lfcSE    pvalue      padj
                <numeric>      <numeric> <numeric> <numeric> <numeric>
ENSG00000000003 101.05115      0.4108102  0.508644  0.314263  0.653799
ENSG00000000005  15.06975      0.7449261  0.992470  0.106750  0.368312
ENSG00000000419 445.70945     -0.1982544  0.443807  0.594405  0.850099
ENSG00000000457 287.05639     -0.4654800  0.530626  0.262554  0.600069
ENSG00000000460 553.73970     -0.0980086  0.428254  0.785786  0.932283
ENSG00000000938   5.64924      0.1550990  0.655715  0.705489  0.900066

If you look at our shrink_results object, we see that the genes are labeled with the Ensembl gene identifiers, as those were the row names of the pseudo-bulked SCE we used as input to build our DESeq2 object. Although some of us may have all of the identifiers memorized by heart, it can be useful to have a human readable symbol in our results. Before we save the results as a file, we will grab the gene symbols from the rowData of our original SCE object and add them as a new column.

deseq_results <- shrink_results |>
  # directly add Ensembl id as a column
  # converting results into a data frame
  tibble::as_tibble(rownames = "ensembl_id")

# convert rowdata to data frame 
sce_rowdata_df <- rowData(tumor_myoblast_sce) |>
  # create a column with rownames stored as ensembl id
  # use for joining with deseq results
  tibble::as_tibble(rownames = "ensembl_id")

# combine deseq results with rowdata by ensembl id 
deseq_results <- deseq_results |>
  dplyr::left_join(sce_rowdata_df, by = "ensembl_id")

head(deseq_results)

We can save the new data frame that we have created with the Ensembl identifiers, gene symbols, and the DESeq2 results as a tab separated (tsv) file.

# save our results as tsv
readr::write_tsv(deseq_results, deseq_output_file)

The last thing that we will do is take a look at how many genes are significant. Here we will want to use the adjusted p-value, found in the padj column of the results, as this accounts for multiple test correction.

# first look at the significant results 
deseq_results_sig <- deseq_results |>
  # filter based on adjusted pvalue
  dplyr::filter(padj <= 0.05)

head(deseq_results_sig)

Exploring the identified differentially expressed genes

Now that we have identified a set of genes that are differentially expressed in the tumor myoblasts between ARMS and ERMS subtypes, lets actually take a look at them and see if we can make some informative plots. The first plot we’ll make is a volcano plot using the EnhancedVolcano package. This package automatically colors the points by cutoffs for both significance and fold change and labels many of the significant genes (subject to spacing). EnhancedVolcano has many, many options, which is a good thing if you don’t like all of its default settings. Even better, it outputs a ggplot2 object, so if we want to customize the plot further, we can use the same ggplot2 commands we have used before.

EnhancedVolcano::EnhancedVolcano(deseq_results,
                x = 'log2FoldChange', # fold change statistic to plot
                y = 'pvalue', # significance values
                lab = deseq_results$gene_symbol, # labels for points
                pCutoff = 1e-05, # p value cutoff (default)
                FCcutoff = 1, # fold change cutoff (default)
                title = NULL, # no title
                subtitle = NULL, # or subtitle
                caption = NULL, # or caption
                drawConnectors = TRUE, # add some fun arrows
                labSize = 3  # smaller labels
                ) +
  # change the overall theme
  theme_bw() +
  # move the legend to the bottom
  theme(legend.position = "bottom")
Warning: ggrepel: 543 unlabeled data points (too many overlaps). Consider
increasing max.overlaps

We can also return back to the SCE object that we used to create our pseudo-bulked SCE and look at gene expression of some of the significant genes. We can create UMAP plots as we did previously, but instead of labeling each cell with metadata, we can color cells by a specified gene’s expression levels. We will also use some of the ggplot2 skills we picked up earlier, like facet_grid() to plot cells from different RMS subtypes separately. This can help us validate the DESeq2 results so that we can visualize gene expression changes across our cell type of interest on a single-cell level.

# filter to just myoblast cells and remove any NA's before plotting
myoblast_combined_sce <- rms_sce[, which(rms_sce$celltype_broad == "Tumor_Myoblast")]

# plot PTPRT (ENSG00000196090) expression in ARMS vs. ERMS
scater::plotReducedDim(myoblast_combined_sce,
                       dimred = "fastmnn_UMAP",
                       color_by = "ENSG00000196090", #PTPRT
                       point_size= 0.5,
                       point_alpha = 0.4,
                       other_fields = "diagnosis_group") +
  facet_grid(cols = vars(diagnosis_group))

In the above plot we only plotted the tumor myoblast cells that we used in our DE analysis. However, we might be interested to see the expression of genes that are differentially expressed in other cell types present in our samples.

# let's compare gene expression across some other cell types
# look at all tumor cells and pick one normal cell type
celltypes <- c("Tumor_Myoblast", 
               "Tumor_Mesoderm", 
               "Tumor_Myocyte", 
               "Vascular Endothelium")

# subset to just celltypes that we are interested in
tumor_sce <- rms_sce[, which(rms_sce$celltype_broad %in% celltypes)]

Next we will look at a few DE genes that we identified, one up regulated gene and one down regulated gene, and compare their expression in myoblasts to other cell types in ARMS and ERMS samples. We will use the scater::plotExpression() function to create a violin plot with RMS subtype on the x-axis and gene expression on the y-axis. We can continue using facet_grid() to show separate panels for each cell type. Because we want to show multiple genes here, we are going to add an additional option to facet_grid() to include multiple rows in our plot grid, one for each gene of interest. One neat trick of the scater::plotExpression() function is that it actually creates a Feature column which corresponds to the features (in this case genes) being used in plotting. We can then directly reference that Feature column when plotting, instead of using the other_fields option we used previously.

# pick a couple genes to look at 
genes_to_plot <- c("ENSG00000196090", #PTPRT
                   "ENSG00000148935") #GAS2

# create a violin plot 
scater::plotExpression(tumor_sce,
                       # a vector of genes to plot
                       features = genes_to_plot, 
                       x = "diagnosis_group", 
                       color_by = "diagnosis_group",
                       other_fields = "celltype_broad",
                       point_size = 0.1) +
  # each celltype is its own column
  facet_grid(cols = vars(celltype_broad),
             # each feature (gene) is its own row
             rows = vars(Feature)) + 
  # change the font size of the facet labels
  theme(strip.text = element_text(size = 7)) + 
  guides(color = guide_legend(
    title = "Subtype", # update the legend title
    # change the size of the legend colors
    override.aes = list(size = 3, alpha = 1))
    )

How do the expression of these genes change across cell types and RMS subtypes?

Go ahead and explore some genes on your own! Feel free to plot any of the genes that are identified as significant, found in the DE results table, or your favorite gene. Remember, you need to use the Ensembl gene identifier to refer to each gene.

# now do some exploration of other genes on your own! 
LS0tCnRpdGxlOiAiRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgZm9yIHNjUk5BLXNlcSBkYXRhIgphdXRob3I6ICJEYXRhIExhYiBmb3IgQUxTRiIKZGF0ZTogMjAyMwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCiMjIE9iamVjdGl2ZXMgCgpUaGlzIG5vdGVib29rIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvOgoKLSBVc2UgcHNldWRvLWJ1bGtpbmcgdG8gcHJlcGFyZSBzY1JOQS1zZXEgbGlicmFyaWVzIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgotIFBlcmZvcm0gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gd2l0aCB0aGUgYERFU2VxMmAgcGFja2FnZQotIFVzZSBgZ2dwbG90MmAgYW5kIGBFbmhhbmNlZFZvbGNhbm9gIHRvIHZpc3VhbGl6ZSBnZW5lIGV4cHJlc3Npb24gY2hhbmdlcyBhY3Jvc3MgY2VsbCB0eXBlcyBhbmQgc2FtcGxlcwoKLS0tCgpKdXN0IGxpa2UgYnVsayBSTkEtc2VxLCBpdCBpcyBsaWtlbHkgdGhhdCBvbmUgb2YgdGhlIGdvYWxzIHdoZW4gcGVyZm9ybWluZyBzY1JOQS1zZXEgd2lsbCBiZSB0byBjb21wYXJlIHRoZSBnZW5lIGV4cHJlc3Npb24gb2YgbXVsdGlwbGUgc2FtcGxlcyB0byBlYWNoIG90aGVyLgpVbmxpa2UgYnVsayBSTkEtc2VxIGFuYWx5c2lzLCBzY1JOQS1zZXEgYW5hbHlzaXMgYWxsb3dzIHVzIHRvIGlkZW50aWZ5IGFuZCBhbm5vdGF0ZSBjZWxsIHR5cGVzIG9yIHN1YnBvcHVsYXRpb25zIG9mIGNlbGxzIHByZXNlbnQgaW4gZWFjaCBvZiBvdXIgc2FtcGxlcy4KVGhpcyBtZWFucyB0aGF0IHdlIGNhbiBhY2NvdW50IGZvciBkaWZmZXJlbmNlcyBpbiBjZWxsIHR5cGUgY29tcG9zaXRpb24gYWNyb3NzIHNhbXBsZXMgYW5kIHNwZWNpZmljYWxseSBmb2N1cyBvbiBjZWxsIHR5cGVzIG9yIHBvcHVsYXRpb25zIG9mIGludGVyZXN0IHdoZW4gcGVyZm9ybWluZyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiAoREUpIGFuYWx5c2lzLgpJbiB0aGlzIG5vdGVib29rLCB3ZSB3aWxsIHdvcmsgd2l0aCBtdWx0aXBsZSBzYW1wbGVzIHRvIGlkZW50aWZ5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBhY3Jvc3MgY2VsbCB0eXBlcyBvZiBpbnRlcmVzdCB1c2luZyB0aGUgW2BERVNlcTJgXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvMy4xNi9iaW9jL2h0bWwvREVTZXEyLmh0bWwpIHBhY2thZ2UuIAoKIVtTaW5nbGUtY2VsbCByb2FkbWFwOiBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbl0oZGlhZ3JhbXMvcm9hZG1hcF9kaWZmZXJlbnRpYWxfZXhwcmVzc2lvbi5wbmcpCgpXZSB3aWxsIGNvbnRpbnVlIHdvcmtpbmcgd2l0aCBzYW1wbGVzIGZyb20gdGhlIFtgU0NQQ1AwMDAwMDVgIHByb2plY3RdKGh0dHBzOi8vc2NwY2EuYWxleHNsZW1vbmFkZS5vcmcvcHJvamVjdHMvU0NQQ1AwMDAwMDUpLCBhbiBpbnZlc3RpZ2F0aW9uIG9mIHBlZGlhdHJpYyBzb2xpZCB0dW1vcnMgbGVkIGJ5IHRoZSBEeWVyIGFuZCBDaGVuIGxhYnMgYXQgU3QuIEp1ZGUgQ2hpbGRyZW4ncyBSZXNlYXJjaCBIb3NwaXRhbC4KVGhpcyBwYXJ0aWN1bGFyIGRhdGFzZXQgY29udGFpbnMgMTAgZGlmZmVyZW50IHNhbXBsZXMgdGhhdCBoYXZlIGJlZW4gaW50ZWdyYXRlZCB1c2luZyBgZmFzdE1OTmAsIGZvbGxvd2luZyB0aGUgc2FtZSBwcm9jZWR1cmUgd2Ugb3V0bGluZWQgaW4gYDAyLWRhdGFzZXRfaW50ZWdyYXRpb24uUm1kYC4KVGhlc2UgMTAgc2FtcGxlcyByZXByZXNlbnQgdHdvIGRpZmZlcmVudCB0eXBlcyBvZiByaGFiZG9teW9zYXJjb21hIChSTVMpOiBlbWJyeW9uYWwgcmhhYmRvbXlvc2FyY29tYSAoRVJNUykgYW5kIGFsdmVvbGFyIHJoYWJkb215b3NhcmNvbWEgKEFSTVMpLgpUaGVzZSB0d28gc3VidHlwZXMgYXJlIGRpc3Rpbmd1aXNoZWQgYnkgdGhlIHByZXNlbmNlIG9mIHRoZSBgUEFYMy9QQVg3LUZPWE8xYCBmdXNpb24gZ2VuZSwgd2hpY2ggaXMgcHJlc2VudCBvbmx5IGluIEFSTVMgcGF0aWVudHMuCkFkZGl0aW9uYWxseSwgY2VsbHMgZm91bmQgaW4gQVJNUyB0dW1vcnMgdGVuZCB0byBoYXZlIGFuIGluY3JlYXNlZCBtdXRhdGlvbmFsIGJ1cmRlbiB3aXRoIGNlbGxzIGluIGEgbW9yZSBkaWZmZXJlbnRpYXRlZCBzdGF0ZSBjb21wYXJlZCB0byBFUk1TIHR1bW9yIGNlbGxzIChbU2hlcm4gX2V0IGFsLl8gMjAxNF0oaHR0cHM6Ly9kb2kub3JnLzEwLjExNTgvMjE1OS04MjkwLkNELTEzLTA2MzkpOyBbU3Rld2FydCBfZXQgYWwuXyAyMDE4XShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmNjZWxsLjIwMTguMDcuMDEyKSkuClJNUyB0dW1vcnMsIHJlZ2FyZGxlc3Mgb2Ygc3VidHlwZSwgYXJlIG1hZGUgdXAgb2YgY2VsbHMgdHlwaWNhbGx5IGFzc29jaWF0ZWQgd2l0aCBkZXZlbG9wbWVudCBvZiBza2VsZXRhbCBtdXNjbGU6IG1lc29kZXJtLCBteW9ibGFzdHMsIGFuZCBteW9jeXRlcyAoW1NlYmlyZSBhbmQgTWFsb25lIDIwMDNdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTM2L2pjcC41Ni42LjQxMikpLgpbUGF0ZWwgX2V0IGFsLl8gKDIwMjIpXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmRldmNlbC4yMDIyLjA0LjAwMykgdGVzdGVkIHRoZSBoeXBvdGhlc2lzIHRoYXQgY2VsbCB0eXBlcyBoYXZlIGRpc3RpbmN0IGdlbmUgZXhwcmVzc2lvbiBwYXR0ZXJucyBpbiBBUk1TIHZzLiBFUk1TIHNhbXBsZXMuCkhlcmUgd2Ugd2lsbCBsb29rIGF0IGEgc3Vic2V0IG9mIHRoZSBzYW1wbGVzIHRoZXkgc2VxdWVuY2VkIGFuZCBpZGVudGlmeSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgaW4gdHVtb3IgY2VsbHMgYmV0d2VlbiBBUk1TIGFuZCBFUk1TIHNhbXBsZXMuCgojIyBTZXQgdXAKCmBgYHtyIHNldHVwLCBtZXNzYWdlPUZBTFNFfQojIHNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMjAyMikKCiMgbG9hZCBsaWJyYXJpZXMKbGlicmFyeShnZ3Bsb3QyKSAjIHBsb3R0aW5nIGZ1bmN0aW9ucwpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KSAKCiMgcGFja2FnZSB1c2VkIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwpsaWJyYXJ5KERFU2VxMikKYGBgCgojIyMgRGlyZWN0b3JpZXMgYW5kIGZpbGVzCgpXZSB3aWxsIHN0YXJ0IGJ5IHJlYWRpbmcgaW4gYSBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIChTQ0UpIG9iamVjdCB0aGF0IGNvbnRhaW5zIGJvdGggdGhlIHVuY29ycmVjdGVkIChtZXJnZWQgYnV0IG5vdCBpbnRlZ3JhdGVkKSBhbmQgY29ycmVjdGVkIChpbnRlZ3JhdGVkKSBnZW5lIGV4cHJlc3Npb24gZGF0YSBmb3IgYWxsIDEwIHNhbXBsZXMuCgpQcmlvciB0byBpbnRlZ3JhdGlvbiwgYWxsIDEwIHNhbXBsZXMgd2VudCB0aHJvdWdoIHRoZSBzYW1lIGZpbHRlcmluZywgbm9ybWFsaXphdGlvbiwgYW5kIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbi4KVGhlc2UgMTAgc2FtcGxlcyB3ZXJlIHRoZW4gbWVyZ2VkIGludG8gb25lIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0IGZvbGxvd2luZyB0aGUgc2FtZSBzdGVwcyBvdXRsaW5lZCBpbiBgMDMtZGF0YXNldF9pbnRlZ3JhdGlvbi5SbWRgLgpUaGUgbWVyZ2VkIG9iamVjdCB3YXMgdGhlbiBpbnRlZ3JhdGVkIHdpdGggYGZhc3RNTk5gIHRvIG9idGFpbiBhIGNvcnJlY3RlZCBnZW5lIGV4cHJlc3Npb24gYXNzYXkgYW5kIGNvcnJlY3RlZCByZWR1Y2VkIGRpbWVuc2lvbmFsaXR5IHJlc3VsdHMuClRoZSBmaW5hbCBTQ0Ugb2JqZWN0IHdhcyBzdG9yZWQgaW4gYGRhdGEvcm1zL2ludGVncmF0ZWQvcm1zX2FsbF9zY2UucmRzYC4KCldlIGFsc28gaGF2ZSBwcm92aWRlZCBhIG1ldGFkYXRhIGZpbGUsIGBkYXRhL3Jtcy9hbm5vdGF0aW9ucy9ybXNfc2FtcGxlX21ldGFkYXRhLnRzdmAsIHRoYXQgY29udGFpbnMgaW5mb3JtYXRpb24gZnJvbSBlYWNoIHNhbXBsZSwgc3VjaCBhcyBkaWFnbm9zaXMsIHNleCwgYWdlLCBldGMuCkVhY2ggcm93IGluIHRoaXMgZmlsZSBjb3JyZXNwb25kcyB0byBhIHNhbXBsZSBmb3VuZCBpbiB0aGUgaW50ZWdyYXRlZCBTQ0Ugb2JqZWN0LgoKVG8gYmVnaW4sIGxldCdzIHNldCB1cCBvdXIgZGlyZWN0b3JpZXMgYW5kIGZpbGVzOgoKYGBge3IgZmlsZXBhdGhzfQojIHNldCB1cCBmaWxlIHBhdGhzIAojIGRhdGEgZGlyZWN0b3J5IGZvciBSTVMgZGF0YQpkYXRhX2RpciA8LSBmaWxlLnBhdGgoImRhdGEiLCAicm1zIikKCiMgaW50ZWdyYXRlZCBmaWxlIGNvbnRhaW5pbmcgc2FtcGxlcyB0byB1c2UgZm9yIERFIGFuYWx5c2lzCmludGVncmF0ZWRfc2NlX2ZpbGUgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImludGVncmF0ZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInJtc19hbGxfc2NlLnJkcyIpCgojIHNhbXBsZSBtZXRhZGF0YSB0byBzZXQgdXAgREUgYW5hbHlzaXMKc2FtcGxlX21ldGFkYXRhX2ZpbGUgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbm5vdGF0aW9ucyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInJtc19zYW1wbGVfbWV0YWRhdGEudHN2IikKCiMgZGlyZWN0b3J5IHRvIHN0b3JlIG91dHB1dApkZXNlcV9kaXIgPC0gZmlsZS5wYXRoKCJhbmFseXNpcyIsICJybXMiLCAiZGVzZXEiKQpmczo6ZGlyX2NyZWF0ZShkZXNlcV9kaXIpCgojIHJlc3VsdHMgZmlsZSB0byBvdXRwdXQgZnJvbSBERSBhbmFseXNpcwpkZXNlcV9vdXRwdXRfZmlsZSA8LSBmaWxlLnBhdGgoZGVzZXFfZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJybXNfbXlvYmxhc3RfZGVzZXFfcmVzdWx0cy50c3YiKQoKIyBvdXRwdXQgaW50ZWdyYXRlZCBzY2Ugb2JqZWN0Cm91dHB1dF9zY2VfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJpbnRlZ3JhdGVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInJtc19zdWJzZXRfc2NlLnJkcyIpCmBgYAoKV2UgY2FuIGdvIGFoZWFkIGFuZCByZWFkIGluIHRoZSBTQ0Ugb2JqZWN0IGFuZCB0aGUgbWV0YWRhdGEgZmlsZS4KCmBgYHtyIHJlYWQgZmlsZXMsIGxpdmU9VFJVRX0KIyByZWFkIGluIHRoZSBTQ0Ugb2JqZWN0IHRoYXQgaGFzIGFscmVhZHkgYmVlbiBpbnRlZ3JhdGVkCmludGVncmF0ZWRfc2NlIDwtIHJlYWRyOjpyZWFkX3JkcyhpbnRlZ3JhdGVkX3NjZV9maWxlKQoKIyByZWFkIGluIHNhbXBsZSBtZXRhZGF0YSBmaWxlIApzYW1wbGVfbWV0YWRhdGEgPC0gcmVhZHI6OnJlYWRfdHN2KHNhbXBsZV9tZXRhZGF0YV9maWxlKQpgYGAKCiMjIERhdGFzZXQgZXhwbG9yYXRpb24KCkJlZm9yZSB3ZSBkaXZlIGludG8gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24sIGxldCdzIGV4cGxvcmUgb3VyIGludGVncmF0ZWQgU0NFIG9iamVjdCBhbmQgdGhlIGRhdGFzZXQgYSBsaXR0bGUgbW9yZS4KCldlJ2xsIHN0YXJ0IGJ5IGxvb2tpbmcgYXQgd2hhdCdzIGluc2lkZSB0aGUgb2JqZWN0LgpIZXJlIHdlIHNob3VsZCBoYXZlIGJvdGggdGhlIG9yaWdpbmFsICh1bmNvcnJlY3RlZCkgZGF0YSBhbmQgdGhlIGludGVncmF0ZWQgKGNvcnJlY3RlZCkgZGF0YSBmb3IgYm90aCB0aGUgZ2VuZSBleHByZXNzaW9uIGFuZCB0aGUgcmVkdWNlZCBkaW1lbnNpb25hbGl0eSByZXN1bHRzLgpIb3cgYXJlIHRob3NlIHN0b3JlZCBpbiBvdXIgb2JqZWN0PwoKYGBge3IgcHJpbnQgc2NlLCBsaXZlPVRSVUV9CiMgcHJpbnQgb3V0IGVudGlyZSBvYmplY3QKaW50ZWdyYXRlZF9zY2UKYGBgCgoKYGBge3IgcHJpbnQgYXNzYXkgbmFtZXMsIGxpdmU9VFJVRX0KIyBsb29rIGF0IHRoZSBhc3NheSBuYW1lcyBpbiBvdXIgb2JqZWN0CmFzc2F5TmFtZXMoaW50ZWdyYXRlZF9zY2UpCmBgYAoKV2hlbiB3ZSBsb29rIGF0IHRoZSBhc3NheSBuYW1lcyB3ZSBzaG91bGQgc2VlIHRoYXQgdGhlcmUgYXJlIDMgbWF0cmljZXMsIGBjb3VudHNgLCBgbG9nY291bnRzYCwgYW5kIGBmYXN0bW5uX2NvcnJlY3RlZGAuIApUaGUgYGNvdW50c2AgYW5kIGBsb2djb3VudHNgIGFzc2F5cyBjb3JyZXNwb25kIHRvIHRoZSB1bmNvcnJlY3RlZCBnZW5lIGV4cHJlc3Npb24gZGF0YSB0aGF0IGhhcyBiZWVuIG1lcmdlZCBidXQgTk9UIGludGVncmF0ZWQuClRoZSBgZmFzdG1ubl9jb3JyZWN0ZWRgIGRhdGEgY29udGFpbnMgdGhlIGNvcnJlY3RlZCBnZW5lIGV4cHJlc3Npb24gZGF0YSBvYnRhaW5lZCBmcm9tIGludGVncmF0aW9uLiAKRm9yIHRoaXMgZXhlcmNpc2Ugd2Ugd2lsbCBub3QgYmUgdXNpbmcgdGhlIGBmYXN0bW5uX2NvcnJlY3RlZGAgZGF0YSAobW9yZSBvbiB3aHkgbm90IG9uY2Ugd2UgZ2V0IHRvIHNldHRpbmcgdXAgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uKSwgYnV0IHdlIG5lZWQgdG8gYmUgYXdhcmUgdGhhdCBpdCBpcyBwcmVzZW50IGFuZCBiZSBhYmxlIHRvIGRpc3Rpbmd1aXNoIGl0IGZyb20gb3VyIHVuY29ycmVjdGVkIGRhdGEuIAoKCmBgYHtyIHByaW50IHJlZHVjZWREaW0gbmFtZXMsIGxpdmU9VFJVRX0KIyBsb29rIGF0IHRoZSBuYW1lcyBvZiB0aGUgZGltZW5zaW9uIHJlZHVjdGlvbnMKcmVkdWNlZERpbU5hbWVzKGludGVncmF0ZWRfc2NlKQpgYGAKCkluIHRoZSBgcmVkdWNlZERpbWAgc2xvdHMgeW91IHNob3VsZCBzZWUgYFBDQWAgYW5kIGBVTUFQYCwgd2hpY2ggd2VyZSBib3RoIGNhbGN1bGF0ZWQgZnJvbSB0aGUgY29tYmluZWQgZGF0YSBfYmVmb3JlXyBpbnRlZ3JhdGlvbi4KWW91IHNob3VsZCBhbHNvIHNlZSBgZmFzdG1ubl9QQ0FgIGFuZCBgZmFzdG1ubl9VTUFQYCByZWR1Y2VkIGRpbWVuc2lvbnMsIHdoaWNoIGNvcnJlc3BvbmQgdG8gdGhlIGludGVncmF0ZWQgcmVzdWx0cy4KCiMjIyBDZWxsIHR5cGUgYW5ub3RhdGlvbnMKCkp1c3QgbGlrZSBpbiB0aGUgaW50ZWdyYXRpb24gbm90ZWJvb2ssIHRoaXMgZGF0YXNldCBhbHNvIGNvbnRhaW5zIHRoZSBjZWxsIHR5cGUgYW5ub3RhdGlvbnMgZm91bmQgaW4gdGhlIGBjZWxsdHlwZV9maW5lYCBhbmQgYGNlbGx0eXBlX2Jyb2FkYCBjb2x1bW5zIG9mIHRoZSBgY29sRGF0YWAuClRoZXNlIGNlbGwgdHlwZXMgd2VyZSBvcmlnaW5hbGx5IGFzc2lnbmVkIGluIFtQYXRlbCBfZXQgYWwuXyAoMjAyMildKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouZGV2Y2VsLjIwMjIuMDQuMDAzKS4KV2Ugd2lsbCB1c2UgdGhlc2UgY2VsbCB0eXBlIGFzc2lnbm1lbnRzIHRvIHNldCB1cCB0aGUgREUgYW5hbHlzaXMgYmVsb3csIGJ1dCB0aGV5IGFyZSBub3QgcmVxdWlyZWQgZm9yIERFIGFuYWx5c2lzIGl0c2VsZi4KSXQncyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IERFIGFuYWx5c2lzIGNhbiBiZSBhcHBsaWVkIHRvIGFueSBzdWJwb3B1bGF0aW9uIG9mIGludGVyZXN0IHRoYXQgaXMgc2hhcmVkIGFjcm9zcyBzYW1wbGVzIGJlc2lkZXMganVzdCBjZWxsIHR5cGVzLgoKQmVjYXVzZSB3ZSBhcmUgZ29pbmcgdG8gYmUgZG9pbmcgREUgYW5hbHlzaXMgYmV0d2VlbiBBUk1TIGFuZCBFUk1TIHNhbXBsZXMsIGxldCdzIHN0YXJ0IGJ5IGxhYmVsaW5nIGNlbGxzIGluIHRoZSBpbnRlZ3JhdGVkIGRhdGFzZXQgYmFzZWQgb24gdGhlaXIgUk1TIHN1YnR5cGUuClRvIGRvIHRoaXMgd2Ugd2lsbCBuZWVkIHRvIGJlIHN1cmUgdGhhdCB0aGUgc3VidHlwZSBpcyBwcmVzZW50IGluIHRoZSBgY29sRGF0YWAgb2YgdGhlIGludGVncmF0ZWQgU0NFIG9iamVjdC4KSWYgaXQncyBub3QgdGhlcmUsIHdlIG5lZWQgdG8gYWRkIGl0IGluLgoKYGBge3IgY29sZGF0YSBoZWFkLCBsaXZlPVRSVUV9CiMgbG9vayBhdCB0aGUgaGVhZCBvZiB0aGUgY29sZGF0YQpoZWFkKGNvbERhdGEoaW50ZWdyYXRlZF9zY2UpKSB8PgogIGFzLmRhdGEuZnJhbWUoKQpgYGAKClVoIG9oLCBpdCBsb29rcyBsaWtlIHRoZSBSTVMgc3VidHlwZSBpcyBub3QgZm91bmQgaW4gdGhlIFNDRSBvYmplY3QuCkZvcnR1bmF0ZWx5IHdlIGFsc28gaGF2ZSB0aGUgc2FtcGxlIG1ldGFkYXRhIHRhYmxlIHRoYXQgd2UgcmVhZCBpbiBlYXJsaWVyLCB3aGljaCBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dCBlYWNoIG9mIHRoZSBzYW1wbGVzIHByZXNlbnQgaW4gdGhlIGRhdGFzZXQuCgpgYGB7ciBzYW1wbGUgbWV0YWRhdGEsIGxpdmU9VFJVRX0KIyBwcmludCBvdXQgc2FtcGxlIG1ldGFkYXRhCmhlYWQoc2FtcGxlX21ldGFkYXRhKQpgYGAKCkxvb2tpbmcgYXQgdGhpcyBzYW1wbGUgdGFibGUsIHdlIHNlZSBhIGNvbHVtbiBuYW1lZCBgc3ViZGlhZ25vc2lzYCB3aGljaCBhY2NvdW50cyBmb3IgdGhlIFJNUyBzdWJ0eXBlLCBBUk1TIG9yIEVSTVMuCldlIGFsc28gc2VlIG90aGVyIGNvbHVtbnMgdGhhdCBjb250YWluIGluZm9ybWF0aW9uIGFib3V0IGVhY2ggc3BlY2lmaWMgc2FtcGxlLgoKV2UgY2FuIGluY29ycG9yYXRlIHRoZSBpbmZvcm1hdGlvbiBpbiB0aGlzIHNhbXBsZSBtZXRhZGF0YSB0YWJsZSBpbnRvIHRoZSBgY29sRGF0YWAgb2YgdGhlIGludGVncmF0ZWQgU0NFIG9iamVjdC4KVGhpcyB3aWxsIGFsbG93IHVzIHRvIG1hdGNoIGVhY2ggb2YgdGhlIHNhbXBsZXMgaW4gdGhlIFNDRSBvYmplY3Qgd2l0aCB0aGUgUk1TIHN1YnR5cGUgYW5kIGFsc28gYWxsb3cgdXMgdG8gdXNlIGFueSBvZiB0aGUgY29sdW1ucyBpbiB0aGUgc2FtcGxlIG1ldGFkYXRhIGZvciBwbG90dGluZy4KCmBgYHtyIG1vZGlmeSBjb2xkYXRhfQojIGFkZCBzYW1wbGUgbWV0YWRhdGEgdG8gY29sRGF0YSBmcm9tIHRoZSBpbnRlZ3JhdGVkIFNDRSBvYmplY3QKY29sZGF0YV9kZiA8LSBjb2xEYXRhKGludGVncmF0ZWRfc2NlKSB8PgogICMgY29udmVydCBmcm9tIERhdGFGcmFtZSB0byBkYXRhLmZyYW1lCiAgYXMuZGF0YS5mcmFtZSgpIHw+CiAgIyBtZXJnZSB3aXRoIHNhbXBsZSBtZXRhZGF0YSAKICBkcGx5cjo6bGVmdF9qb2luKHNhbXBsZV9tZXRhZGF0YSwgYnkgPSBjKCJzYW1wbGUiID0gImxpYnJhcnlfaWQiKSkgfD4KICAjIGNyZWF0ZSBuZXcgY29sdW1ucwogICMgY2VsbF9pZCBpcyBhIGNvbWJpbmF0aW9uIG9mIGJhcmNvZGUgYW5kIHNhbXBsZQogIGRwbHlyOjptdXRhdGUoY2VsbF9pZCA9IGdsdWU6OmdsdWUoIntzYW1wbGV9LXtiYXJjb2RlfSIpLAogICAgICAgICAgICAgICAgIyBzaW1wbGlmeSBzdWJkaWFnbm9zaXMKICAgICAgICAgICAgICAgIGRpYWdub3Npc19ncm91cCA9IGZvcmNhdHM6OmZjdF9yZWNvZGUoCiAgICAgICAgICAgICAgICAgIHN1YmRpYWdub3NpcywKICAgICAgICAgICAgICAgICAgIkFSTVMiID0gIkFsdmVvbGFyIHJoYWJkb215b3NhcmNvbWEiLAogICAgICAgICAgICAgICAgICAiRVJNUyIgPSAiRW1icnlvbmFsIHJoYWJkb215b3NhcmNvbWEiCiAgICAgICAgICAgICAgICApKQoKIyBhZGQgbW9kaWZpZWQgZGF0YSBmcmFtZSBiYWNrIHRvIFNDRSBhcyBEYXRhRnJhbWUKY29sRGF0YShpbnRlZ3JhdGVkX3NjZSkgPC0gRGF0YUZyYW1lKGNvbGRhdGFfZGYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gY29sZGF0YV9kZiRjZWxsX2lkKQpgYGAKCk5vdyB3aGVuIHdlIGxvb2sgYXQgdGhlIGBjb2xEYXRhYCBvZiB0aGUgU0NFIG9iamVjdCB3ZSBzaG91bGQgc2VlIG5ldyBjb2x1bW5zLCBpbmNsdWRpbmcgdGhlIGBkaWFnbm9zaXNfZ3JvdXBgIGNvbHVtbiB3aGljaCBpbmRpY2F0ZXMgaWYgZWFjaCBjZWxsIGNvbWVzIGZyb20gYW4gRVJNUyBvciBBUk1TIHNhbXBsZS4KCmBgYHtyIHByaW50IG5ldyBjb2xkYXRhLCBsaXZlPVRSVUV9CiMgdGFrZSBhIGxvb2sgYXQgdGhlIG5ldyBtb2RpZmllZCBjb2xEYXRhCmhlYWQoY29sRGF0YShpbnRlZ3JhdGVkX3NjZSkpIHw+CiAgYXMuZGF0YS5mcmFtZSgpCmBgYAoKIyMjIFBsb3R0aW5nIHdpdGggYW5ub3RhdGlvbnMKCldlIGNhbiBub3cgdXNlIHRoYXQgY29sdW1uIHRvIGxhYmVsIGFueSBVTUFQIHBsb3RzIChvciBvdGhlciBwbG90IHR5cGVzKSB0aGF0IHdlIG1ha2UuCkluIHRoZSBjaHVuayBiZWxvdyB3ZSB3aWxsIHN0YXJ0IGJ5IHRha2luZyBhIGxvb2sgYXQgb3VyIGludGVncmF0aW9uIHJlc3VsdHMgYW5kIGNvbG9yIG91ciBjZWxscyBieSBSTVMgc3VidHlwZS4KCioqUmVtaW5kZXI6IFlvdSBzaG91bGQgYWx3YXlzIHVzZSB0aGUgYmF0Y2gtY29ycmVjdGVkIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiByZXN1bHRzIGZvciB2aXN1YWxpemluZyBkYXRhc2V0cyBjb250YWluaW5nIG11bHRpcGxlIGxpYnJhcmllcyBvciBzYW1wbGVzLioqCgpgYGB7ciBkaWFnbm9zaXMgZ3JvdXAgVU1BUCwgbGl2ZT1UUlVFfQojIFVNQVAgb2YgYWxsIHNhbXBsZXMsIHNlcGFyYXRpbmcgYnkgZGlhZ25vc2lzIGdyb3VwCnNjYXRlcjo6cGxvdFJlZHVjZWREaW0oaW50ZWdyYXRlZF9zY2UsCiAgICAgICAgICAgICAgICAgICAgICAgZGltcmVkID0gImZhc3Rtbm5fVU1BUCIsCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3JfYnkgPSAiZGlhZ25vc2lzX2dyb3VwIiwKICAgICAgICAgICAgICAgICAgICAgICBwb2ludF9zaXplPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRfYWxwaGEgPSAwLjIpIApgYGAKCkludGVyZXN0aW5nbHksIGl0IGxvb2tzIGxpa2Ugc2FtcGxlcyBmcm9tIHRoZSBBUk1TIGFuZCBFUk1TIHN1YnR5cGVzIHRlbmQgdG8gZ3JvdXAgd2l0aCBzYW1wbGVzIG9mIHRoZSBzYW1lIHN1YnR5cGUgcmF0aGVyIHRoYW4gYWxsIHRvZ2V0aGVyLiAKCkluIHRoZSBpbnRlZ3JhdGlvbiBub3RlYm9vayB3ZSBhbHNvIGxvb2tlZCBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNlbGwgdHlwZXMgYWZ0ZXIgaW50ZWdyYXRpb24uCkluIHRoYXQgbm90ZWJvb2ssIHdlIGRpc2N1c3NlZCB0aGF0IGNlbGxzIG9mIHRoZSBzYW1lIGNlbGwgdHlwZSBhcmUgZXhwZWN0ZWQgdG8gaW50ZWdyYXRlIHdpdGggb3RoZXIgY2VsbHMgb2YgdGhlIHNhbWUgdHlwZS4KSXMgdGhhdCB0aGUgY2FzZSB3aXRoIHRoaXMgZGF0YXNldD8KCkEgd29yZCBvZiBjYXV0aW9uIHdoZW4gZXZhbHVhdGluZyB0aGUgY2VsbCB0eXBlIHJlc3VsdHMgZm9yIHRoaXMgZGF0YXNldDogVGhlIGNlbGwgdHlwZXMgZm9yIHRoaXMgZGF0YXNldCB3ZXJlIGFzc2lnbmVkIGluIGEgdHdvIHN0YWdlIHByb2Nlc3MgYXMgZGVzY3JpYmVkIGluIFtQYXRlbCBfZXQgYWwuXyAoMjAyMildKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouZGV2Y2VsLjIwMjIuMDQuMDAzKS4KVGhlIGZpcnN0IHN0YWdlIGFzc2lnbmVkIGNlbGxzIGFzIHR1bW9yIG9yIG5vbi10dW1vci4KVGhlIG5leHQgc3RhZ2UgZnVydGhlciBjbGFzc2lmaWVkIHR1bW9yIGNlbGxzIGludG8gb25lIG9mIHRocmVlIHR5cGVzIG9mIHR1bW9yIGNlbGxzOiBteW9ibGFzdCwgbXlvY3l0ZSwgb3IgbWVzb2Rlcm0uClNvbWUgc2FtcGxlcyBjb3VsZCBub3QgYmUgZnVydGhlciBjbGFzc2lmaWVkLCBzbyBhbGwgb2YgdGhlaXIgdHVtb3IgY2VsbHMgYXJlIGRlbm90ZWQgYFR1bW9yYC4KVGhlIHNhbXBsZXMgd2hpY2ggY291bGQgYmUgZnVydGhlciBjbGFzc2lmaWVkIGhhdmUgYSBtaXggb2YgYFR1bW9yX01lc29kZXJtYCwgYFR1bW9yX015b2JsYXN0YCwgYW5kIGBUdW1vcl9NeW9jeXRlYC4KCmBgYHtyIGNlbGx0eXBlIFVNQVB9CiMgVU1BUCBvZiBhbGwgc2FtcGxlcyBsYWJlbGVkIGJ5IGNlbGwgdHlwZQpzY2F0ZXI6OnBsb3RSZWR1Y2VkRGltKGludGVncmF0ZWRfc2NlLAogICAgICAgICAgICAgICAgICAgICAgIGRpbXJlZCA9ICJmYXN0bW5uX1VNQVAiLAogICAgICAgICAgICAgICAgICAgICAgICMgY29sb3IgZWFjaCBwb2ludCBieSBjZWxsIHR5cGUKICAgICAgICAgICAgICAgICAgICAgICBjb2xvcl9ieSA9ICJjZWxsdHlwZV9icm9hZCIsCiAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRfc2l6ZT0gMC41LCAKICAgICAgICAgICAgICAgICAgICAgICBwb2ludF9hbHBoYSA9IDAuNCkKYGBgCgpVbmxpa2Ugd2l0aCB0aGUgcHJldmlvdXMgZGF0YXNldHMgd2UgaGF2ZSBzZWVuIHdoZXJlIGFsbCBjZWxscyBvZiB0aGUgc2FtZSBjZWxsIHR5cGUgYWx3YXlzIGdyb3VwZWQgdG9nZXRoZXIsIHRoaXMgZGF0YXNldCBzaG93cyBzb21lIHNsaWdodGx5IGRpZmZlcmVudCBwYXR0ZXJucyBhbmQgbm90IGFsbCBjZWxscyBvZiB0aGUgc2FtZSBjZWxsIHR5cGUgY2x1c3RlciB0b2dldGhlci4KT25lIHJlYXNvbiBpcyB0aGF0IHR1bW9yIGRhdGEgY2FuIGJlIGhldGVyb2dlbmVvdXMgYW5kIGV2ZXJ5IHR1bW9yIGlzIHVuaXF1ZS4KRGVwZW5kaW5nIG9uIHRoZSB0dW1vciB0eXBlIHdlIG1heSBub3QgZXhwZWN0IGV2ZXJ5IHNhbXBsZSB0byBpbnRlZ3JhdGUgcGVyZmVjdGx5IGFuZCBtb3JlIGhldGVyb2dlbmVvdXMgdHVtb3IgdHlwZXMgd2lsbCBiZSBtb3JlIGRpZmZpY3VsdCB0byBpbnRlZ3JhdGUgdG9nZXRoZXIuCkluIHRoaXMgcGFydGljdWxhciBjYXNlIHdlIGFyZSBsb29raW5nIGF0IHR3byBzdWJ0eXBlcyBvZiBSTVMgdGhhdCBoYXZlIGRpc3RpbmN0IG11dGF0aW9uIGJ1cmRlbnMgYW5kIGRpZmZlcmVudGlhdGlvbiBzdGF0ZXMsIHNvIGl0J3MgbGlrZWx5IHRoYXQgdGhvc2UgZGlmZmVyZW5jZXMgY29udHJpYnV0ZSB0byBob3cgd2VsbCB0aGV5IGludGVncmF0ZS4KClRvIGV4cGxvcmUgd2hldGhlciBjZWxscyBhcmUgZ3JvdXBpbmcgdG9nZXRoZXIgYm90aCBieSBjZWxsIHR5cGUgYW5kIGJ5IFJNUyBzdWJ0eXBlLCB3ZSBjYW4gY3JlYXRlIGEgcGxvdCB0aGF0IGluY29ycG9yYXRlcyBib3RoIHBpZWNlcyBvZiBtZXRhZGF0YS4KV2Ugd2lsbCB0YWtlIGFkdmFudGFnZSBvZiB0aGUgYGZhY2V0X2dyaWQoKWAgZnVuY3Rpb24gZnJvbSBgZ2dwbG90MmAgdG8gbG9vayBhdCB0d28gdmFyaWFibGVzIGluIHRoZSBgY29sRGF0YWAgYXQgb25jZSAtIHRoZSBjZWxsIHR5cGUgYW5kIHRoZSBzdWJkaWFnbm9zaXMuCkluIHRoZSBiZWxvdyBwbG90IHdlIHdpbGwgY29sb3Igb3VyIGNlbGxzIGJ5IGNlbGwgdHlwZSB3aGlsZSBhbHNvIHVzaW5nIGBmYWNldF9ncmlkKClgIHNvIHRoYXQgY2VsbHMgZnJvbSBkaWZmZXJlbnQgc3ViZGlhZ25vc2VzIHdpbGwgYmUgaW4gdGhlaXIgb3duIHBsb3QgcGFuZWwuCgpgYGB7ciBjZWxsdHlwZSBzdWJkaWFnbm9zaXMgVU1BUCwgbGl2ZT1UUlVFfQojIFVNQVAgb2YgYWxsIHNhbXBsZXMKIyBzZXBhcmF0aW5nIGJ5IGRpYWdub3NpcyBncm91cCBhbmQgbGFiZWxpbmcgY2VsbCB0eXBlCnNjYXRlcjo6cGxvdFJlZHVjZWREaW0oaW50ZWdyYXRlZF9zY2UsCiAgICAgICAgICAgICAgICAgICAgICAgZGltcmVkID0gImZhc3Rtbm5fVU1BUCIsCiAgICAgICAgICAgICAgICAgICAgICAgIyBjb2xvciBlYWNoIHBvaW50IGJ5IGNlbGwgdHlwZQogICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX2J5ID0gImNlbGx0eXBlX2Jyb2FkIiwKICAgICAgICAgICAgICAgICAgICAgICBwb2ludF9zaXplPSAwLjUsIAogICAgICAgICAgICAgICAgICAgICAgIHBvaW50X2FscGhhID0gMC40LAogICAgICAgICAgICAgICAgICAgICAgICMgdGVsbCBzY2F0ZXIgdG8gdXNlIGRpYWdub3Npc19ncm91cCBmb3IgcGxvdHRpbmcKICAgICAgICAgICAgICAgICAgICAgICBvdGhlcl9maWVsZHMgPSAiZGlhZ25vc2lzX2dyb3VwIikgKwogICMgaW5jbHVkZSBlYWNoIGRpYWdub3NpcyBncm91cCBhcyBpdHMgb3duIGNvbHVtbgogIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoZGlhZ25vc2lzX2dyb3VwKSkKYGBgCgpBcyBleHBlY3RlZCwgd2Ugc2VlIHRoYXQgY2VsbCB0eXBlcyBhcmUgc2VwYXJhdGVkLCBtb3N0IGxpa2VseSBkdWUgdG8gZGlmZmVyZW50IFJNUyBzdWJ0eXBlcy4KCldlIGNhbiBhbHNvIHVzZSBhIHN0YWNrZWQgYmFycGxvdCB0byBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgY2VsbCB0eXBlcyBhY3Jvc3MgZWFjaCBzYW1wbGUsIHdoaWNoIHdpbGwgcmVxdWlyZSBhIGJpdCBvZiB3cmFuZ2xpbmcgZmlyc3QuCgpgYGB7ciBjZWxsdHlwZSBiYXJwbG90fQojIGZpbHRlciBjb2xkYXRhIHRvIG9ubHkgaW5jbHVkZSB0dW1vciBjZWxscwp0dW1vcl9jZWxsc19kZiA8LSBjb2xkYXRhX2RmIHw+CiAgIyBmaW5kIHJvd3Mgd2hlcmUgdGhlIGNlbGwgdHlwZSBuYW1lIGNvbnRhaW5zIHRoZSBzdHJpbmcgIlR1bW9yIgogIGRwbHlyOjpmaWx0ZXIoc3RyaW5ncjo6c3RyX2RldGVjdChjZWxsdHlwZV9icm9hZCwgIlR1bW9yIikpCgojIGNyZWF0ZSBhIHN0YWNrZWQgYmFycGxvdApnZ3Bsb3QodHVtb3JfY2VsbHNfZGYsIGFlcyh4ID0gc2FtcGxlLCBmaWxsID0gY2VsbHR5cGVfYnJvYWQpKSArIAogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuMikgKwogICAgbGFicygKICAgICAgeCA9ICJTYW1wbGUiLAogICAgICB5ID0gIlByb3BvcnRpb24gb2YgY2VsbHMiLCAKICAgICAgZmlsbCA9ICJDZWxsIHR5cGUiCiAgICApICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41KSkrCiAgIyBmYWNldCBieSBkaWFnbm9zaXMgZ3JvdXAgCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhkaWFnbm9zaXNfZ3JvdXApLCAKICAgICAgICAgICAgICMgb25seSBzaG93IG5vbi1OQSB2YWx1ZXMgb24geC1heGlzCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZV94IiwKICAgICAgICAgICAgIHNwYWNlID0gImZyZWVfeCIpCmBgYAoKU2ltaWxhciB0byB0aGUgVU1BUCwgdGhpcyBwbG90IHNob3dzIHRoYXQgQVJNUyBhbmQgRVJNUyBzaGFyZSBhIGxvdCBvZiB0aGUgc2FtZSBjZWxsIHR5cGVzLgoKV2UgYWxzbyBzZWUgdGhhdCBvbmx5IDYgb2YgdGhlc2UgbGlicmFyaWVzIGhhdmUgdHVtb3IgY2VsbHMgdGhhdCBoYXZlIGJlZW4gZnVydGhlciBjbGFzc2lmaWVkIGludG8gbWVzb2Rlcm0sIG15b2JsYXN0LCBhbmQgbXlvY3l0ZS4gCjMgbGlicmFyaWVzIGNvbnRhaW4gY2VsbHMgdGhhdCBhcmUgb25seSBjbGFzc2lmaWVkIGFzIHR1bW9yIG9yIG5vbi10dW1vciwgYW5kIHR1bW9yIGNlbGxzIGFyZSBub3QgZnVydGhlciBjbGFzc2lmaWVkLCBhbmQgdGhlIHJlbWFpbmluZyBsaWJyYXJ5IGlzIG5vdCBldmVuIHByZXNlbnQgaW4gb3VyIHBsb3QgYmVjYXVzZSBpdCB3YXMgbm90IGFzc2lnbmVkIGFueSBjZWxsIHR5cGVzIChhbGwgYXJlIGBOQWApLgpXZSB3aWxsIGNvbnRpbnVlIG91ciBhbmFseXNpcyBvbmx5IHVzaW5nIHRoZSA2IGxpYnJhcmllcyB3aXRoIGZ1bGx5IGNsYXNzaWZpZWQgY2VsbCB0eXBlcywgcmVtb3ZpbmcgdGhlIG90aGVyIDQgYmVmb3JlIHdlIHByb2NlZWQgd2l0aCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4KCiMjIyBGaWx0ZXJpbmcgc2FtcGxlcwoKVGhlIHJlYXNvbiB3ZSB3YW50IHRvIHBhcmUgZG93biBvdXIgbGlzdCBvZiBzYW1wbGVzIHRvIGNvbnNpZGVyIGlzIHRoYXQgd2Ugd2FudCB0byBlbnN1cmUgdGhhdCB0aGUgY2VsbCB0eXBlcyAob3Igc3VicG9wdWxhdGlvbnMpIHRoYXQgd2UgYXJlIGludGVyZXN0ZWQgaW4gYXJlIHByZXNlbnQgaW4gYWxsIHNhbXBsZXMgaW5jbHVkZWQgaW4gb3VyIERFIGFuYWx5c2lzLiAKV2Ugd2FudCB0byByZW1vdmUgYW55IHNhbXBsZXMgdGhhdCBkbyBub3QgY29udGFpbiBvdXIgY2VsbCBwb3B1bGF0aW9uKHMpIG9mIGludGVyZXN0IGFzIHRoZXkgaGF2ZSBubyBjb3VudHMgdG8gY29udHJpYnV0ZSB0byB0aGUgREUgYW5hbHlzaXMuCgpgYGB7ciBzdWJzZXQgc2NlfQojIGRlZmluZSBzYW1wbGVzIHRvIGtlZXAKbGlicmFyeV9pZHMgPC0gYygKICAiU0NQQ0wwMDA0NzkiLAogICJTQ1BDTDAwMDQ4MCIsCiAgIlNDUENMMDAwNDgxIiwKICAiU0NQQ0wwMDA0ODQiLAogICJTQ1BDTDAwMDQ4OCIsCiAgIlNDUENMMDAwNDkxIgopCgojIHN1YnNldCBzY2UgdG8gb25seSBjb250YWluIHNhbXBsZXMgb2YgaW50ZXJlc3QgCnNhbXBsZXNfdG9fa2VlcCA8LSBpbnRlZ3JhdGVkX3NjZSRzYW1wbGUgJWluJSBsaWJyYXJ5X2lkcwpybXNfc2NlIDwtIGludGVncmF0ZWRfc2NlWywgc2FtcGxlc190b19rZWVwXQoKIyBwcmludCBvdXQgb3VyIG5ldyBTQ0UgCnJtc19zY2UKYGBgCgpCZWZvcmUgd2UgbW92ZSBvbiwgd2UnbGwgcmVtb3ZlIHRoZSBvcmlnaW5hbCBpbnRlZ3JhdGVkIG9iamVjdCBmcm9tIG91ciBlbnZpcm9ubWVudCB0byBzYXZlIHNvbWUgbWVtb3J5LiAKCmBgYHtyIHJlbW92ZSBzY2V9CnJtKGludGVncmF0ZWRfc2NlKQpgYGAKCldlIHdpbGwgYWxzbyBzYXZlIG91ciBuZXcgb2JqZWN0IGluIGNhc2Ugd2Ugd2FudCB0byB1c2UgaXQgZm9yIG90aGVyIGFuYWx5c2lzIGxhdGVyIG9uLgoKYGBge3Igc2F2ZSBzY2V9CiMgd3JpdGUgUkRTIGZpbGUgd2l0aCBjb21wcmVzc2lvbgpyZWFkcjo6d3JpdGVfcmRzKHJtc19zY2UsIGZpbGUgPSBvdXRwdXRfc2NlX2ZpbGUsIGNvbXByZXNzID0gImd6IikKYGBgCgpXZSBub3cgaGF2ZSBhbiB1cGRhdGVkIFNDRSBvYmplY3QgdGhhdCBjb250YWlucyA2IHNhbXBsZXMgdGhhdCB3ZXJlIG9idGFpbmVkIGZyb20gYSBtaXggb2YgQVJNUyBhbmQgRVJNUyBwYXRpZW50cy4KV2UgY2FuIHRoZW4gYXNrIHRoZSBxdWVzdGlvbiwgZG8gc3BlY2lmaWMgdHVtb3IgY2VsbCB0eXBlcyBjb250YWluIHNldHMgb2YgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGJldHdlZW4gQVJNUyBhbmQgRVJNUyBzYW1wbGVzPwoKV2Ugc2hvdWxkIG1ha2Ugc3VyZSB0aGF0IHdlIGhhdmUgZW5vdWdoIGJpb2xvZ2ljYWwgcmVwbGljYXRlcyBmcm9tIGVhY2ggZ3JvdXAgdG8gc2V0IHVwIG91ciBleHBlcmltZW50LiAKSXQgaXMgaW1wZXJhdGl2ZSB0byBjb25zaWRlciBnb29kIGV4cGVyaW1lbnRhbCBkZXNpZ24gYW5kIGVuc3VyZSB0aGF0IHdlIGhhdmUgZW5vdWdoIGJpb2xvZ2ljYWwgcmVwbGljYXRlcyAoYXQgbGVhc3QgMyBmb3IgZWFjaCBncm91cCkgd2hlbiBwZXJmb3JtaW5nIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMuCgpJZiB3ZSBsb29rIGJhY2sgYXQgb3VyIHN0YWNrZWQgYmFycGxvdCB3ZSBzZWUgdGhhdCB3ZSBwaWNrZWQgMyBBUk1TIGFuZCAzIEVSTVMgc2FtcGxlcy4KV2UgY2FuIGFsc28gc2VlIHRoYXQgdGhlIG1ham9yaXR5IG9mIGNlbGxzIGFyZSB0dW1vciBjZWxscywgaW4gcGFydGljdWxhciB0aGUgbGFyZ2VzdCBwb3B1bGF0aW9uIG9mIGNlbGxzIGFwcGVhcnMgdG8gYmUgdGhlIGBUdW1vcl9NeW9ibGFzdGAuIApGb3IgdGhpcyBleGFtcGxlIHdlIHdpbGwgZm9jdXMgb24gaWRlbnRpZnlpbmcgREUgZ2VuZXMgaW4gdGhlc2UgYFR1bW9yX015b2JsYXN0YCBjZWxscywgYnV0IHRoZSBwcmluY2lwbGVzIGFwcGxpZWQgYmVsb3cgY2FuIGJlIGFwcGxpZWQgdG8gYW55IGNlbGwgdHlwZXMgb3Igc3VicG9wdWxhdGlvbnMgb2YgaW50ZXJlc3QuCgojIyBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwoKTm93IHdlIGFyZSByZWFkeSB0byBzdGFydCBwcmVwYXJpbmcgZm9yIG91ciBERSBhbmFseXNpcywgd2hlcmUgd2Ugd2lsbCBjb21wYXJlIHRoZSBnZW5lIGV4cHJlc3Npb24gb2YgdHVtb3IgbXlvYmxhc3QgY2VsbHMgYmV0d2VlbiBBUk1TIGFuZCBFUk1TIHNhbXBsZXMuCgpUaHJvdWdob3V0IHRoZSBub3RlYm9vayB3ZSBoYXZlIGJlZW4gd29ya2luZyB3aXRoIGFuIGludGVncmF0ZWQgZGF0YXNldCB0aGF0IGNvbnRhaW5zIGNvcnJlY3RlZCBnZW5lIGV4cHJlc3Npb24gZGF0YSAoYGZhc3Rtbm5fY29ycmVjdGVkYCBhc3NheSkgYW5kIGEgY29ycmVjdGVkIFVNQVAuCkFzIGEgcmVtaW5kZXIsIHRoZSB1bmNvcnJlY3RlZCBnZW5lIGV4cHJlc3Npb24gZGF0YSwgZm91bmQgaW4gdGhlIGBjb3VudHNgIGFuZCBgbG9nY291bnRzYCBhc3NheXMsIGNvcnJlc3BvbmQgdG8gZGF0YSB0aGF0IGhhcyBiZWVuIG1lcmdlZCAodGhlIGZpcnN0IHN0ZXAgd2Ugd2Fsa2VkIHRocm91Z2ggcHJpb3IgdG8gaW50ZWdyYXRpb24pIGludG8gdGhlIHNhbWUgU0NFIGJ1dCBub3QgeWV0IGludGVncmF0ZWQuCldlIGRvIG5vdCB3YW50IHRvIHVzZSBjb3JyZWN0ZWQgZ2VuZSBleHByZXNzaW9uIHZhbHVlcyBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb247IGBERVNlcTJgIGV4cGVjdHMgdGhlIG9yaWdpbmFsIHJhdyBjb3VudHMgYXMgaW5wdXQgc28gd2Ugd2lsbCBiZSB1c2luZyBkYXRhIGZvdW5kIGluIHRoZSBgY291bnRzYCBhc3NheSBvZiB0aGUgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QuIAoKSXQgaXMgYWR2aXNlZCB0byBvbmx5IHVzZSB0aGUgY29ycmVjdGVkIHZhbHVlcyBmb3IgYW55IGFuYWx5c2VzIGJlaW5nIHBlcmZvcm1lZCBhdCB0aGUgY2VsbCBsZXZlbCwgZS5nLiwgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uLgpJbiBjb250cmFzdCwgaXQgaXMgbm90IGFkdmlzZWQgdG8gdXNlIGNvcnJlY3RlZCB2YWx1ZXMgZm9yIGFueSBhbmFseXNlcyB0aGF0IGFyZSBnZW5lLWJhc2VkLCBzdWNoIGFzIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9yIG1hcmtlciBnZW5lIGRldGVjdGlvbiwgYmVjYXVzZSB3aXRoaW4tYmF0Y2ggYW5kIGJldHdlZW4tYmF0Y2ggZ2VuZSBleHByZXNzaW9uIGRpZmZlcmVuY2VzIGFyZSBubyBsb25nZXIgcHJlc2VydmVkLgpUaGUgcmVhc29uIGZvciB0aGlzIGlzIHR3by1mb2xkIOKAkyBtYW55IG9mIHRoZSBERSBtb2RlbHMgd2lsbCBleHBlY3QgdW5jb3JyZWN0ZWQgY291bnRzIGJlY2F1c2UgdGhleSB3aWxsIGFjY291bnQgZm9yIGJldHdlZW4tc2FtcGxlIHZhcmlhdGlvbiB3aXRoaW4gdGhlIG1vZGVsLCBhbmQgd2Ugd2FudCB0byBlbnN1cmUgd2UgYXJlIHByZXNlcnZpbmcgdmFyaWF0aW9uIHRoYXQgaXMgcHJlc2VudCBzbyBhcyBub3QgdG8gYXJ0aWZpY2lhbGx5IGluZmxhdGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBwb3B1bGF0aW9ucy4KU2VlIHRoZSBbT1NDQSBjaGFwdGVyIG9uIFVzaW5nIHRoZSBjb3JyZWN0ZWQgdmFsdWVzXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xNi9PU0NBLm11bHRpc2FtcGxlL3VzaW5nLWNvcnJlY3RlZC12YWx1ZXMuaHRtbCN1c2luZy1jb3JyZWN0ZWQtdmFsdWVzKSBmb3IgbW9yZSBpbnNpZ2h0LgoKIyMjIFBzZXVkby1idWxraW5nIAoKQmVmb3JlIHdlIGNhbiBjb21wYXJlIHRoZSBnZW5lIGV4cHJlc3Npb24gcHJvZmlsZXMgb2YgbXlvYmxhc3RzIGluIEFSTVMgdnMuIEVSTVMgc2FtcGxlcywgd2Ugd2lsbCBuZWVkIHRvICJwc2V1ZG8tYnVsayIgdGhlIGdlbmUgY291bnRzLiAKUHNldWRvLWJ1bGtpbmcgY3JlYXRlcyBhIG5ldyBjb3VudHMgbWF0cml4IHRoYXQgY29udGFpbnMgdGhlIHN1bSBvZiB0aGUgY291bnRzIGZyb20gYWxsIGNlbGxzIHdpdGggYSBnaXZlbiBsYWJlbCAoZS5nLiwgY2VsbCB0eXBlKSBmb3IgZWFjaCBzYW1wbGUgKFtUdW5nIF9ldCBhbC5fIDIwMTddKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDM4L3NyZXAzOTkyMSkpLiAKSWYgd2Ugd2VyZSB0byBrZWVwIGVhY2ggY2VsbCdzIGNvdW50cyBzZXBhcmF0ZSwgdGhleSB3b3VsZCBiZSB0cmVhdGVkIGFzIHJlcGxpY2F0ZXMsIGxlYWRpbmcgdG8gaW5mbGF0ZWQgc3RhdGlzdGljcy4gCkJ5IHBzZXVkby1idWxraW5nIGZpcnN0LCB3ZSB3aWxsIG5vdyBoYXZlIG9uZSBjb3VudCBmb3IgZWFjaCBnZW5lIGZvciBlYWNoIHNhbXBsZSBhbmQgd2UgY2FuIHRha2UgYWR2YW50YWdlIG9mIHdlbGwtZXN0YWJsaXNoZWQgbWV0aG9kcyBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gd2l0aCBidWxrIFJOQS1zZXEuCgpQc2V1ZG8tYnVsa2luZyBpcyBpbXBsZW1lbnRlZCBwcmlvciB0byBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBvbiBzaW5nbGUtY2VsbCBkYXRhIGJlY2F1c2UgaXQ6IAoKLSBQcm9kdWNlcyBsYXJnZXIgYW5kIGxlc3Mgc3BhcnNlIGNvdW50cywgd2hpY2ggYWxsb3dzIHVzIHRvIHVzZSBzdGFuZGFyZCBub3JtYWxpemF0aW9uIGFuZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBtZXRob2RzIHVzZWQgYnkgYnVsayBSTkEtc2VxLiAKLSBDb2xsYXBzZXMgZ2VuZSBleHByZXNzaW9uIGNvdW50cyBieSBzYW1wbGUsIHNvIHRoYXQgc2FtcGxlcywgcmF0aGVyIHRoYW4gY2VsbHMsIHJlcHJlc2VudCByZXBsaWNhdGVzLgotIE1hc2tzIHZhcmlhbmNlIHdpdGhpbiBhIHNhbXBsZSB0byBlbXBoYXNpemUgdmFyaWFuY2UgYWNyb3NzIHNhbXBsZXMuClRoaXMgY2FuIGJlIGJvdGggZ29vZCBhbmQgYmFkISAKTWFza2luZyBpbnRyYS1zYW1wbGUgdmFyaWF0aW9uIG1lYW5zIHlvdSBtaWdodCBub3QgaWRlbnRpZnkgZ2VuZXMgd2hlcmUgYXZlcmFnZSBleHByZXNzaW9uIGRvZXNuJ3QgY2hhbmdlIGJldHdlZW4gc2FtcGxlcyBidXQgdGhlIGRlZ3JlZSBvZiBjZWxsLXRvLWNlbGwgdmFyaWF0aW9uIGRvZXMuCgpCZWZvcmUgd2UgYXBwbHkgcHNldWRvLWJ1bGtpbmcgdG8gb3VyIGRhdGFzZXQsIGxldCdzIGxvb2sgYXQgYSBzaW1wbGUgZXhhbXBsZSBvZiBob3cgcHNldWRvLWJ1bGtpbmcgd29ya3MuCldlJ2xsIHN0YXJ0IGJ5IGNyZWF0aW5nIGEgZmFrZSBtYXRyaXggb2YgY291bnRzLgoKYGBge3IgY3JlYXRlIG1hdHJpeH0KIyBjcmVhdGUgYW4gZXhhbXBsZSBjb3VudHMgbWF0cml4CmNvdW50c19tdHggPC0gbWF0cml4KAogIDE6MTIsIAogIG5jb2wgPSA0LAogIGRpbW5hbWVzID0gbGlzdChjKCJnZW5lQSIsICJnZW5lQiIsICJnZW5lQyIpLAogICAgICAgICAgICAgICAgICBjKCJBLWNlbGwxIiwgIkEtY2VsbDIiLCAiQi1jZWxsMSIsICJCLWNlbGwyIikpCikKY291bnRzX210eApgYGAKCk5leHQgd2Ugd2lsbCBjcmVhdGUgYSBwc2V1ZG8tYnVsa2VkIHZlcnNpb24gb2YgdGhpcyBtYXRyaXggd2l0aCBvbmx5IDIgY29sdW1uczogMSBmb3IgZ3JvdXAgYEFgIGFuZCAxIGZvciBncm91cCBgQmAuClRvIGRvIHRoaXMgd2Ugd2lsbCB1c2UgdGhlIGBEZWxheWVkQXJyYXk6OmNvbHN1bSgpYCBmdW5jdGlvbiwgd2hpY2ggYWxsb3dzIHVzIHRvIHN1bSB0aGUgY291bnRzIGZvciBlYWNoIHJvdyBhY3Jvc3MgZ3JvdXBzIG9mIGNvbHVtbnMuCgpgYGB7ciBwc2V1ZG9idWxrIG1hdHJpeCwgbGl2ZT1UUlVFfQojIGRlZmluZSB0aGUgZ3JvdXAgdGhhdCBlYWNoIGNvbHVtbiBiZWxvbmdzIHRvCmdyb3VwcyA8LSBjKCJBIiwgIkEiLCAiQiIsICJCIikKCiMgc3VtIGNvdW50cyBhY3Jvc3MgY2VsbHMgKGNvbHVtbnMpIGJ5IGdyb3VwIGxhYmVsCnBiX2NvdW50cyA8LSBEZWxheWVkQXJyYXk6OmNvbHN1bShjb3VudHNfbXR4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwcykKcGJfY291bnRzICAKYGBgCgpMb29raW5nIGF0IHRoaXMgb3V0cHV0LCB5b3Ugc2hvdWxkIHNlZSB0aGF0IHRoZSBvcmlnaW5hbCA0IGNvbHVtbnMgaGF2ZSBiZWVuIGNvbmRlbnNlZCB0byBvbmx5IDIgY29sdW1uczogMSBjb2x1bW4gdG8gcmVwcmVzZW50IGFsbCBjZWxscyBmcm9tIGdyb3VwIGBBYCwgYW5kIDEgY29sdW1uIHRvIHJlcHJlc2VudCBhbGwgY2VsbHMgZnJvbSBncm91cCBgQmAuCgpOb3cgdGhlIGFjdHVhbCBwc2V1ZG8tYnVsa2luZyBmb3Igb3VyIGRhdGFzZXQhIAoKV2Ugd2lsbCB1c2UgdGhlIFtgc2N1dHRsZTo6YWdncmVnYXRlQWNyb3NzQ2VsbHMoKWAgZnVuY3Rpb25dKGh0dHBzOi8vcmRyci5pby9naXRodWIvTFRMQS9zY3V0dGxlL21hbi9hZ2dyZWdhdGVBY3Jvc3NDZWxscy5odG1sKSB0byBwc2V1ZG8tYnVsayBvdXIgZGF0YXNldC4KVGhpcyBmdW5jdGlvbiB0YWtlcyBhcyBpbnB1dCBhbiBTQ0Ugb2JqZWN0IGFuZCB0aGUgZ3JvdXBpbmcgYXNzaWdubWVudHMgZm9yIGVhY2ggY2VsbC4KVGhlIG91dHB1dCB3aWxsIGJlIGFuIFNDRSBvYmplY3QgdGhhdCBjb250YWlucyBvbmx5IHRoZSBwc2V1ZG8tYnVsa2VkIGNvdW50cyBmb3IgYWxsIGdlbmVzIGFjcm9zcyBhbGwgc3BlY2lmaWVkIGdyb3VwcywgcmF0aGVyIHRoYW4gYWNyb3NzIGFsbCBjZWxscy4gCldlIGNhbiB0aGVuIHN1YnNldCB0aGlzIFNDRSB0byBqdXN0IGluY2x1ZGUgb3VyIGNlbGwgdHlwZSBvZiBpbnRlcmVzdCAodHVtb3IgbXlvYmxhc3RzKSBmb3IgaW5wdXQgdG8gdGhlIERFIGFuYWx5c2lzLiAKCldlIGNhbiBwc2V1ZG8tYnVsayB1c2luZyBhbnkgZ3JvdXBpbmcgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbi4KRm9yIHJpZ2h0IG5vdywgd2UgYXJlIGludGVyZXN0ZWQgaW4gbG9va2luZyBhdCBnZW5lIGV4cHJlc3Npb24gYWNyb3NzIGNlbGwgdHlwZXMsIHNvIHdlIHdhbnQgdG8gZ3JvdXAgdGhlIHBzZXVkby1idWxrZWQgY291bnRzIG1hdHJpeCBieSBib3RoIGNlbGwgdHlwZSBhbmQgb3JpZ2luYWwgc2FtcGxlLiAKCmBgYHtyIHBzZXVkb2J1bGsgc2NlfQojIGZpcnN0IHN1YnNldCB0aGUgY29sZGF0YSAKIyB0byBvbmx5IGhhdmUgdGhlIGNvbHVtbnMgd2UgY2FyZSBhYm91dCBmb3IgcHNldWRvLWJ1bGtpbmcgCnBiX2dyb3VwcyA8LSBjb2xEYXRhKHJtc19zY2UpWywgYygiY2VsbHR5cGVfYnJvYWQiLCAic2FtcGxlIildCgojIGNyZWF0ZSBhIG5ldyBTQ0Ugb2JqZWN0IHRoYXQgY29udGFpbnMgCiMgdGhlIHBzZXVkby1idWxrZWQgY291bnRzIGFjcm9zcyB0aGUgcHJvdmlkZWQgZ3JvdXBzIApwYl9zY2UgPC0gc2N1dHRsZTo6YWdncmVnYXRlQWNyb3NzQ2VsbHMocm1zX3NjZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZCA9IHBiX2dyb3VwcykKCiMgY29sdW1uIG5hbWVzIGFyZW4ndCBhdXRvbWF0aWNhbGx5IGFkZGVkIHRvIHRoZSBwc2V1ZG8tYnVsa2VkIHNjZSwgCiMgc28gbGV0J3MgYWRkIHRoZW0gaW4gCmNvbG5hbWVzKHBiX3NjZSkgPC0gZ2x1ZTo6Z2x1ZSgKICAie3BiX3NjZSRjZWxsdHlwZV9icm9hZH1fe3BiX3NjZSRzYW1wbGV9IgopCgpwYl9zY2UKYGBgCgpIb3cgZG9lcyB0aGUgbmV3IHBzZXVkby1idWxrZWQgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBsb29rIGRpZmZlcmVudD8gCkhvdyBtYW55IGNvbHVtbnMgZG9lcyBpdCBoYXZlPyAKCkxldCdzIHRha2UgYSBsb29rIGF0IHdoYXQgdGhlIGBjb2xEYXRhYCBsb29rcyBsaWtlIGluIHRoZSBwc2V1ZG8tYnVsa2VkIFNDRSBvYmplY3QuIAoKYGBge3IgcHNldWRvYnVsayBjb2xEYXRhLCBsaXZlPVRSVUV9CiMgbm90ZSB0aGUgbmV3IGNvbHVtbiB3aXRoIG51bWJlciBvZiBjZWxscyBwZXIgZ3JvdXAgCmhlYWQoY29sRGF0YShwYl9zY2UpKSB8PgogIGFzLmRhdGEuZnJhbWUoKQpgYGAKCllvdSBzaG91bGQgc2VlIHRoYXQgY29sdW1ucyBzdWNoIGFzIGBzdW1gLCBgZGV0ZWN0ZWRgLCBgc3Vic2V0c19taXRvX3N1bWAsIGFuZCBvdGhlciBjb2x1bW5zIHRoYXQgdHlwaWNhbGx5IGNvbnRhaW4gcGVyIGNlbGwgUUMgc3RhdGlzdGljcyBub3cgY29udGFpbiBgTkFgIHJhdGhlciB0aGFuIG51bWVyaWMgdmFsdWVzLiAKVGhpcyBpcyBiZWNhdXNlIHRoZXNlIHZhbHVlcyB3ZXJlIGluaXRpYWxseSBjYWxjdWxhdGVkIG9uIGEgcGVyIGNlbGwgbGV2ZWwgKHdlIGRpZCB0aGlzIHVzaW5nIGBzY3V0dGxlOjphZGRQZXJDZWxsUUNNZXRyaWNzKClgKSwgYnV0IHdlIG5vIGxvbmdlciBoYXZlIGEgc2luZ2xlIGNvbHVtbiBwZXIgY2VsbC4KSW5zdGVhZCwgZWFjaCBjb2x1bW4gbm93IHJlcHJlc2VudHMgYSBfZ3JvdXBfIG9mIGNlbGxzLCBpbiB0aGlzIGNhc2UgY29tcHJpc2VkIG9mIGNlbGxzIG9mIGEgZ2l2ZW4gY2VsbCB0eXBlIGFuZCBzYW1wbGUgY29tYmluYXRpb24uClRoZXJlZm9yZSwgdGhlIHZhbHVlcyB0aGF0IHdlIGNhbGN1bGF0ZWQgb24gYSBwZXItY2VsbCBsZXZlbCBhcmUgbm8gbG9uZ2VyIGFwcGxpY2FibGUgdG8gdGhpcyBwc2V1ZG8tYnVsa2VkIFNDRSBvYmplY3QuCgpZb3Ugc2hvdWxkIGFsc28gc2VlIGEgbmV3IGNvbHVtbiB0aGF0IHdhc24ndCBwcmVzZW50IHByZXZpb3VzbHksIHRoZSBgbmNlbGxzYCBjb2x1bW4uClRoaXMgY29sdW1uIHdhcyBhZGRlZCBkdXJpbmcgcHNldWRvLWJ1bGtpbmcgYW5kIGluZGljYXRlcyB0aGUgdG90YWwgbnVtYmVyIG9mIGNlbGxzIHRoYXQgd2VyZSBzdW1tZWQgdG9nZXRoZXIgdG8gZm9ybSBlYWNoIGNvbHVtbiBvZiB0aGUgU0NFIG9iamVjdC4KCkJlZm9yZSB3ZSBwcm9jZWVkIHdlIHdpbGwgd2FudCB0byBmaWx0ZXIgb3V0IGFueSBjb2x1bW5zIHRoYXQgaGF2ZSBhIGxvdyBudW1iZXIgb2YgY2VsbHMuCkEgbG93IG51bWJlciBvZiBjZWxscyB3aWxsIHVzdWFsbHkgcmVzdWx0IGluIHNtYWxsIGNvdW50cyB0aGF0IGNhbiBjYXVzZSBpc3N1ZXMgd2l0aCB0aGUgc3RhdGlzdGljYWwgYXBwcm94aW1hdGlvbnMgbWFkZSBkdXJpbmcgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMuClRoaXMgaXMgZXF1aXZhbGVudCB0byBmaWx0ZXJpbmcgb3V0IGFueSBsaWJyYXJpZXMgaW4gYnVsayBSTkEtc2VxIGFuYWx5c2lzIHRoYXQgaGF2ZSBsb3cgbGlicmFyeSBzaXplcy4KCldlIGNhbiBzZXQgYSB0aHJlc2hvbGQgZm9yIHRoZSBudW1iZXIgb2YgY2VsbHMgcmVxdWlyZWQgdG8gY29udGludWUgd2l0aCBvdXIgYW5hbHlzaXMgYW5kIHJlbW92ZSBhbnkgZ3JvdXBzIHRoYXQgZG8gbm90IG1lZXQgdGhlIG1pbmltdW0gdGhyZXNob2xkLgpIZXJlIHdlIHdpbGwgdXNlIDEwLCBidXQgdGhlIHRocmVzaG9sZCB5b3UgdXNlIGZvciB5b3VyIGRhdGFzZXQgY2FuIHZhcnkgZGVwZW5kaW5nIG9uIHRoZSBjb21wb3NpdGlvbiBvZiBjZWxsIHR5cGVzLgoKYGBge3IgZmlsdGVyIHBzZXVkb2J1bGssIGxpdmU9VFJVRX0KIyByZW1vdmUgYW55IGdyb3VwcyB3aXRoIGZld2VyIHRoYW4gMTAgY2VsbHMKZmlsdGVyX3BiX3NjZSA8LSBwYl9zY2VbLCBwYl9zY2UkbmNlbGxzID49IDEwXQpgYGAKCldlIGNhbiB0aGVuIHRha2UgYSBsb29rIGFuZCBzZWUgaG93IG1hbnkgY2VsbCB0eXBlLXNhbXBsZSBjb2x1bW5zIHdlIHJlbW92ZWQsIGlmIGFueS4KCmBgYHtyIHByaW50IGRpbSwgbGl2ZT1UUlVFfQojIHByaW50IG91dCBkaW1lbnNpb25zIG9mIHVuZmlsdGVyZWQgcHNldWRvYnVsayBzY2UKZGltKHBiX3NjZSkKCiMgZGltZW5zaW9ucyBvZiBmaWx0ZXJlZCBwc2V1ZG9idWxrIHNjZSAKZGltKGZpbHRlcl9wYl9zY2UpCmBgYAoKSXQgbG9va3MgbGlrZSB3ZSBvbmx5IGdvdCByaWQgb2Ygb25lIGdyb3VwLgpXZSBjYW4gZG8gYSBxdWljayBjaGVjayB0byBzZWUgd2hpY2ggZ3JvdXAgd2FzIHJlbW92ZWQgYnkgZmluZGluZyB3aGljaCBjb2x1bW4gaXMgbm8gbG9uZ2VyIHByZXNlbnQgaW4gdGhlIGZpbHRlcmVkIG9iamVjdC4KCmBgYHtyIHJlbW92ZWQgY29sdW1ucywgbGl2ZT1UUlVFfQojIGZpbmQgcmVtb3ZlZCBjb2x1bW5zCnJlbW92ZWRfY29scyA8LSAhKGNvbG5hbWVzKHBiX3NjZSkgJWluJSBjb2xuYW1lcyhmaWx0ZXJfcGJfc2NlKSkKCiMgcHJpbnQgb3V0IG1pc3NpbmcgY29sdW1ucwpjb2xuYW1lcyhwYl9zY2UpW3JlbW92ZWRfY29sc10KYGBgCgpUaGUgbGFzdCBzdGVwIHdlIHdhbnQgdG8gZG8gdG8gcHJlcGFyZSBvdXIgZGF0YXNldCBmb3IgREUgaXMgdG8gc3Vic2V0IHRoZSBwc2V1ZG8tYnVsa2VkIFNDRSBvYmplY3QgdG8gY29udGFpbiBvbmx5IHRoZSBjZWxsIHR5cGUgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBjb21wYXJpbmcgYWNyb3NzIHRoZSB0d28gUk1TIHN1YnR5cGVzLgpBcyBtZW50aW9uZWQgcHJldmlvdXNseSwgd2UgYXJlIHNwZWNpZmljYWxseSBpbnRlcmVzdGVkIGluIHRoZSBgVHVtb3JfTXlvYmxhc3RgIGNlbGwgdHlwZS4KCmBgYHtyIGZpbHRlciBjZWxsdHlwZX0KIyBsb2dpY2FsIHZlY3RvciBpbmRpY2F0aW5nIGlmIGNlbGxzIGFyZSB0dW1vciBteW9ibGFzdCBvciBub3QKbXlvYmxhc3RfY2VsbHMgPC0gZmlsdGVyX3BiX3NjZSRjZWxsdHlwZV9icm9hZCA9PSAiVHVtb3JfTXlvYmxhc3QiCgojIGNyZWF0ZSBhIG5ldyBzY2Ugd2l0aCBvbmx5IHRoZSB0dW1vciBteW9ibGFzdHMKdHVtb3JfbXlvYmxhc3Rfc2NlIDwtIGZpbHRlcl9wYl9zY2VbLCBteW9ibGFzdF9jZWxsc10KYGBgCgpBZnRlciBmaWx0ZXJpbmcgZm9yIG91ciBjZWxsIHR5cGUgb2YgaW50ZXJlc3Qgd2Ugc2hvdWxkIGhhdmUgYSBkYXRhc2V0IHdpdGggNiBjb2x1bW5zLCAxIGZvciBlYWNoIGdyb3VwIG9mIGBUdW1vcl9NeW9ibGFzdGAgY2VsbHMgaW4gZWFjaCBvZiBvdXIgNiBzYW1wbGVzLgoKIyMjIFBlcmZvcm0gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gd2l0aCBgREVTZXEyYAoKTm93IHdlIHdpbGwgdXNlIHRoZSBgREVTZXEyYCBwYWNrYWdlIHRvIHBlcmZvcm0gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gKERFKSBhbmFseXNpcyBvbiBvdXIgcHNldWRvLWJ1bGtlZCBTQ0Ugb2JqZWN0LgpGcm9tIHRoaXMgcG9pbnQsIHdlIGNhbiBwcm9jZWVkIGluIHRoZSBzYW1lIHdheSB3ZSB3b3VsZCBpZiB3ZSBoYWQgYSBidWxrIFJOQS1zZXEgZGF0YXNldCB3aXRoIDYgc2FtcGxlcy4KV2Ugd2lsbCBzdGFydCB3aXRoIHRoZSB1bm5vcm1hbGl6ZWQgcmF3IGNvdW50cyBpbiB0aGUgYGNvdW50c2AgYXNzYXkgb2YgdGhlIHBzZXVkby1idWxrZWQgU0NFIGFuZCBkbyB0aGUgZm9sbG93aW5nIHdpdGggYERFU2VxMmA6CgotIENyZWF0ZSBhIGBERVNlcURhdGFTZXRgIG9iamVjdAotIE5vcm1hbGl6ZSBhbmQgbG9nIHRyYW5zZm9ybSB0aGUgY291bnRzIGRhdGEKLSBFc3RpbWF0ZSBkaXNwZXJzaW9ucyBhbmQgc2hyaW5rIGVzdGltYXRlcwotIEZpdCBhIG5lZ2F0aXZlIGJpbm9taWFsIG1vZGVsIGFuZCBwZXJmb3JtIGh5cG90aGVzaXMgdGVzdGluZyB1c2luZyBXYWxkIHN0YXRpc3RpY3MKCllvdSBjYW4gYWxzbyByZWZlciB0byBvdXIgW21hdGVyaWFscyBmcm9tIG91ciBwcmV2aW91cyB3b3Jrc2hvcHMgY292ZXJpbmcgYnVsayBSTkEtc2VxXShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS90cmFpbmluZy1tb2R1bGVzL3RyZWUvbWFzdGVyL1JOQS1zZXEjcmVhZG1lKSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB1c2luZyBgREVTZXFgLgoKIyMjIyBDcmVhdGUgdGhlIGBERVNlcURhdGFTZXRgIG9iamVjdAoKVG8gY3JlYXRlIHRoZSBgREVTZXFEYXRhU2V0YCBvYmplY3Qgd2Ugd2lsbCBuZWVkIHRoZSB1bm5vcm1hbGl6ZWQgY291bnRzIG1hdHJpeCwgdGhlIG1ldGFkYXRhIGFzc29jaWF0ZWQgd2l0aCB0aGUgc2FtcGxlcywgYW5kIGEgZGVzaWduIGZvcm11bGEuClRoZSBmaXJzdCB0d28gaXRlbXMgYXJlIGFscmVhZHkgc3RvcmVkIGluIG91ciBTQ0Ugb2JqZWN0LCBzbyB3ZSBjYW4gY3JlYXRlIGEgYERFU2VxRGF0YVNldGAgb2JqZWN0IGRpcmVjdGx5IGZyb20gdGhhdCBvYmplY3QgdXNpbmcgdGhlIGBERVNlcURhdGFTZXQoKWAgZnVuY3Rpb24uClRoZSBkZXNpZ24gZm9ybXVsYSBpcyB1c2VkIHRvIGluZGljYXRlIHdoaWNoIGNvbHVtbnMgb2YgdGhlIG1ldGFkYXRhIG5lZWQgdG8gYmUgY29uc2lkZXJlZCBpbiB0aGUgREUgY29tcGFyaXNvbi4KRm9yIG91ciBleHBlcmltZW50IHdlIGFyZSBjb21wYXJpbmcgZ2VuZSBleHByZXNzaW9uIGJldHdlZW4gZGlmZmVyZW50IFJNUyBzdWJ0eXBlcy4KVGhlIHN1YnR5cGUgaW5mb3JtYXRpb24gaXMgc3RvcmVkIGluIHRoZSBgZGlhZ25vc2lzX2dyb3VwYCBjb2x1bW4gb2YgdGhlIGBjb2xEYXRhYCBpbiB0aGUgcHNldWRvLWJ1bGtlZCBTQ0UuCgpgYGB7ciBkZXNlcSBvYmplY3QsIGxpdmU9VFJVRX0KIyBzZXQgdXAgdGhlIGRlc2VxIG9iamVjdCwgZ3JvdXAgYnkgZGlhZ25vc2lzCmRlc2VxX29iamVjdCA8LSBERVNlcTI6OkRFU2VxRGF0YVNldCh0dW1vcl9teW9ibGFzdF9zY2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IGRpYWdub3Npc19ncm91cCkKYGBgCgpUaGUgcHNldWRvLWJ1bGtlZCBTQ0Ugb2JqZWN0IGNvbnRhaW5zIG9ubHkgb25lIGFzc2F5OiB0aGUgYGNvdW50c2AgYXNzYXkuClRoaXMgaXMgYmVjYXVzZSBgREVTZXEyYCBleHBlY3RzIHJhdyBjb3VudHMuCldoZW4gd2UgcnVuIGBERVNlcTJgIG9uIG91ciBkYXRhc2V0LCByYXcgY291bnRzIHdpbGwgZmlyc3QgYmUgbm9ybWFsaXplZCB1c2luZyBzaXplIGZhY3RvcnMgdG8gYWNjb3VudCBmb3IgZGlmZmVyZW5jZXMgaW4gdG90YWwgc2FtcGxlIGNvdW50cy4KVGhlcmVmb3JlIHdlIGRvbid0IGhhdmUgdG8gZG8gYW55IG5vcm1hbGl6YXRpb24gb24gb3VyIG93biDigJMgd2UnbGwgbGV0IGBERVNlcTJgIGRvIGFsbCB0aGUgd29yayBmb3IgdXMuCgpIb3dldmVyLCBiZWZvcmUgd2UgZGl2ZSBpbnRvIERFIGFuYWx5c2lzLCB3ZSBjYW4gZG8gc29tZSBpbml0aWFsIGV4cGxvcmF0aW9uIGFuZCB2aXN1YWxpemF0aW9uIG9mIG91ciBkYXRhIHRvIHNlZSBpZiBvdXIgc2FtcGxlcyBzZXBhcmF0ZSBieSBvdXIga25vd24gZmFjdG9yIG9mIGludGVyZXN0LCBSTVMgc3VidHlwZS4KSW4gcGFydGljdWxhciwgd2UgY2FuIHVzZSBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpIG9mIG91ciBwc2V1ZG8tYnVsa2VkIGRhdGFzZXQgdG8gdmlzdWFsaXplIGFueSB2YXJpYXRpb24gYmV0d2VlbiBzYW1wbGVzLgpJZiB0aGVyZSBpcyB2YXJpYXRpb24gYmV0d2VlbiBSTVMgc3VidHlwZXMsIHdlIGV4cGVjdCB0aGVpciByZXNwZWN0aXZlIHNhbXBsZXMgdG8gc2VwYXJhdGUgaW4gUEMgc3BhY2UsIGxpa2VseSBpbmRpY2F0aW5nIHByZXNlbmNlIG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcy4KV2UgY2FuIGV2YWx1YXRlIHRoaXMgYnkgcGxvdHRpbmcgUEMxIGFuZCBQQzIuCgpJbiBvcmRlciB0byBjcmVhdGUgb3VyIFBDQSBwbG90LCB3ZSB3aWxsIGZpcnN0IG5lZWQgdG8gbm9ybWFsaXplIG91ciBkYXRhIHRvIGFjY291bnQgZm9yIGFueSB0ZWNobmljYWwgdmFyaWF0aW9ucyBhY3Jvc3Mgc2FtcGxlcy4KQXMgYSByZW1pbmRlciwgdGhpcyBpcyBOT1QgcmVxdWlyZWQgZm9yIHJ1bm5pbmcgYERFU2VxMmAgYW5hbHlzaXM7IHdlIGFyZSBqdXN0IHVzaW5nIGl0IHRvIHZpc3VhbGl6ZSBvdXIgZGF0YSBwcmlvciB0byBERSBhbmFseXNpcy4KCmBgYHtyIG5vcm1hbGl6ZX0KIyBlc3RpbWF0ZSBzaXplIGZhY3RvcnMgZmlyc3QKZGVzZXFfb2JqZWN0IDwtIERFU2VxMjo6ZXN0aW1hdGVTaXplRmFjdG9ycyhkZXNlcV9vYmplY3QpCgojIG5vcm1hbGl6ZSBhbmQgbG9nIHRyYW5zZm9ybSB0byB1c2UgZm9yIHZpc3VhbGl6YXRpb24Kbm9ybWFsaXplZF9vYmplY3QgPC0gREVTZXEyOjpybG9nKGRlc2VxX29iamVjdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBibGluZCA9IFRSVUUpCm5vcm1hbGl6ZWRfb2JqZWN0CmBgYAoKV2Ugbm93IGhhdmUgYSBub3JtYWxpemVkIGFuZCB0cmFuc2Zvcm1lZCBvYmplY3QgdGhhdCBjYW4gYmUgZGlyZWN0bHkgaW5wdXQgdG8gdGhlIGBERVNlcTI6OnBsb3RQQ0EoKWAgZnVuY3Rpb24sIHdoaWNoIHdpbGwgYm90aCBjYWxjdWxhdGUgYW5kIHBsb3QgdGhlIFBDIHJlc3VsdHMuCgpgYGB7ciBwbG90UENBLCBsaXZlPVRSVUV9CkRFU2VxMjo6cGxvdFBDQShub3JtYWxpemVkX29iamVjdCwgaW50Z3JvdXAgPSAiZGlhZ25vc2lzX2dyb3VwIikKYGBgCgpBcyBleHBlY3RlZCB3ZSBzZWUgdGhhdCBzYW1wbGVzIGdyb3VwIHRvZ2V0aGVyIGJhc2VkIG9uIFJNUyBzdWJ0eXBlIGFuZCBhcmUgc2VwYXJhdGVkIGFsb25nIHRoZSBQQzEgYXhpcywgdGhlIFBDIGNvbnRyaWJ1dGluZyB0aGUgaGlnaGVzdCBhbW91bnQgb2YgdmFyaWF0aW9uLgoKIyMjIyBSdW4gYERFU2VxYAoKV2UnbGwgbm93IHVzZSB0aGUgY29udmVuaWVuY2UgZnVuY3Rpb24gYERFU2VxKClgIHRvIHBlcmZvcm0gb3VyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLgpUaGlzIGZ1bmN0aW9uIGNhbGN1bGF0ZXMgbm9ybWFsaXphdGlvbiBmYWN0b3JzLCBlc3RpbWF0ZXMgZ2VuZS13aXNlIGRpc3BlcnNpb25zLCBmaXRzIGEgbmVnYXRpdmUgYmlub21pYWwgbW9kZWwgYW5kIHBlcmZvcm1zIGh5cG90aGVzaXMgdGVzdGluZyB1c2luZyBXYWxkIHN0YXRpc3RpY3MuCgpgYGB7ciBkZXNlcSwgbGl2ZT1UUlVFfQojIHJ1biBERVNlcQpkZXNlcV9vYmplY3QgPC0gREVTZXEyOjpERVNlcShkZXNlcV9vYmplY3QpCmBgYAoKV2UgY2FuIGV2YWx1YXRlIGhvdyB3ZWxsIHRoZSBtb2RlbCBmaXQgb3VyIGRhdGEgYnkgbG9va2luZyBhdCB0aGUgZGlzcGVyc2lvbiBlc3RpbWF0ZXMuCldlIGV4cGVjdCB0byBzZWUgdGhlIGRpc3BlcnNpb24gZXN0aW1hdGVzIGRlY3JlYXNlIGFzIG1lYW5zIGFyZSBpbmNyZWFzaW5nIGFuZCBmb2xsb3cgdGhlIGxpbmUgb2YgYmVzdCBmaXQuIAoKYGBge3IgcGxvdCBkaXNwZXJzaW9uLCBsaXZlPVRSVUV9CnBsb3REaXNwRXN0cyhkZXNlcV9vYmplY3QpCmBgYAoKTm93IHdlIGNhbiBleHRyYWN0IHRoZSByZXN1bHRzIGZyb20gdGhlIG9iamVjdCwgc3BlY2lmeWluZyB0aGUgcC12YWx1ZSB0aHJlc2hvbGQgdGhhdCB3ZSB3b3VsZCBsaWtlIHRvIHVzZS4KCmBgYHtyIHJlc3VsdHMsIGxpdmU9VFJVRX0KIyBleHRyYWN0IHRoZSByZXN1bHRzIGFzIGEgRGF0YUZyYW1lCmRlc2VxX3Jlc3VsdHMgPC0gREVTZXEyOjpyZXN1bHRzKGRlc2VxX29iamVjdCwgYWxwaGEgPSAwLjA1KQpgYGAKCkJ1dCB3ZSBhcmVuJ3QgZG9uZSB5ZXQhCgpUaGUgZXN0aW1hdGVzIG9mIGxvZzIgZm9sZCBjaGFuZ2UgY2FsY3VsYXRlZCBieSBgREVTZXEoKWAgYXJlIG5vdCBjb3JyZWN0ZWQgZm9yIGV4cHJlc3Npb24gbGV2ZWwuClRoaXMgbWVhbnMgdGhhdCB3aGVuIGNvdW50cyBhcmUgc21hbGwsIHdlIGFyZSBsaWtlbHkgdG8gZW5kIHVwIHdpdGggc29tZSBsYXJnZSBmb2xkIGNoYW5nZSB2YWx1ZXMgdGhhdCBvdmVyZXN0aW1hdGUgdGhlIHRydWUgZXh0ZW50IG9mIHRoZSBjaGFuZ2UgYmV0d2VlbiBjb25kaXRpb25zLgoKV2UgY2FuIGNvcnJlY3QgdGhpcyBieSBhcHBseWluZyBhICJzaHJpbmthZ2UiIHByb2NlZHVyZSwgd2hpY2ggd2lsbCBhZGp1c3QgbGFyZ2UgdmFsdWVzIHdpdGggc21hbGwgY291bnRzIGRvd253YXJkLCB3aGlsZSBwcmVzZXJ2aW5nIHZhbHVlcyB3aXRoIGxhcmdlciBjb3VudHMsIHdoaWNoIGFyZSBsaWtlbHkgdG8gYmUgbW9yZSBhY2N1cmF0ZS4KClRvIGRvIHRoaXMsIHdlIHdpbGwgdXNlIHRoZSBgbGZjU2hyaW5rKClgIGZ1bmN0aW9uLCBidXQgZmlyc3Qgd2UgbmVlZCB0byBrbm93IHRoZSBuYW1lIGFuZC9vciBwb3NpdGlvbiBvZiB0aGUgImNvZWZmaWNpZW50IiB0aGF0IHdhcyBjYWxjdWxhdGVkIGJ5IGBERVNlcSgpYCwgd2hpY2ggd2UgY2FuIGRvIHdpdGggdGhlIGByZXN1bHRzTmFtZXMoKWAgZnVuY3Rpb24uCgpgYGB7ciBjb2VmZmljaWVudCwgbGl2ZT1UUlVFfQojIGlkZW50aWZ5IHBvc2l0aW9uIG9mIGNvZWZmaWNpZW50CkRFU2VxMjo6cmVzdWx0c05hbWVzKGRlc2VxX29iamVjdCkKYGBgCgoKYGBge3Igc2hyaW5rYWdlfQojIGFwcHlseSBsb2dGQyBzaHJpbmthZ2UgdXNpbmcgdGhlIGRlZmF1bHQgbW9kZWwKc2hyaW5rX3Jlc3VsdHMgPC0gREVTZXEyOjpsZmNTaHJpbmsoCiAgZGVzZXFfb2JqZWN0LCAKICByZXMgPSBkZXNlcV9yZXN1bHRzLCAKICBjb2VmID0gMiwKICB0eXBlID0gImFwZWdsbSIKKQpoZWFkKHNocmlua19yZXN1bHRzKQpgYGAKCklmIHlvdSBsb29rIGF0IG91ciBgc2hyaW5rX3Jlc3VsdHNgIG9iamVjdCwgd2Ugc2VlIHRoYXQgdGhlIGdlbmVzIGFyZSBsYWJlbGVkIHdpdGggdGhlIEVuc2VtYmwgZ2VuZSBpZGVudGlmaWVycywgYXMgdGhvc2Ugd2VyZSB0aGUgcm93IG5hbWVzIG9mIHRoZSBwc2V1ZG8tYnVsa2VkIFNDRSB3ZSB1c2VkIGFzIGlucHV0IHRvIGJ1aWxkIG91ciBgREVTZXEyYCBvYmplY3QuCkFsdGhvdWdoIHNvbWUgb2YgdXMgbWF5IGhhdmUgYWxsIG9mIHRoZSBpZGVudGlmaWVycyBtZW1vcml6ZWQgYnkgaGVhcnQsIGl0IGNhbiBiZSB1c2VmdWwgdG8gaGF2ZSBhIGh1bWFuIHJlYWRhYmxlIHN5bWJvbCBpbiBvdXIgcmVzdWx0cy4KQmVmb3JlIHdlIHNhdmUgdGhlIHJlc3VsdHMgYXMgYSBmaWxlLCB3ZSB3aWxsIGdyYWIgdGhlIGdlbmUgc3ltYm9scyBmcm9tIHRoZSBgcm93RGF0YWAgb2Ygb3VyIG9yaWdpbmFsIFNDRSBvYmplY3QgYW5kIGFkZCB0aGVtIGFzIGEgbmV3IGNvbHVtbi4KCmBgYHtyIGFkZCBnZW5lIHN5bWJvbH0KZGVzZXFfcmVzdWx0cyA8LSBzaHJpbmtfcmVzdWx0cyB8PgogICMgZGlyZWN0bHkgYWRkIEVuc2VtYmwgaWQgYXMgYSBjb2x1bW4KICAjIGNvbnZlcnRpbmcgcmVzdWx0cyBpbnRvIGEgZGF0YSBmcmFtZQogIHRpYmJsZTo6YXNfdGliYmxlKHJvd25hbWVzID0gImVuc2VtYmxfaWQiKQoKIyBjb252ZXJ0IHJvd2RhdGEgdG8gZGF0YSBmcmFtZSAKc2NlX3Jvd2RhdGFfZGYgPC0gcm93RGF0YSh0dW1vcl9teW9ibGFzdF9zY2UpIHw+CiAgIyBjcmVhdGUgYSBjb2x1bW4gd2l0aCByb3duYW1lcyBzdG9yZWQgYXMgZW5zZW1ibCBpZAogICMgdXNlIGZvciBqb2luaW5nIHdpdGggZGVzZXEgcmVzdWx0cwogIHRpYmJsZTo6YXNfdGliYmxlKHJvd25hbWVzID0gImVuc2VtYmxfaWQiKQoKIyBjb21iaW5lIGRlc2VxIHJlc3VsdHMgd2l0aCByb3dkYXRhIGJ5IGVuc2VtYmwgaWQgCmRlc2VxX3Jlc3VsdHMgPC0gZGVzZXFfcmVzdWx0cyB8PgogIGRwbHlyOjpsZWZ0X2pvaW4oc2NlX3Jvd2RhdGFfZGYsIGJ5ID0gImVuc2VtYmxfaWQiKQoKaGVhZChkZXNlcV9yZXN1bHRzKQpgYGAKCldlIGNhbiBzYXZlIHRoZSBuZXcgZGF0YSBmcmFtZSB0aGF0IHdlIGhhdmUgY3JlYXRlZCB3aXRoIHRoZSBFbnNlbWJsIGlkZW50aWZpZXJzLCBnZW5lIHN5bWJvbHMsIGFuZCB0aGUgYERFU2VxMmAgcmVzdWx0cyBhcyBhIHRhYiBzZXBhcmF0ZWQgKGB0c3ZgKSBmaWxlLgoKYGBge3Igc2F2ZSBkZXNlcSwgbGl2ZT1UUlVFfQojIHNhdmUgb3VyIHJlc3VsdHMgYXMgdHN2CnJlYWRyOjp3cml0ZV90c3YoZGVzZXFfcmVzdWx0cywgZGVzZXFfb3V0cHV0X2ZpbGUpCmBgYAoKVGhlIGxhc3QgdGhpbmcgdGhhdCB3ZSB3aWxsIGRvIGlzIHRha2UgYSBsb29rIGF0IGhvdyBtYW55IGdlbmVzIGFyZSBzaWduaWZpY2FudC4KSGVyZSB3ZSB3aWxsIHdhbnQgdG8gdXNlIHRoZSBhZGp1c3RlZCBwLXZhbHVlLCBmb3VuZCBpbiB0aGUgYHBhZGpgIGNvbHVtbiBvZiB0aGUgcmVzdWx0cywgYXMgdGhpcyBhY2NvdW50cyBmb3IgbXVsdGlwbGUgdGVzdCBjb3JyZWN0aW9uLgoKYGBge3Igc2lnbmlmaWNhbnQgcmVzdWx0cywgbGl2ZT1UUlVFfQojIGZpcnN0IGxvb2sgYXQgdGhlIHNpZ25pZmljYW50IHJlc3VsdHMgCmRlc2VxX3Jlc3VsdHNfc2lnIDwtIGRlc2VxX3Jlc3VsdHMgfD4KICAjIGZpbHRlciBiYXNlZCBvbiBhZGp1c3RlZCBwdmFsdWUKICBkcGx5cjo6ZmlsdGVyKHBhZGogPD0gMC4wNSkKCmhlYWQoZGVzZXFfcmVzdWx0c19zaWcpCmBgYAoKCiMjIyBFeHBsb3JpbmcgdGhlIGlkZW50aWZpZWQgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIAoKTm93IHRoYXQgd2UgaGF2ZSBpZGVudGlmaWVkIGEgc2V0IG9mIGdlbmVzIHRoYXQgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbiB0aGUgdHVtb3IgbXlvYmxhc3RzIGJldHdlZW4gQVJNUyBhbmQgRVJNUyBzdWJ0eXBlcywgbGV0cyBhY3R1YWxseSB0YWtlIGEgbG9vayBhdCB0aGVtIGFuZCBzZWUgaWYgd2UgY2FuIG1ha2Ugc29tZSBpbmZvcm1hdGl2ZSBwbG90cy4KVGhlIGZpcnN0IHBsb3Qgd2UnbGwgbWFrZSBpcyBhIHZvbGNhbm8gcGxvdCB1c2luZyB0aGUgW2BFbmhhbmNlZFZvbGNhbm9gIHBhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9rZXZpbmJsaWdoZS9FbmhhbmNlZFZvbGNhbm8pLgpUaGlzIHBhY2thZ2UgYXV0b21hdGljYWxseSBjb2xvcnMgdGhlIHBvaW50cyBieSBjdXRvZmZzIGZvciBib3RoIHNpZ25pZmljYW5jZSBhbmQgZm9sZCBjaGFuZ2UgYW5kIGxhYmVscyBtYW55IG9mIHRoZSBzaWduaWZpY2FudCBnZW5lcyAoc3ViamVjdCB0byBzcGFjaW5nKS4KYEVuaGFuY2VkVm9sY2Fub2AgaGFzIG1hbnksIG1hbnkgb3B0aW9ucywgd2hpY2ggaXMgYSBnb29kIHRoaW5nIGlmIHlvdSBkb24ndCBsaWtlIGFsbCBvZiBpdHMgZGVmYXVsdCBzZXR0aW5ncy4KRXZlbiBiZXR0ZXIsIGl0IG91dHB1dHMgYSBgZ2dwbG90MmAgb2JqZWN0LCBzbyBpZiB3ZSB3YW50IHRvIGN1c3RvbWl6ZSB0aGUgcGxvdCBmdXJ0aGVyLCB3ZSBjYW4gdXNlIHRoZSBzYW1lIGBnZ3Bsb3QyYCBjb21tYW5kcyB3ZSBoYXZlIHVzZWQgYmVmb3JlLgoKYGBge3Igdm9sY2Fub30KRW5oYW5jZWRWb2xjYW5vOjpFbmhhbmNlZFZvbGNhbm8oZGVzZXFfcmVzdWx0cywKICAgICAgICAgICAgICAgIHggPSAnbG9nMkZvbGRDaGFuZ2UnLCAjIGZvbGQgY2hhbmdlIHN0YXRpc3RpYyB0byBwbG90CiAgICAgICAgICAgICAgICB5ID0gJ3B2YWx1ZScsICMgc2lnbmlmaWNhbmNlIHZhbHVlcwogICAgICAgICAgICAgICAgbGFiID0gZGVzZXFfcmVzdWx0cyRnZW5lX3N5bWJvbCwgIyBsYWJlbHMgZm9yIHBvaW50cwogICAgICAgICAgICAgICAgcEN1dG9mZiA9IDFlLTA1LCAjIHAgdmFsdWUgY3V0b2ZmIChkZWZhdWx0KQogICAgICAgICAgICAgICAgRkNjdXRvZmYgPSAxLCAjIGZvbGQgY2hhbmdlIGN1dG9mZiAoZGVmYXVsdCkKICAgICAgICAgICAgICAgIHRpdGxlID0gTlVMTCwgIyBubyB0aXRsZQogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBOVUxMLCAjIG9yIHN1YnRpdGxlCiAgICAgICAgICAgICAgICBjYXB0aW9uID0gTlVMTCwgIyBvciBjYXB0aW9uCiAgICAgICAgICAgICAgICBkcmF3Q29ubmVjdG9ycyA9IFRSVUUsICMgYWRkIHNvbWUgZnVuIGFycm93cwogICAgICAgICAgICAgICAgbGFiU2l6ZSA9IDMgICMgc21hbGxlciBsYWJlbHMKICAgICAgICAgICAgICAgICkgKwogICMgY2hhbmdlIHRoZSBvdmVyYWxsIHRoZW1lCiAgdGhlbWVfYncoKSArCiAgIyBtb3ZlIHRoZSBsZWdlbmQgdG8gdGhlIGJvdHRvbQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpgYGAKCgpXZSBjYW4gYWxzbyByZXR1cm4gYmFjayB0byB0aGUgU0NFIG9iamVjdCB0aGF0IHdlIHVzZWQgdG8gY3JlYXRlIG91ciBwc2V1ZG8tYnVsa2VkIFNDRSBhbmQgbG9vayBhdCBnZW5lIGV4cHJlc3Npb24gb2Ygc29tZSBvZiB0aGUgc2lnbmlmaWNhbnQgZ2VuZXMuIApXZSBjYW4gY3JlYXRlIFVNQVAgcGxvdHMgYXMgd2UgZGlkIHByZXZpb3VzbHksIGJ1dCBpbnN0ZWFkIG9mIGxhYmVsaW5nIGVhY2ggY2VsbCB3aXRoIG1ldGFkYXRhLCB3ZSBjYW4gY29sb3IgY2VsbHMgYnkgYSBzcGVjaWZpZWQgZ2VuZSdzIGV4cHJlc3Npb24gbGV2ZWxzLgpXZSB3aWxsIGFsc28gdXNlIHNvbWUgb2YgdGhlIGBnZ3Bsb3QyYCBza2lsbHMgd2UgcGlja2VkIHVwIGVhcmxpZXIsIGxpa2UgYGZhY2V0X2dyaWQoKWAgdG8gcGxvdCBjZWxscyBmcm9tIGRpZmZlcmVudCBSTVMgc3VidHlwZXMgc2VwYXJhdGVseS4KVGhpcyBjYW4gaGVscCB1cyB2YWxpZGF0ZSB0aGUgYERFU2VxMmAgcmVzdWx0cyBzbyB0aGF0IHdlIGNhbiB2aXN1YWxpemUgZ2VuZSBleHByZXNzaW9uIGNoYW5nZXMgYWNyb3NzIG91ciBjZWxsIHR5cGUgb2YgaW50ZXJlc3Qgb24gYSBzaW5nbGUtY2VsbCBsZXZlbC4gCgpgYGB7ciBleHByZXNzaW9uIHVtYXAsIGxpdmU9VFJVRX0KIyBmaWx0ZXIgdG8ganVzdCBteW9ibGFzdCBjZWxscyBhbmQgcmVtb3ZlIGFueSBOQSdzIGJlZm9yZSBwbG90dGluZwpteW9ibGFzdF9jb21iaW5lZF9zY2UgPC0gcm1zX3NjZVssIHdoaWNoKHJtc19zY2UkY2VsbHR5cGVfYnJvYWQgPT0gIlR1bW9yX015b2JsYXN0IildCgojIHBsb3QgUFRQUlQgKEVOU0cwMDAwMDE5NjA5MCkgZXhwcmVzc2lvbiBpbiBBUk1TIHZzLiBFUk1TCnNjYXRlcjo6cGxvdFJlZHVjZWREaW0obXlvYmxhc3RfY29tYmluZWRfc2NlLAogICAgICAgICAgICAgICAgICAgICAgIGRpbXJlZCA9ICJmYXN0bW5uX1VNQVAiLAogICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX2J5ID0gIkVOU0cwMDAwMDE5NjA5MCIsICNQVFBSVAogICAgICAgICAgICAgICAgICAgICAgIHBvaW50X3NpemU9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICBwb2ludF9hbHBoYSA9IDAuNCwKICAgICAgICAgICAgICAgICAgICAgICBvdGhlcl9maWVsZHMgPSAiZGlhZ25vc2lzX2dyb3VwIikgKwogIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoZGlhZ25vc2lzX2dyb3VwKSkKYGBgCgpJbiB0aGUgYWJvdmUgcGxvdCB3ZSBvbmx5IHBsb3R0ZWQgdGhlIHR1bW9yIG15b2JsYXN0IGNlbGxzIHRoYXQgd2UgdXNlZCBpbiBvdXIgREUgYW5hbHlzaXMuIApIb3dldmVyLCB3ZSBtaWdodCBiZSBpbnRlcmVzdGVkIHRvIHNlZSB0aGUgZXhwcmVzc2lvbiBvZiBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gb3RoZXIgY2VsbCB0eXBlcyBwcmVzZW50IGluIG91ciBzYW1wbGVzLgoKYGBge3IgY2VsbHR5cGUgY29tcGFyaXNvbn0KIyBsZXQncyBjb21wYXJlIGdlbmUgZXhwcmVzc2lvbiBhY3Jvc3Mgc29tZSBvdGhlciBjZWxsIHR5cGVzCiMgbG9vayBhdCBhbGwgdHVtb3IgY2VsbHMgYW5kIHBpY2sgb25lIG5vcm1hbCBjZWxsIHR5cGUKY2VsbHR5cGVzIDwtIGMoIlR1bW9yX015b2JsYXN0IiwgCiAgICAgICAgICAgICAgICJUdW1vcl9NZXNvZGVybSIsIAogICAgICAgICAgICAgICAiVHVtb3JfTXlvY3l0ZSIsIAogICAgICAgICAgICAgICAiVmFzY3VsYXIgRW5kb3RoZWxpdW0iKQoKIyBzdWJzZXQgdG8ganVzdCBjZWxsdHlwZXMgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbgp0dW1vcl9zY2UgPC0gcm1zX3NjZVssIHdoaWNoKHJtc19zY2UkY2VsbHR5cGVfYnJvYWQgJWluJSBjZWxsdHlwZXMpXQpgYGAKCk5leHQgd2Ugd2lsbCBsb29rIGF0IGEgZmV3IERFIGdlbmVzIHRoYXQgd2UgaWRlbnRpZmllZCwgb25lIHVwIHJlZ3VsYXRlZCBnZW5lIGFuZCBvbmUgZG93biByZWd1bGF0ZWQgZ2VuZSwgYW5kIGNvbXBhcmUgdGhlaXIgZXhwcmVzc2lvbiBpbiBteW9ibGFzdHMgdG8gb3RoZXIgY2VsbCB0eXBlcyBpbiBBUk1TIGFuZCBFUk1TIHNhbXBsZXMuCldlIHdpbGwgdXNlIHRoZSBgc2NhdGVyOjpwbG90RXhwcmVzc2lvbigpYCBmdW5jdGlvbiB0byBjcmVhdGUgYSB2aW9saW4gcGxvdCB3aXRoIFJNUyBzdWJ0eXBlIG9uIHRoZSB4LWF4aXMgYW5kIGdlbmUgZXhwcmVzc2lvbiBvbiB0aGUgeS1heGlzLgpXZSBjYW4gY29udGludWUgdXNpbmcgYGZhY2V0X2dyaWQoKWAgdG8gc2hvdyBzZXBhcmF0ZSBwYW5lbHMgZm9yIGVhY2ggY2VsbCB0eXBlLgpCZWNhdXNlIHdlIHdhbnQgdG8gc2hvdyBtdWx0aXBsZSBnZW5lcyBoZXJlLCB3ZSBhcmUgZ29pbmcgdG8gYWRkIGFuIGFkZGl0aW9uYWwgb3B0aW9uIHRvIGBmYWNldF9ncmlkKClgIHRvIGluY2x1ZGUgbXVsdGlwbGUgcm93cyBpbiBvdXIgcGxvdCBncmlkLCBvbmUgZm9yIGVhY2ggZ2VuZSBvZiBpbnRlcmVzdC4KT25lIG5lYXQgdHJpY2sgb2YgdGhlIGBzY2F0ZXI6OnBsb3RFeHByZXNzaW9uKClgIGZ1bmN0aW9uIGlzIHRoYXQgaXQgYWN0dWFsbHkgY3JlYXRlcyBhIGBGZWF0dXJlYCBjb2x1bW4gd2hpY2ggY29ycmVzcG9uZHMgdG8gdGhlIGZlYXR1cmVzIChpbiB0aGlzIGNhc2UgZ2VuZXMpIGJlaW5nIHVzZWQgaW4gcGxvdHRpbmcuCldlIGNhbiB0aGVuIGRpcmVjdGx5IHJlZmVyZW5jZSB0aGF0IGBGZWF0dXJlYCBjb2x1bW4gd2hlbiBwbG90dGluZywgaW5zdGVhZCBvZiB1c2luZyB0aGUgYG90aGVyX2ZpZWxkc2Agb3B0aW9uIHdlIHVzZWQgcHJldmlvdXNseS4KCmBgYHtyIG11bHRpLWdlbmUgcGxvdH0KIyBwaWNrIGEgY291cGxlIGdlbmVzIHRvIGxvb2sgYXQgCmdlbmVzX3RvX3Bsb3QgPC0gYygiRU5TRzAwMDAwMTk2MDkwIiwgI1BUUFJUCiAgICAgICAgICAgICAgICAgICAiRU5TRzAwMDAwMTQ4OTM1IikgI0dBUzIKCiMgY3JlYXRlIGEgdmlvbGluIHBsb3QgCnNjYXRlcjo6cGxvdEV4cHJlc3Npb24odHVtb3Jfc2NlLAogICAgICAgICAgICAgICAgICAgICAgICMgYSB2ZWN0b3Igb2YgZ2VuZXMgdG8gcGxvdAogICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gZ2VuZXNfdG9fcGxvdCwgCiAgICAgICAgICAgICAgICAgICAgICAgeCA9ICJkaWFnbm9zaXNfZ3JvdXAiLCAKICAgICAgICAgICAgICAgICAgICAgICBjb2xvcl9ieSA9ICJkaWFnbm9zaXNfZ3JvdXAiLAogICAgICAgICAgICAgICAgICAgICAgIG90aGVyX2ZpZWxkcyA9ICJjZWxsdHlwZV9icm9hZCIsCiAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRfc2l6ZSA9IDAuMSkgKwogICMgZWFjaCBjZWxsdHlwZSBpcyBpdHMgb3duIGNvbHVtbgogIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoY2VsbHR5cGVfYnJvYWQpLAogICAgICAgICAgICAgIyBlYWNoIGZlYXR1cmUgKGdlbmUpIGlzIGl0cyBvd24gcm93CiAgICAgICAgICAgICByb3dzID0gdmFycyhGZWF0dXJlKSkgKyAKICAjIGNoYW5nZSB0aGUgZm9udCBzaXplIG9mIHRoZSBmYWNldCBsYWJlbHMKICB0aGVtZShzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSkgKyAKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQoCiAgICB0aXRsZSA9ICJTdWJ0eXBlIiwgIyB1cGRhdGUgdGhlIGxlZ2VuZCB0aXRsZQogICAgIyBjaGFuZ2UgdGhlIHNpemUgb2YgdGhlIGxlZ2VuZCBjb2xvcnMKICAgIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMsIGFscGhhID0gMSkpCiAgICApCmBgYAoKSG93IGRvIHRoZSBleHByZXNzaW9uIG9mIHRoZXNlIGdlbmVzIGNoYW5nZSBhY3Jvc3MgY2VsbCB0eXBlcyBhbmQgUk1TIHN1YnR5cGVzPwoKR28gYWhlYWQgYW5kIGV4cGxvcmUgc29tZSBnZW5lcyBvbiB5b3VyIG93biEgCkZlZWwgZnJlZSB0byBwbG90IGFueSBvZiB0aGUgZ2VuZXMgdGhhdCBhcmUgaWRlbnRpZmllZCBhcyBzaWduaWZpY2FudCwgZm91bmQgaW4gdGhlIERFIHJlc3VsdHMgdGFibGUsIG9yIHlvdXIgZmF2b3JpdGUgZ2VuZS4KUmVtZW1iZXIsIHlvdSBuZWVkIHRvIHVzZSB0aGUgRW5zZW1ibCBnZW5lIGlkZW50aWZpZXIgdG8gcmVmZXIgdG8gZWFjaCBnZW5lLgoKYGBge3IgZXhwbG9yZX0KIyBub3cgZG8gc29tZSBleHBsb3JhdGlvbiBvZiBvdGhlciBnZW5lcyBvbiB5b3VyIG93biEgCmBgYAoKIyMgUHJpbnQgc2Vzc2lvbiBpbmZvIAoKYGBge3Igc2Vzc2lvbiBpbmZvfQpzZXNzaW9uSW5mbygpCmBgYAoK