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