In this module, we will learn:

  • How to choose thresholds for calling differentially expressed genes
  • How to generate common differential expression visualizations


Differential Expression Workflow

Here we will generate summary figures for our results and annotate our DE tables.


Summarizing DE results

Part of differential expression analysis is generating visualizations and summary tables to share our results. While the DESeq2 vignette provides examples of other visualizations, a common visualization to summarize DE comparisons are volcano plots.

Tabular DE summary

To create our own summary, we first need to choose thresholds. A standard threshold for the adjusted p-value is less than 0.05. A reasonable threshold for linear fold-change is less than -1.5 or greater than 1.5 (which corresponds to log2 fold-change -0.585 and 0.585, respectively). Including a fold-change threshold ensures that the DE genes have a reasonable effect size as well as statistical significance.

Let’s set these thresholds as variables to reuse. This is generally good practice because if you want to change those thresholds later then you only have to change them in one location of your script, which is faster and can reduce the risk of errors from missing some instances in your code.

fc = 1.5
fdr = 0.05

Note: Choosing thresholds

Thresholding on adjusted p-values < 0.05 is a standard threshold, but depending on the research question and/or how the results will be used, other thresholds might be reasonable.

There is a nice Biostar post that discusses choosing adjusted p-value thresholds, including cases where a more relaxed threshold might be appropriate and (some heated) discussion of the dangers of adjusting the choosen threshold after running an analysis. Additionally, there is a Dalmon et al 2012 paper about p-value and fold-change thresholds for microarray data that may help provide some context.

If we think back to Computational Foundations, conditional statements could allow us to determine the number of genes that pass our thresholds, which would be useful for annotating our results tables and plots.

Exercise

How would we identify the number of genes with adjusted p-values < 0.05 and a fold-change above 1.5 (or below -1.5) in our comparison?

Solution

Here is one possible answer:

sum(results_deficient_vs_control$padj < fdr & abs(results_deficient_vs_control$log2FoldChange) >= log2(fc), na.rm = TRUE)
[1] 189

Checkpoint: If you see the same number of DE genes with our choosen thresholds, please indicate with a green check. Otherwise use a red x.


Let’s now create a new column in results_deficient_vs_control to record the significance “call” based on these thresholds. And let’s separate the call by “Up” or “Down”, noting that these are relative to our “Case” condition. There are many ways to accomplish this, but the following will work:

# create new column called "call"
results_deficient_vs_control$call = 'NS'
head(results_deficient_vs_control)
log2 fold change (MLE): condition deficient vs control 
Wald test p-value: condition deficient vs control 
DataFrame with 6 rows and 7 columns
                      baseMean log2FoldChange     lfcSE      stat    pvalue
                     <numeric>      <numeric> <numeric> <numeric> <numeric>
ENSMUSG00000000001  1489.83039       0.278343  0.148278  1.877165 0.0604955
ENSMUSG00000000028  1748.93544       0.221671  0.128835  1.720588 0.0853256
ENSMUSG00000000031  2151.87715       0.135823  0.284313  0.477724 0.6328467
ENSMUSG00000000037    24.91672       0.599542  0.562067  1.066673 0.2861195
ENSMUSG00000000049     7.78377      -1.227258  1.148327 -1.068735 0.2851890
ENSMUSG00000000056 19653.54030      -0.201183  0.167344 -1.202215 0.2292803
                        padj        call
                   <numeric> <character>
ENSMUSG00000000001  0.324515          NS
ENSMUSG00000000028  0.385578          NS
ENSMUSG00000000031  0.866812          NS
ENSMUSG00000000037        NA          NS
ENSMUSG00000000049        NA          NS
ENSMUSG00000000056  0.596345          NS
# determine the "Up" and "Down" indices:
up_idx = results_deficient_vs_control$padj < fdr & results_deficient_vs_control$log2FoldChange > log2(fc)
down_idx = results_deficient_vs_control$padj < fdr & results_deficient_vs_control$log2FoldChange < -log2(fc)

# use indices to assign the correct "Up" or "Down" values to the correct indices, and look at the head of the result:
results_deficient_vs_control$call[up_idx] = 'Up'
results_deficient_vs_control$call[down_idx] = 'Down'
head(results_deficient_vs_control)
log2 fold change (MLE): condition deficient vs control 
Wald test p-value: condition deficient vs control 
DataFrame with 6 rows and 7 columns
                      baseMean log2FoldChange     lfcSE      stat    pvalue
                     <numeric>      <numeric> <numeric> <numeric> <numeric>
ENSMUSG00000000001  1489.83039       0.278343  0.148278  1.877165 0.0604955
ENSMUSG00000000028  1748.93544       0.221671  0.128835  1.720588 0.0853256
ENSMUSG00000000031  2151.87715       0.135823  0.284313  0.477724 0.6328467
ENSMUSG00000000037    24.91672       0.599542  0.562067  1.066673 0.2861195
ENSMUSG00000000049     7.78377      -1.227258  1.148327 -1.068735 0.2851890
ENSMUSG00000000056 19653.54030      -0.201183  0.167344 -1.202215 0.2292803
                        padj        call
                   <numeric> <character>
ENSMUSG00000000001  0.324515          NS
ENSMUSG00000000028  0.385578          NS
ENSMUSG00000000031  0.866812          NS
ENSMUSG00000000037        NA          NS
ENSMUSG00000000049        NA          NS
ENSMUSG00000000056  0.596345          NS
# modify column to be factor  to make for easier plotting
results_deficient_vs_control$call = factor(results_deficient_vs_control$call, levels = c('Up', 'Down', 'NS'))
head(results_deficient_vs_control)
log2 fold change (MLE): condition deficient vs control 
Wald test p-value: condition deficient vs control 
DataFrame with 6 rows and 7 columns
                      baseMean log2FoldChange     lfcSE      stat    pvalue
                     <numeric>      <numeric> <numeric> <numeric> <numeric>
ENSMUSG00000000001  1489.83039       0.278343  0.148278  1.877165 0.0604955
ENSMUSG00000000028  1748.93544       0.221671  0.128835  1.720588 0.0853256
ENSMUSG00000000031  2151.87715       0.135823  0.284313  0.477724 0.6328467
ENSMUSG00000000037    24.91672       0.599542  0.562067  1.066673 0.2861195
ENSMUSG00000000049     7.78377      -1.227258  1.148327 -1.068735 0.2851890
ENSMUSG00000000056 19653.54030      -0.201183  0.167344 -1.202215 0.2292803
                        padj     call
                   <numeric> <factor>
ENSMUSG00000000001  0.324515       NS
ENSMUSG00000000028  0.385578       NS
ENSMUSG00000000031  0.866812       NS
ENSMUSG00000000037        NA       NS
ENSMUSG00000000049        NA       NS
ENSMUSG00000000056  0.596345       NS

Tip

It is often helpful to include code like this in differential expression analyses so there is a clearly labelled column that makes subsetting and summarizing the results easier.

Now we are in a position to quickly summarize our differential expression results:

# check summary of results
table(results_deficient_vs_control$call)

   Up  Down    NS 
  120    69 16060 

We see quickly how many genes were “Up” in iron replete, how many were “Down” in iron replete, and how many were not significant.

Checkpoint: If you successfully added the call column and got the same table result as above, please indicate with a green check. Otherwise use a red x.

Visual DE summary

As described by the Galaxy project, a volcano plot is a type of scatterplot that shows statistical significance (adjusted p-value) versus effect size (fold change). In a volcano plot, the most upregulated genes are towards the right, the most downregulated genes are towards the left, and the most statistically significant genes are towards the top.

Let’s coerce the DataFrame which was returned by DESeq2::results() into a tibble in anticipation of using the ggplot2 library to plot. We’re also going to modify our results table so that the row names become a separate column, and so that it’s ordered by adjusted p-value.

# Use the rownames argument to create a new column of gene IDs
# Also arrange by adjusted p-value
results_forPlot = as_tibble(results_deficient_vs_control, rownames = 'id') %>% arrange(padj)

Let’s start with a simple volcano plot that plots the log2FoldChange on the x-axis, and -log10(padj) on the y-axis, adding custom labels to the plot with the labs() function:

# Add plot labels and change the theme - save the plot as object `p`
p = ggplot(results_forPlot, aes(x = log2FoldChange, y = -log10(padj))) +
    geom_point() +
    theme_bw() +
    labs(
        title = 'Volcano Plot',
        subtitle = 'control vs deficient',
        x = 'log2 fold-change',
        y = '-log10 FDR'
    )
p
Warning: Removed 7246 rows containing missing values or values outside the scale range
(`geom_point()`).

What if we now added some visual guides to indicate where the significant genes are? We can use the geom_vline() and geom_hline() functions to accomplish this:

# Add threshold lines
p1 = p +
    geom_vline(
        xintercept = c(0, -log2(fc), log2(fc)),
        linetype = c(1, 2, 2)) +
    geom_hline(
        yintercept = -log10(fdr),
        linetype = 2)
p1
Warning: Removed 7246 rows containing missing values or values outside the scale range
(`geom_point()`).

Finally, why not color the points by their significance status? We already created the call column that has the correct values. In this case we can get away with adding geom_point() to our existing plot and specifying the correct aesthetic, before customizing to have a nicer color scheme:

# map color to the `call` column from our input data
p2 = p1 + geom_point(aes(color = call))
p2
Warning: Removed 7246 rows containing missing values or values outside the scale range
(`geom_point()`).
Removed 7246 rows containing missing values or values outside the scale range
(`geom_point()`).

# adjust our color scheme
p3 = p2 + scale_color_manual(name = '', values=c('#B31B21',  '#1465AC', 'darkgray'))
p3
Warning: Removed 7246 rows containing missing values or values outside the scale range
(`geom_point()`).
Removed 7246 rows containing missing values or values outside the scale range
(`geom_point()`).

For additional visualizations for our DE results, we included some example code in the Bonus Content module and this HBC tutorial also includes some nice examples.

Adding gene symbols to our data

What if we wanted to include labels for some of the most up and/or down regulated genes from our comparisons? We can do this by using external information that maps the ENSEMBL ids from our reference files/count tables to corresponding gene symbols. We’ve provided a table with these mappings but in the next module, we’ll show how to access resources and generate this kind of mapping table.

# read in table mapping ENSEMBL ids to gene symbols
id_mapping = read.table("data/ENSEMBL_id_mappings.csv",
                       sep = ",",
                       header = TRUE)
head(id_mapping)

We want to match the id column of results_deficient_vs_control to the ensembl_gene_id column of id_mapping, and once that match is found, we want to extract the external_gene_name column of id_mapping to get the gene symbol. Next, look at the documentation for dplyr::left_join() and merge the id_mapping table into the results_deficient_vs_control table on the columns ensembl_gene_id and external_gene_name.

# check our starting data
head(results_deficient_vs_control)
log2 fold change (MLE): condition deficient vs control 
Wald test p-value: condition deficient vs control 
DataFrame with 6 rows and 7 columns
                      baseMean log2FoldChange     lfcSE      stat    pvalue
                     <numeric>      <numeric> <numeric> <numeric> <numeric>
ENSMUSG00000000001  1489.83039       0.278343  0.148278  1.877165 0.0604955
ENSMUSG00000000028  1748.93544       0.221671  0.128835  1.720588 0.0853256
ENSMUSG00000000031  2151.87715       0.135823  0.284313  0.477724 0.6328467
ENSMUSG00000000037    24.91672       0.599542  0.562067  1.066673 0.2861195
ENSMUSG00000000049     7.78377      -1.227258  1.148327 -1.068735 0.2851890
ENSMUSG00000000056 19653.54030      -0.201183  0.167344 -1.202215 0.2292803
                        padj     call
                   <numeric> <factor>
ENSMUSG00000000001  0.324515       NS
ENSMUSG00000000028  0.385578       NS
ENSMUSG00000000031  0.866812       NS
ENSMUSG00000000037        NA       NS
ENSMUSG00000000049        NA       NS
ENSMUSG00000000056  0.596345       NS
# use `join` to add gene symbols to row with matching ENSEMBL id
results_deficient_vs_control_annotated = as_tibble(results_deficient_vs_control, rownames = "id") %>%
    left_join(id_mapping, by = c('id' = 'ensembl_gene_id'))
head(results_deficient_vs_control_annotated)
# A tibble: 6 × 9
  id                 baseMean log2FoldChange lfcSE   stat pvalue   padj call 
  <chr>                 <dbl>          <dbl> <dbl>  <dbl>  <dbl>  <dbl> <fct>
1 ENSMUSG00000000001  1490.            0.278 0.148  1.88  0.0605  0.325 NS   
2 ENSMUSG00000000028  1749.            0.222 0.129  1.72  0.0853  0.386 NS   
3 ENSMUSG00000000031  2152.            0.136 0.284  0.478 0.633   0.867 NS   
4 ENSMUSG00000000037    24.9           0.600 0.562  1.07  0.286  NA     NS   
5 ENSMUSG00000000049     7.78         -1.23  1.15  -1.07  0.285  NA     NS   
6 ENSMUSG00000000056 19654.           -0.201 0.167 -1.20  0.229   0.596 NS   
# ℹ 1 more variable: external_gene_name <chr>

We can use some of the tidyverse functions we’ve encountered previously to rename the external_gene_name column to symbol and to move it into the second column position? Hint: Because of the order of the packages we may have loaded, we’ll use dplyr::rename() and dplyr::select() instead of just the select() function. We can discuss this in a moment.

# use dplyr functions to rename columns and reorganize table to make more readable
results_deficient_vs_control_annotated = results_deficient_vs_control_annotated %>%
  dplyr::rename('symbol' = 'external_gene_name') %>%
  dplyr::select(id, symbol, everything())
head(results_deficient_vs_control_annotated)
# A tibble: 6 × 9
  id              symbol baseMean log2FoldChange lfcSE   stat pvalue   padj call 
  <chr>           <chr>     <dbl>          <dbl> <dbl>  <dbl>  <dbl>  <dbl> <fct>
1 ENSMUSG0000000… Gnai3   1490.            0.278 0.148  1.88  0.0605  0.325 NS   
2 ENSMUSG0000000… Cdc45   1749.            0.222 0.129  1.72  0.0853  0.386 NS   
3 ENSMUSG0000000… H19     2152.            0.136 0.284  0.478 0.633   0.867 NS   
4 ENSMUSG0000000… Scml2     24.9           0.600 0.562  1.07  0.286  NA     NS   
5 ENSMUSG0000000… Apoh       7.78         -1.23  1.15  -1.07  0.285  NA     NS   
6 ENSMUSG0000000… Narf   19654.           -0.201 0.167 -1.20  0.229   0.596 NS   

And now we have our differential expression results annotated with gene symbols, which can help in the interpretation of the results, and can be used in downstream analysis such as functional analysis. Although we don’t have time to run this together, we can also use this table to label genes of interest in our volcano plot.

Exercise

Now that we annotated our DE results to add gene symbols, what steps would we take to add labels to our volcano plots for the top differentially expressed genes in our comparison?

Summary

In this section, we:

  • Discussed choosing significance and fold-change thresholds
  • Summarized our differential expression results
  • Generated a volcano plot for our differential expression results

Next, we’ll learn how to access …


Sources



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
LS0tCnRpdGxlOiAiREUgVmlzdWFsaXphdGlvbiIKYXV0aG9yOiAiVU0gQmlvaW5mb3JtYXRpY3MgQ29yZSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgICAgICAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgICAgaW5jbHVkZXM6CiAgICAgICAgICAgICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sCiAgICAgICAgICAgIHRoZW1lOiBwYXBlcgogICAgICAgICAgICB0b2M6IHRydWUKICAgICAgICAgICAgdG9jX2RlcHRoOiA0CiAgICAgICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICAgICAgICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICAgICAgICAgIG1hcmtkb3duOiBHRk0KICAgICAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHksIHRkIHsKICAgZm9udC1zaXplOiAxOHB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDEycHg7Cn0KcHJlIHsKICBmb250LXNpemU6IDEycHgKfQo8L3N0eWxlPgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0Kc291cmNlKCIuLi9iaW4vY2h1bmstb3B0aW9ucy5SIikKa25pdHJfZmlnX3BhdGgoIjExLSIpCmBgYAoKSW4gdGhpcyBtb2R1bGUsIHdlIHdpbGwgbGVhcm46CgoqIEhvdyB0byBjaG9vc2UgdGhyZXNob2xkcyBmb3IgY2FsbGluZyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMKKiBIb3cgdG8gZ2VuZXJhdGUgY29tbW9uIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHZpc3VhbGl6YXRpb25zCgo8YnI+CgpgYGB7ciBNb2R1bGVzLCBldmFsPVRSVUUsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkobWF0cml4U3RhdHMpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCiMgbG9hZCgicmRhdGEvUnVubmluZ0RhdGEuUkRhdGEiKQoKcmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbCA9IHJlc3VsdHMoZGRzX2JhdGNoX2ZpdHRlZCwgbmFtZSA9ICdjb25kaXRpb25fZGVmaWNpZW50X3ZzX2NvbnRyb2wnKQpgYGAKCiMgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gV29ya2Zsb3cgey51bmxpc3RlZCAudW5udW1iZXJlZH0KCkhlcmUgd2Ugd2lsbCBnZW5lcmF0ZSBzdW1tYXJ5IGZpZ3VyZXMgZm9yIG91ciByZXN1bHRzIGFuZCBhbm5vdGF0ZSBvdXIgREUgdGFibGVzLgoKIVtdKC4vaW1hZ2VzL3dheWZpbmRlci93YXlmaW5kZXItREVWaXN1YWxpemF0aW9ucy5wbmcpe3dpZHRoPTc1JX0KCi0tLQoKIyBTdW1tYXJpemluZyBERSByZXN1bHRzCgpQYXJ0IG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIGlzIGdlbmVyYXRpbmcgdmlzdWFsaXphdGlvbnMgYW5kIHN1bW1hcnkgdGFibGVzIHRvIHNoYXJlIG91ciByZXN1bHRzLiBXaGlsZSB0aGUgREVTZXEyIHZpZ25ldHRlIHByb3ZpZGVzIFtleGFtcGxlcyBvZiBvdGhlciB2aXN1YWxpemF0aW9uc10oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2V4cGxvcmluZy1hbmQtZXhwb3J0aW5nLXJlc3VsdHMpLCBhIGNvbW1vbiB2aXN1YWxpemF0aW9uIHRvIHN1bW1hcml6ZSBERSBjb21wYXJpc29ucyBhcmUgW3ZvbGNhbm8gcGxvdHNdKGh0dHA6Ly9yZXNvdXJjZXMucWlhZ2VuYmlvaW5mb3JtYXRpY3MuY29tL21hbnVhbHMvY2xjZ2Vub21pY3N3b3JrYmVuY2gvNzUyL2luZGV4LnBocD9tYW51YWw9Vm9sY2Fub19wbG90c19pbnNwZWN0aW5nX3Jlc3VsdF9zdGF0aXN0aWNhbF9hbmFseXNpcy5odG1sKS4KCiMjIFRhYnVsYXIgREUgc3VtbWFyeQoKVG8gY3JlYXRlIG91ciBvd24gc3VtbWFyeSwgd2UgZmlyc3QgbmVlZCB0byBjaG9vc2UgdGhyZXNob2xkcy4gQSBzdGFuZGFyZCB0aHJlc2hvbGQgZm9yIHRoZSBhZGp1c3RlZCBwLXZhbHVlIGlzIGxlc3MgdGhhbiAwLjA1LiBBIHJlYXNvbmFibGUgdGhyZXNob2xkIGZvciBsaW5lYXIgZm9sZC1jaGFuZ2UgaXMgbGVzcyB0aGFuIC0xLjUgb3IgZ3JlYXRlciB0aGFuIDEuNSAod2hpY2ggY29ycmVzcG9uZHMgdG8gbG9nMiBmb2xkLWNoYW5nZSAtMC41ODUgYW5kIDAuNTg1LCByZXNwZWN0aXZlbHkpLiBJbmNsdWRpbmcgYSBmb2xkLWNoYW5nZSB0aHJlc2hvbGQgZW5zdXJlcyB0aGF0IHRoZSBERSBnZW5lcyBoYXZlIGEgcmVhc29uYWJsZSBlZmZlY3Qgc2l6ZSBhcyB3ZWxsIGFzIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZS4KCkxldCdzIHNldCB0aGVzZSB0aHJlc2hvbGRzIGFzIHZhcmlhYmxlcyB0byByZXVzZS4gVGhpcyBpcyBnZW5lcmFsbHkgZ29vZCBwcmFjdGljZSBiZWNhdXNlIGlmIHlvdSB3YW50IHRvIGNoYW5nZSB0aG9zZSB0aHJlc2hvbGRzIGxhdGVyIHRoZW4geW91IG9ubHkgaGF2ZSB0byBjaGFuZ2UgdGhlbSBpbiBvbmUgbG9jYXRpb24gb2YgeW91ciBzY3JpcHQsIHdoaWNoIGlzIGZhc3RlciBhbmQgY2FuIHJlZHVjZSB0aGUgcmlzayBvZiBlcnJvcnMgZnJvbSBtaXNzaW5nIHNvbWUgaW5zdGFuY2VzIGluIHlvdXIgY29kZS4KCmBgYHtyIFBsb3RTZXR1cDF9CmZjID0gMS41CmZkciA9IDAuMDUKYGBgCgo+ICMgTm90ZTogQ2hvb3NpbmcgdGhyZXNob2xkcyB7LnVubGlzdGVkIC51bm51bWJlcmVkfQo+Cj4gVGhyZXNob2xkaW5nIG9uIGFkanVzdGVkIHAtdmFsdWVzIDwgMC4wNSBpcyBhIHN0YW5kYXJkIHRocmVzaG9sZCwgYnV0IGRlcGVuZGluZyBvbiB0aGUgcmVzZWFyY2ggcXVlc3Rpb24gYW5kL29yIGhvdyB0aGUgcmVzdWx0cyB3aWxsIGJlIHVzZWQsIG90aGVyIHRocmVzaG9sZHMgbWlnaHQgYmUgcmVhc29uYWJsZS4KPgo+IFRoZXJlIGlzIGEgbmljZSBbQmlvc3RhciBwb3N0IHRoYXQgZGlzY3Vzc2VzIGNob29zaW5nIGFkanVzdGVkIHAtdmFsdWUgdGhyZXNob2xkc10oaHR0cHM6Ly93d3cuYmlvc3RhcnMub3JnL3AvMjA5MTE4LyksIGluY2x1ZGluZyBjYXNlcyB3aGVyZSBhIG1vcmUgcmVsYXhlZCB0aHJlc2hvbGQgbWlnaHQgYmUgYXBwcm9wcmlhdGUgYW5kIChzb21lIGhlYXRlZCkgZGlzY3Vzc2lvbiBvZiB0aGUgZGFuZ2VycyBvZiBhZGp1c3RpbmcgdGhlIGNob29zZW4gdGhyZXNob2xkIGFmdGVyIHJ1bm5pbmcgYW4gYW5hbHlzaXMuIEFkZGl0aW9uYWxseSwgdGhlcmUgaXMgYSBbRGFsbW9uIGV0IGFsIDIwMTJdKGh0dHBzOi8vYm1jYmlvaW5mb3JtYXRpY3MuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni8xNDcxLTIxMDUtMTMtUzItUzExKSBwYXBlciBhYm91dCBwLXZhbHVlIGFuZCBmb2xkLWNoYW5nZSB0aHJlc2hvbGRzIGZvciBtaWNyb2FycmF5IGRhdGEgdGhhdCBtYXkgaGVscCBwcm92aWRlIHNvbWUgY29udGV4dC4KCklmIHdlIHRoaW5rIGJhY2sgdG8gQ29tcHV0YXRpb25hbCBGb3VuZGF0aW9ucywgY29uZGl0aW9uYWwgc3RhdGVtZW50cyBjb3VsZCBhbGxvdyB1cyB0byBkZXRlcm1pbmUgdGhlIG51bWJlciBvZiBnZW5lcyB0aGF0IHBhc3Mgb3VyIHRocmVzaG9sZHMsIHdoaWNoIHdvdWxkIGJlIHVzZWZ1bCBmb3IgYW5ub3RhdGluZyBvdXIgcmVzdWx0cyB0YWJsZXMgYW5kIHBsb3RzLgoKPiAjIEV4ZXJjaXNlIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4gSG93IHdvdWxkIHdlIGlkZW50aWZ5IHRoZSBudW1iZXIgb2YgZ2VuZXMgd2l0aCBhZGp1c3RlZCBwLXZhbHVlcyA8IDAuMDUgYW5kIGEgZm9sZC1jaGFuZ2UgYWJvdmUgMS41IChvciBiZWxvdyAtMS41KSBpbiBvdXIgY29tcGFyaXNvbj8KCgo8ZGV0YWlscz4KPHN1bW1hcnk+U29sdXRpb248L3N1bW1hcnk+CgpIZXJlIGlzIG9uZSBwb3NzaWJsZSBhbnN3ZXI6CgpgYGB7ciBTdGF0U2lnR2VuZXMyfQpzdW0ocmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbCRwYWRqIDwgZmRyICYgYWJzKHJlc3VsdHNfZGVmaWNpZW50X3ZzX2NvbnRyb2wkbG9nMkZvbGRDaGFuZ2UpID49IGxvZzIoZmMpLCBuYS5ybSA9IFRSVUUpCmBgYAoKKipDaGVja3BvaW50Kio6ICpJZiB5b3Ugc2VlIHRoZSBzYW1lIG51bWJlciBvZiBERSBnZW5lcyB3aXRoIG91ciBjaG9vc2VuIHRocmVzaG9sZHMsIHBsZWFzZSBpbmRpY2F0ZSB3aXRoIGEgZ3JlZW4gY2hlY2suIE90aGVyd2lzZSB1c2UgYSByZWQgeC4qCgo8L2RldGFpbHM+Cjxicj4KCkxldCdzIG5vdyBjcmVhdGUgYSBuZXcgY29sdW1uIGluIGByZXN1bHRzX2RlZmljaWVudF92c19jb250cm9sYCB0byByZWNvcmQgdGhlIHNpZ25pZmljYW5jZSAiY2FsbCIgYmFzZWQgb24gdGhlc2UgdGhyZXNob2xkcy4gQW5kIGxldCdzIHNlcGFyYXRlIHRoZSBjYWxsIGJ5ICJVcCIgb3IgIkRvd24iLCBub3RpbmcgdGhhdCB0aGVzZSBhcmUgcmVsYXRpdmUgdG8gb3VyICJDYXNlIiBjb25kaXRpb24uIFRoZXJlIGFyZSBtYW55IHdheXMgdG8gYWNjb21wbGlzaCB0aGlzLCBidXQgdGhlIGZvbGxvd2luZyB3aWxsIHdvcms6IDwhLS0gbW9kaWZ5IHRvIHRpZHl2ZXJzZSBzeW50YXggJiBtb3ZlICJiYXNlIiBvcHRpb25zIHRvIGRyb3Bkb3duIG1lbnUgLS0+CgoKYGBge3IgREVDb2x1bW59CiMgY3JlYXRlIG5ldyBjb2x1bW4gY2FsbGVkICJjYWxsIgpyZXN1bHRzX2RlZmljaWVudF92c19jb250cm9sJGNhbGwgPSAnTlMnCmhlYWQocmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbCkKCiMgZGV0ZXJtaW5lIHRoZSAiVXAiIGFuZCAiRG93biIgaW5kaWNlczoKdXBfaWR4ID0gcmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbCRwYWRqIDwgZmRyICYgcmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbCRsb2cyRm9sZENoYW5nZSA+IGxvZzIoZmMpCmRvd25faWR4ID0gcmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbCRwYWRqIDwgZmRyICYgcmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbCRsb2cyRm9sZENoYW5nZSA8IC1sb2cyKGZjKQoKIyB1c2UgaW5kaWNlcyB0byBhc3NpZ24gdGhlIGNvcnJlY3QgIlVwIiBvciAiRG93biIgdmFsdWVzIHRvIHRoZSBjb3JyZWN0IGluZGljZXMsIGFuZCBsb29rIGF0IHRoZSBoZWFkIG9mIHRoZSByZXN1bHQ6CnJlc3VsdHNfZGVmaWNpZW50X3ZzX2NvbnRyb2wkY2FsbFt1cF9pZHhdID0gJ1VwJwpyZXN1bHRzX2RlZmljaWVudF92c19jb250cm9sJGNhbGxbZG93bl9pZHhdID0gJ0Rvd24nCmhlYWQocmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbCkKCiMgbW9kaWZ5IGNvbHVtbiB0byBiZSBmYWN0b3IgIHRvIG1ha2UgZm9yIGVhc2llciBwbG90dGluZwpyZXN1bHRzX2RlZmljaWVudF92c19jb250cm9sJGNhbGwgPSBmYWN0b3IocmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbCRjYWxsLCBsZXZlbHMgPSBjKCdVcCcsICdEb3duJywgJ05TJykpCmhlYWQocmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbCkKYGBgCgo+ICMgVGlwIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4gSXQgaXMgb2Z0ZW4gaGVscGZ1bCB0byBpbmNsdWRlIGNvZGUgbGlrZSB0aGlzIGluIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2VzIHNvIHRoZXJlIGlzIGEgY2xlYXJseSBsYWJlbGxlZCBjb2x1bW4gdGhhdCBtYWtlcyBzdWJzZXR0aW5nIGFuZCBzdW1tYXJpemluZyB0aGUgcmVzdWx0cyBlYXNpZXIuCgpOb3cgd2UgYXJlIGluIGEgcG9zaXRpb24gdG8gcXVpY2tseSBzdW1tYXJpemUgb3VyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHM6CgpgYGB7ciBUYWJsZUNhbGx9CiMgY2hlY2sgc3VtbWFyeSBvZiByZXN1bHRzCnRhYmxlKHJlc3VsdHNfZGVmaWNpZW50X3ZzX2NvbnRyb2wkY2FsbCkKYGBgCgpXZSBzZWUgcXVpY2tseSBob3cgbWFueSBnZW5lcyB3ZXJlICJVcCIgaW4gaXJvbiByZXBsZXRlLCBob3cgbWFueSB3ZXJlICJEb3duIiBpbiBpcm9uIHJlcGxldGUsIGFuZCBob3cgbWFueSB3ZXJlIG5vdCBzaWduaWZpY2FudC4KCioqQ2hlY2twb2ludCoqOiAqSWYgeW91IHN1Y2Nlc3NmdWxseSBhZGRlZCB0aGUgYGNhbGxgIGNvbHVtbiBhbmQgZ290IHRoZSBzYW1lIHRhYmxlIHJlc3VsdCBhcyBhYm92ZSwgcGxlYXNlIGluZGljYXRlIHdpdGggYSBncmVlbiBjaGVjay4gT3RoZXJ3aXNlIHVzZSBhIHJlZCB4LioKCiMjIFZpc3VhbCBERSBzdW1tYXJ5CgpBcyBkZXNjcmliZWQgYnkgdGhlIFtHYWxheHkgcHJvamVjdF0oaHR0cHM6Ly9nYWxheHlwcm9qZWN0LmdpdGh1Yi5pby90cmFpbmluZy1tYXRlcmlhbC90b3BpY3MvdHJhbnNjcmlwdG9taWNzL3R1dG9yaWFscy9ybmEtc2VxLXZpei13aXRoLXZvbGNhbm9wbG90L3R1dG9yaWFsLmh0bWwpLCBhIHZvbGNhbm8gcGxvdCBpcyBhIHR5cGUgb2Ygc2NhdHRlcnBsb3QgdGhhdCBzaG93cyBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UgKGFkanVzdGVkIHAtdmFsdWUpIHZlcnN1cyBlZmZlY3Qgc2l6ZSAoZm9sZCBjaGFuZ2UpLiBJbiBhIHZvbGNhbm8gcGxvdCwgdGhlIG1vc3QgdXByZWd1bGF0ZWQgZ2VuZXMgYXJlIHRvd2FyZHMgdGhlIHJpZ2h0LCB0aGUgbW9zdCBkb3ducmVndWxhdGVkIGdlbmVzIGFyZSB0b3dhcmRzIHRoZSBsZWZ0LCBhbmQgdGhlIG1vc3Qgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBnZW5lcyBhcmUgdG93YXJkcyB0aGUgdG9wLgoKTGV0J3MgY29lcmNlIHRoZSBgRGF0YUZyYW1lYCB3aGljaCB3YXMgcmV0dXJuZWQgYnkgYERFU2VxMjo6cmVzdWx0cygpYCBpbnRvIGEgYHRpYmJsZWAgaW4gYW50aWNpcGF0aW9uIG9mIHVzaW5nIHRoZSBgZ2dwbG90MmAgbGlicmFyeSB0byBwbG90LiBXZSdyZSBhbHNvIGdvaW5nIHRvIG1vZGlmeSBvdXIgcmVzdWx0cyB0YWJsZSBzbyB0aGF0IHRoZSByb3cgbmFtZXMgYmVjb21lIGEgc2VwYXJhdGUgY29sdW1uLCBhbmQgc28gdGhhdCBpdCdzIG9yZGVyZWQgYnkgYWRqdXN0ZWQgcC12YWx1ZS4KCmBgYHtyIFBsb3RTZXR1cDJ9CiMgVXNlIHRoZSByb3duYW1lcyBhcmd1bWVudCB0byBjcmVhdGUgYSBuZXcgY29sdW1uIG9mIGdlbmUgSURzCiMgQWxzbyBhcnJhbmdlIGJ5IGFkanVzdGVkIHAtdmFsdWUKcmVzdWx0c19mb3JQbG90ID0gYXNfdGliYmxlKHJlc3VsdHNfZGVmaWNpZW50X3ZzX2NvbnRyb2wsIHJvd25hbWVzID0gJ2lkJykgJT4lIGFycmFuZ2UocGFkaikKYGBgCgpMZXQncyBzdGFydCB3aXRoIGEgc2ltcGxlIHZvbGNhbm8gcGxvdCB0aGF0IHBsb3RzIHRoZSBgbG9nMkZvbGRDaGFuZ2VgIG9uIHRoZSB4LWF4aXMsIGFuZCBgLWxvZzEwKHBhZGopYCBvbiB0aGUgeS1heGlzLCBhZGRpbmcgY3VzdG9tIGxhYmVscyB0byB0aGUgcGxvdCB3aXRoIHRoZSBgbGFicygpYCBmdW5jdGlvbjoKCmBgYHtyIFZvbGNhbm9QbG90Mn0KIyBBZGQgcGxvdCBsYWJlbHMgYW5kIGNoYW5nZSB0aGUgdGhlbWUgLSBzYXZlIHRoZSBwbG90IGFzIG9iamVjdCBgcGAKcCA9IGdncGxvdChyZXN1bHRzX2ZvclBsb3QsIGFlcyh4ID0gbG9nMkZvbGRDaGFuZ2UsIHkgPSAtbG9nMTAocGFkaikpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgdGhlbWVfYncoKSArCiAgICBsYWJzKAogICAgICAgIHRpdGxlID0gJ1ZvbGNhbm8gUGxvdCcsCiAgICAgICAgc3VidGl0bGUgPSAnY29udHJvbCB2cyBkZWZpY2llbnQnLAogICAgICAgIHggPSAnbG9nMiBmb2xkLWNoYW5nZScsCiAgICAgICAgeSA9ICctbG9nMTAgRkRSJwogICAgKQpwCmBgYAoKV2hhdCBpZiB3ZSBub3cgYWRkZWQgc29tZSB2aXN1YWwgZ3VpZGVzIHRvIGluZGljYXRlIHdoZXJlIHRoZSBzaWduaWZpY2FudCBnZW5lcyBhcmU/IFdlIGNhbiB1c2UgdGhlIGBnZW9tX3ZsaW5lKClgIGFuZCBgZ2VvbV9obGluZSgpYCBmdW5jdGlvbnMgdG8gYWNjb21wbGlzaCB0aGlzOgoKYGBge3IgVm9sY2Fub1Bsb3QzfQojIEFkZCB0aHJlc2hvbGQgbGluZXMKcDEgPSBwICsKICAgIGdlb21fdmxpbmUoCiAgICAgICAgeGludGVyY2VwdCA9IGMoMCwgLWxvZzIoZmMpLCBsb2cyKGZjKSksCiAgICAgICAgbGluZXR5cGUgPSBjKDEsIDIsIDIpKSArCiAgICBnZW9tX2hsaW5lKAogICAgICAgIHlpbnRlcmNlcHQgPSAtbG9nMTAoZmRyKSwKICAgICAgICBsaW5ldHlwZSA9IDIpCnAxCmBgYAoKRmluYWxseSwgd2h5IG5vdCBjb2xvciB0aGUgcG9pbnRzIGJ5IHRoZWlyIHNpZ25pZmljYW5jZSBzdGF0dXM/IFdlIGFscmVhZHkgY3JlYXRlZCB0aGUgYGNhbGxgIGNvbHVtbiB0aGF0IGhhcyB0aGUgY29ycmVjdCB2YWx1ZXMuIEluIHRoaXMgY2FzZSB3ZSBjYW4gZ2V0IGF3YXkgd2l0aCBhZGRpbmcgYGdlb21fcG9pbnQoKWAgdG8gb3VyIGV4aXN0aW5nIHBsb3QgYW5kIHNwZWNpZnlpbmcgdGhlIGNvcnJlY3QgYWVzdGhldGljLCBiZWZvcmUgY3VzdG9taXppbmcgdG8gaGF2ZSBhIG5pY2VyIGNvbG9yIHNjaGVtZToKCmBgYHtyIFZvbGNhbm9QbG90NH0KIyBtYXAgY29sb3IgdG8gdGhlIGBjYWxsYCBjb2x1bW4gZnJvbSBvdXIgaW5wdXQgZGF0YQpwMiA9IHAxICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjYWxsKSkKcDIKCiMgYWRqdXN0IG91ciBjb2xvciBzY2hlbWUKcDMgPSBwMiArIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gJycsIHZhbHVlcz1jKCcjQjMxQjIxJywgICcjMTQ2NUFDJywgJ2RhcmtncmF5JykpCnAzCmBgYAoKRm9yIGFkZGl0aW9uYWwgdmlzdWFsaXphdGlvbnMgZm9yIG91ciBERSByZXN1bHRzLCB3ZSBpbmNsdWRlZCBzb21lIGV4YW1wbGUgY29kZSBpbiB0aGUgW0JvbnVzIENvbnRlbnQgbW9kdWxlXShodHRwczovL3VtaWNoLWJyY2YtYmlvaW5mLmdpdGh1Yi5pby8yMDIzLTAzLTEzLXVtaWNoLXJuYXNlcS1kZW15c3RpZmllZC9odG1sL1JfYm9udXNfY29udGVudC5odG1sKSBhbmQgdGhpcyBbSEJDIHR1dG9yaWFsXShodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wNl9ER0VfdmlzdWFsaXppbmdfcmVzdWx0cy5odG1sKSBhbHNvIGluY2x1ZGVzIHNvbWUgbmljZSBleGFtcGxlcy4KCgojIyMgQWRkaW5nIGdlbmUgc3ltYm9scyB0byBvdXIgZGF0YQoKV2hhdCBpZiB3ZSB3YW50ZWQgdG8gaW5jbHVkZSBsYWJlbHMgZm9yIHNvbWUgb2YgdGhlIG1vc3QgdXAgYW5kL29yIGRvd24gcmVndWxhdGVkIGdlbmVzIGZyb20gb3VyIGNvbXBhcmlzb25zPyBXZSBjYW4gZG8gdGhpcyBieSB1c2luZyBleHRlcm5hbCBpbmZvcm1hdGlvbiB0aGF0IG1hcHMgdGhlIEVOU0VNQkwgaWRzIGZyb20gb3VyIHJlZmVyZW5jZSBmaWxlcy9jb3VudCB0YWJsZXMgdG8gY29ycmVzcG9uZGluZyBnZW5lIHN5bWJvbHMuIFdlJ3ZlIHByb3ZpZGVkIGEgdGFibGUgd2l0aCB0aGVzZSBtYXBwaW5ncyBidXQgaW4gdGhlIG5leHQgbW9kdWxlLCB3ZSdsbCBzaG93IGhvdyB0byBhY2Nlc3MgcmVzb3VyY2VzIGFuZCBnZW5lcmF0ZSB0aGlzIGtpbmQgb2YgbWFwcGluZyB0YWJsZS4gCgpgYGB7ciBHZW5lS2V5LCBlY2hvID0gRkFMU0UsIGV2YWwgPSBUUlVFfQppZF9tYXBwaW5nID0gcmVhZC50YWJsZSgiLi4vZGF0YS9SX2RhdGEvRU5TRU1CTF9pZF9tYXBwaW5ncy5jc3YiLAogICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIsIiwKICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFKQpgYGAKCmBgYHtyIEdlbmVLZXkyLCBldmFsID0gRkFMU0V9CiMgcmVhZCBpbiB0YWJsZSBtYXBwaW5nIEVOU0VNQkwgaWRzIHRvIGdlbmUgc3ltYm9scwppZF9tYXBwaW5nID0gcmVhZC50YWJsZSgiZGF0YS9FTlNFTUJMX2lkX21hcHBpbmdzLmNzdiIsCiAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiwiLAogICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUpCmhlYWQoaWRfbWFwcGluZykKYGBgCgpXZSB3YW50IHRvIG1hdGNoIHRoZSBgaWRgIGNvbHVtbiBvZiBgcmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbGAgdG8gdGhlIGBlbnNlbWJsX2dlbmVfaWRgIGNvbHVtbiBvZiBgaWRfbWFwcGluZ2AsIGFuZCBvbmNlIHRoYXQgbWF0Y2ggaXMgZm91bmQsIHdlIHdhbnQgdG8gZXh0cmFjdCB0aGUgYGV4dGVybmFsX2dlbmVfbmFtZWAgY29sdW1uIG9mIGBpZF9tYXBwaW5nYCB0byBnZXQgdGhlIGdlbmUgc3ltYm9sLiBOZXh0LCBsb29rIGF0IHRoZSBkb2N1bWVudGF0aW9uIGZvciBgZHBseXI6OmxlZnRfam9pbigpYCBhbmQgbWVyZ2UgdGhlIGBpZF9tYXBwaW5nYCB0YWJsZSBpbnRvIHRoZSBgcmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbGAgdGFibGUgb24gdGhlIGNvbHVtbnMgYGVuc2VtYmxfZ2VuZV9pZGAgYW5kIGBleHRlcm5hbF9nZW5lX25hbWVgLgpgYGB7ciBsZWZ0X2pvaW59CiMgY2hlY2sgb3VyIHN0YXJ0aW5nIGRhdGEKaGVhZChyZXN1bHRzX2RlZmljaWVudF92c19jb250cm9sKQoKIyB1c2UgYGpvaW5gIHRvIGFkZCBnZW5lIHN5bWJvbHMgdG8gcm93IHdpdGggbWF0Y2hpbmcgRU5TRU1CTCBpZApyZXN1bHRzX2RlZmljaWVudF92c19jb250cm9sX2Fubm90YXRlZCA9IGFzX3RpYmJsZShyZXN1bHRzX2RlZmljaWVudF92c19jb250cm9sLCByb3duYW1lcyA9ICJpZCIpICU+JQogICAgbGVmdF9qb2luKGlkX21hcHBpbmcsIGJ5ID0gYygnaWQnID0gJ2Vuc2VtYmxfZ2VuZV9pZCcpKQpoZWFkKHJlc3VsdHNfZGVmaWNpZW50X3ZzX2NvbnRyb2xfYW5ub3RhdGVkKQpgYGAKCldlIGNhbiB1c2Ugc29tZSBvZiB0aGUgYHRpZHl2ZXJzZWAgZnVuY3Rpb25zIHdlJ3ZlIGVuY291bnRlcmVkIHByZXZpb3VzbHkgdG8gcmVuYW1lIHRoZSBgZXh0ZXJuYWxfZ2VuZV9uYW1lYCBjb2x1bW4gdG8gYHN5bWJvbGAgYW5kIHRvIG1vdmUgaXQgaW50byB0aGUgc2Vjb25kIGNvbHVtbiBwb3NpdGlvbj8gSGludDogQmVjYXVzZSBvZiB0aGUgb3JkZXIgb2YgdGhlIHBhY2thZ2VzIHdlIG1heSBoYXZlIGxvYWRlZCwgd2UnbGwgdXNlIGBkcGx5cjo6cmVuYW1lKClgIGFuZCBgZHBseXI6OnNlbGVjdCgpYCBpbnN0ZWFkIG9mIGp1c3QgdGhlIGBzZWxlY3QoKWAgZnVuY3Rpb24uIFdlIGNhbiBkaXNjdXNzIHRoaXMgaW4gYSBtb21lbnQuCgpgYGB7ciByZW5hbWVfcmVhcnJhbmdlfQojIHVzZSBkcGx5ciBmdW5jdGlvbnMgdG8gcmVuYW1lIGNvbHVtbnMgYW5kIHJlb3JnYW5pemUgdGFibGUgdG8gbWFrZSBtb3JlIHJlYWRhYmxlCnJlc3VsdHNfZGVmaWNpZW50X3ZzX2NvbnRyb2xfYW5ub3RhdGVkID0gcmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbF9hbm5vdGF0ZWQgJT4lCiAgZHBseXI6OnJlbmFtZSgnc3ltYm9sJyA9ICdleHRlcm5hbF9nZW5lX25hbWUnKSAlPiUKICBkcGx5cjo6c2VsZWN0KGlkLCBzeW1ib2wsIGV2ZXJ5dGhpbmcoKSkKaGVhZChyZXN1bHRzX2RlZmljaWVudF92c19jb250cm9sX2Fubm90YXRlZCkKYGBgCgpBbmQgbm93IHdlIGhhdmUgb3VyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHMgYW5ub3RhdGVkIHdpdGggZ2VuZSBzeW1ib2xzLCB3aGljaCBjYW4gaGVscCBpbiB0aGUgaW50ZXJwcmV0YXRpb24gb2YgdGhlIHJlc3VsdHMsIGFuZCBjYW4gYmUgdXNlZCBpbiBkb3duc3RyZWFtIGFuYWx5c2lzIHN1Y2ggYXMgZnVuY3Rpb25hbCBhbmFseXNpcy4gQWx0aG91Z2ggd2UgZG9uJ3QgaGF2ZSB0aW1lIHRvIHJ1biB0aGlzIHRvZ2V0aGVyLCB3ZSBjYW4gYWxzbyB1c2UgdGhpcyB0YWJsZSB0byBsYWJlbCBnZW5lcyBvZiBpbnRlcmVzdCBpbiBvdXIgdm9sY2FubyBwbG90LgoKPiAjIEV4ZXJjaXNlIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4gTm93IHRoYXQgd2UgYW5ub3RhdGVkIG91ciBERSByZXN1bHRzIHRvIGFkZCBnZW5lIHN5bWJvbHMsIHdoYXQgc3RlcHMgd291bGQgd2UgdGFrZSB0byBhZGQgbGFiZWxzIHRvIG91ciB2b2xjYW5vIHBsb3RzIGZvciB0aGUgdG9wIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBpbiBvdXIgY29tcGFyaXNvbj8KCgojIFN1bW1hcnkKCkluIHRoaXMgc2VjdGlvbiwgd2U6CgoqIERpc2N1c3NlZCBjaG9vc2luZyBzaWduaWZpY2FuY2UgYW5kIGZvbGQtY2hhbmdlIHRocmVzaG9sZHMKKiBTdW1tYXJpemVkIG91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzCiogR2VuZXJhdGVkIGEgdm9sY2FubyBwbG90IGZvciBvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0cwoKTmV4dCwgd2UnbGwgbGVhcm4gaG93IHRvIGFjY2VzcyAuLi4gCgoKLS0tCgojIFNvdXJjZXMKCiogSEJDIERHRSB0cmFpbmluZyBtb2R1bGUsIHBhcnQgMTogaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDRfREdFX0RFU2VxMl9hbmFseXNpcy5odG1sCiogSEJDIERHRSB0cmFpbmluZyBtb2R1bGUsIHBhcnQgMjogaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDVfREdFX0RFU2VxMl9hbmFseXNpczIuaHRtbAoqIERFU2VxMiB2aWduZXR0ZTogaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2RpZmZlcmVudGlhbC1leHByZXNzaW9uLWFuYWx5c2lzCgoKLS0tCgpgYGB7ciBXcml0ZU91dC5SRGF0YSwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEhpZGRlbiBjb2RlIGJsb2NrIHRvIHdyaXRlIG91dCBkYXRhIGZvciBrbml0dGluZwojIHNhdmUuaW1hZ2UoZmlsZSA9ICJyZGF0YS9SdW5uaW5nRGF0YV9GdWxsLlJEYXRhIikKYGBgCgoKLS0tCgpUaGVzZSBtYXRlcmlhbHMgaGF2ZSBiZWVuIGFkYXB0ZWQgYW5kIGV4dGVuZGVkIGZyb20gbWF0ZXJpYWxzIGxpc3RlZCBhYm92ZS4gVGhlc2UgYXJlIG9wZW4gYWNjZXNzIG1hdGVyaWFscyBkaXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIGxpY2Vuc2UgKENDIEJZIDQuMCldKGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8pLCB3aGljaCBwZXJtaXRzIHVucmVzdHJpY3RlZCB1c2UsIGRpc3RyaWJ1dGlvbiwgYW5kIHJlcHJvZHVjdGlvbiBpbiBhbnkgbWVkaXVtLCBwcm92aWRlZCB0aGUgb3JpZ2luYWwgYXV0aG9yIGFuZCBzb3VyY2UgYXJlIGNyZWRpdGVkLgoKPGJyLz4KPGJyLz4KPGhyLz4KfCBbUHJldmlvdXMgbGVzc29uXShNb2R1bGUxMF9ERUNvbXBhcmlzb25zLmh0bWwpIHwgW1RvcCBvZiB0aGlzIGxlc3Nvbl0oI3RvcCkgfCBbTmV4dCBsZXNzb25dKE1vZHVsZTEyX0RFQW5ub3RhdGlvbnMuaHRtbCkgfAp8IDotLS0gfCA6LS0tLTogfCAtLS06IHw=