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
) +
# Modify the legend key with larger, easier to see points
guides(color = guide_legend(override.aes = list(size = 3, alpha = 1)))

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)
Next, we will 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(
# update the legend title
title = "Subtype",
# 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!
LS0tCnRpdGxlOiAiRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgZm9yIHNjUk5BLXNlcSBkYXRhIgphdXRob3I6ICJEYXRhIExhYiBmb3IgQUxTRiIKZGF0ZTogMjAyMwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCiMjIE9iamVjdGl2ZXMKClRoaXMgbm90ZWJvb2sgd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG86CgotIFVzZSBwc2V1ZG8tYnVsa2luZyB0byBwcmVwYXJlIHNjUk5BLXNlcSBsaWJyYXJpZXMgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uCi0gUGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB3aXRoIHRoZSBgREVTZXEyYCBwYWNrYWdlCi0gVXNlIGBnZ3Bsb3QyYCBhbmQgYEVuaGFuY2VkVm9sY2Fub2AgdG8gdmlzdWFsaXplIGdlbmUgZXhwcmVzc2lvbiBjaGFuZ2VzIGFjcm9zcyBjZWxsIHR5cGVzIGFuZCBzYW1wbGVzCgotLS0KCkp1c3QgbGlrZSBidWxrIFJOQS1zZXEsIGl0IGlzIGxpa2VseSB0aGF0IG9uZSBvZiB0aGUgZ29hbHMgd2hlbiBwZXJmb3JtaW5nIHNjUk5BLXNlcSB3aWxsIGJlIHRvIGNvbXBhcmUgdGhlIGdlbmUgZXhwcmVzc2lvbiBvZiBtdWx0aXBsZSBzYW1wbGVzIHRvIGVhY2ggb3RoZXIuClVubGlrZSBidWxrIFJOQS1zZXEgYW5hbHlzaXMsIHNjUk5BLXNlcSBhbmFseXNpcyBhbGxvd3MgdXMgdG8gaWRlbnRpZnkgYW5kIGFubm90YXRlIGNlbGwgdHlwZXMgb3Igc3VicG9wdWxhdGlvbnMgb2YgY2VsbHMgcHJlc2VudCBpbiBlYWNoIG9mIG91ciBzYW1wbGVzLgpUaGlzIG1lYW5zIHRoYXQgd2UgY2FuIGFjY291bnQgZm9yIGRpZmZlcmVuY2VzIGluIGNlbGwgdHlwZSBjb21wb3NpdGlvbiBhY3Jvc3Mgc2FtcGxlcyBhbmQgc3BlY2lmaWNhbGx5IGZvY3VzIG9uIGNlbGwgdHlwZXMgb3IgcG9wdWxhdGlvbnMgb2YgaW50ZXJlc3Qgd2hlbiBwZXJmb3JtaW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIChERSkgYW5hbHlzaXMuCkluIHRoaXMgbm90ZWJvb2ssIHdlIHdpbGwgd29yayB3aXRoIG11bHRpcGxlIHNhbXBsZXMgdG8gaWRlbnRpZnkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGFjcm9zcyBjZWxsIHR5cGVzIG9mIGludGVyZXN0IHVzaW5nIHRoZSBbYERFU2VxMmBdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9ERVNlcTIuaHRtbCkgcGFja2FnZS4KCiFbU2luZ2xlLWNlbGwgcm9hZG1hcDogRGlmZmVyZW50aWFsIGV4cHJlc3Npb25dKGRpYWdyYW1zL3JvYWRtYXBfZGlmZmVyZW50aWFsX2V4cHJlc3Npb24ucG5nKQoKV2Ugd2lsbCBjb250aW51ZSB3b3JraW5nIHdpdGggc2FtcGxlcyBmcm9tIHRoZSBbYFNDUENQMDAwMDA1YCBwcm9qZWN0XShodHRwczovL3NjcGNhLmFsZXhzbGVtb25hZGUub3JnL3Byb2plY3RzL1NDUENQMDAwMDA1KSwgYW4gaW52ZXN0aWdhdGlvbiBvZiBwZWRpYXRyaWMgc29saWQgdHVtb3JzIGxlZCBieSB0aGUgRHllciBhbmQgQ2hlbiBsYWJzIGF0IFN0LiBKdWRlIENoaWxkcmVuJ3MgUmVzZWFyY2ggSG9zcGl0YWwuClRoaXMgcGFydGljdWxhciBkYXRhc2V0IGNvbnRhaW5zIDEwIGRpZmZlcmVudCBzYW1wbGVzIHRoYXQgaGF2ZSBiZWVuIGludGVncmF0ZWQgdXNpbmcgYGZhc3RNTk5gLCBmb2xsb3dpbmcgdGhlIHNhbWUgcHJvY2VkdXJlIHdlIG91dGxpbmVkIGluIGAwMi1kYXRhc2V0X2ludGVncmF0aW9uLlJtZGAuClRoZXNlIDEwIHNhbXBsZXMgcmVwcmVzZW50IHR3byBkaWZmZXJlbnQgdHlwZXMgb2YgcmhhYmRvbXlvc2FyY29tYSAoUk1TKTogZW1icnlvbmFsIHJoYWJkb215b3NhcmNvbWEgKEVSTVMpIGFuZCBhbHZlb2xhciByaGFiZG9teW9zYXJjb21hIChBUk1TKS4KVGhlc2UgdHdvIHN1YnR5cGVzIGFyZSBkaXN0aW5ndWlzaGVkIGJ5IHRoZSBwcmVzZW5jZSBvZiB0aGUgYFBBWDMvUEFYNy1GT1hPMWAgZnVzaW9uIGdlbmUsIHdoaWNoIGlzIHByZXNlbnQgb25seSBpbiBBUk1TIHBhdGllbnRzLgpBZGRpdGlvbmFsbHksIGNlbGxzIGZvdW5kIGluIEFSTVMgdHVtb3JzIHRlbmQgdG8gaGF2ZSBhbiBpbmNyZWFzZWQgbXV0YXRpb25hbCBidXJkZW4gd2l0aCBjZWxscyBpbiBhIG1vcmUgZGlmZmVyZW50aWF0ZWQgc3RhdGUgY29tcGFyZWQgdG8gRVJNUyB0dW1vciBjZWxscyAoW1NoZXJuIF9ldCBhbC5fIDIwMTRdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTU4LzIxNTktODI5MC5DRC0xMy0wNjM5KTsgW1N0ZXdhcnQgX2V0IGFsLl8gMjAxOF0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5jY2VsbC4yMDE4LjA3LjAxMikpLgpSTVMgdHVtb3JzLCByZWdhcmRsZXNzIG9mIHN1YnR5cGUsIGFyZSBtYWRlIHVwIG9mIGNlbGxzIHR5cGljYWxseSBhc3NvY2lhdGVkIHdpdGggZGV2ZWxvcG1lbnQgb2Ygc2tlbGV0YWwgbXVzY2xlOiBtZXNvZGVybSwgbXlvYmxhc3RzLCBhbmQgbXlvY3l0ZXMgKFtTZWJpcmUgYW5kIE1hbG9uZSAyMDAzXShodHRwczovL2RvaS5vcmcvMTAuMTEzNi9qY3AuNTYuNi40MTIpKS4KW1BhdGVsIF9ldCBhbC5fICgyMDIyKV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5kZXZjZWwuMjAyMi4wNC4wMDMpIHRlc3RlZCB0aGUgaHlwb3RoZXNpcyB0aGF0IGNlbGwgdHlwZXMgaGF2ZSBkaXN0aW5jdCBnZW5lIGV4cHJlc3Npb24gcGF0dGVybnMgaW4gQVJNUyB2cy4gRVJNUyBzYW1wbGVzLgpIZXJlIHdlIHdpbGwgbG9vayBhdCBhIHN1YnNldCBvZiB0aGUgc2FtcGxlcyB0aGV5IHNlcXVlbmNlZCBhbmQgaWRlbnRpZnkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGluIHR1bW9yIGNlbGxzIGJldHdlZW4gQVJNUyBhbmQgRVJNUyBzYW1wbGVzLgoKIyMgU2V0IHVwCgpgYGB7ciBzZXR1cCwgbWVzc2FnZT1GQUxTRX0KIyBzZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDIwMjIpCgojIGxvYWQgbGlicmFyaWVzCmxpYnJhcnkoZ2dwbG90MikgIyBwbG90dGluZyBmdW5jdGlvbnMKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKCiMgcGFja2FnZSB1c2VkIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwpsaWJyYXJ5KERFU2VxMikKYGBgCgojIyMgRGlyZWN0b3JpZXMgYW5kIGZpbGVzCgpXZSB3aWxsIHN0YXJ0IGJ5IHJlYWRpbmcgaW4gYSBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIChTQ0UpIG9iamVjdCB0aGF0IGNvbnRhaW5zIGJvdGggdGhlIHVuY29ycmVjdGVkIChtZXJnZWQgYnV0IG5vdCBpbnRlZ3JhdGVkKSBhbmQgY29ycmVjdGVkIChpbnRlZ3JhdGVkKSBnZW5lIGV4cHJlc3Npb24gZGF0YSBmb3IgYWxsIDEwIHNhbXBsZXMuCgpQcmlvciB0byBpbnRlZ3JhdGlvbiwgYWxsIDEwIHNhbXBsZXMgd2VudCB0aHJvdWdoIHRoZSBzYW1lIGZpbHRlcmluZywgbm9ybWFsaXphdGlvbiwgYW5kIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbi4KVGhlc2UgMTAgc2FtcGxlcyB3ZXJlIHRoZW4gbWVyZ2VkIGludG8gb25lIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0IGZvbGxvd2luZyB0aGUgc2FtZSBzdGVwcyBvdXRsaW5lZCBpbiBgMDMtZGF0YXNldF9pbnRlZ3JhdGlvbi5SbWRgLgpUaGUgbWVyZ2VkIG9iamVjdCB3YXMgdGhlbiBpbnRlZ3JhdGVkIHdpdGggYGZhc3RNTk5gIHRvIG9idGFpbiBhIGNvcnJlY3RlZCBnZW5lIGV4cHJlc3Npb24gYXNzYXkgYW5kIGNvcnJlY3RlZCByZWR1Y2VkIGRpbWVuc2lvbmFsaXR5IHJlc3VsdHMuClRoZSBmaW5hbCBTQ0Ugb2JqZWN0IHdhcyBzdG9yZWQgaW4gYGRhdGEvcm1zL2ludGVncmF0ZWQvcm1zX2FsbF9zY2UucmRzYC4KCldlIGFsc28gaGF2ZSBwcm92aWRlZCBhIG1ldGFkYXRhIGZpbGUsIGBkYXRhL3Jtcy9hbm5vdGF0aW9ucy9ybXNfc2FtcGxlX21ldGFkYXRhLnRzdmAsIHRoYXQgY29udGFpbnMgaW5mb3JtYXRpb24gZnJvbSBlYWNoIHNhbXBsZSwgc3VjaCBhcyBkaWFnbm9zaXMsIHNleCwgYWdlLCBldGMuCkVhY2ggcm93IGluIHRoaXMgZmlsZSBjb3JyZXNwb25kcyB0byBhIHNhbXBsZSBmb3VuZCBpbiB0aGUgaW50ZWdyYXRlZCBTQ0Ugb2JqZWN0LgoKVG8gYmVnaW4sIGxldCdzIHNldCB1cCBvdXIgZGlyZWN0b3JpZXMgYW5kIGZpbGVzOgoKYGBge3IgZmlsZXBhdGhzfQojIHNldCB1cCBmaWxlIHBhdGhzCiMgZGF0YSBkaXJlY3RvcnkgZm9yIFJNUyBkYXRhCmRhdGFfZGlyIDwtIGZpbGUucGF0aCgiZGF0YSIsICJybXMiKQoKIyBpbnRlZ3JhdGVkIGZpbGUgY29udGFpbmluZyBzYW1wbGVzIHRvIHVzZSBmb3IgREUgYW5hbHlzaXMKaW50ZWdyYXRlZF9zY2VfZmlsZSA8LSBmaWxlLnBhdGgoCiAgZGF0YV9kaXIsCiAgImludGVncmF0ZWQiLAogICJybXNfYWxsX3NjZS5yZHMiCikKCiMgc2FtcGxlIG1ldGFkYXRhIHRvIHNldCB1cCBERSBhbmFseXNpcwpzYW1wbGVfbWV0YWRhdGFfZmlsZSA8LSBmaWxlLnBhdGgoCiAgZGF0YV9kaXIsCiAgImFubm90YXRpb25zIiwKICAicm1zX3NhbXBsZV9tZXRhZGF0YS50c3YiCikKCiMgZGlyZWN0b3J5IHRvIHN0b3JlIG91dHB1dApkZXNlcV9kaXIgPC0gZmlsZS5wYXRoKCJhbmFseXNpcyIsICJybXMiLCAiZGVzZXEiKQpmczo6ZGlyX2NyZWF0ZShkZXNlcV9kaXIpCgojIHJlc3VsdHMgZmlsZSB0byBvdXRwdXQgZnJvbSBERSBhbmFseXNpcwpkZXNlcV9vdXRwdXRfZmlsZSA8LSBmaWxlLnBhdGgoCiAgZGVzZXFfZGlyLAogICJybXNfbXlvYmxhc3RfZGVzZXFfcmVzdWx0cy50c3YiCikKCiMgb3V0cHV0IGludGVncmF0ZWQgc2NlIG9iamVjdApvdXRwdXRfc2NlX2ZpbGUgPC0gZmlsZS5wYXRoKAogIGRhdGFfZGlyLAogICJpbnRlZ3JhdGVkIiwKICAicm1zX3N1YnNldF9zY2UucmRzIgopCmBgYAoKV2UgY2FuIGdvIGFoZWFkIGFuZCByZWFkIGluIHRoZSBTQ0Ugb2JqZWN0IGFuZCB0aGUgbWV0YWRhdGEgZmlsZS4KCmBgYHtyIHJlYWQgZmlsZXMsIGxpdmU9VFJVRX0KIyByZWFkIGluIHRoZSBTQ0Ugb2JqZWN0IHRoYXQgaGFzIGFscmVhZHkgYmVlbiBpbnRlZ3JhdGVkCmludGVncmF0ZWRfc2NlIDwtIHJlYWRyOjpyZWFkX3JkcyhpbnRlZ3JhdGVkX3NjZV9maWxlKQoKIyByZWFkIGluIHNhbXBsZSBtZXRhZGF0YSBmaWxlCnNhbXBsZV9tZXRhZGF0YSA8LSByZWFkcjo6cmVhZF90c3Yoc2FtcGxlX21ldGFkYXRhX2ZpbGUpCmBgYAoKIyMgRGF0YXNldCBleHBsb3JhdGlvbgoKQmVmb3JlIHdlIGRpdmUgaW50byBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiwgbGV0J3MgZXhwbG9yZSBvdXIgaW50ZWdyYXRlZCBTQ0Ugb2JqZWN0IGFuZCB0aGUgZGF0YXNldCBhIGxpdHRsZSBtb3JlLgoKV2UnbGwgc3RhcnQgYnkgbG9va2luZyBhdCB3aGF0J3MgaW5zaWRlIHRoZSBvYmplY3QuCkhlcmUgd2Ugc2hvdWxkIGhhdmUgYm90aCB0aGUgb3JpZ2luYWwgKHVuY29ycmVjdGVkKSBkYXRhIGFuZCB0aGUgaW50ZWdyYXRlZCAoY29ycmVjdGVkKSBkYXRhIGZvciBib3RoIHRoZSBnZW5lIGV4cHJlc3Npb24gYW5kIHRoZSByZWR1Y2VkIGRpbWVuc2lvbmFsaXR5IHJlc3VsdHMuCkhvdyBhcmUgdGhvc2Ugc3RvcmVkIGluIG91ciBvYmplY3Q/CgpgYGB7ciBwcmludCBzY2UsIGxpdmU9VFJVRX0KIyBwcmludCBvdXQgZW50aXJlIG9iamVjdAppbnRlZ3JhdGVkX3NjZQpgYGAKCgpgYGB7ciBwcmludCBhc3NheSBuYW1lcywgbGl2ZT1UUlVFfQojIGxvb2sgYXQgdGhlIGFzc2F5IG5hbWVzIGluIG91ciBvYmplY3QKYXNzYXlOYW1lcyhpbnRlZ3JhdGVkX3NjZSkKYGBgCgpXaGVuIHdlIGxvb2sgYXQgdGhlIGFzc2F5IG5hbWVzIHdlIHNob3VsZCBzZWUgdGhhdCB0aGVyZSBhcmUgMyBtYXRyaWNlcywgYGNvdW50c2AsIGBsb2djb3VudHNgLCBhbmQgYGZhc3Rtbm5fY29ycmVjdGVkYC4KVGhlIGBjb3VudHNgIGFuZCBgbG9nY291bnRzYCBhc3NheXMgY29ycmVzcG9uZCB0byB0aGUgdW5jb3JyZWN0ZWQgZ2VuZSBleHByZXNzaW9uIGRhdGEgdGhhdCBoYXMgYmVlbiBtZXJnZWQgYnV0IE5PVCBpbnRlZ3JhdGVkLgpUaGUgYGZhc3Rtbm5fY29ycmVjdGVkYCBkYXRhIGNvbnRhaW5zIHRoZSBjb3JyZWN0ZWQgZ2VuZSBleHByZXNzaW9uIGRhdGEgb2J0YWluZWQgZnJvbSBpbnRlZ3JhdGlvbi4KRm9yIHRoaXMgZXhlcmNpc2Ugd2Ugd2lsbCBub3QgYmUgdXNpbmcgdGhlIGBmYXN0bW5uX2NvcnJlY3RlZGAgZGF0YSAobW9yZSBvbiB3aHkgbm90IG9uY2Ugd2UgZ2V0IHRvIHNldHRpbmcgdXAgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uKSwgYnV0IHdlIG5lZWQgdG8gYmUgYXdhcmUgdGhhdCBpdCBpcyBwcmVzZW50IGFuZCBiZSBhYmxlIHRvIGRpc3Rpbmd1aXNoIGl0IGZyb20gb3VyIHVuY29ycmVjdGVkIGRhdGEuCgoKYGBge3IgcHJpbnQgcmVkdWNlZERpbSBuYW1lcywgbGl2ZT1UUlVFfQojIGxvb2sgYXQgdGhlIG5hbWVzIG9mIHRoZSBkaW1lbnNpb24gcmVkdWN0aW9ucwpyZWR1Y2VkRGltTmFtZXMoaW50ZWdyYXRlZF9zY2UpCmBgYAoKSW4gdGhlIGByZWR1Y2VkRGltYCBzbG90cyB5b3Ugc2hvdWxkIHNlZSBgUENBYCBhbmQgYFVNQVBgLCB3aGljaCB3ZXJlIGJvdGggY2FsY3VsYXRlZCBmcm9tIHRoZSBjb21iaW5lZCBkYXRhIF9iZWZvcmVfIGludGVncmF0aW9uLgpZb3Ugc2hvdWxkIGFsc28gc2VlIGBmYXN0bW5uX1BDQWAgYW5kIGBmYXN0bW5uX1VNQVBgIHJlZHVjZWQgZGltZW5zaW9ucywgd2hpY2ggY29ycmVzcG9uZCB0byB0aGUgaW50ZWdyYXRlZCByZXN1bHRzLgoKIyMjIENlbGwgdHlwZSBhbm5vdGF0aW9ucwoKSnVzdCBsaWtlIGluIHRoZSBpbnRlZ3JhdGlvbiBub3RlYm9vaywgdGhpcyBkYXRhc2V0IGFsc28gY29udGFpbnMgdGhlIGNlbGwgdHlwZSBhbm5vdGF0aW9ucyBmb3VuZCBpbiB0aGUgYGNlbGx0eXBlX2ZpbmVgIGFuZCBgY2VsbHR5cGVfYnJvYWRgIGNvbHVtbnMgb2YgdGhlIGBjb2xEYXRhYC4KVGhlc2UgY2VsbCB0eXBlcyB3ZXJlIG9yaWdpbmFsbHkgYXNzaWduZWQgaW4gW1BhdGVsIF9ldCBhbC5fICgyMDIyKV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5kZXZjZWwuMjAyMi4wNC4wMDMpLgpXZSB3aWxsIHVzZSB0aGVzZSBjZWxsIHR5cGUgYXNzaWdubWVudHMgdG8gc2V0IHVwIHRoZSBERSBhbmFseXNpcyBiZWxvdywgYnV0IHRoZXkgYXJlIG5vdCByZXF1aXJlZCBmb3IgREUgYW5hbHlzaXMgaXRzZWxmLgpJdCdzIGltcG9ydGFudCB0byBub3RlIHRoYXQgREUgYW5hbHlzaXMgY2FuIGJlIGFwcGxpZWQgdG8gYW55IHN1YnBvcHVsYXRpb24gb2YgaW50ZXJlc3QgdGhhdCBpcyBzaGFyZWQgYWNyb3NzIHNhbXBsZXMgYmVzaWRlcyBqdXN0IGNlbGwgdHlwZXMuCgpCZWNhdXNlIHdlIGFyZSBnb2luZyB0byBiZSBkb2luZyBERSBhbmFseXNpcyBiZXR3ZWVuIEFSTVMgYW5kIEVSTVMgc2FtcGxlcywgbGV0J3Mgc3RhcnQgYnkgbGFiZWxpbmcgY2VsbHMgaW4gdGhlIGludGVncmF0ZWQgZGF0YXNldCBiYXNlZCBvbiB0aGVpciBSTVMgc3VidHlwZS4KVG8gZG8gdGhpcyB3ZSB3aWxsIG5lZWQgdG8gYmUgc3VyZSB0aGF0IHRoZSBzdWJ0eXBlIGlzIHByZXNlbnQgaW4gdGhlIGBjb2xEYXRhYCBvZiB0aGUgaW50ZWdyYXRlZCBTQ0Ugb2JqZWN0LgpJZiBpdCdzIG5vdCB0aGVyZSwgd2UgbmVlZCB0byBhZGQgaXQgaW4uCgpgYGB7ciBjb2xkYXRhIGhlYWQsIGxpdmU9VFJVRX0KIyBsb29rIGF0IHRoZSBoZWFkIG9mIHRoZSBjb2xkYXRhCmhlYWQoY29sRGF0YShpbnRlZ3JhdGVkX3NjZSkpIHw+CiAgYXMuZGF0YS5mcmFtZSgpCmBgYAoKVWggb2gsIGl0IGxvb2tzIGxpa2UgdGhlIFJNUyBzdWJ0eXBlIGlzIG5vdCBmb3VuZCBpbiB0aGUgU0NFIG9iamVjdC4KRm9ydHVuYXRlbHkgd2UgYWxzbyBoYXZlIHRoZSBzYW1wbGUgbWV0YWRhdGEgdGFibGUgdGhhdCB3ZSByZWFkIGluIGVhcmxpZXIsIHdoaWNoIGNvbnRhaW5zIGluZm9ybWF0aW9uIGFib3V0IGVhY2ggb2YgdGhlIHNhbXBsZXMgcHJlc2VudCBpbiB0aGUgZGF0YXNldC4KCmBgYHtyIHNhbXBsZSBtZXRhZGF0YSwgbGl2ZT1UUlVFfQojIHByaW50IG91dCBzYW1wbGUgbWV0YWRhdGEKaGVhZChzYW1wbGVfbWV0YWRhdGEpCmBgYAoKTG9va2luZyBhdCB0aGlzIHNhbXBsZSB0YWJsZSwgd2Ugc2VlIGEgY29sdW1uIG5hbWVkIGBzdWJkaWFnbm9zaXNgIHdoaWNoIGFjY291bnRzIGZvciB0aGUgUk1TIHN1YnR5cGUsIEFSTVMgb3IgRVJNUy4KV2UgYWxzbyBzZWUgb3RoZXIgY29sdW1ucyB0aGF0IGNvbnRhaW4gaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBzcGVjaWZpYyBzYW1wbGUuCgpXZSBjYW4gaW5jb3Jwb3JhdGUgdGhlIGluZm9ybWF0aW9uIGluIHRoaXMgc2FtcGxlIG1ldGFkYXRhIHRhYmxlIGludG8gdGhlIGBjb2xEYXRhYCBvZiB0aGUgaW50ZWdyYXRlZCBTQ0Ugb2JqZWN0LgpUaGlzIHdpbGwgYWxsb3cgdXMgdG8gbWF0Y2ggZWFjaCBvZiB0aGUgc2FtcGxlcyBpbiB0aGUgU0NFIG9iamVjdCB3aXRoIHRoZSBSTVMgc3VidHlwZSBhbmQgYWxzbyBhbGxvdyB1cyB0byB1c2UgYW55IG9mIHRoZSBjb2x1bW5zIGluIHRoZSBzYW1wbGUgbWV0YWRhdGEgZm9yIHBsb3R0aW5nLgoKYGBge3IgbW9kaWZ5IGNvbGRhdGF9CiMgYWRkIHNhbXBsZSBtZXRhZGF0YSB0byBjb2xEYXRhIGZyb20gdGhlIGludGVncmF0ZWQgU0NFIG9iamVjdApjb2xkYXRhX2RmIDwtIGNvbERhdGEoaW50ZWdyYXRlZF9zY2UpIHw+CiAgIyBjb252ZXJ0IGZyb20gRGF0YUZyYW1lIHRvIGRhdGEuZnJhbWUKICBhcy5kYXRhLmZyYW1lKCkgfD4KICAjIG1lcmdlIHdpdGggc2FtcGxlIG1ldGFkYXRhCiAgZHBseXI6OmxlZnRfam9pbihzYW1wbGVfbWV0YWRhdGEsIGJ5ID0gYygic2FtcGxlIiA9ICJsaWJyYXJ5X2lkIikpIHw+CiAgIyBjcmVhdGUgbmV3IGNvbHVtbnMKICAjIGNlbGxfaWQgaXMgYSBjb21iaW5hdGlvbiBvZiBiYXJjb2RlIGFuZCBzYW1wbGUKICBkcGx5cjo6bXV0YXRlKAogICAgY2VsbF9pZCA9IGdsdWU6OmdsdWUoIntzYW1wbGV9LXtiYXJjb2RlfSIpLAogICAgIyBzaW1wbGlmeSBzdWJkaWFnbm9zaXMKICAgIGRpYWdub3Npc19ncm91cCA9IGZvcmNhdHM6OmZjdF9yZWNvZGUoCiAgICAgIHN1YmRpYWdub3NpcywKICAgICAgIkFSTVMiID0gIkFsdmVvbGFyIHJoYWJkb215b3NhcmNvbWEiLAogICAgICAiRVJNUyIgPSAiRW1icnlvbmFsIHJoYWJkb215b3NhcmNvbWEiCiAgICApCiAgKQoKIyBhZGQgbW9kaWZpZWQgZGF0YSBmcmFtZSBiYWNrIHRvIFNDRSBhcyBEYXRhRnJhbWUKY29sRGF0YShpbnRlZ3JhdGVkX3NjZSkgPC0gRGF0YUZyYW1lKAogIGNvbGRhdGFfZGYsCiAgcm93Lm5hbWVzID0gY29sZGF0YV9kZiRjZWxsX2lkCikKYGBgCgpOb3cgd2hlbiB3ZSBsb29rIGF0IHRoZSBgY29sRGF0YWAgb2YgdGhlIFNDRSBvYmplY3Qgd2Ugc2hvdWxkIHNlZSBuZXcgY29sdW1ucywgaW5jbHVkaW5nIHRoZSBgZGlhZ25vc2lzX2dyb3VwYCBjb2x1bW4gd2hpY2ggaW5kaWNhdGVzIGlmIGVhY2ggY2VsbCBjb21lcyBmcm9tIGFuIEVSTVMgb3IgQVJNUyBzYW1wbGUuCgpgYGB7ciBwcmludCBuZXcgY29sZGF0YSwgbGl2ZT1UUlVFfQojIHRha2UgYSBsb29rIGF0IHRoZSBuZXcgbW9kaWZpZWQgY29sRGF0YQpoZWFkKGNvbERhdGEoaW50ZWdyYXRlZF9zY2UpKSB8PgogIGFzLmRhdGEuZnJhbWUoKQpgYGAKCiMjIyBQbG90dGluZyB3aXRoIGFubm90YXRpb25zCgpXZSBjYW4gbm93IHVzZSB0aGF0IGNvbHVtbiB0byBsYWJlbCBhbnkgVU1BUCBwbG90cyAob3Igb3RoZXIgcGxvdCB0eXBlcykgdGhhdCB3ZSBtYWtlLgpJbiB0aGUgY2h1bmsgYmVsb3cgd2Ugd2lsbCBzdGFydCBieSB0YWtpbmcgYSBsb29rIGF0IG91ciBpbnRlZ3JhdGlvbiByZXN1bHRzIGFuZCBjb2xvciBvdXIgY2VsbHMgYnkgUk1TIHN1YnR5cGUuCgoqKlJlbWluZGVyOiBZb3Ugc2hvdWxkIGFsd2F5cyB1c2UgdGhlIGJhdGNoLWNvcnJlY3RlZCBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gcmVzdWx0cyBmb3IgdmlzdWFsaXppbmcgZGF0YXNldHMgY29udGFpbmluZyBtdWx0aXBsZSBsaWJyYXJpZXMgb3Igc2FtcGxlcy4qKgoKYGBge3IgZGlhZ25vc2lzIGdyb3VwIFVNQVAsIGxpdmU9VFJVRX0KIyBVTUFQIG9mIGFsbCBzYW1wbGVzLCBzZXBhcmF0aW5nIGJ5IGRpYWdub3NpcyBncm91cApzY2F0ZXI6OnBsb3RSZWR1Y2VkRGltKAogIGludGVncmF0ZWRfc2NlLAogIGRpbXJlZCA9ICJmYXN0bW5uX1VNQVAiLAogIGNvbG9yX2J5ID0gImRpYWdub3Npc19ncm91cCIsCiAgcG9pbnRfc2l6ZSA9IDAuNSwKICBwb2ludF9hbHBoYSA9IDAuMgopCmBgYAoKSW50ZXJlc3RpbmdseSwgaXQgbG9va3MgbGlrZSBzYW1wbGVzIGZyb20gdGhlIEFSTVMgYW5kIEVSTVMgc3VidHlwZXMgdGVuZCB0byBncm91cCB3aXRoIHNhbXBsZXMgb2YgdGhlIHNhbWUgc3VidHlwZSByYXRoZXIgdGhhbiBhbGwgdG9nZXRoZXIuCgpJbiB0aGUgaW50ZWdyYXRpb24gbm90ZWJvb2sgd2UgYWxzbyBsb29rZWQgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjZWxsIHR5cGVzIGFmdGVyIGludGVncmF0aW9uLgpJbiB0aGF0IG5vdGVib29rLCB3ZSBkaXNjdXNzZWQgdGhhdCBjZWxscyBvZiB0aGUgc2FtZSBjZWxsIHR5cGUgYXJlIGV4cGVjdGVkIHRvIGludGVncmF0ZSB3aXRoIG90aGVyIGNlbGxzIG9mIHRoZSBzYW1lIHR5cGUuCklzIHRoYXQgdGhlIGNhc2Ugd2l0aCB0aGlzIGRhdGFzZXQ/CgpBIHdvcmQgb2YgY2F1dGlvbiB3aGVuIGV2YWx1YXRpbmcgdGhlIGNlbGwgdHlwZSByZXN1bHRzIGZvciB0aGlzIGRhdGFzZXQ6IFRoZSBjZWxsIHR5cGVzIGZvciB0aGlzIGRhdGFzZXQgd2VyZSBhc3NpZ25lZCBpbiBhIHR3byBzdGFnZSBwcm9jZXNzIGFzIGRlc2NyaWJlZCBpbiBbUGF0ZWwgX2V0IGFsLl8gKDIwMjIpXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmRldmNlbC4yMDIyLjA0LjAwMykuClRoZSBmaXJzdCBzdGFnZSBhc3NpZ25lZCBjZWxscyBhcyB0dW1vciBvciBub24tdHVtb3IuClRoZSBuZXh0IHN0YWdlIGZ1cnRoZXIgY2xhc3NpZmllZCB0dW1vciBjZWxscyBpbnRvIG9uZSBvZiB0aHJlZSB0eXBlcyBvZiB0dW1vciBjZWxsczogbXlvYmxhc3QsIG15b2N5dGUsIG9yIG1lc29kZXJtLgpTb21lIHNhbXBsZXMgY291bGQgbm90IGJlIGZ1cnRoZXIgY2xhc3NpZmllZCwgc28gYWxsIG9mIHRoZWlyIHR1bW9yIGNlbGxzIGFyZSBkZW5vdGVkIGBUdW1vcmAuClRoZSBzYW1wbGVzIHdoaWNoIGNvdWxkIGJlIGZ1cnRoZXIgY2xhc3NpZmllZCBoYXZlIGEgbWl4IG9mIGBUdW1vcl9NZXNvZGVybWAsIGBUdW1vcl9NeW9ibGFzdGAsIGFuZCBgVHVtb3JfTXlvY3l0ZWAuCgpgYGB7ciBjZWxsdHlwZSBVTUFQfQojIFVNQVAgb2YgYWxsIHNhbXBsZXMgbGFiZWxlZCBieSBjZWxsIHR5cGUKc2NhdGVyOjpwbG90UmVkdWNlZERpbSgKICBpbnRlZ3JhdGVkX3NjZSwKICBkaW1yZWQgPSAiZmFzdG1ubl9VTUFQIiwKICAjIGNvbG9yIGVhY2ggcG9pbnQgYnkgY2VsbCB0eXBlCiAgY29sb3JfYnkgPSAiY2VsbHR5cGVfYnJvYWQiLAogIHBvaW50X3NpemUgPSAwLjUsCiAgcG9pbnRfYWxwaGEgPSAwLjQKKSArCiAgIyBNb2RpZnkgdGhlIGxlZ2VuZCBrZXkgd2l0aCBsYXJnZXIsIGVhc2llciB0byBzZWUgcG9pbnRzCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMsIGFscGhhID0gMSkpKQpgYGAKClVubGlrZSB3aXRoIHRoZSBwcmV2aW91cyBkYXRhc2V0cyB3ZSBoYXZlIHNlZW4gd2hlcmUgYWxsIGNlbGxzIG9mIHRoZSBzYW1lIGNlbGwgdHlwZSBhbHdheXMgZ3JvdXBlZCB0b2dldGhlciwgdGhpcyBkYXRhc2V0IHNob3dzIHNvbWUgc2xpZ2h0bHkgZGlmZmVyZW50IHBhdHRlcm5zIGFuZCBub3QgYWxsIGNlbGxzIG9mIHRoZSBzYW1lIGNlbGwgdHlwZSBjbHVzdGVyIHRvZ2V0aGVyLgpPbmUgcmVhc29uIGlzIHRoYXQgdHVtb3IgZGF0YSBjYW4gYmUgaGV0ZXJvZ2VuZW91cyBhbmQgZXZlcnkgdHVtb3IgaXMgdW5pcXVlLgpEZXBlbmRpbmcgb24gdGhlIHR1bW9yIHR5cGUgd2UgbWF5IG5vdCBleHBlY3QgZXZlcnkgc2FtcGxlIHRvIGludGVncmF0ZSBwZXJmZWN0bHkgYW5kIG1vcmUgaGV0ZXJvZ2VuZW91cyB0dW1vciB0eXBlcyB3aWxsIGJlIG1vcmUgZGlmZmljdWx0IHRvIGludGVncmF0ZSB0b2dldGhlci4KSW4gdGhpcyBwYXJ0aWN1bGFyIGNhc2Ugd2UgYXJlIGxvb2tpbmcgYXQgdHdvIHN1YnR5cGVzIG9mIFJNUyB0aGF0IGhhdmUgZGlzdGluY3QgbXV0YXRpb24gYnVyZGVucyBhbmQgZGlmZmVyZW50aWF0aW9uIHN0YXRlcywgc28gaXQncyBsaWtlbHkgdGhhdCB0aG9zZSBkaWZmZXJlbmNlcyBjb250cmlidXRlIHRvIGhvdyB3ZWxsIHRoZXkgaW50ZWdyYXRlLgoKVG8gZXhwbG9yZSB3aGV0aGVyIGNlbGxzIGFyZSBncm91cGluZyB0b2dldGhlciBib3RoIGJ5IGNlbGwgdHlwZSBhbmQgYnkgUk1TIHN1YnR5cGUsIHdlIGNhbiBjcmVhdGUgYSBwbG90IHRoYXQgaW5jb3Jwb3JhdGVzIGJvdGggcGllY2VzIG9mIG1ldGFkYXRhLgpXZSB3aWxsIHRha2UgYWR2YW50YWdlIG9mIHRoZSBgZmFjZXRfZ3JpZCgpYCBmdW5jdGlvbiBmcm9tIGBnZ3Bsb3QyYCB0byBsb29rIGF0IHR3byB2YXJpYWJsZXMgaW4gdGhlIGBjb2xEYXRhYCBhdCBvbmNlIC0gdGhlIGNlbGwgdHlwZSBhbmQgdGhlIHN1YmRpYWdub3Npcy4KSW4gdGhlIGJlbG93IHBsb3Qgd2Ugd2lsbCBjb2xvciBvdXIgY2VsbHMgYnkgY2VsbCB0eXBlIHdoaWxlIGFsc28gdXNpbmcgYGZhY2V0X2dyaWQoKWAgc28gdGhhdCBjZWxscyBmcm9tIGRpZmZlcmVudCBzdWJkaWFnbm9zZXMgd2lsbCBiZSBpbiB0aGVpciBvd24gcGxvdCBwYW5lbC4KCmBgYHtyIGNlbGx0eXBlIHN1YmRpYWdub3NpcyBVTUFQLCBsaXZlPVRSVUV9CiMgVU1BUCBvZiBhbGwgc2FtcGxlcwojIHNlcGFyYXRpbmcgYnkgZGlhZ25vc2lzIGdyb3VwIGFuZCBsYWJlbGluZyBjZWxsIHR5cGUKc2NhdGVyOjpwbG90UmVkdWNlZERpbSgKICBpbnRlZ3JhdGVkX3NjZSwKICBkaW1yZWQgPSAiZmFzdG1ubl9VTUFQIiwKICAjIGNvbG9yIGVhY2ggcG9pbnQgYnkgY2VsbCB0eXBlCiAgY29sb3JfYnkgPSAiY2VsbHR5cGVfYnJvYWQiLAogIHBvaW50X3NpemUgPSAwLjUsCiAgcG9pbnRfYWxwaGEgPSAwLjQsCiAgIyB0ZWxsIHNjYXRlciB0byB1c2UgZGlhZ25vc2lzX2dyb3VwIGZvciBwbG90dGluZwogIG90aGVyX2ZpZWxkcyA9ICJkaWFnbm9zaXNfZ3JvdXAiCikgKwogICMgaW5jbHVkZSBlYWNoIGRpYWdub3NpcyBncm91cCBhcyBpdHMgb3duIGNvbHVtbgogIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoZGlhZ25vc2lzX2dyb3VwKSkKYGBgCgpBcyBleHBlY3RlZCwgd2Ugc2VlIHRoYXQgY2VsbCB0eXBlcyBhcmUgc2VwYXJhdGVkLCBtb3N0IGxpa2VseSBkdWUgdG8gZGlmZmVyZW50IFJNUyBzdWJ0eXBlcy4KCldlIGNhbiBhbHNvIHVzZSBhIHN0YWNrZWQgYmFycGxvdCB0byBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgY2VsbCB0eXBlcyBhY3Jvc3MgZWFjaCBzYW1wbGUsIHdoaWNoIHdpbGwgcmVxdWlyZSBhIGJpdCBvZiB3cmFuZ2xpbmcgZmlyc3QuCgpgYGB7ciBjZWxsdHlwZSBiYXJwbG90fQojIGZpbHRlciBjb2xkYXRhIHRvIG9ubHkgaW5jbHVkZSB0dW1vciBjZWxscwp0dW1vcl9jZWxsc19kZiA8LSBjb2xkYXRhX2RmIHw+CiAgIyBmaW5kIHJvd3Mgd2hlcmUgdGhlIGNlbGwgdHlwZSBuYW1lIGNvbnRhaW5zIHRoZSBzdHJpbmcgIlR1bW9yIgogIGRwbHlyOjpmaWx0ZXIoc3RyaW5ncjo6c3RyX2RldGVjdChjZWxsdHlwZV9icm9hZCwgIlR1bW9yIikpCgojIGNyZWF0ZSBhIHN0YWNrZWQgYmFycGxvdApnZ3Bsb3QodHVtb3JfY2VsbHNfZGYsIGFlcyh4ID0gc2FtcGxlLCBmaWxsID0gY2VsbHR5cGVfYnJvYWQpKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuMikgKwogIGxhYnMoCiAgICB4ID0gIlNhbXBsZSIsCiAgICB5ID0gIlByb3BvcnRpb24gb2YgY2VsbHMiLAogICAgZmlsbCA9ICJDZWxsIHR5cGUiCiAgKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSkpICsKICAjIGZhY2V0IGJ5IGRpYWdub3NpcyBncm91cAogIGZhY2V0X2dyaWQoCiAgICBjb2xzID0gdmFycyhkaWFnbm9zaXNfZ3JvdXApLAogICAgIyBvbmx5IHNob3cgbm9uLU5BIHZhbHVlcyBvbiB4LWF4aXMKICAgIHNjYWxlcyA9ICJmcmVlX3giLAogICAgc3BhY2UgPSAiZnJlZV94IgogICkKYGBgCgpTaW1pbGFyIHRvIHRoZSBVTUFQLCB0aGlzIHBsb3Qgc2hvd3MgdGhhdCBBUk1TIGFuZCBFUk1TIHNoYXJlIGEgbG90IG9mIHRoZSBzYW1lIGNlbGwgdHlwZXMuCgpXZSBhbHNvIHNlZSB0aGF0IG9ubHkgNiBvZiB0aGVzZSBsaWJyYXJpZXMgaGF2ZSB0dW1vciBjZWxscyB0aGF0IGhhdmUgYmVlbiBmdXJ0aGVyIGNsYXNzaWZpZWQgaW50byBtZXNvZGVybSwgbXlvYmxhc3QsIGFuZCBteW9jeXRlLgozIGxpYnJhcmllcyBjb250YWluIGNlbGxzIHRoYXQgYXJlIG9ubHkgY2xhc3NpZmllZCBhcyB0dW1vciBvciBub24tdHVtb3IsIGFuZCB0dW1vciBjZWxscyBhcmUgbm90IGZ1cnRoZXIgY2xhc3NpZmllZCwgYW5kIHRoZSByZW1haW5pbmcgbGlicmFyeSBpcyBub3QgZXZlbiBwcmVzZW50IGluIG91ciBwbG90IGJlY2F1c2UgaXQgd2FzIG5vdCBhc3NpZ25lZCBhbnkgY2VsbCB0eXBlcyAoYWxsIGFyZSBgTkFgKS4KV2Ugd2lsbCBjb250aW51ZSBvdXIgYW5hbHlzaXMgb25seSB1c2luZyB0aGUgNiBsaWJyYXJpZXMgd2l0aCBmdWxseSBjbGFzc2lmaWVkIGNlbGwgdHlwZXMsIHJlbW92aW5nIHRoZSBvdGhlciA0IGJlZm9yZSB3ZSBwcm9jZWVkIHdpdGggZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uCgojIyMgRmlsdGVyaW5nIHNhbXBsZXMKClRoZSByZWFzb24gd2Ugd2FudCB0byBwYXJlIGRvd24gb3VyIGxpc3Qgb2Ygc2FtcGxlcyB0byBjb25zaWRlciBpcyB0aGF0IHdlIHdhbnQgdG8gZW5zdXJlIHRoYXQgdGhlIGNlbGwgdHlwZXMgKG9yIHN1YnBvcHVsYXRpb25zKSB0aGF0IHdlIGFyZSBpbnRlcmVzdGVkIGluIGFyZSBwcmVzZW50IGluIGFsbCBzYW1wbGVzIGluY2x1ZGVkIGluIG91ciBERSBhbmFseXNpcy4KV2Ugd2FudCB0byByZW1vdmUgYW55IHNhbXBsZXMgdGhhdCBkbyBub3QgY29udGFpbiBvdXIgY2VsbCBwb3B1bGF0aW9uKHMpIG9mIGludGVyZXN0IGFzIHRoZXkgaGF2ZSBubyBjb3VudHMgdG8gY29udHJpYnV0ZSB0byB0aGUgREUgYW5hbHlzaXMuCgpgYGB7ciBzdWJzZXQgc2NlfQojIGRlZmluZSBzYW1wbGVzIHRvIGtlZXAKbGlicmFyeV9pZHMgPC0gYygKICAiU0NQQ0wwMDA0NzkiLAogICJTQ1BDTDAwMDQ4MCIsCiAgIlNDUENMMDAwNDgxIiwKICAiU0NQQ0wwMDA0ODQiLAogICJTQ1BDTDAwMDQ4OCIsCiAgIlNDUENMMDAwNDkxIgopCgojIHN1YnNldCBzY2UgdG8gb25seSBjb250YWluIHNhbXBsZXMgb2YgaW50ZXJlc3QKc2FtcGxlc190b19rZWVwIDwtIGludGVncmF0ZWRfc2NlJHNhbXBsZSAlaW4lIGxpYnJhcnlfaWRzCnJtc19zY2UgPC0gaW50ZWdyYXRlZF9zY2VbLCBzYW1wbGVzX3RvX2tlZXBdCgojIHByaW50IG91dCBvdXIgbmV3IFNDRQpybXNfc2NlCmBgYAoKQmVmb3JlIHdlIG1vdmUgb24sIHdlJ2xsIHJlbW92ZSB0aGUgb3JpZ2luYWwgaW50ZWdyYXRlZCBvYmplY3QgZnJvbSBvdXIgZW52aXJvbm1lbnQgdG8gc2F2ZSBzb21lIG1lbW9yeS4KCmBgYHtyIHJlbW92ZSBzY2V9CnJtKGludGVncmF0ZWRfc2NlKQpgYGAKCldlIHdpbGwgYWxzbyBzYXZlIG91ciBuZXcgb2JqZWN0IGluIGNhc2Ugd2Ugd2FudCB0byB1c2UgaXQgZm9yIG90aGVyIGFuYWx5c2lzIGxhdGVyIG9uLgoKYGBge3Igc2F2ZSBzY2V9CiMgd3JpdGUgUkRTIGZpbGUgd2l0aCBjb21wcmVzc2lvbgpyZWFkcjo6d3JpdGVfcmRzKHJtc19zY2UsIGZpbGUgPSBvdXRwdXRfc2NlX2ZpbGUsIGNvbXByZXNzID0gImd6IikKYGBgCgpXZSBub3cgaGF2ZSBhbiB1cGRhdGVkIFNDRSBvYmplY3QgdGhhdCBjb250YWlucyA2IHNhbXBsZXMgdGhhdCB3ZXJlIG9idGFpbmVkIGZyb20gYSBtaXggb2YgQVJNUyBhbmQgRVJNUyBwYXRpZW50cy4KV2UgY2FuIHRoZW4gYXNrIHRoZSBxdWVzdGlvbiwgZG8gc3BlY2lmaWMgdHVtb3IgY2VsbCB0eXBlcyBjb250YWluIHNldHMgb2YgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGJldHdlZW4gQVJNUyBhbmQgRVJNUyBzYW1wbGVzPwoKV2Ugc2hvdWxkIG1ha2Ugc3VyZSB0aGF0IHdlIGhhdmUgZW5vdWdoIGJpb2xvZ2ljYWwgcmVwbGljYXRlcyBmcm9tIGVhY2ggZ3JvdXAgdG8gc2V0IHVwIG91ciBleHBlcmltZW50LgpJdCBpcyBpbXBlcmF0aXZlIHRvIGNvbnNpZGVyIGdvb2QgZXhwZXJpbWVudGFsIGRlc2lnbiBhbmQgZW5zdXJlIHRoYXQgd2UgaGF2ZSBlbm91Z2ggYmlvbG9naWNhbCByZXBsaWNhdGVzIChhdCBsZWFzdCAzIGZvciBlYWNoIGdyb3VwKSB3aGVuIHBlcmZvcm1pbmcgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmFseXNpcy4KCklmIHdlIGxvb2sgYmFjayBhdCBvdXIgc3RhY2tlZCBiYXJwbG90IHdlIHNlZSB0aGF0IHdlIHBpY2tlZCAzIEFSTVMgYW5kIDMgRVJNUyBzYW1wbGVzLgpXZSBjYW4gYWxzbyBzZWUgdGhhdCB0aGUgbWFqb3JpdHkgb2YgY2VsbHMgYXJlIHR1bW9yIGNlbGxzLCBpbiBwYXJ0aWN1bGFyIHRoZSBsYXJnZXN0IHBvcHVsYXRpb24gb2YgY2VsbHMgYXBwZWFycyB0byBiZSB0aGUgYFR1bW9yX015b2JsYXN0YC4KRm9yIHRoaXMgZXhhbXBsZSB3ZSB3aWxsIGZvY3VzIG9uIGlkZW50aWZ5aW5nIERFIGdlbmVzIGluIHRoZXNlIGBUdW1vcl9NeW9ibGFzdGAgY2VsbHMsIGJ1dCB0aGUgcHJpbmNpcGxlcyBhcHBsaWVkIGJlbG93IGNhbiBiZSBhcHBsaWVkIHRvIGFueSBjZWxsIHR5cGVzIG9yIHN1YnBvcHVsYXRpb25zIG9mIGludGVyZXN0LgoKIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMKCk5vdyB3ZSBhcmUgcmVhZHkgdG8gc3RhcnQgcHJlcGFyaW5nIGZvciBvdXIgREUgYW5hbHlzaXMsIHdoZXJlIHdlIHdpbGwgY29tcGFyZSB0aGUgZ2VuZSBleHByZXNzaW9uIG9mIHR1bW9yIG15b2JsYXN0IGNlbGxzIGJldHdlZW4gQVJNUyBhbmQgRVJNUyBzYW1wbGVzLgoKVGhyb3VnaG91dCB0aGUgbm90ZWJvb2sgd2UgaGF2ZSBiZWVuIHdvcmtpbmcgd2l0aCBhbiBpbnRlZ3JhdGVkIGRhdGFzZXQgdGhhdCBjb250YWlucyBjb3JyZWN0ZWQgZ2VuZSBleHByZXNzaW9uIGRhdGEgKGBmYXN0bW5uX2NvcnJlY3RlZGAgYXNzYXkpIGFuZCBhIGNvcnJlY3RlZCBVTUFQLgpBcyBhIHJlbWluZGVyLCB0aGUgdW5jb3JyZWN0ZWQgZ2VuZSBleHByZXNzaW9uIGRhdGEsIGZvdW5kIGluIHRoZSBgY291bnRzYCBhbmQgYGxvZ2NvdW50c2AgYXNzYXlzLCBjb3JyZXNwb25kIHRvIGRhdGEgdGhhdCBoYXMgYmVlbiBtZXJnZWQgKHRoZSBmaXJzdCBzdGVwIHdlIHdhbGtlZCB0aHJvdWdoIHByaW9yIHRvIGludGVncmF0aW9uKSBpbnRvIHRoZSBzYW1lIFNDRSBidXQgbm90IHlldCBpbnRlZ3JhdGVkLgpXZSBkbyBub3Qgd2FudCB0byB1c2UgY29ycmVjdGVkIGdlbmUgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uOyBgREVTZXEyYCBleHBlY3RzIHRoZSBvcmlnaW5hbCByYXcgY291bnRzIGFzIGlucHV0IHNvIHdlIHdpbGwgYmUgdXNpbmcgZGF0YSBmb3VuZCBpbiB0aGUgYGNvdW50c2AgYXNzYXkgb2YgdGhlIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0LgoKSXQgaXMgYWR2aXNlZCB0byBvbmx5IHVzZSB0aGUgY29ycmVjdGVkIHZhbHVlcyBmb3IgYW55IGFuYWx5c2VzIGJlaW5nIHBlcmZvcm1lZCBhdCB0aGUgY2VsbCBsZXZlbCwgZS5nLiwgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uLgpJbiBjb250cmFzdCwgaXQgaXMgbm90IGFkdmlzZWQgdG8gdXNlIGNvcnJlY3RlZCB2YWx1ZXMgZm9yIGFueSBhbmFseXNlcyB0aGF0IGFyZSBnZW5lLWJhc2VkLCBzdWNoIGFzIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9yIG1hcmtlciBnZW5lIGRldGVjdGlvbiwgYmVjYXVzZSB3aXRoaW4tYmF0Y2ggYW5kIGJldHdlZW4tYmF0Y2ggZ2VuZSBleHByZXNzaW9uIGRpZmZlcmVuY2VzIGFyZSBubyBsb25nZXIgcHJlc2VydmVkLgpUaGUgcmVhc29uIGZvciB0aGlzIGlzIHR3by1mb2xkIOKAkyBtYW55IG9mIHRoZSBERSBtb2RlbHMgd2lsbCBleHBlY3QgdW5jb3JyZWN0ZWQgY291bnRzIGJlY2F1c2UgdGhleSB3aWxsIGFjY291bnQgZm9yIGJldHdlZW4tc2FtcGxlIHZhcmlhdGlvbiB3aXRoaW4gdGhlIG1vZGVsLCBhbmQgd2Ugd2FudCB0byBlbnN1cmUgd2UgYXJlIHByZXNlcnZpbmcgdmFyaWF0aW9uIHRoYXQgaXMgcHJlc2VudCBzbyBhcyBub3QgdG8gYXJ0aWZpY2lhbGx5IGluZmxhdGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBwb3B1bGF0aW9ucy4KU2VlIHRoZSBbT1NDQSBjaGFwdGVyIG9uIFVzaW5nIHRoZSBjb3JyZWN0ZWQgdmFsdWVzXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xOS9PU0NBLm11bHRpc2FtcGxlL3VzaW5nLWNvcnJlY3RlZC12YWx1ZXMuaHRtbCN1c2luZy1jb3JyZWN0ZWQtdmFsdWVzKSBmb3IgbW9yZSBpbnNpZ2h0LgoKIyMjIFBzZXVkby1idWxraW5nCgpCZWZvcmUgd2UgY2FuIGNvbXBhcmUgdGhlIGdlbmUgZXhwcmVzc2lvbiBwcm9maWxlcyBvZiBteW9ibGFzdHMgaW4gQVJNUyB2cy4gRVJNUyBzYW1wbGVzLCB3ZSB3aWxsIG5lZWQgdG8gInBzZXVkby1idWxrIiB0aGUgZ2VuZSBjb3VudHMuClBzZXVkby1idWxraW5nIGNyZWF0ZXMgYSBuZXcgY291bnRzIG1hdHJpeCB0aGF0IGNvbnRhaW5zIHRoZSBzdW0gb2YgdGhlIGNvdW50cyBmcm9tIGFsbCBjZWxscyB3aXRoIGEgZ2l2ZW4gbGFiZWwgKGUuZy4sIGNlbGwgdHlwZSkgZm9yIGVhY2ggc2FtcGxlIChbVHVuZyBfZXQgYWwuXyAyMDE3XShodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zcmVwMzk5MjEpKS4KSWYgd2Ugd2VyZSB0byBrZWVwIGVhY2ggY2VsbCdzIGNvdW50cyBzZXBhcmF0ZSwgdGhleSB3b3VsZCBiZSB0cmVhdGVkIGFzIHJlcGxpY2F0ZXMsIGxlYWRpbmcgdG8gaW5mbGF0ZWQgc3RhdGlzdGljcy4KQnkgcHNldWRvLWJ1bGtpbmcgZmlyc3QsIHdlIHdpbGwgbm93IGhhdmUgb25lIGNvdW50IGZvciBlYWNoIGdlbmUgZm9yIGVhY2ggc2FtcGxlIGFuZCB3ZSBjYW4gdGFrZSBhZHZhbnRhZ2Ugb2Ygd2VsbC1lc3RhYmxpc2hlZCBtZXRob2RzIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB3aXRoIGJ1bGsgUk5BLXNlcS4KClBzZXVkby1idWxraW5nIGlzIGltcGxlbWVudGVkIHByaW9yIHRvIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIG9uIHNpbmdsZS1jZWxsIGRhdGEgYmVjYXVzZSBpdDoKCi0gUHJvZHVjZXMgbGFyZ2VyIGFuZCBsZXNzIHNwYXJzZSBjb3VudHMsIHdoaWNoIGFsbG93cyB1cyB0byB1c2Ugc3RhbmRhcmQgbm9ybWFsaXphdGlvbiBhbmQgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gbWV0aG9kcyB1c2VkIGJ5IGJ1bGsgUk5BLXNlcS4KLSBDb2xsYXBzZXMgZ2VuZSBleHByZXNzaW9uIGNvdW50cyBieSBzYW1wbGUsIHNvIHRoYXQgc2FtcGxlcywgcmF0aGVyIHRoYW4gY2VsbHMsIHJlcHJlc2VudCByZXBsaWNhdGVzLgotIE1hc2tzIHZhcmlhbmNlIHdpdGhpbiBhIHNhbXBsZSB0byBlbXBoYXNpemUgdmFyaWFuY2UgYWNyb3NzIHNhbXBsZXMuClRoaXMgY2FuIGJlIGJvdGggZ29vZCBhbmQgYmFkIQpNYXNraW5nIGludHJhLXNhbXBsZSB2YXJpYXRpb24gbWVhbnMgeW91IG1pZ2h0IG5vdCBpZGVudGlmeSBnZW5lcyB3aGVyZSBhdmVyYWdlIGV4cHJlc3Npb24gZG9lc24ndCBjaGFuZ2UgYmV0d2VlbiBzYW1wbGVzIGJ1dCB0aGUgZGVncmVlIG9mIGNlbGwtdG8tY2VsbCB2YXJpYXRpb24gZG9lcy4KCkJlZm9yZSB3ZSBhcHBseSBwc2V1ZG8tYnVsa2luZyB0byBvdXIgZGF0YXNldCwgbGV0J3MgbG9vayBhdCBhIHNpbXBsZSBleGFtcGxlIG9mIGhvdyBwc2V1ZG8tYnVsa2luZyB3b3Jrcy4KV2UnbGwgc3RhcnQgYnkgY3JlYXRpbmcgYSBmYWtlIG1hdHJpeCBvZiBjb3VudHMuCgpgYGB7ciBjcmVhdGUgbWF0cml4fQojIGNyZWF0ZSBhbiBleGFtcGxlIGNvdW50cyBtYXRyaXgKY291bnRzX210eCA8LSBtYXRyaXgoCiAgMToxMiwKICBuY29sID0gNCwKICBkaW1uYW1lcyA9IGxpc3QoCiAgICBjKCJnZW5lQSIsICJnZW5lQiIsICJnZW5lQyIpLAogICAgYygiQS1jZWxsMSIsICJBLWNlbGwyIiwgIkItY2VsbDEiLCAiQi1jZWxsMiIpCiAgKQopCmNvdW50c19tdHgKYGBgCgpOZXh0IHdlIHdpbGwgY3JlYXRlIGEgcHNldWRvLWJ1bGtlZCB2ZXJzaW9uIG9mIHRoaXMgbWF0cml4IHdpdGggb25seSAyIGNvbHVtbnM6IDEgZm9yIGdyb3VwIGBBYCBhbmQgMSBmb3IgZ3JvdXAgYEJgLgpUbyBkbyB0aGlzIHdlIHdpbGwgdXNlIHRoZSBgRGVsYXllZEFycmF5Ojpjb2xzdW0oKWAgZnVuY3Rpb24sIHdoaWNoIGFsbG93cyB1cyB0byBzdW0gdGhlIGNvdW50cyBmb3IgZWFjaCByb3cgYWNyb3NzIGdyb3VwcyBvZiBjb2x1bW5zLgoKYGBge3IgcHNldWRvYnVsayBtYXRyaXgsIGxpdmU9VFJVRX0KIyBkZWZpbmUgdGhlIGdyb3VwIHRoYXQgZWFjaCBjb2x1bW4gYmVsb25ncyB0bwpncm91cHMgPC0gYygiQSIsICJBIiwgIkIiLCAiQiIpCgojIHN1bSBjb3VudHMgYWNyb3NzIGNlbGxzIChjb2x1bW5zKSBieSBncm91cCBsYWJlbApwYl9jb3VudHMgPC0gRGVsYXllZEFycmF5Ojpjb2xzdW0oY291bnRzX210eCwgZ3JvdXBzKQpwYl9jb3VudHMKYGBgCgpMb29raW5nIGF0IHRoaXMgb3V0cHV0LCB5b3Ugc2hvdWxkIHNlZSB0aGF0IHRoZSBvcmlnaW5hbCA0IGNvbHVtbnMgaGF2ZSBiZWVuIGNvbmRlbnNlZCB0byBvbmx5IDIgY29sdW1uczogMSBjb2x1bW4gdG8gcmVwcmVzZW50IGFsbCBjZWxscyBmcm9tIGdyb3VwIGBBYCwgYW5kIDEgY29sdW1uIHRvIHJlcHJlc2VudCBhbGwgY2VsbHMgZnJvbSBncm91cCBgQmAuCgpOb3cgdGhlIGFjdHVhbCBwc2V1ZG8tYnVsa2luZyBmb3Igb3VyIGRhdGFzZXQhCgpXZSB3aWxsIHVzZSB0aGUgW2BzY3V0dGxlOjphZ2dyZWdhdGVBY3Jvc3NDZWxscygpYCBmdW5jdGlvbl0oaHR0cHM6Ly9yZHJyLmlvL2dpdGh1Yi9MVExBL3NjdXR0bGUvbWFuL2FnZ3JlZ2F0ZUFjcm9zc0NlbGxzLmh0bWwpIHRvIHBzZXVkby1idWxrIG91ciBkYXRhc2V0LgpUaGlzIGZ1bmN0aW9uIHRha2VzIGFzIGlucHV0IGFuIFNDRSBvYmplY3QgYW5kIHRoZSBncm91cGluZyBhc3NpZ25tZW50cyBmb3IgZWFjaCBjZWxsLgpUaGUgb3V0cHV0IHdpbGwgYmUgYW4gU0NFIG9iamVjdCB0aGF0IGNvbnRhaW5zIG9ubHkgdGhlIHBzZXVkby1idWxrZWQgY291bnRzIGZvciBhbGwgZ2VuZXMgYWNyb3NzIGFsbCBzcGVjaWZpZWQgZ3JvdXBzLCByYXRoZXIgdGhhbiBhY3Jvc3MgYWxsIGNlbGxzLgpXZSBjYW4gdGhlbiBzdWJzZXQgdGhpcyBTQ0UgdG8ganVzdCBpbmNsdWRlIG91ciBjZWxsIHR5cGUgb2YgaW50ZXJlc3QgKHR1bW9yIG15b2JsYXN0cykgZm9yIGlucHV0IHRvIHRoZSBERSBhbmFseXNpcy4KCldlIGNhbiBwc2V1ZG8tYnVsayB1c2luZyBhbnkgZ3JvdXBpbmcgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbi4KRm9yIHJpZ2h0IG5vdywgd2UgYXJlIGludGVyZXN0ZWQgaW4gbG9va2luZyBhdCBnZW5lIGV4cHJlc3Npb24gYWNyb3NzIGNlbGwgdHlwZXMsIHNvIHdlIHdhbnQgdG8gZ3JvdXAgdGhlIHBzZXVkby1idWxrZWQgY291bnRzIG1hdHJpeCBieSBib3RoIGNlbGwgdHlwZSBhbmQgb3JpZ2luYWwgc2FtcGxlLgoKYGBge3IgcHNldWRvYnVsayBzY2V9CiMgZmlyc3Qgc3Vic2V0IHRoZSBjb2xkYXRhCiMgdG8gb25seSBoYXZlIHRoZSBjb2x1bW5zIHdlIGNhcmUgYWJvdXQgZm9yIHBzZXVkby1idWxraW5nCnBiX2dyb3VwcyA8LSBjb2xEYXRhKHJtc19zY2UpWywgYygiY2VsbHR5cGVfYnJvYWQiLCAic2FtcGxlIildCgojIGNyZWF0ZSBhIG5ldyBTQ0Ugb2JqZWN0IHRoYXQgY29udGFpbnMKIyB0aGUgcHNldWRvLWJ1bGtlZCBjb3VudHMgYWNyb3NzIHRoZSBwcm92aWRlZCBncm91cHMKcGJfc2NlIDwtIHNjdXR0bGU6OmFnZ3JlZ2F0ZUFjcm9zc0NlbGxzKAogIHJtc19zY2UsCiAgaWQgPSBwYl9ncm91cHMKKQoKIyBjb2x1bW4gbmFtZXMgYXJlbid0IGF1dG9tYXRpY2FsbHkgYWRkZWQgdG8gdGhlIHBzZXVkby1idWxrZWQgc2NlLAojIHNvIGxldCdzIGFkZCB0aGVtIGluCmNvbG5hbWVzKHBiX3NjZSkgPC0gZ2x1ZTo6Z2x1ZSgKICAie3BiX3NjZSRjZWxsdHlwZV9icm9hZH1fe3BiX3NjZSRzYW1wbGV9IgopCgpwYl9zY2UKYGBgCgpIb3cgZG9lcyB0aGUgbmV3IHBzZXVkby1idWxrZWQgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBsb29rIGRpZmZlcmVudD8KSG93IG1hbnkgY29sdW1ucyBkb2VzIGl0IGhhdmU/CgpMZXQncyB0YWtlIGEgbG9vayBhdCB3aGF0IHRoZSBgY29sRGF0YWAgbG9va3MgbGlrZSBpbiB0aGUgcHNldWRvLWJ1bGtlZCBTQ0Ugb2JqZWN0LgoKYGBge3IgcHNldWRvYnVsayBjb2xEYXRhLCBsaXZlPVRSVUV9CiMgbm90ZSB0aGUgbmV3IGNvbHVtbiB3aXRoIG51bWJlciBvZiBjZWxscyBwZXIgZ3JvdXAKaGVhZChjb2xEYXRhKHBiX3NjZSkpIHw+CiAgYXMuZGF0YS5mcmFtZSgpCmBgYAoKWW91IHNob3VsZCBzZWUgdGhhdCBjb2x1bW5zIHN1Y2ggYXMgYHN1bWAsIGBkZXRlY3RlZGAsIGBzdWJzZXRzX21pdG9fc3VtYCwgYW5kIG90aGVyIGNvbHVtbnMgdGhhdCB0eXBpY2FsbHkgY29udGFpbiBwZXIgY2VsbCBRQyBzdGF0aXN0aWNzIG5vdyBjb250YWluIGBOQWAgcmF0aGVyIHRoYW4gbnVtZXJpYyB2YWx1ZXMuClRoaXMgaXMgYmVjYXVzZSB0aGVzZSB2YWx1ZXMgd2VyZSBpbml0aWFsbHkgY2FsY3VsYXRlZCBvbiBhIHBlciBjZWxsIGxldmVsICh3ZSBkaWQgdGhpcyB1c2luZyBgc2N1dHRsZTo6YWRkUGVyQ2VsbFFDTWV0cmljcygpYCksIGJ1dCB3ZSBubyBsb25nZXIgaGF2ZSBhIHNpbmdsZSBjb2x1bW4gcGVyIGNlbGwuCkluc3RlYWQsIGVhY2ggY29sdW1uIG5vdyByZXByZXNlbnRzIGEgX2dyb3VwXyBvZiBjZWxscywgaW4gdGhpcyBjYXNlIGNvbXByaXNlZCBvZiBjZWxscyBvZiBhIGdpdmVuIGNlbGwgdHlwZSBhbmQgc2FtcGxlIGNvbWJpbmF0aW9uLgpUaGVyZWZvcmUsIHRoZSB2YWx1ZXMgdGhhdCB3ZSBjYWxjdWxhdGVkIG9uIGEgcGVyLWNlbGwgbGV2ZWwgYXJlIG5vIGxvbmdlciBhcHBsaWNhYmxlIHRvIHRoaXMgcHNldWRvLWJ1bGtlZCBTQ0Ugb2JqZWN0LgoKWW91IHNob3VsZCBhbHNvIHNlZSBhIG5ldyBjb2x1bW4gdGhhdCB3YXNuJ3QgcHJlc2VudCBwcmV2aW91c2x5LCB0aGUgYG5jZWxsc2AgY29sdW1uLgpUaGlzIGNvbHVtbiB3YXMgYWRkZWQgZHVyaW5nIHBzZXVkby1idWxraW5nIGFuZCBpbmRpY2F0ZXMgdGhlIHRvdGFsIG51bWJlciBvZiBjZWxscyB0aGF0IHdlcmUgc3VtbWVkIHRvZ2V0aGVyIHRvIGZvcm0gZWFjaCBjb2x1bW4gb2YgdGhlIFNDRSBvYmplY3QuCgpCZWZvcmUgd2UgcHJvY2VlZCB3ZSB3aWxsIHdhbnQgdG8gZmlsdGVyIG91dCBhbnkgY29sdW1ucyB0aGF0IGhhdmUgYSBsb3cgbnVtYmVyIG9mIGNlbGxzLgpBIGxvdyBudW1iZXIgb2YgY2VsbHMgd2lsbCB1c3VhbGx5IHJlc3VsdCBpbiBzbWFsbCBjb3VudHMgdGhhdCBjYW4gY2F1c2UgaXNzdWVzIHdpdGggdGhlIHN0YXRpc3RpY2FsIGFwcHJveGltYXRpb25zIG1hZGUgZHVyaW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLgpUaGlzIGlzIGVxdWl2YWxlbnQgdG8gZmlsdGVyaW5nIG91dCBhbnkgbGlicmFyaWVzIGluIGJ1bGsgUk5BLXNlcSBhbmFseXNpcyB0aGF0IGhhdmUgbG93IGxpYnJhcnkgc2l6ZXMuCgpXZSBjYW4gc2V0IGEgdGhyZXNob2xkIGZvciB0aGUgbnVtYmVyIG9mIGNlbGxzIHJlcXVpcmVkIHRvIGNvbnRpbnVlIHdpdGggb3VyIGFuYWx5c2lzIGFuZCByZW1vdmUgYW55IGdyb3VwcyB0aGF0IGRvIG5vdCBtZWV0IHRoZSBtaW5pbXVtIHRocmVzaG9sZC4KSGVyZSB3ZSB3aWxsIHVzZSAxMCwgYnV0IHRoZSB0aHJlc2hvbGQgeW91IHVzZSBmb3IgeW91ciBkYXRhc2V0IGNhbiB2YXJ5IGRlcGVuZGluZyBvbiB0aGUgY29tcG9zaXRpb24gb2YgY2VsbCB0eXBlcy4KCmBgYHtyIGZpbHRlciBwc2V1ZG9idWxrLCBsaXZlPVRSVUV9CiMgcmVtb3ZlIGFueSBncm91cHMgd2l0aCBmZXdlciB0aGFuIDEwIGNlbGxzCmZpbHRlcl9wYl9zY2UgPC0gcGJfc2NlWywgcGJfc2NlJG5jZWxscyA+PSAxMF0KYGBgCgpXZSBjYW4gdGhlbiB0YWtlIGEgbG9vayBhbmQgc2VlIGhvdyBtYW55IGNlbGwgdHlwZS1zYW1wbGUgY29sdW1ucyB3ZSByZW1vdmVkLCBpZiBhbnkuCgpgYGB7ciBwcmludCBkaW0sIGxpdmU9VFJVRX0KIyBwcmludCBvdXQgZGltZW5zaW9ucyBvZiB1bmZpbHRlcmVkIHBzZXVkb2J1bGsgc2NlCmRpbShwYl9zY2UpCgojIGRpbWVuc2lvbnMgb2YgZmlsdGVyZWQgcHNldWRvYnVsayBzY2UKZGltKGZpbHRlcl9wYl9zY2UpCmBgYAoKSXQgbG9va3MgbGlrZSB3ZSBvbmx5IGdvdCByaWQgb2Ygb25lIGdyb3VwLgpXZSBjYW4gZG8gYSBxdWljayBjaGVjayB0byBzZWUgd2hpY2ggZ3JvdXAgd2FzIHJlbW92ZWQgYnkgZmluZGluZyB3aGljaCBjb2x1bW4gaXMgbm8gbG9uZ2VyIHByZXNlbnQgaW4gdGhlIGZpbHRlcmVkIG9iamVjdC4KCmBgYHtyIHJlbW92ZWQgY29sdW1ucywgbGl2ZT1UUlVFfQojIGZpbmQgcmVtb3ZlZCBjb2x1bW5zCnJlbW92ZWRfY29scyA8LSAhKGNvbG5hbWVzKHBiX3NjZSkgJWluJSBjb2xuYW1lcyhmaWx0ZXJfcGJfc2NlKSkKCiMgcHJpbnQgb3V0IG1pc3NpbmcgY29sdW1ucwpjb2xuYW1lcyhwYl9zY2UpW3JlbW92ZWRfY29sc10KYGBgCgpUaGUgbGFzdCBzdGVwIHdlIHdhbnQgdG8gZG8gdG8gcHJlcGFyZSBvdXIgZGF0YXNldCBmb3IgREUgaXMgdG8gc3Vic2V0IHRoZSBwc2V1ZG8tYnVsa2VkIFNDRSBvYmplY3QgdG8gY29udGFpbiBvbmx5IHRoZSBjZWxsIHR5cGUgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBjb21wYXJpbmcgYWNyb3NzIHRoZSB0d28gUk1TIHN1YnR5cGVzLgpBcyBtZW50aW9uZWQgcHJldmlvdXNseSwgd2UgYXJlIHNwZWNpZmljYWxseSBpbnRlcmVzdGVkIGluIHRoZSBgVHVtb3JfTXlvYmxhc3RgIGNlbGwgdHlwZS4KCmBgYHtyIGZpbHRlciBjZWxsdHlwZX0KIyBsb2dpY2FsIHZlY3RvciBpbmRpY2F0aW5nIGlmIGNlbGxzIGFyZSB0dW1vciBteW9ibGFzdCBvciBub3QKbXlvYmxhc3RfY2VsbHMgPC0gZmlsdGVyX3BiX3NjZSRjZWxsdHlwZV9icm9hZCA9PSAiVHVtb3JfTXlvYmxhc3QiCgojIGNyZWF0ZSBhIG5ldyBzY2Ugd2l0aCBvbmx5IHRoZSB0dW1vciBteW9ibGFzdHMKdHVtb3JfbXlvYmxhc3Rfc2NlIDwtIGZpbHRlcl9wYl9zY2VbLCBteW9ibGFzdF9jZWxsc10KYGBgCgpBZnRlciBmaWx0ZXJpbmcgZm9yIG91ciBjZWxsIHR5cGUgb2YgaW50ZXJlc3Qgd2Ugc2hvdWxkIGhhdmUgYSBkYXRhc2V0IHdpdGggNiBjb2x1bW5zLCAxIGZvciBlYWNoIGdyb3VwIG9mIGBUdW1vcl9NeW9ibGFzdGAgY2VsbHMgaW4gZWFjaCBvZiBvdXIgNiBzYW1wbGVzLgoKIyMjIFBlcmZvcm0gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gd2l0aCBgREVTZXEyYAoKTm93IHdlIHdpbGwgdXNlIHRoZSBgREVTZXEyYCBwYWNrYWdlIHRvIHBlcmZvcm0gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gKERFKSBhbmFseXNpcyBvbiBvdXIgcHNldWRvLWJ1bGtlZCBTQ0Ugb2JqZWN0LgpGcm9tIHRoaXMgcG9pbnQsIHdlIGNhbiBwcm9jZWVkIGluIHRoZSBzYW1lIHdheSB3ZSB3b3VsZCBpZiB3ZSBoYWQgYSBidWxrIFJOQS1zZXEgZGF0YXNldCB3aXRoIDYgc2FtcGxlcy4KV2Ugd2lsbCBzdGFydCB3aXRoIHRoZSB1bm5vcm1hbGl6ZWQgcmF3IGNvdW50cyBpbiB0aGUgYGNvdW50c2AgYXNzYXkgb2YgdGhlIHBzZXVkby1idWxrZWQgU0NFIGFuZCBkbyB0aGUgZm9sbG93aW5nIHdpdGggYERFU2VxMmA6CgotIENyZWF0ZSBhIGBERVNlcURhdGFTZXRgIG9iamVjdAotIE5vcm1hbGl6ZSBhbmQgbG9nIHRyYW5zZm9ybSB0aGUgY291bnRzIGRhdGEKLSBFc3RpbWF0ZSBkaXNwZXJzaW9ucyBhbmQgc2hyaW5rIGVzdGltYXRlcwotIEZpdCBhIG5lZ2F0aXZlIGJpbm9taWFsIG1vZGVsIGFuZCBwZXJmb3JtIGh5cG90aGVzaXMgdGVzdGluZyB1c2luZyBXYWxkIHN0YXRpc3RpY3MKCllvdSBjYW4gYWxzbyByZWZlciB0byBvdXIgW21hdGVyaWFscyBmcm9tIG91ciBwcmV2aW91cyB3b3Jrc2hvcHMgY292ZXJpbmcgYnVsayBSTkEtc2VxXShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS90cmFpbmluZy1tb2R1bGVzL3RyZWUvbWFzdGVyL1JOQS1zZXEjcmVhZG1lKSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB1c2luZyBgREVTZXFgLgoKIyMjIyBDcmVhdGUgdGhlIGBERVNlcURhdGFTZXRgIG9iamVjdAoKVG8gY3JlYXRlIHRoZSBgREVTZXFEYXRhU2V0YCBvYmplY3Qgd2Ugd2lsbCBuZWVkIHRoZSB1bm5vcm1hbGl6ZWQgY291bnRzIG1hdHJpeCwgdGhlIG1ldGFkYXRhIGFzc29jaWF0ZWQgd2l0aCB0aGUgc2FtcGxlcywgYW5kIGEgZGVzaWduIGZvcm11bGEuClRoZSBmaXJzdCB0d28gaXRlbXMgYXJlIGFscmVhZHkgc3RvcmVkIGluIG91ciBTQ0Ugb2JqZWN0LCBzbyB3ZSBjYW4gY3JlYXRlIGEgYERFU2VxRGF0YVNldGAgb2JqZWN0IGRpcmVjdGx5IGZyb20gdGhhdCBvYmplY3QgdXNpbmcgdGhlIGBERVNlcURhdGFTZXQoKWAgZnVuY3Rpb24uClRoZSBkZXNpZ24gZm9ybXVsYSBpcyB1c2VkIHRvIGluZGljYXRlIHdoaWNoIGNvbHVtbnMgb2YgdGhlIG1ldGFkYXRhIG5lZWQgdG8gYmUgY29uc2lkZXJlZCBpbiB0aGUgREUgY29tcGFyaXNvbi4KRm9yIG91ciBleHBlcmltZW50IHdlIGFyZSBjb21wYXJpbmcgZ2VuZSBleHByZXNzaW9uIGJldHdlZW4gZGlmZmVyZW50IFJNUyBzdWJ0eXBlcy4KVGhlIHN1YnR5cGUgaW5mb3JtYXRpb24gaXMgc3RvcmVkIGluIHRoZSBgZGlhZ25vc2lzX2dyb3VwYCBjb2x1bW4gb2YgdGhlIGBjb2xEYXRhYCBpbiB0aGUgcHNldWRvLWJ1bGtlZCBTQ0UuCgpgYGB7ciBkZXNlcSBvYmplY3QsIGxpdmU9VFJVRX0KIyBzZXQgdXAgdGhlIGRlc2VxIG9iamVjdCwgZ3JvdXAgYnkgZGlhZ25vc2lzCmRlc2VxX29iamVjdCA8LSBERVNlcTI6OkRFU2VxRGF0YVNldCgKICB0dW1vcl9teW9ibGFzdF9zY2UsCiAgZGVzaWduID0gfmRpYWdub3Npc19ncm91cAopCmBgYAoKVGhlIHBzZXVkby1idWxrZWQgU0NFIG9iamVjdCBjb250YWlucyBvbmx5IG9uZSBhc3NheTogdGhlIGBjb3VudHNgIGFzc2F5LgpUaGlzIGlzIGJlY2F1c2UgYERFU2VxMmAgZXhwZWN0cyByYXcgY291bnRzLgpXaGVuIHdlIHJ1biBgREVTZXEyYCBvbiBvdXIgZGF0YXNldCwgcmF3IGNvdW50cyB3aWxsIGZpcnN0IGJlIG5vcm1hbGl6ZWQgdXNpbmcgc2l6ZSBmYWN0b3JzIHRvIGFjY291bnQgZm9yIGRpZmZlcmVuY2VzIGluIHRvdGFsIHNhbXBsZSBjb3VudHMuClRoZXJlZm9yZSB3ZSBkb24ndCBoYXZlIHRvIGRvIGFueSBub3JtYWxpemF0aW9uIG9uIG91ciBvd24g4oCTIHdlJ2xsIGxldCBgREVTZXEyYCBkbyBhbGwgdGhlIHdvcmsgZm9yIHVzLgoKSG93ZXZlciwgYmVmb3JlIHdlIGRpdmUgaW50byBERSBhbmFseXNpcywgd2UgY2FuIGRvIHNvbWUgaW5pdGlhbCBleHBsb3JhdGlvbiBhbmQgdmlzdWFsaXphdGlvbiBvZiBvdXIgZGF0YSB0byBzZWUgaWYgb3VyIHNhbXBsZXMgc2VwYXJhdGUgYnkgb3VyIGtub3duIGZhY3RvciBvZiBpbnRlcmVzdCwgUk1TIHN1YnR5cGUuCkluIHBhcnRpY3VsYXIsIHdlIGNhbiB1c2UgcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyAoUENBKSBvZiBvdXIgcHNldWRvLWJ1bGtlZCBkYXRhc2V0IHRvIHZpc3VhbGl6ZSBhbnkgdmFyaWF0aW9uIGJldHdlZW4gc2FtcGxlcy4KSWYgdGhlcmUgaXMgdmFyaWF0aW9uIGJldHdlZW4gUk1TIHN1YnR5cGVzLCB3ZSBleHBlY3QgdGhlaXIgcmVzcGVjdGl2ZSBzYW1wbGVzIHRvIHNlcGFyYXRlIGluIFBDIHNwYWNlLCBsaWtlbHkgaW5kaWNhdGluZyBwcmVzZW5jZSBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuCldlIGNhbiBldmFsdWF0ZSB0aGlzIGJ5IHBsb3R0aW5nIFBDMSBhbmQgUEMyLgoKSW4gb3JkZXIgdG8gY3JlYXRlIG91ciBQQ0EgcGxvdCwgd2Ugd2lsbCBmaXJzdCBuZWVkIHRvIG5vcm1hbGl6ZSBvdXIgZGF0YSB0byBhY2NvdW50IGZvciBhbnkgdGVjaG5pY2FsIHZhcmlhdGlvbnMgYWNyb3NzIHNhbXBsZXMuCkFzIGEgcmVtaW5kZXIsIHRoaXMgaXMgTk9UIHJlcXVpcmVkIGZvciBydW5uaW5nIGBERVNlcTJgIGFuYWx5c2lzOyB3ZSBhcmUganVzdCB1c2luZyBpdCB0byB2aXN1YWxpemUgb3VyIGRhdGEgcHJpb3IgdG8gREUgYW5hbHlzaXMuCgpgYGB7ciBub3JtYWxpemV9CiMgZXN0aW1hdGUgc2l6ZSBmYWN0b3JzIGZpcnN0CmRlc2VxX29iamVjdCA8LSBERVNlcTI6OmVzdGltYXRlU2l6ZUZhY3RvcnMoZGVzZXFfb2JqZWN0KQoKIyBub3JtYWxpemUgYW5kIGxvZyB0cmFuc2Zvcm0gdG8gdXNlIGZvciB2aXN1YWxpemF0aW9uCm5vcm1hbGl6ZWRfb2JqZWN0IDwtIERFU2VxMjo6cmxvZygKICBkZXNlcV9vYmplY3QsCiAgYmxpbmQgPSBUUlVFCikKbm9ybWFsaXplZF9vYmplY3QKYGBgCgpXZSBub3cgaGF2ZSBhIG5vcm1hbGl6ZWQgYW5kIHRyYW5zZm9ybWVkIG9iamVjdCB0aGF0IGNhbiBiZSBkaXJlY3RseSBpbnB1dCB0byB0aGUgYERFU2VxMjo6cGxvdFBDQSgpYCBmdW5jdGlvbiwgd2hpY2ggd2lsbCBib3RoIGNhbGN1bGF0ZSBhbmQgcGxvdCB0aGUgUEMgcmVzdWx0cy4KCmBgYHtyIHBsb3RQQ0EsIGxpdmU9VFJVRX0KREVTZXEyOjpwbG90UENBKG5vcm1hbGl6ZWRfb2JqZWN0LCBpbnRncm91cCA9ICJkaWFnbm9zaXNfZ3JvdXAiKQpgYGAKCkFzIGV4cGVjdGVkIHdlIHNlZSB0aGF0IHNhbXBsZXMgZ3JvdXAgdG9nZXRoZXIgYmFzZWQgb24gUk1TIHN1YnR5cGUgYW5kIGFyZSBzZXBhcmF0ZWQgYWxvbmcgdGhlIFBDMSBheGlzLCB0aGUgUEMgY29udHJpYnV0aW5nIHRoZSBoaWdoZXN0IGFtb3VudCBvZiB2YXJpYXRpb24uCgojIyMjIFJ1biBgREVTZXFgCgpXZSdsbCBub3cgdXNlIHRoZSBjb252ZW5pZW5jZSBmdW5jdGlvbiBgREVTZXEoKWAgdG8gcGVyZm9ybSBvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMuClRoaXMgZnVuY3Rpb24gY2FsY3VsYXRlcyBub3JtYWxpemF0aW9uIGZhY3RvcnMsIGVzdGltYXRlcyBnZW5lLXdpc2UgZGlzcGVyc2lvbnMsIGZpdHMgYSBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbCBhbmQgcGVyZm9ybXMgaHlwb3RoZXNpcyB0ZXN0aW5nIHVzaW5nIFdhbGQgc3RhdGlzdGljcy4KCmBgYHtyIGRlc2VxLCBsaXZlPVRSVUV9CiMgcnVuIERFU2VxCmRlc2VxX29iamVjdCA8LSBERVNlcTI6OkRFU2VxKGRlc2VxX29iamVjdCkKYGBgCgpXZSBjYW4gZXZhbHVhdGUgaG93IHdlbGwgdGhlIG1vZGVsIGZpdCBvdXIgZGF0YSBieSBsb29raW5nIGF0IHRoZSBkaXNwZXJzaW9uIGVzdGltYXRlcy4KV2UgZXhwZWN0IHRvIHNlZSB0aGUgZGlzcGVyc2lvbiBlc3RpbWF0ZXMgZGVjcmVhc2UgYXMgbWVhbnMgYXJlIGluY3JlYXNpbmcgYW5kIGZvbGxvdyB0aGUgbGluZSBvZiBiZXN0IGZpdC4KCmBgYHtyIHBsb3QgZGlzcGVyc2lvbiwgbGl2ZT1UUlVFfQpwbG90RGlzcEVzdHMoZGVzZXFfb2JqZWN0KQpgYGAKCk5vdyB3ZSBjYW4gZXh0cmFjdCB0aGUgcmVzdWx0cyBmcm9tIHRoZSBvYmplY3QsIHNwZWNpZnlpbmcgdGhlIHAtdmFsdWUgdGhyZXNob2xkIHRoYXQgd2Ugd291bGQgbGlrZSB0byB1c2UuCgpgYGB7ciByZXN1bHRzLCBsaXZlPVRSVUV9CiMgZXh0cmFjdCB0aGUgcmVzdWx0cyBhcyBhIERhdGFGcmFtZQpkZXNlcV9yZXN1bHRzIDwtIERFU2VxMjo6cmVzdWx0cyhkZXNlcV9vYmplY3QsIGFscGhhID0gMC4wNSkKYGBgCgpCdXQgd2UgYXJlbid0IGRvbmUgeWV0IQoKVGhlIGVzdGltYXRlcyBvZiBsb2cyIGZvbGQgY2hhbmdlIGNhbGN1bGF0ZWQgYnkgYERFU2VxKClgIGFyZSBub3QgY29ycmVjdGVkIGZvciBleHByZXNzaW9uIGxldmVsLgpUaGlzIG1lYW5zIHRoYXQgd2hlbiBjb3VudHMgYXJlIHNtYWxsLCB3ZSBhcmUgbGlrZWx5IHRvIGVuZCB1cCB3aXRoIHNvbWUgbGFyZ2UgZm9sZCBjaGFuZ2UgdmFsdWVzIHRoYXQgb3ZlcmVzdGltYXRlIHRoZSB0cnVlIGV4dGVudCBvZiB0aGUgY2hhbmdlIGJldHdlZW4gY29uZGl0aW9ucy4KCldlIGNhbiBjb3JyZWN0IHRoaXMgYnkgYXBwbHlpbmcgYSAic2hyaW5rYWdlIiBwcm9jZWR1cmUsIHdoaWNoIHdpbGwgYWRqdXN0IGxhcmdlIHZhbHVlcyB3aXRoIHNtYWxsIGNvdW50cyBkb3dud2FyZCwgd2hpbGUgcHJlc2VydmluZyB2YWx1ZXMgd2l0aCBsYXJnZXIgY291bnRzLCB3aGljaCBhcmUgbGlrZWx5IHRvIGJlIG1vcmUgYWNjdXJhdGUuCgpUbyBkbyB0aGlzLCB3ZSB3aWxsIHVzZSB0aGUgYGxmY1NocmluaygpYCBmdW5jdGlvbiwgYnV0IGZpcnN0IHdlIG5lZWQgdG8ga25vdyB0aGUgbmFtZSBhbmQvb3IgcG9zaXRpb24gb2YgdGhlICJjb2VmZmljaWVudCIgdGhhdCB3YXMgY2FsY3VsYXRlZCBieSBgREVTZXEoKWAsIHdoaWNoIHdlIGNhbiBkbyB3aXRoIHRoZSBgcmVzdWx0c05hbWVzKClgIGZ1bmN0aW9uLgoKYGBge3IgY29lZmZpY2llbnQsIGxpdmU9VFJVRX0KIyBpZGVudGlmeSBwb3NpdGlvbiBvZiBjb2VmZmljaWVudApERVNlcTI6OnJlc3VsdHNOYW1lcyhkZXNlcV9vYmplY3QpCmBgYAoKCmBgYHtyIHNocmlua2FnZX0KIyBhcHB5bHkgbG9nRkMgc2hyaW5rYWdlIHVzaW5nIHRoZSBkZWZhdWx0IG1vZGVsCnNocmlua19yZXN1bHRzIDwtIERFU2VxMjo6bGZjU2hyaW5rKAogIGRlc2VxX29iamVjdCwKICByZXMgPSBkZXNlcV9yZXN1bHRzLAogIGNvZWYgPSAyLAogIHR5cGUgPSAiYXBlZ2xtIgopCmhlYWQoc2hyaW5rX3Jlc3VsdHMpCmBgYAoKSWYgeW91IGxvb2sgYXQgb3VyIGBzaHJpbmtfcmVzdWx0c2Agb2JqZWN0LCB3ZSBzZWUgdGhhdCB0aGUgZ2VuZXMgYXJlIGxhYmVsZWQgd2l0aCB0aGUgRW5zZW1ibCBnZW5lIGlkZW50aWZpZXJzLCBhcyB0aG9zZSB3ZXJlIHRoZSByb3cgbmFtZXMgb2YgdGhlIHBzZXVkby1idWxrZWQgU0NFIHdlIHVzZWQgYXMgaW5wdXQgdG8gYnVpbGQgb3VyIGBERVNlcTJgIG9iamVjdC4KQWx0aG91Z2ggc29tZSBvZiB1cyBtYXkgaGF2ZSBhbGwgb2YgdGhlIGlkZW50aWZpZXJzIG1lbW9yaXplZCBieSBoZWFydCwgaXQgY2FuIGJlIHVzZWZ1bCB0byBoYXZlIGEgaHVtYW4gcmVhZGFibGUgc3ltYm9sIGluIG91ciByZXN1bHRzLgpCZWZvcmUgd2Ugc2F2ZSB0aGUgcmVzdWx0cyBhcyBhIGZpbGUsIHdlIHdpbGwgZ3JhYiB0aGUgZ2VuZSBzeW1ib2xzIGZyb20gdGhlIGByb3dEYXRhYCBvZiBvdXIgb3JpZ2luYWwgU0NFIG9iamVjdCBhbmQgYWRkIHRoZW0gYXMgYSBuZXcgY29sdW1uLgoKYGBge3IgYWRkIGdlbmUgc3ltYm9sfQpkZXNlcV9yZXN1bHRzIDwtIHNocmlua19yZXN1bHRzIHw+CiAgIyBkaXJlY3RseSBhZGQgRW5zZW1ibCBpZCBhcyBhIGNvbHVtbgogICMgY29udmVydGluZyByZXN1bHRzIGludG8gYSBkYXRhIGZyYW1lCiAgdGliYmxlOjphc190aWJibGUocm93bmFtZXMgPSAiZW5zZW1ibF9pZCIpCgojIGNvbnZlcnQgcm93ZGF0YSB0byBkYXRhIGZyYW1lCnNjZV9yb3dkYXRhX2RmIDwtIHJvd0RhdGEodHVtb3JfbXlvYmxhc3Rfc2NlKSB8PgogICMgY3JlYXRlIGEgY29sdW1uIHdpdGggcm93bmFtZXMgc3RvcmVkIGFzIGVuc2VtYmwgaWQKICAjIHVzZSBmb3Igam9pbmluZyB3aXRoIGRlc2VxIHJlc3VsdHMKICB0aWJibGU6OmFzX3RpYmJsZShyb3duYW1lcyA9ICJlbnNlbWJsX2lkIikKCiMgY29tYmluZSBkZXNlcSByZXN1bHRzIHdpdGggcm93ZGF0YSBieSBlbnNlbWJsIGlkCmRlc2VxX3Jlc3VsdHMgPC0gZGVzZXFfcmVzdWx0cyB8PgogIGRwbHlyOjpsZWZ0X2pvaW4oc2NlX3Jvd2RhdGFfZGYsIGJ5ID0gImVuc2VtYmxfaWQiKQoKaGVhZChkZXNlcV9yZXN1bHRzKQpgYGAKCldlIGNhbiBzYXZlIHRoZSBuZXcgZGF0YSBmcmFtZSB0aGF0IHdlIGhhdmUgY3JlYXRlZCB3aXRoIHRoZSBFbnNlbWJsIGlkZW50aWZpZXJzLCBnZW5lIHN5bWJvbHMsIGFuZCB0aGUgYERFU2VxMmAgcmVzdWx0cyBhcyBhIHRhYiBzZXBhcmF0ZWQgKGB0c3ZgKSBmaWxlLgoKYGBge3Igc2F2ZSBkZXNlcSwgbGl2ZT1UUlVFfQojIHNhdmUgb3VyIHJlc3VsdHMgYXMgdHN2CnJlYWRyOjp3cml0ZV90c3YoZGVzZXFfcmVzdWx0cywgZGVzZXFfb3V0cHV0X2ZpbGUpCmBgYAoKTmV4dCwgd2Ugd2lsbCB0YWtlIGEgbG9vayBhdCBob3cgbWFueSBnZW5lcyBhcmUgc2lnbmlmaWNhbnQuCkhlcmUgd2Ugd2lsbCB3YW50IHRvIHVzZSB0aGUgYWRqdXN0ZWQgcC12YWx1ZSwgZm91bmQgaW4gdGhlIGBwYWRqYCBjb2x1bW4gb2YgdGhlIHJlc3VsdHMsIGFzIHRoaXMgYWNjb3VudHMgZm9yIG11bHRpcGxlIHRlc3QgY29ycmVjdGlvbi4KCmBgYHtyIHNpZ25pZmljYW50IHJlc3VsdHMsIGxpdmU9VFJVRX0KIyBmaXJzdCBsb29rIGF0IHRoZSBzaWduaWZpY2FudCByZXN1bHRzCmRlc2VxX3Jlc3VsdHNfc2lnIDwtIGRlc2VxX3Jlc3VsdHMgfD4KICAjIGZpbHRlciBiYXNlZCBvbiBhZGp1c3RlZCBwdmFsdWUKICBkcGx5cjo6ZmlsdGVyKHBhZGogPD0gMC4wNSkKCmhlYWQoZGVzZXFfcmVzdWx0c19zaWcpCmBgYAoKCiMjIyBFeHBsb3JpbmcgdGhlIGlkZW50aWZpZWQgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzCgpOb3cgdGhhdCB3ZSBoYXZlIGlkZW50aWZpZWQgYSBzZXQgb2YgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIHRoZSB0dW1vciBteW9ibGFzdHMgYmV0d2VlbiBBUk1TIGFuZCBFUk1TIHN1YnR5cGVzLCBsZXRzIGFjdHVhbGx5IHRha2UgYSBsb29rIGF0IHRoZW0gYW5kIHNlZSBpZiB3ZSBjYW4gbWFrZSBzb21lIGluZm9ybWF0aXZlIHBsb3RzLgpUaGUgZmlyc3QgcGxvdCB3ZSdsbCBtYWtlIGlzIGEgdm9sY2FubyBwbG90IHVzaW5nIHRoZSBbYEVuaGFuY2VkVm9sY2Fub2AgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL2tldmluYmxpZ2hlL0VuaGFuY2VkVm9sY2FubykuClRoaXMgcGFja2FnZSBhdXRvbWF0aWNhbGx5IGNvbG9ycyB0aGUgcG9pbnRzIGJ5IGN1dG9mZnMgZm9yIGJvdGggc2lnbmlmaWNhbmNlIGFuZCBmb2xkIGNoYW5nZSBhbmQgbGFiZWxzIG1hbnkgb2YgdGhlIHNpZ25pZmljYW50IGdlbmVzIChzdWJqZWN0IHRvIHNwYWNpbmcpLgpgRW5oYW5jZWRWb2xjYW5vYCBoYXMgbWFueSwgbWFueSBvcHRpb25zLCB3aGljaCBpcyBhIGdvb2QgdGhpbmcgaWYgeW91IGRvbid0IGxpa2UgYWxsIG9mIGl0cyBkZWZhdWx0IHNldHRpbmdzLgpFdmVuIGJldHRlciwgaXQgb3V0cHV0cyBhIGBnZ3Bsb3QyYCBvYmplY3QsIHNvIGlmIHdlIHdhbnQgdG8gY3VzdG9taXplIHRoZSBwbG90IGZ1cnRoZXIsIHdlIGNhbiB1c2UgdGhlIHNhbWUgYGdncGxvdDJgIGNvbW1hbmRzIHdlIGhhdmUgdXNlZCBiZWZvcmUuCgpgYGB7ciB2b2xjYW5vfQpFbmhhbmNlZFZvbGNhbm86OkVuaGFuY2VkVm9sY2FubygKICBkZXNlcV9yZXN1bHRzLAogIHggPSAibG9nMkZvbGRDaGFuZ2UiLCAjIGZvbGQgY2hhbmdlIHN0YXRpc3RpYyB0byBwbG90CiAgeSA9ICJwdmFsdWUiLCAjIHNpZ25pZmljYW5jZSB2YWx1ZXMKICBsYWIgPSBkZXNlcV9yZXN1bHRzJGdlbmVfc3ltYm9sLCAjIGxhYmVscyBmb3IgcG9pbnRzCiAgcEN1dG9mZiA9IDFlLTA1LCAjIHAgdmFsdWUgY3V0b2ZmIChkZWZhdWx0KQogIEZDY3V0b2ZmID0gMSwgIyBmb2xkIGNoYW5nZSBjdXRvZmYgKGRlZmF1bHQpCiAgdGl0bGUgPSBOVUxMLCAjIG5vIHRpdGxlCiAgc3VidGl0bGUgPSBOVUxMLCAjIG9yIHN1YnRpdGxlCiAgY2FwdGlvbiA9IE5VTEwsICMgb3IgY2FwdGlvbgogIGRyYXdDb25uZWN0b3JzID0gVFJVRSwgIyBhZGQgc29tZSBmdW4gYXJyb3dzCiAgbGFiU2l6ZSA9IDMgIyBzbWFsbGVyIGxhYmVscwopICsKICAjIGNoYW5nZSB0aGUgb3ZlcmFsbCB0aGVtZQogIHRoZW1lX2J3KCkgKwogICMgbW92ZSB0aGUgbGVnZW5kIHRvIHRoZSBib3R0b20KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKYGBgCgoKV2UgY2FuIGFsc28gcmV0dXJuIGJhY2sgdG8gdGhlIFNDRSBvYmplY3QgdGhhdCB3ZSB1c2VkIHRvIGNyZWF0ZSBvdXIgcHNldWRvLWJ1bGtlZCBTQ0UgYW5kIGxvb2sgYXQgZ2VuZSBleHByZXNzaW9uIG9mIHNvbWUgb2YgdGhlIHNpZ25pZmljYW50IGdlbmVzLgpXZSBjYW4gY3JlYXRlIFVNQVAgcGxvdHMgYXMgd2UgZGlkIHByZXZpb3VzbHksIGJ1dCBpbnN0ZWFkIG9mIGxhYmVsaW5nIGVhY2ggY2VsbCB3aXRoIG1ldGFkYXRhLCB3ZSBjYW4gY29sb3IgY2VsbHMgYnkgYSBzcGVjaWZpZWQgZ2VuZSdzIGV4cHJlc3Npb24gbGV2ZWxzLgpXZSB3aWxsIGFsc28gdXNlIHNvbWUgb2YgdGhlIGBnZ3Bsb3QyYCBza2lsbHMgd2UgcGlja2VkIHVwIGVhcmxpZXIsIGxpa2UgYGZhY2V0X2dyaWQoKWAgdG8gcGxvdCBjZWxscyBmcm9tIGRpZmZlcmVudCBSTVMgc3VidHlwZXMgc2VwYXJhdGVseS4KVGhpcyBjYW4gaGVscCB1cyB2YWxpZGF0ZSB0aGUgYERFU2VxMmAgcmVzdWx0cyBzbyB0aGF0IHdlIGNhbiB2aXN1YWxpemUgZ2VuZSBleHByZXNzaW9uIGNoYW5nZXMgYWNyb3NzIG91ciBjZWxsIHR5cGUgb2YgaW50ZXJlc3Qgb24gYSBzaW5nbGUtY2VsbCBsZXZlbC4KCmBgYHtyIGV4cHJlc3Npb24gdW1hcCwgbGl2ZT1UUlVFfQojIGZpbHRlciB0byBqdXN0IG15b2JsYXN0IGNlbGxzIGFuZCByZW1vdmUgYW55IE5BJ3MgYmVmb3JlIHBsb3R0aW5nCm15b2JsYXN0X2NvbWJpbmVkX3NjZSA8LSBybXNfc2NlWywgd2hpY2gocm1zX3NjZSRjZWxsdHlwZV9icm9hZCA9PSAiVHVtb3JfTXlvYmxhc3QiKV0KCiMgcGxvdCBQVFBSVCAoRU5TRzAwMDAwMTk2MDkwKSBleHByZXNzaW9uIGluIEFSTVMgdnMuIEVSTVMKc2NhdGVyOjpwbG90UmVkdWNlZERpbSgKICBteW9ibGFzdF9jb21iaW5lZF9zY2UsCiAgZGltcmVkID0gImZhc3Rtbm5fVU1BUCIsCiAgY29sb3JfYnkgPSAiRU5TRzAwMDAwMTk2MDkwIiwgIyBQVFBSVAogIHBvaW50X3NpemUgPSAwLjUsCiAgcG9pbnRfYWxwaGEgPSAwLjQsCiAgb3RoZXJfZmllbGRzID0gImRpYWdub3Npc19ncm91cCIKKSArCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhkaWFnbm9zaXNfZ3JvdXApKQpgYGAKCkluIHRoZSBhYm92ZSBwbG90IHdlIG9ubHkgcGxvdHRlZCB0aGUgdHVtb3IgbXlvYmxhc3QgY2VsbHMgdGhhdCB3ZSB1c2VkIGluIG91ciBERSBhbmFseXNpcy4KSG93ZXZlciwgd2UgbWlnaHQgYmUgaW50ZXJlc3RlZCB0byBzZWUgdGhlIGV4cHJlc3Npb24gb2YgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIG90aGVyIGNlbGwgdHlwZXMgcHJlc2VudCBpbiBvdXIgc2FtcGxlcy4KCmBgYHtyIGNlbGx0eXBlIGNvbXBhcmlzb259CiMgbGV0J3MgY29tcGFyZSBnZW5lIGV4cHJlc3Npb24gYWNyb3NzIHNvbWUgb3RoZXIgY2VsbCB0eXBlcwojIGxvb2sgYXQgYWxsIHR1bW9yIGNlbGxzIGFuZCBwaWNrIG9uZSBub3JtYWwgY2VsbCB0eXBlCmNlbGx0eXBlcyA8LSBjKAogICJUdW1vcl9NeW9ibGFzdCIsCiAgIlR1bW9yX01lc29kZXJtIiwKICAiVHVtb3JfTXlvY3l0ZSIsCiAgIlZhc2N1bGFyIEVuZG90aGVsaXVtIgopCgojIHN1YnNldCB0byBqdXN0IGNlbGx0eXBlcyB0aGF0IHdlIGFyZSBpbnRlcmVzdGVkIGluCnR1bW9yX3NjZSA8LSBybXNfc2NlWywgd2hpY2gocm1zX3NjZSRjZWxsdHlwZV9icm9hZCAlaW4lIGNlbGx0eXBlcyldCmBgYAoKTmV4dCB3ZSB3aWxsIGxvb2sgYXQgYSBmZXcgREUgZ2VuZXMgdGhhdCB3ZSBpZGVudGlmaWVkLCBvbmUgdXAgcmVndWxhdGVkIGdlbmUgYW5kIG9uZSBkb3duIHJlZ3VsYXRlZCBnZW5lLCBhbmQgY29tcGFyZSB0aGVpciBleHByZXNzaW9uIGluIG15b2JsYXN0cyB0byBvdGhlciBjZWxsIHR5cGVzIGluIEFSTVMgYW5kIEVSTVMgc2FtcGxlcy4KV2Ugd2lsbCB1c2UgdGhlIGBzY2F0ZXI6OnBsb3RFeHByZXNzaW9uKClgIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhIHZpb2xpbiBwbG90IHdpdGggUk1TIHN1YnR5cGUgb24gdGhlIHgtYXhpcyBhbmQgZ2VuZSBleHByZXNzaW9uIG9uIHRoZSB5LWF4aXMuCldlIGNhbiBjb250aW51ZSB1c2luZyBgZmFjZXRfZ3JpZCgpYCB0byBzaG93IHNlcGFyYXRlIHBhbmVscyBmb3IgZWFjaCBjZWxsIHR5cGUuCkJlY2F1c2Ugd2Ugd2FudCB0byBzaG93IG11bHRpcGxlIGdlbmVzIGhlcmUsIHdlIGFyZSBnb2luZyB0byBhZGQgYW4gYWRkaXRpb25hbCBvcHRpb24gdG8gYGZhY2V0X2dyaWQoKWAgdG8gaW5jbHVkZSBtdWx0aXBsZSByb3dzIGluIG91ciBwbG90IGdyaWQsIG9uZSBmb3IgZWFjaCBnZW5lIG9mIGludGVyZXN0LgpPbmUgbmVhdCB0cmljayBvZiB0aGUgYHNjYXRlcjo6cGxvdEV4cHJlc3Npb24oKWAgZnVuY3Rpb24gaXMgdGhhdCBpdCBhY3R1YWxseSBjcmVhdGVzIGEgYEZlYXR1cmVgIGNvbHVtbiB3aGljaCBjb3JyZXNwb25kcyB0byB0aGUgZmVhdHVyZXMgKGluIHRoaXMgY2FzZSBnZW5lcykgYmVpbmcgdXNlZCBpbiBwbG90dGluZy4KV2UgY2FuIHRoZW4gZGlyZWN0bHkgcmVmZXJlbmNlIHRoYXQgYEZlYXR1cmVgIGNvbHVtbiB3aGVuIHBsb3R0aW5nLCBpbnN0ZWFkIG9mIHVzaW5nIHRoZSBgb3RoZXJfZmllbGRzYCBvcHRpb24gd2UgdXNlZCBwcmV2aW91c2x5LgoKYGBge3IgbXVsdGktZ2VuZSBwbG90fQojIHBpY2sgYSBjb3VwbGUgZ2VuZXMgdG8gbG9vayBhdApnZW5lc190b19wbG90IDwtIGMoCiAgIkVOU0cwMDAwMDE5NjA5MCIsICMgUFRQUlQKICAiRU5TRzAwMDAwMTQ4OTM1IgopICMgR0FTMgoKIyBjcmVhdGUgYSB2aW9saW4gcGxvdApzY2F0ZXI6OnBsb3RFeHByZXNzaW9uKAogIHR1bW9yX3NjZSwKICAjIGEgdmVjdG9yIG9mIGdlbmVzIHRvIHBsb3QKICBmZWF0dXJlcyA9IGdlbmVzX3RvX3Bsb3QsCiAgeCA9ICJkaWFnbm9zaXNfZ3JvdXAiLAogIGNvbG9yX2J5ID0gImRpYWdub3Npc19ncm91cCIsCiAgb3RoZXJfZmllbGRzID0gImNlbGx0eXBlX2Jyb2FkIiwKICBwb2ludF9zaXplID0gMC4xCikgKwogICMgZWFjaCBjZWxsdHlwZSBpcyBpdHMgb3duIGNvbHVtbgogIGZhY2V0X2dyaWQoCiAgICBjb2xzID0gdmFycyhjZWxsdHlwZV9icm9hZCksCiAgICAjIGVhY2ggZmVhdHVyZSAoZ2VuZSkgaXMgaXRzIG93biByb3cKICAgIHJvd3MgPSB2YXJzKEZlYXR1cmUpCiAgKSArCiAgIyBjaGFuZ2UgdGhlIGZvbnQgc2l6ZSBvZiB0aGUgZmFjZXQgbGFiZWxzCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNykpICsKICBndWlkZXMoCiAgICBjb2xvciA9IGd1aWRlX2xlZ2VuZCgKICAgICAgIyB1cGRhdGUgdGhlIGxlZ2VuZCB0aXRsZQogICAgICB0aXRsZSA9ICJTdWJ0eXBlIiwKICAgICAgIyBjaGFuZ2UgdGhlIHNpemUgb2YgdGhlIGxlZ2VuZCBjb2xvcnMKICAgICAgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMywgYWxwaGEgPSAxKQogICAgKQogICkKYGBgCgpIb3cgZG8gdGhlIGV4cHJlc3Npb24gb2YgdGhlc2UgZ2VuZXMgY2hhbmdlIGFjcm9zcyBjZWxsIHR5cGVzIGFuZCBSTVMgc3VidHlwZXM/CgpHbyBhaGVhZCBhbmQgZXhwbG9yZSBzb21lIGdlbmVzIG9uIHlvdXIgb3duIQpGZWVsIGZyZWUgdG8gcGxvdCBhbnkgb2YgdGhlIGdlbmVzIHRoYXQgYXJlIGlkZW50aWZpZWQgYXMgc2lnbmlmaWNhbnQsIGZvdW5kIGluIHRoZSBERSByZXN1bHRzIHRhYmxlLCBvciB5b3VyIGZhdm9yaXRlIGdlbmUuClJlbWVtYmVyLCB5b3UgbmVlZCB0byB1c2UgdGhlIEVuc2VtYmwgZ2VuZSBpZGVudGlmaWVyIHRvIHJlZmVyIHRvIGVhY2ggZ2VuZS4KCmBgYHtyIGV4cGxvcmV9CiMgbm93IGRvIHNvbWUgZXhwbG9yYXRpb24gb2Ygb3RoZXIgZ2VuZXMgb24geW91ciBvd24hCmBgYAoKIyMgUHJpbnQgc2Vzc2lvbiBpbmZvCgpgYGB7ciBzZXNzaW9uIGluZm99CnNlc3Npb25JbmZvKCkKYGBgCgo=