The R markdown is available from the pulldown menu for Code at the upper-right, choose “Download Rmd”, or download the Rmd from GitHub.


This vignette will show you how to map or translate identifiers from one database (e.g., Ensembl) to another (e.g, Entrez Gene). This is a common requirement for data analysis. In the context of Cytoscape, for example, identifier mapping is needed when you want to import data to overlay on a network but you don’t have matching keys. There are three distinct examples below, highlighting different lessons that may apply to your use cases.

Installation

if(!"RCy3" %in% installed.packages()){
    install.packages("BiocManager")
    BiocManager::install("RCy3")
}
library(RCy3)

Required Software

The whole point of RCy3 is to connect with Cytoscape. You will need to install and launch Cytoscape:

cytoscapePing()

Example: Species specific considerations

When planning to import data, you need to consider the key columns you have in your network data and in your table data. It’s always recommended that you use proper identifiers as your keys (e.g., from databases like Ensembl and Uniprot-TrEMBL). Relying on conventional symbols and names is not standard and error prone.

Let’s start with the sample network provided by Cytoscape.

Caution: Loading a session file will discard your current session. Save first, if you have networks or data you want to keep. Use saveSession(‘path_to_file’).

openSession()  #Closes current session (without saving) and opens a sample session file

You should now see a network with just over 300 nodes. If you look at the Node Table, you’ll see that there are proper identifiers in the name columns, like “YDL194W”. These are the Ensembl-supported IDs for Yeast.

Perform identifier mapping

You need to know a few things about your network in order to run this function, e.g., the species and starting (or source) identifier type. This isn’t usually a problem, but this example highlights a unique case where the Ensembl ID type for a particular species (i.e., Yeast) has a particular format (e.g., YDL194W), rather than the more typical ENSXXXG00001232 format.

So, with this knowledge, you can run the following function:

mapped.cols <- mapTableColumn('name','Yeast','Ensembl','Entrez Gene')

We are asking Cytoscape to look in the name column for Yeast Ensembl IDs and then provide a new columns of corresponding Entrez Gene IDs. And if you look back at the Node Table, you’ll see that new column (all the way to the right). That’s it!

The return value is a data frame of all the mappings between Ensembl and Entrez Gene that were found for your network in case you want those details:

mapped.cols[1:3,] #first three entries

Note: the row names of the return data frame are the node SUIDs from Cytoscape. These are handy if you want to load the mappings yourself (see last example).

Example: From proteins to genes

For this next example, you’ll need the STRING app to access the STRING database from within Cytoscape: * Install the STRING app from https://apps.cytoscape.org/apps/stringapp

#available in Cytoscape 3.7.0 and above
installApp('STRINGapp')  

Now we can import protein interaction networks with a ton of annotations from the STRING database with a simple commandsGET function, like this:

string.cmd = 'string disease query disease="breast cancer" cutoff=0.9 species="Homo sapiens" limit=150'
commandsGET(string.cmd)

# for more information on string commands:
# commandsHelp('string')
# commandsHelp('string disease query')

Check out the Node Table and you’ll see display names and identifiers. In particular, the canonical name column appears to hold Uniprot-TrEMBL IDs. Nice, we can use that!

Perform identifier mapping

Say we have a dataset keyed by Ensembl gene identifiers. Well, then we would want to perform this mapping:

mapped.cols <- mapTableColumn('stringdb::canonical name','Human','Uniprot-TrEMBL','Ensembl')

Scroll all the way to the right in the Node Table and you’ll see a new column with Ensembl IDs. This example highlights a useful translation from protein to gene identifiers (or vice versa), but is also a caution to be aware of the assumptions involved when making this translation. For example, a typical gene encodes for many proteins, so you may have many-to-one mappings in your results.

Example: Mixed identifiers

From time to time, you’ll come across a case where the identifiers in your network are of mixed types. This is a rare scenario, but here is one approach to solving it.

First, you’ll need the WikiPathways app to access the WikiPathways database. The pathways in WikiPathways are curated by a community of interested researchers and citizen scientists. As such, there are times where authors might use different sources of identifiers. They are valid IDs, just not all from the same source. Future versions of the WikiPathways app will provide pre-mapped columns to a single ID type. But in the meantime (and relevant to other use cases), this example highlights how to handle a source of mixed identifier types.

#available in Cytoscape 3.7.0 and above
installApp('WikiPathways')  

Now we can import an Apoptosis Pathway from WikiPathways. Either from the web site (https://wikipathways.org), or from the Network Search Tool in Cytoscape GUI or from the rWikiPathways package, we could identify the pathway as WP254.

wp.cmd = 'wikipathways import-as-pathway id="WP254"'
commandsGET(wp.cmd)

# for more information on wikipathways commands:
# commandsHelp('wikipathways')
# commandsHelp('wikipathways import-as-pathway')

Take look in the XrefId column and you’ll see a mix of identifier types. The next column over, XrefDatasource, conveniently names each type’s source. Ignoring the metabolites for this example, we just have a mix of Ensembl and Entrez Gene to deal with.

Perform identifier mapping

Say we want a column with only Ensembl IDs. The easiest approach is to simply overwrite all the non-Ensembl IDs, i.e., in this case, Entrez Gene IDs. Let’s collect the mappings first:

mapped.cols <- mapTableColumn('XrefId','Human','Entrez Gene','Ensembl')

Next, we want to remove the values from the Ensembl column in our resulting mapped.cols data frame. We’ll also remove the original source columns (to avoid confusion) and rename our Ensembl column to XrefId to prepare to overwrite. Then we’ll load that into Cytosacpe:

only.mapped.cols <- mapped.cols[complete.cases(mapped.cols), 'Ensembl', drop=FALSE]
colnames(only.mapped.cols) <- 'XrefId'
loadTableData(only.mapped.cols,table.key.column = 'SUID')

Done! See the updated XrefId column in Cytoscape with all Ensembl IDs.

Note: you’ll want to either update the XrefDatasource* column as well or simply make a note to ignore it at this point.*

More advanced cases

This identifier mapping function is intended to handle the majority of common ID mapping problems. It has limitation, however.

?mapTableColumn

If you need an ID mapping solution for species or ID types not covered by this tool, or if you want to connect to alternative sources of mappings, then check out the BridgeDb app: http://apps.cytoscape.org/apps/bridgedb.

#available in Cytoscape 3.7.0 and above
installApp('BridgeDb')  

And then browse the available function with commandsHelp(‘bridgedb’)

LS0tCnRpdGxlOiAiSWRlbnRpZmllciBtYXBwaW5nIgphdXRob3I6ICJieSBBbGV4YW5kZXIgUGljbyIKcGFja2FnZTogUkN5MwpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiAibm9uZSIKIyAgcGRmX2RvY3VtZW50OgojICAgIHRvYzogdHJ1ZSAKdmlnbmV0dGU6ID4KICAlXFZpZ25ldHRlSW5kZXhFbnRyeXswNy4gSWRlbnRpZmllciBtYXBwaW5nIH4yMCBtaW59CiAgJVxWaWduZXR0ZUVuZ2luZXtrbml0cjo6cm1hcmtkb3dufQogICVcVmlnbmV0dGVFbmNvZGluZ3tVVEYtOH0KLS0tCmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBldmFsPUZBTFNFCikKYGBgCipUaGUgUiBtYXJrZG93biBpcyBhdmFpbGFibGUgZnJvbSB0aGUgcHVsbGRvd24gbWVudSBmb3IqIENvZGUgKmF0IHRoZSB1cHBlci1yaWdodCwgY2hvb3NlICJEb3dubG9hZCBSbWQiLCBvciBbZG93bmxvYWQgdGhlIFJtZCBmcm9tIEdpdEh1Yl0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2N5dG9zY2FwZS9jeXRvc2NhcGUtYXV0b21hdGlvbi9tYXN0ZXIvZm9yLXNjcmlwdGVycy9SL25vdGVib29rcy9JZGVudGlmaWVyLW1hcHBpbmcuUm1kKS4qCgo8aHIgLz4KVGhpcyB2aWduZXR0ZSB3aWxsIHNob3cgeW91IGhvdyB0byBtYXAgb3IgdHJhbnNsYXRlIGlkZW50aWZpZXJzIGZyb20gb25lIGRhdGFiYXNlCihlLmcuLCBFbnNlbWJsKSB0byBhbm90aGVyICAoZS5nLCBFbnRyZXogR2VuZSkuIFRoaXMgaXMgYSBjb21tb24gcmVxdWlyZW1lbnQKZm9yIGRhdGEgYW5hbHlzaXMuIEluIHRoZSBjb250ZXh0IG9mIEN5dG9zY2FwZSwgZm9yIGV4YW1wbGUsIGlkZW50aWZpZXIgbWFwcGluZyBpcwpuZWVkZWQgd2hlbiB5b3Ugd2FudCB0byBpbXBvcnQgZGF0YSB0byBvdmVybGF5IG9uIGEgbmV0d29yayBidXQgeW91IGRvbid0IGhhdmUKbWF0Y2hpbmcga2V5cy4gVGhlcmUgYXJlIHRocmVlIGRpc3RpbmN0IGV4YW1wbGVzIGJlbG93LCBoaWdobGlnaHRpbmcgZGlmZmVyZW50Cmxlc3NvbnMgdGhhdCBtYXkgYXBwbHkgdG8geW91ciB1c2UgY2FzZXMuCgojIEluc3RhbGxhdGlvbgpgYGB7cn0KaWYoISJSQ3kzIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKXsKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKICAgIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJSQ3kzIikKfQpsaWJyYXJ5KFJDeTMpCmBgYAoKIyBSZXF1aXJlZCBTb2Z0d2FyZQpUaGUgd2hvbGUgcG9pbnQgb2YgUkN5MyBpcyB0byBjb25uZWN0IHdpdGggQ3l0b3NjYXBlLiBZb3Ugd2lsbCBuZWVkIHRvIGluc3RhbGwgYW5kIGxhdW5jaCBDeXRvc2NhcGU6IAoKKiBEb3dubG9hZCB0aGUgbGF0ZXN0IEN5dG9zY2FwZSBmcm9tIGh0dHA6Ly93d3cuY3l0b3NjYXBlLm9yZy9kb3dubG9hZC5waHAKKiBDb21wbGV0ZSBpbnN0YWxsYXRpb24gd2l6YXJkCiogTGF1bmNoIEN5dG9zY2FwZSAKCmBgYHtyfQpjeXRvc2NhcGVQaW5nKCkKYGBgCgojIEV4YW1wbGU6IFNwZWNpZXMgc3BlY2lmaWMgY29uc2lkZXJhdGlvbnMKV2hlbiBwbGFubmluZyB0byBpbXBvcnQgZGF0YSwgeW91IG5lZWQgdG8gY29uc2lkZXIgdGhlIGtleSBjb2x1bW5zIHlvdSBoYXZlIGluCnlvdXIgbmV0d29yayBkYXRhIGFuZCBpbiB5b3VyIHRhYmxlIGRhdGEuIEl0J3MgYWx3YXlzIHJlY29tbWVuZGVkIHRoYXQgeW91IHVzZQpwcm9wZXIgaWRlbnRpZmllcnMgYXMgeW91ciBrZXlzIChlLmcuLCBmcm9tIGRhdGFiYXNlcyBsaWtlIEVuc2VtYmwgYW5kIFVuaXByb3QtVHJFTUJMKS4KUmVseWluZyBvbiBjb252ZW50aW9uYWwgc3ltYm9scyBhbmQgbmFtZXMgaXMgbm90IHN0YW5kYXJkIGFuZCBlcnJvciBwcm9uZS4gCgpMZXQncyBzdGFydCB3aXRoIHRoZSBzYW1wbGUgbmV0d29yayBwcm92aWRlZCBieSBDeXRvc2NhcGUuCgoqKkNhdXRpb246IExvYWRpbmcgYSBzZXNzaW9uIGZpbGUgd2lsbCBkaXNjYXJkIHlvdXIgY3VycmVudCBzZXNzaW9uLiBTYXZlIGZpcnN0LAppZiB5b3UgaGF2ZSBuZXR3b3JrcyBvciBkYXRhIHlvdSB3YW50IHRvIGtlZXAuIFVzZSBzYXZlU2Vzc2lvbigncGF0aF90b19maWxlJykuKioKYGBge3J9Cm9wZW5TZXNzaW9uKCkgICNDbG9zZXMgY3VycmVudCBzZXNzaW9uICh3aXRob3V0IHNhdmluZykgYW5kIG9wZW5zIGEgc2FtcGxlIHNlc3Npb24gZmlsZQpgYGAKCllvdSBzaG91bGQgbm93IHNlZSBhIG5ldHdvcmsgd2l0aCBqdXN0IG92ZXIgMzAwIG5vZGVzLiBJZiB5b3UgbG9vayBhdCB0aGUgTm9kZSAKVGFibGUsIHlvdSdsbCBzZWUgdGhhdCB0aGVyZSBhcmUgcHJvcGVyIGlkZW50aWZpZXJzIGluIHRoZSAqbmFtZSogY29sdW1ucywgbGlrZQoiWURMMTk0VyIuIFRoZXNlIGFyZSB0aGUgRW5zZW1ibC1zdXBwb3J0ZWQgSURzIGZvciBZZWFzdC4KCiMjIFBlcmZvcm0gaWRlbnRpZmllciBtYXBwaW5nCllvdSBuZWVkIHRvIGtub3cgYSBmZXcgdGhpbmdzIGFib3V0IHlvdXIgbmV0d29yayBpbiBvcmRlciB0byBydW4gdGhpcyBmdW5jdGlvbiwgCmUuZy4sIHRoZSBzcGVjaWVzIGFuZCBzdGFydGluZyAob3Igc291cmNlKSBpZGVudGlmaWVyIHR5cGUuIFRoaXMgaXNuJ3QgdXN1YWxseQphIHByb2JsZW0sIGJ1dCAqKnRoaXMgZXhhbXBsZSBoaWdobGlnaHRzIGEgdW5pcXVlIGNhc2Ugd2hlcmUgdGhlIEVuc2VtYmwgSUQgdHlwZQpmb3IgYSBwYXJ0aWN1bGFyIHNwZWNpZXMgKGkuZS4sIFllYXN0KSBoYXMgYSBwYXJ0aWN1bGFyIGZvcm1hdCAoZS5nLiwgWURMMTk0VykqKiwgCnJhdGhlciB0aGFuIHRoZSBtb3JlIHR5cGljYWwgRU5TWFhYRzAwMDAxMjMyIGZvcm1hdC4KClNvLCB3aXRoIHRoaXMga25vd2xlZGdlLCB5b3UgY2FuIHJ1biB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uOgoKYGBge3J9Cm1hcHBlZC5jb2xzIDwtIG1hcFRhYmxlQ29sdW1uKCduYW1lJywnWWVhc3QnLCdFbnNlbWJsJywnRW50cmV6IEdlbmUnKQpgYGAKCldlIGFyZSBhc2tpbmcgQ3l0b3NjYXBlIHRvIGxvb2sgaW4gdGhlICpuYW1lKiBjb2x1bW4gZm9yICpZZWFzdCBFbnNlbWJsKiBJRHMgYW5kCnRoZW4gcHJvdmlkZSBhIG5ldyBjb2x1bW5zIG9mIGNvcnJlc3BvbmRpbmcgKkVudHJleiBHZW5lKiBJRHMuIEFuZCBpZiB5b3UgbG9vawpiYWNrIGF0IHRoZSBOb2RlIFRhYmxlLCB5b3UnbGwgc2VlIHRoYXQgbmV3IGNvbHVtbiAoYWxsIHRoZSB3YXkgdG8gdGhlIHJpZ2h0KS4KVGhhdCdzIGl0IQoKVGhlIHJldHVybiB2YWx1ZSBpcyBhIGRhdGEgZnJhbWUgb2YgYWxsIHRoZSBtYXBwaW5ncyBiZXR3ZWVuIEVuc2VtYmwgYW5kIEVudHJleiBHZW5lCnRoYXQgd2VyZSBmb3VuZCBmb3IgeW91ciBuZXR3b3JrIGluIGNhc2UgeW91IHdhbnQgdGhvc2UgZGV0YWlsczoKYGBge3J9Cm1hcHBlZC5jb2xzWzE6MyxdICNmaXJzdCB0aHJlZSBlbnRyaWVzCmBgYAoKKk5vdGU6IHRoZSByb3cgbmFtZXMgb2YgdGhlIHJldHVybiBkYXRhIGZyYW1lIGFyZSB0aGUgbm9kZSBTVUlEcyBmcm9tIEN5dG9zY2FwZS4KVGhlc2UgYXJlIGhhbmR5IGlmIHlvdSB3YW50IHRvIGxvYWQgdGhlIG1hcHBpbmdzIHlvdXJzZWxmIChzZWUgbGFzdCBleGFtcGxlKS4qCgo8Y2VudGVyPgohW10oaHR0cHM6Ly9jeXRvc2NhcGUuZ2l0aHViLmlvL2N5dG9zY2FwZS1hdXRvbWF0aW9uL2Zvci1zY3JpcHRlcnMvUi9ub3RlYm9va3MvZGF0YS9pbWcvaWRlbnRpZmllci1tYXBwaW5nMS5wbmcpe3dpZHRoPTYwJX0KPC9jZW50ZXI+CgojIEV4YW1wbGU6IEZyb20gcHJvdGVpbnMgdG8gZ2VuZXMKRm9yIHRoaXMgbmV4dCBleGFtcGxlLCB5b3UnbGwgbmVlZCB0aGUgU1RSSU5HIGFwcCB0byBhY2Nlc3MgdGhlIFNUUklORyBkYXRhYmFzZQpmcm9tIHdpdGhpbiBDeXRvc2NhcGU6CiogSW5zdGFsbCB0aGUgU1RSSU5HIGFwcCBmcm9tIGh0dHBzOi8vYXBwcy5jeXRvc2NhcGUub3JnL2FwcHMvc3RyaW5nYXBwCgpgYGB7cn0KI2F2YWlsYWJsZSBpbiBDeXRvc2NhcGUgMy43LjAgYW5kIGFib3ZlCmluc3RhbGxBcHAoJ1NUUklOR2FwcCcpICAKYGBgCgpOb3cgd2UgY2FuIGltcG9ydCBwcm90ZWluIGludGVyYWN0aW9uIG5ldHdvcmtzIHdpdGggYSB0b24gb2YgYW5ub3RhdGlvbnMgZnJvbQp0aGUgU1RSSU5HIGRhdGFiYXNlIHdpdGggYSBzaW1wbGUgY29tbWFuZHNHRVQgZnVuY3Rpb24sIGxpa2UgdGhpczoKYGBge3J9CnN0cmluZy5jbWQgPSAnc3RyaW5nIGRpc2Vhc2UgcXVlcnkgZGlzZWFzZT0iYnJlYXN0IGNhbmNlciIgY3V0b2ZmPTAuOSBzcGVjaWVzPSJIb21vIHNhcGllbnMiIGxpbWl0PTE1MCcKY29tbWFuZHNHRVQoc3RyaW5nLmNtZCkKCiMgZm9yIG1vcmUgaW5mb3JtYXRpb24gb24gc3RyaW5nIGNvbW1hbmRzOgojIGNvbW1hbmRzSGVscCgnc3RyaW5nJykKIyBjb21tYW5kc0hlbHAoJ3N0cmluZyBkaXNlYXNlIHF1ZXJ5JykKYGBgCgpDaGVjayBvdXQgdGhlIE5vZGUgVGFibGUgYW5kIHlvdSdsbCBzZWUgZGlzcGxheSBuYW1lcyBhbmQgaWRlbnRpZmllcnMuIEluIHBhcnRpY3VsYXIsCnRoZSAqY2Fub25pY2FsIG5hbWUqIGNvbHVtbiBhcHBlYXJzIHRvIGhvbGQgVW5pcHJvdC1UckVNQkwgSURzLiBOaWNlLCB3ZSBjYW4gdXNlIHRoYXQhCgo8Y2VudGVyPgohW10oaHR0cHM6Ly9jeXRvc2NhcGUuZ2l0aHViLmlvL2N5dG9zY2FwZS1hdXRvbWF0aW9uL2Zvci1zY3JpcHRlcnMvUi9ub3RlYm9va3MvZGF0YS9pbWcvaWRlbnRpZmllci1tYXBwaW5nMi5wbmcpe3dpZHRoPTYwJX0KPC9jZW50ZXI+CgojIyBQZXJmb3JtIGlkZW50aWZpZXIgbWFwcGluZwpTYXkgd2UgaGF2ZSBhIGRhdGFzZXQga2V5ZWQgYnkgRW5zZW1ibCBnZW5lIGlkZW50aWZpZXJzLiBXZWxsLCB0aGVuIHdlIHdvdWxkIHdhbnQKdG8gcGVyZm9ybSB0aGlzIG1hcHBpbmc6CmBgYHtyfQptYXBwZWQuY29scyA8LSBtYXBUYWJsZUNvbHVtbignc3RyaW5nZGI6OmNhbm9uaWNhbCBuYW1lJywnSHVtYW4nLCdVbmlwcm90LVRyRU1CTCcsJ0Vuc2VtYmwnKQpgYGAKClNjcm9sbCBhbGwgdGhlIHdheSB0byB0aGUgcmlnaHQgaW4gdGhlIE5vZGUgVGFibGUgYW5kIHlvdSdsbCBzZWUgYSBuZXcgY29sdW1uIHdpdGgKRW5zZW1ibCBJRHMuICoqVGhpcyBleGFtcGxlIGhpZ2hsaWdodHMgYSB1c2VmdWwgdHJhbnNsYXRpb24gZnJvbSBwcm90ZWluIHRvIGdlbmUKaWRlbnRpZmllcnMgKG9yIHZpY2UgdmVyc2EpLCBidXQgaXMgYWxzbyBhIGNhdXRpb24gdG8gYmUgYXdhcmUgb2YgdGhlIGFzc3VtcHRpb25zCmludm9sdmVkIHdoZW4gbWFraW5nIHRoaXMgdHJhbnNsYXRpb24uKiogRm9yIGV4YW1wbGUsIGEgdHlwaWNhbCBnZW5lIGVuY29kZXMgZm9yIG1hbnkgCnByb3RlaW5zLCBzbyB5b3UgbWF5IGhhdmUgbWFueS10by1vbmUgbWFwcGluZ3MgaW4geW91ciByZXN1bHRzLgoKIyBFeGFtcGxlOiBNaXhlZCBpZGVudGlmaWVycwpGcm9tIHRpbWUgdG8gdGltZSwgeW91J2xsIGNvbWUgYWNyb3NzIGEgY2FzZSB3aGVyZSB0aGUgaWRlbnRpZmllcnMgaW4geW91ciBuZXR3b3JrCmFyZSBvZiBtaXhlZCB0eXBlcy4gVGhpcyBpcyBhIHJhcmUgc2NlbmFyaW8sIGJ1dCBoZXJlIGlzIG9uZSBhcHByb2FjaCB0byBzb2x2aW5nCml0LgoKRmlyc3QsIHlvdSdsbCBuZWVkIHRoZSBXaWtpUGF0aHdheXMgYXBwIHRvIGFjY2VzcyB0aGUgV2lraVBhdGh3YXlzCmRhdGFiYXNlLiBUaGUgcGF0aHdheXMgaW4gV2lraVBhdGh3YXlzIGFyZSBjdXJhdGVkIGJ5IGEgY29tbXVuaXR5IG9mIGludGVyZXN0ZWQKcmVzZWFyY2hlcnMgYW5kIGNpdGl6ZW4gc2NpZW50aXN0cy4gQXMgc3VjaCwgdGhlcmUgYXJlIHRpbWVzIHdoZXJlIGF1dGhvcnMgbWlnaHQKdXNlIGRpZmZlcmVudCBzb3VyY2VzIG9mIGlkZW50aWZpZXJzLiBUaGV5IGFyZSB2YWxpZCBJRHMsIGp1c3Qgbm90IGFsbCBmcm9tIHRoZSBzYW1lCnNvdXJjZS4gRnV0dXJlIHZlcnNpb25zIG9mIHRoZSBXaWtpUGF0aHdheXMgYXBwIHdpbGwgcHJvdmlkZSBwcmUtbWFwcGVkIGNvbHVtbnMKdG8gYSBzaW5nbGUgSUQgdHlwZS4gQnV0IGluIHRoZSBtZWFudGltZSAoYW5kIHJlbGV2YW50IHRvIG90aGVyIHVzZSBjYXNlcyksICoqdGhpcwpleGFtcGxlIGhpZ2hsaWdodHMgaG93IHRvIGhhbmRsZSBhIHNvdXJjZSBvZiBtaXhlZCBpZGVudGlmaWVyIHR5cGVzLioqCgoqIEluc3RhbGwgdGhlIFdpa2lQYXRod2F5cyBhcHAgZnJvbSBodHRwczovL2FwcHMuY3l0b3NjYXBlLm9yZy9hcHBzL3dpa2lwYXRod2F5cwoKYGBge3J9CiNhdmFpbGFibGUgaW4gQ3l0b3NjYXBlIDMuNy4wIGFuZCBhYm92ZQppbnN0YWxsQXBwKCdXaWtpUGF0aHdheXMnKSAgCmBgYAoKCk5vdyB3ZSBjYW4gaW1wb3J0IGFuIEFwb3B0b3NpcyBQYXRod2F5IGZyb20gV2lraVBhdGh3YXlzLiBFaXRoZXIgZnJvbSB0aGUgd2ViCnNpdGUgKGh0dHBzOi8vd2lraXBhdGh3YXlzLm9yZyksIG9yIGZyb20gdGhlIE5ldHdvcmsgU2VhcmNoIFRvb2wgaW4gQ3l0b3NjYXBlCkdVSSBvciBmcm9tIHRoZSByV2lraVBhdGh3YXlzIHBhY2thZ2UsIHdlIGNvdWxkIGlkZW50aWZ5IHRoZSBwYXRod2F5IGFzIFdQMjU0LgpgYGB7cn0Kd3AuY21kID0gJ3dpa2lwYXRod2F5cyBpbXBvcnQtYXMtcGF0aHdheSBpZD0iV1AyNTQiJwpjb21tYW5kc0dFVCh3cC5jbWQpCgojIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHdpa2lwYXRod2F5cyBjb21tYW5kczoKIyBjb21tYW5kc0hlbHAoJ3dpa2lwYXRod2F5cycpCiMgY29tbWFuZHNIZWxwKCd3aWtpcGF0aHdheXMgaW1wb3J0LWFzLXBhdGh3YXknKQoKYGBgCgpUYWtlIGxvb2sgaW4gdGhlICpYcmVmSWQqIGNvbHVtbiBhbmQgeW91J2xsIHNlZSBhIG1peCBvZiBpZGVudGlmaWVyIHR5cGVzLiBUaGUKbmV4dCBjb2x1bW4gb3ZlciwgKlhyZWZEYXRhc291cmNlKiwgY29udmVuaWVudGx5IG5hbWVzIGVhY2ggdHlwZSdzIHNvdXJjZS4gSWdub3JpbmcKdGhlIG1ldGFib2xpdGVzIGZvciB0aGlzIGV4YW1wbGUsIHdlIGp1c3QgaGF2ZSBhIG1peCBvZiBFbnNlbWJsIGFuZCBFbnRyZXogR2VuZSAKdG8gZGVhbCB3aXRoLgoKIyMgUGVyZm9ybSBpZGVudGlmaWVyIG1hcHBpbmcKU2F5IHdlIHdhbnQgYSBjb2x1bW4gd2l0aCBvbmx5IEVuc2VtYmwgSURzLiBUaGUgZWFzaWVzdCBhcHByb2FjaCBpcyB0byBzaW1wbHkgCm92ZXJ3cml0ZSBhbGwgdGhlIG5vbi1FbnNlbWJsIElEcywgaS5lLiwgaW4gdGhpcyBjYXNlLCBFbnRyZXogR2VuZSBJRHMuIExldCdzIApjb2xsZWN0IHRoZSBtYXBwaW5ncyBmaXJzdDoKCmBgYHtyfQptYXBwZWQuY29scyA8LSBtYXBUYWJsZUNvbHVtbignWHJlZklkJywnSHVtYW4nLCdFbnRyZXogR2VuZScsJ0Vuc2VtYmwnKQpgYGAKCk5leHQsIHdlIHdhbnQgdG8gcmVtb3ZlIHRoZSA8TkE+IHZhbHVlcyBmcm9tIHRoZSAqRW5zZW1ibCogY29sdW1uIGluIG91ciByZXN1bHRpbmcKbWFwcGVkLmNvbHMgZGF0YSBmcmFtZS4gV2UnbGwgYWxzbyByZW1vdmUgdGhlIG9yaWdpbmFsIHNvdXJjZSBjb2x1bW5zICh0byBhdm9pZApjb25mdXNpb24pIGFuZCByZW5hbWUgb3VyICpFbnNlbWJsKiBjb2x1bW4gdG8gKlhyZWZJZCogdG8gcHJlcGFyZSB0byBvdmVyd3JpdGUuIApUaGVuIHdlJ2xsIGxvYWQgdGhhdCBpbnRvIEN5dG9zYWNwZToKCmBgYHtyfQpvbmx5Lm1hcHBlZC5jb2xzIDwtIG1hcHBlZC5jb2xzW2NvbXBsZXRlLmNhc2VzKG1hcHBlZC5jb2xzKSwgJ0Vuc2VtYmwnLCBkcm9wPUZBTFNFXQpjb2xuYW1lcyhvbmx5Lm1hcHBlZC5jb2xzKSA8LSAnWHJlZklkJwpsb2FkVGFibGVEYXRhKG9ubHkubWFwcGVkLmNvbHMsdGFibGUua2V5LmNvbHVtbiA9ICdTVUlEJykKYGBgCgpEb25lISBTZWUgdGhlIHVwZGF0ZWQgKlhyZWZJZCogY29sdW1uIGluIEN5dG9zY2FwZSB3aXRoIGFsbCBFbnNlbWJsIElEcy4gCgoqTm90ZTogeW91J2xsIHdhbnQgdG8gZWl0aGVyIHVwZGF0ZSB0aGUgKlhyZWZEYXRhc291cmNlKiBjb2x1bW4gYXMgd2VsbCBvciBzaW1wbHkgbWFrZSAKYSBub3RlIHRvIGlnbm9yZSBpdCBhdCB0aGlzIHBvaW50LioKCiMgTW9yZSBhZHZhbmNlZCBjYXNlcwpUaGlzIGlkZW50aWZpZXIgbWFwcGluZyBmdW5jdGlvbiBpcyBpbnRlbmRlZCB0byBoYW5kbGUgdGhlIG1ham9yaXR5IG9mIGNvbW1vbgpJRCBtYXBwaW5nIHByb2JsZW1zLiBJdCBoYXMgbGltaXRhdGlvbiwgaG93ZXZlci4gCmBgYHtyfQo/bWFwVGFibGVDb2x1bW4KYGBgCgpJZiB5b3UgbmVlZCBhbiBJRCBtYXBwaW5nIHNvbHV0aW9uIGZvciBzcGVjaWVzIG9yIElEIHR5cGVzIG5vdCBjb3ZlcmVkIGJ5IHRoaXMgCnRvb2wsIG9yIGlmIHlvdSB3YW50IHRvIGNvbm5lY3QgdG8gYWx0ZXJuYXRpdmUgc291cmNlcyBvZiBtYXBwaW5ncywgdGhlbiBjaGVjawpvdXQgdGhlIEJyaWRnZURiIGFwcDogaHR0cDovL2FwcHMuY3l0b3NjYXBlLm9yZy9hcHBzL2JyaWRnZWRiLiAKCmBgYHtyfQojYXZhaWxhYmxlIGluIEN5dG9zY2FwZSAzLjcuMCBhbmQgYWJvdmUKaW5zdGFsbEFwcCgnQnJpZGdlRGInKSAgCmBgYAoKQW5kIHRoZW4gYnJvd3NlIHRoZSBhdmFpbGFibGUgZnVuY3Rpb24gd2l0aCAqY29tbWFuZHNIZWxwKCdicmlkZ2VkYicpKg==