Workflow Overview
Introduction
As discussed at the start of the workshop, single-cell experiments
using 10x Chromium instrument aim to have droplets with one cell plus
one bead. However this is an inherently imperfect process and there are
other important considerations like how healthy or intact the cell was
at the time of measurement.
In this section, our goal is to use filtering thresholds to remove
“cells” that were poorly measured, not cells at all, or included more
than one cell.
|
The Cell Ranger barcode feature matrix outputs (A) is a mix of good and
poor quality cells (B). We can use expression patterns to further
visualize and filter to the subset of putative healthy cells (C) to
improve downstream analysis.
|
Similar to many other areas of research, there are often gaps
between how single-cell data is presented versus the reality of running
an analysis. For example, only the final filtering thresholds might be
reported in a paper but our process for choosing those is likely to be
more iterative and include some trial and error.
Objectives
- Discuss QC measures and learn how to calculate and plot them.
- Discuss cell-filtering approaches and apply them to our
dataset.
Adding metadata
We’re going to alter and add some columns to meta.data
to ease some downstream analysis and plotting steps. This will often be
necessary, as sample names contain combined information about
phenotypes. Let’s take a look at the first few rows again, to reacquaint
ourselves.
##### Day 1 - Secondary QC and filtering
# Examine Seurat metdata ----------------------------------------------
head(geo_so@meta.data)
orig.ident nCount_RNA nFeature_RNA
HODay0replicate1_AAACCTGAGAGAACAG-1 HODay0replicate1 10234 3226
HODay0replicate1_AAACCTGGTCATGCAT-1 HODay0replicate1 3158 1499
HODay0replicate1_AAACCTGTCAGAGCTT-1 HODay0replicate1 13464 4102
HODay0replicate1_AAACGGGAGAGACTTA-1 HODay0replicate1 577 346
HODay0replicate1_AAACGGGAGGCCCGTT-1 HODay0replicate1 1189 629
HODay0replicate1_AAACGGGCAACTGGCC-1 HODay0replicate1 7726 2602
We can add arbitrary per-cell information to this table such as:
- Summary statistics, such as percent mitochondrial reads for each
cell.
- Batch, condition, etc. for each cell.
- Cluster membership for each cell.
- Cell cycle phase for each cell.
- Other custom annotations for each cell.
The following code chunk will create new columns from the
orig.ident
column, one for the day
and one for
the replicate
. We’ll also use factors to make the days
appear in the correct order (by default R will order them Day0, Day21,
Day7).
# Make metadata more granular ------------------------------------------
# (So it's easier for us to understand)
orig.ident_levels = apply(
expand.grid(c('HO'), c('replicate1', 'replicate2', 'replicate3', 'replicate4'), c('Day0','Day7','Day21')),
MARGIN = 1, FUN = function(row){paste(row[1], row[3], row[2], sep = '.')})
tmp_meta = geo_so@meta.data
tmp_meta$orig.ident = gsub('HO', 'HO.', tmp_meta$orig.ident)
tmp_meta$orig.ident = gsub('rep', '.rep', tmp_meta$orig.ident)
tmp_meta$orig.ident = factor(tmp_meta$orig.ident, levels = orig.ident_levels)
# Add day information
tmp_meta$day = factor(str_split(tmp_meta$orig.ident, pattern = '[.]', simplify = TRUE)[,2], levels = c('Day0', 'Day7', 'Day21'))
# Add replicate column
tmp_meta$replicate = str_split(tmp_meta$orig.ident, pattern = '[.]', simplify = TRUE)[,3]
# Assign the updated metadata to that in the Seurat object
geo_so@meta.data = tmp_meta
# Set the Idents(geo_so) to the new orig.ident
Idents(geo_so) = 'orig.ident'
Taking a look at the result, we see orig.ident
looks a
little different, and we have new day
and
replicate
columns. Note that there are many ways to have
accomplished this, the above code is just one route.
# Re-examine Seurat metdata -------------------------------------------
head(geo_so@meta.data)
orig.ident nCount_RNA nFeature_RNA day replicate
HODay0replicate1_AAACCTGAGAGAACAG-1 HO.Day0.replicate1 10234 3226 Day0 replicate1
HODay0replicate1_AAACCTGGTCATGCAT-1 HO.Day0.replicate1 3158 1499 Day0 replicate1
HODay0replicate1_AAACCTGTCAGAGCTT-1 HO.Day0.replicate1 13464 4102 Day0 replicate1
HODay0replicate1_AAACGGGAGAGACTTA-1 HO.Day0.replicate1 577 346 Day0 replicate1
HODay0replicate1_AAACGGGAGGCCCGTT-1 HO.Day0.replicate1 1189 629 Day0 replicate1
HODay0replicate1_AAACGGGCAACTGGCC-1 HO.Day0.replicate1 7726 2602 Day0 replicate1
Percent mitochondrial reads
The PercentageFeatureSet()
function enables us to
quickly determine the counts belonging to a subset of the possible
features for each cell. Since mitochondrial transcripts in mouse begin
with “mt”, we will use that pattern to count the percentage of reads
coming from mitochondrial transcripts.
# Consider mitochondrial transcripts ----------------------------------
# We use "mt" because this is mouse, depending on the organism, this might need to be changed
geo_so$percent.mt = PercentageFeatureSet(geo_so, pattern = '^mt-')
# Use summary() to quickly check the range of values
summary(geo_so$percent.mt)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 1.817 2.582 5.281 3.865 98.244
Just looking at the summary, we can see that there are some cells
with a high percentage of mitochondrial reads.
Image: Schematic after altering some meta data
columns.
Quality Metrics
Cell Ranger is a first-pass filter to determine what is a “cell” and
what is not. It only considers one sample at a time, and does not
consider the cells relative to one another.
Let’s dig deeper to determine when a droplet might contain two cells,
a very stressed cell, or some technical issue in the library
preparation. We will use three metrics to determine low-quality cells
based on their expression profiles (reference).
- The total number of UMIs detected. Cells with a small number of UMIs
detected may indicate loss of RNA during library preparation via cell
lysis or inefficient cDNA capture / amplification. Cells with relatively
high number of UMIs detected may indicate a doublet.
- The number of expressed features, defined as number of genes with
non-zero counts. Cells with very few measured genes are likely to be of
low-quality, and may distort downstream variance estimation or dimension
reduction steps.
- The proportion of reads mapped to the mitochondrial genome. High
proportions of mitochondrial transcripts may indicate a damaged cell,
the measure of which may also distort downstream analysis steps.
The number of UMIs detected (nCount
) and number of
expressed features (nFeature
) are already given in the meta
data table.
Why total UMIs instead of total reads?
Since a single-cell inherently contains a limited amount of RNA
molecules, a higher amount of PCR amplification is required to generate
the final sequencing library.
Since PCR can skew proportions of initial input materials, specific
sequences are included in the initial capture probes called unique
molecule identifiers (UMIs). As each initial probe has a different UMI
sequence, each RNA captured will be tagged with a different UMI, which
allows those initial RNAs and subsequent PCR duplicates to be identified
and duplicates collapsed as part of the initial processing by
CellRanger.
Other meanings of nFeatures
For other single-cell data types, nFeatures
would
represent what’s being measured in that experiment. For single-cell
ATAC-seq, nFeatures
would represents the total number of
peaks (e.g. accessible areas of DNA) per cell.
Cell counts
Cell counts per sample (based on the numbrer of unique cellular
barcodes detected by Cell Ranger) can indicate if an entire sample was
of poor quality. The table below is the number of cells called by Cell
Ranger, with the added filter from CreateSeurateObject()
in
the previous lesson: min.cells = 1, min.features = 50
.
Recall, this means a gene is removed if it is expressed in 1 or fewer
cells, and a cell is removed if it contains reads for 50 or fewer
genes.
# Assign overall cell counts ------------------------------------------
cell_counts_pre_tbl = geo_so@meta.data %>% count(orig.ident, name = 'prefilter_cells')
cell_counts_pre_tbl
orig.ident prefilter_cells
1 HO.Day0.replicate1 1183
2 HO.Day0.replicate2 689
3 HO.Day0.replicate3 1310
4 HO.Day0.replicate4 1053
5 HO.Day7.replicate1 5765
6 HO.Day7.replicate2 6020
7 HO.Day7.replicate3 6285
8 HO.Day7.replicate4 5166
9 HO.Day21.replicate1 2321
10 HO.Day21.replicate2 1322
11 HO.Day21.replicate3 1275
12 HO.Day21.replicate4 2827
It appears that the Day 7 samples have systematically more cells than
the other days. If you had an idea of how many cells you expected to see
per sample, this table can help you check that expectation and determine
if a sample failed and should be dropped.
Visualizing quality metrics
A violin plot shows the distribution of a quantity among the cells in
a single sample, or across many samples. Seurat has a built-in function,
VlnPlot()
for this purpose. Let’s orient with a violin plot
of nFeature_RNA
for only one sample,
HO.Day21.replicate1
:
A violin plot is similar to a box plot, but it shows the density of
the data at different values. Here the individual points are the cells
from HO.Day21.replicate1
and the y-axis is the value of
nFeature_RNA
for that cell. The violin part of the function
is essentially showing the density of the cells at different values of
nFeature_RNA
.
Genes per cell
Let’s look at the nFeature_RNA
violin plot across all
the samples. Again, this is the number of genes detected per cell.
# Review feature violin plots ----------------------------------------
VlnPlot(geo_so, features = 'nFeature_RNA', assay = 'RNA', layer = 'counts') + NoLegend() + geom_hline(yintercept = 500) + geom_hline(yintercept = 400) + geom_hline(yintercept = 300) + geom_hline(yintercept = 200)
ggsave(filename = 'results/figures/qc_nFeature_violin.png', width = 12, height = 6, units = 'in')
We observe that samples behave similarly within a day, but have
different distributions across days. We also note that many cells appear
to have a low number of genes detected (< 500).
UMI counts per cell
Now let’s look at nCount_RNA
plot, the total number of
UMIs detected.
# Review count violin plots ------------------------------------------
VlnPlot(geo_so, features = 'nCount_RNA', assay = 'RNA', layer = 'counts') + NoLegend()
ggsave(filename = 'results/figures/qc_nCount_violin.png', width = 12, height = 6, units = 'in')
We observe a similar within-and-across day behavior among the
samples. Overall it appears that the Day 0 samples have fewer UMIs
detected per cell than Day 7 and Day 21. Also of note is the outlier
cell in HO.Day0.replicate1, with around 100K UMIs detected. This cell
might be a doublet.
Percent mitochondrial reads
Finally, lets plot the percent mitochondrial reads,
percent.mt
.
# Review mitochondrial violin plots ----------------------------------
VlnPlot(geo_so, features = 'percent.mt', assay = 'RNA', layer = 'counts') + NoLegend() + geom_hline(yintercept = 25) + geom_hline(yintercept = 20) + geom_hline(yintercept = 15) + geom_hline(yintercept = 10)
ggsave(filename = 'results/figures/qc_mito_violin.png', width = 12, height = 6, units = 'in')
We observe the majority of cells seem to have < 25% mitochondrial
reads, but there are many cells with > 25% that we may want to
remove.
Generally, many tutorials use a cutoff of 5 - 10% mitochondrial
reads. However, for some experiments, high mitochondrial reads would be
expected (such as in cases where the condition/treatment or genotype
increases cell death). In this case, a relaxed threshold would help
preserve biologically relevant cells. In our case, the cells were
collected as part of an injury model, perhaps justifying a more lenient
percent mitochondrial read filter.
Filtering approaches
Fixed thresholds
Fixed thresholds for any combination of nFeature_RNA
,
nCount_RNA
, and percent.mt
can be selected to
remove low quality cells. In general, selecting very stringent
thresholds for each of the metrics may lead to removing informative
cells. As per the HBC training materials (link),
we recommend setting individual thresholds to err on the permissive
side.
Sorkin et
al. chose these thresholds:
We filtered out cells with less than 500 genes per cell and with more
than 25% mitochondrial read content.
In other words, cells with > 500 nFeature_RNA
and
< 25% percent.mt
were retained by the authors. We could
preview what the resulting cell counts would be with these
thresholds:
# Consider excluding suspect cells -------------------------------------
subset(geo_so, subset = nFeature_RNA > 500 & percent.mt < 25)@meta.data %>%
count(orig.ident, name = 'postfilter_cells')
orig.ident postfilter_cells
1 HO.Day0.replicate1 1049
2 HO.Day0.replicate2 615
3 HO.Day0.replicate3 1191
4 HO.Day0.replicate4 943
5 HO.Day7.replicate1 4928
6 HO.Day7.replicate2 4932
7 HO.Day7.replicate3 4897
8 HO.Day7.replicate4 4242
9 HO.Day21.replicate1 2000
10 HO.Day21.replicate2 1182
11 HO.Day21.replicate3 1166
12 HO.Day21.replicate4 2470
Adaptive thresholds
For this workshop we will use fixed thresholds, but another option is
to remove low-quality cells adaptively. This approach assumes that most
of the cells are of acceptable quality. For more information see the
relevant section in Bioconductor’s online book: Orchestrating
Single-Cell Analysis (link).
Removing low-quality cells
We will deviate from the publication slightly and retain the cells
with nFeature_RNA > 300
and
percent.mt < 15
. Noting that the
nFeature_RNA
plot had a gap around 300, and that we want to
be more lenient than the usual 5-10% cutoff for mitochondrial reads.
# Filter to exclude suspect cells and assign to new Seurat object ------
geo_so = subset(geo_so, subset = nFeature_RNA > 300 & percent.mt < 15)
geo_so
An object of class Seurat
26489 features across 31560 samples within 1 assay
Active assay: RNA (26489 features, 0 variable features)
1 layer present: counts
And let’s record the number of cells remaining after filtering:
# Examine remaining cell counts ----------------------------------------
cell_counts_post_tbl = geo_so@meta.data %>% count(orig.ident, name = 'postfilter_cells')
cell_counts_post_tbl
orig.ident postfilter_cells
1 HO.Day0.replicate1 1053
2 HO.Day0.replicate2 629
3 HO.Day0.replicate3 1222
4 HO.Day0.replicate4 971
5 HO.Day7.replicate1 5215
6 HO.Day7.replicate2 5324
7 HO.Day7.replicate3 5626
8 HO.Day7.replicate4 4690
9 HO.Day21.replicate1 2004
10 HO.Day21.replicate2 1186
11 HO.Day21.replicate3 1178
12 HO.Day21.replicate4 2462
Let’s combine the pre and post tables:
# Show cell counts before and after filtering --------------------------
cell_counts_tbl = cell_counts_pre_tbl %>% left_join(cell_counts_post_tbl, by = 'orig.ident')
cell_counts_tbl
orig.ident prefilter_cells postfilter_cells
1 HO.Day0.replicate1 1183 1053
2 HO.Day0.replicate2 689 629
3 HO.Day0.replicate3 1310 1222
4 HO.Day0.replicate4 1053 971
5 HO.Day7.replicate1 5765 5215
6 HO.Day7.replicate2 6020 5324
7 HO.Day7.replicate3 6285 5626
8 HO.Day7.replicate4 5166 4690
9 HO.Day21.replicate1 2321 2004
10 HO.Day21.replicate2 1322 1186
11 HO.Day21.replicate3 1275 1178
12 HO.Day21.replicate4 2827 2462
We can now easily see how many cells we started with, and how many we
retained for downstream analysis. Let’s write this table to a file.
write_csv(cell_counts_tbl, file = 'results/tables/cell_filtering_counts.csv')
Revising thresholds
It is important to recognize that thresholds for retaining cells are
not set in stone. It may happen that the downstream analysis suggests
that thresholds should be more or less stringent. In which case, you may
experiment with the thresholds and observe the downstream effects.
Save our progress
Let’s save this filtered form of our Seurat object. It will also
include our changes to the meta.data
:
# Save the current Seurat object ---------------------------------------
saveRDS(geo_so, file = 'results/rdata/geo_so_filtered.rds')
Summary
In this section we:
- Discussed the big three quality metrics:
nFeature
s,
nCount
s, and percent.mt
.
- Visualized these metrics across cells / samples to help identify
low-quality cells.
- Filtered low-quality cells using fixed thresholds.
Next steps: Normalization
These materials have been adapted and extended from materials listed
above. These are open access materials distributed under the terms of
the Creative
Commons Attribution license (CC BY 4.0), which permits unrestricted
use, distribution, and reproduction in any medium, provided the original
author and source are credited.
LS0tCnRpdGxlOiAiU2Vjb25kYXJ5IFF1YWxpdHkgQ29udHJvbCBhbmQgRmlsdGVyaW5nIgphdXRob3I6ICJVTSBCaW9pbmZvcm1hdGljcyBDb3JlIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAgICAgICBodG1sX2RvY3VtZW50OgogICAgICAgICAgICBpbmNsdWRlczoKICAgICAgICAgICAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgICAgICAgICAgdGhlbWU6IHBhcGVyCiAgICAgICAgICAgIHRvYzogdHJ1ZQogICAgICAgICAgICB0b2NfZGVwdGg6IDQKICAgICAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgICAgICAgICAgZmlnX2NhcHRpb246IHRydWUKICAgICAgICAgICAgbWFya2Rvd246IEdGTQogICAgICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSwgdGQgewogICBmb250LXNpemU6IDE4cHg7Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTJweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogMTJweAp9Cgp0YWJsZS5maWcsIHRoLmZpZywgdGQuZmlnIHsKICBib3JkZXI6IDFweCBzb2xpZCBibGFjazsKICBib3JkZXItY29sbGFwc2U6IGNvbGxhcHNlOwogIHBhZGRpbmc6IDE1cHg7Cn0KCnRhYmxlewogICB3aWR0aDoxMDAlOwp9Cjwvc3R5bGU+CgpgYGB7ciBrbGlwcHksIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRX0Ka2xpcHB5OjprbGlwcHkobGFuZyA9IGMoInIiLCAibWFya2Rvd24iLCAiYmFzaCIpLCBwb3NpdGlvbiA9IGMoInRvcCIsICJyaWdodCIpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnNvdXJjZSgiLi4vYmluL2NodW5rLW9wdGlvbnMuUiIpCmtuaXRyX2ZpZ19wYXRoKCIwMi1RQ2FuZEZpbHRlcmluZy8wMi0iKQpgYGAKCiMgV29ya2Zsb3cgT3ZlcnZpZXcgey51bmxpc3RlZCAudW5udW1iZXJlZH0KCjxici8+CjxpbWcgc3JjPSJpbWFnZXMvd2F5ZmluZGVyL3dheWZpbmRlci5wbmciIGFsdD0id2F5ZmluZGVyIiBzdHlsZT0iaGVpZ2h0OiA0MDBweDsiLz4KPGJyLz4KPGJyLz4KCiMgSW50cm9kdWN0aW9uCgpBcyBkaXNjdXNzZWQgYXQgdGhlIHN0YXJ0IG9mIHRoZSB3b3Jrc2hvcCwgc2luZ2xlLWNlbGwgZXhwZXJpbWVudHMgdXNpbmcgMTB4IENocm9taXVtIGluc3RydW1lbnQgYWltIHRvIGhhdmUgZHJvcGxldHMgd2l0aCBvbmUgY2VsbCBwbHVzIG9uZSBiZWFkLiBIb3dldmVyIHRoaXMgaXMgYW4gaW5oZXJlbnRseSBpbXBlcmZlY3QgcHJvY2VzcyBhbmQgdGhlcmUgYXJlIG90aGVyIGltcG9ydGFudCBjb25zaWRlcmF0aW9ucyBsaWtlIGhvdyBoZWFsdGh5IG9yIGludGFjdCB0aGUgY2VsbCB3YXMgYXQgdGhlIHRpbWUgb2YgbWVhc3VyZW1lbnQuCgpJbiB0aGlzIHNlY3Rpb24sIG91ciBnb2FsIGlzIHRvIHVzZSBmaWx0ZXJpbmcgdGhyZXNob2xkcyB0byByZW1vdmUgImNlbGxzIiB0aGF0IHdlcmUgcG9vcmx5IG1lYXN1cmVkLCBub3QgY2VsbHMgYXQgYWxsLCBvciBpbmNsdWRlZCBtb3JlIHRoYW4gb25lIGNlbGwuCgo8dGFibGUgY2xhc3M9J2ZpZyc+PHRyPjx0ZCBjbGFzcz0nZmlnJz4KIVtdKGltYWdlcy9ncmFwaGljYWxfYWJzdHJhY3RzL2dyYXBoaWNhbF9hYnN0cmFjdF9RQ19hbmRfZmlsdGVyaW5nLnBuZykKPC90ZD48L3RyPjx0cj48dGQgY2xhc3M9J2ZpZyc+VGhlIENlbGwgUmFuZ2VyIGJhcmNvZGUgZmVhdHVyZSBtYXRyaXggb3V0cHV0cyAoQSkgaXMgYSBtaXggb2YgZ29vZCBhbmQgcG9vciBxdWFsaXR5IGNlbGxzIChCKS4gV2UgY2FuIHVzZSBleHByZXNzaW9uIHBhdHRlcm5zIHRvIGZ1cnRoZXIgdmlzdWFsaXplIGFuZCBmaWx0ZXIgdG8gdGhlIHN1YnNldCBvZiBwdXRhdGl2ZSBoZWFsdGh5IGNlbGxzIChDKSB0byBpbXByb3ZlIGRvd25zdHJlYW0gYW5hbHlzaXMuCjwvdGQ+PC90cj48L3RhYmxlPgo8YnIvPgpTaW1pbGFyIHRvIG1hbnkgb3RoZXIgYXJlYXMgb2YgcmVzZWFyY2gsIHRoZXJlIGFyZSBvZnRlbiBnYXBzIGJldHdlZW4gaG93IHNpbmdsZS1jZWxsIGRhdGEgaXMgcHJlc2VudGVkIHZlcnN1cyB0aGUgcmVhbGl0eSBvZiBydW5uaW5nIGFuIGFuYWx5c2lzLiBGb3IgZXhhbXBsZSwgb25seSB0aGUgZmluYWwgZmlsdGVyaW5nIHRocmVzaG9sZHMgbWlnaHQgYmUgcmVwb3J0ZWQgaW4gYSBwYXBlciBidXQgb3VyIHByb2Nlc3MgZm9yIGNob29zaW5nIHRob3NlIGlzIGxpa2VseSB0byBiZSBtb3JlIGl0ZXJhdGl2ZSBhbmQgaW5jbHVkZSBzb21lIHRyaWFsIGFuZCBlcnJvci4KCgojIyBPYmplY3RpdmVzCgotIERpc2N1c3MgUUMgbWVhc3VyZXMgYW5kIGxlYXJuIGhvdyB0byBjYWxjdWxhdGUgYW5kIHBsb3QgdGhlbS4KLSBEaXNjdXNzIGNlbGwtZmlsdGVyaW5nIGFwcHJvYWNoZXMgYW5kIGFwcGx5IHRoZW0gdG8gb3VyIGRhdGFzZXQuCgo8IS0tIENoYWxsZW5nZSBmb3IgaW5zdHJ1Y3RvcnM6IEV2ZXJ5IHZpZ25ldHRlIHVzZXMgZGlmZmVyZW50IGZpbHRlcnMsIGhvdyB0byBoYXJtb25pemUvZ2l2ZSBndWlkYW5jZT8gUmVsYXRlZCwgaG93IG11Y2ggdG8gZGlzY3VzcyBhcmJpdHJhcnkgY3V0b2ZmcyBhbmQgY29udGludWVkIG1hdHVyYXRpb24gb2YgZmllbGQ/LS0+IAo8IS0tIEFkZCBsaW5rcyB0byByZWxldmFudCByZXNvdXJjZXMgdGhyb3VnaG91dCAtLT4gCgo8IS0tIEdlbmVyYWwgZ3VpZGFuY2UgLSBsaWtlbHkgdG8gYmUgbW92ZWQgdG8gZWFybGllciBzZWN0aW9uOgotIE5vdGUgd2l0aCBlYWNoIGZ1bmN0aW9uIGNhbGwgd2hhdCBnZXRzIGFkZGVkIHRvIHRoZSBTZXVyYXQgb2JqZWN0LgotIEFkZGluZyBjaGVja3MgdG8gZW5zdXJlIG9iamVjdCBpcyB1cGRhdGVkIGJ5IGxlYXJuZXJzIHNpbmNlIHdhbnQgdG8gYXZvaWQgZ2VuZXJhdGluZyBjb3BpZWQgb2JqZWN0cwotIE5vdGUgd2hhdCBsYXllcnMgc2hvdWxkIGJlIHVzZWQgZm9yIHdoYXQsIGZvciBleGFtcGxlLCBjb3VudHMgdXNlZCBmb3IgRmVhdHVyZVBsb3RzLiBSTkEgdnMgU0NUIGFzc2F5LiAtLT4KCi0tLQoKYGBge3IsIHJlYWRfcmRzX2hpZGRlbiwgZWNobyA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KaWYoIWV4aXN0cygnZ2VvX3NvJykpIHsKICBsaWJyYXJ5KFNldXJhdCkKICBsaWJyYXJ5KEJQQ2VsbHMpCiAgbGlicmFyeSh0aWR5dmVyc2UpCgogIG9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDFlOSkKCiAgZ2VvX3NvID0gcmVhZFJEUygncmVzdWx0cy9yZGF0YS9nZW9fc29fdW5maWx0ZXJlZC5yZHMnKQp9CmBgYAoKIyBBZGRpbmcgbWV0YWRhdGEKCldlJ3JlIGdvaW5nIHRvIGFsdGVyIGFuZCBhZGQgc29tZSBjb2x1bW5zIHRvIGBtZXRhLmRhdGFgIHRvIGVhc2Ugc29tZSBkb3duc3RyZWFtIGFuYWx5c2lzIGFuZCBwbG90dGluZyBzdGVwcy4gVGhpcyB3aWxsIG9mdGVuIGJlIG5lY2Vzc2FyeSwgYXMgc2FtcGxlIG5hbWVzIGNvbnRhaW4gY29tYmluZWQgaW5mb3JtYXRpb24gYWJvdXQgcGhlbm90eXBlcy4gTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGZpcnN0IGZldyByb3dzIGFnYWluLCB0byByZWFjcXVhaW50IG91cnNlbHZlcy4KCmBgYHtyLCBwcmV2aWV3X21ldGFkYXRhMX0KIyMjIyMgRGF5IDEgLSBTZWNvbmRhcnkgUUMgYW5kIGZpbHRlcmluZwoKIyBFeGFtaW5lIFNldXJhdCBtZXRkYXRhICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmhlYWQoZ2VvX3NvQG1ldGEuZGF0YSkKYGBgCgpXZSBjYW4gYWRkIGFyYml0cmFyeSBwZXItY2VsbCBpbmZvcm1hdGlvbiB0byB0aGlzIHRhYmxlIHN1Y2ggYXM6CgotIFN1bW1hcnkgc3RhdGlzdGljcywgc3VjaCBhcyBwZXJjZW50IG1pdG9jaG9uZHJpYWwgcmVhZHMgZm9yIGVhY2ggY2VsbC4KLSBCYXRjaCwgY29uZGl0aW9uLCBldGMuIGZvciBlYWNoIGNlbGwuCi0gQ2x1c3RlciBtZW1iZXJzaGlwIGZvciBlYWNoIGNlbGwuCi0gQ2VsbCBjeWNsZSBwaGFzZSBmb3IgZWFjaCBjZWxsLgotIE90aGVyIGN1c3RvbSBhbm5vdGF0aW9ucyBmb3IgZWFjaCBjZWxsLgoKVGhlIGZvbGxvd2luZyBjb2RlIGNodW5rIHdpbGwgY3JlYXRlIG5ldyBjb2x1bW5zIGZyb20gdGhlIGBvcmlnLmlkZW50YCBjb2x1bW4sIG9uZSBmb3IgdGhlIGBkYXlgIGFuZCBvbmUgZm9yIHRoZSBgcmVwbGljYXRlYC4gV2UnbGwgYWxzbyB1c2UgZmFjdG9ycyB0byBtYWtlIHRoZSBkYXlzIGFwcGVhciBpbiB0aGUgY29ycmVjdCBvcmRlciAoYnkgZGVmYXVsdCBSIHdpbGwgb3JkZXIgdGhlbSBEYXkwLCBEYXkyMSwgRGF5NykuCgpgYGB7ciwgYWx0ZXJfbWV0YWRhdGF9CiMgTWFrZSBtZXRhZGF0YSBtb3JlIGdyYW51bGFyIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIChTbyBpdCdzIGVhc2llciBmb3IgdXMgdG8gdW5kZXJzdGFuZCkgIApvcmlnLmlkZW50X2xldmVscyA9IGFwcGx5KAogICAgZXhwYW5kLmdyaWQoYygnSE8nKSwgYygncmVwbGljYXRlMScsICdyZXBsaWNhdGUyJywgJ3JlcGxpY2F0ZTMnLCAncmVwbGljYXRlNCcpLCBjKCdEYXkwJywnRGF5NycsJ0RheTIxJykpLCAKICAgIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKHJvdyl7cGFzdGUocm93WzFdLCByb3dbM10sIHJvd1syXSwgc2VwID0gJy4nKX0pCgp0bXBfbWV0YSA9IGdlb19zb0BtZXRhLmRhdGEKCnRtcF9tZXRhJG9yaWcuaWRlbnQgPSBnc3ViKCdITycsICdITy4nLCB0bXBfbWV0YSRvcmlnLmlkZW50KQp0bXBfbWV0YSRvcmlnLmlkZW50ID0gZ3N1YigncmVwJywgJy5yZXAnLCB0bXBfbWV0YSRvcmlnLmlkZW50KQoKdG1wX21ldGEkb3JpZy5pZGVudCA9IGZhY3Rvcih0bXBfbWV0YSRvcmlnLmlkZW50LCBsZXZlbHMgPSBvcmlnLmlkZW50X2xldmVscykKCiMgQWRkIGRheSBpbmZvcm1hdGlvbgp0bXBfbWV0YSRkYXkgPSBmYWN0b3Ioc3RyX3NwbGl0KHRtcF9tZXRhJG9yaWcuaWRlbnQsIHBhdHRlcm4gPSAnWy5dJywgc2ltcGxpZnkgPSBUUlVFKVssMl0sIGxldmVscyA9IGMoJ0RheTAnLCAnRGF5NycsICdEYXkyMScpKQoKIyBBZGQgcmVwbGljYXRlIGNvbHVtbgp0bXBfbWV0YSRyZXBsaWNhdGUgPSBzdHJfc3BsaXQodG1wX21ldGEkb3JpZy5pZGVudCwgcGF0dGVybiA9ICdbLl0nLCBzaW1wbGlmeSA9IFRSVUUpWywzXQoKIyBBc3NpZ24gdGhlIHVwZGF0ZWQgbWV0YWRhdGEgdG8gdGhhdCBpbiB0aGUgU2V1cmF0IG9iamVjdApnZW9fc29AbWV0YS5kYXRhID0gdG1wX21ldGEKCiMgU2V0IHRoZSBJZGVudHMoZ2VvX3NvKSB0byB0aGUgbmV3IG9yaWcuaWRlbnQKSWRlbnRzKGdlb19zbykgPSAnb3JpZy5pZGVudCcKYGBgCgpUYWtpbmcgYSBsb29rIGF0IHRoZSByZXN1bHQsIHdlIHNlZSBgb3JpZy5pZGVudGAgbG9va3MgYSBsaXR0bGUgZGlmZmVyZW50LCBhbmQgd2UgaGF2ZSBuZXcgYGRheWAgYW5kIGByZXBsaWNhdGVgIGNvbHVtbnMuIE5vdGUgdGhhdCB0aGVyZSBhcmUgbWFueSB3YXlzIHRvIGhhdmUgYWNjb21wbGlzaGVkIHRoaXMsIHRoZSBhYm92ZSBjb2RlIGlzIGp1c3Qgb25lIHJvdXRlLgoKYGBge3IsIHByZXZpZXdfbWV0YWRhdGEzfQojIFJlLWV4YW1pbmUgU2V1cmF0IG1ldGRhdGEgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KaGVhZChnZW9fc29AbWV0YS5kYXRhKQpgYGAKCiMjIFBlcmNlbnQgbWl0b2Nob25kcmlhbCByZWFkcwoKVGhlIGBQZXJjZW50YWdlRmVhdHVyZVNldCgpYCBmdW5jdGlvbiBlbmFibGVzIHVzIHRvIHF1aWNrbHkgZGV0ZXJtaW5lIHRoZSBjb3VudHMgYmVsb25naW5nIHRvIGEgc3Vic2V0IG9mIHRoZSBwb3NzaWJsZSBmZWF0dXJlcyBmb3IgZWFjaCBjZWxsLiBTaW5jZSBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHRzIGluIG1vdXNlIGJlZ2luIHdpdGggIm10Iiwgd2Ugd2lsbCB1c2UgdGhhdCBwYXR0ZXJuIHRvIGNvdW50IHRoZSBwZXJjZW50YWdlIG9mIHJlYWRzIGNvbWluZyBmcm9tIG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdHMuCgpgYGB7ciwgYXNzaWduX3BlcmNlbnRfbXR9CiMgQ29uc2lkZXIgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0cyAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFdlIHVzZSAibXQiIGJlY2F1c2UgdGhpcyBpcyBtb3VzZSwgZGVwZW5kaW5nIG9uIHRoZSBvcmdhbmlzbSwgdGhpcyBtaWdodCBuZWVkIHRvIGJlIGNoYW5nZWQKZ2VvX3NvJHBlcmNlbnQubXQgPSBQZXJjZW50YWdlRmVhdHVyZVNldChnZW9fc28sIHBhdHRlcm4gPSAnXm10LScpCgojIFVzZSBzdW1tYXJ5KCkgdG8gcXVpY2tseSBjaGVjayB0aGUgcmFuZ2Ugb2YgdmFsdWVzCnN1bW1hcnkoZ2VvX3NvJHBlcmNlbnQubXQpCmBgYAoKSnVzdCBsb29raW5nIGF0IHRoZSBzdW1tYXJ5LCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIHNvbWUgY2VsbHMgd2l0aCBhIGhpZ2ggcGVyY2VudGFnZSBvZiBtaXRvY2hvbmRyaWFsIHJlYWRzLgoKIVtJbWFnZTogU2NoZW1hdGljIGFmdGVyIGFsdGVyaW5nIHNvbWUgbWV0YSBkYXRhIGNvbHVtbnMuXShpbWFnZXMvc2V1cmF0X3NjaGVtYXRpYy9TbGlkZTMucG5nKQoKIyBRdWFsaXR5IE1ldHJpY3MKCkNlbGwgUmFuZ2VyIGlzIGEgZmlyc3QtcGFzcyBmaWx0ZXIgdG8gZGV0ZXJtaW5lIHdoYXQgaXMgYSAiY2VsbCIgYW5kIHdoYXQgaXMgbm90LiBJdCBvbmx5IGNvbnNpZGVycyBvbmUgc2FtcGxlIGF0IGEgdGltZSwgYW5kIGRvZXMgbm90IGNvbnNpZGVyIHRoZSBjZWxscyByZWxhdGl2ZSB0byBvbmUgYW5vdGhlci4gCgpMZXQncyBkaWcgZGVlcGVyIHRvIGRldGVybWluZSB3aGVuIGEgZHJvcGxldCBtaWdodCBjb250YWluIHR3byBjZWxscywgYSB2ZXJ5IHN0cmVzc2VkIGNlbGwsIG9yIHNvbWUgdGVjaG5pY2FsIGlzc3VlIGluIHRoZSBsaWJyYXJ5IHByZXBhcmF0aW9uLiBXZSB3aWxsIHVzZSB0aHJlZSBtZXRyaWNzIHRvIGRldGVybWluZSBsb3ctcXVhbGl0eSBjZWxscyBiYXNlZCBvbiB0aGVpciBleHByZXNzaW9uIHByb2ZpbGVzIChbcmVmZXJlbmNlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xMi9PU0NBL3F1YWxpdHktY29udHJvbC5odG1sI2Nob2ljZS1vZi1xYy1tZXRyaWNzKSkuCgoxLiBUaGUgdG90YWwgbnVtYmVyIG9mIFVNSXMgZGV0ZWN0ZWQuIENlbGxzIHdpdGggYSBzbWFsbCBudW1iZXIgb2YgVU1JcyBkZXRlY3RlZCBtYXkgaW5kaWNhdGUgbG9zcyBvZiBSTkEgZHVyaW5nIGxpYnJhcnkgcHJlcGFyYXRpb24gdmlhIGNlbGwgbHlzaXMgb3IgaW5lZmZpY2llbnQgY0ROQSBjYXB0dXJlIC8gYW1wbGlmaWNhdGlvbi4gQ2VsbHMgd2l0aCByZWxhdGl2ZWx5IGhpZ2ggbnVtYmVyIG9mIFVNSXMgZGV0ZWN0ZWQgbWF5IGluZGljYXRlIGEgZG91YmxldC4KMi4gVGhlIG51bWJlciBvZiBleHByZXNzZWQgZmVhdHVyZXMsIGRlZmluZWQgYXMgbnVtYmVyIG9mIGdlbmVzIHdpdGggbm9uLXplcm8gY291bnRzLiBDZWxscyB3aXRoIHZlcnkgZmV3IG1lYXN1cmVkIGdlbmVzIGFyZSBsaWtlbHkgdG8gYmUgb2YgbG93LXF1YWxpdHksIGFuZCBtYXkgZGlzdG9ydCBkb3duc3RyZWFtIHZhcmlhbmNlIGVzdGltYXRpb24gb3IgZGltZW5zaW9uIHJlZHVjdGlvbiBzdGVwcy4KMy4gVGhlIHByb3BvcnRpb24gb2YgcmVhZHMgbWFwcGVkIHRvIHRoZSBtaXRvY2hvbmRyaWFsIGdlbm9tZS4gSGlnaCBwcm9wb3J0aW9ucyBvZiBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHRzIG1heSBpbmRpY2F0ZSBhIGRhbWFnZWQgY2VsbCwgdGhlIG1lYXN1cmUgb2Ygd2hpY2ggbWF5IGFsc28gZGlzdG9ydCBkb3duc3RyZWFtIGFuYWx5c2lzIHN0ZXBzLgoKVGhlIG51bWJlciBvZiBVTUlzIGRldGVjdGVkIChgbkNvdW50YCkgYW5kIG51bWJlciBvZiBleHByZXNzZWQgZmVhdHVyZXMgKGBuRmVhdHVyZWApIGFyZSBhbHJlYWR5IGdpdmVuIGluIHRoZSBtZXRhIGRhdGEgdGFibGUuCgo+ICoqV2h5IHRvdGFsIFVNSXMgaW5zdGVhZCBvZiB0b3RhbCByZWFkcz8qKgo+IAo+IFNpbmNlIGEgc2luZ2xlLWNlbGwgaW5oZXJlbnRseSBjb250YWlucyBhIGxpbWl0ZWQgYW1vdW50IG9mIFJOQSBtb2xlY3VsZXMsIGEgaGlnaGVyIGFtb3VudCBvZiBQQ1IgYW1wbGlmaWNhdGlvbiBpcyByZXF1aXJlZCB0byBnZW5lcmF0ZSB0aGUgZmluYWwgc2VxdWVuY2luZyBsaWJyYXJ5Lgo+IAo+IFNpbmNlIFBDUiBjYW4gc2tldyBwcm9wb3J0aW9ucyBvZiBpbml0aWFsIGlucHV0IG1hdGVyaWFscywgc3BlY2lmaWMgc2VxdWVuY2VzIGFyZSBpbmNsdWRlZCBpbiB0aGUgaW5pdGlhbCBjYXB0dXJlIHByb2JlcyBjYWxsZWQgdW5pcXVlIG1vbGVjdWxlIGlkZW50aWZpZXJzIChVTUlzKS4gQXMgZWFjaCBpbml0aWFsIHByb2JlIGhhcyBhIGRpZmZlcmVudCBVTUkgc2VxdWVuY2UsIGVhY2ggUk5BIGNhcHR1cmVkIHdpbGwgYmUgdGFnZ2VkIHdpdGggYSBkaWZmZXJlbnQgVU1JLCB3aGljaCBhbGxvd3MgdGhvc2UgaW5pdGlhbCBSTkFzIGFuZCBzdWJzZXF1ZW50IFBDUiBkdXBsaWNhdGVzIHRvIGJlIGlkZW50aWZpZWQgYW5kIGR1cGxpY2F0ZXMgY29sbGFwc2VkIGFzIHBhcnQgb2YgdGhlIGluaXRpYWwgcHJvY2Vzc2luZyBieSBDZWxsUmFuZ2VyLiAgCj4KCj4gKipPdGhlciBtZWFuaW5ncyBvZiBgbkZlYXR1cmVzYCoqCj4gCj4gRm9yIG90aGVyIHNpbmdsZS1jZWxsIGRhdGEgdHlwZXMsIGBuRmVhdHVyZXNgIHdvdWxkIHJlcHJlc2VudCB3aGF0J3MgYmVpbmcgbWVhc3VyZWQgaW4gdGhhdCBleHBlcmltZW50LiBGb3Igc2luZ2xlLWNlbGwgQVRBQy1zZXEsIGBuRmVhdHVyZXNgIHdvdWxkIHJlcHJlc2VudHMgdGhlIHRvdGFsIG51bWJlciBvZiBwZWFrcyAoZS5nLiBhY2Nlc3NpYmxlIGFyZWFzIG9mIEROQSkgcGVyIGNlbGwuCj4KCjwhLS0gTWF0dCBtYXkgaGF2ZSBkaXNjdXNzZWQgVU1JcyBhcyBwYXJ0IG9mIENlbGxSYW5nZXIgcHJvY2Vzc2luZyBhbmQgTGl2L1RyaWNpYSBtYXkgdG91Y2ggb24gYXMgcGFydCBvZiBsaWJyYXJ5IGdlbmVyYXRpb24gYXQgZW5kIG9mIERheSAyIC0tPgoKIyMgQ2VsbCBjb3VudHMKCkNlbGwgY291bnRzIHBlciBzYW1wbGUgKGJhc2VkIG9uIHRoZSBudW1icmVyIG9mIHVuaXF1ZSBjZWxsdWxhciBiYXJjb2RlcyBkZXRlY3RlZCBieSBDZWxsIFJhbmdlcikgY2FuIGluZGljYXRlIGlmIGFuIGVudGlyZSBzYW1wbGUgd2FzIG9mIHBvb3IgcXVhbGl0eS4gVGhlIHRhYmxlIGJlbG93IGlzIHRoZSBudW1iZXIgb2YgY2VsbHMgY2FsbGVkIGJ5IENlbGwgUmFuZ2VyLCB3aXRoIHRoZSBhZGRlZCBmaWx0ZXIgZnJvbSBgQ3JlYXRlU2V1cmF0ZU9iamVjdCgpYCBpbiB0aGUgcHJldmlvdXMgbGVzc29uOiBgbWluLmNlbGxzID0gMSwgbWluLmZlYXR1cmVzID0gNTBgLiBSZWNhbGwsIHRoaXMgbWVhbnMgYSBnZW5lIGlzIHJlbW92ZWQgaWYgaXQgaXMgZXhwcmVzc2VkIGluIDEgb3IgZmV3ZXIgY2VsbHMsIGFuZCBhIGNlbGwgaXMgcmVtb3ZlZCBpZiBpdCBjb250YWlucyByZWFkcyBmb3IgNTAgb3IgZmV3ZXIgZ2VuZXMuCgpgYGB7ciwgcHJlZmlsdGVyX2NlbGxfY291bnRzfQojIEFzc2lnbiBvdmVyYWxsIGNlbGwgY291bnRzICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KY2VsbF9jb3VudHNfcHJlX3RibCA9IGdlb19zb0BtZXRhLmRhdGEgJT4lIGNvdW50KG9yaWcuaWRlbnQsIG5hbWUgPSAncHJlZmlsdGVyX2NlbGxzJykKY2VsbF9jb3VudHNfcHJlX3RibApgYGAKCkl0IGFwcGVhcnMgdGhhdCB0aGUgRGF5IDcgc2FtcGxlcyBoYXZlIHN5c3RlbWF0aWNhbGx5IG1vcmUgY2VsbHMgdGhhbiB0aGUgb3RoZXIgZGF5cy4gSWYgeW91IGhhZCBhbiBpZGVhIG9mIGhvdyBtYW55IGNlbGxzIHlvdSBleHBlY3RlZCB0byBzZWUgcGVyIHNhbXBsZSwgdGhpcyB0YWJsZSBjYW4gaGVscCB5b3UgY2hlY2sgdGhhdCBleHBlY3RhdGlvbiBhbmQgZGV0ZXJtaW5lIGlmIGEgc2FtcGxlIGZhaWxlZCBhbmQgc2hvdWxkIGJlIGRyb3BwZWQuCgojIyBWaXN1YWxpemluZyBxdWFsaXR5IG1ldHJpY3MKCkEgdmlvbGluIHBsb3Qgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIHF1YW50aXR5IGFtb25nIHRoZSBjZWxscyBpbiBhIHNpbmdsZSBzYW1wbGUsIG9yIGFjcm9zcyBtYW55IHNhbXBsZXMuIFNldXJhdCBoYXMgYSBidWlsdC1pbiBmdW5jdGlvbiwgYFZsblBsb3QoKWAgZm9yIHRoaXMgcHVycG9zZS4gTGV0J3Mgb3JpZW50IHdpdGggYSB2aW9saW4gcGxvdCBvZiBgbkZlYXR1cmVfUk5BYCBmb3Igb25seSBvbmUgc2FtcGxlLCBgSE8uRGF5MjEucmVwbGljYXRlMWA6CgpgYGB7ciwgc2luZ2xlX3Zpb2xpbl9wbG90LCBlY2hvID0gRkFMU0V9ClZsblBsb3Qoc3Vic2V0KGdlb19zbywgb3JpZy5pZGVudCA9PSAnSE8uRGF5MjEucmVwbGljYXRlMScpLCBmZWF0dXJlcyA9ICduRmVhdHVyZV9STkEnLCBhc3NheSA9ICdSTkEnLCBsYXllciA9ICdjb3VudHMnKSArIE5vTGVnZW5kKCkKYGBgCgpBIHZpb2xpbiBwbG90IGlzIHNpbWlsYXIgdG8gYSBib3ggcGxvdCwgYnV0IGl0IHNob3dzIHRoZSBkZW5zaXR5IG9mIHRoZSBkYXRhIGF0IGRpZmZlcmVudCB2YWx1ZXMuIEhlcmUgdGhlIGluZGl2aWR1YWwgcG9pbnRzIGFyZSB0aGUgY2VsbHMgZnJvbSBgSE8uRGF5MjEucmVwbGljYXRlMWAgYW5kIHRoZSB5LWF4aXMgaXMgdGhlIHZhbHVlIG9mIGBuRmVhdHVyZV9STkFgIGZvciB0aGF0IGNlbGwuIFRoZSB2aW9saW4gcGFydCBvZiB0aGUgZnVuY3Rpb24gaXMgZXNzZW50aWFsbHkgc2hvd2luZyB0aGUgZGVuc2l0eSBvZiB0aGUgY2VsbHMgYXQgZGlmZmVyZW50IHZhbHVlcyBvZiBgbkZlYXR1cmVfUk5BYC4KCiMjIyBHZW5lcyBwZXIgY2VsbAoKTGV0J3MgbG9vayBhdCB0aGUgYG5GZWF0dXJlX1JOQWAgdmlvbGluIHBsb3QgYWNyb3NzIGFsbCB0aGUgc2FtcGxlcy4gQWdhaW4sIHRoaXMgaXMgdGhlIG51bWJlciBvZiBnZW5lcyBkZXRlY3RlZCBwZXIgY2VsbC4KCmBgYHtyLCBmZWF0dXJlX3Bsb3QsIGZpZy5zaG93ID0gJ2hvbGQnfQojIFJldmlldyBmZWF0dXJlIHZpb2xpbiBwbG90cyAgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KVmxuUGxvdChnZW9fc28sIGZlYXR1cmVzID0gJ25GZWF0dXJlX1JOQScsIGFzc2F5ID0gJ1JOQScsIGxheWVyID0gJ2NvdW50cycpICsgTm9MZWdlbmQoKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUwMCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA0MDApICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMzAwKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDIwMCkKZ2dzYXZlKGZpbGVuYW1lID0gJ3Jlc3VsdHMvZmlndXJlcy9xY19uRmVhdHVyZV92aW9saW4ucG5nJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAnaW4nKQpgYGAKCldlIG9ic2VydmUgdGhhdCBzYW1wbGVzIGJlaGF2ZSBzaW1pbGFybHkgd2l0aGluIGEgZGF5LCBidXQgaGF2ZSBkaWZmZXJlbnQgZGlzdHJpYnV0aW9ucyBhY3Jvc3MgZGF5cy4gV2UgYWxzbyBub3RlIHRoYXQgbWFueSBjZWxscyBhcHBlYXIgdG8gaGF2ZSBhIGxvdyBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQgKDwgNTAwKS4KCiMjIyBVTUkgY291bnRzIHBlciBjZWxsCgpOb3cgbGV0J3MgbG9vayBhdCBgbkNvdW50X1JOQWAgcGxvdCwgdGhlIHRvdGFsIG51bWJlciBvZiBVTUlzIGRldGVjdGVkLgoKYGBge3IsIGNvdW50X3Bsb3QsIGZpZy5zaG93ID0gJ2hvbGQnfQojIFJldmlldyBjb3VudCB2aW9saW4gcGxvdHMgICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KVmxuUGxvdChnZW9fc28sIGZlYXR1cmVzID0gJ25Db3VudF9STkEnLCBhc3NheSA9ICdSTkEnLCBsYXllciA9ICdjb3VudHMnKSArIE5vTGVnZW5kKCkKZ2dzYXZlKGZpbGVuYW1lID0gJ3Jlc3VsdHMvZmlndXJlcy9xY19uQ291bnRfdmlvbGluLnBuZycsIHdpZHRoID0gMTIsIGhlaWdodCA9IDYsIHVuaXRzID0gJ2luJykKYGBgCgpXZSBvYnNlcnZlIGEgc2ltaWxhciB3aXRoaW4tYW5kLWFjcm9zcyBkYXkgYmVoYXZpb3IgYW1vbmcgdGhlIHNhbXBsZXMuIE92ZXJhbGwgaXQgYXBwZWFycyB0aGF0IHRoZSBEYXkgMCBzYW1wbGVzIGhhdmUgZmV3ZXIgVU1JcyBkZXRlY3RlZCBwZXIgY2VsbCB0aGFuIERheSA3IGFuZCBEYXkgMjEuIEFsc28gb2Ygbm90ZSBpcyB0aGUgb3V0bGllciBjZWxsIGluIEhPLkRheTAucmVwbGljYXRlMSwgd2l0aCBhcm91bmQgMTAwSyBVTUlzIGRldGVjdGVkLiBUaGlzIGNlbGwgbWlnaHQgYmUgYSBkb3VibGV0LgoKIyMjIFBlcmNlbnQgbWl0b2Nob25kcmlhbCByZWFkcwoKRmluYWxseSwgbGV0cyBwbG90IHRoZSBwZXJjZW50IG1pdG9jaG9uZHJpYWwgcmVhZHMsIGBwZXJjZW50Lm10YC4KCmBgYHtyLCBtaXRvX3Bsb3QsIGZpZy5zaG93ID0gJ2hvbGQnfQojIFJldmlldyBtaXRvY2hvbmRyaWFsIHZpb2xpbiBwbG90cyAgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KVmxuUGxvdChnZW9fc28sIGZlYXR1cmVzID0gJ3BlcmNlbnQubXQnLCBhc3NheSA9ICdSTkEnLCBsYXllciA9ICdjb3VudHMnKSArIE5vTGVnZW5kKCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAyNSkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAyMCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxNSkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxMCkKZ2dzYXZlKGZpbGVuYW1lID0gJ3Jlc3VsdHMvZmlndXJlcy9xY19taXRvX3Zpb2xpbi5wbmcnLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA2LCB1bml0cyA9ICdpbicpCmBgYAoKV2Ugb2JzZXJ2ZSB0aGUgbWFqb3JpdHkgb2YgY2VsbHMgc2VlbSB0byBoYXZlIDwgMjUlIG1pdG9jaG9uZHJpYWwgcmVhZHMsIGJ1dCB0aGVyZSBhcmUgbWFueSBjZWxscyB3aXRoID4gMjUlIHRoYXQgd2UgbWF5IHdhbnQgdG8gcmVtb3ZlLgoKR2VuZXJhbGx5LCBtYW55IHR1dG9yaWFscyB1c2UgYSBjdXRvZmYgb2YgNSAtIDEwJSBtaXRvY2hvbmRyaWFsIHJlYWRzLiBIb3dldmVyLCBmb3Igc29tZSBleHBlcmltZW50cywgaGlnaCBtaXRvY2hvbmRyaWFsIHJlYWRzIHdvdWxkIGJlIGV4cGVjdGVkIChzdWNoIGFzIGluIGNhc2VzIHdoZXJlIHRoZSBjb25kaXRpb24vdHJlYXRtZW50IG9yIGdlbm90eXBlIGluY3JlYXNlcyBjZWxsIGRlYXRoKS4gSW4gdGhpcyBjYXNlLCBhIHJlbGF4ZWQgdGhyZXNob2xkIHdvdWxkIGhlbHAgcHJlc2VydmUgYmlvbG9naWNhbGx5IHJlbGV2YW50IGNlbGxzLiBJbiBvdXIgY2FzZSwgdGhlIGNlbGxzIHdlcmUgY29sbGVjdGVkIGFzIHBhcnQgb2YgYW4gaW5qdXJ5IG1vZGVsLCBwZXJoYXBzIGp1c3RpZnlpbmcgYSBtb3JlIGxlbmllbnQgcGVyY2VudCBtaXRvY2hvbmRyaWFsIHJlYWQgZmlsdGVyLgoKIyBGaWx0ZXJpbmcgYXBwcm9hY2hlcwoKIyMgRml4ZWQgdGhyZXNob2xkcwoKRml4ZWQgdGhyZXNob2xkcyBmb3IgYW55IGNvbWJpbmF0aW9uIG9mIGBuRmVhdHVyZV9STkFgLCBgbkNvdW50X1JOQWAsIGFuZCBgcGVyY2VudC5tdGAgY2FuIGJlIHNlbGVjdGVkIHRvIHJlbW92ZSBsb3cgcXVhbGl0eSBjZWxscy4gSW4gZ2VuZXJhbCwgc2VsZWN0aW5nIHZlcnkgc3RyaW5nZW50IHRocmVzaG9sZHMgZm9yIGVhY2ggb2YgdGhlIG1ldHJpY3MgbWF5IGxlYWQgdG8gcmVtb3ZpbmcgaW5mb3JtYXRpdmUgY2VsbHMuIEFzIHBlciB0aGUgSEJDIHRyYWluaW5nIG1hdGVyaWFscyAoW2xpbmtdKGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL3NjUk5BLXNlcV9vbmxpbmUvbGVzc29ucy8wNF9TQ19xdWFsaXR5X2NvbnRyb2wuaHRtbCkpLCB3ZSByZWNvbW1lbmQgc2V0dGluZyBpbmRpdmlkdWFsIHRocmVzaG9sZHMgdG8gZXJyIG9uIHRoZSBwZXJtaXNzaXZlIHNpZGUuCgpbU29ya2luIGV0IGFsLl0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNzAwMjQ1My8pIGNob3NlIHRoZXNlIHRocmVzaG9sZHM6Cgo+IFdlIGZpbHRlcmVkIG91dCBjZWxscyB3aXRoIGxlc3MgdGhhbiA1MDAgZ2VuZXMgcGVyIGNlbGwgYW5kIHdpdGggbW9yZSB0aGFuIDI1JSBtaXRvY2hvbmRyaWFsIHJlYWQgY29udGVudC4KCkluIG90aGVyIHdvcmRzLCBjZWxscyB3aXRoID4gNTAwIGBuRmVhdHVyZV9STkFgIGFuZCA8IDI1JSBgcGVyY2VudC5tdGAgd2VyZSByZXRhaW5lZCBieSB0aGUgYXV0aG9ycy4gV2UgY291bGQgcHJldmlldyB3aGF0IHRoZSByZXN1bHRpbmcgY2VsbCBjb3VudHMgd291bGQgYmUgd2l0aCB0aGVzZSB0aHJlc2hvbGRzOgoKYGBge3IsIHByZXZpZXdfcG9zdGZpbHRlcl9jZWxsX2NvdW50c30KIyBDb25zaWRlciBleGNsdWRpbmcgc3VzcGVjdCBjZWxscyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnN1YnNldChnZW9fc28sIHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDUwMCAmIHBlcmNlbnQubXQgPCAyNSlAbWV0YS5kYXRhICU+JSAKICAgIGNvdW50KG9yaWcuaWRlbnQsIG5hbWUgPSAncG9zdGZpbHRlcl9jZWxscycpCmBgYAoKIyMgQWRhcHRpdmUgdGhyZXNob2xkcwoKRm9yIHRoaXMgd29ya3Nob3Agd2Ugd2lsbCB1c2UgZml4ZWQgdGhyZXNob2xkcywgYnV0IGFub3RoZXIgb3B0aW9uIGlzIHRvIHJlbW92ZSBsb3ctcXVhbGl0eSBjZWxscyBhZGFwdGl2ZWx5LiBUaGlzIGFwcHJvYWNoIGFzc3VtZXMgdGhhdCBtb3N0IG9mIHRoZSBjZWxscyBhcmUgb2YgYWNjZXB0YWJsZSBxdWFsaXR5LiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBzZWUgdGhlIHJlbGV2YW50IHNlY3Rpb24gaW4gQmlvY29uZHVjdG9yJ3Mgb25saW5lIGJvb2s6IE9yY2hlc3RyYXRpbmcgU2luZ2xlLUNlbGwgQW5hbHlzaXMgKFtsaW5rXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xMi9PU0NBL3F1YWxpdHktY29udHJvbC5odG1sI3F1YWxpdHktY29udHJvbC1vdXRsaWVyKSkuCgojIFJlbW92aW5nIGxvdy1xdWFsaXR5IGNlbGxzCgpXZSB3aWxsIGRldmlhdGUgZnJvbSB0aGUgcHVibGljYXRpb24gc2xpZ2h0bHkgYW5kIHJldGFpbiB0aGUgY2VsbHMgd2l0aCBgbkZlYXR1cmVfUk5BID4gMzAwYCBhbmQgYHBlcmNlbnQubXQgPCAxNWAuIE5vdGluZyB0aGF0IHRoZSBgbkZlYXR1cmVfUk5BYCBwbG90IGhhZCBhIGdhcCBhcm91bmQgMzAwLCBhbmQgdGhhdCB3ZSB3YW50IHRvIGJlIG1vcmUgbGVuaWVudCB0aGFuIHRoZSB1c3VhbCA1LTEwJSBjdXRvZmYgZm9yIG1pdG9jaG9uZHJpYWwgcmVhZHMuCgpgYGB7ciwgZmlsdGVyX3NldXJhdH0KIyBGaWx0ZXIgdG8gZXhjbHVkZSBzdXNwZWN0IGNlbGxzIGFuZCBhc3NpZ24gdG8gbmV3IFNldXJhdCBvYmplY3QgLS0tLS0tCmdlb19zbyA9IHN1YnNldChnZW9fc28sIHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDMwMCAmIHBlcmNlbnQubXQgPCAxNSkKZ2VvX3NvCmBgYAoKQW5kIGxldCdzIHJlY29yZCB0aGUgbnVtYmVyIG9mIGNlbGxzIHJlbWFpbmluZyBhZnRlciBmaWx0ZXJpbmc6CgpgYGB7ciwgcG9zdGZpbHRlcl9jZWxsX2NvdW50c30KIyBFeGFtaW5lIHJlbWFpbmluZyBjZWxsIGNvdW50cyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmNlbGxfY291bnRzX3Bvc3RfdGJsID0gZ2VvX3NvQG1ldGEuZGF0YSAlPiUgY291bnQob3JpZy5pZGVudCwgbmFtZSA9ICdwb3N0ZmlsdGVyX2NlbGxzJykKY2VsbF9jb3VudHNfcG9zdF90YmwKYGBgCgpMZXQncyBjb21iaW5lIHRoZSBwcmUgYW5kIHBvc3QgdGFibGVzOgoKYGBge3IsIGNlbGxfY291bnRzfQojIFNob3cgY2VsbCBjb3VudHMgYmVmb3JlIGFuZCBhZnRlciBmaWx0ZXJpbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KY2VsbF9jb3VudHNfdGJsID0gY2VsbF9jb3VudHNfcHJlX3RibCAlPiUgbGVmdF9qb2luKGNlbGxfY291bnRzX3Bvc3RfdGJsLCBieSA9ICdvcmlnLmlkZW50JykKY2VsbF9jb3VudHNfdGJsCmBgYAoKV2UgY2FuIG5vdyBlYXNpbHkgc2VlIGhvdyBtYW55IGNlbGxzIHdlIHN0YXJ0ZWQgd2l0aCwgYW5kIGhvdyBtYW55IHdlIHJldGFpbmVkIGZvciBkb3duc3RyZWFtIGFuYWx5c2lzLiBMZXQncyB3cml0ZSB0aGlzIHRhYmxlIHRvIGEgZmlsZS4KCmBgYHtyLCB3cml0ZV9jZWxsX2NvdW50c30Kd3JpdGVfY3N2KGNlbGxfY291bnRzX3RibCwgZmlsZSA9ICdyZXN1bHRzL3RhYmxlcy9jZWxsX2ZpbHRlcmluZ19jb3VudHMuY3N2JykKYGBgCgo8IS0tV2hhdCB0aHJlc2hvbGRzIHdvdWxkIHdlIHN0YXJ0IHdpdGggYmFzZWQgb24gdGhlIHBsb3RzIGFsb25lPyBIb3cgZG9lcyB0aGF0IGNvbXBhcmUgdG8gdGhlIHRocmVzaG9sZHMgcmVwb3J0ZWQgaW4gcGFwZXI/LS0+Cgo8IS0tT3RoZXIg4oCcYWR2YW5jZWTigJ0gbWV0aG9kczogb3V0IG9mIHNjb3BlIGZvciB0aGlzIHdvcmtzaG9wIGJ1dCB0aGVyZSBhcmUgcGFja2FnZXMgIHNwZWNpZmljYWxseSBkZXZlbG9wZWQgdG8gZGV0ZWN0ICJkb3VibGV0cyIsIGUuZy4gZHJvcGxldHMgdGhhdCBjb250YWluZWQgbW9yZSB0aGFuIG9uZSBjZWxsLCBzdWNoIGFzIERvdWJsZUZpbmRlci0tPgoKPCEtLUFkZGl0aW9uYWwgYXNpZGUgLSBGb3Igc2luZ2xlLW51Y2xlaSBleHBlcmltZW50cyByZW1vdmluZyBiYWNrZ3JvdW5kL2FtYmllbnQgUk5BIHdpdGggQ2VsbEJsZW5kZXIgT1IgRGVjb250WCBpcyBhbiBpbXBvcnRhbnQgYWRkaXRpb25hbCBzdGVwIHNpbmNlIG51Y2xlaSBhcmUgYm90aCBzdGlja3kgYW5kIHBvcm91cy0tPgoKIyBSZXZpc2luZyB0aHJlc2hvbGRzCgpJdCBpcyBpbXBvcnRhbnQgdG8gcmVjb2duaXplIHRoYXQgdGhyZXNob2xkcyBmb3IgcmV0YWluaW5nIGNlbGxzIGFyZSBub3Qgc2V0IGluIHN0b25lLiBJdCBtYXkgaGFwcGVuIHRoYXQgdGhlIGRvd25zdHJlYW0gYW5hbHlzaXMgc3VnZ2VzdHMgdGhhdCB0aHJlc2hvbGRzIHNob3VsZCBiZSBtb3JlIG9yIGxlc3Mgc3RyaW5nZW50LiBJbiB3aGljaCBjYXNlLCB5b3UgbWF5IGV4cGVyaW1lbnQgd2l0aCB0aGUgdGhyZXNob2xkcyBhbmQgb2JzZXJ2ZSB0aGUgZG93bnN0cmVhbSBlZmZlY3RzLgoKIyBTYXZlIG91ciBwcm9ncmVzcwoKTGV0J3Mgc2F2ZSB0aGlzIGZpbHRlcmVkIGZvcm0gb2Ygb3VyIFNldXJhdCBvYmplY3QuIEl0IHdpbGwgYWxzbyBpbmNsdWRlIG91ciBjaGFuZ2VzIHRvIHRoZSBgbWV0YS5kYXRhYDoKCmBgYHtyLCBzYXZlX3Jkc19oaWRkZW4sIGVjaG8gPSBGQUxTRX0KaWYoIWZpbGUuZXhpc3RzKCdyZXN1bHRzL3JkYXRhL2dlb19zb19maWx0ZXJlZC5yZHMnKSkgewogIHNhdmVSRFMoZ2VvX3NvLCBmaWxlID0gJ3Jlc3VsdHMvcmRhdGEvZ2VvX3NvX2ZpbHRlcmVkLnJkcycpCn0KYGBgCgpgYGB7ciwgc2F2ZV9yZHMsIGV2YWwgPSBGQUxTRX0KIyBTYXZlIHRoZSBjdXJyZW50IFNldXJhdCBvYmplY3QgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnNhdmVSRFMoZ2VvX3NvLCBmaWxlID0gJ3Jlc3VsdHMvcmRhdGEvZ2VvX3NvX2ZpbHRlcmVkLnJkcycpCmBgYAoKIyBTdW1tYXJ5CgpJbiB0aGlzIHNlY3Rpb24gd2U6CgotIERpc2N1c3NlZCB0aGUgYmlnIHRocmVlIHF1YWxpdHkgbWV0cmljczogYG5GZWF0dXJlYHMsIGBuQ291bnRgcywgYW5kIGBwZXJjZW50Lm10YC4KLSBWaXN1YWxpemVkIHRoZXNlIG1ldHJpY3MgYWNyb3NzIGNlbGxzIC8gc2FtcGxlcyB0byBoZWxwIGlkZW50aWZ5IGxvdy1xdWFsaXR5IGNlbGxzLgotIEZpbHRlcmVkIGxvdy1xdWFsaXR5IGNlbGxzIHVzaW5nIGZpeGVkIHRocmVzaG9sZHMuCgpOZXh0IHN0ZXBzOiBOb3JtYWxpemF0aW9uCgotLS0tCgpUaGVzZSBtYXRlcmlhbHMgaGF2ZSBiZWVuIGFkYXB0ZWQgYW5kIGV4dGVuZGVkIGZyb20gbWF0ZXJpYWxzIGxpc3RlZCBhYm92ZS4gVGhlc2UgYXJlIG9wZW4gYWNjZXNzIG1hdGVyaWFscyBkaXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIGxpY2Vuc2UgKENDIEJZIDQuMCldKGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8pLCB3aGljaCBwZXJtaXRzIHVucmVzdHJpY3RlZCB1c2UsIGRpc3RyaWJ1dGlvbiwgYW5kIHJlcHJvZHVjdGlvbiBpbiBhbnkgbWVkaXVtLCBwcm92aWRlZCB0aGUgb3JpZ2luYWwgYXV0aG9yIGFuZCBzb3VyY2UgYXJlIGNyZWRpdGVkLgoKPGJyLz4KPGJyLz4KCi0tLS0tLS0KCnwgW1ByZXZpb3VzIGxlc3Nvbl0oMDBCLUNlbGxSYW5nZXJJbkFjdGlvbi5odG1sKSB8IFtUb3Agb2YgdGhpcyBsZXNzb25dKCN0b3ApIHwgW05leHQgbGVzc29uXSgwMy1Ob3JtYWxpemF0aW9uLmh0bWwpIHwKfCA6LS0tIHwgOi0tLS06IHwgLS0tOiB8Cg==