Workflow Overview


wayfinder

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

  1. 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.
  2. 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.
  3. 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: nFeatures, nCounts, 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.




Previous lesson Top of this lesson Next lesson
LS0tCnRpdGxlOiAiU2Vjb25kYXJ5IFF1YWxpdHkgQ29udHJvbCBhbmQgRmlsdGVyaW5nIgphdXRob3I6ICJVTSBCaW9pbmZvcm1hdGljcyBDb3JlIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAgICAgICBodG1sX2RvY3VtZW50OgogICAgICAgICAgICBpbmNsdWRlczoKICAgICAgICAgICAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgICAgICAgICAgdGhlbWU6IHBhcGVyCiAgICAgICAgICAgIHRvYzogdHJ1ZQogICAgICAgICAgICB0b2NfZGVwdGg6IDQKICAgICAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgICAgICAgICAgZmlnX2NhcHRpb246IHRydWUKICAgICAgICAgICAgbWFya2Rvd246IEdGTQogICAgICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSwgdGQgewogICBmb250LXNpemU6IDE4cHg7Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTJweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogMTJweAp9Cgp0YWJsZS5maWcsIHRoLmZpZywgdGQuZmlnIHsKICBib3JkZXI6IDFweCBzb2xpZCBibGFjazsKICBib3JkZXItY29sbGFwc2U6IGNvbGxhcHNlOwogIHBhZGRpbmc6IDE1cHg7Cn0KCnRhYmxlewogICB3aWR0aDoxMDAlOwp9Cjwvc3R5bGU+CgpgYGB7ciBrbGlwcHksIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRX0Ka2xpcHB5OjprbGlwcHkobGFuZyA9IGMoInIiLCAibWFya2Rvd24iLCAiYmFzaCIpLCBwb3NpdGlvbiA9IGMoInRvcCIsICJyaWdodCIpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnNvdXJjZSgiLi4vYmluL2NodW5rLW9wdGlvbnMuUiIpCmtuaXRyX2ZpZ19wYXRoKCIwMi1RQ2FuZEZpbHRlcmluZy8wMi0iKQpgYGAKCiMgV29ya2Zsb3cgT3ZlcnZpZXcgey51bmxpc3RlZCAudW5udW1iZXJlZH0KCjxici8+CjxpbWcgc3JjPSJpbWFnZXMvd2F5ZmluZGVyL3dheWZpbmRlci5wbmciIGFsdD0id2F5ZmluZGVyIiBzdHlsZT0iaGVpZ2h0OiA0MDBweDsiLz4KPGJyLz4KPGJyLz4KCiMgSW50cm9kdWN0aW9uCgpBcyBkaXNjdXNzZWQgYXQgdGhlIHN0YXJ0IG9mIHRoZSB3b3Jrc2hvcCwgc2luZ2xlLWNlbGwgZXhwZXJpbWVudHMgdXNpbmcgMTB4IENocm9taXVtIGluc3RydW1lbnQgYWltIHRvIGhhdmUgZHJvcGxldHMgd2l0aCBvbmUgY2VsbCBwbHVzIG9uZSBiZWFkLiBIb3dldmVyIHRoaXMgaXMgYW4gaW5oZXJlbnRseSBpbXBlcmZlY3QgcHJvY2VzcyBhbmQgdGhlcmUgYXJlIG90aGVyIGltcG9ydGFudCBjb25zaWRlcmF0aW9ucyBsaWtlIGhvdyBoZWFsdGh5IG9yIGludGFjdCB0aGUgY2VsbCB3YXMgYXQgdGhlIHRpbWUgb2YgbWVhc3VyZW1lbnQuCgpJbiB0aGlzIHNlY3Rpb24sIG91ciBnb2FsIGlzIHRvIHVzZSBmaWx0ZXJpbmcgdGhyZXNob2xkcyB0byByZW1vdmUgImNlbGxzIiB0aGF0IHdlcmUgcG9vcmx5IG1lYXN1cmVkLCBub3QgY2VsbHMgYXQgYWxsLCBvciBpbmNsdWRlZCBtb3JlIHRoYW4gb25lIGNlbGwuCgo8dGFibGUgY2xhc3M9J2ZpZyc+PHRyPjx0ZCBjbGFzcz0nZmlnJz4KIVtdKGltYWdlcy9ncmFwaGljYWxfYWJzdHJhY3RzL2dyYXBoaWNhbF9hYnN0cmFjdF9RQ19hbmRfZmlsdGVyaW5nLnBuZykKPC90ZD48L3RyPjx0cj48dGQgY2xhc3M9J2ZpZyc+VGhlIENlbGwgUmFuZ2VyIGJhcmNvZGUgZmVhdHVyZSBtYXRyaXggb3V0cHV0cyAoQSkgaXMgYSBtaXggb2YgZ29vZCBhbmQgcG9vciBxdWFsaXR5IGNlbGxzIChCKS4gV2UgY2FuIHVzZSBleHByZXNzaW9uIHBhdHRlcm5zIHRvIGZ1cnRoZXIgdmlzdWFsaXplIGFuZCBmaWx0ZXIgdG8gdGhlIHN1YnNldCBvZiBwdXRhdGl2ZSBoZWFsdGh5IGNlbGxzIChDKSB0byBpbXByb3ZlIGRvd25zdHJlYW0gYW5hbHlzaXMuCjwvdGQ+PC90cj48L3RhYmxlPgo8YnIvPgpTaW1pbGFyIHRvIG1hbnkgb3RoZXIgYXJlYXMgb2YgcmVzZWFyY2gsIHRoZXJlIGFyZSBvZnRlbiBnYXBzIGJldHdlZW4gaG93IHNpbmdsZS1jZWxsIGRhdGEgaXMgcHJlc2VudGVkIHZlcnN1cyB0aGUgcmVhbGl0eSBvZiBydW5uaW5nIGFuIGFuYWx5c2lzLiBGb3IgZXhhbXBsZSwgb25seSB0aGUgZmluYWwgZmlsdGVyaW5nIHRocmVzaG9sZHMgbWlnaHQgYmUgcmVwb3J0ZWQgaW4gYSBwYXBlciBidXQgb3VyIHByb2Nlc3MgZm9yIGNob29zaW5nIHRob3NlIGlzIGxpa2VseSB0byBiZSBtb3JlIGl0ZXJhdGl2ZSBhbmQgaW5jbHVkZSBzb21lIHRyaWFsIGFuZCBlcnJvci4KCgojIyBPYmplY3RpdmVzCgotIERpc2N1c3MgUUMgbWVhc3VyZXMgYW5kIGxlYXJuIGhvdyB0byBjYWxjdWxhdGUgYW5kIHBsb3QgdGhlbS4KLSBEaXNjdXNzIGNlbGwtZmlsdGVyaW5nIGFwcHJvYWNoZXMgYW5kIGFwcGx5IHRoZW0gdG8gb3VyIGRhdGFzZXQuCgo8IS0tIENoYWxsZW5nZSBmb3IgaW5zdHJ1Y3RvcnM6IEV2ZXJ5IHZpZ25ldHRlIHVzZXMgZGlmZmVyZW50IGZpbHRlcnMsIGhvdyB0byBoYXJtb25pemUvZ2l2ZSBndWlkYW5jZT8gUmVsYXRlZCwgaG93IG11Y2ggdG8gZGlzY3VzcyBhcmJpdHJhcnkgY3V0b2ZmcyBhbmQgY29udGludWVkIG1hdHVyYXRpb24gb2YgZmllbGQ/LS0+IAo8IS0tIEFkZCBsaW5rcyB0byByZWxldmFudCByZXNvdXJjZXMgdGhyb3VnaG91dCAtLT4gCgo8IS0tIEdlbmVyYWwgZ3VpZGFuY2UgLSBsaWtlbHkgdG8gYmUgbW92ZWQgdG8gZWFybGllciBzZWN0aW9uOgotIE5vdGUgd2l0aCBlYWNoIGZ1bmN0aW9uIGNhbGwgd2hhdCBnZXRzIGFkZGVkIHRvIHRoZSBTZXVyYXQgb2JqZWN0LgotIEFkZGluZyBjaGVja3MgdG8gZW5zdXJlIG9iamVjdCBpcyB1cGRhdGVkIGJ5IGxlYXJuZXJzIHNpbmNlIHdhbnQgdG8gYXZvaWQgZ2VuZXJhdGluZyBjb3BpZWQgb2JqZWN0cwotIE5vdGUgd2hhdCBsYXllcnMgc2hvdWxkIGJlIHVzZWQgZm9yIHdoYXQsIGZvciBleGFtcGxlLCBjb3VudHMgdXNlZCBmb3IgRmVhdHVyZVBsb3RzLiBSTkEgdnMgU0NUIGFzc2F5LiAtLT4KCi0tLQoKYGBge3IsIHJlYWRfcmRzX2hpZGRlbiwgZWNobyA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KaWYoIWV4aXN0cygnZ2VvX3NvJykpIHsKICBsaWJyYXJ5KFNldXJhdCkKICBsaWJyYXJ5KEJQQ2VsbHMpCiAgbGlicmFyeSh0aWR5dmVyc2UpCgogIG9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDFlOSkKCiAgZ2VvX3NvID0gcmVhZFJEUygncmVzdWx0cy9yZGF0YS9nZW9fc29fdW5maWx0ZXJlZC5yZHMnKQp9CmBgYAoKIyBBZGRpbmcgbWV0YWRhdGEKCldlJ3JlIGdvaW5nIHRvIGFsdGVyIGFuZCBhZGQgc29tZSBjb2x1bW5zIHRvIGBtZXRhLmRhdGFgIHRvIGVhc2Ugc29tZSBkb3duc3RyZWFtIGFuYWx5c2lzIGFuZCBwbG90dGluZyBzdGVwcy4gVGhpcyB3aWxsIG9mdGVuIGJlIG5lY2Vzc2FyeSwgYXMgc2FtcGxlIG5hbWVzIGNvbnRhaW4gY29tYmluZWQgaW5mb3JtYXRpb24gYWJvdXQgcGhlbm90eXBlcy4gTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGZpcnN0IGZldyByb3dzIGFnYWluLCB0byByZWFjcXVhaW50IG91cnNlbHZlcy4KCmBgYHtyLCBwcmV2aWV3X21ldGFkYXRhMX0KIyMjIyMgRGF5IDEgLSBTZWNvbmRhcnkgUUMgYW5kIGZpbHRlcmluZwoKIyBFeGFtaW5lIFNldXJhdCBtZXRkYXRhICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmhlYWQoZ2VvX3NvQG1ldGEuZGF0YSkKYGBgCgpXZSBjYW4gYWRkIGFyYml0cmFyeSBwZXItY2VsbCBpbmZvcm1hdGlvbiB0byB0aGlzIHRhYmxlIHN1Y2ggYXM6CgotIFN1bW1hcnkgc3RhdGlzdGljcywgc3VjaCBhcyBwZXJjZW50IG1pdG9jaG9uZHJpYWwgcmVhZHMgZm9yIGVhY2ggY2VsbC4KLSBCYXRjaCwgY29uZGl0aW9uLCBldGMuIGZvciBlYWNoIGNlbGwuCi0gQ2x1c3RlciBtZW1iZXJzaGlwIGZvciBlYWNoIGNlbGwuCi0gQ2VsbCBjeWNsZSBwaGFzZSBmb3IgZWFjaCBjZWxsLgotIE90aGVyIGN1c3RvbSBhbm5vdGF0aW9ucyBmb3IgZWFjaCBjZWxsLgoKVGhlIGZvbGxvd2luZyBjb2RlIGNodW5rIHdpbGwgY3JlYXRlIG5ldyBjb2x1bW5zIGZyb20gdGhlIGBvcmlnLmlkZW50YCBjb2x1bW4sIG9uZSBmb3IgdGhlIGBkYXlgIGFuZCBvbmUgZm9yIHRoZSBgcmVwbGljYXRlYC4gV2UnbGwgYWxzbyB1c2UgZmFjdG9ycyB0byBtYWtlIHRoZSBkYXlzIGFwcGVhciBpbiB0aGUgY29ycmVjdCBvcmRlciAoYnkgZGVmYXVsdCBSIHdpbGwgb3JkZXIgdGhlbSBEYXkwLCBEYXkyMSwgRGF5NykuCgpgYGB7ciwgYWx0ZXJfbWV0YWRhdGF9CiMgTWFrZSBtZXRhZGF0YSBtb3JlIGdyYW51bGFyIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIChTbyBpdCdzIGVhc2llciBmb3IgdXMgdG8gdW5kZXJzdGFuZCkgIApvcmlnLmlkZW50X2xldmVscyA9IGFwcGx5KAogICAgZXhwYW5kLmdyaWQoYygnSE8nKSwgYygncmVwbGljYXRlMScsICdyZXBsaWNhdGUyJywgJ3JlcGxpY2F0ZTMnLCAncmVwbGljYXRlNCcpLCBjKCdEYXkwJywnRGF5NycsJ0RheTIxJykpLCAKICAgIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKHJvdyl7cGFzdGUocm93WzFdLCByb3dbM10sIHJvd1syXSwgc2VwID0gJy4nKX0pCgp0bXBfbWV0YSA9IGdlb19zb0BtZXRhLmRhdGEKCnRtcF9tZXRhJG9yaWcuaWRlbnQgPSBnc3ViKCdITycsICdITy4nLCB0bXBfbWV0YSRvcmlnLmlkZW50KQp0bXBfbWV0YSRvcmlnLmlkZW50ID0gZ3N1YigncmVwJywgJy5yZXAnLCB0bXBfbWV0YSRvcmlnLmlkZW50KQoKdG1wX21ldGEkb3JpZy5pZGVudCA9IGZhY3Rvcih0bXBfbWV0YSRvcmlnLmlkZW50LCBsZXZlbHMgPSBvcmlnLmlkZW50X2xldmVscykKCiMgQWRkIGRheSBpbmZvcm1hdGlvbgp0bXBfbWV0YSRkYXkgPSBmYWN0b3Ioc3RyX3NwbGl0KHRtcF9tZXRhJG9yaWcuaWRlbnQsIHBhdHRlcm4gPSAnWy5dJywgc2ltcGxpZnkgPSBUUlVFKVssMl0sIGxldmVscyA9IGMoJ0RheTAnLCAnRGF5NycsICdEYXkyMScpKQoKIyBBZGQgcmVwbGljYXRlIGNvbHVtbgp0bXBfbWV0YSRyZXBsaWNhdGUgPSBzdHJfc3BsaXQodG1wX21ldGEkb3JpZy5pZGVudCwgcGF0dGVybiA9ICdbLl0nLCBzaW1wbGlmeSA9IFRSVUUpWywzXQoKIyBBc3NpZ24gdGhlIHVwZGF0ZWQgbWV0YWRhdGEgdG8gdGhhdCBpbiB0aGUgU2V1cmF0IG9iamVjdApnZW9fc29AbWV0YS5kYXRhID0gdG1wX21ldGEKCiMgU2V0IHRoZSBJZGVudHMoZ2VvX3NvKSB0byB0aGUgbmV3IG9yaWcuaWRlbnQKSWRlbnRzKGdlb19zbykgPSAnb3JpZy5pZGVudCcKYGBgCgpUYWtpbmcgYSBsb29rIGF0IHRoZSByZXN1bHQsIHdlIHNlZSBgb3JpZy5pZGVudGAgbG9va3MgYSBsaXR0bGUgZGlmZmVyZW50LCBhbmQgd2UgaGF2ZSBuZXcgYGRheWAgYW5kIGByZXBsaWNhdGVgIGNvbHVtbnMuIE5vdGUgdGhhdCB0aGVyZSBhcmUgbWFueSB3YXlzIHRvIGhhdmUgYWNjb21wbGlzaGVkIHRoaXMsIHRoZSBhYm92ZSBjb2RlIGlzIGp1c3Qgb25lIHJvdXRlLgoKYGBge3IsIHByZXZpZXdfbWV0YWRhdGEzfQojIFJlLWV4YW1pbmUgU2V1cmF0IG1ldGRhdGEgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KaGVhZChnZW9fc29AbWV0YS5kYXRhKQpgYGAKCiMjIFBlcmNlbnQgbWl0b2Nob25kcmlhbCByZWFkcwoKVGhlIGBQZXJjZW50YWdlRmVhdHVyZVNldCgpYCBmdW5jdGlvbiBlbmFibGVzIHVzIHRvIHF1aWNrbHkgZGV0ZXJtaW5lIHRoZSBjb3VudHMgYmVsb25naW5nIHRvIGEgc3Vic2V0IG9mIHRoZSBwb3NzaWJsZSBmZWF0dXJlcyBmb3IgZWFjaCBjZWxsLiBTaW5jZSBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHRzIGluIG1vdXNlIGJlZ2luIHdpdGggIm10Iiwgd2Ugd2lsbCB1c2UgdGhhdCBwYXR0ZXJuIHRvIGNvdW50IHRoZSBwZXJjZW50YWdlIG9mIHJlYWRzIGNvbWluZyBmcm9tIG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdHMuCgpgYGB7ciwgYXNzaWduX3BlcmNlbnRfbXR9CiMgQ29uc2lkZXIgbWl0b2Nob25kcmlhbCB0cmFuc2NyaXB0cyAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFdlIHVzZSAibXQiIGJlY2F1c2UgdGhpcyBpcyBtb3VzZSwgZGVwZW5kaW5nIG9uIHRoZSBvcmdhbmlzbSwgdGhpcyBtaWdodCBuZWVkIHRvIGJlIGNoYW5nZWQKZ2VvX3NvJHBlcmNlbnQubXQgPSBQZXJjZW50YWdlRmVhdHVyZVNldChnZW9fc28sIHBhdHRlcm4gPSAnXm10LScpCgojIFVzZSBzdW1tYXJ5KCkgdG8gcXVpY2tseSBjaGVjayB0aGUgcmFuZ2Ugb2YgdmFsdWVzCnN1bW1hcnkoZ2VvX3NvJHBlcmNlbnQubXQpCmBgYAoKSnVzdCBsb29raW5nIGF0IHRoZSBzdW1tYXJ5LCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIHNvbWUgY2VsbHMgd2l0aCBhIGhpZ2ggcGVyY2VudGFnZSBvZiBtaXRvY2hvbmRyaWFsIHJlYWRzLgoKIVtJbWFnZTogU2NoZW1hdGljIGFmdGVyIGFsdGVyaW5nIHNvbWUgbWV0YSBkYXRhIGNvbHVtbnMuXShpbWFnZXMvc2V1cmF0X3NjaGVtYXRpYy9TbGlkZTMucG5nKQoKIyBRdWFsaXR5IE1ldHJpY3MKCkNlbGwgUmFuZ2VyIGlzIGEgZmlyc3QtcGFzcyBmaWx0ZXIgdG8gZGV0ZXJtaW5lIHdoYXQgaXMgYSAiY2VsbCIgYW5kIHdoYXQgaXMgbm90LiBJdCBvbmx5IGNvbnNpZGVycyBvbmUgc2FtcGxlIGF0IGEgdGltZSwgYW5kIGRvZXMgbm90IGNvbnNpZGVyIHRoZSBjZWxscyByZWxhdGl2ZSB0byBvbmUgYW5vdGhlci4gCgpMZXQncyBkaWcgZGVlcGVyIHRvIGRldGVybWluZSB3aGVuIGEgZHJvcGxldCBtaWdodCBjb250YWluIHR3byBjZWxscywgYSB2ZXJ5IHN0cmVzc2VkIGNlbGwsIG9yIHNvbWUgdGVjaG5pY2FsIGlzc3VlIGluIHRoZSBsaWJyYXJ5IHByZXBhcmF0aW9uLiBXZSB3aWxsIHVzZSB0aHJlZSBtZXRyaWNzIHRvIGRldGVybWluZSBsb3ctcXVhbGl0eSBjZWxscyBiYXNlZCBvbiB0aGVpciBleHByZXNzaW9uIHByb2ZpbGVzIChbcmVmZXJlbmNlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xMi9PU0NBL3F1YWxpdHktY29udHJvbC5odG1sI2Nob2ljZS1vZi1xYy1tZXRyaWNzKSkuCgoxLiBUaGUgdG90YWwgbnVtYmVyIG9mIFVNSXMgZGV0ZWN0ZWQuIENlbGxzIHdpdGggYSBzbWFsbCBudW1iZXIgb2YgVU1JcyBkZXRlY3RlZCBtYXkgaW5kaWNhdGUgbG9zcyBvZiBSTkEgZHVyaW5nIGxpYnJhcnkgcHJlcGFyYXRpb24gdmlhIGNlbGwgbHlzaXMgb3IgaW5lZmZpY2llbnQgY0ROQSBjYXB0dXJlIC8gYW1wbGlmaWNhdGlvbi4gQ2VsbHMgd2l0aCByZWxhdGl2ZWx5IGhpZ2ggbnVtYmVyIG9mIFVNSXMgZGV0ZWN0ZWQgbWF5IGluZGljYXRlIGEgZG91YmxldC4KMi4gVGhlIG51bWJlciBvZiBleHByZXNzZWQgZmVhdHVyZXMsIGRlZmluZWQgYXMgbnVtYmVyIG9mIGdlbmVzIHdpdGggbm9uLXplcm8gY291bnRzLiBDZWxscyB3aXRoIHZlcnkgZmV3IG1lYXN1cmVkIGdlbmVzIGFyZSBsaWtlbHkgdG8gYmUgb2YgbG93LXF1YWxpdHksIGFuZCBtYXkgZGlzdG9ydCBkb3duc3RyZWFtIHZhcmlhbmNlIGVzdGltYXRpb24gb3IgZGltZW5zaW9uIHJlZHVjdGlvbiBzdGVwcy4KMy4gVGhlIHByb3BvcnRpb24gb2YgcmVhZHMgbWFwcGVkIHRvIHRoZSBtaXRvY2hvbmRyaWFsIGdlbm9tZS4gSGlnaCBwcm9wb3J0aW9ucyBvZiBtaXRvY2hvbmRyaWFsIHRyYW5zY3JpcHRzIG1heSBpbmRpY2F0ZSBhIGRhbWFnZWQgY2VsbCwgdGhlIG1lYXN1cmUgb2Ygd2hpY2ggbWF5IGFsc28gZGlzdG9ydCBkb3duc3RyZWFtIGFuYWx5c2lzIHN0ZXBzLgoKVGhlIG51bWJlciBvZiBVTUlzIGRldGVjdGVkIChgbkNvdW50YCkgYW5kIG51bWJlciBvZiBleHByZXNzZWQgZmVhdHVyZXMgKGBuRmVhdHVyZWApIGFyZSBhbHJlYWR5IGdpdmVuIGluIHRoZSBtZXRhIGRhdGEgdGFibGUuCgo+ICoqV2h5IHRvdGFsIFVNSXMgaW5zdGVhZCBvZiB0b3RhbCByZWFkcz8qKgo+IAo+IFNpbmNlIGEgc2luZ2xlLWNlbGwgaW5oZXJlbnRseSBjb250YWlucyBhIGxpbWl0ZWQgYW1vdW50IG9mIFJOQSBtb2xlY3VsZXMsIGEgaGlnaGVyIGFtb3VudCBvZiBQQ1IgYW1wbGlmaWNhdGlvbiBpcyByZXF1aXJlZCB0byBnZW5lcmF0ZSB0aGUgZmluYWwgc2VxdWVuY2luZyBsaWJyYXJ5Lgo+IAo+IFNpbmNlIFBDUiBjYW4gc2tldyBwcm9wb3J0aW9ucyBvZiBpbml0aWFsIGlucHV0IG1hdGVyaWFscywgc3BlY2lmaWMgc2VxdWVuY2VzIGFyZSBpbmNsdWRlZCBpbiB0aGUgaW5pdGlhbCBjYXB0dXJlIHByb2JlcyBjYWxsZWQgdW5pcXVlIG1vbGVjdWxlIGlkZW50aWZpZXJzIChVTUlzKS4gQXMgZWFjaCBpbml0aWFsIHByb2JlIGhhcyBhIGRpZmZlcmVudCBVTUkgc2VxdWVuY2UsIGVhY2ggUk5BIGNhcHR1cmVkIHdpbGwgYmUgdGFnZ2VkIHdpdGggYSBkaWZmZXJlbnQgVU1JLCB3aGljaCBhbGxvd3MgdGhvc2UgaW5pdGlhbCBSTkFzIGFuZCBzdWJzZXF1ZW50IFBDUiBkdXBsaWNhdGVzIHRvIGJlIGlkZW50aWZpZWQgYW5kIGR1cGxpY2F0ZXMgY29sbGFwc2VkIGFzIHBhcnQgb2YgdGhlIGluaXRpYWwgcHJvY2Vzc2luZyBieSBDZWxsUmFuZ2VyLiAgCj4KCj4gKipPdGhlciBtZWFuaW5ncyBvZiBgbkZlYXR1cmVzYCoqCj4gCj4gRm9yIG90aGVyIHNpbmdsZS1jZWxsIGRhdGEgdHlwZXMsIGBuRmVhdHVyZXNgIHdvdWxkIHJlcHJlc2VudCB3aGF0J3MgYmVpbmcgbWVhc3VyZWQgaW4gdGhhdCBleHBlcmltZW50LiBGb3Igc2luZ2xlLWNlbGwgQVRBQy1zZXEsIGBuRmVhdHVyZXNgIHdvdWxkIHJlcHJlc2VudHMgdGhlIHRvdGFsIG51bWJlciBvZiBwZWFrcyAoZS5nLiBhY2Nlc3NpYmxlIGFyZWFzIG9mIEROQSkgcGVyIGNlbGwuCj4KCjwhLS0gTWF0dCBtYXkgaGF2ZSBkaXNjdXNzZWQgVU1JcyBhcyBwYXJ0IG9mIENlbGxSYW5nZXIgcHJvY2Vzc2luZyBhbmQgTGl2L1RyaWNpYSBtYXkgdG91Y2ggb24gYXMgcGFydCBvZiBsaWJyYXJ5IGdlbmVyYXRpb24gYXQgZW5kIG9mIERheSAyIC0tPgoKIyMgQ2VsbCBjb3VudHMKCkNlbGwgY291bnRzIHBlciBzYW1wbGUgKGJhc2VkIG9uIHRoZSBudW1icmVyIG9mIHVuaXF1ZSBjZWxsdWxhciBiYXJjb2RlcyBkZXRlY3RlZCBieSBDZWxsIFJhbmdlcikgY2FuIGluZGljYXRlIGlmIGFuIGVudGlyZSBzYW1wbGUgd2FzIG9mIHBvb3IgcXVhbGl0eS4gVGhlIHRhYmxlIGJlbG93IGlzIHRoZSBudW1iZXIgb2YgY2VsbHMgY2FsbGVkIGJ5IENlbGwgUmFuZ2VyLCB3aXRoIHRoZSBhZGRlZCBmaWx0ZXIgZnJvbSBgQ3JlYXRlU2V1cmF0ZU9iamVjdCgpYCBpbiB0aGUgcHJldmlvdXMgbGVzc29uOiBgbWluLmNlbGxzID0gMSwgbWluLmZlYXR1cmVzID0gNTBgLiBSZWNhbGwsIHRoaXMgbWVhbnMgYSBnZW5lIGlzIHJlbW92ZWQgaWYgaXQgaXMgZXhwcmVzc2VkIGluIDEgb3IgZmV3ZXIgY2VsbHMsIGFuZCBhIGNlbGwgaXMgcmVtb3ZlZCBpZiBpdCBjb250YWlucyByZWFkcyBmb3IgNTAgb3IgZmV3ZXIgZ2VuZXMuCgpgYGB7ciwgcHJlZmlsdGVyX2NlbGxfY291bnRzfQojIEFzc2lnbiBvdmVyYWxsIGNlbGwgY291bnRzICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KY2VsbF9jb3VudHNfcHJlX3RibCA9IGdlb19zb0BtZXRhLmRhdGEgJT4lIGNvdW50KG9yaWcuaWRlbnQsIG5hbWUgPSAncHJlZmlsdGVyX2NlbGxzJykKY2VsbF9jb3VudHNfcHJlX3RibApgYGAKCkl0IGFwcGVhcnMgdGhhdCB0aGUgRGF5IDcgc2FtcGxlcyBoYXZlIHN5c3RlbWF0aWNhbGx5IG1vcmUgY2VsbHMgdGhhbiB0aGUgb3RoZXIgZGF5cy4gSWYgeW91IGhhZCBhbiBpZGVhIG9mIGhvdyBtYW55IGNlbGxzIHlvdSBleHBlY3RlZCB0byBzZWUgcGVyIHNhbXBsZSwgdGhpcyB0YWJsZSBjYW4gaGVscCB5b3UgY2hlY2sgdGhhdCBleHBlY3RhdGlvbiBhbmQgZGV0ZXJtaW5lIGlmIGEgc2FtcGxlIGZhaWxlZCBhbmQgc2hvdWxkIGJlIGRyb3BwZWQuCgojIyBWaXN1YWxpemluZyBxdWFsaXR5IG1ldHJpY3MKCkEgdmlvbGluIHBsb3Qgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIHF1YW50aXR5IGFtb25nIHRoZSBjZWxscyBpbiBhIHNpbmdsZSBzYW1wbGUsIG9yIGFjcm9zcyBtYW55IHNhbXBsZXMuIFNldXJhdCBoYXMgYSBidWlsdC1pbiBmdW5jdGlvbiwgYFZsblBsb3QoKWAgZm9yIHRoaXMgcHVycG9zZS4gTGV0J3Mgb3JpZW50IHdpdGggYSB2aW9saW4gcGxvdCBvZiBgbkZlYXR1cmVfUk5BYCBmb3Igb25seSBvbmUgc2FtcGxlLCBgSE8uRGF5MjEucmVwbGljYXRlMWA6CgpgYGB7ciwgc2luZ2xlX3Zpb2xpbl9wbG90LCBlY2hvID0gRkFMU0V9ClZsblBsb3Qoc3Vic2V0KGdlb19zbywgb3JpZy5pZGVudCA9PSAnSE8uRGF5MjEucmVwbGljYXRlMScpLCBmZWF0dXJlcyA9ICduRmVhdHVyZV9STkEnLCBhc3NheSA9ICdSTkEnLCBsYXllciA9ICdjb3VudHMnKSArIE5vTGVnZW5kKCkKYGBgCgpBIHZpb2xpbiBwbG90IGlzIHNpbWlsYXIgdG8gYSBib3ggcGxvdCwgYnV0IGl0IHNob3dzIHRoZSBkZW5zaXR5IG9mIHRoZSBkYXRhIGF0IGRpZmZlcmVudCB2YWx1ZXMuIEhlcmUgdGhlIGluZGl2aWR1YWwgcG9pbnRzIGFyZSB0aGUgY2VsbHMgZnJvbSBgSE8uRGF5MjEucmVwbGljYXRlMWAgYW5kIHRoZSB5LWF4aXMgaXMgdGhlIHZhbHVlIG9mIGBuRmVhdHVyZV9STkFgIGZvciB0aGF0IGNlbGwuIFRoZSB2aW9saW4gcGFydCBvZiB0aGUgZnVuY3Rpb24gaXMgZXNzZW50aWFsbHkgc2hvd2luZyB0aGUgZGVuc2l0eSBvZiB0aGUgY2VsbHMgYXQgZGlmZmVyZW50IHZhbHVlcyBvZiBgbkZlYXR1cmVfUk5BYC4KCiMjIyBHZW5lcyBwZXIgY2VsbAoKTGV0J3MgbG9vayBhdCB0aGUgYG5GZWF0dXJlX1JOQWAgdmlvbGluIHBsb3QgYWNyb3NzIGFsbCB0aGUgc2FtcGxlcy4gQWdhaW4sIHRoaXMgaXMgdGhlIG51bWJlciBvZiBnZW5lcyBkZXRlY3RlZCBwZXIgY2VsbC4KCmBgYHtyLCBmZWF0dXJlX3Bsb3QsIGZpZy5zaG93ID0gJ2hvbGQnfQojIFJldmlldyBmZWF0dXJlIHZpb2xpbiBwbG90cyAgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KVmxuUGxvdChnZW9fc28sIGZlYXR1cmVzID0gJ25GZWF0dXJlX1JOQScsIGFzc2F5ID0gJ1JOQScsIGxheWVyID0gJ2NvdW50cycpICsgTm9MZWdlbmQoKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUwMCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA0MDApICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMzAwKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDIwMCkKZ2dzYXZlKGZpbGVuYW1lID0gJ3Jlc3VsdHMvZmlndXJlcy9xY19uRmVhdHVyZV92aW9saW4ucG5nJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAnaW4nKQpgYGAKCldlIG9ic2VydmUgdGhhdCBzYW1wbGVzIGJlaGF2ZSBzaW1pbGFybHkgd2l0aGluIGEgZGF5LCBidXQgaGF2ZSBkaWZmZXJlbnQgZGlzdHJpYnV0aW9ucyBhY3Jvc3MgZGF5cy4gV2UgYWxzbyBub3RlIHRoYXQgbWFueSBjZWxscyBhcHBlYXIgdG8gaGF2ZSBhIGxvdyBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQgKDwgNTAwKS4KCiMjIyBVTUkgY291bnRzIHBlciBjZWxsCgpOb3cgbGV0J3MgbG9vayBhdCBgbkNvdW50X1JOQWAgcGxvdCwgdGhlIHRvdGFsIG51bWJlciBvZiBVTUlzIGRldGVjdGVkLgoKYGBge3IsIGNvdW50X3Bsb3QsIGZpZy5zaG93ID0gJ2hvbGQnfQojIFJldmlldyBjb3VudCB2aW9saW4gcGxvdHMgICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KVmxuUGxvdChnZW9fc28sIGZlYXR1cmVzID0gJ25Db3VudF9STkEnLCBhc3NheSA9ICdSTkEnLCBsYXllciA9ICdjb3VudHMnKSArIE5vTGVnZW5kKCkKZ2dzYXZlKGZpbGVuYW1lID0gJ3Jlc3VsdHMvZmlndXJlcy9xY19uQ291bnRfdmlvbGluLnBuZycsIHdpZHRoID0gMTIsIGhlaWdodCA9IDYsIHVuaXRzID0gJ2luJykKYGBgCgpXZSBvYnNlcnZlIGEgc2ltaWxhciB3aXRoaW4tYW5kLWFjcm9zcyBkYXkgYmVoYXZpb3IgYW1vbmcgdGhlIHNhbXBsZXMuIE92ZXJhbGwgaXQgYXBwZWFycyB0aGF0IHRoZSBEYXkgMCBzYW1wbGVzIGhhdmUgZmV3ZXIgVU1JcyBkZXRlY3RlZCBwZXIgY2VsbCB0aGFuIERheSA3IGFuZCBEYXkgMjEuIEFsc28gb2Ygbm90ZSBpcyB0aGUgb3V0bGllciBjZWxsIGluIEhPLkRheTAucmVwbGljYXRlMSwgd2l0aCBhcm91bmQgMTAwSyBVTUlzIGRldGVjdGVkLiBUaGlzIGNlbGwgbWlnaHQgYmUgYSBkb3VibGV0LgoKIyMjIFBlcmNlbnQgbWl0b2Nob25kcmlhbCByZWFkcwoKRmluYWxseSwgbGV0cyBwbG90IHRoZSBwZXJjZW50IG1pdG9jaG9uZHJpYWwgcmVhZHMsIGBwZXJjZW50Lm10YC4KCmBgYHtyLCBtaXRvX3Bsb3QsIGZpZy5zaG93ID0gJ2hvbGQnfQojIFJldmlldyBtaXRvY2hvbmRyaWFsIHZpb2xpbiBwbG90cyAgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KVmxuUGxvdChnZW9fc28sIGZlYXR1cmVzID0gJ3BlcmNlbnQubXQnLCBhc3NheSA9ICdSTkEnLCBsYXllciA9ICdjb3VudHMnKSArIE5vTGVnZW5kKCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAyNSkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAyMCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxNSkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxMCkKZ2dzYXZlKGZpbGVuYW1lID0gJ3Jlc3VsdHMvZmlndXJlcy9xY19taXRvX3Zpb2xpbi5wbmcnLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA2LCB1bml0cyA9ICdpbicpCmBgYAoKV2Ugb2JzZXJ2ZSB0aGUgbWFqb3JpdHkgb2YgY2VsbHMgc2VlbSB0byBoYXZlIDwgMjUlIG1pdG9jaG9uZHJpYWwgcmVhZHMsIGJ1dCB0aGVyZSBhcmUgbWFueSBjZWxscyB3aXRoID4gMjUlIHRoYXQgd2UgbWF5IHdhbnQgdG8gcmVtb3ZlLgoKR2VuZXJhbGx5LCBtYW55IHR1dG9yaWFscyB1c2UgYSBjdXRvZmYgb2YgNSAtIDEwJSBtaXRvY2hvbmRyaWFsIHJlYWRzLiBIb3dldmVyLCBmb3Igc29tZSBleHBlcmltZW50cywgaGlnaCBtaXRvY2hvbmRyaWFsIHJlYWRzIHdvdWxkIGJlIGV4cGVjdGVkIChzdWNoIGFzIGluIGNhc2VzIHdoZXJlIHRoZSBjb25kaXRpb24vdHJlYXRtZW50IG9yIGdlbm90eXBlIGluY3JlYXNlcyBjZWxsIGRlYXRoKS4gSW4gdGhpcyBjYXNlLCBhIHJlbGF4ZWQgdGhyZXNob2xkIHdvdWxkIGhlbHAgcHJlc2VydmUgYmlvbG9naWNhbGx5IHJlbGV2YW50IGNlbGxzLiBJbiBvdXIgY2FzZSwgdGhlIGNlbGxzIHdlcmUgY29sbGVjdGVkIGFzIHBhcnQgb2YgYW4gaW5qdXJ5IG1vZGVsLCBwZXJoYXBzIGp1c3RpZnlpbmcgYSBtb3JlIGxlbmllbnQgcGVyY2VudCBtaXRvY2hvbmRyaWFsIHJlYWQgZmlsdGVyLgoKIyBGaWx0ZXJpbmcgYXBwcm9hY2hlcwoKIyMgRml4ZWQgdGhyZXNob2xkcwoKRml4ZWQgdGhyZXNob2xkcyBmb3IgYW55IGNvbWJpbmF0aW9uIG9mIGBuRmVhdHVyZV9STkFgLCBgbkNvdW50X1JOQWAsIGFuZCBgcGVyY2VudC5tdGAgY2FuIGJlIHNlbGVjdGVkIHRvIHJlbW92ZSBsb3cgcXVhbGl0eSBjZWxscy4gSW4gZ2VuZXJhbCwgc2VsZWN0aW5nIHZlcnkgc3RyaW5nZW50IHRocmVzaG9sZHMgZm9yIGVhY2ggb2YgdGhlIG1ldHJpY3MgbWF5IGxlYWQgdG8gcmVtb3ZpbmcgaW5mb3JtYXRpdmUgY2VsbHMuIEFzIHBlciB0aGUgSEJDIHRyYWluaW5nIG1hdGVyaWFscyAoW2xpbmtdKGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL3NjUk5BLXNlcV9vbmxpbmUvbGVzc29ucy8wNF9TQ19xdWFsaXR5X2NvbnRyb2wuaHRtbCkpLCB3ZSByZWNvbW1lbmQgc2V0dGluZyBpbmRpdmlkdWFsIHRocmVzaG9sZHMgdG8gZXJyIG9uIHRoZSBwZXJtaXNzaXZlIHNpZGUuCgpbU29ya2luIGV0IGFsLl0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNzAwMjQ1My8pIGNob3NlIHRoZXNlIHRocmVzaG9sZHM6Cgo+IFdlIGZpbHRlcmVkIG91dCBjZWxscyB3aXRoIGxlc3MgdGhhbiA1MDAgZ2VuZXMgcGVyIGNlbGwgYW5kIHdpdGggbW9yZSB0aGFuIDI1JSBtaXRvY2hvbmRyaWFsIHJlYWQgY29udGVudC4KCkluIG90aGVyIHdvcmRzLCBjZWxscyB3aXRoID4gNTAwIGBuRmVhdHVyZV9STkFgIGFuZCA8IDI1JSBgcGVyY2VudC5tdGAgd2VyZSByZXRhaW5lZCBieSB0aGUgYXV0aG9ycy4gV2UgY291bGQgcHJldmlldyB3aGF0IHRoZSByZXN1bHRpbmcgY2VsbCBjb3VudHMgd291bGQgYmUgd2l0aCB0aGVzZSB0aHJlc2hvbGRzOgoKYGBge3IsIHByZXZpZXdfcG9zdGZpbHRlcl9jZWxsX2NvdW50c30KIyBDb25zaWRlciBleGNsdWRpbmcgc3VzcGVjdCBjZWxscyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnN1YnNldChnZW9fc28sIHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDUwMCAmIHBlcmNlbnQubXQgPCAyNSlAbWV0YS5kYXRhICU+JSAKICAgIGNvdW50KG9yaWcuaWRlbnQsIG5hbWUgPSAncG9zdGZpbHRlcl9jZWxscycpCmBgYAoKIyMgQWRhcHRpdmUgdGhyZXNob2xkcwoKRm9yIHRoaXMgd29ya3Nob3Agd2Ugd2lsbCB1c2UgZml4ZWQgdGhyZXNob2xkcywgYnV0IGFub3RoZXIgb3B0aW9uIGlzIHRvIHJlbW92ZSBsb3ctcXVhbGl0eSBjZWxscyBhZGFwdGl2ZWx5LiBUaGlzIGFwcHJvYWNoIGFzc3VtZXMgdGhhdCBtb3N0IG9mIHRoZSBjZWxscyBhcmUgb2YgYWNjZXB0YWJsZSBxdWFsaXR5LiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBzZWUgdGhlIHJlbGV2YW50IHNlY3Rpb24gaW4gQmlvY29uZHVjdG9yJ3Mgb25saW5lIGJvb2s6IE9yY2hlc3RyYXRpbmcgU2luZ2xlLUNlbGwgQW5hbHlzaXMgKFtsaW5rXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xMi9PU0NBL3F1YWxpdHktY29udHJvbC5odG1sI3F1YWxpdHktY29udHJvbC1vdXRsaWVyKSkuCgojIFJlbW92aW5nIGxvdy1xdWFsaXR5IGNlbGxzCgpXZSB3aWxsIGRldmlhdGUgZnJvbSB0aGUgcHVibGljYXRpb24gc2xpZ2h0bHkgYW5kIHJldGFpbiB0aGUgY2VsbHMgd2l0aCBgbkZlYXR1cmVfUk5BID4gMzAwYCBhbmQgYHBlcmNlbnQubXQgPCAxNWAuIE5vdGluZyB0aGF0IHRoZSBgbkZlYXR1cmVfUk5BYCBwbG90IGhhZCBhIGdhcCBhcm91bmQgMzAwLCBhbmQgdGhhdCB3ZSB3YW50IHRvIGJlIG1vcmUgbGVuaWVudCB0aGFuIHRoZSB1c3VhbCA1LTEwJSBjdXRvZmYgZm9yIG1pdG9jaG9uZHJpYWwgcmVhZHMuCgpgYGB7ciwgZmlsdGVyX3NldXJhdH0KIyBGaWx0ZXIgdG8gZXhjbHVkZSBzdXNwZWN0IGNlbGxzIGFuZCBhc3NpZ24gdG8gbmV3IFNldXJhdCBvYmplY3QgLS0tLS0tCmdlb19zbyA9IHN1YnNldChnZW9fc28sIHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDMwMCAmIHBlcmNlbnQubXQgPCAxNSkKZ2VvX3NvCmBgYAoKQW5kIGxldCdzIHJlY29yZCB0aGUgbnVtYmVyIG9mIGNlbGxzIHJlbWFpbmluZyBhZnRlciBmaWx0ZXJpbmc6CgpgYGB7ciwgcG9zdGZpbHRlcl9jZWxsX2NvdW50c30KIyBFeGFtaW5lIHJlbWFpbmluZyBjZWxsIGNvdW50cyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmNlbGxfY291bnRzX3Bvc3RfdGJsID0gZ2VvX3NvQG1ldGEuZGF0YSAlPiUgY291bnQob3JpZy5pZGVudCwgbmFtZSA9ICdwb3N0ZmlsdGVyX2NlbGxzJykKY2VsbF9jb3VudHNfcG9zdF90YmwKYGBgCgpMZXQncyBjb21iaW5lIHRoZSBwcmUgYW5kIHBvc3QgdGFibGVzOgoKYGBge3IsIGNlbGxfY291bnRzfQojIFNob3cgY2VsbCBjb3VudHMgYmVmb3JlIGFuZCBhZnRlciBmaWx0ZXJpbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KY2VsbF9jb3VudHNfdGJsID0gY2VsbF9jb3VudHNfcHJlX3RibCAlPiUgbGVmdF9qb2luKGNlbGxfY291bnRzX3Bvc3RfdGJsLCBieSA9ICdvcmlnLmlkZW50JykKY2VsbF9jb3VudHNfdGJsCmBgYAoKV2UgY2FuIG5vdyBlYXNpbHkgc2VlIGhvdyBtYW55IGNlbGxzIHdlIHN0YXJ0ZWQgd2l0aCwgYW5kIGhvdyBtYW55IHdlIHJldGFpbmVkIGZvciBkb3duc3RyZWFtIGFuYWx5c2lzLiBMZXQncyB3cml0ZSB0aGlzIHRhYmxlIHRvIGEgZmlsZS4KCmBgYHtyLCB3cml0ZV9jZWxsX2NvdW50c30Kd3JpdGVfY3N2KGNlbGxfY291bnRzX3RibCwgZmlsZSA9ICdyZXN1bHRzL3RhYmxlcy9jZWxsX2ZpbHRlcmluZ19jb3VudHMuY3N2JykKYGBgCgo8IS0tV2hhdCB0aHJlc2hvbGRzIHdvdWxkIHdlIHN0YXJ0IHdpdGggYmFzZWQgb24gdGhlIHBsb3RzIGFsb25lPyBIb3cgZG9lcyB0aGF0IGNvbXBhcmUgdG8gdGhlIHRocmVzaG9sZHMgcmVwb3J0ZWQgaW4gcGFwZXI/LS0+Cgo8IS0tT3RoZXIg4oCcYWR2YW5jZWTigJ0gbWV0aG9kczogb3V0IG9mIHNjb3BlIGZvciB0aGlzIHdvcmtzaG9wIGJ1dCB0aGVyZSBhcmUgcGFja2FnZXMgIHNwZWNpZmljYWxseSBkZXZlbG9wZWQgdG8gZGV0ZWN0ICJkb3VibGV0cyIsIGUuZy4gZHJvcGxldHMgdGhhdCBjb250YWluZWQgbW9yZSB0aGFuIG9uZSBjZWxsLCBzdWNoIGFzIERvdWJsZUZpbmRlci0tPgoKPCEtLUFkZGl0aW9uYWwgYXNpZGUgLSBGb3Igc2luZ2xlLW51Y2xlaSBleHBlcmltZW50cyByZW1vdmluZyBiYWNrZ3JvdW5kL2FtYmllbnQgUk5BIHdpdGggQ2VsbEJsZW5kZXIgT1IgRGVjb250WCBpcyBhbiBpbXBvcnRhbnQgYWRkaXRpb25hbCBzdGVwIHNpbmNlIG51Y2xlaSBhcmUgYm90aCBzdGlja3kgYW5kIHBvcm91cy0tPgoKIyBSZXZpc2luZyB0aHJlc2hvbGRzCgpJdCBpcyBpbXBvcnRhbnQgdG8gcmVjb2duaXplIHRoYXQgdGhyZXNob2xkcyBmb3IgcmV0YWluaW5nIGNlbGxzIGFyZSBub3Qgc2V0IGluIHN0b25lLiBJdCBtYXkgaGFwcGVuIHRoYXQgdGhlIGRvd25zdHJlYW0gYW5hbHlzaXMgc3VnZ2VzdHMgdGhhdCB0aHJlc2hvbGRzIHNob3VsZCBiZSBtb3JlIG9yIGxlc3Mgc3RyaW5nZW50LiBJbiB3aGljaCBjYXNlLCB5b3UgbWF5IGV4cGVyaW1lbnQgd2l0aCB0aGUgdGhyZXNob2xkcyBhbmQgb2JzZXJ2ZSB0aGUgZG93bnN0cmVhbSBlZmZlY3RzLgoKIyBTYXZlIG91ciBwcm9ncmVzcwoKTGV0J3Mgc2F2ZSB0aGlzIGZpbHRlcmVkIGZvcm0gb2Ygb3VyIFNldXJhdCBvYmplY3QuIEl0IHdpbGwgYWxzbyBpbmNsdWRlIG91ciBjaGFuZ2VzIHRvIHRoZSBgbWV0YS5kYXRhYDoKCmBgYHtyLCBzYXZlX3Jkc19oaWRkZW4sIGVjaG8gPSBGQUxTRX0KaWYoIWZpbGUuZXhpc3RzKCdyZXN1bHRzL3JkYXRhL2dlb19zb19maWx0ZXJlZC5yZHMnKSkgewogIHNhdmVSRFMoZ2VvX3NvLCBmaWxlID0gJ3Jlc3VsdHMvcmRhdGEvZ2VvX3NvX2ZpbHRlcmVkLnJkcycpCn0KYGBgCgpgYGB7ciwgc2F2ZV9yZHMsIGV2YWwgPSBGQUxTRX0KIyBTYXZlIHRoZSBjdXJyZW50IFNldXJhdCBvYmplY3QgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnNhdmVSRFMoZ2VvX3NvLCBmaWxlID0gJ3Jlc3VsdHMvcmRhdGEvZ2VvX3NvX2ZpbHRlcmVkLnJkcycpCmBgYAoKIyBTdW1tYXJ5CgpJbiB0aGlzIHNlY3Rpb24gd2U6CgotIERpc2N1c3NlZCB0aGUgYmlnIHRocmVlIHF1YWxpdHkgbWV0cmljczogYG5GZWF0dXJlYHMsIGBuQ291bnRgcywgYW5kIGBwZXJjZW50Lm10YC4KLSBWaXN1YWxpemVkIHRoZXNlIG1ldHJpY3MgYWNyb3NzIGNlbGxzIC8gc2FtcGxlcyB0byBoZWxwIGlkZW50aWZ5IGxvdy1xdWFsaXR5IGNlbGxzLgotIEZpbHRlcmVkIGxvdy1xdWFsaXR5IGNlbGxzIHVzaW5nIGZpeGVkIHRocmVzaG9sZHMuCgpOZXh0IHN0ZXBzOiBOb3JtYWxpemF0aW9uCgotLS0tCgpUaGVzZSBtYXRlcmlhbHMgaGF2ZSBiZWVuIGFkYXB0ZWQgYW5kIGV4dGVuZGVkIGZyb20gbWF0ZXJpYWxzIGxpc3RlZCBhYm92ZS4gVGhlc2UgYXJlIG9wZW4gYWNjZXNzIG1hdGVyaWFscyBkaXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIGxpY2Vuc2UgKENDIEJZIDQuMCldKGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8pLCB3aGljaCBwZXJtaXRzIHVucmVzdHJpY3RlZCB1c2UsIGRpc3RyaWJ1dGlvbiwgYW5kIHJlcHJvZHVjdGlvbiBpbiBhbnkgbWVkaXVtLCBwcm92aWRlZCB0aGUgb3JpZ2luYWwgYXV0aG9yIGFuZCBzb3VyY2UgYXJlIGNyZWRpdGVkLgoKPGJyLz4KPGJyLz4KCi0tLS0tLS0KCnwgW1ByZXZpb3VzIGxlc3Nvbl0oMDBCLUNlbGxSYW5nZXJJbkFjdGlvbi5odG1sKSB8IFtUb3Agb2YgdGhpcyBsZXNzb25dKCN0b3ApIHwgW05leHQgbGVzc29uXSgwMy1Ob3JtYWxpemF0aW9uLmh0bWwpIHwKfCA6LS0tIHwgOi0tLS06IHwgLS0tOiB8Cg==