Estimated time: 15 Minutes

Motivation

The data from from our differential expression test (result_plus_vs_minus) is an important result from our analysis.

head(results_plus_vs_minus)
# A tibble: 6 × 8
  id                 baseMean log2FoldChange lfcSE  stat   pvalue     padj call 
  <chr>                 <dbl>          <dbl> <dbl> <dbl>    <dbl>    <dbl> <fct>
1 ENSMUSG00000032715   1178.          -3.64  0.450 -8.10 5.72e-16 7.08e-12 Down 
2 ENSMUSG00000101645     77.3         10.1   1.33   7.58 3.42e-14 2.12e-10 Up   
3 ENSMUSG00000027313    243.          -2.67  0.410 -6.50 7.96e-11 2.47e- 7 Down 
4 ENSMUSG00000027889   3250.          -0.946 0.145 -6.52 7.00e-11 2.47e- 7 Down 
5 ENSMUSG00000054136     92.6         -3.39  0.526 -6.44 1.19e-10 2.95e- 7 Down 
6 ENSMUSG00000020142    625.          -1.72  0.287 -5.99 2.07e- 9 4.28e- 6 Down 

Looking at the first few rows, we notice that the genes are labelled with their ENSEMBL identifiers, e.g. ENSMUSG00000000056. It is quite rare that anyone will know off the top of their head which gene is associated with any given ENSEMBL identifier; we tend to think of genes by their symbols, e.g. Tp53.

Exercise

Using the biomaRt package, to map ENSEMBL IDs to gene symnbols, add a column with the gene symbols to results_plus_vs_minus.

Instructions

  • One group member should share their screen in the breakout room. If nobody volunteers, a helper may randomly select someone.
  • The group members should discuss the exercise and work together to find a solution.
  • If there is time after a solution is found, allow all members to complete the exercise.

Preliminaries

First, let’s download the mapping of ENSEMBL IDs to gene symbols together. Then we’ll split into small groups to add the gene symbol column we want. We’ll start by loading the biomaRt library and calling the useEnsembl() function to select the database we’ll use to extract the information we need.

library('biomaRt')
ensembl = useEnsembl(dataset = 'mmusculus_gene_ensembl', biomart='ensembl')

Note that this process takes some time and will take up a larger amount of working memory so proceed with caution if you try to run these commands on a laptop with less than 4G of memory

To identify possible filters to restrict our data, we can use the listFilters function. To identify the attributes we want to retrive, we can use the listAttributes function. The best approach is to use list or search functions to help narrow down the available options.

head(listFilters(mart = ensembl), n = 20)
head(listAttributes(ensembl), n = 30)

We can access additional genomic annotations using the bioMart package. To identify we’ll structure our ‘query’ or search of the bioMart resources to use the ENSEMBL id from our alignment to add the gene symbols and gene description for each gene.

id_mapping = getBM(attributes=c('ensembl_gene_id', 'external_gene_name'),
      filters = 'ensembl_gene_id',
      values = row.names(assay(dds)),
      mart = ensembl)
# will take some time to run

# Preview the result
head(id_mapping)
     ensembl_gene_id external_gene_name
1 ENSMUSG00000000001              Gnai3
2 ENSMUSG00000000028              Cdc45
3 ENSMUSG00000000031                H19
4 ENSMUSG00000000037              Scml2
5 ENSMUSG00000000049               Apoh
6 ENSMUSG00000000056               Narf

Now that we have the ENSEMBL information and a gene symbol to match to our results, we can proceed in the smaller groups. As with the previous exercise, we have broken it into small steps with hints as needed.

Note: For additional information regarding bioMart, please consult the ENSEMBL bioMart vignette or the broader Bioconductor Annotation Resources vignette.

Steps

  1. Look at the two data frames that are going to be needed for the exercise: id_mapping and results_plus_vs_minus.
Answer
head(id_mapping)
     ensembl_gene_id external_gene_name
1 ENSMUSG00000000001              Gnai3
2 ENSMUSG00000000028              Cdc45
3 ENSMUSG00000000031                H19
4 ENSMUSG00000000037              Scml2
5 ENSMUSG00000000049               Apoh
6 ENSMUSG00000000056               Narf
head(results_plus_vs_minus)
# A tibble: 6 × 8
  id                 baseMean log2FoldChange lfcSE  stat   pvalue     padj call 
  <chr>                 <dbl>          <dbl> <dbl> <dbl>    <dbl>    <dbl> <fct>
1 ENSMUSG00000032715   1178.          -3.64  0.450 -8.10 5.72e-16 7.08e-12 Down 
2 ENSMUSG00000101645     77.3         10.1   1.33   7.58 3.42e-14 2.12e-10 Up   
3 ENSMUSG00000027313    243.          -2.67  0.410 -6.50 7.96e-11 2.47e- 7 Down 
4 ENSMUSG00000027889   3250.          -0.946 0.145 -6.52 7.00e-11 2.47e- 7 Down 
5 ENSMUSG00000054136     92.6         -3.39  0.526 -6.44 1.19e-10 2.95e- 7 Down 
6 ENSMUSG00000020142    625.          -1.72  0.287 -5.99 2.07e- 9 4.28e- 6 Down 


  1. Think about what column of results_plus_vs_minus we want to match to id_mapping, and then what column we want to extract values from in id_mapping.
Answer We want to match the id column of results_plus_vs_minus 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.


  1. Look at the documentation for dplyr::left_join() and try to merge the id_mapping table into the results_plus_vs_minus table on the columns you identified above.
Answer
results_plus_vs_minus_annotated = results_plus_vs_minus %>%
    left_join(id_mapping, by = c('id' = 'ensembl_gene_id'))
head(results_plus_vs_minus_annotated)
# A tibble: 6 × 9
  id                 baseMean log2FoldChange lfcSE  stat   pvalue     padj call  external_gene_name
  <chr>                 <dbl>          <dbl> <dbl> <dbl>    <dbl>    <dbl> <fct> <chr>             
1 ENSMUSG00000032715   1178.          -3.64  0.450 -8.10 5.72e-16 7.08e-12 Down  Trib3             
2 ENSMUSG00000101645     77.3         10.1   1.33   7.58 3.42e-14 2.12e-10 Up    Gm28635           
3 ENSMUSG00000027313    243.          -2.67  0.410 -6.50 7.96e-11 2.47e- 7 Down  Chac1             
4 ENSMUSG00000027889   3250.          -0.946 0.145 -6.52 7.00e-11 2.47e- 7 Down  Ampd2             
5 ENSMUSG00000054136     92.6         -3.39  0.526 -6.44 1.19e-10 2.95e- 7 Down  Adm2              
6 ENSMUSG00000020142    625.          -1.72  0.287 -5.99 2.07e- 9 4.28e- 6 Down  Slc1a4            


  1. Optionally, how could we 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, consider using dplyr::rename() and dplyr::select() instead of just the select() function. We can discuss why this is the case together.
Answer
results_plus_vs_minus_annotated = results_plus_vs_minus_annotated %>%
    dplyr::rename('symbol' = 'external_gene_name') %>%
    dplyr::select(id, symbol, everything())
results_plus_vs_minus_annotated
# A tibble: 16,249 × 9
   id                 symbol   baseMean log2FoldChange lfcSE  stat   pvalue     padj call 
   <chr>              <chr>       <dbl>          <dbl> <dbl> <dbl>    <dbl>    <dbl> <fct>
 1 ENSMUSG00000032715 Trib3      1178.          -3.64  0.450 -8.10 5.72e-16 7.08e-12 Down 
 2 ENSMUSG00000101645 Gm28635      77.3         10.1   1.33   7.58 3.42e-14 2.12e-10 Up   
 3 ENSMUSG00000027313 Chac1       243.          -2.67  0.410 -6.50 7.96e-11 2.47e- 7 Down 
 4 ENSMUSG00000027889 Ampd2      3250.          -0.946 0.145 -6.52 7.00e-11 2.47e- 7 Down 
 5 ENSMUSG00000054136 Adm2         92.6         -3.39  0.526 -6.44 1.19e-10 2.95e- 7 Down 
 6 ENSMUSG00000020142 Slc1a4      625.          -1.72  0.287 -5.99 2.07e- 9 4.28e- 6 Down 
 7 ENSMUSG00000040280 Ndufa4l2     85.9         -3.62  0.628 -5.75 8.68e- 9 1.54e- 5 Down 
 8 ENSMUSG00000023951 Vegfa       350.          -2.44  0.432 -5.64 1.67e- 8 2.59e- 5 Down 
 9 ENSMUSG00000027737 Slc7a11     119.          -2.32  0.429 -5.41 6.47e- 8 8.02e- 5 Down 
10 ENSMUSG00000115420 Rmrp         16.0         21.1   3.91   5.41 6.42e- 8 8.02e- 5 Up   
# … with 16,239 more rows
# ℹ Use `print(n = ...)` to see more rows


LS0tCnRpdGxlOiAnQnJlYWtvdXQgRXhlcmNpc2UgMiAtIEdlbmUgQW5ub3RhdGlvbnMnCmF1dGhvcjogJ1VNIEJpb2luZm9ybWF0aWNzIENvcmUnCmRhdGU6ICdgciBTeXMuRGF0ZSgpYCcKb3V0cHV0OgogICAgICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgICAgICAgIGluY2x1ZGVzOgogICAgICAgICAgICAgICAgaW5faGVhZGVyOiBoZWFkZXIuaHRtbAogICAgICAgICAgICB0aGVtZTogcGFwZXIKICAgICAgICAgICAgdG9jOiB0cnVlCiAgICAgICAgICAgIHRvY19kZXB0aDogNAogICAgICAgICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgICAgICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgICAgICAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgICAgICAgICBtYXJrZG93bjogR0ZNCiAgICAgICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgo8c3R5bGUgdHlwZT0ndGV4dC9jc3MnPgpib2R5LCB0ZCB7CiAgIGZvbnQtc2l6ZTogMThweDsKfQpjb2RlLnJ7CiAgZm9udC1zaXplOiAxMnB4Owp9CnByZSB7CiAgZm9udC1zaXplOiAxMnB4Cn0KPC9zdHlsZT4KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnNvdXJjZSgnLi4vYmluL2NodW5rLW9wdGlvbnMuUicpCmtuaXRyX2ZpZ19wYXRoKCcxMWEtJykKYGBgCgpgYGB7ciBNb2R1bGVzLCBldmFsPVRSVUUsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkobWF0cml4U3RhdHMpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKRXN0aW1hdGVkIHRpbWU6ICoqMTUgTWludXRlcyoqCgojIE1vdGl2YXRpb24KClRoZSBkYXRhIGZyb20gZnJvbSBvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gdGVzdCAoYHJlc3VsdF9wbHVzX3ZzX21pbnVzYCkgaXMgYW4gaW1wb3J0YW50IHJlc3VsdCBmcm9tIG91ciBhbmFseXNpcy4KCmBgYHtyIHJlc3VsdF9wcmV2aWV3fQpoZWFkKHJlc3VsdHNfcGx1c192c19taW51cykKYGBgCgpMb29raW5nIGF0IHRoZSBmaXJzdCBmZXcgcm93cywgd2Ugbm90aWNlIHRoYXQgdGhlIGdlbmVzIGFyZSBsYWJlbGxlZCB3aXRoIHRoZWlyIEVOU0VNQkwgaWRlbnRpZmllcnMsIGUuZy4gRU5TTVVTRzAwMDAwMDAwMDU2LiBJdCBpcyBxdWl0ZSByYXJlIHRoYXQgYW55b25lIHdpbGwga25vdyBvZmYgdGhlIHRvcCBvZiB0aGVpciBoZWFkIHdoaWNoIGdlbmUgaXMgYXNzb2NpYXRlZCB3aXRoIGFueSBnaXZlbiBFTlNFTUJMIGlkZW50aWZpZXI7IHdlIHRlbmQgdG8gdGhpbmsgb2YgZ2VuZXMgYnkgdGhlaXIgc3ltYm9scywgZS5nLiBUcDUzLgoKIyBFeGVyY2lzZQoKVXNpbmcgdGhlIGBiaW9tYVJ0YCBwYWNrYWdlLCB0byBtYXAgRU5TRU1CTCBJRHMgdG8gZ2VuZSBzeW1uYm9scywgYWRkIGEgY29sdW1uIHdpdGggdGhlIGdlbmUgc3ltYm9scyB0byBgcmVzdWx0c19wbHVzX3ZzX21pbnVzYC4KCiMgSW5zdHJ1Y3Rpb25zCgotIE9uZSBncm91cCBtZW1iZXIgc2hvdWxkIHNoYXJlIHRoZWlyIHNjcmVlbiBpbiB0aGUgYnJlYWtvdXQgcm9vbS4gSWYgbm9ib2R5IHZvbHVudGVlcnMsIGEgaGVscGVyIG1heSByYW5kb21seSBzZWxlY3Qgc29tZW9uZS4KLSBUaGUgZ3JvdXAgbWVtYmVycyBzaG91bGQgZGlzY3VzcyB0aGUgZXhlcmNpc2UgYW5kIHdvcmsgdG9nZXRoZXIgdG8gZmluZCBhIHNvbHV0aW9uLgotIElmIHRoZXJlIGlzIHRpbWUgYWZ0ZXIgYSBzb2x1dGlvbiBpcyBmb3VuZCwgYWxsb3cgYWxsIG1lbWJlcnMgdG8gY29tcGxldGUgdGhlIGV4ZXJjaXNlLgoKIyBQcmVsaW1pbmFyaWVzCgpGaXJzdCwgbGV0J3MgZG93bmxvYWQgdGhlIG1hcHBpbmcgb2YgRU5TRU1CTCBJRHMgdG8gZ2VuZSBzeW1ib2xzICoqdG9nZXRoZXIqKi4gVGhlbiB3ZSdsbCBzcGxpdCBpbnRvIHNtYWxsIGdyb3VwcyB0byBhZGQgdGhlIGdlbmUgc3ltYm9sIGNvbHVtbiB3ZSB3YW50LiBXZSdsbCBzdGFydCBieSBsb2FkaW5nIHRoZSBgYmlvbWFSdGAgbGlicmFyeSBhbmQgY2FsbGluZyB0aGUgYHVzZUVuc2VtYmwoKWAgZnVuY3Rpb24gdG8gc2VsZWN0IHRoZSBkYXRhYmFzZSB3ZSdsbCB1c2UgdG8gZXh0cmFjdCB0aGUgaW5mb3JtYXRpb24gd2UgbmVlZC4KCmBgYHtyIFB1bGxtYXJ0LCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KCdiaW9tYVJ0JykKZW5zZW1ibCA9IHVzZUVuc2VtYmwoZGF0YXNldCA9ICdtbXVzY3VsdXNfZ2VuZV9lbnNlbWJsJywgYmlvbWFydD0nZW5zZW1ibCcpCmBgYAoKKk5vdGUgdGhhdCB0aGlzIHByb2Nlc3MgdGFrZXMgc29tZSB0aW1lIGFuZCB3aWxsIHRha2UgdXAgYSBsYXJnZXIgYW1vdW50IG9mIHdvcmtpbmcgbWVtb3J5IHNvIHByb2NlZWQgd2l0aCBjYXV0aW9uIGlmIHlvdSB0cnkgdG8gcnVuIHRoZXNlIGNvbW1hbmRzIG9uIGEgbGFwdG9wIHdpdGggbGVzcyB0aGFuIDRHIG9mIG1lbW9yeSoKClRvIGlkZW50aWZ5IHBvc3NpYmxlICoqZmlsdGVycyoqIHRvIHJlc3RyaWN0IG91ciBkYXRhLCB3ZSBjYW4gdXNlIHRoZSBgbGlzdEZpbHRlcnNgIGZ1bmN0aW9uLiBUbyBpZGVudGlmeSB0aGUgKiphdHRyaWJ1dGVzKiogd2Ugd2FudCB0byByZXRyaXZlLCB3ZSBjYW4gdXNlIHRoZSBgbGlzdEF0dHJpYnV0ZXNgIGZ1bmN0aW9uLiBUaGUgYmVzdCBhcHByb2FjaCBpcyB0byB1c2UgW2xpc3Qgb3Igc2VhcmNoIGZ1bmN0aW9uc10oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvYmlvbWFSdC9pbnN0L2RvYy9hY2Nlc3NpbmdfZW5zZW1ibC5odG1sI2hvdy10by1idWlsZC1hLWJpb21hcnQtcXVlcnkpIHRvIGhlbHAgbmFycm93IGRvd24gdGhlIGF2YWlsYWJsZSBvcHRpb25zLgoKYGBge3IgQWRkQW5ub3RhdGlvbnMyLCB3YXJuaW5nPUZBTFNFLCBldmFsPUZBTFNFfQpoZWFkKGxpc3RGaWx0ZXJzKG1hcnQgPSBlbnNlbWJsKSwgbiA9IDIwKQpoZWFkKGxpc3RBdHRyaWJ1dGVzKGVuc2VtYmwpLCBuID0gMzApCmBgYAoKV2UgY2FuIGFjY2VzcyBhZGRpdGlvbmFsIGdlbm9taWMgYW5ub3RhdGlvbnMgdXNpbmcgdGhlIFtgYmlvTWFydGAgcGFja2FnZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2Jpb21hUnQuaHRtbCkuIFRvIGlkZW50aWZ5ICB3ZSdsbCBzdHJ1Y3R1cmUgb3VyICdxdWVyeScgb3Igc2VhcmNoIG9mIHRoZSBiaW9NYXJ0IHJlc291cmNlcyB0byB1c2UgdGhlIFtFTlNFTUJMIGlkXShodHRwczovL20uZW5zZW1ibC5vcmcvaW5mby9nZW5vbWUvZ2VuZWJ1aWxkL2dlbmVfbmFtZXMuaHRtbCkgZnJvbSBvdXIgYWxpZ25tZW50IHRvIGFkZCB0aGUgZ2VuZSBzeW1ib2xzIGFuZCBnZW5lIGRlc2NyaXB0aW9uIGZvciBlYWNoIGdlbmUuCgpgYGB7ciBBZGRBbm90YXRpb24zLCB3YXJuaW5nPUZBTFNFfQppZF9tYXBwaW5nID0gZ2V0Qk0oYXR0cmlidXRlcz1jKCdlbnNlbWJsX2dlbmVfaWQnLCAnZXh0ZXJuYWxfZ2VuZV9uYW1lJyksCiAgICAgIGZpbHRlcnMgPSAnZW5zZW1ibF9nZW5lX2lkJywKICAgICAgdmFsdWVzID0gcm93Lm5hbWVzKGFzc2F5KGRkcykpLAogICAgICBtYXJ0ID0gZW5zZW1ibCkKIyB3aWxsIHRha2Ugc29tZSB0aW1lIHRvIHJ1bgoKIyBQcmV2aWV3IHRoZSByZXN1bHQKaGVhZChpZF9tYXBwaW5nKQpgYGAKCk5vdyB0aGF0IHdlIGhhdmUgdGhlIEVOU0VNQkwgaW5mb3JtYXRpb24gYW5kIGEgZ2VuZSBzeW1ib2wgdG8gbWF0Y2ggdG8gb3VyIHJlc3VsdHMsIHdlIGNhbiBwcm9jZWVkIGluIHRoZSBzbWFsbGVyIGdyb3Vwcy4gQXMgd2l0aCB0aGUgcHJldmlvdXMgZXhlcmNpc2UsIHdlIGhhdmUgYnJva2VuIGl0IGludG8gc21hbGwgc3RlcHMgd2l0aCBoaW50cyBhcyBuZWVkZWQuCgoqKk5vdGUqKjogRm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gcmVnYXJkaW5nIGJpb01hcnQsIHBsZWFzZSBjb25zdWx0IHRoZSBbRU5TRU1CTCBiaW9NYXJ0IHZpZ25ldHRlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9iaW9tYVJ0L2luc3QvZG9jL2FjY2Vzc2luZ19lbnNlbWJsLmh0bWwpIG9yIHRoZSBicm9hZGVyIFtCaW9jb25kdWN0b3IgQW5ub3RhdGlvbiBSZXNvdXJjZXMgdmlnbmV0dGUgXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC93b3JrZmxvd3MvdmlnbmV0dGVzL2Fubm90YXRpb24vaW5zdC9kb2MvQW5ub3RhdGlvbl9SZXNvdXJjZXMuaHRtbCkuCgojIFN0ZXBzCgoxLiBMb29rIGF0IHRoZSB0d28gZGF0YSBmcmFtZXMgdGhhdCBhcmUgZ29pbmcgdG8gYmUgbmVlZGVkIGZvciB0aGUgZXhlcmNpc2U6IGBpZF9tYXBwaW5nYCBhbmQgYHJlc3VsdHNfcGx1c192c19taW51c2AuCgo8ZGV0YWlscz4KPHN1bW1hcnk+QW5zd2VyPC9zdW1tYXJ5PgpgYGB7ciBwcmV2aWV3X3RhYmxlc30KaGVhZChpZF9tYXBwaW5nKQpoZWFkKHJlc3VsdHNfcGx1c192c19taW51cykKYGBgCjwvZGV0YWlscz4KPGJyPgoKMi4gVGhpbmsgYWJvdXQgd2hhdCBjb2x1bW4gb2YgYHJlc3VsdHNfcGx1c192c19taW51c2Agd2Ugd2FudCB0byBtYXRjaCB0byBgaWRfbWFwcGluZ2AsIGFuZCB0aGVuIHdoYXQgY29sdW1uIHdlIHdhbnQgdG8gZXh0cmFjdCB2YWx1ZXMgZnJvbSBpbiBgaWRfbWFwcGluZ2AuCgo8ZGV0YWlscz4KPHN1bW1hcnk+QW5zd2VyPC9zdW1tYXJ5PgpXZSB3YW50IHRvIG1hdGNoIHRoZSBgaWRgIGNvbHVtbiBvZiBgcmVzdWx0c19wbHVzX3ZzX21pbnVzYCB0byB0aGUgYGVuc2VtYmxfZ2VuZV9pZGAgY29sdW1uIG9mIGBpZF9tYXBwaW5nYCwgYW5kIG9uY2UgdGhhdCBtYXRjaCBpcyBmb3VuZCwgd2Ugd2FudCB0byBleHRyYWN0IHRoZSBgZXh0ZXJuYWxfZ2VuZV9uYW1lYCBjb2x1bW4gb2YgYGlkX21hcHBpbmdgIHRvIGdldCB0aGUgZ2VuZSBzeW1ib2wuCjwvZGV0YWlscz4KPGJyPgoKMy4gTG9vayBhdCB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgYGRwbHlyOjpsZWZ0X2pvaW4oKWAgYW5kIHRyeSB0byBtZXJnZSB0aGUgYGlkX21hcHBpbmdgIHRhYmxlIGludG8gdGhlIGByZXN1bHRzX3BsdXNfdnNfbWludXNgIHRhYmxlIG9uIHRoZSBjb2x1bW5zIHlvdSBpZGVudGlmaWVkIGFib3ZlLgoKPGRldGFpbHM+CjxzdW1tYXJ5PkFuc3dlcjwvc3VtbWFyeT4KYGBge3IgbGVmdF9qb2lufQpyZXN1bHRzX3BsdXNfdnNfbWludXNfYW5ub3RhdGVkID0gcmVzdWx0c19wbHVzX3ZzX21pbnVzICU+JQogICAgbGVmdF9qb2luKGlkX21hcHBpbmcsIGJ5ID0gYygnaWQnID0gJ2Vuc2VtYmxfZ2VuZV9pZCcpKQpoZWFkKHJlc3VsdHNfcGx1c192c19taW51c19hbm5vdGF0ZWQpCmBgYAo8L2RldGFpbHM+Cjxicj4KCjQuIE9wdGlvbmFsbHksIGhvdyBjb3VsZCB3ZSB1c2Ugc29tZSBvZiB0aGUgYHRpZHl2ZXJzZWAgZnVuY3Rpb25zIHdlJ3ZlIGVuY291bnRlcmVkIHByZXZpb3VzbHkgdG8gcmVuYW1lIHRoZSBgZXh0ZXJuYWxfZ2VuZV9uYW1lYCBjb2x1bW4gdG8gYHN5bWJvbGAgYW5kIHRvIG1vdmUgaXQgaW50byB0aGUgc2Vjb25kIGNvbHVtbiBwb3NpdGlvbj8gSGludDogQmVjYXVzZSBvZiB0aGUgb3JkZXIgb2YgdGhlIHBhY2thZ2VzIHdlIG1heSBoYXZlIGxvYWRlZCwgY29uc2lkZXIgdXNpbmcgYGRwbHlyOjpyZW5hbWUoKWAgYW5kIGBkcGx5cjo6c2VsZWN0KClgIGluc3RlYWQgb2YganVzdCB0aGUgYHNlbGVjdCgpYCBmdW5jdGlvbi4gV2UgY2FuIGRpc2N1c3Mgd2h5IHRoaXMgaXMgdGhlIGNhc2UgdG9nZXRoZXIuCgo8ZGV0YWlscz4KPHN1bW1hcnk+QW5zd2VyPC9zdW1tYXJ5PgpgYGB7ciByZW5hbWVfcmVhcnJhbmdlfQpyZXN1bHRzX3BsdXNfdnNfbWludXNfYW5ub3RhdGVkID0gcmVzdWx0c19wbHVzX3ZzX21pbnVzX2Fubm90YXRlZCAlPiUKICAgIGRwbHlyOjpyZW5hbWUoJ3N5bWJvbCcgPSAnZXh0ZXJuYWxfZ2VuZV9uYW1lJykgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGlkLCBzeW1ib2wsIGV2ZXJ5dGhpbmcoKSkKcmVzdWx0c19wbHVzX3ZzX21pbnVzX2Fubm90YXRlZApgYGAKPC9kZXRhaWxzPgo8YnI+CgoKYGBge3IgV3JpdGVPdXQuUkRhdGEsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgSGlkZGVuIGNvZGUgYmxvY2sgdG8gd3JpdGUgb3V0IGRhdGEgZm9yIGtuaXR0aW5nCiMgc2F2ZS5pbWFnZShmaWxlID0gJ3JkYXRhL1J1bm5pbmdEYXRhLlJEYXRhJykKYGBgCg==