Workflow Overview


wayfinder

Introduction

To interpret our initial clustering results for all cells in our experiment, we’ll need to characterize the gene expression driving separation between the clusters.

Expression profiles (A) for for each cell cluster are evaluated against all other clusters (B) to identify marker genes, i.e. genes useful for segregating the cells into clusters (C).


In this section, we will demonstrate: - How to generate “analytical” markers based on gene expression in each cluster - How to visualize the expression of genes of interest to aid in cluster identification and labeling

This process can be a highly variable, from seeing well-characterized marker genes as top markers to needing to dig through the literature and plot the expression for multiple sets of genes of interest. Like the previous sections (and like other areas of research), when this process is more involved or iterative, often only the final results are reported.

Objectives

  • Determine gene markers for each of the clusters using FindAllMarkers()
  • Visualize expression across clusters for genes of interest using DotPlot()

Cluster markers and characterization

After generating clusters, we need to perform differential expression analysis to identify the genes that distinguish those clusters (source). This should allow us to get visibility on some key questions for our clusters, as highlighted by the HBC materials, namely:

  • Are there are biologically meaningful gene expression differences between the clusters?
  • Does the gene expression of the generated clusters correspond to expected cell-types or sub-types?
  • Are there clusters with similar expression that should be combined and/or clusters that might need to be sub-clustered into smaller populations?

Many of the differential expression (DE) tools designed for bulk RNA-seq samples have been benchmarked for performance on scRNA-seq in Soneson and Robinson (2018) and there are also dedicated DE tools for scRNA-seq, like MAST, that use models that account for the expected sparse structure of scRNA-seq data. However in our and others’ experience, the default Wilcoxon test is often sufficient for simple pairwise DE comparisons, while edgeR (Robinson, McCarthy, and Smyth 2010) is recommended by the Ouyang Lab for more complex comparison designs, such as those that include covariates.

Image: Comparison of the performance of DE methods applied to scRNA-seq datasets. Methods are ranked by their average performance across all the listed criteria. Image taken from Soneson and Robinson (2018).
Image: Comparison of the performance of DE methods applied to scRNA-seq datasets. Methods are ranked by their average performance across all the listed criteria. Image taken from Soneson and Robinson (2018).
Additional considerations for differential expression The Ouyang Lab has a section of their tutorial that discusses the methods available for differential expression including some highlighted in the figure below, as well as a more extensive section on threshold considerations, while the HBC section on marker genes identification highlights the different types of marker identification options available via Seurat.


Marker identification

First, we’ll change identities of the cells to “integrated.sct.rpca.clusters” explicitly with SetIdent(). The “seurat_clusters” column is the default column for cell identities and changes each time a new clustering is performed. Then, we’ll ensure that the correct resolution is selected from our Seurat object and then we’ll use the PrepSCTFindMarkers() function in preparation for DE comaprisons to “reverse the individual SCT regression model using minimum of median UMI as the sequencing depth covariate” according to the Seurat documentation. Remember that we’ve performed integration and clustering to assign the cells to clusters regardless of their experimental condition but that we now want to ensure that the data is normalized but not with the SCTransformation scaling needed for the previous steps.

Then we’ll run theFindAllMarkers() function to generate comparisons between each cluster and all other cells, regardless of the experimental group. Note - the statistical test to perform can be specified in FindAllMarkers(), but the default is a Wilcoxon test.

##### Day 3 - Marker identification and visualization 

# Find empirical markers  -------------------------------------------------
# Prep for cluster comparisons
geo_so = SetIdent(geo_so, value = 'integrated.sct.rpca.clusters')
geo_so = PrepSCTFindMarkers(geo_so)

# Run comparisons for each cluster to generate markers
geo_markers = FindAllMarkers(geo_so, only.pos = TRUE, min.pct = 0.05)

# Take a look at the first few rows of the result
head(geo_markers)
        p_val avg_log2FC pct.1 pct.2 p_val_adj cluster    gene
Col12a1     0   4.223843 0.915 0.132         0       0 Col12a1
Thbs2       0   3.851482 0.896 0.151         0       0   Thbs2
Lox         0   3.026665 0.904 0.163         0       0     Lox
Tnc         0   3.936619 0.823 0.098         0       0     Tnc
Col11a1     0   4.996025 0.753 0.062         0       0 Col11a1
Aebp1       0   2.294299 0.972 0.295         0       0   Aebp1
# Write out full cluster marker results to file
write_csv(geo_markers, file = 'results/tables/marker_genes_0.4res.csv')
Image: Schematic after SetIdent().
Image: Schematic after SetIdent().
Seurat v5 improvements For marker generation, Seurat v5 uses the presto package to reduce the time required to run DE comparisons, particularly for large datasets. For users who are not using presto, Seurat recommends increasing the min.pct and logfc.threshold parameterscto increase the speed of DE testing (source).


Note that over-interpretation of these results should be avoided, since each cell is used as a replicate in these comparisons which can lead to inflated (e.g. very low) p-values, the top markers are more likely to be trustworthy (source).

Therefore, it’s useful to filter the results to highlight the top positive markers (since a positive fold-change would mean that gene is more highly expressed in the cluster compared to all other cells), before looking at our results

# Identify marker genes for each cluster ----------------------------------
# Create table of top 5 markers per cluster (using default ranking)
top_5 = geo_markers %>% filter(p_val_adj < 0.01) %>% group_by(cluster) %>% slice_head(n = 5)

# Look at results
head(top_5, n = 10)
# A tibble: 10 × 7
# Groups:   cluster [2]
   p_val avg_log2FC pct.1 pct.2 p_val_adj cluster gene    
   <dbl>      <dbl> <dbl> <dbl>     <dbl> <fct>   <chr>   
 1     0       4.22 0.915 0.132         0 0       Col12a1 
 2     0       3.85 0.896 0.151         0 0       Thbs2   
 3     0       3.03 0.904 0.163         0 0       Lox     
 4     0       3.94 0.823 0.098         0 0       Tnc     
 5     0       5.00 0.753 0.062         0 0       Col11a1 
 6     0       4.28 0.883 0.108         0 1       Clec3b  
 7     0       2.83 0.953 0.29          0 1       Serping1
 8     0       3.38 0.903 0.242         0 1       Igfbp6  
 9     0       4.07 0.725 0.07          0 1       Tnxb    
10     0       2.91 0.764 0.147         0 1       Rarres2 
# Optional - Create table of top 5 markers per cluster (ranked by logFC)
top_5_by_log2FC = geo_markers %>% group_by(cluster) %>% arrange(p_val_adj, desc(avg_log2FC)) %>% slice_head(n = 5)

# Look at results after ranking
head(top_5_by_log2FC, n = 10) # notice difference in pct.1 column between tables
# A tibble: 10 × 7
# Groups:   cluster [2]
   p_val avg_log2FC pct.1 pct.2 p_val_adj cluster gene 
   <dbl>      <dbl> <dbl> <dbl>     <dbl> <fct>   <chr>
 1     0       6.32 0.127 0.004         0 0       Cilp2
 2     0       5.84 0.418 0.013         0 0       Scx  
 3     0       5.68 0.133 0.005         0 0       Kera 
 4     0       5.66 0.083 0.003         0 0       Ucma 
 5     0       5.66 0.08  0.003         0 0       Matn3
 6     0       5.22 0.347 0.018         0 1       Myoc 
 7     0       5.18 0.153 0.006         0 1       Inmt 
 8     0       5.15 0.213 0.008         0 1       C7   
 9     0       4.97 0.096 0.006         0 1       Dact2
10     0       4.86 0.162 0.006         0 1       Lvrn 

We expect to see several columns:

  • gene: gene symbol
  • p_val: p-value not adjusted for multiple test correction
  • avg_logFC: average log fold change. Positive values indicate that the gene is more highly expressed in the cluster.
  • pct.1: percentage of cells where the gene is detected in the cluster
  • pct.2: percentage of cells where the gene is detected on average across all other clusters
  • p_val_adj: adjusted p-value based on bonferroni correction using all genes in the dataset, used to determine significance
  • cluster: cluster represented by pct.1 and for which the statistics in the row are reported

When looking at the output, it is important to prioritize marker genes with both larger fold-change differences and larger difference between pct.1 and pct.2, particularly if pct.1 is high (e.g. if 80% of cells in the cluster evaluated express the gene that more reliable than if only 20% of cells express that gene) (source).

Marker visualization

Now that we have generated a set of marker genes for our clusters, it is useful to visualize the expression of those markers to aid in evaluating them. While the expression of individual genes per cell can be overlaid on our UMAPs (as with the FeaturePlot() function), it’s often more useful to visualize the expression of multiple genes simultaneously. While there are multiple methods supported by Seurat for visualizing marker gene expression, a heatmap or a related plot called a dotplot are commonly used.

We’ll use the DotPlot() function with the SCT values to visualize the top 5 marker genes per cluster:

# Visualize top marker genes as dot plot ----------------------------------
top_5_sct_dot_plot = DotPlot(geo_so, features = unique(top_5$gene)) + 
    theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5)) +
    labs(title = 'Top 5 Cluster Genes by FDR and avg_log2FC') + coord_flip()
top_5_sct_dot_plot

ggsave(filename = 'results/figures/markers_top_5_sct_dot_plot.png', plot = top_5_sct_dot_plot, width = 8, height = 18, units = 'in') 

In the dotplot we can see that the color indicates the expression of the gene while the size of the dot indicates the proportion of cells expressing that gene in each cluster (source).

Using raw RNA values in Dotplots

In addition to plotting the SCT values, the raw or normalized RNA values can be plotted as well:

# Add RNA values to dot plot  ---------------------------------------------
top_5_rna_dot_plot = DotPlot(geo_so, features = unique(top_5_by_log2FC$gene), assay = 'RNA') + 
    theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5)) +
    labs(title = 'Top 5 Cluster Genes by FDR and avg_log2FC') + coord_flip()

ggsave(filename = 'results/figures/markers_top_5_rna_dot_plot.png', plot = top_5_rna_dot_plot, width = 8, height = 18, units = 'in') 



Before proceeding with cluster annotations, we’ll also check the percentage of mitochondrial genes to determine if there are any clusters (or sub populations) that might correspond to interesting cell death patterns (or might indicate further filtering is needed):

# Check mitochondrial gene expression -------------------------------------
percent_mito_plot = FeaturePlot(geo_so, features='percent.mt')
percent_mito_plot

# save to file
ggsave(filename = 'results/figures/percent_umap_mito_plot.png', plot = percent_mito_plot, width = 6, height = 6, units = 'in')

We see that a higher % seem to be somewhat concentrated in a few places, but if cell death might be of interest for the research question, we’d want to consider investigating this pattern further by splitting up the plots by day and/or waiting until after running initial differential expression analysis to determine if these cell populations are interesting biology or not.

Save our progress

Finally, we’ll create an output file for our updated Seurat object and for the cluster marker results:

# Save Seurat object and gene marker data ---------------------------------
saveRDS(geo_so, file = 'results/rdata/geo_so_sct_integrated_with_markers.rds')
saveRDS(geo_markers, file = 'results/rdata/geo_markers.rds')

Summary

Now that we have characterized the expression of both analytical marker genes and literature / knowledge-based marker genes, we may have a better sense of what cell-types or subtypes our clusters might correspond to.

However, marker genes alone might not be sufficient to determine cell-type or sub-type labels for our clusters so we will discuss other more automated approaches to complement these results.

Next steps: Cell type prediction tools


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
LS0tCnRpdGxlOiAiTWFya2VyIGlkZW50aWZpY2F0aW9uIGFuZCB2aXN1YWxpemF0aW9uIgphdXRob3I6ICJVTSBCaW9pbmZvcm1hdGljcyBDb3JlIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAgICAgICBodG1sX2RvY3VtZW50OgogICAgICAgICAgICBpbmNsdWRlczoKICAgICAgICAgICAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgICAgICAgICAgdGhlbWU6IHBhcGVyCiAgICAgICAgICAgIHRvYzogdHJ1ZQogICAgICAgICAgICB0b2NfZGVwdGg6IDQKICAgICAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgICAgICAgICAgZmlnX2NhcHRpb246IHRydWUKICAgICAgICAgICAgbWFya2Rvd246IEdGTQogICAgICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSwgdGQgewogICBmb250LXNpemU6IDE4cHg7Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTJweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogMTJweAp9Cgp0YWJsZS5maWcsIHRoLmZpZywgdGQuZmlnIHsKICBib3JkZXI6IDFweCBzb2xpZCBibGFjazsKICBib3JkZXItY29sbGFwc2U6IGNvbGxhcHNlOwogIHBhZGRpbmc6IDE1cHg7Cn0KPC9zdHlsZT4KCmBgYHtyIGtsaXBweSwgZWNobz1GQUxTRSwgaW5jbHVkZT1UUlVFfQprbGlwcHk6OmtsaXBweShsYW5nID0gYygiciIsICJtYXJrZG93biIsICJiYXNoIiksIHBvc2l0aW9uID0gYygidG9wIiwgInJpZ2h0IikpCmBgYAoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0Kc291cmNlKCIuLi9iaW4vY2h1bmstb3B0aW9ucy5SIikKa25pdHJfZmlnX3BhdGgoIjA2LU1hcmtlclZpc3VhbGl6YXRpb24vMDYtIikKYGBgCgojIFdvcmtmbG93IE92ZXJ2aWV3IHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cgo8YnIvPgo8aW1nIHNyYz0iaW1hZ2VzL3dheWZpbmRlci93YXlmaW5kZXIucG5nIiBhbHQ9IndheWZpbmRlciIgc3R5bGU9ImhlaWdodDogNDAwcHg7Ii8+Cjxici8+Cjxici8+CgojIEludHJvZHVjdGlvbgoKVG8gaW50ZXJwcmV0IG91ciBpbml0aWFsIGNsdXN0ZXJpbmcgcmVzdWx0cyBmb3IgYWxsIGNlbGxzIGluIG91ciBleHBlcmltZW50LCB3ZSdsbCBuZWVkIHRvIGNoYXJhY3Rlcml6ZSB0aGUgZ2VuZSBleHByZXNzaW9uIGRyaXZpbmcgc2VwYXJhdGlvbiBiZXR3ZWVuIHRoZSBjbHVzdGVycy4gCgo8dGFibGUgY2xhc3M9J2ZpZyc+Cjx0ciBjbGFzcz0nZmlnJz48dGQgY2xhc3M9J2ZpZyc+IVtdKGltYWdlcy9ncmFwaGljYWxfYWJzdHJhY3RzL2dyYXBoaWNhbF9hYnN0cmFjdF9tYXJrZXJfaWQucG5nKTwvdGQ+PC90cj4KPHRyIGNsYXNzPSdmaWcnPjx0ZCBjbGFzcz0nZmlnJz5FeHByZXNzaW9uIHByb2ZpbGVzIChBKSBmb3IgZm9yIGVhY2ggY2VsbCBjbHVzdGVyIGFyZSBldmFsdWF0ZWQgYWdhaW5zdCBhbGwgb3RoZXIgY2x1c3RlcnMgKEIpIHRvIGlkZW50aWZ5IG1hcmtlciBnZW5lcywgaS5lLiBnZW5lcyB1c2VmdWwgZm9yIHNlZ3JlZ2F0aW5nIHRoZSBjZWxscyBpbnRvIGNsdXN0ZXJzIChDKS4gCjwvdGQ+PC90cj4KPC90YWJsZT4KPGJyLz4KCkluIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCBkZW1vbnN0cmF0ZToKLSBIb3cgdG8gZ2VuZXJhdGUgImFuYWx5dGljYWwiIG1hcmtlcnMgYmFzZWQgb24gZ2VuZSBleHByZXNzaW9uIGluIGVhY2ggY2x1c3RlciAKLSBIb3cgdG8gdmlzdWFsaXplIHRoZSBleHByZXNzaW9uIG9mIGdlbmVzIG9mIGludGVyZXN0IHRvIGFpZCBpbiBjbHVzdGVyIGlkZW50aWZpY2F0aW9uIGFuZCBsYWJlbGluZwoKVGhpcyBwcm9jZXNzIGNhbiBiZSBhIGhpZ2hseSB2YXJpYWJsZSwgZnJvbSBzZWVpbmcgd2VsbC1jaGFyYWN0ZXJpemVkIG1hcmtlciBnZW5lcyBhcyB0b3AgbWFya2VycyB0byBuZWVkaW5nIHRvIGRpZyB0aHJvdWdoIHRoZSBsaXRlcmF0dXJlIGFuZCBwbG90IHRoZSBleHByZXNzaW9uIGZvciBtdWx0aXBsZSBzZXRzIG9mIGdlbmVzIG9mIGludGVyZXN0LiBMaWtlIHRoZSBwcmV2aW91cyBzZWN0aW9ucyAoYW5kIGxpa2Ugb3RoZXIgYXJlYXMgb2YgcmVzZWFyY2gpLCB3aGVuIHRoaXMgcHJvY2VzcyBpcyBtb3JlIGludm9sdmVkIG9yIGl0ZXJhdGl2ZSwgb2Z0ZW4gb25seSB0aGUgZmluYWwgcmVzdWx0cyBhcmUgcmVwb3J0ZWQuCgojIyBPYmplY3RpdmVzCjwhLS1BZGQgc3BlY2lmaWMgZ29hbHMgZm9yIHNlY3Rpb24tLT4KLSBEZXRlcm1pbmUgZ2VuZSBtYXJrZXJzIGZvciBlYWNoIG9mIHRoZSBjbHVzdGVycyB1c2luZyBgRmluZEFsbE1hcmtlcnMoKWAKLSBWaXN1YWxpemUgZXhwcmVzc2lvbiBhY3Jvc3MgY2x1c3RlcnMgZm9yIGdlbmVzIG9mIGludGVyZXN0IHVzaW5nIGBEb3RQbG90KClgCgotLS0KCmBgYHtyLCByZWFkX3Jkc19oaWRkZW4sIGVjaG8gPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmlmKCFleGlzdHMoJ2dlb19zbycpKSB7CiAgbGlicmFyeShTZXVyYXQpCiAgbGlicmFyeShCUENlbGxzKQogIGxpYnJhcnkodGlkeXZlcnNlKQoKICBvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSAxZTkpCgogIGdlb19zbyA9IHJlYWRSRFMoJ3Jlc3VsdHMvcmRhdGEvZ2VvX3NvX3NjdF9jbHVzdGVyZWQucmRzJykKfQpgYGAKCiMgQ2x1c3RlciBtYXJrZXJzIGFuZCBjaGFyYWN0ZXJpemF0aW9uCgpBZnRlciBnZW5lcmF0aW5nIGNsdXN0ZXJzLCB3ZSBuZWVkIHRvIHBlcmZvcm0gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgdG8gaWRlbnRpZnkgdGhlIGdlbmVzIHRoYXQgZGlzdGluZ3Vpc2ggdGhvc2UgY2x1c3RlcnMgKFtzb3VyY2VdKGh0dHBzOi8vb3V5YW5nbGFiLmNvbS9zaW5nbGVjZWxsL2NsdXN0Lmh0bWwjaWRlbnRpZnlpbmctbWFya2VyLWdlbmVzKSkuICBUaGlzIHNob3VsZCBhbGxvdyB1cyB0byBnZXQgdmlzaWJpbGl0eSBvbiBzb21lIGtleSBxdWVzdGlvbnMgZm9yIG91ciBjbHVzdGVycywgYXMgW2hpZ2hsaWdodGVkIGJ5IHRoZSBIQkMgbWF0ZXJpYWxzXShodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9zY1JOQS1zZXFfb25saW5lL2xlc3NvbnMvMDlfbWVyZ2VkX1NDX21hcmtlcl9pZGVudGlmaWNhdGlvbi5odG1sKSwgbmFtZWx5OgoKLSBBcmUgdGhlcmUgYXJlIGJpb2xvZ2ljYWxseSBtZWFuaW5nZnVsIGdlbmUgZXhwcmVzc2lvbiBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBjbHVzdGVycz8gCi0gRG9lcyB0aGUgZ2VuZSBleHByZXNzaW9uIG9mIHRoZSBnZW5lcmF0ZWQgY2x1c3RlcnMgY29ycmVzcG9uZCB0byBleHBlY3RlZCBjZWxsLXR5cGVzIG9yIHN1Yi10eXBlcz8gCi0gQXJlIHRoZXJlIGNsdXN0ZXJzIHdpdGggc2ltaWxhciBleHByZXNzaW9uIHRoYXQgc2hvdWxkIGJlIGNvbWJpbmVkIGFuZC9vciBjbHVzdGVycyB0aGF0IG1pZ2h0IG5lZWQgdG8gYmUgc3ViLWNsdXN0ZXJlZCBpbnRvIHNtYWxsZXIgcG9wdWxhdGlvbnM/CgpNYW55IG9mIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiAoREUpIHRvb2xzIGRlc2lnbmVkIGZvciBidWxrIFJOQS1zZXEgc2FtcGxlcyBoYXZlIGJlZW4gYmVuY2htYXJrZWQgZm9yIHBlcmZvcm1hbmNlIG9uIHNjUk5BLXNlcSBpbiBbU29uZXNvbiBhbmQgUm9iaW5zb24gKDIwMTgpXShodHRwczovL3B1Ym1lZC5uY2JpLm5sbS5uaWguZ292LzI5NDgxNTQ5LykgYW5kIHRoZXJlIGFyZSBhbHNvIGRlZGljYXRlZCBERSB0b29scyBmb3Igc2NSTkEtc2VxLCBsaWtlIFtNQVNUXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTMwNTktMDE1LTA4NDQtNSksIHRoYXQgdXNlIG1vZGVscyB0aGF0IGFjY291bnQgZm9yIHRoZSBleHBlY3RlZCBzcGFyc2Ugc3RydWN0dXJlIG9mIHNjUk5BLXNlcSBkYXRhLiBIb3dldmVyIGluIG91ciBhbmQgb3RoZXJzJyBleHBlcmllbmNlLCB0aGUgZGVmYXVsdCBXaWxjb3hvbiB0ZXN0IGlzIG9mdGVuIHN1ZmZpY2llbnQgZm9yIHNpbXBsZSBwYWlyd2lzZSBERSBjb21wYXJpc29ucywgd2hpbGUgZWRnZVIgKFtSb2JpbnNvbiwgTWNDYXJ0aHksIGFuZCBTbXl0aCAyMDEwXShodHRwczovL3B1Ym1lZC5uY2JpLm5sbS5uaWguZ292LzE5OTEwMzA4LykpIGlzIHJlY29tbWVuZGVkIGJ5IHRoZSBbT3V5YW5nIExhYl0oW091eWFuZ10oaHR0cHM6Ly9vdXlhbmdsYWIuY29tL3NpbmdsZWNlbGwvY2x1c3QuaHRtbCNpZGVudGlmeWluZy1tYXJrZXItZ2VuZXMpKSBmb3IgbW9yZSBjb21wbGV4IGNvbXBhcmlzb24gZGVzaWducywgc3VjaCBhcyB0aG9zZSB0aGF0IGluY2x1ZGUgY292YXJpYXRlcy4KCjwhLS0gQWRkaXRpb25hbCBjb250ZXh0IGZyb20gW091eWFuZ10oaHR0cHM6Ly9vdXlhbmdsYWIuY29tL3NpbmdsZWNlbGwvY2x1c3QuaHRtbCNpZGVudGlmeWluZy1tYXJrZXItZ2VuZXMpIHJlZ2FyZGluZyB0b29sIG9wdGlvbnMgYW5kIFtIQkMgbWF0ZXJpYWxzXShodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9zY1JOQS1zZXFfb25saW5lL2xlc3NvbnMvMDlfbWVyZ2VkX1NDX21hcmtlcl9pZGVudGlmaWNhdGlvbi5odG1sKSByZWdhcmRpbmcgRmluZE1hcmtlcnMgdnMgRmluZENvbnNlcnZlZE1hcmtlcnMsIGV0Yy4gLS0+CgohW0ltYWdlOiBDb21wYXJpc29uIG9mIHRoZSBwZXJmb3JtYW5jZSBvZiBERSBtZXRob2RzIGFwcGxpZWQgdG8gc2NSTkEtc2VxIGRhdGFzZXRzLiBNZXRob2RzIGFyZSByYW5rZWQgYnkgdGhlaXIgYXZlcmFnZSBwZXJmb3JtYW5jZSBhY3Jvc3MgYWxsIHRoZSBsaXN0ZWQgY3JpdGVyaWEuIEltYWdlIHRha2VuIGZyb20gU29uZXNvbiBhbmQgUm9iaW5zb24gKDIwMTgpLl0oLi9pbWFnZXMvY3VycmljdWx1bS8wNi1NYXJrZXJWaXN1YWxpemF0aW9uL091eWFuZ19jbHVzdC1kZUNvbXBhcmUucG5nKQoKPGRldGFpbHM+CiAgICA8c3VtbWFyeT4qQWRkaXRpb25hbCBjb25zaWRlcmF0aW9ucyBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24qPC9zdW1tYXJ5PgogICAgVGhlIE91eWFuZyBMYWIgaGFzIGEgW3NlY3Rpb24gb2YgdGhlaXIgdHV0b3JpYWxdKGh0dHBzOi8vb3V5YW5nbGFiLmNvbS9zaW5nbGVjZWxsL2NsdXN0Lmh0bWwjc2VjOmRpZmZleHByKSB0aGF0IGRpc2N1c3NlcyB0aGUgbWV0aG9kcyBhdmFpbGFibGUgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGluY2x1ZGluZyBzb21lIGhpZ2hsaWdodGVkIGluIHRoZSBmaWd1cmUgYmVsb3csIGFzIHdlbGwgYXMgYSBtb3JlIGV4dGVuc2l2ZSBzZWN0aW9uIG9uIFt0aHJlc2hvbGQgY29uc2lkZXJhdGlvbnNdKGh0dHBzOi8vb3V5YW5nbGFiLmNvbS9zaW5nbGVjZWxsL2NsdXN0Lmh0bWwjc2VjOmRpZmZleHByKSwgd2hpbGUgdGhlIFtIQkMgc2VjdGlvbiBvbiBtYXJrZXIgZ2VuZXMgaWRlbnRpZmljYXRpb25dKGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL3NjUk5BLXNlcV9vbmxpbmUvbGVzc29ucy8wOV9tZXJnZWRfU0NfbWFya2VyX2lkZW50aWZpY2F0aW9uLmh0bWwpIGhpZ2hsaWdodHMgdGhlIGRpZmZlcmVudCB0eXBlcyBvZiBtYXJrZXIgaWRlbnRpZmljYXRpb24gb3B0aW9ucyBhdmFpbGFibGUgdmlhIFNldXJhdC4KPC9kZXRhaWxzPgo8YnI+CgojIyBNYXJrZXIgaWRlbnRpZmljYXRpb24KCkZpcnN0LCB3ZSdsbCBjaGFuZ2UgaWRlbnRpdGllcyBvZiB0aGUgY2VsbHMgdG8gImludGVncmF0ZWQuc2N0LnJwY2EuY2x1c3RlcnMiIGV4cGxpY2l0bHkgd2l0aCBgU2V0SWRlbnQoKWAuIFRoZSAic2V1cmF0X2NsdXN0ZXJzIiBjb2x1bW4gaXMgdGhlIGRlZmF1bHQgY29sdW1uIGZvciBjZWxsIGlkZW50aXRpZXMgYW5kIGNoYW5nZXMgZWFjaCB0aW1lIGEgbmV3IGNsdXN0ZXJpbmcgaXMgcGVyZm9ybWVkLiBUaGVuLCB3ZSdsbCBlbnN1cmUgdGhhdCB0aGUgY29ycmVjdCByZXNvbHV0aW9uIGlzIHNlbGVjdGVkIGZyb20gb3VyIFNldXJhdCBvYmplY3QgYW5kIHRoZW4gd2UnbGwgdXNlIHRoZSBgUHJlcFNDVEZpbmRNYXJrZXJzKClgIFtmdW5jdGlvbl0oaHR0cHM6Ly9zYXRpamFsYWIub3JnL3NldXJhdC9yZWZlcmVuY2UvcHJlcHNjdGZpbmRtYXJrZXJzKSBpbiBwcmVwYXJhdGlvbiBmb3IgREUgY29tYXByaXNvbnMgdG8gInJldmVyc2UgdGhlIGluZGl2aWR1YWwgU0NUIHJlZ3Jlc3Npb24gbW9kZWwgdXNpbmcgbWluaW11bSBvZiBtZWRpYW4gVU1JIGFzIHRoZSBzZXF1ZW5jaW5nIGRlcHRoIGNvdmFyaWF0ZSIgYWNjb3JkaW5nIHRvIHRoZSBbU2V1cmF0IGRvY3VtZW50YXRpb25dKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvcmVmZXJlbmNlL3ByZXBzY3RmaW5kbWFya2VycykuIFJlbWVtYmVyIHRoYXQgd2UndmUgcGVyZm9ybWVkIGludGVncmF0aW9uIGFuZCBjbHVzdGVyaW5nIHRvIGFzc2lnbiB0aGUgY2VsbHMgdG8gY2x1c3RlcnMgcmVnYXJkbGVzcyBvZiB0aGVpciBleHBlcmltZW50YWwgY29uZGl0aW9uIGJ1dCB0aGF0IHdlIG5vdyB3YW50IHRvIGVuc3VyZSB0aGF0IHRoZSBkYXRhIGlzIG5vcm1hbGl6ZWQgYnV0IG5vdCB3aXRoIHRoZSBTQ1RyYW5zZm9ybWF0aW9uIHNjYWxpbmcgbmVlZGVkIGZvciB0aGUgcHJldmlvdXMgc3RlcHMuCgo8IS0tIGlmIHdlIHdhbnQgdG8gdXNlIHRoZSBub3JtYWxpemVkIFJOQSBkYXRhIChub3QgdGhlIGludGVncmF0ZWQgZGF0YSkgYERlZmF1bHRBc3NheShnZW9fc28pIDwtICJSTkEiYCwgYWx0aG91Z2ggbmVlZCB0byBjaGVjayBpZiB0aGF0IHN0aWxsIG1ha2VzIHNlbnNlIGZvciBTZXVyYXQgdjUgLS0+IAoKVGhlbiB3ZSdsbCBydW4gdGhlYEZpbmRBbGxNYXJrZXJzKClgIFtmdW5jdGlvbl0oaHR0cHM6Ly9zYXRpamFsYWIub3JnL3NldXJhdC9yZWZlcmVuY2UvZmluZGFsbG1hcmtlcnMpIHRvIGdlbmVyYXRlIGNvbXBhcmlzb25zIGJldHdlZW4gZWFjaCBjbHVzdGVyIGFuZCBhbGwgb3RoZXIgY2VsbHMsIHJlZ2FyZGxlc3Mgb2YgdGhlIGV4cGVyaW1lbnRhbCBncm91cC4gTm90ZSAtIHRoZSBzdGF0aXN0aWNhbCB0ZXN0IHRvIHBlcmZvcm0gY2FuIGJlIHNwZWNpZmllZCBpbiBgRmluZEFsbE1hcmtlcnMoKWAsIGJ1dCB0aGUgZGVmYXVsdCBpcyBhIFdpbGNveG9uIHRlc3QuIAoKYGBge3IsIGZpbmRfYWxsX21hcmtlcnMsIGNhY2hlID0gVFJVRSwgY2FjaGUubGF6eSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyMjIyMgRGF5IDMgLSBNYXJrZXIgaWRlbnRpZmljYXRpb24gYW5kIHZpc3VhbGl6YXRpb24gCgojIEZpbmQgZW1waXJpY2FsIG1hcmtlcnMgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBQcmVwIGZvciBjbHVzdGVyIGNvbXBhcmlzb25zCmdlb19zbyA9IFNldElkZW50KGdlb19zbywgdmFsdWUgPSAnaW50ZWdyYXRlZC5zY3QucnBjYS5jbHVzdGVycycpCmdlb19zbyA9IFByZXBTQ1RGaW5kTWFya2VycyhnZW9fc28pCgojIFJ1biBjb21wYXJpc29ucyBmb3IgZWFjaCBjbHVzdGVyIHRvIGdlbmVyYXRlIG1hcmtlcnMKZ2VvX21hcmtlcnMgPSBGaW5kQWxsTWFya2VycyhnZW9fc28sIG9ubHkucG9zID0gVFJVRSwgbWluLnBjdCA9IDAuMDUpCgojIFRha2UgYSBsb29rIGF0IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgcmVzdWx0CmhlYWQoZ2VvX21hcmtlcnMpCgojIFdyaXRlIG91dCBmdWxsIGNsdXN0ZXIgbWFya2VyIHJlc3VsdHMgdG8gZmlsZQp3cml0ZV9jc3YoZ2VvX21hcmtlcnMsIGZpbGUgPSAncmVzdWx0cy90YWJsZXMvbWFya2VyX2dlbmVzXzAuNHJlcy5jc3YnKQpgYGAKCgohW0ltYWdlOiBTY2hlbWF0aWMgYWZ0ZXIgU2V0SWRlbnQoKS5dKGltYWdlcy9zZXVyYXRfc2NoZW1hdGljL1NsaWRlMTEucG5nKQoKPCEtLS0gQ29uc2lkZXIgaG93IHRvIHJlcHJlc2VudCB0aGUgcmVzdWx0IG9mIFByZXBTQ1RGaW5kTWFya2VycygpIGluIHRoZSBzY2hlbWF0aWMuIEl0J3MgYSBsaXR0bGUgdG9vIHN1YnRsZS4uLiAtLT4KCjwhLS0tIENvbnNpZGVyIGFkZGluZyBmaWd1cmUgc2hvd2luZyBleGFtcGxlIG9mIHdoYXQncyBiZWluZyBjb21wYXJlZCAoY2lyY2xlIGNsdXN0ZXIgMSBhbmQgdGhlbiBjaXJjbGUgYWxsIG90aGVyIGNlbGxzIGZvciBleGFtcGxlKSAtLT4KCjxkZXRhaWxzPgogICAgPHN1bW1hcnk+KlNldXJhdCB2NSBpbXByb3ZlbWVudHMqPC9zdW1tYXJ5PgogICAgRm9yIG1hcmtlciBnZW5lcmF0aW9uLCBTZXVyYXQgdjUgdXNlcyB0aGUgW3ByZXN0byBwYWNrYWdlXShodHRwczovL3d3dy5iaW9yeGl2Lm9yZy9jb250ZW50LzEwLjExMDEvNjUzMjUzdjEpIHRvIHJlZHVjZSB0aGUgdGltZSByZXF1aXJlZCB0byBydW4gREUgY29tcGFyaXNvbnMsIHBhcnRpY3VsYXJseSBmb3IgbGFyZ2UgZGF0YXNldHMuIEZvciB1c2VycyB3aG8gYXJlIG5vdCB1c2luZyBwcmVzdG8sIFNldXJhdCByZWNvbW1lbmRzIGluY3JlYXNpbmcgdGhlIG1pbi5wY3QgYW5kIGxvZ2ZjLnRocmVzaG9sZCBwYXJhbWV0ZXJzY3RvIGluY3JlYXNlIHRoZSBzcGVlZCBvZiBERSB0ZXN0aW5nIChbc291cmNlXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL3BibWMza190dXRvcmlhbC5odG1sI2ZpbmRpbmctZGlmZmVyZW50aWFsbHktZXhwcmVzc2VkLWZlYXR1cmVzLWNsdXN0ZXItYmlvbWFya2VycykpLgo8L2RldGFpbHM+Cjxicj4KCk5vdGUgdGhhdCBvdmVyLWludGVycHJldGF0aW9uIG9mIHRoZXNlIHJlc3VsdHMgc2hvdWxkIGJlIGF2b2lkZWQsIHNpbmNlIGVhY2ggY2VsbCBpcyB1c2VkIGFzIGEgcmVwbGljYXRlIGluIHRoZXNlIGNvbXBhcmlzb25zIHdoaWNoIGNhbiBsZWFkIHRvIGluZmxhdGVkIChlLmcuIHZlcnkgbG93KSBwLXZhbHVlcywgdGhlIHRvcCBtYXJrZXJzIGFyZSBtb3JlIGxpa2VseSB0byBiZSB0cnVzdHdvcnRoeSAoW3NvdXJjZV0oaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vc2NSTkEtc2VxX29ubGluZS9sZXNzb25zLzA5X21lcmdlZF9TQ19tYXJrZXJfaWRlbnRpZmljYXRpb24uaHRtbCkpLiAKClRoZXJlZm9yZSwgaXQncyB1c2VmdWwgdG8gZmlsdGVyIHRoZSByZXN1bHRzIHRvIGhpZ2hsaWdodCB0aGUgdG9wIHBvc2l0aXZlIG1hcmtlcnMgKHNpbmNlIGEgcG9zaXRpdmUgZm9sZC1jaGFuZ2Ugd291bGQgbWVhbiB0aGF0IGdlbmUgaXMgbW9yZSBoaWdobHkgZXhwcmVzc2VkIGluIHRoZSBjbHVzdGVyIGNvbXBhcmVkIHRvIGFsbCBvdGhlciBjZWxscyksIGJlZm9yZSBsb29raW5nIGF0IG91ciByZXN1bHRzCgpgYGB7ciwgdG9wXzVfY2x1c3Rlcl9tYXJrZXJzfQojIElkZW50aWZ5IG1hcmtlciBnZW5lcyBmb3IgZWFjaCBjbHVzdGVyIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBDcmVhdGUgdGFibGUgb2YgdG9wIDUgbWFya2VycyBwZXIgY2x1c3RlciAodXNpbmcgZGVmYXVsdCByYW5raW5nKQp0b3BfNSA9IGdlb19tYXJrZXJzICU+JSBmaWx0ZXIocF92YWxfYWRqIDwgMC4wMSkgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSBzbGljZV9oZWFkKG4gPSA1KQoKIyBMb29rIGF0IHJlc3VsdHMKaGVhZCh0b3BfNSwgbiA9IDEwKQoKIyBPcHRpb25hbCAtIENyZWF0ZSB0YWJsZSBvZiB0b3AgNSBtYXJrZXJzIHBlciBjbHVzdGVyIChyYW5rZWQgYnkgbG9nRkMpCnRvcF81X2J5X2xvZzJGQyA9IGdlb19tYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgYXJyYW5nZShwX3ZhbF9hZGosIGRlc2MoYXZnX2xvZzJGQykpICU+JSBzbGljZV9oZWFkKG4gPSA1KQoKIyBMb29rIGF0IHJlc3VsdHMgYWZ0ZXIgcmFua2luZwpoZWFkKHRvcF81X2J5X2xvZzJGQywgbiA9IDEwKSAjIG5vdGljZSBkaWZmZXJlbmNlIGluIHBjdC4xIGNvbHVtbiBiZXR3ZWVuIHRhYmxlcwpgYGAKCldlIGV4cGVjdCB0byBzZWUgc2V2ZXJhbCBjb2x1bW5zOgoKLSBgZ2VuZWA6IGdlbmUgc3ltYm9sCi0gYHBfdmFsYDogcC12YWx1ZSBub3QgYWRqdXN0ZWQgZm9yIG11bHRpcGxlIHRlc3QgY29ycmVjdGlvbgotIGBhdmdfbG9nRkNgOiBhdmVyYWdlIGxvZyBmb2xkIGNoYW5nZS4gUG9zaXRpdmUgdmFsdWVzIGluZGljYXRlIHRoYXQgdGhlIGdlbmUgaXMgbW9yZSBoaWdobHkgZXhwcmVzc2VkIGluIHRoZSBjbHVzdGVyLgotIGBwY3QuMWA6IHBlcmNlbnRhZ2Ugb2YgY2VsbHMgd2hlcmUgdGhlIGdlbmUgaXMgZGV0ZWN0ZWQgaW4gdGhlIGNsdXN0ZXIgCi0gYHBjdC4yYDogcGVyY2VudGFnZSBvZiBjZWxscyB3aGVyZSB0aGUgZ2VuZSBpcyBkZXRlY3RlZCBvbiBhdmVyYWdlIGFjcm9zcyBhbGwgb3RoZXIgY2x1c3RlcnMgCi0gYHBfdmFsX2FkamA6IGFkanVzdGVkIHAtdmFsdWUgYmFzZWQgb24gYm9uZmVycm9uaSBjb3JyZWN0aW9uIHVzaW5nIGFsbCBnZW5lcyBpbiB0aGUgZGF0YXNldCwgdXNlZCB0byBkZXRlcm1pbmUgc2lnbmlmaWNhbmNlCi0gYGNsdXN0ZXJgOiBjbHVzdGVyIHJlcHJlc2VudGVkIGJ5IGBwY3QuMWAgYW5kIGZvciB3aGljaCB0aGUgc3RhdGlzdGljcyBpbiB0aGUgcm93IGFyZSByZXBvcnRlZAoKV2hlbiBsb29raW5nIGF0IHRoZSBvdXRwdXQsIGl0IGlzIGltcG9ydGFudCB0byBwcmlvcml0aXplIG1hcmtlciBnZW5lcyB3aXRoIGJvdGggbGFyZ2VyIGZvbGQtY2hhbmdlIGRpZmZlcmVuY2VzIGFuZCBsYXJnZXIgZGlmZmVyZW5jZSBiZXR3ZWVuIHBjdC4xIGFuZCBwY3QuMiwgcGFydGljdWxhcmx5IGlmIHBjdC4xIGlzIGhpZ2ggKGUuZy4gaWYgODAlIG9mIGNlbGxzIGluIHRoZSBjbHVzdGVyIGV2YWx1YXRlZCBleHByZXNzIHRoZSBnZW5lIHRoYXQgbW9yZSByZWxpYWJsZSB0aGFuIGlmIG9ubHkgMjAlIG9mIGNlbGxzIGV4cHJlc3MgdGhhdCBnZW5lKSAoW3NvdXJjZV0oaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vc2NSTkEtc2VxX29ubGluZS9sZXNzb25zLzA5X21lcmdlZF9TQ19tYXJrZXJfaWRlbnRpZmljYXRpb24uaHRtbCkpLgoKIyMgTWFya2VyIHZpc3VhbGl6YXRpb24KCk5vdyB0aGF0IHdlIGhhdmUgZ2VuZXJhdGVkIGEgc2V0IG9mIG1hcmtlciBnZW5lcyBmb3Igb3VyIGNsdXN0ZXJzLCBpdCBpcyB1c2VmdWwgdG8gdmlzdWFsaXplIHRoZSBleHByZXNzaW9uIG9mIHRob3NlIG1hcmtlcnMgdG8gYWlkIGluIGV2YWx1YXRpbmcgdGhlbS4gV2hpbGUgdGhlIGV4cHJlc3Npb24gb2YgaW5kaXZpZHVhbCBnZW5lcyBwZXIgY2VsbCBjYW4gYmUgb3ZlcmxhaWQgb24gb3VyIFVNQVBzIChhcyB3aXRoIHRoZSBgRmVhdHVyZVBsb3QoKWAgW2Z1bmN0aW9uXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L3JlZmVyZW5jZS9mZWF0dXJlcGxvdCkpLCBpdCdzIG9mdGVuIG1vcmUgdXNlZnVsIHRvIHZpc3VhbGl6ZSB0aGUgZXhwcmVzc2lvbiBvZiBtdWx0aXBsZSBnZW5lcyBzaW11bHRhbmVvdXNseS4gV2hpbGUgdGhlcmUgYXJlIFttdWx0aXBsZSBtZXRob2RzIHN1cHBvcnRlZCBieSBTZXVyYXRdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvdmlzdWFsaXphdGlvbl92aWduZXR0ZSkgZm9yICB2aXN1YWxpemluZyBtYXJrZXIgZ2VuZSBleHByZXNzaW9uLCBhIGhlYXRtYXAgb3IgYSByZWxhdGVkIHBsb3QgY2FsbGVkIGEgZG90cGxvdCBhcmUgY29tbW9ubHkgdXNlZC4gCgpXZSdsbCB1c2UgdGhlIGBEb3RQbG90KClgIFtmdW5jdGlvbl0oaHR0cHM6Ly9zYXRpamFsYWIub3JnL3NldXJhdC9yZWZlcmVuY2UvZG90cGxvdCkgd2l0aCB0aGUgU0NUIHZhbHVlcyB0byB2aXN1YWxpemUgdGhlIHRvcCA1IG1hcmtlciBnZW5lcyBwZXIgY2x1c3RlcjoKCmBgYHtyLCB0b3BfNV9kb3RfcGxvdF9zY3QsIGZpZy5oZWlnaHQgPSAxOCwgZmlnLndpZHRoID0gOH0KIyBWaXN1YWxpemUgdG9wIG1hcmtlciBnZW5lcyBhcyBkb3QgcGxvdCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnRvcF81X3NjdF9kb3RfcGxvdCA9IERvdFBsb3QoZ2VvX3NvLCBmZWF0dXJlcyA9IHVuaXF1ZSh0b3BfNSRnZW5lKSkgKyAKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUpKSArCiAgICBsYWJzKHRpdGxlID0gJ1RvcCA1IENsdXN0ZXIgR2VuZXMgYnkgRkRSIGFuZCBhdmdfbG9nMkZDJykgKyBjb29yZF9mbGlwKCkKdG9wXzVfc2N0X2RvdF9wbG90CgpnZ3NhdmUoZmlsZW5hbWUgPSAncmVzdWx0cy9maWd1cmVzL21hcmtlcnNfdG9wXzVfc2N0X2RvdF9wbG90LnBuZycsIHBsb3QgPSB0b3BfNV9zY3RfZG90X3Bsb3QsIHdpZHRoID0gOCwgaGVpZ2h0ID0gMTgsIHVuaXRzID0gJ2luJykgCmBgYAoKSW4gdGhlIGRvdHBsb3Qgd2UgY2FuIHNlZSB0aGF0IHRoZSBjb2xvciBpbmRpY2F0ZXMgdGhlIGV4cHJlc3Npb24gb2YgdGhlIGdlbmUgd2hpbGUgdGhlIHNpemUgb2YgdGhlIGRvdCBpbmRpY2F0ZXMgdGhlIHByb3BvcnRpb24gb2YgY2VsbHMgZXhwcmVzc2luZyB0aGF0IGdlbmUgaW4gZWFjaCBjbHVzdGVyIChbc291cmNlXShodHRwczovL291eWFuZ2xhYi5jb20vc2luZ2xlY2VsbC9jbHVzdC5odG1sI3Zpc3VhbGlzaW5nLW1hcmtlci1nZW5lcykpLiAKCjxkZXRhaWxzPgogICAgPHN1bW1hcnk+KlVzaW5nIHJhdyBSTkEgdmFsdWVzIGluIERvdHBsb3RzKjwvc3VtbWFyeT4KICAgIEluIGFkZGl0aW9uIHRvIHBsb3R0aW5nIHRoZSBTQ1QgdmFsdWVzLCB0aGUgcmF3IG9yIG5vcm1hbGl6ZWQgUk5BIHZhbHVlcyBjYW4gYmUgcGxvdHRlZCBhcyB3ZWxsOgoKYGBge3IsIHRvcF81X2RvdF9wbG90X3JuYSwgZXZhbD1GQUxTRX0KIyBBZGQgUk5BIHZhbHVlcyB0byBkb3QgcGxvdCAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnRvcF81X3JuYV9kb3RfcGxvdCA9IERvdFBsb3QoZ2VvX3NvLCBmZWF0dXJlcyA9IHVuaXF1ZSh0b3BfNV9ieV9sb2cyRkMkZ2VuZSksIGFzc2F5ID0gJ1JOQScpICsgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSkgKwogICAgbGFicyh0aXRsZSA9ICdUb3AgNSBDbHVzdGVyIEdlbmVzIGJ5IEZEUiBhbmQgYXZnX2xvZzJGQycpICsgY29vcmRfZmxpcCgpCgpnZ3NhdmUoZmlsZW5hbWUgPSAncmVzdWx0cy9maWd1cmVzL21hcmtlcnNfdG9wXzVfcm5hX2RvdF9wbG90LnBuZycsIHBsb3QgPSB0b3BfNV9ybmFfZG90X3Bsb3QsIHdpZHRoID0gOCwgaGVpZ2h0ID0gMTgsIHVuaXRzID0gJ2luJykgCmBgYAoKPC9kZXRhaWxzPgo8YnI+Cjxicj4KCkJlZm9yZSBwcm9jZWVkaW5nIHdpdGggY2x1c3RlciBhbm5vdGF0aW9ucywgd2UnbGwgYWxzbyBjaGVjayB0aGUgcGVyY2VudGFnZSBvZiBtaXRvY2hvbmRyaWFsIGdlbmVzIHRvIGRldGVybWluZSBpZiB0aGVyZSBhcmUgYW55IGNsdXN0ZXJzIChvciBzdWIgcG9wdWxhdGlvbnMpIHRoYXQgbWlnaHQgY29ycmVzcG9uZCB0byBpbnRlcmVzdGluZyBjZWxsIGRlYXRoIHBhdHRlcm5zIChvciBtaWdodCBpbmRpY2F0ZSBmdXJ0aGVyIGZpbHRlcmluZyBpcyBuZWVkZWQpOgpgYGB7ciwgZWNobz1UUlVFLCBldmFsPSBUUlVFfQojIENoZWNrIG1pdG9jaG9uZHJpYWwgZ2VuZSBleHByZXNzaW9uIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KcGVyY2VudF9taXRvX3Bsb3QgPSBGZWF0dXJlUGxvdChnZW9fc28sIGZlYXR1cmVzPSdwZXJjZW50Lm10JykKcGVyY2VudF9taXRvX3Bsb3QKCiMgc2F2ZSB0byBmaWxlCmdnc2F2ZShmaWxlbmFtZSA9ICdyZXN1bHRzL2ZpZ3VyZXMvcGVyY2VudF91bWFwX21pdG9fcGxvdC5wbmcnLCBwbG90ID0gcGVyY2VudF9taXRvX3Bsb3QsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAnaW4nKQpgYGAKCldlIHNlZSB0aGF0IGEgaGlnaGVyICUgc2VlbSB0byBiZSBzb21ld2hhdCBjb25jZW50cmF0ZWQgaW4gYSBmZXcgcGxhY2VzLCBidXQgaWYgY2VsbCBkZWF0aCBtaWdodCBiZSBvZiBpbnRlcmVzdCBmb3IgdGhlIHJlc2VhcmNoIHF1ZXN0aW9uLCB3ZSdkIHdhbnQgdG8gY29uc2lkZXIgaW52ZXN0aWdhdGluZyB0aGlzIHBhdHRlcm4gZnVydGhlciBieSBzcGxpdHRpbmcgdXAgdGhlIHBsb3RzIGJ5IGRheSBhbmQvb3Igd2FpdGluZyB1bnRpbCBhZnRlciBydW5uaW5nIGluaXRpYWwgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgdG8gZGV0ZXJtaW5lIGlmIHRoZXNlIGNlbGwgcG9wdWxhdGlvbnMgYXJlIGludGVyZXN0aW5nIGJpb2xvZ3kgb3Igbm90LgoKCgojIFNhdmUgb3VyIHByb2dyZXNzCgpGaW5hbGx5LCB3ZSdsbCBjcmVhdGUgYW4gb3V0cHV0IGZpbGUgZm9yIG91ciB1cGRhdGVkIFNldXJhdCBvYmplY3QgYW5kIGZvciB0aGUgY2x1c3RlciBtYXJrZXIgcmVzdWx0czoKCmBgYHtyLCBzYXZlX3Jkc19oaWRkZW4sIGVjaG8gPSBGQUxTRX0KaWYoIWZpbGUuZXhpc3RzKCdyZXN1bHRzL3JkYXRhL2dlb19zb19zY3RfaW50ZWdyYXRlZF93aXRoX21hcmtlcnMucmRzJykpIHsKICBzYXZlUkRTKGdlb19zbywgZmlsZSA9ICdyZXN1bHRzL3JkYXRhL2dlb19zb19zY3RfaW50ZWdyYXRlZF93aXRoX21hcmtlcnMucmRzJykKfQoKaWYoIWZpbGUuZXhpc3RzKCdyZXN1bHRzL3JkYXRhL2dlb19tYXJrZXJzLnJkcycpKSB7CiAgc2F2ZVJEUyhnZW9fbWFya2VycywgZmlsZSA9ICdyZXN1bHRzL3JkYXRhL2dlb19tYXJrZXJzLnJkcycpCn0KYGBgCgpgYGB7ciwgc2F2ZV9yZHMsIGV2YWw9RkFMU0V9CiMgU2F2ZSBTZXVyYXQgb2JqZWN0IGFuZCBnZW5lIG1hcmtlciBkYXRhIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpzYXZlUkRTKGdlb19zbywgZmlsZSA9ICdyZXN1bHRzL3JkYXRhL2dlb19zb19zY3RfaW50ZWdyYXRlZF93aXRoX21hcmtlcnMucmRzJykKc2F2ZVJEUyhnZW9fbWFya2VycywgZmlsZSA9ICdyZXN1bHRzL3JkYXRhL2dlb19tYXJrZXJzLnJkcycpCmBgYAoKIyBTdW1tYXJ5CgpOb3cgdGhhdCB3ZSBoYXZlIGNoYXJhY3Rlcml6ZWQgdGhlIGV4cHJlc3Npb24gb2YgYm90aCBhbmFseXRpY2FsIG1hcmtlciBnZW5lcyBhbmQgbGl0ZXJhdHVyZSAvIGtub3dsZWRnZS1iYXNlZCBtYXJrZXIgZ2VuZXMsIHdlIG1heSBoYXZlIGEgYmV0dGVyIHNlbnNlIG9mIHdoYXQgY2VsbC10eXBlcyBvciBzdWJ0eXBlcyBvdXIgY2x1c3RlcnMgbWlnaHQgY29ycmVzcG9uZCB0by4gICAKCkhvd2V2ZXIsIG1hcmtlciBnZW5lcyBhbG9uZSBtaWdodCBub3QgYmUgc3VmZmljaWVudCB0byBkZXRlcm1pbmUgY2VsbC10eXBlIG9yIHN1Yi10eXBlIGxhYmVscyBmb3Igb3VyIGNsdXN0ZXJzIHNvIHdlIHdpbGwgZGlzY3VzcyBvdGhlciBtb3JlIGF1dG9tYXRlZCBhcHByb2FjaGVzIHRvIGNvbXBsZW1lbnQgdGhlc2UgcmVzdWx0cy4KCk5leHQgc3RlcHM6IENlbGwgdHlwZSBwcmVkaWN0aW9uIHRvb2xzCgotLS0tCgpUaGVzZSBtYXRlcmlhbHMgaGF2ZSBiZWVuIGFkYXB0ZWQgYW5kIGV4dGVuZGVkIGZyb20gbWF0ZXJpYWxzIGxpc3RlZCBhYm92ZS4gVGhlc2UgYXJlIG9wZW4gYWNjZXNzIG1hdGVyaWFscyBkaXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIGxpY2Vuc2UgKENDIEJZIDQuMCldKGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8pLCB3aGljaCBwZXJtaXRzIHVucmVzdHJpY3RlZCB1c2UsIGRpc3RyaWJ1dGlvbiwgYW5kIHJlcHJvZHVjdGlvbiBpbiBhbnkgbWVkaXVtLCBwcm92aWRlZCB0aGUgb3JpZ2luYWwgYXV0aG9yIGFuZCBzb3VyY2UgYXJlIGNyZWRpdGVkLgoKPGJyLz4KPGJyLz4KPGhyLz4KfCBbUHJldmlvdXMgbGVzc29uXSgwNS1Qcm9qZWN0aW9uQW5kQ2x1c3RlcmluZy5odG1sKSB8IFtUb3Agb2YgdGhpcyBsZXNzb25dKCN0b3ApIHwgW05leHQgbGVzc29uXSgwNy1DZWxsVHlwZUFubm9zLmh0bWwpIHwKfCA6LS0tIHwgOi0tLS06IHwgLS0tOiB8Cg==