Goals

In this tutorial, you will learn how to:

  1. Import tabular data from CSV files.
  2. Import tabular data from Excel files.
  3. Clean and manipulate tables with the tidyverse.

1. Setup

pacman::p_load(tidyverse, readxl, readr, janitor, ggrepel, gridExtra, ggthemes, scales, writexl)

source("https://github.com/koundy/ggplot_theme_Publication/raw/refs/heads/master/ggplot_theme_Publication-2.R")

Import CSV files

Use readr::read_csv() for fast and friendly CSV import. The separator (delimiter between columns) is automatically detected as a comma. However you can specify another delimiter in the options if needed.

DGE <- read_csv("DGE.csv")

head(DGE)

Example on how to specify a different delimiter (semicolon):

#DGE <- read_delim("DGE.csv", delim = ";")

A file can also contain the first lines with comments which you want to skip. Then you can use the skip argument to ignore those lines:

DGE_skip <- read_csv("weirdDGE.csv", skip = 3)

Import Excel files

Use readxl::read_excel() to import Excel files. You can specify the sheet name or number, and optionally a range of cells.

DGE_CaseControl <- read_excel("DGEtables.xlsx", sheet = "CaseControl")

head(DGE_CaseControl)

Read a specific range of cells in Excel

DGE_subset <- read_excel("DGEtables.xlsx", 
                        sheet = "CaseControl",
                        range = "A1:F50")

head(DGE_subset)

Read all sheets and combine

A more advanced example merging all sheets from excel into one table, and naming the source sheet in a new column:

#name of each sheet in excel file
sheet_names <- excel_sheets("DGEtables.xlsx")
cat("sheet names are ", paste(sheet_names, collapse = ", "), "\n")
sheet names are  CaseExposed, CaseControl 
#takes sheet names and run read_excel for each sheet, 
#we use map_fdr to combine all the tables into one, 
#and we add a new column with the sheet name to keep 
#track of the source of each row
DGE_all <- sheet_names %>%
    set_names() %>%
    map_dfr(
        ~read_excel("DGEtables.xlsx", sheet = .x),
        .id = "sheet"
    )

summary(DGE_all)
    sheet               ...1               logFC         
 Length:13344       Length:13344       Min.   :-15.2190  
 Class :character   Class :character   1st Qu.: -2.4412  
 Mode  :character   Mode  :character   Median : -0.0223  
                                       Mean   : -0.1515  
                                       3rd Qu.:  2.1338  
                                       Max.   : 14.8596  
     logCPM              F               PValue       
 Min.   :-0.6586   Min.   : 0.0000   Min.   :0.00000  
 1st Qu.: 3.2794   1st Qu.: 0.2718   1st Qu.:0.06444  
 Median : 4.7527   Median : 1.2249   Median :0.26843  
 Mean   : 4.7831   Mean   : 2.2634   Mean   :0.34949  
 3rd Qu.: 6.2484   3rd Qu.: 3.4201   3rd Qu.:0.60215  
 Max.   :17.0813   Max.   :43.1210   Max.   :1.00000  
      FDR              diffexpr           geneType        
 Min.   :0.0000004   Length:13344       Length:13344      
 1st Qu.:0.2617545   Class :character   Class :character  
 Median :0.5351169   Mode  :character   Mode  :character  
 Mean   :0.5344405                                        
 3rd Qu.:0.8014345                                        
 Max.   :1.0000000                                        

Core tidyverse table manipulation and piping

Those are operations to manipulate tables. You can chain them together with the pipe operator (%>%). What does the pipe do?

Look below: we define significant_DGE as a sequence of piped operations. Their order is from the first to the last, and in tidyverse names recall very clearly what the operations do. The output of each operation is piped into the next. Here we

glimpse will give an overview of the tibble’s structure and the first few values.

significant_DGE <- DGE %>%
  rename(gene_name = 1) %>% #rename column 1 to gene_name
  janitor::clean_names() %>% #clean up column names
  mutate(gene_type = factor(gene_type)) %>% #factorize categories of gene types
    select(gene_name, log_fc, fdr, gene_type) %>% #select only some columns of interest
    filter(fdr < .05 & gene_type=="protein-coding") %>% #filter by fdr and gene type
    arrange(desc(fdr)) #arrange by descending pvalue

glimpse(significant_DGE)
Rows: 47
Columns: 4
$ gene_name <chr> "VPS39", "GLT1D1", "SDHAF3", "PI3", "NES", …
$ log_fc    <dbl> 11.886819, 12.711685, 11.179818, 11.815255,…
$ fdr       <dbl> 0.04961619, 0.04961619, 0.04961619, 0.04961…
$ gene_type <fct> protein-coding, protein-coding, protein-cod…

Create new columns with mutate

You can also create a new column with mutte. Let’s say we want to define a DGE tibble like above, but without filtering. Instead we create a category saying SIGN(.001), SIGN(.01), SIGN(.05) or NOT.SIGN, all depending on the pvalue, and we also want that the logfold change is at least above 1 or below -1.

You can use mutate to create a new column based on those criteria:

extended_DGE <- DGE %>%
  rename(gene_name = 1) %>% #rename column 1 to gene_name
  janitor::clean_names() %>% #clean up column names
  mutate(gene_type = factor(gene_type)) %>% #factorize categories of gene types
    select(gene_name, log_fc, fdr, gene_type) %>% #select only some columns of interest
  mutate( significance_level = case_when(
    fdr < .001 & abs(log_fc) > 1 ~ "SIGN(.001)",
    fdr < .01 & abs(log_fc) > 1 ~ "SIGN(.01)",
    fdr < .05 & abs(log_fc) > 1 ~ "SIGN(.05)",
    TRUE ~ "NOT.SIGN"
  )) %>% #create a new column with the significance level
  mutate( significance_level = factor(significance_level)) %>%
  arrange(desc(fdr)) #arrange by descending pvalue

glimpse(extended_DGE)
Rows: 6,672
Columns: 5
$ gene_name          <chr> "MIR1184-1", "SLC16A7", "TSPAN33",…
$ log_fc             <dbl> 0.000000000, -0.001326973, -0.0009…
$ fdr                <dbl> 1.0000000, 0.9997601, 0.9997601, 0…
$ gene_type          <fct> ncRNA, protein-coding, protein-cod…
$ significance_level <fct> NOT.SIGN, NOT.SIGN, NOT.SIGN, NOT.…

Summarize by group

You can summarize by group and get group statistics. For example the number of genes in each gene type and their median fdr and log_fc.

summary_DGE <- extended_DGE %>%
    group_by(gene_type) %>%
    summarise(
        n = n(),
        median_lfc = median(log_fc, na.rm = TRUE),
        median_fdr = median(fdr, na.rm = TRUE),
        .groups = "drop"
    )

summary_DGE

Plotting with ggplot2

You can also plot your data with ggplot2, which is part of the tidyverse. For example, a scatter plot of log fold change vs fdr, colored by gene type:

ggplot(extended_DGE, aes(x = log_fc, y = -log10(fdr), color = gene_type)) +
    geom_point(size=3) +
    theme_minimal() +
    labs(title = "DGE Scatter Plot", x = "Log Fold Change", y = "-Log10(FDR)")

ggplot takes the dataframe/tibble as input and adds layers of the plot using the symbol +. There are a lot of ways to customize the plot or do other types of plots.

We can add a column where we write the name of the genes which are differentially expressed. We can do this with the mutate function and case_when. Then we add to the plot a layer of text.

extended_DGE <- extended_DGE %>%
  mutate( plot_label = case_when(
    significance_level ==  "NOT.SIGN" ~ NA,
    TRUE ~ gene_name
    ) )

glimpse(extended_DGE)
Rows: 6,672
Columns: 6
$ gene_name          <chr> "MIR1184-1", "SLC16A7", "TSPAN33",…
$ log_fc             <dbl> 0.000000000, -0.001326973, -0.0009…
$ fdr                <dbl> 1.0000000, 0.9997601, 0.9997601, 0…
$ gene_type          <fct> ncRNA, protein-coding, protein-cod…
$ significance_level <fct> NOT.SIGN, NOT.SIGN, NOT.SIGN, NOT.…
$ plot_label         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA…
ggplot(extended_DGE, aes(x = log_fc, y = -log10(fdr), color = gene_type)) +
    geom_point() +
  ggrepel::geom_text_repel(aes(label = plot_label), size = 3, ) + #add labels to significant genes
    theme_minimal() +
  geom_hline(yintercept=-log10(0.05)) + geom_vline(xintercept=c(1,-1)) +    #vertical line ggplot
    labs(title = "DGE Scatter Plot", x = "Log Fold Change", y = "-Log10(FDR)")

Themes

You can change theme for adapting the appearence. All standard themes are here: https://ggplot2.tidyverse.org/reference/ggtheme.html. For example, it is popular to use the gray theme


<!-- rnb-plot-begin eyJoZWlnaHQiOjQzMi42MzI5LCJ3aWR0aCI6NzAwLCJkcGkiOi0xLCJzaXplX2JlaGF2aW9yIjowLCJjb25kaXRpb25zIjpbWzEsIlx1MDAxYlsxbVx1MDAxYlszM21XYXJuaW5nXHUwMDFiWzM5bTpcdTAwMWJbMjJtXG5cdTAwMWJbMzg7NTsyMzJtUmVtb3ZlZCA2NjE5IHJvd3MgY29udGFpbmluZyBtaXNzaW5nIHZhbHVlcyBvciB2YWx1ZXMgb3V0c2lkZSB0aGUgc2NhbGUgcmFuZ2VcbihgZ2VvbV90ZXh0X3JlcGVsKClgKS5cdTAwMWJbMzltXG5cbiJdXX0= -->

<img src=\data:image/png;base64

You can do nicer themes using the ones developed here https://github.com/koundy/ggplot_theme_Publication, where someone has developed its own themes. We have imported them at the beginning of this notebook. For example you can use a theme developed for publications (nicer sizes and font of text and nicer legend).


<!-- rnb-plot-begin eyJoZWlnaHQiOjQzMi42MzI5LCJ3aWR0aCI6NzAwLCJkcGkiOi0xLCJzaXplX2JlaGF2aW9yIjowLCJjb25kaXRpb25zIjpbWzEsIlx1MDAxYlsxbVx1MDAxYlszM21XYXJuaW5nXHUwMDFiWzM5bTpcdTAwMWJbMjJtXG5cdTAwMWJbMzg7NTsyMzJtUmVtb3ZlZCA2NjE5IHJvd3MgY29udGFpbmluZyBtaXNzaW5nIHZhbHVlcyBvciB2YWx1ZXMgb3V0c2lkZSB0aGUgc2NhbGUgcmFuZ2VcbihgZ2VvbV90ZXh0X3JlcGVsKClgKS5cdTAwMWJbMzltXG5cbiJdXX0= -->

<img src=\data:image/png;base64

Or combine publication theme with a color palette for discrete variables (gene type) from the same author. You can look at the webpage for the themes to see more examples.


<!-- rnb-plot-begin eyJoZWlnaHQiOjQzMi42MzI5LCJ3aWR0aCI6NzAwLCJkcGkiOi0xLCJzaXplX2JlaGF2aW9yIjowLCJjb25kaXRpb25zIjpbWzEsIlx1MDAxYlsxbVx1MDAxYlszM21XYXJuaW5nXHUwMDFiWzM5bTpcdTAwMWJbMjJtXG5cdTAwMWJbMzg7NTsyMzJtUmVtb3ZlZCA2NjE5IHJvd3MgY29udGFpbmluZyBtaXNzaW5nIHZhbHVlcyBvciB2YWx1ZXMgb3V0c2lkZSB0aGUgc2NhbGUgcmFuZ2VcbihgZ2VvbV90ZXh0X3JlcGVsKClgKS5cdTAwMWJbMzltXG5cbiJdXX0= -->

<img src=\data:image/png;base64

Still not enough? The package ggthemes has even more ways of combining color palettes and themes. Look here https://github.com/jrnold/ggthemes where the author makes a lot of examples and try one of them!

saving ggplots

You can save a ggplot with the dedicated command, but to do that you need to assign the plot to a variable. For example:

volcano_plot <- ggplot(extended_DGE, aes(x = log_fc, y = -log10(fdr), color = gene_type)) +
    geom_point() +
  ggrepel::geom_text_repel(aes(label = plot_label), size = 3, ) + #add labels to significant genes
    theme_gray() +
  geom_hline(yintercept=-log10(0.05)) + geom_vline(xintercept=c(1,-1)) +    #vertical line ggplot
    labs(title = "DGE Scatter Plot", x = "Log Fold Change", y = "-Log10(FDR)")

ggsave(filename = "./lifeExp.png", plot = volcano_plot, width = 12, height = 10, dpi = 300, units = "cm")

Some other plots: barplots, boxplots, density plots, …

There is a wide range of plot types you can do with ggplot, and it really depends on your data and what you want to visualize. For example, you can do a bar plot of the number of genes in each gene type:

ggplot(summary_DGE, aes(x = gene_type, y = log(n), fill = gene_type)) +
    geom_bar(stat = "identity") +
  scale_fill_Publication() + 
  theme_dark_blue()

Note that above we needed only one value per gene type to plot the bar. If we had more values per gene type, we would need to use stat = "summary" and specify a summary function (for example fun = "mean" or fun = "median"). For example, if we want to plot the median log fold change for each gene type:

ggplot(extended_DGE, aes(x = gene_type, y = log_fc, fill = gene_type)) +
    geom_bar(stat = "summary", fun = "median") +
  scale_fill_Publication() + 
  theme_dark_blue() +
#chnge x axis angle to 45
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Faceting

Faceting is a powerful way to create multiple plots based on a categorical variable. For example, we can facet the scatter plot by gene type:

ggplot(extended_DGE, aes(x = log_fc, y = -log10(fdr), color = gene_type)) +
    geom_point() +
  ggrepel::geom_text_repel(aes(label = plot_label)) +
    theme_minimal() +
  geom_hline(yintercept=-log10(0.05)) + geom_vline(xintercept=c(1,-1)) +    #vertical line ggplot
    labs(title = "DGE Scatter Plot", x = "Log Fold Change", y = "-Log10(FDR)") +
  facet_wrap(~ gene_type)

A density plot of log fold change by gene type:

ggplot(extended_DGE, aes(x = log_fc, fill = gene_type)) +
    geom_density(alpha = 0.5) +
  scale_fill_Publication() +
  theme_excel_new() +
  labs(title = "Density Plot of Log Fold Change\nby Gene Type", x = "Log Fold Change", y = "Density") 

Export cleaned results

# Export to CSV
write_csv(extended_DGE, "final_DGE.csv")

# Export to Excel (needs writexl package)
writexl::write_xlsx(extended_DGE, "final_DGE.xlsx")

Quick practice tasks

  1. Import one CSV or one Excel file from your own project.
  2. Use clean_names() and mutate() to standardize/cleanup columns
  3. apply some labels of interested, as in the example for significance
  4. make a plot you would like to see, for example a scatter plot or a bar plot, and customize it with themes and labels
  5. Export the final cleaned table

Troubleshooting tips

Some resources

I will just list a few things, as the internet is a wide ocean, and you might want to focus on few selected things.

LS0tDQp0aXRsZTogIkltcG9ydGluZyBUYWJ1bGFyIERhdGEgaW4gUiINCmF1dGhvcjogIlNhbXVlbGUgU29yYWdnaSINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCmVkaXRvcl9vcHRpb25zOg0KICBtYXJrZG93bjoNCiAgICB3cmFwOiA3Mg0KLS0tDQoNCiMjIEdvYWxzDQoNCkluIHRoaXMgdHV0b3JpYWwsIHlvdSB3aWxsIGxlYXJuIGhvdyB0bzoNCg0KMS4gSW1wb3J0IHRhYnVsYXIgZGF0YSBmcm9tIENTViBmaWxlcy4NCjIuIEltcG9ydCB0YWJ1bGFyIGRhdGEgZnJvbSBFeGNlbCBmaWxlcy4NCjMuIENsZWFuIGFuZCBtYW5pcHVsYXRlIHRhYmxlcyB3aXRoIHRoZSB0aWR5dmVyc2UuDQoNCiMjIDEuIFNldHVwDQoNCmBgYHtyIHNldHVwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcGFjbWFuOjpwX2xvYWQodGlkeXZlcnNlLCByZWFkeGwsIHJlYWRyLCBqYW5pdG9yLCBnZ3JlcGVsLCBncmlkRXh0cmEsIGdndGhlbWVzLCBzY2FsZXMsIHdyaXRleGwpDQoNCnNvdXJjZSgiaHR0cHM6Ly9naXRodWIuY29tL2tvdW5keS9nZ3Bsb3RfdGhlbWVfUHVibGljYXRpb24vcmF3L3JlZnMvaGVhZHMvbWFzdGVyL2dncGxvdF90aGVtZV9QdWJsaWNhdGlvbi0yLlIiKQ0KYGBgDQoNCiMjIEltcG9ydCBDU1YgZmlsZXMNCg0KVXNlIGByZWFkcjo6cmVhZF9jc3YoKWAgZm9yIGZhc3QgYW5kIGZyaWVuZGx5IENTViBpbXBvcnQuIFRoZSBzZXBhcmF0b3IgKGRlbGltaXRlciBiZXR3ZWVuIGNvbHVtbnMpIGlzIGF1dG9tYXRpY2FsbHkgZGV0ZWN0ZWQgYXMgYSBjb21tYS4gSG93ZXZlciB5b3UgY2FuIHNwZWNpZnkgYW5vdGhlciBkZWxpbWl0ZXIgaW4gdGhlIG9wdGlvbnMgaWYgbmVlZGVkLg0KDQpgYGB7ciByZWFkLXNpbmdsZS1jc3Z9DQpER0UgPC0gcmVhZF9jc3YoIkRHRS5jc3YiKQ0KDQpoZWFkKERHRSkNCmBgYA0KDQpFeGFtcGxlIG9uIGhvdyB0byBzcGVjaWZ5IGEgZGlmZmVyZW50IGRlbGltaXRlciAoc2VtaWNvbG9uKToNCg0KYGBge3IgcmVhZC1kZWxpbSwgZXZhbD1GQUxTRX0NCiNER0UgPC0gcmVhZF9kZWxpbSgiREdFLmNzdiIsIGRlbGltID0gIjsiKQ0KYGBgDQoNCkEgZmlsZSBjYW4gYWxzbyBjb250YWluIHRoZSBmaXJzdCBsaW5lcyB3aXRoIGNvbW1lbnRzIHdoaWNoIHlvdSB3YW50IHRvIHNraXAuIFRoZW4geW91IGNhbiB1c2UgdGhlIGBza2lwYCBhcmd1bWVudCB0byBpZ25vcmUgdGhvc2UgbGluZXM6DQoNCmBgYHtyIHJlYWQtc2tpcH0NCkRHRV9za2lwIDwtIHJlYWRfY3N2KCJ3ZWlyZERHRS5jc3YiLCBza2lwID0gMykNCmBgYA0KDQojIyBJbXBvcnQgRXhjZWwgZmlsZXMNCg0KVXNlIGByZWFkeGw6OnJlYWRfZXhjZWwoKWAgdG8gaW1wb3J0IEV4Y2VsIGZpbGVzLiBZb3UgY2FuIHNwZWNpZnkgdGhlIHNoZWV0IG5hbWUgb3IgbnVtYmVyLCBhbmQgb3B0aW9uYWxseSBhIHJhbmdlIG9mIGNlbGxzLg0KDQpgYGB7ciByZWFkLWV4Y2VsLW9uZX0NCkRHRV9DYXNlQ29udHJvbCA8LSByZWFkX2V4Y2VsKCJER0V0YWJsZXMueGxzeCIsIHNoZWV0ID0gIkNhc2VDb250cm9sIikNCg0KaGVhZChER0VfQ2FzZUNvbnRyb2wpDQpgYGANCg0KIyMjIFJlYWQgYSBzcGVjaWZpYyByYW5nZSBvZiBjZWxscyBpbiBFeGNlbA0KDQpgYGB7ciByZWFkLWV4Y2VsLXJhbmdlfQ0KREdFX3N1YnNldCA8LSByZWFkX2V4Y2VsKCJER0V0YWJsZXMueGxzeCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAiQ2FzZUNvbnRyb2wiLA0KICAgICAgICAgICAgICAgICAgICAgICAgcmFuZ2UgPSAiQTE6RjUwIikNCg0KaGVhZChER0Vfc3Vic2V0KQ0KYGBgDQoNCiMjIyBSZWFkIGFsbCBzaGVldHMgYW5kIGNvbWJpbmUNCg0KQSBtb3JlIGFkdmFuY2VkIGV4YW1wbGUgbWVyZ2luZyBhbGwgc2hlZXRzIGZyb20gZXhjZWwgaW50byBvbmUgdGFibGUsIGFuZCBuYW1pbmcgdGhlIHNvdXJjZSBzaGVldCBpbiBhIG5ldyBjb2x1bW46DQoNCmBgYHtyIHJlYWQtYWxsLXNoZWV0c30NCiNuYW1lIG9mIGVhY2ggc2hlZXQgaW4gZXhjZWwgZmlsZQ0Kc2hlZXRfbmFtZXMgPC0gZXhjZWxfc2hlZXRzKCJER0V0YWJsZXMueGxzeCIpDQpjYXQoInNoZWV0IG5hbWVzIGFyZSAiLCBwYXN0ZShzaGVldF9uYW1lcywgY29sbGFwc2UgPSAiLCAiKSwgIlxuIikNCg0KI3Rha2VzIHNoZWV0IG5hbWVzIGFuZCBydW4gcmVhZF9leGNlbCBmb3IgZWFjaCBzaGVldCwgDQojd2UgdXNlIG1hcF9mZHIgdG8gY29tYmluZSBhbGwgdGhlIHRhYmxlcyBpbnRvIG9uZSwgDQojYW5kIHdlIGFkZCBhIG5ldyBjb2x1bW4gd2l0aCB0aGUgc2hlZXQgbmFtZSB0byBrZWVwIA0KI3RyYWNrIG9mIHRoZSBzb3VyY2Ugb2YgZWFjaCByb3cNCkRHRV9hbGwgPC0gc2hlZXRfbmFtZXMgJT4lDQoJc2V0X25hbWVzKCkgJT4lDQoJbWFwX2RmcigNCgkJfnJlYWRfZXhjZWwoIkRHRXRhYmxlcy54bHN4Iiwgc2hlZXQgPSAueCksDQoJCS5pZCA9ICJzaGVldCINCgkpDQoNCnN1bW1hcnkoREdFX2FsbCkNCmBgYA0KDQoNCiMjIENvcmUgdGlkeXZlcnNlIHRhYmxlIG1hbmlwdWxhdGlvbiBhbmQgcGlwaW5nDQoNClRob3NlIGFyZSBvcGVyYXRpb25zIHRvIG1hbmlwdWxhdGUgdGFibGVzLiBZb3UgY2FuIGNoYWluIHRoZW0gdG9nZXRoZXIgd2l0aCB0aGUgcGlwZSBvcGVyYXRvciAoYCU+JWApLiBXaGF0IGRvZXMgdGhlIHBpcGUgZG8/DQoNCkxvb2sgYmVsb3c6IHdlIGRlZmluZSBzaWduaWZpY2FudF9ER0UgYXMgYSBzZXF1ZW5jZSBvZiBwaXBlZCBvcGVyYXRpb25zLiBUaGVpciBvcmRlciBpcyBmcm9tIHRoZSBmaXJzdCB0byB0aGUgbGFzdCwgYW5kIGluIHRpZHl2ZXJzZSBuYW1lcyByZWNhbGwgdmVyeSBjbGVhcmx5IHdoYXQgdGhlIG9wZXJhdGlvbnMgZG8uIFRoZSBvdXRwdXQgb2YgZWFjaCBvcGVyYXRpb24gaXMgcGlwZWQgaW50byB0aGUgbmV4dC4gSGVyZSB3ZQ0KDQogLSAxLiB1c2UgdGhlIERHRSB0aWJibGUgKHRpZHl2ZXJzZSB0YWJsZSkNCiAtIDIuIFRIRU4gcmVuYW1lIHRoZSBmaXJzdCBjb2x1bW4gdG8gYGdlbmVfbmFtZWANCiAtIDMuIFRIRU4gY2xlYW4gdXAgY29sdW1uIG5hbWVzIHdpdGggYGNsZWFuX25hbWVzKClgDQogLSA0LiBUSEVOIG11dGF0ZSB0aGUgYGdlbmVfdHlwZWAgY29sdW1uIGludG8gYSBmYWN0b3IgKGNhdGVnb3J5KQ0KIC0gNS4gVEhFTiBzZWxlY3Qgb25seSB0aGUgY29sdW1ucyBvZiBpbnRlcmVzdA0KIC0gNi4gVEhFTiBmaWx0ZXIgdGhlIHJvd3MgYnkgRkRSIGFuZCBnZW5lIHR5cGUNCiAtIDcuIFRIRU4gYXJyYW5nZSB0aGUgcm93cyBieSBkZXNjZW5kaW5nIEZEUg0KIA0KYGdsaW1wc2VgIHdpbGwgZ2l2ZSBhbiBvdmVydmlldyBvZiB0aGUgdGliYmxlJ3Mgc3RydWN0dXJlIGFuZCB0aGUgZmlyc3QgZmV3IHZhbHVlcy4NCg0KYGBge3IgY29yZS12ZXJicy0xfQ0Kc2lnbmlmaWNhbnRfREdFIDwtIERHRSAlPiUNCiAgcmVuYW1lKGdlbmVfbmFtZSA9IDEpICU+JSAjcmVuYW1lIGNvbHVtbiAxIHRvIGdlbmVfbmFtZQ0KICBqYW5pdG9yOjpjbGVhbl9uYW1lcygpICU+JSAjY2xlYW4gdXAgY29sdW1uIG5hbWVzDQogIG11dGF0ZShnZW5lX3R5cGUgPSBmYWN0b3IoZ2VuZV90eXBlKSkgJT4lICNmYWN0b3JpemUgY2F0ZWdvcmllcyBvZiBnZW5lIHR5cGVzDQoJc2VsZWN0KGdlbmVfbmFtZSwgbG9nX2ZjLCBmZHIsIGdlbmVfdHlwZSkgJT4lICNzZWxlY3Qgb25seSBzb21lIGNvbHVtbnMgb2YgaW50ZXJlc3QNCglmaWx0ZXIoZmRyIDwgLjA1ICYgZ2VuZV90eXBlPT0icHJvdGVpbi1jb2RpbmciKSAlPiUgI2ZpbHRlciBieSBmZHIgYW5kIGdlbmUgdHlwZQ0KCWFycmFuZ2UoZGVzYyhmZHIpKSAjYXJyYW5nZSBieSBkZXNjZW5kaW5nIHB2YWx1ZQ0KDQpnbGltcHNlKHNpZ25pZmljYW50X0RHRSkNCmBgYA0KDQojIyMgQ3JlYXRlIG5ldyBjb2x1bW5zIHdpdGggbXV0YXRlDQoNCllvdSBjYW4gYWxzbyBjcmVhdGUgYSBuZXcgY29sdW1uIHdpdGggbXV0dGUuIExldCdzIHNheSB3ZSB3YW50IHRvIGRlZmluZSBhIERHRSB0aWJibGUgbGlrZSBhYm92ZSwgYnV0IHdpdGhvdXQgZmlsdGVyaW5nLiBJbnN0ZWFkIHdlIGNyZWF0ZSBhIGNhdGVnb3J5IHNheWluZyBTSUdOKC4wMDEpLCBTSUdOKC4wMSksIFNJR04oLjA1KSBvciBOT1QuU0lHTiwgYWxsIGRlcGVuZGluZyBvbiB0aGUgcHZhbHVlLCBhbmQgd2UgYWxzbyB3YW50IHRoYXQgdGhlIGxvZ2ZvbGQgY2hhbmdlIGlzIGF0IGxlYXN0IGFib3ZlIDEgb3IgYmVsb3cgLTEuDQoNCllvdSBjYW4gdXNlIG11dGF0ZSB0byBjcmVhdGUgYSBuZXcgY29sdW1uIGJhc2VkIG9uIHRob3NlIGNyaXRlcmlhOg0KDQpgYGB7ciBtdXRhdGUtY29sc30NCmV4dGVuZGVkX0RHRSA8LSBER0UgJT4lDQogIHJlbmFtZShnZW5lX25hbWUgPSAxKSAlPiUgI3JlbmFtZSBjb2x1bW4gMSB0byBnZW5lX25hbWUNCiAgamFuaXRvcjo6Y2xlYW5fbmFtZXMoKSAlPiUgI2NsZWFuIHVwIGNvbHVtbiBuYW1lcw0KICBtdXRhdGUoZ2VuZV90eXBlID0gZmFjdG9yKGdlbmVfdHlwZSkpICU+JSAjZmFjdG9yaXplIGNhdGVnb3JpZXMgb2YgZ2VuZSB0eXBlcw0KCXNlbGVjdChnZW5lX25hbWUsIGxvZ19mYywgZmRyLCBnZW5lX3R5cGUpICU+JSAjc2VsZWN0IG9ubHkgc29tZSBjb2x1bW5zIG9mIGludGVyZXN0DQogIG11dGF0ZSggc2lnbmlmaWNhbmNlX2xldmVsID0gY2FzZV93aGVuKA0KICAgIGZkciA8IC4wMDEgJiBhYnMobG9nX2ZjKSA+IDEgfiAiU0lHTiguMDAxKSIsDQogICAgZmRyIDwgLjAxICYgYWJzKGxvZ19mYykgPiAxIH4gIlNJR04oLjAxKSIsDQogICAgZmRyIDwgLjA1ICYgYWJzKGxvZ19mYykgPiAxIH4gIlNJR04oLjA1KSIsDQogICAgVFJVRSB+ICJOT1QuU0lHTiINCiAgKSkgJT4lICNjcmVhdGUgYSBuZXcgY29sdW1uIHdpdGggdGhlIHNpZ25pZmljYW5jZSBsZXZlbA0KICBtdXRhdGUoIHNpZ25pZmljYW5jZV9sZXZlbCA9IGZhY3RvcihzaWduaWZpY2FuY2VfbGV2ZWwpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGZkcikpICNhcnJhbmdlIGJ5IGRlc2NlbmRpbmcgcHZhbHVlDQoNCmdsaW1wc2UoZXh0ZW5kZWRfREdFKQ0KYGBgDQoNCiMjIyBTdW1tYXJpemUgYnkgZ3JvdXANCg0KWW91IGNhbiBzdW1tYXJpemUgYnkgZ3JvdXAgYW5kIGdldCBncm91cCBzdGF0aXN0aWNzLiBGb3IgZXhhbXBsZSB0aGUgbnVtYmVyIG9mIGdlbmVzIGluIGVhY2ggZ2VuZSB0eXBlIGFuZCB0aGVpciBtZWRpYW4gZmRyIGFuZCBsb2dfZmMuDQoNCmBgYHtyIHN1bW1hcml6ZS1ncm91cH0NCnN1bW1hcnlfREdFIDwtIGV4dGVuZGVkX0RHRSAlPiUNCglncm91cF9ieShnZW5lX3R5cGUpICU+JQ0KCXN1bW1hcmlzZSgNCgkJbiA9IG4oKSwNCgkJbWVkaWFuX2xmYyA9IG1lZGlhbihsb2dfZmMsIG5hLnJtID0gVFJVRSksDQoJCW1lZGlhbl9mZHIgPSBtZWRpYW4oZmRyLCBuYS5ybSA9IFRSVUUpLA0KCQkuZ3JvdXBzID0gImRyb3AiDQoJKQ0KDQpzdW1tYXJ5X0RHRQ0KYGBgDQojIyBQbG90dGluZyB3aXRoIGdncGxvdDINCg0KWW91IGNhbiBhbHNvIHBsb3QgeW91ciBkYXRhIHdpdGggZ2dwbG90Miwgd2hpY2ggaXMgcGFydCBvZiB0aGUgdGlkeXZlcnNlLiBGb3IgZXhhbXBsZSwgYSBzY2F0dGVyIHBsb3Qgb2YgbG9nIGZvbGQgY2hhbmdlIHZzIGZkciwgY29sb3JlZCBieSBnZW5lIHR5cGU6DQoNCmBgYHtyIGdncGxvdH0NCmdncGxvdChleHRlbmRlZF9ER0UsIGFlcyh4ID0gbG9nX2ZjLCB5ID0gLWxvZzEwKGZkciksIGNvbG9yID0gZ2VuZV90eXBlKSkgKw0KCWdlb21fcG9pbnQoc2l6ZT0zKSArDQoJdGhlbWVfbWluaW1hbCgpICsNCglsYWJzKHRpdGxlID0gIkRHRSBTY2F0dGVyIFBsb3QiLCB4ID0gIkxvZyBGb2xkIENoYW5nZSIsIHkgPSAiLUxvZzEwKEZEUikiKQ0KYGBgDQoNCmdncGxvdCB0YWtlcyB0aGUgZGF0YWZyYW1lL3RpYmJsZSBhcyBpbnB1dCBhbmQgYWRkcyBsYXllcnMgb2YgdGhlIHBsb3QgdXNpbmcgdGhlIHN5bWJvbCArLg0KVGhlcmUgYXJlIGEgbG90IG9mIHdheXMgdG8gY3VzdG9taXplIHRoZSBwbG90IG9yIGRvIG90aGVyIHR5cGVzIG9mIHBsb3RzLg0KDQpXZSBjYW4gYWRkIGEgY29sdW1uIHdoZXJlIHdlIHdyaXRlIHRoZSBuYW1lIG9mIHRoZSBnZW5lcyB3aGljaCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLiBXZSBjYW4gZG8gdGhpcyB3aXRoIHRoZSBtdXRhdGUgZnVuY3Rpb24gYW5kIGNhc2Vfd2hlbi4gVGhlbiB3ZSBhZGQgdG8gdGhlIHBsb3QgYSBsYXllciBvZiB0ZXh0Lg0KDQpgYGB7ciBsYWJlbC1uYW1lfQ0KZXh0ZW5kZWRfREdFIDwtIGV4dGVuZGVkX0RHRSAlPiUNCiAgbXV0YXRlKCBwbG90X2xhYmVsID0gY2FzZV93aGVuKA0KICAgIHNpZ25pZmljYW5jZV9sZXZlbCA9PSAgIk5PVC5TSUdOIiB+IE5BLA0KICAgIFRSVUUgfiBnZW5lX25hbWUNCiAgICApICkNCg0KZ2xpbXBzZShleHRlbmRlZF9ER0UpDQpgYGANCg0KYGBge3IgcGxvdC13aXRoLWxhYmVsc30NCmdncGxvdChleHRlbmRlZF9ER0UsIGFlcyh4ID0gbG9nX2ZjLCB5ID0gLWxvZzEwKGZkciksIGNvbG9yID0gZ2VuZV90eXBlKSkgKw0KCWdlb21fcG9pbnQoKSArDQogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBwbG90X2xhYmVsKSwgc2l6ZSA9IDMsICkgKyAjYWRkIGxhYmVscyB0byBzaWduaWZpY2FudCBnZW5lcw0KCXRoZW1lX21pbmltYWwoKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wNSkpICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWMoMSwtMSkpICsgICAgI3ZlcnRpY2FsIGxpbmUgZ2dwbG90DQoJbGFicyh0aXRsZSA9ICJER0UgU2NhdHRlciBQbG90IiwgeCA9ICJMb2cgRm9sZCBDaGFuZ2UiLCB5ID0gIi1Mb2cxMChGRFIpIikNCmBgYA0KIyMjIFRoZW1lcw0KDQpZb3UgY2FuIGNoYW5nZSB0aGVtZSBmb3IgYWRhcHRpbmcgdGhlIGFwcGVhcmVuY2UuIEFsbCBzdGFuZGFyZCB0aGVtZXMgYXJlIGhlcmU6IGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZ3RoZW1lLmh0bWwuIEZvciBleGFtcGxlLCBpdCBpcyBwb3B1bGFyIHRvIHVzZSB0aGUgZ3JheSB0aGVtZQ0KDQpgYGB7ciB0aGVtZS1nZ3Bsb3QgfQ0KZ2dwbG90KGV4dGVuZGVkX0RHRSwgYWVzKHggPSBsb2dfZmMsIHkgPSAtbG9nMTAoZmRyKSwgY29sb3IgPSBnZW5lX3R5cGUpKSArDQoJZ2VvbV9wb2ludCgpICsNCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IHBsb3RfbGFiZWwpLCBzaXplID0gMywgKSArICNhZGQgbGFiZWxzIHRvIHNpZ25pZmljYW50IGdlbmVzDQoJdGhlbWVfZ3JheSgpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS1sb2cxMCgwLjA1KSkgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9YygxLC0xKSkgKyAgICAjdmVydGljYWwgbGluZSBnZ3Bsb3QNCglsYWJzKHRpdGxlID0gIkRHRSBTY2F0dGVyIFBsb3QiLCB4ID0gIkxvZyBGb2xkIENoYW5nZSIsIHkgPSAiLUxvZzEwKEZEUikiKQ0KYGBgDQoNCllvdSBjYW4gZG8gbmljZXIgdGhlbWVzIHVzaW5nIHRoZSBvbmVzIGRldmVsb3BlZCBoZXJlIGh0dHBzOi8vZ2l0aHViLmNvbS9rb3VuZHkvZ2dwbG90X3RoZW1lX1B1YmxpY2F0aW9uLCB3aGVyZSBzb21lb25lIGhhcyBkZXZlbG9wZWQgaXRzIG93biB0aGVtZXMuIFdlIGhhdmUgaW1wb3J0ZWQgdGhlbSBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoaXMgbm90ZWJvb2suIEZvciBleGFtcGxlIHlvdSBjYW4gdXNlIGEgdGhlbWUgZGV2ZWxvcGVkIGZvciBwdWJsaWNhdGlvbnMgKG5pY2VyIHNpemVzIGFuZCBmb250IG9mIHRleHQgYW5kIG5pY2VyIGxlZ2VuZCkuDQoNCmBgYHtyIHRoZW1lLXB1YmxpY2F0aW9ufQ0KZ2dwbG90KGV4dGVuZGVkX0RHRSwgYWVzKHggPSBsb2dfZmMsIHkgPSAtbG9nMTAoZmRyKSwgY29sb3IgPSBnZW5lX3R5cGUpKSArDQoJZ2VvbV9wb2ludCgpICsNCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IHBsb3RfbGFiZWwpLCBzaXplID0gMywgKSArICNhZGQgbGFiZWxzIHRvIHNpZ25pZmljYW50IGdlbmVzDQoJdGhlbWVfUHVibGljYXRpb24oKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wNSkpICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWMoMSwtMSkpICsgICAgI3ZlcnRpY2FsIGxpbmUgZ2dwbG90DQoJbGFicyh0aXRsZSA9ICJER0UgU2NhdHRlciBQbG90IiwgeCA9ICJMb2cgRm9sZCBDaGFuZ2UiLCB5ID0gIi1Mb2cxMChGRFIpIikNCmBgYA0KT3IgY29tYmluZSBwdWJsaWNhdGlvbiB0aGVtZSB3aXRoIGEgY29sb3IgcGFsZXR0ZSBmb3IgZGlzY3JldGUgdmFyaWFibGVzIChnZW5lIHR5cGUpIGZyb20gdGhlIHNhbWUgYXV0aG9yLiBZb3UgY2FuIGxvb2sgYXQgdGhlIHdlYnBhZ2UgZm9yIHRoZSB0aGVtZXMgdG8gc2VlIG1vcmUgZXhhbXBsZXMuDQoNCmBgYHtyIHRoZW1lLXB1YmxpY2F0aW9uLXBhbGV0dGV9DQpnZ3Bsb3QoZXh0ZW5kZWRfREdFLCBhZXMoeCA9IGxvZ19mYywgeSA9IC1sb2cxMChmZHIpLCBjb2xvciA9IGdlbmVfdHlwZSkpICsNCglnZW9tX3BvaW50KCkgKw0KICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gcGxvdF9sYWJlbCksIHNpemUgPSAzLCApICsgI2FkZCBsYWJlbHMgdG8gc2lnbmlmaWNhbnQgZ2VuZXMNCglzY2FsZV9maWxsX1B1YmxpY2F0aW9uKCkgKyB0aGVtZV9kYXJrX2JsdWUoKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wNSkpICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWMoMSwtMSkpICsgICAgI3ZlcnRpY2FsIGxpbmUgZ2dwbG90DQoJbGFicyh0aXRsZSA9ICJER0UgU2NhdHRlciBQbG90IiwgeCA9ICJMb2cgRm9sZCBDaGFuZ2UiLCB5ID0gIi1Mb2cxMChGRFIpIikgDQpgYGANClN0aWxsIG5vdCBlbm91Z2g/IFRoZSBwYWNrYWdlIGdndGhlbWVzIGhhcyBldmVuIG1vcmUgd2F5cyBvZiBjb21iaW5pbmcgY29sb3IgcGFsZXR0ZXMgYW5kIHRoZW1lcy4gTG9vayBoZXJlIGh0dHBzOi8vZ2l0aHViLmNvbS9qcm5vbGQvZ2d0aGVtZXMgd2hlcmUgdGhlIGF1dGhvciBtYWtlcyBhIGxvdCBvZiBleGFtcGxlcyBhbmQgdHJ5IG9uZSBvZiB0aGVtIQ0KDQojIyMgc2F2aW5nIGdncGxvdHMNCg0KWW91IGNhbiBzYXZlIGEgZ2dwbG90IHdpdGggdGhlIGRlZGljYXRlZCBjb21tYW5kLCBidXQgdG8gZG8gdGhhdCB5b3UgbmVlZCB0byBhc3NpZ24gdGhlIHBsb3QgdG8gYSB2YXJpYWJsZS4gRm9yIGV4YW1wbGU6DQoNCmBgYHtyIGdnc2F2ZX0NCnZvbGNhbm9fcGxvdCA8LSBnZ3Bsb3QoZXh0ZW5kZWRfREdFLCBhZXMoeCA9IGxvZ19mYywgeSA9IC1sb2cxMChmZHIpLCBjb2xvciA9IGdlbmVfdHlwZSkpICsNCglnZW9tX3BvaW50KCkgKw0KICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gcGxvdF9sYWJlbCksIHNpemUgPSAzLCApICsgI2FkZCBsYWJlbHMgdG8gc2lnbmlmaWNhbnQgZ2VuZXMNCgl0aGVtZV9ncmF5KCkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDUpKSArIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKDEsLTEpKSArICAgICN2ZXJ0aWNhbCBsaW5lIGdncGxvdA0KCWxhYnModGl0bGUgPSAiREdFIFNjYXR0ZXIgUGxvdCIsIHggPSAiTG9nIEZvbGQgQ2hhbmdlIiwgeSA9ICItTG9nMTAoRkRSKSIpDQoNCmdnc2F2ZShmaWxlbmFtZSA9ICIuL2xpZmVFeHAucG5nIiwgcGxvdCA9IHZvbGNhbm9fcGxvdCwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCwgdW5pdHMgPSAiY20iKQ0KYGBgDQoNCiMjIyBTb21lIG90aGVyIHBsb3RzOiBiYXJwbG90cywgYm94cGxvdHMsIGRlbnNpdHkgcGxvdHMsIC4uLg0KDQpUaGVyZSBpcyBhIHdpZGUgcmFuZ2Ugb2YgcGxvdCB0eXBlcyB5b3UgY2FuIGRvIHdpdGggZ2dwbG90LCBhbmQgaXQgcmVhbGx5IGRlcGVuZHMgb24geW91ciBkYXRhIGFuZCB3aGF0IHlvdSB3YW50IHRvIHZpc3VhbGl6ZS4gRm9yIGV4YW1wbGUsIHlvdSBjYW4gZG8gYSBiYXIgcGxvdCBvZiB0aGUgbnVtYmVyIG9mIGdlbmVzIGluIGVhY2ggZ2VuZSB0eXBlOg0KDQpgYGB7ciBiYXJwbG90fQ0KZ2dwbG90KHN1bW1hcnlfREdFLCBhZXMoeCA9IGdlbmVfdHlwZSwgeSA9IGxvZyhuKSwgZmlsbCA9IGdlbmVfdHlwZSkpICsNCglnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBzY2FsZV9maWxsX1B1YmxpY2F0aW9uKCkgKyANCiAgdGhlbWVfZGFya19ibHVlKCkNCmBgYA0KDQpOb3RlIHRoYXQgYWJvdmUgd2UgbmVlZGVkIG9ubHkgb25lIHZhbHVlIHBlciBnZW5lIHR5cGUgdG8gcGxvdCB0aGUgYmFyLiBJZiB3ZSBoYWQgbW9yZSB2YWx1ZXMgcGVyIGdlbmUgdHlwZSwgd2Ugd291bGQgbmVlZCB0byB1c2UgYHN0YXQgPSAic3VtbWFyeSJgIGFuZCBzcGVjaWZ5IGEgc3VtbWFyeSBmdW5jdGlvbiAoZm9yIGV4YW1wbGUgYGZ1biA9ICJtZWFuImAgb3IgYGZ1biA9ICJtZWRpYW4iYCkuIEZvciBleGFtcGxlLCBpZiB3ZSB3YW50IHRvIHBsb3QgdGhlIG1lZGlhbiBsb2cgZm9sZCBjaGFuZ2UgZm9yIGVhY2ggZ2VuZSB0eXBlOg0KDQpgYGB7ciBiYXJwbG90LW1lZGlhbn0NCmdncGxvdChleHRlbmRlZF9ER0UsIGFlcyh4ID0gZ2VuZV90eXBlLCB5ID0gbG9nX2ZjLCBmaWxsID0gZ2VuZV90eXBlKSkgKw0KCWdlb21fYmFyKHN0YXQgPSAic3VtbWFyeSIsIGZ1biA9ICJtZWRpYW4iKSArDQogIHNjYWxlX2ZpbGxfUHVibGljYXRpb24oKSArIA0KICB0aGVtZV9kYXJrX2JsdWUoKSArDQojY2huZ2UgeCBheGlzIGFuZ2xlIHRvIDQ1DQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KIyMjIEZhY2V0aW5nDQoNCkZhY2V0aW5nIGlzIGEgcG93ZXJmdWwgd2F5IHRvIGNyZWF0ZSBtdWx0aXBsZSBwbG90cyBiYXNlZCBvbiBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBGb3IgZXhhbXBsZSwgd2UgY2FuIGZhY2V0IHRoZSBzY2F0dGVyIHBsb3QgYnkgZ2VuZSB0eXBlOg0KDQpgYGB7ciBmYWNldC1nZ3Bsb3R9DQpnZ3Bsb3QoZXh0ZW5kZWRfREdFLCBhZXMoeCA9IGxvZ19mYywgeSA9IC1sb2cxMChmZHIpLCBjb2xvciA9IGdlbmVfdHlwZSkpICsNCglnZW9tX3BvaW50KCkgKw0KICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gcGxvdF9sYWJlbCkpICsNCiAgCXRoZW1lX21pbmltYWwoKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wNSkpICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWMoMSwtMSkpICsgICAgI3ZlcnRpY2FsIGxpbmUgZ2dwbG90DQogIAlsYWJzKHRpdGxlID0gIkRHRSBTY2F0dGVyIFBsb3QiLCB4ID0gIkxvZyBGb2xkIENoYW5nZSIsIHkgPSAiLUxvZzEwKEZEUikiKSArDQogIGZhY2V0X3dyYXAofiBnZW5lX3R5cGUpDQpgYGANCiAgDQogIA0KQSBkZW5zaXR5IHBsb3Qgb2YgbG9nIGZvbGQgY2hhbmdlIGJ5IGdlbmUgdHlwZToNCg0KYGBge3IgZGVuc2l0eS1nZ3Bsb3R9DQpnZ3Bsb3QoZXh0ZW5kZWRfREdFLCBhZXMoeCA9IGxvZ19mYywgZmlsbCA9IGdlbmVfdHlwZSkpICsNCiAgCWdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSkgKw0KICBzY2FsZV9maWxsX1B1YmxpY2F0aW9uKCkgKw0KICB0aGVtZV9leGNlbF9uZXcoKSArDQogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIExvZyBGb2xkIENoYW5nZVxuYnkgR2VuZSBUeXBlIiwgeCA9ICJMb2cgRm9sZCBDaGFuZ2UiLCB5ID0gIkRlbnNpdHkiKSANCmBgYA0KDQojIyBFeHBvcnQgY2xlYW5lZCByZXN1bHRzDQoNCmBgYHtyIGV4cG9ydCwgZXZhbD1GQUxTRX0NCiMgRXhwb3J0IHRvIENTVg0Kd3JpdGVfY3N2KGV4dGVuZGVkX0RHRSwgImZpbmFsX0RHRS5jc3YiKQ0KDQojIEV4cG9ydCB0byBFeGNlbCAobmVlZHMgd3JpdGV4bCBwYWNrYWdlKQ0Kd3JpdGV4bDo6d3JpdGVfeGxzeChleHRlbmRlZF9ER0UsICJmaW5hbF9ER0UueGxzeCIpDQpgYGANCg0KIyMgUXVpY2sgcHJhY3RpY2UgdGFza3MNCg0KMS4gSW1wb3J0IG9uZSBDU1Ygb3Igb25lIEV4Y2VsIGZpbGUgZnJvbSB5b3VyIG93biBwcm9qZWN0Lg0KMi4gVXNlIGBjbGVhbl9uYW1lcygpYCBhbmQgYG11dGF0ZSgpYCB0byBzdGFuZGFyZGl6ZS9jbGVhbnVwIGNvbHVtbnMNCjMuIGFwcGx5IHNvbWUgbGFiZWxzIG9mIGludGVyZXN0ZWQsIGFzIGluIHRoZSBleGFtcGxlIGZvciBzaWduaWZpY2FuY2UNCjQuIG1ha2UgYSBwbG90IHlvdSB3b3VsZCBsaWtlIHRvIHNlZSwgZm9yIGV4YW1wbGUgYSBzY2F0dGVyIHBsb3Qgb3IgYSBiYXIgcGxvdCwgYW5kIGN1c3RvbWl6ZSBpdCB3aXRoIHRoZW1lcyBhbmQgbGFiZWxzDQo1LiBFeHBvcnQgdGhlIGZpbmFsIGNsZWFuZWQgdGFibGUNCg0KIyMgVHJvdWJsZXNob290aW5nIHRpcHMNCg0KLSBJZiBpbXBvcnQgZmFpbHMgZHVlIHRvIHNlcGFyYXRvcnMsIHRyeSBgcmVhZF9kZWxpbSgpYCB3aXRoIHRoZSBjb3JyZWN0IGBkZWxpbWAuDQotIElmIHRleHQgYXBwZWFycyBicm9rZW4sIGNoZWNrIGVuY29kaW5nIHdpdGggYGxvY2FsZShlbmNvZGluZyA9ICJVVEYtOCIpYC4NCi0gSWYgY29sdW1uIHR5cGVzIGFyZSB3cm9uZywgZml4IHdpdGggYG11dGF0ZSgpYCBhbmQgYGFzLm51bWVyaWMoKWAsIGBhcy5mYWN0b3IoKWAsIGV0Yy4NCi0gSWYgam9pbnMgYWRkIG1hbnkgbWlzc2luZyB2YWx1ZXMsIHZlcmlmeSB0aGUga2V5IGNvbHVtbnMgKGZvciBleGFtcGxlIGBzYW1wbGVfaWRgKSBtYXRjaCBleGFjdGx5IGluIGJvdGggdGFibGVzLg0KDQojIyBTb21lIHJlc291cmNlcw0KDQpJIHdpbGwganVzdCBsaXN0IGEgZmV3IHRoaW5ncywgYXMgdGhlIGludGVybmV0IGlzIGEgd2lkZSBvY2VhbiwgYW5kIHlvdSBtaWdodCB3YW50IHRvIGZvY3VzIG9uIGZldyBzZWxlY3RlZCB0aGluZ3MuDQoNCi0gUiBub3ZpY2UgZ2FwIHJlbWluZGVyIGh0dHBzOi8vc3djYXJwZW50cnkuZ2l0aHViLmlvL3Itbm92aWNlLWdhcG1pbmRlci9pbmRleC5odG1sDQotIEEgc2xpZ2h0bHkgbW9yZSBpbiBkZXB0aCBSIGFuZCB0aWR5ciB3b3Jrc2hvcCB3ZSBqdXN0IGhhZCBhdCB0aGUgY29kaW5nIGNhZmU6IGh0dHBzOi8vYWJjLmF1LmRrL2RvY3VtZW50YXRpb24vMjAyNi0wMi0yNi10aWR5Ui1vbWljczEuaHRtbA0KLSB0aWR5ciBjaGVhdHNlZXQgZnJvbSBQb3NpdCAgaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvYmxvYi9tYWluL3RpZHlyLnBkZg0KLSB0aGUgaGVscCBwYW5lIG9mIFJzdHVkaW8gd2hlcmUgeW91IGNhbiBmaW5kIHRoZSBkb2N1bWVudGF0aW9uIG9mIGFsbCBmdW5jdGlvbg0KLSB0aGUgdGFiIGJ1dHRvbiBmb3IgYXV0b2NvbXBsZXRpbmcgZnVuY3Rpb25zIGFuZCBhcmd1bWVudHMgaW4gUnN0dWRpby4gSXQgaXMgdGhlIHN0YXJ0aW5nIGNvbmRpdGlvbiB0byBoYXZlIGFuIGVhc2llciBsaWZlLg0K